Commit to version 1.3.23.0 (67)

git-svn-id: http://omaha.googlecode.com/svn/trunk@109 e782f428-02b4-11de-a43f-ff96a2b7a9af
diff --git a/VERSION b/VERSION
index b581c17..608225a 100644
--- a/VERSION
+++ b/VERSION
@@ -3,8 +3,9 @@
 # update these manually - don't autogenerate, autoincrement, etc.
 # REMEMBER TO INCREMENT BUILD BY TWO! BUILD SHOULD ALWAYS BE ODD!
 version_major = 1    # 1-65535
-version_minor = 2    # 0-65535
-version_build = 189  # 1-65535
+version_minor = 3    # 0-65535
+version_build = 23   # 1-65535
 version_patch = 0    # 0-65535
 
-oneclick_plugin_version = 8
+oneclick_plugin_version = 9
+update_plugin_version = 3
diff --git a/base/ATLRegMapEx.h b/base/ATLRegMapEx.h
new file mode 100644
index 0000000..8e79394
--- /dev/null
+++ b/base/ATLRegMapEx.h
@@ -0,0 +1,325 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Extension to DECLARE_REGISTRY_RESOURCEID that makes adding stuff to
+// your reg file as simple as using an atl macro map.
+//
+// Adapted from http://thecodeproject.com/atl/RegistryMap.asp
+/*
+ * Defines a 'registry' map for adding variables to rgs files.
+ * Original Code Copyright 2001-2003 Michael Geddes.  All rights reserved.
+ * Modified Code Copyright 2005 Google Inc.
+ */
+
+/* use this as your RGS file -- remove or add parameters as you see fit
+//
+// The ATL registrar requires single quotes to be escaped in substitution data
+// enclosed in single quotes in the RGS file. Since the registry map is not
+// aware of which data fields are quoted, to err on the side of caution, all
+// _ATL_REGMAP_ENTRYKeeper constructors escape szData. It is also important to
+// enclose all substitutions in RGS files in single quotes, as shown in the
+// example here:
+
+HKCR
+{
+  '%PROGID%.%VERSION%' = s '%DESCRIPTION%'
+  {
+    CLSID = s '%CLSID%'
+  }
+  '%PROGID%' = s '%DESCRIPTION%'
+  {
+    CLSID = s '%CLSID%'
+    CurVer = s '%PROGID%.%VERSION%'
+  }
+  NoRemove CLSID
+  {
+    ForceRemove '%CLSID%' = s '%DESCRIPTION%'
+    {
+      ProgID = s '%PROGID%.%VERSION%'
+       VersionIndependentProgID = s '%PROGID%'
+      ForceRemove 'Programmable'
+      InprocServer32 = s '%MODULE%'
+      {
+        val ThreadingModel = s '%THREADING%'
+      }
+      'TypeLib' = s '%LIBID%'
+    }
+  }
+}
+
+*/
+
+#ifndef OMAHA_BASE_ATLREGMAPEX_H__
+#define OMAHA_BASE_ATLREGMAPEX_H__
+
+#include <atlbase.h>
+#include <atlconv.h>
+#include <atlstr.h>
+#include "omaha/base/app_util.h"
+#include "omaha/base/error.h"
+#include "omaha/base/path.h"
+#include "omaha/base/statregex.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+struct _ATL_REGMAP_ENTRYKeeper : public _ATL_REGMAP_ENTRY {
+  // Returns a new Olestr that needs to be freed by caller.
+  LPCOLESTR NewKeyOlestr(LPCTSTR tstr)  {
+    CT2COLE olestr(tstr);
+    int alloc_length = lstrlen(olestr) + 1;
+    LPOLESTR new_olestr =  new OLECHAR[alloc_length];
+    if (new_olestr) {
+      lstrcpyn(new_olestr, olestr, alloc_length);
+    }
+    return new_olestr;
+  }
+
+  // Escapes single quotes and returns a new Olestr that needs to be freed by
+  // caller.
+  LPCOLESTR NewDataOlestr(LPCTSTR tstr)  {
+    CT2COLE olestr(tstr);
+
+    CStringW escaped_str;
+    int alloc_length = lstrlen(olestr) * 2 + 1;
+    ATL::CAtlModule::EscapeSingleQuote(CStrBufW(escaped_str, alloc_length),
+                                       alloc_length,
+                                       olestr);
+
+    alloc_length = escaped_str.GetLength() + 1;
+    LPOLESTR new_escaped_str =  new OLECHAR[alloc_length];
+    if (new_escaped_str) {
+      lstrcpyn(new_escaped_str, escaped_str, alloc_length);
+    }
+    return new_escaped_str;
+  }
+
+  _ATL_REGMAP_ENTRYKeeper() {
+    szKey = NULL;
+    szData = NULL;
+  }
+
+  // REGMAP_ENTRY(x, y)
+  _ATL_REGMAP_ENTRYKeeper(LPCTSTR key_tstr, LPCWSTR data_tstr)  {
+    szKey = NewKeyOlestr(key_tstr);
+    szData = NewDataOlestr(CW2T(data_tstr));
+  }
+
+  // REGMAP_ENTRY(x, y)
+  _ATL_REGMAP_ENTRYKeeper(LPCTSTR key_tstr, LPCSTR data_tstr)  {
+    szKey = NewKeyOlestr(key_tstr);
+    szData = NewDataOlestr(CA2T(data_tstr));
+  }
+
+  // REGMAP_MODULE(x)
+  explicit _ATL_REGMAP_ENTRYKeeper(LPCTSTR key_tstr) {
+    szKey = NewKeyOlestr(key_tstr);
+    szData = NewDataOlestr(EnclosePathIfExe(app_util::GetCurrentModulePath()));
+  }
+
+  // REGMAP_MODULE2(x, modulename)
+  _ATL_REGMAP_ENTRYKeeper(LPCTSTR key_tstr,
+                          LPCTSTR module_name_tstr,
+                          bool /* is_relative_to_current_module */)  {
+    szKey = NewKeyOlestr(key_tstr);
+    szData = NULL;
+
+    CStringW full_module_name(app_util::GetCurrentModuleDirectory());
+    full_module_name += _T("\\");
+    full_module_name += module_name_tstr;
+    szData = NewDataOlestr(EnclosePathIfExe(full_module_name));
+  }
+
+  // REGMAP_EXE_MODULE(x)
+  _ATL_REGMAP_ENTRYKeeper(LPCTSTR key_tstr,
+                          bool /* is_current_exe_module */)  {
+    szKey = NewKeyOlestr(key_tstr);
+    szData = NewDataOlestr(EnclosePathIfExe(app_util::GetModulePath(NULL)));
+  }
+
+  // REGMAP_RESOURCE(x, resid)
+  _ATL_REGMAP_ENTRYKeeper(LPCTSTR key_tstr, UINT resid, bool /* resource */)  {
+    szKey = NewKeyOlestr(key_tstr);
+    CStringW res_name;
+    BOOL success = res_name.LoadString(resid);
+    ATLASSERT(success);
+    szData = NewDataOlestr(res_name);
+  }
+
+  // REGMAP_UUID(x, clsid)
+  _ATL_REGMAP_ENTRYKeeper(LPCTSTR key_tstr, REFGUID guid)  {
+    szKey = NewKeyOlestr(key_tstr);
+    szData = NewDataOlestr(GuidToString(guid));
+  }
+
+  ~_ATL_REGMAP_ENTRYKeeper()  {
+    delete [] szKey;
+    delete [] szData;
+  }
+
+  // This method is mostly the same as the atlmfc_vc80 version of
+  // ATL::CAtlModule::UpdateRegistryFromResourceS. The only functional change
+  // is that it uses omaha::RegObject instead of ATL::CRegObject.
+  static HRESULT UpdateRegistryFromResourceEx(
+      UINT nResID, BOOL bRegister, struct _ATL_REGMAP_ENTRY* pMapEntries) {
+    RegObject ro;
+    HRESULT hr = ro.FinalConstruct();
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    if (pMapEntries != NULL) {
+      while (pMapEntries->szKey != NULL) {
+        ASSERT1(NULL != pMapEntries->szData);
+        ro.AddReplacement(pMapEntries->szKey, pMapEntries->szData);
+        pMapEntries++;
+      }
+    }
+
+    hr = ATL::_pAtlModule->AddCommonRGSReplacements(&ro);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    USES_CONVERSION_EX;
+    TCHAR szModule[MAX_PATH];
+    HINSTANCE hInst = _AtlBaseModule.GetModuleInstance();
+    DWORD dwFLen = ::GetModuleFileName(hInst, szModule, MAX_PATH);
+    if (dwFLen == 0) {
+      return HRESULTFromLastError();
+    } else if (dwFLen == MAX_PATH) {
+      return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+    }
+
+    LPOLESTR pszModule = NULL;
+    pszModule = T2OLE_EX(szModule, _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
+    OLECHAR pszModuleUnquoted[_MAX_PATH * 2];  // NOLINT
+    ATL::CAtlModule::EscapeSingleQuote(pszModuleUnquoted,
+                                       _countof(pszModuleUnquoted),
+                                       pszModule);
+
+    HRESULT hRes;
+    if ((hInst == NULL) || (hInst == GetModuleHandle(NULL))) {
+      // If Registering as an EXE, then we quote the resultant path.
+      // We don't do it for a DLL, because LoadLibrary fails if the path is
+      // quoted
+      OLECHAR pszModuleQuote[(_MAX_PATH + _ATL_QUOTES_SPACE) * 2];  // NOLINT
+      pszModuleQuote[0] = OLESTR('\"');
+      if (!ocscpy_s(pszModuleQuote + 1, (_MAX_PATH + _ATL_QUOTES_SPACE) * 2 - 1,
+                    pszModuleUnquoted)) {
+        return E_FAIL;
+      }
+
+      size_t nLen = ocslen(pszModuleQuote);
+      pszModuleQuote[nLen] = OLESTR('\"');
+      pszModuleQuote[nLen + 1] = 0;
+
+      hRes = ro.AddReplacement(OLESTR("Module"), pszModuleQuote);
+    } else {
+      hRes = ro.AddReplacement(OLESTR("Module"), pszModuleUnquoted);
+    }
+
+    if (FAILED(hRes)) {
+      return hRes;
+    }
+
+    hRes = ro.AddReplacement(OLESTR("Module_Raw"), pszModuleUnquoted);
+    if (FAILED(hRes)) {
+      return hRes;
+    }
+
+    LPCOLESTR szType = OLESTR("REGISTRY");
+    hr = bRegister ? ro.ResourceRegister(pszModule, nResID, szType) :
+                     ro.ResourceUnregister(pszModule, nResID, szType);
+    return hr;
+  }
+};
+
+// This now supports DECLARE_OLEMISC_STATUS()
+#define BEGIN_REGISTRY_MAP()                                                \
+  __if_exists(_GetMiscStatus) {                                             \
+    static LPCTSTR _GetMiscStatusString()  {                                \
+      static TCHAR misc_string[32] = {0}                                    \
+      if (!misc_string[0])  {                                               \
+        wsprintf(misc_string, _T("%d"), _GetMiscStatus());                  \
+      }                                                                     \
+                                                                            \
+      return misc_string;                                                   \
+    }                                                                       \
+  }                                                                         \
+                                                                            \
+  static struct _ATL_REGMAP_ENTRY *_GetRegistryMap() {                      \
+    static const _ATL_REGMAP_ENTRYKeeper map[] = {                          \
+      __if_exists(_GetMiscStatusString) {                                   \
+        _ATL_REGMAP_ENTRYKeeper(_T("OLEMISC"), _GetMiscStatusString()),     \
+      }                                                                     \
+      __if_exists(GetAppIdT) {                                              \
+        _ATL_REGMAP_ENTRYKeeper(_T("APPID"), GetAppIdT()),                  \
+      }                                                                     \
+
+#define REGMAP_ENTRY(x, y) _ATL_REGMAP_ENTRYKeeper((x), (y)),
+
+#define REGMAP_RESOURCE(x, resid) _ATL_REGMAP_ENTRYKeeper((x), (resid), true),
+
+#define REGMAP_UUID(x, clsid) _ATL_REGMAP_ENTRYKeeper((x), (clsid)),
+
+// Add in an entry with key x, and value being the current module path.
+// For example, REGMAP_MODULE("foo"), with the current module being
+// "goopdate.dll" will result in the entry:
+// "foo", "{blah}\\Google\\Update\\1.2.71.7\\goopdate.dll"
+#define REGMAP_MODULE(x) _ATL_REGMAP_ENTRYKeeper((x)),
+
+// Add in an entry with key x, and value being modulename, fully qualified with
+// the current module path. For example, REGMAP_MODULE2("foo", "npClick7.dll")
+// with the current module being "goopdate.dll" will result in the entry:
+// "foo", "{blah}\\Google\\Update\\1.2.71.7\\npClick7.dll"
+#define REGMAP_MODULE2(x, modulename)                                       \
+    _ATL_REGMAP_ENTRYKeeper((x), (modulename), true),
+
+// Add in an entry with key x, and value being the currently running EXE's
+// module path. For example, REGMAP_EXE_MODULE("foo"), with the current process
+// being googleupdate.exe will result in the entry:
+// "foo", "{blah}\\Google\\Update\\googleupdate.exe"
+#define REGMAP_EXE_MODULE(x) _ATL_REGMAP_ENTRYKeeper((x), true),
+
+#define END_REGISTRY_MAP() _ATL_REGMAP_ENTRYKeeper()                        \
+    };                                                                      \
+    return (_ATL_REGMAP_ENTRY *)map;  /* NOLINT */                          \
+  }
+
+#define DECLARE_REGISTRY_RESOURCEID_EX(x)                                   \
+  static HRESULT WINAPI UpdateRegistry(BOOL reg) {                          \
+    return _ATL_REGMAP_ENTRYKeeper::UpdateRegistryFromResourceEx(           \
+        (UINT)(x), (reg), _GetRegistryMap());                               \
+}
+
+#define DECLARE_REGISTRY_APPID_RESOURCEID_EX(resid, appid)                  \
+  static LPCOLESTR GetAppId() throw() {                                     \
+    static const CStringW app_id(appid);                                    \
+    return app_id;                                                          \
+  }                                                                         \
+  static LPCTSTR GetAppIdT() throw() {                                      \
+    return GetAppId();                                                      \
+  }                                                                         \
+  static HRESULT WINAPI UpdateRegistryAppId(BOOL reg) throw() {             \
+    return _ATL_REGMAP_ENTRYKeeper::UpdateRegistryFromResourceEx(           \
+        resid, (reg), _GetRegistryMap());                                   \
+  }
+// END registry map
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_ATLREGMAPEX_H__
+
diff --git a/base/accounts.cc b/base/accounts.cc
new file mode 100644
index 0000000..d196fb1
--- /dev/null
+++ b/base/accounts.cc
@@ -0,0 +1,123 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Enumeration of the user accounts on the PC.
+
+#include "omaha/base/accounts.h"
+
+#include <sddl.h>
+#include "base/basictypes.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/reg_key.h"
+
+namespace omaha {
+
+namespace accounts {
+
+const wchar_t kActiveProfilesKey[] =
+    L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList";
+
+HRESULT GetAllUserSids(CSimpleArray<CString> *sid_array) {
+  ASSERT(sid_array, (L""));
+
+  RegKey key_profiles;
+  HRESULT hr = key_profiles.Open(HKEY_LOCAL_MACHINE, kActiveProfilesKey);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  sid_array->RemoveAll();
+
+  uint32 total_keys = key_profiles.GetSubkeyCount();
+
+  for (uint32 i = 0 ; i < total_keys ; ++i) {
+    CString possible_user_sid, name, domain;
+    SID_NAME_USE user_type;
+
+    if (SUCCEEDED(key_profiles.GetSubkeyNameAt(i, &possible_user_sid))) {
+      if (SUCCEEDED(GetUserInfo(possible_user_sid, &name,
+                                &domain, &user_type)) &&
+          user_type == SidTypeUser) {
+        sid_array->Add(possible_user_sid);
+      }
+    }
+  }
+
+  return hr;
+}
+
+HRESULT GetUserInfo(const wchar_t *sid_str, CString *name,
+                    CString *domain, SID_NAME_USE *user_type) {
+  ASSERT(sid_str, (L""));
+  ASSERT(name, (L""));
+  ASSERT(domain, (L""));
+  ASSERT(user_type, (L""));
+
+  PSID sid = NULL;
+  HRESULT ret = E_FAIL;
+  if (ConvertStringSidToSid(sid_str, &sid)) {
+    DWORD name_size = 0, domain_size = 0;
+    if (!LookupAccountSid(NULL, sid, NULL, &name_size, NULL,
+                          &domain_size, user_type) &&
+        ERROR_INSUFFICIENT_BUFFER != GetLastError()) {
+      ret = GetCurError();
+      LocalFree(sid);
+      return ret;
+    }
+
+    ASSERT(name_size, (L""));
+    ASSERT(domain_size, (L""));
+    if (!domain_size || !name_size) {
+      LocalFree(sid);
+      return E_UNEXPECTED;
+    }
+
+    wchar_t* c_name = new wchar_t[name_size];
+    ASSERT(c_name, (L""));
+    if (!c_name) {
+      LocalFree(sid);
+      return E_OUTOFMEMORY;
+    }
+
+    wchar_t* c_domain = new wchar_t[domain_size];
+    ASSERT(c_domain, (L""));
+    if (!c_domain) {
+      delete[] c_name;
+      LocalFree(sid);
+      return E_OUTOFMEMORY;
+    }
+
+    if (LookupAccountSid(NULL, sid, c_name, &name_size, c_domain,
+                         &domain_size, user_type)) {
+      ret = S_OK;
+      name->SetString(c_name);
+      domain->SetString(c_domain);
+    } else {
+      ret = GetCurError();
+    }
+
+    delete[] c_name;
+    delete[] c_domain;
+    LocalFree(sid);
+  }
+
+  return ret;
+}
+
+}  // namespace accounts
+
+}  // namespace omaha
+
diff --git a/common/accounts.h b/base/accounts.h
similarity index 100%
rename from common/accounts.h
rename to base/accounts.h
diff --git a/base/app_util.cc b/base/app_util.cc
new file mode 100644
index 0000000..81b7080
--- /dev/null
+++ b/base/app_util.cc
@@ -0,0 +1,252 @@
+// Copyright 2003-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/app_util.h"
+#include <shlwapi.h>
+#include <atlsecurity.h>
+#include <vector>
+#include "omaha/base/cgi.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/file_ver.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+namespace app_util {
+
+HMODULE GetModuleHandleFromAddress(void* address) {
+  MEMORY_BASIC_INFORMATION mbi = {0};
+  DWORD result = ::VirtualQuery(address, &mbi, sizeof(mbi));
+  ASSERT1(result == sizeof(mbi));
+  return static_cast<HMODULE>(mbi.AllocationBase);
+}
+
+HMODULE GetCurrentModuleHandle() {
+  return GetModuleHandleFromAddress(GetCurrentModuleHandle);
+}
+
+
+CString GetModulePath(HMODULE module_handle) {
+  ASSERT1(IsModuleHandleValid(module_handle));
+  CString mod_path;
+
+  DWORD result = ::GetModuleFileName(module_handle,
+                                     mod_path.GetBufferSetLength(MAX_PATH),
+                                     MAX_PATH);
+  mod_path.ReleaseBuffer();
+  ASSERT1(result == static_cast<DWORD>(mod_path.GetLength()));
+
+  return mod_path;
+}
+
+CString GetModuleDirectory(HMODULE module_handle) {
+  ASSERT1(IsModuleHandleValid(module_handle));
+  return GetDirectoryFromPath(GetModulePath(module_handle));
+}
+
+CString GetModuleName(HMODULE module_handle) {
+  ASSERT1(IsModuleHandleValid(module_handle));
+  CString app_name(GetFileFromPath(GetModulePath(module_handle)));
+
+  UTIL_LOG(L5, (_T("[GetModuleName][module 0x%08x][path '%s'][name '%s']"),
+                module_handle, GetModulePath(module_handle), app_name));
+
+  return app_name;
+}
+
+CString GetModuleNameWithoutExtension(HMODULE module_handle) {
+  ASSERT1(IsModuleHandleValid(module_handle));
+  CString module_name(GetPathRemoveExtension(GetModuleName(module_handle)));
+
+  UTIL_LOG(L5, (_T("[GetModuleNameWithoutExtension]")
+                _T("[module 0x%08x][module '%s'][name '%s']"),
+                module_handle, GetModulePath(module_handle), module_name));
+
+  return module_name;
+}
+
+CString GetCurrentModulePath() {
+  return GetModulePath(GetCurrentModuleHandle());
+}
+
+CString GetCurrentModuleDirectory() {
+  return GetModuleDirectory(GetCurrentModuleHandle());
+}
+
+CString GetCurrentModuleName() {
+  return GetModuleName(GetCurrentModuleHandle());
+}
+
+CString GetCurrentModuleNameWithoutExtension() {
+  return GetModuleNameWithoutExtension(GetCurrentModuleHandle());
+}
+
+bool IsAddressInCurrentModule(void* address) {
+  return GetCurrentModuleHandle() == GetModuleHandleFromAddress(address);
+}
+
+CString GetHostName() {
+  CString hostName;
+  DWORD name_len = MAX_COMPUTERNAME_LENGTH + 1;
+  bool result = !!::GetComputerName(hostName.GetBufferSetLength(name_len),
+                                    &name_len);
+  ASSERT1(result);
+  hostName.ReleaseBuffer();
+  ASSERT1(name_len == static_cast<DWORD>(hostName.GetLength()));
+
+  return hostName;
+}
+
+CString GetWindowsDir() {
+  CString windows_path;
+
+  DWORD result = ::GetWindowsDirectory(
+      windows_path.GetBufferSetLength(MAX_PATH), MAX_PATH);
+  windows_path.ReleaseBuffer();
+  ASSERT1(result == static_cast<DWORD>(windows_path.GetLength()));
+
+  return windows_path;
+}
+
+CString GetSystemDir() {
+  CString systemPath;
+
+  DWORD result = ::GetSystemDirectory(systemPath.GetBufferSetLength(MAX_PATH),
+                                      MAX_PATH);
+  systemPath.ReleaseBuffer();
+  ASSERT1(result == static_cast<DWORD>(systemPath.GetLength()));
+
+  return systemPath;
+}
+
+CString GetTempDir() {
+  CString tempPath;
+
+  DWORD result = ::GetTempPath(MAX_PATH, tempPath.GetBufferSetLength(MAX_PATH));
+  tempPath.ReleaseBuffer();
+  ASSERT1(result == static_cast<DWORD>(tempPath.GetLength()));
+
+  return tempPath;
+}
+
+bool IsModuleHandleValid(HMODULE module_handle) {
+  if (!module_handle) {
+    return true;
+  }
+  return module_handle == GetModuleHandleFromAddress(module_handle);
+}
+
+DWORD DllGetVersion(const CString& dll_path)  {
+  HINSTANCE hInst = ::GetModuleHandle(dll_path);
+  ASSERT(hInst,
+         (_T("[GetModuleHandle failed][%s][%d]"), dll_path, ::GetLastError()));
+  DWORD dwVersion = 0;
+  DLLGETVERSIONPROC pfn = reinterpret_cast<DLLGETVERSIONPROC>(
+                              ::GetProcAddress(hInst, "DllGetVersion"));
+  if (pfn != NULL) {
+    DLLVERSIONINFO dvi = {0};
+    dvi.cbSize = sizeof(dvi);
+    HRESULT hr = (*pfn)(&dvi);
+    if (SUCCEEDED(hr)) {
+      // Since we're fitting both the major and minor versions into a DWORD,
+      // let's sanity check that we're not in an overflow situation here
+      ASSERT1(dvi.dwMajorVersion <= 0xFFFF);
+      ASSERT1(dvi.dwMinorVersion <= 0xFFFF);
+      dwVersion = MAKELONG(dvi.dwMinorVersion, dvi.dwMajorVersion);
+    }
+  }
+  return dwVersion;
+}
+
+DWORD SystemDllGetVersion(const TCHAR* dll_name)  {
+  ASSERT1(dll_name);
+  CString full_dll_path(String_MakeEndWith(GetSystemDir(), _T("\\"), false) +
+                        dll_name);
+  ASSERT1(File::Exists(full_dll_path));
+  return DllGetVersion(full_dll_path);
+}
+
+ULONGLONG GetVersionFromModule(HMODULE instance) {
+  TCHAR module_path[MAX_PATH] = {0};
+  if (!::GetModuleFileName(instance, module_path, MAX_PATH) != 0) {
+    return 0;
+  }
+
+  return GetVersionFromFile(module_path);
+}
+
+ULONGLONG GetVersionFromFile(const CString& file_path) {
+  FileVer existing_file_ver;
+  if (!existing_file_ver.Open(file_path)) {
+    return 0;
+  }
+
+  return existing_file_ver.GetFileVersionAsULONGLONG();
+}
+
+// Returns a temporary dir for the impersonated user and an empty string if
+// the user is not impersonated or an error occurs.
+CString GetTempDirForImpersonatedUser() {
+  CAccessToken access_token;
+  if (!access_token.GetThreadToken(TOKEN_READ)) {
+    return NULL;
+  }
+
+  CString temp_dir;
+  if (::ExpandEnvironmentStringsForUser(access_token.GetHandle(),
+                                        _T("%TMP%"),
+                                        CStrBuf(temp_dir, MAX_PATH),
+                                        MAX_PATH)) {
+    return temp_dir;
+  }
+  if (::ExpandEnvironmentStringsForUser(access_token.GetHandle(),
+                                        _T("%TEMP%"),
+                                        CStrBuf(temp_dir, MAX_PATH),
+                                        MAX_PATH)) {
+    return temp_dir;
+  }
+
+  const int kCsIdl = CSIDL_LOCAL_APPDATA | CSIDL_FLAG_DONT_VERIFY;
+  if (SUCCEEDED(GetFolderPath(kCsIdl, &temp_dir)) &&
+    ::PathAppend(CStrBuf(temp_dir, MAX_PATH), LOCAL_APPDATA_REL_TEMP_DIR)) {
+    return temp_dir;
+  }
+
+  return NULL;
+}
+
+
+CString GetTempDirForImpersonatedOrCurrentUser() {
+  CString temp_dir_for_impersonated_user(GetTempDirForImpersonatedUser());
+  if (!temp_dir_for_impersonated_user.IsEmpty()) {
+    CStrBuf string_buffer(temp_dir_for_impersonated_user, MAX_PATH);
+    if (::PathAddBackslash(string_buffer)) {
+      return temp_dir_for_impersonated_user;
+    }
+  }
+  return app_util::GetTempDir();
+}
+
+}  // namespace app_util
+
+}  // namespace omaha
+
diff --git a/base/app_util.h b/base/app_util.h
new file mode 100644
index 0000000..e4d103a
--- /dev/null
+++ b/base/app_util.h
@@ -0,0 +1,128 @@
+// Copyright 2003-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Utility functions for getting app and module information.
+
+#ifndef OMAHA_BASE_APP_UTIL_H_
+#define OMAHA_BASE_APP_UTIL_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+#include "omaha/base/constants.h"
+
+namespace omaha {
+
+namespace app_util {
+
+// Gets the handle to the module containing the given executing address.
+HMODULE GetModuleHandleFromAddress(void* address);
+
+// Gets the handle to the currently executing module.
+HMODULE GetCurrentModuleHandle();
+
+// Gets the path of the loaded module.
+// If module_handle == NULL returns the path of the current executable.
+CString GetModulePath(HMODULE module_handle);
+
+// Gets the directory of the specified module
+// Returns the part of the module path, before the last '\'.
+// Returns the dir from where the module was loaded (could be exe or dll).
+CString GetModuleDirectory(HMODULE module_handle);
+
+// Gets the name of the specified module
+// Returns the part of the module path, after the last '\'.
+CString GetModuleName(HMODULE module_handle);
+
+// Gets the name of the specified module without the extension.
+CString GetModuleNameWithoutExtension(HMODULE module_handle);
+
+// Gets the current app name (i.e. exe name).
+CString GetAppName();
+
+// Gets the current app name without the extension.
+CString GetAppNameWithoutExtension();
+
+// Gets the current module path
+// returns the path from where the module was loaded (could be exe or dll).
+CString GetCurrentModulePath();
+
+// Gets the current module directory
+// returns the dir from where the module was loaded (could be exe or dll).
+CString GetCurrentModuleDirectory();
+
+// Gets the current module name.
+CString GetCurrentModuleName();
+
+// Gets the current module name without the extension.
+CString GetCurrentModuleNameWithoutExtension();
+
+// Checks if the given address is in the current module.
+bool IsAddressInCurrentModule(void* address);
+
+// Gets the host machine name.
+CString GetHostName();
+
+// Gets the Windows directory.
+CString GetWindowsDir();
+
+// Gets the System directory.
+CString GetSystemDir();
+
+// Gets the TEMP directory for the current user. The directory path ends
+// with a '\'.
+CString GetTempDir();
+
+// Gets a temporary directory for the impersonated user. Returns the temporary
+// directory of the process if the caller is not impersonated or an error
+// occurs. The directory path ends with a '\'.
+CString GetTempDirForImpersonatedOrCurrentUser();
+
+// Helper that gets us the version of a DLL in a DWORD format,
+// with the major and minor versions squeezed into it.
+DWORD DllGetVersion(const CString& dll_path);
+
+// Helper that gets us the version of a System DLL in a DWORD format,
+// with the major and minor versions squeezed into it. The assumption
+// is that the dll_name is only a name, and not a path. Using this
+// function (over DllGetVersion directly) for System DLLs is recommended
+// from a security perspective.
+// However, this may not work for DLLs that are loaded from the side-by-side
+// location (WinSxS) instead of the system directory.
+DWORD SystemDllGetVersion(const TCHAR* dll_name);
+
+// Gets the version from a module.
+ULONGLONG GetVersionFromModule(HMODULE instance);
+
+// Gets the version from a file path.
+ULONGLONG GetVersionFromFile(const CString& file_path);
+
+// Helper to check if a module handle is valid.
+bool IsModuleHandleValid(HMODULE module_handle);
+
+inline CString GetAppName() {
+  return GetModuleName(NULL);
+}
+
+inline CString GetAppNameWithoutExtension() {
+  return GetModuleNameWithoutExtension(NULL);
+}
+
+}  // namespace app_util
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_APP_UTIL_H_
+
diff --git a/base/app_util_unittest.cc b/base/app_util_unittest.cc
new file mode 100644
index 0000000..ca938f0
--- /dev/null
+++ b/base/app_util_unittest.cc
@@ -0,0 +1,147 @@
+// Copyright 2004-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <atlpath.h>
+#include "omaha/base/app_util.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/file.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/testing/unit_test.h"
+
+const TCHAR* const kKernel32Name  = L"kernel32.dll";
+const TCHAR* const kShell32Name   = L"shell32.dll";
+const TCHAR* const kComCtl32Name  = L"comctl32.dll";
+
+namespace omaha {
+
+namespace app_util {
+
+TEST(AppUtilTest, AppUtil) {
+  HMODULE module = NULL;
+  CString name;
+  struct Local {
+    static void Func() {}
+  };
+
+  // First test the functionality for EXE applications.
+
+  // Test the app name.
+  name = GetAppName();
+  EXPECT_STREQ(kUnittestName, name);
+
+  // Test the module name.
+  name = GetCurrentModuleName();
+  EXPECT_STREQ(kUnittestName, name);
+
+  // Test the app name w/o extension.
+  name = GetAppNameWithoutExtension() + L".exe";
+  EXPECT_STREQ(kUnittestName, name);
+
+  // Test the module name w/o extension.
+  name = GetCurrentModulePath();
+  EXPECT_STREQ(GetCurrentModuleDirectory() + L"\\" + kUnittestName, name);
+
+  // Test the module path and directory.
+  name = GetCurrentModuleName();
+  EXPECT_STREQ(kUnittestName, name);
+
+  // Test an address.
+  module = GetCurrentModuleHandle();
+  EXPECT_TRUE(IsAddressInCurrentModule(module + 0x1));
+  EXPECT_TRUE(IsAddressInCurrentModule(&Local::Func));
+  EXPECT_FALSE(IsAddressInCurrentModule(reinterpret_cast<void*>(0x1)));
+
+  // Test the functionality for DLL modules.
+  // Use kernel32.dll
+
+  // Get the loading address of kernel32.
+  HMODULE kernel32Module = ::LoadLibrary(kKernel32Name);
+  EXPECT_TRUE(kernel32Module != NULL);
+
+  // Test the dll module handle using an address.
+  module = GetModuleHandleFromAddress(::ReadFile);
+  EXPECT_EQ(kernel32Module, module);
+
+  CString system_path;
+  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_SYSTEMX86, &system_path));
+
+  // Test the dll module directory.
+  name = GetModuleDirectory(module);
+  EXPECT_EQ(0, name.CompareNoCase(system_path));
+
+  // Test the dll module path.
+  name = GetModulePath(module);
+  EXPECT_EQ(0, name.CompareNoCase(system_path + L"\\" + kKernel32Name));
+
+  // Test the dll module name.
+  name = GetModuleName(module);
+  EXPECT_EQ(0, name.CompareNoCase(kKernel32Name));
+
+  // Other checks.
+  EXPECT_FALSE(GetWindowsDir().IsEmpty());
+  EXPECT_FALSE(GetHostName().IsEmpty());
+  EXPECT_FALSE(GetTempDir().IsEmpty());
+  EXPECT_TRUE(String_EndsWith(GetTempDir(), _T("\\"), false));
+
+  // DLL versioning.
+  // For the tests to succeed, shell32.dll must be loaded in memory.
+  HMODULE shell32Module = ::LoadLibrary(kShell32Name);
+  EXPECT_NE(0, DllGetVersion(GetSystemDir() + L"\\" + kShell32Name));
+  EXPECT_NE(0, DllGetVersion(kShell32Name));
+  EXPECT_NE(0, SystemDllGetVersion(kShell32Name));
+
+  // For the tests to succeed, comctl32.dll must be loaded in memory.
+  // ComCtl32 may be loaded from a side-by-side (WinSxS) directory, so it is not
+  // practical to do a full-path or SystemDllGetVersion test with it.
+  HMODULE comctl32_module = ::LoadLibrary(kComCtl32Name);
+  EXPECT_NE(0, DllGetVersion(kComCtl32Name));
+
+  // kernel32 does not export DllGetVersion.
+  EXPECT_EQ(0, SystemDllGetVersion(kKernel32Name));
+
+  // Module clean-up.
+  EXPECT_TRUE(::FreeLibrary(comctl32_module));
+  EXPECT_TRUE(::FreeLibrary(shell32Module));
+  EXPECT_TRUE(::FreeLibrary(kernel32Module));
+}
+
+TEST(AppUtilTest, GetVersionFromModule) {
+  EXPECT_EQ(OMAHA_BUILD_VERSION, GetVersionFromModule(NULL));
+}
+
+TEST(AppUtilTest, GetVersionFromFile) {
+  CPath goopdate_path(GetCurrentModuleDirectory());
+  ASSERT_TRUE(goopdate_path.Append(kUnittestName));
+  ASSERT_TRUE(File::Exists(goopdate_path));
+
+  EXPECT_EQ(OMAHA_BUILD_VERSION, GetVersionFromFile(goopdate_path));
+}
+
+TEST(AppUtilTest, GetTempDirForImpersonatedOrCurrentUser) {
+  // The behavior should be the same when the code is not running impersonated.
+  EXPECT_STREQ(GetTempDir(), GetTempDirForImpersonatedOrCurrentUser());
+
+  // The behavior should be the same when the code impersonates as self.
+  EXPECT_TRUE(::ImpersonateSelf(SecurityImpersonation));
+  EXPECT_STREQ(GetTempDir(), GetTempDirForImpersonatedOrCurrentUser());
+
+  EXPECT_TRUE(::RevertToSelf());
+}
+
+}  // namespace app_util
+
+}  // namespace omaha
diff --git a/base/apply_tag.cc b/base/apply_tag.cc
new file mode 100644
index 0000000..e6bc6e8
--- /dev/null
+++ b/base/apply_tag.cc
@@ -0,0 +1,244 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Applies a tag to a signed file.
+
+#include "omaha/base/apply_tag.h"
+#include <atlrx.h>
+#include <vector>
+#include "base/scoped_ptr.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/extractor.h"
+
+namespace omaha {
+
+const char kMagicBytes[] = "Gact";
+const uint32 kPEHeaderOffset = 60;
+
+ApplyTag::ApplyTag()
+    : prev_tag_string_length_(0),
+      prev_cert_length_(0),
+      append_(0) {}
+
+bool ApplyTag::IsValidTagString(const char* tag_string) {
+  ASSERT1(tag_string);
+
+  CAtlRegExp<CAtlRECharTraitsA> regex;
+  REParseError error = regex.Parse(kValidTagStringRegEx);
+  if (error != REPARSE_ERROR_OK) {
+    return false;
+  }
+
+  CAtlREMatchContext<CAtlRECharTraitsA> context;
+  return !!regex.Match(tag_string, &context);
+}
+
+HRESULT ApplyTag::Init(const TCHAR* signed_exe_file,
+                       const char* tag_string,
+                       int tag_string_length,
+                       const TCHAR* tagged_file,
+                       bool append) {
+  ASSERT1(signed_exe_file);
+  ASSERT1(tag_string);
+  ASSERT1(tagged_file);
+
+  signed_exe_file_ = signed_exe_file;
+  tagged_file_ = tagged_file;
+  append_ = append;
+
+  // Check the tag_string for invalid characters.
+  if (!IsValidTagString(tag_string)) {
+    return E_INVALIDARG;
+  }
+
+  for (int i = 0; i < tag_string_length; ++i) {
+    tag_string_.push_back(tag_string[i]);
+  }
+
+  return S_OK;
+}
+
+HRESULT ApplyTag::EmbedTagString() {
+  std::vector<byte> input_file_buffer;
+  HRESULT hr = ReadEntireFile(signed_exe_file_, 0, &input_file_buffer);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  ASSERT1(!input_file_buffer.empty());
+  VERIFY1(ReadExistingTag(&input_file_buffer));
+  if (!append_ && prev_tag_string_length_) {
+    // If there is a previous tag and the append flag is not set, then
+    // we should error out.
+    return APPLYTAG_E_ALREADY_TAGGED;
+  }
+
+  if (!CreateBufferToWrite()) {
+    return E_FAIL;
+  }
+
+  // The input_file_buffer might contain the previously read tag, in which
+  // case the buffer_data_ is larger than the actual output buffer length.
+  // The real output buffer length is returned by the ApplyTagToBuffer
+  // method.
+  buffer_data_.resize(input_file_buffer.size() + tag_buffer_.size());
+
+  copy(input_file_buffer.begin(),
+       input_file_buffer.end(),
+       buffer_data_.begin());
+
+  int output_length = 0;
+  if (!ApplyTagToBuffer(&output_length))
+    return E_FAIL;
+
+  std::vector<byte> output_buffer(output_length);
+  ASSERT1(static_cast<size_t>(output_length) <= buffer_data_.size());
+  copy(buffer_data_.begin(),
+       buffer_data_.begin() + output_length,
+       output_buffer.begin());
+  return WriteEntireFile(tagged_file_, output_buffer);
+}
+
+uint32 ApplyTag::GetUint32(const void* p) {
+  ASSERT1(p);
+
+  const uint32* pu = reinterpret_cast<const uint32*>(p);
+  return *pu;
+}
+
+void ApplyTag::PutUint32(uint32 i, void* p) {
+  ASSERT1(p);
+
+  uint32* pu = reinterpret_cast<uint32*>(p);
+  *pu = i;
+}
+
+bool ApplyTag::ReadExistingTag(std::vector<byte>* binary) {
+  ASSERT1(binary);
+
+  int len = 0;
+  TagExtractor tag;
+  char* bin = reinterpret_cast<char*>(&binary->front());
+  ASSERT1(bin);
+  if (tag.ExtractTag(bin, binary->size(), NULL, &len)) {
+    prev_tag_string_.resize(len);
+    if (tag.ExtractTag(bin, binary->size(), &prev_tag_string_.front(), &len)) {
+      // The extractor returns the actual length
+      // of the string + 1 for the terminating null.
+      prev_tag_string_length_ = len - 1;
+    }
+  }
+
+  // Set the existing certificate length even if previous
+  // tag does not exist.
+  prev_cert_length_ = tag.cert_length();
+  return true;
+}
+
+bool ApplyTag::CreateBufferToWrite() {
+  ASSERT1(!append_ && !prev_tag_string_length_ || append_);
+  ASSERT1(!tag_string_.empty());
+  ASSERT1(!prev_tag_string_.size() ||
+          prev_tag_string_.size() ==
+          static_cast<size_t>(prev_tag_string_length_ + 1));
+
+  // Build the tag buffer.
+  // The format of the tag buffer is:
+  // 000000-000003: 4-byte magic (big-endian)
+  // 000004-000005: unsigned 16-bit int string length (big-endian)
+  // 000006-??????: ASCII string
+  int tag_string_len = tag_string_.size() + prev_tag_string_length_;
+  int kMagicBytesLen = ::lstrlenA(kMagicBytes);
+  int tag_header_len = kMagicBytesLen + 2;
+  int unpadded_tag_buffer_len = tag_string_len + tag_header_len;
+  // The tag buffer should be padded to multiples of 8, otherwise it will
+  // break the signature of the executable file.
+  int padded_tag_buffer_length = (unpadded_tag_buffer_len + 15) & (-8);
+
+  tag_buffer_.clear();
+  tag_buffer_.resize(padded_tag_buffer_length, 0);
+  memcpy(&tag_buffer_.front(), kMagicBytes, kMagicBytesLen);
+  tag_buffer_[kMagicBytesLen] =
+      static_cast<char>((tag_string_len & 0xff00) >> 8);
+  tag_buffer_[kMagicBytesLen+1] = static_cast<char>(tag_string_len & 0xff);
+
+  if (prev_tag_string_length_ > 0) {
+    copy(prev_tag_string_.begin(),
+         prev_tag_string_.end(),
+         tag_buffer_.begin() + tag_header_len);
+  }
+
+  copy(tag_string_.begin(),
+       tag_string_.end(),
+       tag_buffer_.begin() + tag_header_len + prev_tag_string_length_);
+  ASSERT1(static_cast<int>(tag_buffer_.size()) == padded_tag_buffer_length);
+
+  return true;
+}
+
+bool ApplyTag::ApplyTagToBuffer(int* output_len) {
+  ASSERT1(output_len);
+
+  uint32 original_data_len = buffer_data_.size() - tag_buffer_.size();
+  uint32 peheader = GetUint32(&buffer_data_.front() + kPEHeaderOffset);
+  uint32 kCertDirAddressOffset = 152;
+  uint32 kCertDirInfoSize = 4 + 4;
+
+  ASSERT1(peheader + kCertDirAddressOffset + kCertDirInfoSize <=
+          original_data_len);
+
+  // Read certificate directory info.
+  uint32 cert_dir_offset = GetUint32(&buffer_data_.front() + peheader +
+                                     kCertDirAddressOffset);
+  if (cert_dir_offset == 0)
+    return false;
+  uint32 cert_dir_len = GetUint32(&buffer_data_.front() + peheader +
+                                  kCertDirAddressOffset + 4);
+  ASSERT1(cert_dir_offset + cert_dir_len <= original_data_len);
+
+  // Calculate the new output length.
+  int prev_pad_length = cert_dir_len - prev_cert_length_ -
+                        prev_tag_string_length_;
+  ASSERT1(prev_pad_length >= 0);
+  int orig_dir_len = cert_dir_len - prev_tag_string_length_ -
+                     prev_pad_length;
+  ASSERT1(orig_dir_len == prev_cert_length_);
+  int output_length = original_data_len - prev_tag_string_length_ -
+                      prev_pad_length + tag_buffer_.size();
+  *output_len = output_length;
+  ASSERT1(static_cast<size_t>(output_length) <= buffer_data_.size());
+  ASSERT1(output_length >= orig_dir_len);
+
+  // Increase the size of certificate directory.
+  int new_cert_len = prev_cert_length_ + tag_buffer_.size();
+  PutUint32(new_cert_len,
+            &buffer_data_.front() + peheader + kCertDirAddressOffset + 4);
+
+  // Read certificate struct info.
+  uint32 cert_struct_len = GetUint32(&buffer_data_.front() + cert_dir_offset);
+  ASSERT1(!(cert_struct_len > cert_dir_len ||
+            cert_struct_len < cert_dir_len - 8));
+
+  // Increase the certificate struct size.
+  PutUint32(new_cert_len, &buffer_data_.front() + cert_dir_offset);
+
+  // Copy the tag buffer.
+  copy(tag_buffer_.begin(), tag_buffer_.end(),
+       buffer_data_.begin() + cert_dir_offset + prev_cert_length_);
+
+  return true;
+}
+
+}  // namespace omaha
diff --git a/base/apply_tag.h b/base/apply_tag.h
new file mode 100644
index 0000000..bd2aa0f
--- /dev/null
+++ b/base/apply_tag.h
@@ -0,0 +1,97 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_APPLY_TAG_H__
+#define OMAHA_COMMON_APPLY_TAG_H__
+
+#include <atlbase.h>
+#include <atlstr.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/error.h"
+
+namespace omaha {
+
+// This regular expression represents the valid characters allowed in the
+// binary tag.
+// When changing this regular expression make sure that string patterns on
+// the server are also updated.
+const char* const kValidTagStringRegEx = "^[-%{}/&=.,_a-zA-Z0-9_]*$";
+
+// Stamps the tag_string into the signed_exe_file.
+// Appends the tag_string to the existing tag if append is
+// true, else errors out. Note that we do not support a
+// overwrite.
+// The tagging is done by adding bytes to the signature
+// directory in the PE flie.
+// The modified signature directory looks something like this
+// <Signature>Gact.<tag_len><tag_string>
+// There are no restrictions on the tag_string, it is just treated
+// as a sequence of bytes.
+class ApplyTag {
+ public:
+  ApplyTag();
+  HRESULT Init(const TCHAR* signed_exe_file,
+               const char* tag_string,
+               int tag_string_length,
+               const TCHAR* tagged_file,
+               bool append);
+  HRESULT EmbedTagString();
+
+ private:
+  static uint32 GetUint32(const void* p);
+  static void PutUint32(uint32 i, void* p);
+  bool ReadExistingTag(std::vector<byte>* binary);
+  bool CreateBufferToWrite();
+  bool ApplyTagToBuffer(int* output_len);
+  bool IsValidTagString(const char* tag_string);
+
+  // The string to be tagged into the binary.
+  std::vector<char> tag_string_;
+
+  // Existing tag string inside the binary.
+  std::vector<char> prev_tag_string_;
+
+  // This is prev_tag_string_.size - 1, to exclude the terminating null.
+  int prev_tag_string_length_;
+
+  // Length of the certificate inside the binary.
+  int prev_cert_length_;
+
+  // The input binary to be tagged.
+  CString signed_exe_file_;
+
+  // The output binary name.
+  CString tagged_file_;
+
+  // Whether to append the tag string to the existing one.
+  bool append_;
+
+  // Internal buffer to hold the appended string.
+  std::vector<char> tag_buffer_;
+
+  // The output buffer that contains the original binary
+  // data with the tagged information.
+  std::vector<char> buffer_data_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ApplyTag);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_APPLY_TAG_H__
diff --git a/base/atl_regexp.cc b/base/atl_regexp.cc
new file mode 100644
index 0000000..4c5693c
--- /dev/null
+++ b/base/atl_regexp.cc
@@ -0,0 +1,70 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+
+#include "omaha/base/atl_regexp.h"
+
+namespace omaha {
+
+const int kMaxArgs  = 16;
+
+AtlRE::AtlRE(const TCHAR* pattern, bool case_sensitive) {
+  ASSERT(pattern, (L""));
+  REParseError status = re_.Parse(pattern, case_sensitive);
+  ASSERT(status == REPARSE_ERROR_OK, (L""));
+}
+
+AtlRE::~AtlRE() {
+}
+
+bool AtlRE::DoMatchImpl(const TCHAR* text,
+                        CString* args[],
+                        int n,
+                        const TCHAR** match_end) const {
+  // text may be NULL.
+  ASSERT(args, (L""));
+
+  if (!text) {
+    return false;
+  }
+
+  AtlMatchContext matches;
+  BOOL b = re_.Match(text, &matches, match_end);
+  if (!b || matches.m_uNumGroups < static_cast<uint32>(n)) {
+    return false;
+  }
+
+  // Oddly enough, the Match call will make match_end
+  // point off the end of the string if the result is at the
+  // end of the string. We check this and handle it.
+  if (match_end) {
+    if ((*match_end - text) >= lstrlen(text)) {
+      *match_end = NULL;
+    }
+  }
+
+  const TCHAR* start = 0;
+  const TCHAR* end = 0;
+  for (int i = 0; i < n; ++i) {
+    matches.GetMatch(i, &start, &end);
+    ptrdiff_t len = end - start;
+    ASSERT(args[i], (L""));
+    // len+1 for the NULL character that's placed by lstrlen
+    VERIFY1(lstrcpyn(args[i]->GetBufferSetLength(len), start, len + 1) != NULL);
+  }
+  return true;
+}
+
+}  // namespace omaha
diff --git a/base/atl_regexp.h b/base/atl_regexp.h
new file mode 100644
index 0000000..e7f6f90
--- /dev/null
+++ b/base/atl_regexp.h
@@ -0,0 +1,150 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// ATL Regular expression class that implements the RE interface.
+// See MSDN help for example patterns (lookup help on CAtlRegExp)
+// NOTE: This class adds about 8k to release builds.
+// TODO(omaha): add a unit test, showing examples, testing functionality
+// and perf.
+
+#ifndef OMAHA_COMMON_ATL_REGEXP_H__
+#define OMAHA_COMMON_ATL_REGEXP_H__
+
+#pragma warning(push)
+// enumerator 'identifier' in switch of enum 'enumeration' is not explicitly
+// handled by a case label
+#pragma warning(disable:4061)
+#include <atlrx.h>
+#pragma warning(pop)
+#include <atlstr.h>
+#include "base/basictypes.h"
+#include "omaha/base/regexp.h"
+#include "omaha/base/string.h"
+
+namespace omaha {
+
+// This class is essentially a copy of CAtlRECharTraitsWide from <atlrx.h>, but
+// I've replaced CRT functions that are not in our minicrt.
+//
+// TODO(omaha): do we need this?
+class CAtlRECharTraitsWideNoCrt
+{
+public:
+  typedef WCHAR RECHARTYPE;
+
+  // ATL80 addition.
+  static size_t GetBitFieldForRangeArrayIndex(const RECHARTYPE *sz) throw()
+  {
+#ifndef ATL_NO_CHECK_BIT_FIELD
+    ATLASSERT(UseBitFieldForRange());
+#endif
+    return static_cast<size_t>(*sz);
+  }
+
+  static RECHARTYPE *Next(const RECHARTYPE *sz) throw()
+  {
+    return (RECHARTYPE *) (sz+1);
+  }
+
+  static int Strncmp(const RECHARTYPE *szLeft,
+                     const RECHARTYPE *szRight, size_t nCount) throw()
+  {
+    return String_StrNCmp(szLeft, szRight, nCount,false);
+  }
+
+  static int Strnicmp(const RECHARTYPE *szLeft,
+                      const RECHARTYPE *szRight, size_t nCount) throw()
+  {
+    return String_StrNCmp(szLeft, szRight, nCount,true);
+  }
+
+  static RECHARTYPE *Strlwr(RECHARTYPE *sz) throw()
+  {
+    return String_FastToLower(sz);
+  }
+
+  // In ATL 80 Strlwr must be passed a buffer size for security reasons.
+  // TODO(omaha): Implement the function to consider the nSize param.
+  static RECHARTYPE *Strlwr(RECHARTYPE *sz, int) throw()
+  {
+    return Strlwr(sz);
+  }
+
+  static long Strtol(const RECHARTYPE *sz,
+                     RECHARTYPE **szEnd, int nBase) throw()
+  {
+    return Wcstol(sz, szEnd, nBase);
+  }
+
+  static int Isdigit(RECHARTYPE ch) throw()
+  {
+    return String_IsDigit(ch) ? 1 : 0;
+  }
+
+  static const RECHARTYPE** GetAbbrevs()
+  {
+    static const RECHARTYPE *s_szAbbrevs[] =
+    {
+      L"a([a-zA-Z0-9])",  // alpha numeric
+        L"b([ \\t])",    // white space (blank)
+        L"c([a-zA-Z])",  // alpha
+        L"d([0-9])",    // digit
+        L"h([0-9a-fA-F])",  // hex digit
+        L"n(\r|(\r?\n))",  // newline
+        L"q(\"[^\"]*\")|(\'[^\']*\')",  // quoted string
+        L"w([a-zA-Z]+)",  // simple word
+        L"z([0-9]+)",    // integer
+        NULL
+    };
+
+    return s_szAbbrevs;
+  }
+
+  static BOOL UseBitFieldForRange() throw()
+  {
+    return FALSE;
+  }
+
+  static int ByteLen(const RECHARTYPE *sz) throw()
+  {
+    return int(lstrlen(sz)*sizeof(WCHAR));
+  }
+};
+
+typedef CAtlRegExp<CAtlRECharTraitsWideNoCrt> AtlRegExp;
+typedef CAtlREMatchContext<CAtlRECharTraitsWideNoCrt> AtlMatchContext;
+
+// implements the RE class using the ATL Regular Expressions class
+class AtlRE : public RE {
+ public:
+
+  AtlRE(const TCHAR* pattern, bool case_sensitive = true);
+  virtual ~AtlRE();
+
+ protected:
+  // See regexp.h for an explanation.
+  virtual bool DoMatchImpl(const TCHAR* text,
+                           CString* args[],
+                           int n,
+                           const TCHAR** match_end) const;
+
+ private:
+  mutable AtlRegExp re_;
+  DISALLOW_EVIL_CONSTRUCTORS(AtlRE);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_ATL_REGEXP_H__
diff --git a/base/atl_regexp_unittest.cc b/base/atl_regexp_unittest.cc
new file mode 100644
index 0000000..a1c4261
--- /dev/null
+++ b/base/atl_regexp_unittest.cc
@@ -0,0 +1,39 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/atl_regexp.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(AtlRETest, AtlRE) {
+  AtlRE newline_test_re(_T("ab{\\n}cd"));
+  const TCHAR* newline_strings[] = { _T("\n"), _T("\r"), _T("\r\n") };
+  for (size_t i = 0; i < arraysize(newline_strings); ++i) {
+    CString content(_T("ab"));
+    content.Append(newline_strings[i]);
+    content.Append(_T("cd"));
+    const TCHAR* content_ptr = content.GetString();
+    CString newline;
+    EXPECT_TRUE(RE::FindAndConsume(&content_ptr, newline_test_re, &newline));
+    EXPECT_STREQ(newline, newline_strings[i]);
+  }
+
+  // Check that AtlRE works with Unicode characters.
+  AtlRE one_two_three_four(_T("\x1234"));
+  EXPECT_TRUE(RE::PartialMatch(_T("\x4321\x1234\x4321"), one_two_three_four));
+}
+
+}  // namespace omaha
diff --git a/base/atlassert.h b/base/atlassert.h
new file mode 100644
index 0000000..975955d
--- /dev/null
+++ b/base/atlassert.h
@@ -0,0 +1,76 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Take over ATLASSERT
+//
+
+#ifndef OMAHA_BASE_ATLASSERT_H_
+#define OMAHA_BASE_ATLASSERT_H_
+
+#include <tchar.h>
+
+#ifdef _DEBUG
+#ifndef DEBUG
+#error DEBUG and _DEBUG must be in sync
+#endif
+#endif
+
+namespace omaha {
+
+enum ReportType {
+  R_INFO = 1,   // Not an error, used for accumulating statistics.
+  R_WARNING,    // May or may not be an error.
+  R_ERROR,      // Definitely an error.
+  R_FATAL       // halt program == ASSERT for release mode.
+};
+
+enum DebugReportKind {
+  DEBUGREPORT_NONE   = 0,
+  DEBUGREPORT_ASSERT = 1,
+  DEBUGREPORT_REPORT = 2,
+  DEBUGREPORT_ABORT  = 3
+};
+
+#ifdef DEBUG
+extern "C" bool DebugReport(unsigned int id,
+                            omaha::ReportType type,
+                            const char* expr,
+                            const TCHAR* message,
+                            const char* filename,
+                            int linenumber,
+                            omaha::DebugReportKind debug_report_kind);
+  #ifndef ATLASSERT
+  #define ATLASSERT(expr)                         \
+    do {                                          \
+      if (!(expr)) {                              \
+        DebugReport(0,                            \
+                    omaha::R_FATAL,               \
+                    #expr,                        \
+                    _T("ATL assertion"),          \
+                    __FILE__,                     \
+                    __LINE__,                     \
+                    omaha::DEBUGREPORT_ASSERT);   \
+      }                                           \
+    } while (0)
+  #endif
+#else
+  #ifndef ATLASSERT
+  #define ATLASSERT(expr) ((void)0)
+  #endif
+#endif
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_ATLASSERT_H_
diff --git a/base/atlassert_unittest.cc b/base/atlassert_unittest.cc
new file mode 100644
index 0000000..76cebda
--- /dev/null
+++ b/base/atlassert_unittest.cc
@@ -0,0 +1,28 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/debug.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+// Test what happens when we hit an ATLASSERT within ATL code.
+// The CComPtr expects the parameter to be 0.
+TEST(AtlAssertTest, AtlAssert) {
+  ExpectAsserts expect_asserts;
+  CComPtr<IUnknown> p(1);
+}
+
+}  // namespace omaha
diff --git a/common/atlconvfix.h b/base/atlconvfix.h
similarity index 100%
rename from common/atlconvfix.h
rename to base/atlconvfix.h
diff --git a/base/auto_any.h b/base/auto_any.h
new file mode 100644
index 0000000..3291614
--- /dev/null
+++ b/base/auto_any.h
@@ -0,0 +1,28 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// See the comments in omaha/base/scoped_any.h for details.
+
+#ifndef OMAHA_COMMON_AUTO_ANY_H__
+#define OMAHA_COMMON_AUTO_ANY_H__
+
+#pragma warning(push)
+// C4640: construction of local static object is not thread-safe
+#pragma warning(disable : 4640)
+#include "omaha/third_party/smartany/scoped_any.h"
+#pragma warning(pop)
+
+#endif  // OMAHA_COMMON_AUTO_ANY_H__
+
diff --git a/base/browser_utils.cc b/base/browser_utils.cc
new file mode 100644
index 0000000..c21c30e
--- /dev/null
+++ b/base/browser_utils.cc
@@ -0,0 +1,727 @@
+// Copyright 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <exdisp.h>
+#include "omaha/base/browser_utils.h"
+#include "base/basictypes.h"
+#include "omaha/base/commands.h"
+#include "omaha/base/const_utils.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/process.h"
+#include "omaha/base/proc_utils.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/shell.h"
+#include "omaha/base/string.h"
+#include "omaha/base/system.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/thread.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/base/vista_utils.h"
+
+namespace omaha {
+
+namespace {
+
+// Attempts to force instances of IE to quit gracefully using shell APIs.
+HRESULT CloseIeUsingShell(const CString& sid) {
+  CComPtr<IShellWindows> shell_windows;
+  HRESULT hr = shell_windows.CoCreateInstance(CLSID_ShellWindows);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  long num_windows = 0;  // NOLINT
+  hr = shell_windows->get_Count(&num_windows);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  for (int32 i = 0; i < num_windows; ++i) {
+    CComVariant index(i);
+
+    CComPtr<IDispatch> disp;
+    hr = shell_windows->Item(index, &disp);
+    if (FAILED(hr)) {
+      UTIL_LOG(L3, (_T("[CloseIeUsingShell][Item failed][0x%08x]"), hr));
+      return hr;
+    }
+    if (!disp) {
+      // Skip - this shell window was registered with a NULL IDispatch
+      continue;
+    }
+
+    CComPtr<IWebBrowser2> browser;
+    hr = disp.QueryInterface(&browser);
+    if (FAILED(hr)) {
+      // Skip - this shell window doesn't implement IWebBrowser2
+      continue;
+    }
+
+    // Okay, this window implements IWebBrowser2, so it's potentially an
+    // IE session.  Identify the owning process.
+    long hwnd = 0;  // NOLINT
+    hr = browser->get_HWND(&hwnd);
+    if (FAILED(hr)) {
+      UTIL_LOG(L3, (_T("[CloseIeUsingShell][get_HWND failed][0x%08x]"), hr));
+      continue;
+    }
+
+    DWORD process_id = 0;
+    ::GetWindowThreadProcessId(reinterpret_cast<HWND>(hwnd), &process_id);
+    if (0 == process_id) {
+      UTIL_LOG(L3, (_T("[CloseIeUsingShell][invalid process id]")));
+      continue;
+    }
+
+    // If a SID was passed in, check that the SID owns this process.  (If we
+    // can't query the process owner, play it safe and skip it.)
+    if (!sid.IsEmpty()) {
+      CString process_sid;
+      hr = Process::GetProcessOwner(process_id, &process_sid);
+      if (FAILED(hr)) {
+        UTIL_LOG(L3, (_T("[CloseIeUsingShell][GetProcessOwner][0x%08x]"), hr));
+        continue;
+      }
+
+      if (0 != sid.CompareNoCase(process_sid)) {
+        // Skip - process is not owned by us
+        continue;
+      }
+    }
+
+    // Verify that the process executable is iexplore.exe, as this list may
+    // also contain embedded shdocvw sessions in explorer.exe.  (If we can't
+    // fetch the process executable, play it safe and skip it.)
+    CString process_module;
+    hr = Process::GetExecutablePath(process_id, &process_module);
+    if (FAILED(hr)) {
+      UTIL_LOG(L3, (_T("[CloseIeUsingShell][GetExecutablePath][0x%08x]"), hr));
+      continue;
+    }
+
+    if (0 != GetFileFromPath(process_module).CompareNoCase(kIeExeName)) {
+      // Skip - it's not an iexplore.exe
+      continue;
+    }
+
+    // Okay, this is an IWebBrowser2 hosted by an iexplore.exe that is running
+    // under the requested SID.  Ask it to quit.  (Note: It may not necessarily
+    // honor this request.  We can follow it up with WM_CLOSE messages or a
+    // process termination if absolutely necessary.)
+    UTIL_LOG(L3, (_T("[CloseIeUsingShell][Closing IE session][pid %d]"),
+             process_id));
+
+    hr = browser->Quit();
+    if (FAILED(hr)) {
+      UTIL_LOG(L3, (_T("[CloseIeUsingShell][Quit failed][0x%08x]"), hr));
+      continue;
+    }
+  }
+
+  return S_OK;
+}
+
+class CloseIeUsingShellRunnable : public Runnable {
+ public:
+  explicit CloseIeUsingShellRunnable(const CString& sid) : sid_(sid) {}
+ protected:
+  virtual ~CloseIeUsingShellRunnable() {}
+
+  virtual void Run() {
+    UTIL_LOG(L3, (_T("[CloseIeUsingShellRunnable][%s]"), sid_));
+
+    scoped_co_init co_init(COINIT_MULTITHREADED);
+    VERIFY1(SUCCEEDED(co_init.hresult()));
+
+    CloseIeUsingShell(sid_);
+    delete this;
+  }
+
+ private:
+  CString sid_;
+};
+
+}  // end namespace
+
+HRESULT GetLegacyDefaultBrowserInfo(CString* name, CString* path) {
+  UTIL_LOG(L3, (_T("[GetLegacyDefaultBrowserInfo]")));
+  ASSERT1(name);
+  ASSERT1(path);
+  name->Empty();
+  path->Empty();
+
+  CString browser_command_line;
+  HRESULT hr = RegKey::GetValue(kRegKeyLegacyDefaultBrowserCommand,
+                                NULL,
+                                &browser_command_line);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[Read failed][%s]"), kRegKeyLegacyDefaultBrowserCommand));
+    return hr;
+  }
+
+  UTIL_LOG(L5, (_T("[browser_command_line][%s]"), browser_command_line));
+  browser_command_line.Trim(_T(" "));
+  if (browser_command_line.IsEmpty()) {
+    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+  }
+
+  CString browser_path;
+  if (File::Exists(browser_command_line)) {
+    if (File::IsDirectory(browser_command_line)) {
+      UTIL_LOG(LE, (_T("[Unexpected. Command line should not be directory]")));
+      return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+    }
+
+    browser_path = browser_command_line;
+  } else {
+    hr = GetExePathFromCommandLine(browser_command_line, &browser_path);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    if (!File::Exists(browser_path) ||
+        File::IsDirectory(browser_path)) {
+      UTIL_LOG(LE, (_T("[browser_path invalid][%s]"), browser_path));
+      return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+    }
+  }
+
+  CString browser_name = ::PathFindFileName(browser_path);
+  if (browser_name.IsEmpty() ||
+      !String_EndsWith(browser_name, _T(".exe"), true)) {
+    UTIL_LOG(LE, (_T("[browser name invalid][%s]"), browser_name));
+    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+  }
+
+  *path = browser_path;
+  *name = browser_name;
+  return S_OK;
+}
+
+HRESULT GetDefaultBrowserName(CString* name) {
+  ASSERT1(name);
+  name->Empty();
+
+  // Get the default browser name from current user registry.
+  HKEY user_root_key = NULL;
+  VERIFY1(::RegOpenCurrentUser(KEY_READ, &user_root_key) == ERROR_SUCCESS);
+  RegKey user_default_browser;
+  HRESULT hr = user_default_browser.Open(
+      user_root_key ? user_root_key : HKEY_CURRENT_USER,
+      kRegKeyDefaultBrowser,
+      KEY_READ);
+  if (SUCCEEDED(hr)) {
+    hr = user_default_browser.GetValue(NULL, name);
+  }
+  if (user_root_key) {
+    LONG error(::RegCloseKey(user_root_key));
+
+    // See bug http://b/1231862 for details when RegCloseKey can
+    // return ERROR_INVALID_HANDLE.
+    ASSERT1(error == ERROR_SUCCESS ||
+            error == ERROR_INVALID_HANDLE);
+  }
+
+  if (SUCCEEDED(hr) && !name->IsEmpty()) {
+    UTIL_LOG(L3, (_T("[Default browser for the user is %s]"), *name));
+    return S_OK;
+  }
+
+  // Try to get from local machine registry.
+  hr = RegKey::GetValue(kRegKeyMachineDefaultBrowser, NULL, name);
+  if (SUCCEEDED(hr) && !name->IsEmpty()) {
+    UTIL_LOG(L3, (_T("[Default browser for the machine is %s]"), *name));
+    return S_OK;
+  }
+
+  // Try to get from legacy default browser location.
+  CString browser_path;
+  hr = GetLegacyDefaultBrowserInfo(name, &browser_path);
+  if (SUCCEEDED(hr) && !name->IsEmpty()) {
+    UTIL_LOG(L3, (_T("[Legacy default browser is %s]"), *name));
+    return S_OK;
+  }
+
+  // If still failed, we don't want to break in this case so we default to
+  // IEXPLORE.EXE. A scenario where this can happen is when the user installs
+  // a browser that configures itself to be default. Then the user uninstalls
+  // or somehow removes that browser and the registry is not updated correctly.
+  *name = kIeExeName;
+  return S_OK;
+}
+
+HRESULT GetDefaultBrowserType(BrowserType* type) {
+  ASSERT1(type);
+
+  CString name;
+  HRESULT hr = GetDefaultBrowserName(&name);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (name.CompareNoCase(kIeExeName) == 0) {
+    *type = BROWSER_IE;
+  } else if (name.CompareNoCase(kFirefoxExeName) == 0) {
+    *type = BROWSER_FIREFOX;
+  } else if (name.CompareNoCase(kChromeExeName) == 0 ||
+             name.CompareNoCase(kChromeBrowserName) == 0) {
+    *type = BROWSER_CHROME;
+  } else {
+    *type = BROWSER_UNKNOWN;
+  }
+
+  return S_OK;
+}
+
+// Returns a path that is always unenclosed. The method could return a short or
+// long form path.
+HRESULT GetDefaultBrowserPath(CString* path) {
+  ASSERT1(path);
+  path->Empty();
+  ON_SCOPE_EXIT(UnenclosePath, path);
+
+  // Get the default browser name.
+  CString name;
+  if (FAILED(GetDefaultBrowserName(&name))) {
+    // Try getting IE's path through COM registration and app path entries.
+    return GetBrowserImagePath(BROWSER_IE, path);
+  }
+
+  CString shell_open_path(_T("\\"));
+  shell_open_path += name;
+  shell_open_path += kRegKeyShellOpenCommand;
+
+  // Read the path corresponding to it from current user registry.
+  CString browser_key(kRegKeyUserDefaultBrowser);
+  browser_key += shell_open_path;
+  HRESULT hr = RegKey::GetValue(browser_key, NULL, path);
+
+  CString cmd_line(*path);
+  CString args;
+  if (SUCCEEDED(hr) &&
+      !path->IsEmpty() &&
+      SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(cmd_line,
+                                                           path,
+                                                           &args))) {
+    return S_OK;
+  }
+
+  // If failed, try to get from local machine registry.
+  browser_key = kRegKeyMachineDefaultBrowser;
+  browser_key += shell_open_path;
+  hr = RegKey::GetValue(browser_key, NULL, path);
+
+  cmd_line = *path;
+  args.Empty();
+  if (SUCCEEDED(hr) &&
+      !path->IsEmpty() &&
+      SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(cmd_line,
+                                                           path,
+                                                           &args))) {
+    return S_OK;
+  }
+
+  // Try to get from legacy default browser location.
+  hr = GetLegacyDefaultBrowserInfo(&name, path);
+  if (SUCCEEDED(hr) && !path->IsEmpty()) {
+    return S_OK;
+  }
+
+  // If failed and the default browser is not IE, try IE once again.
+  if (name.CompareNoCase(kIeExeName) != 0) {
+    browser_key = kRegKeyMachineDefaultBrowser
+                  _T("\\") kIeExeName kRegKeyShellOpenCommand;
+    hr = RegKey::GetValue(browser_key, NULL, path);
+
+    cmd_line = *path;
+    args.Empty();
+    if (SUCCEEDED(hr) &&
+        !path->IsEmpty() &&
+        SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(cmd_line,
+                                                             path,
+                                                             &args))) {
+      return S_OK;
+    }
+  }
+
+  // Try getting the default browser's path through COM registration and app
+  // path entries.
+  BrowserType default_type = BROWSER_UNKNOWN;
+  hr = GetDefaultBrowserType(&default_type);
+  if (FAILED(hr) ||
+      default_type == BROWSER_UNKNOWN ||
+      default_type == BROWSER_DEFAULT) {
+    default_type = BROWSER_IE;
+  }
+
+  return GetBrowserImagePath(default_type, path);
+}
+
+HRESULT GetFirefoxDefaultProfile(CString* name, CString* path) {
+  ASSERT1(name);
+  ASSERT1(path);
+
+  const TCHAR kFirefoxAppDataPath[] = _T("\\Mozilla\\Firefox\\");
+  const TCHAR kFirefoxProfileIni[] = _T("profiles.ini");
+  const TCHAR kFirefoxDefaultProfileSecName[] = _T("Profile0");
+  const TCHAR kFirefoxProfileIniNameKey[] = _T("Name");
+  const TCHAR kFirefoxProfileIniIsRelativeKey[] = _T("IsRelative");
+  const TCHAR kFirefoxProfileIniPathKey[] = _T("Path");
+  const TCHAR kFirefoxProfileIniDefaultKey[] = _T("Default");
+
+  name->Empty();
+  path->Empty();
+
+  // Get appdata path for storing Firefox settings.
+  CString appdata_path;
+  RET_IF_FAILED(Shell::GetSpecialFolder(CSIDL_APPDATA, false, &appdata_path));
+  appdata_path += kFirefoxAppDataPath;
+
+  // Get profile.ini.
+  CString profile_ini = appdata_path + kFirefoxProfileIni;
+  UTIL_LOG(L3, (_T("[FireFox profile.ini][%s]"), profile_ini));
+
+  if (!File::Exists(profile_ini)) {
+    UTIL_LOG(LE, (_T("[File does not exist][%s]"), profile_ini));
+    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+  }
+
+  // Read all section names in profile.ini.
+  // The buffer is filled with one or more null-terminated strings; the last
+  // string is followed by a second null character.
+  const int kMaxProfileSecNamesLength = 2048;
+  CString profile_sec_names;
+  DWORD char_returned = ::GetPrivateProfileSectionNames(
+      profile_sec_names.GetBufferSetLength(kMaxProfileSecNamesLength),
+      kMaxProfileSecNamesLength,
+      profile_ini);
+  if (char_returned == kMaxProfileSecNamesLength - 2) {
+    UTIL_LOG(LW, (_T("[FireFox profile.ini contains too many sections]")));
+  }
+  profile_sec_names.ReleaseBuffer(char_returned);
+
+  // Iterate through all the sections to find the default profile.
+  const TCHAR* default_profile_sec = NULL;
+  const TCHAR* ptr = profile_sec_names.GetString();
+  const TCHAR* end = ptr + char_returned;
+  while (ptr < end && *ptr) {
+    if (::GetPrivateProfileInt(ptr,
+                               kFirefoxProfileIniDefaultKey,
+                               0,
+                               profile_ini)) {
+      default_profile_sec = ptr;
+      break;
+    }
+
+    for (; ptr < end && *ptr; ++ptr) {
+    }
+    ++ptr;
+  }
+
+  if (!default_profile_sec) {
+    default_profile_sec = kFirefoxDefaultProfileSecName;
+  }
+
+  DWORD name_len = ::GetPrivateProfileString(default_profile_sec,
+                                             kFirefoxProfileIniNameKey, _T(""),
+                                             name->GetBufferSetLength(256),
+                                             256,
+                                             profile_ini);
+  name->ReleaseBuffer(name_len);
+
+  DWORD path_len = ::GetPrivateProfileString(default_profile_sec,
+                                             kFirefoxProfileIniPathKey,
+                                             _T(""),
+                                             path->GetBufferSetLength(1024),
+                                             1024,
+                                             profile_ini);
+  path->ReleaseBuffer(path_len);
+  path->Replace(_T('/'), _T('\\'));
+
+  bool is_relative = ::GetPrivateProfileInt(default_profile_sec,
+                                            kFirefoxProfileIniIsRelativeKey,
+                                            0,
+                                            profile_ini) != 0;
+
+  if (is_relative && !path->IsEmpty()) {
+    path->Insert(0, appdata_path);
+  }
+
+  return S_OK;
+}
+
+HRESULT BrowserTypeToProcessName(BrowserType type, CString* exe_name) {
+  ASSERT1(exe_name);
+  ASSERT1(type < BROWSER_MAX);
+
+  switch (type) {
+    case BROWSER_IE:
+      *exe_name = kIeExeName;
+      break;
+    case BROWSER_FIREFOX:
+      *exe_name = kFirefoxExeName;
+      break;
+    case BROWSER_CHROME:
+      *exe_name = kChromeExeName;
+      break;
+    case BROWSER_DEFAULT:
+      return GetDefaultBrowserName(exe_name);
+    case BROWSER_UNKNOWN:
+      // Fall through.
+    case BROWSER_MAX:
+      // Fall through.
+    default:
+      return E_FAIL;
+  }
+
+  return S_OK;
+}
+
+// Returns a path that is always unenclosed. The method could return a short or
+// long form path.
+HRESULT GetBrowserImagePath(BrowserType type, CString* path) {
+  ASSERT1(path);
+  ASSERT1(type < BROWSER_MAX);
+
+  HRESULT hr = E_FAIL;
+  switch (type) {
+    case BROWSER_IE: {
+      hr = RegKey::GetValue(kRegKeyIeClass, kRegValueIeClass, path);
+      break;
+    }
+    case BROWSER_FIREFOX: {
+      hr = RegKey::GetValue(kRegKeyFirefox, kRegValueFirefox, path);
+      if (SUCCEEDED(hr) && !path->IsEmpty()) {
+        // The Firefox registry key contains a -url %1 value. Remove this
+        // because we only want to return the path.
+        ReplaceCString(*path, _T("-url \"%1\""), _T(""));
+      }
+      break;
+    }
+    case BROWSER_CHROME: {
+      hr = RegKey::GetValue(kRegKeyChrome, kRegValueChrome, path);
+      if (SUCCEEDED(hr) && !path->IsEmpty()) {
+        // The Chrome registry key contains a -- "%1" value. Remove this because
+        // we only want to return the path.
+        ReplaceCString(*path, _T("-- \"%1\""), _T(""));
+      }
+      break;
+    }
+    case BROWSER_DEFAULT: {
+      hr = GetDefaultBrowserPath(path);
+      if (FAILED(hr)) {
+        UTIL_LOG(LE, (_T("[GetDefaultBrowserPath failed.][0x%08x]"), hr));
+      }
+      path->Trim();
+      UnenclosePath(path);
+      return hr;
+    }
+    case BROWSER_UNKNOWN:
+      // Fall through.
+    case BROWSER_MAX:
+      // Fall through.
+    default:
+      return E_FAIL;
+  }
+
+  CString cmd_line(*path);
+  CString args;
+  if (SUCCEEDED(hr) &&
+      !path->IsEmpty() &&
+      SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(cmd_line,
+                                                           path,
+                                                           &args))) {
+    return S_OK;
+  }
+
+  // If the above did not work, then we try to read the value from
+  // "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths".
+  CString browser_exe_name;
+  hr = BrowserTypeToProcessName(type, &browser_exe_name);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[BrowserTypeToProcessName failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  CString exe_path;
+  hr = Shell::GetApplicationExecutablePath(browser_exe_name, &exe_path);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[GetApplicationExecutablePath failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  *path = ConcatenatePath(exe_path, browser_exe_name);
+  ASSERT1(!path->IsEmpty());
+
+  path->Trim();
+  UnenclosePath(path);
+  return S_OK;
+}
+
+HRESULT TerminateBrowserProcess(BrowserType type,
+                                const CString& sid,
+                                int timeout_msec,
+                                bool* found) {
+  UTIL_LOG(L3, (_T("[TerminateBrowserProcess][%d]"), type));
+  ASSERT1(found);
+  ASSERT1(type < BROWSER_MAX);
+
+  *found = false;
+  if (type == BROWSER_UNKNOWN || type >= BROWSER_MAX) {
+    return E_FAIL;
+  }
+
+  if (type == BROWSER_IE) {
+    Thread close_ie_thread;
+
+    close_ie_thread.Start(new CloseIeUsingShellRunnable(sid));
+    close_ie_thread.WaitTillExit(timeout_msec);
+
+    // Fall through after attempting to close IE via automation - if it
+    // succeeded, we'll find no processes, but if it failed we still want
+    // to attempt a conventional WM_CLOSE quit.  (This also applies to
+    // third-party hosts of IE that may not respect IWebBrowser2::Quit.)
+  }
+
+  CString browser_exe_name;
+  HRESULT hr = BrowserTypeToProcessName(type, &browser_exe_name);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[BrowserTypeToProcessName failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  DWORD current_session = System::GetCurrentSessionId();
+  uint32 method_mask = ProcessTerminator::KILL_METHOD_1_WINDOW_MESSAGE;
+  ProcessTerminator process(browser_exe_name, sid, current_session);
+  hr = process.KillTheProcess(timeout_msec, found, method_mask, true);
+  if (FAILED(hr)) {
+    UTIL_LOG(LEVEL_WARNING, (_T("[KillTheProcess failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT WaitForBrowserToDie(BrowserType type,
+                            const CString& sid,
+                            int timeout_msec) {
+  UTIL_LOG(L3, (_T("[WaitForBrowserToDie][%d]"), type));
+  ASSERT1(type < BROWSER_MAX);
+
+  if (type == BROWSER_UNKNOWN || type >= BROWSER_MAX) {
+    return E_FAIL;
+  }
+
+  CString browser_exe_name;
+  HRESULT hr = BrowserTypeToProcessName(type, &browser_exe_name);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[BrowserTypeToProcessName failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  DWORD current_session = System::GetCurrentSessionId();
+  ProcessTerminator process(browser_exe_name, sid, current_session);
+  hr = process.WaitForAllToDie(timeout_msec);
+  if (FAILED(hr)) {
+    UTIL_LOG(LEVEL_WARNING, (_T("[WaitForAllToDie failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT RunBrowser(BrowserType type, const CString& url) {
+  UTIL_LOG(L3, (_T("[RunBrowser][%d][%s]"), type, url));
+  ASSERT1(type < BROWSER_MAX);
+
+  if (type == BROWSER_UNKNOWN || type >= BROWSER_MAX) {
+    return E_FAIL;
+  }
+
+  CString path;
+  HRESULT hr =  GetBrowserImagePath(type, &path);
+  if (FAILED(hr)) {
+    UTIL_LOG(LW, (_T("[GetBrowserImagePath failed][0x%08x]"), hr));
+    // ShellExecute the url directly as a last resort.
+    return Shell::Execute(url);
+  }
+  EnclosePath(&path);
+
+  UTIL_LOG(L3, (_T("[Execute browser][%s][%s]"), path, url));
+
+  // http://b/1219313: For Vista, in some cases, using ShellExecuteEx does not
+  // re-launch the process. So, using CreateProcess instead.
+  // http://b/1223658: For Vista, especially in the impersonated case, need to
+  // create a fresh environment block. Otherwise, the environment is tainted.
+  hr = vista::RunAsCurrentUser(path + _T(' ') + url);
+
+  if (FAILED(hr)) {
+    UTIL_LOG(LW, (_T("[RunAsCurrentUser failed][0x%x]"), hr));
+    // ShellExecute the url directly as a last resort.
+    return Shell::Execute(url);
+  }
+
+  return S_OK;
+}
+
+// Gets the font size of IE. This is the value that corresponds to what IE
+// displays in "Page/Text Size" menu option. There are 5 values and the default
+// is "Medium", for which the numeric value is 2. The "IEFontSize" is only
+// present after the user has modified the default text size in IE, therefore
+// the absence of the value indicates "Medium" text size.
+HRESULT GetIeFontSize(uint32* font_size) {
+  ASSERT1(font_size);
+
+  const TCHAR ie_scripts_key[] =
+    _T("HKCU\\Software\\Microsoft\\Internet Explorer\\International\\Scripts\\3");  // NOLINT
+  const TCHAR ie_font_size[] = _T("IEFontSize");
+  const uint32 kDefaultFontSize = 2;
+
+  // We expect the scripts key to be there in all cases. The "IEFontSize" value
+  // is optional but we want to fail if the key is not there.
+  if (!RegKey::HasKey(ie_scripts_key)) {
+    return E_UNEXPECTED;
+  }
+
+  scoped_array<byte> buf;   // The font size is a binary registry value.
+  DWORD buf_size(0);
+  if (FAILED(RegKey::GetValue(ie_scripts_key, ie_font_size,
+                              address(buf), &buf_size))) {
+    *font_size = kDefaultFontSize;
+    return S_OK;
+  }
+
+  ASSERT1(buf_size == sizeof(uint32));  // NOLINT
+  if (buf_size != sizeof(uint32)) {     // NOLINT
+    return E_UNEXPECTED;
+  }
+
+  uint32 val = *reinterpret_cast<uint32*>(buf.get());
+  ASSERT1(val <= 4);
+  if (val > 4) {
+    return E_UNEXPECTED;
+  }
+
+  *font_size = val;
+  return S_OK;
+}
+
+}  // namespace omaha
diff --git a/base/browser_utils.h b/base/browser_utils.h
new file mode 100644
index 0000000..c062edb
--- /dev/null
+++ b/base/browser_utils.h
@@ -0,0 +1,93 @@
+// Copyright 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// TODO(omaha): namespaces
+
+#ifndef OMAHA_BASE_BROWSER_UTILS_H_
+#define OMAHA_BASE_BROWSER_UTILS_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+// Must be kept in sync with the enum in base/omaha3_idl.idl. Do not include
+// BROWSER_MAX in the IDL file.
+// Do not move or remove existing elements.
+enum BrowserType {
+  BROWSER_UNKNOWN = 0,
+  BROWSER_DEFAULT = 1,
+  BROWSER_IE      = 2,
+  BROWSER_FIREFOX = 3,
+  BROWSER_CHROME  = 4,
+  // Add new browsers above this.
+  BROWSER_MAX
+};
+
+// Read the browser information from legacy keys. See
+// http://support.microsoft.com/kb/224816.
+HRESULT GetLegacyDefaultBrowserInfo(CString* name, CString* browser_path);
+
+// Gets the default browser name.
+// When calling this method from the local system account, the caller must
+// impersonate the user first. This assumes the user profile is loaded under
+// HKEY_USERS, which is true if the user has an interactive session.
+// Otherwise, the caller must load the profile for the user before
+// calling the function.
+HRESULT GetDefaultBrowserName(CString* name);
+
+// Returns the default browser type.
+HRESULT GetDefaultBrowserType(BrowserType* type);
+
+// Get the default browser path
+HRESULT GetDefaultBrowserPath(CString* path);
+
+// Get the default profile of Firefox
+HRESULT GetFirefoxDefaultProfile(CString* name, CString* path);
+
+// Returns the executable name corresponding to the browser type.
+HRESULT BrowserTypeToProcessName(BrowserType type, CString* exe_name);
+
+// Returns the absolute filename of the browser executable.
+HRESULT GetBrowserImagePath(BrowserType type, CString* path);
+
+// Terminates all the browsers identified by type for a user.
+HRESULT TerminateBrowserProcess(BrowserType type,
+                                const CString& sid,
+                                int timeout_msec,
+                                bool* found);
+
+// Waits for all instances of browser to die.
+HRESULT WaitForBrowserToDie(BrowserType type,
+                            const CString& sid,
+                            int timeout_msec);
+
+// Launches the browser using RunAsCurrentUser. On failure, falls back to
+// using ShellExecute.
+HRESULT RunBrowser(BrowserType type, const CString& url);
+
+// Returns the current font size selection in Internet Explorer.
+// Possible font size values:
+// 0 : smallest font
+// 1 : small font
+// 2 : medium font
+// 3 : large font
+// 4 : largest font
+HRESULT GetIeFontSize(uint32* font_size);
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_BROWSER_UTILS_H_
diff --git a/base/browser_utils_unittest.cc b/base/browser_utils_unittest.cc
new file mode 100644
index 0000000..ef75fda
--- /dev/null
+++ b/base/browser_utils_unittest.cc
@@ -0,0 +1,385 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/browser_utils.h"
+#include "omaha/base/const_utils.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+const CString kRegistryHiveOverrideClasses =
+    CString(kRegistryHiveOverrideRoot) + _T("HKCR");
+
+// GetDefaultBrowserName() uses ::RegOpenCurrentUser, which does not appear to
+// be affected by registry hive overrides. Therefore, in order to test methods
+// that rely on it, the actual value must be replaced. This class saves the
+// value and restores it. If the test is interrupted before TearDown, the
+// default browser may not be correctly registered.
+class BrowserUtilsDefaultBrowserSavedTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    if (!RegKey::HasKey(kRegKeyUserDefaultBrowser)) {
+      return;
+    }
+
+    EXPECT_SUCCEEDED(RegKey::GetValue(kRegKeyUserDefaultBrowser,
+                                      NULL,
+                                      &default_browser_name_));
+  }
+
+  virtual void TearDown() {
+    if (default_browser_name_.IsEmpty()) {
+      RegKey::DeleteKey(kRegKeyUserDefaultBrowser);
+      return;
+    }
+
+    EXPECT_SUCCEEDED(RegKey::SetValue(kRegKeyUserDefaultBrowser,
+                                      NULL,
+                                      default_browser_name_));
+  }
+
+  CString default_browser_name_;
+};
+
+class GetLegacyDefaultBrowserInfoTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    RegKey::DeleteKey(kRegistryHiveOverrideClasses, true);
+    RegKey classes_key;
+    ASSERT_HRESULT_SUCCEEDED(classes_key.Create(kRegistryHiveOverrideClasses));
+    ASSERT_HRESULT_SUCCEEDED(::RegOverridePredefKey(HKEY_CLASSES_ROOT,
+                                                    classes_key.Key()));
+  }
+
+  virtual void TearDown() {
+    ASSERT_HRESULT_SUCCEEDED(::RegOverridePredefKey(HKEY_CLASSES_ROOT, NULL));
+    ASSERT_HRESULT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideClasses,
+                             true));
+  }
+};
+
+TEST_F(BrowserUtilsDefaultBrowserSavedTest, GetDefaultBrowserType_IE) {
+  if (!ShouldRunLargeTest()) {
+    return;
+  }
+  EXPECT_SUCCEEDED(
+      RegKey::SetValue(kRegKeyUserDefaultBrowser, NULL, _T("IeXpLoRe.ExE")));
+  BrowserType type = BROWSER_UNKNOWN;
+  EXPECT_SUCCEEDED(GetDefaultBrowserType(&type));
+  EXPECT_EQ(BROWSER_IE, type);
+}
+
+TEST_F(BrowserUtilsDefaultBrowserSavedTest, GetDefaultBrowserType_Firefox) {
+  if (!ShouldRunLargeTest()) {
+    return;
+  }
+  EXPECT_SUCCEEDED(
+      RegKey::SetValue(kRegKeyUserDefaultBrowser, NULL, _T("FiReFoX.ExE")));
+  BrowserType type = BROWSER_UNKNOWN;
+  EXPECT_SUCCEEDED(GetDefaultBrowserType(&type));
+  EXPECT_EQ(BROWSER_FIREFOX, type);
+}
+
+TEST_F(BrowserUtilsDefaultBrowserSavedTest, GetDefaultBrowserType_Chrome) {
+  if (!ShouldRunLargeTest()) {
+    return;
+  }
+  EXPECT_SUCCEEDED(
+      RegKey::SetValue(kRegKeyUserDefaultBrowser, NULL, _T("ChRoMe.ExE")));
+  BrowserType type = BROWSER_UNKNOWN;
+  EXPECT_SUCCEEDED(GetDefaultBrowserType(&type));
+  EXPECT_EQ(BROWSER_CHROME, type);
+}
+
+TEST_F(BrowserUtilsDefaultBrowserSavedTest, GetDefaultBrowserType_Unsupported) {
+  if (!ShouldRunLargeTest()) {
+    return;
+  }
+  EXPECT_SUCCEEDED(
+      RegKey::SetValue(kRegKeyUserDefaultBrowser, NULL, _T("FoO.ExE")));
+  BrowserType type = BROWSER_UNKNOWN;
+  EXPECT_SUCCEEDED(GetDefaultBrowserType(&type));
+  EXPECT_EQ(BROWSER_UNKNOWN, type);
+}
+
+TEST(BrowserUtilsTest, BrowserTypeToProcessName_Unknown) {
+  CString exe_name;
+  EXPECT_EQ(E_FAIL, BrowserTypeToProcessName(BROWSER_UNKNOWN, &exe_name));
+  EXPECT_TRUE(exe_name.IsEmpty());
+}
+
+// Writes the default browser to ensure consistent results.
+TEST_F(BrowserUtilsDefaultBrowserSavedTest, BrowserTypeToProcessName_Default) {
+  if (!ShouldRunLargeTest()) {
+    return;
+  }
+  EXPECT_SUCCEEDED(
+      RegKey::SetValue(kRegKeyUserDefaultBrowser, NULL, _T("IeXpLoRe.ExE")));
+
+  CString default_exe_name;
+  EXPECT_SUCCEEDED(GetDefaultBrowserName(&default_exe_name));
+  EXPECT_STREQ(_T("IeXpLoRe.ExE"), default_exe_name);
+
+  CString exe_name;
+  EXPECT_SUCCEEDED(BrowserTypeToProcessName(BROWSER_DEFAULT, &exe_name));
+  EXPECT_STREQ(_T("IeXpLoRe.ExE"), exe_name);
+}
+
+TEST(BrowserUtilsTest, BrowserTypeToProcessName_Browsers) {
+  CString exe_name;
+  EXPECT_SUCCEEDED(BrowserTypeToProcessName(BROWSER_IE, &exe_name));
+  EXPECT_STREQ(_T("IEXPLORE.EXE"), exe_name);
+  EXPECT_SUCCEEDED(BrowserTypeToProcessName(BROWSER_FIREFOX, &exe_name));
+  EXPECT_STREQ(_T("FIREFOX.EXE"), exe_name);
+  EXPECT_SUCCEEDED(BrowserTypeToProcessName(BROWSER_CHROME, &exe_name));
+  EXPECT_STREQ(_T("CHROME.EXE"), exe_name);
+}
+
+TEST(BrowserUtilsTest, BrowserTypeToProcessName_Invalid) {
+  CString exe_name;
+  ExpectAsserts expect_asserts;
+  EXPECT_EQ(E_FAIL, BrowserTypeToProcessName(BROWSER_MAX, &exe_name));
+  EXPECT_TRUE(exe_name.IsEmpty());
+  EXPECT_EQ(E_FAIL,
+            BrowserTypeToProcessName(static_cast<BrowserType>(9), &exe_name));
+  EXPECT_TRUE(exe_name.IsEmpty());
+  EXPECT_EQ(E_FAIL,
+            BrowserTypeToProcessName(static_cast<BrowserType>(-1), &exe_name));
+  EXPECT_TRUE(exe_name.IsEmpty());
+}
+
+TEST(BrowserUtilsTest, GetBrowserImagePath_DefaultBrowser) {
+  CString browser;
+  ASSERT_SUCCEEDED(GetDefaultBrowserName(&browser));
+
+  BrowserType default_type = BROWSER_UNKNOWN;
+  if (browser.CompareNoCase(kIeExeName) == 0) {
+    default_type = BROWSER_IE;
+  } else if (browser.CompareNoCase(kFirefoxExeName) == 0) {
+    default_type = BROWSER_FIREFOX;
+  } else if (browser.CompareNoCase(kChromeExeName) == 0) {
+    default_type = BROWSER_CHROME;
+  }
+
+  CString exp_browser_path;
+  ASSERT_SUCCEEDED(GetDefaultBrowserPath(&exp_browser_path));
+
+  if (default_type == BROWSER_IE) {
+    CString path;
+    ASSERT_SUCCEEDED(GetBrowserImagePath(BROWSER_IE, &path));
+
+    CString long_name;
+    ASSERT_SUCCEEDED(ShortPathToLongPath(path, &long_name));
+    CString exp_long_name;
+    ASSERT_SUCCEEDED(ShortPathToLongPath(exp_browser_path, &exp_long_name));
+    ASSERT_STREQ(exp_long_name.MakeLower(), long_name.MakeLower());
+  } else if (default_type == BROWSER_FIREFOX) {
+    CString path;
+    ASSERT_SUCCEEDED(GetBrowserImagePath(BROWSER_FIREFOX, &path));
+
+    CString long_name;
+    ASSERT_SUCCEEDED(ShortPathToLongPath(path, &long_name));
+    CString exp_long_name;
+    ASSERT_SUCCEEDED(ShortPathToLongPath(exp_browser_path, &exp_long_name));
+    ASSERT_STREQ(exp_long_name.MakeLower(), long_name.MakeLower());
+  } else if (default_type == BROWSER_CHROME) {
+    CString path;
+    ASSERT_SUCCEEDED(GetBrowserImagePath(BROWSER_CHROME, &path));
+
+    CString long_name;
+    ASSERT_SUCCEEDED(ShortPathToLongPath(path, &long_name));
+    CString exp_long_name;
+    ASSERT_SUCCEEDED(ShortPathToLongPath(exp_browser_path, &exp_long_name));
+    ASSERT_STREQ(exp_long_name.MakeLower(), long_name.MakeLower());
+  }
+
+  CString path;
+  ASSERT_SUCCEEDED(GetBrowserImagePath(BROWSER_DEFAULT, &path));
+  CString long_name;
+  ASSERT_SUCCEEDED(ShortPathToLongPath(path, &long_name));
+  CString exp_long_name;
+  ASSERT_SUCCEEDED(ShortPathToLongPath(exp_browser_path, &exp_long_name));
+  ASSERT_STREQ(exp_long_name.MakeLower(), long_name.MakeLower());
+
+  ASSERT_FAILED(GetBrowserImagePath(BROWSER_UNKNOWN, &path));
+}
+
+// Try all browsers to get more test coverage.
+TEST(BrowserUtilsTest, GetBrowserImagePath_AllSupportedBrowsers) {
+  if (IsTestRunByLocalSystem()) {
+    return;
+  }
+
+  CString program_files_path;
+  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES | CSIDL_FLAG_DONT_VERIFY,
+                                 &program_files_path));
+  CString path;
+
+  HRESULT hr = GetBrowserImagePath(BROWSER_IE, &path);
+  if (SUCCEEDED(hr)) {
+    EXPECT_EQ(0, path.CompareNoCase(program_files_path +
+                                    _T("\\Internet Explorer\\iexplore.exe")))
+        << _T("Actual path: ") << path.GetString();
+  } else {
+    EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), hr);
+  }
+
+  hr = GetBrowserImagePath(BROWSER_FIREFOX, &path);
+  if (SUCCEEDED(hr)) {
+    EXPECT_TRUE(
+        0 == path.CompareNoCase(program_files_path +
+                                _T("\\Mozilla Firefox\\firefox.exe")) ||
+        0 == path.CompareNoCase(
+                                _T("C:\\PROGRA~1\\MOZILL~1\\FIREFOX.EXE")) ||
+        0 == path.CompareNoCase(program_files_path +
+                                _T("\\Minefield\\FIREFOX.EXE"))) // Trunk build.
+        << _T("Actual path: ") << path.GetString();
+  } else {
+    EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), hr);
+  }
+
+  hr = GetBrowserImagePath(BROWSER_CHROME, &path);
+  if (SUCCEEDED(hr)) {
+    EXPECT_TRUE(
+        0 == path.CompareNoCase(program_files_path +
+                            _T("\\Google\\Chrome\\Application\\chrome.exe")) ||
+        0 == path.CompareNoCase(
+            GetLocalAppDataPath() +
+            _T("Google\\Chrome\\Application\\chrome.exe")))
+        << _T("Actual path: ") << path.GetString();
+  } else {
+    EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), hr);
+  }
+}
+
+TEST(BrowserUtilsTest, GetLegacyDefaultBrowserInfo) {
+  CString name;
+  CString browser_path;
+  EXPECT_SUCCEEDED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
+  EXPECT_FALSE(browser_path.IsEmpty());
+  EXPECT_FALSE(name.IsEmpty());
+}
+
+TEST(BrowserUtilsTest, GetIeFontSize) {
+  uint32 font_size = 0;
+  const uint32 kMaxFontSize = 4;
+  EXPECT_TRUE(SUCCEEDED(GetIeFontSize(&font_size)) || IsTestRunByLocalSystem());
+  EXPECT_LE(font_size, kMaxFontSize);
+}
+
+TEST_F(GetLegacyDefaultBrowserInfoTest, ValidQuotedIE) {
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(
+      kRegKeyLegacyDefaultBrowserCommand,
+      NULL,
+      _T("\"C:\\Program Files\\Internet Explorer\\iexplore.exe\" -nohome")));
+
+  CString name;
+  CString browser_path;
+  EXPECT_SUCCEEDED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
+  EXPECT_STREQ(_T("C:\\Program Files\\Internet Explorer\\iexplore.exe"),
+               browser_path);
+  EXPECT_STREQ(_T("iexplore.exe"), name);
+}
+
+TEST_F(GetLegacyDefaultBrowserInfoTest, ValidUnquotedPath) {
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(
+      kRegKeyLegacyDefaultBrowserCommand,
+      NULL,
+      _T("C:\\Program Files\\Internet Explorer\\iexplore.exe")));
+
+  CString name;
+  CString browser_path;
+  EXPECT_SUCCEEDED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
+  EXPECT_STREQ(_T("C:\\Program Files\\Internet Explorer\\iexplore.exe"),
+               browser_path);
+  EXPECT_STREQ(_T("iexplore.exe"), name);
+}
+
+TEST_F(GetLegacyDefaultBrowserInfoTest, InvalidNoKey) {
+  CString name;
+  CString browser_path;
+  EXPECT_FAILED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
+  EXPECT_TRUE(browser_path.IsEmpty());
+  EXPECT_TRUE(name.IsEmpty());
+}
+
+TEST_F(GetLegacyDefaultBrowserInfoTest, InvalidNoValue) {
+  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey(
+      kRegKeyLegacyDefaultBrowserCommand));
+
+  CString name;
+  CString browser_path;
+  EXPECT_FAILED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
+  EXPECT_TRUE(browser_path.IsEmpty());
+  EXPECT_TRUE(name.IsEmpty());
+}
+
+TEST_F(GetLegacyDefaultBrowserInfoTest, InvalidPath) {
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(
+      kRegKeyLegacyDefaultBrowserCommand,
+      NULL,
+      _T("\"C:\\Program File\\iexplore.exe\" -nohome")));
+
+  CString name;
+  CString browser_path;
+  EXPECT_FAILED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
+  EXPECT_TRUE(browser_path.IsEmpty());
+  EXPECT_TRUE(name.IsEmpty());
+}
+
+TEST_F(GetLegacyDefaultBrowserInfoTest, InvalidUnquotedPath) {
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(
+      kRegKeyLegacyDefaultBrowserCommand,
+      NULL,
+      _T("C:\\Program Files\\Internet Explorer\\iexplore.exe -nohome")));
+
+  CString name;
+  CString browser_path;
+  EXPECT_FAILED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
+  EXPECT_TRUE(browser_path.IsEmpty());
+  EXPECT_TRUE(name.IsEmpty());
+}
+
+TEST_F(GetLegacyDefaultBrowserInfoTest, InvalidUnquotedDirectory) {
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(
+      kRegKeyLegacyDefaultBrowserCommand,
+      NULL,
+      _T("C:\\Program Files\\Internet Explorer\\")));
+
+  CString name;
+  CString browser_path;
+  EXPECT_FAILED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
+  EXPECT_TRUE(browser_path.IsEmpty());
+  EXPECT_TRUE(name.IsEmpty());
+}
+
+TEST_F(GetLegacyDefaultBrowserInfoTest, InvalidQuotedDirectory) {
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(
+      kRegKeyLegacyDefaultBrowserCommand,
+      NULL,
+      _T("\"C:\\Program Files\\Internet Explorer\\\" -nohome")));
+
+  CString name;
+  CString browser_path;
+  EXPECT_FAILED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
+  EXPECT_TRUE(browser_path.IsEmpty());
+  EXPECT_TRUE(name.IsEmpty());
+}
+
+}  // namespace omaha
+
diff --git a/base/build.scons b/base/build.scons
new file mode 100644
index 0000000..cf82007
--- /dev/null
+++ b/base/build.scons
@@ -0,0 +1,105 @@
+# Copyright 2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========================================================================
+
+Import('env')
+
+env.BuildSConscript('logging')
+env.BuildSConscript('security')
+
+local_env = env.Clone()
+
+inputs = [
+    'accounts.cc',
+    'app_util.cc',
+    'atl_regexp.cc',
+    'browser_utils.cc',
+    'cgi.cc',
+    'clipboard.cc',
+    'command_line_parser.cc',
+    'command_line_validator.cc',
+    'commands.cc',
+    'crash_if_specific_error.cc',
+    'crc.cc',
+    'debug.cc',
+    'disk.cc',
+    'dynamic_link_dbghelp.cc',
+    'dynamic_link_kernel32.cc',
+    'encrypt.cc',
+    'error.cc',
+    'event_trace_controller.cc',
+    'event_trace_provider.cc',
+    'etw_log_writer.cc',
+    'exception_barrier.cc',
+    'exception_barrier_lowlevel.asm',
+    'exception_utils.cc',
+    'extractor.cc',
+    'file.cc',
+    'file_reader.cc',
+    'file_store.cc',
+    'file_ver.cc',
+    'firewall_product_detection.cc',
+    'highres_timer-win32.cc',
+    'localization.cc',
+    'logging.cc',
+    'md5.cc',
+    'module_utils.cc',
+    'omaha_version.cc',
+    'path.cc',
+    'pe_utils.cc',
+    'popup_menu.cc',
+    'process.cc',
+    'proc_utils.cc',
+    'program_instance.cc',
+    'queue_timer.cc',
+    'reactor.cc',
+    'reg_key.cc',
+    'regexp.cc',
+    'registry_hive.cc',
+    'registry_monitor_manager.cc',
+    'registry_store.cc',
+    'safe_format.cc',
+    'serializable_object.cc',
+    'service_utils.cc',
+    'shell.cc',
+    'shutdown_handler.cc',
+    'signatures.cc',
+    'signaturevalidator.cc',
+    'single_instance.cc',
+    'sta.cc',
+    'string.cc',
+    'synchronized.cc',
+    'system.cc',
+    'system_info.cc',
+    '../third_party/smartany/shared_any.cc',
+    'thread.cc',
+    'thread_pool.cc',
+    'time.cc',
+    'timer.cc',
+    'tr_rand.cc',
+    'user_info.cc',
+    'user_rights.cc',
+    'utils.cc',
+    'vista_utils.cc',
+    'vistautil.cc',
+    'window_utils.cc',
+    'wmi_query.cc',
+    'xml_utils.cc',
+    ]
+
+# Required by the exception barrier code.
+local_env.Append(ASFLAGS = ['/safeseh'])
+
+# Build these into a library.
+local_env.ComponentStaticLibrary('base', inputs)
diff --git a/base/cgi.cc b/base/cgi.cc
new file mode 100644
index 0000000..7175a4c
--- /dev/null
+++ b/base/cgi.cc
@@ -0,0 +1,101 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/cgi.h"
+
+#include <tchar.h>
+#include "base/basictypes.h"
+#include "omaha/base/debug.h"
+
+namespace omaha {
+
+static uint32 _needs_escape[8] = {
+  0xffffffffL,
+  0xf80008fdL,
+  0x78000001L,
+  0xb8000001L,
+  0xffffffffL,
+  0xffffffffL,
+  0xffffffffL,
+  0xffffffffL
+};
+#define needs_escape(c) (_needs_escape[(c)>>5]&(1<<((c)&31)))
+
+// Here are a couple utility methods to change ints to hex chars & back.
+inline int int_to_hex_digit(int i) {
+  ASSERT((i >= 0) && (i <= 16), (_T("")));
+  return ((i < 10) ? (i + '0') : ((i - 10) + 'A'));
+}
+
+inline int hex_digit_to_int(TCHAR c) {
+  ASSERT(isxdigit(c), (_T("")));
+  return ((c >= 'a') ? ((c - 'a') + 10) :
+          (c >= 'A') ? ((c - 'A') + 10) :
+          (c - '0'));
+}
+
+bool CGI::EscapeString(const TCHAR* src, int srcn, TCHAR* dst, int dstn) {
+  ASSERT1(src != dst);  // In-place escaping will fail.
+  ASSERT1(srcn >= 0);
+  ASSERT1(dstn >= 1);
+  dstn--;   // Number of characters we can write, not including null terminator.
+
+  int i, j;
+  for (i = 0, j = 0; i < srcn && j < dstn; i++) {
+    TCHAR c = src[i];
+    if (c == ' ') {
+      dst[j++] = '+';
+    } else if (!needs_escape(c)) {
+      dst[j++] = c;
+    } else if (j + 3 > dstn) {
+      break;  // Escape sequence will not fit.
+    } else {
+      dst[j++] = '%';
+      dst[j++] = static_cast<TCHAR>(int_to_hex_digit((c >> 4) & 0xf));
+      dst[j++] = static_cast<TCHAR>(int_to_hex_digit(c & 0xf));
+    }
+  }
+  dst[j] = '\0';
+  return i == srcn;
+}
+
+bool CGI::UnescapeString(const TCHAR* src, int srcn, TCHAR* dst, int dstn) {
+  ASSERT1(srcn >= 0);
+  ASSERT1(dstn >= 1);
+  dstn--;   // Number of characters we can write, not including null terminator.
+
+  int i, j;
+  for (i = 0, j = 0; i < srcn && j < dstn; ++j) {
+    TCHAR c = src[i++];
+    if (c == '+') {
+      dst[j] = ' ';
+    } else if (c != '%') {
+      dst[j] = c;
+    } else if (i + 2 > srcn) {
+      break;  // Escape sequence is incomplete.
+    } else if (!isxdigit(src[i]) || !isxdigit(src[i + 1])) {
+      break;  // Escape sequence isn't hex.
+    } else {
+      int num = hex_digit_to_int(src[i++]) << 4;
+      num += hex_digit_to_int(src[i++]);
+      dst[j] = static_cast<TCHAR>(num);
+    }
+  }
+  dst[j] = '\0';
+  return i == srcn;
+}
+
+}  // namespace omaha
+
diff --git a/common/cgi.h b/base/cgi.h
similarity index 100%
rename from common/cgi.h
rename to base/cgi.h
diff --git a/base/cgi_unittest.cc b/base/cgi_unittest.cc
new file mode 100644
index 0000000..0aec8c4
--- /dev/null
+++ b/base/cgi_unittest.cc
@@ -0,0 +1,60 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Unit test for the CGI escape/unescape string..
+
+#include "base/scoped_ptr.h"
+#include "omaha/base/cgi.h"
+#include "omaha/base/string.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+void TestEscapeUnescape(const TCHAR* origin, const TCHAR* escaped) {
+  int origin_len = lstrlen(origin);
+  int buffer_len = origin_len * CGI::kEscapeFactor + 1;
+  scoped_array<TCHAR> escaped_buffer(new TCHAR[buffer_len]);
+  ASSERT_TRUE(CGI::EscapeString(origin, origin_len,
+                                escaped_buffer.get(), buffer_len));
+  ASSERT_STREQ(escaped_buffer.get(), escaped);
+
+  scoped_array<TCHAR> origin_buffer(new TCHAR[buffer_len]);
+  ASSERT_TRUE(CGI::UnescapeString(escaped_buffer.get(),
+                                  lstrlen(escaped_buffer.get()),
+                                  origin_buffer.get(), buffer_len));
+  ASSERT_STREQ(origin_buffer.get(), origin);
+}
+
+TEST(CGITEST, EscapeUnescape) {
+  // Regular chars.
+  TCHAR origin1[] = _T("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
+  TestEscapeUnescape(origin1, origin1);
+
+  String_ToLower(origin1);
+  TestEscapeUnescape(origin1, origin1);
+
+  // Special chars.
+  TCHAR origin2[] =  _T("^&`{}|][\"<>\\");    // NOLINT
+  TCHAR escaped2[] = _T("%5E%26%60%7B%7D%7C%5D%5B%22%3C%3E%5C");
+  TestEscapeUnescape(origin2, escaped2);
+
+  // Real case.
+  TCHAR origin3[] = _T("http://foo2.bar.google.com:80/pagead/conversion/1067912086/?ai=123&gclid=456&label=installation&value=0.0");                    // NOLINT
+  TCHAR escaped3[] = _T("http://foo2.bar.google.com:80/pagead/conversion/1067912086/%3Fai%3D123%26gclid%3D456%26label%3Dinstallation%26value%3D0.0");   // NOLINT
+  TestEscapeUnescape(origin3, escaped3);
+}
+
+}  // namespace omaha
+
diff --git a/base/clipboard.cc b/base/clipboard.cc
new file mode 100644
index 0000000..5429555
--- /dev/null
+++ b/base/clipboard.cc
@@ -0,0 +1,69 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/clipboard.h"
+#include "base/basictypes.h"
+#include "omaha/base/debug.h"
+
+namespace omaha {
+
+// Put the given string on the system clipboard
+void SetClipboard(const TCHAR *string_to_set) {
+  ASSERT(string_to_set, (L""));
+
+  int len = lstrlen(string_to_set);
+
+  //
+  // Note to developer: It is not always possible to step through this code
+  //  since the debugger will possibly steal the clipboard.  E.g. OpenClipboard
+  //  might succeed and EmptyClipboard might fail with "Thread does not have
+  //  clipboard open".
+  //
+
+  // Actual clipboard processing
+  if (::OpenClipboard(NULL)) {
+    BOOL b = ::EmptyClipboard();
+    ASSERT(b, (L"EmptyClipboard failed"));
+
+    // Include the terminating null
+    len++;
+
+    HANDLE copy_handle = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
+                                       len * sizeof(TCHAR));
+    ASSERT(copy_handle, (L""));
+
+    byte* copy_data = reinterpret_cast<byte*>(::GlobalLock(copy_handle));
+    memcpy(copy_data, string_to_set, len * sizeof(TCHAR));
+    ::GlobalUnlock(copy_handle);
+
+#ifdef _UNICODE
+    HANDLE h = ::SetClipboardData(CF_UNICODETEXT, copy_handle);
+#else
+    HANDLE h = ::SetClipboardData(CF_TEXT, copy_handle);
+#endif
+
+    ASSERT(h != NULL, (L"SetClipboardData failed"));
+    if (!h) {
+      ::GlobalFree(copy_handle);
+    }
+
+    VERIFY(::CloseClipboard(), (L""));
+  } else {
+    ASSERT(false, (L"OpenClipboard failed - %i", ::GetLastError()));
+  }
+}
+
+}  // namespace omaha
+
diff --git a/common/clipboard.h b/base/clipboard.h
similarity index 100%
rename from common/clipboard.h
rename to base/clipboard.h
diff --git a/base/command_line_parser.cc b/base/command_line_parser.cc
new file mode 100644
index 0000000..c52c0a3
--- /dev/null
+++ b/base/command_line_parser.cc
@@ -0,0 +1,389 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/command_line_parser.h"
+#include "omaha/base/command_line_parser_internal.h"
+#include <shellapi.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+
+namespace omaha {
+
+namespace internal {
+
+void CommandLineParserArgs::Reset() {
+  switch_arguments_.clear();
+}
+
+// Assumes switch_name is already lower case.
+HRESULT CommandLineParserArgs::AddSwitch(const CString& switch_name) {
+  ASSERT1(CString(switch_name).MakeLower().Compare(switch_name) == 0);
+  if (switch_arguments_.find(switch_name) != switch_arguments_.end()) {
+    return E_INVALIDARG;
+  }
+
+  StringVector string_vector;
+  switch_arguments_[switch_name] = string_vector;
+  return S_OK;
+}
+
+// Assumes switch_name is already lower case.
+HRESULT CommandLineParserArgs::AddSwitchArgument(const CString& switch_name,
+                                                 const CString& value) {
+  ASSERT1(CString(switch_name).MakeLower().Compare(switch_name) == 0);
+  ASSERT1(!switch_name.IsEmpty());
+  if (switch_name.IsEmpty()) {
+    // We don't have a switch yet, so this is just a base argument.
+    // Example command line:  "foo.exe myarg /someswitch"
+    // Here, myarg would be a base argument.
+    // TODO(omaha): base_args_.push_back(switch_name_str);
+    return E_INVALIDARG;
+  }
+
+  SwitchAndArgumentsMap::iterator iter = switch_arguments_.find(switch_name);
+  if (iter == switch_arguments_.end()) {
+    return E_UNEXPECTED;
+  }
+  (*iter).second.push_back(value);
+
+  return S_OK;
+}
+
+int CommandLineParserArgs::GetSwitchCount() const {
+  return switch_arguments_.size();
+}
+
+bool CommandLineParserArgs::HasSwitch(const CString& switch_name) const {
+  CString switch_name_lower = switch_name;
+  switch_name_lower.MakeLower();
+  return switch_arguments_.find(switch_name_lower) != switch_arguments_.end();
+}
+
+// The value at a particular index may change if switch_names are added
+// since we're using a map underneath.  But this keeps us from having to write
+// an interator and expose it externally.
+HRESULT CommandLineParserArgs::GetSwitchNameAtIndex(int index,
+                                                    CString* name) const {
+  ASSERT1(name);
+
+  if (index >= static_cast<int>(switch_arguments_.size())) {
+    return E_INVALIDARG;
+  }
+
+  SwitchAndArgumentsMapIter iter = switch_arguments_.begin();
+  for (int i = 0; i < index; ++i) {
+    ++iter;
+  }
+
+  *name = (*iter).first;
+
+  return S_OK;
+}
+
+HRESULT CommandLineParserArgs::GetSwitchArgumentCount(
+    const CString& switch_name,
+    int* count) const {
+  ASSERT1(count);
+
+  CString switch_name_lower = switch_name;
+  switch_name_lower.MakeLower();
+
+  SwitchAndArgumentsMapIter iter = switch_arguments_.find(switch_name_lower);
+  if (iter == switch_arguments_.end()) {
+    return E_INVALIDARG;
+  }
+
+  *count = (*iter).second.size();
+  return S_OK;
+}
+
+HRESULT CommandLineParserArgs::GetSwitchArgumentValue(
+    const CString& switch_name,
+    int argument_index,
+    CString* argument_value) const {
+  ASSERT1(argument_value);
+
+  CString switch_name_lower = switch_name;
+  switch_name_lower.MakeLower();
+
+  int count = 0;
+  HRESULT hr = GetSwitchArgumentCount(switch_name_lower, &count);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (argument_index >= count) {
+    return E_INVALIDARG;
+  }
+
+  SwitchAndArgumentsMapIter iter = switch_arguments_.find(switch_name_lower);
+  if (iter == switch_arguments_.end()) {
+    return E_INVALIDARG;
+  }
+
+  *argument_value = (*iter).second[argument_index];
+  return S_OK;
+}
+
+}  // namespace internal
+
+CommandLineParser::CommandLineParser() {
+  required_args_.reset(new internal::CommandLineParserArgs);
+  optional_args_.reset(new internal::CommandLineParserArgs);
+}
+
+CommandLineParser::~CommandLineParser() {
+}
+
+HRESULT CommandLineParser::ParseFromString(const wchar_t* command_line) {
+  CString command_line_str(command_line);
+  command_line_str.Trim(_T(" "));
+
+  if (command_line_str.IsEmpty()) {
+    // If the first arg to CommandLineToArgvW "is an empty string the function
+    // returns the path to the current executable file." However, it does not
+    // correctly handle the case when the path contains spaces - it breaks the
+    // path up into separate argv elements. To avoid issues while maintaining
+    // the expected behavior, manually get the command line in the empty case.
+    // See http://msdn.microsoft.com/en-us/library/bb776391.aspx.
+    VERIFY1(::GetModuleFileName(NULL,
+                                CStrBuf(command_line_str, MAX_PATH),
+                                MAX_PATH));
+    EnclosePath(&command_line_str);
+  }
+
+  int argc = 0;
+  wchar_t** argv = ::CommandLineToArgvW(command_line_str, &argc);
+  if (!argv) {
+    return HRESULTFromLastError();
+  }
+
+  HRESULT hr = ParseFromArgv(argc, argv);
+  ::LocalFree(argv);
+  return hr;
+}
+
+// TODO(Omaha): Move the rule parser into a separate class.
+// TODO(Omaha): Fail the regular command parser if [/ switch is passed.
+// ParseFromArgv parses either a rule or a command line.
+//
+// Rules have required and optional parameters. An example of a rule is:
+//     "gu.exe /install <extraargs> [/oem [/appargs <appargs> [/silent"
+// This creates a rule for a command line that requires "/install" for the rule
+// to match. The other parameters are optional, indicated by prefixes of "[/".
+//
+// Command lines do not use "[/", and use "/" for all parameters.
+// A command line that looks like this:
+//     "gu.exe /install <extraargs> /oem /appargs <appargs>"
+// will match the rule above.
+HRESULT CommandLineParser::ParseFromArgv(int argc, wchar_t** argv) {
+  if (argc == 0 || !argv) {
+    return E_INVALIDARG;
+  }
+
+  Reset();
+
+  if (argc == 1) {
+    // We only have the program name.  So, we're done parsing.
+    ASSERT1(!IsSwitch(argv[0]));
+    return S_OK;
+  }
+
+  CString current_switch_name;
+  bool is_optional_switch = false;
+
+  // Start parsing at the first argument after the program name (index 1).
+  for (int i = 1; i < argc; ++i) {
+    HRESULT hr = S_OK;
+    CString token = argv[i];
+    token.Trim(_T(" "));
+    if (IsSwitch(token)) {
+      hr = StripSwitchNameFromArgv(token, &current_switch_name);
+      if (FAILED(hr)) {
+        return hr;
+      }
+      hr = AddSwitch(current_switch_name);
+      if (FAILED(hr)) {
+        CORE_LOG(LE, (_T("[AddSwitch failed][%s][0x%x]"),
+                      current_switch_name, hr));
+        return hr;
+      }
+      is_optional_switch = false;
+    } else if (IsOptionalSwitch(token)) {
+      hr = StripOptionalSwitchNameFromArgv(token, &current_switch_name);
+      if (FAILED(hr)) {
+        return hr;
+      }
+      hr = AddOptionalSwitch(current_switch_name);
+      if (FAILED(hr)) {
+        CORE_LOG(LE, (_T("[AddOptionalSwitch failed][%s][0x%x]"),
+                      current_switch_name, hr));
+        return hr;
+      }
+      is_optional_switch = true;
+    } else {
+      hr = is_optional_switch ?
+          AddOptionalSwitchArgument(current_switch_name, token) :
+          AddSwitchArgument(current_switch_name, token);
+
+      if (FAILED(hr)) {
+        CORE_LOG(LE, (_T("[Adding switch argument failed][%d][%s][%s][0x%x]"),
+                      is_optional_switch, current_switch_name, token, hr));
+        return hr;
+      }
+    }
+  }
+
+  return S_OK;
+}
+
+bool CommandLineParser::IsSwitch(const CString& param) const {
+  // Switches must have a prefix (/) or (-), and at least one character.
+  if (param.GetLength() < 2) {
+    return false;
+  }
+
+  // All switches must start with / or -, and not contain any spaces.
+  // Since the argv parser strips out the enclosing quotes around an argument,
+  // we need to handle the following cases properly:
+  // * foo.exe /switch arg     -- /switch is a switch, arg is an arg
+  // * foo.exe /switch "/x y"  -- /switch is a switch, '/x y' is an arg and it
+  //   will get here _without_ the quotes.
+  // If param_str starts with / and contains no spaces, then it's a switch.
+  return ((param[0] == _T('/')) || (param[0] == _T('-'))) &&
+          (param.Find(_T(" ")) == -1) &&
+          (param.Find(_T("%20")) == -1);
+}
+
+bool CommandLineParser::IsOptionalSwitch(const CString& param) const {
+  // Optional switches must have a prefix ([/) or ([-), and at least one
+  // character.
+  return param[0] == _T('[') && IsSwitch(param.Right(param.GetLength() - 1));
+}
+
+HRESULT CommandLineParser::StripSwitchNameFromArgv(const CString& param,
+                                                   CString* switch_name) {
+  ASSERT1(switch_name);
+
+  if (!IsSwitch(param)) {
+    return E_INVALIDARG;
+  }
+
+  *switch_name = param.Right(param.GetLength() - 1);
+  switch_name->Trim(_T(" "));
+  switch_name->MakeLower();
+  return S_OK;
+}
+
+HRESULT CommandLineParser::StripOptionalSwitchNameFromArgv(const CString& param,
+                                                           CString* name) {
+  ASSERT1(name);
+
+  if (!IsOptionalSwitch(param)) {
+    return E_INVALIDARG;
+  }
+
+  return StripSwitchNameFromArgv(param.Right(param.GetLength() - 1), name);
+}
+
+void CommandLineParser::Reset() {
+  required_args_->Reset();
+  optional_args_->Reset();
+}
+
+HRESULT CommandLineParser::AddSwitch(const CString& switch_name) {
+  ASSERT1(switch_name == CString(switch_name).MakeLower());
+  return required_args_->AddSwitch(switch_name);
+}
+
+HRESULT CommandLineParser::AddSwitchArgument(const CString& switch_name,
+                                             const CString& argument_value) {
+  ASSERT1(switch_name == CString(switch_name).MakeLower());
+  return required_args_->AddSwitchArgument(switch_name, argument_value);
+}
+
+int CommandLineParser::GetSwitchCount() const {
+  return required_args_->GetSwitchCount();
+}
+
+bool CommandLineParser::HasSwitch(const CString& switch_name) const {
+  return required_args_->HasSwitch(switch_name);
+}
+
+// The value at a particular index may change if switch_names are added
+// since we're using a map underneath.  But this keeps us from having to write
+// an interator and expose it externally.
+HRESULT CommandLineParser::GetSwitchNameAtIndex(int index,
+                                                CString* switch_name) const {
+  return required_args_->GetSwitchNameAtIndex(index, switch_name);
+}
+
+HRESULT CommandLineParser::GetSwitchArgumentCount(const CString& switch_name,
+                                                  int* count) const {
+  return required_args_->GetSwitchArgumentCount(switch_name, count);
+}
+
+HRESULT CommandLineParser::GetSwitchArgumentValue(
+    const CString& switch_name,
+    int argument_index,
+    CString* argument_value) const {
+  return required_args_->GetSwitchArgumentValue(switch_name,
+                                               argument_index,
+                                               argument_value);
+}
+
+HRESULT CommandLineParser::AddOptionalSwitch(const CString& switch_name) {
+  ASSERT1(switch_name == CString(switch_name).MakeLower());
+  return optional_args_->AddSwitch(switch_name);
+}
+
+HRESULT CommandLineParser::AddOptionalSwitchArgument(const CString& switch_name,
+                                                     const CString& value) {
+  ASSERT1(switch_name == CString(switch_name).MakeLower());
+  return optional_args_->AddSwitchArgument(switch_name, value);
+}
+
+int CommandLineParser::GetOptionalSwitchCount() const {
+  return optional_args_->GetSwitchCount();
+}
+
+bool CommandLineParser::HasOptionalSwitch(const CString& switch_name) const {
+  return optional_args_->HasSwitch(switch_name);
+}
+
+// The value at a particular index may change if switch_names are added
+// since we're using a map underneath.  But this keeps us from having to write
+// an interator and expose it externally.
+HRESULT CommandLineParser::GetOptionalSwitchNameAtIndex(int index,
+                                                        CString* name) const {
+  return optional_args_->GetSwitchNameAtIndex(index, name);
+}
+
+HRESULT CommandLineParser::GetOptionalSwitchArgumentCount(const CString& name,
+                                                          int* count) const {
+  return optional_args_->GetSwitchArgumentCount(name, count);
+}
+
+HRESULT CommandLineParser::GetOptionalSwitchArgumentValue(const CString& name,
+                                                          int argument_index,
+                                                          CString* val) const {
+  return optional_args_->GetSwitchArgumentValue(name,
+                                               argument_index,
+                                               val);
+}
+
+}  // namespace omaha
diff --git a/base/command_line_parser.h b/base/command_line_parser.h
new file mode 100644
index 0000000..03171dc
--- /dev/null
+++ b/base/command_line_parser.h
@@ -0,0 +1,113 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_BASE_COMMAND_LINE_PARSER_H__
+#define OMAHA_BASE_COMMAND_LINE_PARSER_H__
+
+#include <windows.h>
+#include <atlstr.h>
+#include <map>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+
+namespace omaha {
+
+namespace internal {
+
+class CommandLineParserArgs;
+
+}  // namespace internal
+
+// This class will parse a command line either from a string or in argc/argv
+// format.  It then provides information about the parsed command line.
+// When passing the string, make sure it includes the program name as the first
+// argument.
+// A "switch" is an argument preceded by "/".  Each switch can take 0..n
+// arguments.
+// Example:  foo.exe /sw a "b b" /sw2 /sw3
+// * foo.exe is the program name
+// * sw, sw2, and sw3 are switches.
+// * a and 'b b' (without the quotes) are the arguments to the sw switch.
+// * sw has 2 arguments and sw2 and sw3 have no arguments.
+class CommandLineParser {
+ public:
+  CommandLineParser();
+  ~CommandLineParser();
+
+  // Parses the command line from a string.  Must include the program name (e.g.
+  // foo.exe) as the first value in the command line.
+  HRESULT ParseFromString(const wchar_t* command_line);
+
+  // Parses the command line form argc/argv syntax.  Makes the assumption that
+  // argv[0] is the program name (e.g. foo.exe).
+  HRESULT ParseFromArgv(int argc, wchar_t** argv);
+
+  // TODO(Omaha): Name these methods "Required".
+  // Gets the number of required switches in the parsed command line.
+  int GetSwitchCount() const;
+
+  // Returns the required switch at a particular index.
+  HRESULT GetSwitchNameAtIndex(int index, CString* switch_name) const;
+
+  // Returns true if a required switch with the name switch_name is found.
+  bool HasSwitch(const CString& switch_name) const;
+
+  // Returns the number of required arguments for required switch switch_name.
+  HRESULT GetSwitchArgumentCount(const CString& switch_name,
+                                 int* count) const;
+
+  // Returns the value of a required switch argument at the specified offset.
+  HRESULT GetSwitchArgumentValue(const CString& switch_name,
+                                 int argument_index,
+                                 CString* argument_value) const;
+
+  // Functions that have the same functionality as the above functions,
+  // except they operate on the optional switches.
+  int GetOptionalSwitchCount() const;
+  bool HasOptionalSwitch(const CString& switch_name) const;
+  HRESULT GetOptionalSwitchNameAtIndex(int index, CString* switch_name) const;
+  HRESULT GetOptionalSwitchArgumentCount(const CString& switch_name,
+                                         int* count) const;
+  HRESULT GetOptionalSwitchArgumentValue(
+      const CString& switch_name,
+      int argument_index,
+      CString* argument_value) const;
+
+ private:
+  bool IsSwitch(const CString& param) const;
+  HRESULT StripSwitchNameFromArgv(const CString& param, CString* switch_name);
+  bool IsOptionalSwitch(const CString& param) const;
+  HRESULT StripOptionalSwitchNameFromArgv(const CString& param, CString* name);
+
+  void Reset();
+
+  HRESULT AddSwitch(const CString& switch_name);
+  HRESULT AddSwitchArgument(const CString& switch_name,
+                            const CString& argument_value);
+  HRESULT AddOptionalSwitch(const CString& switch_name);
+  HRESULT AddOptionalSwitchArgument(const CString& switch_name,
+                                    const CString& argument_value);
+
+  scoped_ptr<internal::CommandLineParserArgs> required_args_;
+  scoped_ptr<internal::CommandLineParserArgs> optional_args_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(CommandLineParser);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_COMMAND_LINE_PARSER_H__
+
diff --git a/base/command_line_parser_internal.h b/base/command_line_parser_internal.h
new file mode 100644
index 0000000..61ac041
--- /dev/null
+++ b/base/command_line_parser_internal.h
@@ -0,0 +1,77 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_BASE_COMMAND_LINE_PARSER_INTERNAL_H_
+#define OMAHA_BASE_COMMAND_LINE_PARSER_INTERNAL_H_
+
+#include <windows.h>
+#include <map>
+#include <vector>
+
+namespace omaha {
+
+namespace internal {
+
+// Repository for switches and corresponding switch arguments for a command
+// line.
+class CommandLineParserArgs {
+ public:
+  CommandLineParserArgs() {}
+  ~CommandLineParserArgs() {}
+
+  typedef std::vector<CString> StringVector;
+  typedef StringVector::const_iterator StringVectorIter;
+  typedef std::map<CString, StringVector > SwitchAndArgumentsMap;
+  typedef SwitchAndArgumentsMap::const_iterator SwitchAndArgumentsMapIter;
+
+  // Gets the number of switches in the parsed command line.  Will return 0 for
+  // count if a parse has not occurred.
+  int GetSwitchCount() const;
+
+  // Returns the switch at a particular index.
+  // This is meant for iteration only and is not guaranteed to be in the order
+  // of the switches in the parsed command line.
+  HRESULT GetSwitchNameAtIndex(int index, CString* switch_name) const;
+
+  // Returns true if a switch with the name switch_name is found.
+  bool HasSwitch(const CString& switch_name) const;
+
+  // Returns the number of arguments for switch_name.  Will fail if switch_name
+  // is not a valid switch.
+  HRESULT GetSwitchArgumentCount(const CString& switch_name, int* count) const;
+
+  // Returns the value of a switch argument at the specified offset.
+  // Fails if switch_name is not a valid switch.
+  HRESULT GetSwitchArgumentValue(const CString& switch_name,
+                                 int argument_index,
+                                 CString* argument_value) const;
+
+  void Reset();
+  HRESULT AddSwitch(const CString& switch_name);
+  HRESULT AddSwitchArgument(const CString& switch_name,
+                            const CString& argument_value);
+
+ private:
+  SwitchAndArgumentsMap switch_arguments_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(CommandLineParserArgs);
+};
+
+}  // namespace internal
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_COMMAND_LINE_PARSER_INTERNAL_H_
+
diff --git a/base/command_line_parser_unittest.cc b/base/command_line_parser_unittest.cc
new file mode 100644
index 0000000..aaa3f80
--- /dev/null
+++ b/base/command_line_parser_unittest.cc
@@ -0,0 +1,175 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/command_line_parser.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+// This will succeed since the CommandLineToArgvW function returns the
+// path to the current executable file if it's passed the empty string.
+TEST(CommandLineParserTest, ParseFromString_NullString) {
+  CommandLineParser parser;
+  EXPECT_SUCCEEDED(parser.ParseFromString(NULL));
+  EXPECT_EQ(0, parser.GetSwitchCount());
+}
+
+// This will succeed since the CommandLineToArgvW function returns the
+// path to the current executable file if it's passed the empty string.
+TEST(CommandLineParserTest, ParseFromString_EmptyString) {
+  CommandLineParser parser;
+  EXPECT_SUCCEEDED(parser.ParseFromString(_T("")));
+  EXPECT_EQ(0, parser.GetSwitchCount());
+}
+
+// This will succeed since the CommandLineToArgvW function returns the
+// path to the current executable file if it's passed the empty string.
+TEST(CommandLineParserTest, ParseFromString_SpacesOnlyString) {
+  CommandLineParser parser;
+  EXPECT_SUCCEEDED(parser.ParseFromString(_T("    ")));
+}
+
+TEST(CommandLineParserTest, ParseFromArgv_NullArgv) {
+  CommandLineParser parser;
+  EXPECT_FAILED(parser.ParseFromArgv(0, NULL));
+}
+
+TEST(CommandLineParserTest, CallFunctionsBeforeParse) {
+  CommandLineParser parser;
+  int arg_count = 0;
+  CString arg_value;
+  EXPECT_FALSE(parser.HasSwitch(_T("foo")));
+  EXPECT_EQ(0, parser.GetSwitchCount());
+  EXPECT_FAILED(parser.GetSwitchArgumentCount(_T("foo"), &arg_count));
+  EXPECT_FAILED(parser.GetSwitchArgumentValue(_T("foo"), 0, &arg_value));
+}
+
+TEST(CommandLineParserTest, ParseFromString_ProgramNameOnly) {
+  CommandLineParser parser;
+  EXPECT_SUCCEEDED(parser.ParseFromString(_T("myprog.exe")));
+  EXPECT_EQ(0, parser.GetSwitchCount());
+}
+
+TEST(CommandLineParserTest, ValidateSwitchMixedCase) {
+  CommandLineParser parser;
+  EXPECT_SUCCEEDED(parser.ParseFromString(_T("myprog.exe /FooP")));
+  EXPECT_EQ(1, parser.GetSwitchCount());
+  EXPECT_TRUE(parser.HasSwitch(_T("foop")));
+  EXPECT_TRUE(parser.HasSwitch(_T("FooP")));
+  EXPECT_TRUE(parser.HasSwitch(_T("fOOp")));
+  EXPECT_TRUE(parser.HasSwitch(_T("FOOP")));
+  EXPECT_FALSE(parser.HasSwitch(_T("blAH")));
+}
+
+TEST(CommandLineParserTest, ParseFromString_OneSwitchNoArgs) {
+  CommandLineParser parser;
+  int arg_count = 0;
+  EXPECT_SUCCEEDED(parser.ParseFromString(_T("myprog.exe /foo")));
+  EXPECT_EQ(1, parser.GetSwitchCount());
+  EXPECT_TRUE(parser.HasSwitch(_T("foo")));
+  EXPECT_SUCCEEDED(parser.GetSwitchArgumentCount(_T("foo"), &arg_count));
+  EXPECT_EQ(0, arg_count);
+}
+
+TEST(CommandLineParserTest, ParseFromString_OneSwitchOneArg) {
+  CommandLineParser parser;
+  int arg_count = 0;
+  CString arg_value;
+  EXPECT_SUCCEEDED(parser.ParseFromString(_T("myprog.exe /foo bar")));
+  EXPECT_EQ(1, parser.GetSwitchCount());
+  EXPECT_TRUE(parser.HasSwitch(_T("foo")));
+  EXPECT_SUCCEEDED(parser.GetSwitchArgumentCount(_T("foo"), &arg_count));
+  EXPECT_EQ(1, arg_count);
+  EXPECT_SUCCEEDED(parser.GetSwitchArgumentValue(_T("foo"), 0, &arg_value));
+  EXPECT_STREQ(_T("bar"), arg_value);
+}
+
+TEST(CommandLineParserTest, ParseFromString_OneSwitchTwoArgs) {
+  CommandLineParser parser;
+  int arg_count = 0;
+  CString arg_value;
+  EXPECT_SUCCEEDED(parser.ParseFromString(_T("myprog.exe /foo bar baz")));
+  EXPECT_EQ(1, parser.GetSwitchCount());
+  EXPECT_TRUE(parser.HasSwitch(_T("foo")));
+  EXPECT_SUCCEEDED(parser.GetSwitchArgumentCount(_T("foo"), &arg_count));
+  EXPECT_EQ(2, arg_count);
+  EXPECT_SUCCEEDED(parser.GetSwitchArgumentValue(_T("foo"), 0, &arg_value));
+  EXPECT_STREQ(_T("bar"), arg_value);
+  EXPECT_SUCCEEDED(parser.GetSwitchArgumentValue(_T("foo"), 1, &arg_value));
+  EXPECT_STREQ(_T("baz"), arg_value);
+}
+
+TEST(CommandLineParserTest, ParseFromString_TwoSwitchesNoArgs) {
+  CommandLineParser parser;
+  int arg_count = 0;
+  CString arg_value;
+  EXPECT_SUCCEEDED(parser.ParseFromString(_T("myprog.exe /foo /bar")));
+  EXPECT_EQ(2, parser.GetSwitchCount());
+  EXPECT_TRUE(parser.HasSwitch(_T("foo")));
+  EXPECT_TRUE(parser.HasSwitch(_T("bar")));
+  EXPECT_SUCCEEDED(parser.GetSwitchArgumentCount(_T("foo"), &arg_count));
+  EXPECT_EQ(0, arg_count);
+  EXPECT_SUCCEEDED(parser.GetSwitchArgumentCount(_T("bar"), &arg_count));
+  EXPECT_EQ(0, arg_count);
+}
+
+TEST(CommandLineParserTest, ParseFromString_TwoSwitchesOneArgNoArg) {
+  CommandLineParser parser;
+  int arg_count = 0;
+  CString arg_value;
+  EXPECT_SUCCEEDED(parser.ParseFromString(_T("myprog.exe /foo blech /bar")));
+  EXPECT_EQ(2, parser.GetSwitchCount());
+  EXPECT_TRUE(parser.HasSwitch(_T("foo")));
+  EXPECT_TRUE(parser.HasSwitch(_T("bar")));
+  EXPECT_SUCCEEDED(parser.GetSwitchArgumentCount(_T("foo"), &arg_count));
+  EXPECT_EQ(1, arg_count);
+  EXPECT_SUCCEEDED(parser.GetSwitchArgumentValue(_T("foo"), 0, &arg_value));
+  EXPECT_STREQ(_T("blech"), arg_value);
+  EXPECT_SUCCEEDED(parser.GetSwitchArgumentCount(_T("bar"), &arg_count));
+  EXPECT_EQ(0, arg_count);
+}
+
+TEST(CommandLineParserTest, ParseFromString_ArgInQuotesWithLeadingSlash) {
+  CommandLineParser parser;
+  int arg_count = 0;
+  CString arg_value;
+  EXPECT_SUCCEEDED(parser.ParseFromString(_T("f.exe /pi \"arg\" \"/sw x\"")));
+  EXPECT_EQ(1, parser.GetSwitchCount());
+  EXPECT_TRUE(parser.HasSwitch(_T("pi")));
+  EXPECT_SUCCEEDED(parser.GetSwitchArgumentCount(_T("pi"), &arg_count));
+  EXPECT_EQ(2, arg_count);
+  EXPECT_SUCCEEDED(parser.GetSwitchArgumentValue(_T("pi"), 0, &arg_value));
+  EXPECT_STREQ(_T("arg"), arg_value);
+  EXPECT_SUCCEEDED(parser.GetSwitchArgumentValue(_T("pi"), 1, &arg_value));
+  EXPECT_STREQ(_T("/sw x"), arg_value);
+}
+
+// Paths with spaces and no enclosing quotes is not supported. Thus, there is
+// no test for it.
+TEST(CommandLineParserTest, ParseFromString_SpaceInPathWithQuotes) {
+  CommandLineParser parser;
+  int arg_count = 0;
+  CString arg_value;
+  EXPECT_SUCCEEDED(
+      parser.ParseFromString(_T("\"C:\\Space In Path\\myprog.exe\" /foo bar")));
+  EXPECT_EQ(1, parser.GetSwitchCount());
+  EXPECT_TRUE(parser.HasSwitch(_T("foo")));
+  EXPECT_SUCCEEDED(parser.GetSwitchArgumentCount(_T("foo"), &arg_count));
+  EXPECT_EQ(1, arg_count);
+  EXPECT_SUCCEEDED(parser.GetSwitchArgumentValue(_T("foo"), 0, &arg_value));
+  EXPECT_STREQ(_T("bar"), arg_value);
+}
+
+}  // namespace omaha
diff --git a/base/command_line_validator.cc b/base/command_line_validator.cc
new file mode 100644
index 0000000..ef2cc76
--- /dev/null
+++ b/base/command_line_validator.cc
@@ -0,0 +1,256 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/command_line_validator.h"
+
+#include <atlbase.h>
+
+#include "omaha/base/command_line_parser.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+CommandLineValidator::CommandLineValidator() : scenario_sequence_number_(0) {
+}
+
+CommandLineValidator::~CommandLineValidator() {
+  Clear();
+}
+
+HRESULT CommandLineValidator::CreateScenario(const CString& scenario_name) {
+  if (scenarios_.find(scenario_name) != scenarios_.end()) {
+    return E_INVALIDARG;
+  }
+
+  ScenarioParameters scenario_parameters;
+  scenarios_[scenario_name] = scenario_parameters;
+  return S_OK;
+}
+
+// TODO(Omaha): Instead of creating the scenario in the map then populating it,
+// which requires these methods to know about the map, verify the scenario
+// exists, etc. - why not build the scenario then add it to the map? That seems
+// more straightforward.
+HRESULT CommandLineValidator::AddScenarioParameter(
+    const CString& scenario_name,
+    const CString& switch_name,
+    int num_required_parameters) {
+  MapScenariosIter iter = scenarios_.find(scenario_name);
+  if (iter == scenarios_.end()) {
+    return E_INVALIDARG;
+  }
+
+  ScenarioParameter* scenario_parameter =
+      new ScenarioParameter(switch_name, num_required_parameters);
+  (*iter).second.required.push_back(scenario_parameter);
+  return S_OK;
+}
+
+HRESULT CommandLineValidator::AddOptionalScenarioParameter(
+    const CString& scenario_name,
+    const CString& switch_name,
+    int num_required_parameters) {
+  MapScenariosIter iter = scenarios_.find(scenario_name);
+  if (iter == scenarios_.end()) {
+    return E_INVALIDARG;
+  }
+
+  ScenarioParameter* scenario_parameter =
+      new ScenarioParameter(switch_name, num_required_parameters);
+  (*iter).second.optional.push_back(scenario_parameter);
+  return S_OK;
+}
+
+HRESULT CommandLineValidator::CreateScenarioFromCmdLine(
+    const CString& command_line,
+    CString* scenario_name) {
+  ASSERT1(scenario_name);
+
+  CommandLineParser parser;
+  HRESULT hr = parser.ParseFromString(command_line);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Generate a unique scenario name.
+  CString scenario_name_str;
+  do {
+    ++scenario_sequence_number_;
+    scenario_name_str.Format(_T("scenario_%d"), scenario_sequence_number_);
+  } while (scenarios_.find(scenario_name_str) != scenarios_.end());
+
+  CreateScenario(scenario_name_str);
+
+  int switch_count = parser.GetSwitchCount();
+  for (int idx_switch = 0; idx_switch < switch_count; ++idx_switch) {
+    CString switch_name;
+    hr = parser.GetSwitchNameAtIndex(idx_switch, &switch_name);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    int arg_count = 0;
+    hr = parser.GetSwitchArgumentCount(switch_name, &arg_count);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = AddScenarioParameter(scenario_name_str, switch_name, arg_count);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  switch_count = parser.GetOptionalSwitchCount();
+  for (int idx_switch = 0; idx_switch < switch_count; ++idx_switch) {
+    CString switch_name;
+    hr = parser.GetOptionalSwitchNameAtIndex(idx_switch, &switch_name);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    int arg_count = 0;
+    hr = parser.GetOptionalSwitchArgumentCount(switch_name, &arg_count);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = AddOptionalScenarioParameter(scenario_name_str,
+                                      switch_name,
+                                      arg_count);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  *scenario_name = scenario_name_str;
+
+  return S_OK;
+}
+
+HRESULT CommandLineValidator::Validate(
+    const CommandLineParser& command_line_parser,
+    CString* scenario_name) const {
+  // Attempt to verify the data within the command_line_parser against each of
+  // the scenarios.
+  MapScenariosConstIter scenarios_iter;
+  for (scenarios_iter = scenarios_.begin();
+       scenarios_iter != scenarios_.end();
+       ++scenarios_iter) {
+    // Make sure we have a match for the number of switches in this scenario.
+    int parser_switch_count = command_line_parser.GetSwitchCount();
+    int scenario_required_switch_count =
+        (*scenarios_iter).second.required.size();
+    int scenario_optional_switch_count =
+        (*scenarios_iter).second.optional.size();
+
+    if (parser_switch_count < scenario_required_switch_count ||
+        parser_switch_count > scenario_required_switch_count +
+                              scenario_optional_switch_count) {
+      continue;
+    }
+
+    if (DoesScenarioMatch(command_line_parser, (*scenarios_iter).second)) {
+      *scenario_name = (*scenarios_iter).first;
+      return S_OK;
+    }
+  }
+
+  return GOOGLEUPDATE_COMMANDLINE_E_NO_SCENARIO_HANDLER_MATCHED;
+}
+
+bool CommandLineValidator::DoesScenarioMatch(
+    const CommandLineParser& command_line_parser,
+    const ScenarioParameters& scenario_parameters) const {
+  // Make sure that each switch matches with the right number of arguments.
+  ScenarioParameterVectorConstIter parameter_iter;
+  for (parameter_iter = scenario_parameters.required.begin();
+       parameter_iter != scenario_parameters.required.end();
+       ++parameter_iter) {
+    CString current_switch_name = (*parameter_iter)->switch_name_;
+    // This would probably allow duplicate switches (i.e. /c /c) in a command
+    // line.
+    if (!command_line_parser.HasSwitch(current_switch_name)) {
+      return false;
+    }
+
+    int arg_count = 0;
+    HRESULT hr = command_line_parser.GetSwitchArgumentCount(current_switch_name,
+                                                            &arg_count);
+    if (FAILED(hr)) {
+      return false;
+    }
+
+    int switch_arg_count = (*parameter_iter)->num_required_parameters_;
+    if (arg_count != switch_arg_count) {
+      return false;
+    }
+  }
+
+  int parser_optional_switch_count = command_line_parser.GetSwitchCount() -
+                                     scenario_parameters.required.size();
+  for (parameter_iter = scenario_parameters.optional.begin();
+       parser_optional_switch_count != 0 &&
+           parameter_iter != scenario_parameters.optional.end();
+       ++parameter_iter) {
+    CString current_switch_name = (*parameter_iter)->switch_name_;
+    // This would probably allow duplicate optional switches (i.e. /oem /oem) in
+    // a command line.
+    if (!command_line_parser.HasSwitch(current_switch_name)) {
+      continue;
+    }
+
+    int arg_count = 0;
+    HRESULT hr = command_line_parser.GetSwitchArgumentCount(current_switch_name,
+                                                            &arg_count);
+    if (FAILED(hr)) {
+      return false;
+    }
+
+    int switch_arg_count = (*parameter_iter)->num_required_parameters_;
+    if (arg_count != switch_arg_count) {
+      return false;
+    }
+    --parser_optional_switch_count;
+  }
+
+  return parser_optional_switch_count == 0;
+}
+
+void CommandLineValidator::Clear() {
+  MapScenariosIter scenarios_iter;
+  for (scenarios_iter = scenarios_.begin();
+       scenarios_iter != scenarios_.end();
+       ++scenarios_iter) {
+    ScenarioParameterVectorIter param_iter;
+    for (param_iter = (*scenarios_iter).second.required.begin();
+         param_iter != (*scenarios_iter).second.required.end();
+         ++param_iter) {
+      delete *param_iter;
+    }
+    for (param_iter = (*scenarios_iter).second.optional.begin();
+         param_iter != (*scenarios_iter).second.optional.end();
+         ++param_iter) {
+      delete *param_iter;
+    }
+  }
+  scenarios_.clear();
+}
+
+}  // namespace omaha
+
diff --git a/base/command_line_validator.h b/base/command_line_validator.h
new file mode 100644
index 0000000..f20fa21
--- /dev/null
+++ b/base/command_line_validator.h
@@ -0,0 +1,106 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#ifndef OMAHA_BASE_COMMAND_LINE_VALIDATOR_H__
+#define OMAHA_BASE_COMMAND_LINE_VALIDATOR_H__
+
+#include <windows.h>
+#include <atlstr.h>
+#include <map>
+#include <vector>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+class CommandLineParser;
+
+// This class allows creation of scenarios for command line combinations and
+// then provides a mechanism to validate a command line against those scenarios
+// to determine if there's a match.
+class CommandLineValidator {
+ public:
+  class ScenarioParameter {
+   public:
+    ScenarioParameter(const TCHAR* switch_name, int num_required_parameters)
+      : switch_name_(switch_name),
+        num_required_parameters_(num_required_parameters) {
+    }
+    ~ScenarioParameter() {}
+
+    CString switch_name_;
+    int num_required_parameters_;
+
+   private:
+    DISALLOW_EVIL_CONSTRUCTORS(ScenarioParameter);
+  };
+
+  typedef std::vector<ScenarioParameter*> ScenarioParameterVector;
+  typedef ScenarioParameterVector::iterator ScenarioParameterVectorIter;
+  typedef ScenarioParameterVector::const_iterator
+      ScenarioParameterVectorConstIter;
+
+  struct ScenarioParameters {
+   public:
+    ScenarioParameterVector required;
+    ScenarioParameterVector optional;
+  };
+
+  typedef std::map<CString, ScenarioParameters> MapScenarios;
+  typedef MapScenarios::iterator MapScenariosIter;
+  typedef MapScenarios::const_iterator MapScenariosConstIter;
+
+  CommandLineValidator();
+  ~CommandLineValidator();
+
+  void Clear();
+
+  // Parses a command line rule and builds a scenario from it.  Returns a
+  // generated scenario name.
+  // Rules have required and optional parameters. An example of a rule is:
+  //     "gu.exe /install <extraargs> [/oem [/appargs <appargs> [/silent"
+  HRESULT CreateScenarioFromCmdLine(const CString& command_line,
+                                    CString* scenario_name);
+
+  // Validates a CommandLineParser against all scenarios.  If a match, returns
+  // S_OK and the scenario_name.  Fails if not a match.
+  // command_line_parser must already be compiled before calling.
+  HRESULT Validate(const CommandLineParser& command_line_parser,
+                   CString* scenario_name) const;
+
+  // Creates a scenario by name.
+  HRESULT CreateScenario(const CString& scenario_name);
+
+  // Adds a switch and its parameter count to an existing scenario.
+  HRESULT AddScenarioParameter(const CString& scenario_name,
+                               const CString& switch_name,
+                               int num_required_parameters);
+  HRESULT AddOptionalScenarioParameter(const CString& scenario_name,
+                                       const CString& switch_name,
+                                       int num_required_parameters);
+
+ private:
+  bool DoesScenarioMatch(const CommandLineParser& command_line_parser,
+                         const ScenarioParameters& scenario_parameters) const;
+
+  int scenario_sequence_number_;
+  MapScenarios scenarios_;
+  DISALLOW_EVIL_CONSTRUCTORS(CommandLineValidator);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_COMMAND_LINE_VALIDATOR_H__
+
diff --git a/base/command_line_validator_unittest.cc b/base/command_line_validator_unittest.cc
new file mode 100644
index 0000000..3979260
--- /dev/null
+++ b/base/command_line_validator_unittest.cc
@@ -0,0 +1,129 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/command_line_validator.h"
+#include "omaha/base/command_line_parser.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+const TCHAR* kScenario1Name = _T("core");
+const TCHAR* kScenario2Name = _T("SomeScenario");
+const TCHAR* kScenario3Name = _T("OtherMechanism");
+
+const TCHAR* kScenario1CmdLine = _T("program.exe /lang foo");
+const TCHAR* kScenario2CmdLine = _T("program.exe /install x y /service");
+const TCHAR* kScenario3CmdLine = _T("prog.exe /install x y /service /lang en");
+
+const TCHAR* kLangSwitch = _T("lang");
+const int kLangSwitchArgCount = 1;
+const TCHAR* kInstallSwitch = _T("install");
+const int kInstallSwitchArgCount = 2;
+const TCHAR* kServiceSwitch = _T("service");
+const int kServiceSwitchArgCount = 0;
+
+class CommandLineValidatorTest : public testing::Test {
+ public:
+
+ protected:
+  CommandLineValidatorTest() {
+  }
+
+  virtual void SetUp() {
+    scenario_match_name_.Empty();
+
+    // This validator only has one scenario.
+    validator1_.Clear();
+    // "program.exe /lang foo"
+    validator1_.CreateScenario(kScenario1Name);
+    validator1_.AddScenarioParameter(kScenario1Name,
+                                     kLangSwitch,
+                                     kLangSwitchArgCount);
+
+    // This validator has three scenarios.
+    validator2_.Clear();
+    // "program.exe /lang foo"
+    validator2_.CreateScenario(kScenario1Name);
+    validator2_.AddScenarioParameter(kScenario1Name,
+                                     kLangSwitch,
+                                     kLangSwitchArgCount);
+
+    // "program.exe /install x y /service"
+    validator2_.CreateScenario(kScenario2Name);
+    validator2_.AddScenarioParameter(kScenario2Name,
+                                     kInstallSwitch,
+                                     kInstallSwitchArgCount);
+    validator2_.AddScenarioParameter(kScenario2Name,
+                                     kServiceSwitch,
+                                     kServiceSwitchArgCount);
+
+    // "program.exe /install x y /service /lang en"
+    validator2_.CreateScenario(kScenario3Name);
+    validator2_.AddScenarioParameter(kScenario3Name,
+                                     kInstallSwitch,
+                                     kInstallSwitchArgCount);
+    validator2_.AddScenarioParameter(kScenario3Name,
+                                     kServiceSwitch,
+                                     kServiceSwitchArgCount);
+    validator2_.AddScenarioParameter(kScenario3Name,
+                                     kLangSwitch,
+                                     kLangSwitchArgCount);
+  }
+
+  virtual void TearDown() {
+  }
+
+  CommandLineValidator validator1_;
+  CommandLineValidator validator2_;
+  CommandLineParser parser_;
+  CString scenario_match_name_;
+};
+
+TEST_F(CommandLineValidatorTest, BasicScenarioPass) {
+  EXPECT_SUCCEEDED(parser_.ParseFromString(kScenario1CmdLine));
+  EXPECT_SUCCEEDED(validator1_.Validate(parser_, &scenario_match_name_));
+  EXPECT_STREQ(kScenario1Name, scenario_match_name_);
+}
+
+TEST_F(CommandLineValidatorTest, BasicScenarioFail) {
+  EXPECT_SUCCEEDED(parser_.ParseFromString(_T("goopdate.exe /something bad")));
+  EXPECT_FAILED(validator1_.Validate(parser_, &scenario_match_name_));
+}
+
+TEST_F(CommandLineValidatorTest, Scenario1PassMulti) {
+  EXPECT_SUCCEEDED(parser_.ParseFromString(kScenario1CmdLine));
+  EXPECT_SUCCEEDED(validator2_.Validate(parser_, &scenario_match_name_));
+  EXPECT_STREQ(kScenario1Name, scenario_match_name_);
+}
+
+TEST_F(CommandLineValidatorTest, Scenario2PassMulti) {
+  EXPECT_SUCCEEDED(parser_.ParseFromString(kScenario2CmdLine));
+  EXPECT_SUCCEEDED(validator2_.Validate(parser_, &scenario_match_name_));
+  EXPECT_STREQ(kScenario2Name, scenario_match_name_);
+}
+
+TEST_F(CommandLineValidatorTest, Scenario3PassMulti) {
+  EXPECT_SUCCEEDED(parser_.ParseFromString(kScenario3CmdLine));
+  EXPECT_SUCCEEDED(validator2_.Validate(parser_, &scenario_match_name_));
+  EXPECT_STREQ(kScenario3Name, scenario_match_name_);
+}
+
+TEST_F(CommandLineValidatorTest, ScenarioFailMulti) {
+  EXPECT_SUCCEEDED(parser_.ParseFromString(_T("Goopdate.exe /fail me /here")));
+  EXPECT_FAILED(validator2_.Validate(parser_, &scenario_match_name_));
+}
+
+}  // namespace omaha
+
diff --git a/base/commands.cc b/base/commands.cc
new file mode 100644
index 0000000..96aa896
--- /dev/null
+++ b/base/commands.cc
@@ -0,0 +1,578 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Parse command-line options
+
+#include "omaha/base/commands.h"
+#include <cstdlib>
+#include "base/scoped_ptr.h"
+#include "omaha/base/cgi.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+#define kNameValueChar  _T('=')
+#define kTrueValue      _T("true")
+#define kFalseValue     _T("false")
+#define kOnValue        _T("on")
+#define kOffValue       _T("off")
+
+
+//
+// Helper functions
+//
+
+template<class T>
+HRESULT ConvertValue(const TCHAR* str_value, T* value);
+
+// Convert the three-valued value from the string representation
+template<>
+HRESULT ConvertValue<ThreeValue>(const TCHAR* str_value, ThreeValue* value) {
+  ASSERT1(value);
+
+  *value = VALUE_NOT_SET;
+  if (str_value && *str_value) {
+    if (String_StrNCmp(str_value,
+                       kTrueValue,
+                       TSTR_SIZE(kTrueValue) + 1,
+                       true) == 0 ||
+        String_StrNCmp(str_value,
+                       kOnValue,
+                       TSTR_SIZE(kOnValue) + 1,
+                       true) == 0) {
+      *value = TRUE_VALUE;
+    } else if (String_StrNCmp(str_value,
+                              kFalseValue,
+                              TSTR_SIZE(kFalseValue) + 1,
+                              true) == 0 ||
+               String_StrNCmp(str_value,
+                              kOffValue,
+                              TSTR_SIZE(kOffValue) + 1,
+                              true) == 0) {
+      *value = FALSE_VALUE;
+    } else {
+      return CI_E_INVALID_ARG;
+    }
+  }
+  return S_OK;
+}
+
+// Convert the int value from the string representation
+template<>
+HRESULT ConvertValue<int>(const TCHAR* str_value, int* value) {
+  ASSERT1(str_value && *str_value);
+  ASSERT1(value);
+
+  if (_set_errno(0)) {
+    return E_FAIL;
+  }
+
+  *value = _tcstol(str_value, NULL, 0);
+  if (errno == ERANGE) {
+    return CI_E_INVALID_ARG;
+  }
+  return S_OK;
+}
+
+// Convert the unsigned int value from the string representation
+template<>
+HRESULT ConvertValue<uint32>(const TCHAR* str_value, uint32* value) {
+  ASSERT1(str_value && *str_value);
+  ASSERT1(value);
+
+  if (_set_errno(0)) {
+    return E_FAIL;
+  }
+
+  *value = _tcstoul(str_value, NULL, 0);
+  if (errno == ERANGE) {
+    return CI_E_INVALID_ARG;
+  }
+  return S_OK;
+}
+
+// Convert the string value from the string representation
+HRESULT ConvertValue(const TCHAR* str_value, CString* value, bool to_unescape) {
+  ASSERT1(str_value && *str_value);
+  ASSERT1(value);
+
+  *value = str_value;
+
+  if (to_unescape) {
+    int length = value->GetLength();
+    scoped_array<TCHAR> unescaped_value(new TCHAR[length + 1]);
+    RET_IF_FALSE(CGI::UnescapeString(*value, length, unescaped_value.get(),
+      length + 1), CI_E_INVALID_ARG);
+    *value = unescaped_value.get();
+  }
+
+  return S_OK;
+}
+
+//
+// Struct CommandOption
+//
+void CommandOption::Init(const TCHAR* name, CommandOptionType type,
+                         void* value, int max_value_len) {
+  this->name = name;
+  this->type = type;
+  this->value = value;
+  this->max_value_len = max_value_len;
+}
+
+void CommandOption::Copy(const CommandOption& option) {
+  Init(option.name, option.type, option.value, option.max_value_len);
+}
+
+//
+// Class CommandParsingSimple
+//
+
+// Constructor
+CommandParsingSimple::CommandParsingSimple()
+    : separator_(_T(' ')) {
+}
+
+// Constructor
+CommandParsingSimple::CommandParsingSimple(TCHAR separator)
+    : separator_(separator) {
+}
+
+// Parse a command line string into args
+HRESULT CommandParsingSimple::ParseSimple(const TCHAR* cmd_line) {
+  ASSERT1(cmd_line);
+
+  UTIL_LOG(L3, (_T("[CommandParsingSimple::ParseSimple][%s]"), cmd_line));
+
+  args_.clear();
+
+  // Split command line string into list of arguments
+  for (const TCHAR* s = cmd_line; *s; ++s) {
+    // Handle separator
+    if (*s == separator_) {
+      continue;
+    }
+
+    // Handle single/double quote
+    if (*s == _T('"') || *s == _T('\'')) {
+      int right_quote = String_FindChar(s + 1, *s);
+      if (right_quote == -1) {
+        UTIL_LOG(LE, (_T("[CommandParsingSimple::ParseSimple]")
+                      _T("[single/double quote mismatches]")));
+        return CI_E_INVALID_ARG;
+      }
+      args_.push_back(CString(s + 1, right_quote));
+      s += right_quote + 1;
+      continue;
+    }
+
+    // Handle all other char
+    int next_space = String_FindChar(s + 1, separator_);
+    if (next_space == -1) {
+      args_.push_back(CString(s));
+      break;
+    } else {
+      args_.push_back(CString(s, next_space + 1));
+      s += next_space + 1;
+    }
+  }
+
+  return S_OK;
+}
+
+// Get the arg at specified position from the command line
+HRESULT CommandParsingSimple::GetAt(uint32 position, CString* arg) {
+  ASSERT1(arg);
+  ASSERT1(position < args_.size());
+
+  if (!arg || position >= args_.size()) {
+    return E_INVALIDARG;
+  }
+
+  *arg = args_[position];
+  return S_OK;
+}
+
+// Remove the arg at specified position from the command line
+HRESULT CommandParsingSimple::RemoveAt(uint32 position) {
+  ASSERT1(position < args_.size());
+
+  if (position >= args_.size()) {
+    return E_INVALIDARG;
+  }
+
+  uint32 i = 0;
+  std::vector<CString>::iterator it(args_.begin());
+  for (; i < position; ++it, ++i) {
+    ASSERT1(it != args_.end());
+  }
+  args_.erase(it);
+  return S_OK;
+}
+
+// Converted to the string
+HRESULT CommandParsingSimple::ToString(CString* cmd_line) {
+  ASSERT1(cmd_line);
+
+  bool is_first = true;
+  cmd_line->Empty();
+  for (std::vector<CString>::const_iterator it(args_.begin());
+       it != args_.end();
+       ++it) {
+    if (is_first) {
+      is_first = false;
+    } else {
+      cmd_line->AppendChar(separator_);
+    }
+    const TCHAR* arg = it->GetString();
+    if (String_FindChar(arg, separator_) != -1) {
+      cmd_line->AppendChar(_T('"'));
+      cmd_line->Append(arg);
+      cmd_line->AppendChar(_T('"'));
+    } else {
+      cmd_line->Append(arg);
+    }
+  }
+
+  return S_OK;
+}
+
+// Static Helper function that splits a command line
+// string into executable and any arguments
+HRESULT CommandParsingSimple::SplitExeAndArgs(const TCHAR* cmd_line,
+                                              CString* exe,
+                                              CString* args) {
+  ASSERT1(cmd_line);
+  ASSERT1(exe);
+  ASSERT1(args);
+
+  // Do the parsing
+  CommandParsingSimple cmd_parsing_simple;
+
+  RET_IF_FAILED(cmd_parsing_simple.ParseSimple(cmd_line));
+  RET_IF_FAILED(cmd_parsing_simple.GetAt(0, exe));
+  exe->Trim();
+  RET_IF_FAILED(cmd_parsing_simple.RemoveAt(0));
+  return (cmd_parsing_simple.ToString(args));
+}
+
+HRESULT CommandParsingSimple::SplitExeAndArgsGuess(const TCHAR* cmd_line,
+                                                   CString* exe,
+                                                   CString* args) {
+  ASSERT1(cmd_line);
+  ASSERT1(exe);
+  ASSERT1(args);
+
+  if (File::Exists(cmd_line)) {
+    // Optimization for the single executable case.
+    // Fill the [out] parameters and return.
+    *exe = cmd_line;
+    exe->Trim();
+    args->Empty();
+    return S_OK;
+  }
+
+  CString command_line(cmd_line);
+  // Check if the command line is properly enclosed, or that it does not have
+  // spaces
+  if (command_line.GetAt(0) != _T('"') && command_line.Find(_T(' ')) != -1) {
+    // File::Exists() does not handle leading spaces so remove it.
+    command_line.Trim();
+
+    // If not, need to find the executable, and if valid, enclose it in
+    // double quotes
+    const TCHAR* index_dot_exe = stristrW(command_line.GetString(), _T(".EXE"));
+
+    if (index_dot_exe != NULL) {
+      int dot_exe_end = (index_dot_exe - command_line.GetString())
+                         + arraysize(_T(".EXE")) - 1;
+      if (File::Exists(CString(command_line, dot_exe_end))) {
+        // Enclose the EXE in double quotes
+        command_line.Insert(dot_exe_end, _T('"'));
+        command_line.Insert(0, _T('"'));
+      } else {
+        UTIL_LOG(L1, (_T("[CommandParsing::SplitExeAndArgsGuess]")
+                      _T("[Could not guess the Executable file within [%s]. ")
+                      _T("Passing on to SplitExeAndArgs as-is."),
+                      command_line));
+      }
+    }
+  }
+
+  // Do the parsing
+  return SplitExeAndArgs(command_line, exe, args);
+}
+
+
+// Static Helper function that returns the number of arguments
+// in the passed in cmd_line
+HRESULT CommandParsingSimple::GetNumberOfArgs(const TCHAR* cmd_line,
+                                              uint32* number_of_args) {
+  ASSERT1(cmd_line);
+  ASSERT1(number_of_args);
+
+  // Do the parsing
+  CommandParsingSimple cmd_parsing_simple;
+
+  RET_IF_FAILED(cmd_parsing_simple.ParseSimple(cmd_line));
+  *number_of_args = cmd_parsing_simple.args_.size();
+  return S_OK;
+}
+
+
+//
+// Class CommandParsing
+//
+
+// Constructor
+CommandParsing::CommandParsing(CommandOption* options, int options_count)
+    : CommandParsingSimple(),
+      options_(options),
+      options_count_(options_count),
+      as_name_value_pair_(false) {
+}
+
+// Constructor
+CommandParsing::CommandParsing(CommandOption* options, int options_count,
+                               TCHAR separator, bool as_name_value_pair)
+    : CommandParsingSimple(separator),
+      options_(options),
+      options_count_(options_count),
+      as_name_value_pair_(as_name_value_pair) {
+}
+
+// Parse a command line string
+HRESULT CommandParsing::Parse(const TCHAR* cmd_line, bool ignore_unknown_args) {
+  ASSERT1(cmd_line);
+
+  UTIL_LOG(L3, (_T("[CommandParsing::Parse][%s][%d]"),
+                cmd_line, ignore_unknown_args));
+
+  // Parse into args_ vector
+  RET_IF_FAILED(ParseSimple(cmd_line));
+
+  // Do the internal parsing
+  return InternalParse(ignore_unknown_args);
+}
+
+// Parse a list of command line arguments
+HRESULT CommandParsing::ParseArguments(int argc, TCHAR* argv[]) {
+  if (argc <= 1) {
+    return S_OK;
+  }
+
+  // Push each argument
+  args_.clear();
+  for (int i = 1; i < argc; ++i) {
+    args_.push_back(CString(argv[i]));
+  }
+
+  // Do the internal parsing
+  return InternalParse(false);
+}
+
+// Internal parsing
+HRESULT CommandParsing::InternalParse(bool ignore_unknown_args) {
+  CString name, value;
+  for (std::vector<CString>::const_iterator it(args_.begin());
+       it != args_.end();
+       ++it) {
+    RET_IF_FAILED(ExtractName(&name, &it));
+
+    int i = FindOption(name);
+    if (i == -1) {
+      if (ignore_unknown_args) {
+        UTIL_LOG(L3, (_T("[CommandParsing::Parse][unknown arg %s]"), name));
+        continue;
+      } else {
+        UTIL_LOG(LE, (_T("[CommandParsing::Parse][invalid arg %s]"), name));
+        return CI_E_INVALID_ARG;
+      }
+    }
+
+    if (options_[i].type != COMMAND_OPTION_BOOL) {
+      RET_IF_FAILED(ExtractValue(options_[i], &value, &it, args_.end()));
+    }
+
+    switch (options_[i].type & COMMAND_OPTION_FLAGS_MASK) {
+      case COMMAND_OPTION_BOOL: {
+        bool bool_value = true;
+        SetParsedValue(options_[i], bool_value);
+        break;
+      }
+
+      case COMMAND_OPTION_THREE: {
+        ThreeValue three_value = VALUE_NOT_SET;
+        RET_IF_FAILED(ConvertValue(value, &three_value));
+        SetParsedValue(options_[i], three_value);
+        break;
+      }
+
+      case COMMAND_OPTION_INT: {
+        int int_value = 0;
+        RET_IF_FAILED(ConvertValue(value, &int_value));
+        SetParsedValue(options_[i], int_value);
+        break;
+      }
+
+      case COMMAND_OPTION_UINT: {
+        int uint_value = 0;
+        RET_IF_FAILED(ConvertValue(value, &uint_value));
+        SetParsedValue(options_[i], uint_value);
+        break;
+      }
+
+      case COMMAND_OPTION_STRING: {
+        CString str_value;
+        bool is_unescape = (options_[i].type & COMMAND_OPTION_UNESCAPE) != 0;
+        RET_IF_FAILED(ConvertValue(value, &str_value, is_unescape));
+        SetParsedValue(options_[i], str_value);
+        break;
+      }
+
+      default:
+        ASSERT1(false);
+        break;
+    }
+  }
+
+  return S_OK;
+}
+
+// Extract the name
+HRESULT CommandParsing::ExtractName(CString* name,
+                                    std::vector<CString>::const_iterator* it) {
+  ASSERT1(name);
+  ASSERT1(it);
+
+  if (as_name_value_pair_) {
+    int idx = (*it)->Find(kNameValueChar);
+    if (idx == -1) {
+      return CI_E_INVALID_ARG;
+    } else {
+      *name = (*it)->Left(idx);
+    }
+  } else {
+    *name = (*it)->GetString();
+  }
+  return S_OK;
+}
+
+// Extract the value
+// Also validate the value length if necessary
+HRESULT CommandParsing::ExtractValue(
+    const CommandOption& option,
+    CString* value,
+    std::vector<CString>::const_iterator* it,
+    const std::vector<CString>::const_iterator& end) {
+  ASSERT1(value);
+  ASSERT1(it);
+
+  if (as_name_value_pair_) {
+    int idx = (*it)->Find(kNameValueChar);
+    if (idx == -1) {
+      return CI_E_INVALID_ARG;
+    } else {
+      *value = (*it)->Right((*it)->GetLength() - idx - 1);
+    }
+  } else {
+    ++(*it);
+    if (*it == end) {
+      UTIL_LOG(LE, (_T("[CommandParsing::ExtractValue]")
+                    _T("[argument %s missing value]"), option.name));
+      return CI_E_INVALID_ARG;
+    }
+    *value = (*it)->GetString();
+  }
+
+  if (option.max_value_len >= 0) {
+    if (value->GetLength() > option.max_value_len) {
+      return CI_E_INVALID_ARG;
+    }
+  }
+
+  return S_OK;
+}
+
+// Set the parsed value
+template<class T>
+void CommandParsing::SetParsedValue(const CommandOption& option,
+                                    const T& value) {
+  if (option.type & COMMAND_OPTION_MULTIPLE) {
+    ASSERT((option.type & COMMAND_OPTION_FLAGS_MASK) != COMMAND_OPTION_BOOL,
+      (_T("COMMAND_OPTION_BOOL can't be used with COMMAND_OPTION_MULTIPLE")));
+    ASSERT((option.type & COMMAND_OPTION_FLAGS_MASK) != COMMAND_OPTION_THREE,
+      (_T("COMMAND_OPTION_THREE can't be used with COMMAND_OPTION_MULTIPLE")));
+
+    std::vector<T>* ptr = reinterpret_cast<std::vector<T>*>(option.value);
+    ptr->push_back(value);
+  } else {
+    T* ptr = reinterpret_cast<T*>(option.value);
+    *ptr = value;
+  }
+}
+
+// Helper function to find an option in the CommandOption list
+int CommandParsing::FindOption(const TCHAR* option_name) {
+  ASSERT1(option_name);
+
+  for (int i = 0; i < options_count_; ++i) {
+    if (String_StrNCmp(option_name,
+                       options_[i].name,
+                       options_[i].name.GetLength() + 1,
+                       false) == 0) {
+      return i;
+    }
+  }
+
+  return -1;
+}
+
+// Remove an option from the command line
+HRESULT CommandParsing::Remove(const TCHAR* option_name) {
+  ASSERT1(option_name);
+
+  for (std::vector<CString>::iterator it(args_.begin());
+       it != args_.end();
+       ++it) {
+    if (*it == option_name) {
+      int i = FindOption(option_name);
+      if (i == -1) {
+        return E_FAIL;
+      }
+      args_.erase(it);
+      if (!as_name_value_pair_) {
+        if (options_[i].type != COMMAND_OPTION_BOOL) {
+          if (it == args_.end()) {
+            return E_FAIL;
+          }
+          args_.erase(it);
+        }
+      }
+
+      return S_OK;
+    }
+  }
+
+  return E_FAIL;
+}
+
+}  // namespace omaha
+
diff --git a/common/commands.h b/base/commands.h
similarity index 100%
rename from common/commands.h
rename to base/commands.h
diff --git a/base/commands_unittest.cc b/base/commands_unittest.cc
new file mode 100644
index 0000000..8ecf2ef
--- /dev/null
+++ b/base/commands_unittest.cc
@@ -0,0 +1,430 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Unit tests of command line options parsing
+
+#include <cstdio>
+#include "omaha/base/commands.h"
+#include "omaha/base/file.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+#define kDash         _T("-")
+#define kBoolOption   _T("bool")
+#define kThreeOption  _T("three")
+#define kIntOption    _T("int")
+#define kUintOption   _T("uint")
+#define kStrOption    _T("str")
+
+#define kIEBrowserExe \
+  _T("C:\\PROGRAM FILES\\Internet Explorer\\iexplore.exe")
+
+#define kIEBrowserQuotedExe \
+  _T("\"") kIEBrowserExe _T("\"")
+
+#define kIEBrowserQuotedArgs          _T("-h \"%1\"")
+#define kIEBrowserQuotedCommandLine \
+    kIEBrowserQuotedExe _T(" ") kIEBrowserQuotedArgs
+
+#define kIEBrowserQuotedExeResult \
+    _T("C:\\PROGRAM FILES\\Internet Explorer\\iexplore.exe")
+#define kIEBrowserQuotedArgsResult    _T("-h %1")
+
+#define kIEBrowserUnquotedCommandLine \
+    _T("C:\\Program Files\\Internet Explorer\\iexplore.exe -nohome")
+#define kIEBrowserUnquotedExe \
+    _T("C:\\Program Files\\Internet Explorer\\iexplore.exe")
+#define kIEBrowserUnquotedArgs        _T("-nohome")
+
+#define kGEUninstallCommandLine       _T("RunDll32 C:\\PROGRA~1\\COMMON~1\\INSTAL~1\\PROFES~1\\RunTime\\10\\01\\Intel32\\Ctor.dll,LaunchSetup \"C:\\Program Files\\InstallShield Installation Information\\{3DE5E7D4-7B88-403C-A3FD-2017A8240C5B}\\setup.exe\" -l0x9  -removeonly")  // NOLINT
+#define kGEUninstallExe               _T("RunDll32")
+#define kGEUninstallArgs              _T("C:\\PROGRA~1\\COMMON~1\\INSTAL~1\\PROFES~1\\RunTime\\10\\01\\Intel32\\Ctor.dll,LaunchSetup \"C:\\Program Files\\InstallShield Installation Information\\{3DE5E7D4-7B88-403C-A3FD-2017A8240C5B}\\setup.exe\" -l0x9 -removeonly")            // NOLINT
+
+
+struct TestData {
+  TestData() {
+    Clear();
+  }
+
+  void Clear() {
+    bool_value = false;
+    three_value = VALUE_NOT_SET;
+    int_value = 0;
+    uint_value = 0;
+    str_value.Empty();
+  }
+
+  bool bool_value;
+  ThreeValue three_value;
+  int int_value;
+  uint32 uint_value;
+  CString str_value;
+};
+
+void FillTestData(TestData* data) {
+  data->bool_value = true;
+  data->three_value = FALSE_VALUE;
+  data->int_value = -128;
+  data->uint_value = 256;
+  data->str_value = _T("Foo");
+}
+
+void CheckTestData(const TestData& d1, const TestData& d2) {
+  EXPECT_EQ(d1.bool_value, d2.bool_value);
+  EXPECT_EQ(d1.three_value, d2.three_value);
+  EXPECT_EQ(d1.int_value, d2.int_value);
+  EXPECT_EQ(d1.uint_value, d2.uint_value);
+  EXPECT_STREQ(d1.str_value, d2.str_value);
+}
+
+TEST(CommandsTest, TraditionalCommandLineOptionsParsingTest) {
+  TestData data;
+  FillTestData(&data);
+  CString cmd_line;
+  cmd_line.Format(_T("%s%s %s ")
+                  _T("%s%s %d ")
+                  _T("%s%s %u ")
+                  _T("%s%s %s "),
+                  kDash, kThreeOption, (data.three_value == TRUE_VALUE) ? _T("on") : _T("off"),   // NOLINT
+                  kDash, kIntOption, data.int_value,
+                  kDash, kUintOption, data.uint_value,
+                  kDash, kStrOption, data.str_value);
+  if (data.bool_value) {
+    cmd_line.AppendFormat(_T("%s%s"), kDash, kBoolOption);
+  }
+
+  TestData option_data;
+  CommandOption cmd_options[] = {
+    { kDash kBoolOption,  COMMAND_OPTION_BOOL,   &option_data.bool_value,  -1 },
+    { kDash kThreeOption, COMMAND_OPTION_THREE,  &option_data.three_value, -1 },
+    { kDash kIntOption,   COMMAND_OPTION_INT,    &option_data.int_value,   -1 },
+    { kDash kUintOption,  COMMAND_OPTION_UINT,   &option_data.uint_value,  -1 },
+    { kDash kStrOption,   COMMAND_OPTION_STRING, &option_data.str_value,   -1 }
+  };
+
+  option_data.Clear();
+  CommandParsing cmd_parsing(cmd_options, arraysize(cmd_options));
+  ASSERT_SUCCEEDED(cmd_parsing.Parse(cmd_line, false));
+
+  CheckTestData(option_data, data);
+}
+
+TEST(CommandsTest, TraditionalIgnoreUnknownArgsParsingTest) {
+  TestData data;
+  FillTestData(&data);
+  CString cmd_line;
+  cmd_line.Format(_T("%s%s %s ")
+                  _T("%s%s %d ")
+                  _T("%s%s %u ")
+                  _T("%s%s %s "),
+                  kDash, kThreeOption, (data.three_value == TRUE_VALUE) ? _T("on") : _T("off"),   // NOLINT
+                  kDash, kIntOption, data.int_value,
+                  kDash, kUintOption, data.uint_value,
+                  kDash, kStrOption, data.str_value);
+  if (data.bool_value) {
+    cmd_line.AppendFormat(_T("%s%s"), kDash, kBoolOption);
+  }
+
+  TestData option_data;
+  CommandOption cmd_options[] = {
+    { kDash kThreeOption, COMMAND_OPTION_THREE,  &option_data.three_value, -1 },
+    { kDash kIntOption,   COMMAND_OPTION_INT,    &option_data.int_value,   -1 },
+    { kDash kUintOption,  COMMAND_OPTION_UINT,   &option_data.uint_value,  -1 },
+    { kDash kStrOption,   COMMAND_OPTION_STRING, &option_data.str_value,   -1 }
+  };
+
+  option_data.Clear();
+  CommandParsing cmd_parsing(cmd_options, arraysize(cmd_options));
+  ASSERT_FAILED(cmd_parsing.Parse(cmd_line, false));
+  ASSERT_SUCCEEDED(cmd_parsing.Parse(cmd_line, true));
+
+  option_data.bool_value = data.bool_value;
+  CheckTestData(option_data, data);
+}
+
+TEST(CommandsTest, NameValuePairCommandLineOptionsParsingTest) {
+  TestData data;
+  FillTestData(&data);
+  CString cmd_line;
+  cmd_line.Format(
+      _T("%s=%s&")
+      _T("%s=%d&")
+      _T("%s=%u&")
+      _T("%s=%s"),
+      kThreeOption, (data.three_value == TRUE_VALUE) ? _T("on") : _T("off"),
+      kIntOption, data.int_value,
+      kUintOption, data.uint_value,
+      kStrOption, data.str_value);
+  if (data.bool_value) {
+    cmd_line.AppendFormat(_T("&%s="), kBoolOption);
+  }
+
+  TestData option_data;
+  CommandOption cmd_options[] = {
+    { kBoolOption,  COMMAND_OPTION_BOOL,   &option_data.bool_value,    -1 },
+    { kThreeOption, COMMAND_OPTION_THREE,  &option_data.three_value,   -1 },
+    { kIntOption,   COMMAND_OPTION_INT,    &option_data.int_value,     -1 },
+    { kUintOption,  COMMAND_OPTION_UINT,   &option_data.uint_value,    -1 },
+    { kStrOption,   COMMAND_OPTION_STRING, &option_data.str_value,     -1 }
+  };
+
+  option_data.Clear();
+  CommandParsing cmd_parsing(cmd_options,
+                             arraysize(cmd_options),
+                             _T('&'),
+                             true);
+  ASSERT_SUCCEEDED(cmd_parsing.Parse(cmd_line, false));
+
+  CheckTestData(option_data, data);
+}
+
+TEST(CommandsTest, NameValuePairIgnoreUnknownArgsParsingTest) {
+  TestData data;
+  FillTestData(&data);
+  CString cmd_line;
+  cmd_line.Format(
+      _T("%s=%s&")
+      _T("%s=%d&")
+      _T("%s=%u&")
+      _T("%s=%s"),
+      kThreeOption, (data.three_value == TRUE_VALUE) ? _T("on") : _T("off"),
+      kIntOption, data.int_value,
+      kUintOption, data.uint_value,
+      kStrOption, data.str_value);
+  if (data.bool_value) {
+    cmd_line.AppendFormat(_T("&%s="), kBoolOption);
+  }
+
+  TestData option_data;
+  CommandOption cmd_options[] = {
+    { kBoolOption,  COMMAND_OPTION_BOOL,   &option_data.bool_value,    -1 },
+    { kThreeOption, COMMAND_OPTION_THREE,  &option_data.three_value,   -1 },
+    { kUintOption,  COMMAND_OPTION_UINT,   &option_data.uint_value,    -1 },
+    { kStrOption,   COMMAND_OPTION_STRING, &option_data.str_value,     -1 }
+  };
+
+  option_data.Clear();
+  CommandParsing cmd_parsing(cmd_options,
+                             arraysize(cmd_options),
+                             _T('&'),
+                             true);
+  ASSERT_FAILED(cmd_parsing.Parse(cmd_line, false));
+  ASSERT_SUCCEEDED(cmd_parsing.Parse(cmd_line, true));
+
+  option_data.int_value = data.int_value;
+  CheckTestData(option_data, data);
+}
+
+TEST(CommandsTest, CommandParsingSimpleSplitTest) {
+  CString exe;
+  CString args;
+
+  // Test to make sure SplitExeAndArgs correctly splits
+  // a properly constructed command line
+  ASSERT_SUCCEEDED(
+    CommandParsingSimple::SplitExeAndArgs(kIEBrowserQuotedCommandLine,
+                                          &exe,
+                                          &args));
+
+  EXPECT_STREQ(kIEBrowserQuotedExeResult, exe);
+  EXPECT_STREQ(kIEBrowserQuotedArgsResult, args);
+}
+
+TEST(CommandsTest, CommandParsingGuessSplitTest) {
+  CString exe;
+  CString args;
+
+  // Test to make sure SplitExeAndArgsGuess correctly splits
+  // a properly constructed command line
+  ASSERT_SUCCEEDED(
+    CommandParsingSimple::SplitExeAndArgsGuess(kIEBrowserQuotedCommandLine,
+                                               &exe,
+                                               &args));
+
+  EXPECT_STREQ(kIEBrowserQuotedExeResult, exe);
+  EXPECT_STREQ(kIEBrowserQuotedArgsResult, args);
+
+  // Test to make sure SplitExeAndArgsGuess correctly splits
+  // an improperly constructed "Uninstall" command line
+  ASSERT_SUCCEEDED(
+    CommandParsingSimple::SplitExeAndArgsGuess(kIEBrowserUnquotedCommandLine,
+                                               &exe,
+                                               &args));
+
+  EXPECT_STREQ(kIEBrowserUnquotedExe, exe);
+  EXPECT_STREQ(kIEBrowserUnquotedArgs, args);
+
+  // Test to make sure SplitExeAndArgsGuess correctly splits
+  // a properly constructed "Uninstall" command line, where
+  // the executable does not have a ".EXE" extension, and
+  // where there happens to be an argument which happens to
+  // be an executable
+  ASSERT_SUCCEEDED(
+    CommandParsingSimple::SplitExeAndArgsGuess(kGEUninstallCommandLine,
+                                               &exe,
+                                               &args));
+
+  EXPECT_STREQ(kGEUninstallExe, exe);
+  EXPECT_STREQ(kGEUninstallArgs, args);
+}
+
+TEST(CommandsTest, CommandParsingGuessSplit_ExtraWhiteSpace) {
+  CString exe;
+  CString args;
+
+  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
+      kIEBrowserUnquotedCommandLine _T(" "),
+      &exe,
+      &args));
+  EXPECT_STREQ(kIEBrowserUnquotedExe, exe);
+  EXPECT_STREQ(kIEBrowserUnquotedArgs, args);
+
+  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
+      kIEBrowserUnquotedCommandLine _T("\t"),
+      &exe,
+      &args));
+  EXPECT_STREQ(kIEBrowserUnquotedExe, exe);
+  EXPECT_STREQ(kIEBrowserUnquotedArgs, args);
+
+  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
+      _T(" ") kIEBrowserUnquotedCommandLine,
+      &exe,
+      &args));
+  EXPECT_STREQ(kIEBrowserUnquotedExe, exe);
+  EXPECT_STREQ(kIEBrowserUnquotedArgs, args);
+
+  // The following cases have unexpected results.
+  // Quoting a command line with args is not handled correctly.
+  // The entire thing is interpreted as an EXE.
+
+  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
+      _T("\" ") kIEBrowserUnquotedCommandLine _T("\""),
+      &exe,
+      &args));
+  EXPECT_STREQ(kIEBrowserUnquotedCommandLine, exe);
+  EXPECT_TRUE(args.IsEmpty());
+
+  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
+      _T("\"") kIEBrowserUnquotedCommandLine _T(" \""),
+      &exe,
+      &args));
+  EXPECT_STREQ(kIEBrowserUnquotedCommandLine, exe);
+  EXPECT_TRUE(args.IsEmpty());
+
+  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
+      _T("\"") kIEBrowserUnquotedCommandLine _T("\t\""),
+      &exe,
+      &args));
+  EXPECT_STREQ(kIEBrowserUnquotedCommandLine, exe);
+  EXPECT_TRUE(args.IsEmpty());
+}
+
+TEST(CommandsTest, CommandParsingGuessSplit_CommandLineIsExistingFile) {
+  CString exe;
+  CString args;
+
+  EXPECT_TRUE(File::Exists(kIEBrowserExe));
+  EXPECT_SUCCEEDED(
+    CommandParsingSimple::SplitExeAndArgsGuess(kIEBrowserExe, &exe, &args));
+  EXPECT_STREQ(kIEBrowserExe, exe);
+  EXPECT_TRUE(args.IsEmpty());
+
+  // File::Exists does not handle enclosed paths.
+  EXPECT_FALSE(File::Exists(kIEBrowserQuotedExe));
+  EXPECT_SUCCEEDED(
+    CommandParsingSimple::SplitExeAndArgsGuess(kIEBrowserQuotedExe,
+                                               &exe,
+                                               &args));
+  EXPECT_STREQ(kIEBrowserExe, exe);
+  EXPECT_TRUE(args.IsEmpty());
+}
+
+TEST(CommandsTest,
+     CommandParsingGuessSplit_CommandLineIsExistingFileWithExtraWhiteSpace) {
+  CString exe;
+  CString args;
+
+  EXPECT_TRUE(File::Exists(kIEBrowserExe));
+  // File::Exists does not handle enclosed paths.
+  EXPECT_FALSE(File::Exists(kIEBrowserQuotedExe));
+
+  EXPECT_SUCCEEDED(
+    CommandParsingSimple::SplitExeAndArgsGuess(kIEBrowserExe _T(" "),
+                                               &exe,
+                                               &args));
+  EXPECT_STREQ(kIEBrowserExe, exe);
+  EXPECT_TRUE(args.IsEmpty());
+
+  EXPECT_SUCCEEDED(
+    CommandParsingSimple::SplitExeAndArgsGuess(kIEBrowserQuotedExe _T(" "),
+                                               &exe,
+                                               &args));
+  EXPECT_STREQ(kIEBrowserExe, exe);
+  EXPECT_TRUE(args.IsEmpty());
+
+  EXPECT_SUCCEEDED(
+    CommandParsingSimple::SplitExeAndArgsGuess(kIEBrowserExe _T("\t"),
+                                               &exe,
+                                               &args));
+  EXPECT_STREQ(kIEBrowserExe, exe);
+  EXPECT_TRUE(args.IsEmpty());
+
+  EXPECT_SUCCEEDED(
+    CommandParsingSimple::SplitExeAndArgsGuess(kIEBrowserQuotedExe _T("\t"),
+                                               &exe,
+                                               &args));
+  EXPECT_STREQ(kIEBrowserExe, exe);
+  // SplitExeAndArgs does not treat tab like whitespace and args aren't trimmed.
+  EXPECT_STREQ(_T("\t"), args);
+
+  EXPECT_SUCCEEDED(
+    CommandParsingSimple::SplitExeAndArgsGuess(_T(" ") kIEBrowserExe,
+                                               &exe,
+                                               &args));
+  EXPECT_STREQ(kIEBrowserExe, exe);
+  EXPECT_TRUE(args.IsEmpty());
+
+  EXPECT_SUCCEEDED(
+    CommandParsingSimple::SplitExeAndArgsGuess(_T(" ") kIEBrowserQuotedExe,
+                                               &exe,
+                                               &args));
+  EXPECT_STREQ(kIEBrowserExe, exe);
+  EXPECT_TRUE(args.IsEmpty());
+
+  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
+                       _T("\" ") kIEBrowserExe _T("\""),
+                       &exe,
+                       &args));
+  EXPECT_STREQ(kIEBrowserExe, exe);
+  EXPECT_TRUE(args.IsEmpty());
+
+  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
+                       _T("\"") kIEBrowserExe _T(" \""),
+                       &exe,
+                       &args));
+  EXPECT_STREQ(kIEBrowserExe, exe);
+  EXPECT_TRUE(args.IsEmpty());
+
+  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
+                       _T("\"") kIEBrowserExe _T("\t\""),
+                       &exe,
+                       &args));
+  EXPECT_STREQ(kIEBrowserExe, exe);
+  EXPECT_TRUE(args.IsEmpty());
+}
+
+}  // namespace omaha
+
diff --git a/base/commontypes.h b/base/commontypes.h
new file mode 100644
index 0000000..5c2de0b
--- /dev/null
+++ b/base/commontypes.h
@@ -0,0 +1,44 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// TODO(omaha): Maybe eliminate this file or at least rename it.
+
+#ifndef OMAHA_BASE_COMMONTYPES_H_
+#define OMAHA_BASE_COMMONTYPES_H_
+
+namespace omaha {
+
+// Isolate some VisualC++-isms to macros for easy redefinition
+#define SELECTANY __declspec(selectany)
+#define DLLIMPORT __declspec(dllimport)
+#define DLLEXPORT __declspec(dllexport)
+
+// Put this around string literals that don't need to be localized
+// to indicate this fact.  Note that you don't need to do this for string
+// literals used in functions where they obviously don't need to be localized,
+// such as REPORT(), XXX_LOG(), CHK(), ASSERT(), VERIFY(), TRACE(), dbgprint(),
+// OutputDebugString(), GetProcAddress(), GetModuleHandle(), etc.
+//
+// For large blocks of non-localizable string literals, you can use a comment
+// line including "SKIP_LOC_BEGIN" to start a non-localizable section of
+// your file, and "SKIP_LOC_END" to end the section.
+//
+// Don't worry about NOTRANSL or the SKIP_LOC blocks in unit tests, experimental
+// code, etc. as they are ignored when checking for localizable string literals.
+#define NOTRANSL(x) x
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_COMMONTYPES_H_
diff --git a/base/const_addresses.h b/base/const_addresses.h
new file mode 100644
index 0000000..f00219a
--- /dev/null
+++ b/base/const_addresses.h
@@ -0,0 +1,78 @@
+// Copyright 2004-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Constants for dealing with machines.
+//
+// Manifests are requested over HTTPS. All other network communication goes
+// over HTTP.
+
+#ifndef OMAHA_BASE_CONST_ADDRESSES_H_
+#define OMAHA_BASE_CONST_ADDRESSES_H_
+
+#include <tchar.h>
+#include "omaha/base/constants.h"
+
+namespace omaha {
+
+// Static string that gives the main Google website address
+// TODO(omaha): Rename this as a connection-check URL. Name should be in caps
+// and not include "Google".
+#define kGoogleHttpServer _T("www.") COMPANY_DOMAIN
+
+// Static string used as an identity for the "Omaha" Google domain.
+// TODO(omaha): Rename this as a plug-in domain.
+const TCHAR* const kGoopdateServer = _T("tools.") COMPANY_DOMAIN;
+
+// HTTP protocol prefix
+#define kProtoSuffix              _T("://")
+#define kFileProtoScheme          _T("file")
+#define kHttpProtoScheme          _T("http")
+#define kHttpsProtoScheme         _T("https")
+#define kHttpProto                kHttpProtoScheme kProtoSuffix
+#define kHttpsProto               kHttpsProtoScheme kProtoSuffix
+#define kFileProto                kFileProtoScheme kProtoSuffix
+
+// Default ports for proxies
+#define kDefaultHttpProxyPort     80
+#define kDefaultSslProxyPort      443
+
+// Update checks and manifest requests.
+const TCHAR* const kUrlUpdateCheck =
+    kHttpsProto _T("tools.") COMPANY_DOMAIN _T("/service/update2");
+
+// Pings.
+const TCHAR* const kUrlPing =
+    _T("http://tools.") COMPANY_DOMAIN _T("/service/update2");
+
+// Crash reports.
+const TCHAR* const kUrlCrashReport =
+    _T("http://clients2.") COMPANY_DOMAIN _T("/cr/report");
+
+// More information url.
+// Must allow query parameters to be appended to it.
+const TCHAR* const kUrlMoreInfo =
+    _T("http://www.") COMPANY_DOMAIN _T("/support/installer/?");
+
+// Code Red check url.
+const TCHAR* const kUrlCodeRedCheck =
+    _T("http://cr-tools.clients.") COMPANY_DOMAIN _T("/service/check2");
+
+// Usage stats url.
+const TCHAR* const kUrlUsageStatsReport =
+    _T("http://clients5.") COMPANY_DOMAIN _T("/tbproxy/usagestats");
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_CONST_ADDRESSES_H_
diff --git a/base/const_config.h b/base/const_config.h
new file mode 100644
index 0000000..13bb02f
--- /dev/null
+++ b/base/const_config.h
@@ -0,0 +1,48 @@
+// Copyright 2003-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_BASE_CONST_CONFIG_H_
+#define OMAHA_BASE_CONST_CONFIG_H_
+
+#include "omaha/base/constants.h"
+
+namespace omaha {
+
+// TODO(omaha): Move these values used by debug.cc someplace else.
+#define kRegKeyShared        _T("Shared")
+#define kCiRegKeyShared      GOOPDATE_MAIN_KEY kRegKeyShared
+#define kRegValueReportIds   _T("report_ids")
+
+// TODO(omaha): Move these plugin values someplace else. Since we're building
+// constants, that should probably be the customization header. Move the Omaha 3
+// plugin equivalents from config.cc there as well.
+
+// NOTE: ONECLICK_PLUGIN_VERSION_ANSI is defined in main.scons
+// For example: kOneClickProgId == "Google.OneClickCtrl.1"
+const TCHAR* const kOneClickProgId = COMPANY_NAME_IDENTIFIER
+                                     _T(".OneClickCtrl.")
+                                     _T(ONECLICK_PLUGIN_VERSION_ANSI);
+// The plug-in MIME type.
+// For example:
+//     kOneClickPluginMimeTypeAnsi == "application/x-vnd.google.oneclickctrl.1"
+// TODO(omaha): Deal with the "Google.OneClickCtrl.%d") in
+// tools\goopdump\data_dumper_oneclick.cc after integrating goopdump.
+#define kOneClickPluginMimeTypeAnsi \
+    "application/x-vnd." COMPANY_DOMAIN_BASE_ANSI ".oneclickctrl." \
+     ONECLICK_PLUGIN_VERSION_ANSI
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_CONST_CONFIG_H_
diff --git a/base/const_debug.h b/base/const_debug.h
new file mode 100644
index 0000000..8129de4
--- /dev/null
+++ b/base/const_debug.h
@@ -0,0 +1,37 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_BASE_CONST_DEBUG_H_
+#define OMAHA_BASE_CONST_DEBUG_H_
+
+#include <tchar.h>
+#include "omaha/base/constants.h"
+
+namespace omaha {
+
+// kCiDebugDirectory is relative to the system drive if available or the
+// current directory otherwise. The expectation is that %SystemDrive% is
+// always avaialable, including for the code running as system.
+#define kCiDebugDirectory     APP_NAME_IDENTIFIER _T("-debug")
+
+// TODO(omaha): unify the debugging and logging support so that these files
+// are created under the same directory as the log file.
+#define kCiDebugLogFile                   _T("debug.log")
+#define kCiAssertOccurredFile             _T("assert.log")
+#define kCiAbortOccurredFile              _T("abort.log")
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_CONST_DEBUG_H_
diff --git a/base/const_object_names.h b/base/const_object_names.h
new file mode 100644
index 0000000..c133528
--- /dev/null
+++ b/base/const_object_names.h
@@ -0,0 +1,131 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Kernel object names.
+
+#ifndef OMAHA_BASE_CONST_OBJECT_NAMES_H_
+#define OMAHA_BASE_CONST_OBJECT_NAMES_H_
+
+#include <tchar.h>
+#include "omaha/base/constants.h"
+
+namespace omaha {
+
+// The prefix to use for global names in the win32 API's.
+const TCHAR* const kGlobalPrefix = _T("Global\\Omaha");
+
+const TCHAR kCrashPipeNamePrefix[] =
+    _T("\\\\.\\pipe\\") SHORT_COMPANY_NAME _T("CrashServices");
+
+// Ensures that only one instance of machine or user Omaha is trying to setup at
+// a time.
+const TCHAR* const kSetupMutex = _T("{A9A86B93-B54E-4570-BE89-42418507707B}");
+
+// TODO(omaha3): Update this comment.
+// Signals the process to exit. Currently the core and the worker listen to
+// this event.
+// TODO(omaha): Consider making all our processes listen to it. Maybe not the
+// service, since the SCM controls the life time of the service.
+const TCHAR* const kShutdownEvent =
+    _T("{A0C1F415-D2CE-4ddc-9B48-14E56FD55162}");
+
+// This is for Omaha2 backwards compatibility.
+// The installed Omaha3 handoff process sets an event to tell an Omaha2 setup
+// worker running from the temp directory that a UI has been displayed so that
+// the Omaha2 worker will not display a second UI on error. The event's name is
+// passed in this environment variable name by the Omaha2 worker.
+const TCHAR* const kLegacyUiDisplayedEventEnvironmentVariableName =
+    _T("GOOGLE_UPDATE_UI_DISPLAYED_EVENT_NAME");
+
+// Ensures the Core only runs one instance per machine and one instance per
+// each user session.
+const TCHAR* const kCoreSingleInstance =
+    _T("{B5665124-2B19-40e2-A7BC-B44321E72C4B}");
+
+// Ensures the Crash Handler only runs one instance per machine and one
+// instance per each user session.
+const TCHAR* const kCrashHandlerSingleInstance =
+    _T("{C4F406E5-F024-4e3f-89A7-D5AB7663C3CD}");
+
+// Ensures the /ua process only runs one instance per machine and one
+// instance per each user session.
+const TCHAR* const kUpdateAppsSingleInstance =
+    _T("{D0BB2EF1-C183-4cdb-B218-040922092869}");
+
+// Ensures only one installer for an app is running in a session.
+// The %s is replaced with the application ID.
+const TCHAR* const kInstallAppSingleInstance =
+    _T("%s-{F707E94F-D66B-4525-AD84-B1DA87D6A971}");
+
+// Ensures the GoogleUpdate3 server only runs one instance per machine and one
+// instance per each user session.
+const TCHAR* const kGoogleUpdate3SingleInstance =
+    _T("{6885AE8E-C070-458d-9711-37B9BEAB65F6}");
+
+// TODO(omaha): Delete Job Object code.
+
+// Base name of job object for Setup phase 1 processes except self updates.
+// These may not be running as Local System for machine installs like
+// self-updates do.
+const TCHAR* const kSetupPhase1NonSelfUpdateJobObject =
+    _T("{5A913EF1-4160-48bc-B688-4D67EAEB698A}");
+
+// Base name of job object for interactive install processes except /install.
+const TCHAR* const kAppInstallJobObject =
+    _T("{8AD051DB-4FE6-458b-B103-7DCC78D56013}");
+
+// Base name of job object for silent processes that are okay to kill.
+const TCHAR* const kSilentJobObject =
+    _T("{A2300FD6-CBED-48a6-A3CB-B35C38A42F8E}");
+
+// Base name of job object for silent processes that should not be killed.
+const TCHAR* const kSilentDoNotKillJobObject =
+    _T("{D33A8A53-F57D-4fd9-A32D-238FD69B4BC4}");
+
+// The global lock to ensure that a single app is being installed for this
+// user/machine at a given time.
+const TCHAR* const kInstallManagerSerializer =
+    _T("{0A175FBE-AEEC-4fea-855A-2AA549A88846}");
+
+// Serializes access to metrics stores, machine and user, respectively.
+const TCHAR* const kMetricsSerializer =
+    _T("{C68009EA-1163-4498-8E93-D5C4E317D8CE}");
+
+// Serializes access to the global network configuration, such as the CUP keys.
+const TCHAR* const kNetworkConfigLock =
+    _T("{0E900C7B-04B0-47f9-81B0-F8D94F2DF01B}");
+
+// Serializes access to the registry for application state.
+const TCHAR* const kRegistryAccessMutex =
+    _T("{66CC0160-ABB3-4066-AE47-1CA6AD5065C8}");
+
+// Serializes opt user id generation.
+const TCHAR* const kOptUserIdLock =
+    _T("{D19BAF17-7C87-467E-8D63-6C4B1C836373}");
+
+// The name of the shared memory objects containing the serialized COM
+// interface pointers exposed by the machine core.
+// TODO(omaha): Rename these constants to remove "GoogleUpdate".
+// TODO(omaha): Consider following the kGlobalPrefix convention with the 'G'
+// for the new shared Omaha 3 name  and building this from the same #define as
+// kGlobalPrefix.
+const TCHAR* const kGoogleUpdate3SharedMemoryName =
+    _T("Global\\") APP_NAME_IDENTIFIER _T("3");
+const TCHAR* const kGoogleUpdateCoreSharedMemoryName =
+    _T("Global\\") APP_NAME_IDENTIFIER _T("Core");
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_CONST_OBJECT_NAMES_H_
diff --git a/base/const_timeouts.h b/base/const_timeouts.h
new file mode 100644
index 0000000..08bd319
--- /dev/null
+++ b/base/const_timeouts.h
@@ -0,0 +1,31 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Constants used for different timeouts in Common Installer
+
+#ifndef OMAHA_COMMON_CONST_TIMEOUTS_H__
+#define OMAHA_COMMON_CONST_TIMEOUTS_H__
+
+namespace omaha {
+
+// Timeout for tearing down thread
+const int kMaxThreadDestructionTimeMs = 2000;
+
+const int kRegisterExeTimeoutMs =                  120000;      // 2 minutes.
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_CONST_TIMEOUTS_H__
+
diff --git a/base/const_utils.h b/base/const_utils.h
new file mode 100644
index 0000000..f301a3d
--- /dev/null
+++ b/base/const_utils.h
@@ -0,0 +1,92 @@
+// Copyright 2004-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Constants used in util functions
+
+#ifndef OMAHA_BASE_CONST_UTILS_H_
+#define OMAHA_BASE_CONST_UTILS_H_
+
+namespace omaha {
+
+// The registry key for the registered application path. Take a look at
+// http://msdn2.microsoft.com/en-us/library/ms997545.aspx for more information.
+#define kRegKeyApplicationPath \
+    _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths")
+#define kRegKeyPathValue _T("Path")
+
+#define USER_REG_VISTA_LOW_INTEGRITY_HKCU \
+    _T("Software\\Microsoft\\Internet Explorer\\") \
+    _T("InternetRegistry\\REGISTRY\\USER")
+
+// Including this property specification in the msiexec command line will
+// prevent MSI from rebooting in all cases.
+const TCHAR* const kMsiSuppressAllRebootsCmdLine = _T("REBOOT=ReallySuppress");
+
+// TODO(omaha): We could probably move most of these to browser_utils.
+// TODO(omaha): The rest are Windows constants. Maybe name this file as such.
+
+// The regkey for the default browser the Windows Shell opens.
+#define kRegKeyDefaultBrowser _T("SOFTWARE\\Clients\\StartMenuInternet")
+#define kRegKeyUserDefaultBrowser _T("HKCU\\") kRegKeyDefaultBrowser
+#define kRegKeyMachineDefaultBrowser _T("HKLM\\") kRegKeyDefaultBrowser
+#define kRegKeyShellOpenCommand _T("\\shell\\open\\command")
+#define kRegKeyLegacyDefaultBrowser _T("HKCR\\http")
+#define kRegKeyLegacyDefaultBrowserCommand \
+            kRegKeyLegacyDefaultBrowser kRegKeyShellOpenCommand
+
+#define kIeExeName _T("IEXPLORE.EXE")
+#define kFirefoxExeName _T("FIREFOX.EXE")
+#define kChromeExeName _T("CHROME.EXE")
+#define kChromeBrowserName _T("Google Chrome")
+
+// The regkey for proxy settings for IE.
+const TCHAR* const kRegKeyIESettings =
+    _T("HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
+const TCHAR* const kRegKeyIESettingsConnections = _T("Connections");
+const TCHAR* const kRegValueIEDefaultConnectionSettings =
+    _T("DefaultConnectionSettings");
+const TCHAR* const kRegValueIEProxyEnable = _T("ProxyEnable");
+const TCHAR* const kRegValueIEProxyServer = _T("ProxyServer");
+const TCHAR* const kRegValueIEAutoConfigURL = _T("AutoConfigURL");
+
+// Internet Explorer.
+#define kRegKeyIeClass \
+    _T("HKCR\\CLSID\\{0002DF01-0000-0000-C000-000000000046}\\LocalServer32")
+#define kRegValueIeClass _T("")
+
+// Firefox.
+#define kRegKeyFirefox \
+    _T("HKCR\\Applications\\FIREFOX.EXE\\shell\\open\\command")
+#define kRegValueFirefox        _T("")
+#define kFullRegKeyFirefox      _T("HKLM\\SOFTWARE\\Mozilla\\Mozilla Firefox")
+#define kRegKeyFirefoxPlugins   _T("plugins")
+#define kFirefoxCurrentVersion  _T("CurrentVersion")
+#define kFirefoxInstallDir      _T("Install Directory")
+
+// Chrome.
+#define kRegKeyChrome _T("HKCR\\Applications\\chrome.exe\\shell\\open\\command")
+#define kRegValueChrome _T("")
+
+// SEHOP setting under IFEO.
+#define kRegKeyWindowsNTCurrentVersion \
+    _T("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion")
+#define kRegKeyImageFileExecutionOptions \
+      kRegKeyWindowsNTCurrentVersion _T("\\") _T("Image File Execution Options")
+#define kRegKeyDisableSEHOPValue _T("DisableExceptionChainValidation")
+
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_CONST_UTILS_H_
diff --git a/base/constants.h b/base/constants.h
new file mode 100644
index 0000000..7d660f1
--- /dev/null
+++ b/base/constants.h
@@ -0,0 +1,477 @@
+// Copyright 2003-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+//
+// Constants used by Omaha project
+
+#ifndef OMAHA_BASE_CONSTANTS_H_
+#define OMAHA_BASE_CONSTANTS_H_
+
+#include <windows.h>
+#include <tchar.h>
+
+namespace omaha {
+
+//
+// Begin vendor-specific constants.
+//
+// When adding values here or using these values for a constant, add a test for
+// the value to common\omaha_customization_unittest.cc.
+// NOTE: The following are defined in main.scons:
+//  + FULL_COMPANY_NAME_ANSI
+//  + SHORT_COMPANY_NAME_ANSI
+//  + PRODUCT_NAME_ANSI
+//  + COMPANY_DOMAIN_ANSI
+//  + MAIN_DLL_BASE_NAME_ANSI
+//
+// *_IDENTIFIER assume that SHORT_COMPANY_NAME_ANSI and PRODUCT_NAME_ANSI are
+// legal identifiers (i.e. one word).
+
+// TODO(omaha3): Move vendor-specific constants to a single file to make use
+// of open source code by other vendors simpler.
+// TODO(omaha3): Only use very specific const variables outside that file. Do
+// not build values in other source files by concatenating preprocessor defines.
+
+// Full company name.
+// FULL_COMPANY_NAME == "Google Inc."
+const TCHAR* const kFullCompanyName = _T(FULL_COMPANY_NAME_ANSI);
+
+// Short company name (for use in paths and messages and to combine with product
+// name). Does not include "Inc." and similar formal parts of the company name.
+// SHORT_COMPANY_NAME == "Google"
+#define SHORT_COMPANY_NAME _T(SHORT_COMPANY_NAME_ANSI)
+const TCHAR* const kShortCompanyName = SHORT_COMPANY_NAME;
+
+// Product name.
+// PRODUCT_NAME == "Update"
+#define PRODUCT_NAME _T(PRODUCT_NAME_ANSI)
+
+// Company domain name base. Used for MIME type names.
+// COMPANY_DOMAIN_BASE_ANSI = "google"
+#define COMPANY_DOMAIN_BASE _T(COMPANY_DOMAIN_BASE_ANSI)
+
+// Company domain name base. Used for addresses.
+// COMPANY_DOMAIN_ANSI = "google.com"
+#define COMPANY_DOMAIN _T(COMPANY_DOMAIN_ANSI)
+
+// Company's internal network DNS domain. Used for detecting internal users.
+// If the internal network uses a different domain than the public-facing
+// COMPANY_DOMAIN, this will need to be changed.
+// kCompanyInternalDnsName = ".google.com"
+const TCHAR* const kCompanyInternalDnsName = _T(".") COMPANY_DOMAIN;
+
+// Company's internal network NetBIOS name. Used for detecting internal users.
+// If the internal network uses a different domain than the public-facing
+// COMPANY_DOMAIN_BASE, this will need to be changed.
+// kCompanyInternalLanGroupName = "google"
+const TCHAR* const kCompanyInternalLanGroupName = COMPANY_DOMAIN_BASE;
+
+// The base name of the main executable. Everything except the ".exe".
+// Most files start with the main .exe's base name.
+// MAIN_EXE_BASE_NAME = "GoogleUpdate"
+#define MAIN_EXE_BASE_NAME  _T(MAIN_EXE_BASE_NAME_ANSI)
+
+// Base name of the main DLL.
+// MAIN_DLL_BASE_NAME = "goopdate"
+#define MAIN_DLL_BASE_NAME _T(MAIN_DLL_BASE_NAME_ANSI)
+
+// Application name.
+// Use the localized IDS_PRODUCT_DISPLAY_NAME or the formatted
+// IDS_INSTALLER_DISPLAY_NAME instead when string is displayed to user.
+// kAppName == "Google Update"
+// TODO(omaha): Maybe rename all of these kOmahaAppName.
+const TCHAR* const kAppName = _T(OMAHA_APP_NAME_ANSI);
+
+#define COMPANY_NAME_IDENTIFIER SHORT_COMPANY_NAME
+#define PRODUCT_NAME_IDENTIFIER PRODUCT_NAME
+#define APP_NAME_IDENTIFIER SHORT_COMPANY_NAME PRODUCT_NAME
+
+// Prefix for any Win32 objects (mutexes, events, etc.).
+// TODO(omaha): This is used by several files in base/. Maybe we can use a
+// similar prefix to avoid conflicts with some values in const_object_names.h.
+// Consider moving this constant to const_object_names.h.
+#define kLockPrefix \
+    _T("_") COMPANY_NAME_IDENTIFIER _T("_") PRODUCT_NAME_IDENTIFIER _T("_")
+
+const TCHAR* const kOmahaShellFileName         = MAIN_EXE_BASE_NAME _T(".exe");
+const TCHAR* const kCrashHandlerFileName       = CRASH_HANDLER_NAME _T(".exe");
+const TCHAR* const kOmahaDllName               = MAIN_DLL_BASE_NAME _T(".dll");
+const TCHAR* const kOmahaResourceDllNameFormat =
+    MAIN_DLL_BASE_NAME _T("res_%s.dll");
+const TCHAR* const kOmahaBrokerFileName        =
+    MAIN_EXE_BASE_NAME _T("Broker.exe");
+const TCHAR* const kOmahaOnDemandFileName      =
+    MAIN_EXE_BASE_NAME _T("OnDemand.exe");
+const TCHAR* const kPSFileNameMachine  = _T("psmachine.dll");
+const TCHAR* const kPSFileNameUser     = _T("psuser.dll");
+
+// TODO(omaha): Replace the following literal in clickonce\build.scons.
+// '%s/GoogleUpdateSetup.exe'
+
+// These must be in sync with the WiX files.
+// TODO(omaha): Make these constants in main.scons and use them in the .wxs
+// files, kMsiUninstallKey, and elsewhere this GUID appears.
+const TCHAR* const kHelperInstallerName = MAIN_EXE_BASE_NAME _T("Helper.msi");
+const TCHAR* const kHelperInstallerProductGuid =
+    _T("{A92DAB39-4E2C-4304-9AB6-BC44E68B55E2}");
+const TCHAR* const kHelperPatchName = MAIN_EXE_BASE_NAME _T("HelperPatch.msp");
+const TCHAR* const kHelperPatchGuid =
+    _T("{E0D0D2C9-5836-4023-AB1D-54EC3B90AD03}");
+
+// The value that is used in the run key.
+const TCHAR* const kRunValueName = kAppName;
+
+// The company and organization names as expected in Authenticode certificates.
+const TCHAR* const kCertificateSubjectName = _T("Google Inc");
+
+// TODO(omaha): Try to use the above constants in the IDL file help strings.
+// TODO(omaha): Consider moving uuid's from the IDL files to here too.
+// TODO(omaha): Use these values for the registry maps definitions, and progid
+// uses, such as ondemand.h.
+
+//
+// Omaha's app ID
+//
+// TODO(omaha): Rename all of these "Omaha".
+#define GOOPDATE_APP_ID _T("{430FD4D0-B729-4F61-AA34-91526481799D}")
+const TCHAR* const kGoogleUpdateAppId = GOOPDATE_APP_ID;
+const GUID kGoopdateGuid = {0x430FD4D0, 0xB729, 0x4F61,
+                            {0xAA, 0x34, 0x91, 0x52, 0x64, 0x81, 0x79, 0x9D}};
+
+//
+// Directory names
+//
+#define OFFLINE_DIR_NAME _T("Offline")
+
+#define OMAHA_REL_COMPANY_DIR SHORT_COMPANY_NAME
+#define OMAHA_REL_CRASH_DIR OMAHA_REL_COMPANY_DIR _T("\\CrashReports")
+#define OMAHA_REL_GOOPDATE_INSTALL_DIR \
+    OMAHA_REL_COMPANY_DIR _T("\\") PRODUCT_NAME
+#define OMAHA_REL_LOG_DIR OMAHA_REL_GOOPDATE_INSTALL_DIR _T("\\Log")
+#define OMAHA_REL_OFFLINE_STORAGE_DIR \
+    OMAHA_REL_GOOPDATE_INSTALL_DIR _T("\\") OFFLINE_DIR_NAME
+#define OMAHA_REL_DOWNLOAD_STORAGE_DIR \
+    OMAHA_REL_GOOPDATE_INSTALL_DIR _T("\\Download")
+#define OMAHA_REL_INSTALL_WORKING_DIR \
+      OMAHA_REL_GOOPDATE_INSTALL_DIR _T("\\Install")
+
+// This directory is relative to the user profile app data local.
+#define LOCAL_APPDATA_REL_TEMP_DIR _T("\\Temp")
+
+//
+// Registry keys and values
+//
+#define MACHINE_KEY_NAME _T("HKLM")
+#define MACHINE_KEY MACHINE_KEY_NAME _T("\\")
+#define USER_KEY_NAME _T("HKCU")
+#define USER_KEY USER_KEY_NAME _T("\\")
+#define USERS_KEY _T("HKU\\")
+#define COMPANY_MAIN_KEY _T("Software\\") SHORT_COMPANY_NAME _T("\\")
+#define GOOPDATE_MAIN_KEY COMPANY_MAIN_KEY PRODUCT_NAME _T("\\")
+#define GOOPDATE_REG_RELATIVE_CLIENTS GOOPDATE_MAIN_KEY _T("Clients\\")
+#define GOOPDATE_REG_RELATIVE_CLIENT_STATE GOOPDATE_MAIN_KEY _T("ClientState\\")
+#define GOOPDATE_REG_RELATIVE_CLIENT_STATE_MEDIUM \
+    GOOPDATE_MAIN_KEY _T("ClientStateMedium\\")
+#define COMPANY_POLICIES_MAIN_KEY \
+    _T("Software\\Policies\\") SHORT_COMPANY_NAME _T("\\")
+#define GOOPDATE_POLICIES_RELATIVE COMPANY_POLICIES_MAIN_KEY \
+    PRODUCT_NAME _T("\\")
+
+#define USER_REG_GOOGLE USER_KEY COMPANY_MAIN_KEY
+#define USER_REG_UPDATE USER_KEY GOOPDATE_MAIN_KEY
+#define USER_REG_CLIENTS USER_KEY GOOPDATE_REG_RELATIVE_CLIENTS
+#define USER_REG_CLIENTS_GOOPDATE  USER_REG_CLIENTS GOOPDATE_APP_ID
+#define USER_REG_CLIENT_STATE USER_KEY GOOPDATE_REG_RELATIVE_CLIENT_STATE
+#define USER_REG_CLIENT_STATE_GOOPDATE USER_REG_CLIENT_STATE GOOPDATE_APP_ID
+
+#define MACHINE_REG_GOOGLE MACHINE_KEY COMPANY_MAIN_KEY
+#define MACHINE_REG_UPDATE MACHINE_KEY GOOPDATE_MAIN_KEY
+#define MACHINE_REG_CLIENTS MACHINE_KEY GOOPDATE_REG_RELATIVE_CLIENTS
+#define MACHINE_REG_CLIENTS_GOOPDATE  MACHINE_REG_CLIENTS GOOPDATE_APP_ID
+#define MACHINE_REG_CLIENT_STATE MACHINE_KEY GOOPDATE_REG_RELATIVE_CLIENT_STATE
+#define MACHINE_REG_CLIENT_STATE_GOOPDATE \
+    MACHINE_REG_CLIENT_STATE GOOPDATE_APP_ID
+#define MACHINE_REG_CLIENT_STATE_MEDIUM \
+    MACHINE_KEY GOOPDATE_REG_RELATIVE_CLIENT_STATE_MEDIUM
+
+// Expands to HKEY_LOCAL_MACHINE\SOFTWARE\Google\UpdateDev
+#define MACHINE_REG_UPDATE_DEV \
+    MACHINE_KEY COMPANY_MAIN_KEY PRODUCT_NAME _T("Dev\\")
+
+// Regular expressions for the servers allowed to use the Omaha plugins.
+const TCHAR* const kSiteLockPatternStrings[] = {
+  _T("^(gears)|(mail)|(tools)|(www)|(desktop)|(pack)\\.google\\.com$"),
+  _T("^www\\.google\\.(ad)|(bg)|(ca)|(cn)|(cz)|(de)|(es)|(fi)|(fr)|(gr)|(hr)|(hu)|(it)|(ki)|(kr)|(lt)|(lv)|(nl)|(no)|(pl)|(pt)|(ro)|(ru)|(sk)|(sg)|(sl)|(sr)|(vn)$"),  // NOLINT
+  _T("^www\\.google\\.co\\.(hu)|(id)|(il)|(it)|(jp)|(kr)|(th)|(uk)$"),
+  _T("^www\\.google\\.com\\.(ar)|(au)|(br)|(cn)|(et)|(gr)|(hr)|(ki)|(lv)|(om)|(pl)|(pt)|(ru)|(sg)|(sv)|(tr)|(vn)$"),  // NOLINT
+};
+
+//
+// Compatible shell versions
+// The following shell versions are compatible with the current version of
+// goopdate.dll and do not need be replaced: 1.2.131.7 and 1.2.183.9.
+//
+const ULONGLONG kCompatibleOlderShellVersions[] = { 0x0001000200830007,
+                                                    0x0001000200B70009,
+                                                  };
+
+//
+// End vendor-specific constants.
+//
+
+//
+// Registry values under MACHINE_REG_UPDATE_DEV allow customization of the
+// default behavior. The overrides apply for both user and machine
+// instances of omaha.
+//
+// The values below can only be overriden in debug builds.
+const TCHAR* const kRegValueNameOverInstall    = _T("OverInstall");
+const TCHAR* const kRegValueNameCrashIfSpecificError
+    = _T("CrashIfSpecificError");
+
+// The values below can be overriden in both debug and opt builds. Code Red url
+// can be overriden but its value is defined in the Code Red module. The value
+// name is "CodeRedUrl".
+const TCHAR* const kRegValueNameUrl                 = _T("url");
+const TCHAR* const kRegValueNamePingUrl             = _T("PingUrl");
+const TCHAR* const kRegValueNameCrashReportUrl      = _T("CrashReportUrl");
+const TCHAR* const kRegValueNameGetMoreInfoUrl      = _T("MoreInfoUrl");
+const TCHAR* const kRegValueNameUsageStatsReportUrl = _T("UsageStatsReportUrl");
+const TCHAR* const kRegValueTestSource              = _T("TestSource");
+const TCHAR* const kRegValueAuCheckPeriodMs         = _T("AuCheckPeriodMs");
+const TCHAR* const kRegValueCrCheckPeriodMs         = _T("CrCheckPeriodMs");
+const TCHAR* const kRegValueProxyHost               = _T("ProxyHost");
+const TCHAR* const kRegValueProxyPort               = _T("ProxyPort");
+const TCHAR* const kRegValueMID                     = _T("mid");
+
+// The values below can be overriden in unofficial builds.
+const TCHAR* const kRegValueNameWindowsInstalling = _T("WindowsInstalling");
+
+// Allows Omaha to log events in the Windows Event Log. This is
+// a DWORD value 0: Log nothing, 1: Log warnings and errors, 2: Log everything.
+const TCHAR* const kRegValueEventLogLevel      = _T("LogEventLevel");
+
+enum LogEventLevel {
+  LOG_EVENT_LEVEL_NONE           = 0,
+  LOG_EVENT_LEVEL_WARN_AND_ERROR = 1,
+  LOG_EVENT_LEVEL_ALL            = 2
+};
+
+// How often Omaha checks the server for updates.
+const TCHAR* const kRegValueLastCheckPeriodSec = _T("LastCheckPeriodSec");
+
+// Uses the production or the test cup keys. Once the client has negotiated
+// CUP credentials {sk, c} and it has saved them under the corresponding
+// Google\Update\network key then the client does not need any of the CUP keys.
+// To force the client to use test or production keys, {sk, c} credentials must
+// be cleared too.
+const TCHAR* const kRegValueCupKeys            = _T("TestKeys");
+
+// Allow a custom host pattern to be specified. For example,
+// "^https?://some_test_server\.google\.com/". For other examples, see
+// kSiteLockPatternStrings. The detailed regular expression syntax is documented
+// in the MSDN documentation for the CAtlRegExp class:
+// http://msdn.microsoft.com/en-us/library/k3zs4axe.aspx.
+const TCHAR* const kRegValueOneClickHostPattern = _T("OneClickHostPattern");
+
+// Disables the Code Red check.
+const TCHAR* const kRegValueNoCodeRedCheck     = _T("NoCrCheck");
+
+// Enables sending usage stats always if the value is present.
+const TCHAR* const kRegValueForceUsageStats    = _T("UsageStats");
+
+// Enables crash uploads if the value is 1. Crashes can be uploaded only if
+// certain conditions are met. This value allows overriding of the default
+// crash uploading behavior.
+const TCHAR* const kRegValueAlwaysAllowCrashUploads =
+    _T("AlwaysAllowCrashUploads");
+
+// Enables monitoring the 'LastChecked' value for testing purposes. When
+// the 'LastChecked' is deleted, the core starts a worker process to do an
+// update check. This value must be set before the core process starts.
+const TCHAR* const kRegValueMonitorLastChecked = _T("MonitorLastChecked");
+
+// The test_source value to use for Omaha instances that have
+// customizations, and hence should be discarded from metrics.
+const TCHAR* const kRegValueTestSourceAuto     = _T("auto");
+
+// The network configuration to override the network detection.
+// The corresponding value must have the following format:
+// wpad=[false|true];script=script_url;proxy=host:port
+const TCHAR* const kRegValueNetConfig          = _T("NetConfig");
+
+// The maximum length of application and bundle names.
+const int kMaxNameLength = 512;
+
+// Specifies whether a tristate item has a value and if so what the value is.
+enum Tristate {
+  TRISTATE_FALSE,
+  TRISTATE_TRUE,
+  TRISTATE_NONE
+};
+
+// Number of periods to use when abbreviating URLs
+#define kAbbreviationPeriodLength 3
+
+// The Unicode "Byte Order Marker" character.  This is the native
+// encoding, i.e. after conversion from UTF-8 or whatever.
+const wchar_t kUnicodeBom = 0xFEFF;
+
+// Using these constants will make ATL load the
+// typelib directly from a DLL instead of looking up typelib
+// registration in registry.
+const DWORD kMajorTypeLibVersion = 0xFFFF;
+const DWORD kMinorTypeLibVersion = 0xFFFF;
+
+// Brand id length
+const int kBrandIdLength = 4;
+
+// Country code length according to ISO 3166-3.
+const int kCountryCodeMaxLength = 5;
+
+// Language code length.
+const int kLangMaxLength = 10;
+
+// When not specified, the country code defaults to USA
+const TCHAR* const kDefaultCountryCode = _T("us");
+
+// the max length of the extra info we can store inside the install stubs.
+const int kExtraMaxLength = 64 * 1024;  // 64 KB
+
+// Default brand code value when one is not specified.
+// This has been specifically assigned to Omaha.
+const TCHAR* const kDefaultGoogleUpdateBrandCode = _T("GGLS");
+
+// The platform named used for Windows.
+const TCHAR* const kPlatformWin = _T("win");
+
+// TODO(omaha3): Move goopdate-specific values in this file to const_goopdate.h.
+// Maybe there should be a const_server_api.h file so the server API is
+// documented in one location.
+
+// The following are response strings returned by the server.
+// They must exactly match the strings returned by the server.
+const TCHAR* const kResponseStatusOkValue = _T("ok");
+const TCHAR* const kResponseStatusNoUpdate = _T("noupdate");
+const TCHAR* const kResponseStatusRestrictedExportCountry = _T("restricted");
+const TCHAR* const kResponseStatusOsNotSupported = _T("error-osnotsupported");
+const TCHAR* const kResponseStatusUnKnownApplication =
+    _T("error-UnKnownApplication");
+const TCHAR* const kResponseStatusInternalError = _T("error-internal");
+const TCHAR* const kResponseStatusHashError = _T("error-hash");
+const TCHAR* const kResponseStatusUnsupportedProtocol =
+    _T("error-unsupportedprotocol");
+const TCHAR* const kResponseDataStatusNoData = _T("error-nodata");
+
+const TCHAR* const kLocalSystemSid = _T("S-1-5-18");
+
+// Time-related constants for defining durations.
+const int kMsPerSec           = 1000;
+const int kSecPerMin          = 60;
+const int kMinPerHour         = 60;
+const int kSecondsPerHour     = 60 * 60;
+const int kSecondsPerDay      = 24 * kSecondsPerHour;
+
+// Defines LastCheckPeriodSec: the time interval between actual server
+// update checks. Opt builds have an aggressive check for updates every 5 hours.
+// This introduces some time shift for computers connected all the time, for
+// example, the update checks occur at: 12, 17, 22, 3, 8, 13, 18, etc...
+//
+// Since time computation for LastChecked is done in seconds, sometimes it
+// can miss an update check, depending on arithmetic truncations.
+// Adjust down the LastCheckPeriod so that the update worker does not miss it.
+//
+// Almost 5 hours for production users and almost hourly for internal users.
+const int kLastCheckPeriodSec              = 5 * 59 * kMinPerHour;
+const int kLastCheckPeriodInternalUserSec  = 1 * 59 * kMinPerHour;
+
+
+const int kMinLastCheckPeriodSec = 60;  // 60 seconds minimum.
+
+// Defines the time interval when the core is kicking off silent workers. When
+// there is nothing to do, a worker does not take more than 200 ms to run.
+// Internal users are supposed to update at least once every hour. Therefore,
+// start workers every 30 minutes.
+const int kAUCheckPeriodMs             = 60 * 60 * 1000;  // Hourly.
+const int kAUCheckPeriodInternalUserMs = 30 * 60 * 1000;  // 30 minutes.
+
+// Avoids starting workers too soon. This helps reduce disk thrashing at
+// boot or logon, as well as needlessly starting a worker after setting up.
+const int kUpdateTimerStartupDelayMinMs = 5 * 60 * 1000;   // 5 minutes.
+
+// Maximum amount of time to wait before starting an update worker.
+const int kUpdateTimerStartupDelayMaxMs = 15 * 60 * 1000;   // 15 minutes.
+
+// Minimum AU check interval is lowered to 3 seconds to speed up test
+// automation.
+const int kMinAUCheckPeriodMs = 1000 * 3;   // 3 seconds.
+
+// The Code Red check frequency.
+const int kCodeRedCheckPeriodMs     = 24 * 60 * 60 * 1000;    // 24 hours.
+const int kMinCodeRedCheckPeriodMs  = 60 * 1000;              // 1 minute.
+
+// The minimum amount of time after a /oem install that Omaha is considered to
+// be in OEM mode regardless of audit mode.
+const int kMinOemModeSec = 72 * 60 * 60;  // 72 hours.
+
+// The amount of time to wait for the setup lock before giving up.
+const int kSetupLockWaitMs = 1000;  // 1 second.
+
+// The amount of time to wait for other instances to shutdown before giving up.
+const int kSetupInstallShutdownWaitMs = 45 * 1000;      // 45 seconds.
+const int kSetupUpdateShutdownWaitMs  = 3 * 60 * 1000;  // 3 minutes.
+
+// Time to wait for the busy MSI when uninstalling. If MSI is found busy,
+// Omaha won't uninstall. The timeout should be high enough so that it allows
+// a normal application uninstall to finish and trigger an Omaha uninstall
+// when needed.
+const int kWaitForMSIExecuteMs                = 5 * 60000;  // 5 minutes.
+
+// Name of the language key-value pair inside the version resource.
+const TCHAR* const kLanguageVersionName = _T("LanguageId");
+
+// Group name to use to read/write INI files for custom crash client info.
+const TCHAR* const kCustomClientInfoGroup = _T("ClientCustomData");
+
+// ***                                            ***
+// *** Custom HTTP request headers sent by Omaha. ***
+// ***                                            ***
+const TCHAR* const kHeaderUserAgent           = _T("User-Agent");
+
+// The HRESULT and HTTP status code updated by the prior
+// NetworkRequestImpl::DoSendHttpRequest() call.
+const TCHAR* const kHeaderXLastHR             = _T("X-Last-HR");
+const TCHAR* const kHeaderXLastHTTPStatusCode = _T("X-Last-HTTP-Status-Code");
+
+// The "mid" value if it exists in HKLM\SOFTWARE\Google\UpdateDev.
+const TCHAR* const kHeaderXMID                = _T("X-MID");
+
+// The 407 retry count in the case of authenticated proxies.
+const TCHAR* const kHeaderXProxyRetryCount    = _T("X-Proxy-Retry-Count");
+
+// Indicates that we had to prompt the user for proxy credentials.
+const TCHAR* const kHeaderXProxyManualAuth    = _T("X-Proxy-Manual-Auth");
+
+// The age in seconds between the current time and when a ping was first
+// persisted.
+const TCHAR* const kHeaderXRequestAge         = _T("X-RequestAge");
+
+// The current retry count defined by the outermost
+// NetworkRequestImpl::DoSendWithRetries() call.
+const TCHAR* const kHeaderXRetryCount         = _T("X-Retry-Count");
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_CONSTANTS_H_
diff --git a/base/crash_if_specific_error.cc b/base/crash_if_specific_error.cc
new file mode 100644
index 0000000..57bef78
--- /dev/null
+++ b/base/crash_if_specific_error.cc
@@ -0,0 +1,35 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/crash_if_specific_error.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vista_utils.h"
+
+#undef SUCCEEDED
+#undef FAILED
+
+namespace omaha {
+
+HRESULT g_crash_specific_error = 0;
+
+void CrashIfSpecificError(HRESULT hr) {
+  if (g_crash_specific_error && hr == g_crash_specific_error) {
+    ::RaiseException(0, EXCEPTION_NONCONTINUABLE, 0, NULL);
+  }
+}
+
+}  // namespace omaha
+
diff --git a/base/crash_if_specific_error.h b/base/crash_if_specific_error.h
new file mode 100644
index 0000000..6321a47
--- /dev/null
+++ b/base/crash_if_specific_error.h
@@ -0,0 +1,58 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_BASE_CRASH_IF_SPECIFIC_ERROR_H_
+#define OMAHA_BASE_CRASH_IF_SPECIFIC_ERROR_H_
+#include <windows.h>
+
+#if defined __cplusplus
+
+namespace omaha {
+
+extern HRESULT g_crash_specific_error;
+extern void CrashIfSpecificError(HRESULT hr);
+
+#define CRASH_IF_SPECIFIC_ERROR(hr) CrashIfSpecificError(hr)
+
+inline bool CheckSuccessWithSpecificError(HRESULT hr) {
+  CRASH_IF_SPECIFIC_ERROR(hr);
+  return hr >= 0;
+}
+
+inline bool CheckFailureWithSpecificError(HRESULT hr) {
+  CRASH_IF_SPECIFIC_ERROR(hr);
+  return hr < 0;
+}
+
+}  // namespace omaha
+
+#ifdef SUCCEEDED
+#undef SUCCEEDED
+#endif
+#define SUCCEEDED(hr) omaha::CheckSuccessWithSpecificError(hr)
+
+#ifdef FAILED
+#undef FAILED
+#endif
+#define FAILED(hr) omaha::CheckFailureWithSpecificError(hr)
+
+#else   // defined __cplusplus
+
+#define CRASH_IF_SPECIFIC_ERROR(hr)
+
+#endif  // defined __cplusplus
+
+#endif  // OMAHA_BASE_CRASH_IF_SPECIFIC_ERROR_H_
+
diff --git a/base/crc.cc b/base/crc.cc
new file mode 100644
index 0000000..fe3cb25
--- /dev/null
+++ b/base/crc.cc
@@ -0,0 +1,899 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Implemetation of CRCs (aka Rabin Fingerprints).
+// Treats the input as a polynomial with coefficients in Z(2),
+// and finds the remainder when divided by an irreducible polynomial
+// of the appropriate length.
+// It handles all CRC sizes from 8 to 128 bits.
+// It's somewhat complicated by having separate implementations optimized for
+// CRC's <=32 bits, <= 64 bits, and <= 128 bits.
+// The input string is prefixed with a "1" bit, and has "degree" "0" bits
+// appended to it before the remainder is found.   This ensures that
+// short strings are scrambled somewhat and that strings consisting
+// of all nulls have a non-zero CRC.
+
+#include <stddef.h>
+#include "omaha/base/crc.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/commontypes.h"
+
+namespace omaha {
+
+static const int SMALL_BITS = 8;
+                   // When extending an input with a string of zeroes,
+                   // if the number of zeroes is less than 2**SMALL_BITS,
+                   // a normal Extend is done, rather than a polynomial
+                   // multiplication.
+static const char zeroes[1 << SMALL_BITS] = { 0 };  // an array of zeroes
+
+static const uint8 *zero_ptr = 0;   // The 0 pointer---used for alignment
+
+// These are used to index a 2-entry array of words that together
+// for a longer integer.  LO indexes the low-order half.
+#define LO 0
+#define HI (1-LO)
+
+// Constructor and destructor for baseclase CRC.
+CRC::~CRC() {}
+CRC::CRC() {}
+
+struct CRC_pair {             // Used to represent a 128-bit value
+  uint64 lo;
+  uint64 hi;
+};
+
+class CRCImpl : public CRC {    // Implemention of the abstract class CRC
+ public:
+  CRCImpl() {}
+  virtual ~CRCImpl() {}
+
+  // The internal version of CRC::New().
+  static CRCImpl *NewInternal(uint64 lo, uint64 hi,
+                              int degree, size_t roll_length);
+
+  virtual void Empty(uint64 *lo, uint64 *hi) const;
+
+  size_t roll_length_;    // length of window in rolling CRC
+  int degree_;            // bits in the CRC
+  uint64 poly_lo_;        // The CRC of the empty string, low part
+  uint64 poly_hi_;        // The CRC of the empty string, high part
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(CRCImpl);
+};
+
+// This is the 32-bit implementation.  It handles all sizes from 8 to 32.
+class CRC32 : public CRCImpl {
+ public:
+  CRC32() {}
+  virtual ~CRC32() {}
+
+  virtual void Extend(uint64 *lo, uint64 *hi,
+                      const void *bytes, size_t length) const;
+  virtual void ExtendByZeroes(uint64 *lo, uint64 *hi, size_t length) const;
+  virtual void Roll(uint64 *lo, uint64 *hi, uint8 o_byte, uint8 i_byte) const;
+
+  uint32 table0_[256];  // table of byte extensions
+  uint32 table1_[256];  // table of byte extensions, shifted by 1 byte
+  uint32 table2_[256];  // table of byte extensions, shifted by 2 bytes
+  uint32 table3_[256];  // table of byte extensions, shifted by 3 bytes
+  uint32 roll_[256];    // table of byte roll values
+  uint32 zeroes_[256];  // table of zero extensions
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(CRC32);
+};
+
+static const uint64 UINT64_ZERO = 0;    // a 64-bit zero
+static const uint64 UINT64_ONE = 1;     // a 64-bit 1
+
+// The B() macro sets the bit corresponding to X**(_x) in the polynomial
+#define B(_x) (UINT64_ONE << ((_x) < 64?(63-(_x)):(127-(_x))))
+
+// Used to initialize polynomials.
+// The redundant tests on _len are to avoid warnings from the
+// compiler about inappropriate shift lengths.   These shifts
+// occur on not-taken branch of the ?: in some cases.
+#define kDefPoly(_h,_l,_len) \
+ { ((_len) <= 64  ? (_l) >> ((_len) <= 64? 64 - (_len): 0) : \
+    (_len) == 128 ? (_h) : \
+                     ((_h) >> ((_len) > 64? 128 - (_len): 0)) | \
+                     ((_l) << ((_len) > 64 && (_len) < 128? (_len)-64: 0))), \
+   ((_len) <= 64  ? 0 : \
+    (_len) == 128 ? (_l) :  \
+                    (_l) >> ((_len) > 64? 128 - (_len): 0)), \
+   (_len) }
+
+// A table of irreducible polynomials suitable for use with the implementation.
+// Indexes 0...1 have degree 32 polynomials.
+// Indexes 2...3 have degree 64 polynomials.
+// Indexes 4...5 have degree 96 polynomials.
+// Indexes 6...7 have degree 128 polynomials.
+// Index i=8...128 has a degree i polynomial.
+// All polynomials in the table are guaranteed distinct.
+// lint -save -e572 -e648 -e778   Excessive shift value, expression evaluates to 0
+static const struct CRC::Poly poly_list[] = {
+ kDefPoly(UINT64_ZERO, B(30)+B(27)+B(26)+B(25)+B(23)+B(20)+B(17)+B(15)+B(14)+
+          B(12)+B(6)+B(5)+B(2)+B(0), 32),
+ kDefPoly(UINT64_ZERO, B(31)+B(28)+B(27)+B(26)+B(24)+B(22)+B(19)+B(18)+B(16)+
+          B(13)+B(11)+B(10)+B(9)+B(4)+B(2)+B(0), 32),
+ kDefPoly(UINT64_ZERO, B(60)+B(59)+B(58)+B(56)+B(55)+B(54)+B(51)+B(50)+B(49)+
+          B(48)+B(47)+B(45)+B(44)+B(42)+B(40)+B(39)+B(38)+B(36)+B(34)+B(33)+
+          B(32)+B(31)+B(30)+B(27)+B(25)+B(23)+B(22)+B(21)+B(20)+B(19)+
+          B(17)+B(16)+B(15)+B(8)+B(7)+B(6)+B(5)+B(0), 64),
+ kDefPoly(UINT64_ZERO, B(63)+B(62)+B(60)+B(58)+B(57)+B(56)+B(54)+B(52)+B(46)+
+          B(45)+B(43)+B(40)+B(37)+B(36)+B(34)+B(33)+B(32)+B(31)+B(30)+B(29)+
+          B(28)+B(27)+B(26)+B(23)+B(19)+B(18)+B(15)+B(14)+B(13)+B(9)+B(8)+
+          B(0), 64),
+ kDefPoly(B(95)+B(94)+B(91)+B(90)+B(89)+B(88)+B(87)+B(86)+B(79)+B(78)+
+          B(77)+B(76)+B(75)+B(74)+B(73)+B(69)+B(68)+B(66), B(63)+B(61)+
+          B(59)+B(57)+B(53)+B(51)+B(50)+B(47)+B(40)+B(39)+B(38)+B(36)+
+          B(35)+B(33)+B(29)+B(28)+B(27)+B(25)+B(24)+B(23)+B(21)+B(19)+
+          B(18)+B(17)+B(16)+B(13)+B(12)+B(10)+B(9)+B(7)+B(4)+B(2)+B(1)+
+          B(0), 96),
+ kDefPoly(B(95)+B(92)+B(89)+B(88)+B(87)+B(85)+B(84)+B(82)+B(81)+B(80)+
+          B(79)+B(78)+B(76)+B(75)+B(70)+B(69)+B(66)+B(65), B(60)+B(56)+
+          B(55)+B(52)+B(51)+B(49)+B(48)+B(46)+B(44)+B(42)+B(41)+B(39)+
+          B(38)+B(37)+B(35)+B(33)+B(32)+B(30)+B(28)+B(27)+B(25)+B(22)+
+          B(19)+B(17)+B(14)+B(12)+B(10)+B(0), 96),
+ kDefPoly(B(122)+B(121)+B(120)+B(119)+B(117)+B(116)+B(114)+B(113)+B(112)+
+          B(111)+B(109)+B(107)+B(104)+B(102)+B(100)+B(98)+B(96)+B(94)+
+          B(93)+B(92)+B(91)+B(90)+B(88)+B(87)+B(86)+B(84)+B(82)+B(80)+
+          B(75)+B(74)+B(73)+B(69), B(62)+B(61)+B(58)+B(52)+B(48)+B(47)+
+          B(46)+B(45)+B(42)+B(41)+B(38)+B(37)+B(35)+B(33)+B(32)+B(31)+
+          B(30)+B(28)+B(26)+B(24)+B(22)+B(21)+B(20)+B(19)+B(18)+B(17)+
+          B(10)+B(9)+B(8)+B(7)+B(5)+B(2)+B(1)+B(0), 128),
+ kDefPoly(B(127)+B(126)+B(124)+B(121)+B(117)+B(116)+B(115)+B(113)+B(112)+
+          B(111)+B(108)+B(105)+B(104)+B(103)+B(100)+B(98)+B(96)+B(93)+
+          B(92)+B(90)+B(89)+B(88)+B(86)+B(85)+B(80)+B(77)+B(76)+B(72)+
+          B(70)+B(69)+B(68)+B(65)+B(64), B(62)+B(61)+B(59)+B(58)+B(56)+
+          B(53)+B(52)+B(51)+B(50)+B(48)+B(46)+B(39)+B(35)+B(34)+B(33)+
+          B(32)+B(30)+B(29)+B(28)+B(22)+B(21)+B(19)+B(18)+B(17)+B(14)+
+          B(10)+B(9)+B(7)+B(5)+B(4)+B(3)+B(2)+B(0), 128),
+ kDefPoly(UINT64_ZERO, B(7)+B(6)+B(5)+B(4)+B(2)+B(0), 8),
+ kDefPoly(UINT64_ZERO, B(8)+B(4)+B(3)+B(2)+B(1)+B(0), 9),
+ kDefPoly(UINT64_ZERO, B(8)+B(6)+B(5)+B(3)+B(1)+B(0), 10),
+ kDefPoly(UINT64_ZERO, B(10)+B(9)+B(7)+B(5)+B(1)+B(0), 11),
+ kDefPoly(UINT64_ZERO, B(11)+B(10)+B(5)+B(2)+B(1)+B(0), 12),
+ kDefPoly(UINT64_ZERO, B(12)+B(11)+B(10)+B(8)+B(5)+B(3)+B(2)+B(0), 13),
+ kDefPoly(UINT64_ZERO, B(11)+B(10)+B(9)+B(8)+B(7)+B(5)+B(4)+B(2)+B(1)+
+          B(0), 14),
+ kDefPoly(UINT64_ZERO, B(14)+B(12)+B(11)+B(10)+B(9)+B(5)+B(3)+B(2)+B(1)+
+          B(0), 15),
+ kDefPoly(UINT64_ZERO, B(12)+B(11)+B(7)+B(6)+B(5)+B(4)+B(3)+B(0), 16),
+ kDefPoly(UINT64_ZERO, B(16)+B(14)+B(11)+B(10)+B(8)+B(3)+B(1)+B(0), 17),
+ kDefPoly(UINT64_ZERO, B(12)+B(11)+B(9)+B(8)+B(7)+B(4)+B(3)+B(0), 18),
+ kDefPoly(UINT64_ZERO, B(12)+B(11)+B(8)+B(7)+B(6)+B(5)+B(2)+B(0), 19),
+ kDefPoly(UINT64_ZERO, B(18)+B(15)+B(14)+B(12)+B(9)+B(6)+B(3)+B(0), 20),
+ kDefPoly(UINT64_ZERO, B(20)+B(19)+B(14)+B(13)+B(12)+B(11)+B(8)+B(7)+B(6)+
+          B(5)+B(2)+B(0), 21),
+ kDefPoly(UINT64_ZERO, B(21)+B(20)+B(18)+B(16)+B(15)+B(14)+B(12)+B(9)+B(7)+
+          B(2)+B(1)+B(0), 22),
+ kDefPoly(UINT64_ZERO, B(22)+B(21)+B(17)+B(16)+B(15)+B(14)+B(12)+B(10)+B(7)+
+          B(4)+B(1)+B(0), 23),
+ kDefPoly(UINT64_ZERO, B(23)+B(22)+B(21)+B(18)+B(17)+B(15)+B(14)+B(12)+B(4)+
+          B(0), 24),
+ kDefPoly(UINT64_ZERO, B(24)+B(23)+B(22)+B(20)+B(18)+B(17)+B(14)+B(13)+B(9)+
+          B(0), 25),
+ kDefPoly(UINT64_ZERO, B(25)+B(22)+B(21)+B(19)+B(17)+B(15)+B(14)+B(12)+B(11)+
+          B(10)+B(6)+B(4)+B(3)+B(0), 26),
+ kDefPoly(UINT64_ZERO, B(26)+B(25)+B(19)+B(17)+B(16)+B(13)+B(5)+B(4)+B(1)+
+          B(0), 27),
+ kDefPoly(UINT64_ZERO, B(23)+B(22)+B(21)+B(20)+B(19)+B(18)+B(13)+B(12)+B(10)+
+          B(9)+B(8)+B(6)+B(5)+B(3)+B(1)+B(0), 28),
+ kDefPoly(UINT64_ZERO, B(27)+B(26)+B(25)+B(23)+B(22)+B(20)+B(19)+B(15)+B(14)+
+          B(11)+B(10)+B(8)+B(7)+B(6)+B(4)+B(0), 29),
+ kDefPoly(UINT64_ZERO, B(29)+B(27)+B(25)+B(23)+B(20)+B(19)+B(18)+B(17)+B(16)+
+          B(14)+B(11)+B(10)+B(9)+B(7)+B(6)+B(5)+B(4)+B(0), 30),
+ kDefPoly(UINT64_ZERO, B(30)+B(29)+B(28)+B(27)+B(25)+B(23)+B(22)+B(21)+B(20)+
+          B(19)+B(18)+B(16)+B(15)+B(10)+B(9)+B(8)+B(4)+B(3)+B(1)+B(0), 31),
+ kDefPoly(UINT64_ZERO, B(31)+B(29)+B(28)+B(27)+B(21)+B(20)+B(15)+B(13)+B(10)+
+          B(9)+B(8)+B(7)+B(4)+B(3)+B(2)+B(0), 32),
+ kDefPoly(UINT64_ZERO, B(32)+B(31)+B(30)+B(29)+B(27)+B(25)+B(24)+B(22)+B(21)+
+          B(19)+B(15)+B(10)+B(4)+B(3)+B(2)+B(0), 33),
+ kDefPoly(UINT64_ZERO, B(30)+B(27)+B(26)+B(25)+B(24)+B(20)+B(19)+B(18)+B(16)+
+          B(15)+B(14)+B(12)+B(9)+B(8)+B(7)+B(5)+B(1)+B(0), 34),
+ kDefPoly(UINT64_ZERO, B(34)+B(32)+B(28)+B(27)+B(26)+B(22)+B(21)+B(20)+B(19)+
+          B(14)+B(13)+B(12)+B(10)+B(6)+B(5)+B(4)+B(3)+B(0), 35),
+ kDefPoly(UINT64_ZERO, B(35)+B(34)+B(33)+B(32)+B(31)+B(28)+B(26)+B(24)+B(22)+
+          B(21)+B(20)+B(19)+B(18)+B(14)+B(13)+B(12)+B(10)+B(9)+B(8)+B(6)+B(5)+
+          B(4)+B(3)+B(0), 36),
+ kDefPoly(UINT64_ZERO, B(36)+B(35)+B(31)+B(30)+B(28)+B(26)+B(25)+B(23)+B(22)+
+          B(20)+B(19)+B(18)+B(16)+B(13)+B(12)+B(7)+B(6)+B(4)+B(3)+B(0), 37),
+ kDefPoly(UINT64_ZERO, B(37)+B(34)+B(33)+B(32)+B(31)+B(30)+B(29)+B(28)+B(27)+
+          B(26)+B(25)+B(23)+B(18)+B(16)+B(15)+B(13)+B(12)+B(11)+B(10)+B(9)+
+          B(8)+B(7)+B(6)+B(2)+B(1)+B(0), 38),
+ kDefPoly(UINT64_ZERO, B(38)+B(37)+B(33)+B(32)+B(31)+B(27)+B(25)+B(24)+B(21)+
+          B(20)+B(19)+B(18)+B(17)+B(15)+B(14)+B(8)+B(7)+B(6)+B(3)+B(2)+B(1)+
+          B(0), 39),
+ kDefPoly(UINT64_ZERO, B(38)+B(37)+B(35)+B(34)+B(32)+B(31)+B(30)+B(27)+B(24)+
+          B(21)+B(20)+B(14)+B(13)+B(11)+B(8)+B(4)+B(2)+B(0), 40),
+ kDefPoly(UINT64_ZERO, B(38)+B(36)+B(35)+B(34)+B(33)+B(31)+B(30)+B(29)+B(28)+
+          B(27)+B(23)+B(22)+B(20)+B(19)+B(18)+B(17)+B(15)+B(14)+B(11)+B(5)+
+          B(4)+B(0), 41),
+ kDefPoly(UINT64_ZERO, B(41)+B(37)+B(36)+B(35)+B(32)+B(31)+B(30)+B(29)+B(28)+
+          B(25)+B(19)+B(18)+B(14)+B(13)+B(12)+B(7)+B(6)+B(4)+B(2)+B(0), 42),
+ kDefPoly(UINT64_ZERO, B(42)+B(40)+B(38)+B(37)+B(36)+B(35)+B(34)+B(33)+B(31)+
+          B(29)+B(27)+B(26)+B(25)+B(23)+B(21)+B(20)+B(19)+B(15)+B(11)+B(10)+
+          B(9)+B(8)+B(6)+B(5)+B(3)+B(0), 43),
+ kDefPoly(UINT64_ZERO, B(43)+B(42)+B(40)+B(39)+B(37)+B(35)+B(32)+B(30)+B(26)+
+          B(25)+B(24)+B(20)+B(16)+B(13)+B(12)+B(11)+B(8)+B(6)+B(5)+B(4)+B(1)+
+          B(0), 44),
+ kDefPoly(UINT64_ZERO, B(43)+B(42)+B(41)+B(40)+B(39)+B(38)+B(33)+B(32)+B(27)+
+          B(26)+B(25)+B(23)+B(20)+B(18)+B(17)+B(16)+B(14)+B(11)+B(10)+B(9)+
+          B(6)+B(5)+B(1)+B(0), 45),
+ kDefPoly(UINT64_ZERO, B(45)+B(43)+B(42)+B(41)+B(40)+B(39)+B(32)+B(31)+B(30)+
+          B(29)+B(27)+B(25)+B(23)+B(18)+B(17)+B(16)+B(10)+B(9)+B(7)+B(6)+B(4)+
+          B(3)+B(2)+B(0), 46),
+ kDefPoly(UINT64_ZERO, B(45)+B(44)+B(43)+B(41)+B(40)+B(39)+B(38)+B(37)+B(32)+
+          B(30)+B(23)+B(21)+B(20)+B(17)+B(15)+B(13)+B(11)+B(10)+B(7)+B(5)+
+          B(3)+B(0), 47),
+ kDefPoly(UINT64_ZERO, B(46)+B(42)+B(41)+B(39)+B(37)+B(36)+B(35)+B(29)+B(28)+
+          B(25)+B(24)+B(21)+B(20)+B(18)+B(17)+B(13)+B(12)+B(11)+B(10)+B(9)+
+          B(8)+B(5)+B(1)+B(0), 48),
+ kDefPoly(UINT64_ZERO, B(48)+B(44)+B(41)+B(40)+B(39)+B(38)+B(37)+B(36)+B(35)+
+          B(34)+B(30)+B(28)+B(27)+B(24)+B(21)+B(18)+B(17)+B(8)+B(3)+B(0), 49),
+ kDefPoly(UINT64_ZERO, B(48)+B(47)+B(46)+B(45)+B(44)+B(43)+B(42)+B(35)+B(33)+
+          B(29)+B(26)+B(24)+B(23)+B(21)+B(18)+B(16)+B(14)+B(13)+B(12)+B(9)+
+          B(7)+B(6)+B(5)+B(4)+B(3)+B(0), 50),
+ kDefPoly(UINT64_ZERO, B(47)+B(46)+B(45)+B(44)+B(43)+B(40)+B(39)+B(38)+B(36)+
+          B(35)+B(30)+B(29)+B(28)+B(26)+B(25)+B(24)+B(23)+B(22)+B(20)+B(19)+
+          B(18)+B(17)+B(15)+B(11)+B(7)+B(4)+B(3)+B(0), 51),
+ kDefPoly(UINT64_ZERO, B(51)+B(46)+B(43)+B(38)+B(37)+B(36)+B(34)+B(31)+B(27)+
+          B(26)+B(20)+B(17)+B(16)+B(15)+B(13)+B(12)+B(11)+B(9)+B(7)+B(5)+B(1)+
+          B(0), 52),
+ kDefPoly(UINT64_ZERO, B(50)+B(49)+B(47)+B(46)+B(44)+B(42)+B(41)+B(37)+B(36)+
+          B(35)+B(33)+B(29)+B(28)+B(26)+B(24)+B(23)+B(21)+B(20)+B(14)+B(13)+
+          B(12)+B(11)+B(10)+B(9)+B(8)+B(6)+B(3)+B(2)+B(1)+B(0), 53),
+ kDefPoly(UINT64_ZERO, B(52)+B(47)+B(46)+B(44)+B(43)+B(42)+B(40)+B(36)+B(32)+
+          B(31)+B(30)+B(29)+B(28)+B(26)+B(25)+B(24)+B(23)+B(22)+B(20)+B(19)+
+          B(17)+B(16)+B(15)+B(14)+B(13)+B(12)+B(11)+B(10)+B(7)+B(4)+B(2)+
+          B(0), 54),
+ kDefPoly(UINT64_ZERO, B(53)+B(50)+B(48)+B(47)+B(37)+B(35)+B(31)+B(30)+B(25)+
+          B(22)+B(21)+B(20)+B(19)+B(18)+B(15)+B(10)+B(8)+B(6)+B(3)+B(2)+B(1)+
+          B(0), 55),
+ kDefPoly(UINT64_ZERO, B(54)+B(52)+B(51)+B(49)+B(48)+B(42)+B(38)+B(37)+B(31)+
+          B(30)+B(27)+B(26)+B(24)+B(23)+B(22)+B(19)+B(16)+B(12)+B(11)+B(8)+
+          B(6)+B(4)+B(3)+B(0), 56),
+ kDefPoly(UINT64_ZERO, B(55)+B(54)+B(51)+B(49)+B(48)+B(47)+B(46)+B(44)+B(43)+
+          B(42)+B(41)+B(40)+B(39)+B(38)+B(32)+B(29)+B(27)+B(26)+B(23)+B(21)+
+          B(20)+B(15)+B(12)+B(7)+B(6)+B(5)+B(3)+B(0), 57),
+ kDefPoly(UINT64_ZERO, B(57)+B(54)+B(52)+B(47)+B(45)+B(42)+B(41)+B(40)+B(39)+
+          B(36)+B(34)+B(33)+B(31)+B(28)+B(26)+B(21)+B(20)+B(18)+B(17)+B(16)+
+          B(13)+B(11)+B(8)+B(7)+B(4)+B(2)+B(1)+B(0), 58),
+ kDefPoly(UINT64_ZERO, B(58)+B(56)+B(54)+B(49)+B(47)+B(46)+B(43)+B(40)+B(38)+
+          B(36)+B(35)+B(33)+B(32)+B(31)+B(30)+B(27)+B(24)+B(22)+B(21)+B(19)+
+          B(17)+B(16)+B(11)+B(10)+B(9)+B(8)+B(7)+B(4)+B(3)+B(2)+B(1)+B(0),
+          59),
+ kDefPoly(UINT64_ZERO, B(56)+B(54)+B(51)+B(46)+B(43)+B(42)+B(40)+B(39)+B(37)+
+          B(35)+B(34)+B(33)+B(32)+B(31)+B(30)+B(29)+B(27)+B(25)+B(22)+B(21)+
+          B(20)+B(19)+B(17)+B(16)+B(15)+B(14)+B(13)+B(12)+B(9)+B(7)+B(4)+
+          B(3)+B(1)+B(0), 60),
+ kDefPoly(UINT64_ZERO, B(59)+B(58)+B(57)+B(56)+B(54)+B(53)+B(50)+B(49)+B(47)+
+          B(44)+B(42)+B(41)+B(40)+B(37)+B(35)+B(34)+B(32)+B(30)+B(29)+B(27)+
+          B(26)+B(22)+B(21)+B(20)+B(17)+B(14)+B(13)+B(12)+B(8)+B(5)+B(4)+
+          B(0), 61),
+ kDefPoly(UINT64_ZERO, B(61)+B(59)+B(57)+B(55)+B(54)+B(53)+B(52)+B(51)+B(50)+
+          B(49)+B(48)+B(45)+B(44)+B(40)+B(37)+B(35)+B(32)+B(31)+B(29)+B(25)+
+          B(24)+B(23)+B(20)+B(17)+B(16)+B(15)+B(13)+B(12)+B(11)+B(10)+B(6)+
+          B(5)+B(2)+B(0), 62),
+ kDefPoly(UINT64_ZERO, B(62)+B(57)+B(56)+B(53)+B(52)+B(51)+B(50)+B(46)+B(41)+
+          B(38)+B(35)+B(34)+B(33)+B(31)+B(27)+B(25)+B(23)+B(21)+B(19)+B(18)+
+          B(17)+B(16)+B(13)+B(11)+B(7)+B(5)+B(1)+B(0), 63),
+ kDefPoly(UINT64_ZERO, B(62)+B(61)+B(60)+B(57)+B(55)+B(54)+B(53)+B(49)+B(48)+
+          B(46)+B(44)+B(42)+B(40)+B(39)+B(37)+B(36)+B(28)+B(27)+B(25)+B(23)+
+          B(22)+B(21)+B(17)+B(15)+B(13)+B(7)+B(6)+B(4)+B(2)+B(0), 64),
+ kDefPoly(UINT64_ZERO, B(63)+B(62)+B(59)+B(57)+B(54)+B(53)+B(51)+B(48)+
+          B(47)+B(46)+B(45)+B(44)+B(41)+B(40)+B(38)+B(36)+B(35)+B(28)+
+          B(25)+B(24)+B(21)+B(20)+B(18)+B(16)+B(15)+B(13)+B(11)+B(8)+B(7)+
+          B(3)+B(1)+B(0), 65),
+ kDefPoly(UINT64_ZERO, B(63)+B(58)+B(57)+B(56)+B(52)+B(51)+B(50)+B(44)+
+          B(41)+B(40)+B(36)+B(34)+B(32)+B(31)+B(27)+B(25)+B(23)+B(21)+
+          B(20)+B(19)+B(18)+B(17)+B(15)+B(14)+B(12)+B(11)+B(10)+B(8)+B(5)+
+          B(4)+B(3)+B(0), 66),
+ kDefPoly(B(66), B(62)+B(60)+B(59)+B(58)+B(57)+B(56)+B(55)+B(54)+B(52)+
+          B(50)+B(47)+B(46)+B(45)+B(43)+B(42)+B(41)+B(38)+B(37)+B(36)+
+          B(33)+B(32)+B(31)+B(30)+B(28)+B(27)+B(26)+B(24)+B(21)+B(18)+
+          B(17)+B(14)+B(13)+B(12)+B(11)+B(10)+B(7)+B(4)+B(3)+B(0), 67),
+ kDefPoly(B(67)+B(66), B(63)+B(61)+B(57)+B(55)+B(51)+B(47)+B(45)+B(43)+
+          B(42)+B(41)+B(40)+B(39)+B(32)+B(31)+B(30)+B(28)+B(27)+B(25)+
+          B(19)+B(18)+B(17)+B(15)+B(11)+B(9)+B(8)+B(7)+B(6)+B(5)+B(4)+B(3)+
+          B(1)+B(0), 68),
+ kDefPoly(B(68), B(60)+B(57)+B(55)+B(54)+B(52)+B(50)+B(49)+B(48)+B(44)+
+          B(40)+B(38)+B(37)+B(33)+B(31)+B(28)+B(25)+B(22)+B(21)+B(20)+
+          B(19)+B(18)+B(17)+B(13)+B(12)+B(9)+B(8)+B(6)+B(5)+B(4)+B(1)+
+          B(0), 69),
+ kDefPoly(B(69)+B(68)+B(67)+B(66), B(63)+B(62)+B(61)+B(59)+B(51)+B(49)+
+          B(48)+B(46)+B(45)+B(42)+B(40)+B(38)+B(36)+B(35)+B(33)+B(32)+
+          B(30)+B(29)+B(27)+B(23)+B(22)+B(21)+B(16)+B(12)+B(5)+B(4)+B(1)+
+          B(0), 70),
+ kDefPoly(B(70)+B(69)+B(68)+B(64), B(63)+B(62)+B(61)+B(60)+B(59)+B(57)+
+          B(56)+B(55)+B(54)+B(53)+B(51)+B(50)+B(47)+B(44)+B(43)+B(41)+
+          B(39)+B(37)+B(36)+B(33)+B(32)+B(26)+B(25)+B(24)+B(23)+B(21)+
+          B(20)+B(19)+B(17)+B(12)+B(11)+B(10)+B(8)+B(6)+B(5)+B(4)+B(2)+
+          B(0), 71),
+ kDefPoly(B(71)+B(69)+B(68)+B(65)+B(64), B(62)+B(61)+B(59)+B(58)+B(55)+
+          B(53)+B(51)+B(49)+B(48)+B(47)+B(43)+B(40)+B(38)+B(37)+B(36)+
+          B(35)+B(33)+B(32)+B(31)+B(30)+B(29)+B(26)+B(24)+B(19)+B(18)+
+          B(15)+B(13)+B(9)+B(7)+B(6)+B(3)+B(1)+B(0), 72),
+ kDefPoly(B(71)+B(70)+B(69)+B(67)+B(65), B(63)+B(62)+B(61)+B(58)+B(57)+
+          B(56)+B(55)+B(52)+B(51)+B(50)+B(49)+B(46)+B(45)+B(44)+B(43)+
+          B(41)+B(37)+B(36)+B(34)+B(33)+B(27)+B(26)+B(25)+B(21)+B(19)+
+          B(18)+B(16)+B(15)+B(14)+B(13)+B(9)+B(8)+B(6)+B(5)+B(2)+B(1)+
+          B(0), 73),
+ kDefPoly(B(73)+B(71)+B(70)+B(65)+B(64), B(62)+B(60)+B(55)+B(54)+B(52)+
+          B(50)+B(48)+B(47)+B(46)+B(44)+B(41)+B(40)+B(31)+B(29)+B(28)+
+          B(27)+B(26)+B(24)+B(23)+B(22)+B(20)+B(16)+B(12)+B(9)+B(6)+B(5)+
+          B(4)+B(2)+B(0), 74),
+ kDefPoly(B(74)+B(73)+B(72)+B(67)+B(64), B(63)+B(61)+B(60)+B(58)+B(57)+
+          B(56)+B(54)+B(52)+B(51)+B(50)+B(44)+B(43)+B(42)+B(41)+B(40)+
+          B(39)+B(38)+B(36)+B(35)+B(33)+B(32)+B(31)+B(29)+B(28)+B(26)+
+          B(23)+B(21)+B(19)+B(18)+B(16)+B(15)+B(13)+B(12)+B(11)+B(7)+B(6)+
+          B(5)+B(4)+B(3)+B(2)+B(0), 75),
+ kDefPoly(B(75)+B(74)+B(71)+B(70)+B(66), B(63)+B(61)+B(59)+B(57)+B(53)+
+          B(50)+B(49)+B(48)+B(44)+B(43)+B(42)+B(37)+B(33)+B(30)+B(27)+
+          B(24)+B(23)+B(20)+B(18)+B(15)+B(12)+B(11)+B(9)+B(7)+B(6)+B(4)+
+          B(3)+B(2)+B(0), 76),
+ kDefPoly(B(73)+B(71)+B(70)+B(68)+B(67)+B(66)+B(65), B(63)+B(60)+B(59)+
+          B(58)+B(57)+B(54)+B(49)+B(47)+B(46)+B(45)+B(43)+B(41)+B(38)+
+          B(34)+B(33)+B(31)+B(30)+B(29)+B(27)+B(25)+B(24)+B(21)+B(20)+
+          B(19)+B(16)+B(15)+B(14)+B(13)+B(10)+B(8)+B(6)+B(5)+B(4)+B(2)+
+          B(0), 77),
+ kDefPoly(B(77)+B(76)+B(75)+B(74)+B(70)+B(66)+B(65)+B(64), B(63)+B(62)+
+          B(60)+B(58)+B(57)+B(55)+B(52)+B(51)+B(44)+B(41)+B(39)+B(38)+
+          B(35)+B(31)+B(30)+B(29)+B(26)+B(22)+B(21)+B(20)+B(19)+B(15)+
+          B(13)+B(11)+B(6)+B(4)+B(1)+B(0), 78),
+ kDefPoly(B(78)+B(76)+B(75)+B(71)+B(68)+B(67)+B(65), B(63)+B(61)+B(60)+
+          B(55)+B(54)+B(51)+B(50)+B(48)+B(44)+B(42)+B(41)+B(40)+B(38)+
+          B(35)+B(34)+B(32)+B(28)+B(26)+B(23)+B(22)+B(19)+B(15)+B(13)+
+          B(12)+B(8)+B(7)+B(5)+B(2)+B(0), 79),
+ kDefPoly(B(77)+B(76)+B(75)+B(73)+B(70)+B(66), B(63)+B(61)+B(60)+B(59)+
+          B(56)+B(54)+B(53)+B(52)+B(50)+B(44)+B(43)+B(40)+B(39)+B(38)+
+          B(35)+B(34)+B(33)+B(29)+B(28)+B(27)+B(26)+B(25)+B(24)+B(23)+
+          B(22)+B(21)+B(20)+B(18)+B(16)+B(13)+B(12)+B(11)+B(10)+B(8)+B(7)+
+          B(6)+B(3)+B(2)+B(1)+B(0), 80),
+ kDefPoly(B(78)+B(77)+B(76)+B(75)+B(73)+B(71)+B(67)+B(66)+B(65)+
+          B(64), B(61)+B(54)+B(53)+B(52)+B(49)+B(47)+B(44)+B(41)+B(40)+
+          B(35)+B(33)+B(31)+B(30)+B(28)+B(27)+B(26)+B(25)+B(22)+B(21)+
+          B(20)+B(16)+B(15)+B(13)+B(12)+B(11)+B(0), 81),
+ kDefPoly(B(81)+B(80)+B(79)+B(77)+B(76)+B(74)+B(73)+B(72)+B(68)+B(67)+
+          B(66)+B(64), B(62)+B(51)+B(50)+B(49)+B(47)+B(46)+B(45)+B(43)+
+          B(41)+B(38)+B(37)+B(34)+B(32)+B(30)+B(27)+B(26)+B(25)+B(24)+
+          B(23)+B(22)+B(20)+B(19)+B(16)+B(15)+B(13)+B(12)+B(9)+B(7)+B(5)+
+          B(4)+B(1)+B(0), 82),
+ kDefPoly(B(82)+B(81)+B(79)+B(78)+B(77)+B(75)+B(72)+B(71)+B(69)+B(68)+
+          B(67)+B(66)+B(65)+B(64), B(60)+B(58)+B(57)+B(56)+B(53)+B(52)+
+          B(51)+B(49)+B(48)+B(45)+B(43)+B(41)+B(40)+B(39)+B(38)+B(37)+
+          B(36)+B(35)+B(33)+B(26)+B(24)+B(21)+B(19)+B(16)+B(13)+B(12)+
+          B(11)+B(9)+B(7)+B(5)+B(4)+B(3)+B(1)+B(0), 83),
+ kDefPoly(B(79)+B(77)+B(73)+B(72)+B(71)+B(66)+B(64), B(62)+B(61)+B(59)+
+          B(58)+B(57)+B(56)+B(53)+B(52)+B(51)+B(48)+B(47)+B(46)+B(45)+
+          B(43)+B(42)+B(41)+B(38)+B(37)+B(35)+B(33)+B(32)+B(29)+B(24)+
+          B(22)+B(17)+B(16)+B(15)+B(13)+B(11)+B(10)+B(9)+B(7)+B(6)+B(5)+
+          B(0), 84),
+ kDefPoly(B(83)+B(78)+B(76)+B(73)+B(70)+B(69)+B(68)+B(67)+B(66)+
+          B(64), B(62)+B(61)+B(60)+B(59)+B(54)+B(51)+B(50)+B(48)+B(47)+
+          B(42)+B(41)+B(40)+B(38)+B(37)+B(36)+B(34)+B(31)+B(30)+B(28)+
+          B(27)+B(26)+B(24)+B(22)+B(21)+B(20)+B(19)+B(18)+B(16)+B(15)+
+          B(14)+B(13)+B(12)+B(10)+B(6)+B(4)+B(0), 85),
+ kDefPoly(B(84)+B(77)+B(76)+B(75)+B(71)+B(70)+B(69)+B(67)+B(65), B(63)+
+          B(62)+B(59)+B(58)+B(57)+B(55)+B(53)+B(52)+B(51)+B(48)+B(47)+
+          B(45)+B(43)+B(40)+B(38)+B(36)+B(34)+B(33)+B(31)+B(27)+B(25)+
+          B(24)+B(23)+B(22)+B(19)+B(15)+B(13)+B(12)+B(11)+B(8)+B(6)+B(4)+
+          B(0), 86),
+ kDefPoly(B(85)+B(84)+B(83)+B(81)+B(80)+B(78)+B(73)+B(72)+B(70)+B(68)+
+          B(67)+B(64), B(61)+B(60)+B(58)+B(57)+B(55)+B(52)+B(50)+B(49)+
+          B(47)+B(44)+B(37)+B(36)+B(35)+B(34)+B(32)+B(31)+B(30)+B(25)+
+          B(24)+B(23)+B(20)+B(13)+B(12)+B(11)+B(10)+B(9)+B(7)+B(6)+B(4)+
+          B(3)+B(2)+B(0), 87),
+ kDefPoly(B(86)+B(85)+B(84)+B(83)+B(82)+B(80)+B(77)+B(74)+B(70)+B(69)+
+          B(65), B(63)+B(60)+B(59)+B(57)+B(56)+B(55)+B(53)+B(50)+B(49)+
+          B(48)+B(45)+B(42)+B(41)+B(40)+B(39)+B(38)+B(37)+B(36)+B(25)+
+          B(21)+B(19)+B(13)+B(11)+B(8)+B(5)+B(4)+B(2)+B(1)+B(0), 88),
+ kDefPoly(B(86)+B(85)+B(83)+B(82)+B(81)+B(78)+B(77)+B(74)+B(73)+B(72)+
+          B(70)+B(69)+B(68)+B(65)+B(64), B(59)+B(57)+B(55)+B(54)+B(51)+
+          B(50)+B(46)+B(45)+B(44)+B(43)+B(42)+B(40)+B(38)+B(37)+B(33)+
+          B(31)+B(30)+B(29)+B(28)+B(27)+B(23)+B(22)+B(21)+B(20)+B(18)+
+          B(17)+B(16)+B(15)+B(10)+B(9)+B(3)+B(1)+B(0), 89),
+ kDefPoly(B(86)+B(83)+B(82)+B(80)+B(79)+B(73)+B(70)+B(69)+B(67)+
+          B(64), B(63)+B(62)+B(61)+B(57)+B(56)+B(54)+B(51)+B(49)+B(47)+
+          B(46)+B(45)+B(40)+B(39)+B(37)+B(35)+B(33)+B(32)+B(29)+B(28)+
+          B(27)+B(25)+B(24)+B(23)+B(22)+B(21)+B(20)+B(19)+B(18)+B(17)+
+          B(15)+B(9)+B(8)+B(7)+B(3)+B(2)+B(0), 90),
+ kDefPoly(B(90)+B(89)+B(84)+B(81)+B(80)+B(78)+B(74)+B(73)+B(71)+B(68)+
+          B(64), B(60)+B(59)+B(58)+B(57)+B(55)+B(54)+B(52)+B(50)+B(49)+
+          B(47)+B(45)+B(42)+B(41)+B(39)+B(38)+B(36)+B(32)+B(28)+B(25)+
+          B(21)+B(20)+B(19)+B(15)+B(12)+B(11)+B(9)+B(8)+B(3)+
+          B(0), 91),
+ kDefPoly(B(91)+B(89)+B(88)+B(87)+B(86)+B(85)+B(84)+B(83)+B(80)+B(78)+
+          B(76)+B(72)+B(70)+B(68), B(63)+B(62)+B(61)+B(59)+B(57)+B(56)+
+          B(52)+B(51)+B(50)+B(49)+B(43)+B(40)+B(39)+B(37)+B(36)+B(35)+
+          B(34)+B(33)+B(32)+B(26)+B(25)+B(24)+B(23)+B(22)+B(18)+B(15)+
+          B(12)+B(11)+B(9)+B(7)+B(6)+B(3)+B(1)+B(0), 92),
+ kDefPoly(B(86)+B(85)+B(83)+B(82)+B(79)+B(78)+B(77)+B(75)+B(74)+B(73)+
+          B(66)+B(64), B(59)+B(57)+B(56)+B(55)+B(54)+B(52)+B(51)+B(40)+
+          B(38)+B(36)+B(34)+B(33)+B(28)+B(27)+B(26)+B(25)+B(23)+B(22)+
+          B(21)+B(20)+B(19)+B(18)+B(16)+B(15)+B(14)+B(13)+B(12)+B(11)+B(8)+
+          B(7)+B(6)+B(5)+B(4)+B(0), 93),
+ kDefPoly(B(93)+B(92)+B(91)+B(89)+B(88)+B(87)+B(86)+B(81)+B(80)+B(75)+
+          B(66)+B(64), B(62)+B(61)+B(60)+B(59)+B(58)+B(57)+B(56)+B(54)+
+          B(48)+B(47)+B(46)+B(45)+B(44)+B(42)+B(41)+B(38)+B(37)+B(36)+
+          B(34)+B(33)+B(31)+B(30)+B(27)+B(26)+B(25)+B(22)+B(13)+B(12)+
+          B(11)+B(10)+B(8)+B(7)+B(4)+B(0), 94),
+ kDefPoly(B(94)+B(88)+B(87)+B(82)+B(79)+B(78)+B(76)+B(73)+B(65)+
+          B(64), B(62)+B(61)+B(60)+B(59)+B(58)+B(57)+B(53)+B(51)+B(50)+
+          B(49)+B(48)+B(47)+B(46)+B(44)+B(40)+B(36)+B(34)+B(33)+B(30)+
+          B(28)+B(27)+B(25)+B(22)+B(19)+B(18)+B(17)+B(16)+B(14)+B(7)+B(5)+
+          B(3)+B(2)+B(1)+B(0), 95),
+ kDefPoly(B(92)+B(89)+B(88)+B(86)+B(83)+B(79)+B(78)+B(76)+B(75)+B(74)+
+          B(72)+B(70)+B(67)+B(66), B(63)+B(60)+B(57)+B(55)+B(53)+B(51)+
+          B(47)+B(46)+B(44)+B(43)+B(42)+B(39)+B(38)+B(36)+B(34)+B(32)+
+          B(31)+B(30)+B(27)+B(26)+B(25)+B(22)+B(21)+B(19)+B(17)+B(13)+
+          B(11)+B(10)+B(9)+B(8)+B(7)+B(4)+B(1)+B(0), 96),
+ kDefPoly(B(96)+B(94)+B(93)+B(91)+B(89)+B(87)+B(85)+B(83)+B(81)+B(78)+
+          B(76)+B(74)+B(73)+B(68)+B(67)+B(64), B(62)+B(61)+B(57)+B(55)+
+          B(54)+B(53)+B(49)+B(47)+B(41)+B(38)+B(35)+B(33)+B(28)+B(27)+
+          B(24)+B(23)+B(21)+B(19)+B(18)+B(17)+B(15)+B(13)+B(12)+B(11)+B(8)+
+          B(6)+B(4)+B(3)+B(1)+B(0), 97),
+ kDefPoly(B(97)+B(93)+B(92)+B(91)+B(90)+B(87)+B(83)+B(82)+B(80)+B(77)+
+          B(76)+B(75)+B(74)+B(73)+B(72)+B(70)+B(69)+B(68)+B(66)+B(65)+
+          B(64), B(63)+B(62)+B(61)+B(60)+B(59)+B(57)+B(55)+B(53)+B(50)+
+          B(49)+B(48)+B(45)+B(44)+B(43)+B(42)+B(40)+B(38)+B(36)+B(35)+
+          B(34)+B(28)+B(27)+B(24)+B(22)+B(21)+B(18)+B(17)+B(16)+B(15)+
+          B(14)+B(12)+B(11)+B(9)+B(8)+B(2)+B(1)+B(0), 98),
+ kDefPoly(B(96)+B(94)+B(92)+B(86)+B(85)+B(84)+B(78)+B(77)+B(76)+B(75)+
+          B(73)+B(71)+B(69)+B(68)+B(65), B(61)+B(59)+B(57)+B(56)+B(54)+
+          B(50)+B(47)+B(46)+B(44)+B(41)+B(38)+B(36)+B(35)+B(34)+B(33)+
+          B(32)+B(29)+B(27)+B(26)+B(25)+B(23)+B(22)+B(21)+B(19)+B(17)+
+          B(16)+B(11)+B(9)+B(7)+B(6)+B(3)+B(2)+B(0), 99),
+ kDefPoly(B(99)+B(96)+B(95)+B(93)+B(92)+B(88)+B(87)+B(83)+B(78)+B(77)+
+          B(76)+B(75)+B(74)+B(73)+B(70)+B(66)+B(64), B(63)+B(62)+B(60)+
+          B(59)+B(57)+B(56)+B(53)+B(50)+B(47)+B(41)+B(39)+B(38)+B(37)+
+          B(34)+B(25)+B(23)+B(21)+B(20)+B(19)+B(18)+B(17)+B(16)+B(13)+B(9)+
+          B(8)+B(6)+B(5)+B(1)+B(0), 100),
+ kDefPoly(B(100)+B(98)+B(97)+B(95)+B(93)+B(92)+B(91)+B(89)+B(87)+B(85)+
+          B(84)+B(82)+B(81)+B(80)+B(79)+B(76)+B(68)+B(66)+B(65), B(63)+
+          B(62)+B(59)+B(57)+B(52)+B(51)+B(50)+B(47)+B(46)+B(45)+B(42)+
+          B(41)+B(40)+B(39)+B(38)+B(37)+B(36)+B(34)+B(32)+B(31)+B(30)+
+          B(24)+B(22)+B(21)+B(20)+B(18)+B(17)+B(16)+B(14)+B(12)+B(11)+
+          B(10)+B(8)+B(7)+B(5)+B(4)+B(2)+B(1)+B(0), 101),
+ kDefPoly(B(101)+B(99)+B(97)+B(96)+B(92)+B(89)+B(88)+B(87)+B(86)+B(84)+
+          B(82)+B(81)+B(80)+B(78)+B(77)+B(76)+B(75)+B(74)+B(73)+
+          B(69), B(60)+B(59)+B(57)+B(56)+B(55)+B(54)+B(53)+B(51)+B(50)+
+          B(49)+B(47)+B(45)+B(43)+B(41)+B(35)+B(34)+B(32)+B(31)+B(29)+
+          B(27)+B(26)+B(25)+B(24)+B(21)+B(13)+B(12)+B(9)+B(8)+B(6)+B(5)+
+          B(3)+B(0), 102),
+ kDefPoly(B(101)+B(98)+B(97)+B(96)+B(94)+B(93)+B(92)+B(90)+B(89)+B(88)+
+          B(87)+B(85)+B(83)+B(81)+B(80)+B(79)+B(76)+B(75)+B(71)+B(70)+
+          B(69)+B(66), B(63)+B(62)+B(60)+B(59)+B(58)+B(56)+B(54)+B(53)+
+          B(48)+B(45)+B(43)+B(42)+B(41)+B(37)+B(36)+B(32)+B(31)+B(30)+
+          B(27)+B(25)+B(23)+B(22)+B(19)+B(16)+B(15)+B(11)+B(9)+B(5)+B(3)+
+          B(0), 103),
+ kDefPoly(B(98)+B(97)+B(95)+B(94)+B(91)+B(89)+B(88)+B(86)+B(85)+B(84)+
+          B(81)+B(79)+B(78)+B(76)+B(74)+B(73)+B(70)+B(69)+B(68)+B(67)+
+          B(66)+B(64), B(59)+B(53)+B(52)+B(51)+B(48)+B(46)+B(45)+B(43)+
+          B(37)+B(34)+B(33)+B(31)+B(30)+B(28)+B(25)+B(22)+B(21)+B(20)+
+          B(19)+B(14)+B(10)+B(8)+B(4)+B(2)+B(1)+B(0), 104),
+ kDefPoly(B(103)+B(100)+B(99)+B(98)+B(94)+B(90)+B(89)+B(86)+B(84)+B(82)+
+          B(79)+B(76)+B(74)+B(73)+B(72)+B(71)+B(70)+B(69)+B(67)+
+          B(66), B(63)+B(62)+B(59)+B(58)+B(57)+B(55)+B(51)+B(49)+B(48)+
+          B(47)+B(46)+B(43)+B(42)+B(38)+B(36)+B(34)+B(33)+B(31)+B(30)+
+          B(29)+B(28)+B(27)+B(24)+B(21)+B(20)+B(18)+B(17)+B(16)+B(14)+
+          B(13)+B(11)+B(9)+B(7)+B(6)+B(5)+B(0), 105),
+ kDefPoly(B(105)+B(104)+B(103)+B(102)+B(100)+B(98)+B(94)+B(93)+B(92)+B(91)+
+          B(90)+B(89)+B(87)+B(86)+B(85)+B(83)+B(82)+B(81)+B(79)+B(77)+
+          B(69)+B(68)+B(67)+B(64), B(61)+B(60)+B(59)+B(58)+B(56)+B(55)+
+          B(53)+B(50)+B(48)+B(44)+B(40)+B(38)+B(37)+B(36)+B(35)+B(34)+
+          B(33)+B(30)+B(29)+B(26)+B(22)+B(20)+B(13)+B(10)+B(8)+B(7)+B(5)+
+          B(0), 106),
+ kDefPoly(B(105)+B(101)+B(100)+B(98)+B(97)+B(96)+B(93)+B(92)+B(91)+B(90)+
+          B(87)+B(86)+B(81)+B(79)+B(77)+B(75)+B(74)+B(72)+B(68)+B(67)+
+          B(64), B(63)+B(62)+B(61)+B(60)+B(59)+B(58)+B(54)+B(53)+B(52)+
+          B(50)+B(48)+B(47)+B(45)+B(42)+B(41)+B(38)+B(32)+B(29)+B(27)+
+          B(26)+B(24)+B(21)+B(19)+B(18)+B(16)+B(15)+B(14)+B(13)+B(12)+
+          B(10)+B(7)+B(6)+B(4)+B(1)+B(0), 107),
+ kDefPoly(B(106)+B(105)+B(102)+B(100)+B(97)+B(95)+B(90)+B(89)+B(88)+B(86)+
+          B(83)+B(82)+B(81)+B(79)+B(78)+B(75)+B(72)+B(66)+B(64), B(63)+
+          B(62)+B(59)+B(58)+B(56)+B(54)+B(52)+B(51)+B(50)+B(48)+B(46)+
+          B(45)+B(44)+B(42)+B(40)+B(37)+B(36)+B(35)+B(33)+B(29)+B(27)+
+          B(22)+B(19)+B(17)+B(14)+B(12)+B(11)+B(10)+B(9)+B(8)+B(7)+B(6)+
+          B(5)+B(3)+B(0), 108),
+ kDefPoly(B(108)+B(102)+B(101)+B(100)+B(99)+B(98)+B(96)+B(95)+B(94)+B(90)+
+          B(89)+B(88)+B(87)+B(84)+B(83)+B(81)+B(80)+B(77)+B(76)+B(75)+
+          B(71)+B(67)+B(65), B(63)+B(61)+B(60)+B(54)+B(50)+B(49)+B(48)+
+          B(43)+B(40)+B(39)+B(38)+B(36)+B(34)+B(29)+B(28)+B(27)+B(22)+
+          B(21)+B(19)+B(16)+B(14)+B(13)+B(12)+B(10)+B(9)+B(7)+B(6)+B(5)+
+          B(3)+B(2)+B(0), 109),
+ kDefPoly(B(109)+B(108)+B(107)+B(102)+B(101)+B(98)+B(97)+B(96)+B(94)+B(92)+
+          B(91)+B(90)+B(88)+B(87)+B(85)+B(84)+B(83)+B(82)+B(81)+B(80)+
+          B(79)+B(78)+B(74)+B(73)+B(71)+B(70)+B(69)+B(66)+B(64), B(61)+
+          B(58)+B(57)+B(56)+B(50)+B(49)+B(46)+B(44)+B(43)+B(41)+B(36)+
+          B(35)+B(34)+B(30)+B(29)+B(26)+B(25)+B(24)+B(22)+B(21)+B(17)+
+          B(13)+B(11)+B(9)+B(4)+B(1)+B(0), 110),
+ kDefPoly(B(110)+B(109)+B(105)+B(98)+B(97)+B(95)+B(94)+B(93)+B(92)+B(90)+
+          B(88)+B(84)+B(83)+B(82)+B(80)+B(77)+B(75)+B(72)+B(71)+B(70)+
+          B(69)+B(66), B(63)+B(61)+B(60)+B(59)+B(57)+B(56)+B(55)+B(52)+
+          B(51)+B(50)+B(49)+B(47)+B(43)+B(40)+B(36)+B(35)+B(34)+B(33)+
+          B(31)+B(27)+B(26)+B(21)+B(20)+B(19)+B(17)+B(16)+B(12)+B(8)+B(6)+
+          B(4)+B(3)+B(2)+B(1)+B(0), 111),
+ kDefPoly(B(109)+B(107)+B(106)+B(104)+B(100)+B(98)+B(96)+B(95)+B(94)+B(92)+
+          B(91)+B(90)+B(89)+B(88)+B(86)+B(84)+B(81)+B(79)+B(78)+B(77)+
+          B(75)+B(73)+B(71)+B(70)+B(69)+B(67)+B(64), B(63)+B(62)+B(61)+
+          B(60)+B(58)+B(56)+B(54)+B(52)+B(51)+B(49)+B(48)+B(45)+B(44)+
+          B(39)+B(38)+B(37)+B(36)+B(35)+B(34)+B(32)+B(30)+B(26)+B(25)+
+          B(24)+B(23)+B(22)+B(21)+B(19)+B(16)+B(15)+B(11)+B(10)+B(9)+B(8)+
+          B(3)+B(1)+B(0), 112),
+ kDefPoly(B(111)+B(107)+B(102)+B(100)+B(99)+B(98)+B(97)+B(96)+B(95)+B(94)+
+          B(93)+B(92)+B(87)+B(86)+B(82)+B(81)+B(80)+B(79)+B(77)+B(76)+
+          B(75)+B(72)+B(69)+B(64), B(61)+B(58)+B(56)+B(54)+B(53)+B(52)+
+          B(51)+B(49)+B(46)+B(43)+B(40)+B(39)+B(37)+B(36)+B(35)+B(34)+
+          B(33)+B(31)+B(29)+B(24)+B(22)+B(21)+B(20)+B(15)+B(14)+B(12)+
+          B(10)+B(6)+B(1)+B(0), 113),
+ kDefPoly(B(112)+B(111)+B(110)+B(104)+B(102)+B(101)+B(100)+B(92)+B(89)+
+          B(87)+B(83)+B(82)+B(80)+B(79)+B(75)+B(74)+B(73)+B(72)+B(71)+
+          B(70)+B(68)+B(67)+B(65), B(60)+B(59)+B(57)+B(56)+B(55)+B(52)+
+          B(50)+B(47)+B(44)+B(41)+B(36)+B(35)+B(30)+B(29)+B(26)+B(25)+
+          B(24)+B(21)+B(18)+B(17)+B(16)+B(14)+B(12)+B(10)+B(7)+B(6)+
+          B(0), 114),
+ kDefPoly(B(114)+B(112)+B(111)+B(110)+B(108)+B(107)+B(103)+B(102)+B(98)+
+          B(97)+B(96)+B(90)+B(88)+B(87)+B(86)+B(83)+B(82)+B(80)+B(79)+
+          B(77)+B(75)+B(70)+B(66)+B(65)+B(64), B(61)+B(60)+B(59)+B(58)+
+          B(57)+B(53)+B(52)+B(51)+B(50)+B(47)+B(45)+B(43)+B(39)+B(38)+
+          B(33)+B(32)+B(31)+B(29)+B(27)+B(21)+B(17)+B(14)+B(12)+B(10)+B(7)+
+          B(4)+B(2)+B(1)+B(0), 115),
+ kDefPoly(B(113)+B(110)+B(108)+B(106)+B(105)+B(102)+B(101)+B(100)+B(98)+
+          B(96)+B(92)+B(89)+B(87)+B(86)+B(84)+B(81)+B(79)+B(78)+B(76)+
+          B(75)+B(73)+B(72)+B(71)+B(70)+B(67)+B(64), B(63)+B(62)+B(61)+
+          B(52)+B(47)+B(45)+B(44)+B(42)+B(40)+B(39)+B(35)+B(34)+B(33)+
+          B(31)+B(29)+B(25)+B(18)+B(15)+B(14)+B(10)+B(8)+B(6)+B(1)+
+          B(0), 116),
+ kDefPoly(B(113)+B(111)+B(110)+B(109)+B(107)+B(106)+B(103)+B(102)+B(100)+
+          B(96)+B(95)+B(94)+B(91)+B(90)+B(89)+B(86)+B(82)+B(81)+B(78)+
+          B(77)+B(76)+B(75)+B(74)+B(73)+B(70)+B(67)+B(66), B(63)+B(61)+
+          B(59)+B(57)+B(56)+B(55)+B(53)+B(52)+B(51)+B(50)+B(47)+B(45)+
+          B(42)+B(40)+B(37)+B(35)+B(32)+B(30)+B(29)+B(25)+B(22)+B(21)+
+          B(20)+B(19)+B(16)+B(15)+B(14)+B(12)+B(8)+B(5)+B(0), 117),
+ kDefPoly(B(117)+B(113)+B(110)+B(108)+B(105)+B(104)+B(103)+B(102)+B(99)+
+          B(98)+B(97)+B(94)+B(93)+B(91)+B(90)+B(89)+B(85)+B(84)+B(82)+
+          B(81)+B(79)+B(78)+B(77)+B(74)+B(73)+B(69)+B(67)+B(64), B(63)+
+          B(62)+B(61)+B(57)+B(55)+B(51)+B(50)+B(46)+B(45)+B(43)+B(42)+
+          B(41)+B(37)+B(33)+B(32)+B(30)+B(27)+B(26)+B(21)+B(19)+B(18)+
+          B(17)+B(15)+B(14)+B(12)+B(10)+B(8)+B(7)+B(3)+B(2)+B(1)+
+          B(0), 118),
+ kDefPoly(B(118)+B(111)+B(109)+B(107)+B(106)+B(105)+B(104)+B(101)+B(99)+
+          B(98)+B(97)+B(94)+B(92)+B(91)+B(89)+B(83)+B(82)+B(80)+B(79)+
+          B(67)+B(66), B(62)+B(61)+B(60)+B(58)+B(57)+B(52)+B(48)+B(46)+
+          B(44)+B(42)+B(40)+B(39)+B(38)+B(36)+B(34)+B(33)+B(32)+B(29)+
+          B(23)+B(22)+B(20)+B(19)+B(18)+B(15)+B(13)+B(12)+B(11)+B(6)+B(5)+
+          B(4)+B(3)+B(1)+B(0), 119),
+ kDefPoly(B(116)+B(115)+B(113)+B(112)+B(110)+B(107)+B(106)+B(104)+B(103)+
+          B(101)+B(100)+B(99)+B(98)+B(90)+B(89)+B(88)+B(87)+B(82)+B(80)+
+          B(79)+B(77)+B(76)+B(75)+B(74)+B(73)+B(71)+B(70)+B(68)+B(65)+
+          B(64), B(63)+B(62)+B(59)+B(55)+B(54)+B(48)+B(47)+B(45)+B(44)+
+          B(40)+B(39)+B(38)+B(35)+B(33)+B(29)+B(27)+B(26)+B(25)+B(24)+
+          B(23)+B(22)+B(21)+B(18)+B(17)+B(15)+B(13)+B(12)+B(10)+B(8)+B(3)+
+          B(2)+B(0), 120),
+ kDefPoly(B(118)+B(117)+B(114)+B(113)+B(112)+B(110)+B(109)+B(104)+B(103)+
+          B(101)+B(99)+B(97)+B(96)+B(95)+B(93)+B(92)+B(91)+B(90)+B(89)+
+          B(87)+B(85)+B(84)+B(82)+B(81)+B(79)+B(73)+B(72)+B(68)+B(67)+
+          B(66)+B(64), B(60)+B(58)+B(57)+B(56)+B(54)+B(53)+B(52)+B(51)+
+          B(49)+B(48)+B(47)+B(45)+B(44)+B(38)+B(37)+B(36)+B(35)+B(33)+
+          B(32)+B(31)+B(30)+B(27)+B(26)+B(24)+B(23)+B(22)+B(20)+B(19)+
+          B(18)+B(16)+B(15)+B(12)+B(6)+B(5)+B(4)+B(2)+B(0), 121),
+ kDefPoly(B(121)+B(118)+B(114)+B(112)+B(109)+B(106)+B(103)+B(102)+B(101)+
+          B(100)+B(97)+B(95)+B(90)+B(89)+B(87)+B(83)+B(81)+B(80)+B(79)+
+          B(78)+B(77)+B(76)+B(75)+B(74)+B(72)+B(71)+B(70)+B(69)+B(68)+
+          B(66)+B(64), B(61)+B(57)+B(51)+B(50)+B(47)+B(46)+B(43)+B(39)+
+          B(38)+B(37)+B(36)+B(34)+B(33)+B(32)+B(30)+B(28)+B(27)+B(24)+
+          B(22)+B(20)+B(18)+B(17)+B(14)+B(12)+B(11)+B(9)+B(7)+B(2)+
+          B(0), 122),
+ kDefPoly(B(122)+B(121)+B(120)+B(119)+B(118)+B(117)+B(116)+B(113)+B(112)+
+          B(111)+B(109)+B(106)+B(105)+B(103)+B(100)+B(98)+B(97)+B(95)+
+          B(93)+B(92)+B(90)+B(87)+B(86)+B(85)+B(83)+B(81)+B(78)+B(77)+
+          B(75)+B(74)+B(73)+B(72)+B(71)+B(70)+B(69)+B(68)+B(67)+B(65)+
+          B(64), B(63)+B(62)+B(60)+B(55)+B(52)+B(51)+B(49)+B(47)+B(45)+
+          B(43)+B(42)+B(41)+B(37)+B(36)+B(35)+B(34)+B(32)+B(28)+B(27)+
+          B(26)+B(24)+B(23)+B(21)+B(20)+B(16)+B(13)+B(10)+B(9)+B(8)+B(7)+
+          B(5)+B(2)+B(0), 123),
+ kDefPoly(B(123)+B(121)+B(120)+B(118)+B(117)+B(116)+B(115)+B(112)+B(111)+
+          B(110)+B(109)+B(107)+B(104)+B(102)+B(101)+B(100)+B(99)+B(98)+
+          B(97)+B(94)+B(90)+B(87)+B(86)+B(84)+B(83)+B(82)+B(79)+B(75)+
+          B(72)+B(71)+B(70)+B(64), B(63)+B(56)+B(54)+B(51)+B(50)+B(47)+
+          B(45)+B(44)+B(42)+B(39)+B(38)+B(36)+B(34)+B(33)+B(29)+B(26)+
+          B(24)+B(20)+B(16)+B(14)+B(11)+B(10)+B(8)+B(7)+B(6)+B(4)+B(2)+
+          B(0), 124),
+ kDefPoly(B(124)+B(123)+B(121)+B(119)+B(118)+B(116)+B(115)+B(114)+B(107)+
+          B(105)+B(104)+B(103)+B(102)+B(99)+B(98)+B(96)+B(94)+B(93)+B(89)+
+          B(83)+B(82)+B(81)+B(80)+B(79)+B(78)+B(75)+B(74)+B(73)+B(72)+
+          B(70)+B(69)+B(68)+B(64), B(63)+B(59)+B(56)+B(55)+B(52)+B(51)+
+          B(50)+B(49)+B(48)+B(44)+B(42)+B(38)+B(37)+B(36)+B(33)+B(31)+
+          B(29)+B(27)+B(26)+B(25)+B(23)+B(21)+B(19)+B(18)+B(16)+B(14)+
+          B(11)+B(8)+B(7)+B(6)+B(4)+B(1)+B(0), 125),
+ kDefPoly(B(124)+B(122)+B(121)+B(120)+B(119)+B(117)+B(113)+B(110)+B(108)+
+          B(105)+B(103)+B(102)+B(101)+B(97)+B(93)+B(91)+B(90)+B(88)+B(86)+
+          B(84)+B(82)+B(81)+B(79)+B(77)+B(76)+B(75)+B(73)+B(72)+B(71)+
+          B(69)+B(67)+B(64), B(63)+B(62)+B(61)+B(60)+B(58)+B(56)+B(55)+
+          B(52)+B(51)+B(48)+B(47)+B(45)+B(44)+B(42)+B(41)+B(40)+B(39)+
+          B(37)+B(33)+B(32)+B(30)+B(29)+B(28)+B(27)+B(26)+B(25)+B(24)+
+          B(23)+B(19)+B(18)+B(17)+B(16)+B(14)+B(13)+B(11)+B(9)+B(8)+B(7)+
+          B(4)+B(2)+B(1)+B(0), 126),
+ kDefPoly(B(125)+B(124)+B(121)+B(116)+B(115)+B(105)+B(103)+B(101)+B(94)+
+          B(93)+B(91)+B(90)+B(88)+B(87)+B(86)+B(85)+B(77)+B(73)+B(72)+
+          B(70)+B(68)+B(67), B(63)+B(62)+B(61)+B(59)+B(57)+B(53)+B(52)+
+          B(51)+B(49)+B(48)+B(46)+B(44)+B(41)+B(39)+B(38)+B(36)+B(35)+
+          B(30)+B(27)+B(25)+B(23)+B(20)+B(19)+B(13)+B(12)+B(11)+B(10)+B(8)+
+          B(7)+B(5)+B(4)+B(3)+B(2)+B(0), 127),
+ kDefPoly(B(127)+B(122)+B(121)+B(118)+B(117)+B(116)+B(109)+B(108)+B(107)+
+          B(106)+B(104)+B(103)+B(102)+B(101)+B(96)+B(93)+B(92)+B(91)+B(89)+
+          B(86)+B(85)+B(80)+B(78)+B(77)+B(76)+B(75)+B(74)+B(73)+B(72)+
+          B(71)+B(66), B(60)+B(56)+B(53)+B(52)+B(50)+B(47)+B(45)+B(41)+
+          B(39)+B(38)+B(37)+B(35)+B(34)+B(33)+B(30)+B(28)+B(25)+B(24)+
+          B(23)+B(21)+B(20)+B(19)+B(14)+B(13)+B(10)+B(8)+B(5)+B(4)+B(2)+
+          B(1)+B(0), 128),
+};
+// lint -restore
+
+// The number of polynomials in POLYS[].
+SELECTANY const int CRC::N_POLYS = sizeof (poly_list) / sizeof (poly_list[0]);
+
+// The externally visible name of poly_list.
+// This guarantees that the size of poly_list is opaque.
+SELECTANY const struct CRC::Poly *const CRC::POLYS = poly_list;
+
+// The "constructor" for a CRC with an default polynomial.
+CRC *CRC::Default(int degree, size_t roll_length) {
+  ASSERT1(32 == degree);
+
+  CRC *crc = CRCImpl::NewInternal(CRC::POLYS[degree].lo, CRC::POLYS[degree].hi,
+                                  degree, roll_length);  // Build the table
+  return crc;
+}
+
+// The "constructor" for a CRC with an arbitrary polynomial.
+CRC *CRC::New(uint64 lo, uint64 hi, int degree, size_t roll_length) {
+  return CRCImpl::NewInternal(lo, hi, degree, roll_length);
+}
+
+// Internal version of the "constructor".
+CRCImpl *CRCImpl::NewInternal(uint64 lo, uint64 hi,
+                             int degree, size_t roll_length) {
+  ASSERT1(8 <= degree && degree <= 64);  // precondition
+  ASSERT1(lo != 0 || hi != 0);            // precondition
+  // Generate the tables for extending a CRC by 4 bytes at a time.
+  // Why 4 and not 8?  Because Pentium 4 has such small caches.
+  struct CRC_pair t[4][256];
+  for (int j = 0; j != 4; j++) {      // for each byte of extension....
+    t[j][0].lo = 0;                   // a zero has no effect
+    t[j][0].hi = 0;
+    for (int i = 128; i != 0;  i >>= 1) {  // fill in entries for powers of 2
+      if (j == 0 && i == 128) {
+        t[j][i].lo = lo;  // top bit in first byte is easy---it's the polynomial
+        t[j][i].hi = hi;
+      } else {
+                  // each successive power of two is derive from the previous
+                  // one, either in this table, or the last table
+        struct CRC_pair pred;
+        if (i == 128) {
+          pred = t[j-1][1];
+        } else {
+          pred = t[j][i << 1];
+        }
+        // Advance the CRC by one bit (multiply by X, and take remainder
+        // through one step of polynomial long division)
+        if (pred.lo & 1) {
+          t[j][i].lo = (pred.lo >> 1) ^ (pred.hi << 63) ^ lo;
+          t[j][i].hi = (pred.hi >> 1) ^ hi;
+        } else {
+          t[j][i].lo = (pred.lo >> 1) ^ (pred.hi << 63);
+          t[j][i].hi = pred.hi >> 1;
+        }
+      }
+    }
+    // CRCs have the property that CRC(a xor b) == CRC(a) xor CRC(b)
+    // so we can make all the tables for non-powers of two by
+    // xoring previously created entries.
+    for (int i = 2; i != 256;  i <<= 1) {
+      for (int k = i+1; k != (i << 1); k++) {
+        t[j][k].lo = t[j][i].lo ^ t[j][k-i].lo;
+        t[j][k].hi = t[j][i].hi ^ t[j][k-i].hi;
+      }
+    }
+  }
+
+  // Copy the newly built tables in t[] into an appropriate
+  // CRC implenentation object.
+  CRCImpl *result = 0;
+  CRC32 *crc32 = 0;
+  crc32 = new CRC32();
+  for (int i = 0; i != 256; i++) {
+    crc32->table0_[i] = static_cast<uint32>(t[0][i].lo);
+    crc32->table1_[i] = static_cast<uint32>(t[1][i].lo);
+    crc32->table2_[i] = static_cast<uint32>(t[2][i].lo);
+    crc32->table3_[i] = static_cast<uint32>(t[3][i].lo);
+  }
+  result = crc32;
+
+  // "result" is now a CRC object of the right type to handle
+  // the polynomial of the right degree.
+
+  result->roll_length_ = roll_length;
+  result->degree_ = degree;
+  result->poly_lo_ = lo;
+  result->poly_hi_ = hi;
+
+  // Build the table for extending by zeroes.
+  // Entry i=a-1+3*b (a in {1, 2, 3}, b in {0, 1, 2, 3, ...}
+  // contains a polynomial Pi such that multiplying
+  // a CRC by Pi mod P, where P is the CRC polynomial, is equivalent to
+  // appending a*2**(2*b+SMALL_BITS) zero bytes to the original string.
+  // Entry is generated by calling ExtendByZeroes() twice using
+  // half the length from the previous entry.
+  int pos = 0;
+  for (uint64 inc_len = (1 << SMALL_BITS); inc_len != 0; inc_len <<= 2) {
+    result->Empty(&lo, &hi);
+    for (int k = 0; k != 3; k++) {
+      result->ExtendByZeroes(&lo, &hi, (size_t) (inc_len >> 1));
+      result->ExtendByZeroes(&lo, &hi, (size_t) (inc_len >> 1));
+      crc32->zeroes_[pos] = static_cast<uint32>(lo);
+      pos++;
+    }
+  }
+
+  // Calculate the entries in the roll table, used for rolling checksums
+  // of a fixed length.
+  // Extend the powers of two in the one-byte extension table by the roll
+  // length.
+  int bit = 256;
+  do {
+    bit >>= 1;
+    result->ExtendByZeroes(&t[0][bit].lo, &t[0][bit].hi, roll_length);
+  } while (bit != 0);
+  // Calculate the non-powers of two using CRC(a xor b) == CRC(a) xor CRC(b)
+  for (int i = 2; i != 256;  i <<= 1) {
+    for (int j = i+1; j != (i << 1); j++) {
+      t[0][j].lo = t[0][i].lo ^ t[0][j-i].lo;
+      t[0][j].hi = t[0][i].hi ^ t[0][j-i].hi;
+    }
+  }
+  // Now xor the CRC of (binary) 100000001 followed by
+  // the roll length of zeroes.   This will be xored into every
+  // entry.   This will simultaneously roll out the CRC
+  // of the empty string that's been pushed one byte too far,
+  // and roll in the CRC of the empty string in the correct place again.
+  result->Empty(&lo, &hi);
+  const uint8 x = 0x80;
+  result->Extend(&lo, &hi, &x, 1);
+  result->ExtendByZeroes(&lo, &hi, roll_length);
+  for (int i = 0; i != 256; i++) {
+    t[0][i].lo ^= lo;
+    t[0][i].hi ^= hi;
+  }
+
+  // Put the roll table into the object.
+  for (int i = 0; i != 256; i++) {
+    crc32->roll_[i] = static_cast<uint32>(t[0][i].lo);
+  }
+
+  return result;
+}
+
+// The CRC of the empty string is always the CRC polynomial itself.
+void CRCImpl::Empty(uint64 *lo, uint64 *hi) const {
+  ASSERT1(hi);
+  ASSERT1(lo);
+
+  *lo = this->poly_lo_;
+  *hi = this->poly_hi_;
+}
+
+//  The 32-bit implementation
+
+void CRC32::Extend(uint64 *lo, uint64 *hi, const void *bytes, size_t length)
+                      const {
+  ASSERT1(hi);
+  ASSERT1(lo);
+
+  hi;   // unreferenced formal parameter
+
+  const uint8 *p = static_cast<const uint8 *>(bytes);
+  const uint8 *e = p + length;
+  uint32 l = static_cast<uint32>(*lo);
+  // point x at MIN(first 4-byte aligned byte in string, end of string)
+  const uint8 *x = p + ((zero_ptr - p) & 3);
+  if (x > e) {
+    x = e;
+  }
+  // Process bytes until finished or p is 4-byte aligned
+  while (p != x) {
+    int c = (l & 0xff) ^ *p++;
+    l = this->table0_[c] ^ (l >> 8);
+  }
+  // point x at MIN(last 4-byte aligned byte in string, end of string)
+  x = e - ((e - zero_ptr) & 3);
+  // Process bytes 4 at a time
+  while (p < x) {
+    uint32 c = l ^ *reinterpret_cast<const uint32*>(p);
+    p += 4;
+    l = this->table3_[c & 0xff] ^
+        this->table2_[(c >> 8) & 0xff] ^
+        this->table1_[(c >> 16) & 0xff] ^
+        this->table0_[c >> 24];
+  }
+
+  // Process the last few bytes
+  while (p != e) {
+    int c = (l & 0xff) ^ *p++;
+    l = this->table0_[c] ^ (l >> 8);
+  }
+  *lo = l;
+}
+
+void CRC32::ExtendByZeroes(uint64 *lo, uint64 *hi, size_t length) const {
+  ASSERT1(hi);
+  ASSERT1(lo);
+
+  // Process the low order SMALL_BITS of the length by simply
+  // using Extend() on an array of bytes that are zero.
+  int small_part = (length & ((1 << SMALL_BITS)-1));
+  if (small_part != 0) {
+    this->Extend(lo, hi, zeroes, small_part);
+  }
+  length >>= SMALL_BITS;
+  if (length != 0) {          // if the length was at least 2**SMALL_BITS
+    uint32 l = static_cast<uint32>(*lo);
+    uint32 onebit = 1;
+    onebit <<= this->degree_ - 1;
+    // For each pair of bits in length
+    // (after the low-oder bits have been removed)
+    // we lookup the appropriate polynomial in the zeroes_ array
+    // and do a polynomial long multiplication (mod the CRC polynomial)
+    // to extend the CRC by the appropriate number of bits.
+    for (int i = 0; length != 0; i += 3, length >>= 2) {
+      int c = length & 3;       // pick next two bits
+      if (c != 0) {             // if they are not zero,
+                                // multiply by entry in table
+        uint32 m = this->zeroes_[c+i-1];
+        uint32 result = 0;
+        for (uint32 one = onebit; one != 0; one >>= 1) {
+          if ((l & one) != 0) {
+            result ^= m;
+          }
+          if (m & 1) {
+            m = (m >> 1) ^ static_cast<uint32>(poly_lo_);
+          } else {
+            m = (m >> 1);
+          }
+        }
+        l = result;
+      }
+    }
+    *lo = l;
+  }
+}
+
+void CRC32::Roll(uint64 *lo, uint64 *hi, uint8 o_byte, uint8 i_byte) const {
+  ASSERT1(hi);
+  ASSERT1(lo);
+
+  hi;   // unreferenced formal parameter
+
+  uint32 l = static_cast<uint32>(*lo);
+  // Roll in i_byte and out o_byte
+  *lo = this->table0_[(l & 0xff) ^ i_byte] ^ (l >> 8) ^ this->roll_[o_byte];
+}
+
+}  // namespace omaha
+
diff --git a/common/crc.h b/base/crc.h
similarity index 100%
rename from common/crc.h
rename to base/crc.h
diff --git a/base/debug.cc b/base/debug.cc
new file mode 100644
index 0000000..b0b53bd
--- /dev/null
+++ b/base/debug.cc
@@ -0,0 +1,1195 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Debug functions
+
+#include "omaha/base/debug.h"
+
+#include <dbghelp.h>
+#include <wtsapi32.h>
+#include <atlstr.h>
+#ifdef _DEBUG
+#include <atlcom.h>
+#define STRSAFE_NO_DEPRECATE
+#include <strsafe.h>
+#endif
+#include <stdlib.h>
+#include <signal.h>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/clipboard.h"
+#include "omaha/base/commontypes.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/const_config.h"
+#include "omaha/base/const_debug.h"
+#include "omaha/base/const_timeouts.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/module_utils.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/string.h"
+#include "omaha/base/system.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/time.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/base/vista_utils.h"
+
+namespace omaha {
+
+#ifdef _DEBUG
+#define kSprintfBuffers (100)  // number of buffers for SPRINTF
+#else
+#define kSprintfBuffers (3)  // number of buffers for SPRINTF
+#endif
+
+// pad SPRINTF buffer to check for overruns
+#if SHIPPING
+#define kSprintfBufferOverrunPadding 0
+#else  // !SHIPPING
+#ifdef DEBUG
+#define kSprintfBufferOverrunPadding 20000
+#else
+#define kSprintfBufferOverrunPadding 1024
+#endif  // DEBUG
+#endif  // SHIPPING
+
+#ifdef _DEBUG
+const TCHAR* const kErrorRequestToSendFormat =
+    _T("*** Please hit Ignore to continue and send error information to the ")
+    _T("%s team ***\n*** These details have been pasted to the clipboard ***");
+
+// Max length of report summary string.
+const int kMaxReportSummaryLen = 1024 * 100;
+#endif  // DEBUG
+
+#define kReportIdsLock kLockPrefix                                             \
+    _T("Report_Ids_Lock_57146B01-6A07-4b8d-A1D8-0C3AFC3B2F9B")
+
+SELECTANY bool g_always_assert = false;
+SELECTANY TCHAR *g_additional_status_ping_info = NULL;
+
+#define kSprintfMaxLen (1024 + 2)  // max length that wvsprintf writes is 1024
+static bool g_initialized_sprintf = false;
+static volatile LONG g_sprintf_interlock = 0;
+static int g_current_sprintf_buffer = 0;
+static TCHAR *g_sprintf_buffer = NULL;
+static TCHAR *g_sprintf_buffers[kSprintfBuffers];
+SELECTANY volatile LONG g_debugassertrecursioncheck = 0;
+static int g_total_reports = 0;
+
+SELECTANY ReportIds g_report_ids;
+
+// Builds a full path name out of the given filename. If the filename is
+// a relative path, it is appended to the debug directory. Otherwise, if the
+// filename is a full path, it returns it as the full debug filename.
+static CString MakeFullDebugFilename(const TCHAR *filename) {
+  CString full_name;
+  if (lstrlen(filename) <= 2 || filename[1] != _T(':')) {
+    full_name = GetDebugDirectory();
+    full_name += L"\\";
+  }
+  full_name += filename;
+  return full_name;
+}
+
+
+// Displays the assert box. Due to session isolation, MB_SERVICE_NOTIFICATION
+// flag does not work for Vista services. In this case, use WTS to display
+// a message box in the active console session.
+void ShowAssertDialog(const TCHAR *message, const TCHAR *title) {
+  int ret = 0;
+  OSVERSIONINFOEX osviex = {sizeof(OSVERSIONINFOEX), 0};
+  const bool is_vista_or_greater =
+     ::GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&osviex)) &&
+     osviex.dwMajorVersion >= 6;
+  bool is_system_process = false;
+  if (is_vista_or_greater &&
+      SUCCEEDED(IsSystemProcess(&is_system_process)) &&
+      is_system_process) {
+    DWORD session_id = System::WTSGetActiveConsoleSessionId();
+    if (session_id == kInvalidSessionId) {
+      session_id = WTS_CURRENT_SESSION;
+    }
+    DWORD response = 0;
+    ::WTSSendMessage(WTS_CURRENT_SERVER_HANDLE,
+                     session_id,
+                     const_cast<TCHAR*>(title),
+                     _tcslen(title) * sizeof(TCHAR),
+                     const_cast<TCHAR*>(message),
+                     _tcslen(message) * sizeof(TCHAR),
+                     MB_ABORTRETRYIGNORE | MB_ICONERROR,
+                     0,
+                     &response,
+                     true);
+    ret = response;
+  } else {
+    ret = ::MessageBoxW(NULL,
+                        message,
+                        title,
+                        MB_ABORTRETRYIGNORE |
+                        MB_ICONERROR        |
+                        MB_SERVICE_NOTIFICATION);
+  }
+
+  switch (ret) {
+    case IDABORT:
+      // Terminate the process if the user chose 'Abort'. Calling ExitProcess
+      // here results in calling the destructors for static objects which can
+      // result in deadlocks.
+      raise(SIGABRT);
+      break;
+
+    case IDRETRY:
+      // Break if the user chose "Retry".
+      __debugbreak();
+      break;
+    default:
+      // By default we ignore the message.
+      break;
+  }
+}
+
+DebugObserver* g_debug_observer = NULL;
+
+// replaces the debug observer, returns the previous value.
+DebugObserver* SetDebugObserver(DebugObserver* observer) {
+  DebugObserver* old_value = g_debug_observer;
+  g_debug_observer = observer;
+  return old_value;
+}
+
+DebugObserver* PeekDebugObserver() {
+  return g_debug_observer;
+}
+
+int SehSendMinidump(unsigned int code,
+                    struct _EXCEPTION_POINTERS *ep,
+                    time64 time_between_minidumps) {
+    if (code == EXCEPTION_BREAKPOINT)
+    return EXCEPTION_CONTINUE_SEARCH;
+
+  if (::IsDebuggerPresent())
+    return EXCEPTION_CONTINUE_SEARCH;
+
+  OutputDebugString(L"**SehSendMinidump**\r\n");
+
+  if (g_debug_observer) {
+    return g_debug_observer->SehSendMinidump(code, ep, time_between_minidumps);
+  }
+
+  return EXCEPTION_EXECUTE_HANDLER;
+}
+
+#if defined(_DEBUG) || defined(ASSERT_IN_RELEASE)
+CallInterceptor<DebugAssertFunctionType> debug_assert_interceptor;
+
+// Replaces the debug assert function; returns the old value.
+DebugAssertFunctionType* ReplaceDebugAssertFunction(
+    DebugAssertFunctionType* replacement) {
+  return debug_assert_interceptor.ReplaceFunction(replacement);
+}
+
+void OnAssert(const char *expr, const TCHAR *msg,
+              const char *filename, int32 linenumber) {
+  if (g_debug_observer) {
+    g_debug_observer->OnAssert(expr, msg, filename, linenumber);
+  }
+}
+#endif
+
+#if defined(_DEBUG)
+CString OnDebugReport(uint32 id,
+                      bool is_report,
+                      ReportType type,
+                      const char *expr,
+                      const TCHAR *message,
+                      const char *filename,
+                      int32 linenumber,
+                      DebugReportKind debug_report_kind) {
+  CString trace;
+  if (g_debug_observer) {
+    trace = g_debug_observer->OnDebugReport(id, is_report,
+                                            type, expr, message, filename,
+                                            linenumber, debug_report_kind);
+  }
+  return trace;
+}
+
+void SendExceptionReport(const TCHAR *log_file, const TCHAR *filename, int line,
+                         const TCHAR *type, uint32 id, bool offline) {
+  if (g_debug_observer) {
+    g_debug_observer->SendExceptionReport(log_file, filename, line,
+                                          type, id, offline);
+  }
+}
+#endif
+
+
+#ifdef _DEBUG  // won't compile since _CrtDbgReport isn't defined.
+
+#include <crtdbg.h>    // NOLINT
+static CString g_report_summary;
+
+// dump summary of reports on exit
+SELECTANY ReportSummaryGenerator g_report_summary_generator;
+
+ReportSummaryGenerator::~ReportSummaryGenerator() {
+  DumpReportSummary();
+}
+
+void ReportSummaryGenerator::DumpReportSummary() {
+  if (g_total_reports) {
+    ::OutputDebugString(L"REPORT SUMMARY:\r\n");
+    ::OutputDebugString(SPRINTF(L"%d total reports\r\n", g_total_reports));
+    ::OutputDebugString(g_report_summary);
+  } else {
+    ::OutputDebugString(L"NO REPORTS!!\r\n");
+  }
+}
+
+TCHAR *ReportSummaryGenerator::GetReportSummary() {
+  TCHAR *s = new TCHAR[kMaxReportSummaryLen];
+  if (s) {
+    s[0] = 0;
+    if (g_total_reports) {
+      SafeStrCat(s, L"REPORT SUMMARY:\r\n\r\n", kMaxReportSummaryLen);
+      SafeStrCat(s,
+                 SPRINTF(L"%d total reports\r\n\r\n", g_total_reports),
+                 kMaxReportSummaryLen);
+      SafeStrCat(s,
+                 g_report_summary.
+                     Left(kMaxReportSummaryLen - lstrlen(s) - 1).GetString(),
+                 kMaxReportSummaryLen);
+      CString report_string = g_report_ids.DebugReportString();
+      ReplaceCString(report_string, L"&", L"\r\n");
+      SafeStrCat(s,
+                 report_string.
+                     Left(kMaxReportSummaryLen - lstrlen(s) - 1).GetString(),
+                 kMaxReportSummaryLen);
+    } else {
+      SafeStrCat(s, L"NO REPORTS!!\r\n", kMaxReportSummaryLen);
+    }
+  }
+
+  return s;
+}
+
+static CAtlMap<CString, uint32> g_reports_done;
+
+#endif  // _DEBUG
+
+#ifdef _DEBUG
+
+void TraceError(DWORD error) {
+  HLOCAL mem = NULL;
+  ::FormatMessage(
+    FORMAT_MESSAGE_ALLOCATE_BUFFER |
+    FORMAT_MESSAGE_FROM_SYSTEM |
+    FORMAT_MESSAGE_IGNORE_INSERTS,
+    static_cast<LPVOID>(_AtlBaseModule.GetResourceInstance()),
+    error,
+    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),  // default language
+    reinterpret_cast<TCHAR*>(&mem),
+    0,
+    NULL);
+
+  TCHAR* str = reinterpret_cast<TCHAR*>(::LocalLock(mem));
+  ::OutputDebugString(str);
+  REPORT(false, R_ERROR, (str), 3968294226);
+  ::LocalFree(mem);
+}
+
+// ban ASSERT/VERIFY/REPORT to prevent recursion
+#undef ASSERT
+#undef VERIFY
+#undef REPORT
+
+// TODO(omaha): fix static initialization order below.
+// The initialization order of static variables is not deterministic per C++
+// standard and it depends completely on the compiler implementation.
+// For VC++ compiler we are using, it seems to work as expected.
+// One real fix is to put all definitons of static variables inside a class and
+// define a boolean variable in this class to indicate that all necessary
+// static initializations have been done.
+//
+// The follow definition is used to detect whether we get the exception
+// during initializing static variables. If this is the case, DebugReport()
+// will not function and will throw an exception because some of its refering
+// static variables are not initialized yet (i.e. g_reports_done).
+const int kTestInitStaticVariablesDoneValue = 1234;
+struct TestInitStaticVariablesDone {
+  int value;
+  TestInitStaticVariablesDone() : value(kTestInitStaticVariablesDoneValue) {}
+};
+static TestInitStaticVariablesDone test_var;
+
+bool DebugReport(unsigned int id,
+                 ReportType type,
+                 const char *expr,
+                 const TCHAR *message,
+                 const char *filename,
+                 int linenumber,
+                 DebugReportKind debug_report_kind) {
+  int recursion_count = ::InterlockedIncrement(&g_debugassertrecursioncheck);
+  ON_SCOPE_EXIT(::InterlockedDecrement, &g_debugassertrecursioncheck);
+  if (recursion_count > 1) {
+    ::OutputDebugString(_T("recursive debugreport skipped\n"));
+    return 1;
+  }
+
+  if (debug_assert_interceptor.interceptor()) {
+    // call replacement function (typically used for unit tests)
+    // Note that I'm doing this inside the in_assert block for paranoia;
+    // it's not really necessary and perhaps the wrong choice.
+    debug_assert_interceptor.interceptor()(expr, CT2A(message), filename,
+                                            linenumber);
+    return true;
+  }
+
+
+  // Check whether we have already finished initializing all static variables
+  // needed for executing DebugReport(). If not, bail out.
+  if (test_var.value != kTestInitStaticVariablesDoneValue) {
+    CString debug_msg;
+    SafeCStringFormat(&debug_msg, _T("%hs:%d - %s - %S"),
+                      filename, linenumber, message, expr);
+    debug_msg.Append(_T("\n\nException occurs while initializing ")
+                     _T("static variables needed for DebugReport"));
+    ShowAssertDialog(debug_msg, _T("DebugReport"));
+    return true;
+  }
+
+  bool is_assert = debug_report_kind == DEBUGREPORT_ASSERT;
+  bool is_report = debug_report_kind == DEBUGREPORT_REPORT;
+  bool is_abort  = debug_report_kind == DEBUGREPORT_ABORT;
+
+  if (is_report)
+    g_total_reports++;
+
+  g_report_ids.ReleaseReport(id);
+
+  if (type == R_FATAL) {
+    if (is_report) {
+      // Treat as ASSERT
+      is_report = false;
+      is_assert = true;
+    }
+  }
+
+  bool always_assert = g_always_assert;
+
+  if (always_assert) {
+    is_report = false;
+    is_assert = true;
+  }
+
+  if (!message) {
+    message = _T("");
+  }
+
+  // log to debugger
+  TCHAR *debug_string;
+  // ::OutputDebugString(DEBUG_LOG_SEPARATOR);
+  ::OutputDebugString(is_report ? _T("REPORT: ") :
+                                  (is_assert ? _T("ASSERT: ") : _T("ABORT: ")));
+
+  CFixedStringT<CString, 1024> proc_name = app_util::GetAppName();
+
+  // last %s now %s skip %d
+  const TCHAR* format = message && *message ?
+                        _T("[%hs:%d][%hs][%s]") : _T("[%hs:%d][%hs]");
+  debug_string = SPRINTF(format, filename, linenumber, expr, message);
+
+  // String_Int64ToString(g_last_report_time, 10),
+  // String_Int64ToString(time, 10), skip_report));
+
+  // ::OutputDebugString(DEBUG_LOG_SEPARATOR);
+  // ::OutputDebugString(_T("\n"));
+
+#ifdef LOGGING
+  // Log the reports via the logging system to all loggers.
+  CString what = is_report ? _T("REPORT") :
+                             is_assert ? _T("ASSERT") : _T("ABORT");
+  LC_LOG(LC_LOGGING, LEVEL_ERROR, (_T("[%s]%s"), what, debug_string));
+#else
+  ::OutputDebugString(debug_string);
+  ::OutputDebugString(_T("\n"));
+#endif
+
+  // skip sending strack trace for duplicate reports
+  CString report_id;
+  SafeCStringFormat(&report_id, _T("%hs:%d"), filename, linenumber);
+
+  uint32 prev_reports = 0;
+  if (g_reports_done.Lookup(report_id, prev_reports) && is_report) {
+    prev_reports++;
+    g_reports_done.SetAt(report_id, prev_reports);
+    ::OutputDebugString(SPRINTF(_T("skipping duplicate report %s %d\n"),
+                                report_id.GetString(),
+                                prev_reports));
+    return 1;
+  }
+
+  prev_reports++;
+  g_reports_done.SetAt(report_id, prev_reports);
+
+  g_report_summary.Append(debug_string);
+  g_report_summary.Append(L" (");
+  g_report_summary.Append(itostr(id));
+  g_report_summary.Append(L")");
+  g_report_summary.Append(L"\r\n");
+
+  // ::OutputDebugString(_T("log to file\n"));
+
+  // log to file
+  CString path_name(MakeFullDebugFilename(kCiDebugLogFile));
+  HANDLE h = CreateFile(path_name,
+                        GENERIC_WRITE | GENERIC_READ,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE,
+                        NULL,
+                        OPEN_ALWAYS,
+                        FILE_ATTRIBUTE_NORMAL,
+                        NULL);
+
+  HANDLE assert_file = INVALID_HANDLE_VALUE;
+  if (is_assert) {
+    path_name = MakeFullDebugFilename(kCiAssertOccurredFile);
+    assert_file = CreateFile(path_name,
+                             GENERIC_WRITE,
+                             0,
+                             0,
+                             OPEN_ALWAYS,
+                             FILE_FLAG_WRITE_THROUGH,
+                             NULL);
+  }
+
+  HANDLE abort_file = INVALID_HANDLE_VALUE;
+  if (is_abort) {
+    path_name = MakeFullDebugFilename(kCiAbortOccurredFile);
+    abort_file = CreateFile(path_name,
+                            GENERIC_WRITE,
+                            0,
+                            0,
+                            OPEN_ALWAYS,
+                            FILE_FLAG_WRITE_THROUGH,
+                            NULL);
+  }
+
+  if (h != INVALID_HANDLE_VALUE ||
+      assert_file != INVALID_HANDLE_VALUE ||
+      abort_file != INVALID_HANDLE_VALUE) {
+    // more convenient for now to have this in UTF8
+    char *utf8_buffer = new char[(lstrlen(debug_string)*2) + 1];
+    if (utf8_buffer) {
+        int conv_bytes = WideCharToMultiByte(CP_UTF8,
+                                             0,
+                                             debug_string,
+                                             lstrlen(debug_string),
+                                             utf8_buffer,
+                                             (lstrlen(debug_string) * 2) + 1,
+                                             NULL,
+                                             NULL);
+
+        if (conv_bytes) {
+            DWORD bytes_written;
+            BOOL result;
+
+            if (h != INVALID_HANDLE_VALUE) {
+              SetFilePointer(h, 0, NULL, FILE_END);
+              result = ::WriteFile(h,
+                                   (LPCVOID)utf8_buffer,
+                                   conv_bytes,
+                                   &bytes_written,
+                                   NULL);
+              result = ::WriteFile(h,
+                                   (LPCVOID)DEBUG_LOG_SEPARATOR_CHAR,
+                                   strlen(DEBUG_LOG_SEPARATOR_CHAR),
+                                   &bytes_written,
+                                   NULL);
+            }
+            if (assert_file != INVALID_HANDLE_VALUE) {
+              result = ::WriteFile(assert_file,
+                                   (LPCVOID)utf8_buffer,
+                                   conv_bytes,
+                                   &bytes_written,
+                                   NULL);
+            }
+            if (abort_file != INVALID_HANDLE_VALUE) {
+              result = ::WriteFile(abort_file,
+                                   (LPCVOID)utf8_buffer,
+                                   conv_bytes,
+                                   &bytes_written,
+                                   NULL);
+            }
+        }
+
+        delete [] utf8_buffer;
+    }
+  }
+
+  if (h != INVALID_HANDLE_VALUE) {
+    ::CloseHandle(h);
+  }
+  if (assert_file != INVALID_HANDLE_VALUE) {
+    ::CloseHandle(assert_file);
+  }
+  if (abort_file != INVALID_HANDLE_VALUE) {
+    ::CloseHandle(abort_file);
+  }
+
+  CString stack_trace = OnDebugReport(id, is_report, type, expr, message,
+                                      filename, linenumber, debug_report_kind);
+
+  if (is_report) {
+    return 1;
+  }
+
+  ::OutputDebugString(L"show assert dialog\r\n");
+  ::OutputDebugString(stack_trace.GetString());
+
+  CString process_path;
+  GetModuleFileName(NULL, &process_path);
+
+  static TCHAR clipboard_string[4096] = {0};
+  lstrcpyn(clipboard_string,
+           SPRINTF(L"%ls (pid=%i)\r\n%hs:%d\r\n\r\n%hs\r\n%s\r\n\r\n",
+                   process_path,
+                   ::GetCurrentProcessId(),
+                   filename,
+                   linenumber,
+                   expr,
+                   message),
+           arraysize(clipboard_string));
+  stack_trace = stack_trace.Left(
+      arraysize(clipboard_string) - lstrlen(clipboard_string) - 1);
+  SafeStrCat(clipboard_string, stack_trace, arraysize(clipboard_string));
+  SetClipboard(clipboard_string);
+
+  stack_trace = stack_trace.Left(kMaxStackTraceDialogLen);
+
+  CString assert_text;
+  SafeCStringFormat(&assert_text,
+    _T("Assertion (%ls) failed!\r\n\r\nProcess: %d ")
+    _T("(0x%08X)\r\nThread %d (0x%08X)\r\nProgram: %ls\r\n")
+    _T("Version: %s\r\nFile: %hs\r\nLine: %d\r\n\r\n"),
+    debug_report_kind == DEBUGREPORT_ASSERT ?
+      L"Assert" : (debug_report_kind == DEBUGREPORT_ABORT ?
+      L"Abort" : L"Report"),
+    ::GetCurrentProcessId(), ::GetCurrentProcessId(), ::GetCurrentThreadId(),
+    ::GetCurrentThreadId(), process_path, omaha::GetVersionString(), filename,
+    linenumber);
+
+  CString error_request_to_send;
+  SafeCStringFormat(&error_request_to_send, kErrorRequestToSendFormat, kAppName);
+
+  if (lstrlen(message) > 0) {
+    SafeCStringAppendFormat(&assert_text,
+        _T("Expression: %hs\r\nMessage: %s\r\n\r\n%s\r\n\r\n%s"),
+        expr,
+        message,
+        stack_trace,
+        error_request_to_send);
+  } else {
+    SafeCStringAppendFormat(&assert_text,
+        _T("Expression: %hs\r\n\r\n%s\r\n\r\n%s"),
+        expr, stack_trace, error_request_to_send);
+  }
+
+  ShowAssertDialog(assert_text, CString(filename));
+  return 1;
+}
+
+#endif  // #ifdef _DEBUG
+
+
+ReportIds::ReportIds() {
+  data_.report_counts_num = 0;
+  NamedObjectAttributes lock_attr;
+  GetNamedObjectAttributes(kReportIdsLock,
+                           vista_util::IsUserAdmin(),
+                           &lock_attr);
+  InitializeWithSecAttr(lock_attr.name, &lock_attr.sa);
+}
+
+ReportIds::~ReportIds() {
+  // don't attempt to write out reports from low integrity mode.
+  //
+  // TODO(omaha): save reports from a low integrity process (specifically IE
+  // which does some extra special magic to thwart this) --
+  // possible by launch a process or a broker, etc.
+  if (vista::IsProcessProtected()) {
+    return;
+  }
+
+  if (data_.report_counts_num != 0) {
+    // Back the report IDs to the registry
+    __mutexBlock(this) {
+      ReportData *reports_in_config = NULL;
+      if (LoadReportData(&reports_in_config)) {
+        MergeReports(reports_in_config, &data_);
+        SaveReportData(reports_in_config);
+
+        byte *data = reinterpret_cast<byte*>(reports_in_config);
+        delete [] data;
+      } else {
+        // There's no data in the registry, so just fill it up with this
+        // component's data.
+        SaveReportData(&data_);
+      }
+    }
+  }
+}
+
+const TCHAR* const GetRegKeyShared() {
+  return vista_util::IsUserAdmin() ? _T("HKLM\\") kCiRegKeyShared :
+                                     _T("HKCU\\") kCiRegKeyShared;
+}
+
+void ReportIds::ResetReportsAfterPing() {
+  // We will lose reports from TRS between the time DebugReportString was called
+  // and now. We'll also lose reports from non TRS components if they exit in
+  // between this time.  Not important.
+  data_.report_counts_num = 0;
+  __mutexBlock(this) {
+    RegKey::DeleteValue(GetRegKeyShared(), kRegValueReportIds);
+  }
+}
+
+void ReportIds::MergeReports(ReportData *data1, const ReportData *data2) {
+  // Loop through each report ID from data2.  If we find it already, increment
+  // the report's count in data1. Otherwise, if there's enough space, add the
+  // report ID to data1.
+  uint32 i, j;
+  for (i = 0; i < data2->report_counts_num; ++i) {
+    bool duplicate_report = false;
+    for (j = 0; j < data1->report_counts_num; ++j) {
+      if (data1->report_ids[j] == data2->report_ids[i]) {
+        // uint16 is promoted to int.
+        data1->report_counts[j] = static_cast<uint16>(
+            data1->report_counts[j] + data2->report_counts[i]);
+        duplicate_report = true;
+      }
+    }
+
+    if (!duplicate_report && j < kMaxUniqueReports) {
+      data1->report_ids[j] = data2->report_ids[i];
+      data1->report_counts[j] = data2->report_counts[i];
+      data1->report_counts_num++;
+    }
+  }
+}
+
+bool ReportIds::LoadReportData(ReportData **data) {
+  DWORD byte_count = 0;
+  *data = NULL;
+  HRESULT hr = RegKey::GetValue(GetRegKeyShared(),
+                                kRegValueReportIds,
+                                reinterpret_cast<byte**>(data),
+                                &byte_count);
+  if (SUCCEEDED(hr)) {
+    if (byte_count == sizeof(ReportData)) {
+      return true;
+    } else {
+      delete[] data;
+      data = NULL;
+      return false;
+    }
+  } else {
+    return false;
+  }
+}
+
+void ReportIds::SaveReportData(ReportData *data) {
+  if (data->report_counts_num) {
+    RegKey::SetValue(GetRegKeyShared(),
+                     kRegValueReportIds,
+                     reinterpret_cast<byte*>(data),
+                     sizeof(ReportData));
+  }
+}
+
+bool ReportIds::ReleaseReport(uint32 id) {
+  uint32 max = data_.report_counts_num;
+
+  // If two threads call simultaneously, might miss one of an existing report
+  // here; not important.
+
+  uint32 i = 0;
+  for (i = 0; i < max; ++i) {
+    if (data_.report_ids[i] == id) {
+      data_.report_counts[i]++;
+      return true;
+      }
+  }
+
+  // If two threads call simultaneously, might overwrite first of another
+  // report; not important.
+
+  if (i < kMaxUniqueReports) {
+    data_.report_ids[i] = id;
+    data_.report_counts[i] = 1;
+    data_.report_counts_num = i + 1;  // Set only after setting ids and count;
+                                      // don't use ++
+  }
+
+#ifdef _DEBUG
+  OutputDebugString(SPRINTF(_T("release report %u\n"), id));
+#endif
+  // must return true (return value of REPORT)
+  return true;
+}
+
+// caller deletes the string
+TCHAR *ReportIds::DebugReportString() {
+  TCHAR *s = new TCHAR[(kMaxUniqueReports * kMaxReportCountString) + 1];
+  if (!s) { return NULL; }
+  s[0] = '\0';
+
+#if 0
+  // this version if we use a hash table for the report counts:
+  uint32 id;
+  uint16 count;
+  hr = g_reports->First(&found, &id, &count);
+  while (SUCCEEDED(hr) && found) {
+    if (count) {
+        SafeCStringAppendFormat(&status_info, _T("%d=%d"),
+                                id,
+                                static_cast<uint32>(count));
+    }
+
+    hr = g_reports->Next(&found, &id, &count);
+  }
+#endif
+
+  // The registry will contain the REPORTs from other components, so get that
+  // list and merge it with TRS'.
+  __mutexBlock(this) {
+    ReportData *reports_in_config = NULL;
+    if (LoadReportData(&reports_in_config)) {
+      MergeReports(&data_, reports_in_config);
+
+      byte *data = reinterpret_cast<byte*>(reports_in_config);
+      delete[] data;
+    }
+  }
+
+  TCHAR *current_pos = s;
+  for (uint32 i = 0; i < data_.report_counts_num; i++) {
+    if (data_.report_counts[i]) {
+      // should be no chance of overflow, ok to use wsprintf
+      int n = wsprintf(current_pos,
+                       _T("%u:%u,"),
+                       data_.report_ids[i],
+                       static_cast<uint32>(data_.report_counts[i]));
+      current_pos += n;
+    }
+  }
+
+  return s;
+}
+
+// A simple helper function whose sole purpose is
+// to isolate the dtor from SPRINTF which uses try/except.
+// app_util::GetAppName returns a CString and dtor's
+// aren't allowed in functions with try/except when
+// the /EHsc flag is set.
+void FillInSprintfErrorString(const TCHAR * format, TCHAR * error_string) {
+  wsprintf(error_string, L"SPRINTF buffer overrun %ls %hs %ls",
+           app_util::GetAppName(), omaha::GetVersionString(), format);
+}
+
+// following is currently included in release build
+// return string from format+arglist; for debugging
+TCHAR * __cdecl SPRINTF(const TCHAR * format, ...) {
+  while (::InterlockedCompareExchange(&g_sprintf_interlock, 1, 0) == 1) {
+  // while (::InterlockedIncrement(&g_sprintf_interlock)>1) {
+    // ::InterlockedDecrement(&g_sprintf_interlock);
+    // Don't process APCs here.
+    // Can lead to infinite recursion, for example: filecap->logging->filecap...
+    Sleep(0);
+  }
+
+  g_current_sprintf_buffer++;
+  if (g_current_sprintf_buffer >= kSprintfBuffers) {
+    g_current_sprintf_buffer = 0;
+  }
+
+  TCHAR *sprintf_buf = NULL;
+
+  if (!g_initialized_sprintf) {  // initialize buffers
+    g_sprintf_buffer = new TCHAR[
+        ((kSprintfMaxLen + 1) * kSprintfBuffers) +
+        kSprintfBufferOverrunPadding];
+    TCHAR* buffer = g_sprintf_buffer;
+    if (!buffer) { goto cleanup; }
+    for (int i = 0; i < kSprintfBuffers; ++i) {
+      g_sprintf_buffers[i] = buffer;
+      buffer += kSprintfMaxLen + 1;
+    }
+
+#if !SHIPPING
+    for (int i = ((kSprintfMaxLen+1) * kSprintfBuffers);
+         i < ((kSprintfMaxLen + 1) * kSprintfBuffers) +
+            kSprintfBufferOverrunPadding;
+         ++i) {
+      g_sprintf_buffer[i] = 1;
+    }
+#endif
+
+    // InitializeCriticalSection(&g_sprintf_critical_section);
+    g_initialized_sprintf = true;
+  }
+
+  sprintf_buf = g_sprintf_buffers[g_current_sprintf_buffer];
+
+  // EnterCriticalSection(&g_sprintf_critical_section);
+
+  __try {
+    // create the formatted CString
+    va_list vl;
+    va_start(vl, format);
+#ifdef DEBUG
+    StringCbVPrintfW(sprintf_buf, kSprintfMaxLen, format, vl);
+#else
+    wvsprintfW(sprintf_buf, format, vl);
+#endif
+    va_end(vl);
+
+#if !SHIPPING
+    for (int i = ((kSprintfMaxLen+1) * kSprintfBuffers);
+         i < ((kSprintfMaxLen+1) * kSprintfBuffers) +
+            kSprintfBufferOverrunPadding;
+         ++i) {
+      if (g_sprintf_buffer[i] != 1) {
+        TCHAR error_string[1024];
+        FillInSprintfErrorString(format, error_string);
+        MessageBox(NULL,
+                   error_string,
+                   error_string,
+                   MB_OK | MB_SETFOREGROUND | MB_TOPMOST);
+        break;
+      }
+    }
+#endif
+  }
+  __except(EXCEPTION_EXECUTE_HANDLER) {
+    lstrcpyn(sprintf_buf, _T("sprintf failure"), kSprintfMaxLen);
+  }
+
+  // LeaveCriticalSection(&g_sprintf_critical_section);
+
+  cleanup:
+
+  ::InterlockedDecrement(&g_sprintf_interlock);
+  return sprintf_buf;
+}
+
+#if 0
+  TCHAR * __cdecl SPRINTF(const TCHAR * format, ...) {
+  ASSERT(format, (L""));
+
+  g_current_sprintf_buffer++;
+  if (g_current_sprintf_buffer >= kSprintfBuffers) {
+    g_current_sprintf_buffer = 0;
+    }
+
+  TCHAR *sprintf_buf = sprintf_buffers[g_current_sprintf_buffer];
+  CFixedStringT<CString, kSprintfMaxLen> out;
+
+  va_list argptr;
+  va_start(argptr, format);
+  out.FormatV(format, argptr);
+  va_end(argptr);
+
+  // copy to fixed return buffers
+  SafeStrCat(sprintf_buf,
+             out.GetBufferSetLength(kSprintfMaxLen),
+             g_current_sprintf_buffer);
+  sprintf_buf[kSprintfMaxLen] = '\0';
+
+  return sprintf_buf;
+}
+#endif
+
+// Cleanup allocated memory
+class SprintfCleaner {
+ public:
+  SprintfCleaner() {}
+
+  ~SprintfCleaner() {
+    while (::InterlockedCompareExchange(&g_sprintf_interlock, 1, 0) == 1) {
+      Sleep(0);
+    }
+
+    if (g_initialized_sprintf) {
+      delete[] g_sprintf_buffer;
+      for (int i = 0; i < kSprintfBuffers; ++i) {
+        g_sprintf_buffers[i] = NULL;
+      }
+      g_initialized_sprintf = false;
+    }
+
+    ::InterlockedDecrement(&g_sprintf_interlock);
+  }
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(SprintfCleaner);
+};
+
+static SprintfCleaner cleaner;
+
+// This is for our testers to find asserts in release mode.
+#if !defined(_DEBUG) && defined(ASSERT_IN_RELEASE)
+bool ReleaseAssert(const char *expr,
+                   const TCHAR *msg,
+                   const char *filename,
+                   int32 linenumber) {
+  ASSERT(filename, (L""));
+  ASSERT(msg, (L""));
+  ASSERT(expr, (L""));
+
+  if (debug_assert_interceptor.interceptor()) {
+    // call replacement function (typically used for unit tests)
+    // Note that I'm doing this inside the in_assert block for paranoia;
+    // it's not really necessary and perhaps the wrong choice.
+    debug_assert_interceptor.interceptor()(expr,
+                                           CT2CA(msg),
+                                           filename,
+                                           linenumber);
+    return true;
+  }
+
+  OnAssert(expr, msg, filename, linenumber);
+
+  // Also put up a message box.
+  TCHAR error_string[1024] = {0};
+  wsprintf(error_string,
+           L"App: %ls\r\n"
+           L"Expr: %hs\r\n"
+           L"File: %hs\r\n"
+           L"Line: %d\r\n"
+           L"Version: %hs\r\n"
+           L"Message: ",
+           app_util::GetAppName(),
+           expr,
+           filename,
+           linenumber,
+           VER_TIMESTAMP_STR_FILE);
+  SafeStrCat(error_string, msg, arraysize(error_string));
+  SafeStrCat(error_string,
+             L"\r\n\r\n*** This message has been copied to the clipboard. ***",
+             arraysize(error_string));
+  SetClipboard(error_string);
+
+  TCHAR title_string[1024];
+  wsprintf(title_string, L"%s ASSERT %s %hs",
+           kAppName, app_util::GetAppName(), VER_TIMESTAMP_STR_FILE);
+  MessageBox(NULL,
+             error_string,
+             title_string,
+             MB_OK | MB_SETFOREGROUND | MB_TOPMOST);
+  return true;
+}
+#endif
+
+#if defined(_DEBUG)
+void DebugAbort(const TCHAR *msg,
+                const char* filename,
+                int32 linenumber,
+                bool do_abort) {
+  DebugReport(0, R_FATAL, "", msg, filename, linenumber, DEBUGREPORT_ABORT);
+  if (do_abort) {
+    abort();
+  }
+}
+#else
+void ReleaseAbort(const TCHAR *msg,
+                  const char* filename,
+                  int32 linenumber,
+                  bool do_abort) {
+  // Send info to the server.
+#if defined(ASSERT_IN_RELEASE)
+  OnAssert("", msg, filename, linenumber);
+#endif
+
+  // Also put up a message box.
+  TCHAR error_string[1024] = {0};
+  wsprintf(error_string,
+           L"App: %ls\r\n"
+           L"File: %hs\r\n"
+           L"Line: %d\r\n"
+           L"Version: %hs\r\n"
+           L"Message: ",
+           app_util::GetAppName(),
+           filename,
+           linenumber,
+           omaha::GetVersionString());
+  SafeStrCat(error_string, msg, arraysize(error_string));
+  SafeStrCat(error_string,
+             L"\r\n\r\n*** This message has been copied to the clipboard. ***",
+             arraysize(error_string));
+  SetClipboard(error_string);
+
+  TCHAR title_string[1024];
+  wsprintf(title_string,
+           L"%s ABORT %s %hs",
+           kAppName,
+           app_util::GetAppName(),
+           omaha::GetVersionString());
+  MessageBox(NULL,
+             error_string,
+             title_string,
+             MB_OK | MB_SETFOREGROUND | MB_TOPMOST);
+
+  if (do_abort) {
+    abort();
+  }
+}
+#endif
+
+
+#ifdef _DEBUG
+
+void DumpInterface(IUnknown* unknown) {
+  if (!unknown)
+    return;
+
+  OutputDebugString(_T("------------------------------------------------\r\n"));
+
+  // Open the HKCR\Interfaces key where the IIDs of marshalable interfaces
+  // are stored.
+  RegKey key;
+  if (SUCCEEDED(key.Open(HKEY_CLASSES_ROOT, _T("Interface"), KEY_READ))) {
+    TCHAR name[_MAX_PATH + 1] = {0};
+    DWORD name_size = _MAX_PATH;
+    DWORD index = 0;
+    FILETIME last_written;
+
+    //
+    // Enumerate through the IIDs and see if the object supports it
+    // by calling QueryInterface.
+    //
+    while (::RegEnumKeyEx(key.Key(),
+                          index++,
+                          name,
+                          &name_size,
+                          NULL,
+                          NULL,
+                          NULL,
+                          &last_written) == ERROR_SUCCESS) {
+      // Convert the string to an IID
+      IID iid;
+      HRESULT hr = StringToGuidSafe(name, &iid);
+
+      CComPtr<IUnknown> test;
+      if (unknown->QueryInterface(iid,
+                                  reinterpret_cast<void**>(&test)) == S_OK) {
+        //
+        // The object supports this interface.
+        // See if we can get a human readable name for the interface
+        // If not, the name buffer already contains the string
+        // representation of the IID, which we'll use as a fallback.
+        //
+        RegKey sub_key;
+        if (sub_key.Open(key.Key(), name, KEY_READ) == S_OK) {
+          scoped_array<TCHAR> display;
+          // If this fails, we should still have the IID
+          if (sub_key.GetValue(NULL, address(display)) == S_OK)
+            lstrcpyn(name, display.get(), _MAX_PATH);
+        }
+
+        CString fmt;
+        SafeCStringFormat(&fmt, _T("  %s\r\n"), name);
+        OutputDebugString(fmt);
+      }
+
+      ZeroMemory(name, arraysize(name));
+      name_size = _MAX_PATH;
+    }
+  }
+
+  OutputDebugString(_T("------------------------------------------------\r\n"));
+}
+#endif
+
+// TODO(omaha): the implementation below is using CStrings so it is not very
+// conservative in terms of memory allocations.
+int SehNoMinidump(unsigned int code, struct _EXCEPTION_POINTERS *,
+                  const char *filename, int32 linenumber, bool show_message) {
+  if (code == EXCEPTION_BREAKPOINT)
+    return EXCEPTION_CONTINUE_SEARCH;
+
+  uint32 latest_cl = 0;
+#ifdef VERSION_LATEST_CL
+  latest_cl = VERSION_LATEST_CL;
+#endif
+
+  if (show_message) {
+    TCHAR message[1025] = {0};
+    wsprintf(message,
+             _T("Exception %x in %s %s %u\r\n\r\n%hs:%d\r\n"),
+             code,
+             app_util::GetAppName(),
+             omaha::GetVersionString(),
+             latest_cl,
+             filename,
+             linenumber);
+
+    SetClipboard(message);
+    uint32 type = MB_ABORTRETRYIGNORE |
+                  MB_ICONERROR |
+                  MB_SERVICE_NOTIFICATION |
+                  MB_SETFOREGROUND |
+                  MB_TOPMOST;
+    int ret = ::MessageBox(NULL, message, _T("Exception"), type);
+    switch (ret) {
+      case IDABORT:
+        // Kamikaze if the user chose 'abort'
+        ::ExitProcess(static_cast<UINT>(-1));
+        break;
+
+      case IDRETRY:
+        // Break if the user chose "retry"
+        __debugbreak();
+        break;
+
+      default:
+        // By default we ignore the message
+      break;
+    }
+  }
+  return EXCEPTION_EXECUTE_HANDLER;
+}
+
+CString GetDebugDirectory() {
+  CString debug_dir;
+  CString system_drive = GetEnvironmentVariableAsString(_T("SystemDrive"));
+  if (!system_drive.IsEmpty()) {
+    debug_dir += system_drive;
+    debug_dir += L"\\";
+  }
+  debug_dir += kCiDebugDirectory;
+  return debug_dir;
+}
+
+}  // namespace omaha
+
diff --git a/base/debug.h b/base/debug.h
new file mode 100644
index 0000000..5566a0b
--- /dev/null
+++ b/base/debug.h
@@ -0,0 +1,305 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Debug functions
+
+#ifndef OMAHA_BASE_DEBUG_H_
+#define OMAHA_BASE_DEBUG_H_
+
+// To create a release build with asserts turned on, uncomment the
+// following line, and uncomment the linking with atls.lib in api.
+//
+// #define ASSERT_IN_RELEASE
+
+#include "omaha/base/atlassert.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/time.h"
+
+namespace omaha {
+
+// hash table for counts of the number of times REPORTs occur
+// template<class K, class V>
+// class HashTable;
+// extern HashTable<uint32, uint16> *g_reports;
+
+#define kMaxUniqueReports (20)
+#define kMaxReportCountString (20)
+
+class ReportIds;
+extern ReportIds g_report_ids;
+extern volatile LONG g_debugassertrecursioncheck;
+extern bool g_always_assert;
+
+const int kMaxStackTraceDialogLen = 512;  // too long and dialog box fails
+
+// TODO(omaha): consider merging this into DebugObserver.
+//
+// For automated testing, we don't (always) want asserts to fire. So
+// we allow the unit test system to handle asserts instead.
+//
+// Give a function matching the prototype to REPLACE_ASSERT_FUNCTION to
+// have your function called instead of the normal assert function.
+typedef int DebugAssertFunctionType(const char *expression,
+  const char *message, const char *file, int line);
+
+enum ReportType;
+enum DebugReportKind;
+class DebugObserver {
+ public:
+  virtual ~DebugObserver() {}
+  virtual int SehSendMinidump(unsigned int code, struct _EXCEPTION_POINTERS *ep,
+                              time64 time_between_minidumps) = 0;
+
+#if defined(_DEBUG) || defined(ASSERT_IN_RELEASE)
+  virtual void OnAssert(const char *expr, const TCHAR *msg,
+                        const char *filename, int32 linenumber) = 0;
+#endif
+
+#if defined(_DEBUG)
+  virtual void SendExceptionReport(const TCHAR *log_file, const TCHAR *filename,
+                                   int line, const TCHAR *type, uint32 id,
+                                   bool offline) = 0;
+  virtual CString OnDebugReport(uint32 id, bool is_report, ReportType type,
+                                const char *expr, const TCHAR *message,
+                                const char *filename, int32 linenumber,
+                                DebugReportKind debug_report_kind) = 0;
+#endif
+};
+
+// replaces the debug observer, returns the previous value.
+DebugObserver* SetDebugObserver(DebugObserver* observer);
+DebugObserver* PeekDebugObserver();
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int SehSendMinidump(unsigned int code,
+                    struct _EXCEPTION_POINTERS *ep,
+                    time64 time_between_minidumps);
+
+#define kMinReportInterval100ns (60 * kSecsTo100ns)
+#define kMinStackReportInterval100ns (60 * kSecsTo100ns)
+#define DEBUG_LOG_SEPARATOR_CHAR "-------------------------------------------\n"
+#define DEBUG_LOG_SEPARATOR     L"-------------------------------------------\n"
+#define kExceptionReportHeaders L"Content-Type: binary"
+
+#undef ASSERT
+#undef VERIFY
+#undef TRACE
+
+// Holds information about REPORTS and their frequency
+struct ReportData {
+  uint32 report_counts_num;
+  uint32 report_ids[kMaxUniqueReports];
+  uint16 report_counts[kMaxUniqueReports];
+};
+
+// Used to hold REPORT IDs and to back them to the registry, where they'll be
+// read and sent in a ping.
+class ReportIds : public GLock {
+ public:
+  ReportIds();
+  ~ReportIds();
+
+  // Call this after a successful ping to clear the report IDs from the registry
+  // and from this component (TRS).
+  void ResetReportsAfterPing();
+
+  // Adds a report ID to our list, if there's enough space.
+  bool ReleaseReport(uint32 id);
+
+  // Creates a string with the report IDs and their frequency.
+  // Caller deletes string.
+  TCHAR *DebugReportString();
+
+ private:
+  ReportData data_;
+
+  // Merges the report data from data2 with data1.
+  void MergeReports(ReportData *data1, const ReportData *data2);
+
+  // We have to use RegKey directly, because we can't depend on the global
+  // Config object during destruction.
+  bool LoadReportData(ReportData **data);
+  void SaveReportData(ReportData *data);
+
+  DISALLOW_EVIL_CONSTRUCTORS(ReportIds);
+};
+
+#if defined(_DEBUG) || defined(ASSERT_IN_RELEASE)
+  // Replaces the debug assert function; returns the old value.
+  DebugAssertFunctionType *ReplaceDebugAssertFunction(DebugAssertFunctionType
+                                                      *replacement);
+  #define REPLACE_ASSERT_FUNCTION(replacement) \
+      ReplaceDebugAssertFunction(replacement)
+#else
+  #define REPLACE_ASSERT_FUNCTION(replacement) NULL
+#endif
+
+#ifdef _DEBUG
+  void SendExceptionReport(const TCHAR *log_file,
+                           const TCHAR *filename,
+                           int line,
+                           const TCHAR *type,
+                           uint32 id,
+                           bool offline);
+
+  void SendStackTrace(const TCHAR *filename,
+                      int line,
+                      const TCHAR *type,
+                      bool all_threads,
+                      uint32 id);
+
+  // DEBUG MODE
+
+  extern bool g_LSPMode;
+
+  bool DebugReport(unsigned int id,
+                   ReportType type,
+                   const char *expr,
+                   const TCHAR *message,
+                   const char *filename,
+                   int linenumber,
+                   DebugReportKind debug_report_kind);
+
+  #define VERIFY(expr, msg) ASSERT(expr, msg)  // VERIFY is ASSERT
+
+  #define ASSERT(expr, msg)                                                    \
+      do {                                                                     \
+        ((expr) ? 0 : omaha::DebugReport(0, omaha::R_FATAL, #expr,             \
+          omaha::SPRINTF msg, __FILE__, __LINE__, omaha::DEBUGREPORT_ASSERT)); \
+      } while (0)
+
+  #define REPORT(expr, type, msg, id)                           \
+    ((expr) ? 0 : omaha::DebugReport(id,                        \
+                                     type,                      \
+                                     #expr,                     \
+                                     omaha::SPRINTF msg,        \
+                                     __FILE__,                  \
+                                     __LINE__,                  \
+                                     omaha::DEBUGREPORT_REPORT))
+  void DebugAbort(const TCHAR* msg,
+                  const char* filename,
+                  int32 linenumber,
+                  bool do_abort);
+  #define ABORT(msg)                                            \
+      omaha::DebugAbort(omaha::SPRINTF msg, __FILE__, __LINE__, true)
+
+  void TraceError(DWORD error);
+  inline void TraceLastError() { TraceError(GetLastError()); }
+
+  /**
+  * Iterates through HKEY_CLASSES_ROOT\Interface and calls QI for
+  * all the interfaces there.  Useful for finding out what type of
+  * object you're dealing with :-)
+  */
+  void DumpInterface(IUnknown* unknown);
+
+#else  // #ifdef _DEBUG
+
+  #ifdef ASSERT_IN_RELEASE
+    bool ReleaseAssert(const char *expr,
+                       const TCHAR *msg,
+                       const char *filename,
+                       int32 linenumber);
+    #define ASSERT(expr, msg) \
+        ((expr) ? 0 : ReleaseAssert(#expr, SPRINTF msg, __FILE__, __LINE__))
+  #else
+    #define ASSERT(expr, msg) 0
+  #endif
+
+  // VERIFY executes but does not check expression
+  #define VERIFY(expr, msg) \
+    do {                   \
+      (expr);              \
+    } while (0)
+  #define REPORT(expr, type, msg, id) \
+      ((expr) ? 0 : g_report_ids.ReleaseReport(id))
+  void ReleaseAbort(const TCHAR* msg,
+                    const char* filename,
+                    int32 linenumber,
+                    bool do_abort);
+  #define ABORT(msg) ReleaseAbort(SPRINTF msg, __FILE__, __LINE__, true)
+
+#endif  // #ifdef _DEBUG
+
+#define ASSERT1(expr) ASSERT(expr, (_T("")))
+#define VERIFY1(expr) VERIFY(expr, (_T("")))
+
+#ifdef __cplusplus
+}  // extern "C"{
+#endif
+
+#ifdef _DEBUG
+void ShowAssertDialog(const TCHAR *message, const TCHAR *title);
+
+// used to automatically dump the global summary of reports when the
+// program exits
+class ReportSummaryGenerator {
+ public:
+  ReportSummaryGenerator() {}
+  // calls DumpReportSummary()
+  ~ReportSummaryGenerator();
+  // some programs exit without calling destructors, they can use this function
+  // to dump the report summary
+  void DumpReportSummary();
+  // get text summary of reports
+  // caller is responsible for deleting the string returned
+  TCHAR *GetReportSummary();
+  DISALLOW_EVIL_CONSTRUCTORS(ReportSummaryGenerator);
+};
+
+extern ReportSummaryGenerator g_report_summary_generator;
+#endif
+
+// return string from format+arglist; for debugging; not thread-safe
+TCHAR * __cdecl SPRINTF(const TCHAR * format, ...);
+bool DebugError(const char * expr,
+                const TCHAR * message,
+                const char * filename,
+                INT linenumber,
+                BOOL report_only);
+
+// shows an error dialog in DEBUG and when g_release_debug is true
+//
+// example usage:
+//
+//  __try {
+//    do something that sometimes causes a known exception (e.g.,
+//    calling third party code)
+//  } __except(SehNoMinidump(GetExceptionCode(), GetExceptionInformation(),
+//      __FILE__, __LINE__)) {
+//    REPORT(false, R_ERROR, (L"exception doing something"), 103178920);
+//  }
+// show_message - show an error dialog in DEBUG and when g_release_debug is true
+int SehNoMinidump(unsigned int code, struct _EXCEPTION_POINTERS *ep,
+  const char *filename, int32 linenumber, bool show_message);
+
+/**
+* @return Always returns an error value.  If GetLastError is not ERROR_SUCCESS
+*   the function returns an HRESULT value derived from GetLastError()
+*/
+inline HRESULT GetCurError() {
+  return ::GetLastError() == ERROR_SUCCESS ?
+      E_FAIL : HRESULT_FROM_WIN32(::GetLastError());
+}
+
+// Returns the directory where the debugging module stores debug-related files.
+CString GetDebugDirectory();
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_DEBUG_H_
diff --git a/base/debug_unittest.cc b/base/debug_unittest.cc
new file mode 100644
index 0000000..ee74ad8
--- /dev/null
+++ b/base/debug_unittest.cc
@@ -0,0 +1,107 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Debug unittest
+
+#include <stdexcept>
+
+#include "omaha/base/debug.h"
+#include "omaha/base/test.h"
+#include "omaha/base/time.h"
+
+namespace omaha {
+
+// test what happens when we hit an exception
+int SEHExceptionTest(int level, int reserved) {
+  const uint32 kflags = MB_SETFOREGROUND  |
+                        MB_TOPMOST        |
+                        MB_ICONWARNING    |
+                        MB_OKCANCEL;
+  if (::MessageBox(NULL,
+                   L"do exception?",
+                   L"exception test",
+                   kflags) == IDOK) {
+    int *a1 = static_cast<int *>(1);
+    *a1 = 2;
+
+    // if that does not work try:
+    // simulate a divide by zero
+    int a = 10;
+    int b2 = a;
+    b2 /= 2;
+    b2 -= 5;
+    // int c = a/b2;
+    int c = 0;
+    TCHAR *s = 0;
+    s++;
+    *s = 0;
+
+    RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
+  }
+
+  return 0;
+}
+
+int SEHCatchExceptionTest(int level, int reserved) {
+  __try {
+    SEHExceptionTest(level, reserved);
+  } __except (SehSendMinidump(GetExceptionCode(),
+                              GetExceptionInformation(),
+                              kMinsTo100ns)) {
+  }
+
+  return 0;
+}
+
+#pragma warning(push)
+#pragma warning(disable:4702)
+// test what happens when we do a C++ exception
+int CppExceptionTest(int level, int reserved) {
+  _TRY_BEGIN
+#if 0
+    _THROW(std::logic_error, "throwing a fake logic_error");
+#else
+//    std::logic_error e("throwing a fake logic_error");
+  //    e._Raise();
+  std::runtime_error(std::string("throwing a fake logic_error"))._Raise();
+#endif
+  _CATCH(std::logic_error e)
+    ASSERT(false, (L"caught exception"));
+  _CATCH_END
+  return 0;
+}
+#pragma warning(pop)
+
+// test what happens when we do a REPORT
+int ReportTest(int level, int reserved) {
+  REPORT(false, R_ERROR, (L"test REPORT"), 592854117);
+  return 0;
+}
+
+// test what happens when we hit an ASSERT
+int AssertTest(int level, int reserved) {
+  ASSERT(false, (L"test ASSERT"));
+  return 0;
+}
+
+// test what happens when we hit an ABORT
+int AbortTest(int level, int reserved) {
+  ABORT((L"test ABORT"));
+  ASSERT(false, (L"returned from ABORT"));
+  return 0;
+}
+
+}  // namespace omaha
+
diff --git a/base/disk.cc b/base/disk.cc
new file mode 100644
index 0000000..ef09e87
--- /dev/null
+++ b/base/disk.cc
@@ -0,0 +1,401 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Disk functions
+
+#include "omaha/base/disk.h"
+
+#include <winioctl.h>
+#include "omaha/base/commontypes.h"
+#include "omaha/base/const_config.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/localization.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/shell.h"
+#include "omaha/base/string.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/system.h"
+#include "omaha/base/timer.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+#define kNetdiskVendorId "netdisk"
+
+// see also: http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q264203
+
+#if _MSC_VER < 1400
+// Not defined in the headers we have; from MSDN:
+#define IOCTL_STORAGE_QUERY_PROPERTY \
+          CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+typedef struct _STORAGE_DEVICE_DESCRIPTOR {
+  ULONG  Version;
+  ULONG  Size;
+  UCHAR  DeviceType;
+  UCHAR  DeviceTypeModifier;
+  BOOLEAN  RemovableMedia;
+  BOOLEAN  CommandQueueing;
+  ULONG  VendorIdOffset;
+  ULONG  ProductIdOffset;
+  ULONG  ProductRevisionOffset;
+  ULONG  SerialNumberOffset;
+  STORAGE_BUS_TYPE  BusType;
+  ULONG  RawPropertiesLength;
+  UCHAR  RawDeviceProperties[1];
+} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;
+
+typedef enum _STORAGE_QUERY_TYPE {
+  PropertyStandardQuery = 0,
+  PropertyExistsQuery,
+  PropertyMaskQuery,
+  PropertyQueryMaxDefined
+} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE;
+
+typedef enum _STORAGE_PROPERTY_ID {
+  StorageDeviceProperty = 0,
+  StorageAdapterProperty,
+  StorageDeviceIdProperty
+} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID;
+
+typedef struct _STORAGE_PROPERTY_QUERY {
+  STORAGE_PROPERTY_ID  PropertyId;
+  STORAGE_QUERY_TYPE  QueryType;
+  UCHAR  AdditionalParameters[1];
+} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
+
+// -------
+#endif
+
+#define kIoctlBufferSize 1024
+
+#define kMaxDrivesCached 3
+#define kMaxDriveLen 20
+static TCHAR g_cache_drive[kMaxDrivesCached][kMaxDriveLen+1];
+static bool g_cache_external[kMaxDrivesCached];
+static int g_cache_pos;
+static LLock g_cache_lock;
+
+bool IsDiskExternal(const TCHAR *drive) {
+  ASSERT(drive, (L""));
+  ASSERT(lstrlen(drive) < kMaxDriveLen, (L""));
+
+  DisableThreadErrorUI disable_error_dialog_box;
+
+  {
+    __mutexScope(g_cache_lock);
+    for (int i = 0; i < kMaxDrivesCached; i++)
+      if (!lstrcmp(drive, g_cache_drive[i])) {
+        UTIL_LOG(L1, (L"cached disk ext %s %d", drive, g_cache_external[i]));
+        return g_cache_external[i];
+      }
+  }
+
+#ifdef _DEBUG
+  Timer timer(true);
+#endif
+
+  byte buffer[kIoctlBufferSize+1];
+
+  bool external = false;
+  HANDLE device = ::CreateFile(drive,
+                               GENERIC_READ,
+                               FILE_SHARE_READ | FILE_SHARE_WRITE,
+                               NULL,
+                               OPEN_EXISTING,
+                               NULL,
+                               NULL);
+  if (device == INVALID_HANDLE_VALUE) {
+      UTIL_LOG(L1, (L"disk external could not open drive %s", drive));
+      goto done;
+  }
+  STORAGE_DEVICE_DESCRIPTOR *device_desc;
+  STORAGE_PROPERTY_QUERY query;
+  DWORD out_bytes;
+  query.PropertyId = StorageDeviceProperty;
+  query.QueryType = PropertyStandardQuery;
+  *(query.AdditionalParameters) = 0;
+
+  device_desc = reinterpret_cast<STORAGE_DEVICE_DESCRIPTOR*>(buffer);
+  // should not be needed, but just to be safer
+  ZeroMemory(buffer, kIoctlBufferSize);
+
+  BOOL ok = ::DeviceIoControl(device,
+                              IOCTL_STORAGE_QUERY_PROPERTY,
+                              &query,
+                              sizeof(STORAGE_PROPERTY_QUERY),
+                              buffer,
+                              kIoctlBufferSize,
+                              &out_bytes,
+                              (LPOVERLAPPED)NULL);
+
+  if (ok &&
+      device_desc->VendorIdOffset &&
+      stristr(reinterpret_cast<char*>(buffer + device_desc->VendorIdOffset),
+              kNetdiskVendorId)) {
+    external = true;
+    UTIL_LOG(L1, (L"ximeta netdisk %s", drive));
+  }
+
+  if (ok &&
+      (device_desc->BusType == BusTypeUsb ||
+       device_desc->BusType == BusType1394)) {
+    external = true;
+  }
+  if (!ok) {
+    UTIL_LOG(L1, (L"disk external ioctl failed %s", drive));
+  }
+  CloseHandle(device);
+  done:
+  UTIL_LOG(L1, (L"disk external %s %d time %s",
+      drive, external, String_DoubleToString(timer.GetMilliseconds(), 3)));
+
+  {
+    __mutexScope(g_cache_lock);
+    lstrcpyn(g_cache_drive[g_cache_pos], drive, kMaxDriveLen+1);
+    g_cache_external[g_cache_pos] = external;
+    if (++g_cache_pos >= kMaxDrivesCached) g_cache_pos = 0;
+  }
+
+  return external;
+}
+
+// find the first fixed local disk with at least the space requested
+// confirms that we can create a directory on the drive
+// returns the drive in the drive parameter
+// returns E_FAIL if no drive with enough space could be found
+HRESULT FindFirstLocalDriveWithEnoughSpace(const uint64 space_required,
+                                           CString *drive) {
+  ASSERT1(drive);
+
+  DisableThreadErrorUI disable_error_dialog_box;
+
+  const int kMaxNumDrives = 26;
+  static const size_t kBufLen = (STR_SIZE("c:\\\0") * kMaxNumDrives) + 1;
+
+  // obtain the fixed system drives
+  TCHAR buf[kBufLen];
+  DWORD str_len = ::GetLogicalDriveStrings(kBufLen, buf);
+  if (str_len > 0 && str_len < kBufLen) {
+    for (TCHAR* ptr = buf; *ptr != L'\0'; ptr += (lstrlen(ptr) + 1)) {
+      UINT drive_type = GetDriveType(ptr);
+      if (drive_type == DRIVE_FIXED) {
+        CString test_drive(ptr);
+        if (!IsDiskExternal(CString(L"\\\\?\\") + test_drive.Left(2))) {
+          uint64 free_disk_space = 0;
+          HRESULT hr = GetFreeDiskSpace(test_drive, &free_disk_space);
+
+          if (SUCCEEDED(hr) && space_required <= free_disk_space) {
+            CString temp_dir;
+            // confirm that we can create a directory on this drive
+            bool found = false;
+            while (!found) {
+              temp_dir = test_drive +
+                         NOTRANSL(L"test") +
+                         itostr(static_cast<uint32>(::GetTickCount()));
+              if (!File::Exists (temp_dir)) found = true;
+            }
+
+            if (SUCCEEDED(CreateDir(temp_dir, NULL))) {
+              VERIFY1(SUCCEEDED(DeleteDirectory(temp_dir)));
+              *drive = test_drive;
+              UTIL_LOG(L1, (L"drive %s enough space %d", test_drive.GetString(),
+                            free_disk_space));
+              return S_OK;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  return E_FAIL;
+}
+
+// Get free disk space of a drive containing the specified folder
+HRESULT GetFreeDiskSpace(uint32 csidl, uint64* free_disk_space) {
+  ASSERT1(free_disk_space);
+
+  CString path;
+  RET_IF_FAILED(Shell::GetSpecialFolder(csidl, false, &path));
+
+  return GetFreeDiskSpace(path, free_disk_space);
+}
+
+// Get free disk space of a drive containing the specified folder
+HRESULT GetFreeDiskSpace(const TCHAR* folder, uint64* free_disk_space) {
+  ASSERT1(folder && *folder);
+  ASSERT1(free_disk_space);
+
+  DisableThreadErrorUI disable_error_dialog_box;
+
+  CString drive(folder);
+
+  // (Stupid API used by System::GetDiskStatistics will work with any folder -
+  // as long as it EXISTS.  Since the data storage folder might not exist yet
+  // (e.g., on a clean install) we'll just truncate it down to a drive letter.)
+  drive = drive.Left(3);  // "X:\"
+  ASSERT1(String_EndsWith(drive, _T(":\\"), false));
+
+  // Get the free disk space available to this user on this drive
+  uint64 free_bytes_current_user = 0LL;
+  uint64 total_bytes_current_user = 0LL;
+  uint64 free_bytes_all_users = 0LL;
+  RET_IF_FAILED(System::GetDiskStatistics(drive,
+                                          &free_bytes_current_user,
+                                          &total_bytes_current_user,
+                                          &free_bytes_all_users));
+
+  *free_disk_space = std::min(free_bytes_current_user, free_bytes_all_users);
+
+  return S_OK;
+}
+
+// Has enough free disk space on a drive containing the specified folder
+HRESULT HasEnoughFreeDiskSpace(uint32 csidl, uint64 disk_space_needed) {
+  uint64 free_disk_space = 0;
+  if (SUCCEEDED(GetFreeDiskSpace(csidl, &free_disk_space))) {
+    return (disk_space_needed <= free_disk_space) ?
+           S_OK : CI_E_NOT_ENOUGH_DISK_SPACE;
+  }
+  return S_OK;
+}
+
+// Has enough free disk space on a drive containing the specified folder
+HRESULT HasEnoughFreeDiskSpace(const TCHAR* folder, uint64 disk_space_needed) {
+  uint64 free_disk_space = 0;
+  if (SUCCEEDED(GetFreeDiskSpace(folder, &free_disk_space))) {
+    return (disk_space_needed <= free_disk_space) ?
+           S_OK : CI_E_NOT_ENOUGH_DISK_SPACE;
+  }
+  return S_OK;
+}
+
+// The ::CreateFile() call will fail for mapped local drives (subst).
+bool IsHotPluggable(const TCHAR* drive) {
+  ASSERT(drive, (L""));
+
+  // Disable potential error dialogs during this check
+  DisableThreadErrorUI disable_error_dialog_box;
+
+  //
+  // We set the default return value to true so that
+  // we treat the disk as hot-pluggable in case we
+  // don't know.
+  //
+  bool ret = true;
+
+  if (drive && lstrlen(drive) >= 2) {
+    CString volume_path(_T("\\\\.\\"));
+    // We don't want the trailing backslash.
+    volume_path.Append(drive, 2);
+
+    CHandle volume(::CreateFile(volume_path, GENERIC_READ,
+                                FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                NULL, OPEN_EXISTING, 0, NULL));
+
+    if (volume != INVALID_HANDLE_VALUE) {
+      STORAGE_HOTPLUG_INFO shi = {0};
+      shi.Size = sizeof(shi);
+      DWORD bytes_returned = 0;
+      if (::DeviceIoControl(volume, IOCTL_STORAGE_GET_HOTPLUG_INFO,  NULL, 0,
+                            &shi, sizeof(STORAGE_HOTPLUG_INFO), &bytes_returned,
+                            NULL)) {
+          ret = (shi.DeviceHotplug != false);
+      } else {
+        UTIL_LOG(LW, (_T("[::DeviceIoControl failed][%u]"), ::GetLastError()));
+      }
+    } else {
+      UTIL_LOG(LW, (_T("[::CreateFile failed][%u]"), ::GetLastError()));
+    }
+  } else {
+    ASSERT(false, (L"Invalid path"));
+  }
+
+  return ret;
+}
+
+bool IsLargeDrive(const TCHAR* drive) {
+  ASSERT1(drive && *drive);
+
+  DisableThreadErrorUI disable_error_dialog_box;
+
+  ULARGE_INTEGER caller_free_bytes = {0};
+  ULARGE_INTEGER total_bytes = {0};
+  ULARGE_INTEGER total_free_bytes = {0};
+
+  if (!::GetDiskFreeSpaceEx(drive,
+                            &caller_free_bytes,
+                            &total_bytes,
+                            &total_free_bytes)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[IsLargeDrive - failed to GetDiskFreeSpaceEx][0x%x]"), hr));
+    return false;
+  }
+
+  return (total_bytes.QuadPart > kLargeDriveSize);
+}
+
+HRESULT DevicePathToDosPath(const TCHAR* device_path, CString* dos_path) {
+  ASSERT1(device_path);
+  ASSERT1(dos_path);
+  UTIL_LOG(L4, (_T("[DevicePathToDosPath][device_path=%s]"), device_path));
+
+  dos_path->Empty();
+
+  TCHAR drive_strings[MAX_PATH] = _T("");
+  if (!::GetLogicalDriveStrings(arraysize(drive_strings), drive_strings)) {
+    UTIL_LOG(L4, (_T("[DevicePathToDosPath-GetLogicalDriveStrings fail][0x%x]"),
+                  HRESULTFromLastError()));
+    return HRESULTFromLastError();
+  }
+
+  // Drive strings are stored as a set of null terminated strings, with an
+  // extra null after the last string. Each drive string is of the form "C:\".
+  // We convert it to the form "C:", which is the format expected by
+  // ::QueryDosDevice().
+  TCHAR drive_colon[3] = _T(" :");
+  for (const TCHAR* next_drive_letter = drive_strings;
+       *next_drive_letter;
+       next_drive_letter += _tcslen(next_drive_letter) + 1) {
+    // Dos device of the form "C:".
+    *drive_colon = *next_drive_letter;
+    TCHAR device_name[MAX_PATH] = _T("");
+    if (!::QueryDosDevice(drive_colon, device_name, arraysize(device_name))) {
+      UTIL_LOG(LEVEL_ERROR, (_T("[QueryDosDevice failed][0x%x]"),
+                             HRESULTFromLastError()));
+      continue;
+    }
+
+    UTIL_LOG(L4, (_T("[DevicePathToDosPath found drive]")
+                  _T("[logical drive %s][device name %s]"),
+                  drive_colon, device_name));
+
+    size_t name_length = _tcslen(device_name);
+    if (_tcsnicmp(device_path, device_name, name_length) == 0) {
+      // Construct DOS path.
+      dos_path->Format(_T("%s%s"), drive_colon, device_path + name_length);
+      UTIL_LOG(L4, (_T("[DevicePathToDosPath][dos_path=%s]"), *dos_path));
+      return S_OK;
+    }
+  }
+
+  return HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE);
+}
+
+}  // namespace omaha
diff --git a/common/disk.h b/base/disk.h
similarity index 100%
rename from common/disk.h
rename to base/disk.h
diff --git a/base/disk_unittest.cc b/base/disk_unittest.cc
new file mode 100644
index 0000000..ae7a1d2
--- /dev/null
+++ b/base/disk_unittest.cc
@@ -0,0 +1,74 @@
+// Copyright 2004-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <shlobj.h>
+#include <psapi.h>
+
+#include "omaha/base/app_util.h"
+#include "omaha/base/const_utils.h"
+#include "omaha/base/disk.h"
+#include "omaha/base/file.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(DiskTest, GetFreeDiskSpace) {
+  uint64 bytes_available = 0;
+  EXPECT_SUCCEEDED(GetFreeDiskSpace(_T("C:\\"), &bytes_available));
+
+  bytes_available = 0;
+  EXPECT_SUCCEEDED(GetFreeDiskSpace(CSIDL_PROGRAM_FILES, &bytes_available));
+}
+
+TEST(DiskTest, Disk_SystemDrive) {
+  TCHAR system_drive[MAX_PATH] = _T("%SystemDrive%");
+  EXPECT_TRUE(::ExpandEnvironmentStrings(system_drive, system_drive, MAX_PATH));
+  EXPECT_TRUE(::PathAddBackslash(system_drive));
+
+  if (vista_util::IsUserAdmin()) {
+    // System drive should not be hot-pluggable
+    EXPECT_FALSE(IsHotPluggable(system_drive));
+  }
+
+  // System drive is expected to be > 4GB.
+  EXPECT_TRUE(IsLargeDrive(system_drive));
+}
+
+TEST(DiskTest, Disk_FirstLargeLocalDrive) {
+  // Preferred amount of disk space for data (choose first location if found).
+  // Ideally this would be 1 GB, but some test VMs have little free space.
+  const int kDesiredSpace = 100 * 1000 * 1000;  // 100 MB
+
+  CString drive;
+  EXPECT_SUCCEEDED(FindFirstLocalDriveWithEnoughSpace(kDesiredSpace, &drive));
+  EXPECT_TRUE(IsLargeDrive(drive));
+}
+
+// TODO(omaha): Make this work on mapped drives. See http://b/1076675.
+TEST(DiskTest, DISABLED_DevicePathToDosPath) {
+  TCHAR image_name[MAX_PATH] = _T("");
+  EXPECT_TRUE(::GetProcessImageFileName(::GetCurrentProcess(),
+                                        image_name,
+                                        arraysize(image_name)) != 0);
+
+  CString dos_name;
+  EXPECT_SUCCEEDED(DevicePathToDosPath(image_name, &dos_name));
+  EXPECT_TRUE(File::Exists(dos_name));
+
+  EXPECT_EQ(dos_name.CompareNoCase(app_util::GetCurrentModulePath()), 0);
+}
+
+}  // namespace omaha
diff --git a/base/dual_interface_switch.h b/base/dual_interface_switch.h
new file mode 100644
index 0000000..72dd933
--- /dev/null
+++ b/base/dual_interface_switch.h
@@ -0,0 +1,90 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// This class allows for implementing two dual interfaces, ReadOnly being a
+// read-only dual interface, and ReadWrite being a read-write dual interface.
+// Based on the value returned from is_read_only(), the ReadOnly or the
+// ReadWrite dual interface is exposed at runtime.
+//
+// An ATL class needs to
+// (a) derive from DualInterfaceSwitch
+//
+// (b) implement the is_read_only() method. For instance:
+//   bool is_read_only() { return is_read_only_; }
+//
+// (c) add entries similar to below for the dual interfaces as well as IDispatch
+//     to the COM map:
+//  BEGIN_COM_MAP(AppData)
+//    COM_INTERFACE_ENTRY2(IUnknown, IAppData)
+//    COM_INTERFACE_ENTRY_FUNC(__uuidof(IAppDataReadOnly), 0, DualInterfaceQI)
+//    COM_INTERFACE_ENTRY_FUNC(__uuidof(IAppData), 0, DualInterfaceQI)
+//    COM_INTERFACE_ENTRY_FUNC(__uuidof(IDispatch), 0, DualInterfaceQI)
+//  END_COM_MAP()
+
+#ifndef OMAHA_BASE_DUAL_INTERFACE_SWITCH_H_
+#define OMAHA_BASE_DUAL_INTERFACE_SWITCH_H_
+
+#include <windows.h>
+#include <atlbase.h>
+#include "base/basictypes.h"
+#include "omaha/base/debug.h"
+
+namespace omaha {
+
+template <class T, class ReadOnly, REFIID iid_read_only,
+          class ReadWrite, REFIID iid_read_write>
+class ATL_NO_VTABLE DualInterfaceSwitch : public ReadOnly,
+                                          public ReadWrite {
+ protected:
+  // DualInterfaceQI gives out either the ReadOnly or ReadWrite interface
+  // pointer, based on the value of is_read_only() that class T implements.
+  // The signature of this function has to be
+  //   HRESULT WINAPI func(void* pv, REFIID riid, LPVOID* ppv, DWORD dw);
+  // for it to work with COM_INTERFACE_ENTRY_FUNC.
+  static HRESULT WINAPI DualInterfaceQI(void* p,
+                                        REFIID riid,
+                                        LPVOID* v,
+                                        DWORD) {
+    // Ensure that ReadOnly and ReadWrite are derived from IDispatch.
+    ASSERT1(static_cast<IDispatch*>(reinterpret_cast<ReadOnly*>(1)));
+    ASSERT1(static_cast<IDispatch*>(reinterpret_cast<ReadWrite*>(1)));
+
+    if (riid != iid_read_only &&
+        riid != iid_read_write &&
+        riid != __uuidof(IDispatch)) {
+      // Returning S_FALSE allows COM map processing to continue. Another entry
+      // in the COM map might match the interface requested.
+      return S_FALSE;
+    }
+
+    T* t = reinterpret_cast<T*>(p);
+    if (t->is_read_only() && riid == iid_read_write) {
+      return S_FALSE;
+    }
+
+    t->AddRef();
+    if (t->is_read_only() || riid == iid_read_only) {
+      *v = static_cast<ReadOnly*>(t);
+      return S_OK;
+    }
+
+    *v = static_cast<ReadWrite*>(t);
+    return S_OK;
+  }
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_DUAL_INTERFACE_SWITCH_H_
diff --git a/base/dynamic_link_dbghelp.cc b/base/dynamic_link_dbghelp.cc
new file mode 100644
index 0000000..9430726
--- /dev/null
+++ b/base/dynamic_link_dbghelp.cc
@@ -0,0 +1,111 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// dynamic loading of dbghelp dll functions
+
+#include "omaha/base/debug.h"
+#include "omaha/base/dynamic_link_dbghelp.h"
+
+namespace omaha {
+
+BOOL (CALLBACK *Dbghelp::SymInitialize)(HANDLE, PCSTR, BOOL);
+BOOL (CALLBACK *Dbghelp::SymCleanup)(HANDLE);
+BOOL (CALLBACK *Dbghelp::SymEnumSymbols)(HANDLE, ULONG64, PCSTR, PSYM_ENUMERATESYMBOLS_CALLBACK, PVOID);
+DWORD (CALLBACK *Dbghelp::SymSetOptions)(DWORD);
+BOOL (CALLBACK *Dbghelp::SymSetContext)(HANDLE, PIMAGEHLP_STACK_FRAME, PIMAGEHLP_CONTEXT);
+BOOL (CALLBACK *Dbghelp::SymGetLineFromAddr)(HANDLE, DWORD, PDWORD, PIMAGEHLP_LINE);
+BOOL (CALLBACK *Dbghelp::SymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
+BOOL (CALLBACK *Dbghelp::SymFromAddr)(HANDLE, DWORD64, PDWORD64, PSYMBOL_INFO);
+BOOL (CALLBACK *Dbghelp::StackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64,
+        PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64);
+BOOL (CALLBACK *Dbghelp::StackWalk)(DWORD, HANDLE, HANDLE, LPSTACKFRAME, PVOID, PREAD_PROCESS_MEMORY_ROUTINE,
+        PFUNCTION_TABLE_ACCESS_ROUTINE, PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE);
+PVOID (CALLBACK *Dbghelp::SymFunctionTableAccess64)(HANDLE, DWORD64);
+PVOID (CALLBACK *Dbghelp::SymFunctionTableAccess)(HANDLE, DWORD);
+DWORD64 (CALLBACK *Dbghelp::SymGetModuleBase64)(HANDLE, DWORD64);
+DWORD64 (CALLBACK *Dbghelp::SymGetModuleBase)(HANDLE, DWORD);
+BOOL (CALLBACK *Dbghelp::SymGetTypeInfo)(HANDLE, DWORD64, ULONG, IMAGEHLP_SYMBOL_TYPE_INFO, PVOID);
+BOOL (CALLBACK *Dbghelp::MiniDumpWriteDump)(HANDLE, DWORD, HANDLE, MINIDUMP_TYPE, PMINIDUMP_EXCEPTION_INFORMATION,
+        PMINIDUMP_USER_STREAM_INFORMATION, PMINIDUMP_CALLBACK_INFORMATION);
+
+Dbghelp::LoadedState Dbghelp::loaded_state_ = Dbghelp::NOT_LOADED;
+HINSTANCE Dbghelp::library_ = NULL;
+
+template <typename T>
+bool Dbghelp::GPA(const char * function_name, T& function_pointer) {
+  ASSERT(function_name, (L""));
+
+  function_pointer = reinterpret_cast<T>(::GetProcAddress(library_,
+                                                          function_name));
+  return function_pointer != NULL;
+}
+
+HRESULT Dbghelp::Load() {
+  // If we've already tried to load, don't try again.
+  if (loaded_state_ != NOT_LOADED) goto Exit;
+
+  Clear();
+
+  // UTIL_LOG((L2, _T("dbghelp loading")));
+  library_ = ::LoadLibrary(_T("dbghelp"));
+  // UTIL_LOG((L2, _T("dbghelp loaded")));
+
+  if (!library_) return E_FAIL;
+
+  bool all_valid = (GPA("SymInitialize", SymInitialize))
+    & (GPA("SymCleanup", SymCleanup))
+    & (GPA("SymSetOptions", SymSetOptions))
+    & (GPA("SymGetLineFromAddr", SymGetLineFromAddr))
+    & (GPA("SymGetLineFromAddr64", SymGetLineFromAddr64))
+    & (GPA("StackWalk", StackWalk))
+    & (GPA("StackWalk64", StackWalk64))
+    & (GPA("SymFunctionTableAccess", SymFunctionTableAccess))
+    & (GPA("SymFunctionTableAccess64", SymFunctionTableAccess64))
+    & (GPA("SymGetModuleBase", SymGetModuleBase))
+    & (GPA("SymGetModuleBase64", SymGetModuleBase64))
+    & (GPA("MiniDumpWriteDump", MiniDumpWriteDump));
+
+  // These are not supported in the Win2k version of DbgHelp;
+  // failing to load them is not an error.
+  GPA("SymEnumSymbols", SymEnumSymbols);
+  GPA("SymGetTypeInfo", SymGetTypeInfo);
+  GPA("SymSetContext", SymSetContext);
+  GPA("SymFromAddr", SymFromAddr);
+
+  if (!all_valid) Unload();
+  loaded_state_ = all_valid ? LOAD_SUCCEEDED : LOAD_FAILED;
+
+ Exit:
+  return (loaded_state_ == LOAD_SUCCEEDED) ? S_OK : E_FAIL;
+}
+
+void Dbghelp::Unload() {
+  if (library_) {
+    VERIFY(::FreeLibrary(library_), (L""));
+
+    // Must set library_ to NULL so that Loaded() will return FALSE.
+    library_ = NULL;
+    }
+  Clear();
+}
+
+void Dbghelp::Clear() {
+  // just clear the main entry points
+  SymInitialize = NULL;
+  MiniDumpWriteDump = NULL;
+}
+
+}  // namespace omaha
+
diff --git a/common/dynamic_link_dbghelp.h b/base/dynamic_link_dbghelp.h
similarity index 100%
rename from common/dynamic_link_dbghelp.h
rename to base/dynamic_link_dbghelp.h
diff --git a/base/dynamic_link_kernel32.cc b/base/dynamic_link_kernel32.cc
new file mode 100644
index 0000000..302cc15
--- /dev/null
+++ b/base/dynamic_link_kernel32.cc
@@ -0,0 +1,108 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// dynamic loading of Windows kernel32.dll API dll functions
+// wrappers for win32 functions not supported on windows 95/98/ME
+
+#include "omaha/base/debug.h"
+#include "omaha/base/dynamic_link_kernel32.h"
+
+namespace omaha {
+
+typedef BOOL (WINAPI * Module32FirstFunc)(HANDLE, LPMODULEENTRY32);
+typedef BOOL (WINAPI * Module32NextFunc)(HANDLE, LPMODULEENTRY32);
+typedef BOOL (WINAPI * Process32FirstFunc)(HANDLE, LPPROCESSENTRY32);
+typedef BOOL (WINAPI * Process32NextFunc)(HANDLE, LPPROCESSENTRY32);
+typedef BOOL (WINAPI * IsWow64ProcessFunc)(HANDLE, PBOOL);
+
+#define kKernel32Module L"kernel32"
+
+BOOL WINAPI Kernel32::Module32First(HANDLE hSnapshot, LPMODULEENTRY32 lpme) {
+  static Module32FirstFunc f = NULL;
+
+  if (f == NULL) {
+    HMODULE handle = GetModuleHandle(kKernel32Module);
+    ASSERT(handle, (L""));
+    f = (Module32FirstFunc) GetProcAddress(handle, "Module32FirstW");
+  }
+
+  if (f == NULL)
+    return FALSE;
+
+  return f(hSnapshot, lpme);
+}
+
+BOOL WINAPI Kernel32::Module32Next(HANDLE hSnapshot, LPMODULEENTRY32 lpme) {
+  static Module32NextFunc f = NULL;
+
+  if (f == NULL) {
+    HMODULE handle = GetModuleHandle(kKernel32Module);
+    ASSERT(handle, (L""));
+    f = (Module32NextFunc) GetProcAddress(handle, "Module32NextW");
+  }
+
+  if (f == NULL)
+    return FALSE;
+
+  return f(hSnapshot, lpme);
+}
+
+BOOL WINAPI Kernel32::Process32First(HANDLE hSnapshot, LPPROCESSENTRY32 lppe) {
+  static Process32FirstFunc f = NULL;
+
+  if (f == NULL) {
+    HMODULE handle = GetModuleHandle(kKernel32Module);
+    ASSERT(handle, (L""));
+    f = (Process32FirstFunc) GetProcAddress(handle, "Process32FirstW");
+  }
+
+  if (f == NULL)
+    return FALSE;
+
+  return f(hSnapshot, lppe);
+}
+
+BOOL WINAPI Kernel32::Process32Next(HANDLE hSnapshot, LPPROCESSENTRY32 lppe) {
+  static Process32NextFunc f = NULL;
+
+  if (f == NULL) {
+    HMODULE handle = GetModuleHandle(kKernel32Module);
+    ASSERT(handle, (L""));
+    f = (Process32NextFunc) GetProcAddress(handle, "Process32NextW");
+  }
+
+  if (f == NULL)
+    return FALSE;
+
+  return f(hSnapshot, lppe);
+}
+
+BOOL WINAPI Kernel32::IsWow64Process(HANDLE hProcess, PBOOL Wow64Process) {
+  static IsWow64ProcessFunc f = NULL;
+
+  if (f == NULL) {
+    HMODULE handle = GetModuleHandle(kKernel32Module);
+    ASSERT(handle, (L""));
+    f = (IsWow64ProcessFunc) GetProcAddress(handle, "IsWow64Process");
+  }
+
+  if (f == NULL)
+    return FALSE;
+
+  return f(hProcess, Wow64Process);
+}
+
+}  // namespace omaha
+
diff --git a/common/dynamic_link_kernel32.h b/base/dynamic_link_kernel32.h
similarity index 100%
rename from common/dynamic_link_kernel32.h
rename to base/dynamic_link_kernel32.h
diff --git a/base/dynamic_link_kernel32_unittest.cc b/base/dynamic_link_kernel32_unittest.cc
new file mode 100644
index 0000000..80c3253
--- /dev/null
+++ b/base/dynamic_link_kernel32_unittest.cc
@@ -0,0 +1,30 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// dynamic link kernel 32 unittest
+
+#include "omaha/base/dynamic_link_kernel32.h"
+#include "omaha/base/system_info.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(DynamicLinkKernel32Test, DynamicLinkKernel32) {
+  BOOL is64(0);
+  ASSERT_TRUE(Kernel32::IsWow64Process(GetCurrentProcess(), &is64));
+  ASSERT_EQ(static_cast<BOOL>(SystemInfo::Is64BitWindows()), is64);
+}
+
+}  // namespace omaha
diff --git a/base/encrypt.cc b/base/encrypt.cc
new file mode 100644
index 0000000..46c368c
--- /dev/null
+++ b/base/encrypt.cc
@@ -0,0 +1,95 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include "omaha/base/encrypt.h"
+
+#include <vector>
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+
+namespace omaha {
+
+namespace encrypt {
+
+// TODO(omaha): consider loading crypt32.dll dynamically, as these functions
+// are used infrequently.
+
+HRESULT EncryptData(const void* key, size_t key_len,
+                    const void* data, size_t data_len,
+                    std::vector<uint8>* data_out) {
+  // key may be null.
+  ASSERT1(data);
+  ASSERT1(data_out);
+  DATA_BLOB blob_out = {0, NULL};
+  DATA_BLOB blob_in = { data_len, static_cast<BYTE*>(const_cast<void*>(data)) };
+  DATA_BLOB entropy = { 0, NULL };
+  if (key != NULL && key_len != 0) {
+    entropy.cbData = key_len;
+    entropy.pbData = static_cast<BYTE*>(const_cast<void*>(key));
+  }
+
+  // The description parameter is required on W2K.
+  if (!::CryptProtectData(&blob_in, _T("gupdate"), &entropy, NULL, NULL,
+                          CRYPTPROTECT_UI_FORBIDDEN, &blob_out)) {
+    return HRESULTFromLastError();
+  }
+
+  data_out->clear();
+  const uint8* first = reinterpret_cast<const uint8*>(blob_out.pbData);
+  const uint8* last = first + blob_out.cbData;
+  data_out->insert(data_out->begin(), first, last);
+  ::LocalFree(blob_out.pbData);
+
+  ASSERT1(data_out->size() == blob_out.cbData);
+  return S_OK;
+}
+
+HRESULT DecryptData(const void* key, size_t key_len,
+                    const void* data, size_t data_len,
+                    std::vector<uint8>* data_out) {
+  // key may be null.
+  ASSERT1(data);
+  ASSERT1(data_out);
+
+  DATA_BLOB blob_out = {0, NULL};
+  DATA_BLOB blob_in = { data_len, static_cast<BYTE*>(const_cast<void*>(data)) };
+  DATA_BLOB entropy = { 0, NULL };
+
+  if (key != NULL && key_len != 0) {
+    entropy.cbData = key_len;
+    entropy.pbData = static_cast<BYTE*>(const_cast<void*>(key));
+  }
+
+  if (!::CryptUnprotectData(&blob_in, NULL, &entropy, NULL, NULL,
+                            CRYPTPROTECT_UI_FORBIDDEN, &blob_out)) {
+    return (::GetLastError() != ERROR_SUCCESS) ?
+        HRESULTFromLastError() : HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+  }
+
+  data_out->clear();
+  const uint8* first = reinterpret_cast<const uint8*>(blob_out.pbData);
+  const uint8* last = first + blob_out.cbData;
+  data_out->insert(data_out->begin(), first, last);
+  ::LocalFree(blob_out.pbData);
+
+  ASSERT1(data_out->size() == blob_out.cbData);
+  return S_OK;
+}
+
+}  // namespace encrypt
+
+}  // namespace omaha
+
diff --git a/common/encrypt.h b/base/encrypt.h
similarity index 100%
rename from common/encrypt.h
rename to base/encrypt.h
diff --git a/base/encrypt_test.cc b/base/encrypt_test.cc
new file mode 100644
index 0000000..c37bee9
--- /dev/null
+++ b/base/encrypt_test.cc
@@ -0,0 +1,57 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <omaha/base/encrypt.h>
+#include <cstring>
+#include <vector>
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace encrypt {
+
+TEST(EncryptTest, Test) {
+  const char* plaintext = "the quick brown fox jumps over the lazy dog";
+  std::vector<uint8> ciphertext;
+  EXPECT_HRESULT_SUCCEEDED(EncryptData(NULL, 0,
+                                       plaintext, strlen(plaintext),
+                                       &ciphertext));
+  std::vector<uint8> decrypted_text;
+  EXPECT_HRESULT_SUCCEEDED(DecryptData(NULL, 0,
+                                       &ciphertext.front(), ciphertext.size(),
+                                       &decrypted_text));
+  EXPECT_EQ(decrypted_text.size(), strlen(plaintext));
+  decrypted_text.push_back(0);
+  EXPECT_STREQ(reinterpret_cast<char*>(&decrypted_text.front()), plaintext);
+
+
+  const char* key = "foobar";
+  ciphertext.clear();
+  EXPECT_HRESULT_SUCCEEDED(EncryptData(key, strlen(key),
+                                       plaintext, strlen(plaintext),
+                                       &ciphertext));
+  decrypted_text.clear();
+  EXPECT_HRESULT_SUCCEEDED(DecryptData(key, strlen(key),
+                                       &ciphertext.front(), ciphertext.size(),
+                                       &decrypted_text));
+  EXPECT_EQ(decrypted_text.size(), strlen(plaintext));
+  decrypted_text.push_back(0);
+  EXPECT_STREQ(reinterpret_cast<char*>(&decrypted_text.front()), plaintext);
+}
+
+}  // namespace encrypt
+
+}  // namespace omaha
+
diff --git a/base/error.cc b/base/error.cc
new file mode 100644
index 0000000..e0a568e
--- /dev/null
+++ b/base/error.cc
@@ -0,0 +1,57 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/error.h"
+#include <winhttp.h>
+#include "base/basictypes.h"
+#include "omaha/base/crash_if_specific_error.h"
+#include "omaha/base/debug.h"
+
+namespace omaha {
+
+int g_error_extra_code1 = 0;
+
+int error_extra_code1() {
+  return g_error_extra_code1;
+}
+
+void set_error_extra_code1(int extra_code) {
+  g_error_extra_code1 = extra_code;
+}
+
+HRESULT HRESULTFromLastError() {
+  DWORD error_code = ::GetLastError();
+  ASSERT1(error_code != NO_ERROR);
+  HRESULT hr = error_code != NO_ERROR ? HRESULT_FROM_WIN32(error_code) : E_FAIL;
+  CRASH_IF_SPECIFIC_ERROR(hr);
+  return hr;
+}
+
+HRESULT HRESULTFromHttpStatusCode(int status_code) {
+  COMPILE_ASSERT(GOOPDATE_E_NETWORK_FIRST + HTTP_STATUS_LAST <
+                 GOOPDATE_E_NETWORK_LAST,
+                 GOOPDATE_E_NETWORK_LAST_too_small);
+  HRESULT hr = S_OK;
+  if (HTTP_STATUS_FIRST <= status_code && status_code <= HTTP_STATUS_LAST) {
+    hr = GOOPDATE_E_NETWORK_FIRST + status_code;
+  } else {
+    hr = GOOPDATE_E_NETWORK_LAST;
+  }
+  ASSERT1(HRESULT_FACILITY(hr) == FACILITY_ITF);
+  return hr;
+}
+
+}  // namespace omaha
+
diff --git a/base/error.h b/base/error.h
new file mode 100644
index 0000000..fad4aa0
--- /dev/null
+++ b/base/error.h
@@ -0,0 +1,481 @@
+// Copyright 2003-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Error codes and HRESULTS
+//
+// For all new error codes, please use the macro MAKE_OMAHA_HRESULT instead of
+// MAKE_HRESULT. This macro sets both the custom facility code of kFacilityOmaha
+// and the C bit (0x20000000) to indicate it is a customer code.
+//
+// TODO(omaha): reduce the number of custom error codes below by searching and
+// seeing what is handled and what is not.
+//
+// TODO(omaha): rename CI and GOOPDATE to OMAHA.
+
+#ifndef OMAHA_BASE_ERROR_H_
+#define OMAHA_BASE_ERROR_H_
+
+#include <windows.h>
+
+namespace omaha {
+
+//
+// Exception Codes.
+//
+#define EXCEPTION_IMPERSONATION_FAILED          0x1
+#define EXCEPTION_REVERT_IMPERSONATION_FAILED   0x2
+
+//
+// HRESULT Functions.
+//
+
+// Returns the last error as an HRESULT or E_FAIL if last error is NO_ERROR.
+// This is not a drop in replacement for the HRESULT_FROM_WIN32 macro.
+// The macro maps a NO_ERROR to S_OK, whereas the function below maps a
+// NO_ERROR to E_FAIL. Also, the macro is evaluating arguments multiple times.
+HRESULT HRESULTFromLastError();
+
+// Returns the http status_code as an HRESULT.
+HRESULT HRESULTFromHttpStatusCode(int status_code);
+
+//
+// HRESULTs.
+//
+// Top bit indicates success (0) or failure (1)
+// 16 bits available for 'code' field at end
+
+const ULONG kHresultCBit   = 0x20000000;
+const ULONG kFacilityOmaha = 67;
+
+#define MAKE_OMAHA_HRESULT(sev, code)                    \
+    ((HRESULT)(((ULONG)(sev) << 31) |            \
+               kHresultCBit |                            \
+               (kFacilityOmaha << 16) |                  \
+               ((ULONG)(code))))
+
+#define CI_E_NOT_ENOUGH_DISK_SPACE                \
+    MAKE_HRESULT(SEVERITY_ERROR, kFacilityOmaha, 0x0003)
+
+// CI_E_INVALID_MANIFEST is returned when the manifest file is missing a
+// required field or has some other kind of semantic error
+#define CI_E_INVALID_MANIFEST                     \
+    MAKE_HRESULT(SEVERITY_ERROR, kFacilityOmaha, 0x0012)
+
+// CI_E_XML_LOAD_ERROR is returned when MSXML can't load the document into DOM.
+// It may be because the document is not well formed (tags, namespaces, etc)
+#define CI_E_XML_LOAD_ERROR                       \
+    MAKE_HRESULT(SEVERITY_ERROR, kFacilityOmaha, 0x0013)
+
+// CI_E_INVALID_ARG is returned when invalid command line arugment is specified
+#define CI_E_INVALID_ARG                          \
+    MAKE_HRESULT(SEVERITY_ERROR, kFacilityOmaha, 0x001B)
+
+// CI_E_PROXY_AUTH_REQUIRED is returned when proxy authentication is required
+#define CI_E_PROXY_AUTH_REQUIRED                  \
+    MAKE_HRESULT(SEVERITY_ERROR, kFacilityOmaha, 0x0028)
+
+// CI_E_INVALID_PROXY_AUTH_SCHEME is returned when the proxy authentication
+// scheme is unknown or not supported
+#define CI_E_INVALID_PROXY_AUTH_SCHEME            \
+    MAKE_HRESULT(SEVERITY_ERROR, kFacilityOmaha, 0x0029)
+
+// CI_E_BITS_DISABLED is returned by the download manager when the BITS service
+// is not enabled.
+#define CI_E_BITS_DISABLED                        \
+    MAKE_HRESULT(SEVERITY_ERROR, kFacilityOmaha, 0x0030)
+
+// CI_E_HTTPS_CERT_FAILURE is returned when the https connection fails.
+// One cause of this is when the system clock is off by a significant
+// amount which makes the server certificate appear invalid.
+#define CI_E_HTTPS_CERT_FAILURE                   \
+    MAKE_HRESULT(SEVERITY_ERROR, kFacilityOmaha, 0x0035)
+
+// Return values from Process::WaitUntilDeadOrInterrupt
+// TODO(omaha) Move this constants to the Process class. They do not look like
+// error codes.
+#define CI_S_PROCESSWAIT_DEAD                     \
+    MAKE_HRESULT(SEVERITY_SUCCESS, kFacilityOmaha, 0x0100)
+#define CI_S_PROCESSWAIT_TIMEOUT                  \
+    MAKE_HRESULT(SEVERITY_SUCCESS, kFacilityOmaha, 0x0101)
+#define CI_S_PROCESSWAIT_MESSAGE                  \
+    MAKE_HRESULT(SEVERITY_SUCCESS, kFacilityOmaha, 0x0102)
+
+// Signatures error codes.
+#define SIGS_E_INVALID_PFX_CERTIFICATE            \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x0200)
+#define SIGS_E_INVALID_DER_CERTIFICATE            \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x0201)
+#define SIGS_E_INVALID_PASSWORD                   \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x0202)
+#define SIGS_E_INVALID_KEY_TYPE                   \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x0203)
+#define SIGS_E_INVALID_SIGNATURE                  \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x0204)
+
+// Goopdate XML parser error codes.
+#define GOOPDATEXML_E_STRTOUINT                   \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x300)
+#define GOOPDATEXML_E_RESPONSENODE                \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x301)
+#define GOOPDATEXML_E_XMLVERSION                  \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x302)
+#define GOOPDATEXML_E_NEEDSADMIN                  \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x303)
+#define GOOPDATEXML_E_UNEXPECTED_URI              \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x304)
+#define GOOPDATEXML_E_TOO_MANY_APPS               \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x305)
+#define GOOPDATEXML_E_PARSE_ERROR                 \
+    MAKE_OMAHA_HRESULT(SEVERITY_ERROR, 0x306)
+
+// Goopdate job queue error codes.
+// Errors 0x401 - 0x407 are legacy codes and should not be reused.
+
+// Download Manager custom error codes.
+// Obsolete: GOOPDATEDOWNLOAD_E_FILE_ALREADY_DOWNLOADED - 0x500.
+// Obsolete: GOOPDATEDOWNLOAD_E_FAILED_GET_ERROR - 0x501.
+#define GOOPDATEDOWNLOAD_E_INVALID_PATH             \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x502)
+#define GOOPDATEDOWNLOAD_E_CRACKURL_FAILED          \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x503)
+#define GOOPDATEDOWNLOAD_E_FILE_NAME_EMPTY          \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x504)
+#define GOOPDATEDOWNLOAD_E_DEST_FILE_PATH_EMPTY     \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x505)
+#define GOOPDATEDOWNLOAD_E_STORAGE_DIR_NOT_EXIST    \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x506)
+#define GOOPDATEDOWNLOAD_E_FILE_SIZE_ZERO           \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x507)
+#define GOOPDATEDOWNLOAD_E_FILE_SIZE_SMALLER        \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x508)
+#define GOOPDATEDOWNLOAD_E_FILE_SIZE_LARGER         \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x509)
+#define GOOPDATEDOWNLOAD_E_UNIQUE_FILE_PATH_EMPTY   \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x50A)
+#define GOOPDATEDOWNLOAD_E_DEST_PATH_EMPTY          \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x50B)
+#define GOOPDATEDOWNLOAD_E_DEST_FILENAME_EMPTY      \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x50C)
+
+// The file could not be cached.
+#define GOOPDATEDOWNLOAD_E_CACHING_FAILED           \
+    MAKE_OMAHA_HRESULT(SEVERITY_ERROR, 0x50D)
+
+#define GOOPDATEDOWNLOAD_E_FAILED_MOVE              \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x5FF)
+
+// Goopdate custom error codes.
+// Obsolete: GOOPDATE_E_NON_ADMINS_CANNOT_INSTALL_ADMIN - 0x600.
+// Obsolete: GOOPDATE_S_WORKER_ALREADY_RUNNING  - 0x601.
+// Obsolete: GOOPDATE_E_INVALID_ARG_COMBINATION - 0x602.
+#define GOOPDATE_E_UA_ALREADY_RUNNING               \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x603)
+#define GOOPDATE_E_APP_BEING_INSTALLED              \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x604)
+#define GOOPDATE_E_RESOURCE_DLL_PATH_EMPTY          \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x605)
+#define GOOPDATE_E_NO_ARGS                          \
+    MAKE_OMAHA_HRESULT(SEVERITY_ERROR, 0x606)
+#define GOOPDATE_E_SHUTDOWN_SIGNALED                \
+    MAKE_OMAHA_HRESULT(SEVERITY_ERROR, 0x607)
+
+// Setup and metainstaller custom error codes.
+#define GOOPDATE_E_NONADMIN_INSTALL_ADMIN_APP       \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x700)
+// Obsolete: GOOPDATE_E_MANIFEST_FILENAME_EMPTY - 0x701.
+// Obsolete: GOOPDATE_E_MANIFEST_FILE_DOES_NOT_EXIST - 0x702.
+// This error appears in the metainstaller.
+#define GOOPDATE_E_RUNNING_INFERIOR_WINDOWS         \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x703)
+#define GOOPDATE_E_RUNNING_INFERIOR_MSXML           \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x704)
+// Obsolete: GOOPDATE_E_ELEVATION_FAILED     - 0x705
+#define GOOPDATE_E_FAILED_TO_GET_LOCK               \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x706)
+#define GOOPDATE_E_INSTANCES_RUNNING                \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x707)
+#define GOOPDATE_E_HANDOFF_FAILED                   \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x708)
+#define GOOPDATE_E_PATH_APPEND_FAILED               \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x709)
+// Obsolete: GOOPDATE_E_SERVICE_NAME_EMPTY     - 0x70a
+#define GOOPDATE_E_SETUP_LOCK_INIT_FAILED           \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x70b)
+#define GOOPDATE_E_ACCESSDENIED_COPYING_CORE_FILES  \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x70c)
+#define GOOPDATE_E_ACCESSDENIED_COPYING_SHELL       \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x70d)
+#define GOOPDATE_E_ACCESSDENIED_STOP_PROCESSES      \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x70e)
+#define GOOPDATE_E_ACCESSDENIED_SETUP_REG_ACCESS    \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x70f)
+// User may have accidentally run the metainstaller twice.
+#define GOOPDATE_E_FAILED_TO_GET_LOCK_MATCHING_INSTALL_PROCESS_RUNNING  \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x710)
+#define GOOPDATE_E_FAILED_TO_GET_LOCK_NONMATCHING_INSTALL_PROCESS_RUNNING \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x711)
+#define GOOPDATE_E_FAILED_TO_GET_LOCK_UPDATE_PROCESS_RUNNING  \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x712)
+#define GOOPDATE_E_VERIFY_SIGNEE_IS_GOOGLE_FAILED   \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x713)
+// Obsolete: GOOPDATE_E_VERIFY_SIGNEE_IS_GOOGLE_FAILED_TIMESTAMP_CHECK - 0x714.
+#define GOOPDATE_E_INSTALL_ELEVATED_PROCESS_NEEDS_ELEVATION \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x715)
+#define GOOPDATE_E_ELEVATION_FAILED_ADMIN           \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x716)
+#define GOOPDATE_E_ELEVATION_FAILED_NON_ADMIN       \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x717)
+#define GOOPDATE_E_CANT_UNINSTALL                   \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x718)
+#define GOOPDATE_E_SILENT_INSTALL_NEEDS_ELEVATION   \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x719)
+#define GOOPDATE_E_OEM_NOT_MACHINE_AND_PRIVILEGED_AND_AUDIT_MODE  \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x71a)
+#define GOOPDATE_E_OEM_WITH_ONLINE_INSTALLER        \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x71b)
+// Obsolete: GOOPDATE_E_NON_OEM_INSTALL_IN_AUDIT_MODE - 0x71c
+#define GOOPDATE_E_OEM_INSTALL_SUCCEEDED_BUT_NOT_IN_OEM_INSTALLING_MODE \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x71d)
+#define GOOPDATE_E_EULA_REQURED_WITH_ONLINE_INSTALLER \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x71e)
+#define GOOPDATE_E_COM_LOCAL_SERVER_REGISTER_FAILED \
+    MAKE_OMAHA_HRESULT(SEVERITY_ERROR, 0x71f)
+#define GOOPDATE_E_USER_AND_ELEVATED_WITH_UAC_ON    \
+    MAKE_OMAHA_HRESULT(SEVERITY_ERROR, 0x720)
+#define GOOPDATE_E_FAILED_TO_GET_LOCK_UNINSTALL_PROCESS_RUNNING  \
+    MAKE_OMAHA_HRESULT(SEVERITY_ERROR, 0x721)
+#define GOOPDATE_E_POST_COPY_VERIFICATION_FAILED    \
+    MAKE_OMAHA_HRESULT(SEVERITY_ERROR, 0x722)
+
+// Metainstaller custom error codes.
+#define GOOPDATE_E_UNTAGGED_METAINSTALLER           \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x750)
+
+// COM Server error codes.
+// Obsolete: GOOPDATE_E_NO_SERVER_RESPONSE   - 0x800.
+#define GOOPDATE_E_NO_NETWORK \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x801)
+// Obsolete: GOOPDATE_E_UPDATE_CHECK_FAILED  - 0x802.
+// Obsolete: GOOPDATE_COULD_NOT_GET_IGOOGLEUPDATE - 0x803.
+// Obsolete: GOOPDATE_E_BAD_SERVER_RESPONSE  - 0x804.
+#define GOOPDATE_E_UNKNOWN_SERVER_RESPONSE          \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x805)
+#define GOOPDATE_E_RESTRICTED_SERVER_RESPONSE       \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x806)
+// Obsolete: GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED - 0x807.
+// Obsolete: GOOPDATE_E_ABANDON_UPDATE       - 0x808.
+#define GOOPDATE_E_NO_UPDATE_RESPONSE               \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x809)
+// Obsolete: GOOPDATE_E_BUNDLE_ERROR         - 0x80A.
+// Obsolete: GOOPDATE_E_APP_UNINSTALLED      - 0x80B.
+// Obsolete: GOOPDATE_E_CALL_INPROGRESS      - 0x80C.
+#define GOOPDATE_E_UNKNOWN_APP_SERVER_RESPONSE      \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x80D)
+#define GOOPDATE_E_INTERNAL_ERROR_SERVER_RESPONSE   \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x80E)
+#define GOOPDATE_E_NO_SERVER_RESPONSE               \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x80F)
+#define GOOPDATE_E_CANCELLED                 \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x810)
+#define GOOPDATE_E_OS_NOT_SUPPORTED                 \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x811)
+#define GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY   \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x812)
+#define GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY    \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x813)
+// Obsolete: GOOPDATE_E_APP_NOT_REGISTERED   - 0x814.
+#define GOOPDATE_E_CANNOT_USE_NETWORK               \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x815)
+#define GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED  \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x816)
+#define GOOPDATE_E_NON_BLOCKING_CALL_PENDING        \
+    MAKE_OMAHA_HRESULT(SEVERITY_ERROR, 0x817)
+#define GOOPDATE_E_INVALID_STATE_TRANSITION         \
+    MAKE_OMAHA_HRESULT(SEVERITY_ERROR, 0x818)
+#define GOOPDATE_E_CALL_UNEXPECTED                  \
+    MAKE_OMAHA_HRESULT(SEVERITY_ERROR, 0x819)
+// TODO(omaha): Rename other SERVER_RESPONSE errors to match these.
+#define GOOPDATE_E_SERVER_RESPONSE_NO_HASH          \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x81a)
+#define GOOPDATE_E_SERVER_RESPONSE_UNSUPPORTED_PROTOCOL \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x81b)
+#define GOOPDATE_E_UPDATE_DEFERRED                      \
+    MAKE_OMAHA_HRESULT(SEVERITY_ERROR, 0x81c)
+
+//
+// Network stack error codes.
+//
+
+// The CUP response is missing the ETag header containing the server proof.
+#define OMAHA_NET_E_CUP_NO_SERVER_PROOF            \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x880)
+
+// The CUP response is not trusted.
+#define OMAHA_NET_E_CUP_NOT_TRUSTED                \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x881)
+// Obsolete: OMAHA_NET_E_REQUEST_CANCELLED   - 0x882.
+
+// CUP could not instantiate an http client.
+#define OMAHA_NET_E_CUP_NO_HTTP_CLIENT              \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x883)
+
+// CUP could not generate random bytes.
+#define OMAHA_NET_E_CUP_NO_ENTROPY                  \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x884)
+
+// Non-CUP network errors.
+#define OMAHA_NET_E_WINHTTP_NOT_AVAILABLE           \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x890)
+
+// Install Manager custom error codes.
+#define GOOPDATEINSTALL_E_FILENAME_INVALID         \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x900)
+#define GOOPDATEINSTALL_E_INSTALLER_FAILED_START   \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x901)
+#define GOOPDATEINSTALL_E_INSTALLER_FAILED         \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x902)
+#define GOOPDATEINSTALL_E_INSTALLER_INTERNAL_ERROR \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x903)
+#define GOOPDATEINSTALL_E_INSTALLER_TIMED_OUT      \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x904)
+#define GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x905)
+#define GOOPDATEINSTALL_E_FAILED_INIT_INSTALLER_LOCK          \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x906)
+#define GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING         \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x907)
+#define GOOPDATEINSTALL_E_INSTALLER_DID_NOT_CHANGE_VERSION    \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x908)
+#define GOOPDATE_E_INVALID_INSTALL_DATA_INDEX                 \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x909)
+#define GOOPDATE_E_INVALID_INSTALLER_DATA_IN_APPARGS          \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x90A)
+#define GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH  \
+    MAKE_OMAHA_HRESULT(SEVERITY_ERROR, 0x90B)
+
+// GoopdateUtils custom error codes.
+#define GOOPDATEUTILS_E_BROWSERTYPE                 \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xA00)
+
+// GoogleUpdate.exe shell custom error codes.
+#define GOOGLEUPDATE_E_DLL_NOT_FOUND           \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xB00)
+#define GOOGLEUPDATE_E_VERIFY_SIGNEE_IS_GOOGLE_FAILED \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xB01)
+
+// Command line parse custom error codes.
+#define GOOGLEUPDATE_COMMANDLINE_E_NO_SCENARIO_HANDLER \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xC00)
+#define GOOGLEUPDATE_COMMANDLINE_E_NO_SCENARIO_HANDLER_MATCHED \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xC01)
+
+// OneClick custom error codes
+#define GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED        \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xD01)
+#define GOOPDATE_E_ONECLICK_LANGUAGE_NOT_SUPPORTED  \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xD02)
+
+// Usage stats / metrics error codes
+#define GOOPDATE_E_METRICS_LOCK_INIT_FAILED         \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xD80)
+#define GOOPDATE_E_METRICS_AGGREGATE_FAILED         \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xD81)
+
+// Core error codes
+#define GOOPDATE_E_CORE_INTERNAL_ERROR              \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xE00)
+#define GOOPDATE_E_CORE_MISSING_CMD                 \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xE01)
+
+// UI & Observer error codes
+#define GOOPDATE_E_UI_INTERNAL_ERROR                \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xE80)
+#define GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL  \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xE81)
+
+// ApplyTagTool error codes.
+#define APPLYTAG_E_ALREADY_TAGGED                   \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x1000)
+
+// The range [0x2000, 0x2400) is reserved for certain network stack errors
+// when the server returns an HTTP result code that is not a success code.
+// The size of the range is 1024, which is enough to map all the HTTP result
+// codes.
+#define GOOPDATE_E_NETWORK_FIRST                            \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x2000)
+
+// Http Status Code 401 -- Unauthorized.
+#define GOOPDATE_E_NETWORK_UNAUTHORIZED                     \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x2191)
+
+// Http Status Code 403 -- Forbidden.
+#define GOOPDATE_E_NETWORK_FORBIDDEN                        \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x2193)
+
+// Http Status Code 407 -- Proxy Authentication Required.
+#define GOOPDATE_E_NETWORK_PROXYAUTHREQUIRED                \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x2197)
+
+#define GOOPDATE_E_NETWORK_LAST                             \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x23FF)
+
+// Shared Memory Proxy error codes.
+#define GOOPDATE_E_INVALID_SHARED_MEMORY_PTR                \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x2401)
+#define GOOPDATE_E_INVALID_INTERFACE_MARSHAL_SIZE           \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x2402)
+
+// Crash handling error codes.
+
+// The crash reporting cannot start the crash server for
+// out-of-process crash handling.
+#define GOOPDATE_E_CRASH_START_SERVER_FAILED                 \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xFFFA)
+
+// The crash reporting cannot set the security descriptors for
+// a securable object.
+#define GOOPDATE_E_CRASH_SECURITY_FAILED                     \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xFFFB)
+
+// The crash reporting could not get the crash reports dir.
+#define GOOPDATE_E_CRASH_NO_DIR                             \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xFFFC)
+
+// The crash reporting failed due to the client side metering of the crashes.
+#define GOOPDATE_E_CRASH_THROTTLED                          \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xFFFD)
+
+// The crash reporting failed due to the server rejecting the crash.
+#define GOOPDATE_E_CRASH_REJECTED                           \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xFFFE)
+
+// Goopdate crash. The error code is returned when the process is terminated
+// due to a crash handled by breakpad.
+#define GOOPDATE_E_CRASH                            \
+    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xFFFF)
+
+
+// This is the end of the range for FACILITY_ITF errors. Do not define errors
+// below.
+
+// Gets or sets the value of the extra code. Use this feature to convey
+// additional error information in canary builds.
+int error_extra_code1();
+void set_error_extra_code1(int extra_code);
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_ERROR_H_
+
diff --git a/base/error_unittest.cc b/base/error_unittest.cc
new file mode 100644
index 0000000..91bfc2c
--- /dev/null
+++ b/base/error_unittest.cc
@@ -0,0 +1,37 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include "omaha/base/error.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(ErrorTest, HRESULTFromLastError) {
+  ::SetLastError(ERROR_ACCESS_DENIED);
+  EXPECT_EQ(HRESULTFromLastError(), E_ACCESSDENIED);
+
+  ::SetLastError(static_cast<DWORD>(E_INVALIDARG));
+  EXPECT_EQ(HRESULTFromLastError(), E_INVALIDARG);
+}
+
+TEST(ErrorTest, HRESULTFromLastErrorAssert) {
+  ExpectAsserts expect_asserts;
+  ::SetLastError(0);
+  EXPECT_EQ(HRESULTFromLastError(), E_FAIL);
+}
+
+}  // namespace omaha
+
diff --git a/base/etw_log_writer.cc b/base/etw_log_writer.cc
new file mode 100644
index 0000000..6925082
--- /dev/null
+++ b/base/etw_log_writer.cc
@@ -0,0 +1,135 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// ETW log writer implementation.
+
+#include "omaha/base/etw_log_writer.h"
+
+namespace omaha {
+
+// {9B18BFF9-915E-4cc1-9C3E-F4AC112CB36C}
+const GUID EtwLogWriter::kOmahaTraceGuid =
+    { 0x9b18bff9, 0x915e, 0x4cc1,
+        { 0x9c, 0x3e, 0xf4, 0xac, 0x11, 0x2c, 0xb3, 0x6c } };
+
+const GUID EtwLogWriter::kLogEventId =
+    { 0x7fe69228, 0x633e, 0x4f06,
+        { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } };
+
+EtwLogWriter::EtwLogWriter(const GUID& provider_guid)
+    : EtwTraceProvider(provider_guid),
+      rtl_capture_stack_backtrace_(NULL) {
+  HMODULE kernel32 = ::GetModuleHandle(L"kernel32.dll");
+  if (kernel32 != NULL) {
+    rtl_capture_stack_backtrace_ =
+        reinterpret_cast<RtlCaptureStackBackTraceFunc>(
+            ::GetProcAddress(kernel32, "RtlCaptureStackBackTrace"));
+  }
+
+  EtwTraceProvider::Register();
+}
+
+void EtwLogWriter::Cleanup() {
+  EtwTraceProvider::Unregister();
+}
+
+EtwLogWriter* EtwLogWriter::Create() {
+  return new EtwLogWriter(kOmahaTraceGuid);
+}
+
+bool EtwLogWriter::WantsToLogRegardless() const {
+  return session_handle() != 0;
+}
+
+bool EtwLogWriter::IsCatLevelEnabled(LogCategory category,
+                                     LogLevel level) const {
+  if ((enable_flags() & CategoryToEnableFlag(category)) == 0 ||
+      enable_level() < LogLevelToTraceLevel(level))
+    return false;
+
+  return true;
+}
+
+void EtwLogWriter::OutputMessage(const OutputInfo* output_info) {
+  if (!IsCatLevelEnabled(output_info->category, output_info->level))
+    return;
+
+  CStringA msg1(output_info->msg1);
+  CStringA msg2(output_info->msg2);
+  EtwEventLevel level = LogLevelToTraceLevel(output_info->level);
+  if (enable_flags() & kCaptureStackTraceMask) {
+    void* back_trace[32] = {0};
+    DWORD depth = 0;
+
+    if (rtl_capture_stack_backtrace_) {
+      depth = rtl_capture_stack_backtrace_(0,
+                                           arraysize(back_trace),
+                                           back_trace,
+                                           NULL);
+    }
+
+    EtwMofEvent<4> mof_event(kLogEventId, kLogMessageWithStackTraceType, level);
+    mof_event.SetField(0, sizeof(depth), &depth);
+    mof_event.SetField(1, depth * sizeof(back_trace[0]), &back_trace);
+    mof_event.SetField(2, msg1.GetLength(), msg1.GetString());
+    mof_event.SetField(3, msg2.GetLength() + 1, msg2.GetString());
+    Log(mof_event.get());
+  } else {
+    EtwMofEvent<2> mof_event(kLogEventId, kLogMessageType, level);
+    mof_event.SetField(0, msg1.GetLength(), msg1.GetString());
+    mof_event.SetField(1, msg2.GetLength() + 1, msg2.GetString());
+    Log(mof_event.get());
+  }
+}
+
+void EtwLogWriter::OnEventsEnabled() {
+  CORE_LOG(L2, (_T("ETW logging enabled")));
+}
+
+void EtwLogWriter::OnEventsDisabled() {
+  CORE_LOG(L2, (_T("ETW logging disabled")));
+}
+
+EtwEventFlags EtwLogWriter::CategoryToEnableFlag(LogCategory category) {
+  // Bit zero is reserved for the capture stack trace enable flag.
+  return 1 << (category + 1);
+}
+
+EtwEventLevel EtwLogWriter::LogLevelToTraceLevel(LogLevel level) {
+  switch (level) {
+    case LEVEL_FATALERROR:
+      return TRACE_LEVEL_FATAL;
+    case LEVEL_ERROR:
+      return TRACE_LEVEL_ERROR;
+    case LEVEL_WARNING:
+      return TRACE_LEVEL_WARNING;
+    case L1:
+    case L2:
+      return TRACE_LEVEL_INFORMATION;
+    case L3:
+    case L4:
+    case L5:
+    case L6:
+      return TRACE_LEVEL_VERBOSE;
+
+    case LEVEL_ALL:
+    default:
+      return TRACE_LEVEL_NONE;
+  }
+
+  // NOTREACHED
+}
+
+}  // namespace omaha
diff --git a/base/etw_log_writer.h b/base/etw_log_writer.h
new file mode 100644
index 0000000..0aae9c9
--- /dev/null
+++ b/base/etw_log_writer.h
@@ -0,0 +1,84 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// A log writer that is controlled, and outputs to Event Tracing for Windows.
+
+#ifndef OMAHA_BASE_ETW_LOG_WRITER_H_
+#define OMAHA_BASE_ETW_LOG_WRITER_H_
+
+#include "omaha/base/event_trace_provider.h"
+#include "omaha/base/logging.h"
+
+namespace omaha {
+
+class EtwLogWriter : public LogWriter, public EtwTraceProvider {
+ public:
+  // The Omaha trace provider's GUID.
+  static const GUID kOmahaTraceGuid;
+
+  // The event ID for the event types below.
+  static const GUID kLogEventId;
+
+  // The event type for a simple UTF-8 zero-terminated message event.
+  static const EtwEventType kLogMessageType = 10;
+
+  // The event type for a log message with a stack trace, followed by the
+  // zero-terminated UTF-8 message text.
+  static const EtwEventType kLogMessageWithStackTraceType = 11;
+
+  // The lowest-order enable flags bit turn on stack trace capture.
+  // The remaining enable bits correspond to log categories, starting
+  // from LC_LOGGING through LC_MAX_CAT - 1.
+  static const EtwEventFlags kCaptureStackTraceMask = 0x0001;
+
+  // LogWriter overrides.
+  virtual bool WantsToLogRegardless() const;
+  virtual bool IsCatLevelEnabled(LogCategory category, LogLevel level) const;
+  virtual void OutputMessage(const OutputInfo* output_info);
+
+  // Factory for new instances.
+  static EtwLogWriter* Create();
+
+  // Convert a log category to the corresponding ETW enable flag.
+  static EtwEventFlags CategoryToEnableFlag(LogCategory category);
+
+  // Convert from a log level to the corresponding ETW trace level.
+  static EtwEventLevel LogLevelToTraceLevel(LogLevel level);
+
+ protected:
+  explicit EtwLogWriter(const GUID& provider_guid);
+  virtual void Cleanup();
+
+  // Override from EtwTraceProvider.
+  virtual void OnEventsEnabled();
+  virtual void OnEventsDisabled();
+
+ private:
+  // The CaptureStackBackTrace function is only available as of Windows XP,
+  // and is only declared in SDK headers as of the Vista SDK. To uncomplicate
+  // things we get at the function through GetProcAddress.
+  typedef WORD (NTAPI* RtlCaptureStackBackTraceFunc)(DWORD frames_to_skip,
+                                                     DWORD frames_to_capture,
+                                                     PVOID* backtrace,
+                                                     PDWORD backtrace_hash);
+
+  RtlCaptureStackBackTraceFunc rtl_capture_stack_backtrace_;
+
+  DISALLOW_COPY_AND_ASSIGN(EtwLogWriter);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_ETW_LOG_WRITER_H_
diff --git a/base/etw_log_writer_unittest.cc b/base/etw_log_writer_unittest.cc
new file mode 100644
index 0000000..0a5a0de
--- /dev/null
+++ b/base/etw_log_writer_unittest.cc
@@ -0,0 +1,563 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// ETW log writer unittests.
+
+#include <atlbase.h>
+#include <atlstr.h>
+#include <atlsync.h>
+#include "omaha/base/app_util.h"
+#include "omaha/base/etw_log_writer.h"
+#include "omaha/base/event_trace_consumer.h"
+#include "omaha/base/event_trace_controller.h"
+#include "omaha/testing/unit_test.h"
+
+
+namespace {
+
+using omaha::EtwEventType;
+using omaha::EtwLogWriter;
+using omaha::EtwTraceConsumerBase;
+using testing::StrEq;
+
+class TestConsumer : public EtwTraceConsumerBase<TestConsumer> {
+ public:
+  TestConsumer() {
+    EXPECT_EQ(NULL, s_current_);
+    s_current_ = this;
+  }
+
+  ~TestConsumer() {
+    EXPECT_TRUE(this == s_current_);
+    s_current_ = NULL;
+  }
+
+  MOCK_METHOD1(OnLogMessage, void(const char* msg));
+
+  void ProcessLogEvent(EtwEventType type, const void* data, size_t data_len) {
+    data_len;  // Unused
+    if (type == EtwLogWriter::kLogMessageType) {
+      OnLogMessage(reinterpret_cast<const char*>(data));
+    } else if (type == EtwLogWriter::kLogMessageWithStackTraceType) {
+      const DWORD* depth = reinterpret_cast<const DWORD*>(data);
+      void* const* stack_trace = reinterpret_cast<void* const*>(depth + 1);
+      OnLogMessage(reinterpret_cast<const char*>(stack_trace + *depth));
+    } else {
+      FAIL() << "Unexpected message type " << type;
+    }
+  }
+
+  static void ProcessEvent(EVENT_TRACE* event) {
+    if (event->Header.Guid == EtwLogWriter::kLogEventId) {
+      s_current_->ProcessLogEvent(event->Header.Class.Type,
+                                  event->MofData,
+                                  event->MofLength);
+    }
+  }
+
+ private:
+  static TestConsumer* s_current_;
+};
+
+TestConsumer* TestConsumer::s_current_ = NULL;
+
+const wchar_t kTestSessionName[] = L"EtwLogWriterTest Session";
+// {AD914B7A-0C5F-426e-895C-58B125408125}
+const GUID kTestProviderGuid = { 0xad914b7a, 0xc5f, 0x426e,
+    { 0x89, 0x5c, 0x58, 0xb1, 0x25, 0x40, 0x81, 0x25 } };
+
+// Subclass to allow using a distinct provider GUID and overriding
+// event handlers.
+class TestingLogWriter: public EtwLogWriter {
+ public:
+  TestingLogWriter() : EtwLogWriter(kTestProviderGuid) {
+    EXPECT_TRUE(events_enabled_.Create(NULL, TRUE, FALSE, NULL));
+    EXPECT_TRUE(events_disabled_.Create(NULL, TRUE, FALSE, NULL));
+  }
+
+  void WaitUntilEnabled() {
+    // Wait for a the callback to hit, then reset the event.
+    EXPECT_EQ(WAIT_OBJECT_0,
+              ::WaitForSingleObject(events_enabled_, INFINITE));
+    EXPECT_TRUE(::ResetEvent(events_enabled_));
+  }
+
+  void WaitUntilDisabled() {
+    // Wait for a the callback to hit, then reset the event.
+    EXPECT_EQ(WAIT_OBJECT_0,
+              ::WaitForSingleObject(events_disabled_, INFINITE));
+    EXPECT_TRUE(::ResetEvent(events_disabled_));
+  }
+
+  ~TestingLogWriter() {
+    // We explicitly call Cleanup() here so that the ETW provider will
+    // be torn down before the CEvent destructors are called.  We had
+    // issues with the test failing spuriously because the provider
+    // teardown is done on a separate thread, and would attempt to call
+    // OnEventsDisabled() after the event handles had been released.
+    // (http://b/2873205)
+
+    Cleanup();
+  }
+
+ protected:
+  // Override from EtwTraceProvider.
+  virtual void OnEventsEnabled() {
+    EtwLogWriter::OnEventsEnabled();
+    events_enabled_.Set();
+  }
+
+  virtual void OnEventsDisabled() {
+    EtwLogWriter::OnEventsDisabled();
+    events_disabled_.Set();
+  }
+
+  CEvent events_enabled_;
+  CEvent events_disabled_;
+};
+
+}  // namespace
+
+namespace omaha {
+
+class EtwLogWriterTest: public testing::Test {
+ public:
+  EtwLogWriterTest() : counter_(0) {
+  }
+
+  virtual void SetUp() {
+    // Kill any dangling trace session.
+    EtwTraceProperties prop;
+    EtwTraceController::Stop(kTestSessionName, &prop);
+
+    // And create a session on a new temp file.
+    EXPECT_TRUE(::GetTempFileName(app_util::GetTempDir(), _T("tmp"), 0,
+                                  CStrBuf(temp_file_, MAX_PATH)));
+
+    EXPECT_HRESULT_SUCCEEDED(
+        controller_.StartFileSession(kTestSessionName, temp_file_, false));
+  }
+
+  virtual void TearDown() {
+    if (controller_.session() != NULL) {
+      EXPECT_HRESULT_SUCCEEDED(controller_.Stop(NULL));
+      EXPECT_TRUE(::DeleteFile(temp_file_));
+    }
+  }
+
+  // Asserts that enabling ETW logging at trace_level causes
+  // logging at log_level to be enabled.
+  void ExpectLogLevelEnabled(EtwEventLevel trace_level, LogLevel log_level) {
+    TestingLogWriter writer;
+    EXPECT_HRESULT_SUCCEEDED(
+        controller_.EnableProvider(kTestProviderGuid,
+                                   trace_level,
+                                   0xFFFFFFFF)) <<
+    _T("[trace level: ") << static_cast<uint16>(trace_level) <<
+    _T("][log level: ") << log_level << _T("]");
+    writer.WaitUntilEnabled();
+    EXPECT_EQ(true, writer.IsCatLevelEnabled(LC_LOGGING, log_level));
+  }
+
+  // Asserts that enabling ETW logging at trace_level causes
+  // logging at log_level to be disabled.
+  void ExpectLogLevelDisabled(EtwEventLevel trace_level, LogLevel log_level) {
+    TestingLogWriter writer;
+    EXPECT_HRESULT_SUCCEEDED(
+        controller_.EnableProvider(kTestProviderGuid,
+                                   trace_level,
+                                   0xFFFFFFFF)) <<
+    _T("[trace level: ") << static_cast<uint16>(trace_level) <<
+    _T("][log level: ") << log_level << _T("]");
+    writer.WaitUntilEnabled();
+    EXPECT_EQ(false, writer.IsCatLevelEnabled(LC_LOGGING, log_level));
+  }
+
+  void ExpectLogMessage(EtwEventLevel trace_level,
+                        EtwEventFlags enable_bits,
+                        LogLevel log_level,
+                        LogCategory log_cat,
+                        bool should_log) {
+    TestingLogWriter writer;
+    EXPECT_HRESULT_SUCCEEDED(
+        controller_.EnableProvider(kTestProviderGuid,
+                                   trace_level,
+                                   enable_bits)) <<
+    _T("[trace level: ") << static_cast<uint16>(trace_level) <<
+    _T("][flags: 0x") << std::hex << enable_bits << std::dec <<
+    _T("][log level: ") << log_level <<
+    _T("][category: ") << log_cat <<
+    _T("][should_log: ") << should_log << _T("]");
+    writer.WaitUntilEnabled();
+
+    CString message;
+    message.Format(L"[%d][%d][0x%08X][%d][%d]",
+                   ++counter_,  // Make each expectation call unique.
+                   trace_level,
+                   enable_bits,
+                   log_level,
+                   log_cat);
+    OutputInfo info(log_cat, log_level, L"[Prefix]", message);
+    writer.OutputMessage(&info);
+
+    CStringA expected_msg("[Prefix]");
+    expected_msg += message;
+    // Set up an expectation for zero or one calls with this string,
+    // depending whether should_log or not.
+    EXPECT_CALL(consumer_, OnLogMessage(StrEq(expected_msg.GetString())))
+        .Times(should_log ? 1 : 0);
+  }
+
+  void ConsumeAndRestartLog() {
+    EXPECT_HRESULT_SUCCEEDED(controller_.Stop(NULL));
+
+    EXPECT_HRESULT_SUCCEEDED(consumer_.OpenFileSession(temp_file_));
+    EXPECT_HRESULT_SUCCEEDED(consumer_.Consume());
+    EXPECT_HRESULT_SUCCEEDED(consumer_.Close());
+
+    EXPECT_TRUE(::DeleteFile(temp_file_));
+
+    // Restart the log collection.
+    EXPECT_HRESULT_SUCCEEDED(
+        controller_.StartFileSession(kTestSessionName, temp_file_, false));
+  }
+
+ protected:
+  EtwTraceController controller_;
+  TestConsumer consumer_;
+  CString temp_file_;
+  int counter_;
+};
+
+TEST_F(EtwLogWriterTest, ProviderNotEnabled) {
+  // Verify that everything is turned off when the provider is not enabled.
+  TestingLogWriter writer;
+
+  EXPECT_FALSE(writer.WantsToLogRegardless());
+  for (int cat = LC_LOGGING; cat < LC_MAX_CAT; ++cat) {
+    for (int level = LEVEL_FATALERROR; level < LEVEL_ALL; ++level) {
+      EXPECT_EQ(false, writer.IsCatLevelEnabled(static_cast<LogCategory>(cat),
+                                                static_cast<LogLevel>(level)));
+    }
+  }
+}
+
+TEST_F(EtwLogWriterTest, ProviderEnableFlags) {
+  // Test that various provider enable flags have the expected effect on
+  // IsCatLevelEnabled.
+  for (int cat = LC_LOGGING; cat < LC_MAX_CAT; ++cat) {
+    EtwEventFlags flags = 1 << (cat + 1);
+
+    TestingLogWriter writer;
+    EXPECT_HRESULT_SUCCEEDED(
+        controller_.EnableProvider(kTestProviderGuid,
+                                   TRACE_LEVEL_INFORMATION,
+                                   flags)) <<
+    _T("cat[") << cat << _T("]: 0x") << std::hex << flags;
+    writer.WaitUntilEnabled();
+
+    for (int probe = LC_LOGGING; probe < LC_MAX_CAT; ++probe) {
+      bool category_enabled = probe == cat;
+      EXPECT_EQ(category_enabled,
+                writer.IsCatLevelEnabled(static_cast<LogCategory>(probe),
+                                         LEVEL_ERROR));
+    }
+  }
+}
+
+// On Windows XP, it appears that 32 enable/disable trace level operations
+// saturates some sort of a buffer, which causes the subsequent enable/disable
+// operations to fail. To work around this, the following tests are chopped up
+// into unnaturally small pieces.
+TEST_F(EtwLogWriterTest, ProviderLevel) {
+  // Test that various trace levels have the expected effect on
+  // IsCatLevelEnabled.
+
+  // TRACE_LEVEL_NONE
+  ExpectLogLevelDisabled(TRACE_LEVEL_NONE, LEVEL_FATALERROR);
+  ExpectLogLevelDisabled(TRACE_LEVEL_NONE, LEVEL_ERROR);
+  ExpectLogLevelDisabled(TRACE_LEVEL_NONE, LE);
+  ExpectLogLevelDisabled(TRACE_LEVEL_NONE, LEVEL_WARNING);
+  ExpectLogLevelDisabled(TRACE_LEVEL_NONE, LW);
+  ExpectLogLevelDisabled(TRACE_LEVEL_NONE, L1);
+  ExpectLogLevelDisabled(TRACE_LEVEL_NONE, L2);
+  ExpectLogLevelDisabled(TRACE_LEVEL_NONE, L3);
+  ExpectLogLevelDisabled(TRACE_LEVEL_NONE, L4);
+  ExpectLogLevelDisabled(TRACE_LEVEL_NONE, L5);
+  ExpectLogLevelDisabled(TRACE_LEVEL_NONE, L6);
+  ConsumeAndRestartLog();
+
+  if (!ShouldRunLargeTest()) {
+    // This test takes about 6 seconds, so only run part of it by default.
+    return;
+  }
+
+  // TRACE_LEVEL_FATAL
+  ExpectLogLevelEnabled(TRACE_LEVEL_FATAL, LEVEL_FATALERROR);
+  ExpectLogLevelDisabled(TRACE_LEVEL_FATAL, LEVEL_ERROR);
+  ExpectLogLevelDisabled(TRACE_LEVEL_FATAL, LE);
+  ExpectLogLevelDisabled(TRACE_LEVEL_FATAL, LEVEL_WARNING);
+  ExpectLogLevelDisabled(TRACE_LEVEL_FATAL, LW);
+  ExpectLogLevelDisabled(TRACE_LEVEL_FATAL, L1);
+  ExpectLogLevelDisabled(TRACE_LEVEL_FATAL, L2);
+  ExpectLogLevelDisabled(TRACE_LEVEL_FATAL, L3);
+  ExpectLogLevelDisabled(TRACE_LEVEL_FATAL, L4);
+  ExpectLogLevelDisabled(TRACE_LEVEL_FATAL, L5);
+  ExpectLogLevelDisabled(TRACE_LEVEL_FATAL, L6);
+  ConsumeAndRestartLog();
+
+  // TRACE_LEVEL_ERROR
+  ExpectLogLevelEnabled(TRACE_LEVEL_ERROR, LEVEL_FATALERROR);
+  ExpectLogLevelEnabled(TRACE_LEVEL_ERROR, LEVEL_ERROR);
+  ExpectLogLevelEnabled(TRACE_LEVEL_ERROR, LE);
+  ExpectLogLevelDisabled(TRACE_LEVEL_ERROR, LEVEL_WARNING);
+  ExpectLogLevelDisabled(TRACE_LEVEL_ERROR, LW);
+  ExpectLogLevelDisabled(TRACE_LEVEL_ERROR, L1);
+  ExpectLogLevelDisabled(TRACE_LEVEL_ERROR, L2);
+  ExpectLogLevelDisabled(TRACE_LEVEL_ERROR, L3);
+  ExpectLogLevelDisabled(TRACE_LEVEL_ERROR, L4);
+  ExpectLogLevelDisabled(TRACE_LEVEL_ERROR, L5);
+  ExpectLogLevelDisabled(TRACE_LEVEL_ERROR, L6);
+  ConsumeAndRestartLog();
+
+  // TRACE_LEVEL_WARNING
+  ExpectLogLevelEnabled(TRACE_LEVEL_WARNING, LEVEL_FATALERROR);
+  ExpectLogLevelEnabled(TRACE_LEVEL_WARNING, LEVEL_ERROR);
+  ExpectLogLevelEnabled(TRACE_LEVEL_WARNING, LE);
+  ExpectLogLevelEnabled(TRACE_LEVEL_WARNING, LEVEL_WARNING);
+  ExpectLogLevelEnabled(TRACE_LEVEL_WARNING, LW);
+  ExpectLogLevelDisabled(TRACE_LEVEL_WARNING, L1);
+  ExpectLogLevelDisabled(TRACE_LEVEL_WARNING, L2);
+  ExpectLogLevelDisabled(TRACE_LEVEL_WARNING, L3);
+  ExpectLogLevelDisabled(TRACE_LEVEL_WARNING, L4);
+  ExpectLogLevelDisabled(TRACE_LEVEL_WARNING, L5);
+  ExpectLogLevelDisabled(TRACE_LEVEL_WARNING, L6);
+  ConsumeAndRestartLog();
+
+  // TRACE_LEVEL_INFORMATION
+  ExpectLogLevelEnabled(TRACE_LEVEL_INFORMATION, LEVEL_FATALERROR);
+  ExpectLogLevelEnabled(TRACE_LEVEL_INFORMATION, LEVEL_ERROR);
+  ExpectLogLevelEnabled(TRACE_LEVEL_INFORMATION, LE);
+  ExpectLogLevelEnabled(TRACE_LEVEL_INFORMATION, LEVEL_WARNING);
+  ExpectLogLevelEnabled(TRACE_LEVEL_INFORMATION, LW);
+  ExpectLogLevelEnabled(TRACE_LEVEL_INFORMATION, L1);
+  ExpectLogLevelEnabled(TRACE_LEVEL_INFORMATION, L2);
+  ExpectLogLevelDisabled(TRACE_LEVEL_INFORMATION, L3);
+  ExpectLogLevelDisabled(TRACE_LEVEL_INFORMATION, L4);
+  ExpectLogLevelDisabled(TRACE_LEVEL_INFORMATION, L5);
+  ExpectLogLevelDisabled(TRACE_LEVEL_INFORMATION, L6);
+  ConsumeAndRestartLog();
+
+  // TRACE_LEVEL_VERBOSE
+  ExpectLogLevelEnabled(TRACE_LEVEL_VERBOSE, LEVEL_FATALERROR);
+  ExpectLogLevelEnabled(TRACE_LEVEL_VERBOSE, LEVEL_ERROR);
+  ExpectLogLevelEnabled(TRACE_LEVEL_VERBOSE, LE);
+  ExpectLogLevelEnabled(TRACE_LEVEL_VERBOSE, LEVEL_WARNING);
+  ExpectLogLevelEnabled(TRACE_LEVEL_VERBOSE, LW);
+  ExpectLogLevelEnabled(TRACE_LEVEL_VERBOSE, L1);
+  ExpectLogLevelEnabled(TRACE_LEVEL_VERBOSE, L2);
+  ExpectLogLevelEnabled(TRACE_LEVEL_VERBOSE, L3);
+  ExpectLogLevelEnabled(TRACE_LEVEL_VERBOSE, L4);
+  ExpectLogLevelEnabled(TRACE_LEVEL_VERBOSE, L5);
+  ExpectLogLevelEnabled(TRACE_LEVEL_VERBOSE, L6);
+}
+
+TEST_F(EtwLogWriterTest, UpdateProviderLevel) {
+  // Test that changing ETW trace levels and flags causes a pre-existing
+  // ETWLogProvider to update its log levels per IsCatLevelEnabled.
+  TestingLogWriter writer;
+  for (int cat = LC_LOGGING; cat < LC_MAX_CAT; ++cat) {
+    EXPECT_FALSE(writer.IsCatLevelEnabled(static_cast<LogCategory>(cat),
+                                          LEVEL_WARNING));
+  }
+
+  // Turn logging on.
+  EXPECT_HRESULT_SUCCEEDED(
+      controller_.EnableProvider(kTestProviderGuid,
+                                 TRACE_LEVEL_INFORMATION,
+                                 0xFFFFFFFF));
+
+  // Wait for the callback to hit, then assert that logging is now enabled.
+  writer.WaitUntilEnabled();
+  for (int cat = LC_LOGGING; cat < LC_MAX_CAT; ++cat) {
+    EXPECT_TRUE(writer.IsCatLevelEnabled(static_cast<LogCategory>(cat),
+                                         LEVEL_WARNING));
+  }
+
+  // Turn down the enable mask, aka categories.
+  EXPECT_HRESULT_SUCCEEDED(
+      controller_.EnableProvider(kTestProviderGuid,
+                                 TRACE_LEVEL_INFORMATION,
+                                 0x0003));  // 3 is LC_LOGGING + stack traces.
+
+  // Wait for the callback to hit, then assert that logging is still enabled
+  // but only for the LC_LOGGING category.
+  writer.WaitUntilEnabled();
+  for (int cat = LC_LOGGING; cat < LC_MAX_CAT; ++cat) {
+    EXPECT_EQ(cat == LC_LOGGING,
+              writer.IsCatLevelEnabled(static_cast<LogCategory>(cat),
+                                       LEVEL_WARNING));
+  }
+
+  EXPECT_HRESULT_SUCCEEDED(controller_.DisableProvider(kTestProviderGuid));
+
+  // Wait for the callback to hit, then assert that logging is now disabled.
+  writer.WaitUntilDisabled();
+  for (int cat = LC_LOGGING; cat < LC_MAX_CAT; ++cat) {
+    EXPECT_FALSE(writer.IsCatLevelEnabled(static_cast<LogCategory>(cat),
+                                          LEVEL_WARNING));
+  }
+}
+
+TEST_F(EtwLogWriterTest, OutputMessageIsRobust) {
+  // Test that OutputMessage doesn't fall over on unexpected inputs.
+
+  // Turn logging on.
+  TestingLogWriter writer;
+  EXPECT_HRESULT_SUCCEEDED(
+      controller_.EnableProvider(kTestProviderGuid,
+                                 TRACE_LEVEL_INFORMATION,
+                                 0xFFFFFFFF));
+  writer.WaitUntilEnabled();
+
+  OutputInfo msg(LC_LOGGING, LW, L"TEST", NULL);
+  writer.OutputMessage(&msg);
+
+  msg.category = static_cast<LogCategory>(0xFF);
+  msg.level = static_cast<LogLevel>(0xFF);
+  msg.msg1 = NULL;
+  msg.msg2 = L"TEST";
+  writer.OutputMessage(&msg);
+}
+
+TEST_F(EtwLogWriterTest, OutputMessageOnlyLevelsEnabled) {
+#define EXPECT_LOG(trace_level, enable_bits, log_level, log_cat) \
+  ExpectLogMessage(trace_level, enable_bits, log_level, log_cat, true);
+#define EXPECT_NO_LOG(trace_level, enable_bits, log_level, log_cat) \
+  ExpectLogMessage(trace_level, enable_bits, log_level, log_cat, false);
+
+  // Try different levels at LC_LOGGING category.
+  EXPECT_NO_LOG(TRACE_LEVEL_NONE, 0x3, LEVEL_FATALERROR, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_NONE, 0x3, LEVEL_ERROR, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_NONE, 0x3, LE, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_NONE, 0x3, LEVEL_WARNING, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_NONE, 0x3, LW, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_NONE, 0x3, L1, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_NONE, 0x3, L2, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_NONE, 0x3, L3, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_NONE, 0x3, L4, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_NONE, 0x3, L5, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_NONE, 0x3, L6, LC_LOGGING);
+  ConsumeAndRestartLog();
+
+  if (!ShouldRunLargeTest()) {
+    // This test takes about 7 seconds, so only run part of it by default.
+    return;
+  }
+
+  EXPECT_LOG(TRACE_LEVEL_FATAL, 0x3, LEVEL_FATALERROR, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_FATAL, 0x3, LEVEL_ERROR, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_FATAL, 0x3, LE, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_FATAL, 0x3, LEVEL_WARNING, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_FATAL, 0x3, LW, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_FATAL, 0x3, L1, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_FATAL, 0x3, L2, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_FATAL, 0x3, L3, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_FATAL, 0x3, L4, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_FATAL, 0x3, L5, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_FATAL, 0x3, L6, LC_LOGGING);
+  ConsumeAndRestartLog();
+
+  EXPECT_LOG(TRACE_LEVEL_ERROR, 0x3, LEVEL_FATALERROR, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_ERROR, 0x3, LEVEL_ERROR, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_ERROR, 0x3, LE, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_ERROR, 0x3, LEVEL_WARNING, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_ERROR, 0x3, LW, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_ERROR, 0x3, L1, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_ERROR, 0x3, L2, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_ERROR, 0x3, L3, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_ERROR, 0x3, L4, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_ERROR, 0x3, L5, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_ERROR, 0x3, L6, LC_LOGGING);
+  ConsumeAndRestartLog();
+
+  EXPECT_LOG(TRACE_LEVEL_WARNING, 0x3, LEVEL_FATALERROR, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_WARNING, 0x3, LEVEL_ERROR, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_WARNING, 0x3, LE, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_WARNING, 0x3, LEVEL_WARNING, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_WARNING, 0x3, LW, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_WARNING, 0x3, L1, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_WARNING, 0x3, L2, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_WARNING, 0x3, L3, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_WARNING, 0x3, L4, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_WARNING, 0x3, L5, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_WARNING, 0x3, L6, LC_LOGGING);
+  ConsumeAndRestartLog();
+
+  EXPECT_LOG(TRACE_LEVEL_INFORMATION, 0x3, LEVEL_FATALERROR, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_INFORMATION, 0x3, LEVEL_ERROR, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_INFORMATION, 0x3, LE, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_INFORMATION, 0x3, LEVEL_WARNING, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_INFORMATION, 0x3, LW, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_INFORMATION, 0x3, L1, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_INFORMATION, 0x3, L2, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_INFORMATION, 0x3, L3, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_INFORMATION, 0x3, L4, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_INFORMATION, 0x3, L5, LC_LOGGING);
+  EXPECT_NO_LOG(TRACE_LEVEL_INFORMATION, 0x3, L6, LC_LOGGING);
+  ConsumeAndRestartLog();
+
+  EXPECT_LOG(TRACE_LEVEL_VERBOSE, 0x3, LEVEL_FATALERROR, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_VERBOSE, 0x3, LEVEL_ERROR, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_VERBOSE, 0x3, LE, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_VERBOSE, 0x3, LEVEL_WARNING, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_VERBOSE, 0x3, LW, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_VERBOSE, 0x3, L1, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_VERBOSE, 0x3, L2, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_VERBOSE, 0x3, L3, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_VERBOSE, 0x3, L4, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_VERBOSE, 0x3, L5, LC_LOGGING);
+  EXPECT_LOG(TRACE_LEVEL_VERBOSE, 0x3, L6, LC_LOGGING);
+  ConsumeAndRestartLog();
+}
+
+TEST_F(EtwLogWriterTest, OutputMessageOnlyCategoriesEnabled) {
+  int max_cat = LC_MAX_CAT;
+  if (!ShouldRunLargeTest()) {
+    // This test takes about 12 seconds, so only run part of it by default.
+    max_cat = LC_LOGGING + 2;  // Test combinations of the first two categories.
+  }
+
+  // Loop through categories.
+  for (int log_cat = LC_LOGGING; log_cat < max_cat; ++log_cat) {
+    for (int enable_cat = LC_LOGGING; enable_cat < max_cat; ++enable_cat) {
+      EtwEventFlags enable_bits =
+          EtwLogWriter::CategoryToEnableFlag(
+              static_cast<LogCategory>(enable_cat));
+
+      ExpectLogMessage(TRACE_LEVEL_VERBOSE,
+                       enable_bits,
+                       LEVEL_WARNING,
+                       static_cast<LogCategory>(log_cat),
+                       log_cat == enable_cat);
+    }
+
+    // Play the log for every log category, due to the ETW enable/disable
+    // restriction on Windows XP that's mentioned above.
+    ConsumeAndRestartLog();
+  }
+}
+
+}  // namespace omaha
diff --git a/common/event_handler.h b/base/event_handler.h
similarity index 100%
rename from common/event_handler.h
rename to base/event_handler.h
diff --git a/base/event_trace_consumer.h b/base/event_trace_consumer.h
new file mode 100644
index 0000000..9662a3f
--- /dev/null
+++ b/base/event_trace_consumer.h
@@ -0,0 +1,159 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Declaration of a Windows event trace consumer base class.
+#ifndef BASE_EVENT_TRACE_CONSUMER_H_
+#define BASE_EVENT_TRACE_CONSUMER_H_
+
+#include <windows.h>
+#include <wmistr.h>
+#include <evntrace.h>
+#include <vector>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+// This class is a base class that makes it easier to consume events
+// from realtime or file sessions. Concrete consumers need to sublass
+// a specialization of this class and override the ProcessEvent and/or
+// the ProcessBuffer methods to implement the event consumption logic.
+// Usage might look like:
+// class MyConsumer: public EtwTraceConsumerBase<MyConsumer, 1> {
+//  protected:
+//    static VOID WINAPI ProcessEvent(PEVENT_TRACE event);
+// };
+//
+// MyConsumer consumer;
+// consumer.OpenFileSession(file_path);
+// consumer.Consume();
+template <class ImplClass>
+class EtwTraceConsumerBase {
+ public:
+  // Constructs a closed consumer.
+  EtwTraceConsumerBase() {
+  }
+
+  ~EtwTraceConsumerBase() {
+    Close();
+  }
+
+  // Opens the named realtime session, which must be existent.
+  // Note: You can use OpenRealtimeSession or OpenFileSession
+  //    to open as many as MAXIMUM_WAIT_OBJECTS (63) sessions at
+  //    any one time, though only one of them may be a realtime
+  //    session.
+  HRESULT OpenRealtimeSession(const wchar_t* session_name);
+
+  // Opens the event trace log in "file_name", which must be a full or
+  // relative path to an existing event trace log file.
+  // Note: You can use OpenRealtimeSession or OpenFileSession
+  //    to open as many as kNumSessions at any one time.
+  HRESULT OpenFileSession(const wchar_t* file_name);
+
+  // Consume all open sessions from beginning to end.
+  HRESULT Consume();
+
+  // Close all open sessions.
+  HRESULT Close();
+
+ protected:
+  // Override in subclasses to handle events.
+  static void ProcessEvent(EVENT_TRACE* event) {
+  }
+  // Override in subclasses to handle buffers.
+  static bool ProcessBuffer(EVENT_TRACE_LOGFILE* buffer) {
+    buffer;
+    return true;  // keep going
+  }
+
+ protected:
+  // Currently open sessions.
+  std::vector<TRACEHANDLE> trace_handles_;
+
+ private:
+  // These delegate to ImplClass callbacks with saner signatures.
+  static void WINAPI ProcessEventCallback(EVENT_TRACE* event) {
+    ImplClass::ProcessEvent(event);
+  }
+  static ULONG WINAPI ProcessBufferCallback(PEVENT_TRACE_LOGFILE buffer) {
+    return ImplClass::ProcessBuffer(buffer);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(EtwTraceConsumerBase);
+};
+
+template <class ImplClass> inline
+HRESULT EtwTraceConsumerBase<ImplClass>::OpenRealtimeSession(
+    const wchar_t* session_name) {
+  EVENT_TRACE_LOGFILE logfile = {};
+  logfile.LoggerName = const_cast<wchar_t*>(session_name);
+  logfile.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
+  logfile.BufferCallback = &ProcessBufferCallback;
+  logfile.EventCallback = &ProcessEventCallback;
+  logfile.Context = this;
+  TRACEHANDLE trace_handle = ::OpenTrace(&logfile);
+  if (reinterpret_cast<TRACEHANDLE>(INVALID_HANDLE_VALUE) == trace_handle)
+    return HRESULT_FROM_WIN32(::GetLastError());
+
+  trace_handles_.push_back(trace_handle);
+  return S_OK;
+}
+
+template <class ImplClass> inline
+HRESULT EtwTraceConsumerBase<ImplClass>::OpenFileSession(
+    const wchar_t* file_name) {
+  EVENT_TRACE_LOGFILE logfile = {};
+  logfile.LogFileName = const_cast<wchar_t*>(file_name);
+  logfile.BufferCallback = &ProcessBufferCallback;
+  logfile.EventCallback = &ProcessEventCallback;
+  logfile.Context = this;
+  TRACEHANDLE trace_handle = ::OpenTrace(&logfile);
+  if (reinterpret_cast<TRACEHANDLE>(INVALID_HANDLE_VALUE) == trace_handle)
+    return HRESULT_FROM_WIN32(::GetLastError());
+
+  trace_handles_.push_back(trace_handle);
+  return S_OK;
+}
+
+template <class ImplClass> inline
+HRESULT EtwTraceConsumerBase<ImplClass>::Consume() {
+  ULONG err = ::ProcessTrace(&trace_handles_[0],
+                             trace_handles_.size(),
+                             NULL,
+                             NULL);
+  return HRESULT_FROM_WIN32(err);
+}
+
+template <class ImplClass> inline
+HRESULT EtwTraceConsumerBase<ImplClass>::Close() {
+  HRESULT hr = S_OK;
+  for (size_t i = 0; i < trace_handles_.size(); ++i) {
+    if (NULL != trace_handles_[i]) {
+      ULONG ret = ::CloseTrace(trace_handles_[i]);
+      trace_handles_[i] = NULL;
+
+      if (FAILED(HRESULT_FROM_WIN32(ret)))
+        hr = HRESULT_FROM_WIN32(ret);
+    }
+
+    trace_handles_.clear();
+  }
+
+  return hr;
+}
+
+}  // namespace omaha
+
+#endif  // BASE_EVENT_TRACE_CONSUMER_H_
diff --git a/base/event_trace_consumer_unittest.cc b/base/event_trace_consumer_unittest.cc
new file mode 100644
index 0000000..4e2d698
--- /dev/null
+++ b/base/event_trace_consumer_unittest.cc
@@ -0,0 +1,379 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Unit tests for event trace consumer_ base class.
+#include "omaha/base/event_trace_consumer.h"
+#include <atlbase.h>
+#include <atlsync.h>
+#include <list>
+#include "base/basictypes.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/event_trace_controller.h"
+#include "omaha/base/event_trace_provider.h"
+// TODO(omaha): Remove when http://b/2767208 is fixed.
+#include "omaha/base/vistautil.h"
+#include "omaha/testing/unit_test.h"
+
+#include <initguid.h>  // NOLINT - has to be last
+
+namespace omaha {
+
+namespace {
+
+using omaha::EtwTraceConsumerBase;
+using omaha::EtwTraceController;
+using omaha::EtwTraceProperties;
+
+typedef std::list<EVENT_TRACE> EventQueue;
+
+class TestConsumer: public EtwTraceConsumerBase<TestConsumer> {
+ public:
+  TestConsumer() {
+    sank_event_.Create(NULL, TRUE, FALSE, NULL);
+
+    ClearQueue();
+  }
+
+  ~TestConsumer() {
+    ClearQueue();
+    sank_event_.Close();
+  }
+
+  void ClearQueue() {
+    EventQueue::const_iterator it(events_.begin()), end(events_.end());
+
+    for (; it != end; ++it) {
+      delete [] it->MofData;
+    }
+
+    events_.clear();
+  }
+
+  static void EnqueueEvent(EVENT_TRACE* event) {
+    events_.push_back(*event);
+    EVENT_TRACE& back = events_.back();
+
+    if (NULL != event->MofData && 0 != event->MofLength) {
+      back.MofData = new char[event->MofLength];
+      memcpy(back.MofData, event->MofData, event->MofLength);
+    }
+  }
+
+  static void ProcessEvent(EVENT_TRACE* event) {
+    EnqueueEvent(event);
+    sank_event_.Set();
+  }
+
+  static CEvent sank_event_;
+  static EventQueue events_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestConsumer);
+};
+
+CEvent TestConsumer::sank_event_;
+EventQueue TestConsumer::events_;
+
+const wchar_t* const kTestSessionName = L"TestLogSession";
+
+void StopTestTraceSession() {
+  // Shut down any potentially dangling session.
+  EtwTraceProperties prop;
+  EtwTraceController::Stop(kTestSessionName, &prop);
+}
+
+class EtwTraceConsumerBaseTest: public testing::Test {
+ public:
+  virtual void SetUp() {
+    StopTestTraceSession();
+  }
+};
+
+}  // namespace
+
+TEST_F(EtwTraceConsumerBaseTest, Initialize) {
+  TestConsumer consumer_;
+}
+
+TEST_F(EtwTraceConsumerBaseTest, OpenRealtimeSucceedsWhenNoSession) {
+  TestConsumer consumer_;
+
+  EXPECT_HRESULT_SUCCEEDED(consumer_.OpenRealtimeSession(kTestSessionName));
+}
+
+TEST_F(EtwTraceConsumerBaseTest, ConsumerImmediateFailureWhenNoSession) {
+  TestConsumer consumer_;
+
+  EXPECT_HRESULT_SUCCEEDED(consumer_.OpenRealtimeSession(kTestSessionName));
+  EXPECT_HRESULT_FAILED(consumer_.Consume());
+}
+
+class EtwTraceConsumerRealtimeTest: public testing::Test {
+ public:
+  virtual void SetUp() {
+    StopTestTraceSession();
+
+    EXPECT_HRESULT_SUCCEEDED(consumer_.OpenRealtimeSession(kTestSessionName));
+  }
+
+  virtual void TearDown() {
+    consumer_.Close();
+  }
+
+  DWORD ConsumerThread() {
+    ::SetEvent(consumer_ready_);
+
+    HRESULT hr = consumer_.Consume();
+    return hr;
+  }
+
+  static DWORD WINAPI ConsumerThreadMainProc(void* arg) {
+    return reinterpret_cast<EtwTraceConsumerRealtimeTest*>(arg)->
+        ConsumerThread();
+  }
+
+  HRESULT StartConsumerThread() {
+    consumer_ready_.Attach(::CreateEvent(NULL, TRUE, FALSE, NULL));
+    EXPECT_TRUE(consumer_ready_ != NULL);
+    consumer_thread_.Attach(::CreateThread(NULL, 0, ConsumerThreadMainProc,
+        this, 0, NULL));
+    if (NULL == consumer_thread_)
+      return HRESULT_FROM_WIN32(::GetLastError());
+
+    HRESULT hr = S_OK;
+    HANDLE events[] = { consumer_ready_, consumer_thread_ };
+    DWORD result = ::WaitForMultipleObjects(arraysize(events), events,
+                                            FALSE, INFINITE);
+    switch (result) {
+      case WAIT_OBJECT_0:
+        // The event was set, the consumer_ is ready.
+        return S_OK;
+      case WAIT_OBJECT_0 + 1: {
+          // The thread finished. This may race with the event, so check
+          // explicitly for the event here, before concluding there's trouble.
+          if (WAIT_OBJECT_0 == ::WaitForSingleObject(consumer_ready_, 0))
+            return S_OK;
+          DWORD exit_code = 0;
+          if (::GetExitCodeThread(consumer_thread_, &exit_code))
+            return exit_code;
+          else
+            return HRESULT_FROM_WIN32(::GetLastError());
+          break;
+        }
+      default:
+        return E_UNEXPECTED;
+        break;
+    }
+
+    // NOTREACHED
+  }
+
+  // Waits for consumer_ thread to exit, and returns its exit code.
+  HRESULT JoinConsumerThread() {
+    if (WAIT_OBJECT_0 != ::WaitForSingleObject(consumer_thread_, INFINITE))
+      return HRESULT_FROM_WIN32(::GetLastError());
+
+    DWORD exit_code = 0;
+    if (::GetExitCodeThread(consumer_thread_, &exit_code))
+      return exit_code;
+
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  TestConsumer consumer_;
+  CHandle consumer_ready_;
+  CHandle consumer_thread_;
+};
+
+TEST_F(EtwTraceConsumerRealtimeTest, ConsumerReturnsWhenSessionClosed) {
+  if (!IsBuildSystem() && !vista_util::IsVistaOrLater()) {
+    std::wcout << _T("\tTest not run due to http://b/2767208.") << std::endl;
+    return;
+  }
+
+  EtwTraceController controller;
+
+  HRESULT hr = controller.StartRealtimeSession(kTestSessionName, 100 * 1024);
+  if (hr == E_ACCESSDENIED) {
+    SUCCEED() << "You must be an administrator to run this test on Vista";
+    return;
+  }
+
+  // Start the consumer_.
+  EXPECT_HRESULT_SUCCEEDED(StartConsumerThread());
+
+  // Wait around for the consumer_ thread a bit.
+  EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(consumer_thread_, 50));
+
+  EXPECT_HRESULT_SUCCEEDED(controller.Stop(NULL));
+
+  // The consumer_ returns success on session stop.
+  EXPECT_HRESULT_SUCCEEDED(JoinConsumerThread());
+}
+
+namespace {
+
+// {036B8F65-8DF3-46e4-ABFC-6985C43D59BA}
+DEFINE_GUID(kTestProvider,
+  0x36b8f65, 0x8df3, 0x46e4, 0xab, 0xfc, 0x69, 0x85, 0xc4, 0x3d, 0x59, 0xba);
+
+// {57E47923-A549-476f-86CA-503D57F59E62}
+DEFINE_GUID(kTestEventType,
+  0x57e47923, 0xa549, 0x476f, 0x86, 0xca, 0x50, 0x3d, 0x57, 0xf5, 0x9e, 0x62);
+
+}  // namespace
+
+TEST_F(EtwTraceConsumerRealtimeTest, ConsumeEvent) {
+  if (!IsBuildSystem() && !vista_util::IsVistaOrLater()) {
+    std::wcout << _T("\tTest not run due to http://b/2767208.") << std::endl;
+    return;
+  }
+
+  EtwTraceController controller;
+  HRESULT hr = controller.StartRealtimeSession(kTestSessionName, 100 * 1024);
+  if (hr == E_ACCESSDENIED) {
+    SUCCEED() << "You must be an administrator to run this test on Vista";
+    return;
+  }
+
+  EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider(kTestProvider,
+      TRACE_LEVEL_VERBOSE, 0xFFFFFFFF));
+
+  EtwTraceProvider provider(kTestProvider);
+  EXPECT_EQ(ERROR_SUCCESS, provider.Register());
+
+  // Start the consumer_.
+  EXPECT_HRESULT_SUCCEEDED(StartConsumerThread());
+
+  EXPECT_EQ(0, TestConsumer::events_.size());
+
+  EtwMofEvent<1> event(kTestEventType, 1, TRACE_LEVEL_ERROR);
+  EXPECT_EQ(ERROR_SUCCESS, provider.Log(&event.header));
+
+  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(TestConsumer::sank_event_,
+                                                 INFINITE));
+  EXPECT_HRESULT_SUCCEEDED(controller.Stop(NULL));
+  EXPECT_HRESULT_SUCCEEDED(JoinConsumerThread());
+  EXPECT_NE(0, TestConsumer::events_.size());
+}
+
+namespace {
+
+// We run events through a file session to assert that
+// the content comes through.
+class EtwTraceConsumerDataTest: public testing::Test {
+ public:
+  EtwTraceConsumerDataTest() {
+  }
+
+  virtual void SetUp() {
+    StopTestTraceSession();
+
+    // Construct a temp file name.
+    CString temp_dir = omaha::app_util::GetTempDir();
+    EXPECT_TRUE(::GetTempFileName(temp_dir, _T("tmp"), 0,
+                                  CStrBuf(temp_file_, MAX_PATH)));
+  }
+
+  virtual void TearDown() {
+    EXPECT_TRUE(::DeleteFile(temp_file_));
+
+    // Shut down any potentially dangling session.
+    EtwTraceProperties prop;
+    EtwTraceController::Stop(kTestSessionName, &prop);
+  }
+
+  HRESULT LogEventToTempSession(PEVENT_TRACE_HEADER header) {
+    EtwTraceController controller;
+
+    // Set up a file session.
+    HRESULT hr = controller.StartFileSession(kTestSessionName, temp_file_);
+    if (FAILED(hr))
+      return hr;
+
+    // Enable our provider.
+    EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider(kTestProvider,
+        TRACE_LEVEL_VERBOSE, 0xFFFFFFFF));
+
+    EtwTraceProvider provider(kTestProvider);
+    // Then register our provider, means we get a session handle immediately.
+    EXPECT_EQ(ERROR_SUCCESS, provider.Register());
+    // Trace the event, it goes to the temp file.
+    EXPECT_EQ(ERROR_SUCCESS, provider.Log(header));
+    EXPECT_HRESULT_SUCCEEDED(controller.DisableProvider(kTestProvider));
+    EXPECT_HRESULT_SUCCEEDED(provider.Unregister());
+    EXPECT_HRESULT_SUCCEEDED(controller.Flush(NULL));
+    EXPECT_HRESULT_SUCCEEDED(controller.Stop(NULL));
+
+    return S_OK;
+  }
+
+  HRESULT ConsumeEventFromTempSession() {
+    // Now consume the event(s).
+    TestConsumer consumer_;
+    HRESULT hr = consumer_.OpenFileSession(temp_file_);
+    if (SUCCEEDED(hr))
+      hr = consumer_.Consume();
+    consumer_.Close();
+    // And nab the result.
+    events_.swap(TestConsumer::events_);
+    return hr;
+  }
+
+  HRESULT RoundTripEvent(PEVENT_TRACE_HEADER header, PEVENT_TRACE* trace) {
+    ::DeleteFile(temp_file_);
+
+    HRESULT hr = LogEventToTempSession(header);
+    if (SUCCEEDED(hr))
+      hr = ConsumeEventFromTempSession();
+
+    if (FAILED(hr))
+      return hr;
+
+    // We should now have the event in the queue.
+    if (events_.empty())
+      return E_FAIL;
+
+    *trace = &events_.back();
+    return S_OK;
+  }
+
+  EventQueue events_;
+  CString temp_file_;
+};
+
+}  // namespace
+
+
+TEST_F(EtwTraceConsumerDataTest, RoundTrip) {
+  EtwMofEvent<1> event(kTestEventType, 1, TRACE_LEVEL_ERROR);
+
+  static const char kData[] = "This is but test data";
+  event.fields[0].DataPtr = reinterpret_cast<ULONG_PTR>(kData);
+  event.fields[0].Length = sizeof(kData);
+
+  PEVENT_TRACE trace = NULL;
+  HRESULT hr = RoundTripEvent(&event.header, &trace);
+  if (hr == E_ACCESSDENIED) {
+    SUCCEED() << "You must be an administrator to run this test on Vista";
+    return;
+  }
+  ASSERT_TRUE(NULL != trace);
+  EXPECT_EQ(sizeof(kData), trace->MofLength);
+  EXPECT_STREQ(kData, reinterpret_cast<const char*>(trace->MofData));
+}
+
+}  // namespace omaha
+
diff --git a/base/event_trace_controller.cc b/base/event_trace_controller.cc
new file mode 100644
index 0000000..845c6ce
--- /dev/null
+++ b/base/event_trace_controller.cc
@@ -0,0 +1,150 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Implementation of a Windows event trace controller class.
+#include "omaha/base/event_trace_controller.h"
+#include "omaha/base/debug.h"
+
+namespace omaha {
+
+EtwTraceController::EtwTraceController() : session_(NULL) {
+}
+
+EtwTraceController::~EtwTraceController() {
+  Stop(NULL);
+}
+
+HRESULT EtwTraceController::Start(const wchar_t* session_name,
+    EtwTraceProperties* prop) {
+  ASSERT1(NULL == session_ && session_name_.empty());
+  EtwTraceProperties ignore;
+  if (prop == NULL)
+    prop = &ignore;
+
+  HRESULT hr = Start(session_name, prop, &session_);
+  if (SUCCEEDED(hr))
+    session_name_ = session_name;
+
+  return hr;
+}
+
+HRESULT EtwTraceController::StartFileSession(const wchar_t* session_name,
+      const wchar_t* logfile_path, bool realtime) {
+  ASSERT1(NULL == session_ && session_name_.empty());
+
+  EtwTraceProperties prop;
+  prop.SetLoggerFileName(logfile_path);
+  EVENT_TRACE_PROPERTIES& p = *prop.get();
+  p.Wnode.ClientContext = 1;  // QPC timer accuracy.
+  p.LogFileMode = EVENT_TRACE_FILE_MODE_SEQUENTIAL;  // Sequential log.
+  if (realtime)
+    p.LogFileMode |= EVENT_TRACE_REAL_TIME_MODE;
+
+  p.MaximumFileSize = 100;  // 100M file size.
+  p.FlushTimer = 30;  // 30 seconds flush lag.
+  return Start(session_name, &prop);
+}
+
+HRESULT EtwTraceController::StartRealtimeSession(const wchar_t* session_name,
+    size_t buffer_size) {
+  ASSERT1(NULL == session_ && session_name_.empty());
+  EtwTraceProperties prop;
+  EVENT_TRACE_PROPERTIES& p = *prop.get();
+  p.LogFileMode = EVENT_TRACE_REAL_TIME_MODE | EVENT_TRACE_USE_PAGED_MEMORY;
+  p.FlushTimer = 1;  // flush every second.
+  p.BufferSize = buffer_size;  // buffer_size in kilobytes.
+  p.LogFileNameOffset = 0;
+  return Start(session_name, &prop);
+}
+
+HRESULT EtwTraceController::EnableProvider(REFGUID provider, UCHAR level,
+    ULONG flags) {
+  ULONG error = ::EnableTrace(TRUE, flags, level, &provider, session_);
+  return HRESULT_FROM_WIN32(error);
+}
+
+HRESULT EtwTraceController::DisableProvider(REFGUID provider) {
+  ULONG error = ::EnableTrace(FALSE, 0, 0, &provider, session_);
+  return HRESULT_FROM_WIN32(error);
+}
+
+HRESULT EtwTraceController::Stop(EtwTraceProperties* properties) {
+  EtwTraceProperties ignore;
+  if (properties == NULL)
+    properties = &ignore;
+
+  ULONG error = ::ControlTrace(session_, NULL, properties->get(),
+    EVENT_TRACE_CONTROL_STOP);
+  if (ERROR_SUCCESS != error)
+    return HRESULT_FROM_WIN32(error);
+
+  session_ = NULL;
+  session_name_.clear();
+  return S_OK;
+}
+
+HRESULT EtwTraceController::Flush(EtwTraceProperties* properties) {
+  EtwTraceProperties ignore;
+  if (properties == NULL)
+    properties = &ignore;
+
+  ULONG error = ::ControlTrace(session_, NULL, properties->get(),
+                               EVENT_TRACE_CONTROL_FLUSH);
+  if (ERROR_SUCCESS != error)
+    return HRESULT_FROM_WIN32(error);
+
+  return S_OK;
+}
+
+HRESULT EtwTraceController::Start(const wchar_t* session_name,
+    EtwTraceProperties* properties, TRACEHANDLE* session_handle) {
+  ASSERT1(properties != NULL);
+  ULONG err = ::StartTrace(session_handle, session_name, properties->get());
+  return HRESULT_FROM_WIN32(err);
+}
+
+HRESULT EtwTraceController::Query(const wchar_t* session_name,
+    EtwTraceProperties* properties) {
+  ASSERT1(properties != NULL);
+  ULONG err = ::ControlTrace(NULL, session_name, properties->get(),
+                             EVENT_TRACE_CONTROL_QUERY);
+  return HRESULT_FROM_WIN32(err);
+};
+
+HRESULT EtwTraceController::Update(const wchar_t* session_name,
+    EtwTraceProperties* properties) {
+  ASSERT1(properties != NULL);
+  ULONG err = ::ControlTrace(NULL, session_name, properties->get(),
+                             EVENT_TRACE_CONTROL_UPDATE);
+  return HRESULT_FROM_WIN32(err);
+}
+
+HRESULT EtwTraceController::Stop(const wchar_t* session_name,
+    EtwTraceProperties* properties) {
+  ASSERT1(properties != NULL);
+  ULONG err = ::ControlTrace(NULL, session_name, properties->get(),
+                             EVENT_TRACE_CONTROL_STOP);
+  return HRESULT_FROM_WIN32(err);
+}
+
+HRESULT EtwTraceController::Flush(const wchar_t* session_name,
+    EtwTraceProperties* properties) {
+  ASSERT1(properties != NULL);
+  ULONG err = ::ControlTrace(NULL, session_name, properties->get(),
+                             EVENT_TRACE_CONTROL_FLUSH);
+  return HRESULT_FROM_WIN32(err);
+}
+
+}  // namespace omaha
diff --git a/base/event_trace_controller.h b/base/event_trace_controller.h
new file mode 100644
index 0000000..9f96599
--- /dev/null
+++ b/base/event_trace_controller.h
@@ -0,0 +1,183 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Declaration of a Windows event trace controller class.
+// The controller takes care of creating and manipulating event trace
+// sessions.
+//
+// Event tracing for Windows is a system-provided service that provides
+// logging control and high-performance transport for generic, binary trace
+// events. Event trace providers register with the system by their name,
+// which is a GUID, and can from that point forward receive callbacks that
+// start or end tracing and that change their trace level and enable mask.
+//
+// A trace controller can create an event tracing session, which either
+// sends events to a binary file, or to a realtime consumer, or both.
+//
+// A trace consumer consumes events from zero or one realtime session,
+// as well as potentially from multiple binary trace files.
+#ifndef BASE_EVENT_TRACE_CONTROLLER_H_
+#define BASE_EVENT_TRACE_CONTROLLER_H_
+
+#include <windows.h>
+#include <wmistr.h>
+#include <evntrace.h>
+#include <string>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+// Utility class to make it easier to work with EVENT_TRACE_PROPERTIES.
+// The EVENT_TRACE_PROPERTIES structure contains information about an
+// event tracing session.
+class EtwTraceProperties {
+ public:
+  EtwTraceProperties() {
+    memset(buffer_, 0, sizeof(buffer_));
+    EVENT_TRACE_PROPERTIES* prop = get();
+
+    prop->Wnode.BufferSize = sizeof(buffer_);
+    prop->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
+    prop->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
+    prop->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) +
+                              sizeof(wchar_t) * kMaxStringLen;
+  }
+
+  EVENT_TRACE_PROPERTIES* get() {
+    return &properties_;
+  }
+  const EVENT_TRACE_PROPERTIES* get() const {
+    return reinterpret_cast<const EVENT_TRACE_PROPERTIES*>(&properties_);
+  }
+
+  const wchar_t* GetLoggerName() const {
+    return reinterpret_cast<const wchar_t *>(buffer_ + get()->LoggerNameOffset);
+  }
+
+  HRESULT SetLoggerName(const wchar_t* logger_name) {
+    size_t len = wcslen(logger_name) + 1;
+    if (kMaxStringLen < len)
+      return E_INVALIDARG;
+
+    memcpy(buffer_ + get()->LoggerNameOffset,
+           logger_name,
+           sizeof(wchar_t) * len);
+    return S_OK;
+  }
+
+  const wchar_t* GetLoggerFileName() const {
+    return reinterpret_cast<const wchar_t*>(buffer_ + get()->LogFileNameOffset);
+  }
+
+  HRESULT SetLoggerFileName(const wchar_t* logger_file_name) {
+    size_t len = wcslen(logger_file_name) + 1;
+    if (kMaxStringLen < len)
+      return E_INVALIDARG;
+
+    memcpy(buffer_ + get()->LogFileNameOffset,
+           logger_file_name,
+           sizeof(wchar_t) * len);
+    return S_OK;
+  }
+
+  // Max string len for name and session name is 1024 per documentation.
+  static const size_t kMaxStringLen = 1024;
+  // Properties buffer allocates space for header and for
+  // max length for name and session name.
+  static const size_t kBufSize = sizeof(EVENT_TRACE_PROPERTIES)
+      + 2 * sizeof(wchar_t) * (kMaxStringLen);
+
+ private:
+  // The EVENT_TRACE_PROPERTIES structure needs to be overlaid on a
+  // larger buffer to allow storing the logger name and logger file
+  // name contiguously with the structure.
+  union {
+   public:
+    // Our properties header.
+    EVENT_TRACE_PROPERTIES properties_;
+    // The actual size of the buffer is forced by this member.
+    char buffer_[kBufSize];
+  };
+
+  DISALLOW_COPY_AND_ASSIGN(EtwTraceProperties);
+};
+
+// This class implements an ETW controller, which knows how to start and
+// stop event tracing sessions, as well as controlling ETW provider
+// log levels and enable bit masks under the session.
+class EtwTraceController {
+ public:
+  EtwTraceController();
+  ~EtwTraceController();
+
+  // Start a session with given name and properties.
+  HRESULT Start(const wchar_t* session_name, EtwTraceProperties* prop);
+
+  // Starts a session tracing to a file with some default properties.
+  HRESULT StartFileSession(const wchar_t* session_name,
+                           const wchar_t* logfile_path,
+                           bool realtime = false);
+
+  // Starts a realtime session with some default properties.
+  HRESULT StartRealtimeSession(const wchar_t* session_name,
+                               size_t buffer_size);
+
+  // Enables "provider" at "level" for this session.
+  // This will cause all providers registered with the GUID
+  // "provider" to start tracing at the new level, systemwide.
+  HRESULT EnableProvider(const GUID& provider, UCHAR level,
+                         ULONG flags = 0xFFFFFFFF);
+  // Disables "provider".
+  HRESULT DisableProvider(const GUID& provider);
+
+  // Stops our session and retrieve the new properties of the session,
+  // properties may be NULL.
+  HRESULT Stop(EtwTraceProperties* properties);
+
+  // Flushes our session and retrieve the current properties,
+  // properties may be NULL.
+  HRESULT Flush(EtwTraceProperties* properties);
+
+  // Static utility functions for controlling
+  // sessions we don't necessarily own.
+  static HRESULT Start(const wchar_t* session_name,
+                       EtwTraceProperties* properties,
+                       TRACEHANDLE* session_handle);
+
+  static HRESULT Query(const wchar_t* session_name,
+                       EtwTraceProperties* properties);
+
+  static HRESULT Update(const wchar_t* session_name,
+                        EtwTraceProperties* properties);
+
+  static HRESULT Stop(const wchar_t* session_name,
+                      EtwTraceProperties* properties);
+  static HRESULT Flush(const wchar_t* session_name,
+                       EtwTraceProperties* properties);
+
+  // Accessors.
+  TRACEHANDLE session() const { return session_; }
+  const wchar_t* session_name() const { return session_name_.c_str(); }
+
+ private:
+  std::wstring session_name_;
+  TRACEHANDLE session_;
+
+  DISALLOW_COPY_AND_ASSIGN(EtwTraceController);
+};
+
+}  // namespace omaha
+
+#endif  // BASE_EVENT_TRACE_CONTROLLER_H_
diff --git a/base/event_trace_controller_unittest.cc b/base/event_trace_controller_unittest.cc
new file mode 100644
index 0000000..6ce027b
--- /dev/null
+++ b/base/event_trace_controller_unittest.cc
@@ -0,0 +1,239 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Unit tests for event trace controller.
+#include "omaha/base/event_trace_controller.h"
+#include <atlsync.h>
+#include "omaha/base/app_util.h"
+#include "omaha/base/event_trace_provider.h"
+#include "omaha/testing/unit_test.h"
+#include <initguid.h>  // NOLINT - must be last.
+
+namespace {
+
+using omaha::EtwTraceController;
+using omaha::EtwTraceProvider;
+using omaha::EtwTraceProperties;
+
+const wchar_t kTestSessionName[] = L"TestLogSession";
+
+// {0D236A42-CD18-4e3d-9975-DCEEA2106E05}
+DEFINE_GUID(kTestProvider,
+    0xd236a42, 0xcd18, 0x4e3d, 0x99, 0x75, 0xdc, 0xee, 0xa2, 0x10, 0x6e, 0x5);
+
+DEFINE_GUID(kGuidNull,
+    0x0000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0);
+
+const ULONG kTestProviderFlags = 0xCAFEBABE;
+
+class TestingProvider: public EtwTraceProvider {
+ public:
+  explicit TestingProvider(const GUID& provider_name)
+      : EtwTraceProvider(provider_name) {
+    callback_event_.Create(NULL, TRUE, FALSE, NULL);
+  }
+
+  void WaitForCallback() {
+    ::WaitForSingleObject(callback_event_, INFINITE);
+    callback_event_.Reset();
+  }
+
+ private:
+  virtual void OnEventsEnabled() {
+    callback_event_.Set();
+  }
+  virtual void OnEventsDisabled() {
+    callback_event_.Set();
+  }
+
+  CEvent callback_event_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestingProvider);
+};
+
+// These fixtures make sure we clean up dangling trace sessions
+// prior to all tests, to make the tests stable against crashes
+// and failures.
+class EtwTracePropertiesTest : public testing::Test {
+ public:
+  virtual void SetUp() {
+    CloseTestTraceSession();
+  }
+
+  virtual void TearDown() {
+    CloseTestTraceSession();
+  }
+
+ private:
+  void CloseTestTraceSession() {
+    // Clean up potential leftover sessions from previous unsuccessful runs.
+    EtwTraceProperties prop;
+    EtwTraceController::Stop(kTestSessionName, &prop);
+  }
+};
+
+class EtwTraceControllerTest : public EtwTracePropertiesTest {
+};
+
+}  // namespace
+
+namespace omaha {
+
+TEST_F(EtwTracePropertiesTest, Initialization) {
+  EtwTraceProperties prop;
+
+  EVENT_TRACE_PROPERTIES* p = prop.get();
+  EXPECT_NE(0u, p->Wnode.BufferSize);
+  EXPECT_EQ(0u, p->Wnode.ProviderId);
+  EXPECT_EQ(0u, p->Wnode.HistoricalContext);
+
+  EXPECT_TRUE(kGuidNull == p->Wnode.Guid);
+  EXPECT_EQ(0, p->Wnode.ClientContext);
+  EXPECT_EQ(WNODE_FLAG_TRACED_GUID, p->Wnode.Flags);
+
+  EXPECT_EQ(0, p->BufferSize);
+  EXPECT_EQ(0, p->MinimumBuffers);
+  EXPECT_EQ(0, p->MaximumBuffers);
+  EXPECT_EQ(0, p->MaximumFileSize);
+  EXPECT_EQ(0, p->LogFileMode);
+  EXPECT_EQ(0, p->FlushTimer);
+  EXPECT_EQ(0, p->EnableFlags);
+  EXPECT_EQ(0, p->AgeLimit);
+
+  EXPECT_EQ(0, p->NumberOfBuffers);
+  EXPECT_EQ(0, p->FreeBuffers);
+  EXPECT_EQ(0, p->EventsLost);
+  EXPECT_EQ(0, p->BuffersWritten);
+  EXPECT_EQ(0, p->LogBuffersLost);
+  EXPECT_EQ(0, p->RealTimeBuffersLost);
+  EXPECT_EQ(0, p->LoggerThreadId);
+  EXPECT_NE(0u, p->LogFileNameOffset);
+  EXPECT_NE(0u, p->LoggerNameOffset);
+}
+
+TEST_F(EtwTracePropertiesTest, Strings) {
+  EtwTraceProperties prop;
+
+  EXPECT_STREQ(L"", prop.GetLoggerFileName());
+  EXPECT_STREQ(L"", prop.GetLoggerName());
+
+  std::wstring name(1023, L'A');
+  EXPECT_HRESULT_SUCCEEDED(prop.SetLoggerFileName(name.c_str()));
+  EXPECT_HRESULT_SUCCEEDED(prop.SetLoggerName(name.c_str()));
+  EXPECT_STREQ(name.c_str(), prop.GetLoggerFileName());
+  EXPECT_STREQ(name.c_str(), prop.GetLoggerName());
+
+  std::wstring name2(1024, L'A');
+  EXPECT_HRESULT_FAILED(prop.SetLoggerFileName(name2.c_str()));
+  EXPECT_HRESULT_FAILED(prop.SetLoggerName(name2.c_str()));
+}
+
+TEST_F(EtwTraceControllerTest, Initialize) {
+  EtwTraceController controller;
+
+  EXPECT_EQ(NULL, controller.session());
+  EXPECT_STREQ(L"", controller.session_name());
+}
+
+TEST_F(EtwTraceControllerTest, StartRealTimeSession) {
+  EtwTraceController controller;
+
+  HRESULT hr = controller.StartRealtimeSession(kTestSessionName, 100 * 1024);
+  if (hr == E_ACCESSDENIED) {
+    SUCCEED() << "You must be an administrator to run this test on Vista";
+    return;
+  }
+
+  EXPECT_TRUE(NULL != controller.session());
+  EXPECT_STREQ(kTestSessionName, controller.session_name());
+
+  EXPECT_HRESULT_SUCCEEDED(controller.Stop(NULL));
+  EXPECT_EQ(NULL, controller.session());
+  EXPECT_STREQ(L"", controller.session_name());
+}
+
+TEST_F(EtwTraceControllerTest, StartFileSession) {
+  CString temp;
+  EXPECT_TRUE(::GetTempFileName(app_util::GetTempDir(), _T("tmp"), 0,
+                                CStrBuf(temp, MAX_PATH)));
+
+  EtwTraceController controller;
+  HRESULT hr = controller.StartFileSession(kTestSessionName, temp);
+  if (hr == E_ACCESSDENIED) {
+    SUCCEED() << "You must be an administrator to run this test on Vista";
+    return;
+  }
+
+  EXPECT_TRUE(NULL != controller.session());
+  EXPECT_STREQ(kTestSessionName, controller.session_name());
+
+  EXPECT_HRESULT_SUCCEEDED(controller.Stop(NULL));
+  EXPECT_EQ(NULL, controller.session());
+  EXPECT_STREQ(L"", controller.session_name());
+
+  EXPECT_TRUE(::DeleteFile(temp));
+}
+
+TEST_F(EtwTraceControllerTest, EnableDisable) {
+  TestingProvider provider(kTestProvider);
+
+  EXPECT_EQ(ERROR_SUCCESS, provider.Register());
+  EXPECT_EQ(NULL, provider.session_handle());
+
+  EtwTraceController controller;
+  HRESULT hr = controller.StartRealtimeSession(kTestSessionName, 100 * 1024);
+  if (hr == E_ACCESSDENIED) {
+    SUCCEED() << "You must be an administrator to run this test on Vista";
+    return;
+  }
+
+  EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider(kTestProvider,
+                           TRACE_LEVEL_VERBOSE, kTestProviderFlags));
+
+  provider.WaitForCallback();
+
+  EXPECT_EQ(TRACE_LEVEL_VERBOSE, provider.enable_level());
+  EXPECT_EQ(kTestProviderFlags, provider.enable_flags());
+
+  EXPECT_HRESULT_SUCCEEDED(controller.DisableProvider(kTestProvider));
+
+  provider.WaitForCallback();
+
+  EXPECT_EQ(0, provider.enable_level());
+  EXPECT_EQ(0, provider.enable_flags());
+
+  EXPECT_EQ(ERROR_SUCCESS, provider.Unregister());
+
+  // Enable the provider again, before registering.
+  EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider(kTestProvider,
+                           TRACE_LEVEL_VERBOSE, kTestProviderFlags));
+
+  // Register the provider again, the settings above
+  // should take immediate effect.
+  EXPECT_EQ(ERROR_SUCCESS, provider.Register());
+
+  EXPECT_EQ(TRACE_LEVEL_VERBOSE, provider.enable_level());
+  EXPECT_EQ(kTestProviderFlags, provider.enable_flags());
+
+  EXPECT_HRESULT_SUCCEEDED(controller.Stop(NULL));
+
+  provider.WaitForCallback();
+
+  // Session should have wound down.
+  EXPECT_EQ(0, provider.enable_level());
+  EXPECT_EQ(0, provider.enable_flags());
+}
+
+}  // namespace omaha
diff --git a/base/event_trace_provider.cc b/base/event_trace_provider.cc
new file mode 100644
index 0000000..1941f6c
--- /dev/null
+++ b/base/event_trace_provider.cc
@@ -0,0 +1,150 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+#include "omaha/base/event_trace_provider.h"
+#include <windows.h>
+#include <cguid.h>
+
+namespace omaha {
+
+TRACE_GUID_REGISTRATION EtwTraceProvider::obligatory_guid_registration_ = {
+  &GUID_NULL,
+  NULL
+};
+
+EtwTraceProvider::EtwTraceProvider(const GUID& provider_name)
+    : provider_name_(provider_name), registration_handle_(NULL),
+      session_handle_(NULL), enable_flags_(0), enable_level_(0) {
+}
+
+EtwTraceProvider::EtwTraceProvider()
+    : provider_name_(GUID_NULL), registration_handle_(NULL),
+      session_handle_(NULL), enable_flags_(0), enable_level_(0) {
+}
+
+EtwTraceProvider::~EtwTraceProvider() {
+  Unregister();
+}
+
+ULONG EtwTraceProvider::EnableEvents(void* buffer) {
+  session_handle_ = ::GetTraceLoggerHandle(buffer);
+  if (NULL == session_handle_) {
+    return ::GetLastError();
+  }
+
+  enable_flags_ = ::GetTraceEnableFlags(session_handle_);
+  enable_level_ = ::GetTraceEnableLevel(session_handle_);
+
+  // Give subclasses a chance to digest the state change.
+  OnEventsEnabled();
+
+  return ERROR_SUCCESS;
+}
+
+ULONG EtwTraceProvider::DisableEvents() {
+  enable_level_ = 0;
+  enable_flags_ = 0;
+  session_handle_ = NULL;
+
+  // Give subclasses a chance to digest the state change.
+  OnEventsDisabled();
+
+  return ERROR_SUCCESS;
+}
+
+ULONG EtwTraceProvider::Callback(WMIDPREQUESTCODE request, void* buffer) {
+  switch (request) {
+    case WMI_ENABLE_EVENTS:
+      return EnableEvents(buffer);
+    case WMI_DISABLE_EVENTS:
+      return DisableEvents();
+
+    case WMI_GET_ALL_DATA:
+    case WMI_GET_SINGLE_INSTANCE:
+    case WMI_SET_SINGLE_INSTANCE:
+    case WMI_SET_SINGLE_ITEM:
+    case WMI_ENABLE_COLLECTION:
+    case WMI_DISABLE_COLLECTION:
+    case WMI_REGINFO:
+    case WMI_EXECUTE_METHOD:
+    default:
+      return ERROR_INVALID_PARAMETER;
+  }
+  // Not reached.
+}
+
+ULONG WINAPI EtwTraceProvider::ControlCallback(WMIDPREQUESTCODE request,
+    void* context, ULONG *reserved, void* buffer) {
+  reserved;  // Unused.
+  EtwTraceProvider *provider = reinterpret_cast<EtwTraceProvider*>(context);
+
+  return provider->Callback(request, buffer);
+}
+
+ULONG EtwTraceProvider::Register() {
+  if (provider_name_ == GUID_NULL)
+    return ERROR_INVALID_NAME;
+
+  return ::RegisterTraceGuids(ControlCallback, this, &provider_name_,
+      1, &obligatory_guid_registration_, NULL, NULL, &registration_handle_);
+}
+
+ULONG EtwTraceProvider::Unregister() {
+  ULONG ret = ::UnregisterTraceGuids(registration_handle_);
+
+  // Make sure we don't log anything from here on.
+  enable_level_ = 0;
+  enable_flags_ = 0;
+  session_handle_ = NULL;
+  registration_handle_ = NULL;
+
+  return ret;
+}
+
+ULONG EtwTraceProvider::Log(const EtwEventClass& event_class,
+    EtwEventType type, EtwEventLevel level, const char *message) {
+  if (NULL == session_handle_ || enable_level_ < level)
+    return ERROR_SUCCESS;  // No one listening.
+
+  EtwMofEvent<1> event(event_class, type, level);
+
+  event.fields[0].DataPtr = reinterpret_cast<ULONG_PTR>(message);
+  event.fields[0].Length = message ?
+      static_cast<ULONG>(sizeof(message[0]) * (1 + strlen(message))) : 0;
+
+  return ::TraceEvent(session_handle_, &event.header);
+}
+
+ULONG EtwTraceProvider::Log(const EtwEventClass& event_class,
+    EtwEventType type, EtwEventLevel level, const wchar_t *message) {
+  if (NULL == session_handle_ || enable_level_ < level)
+    return ERROR_SUCCESS;  // No one listening.
+
+  EtwMofEvent<1> event(event_class, type, level);
+
+  event.fields[0].DataPtr = reinterpret_cast<ULONG_PTR>(message);
+  event.fields[0].Length = message ?
+      static_cast<ULONG>(sizeof(message[0]) * (1 + wcslen(message))) : 0;
+
+  return ::TraceEvent(session_handle_, &event.header);
+}
+
+ULONG EtwTraceProvider::Log(EVENT_TRACE_HEADER* event) {
+  if (enable_level_ < event->Class.Level)
+    return ERROR_SUCCESS;
+
+  return ::TraceEvent(session_handle_, event);
+}
+
+}  // namespace omaha
diff --git a/base/event_trace_provider.h b/base/event_trace_provider.h
new file mode 100644
index 0000000..7fedeae
--- /dev/null
+++ b/base/event_trace_provider.h
@@ -0,0 +1,171 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Declaration of a Windows event trace provider class, to allow using
+// Windows Event Tracing for logging transport and control.
+#ifndef BASE_EVENT_TRACE_PROVIDER_H_
+#define BASE_EVENT_TRACE_PROVIDER_H_
+
+#include <windows.h>
+#include <wmistr.h>
+#include <evntrace.h>
+#include "omaha/base/debug.h"
+#include "base/basictypes.h"
+
+namespace omaha {
+
+typedef GUID EtwEventClass;
+typedef UCHAR EtwEventType;
+typedef UCHAR EtwEventLevel;
+typedef USHORT EtwEventVersion;
+typedef ULONG EtwEventFlags;
+
+// Base class is a POD for correctness.
+template <size_t N> struct EtwMofEventBase {
+  EVENT_TRACE_HEADER header;
+  MOF_FIELD fields[N];
+};
+
+// Utility class to auto-initialize event trace header structures.
+template <size_t N> class EtwMofEvent: public EtwMofEventBase<N> {
+ public:
+  typedef EtwMofEventBase<N> Super;
+
+  EtwMofEvent() {
+    memset(static_cast<Super*>(this), 0, sizeof(Super));
+  }
+
+  EtwMofEvent(const EtwEventClass& event_class, EtwEventType type,
+              EtwEventLevel level) {
+    memset(static_cast<Super*>(this), 0, sizeof(Super));
+    header.Size = sizeof(Super);
+    header.Guid = event_class;
+    header.Class.Type = type;
+    header.Class.Level = level;
+    header.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
+  }
+
+  EtwMofEvent(const EtwEventClass& event_class, EtwEventType type,
+              EtwEventVersion version, EtwEventLevel level) {
+    memset(static_cast<Super*>(this), 0, sizeof(Super));
+    header.Size = sizeof(Super);
+    header.Guid = event_class;
+    header.Class.Type = type;
+    header.Class.Version = version;
+    header.Class.Level = level;
+    header.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
+  }
+
+  void SetField(int field, size_t size, const void *data) {
+    ASSERT1(field < N);
+    if ((field < N) && (size <= kuint32max)) {
+      fields[field].DataPtr = reinterpret_cast<ULONG_PTR>(data);
+      fields[field].Length = static_cast<ULONG>(size);
+    }
+  }
+
+  EVENT_TRACE_HEADER* get() { return& header; }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(EtwMofEvent);
+};
+
+// Trace provider with Event Tracing for Windows. The trace provider
+// registers with ETW by its name which is a GUID. ETW calls back to
+// the object whenever the trace level or enable flags for this provider
+// name changes.
+// Users of this class can test whether logging is currently enabled at
+// a particular trace level, and whether particular enable flags are set,
+// before other resources are consumed to generate and issue the log
+// messages themselves.
+class EtwTraceProvider {
+ public:
+  // Creates an event trace provider identified by provider_name, which
+  // will be the name registered with Event Tracing for Windows (ETW).
+  explicit EtwTraceProvider(const GUID& provider_name);
+
+  // Creates an unnamed event trace provider, the provider must be given
+  // a name before registration.
+  EtwTraceProvider();
+  virtual ~EtwTraceProvider();
+
+  // Registers the trace provider with Event Tracing for Windows.
+  // Note: from this point forward ETW may call the provider's control
+  //    callback. If the provider's name is enabled in some trace session
+  //    already, the callback may occur recursively from this call, so
+  //    call this only when you're ready to handle callbacks.
+  ULONG Register();
+  // Unregisters the trace provider with ETW.
+  ULONG Unregister();
+
+  // Accessors.
+  void set_provider_name(const GUID& provider_name) {
+    provider_name_ = provider_name;
+  }
+  const GUID& provider_name() const { return provider_name_; }
+  TRACEHANDLE registration_handle() const { return registration_handle_; }
+  TRACEHANDLE session_handle() const { return session_handle_; }
+  EtwEventFlags enable_flags() const { return enable_flags_; }
+  EtwEventLevel enable_level() const { return enable_level_; }
+
+  // Returns true iff logging should be performed for "level" and "flags".
+  // Note: flags is treated as a bitmask, and should normally have a single
+  //      bit set, to test whether to log for a particular sub "facility".
+  bool ShouldLog(EtwEventLevel level, EtwEventFlags flags) {
+    return NULL != session_handle_ && level >= enable_level_ &&
+        (0 != (flags & enable_flags_));
+  }
+
+  // Simple wrappers to log Unicode and ANSI strings.
+  // Do nothing if !ShouldLog(level, 0xFFFFFFFF).
+  ULONG Log(const EtwEventClass& event_class, EtwEventType type,
+            EtwEventLevel level, const char *message);
+  ULONG Log(const EtwEventClass& event_class, EtwEventType type,
+            EtwEventLevel level, const wchar_t *message);
+
+  // Log the provided event.
+  ULONG Log(EVENT_TRACE_HEADER* event);
+
+ protected:
+  // These are called after events have been enabled or disabled.
+  // Override them if you want to do processing at the start or
+  // end of collection.
+  // Note: These may be called ETW's thread and they may be racy.
+  virtual void OnEventsEnabled() {}
+  virtual void OnEventsDisabled() {}
+
+ private:
+  ULONG EnableEvents(PVOID buffer);
+  ULONG DisableEvents();
+  ULONG Callback(WMIDPREQUESTCODE request, PVOID buffer);
+  static ULONG WINAPI ControlCallback(WMIDPREQUESTCODE request, PVOID context,
+                                      ULONG *reserved, PVOID buffer);
+
+  GUID provider_name_;
+  TRACEHANDLE registration_handle_;
+  TRACEHANDLE session_handle_;
+  EtwEventFlags enable_flags_;
+  EtwEventLevel enable_level_;
+
+  // We don't use this, but on XP we're obliged to pass one in to
+  // RegisterTraceGuids. Non-const, because that's how the API needs it.
+  static TRACE_GUID_REGISTRATION obligatory_guid_registration_;
+
+  DISALLOW_COPY_AND_ASSIGN(EtwTraceProvider);
+};
+
+}  // namespace omaha
+
+#endif  // BASE_EVENT_TRACE_PROVIDER_H_
diff --git a/base/event_trace_provider_unittest.cc b/base/event_trace_provider_unittest.cc
new file mode 100644
index 0000000..a99cf21
--- /dev/null
+++ b/base/event_trace_provider_unittest.cc
@@ -0,0 +1,118 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Unit tests for event trace provider.
+#include "omaha/base/event_trace_provider.h"
+#include <new>
+#include "omaha/testing/unit_test.h"
+#include <initguid.h>  // NOLINT - has to be last
+
+namespace omaha {
+
+// {7F0FD37F-FA3C-4cd6-9242-DF60967A2CB2}
+DEFINE_GUID(kTestProvider,
+  0x7f0fd37f, 0xfa3c, 0x4cd6, 0x92, 0x42, 0xdf, 0x60, 0x96, 0x7a, 0x2c, 0xb2);
+
+// {7F0FD37F-FA3C-4cd6-9242-DF60967A2CB2}
+DEFINE_GUID(kTestEventClass,
+  0x7f0fd37f, 0xfa3c, 0x4cd6, 0x92, 0x42, 0xdf, 0x60, 0x96, 0x7a, 0x2c, 0xb2);
+
+TEST(EtwTraceProviderTest, ToleratesPreCreateInvocations) {
+  // Because the trace provider is used in logging, it's important that
+  // it be possible to use static provider instances without regard to
+  // whether they've been constructed or destructed.
+  // The interface of the class is designed to tolerate this usage.
+  char buf[sizeof(EtwTraceProvider)] = {0};
+  EtwTraceProvider& provider = reinterpret_cast<EtwTraceProvider&>(buf);
+
+  EXPECT_EQ(NULL, provider.registration_handle());
+  EXPECT_EQ(NULL, provider.session_handle());
+  EXPECT_EQ(0, provider.enable_flags());
+  EXPECT_EQ(0, provider.enable_level());
+
+  EXPECT_FALSE(provider.ShouldLog(TRACE_LEVEL_FATAL, 0xfffffff));
+
+  // We expect these not to crash.
+  provider.Log(kTestEventClass, 0, TRACE_LEVEL_FATAL, "foo");
+  provider.Log(kTestEventClass, 0, TRACE_LEVEL_FATAL, L"foo");
+
+  EtwMofEvent<1> dummy(kTestEventClass, 0, TRACE_LEVEL_FATAL);
+  DWORD data = 0;
+  dummy.SetField(0, sizeof(data), &data);
+  provider.Log(dummy.get());
+
+  // Placement-new the provider into our buffer.
+  new (buf) EtwTraceProvider(kTestProvider);  // NOLINT
+
+  // Registration is now safe.
+  EXPECT_EQ(ERROR_SUCCESS, provider.Register());
+
+  // Destruct the instance, this should unregister it.
+  provider.EtwTraceProvider::~EtwTraceProvider();
+
+  // And post-destruction, all of the above should still be safe.
+  EXPECT_EQ(NULL, provider.registration_handle());
+  EXPECT_EQ(NULL, provider.session_handle());
+  EXPECT_EQ(0, provider.enable_flags());
+  EXPECT_EQ(0, provider.enable_level());
+
+  EXPECT_FALSE(provider.ShouldLog(TRACE_LEVEL_FATAL, 0xfffffff));
+
+  // We expect these not to crash.
+  provider.Log(kTestEventClass, 0, TRACE_LEVEL_FATAL, "foo");
+  provider.Log(kTestEventClass, 0, TRACE_LEVEL_FATAL, L"foo");
+  provider.Log(dummy.get());
+}
+
+TEST(EtwTraceProviderTest, Initialize) {
+  EtwTraceProvider provider(kTestProvider);
+
+  EXPECT_EQ(NULL, provider.registration_handle());
+  EXPECT_EQ(NULL, provider.session_handle());
+  EXPECT_EQ(0, provider.enable_flags());
+  EXPECT_EQ(0, provider.enable_level());
+}
+
+TEST(EtwTraceProviderTest, Register) {
+  EtwTraceProvider provider(kTestProvider);
+
+  EXPECT_EQ(ERROR_SUCCESS, provider.Register());
+  EXPECT_NE(NULL, provider.registration_handle());
+  EXPECT_EQ(ERROR_SUCCESS, provider.Unregister());
+  EXPECT_EQ(NULL, provider.registration_handle());
+}
+
+TEST(EtwTraceProviderTest, RegisterWithNoNameFails) {
+  EtwTraceProvider provider;
+
+  EXPECT_TRUE(provider.Register() != ERROR_SUCCESS);
+}
+
+TEST(EtwTraceProviderTest, Enable) {
+  EtwTraceProvider provider(kTestProvider);
+
+  EXPECT_EQ(ERROR_SUCCESS, provider.Register());
+  EXPECT_NE(NULL, provider.registration_handle());
+
+  // No session so far.
+  EXPECT_EQ(NULL, provider.session_handle());
+  EXPECT_EQ(0, provider.enable_flags());
+  EXPECT_EQ(0, provider.enable_level());
+
+  EXPECT_EQ(ERROR_SUCCESS, provider.Unregister());
+  EXPECT_EQ(NULL, provider.registration_handle());
+}
+
+}  // namespace omaha
diff --git a/base/exception_barrier.cc b/base/exception_barrier.cc
new file mode 100644
index 0000000..1bfca5e
--- /dev/null
+++ b/base/exception_barrier.cc
@@ -0,0 +1,53 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// A class to make it easy to tag exception propagation boundaries and
+// get crash reports of exceptions that pass over same.
+#include "omaha/base/exception_barrier.h"
+
+enum {
+  // Flag set by exception handling machinery when unwinding
+  EH_UNWINDING = 0x00000002
+};
+
+// TODO(omaha): How to make this statically parameterizable?
+ExceptionBarrier::ExceptionHandler ExceptionBarrier::s_handler_ = NULL;
+
+// This function must be extern "C" to match up with the SAFESEH
+// declaration in our corresponding ASM file
+extern "C" EXCEPTION_DISPOSITION __cdecl
+ExceptionBarrierHandler(struct _EXCEPTION_RECORD *exception_record,
+                        void * establisher_frame,
+                        struct _CONTEXT *context,
+                        void * reserved) {
+  establisher_frame;  // unreferenced formal parameter
+  reserved;
+  if (!(exception_record->ExceptionFlags & EH_UNWINDING)) {
+    // When the exception is really propagating through us, we'd like to be
+    // called before the state of the program has been modified by the stack
+    // unwinding. In the absence of an exception handler, the unhandled
+    // exception filter gets called between the first chance and the second
+    // chance exceptions, so Windows pops either the JIT debugger or WER UI.
+    // This is not desirable in most of the cases.
+    ExceptionBarrier::ExceptionHandler handler = ExceptionBarrier::handler();
+    if (handler) {
+      EXCEPTION_POINTERS ptrs = { exception_record, context };
+
+      handler(&ptrs);
+    }
+  }
+
+  return ExceptionContinueSearch;
+}
diff --git a/common/exception_barrier.h b/base/exception_barrier.h
similarity index 100%
rename from common/exception_barrier.h
rename to base/exception_barrier.h
diff --git a/common/exception_barrier_lowlevel.asm b/base/exception_barrier_lowlevel.asm
similarity index 100%
rename from common/exception_barrier_lowlevel.asm
rename to base/exception_barrier_lowlevel.asm
diff --git a/common/exception_utils.cc b/base/exception_utils.cc
similarity index 100%
rename from common/exception_utils.cc
rename to base/exception_utils.cc
diff --git a/common/exception_utils.h b/base/exception_utils.h
similarity index 100%
rename from common/exception_utils.h
rename to base/exception_utils.h
diff --git a/base/extractor.cc b/base/extractor.cc
new file mode 100644
index 0000000..9e7dcaa
--- /dev/null
+++ b/base/extractor.cc
@@ -0,0 +1,257 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/extractor.h"
+
+#include <windows.h>
+#include <wintrust.h>
+#include <crtdbg.h>
+#pragma warning(push)
+// C4100: unreferenced formal parameter
+// C4310: cast truncates constant value
+// C4548: expression before comma has no effect
+#pragma warning(disable : 4100 4310 4548)
+#include "base/basictypes.h"
+#pragma warning(pop)
+
+namespace omaha {
+
+#define AFFILIATE_ID_MAGIC "Gact"
+
+TagExtractor::TagExtractor()
+    : file_handle_(INVALID_HANDLE_VALUE),
+      file_mapping_(NULL),
+      file_base_(NULL),
+      file_length_(0),
+      cert_length_(0),
+      cert_dir_base_(NULL) {
+}
+
+TagExtractor::~TagExtractor() {
+  CloseFile();
+}
+
+bool TagExtractor::OpenFile(const TCHAR* filename) {
+  CloseFile();
+  file_handle_ = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
+    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+  if (IsFileOpen()) {
+    file_mapping_ = CreateFileMapping(file_handle_, NULL, PAGE_READONLY,
+      0, 0, NULL);
+    if (file_mapping_ != NULL) {
+      file_base_ = MapViewOfFile(file_mapping_, FILE_MAP_READ, 0, 0, 0);
+      if (file_base_ != NULL) {
+        MEMORY_BASIC_INFORMATION info = {0};
+        if (::VirtualQuery(file_base_, &info, sizeof(info))) {
+          file_length_ = info.RegionSize;
+          return true;
+        }
+      }
+      CloseHandle(file_mapping_);
+    }
+    CloseFile();
+  }
+  return false;
+}
+
+bool TagExtractor::IsFileOpen() const {
+  return file_handle_ != INVALID_HANDLE_VALUE;
+}
+
+void TagExtractor::CloseFile() {
+  if (file_base_ != NULL) {
+    UnmapViewOfFile(file_base_);
+    file_base_ = NULL;
+  }
+  if (file_mapping_ != NULL) {
+    CloseHandle(file_mapping_);
+    file_mapping_ = NULL;
+  }
+  if (IsFileOpen()) {
+    CloseHandle(file_handle_);
+    file_handle_ = INVALID_HANDLE_VALUE;
+  }
+}
+
+bool TagExtractor::ExtractTag(const char* binary_file,
+                              size_t binary_file_length,
+                              char* tag_buffer,
+                              int* tag_buffer_len) {
+  file_length_ = binary_file_length;
+  return InternalExtractTag(binary_file, tag_buffer, tag_buffer_len);
+}
+
+bool TagExtractor::ExtractTag(char* tag_buffer, int* tag_buffer_len) {
+  if (tag_buffer_len == NULL) {
+    return false;
+  }
+  if (!IsFileOpen()) {
+    return false;
+  }
+
+  return InternalExtractTag(static_cast<char*>(file_base_),
+                            tag_buffer,
+                            tag_buffer_len);
+}
+
+bool TagExtractor::InternalReadCertificate(const char* file_buffer) {
+  if (!file_buffer) {
+    return false;
+  }
+
+  const void* certificate_directory_pointer =
+    GetCertificateDirectoryPointer(file_buffer);
+  if (NULL == certificate_directory_pointer) {
+    return false;
+  }
+  const void* asn1_signature_pointer =
+    GetASN1SignaturePointer(certificate_directory_pointer);
+  if (NULL == asn1_signature_pointer) {
+    return false;
+  }
+  DWORD asn1_signature_length =
+    GetASN1SignatureLength(asn1_signature_pointer);
+  if (0 == asn1_signature_length) {
+    return false;
+  }
+
+  cert_length_ = asn1_signature_length;
+  cert_dir_base_ = certificate_directory_pointer;
+
+  return true;
+}
+
+bool TagExtractor::InternalExtractTag(const char* file_buffer,
+                                      char* tag_buffer,
+                                      int* tag_buffer_len) {
+  if (!file_buffer) {
+    return false;
+  }
+
+  if (!InternalReadCertificate(file_buffer)) {
+    return false;
+  }
+
+  const char* read_base = static_cast<const char*>(cert_dir_base_) +
+                          cert_length_;
+  if (read_base >= file_buffer + file_length_) {
+    // The file is not tagged.
+    return false;
+  }
+
+  return ReadTag(read_base, tag_buffer, tag_buffer_len);
+}
+
+bool TagExtractor::ReadTag(const char* tag_pointer,
+                           char* tag_buffer,
+                           int* tag_buffer_len) const {
+  int mc = memcmp(tag_pointer,
+                  AFFILIATE_ID_MAGIC,
+                  arraysize(AFFILIATE_ID_MAGIC) - 1);
+  if (0 != mc) {
+    return false;
+  }
+  tag_pointer += arraysize(AFFILIATE_ID_MAGIC) - 1;
+
+  uint16 id_len = 0;
+  const unsigned char* id_len_serialized =
+    reinterpret_cast<const unsigned char*>(tag_pointer);
+  id_len = id_len_serialized[0] << 8;
+  // unsigned char and uint16 get promoted to int.
+  id_len = static_cast<uint16>(id_len + id_len_serialized[1]);
+
+  int buffer_size_required = id_len + 1;
+  if (tag_buffer == NULL) {
+    *tag_buffer_len = buffer_size_required;
+    return true;
+  }
+  if (*tag_buffer_len < buffer_size_required) {
+    return false;
+  }
+  tag_pointer += sizeof(id_len);
+  memcpy(tag_buffer, tag_pointer, id_len);
+  tag_buffer[id_len] = '\0';
+  return true;
+}
+
+const void* TagExtractor::GetCertificateDirectoryPointer(
+    const void* base) const {
+  const char* image_base = reinterpret_cast<const char*>(base);
+
+  // Is this a PEF?
+  const IMAGE_DOS_HEADER* dos_header =
+    reinterpret_cast<const IMAGE_DOS_HEADER *>(image_base);
+  if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
+    return NULL;
+  }
+
+  // Get PE header.
+  const IMAGE_NT_HEADERS* nt_headers = reinterpret_cast<const IMAGE_NT_HEADERS*>
+      (image_base + dos_header->e_lfanew);
+
+  // Again, is this a PEF? This code should get an F for not being endian-
+  // safe, but it gets an A for working in the real world.
+  if (nt_headers->Signature != IMAGE_NT_SIGNATURE) {
+    return NULL;
+  }
+
+  const IMAGE_DATA_DIRECTORY* idd =
+    reinterpret_cast<const IMAGE_DATA_DIRECTORY *>
+    (&nt_headers->
+    OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]);
+  if (idd->VirtualAddress != NULL) {
+    return image_base + idd->VirtualAddress;
+  }
+  return NULL;
+}
+
+const void* TagExtractor::GetASN1SignaturePointer(const void* base) const {
+  const WIN_CERTIFICATE* cert =
+    reinterpret_cast<const WIN_CERTIFICATE *>(base);
+  return cert->bCertificate;
+}
+
+int TagExtractor::GetASN1SignatureLength(const void* base) const {
+  const unsigned char* sig_base =
+    reinterpret_cast<const unsigned char*>(base);
+
+  // No, this isn't a full ASN.1 parser. We're just doing the very bare
+  // minimum to extract a length.
+  if (*sig_base++ == 0x30 && *sig_base++ == 0x82) {
+    int len = (*sig_base++ << 8);
+    len += *sig_base++;
+    // Windows pads the certificate directory to align at a 8-byte boundary.
+    // This piece of code it trying to replicate the logic that is used to
+    // calculate the padding to be added to the certificate. It returns
+    // the entire length of the certificate directory from the windows
+    // certificate directory start to the end of padding.
+    // The windows certificate directory has the following structure
+    // <WIN_CERTIFICATE><Certificate><Padding>.
+    // WIN_CERTIFICATE is the windows certificate directory structure.
+    // <Certificate> has the following format:
+    // <Magic(2 bytes)><Cert length(2 bytes)><Certificate Data>
+    // Note that the "Cert length" does not include the magic bytes or
+    // the length.
+    //
+    // Hence the total length of the certificate is:
+    // cert_length + "WIN_CERTIFICATE header size" + magic + length
+    // + padding = (cert length + 8 + 2 + 2 + 7) & (0-8)
+    return (len + 8 + 2 + 2 + 7) & 0xffffff8;
+  }
+  return 0;
+}
+
+}  // namespace omaha
+
diff --git a/common/extractor.h b/base/extractor.h
similarity index 100%
rename from common/extractor.h
rename to base/extractor.h
diff --git a/base/extractor_unittest.cc b/base/extractor_unittest.cc
new file mode 100644
index 0000000..180fe59
--- /dev/null
+++ b/base/extractor_unittest.cc
@@ -0,0 +1,246 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Unit test for the extractor and the ApplyTag class.
+//
+// TODO(omaha): eliminate the dependency on the hardcoded "GoogleUpdate.exe"
+// program name.
+
+#include <shlobj.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/apply_tag.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/extractor.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+const TCHAR kFilePath[] = _T(".");
+const TCHAR kFileName[] = _T("GoogleUpdate.exe");
+const char kTagString[] = "1234567890abcdefg";
+const char kAppendTagString[] = "..AppendedStr";
+
+TEST(ExtractorTest, EmbedExtract) {
+  // Test the extractor.
+  TagExtractor extractor;
+  ASSERT_FALSE(extractor.IsFileOpen());
+
+  CString signed_exe_file;
+  signed_exe_file.Format(_T("%s\\%s\\%s"),
+                         app_util::GetCurrentModuleDirectory(),
+                         kFilePath, kFileName);
+  ASSERT_TRUE(extractor.OpenFile(signed_exe_file));
+
+  // No tag string in the original exe file.
+  int tag_buffer_size = 0;
+  ASSERT_FALSE(extractor.ExtractTag(NULL, &tag_buffer_size));
+  ASSERT_EQ(tag_buffer_size, 0);
+  extractor.CloseFile();
+
+  // Create a temp dir.
+  TCHAR temp_path[MAX_PATH] = {0};
+  *temp_path = 0;
+  ASSERT_NE(::GetTempPath(MAX_PATH, temp_path), 0);
+
+  // Embed the tag string.
+  CString tagged_file;
+  tagged_file.Format(_T("%s%s"), temp_path, kFileName);
+  omaha::ApplyTag tag;
+  ASSERT_HRESULT_SUCCEEDED(tag.Init(signed_exe_file,
+                                    kTagString,
+                                    strlen(kTagString),
+                                    tagged_file,
+                                    false));
+  ASSERT_SUCCEEDED(tag.EmbedTagString());
+  ON_SCOPE_EXIT(::DeleteFile, tagged_file);
+
+  // Extract the tag string.
+  tag_buffer_size = 0;
+  ASSERT_TRUE(extractor.OpenFile(tagged_file));
+  ASSERT_TRUE(extractor.ExtractTag(NULL, &tag_buffer_size));
+  ASSERT_EQ(tag_buffer_size, arraysize(kTagString));
+
+  char tag_buffer[arraysize(kTagString)] = {0};
+  ASSERT_TRUE(extractor.ExtractTag(tag_buffer, &tag_buffer_size));
+  ASSERT_EQ(tag_buffer_size, arraysize(kTagString));
+  ASSERT_EQ(memcmp(tag_buffer, kTagString, arraysize(kTagString)), 0);
+  extractor.CloseFile();
+}
+
+TEST(ExtractorTest, EmbedAppendExtract) {
+  // Test the extractor.
+  TagExtractor extractor;
+  ASSERT_FALSE(extractor.IsFileOpen());
+
+  CString signed_exe_file;
+  signed_exe_file.Format(_T("%s\\%s\\%s"),
+                         app_util::GetCurrentModuleDirectory(),
+                         kFilePath, kFileName);
+  ASSERT_TRUE(extractor.OpenFile(signed_exe_file));
+
+  // No tag string in the original exe file.
+  int tag_buffer_size = 0;
+  ASSERT_FALSE(extractor.ExtractTag(NULL, &tag_buffer_size));
+  ASSERT_GT(extractor.cert_length(), 0);
+  ASSERT_EQ(tag_buffer_size, 0);
+  extractor.CloseFile();
+
+  // Create a temp dir.
+  TCHAR temp_path[MAX_PATH] = {0};
+  *temp_path = 0;
+  ASSERT_NE(::GetTempPath(MAX_PATH, temp_path), 0);
+
+  // Embed the tag string.
+  CString tagged_file;
+  tagged_file.Format(_T("%s%d%s"), temp_path, 1, kFileName);
+  omaha::ApplyTag tag;
+  ASSERT_HRESULT_SUCCEEDED(tag.Init(signed_exe_file,
+                                    kTagString,
+                                    strlen(kTagString),
+                                    tagged_file,
+                                    false));
+  ASSERT_SUCCEEDED(tag.EmbedTagString());
+  ON_SCOPE_EXIT(::DeleteFile, tagged_file);
+
+  // Append another tag string.
+  CString tagged_appended_file;
+  tagged_appended_file.Format(_T("%s%d%s"), temp_path, 2, kFileName);
+  omaha::ApplyTag tag1;
+
+  ASSERT_HRESULT_SUCCEEDED(tag1.Init(tagged_file,
+                                     kAppendTagString,
+                                     strlen(kAppendTagString),
+                                     tagged_appended_file,
+                                     true));
+  ASSERT_SUCCEEDED(tag1.EmbedTagString());
+  ON_SCOPE_EXIT(::DeleteFile, tagged_appended_file);
+
+  // Append another tag string.
+  CString tagged_appended_file2;
+  tagged_appended_file2.Format(_T("%s%d%s"), temp_path, 3, kFileName);
+  omaha::ApplyTag tag2;
+  ASSERT_HRESULT_SUCCEEDED(tag2.Init(tagged_appended_file,
+                                     kAppendTagString,
+                                     strlen(kAppendTagString),
+                                     tagged_appended_file2,
+                                     true));
+  ASSERT_SUCCEEDED(tag2.EmbedTagString());
+  ON_SCOPE_EXIT(::DeleteFile, tagged_appended_file2);
+
+  // Extract the tag string.
+  tag_buffer_size = 0;
+  CStringA expected_tag_string(kTagString);
+  expected_tag_string += kAppendTagString;
+  expected_tag_string += kAppendTagString;
+  int expected_tag_string_len = expected_tag_string.GetLength() + 1;
+  ASSERT_TRUE(extractor.OpenFile(tagged_appended_file2));
+  ASSERT_TRUE(extractor.ExtractTag(NULL, &tag_buffer_size));
+  ASSERT_EQ(tag_buffer_size, expected_tag_string_len);
+
+  scoped_array<char> tag_buffer(new char[expected_tag_string_len]);
+  ASSERT_TRUE(extractor.ExtractTag(tag_buffer.get(), &tag_buffer_size));
+  ASSERT_EQ(tag_buffer_size, expected_tag_string_len);
+  ASSERT_EQ(memcmp(tag_buffer.get(),
+                   expected_tag_string,
+                   expected_tag_string_len),
+            0);
+  extractor.CloseFile();
+}
+
+TEST(ExtractorTest, AlreadyTaggedError) {
+  // Test the extractor.
+  TagExtractor extractor;
+  ASSERT_FALSE(extractor.IsFileOpen());
+
+  CString signed_exe_file;
+  signed_exe_file.Format(_T("%s\\%s\\%s"),
+                         app_util::GetCurrentModuleDirectory(),
+                         kFilePath, kFileName);
+  ASSERT_TRUE(extractor.OpenFile(signed_exe_file));
+
+  // No tag string in the original exe file.
+  int tag_buffer_size = 0;
+  ASSERT_FALSE(extractor.ExtractTag(NULL, &tag_buffer_size));
+  ASSERT_GT(extractor.cert_length(), 0);
+  ASSERT_EQ(tag_buffer_size, 0);
+  extractor.CloseFile();
+
+  // Create a temp dir.
+  TCHAR temp_path[MAX_PATH] = {0};
+  *temp_path = 0;
+  ASSERT_NE(::GetTempPath(MAX_PATH, temp_path), 0);
+
+  // Embed the tag string.
+  CString tagged_file;
+  tagged_file.Format(_T("%s%d%s"), temp_path, 1, kFileName);
+  omaha::ApplyTag tag1;
+  ASSERT_HRESULT_SUCCEEDED(tag1.Init(signed_exe_file,
+                                     kTagString,
+                                     strlen(kTagString),
+                                     tagged_file,
+                                     false));
+  ASSERT_SUCCEEDED(tag1.EmbedTagString());
+  ON_SCOPE_EXIT(::DeleteFile, tagged_file);
+
+  CString tagged_appended_file;
+  tagged_appended_file.Format(_T("%s%d%s"), temp_path, 2, kFileName);
+  omaha::ApplyTag tag2;
+  ASSERT_HRESULT_SUCCEEDED(tag2.Init(tagged_file,
+                                     kAppendTagString,
+                                     strlen(kAppendTagString),
+                                     tagged_appended_file,
+                                     false));
+  ASSERT_EQ(tag2.EmbedTagString(), APPLYTAG_E_ALREADY_TAGGED);
+  ON_SCOPE_EXIT(::DeleteFile, tagged_appended_file);
+  extractor.CloseFile();
+}
+
+TEST(ApplyTagTest, InvalidCharsTest) {
+  // Accepted Regex = [-%{}/\a&=._]*
+  CString signed_exe_file;
+  signed_exe_file.Format(_T("%s\\%s\\%s"),
+                         app_util::GetCurrentModuleDirectory(),
+                         kFilePath, kFileName);
+  CString tagged_file(_T("out.txt"));
+
+  const char* const input_str = "abcd";
+  omaha::ApplyTag tag1;
+  ASSERT_HRESULT_SUCCEEDED(tag1.Init(signed_exe_file,
+                                     input_str,
+                                     strlen(input_str),
+                                     tagged_file,
+                                     false));
+
+  const char* const input_str2 = "abcd$%#";
+  omaha::ApplyTag tag2;
+  ASSERT_HRESULT_FAILED(tag2.Init(signed_exe_file,
+                                  input_str2,
+                                  strlen(input_str2),
+                                  tagged_file,
+                                  false));
+
+  const char* const input_str3 = "abcd asdf";
+  omaha::ApplyTag tag3;
+  ASSERT_HRESULT_FAILED(tag3.Init(signed_exe_file,
+                                  input_str3,
+                                  strlen(input_str3),
+                                  tagged_file,
+                                  false));
+}
+
+}  // namespace omaha
+
diff --git a/base/file.cc b/base/file.cc
new file mode 100644
index 0000000..8317af1
--- /dev/null
+++ b/base/file.cc
@@ -0,0 +1,1430 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+// File handling routines
+//
+// Possible performance improvement: make a subclass or alternate class
+// that reads an entire file into memory and fulfills read/write requests
+// in memory (or equivalently, use a memory mapped file). This can greatly
+// improve performance if there are files we do a lot of read/write requests on.
+//
+// We generally are dealing with files that can be very large and want to
+// minimize memory usage, so this is not high priority
+//
+// this has the beginnings of asynchronous access support
+//
+// Unfortunately, doing asynchronous reads with FILE_FLAG_OVERLAPPED buys us
+// nothing because the system cache manager enforces serial requests.
+//
+// Hence, we need to also use FILE_FLAG_NO_BUFFERING. this has a number of
+// constraints:
+// - file read/write position must be aligned on multiples of the disk sector
+//   size
+// - file read/write length must be a multiple of the sector size
+// - read/write buffer must be aligned on multiples of the disk sector size
+//
+// In particular, this means that we cannot write 8 bytes, for example, because
+// we have to write an entire sector.
+//
+// Currently, the implementation only supports enough to do some simple read
+// tests
+//
+// The general idea is code that wants to to a sequence of asynchronous actions
+// will look like the following, for an example of reading multiple event
+// records asynchronously:
+//
+// uint32 async_id = File::GetNextAsyncId()
+// while (!done) {
+//   for (everything_to_do, e.g., for each event to read) {
+//     call File::Read to read items needed;
+//     returns TR_E_FILE_ASYNC_PENDING if queued; or returns data if done
+//     process the item (e.g., event) if desired
+//   }
+//   call some routine to process pending completions; initiate delayed action
+// }
+// call some cleanup routine
+
+#include "omaha/base/file.h"
+#include <algorithm>
+#include "base/scoped_ptr.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/const_config.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/string.h"
+#include "omaha/base/system.h"
+#include "omaha/base/timer.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+// Constants
+const uint32 kZeroSize = 4096;  // Buffer size used for clearing data in a file.
+
+// The moves-pending-reboot is a MULTISZ registry key in the HKLM part of the
+// registry.
+static const TCHAR* kSessionManagerKey =
+    _T("HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager");
+static const TCHAR* kPendingFileRenameOps = _T("PendingFileRenameOperations");
+
+File::File()
+    : handle_(INVALID_HANDLE_VALUE), read_only_(false), sequence_id_(0) {
+}
+
+File::~File() {
+  if (handle_ != INVALID_HANDLE_VALUE) {
+    VERIFY1(SUCCEEDED(Close()));
+  }
+}
+
+// open for reading only if write == false, otherwise both reading and writing
+// allow asynchronous operations if async == true. Use this function when you
+// need exclusive access to the file.
+HRESULT File::Open(const TCHAR* file_name, bool write, bool async) {
+  return OpenShareMode(file_name, write, async, 0);
+}
+
+// Allows specifying a sharing mode such as FILE_SHARE_READ. Otherwise,
+// this is identical to File::Open().
+HRESULT File::OpenShareMode(const TCHAR* file_name,
+                            bool write,
+                            bool async,
+                            DWORD share_mode) {
+  ASSERT1(file_name && *file_name);
+  ASSERT1(handle_ == INVALID_HANDLE_VALUE);
+  VERIFY1(!async);
+
+  file_name_ = file_name;
+
+  // there are restrictions on what we can do if using FILE_FLAG_NO_BUFFERING
+  // if (!buffer) { flags |= FILE_FLAG_NO_BUFFERING; }
+  // FILE_FLAG_WRITE_THROUGH
+  // how efficient is NTFS encryption? FILE_ATTRIBUTE_ENCRYPTED
+  // FILE_ATTRIBUTE_TEMPORARY
+  // FILE_FLAG_RANDOM_ACCESS
+  // FILE_FLAG_SEQUENTIAL_SCAN
+
+  handle_ = ::CreateFile(file_name,
+                         write ? (FILE_WRITE_DATA       |
+                                  FILE_WRITE_ATTRIBUTES |
+                                  FILE_READ_DATA) : FILE_READ_DATA,
+                         share_mode,
+                         NULL,
+                         write ? OPEN_ALWAYS : OPEN_EXISTING,
+                         FILE_FLAG_RANDOM_ACCESS,
+                         NULL);
+
+  if (handle_ == INVALID_HANDLE_VALUE) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR,
+            (_T("[File::OpenShareMode - CreateFile failed][%s][%d][%d][0x%x]"),
+             file_name, write, async, hr));
+    return hr;
+  }
+
+  // This attribute is not supported directly by the CreateFile function.
+  if (write &&
+      !::SetFileAttributes(file_name, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR,
+            (_T("[File::OpenShareMode - SetFileAttributes failed][0x%x]"), hr));
+    return hr;
+  }
+
+  read_only_ = !write;
+  pos_ = 0;
+  return S_OK;
+}
+
+// The path must not be enclosed in quotes. This is the Windows standard.
+// ::GetFileAttributesEx() returns ERROR_INVALID_NAME for quoted paths.
+bool File::Exists(const TCHAR* file_name) {
+  ASSERT1(file_name && *file_name);
+  ASSERT1(lstrlen(file_name) > 0);
+
+  // NOTE: This is the fastest implementation I found.  The results were:
+  //   CreateFile           1783739 avg ticks/call
+  //   FindFirstFile         634148 avg ticks/call
+  //   GetFileAttributes     428714 avg ticks/call
+  //   GetFileAttributesEx   396324 avg ticks/call
+  WIN32_FILE_ATTRIBUTE_DATA attrs = {0};
+  return 0 != ::GetFileAttributesEx(file_name, ::GetFileExInfoStandard, &attrs);
+}
+
+bool File::IsDirectory(const TCHAR* file_name) {
+  ASSERT1(file_name && *file_name);
+
+  WIN32_FILE_ATTRIBUTE_DATA attrs;
+  SetZero(attrs);
+  if (!::GetFileAttributesEx(file_name, ::GetFileExInfoStandard, &attrs)) {
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[File::IsDirectory - GetFileAttributesEx failed][%s][0x%x]"),
+              file_name, HRESULTFromLastError()));
+    return false;
+  }
+
+  return (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+}
+
+HRESULT File::GetWildcards(const TCHAR* dir,
+                           const TCHAR* wildcard,
+                           std::vector<CString>* matching_paths) {
+  ASSERT1(dir && *dir);
+  ASSERT1(wildcard && *wildcard);
+  ASSERT1(matching_paths);
+
+  matching_paths->clear();
+
+  // Make sure directory name ends with "\"
+  CString directory = String_MakeEndWith(dir, _T("\\"), false);
+
+  WIN32_FIND_DATA find_data;
+  SetZero(find_data);
+  scoped_hfind hfind(::FindFirstFile(directory + wildcard, &find_data));
+  if (!hfind) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(L5, (_T("[File::GetWildcards - FindFirstFile failed][0x%x]"), hr));
+    return hr;
+  }
+  do {
+    if (find_data.dwFileAttributes == FILE_ATTRIBUTE_NORMAL ||
+        !(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+      CString to_file(directory + find_data.cFileName);
+      matching_paths->push_back(to_file);
+    }
+  } while (::FindNextFile(get(hfind), &find_data));
+
+  HRESULT hr = HRESULTFromLastError();
+  if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) {
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[File::GetWildcards - FindNextFile failed][0x%x]"), hr));
+    return hr;
+  }
+  return S_OK;
+}
+
+// returns error if cannot remove
+// returns success if removed or already removed
+HRESULT File::Remove(const TCHAR* file_name) {
+  ASSERT1(file_name && *file_name);
+
+  if (!Exists(file_name)) {
+    return S_OK;
+  }
+
+  if (!::DeleteFile(file_name)) {
+    return HRESULTFromLastError();
+  }
+
+  return S_OK;
+}
+
+HRESULT File::CopyWildcards(const TCHAR* from_dir,
+                            const TCHAR* to_dir,
+                            const TCHAR* wildcard,
+                            bool replace_existing_files) {
+  ASSERT1(from_dir && *from_dir);
+  ASSERT1(to_dir && *to_dir);
+  ASSERT1(wildcard && *wildcard);
+
+  // Make sure dir names end with a "\"
+  CString from_directory = String_MakeEndWith(from_dir, _T("\\"), false);
+  CString to_directory = String_MakeEndWith(to_dir, _T("\\"), false);
+
+  // Get full path to source files (which is a wildcard)
+  CString from_files(from_directory + wildcard);
+
+  // Run over all files that match wildcard
+  WIN32_FIND_DATA find_data;
+  SetZero(find_data);
+
+  scoped_hfind hfind(::FindFirstFile(from_files, &find_data));
+  if (!hfind) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[File::CopyWildcards - FindFirstFile failed][0x%x]"), hr));
+    return hr;
+  }
+  do {
+    // Copy files
+    if (find_data.dwFileAttributes == FILE_ATTRIBUTE_NORMAL ||
+        !(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+      CString from_file(from_directory + find_data.cFileName);
+      CString to_file(to_directory + find_data.cFileName);
+
+      if (!replace_existing_files && Exists(to_file)) {
+        // Continue, since the caller has explicitly asked us to not replace an
+        // existing file
+        continue;
+      }
+
+      RET_IF_FAILED(Copy(from_file, to_file, replace_existing_files));
+    }
+  } while (::FindNextFile(get(hfind), &find_data));
+
+  HRESULT hr = HRESULTFromLastError();
+  if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) {
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[File::CopyWildcards - FindNextFile failed][0x%x]"), hr));
+    return hr;
+  }
+  return S_OK;
+}
+
+HRESULT File::CopyTree(const TCHAR* from_dir,
+                       const TCHAR* to_dir,
+                       bool replace_existing_files) {
+  ASSERT1(from_dir && *from_dir);
+  ASSERT1(to_dir && *to_dir);
+
+  UTIL_LOG(L3, (L"[File::CopyTree][from_dir %s][to_dir %s][replace %d]",
+                from_dir, to_dir, replace_existing_files));
+
+  // Make sure dir names end with a "\"
+  CString from_directory(String_MakeEndWith(from_dir, L"\\", false));
+  CString to_directory(String_MakeEndWith(to_dir, L"\\", false));
+
+  RET_IF_FAILED(CreateDir(to_directory, NULL));
+  RET_IF_FAILED(CopyWildcards(from_directory,
+                              to_directory,
+                              L"*.*",
+                              replace_existing_files));
+
+  // Run over all directories
+  WIN32_FIND_DATA find_data;
+  SetZero(find_data);
+
+  CString from_files(from_directory);
+  from_files += _T("*.*");
+
+  scoped_hfind hfind(::FindFirstFile(from_files, &find_data));
+  if (!hfind) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[File::CopyTree - FindFirstFile failed][0x%x]"), hr));
+    return hr;
+  }
+  do {
+    // Copy files
+    if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 &&
+        String_StrNCmp(find_data.cFileName, L"..", 2, false) &&
+        String_StrNCmp(find_data.cFileName, L".", 2, false)) {
+      CString from_subdir(from_directory + find_data.cFileName);
+      CString to_subdir(to_directory + find_data.cFileName);
+      RET_IF_FAILED(CopyTree(from_subdir, to_subdir, replace_existing_files));
+    }
+  } while (::FindNextFile(get(hfind), &find_data));
+
+  HRESULT hr = HRESULTFromLastError();
+  if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) {
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[File::CopyTree - FindNextFile failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT File::Copy(const TCHAR* from,
+                   const TCHAR* to,
+                   bool replace_existing_file) {
+  ASSERT1(from && *from);
+  ASSERT1(to && *to);
+
+  if (!replace_existing_file && Exists(to)) {
+    // Return success, since the caller has explicitly asked us to not replace
+    // an existing file
+    return S_OK;
+  }
+
+  if (!::CopyFile(from, to, !replace_existing_file)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[File::Copy - CopyFile failed]")
+                           _T("[from=%s][to=%s][replace=%u][0x%x]"),
+                           from, to, replace_existing_file, hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+// TODO(omaha): Combine common code in Move/MoveAfterReboot
+HRESULT File::Move(const TCHAR* from,
+                   const TCHAR* to,
+                   bool replace_existing_file) {
+  ASSERT1(from && *from);
+  ASSERT1(to && *to);
+
+  DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH;
+  if (replace_existing_file) {
+    flags |= MOVEFILE_REPLACE_EXISTING;
+  }
+
+  if (!::MoveFileEx(from, to, flags)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[File::Move - MoveFileEx failed]")
+              _T("[from=%s][to=%s][replace=%u][0x%x]"),
+              from, to, replace_existing_file, hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+// DeleteAfterReboot tries to delete the files by either moving them to the TEMP
+// directory and deleting them on reboot, or if that fails, by trying to delete
+// them in-place on reboot
+HRESULT File::DeleteAfterReboot(const TCHAR* from) {
+  ASSERT1(from && *from);
+
+  if (File::Exists(from)) {
+    HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+    CString from_temp;
+
+    // No point in moving into TEMP if we're already there
+    if (!String_StartsWith(from, app_util::GetTempDir(), true)) {
+      // Try to move to the TEMP directory first
+      CString temp_dir(String_MakeEndWith(app_util::GetTempDir(),
+                                          _T("\\"),
+                                          false));
+      // Of the form "C:\\Windows\\Temp\\FROM.EXE1f4c0b7f"
+      from_temp.Format(_T("%s%s%x"),
+                       temp_dir,
+                       GetFileFromPath(from),
+                       ::GetTickCount());
+
+      hr = File::Move(from, from_temp, true);
+      UTIL_LOG(L2, (_T("[File::DeleteAfterReboot - move %s to %s][0x%x]"),
+                    from, from_temp, hr));
+    }
+
+    if (SUCCEEDED(hr)) {
+      UTIL_LOG(L2, (_T("[File::DeleteAfterReboot - delete %s after reboot]"),
+                    from_temp));
+      // Move temp file after reboot
+      if (FAILED(hr = File::MoveAfterReboot(from_temp, NULL))) {
+        UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]")
+                               _T("[failed to delete after reboot %s][0x%x]"),
+                               from_temp, hr));
+      }
+    } else  {
+      // Move original file after reboot
+      if (FAILED(hr = File::MoveAfterReboot(from, NULL))) {
+        UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]")
+                               _T("[failed to delete after reboot %s][0x%x]"),
+                               from, hr));
+      }
+    }
+
+    return hr;
+  }
+
+  return S_OK;
+}
+
+
+HRESULT File::MoveAfterReboot(const TCHAR* from, const TCHAR* to) {
+  ASSERT1(from && *from);
+
+  if (!File::Exists(from)) {
+    // File/directory doesn't exist, should this return failure or success?
+    // Decision:  Failure.  Because the caller can decide if it is really
+    // failure or not in his specific case.
+    UTIL_LOG(LEVEL_WARNING, (_T("[File::MoveAfterReboot]")
+                             _T("[file doesn't exist][from %s]"), from));
+    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+  }
+
+  DWORD flags = MOVEFILE_DELAY_UNTIL_REBOOT;
+  if (!File::IsDirectory(from)) {
+    // This flag valid only for files
+    flags |= MOVEFILE_REPLACE_EXISTING;
+  }
+
+  if (!::MoveFileEx(from, to, flags)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[File::MoveAfterReboot]")
+                           _T("[failed to MoveFileEx from '%s' to '%s'][0x%x]"),
+                           from, to, hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+// See if we have any moves pending a reboot. Return SUCCESS if we do
+// not encounter errors (not finding a move is not an error). We need to
+// also check the value of *found_ptr for whether we actually found a move.
+// On return, *value_multisz_ptr is the value within
+// "PendingFileRenameOperations", but with any moves for in_directory removed
+// from it.
+// The prefix_match boolean controls whether we do an exact match on
+// in_directory, or remove all entries with the in_directory prefix.
+// NOTE: If the only values found were our own keys, the whole
+// PendingFileRenameOperations MULTISZ needs to be deleted.
+// This is signified by a returned *value_size_chars_ptr of 0.
+HRESULT File::GetPendingRenamesValueMinusDir(const TCHAR* in_directory,
+                                             bool prefix_match,
+                                             TCHAR** value_multisz_ptr,
+                                             DWORD* value_size_chars_ptr,
+                                             bool* found_ptr) {
+  ASSERT1(in_directory && *in_directory);
+
+  // Convert to references for easier-to-read-code:
+  TCHAR*& value_multisz = *value_multisz_ptr;
+  DWORD& value_size_chars = *value_size_chars_ptr;
+  bool& found = *found_ptr;
+
+  // Initialize [out] parameters
+  value_multisz = NULL;
+  value_size_chars = 0;
+  found = false;
+
+  // Locals mirroring the [out] parameters.
+  // We will only set the corresponding [out] parameters when we have something
+  // meaningful to return to the caller
+  scoped_array<TCHAR> value_multisz_local;
+  DWORD value_size_chars_local = 0;
+
+  DWORD value_size_bytes = 0;
+  // Get the current value of the key
+  // If the Key is missing, that's totally acceptable.
+  RET_IF_FALSE(
+      RegKey::HasValue(kSessionManagerKey, kPendingFileRenameOps) &&
+      SUCCEEDED(RegKey::GetValue(kSessionManagerKey,
+                                 kPendingFileRenameOps,
+                                 reinterpret_cast<byte**>(&value_multisz_local),
+                                 &value_size_bytes)),
+      S_OK);
+
+  ASSERT1(value_multisz_local.get() || value_size_bytes == 0);
+  UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]")
+                _T("[read multisz %d bytes]"),
+                value_size_bytes));
+  RET_IF_FALSE(value_size_bytes > 0, S_OK);
+
+  // The size should always be aligned to a TCHAR boundary, otherwise the key
+  // is corrupted.
+  ASSERT1((value_size_bytes % sizeof(TCHAR)) == 0);
+  RET_IF_FALSE((value_size_bytes % sizeof(TCHAR)) == 0,
+               HRESULT_FROM_WIN32(ERROR_BADKEY));
+  // Valid size, so convert to TCHARs:
+  value_size_chars_local = value_size_bytes / sizeof(TCHAR);
+
+  // Buffer must terminate with two nulls
+  ASSERT(value_size_chars_local >= 2 &&
+         !value_multisz_local[value_size_chars_local - 1] &&
+         !value_multisz_local[value_size_chars_local - 2],
+         (_T("buffer must terminate with two nulls")));
+  RET_IF_FALSE(value_size_chars_local >= 2 &&
+               !value_multisz_local[value_size_chars_local - 1] &&
+               !value_multisz_local[value_size_chars_local - 2],
+               HRESULT_FROM_WIN32(ERROR_BADKEY));
+  // Mark the end of the string.
+  // multisz_end will point at the character past end of buffer:
+  TCHAR* multisz_end = value_multisz_local.get() + value_size_chars_local;
+
+  // We're looking for \??\C:\...  The \??\ was
+  // added by the OS to the directory name we specified.
+  CString from_dir(_T("\\??\\"));
+  from_dir += in_directory;
+  DWORD from_dir_len = from_dir.GetLength();
+
+  // A MULTISZ is a list of null terminated strings, terminated by a double
+  // null.  We keep two pointers marching along the string in parallel.
+  TCHAR* str_read = value_multisz_local.get();
+  TCHAR* str_write = str_read;
+
+  while ((str_read < multisz_end) && *str_read) {
+    size_t str_len = ::lstrlen(str_read);
+  // A FALSE here indicates a corrupt PendingFileRenameOperations
+    RET_IF_FALSE((str_read + str_len + 1) < multisz_end,
+      HRESULT_FROM_WIN32(ERROR_BADKEY));
+    if (0 == String_StrNCmp(str_read,
+                            from_dir,
+                            from_dir_len + (prefix_match ? 0 : 1),
+                            true)) {
+      // String matches, we want to remove this string, so advance only the
+      // read pointer - past this string and the replacement string.
+      UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]")
+                    _T("[skips past match '%s']"),
+                    str_read));
+      str_read += str_len + 1;
+      str_read += ::lstrlen(str_read) + 1;
+      continue;
+    }
+    // String doesn't match, we want to keep it.
+    if (str_read != str_write) {
+      // Here we're not in sync in the buffer, we've got to move two
+      // strings down.
+      UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]")
+                    _T("[copying some other deletion][%s][%s]"),
+                    str_read, str_read + ::lstrlen(str_read) + 1));
+      ASSERT1(str_write < str_read);
+      String_StrNCpy(str_write, str_read, str_len+1);
+      str_read += str_len + 1;
+      str_write += str_len + 1;
+      str_len = ::lstrlen(str_read);
+      String_StrNCpy(str_write, str_read, str_len+1);
+      str_read += str_len + 1;
+      str_write += str_len + 1;
+    } else {
+      // We're in sync in the buffer, advance both pointers past two strings
+      UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]")
+                    _T("[skipping past some other deletion][%s][%s]"),
+                    str_read, str_read + ::lstrlen(str_read) + 1));
+      str_read += str_len + 1;
+      str_read += ::lstrlen(str_read) + 1;
+      str_write = str_read;
+    }
+  }
+
+  // A FALSE here indicates a corrupt PendingFileRenameOperations
+  RET_IF_FALSE(str_read < multisz_end,
+    HRESULT_FROM_WIN32(ERROR_BADKEY));
+
+  if (str_read != str_write) {
+    // We found some values
+    found = true;
+
+    if (str_write == value_multisz_local.get()) {
+      // The only values were our own keys,
+      // and the whole PendingFileRenameOperations
+      // value needs to be deleted. We do not populate
+      // value_size_chars or value_multisz in this case.
+      ASSERT1(!value_size_chars);
+      ASSERT1(!value_multisz);
+    } else  {
+      // The last string should have a NULL terminator:
+      ASSERT1(str_write[-1] == '\0');
+      RET_IF_FALSE(str_write[-1] == '\0',
+        HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
+      // a REG_MULTI_SZ needs to be terminated with an extra NULL.
+      *str_write = '\0';
+      ++str_write;
+
+      // Populate value_size_chars and value_multisz in this case.
+      value_multisz = value_multisz_local.release();
+      value_size_chars = str_write - value_multisz;
+    }
+  }
+
+  return S_OK;
+}
+
+// Remove any moves pending a reboot from the PendingFileRenameOperations
+// in the registry.
+// The prefix_match boolean controls whether we do an exact match on
+// in_directory, or remove all entries with the in_directory prefix.
+HRESULT File::RemoveFromMovesPendingReboot(const TCHAR* in_directory,
+                                           bool prefix_match) {
+  ASSERT1(in_directory && *in_directory);
+
+  bool found = false;
+  // scoped_array will free the value_multisz buffer on stack unwind:
+  scoped_array<TCHAR> value_multisz;
+  DWORD value_size_chars = 0;
+  HRESULT hr = GetPendingRenamesValueMinusDir(in_directory,
+                                              prefix_match,
+                                              address(value_multisz),
+                                              &value_size_chars,
+                                              &found);
+  if (SUCCEEDED(hr) && found) {
+    if (value_multisz.get() == NULL)  {
+      // There's no point in writing an empty value_multisz.
+      // Let's delete the PendingFileRenameOperations value
+      UTIL_LOG(L5, (_T("[File::RemoveFromMovesPendingReboot]")
+                    _T("[deleting PendingFileRenameOperations value]")));
+      RET_IF_FAILED(RegKey::DeleteValue(kSessionManagerKey,
+                    kPendingFileRenameOps));
+    } else  {
+      // Let's write the modified value_multisz into the
+      // PendingFileRenameOperations value
+      UTIL_LOG(L5, (_T("[File::RemoveFromMovesPendingReboot]")
+                    _T("[rewriting multisz %d bytes]"),
+                    value_size_chars * sizeof(TCHAR)));
+      RET_IF_FAILED(RegKey::SetValueMultiSZ(
+          kSessionManagerKey,
+          kPendingFileRenameOps,
+          reinterpret_cast<byte*>(value_multisz.get()),
+          value_size_chars * sizeof(TCHAR)));
+    }
+  }
+
+  // Failure of GetPendingRenamesValueMinusDir() may indicate something
+  // seriously wrong with the system. Propogate error.
+  return hr;
+}
+
+// Did the user try to uninstall a previous install of the same version, and
+// we couldn't clean up without a reboot?
+// We check if there are any moves pending a reboot from the
+// PendingFileRenameOperations in the registry.
+// The prefix_match boolean controls whether we do an exact match on
+// in_directory, or check all entries with the in_directory prefix.
+bool File::AreMovesPendingReboot(const TCHAR* in_directory, bool prefix_match) {
+  ASSERT1(in_directory && *in_directory);
+
+  bool found = false;
+  // scoped_array will free the value_multisz buffer on stack unwind:
+  scoped_array<TCHAR> value_multisz;
+  DWORD value_size_chars = 0;
+
+  if (SUCCEEDED(GetPendingRenamesValueMinusDir(in_directory,
+                                               prefix_match,
+                                               address(value_multisz),
+                                               &value_size_chars,
+                                               &found)) && found) {
+    return true;
+  }
+
+  return false;
+}
+
+HRESULT File::GetFileTime(const TCHAR* file_name,
+                          FILETIME* created,
+                          FILETIME* accessed,
+                          FILETIME* modified) {
+  ASSERT1(file_name && *file_name);
+
+  bool is_dir = IsDirectory(file_name);
+  // To obtain a handle to a directory, call the CreateFile function with
+  // the FILE_FLAG_BACKUP_SEMANTICS flag
+  scoped_hfile file_handle(
+      ::CreateFile(file_name,
+                   FILE_READ_DATA,
+                   FILE_SHARE_READ,
+                   NULL,
+                   OPEN_EXISTING,
+                   is_dir ? FILE_FLAG_BACKUP_SEMANTICS : NULL,
+                   NULL));
+  HRESULT hr = S_OK;
+
+  if (!file_handle) {
+    hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[File::GetFileTime]")
+                  _T("[failed to open file][%s][0x%x]"), file_name, hr));
+  } else {
+    if (!::GetFileTime(get(file_handle), created, accessed, modified)) {
+      hr = HRESULTFromLastError();
+      UTIL_LOG(LEVEL_ERROR, (_T("[File::GetFileTime]")
+                             _T("[failed to get file time][%s][0x%x]"),
+                             file_name, hr));
+    }
+  }
+
+  return hr;
+}
+
+HRESULT File::SetFileTime(const TCHAR* file_name,
+                          const FILETIME* created,
+                          const FILETIME* accessed,
+                          const FILETIME* modified) {
+  ASSERT1(file_name && *file_name);
+
+  bool is_dir = IsDirectory(file_name);
+  // To obtain a handle to a directory, call the CreateFile function with
+  // the FILE_FLAG_BACKUP_SEMANTICS flag
+  scoped_hfile file_handle(
+      ::CreateFile(file_name,
+                   FILE_WRITE_ATTRIBUTES,
+                   FILE_SHARE_WRITE,
+                   NULL,
+                   OPEN_EXISTING,
+                   is_dir ? FILE_FLAG_BACKUP_SEMANTICS : NULL,
+                   NULL));
+  HRESULT hr = S_OK;
+
+  if (!file_handle) {
+    hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[File::GetFileTime]")
+                           _T("[failed to open file][%s][0x%x]"),
+                           file_name, hr));
+  } else {
+    BOOL res = ::SetFileTime(get(file_handle), created, accessed, modified);
+    if (!res) {
+      hr = HRESULTFromLastError();
+      UTIL_LOG(LEVEL_ERROR, (_T("[File::SetFileTime]")
+                             _T("[failed to set file time][%s][0x%x]"),
+                             file_name, hr));
+    }
+  }
+
+  return hr;
+}
+
+HRESULT File::Sync() {
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+
+  if (!::FlushFileBuffers(handle_)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[File::Sync]")
+                           _T("[FlushFileBuffers failed][%s][0x%x]"),
+                           file_name_, hr));
+    return hr;
+  }
+  return S_OK;
+}
+
+HRESULT File::SeekToBegin() {
+  return SeekFromBegin(0);
+}
+
+HRESULT File::SeekFromBegin(uint32 n) {
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+
+  if (::SetFilePointer(handle_, n, NULL, FILE_BEGIN) ==
+      INVALID_SET_FILE_POINTER) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[File::SeekFromBegin]")
+                           _T("[SetFilePointer failed][%s][0x%x]"),
+                           file_name_, hr));
+    return hr;
+  }
+  pos_ = n;
+  return S_OK;
+}
+
+// read nLen bytes starting at position n
+// returns number of bytes read
+//
+// async operations:
+//
+// async_id - identifier of a sequence of async operations, 0 for synchronous
+//
+// if the async operation has not been initiated, we initiate it
+// if it is in progress we do nothing
+// if it has been completed we return the data
+// does not delete async data entry
+HRESULT File::ReadAt(const uint32 offset, byte* buf, const uint32 len,
+                     const uint32, uint32* bytes_read) {
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+  ASSERT1(buf);
+  ASSERT1(len);  // reading 0 bytes is not valid (differs from CRT API)
+
+  RET_IF_FAILED(SeekFromBegin(offset));
+
+  DWORD read = 0;
+  if (!::ReadFile(handle_, buf, len, &read, NULL)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[File::ReadAt]")
+                           _T("[ReadFile failed][%s][0x%x]"), file_name_, hr));
+    return hr;
+  }
+
+  if (bytes_read) {
+    *bytes_read = read;
+  }
+
+  return (read == len) ? S_OK : E_FAIL;
+}
+
+
+// reads up to max_len bytes from the start of the file
+// not considered an error if there are less than max_len bytes read
+// returns number of bytes read
+HRESULT File::ReadFromStartOfFile(const uint32 max_len,
+                                  byte* buf,
+                                  uint32* bytes_read) {
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+  ASSERT1(buf);
+  ASSERT1(max_len);
+
+  RET_IF_FAILED(SeekFromBegin(0));
+
+  uint32 file_len = 0;
+  RET_IF_FAILED(GetLength(&file_len));
+
+  if (!file_len) {
+    if (bytes_read) {
+      *bytes_read = 0;
+    }
+    return S_OK;
+  }
+
+  uint32 len = max_len;
+  if (len > file_len) {
+    len = static_cast<uint32>(file_len);
+  }
+
+  return Read(len, buf, bytes_read);
+}
+
+// this function handles lines terminated with LF or CRLF
+// all CR characters are removed from each line, and LF is assumed
+// to be the end of line and is removed
+HRESULT File::ReadLineAnsi(uint32 max_len, char* line, uint32* len) {
+  ASSERT1(line);
+  ASSERT1(max_len);
+
+  char c = 0;
+  uint32 len_read = 0;
+  uint32 total_len = 0;
+
+  while (SUCCEEDED(Read(1, reinterpret_cast<byte *>(&c), &len_read)) &&
+         len_read  &&
+         c != '\n') {
+    if (total_len < max_len - 1 && c != '\r') {
+      line[total_len++] = c;
+    }
+  }
+
+  ASSERT1(total_len < max_len);
+  line[total_len] = '\0';
+
+  if (len) {
+    *len = total_len;
+  }
+
+  return (len_read || total_len) ? S_OK : E_FAIL;
+}
+
+// used by ReadFromStartOfFile and ReadLineAnsi; not reading all requested bytes
+// is not considered fatal
+HRESULT File::Read(const uint32 len, byte* buf, uint32* bytes_read) {
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+  ASSERT1(buf);
+  ASSERT1(len);
+
+  DWORD read = 0;
+  if (!::ReadFile(handle_, buf, len, &read, NULL)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[File::ReadAt]")
+                           _T("[ReadFile failed][%s][0x%x]"),
+                           file_name_, hr));
+    return hr;
+  }
+
+  if (bytes_read) {
+    *bytes_read = read;
+  }
+
+  return S_OK;
+}
+
+
+// returns number of bytes written
+HRESULT File::WriteAt(const uint32 offset,
+                      const byte* buf,
+                      const uint32 len,
+                      uint32,
+                      uint32* bytes_written) {
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+  ASSERT1(!read_only_);
+  ASSERT1(buf);
+  ASSERT1(len);
+
+  RET_IF_FAILED(SeekFromBegin(offset));
+
+  return Write(buf, len, bytes_written);
+}
+
+
+// write buffer n times
+HRESULT File::WriteN(const byte* buf,
+                     const uint32 len,
+                     const uint32 n,
+                     uint32* bytes_written) {
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+  ASSERT1(!read_only_);
+  ASSERT1(buf);
+  ASSERT1(len);
+  ASSERT1(n);
+
+  HRESULT hr = S_OK;
+
+  uint32 total_wrote = 0;
+
+  byte* temp_buf = const_cast<byte*>(buf);
+
+  scoped_array<byte> encrypt_buf;
+
+  uint32 to_go = n;
+  while (to_go) {
+    uint32 wrote = 0;
+    hr = Write(temp_buf, len, &wrote);
+    if (FAILED(hr)) {
+      if (bytes_written) {
+        *bytes_written = total_wrote;
+      }
+      return hr;
+    }
+
+    total_wrote += wrote;
+    to_go--;
+  }
+
+  if (bytes_written) {
+    *bytes_written = total_wrote;
+  }
+  return hr;
+}
+
+// returns number of bytes written
+HRESULT File::Write(const byte* buf, const uint32 len, uint32* bytes_written) {
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+  ASSERT1(!read_only_);
+  ASSERT1(buf);
+  ASSERT1(len);  // writing 0 bytes is not valid (differs from CRT API)
+
+  byte* b = const_cast<byte*>(buf);
+
+  scoped_array<byte> encrypt_buf;
+
+  DWORD wrote = 0;
+  if (!::WriteFile(handle_, b, len, &wrote, NULL)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[File::Write]")
+                           _T("[WriteFile failed][%s][0x%x]"),
+                           file_name_, hr));
+    return hr;
+  }
+
+  if (bytes_written) {
+    *bytes_written = wrote;
+  }
+  pos_ += wrote;
+
+  return (wrote == len) ? S_OK : E_FAIL;
+}
+
+HRESULT File::ClearAt(const uint32 offset,
+                      const uint32 len,
+                      uint32* bytes_written) {
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+  ASSERT1(!read_only_);
+  ASSERT1(len);
+
+  byte zero[kZeroSize] = {0};
+  uint32 to_go = len;
+  uint32 written = 0;
+  uint32 pos = offset;
+
+  while (to_go) {
+    uint32 wrote = 0;
+    uint32 write_len = std::min(to_go, kZeroSize);
+    RET_IF_FAILED(WriteAt(pos, zero, write_len, 0, &wrote));
+
+    if (wrote != write_len) {
+      return E_FAIL;
+    }
+    pos += wrote;
+    written += wrote;
+    to_go -= write_len;
+  }
+
+  if (bytes_written) {
+    *bytes_written = written;
+  }
+  return S_OK;
+}
+
+// returns true on failure
+// zeros new data if zero_data == true
+HRESULT File::SetLength(const uint32 n, bool zero_data) {
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+  ASSERT1(!read_only_);
+  ASSERT1(n <= kMaxFileSize);
+
+  HRESULT hr = S_OK;
+
+  uint32 len = 0;
+  VERIFY1(SUCCEEDED(GetLength(&len)));
+
+  if (len == n) {
+    return S_OK;
+  }
+
+  // according to the documentation, the
+  // new space will not be initialized
+  if (n > len) {
+    if (zero_data) {
+      uint32 bytes_written = 0;
+      RET_IF_FAILED(ClearAt(len, n - len, &bytes_written));
+      if (bytes_written != n - len) {
+        return E_FAIL;
+      }
+    } else {
+      byte zero = 0;
+      uint32 bytes_written = 0;
+      RET_IF_FAILED(WriteAt(n - 1, &zero, 1, 0, &bytes_written));
+      if (bytes_written != 1) {
+        return E_FAIL;
+      }
+    }
+  } else {
+    SeekFromBegin(n);
+    SetEndOfFile(handle_);
+  }
+
+  ASSERT1(SUCCEEDED(GetLength(&len)) && len == n);
+
+  return S_OK;
+}
+
+HRESULT File::ExtendInBlocks(const uint32 block_size, uint32 size_needed,
+                             uint32* new_size, bool clear_new_space) {
+  ASSERT1(new_size);
+
+  *new_size = size_needed;
+
+  if (*new_size % block_size) {
+    *new_size += block_size - (*new_size % block_size);
+  }
+
+  // is zero_data needed? may reduce fragmentation by causing the block to
+  // be written
+  return SetLength(*new_size, clear_new_space);
+}
+
+// returns S_OK on success
+HRESULT File::GetLength(uint32* length) {
+  ASSERT1(length);
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+
+  DWORD len = GetFileSize(handle_, NULL);
+  if (len == INVALID_FILE_SIZE) {
+    ASSERT(false, (_T("cannot get file length")));
+    return E_FAIL;
+  }
+  *length = len;
+  return S_OK;
+}
+
+HRESULT File::Touch() {
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+
+  FILETIME file_time;
+  SetZero(file_time);
+
+  ::GetSystemTimeAsFileTime(&file_time);
+
+  if (!::SetFileTime(handle_, NULL, NULL, &file_time)) {
+    return HRESULTFromLastError();
+  }
+  return S_OK;
+}
+
+HRESULT File::Close() {
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+
+  HRESULT hr = S_OK;
+  if (!::CloseHandle(handle_)) {
+    hr = HRESULTFromLastError();
+  }
+
+  handle_ = INVALID_HANDLE_VALUE;
+
+  return hr;
+}
+
+// this is just for consistency with other classes; does not do anything
+HRESULT File::Reload(uint32* number_errors) {
+  ASSERT1(number_errors);
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+
+  *number_errors = 0;
+  return S_OK;
+}
+
+// this is just for consistency with other classes; does not do anything
+HRESULT File::Verify(uint32* number_errors) {
+  ASSERT1(number_errors);
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+
+  *number_errors = 0;
+  return S_OK;
+}
+
+// this is just for consistency with other classes; does not do anything
+HRESULT File::Dump() {
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+  return S_OK;
+}
+
+// for consistency with other classes
+HRESULT File::GetSizeOnDisk(uint64* size_on_disk) {
+  ASSERT1(size_on_disk);
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+
+  uint32 len = 0;
+  RET_IF_FAILED(GetLength(&len));
+
+  *size_on_disk = len;
+  return S_OK;
+}
+
+// for consistency with other classes
+HRESULT File::GetReloadDiskSpaceNeeded(uint64* bytes_needed) {
+  ASSERT1(bytes_needed);
+  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
+
+  uint32 len = 0;
+  RET_IF_FAILED(GetLength(&len));
+
+  *bytes_needed = len;
+  return S_OK;
+}
+
+// Get the file size
+HRESULT File::GetFileSizeUnopen(const TCHAR* filename, uint32* out_size) {
+  ASSERT1(filename);
+  ASSERT1(out_size);
+
+  WIN32_FILE_ATTRIBUTE_DATA data;
+  SetZero(data);
+
+  if (!::GetFileAttributesEx(filename, ::GetFileExInfoStandard, &data)) {
+    return HRESULTFromLastError();
+  }
+
+  *out_size = data.nFileSizeLow;
+
+  return S_OK;
+}
+
+// Get the last time with a file was written to, and the size
+HRESULT File::GetLastWriteTimeAndSize(const TCHAR* file_path,
+                                      SYSTEMTIME* out_time,
+                                      unsigned int* out_size) {
+  ASSERT1(file_path);
+
+  WIN32_FIND_DATA wfd;
+  SetZero(wfd);
+
+  HANDLE find = ::FindFirstFile(file_path, &wfd);
+  if (find == INVALID_HANDLE_VALUE) {
+    return HRESULTFromLastError();
+  }
+
+  ::FindClose(find);
+
+  if (out_size) {
+    *out_size = wfd.nFileSizeLow;
+  }
+
+  if (out_time) {
+    // If created time is newer than write time, then use that instead
+    // [it tends to be more relevant when copying files around]
+    FILETIME* latest_time = NULL;
+    if (::CompareFileTime(&wfd.ftCreationTime, &wfd.ftLastWriteTime) > 0) {
+      latest_time = &wfd.ftCreationTime;
+    } else {
+      latest_time = &wfd.ftLastWriteTime;
+    }
+
+    if (!::FileTimeToSystemTime(latest_time, out_time)) {
+      return HRESULTFromLastError();
+    }
+  }
+
+  return S_OK;
+}
+
+bool File::AreFilesIdentical(const TCHAR* filename1, const TCHAR* filename2) {
+  UTIL_LOG(L4, (_T("[File::AreFilesIdentical][%s][%s]"), filename1, filename2));
+
+  uint32 file_size1 = 0;
+  HRESULT hr = File::GetFileSizeUnopen(filename1, &file_size1);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[GetFileSizeUnopen failed file_size1][0x%x]"), hr));
+    return false;
+  }
+
+  uint32 file_size2 = 0;
+  hr = File::GetFileSizeUnopen(filename2, &file_size2);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[GetFileSizeUnopen failed file_size2][0x%x]"), hr));
+    return false;
+  }
+
+  if (file_size1 != file_size2) {
+    UTIL_LOG(L3, (_T("[file_size1 != file_size2][%d][%d]"),
+                  file_size1, file_size2));
+    return false;
+  }
+
+  File file1;
+  hr = file1.OpenShareMode(filename1, false, false, FILE_SHARE_READ);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[file1.OpenShareMode failed][0x%x]"), hr));
+    return false;
+  }
+
+  File file2;
+  hr = file2.OpenShareMode(filename2, false, false, FILE_SHARE_READ);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[file2.OpenShareMode failed][0x%x]"), hr));
+    return false;
+  }
+
+  static const uint32 kBufferSize = 0x10000;
+  std::vector<uint8> buffer1(kBufferSize);
+  std::vector<uint8> buffer2(kBufferSize);
+  uint32 bytes_left = file_size1;
+
+  while (bytes_left > 0) {
+    uint32 bytes_to_read = std::min(bytes_left, kBufferSize);
+    uint32 bytes_read1 = 0;
+    uint32 bytes_read2 = 0;
+
+    hr = file1.Read(bytes_to_read, &buffer1.front(), &bytes_read1);
+    if (FAILED(hr)) {
+      UTIL_LOG(LE, (_T("[file1.Read failed][%d][%d][0x%x]"),
+                    bytes_left, bytes_to_read, hr));
+      return false;
+    }
+
+    hr = file2.Read(bytes_to_read, &buffer2.front(), &bytes_read2);
+    if (FAILED(hr)) {
+      UTIL_LOG(LE, (_T("[file2.Read failed][%d][%d][0x%x]"),
+                    bytes_left, bytes_to_read, hr));
+      return false;
+    }
+
+    if (bytes_to_read != bytes_read1 || bytes_to_read != bytes_read2) {
+      UTIL_LOG(LE,
+          (_T("[bytes_to_read != bytes_read1 || bytes_to_read != bytes_read2]")
+          _T("[%d][%d][%d]"), bytes_to_read, bytes_read1, bytes_read2));
+      return false;
+    }
+
+    if (memcmp(&buffer1.front(), &buffer2.front(), bytes_read1) != 0) {
+      UTIL_LOG(L3, (_T("[memcmp failed][%d][%d]"), bytes_left, bytes_read1));
+      return false;
+    }
+
+    if (bytes_left < bytes_to_read) {
+      UTIL_LOG(LE, (_T("[bytes_left < bytes_to_read][%d][%d]"),
+                    bytes_left, bytes_to_read));
+      return false;
+    }
+
+    bytes_left -= bytes_to_read;
+  }
+
+  return true;
+}
+
+FileLock::FileLock() {
+}
+
+FileLock::~FileLock() {
+  Unlock();
+}
+
+HRESULT FileLock::Lock(const TCHAR* file) {
+  std::vector<CString> files;
+  files.push_back(file);
+  return Lock(files);
+}
+
+HRESULT FileLock::Lock(const std::vector<CString>& files) {
+  ASSERT1(!files.empty());
+
+  // Try to lock all files
+  size_t curr_size = handles_.size();
+  for (size_t i = 0; i < files.size(); ++i) {
+    scoped_hfile handle(::CreateFile(files[i],
+                                     GENERIC_READ,
+                                     FILE_SHARE_READ,
+                                     NULL,
+                                     OPEN_EXISTING,
+                                     FILE_ATTRIBUTE_NORMAL,
+                                     NULL));
+    if (!handle) {
+      UTIL_LOG(LEVEL_ERROR,
+               (_T("[FileLock::Lock - failed to lock file][%s][0x%x]"),
+                files[i], HRESULTFromLastError()));
+      break;
+    }
+    handles_.push_back(release(handle));
+  }
+
+  // Cleanup if we fail to lock all the files
+  if (curr_size +  files.size() < handles_.size()) {
+    for (size_t i = handles_.size() - 1; i >= curr_size; --i) {
+      VERIFY(::CloseHandle(handles_[i]), (_T("")));
+      handles_.pop_back();
+    }
+    return E_FAIL;
+  }
+
+  return S_OK;
+}
+
+HRESULT FileLock::Unlock() {
+  for (size_t i = 0; i < handles_.size(); ++i) {
+    VERIFY(::CloseHandle(handles_[i]), (_T("")));
+  }
+  handles_.clear();
+  return S_OK;
+}
+
+
+// path_name: the directory to watch
+// watch_subtree: watch all subdirectory changes  or
+//                only immediate child values
+// notify_filter: See the documentation for FindFirstChangeNotification
+FileWatcher::FileWatcher(const TCHAR* path_name, bool watch_subtree,
+                         DWORD notify_filter)
+    : path_name_(path_name),
+      watch_subtree_(watch_subtree),
+      notify_filter_(notify_filter) {
+  ASSERT1(path_name && *path_name);
+  UTIL_LOG(L3, (_T("[FileWatcher::FileWatcher][%s]"), path_name));
+}
+
+// Get the event that is signaled on store changes.
+HANDLE FileWatcher::change_event() const {
+  ASSERT(valid(change_event_), (_T("call FileWatcher::SetupEvent first")));
+  return get(change_event_);
+}
+
+
+// Called to create/reset the event that gets signaled
+// any time the store changes.  Access the created
+// event using change_event().
+HRESULT FileWatcher::EnsureEventSetup() {
+  UTIL_LOG(L3, (_T("[FileWatcher::EnsureEventSetup]")));
+  if (!valid(change_event_)) {
+    reset(change_event_, ::FindFirstChangeNotification(path_name_,
+                                                       watch_subtree_,
+                                                       notify_filter_));
+    if (!valid(change_event_)) {
+      ASSERT(false, (_T("unable to get file change notification")));
+      return E_FAIL;
+    }
+    // path name was only needed to set-up the event and now that is done....
+    path_name_.Empty();
+    return S_OK;
+  }
+
+  // if the event is set-up and no changes have occurred,
+  // then there is no need to re-setup the event.
+  if (valid(change_event_) && !HasChangeOccurred()) {
+    return NOERROR;
+  }
+
+  return ::FindNextChangeNotification(get(change_event_)) ? S_OK : E_FAIL;
+}
+
+}  // namespace omaha
+
diff --git a/base/file.h b/base/file.h
new file mode 100644
index 0000000..ca266ef
--- /dev/null
+++ b/base/file.h
@@ -0,0 +1,249 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+// File handling routines
+//
+// encryption is not currently active
+//
+#ifndef OMAHA_BASE_FILE_H_
+#define OMAHA_BASE_FILE_H_
+
+#include <windows.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/store_watcher.h"
+
+namespace omaha {
+
+class File {
+ public:
+
+    File();
+    ~File();
+
+    HRESULT Open(const TCHAR* file_name, bool write, bool async);
+    HRESULT OpenShareMode(const TCHAR* file_name,
+                          bool write,
+                          bool async,
+                          DWORD share_mode);
+
+    HRESULT Close();
+
+    static bool Exists(const TCHAR* file_name);
+    static bool IsDirectory(const TCHAR *file_name);
+    static HRESULT GetWildcards(const TCHAR* dir, const TCHAR* wildcard,
+                                std::vector<CString>* matching_paths);
+    // returns S_OK on successful removal or if not existing
+    static HRESULT Remove(const TCHAR* file_name);
+    // CopyWildcards doesn't work recursively
+    static HRESULT CopyWildcards(const TCHAR* from_dir, const TCHAR* to_dir,
+                                 const TCHAR* wildcard,
+                                 bool replace_existing_files);
+    static HRESULT CopyTree(const TCHAR* from_dir, const TCHAR* to_dir,
+                            bool replace_existing_files);
+                                                    // to_dir need not exist
+    static HRESULT Copy(const TCHAR* from, const TCHAR* to,
+                        bool replace_existing_file);
+    static HRESULT Move(const TCHAR* from, const TCHAR* to,
+                        bool replace_existing_file);
+    // DeleteAfterReboot tries to delete the files by either moving them to
+    // the TEMP directory and deleting them on reboot, or if that fails, by
+    // trying to delete them in-place on reboot
+    static HRESULT DeleteAfterReboot(const TCHAR* from);
+    static HRESULT MoveAfterReboot(const TCHAR* from, const TCHAR* to);
+    // Remove any moves pending a reboot from the PendingFileRenameOperations
+    // in the registry.
+    // The prefix_match boolean controls whether we do an exact match on
+    // in_directory, or remove all entries with the in_directory prefix.
+    static HRESULT RemoveFromMovesPendingReboot(const TCHAR* in_directory,
+                                                bool prefix_match);
+    // Did the user try to uninstall a previous install of the same version,
+    // and we couldn't clean up without a reboot?
+    // We check if there are any moves pending a reboot from the
+    // PendingFileRenameOperations in the registry.
+    // The prefix_match boolean controls whether we do an exact match on
+    // in_directory, or check all entries with the in_directory prefix.
+    static bool AreMovesPendingReboot(const TCHAR* in_directory,
+                                      bool prefix_match);
+
+    // The GetFileTime function retrieves the date and time that a file was
+    // created, last accessed, and last modified. The parameters 'created',
+    // 'accessed', 'modified' can be null if the caller does not require that
+    // information. All times are utc
+    // (http://support.microsoft.com/default.aspx?scid=kb;%5BLN%5D;158588)
+    // To compare FILETIME values, use CompareFileTime API.
+    static HRESULT GetFileTime(const TCHAR* file_name, FILETIME* created,
+                               FILETIME* accessed, FILETIME* modified);
+
+    // Sets the file time
+    static HRESULT SetFileTime(const TCHAR* file_name,
+                               const FILETIME* created,
+                               const FILETIME* accessed,
+                               const FILETIME* modified);
+
+    // sync flushes any pending writes to disk
+    HRESULT Sync();
+    // static HRESULT SyncAllFiles();
+
+    HRESULT SeekToBegin();
+    HRESULT SeekFromBegin(uint32 n);
+
+    HRESULT ReadFromStartOfFile(const uint32 max_len, byte *buf,
+                                uint32 *bytes_read);
+    HRESULT ReadLineAnsi(uint32 max_len, char *line, uint32 *len);
+
+    // read len bytes, reading 0 bytes is invalid
+    HRESULT Read(const uint32 len, byte *buf, uint32 *bytes_read);
+    // read len bytes starting at position n, reading 0 bytes is invalid
+    HRESULT ReadAt(const uint32 offset, byte *buf, const uint32 len,
+                    const uint32 async_id, uint32 *bytes_read);
+
+    // write len bytes, writing 0 bytes is invalid
+    HRESULT Write(const byte *buf, const uint32 len, uint32 *bytes_written);
+    // write len bytes, writing 0 bytes is invalid
+    HRESULT WriteAt(const uint32 offset, const byte *buf, const uint32 len,
+                     const uint32 async_id, uint32 *bytes_written);
+
+    // write buffer n times
+    HRESULT WriteN(const byte *buf, const uint32 len, const uint32 n,
+                    uint32 *bytes_written);
+
+    // zeros section of file
+    HRESULT ClearAt(const uint32 offset, const uint32 len,
+                     uint32 *bytes_written);
+
+    // set length of file
+    // if new length is greater than current length, new data is undefined
+    // unless zero_data == true in which case the new data is zeroed.
+    HRESULT SetLength(const uint32 n, bool zero_data);
+    HRESULT ExtendInBlocks(const uint32 block_size, uint32 size_needed,
+                            uint32 *new_size, bool clear_new_space);
+    HRESULT GetLength(uint32 *len);
+
+    // Sets the last write time to the current time
+    HRESULT Touch();
+
+    // all the data storage classes contain these functions
+    // we implemenent them here for consistency
+    // e.g., so we can do object->GetSizeOnDisk independent of the object type
+    HRESULT GetSizeOnDisk(uint64 *size_on_disk);
+    HRESULT GetReloadDiskSpaceNeeded(uint64 *bytes_needed);
+    HRESULT Reload(uint32 *number_errors);
+    HRESULT Verify(uint32 *number_errors);
+    HRESULT Dump();
+
+    // Gets the size of a file, without opening it [the regular GetFileSize
+    // requires a file handle, which conflicts if the file is already opened
+    // and locked]
+    static HRESULT GetFileSizeUnopen(const TCHAR * filename,
+                                     uint32 * out_size);
+
+    // Optimized function that gets the last write time and size
+    static HRESULT GetLastWriteTimeAndSize(const TCHAR* file_path,
+                                           SYSTEMTIME* out_time,
+                                           unsigned int* out_size);
+
+    // Returns true if the two files are binary-identical.
+    static bool AreFilesIdentical(const TCHAR* filename1,
+                                  const TCHAR* filename2);
+
+ private:
+    // See if we have any moves pending a reboot. Return SUCCESS if we do
+    // not encounter errors (not finding a move is not an error). We need to
+    // also check the value of *found_ptr for whether we actually found a move.
+    // On return, *value_multisz_ptr is the value within
+    // "PendingFileRenameOperations", but with any moves for in_directory
+    // removed from it.
+    // The prefix_match boolean controls whether we do an exact match on
+    // in_directory, or remove all entries with the in_directory prefix.
+    // NOTE: If the only values found were our own keys, the whole
+    // PendingFileRenameOperations MULTISZ needs to be deleted. This is
+    // signified by a returned *value_size_chars_ptr of 0.
+    static HRESULT GetPendingRenamesValueMinusDir(const TCHAR* in_directory,
+      bool prefix_match, TCHAR** value_multisz_ptr, DWORD* value_size_chars_ptr,
+      bool* found_ptr);
+
+    HANDLE handle_;
+    CString file_name_;
+    bool read_only_;
+    bool sync_write_done_;
+    uint32 pos_;
+    uint32 encryption_seed_;
+    uint32 sequence_id_;
+    enum EncryptionTypes encryption_;
+
+    static const int kMaxFileSize = kint32max;
+
+    DISALLOW_EVIL_CONSTRUCTORS(File);
+};
+
+// File lock
+class FileLock {
+ public:
+  // Default constructor
+  FileLock();
+
+  // Destructor
+  ~FileLock();
+
+  // Lock a single file
+  HRESULT Lock(const TCHAR* file);
+
+  // Lock multiple files (atomic)
+  HRESULT Lock(const std::vector<CString>& files);
+
+  // Unlock all
+  HRESULT Unlock();
+
+ private:
+  std::vector<HANDLE> handles_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(FileLock);
+};
+
+
+// Does the common things necessary for watching
+// changes in a directory.  If there are file change or other watchers,
+// there could be a common interface for the three methods to decouple
+// the code that is doing the watching from the code that owns the store.
+class FileWatcher : public StoreWatcher {
+ public:
+  // path_name: the directory to watch
+  // watch_subtree: watch all subdirectory changes  or
+  //                only immediate child values
+  // notify_filter: See the documentation for FindFirstChangeNotification
+  FileWatcher(const TCHAR* path_name, bool watch_subtree, DWORD notify_filter);
+
+  // Called to create/reset the event that gets signaled
+  // any time the store changes.  Access the created
+  // event using change_event().
+  virtual HRESULT EnsureEventSetup();
+
+  // Get the event that is signaled on store changes.
+  virtual HANDLE change_event() const;
+
+ private:
+  scoped_hfind_change_notification change_event_;
+  CString path_name_;
+  bool watch_subtree_;
+  DWORD notify_filter_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(FileWatcher);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_FILE_H_
+
diff --git a/base/file_reader.cc b/base/file_reader.cc
new file mode 100644
index 0000000..0008e76
--- /dev/null
+++ b/base/file_reader.cc
@@ -0,0 +1,248 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/file_reader.h"
+#include "omaha/base/debug.h"
+
+namespace omaha {
+
+FileReader::FileReader()
+    : file_is_open_(false),
+      buffered_byte_count_(0),
+      current_position_(0),
+      file_buffer_size_(0),
+      is_unicode_(false) {}
+
+FileReader::~FileReader() {
+  if (file_is_open_) {
+    file_.Close();
+    file_is_open_ = false;
+  }
+}
+
+HRESULT FileReader::Init(const TCHAR* file_name, size_t buffer_size) {
+  ASSERT1(file_name);
+  ASSERT1(buffer_size);
+  file_buffer_size_ = buffer_size;
+  file_buffer_.reset(new byte[file_buffer_size()]);
+  HRESULT hr = file_.OpenShareMode(file_name, false, false, FILE_SHARE_WRITE |
+                                                            FILE_SHARE_READ);
+  file_is_open_ = SUCCEEDED(hr);
+  is_unicode_ = false;
+
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = file_.SeekToBegin();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  const int unicode_header_length = 2;
+
+  char buf[unicode_header_length] = {0};
+  uint32 bytes_read = 0;
+  hr = file_.Read(sizeof(buf), reinterpret_cast<byte*>(buf), &bytes_read);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (bytes_read == sizeof(buf)) {
+    char unicode_buf[unicode_header_length] = {0xff, 0xfe};
+    is_unicode_ = (memcmp(buf, unicode_buf, sizeof(buf)) == 0);
+  }
+
+  if (!is_unicode_) {
+    file_.SeekToBegin();
+  }
+
+  if (is_unicode_ && (buffer_size < sizeof(WCHAR))) {
+    return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+  }
+
+  return S_OK;
+}
+
+HRESULT FileReader::GetNextChar(bool peek, CString* next_char) {
+  ASSERT1(next_char);
+  next_char->Empty();
+
+  // Do we need to read in more of the file?
+  if (current_position_ >= buffered_byte_count_) {
+    current_position_ = 0;
+    if (FAILED(file_.Read(file_buffer_size(),
+                          file_buffer_.get(),
+                          &buffered_byte_count_))) {
+      // There is no more of the file buffered.
+      buffered_byte_count_ = 0;
+    }
+  }
+
+  // Have we gone past the end of the file?
+  if (current_position_ >= buffered_byte_count_) {
+    return E_FAIL;
+  }
+
+  if (is_unicode_) {
+    // Need to make sure there are at least 2 characters still in the buffer.
+    // If we're right at the end of the buffer and there's only one character,
+    // then we will need to read more from the file.
+
+    if (current_position_ + 1 >= buffered_byte_count_) {
+      // We need one more byte to make a WCHAR.
+      // Due to the need to peek, we're going to take that byte and put it at
+      // the beginning of the file_buffer_ and read in as many remaining bytes
+      // as we can from the file.
+
+      // Copy current (and last) byte to the beginning of the buffer.
+      file_buffer_[0] = file_buffer_[current_position_];
+
+      // Reset current_position.
+      current_position_ = 0;
+
+      if (SUCCEEDED(file_.Read(file_buffer_size() - 1,
+                               file_buffer_.get() + 1,
+                               &buffered_byte_count_))) {
+        // Incrememt count to deal with byte we pre-filled at offset 0.
+        buffered_byte_count_++;
+      } else {
+        // We've got a Unicode file with an extra byte.  We're going to drop the
+        // byte and call it end of file.
+        buffered_byte_count_ = 0;
+        return E_FAIL;
+      }
+    }
+
+    // Get the next character.
+    char c1 = file_buffer_[current_position_];
+    ++current_position_;
+    char c2 = file_buffer_[current_position_];
+    ++current_position_;
+
+    if (peek) {
+      // Reset the current position pointer backwards if we're peeking.
+      current_position_ -= 2;
+    }
+
+    WCHAR c = (static_cast<WCHAR>(c2) << 8) | static_cast<WCHAR>(c1);
+
+    *next_char = c;
+  } else {
+    char c = file_buffer_[current_position_];
+    if (!peek) {
+      ++current_position_;
+    }
+    *next_char = c;
+  }
+
+  return S_OK;
+}
+
+HRESULT FileReader::ReadLineString(CString* line) {
+  ASSERT1(line);
+
+  line->Empty();
+
+  while (true) {
+    CString current_char;
+    HRESULT hr = GetNextChar(false, &current_char);
+    // If we failed to get the next char, we're at the end of the file.
+    // If the current line is empty, then fail out signalling we're done.
+    // Otherwise, return the current line and we'll fail out on the next call to
+    // ReadLine().
+    if (FAILED(hr)) {
+      if (line->IsEmpty()) {
+        return hr;
+      } else {
+        return S_OK;
+      }
+    }
+
+    // Have we reached end of line?
+    if (current_char.Compare(_T("\r")) == 0) {
+      // Seek ahead to see if the next char is "\n"
+      CString next_char;
+      GetNextChar(true, &next_char);
+      if (next_char.Compare(_T("\n")) == 0) {
+        // Get in the next char too.
+        GetNextChar(false, &next_char);
+      }
+      break;
+    } else if (current_char.Compare(_T("\n")) == 0) {
+      break;
+    }
+
+    line->Append(current_char);
+  }
+
+  return S_OK;
+}
+
+HRESULT FileReader::ReadLineAnsi(size_t max_len, char* line) {
+  ASSERT1(line);
+  ASSERT1(max_len);
+
+  size_t total_len = 0;
+
+  while (true) {
+    // Do we need to read in more of the file?
+    if (current_position_ >= buffered_byte_count_) {
+      current_position_ = 0;
+      if (FAILED(file_.Read(file_buffer_size(),
+                            file_buffer_.get(),
+                            &buffered_byte_count_))) {
+        // There is no more of the file buffered.
+        buffered_byte_count_ = 0;
+      }
+    }
+
+    // Have we gone past the end of the file?
+    if (current_position_ >= buffered_byte_count_) {
+      break;
+    }
+
+    // Get the next character.
+    char c = file_buffer_[current_position_];
+    ++current_position_;
+
+    // Have we reached end of line?
+    // TODO(omaha): if the line is terminated with a \r\n pair then perhaps
+    // the code should skip the whole pair not only half of it.
+    if (c == '\n' || c == '\r') {
+      break;
+    }
+
+    // Fill up the passed in buffer for the line.
+    if (total_len < max_len - 1) {
+      line[total_len] = c;
+      ++total_len;
+    }
+  }
+  // Terminate the passed in buffer.
+  ASSERT1(total_len < max_len);
+  line[total_len] = '\0';
+
+  // If we are out of bytes and we didn't read in any bytes.
+  // then fail signaling end of file.
+  if (!buffered_byte_count_ && !total_len) {
+    return E_FAIL;
+  }
+
+  return S_OK;
+}
+
+}  // namespace omaha
+
diff --git a/base/file_reader.h b/base/file_reader.h
new file mode 100644
index 0000000..3412b79
--- /dev/null
+++ b/base/file_reader.h
@@ -0,0 +1,60 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_FILE_READER_H_
+#define OMAHA_COMMON_FILE_READER_H_
+
+#include <windows.h>
+#include <tchar.h>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/file.h"
+
+namespace omaha {
+
+// Allows one to read files quickly and easily line by line.
+class FileReader {
+ public:
+  FileReader();
+  ~FileReader();
+
+  // Specifies the underlying file name and the size of internal read buffer.
+  HRESULT Init(const TCHAR* file_name, size_t buffer_size);
+
+  // Reads one line of text from the file, taking advantage of the internal
+  // file buffer to optimize I/O reads. It reads at most max_len - 1 characters
+  // and it always terminates the line.
+  HRESULT ReadLineAnsi(size_t max_len, char* line);
+  HRESULT ReadLineString(CString* line);
+
+ private:
+  HRESULT GetNextChar(bool peek, CString* next_char);
+  size_t file_buffer_size() const { return file_buffer_size_; }
+
+  File file_;
+  bool file_is_open_;
+  size_t buffered_byte_count_;          // How many bytes are in the buffer.
+  size_t current_position_;             // An index into the buffer.
+  scoped_array<byte> file_buffer_;      // A buffer (cache) of the file.
+  size_t file_buffer_size_;             // How much of the file to slurp
+                                        // in on each read.
+  bool is_unicode_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(FileReader);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_FILE_READER_H_
diff --git a/base/file_reader_unittest.cc b/base/file_reader_unittest.cc
new file mode 100644
index 0000000..1897033
--- /dev/null
+++ b/base/file_reader_unittest.cc
@@ -0,0 +1,304 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// TODO(omaha): improve unit test. For example, test we are handling correctly
+// different types of line termination.
+
+#include <vector>
+#include "base/scoped_ptr.h"
+#include "omaha/base/file_reader.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+class ReadingFilesTest : public testing::Test {
+ protected:
+  ReadingFilesTest() {
+    temp_file_[0] = '\0';
+  }
+
+  virtual void SetUp() {
+    // create a temporary file
+    TCHAR temp_path[MAX_PATH] = {0};
+    EXPECT_LT(::GetTempPath(arraysize(temp_path), temp_path),
+              arraysize(temp_path));
+    EXPECT_NE(::GetTempFileName(temp_path, _T("ut_"), 0, temp_file_), 0);
+  }
+
+  virtual void TearDown() {
+    // remove the temporary file
+    if (::lstrlen(temp_file_) > 0) {
+      ASSERT_SUCCEEDED(File::Remove(temp_file_));
+    }
+  }
+
+  void Compare(const std::vector<char*>& lines1,
+               const std::vector<char*>& lines2,
+               byte** orig_lines, int* lengths, int* copies) {
+    // verify that all three things match
+    //  * The lines read using the File class,
+    //  * The lines read using the FileReader class,
+    //  * The original data used to write the files
+    EXPECT_EQ(lines1.size(), lines2.size());
+
+    for (uint32 line_number = 0; line_number < lines1.size(); line_number++) {
+      int index = 0;
+      int data_length = lengths[line_number];
+      int data_copies = copies[line_number];
+      for (int copy = 0; copy < data_copies; copy++) {
+        for (int index_data = 0; index_data < data_length; index_data++) {
+          EXPECT_EQ(lines1[line_number][index], lines2[line_number][index]);
+          EXPECT_EQ(lines1[line_number][index],
+                    orig_lines[line_number][index_data]);
+          index++;
+        }
+      }
+      EXPECT_EQ(lines1[line_number][index], lines2[line_number][index]);
+      EXPECT_EQ(lines1[line_number][index], '\0');
+    }
+  }
+
+  void DeleteContainedArrays(std::vector<char*>* lines) {
+    for (uint32 line_number = 0; line_number < lines->size(); line_number++) {
+      delete[] (*lines)[line_number];
+      (*lines)[line_number] = NULL;
+    }
+  }
+
+  HRESULT WriteCStringW(File* file, CStringW* str) {
+    return file->Write(reinterpret_cast<const byte*>(str->GetBuffer()),
+                       str->GetLength() * sizeof(WCHAR),
+                       NULL);
+  }
+
+  void TestReadFileStringUnicode(int buffer_size) {
+    CStringW line1 = L"hello there, here's some data";
+    CStringW line2 = L"i've got more\tdata over here.";
+    CStringW eol1 = L"\r";
+    CStringW eol2 = L"\r\n";
+    CStringW eol3 = L"\n";
+
+    std::vector<CString> expected_lines;
+    expected_lines.push_back(CString(line1));
+    expected_lines.push_back(CString(line2));
+    expected_lines.push_back(CString(line1));
+    expected_lines.push_back(CString(line1));
+    expected_lines.push_back(CString(line2));
+
+    File file_write;
+    EXPECT_SUCCEEDED(file_write.Open(temp_file_, true, false));
+
+    // Write the unicode marker to the beginning of the file.
+    byte buf[2] = {0xff, 0xfe};
+    EXPECT_SUCCEEDED(file_write.Write(buf, sizeof(buf), NULL));
+
+    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &line1));
+    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &eol1));
+    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &line2));
+    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &eol1));
+    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &line1));
+    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &eol3));
+    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &line1));
+    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &eol2));
+    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &line2));
+    file_write.Close();
+
+    FileReader reader;
+    EXPECT_SUCCEEDED(reader.Init(temp_file_, buffer_size));
+
+    std::vector<CString> read_lines;
+    CString current_string;
+    while (SUCCEEDED(reader.ReadLineString(&current_string))) {
+      read_lines.push_back(current_string);
+    }
+
+    ASSERT_EQ(expected_lines.size(), read_lines.size());
+
+    if (expected_lines.size() == read_lines.size()) {
+      for (size_t i = 0; i < expected_lines.size(); ++i) {
+        CString expected_str = expected_lines[i];
+        CString read_str = read_lines[i];
+        ASSERT_STREQ(expected_str, read_str);
+      }
+    }
+  }
+
+  TCHAR temp_file_[MAX_PATH];
+
+  DISALLOW_EVIL_CONSTRUCTORS(ReadingFilesTest);
+};
+
+
+TEST_F(ReadingFilesTest, ReadFile1) {
+  // write the data to the file
+  File file_write;
+  EXPECT_SUCCEEDED(file_write.Open(temp_file_, true, false));
+  byte data1[] = {'a', 'b', 'c', 'z', '!', '&', '\t'};
+  int data1_copies = 1;
+  EXPECT_SUCCEEDED(file_write.WriteN(data1, arraysize(data1), data1_copies,
+                                     NULL));
+  byte return_data = '\n';
+  EXPECT_SUCCEEDED(file_write.WriteN(&return_data, 1, 1, NULL));
+
+  byte data2[] = {'d', 'a', 'v', 'e', ' ', ' ', '\t', '\\'};
+  int data2_copies = 2;
+  EXPECT_SUCCEEDED(file_write.WriteN(data2, arraysize(data2), data2_copies,
+                                     NULL));
+  file_write.Close();
+
+  // read in the file line by line using the File class
+  std::vector<char*> lines1;
+  File file_read1;
+  ASSERT_SUCCEEDED(file_read1.Open(temp_file_, false, false));
+  while (true) {
+    uint32 bytes_read;
+    scoped_array<char> line(new char[256]);
+    if (FAILED(file_read1.ReadLineAnsi(256, line.get(), &bytes_read))) {
+      break;
+    }
+    lines1.push_back(line.release());
+  }
+
+  file_read1.Close();
+
+  // read in the file line by line using the FileReader class
+  std::vector<char*> lines2;
+  FileReader file_read2;
+  size_t buffer_size = 512;
+  ASSERT_SUCCEEDED(file_read2.Init(temp_file_, buffer_size));
+  while (true) {
+    scoped_array<char> line(new char[256]);
+    if (FAILED(file_read2.ReadLineAnsi(256, line.get()))) {
+      break;
+    }
+    lines2.push_back(line.release());
+  }
+
+  // Verify that everything matches
+  byte* (orig_lines[]) = {data1, data2};
+  int copies[] = {data1_copies, data2_copies};
+  int lengths[] = {arraysize(data1), arraysize(data2)};
+  Compare(lines1, lines2, orig_lines, lengths, copies);
+
+  // Free the allocated memory
+  DeleteContainedArrays(&lines1);
+  DeleteContainedArrays(&lines1);
+}
+
+// Two readers should be able to read from the same file.
+TEST_F(ReadingFilesTest, ReadFileShare) {
+  File file;
+  EXPECT_SUCCEEDED(file.Open(temp_file_, true, false));
+  file.Close();
+
+  const size_t kBufferSize = 0x100;
+
+  FileReader reader1;
+  EXPECT_SUCCEEDED(reader1.Init(temp_file_, kBufferSize));
+
+  FileReader reader2;
+  EXPECT_SUCCEEDED(reader2.Init(temp_file_, kBufferSize));
+}
+
+HRESULT WriteCStringA(File* file, CStringA* str) {
+  return file->Write(reinterpret_cast<const byte*>(str->GetBuffer()),
+                     str->GetLength(),
+                     NULL);
+}
+
+TEST_F(ReadingFilesTest, ReadFileStringAnsi) {
+  CStringA line1 = "hello there, here's some data";
+  CStringA line2 = "i've got more\tdata over here.";
+  CStringA eol1 = "\r";
+  CStringA eol2 = "\r\n";
+  CStringA eol3 = "\n";
+
+  std::vector<CString> expected_lines;
+  expected_lines.push_back(CString(line1));
+  expected_lines.push_back(CString(line2));
+  expected_lines.push_back(CString(line1));
+  expected_lines.push_back(CString(line1));
+  expected_lines.push_back(CString(line2));
+
+  File file_write;
+  EXPECT_SUCCEEDED(file_write.Open(temp_file_, true, false));
+  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &line1));
+  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &eol1));
+  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &line2));
+  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &eol1));
+  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &line1));
+  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &eol3));
+  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &line1));
+  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &eol2));
+  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &line2));
+  file_write.Close();
+
+  FileReader reader;
+  EXPECT_SUCCEEDED(reader.Init(temp_file_, 20));
+
+  std::vector<CString> read_lines;
+  CString current_string;
+  while (SUCCEEDED(reader.ReadLineString(&current_string))) {
+    read_lines.push_back(current_string);
+  }
+
+  ASSERT_EQ(expected_lines.size(), read_lines.size());
+
+  if (expected_lines.size() == read_lines.size()) {
+    for (size_t i = 0; i < expected_lines.size(); ++i) {
+      CString expected_str = expected_lines[i];
+      CString read_str = read_lines[i];
+      ASSERT_STREQ(expected_str, read_str);
+    }
+  }
+}
+
+TEST_F(ReadingFilesTest, ReadFileStringUnicodeSmallBuffer) {
+  TestReadFileStringUnicode(20);
+}
+
+TEST_F(ReadingFilesTest, ReadFileStringUnicodeHugeBuffer) {
+  TestReadFileStringUnicode(4096);
+}
+
+TEST_F(ReadingFilesTest, ReadFileStringUnicodeSmallOddBufferBuffer) {
+  TestReadFileStringUnicode(19);
+}
+
+TEST_F(ReadingFilesTest, ReadFileStringUnicodeTinyOddBuffer) {
+  TestReadFileStringUnicode(3);
+}
+
+TEST_F(ReadingFilesTest, ReadFileStringUnicodeTinyEvenBuffer) {
+  TestReadFileStringUnicode(2);
+}
+
+TEST_F(ReadingFilesTest, ReadFileStringUnicodeOneByteBuffer) {
+  File file_write;
+  EXPECT_SUCCEEDED(file_write.Open(temp_file_, true, false));
+
+  // Write the unicode marker to the beginning of the file.
+  byte buf[2] = {0xff, 0xfe};
+  EXPECT_SUCCEEDED(file_write.Write(buf, sizeof(buf), NULL));
+
+  file_write.Close();
+
+  FileReader reader;
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER),
+            reader.Init(temp_file_, 1));
+}
+
+}  // namespace omaha
+
diff --git a/base/file_store.cc b/base/file_store.cc
new file mode 100644
index 0000000..372c1b8
--- /dev/null
+++ b/base/file_store.cc
@@ -0,0 +1,116 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Defines class FileStore
+
+#include <vector>
+#include "omaha/base/debug.h"
+#include "omaha/base/file.h"
+#include "omaha/base/file_store.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+// Open the store
+bool FileStore::Open(const TCHAR* file_path) {
+  file_path_ = String_MakeEndWith(file_path, _T("\\"), false);
+  return true;
+}
+
+// Close the store
+bool FileStore::Close() {
+  file_path_.Empty();
+  return true;
+}
+
+// Clear the store
+bool FileStore::Clear() {
+  if (File::Exists(file_path_) && File::IsDirectory(file_path_)) {
+    return SUCCEEDED(DeleteDirectoryFiles(file_path_));
+  } else {
+    return true;
+  }
+}
+
+// Read a value from the store
+bool FileStore::Read(const TCHAR* name, std::vector<byte>* data) const {
+  ASSERT1(name);
+  ASSERT1(data);
+
+  return Exists(name) && SUCCEEDED(ReadEntireFile(file_path_ + name, 0, data));
+}
+
+// Write a value to the store
+bool FileStore::Write(const TCHAR* name, byte* data, int data_size) {
+  ASSERT1(name);
+  ASSERT1(data);
+  ASSERT1(data_size);
+
+  std::vector<byte> buffer(data_size);
+  memcpy(&buffer.front(), data, data_size);
+
+  return SUCCEEDED(WriteEntireFile(file_path_ + name, buffer));
+}
+
+// Check to see a named value exists in the store
+bool FileStore::Exists(const TCHAR* name) const {
+  ASSERT1(name);
+
+  return File::Exists(file_path_ + name);
+}
+
+// Remove a value from the store
+bool FileStore::Remove(const TCHAR* name) {
+  ASSERT1(name);
+
+  return SUCCEEDED(File::Remove(file_path_ + name));
+}
+
+// Get the number of values for this store
+bool FileStore::GetValueCount(uint32* value_count) {
+  ASSERT1(value_count);
+
+  std::vector<CString> matching_paths;
+
+  if (FAILED(File::GetWildcards(file_path_, _T("*"), &matching_paths))) {
+    return false;
+  }
+
+  *value_count = matching_paths.size();
+
+  return true;
+}
+
+// Get the value name for the given value name index
+bool FileStore::GetValueNameAt(uint32 index, CString* value_name) {
+  ASSERT1(value_name);
+
+  std::vector<CString> matching_paths;
+
+  if (FAILED(File::GetWildcards(file_path_, _T("*"), &matching_paths))) {
+    return false;
+  }
+  if (index >= matching_paths.size()) {
+    return false;
+  }
+
+  *value_name = matching_paths[index].Mid(file_path_.GetLength());
+
+  return true;
+}
+
+}  // namespace omaha
+
diff --git a/common/file_store.h b/base/file_store.h
similarity index 100%
rename from common/file_store.h
rename to base/file_store.h
diff --git a/base/file_store_unittest.cc b/base/file_store_unittest.cc
new file mode 100644
index 0000000..d49d7e2
--- /dev/null
+++ b/base/file_store_unittest.cc
@@ -0,0 +1,98 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Unit test for the file_store.
+
+
+
+#include <shlobj.h>
+#include "base/basictypes.h"
+#include "omaha/base/file_store.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+const TCHAR kFilePathPrefix[] = _T("unittest");
+const TCHAR kFileName1[] = _T("fname1");
+const TCHAR kFileName2[] = _T("fname2");
+char kFileContent[] = "1234567890abcdefg";
+
+TEST(FileStoreTest, FileStore) {
+  // Create a temp dir
+  TCHAR temp_path[MAX_PATH];
+  *temp_path = 0;
+  ASSERT_LT(0u, ::GetTempPath(MAX_PATH, temp_path));
+
+  CString temp_dir;
+  temp_dir.Format(_T("%s%s%x"), temp_path, kFilePathPrefix, ::GetTickCount());
+  ASSERT_EQ(::SHCreateDirectoryEx(0, temp_dir, 0), ERROR_SUCCESS);
+  ON_SCOPE_EXIT(DeleteDirectory, temp_dir);
+
+  // Test the file store
+  FileStore file_store;
+  ASSERT_TRUE(file_store.Open(temp_dir));
+
+  // Make sure the folder is empty
+  uint32 value_count;
+  ASSERT_TRUE(file_store.GetValueCount(&value_count));
+  ASSERT_EQ(value_count, 0);
+  CString value_name;
+  ASSERT_FALSE(file_store.GetValueNameAt(0, &value_name));
+
+  // Write 2 files
+  std::vector<byte> buffer;
+  ASSERT_TRUE(file_store.Write(kFileName1,
+                               reinterpret_cast<byte*>(kFileContent),
+                               arraysize(kFileContent)));
+  ASSERT_TRUE(file_store.Exists(kFileName1));
+  ASSERT_TRUE(file_store.GetValueCount(&value_count));
+  ASSERT_EQ(value_count, 1);
+  ASSERT_TRUE(file_store.GetValueNameAt(0, &value_name));
+  ASSERT_TRUE(value_name == kFileName1);
+  ASSERT_TRUE(file_store.Read(kFileName1, &buffer));
+  ASSERT_TRUE(memcmp(kFileContent,
+                     &buffer.front(),
+                     arraysize(kFileContent)) == 0);
+
+  ASSERT_TRUE(file_store.Write(kFileName2,
+                               reinterpret_cast<byte*>(kFileContent),
+                               arraysize(kFileContent)));
+  ASSERT_TRUE(file_store.Exists(kFileName2));
+  ASSERT_TRUE(file_store.GetValueCount(&value_count));
+  ASSERT_EQ(value_count, 2);
+  ASSERT_TRUE(file_store.GetValueNameAt(1, &value_name));
+  ASSERT_TRUE(value_name == kFileName2);
+  ASSERT_TRUE(file_store.Read(kFileName2, &buffer));
+  ASSERT_TRUE(memcmp(kFileContent,
+                     &buffer.front(),
+                     arraysize(kFileContent)) == 0);
+
+  // Remove files
+  ASSERT_TRUE(file_store.Remove(kFileName1));
+  ASSERT_FALSE(file_store.Exists(kFileName1));
+  ASSERT_TRUE(file_store.GetValueCount(&value_count));
+  ASSERT_EQ(value_count, 1);
+  ASSERT_TRUE(file_store.Remove(kFileName2));
+  ASSERT_FALSE(file_store.Exists(kFileName2));
+  ASSERT_TRUE(file_store.GetValueCount(&value_count));
+  ASSERT_EQ(value_count, 0);
+
+  ASSERT_TRUE(file_store.Close());
+}
+
+}  // namespace omaha
+
diff --git a/base/file_unittest.cc b/base/file_unittest.cc
new file mode 100644
index 0000000..c061491
--- /dev/null
+++ b/base/file_unittest.cc
@@ -0,0 +1,439 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// File unittest
+
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/string.h"
+#include "omaha/base/timer.h"
+#include "omaha/base/tr_rand.h"
+#include "omaha/base/utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+// TODO(omaha): test error-prone functions such as ReadLineAnsi
+
+namespace {
+
+#define kIEBrowserExe \
+  _T("C:\\PROGRAM FILES\\Internet Explorer\\iexplore.exe")
+
+#define kIEBrowserQuotedExe \
+  _T("\"") kIEBrowserExe _T("\"")
+
+}  // namespace
+
+void SimpleTest(bool async) {
+  File f;
+
+  char buf[1000] = "test";
+  uint32 len1 = 4;
+
+  char buf2[1000] = "aaaa";
+  uint32 len2 = 4;
+
+  char buf3[1000] = "bbbb";
+  uint32 len3 = 4;
+
+  char buf4[1000] = "    ";
+  uint32 len4 = 4;
+
+  CString s(_T("test"));
+  CString testfile(_T("testfile.1"));
+
+  ASSERT_SUCCEEDED(f.Open(testfile, true, async));
+  uint32 pos = 0;
+  ASSERT_SUCCEEDED(f.WriteAt(pos,
+                             reinterpret_cast<byte*>(buf),
+                             len1,
+                             0,
+                             NULL));
+  pos += len1;
+
+  ASSERT_SUCCEEDED(f.WriteAt(pos,
+                             reinterpret_cast<byte*>(buf3),
+                             len3,
+                             0,
+                             NULL));
+  pos += len3;
+
+  ASSERT_SUCCEEDED(f.WriteAt(pos,
+                             reinterpret_cast<byte*>(buf3),
+                             len3,
+                             0,
+                             NULL));
+  pos += len3;
+
+  ASSERT_SUCCEEDED(f.WriteAt(pos,
+                             reinterpret_cast<byte*>(buf3),
+                             len3,
+                             0,
+                             NULL));
+  pos += len3;
+
+  ASSERT_SUCCEEDED(f.WriteAt(pos,
+                             reinterpret_cast<byte*>(buf3),
+                             len3,
+                             0,
+                             NULL));
+  pos += len3;
+
+  ASSERT_SUCCEEDED(f.WriteAt(pos,
+                             reinterpret_cast<byte*>(buf3),
+                             len3,
+                             0,
+                             NULL));
+  pos += len3;
+
+  ASSERT_SUCCEEDED(f.WriteAt(pos,
+                             reinterpret_cast<byte*>(buf3),
+                             len3,
+                             0,
+                             NULL));
+  pos += len3;
+
+  ASSERT_SUCCEEDED(f.ReadAt(0, reinterpret_cast<byte*>(buf4), len1, 0, NULL));
+  ASSERT_STREQ(buf, buf4);
+
+  ASSERT_SUCCEEDED(f.ReadAt(4, reinterpret_cast<byte*>(buf4), len3, 0, NULL));
+  ASSERT_STREQ(buf3, buf4);
+
+  ASSERT_SUCCEEDED(f.WriteAt(1, reinterpret_cast<byte*>(buf3), 2, 0, NULL));
+
+  ASSERT_SUCCEEDED(f.WriteAt(20, reinterpret_cast<byte*>(buf), 2, 0, NULL));
+  ASSERT_SUCCEEDED(f.ReadAt(20, reinterpret_cast<byte*>(buf), 2, 0, NULL));
+
+  ASSERT_SUCCEEDED(f.WriteAt(30, reinterpret_cast<byte*>(buf), 2, 0, NULL));
+
+  ASSERT_SUCCEEDED(f.SeekFromBegin(0));
+
+  ASSERT_SUCCEEDED(f.ReadAt(30, reinterpret_cast<byte*>(buf), 2, 0, NULL));
+
+  ASSERT_SUCCEEDED(f.Close());
+
+  ASSERT_SUCCEEDED(f.Open(L"testfile.1", false, false));
+  ASSERT_SUCCEEDED(f.ReadAt(0, reinterpret_cast<byte*>(buf), 16, 0, NULL));
+  buf[17] = '\0';
+
+  uint64 size_on_disk = 0;
+  ASSERT_SUCCEEDED(f.GetSizeOnDisk(&size_on_disk));
+  ASSERT_EQ(size_on_disk, 32);
+
+  ASSERT_SUCCEEDED(f.Close());
+
+  ASSERT_TRUE(File::Exists(testfile));
+  ASSERT_SUCCEEDED(File::Remove(testfile));
+  ASSERT_FALSE(File::Exists(testfile));
+}
+
+void FileWriteCreate(uint32 file_size) {
+  Timer time(true);
+  CString testfile;
+  testfile.Format(L"testfile%u", file_size);
+
+  File f;
+  ASSERT_SUCCEEDED(f.Open(testfile, true, false));
+  ASSERT_SUCCEEDED(f.SetLength(file_size, false));
+
+  uint32 write_size = 512;
+
+  byte *buf2 = new byte[write_size];
+
+  for (uint32 j = 0; j < file_size - write_size; j += write_size) {
+      for (uint32 i = 0; i < write_size; i++) {
+        buf2[i] = static_cast<byte>(tr_rand() % 255);
+      }
+      ASSERT_SUCCEEDED(f.WriteAt(j, buf2, write_size, 0, NULL));
+  }
+
+  f.Sync();
+  f.Close();
+
+  EXPECT_SUCCEEDED(File::Remove(testfile));
+}
+
+void FileWriteTimeTest(uint32 file_size,
+                       uint32 number_writes,
+                       uint32 write_size) {
+  Timer time(true);
+  CString testfile;
+  testfile.Format(L"testfile%u", file_size);
+
+  File f;
+  ASSERT_SUCCEEDED(f.Open(testfile, true, false));
+
+  byte *buf = new byte[write_size];
+
+  for (uint32 i = 0; i < number_writes; i++) {
+    for (uint32 j = 0; j < write_size; j++) {
+      buf[j] = static_cast<byte>(tr_rand() % 255);
+    }
+    uint32 pos = (tr_rand() * 65536 + tr_rand()) % (file_size - write_size);
+    ASSERT_SUCCEEDED(f.WriteAt(pos, buf, write_size, 0, NULL));
+  }
+
+  delete[] buf;
+
+  ASSERT_SUCCEEDED(f.Sync());
+  ASSERT_SUCCEEDED(f.Close());
+
+  EXPECT_SUCCEEDED(File::Remove(testfile));
+}
+
+TEST(FileTest, File) {
+  const TCHAR* const kTestFileName = L"testfile.3";
+
+  SimpleTest(false);
+
+  int header = 123;
+  int header2 = 0;
+
+  File f2;
+  ASSERT_SUCCEEDED(f2.Open(kTestFileName, true, false));
+  ASSERT_SUCCEEDED(f2.WriteAt(0,
+                              reinterpret_cast<byte*>(&header),
+                              sizeof(header),
+                              0,
+                              NULL));
+  ASSERT_SUCCEEDED(f2.ReadAt(0,
+                             reinterpret_cast<byte*>(&header2),
+                             sizeof(header),
+                             0,
+                             NULL));
+  ASSERT_EQ(header, header2);
+
+  uint64 size_on_disk = 0;
+
+  ASSERT_SUCCEEDED(f2.GetSizeOnDisk(&size_on_disk));
+  ASSERT_EQ(size_on_disk, sizeof(header));
+  ASSERT_SUCCEEDED(f2.Close());
+
+  const int kLevel = 1;
+  for (uint32 file_size = 2 * 1024 * 1024;
+       file_size <= 2 * 1024 * 1024;
+       file_size *= 2) {
+    for (uint32 number_writes = 100;
+         number_writes <= (300 * static_cast<uint32>(kLevel));
+         number_writes *= 2) {
+      uint32 write_size = 128;
+      FileWriteTimeTest(file_size, number_writes, write_size);
+    }
+  }
+
+  // Test File::Copy, File::Move, File::CopyWildcards
+  {
+    CString windows_dir;
+    CString temp_dir;
+    DWORD dw = ::GetEnvironmentVariable(
+        L"SystemRoot",
+        windows_dir.GetBufferSetLength(MAX_PATH),
+        MAX_PATH);
+    windows_dir.ReleaseBuffer();
+    ASSERT_TRUE(dw);
+    dw = ::GetEnvironmentVariable(L"TEMP",
+                                  temp_dir.GetBufferSetLength(MAX_PATH),
+                                  MAX_PATH);
+    temp_dir.ReleaseBuffer();
+    ASSERT_TRUE(dw);
+    CString known_file1(windows_dir + L"\\NOTEPAD.EXE");
+    CString known_file2(windows_dir + L"\\REGEDIT.EXE");
+    CString temp_file1(temp_dir + L"\\FOO.TMP");
+    CString temp_file2(temp_dir + L"\\BAR.TMP");
+    uint32 known_size1 = 0;
+    uint32 known_size2 = 0;
+    uint32 temp_size1 = 0;
+
+    // Start with neither file existing
+    if (File::Exists(temp_file1))
+      File::Remove(temp_file1);
+    if (File::Exists(temp_file2))
+      File::Remove(temp_file2);
+    ASSERT_FALSE(File::Exists(temp_file1));
+    ASSERT_FALSE(File::Exists(temp_file2));
+    ASSERT_SUCCEEDED(File::GetFileSizeUnopen(known_file1, &known_size1));
+    ASSERT_SUCCEEDED(File::GetFileSizeUnopen(known_file2, &known_size2));
+    ASSERT_NE(known_size1, known_size2);
+
+    // Copy to create a file, move it to 2nd file, then remove 2nd file
+    ASSERT_SUCCEEDED(File::Copy(known_file1, temp_file1, false));
+    ASSERT_TRUE(File::Exists(temp_file1));
+    ASSERT_SUCCEEDED(File::Move(temp_file1, temp_file2, false));
+    ASSERT_FALSE(File::Exists(temp_file1));
+    ASSERT_TRUE(File::Exists(temp_file2));
+    ASSERT_SUCCEEDED(File::Remove(temp_file2));
+    ASSERT_FALSE(File::Exists(temp_file2));
+
+    // Try copying a file on top of a file - with and without
+    // replace_existing_file=true
+    ASSERT_SUCCEEDED(File::Copy(known_file1, temp_file1, false));
+    ASSERT_SUCCEEDED(File::GetFileSizeUnopen(temp_file1, &temp_size1));
+    ASSERT_EQ(temp_size1, known_size1);
+    ASSERT_SUCCEEDED(File::Copy(known_file2, temp_file1, false));
+    ASSERT_TRUE(File::Exists(temp_file1));
+    ASSERT_SUCCEEDED(File::GetFileSizeUnopen(temp_file1, &temp_size1));
+    ASSERT_EQ(temp_size1, known_size1);
+    ASSERT_SUCCEEDED(File::Copy(known_file2, temp_file1, true));
+    ASSERT_TRUE(File::Exists(temp_file1));
+    ASSERT_SUCCEEDED(File::GetFileSizeUnopen(temp_file1, &temp_size1));
+    ASSERT_EQ(temp_size1, known_size2);
+    ASSERT_SUCCEEDED(File::Remove(temp_file1));
+
+    // Try copying a bunch of files
+    CString known_file3(windows_dir + L"\\twunk_32.exe");
+    CString known_file4(windows_dir + L"\\twunk_16.exe");
+    CString temp_file3(temp_dir + L"\\twunk_32.exe");
+    CString temp_file4(temp_dir + L"\\twunk_16.exe");
+    if (File::Exists(temp_file3))
+      File::Remove(temp_file3);
+    if (File::Exists(temp_file4))
+      File::Remove(temp_file4);
+    ASSERT_TRUE(File::Exists(known_file3));
+    ASSERT_TRUE(File::Exists(known_file4));
+    ASSERT_FALSE(File::Exists(temp_file3));
+    ASSERT_FALSE(File::Exists(temp_file4));
+    ASSERT_SUCCEEDED(File::CopyWildcards(windows_dir,
+                                         temp_dir,
+                                         L"twunk*.exe",
+                                         true));
+    ASSERT_TRUE(File::Exists(temp_file3));
+    ASSERT_TRUE(File::Exists(temp_file4));
+    ASSERT_SUCCEEDED(File::Remove(temp_file3));
+    ASSERT_SUCCEEDED(File::Remove(temp_file4));
+
+    std::vector<CString> matching_files;
+    ASSERT_SUCCEEDED(File::GetWildcards(windows_dir,
+                                        L"twunk*.exe",
+                                        &matching_files));
+    ASSERT_EQ(matching_files.size(), 2);
+    ASSERT_TRUE(matching_files[0] == known_file3 ||
+                matching_files[0] == known_file4);
+    ASSERT_TRUE(matching_files[1] == known_file3 ||
+                matching_files[1] == known_file4);
+    ASSERT_TRUE(matching_files[0] != matching_files[1]);
+  }
+
+  EXPECT_SUCCEEDED(File::Remove(kTestFileName));
+}
+
+
+TEST(FileTest, FileChangeWatcher) {
+  CString temp_dir;
+  ASSERT_TRUE(::GetEnvironmentVariable(L"TEMP",
+                                       temp_dir.GetBufferSetLength(MAX_PATH),
+                                       MAX_PATH) != 0);
+  temp_dir.ReleaseBuffer();
+  temp_dir = String_MakeEndWith(temp_dir, _T("\\"), false /* ignore_case */);
+  temp_dir = temp_dir + _T("omaha_unittest") + itostr(tr_rand() % 255);
+  EXPECT_SUCCEEDED(CreateDir(temp_dir, 0));
+
+  // watch the directory for changes
+  FileWatcher watcher(temp_dir, false, FILE_NOTIFY_CHANGE_LAST_WRITE);
+  EXPECT_SUCCEEDED(watcher.EnsureEventSetup());
+  EXPECT_FALSE(watcher.HasChangeOccurred());
+
+  //
+  // verify that the watcher got set-up correctly the first time
+  //
+
+  // do something in the dir
+  File f;
+  int header1 = 94;
+  ASSERT_SUCCEEDED(f.Open(temp_dir + _T("\\testfile.1"), true, false));
+  ASSERT_SUCCEEDED(f.WriteAt(0, reinterpret_cast<byte*>(&header1),
+                             sizeof(header1), 0, NULL));
+  ASSERT_SUCCEEDED(f.Sync());
+  ASSERT_SUCCEEDED(f.Close());
+
+  // Did we noticed that something happened?
+  EXPECT_TRUE(watcher.HasChangeOccurred());
+  EXPECT_SUCCEEDED(watcher.EnsureEventSetup());
+  EXPECT_FALSE(watcher.HasChangeOccurred());
+
+  //
+  // verify that the watcher got set-up correctly the second time
+  //
+
+  // do something in the dir
+  byte header2 = 2;
+  ASSERT_SUCCEEDED(f.Open(temp_dir + _T("\\testfile.2"), true, false));
+  ASSERT_SUCCEEDED(f.WriteAt(0, reinterpret_cast<byte*>(&header2),
+                             sizeof(header2), 0, NULL));
+  ASSERT_SUCCEEDED(f.Sync());
+  ASSERT_SUCCEEDED(f.Close());
+
+  // Did we noticed that something happened?
+  EXPECT_TRUE(watcher.HasChangeOccurred());
+  EXPECT_SUCCEEDED(watcher.EnsureEventSetup());
+  EXPECT_FALSE(watcher.HasChangeOccurred());
+
+  EXPECT_SUCCEEDED(DeleteDirectory(temp_dir));
+}
+
+TEST(FileTest, Exists_UnQuoted) {
+  EXPECT_TRUE(File::Exists(kIEBrowserExe));
+  EXPECT_FALSE(File::Exists(_T("C:\\foo\\does not exist.exe")));
+  EXPECT_FALSE(File::Exists(_T("Z:\\foo\\does not exist.exe")));
+  if (ShouldRunLargeTest()) {
+    EXPECT_FALSE(File::Exists(_T("\\\\foo\\does not exist.exe")));
+  }
+}
+
+// File::Exists() expects unquoted paths.
+TEST(FileTest, Exists_Quoted) {
+  EXPECT_FALSE(File::Exists(kIEBrowserQuotedExe));
+  EXPECT_FALSE(File::Exists(_T("\"C:\\foo\\does not exist.exe\"")));
+  EXPECT_FALSE(File::Exists(_T("\"Z:\\foo\\does not exist.exe\"")));
+  if (ShouldRunLargeTest()) {
+    EXPECT_FALSE(File::Exists(_T("\"\\\\foo\\does not exist.exe\"")));
+  }
+}
+
+// File::Exists() handles trailing spaces but not leading whitespace, tabs, or
+// enclosed paths.
+TEST(FileTest, Exists_ExtraWhitespace) {
+  EXPECT_TRUE(File::Exists(kIEBrowserExe _T(" ")));
+  EXPECT_TRUE(File::Exists(kIEBrowserExe _T("    ")));
+  EXPECT_FALSE(File::Exists(_T(" ") kIEBrowserExe));
+  EXPECT_FALSE(File::Exists(kIEBrowserExe _T("\t")));
+
+  EXPECT_FALSE(File::Exists(kIEBrowserQuotedExe _T(" ")));
+  EXPECT_FALSE(File::Exists(kIEBrowserQuotedExe _T("    ")));
+  EXPECT_FALSE(File::Exists(_T(" ") kIEBrowserQuotedExe));
+  EXPECT_FALSE(File::Exists(kIEBrowserQuotedExe _T("\t")));
+
+  EXPECT_FALSE(File::Exists(_T("\"") kIEBrowserExe _T(" \"")));
+  EXPECT_FALSE(File::Exists(_T("\"") kIEBrowserExe _T("    \"")));
+  EXPECT_FALSE(File::Exists(_T("\" ") kIEBrowserExe _T("\"") ));
+  EXPECT_FALSE(File::Exists(_T("\"") kIEBrowserExe _T("\t\"")));
+
+  EXPECT_FALSE(File::Exists(_T("\"") kIEBrowserExe _T(" \" ")));
+}
+
+TEST(FileTest, AreFilesIdentical) {
+  CString windows_dir;
+  ASSERT_TRUE(::GetEnvironmentVariable(_T("SystemRoot"),
+                                       CStrBuf(windows_dir, MAX_PATH),
+                                       MAX_PATH));
+
+  CString known_file1(windows_dir + _T("\\NOTEPAD.EXE"));
+  CString known_file2(windows_dir + _T("\\REGEDIT.EXE"));
+
+  EXPECT_TRUE(File::AreFilesIdentical(known_file1, known_file1));
+  EXPECT_FALSE(File::AreFilesIdentical(known_file1, known_file2));
+}
+
+}  // namespace omaha
diff --git a/base/file_ver.cc b/base/file_ver.cc
new file mode 100644
index 0000000..a97f90e
--- /dev/null
+++ b/base/file_ver.cc
@@ -0,0 +1,178 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/file_ver.h"
+#include "omaha/base/commontypes.h"
+#include "omaha/base/debug.h"
+
+namespace omaha {
+
+// TODO(omaha): Write unittest for this class.
+
+FileVer::FileVer() {
+  file_ver_data_ = NULL;
+  lang_charset_ = 0;
+}
+
+FileVer::~FileVer() {
+  Close();
+}
+
+void FileVer::Close() {
+  delete[] file_ver_data_;
+  file_ver_data_ = NULL;
+  lang_charset_ = 0;
+}
+
+BOOL FileVer::Open(const TCHAR* lpszModuleName) {
+  ASSERT1(lpszModuleName);
+
+  // Get the version information size and allocate the buffer.
+  DWORD handle;
+  DWORD ver_info_size =
+      ::GetFileVersionInfoSize(const_cast<TCHAR*>(lpszModuleName), &handle);
+  if (ver_info_size == 0) {
+    return FALSE;
+  }
+
+  // Get version information.
+  // file_ver_data_ is allocated here and deleted in Close() (or implicitly
+  // in the destructor).
+  file_ver_data_ = new byte[ver_info_size];
+  ASSERT1(file_ver_data_);
+  if (!file_ver_data_) {
+    return FALSE;
+  }
+
+  if (!::GetFileVersionInfo(const_cast<TCHAR*>(lpszModuleName), handle,
+                            ver_info_size,
+                            reinterpret_cast<void**>(file_ver_data_))) {
+    Close();
+    return FALSE;
+  }
+
+  // Get the first language and character-set identifier.
+  UINT query_size = 0;
+  DWORD* translation_table = NULL;
+  if (!::VerQueryValue(file_ver_data_,
+                       _T("\\VarFileInfo\\Translation"),
+                       reinterpret_cast<void**>(&translation_table),
+                       &query_size) ||
+      query_size == 0) {
+    Close();
+    return FALSE;
+  }
+
+  ASSERT1(query_size != 0);
+  ASSERT1(translation_table);
+
+  // Create charset.
+  lang_charset_ = MAKELONG(HIWORD(translation_table[0]),
+                           LOWORD(translation_table[0]));
+  return TRUE;
+}
+
+CString FileVer::QueryValue(const TCHAR* lpszValueName) const {
+  ASSERT1(lpszValueName);
+
+  if (file_ver_data_ == NULL) {
+    return (CString)_T("");
+  }
+
+  // Query version information value.
+  UINT query_size = 0;
+  LPVOID query_data = NULL;
+  CString str_query_value, str_block_name;
+  str_block_name.Format(_T("\\StringFileInfo\\%08lx\\%s"),
+                        lang_charset_,
+                        lpszValueName);
+
+  if (::VerQueryValue(reinterpret_cast<void**>(file_ver_data_),
+                      str_block_name.GetBuffer(0),
+                      &query_data,
+                      &query_size) &&
+      query_size != 0 &&
+      query_data) {
+    str_query_value = reinterpret_cast<const TCHAR*>(query_data);
+  }
+
+  str_block_name.ReleaseBuffer();
+
+  return str_query_value;
+}
+
+BOOL FileVer::GetFixedInfo(VS_FIXEDFILEINFO& vsffi) const {   // NOLINT
+  if (file_ver_data_ == NULL) {
+    return FALSE;
+  }
+
+  UINT query_size = 0;
+  VS_FIXEDFILEINFO* pVsffi = NULL;
+  if (::VerQueryValue(reinterpret_cast<void**>(file_ver_data_),
+                      _T("\\"),
+                      reinterpret_cast<void**>(&pVsffi),
+                      &query_size) &&
+      query_size != 0 &&
+      pVsffi) {
+    vsffi = *pVsffi;
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+CString FileVer::FormatFixedFileVersion() const {
+  CString str_version;
+  VS_FIXEDFILEINFO vsffi = {0};
+
+  if (GetFixedInfo(vsffi)) {
+    str_version.Format(NOTRANSL(_T("%u.%u.%u.%u")),
+                       HIWORD(vsffi.dwFileVersionMS),
+                       LOWORD(vsffi.dwFileVersionMS),
+                       HIWORD(vsffi.dwFileVersionLS),
+                       LOWORD(vsffi.dwFileVersionLS));
+  }
+  return str_version;
+}
+
+CString FileVer::FormatFixedProductVersion() const {
+  CString str_version;
+  VS_FIXEDFILEINFO vsffi = {0};
+
+  if (GetFixedInfo(vsffi)) {
+    str_version.Format(NOTRANSL(_T("%u.%u.%u.%u")),
+                       HIWORD(vsffi.dwProductVersionMS),
+                       LOWORD(vsffi.dwProductVersionMS),
+                       HIWORD(vsffi.dwProductVersionLS),
+                       LOWORD(vsffi.dwProductVersionLS));
+  }
+  return str_version;
+}
+
+ULONGLONG FileVer::GetFileVersionAsULONGLONG() const {
+  ULONGLONG version = 0;
+  VS_FIXEDFILEINFO vsffi = {0};
+
+  if (GetFixedInfo(vsffi)) {
+    version = MAKEDLLVERULL(HIWORD(vsffi.dwProductVersionMS),
+                            LOWORD(vsffi.dwProductVersionMS),
+                            HIWORD(vsffi.dwProductVersionLS),
+                            LOWORD(vsffi.dwProductVersionLS));
+  }
+  return version;
+}
+
+}  // namespace omaha
+
diff --git a/common/file_ver.h b/base/file_ver.h
similarity index 100%
rename from common/file_ver.h
rename to base/file_ver.h
diff --git a/base/firewall_product_detection.cc b/base/firewall_product_detection.cc
new file mode 100644
index 0000000..fe3c481
--- /dev/null
+++ b/base/firewall_product_detection.cc
@@ -0,0 +1,67 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/firewall_product_detection.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/wmi_query.h"
+
+namespace omaha {
+
+namespace firewall_detection {
+
+namespace {
+
+const TCHAR kWmiSecurityCenter[]        = _T("root\\SecurityCenter");
+const TCHAR kWmiQueryFirewallProduct[]  = _T("select * from FirewallProduct");
+const TCHAR kWmiPropDisplayName[]       = _T("displayName");
+const TCHAR kWmiPropVersionNumber[]     = _T("versionNumber");
+
+}  // namespace
+
+
+HRESULT Detect(CString* name, CString* version) {
+  ASSERT1(name);
+  ASSERT1(version);
+
+  name->Empty();
+  version->Empty();
+
+  WmiQuery wmi_query;
+  HRESULT hr = wmi_query.Connect(kWmiSecurityCenter);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  hr = wmi_query.Query(kWmiQueryFirewallProduct);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  if (wmi_query.AtEnd()) {
+    return E_FAIL;
+  }
+  hr = wmi_query.GetValue(kWmiPropDisplayName, name);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  wmi_query.GetValue(kWmiPropVersionNumber, version);
+  return S_OK;
+}
+
+}  // namespace firewall_detection
+
+}  // namespace omaha
+
diff --git a/common/firewall_product_detection.h b/base/firewall_product_detection.h
similarity index 100%
rename from common/firewall_product_detection.h
rename to base/firewall_product_detection.h
diff --git a/base/firewall_product_detection_unittest.cc b/base/firewall_product_detection_unittest.cc
new file mode 100644
index 0000000..9f26ec3
--- /dev/null
+++ b/base/firewall_product_detection_unittest.cc
@@ -0,0 +1,36 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include "omaha/base/firewall_product_detection.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace firewall_detection {
+
+TEST(FirewallProductDetection, Detect) {
+  CString name, version;
+  HRESULT hr = Detect(&name, &version);
+  if (SUCCEEDED(hr)) {
+    EXPECT_FALSE(name.IsEmpty());
+    EXPECT_FALSE(version.IsEmpty());
+  }
+}
+
+}  // namespace firewall_detection
+
+}  // namespace omaha
+
diff --git a/common/generic_reg_file_appid.rgs b/base/generic_reg_file_appid.rgs
similarity index 100%
rename from common/generic_reg_file_appid.rgs
rename to base/generic_reg_file_appid.rgs
diff --git a/base/generic_reg_file_clsid_elevation_localserver.rgs b/base/generic_reg_file_clsid_elevation_localserver.rgs
new file mode 100644
index 0000000..d672a21
--- /dev/null
+++ b/base/generic_reg_file_clsid_elevation_localserver.rgs
@@ -0,0 +1,16 @@
+'%HKROOT%' {
+  NoRemove Software {
+    NoRemove Classes {
+      NoRemove CLSID {
+        ForceRemove '%CLSID%' = s '%DESCRIPTION%' {
+          Elevation {
+            val Enabled = d 1
+            val IconReference = s '@%MODULE_RAW%, -%ICONRESID%'
+          }
+          LocalServer32 = s '%EXE_MODULE%'
+          val LocalizedString = s '@%MODULE_RAW%,-%STRINGRESID%'
+        }
+      }
+    }
+  }
+}
diff --git a/base/generic_reg_file_dll.rgs b/base/generic_reg_file_dll.rgs
new file mode 100644
index 0000000..7b68f64
--- /dev/null
+++ b/base/generic_reg_file_dll.rgs
@@ -0,0 +1,13 @@
+'%HKROOT%' {
+  NoRemove Software {
+    NoRemove Classes {
+      NoRemove CLSID {
+        ForceRemove '%CLSID%' {
+          InprocServer32 = s '%MODULE%' {
+            val ThreadingModel = s 'Both'
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/base/generic_reg_file_dll_handler.rgs b/base/generic_reg_file_dll_handler.rgs
new file mode 100644
index 0000000..f787d7c
--- /dev/null
+++ b/base/generic_reg_file_dll_handler.rgs
@@ -0,0 +1,13 @@
+'%HKROOT%' {
+  NoRemove Software {
+    NoRemove Classes {
+      NoRemove CLSID {
+        ForceRemove '%CLSID%' {
+          InprocHandler32 = s '%MODULE%' {
+            val ThreadingModel = s 'Both'
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/base/generic_reg_file_elevation_localserver.rgs b/base/generic_reg_file_elevation_localserver.rgs
new file mode 100644
index 0000000..8e24f85
--- /dev/null
+++ b/base/generic_reg_file_elevation_localserver.rgs
@@ -0,0 +1,34 @@
+HKLM
+{
+  NoRemove SOFTWARE
+  {
+    NoRemove Classes
+    {
+      '%PROGID%.%VERSION%' = s '%DESCRIPTION%'
+      {
+        CLSID = s '%CLSID%'
+      }
+      '%PROGID%' = s '%DESCRIPTION%'
+      {
+        CLSID = s '%CLSID%'
+        CurVer = s '%PROGID%.%VERSION%'
+      }
+      NoRemove CLSID
+      {
+        ForceRemove '%CLSID%' = s '%DESCRIPTION%'
+        {
+          ProgID = s '%PROGID%.%VERSION%'
+          VersionIndependentProgID = s '%PROGID%'
+          LocalServer32 = s '%MODULE%'
+          val LocalizedString = s '@%MODULE_RAW%,-%STRINGRESID%'
+          Elevation
+          {
+            val Enabled = d '1'
+            val IconReference = s '@%MODULE_RAW%,-%ICONRESID%'
+          }
+        }
+      }
+    }
+  }
+}
+
diff --git a/base/generic_reg_file_ie_low_local_server.rgs b/base/generic_reg_file_ie_low_local_server.rgs
new file mode 100644
index 0000000..55003fd
--- /dev/null
+++ b/base/generic_reg_file_ie_low_local_server.rgs
@@ -0,0 +1,45 @@
+%HKROOT%
+{
+  NoRemove SOFTWARE
+  {
+    NoRemove Classes
+    {
+      %PROGID%.%VERSION% = s '%DESCRIPTION%'
+      {
+        CLSID = s '%CLSID%'
+      }
+      %PROGID% = s '%DESCRIPTION%'
+      {
+        CLSID = s '%CLSID%'
+        CurVer = s '%PROGID%.%VERSION%'
+      }
+      NoRemove CLSID
+      {
+        ForceRemove %CLSID% = s '%DESCRIPTION%'
+        {
+          ProgID = s '%PROGID%.%VERSION%'
+          VersionIndependentProgID = s '%PROGID%'
+          LocalServer32 = s '%MODULE%'
+        }
+      }
+    }
+    NoRemove Microsoft
+    {
+      NoRemove 'Internet Explorer'
+      {
+        NoRemove 'Low Rights'
+        {
+          NoRemove 'ElevationPolicy'
+          {
+            ForceRemove '%CLSID%'
+            {
+              val CLSID = s '%CLSID%'
+              val Policy = d '3'
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
diff --git a/base/generic_reg_file_local_server.rgs b/base/generic_reg_file_local_server.rgs
new file mode 100644
index 0000000..743a264
--- /dev/null
+++ b/base/generic_reg_file_local_server.rgs
@@ -0,0 +1,28 @@
+'%HKROOT%'
+{
+  NoRemove SOFTWARE
+  {
+    NoRemove Classes
+    {
+      '%PROGID%.%VERSION%' = s '%DESCRIPTION%'
+      {
+        CLSID = s '%CLSID%'
+      }
+      '%PROGID%' = s '%DESCRIPTION%'
+      {
+        CLSID = s '%CLSID%'
+        CurVer = s '%PROGID%.%VERSION%'
+      }
+      NoRemove CLSID
+      {
+        ForceRemove '%CLSID%' = s '%DESCRIPTION%'
+        {
+          ProgID = s '%PROGID%.%VERSION%'
+          VersionIndependentProgID = s '%PROGID%'
+          LocalServer32 = s '%MODULE%'
+        }
+      }
+    }
+  }
+}
+
diff --git a/base/generic_reg_file_local_server_w_appid.rgs b/base/generic_reg_file_local_server_w_appid.rgs
new file mode 100644
index 0000000..0d769c3
--- /dev/null
+++ b/base/generic_reg_file_local_server_w_appid.rgs
@@ -0,0 +1,62 @@
+HKCR
+{
+  '%PROGID%.%VERSION%' = s '%DESCRIPTION%'
+  {
+    CLSID = s '%CLSID%'
+  }
+  '%PROGID%' = s '%DESCRIPTION%'
+  {
+    CLSID = s '%CLSID%'
+    CurVer = s '%PROGID%.%VERSION%'
+  }
+  NoRemove CLSID
+  {
+    ForceRemove '%CLSID%' = s '%DESCRIPTION%'
+    {
+      ProgID = s '%PROGID%.%VERSION%'
+      VersionIndependentProgID = s '%PROGID%'
+      ForceRemove 'Programmable'
+      LocalServer32 = s '%MODULE%'
+      {
+        val ThreadingModel = s '%THREADING%'
+      }
+      val AppID = s '%APPID%'
+      'TypeLib' = s '%LIBID%'
+    }
+  }
+  NoRemove AppID
+  {
+    ForceRemove '%APPID%' = s '%DESCRIPTION%'
+    {
+
+    }
+    ForceRemove '%OBJECTFILENAME%'
+    {
+      val AppID = s '%APPID%'
+    }
+  }
+}
+HKLM
+{
+  NoRemove SOFTWARE
+  {
+    NoRemove Microsoft
+    {
+      NoRemove 'Internet Explorer'
+      {
+        NoRemove 'Low Rights'
+        {
+          NoRemove ElevationPolicy
+          {
+            ForceRemove '%APPID%'
+            {
+              val AppName = s '%OBJECTFILENAME%'
+              val AppPath = s '%MODULEPATH%'
+              val Policy = d '3'
+            }
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/base/generic_reg_file_localservice.rgs b/base/generic_reg_file_localservice.rgs
new file mode 100644
index 0000000..6f48c2b
--- /dev/null
+++ b/base/generic_reg_file_localservice.rgs
@@ -0,0 +1,28 @@
+HKLM
+{
+  NoRemove SOFTWARE
+  {
+    NoRemove Classes
+    {
+      '%PROGID%.%VERSION%' = s '%DESCRIPTION%'
+      {
+        CLSID = s '%CLSID%'
+      }
+      '%PROGID%' = s '%DESCRIPTION%'
+      {
+        CLSID = s '%CLSID%'
+        CurVer = s '%PROGID%.%VERSION%'
+      }
+      NoRemove CLSID
+      {
+        ForceRemove '%CLSID%' = s '%DESCRIPTION%'
+        {
+          ProgID = s '%PROGID%.%VERSION%'
+          VersionIndependentProgID = s '%PROGID%'
+          val AppID = s '%APPID%'
+        }
+      }
+    }
+  }
+}
+
diff --git a/base/generic_reg_file_typelib.rgs b/base/generic_reg_file_typelib.rgs
new file mode 100644
index 0000000..b55a786
--- /dev/null
+++ b/base/generic_reg_file_typelib.rgs
@@ -0,0 +1,10 @@
+HKCR
+{
+  NoRemove CLSID
+  {
+    ForceRemove '%CLSID%' = s '%DESCRIPTION%'
+    {
+      'TypeLib' = s '%LIBID%'
+    }
+  }
+}
diff --git a/common/has_exception_namespace_fix.h b/base/has_exception_namespace_fix.h
similarity index 100%
rename from common/has_exception_namespace_fix.h
rename to base/has_exception_namespace_fix.h
diff --git a/base/highres_timer-win32.cc b/base/highres_timer-win32.cc
new file mode 100644
index 0000000..f3d5111
--- /dev/null
+++ b/base/highres_timer-win32.cc
@@ -0,0 +1,60 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+#include "omaha/base/highres_timer-win32.h"
+
+namespace omaha {
+
+bool HighresTimer::perf_freq_collected_ = false;
+ULONGLONG HighresTimer::perf_freq_ = 0;
+
+ULONGLONG HighresTimer::GetElapsedMs() const {
+  ULONGLONG end_time = GetCurrentTicks();
+
+  // Scale to ms and round to nearerst ms - rounding is important
+  // because otherwise the truncation error may accumulate e.g. in sums.
+  //
+  // Given infinite resolution, this expression could be written as:
+  //  trunc((end - start (units:freq*sec))/freq (units:sec) *
+  //                1000 (unit:ms) + 1/2 (unit:ms))
+  ULONGLONG freq = GetTimerFrequency();
+  return ((end_time - start_ticks_) * 1000L + freq / 2) / freq;
+}
+
+ULONGLONG HighresTimer::GetElapsedSec() const {
+  ULONGLONG end_time = GetCurrentTicks();
+
+  // Scale to ms and round to nearerst ms - rounding is important
+  // because otherwise the truncation error may accumulate e.g. in sums.
+  //
+  // Given infinite resolution, this expression could be written as:
+  //  trunc((end - start (units:freq*sec))/freq (unit:sec) + 1/2 (unit:sec))
+  ULONGLONG freq = GetTimerFrequency();
+  return ((end_time - start_ticks_) + freq / 2) / freq;
+}
+
+void HighresTimer::CollectPerfFreq() {
+  LARGE_INTEGER freq;
+
+  // Note that this is racy.
+  // It's OK, however, because even concurrent executions of this
+  // are idempotent.
+  if (::QueryPerformanceFrequency(&freq)) {
+    perf_freq_ = freq.QuadPart;
+    perf_freq_collected_ = true;
+  }
+}
+
+}  // namespace omaha
+
diff --git a/common/highres_timer-win32.h b/base/highres_timer-win32.h
similarity index 100%
rename from common/highres_timer-win32.h
rename to base/highres_timer-win32.h
diff --git a/base/highres_timer_unittest.cc b/base/highres_timer_unittest.cc
new file mode 100644
index 0000000..6965244
--- /dev/null
+++ b/base/highres_timer_unittest.cc
@@ -0,0 +1,111 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+#include "base/basictypes.h"
+#include "omaha/base/highres_timer-win32.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+// Timing tests are extremely sensitive to external interference from other
+// work currently being done on the machine.  scoped_priority_boost temporarily
+// raises the priority of the caller's thread to reduce the chance of an
+// intervening context switch.
+
+class scoped_priority_boost {
+ public:
+  scoped_priority_boost() {
+    orig_process_priority_ = ::GetPriorityClass(::GetCurrentProcess());
+    if (0 != orig_process_priority_) {
+      if (0 == ::SetPriorityClass(::GetCurrentProcess(),
+                                  HIGH_PRIORITY_CLASS)) {
+        orig_process_priority_ = 0;
+      }
+    }
+
+    orig_thread_priority_ = ::GetThreadPriority(::GetCurrentThread());
+    if (THREAD_PRIORITY_ERROR_RETURN != orig_thread_priority_) {
+      if (0 == ::SetThreadPriority(::GetCurrentThread(),
+                                   THREAD_PRIORITY_HIGHEST)) {
+        orig_thread_priority_ = THREAD_PRIORITY_ERROR_RETURN;
+      }
+    }
+  }
+
+  ~scoped_priority_boost() {
+    if (0 != orig_process_priority_) {
+      ::SetPriorityClass(::GetCurrentProcess(), orig_process_priority_);
+    }
+    if (THREAD_PRIORITY_ERROR_RETURN != orig_thread_priority_) {
+      ::SetPriorityClass(::GetCurrentProcess(), orig_process_priority_);
+    }
+  }
+
+  bool succeeded() const {
+    return (0 != orig_process_priority_) &&
+            (THREAD_PRIORITY_ERROR_RETURN != orig_thread_priority_);
+  }
+
+ private:
+  DWORD orig_process_priority_;
+  int orig_thread_priority_;
+
+  DISALLOW_COPY_AND_ASSIGN(scoped_priority_boost);
+};
+
+TEST(HighresTimer, MillisecondClock) {
+  scoped_priority_boost spb;
+  EXPECT_TRUE(spb.succeeded());
+
+  HighresTimer timer;
+
+  // note: this could fail if we context switch between initializing the timer
+  // and here. Very unlikely however.
+  EXPECT_EQ(0, timer.GetElapsedMs());
+  timer.Start();
+  uint64 half_ms = (HighresTimer::GetTimerFrequency() / 2000) + 1;
+  // busy wait for a fraction more than half a millisecond.
+  while (timer.start_ticks() + half_ms > HighresTimer::GetCurrentTicks()) {
+    // Nothing
+  }
+  EXPECT_EQ(1, timer.GetElapsedMs());
+}
+
+TEST(HighresTimer, SecondClock) {
+  scoped_priority_boost spb;
+  EXPECT_TRUE(spb.succeeded());
+
+  HighresTimer timer;
+
+  EXPECT_EQ(0, timer.GetElapsedSec());
+#ifdef OS_WINDOWS
+  ::Sleep(250);
+#else
+  struct timespec ts1 = {0, 250000000};
+  nanosleep(&ts1, 0);
+#endif
+  EXPECT_EQ(0, timer.GetElapsedSec());
+  EXPECT_LE(230, timer.GetElapsedMs());
+  EXPECT_GE(270, timer.GetElapsedMs());
+#ifdef OS_WINDOWS
+  ::Sleep(251);
+#else
+  struct timespec ts2 = {0, 251000000};
+  nanosleep(&ts2, 0);
+#endif
+  EXPECT_EQ(1, timer.GetElapsedSec());
+}
+
+}  // namespace omaha
+
diff --git a/base/lang_enc.h b/base/lang_enc.h
new file mode 100644
index 0000000..9da8c03
--- /dev/null
+++ b/base/lang_enc.h
@@ -0,0 +1,299 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// This file is for i18n. It contains two enums, namely Language and
+// Encoding, where Language is the linguistic convention, and Encoding
+// contains information on both language encoding and character set.
+//
+// The language and encoding are both based on Teragram's conventions,
+// except for some common ISO-8859 encodings that are not detected by
+// Teragram but might be in the future.
+//
+// This file also includes functions that do mappings among
+// Language/Encoding enums, language/encoding string names (typically
+// the output from Language Encoding identifier), and language codes
+// (iso 639), and two-letter country codes (iso 3166)
+//
+// NOTE: Both Language and Encoding enums should always start from
+// zero value. This assumption has been made and used.
+
+#ifndef  OMAHA_BASE_LANG_ENC_H_
+#define  OMAHA_BASE_LANG_ENC_H_
+
+#include <windows.h>
+
+// some of the popular encoding aliases
+#define LATIN1     ISO_8859_1
+#define LATIN2     ISO_8859_2
+#define LATIN3     ISO_8859_3
+#define LATIN4     ISO_8859_4
+#define CYRILLIC   ISO_8859_5
+#define ARABIC_ENCODING  ISO_8859_6     // avoiding the same name as language
+#define GREEK_ENCODING   ISO_8859_7     // avoiding the same name as language
+#define HEBREW_ENCODING  ISO_8859_8     // avoiding the same name as language
+#define LATIN5     ISO_8859_9
+#define LATIN6     ISO_8859_10
+#define KOREAN_HANGUL  KOREAN_EUC_KR
+
+// NOTE: Only add new languages to the end of this list (but before
+// NUM_LANGUAGES).
+enum Language {
+  ENGLISH = 0,  /* 0 */
+  DANISH,       /* 1 */
+  DUTCH,        /* 2 */
+  FINNISH,      /* 3 */
+  FRENCH,       /* 4 */
+  GERMAN,       /* 5 */
+  HEBREW,       /* 6 */
+  ITALIAN,      /* 7 */
+  JAPANESE,     /* 8 */
+  KOREAN,       /* 9 */
+  NORWEGIAN,    /* 10 */
+  POLISH,       /* 11 */
+  PORTUGUESE,   /* 12 */
+  RUSSIAN,      /* 13 */
+  SPANISH,      /* 14 */
+  SWEDISH,      /* 15 */
+  CHINESE,      /* 16 */
+  CZECH,        /* 17 */
+  GREEK,        /* 18 */
+  ICELANDIC,    /* 19 */
+  LATVIAN,      /* 20 */
+  LITHUANIAN,   /* 21 */
+  ROMANIAN,     /* 22 */
+  HUNGARIAN,    /* 23 */
+  ESTONIAN,     /* 24 */
+  TG_UNKNOWN_LANGUAGE,  /* 25 */
+  UNKNOWN_LANGUAGE,     /* 26 */
+  BULGARIAN,    /* 27 */
+  CROATIAN,     /* 28 */
+  SERBIAN,      /* 29 */
+  IRISH,        /* 30 */
+  GALICIAN,     /* 31 */
+  TAGALOG,      /* 32 */
+  TURKISH,      /* 33 */
+  UKRAINIAN,    /* 34 */
+  HINDI,        /* 35 */
+  MACEDONIAN,   /* 36 */
+  BENGALI,      /* 37 */
+  INDONESIAN,   /* 38 */
+  LATIN,        /* 39 */
+  MALAY,        /* 40 */
+  MALAYALAM,    /* 41 */
+  WELSH,        /* 42 */
+  NEPALI,       /* 43 */
+  TELUGU,       /* 44 */
+  ALBANIAN,     /* 45 */
+  TAMIL,        /* 46 */
+  BELARUSIAN,   /* 47 */
+  JAVANESE,     /* 48 */
+  OCCITAN,      /* 49 */
+  URDU,         /* 50 */
+  BIHARI,       /* 51 */
+  GUJARATI,     /* 52 */
+  THAI,         /* 53 */
+  ARABIC,       /* 54 */
+  CATALAN,      /* 55 */
+  ESPERANTO,    /* 56 */
+  BASQUE,       /* 57 */
+  INTERLINGUA,  /* 58 */
+  KANNADA,      /* 59 */
+  PUNJABI,      /* 60 */
+  SCOTS_GAELIC, /* 61 */
+  SWAHILI,      /* 62 */
+  SLOVENIAN,    /* 63 */
+  MARATHI,      /* 64 */
+  MALTESE,      /* 65 */
+  VIETNAMESE,   /* 66 */
+  FRISIAN,      /* 67 */
+  SLOVAK,       /* 68 */
+  CHINESE_T,    /* 69 */      // This is added to solve the problem of
+                              // distinguishing Traditional and Simplified
+                              // Chinese when the encoding is UTF8.
+  FAROESE,      /* 70 */
+  SUNDANESE,    /* 71 */
+  UZBEK,        /* 72 */
+  AMHARIC,      /* 73 */
+  AZERBAIJANI,  /* 74 */
+  GEORGIAN,     /* 75 */
+  TIGRINYA,     /* 76 */
+  PERSIAN,      /* 77 */
+  BOSNIAN,      /* 78 */
+  SINHALESE,    /* 79 */
+  NORWEGIAN_N,  /* 80 */
+  PORTUGUESE_P, /* 81 */
+  PORTUGUESE_B, /* 82 */
+  XHOSA,        /* 83 */
+  ZULU,         /* 84 */
+  GUARANI,      /* 85 */
+  SESOTHO,      /* 86 */
+  TURKMEN,      /* 87 */
+  KYRGYZ,       /* 88 */
+  BRETON,       /* 89 */
+  TWI,          /* 90 */
+  YIDDISH,      /* 91 */
+  ORIYA,        /* 92 */
+  SERBO_CROATIAN,       /* 93 */
+  SOMALI,       /* 94 */
+  UIGHUR,       /* 95 */
+  KURDISH,      /* 96 */
+  MONGOLIAN,    /* 97 */
+  ARMENIAN,     /* 98 */
+  LAOTHIAN,     /* 99 */
+  SINDHI,       /* 100! */
+  RHAETO_ROMANCE,  /* 101 */
+  CHINESE_JAPANESE_KOREAN,  /* 103 */  // Not really a language
+  PSEUDOTRANSLATION,  /* 104 */  // Not really a language
+  NUM_LANGUAGES,              // Always keep this at the end. It is not a
+                              // valid Language enum, it is only used to
+                              // indicate the total number of Languages.
+};
+
+
+// Language codes for those languages we support, used to map to IDs from
+// the Language enumeration.  We could have used the Rfc1766ToLcid from the
+// Win32 system's mlang.dll to map these to LCIDs, but a) we don't want to
+// have to load mlang.dll and b) we are using our own language IDs.
+const TCHAR* const kLangCodeChinesePrc = _T("zh_cn");
+const TCHAR* const kLangCodeChineseTaiwan = _T("zh_tw");
+const TCHAR* const kLangCodeCjk = _T("cjk");
+const TCHAR* const kLangCodeDutch = _T("nl");
+const TCHAR* const kLangCodeEnglish = _T("en");
+const TCHAR* const kLangCodeFrench = _T("fr");
+const TCHAR* const kLangCodeGerman = _T("de");
+const TCHAR* const kLangCodeItalian = _T("it");
+const TCHAR* const kLangCodeJapanese = _T("ja");
+const TCHAR* const kLangCodeKorean = _T("ko");
+const TCHAR* const kLangCodePseudo = _T("x");
+const TCHAR* const kLangCodeSpanish = _T("es");
+
+
+// Maps language codes to languages.  Terminated by a { NULL, UNKNOWN_LANGUAGE }
+// item.
+struct CodeToLanguage {
+  const TCHAR* code;
+  Language language;
+};
+
+SELECTANY CodeToLanguage codes_to_languages[] = {
+  { kLangCodeChinesePrc, CHINESE },
+  { kLangCodeChineseTaiwan, CHINESE_T },
+  { kLangCodeCjk, CHINESE_JAPANESE_KOREAN },
+  { kLangCodeDutch, DUTCH },
+  { kLangCodeEnglish, ENGLISH },
+  { kLangCodeFrench, FRENCH },
+  { kLangCodeGerman, GERMAN },
+  { kLangCodeItalian, ITALIAN },
+  { kLangCodeJapanese, JAPANESE },
+  { kLangCodeKorean, KOREAN },
+  { kLangCodePseudo, PSEUDOTRANSLATION },
+  { kLangCodeSpanish, SPANISH },
+  { NULL, UNKNOWN_LANGUAGE }
+};
+
+
+
+// Macro to wrap the notion of "unknown language".
+#define IS_LANGUAGE_UNKNOWN(l)  \
+  ((l) == TG_UNKNOWN_LANGUAGE || (l) == UNKNOWN_LANGUAGE)
+
+// NOTE: Only add new encodings to the end of this list (but before
+// NUM_ENCODINGS).
+// NOTE: If you add an encoding here, you must also modify basistech_encoding()
+// and google2/com/google/i18n/Encoding.java
+enum Encoding {
+  ISO_8859_1 = 0,       // 0: Teragram ASCII
+  ISO_8859_2,           // 1: Teragram Latin2
+  ISO_8859_3,           // 2: in BasisTech but not in Teragram
+  ISO_8859_4,           // 3: Teragram Latin4
+  ISO_8859_5,           // 4: Teragram ISO-8859-5
+  ISO_8859_6,           // 5: Teragram Arabic
+  ISO_8859_7,           // 6: Teragram Greek
+  ISO_8859_8,           // 7: Teragram Hebrew
+  ISO_8859_9,           // 8: in BasisTech but not in Teragram
+  ISO_8859_10,          // 9: in BasisTech but not in Teragram
+  JAPANESE_EUC_JP,      // 10: Teragram EUC_JP
+  JAPANESE_SHIFT_JIS,   // 11: Teragram SJS
+  JAPANESE_JIS,         // 12: Teragram JIS
+  CHINESE_BIG5,         // 13: Teragram BIG5
+  CHINESE_GB,           // 14: Teragram GB
+  CHINESE_EUC_CN,       // 15: Teragram EUC-CN
+  KOREAN_EUC_KR,        // 16: Teragram KSC
+  UNICODE_ENCODING,     // 17: Teragram Unicode, changed to UNICODE_ENCODING
+                        //     from UNICODE, which is predefined by WINDOW
+  CHINESE_EUC_DEC,      // 18: Teragram EUC
+  CHINESE_CNS,          // 19: Teragram CNS
+  CHINESE_BIG5_CP950,   // 20: Teragram BIG5_CP950
+  JAPANESE_CP932,       // 21: Teragram CP932
+  UTF8,                 // 22
+  UNKNOWN_ENCODING,     // 23
+  ASCII_7BIT,           // 24: ISO_8859_1 with all characters <= 127.
+                        //     Should be present only in the crawler
+                        //     and in the repository,
+                        //     *never* as a result of Document::encoding().
+  RUSSIAN_KOI8_R,       // 25: Teragram KOI8R
+  RUSSIAN_CP1251,       // 26: Teragram CP1251
+
+  //----------------------------------------------------------
+  // These are _not_ output from teragram. Instead, they are as
+  // detected in the headers of usenet articles.
+  MSFT_CP1252,          // 27: CP1252 aka MSFT euro ascii
+  RUSSIAN_KOI8_RU,      // 28: CP21866 aka KOI8_RU, used for Ukrainian
+  MSFT_CP1250,          // 29: CP1250 aka MSFT eastern european
+  ISO_8859_15,          // 30: aka ISO_8859_0 aka ISO_8859_1 euroized
+  //----------------------------------------------------------
+
+  //----------------------------------------------------------
+  // These are in BasisTech but not in Teragram. They are
+  // needed for new interface languages. Now detected by
+  // research langid
+  MSFT_CP1254,          // 31: used for Turkish
+  MSFT_CP1257,          // 32: used in Baltic countries
+  //----------------------------------------------------------
+
+  //----------------------------------------------------------
+  //----------------------------------------------------------
+  // New encodings detected by Teragram
+  ISO_8859_11,          // 33: aka TIS-620, used for Thai
+  MSFT_CP874,           // 34: used for Thai
+  MSFT_CP1256,          // 35: used for Arabic
+
+  //----------------------------------------------------------
+  // Detected as ISO_8859_8 by Teragram, but can be found in META tags
+  MSFT_CP1255,          // 36: Logical Hebrew Microsoft
+  ISO_8859_8_I,         // 37: Iso Hebrew Logical
+  HEBREW_VISUAL,        // 38: Iso Hebrew Visual
+  //----------------------------------------------------------
+
+  //----------------------------------------------------------
+  // Detected by research langid
+  CZECH_CP852,          // 39
+  CZECH_CSN_369103,     // 40: aka ISO_IR_139 aka KOI8_CS
+  MSFT_CP1253,          // 41: used for Greek
+  RUSSIAN_CP866,        // 42
+  //----------------------------------------------------------
+  HZ_ENCODING,
+  ISO2022_CN,
+  ISO2022_KR,
+
+  NUM_ENCODINGS              // Always keep this at the end. It is not a
+                             // valid Encoding enum, it is only used to
+                             // indicate the total number of Encodings.
+};
+
+const int kNumLanguages = NUM_LANGUAGES;
+const int kNumEncodings = NUM_ENCODINGS;
+
+#endif  // OMAHA_BASE_LANG_ENC_H_
diff --git a/base/localization.cc b/base/localization.cc
new file mode 100644
index 0000000..e982fb1
--- /dev/null
+++ b/base/localization.cc
@@ -0,0 +1,336 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// localization.cpp
+//
+// Localization functions for date-time, strings, locales, and numbers
+
+#include "omaha/base/localization.h"
+
+#include <windows.h>  // SetThreadLocale
+#include <mlang.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/string.h"
+#include "omaha/base/time.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+// The maximum length for a date
+#define kDateLengthMax 200
+
+// The maximum length for chracter seperators
+#define kDecimalSeparatorMaxLen 5
+
+// Allow the unittest to override.
+static LCID lcid_override = MAKELCID(-1, -1);
+
+void SetLcidOverride(const LCID & lcid_new) {
+  lcid_override = lcid_new;
+}
+
+// We should call this, it allows for our override
+LCID GetLiveLcid() {
+  if (lcid_override != MAKELCID(-1, -1))
+    return lcid_override;
+
+  return GetUserDefaultLCID();
+}
+
+
+CString ShowDateInternal(const time64 & t, const LCID & lcid,
+                         // Either type needs to be 0 or format needs to be
+                         // NULL; both cannot be set simultaneously:
+                         const DWORD type, const TCHAR * format ) {
+  SYSTEMTIME time = Time64ToLocalTime(t);
+  TCHAR buf[kDateLengthMax] = {_T('\0')};
+  int num = ::GetDateFormat(lcid, type, &time, format, buf, kDateLengthMax);
+  ASSERT(num > 0, (_T("[localization::ShowDateInternal] - GetDateFormat ")
+                   _T("failed")));
+
+  return CString(buf);
+}
+
+CString ShowDateForLocale(const time64 & t, const LCID & lcid) {
+  return ShowDateInternal(t, lcid, DATE_SHORTDATE, NULL);
+}
+
+CString ShowFormattedDateForLocale(const time64 & t, const LCID & lcid,
+                                   const TCHAR * format) {
+  return ShowDateInternal(t, lcid, 0, format);
+}
+
+
+CString ShowTimeInternal(const time64 & t, const LCID & lcid,
+                         // Either type needs to be 0 or format needs to be
+                         // NULL; both cannot be set simultaneously:
+                         const DWORD type, const TCHAR * format) {
+  ASSERT(IsValidTime(t), (_T("[localization::ShowTimeInternal - Invalid ")
+                          _T("time %llu"), t));
+
+  SYSTEMTIME time = Time64ToLocalTime(t);
+  TCHAR buf[kDateLengthMax] = {_T('\0')};
+  int num = ::GetTimeFormat(lcid, type, &time, format, buf, kDateLengthMax);
+  ASSERT(num > 0, (_T("[localization::ShowTimeInternal - GetTimeFormat ")
+                   _T("failed")));
+
+  return CString(buf);
+}
+
+CString ShowTimeForLocale(const time64 & t, const LCID & lcid) {
+  return ShowTimeInternal(t, lcid, TIME_NOSECONDS, NULL);
+}
+
+CString ShowFormattedTimeForLocale(const time64 & t, const LCID & lcid,
+                                   const TCHAR * format) {
+  return ShowTimeInternal(t, lcid, 0, format);
+}
+
+// Show the long date and time [ie - Tuesday, March 20, 2004 5:15pm]
+CString ShowDateTimeForLocale(const time64 & t, const LCID & lcid) {
+  return ShowDateForLocale(t, lcid) + _T(" ") + ShowTimeForLocale(t, lcid);
+}
+
+// Get the long data and time in a (US English) format for logging
+CString ShowDateTimeForLogging(const time64 & t) {
+  if (t == 0) {
+     return CString();
+  }
+  const LCID lcid = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
+  return ShowDateTimeForLocale(t, lcid);
+}
+
+// Convert a number in string format to formatted string
+static CString Show(const CString & in, const int decimal_places) {
+  NUMBERFMT nf = {0};
+  TCHAR decimal_seperator[8] = {_T('\0')};
+  TCHAR thousands_seperator[8] = {_T('\0')};
+  GetNumberFormatForLCID(GetLiveLcid(), &nf,
+                         decimal_seperator, arraysize(decimal_seperator),
+                         thousands_seperator, arraysize(thousands_seperator));
+  nf.NumDigits = decimal_places;
+
+  TCHAR buf[kDateLengthMax] = {_T('\0')};
+  int num = GetNumberFormat(GetLiveLcid(),  // locale (current user locale)
+    0,                                      // options
+    in,                                     // input string (see MSDN for chars)
+    &nf,                                    // formatting information
+    buf,                                    // formatted string buffer
+    kDateLengthMax);                        // size of buffer
+
+  ASSERT(num > 0, (_T("GetNumberFormat failed: %s?"), in.GetString()));
+
+  return CString(buf);
+}
+
+// If we have a formatted number containing a decimal, and we want it to be
+// an int
+CString TrimDecimal(const CString & in) {
+  // Get the decimal seperator -- cache it as this is very slow
+  static LCID last_user_default_lcid = MAKELCID(-1, -1);
+  static TCHAR buf[kDecimalSeparatorMaxLen] = {_T('\0')};
+
+  LCID current_lcid = GetLiveLcid();
+  if (last_user_default_lcid != current_lcid) {
+    int num = GetLocaleInfo(GetLiveLcid(),
+                            LOCALE_SDECIMAL,
+                            buf,
+                            kDecimalSeparatorMaxLen);
+    ASSERT(num > 0, (L"GetLocaleInfo(.., LOCALE_SDECIMAL, ..) failed?"));
+    last_user_default_lcid = current_lcid;
+  }
+
+  CString sep(buf);
+
+  // Trim it if necessary
+  int pos = String_FindString(in, sep);
+  if (pos != -1)
+    return in.Left(pos);
+
+  return in;
+}
+
+// Number Functions
+// Changes the number into a user viewable format for the current locale
+
+// TODO(omaha): Rename these functions into ShowNumberForLocale.
+CString Show(const int i) {
+  return TrimDecimal(Show(itostr(i), 0));
+}
+
+CString Show(const uint32 u) {
+  return TrimDecimal(Show(itostr(u), 0));
+}
+
+CString Show(const double & d, const int decimal_places) {
+  return Show(String_DoubleToString(d, decimal_places), decimal_places);
+}
+
+HRESULT SetLocaleToRfc1766(const TCHAR * rfc1766_locale) {
+  ASSERT1(rfc1766_locale != NULL);
+
+  // Convert the RFC 1766 locale (eg, "fr-CA" for Canadian French to a
+  // Windows LCID (eg, 0x0c0c for Canadian French)
+  CComPtr<IMultiLanguage2> pIM;
+  RET_IF_FAILED(pIM.CoCreateInstance(__uuidof(CMultiLanguage)));
+
+  LCID lcid = 0;
+  CComBSTR rfc1766_locale_bstr(rfc1766_locale);
+  RET_IF_FAILED(pIM->GetLcidFromRfc1766(&lcid, (BSTR)rfc1766_locale_bstr));
+
+  return SetLocaleToLCID(lcid);
+}
+
+HRESULT SetLocaleToLCID(const LCID & lcid) {
+  // Initialize the locales
+  //   (in an attempt to cut down on our memory footprint, don't call
+  //    the libc version of setlocale)
+  if (!::SetThreadLocale(lcid)) {
+    UTIL_LOG(LEVEL_ERROR, (_T("Unable to SetThreadLocale to lcid 0x%x"),
+                           lcid));
+    return E_FAIL;
+  }
+
+  return S_OK;
+}
+
+HRESULT GetLocaleAsLCID(LCID * lcid) {
+  ASSERT1(lcid != NULL);
+
+  *lcid = GetThreadLocale();
+  return S_OK;
+}
+
+HRESULT GetLocaleAsRfc1766(CString * rfc1766_locale) {
+  ASSERT1(rfc1766_locale != NULL);
+
+  LCID lcid = 0;
+  HRESULT hr = GetLocaleAsLCID(&lcid);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CComPtr<IMultiLanguage2> pIM;
+  RET_IF_FAILED(pIM.CoCreateInstance(__uuidof(CMultiLanguage)));
+
+  CComBSTR bstr;
+  RET_IF_FAILED(pIM->GetRfc1766FromLcid(lcid, &bstr));
+
+  *rfc1766_locale = bstr;
+  return hr;
+}
+
+HRESULT GetNumberFormatForLCID(const LCID & lcid, NUMBERFMT * fmt,
+                               TCHAR * fmt_decimal_buf,
+                               size_t decimal_buf_len,  // including null char
+                               TCHAR * fmt_thousand_buf,
+                               size_t thousand_buf_len) {  // including null
+  ASSERT1(fmt);
+
+  TCHAR buf[64] = {_T('\0')};
+  size_t buf_len = arraysize(buf);
+
+  HRESULT hr = S_OK;
+  int retval = GetLocaleInfo(lcid, LOCALE_IDIGITS, buf, buf_len);
+
+  if (!retval) {
+    CORE_LOG(LEVEL_WARNING, (_T("[localization::GetNumberFormatForLCID - ")
+                             _T("Failed to load LOCALE_IDIGITS]")));
+    hr = E_FAIL;
+  } else {
+    fmt->NumDigits = String_StringToInt(buf);
+  }
+
+  retval = GetLocaleInfo(lcid, LOCALE_ILZERO, buf, buf_len);
+  if (!retval) {
+    CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ")
+                             _T("Failed to load LOCALE_ILZERO]")));
+    hr = E_FAIL;
+  } else {
+    fmt->LeadingZero = String_StringToInt(buf);
+  }
+
+  retval = GetLocaleInfo(lcid, LOCALE_INEGNUMBER, buf, buf_len);
+  if (!retval) {
+    CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ")
+                             _T("Failed to load LOCALE_INEGNUMBER]")));
+    hr = E_FAIL;
+  } else {
+    fmt->NegativeOrder = String_StringToInt(buf);
+  }
+
+  retval = GetLocaleInfo(lcid, LOCALE_SGROUPING, buf, buf_len);
+  if (!retval) {
+    CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ")
+                             _T("Failed to load LOCALE_SGROUPING]")));
+    hr = E_FAIL;
+  } else {
+    // A string terminated in ';0' is equivalent to the substring without
+    // the ';0', so just truncate the ';0' from the string
+    int semicolon_idx = String_ReverseFindChar(buf, _T(';'));
+    if (retval > semicolon_idx && buf[semicolon_idx + 1] == _T('0')) {
+      buf[semicolon_idx] = _T('\0');
+    }
+
+    if (String_FindChar(buf, _T(';')) != -1) {
+      // NUMBERFMT only allows values 0-9 or 32 for number grouping.  If
+      // this locale has variable-length grouping rules (as indicated by
+      // the presence of ';[1-9]'), pass in the only variable-length
+      // grouping rule NUMBERFMT understands: 32.  Note that '3;0' is
+      // considered a fixed-length grouping rule and handled above.
+      // This is a HACK.
+      fmt->Grouping = 32;
+    } else {
+      fmt->Grouping = String_StringToInt(buf);
+    }
+  }
+
+  // GetLocaleInfo doesn't write more than 4 chars for this field (per MSDN)
+  retval = GetLocaleInfo(lcid, LOCALE_SDECIMAL, fmt_decimal_buf,
+                         decimal_buf_len);
+  if (!retval) {
+    CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ")
+                             _T("Failed to load LOCALE_SDECIMAL]")));
+    hr = E_FAIL;
+  } else {
+    fmt->lpDecimalSep = fmt_decimal_buf;
+  }
+
+  // GetLocaleInfo doesn't write more than 4 chars for this field (per MSDN)
+  retval = GetLocaleInfo(lcid, LOCALE_STHOUSAND, fmt_thousand_buf,
+                         thousand_buf_len);
+  if (!retval) {
+    CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ")
+                             _T("Failed to load LOCALE_STHOUSAND]")));
+    hr = E_FAIL;
+  } else {
+    fmt->lpThousandSep = fmt_thousand_buf;
+  }
+
+  retval = GetLocaleInfo(lcid, LOCALE_INEGNUMBER, buf, buf_len);
+  if (!retval) {
+    CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ")
+                             _T("Failed to load LOCALE_INEGNUMBER]")));
+    hr = E_FAIL;
+  } else {
+    fmt->NegativeOrder = String_StringToInt(buf);
+  }
+
+  return hr;
+}
+
+}  // namespace omaha
+
diff --git a/base/localization.h b/base/localization.h
new file mode 100644
index 0000000..87c8b14
--- /dev/null
+++ b/base/localization.h
@@ -0,0 +1,94 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+//
+// localization.h
+//
+// Localization functions for date-time, strings, locales, and numbers
+
+#ifndef OMAHA_BASE_LOCALIZATION_H_
+#define OMAHA_BASE_LOCALIZATION_H_
+
+#include <atlstr.h>
+#include "base/basictypes.h"
+#include "omaha/base/time.h"
+
+namespace omaha {
+
+// Allows us to override LCIDs for unittests
+void SetLcidOverride(const LCID & lcid_new);
+
+
+//
+// Date-time Functions
+//
+
+// Show the time in the specified locale's default format.
+// If you want to use the user's default format, use LOCALE_USER_DEFAULT
+// for the locale [eg - "5:15:34 pm" is the US default]
+CString ShowDateForLocale(const time64 & t, const LCID & lcid);
+
+// Show the time in the specified format for the specified locale.
+// If you want to use the user's default format, use LOCALE_USER_DEFAULT
+// for the locale [eg - "5:15:34 pm" is the US default]
+CString ShowFormattedDateForLocale(const time64 & t, const LCID & lcid,
+                                   const TCHAR * format);
+
+
+// Show the time in the specified locale's default format.
+// If you want to use the user's default format, use LOCALE_USER_DEFAULT
+// for the locale [eg - "5:15:34 pm" is the US default]
+CString ShowTimeForLocale(const time64 & t, const LCID & lcid);
+
+// Show the time in the specified format for the specified locale.
+// If you want to use the user's default format, use LOCALE_USER_DEFAULT
+// for the locale [eg - "5:15:34 pm" is the US default]
+CString ShowFormattedTimeForLocale(const time64 & t, const LCID & lcid,
+                                   const TCHAR * format);
+
+// Show the long date and time [ie - Tuesday, March 20, 2004 5:15pm]
+CString ShowDateTimeForLocale(const time64 & t, const LCID & lcid);
+
+// Get the long data and time in a (US English) format for logging
+CString ShowDateTimeForLogging(const time64 & t);
+
+//
+// Number Functions
+//
+
+// Changes the number into a user viewable format for the current locale
+CString Show(const int i);
+CString Show(const uint32 u);
+CString Show(const double & d, const int decimal_places);
+
+
+//
+// Locale Name / LCID / RFC 1766 conversions
+//
+HRESULT SetLocaleToRfc1766(const TCHAR * rfc1766_locale);
+HRESULT SetLocaleToLCID(const LCID & lcid);
+
+HRESULT GetLocaleAsLCID(LCID * lcid);
+HRESULT GetLocaleAsRfc1766(CString * rfc1766_locale);
+
+HRESULT GetNumberFormatForLCID(const LCID & lcid, NUMBERFMT * fmt,
+                               TCHAR * fmt_decimal_buf,
+                               size_t decimal_buf_len,
+                               TCHAR * fmt_thousand_buf,
+                               size_t thousand_buf_len);
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_LOCALIZATION_H_
diff --git a/base/localization_unittest.cc b/base/localization_unittest.cc
new file mode 100644
index 0000000..253c279
--- /dev/null
+++ b/base/localization_unittest.cc
@@ -0,0 +1,158 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// localization_unittest.cpp
+//
+// Unit test functions for Localization
+
+#include "omaha/base/localization.h"
+#include "omaha/base/string.h"
+#include "omaha/base/time.h"
+#include "omaha/testing/unit_test.h"
+
+using testing::Message;
+
+namespace omaha {
+
+// Test out the time display functions
+void LocalizationTimeTest() {
+  CString time_str;
+
+  // Lets process this a bit to give ourselves a known time.
+  SYSTEMTIME temp_time;
+  temp_time.wYear = 2004;
+  temp_time.wMonth = 4;
+  temp_time.wDayOfWeek = 1;
+  temp_time.wDay = 19;
+  temp_time.wHour = 19;
+  temp_time.wMinute = 18;
+  temp_time.wSecond = 17;
+  temp_time.wMilliseconds = 16;
+
+  time64 override_time = SystemTimeToTime64(&temp_time);
+
+  // Useful when debugging to confirm that this worked
+  SYSTEMTIME confirm = Time64ToSystemTime(override_time);
+
+  // the need to check two different times below is because:
+
+  // FileTimeToLocalFileTime uses the current settings for the time
+  // zone and daylight saving time. Therefore, if it is daylight
+  // saving time, this function will take daylight saving time into
+  // account, even if the time you are converting is in standard
+  // time.
+  // TODO(omaha): we may want to fix this.
+
+  // Show just the time [ie -12:19pm]
+  time_str = ShowTimeForLocale(override_time, 1033 /* US english */);
+  ASSERT_TRUE(time_str == _T("12:18 PM") || time_str == _T("11:18 AM"))
+      << _T("Returned time string was ") << time_str.GetString();
+
+  // Show just the time [ie - 12:19:18pm]
+  time_str = ShowFormattedTimeForLocale(override_time, 1033,
+                                        _T("hh:mm:ss tt"));
+  ASSERT_TRUE(time_str == _T("12:18:17 PM") || time_str == _T("11:18:17 AM"))
+      << _T("Returned time string was ") << time_str.GetString();
+
+  // Try it out with a some different values to test out single digit
+  // minutes and such
+  temp_time.wHour = 15;
+  temp_time.wMinute = 4;
+  temp_time.wSecond = 3;
+  temp_time.wMilliseconds = 2;
+  override_time = SystemTimeToTime64(&temp_time);
+
+  time_str = ShowTimeForLocale(override_time, 1033);
+  ASSERT_TRUE(time_str == _T("8:04 AM") || time_str == _T("7:04 AM"))
+      << _T("Returned time string was ") << time_str.GetString();
+
+  time_str = ShowFormattedTimeForLocale(override_time, 1033,
+                                        _T("hh:mm:ss tt"));
+  ASSERT_TRUE(time_str == _T("08:04:03 AM") || time_str == _T("07:04:03 AM"))
+      << _T("Returned time string was ") << time_str.GetString();
+
+
+  //
+  // Check the date functionality
+  //
+
+  temp_time.wYear = 2004;
+  temp_time.wMonth = 4;
+  temp_time.wDayOfWeek = 1;
+  temp_time.wDay = 19;
+
+  // Show the short date
+  time_str = ShowDateForLocale(override_time, 1033);
+//  CHKM(time_str == _T("Monday, April 19, 2004"),
+  ASSERT_STREQ(time_str, _T("4/19/2004"));
+
+  // Show the customized date
+  time_str = ShowFormattedDateForLocale(override_time, 1033,
+                                        _T("MMM d, yyyy"));
+  ASSERT_STREQ(time_str, _T("Apr 19, 2004"));
+
+  // Try it out with a some different values to test out single dates and such
+  temp_time.wDay = 1;
+  override_time = SystemTimeToTime64(&temp_time);
+
+  time_str = ShowFormattedDateForLocale(override_time, 1033,
+                                        _T("ddd, MMM dd"));
+  ASSERT_STREQ(time_str, _T("Thu, Apr 01"));
+
+  time_str = ShowFormattedDateForLocale(override_time, 1033, _T("MM/dd/yyyy"));
+  ASSERT_STREQ(time_str, _T("04/01/2004"));
+}
+
+// Test out the numbers and display functions
+void LocalizationNumberTest() {
+  // Make sure we are using the normal american version
+  SetLcidOverride(1033);  // the codepage for american english
+
+  // Try some basics
+  ASSERT_STREQ(Show(1), _T("1"));
+  ASSERT_STREQ(Show(2), _T("2"));
+
+  // Try some extremes
+  ASSERT_STREQ(Show(0), _T("0"));
+  ASSERT_STREQ(Show(kint32max), _T("2,147,483,647"));
+  ASSERT_STREQ(Show(-kint32max), _T("-2,147,483,647"));
+  ASSERT_STREQ(Show(kuint32max), _T("4,294,967,295"));
+
+  // Try some doubles
+  ASSERT_STREQ(Show(0.3, 0), _T("0"));
+  ASSERT_STREQ(Show(0.3, 1), _T("0.3"));
+  ASSERT_STREQ(Show(0.3, 2), _T("0.30"));
+  ASSERT_STREQ(Show(0.3, 5), _T("0.30000"));
+
+  // Try some with interesting rounding
+  ASSERT_STREQ(Show(0.159, 0), _T("0"));
+  ASSERT_STREQ(Show(0.159, 1), _T("0.1"));
+  ASSERT_STREQ(Show(0.159, 2), _T("0.15"));
+  ASSERT_STREQ(Show(0.159, 5), _T("0.15900"));
+
+  // Try a nice whole number
+  ASSERT_STREQ(Show(12.0, 0), _T("12"));
+  ASSERT_STREQ(Show(12.0, 1), _T("12.0"));
+  ASSERT_STREQ(Show(12.0, 2), _T("12.00"));
+  ASSERT_STREQ(Show(12.0, 5), _T("12.00000"));
+}
+
+TEST(LocalizationTest, Localization) {
+  LocalizationTimeTest();
+  LocalizationNumberTest();
+}
+
+}  // namespace omaha
+
diff --git a/base/lock_ptr.h b/base/lock_ptr.h
new file mode 100644
index 0000000..74c424a
--- /dev/null
+++ b/base/lock_ptr.h
@@ -0,0 +1,128 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// lock_ptr.h
+//
+// A smart pointer to manage synchronized access to a shared resource.
+//
+// LockPtr provides a simple and concise syntax for accessing a
+// shared resource. The LockPtr is a smart pointer and provides
+// pointer operators -> and *. LockPtr does not have copy semantics and it is
+// not intended to be stored in containers. Instead, instances of LockPtr are
+// usually unamed or short-lived named variables.
+//
+// LockPtr uses an external lock, it acquires the lock in the constructor, and
+// it guarantees the lock is released in the destructor.
+//
+// Since different types of locks have different method names, such as
+// Enter/Exit or Lock/Unlock, etc, LockPtr uses an external customizable policy
+// to bind to different operations. The external policy is a set of template
+// functions that can be specialized for different types of locks, if needed.
+// Think of this policy as an adapter between the lock type and the LockPtr.
+//
+// Usage: let's assume that we have the type below:
+//
+// class X {
+//  public:
+//    X() : i_(0) {}
+//    void f() {}
+//
+//  private:
+//    int i_;
+//
+//    friend int LockPtrTest(int, int);
+// };
+//
+// We have an instance of this type and an external lock instance to serialize
+// the access to the X instance.
+//
+// Using LockPtr, the code is:
+//
+//    X x;
+//    LLock local_lock;
+//
+//    LockPtr<X>(x, local_lock)->f();
+//
+// For more example, please see the unit test of the module.
+
+
+
+#ifndef OMAHA_COMMON_LOCK_PTR_H_
+#define OMAHA_COMMON_LOCK_PTR_H_
+
+#include "omaha/base/debug.h"
+
+namespace omaha {
+
+template <typename T>
+class LockPtr {
+ public:
+  template <typename U>
+  LockPtr(T& obj, U& lock)
+      : pobj_(&obj),
+        plock_(&lock),
+        punlock_method_(&LockPtr::Unlock<U>) {
+    AcquireLock(lock);
+  }
+
+  ~LockPtr() {
+    ASSERT1(punlock_method_);
+    (this->*punlock_method_)();
+  }
+
+  // Pointer behavior
+  T& operator*() {
+    ASSERT1(pobj_);
+    return *pobj_;
+  }
+
+  T* operator->() {
+    return pobj_;
+  }
+
+ private:
+  // template method to restore the type of the lock and to call the
+  // release policy for the lock
+  template <class U>
+  void Unlock() {
+    ASSERT1(plock_);
+    U& lock = *(static_cast<U*>(plock_));
+    ReleaseLock(lock);
+  }
+
+  T* pobj_;       // managed shared object
+  void* plock_;   // type-less lock to control access to pobj_
+
+  void (LockPtr::*punlock_method_)();   // the address of the method to Unlock
+
+  DISALLOW_EVIL_CONSTRUCTORS(LockPtr);
+};
+
+// template functions to define the policy of acquiring and releasing
+// the locks.
+template <class Lock> inline void AcquireLock(Lock& lock) { lock.Lock(); }
+template <class Lock> inline void ReleaseLock(Lock& lock) { lock.Unlock(); }
+
+// specialization of policy for diferent types of locks.
+#include "omaha/base/synchronized.h"
+template <> void inline AcquireLock(CriticalSection& cs) { cs.Enter(); }
+template <> void inline ReleaseLock(CriticalSection& cs) { cs.Exit(); }
+
+// Add more policy specializations below, if needed.
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_LOCK_PTR_H_
+
diff --git a/base/lock_ptr_unittest.cc b/base/lock_ptr_unittest.cc
new file mode 100644
index 0000000..d1cce8b
--- /dev/null
+++ b/base/lock_ptr_unittest.cc
@@ -0,0 +1,98 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// lock_ptr_unittest.cpp
+
+#include "omaha/base/lock_ptr.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+// Test type.
+class X {
+ public:
+  X() : i_(0) {}
+  void f() { i_ = 10; }
+
+ private:
+  int i_;
+
+  friend class LockTest;
+  FRIEND_TEST(LockTest, X);
+};
+
+// A dummy lock just to test that locking and unlocking methods get called.
+class DummyLock {
+ public:
+  DummyLock() : cnt_(0) {}
+
+  void FakeLock() { ++cnt_; }
+  void FakeUnlock() { --cnt_; }
+
+ private:
+  int cnt_;
+
+  friend class LockTest;
+  FRIEND_TEST(LockTest, DummyLock);
+};
+
+// Specialization of the policy for the DummyLock.
+template<> void AcquireLock(DummyLock& lock) { lock.FakeLock(); }
+template<> void ReleaseLock(DummyLock& lock) { lock.FakeUnlock(); }
+
+// Empty test fixture.
+class LockTest : public testing::Test {};
+
+TEST_F(LockTest, X) {
+  // Create a few synchronization objects.
+  CriticalSection cs_lock;
+  LLock local_lock;
+  GLock global_lock;
+  ASSERT_TRUE(global_lock.Initialize(_T("test")));
+
+  // The instance to lock.
+  X x;
+
+  // Lock the instance and call a method.
+  LockPtr<X>(x, local_lock)->f();
+  ASSERT_EQ((*LockPtr<X>(x, cs_lock)).i_, 10);
+
+  // Lock the instance and access a data member.
+  LockPtr<X>(x, cs_lock)->i_ = 0;
+  ASSERT_EQ((*LockPtr<X>(x, cs_lock)).i_, 0);
+
+  // Lock the instance and call a method.
+  LockPtr<X>(x, global_lock)->f();
+  ASSERT_EQ((*LockPtr<X>(x, cs_lock)).i_, 10);
+}
+
+TEST_F(LockTest, DummyLock) {
+  DummyLock dummy_lock;
+  ASSERT_EQ(dummy_lock.cnt_, 0);
+
+  // The instance to lock.
+  X x;
+
+  {
+    LockPtr<X> p(x, dummy_lock);
+    ASSERT_EQ(dummy_lock.cnt_, 1);
+  }
+
+  ASSERT_EQ(dummy_lock.cnt_, 0);
+}
+
+}  // namespace omaha
+
diff --git a/base/logging.cc b/base/logging.cc
new file mode 100644
index 0000000..9285510
--- /dev/null
+++ b/base/logging.cc
@@ -0,0 +1,1369 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Tracing and logging system.
+//
+// The log output goes to "All Users/Application Data/Google/Update/Log/".
+// The log configuration file is under
+// "%Program Files%/C:\Program Files\Google\Common\Update".
+// By default, the logging is on in debug modes and off in opt mode although in
+// opt mode only the OPT_LOG statements write to the log.
+//
+// The log is open to all the users to write to. There is a known vulnerability
+// where a DOS can be created by holding on to the logging mutex.
+//
+// In this module use of ASSERT & REPORT is banned.  This is to prevent any
+// possible recursion issues between logging (logging.h) and
+// asserting/reporting (debug.h).  Both are basement-level systems that need to
+// work when almost nothing else works and interdependencies are best avoided.
+// One unavoidable interdependency is that debugASSERT will send messages to
+// the logger via Logger::OutputMessage - which then broadcasts it to each
+// LogWriter's OutputMessage.  So these methods should be as simple as
+// possible.  (Also, unlike asserting/reporting - this module will not avoid
+// use of the heap, however the code executed from Logger::OutputMessage
+// doesn't use the heap (for all the LogWriters in this file).)
+//
+// TODO(omaha): implement the minidump handling in terms of breakpad.
+//              Log initialization if full of lazy init. Consider doing
+//              eager init of log and its registered log writers when the
+//              log is created and initialized.
+//              Reimplement in terms of smart handles and locks.
+//              Reimplement without dependency on any other compilation unit
+//               that can call assert, verify, or the log itself.
+//              Redo the history logging feature
+
+#include "omaha/base/logging.h"
+
+#include <excpt.h>  // Microsoft specific: structured exceptions.
+#include <shlobj.h>
+#include <shlwapi.h>
+#include <string.h>
+#include <atlpath.h>
+#include <atlsecurity.h>
+#include "base/basictypes.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/const_debug.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/etw_log_writer.h"
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/string.h"
+#include "omaha/base/time.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+// enforce ban on ASSERT/REPORT
+#undef ASSERT
+#undef REPORT
+
+#ifdef LOGGING
+
+#define kNumLockRetries (20)
+#define kLockRetryDelayMs (50)
+
+// Circular buffer to log history.
+static wchar_t history_buffer[kMaxHistoryBufferSize];
+
+// Index into the history buffer to begin writing at.
+static int history_buffer_next_idx = 0;
+
+// Indicates whether the history buffer has ever reached its full capacity.
+// Once this boolean is true, if never becomes false.
+static bool history_buffer_full = false;
+
+//
+// Table of category names to categories.
+//
+#define LC_ENTRY(lc_value)  (L#lc_value), (lc_value)
+struct {
+  wchar_t* category_name;
+  LogCategory category;
+} static LogCategoryNames[] = {
+  LC_ENTRY(LC_UTIL),
+  LC_ENTRY(LC_SETUP),
+  LC_ENTRY(LC_SHELL),
+  LC_ENTRY(LC_CORE),
+  LC_ENTRY(LC_JS),
+  LC_ENTRY(LC_PLUGIN),
+  LC_ENTRY(LC_SERVICE),
+  LC_ENTRY(LC_OPT),
+  LC_ENTRY(LC_NET),
+  LC_ENTRY(LC_REPORT),
+};
+
+COMPILE_ASSERT(arraysize(LogCategoryNames) == LC_MAX_CAT - 1,
+               LogCategoryNames_missing_category);
+
+static CString StripBackslashes(const CString& name) {
+  int n = String_FindChar(name, L'\\');
+  if (n == -1) {
+    return name;
+  } else {
+    CString result;
+    for (int i = 0; i < name.GetLength(); ++i) {
+      if (name[i] != L'\\') {
+        result += name[i];
+      }
+    }
+    return result;
+  }
+}
+
+static CString GetProcName() {
+  CString proc_name(app_util::GetAppNameWithoutExtension());
+  CString module_name(app_util::GetCurrentModuleNameWithoutExtension());
+  CString result(proc_name);
+  if (module_name.CompareNoCase(proc_name) != 0) {
+    result += L":";
+    result += module_name;
+  }
+  return result;
+}
+
+// Formats a line prefix with the current time min:sec:milisec if wanted,
+// otherwise just the process:module.
+static void FormatLinePrefix(bool show_time,
+                             const wchar_t* proc_name,
+                             CString& result) {
+  if (show_time) {
+    SYSTEMTIME system_time = {0};
+    GetLocalTime(&system_time);
+    result.Format(L"[%02d/%02d/%02d %02d:%02d:%02d.%03d]",
+                  system_time.wMonth, system_time.wDay, system_time.wYear % 100,
+                  system_time.wHour, system_time.wMinute, system_time.wSecond,
+                  system_time.wMilliseconds);
+  }
+  result.AppendFormat(L"[%s][%u:%u]",
+                      proc_name,
+                      ::GetCurrentProcessId(),
+                      ::GetCurrentThreadId());
+}
+
+static bool g_logging_valid = false;
+static Logging g_logging;
+
+// Singleton factory for the Logging object.
+Logging* GetLogging() {
+  return g_logging_valid ? &g_logging : NULL;
+}
+
+// Force the logging system to be initialized during elaboration of static
+// constructors, while the program is still single-threaded.  This global
+// static object will cause the static logger inside of GetLogging() to be
+// constructed.  However, it might not be the first call to GetLogging() -
+// another static object in another object file might get constructed first,
+// and in its constructor call a logging API.  Just as long as it is done when
+// the system is single threaded.  (Reason: because the Logger object has a
+// LLock object which has a Win32 critical section which needs to be
+// initialized - only once!)
+
+
+Logging::Logging()
+    : logging_initialized_(false),
+      logging_enabled_(true),
+      force_show_time_(false),
+      show_time_(true),
+      log_to_file_(true),
+      log_to_debug_out_(true),
+      append_to_file_(true),
+      logging_shutdown_(false),
+      num_writers_(0),
+      file_log_writer_(NULL),
+      debug_out_writer_(NULL),
+      etw_log_writer_(NULL),
+      is_initializing_(false),
+      log_file_name_(kDefaultLogFileName),
+      config_file_path_(GetConfigurationFilePath()) {
+  g_last_category_check_time = 0;
+  for (int i = 0; i < max_writers; ++i) {
+    writers_[i] = NULL;
+  }
+  proc_name_ = GetProcName();
+  g_logging_valid = true;
+
+  // Read initial settings from the config file.
+  ReadLoggingSettings();
+}
+
+// TODO(omaha): why aren't we using a mutexscope and what if an the code
+// throws? Will the lock be unlocked?
+Logging::~Logging() {
+  // Acquire the lock outside the try/except block so we'll always release it
+  lock_.Lock();
+
+  __try {
+    // prevent further access to the system
+    // necessary because the static destructors happen
+    // in a non-deterministic order
+    logging_shutdown_ = true;
+
+    // Delete all registered LogWriters
+    for (int i = 0; i < num_writers_; ++i) {
+      if (writers_[i]) {
+        delete writers_[i];
+      }
+    }
+
+    logging_initialized_ = false;
+  } __except(SehNoMinidump(GetExceptionCode(),
+                           GetExceptionInformation(),
+                           __FILE__,
+                           __LINE__,
+                           true)) {
+    OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n");
+    logging_initialized_  = false;
+  }
+
+  g_logging_valid = false;
+  lock_.Unlock();
+}
+
+void Logging::UpdateCatAndLevel(const wchar_t* cat_name, LogCategory cat) {
+  if (cat_name == NULL) {
+    return;
+  }
+  if (cat >= LC_MAX_CAT) {
+    return;
+  }
+  int log_level = kDefaultLogLevel;
+  CString config_file = GetCurrentConfigurationFilePath();
+  if (!config_file.IsEmpty()) {
+    log_level = GetPrivateProfileInt(kConfigSectionLoggingLevel,
+                                     cat_name,
+                                     kDefaultLogLevel,
+                                     config_file);
+  }
+  category_list_[cat].enabled = (log_level != 0);
+  category_list_[cat].log_level = static_cast<LogLevel>(log_level);
+}
+
+void Logging::ReadLoggingSettings() {
+  CString config_file = GetCurrentConfigurationFilePath();
+  if (!config_file.IsEmpty()) {
+    logging_enabled_ = ::GetPrivateProfileInt(
+        kConfigSectionLoggingSettings,
+        kConfigAttrEnableLogging,
+        kDefaultLoggingEnabled,
+        config_file) == 0 ? false : true;
+
+    show_time_ = ::GetPrivateProfileInt(
+        kConfigSectionLoggingSettings,
+        kConfigAttrShowTime,
+        kDefaultShowTime,
+        config_file) == 0 ? false : true;
+
+    log_to_file_ = ::GetPrivateProfileInt(
+        kConfigSectionLoggingSettings,
+        kConfigAttrLogToFile,
+        kDefaultLogToFile,
+        config_file) == 0 ? false : true;
+
+    log_to_debug_out_ = ::GetPrivateProfileInt(
+        kConfigSectionLoggingSettings,
+        kConfigAttrLogToOutputDebug,
+        kDefaultLogToOutputDebug,
+        config_file) == 0 ? false : true;
+
+    append_to_file_ = ::GetPrivateProfileInt(
+        kConfigSectionLoggingSettings,
+        kConfigAttrAppendToFile,
+        kDefaultAppendToFile,
+        config_file) == 0 ? false : true;
+
+    ::GetPrivateProfileString(kConfigSectionLoggingSettings,
+                              kConfigAttrLogFilePath,
+                              kDefaultLogFileName,
+                              CStrBuf(log_file_name_, MAX_PATH),
+                              MAX_PATH,
+                              config_file);
+  } else {
+    logging_enabled_ = kDefaultLoggingEnabled;
+    show_time_ = kDefaultShowTime;
+    log_to_file_ = kDefaultLogToFile;
+    log_to_debug_out_ = kDefaultLogToOutputDebug;
+    append_to_file_ = kDefaultAppendToFile;
+    log_file_name_ = kDefaultLogFileName;
+  }
+
+  if (force_show_time_) {
+    show_time_ = true;
+  }
+
+  // The "default" category is always enabled.
+  category_list_[LC_LOGGING].enabled = true;
+  category_list_[LC_LOGGING].log_level = LEVEL_ALL;
+
+  // Read each category from the ini file.
+  for (size_t i = 0; i < arraysize(LogCategoryNames); ++i) {
+    UpdateCatAndLevel(LogCategoryNames[i].category_name,
+                      LogCategoryNames[i].category);
+  }
+
+  g_last_category_check_time = GetCurrent100NSTime();
+}
+
+CString Logging::GetDefaultLogDirectory() const {
+  CString path;
+  CStrBuf buf(path, MAX_PATH);
+  HRESULT hr = ::SHGetFolderPath(NULL,
+                                 CSIDL_COMMON_APPDATA,
+                                 NULL,
+                                 SHGFP_TYPE_CURRENT,
+                                 buf);
+  if (FAILED(hr)) {
+    return L"";
+  }
+  if (!::PathAppend(buf, OMAHA_REL_LOG_DIR)) {
+    return L"";
+  }
+  return path;
+}
+
+CString Logging::GetLogFilePath() const {
+  if (log_file_name_.IsEmpty()) {
+    return CString();
+  }
+
+  if (!ATLPath::IsRelative(log_file_name_)) {
+    return log_file_name_;
+  }
+
+  CString path = GetDefaultLogDirectory();
+  if (path.IsEmpty()) {
+    return CString();
+  }
+
+  if (!::PathAppend(CStrBuf(path, MAX_PATH), log_file_name_)) {
+    return CString();
+  }
+
+  return path;
+}
+
+void Logging::ConfigureETWLogWriter() {
+  // Always create the ETW log writer, as its log level is controlled
+  // at runtime through Event Tracing for Windows.
+  if (etw_log_writer_ == NULL) {
+    etw_log_writer_ = EtwLogWriter::Create();
+    if (debug_out_writer_ == NULL) {
+      OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - "
+                                L"Cannot create ETW log writer",
+                                proc_name_));
+    }
+  }
+
+  if (etw_log_writer_ != NULL) {
+    InternalRegisterWriter(etw_log_writer_);
+  }
+}
+
+void Logging::ConfigureFileLogWriter() {
+  if (!log_to_file_) {
+    return;
+  }
+
+  // Create the logging file.
+  if (file_log_writer_ == NULL) {
+    CString path = GetLogFilePath();
+    if (path.IsEmpty()) {
+      return;
+    }
+
+    // Extract the final target directory which will not be what
+    // GetDefaultLogDirectory() returns if log_file_name_ is an absolute path.
+    CString log_file_dir = GetDirectoryFromPath(path);
+    if (!File::Exists(log_file_dir)) {
+      if (FAILED(CreateDir(log_file_dir, NULL))) {
+        return;
+      }
+    }
+    file_log_writer_ = FileLogWriter::Create(path, append_to_file_);
+    if (file_log_writer_ == NULL) {
+      OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - "
+                                L"Cannot create log writer to %s",
+                                proc_name_, path));
+    }
+  }
+
+  if (file_log_writer_ != NULL) {
+    InternalRegisterWriter(file_log_writer_);
+  }
+}
+
+void Logging::ConfigureDebugOutLogWriter() {
+  if (!log_to_debug_out_) {
+    return;
+  }
+
+  if (debug_out_writer_ == NULL) {
+    debug_out_writer_ = OutputDebugStringLogWriter::Create();
+    if (debug_out_writer_ == NULL) {
+      OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - "
+                                L"Cannot create OutputDebugString log writer",
+                                proc_name_));
+    }
+  }
+
+  if (debug_out_writer_ != NULL) {
+    InternalRegisterWriter(debug_out_writer_);
+  }
+}
+
+// Configures/unconfigures the log writers for the current settings.
+bool Logging::ConfigureLogging() {
+  ConfigureETWLogWriter();
+  ConfigureFileLogWriter();
+  ConfigureDebugOutLogWriter();
+
+  return num_writers_ > 0;
+}
+
+void Logging::UnconfigureLogging() {
+  if (etw_log_writer_ != NULL) {
+    InternalUnregisterWriter(etw_log_writer_);
+  }
+  if (file_log_writer_ != NULL) {
+    InternalUnregisterWriter(file_log_writer_);
+  }
+  if (debug_out_writer_ != NULL) {
+    InternalUnregisterWriter(debug_out_writer_);
+  }
+}
+
+bool Logging::InternalInitialize() {
+  __try {
+    if (logging_shutdown_ == true) {
+      OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - "
+                                L"Calling the logging system after "
+                                L"it has been shut down \n",
+                                GetProcName()));
+      return false;
+    }
+
+    if (logging_initialized_ == true) {
+      return true;
+    }
+    // If something called by this method is attempting to do logging,
+    // just ignore it. The cost/benefit ratio is too high to do otherwise.
+    if (is_initializing_) {
+      return false;
+    }
+    is_initializing_ = true;
+
+    // Read the initial settings from the config file.
+    ReadLoggingSettings();
+
+    // Initialize logging system if enabled at start.
+    if (logging_enabled_) {
+      logging_initialized_ = ConfigureLogging();
+    }
+  } __except(SehNoMinidump(GetExceptionCode(),
+                           GetExceptionInformation(),
+                           __FILE__,
+                           __LINE__,
+                           true)) {
+    OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n");
+    logging_initialized_  = false;
+    return false;
+  }
+
+  is_initializing_ = false;
+  return true;
+}
+
+bool Logging::InitializeLogging() {
+  // Double-checked locking idiom is broken, especially on multicore machines.
+  // TODO(omaha): understand how this works and fix it.
+  if (logging_shutdown_ == true) {
+    OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - Calling the logging "
+                              L"system after it has been shut down \n",
+                              GetProcName()));
+    return false;
+  }
+
+  if (logging_initialized_ == true) {
+    return true;
+  }
+
+  // Acquire the lock outside the try/except block so we'll always release it.
+  __mutexScope(lock_);
+  return InternalInitialize();
+}
+
+// Enables/disables the logging mechanism. Allows turning logging on/off
+// in mid-run.
+// TODO(omaha):  same comment as for the destructor.
+void Logging::EnableLogging() {
+  if (!InitializeLogging()) {
+    return;
+  }
+
+  // Acquire the lock outside the try/except block so we'll always release it.
+  lock_.Lock();
+
+  __try {
+    if (!logging_enabled_) {
+      ConfigureLogging();
+      logging_enabled_ = true;
+    }
+  } __except(SehNoMinidump(GetExceptionCode(),
+                           GetExceptionInformation(),
+                           __FILE__,
+                           __LINE__,
+                           true)) {
+    OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n");
+    logging_enabled_  = false;
+  }
+
+  lock_.Unlock();
+}
+
+void Logging::DisableLogging() {
+  if (!InitializeLogging()) {
+    return;
+  }
+
+  // Acquire the lock outside the try/except block so we'll always release it.
+  lock_.Lock();
+
+  __try {
+    if (logging_enabled_) {
+      logging_enabled_ = false;
+      UnconfigureLogging();
+    }
+  } __except(SehNoMinidump(GetExceptionCode(),
+             GetExceptionInformation(),
+             __FILE__,
+             __LINE__,
+             true)) {
+    OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n");
+    logging_enabled_  = false;
+  }
+
+  lock_.Unlock();
+}
+
+// Checks if logging is enabled - and updates logging settings from the
+// configuration file every kLogSettingsCheckInterval seconds.
+bool Logging::IsLoggingEnabled() {
+  if (!InitializeLogging()) {
+    return false;
+  }
+
+  // Dynamic update - including reading a new value of logging_enabled_.
+  bool prev_logging_enabled = logging_enabled_;
+  if (GetCurrent100NSTime() >
+      g_last_category_check_time + kLogSettingsCheckInterval) {
+    ReadLoggingSettings();
+  }
+
+  // If enabled state has changed either enable or disable logging.
+  if (prev_logging_enabled != logging_enabled_) {
+    if (logging_enabled_) {
+      EnableLogging();
+    } else {
+      DisableLogging();
+    }
+  }
+
+  return logging_enabled_;
+}
+
+bool Logging::IsLoggingAlreadyEnabled() const {
+  return logging_enabled_;
+}
+
+void Logging::ForceShowTimestamp(bool force_show_time) {
+  force_show_time_ = show_time_ = force_show_time;
+}
+
+// Get category level
+LogLevel Logging::GetCatLevel(LogCategory category) const {
+  if (!IsLoggingAlreadyEnabled()) {
+    return kDefaultLogLevel;
+  }
+
+  if (category >= LC_MAX_CAT) {
+    return kDefaultLogLevel;
+  }
+
+  return category_list_[category].log_level;
+}
+
+// Check if logging is enabled for a given category and level
+DWORD Logging::IsCatLevelEnabled(LogCategory category, LogLevel level) {
+  if (!IsLoggingEnabled()) {
+    return 0;
+  }
+
+  if (category >= LC_MAX_CAT) {
+    return 0;
+  }
+
+  // If the config value is to log: then log to all writers.
+  if (category_list_[category].enabled &&
+      level <= category_list_[category].log_level) {
+    return static_cast<DWORD>(all_writers_mask);
+  }
+
+  // Check each of the registered loggers to see if they want to override the
+  // negative config value.
+  DWORD mask = 0;
+  for (int i = num_writers_ - 1; i >= 0; --i) {
+    mask <<= 1;
+    if (writers_[i] && writers_[i]->IsCatLevelEnabled(category, level)) {
+      mask |= 1;
+    }
+  }
+
+  return mask;
+}
+
+// TODO(omaha): For now this is hard coded and there is no way to override
+// writing other log categories into the history. Add a store_in_history
+// boolean into the CategoryInfo struct that allows reading from the config
+// file. This will enable other log categories to get buffered in the history.
+bool Logging::IsCategoryEnabledForBuffering(LogCategory cat) {
+  return cat == LC_REPORT;
+}
+
+void Logging::LogMessage(LogCategory cat, LogLevel level,
+                         const wchar_t* fmt, ...) {
+  va_list args;
+  va_start(args, fmt);
+  LogMessageVA(cat, level, fmt, args);
+  va_end(args);
+}
+
+void Logging::LogMessageVA(LogCategory cat, LogLevel level,
+                           const wchar_t* fmt, va_list args) {
+  LogMessageMaskedVA(static_cast<DWORD>(all_writers_mask),
+                     cat,
+                     level,
+                     fmt,
+                     args);
+}
+
+void Logging::InternalLogMessageMaskedVA(DWORD writer_mask,
+                                         LogCategory cat,
+                                         LogLevel level,
+                                         CString* log_buffer,
+                                         CString* prefix,
+                                         const wchar_t* fmt,
+                                         va_list args) {
+  __try {
+    // Initial buffer size in characters.
+    // It will adjust dynamically if the message is bigger.
+    DWORD buffer_size = 512;
+
+    // Count of chars / bytes written.
+    int num_chars = 0;
+    bool result = false;
+
+    // Write the message in the buffer.
+    // Dynamically adjust the size to hold the entire message.
+
+    while ((num_chars = _vsnwprintf_s(
+        log_buffer->GetBufferSetLength(buffer_size),
+        buffer_size,
+        _TRUNCATE,
+        fmt,
+        args)) == -1) {
+      // Truncate if the message is too big.
+      if (buffer_size >= kMaxLogMessageSize) {
+        num_chars = buffer_size;
+        break;
+      }
+
+      // Get a buffer that is big enough.
+      buffer_size *= 2;
+    }
+
+    log_buffer->ReleaseBuffer(num_chars);
+
+    FormatLinePrefix(show_time_, proc_name_, *prefix);
+
+    // Log the message.
+    OutputInfo info(cat, level, *prefix, *log_buffer);
+    OutputMessage(writer_mask, &info);
+  } __except(SehSendMinidump(GetExceptionCode(),
+                             GetExceptionInformation(),
+                             kMinsTo100ns)) {
+    OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n");
+    OutputDebugString(fmt);
+    OutputDebugString(L"\n\r");
+  }
+}
+
+void Logging::LogMessageMaskedVA(DWORD writer_mask,
+                                 LogCategory cat,
+                                 LogLevel level,
+                                 const wchar_t* fmt,
+                                 va_list args) {
+  if (!fmt) {
+    return;
+  }
+
+  if (writer_mask == 0 && level > kMaxLevelToStoreInLogHistory) {
+    return;
+  }
+
+  CString log_buffer;    // The buffer for formatted log messages.
+  CString prefix;
+
+  int i = 0;
+  while (++i <= kNumLockRetries) {
+    if (lock_.Lock(0)) {
+      InternalLogMessageMaskedVA(writer_mask, cat, level, &log_buffer,
+                                 &prefix, fmt, args);
+      lock_.Unlock();
+      break;
+    }
+
+    Sleep(kLockRetryDelayMs);
+  }
+
+  if (i > kNumLockRetries) {
+    OutputDebugStringA("LOG_SYSTEM: Couldn't acquire lock - ");
+    OutputDebugString(fmt);
+    OutputDebugString(L"\n\r");
+  }
+}
+
+void Logging::OutputMessage(DWORD writer_mask, LogCategory cat, LogLevel level,
+                            const wchar_t* msg1, const wchar_t* msg2) {
+  OutputInfo info(cat, level, msg1, msg2);
+  OutputMessage(writer_mask, &info);
+}
+
+// Store log message in in-memory history buffer.
+void Logging::StoreInHistory(const OutputInfo* output_info) {
+  AppendToHistory(output_info->msg1);
+  AppendToHistory(output_info->msg2);
+  AppendToHistory(L"\r\n");
+}
+
+// Append string to in-memory history buffer.
+// history_buffer_next_idx points to the next index to write at,
+// thus it should always be in (0 - kHistoryBufferEndIdx).
+void Logging::AppendToHistory(const wchar_t* msg) {
+  int msg_len = wcslen(msg);
+  if (msg_len == 0) {
+    return;
+  }
+
+  if (msg_len >= kMaxHistoryBufferSize) {
+    // Write the first kMaxHistoryBufferSize chars.
+    memcpy(history_buffer, msg, kMaxHistoryBufferSize * sizeof(TCHAR));
+    history_buffer_next_idx = 0;
+    history_buffer_full = true;
+    return;
+  }
+
+  // Determine if the message fits into the portion of the buffer after
+  // history_buffer_next_idx.
+  if (msg_len + history_buffer_next_idx < kMaxHistoryBufferSize) {
+    memcpy(history_buffer + history_buffer_next_idx, msg,
+           msg_len * sizeof(TCHAR));
+    history_buffer_next_idx += msg_len;
+    return;
+  }
+
+  // Have to split the input message into the part that fits in
+  // history_buffer_next_idx to kMaxHistoryBufferSize and the remaining message.
+  int msg_first_part_len = kMaxHistoryBufferSize - history_buffer_next_idx;
+  int msg_second_part_len = msg_len - msg_first_part_len;
+  memcpy(history_buffer + history_buffer_next_idx,
+         msg,
+         msg_first_part_len * sizeof(TCHAR));
+
+  history_buffer_full = true;
+  history_buffer_next_idx = msg_second_part_len;
+  if (msg_second_part_len) {
+    memcpy(history_buffer,
+           msg + msg_first_part_len,
+           msg_second_part_len * sizeof(TCHAR));
+  }
+}
+
+// Retrieve in-memory history buffer.
+CString Logging::GetHistory() {
+  CString history;
+
+  if (history_buffer_full) {
+    history.Append(history_buffer + history_buffer_next_idx,
+                   kMaxHistoryBufferSize - history_buffer_next_idx);
+  }
+  history.Append(history_buffer, history_buffer_next_idx);
+
+  // Reset the history buffer to the original state.
+  history_buffer_next_idx = 0;
+  history_buffer_full = false;
+  memset(history_buffer, 0, kMaxHistoryBufferSize * sizeof(TCHAR));
+
+  return history;
+}
+
+void Logging::OutputMessage(DWORD writer_mask,
+                            const OutputInfo* output_info) {
+  if (output_info->level <= kMaxLevelToStoreInLogHistory &&
+      IsCategoryEnabledForBuffering(output_info->category)) {
+    StoreInHistory(output_info);
+  }
+
+  for (int i = 0; i < num_writers_; ++i) {
+    if (writer_mask & 1) {
+      __try {
+        if (logging_enabled_ || writers_[i]->WantsToLogRegardless()) {
+          writers_[i]->OutputMessage(output_info);
+        }
+      }
+      __except(SehNoMinidump(GetExceptionCode(),
+                             GetExceptionInformation(),
+                             __FILE__,
+                             __LINE__,
+                             true)) {
+        // Just eat errors that happen from within the LogWriters.  This is
+        // important so that if such an error happens when OutputMessage is
+        // called from debugASSERT we don't go recursively into more
+        // error handling ...
+      }
+    }
+    writer_mask >>= 1;
+  }
+}
+
+bool Logging::InternalRegisterWriter(LogWriter* log_writer) {
+  if (num_writers_ >= max_writers) {
+    return false;
+  }
+  writers_[num_writers_++] = log_writer;
+  return true;
+}
+
+bool Logging::RegisterWriter(LogWriter* log_writer) {
+  if (!InternalRegisterWriter(log_writer)) {
+    return false;
+  }
+  if (log_writer->WantsToLogRegardless()) {
+    EnableLogging();
+  }
+  return true;
+}
+
+bool Logging::InternalUnregisterWriter(LogWriter* log_writer) {
+  bool result = false;
+  for (int i = 0; i < num_writers_; ++i) {
+    if (writers_[i] == log_writer) {
+      // Replace this entry with last entry in array, then truncate.
+      writers_[i] = writers_[--num_writers_];
+      result = true;
+      break;
+    }
+  }
+  return result;
+}
+
+bool Logging::UnregisterWriter(LogWriter* log_writer) {
+  if (!InternalUnregisterWriter(log_writer)) {
+    return false;
+  }
+  if (num_writers_ == 0) {
+    DisableLogging();
+  }
+  return true;
+}
+
+// The primary configuration file under %PROGRAMFILES%\Google\Update is
+// removed on uninstall. This is midly inconvenient during development
+// therefore a fallback location for the configuration file is desired.
+CString Logging::GetCurrentConfigurationFilePath() const {
+  if (!config_file_path_.IsEmpty() &&
+      File::Exists(config_file_path_)) {
+    return config_file_path_;
+  } else {
+    return L"";
+  }
+}
+
+CString Logging::GetConfigurationFilePath() const {
+  CString file_path;
+  CString system_drive = GetEnvironmentVariableAsString(_T("SystemDrive"));
+  if (!system_drive.IsEmpty()) {
+    file_path = system_drive;
+    file_path += L"\\";
+  }
+  return file_path + kLogConfigFileName;
+}
+
+LogWriter::LogWriter() {
+}
+
+LogWriter::~LogWriter() {
+}
+
+void LogWriter::Cleanup() {}
+
+bool LogWriter::WantsToLogRegardless() const { return false; }
+
+bool LogWriter::IsCatLevelEnabled(LogCategory, LogLevel) const {
+  return false;
+}
+
+void LogWriter::OutputMessage(const OutputInfo*) { }
+
+bool LogWriter::Register() {
+  Logging* logger = GetLogging();
+  if (logger) {
+    return logger->RegisterWriter(this);
+  } else {
+    return false;
+  }
+}
+
+bool LogWriter::Unregister() {
+  Logging* logger = GetLogging();
+  if (logger) {
+    return logger->RegisterWriter(this);
+  } else {
+    return false;
+  }
+}
+
+// FileLogWriter
+
+FileLogWriter* FileLogWriter::Create(const wchar_t* file_name, bool append) {
+  return new FileLogWriter(file_name, append);
+}
+
+FileLogWriter::FileLogWriter(const wchar_t* file_name, bool append)
+    : initialized_(false),
+      valid_(false),
+      file_name_(file_name),
+      log_file_mutex_(NULL),
+      log_file_(NULL),
+      append_(append),
+      max_file_size_(kDefaultMaxLogFileSize),
+      log_file_wide_(kDefaultLogFileWide) {
+  Logging* logger = GetLogging();
+  if (logger) {
+    CString config_file_path = logger->GetCurrentConfigurationFilePath();
+    if (!config_file_path.IsEmpty()) {
+        max_file_size_ = ::GetPrivateProfileInt(
+            kConfigSectionLoggingSettings,
+            kConfigAttrMaxLogFileSize,
+            kDefaultMaxLogFileSize,
+            config_file_path);
+        log_file_wide_ = ::GetPrivateProfileInt(
+            kConfigSectionLoggingSettings,
+            kConfigAttrLogFileWide,
+            kDefaultLogFileWide,
+            config_file_path) == 0 ? false : true;
+    } else {
+      max_file_size_ = kDefaultMaxLogFileSize;
+      log_file_wide_ = kDefaultLogFileWide;
+    }
+    proc_name_ = logger->proc_name();
+  }
+}
+
+FileLogWriter::~FileLogWriter() {
+  // TODO(omaha): Figure out a way to pass the proc_name - and possibly
+  // the show_time var - into here.
+  Logging* logger = GetLogging();
+  if (logger && logger->IsLoggingAlreadyEnabled()) {
+    // OutputInfo info(LEVEL_WARNING, NULL, kEndOfLogMessage);
+    // OutputMessage(&info);
+  }
+  Cleanup();
+}
+
+void FileLogWriter::Initialize() {
+  if (initialized_) {
+    return;
+  }
+
+  initialized_ = true;
+
+  bool already_created = CreateLoggingMutex();
+  if (!log_file_mutex_) {
+    return;
+  }
+
+  if (already_created) {
+    append_ = true;
+  }
+
+  if (!GetMutex()) {
+    ::OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: "
+                                L"Could not acquire logging mutex %s\n",
+                                proc_name_,
+                                log_file_mutex_name_));
+    return;
+  }
+
+  CreateLoggingFile();
+  if (!log_file_) {
+    ::OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: "
+                                L"Could not create logging file %s\n",
+                                proc_name_,
+                                file_name_));
+    valid_ = false;
+  }
+
+  valid_ = true;
+  ReleaseMutex();
+}
+
+void FileLogWriter::Cleanup() {
+  if (log_file_) {
+    ::CloseHandle(log_file_);
+  }
+  if (log_file_mutex_) {
+    ::ReleaseMutex(log_file_mutex_);
+    ::CloseHandle(log_file_mutex_);
+  }
+}
+
+bool FileLogWriter::CreateLoggingMutex() {
+  log_file_mutex_name_ = StripBackslashes(kLoggingMutexName L"_" + file_name_);
+  // TODO(omaha): I don't see where this class is used, but I guess the
+  // caller is always in the same context. We should use the default security
+  // here.  If the caller can be in different contexts (System Service, Usermode
+  // applications, etc), then we should revisit this code to give access only
+  // to those who really need it.  What if a malicious piece a code decide
+  // to get the mutex and lock it? If it happens, everytime you try to log
+  // something, the thread would hang for 500ms and then fail to log the
+  // message.
+  CSecurityDesc sd;
+  GetEveryoneDaclSecurityDescriptor(&sd, GENERIC_ALL, GENERIC_ALL);
+  CSecurityAttributes sa(sd);
+  log_file_mutex_ = CreateMutexWithSyncAccess(log_file_mutex_name_, &sa);
+  if (log_file_mutex_) {
+    return ERROR_ALREADY_EXISTS == ::GetLastError();
+  }
+  return false;
+}
+
+bool FileLogWriter::CreateLoggingFile() {
+  uint32 file_size(0);
+  File::GetFileSizeUnopen(file_name_, &file_size);
+  if (file_size > max_file_size_) {
+    ArchiveLoggingFile();
+  }
+  log_file_ = ::CreateFile(file_name_,
+                           GENERIC_WRITE,
+                           FILE_SHARE_WRITE | FILE_SHARE_READ,
+                           NULL,
+                           append_ ? OPEN_ALWAYS : CREATE_ALWAYS,
+                           FILE_ATTRIBUTE_NORMAL,
+                           NULL);
+  if (log_file_ == INVALID_HANDLE_VALUE) {
+    // The code in this file is written with the assumption that log_file_ is
+    // NULL on creation errors. The easy fix is to set it to NULL here. The
+    // long term fix should be implementing it in terms of a smart handle.
+    log_file_ = NULL;
+    return false;
+  }
+
+  // Allow users to read, write, and delete the log file.
+  ACCESS_MASK mask = GENERIC_READ | GENERIC_WRITE | DELETE;
+  CDacl dacl;
+  if (dacl.AddAllowedAce(ATL::Sids::Users(), mask)) {
+    AtlSetDacl(file_name_, SE_FILE_OBJECT, dacl);
+  }
+
+  // Insert a BOM in the newly created file.
+  if (GetLastError() != ERROR_ALREADY_EXISTS && log_file_wide_) {
+    DWORD num = 0;
+    ::WriteFile(log_file_, &kUnicodeBom, sizeof(kUnicodeBom), &num, NULL);
+  }
+  return true;
+}
+
+bool FileLogWriter::TruncateLoggingFile() {
+  DWORD share_mode = FILE_SHARE_WRITE;
+  HANDLE log_file = ::CreateFile(file_name_,
+                                 GENERIC_WRITE,
+                                 FILE_SHARE_WRITE | FILE_SHARE_READ,
+                                 NULL,
+                                 TRUNCATE_EXISTING,
+                                 FILE_ATTRIBUTE_NORMAL,
+                                 NULL);
+  if (log_file_ == INVALID_HANDLE_VALUE) {
+    return false;
+  }
+
+  // Insert a BOM in the newly created file.
+  if (log_file_wide_) {
+    DWORD num = 0;
+    ::WriteFile(log_file, &kUnicodeBom, sizeof(kUnicodeBom), &num, NULL);
+  }
+  ::CloseHandle(log_file);
+  return true;
+}
+
+bool FileLogWriter::ArchiveLoggingFile() {
+  ::OutputDebugString(L"LOG_SYSTEM: trying to move log file to backup\n");
+  CString backup_file_name = file_name_ + L".bak";
+  HRESULT hr = File::Move(file_name_, backup_file_name, true);
+  if (FAILED(hr)) {
+    ::OutputDebugString(L"LOG_SYSTEM: failed to move log file to backup\n");
+
+    // Trying to move the log file when loggers have it open returns
+    // ERROR_SHARING_VIOLATION. Each call to MoveFileAfterReboot inserts the
+    // file into PendingFileRenames list. Moving files at reboot requires the
+    // user to be either the LocalSystem account or in the Administrators
+    // group.
+    if (!IsArchivePending()) {
+      File::MoveAfterReboot(file_name_, backup_file_name);
+    }
+    return false;
+  }
+  return true;
+}
+
+bool FileLogWriter::IsArchivePending() {
+  // We look at the PendingFileRenameOperations to see if our log file is
+  // pending a rename. The list is a REG_MULTI_SZ, which is a sequence of
+  // null-terminated strings, terminated by an empty string "\0".
+  // The strings have the structure:
+  // \??\file1\0!\??\file2\0\file3\0\0...\0\0. where file1 is to be renamed to
+  // file2 and file3 is to be deleted.
+  // It is valid for the PFR list to include an empty string in the middle
+  // of the sequence.
+  const wchar_t sub_key_name[] = L"SYSTEM\\CurrentControlSet\\Control\\"
+                                 L"Session Manager";
+  HKEY key = NULL;
+  int res = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                            sub_key_name,
+                            0,
+                            KEY_READ,
+                            &key);
+  if (res != ERROR_SUCCESS) {
+    return false;
+  }
+  DWORD bytes = 0;
+  DWORD type = REG_MULTI_SZ;
+  res = ::RegQueryValueEx(key,
+                          L"PendingFileRenameOperations",
+                          0,
+                          &type,
+                          NULL,
+                          &bytes);
+  if (!(res == ERROR_SUCCESS && type == REG_MULTI_SZ)) {
+    return false;
+  }
+  scoped_array<byte> buf(new byte[bytes]);
+  memset(buf.get(), 0, bytes);
+  res = ::RegQueryValueEx(key,
+                          L"PendingFileRenameOperations",
+                          0,
+                          NULL,
+                          buf.get(),
+                          &bytes);
+  if (res != ERROR_SUCCESS) {
+    return false;
+  }
+  const wchar_t* multi_str = reinterpret_cast<const wchar_t*>(buf.get());
+  size_t count = bytes / sizeof(*multi_str);
+  const size_t kMaxRegistryValueLen = 1024 * 1024;  // 1MB
+  if (!(count >= 2 &&
+        count < kMaxRegistryValueLen &&
+        multi_str[count - 2] == L'\0' &&
+        multi_str[count - 1] == L'\0')) {
+    return false;
+  }
+  // The file names in the PFR list are prefixed by \??\.
+  CString file_name = L"\\??\\" + file_name_;
+  return FindFirstInMultiString(multi_str, count, file_name) != -1;
+}
+
+int FileLogWriter::FindFirstInMultiString(const wchar_t* multi_str,
+                                          size_t count,
+                                          const wchar_t* str) {
+  const wchar_t* p = multi_str;
+  size_t i = 0;
+  while (i < count) {
+    p =  multi_str + i;
+    if (lstrcmp(p, str) == 0) {
+      return i;
+    } else {
+      size_t len = lstrlen(p);
+      i += len + 1;
+    }
+  }
+  return -1;
+}
+
+void FileLogWriter::OutputMessage(const OutputInfo* output_info) {
+  if (!initialized_) {
+    Initialize();
+  }
+
+  if (!valid_) {
+    return;
+  }
+
+  // Acquire the mutex.
+  if (!GetMutex()) {
+    return;
+  }
+
+  // Move to end of file.
+  DWORD pos = ::SetFilePointer(log_file_, 0, NULL, FILE_END);
+  int64 stop_gap_file_size = kStopGapLogFileSizeFactor *
+                             static_cast<int64>(max_file_size_);
+  if (pos >= stop_gap_file_size) {
+    if (!TruncateLoggingFile()) {
+      // Logging stops until the log can be archived over since we do not
+      // want to overfill the disk.
+      return;
+    }
+  }
+  pos = ::SetFilePointer(log_file_, 0, NULL, FILE_END);
+
+  // Write the date, followed by a CRLF
+  DWORD written_size = 0;
+  if (output_info->msg1) {
+    if (log_file_wide_) {
+      ::WriteFile(log_file_, output_info->msg1,
+                  lstrlen(output_info->msg1) * sizeof(wchar_t), &written_size,
+                  NULL);
+    } else {
+      CStringA msg(WideToAnsiDirect(output_info->msg1));
+      ::WriteFile(log_file_, msg.GetString(), msg.GetLength(), &written_size,
+                  NULL);
+    }
+  }
+
+  if (output_info->msg2) {
+    if (log_file_wide_) {
+      ::WriteFile(log_file_, output_info->msg2,
+                  lstrlen(output_info->msg2) * sizeof(wchar_t), &written_size,
+                  NULL);
+    } else {
+      CStringA msg(WideToAnsiDirect(output_info->msg2));
+      ::WriteFile(log_file_, msg.GetString(), msg.GetLength(), &written_size,
+                  NULL);
+    }
+  }
+
+  if (log_file_wide_) {
+    ::WriteFile(log_file_, L"\r\n", 2 * sizeof(wchar_t), &written_size, NULL);
+  } else {
+    ::WriteFile(log_file_, "\r\n", 2, &written_size, NULL);
+  }
+
+  ReleaseMutex();
+}
+
+bool FileLogWriter::GetMutex() {
+  if (!log_file_mutex_) {
+    return false;
+  }
+
+  DWORD res = ::WaitForSingleObject(log_file_mutex_, kMaxMutexWaitTimeMs);
+  if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) {
+    ::OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: "
+                                L"Could not acquire logging mutex %s\n",
+                                proc_name_, log_file_mutex_name_));
+    valid_ = false;
+    return false;
+  }
+
+  return true;
+}
+
+void FileLogWriter::ReleaseMutex() {
+  if (log_file_mutex_) {
+    ::ReleaseMutex(log_file_mutex_);
+  }
+}
+
+// OutputDebugStringLogWriter.
+OutputDebugStringLogWriter* OutputDebugStringLogWriter::Create() {
+  return new OutputDebugStringLogWriter();
+}
+
+OutputDebugStringLogWriter::OutputDebugStringLogWriter() {}
+
+OutputDebugStringLogWriter::~OutputDebugStringLogWriter() {
+  Logging* logger = GetLogging();
+  if (logger && logger->IsLoggingAlreadyEnabled()) {
+    // OutputInfo info(LEVEL_WARNING, NULL, kEndOfLogMessage);
+    // OutputMessage(&info);
+  }
+  Cleanup();
+}
+
+void OutputDebugStringLogWriter::OutputMessage(const OutputInfo* output_info) {
+  // Combine everything into one string so that messages coming from
+  // multiple threads don't get interleaved.
+  ::OutputDebugString(SPRINTF(L"%s%s\n", output_info->msg1, output_info->msg2));
+}
+
+// OverrideConfigLogWriter.
+OverrideConfigLogWriter* OverrideConfigLogWriter::Create(LogCategory category,
+    LogLevel level, LogWriter* log_writer, bool force_logging_enabled) {
+  return new OverrideConfigLogWriter(category,
+                                     level,
+                                     log_writer,
+                                     force_logging_enabled);
+}
+
+OverrideConfigLogWriter::OverrideConfigLogWriter(LogCategory category,
+                                                 LogLevel level,
+                                                 LogWriter* log_writer,
+                                                 bool force_logging_enabled)
+    : category_(category),
+      level_(level),
+      log_writer_(log_writer),
+      force_logging_enabled_(force_logging_enabled) {}
+
+void OverrideConfigLogWriter::Cleanup() {
+  if (log_writer_) {
+    delete log_writer_;
+  }
+}
+
+bool OverrideConfigLogWriter::WantsToLogRegardless() const {
+  return force_logging_enabled_;
+}
+
+bool OverrideConfigLogWriter::IsCatLevelEnabled(LogCategory category,
+                                                LogLevel level) const {
+  if (category != category_) {
+    return false;
+  }
+  if (level > level_) {
+    return false;
+  }
+  return true;
+}
+
+void OverrideConfigLogWriter::OutputMessage(const OutputInfo* output_info) {
+  if (log_writer_) {
+    log_writer_->OutputMessage(output_info);
+  }
+  return;
+}
+
+}  // namespace omaha
+
+#endif  // LOGGING
+
diff --git a/base/logging.h b/base/logging.h
new file mode 100644
index 0000000..e4f0cf1
--- /dev/null
+++ b/base/logging.h
@@ -0,0 +1,528 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// logging.h
+//
+// Tracing and logging system.
+// Allows filtering of the log messages based on logging categories and levels.
+
+#ifndef OMAHA_BASE_LOGGING_H_
+#define OMAHA_BASE_LOGGING_H_
+
+#include "omaha/base/constants.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/time.h"
+
+#ifdef LOGGING
+
+// Logging levels.
+enum LogLevel {
+  LEVEL_FATALERROR = -3,      // crashing fatal error
+  LEVEL_ERROR      = -2,      // errors - recoverable but shouldn't happen
+  LE               = -2,
+  LEVEL_WARNING    = -1,      // warnings
+  LW               = -1,
+  L1               =  1,      // for aprox. 10 logs per run
+  L2,                         // for aprox. 100 logs per run
+  L3,                         // for aprox. 1,000 logs per run
+  L4,                         // for aprox. 10,000 logs per run
+  L5,                         // for aprox. 100,000 logs per run
+  L6,                         // for > 1,000,000 logs per run
+
+  // add above
+  LEVEL_ALL                   // all errors
+};
+
+#endif
+
+namespace omaha {
+
+#define kDefaultLoggingEnabled          1
+#define kLogConfigFileName              MAIN_EXE_BASE_NAME _T(".ini")
+#define kDefaultLogFileName             MAIN_EXE_BASE_NAME _T(".log")
+#define kDefaultLogFileWide             1
+#define kDefaultShowTime                1
+#define kDefaultAppendToFile            1
+
+#ifdef _DEBUG
+#define kDefaultMaxLogFileSize          0xFFFFFFFF  // 4GB
+#define kDefaultLogToFile               1
+#define kDefaultLogToOutputDebug        1
+#define kDefaultLogLevel                L3
+#else
+#define kDefaultMaxLogFileSize          10000000    // 10MB
+#define kDefaultLogToFile               0
+#define kDefaultLogToOutputDebug        0
+#define kDefaultLogLevel                L1
+#endif
+
+// Truncates the log file when the size of the log file is this many
+// times over the MaxLogFileSize to prevent disk overfill.
+#define kStopGapLogFileSizeFactor       10
+
+// config file sections
+#define kConfigSectionLoggingLevel      L"LoggingLevel"
+#define kConfigSectionLoggingSettings   L"LoggingSettings"
+
+// config file attributes
+#define kConfigAttrEnableLogging        L"EnableLogging"
+#define kConfigAttrShowTime             L"ShowTime"
+#define kConfigAttrLogToFile            L"LogToFile"
+#define kConfigAttrLogFilePath          L"LogFilePath"
+#define kConfigAttrLogFileWide          L"LogFileWide"
+#define kConfigAttrLogToOutputDebug     L"LogToOutputDebug"
+#define kConfigAttrAppendToFile         L"AppendToFile"
+#define kConfigAttrMaxLogFileSize       L"MaxLogFileSize"
+
+#define kLoggingMutexName               kLockPrefix L"logging_mutex"
+#define kMaxMutexWaitTimeMs             500
+
+// Does not allow messages bigger than 1 MB.
+#define kMaxLogMessageSize              (1024 * 1024)
+
+#define kLogSettingsCheckInterval       (5 * kSecsTo100ns)
+
+#define kStartOfLogMessage \
+    L"********************* NEW LOG *********************"
+#define kEndOfLogMessage   \
+    L"********************* END LOG *********************"
+
+// TODO(omaha): Allow these defaults to be overriden in the config file.
+#define kMaxLevelToStoreInLogHistory L2
+#define kMaxHistoryBufferSize 1024
+
+#ifdef LOGGING
+
+#define LC_LOG(cat, level, msg) \
+  do {                                                     \
+    omaha::Logging* logger = omaha::GetLogging();          \
+    if (logger) {                                          \
+      omaha::LoggingHelper(logger, cat, level,             \
+        logger->IsCatLevelEnabled(cat, level)) msg;        \
+    }                                                      \
+  } while (0)
+
+#define LC_LOG_OPT(cat, level, msg)   LC_LOG(cat, level, msg)
+
+#else
+#define LC_LOG(cat, level, msg)   ((void)0)
+#endif
+
+#ifdef _DEBUG
+#define LC_LOG_DEBUG(cat, level, msg) LC_LOG(cat, level, msg)
+#else
+#define LC_LOG_DEBUG(cat, level, msg) ((void)0)
+#endif
+
+// Shortcuts for different logging categories - no need to specify the category.
+#define CORE_LOG(x, y)         LC_LOG_DEBUG(omaha::LC_CORE, x, y)
+#define NET_LOG(x, y)          LC_LOG_DEBUG(omaha::LC_NET, x, y)
+#define PLUGIN_LOG(x, y)       LC_LOG_DEBUG(omaha::LC_PLUGIN, x, y)
+#define SERVICE_LOG(x, y)      LC_LOG_DEBUG(omaha::LC_SERVICE, x, y)
+#define SETUP_LOG(x, y)        LC_LOG_DEBUG(omaha::LC_SETUP, x, y)
+#define SHELL_LOG(x, y)        LC_LOG_DEBUG(omaha::LC_SHELL, x, y)
+#define UTIL_LOG(x, y)         LC_LOG_DEBUG(omaha::LC_UTIL, x, y)
+
+#define OPT_LOG(x, y)          LC_LOG_OPT(omaha::LC_OPT, x, y)
+#define REPORT_LOG(x, y)       LC_LOG_OPT(omaha::LC_REPORT, x, y)
+
+#ifdef LOGGING
+
+// Logging components.
+// Maximum 32 categories unless mask is increased to 64 bits.
+enum LogCategory {
+  LC_LOGGING = 0,
+
+  // ADD BELOW - AND REMEMBER:
+  //   - Add a line to the LogCategoryNames table in logging.cc!!!
+  //   - Add to C:\GoogleUpdate.ini.
+
+  LC_UTIL,
+  LC_SETUP,
+  LC_SHELL,
+  LC_CORE,
+  LC_JS,
+  LC_PLUGIN,
+  LC_SERVICE,
+  LC_OPT,
+  LC_NET,
+  LC_REPORT,
+
+  // ADD ABOVE
+
+  LC_MAX_CAT
+};
+
+#define kCatEnabledField      L"Enabled"
+#define kCatLevelField        L"Level"
+
+struct CategoryInfo {
+  bool enabled;
+  LogLevel log_level;
+};
+
+// If you want to log anything else, add it to this structure.  This structure
+// basically says that each logged message is composed of two parts, and they
+// are output one after the other.  Intended to be used for a message "prefix"
+// and the message itself (the prefix can contain the component name, the time,
+// and any other logging system boilerplate, while the message is supplied by
+// the component).  (This basically saves having to copy a variable length -
+// possibly very large - message just to tack it onto the end of the message
+// prefix.)
+struct OutputInfo {
+  LogCategory category;
+  LogLevel level;
+  const wchar_t* msg1;
+  const wchar_t* msg2;
+
+  OutputInfo(LogCategory cat, LogLevel log_level,
+             const wchar_t* m1, const wchar_t* m2)
+      : category(cat),
+        level(log_level),
+        msg1(m1),
+        msg2(m2) {}
+};
+
+// The LogWriter - can decide whether to process message or not, then
+// will process it.  Actually, the message is processed if either a) the
+// individual LogWriter wants to process it or b) it is marked as processable
+// by settings in config ini.
+//
+// Included LogWriters:
+//   OutputDebugStringLogWriter - Logs to OutputDebugString() API
+//   FileLogWriter - Logs to a file
+//   OverrideConfigLogWriter - Overrides the level settings of a
+//     particular category, uses another writer to actually do the writing.
+//     Used, e.g., in installer to force SETUP_LOG messages to go to a file
+//     tr_setup_log.info even if the there is no trconfig.ini file.
+//
+// Not included LogWriters:
+//   StdLogWriter - Logs to stdout or stderr
+//   SubmitToGoogleLogWriter - When done logging submits the log file to
+//     Google's status-receiving server
+class LogWriter {
+ protected:
+  LogWriter();
+  virtual void Cleanup();
+ public:
+  virtual ~LogWriter();
+
+  // Returns true if this Logging object wants to log even if the global
+  // "enable logging" flag is off.  Useful for always creating a log, e.g., an
+  // install log, even without a GoogleUpdate.ini.
+  virtual bool WantsToLogRegardless() const;
+
+  // Returns true if this Logging object wants to handle the message,
+  // regardless of other settings.
+  virtual bool IsCatLevelEnabled(LogCategory category, LogLevel level) const;
+
+  virtual void OutputMessage(const OutputInfo* output_info);
+
+  // Registers and unregisters this LogWriter with the Logging system.  When
+  // registered, the Logging class assumes ownership.
+  bool Register();
+  bool Unregister();
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(LogWriter);
+};
+
+// A LogWriter that writes to a named file.
+class FileLogWriter : public LogWriter {
+ protected:
+  FileLogWriter(const wchar_t* file_name, bool append);
+  ~FileLogWriter();
+  virtual void Cleanup();
+
+ public:
+  static FileLogWriter* Create(const wchar_t* file_name, bool append);
+  virtual void OutputMessage(const OutputInfo* output_info);
+
+ private:
+  void Initialize();
+  bool CreateLoggingMutex();
+  bool CreateLoggingFile();
+  bool ArchiveLoggingFile();
+  bool TruncateLoggingFile();
+  bool GetMutex();
+  void ReleaseMutex();
+
+  // Returns true if archiving of the log file is pending a computer restart.
+  bool IsArchivePending();
+
+  // Returns the first position of str inside of a MULTI_SZ of count characters
+  // including the terminating zeros.
+  static int FindFirstInMultiString(const wchar_t* multi_str,
+                                    size_t count,
+                                    const wchar_t* str);
+
+  uint32 max_file_size_;
+  bool initialized_;
+  bool valid_;
+  bool append_;
+  bool log_file_wide_;
+  CString log_file_mutex_name_;
+  HANDLE log_file_mutex_;
+  CString file_name_;
+  HANDLE log_file_;
+  CString proc_name_;
+
+  friend class FileLogWriterTest;
+
+  DISALLOW_EVIL_CONSTRUCTORS(FileLogWriter);
+};
+
+// A LogWriter that uses OutputDebugString() to write messages.
+class OutputDebugStringLogWriter : public LogWriter {
+ protected:
+  OutputDebugStringLogWriter();
+  ~OutputDebugStringLogWriter();
+ public:
+  static OutputDebugStringLogWriter* Create();
+  virtual void OutputMessage(const OutputInfo* info);
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(OutputDebugStringLogWriter);
+};
+
+// A LogWriter that overrides the settings in trconfig.ini and sends messages
+// to another LogWriter.  Takes ownership of the other LogWriter.
+class OverrideConfigLogWriter : public LogWriter {
+ protected:
+  OverrideConfigLogWriter(LogCategory category, LogLevel level,
+                          LogWriter* log_writer, bool force_logging_enabled);
+  virtual void Cleanup();
+ public:
+  static OverrideConfigLogWriter* Create(LogCategory category, LogLevel level,
+    LogWriter* log_writer, bool force_logging_enabled);
+  virtual bool WantsToLogRegardless() const;
+  virtual bool IsCatLevelEnabled(LogCategory category, LogLevel level) const;
+  virtual void OutputMessage(const OutputInfo* output_info);
+ private:
+  LogCategory category_;
+  LogLevel level_;
+  LogWriter* log_writer_;
+  bool force_logging_enabled_;
+  DISALLOW_EVIL_CONSTRUCTORS(OverrideConfigLogWriter);
+};
+
+// This log writer outputs to Event Tracing for Windows.
+class EtwLogWriter;
+
+// The Logging class - Singleton class
+// Fine-grain logging based on categories and levels.
+// Can log to a file, stdout or debugger.
+class Logging {
+ public:
+  // constructor
+  Logging();
+
+  // destructor
+  ~Logging();
+
+  // Enables/disables the logging mechanism.  Allows turning logging on/off
+  // in mid-run.  Returns true for success (not for 'logging enabled').
+  void EnableLogging();
+  void DisableLogging();
+
+  // Checks if logging is enabled - and updates logging settings from the
+  // configuration file every kLogSettingsCheckInterval seconds
+  bool IsLoggingEnabled();
+
+  // Checks if logging is already enabled. It does not try to enable it.
+  bool IsLoggingAlreadyEnabled() const;
+
+  // Overrides the config file settings for showing the time stamps.
+  void ForceShowTimestamp(bool force_show_time);
+
+  // Checks if logging is enabled for a given category and level.
+  DWORD IsCatLevelEnabled(LogCategory category, LogLevel level);
+  LogLevel GetCatLevel(LogCategory category) const;
+
+  // Logs a message.
+  void LogMessage(LogCategory cat, LogLevel level, const wchar_t* fmt, ...);
+  void LogMessageVA(LogCategory cat, LogLevel level, const wchar_t* fmt,
+                    va_list args);
+
+  // Retrieves the default location of the log directory.
+  CString GetDefaultLogDirectory() const;
+
+  // Computes and returns the complete path of the log file.
+  CString GetLogFilePath() const;
+
+  // Retrieves in-memory history buffer.
+  CString GetHistory();
+
+  // Returns the file path of the current GoogleUpdate.ini.
+  CString GetCurrentConfigurationFilePath() const;
+
+  const CString& proc_name() const { return proc_name_; }
+
+  bool IsCategoryEnabledForBuffering(LogCategory cat);
+ private:
+  bool InternalInitialize();
+  void InternalLogMessageMaskedVA(DWORD writer_mask,
+                                  LogCategory cat,
+                                  LogLevel level,
+                                  CString* log_buffer,
+                                  CString* prefix,
+                                  const wchar_t* fmt,
+                                  va_list args);
+
+  friend class LoggingHelper;
+  void LogMessageMaskedVA(DWORD writer_mask, LogCategory cat, LogLevel level,
+                          const wchar_t* fmt, va_list args);
+
+  // Stores log message in in-memory history buffer.
+  void StoreInHistory(const OutputInfo* output_info);
+
+  // Appends string to in-memory history buffer.
+  void AppendToHistory(const wchar_t* msg);
+
+  // Initializes the logging engine. Harmless to call multiple times.
+  bool InitializeLogging();
+
+  // Configures/unconfigures the log writers for the current settings.  That
+  // is, given the current settings from GoogleUpdate.ini, either initializes
+  // and registers the file-out and debug-out logwriters, or unregisters them.
+  void ConfigureETWLogWriter();
+  void ConfigureFileLogWriter();
+  void ConfigureDebugOutLogWriter();
+  bool ConfigureLogging();
+  void UnconfigureLogging();
+
+  void UpdateCatAndLevel(const wchar_t* cat_name, LogCategory cat);
+  void ReadLoggingSettings();
+
+  // Returns the primary file path of the GoogleUpdate.ini.
+  CString GetConfigurationFilePath() const;
+
+  // Returns the alternate file path of the GoogleUpdate.ini.
+  CString GetAltConfigurationFilePath() const;
+
+ public:
+
+  // Passes the messages along to other OutputMessage()
+  void OutputMessage(DWORD writer_mask, LogCategory cat, LogLevel level,
+                     const wchar_t* msg1, const wchar_t* msg2);
+
+  // Broadcasts the message to each LogWriter.
+  // It should be private but the function we want to be able to use this,
+  // debugASSERT is extern "C" and thus can't be declared a friend of
+  // Logging.
+  void OutputMessage(DWORD writer_mask, const OutputInfo* output_info);
+
+ private:
+
+  CategoryInfo category_list_[LC_MAX_CAT];
+
+  // Checks if logging is initialized.
+  bool logging_initialized_;
+
+  // Is logging in the process of initializing?
+  bool is_initializing_;
+
+  // The logging process name including the calling module.
+  CString proc_name_;
+
+  // Serializes changing logging init/uninit/enable/disable status.
+  LLock lock_;
+
+  // Bunch of settings from the config .ini file.
+  bool logging_enabled_;     // Checks if logging is enabled.
+  bool force_show_time_;
+  bool show_time_;
+  bool log_to_file_;
+  CString log_file_name_;
+  bool log_to_debug_out_;
+  bool append_to_file_;
+
+  // Signals the logging system is shutting down.
+  bool logging_shutdown_;
+
+  // Checkpoint time for dynamic category updates.
+  time64 g_last_category_check_time;
+
+  // The file path of the optional ini file which defines the logging
+  // configuration.
+  CString config_file_path_;
+
+ private:
+  bool InternalRegisterWriter(LogWriter* log_writer);
+  bool InternalUnregisterWriter(LogWriter* log_writer);
+
+ public:
+  bool RegisterWriter(LogWriter* log_writer);
+  bool UnregisterWriter(LogWriter* log_writer);
+  enum { all_writers_mask = -1 };
+
+ private:
+  enum { max_writers = 15 };
+  int num_writers_;
+  LogWriter* writers_[max_writers];
+
+  LogWriter* file_log_writer_;
+  LogWriter* debug_out_writer_;
+  LogWriter* etw_log_writer_;
+
+  friend class HistoryTest;
+
+  DISALLOW_EVIL_CONSTRUCTORS(Logging);
+};
+
+// In order to make the logging macro LC_LOG work out we need to pass a
+// parameter (the mask of loggers to write to) (*) to the actual logging
+// method. However, the last parameter to the macro LC_LOG has its own
+// parenthesis - it encloses multiple expressions (a format string and
+// arguments). So this function object is used as an intermediary in order to
+// hold the writer mask.
+//
+// (*) The mask needs to be transferred separately because we want to keep the
+// LC_LOG structure of asking if the message is going to be logged before
+// evaluating the arguments, and we can't store it in the singleton Logging
+// object - wouldn't be thread-safe.
+
+class LoggingHelper {
+ public:
+  LoggingHelper(Logging* logger, LogCategory cat,
+                LogLevel level, DWORD writer_mask)
+      : logger_(logger),
+        category_(cat),
+        level_(level),
+        writer_mask_(writer_mask) {}
+
+  void operator()(const wchar_t* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+    logger_->LogMessageMaskedVA(writer_mask_, category_, level_, fmt, args);
+    va_end(args);
+  }
+
+ private:
+  Logging* logger_;
+  DWORD writer_mask_;
+  LogLevel level_;
+  LogCategory category_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(LoggingHelper);
+};
+
+// Getter for the Logging singleton class.
+Logging* GetLogging();
+
+#endif  // LOGGING
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_LOGGING_H_
diff --git a/base/logging/build.scons b/base/logging/build.scons
new file mode 100644
index 0000000..106cb01
--- /dev/null
+++ b/base/logging/build.scons
@@ -0,0 +1,34 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========================================================================
+
+
+Import('env')
+
+#
+# Build the googleclient port of the logging module.
+#
+logging_env = env.Clone()
+
+logging_inputs = [
+    'logging.cc',
+    ]
+
+logging_env.ComponentStaticLibrary(
+    lib_name='logging',
+    source=logging_inputs,
+)
+
diff --git a/base/logging/logging.cc b/base/logging/logging.cc
new file mode 100644
index 0000000..04b4cf0
--- /dev/null
+++ b/base/logging/logging.cc
@@ -0,0 +1,348 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// This is code that defines the backend for the new LogMessage definition
+// and the functions that the client application can call to control logging.
+
+#include <ctime>
+#include <iomanip>
+#include <cstring>
+#include <windows.h>
+#include <tchar.h>
+#include <algorithm>
+#include "omaha/base/logging/logging.h"
+
+namespace logging {
+
+const char* const log_severity_names[LOG_NUM_SEVERITIES] = {
+  "INFO", "WARNING", "ERROR", "FATAL" };
+
+int min_log_level = 0;
+LogLockingState lock_log_file = LOCK_LOG_FILE;
+LoggingDestination logging_destination = LOG_ONLY_TO_FILE;
+
+// which log file to use? This is initialized by InitLogging or
+// will be lazily initialized to the default value when it is
+// first needed.
+TCHAR log_file_name[MAX_PATH] = { 0 };
+
+// this file is lazily opened and the handle may be NULL
+HANDLE log_file = NULL;
+
+// what should be prepended to each message?
+bool log_process_id = false;
+bool log_thread_id = false;
+bool log_timestamp = true;
+bool log_tickcount = false;
+
+// An assert handler override specified by the client to be called instead of
+// the debug message dialog.
+LogAssertHandlerFunction log_assert_handler = NULL;
+
+// The critical section is used if log file locking is false. It helps us
+// avoid problems with multiple threads writing to the log file at the same
+// time.
+bool initialized_critical_section = false;
+CRITICAL_SECTION log_critical_section;
+
+// When we don't use a critical section, we are using a global mutex. We
+// need to do this because LockFileEx is not thread safe
+HANDLE log_mutex = NULL;
+
+void InitLogMutex() {
+  if (!log_mutex) {
+    // \ is not a legal character in mutex names so we replace \ with /
+    std::wstring safe_name(log_file_name);
+    std::replace(safe_name.begin(), safe_name.end(), '\\', '/');
+    std::wstring t(L"Global\\");
+    t.append(safe_name);
+    log_mutex = ::CreateMutex(NULL, FALSE, t.c_str());
+  }
+}
+
+void InitLogging(const TCHAR* new_log_file, LoggingDestination logging_dest,
+                 LogLockingState lock_log, OldFileDeletionState delete_old) {
+  if (log_file) {
+    // calling InitLogging twice or after some log call has already opened the
+    // default log file will re-initialize to the new options
+    CloseHandle(log_file);
+    log_file = NULL;
+  }
+
+  lock_log_file = lock_log;
+  logging_destination = logging_dest;
+
+  // ignore file options if logging is only to system
+  if (logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG)
+    return;
+
+  _tcsncpy(log_file_name, new_log_file, MAX_PATH);
+  log_file_name[MAX_PATH - 1] = _T('\0');
+  if (delete_old == DELETE_OLD_LOG_FILE)
+    DeleteFile(log_file_name);
+
+  if (lock_log_file == LOCK_LOG_FILE) {
+    InitLogMutex();
+  } else if (!initialized_critical_section) {
+    // initialize the critical section
+    InitializeCriticalSection(&log_critical_section);
+    initialized_critical_section = true;
+  }
+}
+
+void SetMinLogLevel(int level) {
+  min_log_level = level;
+}
+
+void SetLogItems(bool enable_process_id, bool enable_thread_id,
+                 bool enable_timestamp, bool enable_tickcount) {
+  log_process_id = enable_process_id;
+  log_thread_id = enable_thread_id;
+  log_timestamp = enable_timestamp;
+  log_tickcount = enable_tickcount;
+}
+
+void SetLogAssertHandler(LogAssertHandlerFunction handler) {
+  log_assert_handler = handler;
+}
+
+// Called by logging functions to ensure that debug_file is initialized
+// and can be used for writing. Returns false if the file could not be
+// initialized. debug_file will be NULL in this case.
+bool VerifyLogFileHandle() {
+  if (log_file)
+    return true;
+
+  if (!log_file_name[0]) {
+    // nobody has called InitLogging to specify a debug log file, so here we
+    // initialize the log file name to the default
+    GetModuleFileName(NULL, log_file_name, MAX_PATH);
+    TCHAR* last_backslash = _tcsrchr(log_file_name, '\\');
+    if (last_backslash)
+      last_backslash[1] = 0; // name now ends with the backslash
+    _tcscat(log_file_name, _T("debug.log"));
+  }
+
+  log_file = CreateFile(log_file_name, GENERIC_WRITE,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                        OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+  if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) {
+    log_file = NULL;
+    return false;
+  }
+  SetFilePointer(log_file, 0, 0, FILE_END);
+  return true;
+}
+
+// Displays a message box to the user with the error message in it. For
+// Windows programs, it's possible that the message loop is messed up on
+// a fatal error, and creating a MessageBox will cause that message loop
+// to be run. Instead, we try to spawn another process that displays its
+// command line. We look for "Debug Message.exe" in the same directory as
+// the application. If it exists, we use it, otherwise, we use a regular
+// message box.
+void DisplayDebugMessage(const std::string& str) {
+  if (str.empty())
+    return;
+
+  // look for the debug dialog program next to our application
+  wchar_t prog_name[MAX_PATH];
+  GetModuleFileNameW(NULL, prog_name, MAX_PATH);
+  wchar_t* backslash = wcsrchr(prog_name, '\\');
+  if (backslash)
+    backslash[1] = 0;
+  wcsncat(prog_name, L"DebugMessage.exe", MAX_PATH);
+  prog_name[MAX_PATH - 1] = L'\0';
+
+  // stupid CreateProcess requires a non-const command line and may modify it.
+  // We also want to use the wide string
+  int charcount = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
+  if (!charcount)
+    return;
+  scoped_array<wchar_t> cmdline(new wchar_t[charcount]);
+  if (!MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, cmdline.get(),
+      charcount))
+    return;
+
+  STARTUPINFO startup_info;
+  memset(&startup_info, 0, sizeof(startup_info));
+  startup_info.cb = sizeof(startup_info);
+
+  PROCESS_INFORMATION process_info;
+  if (CreateProcessW(prog_name, cmdline.get(), NULL, NULL, false, 0, NULL,
+                     NULL, &startup_info, &process_info)) {
+    WaitForSingleObject(process_info.hProcess, INFINITE);
+    CloseHandle(process_info.hThread);
+    CloseHandle(process_info.hProcess);
+  } else {
+    // debug process broken, let's just do a message box
+    MessageBoxW(NULL, cmdline.get(), L"Fatal error", MB_OK | MB_ICONHAND);
+  }
+}
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity, int)
+    : severity_(severity) {
+  Init(file, line);
+}
+
+LogMessage::LogMessage(const char* file, int line, const CheckOpString& result)
+    : severity_(LOG_FATAL) {
+  Init(file, line);
+  stream_ << "Check failed: " << (*result.str_);
+}
+
+LogMessage::LogMessage(const char* file, int line)
+     : severity_(LOG_INFO) {
+  Init(file, line);
+}
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity)
+    : severity_(severity) {
+  Init(file, line);
+}
+
+// writes the common header info to the stream
+void LogMessage::Init(const char* file, int line) {
+  // log only the filename
+  const char* last_slash = strrchr(file, '\\');
+  if (last_slash)
+    file = last_slash + 1;
+
+  // TODO(omaha): It might be nice if the columns were fixed width.
+
+  stream_ <<  '[';
+  if (log_process_id)
+    stream_ << GetCurrentProcessId() << ':';
+  if (log_thread_id)
+    stream_ << GetCurrentThreadId() << ':';
+  if (log_timestamp) {
+    time_t t = time(NULL);
+#if _MSC_VER >= 1400
+    struct tm local_time = {0};
+    localtime_s(&local_time, &t);
+    struct tm* tm_time = &local_time;
+#else
+    struct tm* tm_time = localtime(&t);
+#endif
+    stream_ << std::setfill('0')
+            << std::setw(2) << 1 + tm_time->tm_mon
+            << std::setw(2) << tm_time->tm_mday
+            << '/'
+            << std::setw(2) << tm_time->tm_hour
+            << std::setw(2) << tm_time->tm_min
+            << std::setw(2) << tm_time->tm_sec
+            << ':';
+  }
+  if (log_tickcount)
+    stream_ << GetTickCount() << ':';
+  stream_ << log_severity_names[severity_] << ":" << file << "(" << line
+      << ")] ";
+}
+
+LogMessage::~LogMessage() {
+  // TODO(omaha) modify the macros so that nothing is executed when the log
+  // level is too high or there is
+  if (severity_ < min_log_level)
+    return;
+
+  std::string str_newline(stream_.str(), stream_.pcount());
+  str_newline.append("\r\n");
+  if (logging_destination != LOG_ONLY_TO_FILE)
+    OutputDebugStringA(str_newline.c_str());
+
+  // write to log file
+  if (logging_destination != LOG_ONLY_TO_SYSTEM_DEBUG_LOG &&
+      VerifyLogFileHandle()) {
+    // we can have multiple threads and/or processes, so try to prevent them
+    // from clobbering each other's writes
+    if (lock_log_file == LOCK_LOG_FILE) {
+      // Ensure that the mutex is initialized in case the client app did not
+      // call InitLogging. This is not thread safe. See below
+      InitLogMutex();
+
+      DWORD r = ::WaitForSingleObject(log_mutex, INFINITE);
+      DCHECK(r != WAIT_ABANDONED);
+    } else {
+      // use the critical section
+      if (!initialized_critical_section) {
+        // The client app did not call InitLogging, and so the critical section
+        // has not been created. We do this on demand, but if two threads try to
+        // do this at the same time, there will be a race condition to create
+        // the critical section. This is why InitLogging should be called from
+        // the main thread at the beginning of execution.
+        InitializeCriticalSection(&log_critical_section);
+        initialized_critical_section = true;
+      }
+      EnterCriticalSection(&log_critical_section);
+    }
+
+    SetFilePointer(log_file, 0, 0, SEEK_END);
+    DWORD num_written;
+    WriteFile(log_file, (void*)str_newline.c_str(), (DWORD)str_newline.length(),
+              &num_written, NULL);
+
+    if (lock_log_file == LOCK_LOG_FILE) {
+      ReleaseMutex(log_mutex);
+    } else {
+      LeaveCriticalSection(&log_critical_section);
+    }
+  }
+
+  if (severity_ == LOG_FATAL) {
+    // display a message or break into the debugger on a fatal error
+    if (::IsDebuggerPresent()) {
+      DebugBreak();
+    } else {
+      if (log_assert_handler) {
+        log_assert_handler(std::string(stream_.str(), stream_.pcount()));
+      } else {
+        // don't use the string with the newline, get a fresh version to send to
+        // the debug message process
+        DisplayDebugMessage(std::string(stream_.str(), stream_.pcount()));
+        TerminateProcess(GetCurrentProcess(), 1);
+      }
+    }
+  }
+
+  // Calling stream_.str() freezes the stream buffer.  A frozen buffer will
+  // not be freed during strstreambuf destruction.
+  stream_.freeze(false);
+}
+
+void CloseLogFile() {
+  if (!log_file)
+    return;
+
+  CloseHandle(log_file);
+  log_file = NULL;
+}
+
+} // namespace logging
+
+std::ostream& operator<<(std::ostream& out, const wchar_t* wstr) {
+  if (!wstr || !wstr[0])
+    return out;
+
+  // compute the length of the buffer we'll need
+  int charcount = WideCharToMultiByte(CP_UTF8, 0, wstr, -1,
+                                      NULL, 0, NULL, NULL);
+  if (charcount == 0)
+    return out;
+
+  // convert
+  scoped_array<char> buf(new char[charcount]);
+  WideCharToMultiByte(CP_UTF8, 0, wstr, -1, buf.get(), charcount, NULL, NULL);
+  return out << buf.get();
+}
diff --git a/common/logging/logging.h b/base/logging/logging.h
similarity index 100%
rename from common/logging/logging.h
rename to base/logging/logging.h
diff --git a/base/logging_unittest.cc b/base/logging_unittest.cc
new file mode 100644
index 0000000..5b5156e
--- /dev/null
+++ b/base/logging_unittest.cc
@@ -0,0 +1,268 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "base/basictypes.h"
+#include "omaha/base/logging.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(LoggingTest, Logging) {
+#ifdef _DEBUG
+  OPT_LOG(L1, (_T("[OPT_LOG from debug build.]")));
+#else
+  OPT_LOG(L1, (_T("[OPT_LOG from optimized build.]")));
+#endif
+}
+
+class FileLogWriterTest : public testing::Test {
+ public:
+
+  int FindFirstInMultiString(const TCHAR* multi_str,
+                             size_t count,
+                             const TCHAR* str) {
+    return FileLogWriter::FindFirstInMultiString(multi_str, count, str);
+  }
+};
+
+class HistoryTest : public testing::Test {
+ protected:
+  HistoryTest() {
+    logging_ = GetLogging();
+  }
+
+  void AppendToHistory(const wchar_t* msg) {
+    logging_->AppendToHistory(msg);
+  }
+
+  CString GetHistory() {
+    return logging_->GetHistory();
+  }
+
+ private:
+  Logging* logging_;
+};
+
+#define EOS _T("")
+TEST_F(FileLogWriterTest, FindInMultiString) {
+  // One string of one char.
+  const TCHAR s1[] = _T("a\0") EOS;
+  EXPECT_EQ(FindFirstInMultiString(s1, arraysize(s1), _T("a")), 0);
+
+  // One string.
+  const TCHAR s2[] = _T("abc\0") EOS;
+  EXPECT_EQ(FindFirstInMultiString(s2, arraysize(s2), _T("abc")), 0);
+
+  // Two strings of one char.
+  const TCHAR s3[] = _T("a\0b\0") EOS;
+  EXPECT_EQ(FindFirstInMultiString(s3, arraysize(s3), _T("b")), 2);
+
+  // Two strings.
+  const TCHAR s4[] = _T("ab\0cde\0") EOS;
+  EXPECT_EQ(FindFirstInMultiString(s4, arraysize(s4), _T("cde")), 3);
+
+  // Three strings one char.
+  const TCHAR s5[] = _T("a\0b\0c\0") EOS;
+  EXPECT_EQ(FindFirstInMultiString(s5, arraysize(s5), _T("c")), 4);
+
+  // Many strings.
+  const TCHAR s6[] = _T("a\0bcd\0efgh\0") EOS;
+  EXPECT_EQ(FindFirstInMultiString(s6, arraysize(s6), _T("efgh")), 6);
+
+  // Many strings including empty string.
+  const TCHAR s7[] = _T("a\0\0bc\0\0de\0fg") EOS;
+  EXPECT_EQ(FindFirstInMultiString(s7, arraysize(s7), _T("fg")), 10);
+
+  // Many strings, empty string at the end, negative test.
+  const TCHAR s8[] = _T("a\0bcd\0efgh\0\0") EOS;
+  EXPECT_EQ(FindFirstInMultiString(s8, arraysize(s8), _T("foo")), -1);
+
+  // Another negative test.
+  const TCHAR s9[] = _T("a\0bcd\0\0\0efgh\0") EOS;
+  EXPECT_EQ(FindFirstInMultiString(s9, arraysize(s9), _T("foo")), -1);
+
+  // Empty string is always found.
+  const TCHAR s10[] = _T("\0") EOS;
+  EXPECT_EQ(FindFirstInMultiString(s10, arraysize(s10), _T("\0")), 0);
+
+  const TCHAR s11[] = _T("\0") EOS;
+  EXPECT_EQ(FindFirstInMultiString(s11, arraysize(s11), _T("a")), -1);
+}
+
+TEST_F(HistoryTest, GetHistory) {
+  EXPECT_TRUE(GetHistory().IsEmpty());
+
+  const TCHAR msg1[] = _T("Hello");
+  AppendToHistory(msg1);
+  EXPECT_STREQ(msg1, GetHistory());
+  EXPECT_TRUE(GetHistory().IsEmpty());
+}
+
+TEST_F(HistoryTest, AppendToHistoryTest) {
+  // Test one character.
+  const TCHAR msg1[] = _T("A");
+  AppendToHistory(msg1);
+  EXPECT_STREQ(msg1, GetHistory());
+
+  // Test small string.
+  const TCHAR msg2[] = _T("ABCD");
+  AppendToHistory(msg2);
+  EXPECT_STREQ(msg2, GetHistory());
+
+  // Test one string that fills the buffer.
+  TCHAR msg3[kMaxHistoryBufferSize + 1] = {0};
+  for (int i = 0; i <= kMaxHistoryBufferSize; ++i) {
+    msg3[i] = _T('A');
+  }
+  msg3[kMaxHistoryBufferSize] = _T('\0');
+  AppendToHistory(msg3);
+  EXPECT_STREQ(msg3, GetHistory());
+
+  // Test set of strings that exactly fill buffer.
+  const int test_buffer_size = 64;
+  TCHAR msg4[test_buffer_size + 1] = {0};
+  for (int i = 0; i <= test_buffer_size; ++i) {
+    msg4[i] = _T('A');
+  }
+  msg4[test_buffer_size] = _T('\0');
+
+  int num_times_to_append = kMaxHistoryBufferSize / test_buffer_size;
+  EXPECT_EQ(kMaxHistoryBufferSize, num_times_to_append * test_buffer_size);
+  for (int i = 0; i < num_times_to_append; ++i) {
+    AppendToHistory(msg4);
+  }
+  EXPECT_STREQ(msg3, GetHistory());
+}
+
+TEST_F(HistoryTest, AppendToHistoryTest_WrapAround) {
+  // Test string that wraps around the buffer.
+  // First fill kMaxHistoryBufferSize - 1 with one string, then use
+  // another string of length 2("XX"), and another string to length of length 3.
+  // "XFFFGGGGG....GGGX" should be the result. The returned string should
+  // be in correct FIFO order i.e. "GGGGG......XXFFF".
+  const TCHAR msg6[] = _T("XX");
+  const TCHAR msg7[] = _T("FFF");
+  const int test_buffer_size = kMaxHistoryBufferSize - 1;
+  TCHAR msg5[test_buffer_size + 1] = {0};
+  TCHAR expected_buffer[kMaxHistoryBufferSize + 1] = {0};
+  for (int i = 0; i <= test_buffer_size; ++i) {
+    msg5[i] = _T('G');
+  }
+  msg5[test_buffer_size] = _T('\0');
+
+  // Call test method.
+  AppendToHistory(msg5);
+  AppendToHistory(msg6);
+  AppendToHistory(msg7);
+
+  // Create the expected string.
+  for (int i = 0; i <= kMaxHistoryBufferSize; ++i) {
+    expected_buffer[i] = _T('G');
+  }
+  int msg6len = wcslen(msg6);
+  int msg7len = wcslen(msg7);
+  memcpy(expected_buffer + kMaxHistoryBufferSize - msg6len - msg7len,
+         msg6,
+         msg6len * sizeof(TCHAR));
+  memcpy(expected_buffer + kMaxHistoryBufferSize - msg7len,
+         msg7,
+         msg7len * sizeof(TCHAR));
+  expected_buffer[kMaxHistoryBufferSize] = _T('\0');
+  EXPECT_STREQ(expected_buffer, GetHistory());
+}
+
+TEST_F(HistoryTest, AppendToHistoryTest_AnotherWrapAroundTest) {
+  // Test string that wraps around the buffer.
+  // First fill the kMaxHistoryBufferSize - 1 with one string, then fill it with
+  // another string of same length.
+  const int test_buffer_size = kMaxHistoryBufferSize - 1;
+  TCHAR msg2[test_buffer_size + 1] = {0};
+  TCHAR msg1[test_buffer_size + 1] = {0};
+  TCHAR expected_buffer[kMaxHistoryBufferSize + 1] = {0};
+  for (int i = 0; i <= test_buffer_size; ++i) {
+    msg1[i] = _T('G');
+    msg2[i] = _T('J');
+  }
+  msg2[test_buffer_size] = _T('\0');
+  msg1[test_buffer_size] = _T('\0');
+
+  // Call test method.
+  AppendToHistory(msg1);
+  AppendToHistory(msg2);
+
+  // Create the expected string.
+  for (int i = 0; i <= kMaxHistoryBufferSize; ++i) {
+    expected_buffer[i] = _T('J');
+  }
+  expected_buffer[0] = _T('G');
+  expected_buffer[kMaxHistoryBufferSize] = _T('\0');
+  EXPECT_STREQ(expected_buffer, GetHistory());
+}
+
+TEST_F(HistoryTest, AppendToHistoryTest_LotsOfLogs) {
+  // Run over a number of Append calls, with strings length
+  // (kMaxHistoryBufferSize / 2) + 1, causing wrap on every run.
+  TCHAR expected_buffer[kMaxHistoryBufferSize + 1] = {0};
+  const int test_buffer_size = (kMaxHistoryBufferSize / 2) + 1;
+  TCHAR msg1[test_buffer_size + 1] = {0};
+  for (int test_char = 'A'; test_char <= 'Z'; ++test_char) {
+    for (int i = 0; i <= test_buffer_size; ++i) {
+      msg1[i] = static_cast<TCHAR>(test_char);
+    }
+    msg1[test_buffer_size] = _T('\0');
+
+    // Call test method.
+    AppendToHistory(msg1);
+  }
+
+  // Create the expected string.
+  int i = 0;
+  for (; i < test_buffer_size - 2; ++i) {
+    expected_buffer[i] = _T('Y');
+  }
+  for (; i <= kMaxHistoryBufferSize; ++i) {
+    expected_buffer[i] = _T('Z');
+  }
+  expected_buffer[kMaxHistoryBufferSize] = _T('\0');
+  EXPECT_STREQ(expected_buffer, GetHistory());
+}
+
+TEST_F(HistoryTest, AppendToHistoryTest_LargeBuffer) {
+  // Test with a message that is larger than the buffer.
+  const int test_buffer_size = kMaxHistoryBufferSize + 10;
+  TCHAR msg4[test_buffer_size + 1] = {0};
+  TCHAR expected_buffer[kMaxHistoryBufferSize + 1] = {0};
+  for (int i = 0; i < test_buffer_size; ++i) {
+    msg4[i] = _T('A');
+  }
+  msg4[test_buffer_size] = _T('\0');
+
+  for (int i = 0; i <= kMaxHistoryBufferSize; ++i) {
+    expected_buffer[i] = _T('A');
+  }
+  expected_buffer[kMaxHistoryBufferSize] = _T('\0');
+
+  AppendToHistory(msg4);
+  EXPECT_STREQ(expected_buffer, GetHistory());
+}
+
+TEST_F(HistoryTest, AppendToHistoryTest_EmptyBuffer) {
+  CString test_string;
+  AppendToHistory(test_string);
+  EXPECT_TRUE(GetHistory().IsEmpty());
+}
+
+}  // namespace omaha
+
diff --git a/base/marshal_by_value.h b/base/marshal_by_value.h
new file mode 100644
index 0000000..4a87a8c
--- /dev/null
+++ b/base/marshal_by_value.h
@@ -0,0 +1,88 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// This is an implementation of IMarshal that always marshals by value. class T
+// needs to derive from MarshalByValue, and expose IMarshal through
+// QueryInterface. class T also needs to expose the IPersistStream-style methods
+// GetSizeMax(), Save(), and Load().
+
+#ifndef OMAHA_BASE_MARSHAL_BY_VALUE_H_
+#define OMAHA_BASE_MARSHAL_BY_VALUE_H_
+
+#include <atlbase.h>
+#include "omaha/base/debug.h"
+
+namespace omaha {
+
+template <class T>
+class ATL_NO_VTABLE MarshalByValue : public IMarshal {
+ public:
+  STDMETHOD(GetUnmarshalClass)(REFIID, void*, DWORD, void*, DWORD,
+                               CLSID* clsid) {
+    ASSERT1(clsid);
+    *clsid = T::GetObjectCLSID();
+    return S_OK;
+  }
+
+  STDMETHOD(ReleaseMarshalData)(IStream* stream) {
+    UNREFERENCED_PARAMETER(stream);
+    return S_OK;
+  }
+
+  STDMETHOD(DisconnectObject)(DWORD) {
+    return S_OK;
+  }
+
+  STDMETHOD(GetMarshalSizeMax)(REFIID, void*, DWORD, void*, DWORD,
+                               DWORD* size) {
+    ASSERT1(size);
+
+    T* persist = static_cast<T*>(this);
+    ULARGE_INTEGER size_max = {0};
+    HRESULT hr = persist->GetSizeMax(&size_max);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    *size = size_max.LowPart;
+    return S_OK;
+  }
+
+  STDMETHOD(MarshalInterface)(IStream* stream, REFIID, void*, DWORD, void*,
+                              DWORD) {
+    ASSERT1(stream);
+
+    T* persist = static_cast<T*>(this);
+    return persist->Save(stream, FALSE);
+  }
+
+  STDMETHOD(UnmarshalInterface)(IStream* stream, REFIID iid, void** ptr) {
+    ASSERT1(stream);
+    ASSERT1(ptr);
+
+    T* persist = static_cast<T*>(this);
+    HRESULT hr = persist->Load(stream);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    return persist->QueryInterface(iid, ptr);
+  }
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_MARSHAL_BY_VALUE_H_
+
diff --git a/base/md5.cc b/base/md5.cc
new file mode 100644
index 0000000..e357ee3
--- /dev/null
+++ b/base/md5.cc
@@ -0,0 +1,453 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+/*
+ * md5_opt.c V1.0 - optimized md5c.c from RFC1321 reference implementation
+ *
+ * Copyright (c) 1995 University of Southern California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation, advertising
+ * materials, and other materials related to such distribution and use
+ * acknowledge that the software was developed by the University of
+ * Southern California, Information Sciences Institute.  The name of the
+ * University may not be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ J. Touch / touch@isi.edu
+ 5/1/95
+
+*/
+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+*/
+
+#include "omaha/base/md5.h"
+#include "omaha/base/debug.h"
+
+namespace omaha {
+
+#if (defined(i386) || defined (__i386__) || defined (_M_IX86) || defined(__alpha))  // little-endian
+#undef REORDER
+#else
+#define REORDER 1
+#endif
+
+// Constants for MD5Transform routine
+#define kS11 7
+#define kS12 12
+#define kS13 17
+#define kS14 22
+#define kS21 5
+#define kS22 9
+#define kS23 14
+#define kS24 20
+#define kS31 4
+#define kS32 11
+#define kS33 16
+#define kS34 23
+#define kS41 6
+#define kS42 10
+#define kS43 15
+#define kS44 21
+
+static void MD5Transform (uint32 [4], unsigned char [64]);
+static void Encode (unsigned char *, uint32 *, unsigned int);
+#ifdef REORDER
+static void Decode (uint32 *, unsigned char *, unsigned int);
+#endif
+
+// SELECTANY static unsigned char PADDING[64] = {
+static unsigned char PADDING[64] = {
+    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+// F, G, H and I are basic MD5 functions.
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+// ROTATE_LEFT rotates x left n bits.
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+// Rotation is separate from addition to prevent recomputation.
+#define FF(a, b, c, d, x, s, ac) { \
+    (a) += F ((b), (c), (d)) + (x) + (uint32)(ac); \
+    (a) = ROTATE_LEFT ((a), (s)); \
+    (a) += (b); \
+    }
+#define GG(a, b, c, d, x, s, ac) { \
+    (a) += G ((b), (c), (d)) + (x) + (uint32)(ac); \
+    (a) = ROTATE_LEFT ((a), (s)); \
+    (a) += (b); \
+    }
+#define HH(a, b, c, d, x, s, ac) { \
+    (a) += H ((b), (c), (d)) + (x) + (uint32)(ac); \
+    (a) = ROTATE_LEFT ((a), (s)); \
+    (a) += (b); \
+    }
+#define II(a, b, c, d, x, s, ac) { \
+    (a) += I ((b), (c), (d)) + (x) + (uint32)(ac); \
+    (a) = ROTATE_LEFT ((a), (s)); \
+    (a) += (b); \
+    }
+
+// MD5 initialization. Begins an MD5 operation, writing a new context.
+void MD5Init (MD5_CTX *context) {
+    ASSERT(context, (L""));
+
+    context->count[0] = context->count[1] = 0;
+    // Load magic initialization constants.
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xefcdab89;
+    context->state[2] = 0x98badcfe;
+    context->state[3] = 0x10325476;
+}
+
+// MD5 block update operation. Continues an MD5 message-digest
+// operation, processing another message block, and updating the context.
+void MD5Update (MD5_CTX *context, unsigned char *input, unsigned int inputLen) {
+    ASSERT(input, (L""));
+    ASSERT(context, (L""));
+
+    unsigned int i, index, partLen;
+
+    // Compute number of bytes mod 64
+    index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+    // Update number of bits
+    if ((context->count[0] += ((uint32)inputLen << 3)) < ((uint32)inputLen << 3))
+        context->count[1]++;
+
+    context->count[1] += ((uint32)inputLen >> 29);
+    partLen = 64 - index;
+
+    // Transform as many times as possible
+    if (inputLen >= partLen) {
+       memcpy ((POINTER)&context->buffer[index], (POINTER)input, partLen);
+       MD5Transform (context->state, context->buffer);
+
+       for (i = partLen; i + 63 < inputLen; i += 64) MD5Transform (context->state, &input[i]);
+       index = 0;
+       }
+    else
+       i = 0;
+
+    // Buffer remaining input
+    memcpy ((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i);
+}
+
+// MD5 finalization. Ends an MD5 message-digest operation, writing the
+// the message digest and zeroizing the context.
+void MD5Final (unsigned char digest[16], MD5_CTX *context) {
+    ASSERT(context, (L""));
+
+    unsigned char bits[8];
+    unsigned int index, padLen;
+
+    // Save number of bits
+    Encode (bits, context->count, 8);
+
+    // Pad out to 56 mod 64.
+    index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+    padLen = (index < 56) ? (56 - index) : (120 - index);
+    MD5Update (context, PADDING, padLen);
+
+    // Append length (before padding)
+    MD5Update (context, bits, 8);
+    // Store state in digest
+    Encode (digest, context->state, 16);
+
+    // Zeroize sensitive information.
+    memset ((POINTER)context, 0, sizeof (*context));
+}
+
+// MD5 basic transformation. Transforms state based on block.
+void MD5Transform (uint32 state[4], unsigned char block[64]) {
+    // USC/ISI J. Touch - encourage pushing state variables into registers
+    register uint32 a = state[0], b = state[1], c = state[2], d = state[3];
+
+    /* USC/ISI J. Touch
+    decode and using copied data vs. direct use of input buffer
+    depends on whether reordering is required, which is a combination
+    of the byte order of the architecture and the command-line option
+    override
+    */
+
+#ifdef REORDER
+    uint32 x[16];
+
+    Decode (x, block, 64);
+
+    /* Round 1 */
+    FF (a, b, c, d, x[ 0], kS11, 0xd76aa478); /* 1 */
+    FF (d, a, b, c, x[ 1], kS12, 0xe8c7b756); /* 2 */
+    FF (c, d, a, b, x[ 2], kS13, 0x242070db); /* 3 */
+    FF (b, c, d, a, x[ 3], kS14, 0xc1bdceee); /* 4 */
+    FF (a, b, c, d, x[ 4], kS11, 0xf57c0faf); /* 5 */
+    FF (d, a, b, c, x[ 5], kS12, 0x4787c62a); /* 6 */
+    FF (c, d, a, b, x[ 6], kS13, 0xa8304613); /* 7 */
+    FF (b, c, d, a, x[ 7], kS14, 0xfd469501); /* 8 */
+    FF (a, b, c, d, x[ 8], kS11, 0x698098d8); /* 9 */
+    FF (d, a, b, c, x[ 9], kS12, 0x8b44f7af); /* 10 */
+    FF (c, d, a, b, x[10], kS13, 0xffff5bb1); /* 11 */
+    FF (b, c, d, a, x[11], kS14, 0x895cd7be); /* 12 */
+    FF (a, b, c, d, x[12], kS11, 0x6b901122); /* 13 */
+    FF (d, a, b, c, x[13], kS12, 0xfd987193); /* 14 */
+    FF (c, d, a, b, x[14], kS13, 0xa679438e); /* 15 */
+    FF (b, c, d, a, x[15], kS14, 0x49b40821); /* 16 */
+
+    /* Round 2 */
+    GG (a, b, c, d, x[ 1], kS21, 0xf61e2562); /* 17 */
+    GG (d, a, b, c, x[ 6], kS22, 0xc040b340); /* 18 */
+    GG (c, d, a, b, x[11], kS23, 0x265e5a51); /* 19 */
+    GG (b, c, d, a, x[ 0], kS24, 0xe9b6c7aa); /* 20 */
+    GG (a, b, c, d, x[ 5], kS21, 0xd62f105d); /* 21 */
+    GG (d, a, b, c, x[10], kS22,  0x2441453); /* 22 */
+    GG (c, d, a, b, x[15], kS23, 0xd8a1e681); /* 23 */
+    GG (b, c, d, a, x[ 4], kS24, 0xe7d3fbc8); /* 24 */
+    GG (a, b, c, d, x[ 9], kS21, 0x21e1cde6); /* 25 */
+    GG (d, a, b, c, x[14], kS22, 0xc33707d6); /* 26 */
+    GG (c, d, a, b, x[ 3], kS23, 0xf4d50d87); /* 27 */
+    GG (b, c, d, a, x[ 8], kS24, 0x455a14ed); /* 28 */
+    GG (a, b, c, d, x[13], kS21, 0xa9e3e905); /* 29 */
+    GG (d, a, b, c, x[ 2], kS22, 0xfcefa3f8); /* 30 */
+    GG (c, d, a, b, x[ 7], kS23, 0x676f02d9); /* 31 */
+    GG (b, c, d, a, x[12], kS24, 0x8d2a4c8a); /* 32 */
+
+    /* Round 3 */
+    HH (a, b, c, d, x[ 5], kS31, 0xfffa3942); /* 33 */
+    HH (d, a, b, c, x[ 8], kS32, 0x8771f681); /* 34 */
+    HH (c, d, a, b, x[11], kS33, 0x6d9d6122); /* 35 */
+    HH (b, c, d, a, x[14], kS34, 0xfde5380c); /* 36 */
+    HH (a, b, c, d, x[ 1], kS31, 0xa4beea44); /* 37 */
+    HH (d, a, b, c, x[ 4], kS32, 0x4bdecfa9); /* 38 */
+    HH (c, d, a, b, x[ 7], kS33, 0xf6bb4b60); /* 39 */
+    HH (b, c, d, a, x[10], kS34, 0xbebfbc70); /* 40 */
+    HH (a, b, c, d, x[13], kS31, 0x289b7ec6); /* 41 */
+    HH (d, a, b, c, x[ 0], kS32, 0xeaa127fa); /* 42 */
+    HH (c, d, a, b, x[ 3], kS33, 0xd4ef3085); /* 43 */
+    HH (b, c, d, a, x[ 6], kS34,  0x4881d05); /* 44 */
+    HH (a, b, c, d, x[ 9], kS31, 0xd9d4d039); /* 45 */
+    HH (d, a, b, c, x[12], kS32, 0xe6db99e5); /* 46 */
+    HH (c, d, a, b, x[15], kS33, 0x1fa27cf8); /* 47 */
+    HH (b, c, d, a, x[ 2], kS34, 0xc4ac5665); /* 48 */
+
+    /* Round 4 */
+    II (a, b, c, d, x[ 0], kS41, 0xf4292244); /* 49 */
+    II (d, a, b, c, x[ 7], kS42, 0x432aff97); /* 50 */
+    II (c, d, a, b, x[14], kS43, 0xab9423a7); /* 51 */
+    II (b, c, d, a, x[ 5], kS44, 0xfc93a039); /* 52 */
+    II (a, b, c, d, x[12], kS41, 0x655b59c3); /* 53 */
+    II (d, a, b, c, x[ 3], kS42, 0x8f0ccc92); /* 54 */
+    II (c, d, a, b, x[10], kS43, 0xffeff47d); /* 55 */
+    II (b, c, d, a, x[ 1], kS44, 0x85845dd1); /* 56 */
+    II (a, b, c, d, x[ 8], kS41, 0x6fa87e4f); /* 57 */
+    II (d, a, b, c, x[15], kS42, 0xfe2ce6e0); /* 58 */
+    II (c, d, a, b, x[ 6], kS43, 0xa3014314); /* 59 */
+    II (b, c, d, a, x[13], kS44, 0x4e0811a1); /* 60 */
+    II (a, b, c, d, x[ 4], kS41, 0xf7537e82); /* 61 */
+    II (d, a, b, c, x[11], kS42, 0xbd3af235); /* 62 */
+    II (c, d, a, b, x[ 2], kS43, 0x2ad7d2bb); /* 63 */
+    II (b, c, d, a, x[ 9], kS44, 0xeb86d391); /* 64 */
+
+#else
+    // USC/ISI J. Touch
+    // omit reordering, and use the input block as source data
+    /* Round 1 */
+    FF (a, b, c, d, ((uint32 *)block)[ 0], kS11, 0xd76aa478); /* 1 */
+    FF (d, a, b, c, ((uint32 *)block)[ 1], kS12, 0xe8c7b756); /* 2 */
+    FF (c, d, a, b, ((uint32 *)block)[ 2], kS13, 0x242070db); /* 3 */
+    FF (b, c, d, a, ((uint32 *)block)[ 3], kS14, 0xc1bdceee); /* 4 */
+    FF (a, b, c, d, ((uint32 *)block)[ 4], kS11, 0xf57c0faf); /* 5 */
+    FF (d, a, b, c, ((uint32 *)block)[ 5], kS12, 0x4787c62a); /* 6 */
+    FF (c, d, a, b, ((uint32 *)block)[ 6], kS13, 0xa8304613); /* 7 */
+    FF (b, c, d, a, ((uint32 *)block)[ 7], kS14, 0xfd469501); /* 8 */
+    FF (a, b, c, d, ((uint32 *)block)[ 8], kS11, 0x698098d8); /* 9 */
+    FF (d, a, b, c, ((uint32 *)block)[ 9], kS12, 0x8b44f7af); /* 10 */
+    FF (c, d, a, b, ((uint32 *)block)[10], kS13, 0xffff5bb1); /* 11 */
+    FF (b, c, d, a, ((uint32 *)block)[11], kS14, 0x895cd7be); /* 12 */
+    FF (a, b, c, d, ((uint32 *)block)[12], kS11, 0x6b901122); /* 13 */
+    FF (d, a, b, c, ((uint32 *)block)[13], kS12, 0xfd987193); /* 14 */
+    FF (c, d, a, b, ((uint32 *)block)[14], kS13, 0xa679438e); /* 15 */
+    FF (b, c, d, a, ((uint32 *)block)[15], kS14, 0x49b40821); /* 16 */
+
+    /* Round 2 */
+    GG (a, b, c, d, ((uint32 *)block)[ 1], kS21, 0xf61e2562); /* 17 */
+    GG (d, a, b, c, ((uint32 *)block)[ 6], kS22, 0xc040b340); /* 18 */
+    GG (c, d, a, b, ((uint32 *)block)[11], kS23, 0x265e5a51); /* 19 */
+    GG (b, c, d, a, ((uint32 *)block)[ 0], kS24, 0xe9b6c7aa); /* 20 */
+    GG (a, b, c, d, ((uint32 *)block)[ 5], kS21, 0xd62f105d); /* 21 */
+    GG (d, a, b, c, ((uint32 *)block)[10], kS22,  0x2441453); /* 22 */
+    GG (c, d, a, b, ((uint32 *)block)[15], kS23, 0xd8a1e681); /* 23 */
+    GG (b, c, d, a, ((uint32 *)block)[ 4], kS24, 0xe7d3fbc8); /* 24 */
+    GG (a, b, c, d, ((uint32 *)block)[ 9], kS21, 0x21e1cde6); /* 25 */
+    GG (d, a, b, c, ((uint32 *)block)[14], kS22, 0xc33707d6); /* 26 */
+    GG (c, d, a, b, ((uint32 *)block)[ 3], kS23, 0xf4d50d87); /* 27 */
+    GG (b, c, d, a, ((uint32 *)block)[ 8], kS24, 0x455a14ed); /* 28 */
+    GG (a, b, c, d, ((uint32 *)block)[13], kS21, 0xa9e3e905); /* 29 */
+    GG (d, a, b, c, ((uint32 *)block)[ 2], kS22, 0xfcefa3f8); /* 30 */
+    GG (c, d, a, b, ((uint32 *)block)[ 7], kS23, 0x676f02d9); /* 31 */
+    GG (b, c, d, a, ((uint32 *)block)[12], kS24, 0x8d2a4c8a); /* 32 */
+
+    /* Round 3 */
+    HH (a, b, c, d, ((uint32 *)block)[ 5], kS31, 0xfffa3942); /* 33 */
+    HH (d, a, b, c, ((uint32 *)block)[ 8], kS32, 0x8771f681); /* 34 */
+    HH (c, d, a, b, ((uint32 *)block)[11], kS33, 0x6d9d6122); /* 35 */
+    HH (b, c, d, a, ((uint32 *)block)[14], kS34, 0xfde5380c); /* 36 */
+    HH (a, b, c, d, ((uint32 *)block)[ 1], kS31, 0xa4beea44); /* 37 */
+    HH (d, a, b, c, ((uint32 *)block)[ 4], kS32, 0x4bdecfa9); /* 38 */
+    HH (c, d, a, b, ((uint32 *)block)[ 7], kS33, 0xf6bb4b60); /* 39 */
+    HH (b, c, d, a, ((uint32 *)block)[10], kS34, 0xbebfbc70); /* 40 */
+    HH (a, b, c, d, ((uint32 *)block)[13], kS31, 0x289b7ec6); /* 41 */
+    HH (d, a, b, c, ((uint32 *)block)[ 0], kS32, 0xeaa127fa); /* 42 */
+    HH (c, d, a, b, ((uint32 *)block)[ 3], kS33, 0xd4ef3085); /* 43 */
+    HH (b, c, d, a, ((uint32 *)block)[ 6], kS34,  0x4881d05); /* 44 */
+    HH (a, b, c, d, ((uint32 *)block)[ 9], kS31, 0xd9d4d039); /* 45 */
+    HH (d, a, b, c, ((uint32 *)block)[12], kS32, 0xe6db99e5); /* 46 */
+    HH (c, d, a, b, ((uint32 *)block)[15], kS33, 0x1fa27cf8); /* 47 */
+    HH (b, c, d, a, ((uint32 *)block)[ 2], kS34, 0xc4ac5665); /* 48 */
+
+    /* Round 4 */
+    II (a, b, c, d, ((uint32 *)block)[ 0], kS41, 0xf4292244); /* 49 */
+    II (d, a, b, c, ((uint32 *)block)[ 7], kS42, 0x432aff97); /* 50 */
+    II (c, d, a, b, ((uint32 *)block)[14], kS43, 0xab9423a7); /* 51 */
+    II (b, c, d, a, ((uint32 *)block)[ 5], kS44, 0xfc93a039); /* 52 */
+    II (a, b, c, d, ((uint32 *)block)[12], kS41, 0x655b59c3); /* 53 */
+    II (d, a, b, c, ((uint32 *)block)[ 3], kS42, 0x8f0ccc92); /* 54 */
+    II (c, d, a, b, ((uint32 *)block)[10], kS43, 0xffeff47d); /* 55 */
+    II (b, c, d, a, ((uint32 *)block)[ 1], kS44, 0x85845dd1); /* 56 */
+    II (a, b, c, d, ((uint32 *)block)[ 8], kS41, 0x6fa87e4f); /* 57 */
+    II (d, a, b, c, ((uint32 *)block)[15], kS42, 0xfe2ce6e0); /* 58 */
+    II (c, d, a, b, ((uint32 *)block)[ 6], kS43, 0xa3014314); /* 59 */
+    II (b, c, d, a, ((uint32 *)block)[13], kS44, 0x4e0811a1); /* 60 */
+    II (a, b, c, d, ((uint32 *)block)[ 4], kS41, 0xf7537e82); /* 61 */
+    II (d, a, b, c, ((uint32 *)block)[11], kS42, 0xbd3af235); /* 62 */
+    II (c, d, a, b, ((uint32 *)block)[ 2], kS43, 0x2ad7d2bb); /* 63 */
+    II (b, c, d, a, ((uint32 *)block)[ 9], kS44, 0xeb86d391); /* 64 */
+#endif  // REORDER
+
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+
+#ifdef REORDER
+    memset ((POINTER)x, 0, sizeof (x));  // zero sensitive information
+#endif
+}
+
+// Encodes input (uint32) into output (unsigned char). Assumes len is a multiple of 4.
+void Encode (unsigned char *output, uint32 *input, unsigned int len) {
+    ASSERT(input, (L""));
+    ASSERT(output, (L""));
+
+    unsigned int i, j;
+
+    for (i = 0, j = 0; j < len; i++, j += 4) {
+        output[j] = (unsigned char)(input[i] & 0xff);
+        output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+        output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+        output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+        }
+}
+
+#ifdef REORDER
+
+// Decodes input (unsigned char) into output (uint32). Assumes len is a multiple of 4.
+void Decode (uint32 *output, unsigned char *input, unsigned int len) {
+    ASSERT(input, (L""));
+    ASSERT(output, (L""));
+
+    register uint32 out,other;
+
+    // for (i = 0, j = 0; j < len; i++, j += 4)
+    // output[i] = ((uint32)input[j]) | (((uint32)input[j+1]) << 8) |
+    // (((uint32)input[j+2]) << 16) | (((uint32)input[j+3]) << 24);
+
+    // USC/ISI J. Touch
+    // these are optimized swap routines, in C code they cost more in "computation" operations, but less in
+    // "loads" than the above code, and run substantially faster as a result
+#if (!defined(hpux))
+#define swapbyte(src,dst) { \
+     out = ROTATE_LEFT((src),16); \
+     other = out >> 8; \
+     other &= 0x00ff00ff; \
+     out &= 0x00ff00ff; \
+     out <<= 8; \
+     (dst) = out | other; \
+     }
+#else
+#define swapbyte(src,dst) { \
+     (dst) = (ROTATE_LEFT((src),8) & 0x00ff00ff) | ROTATE_LEFT((src) & 0x00ff00ff,24); \
+     }
+#endif
+
+    // USC/ISI J. Touch
+    // unroll the loop above, because the code runs faster with constants for indices than even with variable indices
+    // as conventional (automatic) unrolling would perform (!)
+    swapbyte(((uint32 *)input)[0],output[0]);
+    swapbyte(((uint32 *)input)[1],output[1]);
+    swapbyte(((uint32 *)input)[2],output[2]);
+    swapbyte(((uint32 *)input)[3],output[3]);
+    swapbyte(((uint32 *)input)[4],output[4]);
+    swapbyte(((uint32 *)input)[5],output[5]);
+    swapbyte(((uint32 *)input)[6],output[6]);
+    swapbyte(((uint32 *)input)[7],output[7]);
+    swapbyte(((uint32 *)input)[8],output[8]);
+    swapbyte(((uint32 *)input)[9],output[9]);
+    swapbyte(((uint32 *)input)[10],output[10]);
+    swapbyte(((uint32 *)input)[11],output[11]);
+    swapbyte(((uint32 *)input)[12],output[12]);
+    swapbyte(((uint32 *)input)[13],output[13]);
+    swapbyte(((uint32 *)input)[14],output[14]);
+    swapbyte(((uint32 *)input)[15],output[15]);
+}
+
+#endif // #ifdef REORDER
+
+}  // namespace omaha
+
diff --git a/common/md5.h b/base/md5.h
similarity index 100%
rename from common/md5.h
rename to base/md5.h
diff --git a/base/md5_unittest.cc b/base/md5_unittest.cc
new file mode 100644
index 0000000..049ffe3
--- /dev/null
+++ b/base/md5_unittest.cc
@@ -0,0 +1,58 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+// MD5 unittest
+
+#include <atlstr.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/md5.h"
+#include "omaha/base/string.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+void CheckMD5 (char *string, TCHAR *correct_digest) {
+    ASSERT_TRUE(correct_digest);
+    ASSERT_TRUE(string);
+
+    MD5_CTX context;
+    MD5Init (&context);
+    MD5Update (&context, (unsigned char *)string, strlen (string));
+    unsigned char digest[16];
+    MD5Final (digest, &context);
+
+    const DWORD digest_len = 32+1;
+    TCHAR digest_string[digest_len];
+    digest_string[31] = '\0';
+    digest_string[0] = '\0';
+
+    for (int i = 0; i < 16; i++) {
+        SafeStrCat (digest_string, SPRINTF (_T("%02x"), digest[i]), digest_len);
+    }
+
+    ASSERT_STREQ(digest_string, correct_digest);
+}
+
+TEST(Md5Test, MD5) {
+    CheckMD5 ("", _T("d41d8cd98f00b204e9800998ecf8427e"));
+    CheckMD5 ("a", _T("0cc175b9c0f1b6a831c399e269772661"));
+    CheckMD5 ("abc", _T("900150983cd24fb0d6963f7d28e17f72"));
+    CheckMD5 ("message digest", _T("f96b697d7cb7938d525a2f31aaf161d0"));
+    CheckMD5 ("abcdefghijklmnopqrstuvwxyz", _T("c3fcd3d76192e4007dfb496cca67e13b"));
+    CheckMD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", _T("d174ab98d277d9f5a5611c2c9f419d9f"));
+    CheckMD5 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890", _T("57edf4a22be3c955ac49da2e2107b67a"));
+}
+
+}  // namespace omaha
+
diff --git a/base/module_utils.cc b/base/module_utils.cc
new file mode 100644
index 0000000..c9f9e89
--- /dev/null
+++ b/base/module_utils.cc
@@ -0,0 +1,91 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/module_utils.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+const int kLongPath = (_MAX_PATH * 2);
+const int kReallyLongPath = (kLongPath * 2);
+
+HMODULE ModuleFromStatic(void* pointer_to_static_in_module) {
+  ASSERT(pointer_to_static_in_module, (L""));
+
+  MEMORY_BASIC_INFORMATION info = { 0 };
+  VirtualQuery(reinterpret_cast<void*>(pointer_to_static_in_module),
+    &info, sizeof(info));
+  // Module handles are just the allocation base address of the module.
+  return reinterpret_cast<HMODULE>(info.AllocationBase);
+}
+
+bool GetModuleDirectory(HMODULE module, TCHAR* directory) {
+  ASSERT(directory, (L"Invalid arguments"));
+  if (!directory) {
+    return false;
+  }
+
+  // PathRemoveFileSpec only supports buffers up to MAX_PATH so we must
+  // limit ourselves to this.  It will "always" work anyway, given that
+  // our installation path is not absurdly deep.
+  if (0 == GetModuleFileName(module, directory, MAX_PATH)) {
+    ASSERT(false, (L"Path longer than MAX_PATH"));
+    return false;
+  }
+
+  if (!String_PathRemoveFileSpec(directory)) {
+    ASSERT(false, (L"PathRemoveFileSpec failed"));
+    // Ensure we don't return with an incorrect path in the buffer that was
+    // passed in.
+    ZeroMemory(directory, MAX_PATH * sizeof(TCHAR));
+    return false;
+  }
+
+  return true;
+}
+
+HRESULT GetModuleFileName(HMODULE module, CString* path) {
+  ASSERT(path, (_T("must be valid")));
+
+  // _MAX_PATH should cover at least 99% of the paths
+  int buf_size = _MAX_PATH;
+  int chars_copied = 0;
+  while ((chars_copied = ::GetModuleFileName(module,
+                                             CStrBuf(*path, buf_size + 1),
+                                             buf_size)) == buf_size) {
+    // We'll stop before things get ridiculous
+    if (buf_size >= kReallyLongPath) {
+      UTIL_LOG(LEVEL_ERROR,
+               (_T("[GetModuleFileName - unusually long path '%s']"), path));
+      chars_copied = 0;
+      ::SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+      break;
+    }
+
+    buf_size *= 2;
+  }
+
+  if (!chars_copied) {
+    path->Empty();
+    return GetCurError();
+  }
+
+  return S_OK;
+}
+
+}  // namespace omaha
+
diff --git a/base/module_utils.h b/base/module_utils.h
new file mode 100644
index 0000000..7f71411
--- /dev/null
+++ b/base/module_utils.h
@@ -0,0 +1,52 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_MODULE_UTILS_H_
+#define OMAHA_COMMON_MODULE_UTILS_H_
+
+#include "omaha/base/string.h"
+
+namespace omaha {
+
+// Utilities for working with modules in processes.
+
+// Returns the module handle of a module, given a pointer to a static
+// member of the module (e.g. a static function or static variable).
+HMODULE ModuleFromStatic(void* pointer_to_static_in_module);
+
+// Copies the path of the directory that contains the file for 'module' into
+// 'directory'.
+//
+// @param module Must be a valid, non-NULL module handle.
+// @param directory MUST have room for MAX_PATH characters or more.  The path
+// copied into this buffer will not have a trailing backslash.
+//
+// @return false iff there is an error.
+bool GetModuleDirectory(HMODULE module, TCHAR* directory);
+
+/**
+* Returns a path to a module.  Uses the
+* Win32 GetModuleFileName function, so you
+* can pass NULL for module.
+*
+* @param module Handle to the module or NULL for the current module.
+* @param path Holds the path to the module on successful return.
+* @returns S_OK if successful, otherwise an error value.
+*/
+HRESULT GetModuleFileName(HMODULE module, OUT CString* path);
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_MODULE_UTILS_H_
diff --git a/base/module_utils_unittest.cc b/base/module_utils_unittest.cc
new file mode 100644
index 0000000..ddf2fcb
--- /dev/null
+++ b/base/module_utils_unittest.cc
@@ -0,0 +1,53 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Unit test for module utility functions.
+
+#include "omaha/base/constants.h"
+#include "omaha/base/module_utils.h"
+#include "omaha/base/path.h"
+#include "omaha/base/utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(ModuleUtilsTest, ModuleUtils) {
+  // ModuleFromStatic
+  HMODULE module = ModuleFromStatic(reinterpret_cast<void*>(ModuleFromStatic));
+  ASSERT_TRUE(module);
+
+  // GetModuleDirectory
+  TCHAR directory1[MAX_PATH] = {0};
+  TCHAR directory2[MAX_PATH] = {0};
+  ASSERT_TRUE(GetModuleDirectory(module, directory1));
+  ASSERT_TRUE(GetModuleDirectory(NULL, directory2));
+  EXPECT_STREQ(directory1, directory2);
+
+  // GetModuleFileName
+  CString path1;
+  CString path2;
+  ASSERT_SUCCEEDED(GetModuleFileName(module, &path1));
+  ASSERT_SUCCEEDED(GetModuleFileName(NULL, &path2));
+  EXPECT_STREQ(path1, path2);
+
+  // Verify values, as much as we can.
+  CString file(GetFileFromPath(path1));
+  EXPECT_STREQ(file, kUnittestName);
+
+  CString dir(GetDirectoryFromPath(path1));
+  EXPECT_STREQ(dir, directory1);
+}
+
+}  // namespace omaha
diff --git a/base/object_factory.h b/base/object_factory.h
new file mode 100644
index 0000000..b74b21c
--- /dev/null
+++ b/base/object_factory.h
@@ -0,0 +1,67 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_BASE_OBJECT_FACTORY_H_
+#define OMAHA_BASE_OBJECT_FACTORY_H_
+
+#include <map>
+
+namespace omaha {
+
+// Factory creates instances of objects based on a unique type id.
+//
+// AbstractProduct - base class of the product hierarchy.
+// TypeId - type id for each type in the hierarchy.
+// ProductCreator - callable entity to create objects.
+
+template <class AbstractProduct,
+          typename TypeId,
+          typename ProductCreator = AbstractProduct* (*)()>
+class Factory {
+ public:
+  Factory() {}
+
+  // Registers a creator for the type id. Returns true if the creator has
+  // been registered succesfully.
+  bool Register(const TypeId& id, ProductCreator creator) {
+    return id_to_creators_.insert(Map::value_type(id, creator)).second;
+  }
+
+  // Unregisters a type id.
+  bool Unregister(const TypeId& id) {
+    return id_to_creators_.erase(id) == 1;
+  }
+
+  // Creates an instance of the abstract product.
+  AbstractProduct* CreateObject(const TypeId& id) const {
+    typename Map::const_iterator it = id_to_creators_.find(id);
+    if (it != id_to_creators_.end()) {
+      return (it->second)();
+    } else {
+      return NULL;
+    }
+  }
+
+ private:
+  typedef std::map<TypeId, ProductCreator> Map;
+  Map id_to_creators_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(Factory);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_OBJECT_FACTORY_H_
+
diff --git a/base/object_factory_unittest.cc b/base/object_factory_unittest.cc
new file mode 100644
index 0000000..132ce39
--- /dev/null
+++ b/base/object_factory_unittest.cc
@@ -0,0 +1,19 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/object_factory.h"
+#include "omaha/testing/unit_test.h"
+
+// TODO(omaha): write unit tests
diff --git a/base/omaha_version.cc b/base/omaha_version.cc
new file mode 100644
index 0000000..96bd073
--- /dev/null
+++ b/base/omaha_version.cc
@@ -0,0 +1,63 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/file_ver.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/utils.h"
+
+namespace {
+
+// The version string is not visible outside this module and it is only
+// accessible through the omaha::GetVersionString accessor.
+CString version_string;
+
+ULONGLONG omaha_version = 0;
+
+}  // namespace
+
+namespace omaha {
+
+// In both GetVersion* methods, we assert only that InitializeVersion* was
+// already called and not that they weren't called twice, which is OK.
+// There is no detection that the version_ variables aren't accessed before
+// being initialized because this would require accessor methods to enforce and
+// lead to bloat.
+
+const TCHAR* GetVersionString() {
+  ASSERT1(!version_string.IsEmpty());
+  return version_string;
+}
+
+ULONGLONG GetVersion() {
+  ASSERT1(!version_string.IsEmpty());
+  return omaha_version;
+}
+
+void InitializeVersionFromModule(HINSTANCE instance) {
+  ULONGLONG module_version = app_util::GetVersionFromModule(instance);
+
+  InitializeVersion(module_version);
+}
+
+void InitializeVersion(ULONGLONG version) {
+  omaha_version = version;
+
+  version_string = StringFromVersion(omaha_version);
+}
+
+}  // namespace omaha
diff --git a/common/omaha_version.h b/base/omaha_version.h
similarity index 100%
rename from common/omaha_version.h
rename to base/omaha_version.h
diff --git a/base/omaha_version_unittest.cc b/base/omaha_version_unittest.cc
new file mode 100644
index 0000000..da88fc2
--- /dev/null
+++ b/base/omaha_version_unittest.cc
@@ -0,0 +1,51 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(OmahaVersionTest, InitializeVersion) {
+  CString version_string = GetVersionString();
+  ULONGLONG version = GetVersion();
+
+  EXPECT_STREQ(OMAHA_BUILD_VERSION_STRING, version_string);
+  EXPECT_EQ(OMAHA_BUILD_VERSION, version);
+
+  InitializeVersion(MAKEDLLVERULL(0, 0, 0, 0));
+  EXPECT_STREQ(_T("0.0.0.0"), GetVersionString());
+  EXPECT_EQ(0, GetVersion());
+
+  InitializeVersion(MAKEDLLVERULL(1, 2, 3, 4));
+  EXPECT_STREQ(_T("1.2.3.4"), GetVersionString());
+  EXPECT_EQ(0x0001000200030004, GetVersion());
+
+  InitializeVersion(MAKEDLLVERULL(0x7fff, 0x7fff, 0x7fff, 0x7fff));
+  EXPECT_STREQ(_T("32767.32767.32767.32767"), GetVersionString());
+  EXPECT_EQ(0x7fff7fff7fff7fff, GetVersion());
+
+  InitializeVersion(MAKEDLLVERULL(0xffff, 0xffff, 0xffff, 0xffff));
+  EXPECT_STREQ(_T("65535.65535.65535.65535"), GetVersionString());
+  EXPECT_EQ(0xffffffffffffffff, GetVersion());
+
+  // Sets back the initial version.
+  InitializeVersion(VersionFromString(version_string));
+  EXPECT_STREQ(version_string, GetVersionString());
+  EXPECT_EQ(version, GetVersion());
+}
+
+}  // namespace omaha
diff --git a/base/path.cc b/base/path.cc
new file mode 100644
index 0000000..1035b7c
--- /dev/null
+++ b/base/path.cc
@@ -0,0 +1,461 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Path utility functions.
+
+#include "omaha/base/path.h"
+
+#include <atlbase.h>
+#include <atlstr.h>
+#include <atlpath.h>
+#include <map>
+#include <vector>
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/shell.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+const TCHAR* const kRegSvr32Cmd1 = _T("regsvr32 ");
+const TCHAR* const kRegSvr32Cmd2 = _T("regsvr32.exe ");
+const TCHAR* const kRunDll32Cmd1 = _T("rundll32 ");
+const TCHAR* const kRunDll32Cmd2 = _T("rundll32.exe ");
+const TCHAR* const kMsiExecCmd1  = _T("msiexec ");
+const TCHAR* const kMsiExecCmd2  = _T("msiexec.exe ");
+const TCHAR* const kDotExe       = _T(".exe");
+
+
+namespace detail {
+
+typedef bool (*Filter)(const WIN32_FIND_DATA&);
+
+bool IsFile(const WIN32_FIND_DATA& find_data) {
+  return (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
+}
+
+bool IsDirectory(const WIN32_FIND_DATA& find_data) {
+  return (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+}
+
+bool AllFiles(const WIN32_FIND_DATA&) {
+  return true;
+}
+
+HRESULT FindFilesEx(const CString& dir,
+                    const CString& pattern,
+                    std::vector<CString>* files,
+                    Filter func) {
+  ASSERT1(files);
+
+  files->clear();
+  if (!File::Exists(dir)) {
+    return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
+  }
+
+  CString files_to_find = ConcatenatePath(dir, pattern);
+  WIN32_FIND_DATA find_data = {0};
+  scoped_hfind hfind(::FindFirstFile(files_to_find, &find_data));
+  if (!hfind) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(L5, (_T("[File::GetWildcards - FindFirstFile failed][0x%x]"), hr));
+    return hr;
+  }
+
+  do {
+    if (func(find_data)) {
+      files->push_back(find_data.cFileName);
+    }
+  } while (::FindNextFile(get(hfind), &find_data));
+
+  return S_OK;
+}
+
+}  // namespace detail.
+
+// Get the starting path from the command string
+CString GetStartingPathFromString(const CString& s) {
+  CString path;
+  CString str(s);
+  TrimCString(str);
+
+  int len = str.GetLength();
+  if (len > 0) {
+    if (str[0] == _T('"')) {
+      // For something like:  "c:\Program Files\...\" ...
+      int idx = String_FindChar(str.GetString() + 1, _T('"'));
+      if (idx != -1)
+        path.SetString(str.GetString() + 1, idx);
+    } else {
+      // For something like:  c:\PRGRA~1\... ...
+      int idx = String_FindChar(str, _T(' '));
+      path.SetString(str, idx == -1 ? len : idx);
+    }
+  }
+
+  return path;
+}
+
+// Get the trailing path from the command string
+CString GetTrailingPathFromString(const CString& s) {
+  CString path;
+  CString str(s);
+  TrimCString(str);
+
+  int len = str.GetLength();
+  if (len > 0) {
+    if (str[len - 1] == _T('"')) {
+      // For something like:  regsvr32 /u /s "c:\Program Files\..."
+      str.Truncate(len - 1);
+      int idx = String_ReverseFindChar(str, _T('"'));
+      if (idx != -1)
+        path.SetString(str.GetString() + idx + 1, len - idx - 1);
+    } else {
+      // For something like:  regsvr32 /u /s c:\PRGRA~1\...
+      int idx = String_ReverseFindChar(str, _T(' '));
+      if (idx != -1)
+        path.SetString(str.GetString() + idx + 1, len - idx - 1);
+    }
+  }
+
+  return path;
+}
+
+// Get the file from the command string
+HRESULT GetFileFromCommandString(const TCHAR* s, CString* file) {
+  ASSERT1(file);
+
+  if (!s || !*s) {
+    return E_INVALIDARG;
+  }
+
+  CString str(s);
+  TrimCString(str);
+
+  // Handle the string starting with quotation mark
+  // For example: "C:\Program Files\WinZip\WINZIP32.EXE" /uninstall
+  if (str[0] == _T('"')) {
+    int idx_quote = str.Find(_T('"'), 1);
+    if (idx_quote != -1) {
+      file->SetString(str.GetString() + 1, idx_quote - 1);
+      return S_OK;
+    } else {
+      return E_FAIL;
+    }
+  }
+
+  // Handle the string starting with "regsvr32"
+  // For example: regsvr32 /u /s "c:\program files\google\googletoolbar3.dll"
+  if (String_StartsWith(str, kRegSvr32Cmd1, true) ||
+      String_StartsWith(str, kRegSvr32Cmd2, true)) {
+    file->SetString(GetTrailingPathFromString(str));
+    return S_OK;
+  }
+
+  // Handle the string starting with "rundll32"
+  // For example: "rundll32.exe setupapi.dll,InstallHinfSection DefaultUninstall 132 C:\WINDOWS\INF\PCHealth.inf"  // NOLINT
+  if (String_StartsWith(str, kRunDll32Cmd1, true) ||
+      String_StartsWith(str, kRunDll32Cmd2, true)) {
+    int idx_space = str.Find(_T(' '));
+    ASSERT1(idx_space != -1);
+    int idx_comma = str.Find(_T(','), idx_space + 1);
+    if (idx_comma != -1) {
+      file->SetString(str.GetString() + idx_space + 1,
+                      idx_comma - idx_space - 1);
+      TrimCString(*file);
+      return S_OK;
+    } else {
+      return E_FAIL;
+    }
+  }
+
+  // Handle the string starting with "msiexec"
+  // For example: MsiExec.exe /I{25A13826-8E4A-4FBF-AD2B-776447FE9646}
+  if (String_StartsWith(str, kMsiExecCmd1, true) ||
+      String_StartsWith(str, kMsiExecCmd2, true)) {
+    return E_FAIL;
+  }
+
+  // Otherwise, try to find the file till reaching ".exe"
+  // For example: "C:\Program Files\Google\Google Desktop Search\GoogleDesktopSetup.exe -uninstall"  // NOLINT
+  for (int i = 0; i < str.GetLength(); ++i) {
+    if (String_StartsWith(str.GetString() + i, kDotExe, true)) {
+      file->SetString(str, i + _tcslen(kDotExe));
+      return S_OK;
+    }
+  }
+
+  // As last resort, return the part from the beginning to first space found.
+  int idx = str.Find(_T(' '));
+  if (idx == -1) {
+    file->SetString(str);
+  } else {
+    file->SetString(str, idx);
+  }
+
+  return S_OK;
+}
+
+// Expands the string with embedded special folder variables.
+// TODO(omaha): This function seems to have a very specific purpose, which
+// is not used in our code base. Consider removing it.
+HRESULT ExpandStringWithSpecialFolders(CString* str) {
+  ASSERT(str, (L""));
+
+#pragma warning(push)
+// construction of local static object is not thread-safe
+#pragma warning(disable : 4640)
+  static std::map<CString, CString> g_special_folders_mapping;
+#pragma warning(pop)
+
+  if (g_special_folders_mapping.size() == 0) {
+    RET_IF_FAILED(
+        Shell::GetSpecialFolderKeywordsMapping(&g_special_folders_mapping));
+  }
+
+  CString expanded_str;
+  RET_IF_FAILED(
+      ExpandEnvLikeStrings(*str, g_special_folders_mapping, &expanded_str));
+
+  str->SetString(expanded_str);
+
+  return S_OK;
+}
+
+// Internal helper method for normalizing a path
+HRESULT NormalizePathInternal(const TCHAR* path, CString* normalized_path) {
+  // We use '|' to separate fields
+  CString field;
+  int bar_idx = String_FindChar(path, _T('|'));
+  if (bar_idx == -1)
+    field = path;
+  else
+    field.SetString(path, bar_idx);
+
+  if (IsRegistryPath(field)) {
+    CString key_name, value_name;
+    RET_IF_FAILED(RegSplitKeyvalueName(field, &key_name, &value_name));
+
+    CString reg_value;
+    RET_IF_FAILED(RegKey::GetValue(key_name, value_name, &reg_value));
+    normalized_path->Append(reg_value);
+  } else {
+    RET_IF_FAILED(ExpandStringWithSpecialFolders(&field));
+    normalized_path->Append(field);
+  }
+
+  if (bar_idx != -1)
+    return NormalizePathInternal(path + bar_idx + 1, normalized_path);
+  else
+    return S_OK;
+}
+
+// Normalize a path
+HRESULT NormalizePath(const TCHAR* path, CString* normalized_path) {
+  ASSERT1(normalized_path);
+
+  normalized_path->Empty();
+
+  if (path) {
+    HRESULT hr = NormalizePathInternal(path, normalized_path);
+    if (FAILED(hr)) {
+      normalized_path->Empty();
+      UTIL_LOG(LE, (_T("[NormalizePath - unable to normalize path][%s][0x%x]"),
+                    path, hr));
+    }
+    return hr;
+  } else {
+    return S_OK;
+  }
+}
+
+CString ConcatenatePath(const CString& path1, const CString& path2) {
+  CString ret(path1);
+
+  // Append the file path using the PathAppend.
+  VERIFY1(::PathAppend(CStrBuf(ret, MAX_PATH), path2));
+
+  return ret;
+}
+
+// Get the filename from the path
+// "C:\TEST\sample.txt" returns "sample.txt"
+CString GetFileFromPath(const CString& path) {
+  CPath path1(path);
+  path1.StripPath();
+  return static_cast<CString>(path1);
+}
+
+// Get the directory from the path
+// "C:\TEST\sample.txt" returns "C:\TEST"
+// Get the directory out of the file path
+CString GetDirectoryFromPath(const CString& path) {
+  CPath path1(path);
+  path1.RemoveFileSpec();
+  return static_cast<CString>(path1);
+}
+
+// Remove the extension from the path.
+// "C:\TEST\sample.txt" returns "C:\TEST\sample"
+CString GetPathRemoveExtension(const CString& path) {
+  CPath path1(path);
+  path1.RemoveExtension();
+  return static_cast<CString>(path1);
+}
+
+// Basically, an absolute path starts with X:\ or \\ (a UNC name)
+bool IsAbsolutePath(const TCHAR* path) {
+  ASSERT1(path);
+
+  int len = ::_tcslen(path);
+  if (len < 3)
+    return false;
+  if (*path == _T('"'))
+    path++;
+  if (String_StartsWith(path+1, _T(":\\"), false))
+    return true;
+  if (String_StartsWith(path, _T("\\\\"), false))
+    return true;
+  return false;
+}
+
+void EnclosePath(CString* path) {
+  ASSERT1(path);
+
+  if (path->IsEmpty()) {
+    return;
+  }
+
+  bool starts_with_quote = (_T('"') == path->GetAt(0));
+  bool ends_with_quote = (_T('"') == path->GetAt(path->GetLength() - 1));
+  ASSERT(starts_with_quote == ends_with_quote, (_T("%s"), path->GetString()));
+  bool is_enclosed = starts_with_quote && ends_with_quote;
+  if (is_enclosed) {
+    return;
+  }
+
+  path->Insert(0, _T('"'));
+  path->AppendChar(_T('"'));
+}
+
+CString EnclosePathIfExe(const CString& module_path) {
+  if (!String_EndsWith(module_path, _T(".exe"), true)) {
+    return module_path;
+  }
+
+  CString enclosed_path(module_path);
+  EnclosePath(&enclosed_path);
+  return enclosed_path;
+}
+
+// remove any double quotation masks from an enclosed path
+void UnenclosePath(CString* path) {
+  ASSERT1(path);
+
+  if (path->GetLength() > 1 && path->GetAt(0) == _T('"')) {
+    bool right_quote_exists = (path->GetAt(path->GetLength() - 1) == _T('"'));
+    ASSERT(right_quote_exists,
+           (_T("[UnenclosePath - double quote mismatches]")));
+    if (right_quote_exists) {
+      // Remove the double quotation masks
+      path->Delete(0);
+      path->Truncate(path->GetLength() - 1);
+    }
+  }
+}
+
+void RemoveMismatchedEndQuoteInDirectoryPath(CString* directory_path) {
+  ASSERT1(directory_path);
+
+  if (directory_path->GetLength() <= 1) {
+    return;
+  }
+
+  ASSERT1(directory_path->GetAt(0) != _T('"'));
+  if (directory_path->GetAt(directory_path->GetLength() - 1) == _T('"')) {
+    directory_path->Truncate(directory_path->GetLength() - 1);
+  }
+}
+
+HRESULT ShortPathToLongPath(const CString& short_path, CString* long_path) {
+  ASSERT1(long_path);
+
+  TCHAR long_name[MAX_PATH] = {0};
+  if (!::GetLongPathName(short_path, long_name, MAX_PATH)) {
+    return HRESULTFromLastError();
+  }
+
+  *long_path = long_name;
+  return S_OK;
+}
+
+HRESULT FindFilesEx(const CString& dir,
+                    const CString& pattern,
+                    std::vector<CString>* files) {
+  return detail::FindFilesEx(dir, pattern, files, &detail::IsFile);
+}
+
+HRESULT FindFiles(const CString& dir,
+                  const CString& pattern,
+                  std::vector<CString>* files) {
+  return detail::FindFilesEx(dir, pattern, files, &detail::AllFiles);
+}
+
+HRESULT FindSubDirectories(const CString& dir,
+                           const CString& pattern,
+                           std::vector<CString>* files) {
+  return detail::FindFilesEx(dir, pattern, files, &detail::IsDirectory);
+}
+
+HRESULT FindFileRecursive(const CString& dir,
+                          const CString& pattern,
+                          std::vector<CString>* files) {
+  ASSERT1(files);
+
+  std::vector<CString> temp_files;
+  HRESULT hr = FindFilesEx(dir, pattern, &temp_files);
+  if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) && FAILED(hr)) {
+    return hr;
+  }
+
+  for (size_t i = 0; i < temp_files.size(); ++i) {
+    files->push_back(ConcatenatePath(dir, temp_files[i]));
+  }
+
+  std::vector<CString> sub_dirs;
+  hr = FindSubDirectories(dir, _T("*"), &sub_dirs);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  for (size_t i = 0; i < sub_dirs.size(); ++i) {
+    const CString& sub_dir = sub_dirs[i];
+    if (sub_dir == _T(".") || sub_dir == _T("..")) {
+      continue;
+    }
+
+    CString path = ConcatenatePath(dir, sub_dir);
+    hr = FindFileRecursive(path, pattern, files);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  return S_OK;
+}
+
+}  // namespace omaha
+
diff --git a/base/path.h b/base/path.h
new file mode 100644
index 0000000..2a3ce1e
--- /dev/null
+++ b/base/path.h
@@ -0,0 +1,97 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Path utility functions.
+
+#ifndef OMAHA_BASE_PATH_H_
+#define OMAHA_BASE_PATH_H_
+
+#include <atlstr.h>
+#include <vector>
+
+namespace omaha {
+
+// Get the starting path from the command string
+CString GetStartingPathFromString(const CString& s);
+
+// Get the trailing path from the command string
+CString GetTrailingPathFromString(const CString& s);
+
+// Get the file from the command string
+HRESULT GetFileFromCommandString(const TCHAR* s, CString* file);
+
+// Expands the string with embedded special folder variables
+HRESULT ExpandStringWithSpecialFolders(CString* str);
+
+// Normalize a path
+HRESULT NormalizePath(const TCHAR* path, CString* normalized_path);
+
+// Concatenate two paths together
+CString ConcatenatePath(const CString& path1, const CString& path2);
+
+// Get the file out of the file path
+CString GetFileFromPath(const CString& path);
+
+// Get the directory from the path
+CString GetDirectoryFromPath(const CString& path);
+
+// Remove the extension from the path.
+CString GetPathRemoveExtension(const CString& path);
+
+// Returns true iff path is an absolute path (starts with a drive name)
+bool IsAbsolutePath(const TCHAR* path);
+
+// Makes sure the path is enclosed with double quotation marks.
+void EnclosePath(CString* path);
+
+// Used to enclose paths that are typically used with LocalServer32 entries.
+// Unenclosed LocalServer32 entries with spaces are not recommended because
+// the LocalServer32 entry is a command line, not just an EXE path.
+// DLL paths should not be enclosed, because InProcServer32 entries are just
+// a DLL path and not a command line.
+CString EnclosePathIfExe(const CString& module_path);
+
+// remove any double quotation masks from an enclosed path
+void UnenclosePath(CString* path);
+
+// Removes a trailing double quote from a path. ::CommandLineToArgvW() has some
+// strange treatment for quotation marks and backslashes, as detailed here:
+// http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx
+//
+// So an argument containing a directory path with a trailing backslash:
+// <<prog.exe /dir "c:\a dir\">>
+// is incorrectly parsed with a double-quote at the end:
+// <<c:\a dir">>.
+void RemoveMismatchedEndQuoteInDirectoryPath(CString* directory_path);
+
+// Converts the short path name to long name.
+HRESULT ShortPathToLongPath(const CString& short_path, CString* long_path);
+
+// Returns a list of files that match the criteria.
+HRESULT FindFiles(const CString& dir,
+                  const CString& pattern,
+                  std::vector<CString>* files);
+
+HRESULT FindFilesEx(const CString& dir,
+                    const CString& pattern,
+                    std::vector<CString>* files);
+
+HRESULT FindFileRecursive(const CString& dir,
+                          const CString& pattern,
+                          std::vector<CString>* files);
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_PATH_H_
diff --git a/base/path_unittest.cc b/base/path_unittest.cc
new file mode 100644
index 0000000..ea325a1
--- /dev/null
+++ b/base/path_unittest.cc
@@ -0,0 +1,413 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Path-utility unit tests.
+
+#include <vector>
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(PathTest, IsAbsolutePath) {
+  ASSERT_TRUE(IsAbsolutePath(L"C:\\Foo.bar"));
+  ASSERT_TRUE(IsAbsolutePath(L"\\\\user-laptop1\\path"));
+  ASSERT_FALSE(IsAbsolutePath(L"windows\\system32"));
+}
+
+// EnclosePath is overzealous and quotes a path even though no spaces exists.
+TEST(PathTest, EnclosePath) {
+  CString path;
+  EnclosePath(&path);
+  EXPECT_STREQ(_T(""), path);
+
+  path = _T("");
+  EnclosePath(&path);
+  EXPECT_STREQ(_T(""), path);
+
+  path = _T("a");
+  EnclosePath(&path);
+  EXPECT_STREQ(_T("\"a\""), path);
+
+  path = _T("a b");
+  EnclosePath(&path);
+  EXPECT_STREQ(_T("\"a b\""), path);
+
+  path = " a b ";
+  EnclosePath(&path);
+  EXPECT_STREQ(_T("\" a b \""), path);
+
+  path = _T("\"a b\"");
+  EnclosePath(&path);
+  EXPECT_STREQ(_T("\"a b\""), path);
+
+  path = _T("c:\\Windows\\notepad.exe");
+  EnclosePath(&path);
+  EXPECT_STREQ(_T("\"c:\\Windows\\notepad.exe\""), path);
+
+  path = _T("c:\\Program Files\\Google\\Common\\Google Update");
+  EnclosePath(&path);
+  EXPECT_STREQ(_T("\"c:\\Program Files\\Google\\Common\\Google Update\""),
+               path);
+}
+
+// EnclosePath encloses a string that has a quote only at the beginning or end.
+TEST(PathTest, EnclosePath_OnlyOneQuote) {
+  ExpectAsserts expect_asserts;
+  CString path = _T("\"a b");
+  EnclosePath(&path);
+  EXPECT_STREQ(_T("\"\"a b\""), path);
+
+  path = _T("a b\"");
+  EnclosePath(&path);
+  EXPECT_STREQ(_T("\"a b\"\""), path);
+}
+
+// EnclosePath does not look at the middle of the string.
+TEST(PathTest, EnclosePath_QuoteInMiddle) {
+  CString path = _T("a\" b");
+  EnclosePath(&path);
+  EXPECT_STREQ(_T("\"a\" b\""), path);
+}
+
+TEST(PathTest, EnclosePath_SingleQuotes) {
+  CString path = _T("'foo'");
+  EnclosePath(&path);
+  EXPECT_STREQ(_T("\"'foo'\""), path);
+}
+
+TEST(PathTest, EnclosePathIfExe) {
+  CString original_path;
+  CString new_path;
+  new_path = EnclosePathIfExe(original_path);
+  EXPECT_STREQ(original_path, new_path);
+
+  original_path = _T("");
+  new_path = EnclosePathIfExe(original_path);
+  EXPECT_STREQ(original_path, new_path);
+
+  original_path = _T("a");
+  new_path = EnclosePathIfExe(original_path);
+  EXPECT_STREQ(original_path, new_path);
+
+  original_path = _T("a b");
+  new_path = EnclosePathIfExe(original_path);
+  EXPECT_STREQ(original_path, new_path);
+
+  original_path = " a b ";
+  new_path = EnclosePathIfExe(original_path);
+  EXPECT_STREQ(original_path, new_path);
+
+  original_path = _T("\"a b\"");
+  new_path = EnclosePathIfExe(original_path);
+  EXPECT_STREQ(original_path, new_path);
+
+  original_path = _T("c:\\Windows\\notepad.exe");
+  new_path = EnclosePathIfExe(original_path);
+  EXPECT_STREQ(_T("\"c:\\Windows\\notepad.exe\""), new_path);
+
+  original_path = _T("c:\\Program Files\\Google\\Update");
+  new_path = EnclosePathIfExe(original_path);
+  EXPECT_STREQ(original_path, new_path);
+
+  original_path = _T("c:\\Progra Files\\Google\\Update\\1.1.1.1\\goopdate.dll");
+  new_path = EnclosePathIfExe(original_path);
+  EXPECT_STREQ(original_path, new_path);
+
+  original_path = _T("c:\\Prog F\\Googl\\Update\\GoogleUpdate.exe");
+  new_path = EnclosePathIfExe(original_path);
+  EXPECT_STREQ(_T("\"c:\\Prog F\\Googl\\Update\\GoogleUpdate.exe\""), new_path);
+}
+
+TEST(PathTest, ConcatenatePath) {
+  CString expected_path("C:\\first\\part\\second\\part");
+
+  CString start("C:\\first\\part");
+  CString start_slash("C:\\first\\part\\");
+  CString end("second\\part");
+  CString end_slash("\\second\\part");
+
+  EXPECT_STREQ(expected_path, ConcatenatePath(start, end));
+  EXPECT_STREQ(expected_path, ConcatenatePath(start_slash, end));
+  EXPECT_STREQ(expected_path, ConcatenatePath(start, end_slash));
+  EXPECT_STREQ(expected_path, ConcatenatePath(start_slash, end_slash));
+}
+
+TEST(PathTest, ConcatenatePath_PathTooLong) {
+  CString two_hundred_char_root_path(_T("C:\\reallllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllly\\loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"));  // NOLINT
+  CString two_hundred_char_path(_T("realllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllly\\loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"));  // NOLINT
+  EXPECT_EQ(200, two_hundred_char_root_path.GetLength());
+  CString fifty_eight_char_name(
+      _T("filenaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaame"));
+  EXPECT_EQ(58, fifty_eight_char_name.GetLength());
+  CString fifty_nine_char_name(
+      _T("filenaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaame"));
+  EXPECT_EQ(59, fifty_nine_char_name.GetLength());
+  CString sixty_char_name(
+      _T("filenaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaame"));
+  EXPECT_EQ(60, sixty_char_name.GetLength());
+
+  // Adding the '\' makes it 259 chars.
+  CString result = ConcatenatePath(two_hundred_char_root_path,
+                                   fifty_eight_char_name);
+  EXPECT_STREQ(_T("C:\\reallllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllly\\loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong\\filenaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaame"),  // NOLINT
+               result);
+
+  // Adding the '\' makes it 260 chars.
+  ExpectAsserts expect_asserts;
+  result = ConcatenatePath(two_hundred_char_root_path,
+                                   fifty_nine_char_name);
+  EXPECT_TRUE(result.IsEmpty());
+
+  // Adding the '\' makes it 261 chars.
+  result = ConcatenatePath(two_hundred_char_root_path, sixty_char_name);
+  EXPECT_TRUE(result.IsEmpty());
+
+  // Test for buffer overflow on long strings.
+}
+
+TEST(PathTest, ConcatenatePath_EmptyString) {
+  EXPECT_STREQ(_T("bar.exe"), ConcatenatePath(_T(""), _T("bar.exe")));
+  EXPECT_STREQ(_T("foo"), ConcatenatePath(_T("foo"), _T("")));
+  // This is not what I would expect, but it is what the API does.
+  EXPECT_STREQ(_T("\\"), ConcatenatePath(_T(""), _T("")));
+}
+
+// TODO(omaha): The expected and actual values are reversed throughout.
+
+TEST(PathTest, ShortPathToLongPath) {
+  CString expected_path("C:\\Program Files");
+  CString short_path("C:\\Progra~1");
+
+  CString long_path;
+  ASSERT_SUCCEEDED(ShortPathToLongPath(short_path, &long_path));
+  ASSERT_STREQ(expected_path, long_path);
+}
+
+TEST(PathTest, FindFilesTest) {
+  GUID guid = GUID_NULL;
+  ASSERT_SUCCEEDED(::CoCreateGuid(&guid));
+
+  TCHAR path[MAX_PATH] = {0};
+  ASSERT_NE(::GetTempPath(MAX_PATH, path), 0);
+
+  CString dir = ConcatenatePath(path, GuidToString(guid));
+  EXPECT_FALSE(dir.IsEmpty());
+  EXPECT_FALSE(File::Exists(dir));
+
+  // Test with non-existent dir.
+  std::vector<CString> files;
+  EXPECT_FAILED(FindFiles(dir, _T("*.txt"), &files));
+  EXPECT_EQ(files.size(), 0);
+
+  // Test with empty dir.
+  EXPECT_NE(::CreateDirectory(dir, NULL), 0);
+  files.clear();
+  EXPECT_EQ(FindFiles(dir, _T("asdf.txt"), &files), 0x80070002);
+  EXPECT_EQ(files.size(), 0);
+
+  CString filename1 = _T("one.txt");
+  CString filepath1 = ConcatenatePath(dir, filename1);
+  CString filename2 = _T("two_en.txt");
+  CString filepath2 = ConcatenatePath(dir, filename2);
+
+  // Test with dir containing one file.
+  scoped_hfile handle1(::CreateFile(filepath1, GENERIC_READ, FILE_SHARE_READ,
+                                    NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
+                                    NULL));
+  EXPECT_TRUE(valid(handle1));
+  files.clear();
+  EXPECT_SUCCEEDED(FindFiles(dir, _T("*.txt"), &files));
+  EXPECT_EQ(files.size(), 1);
+  EXPECT_STREQ(files[0], filename1);
+
+  files.clear();
+  EXPECT_SUCCEEDED(FindFiles(dir, _T("o*.txt"), &files));
+  EXPECT_EQ(files.size(), 1);
+  EXPECT_STREQ(files[0], filename1);
+
+  files.clear();
+  EXPECT_SUCCEEDED(FindFiles(dir, filepath1, &files));
+  EXPECT_EQ(files.size(), 1);
+  EXPECT_STREQ(files[0], filename1);
+
+  files.clear();
+  EXPECT_EQ(FindFiles(dir, _T("t*.txt"), &files), 0x80070002);
+  EXPECT_EQ(files.size(), 0);
+
+  // Test with dir containing two files.
+  scoped_hfile handle2(::CreateFile(filepath2, GENERIC_READ, FILE_SHARE_READ,
+                                    NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
+                                    NULL));
+  EXPECT_TRUE(valid(handle2));
+  files.clear();
+  EXPECT_SUCCEEDED(FindFiles(dir, _T("*.txt"), &files));
+  EXPECT_EQ(files.size(), 2);
+  EXPECT_STREQ(files[0], filename1);
+  EXPECT_STREQ(files[1], filename2);
+
+  files.clear();
+  EXPECT_SUCCEEDED(FindFiles(dir, _T("o*.txt"), &files));
+  EXPECT_EQ(files.size(), 1);
+  EXPECT_STREQ(files[0], filename1);
+
+  files.clear();
+  EXPECT_SUCCEEDED(FindFiles(dir, _T("t*.txt"), &files));
+  EXPECT_EQ(files.size(), 1);
+  EXPECT_STREQ(files[0], filename2);
+
+  files.clear();
+  EXPECT_EQ(FindFiles(dir, _T("asdf.txt"), &files), 0x80070002);
+  EXPECT_EQ(files.size(), 0);
+
+  reset(handle1);
+  EXPECT_SUCCEEDED(File::Remove(filepath1));
+  reset(handle2);
+  EXPECT_SUCCEEDED(File::Remove(filepath2));
+  EXPECT_NE(::RemoveDirectory(dir), 0);
+}
+
+namespace detail {
+
+struct Directory {
+  Directory() {}
+  explicit Directory(const CString& name) : dir_name(name) {}
+
+  CString dir_name;
+  std::vector<Directory> sub_dirs;
+  std::vector<CString> files;
+};
+
+void ConvertDirectoryStructureToFiles(const Directory& directory,
+                                      const CString& dir_path,
+                                      std::vector<CString>* files) {
+  ASSERT_TRUE(files != NULL);
+  ASSERT_HRESULT_SUCCEEDED(CreateDir(dir_path, NULL));
+
+  for (size_t i = 0; i < directory.files.size(); ++i) {
+    const CString& file = ConcatenatePath(dir_path, directory.files[i]);
+    scoped_hfile handle(::CreateFile(file, GENERIC_READ, FILE_SHARE_READ,
+                                      NULL, CREATE_ALWAYS,
+                                      FILE_ATTRIBUTE_NORMAL,
+                                      NULL));
+    EXPECT_TRUE(valid(handle));
+    files->push_back(file);
+  }
+
+  for (size_t i = 0; i < directory.sub_dirs.size(); ++i) {
+    ConvertDirectoryStructureToFiles(
+        directory.sub_dirs[i],
+        ConcatenatePath(dir_path, directory.sub_dirs[i].dir_name),
+        files);
+  }
+}
+
+void CreateTestDirectoryStructure(Directory* root_dir) {
+  EXPECT_TRUE(root_dir != NULL);
+  GUID guid = GUID_NULL;
+  ASSERT_HRESULT_SUCCEEDED(::CoCreateGuid(&guid));
+
+  TCHAR path[MAX_PATH] = {0};
+  ASSERT_NE(0, ::GetTempPath(MAX_PATH, path));
+
+  CString dir = ConcatenatePath(path, GuidToString(guid));
+  EXPECT_FALSE(dir.IsEmpty());
+  EXPECT_FALSE(File::Exists(dir));
+
+  root_dir->dir_name = dir;
+  root_dir->files.push_back(_T("test1.txt"));
+  root_dir->files.push_back(_T("test2.txt"));
+
+  Directory sub_dir1_level1(_T("sub_dir1_level1"));
+  sub_dir1_level1.files.push_back(_T("sub_dir1_level1_test1.txt"));
+  Directory sub_dir1_level2(_T("sub_dir1_level2"));
+  sub_dir1_level2.files.push_back(_T("sub_dir1_level2_test1.txt"));
+  sub_dir1_level1.sub_dirs.push_back(sub_dir1_level2);
+  root_dir->sub_dirs.push_back(sub_dir1_level1);
+
+  Directory sub_dir2_level1(_T("sub_dir2_level1"));
+  sub_dir2_level1.files.push_back(_T("sub_dir2_level1_test1.txt"));
+  root_dir->sub_dirs.push_back(sub_dir2_level1);
+}
+}  // detail.
+
+TEST(PathTest, FindFileRecursiveTest) {
+  detail::Directory dir;
+  detail::CreateTestDirectoryStructure(&dir);
+
+  std::vector<CString> expected_files;
+  detail::ConvertDirectoryStructureToFiles(dir, dir.dir_name, &expected_files);
+
+  // Call the test method.
+  std::vector<CString> files;
+  ASSERT_HRESULT_SUCCEEDED(FindFileRecursive(dir.dir_name,
+                                             _T("*test*.txt"),
+                                             &files));
+
+  // Validate the results.
+  ASSERT_EQ(expected_files.size(), files.size());
+  for (size_t i = 0; i < expected_files.size(); ++i) {
+    EXPECT_STREQ(expected_files[i], files[i]);
+  }
+
+  // Cleanup.
+  ASSERT_HRESULT_SUCCEEDED(DeleteDirectory(dir.dir_name));
+}
+
+TEST(PathTest, FindFileRecursiveTest_Empty) {
+  GUID guid = GUID_NULL;
+  ASSERT_HRESULT_SUCCEEDED(::CoCreateGuid(&guid));
+
+  TCHAR path[MAX_PATH] = {0};
+  ASSERT_NE(0, ::GetTempPath(MAX_PATH, path));
+
+  CString dir = ConcatenatePath(path, GuidToString(guid));
+  EXPECT_FALSE(dir.IsEmpty());
+  EXPECT_FALSE(File::Exists(dir));
+
+  ASSERT_HRESULT_SUCCEEDED(CreateDir(dir, NULL));
+
+  // Call the test method.
+  std::vector<CString> files;
+  ASSERT_HRESULT_SUCCEEDED(FindFileRecursive(dir, _T("*test*.txt"),
+                                             &files));
+
+  // Validate results.
+  ASSERT_EQ(0, files.size());
+
+  // Cleanup.
+  ASSERT_HRESULT_SUCCEEDED(DeleteDirectory(dir));
+}
+
+TEST(PathTest, FindFileRecursiveTest_DirNotCreated) {
+  GUID guid = GUID_NULL;
+  ASSERT_HRESULT_SUCCEEDED(::CoCreateGuid(&guid));
+
+  TCHAR path[MAX_PATH] = {0};
+  ASSERT_NE(0, ::GetTempPath(MAX_PATH, path));
+
+  CString dir = ConcatenatePath(path, GuidToString(guid));
+  EXPECT_FALSE(dir.IsEmpty());
+  EXPECT_FALSE(File::Exists(dir));
+
+  // Call the test method.
+  std::vector<CString> files;
+  ASSERT_HRESULT_FAILED(FindFileRecursive(dir, _T("*test*.txt"), &files));
+}
+
+}  // namespace omaha
+
diff --git a/base/pe_utils.cc b/base/pe_utils.cc
new file mode 100644
index 0000000..ce4ca22
--- /dev/null
+++ b/base/pe_utils.cc
@@ -0,0 +1,219 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Utility functions related to PE files (executables)
+
+#include "omaha/base/pe_utils.h"
+
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/scoped_any.h"
+
+namespace omaha {
+
+namespace {
+
+// Not really necessary as long as running on x86 architecture throughout, but
+// what the hell
+#if defined(BIG_ENDIAN)
+uint32 GetUint32LE(void const * const p) {
+  uint8 const * const pu = reinterpret_cast<uint8 const * const>(p);
+  uint32 i = pu[0] | pu[1]<<8 | pu[2]<<16 | pu[3]<<24;
+  return i;
+}
+
+void PutUint32LE(uint32 i, void * const p) {
+  uint8 * const pu = reinterpret_cast<uint8 * const>(p);
+  pu[0] = i & 0xff;
+  pu[1] = (i >>  8) & 0xff;
+  pu[2] = (i >> 16) & 0xff;
+  pu[3] = (i >> 24) & 0xff;
+}
+#else  // LITTLE_ENDIAN
+inline uint32 GetUint32LE(void const * const p) {
+  uint32 const * const pu = reinterpret_cast<uint32 const * const>(p);
+  return *pu;
+}
+
+inline void PutUint32LE(uint32 i, void * const p) {
+  uint32 * const pu = reinterpret_cast<uint32 * const>(p);
+  *pu = i;
+}
+#endif
+
+// Magic PE constants
+const uint32 kPEHeaderOffset         = 60;
+const uint32 kPEHeaderChecksumOffset = 88;
+const uint32 kPEHeaderSizeMin        = 160;
+const char magic_EXE_header[] = "MZ\0\0";
+const char magic_PE_header[]  = "PE\0\0";
+
+}  // namespace
+
+HRESULT SetPEChecksum(const TCHAR *filename, uint32 checksum) {
+  // Write the checksum field of the Windows NT-specific "optional" header.
+  // Use Windows API calls rather than C library calls so that it will be
+  // really a small routine when used in the stub executable.
+
+  ASSERT(filename, (L""));
+
+  scoped_hfile file(::CreateFile(filename,
+                                 GENERIC_READ | GENERIC_WRITE,
+                                 0,
+                                 NULL,
+                                 OPEN_EXISTING,
+                                 FILE_ATTRIBUTE_NORMAL,
+                                 NULL));
+  if (!file)
+    return HRESULTFromLastError();
+
+  size_t size = ::GetFileSize(get(file), NULL);
+  if (size == INVALID_FILE_SIZE)
+    return HRESULTFromLastError();
+
+  scoped_file_mapping mapping(::CreateFileMapping(get(file),
+                                                  NULL,
+                                                  PAGE_READWRITE,
+                                                  0,
+                                                  0,
+                                                  NULL));
+  if (!mapping)
+    return HRESULTFromLastError();
+
+  scoped_file_view file_data(::MapViewOfFile(get(mapping),
+                                             FILE_MAP_WRITE,
+                                             0,
+                                             0,
+                                             size));
+  if (!file_data)
+    return HRESULTFromLastError();
+
+  uint8 * image = reinterpret_cast<uint8 *>(get(file_data));
+
+  return SetPEChecksumToBuffer(image, size, checksum);
+}
+
+HRESULT GetPEChecksum(const TCHAR *filename, uint32 * checksum) {
+  // Read the checksum field out of the Windows NT-specific "optional" header.
+  // Use Windows API calls rather than C library calls so that it will be
+  // really a small routine when used in the stub executable.
+
+  ASSERT(filename, (L""));
+  ASSERT(checksum, (L""));
+
+  scoped_hfile file(::CreateFile(filename,
+                                 GENERIC_READ,
+                                 FILE_SHARE_READ,
+                                 NULL,
+                                 OPEN_EXISTING,
+                                 FILE_ATTRIBUTE_READONLY,
+                                 NULL));
+  if (!file)
+    return HRESULTFromLastError();
+
+  size_t size = ::GetFileSize(get(file), NULL);
+  if (size == INVALID_FILE_SIZE)
+    return HRESULTFromLastError();
+
+  scoped_file_mapping mapping(::CreateFileMapping(get(file),
+                              NULL,
+                              PAGE_READONLY,
+                              0,
+                              0,
+                              NULL));
+  if (!mapping)
+    return HRESULTFromLastError();
+
+  scoped_file_view file_data(::MapViewOfFile(get(mapping),
+                             FILE_MAP_READ,
+                             0,
+                             0,
+                             size));
+  if (!file_data)
+    return HRESULTFromLastError();
+
+  uint8 * image = reinterpret_cast<uint8 *>(get(file_data));
+
+  return GetPEChecksumFromBuffer(image, size, checksum);
+}
+
+HRESULT SetPEChecksumToBuffer(uint8 *buffer, size_t size, uint32 checksum) {
+  // Sanity checks
+  if (size < 64) {
+    ASSERT(false, (L"File too short to be valid executable"));
+    return E_FAIL;
+  }
+
+  uint32 x = GetUint32LE(magic_EXE_header);
+  if (::memcmp(buffer, &x, 2)) {
+    ASSERT(false, (L"Missing executable's magic number"));
+    return E_FAIL;
+  }
+
+  uint32 peheader = GetUint32LE(buffer + kPEHeaderOffset);
+  if (size < peheader + kPEHeaderSizeMin) {
+    ASSERT(false, (L"Too small given PE header size"));
+    return E_FAIL;
+  }
+
+  x = GetUint32LE(magic_PE_header);
+  if (::memcmp(buffer + peheader, &x, 4)) {
+    ASSERT(false, (L"Missing PE header magic number"));
+    return E_FAIL;
+  }
+
+  // Finally, write the checksum
+  PutUint32LE(checksum, &x);
+  ::memcpy(buffer + peheader + kPEHeaderChecksumOffset, &x, 4);
+
+  return S_OK;
+}
+
+HRESULT GetPEChecksumFromBuffer(const unsigned char *buffer,
+                                size_t size,
+                                uint32 *checksum) {
+  // Sanity checks
+  if (size < 64) {
+    ASSERT(false, (L"File too short to be valid executable"));
+    return E_FAIL;
+  }
+
+  uint32 x = GetUint32LE(magic_EXE_header);
+  if (::memcmp(buffer, &x, 2)) {
+    ASSERT(false, (L"Missing executable's magic number"));
+    return E_FAIL;
+  }
+
+  uint32 peheader = GetUint32LE(buffer + kPEHeaderOffset);
+  if (size < peheader + kPEHeaderSizeMin) {
+    ASSERT(false, (L"Too small given PE header size"));
+    return E_FAIL;
+  }
+
+  x = GetUint32LE(magic_PE_header);
+  if (::memcmp(buffer + peheader, &x, 4)) {
+    ASSERT(false, (L"Missing PE header magic number"));
+    return E_FAIL;
+  }
+
+  // Finally, read the checksum
+
+  *checksum = GetUint32LE(buffer + peheader + kPEHeaderChecksumOffset);
+
+  return S_OK;
+}
+
+}  // namespace omaha
+
diff --git a/common/pe_utils.h b/base/pe_utils.h
similarity index 100%
rename from common/pe_utils.h
rename to base/pe_utils.h
diff --git a/base/pe_utils_unittest.cc b/base/pe_utils_unittest.cc
new file mode 100644
index 0000000..56b11c6
--- /dev/null
+++ b/base/pe_utils_unittest.cc
@@ -0,0 +1,87 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Unittests for pe_utils
+
+
+#include "omaha/base/debug.h"
+#include "omaha/base/file.h"
+#include "omaha/base/pe_utils.h"
+#include "omaha/base/utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(PEUtilsTest, PEUtils) {
+  // Get some known directories
+  CString windows_dir;
+  CString temp_dir;
+  DWORD dw = ::GetEnvironmentVariable(L"SystemRoot",
+                                      CStrBuf(windows_dir, MAX_PATH), MAX_PATH);
+  ASSERT_TRUE(dw);
+  dw = ::GetEnvironmentVariable(L"TEMP", CStrBuf(temp_dir, MAX_PATH), MAX_PATH);
+  ASSERT_TRUE(dw);
+
+  // Get a known executable to play with
+  CString notepad(windows_dir + L"\\NOTEPAD.EXE");
+  ASSERT_TRUE(File::Exists(notepad));
+
+  CString temp_exe(temp_dir + L"\\pe_utils_test.exe");
+  if (File::Exists(temp_exe)) {
+    File::Remove(temp_exe);
+  }
+  ASSERT_FALSE(File::Exists(temp_exe));
+  ASSERT_SUCCEEDED(File::Copy(notepad, temp_exe, true));
+
+  // Stomp on its checksum and check the result
+  const unsigned int kChk1 = 0xFEE1BAD;
+  const unsigned int kChk2 = 0x600DF00D;
+
+  unsigned int checksum = 0;
+
+  // Test Get/SetPEChecksum
+  ASSERT_SUCCEEDED(SetPEChecksum(temp_exe, kChk1));
+  ASSERT_SUCCEEDED(GetPEChecksum(temp_exe, &checksum));
+  ASSERT_EQ(kChk1, checksum);
+
+  ASSERT_SUCCEEDED(SetPEChecksum(temp_exe, kChk2));
+  ASSERT_SUCCEEDED(GetPEChecksum(temp_exe, &checksum));
+  ASSERT_EQ(kChk2, checksum);
+
+  // Test GetPEChecksumFromBuffer/SetPEChecksumToBuffer
+  std::vector<byte> buffer;
+  ASSERT_SUCCEEDED(ReadEntireFile(temp_exe, 0, &buffer));
+
+  int buffer_data_len = buffer.size();
+  uint8 *buffer_data = reinterpret_cast<uint8*>(&buffer.front());
+
+  ASSERT_SUCCEEDED(SetPEChecksumToBuffer(buffer_data, buffer_data_len, kChk1));
+  ASSERT_SUCCEEDED(GetPEChecksumFromBuffer(buffer_data,
+                                           buffer_data_len,
+                                           &checksum));
+  ASSERT_EQ(kChk1, checksum);
+
+  ASSERT_SUCCEEDED(SetPEChecksumToBuffer(buffer_data, buffer_data_len, kChk2));
+  ASSERT_SUCCEEDED(GetPEChecksumFromBuffer(buffer_data,
+                                           buffer_data_len,
+                                           &checksum));
+  ASSERT_EQ(kChk2, checksum);
+
+  // Clean up
+  ASSERT_SUCCEEDED(File::Remove(temp_exe));
+}
+
+}  // namespace omaha
+
diff --git a/base/popup_menu.cc b/base/popup_menu.cc
new file mode 100644
index 0000000..bfb15e5
--- /dev/null
+++ b/base/popup_menu.cc
@@ -0,0 +1,464 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/popup_menu.h"
+
+#include <windows.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+// Hold owner draw data
+struct OwnerDrawData {
+  HFONT font;
+  CString text;
+  HICON icon;
+
+  OwnerDrawData(HFONT f, const TCHAR* t, HICON i) {
+    font = f;
+    text = t;
+    icon = i;
+  }
+};
+
+
+// Default constructor
+PopupMenu::PopupMenu()
+    : wnd_(NULL) {
+  reset(menu_, ::CreatePopupMenu());
+  ASSERT1(menu_);
+}
+
+// Constructor
+PopupMenu::PopupMenu(HINSTANCE inst, const TCHAR* name)
+    : wnd_(NULL) {
+  LoadFromResource(inst, name);
+  ASSERT1(menu_);
+}
+
+// Destructor
+PopupMenu::~PopupMenu() {
+}
+
+// Load from resource
+bool PopupMenu::LoadFromResource(HINSTANCE inst, const TCHAR* name) {
+  reset(menu_, GetSubMenu(reinterpret_cast<HMENU>(::LoadMenu(inst, name)), 0));
+  return get(menu_) != NULL;
+}
+
+// Append menu item
+bool PopupMenu::AppendMenuItem(int menu_item_id, const TCHAR* text) {
+  return AppendMenuItem(menu_item_id, text, NULL);
+}
+
+// Append menu item
+bool PopupMenu::AppendMenuItem(int menu_item_id,
+                               const TCHAR* text,
+                               const MenuItemDrawStyle* style) {
+  int count = ::GetMenuItemCount(get(menu_));
+  if (count == -1)
+    return false;
+
+  return InsertMenuItem(menu_item_id, count, true, text, style);
+}
+
+// Append separator
+bool PopupMenu::AppendSeparator() {
+  return AppendMenuItem(-1, NULL, NULL);
+}
+
+// Insert menu item
+bool PopupMenu::InsertMenuItem(int menu_item_id,
+                               int before_item,
+                               bool by_pos,
+                               const TCHAR* text) {
+  return InsertMenuItem(menu_item_id, before_item, by_pos, text, NULL);
+}
+
+// Helper function that populates the MENUITEMINFO structure and sets
+// accelerator keys for OWNERDRAW menu items
+MENUITEMINFO PopupMenu::PrepareMenuItemInfo(int menu_item_id, const TCHAR* text,
+                                            const MenuItemDrawStyle* style) {
+  // Fill in the MENUITEMINFO structure
+  MENUITEMINFO menuitem_info;
+  SetZero(menuitem_info);
+  menuitem_info.cbSize = sizeof(MENUITEMINFO);
+  menuitem_info.wID = menu_item_id;
+  if (text == NULL) {
+    menuitem_info.fMask = MIIM_FTYPE | MIIM_ID;
+    menuitem_info.fType = MFT_SEPARATOR;
+  } else {
+    if (!style) {
+      menuitem_info.fMask = MIIM_STRING | MIIM_ID;
+      menuitem_info.fType = MFT_STRING;
+      menuitem_info.dwTypeData = const_cast<TCHAR*>(text);
+    } else {
+      // Handle bold font style
+      HFONT font = NULL;
+      if (style->is_bold) {
+        font = GetBoldFont();
+      }
+
+      // Remove '&' if it is there
+      CString text_str(text);
+      int pos = String_FindChar(text_str, _T('&'));
+      if (pos != -1) {
+        if (pos + 1 < text_str.GetLength()) {
+          accelerator_keys_.Add(Char_ToLower(text_str[pos + 1]), menu_item_id);
+        }
+        ReplaceCString(text_str, _T("&"), _T(""));
+      }
+
+      // Set owner-draw related properties
+      OwnerDrawData* data = new OwnerDrawData(font, text_str, style->icon);
+      menuitem_info.fMask = MIIM_FTYPE | MIIM_DATA | MIIM_ID;
+      menuitem_info.fType = MFT_OWNERDRAW;
+      menuitem_info.dwItemData = reinterpret_cast<ULONG_PTR>(data);
+    }
+  }
+
+  return menuitem_info;
+}
+
+// Insert menu item
+bool PopupMenu::InsertMenuItem(int menu_item_id,
+                               int before_item,
+                               bool by_pos,
+                               const TCHAR* text,
+                               const MenuItemDrawStyle* style) {
+  MENUITEMINFO menuitem_info = PrepareMenuItemInfo(menu_item_id, text, style);
+  if (!::InsertMenuItem(get(menu_), before_item, by_pos, &menuitem_info))
+    return false;
+
+  return Redraw();
+}
+
+// Insert separator
+bool PopupMenu::InsertSeparator(int before_item, bool by_pos) {
+  return InsertMenuItem(-1, before_item, by_pos, NULL, NULL);
+}
+
+// Modify a given menu item
+bool PopupMenu::ModifyMenuItem(int menu_item, bool by_pos, const TCHAR* text,
+                               const MenuItemDrawStyle* style) {
+  // Get OWNERDRAW data for later deletion
+  MENUITEMINFO menuitem_info;
+  SetZero(menuitem_info);
+  menuitem_info.cbSize = sizeof(MENUITEMINFO);
+  menuitem_info.fMask = MIIM_FTYPE | MIIM_DATA;
+  if (!::GetMenuItemInfo(get(menu_), menu_item, by_pos, &menuitem_info)) {
+    return false;
+  }
+
+  OwnerDrawData* old_owner_data = NULL;
+  if ((menuitem_info.fType | MFT_OWNERDRAW) && menuitem_info.dwItemData) {
+    old_owner_data =
+        reinterpret_cast<OwnerDrawData *>(menuitem_info.dwItemData);
+  }
+
+  // Remove old accelerator mapping
+  int menu_item_id = by_pos ? ::GetMenuItemID(get(menu_), menu_item) :
+                              menu_item;
+  int key_pos = accelerator_keys_.FindVal(menu_item_id);
+  if (key_pos != -1) {
+    accelerator_keys_.RemoveAt(key_pos);
+  }
+
+  // Set new menu item info
+  menuitem_info = PrepareMenuItemInfo(menu_item_id, text, style);
+  if (!::SetMenuItemInfo(get(menu_), menu_item, by_pos, &menuitem_info)) {
+    return false;
+  }
+
+  // Delete old owner draw data
+  if (old_owner_data) {
+    delete old_owner_data;
+  }
+
+  // Redraw
+  return Redraw();
+}
+
+// Remove a menu item
+bool PopupMenu::RemoveMenuItem(int menu_item, bool by_pos) {
+  // Get OWNERDRAW data for later deletion
+  MENUITEMINFO menuitem_info;
+  SetZero(menuitem_info);
+  menuitem_info.cbSize = sizeof(MENUITEMINFO);
+  menuitem_info.fMask = MIIM_FTYPE | MIIM_DATA;
+  if (!::GetMenuItemInfo(get(menu_), menu_item, by_pos, &menuitem_info)) {
+    return false;
+  }
+
+  OwnerDrawData* old_owner_data = NULL;
+  if ((menuitem_info.fType | MFT_OWNERDRAW) && menuitem_info.dwItemData) {
+    old_owner_data =
+        reinterpret_cast<OwnerDrawData *>(menuitem_info.dwItemData);
+  }
+
+  // Remove the menu item
+  if (!::RemoveMenu(get(menu_), menu_item, by_pos ? MF_BYPOSITION :
+                                                    MF_BYCOMMAND)) {
+    return false;
+  }
+
+  // Remove old accelerator mapping
+  int menu_item_id = by_pos ? ::GetMenuItemID(get(menu_), menu_item) :
+                              menu_item;
+  int key_pos = accelerator_keys_.FindVal(menu_item_id);
+  if (key_pos != -1) {
+    accelerator_keys_.RemoveAt(key_pos);
+  }
+
+  // Delete old owner draw data
+  if (old_owner_data) {
+    delete old_owner_data;
+  }
+
+  // Redraw
+  return Redraw();
+}
+
+// Enable menu item
+bool PopupMenu::EnableMenuItem(int menu_item, bool by_pos, bool enabled) {
+  if (::EnableMenuItem(get(menu_), menu_item,
+                        (by_pos ? MF_BYPOSITION : MF_BYCOMMAND) |
+                        (enabled ? MF_ENABLED : MF_GRAYED)) == -1)
+    return false;
+
+  return Redraw();
+}
+
+// Get menu state
+bool PopupMenu::GetMenuState(int menu_item, bool by_pos, int* menu_state) {
+  int state = ::GetMenuState(get(menu_),
+                             menu_item, by_pos ? MF_BYPOSITION : MF_BYCOMMAND);
+  if (menu_state)
+    *menu_state = state;
+  return state != -1;
+}
+
+// Exists a menu item
+bool PopupMenu::ExistsMenuItem(int menu_item_id) {
+  return GetMenuState(menu_item_id, false, NULL);
+}
+
+// Redraw menu
+bool PopupMenu::Redraw() {
+  if (!wnd_)
+    return true;
+
+  return ::DrawMenuBar(wnd_) == TRUE;
+}
+
+// Track menu
+bool PopupMenu::Track() {
+  ASSERT1(wnd_);
+
+  // If we don't set it to be foreground, it will not stop tracking even
+  // if we click outside of menu.
+  ::SetForegroundWindow(wnd_);
+
+  POINT point = {0, 0};
+  VERIFY(::GetCursorPos(&point), (_T("")));
+
+  uint32 kFlags = TPM_LEFTALIGN  |
+                  TPM_RETURNCMD  |
+                  TPM_NONOTIFY   |
+                  TPM_LEFTBUTTON |
+                  TPM_VERTICAL;
+  int command = ::TrackPopupMenuEx(get(menu_),
+                                   kFlags,
+                                   point.x, point.y, wnd_, NULL);
+
+  if (command != 0)
+    ::SendMessage(wnd_, WM_COMMAND, command, 0);
+
+  return true;
+}
+
+// Handle WM_MEASUREITEM message
+bool PopupMenu::OnMeasureItem(MEASUREITEMSTRUCT* mi) {
+  ASSERT1(wnd_);
+
+  // Get owner draw data
+  ASSERT1(mi->itemData);
+  OwnerDrawData* data = reinterpret_cast<OwnerDrawData*>(mi->itemData);
+
+  // Get the DC
+  scoped_hdc dc;
+  reset(dc, ::GetDC(wnd_));
+
+  // Select the font
+  HFONT old_font = reinterpret_cast<HFONT>(::SelectObject(get(dc), data->font));
+  if (!old_font)
+    return false;
+
+  // compute the size of the text
+  SIZE size = {0, 0};
+  bool success = ::GetTextExtentPoint32(get(dc),
+                                        data->text.GetString(),
+                                        data->text.GetLength(),
+                                        &size) != 0;
+  if (success) {
+    mi->itemWidth = size.cx;
+    mi->itemHeight = size.cy;
+  }
+
+  // deselect the title font
+  ::SelectObject(get(dc), old_font);
+
+  return success;
+}
+
+// Handle WM_MDRAWITEM message
+bool PopupMenu::OnDrawItem(DRAWITEMSTRUCT* di) {
+  ASSERT1(di);
+
+  // Get owner draw data
+  ASSERT1(di->itemData);
+  OwnerDrawData* data = reinterpret_cast<OwnerDrawData*>(di->itemData);
+
+  // Select the font
+  HFONT prev_font = NULL;
+  if (data->font) {
+    prev_font = reinterpret_cast<HFONT>(::SelectObject(di->hDC, data->font));
+    if (!prev_font) {
+      return false;
+    }
+  }
+
+  // Draw the text per the menuitem state
+  int fg_color_idx =
+      (di->itemState & ODS_DISABLED) ?
+      COLOR_GRAYTEXT :
+      ((di->itemState & ODS_SELECTED) ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT);
+
+  int bg_color_idx =
+      (di->itemState & ODS_SELECTED) ? COLOR_HIGHLIGHT : COLOR_MENU;
+
+  bool success = DrawText(data->text, di, fg_color_idx, bg_color_idx);
+
+  // Restore the original font
+  if (prev_font) {
+    ::SelectObject(di->hDC, prev_font);
+  }
+
+  // Compute the width and height
+  int height = di->rcItem.bottom - di->rcItem.top + 1;
+  int width = static_cast<int>(::GetSystemMetrics(SM_CXMENUCHECK) *
+    (static_cast<double>(height) / ::GetSystemMetrics(SM_CYMENUCHECK)));
+
+  // Draw the icon
+  // TODO(omaha): Draw a grayed icon when the menuitem is disabled
+  if (success && data->icon) {
+    success = ::DrawIconEx(di->hDC,
+                           di->rcItem.left,
+                           di->rcItem.top,
+                           data->icon,
+                           width,
+                           height,
+                           0,
+                           NULL,
+                           DI_NORMAL) != 0;
+  }
+
+  return success;
+}
+
+// Draw the text
+bool PopupMenu::DrawText(const CString& text,
+                         DRAWITEMSTRUCT* di,
+                         int fg_color_idx,
+                         int bg_color_idx) {
+  // Set the appropriate foreground and background colors
+  COLORREF prev_fg_color = 0, prev_bg_color = 0;
+  prev_fg_color = ::SetTextColor(di->hDC, ::GetSysColor(fg_color_idx));
+  if (prev_fg_color == CLR_INVALID) {
+    return false;
+  }
+  prev_bg_color = ::SetBkColor(di->hDC, ::GetSysColor(bg_color_idx));
+  if (prev_bg_color == CLR_INVALID) {
+    return false;
+  }
+
+  // Draw the text
+  bool success = ::ExtTextOut(
+      di->hDC,
+      di->rcItem.left + ::GetSystemMetrics(SM_CXMENUCHECK) + 4,
+      di->rcItem.top,
+      ETO_OPAQUE,
+      &di->rcItem,
+      text.GetString(),
+      text.GetLength(),
+      NULL) == TRUE;
+
+  // Restore the original colors
+  ::SetTextColor(di->hDC, prev_fg_color);
+  ::SetBkColor(di->hDC, prev_bg_color);
+
+  return success;
+}
+
+// Handle WM_MENUCHAR message
+int PopupMenu::OnMenuChar(TCHAR key) {
+  int pos = accelerator_keys_.FindKey(Char_ToLower(key));
+  if (pos != -1)
+    return GetMenuPosFromID(accelerator_keys_.GetValueAt(pos));
+  else
+    return -1;
+}
+
+HFONT PopupMenu::GetBoldFont() {
+  if (!bold_font_) {
+    NONCLIENTMETRICS ncm;
+    SetZero(ncm);
+    ncm.cbSize = sizeof(NONCLIENTMETRICS);
+    if (::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0)) {
+      ncm.lfMenuFont.lfWeight = FW_BOLD;
+      reset(bold_font_, ::CreateFontIndirect(&ncm.lfMenuFont));
+    } else {
+      UTIL_LOG(LEVEL_ERROR, (_T("[PopupMenu::GetBoldFont]")
+                             _T("[failed to get system menu font][0x%x]"),
+                             HRESULTFromLastError()));
+    }
+  }
+
+  ASSERT1(bold_font_);
+
+  return get(bold_font_);
+}
+
+// Get menu pos from ID
+int PopupMenu::GetMenuPosFromID(int id) {
+  ASSERT1(id >= 0);
+  int count = ::GetMenuItemCount(get(menu_));
+  if (count > 0) {
+    for (int pos = 0; pos < count; ++pos) {
+      if (::GetMenuItemID(get(menu_), pos) == static_cast<UINT>(id)) {
+        return pos;
+      }
+    }
+  }
+
+  return -1;
+}
+
+}  // namespace omaha
+
diff --git a/base/popup_menu.h b/base/popup_menu.h
new file mode 100644
index 0000000..9428381
--- /dev/null
+++ b/base/popup_menu.h
@@ -0,0 +1,139 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef  OMAHA_COMMON_POPUP_MENU_H_
+#define  OMAHA_COMMON_POPUP_MENU_H_
+
+#include "omaha/base/debug.h"
+#include "omaha/base/scoped_any.h"
+
+namespace omaha {
+
+struct MenuItemDrawStyle {
+  bool is_bold;
+  HICON icon;
+
+  MenuItemDrawStyle() : is_bold(false), icon(NULL) {}
+};
+
+
+class PopupMenu {
+ public:
+  PopupMenu();
+  PopupMenu(HINSTANCE inst, const TCHAR* name);
+
+  ~PopupMenu();
+
+  // Load from resource
+  bool LoadFromResource(HINSTANCE inst, const TCHAR* name);
+
+  // Append menu item
+  bool AppendMenuItem(int menu_item_id, const TCHAR* text);
+  bool AppendMenuItem(int menu_item_id,
+                      const TCHAR* text,
+                      const MenuItemDrawStyle* style);
+
+  // Append separator
+  bool AppendSeparator();
+
+  // Helper function that populates the MENUITEMINFO structure and sets
+  // accelerator keys for OWNERDRAW menu items
+  MENUITEMINFO PrepareMenuItemInfo(int menu_item_id,
+                                   const TCHAR* text,
+                                   const MenuItemDrawStyle* style);
+
+  // Insert menu item
+  bool InsertMenuItem(int menu_item_id,
+                      int before_item,
+                      bool by_pos,
+                      const TCHAR* text);
+
+  bool InsertMenuItem(int menu_item_id,
+                      int before_item,
+                      bool by_pos,
+                      const TCHAR* text,
+                      const MenuItemDrawStyle* style);
+
+  // Insert separator
+  bool InsertSeparator(int before_item, bool by_pos);
+
+  // Modify a given menu item
+  bool ModifyMenuItem(int menu_item,
+                      bool by_pos,
+                      const TCHAR* text,
+                      const MenuItemDrawStyle* style);
+
+  // Remove menu item
+  bool RemoveMenuItem(int menu_item, bool by_pos);
+
+  // Enable menu item
+  bool EnableMenuItem(int menu_item, bool by_pos, bool enabled);
+
+  // Get menu state
+  bool GetMenuState(int menu_item, bool by_pos, int* menu_state);
+
+  // Exists a menu item
+  bool ExistsMenuItem(int menu_item_id);
+
+  // Get menu pos from ID
+  int GetMenuPosFromID(int id);
+
+  // Attach to the window
+  void AttachToWindow(HWND wnd) {
+    ASSERT1(wnd);
+    wnd_ = wnd;
+  }
+
+  // Redraw menu
+  bool Redraw();
+
+  // Track menu
+  bool Track();
+
+  // Handle WM_MEASUREITEM message
+  bool OnMeasureItem(MEASUREITEMSTRUCT* mi);
+
+  // Handle WM_MDRAWITEM message
+  bool OnDrawItem(DRAWITEMSTRUCT* di);
+
+  // Handle WM_MENUCHAR message
+  int OnMenuChar(TCHAR key);
+
+ private:
+  // Get bold font
+  HFONT GetBoldFont();
+
+  // Draw the text
+  bool DrawText(const CString& text,
+                DRAWITEMSTRUCT* di,
+                int fg_color_idx,
+                int bg_color_idx);
+
+  HWND wnd_;           // HWND associated with this menu
+  scoped_hmenu menu_;  // HMENU associated with this menu
+
+  // Bold font used in owner draw menu-item
+  scoped_hfont bold_font_;
+
+  // Accelerator key used in owner draw menu-item
+  CSimpleMap<TCHAR, int> accelerator_keys_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(PopupMenu);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_POPUP_MENU_H_
+
diff --git a/common/preprocessor_fun.h b/base/preprocessor_fun.h
similarity index 100%
rename from common/preprocessor_fun.h
rename to base/preprocessor_fun.h
diff --git a/base/proc_utils.cc b/base/proc_utils.cc
new file mode 100644
index 0000000..fa1e6ad
--- /dev/null
+++ b/base/proc_utils.cc
@@ -0,0 +1,463 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// proc_utils.cpp
+//
+// Useful functions that relate to process/thread manipulation/information
+// (Originally moved from utils.cpp)
+
+#include "omaha/base/proc_utils.h"
+
+#include <psapi.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/const_config.h"
+#include "omaha/base/const_timeouts.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/process.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/window_utils.h"
+
+namespace omaha {
+
+ProcessTerminator::ProcessTerminator(const CString& process_name)
+    : recursion_level_(0),
+      process_name_(process_name),
+      flash_window_(false),
+      session_id_(INVALID_SESSION_ID) {
+  MakeLowerCString(process_name_);
+}
+
+ProcessTerminator::ProcessTerminator(const CString& process_name,
+                                     const CString& user_sid)
+    : recursion_level_(0),
+      user_sid_(user_sid),
+      process_name_(process_name),
+      flash_window_(false),
+      session_id_(INVALID_SESSION_ID) {
+  MakeLowerCString(process_name_);
+}
+
+ProcessTerminator::ProcessTerminator(const CString& process_name,
+                                     const CString& user_sid,
+                                     int session_id)
+    : recursion_level_(0),
+      user_sid_(user_sid),
+      session_id_(session_id),
+      process_name_(process_name),
+      flash_window_(false) {
+  MakeLowerCString(process_name_);
+}
+
+ProcessTerminator::~ProcessTerminator() {
+  CloseAllHandles();
+}
+
+// Will close all currently opened handles.
+void ProcessTerminator::CloseAllHandles() {
+  UTIL_LOG(L3, (_T("[CloseAllHandles]")));
+  // Do clean up if we have opened handles.
+  for (size_t i = 0; i < process_handles_.size(); i++) {
+    VERIFY1(::CloseHandle(process_handles_[i]));
+  }
+
+  process_handles_.clear();
+}
+
+// Wait for a while till all process instances will die.
+bool ProcessTerminator::WaitForProcessInstancesToDie(
+    uint32 timeout_msec) const {
+  UTIL_LOG(L3, (_T("[WaitForProcessInstancesToDie]")));
+  size_t size = process_handles_.size();
+  scoped_array<HANDLE> handles(new HANDLE[size]);
+
+  for (size_t i = 0; i < size; i++) {
+    handles[i] = process_handles_[i];
+  }
+
+  DWORD wait_result = ::WaitForMultipleObjectsEx(size,
+                                                 handles.get(),
+                                                 true,
+                                                 timeout_msec,
+                                                 false);
+#pragma warning(disable : 4296)
+// C4296: '>=' : expression is always true
+  if ((wait_result >= WAIT_OBJECT_0) &&
+      (wait_result < WAIT_OBJECT_0 + size)) {
+    return true;
+  }
+#pragma warning(default : 4296)
+
+  UTIL_LOG(L3, (_T("WaitForProcessToDie timed out for '%s'. Waited for %d ms."),
+                process_name_, timeout_msec));
+  return false;
+}
+
+// Finds all process ids for the process of a given name.
+bool ProcessTerminator::FindProcessInstances() {
+  UTIL_LOG(L3, (_T("[FindProcessInstances]")));
+
+  DWORD exclude_mask = EXCLUDE_CURRENT_PROCESS;
+  if (!user_sid_.IsEmpty()) {
+    exclude_mask |= INCLUDE_ONLY_PROCESS_OWNED_BY_USER;
+  }
+
+  std::vector<CString> command_lines;
+  HRESULT hr = S_OK;
+  if (session_id_ != INVALID_SESSION_ID) {
+    hr = Process::FindProcessesInSession(session_id_,
+                                         exclude_mask,
+                                         process_name_,
+                                         true,
+                                         user_sid_,
+                                         command_lines,
+                                         &process_ids_);
+  } else {
+    hr = Process::FindProcesses(exclude_mask,
+                                process_name_,
+                                true,
+                                user_sid_,
+                                command_lines,
+                                &process_ids_);
+  }
+
+  return SUCCEEDED(hr) && !process_ids_.empty();
+}
+
+// Tries to kill all instances of the process that was specified in the
+// constructor.
+// 'method_mask' determines which technique to attempt.
+// 'was_found' is optional and can be NULL.
+// Returns S_OK if all instances were killed, S_FALSE if process wasn't running,
+// and E_FAIL if one or more instances weren't killed.
+// Always sets 'was_found' correctly, regardless of return value.
+HRESULT ProcessTerminator::KillTheProcess(uint32 timeout_msec,
+                                          bool* was_found,
+                                          uint32 method_mask,
+                                          bool flash_window) {
+  UTIL_LOG(L3, (_T("[KillTheProcess]")));
+  if (!FindProcessInstances()) {
+    if (was_found != NULL) {
+      *was_found = false;
+    }
+    return S_FALSE;  // process is not running, so don't return a FAILED hr
+  }
+
+  // If got here, found at least one process to kill
+  if (was_found != NULL) {
+    *was_found = true;
+  }
+
+  flash_window_ = flash_window;
+  // Try the nicest, cleanest method of closing a process: window messages
+  if (method_mask & KILL_METHOD_1_WINDOW_MESSAGE) {
+    if (PrepareToKill(KILL_METHOD_1_WINDOW_MESSAGE)) {
+      KillProcessViaWndMessages(timeout_msec);
+    }
+
+    // Are any instances of the process still running?
+    if (!FindProcessInstances()) {
+      return S_OK;  // killed them all
+    }
+  }
+
+  // Also nice method
+  if (method_mask & KILL_METHOD_2_THREAD_MESSAGE) {
+    if (PrepareToKill(KILL_METHOD_2_THREAD_MESSAGE)) {
+      KillProcessViaThreadMessages(timeout_msec);
+    }
+    // Are any instances of the process still running?
+    if (!FindProcessInstances()) {
+      return S_OK;  // killed them all
+    }
+  }
+
+  // the crude one.
+  if (method_mask & KILL_METHOD_4_TERMINATE_PROCESS) {
+    if (PrepareToKill(KILL_METHOD_4_TERMINATE_PROCESS)) {
+      KillProcessViaTerminate(timeout_msec);
+    }
+    // Are any instances of the process still running?
+    if (!FindProcessInstances()) {
+      return S_OK;  // killed them all
+    }
+
+    UTIL_LOG(LEVEL_ERROR, (_T("[ProcessTerminator::KillTheProcess]")
+                           _T("[totally unable to kill process '%s']"),
+                           process_name_));
+  }
+
+  return E_FAIL;
+}
+
+HRESULT ProcessTerminator::WaitForAllToDie(uint32 timeout_msec) {
+  UTIL_LOG(L3, (_T("[WaitForAllToDie]")));
+  if (!FindProcessInstances()) {
+    return S_OK;
+  }
+
+  if (PrepareToKill(KILL_METHOD_1_WINDOW_MESSAGE)) {
+    return WaitForProcessInstancesToDie(timeout_msec) ? S_OK :
+              HRESULT_FROM_WIN32(WAIT_TIMEOUT);
+  }
+
+  return E_FAIL;
+}
+
+// Given process_ids array will try to
+// open handle to each instance.
+// Leaves process handles open (in member process_handles_)
+// Will use access rights for opening appropriate for the purpose_of_opening.
+// This function recursively calls itself if by the time it tries to open
+// handles to process instances some of the processes died or naturally exited.
+bool ProcessTerminator::PrepareToKill(uint32 method_mask) {
+  UTIL_LOG(L3, (_T("[PrepareToKill]")));
+  uint32 desired_access = 0;
+
+  if (method_mask & KILL_METHOD_4_TERMINATE_PROCESS) {
+    desired_access = SYNCHRONIZE       |
+                     PROCESS_TERMINATE |
+                     PROCESS_QUERY_INFORMATION;
+  } else {
+    desired_access = SYNCHRONIZE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
+  }
+
+  // do clean up in case some handles are opened.
+  CloseAllHandles();
+
+  if (process_ids_.empty()) {
+    // no instances are running.
+    return false;
+  }
+
+  for (size_t i = 0; i < process_ids_.size(); i++) {
+    HANDLE handle = ::OpenProcess(desired_access, false, process_ids_[i]);
+    if (handle) {
+      process_handles_.push_back(handle);
+    } else {
+      if (::GetLastError() == ERROR_ACCESS_DENIED) {
+        // If we are here that means that we do not have enough priveleges to
+        // open the process for a given kill method. No reason to attempt other
+        // instances. Just clean up and return false.
+        UTIL_LOG(L3, (_T("PrepareToKill failed for '%s'. Kill method %d."),
+                      process_name_, method_mask));
+        CloseAllHandles();
+        return false;
+      }
+    }
+  }
+  // We already handled the case when we don't have enough privileges to open
+  // the process. So if we have less handles than process ids -> some of the
+  // processes have died since we made a snapshot untill the time we tried to
+  // open handles. We need to do another snapshot and try to open handles one
+  // more time. We need number of handles and number of ids to be equal.
+  // We can do it with recursion. The idea is: make the next snapshot and open
+  // handles. Hopefully the number will be equal. Stop recursion at the third
+  // level.
+
+  if (process_handles_.size() != process_ids_.size()) {
+    recursion_level_++;
+
+    // we have a disbalance here. This is pretty bad.
+    // Some of the processes died already so let's try
+    // to balance them.
+    if (!FindProcessInstances()) {
+      // they are all dead.
+      recursion_level_ = 0;
+      return false;
+    }
+
+    // try to balance three times no more.
+    if (recursion_level_ >= 3) {
+      recursion_level_ = 0;
+      UTIL_LOG(L3, (_T("Recursion level too deep in PrepareToKill for '%s'."),
+                    process_name_));
+      return false;
+    }
+
+    // recursively call the function
+    return PrepareToKill(method_mask);
+  }
+  recursion_level_ = 0;
+  return true;
+}
+
+// ProcessTerminator::FindProcessWindows
+// Just calls enumeration function
+bool ProcessTerminator::FindProcessWindows() {
+  window_handles_.clear();
+  return ::EnumWindows(EnumAllWindowsProc, reinterpret_cast<LPARAM>(this)) &&
+         !window_handles_.empty();
+}
+
+// ProcessTerminator::EnumAllWindowsProc
+// During enumeration this function will try to find a match between
+// process id we already found and process id obtained from each window.
+// if there is a match, we record the window in an array
+BOOL ProcessTerminator::EnumAllWindowsProc(HWND hwnd, LPARAM lparam) {
+  ProcessTerminator* this_pointer =
+        reinterpret_cast<ProcessTerminator*>(lparam);
+  ASSERT1(this_pointer);
+
+  uint32 process_id = 0;
+  uint32 thread_id =
+    ::GetWindowThreadProcessId(hwnd, reinterpret_cast<DWORD*>(&process_id));
+
+  typedef std::vector<uint32>::const_iterator ProcessIdIterator;
+  for (ProcessIdIterator it = this_pointer->process_ids_.begin();
+       it != this_pointer->process_ids_.end();
+       ++it) {
+    if (*it == process_id) {
+      // The main idea is: Find all top level windows (NO PARENT!!!)
+      // AND this windows must have system menu and be visible. So we make sure
+      // that we send WM_CLOSE ONLY to the windows that user might close
+      // interactively. This way we are safe. The last thing to check is if it
+      // is tr hidden window.
+      if (WindowUtils::IsMainWindow(hwnd) && WindowUtils::HasSystemMenu(hwnd)) {
+        this_pointer->window_handles_.push_back(hwnd);
+      }
+    }
+  }
+  return TRUE;
+}
+
+// ProcessTerminator::KillProcessViaWndMessages()
+// try to post a windows message
+bool  ProcessTerminator::KillProcessViaWndMessages(uint32 timeout_msec) {
+  UTIL_LOG(L3, (_T("[KillProcessViaWndMessages]")));
+  if (!FindProcessWindows()) {
+    UTIL_LOG(L1, (_T("[KillProcessViaWndMessages]")
+                  _T("[failed to find any windows for '%s']"), process_name_));
+    return false;
+  }
+
+  bool post_messages_succeeded = false;
+
+  for (size_t i = 0; i < window_handles_.size(); i++) {
+    // Previous method used WM_CLOSE, WM_SYSCOMMAND+SC_CLOSE is slightly better.
+    // It closes our apps, and also works correctly on AOL!
+    if (::PostMessage(window_handles_[i], WM_SYSCOMMAND, SC_CLOSE, 0)) {
+      if (flash_window_) {
+        UTIL_LOG(L3, (_T("[PostMessageSucceeded flashing window]")));
+        ::FlashWindow(window_handles_[i], true);
+      }
+      post_messages_succeeded = true;
+    }
+  }
+
+  if (!post_messages_succeeded) {
+    UTIL_LOG(L3, (_T("[KillProcessViaWndMessages]")
+                  _T("[failed to PostMessage to windows of '%s']"),
+                  process_name_));
+  }
+  // If we succeeded in posting message at least one time we have to wait.
+  // We don't know the relationship between windows in the process.
+  return post_messages_succeeded && WaitForProcessInstancesToDie(timeout_msec);
+}
+
+// Try to post a thread message.
+bool ProcessTerminator::KillProcessViaThreadMessages(uint32 timeout_msec) {
+  UTIL_LOG(L3, (_T("[KillProcessViaThreadMessages]")));
+  std::vector<uint32> thread_ids;
+
+  if (!FindProcessThreads(&thread_ids)) {
+    UTIL_LOG(L3, (_T("[KillProcessViaThreadMessages]")
+                  _T("[failed to find any threads for '%s']"), process_name_));
+    return false;
+  }
+
+  bool post_messages_succeeded = false;
+  for (size_t i = 0; i < thread_ids.size(); i++) {
+    if (::PostThreadMessage(thread_ids[i], WM_CLOSE, 0, 0)) {
+      post_messages_succeeded = true;
+    }
+  }
+
+  if (!post_messages_succeeded) {
+    UTIL_LOG(L3, (_T("[KillProcessViaWndMessages]")
+                  _T("[failed to PostMessage to threads of '%s'."),
+                  process_name_));
+  }
+  // If we succeded in posting message to at least one thread we have to wait.
+  // We don't know the relationship between threads in the process.
+  return post_messages_succeeded && WaitForProcessInstancesToDie(timeout_msec);
+}
+
+// find all the threads running in a given process.
+bool ProcessTerminator::FindProcessThreads(std::vector<uint32>* thread_ids) {
+  HANDLE process_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
+  if (process_snapshot == INVALID_HANDLE_VALUE) {
+    return false;
+  }
+
+  THREADENTRY32 thread_info = {0};  // zero it out just in case.
+  thread_info.dwSize = sizeof(THREADENTRY32);
+
+  if (::Thread32First(process_snapshot, &thread_info))  {
+    do {
+      for (std::vector<uint32>::const_iterator it = process_ids_.begin();
+           it != process_ids_.end(); ++it) {
+        if (*it == thread_info.th32OwnerProcessID) {
+          // we have found it.
+          thread_ids->push_back(thread_info.th32ThreadID);
+        }
+      }
+      // system changes this value, do not forget to reset to
+      // max possible.
+      thread_info.dwSize = sizeof(THREADENTRY32);
+    } while (::Thread32Next(process_snapshot, &thread_info));
+  }
+
+  return !thread_ids->empty();
+}
+
+// Last and crude method to kill the process. Should be used only
+// if all other methods have failed.
+bool ProcessTerminator::KillProcessViaTerminate(uint32 timeout_msec) {
+  UTIL_LOG(L3, (_T("[KillProcessViaTerminate]")));
+  bool at_least_one_terminated = false;
+
+  for (size_t i = 0; i < process_handles_.size(); i++) {
+    if (!::TerminateProcess(process_handles_[i], 0)) {
+      UTIL_LOG(L3, (_T("[KillProcessViaTerminate]")
+                    _T("[failed for instance of '%s'][System error %d]"),
+                    process_name_, ::GetLastError()));
+    } else {
+       at_least_one_terminated = true;
+    }
+  }
+  return at_least_one_terminated ? WaitForProcessInstancesToDie(timeout_msec) :
+                                   false;
+}
+
+HRESULT SetProcessSilentShutdown() {
+  DWORD shut_down_level(0), shut_down_flags(0);
+  if (!::GetProcessShutdownParameters(&shut_down_level, &shut_down_flags)) {
+    return HRESULTFromLastError();
+  }
+  shut_down_flags |= SHUTDOWN_NORETRY;
+  if (!::SetProcessShutdownParameters(shut_down_level, shut_down_flags)) {
+    return HRESULTFromLastError();
+  }
+  return S_OK;
+}
+
+}  // namespace omaha
+
diff --git a/common/proc_utils.h b/base/proc_utils.h
similarity index 100%
rename from common/proc_utils.h
rename to base/proc_utils.h
diff --git a/base/proc_utils_unittest.cc b/base/proc_utils_unittest.cc
new file mode 100644
index 0000000..0a47041
--- /dev/null
+++ b/base/proc_utils_unittest.cc
@@ -0,0 +1,26 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/proc_utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(ProcUtilsTest, SetSilentShutdown) {
+  ASSERT_HRESULT_SUCCEEDED(SetProcessSilentShutdown());
+}
+
+}  // namespace omaha
+
diff --git a/base/process.cc b/base/process.cc
new file mode 100644
index 0000000..0ebfb07
--- /dev/null
+++ b/base/process.cc
@@ -0,0 +1,1650 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Defines class Process to incapsulate win32
+// functions for creation and some manipulations of
+// processes.
+
+#include "omaha/base/process.h"
+
+#include <ntsecapi.h>
+#include <psapi.h>
+#include <stierr.h>
+#include <tlhelp32.h>
+#include <vector>
+
+#ifndef NT_SUCCESS
+#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
+#endif
+
+#include "omaha/base/debug.h"
+#include "omaha/base/disk.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/string.h"
+#include "omaha/base/system.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/window_utils.h"
+
+namespace omaha {
+
+const int kNumRetriesToFindProcess = 4;
+const int kFindProcessRetryIntervalMs = 500;
+const int kMaxCmdLineLengthBytes = 4096;
+
+// Constructor
+Process::Process(const TCHAR* name,
+                 const TCHAR* window_class_name)
+    : process_id_(0),
+      exit_code_(0),
+      number_of_restarts_(static_cast<uint32>(-1)),
+      name_(name),
+      shutdown_event_(NULL) {
+  ASSERT1(name);
+  command_line_ = name;
+  window_class_name_ = window_class_name;
+}
+
+// Constructor
+Process::Process(uint32 process_id)
+    : process_id_(process_id),
+      exit_code_(0),
+      number_of_restarts_(static_cast<uint32>(-1)),
+      name_(itostr(static_cast<uint32>(process_id))),
+      shutdown_event_(NULL) {
+  reset(process_, ::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
+                                false,
+                                process_id));
+  if (!valid(process_)) {
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[Process::Process - failed to open process][%u][0x%x]"),
+              process_id, HRESULTFromLastError()));
+  }
+}
+
+// Destructor
+Process::~Process() {
+}
+
+// Start with command params
+HRESULT Process::Start(const TCHAR* command_line_parameters,
+                       HANDLE runas_token) {
+  if (command_line_parameters && *command_line_parameters) {
+    command_line_parameters_ = command_line_parameters;
+  }
+
+  number_of_restarts_ = static_cast<uint32>(-1);
+  time_of_start_      = GetTickCount();
+
+  return Restart(runas_token);
+}
+
+// Restart with the old command params
+HRESULT Process::Restart(HANDLE runas_token) {
+  // Can't start the same process twice in the same containing object.
+  if (Running()) {
+    return E_FAIL;
+  }
+
+  PROCESS_INFORMATION process_info = {0};
+  HRESULT hr = runas_token ?
+                   System::StartProcessAsUser(runas_token,
+                                              command_line_,
+                                              command_line_parameters_,
+                                              _T("WinSta0\\Default"),
+                                              &process_info) :
+                   System::StartProcessWithArgsAndInfo(command_line_,
+                                                       command_line_parameters_,
+                                                       &process_info);
+
+  if (SUCCEEDED(hr)) {
+    VERIFY1(::CloseHandle(process_info.hThread));
+
+    reset(process_, process_info.hProcess);
+    process_id_ = process_info.dwProcessId;
+
+    ASSERT1(process_id_);
+    number_of_restarts_++;
+  } else {
+    UTIL_LOG(LE, (_T("[Process Restart failed][%s][0x%x]"), command_line_, hr));
+  }
+
+  return hr;
+}
+
+// Check if the process is running.
+bool Process::Running() const {
+  if (!get(process_)) {
+    return false;
+  }
+
+  return (::WaitForSingleObject(get(process_), 0) == WAIT_TIMEOUT);
+}
+
+// Create a job and assign the process to it
+HANDLE Process::AssignToJob() {
+  // Make sure that the process handle is valid
+  if (!get(process_)) {
+    return false;
+  }
+
+  // Create a job
+  scoped_job job(::CreateJobObject(NULL, NULL));
+  if (!valid(job)) {
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[Process::AssignToJob - CreateJobObject failed][0x%x]"),
+              HRESULTFromLastError()));
+    return false;
+  }
+
+  // Assign the process to the job
+  if (!::AssignProcessToJobObject(get(job), get(process_))) {
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[Process::AssignToJob-AssignProcessToJobObject fail][0x%x]"),
+              HRESULTFromLastError()));
+    return false;
+  }
+
+  return release(job);
+}
+
+// Wait till the process finishes
+bool Process::WaitUntilDead(uint32 timeout_msec) {
+  ASSERT1(timeout_msec);
+
+  if (!get(process_)) {
+    return false;
+  }
+
+  uint32 ret = 0;
+  if (shutdown_event_) {
+    HANDLE wait_handles[2] = {0};
+    wait_handles[0] = get(process_);
+    wait_handles[1] = shutdown_event_;
+    ret = ::WaitForMultipleObjectsEx(2,
+                                     wait_handles,
+                                     false,
+                                     timeout_msec,
+                                     true);
+  } else {
+    ret = ::WaitForSingleObjectEx(get(process_), timeout_msec, true);
+  }
+  if (ret == WAIT_OBJECT_0) {
+    UTIL_LOG(L2, (_T("[Process::WaitUntilDead - succeeded to wait process]")
+                  _T("[%s]"), GetName()));
+    return true;
+  } else if (ret == WAIT_IO_COMPLETION) {
+    UTIL_LOG(LEVEL_ERROR, (_T("[Process::WaitUntilDead-recv APC][%s][%u][%u]"),
+                           GetName(), process_id_));
+    return false;
+  } else {
+    UTIL_LOG(LEVEL_ERROR, (_T("[Process::WaitUntilDead - fail to wait process,")
+                           _T("possibly timeout][%s][%u][%u]"),
+                           GetName(), process_id_, ret));
+    return false;
+  }
+}
+
+// Wait some time till the process and all its descendent processes finish
+//
+// Background:
+//   Some process might spawn another process and get itself terminated
+// without waiting the descendant process to finish.
+//
+// Args:
+//   job:                Job to which the process is assigned
+//                       AssignToJob() will be called when NULL value is passed
+//   timeout_msec:       Timeout value in msec
+//   path_to_exclude:    Path of descendant process to excluded from waiting
+//                       (this should be in long format)
+//   exit_code:          To hold the exit code being returned
+bool Process::WaitUntilAllDead(HANDLE job,
+                               uint32 timeout_msec,
+                               const TCHAR* path_to_exclude,
+                               uint32* exit_code) {
+  ASSERT1(timeout_msec);
+
+  UTIL_LOG(L2, (_T("[Process::WaitUntilAllDead][%u][%s]"),
+                timeout_msec, path_to_exclude));
+
+  if (exit_code) {
+  *exit_code = 0;
+  }
+
+  scoped_job job_guard;
+  if (!job) {
+    reset(job_guard, AssignToJob());
+    if (!valid(job_guard)) {
+      return false;
+    }
+    job = get(job_guard);
+  }
+
+  return InternalWaitUntilAllDead(job,
+                                  timeout_msec,
+                                  path_to_exclude,
+                                  exit_code);
+}
+
+// Helper function to wait till the process and all its descendent processes
+// finish.
+bool Process::InternalWaitUntilAllDead(HANDLE job,
+                                       uint32 timeout_msec,
+                                       const TCHAR* path_to_exclude,
+                                       uint32* exit_code) {
+  ASSERT1(job);
+  ASSERT1(timeout_msec);
+
+  // Wait until current process finishes
+  if (!WaitUntilDead(timeout_msec)) {
+    return false;
+  }
+
+  // Find descendant process
+  uint32 desc_process_id = GetDescendantProcess(
+                               job,
+                               false,  // child_only
+                               exit_code != NULL,  // sole_descendent
+                               NULL,  // search_name
+                               path_to_exclude);
+
+  if (desc_process_id) {
+    // Open descendent process
+    Process desc_process(desc_process_id);
+
+    // If descendant process dies too soon, do not need to wait for it
+    if (desc_process.Running()) {
+      // Release the parent process handle
+      // This to handle the scenario that Firefox uninstall code will wait till
+      // parent process handle becomes NULL
+      reset(process_);
+
+      UTIL_LOG(L2, (_T("[Process::InternalWaitUntilAllDead]")
+                    _T("[waiting descendant process][%u]"), desc_process_id));
+
+      // Propagate the shutdown event to descendent process
+      if (shutdown_event_) {
+        desc_process.SetShutdownEvent(shutdown_event_);
+      }
+
+      // Wait till descendant process finishes
+      bool wait_ret = desc_process.InternalWaitUntilAllDead(job,
+                                                            timeout_msec,
+                                                            path_to_exclude,
+                                                            exit_code);
+
+      return wait_ret;
+    }
+  }
+
+  // Use the exit code from parent process
+  if (exit_code) {
+  VERIFY1(GetExitCode(exit_code));
+  }
+
+  // Release the parent process handle
+  reset(process_);
+
+  return true;
+}
+
+// Wait until process is dead or a windows message arrives (for use in a message
+// loop while waiting)
+HRESULT Process::WaitUntilDeadOrInterrupt(uint32 msec) {
+  if (!get(process_)) {
+    return E_FAIL;
+  }
+
+  HANDLE events[1] = { get(process_) };
+  uint32 dw = ::MsgWaitForMultipleObjects(1, events, FALSE, msec, QS_ALLEVENTS);
+  switch (dw) {
+    case WAIT_OBJECT_0:
+      return CI_S_PROCESSWAIT_DEAD;
+    case WAIT_OBJECT_0 + 1:
+      return CI_S_PROCESSWAIT_MESSAGE;
+    case WAIT_TIMEOUT:
+      return CI_S_PROCESSWAIT_TIMEOUT;
+    case WAIT_FAILED:
+    default:
+      return E_FAIL;
+  }
+}
+
+#if !SHIPPING
+CString Process::GetDebugInfo() const {
+  return debug_info_;
+}
+#endif
+
+// Return the process ID
+uint32 Process::GetId() const {
+  return process_id_;
+}
+
+// Return the process name
+const TCHAR *Process::GetName() const {
+  return name_;
+}
+
+// Return win32 handle to the process.
+HANDLE Process::GetHandle() const {
+  return get(process_);
+}
+
+// Get process exit code.
+bool Process::GetExitCode(uint32* exit_code) const {
+  ASSERT1(exit_code);
+
+  if (!get(process_)) {
+    return false;
+  }
+
+  if (!::GetExitCodeProcess(get(process_),
+                            reinterpret_cast<DWORD*>(&exit_code_))) {
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[Process::GetExitCode - failed to get exit code][%u][0x%x]"),
+              process_id_, HRESULTFromLastError()));
+    return false;
+  }
+  if (exit_code_ == STILL_ACTIVE) {
+    return false;
+  }
+
+  *exit_code = exit_code_;
+  return true;
+}
+
+// default implementation allows termination
+bool Process::IsTerminationAllowed() const {
+  return true;
+}
+
+// Terminate the process. If wait_for_terminate_msec == 0 return value doesn't
+// mean that the process actualy terminated. It becomes assync. operation.
+// Check the status with Running accessor function in this case.
+bool Process::Terminate(uint32 wait_for_terminate_msec) {
+  if (!Running()) {
+    return true;
+  }
+
+  if (!IsTerminationAllowed()) {
+    return false;
+  }
+
+  if (!::TerminateProcess(get(process_), 1)) {
+    return false;
+  }
+
+  return wait_for_terminate_msec ? WaitUntilDead(wait_for_terminate_msec) :
+                                   true;
+}
+
+// Default returns INFINITE means never restart.
+// Return any number of msec if overwriting
+uint32 Process::GetRestartInterval() const {
+  return INFINITE;
+}
+
+// How many times the process can be restarted
+// in case it crashes. When overriding return any
+// number or INFINITE to restart forever.
+uint32 Process::GetMaxNumberOfRestarts() const {
+  return 0;
+}
+
+// what is the time window for number of crashes returned by
+// GetMaxNumberOfRestarts(). If crashed more that this number of restarts
+// in a specified time window - do not restart it anymore.
+// Default implementation returns INFINITE which means that this is not time
+// based at all, if the process crashed more than the value returned by
+// GetMaxNumberOfRestarts it will not be restarted no matter how long it took.
+uint32 Process::GetTimeWindowForCrashes() const {
+  return INFINITE;
+}
+
+uint32 Process::GetMaxMemory() const {
+  return 0;
+}
+
+// Have we exceeded the number of maximum restarting?
+bool Process::AllowedToRestart() const {
+  uint32 max_number_of_restarts = GetMaxNumberOfRestarts();
+
+  if ((max_number_of_restarts == INFINITE) ||
+     (number_of_restarts_ < max_number_of_restarts)) {
+    return true;
+  }
+
+  // process crashed too many times. Let's look at the rate of crashes.
+  // Maybe we can "forgive" the process if it took some time for it to crash.
+  if ((::GetTickCount() - time_of_start_) < GetTimeWindowForCrashes()) {
+    return false;  // not forgiven
+  }
+
+  // Everything is forgiven. Give the process
+  // new start in life.
+  time_of_start_ = ::GetTickCount();
+  number_of_restarts_ = static_cast<uint32>(-1);
+
+  return true;
+}
+
+// Set shutdown event using in signaling the process watch
+void Process::SetShutdownEvent(HANDLE shutdown_event) {
+  ASSERT1(shutdown_event);
+
+  shutdown_event_ = shutdown_event;
+}
+
+// Set priority class to the process.
+bool Process::SetPriority(uint32 priority_class) const {
+  if (!get(process_)) {
+    return false;
+  }
+
+  VERIFY1(::SetPriorityClass(get(process_), priority_class));
+  return true;
+}
+
+HRESULT Process::GetParentProcessId(uint32* parent_pid) {
+  ASSERT1(parent_pid);
+  *parent_pid = 0;
+
+  scoped_hfile process_snap(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
+  if (!process_snap) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[GetParentProcessId][Failed snapshot][0x%x]"), hr));
+    return hr;
+  }
+
+  // Eumerate all processes in the snapshot
+  PROCESSENTRY32 pe32;
+  SetZero(pe32);
+  pe32.dwSize = sizeof(PROCESSENTRY32);
+  if (!::Process32First(get(process_snap), &pe32)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[Process32First failed][0x%x]"), hr));
+    return hr;
+  }
+
+  do {
+    if (pe32.th32ProcessID != process_id_) {
+      continue;
+    }
+
+    if (pe32.th32ParentProcessID) {
+      *parent_pid = pe32.th32ParentProcessID;
+      return S_OK;
+    }
+  } while (::Process32Next(get(process_snap), &pe32));
+
+  return E_FAIL;
+}
+
+// Try to get a descendant process. Return process id if found.
+uint32 Process::GetDescendantProcess(HANDLE job,
+                                     bool child_only,
+                                     bool sole_descedent,
+                                     const TCHAR* search_name,
+                                     const TCHAR* path_to_exclude) {
+  ASSERT1(job);
+
+  // Find all descendent processes
+  std::vector<ProcessInfo> descendant_processes;
+  if (FAILED(GetAllDescendantProcesses(job,
+                                       child_only,
+                                       search_name,
+                                       path_to_exclude,
+                                       &descendant_processes))) {
+    return 0;
+  }
+
+  // If more than one decendent processes is found, filter out those that are
+  // not direct children. This is because it might be the case that in a very
+  // short period of time, process A spawns B and B spawns C, and we capture
+  // both B and C.
+  std::vector<ProcessInfo> child_processes;
+  typedef std::vector<ProcessInfo>::const_iterator ProcessInfoConstIterator;
+  if (descendant_processes.size() > 1) {
+    for (ProcessInfoConstIterator it(descendant_processes.begin());
+         it != descendant_processes.end(); ++it) {
+      if (it->parent_id == process_id_) {
+        child_processes.push_back(*it);
+      }
+    }
+    if (!child_processes.empty()) {
+      descendant_processes = child_processes;
+    }
+  }
+
+  // Save the debugging information if needed
+#if !SHIPPING
+  if (sole_descedent && descendant_processes.size() > 1) {
+    debug_info_ = _T("More than one descendent process is found for process ");
+    debug_info_ += itostr(process_id_);
+    debug_info_ += _T("\n");
+    for (ProcessInfoConstIterator it(descendant_processes.begin());
+         it != descendant_processes.end(); ++it) {
+      debug_info_.AppendFormat(_T("%u %u %s\n"),
+                               it->process_id,
+                               it->parent_id,
+                               it->exe_file);
+    }
+  }
+#else
+  sole_descedent;   // unreferenced formal parameter
+#endif
+
+  return descendant_processes.empty() ? 0 : descendant_processes[0].process_id;
+}
+
+BOOL Process::IsProcessInJob(HANDLE process_handle,
+                             HANDLE job_handle,
+                             PBOOL result)  {
+  typedef BOOL (WINAPI *Fun)(HANDLE process_handle,
+                             HANDLE job_handle,
+                             PBOOL result);
+
+  HINSTANCE kernel_instance = ::GetModuleHandle(_T("kernel32.dll"));
+  ASSERT1(kernel_instance);
+  Fun pfn = reinterpret_cast<Fun>(::GetProcAddress(kernel_instance,
+                                                   "IsProcessInJob"));
+  ASSERT(pfn, (_T("IsProcessInJob export not found in kernel32.dll")));
+  return pfn ? (*pfn)(process_handle, job_handle, result) : FALSE;
+}
+
+// Try to get all matching descendant processes
+HRESULT Process::GetAllDescendantProcesses(
+                     HANDLE job,
+                     bool child_only,
+                     const TCHAR* search_name,
+                     const TCHAR* path_to_exclude,
+                     std::vector<ProcessInfo>* descendant_processes) {
+  ASSERT1(job);
+  ASSERT1(descendant_processes);
+
+  // Take a snapshot
+  // Note that we do not have a seperate scoped_* type defined to wrap the
+  // handle returned by CreateToolhelp32Snapshot. So scoped_hfile with similar
+  // behavior is used.
+  scoped_hfile process_snap(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
+  if (!process_snap) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[Process::GetAllDescendantProcesses - fail to get snapshot]")
+              _T("[0x%x]"), hr));
+    return hr;
+  }
+
+  // Eumerate all processes in the snapshot
+  PROCESSENTRY32 pe32;
+  SetZero(pe32);
+  pe32.dwSize = sizeof(PROCESSENTRY32);
+  if (!::Process32First(get(process_snap), &pe32)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[Process::GetAllDescendantProcesses - failed to")
+                           _T("get first process][0x%x]"), hr));
+    return hr;
+  }
+
+  do {
+    // Skip process 0 and current process
+    if (pe32.th32ProcessID == 0 || pe32.th32ProcessID == process_id_) {
+      continue;
+    }
+
+    // If searching for child only, perform the check
+    if (child_only && pe32.th32ParentProcessID != process_id_) {
+      continue;
+    }
+
+    // Open the process
+    scoped_process process(::OpenProcess(PROCESS_QUERY_INFORMATION |
+                                         SYNCHRONIZE,
+                                         false,
+                                         pe32.th32ProcessID));
+    if (!valid(process)) {
+      continue;
+    }
+
+    // Determines whether the process is running in the specified job
+    BOOL result = FALSE;
+    if (!IsProcessInJob(get(process), job, &result) || !result) {
+      continue;
+    }
+
+    // Check whether the process is still running
+    if (::WaitForSingleObject(get(process), 0) != WAIT_TIMEOUT) {
+      continue;
+    }
+
+    // Compare the name if needed
+    if (search_name && *search_name) {
+      if (_tcsicmp(pe32.szExeFile, search_name) != 0) {
+        continue;
+      }
+    }
+
+    // If we need to exclude certain path, check it now
+    if (path_to_exclude && *path_to_exclude) {
+      if (IsProcessRunningWithPath(pe32.th32ProcessID, path_to_exclude)) {
+        continue;
+      }
+    }
+
+    // Add to the list
+    ProcessInfo proc_info;
+    proc_info.process_id = pe32.th32ProcessID;
+    proc_info.parent_id = pe32.th32ParentProcessID;
+#if !SHIPPING
+    proc_info.exe_file = pe32.szExeFile;
+#endif
+    descendant_processes->push_back(proc_info);
+  } while (::Process32Next(get(process_snap), &pe32));
+
+  return S_OK;
+}
+
+HRESULT Process::FindProcesses(uint32 exclude_mask,
+                               const TCHAR* search_name,
+                               bool search_main_executable_only,
+                               std::vector<uint32>* process_ids_found) {
+  ASSERT1(process_ids_found);
+  // Remove the only include processes owned by user mask from the exclude
+  // mask. This is needed as this is the behavior expected by the method,
+  // before the addition of the user_sid.
+  exclude_mask &= (~INCLUDE_ONLY_PROCESS_OWNED_BY_USER);
+  std::vector<CString> command_lines;
+  return FindProcesses(exclude_mask, search_name, search_main_executable_only,
+                       _T(""), command_lines, process_ids_found);
+}
+
+bool Process::IsStringPresentInList(const CString& process_command_line,
+                                    const std::vector<CString>& list) {
+  std::vector<CString>::const_iterator iter = list.begin();
+  for (; iter != list.end(); ++iter) {
+    CString value_to_find = *iter;
+
+    // If we are able to open the process command line, then we should
+    // ensure that it does not contain the value that we are looking for.
+    if (process_command_line.Find(value_to_find) != -1) {
+      // Found a match.
+      return true;
+    }
+  }
+
+  return false;
+}
+
+// TODO(omaha): Change the implementation of this method to take in a
+// predicate that determines whether a process should be included in the
+// result set.
+HRESULT Process::FindProcesses(uint32 exclude_mask,
+                               const TCHAR* search_name,
+                               bool search_main_executable_only,
+                               const CString& user_sid,
+                               const std::vector<CString>& command_lines,
+                               std::vector<uint32>* process_ids_found) {
+  ASSERT1(search_name && *search_name);
+  ASSERT1(process_ids_found);
+  ASSERT1(!((exclude_mask & EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING) &&
+            (exclude_mask & INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING)));
+
+  const TCHAR* const kLocalSystemSid = _T("S-1-5-18");
+
+  // Clear the output queue
+  process_ids_found->clear();
+
+  // Get the list of process identifiers.
+  uint32 process_ids[kMaxProcesses] = {0};
+  uint32 bytes_returned = 0;
+  if (!::EnumProcesses(reinterpret_cast<DWORD*>(process_ids),
+                       sizeof(process_ids),
+                       reinterpret_cast<DWORD*>(&bytes_returned))) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[Process::FindProcesses-fail to EnumProcesses]")
+                           _T("[0x%x]"), hr));
+    return hr;
+  }
+
+  // Enumerate all processes
+  int num_processes = bytes_returned / sizeof(process_ids[0]);
+  // We have found an elevated number of crashes in 1.2.584.15114 on what
+  // we believe are Italian systems. The first step to solving this Italian job
+  // is to assert on the condition while we are further testing this.
+  ASSERT1(num_processes <= kMaxProcesses);
+
+  // In Vista, SeDebugPrivilege is required to open the process not owned by
+  // current user. Also required for XP admins to open Local System processes
+  // with PROCESS_QUERY_INFORMATION access rights.
+  System::AdjustPrivilege(SE_DEBUG_NAME, true);
+
+  const uint32 cur_process_id = ::GetCurrentProcessId();
+
+  uint32 parent_process_id = 0;
+  if (exclude_mask & EXCLUDE_PARENT_PROCESS) {
+    Process current_process(cur_process_id);
+    uint32 ppid = 0;
+    HRESULT hr = current_process.GetParentProcessId(&ppid);
+    parent_process_id = SUCCEEDED(hr) ? ppid : 0;
+  }
+
+  // Get SID of current user
+  CString cur_user_sid;
+  HRESULT hr = omaha::user_info::GetProcessUser(NULL, NULL, &cur_user_sid);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  UTIL_LOG(L4, (_T("[Process::FindProcesses][processes=%d]"), num_processes));
+  for (int i = 0; i < num_processes; ++i) {
+    // Skip the system idle process.
+    if (process_ids[i] == 0) {
+      continue;
+    }
+
+    // Skip the current process if needed.
+    if ((exclude_mask & EXCLUDE_CURRENT_PROCESS) &&
+        (process_ids[i] == cur_process_id)) {
+      UTIL_LOG(L4, (_T("[Excluding current process %d"), process_ids[i]));
+      continue;
+    }
+
+    // Skip the parent process if needed.
+    if ((exclude_mask & EXCLUDE_PARENT_PROCESS) &&
+        (process_ids[i] == parent_process_id)) {
+      UTIL_LOG(L4, (_T("[Excluding parent process(%d) of %d"),
+          process_ids[i], cur_process_id));
+      continue;
+    }
+
+
+    // Get the owner sid.
+    // Note that we may fail to get the owner which is not current user.
+    // So if the owner_sid is empty, the process is sure not to be owned by the
+    // current user.
+    CString owner_sid;
+    Process::GetProcessOwner(process_ids[i], &owner_sid);
+
+    if ((exclude_mask & INCLUDE_ONLY_PROCESS_OWNED_BY_USER) &&
+      owner_sid != user_sid) {
+      UTIL_LOG(L4,
+          (_T("[Excluding process as not owned by user][%d]"), process_ids[i]));
+      continue;
+    }
+
+    if ((exclude_mask & EXCLUDE_PROCESS_OWNED_BY_CURRENT_USER) &&
+        owner_sid == cur_user_sid) {
+      UTIL_LOG(L4,
+          (_T("[Excluding process as owned by current user][%d]"),
+           process_ids[i]));
+      continue;
+    }
+    if ((exclude_mask & EXCLUDE_PROCESS_OWNED_BY_SYSTEM) &&
+        owner_sid == kLocalSystemSid) {
+      UTIL_LOG(L4,
+          (_T("[Excluding process as owned by system][%d]"), process_ids[i]));
+      continue;
+    }
+    if (exclude_mask & EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING ||
+        exclude_mask & INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING) {
+      CString process_command_line;
+      HRESULT hr = GetCommandLine(process_ids[i], &process_command_line);
+      if (FAILED(hr)) {
+        UTIL_LOG(L4,
+          (_T("[Excluding process could not get command line][%d]"),
+           process_ids[i]));
+        continue;
+      }
+
+      // If we are able to open the process command line, then we should
+      // ensure that it does not contain the value that we are looking for if
+      // we are excluding the command line or that it contains the command line
+      // that we are looking for in case the include switch is specified.
+      bool present = IsStringPresentInList(process_command_line, command_lines);
+      if ((present &&
+            (exclude_mask & EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING)) ||
+          (!present &&
+            (exclude_mask & INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING))) {
+        UTIL_LOG(L4, (_T("[Process command line matches criteria][%d]'[%s]'"),
+                 process_ids[i], process_command_line));
+        continue;
+      }
+    }
+
+    // If search_name is provided, make sure it matches
+    if (Process::IsProcessUsingExeOrDll(process_ids[i],
+                                        search_name,
+                                        search_main_executable_only)) {
+      UTIL_LOG(L4,
+          (_T("[Including process][%d][%s]"), process_ids[i], search_name));
+      process_ids_found->push_back(process_ids[i]);
+    }
+  }
+
+  return S_OK;
+}
+
+HRESULT Process::FindProcessesInSession(
+    DWORD session_id,
+    uint32 exclude_mask,
+    const TCHAR* search_name,
+    bool search_main_executable_only,
+    const CString& user_sid,
+    const std::vector<CString>& cmd_lines,
+    std::vector<uint32>* process_ids_found) {
+  HRESULT hr = FindProcesses(exclude_mask,
+                             search_name,
+                             search_main_executable_only,
+                             user_sid,
+                             cmd_lines,
+                             process_ids_found);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Filter to processes running under session_id.
+  std::vector<uint32>::iterator iter = process_ids_found->begin();
+  while (iter != process_ids_found->end()) {
+    uint32 process_pid = *iter;
+    DWORD process_session = 0;
+    hr = S_OK;
+    if (!::ProcessIdToSessionId(process_pid, &process_session)) {
+      hr = HRESULTFromLastError();
+      UTIL_LOG(LE,  (_T("[::ProcessIdToSessionId failed][0x%x]"), hr));
+    } else if (process_session != session_id) {
+      UTIL_LOG(L4, (_T("[Excluding process, different session][%d][%d][%d]"),
+                    process_pid, process_session, session_id));
+    }
+
+    if (FAILED(hr) || process_session != session_id) {
+      // Remove from list and continue.
+      iter = process_ids_found->erase(iter);
+      continue;
+    }
+
+    ++iter;
+  }
+
+  return S_OK;
+}
+
+bool Process::IsModuleMatchingExeOrDll(const TCHAR* module_name,
+                                       const TCHAR* search_name,
+                                       bool is_fully_qualified_name) {
+  CString module_file_name;
+  if (is_fully_qualified_name) {
+    if (FAILED(GetLongPathName(module_name, &module_file_name))) {
+      return false;
+    }
+  } else {
+    module_file_name = ::PathFindFileName(module_name);
+    ASSERT1(!module_file_name.IsEmpty());
+    if (module_file_name.IsEmpty()) {
+      return false;
+    }
+  }
+
+  return (module_file_name.CompareNoCase(search_name) == 0);
+}
+
+DWORD Process::GetProcessImageFileName(HANDLE proc_handle,
+                                       LPTSTR image_file,
+                                       DWORD file_size)  {
+  typedef DWORD (WINAPI *Fun)(HANDLE proc_handle,
+                              LPWSTR image_file,
+                              DWORD file_size);
+
+  HINSTANCE psapi_instance = ::GetModuleHandle(_T("Psapi.dll"));
+  ASSERT1(psapi_instance);
+  Fun pfn = reinterpret_cast<Fun>(::GetProcAddress(psapi_instance,
+                                                   "GetProcessImageFileNameW"));
+  if (!pfn) {
+    UTIL_LOG(L1, (_T("::GetProcessImageFileNameW() not found in Psapi.dll")));
+    return 0;
+  }
+  return (*pfn)(proc_handle, image_file, file_size);
+}
+
+bool Process::IsProcImageMatch(HANDLE proc_handle,
+                               const TCHAR* search_name,
+                               bool is_fully_qualified_name)  {
+  TCHAR image_name[MAX_PATH] = _T("");
+  if (!GetProcessImageFileName(proc_handle,
+                               image_name,
+                               arraysize(image_name))) {
+    UTIL_LOG(L4, (_T("[GetProcessImageFileName fail[0x%x]"),
+                  HRESULTFromLastError()));
+    return false;
+  }
+
+  UTIL_LOG(L4, (_T("[GetProcessImageFileName][%s]"), image_name));
+  CString dos_name;
+  HRESULT hr(DevicePathToDosPath(image_name, &dos_name));
+  if (FAILED(hr)) {
+    UTIL_LOG(L4, (_T("[DevicePathToDosPath fail[0x%x]"), hr));
+    return false;
+  }
+
+  return IsModuleMatchingExeOrDll(dos_name,
+                                  search_name,
+                                  is_fully_qualified_name);
+}
+
+// Is the process using the specified exe/dll?
+bool Process::IsProcessUsingExeOrDll(uint32 process_id,
+                                     const TCHAR* search_name,
+                                     bool search_main_executable_only) {
+  ASSERT1(search_name);
+
+  // Open the process
+  scoped_process process_handle(::OpenProcess(
+                                    PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
+                                    FALSE,
+                                    process_id));
+  if (!process_handle) {
+    UTIL_LOG(L4, (_T("[::OpenProcess failed][0x%x]"), HRESULTFromLastError()));
+    return false;
+  }
+
+  // Does the name represent a fully qualified name?
+  // We only do a simple check here
+  bool is_fully_qualified_name = String_FindChar(search_name, _T('\\')) != -1;
+  CString long_search_name;
+  if (is_fully_qualified_name) {
+    HRESULT hr(GetLongPathName(search_name, &long_search_name));
+    if (FAILED(hr)) {
+      UTIL_LOG(L4, (_T("[GetLongPathName fail][hr=x%x]"), hr));
+      return false;
+    }
+    search_name = long_search_name;
+  }
+
+  // Take a snapshot of all modules in the specified process
+  int num_modules_to_fetch = search_main_executable_only ? 1 :
+                                                           kMaxProcessModules;
+  HMODULE module_handles[kMaxProcessModules];
+  SetZero(module_handles);
+  uint32 bytes_needed = 0;
+  if (!::EnumProcessModules(get(process_handle),
+                            module_handles,
+                            num_modules_to_fetch * sizeof(HMODULE),
+                            reinterpret_cast<DWORD*>(&bytes_needed))) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[EnumProcessModules failed][0x%x]"), hr));
+
+    if (IsWow64(::GetCurrentProcessId())) {
+      // ::EnumProcessModules from a WoW64 process fails for x64 processes.
+      // We try ::GetProcessImageFileName as a workaround here.
+      return search_main_executable_only ?
+                 IsProcImageMatch(get(process_handle),
+                                  search_name,
+                                  is_fully_qualified_name) :
+                 false;
+    } else {
+      return false;
+    }
+  }
+
+  int num_modules = bytes_needed / sizeof(HMODULE);
+  if (num_modules > num_modules_to_fetch) {
+    num_modules = num_modules_to_fetch;
+  }
+
+  for (int i = 0; i < num_modules; ++i) {
+    TCHAR module_name[MAX_PATH];
+    SetZero(module_name);
+    if (!::GetModuleFileNameEx(get(process_handle),
+                               module_handles[i],
+                               module_name,
+                               arraysize(module_name))) {
+      UTIL_LOG(LEVEL_ERROR, (_T("[GetModuleFileNameEx fail[x%x]"),
+                             HRESULTFromLastError()));
+      continue;
+    }
+
+    if (IsModuleMatchingExeOrDll(module_name,
+                                 search_name,
+                                 is_fully_qualified_name)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+// Helper function to get long path name
+HRESULT Process::GetLongPathName(const TCHAR* short_name, CString* long_name) {
+  ASSERT1(short_name);
+  ASSERT1(long_name);
+
+  TCHAR temp_name[MAX_PATH];
+  SetZero(temp_name);
+
+  HRESULT hr = S_OK;
+  if (!::GetLongPathName(short_name, temp_name, arraysize(temp_name))) {
+    hr = HRESULTFromLastError();
+  } else {
+    long_name->SetString(temp_name);
+  }
+
+  return hr;
+}
+
+// Retrieve the FQPN for the executable file for a process.  (Note: Using
+// GetModuleFileNameEx is slower than GetProcessImageFileName, but the former
+// is available on Win2K, while the latter is only only on XP and up.)
+HRESULT Process::GetExecutablePath(uint32 process_id, CString *exe_path) {
+  ASSERT1(process_id);
+  ASSERT1(exe_path);
+
+  TCHAR temp_path[MAX_PATH];
+  SetZero(temp_path);
+
+  scoped_process process(::OpenProcess(PROCESS_QUERY_INFORMATION |
+                                       PROCESS_VM_READ,
+                                       FALSE,
+                                       process_id));
+  if (!valid(process)) {
+    return HRESULTFromLastError();
+  }
+
+  if (0 == ::GetModuleFileNameEx(get(process), NULL, temp_path, MAX_PATH)) {
+    return HRESULTFromLastError();
+  }
+
+  exe_path->SetString(temp_path);
+  return S_OK;
+}
+
+// Type definitions needed for GetCommandLine() and GetProcessIdFromHandle()
+// From MSDN document on NtQueryInformationProcess() and other sources
+typedef struct _PROCESS_BASIC_INFORMATION {
+  PVOID Reserved1;
+  BYTE *PebBaseAddress;
+  PVOID Reserved2[2];
+  ULONG_PTR UniqueProcessId;
+  PVOID Reserved3;
+} PROCESS_BASIC_INFORMATION;
+
+typedef enum _PROCESSINFOCLASS {
+  ProcessBasicInformation = 0,
+  ProcessWow64Information = 26
+} PROCESSINFOCLASS;
+
+typedef WINBASEAPI DWORD WINAPI
+GetProcessIdFn(
+    HANDLE Process
+);
+
+typedef LONG WINAPI
+NtQueryInformationProcess(
+  IN HANDLE ProcessHandle,
+  IN PROCESSINFOCLASS ProcessInformationClass,
+  OUT PVOID ProcessInformation,
+  IN ULONG ProcessInformationLength,
+  OUT PULONG ReturnLength OPTIONAL
+);
+
+typedef struct _RTL_DRIVE_LETTER_CURDIR {
+  USHORT Flags;
+  USHORT Length;
+  ULONG TimeStamp;
+  UNICODE_STRING DosPath;
+} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR;
+
+typedef struct _RTL_USER_PROCESS_PARAMETERS {
+  ULONG MaximumLength;
+  ULONG Length;
+  ULONG Flags;
+  ULONG DebugFlags;
+  PVOID ConsoleHandle;
+  ULONG ConsoleFlags;
+  HANDLE StdInputHandle;
+  HANDLE StdOutputHandle;
+  HANDLE StdErrorHandle;
+  UNICODE_STRING CurrentDirectoryPath;
+  HANDLE CurrentDirectoryHandle;
+  UNICODE_STRING DllPath;
+  UNICODE_STRING ImagePathName;
+  UNICODE_STRING CommandLine;
+  PVOID Environment;
+  ULONG StartingPositionLeft;
+  ULONG StartingPositionTop;
+  ULONG Width;
+  ULONG Height;
+  ULONG CharWidth;
+  ULONG CharHeight;
+  ULONG ConsoleTextAttributes;
+  ULONG WindowFlags;
+  ULONG ShowWindowFlags;
+  UNICODE_STRING WindowTitle;
+  UNICODE_STRING DesktopName;
+  UNICODE_STRING ShellInfo;
+  UNICODE_STRING RuntimeData;
+  RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20];
+} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
+
+// Get the function pointer to GetProcessId in KERNEL32.DLL
+static HRESULT EnsureGPIFunction(GetProcessIdFn** gpi_func_ptr) {
+  static GetProcessIdFn* gpi_func = NULL;
+  if (!gpi_func) {
+    HMODULE kernel32_module = ::GetModuleHandle(_T("kernel32.dll"));
+    if (!kernel32_module) {
+      return HRESULTFromLastError();
+    }
+    gpi_func = reinterpret_cast<GetProcessIdFn*>(
+                  ::GetProcAddress(kernel32_module, "GetProcessId"));
+    if (!gpi_func) {
+      return HRESULTFromLastError();
+    }
+  }
+
+  *gpi_func_ptr = gpi_func;
+  return S_OK;
+}
+
+// Get the function pointer to NtQueryInformationProcess in NTDLL.DLL
+static HRESULT EnsureQIPFunction(NtQueryInformationProcess** qip_func_ptr) {
+  static NtQueryInformationProcess* qip_func = NULL;
+  if (!qip_func) {
+    HMODULE ntdll_module = ::GetModuleHandle(_T("ntdll.dll"));
+    if (!ntdll_module) {
+      return HRESULTFromLastError();
+    }
+    qip_func = reinterpret_cast<NtQueryInformationProcess*>(
+                  ::GetProcAddress(ntdll_module, "NtQueryInformationProcess"));
+    if (!qip_func) {
+      return HRESULTFromLastError();
+    }
+  }
+
+  *qip_func_ptr = qip_func;
+  return S_OK;
+}
+
+// Obtain the process ID from a hProcess HANDLE
+ULONG Process::GetProcessIdFromHandle(HANDLE hProcess) {
+  if (SystemInfo::IsRunningOnXPSP1OrLater()) {
+    // Thunk to the documented ::GetProcessId() API
+    GetProcessIdFn* gpi_func = NULL;
+    HRESULT hr = EnsureGPIFunction(&gpi_func);
+    if (FAILED(hr)) {
+      ASSERT(FALSE,
+             (_T("Process::GetProcessIdFromHandle - EnsureGPIFunction")
+              _T(" failed[0x%x]"), hr));
+      return 0;
+    }
+    ASSERT1(gpi_func);
+    return gpi_func(hProcess);
+  }
+
+  // For lower versions of Windows, we use undocumented
+  // function NtQueryInformationProcess to get at the PID
+  NtQueryInformationProcess* qip_func = NULL;
+  HRESULT hr = EnsureQIPFunction(&qip_func);
+  if (FAILED(hr)) {
+    ASSERT(FALSE,
+           (_T("Process::GetProcessIdFromHandle - EnsureQIPFunction")
+            _T(" failed[0x%x]"), hr));
+    return 0;
+  }
+  ASSERT1(qip_func);
+
+  PROCESS_BASIC_INFORMATION info;
+  SetZero(info);
+  if (!NT_SUCCESS(qip_func(hProcess,
+                           ProcessBasicInformation,
+                           &info,
+                           sizeof(info),
+                           NULL))) {
+    ASSERT(FALSE, (_T("Process::GetProcessIdFromHandle - ")
+                   _T("NtQueryInformationProcess failed!")));
+    return 0;
+  }
+
+  return info.UniqueProcessId;
+}
+
+// Get the command line of a process
+HRESULT Process::GetCommandLine(uint32 process_id, CString* cmd_line) {
+  ASSERT1(process_id);
+  ASSERT1(cmd_line);
+
+  // Open the process
+  scoped_process process_handle(::OpenProcess(
+                                    PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
+                                    false,
+                                    process_id));
+  if (!process_handle) {
+    return HRESULTFromLastError();
+  }
+
+  // Obtain Process Environment Block
+  // Note that NtQueryInformationProcess is not available in Windows 95/98/ME
+  NtQueryInformationProcess* qip_func = NULL;
+  HRESULT hr = EnsureQIPFunction(&qip_func);
+
+  if (FAILED(hr)) {
+    return hr;
+  }
+  ASSERT1(qip_func);
+
+  PROCESS_BASIC_INFORMATION info;
+  SetZero(info);
+  if (!NT_SUCCESS(qip_func(get(process_handle),
+                           ProcessBasicInformation,
+                           &info,
+                           sizeof(info),
+                           NULL))) {
+    return E_FAIL;
+  }
+  BYTE* peb = info.PebBaseAddress;
+
+  // Read address of parameters (see some PEB reference)
+  // TODO(omaha): use offsetof(PEB, ProcessParameters) to replace 0x10
+  // http://msdn.microsoft.com/en-us/library/aa813706.aspx
+  SIZE_T bytes_read = 0;
+  uint32 dw = 0;
+  if (!::ReadProcessMemory(get(process_handle),
+                           peb + 0x10,
+                           &dw,
+                           sizeof(dw),
+                           &bytes_read)) {
+    return HRESULTFromLastError();
+  }
+
+  // Read all the parameters
+  RTL_USER_PROCESS_PARAMETERS params;
+  SetZero(params);
+  if (!::ReadProcessMemory(get(process_handle),
+                           reinterpret_cast<PVOID>(dw),
+                           &params,
+                           sizeof(params),
+                           &bytes_read)) {
+    return HRESULTFromLastError();
+  }
+
+  // Read the command line parameter
+  const int max_cmd_line_len = std::min(
+      static_cast<int>(params.CommandLine.MaximumLength),
+      kMaxCmdLineLengthBytes);
+  if (!::ReadProcessMemory(get(process_handle),
+                           params.CommandLine.Buffer,
+                           cmd_line->GetBufferSetLength(max_cmd_line_len),
+                           max_cmd_line_len,
+                           &bytes_read)) {
+    return HRESULTFromLastError();
+  }
+
+  cmd_line->ReleaseBuffer();
+
+  return S_OK;
+}
+
+// Check if the process is running with a specified path
+bool Process::IsProcessRunningWithPath(uint32 process_id, const TCHAR* path) {
+  ASSERT1(process_id);
+  ASSERT1(path && *path);
+
+  const int kProcessWaitModuleFullyUpMs = 100;
+  const int kProcessWaitModuleRetries = 10;
+
+  // Open the process
+  scoped_process process(::OpenProcess(
+                             PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
+                             false,
+                             process_id));
+  if (!process) {
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[Process::IsProcessRunningWithPath - OpenProcess failed]")
+              _T("[%u][0x%x]"),
+             process_id, HRESULTFromLastError()));
+    return false;
+  }
+
+  for (int i = 0; i < kProcessWaitModuleRetries; ++i) {
+    // Get the command line path of the main module
+    // Note that we are using psapi functions which is not supported in Windows
+    // 95/98/ME
+    //
+    // Sometimes it might be the case that the process is created but the main
+    // module is not fully loaded. If so, wait a while and then try again
+    TCHAR process_path[MAX_PATH];
+    if (::GetModuleFileNameEx(get(process),
+                              NULL,
+                              process_path,
+                              arraysize(process_path))) {
+      // Do the check
+      if (String_StartsWith(process_path, path, true)) {
+        return true;
+      }
+
+      // Try again with short form
+      TCHAR short_path[MAX_PATH];
+      if (::GetShortPathName(path, short_path, arraysize(short_path)) &&
+          String_StartsWith(process_path, short_path, true)) {
+        return true;
+      }
+
+      return false;
+    }
+
+    UTIL_LOG(LEVEL_ERROR,
+              (_T("[Process::IsProcessRunningWithPath - GetModuleFileNameEx ")
+               _T("failed][%u][0x%x]"),
+              process_id, HRESULTFromLastError()));
+
+    ::Sleep(kProcessWaitModuleFullyUpMs);
+  }
+
+  UTIL_LOG(LEVEL_ERROR,
+           (_T("[Process::IsProcessRunningWithPath - failed to get process ")
+            _T("path][%u][0x%x]"),
+            process_id, HRESULTFromLastError()));
+
+  return false;
+}
+
+// Get the process owner
+// Note that we may fail to get the owner which is not current user.
+HRESULT Process::GetProcessOwner(uint32 pid, CString* owner_sid) {
+  ASSERT1(pid);
+  ASSERT1(owner_sid);
+
+  scoped_process process(::OpenProcess(PROCESS_QUERY_INFORMATION, false, pid));
+  if (!valid(process)) {
+    return HRESULTFromLastError();
+  }
+
+  CAccessToken token;
+  CSid sid;
+  if (!token.GetProcessToken(READ_CONTROL | TOKEN_QUERY, get(process)) ||
+      !token.GetUser(&sid)) {
+    return HRESULTFromLastError();
+  }
+
+  *owner_sid = sid.Sid();
+  return S_OK;
+}
+
+// Creates an impersonation token for the user running process_id.
+// The caller is responsible for closing the returned handle.
+HRESULT Process::GetImpersonationToken(DWORD process_id, HANDLE* user_token) {
+  // Get a handle to the process.
+  scoped_process process(::OpenProcess(
+                             PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION,
+                             TRUE,
+                             process_id));
+  if (!valid(process)) {
+    HRESULT hr(HRESULTFromLastError());
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[GetImpersonationToken - ::OpenProcess failed][0x%x]"),
+              hr));
+    return hr;
+  }
+
+  HRESULT result = S_OK;
+  scoped_handle process_token;
+  if (!::OpenProcessToken(get(process), TOKEN_DUPLICATE | TOKEN_QUERY,
+                          address(process_token))) {
+    result = HRESULTFromLastError();
+  } else {
+    if (!::DuplicateTokenEx(get(process_token),
+                            TOKEN_IMPERSONATE | TOKEN_QUERY |
+                            TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE,
+                            NULL,
+                            SecurityImpersonation,
+                            TokenPrimary,
+                            user_token)) {
+      result = HRESULTFromLastError();
+    }
+  }
+
+  ASSERT(SUCCEEDED(result), (_T("[GetImpersonationToken Failed][hr=0x%x]"),
+                             result));
+  return result;
+}
+
+HRESULT Process::GetUsersOfProcesses(const TCHAR* task_name,
+                                     int maximum_users,
+                                     scoped_handle user_tokens[],
+                                     int* number_of_users) {
+  ASSERT1(task_name && *task_name);
+  ASSERT1(maximum_users);
+  ASSERT1(user_tokens);
+  ASSERT1(number_of_users);
+
+  scoped_hfile th32cs_snapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,
+                                                          0));
+  if (!valid(th32cs_snapshot)) {
+    HRESULT hr(HRESULTFromLastError());
+    UTIL_LOG(LEVEL_ERROR, (_T("[::CreateToolhelp32Snapshot fail][0x%x]"), hr));
+    return hr;
+  }
+
+  HRESULT result = S_OK;
+  *number_of_users = 0;
+  // Walk the list of processes.
+  PROCESSENTRY32 process = {0};
+  process.dwSize = sizeof(PROCESSENTRY32);
+  for (BOOL found = ::Process32First(get(th32cs_snapshot), &process); found;
+       found = ::Process32Next(get(th32cs_snapshot), &process)) {
+    // Check if it is one of the processes we are looking for.
+    if (_tcsicmp(task_name, process.szExeFile) == 0) {
+      // We match.  Get the user's token.
+      scoped_handle user_token;
+      if (FAILED(GetImpersonationToken(process.th32ProcessID,
+                                       address(user_token))))
+        continue;
+
+      // Search through the existing list to see if it's a duplicate.
+      // It's O(n^2) but we should have very few logged on users.
+      int i = 0;
+      for (; i < *number_of_users; i++) {
+        if (get(user_tokens[i]) == get(user_token)) {
+          // It's a duplicate.
+          break;
+        }
+      }
+      if (i >= *number_of_users) {
+        // It's a new one.  Add it if there's room.
+        ASSERT1(i < maximum_users);
+        if (i < maximum_users) {
+          // Release the user_token, we don't want it to be closed
+          // by the user_token destructor
+          reset(user_tokens[(*number_of_users)++], release(user_token));
+        }
+      }
+     }
+  }
+  return result;
+}
+
+HRESULT Process::GetImagePath(const CString& process_name,
+                              const CString& user_sid,
+                              CString* path) {
+  ASSERT1(path);
+
+  // Search for running processes with process_name.
+  uint32 mask = INCLUDE_ONLY_PROCESS_OWNED_BY_USER;
+  std::vector<CString> command_line;
+  std::vector<uint32> process_ids;
+  HRESULT hr = FindProcesses(mask,
+                             process_name,
+                             true,
+                             user_sid,
+                             command_line,
+                             &process_ids);
+  if (FAILED(hr)) {
+    UTIL_LOG(LEVEL_WARNING, (_T("[FindProcesses failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  if (process_ids.empty()) {
+    return E_FAIL;
+  }
+
+  uint32 process_id = process_ids[0];
+  UTIL_LOG(L4, (_T("[GetImagePath][pid=%d]"), process_id));
+  scoped_process process_handle(::OpenProcess(PROCESS_QUERY_INFORMATION |
+                                              PROCESS_VM_READ,
+                                              FALSE,
+                                              process_id));
+  if (!process_handle) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(L4, (_T("[OpenProcess failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  HMODULE module_handle = NULL;
+  DWORD bytes_needed = 0;
+  if (!::EnumProcessModules(get(process_handle),
+                            &module_handle,
+                            sizeof(HMODULE),
+                            &bytes_needed)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_WARNING, (_T("[EnumProcessModules failed][0x%08x]"), hr));
+    // ::EnumProcessModules from a WoW64 process fails for x64 processes. We try
+    // ::GetProcessImageFileName as a workaround here.
+    TCHAR image_name[MAX_PATH] = {0};
+    if (!GetProcessImageFileName(get(process_handle),
+                                 image_name,
+                                 arraysize(image_name))) {
+      HRESULT hr = HRESULTFromLastError();
+      UTIL_LOG(LE, (_T("[GetProcessImageFileName failed][0x%08x]"), hr));
+      return hr;
+    } else {
+      *path = image_name;
+      return S_OK;
+    }
+  }
+
+  TCHAR module_name[MAX_PATH] = {0};
+  if (!::GetModuleFileNameEx(get(process_handle),
+                             module_handle,
+                             module_name,
+                             arraysize(module_name))) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[GetModuleFileNameEx failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  *path = module_name;
+  return S_OK;
+}
+
+bool Process::IsWow64(uint32 pid) {
+  typedef BOOL (WINAPI *IsWow64Process)(HANDLE, BOOL*);
+  scoped_process handle(::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
+                                      false,
+                                      pid));
+  if (!handle) {
+    return false;
+  }
+
+  HINSTANCE kernel_instance = ::GetModuleHandle(_T("kernel32.dll"));
+  if (kernel_instance == NULL) {
+    ASSERT1(false);
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LW, (_T("[::GetModuleHandle  kernel32.dll failed][0x%08x]"), hr));
+    return false;
+  }
+
+  IsWow64Process pfn = reinterpret_cast<IsWow64Process>(::GetProcAddress(
+      kernel_instance,
+      "IsWow64Process"));
+  if (!pfn) {
+    UTIL_LOG(LW, (_T("[::IsWow64Process() not found in kernel32.dll]")));
+    return false;
+  }
+
+  BOOL wow64 = FALSE;
+  if (!(*pfn)(get(handle), &wow64)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LW, (_T("[::IsWow64Process() failed][0x%08x]"), hr));
+    return false;
+  }
+
+  return (wow64 != 0);
+}
+
+HRESULT Process::MakeProcessWindowForeground(const CString& executable) {
+  UTIL_LOG(L3, (_T("[MakeProcessWindowForeground]")));
+
+  CString sid;
+  HRESULT hr = omaha::user_info::GetProcessUser(NULL, NULL, &sid);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // This code does not handle two cases:
+  // 1. If a new process instance is starting up but there are other process
+  //    instances running, then we will not wait for the new process instance.
+  //    One way to fix this is to pass the number of expected processes to this
+  //    method.
+  // 2. If we find multiple processes, and we are able to find the windows only
+  //    for some of the processes (maybe because the rest are still starting up)
+  //    then we will only set the windows of the one that we found to the
+  //    foreground and ignore the rest.
+  bool found = false;
+  for (int retries = 0; retries < kNumRetriesToFindProcess && !found;
+       ++retries) {
+    std::vector<CString> command_lines;
+    std::vector<uint32> processes;
+    DWORD flags = EXCLUDE_CURRENT_PROCESS | INCLUDE_ONLY_PROCESS_OWNED_BY_USER;
+    hr = Process::FindProcesses(flags,
+                                executable,
+                                true,
+                                sid,
+                                command_lines,
+                                &processes);
+    if (FAILED(hr)) {
+      UTIL_LOG(LW, (_T("[FindProcesses failed][0x%08x]"), hr));
+      return hr;
+    }
+
+    UTIL_LOG(L3, (_T("[Found %d processes]"), processes.size()));
+    for (size_t i = 0; i < processes.size(); ++i) {
+      CSimpleArray<HWND> windows;
+      if (!WindowUtils::FindProcessWindows(processes[i], 0, &windows)) {
+        UTIL_LOG(L3, (_T("[FindProcessWindows failed][0x%08x]"), hr));
+        continue;
+      }
+
+      for (int j = 0; j < windows.GetSize(); ++j) {
+        if (WindowUtils::IsMainWindow(windows[j])) {
+          UTIL_LOG(L4, (_T("[Found main window of process %d]"), processes[i]));
+          WindowUtils::MakeWindowForeground(windows[j]);
+          ::FlashWindow(windows[j], true);
+          found = true;
+          break;
+        }
+      }
+    }
+
+    if (!found) {
+      ::Sleep(kFindProcessRetryIntervalMs);
+    }
+  }
+
+  return S_OK;
+}
+
+}  // namespace omaha
+
diff --git a/base/process.h b/base/process.h
new file mode 100644
index 0000000..1cde0c3
--- /dev/null
+++ b/base/process.h
@@ -0,0 +1,314 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Declares class Process to incapsulate win32
+// functions for creation and some manipulations of
+// processes.
+
+#ifndef OMAHA_BASE_PROCESS_H_
+#define OMAHA_BASE_PROCESS_H_
+
+#include <windows.h>
+#include <psapi.h>
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/scoped_any.h"
+
+namespace omaha {
+
+const int kMaxProcesses = 1024;
+const int kMaxProcessModules = 1024;
+
+// Exclude mask for finding processes.
+enum FindProcessesExcludeMask {
+  EXCLUDE_NONE = 0,
+  EXCLUDE_CURRENT_PROCESS = 0x1,
+  EXCLUDE_PROCESS_OWNED_BY_CURRENT_USER = 0x2,
+  EXCLUDE_PROCESS_OWNED_BY_SYSTEM = 0x4,
+  INCLUDE_ONLY_PROCESS_OWNED_BY_USER = 0x08,
+  EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING = 0x10,
+  INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING = 0x20,
+  EXCLUDE_PARENT_PROCESS = 0x40,
+};
+
+// Process info used in finding descendent processes.
+struct ProcessInfo {
+  uint32 process_id;
+  uint32 parent_id;
+#if !SHIPPING
+  CString exe_file;
+#endif
+};
+
+// Process class
+class Process {
+ public:
+  // Constructor
+  // Init the process object with the executable name
+  // and if known the window class name of the process.
+  // If window_class_name is known it will be easy
+  // to stop the process just by sending messages to
+  // a window.
+  // TODO(omaha3): window_class_name is not being used.
+  Process(const TCHAR* name, const TCHAR* window_class_name);
+
+  // Constructor.
+  // Init the process object with the process id.
+  explicit Process(uint32 process_id);
+
+  // Destructor
+  virtual ~Process();
+
+  // Start the process with some command line params if any.
+  // If a token is provided, it will be used to start
+  // the process in the default desktop of the
+  // token's session. The caller needs to be SYSTEM in
+  // this case.
+  virtual HRESULT Start(const TCHAR* command_line_parameters,
+                        HANDLE runas_token);
+
+  // Restart the process with the old command line params.
+  HRESULT Restart(HANDLE runas_token);
+
+  // Set shutdown event using in signaling the process watch.
+  void SetShutdownEvent(HANDLE shutdown_event);
+
+  // Sets the specified priority class to the process.
+  bool SetPriority(uint32 priority_class) const;
+
+  // Check if the process is running.
+  bool Running() const;
+
+  // Create a job and assign the process to it.
+  HANDLE AssignToJob();
+
+  // Wait some time till the process finishes.
+  bool WaitUntilDead(uint32 timeout_msec);
+
+  // Wait some time till the process and all its descendent processes finish.
+  bool WaitUntilAllDead(HANDLE job,
+                        uint32 timeout_msec,
+                        const TCHAR* path_to_exclude,
+                        uint32* exit_code);
+
+  // Wait until process is dead or a windows message arrives. For use in a
+  // message loop while waiting.
+  HRESULT WaitUntilDeadOrInterrupt(uint32 msec);
+  // Return values include CI_S_PROCESSWAIT_DEAD, CI_S_PROCESSWAIT_TIMEOUT,
+  // CI_S_PROCESSWAIT_MESSAGE.
+
+#if !SHIPPING
+  CString GetDebugInfo() const;
+#endif
+
+  // Return the process ID.
+  uint32 GetId() const;
+
+  // Return a readable representation of the process's name.
+  const TCHAR *GetName() const;
+
+  // Get win32 handle to process.
+  HANDLE GetHandle() const;
+
+  // Get process exit code.
+  bool GetExitCode(uint32* exit_code) const;
+
+  // can we kill the process via terminating
+  // some processes are not safe to terminate.
+  virtual bool IsTerminationAllowed() const;
+
+  // Second, more rude method to stop the process. window_class_name was
+  // not given or CloseWithMessage didn't succeed.
+  bool Terminate(uint32 wait_for_terminate_msec);
+
+  // Gets the parent process id.
+  HRESULT GetParentProcessId(uint32* parent_pid);
+
+  // Try to get a descendant process. Return process id if found.
+  uint32 GetDescendantProcess(HANDLE job,
+                              bool child_only,
+                              bool sole_descedent,
+                              const TCHAR* search_name,
+                              const TCHAR* path_to_exclude);
+
+  // Dynamically links and calls ::IsProcessInJob() in kernel32.dll.
+  static BOOL IsProcessInJob(HANDLE process_handle,
+                             HANDLE job_handle,
+                             PBOOL result);
+
+  // Try to get all matching descendant processes.
+  HRESULT GetAllDescendantProcesses(
+      HANDLE job,
+      bool child_only,
+      const TCHAR* search_name,
+      const TCHAR* path_to_exclude,
+      std::vector<ProcessInfo>* descendant_proc_ids);
+
+  // Finds the processes based on passed criteria.
+  static HRESULT FindProcesses(uint32 exclude_mask,
+                               const TCHAR* search_name,
+                               bool search_main_executable_only,
+                               std::vector<uint32>* process_ids_found);
+
+  // Find processes which loads the specified exe/dll. Uses the user_sid only
+  // if the INCLUDE_ONLY_PROCESS_OWNED_BY_USER flag has been set.
+  // The command_line is only used when
+  // EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING is set.
+  static HRESULT FindProcesses(uint32 exclude_mask,
+                               const TCHAR* search_name,
+                               bool search_main_executable_only,
+                               const CString& user_sid,
+                               const std::vector<CString>& command_line,
+                               std::vector<uint32>* process_ids_found);
+
+  // Find processes with the specified criteria running in specific session.
+  static HRESULT FindProcessesInSession(DWORD session_id,
+                                        uint32 exclude_mask,
+                                        const TCHAR* search_name,
+                                        bool search_main_executable_only,
+                                        const CString& user_sid,
+                                        const std::vector<CString>& cmd_lines,
+                                        std::vector<uint32>* process_ids_found);
+
+  // Is the process using the specified exe/dll.
+  static bool IsProcessUsingExeOrDll(uint32 process_id,
+                                     const TCHAR* search_name,
+                                     bool search_main_executable_only);
+
+  // Obtain the process ID from a hProcess HANDLE.
+  static ULONG GetProcessIdFromHandle(HANDLE hProcess);
+
+  // Get the fully qualified path of the executable file for a process.
+  static HRESULT GetExecutablePath(uint32 process_id, CString *exe_path);
+
+  // Get the command line of a process.
+  static HRESULT GetCommandLine(uint32 process_id, CString* cmd_line);
+
+  // Get the process owner.
+  static HRESULT GetProcessOwner(uint32 pid, CString* owner_sid);
+
+  // Creates an impersonation token for the user running process_id.
+  // The caller is responsible for closing the returned handle.
+  static HRESULT GetImpersonationToken(DWORD process_id, HANDLE* user_token);
+
+  // Returns user token handles for the users currently running the named task.
+  // maximum_users specifies the maximun number of handles to be retured.
+  // The actual number filled is returned.
+  static HRESULT GetUsersOfProcesses(const TCHAR* task_name,
+                                     int maximum_users,
+                                     scoped_handle users[],
+                                     int* number_of_users);
+
+  // Gets the on disk path from where the process image is loaded.
+  static HRESULT GetImagePath(const CString& process_name,
+                              const CString& user_sid,
+                              CString* path);
+
+  // Returns if the process is running under WOW64.
+  static bool IsWow64(uint32 pid);
+
+ public:
+  // How many times the process can be restarted in case it crashes.
+  virtual uint32 GetMaxNumberOfRestarts() const;
+
+  // Maximum amount of memory process is allowed to use before it's killed.
+  // Default of 0 means unlimited.
+  virtual uint32 GetMaxMemory() const;
+
+  // Have we exceeded the number of maximum restarting.
+  bool AllowedToRestart() const;
+
+  // In case of crash, how soon to restart.
+  virtual uint32 GetRestartInterval() const;
+
+  // The idea is the following. Each process has maximum number of restarts.
+  // As soon as the process reaches that number of restarts in should no longer
+  // be restarted unless the time window in which the process was crashing is
+  // more than the value returned by this function. For example:
+  // Process X returns 3 from the function GetMaxNumberOfRestarts.
+  // The same process returns 30*1000*60 (30 minutes) from
+  // GetTimeWindowForCrashes if  process X crashed more than 3 times in 30
+  // minutes it will not be restarted. if it took more than 30 minutes for
+  // process X to crash more than 3 times - internal counters for number of
+  // crashes will be reset and the process will be happily restarted.
+  // Each derived process can override this function to return its own time
+  // window for crashes.
+  // Default implementation returns INFINITE which means that this is not time
+  // based at all, if the process crashed more than the value returned by
+  // GetMaxNumberOfRestarts it will not be restarted no matter how long it took.
+  virtual uint32 GetTimeWindowForCrashes() const;
+
+  // Sets the main window of all process instances to the foreground.
+  static HRESULT MakeProcessWindowForeground(const CString& executable);
+
+ private:
+  mutable uint32 number_of_restarts_;
+  CString command_line_;
+  CString command_line_parameters_;
+  CString window_class_name_;
+  CString name_;
+  scoped_process process_;
+  uint32 process_id_;
+  mutable uint32 time_of_start_;
+  mutable uint32 exit_code_;
+  HANDLE shutdown_event_;
+
+  // Helper function to wait till the process and all its descendent processes
+  // finish.
+  bool InternalWaitUntilAllDead(HANDLE job,
+                                uint32 timeout_msec,
+                                const TCHAR* path_to_exclude,
+                                uint32* exit_code);
+
+  // Check if the process is running with a specified path.
+  static bool IsProcessRunningWithPath(uint32 process_id, const TCHAR* path);
+
+  // Checks if the command line of the process has been specified as one to
+  // ignore.
+  static bool IsStringPresentInList(const CString& process_command_line,
+                                    const std::vector<CString>& list);
+
+
+  // Helper function to get long path name.
+  static HRESULT GetLongPathName(const TCHAR* short_name, CString* long_name);
+
+  // Helper for Process::IsProcessUsingExeOrDll(). Use GetProcessImageFileName
+  // to get the filename, and match against search_name.
+  static bool IsProcImageMatch(HANDLE proc_handle,
+                               const TCHAR* search_name,
+                               bool is_fully_qualified_name);
+
+  // Dynamically links and calls ::GetProcessImageFileName() in psapi.dll.
+  static DWORD GetProcessImageFileName(HANDLE proc_handle,
+                                       LPTSTR image_file,
+                                       DWORD file_size);
+
+  // Helper for Process::IsProcessUsingExeOrDll().
+  // Is there a match between the module and the specified exe/dll?
+  static bool IsModuleMatchingExeOrDll(const TCHAR* module_name,
+                                       const TCHAR* search_name,
+                                       bool is_fully_qualified_name);
+
+#if !SHIPPING
+  CString debug_info_;
+#endif
+  DISALLOW_EVIL_CONSTRUCTORS(Process);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_PROCESS_H_
diff --git a/base/process_unittest.cc b/base/process_unittest.cc
new file mode 100644
index 0000000..70bfd82
--- /dev/null
+++ b/base/process_unittest.cc
@@ -0,0 +1,243 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+//
+// Process unit tests.
+
+#include <vector>
+
+#include "omaha/base/app_util.h"
+#include "omaha/base/path.h"
+#include "omaha/base/process.h"
+#include "omaha/base/user_info.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+const int kWaitUntilDeadMs = 10000;
+
+// Process class that terminates the associated process when deleted.
+class ScopedProcess : public Process {
+ public:
+  explicit ScopedProcess(const TCHAR* name) : Process(name, NULL) {}
+  virtual ~ScopedProcess() {
+    Terminate(0);
+    EXPECT_TRUE(WaitUntilDead(kWaitUntilDeadMs));
+  }
+};
+
+TEST(ProcessTest, StartOneProcess) {
+  const TCHAR kExecutableName[] = _T("cmd.exe");
+  const TCHAR kExecutableArguments[] = _T("/c exit 702");
+  const int kExpectedExitCode = 702;
+
+  CString path = ConcatenatePath(app_util::GetSystemDir(), kExecutableName);
+  ScopedProcess process(path);
+
+  ASSERT_HRESULT_SUCCEEDED(process.Start(kExecutableArguments, NULL));
+  EXPECT_TRUE(process.WaitUntilDead(kWaitUntilDeadMs));
+
+  // Check the exit code to get some assurance that the process actually ran.
+  uint32 exit_code = 0;
+  EXPECT_TRUE(process.GetExitCode(&exit_code));
+  EXPECT_EQ(kExpectedExitCode, exit_code);
+}
+
+// Dummy process to spin off and then find.  The numeric argument will make
+// netstat run until it's killed by the ScopedProcess destructor.
+const TCHAR kTestExecutable[] = _T("netstat.exe");
+const TCHAR kTestArguments[] = _T("10");
+const TCHAR kTestExcludeArguments[] = _T("-o 20");
+const TCHAR kTestExcludeString[] = _T("20");
+const TCHAR kTestIncludeArguments[] = _T("-o 30");
+const TCHAR kTestIncludeString[] = _T("30");
+const int kWaitForProcessStartMs = 500;
+const int kMaxWaitIterations = 10;
+
+TEST(ProcessTest, FindOneProcess) {
+  CString path = ConcatenatePath(app_util::GetSystemDir(), kTestExecutable);
+  ScopedProcess process(path);
+  ASSERT_HRESULT_SUCCEEDED(process.Start(kTestArguments, NULL));
+  for (int i = 0; i < kMaxWaitIterations; ++i) {
+    ::Sleep(kWaitForProcessStartMs);
+    if (process.Running())
+      break;
+  }
+  EXPECT_TRUE(process.Running());
+
+  // Try to find the test process.
+  uint32 exclude_mask = INCLUDE_ONLY_PROCESS_OWNED_BY_USER;
+  CString user_sid;
+  std::vector<CString> command_lines;
+  std::vector<uint32> process_ids;
+
+  ASSERT_SUCCEEDED(omaha::user_info::GetProcessUser(NULL, NULL, &user_sid));
+
+  // This test intermittently fails to find the process when run on Pulse.
+  // This code attempts to ensure that the process is further along in the
+  // initialization process by waiting until Process::GetCommandLine succeeds.
+  // This test case does not result in FindProcesses using GetCommandLine, but
+  // waiting until this point may be enough to address the intermitent failures.
+  HRESULT hr = E_FAIL;
+  CString process_cmd;
+  for (int tries = 0; tries < 100 && FAILED(hr); ++tries) {
+    ::Sleep(50);
+    hr = Process::GetCommandLine(process.GetId(), &process_cmd);
+  }
+  EXPECT_SUCCEEDED(hr);
+
+  ASSERT_SUCCEEDED(Process::FindProcesses(exclude_mask,
+                                          kTestExecutable,
+                                          true,
+                                          user_sid,
+                                          command_lines,
+                                          &process_ids));
+  ASSERT_EQ(1, process_ids.size());  // Exit before accessing invalid element.
+  EXPECT_EQ(process.GetId(), process_ids[0]);
+}
+
+TEST(ProcessTest, ExcludeProcess) {
+  // Make sure the test process is not already running.
+  uint32 exclude_mask = INCLUDE_ONLY_PROCESS_OWNED_BY_USER;
+  CString user_sid;
+  std::vector<CString> command_lines;
+  std::vector<uint32> process_ids;
+
+  ASSERT_SUCCEEDED(omaha::user_info::GetProcessUser(NULL, NULL, &user_sid));
+  ASSERT_SUCCEEDED(Process::FindProcesses(exclude_mask,
+                                          kTestExecutable,
+                                          true,
+                                          user_sid,
+                                          command_lines,
+                                          &process_ids));
+  ASSERT_EQ(0, process_ids.size());
+
+  // Ok, test process not running. Let's continue running the test.
+  CString path = ConcatenatePath(app_util::GetSystemDir(), kTestExecutable);
+  ScopedProcess process(path);
+  ScopedProcess exclude_process(path);
+
+  ASSERT_HRESULT_SUCCEEDED(process.Start(kTestArguments, NULL));
+  ASSERT_HRESULT_SUCCEEDED(exclude_process.Start(kTestExcludeArguments, NULL));
+  for (int i = 0; i < kMaxWaitIterations; ++i) {
+    ::Sleep(kWaitForProcessStartMs);
+    if (process.Running() && exclude_process.Running())
+      break;
+  }
+  EXPECT_TRUE(process.Running());
+  EXPECT_TRUE(exclude_process.Running());
+
+  // Try to find just the first process, excluding the other.
+  exclude_mask = INCLUDE_ONLY_PROCESS_OWNED_BY_USER |
+                 EXCLUDE_CURRENT_PROCESS |
+                 EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING;
+
+  command_lines.push_back(kTestExcludeString);
+  ASSERT_SUCCEEDED(Process::FindProcesses(exclude_mask,
+                                          kTestExecutable,
+                                          true,
+                                          user_sid,
+                                          command_lines,
+                                          &process_ids));
+  ASSERT_EQ(1, process_ids.size());
+  EXPECT_EQ(process.GetId(), process_ids[0]);
+}
+
+TEST(ProcessTest, IncludeProcess) {
+  CString path = ConcatenatePath(app_util::GetSystemDir(), kTestExecutable);
+  ScopedProcess process(path);
+  ScopedProcess include_process(path);
+
+  ASSERT_HRESULT_SUCCEEDED(process.Start(kTestArguments, NULL));
+  ASSERT_HRESULT_SUCCEEDED(include_process.Start(kTestIncludeArguments, NULL));
+  for (int i = 0; i < kMaxWaitIterations; ++i) {
+    ::Sleep(kWaitForProcessStartMs);
+    if (process.Running() && include_process.Running())
+      break;
+  }
+  EXPECT_TRUE(process.Running());
+  EXPECT_TRUE(include_process.Running());
+
+  // Try to find just the first process, excluding the other.
+  uint32 exclude_mask = INCLUDE_ONLY_PROCESS_OWNED_BY_USER |
+                        EXCLUDE_CURRENT_PROCESS |
+                        INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING;
+  CString user_sid;
+  std::vector<CString> command_lines;
+  std::vector<uint32> process_ids;
+
+  command_lines.push_back(kTestIncludeString);
+  ASSERT_SUCCEEDED(omaha::user_info::GetProcessUser(NULL, NULL, &user_sid));
+  ASSERT_SUCCEEDED(Process::FindProcesses(exclude_mask,
+                                          kTestExecutable,
+                                          true,
+                                          user_sid,
+                                          command_lines,
+                                          &process_ids));
+  ASSERT_EQ(1, process_ids.size());
+  EXPECT_EQ(include_process.GetId(), process_ids[0]);
+}
+
+TEST(ProcessTest, GetImagePath) {
+  // Get this module's path.
+  HMODULE handle = ::GetModuleHandle(NULL);
+  ASSERT_TRUE(handle != NULL);
+
+  TCHAR file_name[MAX_PATH] = {0};
+  ASSERT_NE(::GetModuleFileName(handle, file_name, MAX_PATH), 0);
+  ASSERT_NE(0, wcslen(file_name));
+
+  CString exe = GetFileFromPath(file_name);
+  ASSERT_FALSE(exe.IsEmpty());
+
+  CString user_sid;
+  ASSERT_SUCCEEDED(omaha::user_info::GetProcessUser(NULL, NULL, &user_sid));
+
+  // Test the method.
+  CString path;
+  ASSERT_SUCCEEDED(Process::GetImagePath(exe, user_sid, &path));
+
+  // Compare the result.
+  ASSERT_STREQ(file_name, path);
+}
+
+TEST(ProcessTest, GetParentProcessId_CurrentProcess) {
+  Process process(::GetCurrentProcessId());
+  uint32 parent_pid = 0;
+  EXPECT_SUCCEEDED(process.GetParentProcessId(&parent_pid));
+  EXPECT_NE(0, parent_pid);
+}
+
+TEST(ProcessTest, GetParentProcessId_ChildProcess) {
+  CString path = ConcatenatePath(app_util::GetSystemDir(), kTestExecutable);
+  ScopedProcess process(path);
+
+  EXPECT_HRESULT_SUCCEEDED(process.Start(kTestArguments, NULL));
+  for (int i = 0; i < kMaxWaitIterations; ++i) {
+    ::Sleep(kWaitForProcessStartMs);
+    if (process.Running()) {
+      break;
+    }
+  }
+
+  EXPECT_TRUE(process.Running());
+
+  uint32 parent_pid = 0;
+  EXPECT_SUCCEEDED(process.GetParentProcessId(&parent_pid));
+  EXPECT_EQ(::GetCurrentProcessId(), parent_pid);
+}
+
+}  // namespace omaha
+
diff --git a/base/processor_type.cc b/base/processor_type.cc
new file mode 100644
index 0000000..7517464
--- /dev/null
+++ b/base/processor_type.cc
@@ -0,0 +1,51 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Processor brand detection implementation
+//
+#include "omaha/base/processor_type.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+// Returns "GenuineIntel", "AuthenticAMD", etc...
+CString GetProcessorType() {
+  // Reference for checking for chip type
+  // http://en.wikipedia.org/wiki/CPUID
+
+  // There are 12 characters in the chip id (e.g. "GenuineIntel")
+  union {
+    char str[16];
+    uint32 n[4];
+  } regs;
+  SetZero(regs.str);
+  __asm {
+    xor eax, eax;  // Set EAX = 0 to get CPU type
+    cpuid;         // Now ebx, edx, ecx will contain cpu brand
+    mov regs.n[0], ebx;
+    mov regs.n[4], edx;
+    mov regs.n[8], ecx;
+  }
+  return CString(regs.str);
+}
+
+// Returns true if we're running on an Intel
+bool IsIntelProcessor() {
+  CString chip = GetProcessorType();
+  return (chip == kIntelVendorId);
+}
+
+}  // namespace omaha
diff --git a/common/processor_type.h b/base/processor_type.h
similarity index 100%
rename from common/processor_type.h
rename to base/processor_type.h
diff --git a/base/program_instance.cc b/base/program_instance.cc
new file mode 100644
index 0000000..5758b15
--- /dev/null
+++ b/base/program_instance.cc
@@ -0,0 +1,44 @@
+// Copyright 2005-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// The class uses a singleton mutex, with a local name in the user session, to
+// prevent multiple instances of a program.
+
+#include "omaha/base/program_instance.h"
+#include "omaha/base/debug.h"
+
+namespace omaha {
+
+bool ProgramInstance::EnsureSingleInstance() {
+  return CheckSingleInstance();
+}
+
+bool ProgramInstance::CheckSingleInstance() {
+  ASSERT1(!mutex_name_.IsEmpty());
+
+  reset(mutex_, ::CreateMutex(NULL, false, mutex_name_));
+  if (!mutex_) {
+    // We were not able to create the mutex instance for some reason.
+    return false;
+  }
+  DWORD error = ::GetLastError();
+  if (error == ERROR_ALREADY_EXISTS) {
+    // The program instance is already running since the mutex already exists.
+    return false;
+  }
+  return true;
+}
+
+}  // namespace omaha
diff --git a/base/program_instance.h b/base/program_instance.h
new file mode 100644
index 0000000..06a923d
--- /dev/null
+++ b/base/program_instance.h
@@ -0,0 +1,43 @@
+// Copyright 2005-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_BASE_PROGRAM_INSTANCE_H_
+#define OMAHA_BASE_PROGRAM_INSTANCE_H_
+
+#include <atlstr.h>
+#include "base/basictypes.h"
+#include "omaha/third_party/smartany/auto_any.h"
+
+namespace omaha {
+
+  // Helps limit the number of instances of a program. The class itself does not
+  // limit the number of instances. The calling code is expected to take action
+  // based on the return of EnsureSingleInstance function.
+  class ProgramInstance {
+   public:
+    explicit ProgramInstance(const TCHAR* mutex_name)
+        : mutex_name_(mutex_name) {}
+    virtual ~ProgramInstance() {}
+    bool EnsureSingleInstance();
+   private:
+    bool CheckSingleInstance();
+    CString mutex_name_;
+    auto_mutex mutex_;
+    DISALLOW_EVIL_CONSTRUCTORS(ProgramInstance);
+  };
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_PROGRAM_INSTANCE_H_
diff --git a/base/queue_timer.cc b/base/queue_timer.cc
new file mode 100644
index 0000000..255fd2b
--- /dev/null
+++ b/base/queue_timer.cc
@@ -0,0 +1,185 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// The implementation is straightforward except the destruction of the
+// QueueTimer which needs some clarification.
+// If there is no callback running, then the destructor gets the critical
+// section and then it blocks on the DeleteTimerQueueTimer call, waiting for
+// the kernel to clean up the timer handle. The callback never fires in this
+// case.
+// If a callback is running, then there are two possibilities:
+// 1. The callback gets the critical section. The callback runs as usual and
+// then the destructor gets the critical section. This is also easy.
+// 2. The destructor gets the critical section. In this case, the callback
+// tries the critical section then it returns right away.
+//
+// Alarm timers are started and restarted every time they fire. The usage
+// patterns for alarms is usually Start, Callback, Start, Callback, etc...
+// The cleanup of an alarm timer handle usually happens in the callback, unless
+// the destructor of the QueueTimer is called, in which case the logic
+// above applies.
+//
+// Periodic timers are only started once: Start, Callback, Callback, etc...
+// In this case, the destructor does all the necessary cleanup.
+// Periodic timers must fire at intervals that are reasonable long so that
+// the callbacks do not queue up.
+
+#include "omaha/base/queue_timer.h"
+
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+
+namespace omaha {
+
+QueueTimer::QueueTimer(HANDLE timer_queue, Callback callback, void* ctx)
+    : callback_tid_(0),
+      ctx_(ctx),
+      due_time_(0),
+      period_(0),
+      flags_(0),
+      timer_handle_(NULL),
+      timer_queue_(timer_queue),
+      callback_(callback) {
+  UTIL_LOG(L3, (_T("[QueueTimer::QueueTimer][0x%p]"), this));
+  ASSERT1(timer_queue);
+  ASSERT1(callback);
+  ::InitializeCriticalSection(&dtor_cs_);
+  ::InitializeCriticalSection(&cs_);
+}
+
+// The destructor blocks on waiting for the timer kernel object to be deleted.
+// We can't call the destructor of QueueTimer while we are handling a callback.
+// This will result is a deadlock.
+QueueTimer::~QueueTimer() {
+  UTIL_LOG(L3, (_T("[QueueTimer::~QueueTimer][0x%p]"), this));
+
+  ::EnterCriticalSection(&dtor_cs_);
+  if (timer_handle_) {
+    ASSERT1(callback_tid_ != ::GetCurrentThreadId());
+
+    // This is a blocking call waiting for all callbacks to clear up.
+    bool res = !!::DeleteTimerQueueTimer(timer_queue_,
+                                         timer_handle_,
+                                         INVALID_HANDLE_VALUE);
+    ASSERT1(res);
+    timer_handle_ = NULL;
+  }
+  callback_ = NULL;
+  timer_queue_ = NULL;
+  flags_ = 0;
+  period_ = 0;
+  due_time_ = 0;
+  ctx_ = 0;
+  callback_tid_ = 0;
+  ::LeaveCriticalSection(&dtor_cs_);
+
+  ::DeleteCriticalSection(&cs_);
+  ::DeleteCriticalSection(&dtor_cs_);
+}
+
+// Thread safe.
+HRESULT QueueTimer::Start(int due_time, int period, uint32 flags) {
+  // Since Start creates the timer there could be a race condition where
+  // the timer could fire while we are still executing Start. We protect
+  // the start with a critical section so the Start completes before the
+  // timer can be entered by the callback.
+
+  ::EnterCriticalSection(&cs_);
+  HRESULT hr = DoStart(due_time, period, flags);
+  ::LeaveCriticalSection(&cs_);
+  return hr;
+}
+
+// Thread-safe.
+void QueueTimer::TimerCallback(void* param, BOOLEAN timer_or_wait) {
+  ASSERT1(param);
+  VERIFY1(timer_or_wait);
+
+  QueueTimer* timer = static_cast<QueueTimer*>(param);
+
+  if (!::TryEnterCriticalSection(&timer->dtor_cs_)) {
+    return;
+  }
+
+  ::EnterCriticalSection(&timer->cs_);
+  timer->DoCallback();
+  ::LeaveCriticalSection(&timer->cs_);
+
+  ::LeaveCriticalSection(&timer->dtor_cs_);
+}
+
+
+HRESULT QueueTimer::DoStart(int due_time, int period, uint32 flags) {
+  UTIL_LOG(L2, (_T("[QueueTimer::DoStart][0x%p][%d][%d][0x%08u]"),
+                this, due_time, period, flags));
+  due_time_ = due_time;
+  period_ = period;
+  flags_ = flags;
+
+  // Application Verifier says period must be 0 for WT_EXECUTEONLYONCE timers.
+  if ((flags & WT_EXECUTEONLYONCE) && period != 0) {
+    return E_INVALIDARG;
+  }
+
+  // Periodic timers can't be started more than one time.
+  if (timer_handle_) {
+    return E_UNEXPECTED;
+  }
+
+  bool res = !!::CreateTimerQueueTimer(&timer_handle_,
+                                       timer_queue_,
+                                       &QueueTimer::TimerCallback,
+                                       this,
+                                       due_time,
+                                       period,
+                                       flags_);
+  if (!res) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[QueueTimer::Start failed][0x%p][0x%08x]"), this, hr));
+    return hr;
+  }
+
+  ASSERT1(timer_handle_);
+  UTIL_LOG(L3, (_T("[QueueTimer::Start timer created][0x%p]"), this));
+  return S_OK;
+}
+
+void QueueTimer::DoCallback() {
+  UTIL_LOG(L2, (_T("[QueueTimer::OnCallback][0x%p]"), this));
+
+  ASSERT1(timer_queue_);
+  ASSERT1(timer_handle_);
+  ASSERT1(callback_);
+
+  if (!period_) {
+    // Non-periodic aka alarm timers fire only once. We delete the timer
+    // handle so that the timer object can be restarted later on.
+    // The call below is non-blocking. The deletion of the kernel object can
+    // succeed right away, for example if the timer runs in the timer thread
+    // itself. Otherwise, if the last error is ERROR_IO_PENDING the kernel
+    // cleans up the object once the callback returns.
+    bool res = !!::DeleteTimerQueueTimer(timer_queue_, timer_handle_, NULL);
+    ASSERT1(res || (!res && ::GetLastError() == ERROR_IO_PENDING));
+    timer_handle_ = NULL;
+  }
+
+  callback_tid_ = ::GetCurrentThreadId();
+  callback_(this);
+  callback_tid_ = 0;
+}
+
+}  // namespace omaha
+
diff --git a/common/queue_timer.h b/base/queue_timer.h
similarity index 100%
rename from common/queue_timer.h
rename to base/queue_timer.h
diff --git a/base/queue_timer_unittest.cc b/base/queue_timer_unittest.cc
new file mode 100644
index 0000000..383e1a5
--- /dev/null
+++ b/base/queue_timer_unittest.cc
@@ -0,0 +1,171 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include <iostream>
+#include "base/scoped_ptr.h"
+#include "omaha/base/queue_timer.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/timer.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+class QueueTimerTest : public testing::Test {
+ protected:
+  QueueTimerTest()
+      : timer_queue_(NULL),
+        cnt_(0),
+        max_cnt_(0) {}
+
+  virtual void SetUp() {
+    cnt_ = 0;
+    timer_queue_ = ::CreateTimerQueue();
+    ASSERT_TRUE(timer_queue_);
+    reset(ev_, ::CreateEvent(NULL, true, false, NULL));
+  }
+
+  virtual void TearDown() {
+    // First destroy the timer, otherwise the timer could fire and
+    // access invalid test case state.
+    queue_timer_.reset();
+    reset(ev_);
+    ASSERT_TRUE(::DeleteTimerQueueEx(timer_queue_, INVALID_HANDLE_VALUE));
+    cnt_ = 0;
+    max_cnt_ = 0;
+  }
+
+  // Handles the alarm mode of the timer queue, where the timer fires once.
+  static void AlarmCallback(QueueTimer* queue_timer);
+
+  // Handles the periodic timer.
+  static void TimerCallback(QueueTimer* queue_timer);
+
+  HANDLE timer_queue_;
+  scoped_ptr<QueueTimer> queue_timer_;
+  scoped_event ev_;
+  volatile int cnt_;
+  volatile int max_cnt_;
+};
+
+void QueueTimerTest::AlarmCallback(QueueTimer* queue_timer) {
+  ASSERT_TRUE(queue_timer);
+  void* ctx = queue_timer->ctx();
+  QueueTimerTest* test = static_cast<QueueTimerTest*>(ctx);
+  test->cnt_ = 1;
+  ASSERT_TRUE(::SetEvent(get(test->ev_)));
+}
+
+void QueueTimerTest::TimerCallback(QueueTimer* queue_timer) {
+  ASSERT_TRUE(queue_timer);
+  void* ctx = queue_timer->ctx();
+  QueueTimerTest* test = static_cast<QueueTimerTest*>(ctx);
+
+  // Wait max_cnt_ ticks before signaling.
+  ++test->cnt_;
+  if (test->cnt_ == test->max_cnt_) {
+    ::SetEvent(get(test->ev_));
+  }
+}
+
+TEST_F(QueueTimerTest, QuickAlarm) {
+  queue_timer_.reset(new QueueTimer(timer_queue_,
+                                    &QueueTimerTest::AlarmCallback,
+                                    this));
+  const int kWaitTimeMaxMs = 1000;
+
+  // Set the timer to fire once right away.
+  LowResTimer timer(true);
+  ASSERT_HRESULT_SUCCEEDED(
+      queue_timer_->Start(0, 0, WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE));
+  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(ev_), kWaitTimeMaxMs));
+  EXPECT_EQ(1, cnt_);
+
+  // Expect the alarm to fire quickly.
+  EXPECT_GE(50u, timer.GetMilliseconds());
+}
+
+// The test takes about 50 seconds to run.
+TEST_F(QueueTimerTest, Alarm) {
+  if (!ShouldRunEnormousTest()) {
+    return;
+  }
+
+  queue_timer_.reset(new QueueTimer(timer_queue_,
+                                    &QueueTimerTest::AlarmCallback,
+                                    this));
+  const int kWaitTimeMaxMs = 60 * 1000;     // 60 seconds.
+
+  // Set the timer to fire once after 5 sec, 10 sec, 15 sec, 20 sec and wait.
+  LowResTimer timer(false);
+  for (int i = 1; i <= 4; ++i) {
+    const int time_interval_ms = 5 * 1000 * i;
+    SCOPED_TRACE(testing::Message() << "time_interval_ms=" << time_interval_ms);
+
+    timer.Start();
+    ASSERT_HRESULT_SUCCEEDED(
+        queue_timer_->Start(time_interval_ms, 0, WT_EXECUTEONLYONCE));
+    EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(ev_), kWaitTimeMaxMs));
+
+    int actual_time_ms = timer.GetMilliseconds();
+    timer.Reset();
+
+    // Expect the alarm to fire anytime between a narrow interval.
+    EXPECT_EQ(1, cnt_);
+    EXPECT_LE(time_interval_ms - 50, actual_time_ms);
+    EXPECT_GE(time_interval_ms + 150, actual_time_ms);
+
+    cnt_ = 0;
+    ::ResetEvent(get(ev_));
+  }
+
+  // Set the timer to fire once after 2000 ms but do not wait for it to fire.
+  ASSERT_HRESULT_SUCCEEDED(
+      queue_timer_->Start(2000, 0, WT_EXECUTEONLYONCE));
+  EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(get(ev_), 100));
+  EXPECT_EQ(0, cnt_);
+}
+
+// The test takes about 35 seconds to run.
+TEST_F(QueueTimerTest, Timer) {
+  if (!ShouldRunEnormousTest()) {
+    return;
+  }
+
+  queue_timer_.reset(new QueueTimer(timer_queue_,
+                                    &QueueTimerTest::TimerCallback,
+                                    this));
+  const int kWaitTimeMaxMs = 60 * 1000;       // 60 seconds.
+
+  max_cnt_ = 4;
+
+  // Set the timer to fire at 10 seconds intervals with an initial delay of
+  // 5 seconds.
+  LowResTimer timer(true);
+  ASSERT_HRESULT_SUCCEEDED(queue_timer_->Start(5000, 10000, WT_EXECUTEDEFAULT));
+  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(ev_), kWaitTimeMaxMs));
+
+  int actual_time_ms = timer.GetMilliseconds();
+
+  EXPECT_EQ(4, cnt_);
+  EXPECT_LE(35 * 1000 - 50, actual_time_ms);
+  EXPECT_GE(35 * 1000 + 350, actual_time_ms);
+
+  // Tests it can't start periodic timers more than one time.
+  ASSERT_EQ(E_UNEXPECTED, queue_timer_->Start(25, 50, WT_EXECUTEDEFAULT));
+}
+
+}  // namespace omaha
+
diff --git a/base/reactor.cc b/base/reactor.cc
new file mode 100644
index 0000000..1a02aa3
--- /dev/null
+++ b/base/reactor.cc
@@ -0,0 +1,205 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include "omaha/base/reactor.h"
+
+#include <vector>
+#include "base/scoped_ptr.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/event_handler.h"
+
+namespace omaha {
+
+Reactor::Reactor() {
+  CORE_LOG(L4, (_T("[Reactor::Reactor]")));
+  ::InitializeCriticalSection(&cs_);
+}
+
+Reactor::~Reactor() {
+  CORE_LOG(L4, (_T("[Reactor::~Reactor]")));
+
+  // Each handle must be unregistered before destroying the reactor.
+  ASSERT1(handlers_.empty());
+  ::DeleteCriticalSection(&cs_);
+}
+
+// The reactor loop is just an efficient wait, as the demultiplexing of the
+// events is actually done by the OS thread pool.
+// TODO(omaha): replace the alertable wait with waiting on an event and provide
+// a method for the reactor to stop handling events.
+HRESULT Reactor::HandleEvents() {
+  CORE_LOG(L1, (_T("[Reactor::HandleEvents]")));
+  VERIFY1(::SleepEx(INFINITE, true) == WAIT_IO_COMPLETION);
+  CORE_LOG(L1, (_T("[Reactor::HandleEvents exit]")));
+  return S_OK;
+}
+
+void __stdcall Reactor::Callback(void* param, BOOLEAN timer_or_wait) {
+  ASSERT1(param);
+
+  // Since we wait an INFINITE the wait handle is always signaled.
+  VERIFY1(!timer_or_wait);
+  RegistrationState* state = static_cast<RegistrationState*>(param);
+  ASSERT1(state->reactor);
+  state->reactor->DoCallback(state);
+}
+
+// Method does not check to see if the same handle is registered twice.
+HRESULT Reactor::RegisterHandle(HANDLE handle,
+                                EventHandler* event_handler,
+                                uint32 flags) {
+  ASSERT1(handle);
+  ASSERT1(event_handler);
+
+  if (!handle || !event_handler) {
+    return E_INVALIDARG;
+  }
+
+  scoped_ptr<RegistrationState> state(new RegistrationState);
+  state->event_handler = event_handler;
+  state->handle = handle;
+  state->reactor = this;
+  state->flags = flags | WT_EXECUTEONLYONCE;
+
+  // The reactor only calls the handler once.
+  ASSERT1(WT_EXECUTEDEFAULT == 0);
+
+  // As soon as the handle is registered, the thread pool can queue up a
+  // callback and reenter the reactor on a different thread.
+  // Acquire the critical section before registering the handle.
+  ::EnterCriticalSection(&cs_);
+#if DEBUG
+  // The same handle should not be registered multiple times.
+  std::vector<RegistrationState*>::iterator it = handlers_.begin();
+  for (; it != handlers_.end(); ++it) {
+    ASSERT((*it)->handle != handle, (_T("[already registered %d]"), handle));
+  }
+#endif
+  bool res = !!::RegisterWaitForSingleObject(&state->wait_handle,
+                                             state->handle,
+                                             &Reactor::Callback,
+                                             state.get(),
+                                             INFINITE,
+                                             state->flags);
+  HRESULT hr = res ? S_OK : HRESULTFromLastError();
+  if (SUCCEEDED(hr)) {
+    handlers_.push_back(state.release());
+  }
+  ::LeaveCriticalSection(&cs_);
+
+  return hr;
+}
+
+HRESULT Reactor::RegisterHandle(HANDLE handle) {
+  ::EnterCriticalSection(&cs_);
+  HRESULT hr = DoRegisterHandle(handle);
+  ::LeaveCriticalSection(&cs_);
+  return hr;
+}
+
+HRESULT Reactor::DoRegisterHandle(HANDLE handle) {
+  ASSERT1(handle);
+  std::vector<RegistrationState*>::iterator it = handlers_.begin();
+  for (; it != handlers_.end(); ++it) {
+    if ((*it)->handle == handle) {
+      break;
+    }
+  }
+  if (it == handlers_.end()) {
+    // The handle is not registered with the reactor anymore. Registering the
+    // the handle again is not possible.
+    return E_FAIL;
+  }
+
+  // Unregister and register the handle again. Unregistering is an non blocking
+  // call.
+  RegistrationState* state = *it;
+  bool res = !!::UnregisterWaitEx(state->wait_handle, NULL);
+  if (!res && ::GetLastError() != ERROR_IO_PENDING) {
+    return HRESULTFromLastError();
+  }
+  if (!::RegisterWaitForSingleObject(&state->wait_handle,
+                                     state->handle,
+                                     &Reactor::Callback,
+                                     state,
+                                     INFINITE,
+                                     state->flags)) {
+    return HRESULTFromLastError();
+  }
+  return S_OK;
+}
+
+HRESULT Reactor::UnregisterHandle(HANDLE handle) {
+  ASSERT1(handle);
+  if (!handle) {
+    return E_INVALIDARG;
+  }
+
+  // Attempts to take the ownership of the registration state for the handle.
+  // If taking the ownership does not succeed, it means the handle has already
+  // been unregistered.
+  scoped_ptr<RegistrationState> state(ReleaseHandlerState(handle));
+  if (!state.get()) {
+    return E_UNEXPECTED;
+  }
+
+  // Unregisters the wait handle from the thread pool. The call blocks waiting
+  // for any pending callbacks to finish. No lock is being held while waiting
+  // here. If there is no callback pending, the call will succeed right away.
+  // Otherwise, if a callback has already started, the call waits for the
+  // callback to complete.
+  bool res = !!::UnregisterWaitEx(state->wait_handle, INVALID_HANDLE_VALUE);
+
+  // Clear the registration state, as a defensive programming measure and
+  // for debugging purposes.
+  state->reactor          = NULL;
+  state->handle           = NULL;
+  state->wait_handle      = NULL;
+  state->event_handler    = NULL;
+
+  return res ? S_OK : HRESULTFromLastError();
+}
+
+void Reactor::DoCallback(RegistrationState* state) {
+  ASSERT1(state);
+  ASSERT1(state->event_handler);
+  ASSERT1(state->handle);
+  state->event_handler->HandleEvent(state->handle);
+}
+
+// Looks up the registration state for a handle and releases the ownership
+// of it to the caller. As the clean up of the state can happen from multiple
+// places, the transfer of ownership ensures the clean up happens once and
+// only once.
+Reactor::RegistrationState* Reactor::ReleaseHandlerState(HANDLE handle) {
+  RegistrationState* registration_state = NULL;
+  ::EnterCriticalSection(&cs_);
+  std::vector<RegistrationState*>::iterator it = handlers_.begin();
+  for (; it != handlers_.end(); ++it) {
+    if ((*it)->handle == handle) {
+      registration_state = *it;
+      handlers_.erase(it);
+      break;
+    }
+  }
+  ::LeaveCriticalSection(&cs_);
+  return registration_state;
+}
+
+}  // namespace omaha
+
diff --git a/common/reactor.h b/base/reactor.h
similarity index 100%
rename from common/reactor.h
rename to base/reactor.h
diff --git a/base/reactor_unittest.cc b/base/reactor_unittest.cc
new file mode 100644
index 0000000..99463b6
--- /dev/null
+++ b/base/reactor_unittest.cc
@@ -0,0 +1,136 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include <stdlib.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/event_handler.h"
+#include "omaha/base/reactor.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+// TODO(omaha): rename EventHandler to EventHandlerInterface.
+
+// Creates and registers two waitable timers with the reactor. They go off
+// randomly until the reactor stops handling events.
+class ReactorTest
+    : public testing::Test,
+      public EventHandler {
+ protected:
+  ReactorTest() : cnt_(0) {}
+
+  virtual void SetUp() {
+    // Timer handles are with auto reset for simplicity.
+    reset(timer1_, ::CreateWaitableTimer(NULL, false, NULL));
+    reset(timer2_, ::CreateWaitableTimer(NULL, false, NULL));
+
+    reset(event_done_, ::CreateEvent(NULL, true, false, NULL));
+
+    ASSERT_TRUE(timer1_);
+    ASSERT_TRUE(timer2_);
+    ASSERT_TRUE(event_done_);
+
+    // We only need the thread handle to queue an empty APC to it.
+    reset(main_thread_, ::OpenThread(THREAD_ALL_ACCESS,
+                                     false,
+                                     ::GetCurrentThreadId()));
+    ASSERT_TRUE(main_thread_);
+  }
+
+  virtual void TearDown() {
+  }
+
+  // EventHandler.
+  virtual void HandleEvent(HANDLE h);
+
+  // Empty APC to stop the reactor.
+  static void _stdcall Stop(ULONG_PTR) {}
+
+  // Returns an integer value in the [0, 10) range.
+  static int GetSmallInt() {
+    unsigned int val = 0;
+    rand_s(&val);
+    return val % 10;
+  }
+
+  Reactor reactor_;
+
+  scoped_timer timer1_;
+  scoped_timer timer2_;
+  scoped_event event_done_;
+
+  scoped_handle main_thread_;
+  LONG cnt_;
+  static const LONG ReactorTest::kMaxCount = 10;
+};
+
+const LONG ReactorTest::kMaxCount;
+
+void ReactorTest::HandleEvent(HANDLE h) {
+  EXPECT_TRUE(h);
+  if (h == get(event_done_)) {
+    ASSERT_TRUE(::QueueUserAPC(&ReactorTest::Stop,
+                               get(main_thread_),
+                               0));
+  } else if (h == get(timer1_) || h == get(timer2_)) {
+    // Check the handles auto reset correctly.
+    EXPECT_EQ(::WaitForSingleObject(h, 0), WAIT_TIMEOUT);
+    if (::InterlockedIncrement(&cnt_) > kMaxCount) {
+      ASSERT_TRUE(::SetEvent(get(event_done_)));
+    } else {
+      ASSERT_HRESULT_SUCCEEDED(reactor_.RegisterHandle(h));
+
+      unsigned int val = 0;
+      ASSERT_EQ(rand_s(&val), 0);
+      val %= 10;
+
+      // Set the timer to fire; negative values indicate relative time.
+      LARGE_INTEGER due_time_100ns = {0};
+      due_time_100ns.QuadPart = -(static_cast<int>(val) * 10 * 1000);
+      ASSERT_TRUE(::SetWaitableTimer(h, &due_time_100ns, 0, NULL, NULL, false));
+    }
+  }
+}
+
+// Registers the handles, primes the timers, and handles events.
+TEST_F(ReactorTest, HandleEvents) {
+  ASSERT_HRESULT_SUCCEEDED(reactor_.RegisterHandle(get(event_done_), this, 0));
+  ASSERT_HRESULT_SUCCEEDED(reactor_.RegisterHandle(get(timer1_), this, 0));
+  ASSERT_HRESULT_SUCCEEDED(reactor_.RegisterHandle(get(timer2_), this, 0));
+
+  LARGE_INTEGER due_time_100ns = {0};
+  ASSERT_TRUE(SetWaitableTimer(get(timer1_),
+                               &due_time_100ns,
+                               0,
+                               NULL,
+                               NULL,
+                               false));
+  ASSERT_TRUE(SetWaitableTimer(get(timer2_),
+                               &due_time_100ns,
+                               0,
+                               NULL,
+                               NULL,
+                               false));
+
+  ASSERT_HRESULT_SUCCEEDED(reactor_.HandleEvents());
+
+  ASSERT_HRESULT_SUCCEEDED(reactor_.UnregisterHandle(get(timer2_)));
+  ASSERT_HRESULT_SUCCEEDED(reactor_.UnregisterHandle(get(timer1_)));
+  ASSERT_HRESULT_SUCCEEDED(reactor_.UnregisterHandle(get(event_done_)));
+}
+
+}  // namespace omaha
diff --git a/base/reg_key.cc b/base/reg_key.cc
new file mode 100644
index 0000000..67abedf
--- /dev/null
+++ b/base/reg_key.cc
@@ -0,0 +1,1277 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Registry configuration wrapers class implementation
+
+#include <raserror.h>
+#include "omaha/base/reg_key.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/static_assert.h"
+#include "omaha/base/string.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/system.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+HRESULT RegKey::Close() {
+  HRESULT hr = S_OK;
+  if (h_key_ != NULL) {
+    LONG res = RegCloseKey(h_key_);
+    hr = HRESULT_FROM_WIN32(res);
+    h_key_ = NULL;
+  }
+  return hr;
+}
+
+HRESULT RegKey::Create(HKEY hKeyParent,
+                       const TCHAR * key_name,
+                       TCHAR * lpszClass,
+                       DWORD options,
+                       REGSAM sam_desired,
+                       LPSECURITY_ATTRIBUTES lpSecAttr,
+                       LPDWORD lpdwDisposition) {
+  // lpszClass may be NULL
+  ASSERT1(key_name);
+  ASSERT1(hKeyParent != NULL);
+  DWORD dw;
+  HKEY hKey = NULL;
+  LONG res = ::RegCreateKeyEx(hKeyParent,
+                              key_name,
+                              0,
+                              lpszClass,
+                              options,
+                              sam_desired,
+                              lpSecAttr,
+                              &hKey,
+                              &dw);
+  HRESULT hr = HRESULT_FROM_WIN32(res);
+
+  if (lpdwDisposition != NULL)
+    *lpdwDisposition = dw;
+  // we have to close the currently opened key
+  // before replacing it with the new one
+  if (hr == S_OK) {
+    hr = Close();
+    ASSERT1(hr == S_OK);
+    h_key_ = hKey;
+  }
+  return hr;
+}
+
+HRESULT RegKey::Create(const TCHAR * full_key_name,
+                       TCHAR * lpszClass, DWORD options,
+                       REGSAM sam_desired,
+                       LPSECURITY_ATTRIBUTES lpSecAttr,
+                       LPDWORD lpdwDisposition) {
+  // lpszClass may be NULL
+  ASSERT1(full_key_name);
+  CString key_name(full_key_name);
+
+  HKEY parent_key = RegKey::GetRootKeyInfo(&key_name);
+  if (!parent_key) {
+    ASSERT(false, (_T("unable to get root key location %s"), full_key_name));
+    return HRESULT_FROM_WIN32(ERROR_KEY_NOT_FOUND);
+  }
+
+  return Create(parent_key, key_name, lpszClass,
+    options, sam_desired, lpSecAttr, lpdwDisposition);
+}
+
+HRESULT RegKey::CreateKeys(const TCHAR* keys_to_create[],
+                           DWORD number_of_keys,
+                           TCHAR* lpszClass,
+                           DWORD options,
+                           LPSECURITY_ATTRIBUTES lpSecAttr) {
+  ASSERT1(keys_to_create);
+  ASSERT1(number_of_keys);
+
+  for (DWORD i = 0; i < number_of_keys; i++) {
+    HRESULT hr = CreateKey(keys_to_create[i], lpszClass, options, lpSecAttr);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  return S_OK;
+}
+
+HRESULT RegKey::CreateKey(const TCHAR* full_key_name,
+                          TCHAR* lpszClass,
+                          DWORD options,
+                          LPSECURITY_ATTRIBUTES lpSecAttr) {
+  ASSERT1(full_key_name);
+
+  RegKey key;
+  HRESULT hr = key.Create(full_key_name,
+                          lpszClass,
+                          options,
+                          KEY_ALL_ACCESS,
+                          lpSecAttr,
+                          NULL);
+  if (FAILED(hr)) {
+    UTIL_LOG(L3, (_T("[couldn't create %s reg key]"), full_key_name));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT RegKey::Open(HKEY hKeyParent,
+                     const TCHAR * key_name,
+                     REGSAM sam_desired) {
+  ASSERT1(key_name);
+  ASSERT1(hKeyParent != NULL);
+  HKEY hKey = NULL;
+  LONG res = ::RegOpenKeyEx(hKeyParent, key_name, 0, sam_desired, &hKey);
+  HRESULT hr = HRESULT_FROM_WIN32(res);
+
+  // we have to close the currently opened key
+  // before replacing it with the new one
+  if (hr == S_OK) {
+    // close the currently opened key if any
+    hr = Close();
+    ASSERT1(hr == S_OK);
+    h_key_ = hKey;
+  }
+  return hr;
+}
+
+HRESULT RegKey::Open(const TCHAR * full_key_name, REGSAM sam_desired) {
+  ASSERT1(full_key_name);
+  CString key_name(full_key_name);
+
+  HKEY parent_key = RegKey::GetRootKeyInfo(&key_name);
+  if (!parent_key) {
+    ASSERT(false, (_T("unable to get root key for %s"), full_key_name));
+    return HRESULT_FROM_WIN32(ERROR_KEY_NOT_FOUND);
+  }
+
+  return Open(parent_key, key_name, sam_desired);
+}
+
+// save the key and all of its subkeys and values to a file
+HRESULT RegKey::Save(const TCHAR* full_key_name, const TCHAR* file_name) {
+  ASSERT1(full_key_name);
+  ASSERT1(file_name);
+
+  CString key_name(full_key_name);
+  HKEY h_key = GetRootKeyInfo(&key_name);
+  if (!h_key) {
+    return E_FAIL;
+  }
+
+  RegKey key;
+  HRESULT hr = key.Open(h_key, key_name, KEY_READ);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  System::AdjustPrivilege(SE_BACKUP_NAME, true);
+  LONG res = ::RegSaveKey(key.h_key_, file_name, NULL);
+  System::AdjustPrivilege(SE_BACKUP_NAME, false);
+
+  return HRESULT_FROM_WIN32(res);
+}
+
+// restore the key and all of its subkeys and values which are saved into a file
+HRESULT RegKey::Restore(const TCHAR* full_key_name, const TCHAR* file_name) {
+  ASSERT1(full_key_name);
+  ASSERT1(file_name);
+
+  CString key_name(full_key_name);
+  HKEY h_key = GetRootKeyInfo(&key_name);
+  if (!h_key) {
+    return E_FAIL;
+  }
+
+  RegKey key;
+  HRESULT hr = key.Open(h_key, key_name, KEY_WRITE);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  System::AdjustPrivilege(SE_RESTORE_NAME, true);
+  LONG res = ::RegRestoreKey(key.h_key_, file_name, REG_FORCE_RESTORE);
+  System::AdjustPrivilege(SE_RESTORE_NAME, false);
+
+  return HRESULT_FROM_WIN32(res);
+}
+
+// check if the current key has the specified subkey
+bool RegKey::HasSubkey(const TCHAR * key_name) const {
+  ASSERT1(key_name);
+  ASSERT1(h_key_);
+
+  RegKey key;
+  HRESULT hr = key.Open(h_key_, key_name, KEY_READ);
+  key.Close();
+  return S_OK == hr;
+}
+
+// static flush key
+HRESULT RegKey::FlushKey(const TCHAR * full_key_name) {
+  ASSERT1(full_key_name);
+
+  HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
+  // get the root HKEY
+  CString key_name(full_key_name);
+  HKEY h_key = GetRootKeyInfo(&key_name);
+
+  if (h_key != NULL) {
+    LONG res = RegFlushKey(h_key);
+    hr = HRESULT_FROM_WIN32(res);
+  }
+  return hr;
+}
+
+// static SET helper
+HRESULT RegKey::SetValueStaticHelper(const TCHAR * full_key_name,
+                                     const TCHAR * value_name,
+                                     DWORD type,
+                                     LPVOID value,
+                                     DWORD byte_count) {
+  // value_name may be NULL
+  ASSERT1(full_key_name);
+
+  HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
+  // get the root HKEY
+  CString key_name(full_key_name);
+  HKEY h_key = GetRootKeyInfo(&key_name);
+
+  if (h_key != NULL) {
+    RegKey key;
+    hr = key.Create(h_key, key_name.GetString());
+    if (hr == S_OK) {
+      switch (type) {
+        case REG_DWORD:
+          hr = key.SetValue(value_name, *reinterpret_cast<DWORD *>(value));
+          if (SUCCEEDED(hr)) {
+            UTIL_LOG(L6, (_T("[Wrote int32 value: %s:%s = %d]"),
+                          full_key_name,
+                          value_name,
+                          *reinterpret_cast<DWORD*>(value)));
+          }
+          break;
+        case REG_QWORD:
+          hr = key.SetValue(value_name, *reinterpret_cast<DWORD64 *>(value));
+          if (SUCCEEDED(hr)) {
+            UTIL_LOG(L6, (_T("[Wrote int64 value: %s:%s = %s]"),
+                          full_key_name,
+                          value_name,
+                          String_Int64ToString(
+                              *reinterpret_cast<DWORD64*>(value), 10)));
+          }
+          break;
+        case REG_SZ:
+          hr = key.SetValue(value_name, reinterpret_cast<const TCHAR *>(value));
+          if (SUCCEEDED(hr)) {
+            UTIL_LOG(L6, (_T("[Wrote string value: %s:%s = %s]"),
+                          full_key_name,
+                          value_name,
+                          reinterpret_cast<const TCHAR *>(value)));
+          }
+          break;
+        case REG_BINARY:
+          hr = key.SetValue(value_name,
+                            reinterpret_cast<const byte *>(value),
+                            byte_count);
+          if (SUCCEEDED(hr)) {
+            UTIL_LOG(L6, (_T("[Wrote binary value: %s:%s, len = %d]"),
+                          full_key_name, value_name, byte_count));
+          }
+          break;
+        case REG_MULTI_SZ:
+          hr = key.SetValue(value_name,
+                            reinterpret_cast<const byte *>(value),
+                            byte_count,
+                            type);
+          if (SUCCEEDED(hr)) {
+            UTIL_LOG(L6, (_T("[Wrote multi-sz value: %s:%s, len = %d]"),
+                          full_key_name, value_name, byte_count));
+          }
+          break;
+        case REG_EXPAND_SZ:
+          hr = key.SetStringValue(value_name,
+                                  reinterpret_cast<const TCHAR *>(value),
+                                  type);
+          if (SUCCEEDED(hr)) {
+            UTIL_LOG(L6, (_T("[Wrote expandable string value: %s:%s = %s]"),
+                          full_key_name, value_name, (const TCHAR *)value));
+          }
+          break;
+        default:
+          ASSERT(false, (_T("Unsupported Registry Type")));
+          hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
+          break;
+      }
+      // close the key after writing
+      HRESULT temp_res = key.Close();
+      if (hr == S_OK) {
+        hr = temp_res;
+      } else {
+        ASSERT(false, (_T("Failed to write reg value: %s:%s (hr=0x%x)"),
+                       full_key_name, value_name, hr));
+      }
+    } else {
+      UTIL_LOG(L3, (_T("[Failed to create reg key: %s]"), full_key_name));
+    }
+  }
+  return hr;
+}
+
+// static GET helper
+// byte_count may be NULL.
+// value_name may be NULL.
+HRESULT RegKey::GetValueStaticHelper(const TCHAR * full_key_name,
+                                     const TCHAR * value_name,
+                                     DWORD type,
+                                     LPVOID value,
+                                     DWORD * byte_count) {
+  ASSERT1(full_key_name);
+
+  HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
+  // get the root HKEY
+  CString key_name(full_key_name);
+  HKEY h_key = GetRootKeyInfo(&key_name);
+
+  if (h_key != NULL) {
+    RegKey key;
+    hr = key.Open(h_key, key_name.GetString(), KEY_READ);
+    if (hr == S_OK) {
+      switch (type) {
+        case REG_DWORD:
+          hr = key.GetValue(value_name, reinterpret_cast<DWORD *>(value));
+          if (SUCCEEDED(hr)) {
+            UTIL_LOG(L6, (_T("[Read int32 value: %s:%s = %d]"),
+                          full_key_name,
+                          value_name,
+                          *reinterpret_cast<DWORD*>(value)));
+          }
+          break;
+        case REG_QWORD:
+          hr = key.GetValue(value_name, reinterpret_cast<DWORD64 *>(value));
+          if (SUCCEEDED(hr)) {
+            UTIL_LOG(L6, (_T("[Read int64 value: %s:%s = %s]"),
+                          full_key_name,
+                          value_name,
+                          String_Int64ToString(
+                              *(reinterpret_cast<DWORD64*>(value)), 10)));
+          }
+          break;
+        case REG_SZ:
+          hr = key.GetValue(value_name, reinterpret_cast<TCHAR * *>(value));
+          if (SUCCEEDED(hr)) {
+            UTIL_LOG(L6, (_T("[Read string value: %s:%s = %s]"),
+                          full_key_name,
+                          value_name,
+                          *reinterpret_cast<TCHAR * *>(value)));
+          }
+          break;
+        case REG_MULTI_SZ:
+          hr = key.GetValue(value_name,
+                            reinterpret_cast<std::vector<CString> *>(value));
+          if (SUCCEEDED(hr)) {
+            UTIL_LOG(L6, (_T("[Read multi string value: %s:%s = %d]"),
+                full_key_name,
+                value_name,
+                reinterpret_cast<std::vector<CString>*>(value)->size()));
+          }
+          break;
+        case REG_BINARY:
+          hr = key.GetValue(value_name,
+                            reinterpret_cast<byte * *>(value),
+                            byte_count);
+          if (SUCCEEDED(hr)) {
+            UTIL_LOG(L6, (_T("[Read binary value: %s:%s, len = %d]"),
+                          full_key_name, value_name, byte_count));
+          }
+          break;
+        default:
+          ASSERT(false, (_T("Unsupported Registry Type")));
+          hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
+          break;
+      }
+      // close the key after writing
+      HRESULT temp_res = key.Close();
+      if (hr == S_OK) {
+        hr = temp_res;
+      } else {
+        UTIL_LOG(L5, (_T("[Failed to read reg value: %s:%s]"),
+                      full_key_name, value_name));
+      }
+    } else {
+      UTIL_LOG(L5, (_T("[reg value does not exist: %s]"), key_name));
+    }
+  }
+  return hr;
+}
+
+// GET helper
+// value_name may be NULL.
+HRESULT RegKey::GetValueHelper(const TCHAR * value_name,
+                               DWORD * type,
+                               byte * * value,
+                               DWORD * byte_count) const {
+  ASSERT1(byte_count);
+  ASSERT1(value);
+  ASSERT1(type);
+  ASSERT1(h_key_);
+
+  // init return buffer
+  *value = NULL;
+
+  // get the size of the return data buffer
+  LONG res = ::SHQueryValueEx(h_key_, value_name, NULL, type, NULL, byte_count);
+  HRESULT hr = HRESULT_FROM_WIN32(res);
+
+  if (hr == S_OK) {
+    // if the value length is 0, nothing to do
+    if (*byte_count != 0) {
+      // allocate the buffer
+      *value = new byte[*byte_count];
+      ASSERT1(*value);
+
+      // make the call again to get the data
+      res = ::SHQueryValueEx(h_key_,
+                             value_name,
+                             NULL,
+                             type,
+                             *value,
+                             byte_count);
+      hr = HRESULT_FROM_WIN32(res);
+      ASSERT1(S_OK == hr);
+    }
+  }
+  return hr;
+}
+
+// value_name may be NULL
+HRESULT RegKey::GetValueType(const TCHAR* value_name,
+                             DWORD* value_type) const {
+  ASSERT1(value_type);
+
+  *value_type = REG_NONE;
+
+  LONG res = ::SHQueryValueEx(h_key_, value_name, NULL, value_type, NULL, NULL);
+  if (res != ERROR_SUCCESS) {
+    return HRESULT_FROM_WIN32(res);
+  }
+
+  return S_OK;
+}
+
+// Int32 Get
+// value_name may be NULL.
+HRESULT RegKey::GetValue(const TCHAR * value_name, DWORD * value) const {
+  ASSERT1(value);
+  ASSERT1(h_key_);
+
+  DWORD type = 0;
+  DWORD byte_count = sizeof(DWORD);
+  LONG res = ::SHQueryValueEx(h_key_,
+                              value_name,
+                              NULL,
+                              &type,
+                              reinterpret_cast<byte*>(value),
+                              &byte_count);
+  HRESULT hr = HRESULT_FROM_WIN32(res);
+  ASSERT1((hr != S_OK) || (type == REG_DWORD));
+  ASSERT1((hr != S_OK) || (byte_count == sizeof(DWORD)));
+  return hr;
+}
+
+// Int64 Get
+// value_name may be NULL.
+HRESULT RegKey::GetValue(const TCHAR * value_name, DWORD64 * value) const {
+  ASSERT1(value);
+  ASSERT1(h_key_);
+
+  DWORD type = 0;
+  DWORD byte_count = sizeof(DWORD64);
+  LONG res = ::SHQueryValueEx(h_key_,
+                              value_name,
+                              NULL,
+                              &type,
+                              reinterpret_cast<byte *>(value),
+                              &byte_count);
+  HRESULT hr = HRESULT_FROM_WIN32(res);
+  ASSERT1((hr != S_OK) || (type == REG_QWORD));
+  ASSERT1((hr != S_OK) || (byte_count == sizeof(DWORD64)));
+  return hr;
+}
+
+// String Get
+// value_name may be NULL.
+HRESULT RegKey::GetValue(const TCHAR * value_name, TCHAR * * value) const {
+  ASSERT1(value);
+  ASSERT1(h_key_);
+
+  DWORD byte_count = 0;
+  DWORD type = 0;
+
+  // first get the size of the string buffer
+  LONG res = ::SHQueryValueEx(h_key_,
+                              value_name,
+                              NULL,
+                              &type,
+                              NULL,
+                              &byte_count);
+  HRESULT hr = HRESULT_FROM_WIN32(res);
+
+  if (hr == S_OK) {
+    // allocate room for the string and a terminating \0
+    *value = new TCHAR[(byte_count / sizeof(TCHAR)) + 1];
+
+    if ((*value) != NULL) {
+      if (byte_count != 0) {
+        // make the call again
+        res = ::SHQueryValueEx(h_key_, value_name, NULL, &type,
+                              reinterpret_cast<byte*>(*value), &byte_count);
+        hr = HRESULT_FROM_WIN32(res);
+      } else {
+        (*value)[0] = _T('\0');
+      }
+
+      ASSERT1((hr != S_OK) || (type == REG_SZ) ||
+              (type == REG_MULTI_SZ) || (type == REG_EXPAND_SZ));
+    } else {
+      hr = E_OUTOFMEMORY;
+    }
+  }
+
+  return hr;
+}
+
+// CString Get
+// value_name may be NULL.
+HRESULT RegKey::GetValue(const TCHAR* value_name, OUT CString* value) const {
+  ASSERT1(value);
+  ASSERT1(h_key_);
+
+  DWORD byte_count = 0;
+  DWORD type = 0;
+
+  // first get the size of the string buffer
+  LONG res = ::SHQueryValueEx(h_key_,
+                              value_name,
+                              NULL,
+                              &type,
+                              NULL,
+                              &byte_count);
+  HRESULT hr = HRESULT_FROM_WIN32(res);
+
+  if (hr == S_OK) {
+    if (byte_count != 0) {
+      // Allocate some memory and make the call again
+      TCHAR* buffer = value->GetBuffer(byte_count / sizeof(TCHAR) + 1);
+      if (buffer == NULL) {
+        hr = E_OUTOFMEMORY;
+      } else {
+        res = ::SHQueryValueEx(h_key_, value_name, NULL, &type,
+                              reinterpret_cast<byte*>(buffer), &byte_count);
+        hr = HRESULT_FROM_WIN32(res);
+      }
+      value->ReleaseBuffer();
+    } else {
+      value->Empty();
+    }
+
+    ASSERT1((hr != S_OK) || (type == REG_SZ) ||
+            (type == REG_MULTI_SZ) || (type == REG_EXPAND_SZ));
+  }
+
+  return hr;
+}
+
+// convert REG_MULTI_SZ bytes to string array
+HRESULT RegKey::MultiSZBytesToStringArray(const byte * buffer,
+                                          DWORD byte_count,
+                                          std::vector<CString> * value) {
+  ASSERT1(buffer);
+  ASSERT1(value);
+
+  const TCHAR* data = reinterpret_cast<const TCHAR*>(buffer);
+  DWORD data_len = byte_count / sizeof(TCHAR);
+  value->clear();
+  if (data_len > 1) {
+    // must be terminated by two null characters
+    if (data[data_len - 1] != 0 || data[data_len - 2] != 0) {
+      return E_INVALIDARG;
+    }
+
+    // put null-terminated strings into arrays
+    while (*data) {
+      CString str(data);
+      value->push_back(str);
+      data += str.GetLength() + 1;
+    }
+  }
+  return S_OK;
+}
+
+// get a vector<CString> value from REG_MULTI_SZ type
+HRESULT RegKey::GetValue(const TCHAR * value_name,
+                         std::vector<CString> * value) const {
+  ASSERT1(value);
+  // value_name may be NULL
+
+  DWORD byte_count = 0;
+  DWORD type = 0;
+  byte* buffer = 0;
+
+  // first get the size of the buffer
+  HRESULT hr = GetValueHelper(value_name, &type, &buffer, &byte_count);
+  ASSERT1((hr != S_OK) || (type == REG_MULTI_SZ));
+
+  if (SUCCEEDED(hr)) {
+    hr = MultiSZBytesToStringArray(buffer, byte_count, value);
+  }
+
+  return hr;
+}
+
+// Binary data Get
+HRESULT RegKey::GetValue(const TCHAR * value_name,
+                         byte * * value,
+                         DWORD * byte_count) const {
+  ASSERT1(byte_count);
+  ASSERT1(value);
+  // value_name may be NULL
+
+  DWORD type = 0;
+  HRESULT hr = GetValueHelper(value_name, &type, value, byte_count);
+  ASSERT1((hr != S_OK) || (type == REG_MULTI_SZ) || (type == REG_BINARY));
+  return hr;
+}
+
+// Raw data get
+HRESULT RegKey::GetValue(const TCHAR * value_name,
+                         byte * * value,
+                         DWORD * byte_count,
+                         DWORD *type) const {
+  ASSERT1(type);
+  ASSERT1(byte_count);
+  ASSERT1(value);
+
+  return GetValueHelper(value_name, type, value, byte_count);
+}
+
+// Int32 set
+// value_name may be NULL
+HRESULT RegKey::SetValue(const TCHAR * value_name, DWORD value) const {
+  ASSERT1(h_key_);
+  LONG res = RegSetValueEx(h_key_,
+                           value_name,
+                           NULL,
+                           REG_DWORD,
+                           reinterpret_cast<byte *>(&value),
+                           sizeof(DWORD));
+  return HRESULT_FROM_WIN32(res);
+}
+
+// Int64 set
+// value_name may be NULL
+HRESULT RegKey::SetValue(const TCHAR * value_name, DWORD64 value) const {
+  ASSERT1(h_key_);
+  LONG res = RegSetValueEx(h_key_,
+                           value_name,
+                           NULL,
+                           REG_QWORD,
+                           reinterpret_cast<byte *>(&value),
+                           sizeof(DWORD64));
+  return HRESULT_FROM_WIN32(res);
+}
+
+// String set
+HRESULT RegKey::SetValue(const TCHAR * value_name, const TCHAR * value) const {
+  return SetStringValue(value_name, value, REG_SZ);
+}
+
+// String set helper
+// value_name may be NULL.
+HRESULT RegKey::SetStringValue(const TCHAR * value_name,
+                               const TCHAR * value,
+                               DWORD type) const {
+  ASSERT1(value);
+  ASSERT1(h_key_);
+  ASSERT1(type == REG_SZ || type == REG_EXPAND_SZ);
+  LONG res = RegSetValueEx(h_key_,
+                           value_name,
+                           NULL,
+                           type,
+                           reinterpret_cast<const byte *>(value),
+                           (lstrlen(value) + 1) * sizeof(TCHAR));
+  return HRESULT_FROM_WIN32(res);
+}
+
+// Binary data set
+// value may be NULL.
+// value_name may be NULL.
+HRESULT RegKey::SetValue(const TCHAR * value_name,
+                         const byte * value,
+                         DWORD byte_count) const {
+  ASSERT1(h_key_);
+
+  // special case - if 'value' is NULL make sure byte_count is zero
+  if (value == NULL) {
+    byte_count = 0;
+  }
+
+  LONG res = RegSetValueEx(h_key_,
+                           value_name,
+                           NULL,
+                           REG_BINARY,
+                           value,
+                           byte_count);
+  return HRESULT_FROM_WIN32(res);
+}
+
+// Raw data set
+// value_name may be NULL.
+HRESULT RegKey::SetValue(const TCHAR * value_name,
+                         const byte * value,
+                         DWORD byte_count,
+                         DWORD type) const {
+  ASSERT1(value);
+  ASSERT1(h_key_);
+  LONG res = RegSetValueEx(h_key_, value_name, NULL, type, value, byte_count);
+  return HRESULT_FROM_WIN32(res);
+}
+
+HRESULT RegKey::RenameValue(const TCHAR* old_value_name,
+                            const TCHAR* new_value_name) const {
+  ASSERT1(h_key_);
+  ASSERT1(new_value_name);
+  ASSERT1(old_value_name);
+
+  scoped_ptr<byte> value;
+  DWORD byte_count = 0;
+  DWORD type = 0;
+
+  HRESULT hr = GetValue(old_value_name, address(value), &byte_count, &type);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = SetValue(new_value_name, value.get(), byte_count, type);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  VERIFY1(SUCCEEDED(DeleteValue(old_value_name)));
+  return S_OK;
+}
+
+bool RegKey::HasKey(const TCHAR * full_key_name) {
+  return HasKeyHelper(full_key_name, KEY_READ);
+}
+
+bool RegKey::HasNativeKey(const TCHAR * full_key_name) {
+  return HasKeyHelper(full_key_name, KEY_READ | KEY_WOW64_64KEY);
+}
+
+bool RegKey::HasKeyHelper(const TCHAR * full_key_name, DWORD sam_flags) {
+  ASSERT1(full_key_name);
+  ASSERT1(sam_flags & KEY_READ);
+
+  // get the root HKEY
+  CString key_name(full_key_name);
+  HKEY h_key = GetRootKeyInfo(&key_name);
+
+  if (h_key != NULL) {
+    RegKey key;
+    HRESULT hr = key.Open(h_key, key_name.GetString(), sam_flags);
+    key.Close();
+    return S_OK == hr;
+  }
+  return false;
+}
+
+HRESULT RegKey::CopyValue(const TCHAR * full_from_key_name,
+                          const TCHAR * from_value_name,
+                          const TCHAR * full_to_key_name,
+                          const TCHAR * to_value_name) {
+  ASSERT1(full_from_key_name);
+  ASSERT1(full_to_key_name);
+
+  RegKey from_reg_key;
+  HRESULT hr = from_reg_key.Open(full_from_key_name, KEY_READ);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  scoped_ptr<byte> val;
+  DWORD byte_count = 0;
+  DWORD type = 0;
+  hr = from_reg_key.GetValue(from_value_name, address(val), &byte_count, &type);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  RegKey to_reg_key;
+  hr = to_reg_key.Open(full_to_key_name, KEY_WRITE);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return to_reg_key.SetValue(to_value_name, val.get(), byte_count, type);
+}
+
+// static version of HasValue
+bool RegKey::HasValue(const TCHAR * full_key_name, const TCHAR * value_name) {
+  ASSERT1(full_key_name);
+
+  bool has_value = false;
+  // get the root HKEY
+  CString key_name(full_key_name);
+  HKEY h_key = GetRootKeyInfo(&key_name);
+
+  if (h_key != NULL) {
+    RegKey key;
+    if (key.Open(h_key, key_name.GetString(), KEY_READ) == S_OK) {
+      has_value = key.HasValue(value_name);
+      key.Close();
+    }
+  }
+  return has_value;
+}
+
+HRESULT RegKey::GetValueType(const TCHAR* full_key_name,
+                             const TCHAR* value_name,
+                             DWORD* value_type) {
+  ASSERT1(full_key_name);
+  // value_name may be NULL
+  ASSERT1(value_type);
+
+  *value_type = REG_NONE;
+
+  CString key_name(full_key_name);
+  HKEY root_key = GetRootKeyInfo(&key_name);
+
+  RegKey key;
+  HRESULT hr = key.Open(root_key, key_name, KEY_READ);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return key.GetValueType(value_name, value_type);
+}
+
+HRESULT RegKey::DeleteKey(const TCHAR* full_key_name) {
+  ASSERT1(full_key_name);
+
+  return DeleteKey(full_key_name, true);
+}
+
+HRESULT RegKey::DeleteKey(const TCHAR* full_key_name, bool recursively) {
+  ASSERT1(full_key_name);
+
+  // need to open the parent key first
+  // get the root HKEY
+  CString key_name(full_key_name);
+  HKEY h_key = GetRootKeyInfo(&key_name);
+
+  // get the parent key
+  CString parent_key(GetParentKeyInfo(&key_name));
+
+  RegKey key;
+  HRESULT hr = key.Open(h_key, parent_key);
+
+  if (hr == S_OK) {
+    hr = recursively ? key.RecurseDeleteSubKey(key_name) :
+                       key.DeleteSubKey(key_name);
+  } else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
+             hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) {
+    hr = S_FALSE;
+  }
+
+  key.Close();
+  return hr;
+}
+
+HRESULT RegKey::DeleteValue(const TCHAR * full_key_name,
+                            const TCHAR * value_name) {
+  ASSERT1(value_name);
+  ASSERT1(full_key_name);
+
+  HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
+  // get the root HKEY
+  CString key_name(full_key_name);
+  HKEY h_key = GetRootKeyInfo(&key_name);
+
+  if (h_key != NULL) {
+    RegKey key;
+    hr = key.Open(h_key, key_name.GetString());
+    if (hr == S_OK) {
+      hr = key.DeleteValue(value_name);
+      key.Close();
+    }
+  }
+  return hr;
+}
+
+HRESULT RegKey::RecurseDeleteSubKey(const TCHAR * key_name) {
+  ASSERT1(key_name);
+  ASSERT1(h_key_);
+
+  RegKey key;
+  HRESULT hr = key.Open(h_key_, key_name);
+  if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
+      hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) {
+    hr = S_FALSE;
+  }
+  if (hr != S_OK) {
+    return hr;
+  }
+
+  // enumerate all subkeys of this key
+  // and recursivelly delete them
+  FILETIME time;
+  TCHAR key_name_buf[kMaxKeyNameChars];
+  DWORD key_name_buf_size = kMaxKeyNameChars;
+  while (RegEnumKeyEx(key.h_key_,
+                      0,
+                      key_name_buf,
+                      &key_name_buf_size,
+                      NULL,
+                      NULL,
+                      NULL,
+                      &time) == ERROR_SUCCESS) {
+    hr = key.RecurseDeleteSubKey(key_name_buf);
+    // return if error deleting key
+    if (hr != S_OK)
+      return hr;
+    // restore the buffer size
+    key_name_buf_size = kMaxKeyNameChars;
+  }
+  // close the top key
+  key.Close();
+
+  // the key has no more children keys
+  // delete the key and all of its values
+  return DeleteSubKey(key_name);
+}
+
+HKEY RegKey::GetRootKeyInfo(CString * full_key_name) {
+  ASSERT1(full_key_name);
+
+  HKEY h_key = NULL;
+  // get the root HKEY
+  int index = String_FindChar(*(full_key_name), '\\');
+  CString root_key;
+
+  if (index == -1) {
+    root_key = *full_key_name;
+    *full_key_name = _T("");
+  } else {
+    root_key= full_key_name->Left(index);
+    *full_key_name =
+        full_key_name->Right(full_key_name->GetLength() - index - 1);
+  }
+
+  if (!root_key.CompareNoCase(_T("HKLM")) ||
+      !root_key.CompareNoCase(_T("HKEY_LOCAL_MACHINE")))
+    h_key = HKEY_LOCAL_MACHINE;
+  else if (!root_key.CompareNoCase(_T("HKCU")) ||
+           !root_key.CompareNoCase(_T("HKEY_CURRENT_USER")))
+    h_key = HKEY_CURRENT_USER;
+  else if (!root_key.CompareNoCase(_T("HKU")) ||
+           !root_key.CompareNoCase(_T("HKEY_USERS")))
+    h_key = HKEY_USERS;
+  else if (!root_key.CompareNoCase(_T("HKCR")) ||
+           !root_key.CompareNoCase(_T("HKEY_CLASSES_ROOT")))
+    h_key = HKEY_CLASSES_ROOT;
+
+  return h_key;
+}
+
+
+// Returns true if this key name is 'safe' for deletion (doesn't specify a
+// key root)
+bool RegKey::SafeKeyNameForDeletion(const wchar_t *key_name) {
+  ASSERT1(key_name);
+  CString key(key_name);
+
+  HKEY root_key = GetRootKeyInfo(&key);
+
+  if ( !root_key ) {
+    key = key_name;
+  }
+  if ( key.IsEmpty() ) {
+    return false;
+  }
+  bool found_subkey = false, backslash_found = false;
+  for (int i = 0 ; i < key.GetLength() ; ++i) {
+    if ( key[i] == L'\\' ) {
+      backslash_found = true;
+    } else if ( backslash_found ) {
+      found_subkey = true;
+      break;
+    }
+  }
+  return ( root_key == HKEY_USERS ) ? found_subkey : true;
+}
+
+CString RegKey::GetParentKeyInfo(CString * key_name) {
+  ASSERT1(key_name);
+
+  // get the parent key
+  int index = key_name->ReverseFind('\\');
+  CString parent_key;
+  if (index == -1) {
+    parent_key = _T("");
+  } else {
+    parent_key = key_name->Left(index);
+    *key_name = key_name->Right(key_name->GetLength() - index - 1);
+  }
+
+  return parent_key;
+}
+
+// get the number of values for this key
+uint32 RegKey::GetValueCount() {
+  ASSERT1(h_key_);
+  // number of values for key
+  DWORD  num_values = 0;
+
+  LONG res = ::RegQueryInfoKey(h_key_,       // key handle
+                               NULL,         // buffer for class name
+                               NULL,         // size of class string
+                               NULL,         // reserved
+                               NULL,         // number of subkeys
+                               NULL,         // longest subkey size
+                               NULL,         // longest class string
+                               &num_values,  // number of values for this key
+                               NULL,         // longest value name
+                               NULL,         // longest value data
+                               NULL,         // security descriptor
+                               NULL);        // last write time
+
+  ASSERT1(res == ERROR_SUCCESS);
+  return num_values;
+}
+
+// Enumerators for the value_names for this key
+
+// Called to get the value name for the given value name index
+// Use GetValueCount() to get the total value_name count for this key
+// Returns failure if no key at the specified index
+// type may be NULL.
+HRESULT RegKey::GetValueNameAt(int index, CString *value_name, DWORD *type) {
+  ASSERT1(value_name);
+  ASSERT1(h_key_);
+
+  LONG res = ERROR_SUCCESS;
+  TCHAR value_name_buf[kMaxValueNameChars];
+  DWORD value_name_buf_size = kMaxValueNameChars;
+  res = ::RegEnumValue(h_key_,
+                       index,
+                       value_name_buf,
+                       &value_name_buf_size,
+                       NULL,
+                       type,
+                       NULL,
+                       NULL);
+
+  if (res == ERROR_SUCCESS) {
+    value_name->SetString(value_name_buf);
+  }
+
+  return HRESULT_FROM_WIN32(res);
+}
+
+uint32 RegKey::GetSubkeyCount() {
+  ASSERT1(h_key_);
+
+  DWORD num_subkeys = 0;   // number of values for key
+
+  LONG res = ::RegQueryInfoKey(h_key_,        // key handle
+                               NULL,          // buffer for class name
+                               NULL,          // size of class string
+                               NULL,          // reserved
+                               &num_subkeys,  // number of subkeys
+                               NULL,          // longest subkey size
+                               NULL,          // longest class string
+                               NULL,          // number of values for this key
+                               NULL,          // longest value name
+                               NULL,          // longest value data
+                               NULL,          // security descriptor
+                               NULL);         // last write time
+
+  ASSERT1(res == ERROR_SUCCESS);
+  return num_subkeys;
+}
+
+HRESULT RegKey::GetSubkeyNameAt(int index, CString * key_name) {
+  ASSERT1(key_name);
+  ASSERT1(h_key_);
+
+  LONG res = ERROR_SUCCESS;
+  TCHAR key_name_buf[kMaxKeyNameChars];
+  DWORD key_name_buf_size = kMaxKeyNameChars;
+
+  res = ::RegEnumKeyEx(h_key_,
+                       index,
+                       key_name_buf,
+                       &key_name_buf_size,
+                       NULL,
+                       NULL,
+                       NULL,
+                       NULL);
+
+  if (res == ERROR_SUCCESS) {
+    key_name->SetString(key_name_buf);
+  }
+
+  return HRESULT_FROM_WIN32(res);
+}
+
+// Is the key empty: having no sub-keys and values
+bool RegKey::IsKeyEmpty(const TCHAR* full_key_name) {
+  ASSERT1(full_key_name);
+
+  bool is_empty = true;
+
+  // Get the root HKEY
+  CString key_name(full_key_name);
+  HKEY h_key = GetRootKeyInfo(&key_name);
+
+  // Open the key to check
+  if (h_key != NULL) {
+    RegKey key;
+    HRESULT hr = key.Open(h_key, key_name.GetString(), KEY_READ);
+    if (SUCCEEDED(hr)) {
+      is_empty = key.GetSubkeyCount() == 0 && key.GetValueCount() == 0;
+      key.Close();
+    }
+  }
+
+  return is_empty;
+}
+
+// close this reg key and the event
+HRESULT RegKeyWithChangeEvent::Close() {
+  reset(change_event_);
+  return RegKey::Close();
+}
+
+// Called to create/reset the event that gets signaled
+// any time the registry key changes
+// Note:
+//   * reg key should have been opened using KEY_NOTIFY for the sam_desired
+//
+// See the documentation for RegNotifyChangeKeyValue
+// for values for notify_filter.
+HRESULT RegKeyWithChangeEvent::SetupEvent(bool watch_subtree,
+                                          DWORD notify_filter) {
+  // If the event exists, then it should be in the signaled state
+  // indicating a registry change took place.  If not, then
+  // the caller is setting up the event a second time and this
+  // will create a memory leak.
+  ASSERT(!valid(change_event_) || HasChangeOccurred(),
+         (_T("Event is getting set-up for a second ")
+          _T("time without being signaled.")));
+
+  if (!valid(change_event_)) {
+    reset(change_event_, ::CreateEvent(NULL, TRUE, FALSE, NULL));
+    if (!valid(change_event_)) {
+      ASSERT(false, (_T("create event failed")));
+      return HRESULT_FROM_WIN32(::GetLastError());
+    }
+  } else {
+    if (!::ResetEvent(get(change_event_))) {
+      ASSERT(false, (_T("reset event failed")));
+      return HRESULT_FROM_WIN32(::GetLastError());
+    }
+  }
+
+  LONG res = ::RegNotifyChangeKeyValue(Key(), watch_subtree, notify_filter,
+      get(change_event_), TRUE);
+
+  if (res != ERROR_SUCCESS) {
+    // You may get this failure if you didn't pass in KEY_NOTIFY
+    // as part of the sam_desired flags during Open or Create
+    ASSERT(false, (_T("setting up change notification for a reg key failed")));
+
+    // Leave the event around so that it never changes once it has been set-up
+    // but in this case it will not get signaled again.
+  }
+
+  return HRESULT_FROM_WIN32(res);
+}
+
+// Indicates if any changes (that are being monitored have occured)
+bool RegKeyWithChangeEvent::HasChangeOccurred() const {
+  return IsHandleSignaled(get(change_event_));
+}
+
+
+RegKeyWatcher::RegKeyWatcher(const TCHAR* reg_key, bool watch_subtree,
+                             DWORD notify_filter, bool allow_creation)
+    : reg_key_string_(reg_key),
+      watch_subtree_(watch_subtree),
+      notify_filter_(notify_filter),
+      allow_creation_(allow_creation) {
+  UTIL_LOG(L3, (_T("[RegKeyWatcher::RegKeyWatcher][%s]"), reg_key));
+}
+
+HRESULT RegKeyWatcher::EnsureEventSetup() {
+  UTIL_LOG(L3, (_T("[RegKeyWatcher::EnsureEventSetup]")));
+  if (!reg_key_with_change_event_.get()) {
+    scoped_ptr<RegKeyWithChangeEvent> local_reg_key(new RegKeyWithChangeEvent);
+    if (!local_reg_key.get()) {
+      ASSERT(false, (_T("unable to allocate local_reg_key")));
+      return E_FAIL;
+    }
+
+    if (allow_creation_ && !RegKey::HasKey(reg_key_string_)) {
+      RegKey key;
+      VERIFY1(SUCCEEDED(key.Create(reg_key_string_)));
+    }
+
+    HRESULT hr = local_reg_key->Open(reg_key_string_, KEY_NOTIFY);
+    if (FAILED(hr)) {
+      ASSERT(false, (_T("couldn't open %s reg key for notifications. ")
+                     _T("Make sure you have pre-created the key!"),
+                     reg_key_string_));
+      return hr;
+    }
+    reg_key_with_change_event_.reset(local_reg_key.release());
+    reg_key_string_.Empty();
+  }
+
+  // if the event is set-up and no changes have occurred,
+  // then there is no need to re-setup the event.
+  if (reg_key_with_change_event_->change_event() && !HasChangeOccurred()) {
+    return S_OK;
+  }
+
+  return reg_key_with_change_event_->SetupEvent(watch_subtree_,
+                                                notify_filter_);
+}
+
+// Get the event that is signaled on registry changes.
+HANDLE RegKeyWatcher::change_event() const {
+  if (!reg_key_with_change_event_.get()) {
+    ASSERT(false, (_T("call RegKeyWatcher::EnsureEventSetup first")));
+    return NULL;
+  }
+  return reg_key_with_change_event_->change_event();
+}
+
+}  // namespace omaha
+
diff --git a/base/reg_key.h b/base/reg_key.h
new file mode 100644
index 0000000..295df98
--- /dev/null
+++ b/base/reg_key.h
@@ -0,0 +1,787 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// reg_key.h
+//
+// Registry configuration wrappers class
+//
+// Offers static functions for convenient
+// fast access for individual values
+//
+// Also provides a wrapper class for efficient
+// batch operations on values of a given registry key.
+
+#ifndef OMAHA_BASE_REG_KEY_H_
+#define OMAHA_BASE_REG_KEY_H_
+
+#include <windows.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/static_assert.h"
+#include "omaha/base/store_watcher.h"
+
+namespace omaha {
+
+// maximum sizes registry key and value names
+#define kMaxKeyNameChars   (255 + 1)
+#define kMaxValueNameChars (16383 + 1)
+
+class RegKey {
+ public:
+  RegKey();
+  virtual ~RegKey();
+
+  // create a reg key
+  HRESULT Create(HKEY hKeyParent, const TCHAR * key_name,
+        TCHAR * reg_class = REG_NONE, DWORD options = REG_OPTION_NON_VOLATILE,
+        REGSAM sam_desired = KEY_ALL_ACCESS,
+        LPSECURITY_ATTRIBUTES lp_sec_attr = NULL,
+        LPDWORD lp_disposition = NULL);
+
+  // create a reg key, given the full key name, including the HKEY root
+  // (say for example, "HKLM\\Software")
+  HRESULT Create(const TCHAR * full_key_name,
+        TCHAR * reg_class = REG_NONE, DWORD options = REG_OPTION_NON_VOLATILE,
+        REGSAM sam_desired = KEY_ALL_ACCESS,
+        LPSECURITY_ATTRIBUTES lp_sec_attr = NULL,
+        LPDWORD lp_disposition = NULL);
+
+  // static helper function that create a set of reg keys,
+  // given an array of full key names including the HKEY root
+  // (say for example, "HKLM\\Software")
+  static HRESULT CreateKeys(const TCHAR* keys_to_create[],
+                            DWORD number_of_keys,
+                            TCHAR* reg_class = REG_NONE,
+                            DWORD options = REG_OPTION_NON_VOLATILE,
+                            LPSECURITY_ATTRIBUTES lp_sec_attr = NULL);
+
+  // Static method to create a single key.
+  static HRESULT CreateKey(const TCHAR * full_key_name,
+                           TCHAR * reg_class = REG_NONE,
+                           DWORD options = REG_OPTION_NON_VOLATILE,
+                           LPSECURITY_ATTRIBUTES lp_sec_attr = NULL);
+
+  // open an existing reg key
+  HRESULT Open(HKEY hKeyParent,
+               const TCHAR * key_name,
+               REGSAM sam_desired = KEY_ALL_ACCESS);
+
+  // open an existing reg key, given the full key name, including the HKEY root
+  // (say for example, "HKLM\\Software")
+  HRESULT Open(const TCHAR * full_key_name,
+               REGSAM sam_desired = KEY_ALL_ACCESS);
+
+  // close this reg key
+  virtual HRESULT Close();
+
+  // check if the key has a specified value
+  bool HasValue(const TCHAR * value_name);
+
+  // get the number of values for this key
+  uint32 GetValueCount();
+
+  // Called to get the value name for the given value name index
+  // Use GetValueCount() to get the total value_name count for this key
+  // Returns failure if no key at the specified index
+  // If you modify the key while enumerating, the indexes will be out of order.
+  // Since the index order is not guaranteed, you need to reset your counting
+  // loop.
+  // type refers to REG_DWORD, REG_QWORD, etc..
+  // 'type' can be NULL if not interested in the value type
+  HRESULT GetValueNameAt(int index, CString * value_name, DWORD *type);
+
+  // check if the current key has the specified subkey
+  bool HasSubkey(const TCHAR * key_name) const;
+
+  // get the number of subkeys for this key
+  uint32 GetSubkeyCount();
+
+  // Called to get the key name for the given key index
+  // Use GetSubkeyCount() to get the total count for this key
+  // Returns failure if no key at the specified index
+  // If you modify the key while enumerating, the indexes will be out of order.
+  // Since the index order is not guaranteed, you need to reset your counting
+  // loop.
+  HRESULT GetSubkeyNameAt(int index, CString * key_name);
+
+  // SETTERS
+
+  // set an int32 value - use when reading multiple values from a key
+  HRESULT SetValue(const TCHAR * value_name, DWORD value) const;
+
+  // set an int64 value
+  HRESULT SetValue(const TCHAR * value_name, DWORD64 value) const;
+
+  // set a string value
+  HRESULT SetValue(const TCHAR * value_name, const TCHAR * value) const;
+
+  // set binary data
+  HRESULT SetValue(const TCHAR * value_name,
+                   const byte * value,
+                   DWORD byte_count) const;
+
+  // set raw data, including type
+  HRESULT SetValue(const TCHAR * value_name,
+                   const byte * value,
+                   DWORD byte_count,
+                   DWORD type) const;
+
+  // GETTERS
+
+  // get type of a registry value
+  HRESULT GetValueType(const TCHAR* value_name, DWORD* value_type) const;
+
+  // get an int32 value
+  HRESULT GetValue(const TCHAR * value_name, DWORD * value) const;
+
+  // get an int64 value
+  //
+  // Note: if you are using time64 you should
+  // likely use GetLimitedTimeValue (util.h) instead of this method.
+  HRESULT GetValue(const TCHAR * value_name, DWORD64 * value) const;
+
+  // get a string value - the caller must free the return buffer
+  HRESULT GetValue(const TCHAR * value_name, TCHAR * * value) const;
+
+  // get a CString value
+  HRESULT GetValue(const TCHAR* value_name, OUT CString* value) const;
+
+  // get a vector<CString> value from REG_MULTI_SZ type
+  HRESULT GetValue(const TCHAR * value_name,
+                   std::vector<CString> * value) const;
+
+  // get binary data - the caller must free the return buffer
+  HRESULT GetValue(const TCHAR * value_name,
+                   byte * * value,
+                   DWORD * byte_count) const;
+
+  // get raw data, including type - the caller must free the return buffer
+  HRESULT GetValue(const TCHAR * value_name,
+                   byte * * value,
+                   DWORD * byte_count,
+                   DWORD *type) const;
+
+  // RENAMERS
+
+  // Rename a named value.
+  HRESULT RenameValue(const TCHAR * old_value_name,
+                      const TCHAR * new_value_name) const;
+
+  // STATIC VERSIONS
+
+  // flush
+  static HRESULT FlushKey(const TCHAR * full_key_name);
+
+  // Check if a key exists.
+  static bool HasKey(const TCHAR * full_key_name);
+
+  // Check if a key exists in the native (i.e. non-redirected) registry.
+  static bool HasNativeKey(const TCHAR * full_key_name);
+
+  // check if the key has a specified value
+  static bool HasValue(const TCHAR * full_key_name, const TCHAR * value_name);
+
+  // SETTERS
+
+  // STATIC int32 set
+  static HRESULT SetValue(const TCHAR * full_key_name,
+                          const TCHAR * value_name,
+                          DWORD value);
+
+  // STATIC int64 set
+  static HRESULT SetValue(const TCHAR * full_key_name,
+                          const TCHAR * value_name,
+                          DWORD64 value);
+
+  // STATIC float set
+  static HRESULT SetValue(const TCHAR * full_key_name,
+                          const TCHAR * value_name,
+                          float value);
+
+  // STATIC double set
+  static HRESULT SetValue(const TCHAR * full_key_name,
+                          const TCHAR * value_name,
+                          double value);
+
+  // STATIC string set
+  static HRESULT SetValue(const TCHAR * full_key_name,
+                          const TCHAR * value_name,
+                          const TCHAR * value);
+
+  // STATIC binary data set
+  static HRESULT SetValue(const TCHAR * full_key_name,
+                          const TCHAR * value_name,
+                          const byte * value,
+                          DWORD byte_count);
+
+  // STATIC array of strings set
+  static HRESULT SetValueMultiSZ(const TCHAR * full_key_name,
+                                 const TCHAR * value_name,
+                                 const byte * value,
+                                 DWORD byte_count);
+
+  // STATIC expandable string set
+  static HRESULT SetValueExpandSZ(const TCHAR * full_key_name,
+                                  const TCHAR * value_name,
+                                  const TCHAR * value);
+
+  // GETTERS
+
+  // STATIC int32 get
+  static HRESULT GetValue(const TCHAR * full_key_name,
+                          const TCHAR * value_name,
+                          DWORD * value);
+
+  // STATIC int64 get
+  //
+  // Note: if you are using time64 you should
+  // likely use GetLimitedTimeValue (util.h) instead of this method.
+  static HRESULT GetValue(const TCHAR * full_key_name,
+                          const TCHAR * value_name,
+                          DWORD64 * value);
+
+  // STATIC float get
+  static HRESULT GetValue(const TCHAR * full_key_name,
+                          const TCHAR * value_name,
+                          float * value);
+
+  // STATIC double get
+  static HRESULT GetValue(const TCHAR * full_key_name,
+                          const TCHAR * value_name,
+                          double * value);
+
+  // STATIC string get (STR and CString versions) - the caller must free
+  // the return buffer
+  static HRESULT GetValue(const TCHAR * full_key_name,
+                          const TCHAR * value_name,
+                          TCHAR * * value);
+
+  static HRESULT GetValue(const TCHAR * full_key_name,
+                          const TCHAR * value_name,
+                          CString * value);
+
+  // STATIC REG_MULTI_SZ get
+  static HRESULT GetValue(const TCHAR * full_key_name,
+                          const TCHAR * value_name,
+                          std::vector<CString> * value);
+
+  // STATIC get binary data - the caller must free the return buffer
+  static HRESULT GetValue(const TCHAR * full_key_name,
+                          const TCHAR * value_name,
+                          byte * * value,
+                          DWORD * byte_count);
+
+  // Try reg keys successively if there is a failure in getting a value.
+  //
+  // Typically used when there is a user value and a default value if the
+  // user has none.
+  template<typename T>
+  static HRESULT GetValue(const TCHAR * full_key_names[],
+                          int key_names_length,
+                          const TCHAR * value_name,
+                          T* value);
+
+  // RENAMERS
+
+  // Rename a named value.
+  static HRESULT RenameValue(const TCHAR * full_key_name,
+                             const TCHAR * old_value_name,
+                             const TCHAR * new_value_name);
+
+  // COPIERS
+
+  // The full_to_key must exist for CopyValue to succeed.
+  static HRESULT CopyValue(const TCHAR * full_from_key_name,
+                           const TCHAR * from_value_name,
+                           const TCHAR * full_to_key_name,
+                           const TCHAR * to_value_name);
+
+  static HRESULT CopyValue(const TCHAR * full_from_key_name,
+                           const TCHAR * full_to_key_name,
+                           const TCHAR * value_name);
+
+  // Get type of a registry value
+  static HRESULT GetValueType(const TCHAR* full_key_name,
+                              const TCHAR* value_name,
+                              DWORD* value_type);
+
+  // delete a subkey of the current key (with no subkeys)
+  HRESULT DeleteSubKey(const TCHAR * key_name);
+
+  // recursively delete a sub key of the current key (and all its subkeys)
+  HRESULT RecurseDeleteSubKey(const TCHAR * key_name);
+
+  // STATIC version of delete key - handles nested keys also
+  // delete a key and all its sub-keys recursively
+  // Returns S_FALSE if key didn't exist, S_OK if deletion was successful,
+  // and failure otherwise.
+  static HRESULT DeleteKey(const TCHAR* full_key_name);
+
+  // STATIC version of delete key
+  // delete a key recursively or non-recursively
+  // Returns S_FALSE if key didn't exist, S_OK if deletion was successful,
+  // and failure otherwise.
+  static HRESULT DeleteKey(const TCHAR* full_key_name, bool recursive);
+
+  // delete the specified value
+  HRESULT DeleteValue(const TCHAR * value_name) const;
+
+  // STATIC version of delete value
+  // Returns S_FALSE if key didn't exist, S_OK if deletion was successful,
+  // and failure otherwise.
+  static HRESULT DeleteValue(const TCHAR * full_key_name,
+                             const TCHAR * value_name);
+
+  // Peek inside (use a RegKey as a smart wrapper around a registry handle)
+  HKEY Key() { return h_key_; }
+
+  // Used to help test the private functionality
+  friend class RegKeyTestClass;
+
+  // helper function to get the HKEY and the root key from a string
+  // representation modifies the argument in place and returns the key name
+  // e.g. HKLM\\Software\\Google\... returns HKLM, "Software\\Google\..."
+  // Necessary for the static versions that use the full name of the reg key
+  static HKEY GetRootKeyInfo(CString * full_key_name);
+
+  // Returns true if this key name is 'safe' for deletion (doesn't specify
+  // a key root)
+  static bool SafeKeyNameForDeletion(const wchar_t *key_name);
+
+  // save the key and all of its subkeys and values to a file
+  static HRESULT Save(const TCHAR* full_key_name, const TCHAR* file_name);
+
+  // restore the key and all of its subkeys and values which are saved into
+  // a file
+  static HRESULT Restore(const TCHAR* full_key_name, const TCHAR* file_name);
+
+  // Is the key empty: having no sub-keys and values
+  static bool IsKeyEmpty(const TCHAR* full_key_name);
+
+ private:
+
+  // Helper function to check if a key exists, using the sam flags specified.
+  // Note: KEY_READ must be included in sam_flags.
+  static bool HasKeyHelper(const TCHAR * full_key_name, DWORD sam_flags);
+
+  // helper function to get the parent key name and the subkey from a string
+  // representation modifies the argument in place and returns the key name
+  // e.g. Software\\Google\\Foo_Bar returns "Software\\Google", "Foo_Bar"
+  // Necessary for the static versions that use the full name of the reg key
+  static CString GetParentKeyInfo(CString * key_name);
+
+  // helper function to get any value from the registry
+  // used when the size of the data is unknown
+  HRESULT GetValueHelper(const TCHAR * value_name,
+                         DWORD * type,
+                         byte * * value,
+                         DWORD * byte_count) const;
+
+  // common SET Helper for the static case
+  static HRESULT SetValueStaticHelper(const TCHAR * full_key_name,
+                                      const TCHAR * value_name,
+                                      DWORD type,
+                                      LPVOID value,
+                                      DWORD byte_count = 0);
+
+  // common GET Helper for the static case
+  static HRESULT GetValueStaticHelper(const TCHAR * full_key_name,
+                                      const TCHAR * value_name,
+                                      DWORD type,
+                                      LPVOID value,
+                                      DWORD * byte_count = NULL);
+
+  // convert REG_MULTI_SZ bytes to string array
+  static HRESULT MultiSZBytesToStringArray(const byte * buffer,
+                                           DWORD byte_count,
+                                           std::vector<CString> * value);
+
+  // set a string or expandable string value
+  HRESULT SetStringValue(const TCHAR * value_name,
+                         const TCHAR * value,
+                         DWORD type) const;
+
+  // the HKEY for the current key
+  HKEY h_key_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(RegKey);
+};
+
+// Provides all the functionality of RegKey plus
+// an event to watch for changes to the registry key.
+class RegKeyWithChangeEvent : public RegKey {
+ public:
+  RegKeyWithChangeEvent() {}
+  // close this reg key and the event
+  virtual HRESULT Close();
+
+  // Called to create/reset the event that gets signaled
+  // any time the registry key changes.  Access the created
+  // event using change_event().
+  //
+  // See the documentation for RegNotifyChangeKeyValue
+  // for values for notify_filter.
+  HRESULT SetupEvent(bool watch_subtree, DWORD notify_filter);
+
+  // Indicates if any changes (that are being monitored) have occured
+  bool HasChangeOccurred() const;
+
+  // Get the event that is signaled on registry changes.
+  // Note:
+  //   * This event will remain constant until Close() is called.
+  //   * One should call SetupEvent to set-up the event.
+  //   * The event is only signaled on the next change and remains signaled.
+  //     Do not call ::ResetEvent().  Call SetupEvent() to reset
+  //     the event and wait for more changes.
+  HANDLE change_event() const {
+    return get(change_event_);
+  }
+
+ private:
+  scoped_handle change_event_;
+  DISALLOW_EVIL_CONSTRUCTORS(RegKeyWithChangeEvent);
+};
+
+// Does the common things necessary for watching
+// registry key changes.  If there are file change or other watchers,
+// there could be a common interface for the three methods to decouple
+// the code that is doing the watching from the code that owns the store.
+class RegKeyWatcher : public StoreWatcher {
+ public:
+  // reg_key: the full string for the reg key
+  // watch_subtree: watch all subkey changes  or
+  //                only immediate child values
+  // notify_filter: See the documentation for RegNotifyChangeKeyValue
+  // allow_creation: Should the key be created if it doesn't exist?
+  RegKeyWatcher(const TCHAR* reg_key, bool watch_subtree,
+                DWORD notify_filter, bool allow_creation);
+  virtual ~RegKeyWatcher() {}
+
+  // Called to create/reset the event that gets signaled
+  // any time the registry key changes.  Access the created
+  // event using change_event().
+  virtual HRESULT EnsureEventSetup();
+
+  // Get the event that is signaled on registry changes.
+  virtual HANDLE change_event() const;
+
+ private:
+  // Used to do the SetupEvent method
+  scoped_ptr<RegKeyWithChangeEvent> reg_key_with_change_event_;
+
+  CString reg_key_string_;
+  bool watch_subtree_;
+  bool allow_creation_;
+  DWORD notify_filter_;
+  DISALLOW_EVIL_CONSTRUCTORS(RegKeyWatcher);
+};
+
+
+inline RegKey::RegKey() { h_key_ = NULL; }
+
+inline RegKey::~RegKey() { Close(); }
+
+inline bool RegKey::HasValue(const TCHAR* value_name) {
+  return (ERROR_SUCCESS == ::RegQueryValueEx(h_key_,
+                                             value_name,
+                                             NULL, NULL,
+                                             NULL,
+                                             NULL));
+}
+
+// SETTERS static versions
+inline HRESULT RegKey::SetValue(const TCHAR* full_key_name,
+                                const TCHAR* value_name,
+                                DWORD value) {
+  ASSERT1(full_key_name);
+
+  return SetValueStaticHelper(full_key_name, value_name, REG_DWORD, &value);
+}
+
+inline HRESULT RegKey::SetValue(const TCHAR* full_key_name,
+                                const TCHAR* value_name,
+                                DWORD64 value) {
+  ASSERT1(full_key_name);
+
+  return SetValueStaticHelper(full_key_name, value_name, REG_QWORD, &value);
+}
+
+inline HRESULT RegKey::SetValue(const TCHAR* full_key_name,
+                                const TCHAR* value_name,
+                                float value) {
+  ASSERT1(full_key_name);
+
+  return SetValueStaticHelper(full_key_name,
+                              value_name,
+                              REG_BINARY,
+                              &value,
+                              sizeof(value));
+}
+
+inline HRESULT RegKey::SetValue(const TCHAR* full_key_name,
+                                const TCHAR* value_name,
+                                double value) {
+  ASSERT1(full_key_name);
+
+  return SetValueStaticHelper(full_key_name,
+                              value_name,
+                              REG_BINARY,
+                              &value,
+                              sizeof(value));
+}
+
+inline HRESULT RegKey::SetValue(const TCHAR* full_key_name,
+                                const TCHAR* value_name,
+                                const TCHAR* value) {
+  ASSERT1(full_key_name);
+  ASSERT1(value);
+
+  return SetValueStaticHelper(full_key_name,
+                              value_name,
+                              REG_SZ,
+                              const_cast<TCHAR*>(value));
+}
+
+inline HRESULT RegKey::SetValue(const TCHAR* full_key_name,
+                                const TCHAR* value_name,
+                                const byte* value,
+                                DWORD byte_count) {
+  ASSERT1(full_key_name);
+
+  return SetValueStaticHelper(full_key_name, value_name, REG_BINARY,
+                              const_cast<byte*>(value), byte_count);
+}
+
+inline HRESULT RegKey::SetValueMultiSZ(const TCHAR* full_key_name,
+                                       const TCHAR* value_name,
+                                       const byte* value,
+                                       DWORD byte_count) {
+  ASSERT1(full_key_name);
+
+  return SetValueStaticHelper(full_key_name, value_name, REG_MULTI_SZ,
+                              const_cast<byte*>(value), byte_count);
+}
+
+inline HRESULT RegKey::SetValueExpandSZ(const TCHAR* full_key_name,
+                                        const TCHAR* value_name,
+                                        const TCHAR* value) {
+  ASSERT1(full_key_name);
+  ASSERT1(value);
+
+  return SetValueStaticHelper(full_key_name,
+                              value_name,
+                              REG_EXPAND_SZ,
+                              const_cast<TCHAR*>(value));
+}
+
+// GETTERS static versions
+inline HRESULT RegKey::GetValue(const TCHAR* full_key_name,
+                                const TCHAR* value_name,
+                                DWORD* value) {
+  ASSERT1(full_key_name);
+  ASSERT1(value);
+
+  return GetValueStaticHelper(full_key_name, value_name, REG_DWORD, value);
+}
+
+inline HRESULT RegKey::GetValue(const TCHAR* full_key_name,
+                                const TCHAR* value_name,
+                                DWORD64* value) {
+  ASSERT1(full_key_name);
+  ASSERT1(value);
+
+  return GetValueStaticHelper(full_key_name, value_name, REG_QWORD, value);
+}
+
+inline HRESULT RegKey::GetValue(const TCHAR* full_key_name,
+                                const TCHAR* value_name,
+                                float* value) {
+  ASSERT1(value);
+  ASSERT1(value_name);
+  ASSERT1(full_key_name);
+
+  DWORD byte_count = 0;
+  byte* buffer = NULL;
+  HRESULT hr = GetValueStaticHelper(full_key_name,
+                                    value_name,
+                                    REG_BINARY,
+                                    &buffer,
+                                    &byte_count);
+  scoped_array<byte> free_buffer(buffer);
+
+  if (SUCCEEDED(hr)) {
+    if (byte_count == sizeof(*value)) {
+      ::CopyMemory(value, buffer, sizeof(*value));
+    } else {
+      UTIL_LOG(LEVEL_ERROR, (_T("[RegKey::GetValue]")
+                             _T("[size mismatches for float value][%s\\%s]"),
+                             full_key_name, value_name));
+      return HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
+    }
+  }
+
+  return hr;
+}
+
+inline HRESULT RegKey::GetValue(const TCHAR* full_key_name,
+                                const TCHAR* value_name,
+                                double* value) {
+  ASSERT1(value);
+  ASSERT1(value_name);
+  ASSERT1(full_key_name);
+
+  DWORD byte_count = 0;
+  byte* buffer = NULL;
+  HRESULT hr = GetValueStaticHelper(full_key_name,
+                                    value_name,
+                                    REG_BINARY,
+                                    &buffer,
+                                    &byte_count);
+  scoped_array<byte> free_buffer(buffer);
+
+  if (SUCCEEDED(hr)) {
+    if (byte_count == sizeof(*value)) {
+      ::CopyMemory(value, buffer, sizeof(*value));
+    } else {
+      UTIL_LOG(LEVEL_ERROR, (_T("[RegKey::GetValue]")
+                             _T("[size mismatches for double value][%s\\%s]"),
+                             full_key_name, value_name));
+      return HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
+    }
+  }
+
+  return hr;
+}
+
+inline HRESULT RegKey::GetValue(const TCHAR* full_key_name,
+                                const TCHAR* value_name,
+                                TCHAR** value) {
+  ASSERT1(full_key_name);
+  ASSERT1(value);
+
+  return GetValueStaticHelper(full_key_name, value_name, REG_SZ, value);
+}
+
+inline HRESULT RegKey::GetValue(const TCHAR* full_key_name,
+                                const TCHAR* value_name,
+                                CString* value) {
+  ASSERT1(full_key_name);
+  ASSERT1(value);
+
+  TCHAR* buffer = NULL;
+  HRESULT hr = RegKey::GetValue(full_key_name, value_name, &buffer);
+  value->SetString(buffer);
+  delete [] buffer;
+  return hr;
+}
+
+inline HRESULT RegKey::GetValue(const TCHAR* full_key_name,
+                                const TCHAR* value_name,
+                                std::vector<CString>* value) {
+  ASSERT1(full_key_name);
+  ASSERT1(value);
+
+  return GetValueStaticHelper(full_key_name, value_name, REG_MULTI_SZ, value);
+}
+
+inline HRESULT RegKey::GetValue(const TCHAR* full_key_name,
+                                const TCHAR* value_name,
+                                byte** value,
+                                DWORD* byte_count) {
+  ASSERT1(full_key_name);
+  ASSERT1(value);
+  ASSERT1(byte_count);
+
+  return GetValueStaticHelper(full_key_name,
+                              value_name,
+                              REG_BINARY,
+                              value,
+                              byte_count);
+}
+
+template<typename T>
+HRESULT RegKey::GetValue(const TCHAR* full_key_names[],
+                         int key_names_length,
+                         const TCHAR* value_name,
+                         T* value) {
+  HRESULT hr = S_OK;
+  for (int i = 0; i < key_names_length; ++i) {
+    hr = GetValue(full_key_names[i], value_name, value);
+    if (SUCCEEDED(hr)) {
+      return hr;
+    }
+  }
+  return hr;
+}
+
+// Rename a named value.
+inline HRESULT RegKey::RenameValue(const TCHAR * full_key_name,
+                                   const TCHAR * old_value_name,
+                                   const TCHAR * new_value_name) {
+  ASSERT1(full_key_name);
+
+  RegKey reg_key;
+  HRESULT hr = reg_key.Open(full_key_name);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return reg_key.RenameValue(old_value_name, new_value_name);
+}
+
+inline HRESULT RegKey::CopyValue(const TCHAR * full_from_key_name,
+                                 const TCHAR * full_to_key_name,
+                                 const TCHAR * value_name) {
+  return CopyValue(full_from_key_name,
+                   value_name,
+                   full_to_key_name,
+                   value_name);
+}
+
+// DELETE
+inline HRESULT RegKey::DeleteSubKey(const TCHAR* key_name) {
+  ASSERT1(key_name);
+  ASSERT1(h_key_);
+
+  LONG res = ::RegDeleteKey(h_key_, key_name);
+  HRESULT hr = HRESULT_FROM_WIN32(res);
+  if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
+      hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) {
+    hr = S_FALSE;
+  }
+  return hr;
+}
+
+inline HRESULT RegKey::DeleteValue(const TCHAR* value_name) const {
+  ASSERT1(value_name);
+  ASSERT1(h_key_);
+
+  LONG res = ::RegDeleteValue(h_key_, value_name);
+  HRESULT hr = HRESULT_FROM_WIN32(res);
+  if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
+      hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) {
+    hr = S_FALSE;
+  }
+  return hr;
+}
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_REG_KEY_H_
+
diff --git a/base/reg_key_unittest.cc b/base/reg_key_unittest.cc
new file mode 100644
index 0000000..31c5b6c
--- /dev/null
+++ b/base/reg_key_unittest.cc
@@ -0,0 +1,918 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/reg_key.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+#define kStTestRkeyRelativeBase   _T("Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\UnitTest")
+#define kStTestRkeyBase   _T("HKCU\\") kStTestRkeyRelativeBase
+#define kStRkey1Name      _T("TEST")
+#define kStRkey1          kStTestRkeyBase _T("\\") kStRkey1Name
+#define kRkey1            kStTestRkeyRelativeBase _T("\\") kStRkey1Name
+#define kStRkey2          kStTestRkeyBase _T("\\TEST2")
+#define kStRkey3          kStTestRkeyBase _T("\\TEST3")
+#define kRkey1SubkeyName  _T("subkey_test")
+#define kRkey1Subkey      kRkey1 _T("\\") kRkey1SubkeyName
+#define kStRkey1Subkey    kStRkey1 _T("\\") kRkey1SubkeyName
+
+// NON - STATIC
+
+#define kValNameInt          _T("Int32 Value")
+#define kRenameValNameInt    _T("Renamed Int32 Value")
+#define kIntVal              (DWORD)20
+#define kIntVal2             (DWORD)30
+
+#define kValNameInt64        _T("Int64 Value")
+#define kIntVal64            (DWORD64)40
+#define kIntVal642           (DWORD64)50
+
+#define kValNameStr          _T("Str Value")
+#define kStrVal              _T("Some string data 1")
+#define kStrVal2             _T("Some string data 2")
+
+#define kValNameBinary       _T("Binary Value")
+#define kBinaryVal           "Some binary data abcdefghi 1"
+#define kBinaryVal2          "Some binary data abcdefghi 2"
+
+// STATIC
+
+#define kStValNameInt        _T("Static Int32 Value")
+#define kStIntVal            (DWORD)60
+
+#define kStValNameInt64      _T("Static Int64 Value")
+#define kStIntVal64          (DWORD64)80
+
+#define kStValNameFloat      _T("Static Float Value")
+#define kStFloatVal          (static_cast<float>(12.3456789))
+
+#define kStValNameDouble     _T("Static Double Value")
+#define kStDoubleVal         (static_cast<double>(98.7654321))
+
+#define kStValNameStr        _T("Static Str Value")
+#define kRenameStValNameStr  _T("Renamed Static Str Value")
+#define kStStrVal            _T("Some static string data 2")
+
+#define kStValNameBinary     _T("Static Binary Value")
+#define kStBinaryVal         "Some static binary data abcdefghi 2"
+
+// Test the private member functions of RegKey
+class RegKeyTestClass : public testing::Test {
+ protected:
+  static const HKEY GetHKey(const RegKey& reg) {
+    return reg.h_key_;
+  }
+
+  static CString GetParentKeyInfo(CString* key_name) {
+    return RegKey::GetParentKeyInfo(key_name);
+  }
+};
+
+class RegKeyCleanupTestKeyTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    EXPECT_SUCCEEDED(RegKey::DeleteKey(kStTestRkeyBase));
+  }
+
+  virtual void TearDown() {
+    EXPECT_SUCCEEDED(RegKey::DeleteKey(kStTestRkeyBase));
+  }
+
+  RegKey key_;
+};
+
+// Make sure the RegKey is nice and clean when we first initialize it
+TEST_F(RegKeyTestClass, Init) {
+  // Make a new RegKey object so we can test its pristine state
+  RegKey reg;
+
+  ASSERT_TRUE(GetHKey(reg) == NULL);
+}
+
+// Make sure the helper functions work
+TEST_F(RegKeyTestClass, Helper) {
+  // Dud items cause NULL
+  CString temp_key;
+
+  // RegKey::GetRootKeyInfo turns a string into the HKEY and subtree value
+
+  // Try out some dud values
+  temp_key = _T("");
+  ASSERT_TRUE(RegKey::GetRootKeyInfo(&temp_key) == NULL);
+  ASSERT_STREQ(temp_key, _T(""));
+
+  temp_key = _T("a");
+  ASSERT_TRUE(RegKey::GetRootKeyInfo(&temp_key) == NULL);
+  ASSERT_STREQ(temp_key, _T(""));
+
+  // The basics
+  temp_key = _T("HKLM\\a");
+  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_LOCAL_MACHINE);
+  ASSERT_STREQ(temp_key, _T("a"));
+
+  temp_key = _T("HKEY_LOCAL_MACHINE\\a");
+  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_LOCAL_MACHINE);
+  ASSERT_STREQ(temp_key, _T("a"));
+
+  temp_key = _T("HKCU\\a");
+  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CURRENT_USER);
+  ASSERT_STREQ(temp_key, _T("a"));
+
+  temp_key = _T("HKEY_CURRENT_USER\\a");
+  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CURRENT_USER);
+  ASSERT_STREQ(temp_key, _T("a"));
+
+  temp_key = _T("HKU\\a");
+  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_USERS);
+  ASSERT_STREQ(temp_key, _T("a"));
+
+  temp_key = _T("HKEY_USERS\\a");
+  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_USERS);
+  ASSERT_STREQ(temp_key, _T("a"));
+
+  temp_key = _T("HKCR\\a");
+  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CLASSES_ROOT);
+  ASSERT_STREQ(temp_key, _T("a"));
+
+  temp_key = _T("HKEY_CLASSES_ROOT\\a");
+  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CLASSES_ROOT);
+  ASSERT_STREQ(temp_key, _T("a"));
+
+  // Make sure it is case insensitive
+  temp_key = _T("hkcr\\a");
+  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CLASSES_ROOT);
+  ASSERT_STREQ(temp_key, _T("a"));
+
+  temp_key = _T("hkey_CLASSES_ROOT\\a");
+  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CLASSES_ROOT);
+  ASSERT_STREQ(temp_key, _T("a"));
+
+  // Test out temp_GetParentKeyInfo
+
+  // dud cases
+  temp_key = _T("");
+  ASSERT_STREQ(GetParentKeyInfo(&temp_key), _T(""));
+  ASSERT_STREQ(temp_key, _T(""));
+
+  temp_key = _T("a");
+  ASSERT_STREQ(GetParentKeyInfo(&temp_key), _T(""));
+  ASSERT_STREQ(temp_key, _T("a"));
+
+  temp_key = _T("a\\b");
+  ASSERT_STREQ(GetParentKeyInfo(&temp_key), _T("a"));
+  ASSERT_STREQ(temp_key, _T("b"));
+
+  temp_key = _T("\\b");
+  ASSERT_STREQ(GetParentKeyInfo(&temp_key), _T(""));
+  ASSERT_STREQ(temp_key, _T("b"));
+
+
+  // Some regular cases
+  temp_key = _T("HKEY_CLASSES_ROOT\\moon");
+  ASSERT_STREQ(GetParentKeyInfo(&temp_key), _T("HKEY_CLASSES_ROOT"));
+  ASSERT_STREQ(temp_key, _T("moon"));
+
+  temp_key = _T("HKEY_CLASSES_ROOT\\moon\\doggy");
+  ASSERT_STREQ(GetParentKeyInfo(&temp_key),
+               _T("HKEY_CLASSES_ROOT\\moon"));
+  ASSERT_STREQ(temp_key, _T("doggy"));
+}
+
+
+TEST(RegKeyTest, RegKey) {
+  //
+  // PRIVATE MEMBER WHITE BOX TESTS
+  //
+  RegKeyWithChangeEvent r_key;
+  bool bool_res = false;
+  HRESULT hr = E_FAIL;
+  DWORD int_val = 0;
+  DWORD64 int64_val = 0;
+  time64 t = 0;
+  float float_val = 0;
+  double double_val = 0;
+  TCHAR * str_val = NULL;
+  byte * binary_val = NULL;
+  DWORD byte_count = 0;
+
+  // Just in case...
+  // make sure the no test key residue is left from previous aborted runs
+  hr = RegKey::DeleteKey(kStTestRkeyBase);
+
+  // first test the non-static version
+
+  // create a reg key
+  hr = r_key.Create(HKEY_CURRENT_USER, kRkey1);
+  ASSERT_SUCCEEDED(hr);
+
+  // do the create twice - it should return the already created one
+  hr = r_key.Create(HKEY_CURRENT_USER, kRkey1);
+  ASSERT_SUCCEEDED(hr);
+
+  // now do an open - should work just fine
+  hr = r_key.Open(HKEY_CURRENT_USER, kRkey1);
+  ASSERT_SUCCEEDED(hr);
+
+  // get an in-existent value
+  hr = r_key.GetValue(kValNameInt, &int_val);
+  ASSERT_EQ(hr, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
+
+  // get an in-existent value type
+  DWORD value_type = REG_NONE;
+  hr = r_key.GetValueType(kValNameInt, &value_type);
+  ASSERT_EQ(hr, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
+
+  // set-up an event to watch for changes
+  hr = r_key.SetupEvent(TRUE,
+                        REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET);
+  ASSERT_SUCCEEDED(hr);
+  HANDLE change_event = r_key.change_event();
+  ASSERT_EQ(::WaitForSingleObject(change_event, 0), WAIT_TIMEOUT);
+
+  // set and get some values and verify that the handle gets signaled
+
+  // set an INT 32
+  hr = r_key.SetValue(kValNameInt, kIntVal);
+  ASSERT_SUCCEEDED(hr);
+
+  hr = r_key.GetValueType(kValNameInt, &value_type);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(REG_DWORD, value_type);
+  hr = RegKey::GetValueType(kStRkey1, kValNameInt, &value_type);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(REG_DWORD, value_type);
+
+  // verify that we got the change and that the event got reset appropriately
+  // and set-up the notification again (use the actual event this time)
+  ASSERT_EQ(::WaitForSingleObject(change_event, 0), WAIT_OBJECT_0);
+  hr = r_key.SetupEvent(TRUE,
+                        REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_FALSE(r_key.HasChangeOccurred());
+
+  // check that the value exists
+  bool_res = r_key.HasValue(kValNameInt);
+  ASSERT_TRUE(bool_res);
+  // No change expected on a read
+  ASSERT_FALSE(r_key.HasChangeOccurred());
+
+  // read it back
+  hr = r_key.GetValue(kValNameInt, &int_val);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(int_val, kIntVal);
+  // No change expected on a read
+  ASSERT_FALSE(r_key.HasChangeOccurred());
+
+  // set it again!
+  hr = r_key.SetValue(kValNameInt, kIntVal2);
+  ASSERT_SUCCEEDED(hr);
+  // verify that we got the change and that the event got reset appropriately
+  // and set-up the notification again
+  ASSERT_TRUE(r_key.HasChangeOccurred());
+  hr = r_key.SetupEvent(TRUE,
+                        REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_FALSE(r_key.HasChangeOccurred());
+
+  // read it again
+  hr = r_key.GetValue(kValNameInt, &int_val);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(int_val, kIntVal2);
+  // No change expected on a read
+  ASSERT_FALSE(r_key.HasChangeOccurred());
+
+  // delete the value
+  hr = r_key.DeleteValue(kValNameInt);
+  ASSERT_SUCCEEDED(hr);
+  // verify that we got the change
+  ASSERT_TRUE(r_key.HasChangeOccurred());
+
+  // check that the value is gone
+  bool_res = r_key.HasValue(kValNameInt);
+  ASSERT_FALSE(bool_res);
+
+  // set an INT 64
+  hr = r_key.SetValue(kValNameInt64, kIntVal64);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value exists
+  bool_res = r_key.HasValue(kValNameInt64);
+  ASSERT_TRUE(bool_res);
+
+  // read it back
+  hr = r_key.GetValue(kValNameInt64, &int64_val);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(int64_val, kIntVal64);
+
+  // delete the value
+  hr = r_key.DeleteValue(kValNameInt64);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value is gone
+  bool_res = r_key.HasValue(kValNameInt64);
+  ASSERT_FALSE(bool_res);
+
+  // set a string
+  hr = r_key.SetValue(kValNameStr, kStrVal);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value exists
+  bool_res = r_key.HasValue(kValNameStr);
+  ASSERT_TRUE(bool_res);
+
+  // read it back
+  hr = r_key.GetValue(kValNameStr, &str_val);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_STREQ(str_val, kStrVal);
+  delete [] str_val;
+
+  // set it again
+  hr = r_key.SetValue(kValNameStr, kStrVal2);
+  ASSERT_SUCCEEDED(hr);
+
+  // read it again
+  hr = r_key.GetValue(kValNameStr, &str_val);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_STREQ(str_val, kStrVal2);
+  delete [] str_val;
+
+  // delete the value
+  hr = r_key.DeleteValue(kValNameStr);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value is gone
+  bool_res = r_key.HasValue(kValNameInt);
+  ASSERT_FALSE(bool_res);
+
+  // set a binary value
+  hr = r_key.SetValue(kValNameBinary, (const byte *)kBinaryVal,
+                      sizeof(kBinaryVal)-1);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value exists
+  bool_res = r_key.HasValue(kValNameBinary);
+  ASSERT_TRUE(bool_res);
+
+  // read it back
+  hr = r_key.GetValue(kValNameBinary, &binary_val, &byte_count);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(0, memcmp(binary_val, kBinaryVal, sizeof(kBinaryVal)-1));
+  delete [] binary_val;
+
+  // set it again
+  hr = r_key.SetValue(kValNameBinary, (const byte *)kBinaryVal2,
+                      sizeof(kBinaryVal)-1);
+  ASSERT_SUCCEEDED(hr);
+
+  // read it again
+  hr = r_key.GetValue(kValNameBinary, &binary_val, &byte_count);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(0, memcmp(binary_val, kBinaryVal2, sizeof(kBinaryVal2)-1));
+  delete [] binary_val;
+
+  // delete the value
+  hr = r_key.DeleteValue(kValNameBinary);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value is gone
+  bool_res = r_key.HasValue(kValNameBinary);
+  ASSERT_FALSE(bool_res);
+
+  // set some values and check the total count
+
+  // set an INT 32
+  hr = r_key.SetValue(kValNameInt, kIntVal);
+  ASSERT_SUCCEEDED(hr);
+
+  // set an INT 64
+  hr = r_key.SetValue(kValNameInt64, kIntVal64);
+  ASSERT_SUCCEEDED(hr);
+
+  // set a string
+  hr = r_key.SetValue(kValNameStr, kStrVal);
+  ASSERT_SUCCEEDED(hr);
+
+  hr = r_key.GetValueType(kValNameStr, &value_type);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(REG_SZ, value_type);
+  hr = RegKey::GetValueType(kStRkey1, kValNameStr, &value_type);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(REG_SZ, value_type);
+
+  // set a binary value
+  hr = r_key.SetValue(kValNameBinary, (const byte *)kBinaryVal,
+                      sizeof(kBinaryVal)-1);
+  ASSERT_SUCCEEDED(hr);
+
+  // get the value count
+  uint32 value_count = r_key.GetValueCount();
+  ASSERT_EQ(value_count, 4);
+
+  // check the value names
+  CString value_name;
+  DWORD type;
+
+  hr = r_key.GetValueNameAt(0, &value_name, &type);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(value_name, kValNameInt);
+  ASSERT_EQ(type, REG_DWORD);
+
+  hr = r_key.GetValueNameAt(1, &value_name, &type);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(value_name, kValNameInt64);
+  ASSERT_EQ(type, REG_QWORD);
+
+  hr = r_key.GetValueNameAt(2, &value_name, &type);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(value_name, kValNameStr);
+  ASSERT_EQ(type, REG_SZ);
+
+  hr = r_key.GetValueNameAt(3, &value_name, &type);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(value_name, kValNameBinary);
+  ASSERT_EQ(type, REG_BINARY);
+
+  // check that there are no more values
+  hr = r_key.GetValueNameAt(4, &value_name, &type);
+  ASSERT_FAILED(hr);
+
+  uint32 subkey_count = r_key.GetSubkeyCount();
+  ASSERT_EQ(subkey_count, 0);
+
+  RegKey temp_key;
+
+  // now create a subkey and make sure we can get the name
+  hr = temp_key.Create(HKEY_CURRENT_USER, kRkey1Subkey);
+  ASSERT_SUCCEEDED(hr);
+
+  // check the subkey exists
+  bool_res = r_key.HasSubkey(kRkey1SubkeyName);
+  ASSERT_TRUE(bool_res);
+
+  // check the name
+  subkey_count = r_key.GetSubkeyCount();
+  ASSERT_EQ(subkey_count, 1);
+
+  CString subkey_name;
+  hr = r_key.GetSubkeyNameAt(0, &subkey_name);
+  ASSERT_EQ(subkey_name, kRkey1SubkeyName);
+
+  // verify that the event handle remained the same throughout everything
+  ASSERT_EQ(change_event, r_key.change_event());
+
+  // close this key
+  r_key.Close();
+
+  // whack the whole key
+  hr = RegKey::DeleteKey(kStTestRkeyBase);
+  ASSERT_SUCCEEDED(hr);
+
+  // STATIC
+  // now set a different value using the static versions
+
+  // get an in-existent value from an un-existent key
+  hr = RegKey::GetValue(kStRkey1, kStValNameInt, &int_val);
+  ASSERT_EQ(hr, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
+
+  // set int32
+  hr = RegKey::SetValue(kStRkey1, kStValNameInt, kStIntVal);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value exists
+  bool_res = RegKey::HasValue(kStRkey1, kStValNameInt);
+  ASSERT_TRUE(bool_res);
+
+  // get an in-existent value from an existent key
+  hr = RegKey::GetValue(kStRkey1, _T("bogus"), &int_val);
+  ASSERT_EQ(hr, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
+
+  // read it back
+  hr = RegKey::GetValue(kStRkey1, kStValNameInt, &int_val);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(int_val, kStIntVal);
+
+  // delete the value
+  hr = RegKey::DeleteValue(kStRkey1, kStValNameInt);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value is gone
+  bool_res = RegKey::HasValue(kStRkey1, kStValNameInt);
+  ASSERT_FALSE(bool_res);
+
+
+  // set int64
+  hr = RegKey::SetValue(kStRkey1, kStValNameInt64, kStIntVal64);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value exists
+  bool_res = RegKey::HasValue(kStRkey1, kStValNameInt64);
+  ASSERT_TRUE(bool_res);
+
+  // read it back
+  hr = RegKey::GetValue(kStRkey1, kStValNameInt64, &int64_val);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(int64_val, kStIntVal64);
+
+  // read it back to test time64
+  bool limited_value;
+  hr = GetLimitedTimeValue(kStRkey1, kStValNameInt64,  kStIntVal64 + 10, &t,
+                           &limited_value);
+  ASSERT_SUCCEEDED(hr);
+  EXPECT_FALSE(limited_value);
+  ASSERT_EQ(t, kStIntVal64);
+  hr = GetLimitedTimeValue(kStRkey1, kStValNameInt64,  kStIntVal64 - 10, &t,
+                           &limited_value);
+  EXPECT_TRUE(limited_value);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(t, kStIntVal64 - 10);
+  // Verify that the GetValue permanently made the value lower
+  hr = GetLimitedTimeValue(kStRkey1, kStValNameInt64,  kStIntVal64, &t,
+                           &limited_value);
+  EXPECT_FALSE(limited_value);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(t, kStIntVal64 - 10);
+
+  // delete the value
+  hr = RegKey::DeleteValue(kStRkey1, kStValNameInt64);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value is gone
+  bool_res = RegKey::HasValue(kStRkey1, kStValNameInt64);
+  ASSERT_FALSE(bool_res);
+
+  // set float
+  hr = RegKey::SetValue(kStRkey1, kStValNameFloat, kStFloatVal);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value exists
+  bool_res = RegKey::HasValue(kStRkey1, kStValNameFloat);
+  ASSERT_TRUE(bool_res);
+
+  // read it back
+  hr = RegKey::GetValue(kStRkey1, kStValNameFloat, &float_val);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(float_val, kStFloatVal);
+
+  // delete the value
+  hr = RegKey::DeleteValue(kStRkey1, kStValNameFloat);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value is gone
+  bool_res = RegKey::HasValue(kStRkey1, kStValNameFloat);
+  ASSERT_FALSE(bool_res);
+  hr = RegKey::GetValue(kStRkey1, kStValNameFloat, &float_val);
+  ASSERT_FAILED(hr);
+
+
+  // set double
+  hr = RegKey::SetValue(kStRkey1, kStValNameDouble, kStDoubleVal);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value exists
+  bool_res = RegKey::HasValue(kStRkey1, kStValNameDouble);
+  ASSERT_TRUE(bool_res);
+
+  // read it back
+  hr = RegKey::GetValue(kStRkey1, kStValNameDouble, &double_val);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(double_val, kStDoubleVal);
+
+  // delete the value
+  hr = RegKey::DeleteValue(kStRkey1, kStValNameDouble);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value is gone
+  bool_res = RegKey::HasValue(kStRkey1, kStValNameDouble);
+  ASSERT_FALSE(bool_res);
+  hr = RegKey::GetValue(kStRkey1, kStValNameDouble, &double_val);
+  ASSERT_FAILED(hr);
+
+  // set string
+  hr = RegKey::SetValue(kStRkey1, kStValNameStr, kStStrVal);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value exists
+  bool_res = RegKey::HasValue(kStRkey1, kStValNameStr);
+  ASSERT_TRUE(bool_res);
+
+  // read it back
+  hr = RegKey::GetValue(kStRkey1, kStValNameStr, &str_val);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_STREQ(str_val, kStStrVal);
+  delete [] str_val;
+
+  // get an in-existent value from an existent key
+  hr = RegKey::GetValue(kStRkey1, _T("bogus"), &str_val);
+  ASSERT_EQ(hr, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
+
+  // delete the value
+  hr = RegKey::DeleteValue(kStRkey1, kStValNameStr);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value is gone
+  bool_res = RegKey::HasValue(kStRkey1, kStValNameStr);
+  ASSERT_FALSE(bool_res);
+
+  // set binary
+  hr = RegKey::SetValue(kStRkey1, kStValNameBinary, (const byte *)kStBinaryVal,
+                        sizeof(kStBinaryVal)-1);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value exists
+  bool_res = RegKey::HasValue(kStRkey1, kStValNameBinary);
+  ASSERT_TRUE(bool_res);
+
+  // read it back
+  hr = RegKey::GetValue(kStRkey1, kStValNameBinary, &binary_val, &byte_count);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(0, memcmp(binary_val, kStBinaryVal, sizeof(kStBinaryVal)-1));
+  delete [] binary_val;
+
+  // delete the value
+  hr = RegKey::DeleteValue(kStRkey1, kStValNameBinary);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value is gone
+  bool_res = RegKey::HasValue(kStRkey1, kStValNameBinary);
+  ASSERT_FALSE(bool_res);
+
+  // special case - set a binary value with length 0
+  hr = RegKey::SetValue(kStRkey1, kStValNameBinary,
+                        (const byte *)kStBinaryVal, 0);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value exists
+  bool_res = RegKey::HasValue(kStRkey1, kStValNameBinary);
+  ASSERT_TRUE(bool_res);
+
+  // read it back
+  hr = RegKey::GetValue(kStRkey1, kStValNameBinary, &binary_val, &byte_count);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(byte_count, 0);
+  ASSERT_TRUE(binary_val == NULL);
+  delete [] binary_val;
+
+  // delete the value
+  hr = RegKey::DeleteValue(kStRkey1, kStValNameBinary);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value is gone
+  bool_res = RegKey::HasValue(kStRkey1, kStValNameBinary);
+  ASSERT_FALSE(bool_res);
+
+  // special case - set a NULL binary value
+  hr = RegKey::SetValue(kStRkey1, kStValNameBinary, NULL, 100);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value exists
+  bool_res = RegKey::HasValue(kStRkey1, kStValNameBinary);
+  ASSERT_TRUE(bool_res);
+
+  // read it back
+  hr = RegKey::GetValue(kStRkey1, kStValNameBinary, &binary_val, &byte_count);
+  ASSERT_SUCCEEDED(hr);
+  ASSERT_EQ(byte_count, 0);
+  ASSERT_TRUE(binary_val == NULL);
+  delete [] binary_val;
+
+  // delete the value
+  hr = RegKey::DeleteValue(kStRkey1, kStValNameBinary);
+  ASSERT_SUCCEEDED(hr);
+
+  // check that the value is gone
+  bool_res = RegKey::HasValue(kStRkey1, kStValNameBinary);
+  ASSERT_FALSE(bool_res);
+
+  // whack the whole key
+
+  hr = RegKey::DeleteKey(kStTestRkeyBase);
+  ASSERT_SUCCEEDED(hr);
+}
+
+// RegKey::GetValue changes the output CString when errors occur.
+TEST_F(RegKeyTestClass, ChangesStringOnErrors) {
+  CString string_val = _T("foo");
+  EXPECT_FAILED(RegKey::GetValue(_T("HCKU"), _T("no_such_value"), &string_val));
+  ASSERT_TRUE(string_val.IsEmpty());
+}
+
+TEST_F(RegKeyCleanupTestKeyTest, CreateKeys) {
+  // 3 keys specified but the count is two.
+  const TCHAR* keys[] = {kStRkey1, kStRkey2, kStRkey3};
+  ASSERT_SUCCEEDED(RegKey::CreateKeys(keys, 2));
+
+  EXPECT_TRUE(RegKey::HasKey(kStRkey1));
+  EXPECT_TRUE(RegKey::HasKey(kStRkey2));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey3));
+}
+
+TEST_F(RegKeyCleanupTestKeyTest, CreateKey) {
+  ASSERT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
+
+  EXPECT_TRUE(RegKey::HasKey(kStRkey1));
+}
+
+TEST_F(RegKeyCleanupTestKeyTest, RenameValue) {
+  RegKey reg_key;
+  ASSERT_SUCCEEDED(reg_key.Create(HKEY_CURRENT_USER, kRkey1));
+  ASSERT_SUCCEEDED(reg_key.SetValue(kValNameInt, kIntVal));
+  ASSERT_TRUE(reg_key.HasValue(kValNameInt));
+
+  ASSERT_SUCCEEDED(reg_key.RenameValue(kValNameInt, kRenameValNameInt));
+  ASSERT_FALSE(reg_key.HasValue(kValNameInt));
+
+  DWORD int_val = 0;
+  EXPECT_SUCCEEDED(reg_key.GetValue(kRenameValNameInt, &int_val));
+  EXPECT_EQ(kIntVal, int_val);
+
+  EXPECT_SUCCEEDED(reg_key.Close());
+}
+
+TEST_F(RegKeyCleanupTestKeyTest, RenameValueStatic) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kStRkey1, kStValNameStr, kStStrVal));
+  ASSERT_TRUE(RegKey::HasValue(kStRkey1, kStValNameStr));
+
+  RegKey::RenameValue(kStRkey1, kStValNameStr, kRenameStValNameStr);
+  ASSERT_FALSE(RegKey::HasValue(kStRkey1, kStValNameStr));
+
+  CString str_val;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kStRkey1, kRenameStValNameStr, &str_val));
+  EXPECT_STREQ(kStStrVal, str_val);
+}
+
+TEST_F(RegKeyCleanupTestKeyTest, CopyValue) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kStRkey1, kStValNameStr, kStStrVal));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kStRkey1, NULL, kStStrVal));
+  EXPECT_TRUE(RegKey::HasValue(kStRkey1, kStValNameStr));
+
+  // Test that CopyValue fails when the to_key does not exist.
+  EXPECT_FALSE(RegKey::HasKey(kStRkey2));
+  EXPECT_FAILED(RegKey::CopyValue(kStRkey1, kStRkey2, kStValNameStr));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey2));
+
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey2));
+  // Test CopyValue(full_from_key_name, full_to_key_name, value_name).
+  EXPECT_FALSE(RegKey::HasValue(kStRkey2, kStValNameStr));
+  RegKey::CopyValue(kStRkey1, kStRkey2, kStValNameStr);
+  CString str_val;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kStRkey2, kStValNameStr, &str_val));
+  EXPECT_STREQ(kStStrVal, str_val);
+
+  // Test CopyValue to a (Default) value.
+  EXPECT_FALSE(RegKey::HasValue(kStRkey2, NULL));
+  RegKey::CopyValue(kStRkey1, kStRkey2, NULL);
+  str_val.Empty();
+  EXPECT_SUCCEEDED(RegKey::GetValue(kStRkey2, NULL, &str_val));
+  EXPECT_STREQ(kStStrVal, str_val);
+
+  // Test CopyValue(full_from_key_name, from_value_name, full_to_key_name,
+  //                to_value_name).
+  EXPECT_FALSE(RegKey::HasValue(kStRkey2, kRenameStValNameStr));
+  RegKey::CopyValue(kStRkey1, kStValNameStr, kStRkey2, kRenameStValNameStr);
+  str_val.Empty();
+  EXPECT_SUCCEEDED(RegKey::GetValue(kStRkey2, kRenameStValNameStr, &str_val));
+  EXPECT_STREQ(kStStrVal, str_val);
+}
+
+// Delete a key that does not have children.
+
+TEST_F(RegKeyCleanupTestKeyTest, DeleteKey_NoChildren_Recursively) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
+
+  EXPECT_EQ(S_OK, RegKey::DeleteKey(kStRkey1, true));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
+}
+
+TEST_F(RegKeyCleanupTestKeyTest, DeleteKey_NoChildren_NotRecursively) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
+
+  EXPECT_EQ(S_OK, RegKey::DeleteKey(kStRkey1, false));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
+}
+
+TEST_F(RegKeyCleanupTestKeyTest, RecurseDeleteSubKey_NoChildren) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
+  EXPECT_SUCCEEDED(key_.Open(kStTestRkeyBase));
+
+  EXPECT_EQ(S_OK, key_.RecurseDeleteSubKey(kStRkey1Name));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
+}
+
+TEST_F(RegKeyCleanupTestKeyTest, DeleteSubKey_NoChildren) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
+  EXPECT_SUCCEEDED(key_.Open(kStTestRkeyBase));
+
+  EXPECT_EQ(S_OK, key_.DeleteSubKey(kStRkey1Name));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
+}
+
+// Delete a key that has a child.
+
+TEST_F(RegKeyCleanupTestKeyTest, DeleteKey_WithChild_Recursively) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1Subkey));
+
+  EXPECT_EQ(S_OK, RegKey::DeleteKey(kStRkey1, true));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
+}
+
+// Deleting a key with children present results in ERROR_ACCESS_DENIED.
+TEST_F(RegKeyCleanupTestKeyTest, DeleteKey_WithChild_NotRecursively) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1Subkey));
+
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED),
+            RegKey::DeleteKey(kStRkey1, false));
+  EXPECT_TRUE(RegKey::HasKey(kStRkey1));
+  EXPECT_TRUE(RegKey::HasKey(kStRkey1Subkey));
+}
+
+TEST_F(RegKeyCleanupTestKeyTest, RecurseDeleteSubKey_WithChild) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1Subkey));
+  EXPECT_SUCCEEDED(key_.Open(kStTestRkeyBase));
+
+  EXPECT_EQ(S_OK, key_.RecurseDeleteSubKey(kStRkey1Name));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
+}
+
+// Deleting a key with children present results in ERROR_ACCESS_DENIED.
+TEST_F(RegKeyCleanupTestKeyTest, DeleteSubKey_WithChild) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1Subkey));
+  EXPECT_SUCCEEDED(key_.Open(kStTestRkeyBase));
+
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED),
+            key_.DeleteSubKey(kStRkey1Name));
+  EXPECT_TRUE(RegKey::HasKey(kStRkey1));
+  EXPECT_TRUE(RegKey::HasKey(kStRkey1Subkey));
+}
+
+// Delete a key that does not exist.
+
+TEST_F(RegKeyCleanupTestKeyTest, DeleteKey_KeyDoesNotExist_Recursively) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
+
+  EXPECT_EQ(S_FALSE, RegKey::DeleteKey(kStRkey1Subkey, true));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
+}
+
+TEST_F(RegKeyCleanupTestKeyTest, DeleteKey_KeyDoesNotExist_NotRecursively) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
+
+  EXPECT_EQ(S_FALSE, RegKey::DeleteKey(kStRkey1Subkey, false));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
+}
+
+TEST_F(RegKeyCleanupTestKeyTest, RecurseDeleteSubKey_KeyDoesNotExist) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
+  EXPECT_SUCCEEDED(key_.Open(kStRkey1));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
+
+  EXPECT_EQ(S_FALSE, key_.RecurseDeleteSubKey(kRkey1SubkeyName));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
+}
+
+TEST_F(RegKeyCleanupTestKeyTest, DeleteSubKey_KeyDoesNotExist) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
+  EXPECT_SUCCEEDED(key_.Open(kStRkey1));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
+
+  EXPECT_EQ(S_FALSE, key_.DeleteSubKey(kRkey1SubkeyName));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
+}
+
+// Delete a key whose parent does not exist.
+// There is no equivalent test for RecurseDeleteSubKey and DeleteSubKey.
+
+TEST_F(RegKeyCleanupTestKeyTest, DeleteKey_ParentKeyDoesNotExist_Recursively) {
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
+
+  EXPECT_EQ(S_FALSE, RegKey::DeleteKey(kStRkey1Subkey, true));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
+}
+
+TEST_F(RegKeyCleanupTestKeyTest,
+       DeleteKey_ParentKeyDoesNotExist_NotRecursively) {
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
+
+  EXPECT_EQ(S_FALSE, RegKey::DeleteKey(kStRkey1Subkey, false));
+  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
+}
+
+}  // namespace omaha
diff --git a/base/regexp.cc b/base/regexp.cc
new file mode 100644
index 0000000..ba071ca
--- /dev/null
+++ b/base/regexp.cc
@@ -0,0 +1,146 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/regexp.h"
+#include "omaha/base/debug.h"
+
+namespace omaha {
+
+#define kMaxArgs 16
+
+bool RE::PartialMatch(const TCHAR* text, const RE& re,  // 3..16 args
+                      CString * a0,
+                      CString * a1,
+                      CString * a2,
+                      CString * a3,
+                      CString * a4,
+                      CString * a5,
+                      CString * a6,
+                      CString * a7,
+                      CString * a8,
+                      CString * a9,
+                      CString * a10,
+                      CString * a11,
+                      CString * a12,
+                      CString * a13,
+                      CString * a14,
+                      CString * a15)
+{
+  ASSERT(text, (L""));
+  // a0 may be NULL
+  // a1 may be NULL
+  // a2 may be NULL
+  // a3 may be NULL
+  // a4 may be NULL
+  // a5 may be NULL
+  // a6 may be NULL
+  // a7 may be NULL
+  // a8 may be NULL
+  // a9 may be NULL
+  // a10 may be NULL
+  // a11 may be NULL
+  // a12 may be NULL
+  // a13 may be NULL
+  // a14 may be NULL
+  // a15 may be NULL
+
+  CString * args[kMaxArgs];
+  int n = 0;
+  if (a0 == NULL) goto done; args[n++] = a0;
+  if (a1 == NULL) goto done; args[n++] = a1;
+  if (a2 == NULL) goto done; args[n++] = a2;
+  if (a3 == NULL) goto done; args[n++] = a3;
+  if (a4 == NULL) goto done; args[n++] = a4;
+  if (a5 == NULL) goto done; args[n++] = a5;
+  if (a6 == NULL) goto done; args[n++] = a6;
+  if (a7 == NULL) goto done; args[n++] = a7;
+  if (a8 == NULL) goto done; args[n++] = a8;
+  if (a9 == NULL) goto done; args[n++] = a9;
+  if (a10 == NULL) goto done; args[n++] = a10;
+  if (a11 == NULL) goto done; args[n++] = a11;
+  if (a12 == NULL) goto done; args[n++] = a12;
+  if (a13 == NULL) goto done; args[n++] = a13;
+  if (a14 == NULL) goto done; args[n++] = a14;
+  if (a15 == NULL) goto done; args[n++] = a15;
+
+done:
+  return re.DoMatchImpl(text,args,n,NULL);
+}
+
+// Like PartialMatch(), except the "input" is advanced past the matched
+// text.  Note: "input" is modified iff this routine returns true.
+// For example, "FindAndConsume(s, "(\\w+)", &word)" finds the next
+// word in "s" and stores it in "word".
+bool RE::FindAndConsume(const TCHAR **input, const RE& re,
+                        CString * a0,
+                        CString * a1,
+                        CString * a2,
+                        CString * a3,
+                        CString * a4,
+                        CString * a5,
+                        CString * a6,
+                        CString * a7,
+                        CString * a8,
+                        CString * a9,
+                        CString * a10,
+                        CString * a11,
+                        CString * a12,
+                        CString * a13,
+                        CString * a14,
+                        CString * a15)
+{
+  ASSERT(input, (L""));
+  // a0 may be NULL
+  // a1 may be NULL
+  // a2 may be NULL
+  // a3 may be NULL
+  // a4 may be NULL
+  // a5 may be NULL
+  // a6 may be NULL
+  // a7 may be NULL
+  // a8 may be NULL
+  // a9 may be NULL
+  // a10 may be NULL
+  // a11 may be NULL
+  // a12 may be NULL
+  // a13 may be NULL
+  // a14 may be NULL
+  // a15 may be NULL
+
+  CString * args[kMaxArgs];
+  int n = 0;
+  if (a0 == NULL) goto done; args[n++] = a0;
+  if (a1 == NULL) goto done; args[n++] = a1;
+  if (a2 == NULL) goto done; args[n++] = a2;
+  if (a3 == NULL) goto done; args[n++] = a3;
+  if (a4 == NULL) goto done; args[n++] = a4;
+  if (a5 == NULL) goto done; args[n++] = a5;
+  if (a6 == NULL) goto done; args[n++] = a6;
+  if (a7 == NULL) goto done; args[n++] = a7;
+  if (a8 == NULL) goto done; args[n++] = a8;
+  if (a9 == NULL) goto done; args[n++] = a9;
+  if (a10 == NULL) goto done; args[n++] = a10;
+  if (a11 == NULL) goto done; args[n++] = a11;
+  if (a12 == NULL) goto done; args[n++] = a12;
+  if (a13 == NULL) goto done; args[n++] = a13;
+  if (a14 == NULL) goto done; args[n++] = a14;
+  if (a15 == NULL) goto done; args[n++] = a15;
+
+done:
+  return re.DoMatchImpl(*input,args,n,input);
+}
+
+}  // namespace omaha
+
diff --git a/common/regexp.h b/base/regexp.h
similarity index 100%
rename from common/regexp.h
rename to base/regexp.h
diff --git a/base/registry_hive.cc b/base/registry_hive.cc
new file mode 100644
index 0000000..006d4cf
--- /dev/null
+++ b/base/registry_hive.cc
@@ -0,0 +1,269 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/registry_hive.h"
+#include "omaha/base/accounts.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/string.h"
+#include "omaha/base/system.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+RegistryHive::RegistryHive()
+    : hive_holding_key_(NULL) {
+}
+
+RegistryHive::~RegistryHive() {
+  if ( !hive_name_.IsEmpty() ) {
+    UnloadHive();
+  }
+}
+
+// Loads hive for requested SID. SID should be in format "S-X-X....."
+// name is a name under which the hive will be added to the HKEY_USERS,
+// you could use persons name here. This parameter should not have '\\'
+// characters!
+// It is not recommended to load more than one hive at once - LoadHive,
+// manipulate hive, UnloadHive, and then work on the next one.
+//
+// Hive could be already loaded: you logged out from other user but system
+// had open key in your current user. In that case I open hive_holding_key_ to
+// prevent system from unloading hive and do not load/unload hive - the system
+// will do it.
+HRESULT RegistryHive::LoadHive(TCHAR const * sid, TCHAR const *name) {
+  ASSERT1(sid);
+  ASSERT1(name);
+  ASSERT1(hive_name_.IsEmpty());
+  ASSERT1(String_FindChar(name, _T('\\')) == -1);
+
+  CString profile_key;
+  CString hive_path;
+
+  // Need set SE_RESTORE_NAME/SE_BACKUP_NAME priveleges to current process
+  // otherwise loading of the hive will fail
+  RET_IF_FAILED(System::AdjustPrivilege(SE_RESTORE_NAME, true));
+  RET_IF_FAILED(System::AdjustPrivilege(SE_BACKUP_NAME, true));
+
+  SafeCStringFormat(&profile_key, kProfileKeyFormat, sid);
+
+  if ( FAILED(RegKey::GetValue(profile_key, kProfilePathValue, &hive_path)) ) {
+    return E_FAIL;
+  }
+
+
+  wchar_t temporary_buffer[MAX_PATH];
+  DWORD ret = ExpandEnvironmentStrings(hive_path, temporary_buffer, MAX_PATH);
+
+  if ( !ret || ret >= MAX_PATH ) {
+    return E_FAIL;
+  }
+  hive_path = temporary_buffer;
+
+  hive_path.Append(_T("\\"));
+  hive_path.Append(kHiveName);
+
+  hive_name_ = name;
+
+  LONG res = RegLoadKey(HKEY_USERS, hive_name_, hive_path);
+
+  if ( ERROR_SHARING_VIOLATION == res ) {
+    // It is quite possible that the hive is still held by system.
+    hive_name_ = sid;
+
+    // if it is the case, this call will succeeed, and the system will not
+    // unload the hive while there are outstanding keys opened.
+    res = RegOpenKeyEx(HKEY_USERS,
+                       hive_name_,
+                       0,
+                       KEY_ALL_ACCESS,
+                       &hive_holding_key_);
+  }
+
+  return (res == ERROR_SUCCESS) ? S_OK : E_FAIL;
+}
+
+// Loads hive for requested SID, but only if the SID is another user
+// (since we don't need to do anything if the sid is ours)
+HRESULT RegistryHive::LoadHive(TCHAR const * user_sid) {
+  ASSERT1(user_sid != NULL);
+  bool other_user = false;
+
+  // Determine if the SID passed in is really another user
+  CString current_user_sid;
+  HRESULT hr = omaha::user_info::GetProcessUser(NULL, NULL, &current_user_sid);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[RegistryHive::LoadHive - failed to get current user")));
+    return hr;
+  }
+
+  // user_sid is the current user - no need to load the hive
+  if (lstrcmpi(current_user_sid, user_sid) == 0)
+    return S_FALSE;
+
+  // Get info on the sid we're being asked to load for
+  CString name;
+  CString domain;
+  SID_NAME_USE user_type;
+  hr = accounts::GetUserInfo(user_sid, &name, &domain, &user_type);
+  if ( FAILED(hr) || user_type != SidTypeUser ) {
+    // Either Sid no longer exists or Sid is not a user Sid
+    // (There is other possibility: Sid could be for roaming profile on domain
+    // which is currently down, but we do not support roaming profiles)
+    return FAILED(hr) ? hr : E_FAIL;
+  }
+
+  hr = LoadHive(user_sid, name);  // Use user name as a temporary key name.
+  if ( FAILED(hr) ) {
+    // Hive no longer present.
+    return E_FAIL;
+  }
+
+  return S_OK;
+}
+
+
+// Unloads and saves loaded hive
+HRESULT RegistryHive::UnloadHive() {
+  if (hive_name_.IsEmpty())
+    return S_OK;
+
+  LONG res;
+  if ( hive_holding_key_ ) {
+    res = RegCloseKey(hive_holding_key_);
+    hive_holding_key_ = NULL;
+    // no need to unload hive. System will do it.
+  } else {
+    res = RegUnLoadKey(HKEY_USERS, hive_name_);
+  }
+  hive_name_.Empty();
+  return (res == ERROR_SUCCESS) ? S_OK : E_FAIL;
+}
+
+// Does it recursively. The name should be relative to HKEY_CURRENT_USER.
+HRESULT RegistryHive::DeleteUserKey(TCHAR const * key_name) {
+  ASSERT(key_name && *key_name, (L""));
+  if ( !key_name || !*key_name ) {
+    return E_FAIL;
+  }
+  CString key(key_name);
+  ExpandKeyName(&key);
+
+  if ( !RegKey::SafeKeyNameForDeletion(key) ) {
+    return E_FAIL;
+  }
+
+  return RegKey::DeleteKey(key);
+}
+
+void RegistryHive::ExpandKeyName(CString * str) {
+  ASSERT1(str);
+
+  // If we haven't loaded another user's hive, use HKCU instead of
+  // HKEY_USERS
+  CString key_name;
+  if (hive_name_.IsEmpty()) {
+    key_name = _T("HKCU\\");
+  } else {
+    key_name = _T("HKEY_USERS\\");
+    key_name.Append(hive_name_ + _T("\\"));
+  }
+
+  key_name.Append(*str);
+  *str = key_name;
+}
+
+// Load a user registry
+int LoadUserRegistry(const TCHAR* user_sid,
+                     ProcessUserRegistryFunc* handler,
+                     LONG_PTR param) {
+  ASSERT1(user_sid && *user_sid);
+  ASSERT1(handler);
+
+  // Get current user SID
+  CString curr_user_sid;
+  HRESULT hr = omaha::user_info::GetProcessUser(NULL, NULL, &curr_user_sid);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[LoadUserRegistry - can't get current user][0x%x]"), hr));
+    return 0;
+  }
+
+  // Is current user?
+  bool other_user = curr_user_sid.CompareNoCase(user_sid) != 0;
+
+  // Get the hive for this user
+  RegistryHive user_hive;
+  if (other_user) {
+    // Get the info about this user
+    SID_NAME_USE user_type = SidTypeInvalid;
+    CString name, domain;
+    hr = accounts::GetUserInfo(user_sid, &name, &domain, &user_type);
+    if (FAILED(hr) || user_type != SidTypeUser) {
+      // Either SID no longer exists or SID is not a user Sid
+      // (There is other possibility: SID could be for roaming profile on domain
+      // which is currently down, but we do not support roaming profiles)
+      UTIL_LOG(LEVEL_WARNING,
+               (_T("[LoadUserRegistry - SID %s invalid or unsupported][0x%x]"),
+                user_sid, hr));
+      return 0;
+    }
+
+    // Load the hive
+    hr = user_hive.LoadHive(user_sid, domain + _T("_") + name);
+    if (FAILED(hr)) {
+      // Hive no longer present.
+      UTIL_LOG(LW, (_T("[LoadUserRegistry]")
+                    _T("[hive not present for %s][0x%x]"), user_sid, hr));
+      return 0;
+    }
+  }
+
+  // Get the registry key path
+  CString user_reg_path;
+  user_hive.ExpandKeyName(&user_reg_path);
+
+  // Call the handler
+  int res = (*handler)(user_sid, user_reg_path, param);
+
+  // Unload the hive
+  if (other_user) {
+    hr = user_hive.UnloadHive();
+    if (FAILED(hr)) {
+      UTIL_LOG(LE, (_T("[LoadUserRegistry]")
+                    _T("[failed to save hive for %s][0x%x]"), user_sid, hr));
+    }
+  }
+
+  return res;
+}
+
+// Enumerate all user registries
+void EnumerateAllUserRegistries(ProcessUserRegistryFunc* handler,
+                                LONG_PTR param) {
+  ASSERT1(handler);
+
+  CSimpleArray<CString> sid_array;
+  accounts::GetAllUserSids(&sid_array);
+  for (int i = 0 ; i < sid_array.GetSize() ; ++i) {
+    if (LoadUserRegistry(sid_array[i], handler, param)) {
+      return;
+    }
+  }
+}
+
+}  // namespace omaha
+
diff --git a/common/registry_hive.h b/base/registry_hive.h
similarity index 100%
rename from common/registry_hive.h
rename to base/registry_hive.h
diff --git a/base/registry_monitor_manager.cc b/base/registry_monitor_manager.cc
new file mode 100644
index 0000000..e57d7de
--- /dev/null
+++ b/base/registry_monitor_manager.cc
@@ -0,0 +1,586 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// RegistryMonitor creates a KeyWatcher for every unique registry key that
+// contains a value registered by MonitorValue. Each KeyWatcher is responsible
+// for monitoring one or more values in a single registry key but not its
+// subkeys. RegistryMonitor manages a thread which waits on event objects.
+// The events are signaled when the corresponding monitored key changes.
+
+#include "omaha/base/registry_monitor_manager.h"
+#include <atlbase.h>
+#include <utility>
+#include <vector>
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/thread.h"
+
+namespace omaha {
+
+namespace detail {
+
+// converts a registry change type value to a string for logging purposes.
+CString RegistryChangeTypeToString(RegistryChangeType registry_change_type) {
+  switch (registry_change_type) {
+  case REGISTRY_CHANGE_TYPE_CREATE:
+    return _T("create");
+  case REGISTRY_CHANGE_TYPE_UPDATE:
+    return _T("update");
+  case REGISTRY_CHANGE_TYPE_DELETE:
+    return _T("delete");
+  default:
+    ASSERT1(false);
+    return _T("unknown");
+  }
+};
+
+// Holds a pair of root key and sub key, as monitoring must be unique for
+// each pair.
+class KeyId {
+ public:
+  KeyId(HKEY parent_key, const CString& key_name)
+      : parent_key_(parent_key), key_name_(key_name) {}
+
+  HKEY parent_key() const { return parent_key_; }
+  CString key_name() const { return key_name_; }
+
+  static bool IsEqual(const KeyId& id1, const KeyId& id2) {
+    return id1.parent_key_ == id2.parent_key_ &&
+           id1.key_name_   == id2.key_name_;
+  }
+ private:
+  HKEY    parent_key_;
+  CString key_name_;
+};
+
+class KeyWatcher;
+
+// ValueWatcher represents a single monitored registry value.
+// It is used by KeyWatcher to determine which registry value has changed when
+// it detects a change in its key.
+class ValueWatcher {
+ public:
+  ValueWatcher(KeyWatcher* key_watcher,
+               const CString& value_name,
+               int value_type,
+               RegistryValueChangeCallback callback,
+               void* user_data);
+  ~ValueWatcher();
+
+  // Returns true if the initial value has changed.
+  bool HasChanged();
+
+  // Calls the callback function to do the notification of the change.
+  void DoCallback();
+
+ private:
+  CString GetCurrentValueString();
+  DWORD   GetCurrentValueDword();
+
+  CString last_known_value_string_;
+  DWORD last_known_value_dword_;
+  bool value_is_valid_;
+  RegistryChangeType change_type_;
+  CString value_name_;
+  int value_type_;
+  KeyWatcher* key_watcher_;
+  RegistryValueChangeCallback callback_;
+  void* callback_param_;
+};
+
+
+// KeyWatcher is responsible for monitoring changes to a single key in the
+// Windows registry. RegistryMonitor keeps a container of KeyWatcher objects,
+// one object for each key that contains a value to be monitored.
+class KeyWatcher {
+ public:
+  explicit KeyWatcher(const KeyId& key_id);
+
+  ~KeyWatcher();
+
+  // Adds a new registry value to monitor.
+  HRESULT AddValue(const CString& value_name,
+                   int value_type,
+                   RegistryValueChangeCallback callback,
+                   void* user_data);
+
+  // Registers the key watcher with the OS and gets ready to receive events.
+  HRESULT StartWatching();
+
+  // Returns true if the underlying registry handle corresponds to a valid key.
+  bool IsKeyValid();
+
+  HANDLE notification_event() const { return get(notification_event_); }
+
+  RegKey& key() { return key_; }
+
+  CString key_name() const { return key_id_.key_name(); }
+
+  void set_callback(RegistryKeyChangeCallback callback, void* callback_param) {
+    callback_       = callback;
+    callback_param_ = callback_param;
+  }
+
+  // Callback called when the notification event is signaled by the OS
+  // as a result of a change in the monitored key.
+  void HandleEvent(HANDLE handle);
+
+ private:
+  // Ensures the key to monitor is always open.
+  HRESULT EnsureOpen();
+
+  std::vector<ValueWatcher*> values_;
+  RegKey key_;
+  const KeyId key_id_;
+  scoped_event notification_event_;
+
+  RegistryKeyChangeCallback callback_;
+  void* callback_param_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(KeyWatcher);
+};
+
+class RegistryMonitorImpl : public Runnable {
+ public:
+  RegistryMonitorImpl();
+  ~RegistryMonitorImpl();
+
+  HRESULT MonitorKey(HKEY root_key,
+                     const CString& sub_key,
+                     RegistryKeyChangeCallback callback,
+                     void* user_data);
+
+  HRESULT MonitorValue(HKEY root_key,
+                       const CString& sub_key,
+                       const CString& value_name,
+                       int value_type,
+                       RegistryValueChangeCallback callback,
+                       void* user_data);
+
+  HRESULT Initialize();
+
+  HRESULT StartMonitoring();
+
+ private:
+
+  // Runnable.
+  virtual void Run();
+
+  typedef std::pair<KeyId, KeyWatcher*> Watcher;
+  std::vector<Watcher> watchers_;
+
+  Thread thread_;
+  scoped_ptr<Gate> start_monitoring_gate_;
+  scoped_event stop_monitoring_;
+  DISALLOW_EVIL_CONSTRUCTORS(RegistryMonitorImpl);
+};
+
+
+ValueWatcher::ValueWatcher(KeyWatcher* key_watcher,
+                           const CString &value_name,
+                           int value_type,
+                           RegistryValueChangeCallback callback,
+                           void* user_data)
+    : key_watcher_(key_watcher),
+      value_is_valid_(false),
+      change_type_(REGISTRY_CHANGE_TYPE_CREATE),
+      callback_(callback),
+      callback_param_(user_data),
+      value_name_(value_name),
+      value_type_(value_type),
+      last_known_value_dword_(0) {
+  ASSERT1(key_watcher);
+  ASSERT1(callback);
+  if (value_type_ == REG_SZ) {
+    last_known_value_string_ = GetCurrentValueString();
+  } else if (value_type_ == REG_DWORD) {
+    last_known_value_dword_ = GetCurrentValueDword();
+  } else {
+    ASSERT(false, (_T("value type not supported")));
+  }
+}
+
+ValueWatcher::~ValueWatcher() {
+}
+
+bool ValueWatcher::HasChanged() {
+  UTIL_LOG(L3, (_T("[ValueWatcher::HasChanged]")
+                _T("[key name '%s'][value '%s'][valid %d]"),
+                key_watcher_->key_name(), value_name_, value_is_valid_));
+
+  const bool value_was_valid = value_is_valid_;
+
+  bool has_changed = false;
+  if (value_type_ == REG_SZ) {
+    CString new_value = GetCurrentValueString();
+    has_changed = last_known_value_string_ != new_value;
+
+    UTIL_LOG(L3, (_T("[ValueWatcher::HasChanged][old value %s][new value %s]"),
+                  last_known_value_string_, new_value));
+
+    last_known_value_string_ = new_value;
+  } else if (value_type_ == REG_DWORD) {
+    DWORD new_value = GetCurrentValueDword();
+    has_changed = last_known_value_dword_ != new_value;
+
+    UTIL_LOG(L3, (_T("[ValueWatcher::HasChanged][old value %d][new value %d]"),
+                  last_known_value_dword_, new_value));
+
+    last_known_value_dword_ = new_value;
+  } else {
+    ASSERT(false, (_T("value type not supported")));
+  }
+
+  // Detect the type of the change based on previous and current value state.
+  if (value_was_valid && value_is_valid_) {
+    change_type_ = REGISTRY_CHANGE_TYPE_UPDATE;
+  } else if (value_was_valid && !value_is_valid_) {
+    change_type_ = REGISTRY_CHANGE_TYPE_DELETE;
+  } else if (!value_was_valid && value_is_valid_) {
+    change_type_ = REGISTRY_CHANGE_TYPE_CREATE;
+  } else {
+    ASSERT1(!value_was_valid && !value_is_valid_);
+  }
+
+  if (has_changed) {
+    UTIL_LOG(L3, (_T("[ValueWatcher::HasChanged]")
+                  _T("[key name '%s'][value '%s' has changed][%s]"),
+                  key_watcher_->key_name(), value_name_,
+                  RegistryChangeTypeToString(change_type_)));
+  } else {
+    UTIL_LOG(L3, (_T("[ValueWatcher::HasChanged]")
+                  _T("[key name '%s'][value '%s' is the same]"),
+                  key_watcher_->key_name(), value_name_));
+  }
+
+  return has_changed;
+}
+
+CString ValueWatcher::GetCurrentValueString() {
+  CString value_data;
+  RegKey& key = key_watcher_->key();
+  ASSERT1(key.Key());
+  value_is_valid_ = SUCCEEDED(key.GetValue(value_name_, &value_data));
+  return value_is_valid_ ? value_data : CString();
+}
+
+DWORD ValueWatcher::GetCurrentValueDword() {
+  DWORD value_data = 0;
+  RegKey& key = key_watcher_->key();
+  ASSERT1(key.Key());
+  value_is_valid_ = SUCCEEDED(key.GetValue(value_name_, &value_data));
+  return value_is_valid_ ? value_data : static_cast<DWORD>(-1);
+}
+
+void ValueWatcher::DoCallback() {
+  ASSERT1(callback_ != NULL);
+
+  const void* value = NULL;
+  if (value_type_ == REG_SZ) {
+    value = static_cast<const TCHAR*>(last_known_value_string_);
+  } else if (value_type_ == REG_DWORD) {
+    value = reinterpret_cast<void*>(last_known_value_dword_);
+  }
+
+  callback_(key_watcher_->key_name(), value_name_, change_type_,
+            value, callback_param_);
+
+  // If value was not valid, for example, the key was deleted or renamed, and
+  // it is valid after callback, update last known with the current value.
+  if (!value_is_valid_) {
+    if (value_type_ == REG_SZ) {
+      CString new_value = GetCurrentValueString();
+      if (value_is_valid_) {
+        last_known_value_string_ = new_value;
+      }
+    } else if (value_type_ == REG_DWORD) {
+      DWORD new_value = GetCurrentValueDword();
+      if (value_is_valid_) {
+        last_known_value_dword_ = new_value;
+      }
+    }
+  }
+}
+
+KeyWatcher::KeyWatcher(const KeyId& key_id)
+    : key_id_(key_id),
+      notification_event_(::CreateEvent(NULL, false, false, NULL)),
+      callback_(NULL),
+      callback_param_(NULL) {
+}
+
+KeyWatcher::~KeyWatcher() {
+  for (size_t i = 0; i != values_.size(); ++i) {
+    delete values_[i];
+  }
+}
+
+HRESULT KeyWatcher::StartWatching() {
+  // By this time the key could be deleted or renamed. Check if the handle
+  // is still valid and reopen the key if needed.
+  HRESULT hr = EnsureOpen();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  ASSERT1(key_.Key());
+  const DWORD kNotifyFilter = REG_NOTIFY_CHANGE_NAME          |
+                              REG_NOTIFY_CHANGE_ATTRIBUTES    |
+                              REG_NOTIFY_CHANGE_LAST_SET      |
+                              REG_NOTIFY_CHANGE_SECURITY;
+  LONG result = ::RegNotifyChangeKeyValue(key_.Key(), false, kNotifyFilter,
+                                          get(notification_event_), true);
+  UTIL_LOG(L3, (_T("[KeyWatcher::StartWatching][key '%s' %s]"),
+                key_id_.key_name(),
+                result == ERROR_SUCCESS ? _T("ok") : _T("failed")));
+  return HRESULT_FROM_WIN32(result);
+}
+
+HRESULT KeyWatcher::AddValue(const CString& value_name,
+                             int value_type,
+                             RegistryValueChangeCallback callback,
+                             void* user_data) {
+  ASSERT1(callback);
+  HRESULT hr = EnsureOpen();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  values_.push_back(
+      new ValueWatcher(this, value_name, value_type, callback, user_data));
+  return S_OK;
+}
+
+void KeyWatcher::HandleEvent(HANDLE handle) {
+  UTIL_LOG(L3, (_T("[KeyWatcher::HandleEvent][key '%s']"), key_id_.key_name()));
+
+  ASSERT1(handle);
+  ASSERT1(handle == get(notification_event_));
+  UNREFERENCED_PARAMETER(handle);
+
+  // Although not documented, it seems the OS pulses the event so the event
+  // is never signaled at this point.
+  ASSERT1(::WaitForSingleObject(handle, 0) == WAIT_TIMEOUT);
+
+  // Notify the key has changed.
+  if (callback_) {
+    callback_(key_name(), callback_param_);
+  }
+
+  // Notify the values have changed.
+  for (size_t i = 0; i != values_.size(); ++i) {
+    ValueWatcher* value = values_[i];
+    if (value != NULL) {
+      if (value->HasChanged()) {
+        value->DoCallback();
+      }
+    }
+  }
+
+  VERIFY1(SUCCEEDED(StartWatching()));
+}
+
+HRESULT KeyWatcher::EnsureOpen() {
+  // Close the key if it is not valid for whatever reasons, such as it was
+  // deleted and recreated back.
+  if (!IsKeyValid()) {
+    UTIL_LOG(L3, (_T("[key '%s' is not valid]"), key_id_.key_name()));
+    VERIFY1(SUCCEEDED(key_.Close()));
+  }
+
+  // Open the key if not already open or create the key if needed.
+  HRESULT hr = S_OK;
+  if (!key_.Key()) {
+    hr = key_.Create(key_id_.parent_key(), key_id_.key_name());
+    if (SUCCEEDED(hr)) {
+      UTIL_LOG(L3, (_T("[key '%s' has been created]"), key_id_.key_name()));
+    }
+  }
+  return hr;
+}
+
+bool KeyWatcher::IsKeyValid() {
+  if (!key_.Key()) {
+    return false;
+  }
+  LONG ret = RegQueryInfoKey(key_.Key(),
+                             NULL, NULL, NULL, NULL, NULL,
+                             NULL, NULL, NULL, NULL, NULL, NULL);
+  return ret == ERROR_SUCCESS;
+}
+
+RegistryMonitorImpl::RegistryMonitorImpl() {
+}
+
+RegistryMonitorImpl::~RegistryMonitorImpl() {
+  if (stop_monitoring_) {
+    VERIFY1(::SetEvent(get(stop_monitoring_)));
+  }
+  VERIFY1(thread_.WaitTillExit(INFINITE));
+
+  for (size_t i = 0; i != watchers_.size(); ++i) {
+    ASSERT1(watchers_[i].second);
+    delete watchers_[i].second;
+  }
+}
+
+HRESULT RegistryMonitorImpl::Initialize() {
+  reset(stop_monitoring_, ::CreateEvent(NULL, true, false, NULL));
+  if (!stop_monitoring_) {
+    return HRESULTFromLastError();
+  }
+  return S_OK;
+}
+
+HRESULT RegistryMonitorImpl::MonitorKey(HKEY root_key,
+                                        const CString& sub_key,
+                                        RegistryKeyChangeCallback callback,
+                                        void* user_data) {
+  ASSERT1(callback);
+  ASSERT1(!thread_.Running());
+
+  KeyId key_id(root_key, sub_key);
+  for (size_t i = 0; i != watchers_.size(); ++i) {
+    if (KeyId::IsEqual(watchers_[i].first, key_id)) {
+      watchers_[i].second->set_callback(callback, user_data);
+      return S_OK;
+    }
+  }
+  scoped_ptr<KeyWatcher> key_watcher(new KeyWatcher(key_id));
+  key_watcher->set_callback(callback, user_data);
+  Watcher watcher(key_id, key_watcher.release());
+  watchers_.push_back(watcher);
+  return S_OK;
+}
+
+HRESULT RegistryMonitorImpl::MonitorValue(
+    HKEY root_key, const CString& sub_key, const CString& value_name,
+    int value_type, RegistryValueChangeCallback callback, void* user_data) {
+  ASSERT1(callback);
+  ASSERT1(!thread_.Running());
+
+  // Reuse an existing key watcher if there is a value already registered
+  // for monitoring under the respective registry key.
+  KeyId key_id(root_key, sub_key);
+  for (size_t i = 0; i != watchers_.size(); ++i) {
+    if (KeyId::IsEqual(watchers_[i].first, key_id)) {
+      return watchers_[i].second->AddValue(value_name, value_type,
+                                           callback, user_data);
+    }
+  }
+  scoped_ptr<KeyWatcher> key_watcher(new KeyWatcher(key_id));
+  HRESULT hr = key_watcher->AddValue(value_name, value_type,
+                                     callback, user_data);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[RegistryMonitorImpl::RegisterValue failed]")
+                  _T("[key %s][value %s][0x%x]"), sub_key, value_name, hr));
+    return hr;
+  }
+  Watcher watcher(key_id, key_watcher.release());
+  watchers_.push_back(watcher);
+  return S_OK;
+}
+
+HRESULT RegistryMonitorImpl::StartMonitoring() {
+  // Starts the thread and waits on the gate for the thread to open after
+  // it has registered all watchers for notifications and it is ready to
+  // handle notification events. The gate is only needed to synchronize the
+  // caller and the monitoring threads.
+  start_monitoring_gate_.reset(new Gate);
+  if (!thread_.Start(this)) {
+    return E_FAIL;
+  }
+  bool wait_result = start_monitoring_gate_->Wait(INFINITE);
+  start_monitoring_gate_.reset();
+  ASSERT1(wait_result);
+  return wait_result ? S_OK : HRESULTFromLastError();
+}
+
+void RegistryMonitorImpl::Run() {
+  UTIL_LOG(L3, (_T("[started monitoring registry]")));
+
+  const size_t kNumNotificationHandles = watchers_.size();
+  const size_t kNumHandles = kNumNotificationHandles + 1;
+  const size_t kStopMonitoringHandleIndex = kNumNotificationHandles;
+
+  scoped_array<HANDLE> handles(new HANDLE[kNumHandles]);
+  for (size_t i = 0; i != watchers_.size(); ++i) {
+    handles[i] = watchers_[i].second->notification_event();
+    VERIFY1(SUCCEEDED(watchers_[i].second->StartWatching()));
+  }
+  handles[kStopMonitoringHandleIndex] = get(stop_monitoring_);
+
+  // Open the gate and allow the RegistryMonitor::StartMonitoring call to
+  // to return to the caller.
+  ASSERT1(start_monitoring_gate_.get());
+  VERIFY1(start_monitoring_gate_->Open());
+
+  for (;;) {
+    DWORD result = ::WaitForMultipleObjects(kNumHandles,
+                                            handles.get(),
+                                            false,
+                                            INFINITE);
+    COMPILE_ASSERT(0 == WAIT_OBJECT_0, invalid_wait_object_0);
+    ASSERT1(result < kNumHandles);
+    if (result < kNumHandles) {
+      if (result == kStopMonitoringHandleIndex) {
+        break;
+      } else {
+        size_t i = result - WAIT_OBJECT_0;
+        watchers_[i].second->HandleEvent(handles[i]);
+      }
+    }
+  }
+  UTIL_LOG(L3, (_T("[stopped monitoring registry]")));
+}
+
+}  // namespace detail
+
+RegistryMonitor::RegistryMonitor()
+    : impl_(new detail::RegistryMonitorImpl) {
+}
+
+RegistryMonitor::~RegistryMonitor() {
+}
+
+HRESULT RegistryMonitor::MonitorKey(HKEY root_key,
+                                    const CString& sub_key,
+                                    RegistryKeyChangeCallback callback,
+                                    void* user_data) {
+  return impl_->MonitorKey(root_key, sub_key, callback, user_data);
+}
+
+HRESULT RegistryMonitor::MonitorValue(HKEY root_key,
+                                      const CString& sub_key,
+                                      const CString& value_name,
+                                      int value_type,
+                                      RegistryValueChangeCallback callback,
+                                      void* user_data) {
+  return impl_->MonitorValue(root_key, sub_key, value_name, value_type,
+                             callback, user_data);
+}
+
+HRESULT RegistryMonitor::Initialize() {
+  return impl_->Initialize();
+}
+
+HRESULT RegistryMonitor::StartMonitoring() {
+  return impl_->StartMonitoring();
+}
+
+}  // namespace omaha
+
diff --git a/common/registry_monitor_manager.h b/base/registry_monitor_manager.h
similarity index 100%
rename from common/registry_monitor_manager.h
rename to base/registry_monitor_manager.h
diff --git a/base/registry_monitor_manager_unittest.cc b/base/registry_monitor_manager_unittest.cc
new file mode 100644
index 0000000..4d76705
--- /dev/null
+++ b/base/registry_monitor_manager_unittest.cc
@@ -0,0 +1,235 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <windows.h>
+#include <limits.h>
+#include "base/basictypes.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/reactor.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/registry_monitor_manager.h"
+#include "omaha/base/thread.h"
+#include "omaha/base/utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+const TCHAR kKeyNameFull[] = _T("HKCU\\key");
+const TCHAR kKeyName[]     = _T("key");
+const TCHAR kValueName[]   = _T("value");
+
+}  // namespace
+
+class RegistryMonitorTest : public testing::Test {
+ protected:
+  RegistryMonitorTest() {}
+
+  virtual void SetUp() {
+    // Override HKCU.
+    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
+    OverrideSpecifiedRegistryHives(kRegistryHiveOverrideRoot, false, true);
+    reset(registry_changed_event_, ::CreateEvent(NULL, true, false, NULL));
+  }
+
+  virtual void TearDown() {
+    reset(registry_changed_event_);
+    RestoreRegistryHives();
+    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
+  }
+
+  static void RegistryDeleteCallback(const TCHAR* key_name,
+                                     const TCHAR* value_name,
+                                     RegistryChangeType change_type,
+                                     const void* new_value_data,
+                                     void* user_data) {
+    EXPECT_STREQ(kKeyName, key_name);
+    EXPECT_STREQ(kValueName, value_name);
+    EXPECT_EQ(REGISTRY_CHANGE_TYPE_DELETE, change_type);
+    EXPECT_TRUE(new_value_data);
+    EXPECT_TRUE(user_data);
+    RegistryMonitorTest* object = static_cast<RegistryMonitorTest*>(user_data);
+    DWORD actual_value = reinterpret_cast<DWORD>(new_value_data);
+    EXPECT_EQ(ULONG_MAX, actual_value);
+    EXPECT_TRUE(::SetEvent(get(object->registry_changed_event_)));
+  }
+
+  static void RegistryChangeCallback(const TCHAR* key_name,
+                                     const TCHAR* value_name,
+                                     RegistryChangeType change_type,
+                                     const void* new_value_data,
+                                     void* user_data) {
+    EXPECT_STREQ(kKeyName, key_name);
+    EXPECT_STREQ(kValueName, value_name);
+    EXPECT_EQ(REGISTRY_CHANGE_TYPE_UPDATE, change_type);
+    EXPECT_TRUE(new_value_data);
+    EXPECT_TRUE(user_data);
+    RegistryMonitorTest* object = static_cast<RegistryMonitorTest*>(user_data);
+    const TCHAR* actual_value = static_cast<const TCHAR*>(new_value_data);
+    EXPECT_STREQ(_T("foo"), actual_value);
+    EXPECT_TRUE(::SetEvent(get(object->registry_changed_event_)));
+  }
+
+  static void RegistryChangesCallback(const TCHAR* key_name,
+                                     const TCHAR* value_name,
+                                     RegistryChangeType change_type,
+                                     const void* new_value_data,
+                                     void* user_data) {
+    EXPECT_STREQ(kKeyName, key_name);
+    EXPECT_STREQ(kValueName, value_name);
+    EXPECT_EQ(REGISTRY_CHANGE_TYPE_UPDATE, change_type);
+    EXPECT_TRUE(new_value_data);
+    EXPECT_TRUE(user_data);
+    RegistryMonitorTest* object = static_cast<RegistryMonitorTest*>(user_data);
+    EXPECT_TRUE(::SetEvent(get(object->registry_changed_event_)));
+  }
+
+  static void RegistryCreateCallback(const TCHAR* key_name,
+                                     const TCHAR* value_name,
+                                     RegistryChangeType change_type,
+                                     const void* new_value_data,
+                                     void* user_data) {
+    EXPECT_STREQ(kKeyName, key_name);
+    EXPECT_STREQ(kValueName, value_name);
+    EXPECT_EQ(REGISTRY_CHANGE_TYPE_CREATE, change_type);
+    EXPECT_TRUE(new_value_data);
+    EXPECT_TRUE(user_data);
+    RegistryMonitorTest* object = static_cast<RegistryMonitorTest*>(user_data);
+    DWORD actual_value = reinterpret_cast<DWORD>(new_value_data);
+    EXPECT_EQ(1, actual_value);
+    EXPECT_TRUE(::SetEvent(get(object->registry_changed_event_)));
+  }
+
+  static void RegistryKeyCallback(const TCHAR* key_name, void* user_data) {
+    EXPECT_STREQ(kKeyName, key_name);
+    RegistryMonitorTest* object = static_cast<RegistryMonitorTest*>(user_data);
+    EXPECT_TRUE(::SetEvent(get(object->registry_changed_event_)));
+  }
+
+  scoped_event registry_changed_event_;
+
+  static DWORD const kWaitForChangeMs = 5000;
+};
+
+TEST_F(RegistryMonitorTest, DeleteValue) {
+  DWORD value = 0;
+  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(kKeyNameFull, kValueName, value));
+  RegistryMonitor registry_monitor;
+  ASSERT_HRESULT_SUCCEEDED(registry_monitor.Initialize());
+  ASSERT_HRESULT_SUCCEEDED(registry_monitor.MonitorValue(
+      HKEY_CURRENT_USER, kKeyName, kValueName, REG_DWORD,
+      RegistryDeleteCallback, this));
+  ASSERT_HRESULT_SUCCEEDED(registry_monitor.StartMonitoring());
+
+  EXPECT_HRESULT_SUCCEEDED(RegKey::DeleteValue(kKeyNameFull, kValueName));
+  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(registry_changed_event_),
+                                                 kWaitForChangeMs));
+}
+
+TEST_F(RegistryMonitorTest, ChangeValue) {
+  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(kKeyNameFull, kValueName, _T("")));
+  RegistryMonitor registry_monitor;
+  ASSERT_HRESULT_SUCCEEDED(registry_monitor.Initialize());
+  ASSERT_HRESULT_SUCCEEDED(registry_monitor.MonitorValue(
+    HKEY_CURRENT_USER, kKeyName, kValueName, REG_SZ,
+    RegistryChangeCallback, this));
+  ASSERT_HRESULT_SUCCEEDED(registry_monitor.StartMonitoring());
+
+  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(kKeyNameFull,
+                                            kValueName,
+                                            _T("foo")));
+  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(registry_changed_event_),
+                                                 kWaitForChangeMs));
+}
+
+// Tests changing the same value two times. This is useful to detect if
+// the key is registered back for notification after a succesful callback.
+TEST_F(RegistryMonitorTest, ChangeValues) {
+  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(kKeyNameFull,
+                                            kValueName,
+                                            _T("")));
+
+  RegistryMonitor registry_monitor;
+  ASSERT_HRESULT_SUCCEEDED(registry_monitor.Initialize());
+  ASSERT_HRESULT_SUCCEEDED(registry_monitor.MonitorValue(
+      HKEY_CURRENT_USER, kKeyName, kValueName, REG_SZ,
+      RegistryChangesCallback, this));
+  ASSERT_HRESULT_SUCCEEDED(registry_monitor.StartMonitoring());
+
+  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(kKeyNameFull,
+                                            kValueName,
+                                            _T("foo")));
+  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(registry_changed_event_),
+                                                 kWaitForChangeMs));
+  EXPECT_TRUE(::ResetEvent(get(registry_changed_event_)));
+
+  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(kKeyNameFull,
+                                            kValueName,
+                                            _T("bar")));
+  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(registry_changed_event_),
+                                                 kWaitForChangeMs));
+}
+
+TEST_F(RegistryMonitorTest, CreateValue) {
+  RegistryMonitor registry_monitor;
+  ASSERT_HRESULT_SUCCEEDED(registry_monitor.Initialize());
+  ASSERT_HRESULT_SUCCEEDED(RegKey::CreateKey(kKeyNameFull));
+  ASSERT_HRESULT_SUCCEEDED(registry_monitor.MonitorValue(
+      HKEY_CURRENT_USER, kKeyName, kValueName, REG_DWORD,
+      RegistryCreateCallback, this));
+  ASSERT_HRESULT_SUCCEEDED(registry_monitor.StartMonitoring());
+
+  DWORD value = 1;
+  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(kKeyNameFull, kValueName, value));
+  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(registry_changed_event_),
+                                                 kWaitForChangeMs));
+}
+
+// Monitoring values under the same key pair is allowed.
+TEST_F(RegistryMonitorTest, MonitorSame) {
+  RegistryMonitor registry_monitor;
+  ASSERT_HRESULT_SUCCEEDED(registry_monitor.Initialize());
+  ASSERT_HRESULT_SUCCEEDED(RegKey::CreateKey(kKeyNameFull));
+  ASSERT_HRESULT_SUCCEEDED(registry_monitor.MonitorValue(
+      HKEY_CURRENT_USER, kKeyName, kValueName, REG_DWORD,
+      RegistryCreateCallback, this));
+  ASSERT_HRESULT_SUCCEEDED(registry_monitor.MonitorValue(
+      HKEY_CURRENT_USER, kKeyName, kValueName, REG_DWORD,
+      RegistryCreateCallback, this));
+}
+
+TEST_F(RegistryMonitorTest, MonitorKey) {
+  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey(kKeyNameFull));
+
+  RegistryMonitor registry_monitor;
+  EXPECT_HRESULT_SUCCEEDED(registry_monitor.Initialize());
+  EXPECT_HRESULT_SUCCEEDED(registry_monitor.MonitorKey(
+      HKEY_CURRENT_USER, kKeyName, RegistryKeyCallback, this));
+
+  EXPECT_HRESULT_SUCCEEDED(registry_monitor.StartMonitoring());
+
+  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey(_T("HKCU\\key\\subkey")));
+  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(registry_changed_event_),
+                                                 kWaitForChangeMs));
+
+  EXPECT_TRUE(::ResetEvent(get(registry_changed_event_)));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::DeleteKey(_T("HKCU\\key\\subkey")));
+  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(registry_changed_event_),
+                                                 kWaitForChangeMs));
+}
+
+}  // namespace omaha
+
diff --git a/base/registry_store.cc b/base/registry_store.cc
new file mode 100644
index 0000000..ccfb6a3
--- /dev/null
+++ b/base/registry_store.cc
@@ -0,0 +1,115 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/registry_store.h"
+#include <vector>
+#include "omaha/base/debug.h"
+#include "omaha/base/reg_key.h"
+
+namespace omaha {
+
+bool RegistryStore::Open(const TCHAR* key_path) {
+  key_path_ = key_path;
+  return true;
+}
+
+bool RegistryStore::Close() {
+  key_path_.Empty();
+  return true;
+}
+
+bool RegistryStore::Clear() {
+  if (RegKey::HasKey(key_path_)) {
+    return SUCCEEDED(RegKey::DeleteKey(key_path_, false));
+  } else {
+    return true;
+  }
+}
+
+bool RegistryStore::Read(const TCHAR* name, std::vector<byte>* data) const {
+  ASSERT1(name);
+  ASSERT1(data);
+
+  byte* sdata = NULL;
+  DWORD sdata_size = 0;
+  HRESULT hr = RegKey::GetValue(key_path_, name, &sdata, &sdata_size);
+  if (FAILED(hr) || !sdata || !sdata_size)
+    return false;
+
+  data->resize(sdata_size);
+  memcpy(&data->front(), sdata, sdata_size);
+
+  delete[] sdata;
+
+  return true;
+}
+
+bool RegistryStore::Write(const TCHAR* name, byte* data, int data_size) {
+  ASSERT1(name);
+  ASSERT1(data);
+  ASSERT1(data_size);
+
+  return SUCCEEDED(RegKey::SetValue(key_path_, name, data, data_size));
+}
+
+bool RegistryStore::Exists(const TCHAR* name) {
+  ASSERT1(name);
+
+  return RegKey::HasValue(key_path_, name);
+}
+
+bool RegistryStore::Remove(const TCHAR* name) {
+  ASSERT1(name);
+
+  return SUCCEEDED(RegKey::DeleteValue(key_path_, name));
+}
+
+bool RegistryStore::GetValueCount(uint32* value_count) {
+  ASSERT1(value_count);
+
+  CString key_name(key_path_);
+  HKEY h_key = RegKey::GetRootKeyInfo(&key_name);
+
+  RegKey reg_key;
+  if (FAILED(reg_key.Open(h_key, key_name.GetString(), KEY_READ)))
+    return false;
+
+  *value_count = reg_key.GetValueCount();
+
+  reg_key.Close();
+
+  return true;
+}
+
+bool RegistryStore::GetValueNameAt(int index, CString* value_name) {
+  ASSERT1(index >= 0);
+  ASSERT1(value_name);
+
+  CString key_name(key_path_);
+  HKEY h_key = RegKey::GetRootKeyInfo(&key_name);
+
+  RegKey reg_key;
+  if (FAILED(reg_key.Open(h_key, key_name.GetString(), KEY_READ)))
+    return false;
+
+  HRESULT hr = reg_key.GetValueNameAt(index, value_name, NULL);
+
+  reg_key.Close();
+
+  return SUCCEEDED(hr);
+}
+
+}  // namespace omaha
+
diff --git a/common/registry_store.h b/base/registry_store.h
similarity index 100%
rename from common/registry_store.h
rename to base/registry_store.h
diff --git a/base/registry_store_unittest.cc b/base/registry_store_unittest.cc
new file mode 100644
index 0000000..6d7bdb3
--- /dev/null
+++ b/base/registry_store_unittest.cc
@@ -0,0 +1,75 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+//
+// Unit test for RegistryStore.
+
+#include "omaha/base/registry_store.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+static const TCHAR kRSTestKey[] =
+    _T("HKCU\\Software\\Google\\Common_Installer__TEST_STORE");
+static const TCHAR kRSTestName[] = _T("TestValueName");
+static const byte kRSTestValue[] = {0x01, 0x02, 0x03, 0x04, 0x05};
+static const int kRSTestValueSize = arraysize(kRSTestValue);
+
+TEST(RegistryStoreTest, RegistryStore) {
+  RegistryStore registry_store;
+  uint32 value_count = 42; // We want to make sure it's overwritten with 0.
+  CString value_name;
+  std::vector<byte> data;
+
+  // Set up and get in a known state.
+  EXPECT_TRUE(registry_store.Open(kRSTestKey));
+  EXPECT_TRUE(registry_store.Clear());
+
+  // Add and test a single value
+  EXPECT_FALSE(registry_store.Exists(kRSTestName));
+  EXPECT_FALSE(registry_store.Read(kRSTestName, &data));
+  data.clear();
+
+  EXPECT_TRUE(registry_store.Write(kRSTestName,
+                                   const_cast<byte*>(kRSTestValue),
+                                   kRSTestValueSize));
+
+  EXPECT_TRUE(registry_store.Exists(kRSTestName));
+  EXPECT_TRUE(registry_store.Read(kRSTestName, &data));
+  EXPECT_EQ(data.size(), kRSTestValueSize);
+  for (int i = 0; i < kRSTestValueSize; i++)
+    EXPECT_EQ(data[i], kRSTestValue[i]);
+
+  // Remove and re-add value
+  EXPECT_TRUE(registry_store.Remove(kRSTestName));
+  EXPECT_FALSE(registry_store.Exists(kRSTestName));
+  EXPECT_TRUE(registry_store.GetValueCount(&value_count));
+  EXPECT_EQ(value_count, 0);
+  EXPECT_TRUE(registry_store.Write(kRSTestName,
+                                   const_cast<byte*>(kRSTestValue),
+                                   kRSTestValueSize));
+  EXPECT_TRUE(registry_store.GetValueCount(&value_count));
+  EXPECT_EQ(value_count, 1);
+  EXPECT_TRUE(registry_store.GetValueNameAt(0, &value_name));
+  EXPECT_TRUE(value_name == kRSTestName);
+
+  // Clean up and finish.
+  EXPECT_TRUE(registry_store.Clear());
+  EXPECT_FALSE(registry_store.Exists(kRSTestName));
+  EXPECT_FALSE(registry_store.GetValueCount(&value_count));
+  EXPECT_TRUE(registry_store.Close());
+}
+
+}  // namespace omaha
diff --git a/common/run_as_invoker.manifest b/base/run_as_invoker.manifest
similarity index 100%
rename from common/run_as_invoker.manifest
rename to base/run_as_invoker.manifest
diff --git a/base/run_as_invoker.rc b/base/run_as_invoker.rc
new file mode 100644
index 0000000..8c37334
--- /dev/null
+++ b/base/run_as_invoker.rc
@@ -0,0 +1,21 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <afxres.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+
+1 RT_MANIFEST "omaha/base/run_as_invoker.manifest"
diff --git a/base/safe_format.cc b/base/safe_format.cc
new file mode 100644
index 0000000..e52c749
--- /dev/null
+++ b/base/safe_format.cc
@@ -0,0 +1,163 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <algorithm>
+#include "omaha/base/debug.h"
+#include "omaha/base/safe_format.h"
+
+namespace omaha {
+
+namespace {
+
+// Define a templated wrapper for StringCchVPrintf_() that will call the
+// appropriate W/A version based on template parameter.
+template <typename CharType>
+HRESULT InternalStringCchVPrintf(CharType* dest_buffer,
+                                 size_t dest_size,
+                                 const CharType* format_str,
+                                 va_list arg_list);
+
+template <>
+HRESULT InternalStringCchVPrintf<char>(char* dest_buffer,
+                                       size_t dest_size,
+                                       const char* format_str,
+                                       va_list arg_list) {
+  return ::StringCchVPrintfA(dest_buffer, dest_size, format_str, arg_list);
+}
+
+template <>
+HRESULT InternalStringCchVPrintf<wchar_t>(wchar_t* dest_buffer,
+                                          size_t dest_size,
+                                          const wchar_t* format_str,
+                                          va_list arg_list) {
+  return ::StringCchVPrintfW(dest_buffer, dest_size, format_str, arg_list);
+}
+
+// Define a templated wrapper for strlen() that will call the appropriate
+// W/A version based on template parameter.
+template <typename CharType>
+size_t InternalStrlen(const CharType* str);
+
+template <>
+size_t InternalStrlen<char>(const char* str) {
+  return ::strlen(str);
+}
+
+template <>
+size_t InternalStrlen<wchar_t>(const wchar_t* str) {
+  return ::wcslen(str);
+}
+
+// InternalCStringVPrintf() wraps InternalStringCchVPrintf() to accept a
+// CStringT as the output parameter and resize it until the latter succeeds
+// or we hit the StrSafe.h limit.
+template <typename CharType, typename CharTraits>
+HRESULT InternalCStringVPrintf(
+               ATL::CStringT<CharType, CharTraits>& dest_str,
+               const CharType* format_str,
+               va_list arg_list) {
+  size_t buf_length = std::max(InternalStrlen(format_str),
+                               static_cast<size_t>(256));
+
+  for (;;) {
+    CStrBufT<CharType> str_buf(dest_str, buf_length);
+    HRESULT hr = InternalStringCchVPrintf(static_cast<CharType*>(str_buf),
+                                    buf_length - 1,
+                                    format_str,
+                                    arg_list);
+    if (hr != STRSAFE_E_INSUFFICIENT_BUFFER) {
+      return hr;
+    }
+    if (buf_length >= STRSAFE_MAX_CCH) {
+      return STRSAFE_E_INVALID_PARAMETER;
+    }
+    buf_length = std::min(buf_length * 2, static_cast<size_t>(STRSAFE_MAX_CCH));
+  }
+}
+
+// InternalCStringVPrintf() will have an overflow bug if STRSAFE_MAX_CCH ever
+// becomes larger than MAX_SIZE_T / 2.  Ensure at compile time that this is so.
+COMPILE_ASSERT(STRSAFE_MAX_CCH <= SIZE_MAX / 2, strsafe_limit_has_changed);
+
+}  // namespace
+
+// Define the non-templated API calls.
+
+void SafeCStringWFormatV(CStringW* dest_str,
+                         LPCWSTR format_str,
+                         va_list arg_list) {
+  ASSERT1(dest_str);
+  ASSERT1(format_str);
+  VERIFY1(SUCCEEDED(InternalCStringVPrintf(*dest_str, format_str, arg_list)));
+}
+
+void SafeCStringAFormatV(CStringA* dest_str,
+                         LPCSTR format_str,
+                         va_list arg_list) {
+  ASSERT1(dest_str);
+  ASSERT1(format_str);
+  VERIFY1(SUCCEEDED(InternalCStringVPrintf(*dest_str, format_str, arg_list)));
+}
+
+void SafeCStringWFormat(CStringW* dest_str, LPCWSTR format_str, ...) {
+  ASSERT1(dest_str);
+  ASSERT1(format_str);
+
+  va_list arg_list;
+  va_start(arg_list, format_str);
+  VERIFY1(SUCCEEDED(InternalCStringVPrintf(*dest_str, format_str, arg_list)));
+  va_end(arg_list);
+}
+
+void SafeCStringAFormat(CStringA* dest_str, LPCSTR format_str, ...) {
+  ASSERT1(dest_str);
+  ASSERT1(format_str);
+
+  va_list arg_list;
+  va_start(arg_list, format_str);
+  VERIFY1(SUCCEEDED(InternalCStringVPrintf(*dest_str, format_str, arg_list)));
+  va_end(arg_list);
+}
+
+void SafeCStringWAppendFormat(CStringW* dest_str, LPCWSTR format_str, ...) {
+  ASSERT1(dest_str);
+  ASSERT1(format_str);
+
+  va_list arg_list;
+  va_start(arg_list, format_str);
+
+  CStringW append_str;
+  VERIFY1(SUCCEEDED(InternalCStringVPrintf(append_str, format_str, arg_list)));
+  dest_str->Append(append_str);
+
+  va_end(arg_list);
+}
+
+void SafeCStringAAppendFormat(CStringA* dest_str, LPCSTR format_str, ...) {
+  ASSERT1(dest_str);
+  ASSERT1(format_str);
+
+  va_list arg_list;
+  va_start(arg_list, format_str);
+
+  CStringA append_str;
+  VERIFY1(SUCCEEDED(InternalCStringVPrintf(append_str, format_str, arg_list)));
+  dest_str->Append(append_str);
+
+  va_end(arg_list);
+}
+
+}  // namespace omaha
+
diff --git a/base/safe_format.h b/base/safe_format.h
new file mode 100644
index 0000000..421441d
--- /dev/null
+++ b/base/safe_format.h
@@ -0,0 +1,60 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// As of Sept 2010, the implementation of Format() in ATL's CStringT uses a
+// fixed internal buffer of 1024 bytes; any output beyond that point will
+// be silently truncated.  (AppendFormat() is affected by the same bug.)
+// A bug has been filed with Microsoft; in the meantime, we provide the
+// following safer implementations of Format/AppendFormat.  These functions
+// currently support up to MAX_INT characters and will return an HRESULT
+// error code on overflow or invalid parameters.
+
+#ifndef OMAHA_BASE_SAFE_FORMAT_H_
+#define OMAHA_BASE_SAFE_FORMAT_H_
+
+#include <atlstr.h>
+#include <strsafe.h>
+
+namespace omaha {
+
+void SafeCStringWFormatV(CStringW* dest_str,
+                         LPCWSTR format_str,
+                         va_list arg_list);
+
+void SafeCStringAFormatV(CStringA* dest_str,
+                         LPCSTR format_str,
+                         va_list arg_list);
+
+void SafeCStringWFormat(CStringW* dest_str, LPCWSTR format_str, ...);
+
+void SafeCStringAFormat(CStringA* dest_str, LPCSTR format_str, ...);
+
+void SafeCStringWAppendFormat(CStringW* dest_str, LPCWSTR format_str, ...);
+
+void SafeCStringAAppendFormat(CStringA* dest_str, LPCSTR format_str, ...);
+
+}  // namespace omaha
+
+#ifdef UNICODE
+#define SafeCStringFormatV       SafeCStringWFormatV
+#define SafeCStringFormat        SafeCStringWFormat
+#define SafeCStringAppendFormat  SafeCStringWAppendFormat
+#else
+#define SafeCStringFormatV       SafeCStringAFormatV
+#define SafeCStringFormat        SafeCStringAFormat
+#define SafeCStringAppendFormat  SafeCStringAAppendFormat
+#endif  // UNICODE
+
+#endif  // OMAHA_BASE_SAFE_FORMAT_H_
diff --git a/base/safe_format_unittest.cc b/base/safe_format_unittest.cc
new file mode 100644
index 0000000..d404306
--- /dev/null
+++ b/base/safe_format_unittest.cc
@@ -0,0 +1,141 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// SafeCStringFormat unit tests.
+
+#include "omaha/base/safe_format.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(SafeFormatTest, BrokenCStringFormatTruncates) {
+  // TODO(omaha): See http://b/1016121 for details.  As of Sept 2010,
+  // CString::Format() is implemented using ::wvsprintf(), which has
+  // an internal 1024-byte buffer limit.  A bug has been filed with
+  // Microsoft.  If this test breaks, it means that we have gotten a
+  // new version of ATL/MFC with a fixed CString and can use that
+  // instead of SafeCStrFormat/AppendFormat.
+
+  TCHAR largestr[4000] = { 0 };
+
+  for (int i = 0; i < ARRAYSIZE(largestr); ++i) {
+    largestr[i] = _T('a') + static_cast<TCHAR>(i % 26);
+  }
+  largestr[ARRAYSIZE(largestr) - 1] = _T('\0');
+
+  CString test_string;
+  test_string.Format(_T("%s"), largestr);
+  EXPECT_EQ(1024, test_string.GetLength());
+  test_string.AppendFormat(_T("%s"), largestr);
+  EXPECT_EQ(2048, test_string.GetLength());
+}
+
+TEST(SafeFormatTest, SafeFormatDoesNotTruncate) {
+  TCHAR largestr[4000] = { 0 };
+
+  for (int i = 0; i < ARRAYSIZE(largestr); ++i) {
+    largestr[i] = _T('a') + static_cast<TCHAR>(i % 26);
+  }
+  largestr[ARRAYSIZE(largestr) - 1] = _T('\0');
+
+  CString test_string;
+  SafeCStringFormat(&test_string, _T("%s"), largestr);
+  EXPECT_EQ(ARRAYSIZE(largestr) - 1, test_string.GetLength());
+
+  SafeCStringAppendFormat(&test_string, _T("%s"), largestr);
+  EXPECT_EQ(2 * (ARRAYSIZE(largestr) - 1), test_string.GetLength());
+}
+
+TEST(SafeFormatTest, FormatBasicFieldTypes) {
+  CString test_string;
+
+  test_string.Empty();
+  SafeCStringFormat(&test_string, _T("%%"));
+  EXPECT_STREQ(_T("%"), test_string);
+
+  test_string.Empty();
+  SafeCStringFormat(&test_string, _T("%c"), _T('h'));
+  EXPECT_STREQ(_T("h"), test_string);
+
+  test_string.Empty();
+  SafeCStringFormat(&test_string, _T("%d"), -42);
+  EXPECT_STREQ(_T("-42"), test_string);
+
+  test_string.Empty();
+  SafeCStringFormat(&test_string, _T("%6u"), 1337);
+  EXPECT_STREQ(_T("  1337"), test_string);
+
+  test_string.Empty();
+  SafeCStringFormat(&test_string, _T("%010X"), 3545084735U);
+  EXPECT_STREQ(_T("00D34DB33F"), test_string);
+
+  test_string.Empty();
+  SafeCStringFormat(&test_string, _T("%0.3f"), 123.456);
+  EXPECT_STREQ(_T("123.456"), test_string);
+
+  test_string.Empty();
+  SafeCStringFormat(&test_string, _T("\"%s\""), _T("ut_str"));
+  EXPECT_STREQ(_T("\"ut_str\""), test_string);
+
+  test_string.Empty();
+  SafeCStringFormat(&test_string, _T("\"%s\""), NULL);
+  EXPECT_STREQ(_T("\"(null)\""), test_string);
+}
+
+TEST(SafeFormatTest, AppendFormatBasicFieldTypes) {
+  const CString prefix = _T("ut_prefix");
+  CString test_string;
+
+  test_string = prefix;
+  SafeCStringAppendFormat(&test_string, _T("%%"));
+  EXPECT_STREQ(prefix + _T("%"), test_string);
+
+  test_string = prefix;
+  SafeCStringAppendFormat(&test_string, _T("%c"), _T('h'));
+  EXPECT_STREQ(prefix + _T("h"), test_string);
+
+  test_string = prefix;
+  SafeCStringAppendFormat(&test_string, _T("%d"), -42);
+  EXPECT_STREQ(prefix + _T("-42"), test_string);
+
+  test_string = prefix;
+  SafeCStringAppendFormat(&test_string, _T("%6u"), 1337);
+  EXPECT_STREQ(prefix + _T("  1337"), test_string);
+
+  test_string = prefix;
+  SafeCStringAppendFormat(&test_string, _T("%010X"), 3545084735U);
+  EXPECT_STREQ(prefix + _T("00D34DB33F"), test_string);
+
+  test_string = prefix;
+  SafeCStringAppendFormat(&test_string, _T("%0.3f"), 123.456);
+  EXPECT_STREQ(prefix + _T("123.456"), test_string);
+
+  test_string = prefix;
+  SafeCStringAppendFormat(&test_string, _T("\"%s\""), _T("ut_str"));
+  EXPECT_STREQ(prefix + _T("\"ut_str\""), test_string);
+
+  test_string = prefix;
+  SafeCStringAppendFormat(&test_string, _T("\"%s\""), NULL);
+  EXPECT_STREQ(prefix + _T("\"(null)\""), test_string);
+}
+
+TEST(SafeFormatTest, FormatComplex) {
+  CString test_string = _T("prefix: ");
+  SafeCStringAppendFormat(&test_string, _T("Test: %cx%08X '%s' %4d %0.2f"),
+                          _T('0'), 12648430, _T("utstr"), 42, 123.456);
+  EXPECT_STREQ(_T("prefix: Test: 0x00C0FFEE 'utstr'   42 123.46"), test_string);
+}
+
+}  // namespace omaha
diff --git a/base/scope_guard.h b/base/scope_guard.h
new file mode 100644
index 0000000..d0227c9
--- /dev/null
+++ b/base/scope_guard.h
@@ -0,0 +1,369 @@
+//
+// Author: Andrei Alexandrescu - andrei@metalanguage.com
+//
+// The code below is based on the following article published in
+// C/C++ Users Journal by Andrei Alexandrescu:
+//
+// http://www.cuj.com/documents/s=8000/cujcexp1812alexandr/alexandr.htm
+//
+// ScopeGuard is useful when you need to perform automatic cleanup of resources.
+// This idiom is important when you want to assemble an operation out of several
+// atomic operations, each of which could fail.
+//
+// Usage
+// ------
+// Scope guard for objects:
+// void f(T& t) {
+//   std::vector<T> v;
+//   v.push_back(t);
+//   ScopeGuard guard = MakeObjGuard(v, &std::vector<T>v::pop_back);
+//   if (!Commit()) {
+//     return;
+//   }
+//   guard.Dismiss();     // removes the t from the vector in the case Commit fails
+// }
+//
+// Scope guard for functions:
+// void open();
+// void close(int i);
+// void g(int i) {
+//   open();
+//   ScopeGuard guard = MakeGuard(close, 0);
+//   if (!read()) {
+//       return;
+//   }
+//   if (!write()) {
+//       return;
+//   }
+// }
+//
+// Using the macros:
+// void g(int i) {
+//   open();
+//   ON_SCOPE_EXIT(close, 0);
+//   if (!read()) {
+//       return;
+//   }
+//   if (!write()) {
+//       return;
+//   }
+// }
+
+// TODO(omaha): provide support to run with or without exceptions enabled.
+// For now it assumes that the code is not throwing exceptions.
+#ifndef SCOPEGUARD_H_
+#define SCOPEGUARD_H_
+
+namespace omaha {
+
+template <class T>
+class RefHolder
+{
+  T& ref_;
+public:
+  RefHolder(T& ref) : ref_(ref) {}
+  operator T& () const
+  {
+    return ref_;
+  }
+private:
+    // Disable assignment - not implemented
+    RefHolder& operator=(const RefHolder&);
+};
+
+template <class T>
+inline RefHolder<T> ByRef(T& t)
+{
+  return RefHolder<T>(t);
+}
+
+class ScopeGuardImplBase
+{
+  ScopeGuardImplBase& operator =(const ScopeGuardImplBase&);
+protected:
+  ~ScopeGuardImplBase()
+  {
+  }
+  ScopeGuardImplBase(const ScopeGuardImplBase& other) throw()
+    : dismissed_(other.dismissed_)
+  {
+    other.Dismiss();
+  }
+  template <typename J>
+  static void SafeExecute(J& j) throw()
+  {
+    if (!j.dismissed_)
+    {
+      // TODO(omaha): assume this does not throw
+      j.Execute();
+    }
+  }
+
+  mutable bool dismissed_;
+public:
+  ScopeGuardImplBase() throw() : dismissed_(false)
+  {
+  }
+  void Dismiss() const throw()
+  {
+    dismissed_ = true;
+  }
+};
+
+typedef const ScopeGuardImplBase& ScopeGuard;
+
+template <typename F>
+class ScopeGuardImpl0 : public ScopeGuardImplBase
+{
+public:
+  static ScopeGuardImpl0<F> MakeGuard(F fun)
+  {
+    return ScopeGuardImpl0<F>(fun);
+  }
+  ~ScopeGuardImpl0() throw()
+  {
+    SafeExecute(*this);
+  }
+  void Execute()
+  {
+    fun_();
+  }
+protected:
+  ScopeGuardImpl0(F fun) : fun_(fun)
+  {
+  }
+  F fun_;
+};
+
+template <typename F>
+inline ScopeGuardImpl0<F> MakeGuard(F fun)
+{
+  return ScopeGuardImpl0<F>::MakeGuard(fun);
+}
+
+template <typename F, typename P1>
+class ScopeGuardImpl1 : public ScopeGuardImplBase
+{
+public:
+  static ScopeGuardImpl1<F, P1> MakeGuard(F fun, P1 p1)
+  {
+    return ScopeGuardImpl1<F, P1>(fun, p1);
+  }
+  ~ScopeGuardImpl1() throw()
+  {
+    SafeExecute(*this);
+  }
+  void Execute()
+  {
+    fun_(p1_);
+  }
+protected:
+  ScopeGuardImpl1(F fun, P1 p1) : fun_(fun), p1_(p1)
+  {
+  }
+  F fun_;
+  const P1 p1_;
+};
+
+template <typename F, typename P1>
+inline ScopeGuardImpl1<F, P1> MakeGuard(F fun, P1 p1)
+{
+  return ScopeGuardImpl1<F, P1>::MakeGuard(fun, p1);
+}
+
+template <typename F, typename P1, typename P2>
+class ScopeGuardImpl2: public ScopeGuardImplBase
+{
+public:
+  static ScopeGuardImpl2<F, P1, P2> MakeGuard(F fun, P1 p1, P2 p2)
+  {
+    return ScopeGuardImpl2<F, P1, P2>(fun, p1, p2);
+  }
+  ~ScopeGuardImpl2() throw()
+  {
+    SafeExecute(*this);
+  }
+  void Execute()
+  {
+    fun_(p1_, p2_);
+  }
+protected:
+  ScopeGuardImpl2(F fun, P1 p1, P2 p2) : fun_(fun), p1_(p1), p2_(p2)
+  {
+  }
+  F fun_;
+  const P1 p1_;
+  const P2 p2_;
+};
+
+template <typename F, typename P1, typename P2>
+inline ScopeGuardImpl2<F, P1, P2> MakeGuard(F fun, P1 p1, P2 p2)
+{
+  return ScopeGuardImpl2<F, P1, P2>::MakeGuard(fun, p1, p2);
+}
+
+template <typename F, typename P1, typename P2, typename P3>
+class ScopeGuardImpl3 : public ScopeGuardImplBase
+{
+public:
+  static ScopeGuardImpl3<F, P1, P2, P3> MakeGuard(F fun, P1 p1, P2 p2, P3 p3)
+  {
+    return ScopeGuardImpl3<F, P1, P2, P3>(fun, p1, p2, p3);
+  }
+  ~ScopeGuardImpl3() throw()
+  {
+    SafeExecute(*this);
+  }
+  void Execute()
+  {
+    fun_(p1_, p2_, p3_);
+  }
+protected:
+  ScopeGuardImpl3(F fun, P1 p1, P2 p2, P3 p3) : fun_(fun), p1_(p1), p2_(p2), p3_(p3)
+  {
+  }
+  F fun_;
+  const P1 p1_;
+  const P2 p2_;
+  const P3 p3_;
+};
+
+template <typename F, typename P1, typename P2, typename P3>
+inline ScopeGuardImpl3<F, P1, P2, P3> MakeGuard(F fun, P1 p1, P2 p2, P3 p3)
+{
+  return ScopeGuardImpl3<F, P1, P2, P3>::MakeGuard(fun, p1, p2, p3);
+}
+
+
+template <class Obj, typename MemFun>
+class ObjScopeGuardImpl0 : public ScopeGuardImplBase
+{
+public:
+  static ObjScopeGuardImpl0<Obj, MemFun> MakeObjGuard(Obj& obj, MemFun memFun)
+  {
+    return ObjScopeGuardImpl0<Obj, MemFun>(obj, memFun);
+  }
+  ~ObjScopeGuardImpl0() throw()
+  {
+    SafeExecute(*this);
+  }
+  void Execute()
+  {
+    (obj_.*memFun_)();
+  }
+protected:
+  ObjScopeGuardImpl0(Obj& obj, MemFun memFun)
+    : obj_(obj), memFun_(memFun) {}
+  Obj& obj_;
+  MemFun memFun_;
+};
+
+template <class Obj, typename MemFun>
+inline ObjScopeGuardImpl0<Obj, MemFun> MakeObjGuard(Obj& obj, MemFun memFun)
+{
+  return ObjScopeGuardImpl0<Obj, MemFun>::MakeObjGuard(obj, memFun);
+}
+
+template <class Obj, typename MemFun, typename P1>
+class ObjScopeGuardImpl1 : public ScopeGuardImplBase
+{
+public:
+  static ObjScopeGuardImpl1<Obj, MemFun, P1> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1)
+  {
+    return ObjScopeGuardImpl1<Obj, MemFun, P1>(obj, memFun, p1);
+  }
+  ~ObjScopeGuardImpl1() throw()
+  {
+    SafeExecute(*this);
+  }
+  void Execute()
+  {
+    (obj_.*memFun_)(p1_);
+  }
+protected:
+  ObjScopeGuardImpl1(Obj& obj, MemFun memFun, P1 p1)
+    : obj_(obj), memFun_(memFun), p1_(p1) {}
+  Obj& obj_;
+  MemFun memFun_;
+  const P1 p1_;
+};
+
+template <class Obj, typename MemFun, typename P1>
+inline ObjScopeGuardImpl1<Obj, MemFun, P1> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1)
+{
+  return ObjScopeGuardImpl1<Obj, MemFun, P1>::MakeObjGuard(obj, memFun, p1);
+}
+
+template <class Obj, typename MemFun, typename P1, typename P2>
+class ObjScopeGuardImpl2 : public ScopeGuardImplBase
+{
+public:
+  static ObjScopeGuardImpl2<Obj, MemFun, P1, P2> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1, P2 p2)
+  {
+    return ObjScopeGuardImpl2<Obj, MemFun, P1, P2>(obj, memFun, p1, p2);
+  }
+  ~ObjScopeGuardImpl2() throw()
+  {
+    SafeExecute(*this);
+  }
+  void Execute()
+  {
+    (obj_.*memFun_)(p1_, p2_);
+  }
+protected:
+  ObjScopeGuardImpl2(Obj& obj, MemFun memFun, P1 p1, P2 p2)
+    : obj_(obj), memFun_(memFun), p1_(p1), p2_(p2) {}
+  Obj& obj_;
+  MemFun memFun_;
+  const P1 p1_;
+  const P2 p2_;
+};
+
+template <class Obj, typename MemFun, typename P1, typename P2>
+inline ObjScopeGuardImpl2<Obj, MemFun, P1, P2> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1, P2 p2)
+{
+  return ObjScopeGuardImpl2<Obj, MemFun, P1, P2>::MakeObjGuard(obj, memFun, p1, p2);
+}
+
+template <class Obj, typename MemFun, typename P1, typename P2, typename P3>
+class ObjScopeGuardImpl3 : public ScopeGuardImplBase
+{
+public:
+  static ObjScopeGuardImpl3<Obj, MemFun, P1, P2, P3> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1, P2 p2, P3 p3)
+  {
+    return ObjScopeGuardImpl3<Obj, MemFun, P1, P2, P3>(obj, memFun, p1, p2, p3);
+  }
+  ~ObjScopeGuardImpl3() throw()
+  {
+    SafeExecute(*this);
+  }
+  void Execute()
+  {
+    (obj_.*memFun_)(p1_, p2_, p3_);
+  }
+protected:
+  ObjScopeGuardImpl3(Obj& obj, MemFun memFun, P1 p1, P2 p2, P3 p3)
+    : obj_(obj), memFun_(memFun), p1_(p1), p2_(p2), p3_(p3) {}
+  Obj& obj_;
+  MemFun memFun_;
+  const P1 p1_;
+  const P2 p2_;
+  const P3 p3_;
+};
+
+template <class Obj, typename MemFun, typename P1, typename P2, typename P3>
+inline ObjScopeGuardImpl3<Obj, MemFun, P1, P2, P3> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1, P2 p2, P3 p3)
+{
+  return ObjScopeGuardImpl3<Obj, MemFun, P1, P2, P3>::MakeObjGuard(obj, memFun, p1, p2, p3);
+}
+
+#define CONCATENATE_DIRECT(s1, s2) s1##s2
+#define CONCATENATE(s1, s2) CONCATENATE_DIRECT(s1, s2)
+#define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__)
+
+#define ON_SCOPE_EXIT ScopeGuard ANONYMOUS_VARIABLE(scopeGuard) = MakeGuard
+#define ON_SCOPE_EXIT_OBJ ScopeGuard ANONYMOUS_VARIABLE(scopeGuard) = MakeObjGuard
+
+}  // namespace omaha
+
+#endif //SCOPEGUARD_H_
diff --git a/common/scoped_any.h b/base/scoped_any.h
similarity index 100%
rename from common/scoped_any.h
rename to base/scoped_any.h
diff --git a/common/scoped_current_directory.h b/base/scoped_current_directory.h
similarity index 100%
rename from common/scoped_current_directory.h
rename to base/scoped_current_directory.h
diff --git a/base/scoped_impersonation.h b/base/scoped_impersonation.h
new file mode 100644
index 0000000..aa08849
--- /dev/null
+++ b/base/scoped_impersonation.h
@@ -0,0 +1,173 @@
+// Copyright 2008-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// TODO(omaha): deprecate and use ATL::CAccessToken instead.
+
+#ifndef OMAHA_BASE_SCOPED_IMPERSONATION_H_
+#define OMAHA_BASE_SCOPED_IMPERSONATION_H_
+
+#include <windows.h>
+#include <atlsecurity.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_any.h"
+
+namespace omaha {
+
+inline void ImpersonateLoggedOnUserOrDie(HANDLE token) {
+  if (!::ImpersonateLoggedOnUser(token)) {
+    CORE_LOG(LE, (_T("[ImpersonateLoggedOnUser failed][0x%08x]"),
+                  HRESULTFromLastError()));
+    ::RaiseException(EXCEPTION_IMPERSONATION_FAILED,
+                     EXCEPTION_NONCONTINUABLE,
+                     0,
+                     NULL);
+  }
+}
+
+inline void RevertToSelfOrDie() {
+  if (!::RevertToSelf()) {
+    CORE_LOG(LE, (_T("[RevertToSelf failed][0x%08x]"), HRESULTFromLastError()));
+    ::RaiseException(EXCEPTION_REVERT_IMPERSONATION_FAILED,
+                     EXCEPTION_NONCONTINUABLE,
+                     0,
+                     NULL);
+  }
+}
+
+inline HRESULT smart_impersonate_helper(HANDLE token) {
+  if (!token) {
+    return S_FALSE;
+  }
+  return ::ImpersonateLoggedOnUser(token) ? S_OK : HRESULTFromLastError();
+}
+
+inline void smart_unimpersonate_helper(HRESULT result) {
+  if (result == S_OK) {
+    RevertToSelfOrDie();
+  }
+}
+
+typedef close_fun<void (*)(HRESULT), smart_unimpersonate_helper>
+    close_impersonation;
+
+typedef value_const<HRESULT, E_FAIL> impersonation_not_init;
+
+typedef scoped_any<HRESULT, close_impersonation, impersonation_not_init>
+    scoped_impersonation_close;
+
+// Manages the calls to ImpersonateLoggedOnUser and RevertToSelf. The input
+// token is allowed to be NULL in which case no impersonation/revert is
+// performed.
+struct scoped_impersonation {
+  explicit scoped_impersonation(HANDLE token)
+      : result_(smart_impersonate_helper(token)) {
+    HRESULT hr = result();
+    if (token && SUCCEEDED(hr)) {
+      CORE_LOG(L3, (_T("[Impersonation succeeded]")));
+    } else if (token && FAILED(hr)) {
+      CORE_LOG(LW, (_T("[Impersonation failed][0x%x]"), hr));
+    } else if (!token) {
+      CORE_LOG(LW, (_T("[Impersonation requested but the token was null]")));
+    }
+  }
+
+  HRESULT result() const { return get(result_); }
+
+ private:
+  const scoped_impersonation_close result_;
+};
+
+class scoped_revert_to_self {
+ public:
+  scoped_revert_to_self() {
+    token_.GetThreadToken(TOKEN_ALL_ACCESS);
+    if (token_.GetHandle()) {
+      RevertToSelfOrDie();
+    }
+  }
+  ~scoped_revert_to_self() {
+    if (token_.GetHandle()) {
+      ImpersonateLoggedOnUserOrDie(token_.GetHandle());
+    }
+  }
+ private:
+  CAccessToken token_;
+};
+
+// Calls a function member using the security context of the process, and
+// re-impersonates after the call if the calling thread previously had a valid
+// thread token.
+// This is particularly useful when a thread running impersonated needs to
+// revert to self, call a member function, and impersonate back. If the callee
+// has a result type, it returns the result of the actual call as an out
+// parameter. The function crashes the process if either revert to self or
+// re-impersonation fails.
+
+// Callers for function members without arguments.
+template <typename T, typename R>
+R CallAsSelfAndImpersonate0(T* object, R (T::*pm)()) {
+  ASSERT1(object);
+  ASSERT1(pm);
+
+  scoped_revert_to_self revert_to_self;
+  return (object->*pm)();
+}
+
+// Callers for __stdcall function members with one argument.
+template <typename T, typename P1, typename R>
+R StdCallAsSelfAndImpersonate1(T* object, R (__stdcall T::*pm)(P1), P1 p1) {
+  ASSERT1(object);
+  ASSERT1(pm);
+
+  scoped_revert_to_self revert_to_self;
+  return (object->*pm)(p1);
+}
+
+// Callers for function members with one argument.
+template <typename T, typename P1, typename R>
+R CallAsSelfAndImpersonate1(T* object, R (T::*pm)(P1), P1 p1) {
+  ASSERT1(object);
+  ASSERT1(pm);
+
+  scoped_revert_to_self revert_to_self;
+  return (object->*pm)(p1);
+}
+
+// Callers for function members with two arguments.
+template <class T, typename P1, typename P2, class R>
+R CallAsSelfAndImpersonate2(T* object, R (T::*pm)(P1, P2), P1 p1, P2 p2) {
+  ASSERT1(object);
+  ASSERT1(pm);
+
+  scoped_revert_to_self revert_to_self;
+  return (object->*pm)(p1, p2);
+}
+
+// Callers for function members with three arguments.
+template <class T, typename P1, typename P2, typename P3, typename R>
+R CallAsSelfAndImpersonate3(T* object, R (T::*pm)(P1, P2, P3),
+                            P1 p1, P2 p2, P3 p3) {
+  ASSERT1(object);
+  ASSERT1(pm);
+
+  scoped_revert_to_self revert_to_self;
+  return (object->*pm)(p1, p2, p3);
+}
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_SCOPED_IMPERSONATION_H_
diff --git a/base/scoped_impersonation_unittest.cc b/base/scoped_impersonation_unittest.cc
new file mode 100644
index 0000000..a476a34
--- /dev/null
+++ b/base/scoped_impersonation_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright 2008-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <windows.h>
+#include "base/basictypes.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/scoped_impersonation.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/vista_utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(ScopedImpersonationTest, ImpersonateLoggedOnUser) {
+  scoped_handle token;
+  vista::GetLoggedOnUserToken(address(token));
+  if (token) {
+    scoped_impersonation impersonate_user(get(token));
+    EXPECT_EQ(impersonate_user.result(), S_OK);
+  }
+}
+
+TEST(ScopedImpersonationTest, ImpersonateLoggedOnUserNullHandle) {
+  scoped_impersonation impersonate_user(NULL);
+  EXPECT_EQ(impersonate_user.result(), S_FALSE);
+}
+
+
+}  // namespace omaha
+
diff --git a/base/scoped_ptr_address.h b/base/scoped_ptr_address.h
new file mode 100644
index 0000000..0fa6930
--- /dev/null
+++ b/base/scoped_ptr_address.h
@@ -0,0 +1,58 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Helper functions to enable a useful pattern of using scoped pointers in
+// which the ownership of the memory is transferred out to an empty
+// scoped_ptr or scoped_array.
+//
+// The usage pattern is as follows:
+//    void foo(Bar** b);
+//
+//    scoped_ptr<B> p;
+//    foo(address(p));
+//
+// To receive the ownwership of the resource the scoped pointer must be
+// empty, otherwise it will leak.
+//
+// As an implementation detail, the scoped pointers in "base/scoped_ptr.h" do
+// not offer support for this idiom. The code below may break if the
+// implementation of the scoped_ptr changes. The code works with the vast
+// majority of the scoped_ptr implementations though.
+//
+// TODO(omaha): add unit tests.
+
+#ifndef OMAHA_COMMON_SCOPED_PTR_ADDRESS__
+#define OMAHA_COMMON_SCOPED_PTR_ADDRESS__
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/debug.h"
+
+template <typename T>
+inline T** address(const scoped_ptr<T>& t) {
+  COMPILE_ASSERT(sizeof(T*) == sizeof(scoped_ptr<T>), types_do_not_match);
+  ASSERT1(!t.get());
+  return reinterpret_cast<T**>(&const_cast<scoped_ptr<T>&>(t));
+}
+
+template <typename T>
+inline T** address(const scoped_array<T>& t) {
+  COMPILE_ASSERT(sizeof(T*) == sizeof(scoped_ptr<T>), types_do_not_match);
+  ASSERT1(!t.get());
+  return reinterpret_cast<T**>(&const_cast<scoped_array<T>&>(t));
+}
+
+#endif // OMAHA_COMMON_SCOPED_PTR_ADDRESS__
+
diff --git a/base/scoped_ptr_cotask.h b/base/scoped_ptr_cotask.h
new file mode 100644
index 0000000..85dc2a1
--- /dev/null
+++ b/base/scoped_ptr_cotask.h
@@ -0,0 +1,260 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Defines utility classes and functions which facilitate working with the
+// memory allocation functions CoTaskMemAlloc and CoTaskMemFree.
+
+#ifndef OMAHA_COMMON_SCOPED_PTR_COTASK_H__
+#define OMAHA_COMMON_SCOPED_PTR_COTASK_H__
+
+#include "omaha/base/debug.h"
+
+// scoped_ptr_cotask is identical to scoped_ptr, except that CoTaskMemFree is
+// called instead of delete.  For documentation of the interface, see
+// scoped_ptr.h.
+
+template <typename T>
+class scoped_ptr_cotask;
+
+template <typename T>
+scoped_ptr_cotask<T> make_scoped_ptr_cotask(T* p);
+
+template <typename T>
+class scoped_ptr_cotask {
+ private:
+  T* ptr_;
+
+  scoped_ptr_cotask(scoped_ptr_cotask const &);
+  scoped_ptr_cotask & operator=(scoped_ptr_cotask const &);
+
+  friend scoped_ptr_cotask<T> make_scoped_ptr_cotask<T>(T* p);
+
+ public:
+  typedef T element_type;
+
+  explicit scoped_ptr_cotask(T* p = 0): ptr_(p) {}
+
+  ~scoped_ptr_cotask() {
+    typedef char type_must_be_complete[sizeof(T)];
+    ::CoTaskMemFree(ptr_);
+  }
+
+  void reset(T* p = 0) {
+    typedef char type_must_be_complete[sizeof(T)];
+
+    if (ptr_ != p) {
+      ::CoTaskMemFree(ptr_);
+      ptr_ = p;
+    }
+  }
+
+  T& operator*() const {
+    assert(ptr_ != 0);
+    return *ptr_;
+  }
+
+  T* operator->() const  {
+    assert(ptr_ != 0);
+    return ptr_;
+  }
+
+  bool operator==(T* p) const {
+    return ptr_ == p;
+  }
+
+  bool operator!=(T* p) const {
+    return ptr_ != p;
+  }
+
+  T* get() const  {
+    return ptr_;
+  }
+
+  void swap(scoped_ptr_cotask & b) {
+    T* tmp = b.ptr_;
+    b.ptr_ = ptr_;
+    ptr_ = tmp;
+  }
+
+  T* release() {
+    T* tmp = ptr_;
+    ptr_ = 0;
+    return tmp;
+  }
+
+ private:
+  template <typename U> bool operator==(scoped_ptr_cotask<U> const& p) const;
+  template <typename U> bool operator!=(scoped_ptr_cotask<U> const& p) const;
+};
+
+template <typename T>
+scoped_ptr_cotask<T> make_scoped_ptr_cotask(T* p) {
+  return scoped_ptr_cotask<T>(p);
+}
+
+template <typename T> inline
+void swap(scoped_ptr_cotask<T>& a, scoped_ptr_cotask<T>& b) {
+  a.swap(b);
+}
+
+template <typename T> inline
+bool operator==(T* p, const scoped_ptr_cotask<T>& b) {
+  return p == b.get();
+}
+
+template <typename T> inline
+bool operator!=(T* p, const scoped_ptr_cotask<T>& b) {
+  return p != b.get();
+}
+
+template <typename T>
+inline T** address(const scoped_ptr_cotask<T>& t) {
+  COMPILE_ASSERT(sizeof(T*) == sizeof(t), types_do_not_match);
+  ASSERT1(!t.get());
+  return reinterpret_cast<T**>(&const_cast<scoped_ptr_cotask<T>&>(t));
+}
+
+// scoped_array_cotask manages an array of pointers to objects allocated by
+// CoTaskMemAlloc.  The array is also allocated via CoTaskMemAlloc.  The
+// interface is similar to scoped_array, except that an array length must be
+// explicitly provided.  When the object is destructed, the array and each
+// element of the array are explicitly freed.
+
+template <typename T>
+class scoped_array_cotask;
+
+template <typename T>
+class scoped_array_cotask<T*> {
+ private:
+  size_t count_;
+  T** ptr_;
+
+  scoped_array_cotask(scoped_array_cotask const &);
+  scoped_array_cotask & operator=(scoped_array_cotask const &);
+
+ public:
+  typedef T* element_type;
+
+  explicit scoped_array_cotask(size_t c, T** p = 0)
+      : count_(c), ptr_(p) {
+    if (!ptr_) {
+      const size_t array_size = sizeof(T*) * count_;
+      ptr_ = static_cast<T**>(::CoTaskMemAlloc(array_size));
+      memset(ptr_, 0, array_size);
+    }
+  }
+
+  ~scoped_array_cotask() {
+    typedef char type_must_be_complete[sizeof(T*)];
+    if (ptr_) {
+      for (size_t i = 0; i < count_; ++i) {
+        ::CoTaskMemFree(ptr_[i]);
+      }
+      ::CoTaskMemFree(ptr_);
+    }
+  }
+
+  size_t size() const { return count_; }
+
+  void reset(size_t c, T** p = 0) {
+    typedef char type_must_be_complete[sizeof(T*)];
+
+    if (ptr_ != p) {
+      if (ptr_) {
+        for (size_t i = 0; i < count_; ++i) {
+          ::CoTaskMemFree(ptr_[i]);
+        }
+        ::CoTaskMemFree(ptr_);
+      }
+      ptr_ = p;
+    }
+    count_ = c;
+    if (!ptr_) {
+      const size_t array_size = sizeof(T*) * count_;
+      ptr_ = static_cast<T**>(::CoTaskMemAlloc(array_size));
+      memset(ptr_, 0, array_size);
+    }
+  }
+
+  T*& operator[](std::ptrdiff_t i) const {
+    assert(ptr_ != 0);
+    assert(i >= 0);
+    return ptr_[i];
+  }
+
+  bool operator==(T** p) const {
+    return ptr_ == p;
+  }
+
+  bool operator!=(T** p) const {
+    return ptr_ != p;
+  }
+
+  T** get() const {
+    return ptr_;
+  }
+
+  void swap(scoped_array_cotask & b) {
+    T** tmp = b.ptr_;
+    b.ptr_ = ptr_;
+    ptr_ = tmp;
+  }
+
+  T** release() {
+    T** tmp = ptr_;
+    ptr_ = 0;
+    return tmp;
+  }
+
+ private:
+  template <typename U> bool operator==(scoped_array_cotask<U> const& p) const;
+  template <typename U> bool operator!=(scoped_array_cotask<U> const& p) const;
+};
+
+template <typename T> inline
+void swap(scoped_array_cotask<T>& a, scoped_array_cotask<T>& b) {
+  a.swap(b);
+}
+
+template <typename T> inline
+bool operator==(T* p, const scoped_array_cotask<T>& b) {
+  return p == b.get();
+}
+
+template <typename T> inline
+bool operator!=(T* p, const scoped_array_cotask<T>& b) {
+  return p != b.get();
+}
+
+// address() is not relevant to scoped_array_cotask, due to the count parameter.
+//
+// template <typename T>
+// inline T** address(const scoped_array_cotask<T>& t) {
+//   COMPILE_ASSERT(sizeof(T*) == sizeof(t), types_do_not_match);
+//   ASSERT1(!t.get());
+//   return reinterpret_cast<T**>(&const_cast<scoped_array_cotask<T>&>(t));
+// }
+
+// StrDupCoTask allocates a copy of a string using CoTaskMemAlloc.
+
+template <class T>
+inline T* StrDupCoTask(const T* str, size_t length) {
+  T* mem = static_cast<T*>(::CoTaskMemAlloc(sizeof(T) * (length + 1)));
+  memcpy(mem, str, sizeof(T) * length);
+  mem[length] = 0;
+  return mem;
+}
+
+#endif  // OMAHA_COMMON_SCOPED_PTR_COTASK_H__
diff --git a/base/scoped_ptr_cotask_unittest.cc b/base/scoped_ptr_cotask_unittest.cc
new file mode 100644
index 0000000..8b22d2c
--- /dev/null
+++ b/base/scoped_ptr_cotask_unittest.cc
@@ -0,0 +1,308 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <vector>
+#include "omaha/base/debug.h"
+#include "omaha/base/scoped_ptr_cotask.h"
+#include "omaha/testing/unit_test.h"
+
+// TestMallocSpy monitors CoTaskMemAlloc/Free, and records statistics about
+// them.
+
+class TestMallocSpy : public IMallocSpy {
+ public:
+  struct Alloc {
+    size_t size;
+    void* ptr;
+    bool freed;
+  };
+
+  TestMallocSpy() : ref_(1) {}
+
+  virtual ~TestMallocSpy() {}
+
+  size_t NumAllocs() const { return allocs_.size(); }
+
+  const Alloc* GetAlloc(size_t i) const {
+    if (i >= allocs_.size())
+      return NULL;
+    return &allocs_[i];
+  }
+
+  size_t NumFrees() const { return frees_.size(); }
+
+  const Alloc* GetFree(size_t i) const {
+    if (i >= frees_.size())
+      return NULL;
+    ASSERT1(frees_[i] < allocs_.size());
+    return &allocs_[frees_[i]];
+  }
+
+  // IUnknown methods
+  virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) {
+    if (NULL == ppv) {
+      return E_POINTER;
+    }
+    if (::IsEqualIID(__uuidof(IUnknown), riid) ||
+        ::IsEqualIID(__uuidof(IMallocSpy), riid)) {
+      AddRef();
+      *ppv = static_cast<IUnknown*>(this);
+      return S_OK;
+    }
+    *ppv = NULL;
+    return E_NOINTERFACE;
+  }
+  virtual ULONG STDMETHODCALLTYPE AddRef() {
+    return ++ref_;
+  }
+  virtual ULONG STDMETHODCALLTYPE Release() {
+    ULONG r = --ref_;
+    if (0 == r) {
+      delete this;
+    }
+    return r;
+  }
+
+  // IMallocSpy methods
+  virtual SIZE_T STDMETHODCALLTYPE PreAlloc(SIZE_T request_size) {
+    Alloc a = { request_size, NULL, false };
+    allocs_.push_back(a);
+    return request_size;
+  }
+  virtual void* STDMETHODCALLTYPE PostAlloc(void* actual) {
+    ASSERT1(!allocs_.empty());
+    ASSERT1(NULL == allocs_.back().ptr);
+    allocs_.back().ptr = actual;
+    return actual;
+  }
+  virtual void* STDMETHODCALLTYPE PreFree(void* request, BOOL spyed) {
+    if (spyed) {
+      bool found = false;
+      for (size_t i = 0; i < allocs_.size(); ++i) {
+        if ((allocs_[i].ptr == request) && !allocs_[i].freed) {
+          allocs_[i].freed = true;
+          frees_.push_back(i);
+          found = true;
+          break;
+        }
+      }
+      ASSERT1(found);
+    }
+    return request;
+  }
+  virtual void STDMETHODCALLTYPE PostFree(BOOL) {}
+  virtual SIZE_T STDMETHODCALLTYPE PreRealloc(void*,
+                                              SIZE_T request_size,
+                                              void**,
+                                              BOOL) {
+    return request_size;
+  }
+  virtual void* STDMETHODCALLTYPE PostRealloc(void* actual, BOOL) {
+    return actual;
+  }
+  virtual void* STDMETHODCALLTYPE PreGetSize(void* request, BOOL) {
+    return request;
+  }
+  virtual SIZE_T STDMETHODCALLTYPE PostGetSize(SIZE_T actual_size, BOOL) {
+    return actual_size;
+  }
+  virtual void* STDMETHODCALLTYPE PreDidAlloc(void* request, BOOL) {
+    return request;
+  }
+  virtual int STDMETHODCALLTYPE PostDidAlloc(void*, BOOL, int fActual) {
+    return fActual;
+  }
+  virtual void STDMETHODCALLTYPE PreHeapMinimize() {}
+  virtual void STDMETHODCALLTYPE PostHeapMinimize() {}
+
+ private:
+  ULONG ref_;
+  std::vector<Alloc> allocs_;
+  std::vector<size_t> frees_;
+};
+
+// MallocTest runs tests with a TestMallocSpy installed.
+
+class MallocTest : public testing::Test {
+ public:
+  virtual void SetUp() {
+    spy_.Attach(new TestMallocSpy);
+    ASSERT_SUCCEEDED(::CoRegisterMallocSpy(spy_.p));
+    EXPECT_EQ(0, spy()->NumAllocs());
+  }
+  virtual void TearDown() {
+    EXPECT_EQ(spy()->NumAllocs(), spy()->NumFrees());
+    ASSERT_SUCCEEDED(::CoRevokeMallocSpy());
+  }
+  TestMallocSpy* spy() { return spy_.p; }
+
+ private:
+  CComPtr<TestMallocSpy> spy_;
+};
+
+TEST_F(MallocTest, StrDupCoTask) {
+  const char kNarrowString[] = "Hello";
+  const size_t kNarrowLen = strlen(kNarrowString);
+  const wchar_t kWideString[] = L"World";
+  const size_t kWideLen = wcslen(kWideString);
+
+  // Test StrDupCoTask with narrow strings.
+  char* narrow_copy = StrDupCoTask(kNarrowString, kNarrowLen);
+
+  ASSERT_EQ(1, spy()->NumAllocs());
+  EXPECT_EQ(0, spy()->NumFrees());
+  EXPECT_EQ((kNarrowLen + 1) * sizeof(char), spy()->GetAlloc(0)->size);
+  EXPECT_EQ(narrow_copy, spy()->GetAlloc(0)->ptr);
+  EXPECT_FALSE(spy()->GetAlloc(0)->freed);
+
+  ::CoTaskMemFree(narrow_copy);
+
+  ASSERT_EQ(1, spy()->NumFrees());
+  EXPECT_EQ(spy()->GetAlloc(0), spy()->GetFree(0));
+  EXPECT_EQ(narrow_copy, spy()->GetFree(0)->ptr);
+  EXPECT_TRUE(spy()->GetFree(0)->freed);
+
+  // Test StrDupCoTask with wide strings.
+  wchar_t* wide_copy = StrDupCoTask(kWideString, kWideLen);
+
+  ASSERT_EQ(2, spy()->NumAllocs());
+  EXPECT_EQ(1, spy()->NumFrees());
+  EXPECT_EQ((kWideLen + 1) * sizeof(wchar_t), spy()->GetAlloc(1)->size);
+  EXPECT_EQ(wide_copy, spy()->GetAlloc(1)->ptr);
+  EXPECT_FALSE(spy()->GetAlloc(1)->freed);
+
+  ::CoTaskMemFree(wide_copy);
+
+  ASSERT_EQ(2, spy()->NumFrees());
+  EXPECT_EQ(spy()->GetAlloc(1), spy()->GetFree(1));
+  EXPECT_EQ(wide_copy, spy()->GetFree(1)->ptr);
+  EXPECT_TRUE(spy()->GetFree(1)->freed);
+}
+
+TEST_F(MallocTest, scoped_ptr_cotask) {
+  scoped_ptr_cotask<wchar_t>* string_ptr;
+
+  // Creating an empty ptr does no additional allocations.
+  string_ptr = new scoped_ptr_cotask<wchar_t>;
+  ASSERT_EQ(0, spy()->NumAllocs());
+  EXPECT_EQ(0, spy()->NumFrees());
+
+  // Assigning a string does not additional allocations.
+  string_ptr->reset(StrDupCoTask(L"hi", 2));
+  ASSERT_EQ(1, spy()->NumAllocs());
+  EXPECT_EQ(0, spy()->NumFrees());
+  EXPECT_EQ(3 * sizeof(wchar_t), spy()->GetAlloc(0)->size);
+  EXPECT_FALSE(spy()->GetAlloc(0)->freed);
+
+  EXPECT_EQ(0, memcmp(string_ptr->get(), L"hi", 3 * sizeof(wchar_t)));
+
+  // Replacing the string frees the old memory.
+  string_ptr->reset(StrDupCoTask(L"there", 5));
+  ASSERT_EQ(2, spy()->NumAllocs());
+  EXPECT_EQ(1, spy()->NumFrees());
+  EXPECT_EQ(6 * sizeof(wchar_t), spy()->GetAlloc(1)->size);
+  EXPECT_TRUE(spy()->GetAlloc(0)->freed);
+  EXPECT_FALSE(spy()->GetAlloc(1)->freed);
+
+  // Deleting the string frees the memory.
+  delete string_ptr;
+  ASSERT_EQ(2, spy()->NumAllocs());
+  EXPECT_EQ(2, spy()->NumFrees());
+  EXPECT_TRUE(spy()->GetAlloc(1)->freed);
+}
+
+TEST_F(MallocTest, scoped_array_cotask) {
+  const size_t kSize = 5;
+  scoped_array_cotask<wchar_t*>* string_array;
+
+  // Allocate an array of 5 empty elements.
+  string_array = new scoped_array_cotask<wchar_t*>(kSize);
+  ASSERT_EQ(kSize, string_array->size());
+  ASSERT_EQ(1, spy()->NumAllocs());
+  EXPECT_EQ(0, spy()->NumFrees());
+  EXPECT_EQ(kSize * sizeof(wchar_t*), spy()->GetAlloc(0)->size);
+
+  // Populate array elements.
+  for (size_t i = 0; i < kSize; ++i) {
+    EXPECT_TRUE(NULL == (*string_array)[i]);
+    (*string_array)[i] = StrDupCoTask(L"hi", 2);
+  }
+  EXPECT_EQ(1 + kSize, spy()->NumAllocs());
+  EXPECT_EQ(0, spy()->NumFrees());
+
+  // Get is idempotent.
+  wchar_t** ptr = string_array->get();
+  EXPECT_EQ(ptr, string_array->get());
+  EXPECT_EQ(ptr, spy()->GetAlloc(0)->ptr);
+  EXPECT_EQ(0, spy()->NumFrees());
+
+  // Release is not idempotent, but does not free memory.
+  ptr = string_array->release();
+  EXPECT_TRUE(NULL == string_array->release());
+  EXPECT_EQ(ptr, spy()->GetAlloc(0)->ptr);
+  EXPECT_EQ(0, spy()->NumFrees());
+
+  // Deleting a released array does not free memory.
+  delete string_array;
+  EXPECT_EQ(0, spy()->NumFrees());
+
+  // Constructing an array from existing memory, does not cause allocations.
+  string_array = new scoped_array_cotask<wchar_t*>(kSize, ptr);
+  EXPECT_EQ(1 + kSize, spy()->NumAllocs());
+  EXPECT_EQ(0, spy()->NumFrees());
+
+  // Deleting an array frees all elements and the array.
+  delete string_array;
+  ASSERT_EQ(1 + kSize, spy()->NumAllocs());
+  EXPECT_EQ(1 + kSize, spy()->NumFrees());
+  for (size_t i = 0; i < spy()->NumAllocs(); ++i) {
+    EXPECT_TRUE(spy()->GetAlloc(i)->freed);
+  }
+}
+
+TEST_F(MallocTest, scoped_array_cotask_reset) {
+  // This test exposes a former bug, where reset did not reallocate a new
+  // array after being released.
+
+  // Allocate an empty array.
+  const size_t kSize = 5;
+  scoped_array_cotask<int*>* array = new scoped_array_cotask<int*>(kSize);
+  ASSERT_EQ(1, spy()->NumAllocs());
+
+  // Release the array, to verify it was allocated.
+  int** first_raw_array = array->release();
+  EXPECT_TRUE(NULL != first_raw_array);
+  EXPECT_FALSE(spy()->GetAlloc(0)->freed);
+
+  // Allocate another empty array.
+  array->reset(kSize);
+  ASSERT_EQ(2, spy()->NumAllocs());
+
+  // Release the second array, to verify it was allocated.
+  int** second_raw_array = array->release();
+  EXPECT_TRUE(NULL != second_raw_array);
+  EXPECT_FALSE(spy()->GetAlloc(1)->freed);
+
+  // Use the scoped_array_cotask object to dispose of the allocated arrays.
+  array->reset(kSize, first_raw_array);
+  array->reset(kSize, second_raw_array);
+  delete array;
+
+  // Check the final conditions.
+  ASSERT_EQ(2, spy()->NumAllocs());
+  for (size_t i = 0; i < spy()->NumAllocs(); ++i) {
+    EXPECT_TRUE(spy()->GetAlloc(i)->freed);
+  }
+}
diff --git a/common/security/aes.c b/base/security/aes.c
similarity index 100%
rename from common/security/aes.c
rename to base/security/aes.c
diff --git a/common/security/aes.h b/base/security/aes.h
similarity index 100%
rename from common/security/aes.h
rename to base/security/aes.h
diff --git a/common/security/b64.c b/base/security/b64.c
similarity index 100%
rename from common/security/b64.c
rename to base/security/b64.c
diff --git a/common/security/b64.h b/base/security/b64.h
similarity index 100%
rename from common/security/b64.h
rename to base/security/b64.h
diff --git a/base/security/build.scons b/base/security/build.scons
new file mode 100644
index 0000000..d8703a4
--- /dev/null
+++ b/base/security/build.scons
@@ -0,0 +1,52 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========================================================================
+
+Import('env')
+
+#
+# Build Security library
+#
+security_env = env.Clone()
+
+security_env.Append(
+    CPPPATH = [
+        '$MAIN_DIR/third_party/c99/include',
+        ],
+    CCFLAGS = [
+        '/wd4242',  # conversion from 'type1' to 'type2', possible loss of data
+        '/wd4244',  # conversion from 'type1' to 'type2', possible loss of data
+        '/wd4510',  # default constructor could not be generated
+        '/wd4610',  # object 'class' can never be instantiated
+        ],
+)
+
+
+security_inputs = [
+    'aes.c',
+    'b64.c',
+    'challenger.cc',
+    'hmac.c',
+    'md5.c',
+    'rc4.c',
+    'rsa.cc',
+    'sha.c',
+    ]
+
+# Precompiled headers cannot be used with C files.
+security_env.ComponentStaticLibrary('security',
+                                    security_inputs,
+                                    use_pch_default=False)
diff --git a/common/security/challenger.cc b/base/security/challenger.cc
similarity index 100%
rename from common/security/challenger.cc
rename to base/security/challenger.cc
diff --git a/common/security/challenger.h b/base/security/challenger.h
similarity index 100%
rename from common/security/challenger.h
rename to base/security/challenger.h
diff --git a/common/security/hash-internal.h b/base/security/hash-internal.h
similarity index 100%
rename from common/security/hash-internal.h
rename to base/security/hash-internal.h
diff --git a/common/security/hmac.c b/base/security/hmac.c
similarity index 100%
rename from common/security/hmac.c
rename to base/security/hmac.c
diff --git a/common/security/hmac.h b/base/security/hmac.h
similarity index 100%
rename from common/security/hmac.h
rename to base/security/hmac.h
diff --git a/common/security/md5.c b/base/security/md5.c
similarity index 100%
rename from common/security/md5.c
rename to base/security/md5.c
diff --git a/common/security/md5.h b/base/security/md5.h
similarity index 100%
rename from common/security/md5.h
rename to base/security/md5.h
diff --git a/common/security/rc4.c b/base/security/rc4.c
similarity index 100%
rename from common/security/rc4.c
rename to base/security/rc4.c
diff --git a/common/security/rc4.h b/base/security/rc4.h
similarity index 100%
rename from common/security/rc4.h
rename to base/security/rc4.h
diff --git a/common/security/rsa.cc b/base/security/rsa.cc
similarity index 100%
rename from common/security/rsa.cc
rename to base/security/rsa.cc
diff --git a/common/security/rsa.h b/base/security/rsa.h
similarity index 100%
rename from common/security/rsa.h
rename to base/security/rsa.h
diff --git a/common/security/sha.c b/base/security/sha.c
similarity index 100%
rename from common/security/sha.c
rename to base/security/sha.c
diff --git a/common/security/sha.h b/base/security/sha.h
similarity index 100%
rename from common/security/sha.h
rename to base/security/sha.h
diff --git a/base/serializable_object.cc b/base/serializable_object.cc
new file mode 100644
index 0000000..ae0e8d4
--- /dev/null
+++ b/base/serializable_object.cc
@@ -0,0 +1,375 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Provides the base class framework for those objects to be serialized
+//
+// HACK:
+//
+// During the serialization/deserialization of vector<T> members, we
+// coerce the type from vector<T> to vector<byte> since we are unable to
+// get the real type vector<T> at later time. This is feasible because
+// vector<T> in the vector library we are linking now keeps track of
+// only front() and end() pointers and use them to calculate size().
+// We need to check whether this approach is still OK if we upgrade the
+// standard libraries.
+
+#include "omaha/base/serializable_object.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+
+namespace omaha {
+
+// Serialize
+bool SerializableObject::Serialize(std::vector<byte>* data) const {
+  ASSERT(data, (_T("")));
+
+  // Estimate how much memory we need
+  int size = data->size();
+  for (size_t i = 0; i < members_.size(); ++i)
+    size += (members_[i].size > 0) ? members_[i].size : sizeof(int);
+
+  // Reserve the estimated size fo vector memory
+  data->reserve(size);
+
+  // Copy over the data
+  for (size_t i = 0; i < members_.size(); ++i) {
+    switch (members_[i].type) {
+      case SERIALIZABLE_VALUE_TYPE: {
+        int pos = data->size();
+        data->resize(data->size() + members_[i].size);
+        memcpy(&(*data)[pos], members_[i].ptr, members_[i].size);
+        break;
+      }
+
+      case SERIALIZABLE_CSTRING: {
+        CString* s = reinterpret_cast<CString*>(members_[i].ptr);
+        SerializeValueList(data,
+                           reinterpret_cast<const byte*>(s->GetString()),
+                           sizeof(TCHAR),
+                           s->GetLength());
+        break;
+      }
+
+      case SERIALIZABLE_NESTED_OBJECT: {
+        SerializableObject* nested_obj =
+            reinterpret_cast<SerializableObject*>(members_[i].ptr);
+        if (!nested_obj->Serialize(data))
+          return false;
+        break;
+      }
+
+      case SERIALIZABLE_VECTOR | SERIALIZABLE_VALUE_TYPE: {
+        // Hack: coerce vector<T> to vector<byte>
+        std::vector<byte>* v =
+            reinterpret_cast<std::vector<byte>*>(members_[i].ptr);
+        if (v->size() != 0) {
+          SerializeValueList(data,
+                             &v->front(),
+                             members_[i].size,
+                             v->size() / members_[i].size);
+        } else {
+          SerializeValueList(data,
+                             NULL,
+                             members_[i].size,
+                             v->size() / members_[i].size);
+        }
+        break;
+      }
+
+      case SERIALIZABLE_VECTOR | SERIALIZABLE_CSTRING: {
+        std::vector<CString>* v =
+            reinterpret_cast<std::vector<CString>*>(members_[i].ptr);
+        SerializeSizeAndCount(data, 1, v->size());
+        if (!v->empty()) {
+          for (std::vector<CString>::const_iterator it = v->begin();
+               it != v->end();
+               ++it) {
+            SerializeValueList(data,
+                               reinterpret_cast<const byte*>(it->GetString()),
+                               sizeof(TCHAR),
+                               it->GetLength());
+          }
+        }
+        break;
+      }
+
+      case SERIALIZABLE_VECTOR | SERIALIZABLE_NESTED_OBJECT: {
+        if (!SerializeVectorNestedObject(data, members_[i].ptr))
+          return false;
+        break;
+      }
+
+      default:
+        ASSERT(false, (_T("")));
+        return false;
+    }
+  }
+
+  return true;
+}
+
+// Serialize the size and count values
+void SerializableObject::SerializeSizeAndCount(std::vector<byte>* data,
+                                               int size,
+                                               int count) const {
+  ASSERT(data, (_T("")));
+  ASSERT(size >= 0, (_T("")));
+
+  // Get current size
+  int pos = data->size();
+
+  // Adjust the size of the data buffer
+  data->resize(data->size() + 2 * sizeof(int));
+
+  // Get pointer to the position of data buffer we start to write
+  byte* ptr = &((*data)[pos]);
+
+  // Push size
+  memcpy(ptr, &size, sizeof(int));
+  ptr += sizeof(int);
+
+  // Push count
+  memcpy(ptr, &count, sizeof(int));
+  ptr += sizeof(int);
+}
+
+// Serialize a list of value-typed elements
+//
+// Args:
+//   ser_data:  pointer to the vector for the serialized data
+//   raw_data:  pointer to the raw data to be serialized
+//   size:      the size of the element in the list
+//   count:     the number of the elements in the list
+void SerializableObject::SerializeValueList(std::vector<byte>* ser_data,
+                                            const byte* raw_data,
+                                            int size,
+                                            int count) const {
+  ASSERT(ser_data, (_T("")));
+  ASSERT(size > 0, (_T("")));
+
+  // Serialize the size and count values
+  SerializeSizeAndCount(ser_data, size, count);
+
+  // Push data
+  if (count > 0) {
+    // Get current size
+    int pos = ser_data->size();
+
+    // Adjust the size of the data buffer
+    ser_data->resize(ser_data->size() + count * size);
+
+    // Get pointer to the position of data buffer we start to write
+    byte* ptr = &((*ser_data)[pos]);
+
+    // Copy data
+    memcpy(ptr, raw_data, count * size);
+  }
+}
+
+// Deserialize
+bool SerializableObject::Deserialize(byte* data, int size, uint32 version) {
+  ASSERT(data, (_T("")));
+  ASSERT(size > 0, (_T("")));
+
+  byte* tail = data + size;
+  byte** data_ptr = &data;
+  if (!DeserializeHelper(data_ptr, size, version))
+    return false;
+
+  if (*data_ptr != tail) {
+    UTIL_LOG(LE, (_T("[SerializableObject::Deserialize]")
+                  _T("[failed to deserialize all data]")));
+    return false;
+  }
+
+  return true;
+}
+
+// Deserialize helper
+bool SerializableObject::DeserializeHelper(byte** data,
+                                           int size,
+                                           uint32 version) {
+  ASSERT(data, (_T("")));
+  ASSERT(size > 0, (_T("")));
+
+  byte* tail = *data + size;
+
+  for (size_t i = 0; i < members_.size(); ++i) {
+    // Ignore those members which are persisted in newer versions
+    if (version != kLatestSerializableVersion &&
+        members_[i].version  > version) {
+      continue;
+    }
+
+    switch (members_[i].type) {
+      case SERIALIZABLE_VALUE_TYPE:
+        if (*data + members_[i].size > tail) {
+          UTIL_LOG(L6, (_T("[SerializableObject::DeserializeHelper]")
+                        _T("[overflow when deserializing value type]")));
+          return false;
+        }
+        memcpy(members_[i].ptr, *data, members_[i].size);
+        *data += members_[i].size;
+        break;
+
+      case SERIALIZABLE_CSTRING: {
+        std::vector<byte> deser_data;
+        if (!DeserializeValueList(&deser_data,
+                                  members_[i].size,
+                                  data,
+                                  tail - *data))
+          return false;
+        CString* s = reinterpret_cast<CString*>(members_[i].ptr);
+        if (deser_data.size() != 0) {
+          s->SetString(reinterpret_cast<const TCHAR*>(&deser_data.front()),
+                       deser_data.size() / members_[i].size);
+        } else {
+          s->SetString(_T(""));
+        }
+        break;
+      }
+
+      case SERIALIZABLE_NESTED_OBJECT: {
+        SerializableObject* nested_obj =
+            reinterpret_cast<SerializableObject*>(members_[i].ptr);
+        if (!nested_obj->DeserializeHelper(data, size, version))
+          return false;
+        break;
+      }
+
+      case SERIALIZABLE_VECTOR | SERIALIZABLE_VALUE_TYPE: {
+        // Hack: coerce vector<T> to vector<byte>
+        std::vector<byte>* v =
+            reinterpret_cast<std::vector<byte>*>(members_[i].ptr);
+        if (!DeserializeValueList(v, members_[i].size, data, tail - *data))
+          return false;
+        break;
+      }
+
+      case SERIALIZABLE_VECTOR | SERIALIZABLE_CSTRING: {
+        std::vector<CString>* v =
+              reinterpret_cast<std::vector<CString>*>(members_[i].ptr);
+        int count = 0;
+        if (!DeserializeSizeAndCount(&count, 1, data, tail - *data))
+          return false;
+        for (int j = 0; j < count; ++j) {
+          std::vector<byte> deser_data;
+          if (!DeserializeValueList(&deser_data,
+                                    members_[i].size,
+                                    data,
+                                    tail - *data))
+            return false;
+
+          CString s;
+          if (deser_data.size() != 0) {
+            s = CString(reinterpret_cast<const TCHAR*>(&deser_data.front()),
+                        deser_data.size() / members_[i].size);
+          }
+          v->push_back(s);
+        }
+        break;
+      }
+
+      case SERIALIZABLE_VECTOR | SERIALIZABLE_NESTED_OBJECT: {
+        if (!DeserializeVectorNestedObject(data,
+                                           tail - *data,
+                                           members_[i].ptr,
+                                           version))
+          return false;
+        break;
+      }
+
+      default:
+        ASSERT(false, (_T("")));
+        break;
+    }
+  }
+
+  return true;
+}
+
+// Serialize the size and count values
+bool SerializableObject::DeserializeSizeAndCount(int* count,
+                                                 int size,
+                                                 byte** ser_data,
+                                                 int ser_size) const {
+  ASSERT(ser_data, (_T("")));
+  ASSERT(count, (_T("")));
+
+  byte* ser_tail = *ser_data + ser_size;
+
+  // Check to make sure that the serialization data should at least contain
+  // 'size' and 'count'
+  if (*ser_data + 2 * sizeof(int) > ser_tail) {
+    UTIL_LOG(L6, (_T("[SerializableObject::DeserializeSizeAndCount]")
+                  _T("[overflow when deserializing size and count]")));
+    return false;
+  }
+
+  // Get size
+  // If the passing size is 0, skip the size check
+  int size2 = *(reinterpret_cast<const int*>(*ser_data));
+  *ser_data += sizeof(int);
+  if (size && size != size2)
+    return false;
+
+  // Get count
+  *count = *(reinterpret_cast<const int*>(*ser_data));
+  *ser_data += sizeof(int);
+
+  return true;
+}
+
+// Deserialize a list of value-typed elements
+//
+// Args:
+//   ser_data:  pointer to the vector for the serialized data
+//   size:      the size of the element in the list
+//   raw_data:  pointer to the raw data to be serialized
+//   ser_size:  size of the serization data
+bool SerializableObject::DeserializeValueList(std::vector<byte>* raw_data,
+                                              int size,
+                                              byte** ser_data,
+                                              int ser_size) {
+  ASSERT(raw_data, (_T("")));
+  ASSERT(ser_data, (_T("")));
+
+  byte* ser_tail = *ser_data + ser_size;
+
+  // Deserialize the size and count values
+  int count = 0;
+  bool ret = DeserializeSizeAndCount(&count, size, ser_data, ser_size);
+  if (!ret)
+    return false;
+
+  // Check to make sure that the serialization data is in the right size
+  if (*ser_data + count * size > ser_tail) {
+    UTIL_LOG(L6, (_T("[SerializableObject::DeserializeValueList]")
+                  _T("[overflow when deserializing value list]")));
+    return false;
+  }
+
+  // Get data
+  raw_data->resize(size * count);
+  if (count > 0) {
+    memcpy(&raw_data->front(), *ser_data, count * size);
+    *ser_data += count * size;
+  }
+
+  return true;
+}
+
+}  // namespace omaha
+
diff --git a/base/serializable_object.h b/base/serializable_object.h
new file mode 100644
index 0000000..5dd712d
--- /dev/null
+++ b/base/serializable_object.h
@@ -0,0 +1,373 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Declares class SerializableObject
+//
+// Provides the base class framework for those objects to be serialized
+//
+// Currently we support the serialization for the following member type
+//
+//   1) Value-type object
+//   2) CString
+//   3) Nested serializable object
+//   4) Vector of the value-type objects
+//   5) Vector of CString
+//   6) Vector of serializable objects
+//
+// Usage:
+//
+//   1) Declare the object class, which you want to serialize, to be derived
+//      from SerializableObject
+//   2) In its constructor, call AddSerializableMember(...) to add those fields
+//      to be included in the serialization
+//   3) If you need to serialize a vector of serializable objects,
+//      a) The inner object class has to implement copy constructor and
+//         operator =
+//      b) The outer object class has to override SerializeVectorNestedObject()
+//         and DeserializeVectorNestedObject() (see sample)
+//
+// Versioning support:
+//
+//   To add new fields in the new version, call
+//        AddSerializableMember(version, &member);
+//   If "version" is not given, it is 0 by default.
+//
+//   To deserialize the data for latest version, call
+//        Deserialize(data, size, kLatestSerializableVersion);
+//   To deserialize the data for a particular version, call
+//        Deserialize(data, size, version);
+//
+// Sample:
+//
+//   class FooObject : public SerializableObject {
+//     ...
+//   };
+//
+//   class BarObject : public SerializableObject {
+//    public:
+//     BarObject() {
+//      AddSerializableMember(&value1_);
+//      AddSerializableMember(&value2_);
+//      AddSerializableMember(&value3_);
+//      AddSerializableMember(&value4_);
+//      AddSerializableMember(&value5_);
+//      AddSerializableMember(&value6_);
+//      AddSerializableMember(1, &value7_);     // New version
+//     }
+//
+//    protected:
+//     virtual bool SerializeVectorNestedObject(std::vector<byte>* data,
+//                                              const byte* ptr) const {
+//       ASSERT(data, (_T("")));
+//       ASSERT(ptr, (_T("")));
+//       if (ptr == reinterpret_cast<const byte*>(&value6_))
+//         return SerializeVectorNestedObjectHelper(data, &value6_);
+//       return false;
+//     }
+//
+//     virtual bool DeserializeVectorNestedObject(byte** data,
+//                                                int size,
+//                                                byte* ptr,
+//                                                uint32 version) {
+//       ASSERT(data, (_T("")));
+//       ASSERT(*data, (_T("")));
+//       ASSERT(ptr, (_T("")));
+//       if (ptr == reinterpret_cast<byte*>(&value6_))
+//         return DeserializeVectorNestedObjectHelper(data,
+//                                                    size,
+//                                                    &value6_,
+//                                                    version);
+//       return false;
+//     }
+//
+//    private:
+//     int value1_;
+//     CString value2_;
+//     FooObject value3_;
+//     std::vector<byte> value4_;
+//     std::vector<CString> value5_;
+//     std::vector<BarObject> value6_;
+//     int value7_;
+//   };
+//
+// Binary format:
+//
+//   1) Value type: data
+//      e.g.  100           =>   64 00 00 00
+//   2) CString:    size count data
+//      e.g.  "ABC"         =>   02 00 00 00 03 00 00 00 41 00 42 00 43 00
+//   3) Vector:     size count data
+//      e.g.  vector<int> = {1, 2}
+//                          =>   04 00 00 00 02 00 00 00 01 00 00 00 02 00 00 00
+//
+// TODO(omaha):
+//   1) Define struct TypeTrait for all type-related info
+//   2) Initialize TypeTrait on per-type basis instead of per-object basis
+
+#ifndef OMAHA_COMMON_SERIALIZABLE_OBJECT_H_
+#define OMAHA_COMMON_SERIALIZABLE_OBJECT_H_
+
+#include <vector>
+#include "base/basictypes.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/type_utils.h"
+
+namespace omaha {
+
+// Constants
+const uint32 kLatestSerializableVersion = 0xFFFFFFFF;
+
+// Serializable object
+class SerializableObject {
+ private:
+  // Define SerializableMemberType, for internal use
+  typedef uint32 SerializableMemberType;
+
+  #define SERIALIZABLE_VALUE_TYPE     1
+  #define SERIALIZABLE_CSTRING        2
+  #define SERIALIZABLE_NESTED_OBJECT  3
+  #define SERIALIZABLE_VECTOR         0x8000
+
+  // Serializable member info
+  struct SerializableMemberInfo {
+    byte* ptr;                      // Pointers to the serializable member
+    SerializableMemberType type;    // Type of the serializable member
+    int size;                       // Size of the serializable member
+    uint32 version;                 // Version when the member is added
+
+    SerializableMemberInfo()
+        : ptr(NULL), type(SERIALIZABLE_VALUE_TYPE), size(0) {}
+  };
+
+ public:
+  // Constructor
+  SerializableObject() {}
+
+  // Destructor
+  virtual ~SerializableObject() {}
+
+  // Serialize
+  bool Serialize(std::vector<byte>* data) const;
+
+  // Deserialize the data for the latest version
+  bool Deserialize(byte* data, int size) {
+    return Deserialize(data, size, kLatestSerializableVersion);
+  }
+
+  // Deserialize the data for a particular version
+  bool Deserialize(byte* data, int size, uint32 version);
+
+ protected:
+  // Clear the serializable member list
+  void ClearSerializableMemberList() {
+    members_.clear();
+  }
+
+  // Add value-typed member to the serializable member list
+  template<typename T>
+  void AddSerializableMember(T* ptr) {
+    return AddSerializableMember(0, ptr);
+  }
+
+  // Add value-typed member to the serializable member list
+  template<typename T>
+  void AddSerializableMember(uint32 version, T* ptr) {
+    if (SUPERSUBCLASS(SerializableObject, T)) {
+      #pragma warning(push)
+      // reinterpret_cast used between related classes
+      #pragma warning(disable : 4946)
+      return AddSerializableMember(version,
+                                   reinterpret_cast<SerializableObject*>(ptr),
+                                   sizeof(T));
+      #pragma warning(pop)
+    }
+    SerializableMemberInfo member;
+    member.ptr = reinterpret_cast<byte*>(ptr);
+    member.type = SERIALIZABLE_VALUE_TYPE;
+    member.size = sizeof(T);
+    member.version = version;
+    members_.push_back(member);
+  }
+
+  // Add CString-typed member to the serializable member list
+  void AddSerializableMember(CString* ptr) {
+    AddSerializableMember(0, ptr);
+  }
+
+  // Add CString-typed member to the serializable member list
+  void AddSerializableMember(uint32 version, CString* ptr) {
+    SerializableMemberInfo member;
+    member.ptr = reinterpret_cast<byte*>(ptr);
+    member.type = SERIALIZABLE_CSTRING;
+    member.size = sizeof(TCHAR);
+    member.version = version;
+    members_.push_back(member);
+  }
+
+  // Add nested serializable member to the serializable member list
+  void AddSerializableMember(SerializableObject* ptr, int size) {
+    AddSerializableMember(0, ptr, size);
+  }
+
+  // Add nested serializable member to the serializable member list
+  void AddSerializableMember(uint32 version,
+                             SerializableObject* ptr,
+                             int size) {
+    SerializableMemberInfo member;
+    member.ptr = reinterpret_cast<byte*>(ptr);
+    member.type = SERIALIZABLE_NESTED_OBJECT;
+    member.size = size;
+    member.version = version;
+    members_.push_back(member);
+  }
+
+  // Add vector-typed member to the serializable member list
+  template<typename T>
+  void AddSerializableMember(std::vector<T>* ptr) {
+    AddSerializableMember(0, ptr);
+  }
+
+  // Add vector-typed member to the serializable member list
+  template<typename T>
+  void AddSerializableMember(uint32 version, std::vector<T>* ptr) {
+    SerializableMemberInfo member;
+    member.ptr = reinterpret_cast<byte*>(ptr);
+    member.version = version;
+
+    if (SUPERSUBCLASS(CString, T)) {
+      member.type =
+          static_cast<SerializableMemberType>(SERIALIZABLE_VECTOR |
+                                              SERIALIZABLE_CSTRING);
+      member.size = sizeof(TCHAR);
+    } else if (SUPERSUBCLASS(SerializableObject, T)) {
+      member.type =
+          static_cast<SerializableMemberType>(SERIALIZABLE_VECTOR |
+                                              SERIALIZABLE_NESTED_OBJECT);
+      member.size = sizeof(T);
+    } else {
+      member.type =
+          static_cast<SerializableMemberType>(SERIALIZABLE_VECTOR |
+                                              SERIALIZABLE_VALUE_TYPE);
+      member.size = sizeof(T);
+    }
+
+    members_.push_back(member);
+  }
+
+  // If there is a vector of SerializableObject to be serialized, the derived
+  // class need to provide the implementation
+  virtual bool SerializeVectorNestedObject(std::vector<byte>*,
+                                           const byte*) const {
+    ASSERT(false, (_T("Provide the implementation in the derived class.")));
+    return false;
+  }
+
+  // Helper method to serialize a vector of SerializableObject
+  template<typename T>
+  bool SerializeVectorNestedObjectHelper(std::vector<byte>* data,
+                                         const std::vector<T>* list) const {
+    ASSERT(data, (_T("")));
+    ASSERT(list, (_T("")));
+    ASSERT(SUPERSUBCLASS(SerializableObject, T), (_T("")));
+
+    // Size of SerializableObject is unknown
+    SerializeSizeAndCount(data, 0, list->size());
+    for (size_t i = 0; i < list->size(); ++i) {
+      // To work around compiler complaint while using dynamic_cast
+      SerializableObject* so =
+          const_cast<SerializableObject*>(
+              static_cast<const SerializableObject*>(&(*list)[i]));
+      bool res = so->Serialize(data);
+      if (!res)
+        return false;
+    }
+    return true;
+  }
+
+  // If there is a vector of SerializableObject to be serialized, the derived
+  // class need to provide the implementation
+  virtual bool DeserializeVectorNestedObject(byte**, int, byte*, uint32) {
+    ASSERT(false, (_T("provide the implementation in the derived class.")));
+    return false;
+  }
+
+  // Helper method to deserialize a vector of SerializableObject
+  template<typename T>
+  bool DeserializeVectorNestedObjectHelper(byte** data,
+                                           int size,
+                                           std::vector<T>* list,
+                                           uint32 version) {
+    ASSERT(data, (_T("")));
+    ASSERT(*data, (_T("")));
+    ASSERT(size, (_T("")));
+    ASSERT(list, (_T("")));
+    ASSERT(SUPERSUBCLASS(SerializableObject, T), (_T("")));
+
+    byte* tail = *data + size;
+
+    // Size of SerializableObject is unknown
+    int count = 0;
+    bool res = DeserializeSizeAndCount(&count, 0, data, size);
+    if (!res)
+      return false;
+
+    for (int i = 0; i < count; ++i) {
+      T obj;
+      bool res = obj.DeserializeHelper(data, tail - *data, version);
+      if (!res)
+        return false;
+      list->push_back(obj);
+    }
+    return true;
+  }
+
+ private:
+  // Serialize the size and count values
+  void SerializeSizeAndCount(std::vector<byte>* data,
+                             int size,
+                             int count) const;
+
+  // Serialize a list of value-typed elements
+  void SerializeValueList(std::vector<byte>* ser_data,
+                          const byte* raw_data,
+                          int size,
+                          int count) const;
+
+  // Deserialize helper
+  bool DeserializeHelper(byte** data, int size, uint32 version);
+
+  // Deserialize the size and count values
+  bool DeserializeSizeAndCount(int* count,
+                               int size,
+                               byte** ser_data,
+                               int ser_size) const;
+
+  // Deserialize a list of value-typed elements
+  bool DeserializeValueList(std::vector<byte>* raw_data,
+                            int size,
+                            byte** ser_data,
+                            int ser_size);
+
+  // List of serializable members
+  std::vector<SerializableMemberInfo> members_;
+
+  // We need to initialize TypeTrait on per-type basis instead of per-object
+  // basis and remove the following use of macro.
+  DISALLOW_EVIL_CONSTRUCTORS(SerializableObject);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_SERIALIZABLE_OBJECT_H_
diff --git a/base/serializable_object_unittest.cc b/base/serializable_object_unittest.cc
new file mode 100644
index 0000000..8ae5fec
--- /dev/null
+++ b/base/serializable_object_unittest.cc
@@ -0,0 +1,248 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/serializable_object.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+// Template test function to round-trip an object through its serialized form.
+// Requires the object to implement SetTestValues() and VerifyTestValues().
+template <class T>
+void TestSerializeRoundTrip() {
+  T in;
+  T out;
+  std::vector<byte> data;
+
+  in.SetTestValues();
+  in.VerifyTestValues();  // sanity check to catch broken tests
+  EXPECT_TRUE(in.Serialize(&data));
+  EXPECT_TRUE(out.Deserialize(&data[0], data.size()));
+  out.VerifyTestValues();
+}
+
+
+// Ordinary values.
+class SimpleValues : public SerializableObject {
+ public:
+  SimpleValues() {
+    AddSerializableMember(&value1_);
+    AddSerializableMember(&value2_);
+    AddSerializableMember(&value3_);
+    AddSerializableMember(&value4_);
+  }
+
+  virtual void SetTestValues() {
+    value1_ = 452;
+    value2_ = 'Z';
+    value3_ = false;
+    value4_ = 9276554;
+  }
+
+  virtual void VerifyTestValues() {
+    EXPECT_EQ(452, value1_);
+    EXPECT_EQ('Z', value2_);
+    EXPECT_EQ(false, value3_);
+    EXPECT_EQ(9276554, value4_);
+  }
+
+ private:
+  int value1_;
+  char value2_;
+  bool value3_;
+  int value4_;
+};
+
+TEST(SerializableObjectTest, SimpleValues) {
+  TestSerializeRoundTrip<SimpleValues>();
+}
+
+
+// Strings.
+const TCHAR kString1[] = _T("an example\tvalue\n");
+const TCHAR kString2[] = _T("");
+const TCHAR kString3[] = _T("and the mome raths outgrabe");
+
+class StringValues : public SerializableObject {
+ public:
+  StringValues() {
+    AddSerializableMember(&string1_);
+    AddSerializableMember(&string2_);
+    AddSerializableMember(&string3_);
+  }
+
+  virtual void SetTestValues() {
+    string1_ = kString1;
+    string2_ = kString2;
+    string3_ = kString3;
+  }
+
+  virtual void VerifyTestValues() {
+    EXPECT_STREQ(kString1, string1_);
+    EXPECT_STREQ(kString2, string2_);
+    EXPECT_STREQ(kString3, string3_);
+  }
+
+ private:
+  CString string1_;
+  CString string2_;
+  CString string3_;
+};
+
+TEST(SerializableObjectTest, StringValues) {
+  TestSerializeRoundTrip<StringValues>();
+}
+
+
+// Nested objects.
+class NestedObjects : public SerializableObject {
+ public:
+  NestedObjects() {
+    AddSerializableMember(&simple_values_);
+    AddSerializableMember(&string_values_);
+  }
+
+  virtual void SetTestValues() {
+    simple_values_.SetTestValues();
+    string_values_.SetTestValues();
+  }
+
+  virtual void VerifyTestValues() {
+    simple_values_.VerifyTestValues();
+    string_values_.VerifyTestValues();
+  }
+
+ private:
+  SimpleValues simple_values_;
+  StringValues string_values_;
+};
+
+TEST(SerializableObjectTest, NestedObjects) {
+  TestSerializeRoundTrip<NestedObjects>();
+}
+
+
+// Vector of values.
+class ValueVector : public SerializableObject {
+ public:
+  ValueVector() {
+    AddSerializableMember(&vector_);
+    AddSerializableMember(&empty_vector_);
+  }
+
+  virtual void SetTestValues() {
+    vector_.push_back(5);
+    vector_.push_back(8);
+    vector_.push_back(13);
+  }
+
+  virtual void VerifyTestValues() {
+    EXPECT_EQ(0, empty_vector_.size());
+    EXPECT_EQ(3, vector_.size());
+    EXPECT_EQ(5, vector_[0]);
+    EXPECT_EQ(8, vector_[1]);
+    EXPECT_EQ(13, vector_[2]);
+  }
+
+ private:
+  std::vector<int> vector_;
+  std::vector<int> empty_vector_;
+};
+
+TEST(SerializableObjectTest, ValueVector) {
+  TestSerializeRoundTrip<ValueVector>();
+}
+
+
+// Vector of objects.
+class InnerObject : public SerializableObject {
+ public:
+  InnerObject() : value_(-1) {
+    AddSerializableMember(&value_);
+  }
+
+  explicit InnerObject(int i) : value_(i) {
+    AddSerializableMember(&value_);
+  }
+
+  InnerObject(const InnerObject& other) {
+    AddSerializableMember(&value_);
+    value_ = other.value_;
+  }
+
+  InnerObject& operator=(const InnerObject& other) {
+    value_ = other.value_;
+    return *this;
+  }
+
+  int value() {
+    return value_;
+  }
+
+ private:
+  int value_;
+};
+
+class ObjectVector : public SerializableObject {
+ public:
+  ObjectVector() {
+    AddSerializableMember(&vector_);
+  }
+
+  virtual void SetTestValues() {
+    vector_.push_back(InnerObject(21));
+    vector_.push_back(InnerObject(34));
+    vector_.push_back(InnerObject(55));
+  }
+
+  virtual void VerifyTestValues() {
+    EXPECT_EQ(3, vector_.size());
+    EXPECT_EQ(21, vector_[0].value());
+    EXPECT_EQ(34, vector_[1].value());
+    EXPECT_EQ(55, vector_[2].value());
+  }
+
+  virtual bool SerializeVectorNestedObject(std::vector<byte>* data,
+                                           const byte* ptr) const {
+    EXPECT_TRUE(data);
+    EXPECT_TRUE(ptr);
+    if (ptr == reinterpret_cast<const byte*>(&vector_))
+      return SerializeVectorNestedObjectHelper(data, &vector_);
+    return false;
+  }
+
+  virtual bool DeserializeVectorNestedObject(byte** data,
+                                             int size,
+                                             byte* ptr,
+                                             uint32 version) {
+    EXPECT_TRUE(data);
+    EXPECT_TRUE(ptr);
+    if (ptr == reinterpret_cast<byte*>(&vector_)) {
+      return DeserializeVectorNestedObjectHelper(data, size,
+                                                 &vector_, version);
+    }
+    return false;
+  }
+
+ private:
+  std::vector<InnerObject> vector_;
+};
+
+TEST(SerializableObjectTest, ObjectVector) {
+  TestSerializeRoundTrip<ObjectVector>();
+}
+
+}  // namespace omaha
+
diff --git a/base/service_utils.cc b/base/service_utils.cc
new file mode 100644
index 0000000..e190bd5
--- /dev/null
+++ b/base/service_utils.cc
@@ -0,0 +1,360 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+//
+// Service-related utilities.
+//
+
+#include "omaha/base/service_utils.h"
+
+#include <windows.h>
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/smart_handle.h"
+#include "omaha/base/string.h"
+#include "omaha/base/timer.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+HRESULT ScmDatabase::EnumerateServices(
+    ScmDatabase::EnumerateServicesCallback callback,
+    void* callback_context) {
+  ASSERT1(callback);
+  if (!callback)
+    return E_POINTER;
+
+  const wchar_t* kServicesRegKeyFromRoot =
+    L"SYSTEM\\CurrentControlSet\\Services";
+
+  HRESULT hr = E_FAIL;
+
+  RegKey services_key;
+  if (FAILED(hr = services_key.Open(HKEY_LOCAL_MACHINE,
+                                    kServicesRegKeyFromRoot,
+                                    KEY_ENUMERATE_SUB_KEYS))) {
+    ASSERT1(false);
+    REPORT(false, R_ERROR, (L"Couldn't open services subkey, hr=0x%x", hr),
+           9834572);
+    return hr;
+  }
+
+  CString service_name;
+  int key_index = 0;
+  while (SUCCEEDED(hr = services_key.GetSubkeyNameAt(key_index++,
+                                                     &service_name))) {
+    hr = callback(callback_context, service_name);
+    if (FAILED(hr) || hr == S_FALSE) {
+      // Callback asked to terminate enumeration.
+      return hr;
+    }
+  }
+
+  if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)) {
+    ASSERT1(false);
+    REPORT(false, R_ERROR, (L"Failed enumerating service subkeys: 0x%x", hr),
+           1499372);
+    return hr;
+  }
+
+  return S_OK;
+}
+
+bool ScmDatabase::IsServiceStateEqual(SC_HANDLE service, DWORD state) {
+  ASSERT1(service);
+
+  DWORD bytes_needed_ignored = 0;
+  byte buffer[8 * 1024] = { 0 };
+  QUERY_SERVICE_CONFIG* service_config =
+    reinterpret_cast<QUERY_SERVICE_CONFIG*>(buffer);
+  if (!::QueryServiceConfig(service, service_config, sizeof(buffer),
+                            &bytes_needed_ignored)) {
+    ASSERT(false, (L"Failed to query service config, perhaps handle is missing "
+                   L"SERVICE_QUERY_CONFIG rights?"));
+    return false;
+  }
+
+  return (service_config[0].dwStartType == state);
+}
+
+bool ScmDatabase::IsServiceMarkedDeleted(SC_HANDLE service) {
+  ASSERT1(service);
+
+  // Services that have been marked deleted are always in the
+  // SERVICE_DISABLED state.  The converse is not true, and unfortunately
+  // there is no way to check if a service has been marked deleted except by
+  // attempting to change one of its configuration parameters, at which
+  // point you get a specific error indicating it has been marked deleted.
+  //
+  // The following call to ChangeServiceConfig does not actually change any
+  // of the service's configuration, but should hopefully return the
+  // specific error if the service has been marked deleted.
+  if (!::ChangeServiceConfig(service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
+                             SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL,
+                             NULL, NULL, NULL) &&
+      ::GetLastError() == ERROR_SERVICE_MARKED_FOR_DELETE) {
+    ASSERT1(IsServiceStateEqual(service, SERVICE_DISABLED));
+    return true;
+  } else {
+    return false;
+  }
+}
+
+HRESULT ServiceInstall::UninstallByPrefix(void* context,
+                                          const wchar_t* service_name) {
+  ASSERT1(context != NULL);
+  if (!context)
+    return E_POINTER;
+
+  UninstallByPrefixParams* params =
+    reinterpret_cast<UninstallByPrefixParams*>(context);
+
+  if (String_StartsWith(service_name, params->prefix, true) &&
+      lstrcmpiW(service_name, params->unless_matches) != 0) {
+    // The service must be stopped before attempting to remove it from the
+    // database. Otherwise, the SCM database remains dirty and all service
+    // functions return ERROR_SERVICE_MARKED_FOR_DELETE until the system is
+    // restarted.
+    StopService(service_name);
+
+    scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
+    if (!scm) {
+      HRESULT hr = HRESULTFromLastError();
+      ASSERT1(false);
+      REPORT(false, R_ERROR, (L"Failed to open SCM: 0x%x", hr), 77223399);
+      return hr;
+    }
+    scoped_service service(::OpenService(get(scm),
+                                         service_name,
+                                         SERVICE_CHANGE_CONFIG | DELETE));
+    if (service) {
+      // The service may not get deleted immediately; if there are handles to
+      // it open, it won't get deleted until the last one is closed.  If the
+      // service is running, it won't get deleted immediately but rather will be
+      // marked for deletion (which happens on next reboot).  Having to wait for
+      // a while and even until reboot doesn't matter much to us as our new
+      // service is installed under a new name and we are just cleaning up old
+      // ones.
+      if (!::DeleteService(get(service))) {
+        // We do not assert but just report so that we know if this happens
+        // abnormally often.
+        if (::GetLastError() == ERROR_SERVICE_MARKED_FOR_DELETE) {
+          REPORT(false, R_INFO,
+                 (L"Failed to immediately delete service %s", service_name),
+                 5440098);
+        } else {
+          ASSERT(false, (L"Failed to delete service %s, error %d",
+                         service_name, ::GetLastError()));
+        }
+        // DO NOT return an error here; we want to keep going through all the
+        // services.
+      } else {
+        SERVICE_LOG(L1,
+                    (L"Deleted old service %s", service_name));
+      }
+    } else {
+      // Per documentation of the EnumerateServicesCallback interface we can
+      // expect not to be able to open the service with one of the following two
+      // error codes, because of discrepancies between the registry and the SCM
+      // database in memory.
+      DWORD last_error = ::GetLastError();
+      ASSERT(last_error == ERROR_SERVICE_DOES_NOT_EXIST ||
+             last_error == ERROR_INVALID_NAME,
+             (L"Failed to open service %s, last error %d", service_name,
+              last_error));
+      REPORT(last_error == ERROR_SERVICE_DOES_NOT_EXIST ||
+             last_error == ERROR_INVALID_NAME, R_ERROR,
+             (L"Failed to open service %s, last error %d", service_name,
+              last_error), 5576234);
+    }
+  }
+
+  return S_OK;
+}
+
+CString ServiceInstall::GenerateServiceName(const TCHAR* service_prefix) {
+  FILETIME ft = {0};
+  ::GetSystemTimeAsFileTime(&ft);
+  CString versioned_service_name;
+  versioned_service_name.Format(_T("%s%x%x"),
+                                service_prefix,
+                                ft.dwHighDateTime,
+                                ft.dwLowDateTime);
+
+  ASSERT1(!versioned_service_name.IsEmpty());
+  return versioned_service_name;
+}
+
+HRESULT ServiceInstall::UninstallServices(const TCHAR* service_prefix,
+                                          const TCHAR* exclude_service) {
+  SERVICE_LOG(L2, (L"ServiceInstall::UninstallServices"));
+
+  UninstallByPrefixParams params = {
+    service_prefix,
+    exclude_service,
+  };
+
+  return ScmDatabase::EnumerateServices(UninstallByPrefix, &params);
+}
+
+bool ServiceInstall::CanInstallWithoutReboot() {
+  scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
+  if (!scm) {
+    ASSERT1(false);
+    REPORT(false, R_ERROR, (L"Failed to open SCM: %d", ::GetLastError()),
+           77224449);
+    return false;  // request reboot just in case
+  }
+
+  scoped_service service(::OpenService(get(scm),
+                                       _T("gupdate"),
+                                       SERVICE_QUERY_CONFIG |
+                                       SERVICE_CHANGE_CONFIG));
+  if (!service) {
+    DWORD last_error = ::GetLastError();
+    if (last_error == ERROR_ACCESS_DENIED ||
+        last_error == ERROR_INVALID_HANDLE) {
+      // unable to verify the service is fully deleted, so request reboot
+      ASSERT(false, (L"Expected access and correct handle"));
+      return false;
+    } else {
+      // service does not exist
+      return true;
+    }
+  }
+
+  return !ScmDatabase::IsServiceMarkedDeleted(get(service));
+}
+
+HRESULT ServiceInstall::StopService(const CString& service_name) {
+  SERVICE_LOG(L1, (_T("[ServiceInstall::StopService]")));
+
+  scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
+  if (!scm) {
+    return HRESULTFromLastError();
+  }
+  scoped_service service(::OpenService(get(scm),
+                                       service_name,
+                                       SERVICE_QUERY_STATUS | SERVICE_STOP));
+  if (!service) {
+    return HRESULTFromLastError();
+  }
+
+  SERVICE_STATUS status = {0};
+  if (::QueryServiceStatus(get(service), &status)) {
+    if (status.dwCurrentState != SERVICE_STOPPED &&
+        status.dwCurrentState != SERVICE_STOP_PENDING) {
+      // Stop the service.
+      SetZero(status);
+      if (!::ControlService(get(service), SERVICE_CONTROL_STOP, &status)) {
+        return HRESULTFromLastError();
+      }
+    }
+  }
+
+  if (status.dwCurrentState != SERVICE_STOPPED) {
+    SERVICE_LOG(L1, (_T("[Service is stopping...]")));
+
+    const int kWaitForServiceToStopMs = 8000;
+    LowResTimer t(true);
+
+    while (status.dwCurrentState != SERVICE_STOPPED &&
+           t.GetMilliseconds() < kWaitForServiceToStopMs) {
+      const int kSleepTimeMs = 50;
+      ::Sleep(kSleepTimeMs);
+      SetZero(status);
+      VERIFY1(::QueryServiceStatus(get(service), &status));
+      SERVICE_LOG(L1, (_T("[Waiting for service to stop][time elapsed: %d ms]"),
+          static_cast<int>(t.GetMilliseconds())));
+    }
+
+    if (status.dwCurrentState != SERVICE_STOPPED) {
+      SERVICE_LOG(LEVEL_WARNING, (_T("[Service did not stop! Not good...]")));
+      return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
+    }
+  }
+
+  ASSERT1(status.dwCurrentState == SERVICE_STOPPED);
+  SERVICE_LOG(L1, (_T("[ServiceInstall::StopService - service stopped]")));
+  return S_OK;
+}
+
+bool ServiceInstall::IsServiceInstalled(const TCHAR* service_name) {
+  ASSERT1(service_name);
+  scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT));
+  if (!scm) {
+    return false;
+  }
+  scoped_service service(::OpenService(get(scm),
+                                       service_name,
+                                       SERVICE_QUERY_CONFIG));
+  return valid(service);
+}
+
+// TODO(Omaha): Move all functions under a common ServiceUtils namespace.
+bool ServiceUtils::IsServiceRunning(const TCHAR* service_name) {
+  ASSERT1(service_name);
+
+  scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT));
+  if (!scm) {
+    SERVICE_LOG(LE, (_T("[OpenSCManager fail][0x%x]"), HRESULTFromLastError()));
+    return false;
+  }
+
+  scoped_service service(::OpenService(get(scm),
+                                       service_name,
+                                       SERVICE_QUERY_STATUS));
+  if (!service) {
+    SERVICE_LOG(LE, (_T("[OpenService failed][%s][0x%x]"),
+                     service_name, HRESULTFromLastError()));
+    return false;
+  }
+
+  SERVICE_STATUS status = {0};
+  if (!::QueryServiceStatus(get(service), &status)) {
+    SERVICE_LOG(LE, (_T("[QueryServiceStatus failed][%s][0x%x]"),
+                     service_name, HRESULTFromLastError()));
+    return false;
+  }
+
+  return status.dwCurrentState == SERVICE_RUNNING ||
+         status.dwCurrentState == SERVICE_START_PENDING;
+}
+
+bool ServiceUtils::IsServiceDisabled(const TCHAR* service_name) {
+  ASSERT1(service_name);
+
+  scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT));
+  if (!scm) {
+    SERVICE_LOG(LE, (_T("[OpenSCManager fail][0x%x]"), HRESULTFromLastError()));
+    return false;
+  }
+
+  scoped_service service(::OpenService(get(scm),
+                                       service_name,
+                                       SERVICE_QUERY_CONFIG));
+  if (!service) {
+    SERVICE_LOG(LE, (_T("[OpenService failed][%s][0x%x]"),
+                     service_name, HRESULTFromLastError()));
+    return false;
+  }
+
+  return ScmDatabase::IsServiceStateEqual(get(service), SERVICE_DISABLED);
+}
+
+}  // namespace omaha
+
diff --git a/base/service_utils.h b/base/service_utils.h
new file mode 100644
index 0000000..103e307
--- /dev/null
+++ b/base/service_utils.h
@@ -0,0 +1,143 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Service-related utilities.
+
+#ifndef OMAHA_BASE_SERVICE_UTILS_H__
+#define OMAHA_BASE_SERVICE_UTILS_H__
+
+#include <windows.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+// Utility functions for working with the SCM database.
+class ScmDatabase {
+ public:
+  // Callback function type for EnumerateServices. This gets called for each
+  // service in the registry.
+  //
+  // @param callback_context Passed unchanged from the caller of
+  // EnumerateServices to the callback function.
+  // @param service_name The name of the service (not the display name but
+  // rather the canonical name, to be used with e.g. ::OpenService()).  Note
+  // that because this function is based on enumerating the registry, it's
+  // possible that services that were recently deleted will show up in the
+  // enumeration; therefore, it should not be considered an error if you try
+  // to ::OpenService() on this name and it fails with a last error of
+  // ERROR_SERVICE_DOES_NOT_EXIST or possibly ERROR_INVALID_NAME (if
+  // somebody messed up the registry by hand).
+  //
+  // @return S_OK to continue enumeration, S_FALSE or a COM error code to
+  // stop enumeration.  The return value will be propagated to the caller
+  // of Enumerate.
+  //
+  // @note The initial version of this function used EnumServicesStatusEx
+  // but it turns out the function is a fair bit flaky, not returning all
+  // recently created (e.g. created but never started) services.
+  typedef HRESULT(*EnumerateServicesCallback)(void* callback_context,
+                                              const wchar_t* service_name);
+
+  // Calls 'callback' for each of the services in the registry.
+  //
+  // @param callback Callback function to call
+  // @param callback_context Passed unchanged to your callback function
+  //
+  // @return S_OK or a COM error code.
+  static HRESULT EnumerateServices(EnumerateServicesCallback callback,
+                                   void* callback_context);
+
+  // Returns true iff the service passed in is in the indicated state.
+  //
+  // @param service An open handle to a service.  The handle must have at
+  // least SERVICE_QUERY_CONFIG rights.
+  // @param state One of the SERVICE_XXX constants indicating the state of a
+  // service (e.g. SERVICE_DISABLED).
+  //
+  // @return True iff 'service' is in state 'state'.
+  static bool IsServiceStateEqual(SC_HANDLE service, DWORD state);
+
+  // Returns true iff the service passed in has been marked deleted.
+  //
+  // @param service An open handle to a service.  The handle must have at
+  // least SERVICE_QUERY_CONFIG and SERVICE_CHANGE_CONFIG rights.
+  //
+  // @return True iff 'service' has been marked deleted.
+  static bool IsServiceMarkedDeleted(SC_HANDLE service);
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(ScmDatabase);
+};
+
+// Utility functions for the service's installation, overinstall etc.
+class ServiceInstall {
+ public:
+
+  // Generates a versioned service name based on the current system time.
+  static CString GenerateServiceName(const TCHAR* service_prefix);
+
+  // Uninstalls all versions of the service other than the one that matches
+  // the service name passed in. Pass in NULL to uninstall everything.
+  static HRESULT UninstallServices(const TCHAR* service_prefix,
+                                   const TCHAR* exclude_service);
+
+  static bool IsServiceInstalled(const TCHAR* service_name);
+
+  // @return True if the current service can be installed without rebooting,
+  // false if a reboot is required before it can be installed.  The cases
+  // where the current service can be installed without rebooting are:
+  // a) when no service exists with the current name
+  // b) when there is an existing service with the current name but it is
+  //    not marked for deletion
+  static bool CanInstallWithoutReboot();
+
+  // Given a service name, stops it if it is already running.
+  static HRESULT StopService(const CString& service_name);
+
+ protected:
+  // Context passed to the UninstallIfNotCurrent function; this is made a
+  // parameter so we can unit test the function without mucking with the
+  // "actual" services.
+  struct UninstallByPrefixParams {
+    CString prefix;  // prefix of services we want to uninstall
+    CString unless_matches;  // name of current service, to not touch
+  };
+
+  // Uninstalls a given service if it matches a given prefix but does not match
+  // a given full service name.
+  //
+  // This is an ScmDatabase::EnumerateServicesCallback function.
+  //
+  // @param context Pointer to an UninstallByPrefix structure.
+  static HRESULT UninstallByPrefix(void* context, const wchar_t* service_name);
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ServiceInstall);
+};
+
+// Service utility functions for querying current state, and eventually more.
+class ServiceUtils {
+ public:
+  static bool IsServiceRunning(const TCHAR* service_name);
+  static bool IsServiceDisabled(const TCHAR* service_name);
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ServiceUtils);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_SERVICE_UTILS_H__
diff --git a/base/service_utils_unittest.cc b/base/service_utils_unittest.cc
new file mode 100644
index 0000000..00fe871
--- /dev/null
+++ b/base/service_utils_unittest.cc
@@ -0,0 +1,59 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/service_utils.h"
+#include <lmsname.h>
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+// Set to true when the test callback sees the service indicated in the
+// context parameter.
+bool found_service = false;
+
+HRESULT TestEnumCallback(void* context, const wchar_t* service_name) {
+  EXPECT_TRUE(context);
+
+  const wchar_t* find_service_name = reinterpret_cast<const wchar_t*>(context);
+  if (lstrcmpW(service_name, find_service_name) == 0) {
+    found_service = true;
+  }
+
+  return S_OK;
+}
+
+TEST(ServiceUtilsTest, ScmDatabaseEnumerateServices) {
+  found_service = false;
+  EXPECT_TRUE(SUCCEEDED(ScmDatabase::EnumerateServices(TestEnumCallback,
+                            reinterpret_cast<void*>(_T("RpcSs")))));
+  EXPECT_TRUE(found_service);
+}
+
+TEST(ServiceUtilsTest, IsServiceInstalled) {
+  EXPECT_TRUE(ServiceInstall::IsServiceInstalled(SERVICE_SCHEDULE));
+  EXPECT_FALSE(ServiceInstall::IsServiceInstalled(_T("FooBar")));
+}
+
+TEST(ServiceUtilsTest, IsServiceRunning) {
+  EXPECT_TRUE(ServiceUtils::IsServiceRunning(SERVICE_SCHEDULE));
+  EXPECT_FALSE(ServiceUtils::IsServiceRunning(_T("FooBar")));
+}
+
+TEST(ServiceUtilsTest, IsServiceDisabled) {
+  EXPECT_FALSE(ServiceUtils::IsServiceDisabled(SERVICE_SCHEDULE));
+}
+
+}  // namespace omaha
+
diff --git a/base/shared_any.h b/base/shared_any.h
new file mode 100644
index 0000000..d12413a
--- /dev/null
+++ b/base/shared_any.h
@@ -0,0 +1,28 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// See the comments in omaha/base/scoped_any.h for details.
+
+#ifndef OMAHA_COMMON_SHARED_ANY__
+#define OMAHA_COMMON_SHARED_ANY__
+
+#pragma warning(push)
+// C4640: construction of local static object is not thread-safe
+#pragma warning(disable : 4640)
+#include "omaha/third_party/smartany/shared_any.h"
+#pragma warning(pop)
+
+#endif  // OMAHA_COMMON_SHARED_ANY__
+
diff --git a/base/shared_memory_ptr.h b/base/shared_memory_ptr.h
new file mode 100644
index 0000000..d813cc6
--- /dev/null
+++ b/base/shared_memory_ptr.h
@@ -0,0 +1,392 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Defines template class used to share data
+// between processes.
+//
+
+#ifndef OMAHA_COMMON_SHARED_MEMORY_PTR_H__
+#define OMAHA_COMMON_SHARED_MEMORY_PTR_H__
+
+#include "base/debug.h"
+#include "base/singleton.h"
+#include "base/system_info.h"
+#include "base/vista_utils.h"
+
+namespace omaha {
+
+// SharedMemoryPtr class is designed to allow seamless data sharing process boundaries.
+// All the data passed as a template parameter will be shared between processes.
+// Very important to remember that for now - all shared data should be on stack.
+// For example if the class A had stl vector as a member, the members of the vector
+// would be allocated not from shared memory and therefore will not be shared.
+// That could be solved with allocators, but for now we don't need that.
+//
+// Here is a typical example of usage:
+//   Class A {
+//    int i_;
+//    double d_;
+//    .......
+//    ........
+//
+//    public:
+//    set_double(double d){d_=d;}
+//    double get_double(){return d_};
+//
+// };
+//
+// ... Prosess one...
+//    SharedMemoryPtr<A> spA("ABC");
+//    if (!spA)
+//      return false;
+//
+//    spA->set_double(3.14);
+//
+//  ... Process two ...
+//
+//
+//    SharedMemoryPtr<A> spA1("ABC");
+//    if (!spA1)
+//      return false;
+//
+//    process two will see the value set by process one.
+//    it will be 3.14
+//    double d = spA1->get_double();
+//
+
+// You should implement a class member of SystemSharedData if the data you want
+// to share is several hundred bytes. Always try this approach first before you implement
+// new class that is derived from SharedMemoryPtr. The main difference is that SharedMemoryPtr
+// will allocate a least page of shared memory. If your class is just a member of SystemSharedData
+// memory mapped file will be shared between members. It is just more efficient.
+// Look in system_shared_data.h , shared_data_member.h, and system_shared_data_members.h
+// for more details.
+
+// Forward declaration.
+template <typename LockType, typename T> class SharedMemoryPtr;
+
+// During several code reviews it has been noticed that the same error gets repeated over and over.
+// People create SharedMemoryPtr<SomeData>. And than the access to member functions of SomeData
+// is not synchronized by __mutexBlock or __mutexScope. So we need to somehow find a way to make
+// automatic syncronization whenever people access shared data methods or  members.
+// Since by design the only way we can acess shared data is through operator -> of SharedMemoryPtr
+// we need to somehow invoke synchronization at the time of access.
+// We can implement this mainly because of the mechanics of operator-> dictated by C++ standard.
+// When you apply operator-> to a type that's not a built-in pointer, the compiler does an interesting thing.
+// After looking up and applying the user-defined operator-> to that type, it applies operator-> again to the result.
+// The compiler keeps doing this recursively until it reaches a pointer to a built-in type, and only then proceeds with member access.
+// It follows that a SharedMemoryPtr<T> operator-> does not have to return a pointer.
+// It can return an object that in turn implements operator->, without changing the use syntax.
+// So we can implement: pre- and postfunction calls. (See Stroustrup 2000)
+// If you return an object of some type X
+// by value from operator->, the sequence of execution is as follows:
+// 1. Constructor of type X
+// 2. X::operator-> called;  returns a pointer to an object of type T of SharedMemoryPtr
+// 3. Member access
+// 4. Destructor of X
+// In a nutshell, we have a  way of implementing locked function calls.
+
+template <typename LockType, typename T>
+class SharedDataLockingProxy {
+public:
+  // Lock on construction.
+  SharedDataLockingProxy(SharedMemoryPtr<LockType, T> * mem_ptr, T* shared_data)
+    : mem_ptr_(mem_ptr), shared_data_(shared_data) {
+      mem_ptr_->Lock();
+    }
+   // Unlock on destruction.
+  ~SharedDataLockingProxy() {
+    mem_ptr_->Unlock();
+  }
+  // operator
+  T* operator->() const  {
+    ASSERT(shared_data_ != NULL, (L"NULL object pointer being dereferenced"));
+    return shared_data_;
+  }
+private:
+  SharedDataLockingProxy& operator=(const SharedDataLockingProxy&);
+  SharedMemoryPtr<LockType, T>* mem_ptr_;
+  T* shared_data_;
+  // To allow this implicit locking - copy constructor must be
+  // enabled. hence, no DISALLOW_EVIL_CONSTRUCTORS
+};
+
+template <typename LockType, typename T> class SharedMemoryPtr
+    : public LockType {
+  // Handle to disk file if we're backing this shared memory by a file
+  HANDLE file_;
+  // Local handle to file mapping.
+  HANDLE file_mapping_;
+  // pointer to a view.
+  T*     data_;
+  // If the first time creation can do some initialization.
+  bool   first_instance_;
+public:
+  // The heart of the whole idea. Points to shared memrory
+  // instead of the beginning of the class.
+  SharedDataLockingProxy<LockType, T> operator->() {
+    return SharedDataLockingProxy<LockType, T>(this, data_);
+  }
+  // To check after creation.
+  // For example:
+  // SharedMemoryPtr<GLock, SomeClass> sm;
+  // if (sm)
+  // { do whatever you want}
+  // else
+  // {error reporting}
+  operator bool() const {return ((file_mapping_ != NULL) && (data_ != NULL));}
+
+  // Initialize memory mapped file and sync mechanics.
+  // by calling InitializeSharedAccess
+  SharedMemoryPtr(const CString& name,
+                  LPSECURITY_ATTRIBUTES sa,
+                  LPSECURITY_ATTRIBUTES sa_mutex,
+                  bool read_only)
+      : file_(INVALID_HANDLE_VALUE),
+        file_mapping_(NULL),
+        data_(NULL) {
+    HRESULT hr = InitializeSharedAccess(name, false, sa, sa_mutex, read_only);
+    if (FAILED(hr)) {
+      UTIL_LOG(LE, (_T("InitializeSharedAccess failed [%s][%s][0x%x]"),
+                    name, read_only ? _T("R") : _T("RW"), hr));
+    }
+  }
+
+  // Use this constructor if you want to back the shared memory by a file.
+  // NOTE: if using a persistent shared memory, every object with this same
+  // name should be persistent. Otherwise, the objects marked as
+  // non-persistent will lead to InitializeSharedData called again if
+  // they are instantiated before the ones marked as persistent.
+  SharedMemoryPtr(bool persist,
+                  LPSECURITY_ATTRIBUTES sa,
+                  LPSECURITY_ATTRIBUTES sa_mutex,
+                  bool read_only)
+      : file_(INVALID_HANDLE_VALUE),
+        file_mapping_(NULL),
+        data_(NULL) {
+    // Each shared data must implement GetFileName() to use this c-tor. The
+    // implementation should be:
+    // const CString GetFileName() const {return L"C:\\directory\file";}
+    // This is purposedly different from GetSharedName, so that the user is
+    // well aware that a file name is expected, not a mutex name.
+    HRESULT hr = InitializeSharedAccess(data_->GetFileName(),
+                                        persist,
+                                        sa,
+                                        sa_mutex,
+                                        read_only);
+    if (FAILED(hr)) {
+      UTIL_LOG(LE, (_T("InitializeSharedAccess failed [%s][%s][0x%x]"),
+                    data_->GetFileName(), read_only ? _T("R") : _T("RW"), hr));
+    }
+  }
+
+  // Initialize memory mapped file and sync mechanics.
+  // by calling InitializeSharedAccess
+  SharedMemoryPtr() :
+      file_(INVALID_HANDLE_VALUE), file_mapping_(NULL), data_(NULL) {
+    // This should never happen but let's assert
+    // in case it does.
+    // Each shared data must implement GetSharedData() to use this c-tor.
+    // The implementation should be:
+    // const TCHAR * GetSharedName() const
+    //     {return L"Some_unique_string_with_no_spaces";}
+    HRESULT hr = InitializeSharedAccess(data_->GetSharedName(),
+                                        false,
+                                        NULL,
+                                        NULL,
+                                        false);
+    if (FAILED(hr)) {
+      UTIL_LOG(LE, (_T("InitializeSharedAccess failed [%s][%s][0x%x]"),
+                    data_->GetSharedName(), _T("RW"), hr));
+    }
+  }
+
+  // Clean up.
+  ~SharedMemoryPtr() {
+    Cleanup();
+  }
+
+  void Cleanup() {
+    __mutexScope(this);
+    if (data_)
+      UnmapViewOfFile(data_);
+    if (file_mapping_)
+      VERIFY(CloseHandle(file_mapping_), (L""));
+    if (file_ != INVALID_HANDLE_VALUE)
+      VERIFY(CloseHandle(file_), (L""));
+  }
+
+  // Initialize memory mapped file and sync object.
+  bool InitializeSharedAccess(const CString& name,
+                              bool persist,
+                              LPSECURITY_ATTRIBUTES sa,
+                              LPSECURITY_ATTRIBUTES sa_mutex,
+                              bool read_only) {
+    return InitializeSharedAccessInternal(name,
+                                          persist,
+                                          sa,
+                                          sa_mutex,
+                                          read_only,
+                                          sizeof(T),
+                                          &T::InitializeSharedData);
+  }
+
+ private:
+  // Initialize memory mapped file and sync object.
+  //
+  // This internal method allows template method folding by only using things
+  // that are consistent in all templates.  Things that vary are passed in.
+  bool InitializeSharedAccessInternal(const CString& name, bool persist,
+                                      LPSECURITY_ATTRIBUTES sa,
+                                      LPSECURITY_ATTRIBUTES sa_mutex,
+                                      bool read_only,
+                                      size_t data_size,
+                                      void (T::*initialize_shared_data)
+                                          (const CString&)) {
+    // If this memory mapped object is backed by a file, then "name" is a fully
+    // qualified name with backslashes. Since we can't use backslashes in a
+    // mutex's name, let's make another name where we convert them to
+    // underscores.
+    CString mem_name(name);
+    if (persist) {
+      mem_name.Replace(_T('\\'), _T('_'));
+    }
+
+    // Initialize the mutex
+    CString mutex_name(mem_name + _T("MUTEX"));
+    LPSECURITY_ATTRIBUTES mutex_attr = sa_mutex ? sa_mutex : sa;
+    if (!InitializeWithSecAttr(mutex_name, mutex_attr)) {
+      ASSERT(false, (L"Failed to initialize mutex. Err=%i", ::GetLastError()));
+      return false;
+    }
+
+    // everything is synchronized till the end of the function or return.
+    __mutexScope(this);
+
+    first_instance_ = false;
+
+    if (persist) {
+      // Back this shared memory by a file
+      file_ = CreateFile(name,
+                         GENERIC_READ | (read_only ? 0 : GENERIC_WRITE),
+                         FILE_SHARE_READ | (read_only ? 0 : FILE_SHARE_WRITE),
+                         sa,
+                         OPEN_ALWAYS,
+                         NULL,
+                         NULL);
+      if (file_ == INVALID_HANDLE_VALUE)
+        return false;
+
+      if (!read_only && GetLastError() != ERROR_ALREADY_EXISTS)
+        first_instance_ = true;
+    } else {
+      ASSERT(file_ == INVALID_HANDLE_VALUE, (L""));
+      file_ = INVALID_HANDLE_VALUE;
+    }
+
+    if (read_only) {
+      file_mapping_ = OpenFileMapping(FILE_MAP_READ, false, mem_name);
+      if (!file_mapping_) {
+        UTIL_LOG(LW, (L"[OpenFileMapping failed][error %i]", ::GetLastError()));
+      }
+    } else {
+      file_mapping_ = CreateFileMapping(file_, sa,
+                                        PAGE_READWRITE, 0, data_size, mem_name);
+      ASSERT(file_mapping_, (L"CreateFileMapping. Err=%i", ::GetLastError()));
+    }
+
+    if (!file_mapping_) {
+      return false;
+    } else if (!read_only &&
+               file_ == INVALID_HANDLE_VALUE &&
+               GetLastError() != ERROR_ALREADY_EXISTS) {
+      first_instance_ = true;
+    }
+
+    data_ = reinterpret_cast<T*>(MapViewOfFile(file_mapping_,
+                                               FILE_MAP_READ |
+                                               (read_only ? 0 : FILE_MAP_WRITE),
+                                               0,
+                                               0,
+                                               data_size));
+
+    if (!data_) {
+      ASSERT(false, (L"MapViewOfFile. Err=%i", ::GetLastError()));
+      VERIFY(CloseHandle(file_mapping_), (L""));
+      file_mapping_ = NULL;
+
+      if (file_ != INVALID_HANDLE_VALUE) {
+        VERIFY(CloseHandle(file_), (L""));
+        file_ = INVALID_HANDLE_VALUE;
+      }
+
+      return false;
+    }
+
+    if (!first_instance_) {
+      return true;
+    }
+
+    // If this is the first instance of shared object
+    // call initialization function. This is nice but
+    // at the same time we can not share built in data types.
+    // SharedMemoryPtr<double> - will not compile. But this is OK
+    // We don't want all the overhead to just share couple of bytes.
+    // Signature is void InitializeSharedData()
+    (data_->*initialize_shared_data)(name);
+
+    return true;
+  }
+
+  DISALLOW_EVIL_CONSTRUCTORS(SharedMemoryPtr);
+};
+
+// Sometimes we want Singletons that are shared between processes.
+// SharedMemoryPtr can do that. But if used in C-written module there will be
+// a need to make SharedMemoryPtr a global object. Making a Singleton from SharedMemoryPtr
+// is possible in this situation, but syntactically this is very difficult to read.
+// The following template solves the problem. It hides difficult to read details inside.
+// Usage is the same as SharedMemoryPtr (ONLY through -> operator). Completely thread-safe.
+// Can be used in two ways:
+// Class A {
+//  public:
+//  void foo(){}
+//
+//};
+// SharedMemorySingleton<A> a, b;
+// a->foo();
+// b->foo();  //refers to the same data in any process.
+//
+//  or
+//
+// class A : public SharedMemorySingleton<A> {
+//  public:
+//  void foo(){}
+//};
+//  A a, b;
+//  a->foo();
+//  b->foo(); //refers to the same data in any process.
+
+template <typename LockType, typename T> class SharedMemorySingleton  {
+public:
+  SharedDataLockingProxy<LockType, T> operator->() {
+    return
+      Singleton<SharedMemoryPtr<LockType, T> >::Instance()->operator->();
+  }
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_SHARED_MEMORY_PTR_H__
diff --git a/base/shell.cc b/base/shell.cc
new file mode 100644
index 0000000..7483b03
--- /dev/null
+++ b/base/shell.cc
@@ -0,0 +1,397 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Shell functions
+
+#include "omaha/base/shell.h"
+
+#include <shlobj.h>
+#include <shellapi.h>
+#include "omaha/base/app_util.h"
+#include "omaha/base/browser_utils.h"
+#include "omaha/base/const_utils.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/string.h"
+#include "omaha/base/system.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+// create and store a shortcut
+// uses shell IShellLink and IPersistFile interfaces
+HRESULT Shell::CreateLink(const TCHAR *source,
+                          const TCHAR *destination,
+                          const TCHAR *working_dir,
+                          const TCHAR *arguments,
+                          const TCHAR *description,
+                          WORD hotkey_virtual_key_code,
+                          WORD hotkey_modifiers,
+                          const TCHAR *icon) {
+  ASSERT1(source);
+  ASSERT1(destination);
+  ASSERT1(working_dir);
+  ASSERT1(arguments);
+  ASSERT1(description);
+
+  scoped_co_init co_init(COINIT_APARTMENTTHREADED);
+  HRESULT hr = co_init.hresult();
+  if (FAILED(hr) && hr != RPC_E_CHANGED_MODE) {
+    UTIL_LOG(LEVEL_ERROR, (_T("[Shell::CreateLink - failed to co_init]"), hr));
+    return hr;
+  }
+
+  UTIL_LOG(L1, (_T("[Create shell link]")
+                _T("[source %s dest %s dir %s arg %s desc %s hotkey %x:%x]"),
+                source, destination, working_dir, arguments, description,
+                hotkey_modifiers, hotkey_virtual_key_code));
+
+  // Get a pointer to the IShellLink interface
+  CComPtr<IShellLink> shell_link;
+
+  RET_IF_FAILED(shell_link.CoCreateInstance(CLSID_ShellLink));
+  ASSERT(shell_link, (L""));
+
+  // Set the path to the shortcut target and add the description
+  VERIFY1(SUCCEEDED(shell_link->SetPath(source)));
+  VERIFY1(SUCCEEDED(shell_link->SetArguments(arguments)));
+  VERIFY1(SUCCEEDED(shell_link->SetDescription(description)));
+  VERIFY1(SUCCEEDED(shell_link->SetWorkingDirectory(working_dir)));
+
+  // If we are given an icon, then set it
+  // For now, we always use the first icon if this happens to have multiple ones
+  if (icon) {
+    VERIFY1(SUCCEEDED(shell_link->SetIconLocation(icon, 0)));
+  }
+
+// C4201: nonstandard extension used : nameless struct/union
+#pragma warning(disable : 4201)
+  union {
+    WORD flags;
+    struct {                  // little-endian machine:
+      WORD virtual_key:8;     // low order byte
+      WORD modifiers:8;       // high order byte
+    };
+  } hot_key;
+#pragma warning(default : 4201)
+
+  hot_key.virtual_key = hotkey_virtual_key_code;
+  hot_key.modifiers = hotkey_modifiers;
+
+  if (hot_key.flags) {
+    shell_link->SetHotkey(hot_key.flags);
+  }
+
+  // Query IShellLink for the IPersistFile interface for saving the shortcut in
+  // persistent storage
+  CComQIPtr<IPersistFile> persist_file(shell_link);
+  if (!persist_file)
+    return E_FAIL;
+
+  // Save the link by calling IPersistFile::Save
+  RET_IF_FAILED(persist_file->Save(destination, TRUE));
+
+  return S_OK;
+}
+
+HRESULT Shell::RemoveLink(const TCHAR *link) {
+  ASSERT(link, (L""));
+  ASSERT(*link, (L""));
+
+  return File::Remove(link);
+}
+
+// Open a URL in a new browser window
+HRESULT Shell::OpenLinkInNewWindow(const TCHAR* url, UseBrowser use_browser) {
+  ASSERT1(url);
+
+  HRESULT hr = S_OK;
+  CString browser_path;
+
+  // Try to open with default browser
+  if (use_browser == USE_DEFAULT_BROWSER) {
+    // Load full browser path from regkey
+    hr = GetDefaultBrowserPath(&browser_path);
+
+    // If there is a default browser and it is not AOL, load the url in that
+    // browser
+    if (SUCCEEDED(hr) && !String_Contains(browser_path, _T("aol"))) {
+      if (!browser_path.IsEmpty()) {
+        // Have we figured out how to append the URL onto the browser path?
+        bool acceptable_url = false;
+
+        if (ReplaceCString(browser_path, _T("\"%1\""), url)) {
+          // the "browser.exe "%1"" case
+          acceptable_url = true;
+        } else if (ReplaceCString(browser_path, _T("%1"), url)) {
+          // the "browser.exe %1 "case
+          acceptable_url = true;
+        } else if (ReplaceCString(browser_path, _T("-nohome"), url)) {
+          // the "browser.exe -nohome" case
+          acceptable_url = true;
+        } else {
+          // the browser.exe case.
+          // simply append the quoted url.
+          EnclosePath(&browser_path);
+          browser_path.AppendChar(_T(' '));
+          CString quoted_url(url);
+          EnclosePath(&quoted_url);
+          browser_path.Append(quoted_url);
+          acceptable_url = true;
+        }
+
+        if (acceptable_url) {
+          hr = System::ShellExecuteCommandLine(browser_path, NULL, NULL);
+          if (SUCCEEDED(hr)) {
+            return S_OK;
+          } else {
+            UTIL_LOG(LE, (_T("[Shell::OpenLinkInNewWindow]")
+                          _T("[failed to start default browser to open url]")
+                          _T("[%s][0x%x]"), url, hr));
+          }
+        }
+      }
+    }
+  }
+
+  // Try to open with IE if can't open with default browser or required
+  if (use_browser == USE_DEFAULT_BROWSER ||
+      use_browser == USE_INTERNET_EXPLORER) {
+    hr = RegKey::GetValue(kRegKeyIeClass, kRegValueIeClass, &browser_path);
+    if (SUCCEEDED(hr)) {
+      hr = System::ShellExecuteProcess(browser_path, url, NULL, NULL);
+      if (SUCCEEDED(hr)) {
+        return S_OK;
+      } else {
+        UTIL_LOG(LE, (_T("[Shell::OpenLinkInNewWindow]")
+                      _T("[failed to start IE to open url][%s][0x%x]"),
+                      url, hr));
+      }
+    }
+  }
+
+  // Try to open with Firefox if can't open with default browser or required
+  if (use_browser == USE_DEFAULT_BROWSER || use_browser == USE_FIREFOX) {
+    hr = RegKey::GetValue(kRegKeyFirefox, kRegValueFirefox, &browser_path);
+    if (SUCCEEDED(hr) && !browser_path.IsEmpty()) {
+      ReplaceCString(browser_path, _T("%1"), url);
+      hr = System::ShellExecuteCommandLine(browser_path, NULL, NULL);
+      if (SUCCEEDED(hr)) {
+        return S_OK;
+      } else {
+        UTIL_LOG(LE, (_T("[Shell::OpenLinkInNewWindow]")
+                      _T("[failed to start Firefox to open url][%s][0x%x]"),
+                      url, hr));
+      }
+    }
+  }
+
+  // ShellExecute the url directly as a last resort
+  hr = Shell::Execute(url);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[Shell::OpenLinkInNewWindow]")
+                  _T("[failed to run ShellExecute to open url][%s][0x%x]"),
+                  url, hr));
+  }
+
+  return hr;
+}
+
+HRESULT Shell::Execute(const TCHAR* file) {
+  ASSERT1(file);
+
+  // Prepare everything required for ::ShellExecuteEx().
+  SHELLEXECUTEINFO sei;
+  SetZero(sei);
+  sei.cbSize = sizeof(sei);
+  sei.fMask = SEE_MASK_FLAG_NO_UI     |  // Do not display an error message box.
+              SEE_MASK_NOZONECHECKS   |  // Do not perform a zone check.
+              SEE_MASK_NOASYNC;          // Wait to complete before returning.
+  // Pass NULL for hwnd. This will have ShellExecuteExEnsureParent()
+  // create a dummy parent window for us.
+  // sei.hwnd = NULL;
+  sei.lpVerb = _T("open");
+  sei.lpFile = file;
+  // No parameters to pass
+  // sei.lpParameters = NULL;
+  // Use parent's starting directory
+  // sei.lpDirectory = NULL;
+  sei.nShow = SW_SHOWNORMAL;
+
+  // Use ShellExecuteExEnsureParent to ensure that we always have a parent HWND.
+  // We need to use the HWND Property to be acknowledged as a Foreground
+  // Application on Vista. Otherwise, the elevation prompt will appear minimized
+  // on the taskbar.
+  if (!ShellExecuteExEnsureParent(&sei)) {
+    HRESULT hr(HRESULTFromLastError());
+    ASSERT(false,
+        (_T("Shell::Execute - ShellExecuteEx failed][%s][0x%x]"), file, hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT Shell::BasicGetSpecialFolder(DWORD csidl, CString* folder_path) {
+  ASSERT1(folder_path);
+
+  // Get a ITEMIDLIST* (called a PIDL in the MSDN documentation) to the
+  // special folder
+  scoped_any<ITEMIDLIST*, close_co_task_free> folder_location;
+  RET_IF_FAILED(::SHGetFolderLocation(NULL,
+                                      csidl,
+                                      NULL,
+                                      0,
+                                      address(folder_location)));
+  ASSERT(get(folder_location), (_T("")));
+
+  // Get an interface to the Desktop folder
+  CComPtr<IShellFolder> desktop_folder;
+  RET_IF_FAILED(::SHGetDesktopFolder(&desktop_folder));
+  ASSERT1(desktop_folder);
+
+  // Ask the desktop for the display name of the special folder
+  STRRET str_return;
+  SetZero(str_return);
+  str_return.uType = STRRET_WSTR;
+  RET_IF_FAILED(desktop_folder->GetDisplayNameOf(get(folder_location),
+                                                 SHGDN_FORPARSING,
+                                                 &str_return));
+
+  // Get the display name of the special folder and return it
+  scoped_any<wchar_t*, close_co_task_free> folder_name;
+  RET_IF_FAILED(::StrRetToStr(&str_return,
+                              get(folder_location),
+                              address(folder_name)));
+  *folder_path = get(folder_name);
+
+  return S_OK;
+}
+
+HRESULT Shell::GetSpecialFolder(DWORD csidl,
+                                bool create_if_missing,
+                                CString* folder_path) {
+  ASSERT(folder_path, (L""));
+
+  HRESULT hr = Shell::BasicGetSpecialFolder(csidl, folder_path);
+
+  // If the folder does not exist, ::SHGetFolderLocation may return error
+  // code ERROR_FILE_NOT_FOUND.
+  if (create_if_missing) {
+    if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
+        (SUCCEEDED(hr) && !File::Exists(*folder_path))) {
+      hr = Shell::BasicGetSpecialFolder(csidl | CSIDL_FLAG_CREATE, folder_path);
+    }
+  }
+  ASSERT(FAILED(hr) || File::Exists(*folder_path), (_T("")));
+
+  return hr;
+}
+
+#pragma warning(disable : 4510 4610)
+// C4510: default constructor could not be generated
+// C4610: struct can never be instantiated - user defined constructor required
+struct {
+  const TCHAR* name;
+  const DWORD csidl;
+} const folder_mappings[] = {
+  L"APPDATA",            CSIDL_APPDATA,
+  L"DESKTOP",            CSIDL_DESKTOPDIRECTORY,
+  L"LOCALAPPDATA",       CSIDL_LOCAL_APPDATA,
+  L"MYMUSIC",            CSIDL_MYMUSIC,
+  L"MYPICTURES",         CSIDL_MYPICTURES,
+  L"PROGRAMFILES",       CSIDL_PROGRAM_FILES,
+  L"PROGRAMFILESCOMMON", CSIDL_PROGRAM_FILES_COMMON,
+  L"PROGRAMS",           CSIDL_PROGRAMS,
+  L"STARTMENU",          CSIDL_STARTMENU,
+  L"STARTUP",            CSIDL_STARTUP,
+  L"SYSTEM",             CSIDL_SYSTEM,
+  L"WINDOWS",            CSIDL_WINDOWS,
+};
+#pragma warning(default : 4510 4610)
+
+HRESULT Shell::GetSpecialFolderKeywordsMapping(
+    std::map<CString, CString>* special_folders_map) {
+  ASSERT1(special_folders_map);
+
+  special_folders_map->clear();
+
+  for (size_t i = 0; i < arraysize(folder_mappings); ++i) {
+    CString name(folder_mappings[i].name);
+    DWORD csidl(folder_mappings[i].csidl);
+    CString folder;
+    HRESULT hr = GetSpecialFolder(csidl, false, &folder);
+    if (FAILED(hr)) {
+      UTIL_LOG(LE, (_T("[Shell::GetSpecialFolderKeywordsMapping]")
+                    _T("[failed to retrieve %s]"), name));
+      continue;
+    }
+    special_folders_map->insert(std::make_pair(name, folder));
+  }
+
+  // Get the current module directory
+  CString module_dir = app_util::GetModuleDirectory(::GetModuleHandle(NULL));
+  ASSERT1(module_dir.GetLength() > 0);
+  special_folders_map->insert(std::make_pair(_T("CURRENTMODULEDIR"),
+                                             module_dir));
+
+  return S_OK;
+}
+
+HRESULT Shell::DeleteDirectory(const TCHAR* dir) {
+  ASSERT1(dir && *dir);
+
+  if (!SafeDirectoryNameForDeletion(dir)) {
+    return E_INVALIDARG;
+  }
+
+  uint32 dir_len = lstrlen(dir);
+  if (dir_len >= MAX_PATH) {
+    return E_INVALIDARG;
+  }
+
+  // the 'from' must be double-terminated with 0. Reserve space for one more
+  // zero at the end
+  TCHAR from[MAX_PATH + 1] = {0};
+  lstrcpyn(from, dir, MAX_PATH);
+  from[1 + dir_len] = 0;    // the second zero terminator.
+
+  SHFILEOPSTRUCT file_op = {0};
+
+  file_op.hwnd   = 0;
+  file_op.wFunc  = FO_DELETE;
+  file_op.pFrom  = from;
+  file_op.pTo    = 0;
+  file_op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
+
+  // ::SHFileOperation returns non-zero on errors
+  return ::SHFileOperation(&file_op) ? HRESULTFromLastError() : S_OK;
+}
+
+HRESULT Shell::GetApplicationExecutablePath(const CString& exe,
+                                            CString* path) {
+  ASSERT1(path);
+
+  CString reg_key_name = AppendRegKeyPath(kRegKeyApplicationPath, exe);
+  return RegKey::GetValue(reg_key_name, kRegKeyPathValue, path);
+}
+
+}  // namespace omaha
+
diff --git a/base/shell.h b/base/shell.h
new file mode 100644
index 0000000..eb5ef45
--- /dev/null
+++ b/base/shell.h
@@ -0,0 +1,99 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Shell functions
+//
+// create shortcut links
+// remove shortcut links
+
+#ifndef OMAHA_BASE_SHELL_H_
+#define OMAHA_BASE_SHELL_H_
+
+#include <windows.h>
+#include <shlobj.h>   // for the CSIDL definitions
+#include <atlstr.h>
+#include <map>
+
+#include "base/basictypes.h"
+
+namespace omaha {
+
+// TODO(omaha): Use BrowserType instead.
+enum UseBrowser {
+  USE_DEFAULT_BROWSER = 0,
+  USE_INTERNET_EXPLORER = 1,
+  USE_FIREFOX = 2
+};
+
+class Shell {
+ public:
+
+  // create and store a shortcut link
+  // note that icon can be NULL if no icon is required
+  // note that we always pick the 0th icon for now, this can be changed later
+  // [relevant with EXE and compound icons]
+  static HRESULT CreateLink(const TCHAR *source,
+                            const TCHAR *destination,
+                            const TCHAR *working_dir,
+                            const TCHAR *arguments,
+                            const TCHAR *description,
+                            WORD hotkey_virtual_key_code,
+                            WORD hotkey_modifiers,
+                            const TCHAR *icon);
+  // For information on hotkey modifiers see MSDN IShellLink::GetHotKey method.
+
+  // Delete a shortcut link
+  static HRESULT RemoveLink(const TCHAR *link);
+
+  // Open a URL in a new browser window
+  static HRESULT OpenLinkInNewWindow(const TCHAR* url, UseBrowser use_browser);
+
+  // Execute a file
+  static HRESULT Shell::Execute(const TCHAR* file);
+
+  // Get the location of a special folder.  The special folders are identified
+  // by a unique integer - see the platform SDK files shfolder.h and
+  // shlobj.h.  (These names will be the localized versions for the
+  // system that is running.)
+  static HRESULT GetSpecialFolder(DWORD csidl,
+                                  bool create_if_missing,
+                                  CString* folder_path);
+
+  // Get a mapping from special folder "env var" names to special folder
+  // pathnames.
+  // Provides mappings for: APPDATA, DESKTOP, LOCALAPPDATA, MYMUSIC, MYPICTURES,
+  // PROGRAMFILES, PROGRAMFILESCOMMON, PROGRAMS, STARTMENU, STARTUP, SYSTEM,
+  // WINDOWS.
+  static HRESULT GetSpecialFolderKeywordsMapping(
+      std::map<CString, CString>* special_folders_map);
+
+  // Recursively delete a directory including its files.
+  static HRESULT DeleteDirectory(const TCHAR* dir);
+
+  // Reads the application executable path from
+  // HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths.
+  static HRESULT GetApplicationExecutablePath(const CString& exe,
+                                              CString* path);
+
+ private:
+
+  static HRESULT BasicGetSpecialFolder(DWORD csidl, CString* folder_path);
+
+  DISALLOW_EVIL_CONSTRUCTORS(Shell);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_SHELL_H_
diff --git a/base/shell_unittest.cc b/base/shell_unittest.cc
new file mode 100644
index 0000000..45bf92d
--- /dev/null
+++ b/base/shell_unittest.cc
@@ -0,0 +1,104 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Shell functions
+
+#include <shlobj.h>
+#include <map>
+#include "base/basictypes.h"
+#include "omaha/base/dynamic_link_kernel32.h"
+#include "omaha/base/file.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/shell.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(ShellTest, ShellLink) {
+  if (IsTestRunByLocalSystem()) {
+    return;
+  }
+
+  CString desktop;
+  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_DESKTOP, &desktop));
+  CString link(desktop + _T("\\Shell Unittest.lnk"));
+  CString install_dir;
+  ASSERT_SUCCEEDED(Shell::GetSpecialFolder(CSIDL_PROGRAM_FILES,
+                                           true,
+                                           &install_dir));
+  install_dir += _T("\\Shell Unittest");
+  CString exe = install_dir + _T("\\foo.bar.exe");
+  ASSERT_FALSE(File::Exists(link));
+  ASSERT_SUCCEEDED(Shell::CreateLink(exe,
+                                     link,
+                                     install_dir,
+                                     _T(""),
+                                     _T("Google Update Unit Test"),
+                                     'W',
+                                     HOTKEYF_ALT | HOTKEYF_CONTROL,
+                                     NULL));
+  ASSERT_TRUE(File::Exists(link));
+  ASSERT_SUCCEEDED(Shell::RemoveLink(link));
+  ASSERT_FALSE(File::Exists(link));
+}
+
+struct Folders {
+    DWORD csidl;
+    CString name;
+};
+
+TEST(ShellTest, GetSpecialFolder) {
+  Folders folders[] = {
+    { CSIDL_COMMON_APPDATA,
+      CString("C:\\Documents and Settings\\All Users\\Application Data") },
+    { CSIDL_FONTS,
+      CString("C:\\WINDOWS\\Fonts") },
+    { CSIDL_PROGRAM_FILES,
+      CString("C:\\Program Files") },
+  };
+
+  if (SystemInfo::IsRunningOnVistaOrLater()) {
+    folders[0].name = _T("C:\\ProgramData");
+  }
+
+  // Override the program files location, which changes for 32-bit processes
+  // running on 64-bit systems.
+  BOOL isWow64 = FALSE;
+  EXPECT_SUCCEEDED(Kernel32::IsWow64Process(GetCurrentProcess(), &isWow64));
+  if (isWow64) {
+    folders[2].name += _T(" (x86)");
+  }
+
+  for (size_t i = 0; i != arraysize(folders); ++i) {
+    CString folder_name;
+    EXPECT_SUCCEEDED(Shell::GetSpecialFolder(folders[i].csidl,
+                                             false,
+                                             &folder_name));
+    // This should work, but CmpHelperSTRCASEEQ is not overloaded for wchars.
+    // EXPECT_STRCASEEQ(folder_name, folders[i].name);
+    EXPECT_EQ(folder_name.CompareNoCase(folders[i].name), 0);
+  }
+}
+
+TEST(ShellTest, GetSpecialFolderKeywordsMapping) {
+  typedef std::map<CString, CString> mapping;
+  mapping folder_map;
+  ASSERT_SUCCEEDED(Shell::GetSpecialFolderKeywordsMapping(&folder_map));
+}
+
+}  // namespace omaha
+
diff --git a/common/shutdown_callback.h b/base/shutdown_callback.h
similarity index 100%
rename from common/shutdown_callback.h
rename to base/shutdown_callback.h
diff --git a/base/shutdown_handler.cc b/base/shutdown_handler.cc
new file mode 100644
index 0000000..8c72af1
--- /dev/null
+++ b/base/shutdown_handler.cc
@@ -0,0 +1,75 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/shutdown_handler.h"
+
+#include <atlsecurity.h>
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/reactor.h"
+#include "omaha/base/shutdown_callback.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+ShutdownHandler::ShutdownHandler()
+  : shutdown_callback_(NULL) {
+}
+
+ShutdownHandler::~ShutdownHandler() {
+  if (get(shutdown_event_)) {
+    VERIFY1(SUCCEEDED(reactor_->UnregisterHandle(get(shutdown_event_))));
+  }
+}
+
+HRESULT ShutdownHandler::Initialize(Reactor* reactor,
+                                    ShutdownCallback* shutdown,
+                                    bool is_machine) {
+  ASSERT1(reactor);
+  ASSERT1(shutdown);
+  shutdown_callback_ = shutdown;
+  reactor_ = reactor;
+  is_machine_ = is_machine;
+
+  NamedObjectAttributes attr;
+  GetNamedObjectAttributes(kShutdownEvent, is_machine_, &attr);
+  // Manual reset=true and signaled=false
+  reset(shutdown_event_, ::CreateEvent(&attr.sa, true, false, attr.name));
+  if (!shutdown_event_) {
+    return HRESULTFromLastError();
+  }
+
+  HRESULT hr = reactor_->RegisterHandle(get(shutdown_event_), this, 0);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+void ShutdownHandler::HandleEvent(HANDLE handle) {
+  if (handle == get(shutdown_event_)) {
+    CORE_LOG(L1, (_T("[shutdown event is signaled]")));
+  } else {
+    ASSERT1(false);
+  }
+  ASSERT1(shutdown_callback_);
+  shutdown_callback_->Shutdown();
+}
+
+}  // namespace omaha
+
diff --git a/base/shutdown_handler.h b/base/shutdown_handler.h
new file mode 100644
index 0000000..fb8f661
--- /dev/null
+++ b/base/shutdown_handler.h
@@ -0,0 +1,51 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// ShutdownHandler monitors a shutdown event.
+
+#ifndef OMAHA_BASE_SHUTDOWN_HANDLER_H_
+#define OMAHA_BASE_SHUTDOWN_HANDLER_H_
+
+#include <windows.h>
+#include "base/basictypes.h"
+#include "omaha/base/event_handler.h"
+#include "omaha/base/scoped_any.h"
+
+namespace omaha {
+
+class Reactor;
+class ShutdownCallback;
+
+class ShutdownHandler : public EventHandler {
+ public:
+  ShutdownHandler();
+  ~ShutdownHandler();
+
+  HRESULT Initialize(Reactor* reactor,
+                     ShutdownCallback* shutdown,
+                     bool is_machine);
+  virtual void HandleEvent(HANDLE handle);
+
+ private:
+  Reactor* reactor_;
+  scoped_event shutdown_event_;
+  ShutdownCallback* shutdown_callback_;
+  bool is_machine_;
+  DISALLOW_EVIL_CONSTRUCTORS(ShutdownHandler);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_SHUTDOWN_HANDLER_H_
diff --git a/base/signatures.cc b/base/signatures.cc
new file mode 100644
index 0000000..6d40f86
--- /dev/null
+++ b/base/signatures.cc
@@ -0,0 +1,1113 @@
+// Copyright 2003-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// signatures.cpp
+//
+// Classes and functions related to crypto-hashes of buffers and digital
+// signatures of buffers.
+
+#include "omaha/base/signatures.h"
+#include <wincrypt.h>
+#include <memory.h>
+#pragma warning(disable : 4245)
+// C4245 : conversion from 'type1' to 'type2', signed/unsigned mismatch
+#include <atlenc.h>
+#pragma warning(default : 4245)
+#include <vector>
+#include "base/scoped_ptr.h"
+#include "omaha/base/const_utils.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+const ALG_ID kHashAlgorithm = CALG_SHA1;
+const DWORD kEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
+const DWORD kProviderType = PROV_RSA_FULL;
+const DWORD kCertificateNameType = CERT_NAME_SIMPLE_DISPLAY_TYPE;
+const DWORD kKeyPairType = AT_SIGNATURE;
+
+// Maximum file size allowed for performing authentication.
+const int kMaxFileSizeForAuthentication = 512 * 1024 * 1024;  // 512MB
+
+// Buffer size used to read files from disk.
+const int kFileReadBufferSize = 128 * 1024;
+
+namespace CryptDetails {
+
+  // Useful scoped pointers for working with CryptoAPI objects
+
+  void crypt_release_context(HCRYPTPROV provider) {
+    UTIL_LOG(L3, (L"Releasing HCRYPTPROV 0x%08lx", provider));
+    BOOL b = ::CryptReleaseContext(provider, 0 /*flags*/);
+    ASSERT(b, (L""));
+  }
+
+  void crypt_close_store(HCERTSTORE store) {
+    UTIL_LOG(L3, (L"Releasing HCERTSTORE 0x%08lx", store));
+    BOOL b = ::CertCloseStore(store, 0 /*flags*/);
+    ASSERT(b, (L""));
+    ASSERT(::GetLastError() != CRYPT_E_PENDING_CLOSE, (L""));
+  }
+
+  void crypt_free_certificate(PCCERT_CONTEXT certificate) {
+    UTIL_LOG(L3, (L"Releasing PCCERT_CONTEXT 0x%08lx", certificate));
+    BOOL b = ::CertFreeCertificateContext(certificate);
+    ASSERT(b, (L""));
+  }
+
+  void crypt_destroy_key(HCRYPTKEY key) {
+    UTIL_LOG(L3, (L"Releasing HCRYPTKEY 0x%08lx", key));
+    BOOL b = ::CryptDestroyKey(key);
+    ASSERT(b, (L""));
+  }
+
+  void crypt_destroy_hash(HCRYPTHASH hash) {
+    UTIL_LOG(L3, (L"Releasing HCRYPTHASH 0x%08lx", hash));
+    BOOL b = ::CryptDestroyHash(hash);
+    ASSERT(b, (L""));
+  }
+
+  typedef close_fun<void (*)(HCRYPTHASH),
+                    crypt_destroy_hash> smart_destroy_hash;
+  typedef scoped_any<HCRYPTHASH, smart_destroy_hash, null_t> scoped_crypt_hash;
+}
+
+// Base64 encode/decode functions are part of ATL Server
+HRESULT Base64::Encode(const std::vector<byte>& buffer_in,
+                       std::vector<byte>* encoded,
+                       bool break_into_lines) {
+  ASSERT(encoded, (L""));
+
+  if (buffer_in.empty()) {
+    encoded->resize(0);
+    return S_OK;
+  }
+
+  int32 encoded_len =
+    Base64EncodeGetRequiredLength(
+        buffer_in.size(),
+        break_into_lines ? ATL_BASE64_FLAG_NONE : ATL_BASE64_FLAG_NOCRLF);
+  ASSERT(encoded_len > 0, (L""));
+
+  encoded->resize(encoded_len);
+  int32 str_out_len = encoded_len;
+
+  BOOL result = Base64Encode(
+      &buffer_in.front(),
+      buffer_in.size(),
+      reinterpret_cast<char*>(&encoded->front()),
+      &str_out_len,
+      break_into_lines ? ATL_BASE64_FLAG_NONE : ATL_BASE64_FLAG_NOCRLF);
+  if (!result)
+    return E_FAIL;
+  ASSERT(str_out_len <= encoded_len, (L""));
+  if (str_out_len < encoded_len)
+    encoded->resize(str_out_len);
+
+  return S_OK;
+}
+
+HRESULT Base64::Encode(const std::vector<byte>& buffer_in,
+                       CStringA* encoded,
+                       bool break_into_lines) {
+  ASSERT(encoded, (L""));
+
+  if (buffer_in.empty()) {
+    return S_OK;
+  }
+
+  std::vector<byte> buffer_out;
+  RET_IF_FAILED(Encode(buffer_in, &buffer_out, break_into_lines));
+  encoded->Append(reinterpret_cast<const char*>(&buffer_out.front()),
+                                                buffer_out.size());
+
+  return S_OK;
+}
+
+HRESULT Base64::Encode(const std::vector<byte>& buffer_in,
+                       CString* encoded,
+                       bool break_into_lines) {
+  ASSERT(encoded, (L""));
+
+  CStringA string_out;
+  RET_IF_FAILED(Encode(buffer_in, &string_out, break_into_lines));
+  *encoded = string_out;
+
+  return S_OK;
+}
+
+HRESULT Base64::Decode(const std::vector<byte>& encoded,
+                       std::vector<byte>* buffer_out) {
+  ASSERT(buffer_out, (L""));
+
+  size_t encoded_len = encoded.size();
+  int32 required_len = Base64DecodeGetRequiredLength(encoded_len);
+
+  buffer_out->resize(required_len);
+
+  if (required_len == 0) {
+    return S_OK;
+  }
+
+  int32 bytes_written = required_len;
+  BOOL result = Base64Decode(reinterpret_cast<const char*>(&encoded.front()),
+                             encoded_len,
+                             &buffer_out->front(),
+                             &bytes_written);
+  if (!result)
+    return E_FAIL;
+  ASSERT(bytes_written <= required_len, (L""));
+  if (bytes_written < required_len) {
+    buffer_out->resize(bytes_written);
+  }
+
+  return S_OK;
+}
+
+HRESULT Base64::Decode(const CStringA& encoded, std::vector<byte>* buffer_out) {
+  ASSERT(buffer_out, (L""));
+
+  size_t encoded_len = encoded.GetLength();
+  std::vector<byte> buffer_in(encoded_len);
+  if (encoded_len != 0) {
+    ::memcpy(&buffer_in.front(), encoded.GetString(), encoded_len);
+  }
+
+  return Decode(buffer_in, buffer_out);
+}
+
+// Base64 in a CString -> binary
+HRESULT Base64::Decode(const CString& encoded, std::vector<byte>* buffer_out) {
+  ASSERT(buffer_out, (L""));
+
+  CW2A encoded_a(encoded.GetString());
+
+  size_t encoded_len = ::strlen(encoded_a);
+  std::vector<byte> buffer_in(encoded_len);
+  if (encoded_len != 0) {
+    ::memcpy(&buffer_in.front(), encoded_a, encoded_len);
+  }
+
+  return Decode(buffer_in, buffer_out);
+}
+
+CryptoHash::CryptoHash() {
+}
+
+CryptoHash::~CryptoHash() {
+}
+
+HRESULT CryptoHash::Compute(const TCHAR* filepath,
+                            uint64 max_len,
+                            std::vector<byte>* hash_out) {
+  ASSERT1(filepath);
+  ASSERT1(hash_out);
+
+  std::vector<CString> filepaths;
+  filepaths.push_back(filepath);
+  return Compute(filepaths, max_len, hash_out);
+}
+
+HRESULT CryptoHash::Compute(const std::vector<CString>& filepaths,
+                            uint64 max_len,
+                            std::vector<byte>* hash_out) {
+  ASSERT1(filepaths.size() > 0);
+  ASSERT1(hash_out);
+
+  return ComputeOrValidate(filepaths, max_len, NULL, hash_out);
+}
+
+HRESULT CryptoHash::Compute(const std::vector<byte>& buffer_in,
+                            std::vector<byte>* hash_out) {
+  ASSERT1(buffer_in.size() > 0);
+  ASSERT1(hash_out);
+
+  return ComputeOrValidate(buffer_in, NULL, hash_out);
+}
+
+HRESULT CryptoHash::Validate(const TCHAR* filepath,
+                             uint64 max_len,
+                             const std::vector<byte>& hash_in) {
+  ASSERT1(filepath);
+  ASSERT1(hash_in.size() == kHashSize);
+
+  std::vector<CString> filepaths;
+  filepaths.push_back(filepath);
+  return Validate(filepaths, max_len, hash_in);
+}
+
+HRESULT CryptoHash::Validate(const std::vector<CString>& filepaths,
+                             uint64 max_len,
+                             const std::vector<byte>& hash_in) {
+  ASSERT1(hash_in.size() == kHashSize);
+
+  return ComputeOrValidate(filepaths, max_len, &hash_in, NULL);
+}
+
+
+HRESULT CryptoHash::Validate(const std::vector<byte>& buffer_in,
+                             const std::vector<byte>& hash_in) {
+  ASSERT1(buffer_in.size() > 0);
+  ASSERT1(hash_in.size() == kHashSize);
+
+  return ComputeOrValidate(buffer_in, &hash_in, NULL);
+}
+
+HRESULT CryptoHash::ComputeOrValidate(const std::vector<CString>& filepaths,
+                                      uint64 max_len,
+                                      const std::vector<byte>* hash_in,
+                                      std::vector<byte>* hash_out) {
+  ASSERT1(filepaths.size() > 0);
+  ASSERT1(hash_in && !hash_out || !hash_in && hash_out);
+  UTIL_LOG(L1, (_T("[CryptoHash::ComputeOrValidate]")));
+
+  std::vector<byte> buf(kFileReadBufferSize);
+  uint64 curr_len = 0;
+
+  CryptDetails::scoped_crypt_context scoped_csp_handle;
+  if (0 == ::CryptAcquireContext(address(scoped_csp_handle),
+                                 NULL,
+                                 NULL,  // Use OS-default CSP.
+                                 kProviderType,
+                                 CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[CryptAcquireContext failed][0x%08lX]"), hr));
+    return hr;
+  }
+
+  CryptDetails::scoped_crypt_hash scoped_hash_handle;
+  if (0 == ::CryptCreateHash(get(scoped_csp_handle),
+                             kHashAlgorithm,
+                             NULL,
+                             0,
+                             address(scoped_hash_handle))) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[CryptCreateHash failed][0x%08lX]"), hr));
+    return hr;
+  }
+
+  for (size_t i = 0; i < filepaths.size(); ++i) {
+    scoped_hfile file_handle(::CreateFile(filepaths[i],
+                                          FILE_READ_DATA,
+                                          FILE_SHARE_READ,
+                                          NULL,
+                                          OPEN_EXISTING,
+                                          FILE_ATTRIBUTE_NORMAL,
+                                          NULL));
+    if (!file_handle) {
+      return HRESULTFromLastError();
+    }
+
+    if (max_len) {
+      LARGE_INTEGER file_size = {0};
+      if (!::GetFileSizeEx(get(file_handle), &file_size)) {
+        return HRESULTFromLastError();
+      }
+      curr_len += file_size.QuadPart;
+      if (curr_len > max_len) {
+        UTIL_LOG(LE, (_T("[exceed max len][curr_len=%lu][max_len=%lu]"),
+                      curr_len, max_len));
+        return E_FAIL;
+      }
+    }
+
+    DWORD bytes_read = 0;
+    do {
+      if (!::ReadFile(get(file_handle),
+                      &buf[0],
+                      buf.size(),
+                      &bytes_read,
+                      NULL)) {
+        return HRESULTFromLastError();
+      }
+
+      if (bytes_read > 0) {
+        if (0 == ::CryptHashData(get(scoped_hash_handle),
+                                 &buf[0],
+                                 bytes_read,
+                                 0)) {
+          HRESULT hr = HRESULTFromLastError();
+          UTIL_LOG(LE, (_T("[CryptHashData failed][0x%08lX]"), hr));
+          return hr;
+        }
+      }
+    } while (bytes_read == buf.size());
+  }
+
+  DWORD digest_size = kHashSize;
+  byte digest_data[kHashSize] = {};
+  if (0 == ::CryptGetHashParam(get(scoped_hash_handle),
+                               HP_HASHVAL,
+                               digest_data,
+                               &digest_size,
+                               0)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[CryptGetHashParam failed][0x%08lX]"), hr));
+    return hr;
+  }
+  if (digest_size != kHashSize) {
+    UTIL_LOG(LE, (_T("[CryptGetHashParam returned %d bytes]"), digest_size));
+    return E_UNEXPECTED;
+  }
+
+  if (hash_in) {
+    int res = ::memcmp(&hash_in->front(), digest_data, kHashSize);
+    if (res == 0) {
+      return S_OK;
+    }
+
+    std::vector<byte> calculated_hash(kHashSize);
+    ::memcpy(&calculated_hash.front(), digest_data, kHashSize);
+    CStringA base64_encoded_hash;
+    Base64::Encode(calculated_hash, &base64_encoded_hash, false);
+    CString hash = AnsiToWideString(base64_encoded_hash,
+                                    base64_encoded_hash.GetLength());
+    REPORT_LOG(L1, (_T("[actual hash=%s]"), hash));
+    return SIGS_E_INVALID_SIGNATURE;
+  } else {
+    hash_out->resize(kHashSize);
+    ::memcpy(&hash_out->front(), digest_data, kHashSize);
+    return S_OK;
+  }
+}
+
+HRESULT CryptoHash::ComputeOrValidate(const std::vector<byte>& buffer_in,
+                                      const std::vector<byte>* hash_in,
+                                      std::vector<byte>* hash_out) {
+  ASSERT1(hash_in && !hash_out || !hash_in && hash_out);
+  UTIL_LOG(L1, (_T("[CryptoHash::ComputeOrValidate]")));
+
+  CryptDetails::scoped_crypt_context scoped_csp_handle;
+  if (0 == ::CryptAcquireContext(address(scoped_csp_handle),
+                                 NULL,
+                                 NULL,  // Use OS-default CSP.
+                                 kProviderType,
+                                 CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[CryptAcquireContext failed][0x%08lX]"), hr));
+    return hr;
+  }
+
+  CryptDetails::scoped_crypt_hash scoped_hash_handle;
+  if (0 == ::CryptCreateHash(get(scoped_csp_handle),
+                             kHashAlgorithm,
+                             NULL,
+                             0,
+                             address(scoped_hash_handle))) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[CryptCreateHash failed][0x%08lX]"), hr));
+    return hr;
+  }
+
+  if (!buffer_in.empty()) {
+    if (0 == ::CryptHashData(get(scoped_hash_handle),
+                             &buffer_in.front(),
+                             buffer_in.size(),
+                             0)) {
+      HRESULT hr = HRESULTFromLastError();
+      UTIL_LOG(LE, (_T("[CryptHashData failed][0x%08lX]"), hr));
+      return hr;
+    }
+  }
+
+  DWORD digest_size = kHashSize;
+  byte digest_data[kHashSize] = {};
+  if (0 == ::CryptGetHashParam(get(scoped_hash_handle),
+                               HP_HASHVAL,
+                               digest_data,
+                               &digest_size,
+                               0)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[CryptGetHashParam failed][0x%08lX]"), hr));
+    return hr;
+  }
+  if (digest_size != kHashSize) {
+    UTIL_LOG(LE, (_T("[CryptGetHashParam returned %d bytes]"), digest_size));
+    return E_UNEXPECTED;
+  }
+
+  if (hash_in) {
+    int res = ::memcmp(&hash_in->front(), digest_data, kHashSize);
+    return (res == 0) ? S_OK : SIGS_E_INVALID_SIGNATURE;
+  } else {
+    hash_out->resize(kHashSize);
+    ::memcpy(&hash_out->front(), digest_data, kHashSize);
+    return S_OK;
+  }
+}
+
+// To sign data you need a CSP with the proper private key installed.
+// To get a signing certificate you start with a PFX file.  This file
+// encodes a "certificate store" which can hold more than one
+// certificate.  (In general it can hold a certificate chain, but we
+// only use the signing certificate.)  There are special APIs to verify
+// the format of a PFX file and read it into a new certificate store.  A
+// password must be specified to read the PFX file as it is encrypted.
+// The password was set when the PFX file was exported or otherwise
+// created.  Then you search for the proper certificate in the store
+// (using the subject_name which tells who the certificate was issued
+// to).  Finally, to get a CSP with the certificate's private key
+// available there is a special API, CryptAcquireCertificatePrivateKey,
+// that takes a CSP and a certificate and makes the private key of the
+// certificate the private key of the CSP.
+
+CryptoSigningCertificate::CryptoSigningCertificate() : key_spec_(0) {
+}
+
+CryptoSigningCertificate::~CryptoSigningCertificate() {
+}
+
+HRESULT CryptoSigningCertificate::ImportCertificate(
+    const TCHAR * filepath,
+    const TCHAR * password,
+    const TCHAR * subject_name) {
+  ASSERT(filepath, (L""));
+  ASSERT(password, (L""));
+
+  std::vector<byte> buffer;
+  HRESULT hr = ReadEntireFile(filepath, kMaxCertificateSize, &buffer);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (L"[CryptoSigningCertificate::ImportCertificate]"
+                  L"['%s' not read, hr 0x%08lx]", filepath, hr));
+    return hr;
+  }
+  return ImportCertificate(buffer, password, subject_name);
+}
+
+HRESULT CryptoSigningCertificate::ImportCertificate(
+    const std::vector<byte>& certificate_in,
+    const TCHAR * password,
+    const TCHAR * subject_name) {
+  ASSERT(password, (L""));
+  ASSERT1(!certificate_in.empty());
+
+  UTIL_LOG(L2, (L"[CryptoSigningCertificate::ImportCertificate]"
+                L"[%d bytes, subject_name '%s']",
+                certificate_in.size(), subject_name ? subject_name : L""));
+
+  // CryptoAPI treats the certificate as a "blob"
+  CRYPT_DATA_BLOB blob;
+  blob.cbData = certificate_in.size();
+  blob.pbData = const_cast<BYTE*>(&certificate_in.front());
+
+  // Ensure that it is PFX formatted
+  BOOL b = ::PFXIsPFXBlob(&blob);
+  if (!b) {
+    ASSERT(0, (L"Invalid PFX certificate, err 0x%08lx", ::GetLastError()));
+    return SIGS_E_INVALID_PFX_CERTIFICATE;
+  }
+
+  // Make sure the password checks out
+  b = ::PFXVerifyPassword(&blob, password, 0 /* flags */);
+  if (!b) {
+    UTIL_LOG(LE, (L"[CryptoSigningCertificate::ImportCertificate]"
+                  L"[invalid password, err 0x%08lx]", ::GetLastError()));
+    return SIGS_E_INVALID_PASSWORD;
+  }
+
+  // Do the import from the certificate to a new certificate store
+  // TODO(omaha): Check that this is in fact a new certificate store, not an
+  // existing one.  If it is an existing one we'll need to delete the
+  // certificate later.
+  // The last parameter to ::PFXImportCertStore() is 0, indicating that we want
+  // the CSP to be "silent"; i.e., not prompt.
+  reset(store_, ::PFXImportCertStore(&blob, password, 0));
+  if (!store_) {
+    DWORD err = ::GetLastError();
+    ASSERT(0, (L"Failed to import PFX certificate into a certificate store, "
+               L"err 0x%08lx", err));
+    return HRESULT_FROM_WIN32(err);
+  }
+  UTIL_LOG(L3, (L"[CryptoSigningCertificate::ImportCertificate]"
+                L"[new store 0x%08lx]", get(store_)));
+
+  // Now that we have a store, look for the correct certificate.  (There may
+  // have been more than one in the PFX file, e.g., a certificate chain.)
+  PCCERT_CONTEXT certificate_context = NULL;
+  while ((certificate_context =
+          ::CertEnumCertificatesInStore(get(store_),
+                                        certificate_context)) != NULL) {
+    // Have a certificate, does it look like the right one?  Check the name
+    DWORD name_len = ::CertGetNameString(certificate_context,
+                                         kCertificateNameType,
+                                         0 /*flags*/,
+                                         NULL,
+                                         NULL,
+                                         0);
+    if (name_len <= 1) {
+      // Name attribute not found - should never happen
+      ASSERT(0, (L"CryptoSigningCertificate::ImportCertificate failed to get "
+                 L"certificate name length, err 0x%08lx", ::GetLastError()));
+      continue;
+    }
+    // name_len includes the terminating null
+
+    std::vector<TCHAR> name;
+    name.resize(name_len);
+    ASSERT1(!name.empty());
+    DWORD name_len2 = ::CertGetNameString(certificate_context,
+                                          kCertificateNameType,
+                                          0,
+                                          NULL,
+                                          &name.front(),
+                                          name_len);
+    ASSERT(name_len2 == name_len, (L""));
+
+    UTIL_LOG(L3, (L"[CryptoSigningCertificate::ImportCertificate]"
+                  L"[found '%s' in store]", &name.front()));
+
+    // Check the name if the user so desires.  (If subject_name == NULL then
+    // the first certificate found is used.)
+    if (subject_name && (0 != String_StrNCmp(&name.front(),
+                                             subject_name,
+                                             ::lstrlen(subject_name),
+                                             false))) {
+      // name mismatch
+      UTIL_LOG(L3, (L"[CryptoSigningCertificate::ImportCertificate]"
+                    L"[not the right certificate, we're looking for '%s']",
+                    subject_name));
+      continue;
+    }
+
+    // This is the right certificate
+    subject_name_ = &name.front();
+    reset(certificate_, certificate_context);
+    UTIL_LOG(L3, (L"[CryptoSigningCertificate::ImportCertificate]"
+                  L"[new certificate 0x%08lx]", get(certificate_)));
+    break;
+  }
+
+  return S_OK;
+}
+
+HRESULT CryptoSigningCertificate::GetCSPContext(HCRYPTPROV* csp_context) {
+  ASSERT(csp_context, (L""));
+  ASSERT(get(certificate_), (L""));
+
+  // CSP may have already been used - reset it
+  reset(csp_);
+
+  // Create a CSP context using the private key of the certificate we imported
+  // earlier.
+  HCRYPTPROV csp = NULL;
+  BOOL must_free_csp = FALSE;
+  BOOL b = ::CryptAcquireCertificatePrivateKey(get(certificate_),
+                                               0 /*flags*/,
+                                               0 /*reserved*/,
+                                               &csp,
+                                               &key_spec_,
+                                               &must_free_csp);
+  if (!b) {
+    DWORD err = ::GetLastError();
+    ASSERT(0, (L"CryptoSigningCertificate::GetCSPContext "
+               L"CryptAcquireCertificatePrivateKey failed, err 0x%08lx", err));
+    return HRESULT_FROM_WIN32(err);
+  }
+
+  // (Funky API returns a boolean which tells you whether it is your
+  // responsibility to delete the CSP context or not.)
+  if (must_free_csp) {
+    reset(csp_, csp);
+  }
+  if (get(csp_)) {
+    UTIL_LOG(L3, (L"[CryptoSigningCertificate::GetCSPContext new CSP 0x%08lx]",
+                  get(csp_)));
+  }
+
+  ASSERT(key_spec_ == AT_SIGNATURE || key_spec_ == AT_KEYEXCHANGE, (L""));
+  if (key_spec_ != kKeyPairType) {
+    UTIL_LOG(LE, (L"[CryptoSigningCertificate::GetCSPContext]"
+                  L"[requires a AT_SIGNATURE type key]"));
+    return SIGS_E_INVALID_KEY_TYPE;
+  }
+
+#ifdef _DEBUG
+  // Which CSP did we get?
+  char csp_name[256] = {0};
+  DWORD csp_name_len = arraysize(csp_name);
+  b = ::CryptGetProvParam(csp,
+                          PP_NAME,
+                          reinterpret_cast<BYTE*>(&csp_name[0]),
+                          &csp_name_len,
+                          0 /*flags*/);
+  if (!b) {
+    DWORD err = ::GetLastError();
+    UTIL_LOG(LE, (L"[CryptoSigningCertificate::GetCSPContext]"
+                  L"[error getting CSP name, err 0x%08lx]", err));
+  }
+  DWORD csp_prov_type;
+  DWORD csp_prov_type_len = sizeof(csp_prov_type);
+  b = ::CryptGetProvParam(csp,
+                          PP_PROVTYPE,
+                          reinterpret_cast<BYTE*>(&csp_prov_type),
+                          &csp_prov_type_len,
+                          0 /*flags*/);
+  if (!b) {
+    DWORD err = ::GetLastError();
+    UTIL_LOG(LE, (L"[CryptoSigningCertificate::GetCSPContext]"
+                  L"[error getting CSP provtype, err 0x%08lx]", err));
+  }
+  char csp_container[256] = {0};
+  DWORD csp_container_len = arraysize(csp_container);
+  b = ::CryptGetProvParam(csp,
+                          PP_CONTAINER,
+                          reinterpret_cast<BYTE*>(&csp_container[0]),
+                          &csp_container_len,
+                          0 /*flags*/);
+  if (!b) {
+    DWORD err = ::GetLastError();
+    UTIL_LOG(LE, (L"[CryptoSigningCertificate::GetCSPContext]"
+                  L"[error getting CSP current container name, err 0x%08lx]",
+                  err));
+  }
+  UTIL_LOG(L2, (L"[CryptoSigningCertificate::GetCSPContext]"
+                L"[have CSP '%S' (provtype %d) key container '%S']",
+                csp_name, csp_prov_type, csp_container));
+  // End of which CSP did we get
+#endif
+
+  *csp_context = csp;
+
+  UTIL_LOG(L2, (L"[CryptoSigningCertificate::GetCSPContext]"
+                L"[getting CSP with private key from certificate]"
+                L"[HCRYPTPROV 0x%08lx]", csp));
+
+  return S_OK;
+}
+
+// To sign some data using CryptoAPI you first hash it into a hash
+// object, then sign it using the CSP.  The CSP needs to have the
+// private key, of type AT_SIGNATURE, in it already, as it isn't a
+// parameter of the CryptSignHash API.  The CryptoSigningCertificate
+// can provide such a CSP.
+
+CryptoComputeSignature::CryptoComputeSignature(
+    CryptoSigningCertificate* certificate)
+    : certificate_(certificate) {
+}
+
+CryptoComputeSignature::~CryptoComputeSignature() {
+}
+
+HRESULT CryptoComputeSignature::Sign(TCHAR const * const filepath,
+                                     uint32 max_len,
+                                     std::vector<byte>* signature_out) {
+  ASSERT(filepath, (L""));
+  std::vector<byte> buffer;
+  HRESULT hr = ReadEntireFile(filepath, max_len, &buffer);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]"
+                  L"['%s not read, hr 0x%08lx]", filepath, hr));
+    return hr;
+  }
+  return Sign(buffer, signature_out);
+}
+
+HRESULT CryptoComputeSignature::Sign(const std::vector<byte>& buffer_in,
+                                     std::vector<byte>* signature_out) {
+  ASSERT(signature_out, (L""));
+  ASSERT1(!buffer_in.empty());
+
+  UTIL_LOG(L2, (L"[CryptoComputeSignature::Sign]"
+                L"[buffer of %d bytes]", buffer_in.size()));
+
+  // Get the proper CSP with the private key (certificate retains ownership)
+  HCRYPTPROV csp = NULL;
+  HRESULT hr = certificate_->GetCSPContext(&csp);
+  ASSERT(SUCCEEDED(hr) && csp, (L""));
+
+  // Hash the data
+  CryptDetails::scoped_crypt_hash hash;
+  BOOL b = ::CryptCreateHash(csp, kHashAlgorithm, 0, 0, address(hash));
+  if (!b) {
+    // hash is now invalid, but might not be NULL, so stomp on it
+    DWORD err = ::GetLastError();
+    ASSERT(!hash, (L""));
+    UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]"
+                  L"[could not create hash, err 0x%08lx]", err));
+    return HRESULT_FROM_WIN32(err);
+  }
+  UTIL_LOG(L3, (L"CryptoComputeSignature::Sign new hash 0x%08lx", get(hash)));
+
+  b = ::CryptHashData(get(hash), &buffer_in.front(), buffer_in.size(), 0);
+  if (!b) {
+    DWORD err = ::GetLastError();
+    UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]"
+                  L"[could not hash data, err 0x%08lx]", err));
+    return HRESULT_FROM_WIN32(err);
+  }
+
+  // Sign the hash (first get length, then allocate buffer and do real signing)
+  DWORD signature_len = 0;
+  b = ::CryptSignHash(get(hash),
+                      kKeyPairType,
+                      NULL,
+                      0 /*flags*/,
+                      NULL,
+                      &signature_len);
+  if (!b && ::GetLastError() != ERROR_MORE_DATA) {
+    DWORD err = ::GetLastError();
+    UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]"
+                  L"[could not compute size of signature, err 0x%08lx]", err));
+    return HRESULT_FROM_WIN32(err);
+  }
+  signature_out->resize(signature_len);
+  b = ::CryptSignHash(get(hash),
+                      kKeyPairType,
+                      NULL,
+                      0,
+                      &signature_out->front(),
+                      &signature_len);
+  if (!b) {
+    DWORD err = ::GetLastError();
+    UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]"
+                  L"[could not compute signature, err 0x%08lx]", err));
+    return HRESULT_FROM_WIN32(err);
+  }
+  ASSERT(signature_len == signature_out->size(), (L""));
+
+  UTIL_LOG(L3, (L"[CryptoComputeSignature::Sign]"
+                L"[have %d byte signature]", signature_out->size()));
+
+  return S_OK;
+}
+
+// To verify signed data you need a CSP, and you also need the public
+// key extracted from a certificate.  The CSP can be any RSA CSP on the
+// machine, the default one is fine.  To get the public key you start
+// by importing a certificate in standard "DER encoded" format.  That
+// returns a giant data structure, one field of which is the public key
+// in a format that CryptoAPI understands.  You import this public key
+// into the CSP with the CryptImportPublicKey() API, and then create a
+// key object from it suitable for use with the verification API.
+
+CryptoSignatureVerificationCertificate::CryptoSignatureVerificationCertificate() {   // NOLINT
+}
+
+CryptoSignatureVerificationCertificate::~CryptoSignatureVerificationCertificate() {  // NOLINT
+}
+
+HRESULT CryptoSignatureVerificationCertificate::ImportCertificate(
+    const TCHAR * filepath,
+    const TCHAR * subject_name) {
+  ASSERT(filepath, (L""));
+  std::vector<byte> buffer;
+  HRESULT hr = ReadEntireFile(filepath, kMaxCertificateSize, &buffer);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]"
+                  L"['%s' not read, hr 0x%08lx]", filepath, hr));
+    return hr;
+  }
+  return ImportCertificate(buffer, subject_name);
+}
+
+HRESULT CryptoSignatureVerificationCertificate::ImportCertificate(
+    const std::vector<byte>& certificate_in,
+    const TCHAR * subject_name) {
+  // Import the certificate
+  ASSERT1(!certificate_in.empty());
+  reset(certificate_, ::CertCreateCertificateContext(kEncodingType,
+                                                     &certificate_in.front(),
+                                                     certificate_in.size()));
+  if (!certificate_) {
+    DWORD err = ::GetLastError();
+    UTIL_LOG(LE, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]"
+                  L"[could not import certificate, err 0x%08lx]", err));
+    return SIGS_E_INVALID_DER_CERTIFICATE;
+  }
+  UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]"
+                L"[new certificate 0x%08lx]", get(certificate_)));
+
+  // Get certificate's subject name
+  DWORD name_len = ::CertGetNameString(get(certificate_),
+                                       kCertificateNameType,
+                                       0 /*flags*/,
+                                       NULL,
+                                       NULL,
+                                       0);
+  if (name_len <= 1) {
+    // Name attribute not found - should never happen
+    ASSERT(0, (L"CryptoSignatureVerificationCertificate failed to get "
+               L"certificate name length, err 0x%08lx", ::GetLastError()));
+    return E_FAIL;
+  }
+  // name_len includes the terminating NULL
+
+  std::vector <TCHAR> name;
+  name.resize(name_len);
+  ASSERT1(!name.empty());
+  DWORD name_len2 = ::CertGetNameString(get(certificate_),
+                                        kCertificateNameType,
+                                        0,
+                                        NULL,
+                                        &name.front(),
+                                        name_len);
+  ASSERT(name_len2 == name_len, (L""));
+
+  UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]"
+                L"['%s' is subject of certificate]", &name.front()));
+
+  subject_name_ = &name.front();
+
+  // Check the name if the user so desires.
+  if (subject_name && (0 != String_StrNCmp(&name.front(),
+                                           subject_name,
+                                           ::lstrlen(subject_name), false))) {
+      // name mismatch
+    UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]"
+                  L"[not the right certificate, we're looking for '%s']",
+                  subject_name));
+    return E_FAIL;
+  }
+
+  return S_OK;
+}
+
+HRESULT CryptoSignatureVerificationCertificate::GetCSPContextAndKey(
+    HCRYPTPROV* csp_context,
+    HCRYPTKEY* public_key) {
+  ASSERT(csp_context, (L""));
+  ASSERT(public_key, (L""));
+  ASSERT(get(certificate_), (L""));
+
+  // Get the public key out of the certificate
+  PCERT_INFO cert_info = get(certificate_)->pCertInfo;
+  ASSERT(cert_info, (L""));
+  PCERT_PUBLIC_KEY_INFO public_key_info = &cert_info->SubjectPublicKeyInfo;
+  ASSERT(public_key_info, (L""));
+
+  // Reset the CSP and key in case it has been used already
+  reset(key_);
+  reset(csp_);
+
+  // Get the default CSP.  With CRYPT_VERIFYCONTEXT don't need to worry
+  // about creating/destroying a key container.
+  // TODO(omaha):  Why wasn't PROV_RSA_SIG available?  Maybe looking for the
+  // default isn't a good idea?
+  BOOL b = ::CryptAcquireContext(address(csp_),
+                                 NULL,
+                                 NULL,
+                                 kProviderType,
+                                 CRYPT_VERIFYCONTEXT|CRYPT_SILENT);
+  if (!b) {
+    DWORD err = ::GetLastError();
+    UTIL_LOG(LE, (L"[GetCSPContextAndKey]"
+                  L"[failed to acquire CSP, err 0x%08lx]", err));
+    return HRESULT_FROM_WIN32(err);
+  }
+  UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::GetCSPContextAndKey]"
+                L"[new CSP 0x%08lx]", get(csp_)));
+
+  // Convert the public key in encoded form into a CryptoAPI HCRYPTKEY
+  b = ::CryptImportPublicKeyInfo(get(csp_),
+                                 kEncodingType,
+                                 public_key_info,
+                                 address(key_));
+  if (!b) {
+    DWORD err = ::GetLastError();
+    UTIL_LOG(LE, (L"[GetCSPContextAndKey]"
+                  L"[failed to import public key, err 0x%08lx]", err));
+    return HRESULT_FROM_WIN32(err);
+  }
+  UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::GetCSPContextAndKey]"
+                L"[new key 0x%08lx]", get(key_)));
+
+  *csp_context = get(csp_);
+  *public_key = get(key_);
+
+  return S_OK;
+}
+
+// To verify the signature of some data using CryptoAPI you first hash
+// it into a hash object, then verify it using the CSP and a public key.
+// In this case the CryptVerifySignature takes the key (of type
+// AT_SIGNATURE) as a separate parameter. The
+// CryptoSignatureVerificationCertificate can provide the proper CSP and
+// the public key from the certificate.
+
+CryptoVerifySignature::CryptoVerifySignature(
+    CryptoSignatureVerificationCertificate& certificate)
+    : certificate_(&certificate) {
+}
+
+CryptoVerifySignature::~CryptoVerifySignature() {
+}
+
+HRESULT CryptoVerifySignature::Validate(const TCHAR* filepath,
+                                        uint32 max_len,
+                                        const std::vector<byte>& signature_in) {
+  ASSERT(filepath, (L""));
+  std::vector<byte> buffer;
+  HRESULT hr = ReadEntireFile(filepath, max_len, &buffer);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (L"[CryptoVerifySignature::Validate]"
+                  L"['%s' not read, hr 0x%08lx]", filepath, hr));
+    return hr;
+  }
+  return Validate(buffer, signature_in);
+}
+
+HRESULT CryptoVerifySignature::Validate(const std::vector<byte>& buffer_in,
+                                        const std::vector<byte>& signature_in) {
+  ASSERT(certificate_, (L""));
+  ASSERT1(!buffer_in.empty());
+  ASSERT1(!signature_in.empty());
+
+  UTIL_LOG(L2, (L"[CryptoVerifySignature::Validate]"
+                L"[buffer of %d bytes, signature of %d bytes]",
+                buffer_in.size(), signature_in.size()));
+
+  // Get the CSP context and the public key from the certificate
+  HCRYPTPROV csp = NULL;
+  HCRYPTKEY key = NULL;
+  HRESULT hr = certificate_->GetCSPContextAndKey(&csp, &key);
+  ASSERT(SUCCEEDED(hr) && csp && key, (L""));
+
+  // Hash the data
+  CryptDetails::scoped_crypt_hash hash;
+  BOOL b = ::CryptCreateHash(csp, kHashAlgorithm, 0, 0, address(hash));
+  if (!b) {
+    // hash is now invalid, but might not be NULL, so stomp on it
+    DWORD err = ::GetLastError();
+    ASSERT(!hash, (L""));
+    UTIL_LOG(LE, (L"[CrypoVerifySignature::Validate]"
+                  L"[could not create hash], err 0x%08lx", err));
+    return HRESULT_FROM_WIN32(err);
+  }
+  UTIL_LOG(L3, (L"CryptoVerifySignature::Validate new hash 0x%08lx", hash));
+
+  b = ::CryptHashData(get(hash),
+                      &buffer_in.front(),
+                      buffer_in.size(),
+                      0 /*flags*/);
+  if (!b) {
+    DWORD err = ::GetLastError();
+    UTIL_LOG(LE, (L"[CryptoVerifySignature::Validate]"
+                  L"[could not hash data, err 0x%08lx]", err));
+    return HRESULT_FROM_WIN32(err);
+  }
+
+  // Verify the hash
+  b = ::CryptVerifySignature(get(hash),
+                             &signature_in.front(),
+                             signature_in.size(),
+                             key,
+                             NULL,
+                             0 /*flags*/);
+  if (!b) {
+    DWORD err = ::GetLastError();
+#ifdef LOGGING
+    CString encoded_signature;
+    Base64::Encode(signature_in, &encoded_signature, false);
+
+    UTIL_LOG(LE, (_T("CryptoVerifySignature::Validate could not ")
+                  _T("verify signature, err 0x%08lx with sig \"%s\""),
+                  err, encoded_signature));
+#endif
+    if (err == NTE_BAD_SIGNATURE)
+      return SIGS_E_INVALID_SIGNATURE;
+    else
+      return HRESULT_FROM_WIN32(err);
+  }
+
+  return S_OK;
+}
+
+HRESULT SignData(const TCHAR* certificate_path,
+                 const TCHAR* certificate_password,
+                 const TCHAR* certificate_subject_name,
+                 const std::vector<byte>& data,
+                 CString* signature_base64) {
+  ASSERT(certificate_path, (L""));
+  ASSERT(certificate_password, (L""));
+  // certificate_subject_name can be NULL
+  ASSERT(signature_base64, (L""));
+
+  CryptoSigningCertificate certificate;
+  RET_IF_FAILED(certificate.ImportCertificate(certificate_path,
+                                              certificate_password,
+                                              certificate_subject_name));
+
+  CryptoComputeSignature signer(&certificate);
+  std::vector<byte> signature;
+  RET_IF_FAILED(signer.Sign(data, &signature));
+  RET_IF_FAILED(Base64::Encode(signature, signature_base64, false));
+
+  return S_OK;
+}
+
+HRESULT VerifyData(const TCHAR* certificate_path,
+                   const TCHAR* certificate_subject_name,
+                   const std::vector<byte>& data,
+                   const TCHAR* signature_base64) {
+  ASSERT(certificate_path, (L""));
+  // certificate_subject_name can be NULL
+  ASSERT(signature_base64, (L""));
+
+  std::vector<byte> signature;
+  RET_IF_FAILED(Base64::Decode(CString(signature_base64), &signature));
+
+  CryptoSignatureVerificationCertificate certificate;
+  RET_IF_FAILED(certificate.ImportCertificate(certificate_path,
+                                              certificate_subject_name));
+
+  CryptoVerifySignature verifier(certificate);
+  RET_IF_FAILED(verifier.Validate(data, signature));
+
+  return S_OK;
+}
+
+HRESULT VerifyData(const std::vector<byte>& certificate_buffer,
+                   const TCHAR* certificate_subject_name,
+                   const std::vector<byte>& data,
+                   const TCHAR* signature_base64) {
+  // certificate_subject_name can be NULL
+  ASSERT(signature_base64, (L""));
+
+  std::vector<byte> signature;
+  RET_IF_FAILED(Base64::Decode(CString(signature_base64), &signature));
+
+  CryptoSignatureVerificationCertificate certificate;
+  RET_IF_FAILED(certificate.ImportCertificate(certificate_buffer,
+                                              certificate_subject_name));
+
+  CryptoVerifySignature verifier(certificate);
+  RET_IF_FAILED(verifier.Validate(data, signature));
+
+  return S_OK;
+}
+
+// TODO(omaha): function is missing the unit test.
+HRESULT AuthenticateFiles(const std::vector<CString>& files,
+                          const CString& hash) {
+  ASSERT1(!files.empty());
+
+  std::vector<byte> hash_vector;
+  RET_IF_FAILED(Base64::Decode(hash, &hash_vector));
+  if (hash_vector.size() != CryptoHash::kHashSize) {
+    return E_INVALIDARG;
+  }
+
+  CryptoHash crypto;
+  return crypto.Validate(files, kMaxFileSizeForAuthentication, hash_vector);
+}
+
+}  // namespace omaha
+
diff --git a/base/signatures.h b/base/signatures.h
new file mode 100644
index 0000000..2b2b507
--- /dev/null
+++ b/base/signatures.h
@@ -0,0 +1,296 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// signatures.h
+//
+// Classes and functions related to crypto-hashes of buffers and digital
+// signatures of buffers.
+
+#ifndef OMAHA_BASE_SIGNATURES_H_
+#define OMAHA_BASE_SIGNATURES_H_
+
+#include <windows.h>
+#include <wincrypt.h>
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "omaha/base/scoped_any.h"
+
+namespace omaha {
+
+// Forward decls of classes defined here
+class CryptoHash;
+class CryptoComputeSignature;
+class CryptoVerifySignature;
+class CryptoSigningCertificate;
+class CryptoSignatureVerificationCertificate;
+
+// Useful scoped pointers for working with CryptoAPI objects
+namespace CryptDetails {
+
+  void crypt_close_store(HCERTSTORE);
+  void crypt_release_context(HCRYPTPROV);
+  void crypt_free_certificate(PCCERT_CONTEXT);
+  void crypt_destroy_key(HCRYPTKEY);
+
+  typedef close_fun<void (*)(HCERTSTORE),crypt_close_store>          smart_close_store;       // NOLINT
+  typedef close_fun<void (*)(HCRYPTPROV),crypt_release_context>      smart_release_context;   // NOLINT
+  typedef close_fun<void (*)(PCCERT_CONTEXT),crypt_free_certificate> smart_free_certificate;  // NOLINT
+  typedef close_fun<void (*)(HCRYPTKEY),crypt_destroy_key>           smart_destroy_key;       // NOLINT
+
+  typedef scoped_any<HCERTSTORE,smart_close_store,null_t>            scoped_crypt_store;      // NOLINT
+  typedef scoped_any<HCRYPTPROV,smart_release_context,null_t>        scoped_crypt_context;    // NOLINT
+  typedef scoped_any<PCCERT_CONTEXT,smart_free_certificate,null_t>   scoped_crypt_cert;       // NOLINT
+  typedef scoped_any<HCRYPTKEY,smart_destroy_key,null_t>             scoped_crypt_key;        // NOLINT
+}
+
+// A namespace for encoding binary into base64 (portable ASCII) representation
+// and for decoding it again.
+namespace Base64 {
+  // Binary -> base64 in a buffer
+  HRESULT Encode(const std::vector<byte>& buffer_in,
+                 std::vector<byte>* encoded,
+                 bool break_into_lines = true);
+
+  // Binary -> base64 in a string
+  HRESULT Encode(const std::vector<byte>& buffer_in,
+                 CStringA* encoded,
+                 bool break_into_lines = true);
+
+  // Binary -> base64 in a wide string
+  HRESULT Encode(const std::vector<byte>& buffer_in,
+                 CString* encoded,
+                 bool break_into_lines = true);
+
+  // Base64 in a buffer -> binary
+  HRESULT Decode(const std::vector<byte>& encoded,
+                 std::vector<byte>* buffer_out);
+
+  // Base64 in a CStringA -> binary
+  HRESULT Decode(const CStringA& encoded, std::vector<byte>* buffer_out);
+
+  // Base64 in a CString -> binary
+  HRESULT Decode(const CString& encoded, std::vector<byte>* buffer_out);
+}
+
+
+// Compute and validate SHA-1 hashes of data
+class CryptoHash {
+  public:
+
+    CryptoHash();
+    ~CryptoHash();
+
+    static const int kHashSize = 20;
+
+    // Hash a file
+    HRESULT Compute(const TCHAR * filepath,
+                    uint64 max_len,
+                    std::vector<byte>* hash_out);
+
+    // Hash a list of files
+    HRESULT Compute(const std::vector<CString>& filepaths,
+                    uint64 max_len,
+                    std::vector<byte>* hash_out);
+
+    // Hash a buffer
+    HRESULT Compute(const std::vector<byte>& buffer_in,
+                    std::vector<byte>* hash_out);
+
+    // Verify hash of a file
+    HRESULT Validate(const TCHAR * filepath,
+                     uint64 max_len,
+                     const std::vector<byte>& hash_in);
+
+    // Verify hash of a list of files
+    HRESULT Validate(const std::vector<CString>& filepaths,
+                     uint64 max_len,
+                     const std::vector<byte>& hash_in);
+
+    // Verify hash of a buffer
+    HRESULT Validate(const std::vector<byte>& buffer_in,
+                     const std::vector<byte>& hash_in);
+
+  private:
+    // Compute or verify hash of a file
+    HRESULT ComputeOrValidate(const std::vector<CString>& filepaths,
+                              uint64 max_len,
+                              const std::vector<byte>* hash_in,
+                              std::vector<byte>* hash_out);
+
+    // Compute or verify hash of a buffer
+    HRESULT ComputeOrValidate(const std::vector<byte>& buffer_in,
+                              const std::vector<byte>* hash_in,
+                              std::vector<byte>* hash_out);
+
+    DISALLOW_EVIL_CONSTRUCTORS(CryptoHash);
+};
+
+
+// Import and use a certificate for signing data (has a private key)
+class CryptoSigningCertificate {
+  public:
+    CryptoSigningCertificate();
+    ~CryptoSigningCertificate();
+
+    // Import certificate - with both public key and private key.
+    // Must be in PFX format.  Password must unlock the PFX file.
+    // subject_name is the certificate's subject name (who it was
+    // issued to) - if not NULL then it is checked for an exact
+    // match against the certificate.
+
+    // User can get the certificate in PFX format by following the procedure at
+    // http://support.globalsign.net/en/objectsign/transform.cfm
+
+    HRESULT ImportCertificate(const TCHAR * filepath,
+                              const TCHAR * password,
+                              const TCHAR * subject_name);
+
+    HRESULT ImportCertificate(const std::vector<byte>& certificate_in,
+                              const TCHAR * password,
+                              const TCHAR * subject_name);
+
+    CString subject_name() { return subject_name_; }
+
+  private:
+    static const int kMaxCertificateSize = 100000;
+
+    CryptDetails::scoped_crypt_store   store_;
+    CryptDetails::scoped_crypt_cert    certificate_;
+    CryptDetails::scoped_crypt_context csp_;
+    CString subject_name_;
+    DWORD key_spec_;
+
+    friend class CryptoComputeSignature;
+    // Get the CSP with the private key
+    // (CryptoSigningCertificate retains ownership of csp_context.)
+    HRESULT GetCSPContext(HCRYPTPROV* csp_context);
+
+    DISALLOW_EVIL_CONSTRUCTORS(CryptoSigningCertificate);
+};
+
+
+// Compute digital signatures
+class CryptoComputeSignature {
+  public:
+    explicit CryptoComputeSignature(CryptoSigningCertificate* certificate);
+    ~CryptoComputeSignature();
+
+    // Sign a file, returning a separate signature
+    HRESULT Sign(const TCHAR * filepath,
+                 uint32 max_len,
+                 std::vector<byte>* signature_out);
+
+    // Sign a chunk of memory, returning a separate signature
+    HRESULT Sign(const std::vector<byte>& buffer_in,
+                 std::vector<byte>* signature_out);
+
+  private:
+    // Does not take ownership of the certificate
+    CryptoSigningCertificate* const certificate_;
+
+    DISALLOW_EVIL_CONSTRUCTORS(CryptoComputeSignature);
+};
+
+
+// Import and use a certificate for verifying signatures (has public key only)
+class CryptoSignatureVerificationCertificate {
+  public:
+    CryptoSignatureVerificationCertificate();
+    ~CryptoSignatureVerificationCertificate();
+
+    // Import certificate - with only public key.  Must be in DER (.cer) format.
+    // subject_name is the certificate's subject name (who it was
+    // issued to) - if not NULL then it is checked for an exact
+    // match against the certificate.
+
+    // User can get certificate in DER format (.cer) by exporting from
+    // certmgr.exe, using openssl, etc.)
+
+    HRESULT ImportCertificate(const TCHAR * filepath,
+                              const TCHAR * subject_name);
+    HRESULT ImportCertificate(const std::vector<byte>& certificate_in,
+                              const TCHAR * subject_name);
+
+    CString subject_name() { return subject_name_; }
+
+  private:
+    static const int kMaxCertificateSize = 100000;
+
+    CryptDetails::scoped_crypt_cert    certificate_;
+    CryptDetails::scoped_crypt_context csp_;
+    CryptDetails::scoped_crypt_key     key_;
+    CString subject_name_;
+
+    friend class CryptoVerifySignature;
+    // Get the CSP and the public key
+    // (CryptoSignatureVerificationCertificate retains ownership of csp_context
+    // and public_key.)
+    HRESULT GetCSPContextAndKey(HCRYPTPROV* csp_context, HCRYPTKEY* public_key);
+
+    DISALLOW_EVIL_CONSTRUCTORS(CryptoSignatureVerificationCertificate);
+};
+
+
+// Verify digital signatures
+class CryptoVerifySignature {
+  public:
+    explicit CryptoVerifySignature(
+        CryptoSignatureVerificationCertificate& certificate);
+    ~CryptoVerifySignature();
+
+    // Validate signature of a file, signature given separately
+    HRESULT Validate(const TCHAR * filepath,
+                     uint32 max_len,
+                     const std::vector<byte>& signature_in);
+
+    // Validate signature of a buffer of data, signature given separately
+    HRESULT Validate(const std::vector<byte>& buffer_in,
+                     const std::vector<byte>& signature_in);
+
+  private:
+    // Does not take ownership of the certificate
+    CryptoSignatureVerificationCertificate* const certificate_;
+
+    DISALLOW_EVIL_CONSTRUCTORS(CryptoVerifySignature);
+};
+
+
+// All-in-one routine to sign a chunk of data and return the signature
+// (encoded in base64)
+HRESULT SignData(const TCHAR* certificate_path,
+                 const TCHAR* certificate_password,
+                 const TCHAR* certificate_subject_name,
+                 const std::vector<byte>& data,
+                 CString* signature_base64);
+
+// All-in-one routine to verify the signature of a chunk of data
+HRESULT VerifyData(const TCHAR* certificate_path,
+                   const TCHAR* certificate_subject_name,
+                   const std::vector<byte>& data,
+                   const TCHAR* signature_base64);
+
+HRESULT VerifyData(const std::vector<byte>& certificate_buffer,
+                   const TCHAR* certificate_subject_name,
+                   const std::vector<byte>& data,
+                   const TCHAR* signature_base64);
+
+// Authenticate files
+HRESULT AuthenticateFiles(const std::vector<CString>& files,
+                          const CString& hash);
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_SIGNATURES_H_
diff --git a/base/signatures_unittest.cc b/base/signatures_unittest.cc
new file mode 100644
index 0000000..8bb82ad
--- /dev/null
+++ b/base/signatures_unittest.cc
@@ -0,0 +1,213 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// signatures_unittest.cpp
+//
+// Unittests for classes and functions related to crypto-hashes of buffers and
+// digital signatures of buffers.
+// TODO(omaha): There are a number of places inside the signatures code, where
+// empty vector iterators were being dereferenced. Ensure that all these are
+// being tested.
+
+#include <cstring>
+#include <vector>
+#include "omaha/base/app_util.h"
+#include "omaha/base/error.h"
+#include "omaha/base/module_utils.h"
+#include "omaha/base/path.h"
+#include "omaha/base/signatures.h"
+#include "omaha/base/utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+struct {
+  char* binary;
+  char* base64;
+} test_data[] = {
+  "",                                "",
+  "what",                            "d2hhdA==",
+  "what will print out",             "d2hhdCB3aWxsIHByaW50IG91dA==",
+  "foobar",                          "Zm9vYmFy",
+  "a man, a plan, a canal: panama!", "YSBtYW4sIGEgcGxhbiwgYSBjYW5hbDogcGFuYW1hIQ==",    // NOLINT
+};
+
+// This test data from http://en.wikipedia.org/wiki/SHA-1:
+struct {
+  char* binary;
+  byte  hash[20];
+} test_hash[] = {
+  "The quick brown fox jumps over the lazy dog",
+    0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28, 0xfc, 0xed, 0x84,
+    0x9e, 0xe1, 0xbb, 0x76, 0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12,
+  "The quick brown fox jumps over the lazy cog",
+    0xde, 0x9f, 0x2c, 0x7f, 0xd2, 0x5e, 0x1b, 0x3a, 0xfa, 0xd3,
+    0xe8, 0x5a, 0x0b, 0xd1, 0x7d, 0x9b, 0x10, 0x0d, 0xb4, 0xb3,
+};
+
+}  // namespace
+
+TEST(SignaturesTest, Base64) {
+  for (size_t i = 0; i != arraysize(test_data); i++) {
+    std::vector<byte> buffer(strlen(test_data[i].binary));
+    if (strlen(test_data[i].binary) != 0) {
+      memcpy(&buffer.front(), test_data[i].binary, strlen(test_data[i].binary));
+    }
+    CStringA test_e;
+    ASSERT_SUCCEEDED(Base64::Encode(buffer, &test_e));
+    ASSERT_STREQ(test_e, test_data[i].base64);
+    std::vector<byte> test_d;
+    uint32 test_d_written = 0;
+    ASSERT_SUCCEEDED(Base64::Decode(test_e, &test_d));
+    ASSERT_EQ(test_d.size(), strlen(test_data[i].binary));
+    if (strlen(test_data[i].binary) != 0) {
+      ASSERT_EQ(0, memcmp(&test_d.front(),
+                          test_data[i].binary,
+                          strlen(test_data[i].binary)));
+    }
+  }
+}
+
+TEST(SignaturesTest, CryptoHash) {
+  CryptoHash chash;
+  for (size_t i = 0; i != arraysize(test_hash); i++) {
+    std::vector<byte> buffer(strlen(test_hash[i].binary));
+    memcpy(&buffer.front(), test_hash[i].binary, strlen(test_hash[i].binary));
+    std::vector<byte> hash;
+    ASSERT_SUCCEEDED(chash.Compute(buffer, &hash));
+    ASSERT_EQ(hash.size(), CryptoHash::kHashSize);
+    ASSERT_EQ(0, memcmp(&hash.front(),
+                        test_hash[i].hash,
+                        CryptoHash::kHashSize));
+    ASSERT_SUCCEEDED(chash.Validate(buffer, hash));
+  }
+}
+
+TEST(SignaturesTest, CreationVerification) {
+  TCHAR module_directory[MAX_PATH] = {0};
+  ASSERT_TRUE(GetModuleDirectory(NULL, module_directory));
+  CString directory;
+  directory.Format(_T("%s\\unittest_support"), module_directory);
+
+  CString encoded_cert_with_private_key_path;
+  encoded_cert_with_private_key_path.AppendFormat(
+      _T("%s\\certificate-with-private-key.pfx"), directory);
+  CString encoded_cert_without_private_key_path;
+  encoded_cert_without_private_key_path.AppendFormat(
+      _T("%s\\certificate-without-private-key.cer"), directory);
+  CString raw_test_data_path;
+  raw_test_data_path.AppendFormat(_T("%s\\declaration.txt"), directory);
+
+  // Get cert with private key and cert without private key.
+  std::vector<byte> encoded_cert_with_private_key;
+  std::vector<byte> encoded_cert_without_private_key;
+  ASSERT_SUCCEEDED(ReadEntireFile(encoded_cert_with_private_key_path,
+                                  0,
+                                  &encoded_cert_with_private_key));
+  ASSERT_SUCCEEDED(ReadEntireFile(encoded_cert_without_private_key_path,
+                                  0,
+                                  &encoded_cert_without_private_key));
+  CString cert_password = _T("f00bar");
+  CString cert_subject_name = _T("Unofficial Google Test");
+
+  // Get testdata.
+  std::vector<byte> raw_testdata;
+  ASSERT_SUCCEEDED(ReadEntireFile(raw_test_data_path, 0, &raw_testdata));
+
+  // Create a signing certificate.
+  CryptoSigningCertificate signing_certificate;
+  ASSERT_SUCCEEDED(signing_certificate.ImportCertificate(
+      encoded_cert_with_private_key, cert_password, cert_subject_name));
+
+  // Create a signature object and sign the test data.
+  std::vector<byte> signature;
+  CryptoComputeSignature signer(&signing_certificate);
+  ASSERT_SUCCEEDED(signer.Sign(raw_testdata, &signature));
+
+  // Create a validating certificate.
+  CryptoSignatureVerificationCertificate verification_certificate;
+  ASSERT_SUCCEEDED(verification_certificate.ImportCertificate(
+      encoded_cert_without_private_key, cert_subject_name));
+
+  // Create a signature object and verify the test data's signature.
+  CryptoVerifySignature verifier(verification_certificate);
+  ASSERT_SUCCEEDED(verifier.Validate(raw_testdata, signature));
+
+  // Mess up the signature and show it doesn't verify.
+  size_t mid = signature.size() / 2;
+  byte mid_byte = signature[mid];
+  signature[mid] = ~mid_byte;
+  ASSERT_FAILED(verifier.Validate(raw_testdata, signature));
+
+  // Restore the signature, mess up the test data, and show it doesn't verify.
+  signature[mid] = mid_byte;
+  mid = raw_testdata.size() / 2;
+  mid_byte = raw_testdata[mid];
+  raw_testdata[mid] = ~mid_byte;
+  ASSERT_FAILED(verifier.Validate(raw_testdata, signature));
+}
+
+TEST(SignaturesTest, AuthenticateFiles) {
+  const CString executable_path(app_util::GetCurrentModuleDirectory());
+
+  const CString source_file1 = ConcatenatePath(
+      executable_path,
+      _T("unittest_support\\download_cache_test\\")
+      _T("{89640431-FE64-4da8-9860-1A1085A60E13}\\gears-win32-opt.msi"));
+
+  const CString hash_file1 = _T("ImV9skETZqGFMjs32vbZTvzAYJU=");
+
+  const CString source_file2 = ConcatenatePath(
+       executable_path,
+       _T("unittest_support\\download_cache_test\\")
+       _T("{7101D597-3481-4971-AD23-455542964072}\\livelysetup.exe"));
+
+  const CString hash_file2 = _T("Igq6bYaeXFJCjH770knXyJ6V53s=");
+
+  const CString hash_files = _T("e2uzy96jlusKbADl87zie6F5iwE=");
+
+  const CString bad_hash = _T("sFzmoHgCbowEnioqVb8WanTYbhIabcde=");
+
+  std::vector<CString> files;
+
+  // Authenticate one file.
+  files.push_back(source_file1);
+  EXPECT_HRESULT_SUCCEEDED(AuthenticateFiles(files, hash_file1));
+
+  // Incorrect hash.
+  EXPECT_EQ(SIGS_E_INVALID_SIGNATURE, AuthenticateFiles(files, hash_file2));
+
+  // Bad hash.
+  EXPECT_EQ(E_INVALIDARG, AuthenticateFiles(files, bad_hash));
+  EXPECT_EQ(E_INVALIDARG, AuthenticateFiles(files, _T("")));
+
+  // Authenticate two files.
+  files.push_back(source_file2);
+  EXPECT_HRESULT_SUCCEEDED(AuthenticateFiles(files, hash_files));
+
+  // Round trip through CryptoHash::Compute to verify the hash of two files.
+  CryptoHash crypto;
+  std::vector<byte> hash_out;
+  EXPECT_HRESULT_SUCCEEDED(crypto.Compute(files, 0, &hash_out));
+
+  CStringA actual_hash_files;
+  EXPECT_HRESULT_SUCCEEDED(Base64::Encode(hash_out, &actual_hash_files));
+  EXPECT_STREQ(hash_files, CString(actual_hash_files));
+}
+
+}  // namespace omaha
+
diff --git a/base/signaturevalidator.cc b/base/signaturevalidator.cc
new file mode 100644
index 0000000..c845e34
--- /dev/null
+++ b/base/signaturevalidator.cc
@@ -0,0 +1,521 @@
+// Copyright 2002-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include "omaha/base/signaturevalidator.h"
+
+#include <atltime.h>
+#include <softpub.h>
+#include <wincrypt.h>
+#include <wintrust.h>
+#pragma warning(push)
+// C4100: unreferenced formal parameter
+// C4310: cast truncates constant value
+// C4548: expression before comma has no effect
+#pragma warning(disable : 4100 4310 4548)
+#include "base/basictypes.h"
+#pragma warning(pop)
+#include "omaha/base/constants.h"
+#include "omaha/base/error.h"
+
+namespace omaha {
+
+namespace {
+
+const LPCTSTR kEmptyStr = _T("");
+const DWORD kCertificateEncoding = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
+
+// Gets a handle to the certificate store and optionally the cryptographic
+// message from the specified file.
+// The caller is responsible for closing the store and message.
+// message can be NULL if the handle is not needed.
+HRESULT GetCertStoreFromFile(const wchar_t* signed_file,
+                             HCERTSTORE* cert_store,
+                             HCRYPTMSG* message) {
+  if (!signed_file || !cert_store) {
+    return E_INVALIDARG;
+  }
+
+  // Get message handle and store handle from the signed file.
+  if (!::CryptQueryObject(CERT_QUERY_OBJECT_FILE,
+                          signed_file,
+                          CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
+                          CERT_QUERY_FORMAT_FLAG_BINARY,
+                          0,              // reserved, must be 0
+                          NULL,           // pdwMsgAndCertEncodingType
+                          NULL,           // pdwContentType
+                          NULL,           // pdwFormatType
+                          cert_store,
+                          message,
+                          NULL)) {        // ppvContext
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  return S_OK;
+}
+
+// Gets the signer info from the crypt message.
+// The caller is responsible for freeing the signer info using LocalFree.
+HRESULT GetSignerInfo(HCRYPTMSG message, PCMSG_SIGNER_INFO* signer_info) {
+  if (!signer_info) {
+    return E_INVALIDARG;
+  }
+  *signer_info = NULL;
+
+  DWORD info_size = 0;
+  if (!::CryptMsgGetParam(message,
+                          CMSG_SIGNER_INFO_PARAM,
+                          0,
+                          NULL,
+                          &info_size)) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  *signer_info = static_cast<PCMSG_SIGNER_INFO>(::LocalAlloc(LPTR, info_size));
+  if (!*signer_info) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  if (!::CryptMsgGetParam(message,
+                          CMSG_SIGNER_INFO_PARAM,
+                          0,
+                          *signer_info,
+                          &info_size)) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  return S_OK;
+}
+
+// Gets the signer info for the time stamp signature in the specified signature.
+HRESULT GetTimeStampSignerInfo(PCMSG_SIGNER_INFO signer_info,
+                               PCMSG_SIGNER_INFO* countersigner_info) {
+  if (!signer_info || !countersigner_info) {
+    return E_INVALIDARG;
+  }
+  *countersigner_info = NULL;
+
+  PCRYPT_ATTRIBUTE attr = NULL;
+
+  // The countersigner info is contained in the unauthenticated attributes and
+  // indicated by the szOID_RSA_counterSign OID.
+  for (size_t i = 0; i < signer_info->UnauthAttrs.cAttr; ++i) {
+    if (lstrcmpA(szOID_RSA_counterSign,
+                 signer_info->UnauthAttrs.rgAttr[i].pszObjId) == 0) {
+      attr = &signer_info->UnauthAttrs.rgAttr[i];
+      break;
+    }
+  }
+
+  if (!attr) {
+    return E_FAIL;
+  }
+
+  // Decode and get CMSG_SIGNER_INFO structure for the timestamp certificate.
+  DWORD data_size = 0;
+  if (!::CryptDecodeObject(kCertificateEncoding,
+                           PKCS7_SIGNER_INFO,
+                           attr->rgValue[0].pbData,
+                           attr->rgValue[0].cbData,
+                           0,
+                           NULL,
+                           &data_size)) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  *countersigner_info =
+      static_cast<PCMSG_SIGNER_INFO>(::LocalAlloc(LPTR, data_size));
+  if (!*countersigner_info) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  if (!::CryptDecodeObject(kCertificateEncoding,
+                           PKCS7_SIGNER_INFO,
+                           attr->rgValue[0].pbData,
+                           attr->rgValue[0].cbData,
+                           0,
+                           *countersigner_info,
+                           &data_size)) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  return S_OK;
+}
+
+// Gets the time of the date stamp for the specified signature.
+// The time is in UTC.
+HRESULT GetDateOfTimeStamp(PCMSG_SIGNER_INFO signer_info,
+                           SYSTEMTIME* system_time) {
+  if (!signer_info || !system_time) {
+    return E_INVALIDARG;
+  }
+
+  PCRYPT_ATTRIBUTE attr = NULL;
+
+  // The signing time is contained in the authenticated attributes and
+  // indicated by the szOID_RSA_signingTime OID.
+  for (size_t i = 0; i < signer_info->AuthAttrs.cAttr; ++i) {
+    if (lstrcmpA(szOID_RSA_signingTime,
+                 signer_info->AuthAttrs.rgAttr[i].pszObjId) == 0) {
+      attr = &signer_info->AuthAttrs.rgAttr[i];
+      break;
+    }
+  }
+
+  if (!attr) {
+    return E_FAIL;
+  }
+
+  FILETIME file_time = {0};
+
+  // Decode and get FILETIME structure.
+  DWORD data_size = sizeof(file_time);
+  if (!::CryptDecodeObject(kCertificateEncoding,
+                           szOID_RSA_signingTime,
+                           attr->rgValue[0].pbData,
+                           attr->rgValue[0].cbData,
+                           0,
+                           &file_time,
+                           &data_size)) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  if (!::FileTimeToSystemTime(&file_time, system_time)) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  return S_OK;
+}
+
+}  // namespace
+
+CertInfo::CertInfo(const CERT_CONTEXT* given_cert_context)
+    : cert_context_(NULL) {
+  if (given_cert_context) {
+    // CertDuplicateCertificateContext just increases reference count of a given
+    // CERT_CONTEXT.
+    cert_context_ = CertDuplicateCertificateContext(given_cert_context);
+    not_valid_before_ = cert_context_->pCertInfo->NotBefore;
+    not_valid_after_ = cert_context_->pCertInfo->NotAfter;
+    // Extract signed party details.
+    ExtractIssuerInfo(cert_context_,
+                      &issuing_company_name_,
+                      &issuing_dept_name_,
+                      &trust_authority_name_);
+  }
+}
+
+CertInfo::~CertInfo() {
+  // Decrement reference count, if needed.
+  if (cert_context_)
+    CertFreeCertificateContext(cert_context_);
+}
+
+
+bool CertInfo::IsValidNow() const {
+  // we cannot directly get current time in FILETIME format.
+  // so first get it in SYSTEMTIME format and convert it into FILETIME.
+  SYSTEMTIME now;
+  GetSystemTime(&now);
+  FILETIME filetime_now;
+  SystemTimeToFileTime(&now, &filetime_now);
+  // CompareFileTime() is a windows function
+  return ((CompareFileTime(&filetime_now, &not_valid_before_) > 0)
+          && (CompareFileTime(&filetime_now, &not_valid_after_) < 0));
+}
+
+
+CString CertInfo::FileTimeToString(const FILETIME* ft) {
+  if (ft == NULL)
+    return _T("");
+  SYSTEMTIME st;
+  if (!FileTimeToSystemTime(ft, &st))
+    return _T("");
+
+  // Build a string showing the date and time.
+  CString time_str;
+  time_str.Format(_T("%02d/%02d/%d  %02d:%02d"), st.wDay, st.wMonth, st.wYear,
+    st.wHour, st.wMinute);
+  return time_str;
+}
+
+
+bool CertInfo::ExtractField(const CERT_CONTEXT* cert_context,
+                            const char* field_name,
+                            CString* field_value) {
+  if ((!cert_context) || (!field_name) || (!field_value)) {
+    return false;
+  }
+
+  field_value->Empty();
+
+  DWORD num_chars = ::CertGetNameString(cert_context,
+                                        CERT_NAME_ATTR_TYPE,
+                                        0,
+                                        const_cast<char*>(field_name),
+                                        NULL,
+                                        0);
+  if (num_chars > 1) {
+    num_chars = ::CertGetNameString(cert_context,
+                                  CERT_NAME_ATTR_TYPE,
+                                  0,
+                                  const_cast<char*>(field_name),
+                                  CStrBuf(*field_value, num_chars),
+                                  num_chars);
+  }
+
+  return num_chars > 1 ? true : false;
+}
+
+
+bool CertInfo::ExtractIssuerInfo(const CERT_CONTEXT* cert_context,
+                                 CString* orgn_name,
+                                 CString* orgn_dept_name,
+                                 CString* trust_authority) {
+  // trust-authority is optional, so no check.
+  if ((!orgn_name) || (!orgn_dept_name)) {
+    return false;
+  }
+
+  ExtractField(cert_context, szOID_COMMON_NAME, orgn_name);
+  ExtractField(cert_context, szOID_ORGANIZATIONAL_UNIT_NAME, orgn_dept_name);
+  if (trust_authority != NULL) {
+    ExtractField(cert_context, szOID_ORGANIZATION_NAME, trust_authority);
+  }
+
+  return true;
+}
+
+
+void CertList::FindFirstCert(CertInfo** result_cert_info,
+                             const CString &company_name_to_match,
+                             const CString &orgn_unit_to_match,
+                             const CString &trust_authority_to_match,
+                             bool allow_test_variant,
+                             bool check_cert_is_valid_now) {
+  if (!result_cert_info)
+    return;
+  (*result_cert_info) = NULL;
+
+  for (CertInfoList::const_iterator cert_iter = cert_list_.begin();
+       cert_iter != cert_list_.end();
+       ++cert_iter) {
+    // If any of the criteria does not match, continue on to next certificate
+    if (!company_name_to_match.IsEmpty()) {
+      const TCHAR* certificate_company_name =
+          (*cert_iter)->issuing_company_name_;
+      bool names_match = company_name_to_match == certificate_company_name;
+      if (!names_match && allow_test_variant) {
+        CString test_variant = company_name_to_match;
+        test_variant += _T(" (TEST)");
+        names_match = test_variant == certificate_company_name;
+      }
+      if (!names_match)
+        continue;
+    }
+    if (!orgn_unit_to_match.IsEmpty() &&
+        orgn_unit_to_match != (*cert_iter)->issuing_dept_name_)
+      continue;
+    if (!trust_authority_to_match.IsEmpty() &&
+        trust_authority_to_match != (*cert_iter)->trust_authority_name_)
+      continue;
+    // All the criteria matched. But, add only if it is a valid certificate.
+    if (!check_cert_is_valid_now || (*cert_iter)->IsValidNow()) {
+      (*result_cert_info) = (*cert_iter);
+      return;
+    }
+  }
+}
+
+
+void ExtractAllCertificatesFromSignature(const wchar_t* signed_file,
+                                         CertList* cert_list) {
+  if ((!signed_file) || (!cert_list))
+    return;
+
+  DWORD encoding_type = 0, content_type = 0, format_type = 0;
+  // If successful, cert_store will be populated by
+  // a store containing all the certificates related to the file signature.
+  HCERTSTORE cert_store = NULL;
+  BOOL succeeded = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
+                    signed_file,
+                    CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
+                    CERT_QUERY_FORMAT_FLAG_ALL,
+                    0,               // has to be zero as documentation says
+                    &encoding_type,  // DWORD *pdwMsgAndCertEncodingType,
+                    &content_type,   // DWORD *pdwContentType,
+                    &format_type,    // DWORD *pdwFormatType,
+                    &cert_store,     // HCERTSTORE *phCertStore,
+                    NULL,            // HCRYPTMSG *phMsg,
+                    NULL);           // const void** pvContext
+
+  if (succeeded && (cert_store != NULL)) {
+    PCCERT_CONTEXT   cert_context_ptr = NULL;
+    while ((cert_context_ptr =
+            CertEnumCertificatesInStore(cert_store, cert_context_ptr))
+           != NULL) {
+      CertInfo* cert_info = new CertInfo(cert_context_ptr);
+      cert_list->AddCertificate(cert_info);
+    }
+  }
+  if (cert_store) {
+    CertCloseStore(cert_store, 0);
+  }
+  return;
+}
+
+// Only check the CN. The OU can change.
+// TODO(omaha): A better way to implement the valid now check would be to add
+// a parameter to VerifySignature that adds WTD_LIFETIME_SIGNING_FLAG.
+bool VerifySigneeIsGoogleInternal(const wchar_t* signed_file,
+                                  bool check_cert_is_valid_now) {
+  CertList cert_list;
+  ExtractAllCertificatesFromSignature(signed_file, &cert_list);
+  if (cert_list.size() > 0) {
+    CertInfo* required_cert = NULL;
+    // now, see if one of the certificates in the signature belongs to Google.
+    cert_list.FindFirstCert(&required_cert,
+                            kCertificateSubjectName,
+                            CString(),
+                            CString(),
+                            true,
+                            check_cert_is_valid_now);
+    if (required_cert != NULL) {
+      return true;
+    }
+  }
+  return false;
+}
+
+// Does not verify that the certificate is currently valid.
+// VerifySignature verifies that the certificate was valid at signing time as
+// part of the normal signature verification.
+bool VerifySigneeIsGoogle(const wchar_t* signed_file) {
+  return VerifySigneeIsGoogleInternal(signed_file, false);
+}
+
+HRESULT VerifySignature(const wchar_t* signed_file, bool allow_network_check) {
+  // Don't pop up any windows
+  HWND const kWindowMode = reinterpret_cast<HWND>(INVALID_HANDLE_VALUE);
+
+  // Verify file & certificates
+  GUID verification_type = WINTRUST_ACTION_GENERIC_VERIFY_V2;
+
+  // Info for the file we're going to verify
+  WINTRUST_FILE_INFO file_info = {0};
+  file_info.cbStruct = sizeof(file_info);
+  file_info.pcwszFilePath = signed_file;
+
+  // Info for request to WinVerifyTrust
+  WINTRUST_DATA trust_data;
+  ZeroMemory(&trust_data, sizeof(trust_data));
+  trust_data.cbStruct = sizeof(trust_data);
+  trust_data.dwUIChoice = WTD_UI_NONE;               // no graphics
+  // No additional revocation checking -- note that this flag does not
+  // cancel the flag we set in dwProvFlags; it specifies that no -additional-
+  // checks are to be performed beyond the provider-specified ones.
+  trust_data.fdwRevocationChecks = WTD_REVOKE_NONE;
+  trust_data.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
+
+  if (!allow_network_check)
+    trust_data.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL;
+
+  trust_data.dwUnionChoice = WTD_CHOICE_FILE;        // check a file
+  trust_data.pFile = &file_info;                     // check this file
+
+  // If the trust provider verifies that the subject is trusted for the
+  // specified action, the return value is zero. No other value besides zero
+  // should be considered a successful return.
+  LONG result = ::WinVerifyTrust(kWindowMode, &verification_type, &trust_data);
+  if (result != 0) {
+    return FAILED(result) ? result : HRESULT_FROM_WIN32(result);
+  }
+  return S_OK;
+}
+
+// This method must not return until the end to avoid leaking memory.
+// More info on Authenticode Signatures Time Stamping can be found at
+// http://msdn2.microsoft.com/en-us/library/bb931395.aspx.
+HRESULT GetSigningTime(const wchar_t* signed_file, SYSTEMTIME* signing_time) {
+  if (!signed_file || !signing_time) {
+    return E_INVALIDARG;
+  }
+
+  HCERTSTORE cert_store = NULL;
+  HCRYPTMSG message = NULL;
+  PCMSG_SIGNER_INFO signer_info = NULL;
+  PCMSG_SIGNER_INFO countersigner_info = NULL;
+
+  HRESULT hr = GetCertStoreFromFile(signed_file, &cert_store, &message);
+
+  if (SUCCEEDED(hr)) {
+    hr = GetSignerInfo(message, &signer_info);
+  }
+
+  if (SUCCEEDED(hr)) {
+    hr = GetTimeStampSignerInfo(signer_info, &countersigner_info);
+  }
+
+  if (SUCCEEDED(hr)) {
+    hr = GetDateOfTimeStamp(countersigner_info, signing_time);
+  }
+
+  if (cert_store) {
+    ::CertCloseStore(cert_store, 0);
+  }
+  if (message) {
+    ::CryptMsgClose(message);
+  }
+  ::LocalFree(signer_info);
+  ::LocalFree(countersigner_info);
+
+  return hr;
+}
+
+HRESULT VerifyFileSignedWithinDays(const wchar_t* signed_file, int days) {
+  if (!signed_file || days <= 0) {
+    return E_INVALIDARG;
+  }
+
+  SYSTEMTIME signing_time = {0};
+  HRESULT hr = GetSigningTime(signed_file, &signing_time);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Use the Win32 API instead of CTime::GetCurrentTime() because the latter
+  // is broken in VS 2003 and 2005 and doesn't account for the timezone.
+  SYSTEMTIME current_system_time = {0};
+  ::GetSystemTime(&current_system_time);
+
+  CTime signed_time(signing_time);
+  CTime current_time(current_system_time);
+
+  if (current_time <= signed_time) {
+    return TRUST_E_TIME_STAMP;
+  }
+
+  CTimeSpan time_since_signed = current_time - signed_time;
+  CTimeSpan max_duration(days, 0, 0, 0);
+
+  if (max_duration < time_since_signed) {
+    return TRUST_E_TIME_STAMP;
+  }
+
+  return S_OK;
+}
+
+}  // namespace omaha
+
diff --git a/common/signaturevalidator.h b/base/signaturevalidator.h
similarity index 100%
rename from common/signaturevalidator.h
rename to base/signaturevalidator.h
diff --git a/base/signaturevalidator_unittest.cc b/base/signaturevalidator_unittest.cc
new file mode 100644
index 0000000..6517bc1
--- /dev/null
+++ b/base/signaturevalidator_unittest.cc
@@ -0,0 +1,118 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Unit tests for the Google file signature validation.
+
+#include <windows.h>
+#include <atlstr.h>
+#include "omaha/base/app_util.h"
+#include "omaha/base/file.h"
+#include "omaha/base/signaturevalidator.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+class SignatureValidatorTest : public testing::Test {
+  virtual void SetUp() {
+  }
+
+  virtual void TearDown() {
+  }
+};
+
+
+TEST_F(SignatureValidatorTest, VerifySigneeIsGoogle_OfficiallySigned) {
+  const TCHAR kRelativePath[] = _T("unittest_support\\SaveArguments.exe");
+
+  CString executable_full_path(app_util::GetCurrentModuleDirectory());
+  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kRelativePath));
+  ASSERT_TRUE(File::Exists(executable_full_path));
+  EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path));
+}
+
+// Tests a certificate subject containing multiple CNs such as:
+//    "CN = Google Inc (TEST), CN = Some Other CN, ...
+// The code exactly matches on the first CN only.
+TEST_F(SignatureValidatorTest, VerifySigneeIsGoogle_TestSigned_MultipleCN) {
+  const TCHAR kRelativePath[] =
+      _T("unittest_support\\SaveArguments_multiple_cn.exe");
+
+  CString executable_full_path(app_util::GetCurrentModuleDirectory());
+  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kRelativePath));
+  ASSERT_TRUE(File::Exists(executable_full_path));
+  EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path));
+}
+
+TEST_F(SignatureValidatorTest,
+       VerifySigneeIsGoogle_OfficiallySigned_DifferentOU) {
+  const TCHAR kRelativePath[] =
+      _T("unittest_support\\SaveArguments_different_ou.exe");
+
+  CString executable_full_path(app_util::GetCurrentModuleDirectory());
+  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kRelativePath));
+  ASSERT_TRUE(File::Exists(executable_full_path));
+  EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path));
+}
+
+TEST_F(SignatureValidatorTest, VerifySigneeIsGoogle_OmahaTestSigned) {
+  const TCHAR kRelativePath[] =
+      _T("unittest_support\\SaveArguments_OmahaTestSigned.exe");
+
+  CString executable_full_path(app_util::GetCurrentModuleDirectory());
+  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kRelativePath));
+  ASSERT_TRUE(File::Exists(executable_full_path));
+  EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path));
+}
+
+// The certificate was valid when it was used to sign the executable, but it has
+// since expired.
+TEST_F(SignatureValidatorTest, VerifySigneeIsGoogle_SignedWithNowExpiredCert) {
+  const TCHAR kRelativePath[] =
+      _T("unittest_support\\GoogleUpdate_now_expired_cert.exe");
+
+  CString executable_full_path(app_util::GetCurrentModuleDirectory());
+  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kRelativePath));
+  ASSERT_TRUE(File::Exists(executable_full_path));
+  EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path));
+}
+
+TEST_F(SignatureValidatorTest, VerifySigneeIsGoogle_TestSigned_NoCN) {
+  const TCHAR kRelativePath[] =
+      _T("unittest_support\\SaveArguments_no_cn.exe");
+
+  CString executable_full_path(app_util::GetCurrentModuleDirectory());
+  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kRelativePath));
+  ASSERT_TRUE(File::Exists(executable_full_path));
+  EXPECT_FALSE(VerifySigneeIsGoogle(executable_full_path));
+}
+
+TEST_F(SignatureValidatorTest, VerifySigneeIsGoogle_TestSigned_WrongCN) {
+  const TCHAR kRelativePath[] =
+      _T("unittest_support\\SaveArguments_wrong_cn.exe");
+
+  CString executable_full_path(app_util::GetCurrentModuleDirectory());
+  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kRelativePath));
+  ASSERT_TRUE(File::Exists(executable_full_path));
+  EXPECT_FALSE(VerifySigneeIsGoogle(executable_full_path));
+}
+
+}  // namespace omaha
diff --git a/base/single_instance.cc b/base/single_instance.cc
new file mode 100644
index 0000000..c536baf
--- /dev/null
+++ b/base/single_instance.cc
@@ -0,0 +1,196 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Synchronization functions
+
+#include "omaha/base/single_instance.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+// Check to see whether an instance is already running across all sessions.
+// If not, enter single instance protection. The user must call Shutdown()
+// on that SingleInstance once single instance protection is no longer needed.
+bool SingleInstance::StartupSingleInstance(const TCHAR* id) {
+  ASSERT1(id);
+
+  bool already_running = false, already_running_in_different_session = false;
+  HRESULT hr = Startup(id,
+                       &already_running,
+                       &already_running_in_different_session);
+  ASSERT(SUCCEEDED(hr), (_T("")));
+
+  return already_running || already_running_in_different_session;
+}
+
+// Check to see whether an instance is already running in this session. If not,
+// enter session-only single instance protection. The user must call Shutdown()
+// on that SingleInstance once single instance protection is no longer needed.
+bool SingleInstance::StartupSingleSessionInstance(const TCHAR* id) {
+  ASSERT1(id);
+
+  bool already_running = false;
+  HRESULT hr = Startup(id, &already_running, NULL);
+  ASSERT(SUCCEEDED(hr), (_T("")));
+
+  return already_running;
+}
+
+// Startup a single instance protection. The user must call Shutdown() on
+// that SingleInstance once the single instance protection is no longer needed.
+//
+// Returns whether or not the process is already running
+// already_running means "already running in same session".
+// already_running_in_different_session means "already running on machine"
+HRESULT SingleInstance::Startup(const TCHAR* id,
+                                bool* already_running,
+                                bool* already_running_in_different_session) {
+  ASSERT1(id);
+  ASSERT1(already_running);
+
+  CString mutex_id;
+
+  // Use two mutexes: one to check for being the only instance in this
+  // session, and one for being the only instance in any terminal session.
+  // Only create (and check) the global mutex for one-per-machine check if
+  // the result is asked for.
+  // We don't actually obtain ownership of the mutex
+  // For information on the "Local" and "Global" namespace prefixes, see MSDN
+  // article "Kernel Object Namespaces".
+
+  // Create a user level mutex
+  CreateSyncId(id, SYNC_USER, &mutex_id);
+  RET_IF_FAILED(CreateInstanceMutex(mutex_id,
+                                    &user_mutex_handle_,
+                                    already_running));
+
+  // Create a global mutex
+  if (already_running_in_different_session) {
+    CreateSyncId(id, SYNC_GLOBAL, &mutex_id);
+    RET_IF_FAILED(CreateInstanceMutex(mutex_id,
+                                      &global_mutex_handle_,
+                                      already_running_in_different_session));
+  }
+
+  return S_OK;
+}
+
+// Create a mutex
+HRESULT SingleInstance::CreateInstanceMutex(const TCHAR* mutex_id,
+                                            HANDLE* mutex_handle,
+                                            bool* already_running) {
+  ASSERT1(mutex_id && *mutex_id);
+  ASSERT1(mutex_handle);
+  ASSERT1(already_running);
+
+  *already_running = false;
+
+  *mutex_handle = ::CreateMutex(NULL, false, mutex_id);
+  DWORD last_error = ::GetLastError();
+
+  // We check for both values because we sometimes see access
+  // denied.  We expect this to mean that the mutex was created by a
+  // different set of user credentials, which shouldn't happen under
+  // normal circumstances in our applications, but in fact we did
+  // see it happen.
+  if (last_error == ERROR_ALREADY_EXISTS || last_error == ERROR_ACCESS_DENIED) {
+    *already_running = true;
+    return S_OK;
+  }
+
+  if (*mutex_handle == NULL) {
+    HRESULT hr = HRESULT_FROM_WIN32(last_error);
+    ASSERT(false, (_T("[SingleInstance::CreateInstanceMutex]")
+                   _T("[failed to create mutex][%s][0x%x]"), mutex_id, hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+// Shutdown a single instance protection
+HRESULT SingleInstance::Shutdown() {
+  if (user_mutex_handle_) {
+    VERIFY(::CloseHandle(user_mutex_handle_), (_T("")));
+    user_mutex_handle_ = NULL;
+  }
+
+  if (global_mutex_handle_) {
+    VERIFY(::CloseHandle(global_mutex_handle_), (_T("")));
+    global_mutex_handle_ = NULL;
+  }
+
+  return S_OK;
+}
+
+// Check to see whether an instance is already running
+HRESULT SingleInstance::CheckAlreadyRunning(
+    const TCHAR* id,
+    bool* already_running,
+    bool* already_running_in_different_session) {
+  ASSERT1(id);
+  ASSERT1(already_running);
+
+  CString mutex_id;
+
+  // Open a user level mutex
+  CreateSyncId(id, SYNC_USER, &mutex_id);
+  RET_IF_FAILED(OpenInstanceMutex(mutex_id, already_running));
+
+  // Open a global mutex
+  if (already_running_in_different_session) {
+    CreateSyncId(id, SYNC_GLOBAL, &mutex_id);
+    RET_IF_FAILED(OpenInstanceMutex(mutex_id,
+                                    already_running_in_different_session));
+  }
+
+  return S_OK;
+}
+
+// Open a mutex
+HRESULT SingleInstance::OpenInstanceMutex(const TCHAR* mutex_id,
+                                          bool* already_running) {
+  ASSERT1(mutex_id && *mutex_id);
+  ASSERT1(already_running);
+
+  *already_running = false;
+
+  scoped_handle mutex_handle(::OpenMutex(NULL, false, mutex_id));
+  DWORD last_error = ::GetLastError();
+
+  if (get(mutex_handle) || last_error == ERROR_ACCESS_DENIED) {
+    UTIL_LOG(L3, (_T("[SingleInstance::OpenInstanceMutex]")
+                  _T("[already running][0x%x]"), last_error));
+    *already_running = true;
+    return S_OK;
+  }
+
+  if (last_error != ERROR_FILE_NOT_FOUND) {
+    HRESULT hr = HRESULT_FROM_WIN32(last_error);
+    ASSERT(false, (_T("[SingleInstance::OpenInstanceMutex]")
+                   _T("[failed to open mutex][%s][0x%x]"), mutex_id, hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+}  // namespace omaha
+
diff --git a/base/single_instance.h b/base/single_instance.h
new file mode 100644
index 0000000..af1ca07
--- /dev/null
+++ b/base/single_instance.h
@@ -0,0 +1,83 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+// Single Instance of a process running (plus some synchronization functions
+// that should be moved elsewhere)
+//
+// synchronization functions
+
+#ifndef OMAHA_COMMON_SINGLE_INSTANCE_H_
+#define OMAHA_COMMON_SINGLE_INSTANCE_H_
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "omaha/base/debug.h"
+
+namespace omaha {
+
+// Used to ensure only a single instance of a process per machine
+// (e.g., even in terminal server sessions)
+class SingleInstance {
+ public:
+  // Constructor
+  SingleInstance() : user_mutex_handle_(NULL), global_mutex_handle_(NULL) {}
+
+  // Destructor
+  ~SingleInstance() { Shutdown(); }
+
+  // Check to see whether an instance is already running across all sessions.
+  // If not, enter single instance protection. The user must call Shutdown()
+  // on that SingleInstance once single instance protection is no longer needed.
+  bool StartupSingleInstance(const TCHAR* id);
+
+  // Check to see whether an instance is already running for this user. If not,
+  // enter user-only single instance protection. The user must call Shutdown()
+  // on that SingleInstance once single instance protection is no longer needed.
+  bool StartupSingleSessionInstance(const TCHAR* id);
+
+  // Startup a single instance protection. The user must call Shutdown() on
+  // that SingleInstance once the single instance protection is no longer needed.
+  HRESULT Startup(const TCHAR* id,
+                  bool* already_running,
+                  bool* already_running_in_different_session);
+
+  // Shutdown a single instance protection
+  HRESULT Shutdown();
+
+  // Check to see whether an instance is already running
+  static HRESULT CheckAlreadyRunning(
+      const TCHAR* id,
+      bool* already_running,
+      bool* already_running_in_different_session);
+
+ private:
+  // Create a mutex
+  static HRESULT CreateInstanceMutex(const TCHAR* mutex_id,
+                                     HANDLE* mutex_handle,
+                                     bool* already_running);
+
+  // Open a mutex
+  static HRESULT OpenInstanceMutex(const TCHAR* mutex_id,
+                                   bool* already_running);
+
+  HANDLE user_mutex_handle_;
+  HANDLE global_mutex_handle_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(SingleInstance);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_SINGLE_INSTANCE_H_
diff --git a/base/singleton.h b/base/singleton.h
new file mode 100644
index 0000000..fc7c043
--- /dev/null
+++ b/base/singleton.h
@@ -0,0 +1,133 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Defines two classes
+// 1. class SingletonBase.
+// 2. template class Singleton.
+// Creation of singletons is a common
+// activity. Use Singleton class to not
+// repeat code every time.
+
+#ifndef OMAHA_COMMON_SINGLETON_H_
+#define OMAHA_COMMON_SINGLETON_H_
+
+#include "omaha/base/debug.h"
+#include "omaha/base/synchronized.h"
+
+
+// Very important design pattern.
+// Singleton class can be used in two ways.
+// 1. Pass the class you want to make into Singleton as a
+//    template parameter.
+//
+//    class SomeClass {
+//     protected:
+//    SomeClass(){}  // note - it is protected
+//    ~SomeClass(){} // note - it is protected
+//    public:
+//    void Foo() {}
+//  };
+//
+//   Singleton<SomeClass> s;
+//   s::Instance()->Foo();
+//
+//     OR
+// 2. You class can be derived from Singleton in a following way:
+//    class SomeClass : public Singleton<SomeClass> {
+//     protected:
+//    SomeClass(){}
+//    ~SomeClass(){}
+//    public:
+//    void Foo() {}
+//  };
+//
+//   SomeClass::Instance()->Foo();
+//
+//  There is no requirement on the class you want to make into
+//  Singleton except is has to have constructor that takes nothing.
+//  As long as the class has void constructor it can become a singleton.
+//  However if you want you class to be trully singleton you have to make
+//  it constructors and destructors protected. Than you can only access your
+//  class through the singlenot interface Instance().
+//  If simple void constructor is not enough for you class, provide some kind of
+//  initialization function, which could be called after the instance is
+// created.
+
+#define kSingletonMutexName               kLockPrefix L"Singleton_Creation_Lock"
+
+#ifdef _DEBUG
+  #define InstanceReturnTypeDeclaration SingletonProxy<T>
+  #define InstanceReturnTypeStatement   SingletonProxy<T>
+#else
+  #define InstanceReturnTypeDeclaration T*
+  #define InstanceReturnTypeStatement
+#endif
+
+template <typename T> class Singleton  {
+  // Caching pointers to Singletons is very dangerous and goes against
+  // Singleton philosophy. So we will return proxy from instance in Debug mode.
+  // In release mode we will not go this route for efficiency.
+  template <typename T> class SingletonProxy {
+    T* data_;
+  public:
+    explicit SingletonProxy(T* data) : data_(data) {}
+    T* operator->() const  {
+      return data_;
+    }
+    SingletonProxy& operator=(const SingletonProxy&);
+  };
+
+ public:
+  Singleton() {}
+
+  // Use double-check pattern for efficiency.
+  // TODO(omaha): the pattern is broken on multicore.
+  static InstanceReturnTypeDeclaration Instance() {
+    if(instance_ == NULL) {
+      // We use GLock here since LLock will not give us synchronization and
+      // SimpleLock will create deadlock if one singleton is created in the
+      // constructor of the other singleton.
+      GLock creation_lock;
+      TCHAR mutex_name[MAX_PATH] = {0};
+      wsprintf(mutex_name, L"%s%d",
+               kSingletonMutexName, ::GetCurrentProcessId());
+
+      VERIFY1(creation_lock.Initialize(mutex_name));
+      __mutexScope(creation_lock);
+      if(instance_ == NULL)
+         instance_ = GetInstance();
+    }
+    return InstanceReturnTypeStatement(instance_);
+  }
+
+ private:
+  static T* GetInstance() {
+    static MyT my_t;
+    return &my_t;
+  }
+
+  // shared between the same type T.
+  static T * instance_;
+
+  // Needed to access the protected constructor
+  // of a client.
+  class MyT : public T {
+  };
+};
+
+// This instance_ is shared between template of the same type.
+template <typename T>  T* Singleton<T>::instance_ = NULL;
+
+#endif  // OMAHA_COMMON_SINGLETON_H_
diff --git a/common/smart_handle.h b/base/smart_handle.h
similarity index 100%
rename from common/smart_handle.h
rename to base/smart_handle.h
diff --git a/base/sta.cc b/base/sta.cc
new file mode 100644
index 0000000..86604d4
--- /dev/null
+++ b/base/sta.cc
@@ -0,0 +1,353 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/sta.h"
+
+#include <atlbase.h>
+#include <atlwin.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/sta_call.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+namespace {
+
+class CallDispatcher;
+
+// ApartmentState maintains the state of the apartment. It keeps a reference
+// to a call dispatcher. The dispatcher is basically a window object that
+// handles user messages corresponding to each call.
+//
+// ApartmentState is a singleton. It's lifetime is controlled by the
+// 'InitializeApartment' and 'UnintializeApartment'.
+//
+// The current implementation is limited to the main STA. Creating multiple
+// apartments in a process is not possible.
+//
+// TODO(omaha): implement a simple reference counting of the threads to make
+// sure the all the calling threads have returned when the apt is destroyed.
+
+class ApartmentState {
+ public:
+  ApartmentState(DWORD thread_id, CallDispatcher* call_disp)
+      : thread_id_(thread_id), call_dispatcher_(call_disp) {}
+
+  ~ApartmentState() {
+    ASSERT1(ref_cnt_ == 0);
+  }
+
+  // Initialize the state of the singleton.
+  static HRESULT Initialize(DWORD reserved);
+
+  // Uninitialize the state of the singleton.
+  static HRESULT Uninitialize();
+
+  // Accessors.
+  static ApartmentState* apartment_state() {
+    return apartment_state_;
+  }
+
+  CallDispatcher& call_dispatcher() const {
+    ASSERT(call_dispatcher_.get(),
+      (_T("'InitializeApartment' has not been called.")));
+    return *call_dispatcher_;
+  }
+
+  DWORD thread_id() const {
+    ASSERT(thread_id_,
+      (_T("'InitializeApartment' has not been called.")));
+    return thread_id_;
+  }
+
+ private:
+  static int ref_cnt_;    // the reference count of init/uninit.
+
+  const DWORD thread_id_;   // thread that called the InitializeApartment
+  scoped_ptr<CallDispatcher> call_dispatcher_;
+
+  static ApartmentState* apartment_state_;    // the instance of the state
+  DISALLOW_EVIL_CONSTRUCTORS(ApartmentState);
+};
+
+
+// CallDispatcher uses functors to cross call from the caller's thread to
+// this apartment thread (main thread).
+class CallDispatcher
+    : public CWindowImpl<CallDispatcher,
+                         CWindow,
+                         CWinTraits<WS_OVERLAPPED, WS_EX_TOOLWINDOW> > {
+ public:
+  explicit CallDispatcher(DWORD options);
+  ~CallDispatcher();
+
+  // Two-phase initialization
+  HRESULT Init();
+
+  // The initiator of the cross call.
+  HRESULT DoCrossApartmentCall(BaseFunctor* caller, void* presult);
+
+ private:
+  static const UINT WM_METHOD_CALL = WM_USER + 0x100;
+  static const UINT WM_METHOD_CALL_COMPLETE = WM_USER + 0x101;
+
+  BEGIN_MSG_MAP(CallDispatcher)
+    MESSAGE_HANDLER(WM_METHOD_CALL, OnMethodCall)
+  END_MSG_MAP()
+
+ private:
+  LRESULT OnMethodCall(UINT uMsg, WPARAM wParam,
+    LPARAM lParam, BOOL& bHandled);
+
+  DWORD options_;
+
+  // Currently only one option is supported for testing purposes.
+  // It testing mode, this option disables the cross-call mechanism and
+  // directly calls the functor in the same thread as the invoker.
+  static const DWORD kTestingMode = DWORD(-1);
+};
+
+// initialize the static data memebers
+ApartmentState* ApartmentState::apartment_state_ = 0;
+int ApartmentState::ref_cnt_ = 0;
+
+HRESULT ApartmentState::Initialize(DWORD reserved) {
+  CORE_LOG(L3, (_T("[ApartmentState::Initialize]")));
+  ASSERT(ref_cnt_ >= 0, (_T("Apartment Reference Counting")));
+  if (ref_cnt_ < 0) return E_UNEXPECTED;
+
+  DWORD thread_id = ::GetCurrentThreadId();
+  ASSERT1(thread_id);
+
+  if (ref_cnt_ > 0) {
+    ASSERT(apartment_state(), (_T("Apartment State is 0.")));
+    bool same_thread = thread_id == apartment_state()->thread_id();
+    // if initialized multiple times verify the thread identity just in case
+    ASSERT(same_thread, (_T("Wrong Thread.")));
+    if (!same_thread) return E_UNEXPECTED;
+    ++ref_cnt_;
+    return S_OK;
+  }
+
+  ASSERT1(ref_cnt_ == 0);
+
+  // do the initialization of the apartment
+  scoped_ptr<CallDispatcher> call_disp(new CallDispatcher(reserved));
+  RET_IF_FAILED(call_disp->Init());
+  scoped_ptr<ApartmentState> ap_state(
+    new ApartmentState(thread_id, call_disp.get()));
+
+  call_disp.release();
+  ApartmentState::apartment_state_ = ap_state.release();
+
+  ++ref_cnt_;
+  return S_OK;
+}
+
+HRESULT ApartmentState::Uninitialize() {
+  ASSERT(ref_cnt_ > 0, (_T("Apartment Reference Counting")));
+  if (ref_cnt_ <= 0) return E_UNEXPECTED;
+
+  DWORD thread_id = ::GetCurrentThreadId();
+  ASSERT1(thread_id);
+
+  ASSERT(apartment_state(), (_T("Apartment State is 0.")));
+  bool same_thread = thread_id == apartment_state()->thread_id();
+  // verify the thread identity just in case
+  ASSERT(same_thread, (_T("Wrong Thread.")));
+  if (!same_thread) return E_UNEXPECTED;
+
+  if (--ref_cnt_ == 0) {
+    delete ApartmentState::apartment_state();
+    ApartmentState::apartment_state_ = 0;
+  }
+
+  return S_OK;
+}
+
+CallDispatcher::CallDispatcher(DWORD options) : options_(options) {
+  // TODO(omaha): Log
+}
+
+CallDispatcher::~CallDispatcher() {
+  // TODO(omaha): Log
+  if (m_hWnd) {
+    DestroyWindow();
+  }
+}
+
+HRESULT CallDispatcher::Init() {
+  // Create a message-only window for the dispatcher. It is not visible,
+  // has no z-order, cannot be enumerated, and does not receive broadcast
+  // messages. The window simply dispatches messages.
+  const TCHAR kWndName[] = _T("{FFE21900-612E-44a9-8424-3FC71B382E61}");
+  HWND hwnd = Create(HWND_MESSAGE, NULL, kWndName);
+  return hwnd ? S_OK : HRESULT_FROM_WIN32(::GetLastError());
+}
+
+//
+LRESULT CallDispatcher::OnMethodCall(UINT, WPARAM wParam,
+                                     LPARAM result, BOOL&) {
+  CORE_LOG(L6, (_T("[CallDispatcher::OnMethodCall]")));
+
+  ASSERT1(wParam);
+  BaseFunctor& call = *reinterpret_cast<BaseFunctor*>(wParam);
+
+  // presult is non-zero if the method or function has a return type.
+  // presult is zero for void methods and functions.
+
+  void* presult = reinterpret_cast<void*>(result);
+
+  ASSERT(
+    ApartmentState::apartment_state()->thread_id() == ::GetCurrentThreadId(),
+    (_T("Wrong Thread")));
+
+  // the function object virtual call;
+  call(presult);
+
+  bool is_async = call.is_async();
+  // For async calls, do not post a message, because the caller will not be
+  // waiting for the call to complete.
+  if (!is_async &&
+      !::PostThreadMessage(call.thread_id(), WM_METHOD_CALL_COMPLETE, 0, 0)) {
+    DWORD error = ::GetLastError();
+    CORE_LOG(LEVEL_ERROR,
+        (_T("[CallDispatcher::OnMethodCall - PostThreadMessage][%d]"), error));
+    ASSERT(false, (_T("Failed to PostThreadMessage.")));
+
+    // TODO(omaha): raise here.
+  }
+
+  // DO NOT ACCESS THE CALL OBJECT FROM DOWN ON. IN THE CASE OF A SYNCHRONOUS
+  // CALL THE CALL OBJECT MAY HAVE ALREADY DESTROYED.
+
+  if (is_async) {
+    // Auto cleanup of the call object in the case of a async call.
+    delete &call;
+  }
+
+  CORE_LOG(L6, (_T("CallDispatcher::OnMethodCall returns.")));
+  return true;
+}
+
+//
+HRESULT CallDispatcher::DoCrossApartmentCall(BaseFunctor* call,
+                                             void* presult) {
+  CORE_LOG(L6, (_T("[CallDispatcher::DoCrossApartmentCall]")));
+
+  ASSERT(IsWindow(), (_T("The dispatcher must have a window.")));
+  bool is_async = call->is_async();
+  if (options_ == kTestingMode) {
+    (*call)(presult);
+    if (is_async) {
+      // We need to delete the functor as if we were the callee.
+      delete call;
+    }
+    return S_OK;
+  }
+
+  if (!is_async) {
+    // Usually it is a mistake to call a synchronous method from the main STA
+    // to the main STA.
+
+    DWORD thread_id = ApartmentState::apartment_state()->thread_id();
+    ASSERT(thread_id != ::GetCurrentThreadId(), (_T("Wrong Thread")));
+
+    ASSERT(GetWindowThreadID() != ::GetCurrentThreadId(),
+           (_T("DoCrossApartmentCall calling its own thread.")));
+  }
+
+  if (!PostMessage(WM_METHOD_CALL,
+                   reinterpret_cast<WPARAM>(call),
+                   reinterpret_cast<LPARAM>(presult))) {
+    DWORD err = ::GetLastError();
+    CORE_LOG(LEVEL_ERROR,
+        (_T("[CallDispatcher::DoCrossApartmentCall - PostMessage][%d]"), err));
+    ASSERT(false, (_T("Failed to PostMessage.")));
+
+    return HRESULT_FROM_WIN32(err);
+  }
+
+  // Once the call has been made, do not access the state of the functor as
+  // the other end might have already executed the call and delete the functor.
+  // This is true for asyncronous calls but it would not hurt for synchronous
+  // calls as well.
+  call = NULL;
+
+  if (is_async) {
+    // Do not wait for the call to complete. The call will complete at
+    // some time in the future and the call object is going to be cleaned up.
+    return S_OK;
+  }
+
+  // Pump all messages, waiting for WM_METHOD_CALL_COMPLETE or WM_QUIT.
+  MSG msg;
+  SetZero(msg);
+  int ret = 0;
+  while ((ret = ::GetMessage(&msg, 0, 0, 0)) != 0) {
+    if (ret == -1) {
+      DWORD error = ::GetLastError();
+      CORE_LOG(LEVEL_ERROR,
+               (_T("[CallDispatcher::DoCrossApartmentCall - GetMessage][%d]"),
+               error));
+      // TODO(omaha): raise here.
+    }
+
+    if (msg.message == WM_METHOD_CALL_COMPLETE) {
+      break;
+    }
+
+    ::DispatchMessage(&msg);
+  }
+
+  // Repost the WM_QUIT message to properly exit all message loops.
+  if (msg.message == WM_QUIT) {
+    ASSERT1(ret == 0);
+    ::PostQuitMessage(msg.wParam);
+  }
+
+  CORE_LOG(L6, (_T("CallDispatcher::DoCrossApartmentCall returns")));
+  return S_OK;
+}
+
+}  // namespace
+
+void BaseFunctor::DoInvoke(void* presult) {
+  ASSERT(ApartmentState::apartment_state(),
+    (_T("Did you forgot to call 'InitializeApartment'?")));
+
+  CallDispatcher& call_dispatcher =
+    ApartmentState::apartment_state()->call_dispatcher();
+
+  HRESULT hr = call_dispatcher.DoCrossApartmentCall(this, presult);
+
+  if (FAILED(hr)) {
+    ASSERT(false, (_T("Failed to call across apartments.")));
+    // TODO(omaha): log, report, raise.
+  }
+}
+
+HRESULT InitializeApartment(DWORD reserved) {
+  return ApartmentState::Initialize(reserved);
+}
+
+HRESULT UninitializeApartment() {
+  return ApartmentState::Uninitialize();
+}
+
+}  // namespace omaha
+
diff --git a/base/sta.h b/base/sta.h
new file mode 100644
index 0000000..073f17c
--- /dev/null
+++ b/base/sta.h
@@ -0,0 +1,78 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// STA initializes a custom non-COM Single Threaded Apartment to facilitate
+// calling of functions and object methods in the thread of the STA. This is
+// useful when creating a simple threading model based on a main thread and
+// several worker threads, especially for client components that have a UI or
+// use the COM STA models.
+//
+// The current implementation only supports initializing the main STA, which is
+// the STA created by the main thread of the process. This is usually the UI
+// thread. Having multiple STA in a process is not possible yet.
+//
+// This custom STA does not interfere with the COM STAs.
+//
+// In order for the STA to work properly, the STA thread must keep processing
+// messages and not block, just like in the COM STA case.
+
+#ifndef OMAHA_COMMON_STA_H__
+#define OMAHA_COMMON_STA_H__
+
+#include <windows.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/scoped_any.h"
+
+namespace omaha {
+
+// Initializes the STA apartment. The 'reserved' parameter must be 0.
+// InitializeApartment and UninitializeApartment are reference-counted.
+HRESULT InitializeApartment(DWORD reserved);
+
+// Uninitializes the STA apartment.
+HRESULT UninitializeApartment();
+
+// A scoped_sta smart pointer is provided to manage the calls to
+// InitializeApartment and UninitializeApartment.
+inline HRESULT smart_sta_init_helper(DWORD reserved) {
+  return InitializeApartment(reserved);
+}
+
+inline void smart_uninit_helper(HRESULT result) {
+  if (result == S_OK) {
+    VERIFY1(SUCCEEDED(UninitializeApartment()));
+  }
+}
+
+typedef close_fun<void (*)(HRESULT), smart_uninit_helper> close_sta;
+
+typedef value_const<HRESULT, E_UNEXPECTED> sta_not_init;
+
+typedef scoped_any<HRESULT, close_sta, sta_not_init> scoped_sta_close;
+
+struct scoped_sta {
+  explicit scoped_sta(DWORD reserved)
+      : result_(smart_sta_init_helper(reserved)) {}
+
+  HRESULT result() const { return get(result_); }
+
+ private:
+  const scoped_sta_close result_;
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_STA_H__
+
diff --git a/base/sta_call.h b/base/sta_call.h
new file mode 100644
index 0000000..4727fa9
--- /dev/null
+++ b/base/sta_call.h
@@ -0,0 +1,1117 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// sta_call.h generics for cross apartment calling.
+//
+// The code is using compile-time and run-time polymorphism to create
+// type-safe call wrappers that can be used to cross call from a
+// worker thread to an STA thread. The current implementation only
+// supports calling the main STA.
+//
+// Functions as well as object methods can be called.
+//
+// Examples:
+// class X {
+//   public:
+//     int Add(int i, int j) { return i + j; }
+// };
+//
+//
+// namespace {
+//   int Add(long i, long j) { return i + j; }
+// }
+//
+// X x;
+// int sum = CallMethod(&x, X::Add, 10, 20);
+// int j = CallFunction(Add, -10, 10);
+//
+// The central piece of machinery is a hierarchy of functors. A functor is
+// instantiated by a function template (CallFunction or CallMethod) and its
+// 'Invoke' method gets called.
+// Calling 'Invoke' will send the functor using 'SendMessage'
+// to a window. The window message handler picks up the functor and
+// calls the functor virtual operator().
+// This virtual call is what actually calls the specified function or the
+// method, the only difference being that the call is now made in a thread
+// different than the thread that called 'Invoke'. There is a partial
+// specialization of the templates for void, so that void type is supported
+// as a return type.
+//
+//
+// !!! Limitations !!!
+//
+// There are a few important design and implementation limitations. They are
+// mostly related to template parameters ambiguities (T or T&) especially
+// for overloaded names or const types. The limitations are significant although
+// the code is useful enough as it is in most of the cases.
+// However, when using the code it is frustrating to discover that it does not
+// compile for obvious and useful cases, a constant reminder that a better
+// solution is to be seeked.
+//
+//
+// The implementation does not support calling all 'stdcall' calling convention.
+//
+// The design does not support calling functions or methods that use pass by
+// reference arguments: f(std::string&) .
+//
+// The design does not support well calling functions or methods that take
+// pointer to const types parameters : f(const std::string*) .
+//
+// The implementation does not support calling methods of const objects.
+//
+// To reduce the number of templates that get instantiated, the types of the
+// arguments of the call must match exactly the types of parameters of the
+// function or method . In some cases static_casts mey be required
+// at the point of the call. Example: CallMethod(f, static_cast<long>(10));
+
+#ifndef OMAHA_COMMON_STA_CALL_H__
+#define OMAHA_COMMON_STA_CALL_H__
+
+#include "base/scoped_ptr.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+
+namespace omaha {
+
+// C4347: 'function template' is called instead of 'function'
+#pragma warning(disable : 4347)
+
+// The Base Functor is the base of the functor hierarchy.
+class BaseFunctor {
+ public:
+  explicit BaseFunctor(bool is_async) :
+      thread_id_(::GetCurrentThreadId()),
+      is_async_(is_async) {
+    CORE_LOG(L6, (_T("[BaseFunctor::BaseFunctor]")));
+  }
+
+  // Functors are polymorphic objects.
+  virtual ~BaseFunctor() {
+    CORE_LOG(L6, (_T("[BaseFunctor::~BaseFunctor]")));
+  }
+
+  // Abstract virtual function call operator. This is always called
+  // in the callee thread by the dispatcher of the apartment.
+  virtual void operator()(void* presult) = 0;
+
+  // The thread id of the calling thread.
+  DWORD thread_id() const { return thread_id_; }
+
+  bool is_async() const { return is_async_; }
+
+ protected:
+
+  // Invoke is called by each of the derived functors. This is how
+  // the  cross thread invocation is made and the result of the invocation
+  // is retrieved. Invoke is always called in the caller thread.
+  template <typename R>
+  R Invoke() {
+    R r = R();      // ensure r is initialized even for primitive types.
+    if (!is_async_) {
+      DoInvoke(&r);
+    } else {
+      // We handle the async calls as if the call returns void.
+      DoInvoke(0);
+    }
+    return r;
+  }
+
+  // non-template method to be called by the derived functors
+  // specialized for void.
+  void Invoke() {
+    // When the argument of the invocation is 0, we are not
+    // interested in the result.
+    DoInvoke(0);
+  }
+
+ private:
+  void DoInvoke(void* presult);   // Does the actual invocation.
+  DWORD thread_id_;               // The thread id of the calling thread.
+  bool is_async_;                 // True for async calls.
+
+  DISALLOW_EVIL_CONSTRUCTORS(BaseFunctor);
+};
+
+//
+// 0-ary method functor.
+//
+template <class T, typename R>
+class MethodFunctor0 : public BaseFunctor {
+ public:
+  MethodFunctor0(bool is_async, T* pt, R (T::*pm)()) :
+      BaseFunctor(is_async), pobj_(pt), pm_(pm) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT(pobj_, (_T("Null object.")));
+    if (presult) {
+      *static_cast<R*>(presult) = (pobj_->*pm_)();
+    } else {
+      (pobj_->*pm_)();
+    }
+  }
+
+  R Invoke() {
+    // Don't forget to call the base implementation.
+    return BaseFunctor::Invoke<R>();
+  }
+
+ private:
+  T* pobj_;
+  R (T::*pm_)();
+};
+
+//
+// 0-ary partial specialization for void return types.
+//
+template <class T>
+class MethodFunctor0<T, void> : public BaseFunctor {
+ public:
+  MethodFunctor0(bool is_async, T* pt, void (T::*pm)()) :
+      BaseFunctor(is_async), pobj_(pt), pm_(pm) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT(pobj_, (_T("Null object.")));
+    ASSERT1(!presult);
+    presult;  //  unreferenced formal parameter
+
+    // the actual call. There is no return value when the return type is void.
+    (pobj_->*pm_)();
+  }
+
+  // Bring in the name from the Base
+  using BaseFunctor::Invoke;
+
+ private:
+  T* pobj_;
+  void (T::*pm_)();
+};
+
+//
+// 0-ary functor and specialization for void.
+//
+template <typename R>
+class Functor0 : public BaseFunctor {
+ public:
+  Functor0(bool is_async, R (*pf)()) :
+      BaseFunctor(is_async), pf_(pf) {}
+
+  virtual void operator()(void* presult) {
+    if (presult) {
+      *static_cast<R*>(presult) = (*pf_)();
+    } else {
+      (*pf_)();
+    }
+  }
+
+  R Invoke() {
+    return BaseFunctor::Invoke<R>();
+  }
+
+ private:
+  R (*pf_)();
+};
+
+template <>
+class Functor0<void> : public BaseFunctor {
+ public:
+  Functor0(bool is_async, void (*pf)()) :
+      BaseFunctor(is_async), pf_(pf) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT1(!presult);
+    presult;  // unreferenced formal parameter
+
+    (*pf_)();
+  }
+
+  using BaseFunctor::Invoke;
+
+ private:
+  void (*pf_)();
+};
+
+
+//
+// 1-ary
+//
+template <class T, typename R, typename P>
+class MethodFunctor1 : public BaseFunctor {
+ public:
+  MethodFunctor1(bool is_async, T* pt, R (T::*pm)(P), P p) :
+      BaseFunctor(is_async), pobj_(pt), pm_(pm), p_(p) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT(pobj_, (_T("Null object.")));
+    if (presult) {
+      *static_cast<R*>(presult) = (pobj_->*pm_)(p_);
+    } else {
+      (pobj_->*pm_)(p_);
+    }
+  }
+
+  R Invoke() {
+    return BaseFunctor::Invoke<R>();
+  }
+
+ private:
+  T* pobj_;
+  R (T::*pm_)(P);
+  P p_;
+};
+
+template <class T, typename P>
+class MethodFunctor1<T, void, P> : public BaseFunctor {
+ public:
+  MethodFunctor1(bool is_async, T* pt, void (T::*pm)(P), P p) :
+      BaseFunctor(is_async), pobj_(pt), pm_(pm), p_(p) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT(pobj_, (_T("Null object.")));
+    ASSERT1(!presult);
+    presult;  //  unreferenced formal parameter
+
+    (pobj_->*pm_)(p_);
+  }
+
+  using BaseFunctor::Invoke;
+
+ private:
+  T* pobj_;
+  void (T::*pm_)(P);
+  P p_;
+};
+
+template <typename R, typename P1>
+class Functor1 : public BaseFunctor {
+ public:
+  Functor1(bool is_async, R (*pf)(P1), P1 p1) :
+      BaseFunctor(is_async), pf_(pf), p1_(p1) {}
+
+  virtual void operator()(void* presult) {
+    if (presult) {
+      *static_cast<R*>(presult) = (*pf_)(p1_);
+    } else {
+      (*pf_)(p1_);
+    }
+  }
+
+  R Invoke() {
+    return BaseFunctor::Invoke<R>();
+  }
+
+ private:
+  R (*pf_)(P1);
+  P1 p1_;
+};
+
+template <typename P1>
+class Functor1<void, P1> : public BaseFunctor {
+ public:
+  Functor1(bool is_async, void (*pf)(P1), P1 p1) :
+      BaseFunctor(is_async), pf_(pf), p1_(p1) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT1(!presult);
+    presult;  //  unreferenced formal parameter
+
+    (*pf_)(p1_);
+  }
+
+  using BaseFunctor::Invoke;
+
+ private:
+  void (*pf_)(P1);
+  P1 p1_;
+};
+
+
+//
+// 2-ary
+//
+template <class T, typename R, typename P1, typename P2>
+class MethodFunctor2 : public BaseFunctor {
+ public:
+  MethodFunctor2(bool is_async, T* pt, R (T::*pm)(P1, P2), P1 p1, P2 p2) :
+      BaseFunctor(is_async), pobj_(pt), pm_(pm), p1_(p1), p2_(p2) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT(pobj_, (_T("Null object.")));
+    if (presult) {
+      *static_cast<R*>(presult) = (pobj_->*pm_)(p1_, p2_);
+    } else {
+      (pobj_->*pm_)(p1_, p2_);
+    }
+  }
+
+  R Invoke() {
+    return BaseFunctor::Invoke<R>();
+  }
+
+ private:
+  T* pobj_;
+  R (T::*pm_)(P1, P2);
+  P1 p1_;
+  P2 p2_;
+};
+
+template <class T, typename P1, typename P2>
+class MethodFunctor2<T, void, P1, P2> : public BaseFunctor {
+ public:
+  MethodFunctor2(bool is_async, T* pt, void (T::*pm)(P1, P2), P1 p1, P2 p2) :
+      BaseFunctor(is_async), pobj_(pt), pm_(pm), p1_(p1), p2_(p2) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT(pobj_, (_T("Null object.")));
+    ASSERT1(!presult);
+    presult;  //  unreferenced formal parameter
+
+    (pobj_->*pm_)(p1_, p2_);
+  }
+
+  using BaseFunctor::Invoke;
+
+ private:
+  T* pobj_;
+  void (T::*pm_)(P1, P2);
+  P1 p1_;
+  P2 p2_;
+};
+
+template <typename R, typename P1, typename P2>
+class Functor2 : public BaseFunctor {
+ public:
+  Functor2(bool is_async, R (*pf)(P1, P2), P1 p1, P2 p2) :
+      BaseFunctor(is_async), pf_(pf), p1_(p1), p2_(p2) {}
+
+  virtual void operator()(void* presult) {
+    if (presult) {
+      *static_cast<R*>(presult) = pf_(p1_, p2_);
+    } else {
+      pf_(p1_, p2_);
+    }
+  }
+
+  R Invoke() {
+    return BaseFunctor::Invoke<R>();
+  }
+
+ private:
+  R (*pf_)(P1, P2);
+  P1 p1_;
+  P2 p2_;
+};
+
+template <typename P1, typename P2>
+class Functor2<void, P1, P2> : public BaseFunctor {
+ public:
+  Functor2(bool is_async, void (*pf)(P1, P2), P1 p1, P2 p2) :
+      BaseFunctor(is_async), pf_(pf), p1_(p1), p2_(p2) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT1(!presult);
+    presult;  //  unreferenced formal parameter
+
+    (*pf_)(p1_, p2_);
+  }
+
+  using BaseFunctor::Invoke;
+
+ private:
+  void (*pf_)(P1, P2);
+  P1 p1_;
+  P2 p2_;
+};
+
+//
+// 3-ary
+//
+template <class T, typename R, typename P1, typename P2, typename P3>
+class MethodFunctor3 : public BaseFunctor {
+ public:
+  MethodFunctor3(bool is_async,
+                 T* pt,
+                 R (T::*pm)(P1, P2, P3),
+                 P1 p1,
+                 P2 p2,
+                 P3 p3) :
+      BaseFunctor(is_async), pobj_(pt), pm_(pm), p1_(p1), p2_(p2), p3_(p3) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT(pobj_, (_T("Null object.")));
+    if (presult) {
+      *static_cast<R*>(presult) = (pobj_->*pm_)(p1_, p2_, p3_);
+    } else {
+      (pobj_->*pm_)(p1_, p2_, p3_);
+    }
+  }
+
+  R Invoke() {
+    return BaseFunctor::Invoke<R>();
+  }
+
+ private:
+  T* pobj_;
+  R (T::*pm_)(P1, P2, P3);
+  P1 p1_;
+  P2 p2_;
+  P3 p3_;
+};
+
+template <class T, typename P1, typename P2, typename P3>
+class MethodFunctor3<T, void, P1, P2, P3> : public BaseFunctor {
+ public:
+  MethodFunctor3(bool is_async,
+                 T* pt,
+                 void (T::*pm)(P1, P2, P3),
+                 P1 p1,
+                 P2 p2,
+                 P3 p3) :
+      BaseFunctor(is_async), pobj_(pt), pm_(pm), p1_(p1), p2_(p2), p3_(p3) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT(pobj_, (_T("Null object.")));
+    ASSERT1(!presult);
+    presult;  //  unreferenced formal parameter
+
+    (pobj_->*pm_)(p1_, p2_, p3_);
+  }
+
+  using BaseFunctor::Invoke;
+
+ private:
+  T* pobj_;
+  void (T::*pm_)(P1, P2, P3);
+  P1 p1_;
+  P2 p2_;
+  P3 p3_;
+};
+
+
+template <typename R, typename P1, typename P2, typename P3>
+class Functor3 : public BaseFunctor {
+ public:
+  Functor3(bool is_async, R (*pf)(P1, P2, P3), P1 p1, P2 p2, P3 p3) :
+      BaseFunctor(is_async), pf_(pf), p1_(p1), p2_(p2), p3_(p3) {}
+  virtual void operator()(void* presult) {
+    if (presult) {
+      *static_cast<R*>(presult) = (*pf_)(p1_, p2_, p3_);
+    } else {
+      (*pf_)(p1_, p2_, p3_);
+    }
+  }
+
+  R Invoke() {
+    return BaseFunctor::Invoke<R>();
+  }
+
+ private:
+  R (*pf_)(P1, P2, P3);
+  P1 p1_;
+  P2 p2_;
+  P3 p3_;
+};
+
+template <typename P1, typename P2, typename P3>
+class Functor3<void, P1, P2, P3> : public BaseFunctor {
+ public:
+  Functor3(bool is_async, void (*pf)(P1, P2, P3), P1 p1, P2 p2, P3 p3) :
+      BaseFunctor(is_async), pf_(pf), p1_(p1), p2_(p2), p3_(p3) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT1(!presult);
+    presult;  //  unreferenced formal parameter
+
+    (*pf_)(p1_, p2_, p3_);
+  }
+
+  using BaseFunctor::Invoke;
+
+ private:
+  void (*pf_)(P1, P2, P3);
+  P1 p1_;
+  P2 p2_;
+  P3 p3_;
+};
+
+//
+// 4-ary
+//
+template <class T,
+          typename R,
+          typename P1,
+          typename P2,
+          typename P3,
+          typename P4>
+class MethodFunctor4 : public BaseFunctor {
+ public:
+  MethodFunctor4(bool is_async,
+                 T* pt,
+                 R (T::*pm)(P1, P2, P3, P4),
+                 P1 p1,
+                 P2 p2,
+                 P3 p3,
+                 P4 p4) :
+      BaseFunctor(is_async),
+      pobj_(pt),
+      pm_(pm),
+      p1_(p1),
+      p2_(p2),
+      p3_(p3),
+      p4_(p4) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT(pobj_, (_T("Null object.")));
+    if (presult) {
+      *static_cast<R*>(presult) = (pobj_->*pm_)(p1_, p2_, p3_, p4_);
+    } else {
+      (pobj_->*pm_)(p1_, p2_, p3_, p4_);
+    }
+  }
+
+  R Invoke() {
+    return BaseFunctor::Invoke<R>();
+  }
+
+ private:
+  T* pobj_;
+  R (T::*pm_)(P1, P2, P3, P4);
+  P1 p1_;
+  P2 p2_;
+  P3 p3_;
+  P4 p4_;
+};
+
+template <class T, typename P1, typename P2, typename P3, typename P4>
+class MethodFunctor4<T, void, P1, P2, P3, P4> : public BaseFunctor {
+ public:
+  MethodFunctor4(bool is_async,
+                 T* pt,
+                 void (T::*pm)(P1, P2, P3, P4),
+                 P1 p1,
+                 P2 p2,
+                 P3 p3,
+                 P4 p4) :
+      BaseFunctor(is_async),
+      pobj_(pt),
+      pm_(pm),
+      p1_(p1),
+      p2_(p2),
+      p3_(p3),
+      p4_(p4) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT(pobj_, (_T("Null object.")));
+    ASSERT1(!presult);
+    presult;  //  unreferenced formal parameter
+
+    (pobj_->*pm_)(p1_, p2_, p3_, p4_);
+  }
+
+  using BaseFunctor::Invoke;
+
+ private:
+  T* pobj_;
+  void (T::*pm_)(P1, P2, P3, P4);
+  P1 p1_;
+  P2 p2_;
+  P3 p3_;
+  P4 p4_;
+};
+
+
+template <typename R, typename P1, typename P2, typename P3, typename P4>
+class Functor4 : public BaseFunctor {
+ public:
+  Functor4(bool is_async, R (*pf)(P1, P2, P3, P4), P1 p1, P2 p2, P3 p3, P4 p4) :
+      BaseFunctor(is_async), pf_(pf), p1_(p1), p2_(p2), p3_(p3), p4_(p4) {}
+
+  virtual void operator()(void* presult) {
+    if (presult) {
+      *static_cast<R*>(presult) = (*pf_)(p1_, p2_, p3_, p4_);
+    } else {
+      (*pf_)(p1_, p2_, p3_, p4_);
+    }
+  }
+
+  R Invoke() {
+    return BaseFunctor::Invoke<R>();
+  }
+
+ private:
+  R (*pf_)(P1, P2, P3, P4);
+  P1 p1_;
+  P2 p2_;
+  P3 p3_;
+  P4 p4_;
+};
+
+template <typename P1, typename P2, typename P3, typename P4>
+class Functor4<void, P1, P2, P3, P4> : public BaseFunctor {
+ public:
+  Functor4(bool is_async,
+           void (*pf)(P1, P2, P3, P4),
+           P1 p1,
+           P2 p2,
+           P3 p3,
+           P4 p4) :
+      BaseFunctor(is_async), pf_(pf), p1_(p1), p2_(p2), p3_(p3), p4_(p4) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT1(!presult);
+    presult;  //  unreferenced formal parameter
+
+    (*pf_)(p1_, p2_, p3_, p4_);
+  }
+
+  using BaseFunctor::Invoke;
+
+ private:
+  void (*pf_)(P1, P2, P3, P4);
+  P1 p1_;
+  P2 p2_;
+  P3 p3_;
+  P4 p4_;
+};
+
+//
+// 5-ary
+//
+template <class T,
+          typename R,
+          typename P1,
+          typename P2,
+          typename P3,
+          typename P4,
+          typename P5>
+class MethodFunctor5 : public BaseFunctor {
+ public:
+  MethodFunctor5(bool is_async,
+                 T* pt,
+                 R (T::*pm)(P1, P2, P3, P4, P5),
+                 P1 p1,
+                 P2 p2,
+                 P3 p3,
+                 P4 p4,
+                 P5 p5) :
+      BaseFunctor(is_async),
+      pobj_(pt),
+      pm_(pm),
+      p1_(p1),
+      p2_(p2),
+      p3_(p3),
+      p4_(p4),
+      p5_(p5) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT(pobj_, (_T("Null object.")));
+    if (presult) {
+      *static_cast<R*>(presult) = (pobj_->*pm_)(p1_, p2_, p3_, p4_, p5_);
+    } else {
+      (pobj_->*pm_)(p1_, p2_, p3_, p4_, p5_);
+    }
+  }
+
+  R Invoke() {
+    return BaseFunctor::Invoke<R>();
+  }
+
+ private:
+  T* pobj_;
+  R (T::*pm_)(P1, P2, P3, P4, P5);
+  P1 p1_;
+  P2 p2_;
+  P3 p3_;
+  P4 p4_;
+  P5 p5_;
+};
+
+template <class T,
+          typename P1,
+          typename P2,
+          typename P3,
+          typename P4,
+          typename P5>
+class MethodFunctor5<T, void, P1, P2, P3, P4, P5> : public BaseFunctor {
+ public:
+  MethodFunctor5(bool is_async,
+                 T* pt,
+                 void (T::*pm)(P1, P2, P3, P4, P5),
+                 P1 p1,
+                 P2 p2,
+                 P3 p3,
+                 P4 p4,
+                 P5 p5) :
+      BaseFunctor(is_async),
+      pobj_(pt),
+      pm_(pm),
+      p1_(p1),
+      p2_(p2),
+      p3_(p3),
+      p4_(p4),
+      p5_(p5) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT(pobj_, (_T("Null object.")));
+    ASSERT1(!presult);
+    presult;  //  unreferenced formal parameter
+
+    (pobj_->*pm_)(p1_, p2_, p3_, p4_, p5_);
+  }
+
+  using BaseFunctor::Invoke;
+
+ private:
+  T* pobj_;
+  void (T::*pm_)(P1, P2, P3, P4, P5);
+  P1 p1_;
+  P2 p2_;
+  P3 p3_;
+  P4 p4_;
+  P5 p5_;
+};
+
+template <typename R,
+          typename P1,
+          typename P2,
+          typename P3,
+          typename P4,
+          typename P5>
+class Functor5 : public BaseFunctor {
+ public:
+  Functor5(bool is_async,
+           R (*pf)(P1, P2, P3, P4, P5),
+           P1 p1,
+           P2 p2,
+           P3 p3,
+           P4 p4,
+           P5 p5) :
+      BaseFunctor(is_async),
+      pf_(pf),
+      p1_(p1),
+      p2_(p2),
+      p3_(p3),
+      p4_(p4),
+      p5_(p5) {}
+  virtual void operator()(void* presult) {
+    if (presult) {
+      *static_cast<R*>(presult) = (*pf_)(p1_, p2_, p3_, p4_, p5_);
+    } else {
+      (*pf_)(p1_, p2_, p3_, p4_, p5_);
+    }
+  }
+
+  R Invoke() {
+    return BaseFunctor::Invoke<R>();
+  }
+
+ private:
+  R (*pf_)(P1, P2, P3, P4, P5);
+  P1 p1_;
+  P2 p2_;
+  P3 p3_;
+  P4 p4_;
+  P5 p5_;
+};
+
+template <typename P1, typename P2, typename P3, typename P4, typename P5>
+class Functor5<void, P1, P2, P3, P4, P5> : public BaseFunctor {
+ public:
+  Functor5(bool is_async,
+           void (*pf)(P1, P2, P3, P4, P5),
+           P1 p1,
+           P2 p2,
+           P3 p3,
+           P4 p4,
+           P5 p5) :
+      BaseFunctor(is_async),
+      pf_(pf),
+      p1_(p1),
+      p2_(p2),
+      p3_(p3),
+      p4_(p4),
+      p5_(p5) {}
+
+  virtual void operator()(void* presult) {
+    ASSERT1(!presult);
+    presult;  //  unreferenced formal parameter
+
+    (*pf_)(p1_, p2_, p3_, p4_, p5_);
+  }
+
+  using BaseFunctor::Invoke;
+
+ private:
+  void (*pf_)(P1, P2, P3, P4, P5);
+  P1 p1_;
+  P2 p2_;
+  P3 p3_;
+  P4 p4_;
+  P5 p5_;
+};
+
+
+// This is what the clients of the STA code instantiate and call.
+//
+// Synchronous Callers.
+//
+template <class T, typename R>
+R CallMethod(T* object, R (T::*pm)()) {
+  return MethodFunctor0<T, R>(false, object, pm).Invoke();
+}
+
+template <typename R>
+R CallFunction(R (*pf)()) {
+  return Functor0<R>(false, pf).Invoke();
+}
+
+template <class T, typename R, typename P>
+R CallMethod(T* object, R (T::*pm)(P), P p) {
+  return MethodFunctor1<T, R, P>(false, object, pm, p).Invoke();
+}
+
+template <typename R, typename P>
+R CallFunction(R (*pf)(P), P p) {
+  return Functor1<R, P>(false, pf, p).Invoke();
+}
+
+template <class T, typename R, typename P1, typename P2>
+R CallMethod(T* object, R (T::*pm)(P1, P2), P1 p1, P2 p2) {
+  return MethodFunctor2<T, R, P1, P2>(false, object, pm, p1, p2).Invoke();
+}
+
+template <typename R, typename P1, typename P2>
+R CallFunction(R (*pf)(P1, P2), P1 p1, P2 p2) {
+  return Functor2<R, P1, P2>(false, pf, p1, p2).Invoke();
+}
+
+template <class T, typename R, typename P1, typename P2, typename P3>
+R CallMethod(T* object, R (T::*pm)(P1, P2, P3), P1 p1, P2 p2, P3 p3) {
+  return MethodFunctor3<T, R, P1, P2, P3>(false,
+                                          object, pm, p1, p2, p3).Invoke();
+}
+
+template <typename R, typename P1, typename P2, typename P3>
+R CallFunction(R (*pf)(P1, P2, P3), P1 p1, P2 p2, P3 p3) {
+  return Functor3<R, P1, P2, P3>(false, pf, p1, p2, p3).Invoke();
+}
+
+template <class T,
+          typename R,
+          typename P1,
+          typename P2,
+          typename P3,
+          typename P4>
+R CallMethod(T* object,
+             R (T::*pm)(P1, P2, P3, P4),
+             P1 p1,
+             P2 p2,
+             P3 p3,
+             P4 p4) {
+  return MethodFunctor4<T, R, P1, P2, P3, P4>(false,
+                                              object,
+                                              pm,
+                                              p1,
+                                              p2,
+                                              p3,
+                                              p4).Invoke();
+}
+
+template <typename R, typename P1, typename P2, typename P3, typename P4>
+R CallFunction(R (*pf)(P1, P2, P3, P4), P1 p1, P2 p2, P3 p3, P4 p4) {
+  return Functor4<R, P1, P2, P3, P4>(false, pf, p1, p2, p3, p4).Invoke();
+}
+
+template <class T,
+          typename R,
+          typename P1,
+          typename P2,
+          typename P3,
+          typename P4,
+          typename P5>
+R CallMethod(T* object,
+             R (T::*pm)(P1, P2, P3, P4, P5),
+             P1 p1,
+             P2 p2,
+             P3 p3,
+             P4 p4,
+             P5 p5) {
+  return MethodFunctor5<T, R, P1, P2, P3, P4, P5>(false,
+                                                  object,
+                                                  pm,
+                                                  p1,
+                                                  p2,
+                                                  p3,
+                                                  p4,
+                                                  p5).Invoke();
+}
+
+template <typename R,
+          typename P1,
+          typename P2,
+          typename P3,
+          typename P4,
+          typename P5>
+R CallFunction(R (*pf)(P1, P2, P3, P4, P5), P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) {
+  return Functor5<R, P1, P2, P3, P4, P5>(false,
+                                         pf,
+                                         p1,
+                                         p2,
+                                         p3,
+                                         p4,
+                                         p5).Invoke();
+}
+
+//
+// Asynchronous Callers.
+//
+template <class T, typename R>
+void CallMethodAsync(T* object, R (T::*pm)()) {
+  scoped_ptr<MethodFunctor0<T, R> > fun(
+      new MethodFunctor0<T, R>(true, object, pm));
+  fun->Invoke();
+  fun.release();
+}
+
+template <typename R>
+void CallFunctionAsync(R (*pf)()) {
+  scoped_ptr<Functor0<R> > fun(new Functor0<R>(true, pf));
+  fun->Invoke();
+  fun.release();
+}
+
+template <class T, typename R, typename P>
+void CallMethodAsync(T* object, R (T::*pm)(P), P p) {
+  scoped_ptr<MethodFunctor1<T, R, P> > fun(
+      new MethodFunctor1<T, R, P>(true, object, pm, p));
+  fun->Invoke();
+  fun.release();
+}
+
+template <typename R, typename P>
+void CallFunctionAsync(R (*pf)(P), P p) {
+  scoped_ptr<Functor1<R, P> > fun(new Functor1<R, P>(true, pf, p));
+  fun->Invoke();
+  fun.release();
+}
+
+template <class T, typename R, typename P1, typename P2>
+void CallMethodAsync(T* object, R (T::*pm)(P1, P2), P1 p1, P2 p2) {
+  scoped_ptr<MethodFunctor2<T, R, P1, P2> > fun(
+      new MethodFunctor2<T, R, P1, P2>(true, object, pm, p1, p2));
+  fun->Invoke();
+  fun.release();
+}
+
+template <typename R, typename P1, typename P2>
+void CallFunctionAsync(R (*pf)(P1, P2), P1 p1, P2 p2) {
+  scoped_ptr<Functor2<R, P1, P2> > fun(
+      new Functor2<R, P1, P2>(true, pf, p1, p2));
+  fun->Invoke();
+  fun.release();
+}
+
+template <class T, typename R, typename P1, typename P2, typename P3>
+void CallMethodAsync(T* object, R (T::*pm)(P1, P2, P3), P1 p1, P2 p2, P3 p3) {
+  scoped_ptr<MethodFunctor3<T, R, P1, P2, P3> > fun(
+      new MethodFunctor3<T, R, P1, P2, P3>(true, object, pm, p1, p2, p3));
+  fun->Invoke();
+  fun.release();
+}
+
+template <typename R, typename P1, typename P2, typename P3>
+void CallFunctionAsync(R (*pf)(P1, P2, P3), P1 p1, P2 p2, P3 p3) {
+  scoped_ptr<Functor3<R, P1, P2, P3> > fun(
+      new Functor3<R, P1, P2, P3>(true, pf, p1, p2, p3));
+  fun->Invoke();
+  fun.release();
+}
+
+template <class T,
+          typename R,
+          typename P1,
+          typename P2,
+          typename P3,
+          typename P4>
+void CallMethodAsync(T* obj,
+                     R (T::*pm)(P1, P2, P3, P4),
+                     P1 p1,
+                     P2 p2,
+                     P3 p3,
+                     P4 p4) {
+  scoped_ptr<MethodFunctor4<T, R, P1, P2, P3, P4> > fun(
+      new MethodFunctor4<T, R, P1, P2, P3, P4>(true, obj, pm, p1, p2, p3, p4));
+  fun->Invoke();
+  fun.release();
+}
+
+template <typename R, typename P1, typename P2, typename P3, typename P4>
+void CallFunctionAsync(R (*pf)(P1, P2, P3, P4), P1 p1, P2 p2, P3 p3, P4 p4) {
+  scoped_ptr<Functor4<R, P1, P2, P3, P4> > fun(
+      new Functor4<R, P1, P2, P3, P4>(true, pf, p1, p2, p3, p4));
+  fun->Invoke();
+  fun.release();
+}
+
+template <class T,
+          typename R,
+          typename P1,
+          typename P2,
+          typename P3,
+          typename P4,
+          typename P5>
+void CallMethodAsync(T* object,
+                     R (T::*pm)(P1, P2, P3, P4, P5),
+                     P1 p1,
+                     P2 p2,
+                     P3 p3,
+                     P4 p4,
+                     P5 p5) {
+  scoped_ptr<MethodFunctor5<T, R, P1, P2, P3, P4, P5> > fun(
+      new MethodFunctor5<T, R, P1, P2, P3, P4, P5>(true,
+                                                   object,
+                                                   pm,
+                                                   p1,
+                                                   p2,
+                                                   p3,
+                                                   p4,
+                                                   p5));
+  fun->Invoke();
+  fun.release();
+}
+
+template <typename R,
+          typename P1,
+          typename P2,
+          typename P3,
+          typename P4,
+          typename P5>
+void CallFunctionAsync(R (*pf)(P1, P2, P3, P4, P5),
+                       P1 p1,
+                       P2 p2,
+                       P3 p3,
+                       P4 p4,
+                       P5 p5) {
+  scoped_ptr<Functor5<R, P1, P2, P3, P4, P5> > fun(
+      new Functor5<R, P1, P2, P3, P4, P5>(true, pf, p1, p2, p3, p4, p5));
+  fun->Invoke();
+  fun.release();
+}
+
+#pragma warning(default : 4347)
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_STA_CALL_H__
+
diff --git a/base/sta_unittest.cc b/base/sta_unittest.cc
new file mode 100644
index 0000000..e7a000f
--- /dev/null
+++ b/base/sta_unittest.cc
@@ -0,0 +1,288 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include <atlstr.h>
+#include "omaha/base/sta.h"
+#include "omaha/base/sta_call.h"
+#include "omaha/base/thread.h"
+#include "omaha/base/utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+class X {
+ public:
+  void f() {}
+  void f(int) {}
+  void f(unsigned int, X*) {}
+  void f(bool, char, long*) {}
+
+  static void g() {}
+  static void g(int) {}
+  static void g(unsigned int, X*) {}
+  static void g(bool, char, long*) {}
+};
+
+class Y {
+ public:
+  HRESULT f() { return S_OK; }
+  HRESULT f(int) { return S_OK; }
+  HRESULT f(unsigned int, X*) { return S_OK; }
+  HRESULT f(bool, char, long*) { return S_OK; }
+
+  static HRESULT g() { return S_OK; }
+  static HRESULT g(int) { return S_OK; }
+  static HRESULT g(unsigned int, X*) { return S_OK; }
+  static HRESULT g(bool, char, long*) { return S_OK; }
+};
+
+class Z {
+ public:
+  void f(char, signed char, unsigned char) {}
+
+  void f(Y*) {}
+  // void f(const Y*) {} // not supported !!!
+
+  void m() {}
+  void m() const {}
+};
+
+class Test {
+ public:
+  int Add(int i, int j) { return i + j; }
+  void Add(int i, int j, int* sum) { *sum = i + j; }
+};
+
+int Add(long i, long j) { return i + j; }
+void Add(long i, long j, long* sum) { *sum = i + j; }
+
+void Print(const char*) {}
+void Print1(CString*) {}
+
+}  // namespace
+
+class CompileTest : public Runnable {
+ protected:
+  virtual void Run();
+};
+
+void CompileTest::Run() {
+  X x;
+  Y y;
+
+  CallFunction(X::g);
+  CallFunction(X::g, 10);
+  CallFunction(X::g, static_cast<unsigned int>(10), &x);
+  CallFunction(X::g, true, 'a', static_cast<long*>(0));
+
+  CallFunction(Y::g);
+  CallFunction(Y::g, 10);
+  CallFunction(Y::g, static_cast<unsigned int>(10), &x);
+  CallFunction(Y::g, true, 'a', static_cast<long*>(0));
+
+  CallMethod(&x, &X::f);
+  CallMethod(&x, &X::f, 10);
+  CallMethod(&x, &X::f, static_cast<unsigned int>(10), &x);
+  CallMethod(&x, &X::f, true, 'a', static_cast<long*>(0));
+
+  CallMethod(&y, &Y::f);
+  CallMethod(&y, &Y::f, 20);
+  CallMethod(&y, &Y::f, static_cast<unsigned int>(10), &x);
+  CallMethod(&y, &Y::f, true, 'a', static_cast<long*>(0));
+
+  Z z;
+  CallMethod(&z,
+             &Z::f,
+             'a',
+             static_cast<signed char>('a'),
+             static_cast<unsigned char>('a'));
+
+
+  CallMethod(&z, &Z::f, &y);
+
+  // Does not compile: template parameter 'P' is ambiguous
+  // const Y cy;
+  // CallMethod(&z, &Z::f, &cy);
+
+  CallMethod(&z, &Z::m);
+
+  // Does not compile: template parameter 'T' is ambiguous
+  // const Z cz;
+  // CallMethod(&cz, &Z::m);
+
+  // Does not compile: cannot convert from 'const Z *' to 'Z *const '
+  // const Z cz;
+  // CallMethod<const Z, void>(&cz, &Z::m);
+
+  CString msg(_T("test"));
+  CallFunction(Print, "test");
+  CallFunction(Print1, &msg);
+}
+
+class RuntimeTest : public Runnable {
+ protected:
+  virtual void Run();
+};
+
+void RuntimeTest::Run() {
+  Test test;
+  ASSERT_EQ(CallMethod(&test, &Test::Add, 10, 20), 30);
+
+  int sum(0);
+  CallMethod(&test, &Test::Add, -10, 20, &sum);
+  ASSERT_EQ(sum, 10);
+
+  {
+  ASSERT_EQ(CallFunction(Add, long(10), long(20)), 30);
+
+  long sum = 0;
+  CallFunction(Add, long(10), long(-20), &sum);
+  ASSERT_EQ(sum, -10);
+  }
+}
+
+
+class AsyncTest : public Runnable {
+ protected:
+  virtual void Run();
+};
+
+void AsyncTest::Run() {
+  static X x;
+  static Y y;
+
+  CallFunctionAsync(X::g);
+  CallFunctionAsync(X::g, 10);
+  CallFunctionAsync(X::g, static_cast<unsigned int>(10), &x);
+  CallFunctionAsync(X::g, true, 'a', static_cast<long*>(0));
+
+  CallFunctionAsync(Y::g);
+  CallFunctionAsync(Y::g, 10);
+  CallFunctionAsync(Y::g, static_cast<unsigned int>(10), &x);
+  CallFunctionAsync(Y::g, true, 'a', static_cast<long*>(0));
+
+  CallMethodAsync(&x, &X::f);
+  CallMethodAsync(&x, &X::f, 10);
+  CallMethodAsync(&x, &X::f, static_cast<unsigned int>(10), &x);
+  CallMethodAsync(&x, &X::f, true, 'a', static_cast<long*>(0));
+
+  CallMethodAsync(&y, &Y::f);
+  CallMethodAsync(&y, &Y::f, 20);
+  CallMethodAsync(&y, &Y::f, static_cast<unsigned int>(10), &x);
+  CallMethodAsync(&y, &Y::f, true, 'a', static_cast<long*>(0));
+
+  static Z z;
+  CallMethodAsync(&z,
+                  &Z::f,
+                  'a',
+                  static_cast<signed char>('a'),
+                  static_cast<unsigned char>('a'));
+
+
+  CallMethodAsync(&z, &Z::f, &y);
+
+  // Does not compile: template parameter 'P' is ambiguous
+  // const Y cy;
+  // CallMethod(&z, &Z::f, &cy);
+
+  CallMethodAsync(&z, &Z::m);
+
+  // Does not compile: template parameter 'T' is ambiguous
+  // const Z cz;
+  // CallMethod(&cz, &Z::m);
+
+  // Does not compile: cannot convert from 'const Z *' to 'Z *const '
+  // const Z cz;
+  // CallMethod<const Z, void>(&cz, &Z::m);
+
+  CString msg(_T("test"));
+  CallFunctionAsync(Print, "test");
+  CallFunctionAsync(Print1, &msg);
+
+  WaitWithMessageLoopTimed(1000);
+}
+
+
+TEST(STATest, CompileTest) {
+  ASSERT_SUCCEEDED(InitializeApartment(0));
+
+  Thread t;
+  CompileTest compile_test;
+  t.Start(&compile_test);
+  EXPECT_TRUE(WaitWithMessageLoop(t.GetThreadHandle()));
+
+  ASSERT_SUCCEEDED(UninitializeApartment());
+}
+
+TEST(STATest, RuntimeTest) {
+  ASSERT_SUCCEEDED(InitializeApartment(0));
+
+  Thread t;
+  RuntimeTest runtime_test;
+  t.Start(&runtime_test);
+  EXPECT_TRUE(WaitWithMessageLoop(t.GetThreadHandle()));
+
+  ASSERT_SUCCEEDED(UninitializeApartment());
+}
+
+
+TEST(STATest, AsyncTest) {
+  ASSERT_SUCCEEDED(InitializeApartment(0));
+
+  Thread t;
+  AsyncTest async_test;
+  t.Start(&async_test);
+  EXPECT_TRUE(WaitWithMessageLoop(t.GetThreadHandle()));
+
+  ASSERT_SUCCEEDED(UninitializeApartment());
+}
+
+TEST(STATest, ApartmentRefCounting) {
+  // Check the reference counting is working.
+  ASSERT_SUCCEEDED(InitializeApartment(0));
+  ASSERT_SUCCEEDED(InitializeApartment(0));
+  ASSERT_SUCCEEDED(UninitializeApartment());
+  ASSERT_SUCCEEDED(UninitializeApartment());
+
+  // The call below will raise an assert in the the STA code.
+  ExpectAsserts expect_asserts;
+  ASSERT_EQ(E_UNEXPECTED, UninitializeApartment());
+}
+
+TEST(STATest, ScopedSTA) {
+  {
+    scoped_sta sta(0);
+    ASSERT_SUCCEEDED(sta.result());
+  }
+  {
+    scoped_sta sta(0);
+    ASSERT_SUCCEEDED(sta.result());
+  }
+  {
+    scoped_sta sta1(0);
+    scoped_sta sta2(0);
+    ASSERT_SUCCEEDED(sta1.result());
+    ASSERT_SUCCEEDED(sta2.result());
+  }
+
+  ExpectAsserts expect_asserts;
+  ASSERT_EQ(E_UNEXPECTED, UninitializeApartment());
+}
+
+}  // namespace omaha
+
diff --git a/common/static_assert.h b/base/static_assert.h
similarity index 100%
rename from common/static_assert.h
rename to base/static_assert.h
diff --git a/base/statregex.h b/base/statregex.h
new file mode 100644
index 0000000..50b47e5
--- /dev/null
+++ b/base/statregex.h
@@ -0,0 +1,1357 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Reuses most of the functionality of the classes within atlmfc_vc80 statreg.h.
+// The only functional difference is that GenerateError() returns a unique
+// HRESULT based on the component CLSID/APPID as well as one of the E_ATL_XXX
+// codes, instead of the generic DISP_E_EXCEPTION.
+
+#ifndef OMAHA_BASE_STATREGEX_H__
+#define OMAHA_BASE_STATREGEX_H__
+
+#include <atlbase.h>
+#include <statreg.h>
+
+namespace omaha {
+
+class RegObject;
+
+class RegParser
+{
+public:
+  RegParser(RegObject* pRegObj);
+
+  HRESULT  PreProcessBuffer(__in_z LPTSTR lpszReg, __deref_out_z LPTSTR* ppszReg);
+  HRESULT  RegisterBuffer(__in_z LPTSTR szReg, BOOL bRegister);
+
+protected:
+
+  void    SkipWhiteSpace();
+  HRESULT NextToken(__out_ecount_z(MAX_VALUE) LPTSTR szToken);
+  HRESULT AddValue(__in CRegKey& rkParent, __in_z_opt LPCTSTR szValueName, __out_ecount_z(MAX_VALUE) LPTSTR szToken);
+  BOOL    CanForceRemoveKey(LPCTSTR szKey);
+  BOOL    HasSubKeys(HKEY hkey);
+  BOOL    HasValues(HKEY hkey);
+  HRESULT RegisterSubkeys(__out_ecount_z(MAX_VALUE) LPTSTR szToken, __in HKEY hkParent, __in BOOL bRegister, __in BOOL bInRecovery = FALSE);
+  BOOL    IsSpace(TCHAR ch);
+  LPTSTR  m_pchCur;
+
+  RegObject*     m_pRegObj;
+  unsigned long guid_prefix_;
+
+  HRESULT GenerateError(UINT registrar_error) {
+    return MAKE_HRESULT(SEVERITY_ERROR, registrar_error, guid_prefix_);
+  }
+
+  //HRESULT HandleReplacements(LPTSTR& szToken);
+  HRESULT SkipAssignment(__inout_ecount_z(MAX_VALUE) LPTSTR szToken);
+
+  BOOL    EndOfVar() { return chQuote == *m_pchCur && chQuote != *CharNext(m_pchCur); }
+  static LPTSTR StrChr(__in_z LPTSTR lpsz, __in TCHAR ch);
+  static HKEY HKeyFromString(__in_z LPTSTR szToken);
+  static BYTE ChToByte(const TCHAR ch);
+  static BOOL VTFromRegType(LPCTSTR szValueType, VARTYPE& vt);
+  static const TCHAR* const rgszNeverDelete[];
+  static const int cbNeverDelete;
+  static const int MAX_VALUE = 4096;
+  static const int MAX_TYPE = 4096;
+
+  // Implementation Helper
+  class CParseBuffer
+  {
+  public:
+    int nPos;
+    int nSize;
+    LPTSTR p;
+    CParseBuffer(int nInitial)
+    {
+      if (nInitial < 100)
+        nInitial = 1000;
+      nPos = 0;
+      nSize = nInitial;
+      p = (LPTSTR) ::ATL::AtlCoTaskMemCAlloc(nSize,static_cast<ULONG>(sizeof(TCHAR)));
+      if (p != NULL)
+        *p = NULL;
+    }
+    ~CParseBuffer()
+    {
+      CoTaskMemFree(p);
+    }
+    BOOL Append(const TCHAR* pch, int nChars)
+    {
+      ATLASSERT(p != NULL);
+      int newSize = nPos + nChars + 1;
+      if ((newSize <= nPos) || (newSize <= nChars))
+        return FALSE;
+
+      if (newSize >= nSize)
+      {
+        while (newSize >= nSize) {
+          if (nSize > INT_MAX / 2)
+          return FALSE;
+          nSize *= 2;
+        }
+        LPTSTR pTemp = (LPTSTR)::ATL::AtlCoTaskMemRecalloc(p, nSize, sizeof(TCHAR));
+        if (pTemp == NULL)
+          return FALSE;
+        p = pTemp;
+      }
+      if ((nPos < 0) || (nPos >= nSize) || nSize - nPos > nSize)
+        return FALSE;
+
+#pragma warning(push)
+#pragma warning(disable: 22008)
+      /* Prefast false warning is fired here despite the all above checks */
+      Checked::memcpy_s(p + nPos, (nSize-nPos) * sizeof(TCHAR), pch, int(nChars * sizeof(TCHAR)));
+      nPos += nChars;
+      *(p + nPos) = NULL;
+#pragma warning(pop)
+      return TRUE;
+    }
+
+    BOOL AddChar(const TCHAR* pch)
+    {
+#ifndef _UNICODE
+      int nChars = int(CharNext(pch) - pch);
+#else
+      int nChars = 1;
+#endif
+      return Append(pch, nChars);
+
+    }
+    BOOL AddString(LPCOLESTR lpsz)
+    {
+      if (lpsz == NULL)
+      {
+        return FALSE;
+      }
+      USES_CONVERSION_EX;
+      LPCTSTR lpszT = OLE2CT_EX(lpsz, _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
+      if (lpszT == NULL)
+      {
+        return FALSE;
+      }
+      return Append(lpszT, (int)lstrlen(lpszT));
+    }
+    LPTSTR Detach()
+    {
+      LPTSTR lp = p;
+      p = NULL;
+      nSize = nPos = 0;
+      return lp;
+    }
+
+  };
+};
+
+class RegObject  : public IRegistrarBase
+{
+public:
+
+  STDMETHOD(QueryInterface)(const IID &,void ** )
+  {
+    ATLASSERT(_T("statically linked in RegObject is not a com object. Do not callthis function"));
+    return E_NOTIMPL;
+  }
+
+  STDMETHOD_(ULONG, AddRef)(void)
+  {
+    ATLASSERT(_T("statically linked in RegObject is not a com object. Do not callthis function"));
+    return 1;
+  }
+  STDMETHOD_(ULONG, Release)(void)
+  {
+    ATLASSERT(_T("statically linked in RegObject is not a com object. Do not callthis function"));
+    return 0;
+  }
+
+  virtual ~RegObject(){ClearReplacements();}
+  HRESULT FinalConstruct() { return m_csMap.Init(); }
+  void FinalRelease() {}
+
+  // Map based methods
+  HRESULT STDMETHODCALLTYPE AddReplacement(LPCOLESTR lpszKey, LPCOLESTR lpszItem);
+  HRESULT STDMETHODCALLTYPE ClearReplacements();
+  LPCOLESTR StrFromMap(__in_z LPTSTR lpszKey);
+
+  // Register via a given mechanism
+  HRESULT STDMETHODCALLTYPE ResourceRegister(LPCOLESTR pszFileName, UINT nID, LPCOLESTR pszType);
+  HRESULT STDMETHODCALLTYPE ResourceRegisterSz(LPCOLESTR pszFileName, LPCOLESTR pszID, LPCOLESTR pszType);
+  HRESULT STDMETHODCALLTYPE ResourceUnregister(LPCOLESTR pszFileName, UINT nID, LPCOLESTR pszType);
+  HRESULT STDMETHODCALLTYPE ResourceUnregisterSz(LPCOLESTR pszFileName, LPCOLESTR pszID, LPCOLESTR pszType);
+  HRESULT STDMETHODCALLTYPE FileRegister(LPCOLESTR bstrFileName)
+  {
+    return CommonFileRegister(bstrFileName, TRUE);
+  }
+
+  HRESULT STDMETHODCALLTYPE FileUnregister(LPCOLESTR bstrFileName)
+  {
+    return CommonFileRegister(bstrFileName, FALSE);
+  }
+
+  HRESULT STDMETHODCALLTYPE StringRegister(LPCOLESTR bstrData)
+  {
+    return RegisterWithString(bstrData, TRUE);
+  }
+
+  HRESULT STDMETHODCALLTYPE StringUnregister(LPCOLESTR bstrData)
+  {
+    return RegisterWithString(bstrData, FALSE);
+  }
+
+protected:
+
+  HRESULT CommonFileRegister(LPCOLESTR pszFileName, BOOL bRegister);
+  HRESULT RegisterFromResource(LPCOLESTR pszFileName, LPCTSTR pszID, LPCTSTR pszType, BOOL bRegister);
+  HRESULT RegisterWithString(LPCOLESTR pszData, BOOL bRegister);
+
+  static HRESULT GenerateError(UINT) {return DISP_E_EXCEPTION;}
+
+  CExpansionVector                m_RepMap;
+  CComObjectThreadModel::AutoDeleteCriticalSection      m_csMap;
+};
+
+inline HRESULT STDMETHODCALLTYPE RegObject::AddReplacement(LPCOLESTR lpszKey, LPCOLESTR lpszItem)
+{
+  if (lpszKey == NULL || lpszItem == NULL)
+    return E_INVALIDARG;
+  m_csMap.Lock();
+  USES_CONVERSION_EX;
+
+  LPCTSTR lpszT = OLE2CT_EX(lpszKey, _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
+
+#ifndef _UNICODE
+  if(lpszT == NULL)
+    return E_OUTOFMEMORY;
+#endif
+
+  BOOL bRet = m_RepMap.Add(lpszT, lpszItem);
+
+  m_csMap.Unlock();
+  return bRet ? S_OK : E_OUTOFMEMORY;
+}
+
+inline HRESULT RegObject::RegisterFromResource(LPCOLESTR bstrFileName, LPCTSTR szID,
+                     LPCTSTR szType, BOOL bRegister)
+{
+  USES_CONVERSION_EX;
+
+  HRESULT     hr;
+  RegParser  parser(this);
+  HINSTANCE   hInstResDll;
+  HRSRC       hrscReg;
+  HGLOBAL     hReg;
+  DWORD       dwSize;
+  LPSTR       szRegA;
+  CTempBuffer<TCHAR, 1024> szReg;
+
+  LPCTSTR lpszBSTRFileName = OLE2CT_EX(bstrFileName, _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
+#ifndef _UNICODE
+  if (lpszBSTRFileName == NULL)
+  {
+    return E_OUTOFMEMORY;
+  }
+#endif // _UNICODE
+
+  hInstResDll = LoadLibraryEx(lpszBSTRFileName, NULL, LOAD_LIBRARY_AS_DATAFILE);
+
+  if (NULL == hInstResDll)
+  {
+    ATLTRACE(atlTraceRegistrar, 0, _T("Failed to LoadLibrary on %s\n"), bstrFileName);
+    hr = AtlHresultFromLastError();
+    goto ReturnHR;
+  }
+
+  hrscReg =FindResource((HMODULE)hInstResDll, szID, szType);
+
+  if (NULL == hrscReg)
+  {
+    ATLTRACE(atlTraceRegistrar, 0, (HIWORD(szID) == NULL) ?
+      _T("Failed to FindResource on ID:%d TYPE:%s\n") :
+      _T("Failed to FindResource on ID:%s TYPE:%s\n"),
+      szID, szType);
+    hr = AtlHresultFromLastError();
+    goto ReturnHR;
+  }
+  hReg = LoadResource((HMODULE)hInstResDll, hrscReg);
+
+  if (NULL == hReg)
+  {
+    ATLTRACE(atlTraceRegistrar, 0, _T("Failed to LoadResource\n"));
+    hr = AtlHresultFromLastError();
+    goto ReturnHR;
+  }
+
+  dwSize = SizeofResource((HMODULE)hInstResDll, hrscReg);
+  szRegA = (LPSTR)hReg;
+
+  // Allocate extra space for NULL.
+  if (dwSize + 1 < dwSize)
+    return E_OUTOFMEMORY;
+  ATLTRY(szReg.Allocate(dwSize + 1));
+  if (szReg == NULL)
+  {
+    hr = E_OUTOFMEMORY;
+    goto ReturnHR;
+  }
+
+#ifdef _UNICODE
+  DWORD uniSize = ::MultiByteToWideChar(_AtlGetConversionACP(), 0, szRegA, dwSize, szReg, dwSize);
+  if (uniSize == 0)
+  {
+    hr = AtlHresultFromLastError();
+    goto ReturnHR;
+  }
+  // Append a NULL at the end.
+  szReg[uniSize] = NULL;
+#else
+  Checked::memcpy_s(szReg, dwSize, szRegA, dwSize);
+  // Append a NULL at the end.
+     szReg[dwSize] = NULL;
+#endif
+
+
+
+  hr = parser.RegisterBuffer(szReg, bRegister);
+
+ReturnHR:
+
+  if (NULL != hInstResDll)
+    FreeLibrary((HMODULE)hInstResDll);
+  return hr;
+}
+
+inline HRESULT STDMETHODCALLTYPE RegObject::ResourceRegister(LPCOLESTR szFileName, UINT nID, LPCOLESTR szType)
+{
+  USES_CONVERSION_EX;
+
+  LPCTSTR lpszT = OLE2CT_EX(szType, _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
+#ifndef _UNICODE
+  if (lpszT == NULL)
+  {
+    return E_OUTOFMEMORY;
+  }
+#endif // _UNICODE
+
+  return RegisterFromResource(szFileName, MAKEINTRESOURCE(nID), lpszT, TRUE);
+}
+
+inline HRESULT STDMETHODCALLTYPE RegObject::ResourceRegisterSz(LPCOLESTR szFileName, LPCOLESTR szID, LPCOLESTR szType)
+{
+  USES_CONVERSION_EX;
+  if (szID == NULL || szType == NULL)
+    return E_INVALIDARG;
+
+  LPCTSTR lpszID = OLE2CT_EX(szID, _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
+  LPCTSTR lpszType = OLE2CT_EX(szType, _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
+#ifndef _UNICODE
+  if (lpszID == NULL || lpszType==NULL)
+  {
+    return E_OUTOFMEMORY;
+  }
+#endif // _UNICODE
+  return RegisterFromResource(szFileName, lpszID, lpszType, TRUE);
+}
+
+inline HRESULT STDMETHODCALLTYPE RegObject::ResourceUnregister(LPCOLESTR szFileName, UINT nID, LPCOLESTR szType)
+{
+  USES_CONVERSION_EX;
+
+  LPCTSTR lpszT = OLE2CT_EX(szType, _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
+#ifndef _UNICODE
+  if (lpszT == NULL)
+  {
+    return E_OUTOFMEMORY;
+  }
+#endif // _UNICODE
+  return RegisterFromResource(szFileName, MAKEINTRESOURCE(nID), lpszT, FALSE);
+}
+
+inline HRESULT STDMETHODCALLTYPE RegObject::ResourceUnregisterSz(LPCOLESTR szFileName, LPCOLESTR szID, LPCOLESTR szType)
+{
+  USES_CONVERSION_EX;
+  if (szID == NULL || szType == NULL)
+    return E_INVALIDARG;
+
+  LPCTSTR lpszID = OLE2CT_EX(szID, _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
+  LPCTSTR lpszType = OLE2CT_EX(szType, _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
+#ifndef _UNICODE
+  if (lpszID == NULL || lpszType == NULL)
+  {
+    return E_OUTOFMEMORY;
+  }
+#endif // _UNICODE
+
+  return RegisterFromResource(szFileName, lpszID, lpszType, FALSE);
+}
+
+inline HRESULT RegObject::RegisterWithString(LPCOLESTR bstrData, BOOL bRegister)
+{
+  USES_CONVERSION_EX;
+  RegParser  parser(this);
+
+  LPCTSTR szReg = OLE2CT_EX(bstrData, _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
+#ifndef _UNICODE
+  if (szReg == NULL)
+  {
+    return E_OUTOFMEMORY;
+  }
+#endif // _UNICODE
+
+  HRESULT hr = parser.RegisterBuffer((LPTSTR)szReg, bRegister);
+
+  return hr;
+}
+
+inline HRESULT RegObject::ClearReplacements()
+{
+  m_csMap.Lock();
+  HRESULT hr = m_RepMap.ClearReplacements();
+  m_csMap.Unlock();
+  return hr;
+}
+
+
+inline LPCOLESTR RegObject::StrFromMap(__in_z LPTSTR lpszKey)
+{
+  m_csMap.Lock();
+  LPCOLESTR lpsz = m_RepMap.Lookup(lpszKey);
+  if (lpsz == NULL) // not found!!
+    ATLTRACE(atlTraceRegistrar, 0, _T("Map Entry not found\n"));
+  m_csMap.Unlock();
+  return lpsz;
+}
+
+inline HRESULT RegObject::CommonFileRegister(LPCOLESTR bstrFileName, BOOL bRegister)
+{
+  USES_CONVERSION_EX;
+
+  RegParser  parser(this);
+
+  LPCTSTR lpszBSTRFileName = OLE2CT_EX(bstrFileName, _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
+#ifndef _UNICODE
+  if (lpszBSTRFileName == NULL)
+  {
+    return E_OUTOFMEMORY;
+  }
+#endif // _UNICODE
+
+  HANDLE hFile = CreateFile(lpszBSTRFileName, GENERIC_READ, 0, NULL,
+                OPEN_EXISTING,
+                FILE_ATTRIBUTE_READONLY,
+                NULL);
+  if (INVALID_HANDLE_VALUE == hFile)
+  {
+    ATLTRACE2(atlTraceRegistrar, 0, _T("Failed to CreateFile on %s\n"), lpszBSTRFileName);
+    return AtlHresultFromLastError();
+  }
+
+  HRESULT hRes = S_OK;
+  DWORD cbRead;
+  DWORD cbFile = GetFileSize(hFile, NULL); // No HiOrder DWORD required
+
+  CTempBuffer<char, 1024> szReg;
+  // Extra space for NULL.
+  ATLTRY(szReg.Allocate(cbFile + 1));
+  if (szReg == NULL)
+  {
+    hRes = E_OUTOFMEMORY;
+    goto ReturnHR;
+  }
+
+  if (ReadFile(hFile, szReg, cbFile, &cbRead, NULL) == 0)
+  {
+    ATLTRACE2(atlTraceRegistrar, 0, "Read Failed on file%s\n", lpszBSTRFileName);
+    hRes =  AtlHresultFromLastError();
+  }
+  if (SUCCEEDED(hRes))
+  {
+    szReg[cbRead] = NULL;
+
+#ifdef _UNICODE
+    CTempBuffer<WCHAR, 1024> szConverted;
+    ATLTRY(szConverted.Allocate(cbFile + 1));
+    if (szConverted == NULL)
+    {
+      hRes =  E_OUTOFMEMORY;
+      goto ReturnHR;
+
+    }
+    if (::MultiByteToWideChar(_AtlGetConversionACP(), 0, szReg, cbFile + 1, szConverted, cbFile + 1) == 0)
+    {
+      hRes = AtlHresultFromLastError();
+      goto ReturnHR;
+    }
+
+
+
+
+#else
+    LPTSTR szConverted = szReg;
+#endif
+    hRes = parser.RegisterBuffer(szConverted, bRegister);
+  }
+ReturnHR:
+  CloseHandle(hFile);
+  return hRes;
+}
+
+__declspec(selectany) const TCHAR* const RegParser::rgszNeverDelete[] =
+{
+  _T("AppID"),
+  _T("CLSID"),
+  _T("Component Categories"),
+  _T("FileType"),
+  _T("Interface"),
+  _T("Hardware"),
+  _T("Mime"),
+  _T("SAM"),
+  _T("SECURITY"),
+  _T("SYSTEM"),
+  _T("Software"),
+  _T("TypeLib")
+};
+
+__declspec(selectany) const int RegParser::cbNeverDelete = sizeof(rgszNeverDelete) / sizeof(LPCTSTR*);
+
+
+inline BOOL RegParser::VTFromRegType(LPCTSTR szValueType, VARTYPE& vt)
+{
+  struct typemap
+  {
+    LPCTSTR lpsz;
+    VARTYPE vt;
+  };
+#pragma warning (push)
+#pragma warning (disable : 4640)  // construction of local static object is not thread-safe
+
+  static const typemap map[] = {
+    {szStringVal, VT_BSTR},
+    {multiszStringVal, VT_BSTR | VT_BYREF},
+    {szDwordVal,  VT_UI4},
+    {szBinaryVal, VT_UI1}
+  };
+
+#pragma warning (pop)
+
+  for (int i=0;i<sizeof(map)/sizeof(typemap);i++)
+  {
+    if (!lstrcmpi(szValueType, map[i].lpsz))
+    {
+      vt = map[i].vt;
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+
+}
+
+inline BYTE RegParser::ChToByte(const TCHAR ch)
+{
+  switch (ch)
+  {
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+        return (BYTE) (ch - '0');
+    case 'A':
+    case 'B':
+    case 'C':
+    case 'D':
+    case 'E':
+    case 'F':
+        return (BYTE) (10 + (ch - 'A'));
+    case 'a':
+    case 'b':
+    case 'c':
+    case 'd':
+    case 'e':
+    case 'f':
+        return (BYTE) (10 + (ch - 'a'));
+    default:
+        ATLASSERT(FALSE);
+        ATLTRACE(atlTraceRegistrar, 0, _T("Bogus value %c passed as binary Hex value\n"), ch);
+        return 0;
+  }
+}
+
+inline HKEY RegParser::HKeyFromString(__in_z LPTSTR szToken)
+{
+  struct keymap
+  {
+    LPCTSTR lpsz;
+    HKEY hkey;
+  };
+  static const keymap map[] = {
+    {_T("HKCR"), HKEY_CLASSES_ROOT},
+    {_T("HKCU"), HKEY_CURRENT_USER},
+    {_T("HKLM"), HKEY_LOCAL_MACHINE},
+    {_T("HKU"),  HKEY_USERS},
+    {_T("HKPD"), HKEY_PERFORMANCE_DATA},
+    {_T("HKDD"), HKEY_DYN_DATA},
+    {_T("HKCC"), HKEY_CURRENT_CONFIG},
+    {_T("HKEY_CLASSES_ROOT"), HKEY_CLASSES_ROOT},
+    {_T("HKEY_CURRENT_USER"), HKEY_CURRENT_USER},
+    {_T("HKEY_LOCAL_MACHINE"), HKEY_LOCAL_MACHINE},
+    {_T("HKEY_USERS"), HKEY_USERS},
+    {_T("HKEY_PERFORMANCE_DATA"), HKEY_PERFORMANCE_DATA},
+    {_T("HKEY_DYN_DATA"), HKEY_DYN_DATA},
+    {_T("HKEY_CURRENT_CONFIG"), HKEY_CURRENT_CONFIG}
+  };
+
+  for (int i=0;i<sizeof(map)/sizeof(keymap);i++)
+  {
+    if (!lstrcmpi(szToken, map[i].lpsz))
+      return map[i].hkey;
+  }
+  return NULL;
+}
+
+inline LPTSTR RegParser::StrChr(__in_z LPTSTR lpsz, __in TCHAR ch)
+{
+  LPTSTR p = NULL;
+
+  if (lpsz == NULL)
+    return NULL;
+
+  while (*lpsz)
+  {
+    if (*lpsz == ch)
+    {
+      p = lpsz;
+      break;
+    }
+    lpsz = CharNext(lpsz);
+  }
+  return p;
+}
+
+inline RegParser::RegParser(RegObject* pRegObj)
+{
+  m_pRegObj           = pRegObj;
+  m_pchCur            = NULL;
+
+  CString guid(m_pRegObj->StrFromMap(_T("CLSID")));
+  if (guid.IsEmpty()) {
+    guid = m_pRegObj->StrFromMap(_T("APPID"));
+  }
+
+  guid_prefix_ = _tcstoul(guid.Mid(1, 4), NULL, 16);
+
+  ATLASSERT(guid_prefix_);
+}
+
+inline BOOL RegParser::IsSpace(TCHAR ch)
+{
+  switch (ch)
+  {
+    case _T(' '):
+    case _T('\t'):
+    case _T('\r'):
+    case _T('\n'):
+        return TRUE;
+  }
+
+  return FALSE;
+}
+
+inline void RegParser::SkipWhiteSpace()
+{
+  while(IsSpace(*m_pchCur))
+    m_pchCur = CharNext(m_pchCur);
+}
+
+inline HRESULT RegParser::NextToken(__out_ecount_z(MAX_VALUE) LPTSTR szToken)
+{
+  SkipWhiteSpace();
+
+  // NextToken cannot be called at EOS
+  if (NULL == *m_pchCur)
+    return GenerateError(E_ATL_UNEXPECTED_EOS);
+
+  LPCTSTR szOrig = szToken;
+  // handle quoted value / key
+  if (chQuote == *m_pchCur)
+  {
+    m_pchCur = CharNext(m_pchCur);
+
+    while (NULL != *m_pchCur && !EndOfVar())
+    {
+      if (chQuote == *m_pchCur) // If it is a quote that means we must skip it
+        m_pchCur = CharNext(m_pchCur);
+
+      LPTSTR pchPrev = m_pchCur;
+      m_pchCur = CharNext(m_pchCur);
+
+      INT_PTR nChars = m_pchCur - pchPrev;
+
+      // Make sure we have room for nChars plus terminating NULL
+      if ((szToken + nChars + 1) >= szOrig + MAX_VALUE)
+        return GenerateError(E_ATL_VALUE_TOO_LARGE);
+
+      for (int i = 0; i < (int)nChars; i++, szToken++, pchPrev++)
+        *szToken = *pchPrev;
+    }
+
+    if (NULL == *m_pchCur)
+    {
+      ATLTRACE(atlTraceRegistrar, 0, _T("NextToken : Unexpected End of File\n"));
+      return GenerateError(E_ATL_UNEXPECTED_EOS);
+    }
+
+    *szToken = NULL;
+    m_pchCur = CharNext(m_pchCur);
+  }
+
+  else
+  {
+    // Handle non-quoted ie parse up till first "White Space"
+    while (NULL != *m_pchCur && !IsSpace(*m_pchCur))
+    {
+      LPTSTR pchPrev = m_pchCur;
+      m_pchCur = CharNext(m_pchCur);
+
+      INT_PTR nChars = m_pchCur - pchPrev;
+
+      // Make sure we have room for nChars plus terminating NULL
+      if ((szToken + nChars + 1) >= szOrig + MAX_VALUE)
+        return GenerateError(E_ATL_VALUE_TOO_LARGE);
+
+      for (int i = 0; i < (int)nChars; i++, szToken++, pchPrev++)
+        *szToken = *pchPrev;
+    }
+
+    *szToken = NULL;
+  }
+  return S_OK;
+}
+
+inline HRESULT RegParser::AddValue(__in CRegKey& rkParent, __in_z_opt LPCTSTR szValueName, __out_ecount_z(MAX_VALUE) LPTSTR szToken)
+{
+  HRESULT hr;
+
+  TCHAR    szValue[MAX_VALUE];
+  VARTYPE     vt = VT_EMPTY;
+  LONG        lRes = ERROR_SUCCESS;
+  UINT        nIDRes = 0;
+
+  if (FAILED(hr = NextToken(szValue)))
+    return hr;
+  if (!VTFromRegType(szValue, vt))
+  {
+    ATLTRACE(atlTraceRegistrar, 0, _T("%s Type not supported\n"), szValue);
+    return GenerateError(E_ATL_TYPE_NOT_SUPPORTED);
+  }
+
+  SkipWhiteSpace();
+  if (FAILED(hr = NextToken(szValue)))
+    return hr;
+
+  switch (vt)
+  {
+  case VT_BSTR:
+    {
+      lRes = rkParent.SetStringValue(szValueName, szValue);
+      ATLTRACE(atlTraceRegistrar, 2, _T("Setting Value %s at %s\n"), szValue, !szValueName ? _T("default") : szValueName);
+      break;
+    }
+  case VT_BSTR | VT_BYREF:
+    {
+      ATLTRACE(atlTraceRegistrar, 2, _T("Setting Value %s at %s\n"), szValue, !szValueName ? _T("default") : szValueName);
+      int nLen = lstrlen(szValue) + 2; //Allocate space for double null termination.
+      CTempBuffer<TCHAR, 256> pszDestValue;
+      //nLen should be >= the max size of the target buffer.
+      ATLTRY(pszDestValue.Allocate(nLen));
+      if (pszDestValue != NULL)
+      {
+        TCHAR* p = pszDestValue;
+        TCHAR* q = szValue;
+        nLen = 0;
+        while (*q != _T('\0'))
+        {
+          TCHAR* r = CharNext(q);
+          if (*q == _T('\\') && *r == _T('0'))
+          {
+            *p++ = NULL;
+            q = CharNext(r);
+          }
+          else
+          {
+            *p = *q;
+#ifndef _UNICODE
+            if (IsDBCSLeadByte(*q))
+            {
+              p++;
+              q++;
+              //Protect from Lead byte followed by the zero terminator.May skip beyond the end of the string.
+              if (*q == _T('\0')) { break; }
+              *p = *q;
+            }
+#endif
+            p++;
+            q++;
+          }
+          nLen ++;
+        }
+         //Always terminate with 2 NULLs.
+        *p = NULL;
+        p++;
+          *p = NULL;
+        lRes = rkParent.SetMultiStringValue(szValueName, pszDestValue);
+      }
+      else
+      {
+        lRes = ERROR_OUTOFMEMORY;
+      }
+    }
+    break;
+  case VT_UI4:
+    {
+      ULONG ulVal;
+      USES_CONVERSION_EX;
+
+      LPOLESTR lpszV = T2OLE_EX(szValue, _ATL_SAFE_ALLOCA_DEF_THRESHOLD);
+  #ifndef _UNICODE
+      if(lpszV == NULL)
+        return E_OUTOFMEMORY;
+  #endif
+      VarUI4FromStr(lpszV, 0, 0, &ulVal);
+
+      lRes = rkParent.SetDWORDValue(szValueName, ulVal);
+      ATLTRACE(atlTraceRegistrar, 2, _T("Setting Value %d at %s\n"), ulVal, !szValueName ? _T("default") : szValueName);
+      break;
+    }
+  case VT_UI1:
+    {
+      int cbValue = lstrlen(szValue);
+      if (cbValue & 0x00000001)
+      {
+        ATLTRACE(atlTraceRegistrar, 0, _T("Binary Data does not fall on BYTE boundries\n"));
+        return E_FAIL;
+      }
+      int cbValDiv2 = cbValue/2;
+      CTempBuffer<BYTE, 256> rgBinary;
+      ATLTRY(rgBinary.Allocate(cbValDiv2));
+      if (rgBinary == NULL)
+        return E_FAIL;
+      memset(rgBinary, 0, cbValDiv2);
+      for (int irg = 0; irg < cbValue; irg++)
+        rgBinary[(irg/2)] |= (ChToByte(szValue[irg])) << (4*(1 - (irg & 0x00000001)));
+      lRes = RegSetValueEx(rkParent, szValueName, 0, REG_BINARY, rgBinary, cbValDiv2);
+      break;
+    }
+  }
+
+  if (ERROR_SUCCESS != lRes)
+  {
+    nIDRes = E_ATL_VALUE_SET_FAILED;
+    return AtlHresultFromWin32(lRes);
+  }
+
+  if (FAILED(hr = NextToken(szToken)))
+    return hr;
+
+  return S_OK;
+}
+
+inline BOOL RegParser::CanForceRemoveKey(LPCTSTR szKey)
+{
+  for (int iNoDel = 0; iNoDel < cbNeverDelete; iNoDel++)
+    if (!lstrcmpi(szKey, rgszNeverDelete[iNoDel]))
+       return FALSE;                       // We cannot delete it
+
+  return TRUE;
+}
+
+inline BOOL RegParser::HasSubKeys(HKEY hkey)
+{
+  DWORD       cbSubKeys = 0;
+
+  if (RegQueryInfoKey(hkey, NULL, NULL, NULL,
+                 &cbSubKeys, NULL, NULL,
+                 NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
+  {
+    ATLTRACE(atlTraceRegistrar, 0, _T("Should not be here!!\n"));
+    ATLASSERT(FALSE);
+    return FALSE;
+  }
+
+  return cbSubKeys > 0;
+}
+
+inline BOOL RegParser::HasValues(HKEY hkey)
+{
+  DWORD       cbValues = 0;
+
+  LONG lResult = RegQueryInfoKey(hkey, NULL, NULL, NULL,
+                  NULL, NULL, NULL,
+                  &cbValues, NULL, NULL, NULL, NULL);
+  if (ERROR_SUCCESS != lResult)
+  {
+    ATLTRACE(atlTraceRegistrar, 0, _T("RegQueryInfoKey Failed "));
+    ATLASSERT(FALSE);
+    return FALSE;
+  }
+
+  if (1 == cbValues)
+  {
+    DWORD cbMaxName= MAX_VALUE;
+    TCHAR szValueName[MAX_VALUE];
+    // Check to see if the Value is default or named
+    lResult = RegEnumValue(hkey, 0, szValueName, &cbMaxName, NULL, NULL, NULL, NULL);
+    if (ERROR_SUCCESS == lResult && (szValueName[0] != NULL))
+      return TRUE; // Named Value means we have a value
+    return FALSE;
+  }
+
+  return cbValues > 0; // More than 1 means we have a non-default value
+}
+
+inline HRESULT RegParser::SkipAssignment(__inout_ecount_z(MAX_VALUE) LPTSTR szToken)
+{
+  HRESULT hr;
+  TCHAR szValue[MAX_VALUE];
+
+  if (*szToken == chEquals)
+  {
+    if (FAILED(hr = NextToken(szToken)))
+      return hr;
+    // Skip assignment
+    SkipWhiteSpace();
+    if (FAILED(hr = NextToken(szValue)))
+      return hr;
+    if (FAILED(hr = NextToken(szToken)))
+      return hr;
+  }
+
+  return S_OK;
+}
+
+inline HRESULT RegParser::PreProcessBuffer(__in_z LPTSTR lpszReg, __deref_out_z LPTSTR* ppszReg)
+{
+  ATLASSERT(lpszReg != NULL);
+  ATLASSERT(ppszReg != NULL);
+
+  if (lpszReg == NULL || ppszReg == NULL)
+    return E_POINTER;
+
+  *ppszReg = NULL;
+  int nSize = lstrlen(lpszReg)*2;
+  CParseBuffer pb(nSize);
+  if (pb.p == NULL)
+    return E_OUTOFMEMORY;
+  m_pchCur = lpszReg;
+  HRESULT hr = S_OK;
+
+  while (*m_pchCur != NULL) // look for end
+  {
+    if (*m_pchCur == _T('%'))
+    {
+      m_pchCur = CharNext(m_pchCur);
+      if (*m_pchCur == _T('%'))
+      {
+        if (!pb.AddChar(m_pchCur))
+        {
+          hr = E_OUTOFMEMORY;
+          break;
+        }
+      }
+      else
+      {
+        LPTSTR lpszNext = StrChr(m_pchCur, _T('%'));
+        if (lpszNext == NULL)
+        {
+          ATLTRACE(atlTraceRegistrar, 0, _T("Error no closing %% found\n"));
+          hr = GenerateError(E_ATL_UNEXPECTED_EOS);
+          break;
+        }
+        if ((lpszNext-m_pchCur) > 31)
+        {
+          hr = E_FAIL;
+          break;
+        }
+        int nLength = int(lpszNext - m_pchCur);
+        TCHAR buf[32];
+        Checked::tcsncpy_s(buf, _countof(buf), m_pchCur, nLength);
+        LPCOLESTR lpszVar = m_pRegObj->StrFromMap(buf);
+        if (lpszVar == NULL)
+        {
+          hr = GenerateError(E_ATL_NOT_IN_MAP);
+          break;
+        }
+        if (!pb.AddString(lpszVar))
+        {
+          hr = E_OUTOFMEMORY;
+          break;
+        }
+
+        while (m_pchCur != lpszNext)
+          m_pchCur = CharNext(m_pchCur);
+      }
+    }
+    else
+    {
+      if (!pb.AddChar(m_pchCur))
+      {
+        hr = E_OUTOFMEMORY;
+        break;
+      }
+    }
+
+    m_pchCur = CharNext(m_pchCur);
+  }
+  if (SUCCEEDED(hr))
+    *ppszReg = pb.Detach();
+  return hr;
+}
+
+inline HRESULT RegParser::RegisterBuffer(__in_z LPTSTR szBuffer, __in BOOL bRegister)
+{
+  TCHAR   szToken[MAX_VALUE];
+  HRESULT hr = S_OK;
+
+  LPTSTR szReg = NULL;
+  hr = PreProcessBuffer(szBuffer, &szReg);
+  if (FAILED(hr))
+    return hr;
+
+  ATLTRACE(atlTraceRegistrar, 0, _T("%s\n"), szReg);
+
+  m_pchCur = szReg;
+
+  // Preprocess szReg
+
+  while (NULL != *m_pchCur)
+  {
+    if (FAILED(hr = NextToken(szToken)))
+      break;
+    HKEY hkBase;
+    if ((hkBase = HKeyFromString(szToken)) == NULL)
+    {
+      ATLTRACE(atlTraceRegistrar, 0, _T("HKeyFromString failed on %s\n"), szToken);
+      hr = GenerateError(E_ATL_BAD_HKEY);
+      break;
+    }
+
+    if (FAILED(hr = NextToken(szToken)))
+      break;
+
+    if (chLeftBracket != *szToken)
+    {
+      ATLTRACE(atlTraceRegistrar, 0, _T("Syntax error, expecting a {, found a %s\n"), szToken);
+      hr = GenerateError(E_ATL_MISSING_OPENKEY_TOKEN);
+      break;
+    }
+    if (bRegister)
+    {
+      LPTSTR szRegAtRegister = m_pchCur;
+      hr = RegisterSubkeys(szToken, hkBase, bRegister);
+      if (FAILED(hr))
+      {
+        ATLTRACE(atlTraceRegistrar, 0, _T("Failed to register, cleaning up!\n"));
+        m_pchCur = szRegAtRegister;
+        RegisterSubkeys(szToken, hkBase, FALSE);
+        break;
+      }
+    }
+    else
+    {
+      if (FAILED(hr = RegisterSubkeys(szToken, hkBase, bRegister)))
+        break;
+    }
+
+    SkipWhiteSpace();
+  }
+  CoTaskMemFree(szReg);
+  return hr;
+}
+
+inline HRESULT RegParser::RegisterSubkeys(__out_ecount_z(MAX_VALUE) LPTSTR szToken, __in HKEY hkParent, __in BOOL bRegister, __in BOOL bRecover)
+{
+  CRegKey keyCur;
+  LONG    lRes;
+  TCHAR  szKey[_MAX_PATH];
+  BOOL    bDelete = TRUE;
+  BOOL    bInRecovery = bRecover;
+  HRESULT hr = S_OK;
+
+  ATLTRACE(atlTraceRegistrar, 2, _T("Num Els = %d\n"), cbNeverDelete);
+  if (FAILED(hr = NextToken(szToken)))
+    return hr;
+
+
+  while (*szToken != chRightBracket) // Continue till we see a }
+  {
+
+
+    bDelete = TRUE;
+    BOOL bTokenDelete = !lstrcmpi(szToken, szDelete);
+
+    if (!lstrcmpi(szToken, szForceRemove) || bTokenDelete)
+    {
+      if (FAILED(hr = NextToken(szToken)))
+        break;
+
+      if (bRegister)
+      {
+        CRegKey rkForceRemove;
+
+        if (StrChr(szToken, chDirSep) != NULL)
+          return GenerateError(E_ATL_COMPOUND_KEY);
+
+        if (CanForceRemoveKey(szToken))
+        {
+          rkForceRemove.Attach(hkParent);
+          // Error not returned. We will overwrite the values any way.
+          rkForceRemove.RecurseDeleteKey(szToken);
+          rkForceRemove.Detach();
+        }
+        if (bTokenDelete)
+        {
+          if (FAILED(hr = NextToken(szToken)))
+            break;
+          if (FAILED(hr = SkipAssignment(szToken)))
+            break;
+          goto EndCheck;
+        }
+      }
+
+    }
+
+    if (!lstrcmpi(szToken, szNoRemove))
+    {
+      bDelete = FALSE;    // set even for register
+      if (FAILED(hr = NextToken(szToken)))
+        break;
+    }
+
+    if (!lstrcmpi(szToken, szValToken)) // need to add a value to hkParent
+    {
+      TCHAR  szValueName[_MAX_PATH];
+
+
+
+      if (FAILED(hr = NextToken(szValueName)))
+        break;
+      if (FAILED(hr = NextToken(szToken)))
+        break;
+
+      if (*szToken != chEquals)
+        return GenerateError(E_ATL_EXPECTING_EQUAL);
+
+      if (bRegister)
+      {
+        CRegKey rk;
+
+        rk.Attach(hkParent);
+        hr = AddValue(rk, szValueName, szToken);
+        rk.Detach();
+
+        if (FAILED(hr))
+          return hr;
+
+        goto EndCheck;
+      }
+      else
+      {
+        if (!bRecover && bDelete)
+        {
+          ATLTRACE(atlTraceRegistrar, 1, _T("Deleting %s\n"), szValueName);
+          // We have to open the key for write to be able to delete.
+          CRegKey rkParent;
+          lRes = rkParent.Open(hkParent, NULL, KEY_WRITE);
+          if (lRes == ERROR_SUCCESS)
+          {
+            lRes = rkParent.DeleteValue(szValueName);
+            if (lRes != ERROR_SUCCESS && lRes != ERROR_FILE_NOT_FOUND)
+            {
+              // Key not present is not an error
+              hr = AtlHresultFromWin32(lRes);
+              break;
+            }
+          }
+          else
+          {
+            hr = AtlHresultFromWin32(lRes);
+            break;
+          }
+        }
+        if (FAILED(hr = SkipAssignment(szToken)))
+          break;
+        continue;  // can never have a subkey
+      }
+    }
+
+    if (StrChr(szToken, chDirSep) != NULL)
+      return GenerateError(E_ATL_COMPOUND_KEY);
+
+    if (bRegister)
+    {
+      lRes = keyCur.Open(hkParent, szToken, KEY_READ | KEY_WRITE);
+      if (ERROR_SUCCESS != lRes)
+      {
+        // Failed all access try read only
+        lRes = keyCur.Open(hkParent, szToken, KEY_READ);
+        if (ERROR_SUCCESS != lRes)
+        {
+          // Finally try creating it
+          ATLTRACE(atlTraceRegistrar, 2, _T("Creating key %s\n"), szToken);
+          lRes = keyCur.Create(hkParent, szToken, REG_NONE, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE);
+          if (lRes != ERROR_SUCCESS)
+            return AtlHresultFromWin32(lRes);
+        }
+      }
+
+      if (FAILED(hr = NextToken(szToken)))
+        break;
+
+
+      if (*szToken == chEquals)
+      {
+        if (FAILED(hr = AddValue(keyCur, NULL, szToken))) // NULL == default
+          break;
+      }
+    }
+    else //Unregister
+    {
+      if (!bRecover)
+      {
+         lRes = keyCur.Open(hkParent, szToken, KEY_READ);
+
+      }
+      else
+        lRes = ERROR_FILE_NOT_FOUND;
+
+
+      // Open failed set recovery mode
+      if (lRes != ERROR_SUCCESS)
+        bRecover = true;
+
+      // TRACE out Key open status and if in recovery mode
+#ifdef _DEBUG
+      if (!bRecover)
+        ATLTRACE(atlTraceRegistrar, 1, _T("Opened Key %s\n"), szToken);
+      else
+        ATLTRACE(atlTraceRegistrar, 0, _T("Ignoring Open key on %s : In Recovery mode\n"), szToken);
+#endif //_DEBUG
+
+      // Remember Subkey
+      Checked::tcsncpy_s(szKey, _countof(szKey), szToken, _TRUNCATE);
+
+      if (FAILED(hr = NextToken(szToken)))
+        break;
+      if (FAILED(hr = SkipAssignment(szToken)))
+        break;
+
+      if (*szToken == chLeftBracket && lstrlen(szToken) == 1)
+      {
+        hr = RegisterSubkeys(szToken, keyCur.m_hKey, bRegister, bRecover);
+        // In recover mode ignore error
+        if (FAILED(hr) && !bRecover)
+          break;
+        // Skip the }
+        if (FAILED(hr = NextToken(szToken)))
+          break;
+      }
+
+#ifdef _DEBUG
+      if (bRecover != bInRecovery)
+        ATLTRACE(atlTraceRegistrar, 0, _T("Ending Recovery Mode\n"));
+#endif
+      bRecover = bInRecovery;
+
+      if (lRes == ERROR_FILE_NOT_FOUND)
+        // Key already not present so not an error.
+        continue;
+
+      if (lRes != ERROR_SUCCESS)
+      {
+        // We are recovery mode continue on errors else break
+        if (bRecover)
+          continue;
+        else
+        {
+          hr = AtlHresultFromWin32(lRes);
+          break;
+        }
+      }
+
+      // If in recovery mode
+      if (bRecover && HasSubKeys(keyCur))
+      {
+        // See if the KEY is in the NeverDelete list and if so, don't
+        if (CanForceRemoveKey(szKey) && bDelete)
+        {
+          ATLTRACE(atlTraceRegistrar, 0, _T("Deleting non-empty subkey %s by force\n"), szKey);
+          // Error not returned since we are in recovery mode. The error that caused recovery mode is returned
+          keyCur.RecurseDeleteKey(szKey);
+        }
+        continue;
+      }
+
+      BOOL bHasSubKeys=HasSubKeys(keyCur);
+      lRes = keyCur.Close();
+      if (lRes != ERROR_SUCCESS)
+      return AtlHresultFromWin32(lRes);
+
+      if (bDelete&& !bHasSubKeys)
+      {
+        ATLTRACE(atlTraceRegistrar, 0, _T("Deleting Key %s\n"), szKey);
+        CRegKey rkParent;
+        rkParent.Attach(hkParent);
+        lRes = rkParent.DeleteSubKey(szKey);
+        rkParent.Detach();
+        if (lRes != ERROR_SUCCESS)
+        {
+
+          hr = AtlHresultFromWin32(lRes);
+          break;
+        }
+      }
+
+
+
+
+    }
+
+EndCheck:
+
+    if (bRegister)
+    {
+      if (*szToken == chLeftBracket && lstrlen(szToken) == 1)
+      {
+        if (FAILED(hr = RegisterSubkeys(szToken, keyCur.m_hKey, bRegister, FALSE)))
+          break;
+        if (FAILED(hr = NextToken(szToken)))
+          break;
+      }
+    }
+  }
+
+  return hr;
+}
+
+};  //namespace omaha
+
+#endif  //OMAHA_BASE_STATREGEX_H__
+
diff --git a/base/store_watcher.h b/base/store_watcher.h
new file mode 100644
index 0000000..fb55e24
--- /dev/null
+++ b/base/store_watcher.h
@@ -0,0 +1,59 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+//
+// A simple interface for monitoring changes
+// happening to a store.
+//
+#ifndef OMAHA_COMMON_STORE_WATCHER_H_
+#define OMAHA_COMMON_STORE_WATCHER_H_
+
+#include "omaha/base/synchronized.h"
+
+namespace omaha {
+
+// Allows for monitoring changes happening to a store
+// (independant of what the underlying store is).
+class StoreWatcher {
+ public:
+  StoreWatcher() {}
+  virtual ~StoreWatcher() {}
+
+  // Called to create/reset the event that gets signaled
+  // any time the store changes.  Access the created
+  // event using change_event().
+  virtual HRESULT EnsureEventSetup() = 0;
+
+  // Indicates if any changes have occured
+  bool HasChangeOccurred() const {
+    return IsHandleSignaled(change_event());
+  }
+
+  // Get the event that is signaled on store changes.
+  // Note:
+  //   * This event will remain constant until the class is destroyed.
+  //   * One should call EnsureEventSetup to set-up the event.
+  //   * The event is only signaled on the next change and remains signaled.
+  //     Do not call ::ResetEvent(). Call EnsureEventSetup() to reset
+  //     the event and wait for more changes.
+  virtual HANDLE change_event() const = 0;
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(StoreWatcher);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_STORE_WATCHER_H_
diff --git a/base/string.cc b/base/string.cc
new file mode 100644
index 0000000..a206660
--- /dev/null
+++ b/base/string.cc
@@ -0,0 +1,3391 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/string.h"
+
+#include <wininet.h>        // For INTERNET_MAX_URL_LENGTH.
+#include <algorithm>
+#include <cstdlib>
+#include "base/scoped_ptr.h"
+#include "omaha/base/commontypes.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/localization.h"
+#include "omaha/base/logging.h"
+
+namespace omaha {
+
+namespace {
+// Testing shows that only the following ASCII characters are
+// considered spaces by GetStringTypeA: 9-13, 32, 160.
+// Rather than call GetStringTypeA with no locale, as we used to,
+// we look up the values directly in a precomputed array.
+
+SELECTANY byte spaces[256] = {
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 1,  // 0-9
+  1, 1, 1, 1, 0, 0, 0, 0, 0, 0,  // 10-19
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 20-29
+  0, 0, 1, 0, 0, 0, 0, 0, 0, 0,  // 30-39
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 40-49
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 50-59
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 60-69
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 70-79
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 80-89
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 90-99
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 100-109
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 110-119
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 120-129
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 130-139
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 140-149
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 150-159
+  1, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 160-169
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 170-179
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 180-189
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 190-199
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 200-209
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 210-219
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 220-229
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 230-239
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 240-249
+  0, 0, 0, 0, 0, 1,              // 250-255
+};
+}  // namespace
+
+const TCHAR* const kFalse = _T("false");
+const TCHAR* const kTrue  = _T("true");
+
+bool IsSpaceW(WCHAR c) {
+  // GetStringTypeW considers these characters to be spaces:
+  // 9-13, 32, 133, 160, 5760, 8192-8203, 8232, 8233, 12288
+  if (c < 256)
+    return (c == 133 || IsSpaceA((char) (c & 0xff)));
+
+  return (c >= 8192 && c <= 8203) || c == 8232 ||
+    c == 8233 || c == 12288;
+}
+
+bool IsSpaceA(char c) {
+  return spaces[static_cast<unsigned char>(c)] == 1;
+}
+
+int TrimCString(CString &s) {
+  int len = Trim(s.GetBuffer());
+  s.ReleaseBufferSetLength(len);
+  return len;
+}
+
+void MakeLowerCString(CString & s) {
+  int len = s.GetLength();
+  String_FastToLower(s.GetBuffer());
+  s.ReleaseBufferSetLength(len);
+}
+
+int Trim(TCHAR *s) {
+  ASSERT(s, (L""));
+
+  // First find end of leading spaces
+  TCHAR *start = s;
+  while (*start) {
+    if (!IsSpace(*start))
+      break;
+    ++start;
+  }
+
+  // Now search for the end, remembering the start of the last spaces
+  TCHAR *end = start;
+  TCHAR *last_space = end;
+  while (*end) {
+    if (!IsSpace(*end))
+      last_space = end + 1;
+    ++end;
+  }
+
+  // Copy the part we want
+  int len = last_space - start;
+  // lint -e{802}  Conceivably passing a NULL pointer
+  memmove(s, start, len * sizeof(TCHAR));
+
+  // 0 terminate
+  s[len] = 0;
+
+  return len;
+}
+
+void TrimString(CString& s, const TCHAR* delimiters) {
+  s = s.Trim(delimiters);
+}
+
+// Strip the first token from the front of argument s.  A token is a
+// series of consecutive non-blank characters - unless the first
+// character is a double-quote ("), in that case the token is the full
+// quoted string
+CString StripFirstQuotedToken(const CString& s) {
+  const int npos = -1;
+
+  // Make a writeable copy
+  CString str(s);
+
+  // Trim any surrounding blanks (and tabs, for the heck of it)
+  TrimString(str, L" \t");
+
+  // Too short to have a second token
+  if (str.GetLength() <= 1)
+    return L"";
+
+  // What kind of token are we stripping?
+  if (str[0] == L'\"') {
+    // Remove leading quoting string
+    int i = str.Find(L"\"", 1);
+    if (i != npos)
+      i++;
+    return str.Mid(i);
+  } else {
+    // Remove leading token
+    int i = str.FindOneOf(L" \t");
+    if (i != npos)
+      i++;
+    return str.Mid(i);
+  }
+}
+
+// A block of text to separate lines, and back
+void TextToLines(const CString& text, const TCHAR* delimiter, std::vector<CString>* lines) {
+  ASSERT(delimiter, (L""));
+  ASSERT(lines, (L""));
+
+  size_t delimiter_len = ::lstrlen(delimiter);
+  int b = 0;
+  int e = 0;
+
+  for (b = 0; e != -1 && b < text.GetLength(); b = e + delimiter_len) {
+    e = text.Find(delimiter, b);
+    if (e != -1) {
+      ASSERT1(e - b > 0);
+      lines->push_back(text.Mid(b, e - b));
+    } else {
+      lines->push_back(text.Mid(b));
+    }
+  }
+}
+
+void LinesToText(const std::vector<CString>& lines, const TCHAR* delimiter, CString* text) {
+  ASSERT(delimiter, (L""));
+  ASSERT(text, (L""));
+
+  size_t delimiter_len = ::lstrlen(delimiter);
+  size_t len = 0;
+  for (size_t i = 0; i < lines.size(); ++i) {
+    len += lines[i].GetLength() + delimiter_len;
+  }
+  text->Empty();
+  text->Preallocate(len);
+  for (std::vector<CString>::size_type i = 0; i < lines.size(); ++i) {
+    text->Append(lines[i]);
+    if (delimiter_len) {
+      text->Append(delimiter);
+    }
+  }
+}
+
+int CleanupWhitespaceCString(CString &s) {
+  int len = CleanupWhitespace(s.GetBuffer());
+  s.ReleaseBufferSetLength(len);
+  return len;
+}
+
+int CleanupWhitespace(TCHAR *str) {
+  ASSERT(str, (L""));
+
+  TCHAR *src      = str;
+  TCHAR *dest     = str;
+  int    spaces   = 0;
+  bool   at_start = true;
+  while (true) {
+    // At end of string?
+    TCHAR c = *src;
+    if (0 == c)
+      break;
+
+    // Look for whitespace; copy it over if not whitespace
+    if (IsSpace(c)) {
+      ++spaces;
+    }
+    else {
+      *dest++ = c;
+      at_start = false;
+      spaces = 0;
+    }
+
+    // Write only first consecutive space (but skip space at start)
+    if (1 == spaces && !at_start)
+      *dest++ = ' ';
+
+    ++src;
+  }
+
+  // Remove trailing space, if any
+  if (dest > str && *(dest - 1) == L' ')
+    --dest;
+
+  // 0-terminate
+  *dest = 0;
+
+  return dest - str;
+}
+
+// Take 1 single hexadecimal "digit" (as a character) and return its decimal value
+// Returns -1 if given invalid hex digit
+int HexDigitToDec(const TCHAR digit) {
+  if (digit >= L'A' && digit <= L'F')
+    return 10 + (digit - L'A');
+  else if (digit >= L'a' && digit <= L'f')
+    return 10 + (digit - L'a');
+  else if (digit >= L'0' && digit <= L'9')
+    return (digit - L'0');
+  else
+    return -1;
+}
+
+// Convert the 2 hex chars at positions <pos> and <pos>+1 in <s> to a char (<char_out>)
+// Note: scanf was giving me troubles, so here's the manual version
+// Extracted char gets written to <char_out>, which must be allocated by
+// the caller; return true on success or false if parameters are incorrect
+// or string does not have 2 hex digits at the specified position
+// NOTE: <char_out> is NOT a string, just a pointer to a char for the result
+bool ExtractChar(const CString & s, int pos, unsigned char * char_out) {
+  // char_out may be NULL
+
+  if (s.GetLength() < pos + 1) {
+    return false;
+  }
+
+  if (pos < 0 || NULL == char_out) {
+    ASSERT(0, (_T("invalid params: pos<0 or char_out is NULL")));
+    return false;
+  }
+
+  TCHAR c1 = s.GetAt(pos);
+  TCHAR c2 = s.GetAt(pos+1);
+
+  int p1 = HexDigitToDec(c1);
+  int p2 = HexDigitToDec(c2);
+
+  if (p1 == -1 || p2 == -1) {
+    return false;
+  }
+
+  *char_out = (unsigned char)(p1 * 16 + p2);
+  return true;
+}
+
+WCHAR *ToWide (const char *s, int len) {
+    ASSERT (s, (L""));
+    WCHAR *w = new WCHAR [len+1]; if (!w) { return NULL; }
+    // int rc = MultiByteToWideChar (CP_ACP, 0, s.GetString(), (int)s.GetLength()+1, w, s.GetLength()+1);
+    // TODO(omaha): why would it ever be the case that rc > len?
+    int rc = MultiByteToWideChar (CP_ACP, 0, s, len, w, len);
+    if (rc > len) { delete [] w; return NULL; }
+    // ASSERT (rc <= len, (L""));
+    w[rc]=L'\0';
+    return w;
+}
+
+const byte *BufferContains (const byte *buf, uint32 buf_len, const byte *data, uint32 data_len) {
+  ASSERT(data, (L""));
+  ASSERT(buf, (L""));
+
+  for (uint32 i = 0; i < buf_len; i++) {
+    uint32 j = i;
+    uint32 k = 0;
+    uint32 len = 0;
+    while (j < buf_len && k < data_len && buf[j++] == data[k++]) { len++; }
+    if (len == data_len) { return buf + i; }
+  }
+  return 0;
+}
+
+// Converting the Ansi Multibyte String into unicode string. The multibyte
+// string is encoded using the specified codepage.
+// The code is pretty much like the U2W function, except the codepage can be
+// any valid windows CP.
+BOOL AnsiToWideString(const char *from, int length, UINT codepage, CString *to) {
+  ASSERT(from, (L""));
+  ASSERT(to, (L""));
+  ASSERT1(length >= -1);
+  // Figure out how long the string is
+  int req_chars = MultiByteToWideChar(codepage, 0, from, length, NULL, 0);
+
+  if (req_chars <= 0) {
+    UTIL_LOG(LEVEL_WARNING, (_T("MultiByteToWideChar Failed ")));
+    *to = AnsiToWideString(from, length);
+    return FALSE;
+  }
+
+  TCHAR *buffer = to->GetBufferSetLength(req_chars);
+  int conv_chars = MultiByteToWideChar(codepage, 0, from, length, buffer, req_chars);
+  if (conv_chars == 0) {
+    UTIL_LOG(LEVEL_WARNING, (_T("MultiByteToWideChar Failed ")));
+    to->ReleaseBuffer(0);
+    *to = AnsiToWideString(from, length);
+    return FALSE;
+  }
+
+  // Something truly horrible happened.
+  ASSERT (req_chars == conv_chars, (L"MBToWide returned unexpected value: GetLastError()=%d",GetLastError()));
+  // If length was inferred, conv_chars includes the null terminator.
+  // Adjust the length here to remove null termination,
+  // because we use the length-qualified CString constructor,
+  // which automatically adds null termination given an unterminated array.
+  if (-1 == length) { --conv_chars; }
+  to->ReleaseBuffer(conv_chars);
+  return TRUE;
+}
+
+// CStringW(const char* from) did not cast all character properly
+// so we write our own.
+CString AnsiToWideString(const char *from, int length) {
+  ASSERT(from, (L""));
+  ASSERT1(length >= -1);
+  if (length < 0)
+    length = strlen(from);
+  CString to;
+  TCHAR *buffer = to.GetBufferSetLength(length);
+  for (int i = 0; i < length; ++i)
+      buffer[i] = static_cast<UINT8>(from[i]);
+  to.ReleaseBuffer(length);
+  return to;
+}
+
+
+// Transform a unicode string into UTF8, as represented in an ASCII string
+CStringA WideToUtf8(const CString& w) {
+  // Add a cutoff. If it's all ascii, convert it directly
+  const TCHAR* input = static_cast<const TCHAR*>(w.GetString());
+  int input_len = w.GetLength(), i;
+  for (i = 0; i < input_len; ++i) {
+    if (input[i] > 127) {
+      break;
+    }
+  }
+
+  // If we made it to the end without breaking, then it's all ANSI, so do a quick convert
+  if (i == input_len) {
+    return WideToAnsiDirect(w);
+  }
+
+  // Figure out how long the string is
+  int req_bytes = ::WideCharToMultiByte(CP_UTF8, 0, w, -1, NULL, 0, NULL, NULL);
+
+  scoped_array<char> utf8_buffer(new char[req_bytes]);
+
+  int conv_bytes = ::WideCharToMultiByte(CP_UTF8, 0, w, -1, utf8_buffer.get(), req_bytes, NULL, NULL);
+  ASSERT1(req_bytes == conv_bytes);
+
+  // conv_bytes includes the null terminator, when we read this in, don't read the terminator
+  CStringA out(utf8_buffer.get(), conv_bytes - 1);
+
+  return out;
+}
+
+CString Utf8ToWideChar(const char* utf8, uint32 num_bytes) {
+  ASSERT1(utf8);
+  if (num_bytes == 0) {
+    return CString();
+  }
+
+  uint32 number_of_wide_chars = ::MultiByteToWideChar(CP_UTF8, 0, utf8, num_bytes, NULL, 0);
+  number_of_wide_chars += 1;  // make room for NULL terminator
+
+  CString ret_string;
+  TCHAR* buffer = ret_string.GetBuffer(number_of_wide_chars);
+  DWORD number_of_characters_copied = ::MultiByteToWideChar(CP_UTF8, 0, utf8, num_bytes, buffer, number_of_wide_chars);
+  ASSERT1(number_of_characters_copied == number_of_wide_chars - 1);
+  buffer[number_of_wide_chars - 1] = _T('\0');  // ensure there is a NULL terminator
+  ret_string.ReleaseBuffer();
+
+  // Strip the byte order marker if there is one in the document.
+  if (ret_string[0] == kUnicodeBom) {
+    ret_string = ret_string.Right(ret_string.GetLength() - 1);
+  }
+
+  if (number_of_characters_copied > 0) {
+    return ret_string;
+  }
+
+  // Failure case
+  return CString();
+}
+
+CString Utf8BufferToWideChar(const std::vector<uint8>& buffer) {
+  CString result;
+  if (!buffer.empty()) {
+    result = Utf8ToWideChar(
+        reinterpret_cast<const char*>(&buffer.front()), buffer.size());
+  }
+  return result;
+}
+
+CString AbbreviateString (const CString & title, int32 max_len) {
+    ASSERT (max_len, (L""));
+    CString s(title);
+    TrimCString(s);  // remove whitespace at start/end
+    if (s.GetLength() > max_len) {
+        s = s.Left (max_len - 2);
+        CString orig(s);
+        // remove partial words
+        while (s.GetLength() > 1 && !IsSpace(s[s.GetLength()-1])) { s = s.Left (s.GetLength() - 1); }
+        // but not if it would make the string very short
+        if (s.GetLength() < max_len / 2) { s = orig; }
+        s += _T("..");
+        }
+
+    return s;
+}
+
+CString GetAbsoluteUri(const CString& uri) {
+  int i = String_FindString(uri, _T("://"));
+  if (i==-1) return uri;
+
+  // add trailing / if none exists
+  int j = String_FindChar(uri, L'/',i+3);
+  if (j==-1) return (uri+NOTRANSL(_T("/")));
+
+  // remove duplicate trailing slashes
+  int len = uri.GetLength();
+  if (len > 1 && uri.GetAt(len-1) == '/' && uri.GetAt(len-2) == '/') {
+    CString new_uri(uri);
+    int new_len = new_uri.GetLength();
+    while (new_len > 1 && new_uri.GetAt(new_len-1) == '/' && new_uri.GetAt(new_len-2) == '/') {
+      new_len--;
+      new_uri = new_uri.Left(new_len);
+    }
+    return new_uri;
+  }
+  else return uri;
+}
+
+// requires that input have a PROTOCOL (http://) for proper behavior
+// items with the "file" protocol are returned as is (what is the hostname in that case? C: ? doesn't make sense)
+// TODO(omaha): loosen requirement
+// includes http://, e.g. http://www.google.com/
+CString GetUriHostName(const CString& uri, bool strip_leading) {
+  if (String_StartsWith(uri,NOTRANSL(_T("file:")),true)) return uri;
+
+  // correct any "errors"
+  CString s(GetAbsoluteUri(uri));
+
+  // Strip the leading "www."
+  if (strip_leading)
+  {
+    int index_www = String_FindString(s, kStrLeadingWww);
+    if (index_www != -1)
+      ReplaceCString (s, kStrLeadingWww, _T(""));
+  }
+
+  int i = String_FindString(s, _T("://"));
+  if(i==-1) return uri;
+  int j = String_FindChar(s, L'/',i+3);
+  if(j==-1) return uri;
+  return s.Left(j+1);
+}
+
+// requires that input have a PROTOCOL (http://) for proper behavior
+// TODO(omaha): loosen requirement
+// removes the http:// and the extra slash '/' at the end.
+// http://www.google.com/ -> www.google.com (or google.com if strip_leading = true)
+CString GetUriHostNameHostOnly(const CString& uri, bool strip_leading) {
+  CString s(GetUriHostName(uri,strip_leading));
+
+  // remove protocol
+  int i = String_FindString (s, _T("://"));
+  if(i==-1) return s;
+  CString ss(s.Right (s.GetLength() - i-3));
+
+  // remove the last '/'
+  int j = ss.ReverseFind('/');
+  if (j == -1) return ss;
+  return ss.Left(j);
+}
+
+CString AbbreviateUri(const CString& uri, int32 max_len) {
+  ASSERT1(max_len);
+  ASSERT1(!uri.IsEmpty());
+
+  CString s(uri);
+  VERIFY1(String_FindString (s, _T("://")));
+
+  TrimCString(s);
+  // SKIP_LOC_BEGIN
+  RemoveFromStart (s, _T("ftp://"), false);
+  RemoveFromStart (s, _T("http://"), false);
+  RemoveFromStart (s, _T("https://"), false);
+  RemoveFromStart (s, _T("www."), false);
+  RemoveFromStart (s, _T("ftp."), false);
+  RemoveFromStart (s, _T("www-"), false);
+  RemoveFromStart (s, _T("ftp-"), false);
+  RemoveFromEnd (s, _T(".htm"));
+  RemoveFromEnd (s, _T(".html"));
+  RemoveFromEnd (s, _T(".asp"));
+  // SKIP_LOC_END
+  if (s.GetLength() > max_len) {
+    // try to keep the portion after the last /
+    int32 last_slash = s.ReverseFind ((TCHAR)'/');
+    CString after_last_slash;
+    if (last_slash == -1) { after_last_slash = _T(""); }
+    else { after_last_slash = s.Right (uri.GetLength() - last_slash - 1); }
+    if (after_last_slash.GetLength() > max_len / 2) {
+        after_last_slash = after_last_slash.Right (max_len / 2);
+    }
+    s = s.Left (max_len - after_last_slash.GetLength() - 2);
+    s += "..";
+    s += after_last_slash;
+  }
+  return s;
+}
+
+// normalized version of a URI intended to map duplicates to the same string
+// the normalized URI is not a valid URI
+CString NormalizeUri (const CString & uri) {
+  CString s(uri);
+  TrimCString(s);
+  MakeLowerCString(s);
+  // SKIP_LOC_BEGIN
+  ReplaceCString (s, _T(":80"), _T(""));
+
+  RemoveFromEnd (s, _T("/index.html"));
+  RemoveFromEnd (s, _T("/welcome.html"));  // old netscape standard
+  RemoveFromEnd (s, _T("/"));
+
+  RemoveFromStart (s, _T("ftp://"), false);
+  RemoveFromStart (s, _T("http://"), false);
+  RemoveFromStart (s, _T("https://"), false);
+  RemoveFromStart (s, _T("www."), false);
+  RemoveFromStart (s, _T("ftp."), false);
+  RemoveFromStart (s, _T("www-"), false);
+  RemoveFromStart (s, _T("ftp-"), false);
+
+  ReplaceCString (s, _T("/./"), _T("/"));
+  // SKIP_LOC_END
+
+  // TODO(omaha):
+  // fixup URLs like a/b/../../c
+  // while ($s =~ m!\/\.\.\!!) {
+  //    $s =~ s!/[^/]*/\.\./!/!;
+  //    }
+
+  // TODO(omaha):
+  // unescape characters
+  // Note from RFC1630:  "Sequences which start with a percent sign
+  // but are not followed by two hexadecimal characters are reserved
+  // for future extension"
+  // $str =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg if defined $str;
+
+  return s;
+}
+
+CString RemoveInternetProtocolHeader (const CString& url) {
+  int find_colon_slash_slash = String_FindString(url, NOTRANSL(L"://"));
+  if( find_colon_slash_slash != -1 ) {
+    // remove PROTOCOL://
+    return url.Right(url.GetLength() - find_colon_slash_slash - 3);
+  } else if (String_StartsWith(url, NOTRANSL(L"mailto:"), true)) {
+    // remove "mailto:"
+    return url.Right(url.GetLength() - 7);
+  } else {
+    // return as is
+    return url;
+  }
+}
+
+HRESULT ConvertFileUriToLocalPath(const CString& uri, CString* path_out) {
+  ASSERT1(path_out);
+  ASSERT1(uri.GetLength() < INTERNET_MAX_URL_LENGTH);
+
+  if (uri.IsEmpty()) {
+    return E_INVALIDARG;
+  }
+
+  DWORD buf_len = MAX_PATH;
+  HRESULT hr = ::PathCreateFromUrl(uri,
+                                   CStrBuf(*path_out, MAX_PATH),
+                                   &buf_len,
+                                   NULL);
+  return hr;
+}
+
+void RemoveFromStart (CString & s, const TCHAR* remove, bool ignore_case) {
+  ASSERT(remove, (L""));
+
+  // Remove the characters if it is the prefix
+  if (String_StartsWith(s, remove, ignore_case))
+    s.Delete(0, lstrlen(remove));
+}
+
+bool String_EndsWith(const TCHAR *str, const TCHAR *end_str, bool ignore_case) {
+  ASSERT(end_str, (L""));
+  ASSERT(str, (L""));
+
+  int str_len = lstrlen(str);
+  int end_len = lstrlen(end_str);
+
+  // Definitely false if the suffix is longer than the string
+  if (end_len > str_len)
+    return false;
+
+  const TCHAR *str_ptr = str + str_len;
+  const TCHAR *end_ptr = end_str + end_len;
+
+  while (end_ptr >= end_str) {
+    // Check for matching characters
+    TCHAR c1 = *str_ptr;
+    TCHAR c2 = *end_ptr;
+
+    if (ignore_case) {
+      c1 = Char_ToLower(c1);
+      c2 = Char_ToLower(c2);
+    }
+
+    if (c1 != c2)
+      return false;
+
+    --str_ptr;
+    --end_ptr;
+  }
+
+  // if we haven't failed out, it must be ok!
+  return true;
+}
+
+CString String_MakeEndWith(const TCHAR* str, const TCHAR* end_str, bool ignore_case) {
+  if (String_EndsWith(str, end_str, ignore_case)) {
+    return str;
+  } else {
+    CString r(str);
+    r += end_str;
+    return r;
+  }
+}
+
+void RemoveFromEnd (CString & s, const TCHAR* remove) {
+  ASSERT(remove, (L""));
+
+  // If the suffix is shorter than the string, don't bother
+  int remove_len = lstrlen(remove);
+  if (s.GetLength() < remove_len) return;
+
+  // If the suffix is equal
+  int suffix_begin = s.GetLength() - remove_len;
+  if (0 == lstrcmp(s.GetString() + suffix_begin, remove))
+    s.Delete(suffix_begin, remove_len);
+}
+
+CString ElideIfNeeded (const CString & input_string, int max_len, int min_len) {
+  ASSERT (min_len <= max_len, (L""));
+  ASSERT (max_len >= TSTR_SIZE(kEllipsis)+1, (L""));
+  ASSERT (min_len >= TSTR_SIZE(kEllipsis)+1, (L""));
+
+  CString s = input_string;
+
+  s.TrimRight();
+  if (s.GetLength() > max_len) {
+    int truncate_at = max_len - TSTR_SIZE(kEllipsis);
+    // find first space going backwards from character one after the truncation point
+    while (truncate_at >= min_len && !IsSpace(s.GetAt(truncate_at)))
+      truncate_at--;
+
+    // skip the space(s)
+    while (truncate_at >= min_len && IsSpace(s.GetAt(truncate_at)))
+      truncate_at--;
+
+    truncate_at++;
+
+    if (truncate_at <= min_len || truncate_at > (max_len - static_cast<int>(TSTR_SIZE(kEllipsis)))) {
+      // we weren't able to break at a word boundary, may as well use more of the string
+      truncate_at = max_len - TSTR_SIZE(kEllipsis);
+
+      // skip space(s)
+      while (truncate_at > 0 && IsSpace(s.GetAt(truncate_at-1)))
+        truncate_at--;
+    }
+
+    s = s.Left(truncate_at);
+    s += kEllipsis;
+  }
+
+  UTIL_LOG(L6, (L"elide (%d %d) %s -> %s", min_len, max_len, input_string, s));
+  return s;
+}
+
+// these functions untested
+// UTF8 parameter supported on XP/2000 only
+HRESULT AnsiToUTF8 (char * src, int src_len, char * dest, int *dest_len) {
+  ASSERT (dest_len, (L""));
+  ASSERT (dest, (L""));
+  ASSERT (src, (L""));
+
+  // First use MultiByteToWideChar(CP_UTF8, ...) to convert to Unicode
+  // then use WideCharToMultiByte to convert from Unicode to UTF8
+  WCHAR *unicode = new WCHAR [(src_len + 1) * sizeof (TCHAR)]; ASSERT (unicode, (L""));
+  int chars_written = MultiByteToWideChar (CP_ACP, 0, src, src_len, unicode, src_len);
+  ASSERT (chars_written == src_len, (L""));
+  char *unmappable = " ";
+  BOOL unmappable_characters = false;
+  *dest_len = WideCharToMultiByte (CP_UTF8, 0, unicode, chars_written, dest, *dest_len, unmappable, &unmappable_characters);
+  delete [] unicode;
+  return S_OK;
+}
+
+// Convert Wide to ANSI directly. Use only when it is all ANSI
+CStringA WideToAnsiDirect(const CString & in) {
+  int in_len = in.GetLength();
+  const TCHAR * in_buf = static_cast<const TCHAR*>(in.GetString());
+
+  CStringA out;
+  unsigned char * out_buf = (unsigned char *)out.GetBufferSetLength(in_len);
+
+  for(int i = 0; i < in_len; ++i)
+    out_buf[i] = static_cast<unsigned char>(in_buf[i]);
+
+  out.ReleaseBuffer(in_len);
+  return out;
+}
+
+HRESULT UCS2ToUTF8 (LPCWSTR src, int src_len, char * dest, int *dest_len) {
+  ASSERT(dest_len, (L""));
+  ASSERT(dest, (L""));
+
+  *dest_len = WideCharToMultiByte (CP_UTF8, 0, src, src_len, dest, *dest_len, NULL,NULL);
+  return S_OK;
+}
+
+HRESULT UTF8ToUCS2 (const char * src, int src_len, LPWSTR dest, int *dest_len) {
+  ASSERT (dest_len, (L""));
+  ASSERT (src, (L""));
+
+  *dest_len = MultiByteToWideChar (CP_UTF8, 0, src, src_len, dest, *dest_len);
+  ASSERT (*dest_len == src_len, (L""));
+  return S_OK;
+}
+
+HRESULT UTF8ToAnsi (char * src, int, char * dest, int *dest_len) {
+  ASSERT(dest_len, (L""));
+  ASSERT(dest, (L""));
+  ASSERT(src, (L""));
+
+  src; dest; dest_len;  // unreferenced formal parameter
+
+  // First use MultiByteToWideChar(CP_UTF8, ...) to convert to Unicode
+  // then use WideCharToMultiByte to convert from Unicode to ANSI
+  return E_FAIL;
+}
+
+// clean up a string so it can be included within a JavaScript string
+// mainly involves escaping characters
+CString SanitizeString(const CString & in, DWORD mode) {
+  CString out(in);
+
+  if (mode & kSanHtml) {
+    // SKIP_LOC_BEGIN
+    ReplaceCString(out, _T("&"), _T("&amp;"));
+    ReplaceCString(out, _T("<"), _T("&lt;"));
+    ReplaceCString(out, _T(">"), _T("&gt;"));
+    // SKIP_LOC_END
+  }
+
+  if ((mode & kSanXml) == kSanXml) {
+    // SKIP_LOC_BEGIN
+    ReplaceCString(out, _T("'"), _T("&apos;"));
+    ReplaceCString(out, _T("\""), _T("&quot;"));
+    // SKIP_LOC_END
+  }
+
+  // Note that this SAN_JAVASCRIPT and kSanXml should not be used together.
+  ASSERT ((mode & (kSanJs | kSanXml)) != (kSanJs | kSanXml), (L""));
+
+  if ((mode & kSanJs) == kSanJs) {
+    // SKIP_LOC_BEGIN
+    ReplaceCString(out, _T("\\"), _T("\\\\"));
+    ReplaceCString(out, _T("\'"), _T("\\\'"));
+    ReplaceCString(out, _T("\""), _T("\\\""));
+    ReplaceCString(out, _T("\n"), _T(" "));
+    ReplaceCString(out, _T("\t"), _T(" "));
+    // SKIP_LOC_END
+  }
+
+  if ((mode & kSanHtmlInput) == kSanHtmlInput) {
+    // SKIP_LOC_BEGIN
+    ReplaceCString(out, _T("\""), _T("&quot;"));
+    ReplaceCString(out, _T("'"), _T("&#39;"));
+    // SKIP_LOC_END
+  }
+
+  return out;
+}
+
+// Bolds the periods used for abbreviation.  Call this after HighlightTerms.
+CString BoldAbbreviationPeriods(const CString & in) {
+  CString out(in);
+  CString abbrev;
+  for (int i = 0; i < kAbbreviationPeriodLength; ++i)
+    abbrev += _T(".");
+  ReplaceCString(out, abbrev, NOTRANSL(_T("<b>")) + abbrev + NOTRANSL(_T("</b>")));
+  return out;
+}
+
+// Unescape a escaped sequence leading by a percentage symbol '%',
+// and converted the unescaped sequence (in UTF8) into unicode.
+// Inputs:  src is the input string.
+//          pos is the starting position.
+// Returns: true if a EOS(null) char was encounted.
+//          out contains the unescaped and converted unicode string.
+//          consumed_length is how many bytes in the src string have been
+//          unescaped.
+// We can avoid the expensive UTF8 conversion step if there are no higher
+// ansi characters So if there aren't any, just convert it ANSI-to-WIDE
+// directly, which is cheaper.
+inline bool UnescapeSequence(const CString &src, int pos,
+                             CStringW *out, int *consumed_length) {
+  ASSERT1(out);
+  ASSERT1(consumed_length);
+
+  int length = src.GetLength();
+  // (input_len - pos) / 3 is enough for un-escaping the (%xx)+ sequences.
+  int max_dst_length = (length - pos) / 3;
+  scoped_array<char> unescaped(new char[max_dst_length]);
+  char *buf = unescaped.get();
+  if (buf == NULL) {  // no enough space ???
+    *consumed_length = 0;
+    return false;
+  }
+  char *dst = buf;
+  bool is_utf8 = false;
+  // It is possible that there is a null character '\0' in the sequence.
+  // Because the CStringT does't support '\0' in it, we stop
+  // parsing the input string when it is encounted.
+  bool eos_encounted = false;
+  uint8 ch;
+  int s = pos;
+  while (s + 2 < length && src[s] == '%' && !eos_encounted &&
+         ExtractChar(src, s + 1, &ch)) {
+    if (ch != 0)
+      *dst++ = ch;
+    else
+      eos_encounted = true;
+    if (ch >= 128)
+      is_utf8 = true;
+    s += 3;
+  }
+
+  ASSERT1(dst <= buf + max_dst_length);  // just to make sure
+
+  *consumed_length = s - pos;
+  if (is_utf8)
+    AnsiToWideString(buf, dst - buf, CP_UTF8, out);
+  else
+    *out = AnsiToWideString(buf, dst - buf);
+  return eos_encounted;
+}
+
+// There is an encoding called "URL-encoding". This function takes a URL-encoded string
+// and converts it back to the original representation
+// example: "?q=moon+doggy_%25%5E%26&" = "moon doggy_%^&"
+CString Unencode(const CString &input) {
+  const int input_len = input.GetLength();
+  const TCHAR *src = input.GetString();
+  // input_len is enough for containing the unencoded string.
+  CString out;
+  TCHAR *head = out.GetBuffer(input_len);
+  TCHAR *dst = head;
+  int s = 0;
+  bool eos_encounted = false;
+  bool is_utf8 = false;
+  CStringW fragment;
+  int consumed_length = 0;
+  while (s < input_len && !eos_encounted) {
+    switch (src[s]) {
+      case '+' :
+        *dst++ = ' ';
+        ASSERT1(dst <= head + input_len);
+        ++s;
+        break;
+      case '%' :
+        eos_encounted =
+          UnescapeSequence(input, s, &fragment, &consumed_length);
+        if (consumed_length > 0) {
+          s += consumed_length;
+          ASSERT1(dst + fragment.GetLength() <= head + input_len);
+          for (int i = 0; i < fragment.GetLength(); ++i)
+            *dst++ = fragment[i];
+        } else {
+          *dst++ = src[s++];
+          ASSERT1(dst <= head + input_len);
+        }
+        break;
+      default:
+        *dst++ = src[s];
+        ASSERT1(dst <= head + input_len);
+        ++s;
+    }
+  }
+  int out_len = dst - head;
+  out.ReleaseBuffer(out_len);
+  return out;
+}
+
+CString GetTextInbetween(const CString &input, const CString &start, const CString &end) {
+  int start_index = String_FindString(input, start);
+  if (start_index == -1)
+    return L"";
+
+  start_index += start.GetLength();
+  int end_index = String_FindString(input, end, start_index);
+  if (end_index == -1)
+    return L"";
+
+  return input.Mid(start_index, end_index - start_index);
+}
+
+// Given a string, get the parameter and url-unencode it
+CString GetParam(const CString & input, const CString & key) {
+  CString my_key(_T("?"));
+  my_key.Append(key);
+  my_key += L'=';
+
+  return Unencode(GetTextInbetween(input, my_key, NOTRANSL(L"?")));
+}
+
+// Get an xml-like field from a string
+CString GetField (const CString & input, const CString & field) {
+  CString start_field(NOTRANSL(_T("<")));
+  start_field += field;
+  start_field += L'>';
+
+  int32 start = String_FindString(input, start_field);
+  if (start == -1) { return _T(""); }
+  start += 2 + lstrlen (field);
+
+  CString end_field(NOTRANSL(_T("</")));
+  end_field += field;
+  end_field += L'>';
+
+  int32 end = String_FindString(input, end_field);
+  if (end == -1) { return _T(""); }
+
+  return input.Mid (start, end - start);
+}
+
+// ------------------------------------------------------------
+// Finds a whole word match in the query.
+// If the word has non-spaces either before or after, it will not qualify as
+//   a match.  i.e. "pie!" is not a match because of the exclamation point.
+// TODO(omaha): Add parameter that will consider punctuation acceptable.
+//
+// Optionally will look for a colon at the end.
+// If not found, return -1.
+int FindWholeWordMatch (const CString &query,
+  const CString &word_to_match,
+  const bool end_with_colon,
+  const int index_begin) {
+  if (word_to_match.IsEmpty()) {
+    return -1;
+  }
+
+  int index_word_begin = index_begin;
+
+  // Keep going until we find a whole word match, or the string ends.
+  do {
+    index_word_begin = String_FindString (query, word_to_match, index_word_begin);
+
+    if (-1 == index_word_begin) {
+      return index_word_begin;
+    }
+
+    // If it's not a whole word match, keep going.
+    if (index_word_begin > 0 &&
+      !IsSpaceW (query[index_word_begin - 1])) {
+      goto LoopEnd;
+    }
+
+    if (end_with_colon) {
+      int index_colon = String_FindChar (query, L':', index_word_begin);
+
+      // If there is no colon in the string, return now.
+      if (-1 == index_colon) {
+        return -1;
+      }
+
+      // If there is text between the end of the word and the colon, keep going.
+      if (index_colon - index_word_begin != word_to_match.GetLength()) {
+        goto LoopEnd;
+      }
+    } else {
+      // If there are more chars left after this word/phrase, and
+      // they are not spaces, return.
+      if (query.GetLength() > index_word_begin + word_to_match.GetLength() &&
+        !IsSpaceW (query.GetAt (index_word_begin + word_to_match.GetLength()))) {
+        goto LoopEnd;
+      }
+    }
+
+    // It fits all the requirements, so return the index to the beginning of the word.
+    return index_word_begin;
+
+LoopEnd:
+    ++index_word_begin;
+
+  } while (-1 != index_word_begin);
+
+  return index_word_begin;
+}
+
+// --------------------------------------------------------
+// Do whole-word replacement in "str".
+void ReplaceWholeWord (const CString &string_to_replace,
+  const CString &replacement,
+  const bool trim_whitespace,
+  CString *str) {
+  ASSERT (str, (L"ReplaceWholeWord"));
+
+  if (string_to_replace.IsEmpty() || str->IsEmpty()) {
+    return;
+  }
+
+  int index_str = 0;
+  do {
+    index_str = FindWholeWordMatch (*str, string_to_replace, false, index_str);
+
+    if (-1 != index_str) {
+      // Get the strings before and after, and trim whitespace.
+      CString str_before_word(str->Left (index_str));
+      if (trim_whitespace) {
+        str_before_word.TrimRight();
+      }
+
+      CString str_after_word(str->Mid (index_str + string_to_replace.GetLength()));
+      if (trim_whitespace) {
+        str_after_word.TrimLeft();
+      }
+
+      *str = str_before_word + replacement + str_after_word;
+      index_str += replacement.GetLength() + 1;
+    }
+  } while (index_str != -1);
+}
+
+// --------------------------------------------------------
+// Reverse (big-endian<->little-endian) the shorts that make up
+// Unicode characters in a byte array of Unicode chars
+HRESULT ReverseUnicodeByteOrder(byte* unicode_string, int size_in_bytes) {
+  ASSERT (unicode_string, (L""));
+
+  // If odd # of bytes, just leave the last one alone
+  for (int i = 0; i < size_in_bytes - 1; i += 2) {
+    byte b = unicode_string[i];
+    unicode_string[i] = unicode_string[i+1];
+    unicode_string[i+1] = b;
+  }
+
+  return S_OK;
+}
+
+// case insensitive strstr
+// adapted from http://c.snippets.org/snip_lister.php?fname=stristr.c
+const char *stristr(const char *string, const char *pattern)
+{
+  ASSERT (pattern, (L""));
+  ASSERT (string, (L""));
+  ASSERT (string && pattern, (L""));
+  char *pattern_ptr, *string_ptr;
+  const char *start;
+
+  for (start = string; *start != 0; start++)
+  {
+    // find start of pattern in string
+    for ( ; ((*start!=0) && (String_ToUpperA(*start) != String_ToUpperA(*pattern))); start++)
+     ;
+    if (0 == *start)
+     return NULL;
+
+    pattern_ptr = (char *)pattern;
+    string_ptr = (char *)start;
+
+    while (String_ToUpperA(*string_ptr) == String_ToUpperA(*pattern_ptr))
+    {
+      string_ptr++;
+      pattern_ptr++;
+
+      // if end of pattern then pattern was found
+      if (0 == *pattern_ptr)
+        return (start);
+    }
+  }
+
+  return NULL;
+}
+
+// case insensitive Unicode strstr
+// adapted from http://c.snippets.org/snip_lister.php?fname=stristr.c
+const WCHAR *stristrW(const WCHAR *string, const WCHAR *pattern)
+{
+  ASSERT (pattern, (L""));
+  ASSERT (string, (L""));
+  ASSERT (string && pattern, (L""));
+  const WCHAR *start;
+
+  for (start = string; *start != 0; start++)
+  {
+    // find start of pattern in string
+    for ( ; ((*start!=0) && (String_ToUpper(*start) != String_ToUpper(*pattern))); start++)
+     ;
+    if (0 == *start)
+     return NULL;
+
+    const WCHAR *pattern_ptr = pattern;
+    const WCHAR *string_ptr = start;
+
+    while (String_ToUpper(*string_ptr) == String_ToUpper(*pattern_ptr))
+    {
+      string_ptr++;
+      pattern_ptr++;
+
+      // if end of pattern then pattern was found
+      if (0 == *pattern_ptr)
+        return (start);
+    }
+  }
+
+  return NULL;
+}
+
+// case sensitive Unicode strstr
+// adapted from http://c.snippets.org/snip_lister.php?fname=stristr.c
+const WCHAR *strstrW(const WCHAR *string, const WCHAR *pattern)
+{
+  ASSERT (pattern, (L""));
+  ASSERT (string, (L""));
+  ASSERT (string && pattern, (L""));
+  const WCHAR *start;
+
+  for (start = string; *start != 0; start++)
+  {
+    // find start of pattern in string
+    for ( ; ((*start!=0) && (*start != *pattern)); start++)
+     ;
+    if (0 == *start)
+     return NULL;
+
+    const WCHAR *pattern_ptr = pattern;
+    const WCHAR *string_ptr = start;
+
+    while (*string_ptr == *pattern_ptr)
+    {
+      string_ptr++;
+      pattern_ptr++;
+
+      // if end of pattern then pattern was found
+      if (0 == *pattern_ptr)
+        return (start);
+    }
+  }
+
+  return NULL;
+}
+
+// -------------------------------------------------------------------------
+// Helper function
+float GetLenWithWordWrap (const float len_so_far,
+  const float len_to_add,
+  const uint32 len_line) {
+  // lint -save -e414  Possible division by 0
+  ASSERT (len_line != 0, (L""));
+
+  float len_total = len_so_far + len_to_add;
+
+  // Figure out if we need to word wrap by seeing if adding the second
+  // string will cause us to span more lines than before.
+  uint32 num_lines_before = static_cast<uint32> (len_so_far / len_line);
+  uint32 num_lines_after = static_cast<uint32> (len_total / len_line);
+
+  // If it just barely fit onto the line, do not wrap to the next line.
+  if (num_lines_after > 0 && (len_total / len_line - num_lines_after == 0)) {
+    --num_lines_after;
+  }
+
+  if (num_lines_after > num_lines_before)  {
+    // Need to word wrap.
+    // lint -e{790}  Suspicious truncation
+    return num_lines_after * len_line + len_to_add;
+  }
+  else
+    return len_total;
+
+  // lint -restore
+}
+
+int CalculateBase64EscapedLen(int input_len, bool do_padding) {
+  // these formulae were copied from comments that used to go with the base64
+  // encoding functions
+  int intermediate_result = 8 * input_len + 5;
+  ASSERT(intermediate_result > 0,(L""));     // make sure we didn't overflow
+  int len = intermediate_result / 6;
+  if (do_padding) len = ((len + 3) / 4) * 4;
+  return len;
+}
+
+// Base64Escape does padding, so this calculation includes padding.
+int CalculateBase64EscapedLen(int input_len) {
+  return CalculateBase64EscapedLen(input_len, true);
+}
+
+// Base64Escape
+//   Largely based on b2a_base64 in google/docid_encryption.c
+//
+//
+int Base64EscapeInternal(const char *src, int szsrc,
+                         char *dest, int szdest, const char *base64,
+                         bool do_padding)
+{
+  ASSERT(base64, (L""));
+  ASSERT(dest, (L""));
+  ASSERT(src, (L""));
+
+  static const char kPad64 = '=';
+
+  if (szsrc <= 0) return 0;
+
+  char *cur_dest = dest;
+  const unsigned char *cur_src = reinterpret_cast<const unsigned char*>(src);
+
+  // Three bytes of data encodes to four characters of cyphertext.
+  // So we can pump through three-byte chunks atomically.
+  while (szsrc > 2) { /* keep going until we have less than 24 bits */
+    if( (szdest -= 4) < 0 ) return 0;
+    cur_dest[0] = base64[cur_src[0] >> 2];
+    cur_dest[1] = base64[((cur_src[0] & 0x03) << 4) + (cur_src[1] >> 4)];
+    cur_dest[2] = base64[((cur_src[1] & 0x0f) << 2) + (cur_src[2] >> 6)];
+    cur_dest[3] = base64[cur_src[2] & 0x3f];
+
+    cur_dest += 4;
+    cur_src += 3;
+    szsrc -= 3;
+  }
+
+  /* now deal with the tail (<=2 bytes) */
+  switch (szsrc) {
+case 0:
+  // Nothing left; nothing more to do.
+  break;
+case 1:
+  // One byte left: this encodes to two characters, and (optionally)
+  // two pad characters to round out the four-character cypherblock.
+  if( (szdest -= 2) < 0 ) return 0;
+  cur_dest[0] = base64[cur_src[0] >> 2];
+  cur_dest[1] = base64[(cur_src[0] & 0x03) << 4];
+  cur_dest += 2;
+  if (do_padding) {
+    if( (szdest -= 2) < 0 ) return 0;
+    cur_dest[0] = kPad64;
+    cur_dest[1] = kPad64;
+    cur_dest += 2;
+  }
+  break;
+case 2:
+  // Two bytes left: this encodes to three characters, and (optionally)
+  // one pad character to round out the four-character cypherblock.
+  if( (szdest -= 3) < 0 ) return 0;
+  cur_dest[0] = base64[cur_src[0] >> 2];
+  cur_dest[1] = base64[((cur_src[0] & 0x03) << 4) + (cur_src[1] >> 4)];
+  cur_dest[2] = base64[(cur_src[1] & 0x0f) << 2];
+  cur_dest += 3;
+  if (do_padding) {
+    if( (szdest -= 1) < 0 ) return 0;
+    cur_dest[0] = kPad64;
+    cur_dest += 1;
+  }
+  break;
+default:
+  // Should not be reached: blocks of 3 bytes are handled
+  // in the while loop before this switch statement.
+  ASSERT(false, (L"Logic problem? szsrc = %S",szsrc));
+  break;
+  }
+  return (cur_dest - dest);
+}
+
+#define kBase64Chars  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+
+#define kWebSafeBase64Chars "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
+
+int Base64Escape(const char *src, int szsrc, char *dest, int szdest) {
+  ASSERT(dest, (L""));
+  ASSERT(src, (L""));
+
+  return Base64EscapeInternal(src, szsrc, dest, szdest, kBase64Chars, true);
+}
+int WebSafeBase64Escape(const char *src, int szsrc, char *dest,
+  int szdest, bool do_padding) {
+    ASSERT(dest, (L""));
+    ASSERT(src, (L""));
+
+    return Base64EscapeInternal(src, szsrc, dest, szdest,
+      kWebSafeBase64Chars, do_padding);
+  }
+
+void Base64Escape(const char *src, int szsrc,
+                  CStringA* dest, bool do_padding)
+{
+  ASSERT(src, (L""));
+  ASSERT(dest,(L""));
+  const int max_escaped_size = CalculateBase64EscapedLen(szsrc, do_padding);
+  dest->Empty();
+  const int escaped_len = Base64EscapeInternal(src, szsrc,
+      dest->GetBufferSetLength(max_escaped_size + 1), max_escaped_size + 1,
+    kBase64Chars,
+    do_padding);
+  ASSERT(max_escaped_size <= escaped_len,(L""));
+  dest->ReleaseBuffer(escaped_len);
+}
+
+void WebSafeBase64Escape(const char *src, int szsrc,
+                         CStringA *dest, bool do_padding)
+{
+  ASSERT(src, (L""));
+  ASSERT(dest,(L""));
+  const int max_escaped_size =
+    CalculateBase64EscapedLen(szsrc, do_padding);
+  dest->Empty();
+  const int escaped_len = Base64EscapeInternal(src, szsrc,
+    dest->GetBufferSetLength(max_escaped_size + 1), max_escaped_size + 1,
+    kWebSafeBase64Chars,
+    do_padding);
+  ASSERT(max_escaped_size <= escaped_len,(L""));
+  dest->ReleaseBuffer(escaped_len);
+}
+
+void WebSafeBase64Escape(const CStringA& src, CStringA* dest) {
+  ASSERT(dest,(L""));
+  int encoded_len = CalculateBase64EscapedLen(src.GetLength());
+  scoped_array<char> buf(new char[encoded_len]);
+  int len = WebSafeBase64Escape(src,src.GetLength(), buf.get(), encoded_len, false);
+  dest->SetString(buf.get(), len);
+}
+
+// ----------------------------------------------------------------------
+// int Base64Unescape() - base64 decoder
+//
+// Check out
+// http://www.cis.ohio-state.edu/htbin/rfc/rfc2045.html for formal
+// description, but what we care about is that...
+//   Take the encoded stuff in groups of 4 characters and turn each
+//   character into a code 0 to 63 thus:
+//           A-Z map to 0 to 25
+//           a-z map to 26 to 51
+//           0-9 map to 52 to 61
+//           +(- for WebSafe) maps to 62
+//           /(_ for WebSafe) maps to 63
+//   There will be four numbers, all less than 64 which can be represented
+//   by a 6 digit binary number (aaaaaa, bbbbbb, cccccc, dddddd respectively).
+//   Arrange the 6 digit binary numbers into three bytes as such:
+//   aaaaaabb bbbbcccc ccdddddd
+//   Equals signs (one or two) are used at the end of the encoded block to
+//   indicate that the text was not an integer multiple of three bytes long.
+// ----------------------------------------------------------------------
+int Base64UnescapeInternal(const char *src, int len_src,
+                           char *dest, int len_dest, const char* unbase64) {
+  ASSERT (unbase64, (L""));
+  ASSERT (src, (L""));
+
+  static const char kPad64 = '=';
+
+  int decode;
+  int destidx = 0;
+  int state = 0;
+  // Used an unsigned char, since ch is used as an array index (into unbase64).
+  unsigned char ch = 0;
+  while (len_src-- && (ch = *src++) != '\0')  {
+    if (IsSpaceA(ch))  // Skip whitespace
+      continue;
+
+    if (ch == kPad64)
+      break;
+
+    decode = unbase64[ch];
+    if (decode == 99)  // A non-base64 character
+      return (-1);
+
+    // Four cyphertext characters decode to three bytes.
+    // Therefore we can be in one of four states.
+    switch (state) {
+      case 0:
+        // We're at the beginning of a four-character cyphertext block.
+        // This sets the high six bits of the first byte of the
+        // plaintext block.
+        if (dest) {
+          if (destidx >= len_dest)
+            return (-1);
+          // lint -e{734} Loss of precision
+          dest[destidx] = static_cast<char>(decode << 2);
+        }
+        state = 1;
+        break;
+      case 1:
+        // We're one character into a four-character cyphertext block.
+        // This sets the low two bits of the first plaintext byte,
+        // and the high four bits of the second plaintext byte.
+        // However, if this is the end of data, and those four
+        // bits are zero, it could be that those four bits are
+        // leftovers from the encoding of data that had a length
+        // of one mod three.
+        if (dest) {
+          if (destidx >= len_dest)
+            return (-1);
+          // lint -e{734} Loss of precision
+          dest[destidx]   |=  decode >> 4;
+          if (destidx + 1 >= len_dest) {
+            if (0 != (decode & 0x0f))
+              return (-1);
+            else
+              ;
+          } else {
+            // lint -e{734} Loss of precision
+            dest[destidx+1] = static_cast<char>((decode & 0x0f) << 4);
+          }
+        }
+        destidx++;
+        state = 2;
+        break;
+      case 2:
+        // We're two characters into a four-character cyphertext block.
+        // This sets the low four bits of the second plaintext
+        // byte, and the high two bits of the third plaintext byte.
+        // However, if this is the end of data, and those two
+        // bits are zero, it could be that those two bits are
+        // leftovers from the encoding of data that had a length
+        // of two mod three.
+        if (dest) {
+          if (destidx >= len_dest)
+            return (-1);
+          // lint -e{734} Loss of precision
+          dest[destidx]   |=  decode >> 2;
+          if (destidx +1 >= len_dest) {
+            if (0 != (decode & 0x03))
+              return (-1);
+            else
+              ;
+          } else {
+            // lint -e{734} Loss of precision
+            dest[destidx+1] = static_cast<char>((decode & 0x03) << 6);
+          }
+        }
+        destidx++;
+        state = 3;
+        break;
+      case 3:
+        // We're at the last character of a four-character cyphertext block.
+        // This sets the low six bits of the third plaintext byte.
+        if (dest) {
+          if (destidx >= len_dest)
+            return (-1);
+          // lint -e{734} Loss of precision
+          dest[destidx] |= decode;
+        }
+        destidx++;
+        state = 0;
+        break;
+
+    default:
+      ASSERT (false, (L""));
+      break;
+    }
+  }
+
+  // We are done decoding Base-64 chars.  Let's see if we ended
+  //      on a byte boundary, and/or with erroneous trailing characters.
+  if (ch == kPad64) {               // We got a pad char
+    if ((state == 0) || (state == 1))
+      return (-1);  // Invalid '=' in first or second position
+    if (len_src == 0) {
+      if (state == 2)  // We run out of input but we still need another '='
+        return (-1);
+      // Otherwise, we are in state 3 and only need this '='
+    } else {
+      if (state == 2) {  // need another '='
+        while ((ch = *src++) != '\0' && (len_src-- > 0)) {
+          if (!IsSpaceA(ch))
+            break;
+        }
+        if (ch != kPad64)
+          return (-1);
+      }
+      // state = 1 or 2, check if all remain padding is space
+      while ((ch = *src++) != '\0' && (len_src-- > 0)) {
+        if (!IsSpaceA(ch))
+          return(-1);
+      }
+    }
+  } else {
+    // We ended by seeing the end of the string.  Make sure we
+    //      have no partial bytes lying around.  Note that we
+    //      do not require trailing '=', so states 2 and 3 are okay too.
+    if (state == 1)
+      return (-1);
+  }
+
+  return (destidx);
+}
+
+int Base64Unescape(const char *src, int len_src, char *dest, int len_dest) {
+  ASSERT(dest, (L""));
+  ASSERT(src, (L""));
+
+  static const char UnBase64[] = {
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      62/*+*/, 99,      99,      99,      63/*/ */,
+     52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
+     60/*8*/, 61/*9*/, 99,      99,      99,      99,      99,      99,
+     99,       0/*A*/,  1/*B*/,  2/*C*/,  3/*D*/,  4/*E*/,  5/*F*/,  6/*G*/,
+      7/*H*/,  8/*I*/,  9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
+     15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
+     23/*X*/, 24/*Y*/, 25/*Z*/, 99,      99,      99,      99,      99,
+     99,      26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
+     33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
+     41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
+     49/*x*/, 50/*y*/, 51/*z*/, 99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99,
+     99,      99,      99,      99,      99,      99,      99,      99
+  };
+
+  // The above array was generated by the following code
+  // #include <sys/time.h>
+  // #include <stdlib.h>
+  // #include <string.h>
+  // main()
+  // {
+  //   static const char Base64[] =
+  //     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  //   char *pos;
+  //   int idx, i, j;
+  //   printf("    ");
+  //   for (i = 0; i < 255; i += 8) {
+  //     for (j = i; j < i + 8; j++) {
+  //       pos = strchr(Base64, j);
+  //       if ((pos == NULL) || (j == 0))
+  //         idx = 99;
+  //       else
+  //         idx = pos - Base64;
+  //       if (idx == 99)
+  //         printf(" %2d,     ", idx);
+  //       else
+  //         printf(" %2d/*%c*/,", idx, j);
+  //     }
+  //     printf("\n    ");
+  //   }
+  // }
+
+  return Base64UnescapeInternal(src, len_src, dest, len_dest, UnBase64);
+}
+
+int WebSafeBase64Unescape(const char *src, int szsrc, char *dest, int szdest) {
+  ASSERT(dest, (L""));
+  ASSERT(src, (L""));
+
+  static const char UnBase64[] = {
+    99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      62/*-*/, 99,      99,
+      52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
+      60/*8*/, 61/*9*/, 99,      99,      99,      99,      99,      99,
+      99,       0/*A*/,  1/*B*/,  2/*C*/,  3/*D*/,  4/*E*/,  5/*F*/,  6/*G*/,
+      7/*H*/,  8/*I*/,  9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
+      15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
+      23/*X*/, 24/*Y*/, 25/*Z*/, 99,      99,      99,      99,      63/*_*/,
+      99,      26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
+      33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
+      41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
+      49/*x*/, 50/*y*/, 51/*z*/, 99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99,
+      99,      99,      99,      99,      99,      99,      99,      99
+  };
+  // The above array was generated by the following code
+  // #include <sys/time.h>
+  // #include <stdlib.h>
+  // #include <string.h>
+  // main()
+  // {
+  //   static const char Base64[] =
+  //     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+  //   char *pos;
+  //   int idx, i, j;
+  //   printf("    ");
+  //   for (i = 0; i < 255; i += 8) {
+  //     for (j = i; j < i + 8; j++) {
+  //       pos = strchr(Base64, j);
+  //       if ((pos == NULL) || (j == 0))
+  //         idx = 99;
+  //       else
+  //         idx = pos - Base64;
+  //       if (idx == 99)
+  //         printf(" %2d,     ", idx);
+  //       else
+  //         printf(" %2d/*%c*/,", idx, j);
+  //     }
+  //     printf("\n    ");
+  //   }
+  // }
+
+  return Base64UnescapeInternal(src, szsrc, dest, szdest, UnBase64);
+}
+
+bool IsHexDigit (WCHAR c) {
+  return (((c >= L'a') && (c <= L'f'))
+     || ((c >= L'A') && (c <= L'F'))
+     || ((c >= L'0') && (c <= L'9')));
+}
+
+int HexDigitToInt (WCHAR c) {
+  return ((c >= L'a') ? ((c - L'a') + 10) :
+          (c >= L'A') ? ((c - L'A') + 10) :
+          (c - L'0'));
+}
+
+// ----------------------------------------------------------------------
+// int QuotedPrintableUnescape()
+//
+// Check out http://www.cis.ohio-state.edu/htbin/rfc/rfc2045.html for
+// more details, only briefly implemented. But from the web...
+// Quoted-printable is an encoding method defined in the MIME
+// standard. It is used primarily to encode 8-bit text (such as text
+// that includes foreign characters) into 7-bit US ASCII, creating a
+// document that is mostly readable by humans, even in its encoded
+// form. All MIME compliant applications can decode quoted-printable
+// text, though they may not necessarily be able to properly display the
+// document as it was originally intended. As quoted-printable encoding
+// is implemented most commonly, printable ASCII characters (values 33
+// through 126, excluding 61), tabs and spaces that do not appear at the
+// end of lines, and end-of-line characters are not encoded. Other
+// characters are represented by an equal sign (=) immediately followed
+// by that character's hexadecimal value. Lines that are longer than 76
+// characters are shortened by line breaks, with the equal sign marking
+// where the breaks occurred.
+//
+// Update: we really want QuotedPrintableUnescape to conform to rfc2047,
+// which expands the q encoding. In particular, it specifices that _'s are
+// to be treated as spaces.
+// ----------------------------------------------------------------------
+int QuotedPrintableUnescape(const WCHAR *source, int slen,
+                            WCHAR *dest, int len_dest) {
+  ASSERT(dest, (L""));
+  ASSERT(source, (L""));
+
+  WCHAR* d = dest;
+  const WCHAR* p = source;
+
+  while (*p != '\0' && p < source+slen && d < dest+len_dest) {
+    switch (*p) {
+      case '=':
+        if (p == source+slen-1) {
+          // End of line, no need to print the =..
+          return (d-dest);
+        }
+        // if its valid, convert to hex and insert
+        if (p < source+slen-2 && IsHexDigit(p[1]) && IsHexDigit(p[2])) {
+          // lint -e{734} Loss of precision
+          *d++ = static_cast<WCHAR>(
+                    HexDigitToInt(p[1]) * 16 + HexDigitToInt(p[2]));
+          p += 3;
+        } else {
+          p++;
+        }
+        break;
+      case '_':   // According to rfc2047, _'s are to be treated as spaces
+        *d++ = ' '; p++;
+        break;
+      default:
+        *d++ = *p++;
+        break;
+    }
+  }
+  return (d-dest);
+}
+
+// TODO(omaha): currently set not to use IsCharUpper because that is relatively slow
+// this is used in the QUIB; consider if we need to use IsCharUpper or a replacement
+bool String_IsUpper(TCHAR c) {
+  return (c >= 'A' && c <= 'Z');
+  // return (IsCharUpper (c));
+}
+
+// Replacement for the CRT toupper(c)
+int String_ToUpper(int c) {
+  // If it's < 128, then convert is ourself, which is far cheaper than the system conversion
+  if (c < 128)
+    return String_ToUpperA(static_cast<char>(c));
+
+  TCHAR * p_c = reinterpret_cast<TCHAR *>(c);
+  int conv_c = reinterpret_cast<int>(::CharUpper(p_c));
+  return conv_c;
+}
+
+// Replacement for the CRT toupper(c)
+char String_ToUpperA(char c) {
+  if (c >= 'a' && c <= 'z') return (c - ('a' - 'A'));
+  return c;
+}
+
+void String_ToLower(TCHAR* str) {
+  ASSERT1(str);
+  ::CharLower(str);
+}
+
+void String_ToUpper(TCHAR* str) {
+  ASSERT1(str);
+  ::CharUpper(str);
+}
+
+// String comparison based on length
+// Replacement for the CRT strncmp(i)
+int String_StrNCmp(const TCHAR * str1, const TCHAR * str2, uint32 len, bool ignore_case) {
+  ASSERT(str2, (L""));
+  ASSERT(str1, (L""));
+
+  TCHAR c1, c2;
+
+  if (len == 0)
+    return 0;
+
+  // compare each char
+  // TODO(omaha): If we use a lot of case sensitive compares consider having 2 loops.
+  do {
+    c1 = *str1++;
+    c2 = *str2++;
+    if (ignore_case) {
+      c1 = (TCHAR)String_ToLowerChar((int)(c1));  // lint !e507  Suspicious truncation
+      c2 = (TCHAR)String_ToLowerChar((int)(c2));  // lint !e507
+    }
+  } while ( (--len) && c1 && (c1 == c2) );
+
+  return (int)(c1 - c2);
+}
+
+// TODO(omaha): Why do we introduce this behaviorial difference?
+// Replacement for strncpy() - except ALWAYS ends string with null
+TCHAR* String_StrNCpy(TCHAR* destination, const TCHAR* source, uint32 len) {
+  ASSERT (source, (L""));
+  ASSERT (destination, (L""));
+
+  TCHAR* result = destination;
+
+  ASSERT (0 != len, (L""));     // Too short a destination for even the null character
+
+  while (*source && len) {
+    *destination++ = *source++;
+    len--;
+  }
+
+  // If we ran out of space, back up one
+  if (0 == len) {
+    destination--;
+  }
+
+  // Null-terminate the string
+  *destination = _T('\0');
+
+  return result;
+}
+
+// check if a string starts with another string
+bool String_StartsWith(const TCHAR *str, const TCHAR *start_str,
+                            bool ignore_case) {
+  ASSERT(start_str, (L""));
+  ASSERT(str, (L""));
+
+  while (0 != *str) {
+    // Check for matching characters
+    TCHAR c1 = *str;
+    TCHAR c2 = *start_str;
+
+    // Reached the end of start_str?
+    if (0 == c2)
+      return true;
+
+    if (ignore_case) {
+      c1 = (TCHAR)String_ToLowerChar((int)(c1));  // lint !e507  Suspicious truncation
+      c2 = (TCHAR)String_ToLowerChar((int)(c2));  // lint !e507  Suspicious truncation
+    }
+
+    if (c1 != c2)
+      return false;
+
+    ++str;
+    ++start_str;
+  }
+
+  // If str is shorter than start_str, no match.  If equal size, match.
+  return 0 == *start_str;
+}
+
+// check if a string starts with another string
+bool String_StartsWithA(const char *str, const char *start_str, bool ignore_case) {
+  ASSERT(start_str, (L""));
+  ASSERT(str, (L""));
+
+  while (0 != *str) {
+    // Check for matching characters
+    char c1 = *str;
+    char c2 = *start_str;
+
+    // Reached the end of start_str?
+    if (0 == c2)
+      return true;
+
+    if (ignore_case) {
+      c1 = String_ToLowerCharAnsi(c1);
+      c2 = String_ToLowerCharAnsi(c2);
+    }
+
+    if (c1 != c2)
+      return false;
+
+    ++str;
+    ++start_str;
+  }
+
+  // If str is shorter than start_str, no match.  If equal size, match.
+  return 0 == *start_str;
+}
+
+// the wrapper version below actually increased code size as of 5/31/04
+// perhaps because the int64 version is larger and in some EXE/DLLs we only need the int32 version
+
+// converts a string to an int
+// Does not check for overflow
+// is the direct int32 version significantly faster for our usage?
+// int32 String_StringToInt(const TCHAR * str) {
+//    ASSERT(str, (L""));
+//    return static_cast<int32>(String_StringToInt64 (str));
+// }
+
+// converts a string to an int
+// Does not check for overflow
+int32 String_StringToInt(const TCHAR * str) {
+  ASSERT(str, (L""));
+
+  int c;              // current char
+  int32 total;         // current total
+  int sign;           // if '-', then negative, otherwise positive
+
+  // remove spaces
+  while ( *str == _T(' '))
+      ++str;
+
+  c = (int)*str++;
+  sign = c;           // save sign indication
+  if (c == _T('-') || c == _T('+'))
+      c = (int)*str++;    // skip sign
+
+  total = 0;
+
+  while ((c = String_CharToDigit(static_cast<TCHAR>(c))) != -1 ) {
+      total = 10 * total + c;     // accumulate digit
+      c = *str++;    // get next char
+  }
+
+  if (sign == '-')
+    return -total;
+  else
+    return total;   // return result, negated if necessary
+}
+
+// converts a string to an int64
+// Does not check for overflow
+int64 String_StringToInt64(const TCHAR * str) {
+  ASSERT(str, (L""));
+
+  int c;  // current char
+  int64 total;  // current total
+  int sign;
+
+  while (*str == ' ') ++str;  // skip space
+
+  c = (int)*str++;
+  sign = c;           /* save sign indication */
+  if (c == '-' || c == '+')
+    c = (int)*str++;
+
+  total = 0;
+
+  while ((c = String_CharToDigit(static_cast<TCHAR>(c))) != -1) {
+    total = 10 * total + c;     /* accumulate digit */
+    c = *str++;    /* get next char */
+  }
+
+  if (sign == '-')
+    return -total;
+  else
+    return total;
+}
+
+// A faster version of the ::CharLower command. We first check if all characters are in low ANSI
+// If so, we can convert it ourselves [which is about 10x faster]
+// Otherwise, ask the system to do it for us.
+TCHAR * String_FastToLower(TCHAR * str) {
+  ASSERT(str, (L""));
+
+  TCHAR * p = str;
+  while (*p) {
+    // If we can't process it ourselves, then do it with the API
+    if (*p > 127)
+      return ::CharLower(str);
+    ++p;
+  }
+
+  // If we're still here, do it ourselves
+  p = str;
+  while (*p) {
+    // Lower case it
+    if (*p >= L'A' && *p <= 'Z')
+      *p |= 0x20;
+    ++p;
+  }
+
+  return str;
+}
+
+// Convert a size_t to a CString
+CString sizet_to_str(const size_t & i) {
+  CString out;
+  out.Format(NOTRANSL(_T("%u")),i);
+  return out;
+}
+
+// Convert an int to a CString
+CString itostr(const int i) {
+  return String_Int64ToString(i, 10);
+}
+
+// Convert a uint to a CString
+CString itostr(const uint32 i) {
+  return String_Int64ToString(i, 10);
+}
+
+// converts an int to a string
+// Does not check for overflow
+CString String_Int64ToString(int64 value, int radix) {
+  ASSERT(radix > 0, (L""));
+
+  // Space big enough for it in binary, plus the sign
+  TCHAR temp[66];
+
+  bool negative = false;
+  if (value < 0) {
+    negative = true;
+    value = -value;
+  }
+
+  int pos = 0;
+
+  // Add digits in reverse order
+  do {
+    TCHAR digit = (TCHAR) (value % radix);
+    if (digit > 9)
+      temp[pos] = L'a' + digit - 10;
+    else
+      temp[pos] = L'0' + digit;
+
+    pos++;
+    value /= radix;
+  } while (value > 0);
+
+  if (negative)
+    temp[pos++] = L'-';
+
+  // Reverse it before making a CString out of it
+  int start = 0, end = pos - 1;
+  while (start < end) {
+    TCHAR t = temp[start];
+    temp[start] = temp[end];
+    temp[end] = t;
+
+    end--;
+    start++;
+  }
+
+  return CString(temp, pos);
+}
+
+// converts an uint64 to a string
+// Does not check for overflow
+CString String_Uint64ToString(uint64 value, int radix) {
+  ASSERT1(radix > 0);
+
+  CString ret;
+
+  const uint32 kMaxUint64Digits = 65;
+
+  // Space big enough for it in binary
+  TCHAR* temp = ret.GetBufferSetLength(kMaxUint64Digits);
+
+  int pos = 0;
+
+  // Add digits in reverse order
+  do {
+    TCHAR digit = static_cast<TCHAR>(value % radix);
+    if (digit > 9) {
+      temp[pos] = _T('a') + digit - 10;
+    } else {
+      temp[pos] = _T('0') + digit;
+    }
+
+    pos++;
+    value /= radix;
+  } while (value > 0 && pos < kMaxUint64Digits);
+
+  ret.ReleaseBuffer(pos);
+
+  // Reverse it before making a CString out of it
+  ret.MakeReverse();
+
+  return ret;
+}
+
+// converts an double to a string specifies the number of digits after
+// the decimal point
+CString String_DoubleToString(double value, int point_digits) {
+  int64 int_val = (int64) value;
+
+  // Deal with integer part
+  CString result(String_Int64ToString(int_val, 10));
+
+  if (point_digits > 0) {
+    result.AppendChar(L'.');
+
+    // get the fp digits
+    double rem_val = value - int_val;
+    if (rem_val < 0)
+      rem_val = -rem_val;
+
+    // multiply w/ the requested number of significant digits
+    // construct the string in place
+    for(int i=0; i<point_digits; i++) {
+      // TODO(omaha): I have seen 1.2 turn into 1.1999999999999, and generate that string.
+      // We should round better. For now, I'll add a quick fix to favor high
+      rem_val += 1e-12;
+      rem_val *= 10;
+      // Get the ones digit
+      int64 int_rem_dig = std::min(10LL, static_cast<int64>(rem_val));
+      result += static_cast<TCHAR>(int_rem_dig + L'0');
+      rem_val = rem_val - int_rem_dig;
+    }
+  }
+
+  return result;
+}
+
+double String_StringToDouble (const TCHAR *s) {
+  ASSERT(s, (L""));
+
+  double value, power;
+  int i = 0, sign;
+
+  while (IsSpaceW(s[i])) i++;
+
+  // get sign
+  sign = (s[i] == '-') ? -1 : 1;
+  if (s[i] == '+' || s[i] == '-') i++;
+
+  for (value = 0.0; s[i] >= '0' && s[i] <= '9'; i++)
+    value = 10.0 * value + (s[i] - '0');
+
+  if (s[i] == '.') i++;
+
+  for (power = 1.0; s[i] >= '0' && s[i] <= '9'; i++) {
+    value = 10.0 * value + (s[i] - '0');
+    power *= 10.0;
+  }
+
+  return sign * value / power;
+}
+
+// Converts a character to a digit
+// if the character is not a digit return -1 (same as CRT)
+int32 String_CharToDigit(const TCHAR c) {
+  return ((c) >= '0' && (c) <= '9' ? (c) - '0' : -1);
+}
+
+bool String_IsDigit (const TCHAR c) {
+  return ((c) >= '0' && (c) <= '9');
+}
+
+TCHAR String_DigitToChar(unsigned int n) {
+  ASSERT1(n < 10);
+  return static_cast<TCHAR>(_T('0') + n % 10);
+}
+
+// Returns true if an identifier character: letter, digit, or "_"
+bool String_IsIdentifierChar(const TCHAR c) {
+  return ((c >= _T('A') && c <= _T('Z')) ||
+          (c >= _T('a') && c <= _T('z')) ||
+          (c >= _T('0') && c <= _T('9')) ||
+          c == _T('_'));
+}
+
+// Returns true if the string has letters in it.
+// This is used by the keyword extractor to downweight numbers,
+// IDs (sequences of numbers like social security numbers), etc.
+bool String_HasAlphabetLetters (const TCHAR * str) {
+  ASSERT (str, (L""));
+
+  while (*str != '\0') {
+    // if (iswalpha (*str)) {
+    // Note that IsCharAlpha is slower but we want to avoid the CRT
+    if (IsCharAlpha (*str)) {
+      return true;
+    }
+    ++str;
+  }
+
+  return false;
+}
+
+CString String_LargeIntToApproximateString(uint64 value, bool base_ten, int* power) {
+  uint32 to_one_decimal;
+
+  uint32 gig         = base_ten ? 1000000000 : (1<<30);
+  uint32 gig_div_10  = base_ten ?  100000000 : (1<<30)/10;
+  uint32 meg         = base_ten ?    1000000 : (1<<20);
+  uint32 meg_div_10  = base_ten ?     100000 : (1<<20)/10;
+  uint32 kilo        = base_ten ?       1000 : (1<<10);
+  uint32 kilo_div_10 = base_ten ?        100 : (1<<10)/10;
+
+  if (value >= gig) {
+    if (power) *power = 3;
+    to_one_decimal = static_cast<uint32>(value / gig_div_10);
+  } else if (value >= meg) {
+    if (power) *power = 2;
+    to_one_decimal = static_cast<uint32>(value / meg_div_10);
+  } else if (value >= kilo) {
+    if (power) *power = 1;
+    to_one_decimal = static_cast<uint32>(value / kilo_div_10);
+  } else {
+    if (power) *power = 0;
+    return String_Int64ToString(static_cast<uint32>(value), 10 /*radix*/);
+  }
+
+  uint32 whole_part = to_one_decimal / 10;
+
+  if (whole_part < 10)
+    return Show(0.1 * static_cast<double>(to_one_decimal), 1);
+
+  return String_Int64ToString(whole_part, 10 /*radix*/);
+}
+
+int String_FindString(const TCHAR *s1, const TCHAR *s2) {
+  ASSERT(s2, (L""));
+  ASSERT(s1, (L""));
+
+  // Naive implementation, but still oodles better than ATL's implementation
+  // (which deals with variable character widths---we don't).
+
+  const TCHAR *found = _tcsstr(s1, s2);
+  if (NULL == found)
+    return -1;
+
+  return found - s1;
+}
+
+int String_FindString(const TCHAR *s1, const TCHAR *s2, int start_pos) {
+  ASSERT(s2, (L""));
+  ASSERT(s1, (L""));
+
+  // Naive implementation, but still oodles better than ATL's implementation
+  // (which deals with variable character widths---we don't).
+
+  int skip = start_pos;
+
+  const TCHAR *s = s1;
+  while (skip && *s) {
+    ++s;
+    --skip;
+  }
+  if (!(*s))
+    return -1;
+
+  const TCHAR *found = _tcsstr(s, s2);
+  if (NULL == found)
+    return -1;
+
+  return found - s1;
+}
+
+int String_FindChar(const TCHAR *str, const TCHAR c) {
+  ASSERT (str, (L""));
+  const TCHAR *s = str;
+  while (*s) {
+    if (*s == c)
+      return s - str;
+    ++s;
+  }
+
+  return -1;
+}
+
+// taken from wcsrchr, modified to behave in the CString way
+int String_ReverseFindChar(const TCHAR * str,TCHAR c) {
+  ASSERT (str, (L""));
+  TCHAR *start = (TCHAR *)str;
+
+  while (*str++)                       /* find end of string */
+    ;
+  /* search towards front */
+  while (--str != start && *str != (TCHAR)c)
+    ;
+
+  if (*str == (TCHAR)c)             /* found ? */
+    return( str - start );
+
+  return -1;
+}
+
+int String_FindChar(const TCHAR *str, const TCHAR c, int start_pos) {
+  ASSERT (str, (L""));
+  int n = 0;
+  const TCHAR *s = str;
+  while (*s) {
+    if (n++ >= start_pos && *s == c)
+      return s - str;
+    ++s;
+  }
+
+  return -1;
+}
+
+bool String_Contains(const TCHAR *s1, const TCHAR *s2) {
+  ASSERT(s2, (L""));
+  ASSERT(s1, (L""));
+
+  return -1 != String_FindString(s1, s2);
+}
+
+void String_ReplaceChar(TCHAR *str, TCHAR old_char, TCHAR new_char) {
+  ASSERT (str, (L""));
+  while (*str) {
+    if (*str == old_char)
+      *str = new_char;
+
+    ++str;
+  }
+}
+
+void String_ReplaceChar(CString & str, TCHAR old_char, TCHAR new_char) {
+  String_ReplaceChar (str.GetBuffer(), old_char, new_char);
+  str.ReleaseBuffer();
+}
+
+int ReplaceCString (CString & src, const TCHAR *from, const TCHAR *to) {
+  ASSERT(to, (L""));
+  ASSERT(from, (L""));
+
+  return ReplaceCString(src, from, lstrlen(from), to, lstrlen(to), kRepMax);
+}
+
+// A special version of the replace function which takes advantage of CString properties
+// to make it much faster when the string grows
+// 1) It will resize the string in place if possible. Even if it has to 'grow' the string
+// 2) It will cutoff after a maximum number of matches
+// 3) It expects sizing data to be passed to it
+int ReplaceCString (CString & src, const TCHAR *from, unsigned int from_len,
+                                   const TCHAR *to, unsigned int to_len,
+                                   unsigned int max_matches) {
+  ASSERT (from, (L""));
+  ASSERT (to, (L""));
+  ASSERT (from[0] != '\0', (L""));
+  int i = 0, j = 0;
+  unsigned int matches = 0;
+
+  // Keep track of the matches, it's easier than recalculating them
+  unsigned int match_pos_stack[kExpectedMaxReplaceMatches];
+
+  // We might need to dynamically allocate space for the matches
+  bool dynamic_allocate = false;
+  unsigned int * match_pos = (unsigned int*)match_pos_stack;
+  unsigned int max_match_size = kExpectedMaxReplaceMatches;
+
+  // Is the string getting bigger?
+  bool longer = to_len > from_len;
+
+  // don't compute the lengths unless we know we need to
+  int src_len = src.GetLength();
+  int cur_len = src_len;
+
+  // Trick: We temporarily add 1 extra character to the string. The first char from the from
+  // string. This way we can avoid searching for NULL, since we are guaranteed to find it
+  TCHAR * buffer = src.GetBufferSetLength(src_len+1);
+  const TCHAR from_0 = from[0];
+  buffer[src_len] = from[0];
+
+  while (i < cur_len) {
+    // If we have too many matches, then re-allocate to a dynamic buffer that is
+    // twice as big as the one we are currently using
+    if (longer && (matches == max_match_size)) {
+      // Double the buffer size, and copy it over
+      unsigned int * temp = new unsigned int[max_match_size * 2];
+      memcpy(temp, match_pos, matches * sizeof(unsigned int));
+      if (dynamic_allocate)
+        delete [] match_pos;  // lint !e424  Inappropriate deallocation
+      match_pos = temp;
+
+      max_match_size *= 2;
+      dynamic_allocate = true;
+    }
+
+    // If we have the maximum number of matches already, then stop
+    if (matches >= max_matches) {
+      break;
+    }
+
+    // For each potential match
+    // Note: oddly enough, this is the most expensive line in the function under normal usage. So I am optimizing the heck out of it
+    TCHAR * buf_ptr = buffer + i;
+    while (*buf_ptr != from_0) { ++buf_ptr; }
+    i = buf_ptr - buffer;
+
+    // We're done!
+    if (i >= cur_len)
+      break;
+
+    // buffer is not NULL terminated, we replaced the NULL above
+    while (i < cur_len && buffer[i] && buffer[i] == from[j]) {
+      ++i; ++j;
+      if (from[j] == '\0') {  // found match
+
+        if (!longer) {  // modify in place
+
+          memcpy ((byte *)(buffer+i) - (sizeof (TCHAR) * from_len), (byte *)to, sizeof (TCHAR) * to_len);
+          // if there are often a lot of replacements, it would be faster to create a new string instead
+          // of using memmove
+
+          // TODO(omaha): - memmove will cause n^2 behavior in strings with multiple matches since it will be moved many times...
+          if (to_len < from_len) { memmove ((byte *)(buffer+i) - (sizeof (TCHAR) * (from_len - to_len)),
+                                          (byte *)(buffer+i), (src_len - i + 1) * sizeof (TCHAR)); }
+
+          i -= (from_len - to_len);
+          cur_len -= (from_len - to_len);
+        }
+        else
+          match_pos[matches] = i - from_len;
+
+        ++matches;
+
+        break;
+      }
+    }
+
+    j = 0;
+  }
+
+  if (to_len <= from_len)
+    src_len -= matches * (from_len - to_len);
+
+  // if the new string is longer we do another pass now that we know how long the new string needs to be
+  if (matches && to_len > from_len) {
+    src.ReleaseBuffer(src_len);
+
+    int new_len = src_len + matches * (to_len - from_len);
+    buffer = src.GetBufferSetLength(new_len);
+
+    // It's easier to assemble it backwards...
+    int temp_end = new_len;
+    for(i = matches-1; i >= 0; --i) {
+      // Figure out where the trailing portion isthe trailing portion
+      int len = src_len - match_pos[i] - from_len;
+      int start  = match_pos[i] + from_len;
+      int dest   = temp_end - len;
+      memmove(buffer+dest, buffer+start, (len) * sizeof(TCHAR));
+
+      // copy the new item
+      memcpy(buffer + dest - to_len, to, to_len * sizeof(TCHAR));
+
+      // Update the pointers
+      temp_end = dest - to_len;
+      src_len = match_pos[i];
+
+    }
+    src_len = new_len;
+  }
+
+  src.ReleaseBuffer(src_len);
+  if (dynamic_allocate)
+    delete [] match_pos;  // lint !e673  Possibly inappropriate deallocation
+
+  return matches;
+}
+
+/*
+   The following 2 functions will do replacement on TCHAR* directly. They is currently unused.
+   Feel free to put it back if you need to.
+*/
+int ReplaceString (TCHAR *src, const TCHAR *from, const TCHAR *to, TCHAR **out, int *out_len) {
+  ASSERT(out_len, (L""));
+  ASSERT(out, (L""));
+  ASSERT(to, (L""));
+  ASSERT(from, (L""));
+  ASSERT(src, (L""));
+
+  bool created_new_string;
+  int matches = ReplaceStringMaybeInPlace (src, from, to, out, out_len, &created_new_string);
+  if (!created_new_string) {
+      *out = new TCHAR [(*out_len)+1];
+      if (!(*out)) { *out = src; return 0; }
+      _tcscpy_s(*out, *out_len + 1, src);
+  }
+
+  return matches;
+}
+
+int ReplaceStringMaybeInPlace (TCHAR *src, const TCHAR *from, const TCHAR *to, TCHAR **out, int *out_len, bool *created_new_string) {
+  ASSERT (created_new_string, (L""));
+  ASSERT (out_len, (L""));
+  ASSERT (src, (L""));
+  ASSERT (from, (L""));
+  ASSERT (to, (L""));
+  ASSERT (out, (L""));
+  ASSERT (from[0] != '\0', (L""));
+  int i = 0, j = 0;
+  int matches = 0;
+
+  // don't compute the lengths unless we know we need to
+  int from_len = -1, to_len = -1, src_len = -1;
+
+  *created_new_string = false;
+  *out = src;
+
+  while (src[i]) {
+    while (src[i] && src[i] != from[0]) { i++; }
+    while (src[i] && src[i] == from[j]) {
+      i++; j++;
+      if (from[j] == '\0') {  // found match
+        if (from_len == -1) {  // compute lengths if not known
+          from_len = lstrlen (from);
+          to_len = lstrlen (to);
+          src_len = lstrlen (src);
+        }
+
+        matches++;
+
+        if (to_len <= from_len) {  // modify in place
+          memcpy ((byte *)(src+i) - (sizeof (TCHAR) * from_len), (byte *)to, sizeof (TCHAR) * to_len);
+          // if there are often a lot of replacements, it would be faster to create a new string instead
+          // of using memmove
+          if (to_len < from_len) { memmove ((byte *)(src+i) - (sizeof (TCHAR) * (from_len - to_len)),
+                                            (byte *)(src+i), (src_len - i + 1) * sizeof (TCHAR)); }
+          i -= (from_len - to_len);
+        }
+
+        break;
+      }
+    }
+
+    j = 0;
+  }
+
+  *out_len = i;
+
+  // if the new string is longer we do another pass now that we know how long the new string needs to be
+  if (matches && to_len > from_len) {
+      ASSERT (src_len == i, (L""));
+      int new_len = src_len + matches * (to_len - from_len);
+      *out = new TCHAR [new_len+1];
+      if (!(*out)) { *out = src; *out_len = lstrlen (src); return 0; }
+      *created_new_string = true;
+      i = 0; j = 0; int k = 0;
+
+      while (src[i]) {
+          while (src[i] && src[i] != from[0]) {
+              (*out)[k++] = src[i++];
+          }
+          while (src[i] && src[i] == from[j]) {
+              (*out)[k++] = src[i++];
+              j++;
+
+              if (from[j] == '\0') {  // found match
+                  k -= from_len;
+                  ASSERT (k >= 0, (L""));
+                  memcpy ((byte *)((*out)+k), (byte *)to, sizeof (TCHAR) * to_len);
+                  k += to_len;
+                  break;
+              }
+          }
+
+          j = 0;
+      }
+
+      (*out)[k] = '\0';
+      ASSERT (k == new_len, (L""));
+      *out_len = new_len;
+  }
+
+  return matches;
+}
+
+/****************************************************************************
+* wcstol, wcstoul(nptr,endptr,ibase) - Convert ascii string to long un/signed int.
+*
+* modified from:
+*
+* wcstol.c - Contains C runtimes wcstol and wcstoul
+*
+*       Copyright (c) Microsoft Corporation. All rights reserved.
+*
+*   Purpose:
+*       Convert an ascii string to a long 32-bit value.  The base
+*       used for the caculations is supplied by the caller.  The base
+*       must be in the range 0, 2-36.  If a base of 0 is supplied, the
+*       ascii string must be examined to determine the base of the
+*       number:
+*           (a) First char = '0', second char = 'x' or 'X',
+*               use base 16.
+*           (b) First char = '0', use base 8
+*           (c) First char in range '1' - '9', use base 10.
+*
+*       If the 'endptr' value is non-NULL, then wcstol/wcstoul places
+*       a pointer to the terminating character in this value.
+*       See ANSI standard for details
+*
+*Entry:
+*       nptr == NEAR/FAR pointer to the start of string.
+*       endptr == NEAR/FAR pointer to the end of the string.
+*       ibase == integer base to use for the calculations.
+*
+*       string format: [whitespace] [sign] [0] [x] [digits/letters]
+*
+*Exit:
+*       Good return:
+*           result
+*
+*       Overflow return:
+*           wcstol -- LONG_MAX or LONG_MIN
+*           wcstoul -- ULONG_MAX
+*           wcstol/wcstoul -- errno == ERANGE
+*
+*       No digits or bad base return:
+*           0
+*           endptr = nptr*
+*
+*Exceptions:
+*       None.
+*
+*******************************************************************************/
+
+// flag values */
+#define kFlUnsigned   (1)       // wcstoul called */
+#define kFlNeg        (2)       // negative sign found */
+#define kFlOverflow   (4)       // overflow occured */
+#define kFlReaddigit  (8)       // we've read at least one correct digit */
+
+static unsigned long __cdecl wcstoxl (const wchar_t *nptr, wchar_t **endptr, int ibase, int flags) {
+  ASSERT(nptr, (L""));
+
+  const wchar_t *p;
+  wchar_t c;
+  unsigned long number;
+  unsigned digval;
+  unsigned long maxval;
+  // #ifdef _MT
+  // pthreadlocinfo ptloci = _getptd()->ptlocinfo;
+
+  // if ( ptloci != __ptlocinfo )
+  //    ptloci = __updatetlocinfo();
+  // #endif  // _MT */
+
+  p = nptr;           // p is our scanning pointer */
+  number = 0;         // start with zero */
+
+  c = *p++;           // read char */
+
+  // #ifdef _MT
+  //        while ( __iswspace_mt(ptloci, c) )
+  // #else  // _MT */
+  while (c == ' ')
+  //        while ( iswspace(c) )
+  // #endif  // _MT */
+      c = *p++;       // skip whitespace */
+
+  if (c == '-') {
+      flags |= kFlNeg;    // remember minus sign */
+      c = *p++;
+  }
+  else if (c == '+')
+      c = *p++;       // skip sign */
+
+  if (ibase < 0 || ibase == 1 || ibase > 36) {
+      // bad base! */
+      if (endptr)
+          // store beginning of string in endptr */
+          *endptr = const_cast<wchar_t *>(nptr);
+      return 0L;      // return 0 */
+  }
+  else if (ibase == 0) {
+      // determine base free-lance, based on first two chars of
+      // string */
+      if (String_CharToDigit(c) != 0)
+          ibase = 10;
+      else if (*p == L'x' || *p == L'X')
+          ibase = 16;
+      else
+          ibase = 8;
+  }
+
+  if (ibase == 16) {
+      // we might have 0x in front of number; remove if there */
+      if (String_CharToDigit(c) == 0 && (*p == L'x' || *p == L'X')) {
+          ++p;
+          c = *p++;   // advance past prefix */
+      }
+  }
+
+  // if our number exceeds this, we will overflow on multiply */
+  maxval = ULONG_MAX / ibase;
+
+  for (;;) {  // exit in middle of loop */
+
+      // convert c to value */
+      if ( (digval = String_CharToDigit(c)) != (unsigned) -1 )
+          ;
+      else if (c >= 'A' && c <= 'F') { digval = c - 'A' + 10; }
+      else if (c >= 'a' && c <= 'f') { digval = c - 'a' + 10; }
+      // else if ( __ascii_iswalpha(c))
+      //     digval = __ascii_towupper(c) - L'A' + 10;
+      else
+          break;
+
+      if (digval >= (unsigned)ibase)
+          break;      // exit loop if bad digit found */
+
+      // record the fact we have read one digit */
+      flags |= kFlReaddigit;
+
+      // we now need to compute number = number * base + digval,
+      // but we need to know if overflow occured.  This requires
+      // a tricky pre-check. */
+
+      if (number < maxval || (number == maxval &&
+      (unsigned long)digval <= ULONG_MAX % ibase)) {
+          // we won't overflow, go ahead and multiply */
+          number = number * ibase + digval;
+      }
+      else {
+          // we would have overflowed -- set the overflow flag */
+          flags |= kFlOverflow;
+      }
+
+      c = *p++;       // read next digit */
+  }
+
+  --p;                // point to place that stopped scan */
+
+  if (!(flags & kFlReaddigit)) {
+      // no number there; return 0 and point to beginning of string */
+      if (endptr)
+          // store beginning of string in endptr later on */
+          p = nptr;
+      number = 0L;        // return 0 */
+  }
+  // lint -save -e648 -e650  Overflow in -LONG_MIN
+#pragma warning(push)
+// C4287 : unsigned/negative constant mismatch.
+// The offending expression is number > -LONG_MIN. -LONG_MIN overflows and
+// technically -LONG_MIN == LONG_MIN == 0x80000000. It should actually
+// result in a compiler warning, such as C4307: integral constant overflow.
+// Anyway, in the expression (number > -LONG_MIN) the right operand is converted
+// to unsigned long, so the expression is actually evaluated as
+// number > 0x80000000UL. The code is probably correct but subtle, to say the
+// least.
+#pragma warning(disable : 4287)
+  else if ( (flags & kFlOverflow) ||
+        ( !(flags & kFlUnsigned) &&
+          ( ( (flags & kFlNeg) && (number > -LONG_MIN) ) ||
+            ( !(flags & kFlNeg) && (number > LONG_MAX) ) ) ) )
+  {
+      // overflow or signed overflow occurred */
+      // errno = ERANGE;
+      if ( flags & kFlUnsigned )
+          number = ULONG_MAX;
+      else if ( flags & kFlNeg )
+        // lint -e{648, 650}  Overflow in -LONG_MIN
+        number = (unsigned long)(-LONG_MIN);
+      else
+        number = LONG_MAX;
+  }
+#pragma warning(pop)
+  // lint -restore
+
+  if (endptr != NULL)
+      // store pointer to char that stopped the scan */
+      *endptr = const_cast<wchar_t *>(p);
+
+  if (flags & kFlNeg)
+      // negate result if there was a neg sign */
+      number = (unsigned long)(-(long)number);
+
+  return number;          // done. */
+}
+
+long __cdecl Wcstol (const wchar_t *nptr, wchar_t **endptr, int ibase) {
+  ASSERT(endptr, (L""));
+  ASSERT(nptr, (L""));
+
+  return (long) wcstoxl(nptr, endptr, ibase, 0);
+}
+
+unsigned long __cdecl Wcstoul (const wchar_t *nptr, wchar_t **endptr, int ibase) {
+  // endptr may be NULL
+  ASSERT(nptr, (L""));
+
+  return wcstoxl(nptr, endptr, ibase, kFlUnsigned);
+}
+
+// Functions on arrays of strings
+
+// Returns true iff s is in the array strings (case-insensitive compare)
+bool String_MemberOf(const TCHAR* const* strings, const TCHAR* s) {
+  ASSERT(s, (L""));
+  // strings may be NULL
+
+  const int s_length = lstrlen(s);
+  if (strings == NULL)
+    return false;
+  for (; *strings != NULL; strings++) {
+    if (0 == String_StrNCmp(*strings, s, s_length, true)) {
+      return true;      // Found equal string
+    }
+  }
+  return false;
+}
+
+// Returns index of s in the array of strings (or -1 for missing) (case-insensitive compare)
+int String_IndexOf(const TCHAR* const* strings, const TCHAR* s) {
+  ASSERT(s, (L""));
+  // strings may be NULL
+
+  const int s_length = lstrlen(s);
+  if (strings == NULL)
+    return -1;
+  for (int i = 0; *strings != NULL; i++, strings++) {
+    if (0 == String_StrNCmp(*strings, s, s_length, true)) {
+      return i;      // Found equal string
+    }
+  }
+  return -1;
+}
+
+// The internal format is a int64.
+time64 StringToTime(const CString & time) {
+  return static_cast<time64>(String_StringToInt64(time));
+}
+
+// See above comment from StringToTime.
+// Just show it as a INT64 for now
+// NOTE: this will truncating it to INT64, which may lop off some times in the future
+CString TimeToString(const time64 & time) {
+  return String_Int64ToString(static_cast<int64>(time), 10);
+}
+
+const TCHAR *FindStringASpaceStringB (const TCHAR *s, const TCHAR *a, const TCHAR *b) {
+  ASSERT(s, (L""));
+  ASSERT(a, (L""));
+  ASSERT(b, (L""));
+
+  const TCHAR *search_from = s;
+  const TCHAR *pos;
+  while (*search_from && (pos = stristrW (search_from, a)) != NULL) {
+      const TCHAR *start = pos;
+      pos += lstrlen(a);
+      search_from = pos;
+      while (*pos == ' ' || *pos == '\t') pos++;
+      if (!String_StrNCmp (pos, b, lstrlen(b), true)) return start;
+  }
+
+  return 0;
+}
+
+bool IsAlphaA (const char c) {
+  return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
+}
+
+bool IsDigitA (const char c) {
+  return (c >= '0' && c <= '9');
+}
+
+void SafeStrCat (TCHAR *dest, const TCHAR *src, int dest_buffer_len) {
+  _tcscat_s(dest, dest_buffer_len, src);
+}
+
+// extracts next float in a string
+// skips any non-digit characters
+// return position after end of float
+const TCHAR *ExtractNextDouble (const TCHAR *s, double *f) {
+  ASSERT (f, (L""));
+  ASSERT (s, (L""));
+
+  CString num;
+  while (*s && !String_IsDigit (*s)) s++;
+  while (*s && (*s == '.' || String_IsDigit (*s))) { num += *s; s++; }
+  ASSERT (num.GetLength(), (L""));
+  *f = String_StringToDouble (num);
+  return s;
+}
+
+TCHAR *String_PathFindExtension(const TCHAR *path) {
+  ASSERT(path, (L""));
+
+  // Documentation says PathFindExtension string must be of max length
+  // MAX_PATH but a trusted tester hit the ASSERT and we don't really
+  // need it here, so commented out. We can't address where it is
+  // called because it's called from ATL code.
+  // ASSERT(lstrlen(path)<=MAX_PATH, (L""));
+
+  // point to terminating NULL
+  const TCHAR *ret = path + lstrlen(path);
+  const TCHAR *pos = ret;
+
+  while (--pos >= path) {
+    if (*pos == '.')
+      return const_cast<TCHAR *>(pos);
+  }
+
+  return const_cast<TCHAR *>(ret);
+}
+
+char String_ToLowerCharAnsi(char c) {
+  if (c >= 'A' && c <= 'Z') return (c + ('a' - 'A'));
+  return c;
+}
+
+int String_ToLowerChar(int c) {
+  // If it's < 128, then convert is ourself, which is far cheaper than the system conversion
+  if (c < 128)
+    return String_ToLowerCharAnsi(static_cast<char>(c));
+
+  return Char_ToLower(static_cast<TCHAR>(c));
+}
+
+
+bool String_PathRemoveFileSpec(TCHAR *path) {
+  ASSERT (path, (L""));
+
+  int len, pos;
+  len = pos = lstrlen (path);
+
+  // You might think that the SHLWAPI API does not change "c:\windows" -> "c:\"
+  // when c:\windows is a directory, but it does.
+
+  // If we don't want to match this weird API we can use the following to check
+  // for directories:
+
+  // Check if we are already a directory.
+  WIN32_FILE_ATTRIBUTE_DATA attrs;
+  // Failure (if file does not exist) is OK.
+  BOOL success = GetFileAttributesEx(path, GetFileExInfoStandard, &attrs);
+  UTIL_LOG(L4, (_T("[String_PathRemoveFileSpec][path %s][success %d][dir %d]"),
+                path,
+                success,
+                attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
+  if (success && (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+    // Remove trailing backslash, if any.
+    if (path[pos-1] == '\\')
+      path[pos-1] = '\0';
+    return 1;
+  }
+
+  // Find last backslash.
+  while (pos && path[pos] != '\\') pos--;
+  if (!pos && path[pos] != '\\') return 0;
+
+  ASSERT (pos < len, (L""));
+
+  // The documentation says it removes backslash but it doesn't for c:\.
+  if (!pos || path[pos-1] == ':' || (pos == 1 && path[0] == '\\'))
+    // Keep the backslash in this case.
+    path[pos+1] = '\0';
+  else
+    path[pos] = '\0';
+
+  return 1;
+}
+
+void String_EndWithChar(TCHAR *str, TCHAR c) {
+  ASSERT (str, (L""));
+  int len = lstrlen(str);
+  if (len == 0 || str[len - 1] != c) {
+    str[len] = c;
+    str[len + 1] = 0;
+  }
+}
+
+bool StartsWithBOM(const TCHAR* string) {
+  ASSERT(string, (L""));
+  wchar_t c = string[0];
+  if (c == 0xFFFE || c == 0xFEFF)
+    return true;
+  else
+    return false;
+}
+
+const TCHAR* StringAfterBOM(const TCHAR* string) {
+  ASSERT(string, (L""));
+  return &string[StartsWithBOM(string) ? 1 : 0];
+}
+
+bool String_StringToDecimalIntChecked(const TCHAR* str, int* value) {
+  ASSERT1(str);
+  ASSERT1(value);
+
+  if (_set_errno(0)) {
+    return false;
+  }
+
+  TCHAR* end_ptr = NULL;
+  *value = _tcstol(str, &end_ptr, 10);
+  ASSERT1(end_ptr);
+
+  if (errno) {
+    ASSERT1(ERANGE == errno);
+    // Overflow or underflow.
+    return false;
+  } else if (*value == 0) {
+    // The value returned could be an error code. tcsltol returns
+    // zero when it cannot convert the string. However we need to
+    // distinguish a real zero. Thus check to see if end_ptr is not the start
+    // of the string (str is not an empty string) and is pointing to a '\0'.
+    // If not, we have an error.
+    if ((str == end_ptr) || (*end_ptr != '\0')) {
+      return false;
+    }
+  } else if (*end_ptr != '\0') {
+    // The end_ptr is pointing at a character that is
+    // not the end of the string. Only part of the string could be converted.
+    return false;
+  }
+
+  return true;
+}
+
+bool CLSIDToCString(const GUID& guid, CString* str) {
+  ASSERT(str, (L""));
+
+  LPOLESTR string_guid = NULL;
+  if (::StringFromCLSID(guid, &string_guid) != S_OK) {
+    return false;
+  }
+  *str = string_guid;
+  ::CoTaskMemFree(string_guid);
+
+  return true;
+}
+
+HRESULT String_StringToBool(const TCHAR* str, bool* value) {
+  ASSERT1(str);
+  ASSERT1(value);
+
+  // This method now performs a case-insentitive
+  // culture aware compare. We should however be ok as we are only comparing
+  // latin characters.
+  if (_tcsicmp(kFalse, str) == 0) {
+    *value = false;
+  } else if (_tcsicmp(kTrue, str) == 0) {
+    *value = true;
+  } else {
+    // we found another string. should error out.
+    return E_FAIL;
+  }
+  return S_OK;
+}
+
+HRESULT String_BoolToString(bool value, CString* string) {
+  ASSERT1(string);
+  *string = value ? kTrue : kFalse;
+  return S_OK;
+}
+
+CString String_ReplaceIgnoreCase(const CString& string,
+                                 const CString& token,
+                                 const CString& replacement) {
+  int token_length = token.GetLength();
+  if (!token_length) {
+    return string;
+  }
+
+  CString string_lowercase(string);
+  CString token_lowercase(token);
+  string_lowercase.MakeLower();
+  token_lowercase.MakeLower();
+
+  CString output(string);
+  int replacement_length = replacement.GetLength();
+
+  int index = 0;
+  int output_index = 0;
+
+  for (int new_index = 0;
+       (new_index = string_lowercase.Find(token_lowercase, index)) != -1;
+       index = new_index + token_length) {
+    output_index += new_index - index;
+    output.Delete(output_index, token_length);
+    output.Insert(output_index, replacement);
+    output_index += replacement_length;
+  }
+
+  return output;
+}
+
+// Escape and unescape strings (shlwapi-based implementation).
+// The intended usage for these APIs is escaping strings to make up
+// URLs, for example building query strings.
+//
+// Pass false to the flag segment_only to escape the url. This will not
+// cause the conversion of the # (%23), ? (%3F), and / (%2F) characters.
+
+// Characters that must be encoded include any characters that have no
+// corresponding graphic character in the US-ASCII coded character
+// set (hexadecimal 80-FF, which are not used in the US-ASCII coded character
+// set, and hexadecimal 00-1F and 7F, which are control characters),
+// blank spaces, "%" (which is used to encode other characters),
+// and unsafe characters (<, >, ", #, {, }, |, \, ^, ~, [, ], and ').
+//
+// The input and output strings can't be longer than INTERNET_MAX_URL_LENGTH
+
+HRESULT StringEscape(const CString& str_in,
+                     bool segment_only,
+                     CString* str_out) {
+  ASSERT1(str_out);
+  ASSERT1(str_in.GetLength() < INTERNET_MAX_URL_LENGTH);
+
+  DWORD buf_len = INTERNET_MAX_URL_LENGTH + 1;
+  HRESULT hr = ::UrlEscape(str_in, str_out->GetBufferSetLength(buf_len), &buf_len,
+    segment_only ? URL_ESCAPE_PERCENT | URL_ESCAPE_SEGMENT_ONLY : URL_ESCAPE_PERCENT);
+  if (SUCCEEDED(hr)) {
+    str_out->ReleaseBuffer();
+    ASSERT1(buf_len <= INTERNET_MAX_URL_LENGTH);
+  }
+  return hr;
+}
+
+HRESULT StringUnescape(const CString& str_in, CString* str_out) {
+  ASSERT1(str_out);
+  ASSERT1(str_in.GetLength() < INTERNET_MAX_URL_LENGTH);
+
+  DWORD buf_len = INTERNET_MAX_URL_LENGTH + 1;
+  HRESULT hr = ::UrlUnescape(const_cast<TCHAR*>(str_in.GetString()),
+    str_out->GetBufferSetLength(buf_len), &buf_len, 0);
+  if (SUCCEEDED(hr)) {
+    str_out->ReleaseBuffer(buf_len + 1);
+    ASSERT1(buf_len <= INTERNET_MAX_URL_LENGTH);
+  }
+  return hr;
+}
+
+bool String_StringToTristate(const TCHAR* str, Tristate* value) {
+  ASSERT1(str);
+  ASSERT1(value);
+
+  int numerical_value = 0;
+  if (!String_StringToDecimalIntChecked(str, &numerical_value)) {
+    return false;
+  }
+
+  switch (numerical_value) {
+    case 0:
+      *value = TRISTATE_FALSE;
+      break;
+    case 1:
+      *value = TRISTATE_TRUE;
+      break;
+    case 2:
+      *value = TRISTATE_NONE;
+      break;
+    default:
+      return false;
+  }
+
+  return true;
+}
+
+// Extracts the name and value from a string that contains a name/value pair.
+bool ParseNameValuePair(const CString& token,
+                        TCHAR separator,
+                        CString* name,
+                        CString* value) {
+  ASSERT1(name);
+  ASSERT1(value);
+
+  int separator_index = token.Find(separator);
+  if ((separator_index == -1) ||  // Not a name-value pair.
+      (separator_index == 0) ||  // No name was supplied.
+      (separator_index == (token.GetLength() - 1))) {  // No value was supplied.
+    return false;
+  }
+
+  *name = token.Left(separator_index);
+  *value = token.Right(token.GetLength() - separator_index - 1);
+
+  ASSERT1(token.GetLength() == name->GetLength() + value->GetLength() + 1);
+
+  // It's not possible for the name to contain the separator.
+  ASSERT1(-1 == name->Find(separator));
+  if (-1 != value->Find(separator)) {
+    // The value contains the separator.
+    return false;
+  }
+
+  return true;
+}
+
+bool SplitCommandLineInPlace(TCHAR *command_line,
+                             TCHAR **first_argument_parameter,
+                             TCHAR **remaining_arguments_parameter) {
+  if (!command_line ||
+      !first_argument_parameter ||
+      !remaining_arguments_parameter) {
+    return false;
+  }
+
+  TCHAR end_char;
+  TCHAR *&first_argument = *first_argument_parameter;
+  TCHAR *&remaining_arguments = *remaining_arguments_parameter;
+  if (_T('\"') == *command_line) {
+    end_char = _T('\"');
+    first_argument = remaining_arguments = command_line + 1;
+  } else {
+    end_char = _T(' ');
+    first_argument = remaining_arguments = command_line;
+  }
+  // Search for the end of the first argument
+  while (end_char != *remaining_arguments && '\0' != *remaining_arguments) {
+    ++remaining_arguments;
+  }
+  if (end_char == *remaining_arguments) {
+    *remaining_arguments = '\0';
+    do {
+      // Skip the spaces between the first argument and the remaining arguments.
+      ++remaining_arguments;
+    } while (_T(' ') == *remaining_arguments);
+  }
+  return true;
+}
+
+bool ContainsOnlyAsciiChars(const CString& str) {
+  for (int i = 0; i < str.GetLength(); ++i) {
+    if (str[i] > 0x7F) {
+      return false;
+    }
+  }
+  return true;
+}
+CString BytesToHex(const uint8* bytes, size_t num_bytes) {
+  CString result;
+  if (bytes) {
+    result.Preallocate(num_bytes * sizeof(TCHAR));
+    static const TCHAR* const kHexChars = _T("0123456789abcdef");
+    for (size_t i = 0; i != num_bytes; ++i) {
+      result.AppendChar(kHexChars[(bytes[i] >> 4)]);
+      result.AppendChar(kHexChars[(bytes[i] & 0xf)]);
+    }
+  }
+  return result;
+}
+
+CString BytesToHex(const std::vector<uint8>& bytes) {
+  CString result;
+  if (!bytes.empty()) {
+    result.SetString(BytesToHex(&bytes.front(), bytes.size()));
+  }
+  return result;
+}
+
+void JoinStrings(const std::vector<CString>& components,
+                 const TCHAR* delim,
+                 CString* result) {
+  ASSERT1(result);
+  result->Empty();
+
+  // Compute length so we can reserve memory.
+  size_t length = 0;
+  size_t delim_length = delim ? _tcslen(delim) : 0;
+  for (size_t i = 0; i != components.size(); ++i) {
+    if (i != 0) {
+      length += delim_length;
+    }
+    length += components[i].GetLength();
+  }
+
+  result->Preallocate(length);
+
+  for (size_t i = 0; i != components.size(); ++i) {
+    if (i != 0 && delim) {
+      result->Append(delim, delim_length);
+    }
+    result->Append(components[i]);
+  }
+}
+
+void JoinStringsInArray(const TCHAR* components[],
+                        int num_components,
+                        const TCHAR* delim,
+                        CString* result) {
+  ASSERT1(result);
+  result->Empty();
+
+  for (int i = 0; i != num_components; ++i) {
+    if (i != 0 && delim) {
+      result->Append(delim);
+    }
+    if (components[i]) {
+      result->Append(components[i]);
+    }
+  }
+}
+
+CString FormatResourceMessage(uint32 resource_id, ...) {
+  CString format;
+  const bool is_loaded = !!format.LoadString(resource_id);
+
+  if (!is_loaded) {
+    return CString();
+  }
+
+  va_list arg_list;
+  va_start(arg_list, resource_id);
+
+  CString formatted;
+  formatted.FormatMessageV(format, &arg_list);
+
+  va_end(arg_list);
+
+  return formatted;
+}
+
+CString FormatErrorCode(DWORD error_code) {
+  CString error_code_string;
+  if (FAILED(error_code)) {
+    error_code_string.Format(_T("0x%08x"), error_code);
+  } else {
+    error_code_string.Format(_T("%u"), error_code);
+  }
+  return error_code_string;
+}
+
+HRESULT WideStringToUtf8UrlEncodedString(const CString& str, CString* out) {
+  ASSERT1(out);
+
+  out->Empty();
+  if (str.IsEmpty()) {
+    return S_OK;
+  }
+
+  // Utf8 encode the Utf16 string first. Next urlencode it.
+  CStringA utf8str = WideToUtf8(str);
+  ASSERT1(!utf8str.IsEmpty());
+  DWORD buf_len = INTERNET_MAX_URL_LENGTH;
+  CStringA escaped_utf8_name;
+  HRESULT hr = ::UrlEscapeA(utf8str,
+                            CStrBufA(escaped_utf8_name, buf_len),
+                            &buf_len,
+                            0);
+  ASSERT1(buf_len <= INTERNET_MAX_URL_LENGTH);
+  ASSERT1(escaped_utf8_name.GetLength() == static_cast<int>(buf_len));
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[UrlEscapeA failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  *out = CString(escaped_utf8_name);
+  return S_OK;
+}
+
+HRESULT Utf8UrlEncodedStringToWideString(const CString& str, CString* out) {
+  ASSERT1(out);
+
+  out->Empty();
+  if (str.IsEmpty()) {
+    return S_OK;
+  }
+
+  // The value is a utf8 encoded url escaped string that is stored as a
+  // unicode string. Because of this, it should contain only ascii chars.
+  if (!ContainsOnlyAsciiChars(str)) {
+    UTIL_LOG(LE, (_T("[String contains non ascii chars]")));
+    return E_INVALIDARG;
+  }
+
+  CStringA escaped_utf8_val = WideToAnsiDirect(str);
+  DWORD buf_len = INTERNET_MAX_URL_LENGTH;
+  CStringA unescaped_val;
+  HRESULT hr = ::UrlUnescapeA(const_cast<char*>(escaped_utf8_val.GetString()),
+                              CStrBufA(unescaped_val, buf_len),
+                              &buf_len,
+                              0);
+  ASSERT1(unescaped_val.GetLength() == static_cast<int>(buf_len));
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[UrlUnescapeA failed][0x%08x]"), hr));
+    return hr;
+  }
+  ASSERT1(buf_len == static_cast<DWORD>(unescaped_val.GetLength()));
+  ASSERT1(buf_len <= INTERNET_MAX_URL_LENGTH);
+  CString app_name = Utf8ToWideChar(unescaped_val,
+                                    unescaped_val.GetLength());
+  if (app_name.IsEmpty()) {
+    return E_INVALIDARG;
+  }
+
+  *out = app_name;
+  return S_OK;
+}
+
+}  // namespace omaha
+
diff --git a/base/string.h b/base/string.h
new file mode 100644
index 0000000..7706341
--- /dev/null
+++ b/base/string.h
@@ -0,0 +1,542 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_BASE_STRING_H_
+#define OMAHA_BASE_STRING_H_
+
+#include <windows.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+
+namespace omaha {
+
+#define STR_SIZE(str) (arraysize(str)-1)  // number of characters in char array (only for single-byte string literals!!!)
+#define TSTR_SIZE(tstr) (arraysize(tstr)-1)  // like STR_SIZE but works on _T("string literal") ONLY!!!
+
+#define kEllipsis L".."
+
+// The number of replacements matches we expect, before we start allocating extra memory
+// to process it. This is an optimizing constant
+#define kExpectedMaxReplaceMatches 100
+
+// TODO(omaha): above each of these function names, we should
+// define what we expect the implementation to do. that way,
+// implementers will know what is desired. an example would probably
+// make things easiest.
+CString AbbreviateString (const CString & title, int32 max_len);
+CString AbbreviateUri (const CString & uri, int32 max_len);
+CString NormalizeUri (const CString & uri);
+
+// removes "http://", "ftp://", "mailto:" or "file://" (note that the "file" protocol is
+// like: "file:///~/calendar", this method removes only the first two slashes
+CString RemoveInternetProtocolHeader (const CString& url);
+
+// Converts a file:// URI to a valid Windows path.
+HRESULT ConvertFileUriToLocalPath(const CString& uri, CString* path_out);
+
+void RemoveFromStart (CString & s, const TCHAR* remove, bool ignore_case);
+void RemoveFromEnd (CString & s, const TCHAR* remove);
+
+// Limit string to max length, truncating and adding ellipsis if needed
+// Attempts to not leave a partial word at the end, unless min_len is reached
+CString ElideIfNeeded (const CString & input_string, int max_len, int min_len);
+
+// The ability to clean up a string for relevant target audiences. Add flags accordingly
+
+// Sanitizes for insertion in an HTML document, uses the basic literals [<>&]
+#define kSanHtml 0x1
+
+// XML is the HTML replacements, and a few more
+#define kSanXml (kSanHtml | 0x2)
+
+// Javascript has a seperate set of encodings [which is a superset of HTML replacements]
+#define kSanJs (kSanHtml | 0x4)
+
+// For input fields on HTML documents
+#define kSanHtmlInput 0x8
+
+// TODO(omaha): be consistent on use of int/uint32/int32 for lengths
+
+// The input length of the string does not include the null terminator.
+// Caller deletes the returned buffer.
+WCHAR *ToWide (const char *s, int len);
+
+// returns pointer to data if found otherwise NULL
+const byte *BufferContains (const byte *buf, uint32 buf_len, const byte *data, uint32 data_len);
+
+// Given a string, 'protect' the characters that are invalid for a given mode
+// For instance, kSanHtml will replace < with the HTML literal equivalent
+// If kSanHtml is used, and bold_periods is true, then periods used for url abbreviation are bolded.
+// NOTE: If you call AbbreviateLinkForDisplay before this function, then there might be periods
+// used for abbreviation.  BoldAbbreviationPeriods should be called after HighlightTerms.
+CString SanitizeString(const CString & in, DWORD mode);
+
+// Bolds the periods used for abbreviation.  Call this after HighlightTerms.
+CString BoldAbbreviationPeriods(const CString & in);
+
+// Unencode a URL encoded string
+CString Unencode(const CString & input);
+
+CString GetTextInbetween(const CString &input, const CString &start, const CString &end);
+
+// Given a ? seperated string, extract a particular segment, and URL-Unencode it
+CString GetParam(const CString & input, const CString & key);
+
+// Given an XML style string, extract the contents of a <INPUT>...</INPUT> pair
+CString GetField (const CString & input, const CString & field);
+
+// Finds a whole word match in the query, followed by a ":".
+// If not found, return -1.
+//
+// Note: this is case sensitive.
+int FindWholeWordMatch (const CString &query,
+  const CString &word_to_match,
+  const bool end_with_colon,
+  const int index_begin);
+
+// Do whole-word replacement in "str".
+// This does not do partial matches (unlike CString::Replace),
+//   e.g.  CString::Replace will replace "ie" within "pie" and
+// this function will not.
+//
+// Note: this is case sensitive.
+void ReplaceWholeWord (const CString &string_to_replace,
+  const CString &replacement,
+  const bool trim_whitespace,
+  CString *str);
+
+// Convert Wide to ANSI directly. Use only when it is all ANSI
+CStringA WideToAnsiDirect(const CString & in);
+
+// Transform a unicode string into UTF8, used primarily by the webserver
+CStringA WideToUtf8(const CString& w);
+
+// Converts the UTF-8 encoded buffer to an in-memory Unicode (wide character)
+// string.
+// @param utf8 A non-NULL pointer to a UTF-8 encoded buffer that has at
+// least num_bytes valid characters.
+// @param num_bytes Number of bytes to process from utf8.
+// @return The Unicode string represented by utf8 (or that part of it
+// specified by num_bytes).  If the UTF-8 representation of the string started
+// with a byte-order marker (BOM), it will be ignored and not included in the
+// returned string.  On failure, the function returns the empty string.
+CString Utf8ToWideChar(const char* utf8, uint32 num_bytes);
+CString Utf8BufferToWideChar(const std::vector<uint8>& buffer);
+
+// Dealing with Unicode BOM
+bool StartsWithBOM(const TCHAR* string);
+const TCHAR* StringAfterBOM(const TCHAR* string);
+
+// Convert an ANSI string into Widechar string, according to the specified
+// codepage. The input length can be -1, if the string is null terminated, and
+// the actual length will be used internally.
+BOOL AnsiToWideString(const char *from, int length, UINT codepage, CString *to);
+
+// Convert char to Wchar directly
+CString AnsiToWideString(const char *from, int length);
+
+// these functions untested
+// they should not be used unless tested
+// HRESULT AnsiToUTF8 (char * src, int src_len, char * dest, int *dest_len);
+// HRESULT UTF8ToAnsi (char * src, int src_len, char * dest, int *dest_len);
+// HRESULT UCS2ToUTF8 (LPCWSTR src, int src_len, char * dest, int *dest_len);
+// HRESULT UTF8ToUCS2 (char * src, int src_len, LPWSTR dest, int *dest_len);
+
+// "Absolute" is perhaps not the right term, this normalizes the Uri
+// given http://www.google.com changes to correct http://www.google.com/
+// given http://www.google.com// changes to correct http://www.google.com/
+// given http://www.google.com/home.html returns the same
+CString GetAbsoluteUri(const CString& uri);
+
+// Reverse (big-endian<->little-endian) the shorts that make up
+// Unicode characters in a byte array of Unicode chars
+HRESULT ReverseUnicodeByteOrder(byte* unicode_string, int size_in_bytes);
+
+// given http://google.com/bobby this returns http://google.com/
+// If strip_leading is specified, it will turn
+// http://www.google.com into http://google.com
+#define kStrLeadingWww _T("www.")
+// TODO(omaha): no default parameters
+CString GetUriHostName(const CString& uri, bool strip_leading = false);
+CString GetUriHostNameHostOnly(const CString& uri, bool strip_leading_www);
+
+const char *stristr(const char *string, const char *pattern);
+const WCHAR *stristrW(const WCHAR *string, const WCHAR *pattern);
+const WCHAR *strstrW(const WCHAR *string, const WCHAR *pattern);
+
+// Add len_to_add to len_so_far, assuming that if it exceeds the
+// length of the line, it will word wrap onto the next line.  Returns
+// the total length of all the lines summed together.
+float GetLenWithWordWrap (const float len_so_far,
+  const float len_to_add,
+  const uint32 len_line);
+
+// ----------------------------------------------------------------------
+// QuotedPrintableUnescape()
+//    Copies "src" to "dest", rewriting quoted printable escape sequences
+//    =XX to their ASCII equivalents. src is not null terminated, instead
+//    specify len. I recommend that slen<len_dest, but we honour len_dest
+//    anyway.
+//    RETURNS the length of dest.
+// ----------------------------------------------------------------------
+int QuotedPrintableUnescape(const WCHAR *src, int slen, WCHAR *dest, int len_dest);
+
+// Return the length to use for the output buffer given to the base64 escape
+// routines. Make sure to use the same value for do_padding in both.
+// This function may return incorrect results if given input_len values that
+// are extremely high, which should happen rarely.
+int CalculateBase64EscapedLen(int input_len, bool do_padding);
+// Use this version when calling Base64Escape without a do_padding arg.
+int CalculateBase64EscapedLen(int input_len);
+
+// ----------------------------------------------------------------------
+// Base64Escape()
+// WebSafeBase64Escape()
+//    Encode "src" to "dest" using base64 encoding.
+//    src is not null terminated, instead specify len.
+//    'dest' should have at least CalculateBase64EscapedLen() length.
+//    RETURNS the length of dest.
+//    The WebSafe variation use '-' instead of '+' and '_' instead of '/'
+//    so that we can place the out in the URL or cookies without having
+//    to escape them.  It also has an extra parameter "do_padding",
+//    which when set to false will prevent padding with "=".
+// ----------------------------------------------------------------------
+int Base64Escape(const char *src, int slen, char *dest, int szdest);
+int WebSafeBase64Escape(const char *src, int slen, char *dest,
+                        int szdest, bool do_padding);
+void WebSafeBase64Escape(const CStringA& src, CStringA* dest);
+
+void Base64Escape(const char *src, int szsrc,
+                  CStringA* dest, bool do_padding);
+void WebSafeBase64Escape(const char *src, int szsrc,
+                         CStringA* dest, bool do_padding);
+
+// ----------------------------------------------------------------------
+// Base64Unescape()
+//    Copies "src" to "dest", where src is in base64 and is written to its
+//    ASCII equivalents. src is not null terminated, instead specify len.
+//    I recommend that slen<len_dest, but we honour len_dest anyway.
+//    RETURNS the length of dest.
+//    The WebSafe variation use '-' instead of '+' and '_' instead of '/'.
+// ----------------------------------------------------------------------
+int Base64Unescape(const char *src, int slen, char *dest, int len_dest);
+int WebSafeBase64Unescape(const char *src, int slen, char *dest, int szdest);
+
+#ifdef UNICODE
+#define IsSpace IsSpaceW
+#else
+#define IsSpace IsSpaceA
+#endif
+
+bool IsSpaceW(WCHAR c);
+bool IsSpaceA(char c);
+
+// Remove all leading and trailing whitespace from s.
+// Returns the new length of the string (not including 0-terminator)
+int TrimCString(CString &s);
+int Trim(TCHAR *s);
+
+// Trims all characters in the delimiter string from both ends of the
+// string s
+void TrimString(CString& s, const TCHAR* delimiters);
+
+// Strip the first token from the front of argument s.  A token is a
+// series of consecutive non-blank characters - unless the first
+// character is a double-quote ("), in that case the token is the full
+// quoted string
+CString StripFirstQuotedToken(const CString& s);
+
+// A block of text to separate lines, and back
+void TextToLines(const CString& text, const TCHAR* delimiter, std::vector<CString>* lines);
+// (LinesToText puts a delimiter at the end of the last line too)
+void LinesToText(const std::vector<CString>& lines, const TCHAR* delimiter, CString* text);
+
+// Make a CString lower case
+void MakeLowerCString(CString & s);
+
+// Clean up the string: replace all whitespace with spaces, and
+// replace consecutive spaces with one.
+// Returns the new length of the string (not including 0-terminator)
+int CleanupWhitespaceCString(CString &s);
+int CleanupWhitespace(TCHAR *s);
+
+int HexDigitToInt (WCHAR c);
+bool IsHexDigit (WCHAR c);
+
+// Converts to lower, but does so much faster if the string is ANSI
+TCHAR * String_FastToLower(TCHAR * str);
+
+// Replacement for the CRT toupper(c)
+int String_ToUpper(int c);
+
+// Replacement for the CRT toupper(c)
+char String_ToUpperA(char c);
+
+// Converts str to lowercase in place.
+void String_ToLower(TCHAR* str);
+
+// Converts str to uppercase in place.
+void String_ToUpper(TCHAR* str);
+
+bool String_IsUpper(TCHAR c);
+
+// String comparison based on length
+// Replacement for the CRT strncmp(i)
+int String_StrNCmp(const TCHAR * str1, const TCHAR * str2, uint32 len, bool ignore_case);
+
+// Replacement for strncpy() - except ALWAYS ends string with null
+TCHAR* String_StrNCpy(TCHAR* destination, const TCHAR* source, uint32 len);
+
+// check if str starts with start_str
+bool String_StartsWith(const TCHAR *str, const TCHAR *start_str, bool ignore_case);
+
+// check if str starts with start_str, for char *
+bool String_StartsWithA(const char *str, const char *start_str, bool ignore_case);
+
+// check if str ends with end_str
+bool String_EndsWith(const TCHAR *str, const TCHAR *end_str, bool ignore_case);
+
+// If the input string str doesn't already end with the string end_str,
+// make it end with the string end_str.
+CString String_MakeEndWith(const TCHAR *str, const TCHAR* end_str, bool ignore_case);
+
+// converts an int to a string
+CString String_Int64ToString(int64 value, int radix);
+
+// converts an uint64 to a string
+CString String_Uint64ToString(uint64 value, int radix);
+
+// Convert numeric types to CString
+CString sizet_to_str(const size_t & i);
+CString itostr(const int i);
+CString itostr(const uint32 i);
+
+// converts a large number to an approximate value, like "1.2G" or "900M"
+// base_ten = true if based on powers of 10 (like disk space) otherwise based
+// on powers of two.  power = 0 for *10^0, 1 for *10^3 or 2^10, 2 for *10^6
+// or 2^20, and 3 for *10^9 or 2^30, in other words: no units, K, M, or G.
+CString String_LargeIntToApproximateString(uint64 value, bool base_ten, int* power);
+
+// converts a string to an  int
+// Does not check for overflow
+int32 String_StringToInt(const TCHAR * str);
+
+int64 String_StringToInt64(const TCHAR * str);
+
+// converts an double to a string
+// specifies the number of digits after the decimal point
+// TODO(omaha): Make this work for negative values
+CString String_DoubleToString(double value, int point_digits);
+
+// convert string to double
+double String_StringToDouble (const TCHAR *s);
+
+// Converts a character to a digit
+// if the character is not a digit return -1
+int32 String_CharToDigit(const TCHAR c);
+
+// returns true if ASCII digit
+bool String_IsDigit(const TCHAR c);
+
+// Converts the digit to a character.
+TCHAR String_DigitToChar(unsigned int n);
+
+// Returns true if an identifier character: letter, digit, or "_"
+bool String_IsIdentifierChar(const TCHAR c);
+
+// Returns true if the string has letters in it.
+// This is used by the keyword extractor to downweight numbers,
+// IDs (sequences of numbers like social security numbers), etc.
+bool String_HasAlphabetLetters (const TCHAR *str);
+
+// Return the index of the first occurrence of s2 in s1, or -1 if none.
+int String_FindString(const TCHAR *s1, const TCHAR *s2);
+int String_FindString(const TCHAR *s1, const TCHAR *s2, int start_pos);
+
+// Return the index of the first occurrence of c in s1, or -1 if none.
+int String_FindChar(const TCHAR *str, const TCHAR c);
+// start from index start_pos
+int String_FindChar(const TCHAR *str, const TCHAR c, int start_pos);
+
+// Return the index of the first occurrence of c in string, or -1 if none.
+int String_ReverseFindChar(const TCHAR * str, TCHAR c);
+
+bool String_Contains(const TCHAR *s1, const TCHAR *s2);
+
+// Replace old_char with new_char in str.
+void String_ReplaceChar(TCHAR *str, TCHAR old_char, TCHAR new_char);
+void String_ReplaceChar(CString & str, TCHAR old_char, TCHAR new_char);
+
+// Append the given character to the string if it doesn't already end with it.
+// There must be room in the string to append the character if necessary.
+void String_EndWithChar(TCHAR *str, TCHAR c);
+
+// A special version of the replace function which takes advantage of CString properties
+// to make it much faster when the string grows
+
+// NOTE: it CANNOT match more than kMaxReplaceMatches instances within the string
+// do not use this function if that is a possibility
+
+// The maximum number of replacements to perform. Essentially infinite
+const unsigned int kRepMax = kuint32max;
+int ReplaceCString (CString & src, const TCHAR *from, unsigned int from_len,
+                                   const TCHAR *to, unsigned int to_len,
+                                   unsigned int max_matches);
+
+// replace from with to in src
+// on memory allocation error, returns the original string
+int ReplaceString (TCHAR *src, const TCHAR *from, const TCHAR *to, TCHAR **out, int *out_len);
+
+// replace from with to in src
+// will replace in place if length(to) <= length(from) and return *out == src
+// WILL CREATE NEW OUTPUT BUFFER OTHERWISE and set created_new_string to true
+// on memory allocation error, returns the original string
+int ReplaceStringMaybeInPlace (TCHAR *src, const TCHAR *from, const TCHAR *to, TCHAR **out, int *out_len, bool *created_new_string);
+
+// you really want to use the straight TCHAR version above. you know it
+// on memory allocation error, returns the original string
+int ReplaceCString (CString & src, const TCHAR *from, const TCHAR *to);
+
+long __cdecl Wcstol (const wchar_t *nptr, wchar_t **endptr, int ibase);
+unsigned long __cdecl Wcstoul (const wchar_t *nptr, wchar_t **endptr, int ibase);
+
+// Functions on arrays of strings
+
+// Returns true iff s is in the array strings (case-insensitive compare)
+bool String_MemberOf(const TCHAR* const* strings, const TCHAR* s);
+// Returns index of s in the array of strings (or -1 for missing) (case-insensitive compare)
+int String_IndexOf(const TCHAR* const* strings, const TCHAR* s);
+
+// Serializes a time64 to a string, and then loads it out again, this string it not for human consumption
+time64 StringToTime(const CString & time);
+CString TimeToString(const time64 & time);
+
+// looks for string A followed by any number of spaces/tabs followed by string b
+// returns starting position of a if found, NULL if not
+// case insensitive
+const TCHAR *FindStringASpaceStringB (const TCHAR *s, const TCHAR *a, const TCHAR *b);
+
+bool IsAlphaA (const char c);
+bool IsDigitA (const char c);
+
+// TODO(omaha): deprecate since we have secure CRT now.
+// dest_buffer_len includes the NULL
+// always NULL terminates
+// dest must be a valid string with length < dest_buffer_len
+void SafeStrCat (TCHAR *dest, const TCHAR *src, int dest_buffer_len);
+
+const TCHAR *ExtractNextDouble (const TCHAR *s, double *f);
+
+TCHAR *String_PathFindExtension(const TCHAR *path);
+
+inline TCHAR Char_ToLower(TCHAR c) {
+// C4302: truncation from 'type 1' to 'type 2'
+#pragma  warning(disable : 4302)
+  return reinterpret_cast<TCHAR>(::CharLower(reinterpret_cast<TCHAR*>(c)));
+#pragma warning(default : 4302)
+}
+
+// @returns the lowercase character (type is int to be consistent with the CRT)
+int String_ToLowerChar(int c);
+
+// Replacement for the CRT tolower(c)
+char String_ToLowerCharAnsi(char c);
+
+bool String_PathRemoveFileSpec(TCHAR *path);
+
+// Escapes and unescapes strings (shlwapi-based implementation).
+// The indended usage for these APIs is escaping strings to make up
+// URLs, for example building query strings.
+//
+// Pass false to the flag segment_only to escape the url. This will not
+// cause the conversion of the # (%23), ? (%3F), and / (%2F) characters.
+HRESULT StringEscape(const CString& str_in,
+                     bool segment_only,
+                     CString* str_out);
+
+HRESULT StringUnescape(const CString& str_in, CString* str_out);
+
+// Converts a string to an int, performs all the necessary
+// checks to ensure that the string is correct.
+// Tests for overflow and non-int strings.
+bool String_StringToDecimalIntChecked(const TCHAR* str, int* value);
+
+// Converts CLSID to a string.
+bool CLSIDToCString(const GUID& guid, CString* str);
+
+// Converts a string to a bool.
+HRESULT String_StringToBool(const TCHAR* str, bool* value);
+
+// Convert boolean to its string representation.
+HRESULT String_BoolToString(bool value, CString* string);
+
+// Similar to ATL::CStringT::Replace() except that it ignores case.
+CString String_ReplaceIgnoreCase(const CString& string,
+                                 const CString& token,
+                                 const CString& replacement);
+// Converts a string to a Tristate enum.
+bool String_StringToTristate(const TCHAR* str, Tristate* value);
+
+// Extracts the name and value from a string that contains a name/value pair.
+bool ParseNameValuePair(const CString& token, TCHAR separator,
+                        CString* name, CString* value);
+
+// Splits a command line buffer into two parts in place:
+// first argument (which could be path to executable) and remaining arguments.
+// Note that the same pointer can be used for both command_line and
+// either of the remaining parameters.
+bool SplitCommandLineInPlace(TCHAR *command_line,
+                             TCHAR **first_argument,
+                             TCHAR **remaining_arguments);
+
+// Returns true if the unicode string only contains ascii values.
+bool ContainsOnlyAsciiChars(const CString& str);
+// Converts a buffer of bytes to a hex string.
+CString BytesToHex(const uint8* bytes, size_t num_bytes);
+
+// Converts a vector of bytes to a hex string.
+CString BytesToHex(const std::vector<uint8>& bytes);
+
+void JoinStrings(const std::vector<CString>& components,
+                 const TCHAR* delim,
+                 CString* result);
+
+void JoinStringsInArray(const TCHAR* components[],
+                        int num_components,
+                        const TCHAR* delim,
+                        CString* result);
+
+// Formats the specified message ID.
+// It is similar to CStringT::FormatMessage() but it returns an empty string
+// instead of throwing when the message ID cannot be loaded.
+CString FormatResourceMessage(uint32 resource_id, ...);
+
+// Formats an error code as an 8-digit HRESULT-style hex number or an unsigned
+// integer depending on whether it matches the HRESULT failure format.
+CString FormatErrorCode(DWORD error_code);
+
+// Converts the unicode string into a utf8 encoded, urlencoded string.
+// The resulting ascii string is returned in a wide CString.
+HRESULT WideStringToUtf8UrlEncodedString(const CString& str, CString* out);
+
+// Converts a string that is in the utf8 representation and is urlencoded
+// into a unicode string.
+HRESULT Utf8UrlEncodedStringToWideString(const CString& str, CString* out);
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_STRING_H_
diff --git a/base/string_unittest.cc b/base/string_unittest.cc
new file mode 100644
index 0000000..99fc571
--- /dev/null
+++ b/base/string_unittest.cc
@@ -0,0 +1,1751 @@
+// Copyright 2003-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "base/basictypes.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/localization.h"
+#include "omaha/base/string.h"
+#include "omaha/base/time.h"
+#include "omaha/base/timer.h"
+#include "omaha/base/tr_rand.h"
+#include "omaha/testing/resource.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(StringTest, IntToString) {
+  ASSERT_STREQ(String_Int64ToString(0, 10), L"0");
+  ASSERT_STREQ(String_Int64ToString(1, 10), L"1");
+  ASSERT_STREQ(String_Int64ToString(-1, 10), L"-1");
+  ASSERT_STREQ(String_Int64ToString(123456789, 10), L"123456789");
+  ASSERT_STREQ(String_Int64ToString(-123456789, 10), L"-123456789");
+  ASSERT_STREQ(String_Int64ToString(1234567890987654321, 10),
+               L"1234567890987654321");
+  ASSERT_STREQ(String_Int64ToString(-1234567890987654321, 10),
+               L"-1234567890987654321");
+  ASSERT_STREQ(String_Int64ToString(0xabcdef, 16), L"abcdef");
+  ASSERT_STREQ(String_Int64ToString(0x101fff, 16), L"101fff");
+  ASSERT_STREQ(String_Int64ToString(0x999999, 16), L"999999");
+  ASSERT_STREQ(String_Int64ToString(0x0, 16), L"0");
+
+  ASSERT_STREQ(String_Int64ToString(01234, 8), L"1234");
+  ASSERT_STREQ(String_Int64ToString(0, 8), L"0");
+  ASSERT_STREQ(String_Int64ToString(0777, 8), L"777");
+  ASSERT_STREQ(String_Int64ToString(0123456, 8), L"123456");
+
+  ASSERT_STREQ(String_Int64ToString(0, 2), L"0");
+  ASSERT_STREQ(String_Int64ToString(0xf, 2), L"1111");
+  ASSERT_STREQ(String_Int64ToString(0x5ad1, 2), L"101101011010001");
+  ASSERT_STREQ(String_Int64ToString(-1, 2), L"-1");
+}
+
+TEST(StringTest, UintToString) {
+  ASSERT_STREQ(String_Uint64ToString(0, 10), L"0");
+  ASSERT_STREQ(String_Uint64ToString(1, 10), L"1");
+  ASSERT_STREQ(String_Uint64ToString(123456789, 10), L"123456789");
+  ASSERT_STREQ(String_Uint64ToString(1234567890987654321, 10),
+               L"1234567890987654321");
+  ASSERT_STREQ(String_Uint64ToString(18446744073709551615, 10),
+               L"18446744073709551615");
+
+  ASSERT_STREQ(String_Uint64ToString(0xabcdef, 16), L"abcdef");
+  ASSERT_STREQ(String_Uint64ToString(0x101fff, 16), L"101fff");
+  ASSERT_STREQ(String_Uint64ToString(0x999999, 16), L"999999");
+  ASSERT_STREQ(String_Uint64ToString(0x0, 16), L"0");
+  ASSERT_STREQ(String_Uint64ToString(0xffffffffffffffff, 16), L"ffffffffffffffff");
+
+  ASSERT_STREQ(String_Uint64ToString(01234, 8), L"1234");
+  ASSERT_STREQ(String_Uint64ToString(0, 8), L"0");
+  ASSERT_STREQ(String_Uint64ToString(0777, 8), L"777");
+  ASSERT_STREQ(String_Uint64ToString(0123456, 8), L"123456");
+
+  ASSERT_STREQ(String_Uint64ToString(0, 2), L"0");
+  ASSERT_STREQ(String_Uint64ToString(0xf, 2), L"1111");
+  ASSERT_STREQ(String_Uint64ToString(0x5ad1, 2), L"101101011010001");
+}
+
+TEST(StringTest, DoubleToString) {
+  ASSERT_STREQ(String_DoubleToString(1.234, 1), L"1.2");
+  ASSERT_STREQ(String_DoubleToString(0.0, 0), L"0");
+  ASSERT_STREQ(String_DoubleToString(0.0, 2), L"0.00");
+  ASSERT_STREQ(String_DoubleToString(199.234, 2), L"199.23");
+  ASSERT_STREQ(String_DoubleToString(-199.234, 2), L"-199.23");
+  ASSERT_STREQ(String_DoubleToString(199.23490776, 5), L"199.23490");
+  ASSERT_STREQ(String_DoubleToString(-1.0001, 1), L"-1.0");
+  ASSERT_STREQ(String_DoubleToString(123456789.987654321, 3), L"123456789.987");
+}
+
+TEST(StringTest, StrNCpy) {
+  TCHAR * str1 = L"test str 1234";
+  TCHAR * str2 = L"test str 12";
+  TCHAR * str3 = L"Test StR 1234";
+
+  // check case sensitive
+  ASSERT_TRUE(0 == String_StrNCmp(str1, str2, 10, false));
+  ASSERT_TRUE(0 == String_StrNCmp(str1, str2, 11, false));
+
+  // check case in-sensitive
+  ASSERT_TRUE(0 == String_StrNCmp(str2, str3, 10, true));
+  ASSERT_TRUE(0 == String_StrNCmp(str2, str3, 11, true));
+}
+
+TEST(StringTest, StartsWith) {
+  ASSERT_TRUE(String_StartsWith(L"", L"", false));
+  ASSERT_TRUE(String_StartsWith(L"Joe", L"", false));
+  ASSERT_TRUE(String_StartsWith(L"Joe", L"J", false));
+  ASSERT_TRUE(String_StartsWith(L"Joe\\", L"J", false));
+  ASSERT_TRUE(String_StartsWith(L"Joe", L"Joe", false));
+  ASSERT_TRUE(String_StartsWith(L"The quick brown fox", L"The quic", false));
+  ASSERT_FALSE(String_StartsWith(L"", L"J", false));
+  ASSERT_FALSE(String_StartsWith(L"Joe", L"Joe2", false));
+  ASSERT_FALSE(String_StartsWith(L"The quick brown fox", L"The quiC", false));
+
+  ASSERT_TRUE(String_StartsWith(L"", L"", true));
+  ASSERT_TRUE(String_StartsWith(L"Joe", L"j", true));
+  ASSERT_TRUE(String_StartsWith(L"The quick brown fox", L"The quiC", true));
+}
+
+TEST(StringTest, StartsWithA) {
+  ASSERT_TRUE(String_StartsWithA("", "", false));
+  ASSERT_TRUE(String_StartsWithA("Joe", "", false));
+  ASSERT_TRUE(String_StartsWithA("Joe", "J", false));
+  ASSERT_TRUE(String_StartsWithA("Joe\\", "J", false));
+  ASSERT_TRUE(String_StartsWithA("Joe", "Joe", false));
+  ASSERT_TRUE(String_StartsWithA("The quick brown fox", "The quic", false));
+  ASSERT_FALSE(String_StartsWithA("", "J", false));
+  ASSERT_FALSE(String_StartsWithA("Joe", "Joe2", false));
+  ASSERT_FALSE(String_StartsWithA("The quick brown fox", "The quiC", false));
+
+  ASSERT_TRUE(String_StartsWithA("", "", true));
+  ASSERT_TRUE(String_StartsWithA("Joe", "j", true));
+  ASSERT_TRUE(String_StartsWithA("The quick brown fox", "The quiC", true));
+}
+
+TEST(StringTest, EndsWith) {
+  // Case sensitive
+
+  // Empty suffix
+  ASSERT_TRUE(String_EndsWith(L"", L"", false));
+  ASSERT_TRUE(String_EndsWith(L"Joe", L"", false));
+
+  // Partial suffix
+  ASSERT_TRUE(String_EndsWith(L"Joe", L"e", false));
+  ASSERT_TRUE(String_EndsWith(L"Joe\\", L"\\", false));
+  ASSERT_TRUE(String_EndsWith(L"The quick brown fox", L"n fox", false));
+
+  // Suffix == String
+  ASSERT_TRUE(String_EndsWith(L"Joe", L"Joe", false));
+  ASSERT_TRUE(String_EndsWith(L"The quick brown fox",
+                              L"The quick brown fox",
+                              false));
+
+  // Fail cases
+  ASSERT_FALSE(String_EndsWith(L"", L"J", false));
+  ASSERT_FALSE(String_EndsWith(L"Joe", L"Joe2", false));
+  ASSERT_FALSE(String_EndsWith(L"Joe", L"2Joe", false));
+  ASSERT_FALSE(String_EndsWith(L"The quick brown fox", L"n foX", false));
+
+  // Check case insensitive
+
+  // Empty suffix
+  ASSERT_TRUE(String_EndsWith(L"", L"", true));
+  ASSERT_TRUE(String_EndsWith(L"Joe", L"", true));
+
+  // Partial suffix
+  ASSERT_TRUE(String_EndsWith(L"Joe", L"E", true));
+  ASSERT_TRUE(String_EndsWith(L"The quick brown fox", L"n FOX", true));
+
+  // Suffix == String
+  ASSERT_TRUE(String_EndsWith(L"Joe", L"JOE", true));
+  ASSERT_TRUE(String_EndsWith(L"The quick brown fox",
+                              L"The quick brown FOX",
+                              true));
+
+  // Fail cases
+  ASSERT_FALSE(String_EndsWith(L"The quick brown fox", L"s", true));
+  ASSERT_FALSE(String_EndsWith(L"The quick brown fox", L"Xs", true));
+  ASSERT_FALSE(String_EndsWith(L"The quick brown fox", L"the brown foX", true));
+}
+
+TEST(StringTest, Unencode) {
+  // Normal, correct usage.
+  // char 0x25 is '%'
+  ASSERT_STREQ(Unencode(L"?q=moon+doggy_%25%5E%26"), L"?q=moon doggy_%^&");
+  ASSERT_STREQ(Unencode(L"%54%68%69%73+%69%73%09%61%20%74%65%73%74%0A"),
+               L"This is\ta test\n");
+  ASSERT_STREQ(Unencode(L"This+is%09a+test%0a"), L"This is\ta test\n");
+
+  // NULL char.
+  ASSERT_STREQ(Unencode(L"Terminated%00before+this"), L"Terminated");
+  ASSERT_STREQ(Unencode(L"invalid+%a%25"), L"invalid %a%");
+  ASSERT_STREQ(Unencode(L"invalid+%25%41%37"), L"invalid %A7");
+  ASSERT_STREQ(Unencode(L"not a symbol %RA"), L"not a symbol %RA");
+  ASSERT_STREQ(Unencode(L"%ag"), L"%ag");
+  ASSERT_STREQ(Unencode(L"dontdecode%dont"), L"dontdecode%dont");
+  ASSERT_STREQ(Unencode(L""), L"");
+  ASSERT_STREQ(Unencode(L"%1"), L"%1");
+  ASSERT_STREQ(Unencode(L"\x100"), L"\x100");
+  ASSERT_STREQ(Unencode(L"this is%20a%20wide%20char%20\x345"),
+               L"this is a wide char \x345");
+  ASSERT_STREQ(Unencode(L"a utf8 string %E7%BC%9c %E4%B8%8a = 2"),
+               L"a utf8 string \x7f1c \x4e0a = 2");
+}
+
+#if 0
+static const struct {
+  const char *ansi;
+  const TCHAR *wide;
+  UINT cp;
+} kAnsi2WideTests[] = {
+  { "\xc8\xae\xc1\xbe", L"\x72ac\x8f86", CP_GB2312},
+  { "\xa5\x69\xb1\x4e\xc2\xb2\xc5\xe9",
+    L"\x53ef\x5c07\x7c21\x9ad4", CP_BIG5},
+  { "\xE7\xBC\x96\xE4\xB8\x8B", L"\x7f16\x4e0b", CP_UTF8},
+  { "ascii", L"ascii", CP_GB2312},
+  { "\x3C\x20\xE7\xBC\x96", L"\x003c\x0020\x00E7\x00BC\x0096", 0 },
+};
+
+bool TestAnsiToWideString() {
+  for (size_t i = 0; i < arraysize(kAnsi2WideTests); ++i) {
+    CStringW out;
+    if (kAnsi2WideTests[i].cp == 0) {
+      out = AnsiToWideString(kAnsi2WideTests[i].ansi,
+                             strlen(kAnsi2WideTests[i].ansi));
+    } else {
+      AnsiToWideString(kAnsi2WideTests[i].ansi,
+                       strlen(kAnsi2WideTests[i].ansi),
+                       kAnsi2WideTests[i].cp, &out);
+    }
+    CHK(out == kAnsi2WideTests[i].wide);
+  }
+  return true;
+}
+#endif
+
+TEST(StringTest, Show) {
+  ASSERT_STREQ(Show(0), _T("0"));
+  ASSERT_STREQ(Show(1), _T("1"));
+  ASSERT_STREQ(Show(-1), _T("-1"));
+}
+
+
+// Test international strings.
+TEST(StringTest, International) {
+  CString tabs_by_lang[] = {
+      _T("Web    Prente    Groepe    Gids    "),                    // Afrikaans
+      _T("Web    Fotografitë    Grupet    Drejtoriumi    "),        // Albanian
+      // Amharic is missing, that doesn't show in normal windows fonts
+      _T("ويب    صور    مجموعات    الدليل     "),                    // Arabic
+      _T("Web   Şəkillər   Qruplar   Qovluq   "),                   // Azerbaijani
+      _T("Web   Irudiak   Taldeak   Direktorioa   "),               // Basque
+      _T("Ўэб    Малюнкі    Групы    Каталёг    "),                 // Belarusian
+      _T("Antorjal    Chitraboli    Gosthi    Bishoy-Talika    "),  // Bengali
+      _T("MakarJal    Chhaya    Jerow    Nirdeshika    "),          // Bihari
+      _T("Veb   Imeges   Gruoops   Durectury "),                    // Bork
+      _T("Internet    Slike    Grupe    Katalog    "),              // Bosnian
+      _T("Gwiad    Skeudennoù    Strolladoù    Roll    "),          // Breton
+      _T("Мрежата    Изображения    Групи    Директория    "),      // Bulgarian
+      _T("Web    Imatges    Grups    Directori    "),               // Catalan
+      _T("所有网站    图像    网上论坛    网页目录    "),                            // Chinese Simplified
+      _T("所有網頁    圖片    網上論壇    網頁目錄     "),                            // Chinese Traditional
+      _T("Web    Slike    Grupe    Imenik    "),                    // Croatian
+      _T("Web    Obrázky    Skupiny    Adresář    "),               // Czech
+      _T("Nettet    Billeder    Grupper    Katalog    "),           // Danish
+      _T("Het Internet    Afbeeldingen    Discussiegroepen    Gids    "),  // Dutch
+      _T("Web    Images    Gwoups    Diwectowy    "),               // Elmer
+      _T("Web    Images    Groups    News    Froogle    more »"),   // English
+      _T("TTT    Bildoj    Grupoj    Katalogo     "),               // Esperanto
+      _T("Veeb    Pildid    Grupid    Kataloog    "),               // Estonian
+      _T("Netið    Myndir    Bólkar    Øki    "),                   // Faroese
+      _T("Web    Mga Larawan    Mga Grupo    Direktoryo    "),      // Filipino
+      _T("Web    Kuvat    Keskusteluryhmät    Hakemisto    "),      // Finnish
+      _T("Web    Images    Groupes    Annuaire    Actualités    "),  // French
+      _T("Web   Printsjes   Diskusjegroepen   Directory   "),       // Frisian
+      _T("Web    Imaxes    Grupos    Directorio    "),              // Galician
+      _T("ინტერნეტი   სურათები   ჯგუფები   კატალოგი   "),                      // Georgian
+      _T("Web    Bilder    Groups    Verzeichnis    News    "),     // German
+      _T("Ιστός    Eικόνες    Ομάδες    Κατάλογος    "),            // Greek
+      _T("Ñanduti   Ta'anga   Atypy   Sãmbyhypy "),                 // Guarani
+      _T("jalu    Chhabi    Sangathan    Shabdakosh    "),          // Gujarati
+      _T("n0rM4L s33rCh    1|\\/|4935    6r00pZ    d1r3c70rY    "),  // Hacker
+      _T("אתרים ברשת    תמונות    קבוצות דיון    מדריך האתרים     "),  // Hebrew
+      _T("वेब    छवियाँ    समूह    निर्देशिका    "),                             // Hindi
+      _T("Web    Képek    Csoportok    Címtár    "),                // Hungarian
+      _T("Vefur    Myndir    Hópar    Flokkar    "),                // Icelandic
+      _T("Web    Gambar    Grup    Direktori    "),                 // Indonesian
+      _T("Web    Imagines    Gruppos    Catalogo  "),               // Interlingua
+      _T("An Gréasán    Íomhánna    Grúpaí    Eolaire    "),        // Irish
+      _T("Web    Immagini    Gruppi    Directory    News Novità!    "),  // Italian
+      _T("ウェブ    イメージ    グループ    ディレクトリ    "),                        // Japanese
+      _T("Web    Gambar - gambar    Paguyuban    Bagian    "),      // Javanese
+      _T("antharajAla    chitragaLu    gumpugaLu    Huduku vibhaagagaLu    "),  // Kannada
+      _T("Daqmey pat    naghmey beQ    ghommey    mem    "),        // Klingon
+      _T("웹 문서    이미지    뉴스그룹    디렉토리    "),                            // Klingon
+      _T("Желе   Суроттор   Группалар   Тизме   "),                 // Kyrgyz
+      _T("Tela   Imagines   Circuli   Index "),                     // Latin
+      _T("Internets    Attēli    Vēstkopas    Katalogs"),           // Latvian
+      _T("Internetas    Vaizdai    Grupės    Katalogas    "),       // Lithuanian
+      _T("Мрежа    Слики    Групи    Директориум    "),             // Macedonian
+      _T("Jaringan    Imej    Kumpulan    Direktori    "),          // Malay
+      _T("വെബ്    ചിത്രങ്ങള്    സംഘങ്ങള്    ഡയറക്ടറി    "),                     // Malayalam
+      _T("Web    Stampi    Gruppi    Direttorju    "),              // Maltese
+      _T("वेबशोध    चित्रशोध    ग्रूप्स    डिरेक्टरी    "),                          // Marathi
+      _T("वेब    तस्वीर    समूह    डाइरेक्टरी    "),                            // Nepali
+      _T("Nett    Bilder    Grupper    Katalog    "),               // Norwegian
+      _T("Veven    Bilete    Grupper    Katalog    "),              // Norwegian (Nynorsk)
+      _T("Ret    Imatges    Grops    Directori    "),               // Occitan
+      _T("web   chitra   goSThi   prasanga tAlikA "),               // Oriya
+      _T("وب    تصويرها    گروهها    فهرست     "),                  // Persian
+      _T("ebway    imagesyay    oupsgray    Irectoryday    "),      // P. Latin
+      _T("WWW    Grafika    Grupy dyskusyjne    Katalog   "),       // Polish
+      _T("Web    Imagens    Grupos    Diretório    "),              // Potruguese (Brazil)
+      _T("Web    Imagens    Grupos    Directório  "),               // Potruguese (Portugal)
+      _T("Web/Zaal    Tasveraan    Gutt    Directory    "),         // Punjabi
+      _T("Web    Imagini    Grupuri    Director    "),              // Romanian
+      _T("Веб    Картинки    Группы    Каталог  "),                 // Russian
+      _T("Lìon    Dealbhan    Cuantail    Eòlaire    "),            // Scots Gaelic
+      _T("Интернет    Слике    Групе    Каталог    "),              // Serbian
+      _T("Internet    Slike    Grupe    Spisak    "),               // Serbo-Croatian
+      _T("Web   Ponahalo   Dihlopha   Tshupetso "),                 // Sesotho
+      _T("WEB    Roopa    Kandayam    Namawaliya    "),             // Sinhalese
+      _T("Web    Obrázky    Skupiny    Katalóg    "),               // Slovak
+      _T("Internet    Slike    Skupine    Imenik    "),             // Slovenian
+      _T("La Web    Imágenes    Grupos    Directorio    News ¡Nuevo!    "),  // Spanish
+      _T("Web   Gambar   Grup   Direktori "),                       // Sudanese
+      _T("Mtandao    Picha    Vikundi    Orodha    "),              // Swahili
+      _T("Nätet    Bilder    Grupper    Kategori    "),             // Swedish
+      _T("வலை    படங்கள்    குழுக்கள்    விபரக்கோவை    "),          // Tamil
+      _T("వెబ్    చిత్రాలు    సమూహములు    darshini    "),                        // Telugu
+      _T("เว็บ    รูปภาพ    กลุ่มข่าว    สารบบเว็บ    "),                            // Thai
+      // Tigrinya is missing, that doesn't show in normal windows fonts
+      _T("Web    Grafikler    Gruplar    Dizin    "),               // Turkish
+      _T("Web   Suratlar   Toparlar   Düzine "),                    // Turkmen
+      _T("tintan   Nfonyin   Akuokuo   Krataa nhwemu "),            // Twi
+      _T("Веб    Зображення    Групи    Каталог    "),              // Ukrainian
+      _T("ويب    تصاوير    گروہ    فہرست         ")                           // Urdu
+      _T("To'r    Tasvirlar    Gruppalar    Papka    "),            // Uzbek
+      _T("Internet    Hình Ảnh    Nhóm    Thư Mục    "),            // Vietnamese
+      _T("Y We    Lluniau    Grwpiau    Cyfeiriadur    "),          // Welsh
+      _T("Web   Imifanekiso   Amaqela   Isilawuli "),               // Xhosa
+      _T("װעב    בילדער    גרופּעס    פּאַפּקע     "),                  // Yiddish
+      _T("I-web   Izithombe   Amaqembu   Uhlu lwamafayela   "),     // Zulu
+  };
+
+  int i = 0;
+  for(i = 0; i < arraysize(tabs_by_lang); ++i) {
+    // Get the cannonical lower version with ::CharLower
+    CString true_lower(tabs_by_lang[i]);
+    ::CharLower(true_lower.GetBuffer());
+    true_lower.ReleaseBuffer();
+
+    // Get the lower version with String_ToLower,
+    CString low_temp(tabs_by_lang[i]);
+    String_ToLower(low_temp.GetBuffer());
+    low_temp.ReleaseBuffer();
+
+    // make sure they match
+    ASSERT_STREQ(low_temp, true_lower);
+
+    // Now make sure they match letter by letter
+    for(int j = 0; j < tabs_by_lang[i].GetLength(); ++j) {
+      TCHAR cur_char = tabs_by_lang[i].GetAt(j);
+
+      TCHAR low1 = static_cast<TCHAR>(String_ToLowerChar(cur_char));
+
+      ASSERT_EQ(low1, true_lower.GetAt(j));
+      ASSERT_EQ(Char_ToLower(cur_char), true_lower.GetAt(j));
+
+      // Check the Ansi version if applicable
+      if (cur_char < 128)
+        ASSERT_EQ(String_ToLowerChar(static_cast<char>(cur_char)),
+                  true_lower.GetAt(j));
+    }
+
+    // Test out the CString conversion
+    CString temp(tabs_by_lang[i]);
+    MakeLowerCString(temp);
+    ASSERT_STREQ(temp, true_lower);
+
+    // Test out the fast version
+    temp = tabs_by_lang[i];
+    String_FastToLower(temp.GetBuffer());
+    temp.ReleaseBuffer();
+
+    ASSERT_STREQ(temp, true_lower);
+
+    // Make sure that the normal CString::Trim works the same as our fast one
+    CString trim_normal(tabs_by_lang[i]);
+    trim_normal.Trim();
+
+    CString trim_fast(tabs_by_lang[i]);
+    TrimCString(trim_fast);
+
+    ASSERT_STREQ(trim_normal, trim_fast);
+  }
+}
+
+void TestReplaceString (TCHAR *src, TCHAR *from, TCHAR *to, TCHAR *expected) {
+  ASSERT_TRUE(expected);
+  ASSERT_TRUE(to);
+  ASSERT_TRUE(from);
+  ASSERT_TRUE(src);
+
+  size_t new_src_size = _tcslen(src) + 1;
+  TCHAR* new_src = new TCHAR[new_src_size];
+
+  _tcscpy_s(new_src, new_src_size, src);
+
+  Timer tchar (false);
+  Timer tchar2 (false);
+  Timer cstring (false);
+  Timer orig_cstring (false);
+
+  // int iterations = 10000;
+  int iterations = 10;
+
+  int out_len;
+  TCHAR *out;
+
+  for (int i = 0; i < iterations; i++) {
+      _tcscpy_s(new_src, new_src_size, src);
+      bool created_new_string = false;
+
+      tchar.Start();
+      ReplaceString (new_src, from, to, &out, &out_len);
+      tchar.Stop();
+
+      ASSERT_STREQ(out, expected);
+      delete [] out;
+  }
+
+  for (int i = 0; i < iterations; i++) {
+      _tcscpy_s(new_src, new_src_size, src);
+      bool created_new_string = false;
+
+      tchar2.Start();
+      ReplaceStringMaybeInPlace (new_src, from, to, &out,
+                                 &out_len, &created_new_string);
+      tchar2.Stop();
+
+      ASSERT_STREQ(out, expected);
+      if (out != new_src) { delete [] out; }
+  }
+
+  for (int i = 0; i < iterations; i++) {
+      CString src_string(src);
+
+      orig_cstring.Start();
+      src_string.Replace (from, to);
+      orig_cstring.Stop();
+
+      ASSERT_STREQ(src_string, CString(expected));
+  }
+
+  for (int i = 0; i < iterations; i++) {
+      CString src_string(src);
+
+      cstring.Start();
+      ReplaceCString (src_string, from, to);
+      cstring.Stop();
+
+      ASSERT_STREQ(src_string, CString(expected));
+  }
+
+  delete [] new_src;
+}
+
+TEST(StringTest, ReplaceCString) {
+  CString t;
+  t = _T("a a a b ");
+  ReplaceCString(t, _T("a"), 1, _T("d"), 1, 5);
+  ASSERT_STREQ(_T("d d d b "), t);
+
+  t = _T("a a a b ");
+  ReplaceCString(t, _T("b"), 1, _T("d"), 1, 5);
+  ASSERT_STREQ(_T("a a a d "), t);
+
+  t = _T("a a a b ");
+  ReplaceCString(t, _T("a"), 1, _T("d"), 1, 1);
+  ASSERT_STREQ(_T("d a a b "), t);
+
+  t = _T("a a a b ");
+  ReplaceCString(t, _T("a"), 1, _T("dd"), 2, 5);
+  ASSERT_STREQ(_T("dd dd dd b "), t);
+
+  ReplaceCString(t, _T("dd"), 2, _T("dddd"), 4, 5);
+  ASSERT_STREQ(_T("dddd dddd dddd b "), t);
+
+  ReplaceCString(t, _T("dd"), 2, _T("dddd"), 4, 5);
+  ASSERT_STREQ(_T("dddddddd dddddddd dddddd b "), t);
+
+  ReplaceCString(t, _T("dddddddd"), 8, _T("dddd"), 4, 2);
+  ASSERT_STREQ(_T("dddd dddd dddddd b "), t);
+
+  ReplaceCString(t, _T("d"), 1, _T("a"), 1, 2);
+  ASSERT_STREQ(_T("aadd dddd dddddd b "), t);
+
+  ReplaceCString(t, _T("d d"), 3, _T("c"), 1, 2);
+  ASSERT_STREQ(_T("aadcddcddddd b "), t);
+
+  ReplaceCString(t, _T("c"), 1, _T("1234567890"), 10, 2);
+  ASSERT_STREQ(_T("aad1234567890dd1234567890ddddd b "), t);
+
+  ReplaceCString(t, _T("1"), 1, _T("1234567890"), 10, 2);
+  ASSERT_STREQ(_T("aad1234567890234567890dd1234567890234567890ddddd b "), t);
+
+  ReplaceCString(t, _T("1234567890"), 10, _T(""), 0, 2);
+  ASSERT_STREQ(_T("aad234567890dd234567890ddddd b "), t);
+
+  t = _T("a aa aa b ");
+  ReplaceCString(t, _T("aa"), 2, _T("b"), 1, 5);
+  ASSERT_STREQ(_T("a b b b "), t);
+
+  t = _T("moo a aa aa b ");
+  ReplaceCString(t, _T("aa"), 2, _T("b"), 1, 5);
+  ASSERT_STREQ(_T("moo a b b b "), t);
+
+  // Time to test some big strings
+  int test_sizes[] = {200, 500, 900, 10000};
+
+  int i;
+  for(i = 0; i < arraysize(test_sizes); ++i) {
+    CString in, out;
+    for(int j = 0; j < test_sizes[i]; ++j) {
+      in += L'a';
+      out += _T("bb");
+    }
+    CString bak_in(in);
+
+    // Make it a bit bigger
+    int times = ReplaceCString(in, _T("a"), 1, _T("bb"), 2, kRepMax);
+    ASSERT_EQ(times, test_sizes[i]);
+    ASSERT_EQ(out, in);
+
+    // Make it bigger still
+    times = ReplaceCString(in, _T("bb"), 2, _T("ccc"), 3, kRepMax);
+    ASSERT_EQ(times, test_sizes[i]);
+
+    // Same size swap
+    times = ReplaceCString(in, _T("c"), 1, _T("d"), 1, kRepMax);
+    ASSERT_EQ(times, test_sizes[i] * 3);
+
+    // Make it smaller again
+    times = ReplaceCString(in, _T("ddd"), 3, _T("a"), 1, kRepMax);
+    ASSERT_EQ(times, test_sizes[i]);
+    ASSERT_EQ(bak_in, in);
+  }
+}
+
+TEST(StringTest, GetField) {
+  CString s(_T("<a>a</a><b>123</b><c>aa\ndd</c>"));
+
+  CString a(GetField (s, L"a"));
+  ASSERT_STREQ(a, L"a");
+
+  CString b(GetField (s, L"b"));
+  ASSERT_STREQ(b, L"123");
+
+  CString c(GetField (s, L"c"));
+  ASSERT_STREQ(c, L"aa\ndd");
+}
+
+TEST(StringTest, String_HasAlphabetLetters) {
+  ASSERT_TRUE(String_HasAlphabetLetters (L"abc"));
+  ASSERT_TRUE(String_HasAlphabetLetters (L"X"));
+  ASSERT_TRUE(String_HasAlphabetLetters (L" pie "));
+  ASSERT_FALSE(String_HasAlphabetLetters (L"1"));
+  ASSERT_FALSE(String_HasAlphabetLetters (L"0"));
+  ASSERT_FALSE(String_HasAlphabetLetters (L"010"));
+  ASSERT_FALSE(String_HasAlphabetLetters (L"314-159"));
+  ASSERT_TRUE(String_HasAlphabetLetters (L"pie0"));
+}
+
+TEST(StringTest, String_LargeIntToApproximateString) {
+  int power;
+  ASSERT_TRUE(String_LargeIntToApproximateString(10LL, true, &power) == _T("10") && power == 0);
+  ASSERT_TRUE(String_LargeIntToApproximateString(99LL, true, &power) == _T("99") && power == 0);
+  ASSERT_TRUE(String_LargeIntToApproximateString(990LL, true, &power) == _T("990") && power == 0);
+  ASSERT_TRUE(String_LargeIntToApproximateString(999LL, true, &power) == _T("999") && power == 0);
+
+  ASSERT_TRUE(String_LargeIntToApproximateString(1000LL, true, &power) == _T("1.0") && power == 1);
+  ASSERT_TRUE(String_LargeIntToApproximateString(1200LL, true, &power) == _T("1.2") && power == 1);
+  ASSERT_TRUE(String_LargeIntToApproximateString(7500LL, true, &power) == _T("7.5") && power == 1);
+  ASSERT_TRUE(String_LargeIntToApproximateString(9900LL, true, &power) == _T("9.9") && power == 1);
+  ASSERT_TRUE(String_LargeIntToApproximateString(10000LL, true, &power) == _T("10") && power == 1);
+  ASSERT_TRUE(String_LargeIntToApproximateString(11000LL, true, &power) == _T("11") && power == 1);
+  ASSERT_TRUE(String_LargeIntToApproximateString(987654LL, true, &power) == _T("987") && power == 1);
+
+  ASSERT_TRUE(String_LargeIntToApproximateString(1000000LL, true, &power) == _T("1.0") && power == 2);
+  ASSERT_TRUE(String_LargeIntToApproximateString(1300000LL, true, &power) == _T("1.3") && power == 2);
+  ASSERT_TRUE(String_LargeIntToApproximateString(987654321LL, true, &power) == _T("987") && power == 2);
+
+  ASSERT_TRUE(String_LargeIntToApproximateString(1000000000LL, true, &power) == _T("1.0") && power == 3);
+  ASSERT_TRUE(String_LargeIntToApproximateString(1999999999LL, true, &power) == _T("1.9") && power == 3);
+  ASSERT_TRUE(String_LargeIntToApproximateString(20000000000LL, true, &power) == _T("20") && power == 3);
+  ASSERT_TRUE(String_LargeIntToApproximateString(1000000000000LL, true, &power) == _T("1000") && power == 3);
+  ASSERT_TRUE(String_LargeIntToApproximateString(12345678901234LL, true, &power) == _T("12345") && power == 3);
+
+  ASSERT_TRUE(String_LargeIntToApproximateString(1023LL, false, &power) == _T("1023") && power == 0);
+
+  ASSERT_TRUE(String_LargeIntToApproximateString(1024LL, false, &power) == _T("1.0") && power == 1);
+  ASSERT_TRUE(String_LargeIntToApproximateString(1134LL, false, &power) == _T("1.1") && power == 1);
+  ASSERT_TRUE(String_LargeIntToApproximateString(10240LL, false, &power) == _T("10") && power == 1);
+
+  ASSERT_TRUE(String_LargeIntToApproximateString(5242880LL, false, &power) == _T("5.0") && power == 2);
+
+  ASSERT_TRUE(String_LargeIntToApproximateString(1073741824LL, false, &power) == _T("1.0") && power == 3);
+  ASSERT_TRUE(String_LargeIntToApproximateString(17179869184LL, false, &power) == _T("16") && power == 3);
+}
+
+TEST(StringTest, FindWholeWordMatch) {
+  // words with spaces before / after
+  ASSERT_EQ(0, FindWholeWordMatch (L"pi", L"pi", false, 0));
+  ASSERT_EQ(1, FindWholeWordMatch (L" pi", L"pi", false, 0));
+  ASSERT_EQ(1, FindWholeWordMatch (L" pi ", L"pi", false, 0));
+  ASSERT_EQ(0, FindWholeWordMatch (L"pi ", L"pi", false, 0));
+
+  // partial matches
+  ASSERT_EQ(-1, FindWholeWordMatch (L"pie ", L"pi", false, 0));
+  ASSERT_EQ(-1, FindWholeWordMatch (L" pie ", L"pi", false, 0));
+  ASSERT_EQ(-1, FindWholeWordMatch (L"pie", L"pi", false, 0));
+  ASSERT_EQ(-1, FindWholeWordMatch (L" pie", L"pi", false, 0));
+
+  // partial match with non-alphanumeric chars
+  ASSERT_EQ(-1, FindWholeWordMatch (L" pumpkin_pie ", L"pie", false, 0));
+  ASSERT_EQ(-1, FindWholeWordMatch (L" pie_crust ", L"pie", false, 0));
+  ASSERT_EQ(-1, FindWholeWordMatch (L"tartar", L"tar", false, 0));
+  ASSERT_EQ(-1, FindWholeWordMatch (L"pie!", L"pie", false, 0));
+}
+
+TEST(StringTest, ReplaceWholeWord) {
+  CString str (L"pie");
+  ReplaceWholeWord (L"ie", L"..", false, &str);
+  ASSERT_STREQ(str, L"pie");
+
+  ReplaceWholeWord (L"pie", L"..", false, &str);
+  ASSERT_STREQ(str, L"..");
+
+  str = L"banana pie";
+  ReplaceWholeWord (L"pie", L"..", false, &str);
+  ASSERT_STREQ(str, L"banana ..");
+
+  str = L"banana pie";
+  ReplaceWholeWord (L"banana", L"..", false, &str);
+  ASSERT_STREQ(str, L".. pie");
+
+  str = L"banana pie";
+  ReplaceWholeWord (L"banana pie", L" .. ", false, &str);
+  ASSERT_STREQ(str, L" .. ");
+
+  str = L"banana pie";
+  ReplaceWholeWord (L"pi", L" .. ", false, &str);
+  ASSERT_STREQ(str, L"banana pie");
+
+  str = L"ishniferatsu";
+  ReplaceWholeWord (L"era", L" .. ", false, &str);
+  ASSERT_STREQ(str, L"ishniferatsu");
+
+  str = L"i i i hi ii i";
+  ReplaceWholeWord (L"i", L"you", false, &str);
+  ASSERT_STREQ(str, L"you you you hi ii you");
+
+  str = L"a nice cream cheese pie";
+  ReplaceWholeWord (L"cream cheese", L"..", false, &str);
+  ASSERT_STREQ(str, L"a nice .. pie");
+
+  // ---
+  // Test replacement with whitespace trimming
+
+  // Replace in the middle of the string.
+  str = L"a nice cream cheese pie";
+  ReplaceWholeWord (L"cream cheese", L"..", true, &str);
+  ASSERT_STREQ(str, L"a nice..pie");
+
+  // Replace in the beginning of the string.
+  str = L"a nice cream cheese pie";
+  ReplaceWholeWord (L"a nice", L"..", true, &str);
+  ASSERT_STREQ(str, L"..cream cheese pie");
+
+  // Replace in the end of the string.
+  str = L"a nice cream cheese pie";
+  ReplaceWholeWord (L"pie", L"..", true, &str);
+  ASSERT_STREQ(str, L"a nice cream cheese..");
+}
+
+
+TEST(StringTest, TestReplaceString) {
+  // timing for replace string, for the specific tests below shows:
+  //
+  // the TCHAR version is always faster than CRT CString::Replace
+  //
+  // the CString version is faster than CRT CString::Replace:
+  // - always if the replacement is shorter
+  // - if the source string is longer than ~60 characters if the replacement is
+  //   longer
+  //
+  // based on our current usage of CString::Replace, I expect the new CString
+  // version is faster on average than CRT CString::Replace
+  //
+  // non-CRT CString::Replace is much slower, so all of these should be much
+  // faster than that
+
+  TestReplaceString(L"that's what i changed -it was propagating the error code but i ..", L" .. ", L"<b> .. </b>", L"that's what i changed -it was propagating the error code but i ..");
+  TestReplaceString(L"news.com.url", L".url", L"", L"news.com");
+  TestReplaceString(L"news.com..url", L".url", L"", L"news.com.");
+  TestReplaceString(L"news.com.u.url", L".url", L"", L"news.com.u");
+  TestReplaceString(L"abanana pie banana", L"banana", L"c", L"ac pie c");
+  TestReplaceString(L"bananabananabanana", L"banana", L"c", L"ccc");
+  TestReplaceString(L"abanana pie banana", L"banana", L"cabanapie", L"acabanapie pie cabanapie");
+  TestReplaceString(L"bananabananabanana", L"banana", L"cabanapie", L"cabanapiecabanapiecabanapie");
+  TestReplaceString(L"banana pie banana pie", L"banana", L"c", L"c pie c pie");
+  TestReplaceString(L"banana pie banana pie", L"pie", L"z", L"banana z banana z");
+  TestReplaceString(L"banana pie banana pie", L"banana", L"bananacabana", L"bananacabana pie bananacabana pie");
+  TestReplaceString(L"banana pie banana pie", L"pie", L"pietie", L"banana pietie banana pietie");
+  TestReplaceString(L"banana pie banana pie", L"tie", L"pietie", L"banana pie banana pie");
+  TestReplaceString(L"banana pie banana pie banana pie banana pie banana pie", L"banana", L"bananacab", L"bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie");
+  TestReplaceString(L"banana pie banana pie banana pie banana pie banana pie banana pie banana pie", L"banana", L"bananacab", L"bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie");
+  TestReplaceString(L"banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie", L"banana", L"bananacab", L"bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie");
+  TestReplaceString(L"banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie", L"banana", L"bananacab", L"bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie");
+  TestReplaceString(L"banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie", L"banana", L"cab", L"cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie");
+  TestReplaceString(L"news", L"news", L"", L"");
+  TestReplaceString(L"&nbsp;", L"&nbsp;", L"", L"");
+  TestReplaceString(L"&nbsp;&nbsp;&nbsp;", L"&nbsp;", L"", L"");
+  TestReplaceString(L"&nbsp; &nbsp;&nbsp;", L"&nbsp;", L"", L" ");
+}
+
+
+TEST(StringTest, GetAbsoluteUri) {
+  ASSERT_STREQ(GetAbsoluteUri(L"http://www.google.com"),
+               L"http://www.google.com/");
+  ASSERT_STREQ(GetAbsoluteUri(L"http://www.google.com/"),
+               L"http://www.google.com/");
+  ASSERT_STREQ(GetAbsoluteUri(L"http://www.google.com//"),
+               L"http://www.google.com/");
+  ASSERT_STREQ(GetAbsoluteUri(L"http://www.google.com/test"),
+               L"http://www.google.com/test");
+}
+
+void TestTrim(const TCHAR *str, const TCHAR *result) {
+  ASSERT_TRUE(result);
+  ASSERT_TRUE(str);
+
+  size_t ptr_size = _tcslen(str) + 1;
+  TCHAR* ptr = new TCHAR[ptr_size];
+  _tcscpy_s(ptr, ptr_size, str);
+
+  int len = Trim(ptr);
+  ASSERT_STREQ(ptr, result);
+  ASSERT_EQ(len, lstrlen(result));
+
+  delete [] ptr;
+}
+
+TEST(StringTest, Trim) {
+  TestTrim(L"", L"");
+  TestTrim(L" ", L"");
+  TestTrim(L"\t", L"");
+  TestTrim(L"\n", L"");
+  TestTrim(L"\n\t    \t \n", L"");
+  TestTrim(L"    joe", L"joe");
+  TestTrim(L"joe      ", L"joe");
+  TestTrim(L"    joe      ", L"joe");
+  TestTrim(L"joe smith    ", L"joe smith");
+  TestTrim(L"     joe smith    ", L"joe smith");
+  TestTrim(L"     joe   smith    ", L"joe   smith");
+  TestTrim(L"     The quick brown fox,\tblah", L"The quick brown fox,\tblah");
+  TestTrim(L" \tblah\n    joe smith    ", L"blah\n    joe smith");
+}
+
+// IsSpaceA1 is much faster without the cache clearing (which is what happends
+// in release mode)
+// IsSpaceA1 is roughly the same speed as IsSpaceA2 with cache clearing (in
+// debug mode)
+// IsSpaceA3 is always much slower
+
+static const byte spacesA[256] = {
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 1,  // 0-9
+  1, 1, 1, 1, 0, 0, 0, 0, 0, 0,  // 10-19
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 20-29
+  0, 0, 1, 0, 0, 0, 0, 0, 0, 0,  // 30-39
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 40-49
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 50-59
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 60-69
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 70-79
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 80-89
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 90-99
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 100-109
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 110-119
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 120-129
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 130-139
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 140-149
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 150-159
+  1, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 160-169
+};
+
+bool IsSpaceA1(char c) {
+  return spacesA[c] == 1;
+}
+
+bool IsSpaceA2(char c) {
+  // return (c==32);
+  // if (c>32) { return 0; }
+  // most characters >32, check first for that case
+  if (c>32 && c!=160) { return 0; }
+  if (c==32) { return 1; }
+  if (c>=9&&c<=13) return 1; else return 0;
+}
+
+bool IsSpaceA3(char c) {
+  WORD result;
+  if (GetStringTypeA(0, CT_CTYPE1, &c, 1, &result)) {
+    return (0 != (result & C1_SPACE));
+  }
+  return false;
+}
+
+void TestIsSpace (char *s) {
+    ASSERT_TRUE(s);
+
+    Timer t1 (false);
+    Timer t2 (false);
+    Timer t3 (false);
+
+    // int iterations = 10000;
+    int iterations = 100;
+    int len = strlen (s);
+
+    // used to try to clear the processor cache
+    int dlen = 100000;
+    char *dummy = new char [dlen];
+    for (int i = 0; i < dlen; i++) {
+      dummy[i] = static_cast<char>(tr_rand() % 256);
+    }
+
+    int num_spaces = 0;
+    int n = iterations * len;
+    for (int i = 0; i < iterations; i++) {
+        t1.Start();
+        for (int j = 0; j < len; j++) {
+            num_spaces += IsSpaceA1 (s[j]);
+        }
+        t1.Stop();
+        // this cache clearing code gets optimized out in release mode
+        int d2 = 0;
+        for (int i = 0; i < dlen; i++) { d2 += dummy[i]; }
+    }
+
+    num_spaces = 0;
+    for (int i = 0; i < iterations; i++) {
+        t2.Start();
+        for (int j = 0; j < len; j++) {
+            num_spaces += IsSpaceA2 (s[j]);
+        }
+        t2.Stop();
+        int d2 = 0;
+        for (int i = 0; i < dlen; i++) { d2 += dummy[i]; }
+    }
+
+    num_spaces = 0;
+    for (int i = 0; i < iterations; i++) {
+        t3.Start();
+        for (int j = 0; j < len; j++) {
+            num_spaces += IsSpaceA3 (s[j]);
+        }
+        t3.Stop();
+        int d2 = 0;
+        for (int i = 0; i < dlen; i++) { d2 += dummy[i]; }
+    }
+}
+
+TEST(StringTest, IsSpace) {
+  TestIsSpace("banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie");
+  TestIsSpace("sdlfhdkgheorutsgj sdlj aoi oaj gldjg opre gdsfjng oate yhdnv ;zsj fpoe v;kjae hgpaieh dajlgn aegh avn WEIf h9243y 9814cu 902t7 9[-32 [O8W759 RC90817 V9pDAHc n( ny(7LKFJAOISF *&^*^%$$%#*&^(*_*)_^& 67% 796%&$*^$ 8)6 (^ 08&^ )*^ 9-7=90z& +(^ )^* %9%4386 $& (& &+ 7- &(_* ");
+}
+
+void TestCleanupWhitespace(const TCHAR *str, const TCHAR *result) {
+  ASSERT_TRUE(result);
+  ASSERT_TRUE(str);
+
+  size_t ptr_size = _tcslen(str) + 1;
+  TCHAR* ptr = new TCHAR[ptr_size];
+  _tcscpy_s(ptr, ptr_size, str);
+
+  int len = CleanupWhitespace(ptr);
+  ASSERT_STREQ(ptr, result);
+  ASSERT_EQ(len, lstrlen(result));
+
+  delete [] ptr;
+}
+
+TEST(StringTest, CleanupWhitespace) {
+  TestCleanupWhitespace(L"", L"");
+  TestCleanupWhitespace(L"a    ", L"a");
+  TestCleanupWhitespace(L"    a", L"a");
+  TestCleanupWhitespace(L" a   ", L"a");
+  TestCleanupWhitespace(L"\t\n\r a   ", L"a");
+  TestCleanupWhitespace(L"  \n   a  \t   \r ", L"a");
+  TestCleanupWhitespace(L"a      b", L"a b");
+  TestCleanupWhitespace(L"   a \t\n\r     b", L"a b");
+  TestCleanupWhitespace(L"   vool                 voop", L"vool voop");
+  TestCleanupWhitespace(L"thisisaverylongstringwithsometext",
+                        L"thisisaverylongstringwithsometext");
+  TestCleanupWhitespace(L"thisisavery   longstringwithsometext",
+                        L"thisisavery longstringwithsometext");
+}
+
+void TestWcstoul (TCHAR *string, int radix, unsigned long expected) {
+    ASSERT_TRUE(string);
+
+    wchar_t *ptr;
+    int v = Wcstoul (string, &ptr, radix);
+    ASSERT_EQ(v, expected);
+
+#ifdef DEBUG
+    int v2 = wcstoul (string, &ptr, radix);
+    ASSERT_EQ(v, v2);
+#endif
+}
+
+TEST(StringTest, Wcstoul) {
+  TestWcstoul(L"625", 16, 1573);
+  TestWcstoul(L" 625", 16, 1573);
+  TestWcstoul(L"a3", 16, 163);
+  TestWcstoul(L"A3", 16, 163);
+  TestWcstoul(L"  A3", 16, 163);
+  TestWcstoul(L" 12445", 10, 12445);
+  TestWcstoul(L"12445778", 10, 12445778);
+}
+
+TEST(StringTest, IsDigit) {
+  ASSERT_TRUE(String_IsDigit('0'));
+  ASSERT_TRUE(String_IsDigit('1'));
+  ASSERT_TRUE(String_IsDigit('2'));
+  ASSERT_TRUE(String_IsDigit('3'));
+  ASSERT_TRUE(String_IsDigit('4'));
+  ASSERT_TRUE(String_IsDigit('5'));
+  ASSERT_TRUE(String_IsDigit('6'));
+  ASSERT_TRUE(String_IsDigit('7'));
+  ASSERT_TRUE(String_IsDigit('8'));
+  ASSERT_TRUE(String_IsDigit('9'));
+  ASSERT_FALSE(String_IsDigit('a'));
+  ASSERT_FALSE(String_IsDigit('b'));
+  ASSERT_FALSE(String_IsDigit('z'));
+  ASSERT_FALSE(String_IsDigit('A'));
+  ASSERT_FALSE(String_IsDigit(' '));
+  ASSERT_FALSE(String_IsDigit('#'));
+}
+
+TEST(StringTest, IsUpper) {
+  ASSERT_FALSE(String_IsUpper('0'));
+  ASSERT_FALSE(String_IsUpper(' '));
+  ASSERT_FALSE(String_IsUpper('#'));
+  ASSERT_FALSE(String_IsUpper('a'));
+  ASSERT_FALSE(String_IsUpper('z'));
+  ASSERT_TRUE(String_IsUpper('A'));
+  ASSERT_TRUE(String_IsUpper('B'));
+  ASSERT_TRUE(String_IsUpper('C'));
+  ASSERT_TRUE(String_IsUpper('D'));
+  ASSERT_TRUE(String_IsUpper('H'));
+  ASSERT_TRUE(String_IsUpper('Y'));
+  ASSERT_TRUE(String_IsUpper('Z'));
+}
+
+TEST(StringTest, StringToDouble) {
+  ASSERT_DOUBLE_EQ(String_StringToDouble(L"625"), 625);
+  ASSERT_DOUBLE_EQ(String_StringToDouble(L"-625"), -625);
+  ASSERT_DOUBLE_EQ(String_StringToDouble(L"-6.25"), -6.25);
+  ASSERT_DOUBLE_EQ(String_StringToDouble(L"6.25"), 6.25);
+  ASSERT_DOUBLE_EQ(String_StringToDouble(L"0.00"), 0);
+  ASSERT_DOUBLE_EQ(String_StringToDouble(L" 55.1"), 55.1);
+  ASSERT_DOUBLE_EQ(String_StringToDouble(L" 55.001"), 55.001);
+  ASSERT_DOUBLE_EQ(String_StringToDouble(L"  1.001"), 1.001);
+}
+
+TEST(StringTest, StringToInt) {
+  ASSERT_EQ(String_StringToInt(L"625"), 625);
+  ASSERT_EQ(String_StringToInt(L"6"), 6);
+  ASSERT_EQ(String_StringToInt(L"0"), 0);
+  ASSERT_EQ(String_StringToInt(L" 122"), 122);
+  ASSERT_EQ(String_StringToInt(L"a"), 0);
+  ASSERT_EQ(String_StringToInt(L" a"), 0);
+}
+
+TEST(StringTest, StringToInt64) {
+  ASSERT_EQ(String_StringToInt64(L"119600064000000000"),
+            119600064000000000uI64);
+  ASSERT_EQ(String_StringToInt64(L" 119600064000000000"),
+            119600064000000000uI64);
+  ASSERT_EQ(String_StringToInt64(L"625"), 625);
+  ASSERT_EQ(String_StringToInt64(L"6"), 6);
+  ASSERT_EQ(String_StringToInt64(L"0"), 0);
+  ASSERT_EQ(String_StringToInt64(L" 122"), 122);
+  ASSERT_EQ(String_StringToInt64(L"a"), 0);
+  ASSERT_EQ(String_StringToInt64(L" a"), 0);
+}
+
+void TestEndWithChar(const TCHAR *s, char c, const TCHAR *expected) {
+  ASSERT_TRUE(expected);
+  ASSERT_TRUE(s);
+
+  TCHAR buf[5000];
+  _tcscpy(buf, s);
+  String_EndWithChar(buf, c);
+  ASSERT_STREQ(buf, expected);
+}
+
+TEST(StringTest, EndWithChar) {
+  TestEndWithChar(L"", L'a', L"a");
+  TestEndWithChar(L"", L'\\', L"\\");
+  TestEndWithChar(L"a", L'a', L"a");
+  TestEndWithChar(L"a", L'b', L"ab");
+  TestEndWithChar(L"abcdefghij", L'a', L"abcdefghija");
+  TestEndWithChar(L"abcdefghij", L'\\', L"abcdefghij\\");
+}
+
+TEST(StringTest, HexDigitToInt) {
+  ASSERT_EQ(HexDigitToInt(L'0'), 0);
+  ASSERT_EQ(HexDigitToInt(L'1'), 1);
+  ASSERT_EQ(HexDigitToInt(L'2'), 2);
+  ASSERT_EQ(HexDigitToInt(L'3'), 3);
+  ASSERT_EQ(HexDigitToInt(L'4'), 4);
+  ASSERT_EQ(HexDigitToInt(L'5'), 5);
+  ASSERT_EQ(HexDigitToInt(L'6'), 6);
+  ASSERT_EQ(HexDigitToInt(L'7'), 7);
+  ASSERT_EQ(HexDigitToInt(L'8'), 8);
+  ASSERT_EQ(HexDigitToInt(L'9'), 9);
+  ASSERT_EQ(HexDigitToInt(L'A'), 10);
+  ASSERT_EQ(HexDigitToInt(L'a'), 10);
+  ASSERT_EQ(HexDigitToInt(L'B'), 11);
+  ASSERT_EQ(HexDigitToInt(L'b'), 11);
+  ASSERT_EQ(HexDigitToInt(L'C'), 12);
+  ASSERT_EQ(HexDigitToInt(L'c'), 12);
+  ASSERT_EQ(HexDigitToInt(L'D'), 13);
+  ASSERT_EQ(HexDigitToInt(L'd'), 13);
+  ASSERT_EQ(HexDigitToInt(L'E'), 14);
+  ASSERT_EQ(HexDigitToInt(L'e'), 14);
+  ASSERT_EQ(HexDigitToInt(L'F'), 15);
+  ASSERT_EQ(HexDigitToInt(L'f'), 15);
+}
+
+TEST(StringTest, IsHexDigit) {
+  ASSERT_TRUE(IsHexDigit(L'0'));
+  ASSERT_TRUE(IsHexDigit(L'1'));
+  ASSERT_TRUE(IsHexDigit(L'2'));
+  ASSERT_TRUE(IsHexDigit(L'3'));
+  ASSERT_TRUE(IsHexDigit(L'4'));
+  ASSERT_TRUE(IsHexDigit(L'5'));
+  ASSERT_TRUE(IsHexDigit(L'6'));
+  ASSERT_TRUE(IsHexDigit(L'7'));
+  ASSERT_TRUE(IsHexDigit(L'8'));
+  ASSERT_TRUE(IsHexDigit(L'9'));
+  ASSERT_TRUE(IsHexDigit(L'a'));
+  ASSERT_TRUE(IsHexDigit(L'A'));
+  ASSERT_TRUE(IsHexDigit(L'b'));
+  ASSERT_TRUE(IsHexDigit(L'B'));
+  ASSERT_TRUE(IsHexDigit(L'c'));
+  ASSERT_TRUE(IsHexDigit(L'C'));
+  ASSERT_TRUE(IsHexDigit(L'd'));
+  ASSERT_TRUE(IsHexDigit(L'D'));
+  ASSERT_TRUE(IsHexDigit(L'e'));
+  ASSERT_TRUE(IsHexDigit(L'E'));
+  ASSERT_TRUE(IsHexDigit(L'f'));
+  ASSERT_TRUE(IsHexDigit(L'F'));
+
+  for(TCHAR digit = static_cast<TCHAR>(127); digit < 10000; ++digit) {
+    ASSERT_FALSE(IsHexDigit(digit));
+  }
+}
+
+TEST(StringTest, Remove) {
+  CString temp_remove;
+
+  // Remove everything
+  temp_remove = _T("ftp://");
+  RemoveFromStart (temp_remove, _T("ftp://"), false);
+  ASSERT_STREQ(temp_remove, _T(""));
+
+  // Remove all but 1 letter
+  temp_remove = _T("ftp://a");
+  RemoveFromStart (temp_remove, _T("ftp://"), false);
+  ASSERT_STREQ(temp_remove, _T("a"));
+
+  // Remove the first instance
+  temp_remove = _T("ftp://ftp://");
+  RemoveFromStart (temp_remove, _T("ftp://"), false);
+  ASSERT_STREQ(temp_remove, _T("ftp://"));
+
+  // Remove normal
+  temp_remove = _T("ftp://taz the tiger");
+  RemoveFromStart (temp_remove, _T("ftp://"), false);
+  ASSERT_STREQ(temp_remove, _T("taz the tiger"));
+
+  // Wrong prefix
+  temp_remove = _T("ftp:/taz the tiger");
+  RemoveFromStart (temp_remove, _T("ftp://"), false);
+  ASSERT_STREQ(temp_remove, _T("ftp:/taz the tiger"));
+
+  // Not long enough
+  temp_remove = _T("ftp:/");
+  RemoveFromStart (temp_remove, _T("ftp://"), false);
+  ASSERT_STREQ(temp_remove, _T("ftp:/"));
+
+  // Remove nothing
+  temp_remove = _T("ftp:/");
+  RemoveFromStart (temp_remove, _T(""), false);
+  ASSERT_STREQ(temp_remove, _T("ftp:/"));
+
+  // Remove 1 character
+  temp_remove = _T("ftp:/");
+  RemoveFromStart (temp_remove, _T("f"), false);
+  ASSERT_STREQ(temp_remove, _T("tp:/"));
+
+  // Wrong case
+  temp_remove = _T("ftp:/");
+  RemoveFromStart (temp_remove, _T("F"), false);
+  ASSERT_STREQ(temp_remove, _T("ftp:/"));
+
+  // Remove everything
+  temp_remove = _T(".edu");
+  RemoveFromEnd (temp_remove, _T(".edu"));
+  ASSERT_STREQ(temp_remove, _T(""));
+
+  // Remove all but 1 letter
+  temp_remove = _T("a.edu");
+  RemoveFromEnd(temp_remove, _T(".edu"));
+  ASSERT_STREQ(temp_remove, _T("a"));
+
+  // Remove the first instance
+  temp_remove = _T(".edu.edu");
+  RemoveFromEnd(temp_remove, _T(".edu"));
+  ASSERT_STREQ(temp_remove, _T(".edu"));
+
+  // Remove normal
+  temp_remove = _T("ftp://taz the tiger.edu");
+  RemoveFromEnd(temp_remove, _T(".edu"));
+  ASSERT_STREQ(temp_remove, _T("ftp://taz the tiger"));
+
+  // Wrong suffix
+  temp_remove = _T("ftp:/taz the tiger.edu");
+  RemoveFromEnd(temp_remove, _T("/edu"));
+  ASSERT_STREQ(temp_remove, _T("ftp:/taz the tiger.edu"));
+
+  // Not long enough
+  temp_remove = _T("edu");
+  RemoveFromEnd(temp_remove, _T(".edu"));
+  ASSERT_STREQ(temp_remove, _T("edu"));
+
+  // Remove nothing
+  temp_remove = _T(".edu");
+  RemoveFromEnd(temp_remove, _T(""));
+  ASSERT_STREQ(temp_remove, _T(".edu"));
+
+  // Remove 1 character
+  temp_remove = _T(".edu");
+  RemoveFromEnd(temp_remove, _T("u"));
+  ASSERT_STREQ(temp_remove, _T(".ed"));
+
+  // Wrong case
+  temp_remove = _T(".edu");
+  RemoveFromEnd(temp_remove, _T("U"));
+  ASSERT_STREQ(temp_remove, _T(".edu"));
+}
+
+TEST(StringTest, WideToAnsiDirect) {
+  CString temp_convert;
+  ASSERT_STREQ("", WideToAnsiDirect(_T("")));
+  ASSERT_STREQ("a", WideToAnsiDirect(_T("a")));
+  ASSERT_STREQ("moon doggy", WideToAnsiDirect(_T("moon doggy")));
+
+  // Generate a string of all characters 0-255.
+  const int kNumChars = 256;
+  TCHAR nasty_chars[kNumChars];
+  for (int i = 0; i < kNumChars; ++i) {
+    nasty_chars[i] = static_cast<TCHAR>(i);
+  }
+  CString temp(nasty_chars, kNumChars);
+
+  // Convert it and make sure it matches.
+  CStringA out = WideToAnsiDirect(temp);
+  ASSERT_EQ(out.GetLength(), kNumChars);
+  for (int i = 0; i < kNumChars; ++i) {
+    ASSERT_EQ(static_cast<unsigned char>(nasty_chars[i]),
+              static_cast<unsigned char>(out.GetAt(i)));
+  }
+}
+
+TEST(StringTest, FindStringASpaceStringB) {
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-type: text/html", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-TYPE: text/html", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-TYPE: text/HTML", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-TYPE: text/HTML", L"content-type:", L"text/HTML"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-TYPE: text/HTML", L"content-TYPE:", L"text/HTML"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:text/html", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:  text/html", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:   text/html", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-type: sdfjsldkgjsdg content-type:    text/html", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-type: content-type: sdfjsldkgjsdg content-type:    text/html", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:content-type: sdfjsldkgjsdg content-type:    text/html", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:content-type:    text/html", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"test/html content-type:content-type:    text/html", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:    text/html", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:\ttext/html", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:\t text/html", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"Content-Type:\t text/html", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"aasd content-type: text/html", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"aa content-TYPE: text/html", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"text.html  content-TYPE: text/HTML", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"text/html content-TYPE: text/HTML", L"content-type:", L"text/HTML"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"AAAA content-TYPE: text/HTML", L"content-TYPE:", L"text/HTML"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:text/html AAAAA", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:  text/html", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:   text/htmlaaa", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:    text/html  asdsdg content-type", L"content-type:", L"text/html"));
+  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:\ttext/htmlconttent-type:te", L"content-type:", L"text/html"));
+
+  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:  a  text/html", L"content-type:", L"text/html"));
+  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:  content-type:  a  text/html", L"content-type:", L"text/html"));
+  ASSERT_FALSE(FindStringASpaceStringB(L"content-type: b text/html  content-type:  a  text/html", L"content-type:", L"text/html"));
+  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:-text/html", L"content-type:", L"text/html"));
+  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:\ntext/html", L"content-type:", L"text/html"));
+  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:  a  TEXT/html", L"content-type:", L"text/html"));
+  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:  a  html/text", L"content-type:", L"text/html"));
+  ASSERT_FALSE(FindStringASpaceStringB(L"a dss content-type:  a  text/html", L"content-type:", L"text/html"));
+  ASSERT_FALSE(FindStringASpaceStringB(L"text/html content-type:-text/html", L"content-type:", L"text/html"));
+  ASSERT_FALSE(FindStringASpaceStringB(L"text/html sdfsd fcontent-type:\ntext/html", L"content-type:", L"text/html"));
+  ASSERT_FALSE(FindStringASpaceStringB(L"AAAA content-type:  a  TEXT/html", L"content-type:", L"text/html"));
+  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:  a  html/text AAA", L"content-type:", L"text/html"));
+  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:", L"content-type:", L"text/html"));
+  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:content-type:", L"content-type:", L"text/html"));
+  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:content-type: content-type:", L"content-type:", L"text/html"));
+}
+
+TEST(StringTest, ElideIfNeeded) {
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 3, 3), L"1..");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 4, 3), L"12..");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 5, 3), L"123..");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 6, 3), L"1234..");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 7, 3), L"1234..");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 8, 3), L"1234..");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 9, 3), L"1234..");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 10, 3), L"1234..");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 11, 3), L"1234 6789..");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 12, 3), L"1234 6789..");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 13, 3), L"1234 6789..");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 14, 3), L"1234 6789 1234");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 15, 3), L"1234 6789 1234");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 16, 3), L"1234 6789 1234");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 17, 3), L"1234 6789 1234");
+
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 7, 6), L"1234..");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 8, 6), L"1234 6..");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 9, 6), L"1234 67..");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 10, 6), L"1234 678..");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 11, 6), L"1234 6789..");
+  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 12, 6), L"1234 6789..");
+}
+
+TEST(StringTest, SafeStrCat) {
+  const int kDestLen = 7;
+  TCHAR dest[kDestLen];
+  lstrcpyn(dest, L"short", kDestLen);
+  ASSERT_LT(lstrlen(dest), kDestLen);
+
+  dest[kDestLen-1] = 'a';
+  lstrcpyn(dest, L"medium123", kDestLen);
+  ASSERT_EQ(dest[kDestLen - 1], '\0');
+  ASSERT_LT(lstrlen(dest), kDestLen);
+
+  lstrcpyn(dest, L"longerlonger", kDestLen);
+  ASSERT_EQ(dest[kDestLen - 1], '\0');
+  ASSERT_LT(lstrlen(dest), kDestLen);
+
+  lstrcpyn(dest, L"12", kDestLen);
+  SafeStrCat(dest, L"3456", kDestLen);
+  ASSERT_EQ(dest[kDestLen - 1], '\0');
+  ASSERT_LT(lstrlen(dest), kDestLen);
+}
+
+void TestPathFindExtension(const TCHAR *s) {
+  ASSERT_STREQ(String_PathFindExtension(s), PathFindExtension(s));
+}
+
+TEST(StringTest, TestPathFindExtension) {
+  TestPathFindExtension(L"c:\\test.tmp");
+  TestPathFindExtension(L"c:\\test.temp");
+  TestPathFindExtension(L"c:\\t\\e\\st.temp");
+  TestPathFindExtension(L"c:\\a.temp");
+  TestPathFindExtension(L"\\aaa\\a.temp");
+  TestPathFindExtension(L"\\a\\a.temp");
+  TestPathFindExtension(L"\\a\\a.temp");
+  TestPathFindExtension(L"\\a\\a.t....emp");
+  TestPathFindExtension(L"\\a.a.a...a\\a.t....emp");
+  TestPathFindExtension(L"\\a\\a\\bbbb\\ddddddddddddddd.temp");
+  TestPathFindExtension(L"\\a\\a\\bbbb\\ddddddddddddddd.te___124567mp");
+  TestPathFindExtension(L"\\a\\a\\bbbb\\ddddddd.dddddddd.te___124567mp");
+}
+
+TEST(StringTest, TextToLinesAndBack) {
+  const TCHAR sample_input[]  = L"Now is the time\r\nfor all good men\r\nto come to the aid of their country";
+  const TCHAR* sample_lines[] = { L"Now is the time", L"for all good men", L"to come to the aid of their country" };
+  const TCHAR sample_output1[] = L"Now is the time\nfor all good men\nto come to the aid of their country\n";
+  const TCHAR sample_output2[] = L"Now is the timefor all good mento come to the aid of their country";
+
+  CString text_in(sample_input);
+  std::vector<CString> lines;
+  CString text_out;
+
+  TextToLines(text_in, L"\r\n", &lines);
+  ASSERT_EQ(lines.size(), 3);
+  for (size_t i = 0; i < arraysize(sample_lines); ++i) {
+    ASSERT_TRUE(0 == lines[i].Compare(sample_lines[i]));
+  }
+  LinesToText(lines, L"\n", &text_out);
+  ASSERT_TRUE(0 == text_out.Compare(sample_output1));
+  LinesToText(lines, L"", &text_out);
+  ASSERT_TRUE(0 == text_out.Compare(sample_output2));
+}
+
+CString TrimStdString(const TCHAR* str) {
+  CString s(str);
+  TrimString(s, L" \t");
+  return s;
+}
+
+TEST(StringTest, TrimString) {
+  ASSERT_STREQ(L"abc", TrimStdString(L"abc"));
+  ASSERT_STREQ(L"abc", TrimStdString(L" abc "));
+  ASSERT_STREQ(L"a c", TrimStdString(L" a c  "));
+  ASSERT_STREQ(L"abc", TrimStdString(L" \tabc\t "));
+  ASSERT_STREQ(L"", TrimStdString(L""));
+  ASSERT_STREQ(L"", TrimStdString(L"   "));
+}
+
+TEST(StringTest, StripFirstQuotedToken) {
+  ASSERT_STREQ(StripFirstQuotedToken(L""), L"");
+  ASSERT_STREQ(StripFirstQuotedToken(L"a" ), L"");
+  ASSERT_STREQ(StripFirstQuotedToken(L"  a b  "), L"b");
+  ASSERT_STREQ(StripFirstQuotedToken(L"\"abc\" def"), L" def");
+  ASSERT_STREQ(StripFirstQuotedToken(L"  \"abc def\" ghi  "), L" ghi");
+  ASSERT_STREQ(StripFirstQuotedToken(L"\"abc\"   \"def\" "), L"   \"def\"");
+}
+
+TEST(StringTest, EscapeUnescape) {
+  CString original_str(_T("test <>\"#{}|\\^[]?%&/"));
+  CString escaped_str;
+  ASSERT_SUCCEEDED(StringEscape(original_str, true, &escaped_str));
+  ASSERT_STREQ(escaped_str,
+               _T("test%20%3C%3E%22%23%7B%7D%7C%5C%5E%5B%5D%3F%25%26%2F"));
+  CString unescaped_str;
+  ASSERT_SUCCEEDED(StringUnescape(escaped_str, &unescaped_str));
+  ASSERT_STREQ(original_str, unescaped_str);
+
+  original_str = _T("foo.test path?app=1");
+  ASSERT_SUCCEEDED(StringEscape(original_str, false, &escaped_str));
+  ASSERT_STREQ(escaped_str,
+               _T("foo.test%20path?app=1"));
+  ASSERT_SUCCEEDED(StringUnescape(escaped_str, &unescaped_str));
+  ASSERT_STREQ(original_str, unescaped_str);
+}
+
+TEST(StringTest, ReplaceIgnoreCase) {
+  EXPECT_STREQ(_T("AddddAddddAB"),
+               String_ReplaceIgnoreCase(_T("ABCABCAB"), _T("bc"), _T("dddd")));
+  EXPECT_STREQ(_T("AdAdAd"),
+               String_ReplaceIgnoreCase(_T("ABCABCABC"), _T("bc"), _T("d")));
+}
+
+TEST(StringTest, String_StringToDecimalIntChecked) {
+  int value = 0;
+
+  // This code before the first valid case verifies that errno is properly
+  // cleared and there are no dependencies on prior code.
+  EXPECT_EQ(0, _set_errno(ERANGE));
+
+  // Valid Cases
+  EXPECT_TRUE(String_StringToDecimalIntChecked(_T("935"), &value));
+  EXPECT_EQ(value, 935);
+  EXPECT_TRUE(String_StringToDecimalIntChecked(_T("-935"), &value));
+  EXPECT_EQ(value, -935);
+  EXPECT_TRUE(String_StringToDecimalIntChecked(_T("0"), &value));
+  EXPECT_EQ(value, 0);
+  EXPECT_TRUE(String_StringToDecimalIntChecked(_T("2147483647"), &value));
+  EXPECT_EQ(value, LONG_MAX);
+  EXPECT_TRUE(String_StringToDecimalIntChecked(_T("-2147483648"), &value));
+  EXPECT_EQ(value, LONG_MIN);
+  EXPECT_TRUE(String_StringToDecimalIntChecked(_T(" 0"), &value));
+  EXPECT_EQ(value, 0);
+
+  // Failing Cases
+  EXPECT_FALSE(String_StringToDecimalIntChecked(_T(""), &value));
+  EXPECT_FALSE(String_StringToDecimalIntChecked(_T("2147483648"), &value));
+  EXPECT_EQ(value, LONG_MAX);
+  EXPECT_FALSE(String_StringToDecimalIntChecked(_T("-2147483649"), &value));
+  EXPECT_EQ(value, LONG_MIN);
+  EXPECT_FALSE(String_StringToDecimalIntChecked(_T("0x935"), &value));
+  EXPECT_FALSE(String_StringToDecimalIntChecked(_T("nine"), &value));
+  EXPECT_FALSE(String_StringToDecimalIntChecked(_T("9nine"), &value));
+  EXPECT_FALSE(String_StringToDecimalIntChecked(_T("nine9"), &value));
+
+  // A valid case after an overflow verifies that this method clears errno.
+  EXPECT_FALSE(String_StringToDecimalIntChecked(_T("2147483648"), &value));
+  EXPECT_TRUE(String_StringToDecimalIntChecked(_T("935"), &value));
+}
+TEST(StringTest, String_StringToTristate) {
+  Tristate value = TRISTATE_NONE;
+
+  // Valid Cases
+  EXPECT_TRUE(String_StringToTristate(_T("0"), &value));
+  EXPECT_EQ(value, TRISTATE_FALSE);
+  EXPECT_TRUE(String_StringToTristate(_T("1"), &value));
+  EXPECT_EQ(value, TRISTATE_TRUE);
+  EXPECT_TRUE(String_StringToTristate(_T("2"), &value));
+  EXPECT_EQ(value, TRISTATE_NONE);
+
+  // Invalid Cases
+  EXPECT_FALSE(String_StringToTristate(_T("-1"), &value));
+  EXPECT_FALSE(String_StringToTristate(_T("3"), &value));
+  EXPECT_FALSE(String_StringToTristate(_T(""), &value));
+}
+
+TEST(StringTest, ParseNameValuePair) {
+  CString name;
+  CString value;
+
+  // Valid Cases
+  EXPECT_TRUE(ParseNameValuePair(_T("xx=yyzz"), _T('='), &name, &value));
+  EXPECT_EQ(name, _T("xx"));
+  EXPECT_EQ(value, _T("yyzz"));
+  EXPECT_TRUE(ParseNameValuePair(_T("x=3?\\/\r\n "), _T('='), &name, &value));
+  EXPECT_EQ(name, _T("x"));
+  EXPECT_EQ(value, _T("3?\\/\r\n "));
+  EXPECT_TRUE(ParseNameValuePair(_T("3?google"), _T('?'), &name, &value));
+  EXPECT_EQ(name, _T("3"));
+  EXPECT_EQ(value, _T("google"));
+
+  // Invalid Cases
+  EXPECT_FALSE(ParseNameValuePair(_T(""), _T('='), &name, &value));
+  EXPECT_FALSE(ParseNameValuePair(_T(" "), _T('='), &name, &value));
+  EXPECT_FALSE(ParseNameValuePair(_T("="), _T('='), &name, &value));
+  EXPECT_FALSE(ParseNameValuePair(_T("x="), _T('='), &name, &value));
+  EXPECT_FALSE(ParseNameValuePair(_T("=y"), _T('='), &name, &value));
+  EXPECT_FALSE(ParseNameValuePair(_T("="), _T('='), &name, &value));
+  EXPECT_FALSE(ParseNameValuePair(_T("xxyyzz"), _T('='), &name, &value));
+  EXPECT_FALSE(ParseNameValuePair(_T("xx yyzz"), _T('='), &name, &value));
+  EXPECT_FALSE(ParseNameValuePair(_T("xx==yyzz"), _T('='), &name, &value));
+  EXPECT_FALSE(ParseNameValuePair(_T("xx=yy=zz"), _T('='), &name, &value));
+}
+
+TEST(StringTest, SplitCommandLineInPlace) {
+  const TCHAR * const test_long_paths[] = {
+    _T("c:\\Program Files\\Google\\App\\App.exe"),
+    _T("c:\\Program Files\\Google\\App\\Long Name App.exe"),
+  };
+  const TCHAR * const test_short_paths[] = {
+    _T("notepad.exe"),
+  };
+  const TCHAR * const test_arguments[] = {
+    _T("/a=\"some text\""),
+    _T("/a /b /c"),
+    _T(""),
+  };
+  TCHAR command_line[1024] = {};
+  TCHAR* path = NULL;
+  TCHAR* arguments = NULL;
+  for (int ii = 0; ii < ARRAYSIZE(test_arguments) ; ++ii) {
+    for (int jj = 0; jj < ARRAYSIZE(test_long_paths) ; ++jj) {
+      _snwprintf_s(command_line, ARRAYSIZE(command_line), _TRUNCATE,
+          _T("\"%s\" %s"), test_long_paths[jj], test_arguments[ii]);
+      EXPECT_EQ(true, SplitCommandLineInPlace(command_line, &path, &arguments));
+      EXPECT_STREQ(test_long_paths[jj], path);
+      EXPECT_STREQ(test_arguments[ii], arguments);
+    }
+    for (int kk = 0; kk < ARRAYSIZE(test_short_paths) ; ++kk) {
+      _snwprintf_s(command_line, ARRAYSIZE(command_line), _TRUNCATE,
+          _T("%s %s"), test_short_paths[kk], test_arguments[ii]);
+      EXPECT_EQ(true, SplitCommandLineInPlace(command_line, &path, &arguments));
+      EXPECT_STREQ(test_short_paths[kk], path);
+      EXPECT_STREQ(test_arguments[ii], arguments);
+    }
+  }
+}
+
+TEST(StringTest, ContainsOnlyAsciiChars) {
+  CString test(_T("hello worlr"));
+  ASSERT_TRUE(ContainsOnlyAsciiChars(test));
+
+  TCHAR non_ascii[] = {0x4345, 0x1234, 0x2000};
+  ASSERT_FALSE(ContainsOnlyAsciiChars(non_ascii));
+}
+TEST(StringTest, BytesToHex) {
+  EXPECT_STREQ(BytesToHex(NULL, 0), _T(""));
+  uint8 i = 0;
+  EXPECT_STREQ(BytesToHex(&i, sizeof(i)), _T("00"));
+  i = 0x7f;
+  EXPECT_STREQ(BytesToHex(&i, sizeof(i)), _T("7f"));
+  i = 0xff;
+  EXPECT_STREQ(BytesToHex(&i, sizeof(i)), _T("ff"));
+
+  // Assumes little-endian representation of integers.
+  const uint32 array[] = {0x67452301, 0xefcdab89};
+  EXPECT_STREQ(BytesToHex(reinterpret_cast<const uint8*>(array), sizeof(array)),
+               _T("0123456789abcdef"));
+
+  const uint8* first = reinterpret_cast<const uint8*>(array);
+  const uint8* last  = first + sizeof(array);
+  EXPECT_STREQ(BytesToHex(std::vector<uint8>(first, last)),
+               _T("0123456789abcdef"));
+}
+
+TEST(StringTest, JoinStrings) {
+  std::vector<CString> components;
+  const TCHAR* delim = _T("-");
+  CString result;
+
+  JoinStrings(components, delim, &result);
+  EXPECT_TRUE(result.IsEmpty());
+  JoinStrings(components, NULL, &result);
+  EXPECT_TRUE(result.IsEmpty());
+
+  components.push_back(CString(_T("foo")));
+  JoinStrings(components, delim, &result);
+  EXPECT_STREQ(result, (_T("foo")));
+  JoinStrings(components, NULL, &result);
+  EXPECT_STREQ(result, (_T("foo")));
+
+  components.push_back(CString(_T("bar")));
+  JoinStrings(components, delim, &result);
+  EXPECT_STREQ(result, (_T("foo-bar")));
+  JoinStrings(components, NULL, &result);
+  EXPECT_STREQ(result, (_T("foobar")));
+
+  components.push_back(CString(_T("baz")));
+  JoinStrings(components, delim, &result);
+  EXPECT_STREQ(result, (_T("foo-bar-baz")));
+  JoinStrings(components, NULL, &result);
+  EXPECT_STREQ(result, (_T("foobarbaz")));
+
+
+  JoinStringsInArray(NULL, 0, delim, &result);
+  EXPECT_TRUE(result.IsEmpty());
+  JoinStringsInArray(NULL, 0, NULL, &result);
+  EXPECT_TRUE(result.IsEmpty());
+
+  const TCHAR* array1[] = {_T("foo")};
+  JoinStringsInArray(array1, arraysize(array1), delim, &result);
+  EXPECT_STREQ(result, (_T("foo")));
+  JoinStringsInArray(array1, arraysize(array1), NULL, &result);
+  EXPECT_STREQ(result, (_T("foo")));
+
+  const TCHAR* array2[] = {_T("foo"), _T("bar")};
+  JoinStringsInArray(array2, arraysize(array2), delim, &result);
+  EXPECT_STREQ(result, (_T("foo-bar")));
+  JoinStringsInArray(array2, arraysize(array2), NULL, &result);
+  EXPECT_STREQ(result, (_T("foobar")));
+
+  const TCHAR* array3[] = {_T("foo"), _T("bar"), _T("baz")};
+  JoinStringsInArray(array3, arraysize(array3), delim, &result);
+  EXPECT_STREQ(result, (_T("foo-bar-baz")));
+  JoinStringsInArray(array3, arraysize(array3), NULL, &result);
+  EXPECT_STREQ(result, (_T("foobarbaz")));
+
+  const TCHAR* array_null_1[] = {NULL};
+  JoinStringsInArray(array_null_1, arraysize(array_null_1), delim, &result);
+  EXPECT_STREQ(result, (_T("")));
+
+  const TCHAR* array_null_2[] = {NULL, NULL};
+  JoinStringsInArray(array_null_2, arraysize(array_null_2), delim, &result);
+  EXPECT_STREQ(result, (_T("-")));
+}
+
+TEST(StringTest, String_ToUpper) {
+  // String_ToUpper is a wrapper over ::CharUpper.
+  TCHAR s[] = _T("foo");
+  String_ToUpper(s);
+  EXPECT_STREQ(s, _T("FOO"));
+}
+
+TEST(StringTest, FormatResourceMessage_Valid) {
+  EXPECT_STREQ(
+      _T("Thanks for installing Gears."),
+      FormatResourceMessage(IDS_BUNDLE_INSTALLED_SUCCESSFULLY, _T("Gears")));
+
+  EXPECT_STREQ(
+      _T("The installer encountered error 12345: Action failed."),
+      FormatResourceMessage(IDS_INSTALLER_FAILED_WITH_MESSAGE,
+                            _T("12345"),
+                            _T("Action failed.")));
+}
+
+TEST(StringTest, FormatResourceMessage_IdNotFound) {
+  EXPECT_STREQ(_T(""), FormatResourceMessage(100000, "foo", 9));
+}
+
+TEST(StringTest, FormatErrorCode) {
+  EXPECT_STREQ(_T("0xffffffff"), FormatErrorCode(static_cast<DWORD>(-1)));
+  EXPECT_STREQ(_T("0"), FormatErrorCode(0));
+  EXPECT_STREQ(_T("567"), FormatErrorCode(567));
+  EXPECT_STREQ(_T("2147483647"), FormatErrorCode(0x7fffffff));
+  EXPECT_STREQ(_T("0x80000000"), FormatErrorCode(0x80000000));
+  EXPECT_STREQ(_T("0x80000001"), FormatErrorCode(0x80000001));
+  EXPECT_STREQ(_T("0x8fffffff"), FormatErrorCode(0x8fffffff));
+}
+
+TEST(StringTest, Utf8BufferToWideChar) {
+  // Unicode Greek capital letters.
+  const TCHAR expected_string[] = {913, 914, 915, 916, 917, 918, 919, 920,
+                                   921, 922, 923, 924, 925, 926, 927, 928,
+                                   929, 931, 932, 933, 934, 935, 936, 937, 0};
+  // Greek capital letters UTF-8 encoded.
+  const char buffer[] = "ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ";
+  CString actual_string = Utf8BufferToWideChar(
+      std::vector<uint8>(buffer, buffer + arraysize(buffer)));
+  EXPECT_STREQ(expected_string, actual_string);
+
+  EXPECT_STREQ(_T(""), Utf8BufferToWideChar(std::vector<uint8>()));
+}
+
+TEST(StringTest, WideStringToUtf8UrlEncodedStringRoundTrip) {
+  CString unicode_string;
+  ASSERT_TRUE(unicode_string.LoadString(IDS_ESCAPE_TEST));
+
+  // Convert from unicode to a wide representation of utf8,url encoded string.
+  CString utf8encoded_str;
+  ASSERT_HRESULT_SUCCEEDED(WideStringToUtf8UrlEncodedString(unicode_string,
+                                                            &utf8encoded_str));
+  ASSERT_FALSE(utf8encoded_str.IsEmpty());
+
+  // Reconvert from the utf8, url encoded string to the wide version.
+  CString out;
+  ASSERT_HRESULT_SUCCEEDED(Utf8UrlEncodedStringToWideString(utf8encoded_str,
+                                                            &out));
+  ASSERT_FALSE(out.IsEmpty());
+  ASSERT_STREQ(unicode_string, out);
+}
+
+TEST(StringTest, WideStringToUtf8UrlEncodedStringEmptyString) {
+  CString unicode_string;
+
+  // Convert from unicode to a wide representation of utf8,url encoded string.
+  CString utf8encoded_str;
+  ASSERT_HRESULT_SUCCEEDED(WideStringToUtf8UrlEncodedString(unicode_string,
+                                                            &utf8encoded_str));
+  ASSERT_TRUE(utf8encoded_str.IsEmpty());
+}
+
+TEST(StringTest, WideStringToUtf8UrlEncodedStringSpaces) {
+  CString unicode_string(_T("   "));
+  CStringA expected("%20%20%20");
+  CString exp(expected);
+
+  // Convert from unicode to a wide representation of utf8,url encoded string.
+  CString utf8encoded_str;
+  ASSERT_HRESULT_SUCCEEDED(WideStringToUtf8UrlEncodedString(unicode_string,
+                                                            &utf8encoded_str));
+  ASSERT_STREQ(exp, utf8encoded_str);
+}
+
+TEST(StringTest, WideStringToUtf8UrlEncodedStringTestString) {
+  CString unicode_string(_T("Test Str/ing&values=&*^%$#"));
+  CStringA ansi_exp("Test%20Str/ing%26values=%26*%5E%$#");
+  CString exp(ansi_exp);
+
+  // Convert from unicode to a wide representation of utf8,url encoded string.
+  CString utf8encoded_str;
+  ASSERT_HRESULT_SUCCEEDED(WideStringToUtf8UrlEncodedString(unicode_string,
+                                                            &utf8encoded_str));
+  ASSERT_STREQ(exp, utf8encoded_str);
+}
+
+TEST(StringTest, WideStringToUtf8UrlEncodedStringSimpleTestString) {
+  CString unicode_string(_T("TestStr"));
+  CStringA ansi_exp("TestStr");
+  CString exp(ansi_exp);
+
+  // Convert from unicode to a wide representation of utf8,url encoded string.
+  CString utf8encoded_str;
+  ASSERT_HRESULT_SUCCEEDED(WideStringToUtf8UrlEncodedString(unicode_string,
+                                                            &utf8encoded_str));
+  ASSERT_STREQ(exp, utf8encoded_str);
+}
+
+TEST(StringTest, Utf8UrlEncodedStringToWideStringEmpty) {
+  // Convert from wide representation of utf8,url encoded string to unicode.
+  CString unicode_string;
+  CString utf8encoded_str;
+  ASSERT_HRESULT_SUCCEEDED(Utf8UrlEncodedStringToWideString(utf8encoded_str,
+                                                            &unicode_string));
+  ASSERT_TRUE(unicode_string.IsEmpty());
+}
+
+TEST(StringTest, Utf8UrlEncodedStringToWideStringSpaces) {
+  CString exp(_T("   "));
+  CStringA utf8("%20%20%20");
+  CString utf8encoded_str(utf8);
+
+  // Convert from wide representation of utf8,url encoded string to unicode.
+  CString unicode_string;
+  ASSERT_HRESULT_SUCCEEDED(Utf8UrlEncodedStringToWideString(utf8encoded_str,
+                                                            &unicode_string));
+  ASSERT_STREQ(exp, unicode_string);
+}
+
+TEST(StringTest, Utf8UrlEncodedStringToWideStringSimpleString) {
+  CString exp(_T("TestStr"));
+  CStringA utf8("TestStr");
+  CString utf8encoded_str(utf8);
+
+  // Convert from wide representation of utf8,url encoded string to unicode.
+  CString unicode_string;
+  ASSERT_HRESULT_SUCCEEDED(Utf8UrlEncodedStringToWideString(utf8encoded_str,
+                                                            &unicode_string));
+  ASSERT_STREQ(exp, unicode_string);
+}
+
+TEST(StringTest, ConvertFileUriToLocalPath) {
+  CString path_out;
+
+  EXPECT_HRESULT_SUCCEEDED(ConvertFileUriToLocalPath(
+    _T("file:///c:/test/path/one/test.txt"), &path_out));
+  EXPECT_STREQ(_T("c:\\test\\path\\one\\test.txt"), path_out);
+
+  EXPECT_HRESULT_SUCCEEDED(ConvertFileUriToLocalPath(
+    _T("file:///d:/path%20with%20spaces/test.txt"), &path_out));
+  EXPECT_STREQ(_T("d:\\path with spaces\\test.txt"), path_out);
+
+  EXPECT_HRESULT_SUCCEEDED(ConvertFileUriToLocalPath(
+    _T("file://e|/pipe_for_colon/test.txt"), &path_out));
+  EXPECT_STREQ(_T("e:\\pipe_for_colon\\test.txt"), path_out);
+
+  // The unescaped & is illegal in both URIs and Win32 paths, and the escaped
+  // bracket is illegal in a Win32 path.  However, we should pass them through
+  // and allow the file system layer to reject it at an appropriate spot.
+  EXPECT_HRESULT_SUCCEEDED(ConvertFileUriToLocalPath(
+    _T("file://f:/path_with_illegal_characters/te&st%60.txt"), &path_out));
+  EXPECT_STREQ(_T("f:\\path_with_illegal_characters\\te&st%60.txt"), path_out);
+
+  EXPECT_HRESULT_SUCCEEDED(ConvertFileUriToLocalPath(
+    _T("file://g:/url_has_get_arguments/test.txt?q=abcde"), &path_out));
+  EXPECT_STREQ(_T("g:\\url_has_get_arguments\\test.txt"), path_out);
+
+  EXPECT_HRESULT_FAILED(ConvertFileUriToLocalPath(
+    _T("i:\\actual_path_instead_of_url\\test.txt"), &path_out));
+  EXPECT_HRESULT_FAILED(ConvertFileUriToLocalPath(
+    _T("http://uri_uses_another_scheme.com/test.txt"), &path_out));
+  EXPECT_HRESULT_FAILED(ConvertFileUriToLocalPath(_T("not_a_uri"), &path_out));
+  EXPECT_HRESULT_FAILED(ConvertFileUriToLocalPath(_T(""), &path_out));
+}
+
+}  // namespace omaha
+
diff --git a/base/synchronized.cc b/base/synchronized.cc
new file mode 100644
index 0000000..d8a9b65
--- /dev/null
+++ b/base/synchronized.cc
@@ -0,0 +1,512 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Defines methods of classes used to encapsulate
+// the synchronization primitives.
+
+#include "omaha/base/synchronized.h"
+
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/system.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+typedef HANDLE WINAPI CreateMutexExFunction(
+  LPSECURITY_ATTRIBUTES attributes,
+  const WCHAR* name,
+  DWORD flags,
+  DWORD desired_access
+);
+
+#define CREATE_EVENT_MANUAL_RESET 0x01
+typedef HANDLE WINAPI CreateEventExFunction(
+  LPSECURITY_ATTRIBUTES attributes,
+  const WCHAR* name,
+  DWORD flags,
+  DWORD desired_access
+);
+
+CreateMutexExFunction* create_mutex_ex_function = NULL;
+CreateEventExFunction* create_event_ex_function = NULL;
+
+void EnsureCreateEx() {
+  if ((create_mutex_ex_function && create_event_ex_function) ||
+      !SystemInfo::IsRunningOnVistaOrLater()) {
+    return;
+  }
+
+  HMODULE kernel32_module = ::GetModuleHandle(_T("kernel32.dll"));
+  if (!kernel32_module) {
+    ASSERT(kernel32_module,
+           (_T("[GetModuleHandle error 0x%x]"), ::GetLastError()));
+    return;
+  }
+  GPA(kernel32_module, "CreateMutexExW", &create_mutex_ex_function);
+  ASSERT(create_mutex_ex_function,
+         (_T("[GPA error 0x%x]"), ::GetLastError()));
+
+  GPA(kernel32_module, "CreateEventExW", &create_event_ex_function);
+  ASSERT(create_event_ex_function,
+         (_T("[GPA error 0x%x]"), ::GetLastError()));
+}
+
+HANDLE CreateMutexWithSyncAccess(const TCHAR* name,
+                                 LPSECURITY_ATTRIBUTES lock_attributes) {
+  EnsureCreateEx();
+  if (create_mutex_ex_function) {
+    return create_mutex_ex_function(lock_attributes, name, 0,
+                                    SYNCHRONIZE |
+                                    MUTEX_MODIFY_STATE);  // for ReleaseMutex
+  }
+  return ::CreateMutex(lock_attributes, false, name);
+}
+
+HANDLE CreateEventWithSyncAccess(const TCHAR* name,
+                                 LPSECURITY_ATTRIBUTES event_attributes) {
+  EnsureCreateEx();
+  if (create_event_ex_function) {
+    return create_event_ex_function(event_attributes, name,
+                                    CREATE_EVENT_MANUAL_RESET,
+                                    SYNCHRONIZE |
+                                    EVENT_MODIFY_STATE);  // for Set/Reset, etc.
+  }
+  return ::CreateEvent(event_attributes, true, false, name);
+}
+
+// c-tor will take mutex.
+AutoSync::AutoSync(const Lockable *pLock)
+    : lock_(pLock),
+      first_time_(true) {
+  ASSERT(lock_, (L""));
+  VERIFY(lock_->Lock(), (L"Failed to lock in constructor"));
+}
+
+// c-tor will take mutex.
+AutoSync::AutoSync(const Lockable &rLock)
+    : lock_(&rLock),
+      first_time_(true) {
+  ASSERT(lock_, (L""));
+  VERIFY(lock_->Lock(), (L"Failed to lock in constructor"));
+}
+
+// d-tor will release mutex.
+AutoSync::~AutoSync() {
+  ASSERT(lock_, (L""));
+  VERIFY(lock_->Unlock(), (L"Failed to unlock in denstructor"));
+}
+
+// Allows to write the for loop of __mutexBlock macro
+bool AutoSync::FirstTime() {
+  if (first_time_) {
+     first_time_ = false;
+     return true;
+  }
+  return false;
+}
+
+// Constructor.
+GLock::GLock() : mutex_(NULL) {
+}
+
+bool GLock::InitializeWithSecAttr(const TCHAR* name,
+                                  LPSECURITY_ATTRIBUTES lock_attributes) {
+  ASSERT(!mutex_, (L""));
+
+#if defined(DEBUG) || defined(ASSERT_IN_RELEASE)
+  name_ = name;
+#endif
+
+  mutex_ = CreateMutexWithSyncAccess(name, lock_attributes);
+  return mutex_ != NULL;
+}
+
+// Create mutex return the status of creation. Sets to default DACL.
+bool GLock::Initialize(const TCHAR* name) {
+  return InitializeWithSecAttr(name, NULL);
+}
+
+// Clean up.
+GLock::~GLock() {
+  if (mutex_) {
+    VERIFY(::CloseHandle(mutex_), (_T("")));
+  }
+};
+
+// Wait until signaled.
+bool GLock::Lock() const {
+  return Lock(INFINITE);
+}
+
+bool GLock::Lock(DWORD dwMilliseconds) const {
+  ASSERT1(mutex_);
+
+  DWORD ret = ::WaitForSingleObject(mutex_, dwMilliseconds);
+  if (ret == WAIT_OBJECT_0) {
+    return true;
+  } else if (ret == WAIT_ABANDONED) {
+    UTIL_LOG(LE, (_T("[GLock::Lock - mutex was abandoned %s]"), name_));
+    return true;
+  }
+  return false;
+}
+
+// Release.
+bool GLock::Unlock() const {
+  ASSERT1(mutex_);
+
+  bool ret = (false != ::ReleaseMutex(mutex_));
+  ASSERT(ret, (_T("ReleaseMutex failed.  Err=%i"), ::GetLastError()));
+
+  return ret;
+}
+
+LLock::LLock() {
+  InitializeCriticalSection(&critical_section_);
+}
+
+LLock::~LLock() {
+  DeleteCriticalSection(&critical_section_);
+}
+
+bool LLock::Lock() const {
+  EnterCriticalSection(&critical_section_);
+  return true;
+}
+
+// not very precise funcion, but OK for our goals.
+bool LLock::Lock(DWORD wait_ms) const {
+  if (::TryEnterCriticalSection(&critical_section_))
+    return true;
+  DWORD ticks_at_the_begin_of_wait = GetTickCount();
+  do {
+    ::Sleep(0);
+    if (::TryEnterCriticalSection(&critical_section_)) {
+      return true;
+    }
+  } while (::GetTickCount() - ticks_at_the_begin_of_wait <  wait_ms);
+  return false;
+}
+
+bool LLock::Unlock() const {
+  LeaveCriticalSection(&critical_section_);
+  return true;
+}
+
+// LockCount is initialized to a value of -1; a value of 0 or greater indicates
+// that the critical section is held or owned. When LockCount is not equal
+// to -1, the OwningThread field contains the thread id that owns the section.
+DWORD LLock::GetOwner() const {
+  return critical_section_.LockCount != -1 ?
+         reinterpret_cast<DWORD>(critical_section_.OwningThread) : 0;
+}
+
+// Use this c-tor for interprocess gates.
+Gate::Gate(const TCHAR * event_name) : gate_(NULL) {
+  VERIFY(Initialize(event_name), (_T("")));
+}
+
+// Use this c-tor for in-process gates.
+Gate::Gate() : gate_(NULL) {
+  VERIFY(Initialize(NULL), (_T("")));
+}
+
+// clean up.
+Gate::~Gate() {
+  VERIFY(CloseHandle(gate_), (_T("")));
+}
+
+bool Gate::Initialize(const TCHAR * event_name) {
+  // event_name may be NULL
+  ASSERT1(gate_ == NULL);
+
+  // Create the event. The gate is initially closed.
+  // if this is in process gate we don't name event, otherwise we do.
+  // Created with default permissions.
+  gate_ = CreateEventWithSyncAccess(event_name, NULL);
+  return (NULL != gate_);
+}
+
+// Open the gate. Anyone can go through.
+bool Gate::Open() {
+  return FALSE != SetEvent(gate_);
+}
+
+// Shut the gate closed.
+bool Gate::Close() {
+  return FALSE != ResetEvent(gate_);
+}
+
+bool Gate::Wait(DWORD msec) {
+  return WAIT_OBJECT_0 == WaitForSingleObject(gate_, msec);
+}
+
+// Returns S_OK, and sets selected_gate to zero based index of the gate that
+// was opened
+// Returns E_FAIL if timeout occured or gate was abandoned.
+HRESULT Gate::WaitAny(Gate const * const *gates,
+                      int num_gates,
+                      DWORD msec,
+                      int *selected_gate) {
+  ASSERT1(selected_gate);
+  ASSERT1(gates);
+
+  return WaitMultipleHelper(gates, num_gates, msec, selected_gate, false);
+}
+
+// Returns S_OK if all gates were opened
+// Returns E_FAIL if timeout occured or gate was abandoned.
+HRESULT Gate::WaitAll(Gate const * const *gates, int num_gates, DWORD msec) {
+  ASSERT1(gates);
+
+  return WaitMultipleHelper(gates, num_gates, msec, NULL, true);
+}
+
+HRESULT Gate::WaitMultipleHelper(Gate const * const *gates,
+                                 int num_gates,
+                                 DWORD msec,
+                                 int *selected_gate,
+                                 bool wait_all) {
+  ASSERT1(gates);
+  ASSERT(num_gates > 0, (_T("There must be at least 1 gate")));
+
+  if ( num_gates <= 0 ) {
+    return E_FAIL;
+  }
+  HANDLE *gate_array = new HANDLE[ num_gates ];
+  ASSERT1(gate_array);
+  for ( int i = 0 ; i < num_gates ; ++i ) {
+    gate_array[ i ] = gates[ i ]->gate_;
+  }
+  DWORD res = WaitForMultipleObjects(num_gates,
+                                     gate_array,
+                                     wait_all ? TRUE : FALSE,
+                                     msec);
+  delete[] gate_array;
+
+#pragma warning(disable : 4296)
+// C4296: '>=' : expression is always true
+  if (WAIT_OBJECT_0 <= res && res < (WAIT_OBJECT_0 + num_gates)) {
+    if (selected_gate) {
+      *selected_gate = res - WAIT_OBJECT_0;
+    }
+    return S_OK;
+  }
+#pragma warning(default : 4296)
+  return E_FAIL;
+}
+
+bool WaitAllowRepaint(const Gate& gate, DWORD msec) {
+  DWORD wait = 0;
+  HANDLE gate_handle = gate;
+  while ((wait = ::MsgWaitForMultipleObjects(1,
+                                             &gate_handle,
+                                             FALSE,
+                                             msec,
+                                             QS_PAINT)) == WAIT_OBJECT_0 + 1) {
+    MSG msg;
+    if (::PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) ||
+        ::PeekMessage(&msg, NULL, WM_NCPAINT, WM_NCPAINT, PM_REMOVE)) {
+      ::TranslateMessage(&msg);
+      ::DispatchMessage(&msg);
+    }
+  }
+  return (wait == WAIT_OBJECT_0);
+}
+
+// SimpleLock
+// TODO(omaha): Replace InterlockedCompareExchange with
+// InterlockedCompareExchangeAcquire
+// and InterlockedDecrement with InterlockedDecrementRelease for Windows 2003
+
+bool SimpleLock::Lock() const {
+  while (1 == ::InterlockedCompareExchange(&lock_, 1, 0))
+    ::SleepEx(0, TRUE);
+  return true;
+}
+
+bool SimpleLock::Unlock() const {
+  ::InterlockedDecrement(&lock_);
+  return true;
+}
+
+// same with a delay in the loop to prevent CPU usage with significant
+// contention
+
+bool SimpleLockWithDelay::Lock() const {
+  while (1 == ::InterlockedCompareExchange(&lock_, 1, 0))
+    ::SleepEx(25, FALSE);
+  return true;
+}
+
+bool SimpleLockWithDelay::Unlock() const {
+  ::InterlockedDecrement(&lock_);
+  return true;
+}
+
+
+CriticalSection::CriticalSection()
+: number_entries_(0) {
+  InitializeCriticalSection(&critical_section_);
+}
+
+// allow only one thread to hold a lock
+CriticalSection::~CriticalSection() {
+  // we should not have to do anything in the destructor
+  ASSERT(!number_entries_, (_T("critical section destroyed while active")));
+  while (number_entries_) {
+    LeaveCriticalSection(&critical_section_);
+    number_entries_--;
+  }
+
+  DeleteCriticalSection(&critical_section_);
+}
+
+// enter the critical section
+// entries may be nested
+void CriticalSection::Enter() {
+  EnterCriticalSection(&critical_section_);
+  number_entries_++;
+}
+
+// exit the critical section
+// number of exits must match number of entries
+void CriticalSection::Exit() {
+  LeaveCriticalSection(&critical_section_);
+  number_entries_--;
+}
+
+// Take a CriticalSection and lock it
+SingleLock::SingleLock(CriticalSection * cs) {
+  ASSERT(cs, (L""));
+  critical_section_ = cs;
+  critical_section_->Enter();
+}
+
+// If we haven't freed it yet, do so now since we fell out of scope
+SingleLock::~SingleLock() {
+  if (critical_section_) {
+    critical_section_->Exit();
+    critical_section_ = NULL;
+  }
+}
+
+// Explicitly unlock
+HRESULT SingleLock::Unlock() {
+  // If they did not
+  if (critical_section_ == NULL)
+    return S_FALSE;
+
+  critical_section_->Exit();
+  critical_section_ = NULL;
+  return S_OK;
+}
+
+// Encapsulation for kernel Event. Initializes and destroys with it's lifetime
+void EventObj::Init(const TCHAR * event_name) {
+  ASSERT(event_name, (L""));
+
+  h_ = ::CreateEvent(NULL, false, false, event_name);
+  ASSERT1(h_);
+}
+
+EventObj::~EventObj() {
+  if (h_) {
+    VERIFY(CloseHandle(h_), (L""));
+    h_ = NULL;
+  }
+}
+
+BOOL EventObj::SetEvent() {
+  ASSERT(h_, (L""));
+  return ::SetEvent(h_);
+}
+
+// Is the given handle signaled?
+//
+// Typically used for events.
+bool IsHandleSignaled(HANDLE h) {
+  ASSERT(h != NULL &&
+         h != INVALID_HANDLE_VALUE, (_T("")));
+
+  DWORD result = ::WaitForSingleObject(h, 0);
+  if (result == WAIT_OBJECT_0) {
+    return true;
+  }
+
+  ASSERT(result == WAIT_TIMEOUT,
+         (_T("unexpected result value: %u (hr=0x%x)"),
+          result, HRESULTFromLastError()));
+  return false;
+}
+
+
+// Create an id for the events/mutexes that can be used at the given scope.
+// TODO(omaha): Error handling.
+void CreateSyncId(const TCHAR* id, SyncScope scope, CString* sync_id) {
+  ASSERT1(id);
+  ASSERT1(sync_id);
+
+  CString postfix;
+  switch (scope) {
+    default:
+      ASSERT1(false);
+      break;
+
+    case SYNC_LOCAL:
+      sync_id->SetString(_T("Local\\"));
+      // no postfix for local ids
+      break;
+
+    case SYNC_USER:
+    case SYNC_GLOBAL:
+      sync_id->SetString(_T("Global\\"));
+
+      if (scope == SYNC_GLOBAL) {
+        // (MSDN insists that you can create objects with the same name with the
+        // prefixes "Global\" and "Local\" in a system "running Terminal
+        // Services". And it also assures that XP when running Fast User
+        // Switching uses Terminal Services. But when you try to create two
+        // objects with the same name but in the different namespaces on an
+        // XP Pro workstation NOT running Fast User Switching you can't - you
+        // get ERROR_ALREADY_EXISTS. And the reason is that in the Object table,
+        // Global and Local are both symlinks to the same object directory.
+        // Yet every technique that you can use to interrogate the system on
+        // whether or not the system is "running Terminal Services" says that
+        // the system is, in fact, running Terminal Services.
+        // Which is exactly what you'd expect, yet you can't create the
+        // two objects with the same name in different workspaces.  So we change
+        // the name slightly.)
+        postfix.SetString(_T("_global"));
+      } else {
+        ASSERT1(scope == SYNC_USER);
+        // make the postfix the sid
+        VERIFY1(SUCCEEDED(omaha::user_info::GetProcessUser(NULL,
+                                                           NULL,
+                                                           &postfix)));
+      }
+      break;
+  }
+
+  sync_id->Append(id);
+  sync_id->Append(postfix);
+}
+
+}  // namespace omaha
+
diff --git a/base/synchronized.h b/base/synchronized.h
new file mode 100644
index 0000000..20d7208
--- /dev/null
+++ b/base/synchronized.h
@@ -0,0 +1,358 @@
+// Copyright 2004-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Declares some classes and macros to encapsulate
+// the synchronization primitives.
+
+// TODO(omaha): remove dependency on atlstr
+
+#ifndef OMAHA_BASE_SYNCHRONIZED_H_
+#define OMAHA_BASE_SYNCHRONIZED_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+// This macros are used to create a unique name
+// We need to go through two steps of expansion.
+// Macro kMakeName1 will expand to string + number
+// and macro kMakeName1 will put them together to create
+// the unique name.
+#define MAKE_NAME2(x, y) x##y
+#define MAKE_NAME1(x, y) MAKE_NAME2(x, y)
+#define MAKE_NAME(x) MAKE_NAME1(x, __COUNTER__)
+
+// Declare the interface in implement mutual
+// exclusion. For in process mutual exclusion
+// simple critical sections can be used. For
+// interprocess mutual exclusion some named
+// kernel mode object will have to be used.
+struct Lockable {
+  virtual ~Lockable() {}
+  virtual bool Lock() const = 0;
+  virtual bool Unlock() const = 0;
+};
+
+// Scope based mutual exclusion. Locks
+// the object on construction and unlocks
+// during destruction. Very convinient to use
+// with the macros __mutexScope and __mutexBlock
+class AutoSync {
+  bool first_time_;
+ public:
+  explicit AutoSync(const Lockable *pLock);
+  explicit AutoSync(const Lockable &rLock);
+  ~AutoSync();
+  // this function is only needed to use with
+  // the macro __mutexBlock
+  bool FirstTime();
+ private:
+  const Lockable * lock_;
+  DISALLOW_EVIL_CONSTRUCTORS(AutoSync);
+};
+
+// the usaage:
+// class A : public Lockable {
+//
+//
+//
+//  void foo(){
+//   __mutexScope(this);
+// ......
+// .......
+// everything is synchronized till the end of the
+// function or the time it returns (from any place)
+// } // end foo.
+//
+// void bar() {
+// ......
+// ...... do something here.
+// ......
+//   __mutexBlock(this){
+//    .... do some other stuff
+//    ....
+//    ....
+//    } everything is synchronized till here
+//
+// }; // end class A
+
+//
+#define __mutexScope(lock) AutoSync MAKE_NAME(hiddenLock)(lock)
+#define __mutexBlock(lock) \
+    for (AutoSync hiddenLock(lock); hiddenLock.FirstTime(); )
+
+// GLock stands for global lock.
+// Implementaion of Lockable to allow mutual exclusion
+// between different processes.
+// For in-process mutual exclusion use LLock - local lock
+class GLock : public Lockable {
+ public:
+  GLock();
+  virtual ~GLock();
+
+  // Create mutex returns the status of creation. Use ::GetLastError for
+  // error information.
+  bool InitializeWithSecAttr(const TCHAR* name,
+                             LPSECURITY_ATTRIBUTES lock_attributes);
+
+  // Create mutex return the status of creation. Sets to default DACL.
+  bool Initialize(const TCHAR* name);
+
+  virtual bool Lock() const;
+  virtual bool Lock(DWORD dwMilliseconds) const;
+  virtual bool Unlock() const;
+
+ private:
+#if defined(DEBUG) || defined(ASSERT_IN_RELEASE)
+  CString name_;
+#endif
+  mutable HANDLE mutex_;
+  DISALLOW_EVIL_CONSTRUCTORS(GLock);
+};
+
+// FakeGLock looks like a GLock, but none of its methods do anything.
+// Only used with SharedMemoryPtr, in cases where locking is not required or
+// desired.
+class FakeGLock : public Lockable {
+ public:
+  FakeGLock() {}
+  virtual ~FakeGLock() {}
+  bool InitializeWithSecAttr(const TCHAR*, LPSECURITY_ATTRIBUTES) {
+    return true;
+  }
+  bool Initialize(const TCHAR*) { return true; }
+  virtual bool Lock() const { return true; }
+  virtual bool Lock(DWORD) const { return true; }
+  virtual bool Unlock() const { return true; }
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(FakeGLock);
+};
+
+// LLock stands for local lock.
+// means works only inside the process.
+// use GLock - global lock for inter-process
+// guarded access to data.
+class LLock : public Lockable {
+ public:
+  LLock();
+  virtual ~LLock();
+  virtual bool Lock() const;
+  virtual bool Lock(DWORD wait_ms) const;
+  virtual bool Unlock() const;
+
+  // Returns the thread id of the owner or 0 if the lock is not owned.
+  DWORD GetOwner() const;
+ private:
+  mutable CRITICAL_SECTION        critical_section_;
+  DISALLOW_EVIL_CONSTRUCTORS(LLock);
+};
+
+// A gate is a synchronization object used to either stop all
+// threads from proceeding through a point or to allow them all to proceed.
+class Gate {
+ public:
+  // In process gate.
+  Gate();
+
+  // Interprocess gate.
+  explicit Gate(const TCHAR * event_name);
+
+  ~Gate();
+
+  // Open the gate.
+  bool Open();
+
+  // Close the gate.
+  bool Close();
+
+  // Wait to enter the gate.
+  bool Wait(DWORD msec);
+
+  // Conversion from the object to a HANDLE.
+  operator HANDLE() const {return gate_;}
+
+  // Returns S_OK, and sets selected_gate to zero based index of the gate that
+  // was opened.
+  // Returns E_FAIL if timeout occured or gate was abandoned.
+  static HRESULT WaitAny(Gate const * const *gates,
+                         int num_gates,
+                         DWORD msec,
+                         int *selected_gate);
+
+  // Returns S_OK if all gates were opened
+  // Returns E_FAIL if timeout occured or gate was abandoned.
+  static HRESULT WaitAll(Gate const * const *gates, int num_gates, DWORD msec);
+
+ private:
+  bool Initialize(const TCHAR * event_name);
+  static HRESULT WaitMultipleHelper(Gate const * const *gates,
+                                    int num_gates,
+                                    DWORD msec,
+                                    int *selected_gate,
+                                    bool wait_all);
+  HANDLE gate_;
+  DISALLOW_EVIL_CONSTRUCTORS(Gate);
+};
+
+bool WaitAllowRepaint(const Gate& gate, DWORD msec);
+
+class AutoGateKeeper {
+ public:
+  explicit AutoGateKeeper(Gate *gate) : gate_(gate) {
+    gate_->Open();
+  }
+  ~AutoGateKeeper() {
+    gate_->Close();
+  }
+ private:
+  Gate *gate_;
+  DISALLOW_EVIL_CONSTRUCTORS(AutoGateKeeper);
+};
+
+// A very simple rather fast lock - if uncontested.  USE ONLY AS A GLOBAL OBJECT
+// (i.e., DECLARED AT FILE SCOPE or as a STATIC CLASS MEMBER) - this is not
+// enforced.  Uses interlocked instructions on an int to get a fast user-mode
+// lock.  (Locks the bus and does a couple of memory references so it isn't
+// free.)  Spin-waits to get the lock.  Has the advantage that it needs no
+// initialization - thus has no order-of-evaluation problems with respect to
+// other global objects.  Does not work (causes deadlock) if locked twice by
+// the same thread. (Has no constructor so is initialized to 0 by C++.
+// This is why it must be a global or static class member: it doesn't initialize
+// itself to 0.  This is also why it doesn't inherit from Lockable, which would
+// make it need to initialize a virtual table.)
+struct SimpleLock {
+  bool Lock() const;
+  bool Unlock() const;
+ private:
+  mutable volatile long lock_;
+};
+
+struct SimpleLockWithDelay {
+  bool Lock() const;
+  bool Unlock() const;
+ private:
+  mutable volatile long lock_;
+};
+
+class AutoSimpleLock {
+ public:
+  explicit AutoSimpleLock(const SimpleLock& lock)
+      : lock_(lock) { lock_.Lock(); }
+  ~AutoSimpleLock() { lock_.Unlock(); }
+ private:
+  const SimpleLock& lock_;
+  DISALLOW_EVIL_CONSTRUCTORS(AutoSimpleLock);
+};
+
+class AutoSimpleLockWithDelay {
+ public:
+  explicit AutoSimpleLockWithDelay(const SimpleLockWithDelay& lock)
+      : lock_(lock) { lock_.Lock(); }
+  ~AutoSimpleLockWithDelay() { lock_.Unlock(); }
+ private:
+  const SimpleLockWithDelay& lock_;
+  DISALLOW_EVIL_CONSTRUCTORS(AutoSimpleLockWithDelay);
+};
+
+
+// allow only one thread to hold a lock
+class CriticalSection {
+ public:
+  CriticalSection();
+  ~CriticalSection();
+
+  void Enter();
+  void Exit();
+
+ private:
+  CRITICAL_SECTION critical_section_;
+  uint32 number_entries_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(CriticalSection);
+};
+
+// A class that manages a CriticalSection with its lifetime, you pass
+// it one in the constructor and then it will either be freed in the
+// destructor or implicitly [but only once]
+class SingleLock {
+ public:
+  // TODO(omaha): Not sure if immediately locking is a good idea;
+  // the API is asymmetrical (there's an Unlock but no Lock).
+
+  // Lock a critical section immediately
+  explicit SingleLock(CriticalSection * cs);
+
+  // If we have not explicitly unlocked it, this destructor will
+  ~SingleLock();
+
+  // Release the lock explicitly [should be called only once, after that
+  // does nothing. If we do not do so, the destructor will]
+  HRESULT Unlock();
+
+ private:
+  CriticalSection * critical_section_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(SingleLock);
+};
+
+// Encapsulation for kernel Event. Initializes and destroys with it's lifetime
+class EventObj {
+ public:
+  explicit EventObj(const TCHAR * event_name) {
+    Init(event_name);
+  }
+  ~EventObj();
+  void Init(const TCHAR * event_name);
+  BOOL SetEvent();
+  HANDLE GetHandle() { return h_; }
+
+ private:
+  HANDLE h_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(EventObj);
+};
+
+// Is the given handle signaled?
+//
+// Typically used for events.
+bool IsHandleSignaled(HANDLE h);
+
+
+enum SyncScope {
+  // local to a session
+  SYNC_LOCAL,
+
+  // global scope but the name is decorated to make it unique for the user
+  SYNC_USER,
+
+  // a globally scoped name
+  SYNC_GLOBAL,
+};
+
+// Create an id for the events/mutexes that can be used at the given scope
+void CreateSyncId(const TCHAR* id, SyncScope scope, CString* sync_id);
+
+// If any place needs to create a mutex that multiple
+// processes need to access, use this.
+HANDLE CreateMutexWithSyncAccess(const TCHAR* name,
+                                 LPSECURITY_ATTRIBUTES lock_attributes);
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_SYNCHRONIZED_H_
+
diff --git a/base/synchronized_unittest.cc b/base/synchronized_unittest.cc
new file mode 100644
index 0000000..6b9352a
--- /dev/null
+++ b/base/synchronized_unittest.cc
@@ -0,0 +1,34 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/synchronized.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(LLockTest, GetOwner) {
+  LLock lock;
+
+  EXPECT_EQ(0, lock.GetOwner());
+
+  EXPECT_TRUE(lock.Lock());
+  EXPECT_EQ(::GetCurrentThreadId(), lock.GetOwner());
+
+  EXPECT_TRUE(lock.Unlock());
+  EXPECT_EQ(0, lock.GetOwner());
+}
+
+}  // namespace omaha
+
diff --git a/base/system.cc b/base/system.cc
new file mode 100644
index 0000000..b60f183
--- /dev/null
+++ b/base/system.cc
@@ -0,0 +1,1086 @@
+// Copyright 2004-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/system.h"
+
+#include <objidl.h>
+#include <psapi.h>
+#include <winioctl.h>
+#include <wtsapi32.h>
+#include "omaha/base/commands.h"
+#include "omaha/base/commontypes.h"
+#include "omaha/base/const_config.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/disk.h"
+#include "omaha/base/dynamic_link_kernel32.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/module_utils.h"
+#include "omaha/base/path.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/string.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+
+namespace omaha {
+
+// Constant
+const TCHAR kNeedRebootHiddenFileSuffix[] = _T(".needreboot");
+
+HRESULT System::WaitForDiskActivity(const uint32 max_delay_milliseconds,
+                                    const uint32 sleep_time_ms,
+                                    uint32 *time_waited) {
+  ASSERT(time_waited, (L""));
+  uint32 sleep_time = sleep_time_ms;
+  if (sleep_time < 20) { sleep_time = 20; }
+  else if (sleep_time > 1000) { sleep_time = 1000; }
+  HRESULT r;
+  *time_waited = 0;
+  uint64 writes = 0;
+  uint64 new_writes = 0;
+  // get current counters
+  if (FAILED(r=GetDiskActivityCounters(NULL, &writes, NULL, NULL))) {
+    return r;
+  }
+
+  // wait until a write - reads may be cached
+  while (1) {
+    if (FAILED(r=GetDiskActivityCounters(NULL, &new_writes, NULL, NULL))) {
+      return r;
+    }
+    if (new_writes > writes) { return S_OK; }
+    if (*time_waited > max_delay_milliseconds) { return E_FAIL; }
+    SleepEx(sleep_time, TRUE);
+    *time_waited += sleep_time;
+  }
+}
+
+HRESULT System::GetDiskActivityCounters(uint64* reads,
+                                        uint64* writes,
+                                        uint64* bytes_read,
+                                        uint64* bytes_written) {
+  if (reads) {
+    *reads = 0;
+  }
+
+  if (writes) {
+    *writes = 0;
+  }
+
+  if (bytes_read) {
+    *bytes_read = 0;
+  }
+
+  if (bytes_written) {
+    *bytes_written = 0;
+  }
+
+  // Don't want to risk displaying UI errors here
+  DisableThreadErrorUI disable_error_dialog_box;
+
+  // for all drives
+  for (int drive = 0; ; drive++) {
+    struct _DISK_PERFORMANCE perf_data;
+    const int max_device_len = 50;
+
+    // check whether we can access this device
+    CString device_name;
+    device_name.Format(_T("\\\\.\\PhysicalDrive%d"), drive);
+    scoped_handle device(::CreateFile(device_name, 0,
+                                      FILE_SHARE_READ | FILE_SHARE_WRITE,
+                                      NULL, OPEN_EXISTING, 0, NULL));
+
+    if (get(device) == INVALID_HANDLE_VALUE) {
+      if (!drive) {
+        UTIL_LOG(LEVEL_ERROR, (_T("[Failed to access drive %i][0x%x]"),
+                               drive,
+                               HRESULTFromLastError()));
+      }
+      break;
+    }
+
+    // disk performance counters must be on (diskperf -y on older machines;
+    // defaults to on on newer windows)
+    DWORD size = 0;
+    if (::DeviceIoControl(get(device),
+                          IOCTL_DISK_PERFORMANCE,
+                          NULL,
+                          0,
+                          &perf_data,
+                          sizeof(_DISK_PERFORMANCE),
+                          &size,
+                          NULL)) {
+      if (reads) {
+        *reads += perf_data.ReadCount;
+      }
+
+      if (writes) {
+        *writes += perf_data.WriteCount;
+      }
+
+      if (bytes_read) {
+        *bytes_read += perf_data.BytesRead.QuadPart;
+      }
+
+      if (bytes_written) {
+        *bytes_written += perf_data.BytesWritten.QuadPart;
+      }
+    } else {
+      HRESULT hr = HRESULTFromLastError();
+      UTIL_LOG(LEVEL_ERROR,
+               (_T("[System::GetDiskActivityCounters - failed to ")
+                _T("DeviceIoControl][0x%x]"), hr));
+      return hr;
+    }
+  }
+
+  return S_OK;
+}
+
+HRESULT System::GetDiskStatistics(const TCHAR* path,
+                                  uint64 *free_bytes_current_user,
+                                  uint64 *total_bytes_current_user,
+                                  uint64 *free_bytes_all_users) {
+  ASSERT1(path);
+  ASSERT1(free_bytes_current_user);
+  ASSERT1(total_bytes_current_user);
+  ASSERT1(free_bytes_all_users);
+  ASSERT1(sizeof(LARGE_INTEGER) == sizeof(uint64));  // NOLINT
+
+  DisableThreadErrorUI disable_error_dialog_box;
+
+  if (!::GetDiskFreeSpaceEx(
+           path,
+           reinterpret_cast<PULARGE_INTEGER>(free_bytes_current_user),
+           reinterpret_cast<PULARGE_INTEGER>(total_bytes_current_user),
+           reinterpret_cast<PULARGE_INTEGER>(free_bytes_all_users))) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[Failed to GetDiskFreeSpaceEx][%s][0x%x]"), path, hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT System::GetProcessMemoryStatistics(uint64 *current_working_set,
+                                           uint64 *peak_working_set,
+                                           uint64 *min_working_set_size,
+                                           uint64 *max_working_set_size) {
+  HANDLE process_handle = GetCurrentProcess();
+  HRESULT hr = S_OK;
+
+  DWORD min_size(0), max_size(0);
+  if (GetProcessWorkingSetSize(process_handle, &min_size, &max_size)) {
+    UTIL_LOG(L2, (_T("[working set][min: %lu][max: %lu]"), min_size, max_size));
+    if (min_working_set_size) {
+      *min_working_set_size = min_size;
+    }
+    if (max_working_set_size) {
+      *max_working_set_size = max_size;
+    }
+  } else {
+    if (min_working_set_size) {
+      *min_working_set_size = 0;
+    }
+    if (max_working_set_size) {
+      *max_working_set_size = 0;
+    }
+    hr = E_FAIL;
+  }
+
+  if (current_working_set) { *current_working_set = 0; }
+  if (peak_working_set) { *peak_working_set = 0; }
+
+  // including this call (w/psapi.lib) adds 24k to the process memory
+  // according to task manager in one test, memory usage according to task
+  // manager increased by 4k after calling this
+  PROCESS_MEMORY_COUNTERS counters = { sizeof(counters), 0 };
+  if (GetProcessMemoryInfo(process_handle,
+                           &counters,
+                           sizeof(PROCESS_MEMORY_COUNTERS))) {
+    if (current_working_set) {
+      *current_working_set = counters.WorkingSetSize;
+    }
+    if (peak_working_set) {
+      *peak_working_set = counters.PeakWorkingSetSize;
+    }
+    UTIL_LOG(L2, (_T("[working set][current: %s][peak: %s]"),
+                  String_Int64ToString(*current_working_set, 10),
+                  String_Int64ToString(*peak_working_set, 10)));
+  } else {
+    if (current_working_set) {
+      *current_working_set = 0;
+    }
+    if (peak_working_set) {
+      *peak_working_set = 0;
+    }
+    hr = E_FAIL;
+  }
+
+  return hr;
+}
+
+HRESULT System::MaxPhysicalMemoryAvailable(uint64* max_bytes) {
+  ASSERT1(max_bytes);
+
+  *max_bytes = 0;
+
+  uint32 memory_load_percentage = 0;
+  uint64 free_physical_memory = 0;
+
+  RET_IF_FAILED(System::GetGlobalMemoryStatistics(&memory_load_percentage,
+    &free_physical_memory, NULL, NULL, NULL, NULL, NULL));
+
+  UTIL_LOG(L4, (_T("mem load %u max physical memory available %s"),
+                memory_load_percentage,
+                String_Int64ToString(free_physical_memory, 10)));
+
+  *max_bytes = free_physical_memory;
+
+  return S_OK;
+}
+
+HRESULT System::GetGlobalMemoryStatistics(uint32 *memory_load_percentage,
+                                          uint64 *free_physical_memory,
+                                          uint64 *total_physical_memory,
+                                          uint64 *free_paged_memory,
+                                          uint64 *total_paged_memory,
+                                          uint64 *process_free_virtual_memory,
+                                          uint64 *process_total_virtual_mem) {
+  MEMORYSTATUSEX status;
+  status.dwLength = sizeof(status);
+  if (!GlobalMemoryStatusEx(&status)) {
+    UTIL_LOG(LEVEL_ERROR, (_T("memory status error %u"), GetLastError()));
+    return E_FAIL;
+  }
+  if (memory_load_percentage) { *memory_load_percentage = status.dwMemoryLoad; }
+  if (free_physical_memory) { *free_physical_memory = status.ullAvailPhys; }
+  if (total_physical_memory) { *total_physical_memory = status.ullTotalPhys; }
+  if (free_paged_memory) { *free_paged_memory = status.ullAvailPageFile; }
+  if (total_paged_memory) { *total_paged_memory = status.ullTotalPageFile; }
+  if (process_free_virtual_memory) {
+    *process_free_virtual_memory = status.ullAvailVirtual;
+  }
+  if (process_total_virtual_mem) {
+    *process_total_virtual_mem = status.ullTotalVirtual;
+  }
+  // GetPerformanceInfo;
+  return S_OK;
+}
+
+void System::FreeProcessWorkingSet() {
+  // -1,-1 is a special signal to the OS to temporarily trim the working set
+  // size to 0.  See MSDN for further information.
+  ::SetProcessWorkingSetSize(::GetCurrentProcess(), (SIZE_T)-1, (SIZE_T)-1);
+}
+
+HRESULT System::SetThreadPriority(enum Priority priority) {
+  int pri;
+
+  switch (priority) {
+    case LOW: pri = THREAD_PRIORITY_BELOW_NORMAL; break;
+    case HIGH: pri = THREAD_PRIORITY_HIGHEST; break;
+    case NORMAL: pri = THREAD_PRIORITY_NORMAL; break;
+    case IDLE: pri = THREAD_PRIORITY_IDLE; break;
+    default: return E_FAIL;
+  }
+
+  if (::SetThreadPriority(GetCurrentThread(), pri)) {
+    return S_OK;
+  } else {
+    return E_FAIL;
+  }
+}
+
+HRESULT System::SetProcessPriority(enum Priority priority) {
+  DWORD pri = 0;
+  switch (priority) {
+    case LOW: pri = BELOW_NORMAL_PRIORITY_CLASS; break;
+    case HIGH: pri = ABOVE_NORMAL_PRIORITY_CLASS; break;
+    case NORMAL: pri = NORMAL_PRIORITY_CLASS; break;
+    case IDLE: return E_INVALIDARG;
+    default: return E_INVALIDARG;
+  }
+
+  DWORD pid = ::GetCurrentProcessId();
+
+  scoped_handle handle(::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid));
+  if (!valid(handle)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[::OpenProcess failed][%u][0x%x]"), pid, hr));
+    return hr;
+  }
+
+  if (!::SetPriorityClass(get(handle), pri)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[::SetPriorityClass failed][%u][0x%x]"), pid, hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+// start another process painlessly via ::CreateProcess. Use the
+// ShellExecuteProcessXXX variants instead of these methods where possible,
+// since ::ShellExecuteEx has better behavior on Windows Vista.
+// When using this method, avoid using process_name - see
+// http://blogs.msdn.com/oldnewthing/archive/2006/05/15/597984.aspx.
+HRESULT System::StartProcess(const TCHAR* process_name,
+                             TCHAR* command_line,
+                             PROCESS_INFORMATION* pi) {
+  ASSERT1(pi);
+  ASSERT1(command_line || process_name);
+  ASSERT(!process_name, (_T("Avoid using process_name. See method comment.")));
+
+  STARTUPINFO si = {sizeof(si), 0};
+
+  // Feedback cursor is off while the process is starting.
+  si.dwFlags = STARTF_FORCEOFFFEEDBACK;
+
+  UTIL_LOG(L3, (_T("[System::StartProcess][process %s][cmd %s]"),
+                process_name, command_line));
+
+  BOOL success = ::CreateProcess(
+      process_name,     // Module name
+      command_line,     // Command line
+      NULL,             // Process handle not inheritable
+      NULL,             // Thread handle not inheritable
+      FALSE,            // Set handle inheritance to FALSE
+      0,                // No creation flags
+      NULL,             // Use parent's environment block
+      NULL,             // Use parent's starting directory
+      &si,              // Pointer to STARTUPINFO structure
+      pi);              // Pointer to PROCESS_INFORMATION structure
+
+  if (!success) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[System::StartProcess][::CreateProcess failed][0x%x]"), hr));
+    return hr;
+  }
+
+  OPT_LOG(L1, (_T("[Started process][%u]"), pi->dwProcessId));
+
+  return S_OK;
+}
+
+// start another process painlessly via ::CreateProcess. Use the
+// ShellExecuteProcessXXX variants instead of these methods where possible,
+// since ::ShellExecuteEx has better behavior on Windows Vista.
+HRESULT System::StartProcessWithArgsAndInfo(const TCHAR *process_name,
+                                            const TCHAR *cmd_line_arguments,
+                                            PROCESS_INFORMATION *pi) {
+  ASSERT1(process_name && cmd_line_arguments && pi);
+
+  CString command_line(process_name);
+  EnclosePath(&command_line);
+  command_line.AppendChar(_T(' '));
+  command_line.Append(cmd_line_arguments);
+  return System::StartProcess(NULL, CStrBuf(command_line), pi);
+}
+
+// start another process painlessly via ::CreateProcess. Use the
+// ShellExecuteProcessXXX variants instead of these methods where possible,
+// since ::ShellExecuteEx has better behavior on Windows Vista.
+HRESULT System::StartProcessWithArgs(const TCHAR *process_name,
+                                     const TCHAR *cmd_line_arguments) {
+  ASSERT1(process_name && cmd_line_arguments);
+  PROCESS_INFORMATION pi = {0};
+  HRESULT hr = System::StartProcessWithArgsAndInfo(process_name,
+                                                   cmd_line_arguments,
+                                                   &pi);
+  if (SUCCEEDED(hr)) {
+    ::CloseHandle(pi.hProcess);
+    ::CloseHandle(pi.hThread);
+  }
+  return hr;
+}
+
+HRESULT System::StartCommandLine(const TCHAR* command_line_to_execute) {
+  ASSERT1(command_line_to_execute);
+
+  CString command_line(command_line_to_execute);
+  PROCESS_INFORMATION pi = {0};
+  HRESULT hr = System::StartProcess(NULL, CStrBuf(command_line), &pi);
+  if (SUCCEEDED(hr)) {
+    ::CloseHandle(pi.hProcess);
+    ::CloseHandle(pi.hThread);
+  }
+  return hr;
+}
+
+// TODO(omaha3): Unit test this method.
+HRESULT System::StartProcessAsUser(HANDLE user_token,
+                                   const CString& executable_path,
+                                   const CString& parameters,
+                                   LPWSTR desktop,
+                                   PROCESS_INFORMATION* pi) {
+  UTIL_LOG(L3, (_T("[StartProcessAsUser][%s][%s][%s]"),
+                executable_path, parameters, desktop));
+  ASSERT1(pi);
+
+  CString cmd(executable_path);
+  EnclosePath(&cmd);
+  cmd.AppendChar(_T(' '));
+  cmd.Append(parameters);
+
+  STARTUPINFO startup_info = { sizeof(startup_info) };
+  startup_info.lpDesktop = desktop;
+  DWORD creation_flags(0);
+
+  void* environment_block(NULL);
+  if (!::CreateEnvironmentBlock(&environment_block, user_token, TRUE)) {
+    HRESULT hr = HRESULTFromLastError();
+    ASSERT(false, (_T("[::CreateEnvironmentBlock failed][0x%x]"), hr));
+    return hr;
+  }
+
+  ON_SCOPE_EXIT(::DestroyEnvironmentBlock, environment_block);
+
+  creation_flags |= CREATE_UNICODE_ENVIRONMENT;
+  BOOL success = ::CreateProcessAsUser(user_token,
+                                       0,
+                                       CStrBuf(cmd, MAX_PATH),
+                                       0,
+                                       0,
+                                       false,
+                                       creation_flags,
+                                       environment_block,
+                                       0,
+                                       &startup_info,
+                                       pi);
+
+  if (!success) {
+    HRESULT hr(HRESULTFromLastError());
+    UTIL_LOG(LE, (_T("[::CreateProcessAsUser failed][%s][0x%x]"), cmd, hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+// start another process painlessly via ::ShellExecuteEx. Use this method
+// instead of the StartProcessXXX methods that use ::CreateProcess where
+// possible, since ::ShellExecuteEx has better behavior on Windows Vista.
+//
+// ShellExecuteExEnsureParent displays the PID of the started process if it is
+// returned. It is only returned if the mask includes SEE_MASK_NOCLOSEPROCESS.
+// Therefore, we always set this flag and pass a handle. If the caller did not
+// request the handle, we close it.
+HRESULT System::ShellExecuteProcess(const TCHAR* file_name_to_execute,
+                                    const TCHAR* command_line_parameters,
+                                    HWND hwnd,
+                                    HANDLE* process_handle) {
+  ASSERT1(file_name_to_execute);
+
+  UTIL_LOG(L3, (_T("[System::ShellExecuteProcess]")
+                _T("[file_name_to_execute '%s' command_line_parameters '%s']"),
+                file_name_to_execute, command_line_parameters));
+
+  SHELLEXECUTEINFO sei = {0};
+  sei.cbSize = sizeof(sei);
+  // SEE_MASK_NOZONECHECKS is set below to work around a problem in systems that
+  // had Internet Explorer 7 Beta installed. See http://b/804674.
+  // This only works for Windows XP SP1 and later.
+  sei.fMask = SEE_MASK_NOCLOSEPROCESS |  // Set hProcess to process handle.
+              SEE_MASK_FLAG_NO_UI     |  // Do not display an error message box.
+              SEE_MASK_NOZONECHECKS   |  // Do not perform a zone check.
+              SEE_MASK_NOASYNC;          // Wait to complete before returning.
+  sei.lpVerb = _T("open");
+  sei.lpFile = file_name_to_execute;
+  sei.lpParameters = command_line_parameters;
+  sei.nShow = SW_SHOWNORMAL;
+  sei.hwnd = hwnd;
+
+  // Use ShellExecuteExEnsureParent to ensure that we always have a parent
+  // window. We need to use the HWND property to be acknowledged as a foreground
+  // application on Windows Vista. Otherwise, the elevation prompt will appear
+  // minimized on the taskbar.
+  if (!ShellExecuteExEnsureParent(&sei)) {
+    HRESULT hr(HRESULTFromLastError());
+    OPT_LOG(LEVEL_ERROR, (_T("[Failed to ::ShellExecuteEx][%s][%s][0x%08x]"),
+                          file_name_to_execute, command_line_parameters, hr));
+    return hr;
+  }
+
+  if (process_handle) {
+    *process_handle = sei.hProcess;
+  } else {
+    ::CloseHandle(sei.hProcess);
+  }
+
+  return S_OK;
+}
+
+// start another process painlessly via ::ShellExecuteEx. Use this method
+// instead of the StartProcessXXX methods that use ::CreateProcess where
+// possible, since ::ShellExecuteEx has better behavior on Windows Vista.
+HRESULT System::ShellExecuteCommandLine(const TCHAR* command_line_to_execute,
+                                        HWND hwnd,
+                                        HANDLE* process_handle) {
+  ASSERT1(command_line_to_execute);
+
+  CString exe;
+  CString args;
+
+  HRESULT hr = CommandParsingSimple::SplitExeAndArgs(command_line_to_execute,
+                                                     &exe,
+                                                     &args);
+
+  if (SUCCEEDED(hr)) {
+    hr = System::ShellExecuteProcess(exe, args, hwnd, process_handle);
+    if (FAILED(hr)) {
+      UTIL_LOG(LEVEL_ERROR, (_T("[System::ShellExecuteProcess failed]")
+                             _T("[%s][%s][0x%08x]"), exe, args, hr));
+    }
+  }
+
+  return hr;
+}
+
+// returns the number of ms the system has had no user input
+int System::GetUserIdleTime() {
+  LASTINPUTINFO last_input_info;
+  last_input_info.cbSize = sizeof(LASTINPUTINFO);
+  // get time in windows ticks since system start of last activity
+  BOOL b = GetLastInputInfo(&last_input_info);
+  if (b == TRUE) {
+    return (GetTickCount()-last_input_info.dwTime);  // compute idle time
+  }
+  return 0;
+}
+
+bool System::IsUserIdle() {
+  // Only notify when the user has been idle less than this time
+  static int user_idle_threshold_ms = kUserIdleThresholdMs;
+
+  bool is_user_idle = (GetUserIdleTime() > user_idle_threshold_ms);
+  UTIL_LOG(L2, (_T("System::IsUserIdle() %s; user_idle_threshold_ms = %d"),
+                is_user_idle ? _T("TRUE") : _T("FALSE"),
+                user_idle_threshold_ms));
+  return is_user_idle;
+}
+
+bool System::IsUserBusy() {
+  // The user is busy typing or interacting with another application
+  // if the user is below the minimum threshold:
+  static int user_idle_min_threshold_ms = kUserIdleMinThresholdMs;
+  // The user is probably not paying attention
+  // if the user is above the maximum threshold:
+  static int user_idle_max_threshold_ms = kUserIdleMaxThresholdMs;
+
+  int user_idle_time = GetUserIdleTime();
+  bool is_user_busy = user_idle_time < user_idle_min_threshold_ms ||
+    user_idle_time > user_idle_max_threshold_ms;
+  UTIL_LOG(L2, (_T("[System::IsUserBusy() %s][user_idle_time = %d]")
+                _T("[user_idle_min_threshold_ms = %d]")
+                _T("[user_idle_max_threshold_ms = %d]"),
+                is_user_busy? _T("TRUE") : _T("FALSE"),
+                user_idle_time,
+                user_idle_min_threshold_ms,
+                user_idle_max_threshold_ms));
+  return is_user_busy;
+}
+
+bool System::IsScreensaverRunning() {
+  // NT 4.0 and below require testing OpenDesktop("screen-saver")
+  // We require W2K or better so we have an easier way
+  DWORD result = 0;
+  ::SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &result, 0);
+  bool is_screensaver_running = (result != FALSE);
+  UTIL_LOG(L2, (_T("System::IsScreensaverRunning() %s"),
+                is_screensaver_running? _T("TRUE") : _T("FALSE")));
+  return is_screensaver_running;
+}
+
+bool System::IsWorkstationLocked() {
+  bool is_workstation_locked = true;
+  HDESK inputdesk = ::OpenInputDesktop(0, 0, GENERIC_READ);
+  if (NULL != inputdesk)  {
+    TCHAR name[256];
+    DWORD needed = arraysize(name);
+    BOOL ok = ::GetUserObjectInformation(inputdesk,
+                                         UOI_NAME,
+                                         name,
+                                         sizeof(name),
+                                         &needed);
+    ::CloseDesktop(inputdesk);
+    if (ok) {
+      is_workstation_locked = (0 != lstrcmpi(name, NOTRANSL(_T("default"))));
+    }
+  }
+
+  UTIL_LOG(L2, (_T("System::IsWorkstationLocked() %s"),
+                is_workstation_locked? _T("TRUE") : _T("FALSE")));
+  return is_workstation_locked;
+}
+
+bool System::IsUserAway() {
+  return IsScreensaverRunning() || IsWorkstationLocked();
+}
+
+uint32 System::GetProcessHandleCount() {
+  typedef LONG (CALLBACK *Fun)(HANDLE, int32, PVOID, ULONG, PULONG);
+
+  // This new version of getting the number of open handles works on win2k.
+  HMODULE h = GetModuleHandle(_T("ntdll.dll"));
+  Fun NtQueryInformationProcess =
+      reinterpret_cast<Fun>(::GetProcAddress(h, "NtQueryInformationProcess"));
+
+  if (!NtQueryInformationProcess) {
+    UTIL_LOG(LEVEL_ERROR, (_T("[NtQueryInformationProcess failed][0x%x]"),
+                           HRESULTFromLastError()));
+    return 0;
+  }
+
+  DWORD count = 0;
+  VERIFY(NtQueryInformationProcess(GetCurrentProcess(),
+                                   kProcessHandleCount,
+                                   &count,
+                                   sizeof(count),
+                                   NULL) >= 0, (L""));
+
+  return count;
+}
+
+uint32 System::GetProcessHandleCountOld() {
+  typedef BOOL (CALLBACK * Fun)(HANDLE, PDWORD);
+
+  // GetProcessHandleCount not available on win2k
+  HMODULE handle = GetModuleHandle(_T("kernel32"));
+  Fun f = reinterpret_cast<Fun>(GetProcAddress(handle,
+                                               "GetProcessHandleCount"));
+
+  if (!f) return 0;
+
+  DWORD count = 0;
+  VERIFY((*f)(GetCurrentProcess(), &count), (L""));
+  return count;
+
+  //  DWORD GetGuiResources (HANDLE hProcess, DWORD uiFlags);
+  //  Parameters, hProcess
+  //  [in] Handle to the process. The handle must have the
+  //  PROCESS_QUERY_INFORMATION access right. For more information, see Process
+  //  Security and Access Rights.
+  //  uiFlags
+  //  [in] GUI object type. This parameter can be one of the following values.
+  //  Value          Meaning
+  //  GR_GDIOBJECTS  Return the count of GDI objects.
+  //  GR_USEROBJECTS Return the count of USER objects.
+}
+
+void System::GetGuiObjectCount(uint32 *gdi, uint32 *user) {
+  if (gdi) {
+    *gdi = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
+  }
+  if (user) {
+    *user = GetGuiResources(GetCurrentProcess(), GR_USEROBJECTS);
+  }
+}
+
+HRESULT System::GetRebootCheckDummyFileName(const TCHAR* base_file,
+                                            CString* dummy_file) {
+  ASSERT1(dummy_file);
+
+  if (base_file && *base_file) {
+    ASSERT1(File::Exists(base_file));
+    dummy_file->SetString(base_file);
+  } else {
+    RET_IF_FAILED(GetModuleFileName(NULL, dummy_file));
+  }
+  dummy_file->Append(_T(".needreboot"));
+  return S_OK;
+}
+
+// Is the system being rebooted?
+bool System::IsRebooted(const TCHAR* base_file) {
+  CString dummy_file;
+  if (SUCCEEDED(GetRebootCheckDummyFileName(base_file, &dummy_file))) {
+    if (File::Exists(dummy_file)) {
+      // If the file exists but it is not found in the
+      // PendingFileRenameOperations, (probably becaused that this key is messed
+      // up and thus the system restart fails to delete the file), re-add it
+      if (!File::AreMovesPendingReboot(dummy_file, true)) {
+        File::MoveAfterReboot(dummy_file, NULL);
+      }
+      return false;
+    } else {
+      return true;
+    }
+  }
+  return false;
+}
+
+// Mark the system as reboot required
+HRESULT System::MarkAsRebootRequired(const TCHAR* base_file) {
+  // Create a dummy file if needed
+  CString dummy_file;
+  RET_IF_FAILED(GetRebootCheckDummyFileName(base_file, &dummy_file));
+  if (File::Exists(dummy_file)) {
+    return S_OK;
+  }
+
+  File file;
+  RET_IF_FAILED(file.Open(dummy_file, true, false));
+  RET_IF_FAILED(file.Close());
+
+  // Hide it
+  DWORD file_attr = ::GetFileAttributes(dummy_file);
+  if (file_attr == INVALID_FILE_ATTRIBUTES ||
+      !::SetFileAttributes(dummy_file, file_attr | FILE_ATTRIBUTE_HIDDEN)) {
+    return HRESULTFromLastError();
+  }
+
+  // Mark it as being deleted after reboot
+  return File::MoveAfterReboot(dummy_file, NULL);
+}
+
+// Unmark the system as reboot required
+HRESULT System::UnmarkAsRebootRequired(const TCHAR* base_file) {
+  CString dummy_file;
+  RET_IF_FAILED(GetRebootCheckDummyFileName(base_file, &dummy_file));
+
+  return File::RemoveFromMovesPendingReboot(dummy_file, false);
+}
+
+// Restart the computer
+HRESULT System::RestartComputer() {
+  RET_IF_FAILED(AdjustPrivilege(SE_SHUTDOWN_NAME, true));
+
+  if (!::ExitWindowsEx(EWX_REBOOT, SHTDN_REASON_MAJOR_APPLICATION |
+                       SHTDN_REASON_MINOR_INSTALLATION |
+                       SHTDN_REASON_FLAG_PLANNED)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[System::RestartComputer - failed to")
+                           _T(" ExitWindowsEx][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+// The implementation works on all Windows versions. On NT and XP the screen
+// saver is actually stored in registry at
+// HKEY_CURRENT_USER\Control Panel\Desktop\SCRNSAVE.EXE but the
+// GetPrivateProfileString call is automatically mapped to the registry
+HRESULT System::GetCurrentScreenSaver(CString* fileName) {
+  if (!fileName) return E_POINTER;
+
+  DWORD nChars = ::GetPrivateProfileString(_T("boot"),
+                                           _T("SCRNSAVE.EXE"),
+                                           _T(""),
+                                           fileName->GetBuffer(MAX_PATH),
+                                           MAX_PATH,
+                                           _T("system.ini"));
+  fileName->ReleaseBufferSetLength(nChars);
+
+  return S_OK;
+}
+
+HRESULT System::CoCreateInstanceAsAdmin(HWND hwnd,
+                                        REFCLSID rclsid,
+                                        REFIID riid,
+                                        void** ppv) {
+  UTIL_LOG(L6, (_T("[CoCreateInstanceAsAdmin][%d][%s][%s]"),
+                hwnd, GuidToString(rclsid), GuidToString(riid)));
+
+  if (vista_util::IsUserAdmin()) {
+    return ::CoCreateInstance(rclsid, NULL, CLSCTX_LOCAL_SERVER, riid, ppv);
+  }
+
+  if (!SystemInfo::IsRunningOnVistaOrLater()) {
+    return E_ACCESSDENIED;
+  }
+
+  CString moniker_name(_T("Elevation:Administrator!new:"));
+  moniker_name += GuidToString(rclsid);
+  BIND_OPTS3 bo;
+  SetZero(bo);
+  bo.cbStruct = sizeof(bo);
+  bo.hwnd = hwnd;
+  bo.dwClassContext = CLSCTX_LOCAL_SERVER;
+
+  return ::CoGetObject(moniker_name, &bo, riid, ppv);
+}
+
+HRESULT System::IsPrivilegeEnabled(const TCHAR* privilege, bool* present) {
+  ASSERT1(privilege);
+  ASSERT1(present);
+
+  *present = false;
+
+  scoped_handle token;
+  if (!::OpenProcessToken(::GetCurrentProcess(),
+                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
+                          address(token))) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[System::IsPrivilegeEnabled - failed to ")
+                           _T("OpenProcessToken][0x%x]"), hr));
+    return hr;
+  }
+
+  LUID luid = {0};
+  if (!::LookupPrivilegeValue(NULL, privilege, &luid)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[System::IsPrivilegeEnabled - failed to")
+                           _T("LookupPrivilegeValue][0x%x]"), hr));
+    return hr;
+  }
+
+  PRIVILEGE_SET required_privilege = {0};
+  required_privilege.PrivilegeCount = 1;
+  required_privilege.Control = PRIVILEGE_SET_ALL_NECESSARY;
+  required_privilege.Privilege[0].Luid = luid;
+
+  BOOL result = FALSE;
+  if (!::PrivilegeCheck(get(token), &required_privilege, &result)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[System::IsPrivilegeEnabled - failed to")
+                           _T("PrivilegeCheck][0x%x]"), hr));
+    return hr;
+  }
+
+  if (required_privilege.Privilege[0].Attributes &
+      SE_PRIVILEGE_USED_FOR_ACCESS) {
+    *present = true;
+  }
+
+  return S_OK;
+}
+
+// Attempts to adjust current process privileges.
+// Only process running with administrator privileges will succeed.
+HRESULT System::AdjustPrivilege(const TCHAR* privilege, bool enable) {
+  ASSERT1(privilege);
+
+  scoped_handle token;
+  if (!::OpenProcessToken(::GetCurrentProcess(),
+                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
+                          address(token))) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[System::AdjustPrivilege - failed to ")
+                           _T("OpenProcessToken][0x%x]"), hr));
+    return hr;
+  }
+
+  LUID luid = {0};
+  if (!::LookupPrivilegeValue(NULL, privilege, &luid)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[System::AdjustPrivilege - failed to")
+                           _T("LookupPrivilegeValue][0x%x]"), hr));
+    return hr;
+  }
+
+  TOKEN_PRIVILEGES privs;
+  privs.PrivilegeCount = 1;
+  privs.Privileges[0].Luid = luid;
+  privs.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
+
+  if (!::AdjustTokenPrivileges(get(token), FALSE, &privs, 0, NULL, 0)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[System::AdjustPrivilege - failed to ")
+                           _T("AdjustTokenPrivileges][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+DWORD System::WTSGetActiveConsoleSessionId()  {
+  typedef DWORD (* Fun)();
+
+  HINSTANCE hInst = ::GetModuleHandle(_T("kernel32.dll"));
+  ASSERT1(hInst);
+  Fun pfn = reinterpret_cast<Fun>(::GetProcAddress(
+                                      hInst,
+                                      "WTSGetActiveConsoleSessionId"));
+  return !pfn ? kInvalidSessionId : (*pfn)();
+}
+
+// Get the session the current process is running under
+DWORD System::GetCurrentSessionId() {
+  DWORD session_id = kInvalidSessionId;
+  DWORD* session_id_ptr = NULL;
+  DWORD bytes_returned = 0;
+
+  if (::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,
+                                   WTS_CURRENT_SESSION,
+                                   WTSSessionId,
+                                   reinterpret_cast<LPTSTR*>(&session_id_ptr),
+                                   &bytes_returned)) {
+    ASSERT1(bytes_returned == sizeof(*session_id_ptr));
+    session_id = *session_id_ptr;
+    ::WTSFreeMemory(session_id_ptr);
+    UTIL_LOG(L6, (_T("[System::GetCurrentSessionId]")
+                  _T("[session_id from ::WTSQuerySessionInformation][%d]"),
+                  session_id));
+    return session_id;
+  }
+
+  // ::WTSQuerySessionInformation can fail if we are not running
+  // in a Terminal Services scenario, in which case, we use
+  // ::ProcessIdToSessionId()
+  if (::ProcessIdToSessionId(::GetCurrentProcessId(), &session_id)) {
+    UTIL_LOG(L6,  (_T("[System::GetCurrentSessionId]")
+                   _T("[session_id from ::ProcessIdToSessionId][%d]"),
+                   session_id));
+    return session_id;
+  }
+
+  UTIL_LOG(LEVEL_ERROR,
+           (_T("[System::GetCurrentSessionId - both")
+            _T("::WTSQuerySessionInformation and ")
+            _T("::ProcessIdToSessionId failed][0x%x]"),
+            ::GetLastError()));
+
+  return kInvalidSessionId;
+}
+
+// Get the best guess as to the currently active session, or kInvalidSessionId
+// if there is no active session.
+DWORD System::GetActiveSessionId() {
+  // WTSGetActiveConsoleSessionId retrieves the Terminal Services session
+  // currently attached to the physical console.
+  DWORD active_session_id = WTSGetActiveConsoleSessionId();
+
+  if (IsSessionActive(active_session_id)) {
+    UTIL_LOG(L6, (_T("[System::GetActiveSessionId]")
+                  _T("[Active session id from ::WTSGetActiveConsoleSessionId]")
+                  _T("[%d]"), active_session_id));
+
+    return active_session_id;
+  }
+
+  // WTSGetActiveConsoleSessionId works for FUS, but it does not work for TS
+  // servers where the current active session is always the console. We then use
+  // a different method as below. We get all the sessions that are present on
+  // the system, to see if we can find an active session.
+  active_session_id = kInvalidSessionId;
+  WTS_SESSION_INFO* session_info = NULL;
+  DWORD num_sessions = 0;
+  if (::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1,
+                              &session_info, &num_sessions)) {
+    // Pick the first active session we can find
+    for (DWORD i = 0 ; i < num_sessions; ++i) {
+      if (session_info[i].State == WTSActive) {
+        // There is a user logged on to the WinStation associated with the
+        // session.
+        active_session_id = session_info[i].SessionId;
+        break;
+      }
+    }
+
+    ::WTSFreeMemory(session_info);
+    UTIL_LOG(L6, (_T("[System::GetActiveSessionId]")
+                  _T("[Active session id from ::WTSEnumerateSessions][0x%x]"),
+                  active_session_id));
+
+    return active_session_id;
+  }
+
+  UTIL_LOG(LEVEL_ERROR,
+           (_T("[System::GetActiveSessionId - ")
+           _T("Both ::WTSGetActiveConsoleSessionId and ::WTSEnumerateSessions ")
+           _T("failed][0x%x]"),
+           ::GetLastError()));
+
+  return kInvalidSessionId;
+}
+
+// Is there a user logged on and active in the specified session?
+bool System::IsSessionActive(DWORD session_id) {
+  if (kInvalidSessionId == session_id) {
+    return false;
+  }
+
+  WTS_CONNECTSTATE_CLASS wts_connect_state = WTSDisconnected;
+  WTS_CONNECTSTATE_CLASS* ptr_wts_connect_state = NULL;
+  DWORD bytes_returned = 0;
+  if (::WTSQuerySessionInformation(
+          WTS_CURRENT_SERVER_HANDLE,
+          session_id,
+          WTSConnectState,
+          reinterpret_cast<LPTSTR*>(&ptr_wts_connect_state),
+          &bytes_returned)) {
+    ASSERT1(bytes_returned == sizeof(*ptr_wts_connect_state));
+    wts_connect_state = *ptr_wts_connect_state;
+    ::WTSFreeMemory(ptr_wts_connect_state);
+
+    UTIL_LOG(L6, (_T("[System::IsSessionActive]")
+                  _T("[wts_connect_state %d]"), wts_connect_state));
+    return WTSActive == wts_connect_state;
+  }
+
+  UTIL_LOG(LE, (_T("[WTSQuerySessionInformation failed][0x%x]"),
+                ::GetLastError()));
+  return false;
+}
+
+// Is the current process running under WinSta0
+bool System::IsCurrentProcessInteractive() {
+  // Use a non-scoped handle, since a handle retrieved via
+  // ::GetProcessWindowStation() should not be closed.
+  HWINSTA handle_window_station(::GetProcessWindowStation());
+  DWORD len = 0;
+  CString str_window_station;
+
+  if (!handle_window_station || handle_window_station == INVALID_HANDLE_VALUE) {
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[System::IsCurrentProcessInteractive - ")
+              _T("::GetProcessWindowStation() failed (%d)]"),
+              ::GetLastError()));
+    return false;
+  }
+
+  if (!::GetUserObjectInformation(handle_window_station,
+                                  UOI_NAME,
+                                  CStrBuf(str_window_station, MAX_PATH),
+                                  MAX_PATH,
+                                  &len)) {
+    UTIL_LOG(LEVEL_ERROR,
+             (_T("[System::IsCurrentProcessInteractive - ")
+              _T("::GetUserObjectInfoformation(hWinSta) failed (%d)]"),
+              ::GetLastError()));
+    return false;
+  }
+
+  UTIL_LOG(L6, (_T("[System::IsCurrentProcessInteractive]")
+                _T("[WindowStation name][%s]"),
+                str_window_station));
+  return (str_window_station == _T("WinSta0"));
+}
+
+// is the current process running under WinSta0 for the currently active session
+bool System::IsCurrentProcessActiveAndInteractive() {
+  return IsSessionActive(GetCurrentSessionId()) &&
+         IsCurrentProcessInteractive();
+}
+
+bool System::IsRunningOnBatteries() {
+  SYSTEM_POWER_STATUS system_power_status = {0};
+  if (::GetSystemPowerStatus(&system_power_status)) {
+    bool has_battery = !(system_power_status.BatteryFlag & 128);
+    bool ac_status_offline = system_power_status.ACLineStatus == 0;
+    return ac_status_offline && has_battery;
+  }
+  return false;
+}
+
+}  // namespace omaha
+
diff --git a/base/system.h b/base/system.h
new file mode 100644
index 0000000..9285b6c
--- /dev/null
+++ b/base/system.h
@@ -0,0 +1,231 @@
+// Copyright 2004-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// system functions for checking disk space / memory usage / etc.
+
+#ifndef OMAHA_BASE_SYSTEM_H_
+#define OMAHA_BASE_SYSTEM_H_
+
+#include <atlstr.h>
+#include <base/basictypes.h>
+
+namespace omaha {
+
+#define kMaxRegistryBackupWaitMs 3000
+#define kMaxRegistryRestoreWaitMs 30000
+
+// amount of time the user must have no input before we declare them idle
+// used by outlook addin, outlook_cap, and filecap.
+#define kUserIdleThresholdMs 30000
+
+// The user is busy typing or interacting with another application
+// if the user is below the minimum threshold.
+#define kUserIdleMinThresholdMs 30000
+
+// The user is probably not paying attention
+// if the user is above the maximum threshold.
+#define kUserIdleMaxThresholdMs 600000
+
+const DWORD kInvalidSessionId = 0xFFFFFFFF;
+
+class System {
+  public:
+
+    // disk activity.
+
+    // waits up to specified time for disk activity to occur; sleeps in
+    // increments of sleep_time.
+    static HRESULT WaitForDiskActivity(uint32 max_delay_milliseconds,
+                                       uint32 sleep_time_ms,
+                                       uint32 *time_waited);
+    // disk activity counters; may require admin on some machines? should return
+    // E_FAIL if so.
+    static HRESULT GetDiskActivityCounters(uint64 *reads,
+                                           uint64 *writes,
+                                           uint64 *bytes_read,
+                                           uint64 *bytes_written);
+
+    // disk statistics.
+
+    // disk total and free space.
+    // Path is either the root of a drive or an existing folder on a drive; the
+    // statistics are for that drive.
+    static HRESULT GetDiskStatistics(const TCHAR* path,
+                                     uint64 *free_bytes_current_user,
+                                     uint64 *total_bytes_current_user,
+                                     uint64 *free_bytes_all_users);
+
+    enum Priority {
+        LOW,
+        HIGH,
+        NORMAL,
+        IDLE
+    };
+
+    // functions to alter process/thread priority.
+    static HRESULT SetThreadPriority(enum Priority priority);
+    static HRESULT SetProcessPriority(enum Priority priority);
+
+    // The three functions below start background processes via ::CreateProcess.
+    // Use the ShellExecuteProcessXXX functions when starting foreground
+    // processes.
+    static HRESULT StartProcessWithArgs(const TCHAR *process_name,
+                                        const TCHAR *cmd_line_arguments);
+    static HRESULT StartProcessWithArgsAndInfo(const TCHAR *process_name,
+                                               const TCHAR *cmd_line_arguments,
+                                               PROCESS_INFORMATION *pi);
+    static HRESULT StartCommandLine(const TCHAR* command_line_to_execute);
+    static HRESULT StartProcess(const TCHAR *process_name,
+                                TCHAR *command_line,
+                                PROCESS_INFORMATION *pi);
+
+    // Start the process with the provided token, in the specified desktop of
+    // the token's session. The caller needs to be SYSTEM.
+    static HRESULT StartProcessAsUser(HANDLE user_token,
+                                      const CString& executable_path,
+                                      const CString& parameters,
+                                      LPWSTR desktop,
+                                      PROCESS_INFORMATION* pi);
+
+    // start another process painlessly via ::ShellExecuteEx. Use this method
+    // instead of the StartProcessXXX methods that use ::CreateProcess where
+    // possible, since ::ShellExecuteEx has better behavior on Vista.
+    static HRESULT ShellExecuteProcess(const TCHAR* file_name_to_execute,
+                                       const TCHAR* command_line_parameters,
+                                       HWND hwnd,
+                                       HANDLE* process_handle);
+
+    // start another process painlessly via ::ShellExecuteEx. Use this method
+    // instead of the StartProcessXXX methods that use ::CreateProcess where
+    // possible, since ::ShellExecuteEx has better behavior on Vista.
+    static HRESULT ShellExecuteCommandLine(const TCHAR* command_line_to_execute,
+                                           HWND hwnd,
+                                           HANDLE* process_handle);
+
+    // memory statistics.
+
+    // max amount of memory that can be allocated without paging.
+    static HRESULT MaxPhysicalMemoryAvailable(uint64 *max_bytes);
+
+    // global memory stats
+    static HRESULT GetGlobalMemoryStatistics(
+                       uint32 *memory_load_percentage,
+                       uint64 *free_physical_memory,
+                       uint64 *total_physical_memory,
+                       uint64 *free_paged_memory,
+                       uint64 *total_paged_memory,
+                       uint64 *process_free_virtual_memory,
+                       uint64 *process_total_virtual_memory);
+
+    // process memory stats
+    static HRESULT GetProcessMemoryStatistics(uint64 *current_working_set,
+                                              uint64 *peak_working_set,
+                                              uint64 *min_working_set_size,
+                                              uint64 *max_working_set_size);
+
+    // TODO(omaha): determine if using this where we do with machines
+    // with slow disks causes noticeable slowdown
+
+    // reduce process working set - beware of possible negative performance
+    // implications - this function frees (to the page cache) all used pages,
+    // minimizing the working set - but could lead to additional page faults
+    // when the process continues. If the process continues soon enough the
+    // pages will still be in the page cache so they'll be relatively cheap
+    // soft page faults. This function is best used to reduce memory footprint
+    // when a component is about to go idle for "awhile".
+    static void FreeProcessWorkingSet();
+
+    // returns the number of ms the system has had no user input.
+    static int GetUserIdleTime();
+
+    // from ntddk.h, used as a parameter to get the process handle count.
+    static const int kProcessHandleCount = 20;
+    static uint32 GetProcessHandleCount();
+    static uint32 GetProcessHandleCountOld();
+
+    static void GetGuiObjectCount(uint32 *gdi, uint32 *user);
+
+    static bool IsUserIdle();
+    static bool IsUserBusy();
+    static bool IsScreensaverRunning();
+    static bool IsWorkstationLocked();
+    static bool IsUserAway();
+
+    // Is the system requiring reboot.
+    static bool IsRebooted(const TCHAR* base_file);
+
+    // Mark the system as reboot required.
+    static HRESULT MarkAsRebootRequired(const TCHAR* base_file);
+
+    // Unmark the system as reboot required.
+    static HRESULT UnmarkAsRebootRequired(const TCHAR* base_file);
+
+    // Restart the computer.
+    static HRESULT RestartComputer();
+
+    // Get the full path name of the screen saver program currently selected.
+    // If no screen saver is selected then "fileName" is empty.
+    static HRESULT GetCurrentScreenSaver(CString* fileName);
+
+    // Create an instance of a COM Local Server class using either plain vanilla
+    // CoCreateInstance, or using the Elevation moniker if Vista or later and
+    // the user is not an elevated admin.
+    static HRESULT CoCreateInstanceAsAdmin(HWND hwnd,
+                                           REFCLSID rclsid,
+                                           REFIID riid,
+                                           void** ppv);
+
+    // Attempts to adjust current process privileges.
+    // Only process running with administrator privileges will succeed.
+    static HRESULT AdjustPrivilege(const TCHAR* privilege, bool enable);
+
+    // Checks if the given privilege is enabled for the current process.
+    static HRESULT IsPrivilegeEnabled(const TCHAR* privilege, bool* present);
+
+    // Dynamically links and calls ::WTSGetActiveConsoleSessionId(). Returns
+    // kInvalidSessionId if it cannot find the export in kernel32.dll.
+    static DWORD WTSGetActiveConsoleSessionId();
+
+    // Get the session the current process is running under.
+    static DWORD GetCurrentSessionId();
+
+    // Get the best guess as to the currently active session,
+    // or kInvalidSessionId if there is no active session.
+    static DWORD GetActiveSessionId();
+
+    // Is there a user logged on and active in the specified session?
+    static bool IsSessionActive(DWORD session_id);
+
+    // Is the current process running under WinSta0.
+    static bool IsCurrentProcessInteractive();
+
+    // is the current process running under WinSta0 for the currently active
+    // session.
+    static bool IsCurrentProcessActiveAndInteractive();
+
+    // Returns true if a system battery is detected and the AC line
+    // status is 'offline', otherwise it returns false.
+    static bool IsRunningOnBatteries();
+
+  private:
+    static HRESULT GetRebootCheckDummyFileName(const TCHAR* base_file,
+                                               CString* dummy_file);
+    DISALLOW_EVIL_CONSTRUCTORS(System);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_SYSTEM_H_
+
diff --git a/base/system_info.cc b/base/system_info.cc
new file mode 100644
index 0000000..3df007e
--- /dev/null
+++ b/base/system_info.cc
@@ -0,0 +1,467 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/system_info.h"
+#include "base/basictypes.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/process.h"
+#include "omaha/base/string.h"
+
+namespace omaha {
+
+bool SystemInfo::OSWinXPSP2OrLater() {
+  OSVersionType os_type(OS_WINDOWS_UNKNOWN);
+  DWORD sp(0);
+
+  HRESULT hr = CategorizeOS(&os_type, &sp);
+  if (FAILED(hr)) {
+    ASSERT(false, (_T("[CategorizeOS failed][0x%x]"), hr));
+    return false;
+  }
+
+  return ((os_type == SystemInfo::OS_WINDOWS_XP && sp >= 2) ||
+          os_type > SystemInfo::OS_WINDOWS_XP);
+}
+
+bool SystemInfo::IsRunningOnW2K() {
+  OSVERSIONINFO os_info = {0};
+  os_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+
+  if (!::GetVersionEx(&os_info)) {
+    ASSERT(false, (_T("GetVersionEx")));
+    return false;
+  }
+
+  return os_info.dwMajorVersion == 5 && os_info.dwMinorVersion == 0;
+}
+
+bool SystemInfo::IsRunningOnXPOrLater() {
+  OSVersionType os_type(OS_WINDOWS_UNKNOWN);
+
+  HRESULT hr = CategorizeOS(&os_type, NULL);
+  if (FAILED(hr)) {
+    ASSERT(false, (_T("[Failed to get os type][0x%x]"), hr));
+    return false;
+  }
+
+  return os_type >= SystemInfo::OS_WINDOWS_XP;
+}
+
+bool SystemInfo::IsRunningOnXPSP1OrLater() {
+  OSVersionType os_type(OS_WINDOWS_UNKNOWN);
+  DWORD sp(0);
+
+  HRESULT hr = CategorizeOS(&os_type, &sp);
+  if (FAILED(hr)) {
+    ASSERT(false, (_T("[Failed to get os type][0x%x]"), hr));
+    return false;
+  }
+
+  return ((os_type == SystemInfo::OS_WINDOWS_XP && sp >= 1) ||
+          os_type > SystemInfo::OS_WINDOWS_XP);
+}
+
+
+bool SystemInfo::IsRunningOnVistaOrLater() {
+  OSVersionType os_type(OS_WINDOWS_UNKNOWN);
+  DWORD sp(0);
+
+  HRESULT hr = CategorizeOS(&os_type, &sp);
+  if (FAILED(hr)) {
+    ASSERT(false, (_T("[Failed to get os type][0x%x]"), hr));
+    return false;
+  }
+
+  return (os_type >= OS_WINDOWS_VISTA);
+}
+
+bool SystemInfo::IsRunningOnVistaRTM() {
+  OSVersionType os_type(OS_WINDOWS_UNKNOWN);
+  DWORD sp(0);
+
+  HRESULT hr = CategorizeOS(&os_type, &sp);
+  if (FAILED(hr)) {
+    ASSERT(false, (_T("[Failed to get os type][0x%x]"), hr));
+    return false;
+  }
+
+  return (os_type == SystemInfo::OS_WINDOWS_VISTA && sp == 0);
+}
+
+HRESULT SystemInfo::CategorizeOS(OSVersionType* os_ver, DWORD* sp) {
+  static OSVersionType os_ver_cached(OS_WINDOWS_UNKNOWN);
+  // Hopefully, Windows doesn't release a SP that's kuint32max.
+  static DWORD sp_cached(kuint32max);
+
+  ASSERT(os_ver, (_T("")));
+
+  if (sp) {
+    *sp = 0;
+  }
+
+  if (os_ver_cached == OS_WINDOWS_UNKNOWN || sp_cached == kuint32max) {
+    // Use GetVersionEx to get OS and Service Pack information.
+    OSVERSIONINFOEX osviex;
+    ::ZeroMemory(&osviex, sizeof(OSVERSIONINFOEX));
+    osviex.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+    BOOL r = ::GetVersionEx(reinterpret_cast<OSVERSIONINFO *>(&osviex));
+
+    // If ::GetVersionEx fails when given an OSVERSIONINFOEX then we're running
+    // on NT4.0SP5 or earlier.
+    if (!r) {
+      os_ver_cached = OS_WINDOWS_9X_OR_NT;
+    } else {
+      switch (osviex.dwPlatformId) {
+        case VER_PLATFORM_WIN32_NT:
+          // Windows 7 beta 1 reports the same major version as Vista does.
+          if (osviex.dwMajorVersion == 6 && osviex.dwMinorVersion == 1) {
+            os_ver_cached = OS_WINDOWS_7;
+          } else if (osviex.dwMajorVersion == 6 && osviex.dwMinorVersion == 0) {
+            os_ver_cached = OS_WINDOWS_VISTA;
+          } else if (osviex.dwMajorVersion == 5 && osviex.dwMinorVersion == 2) {
+            os_ver_cached = OS_WINDOWS_SERVER_2003;
+          } else if (osviex.dwMajorVersion == 5 && osviex.dwMinorVersion == 1) {
+            os_ver_cached = OS_WINDOWS_XP;
+          } else if (osviex.dwMajorVersion == 5 && osviex.dwMinorVersion == 0) {
+            os_ver_cached = OS_WINDOWS_2000;
+          } else if (osviex.dwMajorVersion <= 4) {
+            os_ver_cached = OS_WINDOWS_9X_OR_NT;
+            break;
+          } else {
+            os_ver_cached = OS_WINDOWS_UNKNOWN;
+            break;
+          }
+          sp_cached = osviex.wServicePackMajor;
+          break;
+
+        case VER_PLATFORM_WIN32_WINDOWS:
+        case VER_PLATFORM_WIN32s:
+        default:
+          os_ver_cached = OS_WINDOWS_9X_OR_NT;
+          break;
+      }
+    }
+
+    OPT_LOG(L1, (_T("[OS][version: %s][service pack: %d]"),
+                 OSVersionTypeAsString(os_ver_cached),
+                 sp_cached));
+  }
+
+  ASSERT1(os_ver_cached != OS_WINDOWS_UNKNOWN && sp_cached != kuint32max);
+
+  *os_ver = os_ver_cached;
+  if (sp) {
+    *sp = sp_cached;
+  }
+
+  return S_OK;
+}
+
+const wchar_t* SystemInfo::OSVersionTypeAsString(OSVersionType t) {
+  switch (t) {
+    case OS_WINDOWS_9X_OR_NT:    return _T("OS_WINDOWS_9X_OR_NT");
+    case OS_WINDOWS_2000:        return _T("OS_WINDOWS_2000");
+    case OS_WINDOWS_XP:          return _T("OS_WINDOWS_XP");
+    case OS_WINDOWS_SERVER_2003: return _T("OS_WINDOWS_SERVER_2003");
+    case OS_WINDOWS_UNKNOWN:     return _T("OS_WINDOWS_UNKNOWN");
+    case OS_WINDOWS_VISTA:       return _T("OS_WINDOWS_VISTA");
+    case OS_WINDOWS_7:           return _T("OS_WINDOWS_7");
+    default:                     return _T("<unknown>");
+  }
+}
+
+// The following code which names the operating system comes from MSDN article
+// "Getting the System Version"
+#define kNullChar (_T('\0'))
+bool SystemInfo::GetSystemVersion(int* major_version,
+                                  int* minor_version,
+                                  int* service_pack_major,
+                                  int* service_pack_minor,
+                                  TCHAR*  name_buf,
+                                  size_t name_buf_len) {
+  ASSERT1(major_version);
+  ASSERT1(minor_version);
+  ASSERT1(service_pack_major);
+  ASSERT1(service_pack_minor);
+  ASSERT1(name_buf);
+  ASSERT1(0 < name_buf_len);
+
+  // Clear the name to start with.
+  name_buf[0] = kNullChar;
+
+  DWORD buf_len = MAX_PATH;
+  TCHAR buffer[MAX_PATH];
+  TCHAR format_buffer[64];
+
+  buffer[0] = kNullChar;
+
+  OSVERSIONINFOEX osvi;
+  BOOL ver_info_exists;
+
+  // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
+  // If that fails, try using the OSVERSIONINFO structure.
+  ::ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+  ver_info_exists = ::GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&osvi));
+  if (!ver_info_exists) {
+    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+    if (!::GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&osvi))) {
+      return false;
+    }
+  }
+
+  *major_version      = osvi.dwMajorVersion;
+  *minor_version      = osvi.dwMinorVersion;
+  *service_pack_major = osvi.wServicePackMajor;
+  *service_pack_minor = osvi.wServicePackMinor;
+
+  switch (osvi.dwPlatformId) {
+    // Test for the Windows NT product family.
+    case VER_PLATFORM_WIN32_NT:
+
+      // Test for the specific product family.
+      if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
+        SafeStrCat(buffer,
+                   _T("Microsoft Windows Server 2003 family, "),
+                   buf_len);
+      }
+
+      if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
+        SafeStrCat(buffer, _T("Microsoft Windows XP "), buf_len);
+      }
+
+      if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
+        SafeStrCat(buffer, _T("Microsoft Windows 2000 "), buf_len);
+      }
+
+      if (osvi.dwMajorVersion <= 4) {
+        SafeStrCat(buffer, _T("Microsoft Windows NT "), buf_len);
+      }
+
+      // Test for specific product on Windows NT 4.0 SP6 and later.
+      if (ver_info_exists) {
+        // Test for the workstation type.
+        if (osvi.wProductType == VER_NT_WORKSTATION) {
+          if (osvi.dwMajorVersion == 4) {
+            SafeStrCat(buffer, _T("Workstation 4.0 "), buf_len);
+          } else if (osvi.wSuiteMask & VER_SUITE_PERSONAL) {
+            SafeStrCat(buffer, _T("Home Edition "), buf_len);
+          } else {
+            SafeStrCat(buffer, _T("Professional "), buf_len);
+          }
+        } else if (osvi.wProductType == VER_NT_SERVER ||
+                   osvi.wProductType == VER_NT_DOMAIN_CONTROLLER) {
+          // server type.
+          if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
+            if (osvi.wSuiteMask & VER_SUITE_DATACENTER) {
+              SafeStrCat(buffer, _T("Datacenter Edition "), buf_len);
+            } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
+              SafeStrCat(buffer, _T("Enterprise Edition "), buf_len);
+            } else if (osvi.wSuiteMask == VER_SUITE_BLADE) {
+              SafeStrCat(buffer, _T("Web Edition "), buf_len);
+            } else {
+              SafeStrCat(buffer, _T("Standard Edition "), buf_len);
+            }
+          } else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
+            if (osvi.wSuiteMask & VER_SUITE_DATACENTER) {
+              SafeStrCat(buffer, _T("Datacenter Server "), buf_len);
+            } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
+              SafeStrCat(buffer, _T("Advanced Server "), buf_len);
+            } else {
+              SafeStrCat(buffer, _T("Server "), buf_len);
+            }
+          } else {
+            // Windows NT 4.0.
+            if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
+              SafeStrCat(buffer,
+                         _T("Server 4.0, Enterprise Edition "),
+                         buf_len);
+            } else {
+              SafeStrCat(buffer, _T("Server 4.0 "), buf_len);
+            }
+          }
+        }
+      } else {
+        // Test for specific product on Windows NT 4.0 SP5 and earlier.
+        HKEY hKey;
+        TCHAR product_type[64] = {0};
+        DWORD dwBufLen = arraysize(product_type);
+        LONG lRet;
+
+        // TODO(omaha): should we use the RegKey API for consistency.
+        lRet = ::RegOpenKeyEx(
+                   HKEY_LOCAL_MACHINE,
+                   _T("SYSTEM\\CurrentControlSet\\Control\\ProductOptions"),
+                   0,
+                   KEY_QUERY_VALUE,
+                   &hKey);
+        if (lRet != ERROR_SUCCESS) {
+          return false;
+        }
+
+        lRet = ::RegQueryValueEx(hKey,
+                                 _T("ProductType"),
+                                 NULL,
+                                 NULL,
+                                 reinterpret_cast<byte *>(product_type),
+                                 &dwBufLen);
+        if ((lRet != ERROR_SUCCESS) || (dwBufLen > arraysize(product_type))) {
+          return false;
+        }
+
+        ::RegCloseKey(hKey);
+
+        if (::lstrcmpi(_T("WINNT"), product_type) == 0) {
+          SafeStrCat(buffer, _T("Workstation "), buf_len);
+        }
+        if (::lstrcmpi(_T("LANMANNT"), product_type) == 0) {
+          SafeStrCat(buffer, _T("Server "), buf_len);
+        }
+        if (::lstrcmpi(_T("SERVERNT"), product_type) == 0) {
+          SafeStrCat(buffer, _T("Advanced Server "), buf_len);
+        }
+
+        ::wsprintf(format_buffer,
+                   _T("%d.%d "),
+                   osvi.dwMajorVersion,
+                   osvi.dwMinorVersion);
+        SafeStrCat(buffer, format_buffer, buf_len);
+      }
+
+      // Display service pack (if any) and build number.
+      if (osvi.dwMajorVersion == 4 &&
+          ::lstrcmpi(osvi.szCSDVersion, _T("Service Pack 6")) == 0) {
+        HKEY hKey;
+        LONG lRet;
+
+        // Test for SP6 versus SP6a.
+        lRet = ::RegOpenKeyEx(
+                   HKEY_LOCAL_MACHINE,
+                   _T("SOFTWARE\\Microsoft\\Windows NT\\")
+                       _T("CurrentVersion\\Hotfix\\Q246009"),
+                   0,
+                   KEY_QUERY_VALUE,
+                   &hKey);
+        if (lRet == ERROR_SUCCESS) {
+          ::wsprintf(format_buffer,
+                     _T("Service Pack 6a (Build %d)"),
+                     osvi.dwBuildNumber & 0xFFFF);
+          SafeStrCat(buffer, format_buffer, buf_len);
+        } else {
+          // Windows NT 4.0 prior to SP6a.
+          ::wsprintf(format_buffer, _T("%s (Build %d)"),
+                      osvi.szCSDVersion,
+                      osvi.dwBuildNumber & 0xFFFF);
+          SafeStrCat(buffer, format_buffer, buf_len);
+        }
+        ::RegCloseKey(hKey);
+      } else {
+        // Windows NT 3.51 and earlier or Windows 2000 and later.
+        ::wsprintf(format_buffer,
+                   _T("%s (Build %d)"),
+                   osvi.szCSDVersion,
+                   osvi.dwBuildNumber & 0xFFFF);
+        SafeStrCat(buffer, format_buffer, buf_len);
+      }
+
+      break;
+
+      // Test for the Windows 95 product family.
+    case VER_PLATFORM_WIN32_WINDOWS:
+
+      if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) {
+        SafeStrCat(buffer, _T("Microsoft Windows 95 "), buf_len);
+        if (osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B') {
+          SafeStrCat(buffer, _T("OSR2 "), buf_len);
+        }
+      }
+
+      if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) {
+        SafeStrCat(buffer, _T("Microsoft Windows 98 "), buf_len);
+        if (osvi.szCSDVersion[1] == 'A') {
+          SafeStrCat(buffer, _T("SE "), buf_len);
+        }
+      }
+
+      if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) {
+        SafeStrCat(buffer,
+                   _T("Microsoft Windows Millennium Edition"),
+                   buf_len);
+      }
+      break;
+
+    case VER_PLATFORM_WIN32s:
+
+      SafeStrCat(buffer, _T("Microsoft Win32s"), buf_len);
+      break;
+
+  default:
+    SafeStrCat(buffer, _T("Unknown operating system"), buf_len);
+    break;
+  }
+  // SKIP_LOC_END
+
+  // Remove trailing space, if any.
+  DWORD buffer_len = ::lstrlen(buffer);
+  if (buffer[buffer_len-1] == kNullChar) {
+    buffer[buffer_len-1] = kNullChar;
+  }
+
+  // Copy to destination argument.
+  String_StrNCpy(name_buf, buffer, name_buf_len);
+
+  return true;
+}
+
+DWORD SystemInfo::GetProcessorArchitecture() {
+  static DWORD processor_architecture_cached(PROCESSOR_ARCHITECTURE_UNKNOWN);
+
+  if (processor_architecture_cached == PROCESSOR_ARCHITECTURE_UNKNOWN) {
+    typedef void (WINAPI * GetSystemInfoFunc)(LPSYSTEM_INFO);
+
+    HMODULE handle = ::GetModuleHandle(_T("kernel32"));
+    ASSERT1(handle);
+    GetSystemInfoFunc get_native_system_info =
+        reinterpret_cast<GetSystemInfoFunc>(::GetProcAddress(
+                                                handle,
+                                                "GetNativeSystemInfo"));
+
+    if (get_native_system_info != NULL) {
+      SYSTEM_INFO sys_info = {0};
+
+      get_native_system_info(&sys_info);
+
+      processor_architecture_cached = sys_info.wProcessorArchitecture;
+    } else {
+      // If we couldn't get the _native_ system info, then we must be on OS
+      // earlier than XP, so can't be 64-bit anyway. Assume Intel.
+      processor_architecture_cached = PROCESSOR_ARCHITECTURE_INTEL;
+    }
+  }
+
+  return processor_architecture_cached;
+}
+
+bool SystemInfo::Is64BitWindows() {
+#if defined(_WIN64)
+  return true;
+#else
+  return Process::IsWow64(::GetCurrentProcessId());
+#endif
+}
+
+}  // namespace omaha
diff --git a/base/system_info.h b/base/system_info.h
new file mode 100644
index 0000000..6755812
--- /dev/null
+++ b/base/system_info.h
@@ -0,0 +1,115 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// TODO(omaha): this code should be updated according to code published by
+// Microsoft at http://msdn.microsoft.com/en-us/library/ms724429(VS.85).aspx.
+// We need a more rigorous clasification of versions.
+
+#ifndef OMAHA_BASE_SYSTEM_INFO_H_
+#define OMAHA_BASE_SYSTEM_INFO_H_
+
+#include <windows.h>
+#include <tchar.h>
+
+namespace omaha {
+
+// TODO(omaha): refactor to use a namespace.
+class SystemInfo {
+ public:
+  // Find out if the OS is at least Windows 2000
+  // Service pack 4. If OS version is less than that
+  // will return false, all other cases true.
+  static bool OSWin2KSP4OrLater() {
+    // Use GetVersionEx to get OS and Service Pack information.
+    OSVERSIONINFOEX osviex;
+    ::ZeroMemory(&osviex, sizeof(osviex));
+    osviex.dwOSVersionInfoSize = sizeof(osviex);
+    BOOL success = ::GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&osviex));
+    // If this failed we're on Win9X or a pre NT4SP6 OS.
+    if (!success) {
+      return false;
+    }
+
+    if (osviex.dwMajorVersion < 5) {
+      return false;
+    }
+    if (osviex.dwMajorVersion > 5) {
+      return true;    // way beyond Windows XP.
+    }
+    if (osviex.dwMinorVersion >= 1) {
+      return true;    // Windows XP or better.
+    }
+    if (osviex.wServicePackMajor >= 4) {
+      return true;    // Windows 2000 SP4.
+    }
+
+    return false;     // Windows 2000, < SP4.
+  }
+
+  // Returns true if the OS is at least XP SP2.
+  static bool OSWinXPSP2OrLater();
+
+  // CategorizeOS returns a categorization of what operating system is running,
+  // and the service pack level.
+  // NOTE: Please keep this in the order of increasing OS versions
+  enum OSVersionType {
+    OS_WINDOWS_UNKNOWN = 1,
+    OS_WINDOWS_9X_OR_NT,
+    OS_WINDOWS_2000,
+    OS_WINDOWS_XP,
+    OS_WINDOWS_SERVER_2003,
+    OS_WINDOWS_VISTA,
+    OS_WINDOWS_7
+  };
+  static HRESULT CategorizeOS(OSVersionType* os_version, DWORD* service_pack);
+  static const wchar_t* OSVersionTypeAsString(OSVersionType t);
+
+  // Returns true if the current operating system is Windows 2000.
+  static bool IsRunningOnW2K();
+
+  // Are we running on Windows XP or later.
+  static bool IsRunningOnXPOrLater();
+
+  // Are we running on Windows XP SP1 or later.
+  static bool IsRunningOnXPSP1OrLater();
+
+  // Are we running on Windows Vista or later.
+  static bool IsRunningOnVistaOrLater();
+
+  static bool IsRunningOnVistaRTM();
+
+  // Returns the version and the name of the operating system.
+  static bool GetSystemVersion(int* major_version,
+                               int* minor_version,
+                               int* service_pack_major,
+                               int* service_pack_minor,
+                               TCHAR* name_buf,
+                               size_t name_buf_len);
+
+  // Returns the processor architecture. We use wProcessorArchitecture in
+  // SYSTEM_INFO returned by ::GetNativeSystemInfo() to detect the processor
+  // architecture of the installed operating system. Note the "Native" in the
+  // function name - this is important. See
+  // http://msdn.microsoft.com/en-us/library/ms724340.aspx.
+  static DWORD GetProcessorArchitecture();
+
+  // Returns whether this is a 64-bit Windows system.
+  static bool Is64BitWindows();
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_SYSTEM_INFO_H_
+
diff --git a/base/system_info_unittest.cc b/base/system_info_unittest.cc
new file mode 100644
index 0000000..77ef45f
--- /dev/null
+++ b/base/system_info_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <atlstr.h>
+#include "omaha/base/system_info.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(SystemInfoTest, SystemInfo) {
+  SystemInfo::OSVersionType os_type = SystemInfo::OS_WINDOWS_UNKNOWN;
+  DWORD service_pack = 0;
+  ASSERT_SUCCEEDED(SystemInfo::CategorizeOS(&os_type, &service_pack));
+}
+
+TEST(SystemInfoTest, GetSystemVersion) {
+  int major_version(0);
+  int minor_version(0);
+  int service_pack_major(0);
+  int service_pack_minor(0);
+
+  CString name;
+  ASSERT_TRUE(SystemInfo::GetSystemVersion(&major_version,
+                                           &minor_version,
+                                           &service_pack_major,
+                                           &service_pack_minor,
+                                           CStrBuf(name, MAX_PATH),
+                                           MAX_PATH));
+  EXPECT_NE(0, major_version);
+  EXPECT_EQ(0, service_pack_minor);
+
+  EXPECT_FALSE(name.IsEmpty());
+}
+
+TEST(SystemInfoTest, Is64BitWindows) {
+  DWORD arch(SystemInfo::GetProcessorArchitecture());
+
+  if (arch == PROCESSOR_ARCHITECTURE_INTEL) {
+    EXPECT_FALSE(SystemInfo::Is64BitWindows());
+  } else {
+    EXPECT_TRUE(SystemInfo::Is64BitWindows());
+  }
+}
+
+TEST(SystemInfoTest, GetProcessorArchitecture) {
+  DWORD arch(SystemInfo::GetProcessorArchitecture());
+
+  // TODO(omaha3): Maybe we could look for the presence of a wow6432 key in the
+  // registry to detect.
+  EXPECT_TRUE(arch == PROCESSOR_ARCHITECTURE_INTEL ||
+              arch == PROCESSOR_ARCHITECTURE_AMD64);
+}
+
+}  // namespace omaha
+
diff --git a/base/system_unittest.cc b/base/system_unittest.cc
new file mode 100644
index 0000000..4eae206
--- /dev/null
+++ b/base/system_unittest.cc
@@ -0,0 +1,73 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+// System unittest
+//
+// TODO(omaha): there are some untested functions: memory stats, thread
+// priorities, getdirsize (that's mine), backup/restore of registry trees.. not
+// sure how high priority it is to test these things but should probably be
+// added
+
+#include "omaha/base/system.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(SystemTest, System) {
+    uint32 time_waited = 0;
+    ASSERT_SUCCEEDED(System::WaitForDiskActivity(10000, 25, &time_waited));
+
+    uint64 free_bytes_current_user = 0;
+    uint64 total_bytes_current_user = 0;
+    uint64 free_bytes_all_users = 0;
+    ASSERT_SUCCEEDED(System::GetDiskStatistics(_T("C:\\"),
+                                               &free_bytes_current_user,
+                                               &total_bytes_current_user,
+                                               &free_bytes_all_users));
+
+    ASSERT_EQ(System::GetProcessHandleCount(),
+              System::GetProcessHandleCountOld());
+}
+
+// Assume the workstations and PULSE are not running on batteries. The test
+// fails on laptops running on batteries.
+TEST(SystemTest, IsRunningOnBatteries) {
+  ASSERT_FALSE(System::IsRunningOnBatteries());
+}
+
+TEST(SystemTest, GetProcessMemoryStatistics) {
+  uint64 current_working_set(0);
+  uint64 peak_working_set(0);
+  uint64 min_working_set_size(0);
+  uint64 max_working_set_size(0);
+  ASSERT_HRESULT_SUCCEEDED(
+    System::GetProcessMemoryStatistics(&current_working_set,
+                                       &peak_working_set,
+                                       &min_working_set_size,
+                                       &max_working_set_size));
+  EXPECT_LT(0, current_working_set);
+  EXPECT_LT(0, peak_working_set);
+  EXPECT_LT(0, min_working_set_size);
+  EXPECT_LT(0, max_working_set_size);
+}
+
+TEST(SystemTest, GetProcessHandleCount) {
+  DWORD handle_count(0);
+  ASSERT_TRUE(::GetProcessHandleCount(::GetCurrentProcess(), &handle_count));
+  EXPECT_LE(0u, handle_count);
+  EXPECT_EQ(handle_count, System::GetProcessHandleCount());
+}
+
+}  // namespace omaha
+
diff --git a/base/thread.cc b/base/thread.cc
new file mode 100644
index 0000000..a3abf4a
--- /dev/null
+++ b/base/thread.cc
@@ -0,0 +1,169 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/thread.h"
+
+#include "omaha/base/debug.h"
+#include "omaha/base/exception_barrier.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/time.h"
+
+namespace omaha {
+
+// The system keeps an event associated with the thread message queue for a
+// while even after the thread is dead. It can appear as a handle leak in the
+// unit test but in fact it is not.
+
+Thread::Thread() : thread_id_(0), thread_(NULL) {
+}
+
+Thread::~Thread() {
+  if (thread_) {
+    VERIFY1(CloseHandle(thread_));
+  }
+  thread_ = NULL;
+}
+
+// This is the thread proc function as required by win32.
+DWORD __stdcall Thread::Prepare(void* this_pointer) {
+  ExceptionBarrier eb;
+
+  ASSERT1(this_pointer);
+  Thread   * this_thread =  reinterpret_cast<Thread*>(this_pointer);
+  Runnable * this_runner =  this_thread->runner_;
+
+  // Create a message queue. Our thread should have one.
+  MSG message = {0};
+  PeekMessage(&message, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+
+  // Start method is waiting on this gate to be open in order
+  // to proceed. By opening gate we say: OK thread is running.
+  this_thread->start_gate_.Open();
+
+  // Now call the interface method. We are done.
+  UTIL_LOG(L4, (L"Thread::Prepare calling thread's Run()"));
+  this_runner->Run();
+
+  return 0;
+}
+
+// Starts the thread. It does not return until the thread is started.
+bool Thread::Start(Runnable* runner) {
+  ASSERT1(runner);
+
+  // Allow the thread object to be reused by cleaning its state up.
+  if (thread_) {
+    VERIFY1(CloseHandle(thread_));
+  }
+  start_gate_.Close();
+
+  runner_ = runner;
+  thread_ = CreateThread(NULL,              // default security attributes
+                         0,                 // use default stack size
+                         &Thread::Prepare,  // thread function
+                         this,              // argument to thread function
+                         0,                 // use default creation flags
+                         &thread_id_);      // returns the thread identifier
+  if (!thread_) {
+    return false;
+  }
+  // Wait until the newly created thread opens the gate for us.
+  return start_gate_.Wait(INFINITE);
+}
+
+DWORD Thread::GetThreadId() const {
+  return thread_id_;
+}
+
+HANDLE Thread::GetThreadHandle() const {
+  return thread_;
+}
+
+bool Thread::Suspend() {
+  return (static_cast<DWORD>(-1) != SuspendThread(thread_));
+}
+
+bool Thread::Resume() {
+  return (static_cast<DWORD>(-1) != ResumeThread(thread_));
+}
+
+bool Thread::Terminate(int exit_code) {
+  return TRUE == TerminateThread(thread_, exit_code);
+}
+
+bool Thread::SetPriority(int priority) {
+  return TRUE == SetThreadPriority(thread_, priority);
+}
+
+bool Thread::GetPriority(int* priority) const {
+  if (!priority) {
+    return false;
+  }
+  *priority = GetThreadPriority(thread_);
+  return THREAD_PRIORITY_ERROR_RETURN != *priority;
+}
+
+// Waits for handle to become signaled.
+bool Thread::WaitTillExit(DWORD msec) const {
+  if (!Running()) {
+    return true;
+  }
+  return WAIT_OBJECT_0 == WaitForSingleObject(thread_, msec);
+}
+
+// Checks if the thread is running.
+bool Thread::Running() const {
+  if (NULL == thread_) {
+    return false;
+  }
+  return WAIT_TIMEOUT == WaitForSingleObject(thread_, 0);
+}
+
+// Executes an APC request.
+void __stdcall Thread::APCProc(ULONG_PTR param) {
+  ApcInfo* pInfo = reinterpret_cast<ApcInfo*>(param);
+  if (pInfo) {
+    if (pInfo->receiver_) {
+      pInfo->receiver_->OnApc(pInfo->param_);
+    }
+    // Deallocates what was allocated in QueueApc.
+    delete pInfo;
+  }
+}
+
+// ApcReceiver wants to execute its OnApc function in the
+// context of this thread.
+bool Thread::QueueApc(ApcReceiver* receiver, ULONG_PTR param) {
+  ASSERT1(receiver);
+  if (!Running()) {
+    // No reason to queue anything to not running thread.
+    return true;
+  }
+
+  // This allocation will be freed in Thread::APCProc
+  ApcInfo* pInfo = new ApcInfo();
+  pInfo->receiver_ = receiver;
+  pInfo->param_    = param;
+  return 0 != QueueUserAPC(&Thread::APCProc,
+                           thread_,
+                           reinterpret_cast<ULONG_PTR>(pInfo));
+}
+
+bool Thread::PostMessage(UINT msg, WPARAM wparam, LPARAM lparam) {
+  return TRUE == PostThreadMessage(thread_id_, msg, wparam, lparam);
+}
+
+}  // namespace omaha
+
diff --git a/base/thread.h b/base/thread.h
new file mode 100644
index 0000000..bf576b9
--- /dev/null
+++ b/base/thread.h
@@ -0,0 +1,105 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Defines interface Runnable and class Thread.
+//
+// Thread encapsulates win32 primitives of creating and
+// manipulating win32 threads.
+
+#ifndef OMAHA_COMMON_THREAD_H__
+#define OMAHA_COMMON_THREAD_H__
+
+#include "omaha/base/synchronized.h"
+
+namespace omaha {
+
+// Any class which requires part of its execution in a
+// separate thread should be derived from Runnable interface.
+// It can have member variable of type Thread. When the
+// thread needs to be launched one does something like that.
+//  A::func() {
+//    thread_.start(this);
+//  }
+
+class Runnable {
+  friend class Thread;
+ protected:
+  Runnable() {}
+  virtual ~Runnable() {}
+  virtual void Run() = 0;
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(Runnable);
+};
+
+// Any class devived from this one will be able to call
+// Thread function QueueApc and have the function OnApc get
+// executed in context of this thread. Thread must be in alertable
+// state to be able to execute the apc function.
+class ApcReceiver {
+  friend class Thread;
+ protected:
+  ApcReceiver() {}
+  virtual ~ApcReceiver() {}
+  virtual void OnApc(ULONG_PTR param) = 0;
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(ApcReceiver);
+};
+
+// This class encapsulates win32 thread management functions.
+class Thread {
+ public:
+  Thread();
+  ~Thread();
+
+  bool Start(Runnable* runner);
+  bool Suspend();
+  bool Resume();
+  bool Terminate(int exit_code);
+  bool SetPriority(int priority);
+  bool GetPriority(int* priority) const;
+  DWORD GetThreadId() const;
+  HANDLE GetThreadHandle() const;
+
+  // Checks if the thread is running.
+  bool Running() const;
+
+  // Waits until thread exits.
+  bool WaitTillExit(DWORD msec) const;
+
+  // Queues an APC to the ApcReceiver.
+  bool QueueApc(ApcReceiver* receiver, ULONG_PTR param);
+
+  // Posts message to a thread.
+  bool PostMessage(UINT msg, WPARAM wparam, LPARAM lparam);
+ private:
+  static DWORD __stdcall Prepare(void* thisPointer);      // Thread proc.
+  static void __stdcall APCProc(ULONG_PTR dwParam);
+
+  Runnable* runner_;     // Interface to work with.
+  HANDLE    thread_;
+  DWORD     thread_id_;
+  Gate start_gate_;     // Synchronizes the thread start.
+
+  struct ApcInfo {
+    ApcReceiver* receiver_;
+    ULONG_PTR    param_;
+  };
+
+  DISALLOW_EVIL_CONSTRUCTORS(Thread);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_THREAD_H__
diff --git a/base/thread_pool.cc b/base/thread_pool.cc
new file mode 100644
index 0000000..e614dc4
--- /dev/null
+++ b/base/thread_pool.cc
@@ -0,0 +1,129 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/thread_pool.h"
+
+#include "base/scoped_ptr.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/exception_barrier.h"
+#include "omaha/base/logging.h"
+
+namespace omaha {
+
+namespace {
+
+// Context keeps track the information necessary to execute a work item
+// inside a thread pool thread.
+class Context {
+ public:
+  Context(ThreadPool* pool, UserWorkItem* work_item)
+      : pool_(pool),
+        work_item_(work_item) {
+    ASSERT1(pool);
+    ASSERT1(work_item);
+  }
+
+  ThreadPool*   pool() const { return pool_; }
+  UserWorkItem* work_item() const { return work_item_; }
+
+ private:
+  ThreadPool*   pool_;
+  UserWorkItem* work_item_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(Context);
+};
+
+// Returns true if delta time since 'baseline' is greater or equal than
+// 'milisecs'. Note: GetTickCount wraps around every ~48 days.
+bool TimeHasElapsed(DWORD baseline, DWORD milisecs) {
+  DWORD current = ::GetTickCount();
+  DWORD wrap_bias = 0;
+  if (current < baseline) {
+    wrap_bias = static_cast<DWORD>(0xFFFFFFFF);
+  }
+  return (current - baseline + wrap_bias) >= milisecs ? true : false;
+}
+
+}   // namespace
+
+
+DWORD WINAPI ThreadPool::ThreadProc(void* param) {
+  ExceptionBarrier eb;
+  UTIL_LOG(L4, (_T("[ThreadPool::ThreadProc]")));
+  ASSERT1(param);
+  Context* context = static_cast<Context*>(param);
+  context->pool()->ProcessWorkItem(context->work_item());
+  delete context;
+  return 0;
+}
+
+ThreadPool::ThreadPool()
+    : work_item_count_(0),
+      shutdown_delay_(0) {
+  UTIL_LOG(L2, (_T("[ThreadPool::ThreadPool]")));
+}
+
+ThreadPool::~ThreadPool() {
+  UTIL_LOG(L2, (_T("[ThreadPool::~ThreadPool]")));
+
+  if (!shutdown_event_) {
+    return;
+  }
+
+  DWORD baseline_tick_count = ::GetTickCount();
+  if (::SetEvent(get(shutdown_event_))) {
+    while (work_item_count_ != 0) {
+      ::Sleep(1);
+      if (TimeHasElapsed(baseline_tick_count, shutdown_delay_)) {
+        UTIL_LOG(LE, (_T("[ThreadPool::~ThreadPool][timeout elapsed]")));
+        break;
+      }
+    }
+  }
+}
+
+HRESULT ThreadPool::Initialize(int shutdown_delay) {
+  shutdown_delay_ = shutdown_delay;
+  reset(shutdown_event_, ::CreateEvent(NULL, true, false, NULL));
+  return shutdown_event_ ? S_OK : HRESULTFromLastError();
+}
+
+void ThreadPool::ProcessWorkItem(UserWorkItem* work_item) {
+  ASSERT1(work_item);
+  work_item->Process();
+  delete work_item;
+  ::InterlockedDecrement(&work_item_count_);
+}
+
+HRESULT ThreadPool::QueueUserWorkItem(UserWorkItem* work_item, uint32 flags) {
+  UTIL_LOG(L4, (_T("[ThreadPool::QueueUserWorkItem]")));
+  ASSERT1(work_item);
+
+  scoped_ptr<Context> context(new Context(this, work_item));
+  work_item->set_shutdown_event(get(shutdown_event_));
+  ::InterlockedIncrement(&work_item_count_);
+  if (!::QueueUserWorkItem(&ThreadPool::ThreadProc, context.get(), flags)) {
+    ::InterlockedDecrement(&work_item_count_);
+    return HRESULTFromLastError();
+  }
+
+  // The thread pool has the ownership of the work item thereon.
+  context.release();
+  return S_OK;
+}
+
+}   // namespace omaha
+
diff --git a/base/thread_pool.h b/base/thread_pool.h
new file mode 100644
index 0000000..a18f6f2
--- /dev/null
+++ b/base/thread_pool.h
@@ -0,0 +1,88 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_BASE_THREAD_POOL_H_
+#define OMAHA_BASE_THREAD_POOL_H_
+
+#include <windows.h>
+#include "base/basictypes.h"
+#include "omaha/base/scoped_any.h"
+
+namespace omaha {
+
+class UserWorkItem {
+ public:
+  UserWorkItem() : shutdown_event_(NULL) {}
+  virtual ~UserWorkItem() {}
+
+  // Template method interface
+  void Process() { DoProcess(); }
+
+  HANDLE shutdown_event() const { return shutdown_event_; }
+  void set_shutdown_event(HANDLE shutdown_event) {
+    shutdown_event_ = shutdown_event;
+  }
+
+ private:
+  // Executes the work item.
+  virtual void DoProcess() = 0;
+
+  // It is the job of implementers to watch for the signaling of this event
+  // and shutdown correctly. This event is set when the thread pool is closing.
+  // Do not close this event as is owned by the thread pool.
+  HANDLE shutdown_event_;
+  DISALLOW_EVIL_CONSTRUCTORS(UserWorkItem);
+};
+
+class ThreadPool {
+ public:
+  ThreadPool();
+
+  // The destructor might block for 'shutdown_delay'.
+  ~ThreadPool();
+
+  HRESULT Initialize(int shutdown_delay);
+
+  // Returns true if any work items are still in progress.
+  bool HasWorkItems() const { return (0 != work_item_count_); }
+
+  // Adds a work item to the queue. If the add fails the ownership of the
+  // work items remains with the caller.
+  HRESULT QueueUserWorkItem(UserWorkItem* work_item, uint32 flags);
+
+ private:
+  // Calls UserWorkItem::Process() in the context of the worker thread.
+  void ProcessWorkItem(UserWorkItem* work_item);
+
+  // This is the thread callback required by the underlying windows API.
+  static DWORD WINAPI ThreadProc(void* context);
+
+  // Approximate number of work items in the pool.
+  volatile LONG work_item_count_;
+
+  // This event signals when the thread pool destructor is in progress.
+  scoped_event shutdown_event_;
+
+  // How many milliseconds to wait for the work items to finish when
+  // the thread pool is shutting down. The shutdown delay resolution is ~10ms.
+  int shutdown_delay_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ThreadPool);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_THREAD_POOL_H_
+
diff --git a/base/thread_pool_callback.h b/base/thread_pool_callback.h
new file mode 100644
index 0000000..52eac45
--- /dev/null
+++ b/base/thread_pool_callback.h
@@ -0,0 +1,92 @@
+// Copyright 2004-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_BASE_THREAD_POOL_CALLBACK_H_
+#define OMAHA_BASE_THREAD_POOL_CALLBACK_H_
+
+#include <windows.h>
+#include "base/basictypes.h"
+#include "omaha/base/thread_pool.h"
+
+namespace omaha {
+
+template <typename T>
+class ThreadPoolCallBack0 : public UserWorkItem {
+ public:
+  explicit ThreadPoolCallBack0(T* obj, void (T::*fun)())
+      : obj_(obj), fun_(fun) {}
+ private:
+  virtual void DoProcess() {
+    (obj_->*fun_)();
+  }
+  T* obj_;
+  void (T::*fun_)();
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadPoolCallBack0);
+};
+
+template <typename T, typename P1>
+class ThreadPoolCallBack1 : public UserWorkItem {
+ public:
+  explicit ThreadPoolCallBack1(T* obj, void (T::*fun)(P1), P1 p1)
+      : obj_(obj), fun_(fun), p1_(p1) {}
+ private:
+  virtual void DoProcess() {
+    (obj_->*fun_)(p1_);
+  }
+  T* obj_;
+  void (T::*fun_)(P1);
+  P1 p1_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadPoolCallBack1);
+};
+
+template <typename P1>
+class StaticThreadPoolCallBack1 : public UserWorkItem {
+ public:
+  explicit StaticThreadPoolCallBack1(void (*fun)(P1), P1 p1)
+      : fun_(fun), p1_(p1) {}
+ private:
+  virtual void DoProcess() {
+    (*fun_)(p1_);
+  }
+
+  void (*fun_)(P1);
+  P1 p1_;
+
+  DISALLOW_COPY_AND_ASSIGN(StaticThreadPoolCallBack1);
+};
+
+template <typename T, typename P1, typename P2>
+class ThreadPoolCallBack2 : public UserWorkItem {
+ public:
+  explicit ThreadPoolCallBack2(T* obj, void (T::*fun)(P1, P2), P1 p1, P2 p2)
+      : obj_(obj), fun_(fun), p1_(p1), p2_(p2) {}
+ private:
+  virtual void DoProcess() {
+    (obj_->*fun_)(p1_, p2_);
+  }
+  T* obj_;
+  void (T::*fun_)(P1, P2);
+  P1 p1_;
+  P2 p2_;
+
+  DISALLOW_COPY_AND_ASSIGN(ThreadPoolCallBack2);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_THREAD_POOL_CALLBACK_H_
+
diff --git a/base/thread_pool_unittest.cc b/base/thread_pool_unittest.cc
new file mode 100644
index 0000000..389c883
--- /dev/null
+++ b/base/thread_pool_unittest.cc
@@ -0,0 +1,118 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/thread_pool.h"
+#include "omaha/base/timer.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+volatile LONG g_completed_count = 0;
+
+// Increments the global count by 1.
+class MyJob1 : public UserWorkItem {
+ public:
+  MyJob1() {}
+
+ private:
+  virtual void DoProcess() { ::InterlockedExchangeAdd(&g_completed_count, 1); }
+
+  DISALLOW_EVIL_CONSTRUCTORS(MyJob1);
+};
+
+// Increments the global count by 2.
+class MyJob2 : public UserWorkItem {
+ public:
+  MyJob2() {}
+
+ private:
+  virtual void DoProcess() { ::InterlockedExchangeAdd(&g_completed_count, 2); }
+
+  DISALLOW_EVIL_CONSTRUCTORS(MyJob2);
+};
+
+// Increments the global count by 3.
+class MyJob3 : public UserWorkItem {
+ public:
+  MyJob3() {}
+
+ private:
+  virtual void DoProcess() { ::InterlockedExchangeAdd(&g_completed_count, 3); }
+
+  DISALLOW_EVIL_CONSTRUCTORS(MyJob3);
+};
+
+HRESULT QueueMyJob1(ThreadPool* thread_pool) {
+  scoped_ptr<MyJob1> job(new MyJob1);
+  HRESULT hr = thread_pool->QueueUserWorkItem(job.get(), WT_EXECUTEDEFAULT);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  job.release();
+  return S_OK;
+}
+
+HRESULT QueueMyJob2(ThreadPool* thread_pool) {
+  scoped_ptr<MyJob2> job(new MyJob2);
+  HRESULT hr = thread_pool->QueueUserWorkItem(job.get(), WT_EXECUTEDEFAULT);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  job.release();
+  return S_OK;
+}
+
+HRESULT QueueMyJob3(ThreadPool* thread_pool) {
+  scoped_ptr<MyJob3> job(new MyJob3);
+  HRESULT hr = thread_pool->QueueUserWorkItem(job.get(), WT_EXECUTEDEFAULT);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  job.release();
+  return S_OK;
+}
+
+}   // namespace
+
+// Creates several jobs to increment a global counter by different values and
+// then it checks the value is correct.
+TEST(ThreadPoolTest, ThreadPool) {
+  const int kShutdownDelayMs = 0;
+  const int kNumJobsEachType = 100;
+
+  ThreadPool thread_pool;
+  ASSERT_HRESULT_SUCCEEDED(thread_pool.Initialize(kShutdownDelayMs));
+
+  for (int i = 0; i != kNumJobsEachType; ++i) {
+    EXPECT_HRESULT_SUCCEEDED(QueueMyJob1(&thread_pool));
+    EXPECT_HRESULT_SUCCEEDED(QueueMyJob2(&thread_pool));
+    EXPECT_HRESULT_SUCCEEDED(QueueMyJob3(&thread_pool));
+  }
+
+  const int kMaxWaitForJobsMs = 2000;
+  LowResTimer t(true);
+  while (thread_pool.HasWorkItems() &&
+         t.GetMilliseconds() < kMaxWaitForJobsMs) {
+    ::Sleep(100);
+  }
+  EXPECT_EQ(g_completed_count, 6 * kNumJobsEachType);
+}
+
+}   // namespace omaha
+
diff --git a/base/time.cc b/base/time.cc
new file mode 100644
index 0000000..7c6d91c
--- /dev/null
+++ b/base/time.cc
@@ -0,0 +1,504 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Time functions
+
+#include "omaha/base/time.h"
+#include "omaha/base/commontypes.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+// Date Constants
+
+#define kNumOfDays 7
+#define kNumOfMonth 12
+
+static const TCHAR kRFC822_DateDelimiters[]    = _T(" ,:");
+static const TCHAR kRFC822_TimeDelimiter[]     = _T(":");
+SELECTANY const TCHAR* kRFC822_Day[kNumOfDays] = {
+  _T("Mon"),
+  _T("Tue"),
+  _T("Wed"),
+  _T("Thu"),
+  _T("Fri"),
+  _T("Sat"),
+  _T("Sun") };
+
+SELECTANY const TCHAR* kRFC822_Month[kNumOfMonth] = {
+  _T("Jan"),
+  _T("Feb"),
+  _T("Mar"),
+  _T("Apr"),
+  _T("May"),
+  _T("Jun"),
+  _T("Jul"),
+  _T("Aug"),
+  _T("Sep"),
+  _T("Oct"),
+  _T("Nov"),
+  _T("Dec") };
+
+struct TimeZoneInfo {
+  const TCHAR* zone_name;
+  int hour_dif;
+};
+
+SELECTANY TimeZoneInfo kRFC822_TimeZone[] = {
+  { _T("UT"),  0 },
+  { _T("GMT"), 0 },
+  { _T("EST"), -5 },
+  { _T("EDT"), -4 },
+  { _T("CST"), -6 },
+  { _T("CDT"), -5 },
+  { _T("MST"), -7 },
+  { _T("MDT"), -6 },
+  { _T("PST"), -8 },
+  { _T("PDT"), -7 },
+  { _T("A"),   -1 },  // Military time zones
+  { _T("B"),   -2 },
+  { _T("C"),   -3 },
+  { _T("D"),   -4 },
+  { _T("E"),   -5 },
+  { _T("F"),   -6 },
+  { _T("G"),   -7 },
+  { _T("H"),   -8 },
+  { _T("I"),   -9 },
+  { _T("K"),   -10 },
+  { _T("L"),   -11 },
+  { _T("M"),   -12 },
+  { _T("N"),    1 },
+  { _T("O"),    2 },
+  { _T("P"),    3 },
+  { _T("Q"),    4 },
+  { _T("R"),    5 },
+  { _T("S"),    6 },
+  { _T("T"),    7 },
+  { _T("U"),    8 },
+  { _T("V"),    9 },
+  { _T("W"),    10 },
+  { _T("X"),    11 },
+  { _T("Y"),    12 },
+  { _T("Z"),    0 },
+};
+
+SELECTANY const TCHAR *days[] =
+  { L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat" };
+
+SELECTANY const TCHAR *months[] = {
+  L"Jan",
+  L"Feb",
+  L"Mar",
+  L"Apr",
+  L"May",
+  L"Jun",
+  L"Jul",
+  L"Aug",
+  L"Sep",
+  L"Oct",
+  L"Nov",
+  L"Dec"
+};
+
+// NOTE: so long as the output is used internally only, no localization is
+// needed here.
+CString ConvertTimeToGMTString(const FILETIME *ft) {
+  ASSERT(ft, (L""));
+
+  CString s;
+  SYSTEMTIME st;
+  if (!FileTimeToSystemTime(ft, &st)) {
+    return L"";
+  }
+
+  // same as FormatGmt(_T("%a, %d %b %Y %H:%M:%S GMT"));
+  s.Format(NOTRANSL(L"%s, %02d %s %d %02d:%02d:%02d GMT"), days[st.wDayOfWeek],
+    st.wDay, months[st.wMonth-1], st.wYear, st.wHour, st.wMinute, st.wSecond);
+  return s;
+}
+
+time64 ConvertTime16ToTime64(uint16 time16) {
+  return time16 * kTimeGranularity + kStart100NsTime;
+}
+
+uint16 ConvertTime64ToTime16(time64 time) {
+  ASSERT1(time >= kStart100NsTime);
+
+  time64 t64 = (time - kStart100NsTime) / kTimeGranularity;
+  ASSERT1(t64 <= kTime16Max);
+
+  return static_cast<uint16>(t64);
+}
+
+time64 TimeTToTime64(const time_t& old_value) {
+  FILETIME file_time;
+  TimeTToFileTime(old_value, &file_time);
+  return FileTimeToTime64(file_time);
+}
+
+#ifdef _DEBUG
+void ComputeStartTime() {
+    SYSTEMTIME start_system_time = kStartSystemTime;
+    time64 start_100ns_time = SystemTimeToTime64(&start_system_time);
+    UTIL_LOG(L1, (_T("posting list starting time = %s\n"),
+                  String_Int64ToString(start_100ns_time, 10)));
+}
+#endif
+
+// Time management
+
+// Allow the unittest to override.
+static time64 time_override = 0;
+
+// #ifdef UNITTEST
+void SetTimeOverride(const time64 & time_new) {
+  time_override = time_new;
+}
+
+// #endif
+
+time64 GetCurrent100NSTime() {
+  if (time_override != 0)
+    return time_override;
+
+  // In order gte the 100ns time we shouldn't use SystemTime
+  // as it's granularity is 1 ms. Below is the correct implementation.
+  // On the other hand the system clock granularity is 15 ms, so we
+  // are not gaining much by having the timestamp in nano-sec
+  // If we decide to go with ms, divide "time64 time" by 10000
+  // SYSTEMTIME sys_time;
+  // GetLocalTime(&sys_time);
+  // return SystemTimeToTime64(&sys_time);
+
+  // get the current time in 100-nanoseconds intervals
+  FILETIME file_time;
+  ::GetSystemTimeAsFileTime(&file_time);
+
+  time64 time = FileTimeToTime64(file_time);
+  return time;
+}
+
+time64 GetCurrentMsTime() {
+  return GetCurrent100NSTime() / kMillisecsTo100ns;
+}
+
+time64 SystemTimeToTime64(const SYSTEMTIME* sys_time) {
+  ASSERT1(sys_time);
+
+  FILETIME file_time;
+  SetZero(file_time);
+
+  if (!::SystemTimeToFileTime(sys_time, &file_time)) {
+    UTIL_LOG(LE,
+             (_T("[SystemTimeToTime64 - failed to SystemTimeToFileTime][0x%x]"),
+              HRESULTFromLastError()));
+    return 0;
+  }
+
+  return FileTimeToTime64(file_time);
+}
+
+// returns a value compatible with EXE/DLL timestamps
+// and the C time() function
+// NOTE: behavior is independent of wMilliseconds value
+int32 SystemTimeToInt32(const SYSTEMTIME *sys_time) {
+  ASSERT(sys_time, (L""));
+
+  time64 t64 = SystemTimeToTime64(sys_time);
+  int32 t32 = 0;
+
+  if (t64 != 0) {
+    t32 = Time64ToInt32(t64);
+  }
+  return t32;
+}
+
+int32 Time64ToInt32(const time64 & time) {
+  // convert to 32-bit format
+  // time() (32-bit) measures seconds since 1970/01/01 00:00:00 (UTC)
+  // FILETIME (64-bit) measures 100-ns intervals since 1601/01/01 00:00:00 (UTC)
+
+  // seconds between 1601 and 1970
+  time64 t32 = (time / kSecsTo100ns) -
+               ((time64(60*60*24) * time64(365*369 + 89)));
+  ASSERT(t32 == (t32 & 0x7FFFFFFF), (L""));  // make sure it fits
+
+  // cast at the end (avoids overflow/underflow when computing 32-bit value)
+  return static_cast<int32>(t32);
+}
+
+time64 Int32ToTime64(const int32 & time) {
+  // convert to 64-bit format
+  // time() (32-bit) measures seconds since 1970/01/01 00:00:00 (UTC)
+  // FILETIME (64-bit) measures 100-ns intervals since 1601/01/01 00:00:00 (UTC)
+
+  // seconds between 1601 and 1970
+  time64 t64 = (static_cast<time64>(time) +
+               (time64(60*60*24) * time64(365*369 + 89))) * kSecsTo100ns;
+  return t64;
+}
+
+// TODO(omaha): The next 2 functions can fail if FileTimeToLocalFileTime or
+// FileTimeToSystemTime fails.
+// Consider having it return a HRESULT. Right now if FileTimeToSystemTime fails,
+// it returns an undefined value.
+
+// Convert a uint to a genuine systemtime
+SYSTEMTIME Time64ToSystemTime(const time64& time) {
+  FILETIME file_time;
+  SetZero(file_time);
+  Time64ToFileTime(time, &file_time);
+
+  SYSTEMTIME sys_time;
+  SetZero(sys_time);
+  if (!FileTimeToSystemTime(&file_time, &sys_time)) {
+    UTIL_LOG(LE, (_T("[Time64ToSystemTime]")
+                  _T("[failed to FileTimeToSystemTime][0x%x]"),
+                  HRESULTFromLastError()));
+  }
+
+  return sys_time;
+}
+
+
+// Convert a uint to a genuine localtime
+// Should ONLY be used for display, since internally we use only UTC
+SYSTEMTIME Time64ToLocalTime(const time64& time) {
+  FILETIME file_time;
+  SetZero(file_time);
+  Time64ToFileTime(time, &file_time);
+
+  FILETIME local_file_time;
+  SetZero(local_file_time);
+  if (!FileTimeToLocalFileTime(&file_time, &local_file_time)) {
+    UTIL_LOG(LE, (_T("[Time64ToLocalTime]")
+                  _T("[failed to FileTimeToLocalFileTime][0x%x]"),
+                  HRESULTFromLastError()));
+  }
+
+  SYSTEMTIME local_time;
+  SetZero(local_time);
+  if (!FileTimeToSystemTime(&local_file_time, &local_time)) {
+    UTIL_LOG(LE, (_T("[Time64ToLocalTime]")
+                  _T("[failed to FileTimeToSystemTime][0x%x]"),
+                  HRESULTFromLastError()));
+  }
+
+  return local_time;
+}
+
+time64 FileTimeToTime64(const FILETIME & file_time) {
+  return static_cast<time64>(
+      file_time.dwHighDateTime) << 32 | file_time.dwLowDateTime;
+}
+
+void Time64ToFileTime(const time64 & time, FILETIME *ft) {
+  ASSERT(ft, (L""));
+
+  ft->dwHighDateTime = static_cast<DWORD>(time >> 32);
+  ft->dwLowDateTime = static_cast<DWORD>(time & 0xffffffff);
+}
+
+// Convert from FILETIME to time_t
+time_t FileTimeToTimeT(const FILETIME& file_time) {
+  return static_cast<time_t>(
+      (FileTimeToTime64(file_time) - kTimeTConvValue) / kSecsTo100ns);
+}
+
+// Convert from time_t to FILETIME
+void TimeTToFileTime(const time_t& time, FILETIME* file_time) {
+  ASSERT1(file_time);
+
+  LONGLONG ll = Int32x32To64(time, kSecsTo100ns) + kTimeTConvValue;
+  file_time->dwLowDateTime = static_cast<DWORD>(ll);
+  file_time->dwHighDateTime = static_cast<DWORD>(ll >> 32);
+}
+
+// Parses RFC 822 Date/Time format
+//    5.  DATE AND TIME SPECIFICATION
+//     5.1.  SYNTAX
+//
+//     date-time   =  [ day "," ] date time        ; dd mm yy
+//                                                 ;  hh:mm:ss zzz
+//     day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
+//                 /  "Fri"  / "Sat" /  "Sun"
+//
+//     date        =  1*2DIGIT month 2DIGIT        ; day month year
+//                                                 ;  e.g. 20 Jun 82
+//
+//     month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
+//                 /  "May"  /  "Jun" /  "Jul"  /  "Aug"
+//                 /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"
+//
+//     time        =  hour zone                    ; ANSI and Military
+//
+//     hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT]
+//                                                 ; 00:00:00 - 23:59:59
+//
+//     zone        =  "UT"  / "GMT"                ; Universal Time
+//                                                 ; North American : UT
+//                 /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4
+//                 /  "CST" / "CDT"                ;  Central:  - 6/ - 5
+//                 /  "MST" / "MDT"                ;  Mountain: - 7/ - 6
+//                 /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7
+//                 /  1ALPHA                       ; Military: Z = UT;
+//                                                 ;  A:-1; (J not used)
+//                                                 ;  M:-12; N:+1; Y:+12
+//                 / ( ("+" / "-") 4DIGIT )        ; Local differential
+//                                                 ;  hours+min. (HHMM)
+// return local time if ret_local_time == true,
+// return time is GMT / UTC time otherwise
+bool RFC822DateToSystemTime(const TCHAR* str_RFC822_date,
+                            SYSTEMTIME* psys_time,
+                            bool ret_local_time) {
+  ASSERT(str_RFC822_date != NULL, (L""));
+  ASSERT(psys_time != NULL, (L""));
+
+  CString str_date = str_RFC822_date;
+  CString str_token;
+  int cur_pos = 0;
+
+  str_token= str_date.Tokenize(kRFC822_DateDelimiters, cur_pos);
+  if (str_token == "")
+    return false;
+
+  int i = 0;
+  for (i = 0; i < kNumOfDays; i++) {
+    if (str_token == kRFC822_Day[i]) {
+      str_token = str_date.Tokenize(kRFC822_DateDelimiters, cur_pos);
+      if (str_token == "")
+        return false;
+      break;
+    }
+  }
+
+  int day = String_StringToInt(str_token);
+  if (day < 0 || day > 31)
+    return false;
+
+  str_token = str_date.Tokenize(kRFC822_DateDelimiters, cur_pos);
+  if (str_token == "")
+    return false;
+
+  int month = -1;
+  for (i = 0; i < kNumOfMonth; i++) {
+    if (str_token == kRFC822_Month[i]) {
+      month = i+1;  // month is 1 based number
+      break;
+    }
+  }
+  if (month == -1)  // month not found
+    return false;
+
+  str_token = str_date.Tokenize(kRFC822_DateDelimiters, cur_pos);
+  if (str_token == "")
+    return false;
+
+  int year = String_StringToInt(str_token);
+  if (year < 100)  // two digit year format, convert to 1950 - 2050 range
+    if (year < 50)
+      year += 2000;
+    else
+      year += 1900;
+
+  str_token = str_date.Tokenize(kRFC822_DateDelimiters, cur_pos);
+  if (str_token == "")
+    return false;
+
+  int hour = String_StringToInt(str_token);
+  if (hour < 0 || hour > 23)
+    return false;
+
+  str_token = str_date.Tokenize(kRFC822_DateDelimiters, cur_pos);
+  if (str_token == "")
+    return false;
+
+  int minute = String_StringToInt(str_token);
+  if (minute < 0 || minute > 59)
+    return false;
+
+  str_token = str_date.Tokenize(kRFC822_DateDelimiters, cur_pos);
+  if (str_token == "")
+    return false;
+
+  int second = 0;
+  // distingushed between XX:XX and XX:XX:XX time formats
+  if (str_token.GetLength() == 2 &&
+      String_IsDigit(str_token[0]) &&
+      String_IsDigit(str_token[1])) {
+    second = String_StringToInt(str_token);
+    if (second < 0 || second > 59)
+      return false;
+
+    str_token = str_date.Tokenize(kRFC822_DateDelimiters, cur_pos);
+    if (str_token == "")
+      return false;
+  }
+
+  int bias = 0;
+  if (str_token[0] == '+' ||
+      str_token[0] == '-' ||
+      String_IsDigit(str_token[0])) {  // numeric format
+    int zone = String_StringToInt(str_token);
+
+    // zone is in HHMM format, need to convert to the number of minutes
+    bias = (zone / 100) * 60 + (zone % 100);
+  } else {  // text format
+    for (i = 0; i < sizeof(kRFC822_TimeZone) / sizeof(TimeZoneInfo); i++)
+      if (str_token == kRFC822_TimeZone[i].zone_name) {
+        bias = kRFC822_TimeZone[i].hour_dif * 60;
+        break;
+      }
+  }
+
+  SYSTEMTIME mail_time;
+  memset(&mail_time, 0, sizeof(mail_time));
+
+  mail_time.wYear   = static_cast<WORD>(year);
+  mail_time.wMonth  = static_cast<WORD>(month);
+  mail_time.wDay    = static_cast<WORD>(day);
+  mail_time.wHour   = static_cast<WORD>(hour);
+  mail_time.wMinute = static_cast<WORD>(minute);
+  mail_time.wSecond = static_cast<WORD>(second);
+
+  // TzSpecificLocalTimeToSystemTime() is incompatible with Win 2000,
+  // convert time manually here
+  time64 time_64 = SystemTimeToTime64(&mail_time);
+  time_64 = time_64 - (bias*kMinsTo100ns);
+
+  *psys_time = Time64ToSystemTime(time_64);
+
+  if (ret_local_time) {
+    TIME_ZONE_INFORMATION local_time_zone_info;
+    SYSTEMTIME universal_time = *psys_time;
+
+    if (GetTimeZoneInformation(&local_time_zone_info) == TIME_ZONE_ID_INVALID) {
+      return false;
+    }
+    if (!SystemTimeToTzSpecificLocalTime(&local_time_zone_info,
+                                    &universal_time,
+                                    psys_time)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+}  // namespace omaha
+
diff --git a/base/time.h b/base/time.h
new file mode 100644
index 0000000..60c6083
--- /dev/null
+++ b/base/time.h
@@ -0,0 +1,151 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Time functions
+
+#ifndef OMAHA_BASE_TIME_H_
+#define OMAHA_BASE_TIME_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+#define kMicrosecsTo100ns (10ULL)
+#define kMillisecsTo100ns (10000ULL)
+#define kSecsTo100ns (1000 * kMillisecsTo100ns)
+#define kMinsTo100ns (60 * kSecsTo100ns)
+#define kHoursTo100ns (60 * kMinsTo100ns)
+#define kDaysTo100ns (24 * kHoursTo100ns)
+
+// Jan 1 1980 was tuesday (day 2)
+#define kStartSystemTime {1980, 1, 2, 1, 0, 0, 0, 0}
+
+// this is Jan 1 1980 in time64
+#define kStart100NsTime (119600064000000000uI64)
+#define kTimeGranularity (kDaysTo100ns)
+
+// 2^15-1 because we use signed delta times
+#define kTime16Max ((1 << 15) - 1)
+
+// Constant value used in conversion between FILETIME and time_t
+// It is the time difference between January 1, 1601 and January 1, 1970
+#define kTimeTConvValue (116444736000000000)
+
+typedef uint16 time16;
+typedef uint64 time64;
+
+time64 ConvertTime16ToTime64(uint16 time16);
+uint16 ConvertTime64ToTime16(time64 time);
+
+#ifdef _DEBUG
+void ComputeStartTime();
+#endif
+
+uint64 GetCurrent100NSTime();
+
+uint64 GetCurrentMsTime();
+
+// Note - these return 0 if we can't convert the time
+time64 SystemTimeToTime64(const SYSTEMTIME *sys_time);
+
+// Conversions to/from values compatible with
+// EXE/DLL timestamps and the C time() function
+// NOTE: behavior is independent of wMilliseconds value
+int32  SystemTimeToInt32(const SYSTEMTIME *sys_time);
+int32  Time64ToInt32(const time64 & time);
+time64 Int32ToTime64(const int32 & time);
+time64 TimeTToTime64(const time_t& old_value);
+
+// Returns the system time in GMT
+SYSTEMTIME Time64ToSystemTime(const time64 & time);
+
+// Returns the system time in the computer's time zone
+SYSTEMTIME Time64ToLocalTime(const time64 & time);
+
+// Returns the UTC (system) time given the local time
+SYSTEMTIME LocalTimeToSystemTime(const SYSTEMTIME *local_time);
+
+// This returns a standard formatted string that represents
+// the UTC time corresponding to 'ft'.  This is suitable for use
+// in e.g. HTTP headers.
+//
+// @note IMPORTANT!  This does not return a localized string - it's
+// always in English.  The string returned is intended for use in
+// machine-readable contexts, i.e. HTTP headers and thus should not
+// be localized.
+CString ConvertTimeToGMTString(const FILETIME *ft);
+
+// Convert to and from FileTime
+time64 FileTimeToTime64(const FILETIME & file_time);
+void Time64ToFileTime(const time64 & time, FILETIME *ft);
+
+void SetTimeOverride(const time64 & time_new);
+
+// Convert from FILETIME to time_t
+time_t FileTimeToTimeT(const FILETIME& file_time);
+
+// Convert from time_t to FILETIME
+void TimeTToFileTime(const time_t& time, FILETIME* file_time);
+
+// Parses RFC 822 Date/Time format
+//    5.  DATE AND TIME SPECIFICATION
+//     5.1.  SYNTAX
+//
+//     date-time   =  [ day "," ] date time        ; dd mm yy
+//                                                 ;  hh:mm:ss zzz
+//     day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
+//                 /  "Fri"  / "Sat" /  "Sun"
+//
+//     date        =  1*2DIGIT month 2DIGIT        ; day month year
+//                                                 ;  e.g. 20 Jun 82
+//
+//     month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
+//                 /  "May"  /  "Jun" /  "Jul"  /  "Aug"
+//                 /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"
+//
+//     time        =  hour zone                    ; ANSI and Military
+//
+//     hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT]
+//                                                 ; 00:00:00 - 23:59:59
+//
+//     zone        =  "UT"  / "GMT"                ; Universal Time
+//                                                 ; North American : UT
+//                 /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4
+//                 /  "CST" / "CDT"                ;  Central:  - 6/ - 5
+//                 /  "MST" / "MDT"                ;  Mountain: - 7/ - 6
+//                 /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7
+//                 /  1ALPHA                       ; Military: Z = UT;
+//                                                 ;  A:-1; (J not used)
+//                                                 ;  M:-12; N:+1; Y:+12
+//                 / ( ("+" / "-") 4DIGIT )        ; Local differential
+//                                                 ;  hours+min. (HHMM)
+// return local time if ret_local_time == true,
+// return time is GMT / UTC time otherwise
+bool RFC822DateToSystemTime(const TCHAR* str_RFC822_date,
+                            SYSTEMTIME* psys_time,
+                            bool ret_local_time);
+
+// TODO(omaha): overlap in functionality with FileTimeToTime64. Consider
+// removing this one.
+inline int64 FileTimeToInt64(const FILETIME& filetime) {
+  LARGE_INTEGER large_int = {filetime.dwLowDateTime, filetime.dwHighDateTime};
+  return large_int.QuadPart;
+}
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_TIME_H_
diff --git a/base/time_unittest.cc b/base/time_unittest.cc
new file mode 100644
index 0000000..60f13de
--- /dev/null
+++ b/base/time_unittest.cc
@@ -0,0 +1,189 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Time unittest
+
+#include <atltime.h>
+
+#include "omaha/base/time.h"
+#include "omaha/base/utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+// Test generation of int32 time values (which uses Time64ToInt32 internally).
+TEST(TimeTest, SystemTimeToInt32NearEpoch) {
+  // Try a simple value near the start of int32 time.
+  SYSTEMTIME system_time = {1970,  // year
+                            1,     // month (1 == January)
+                            0,     // day of week (0 == Sunday)
+                            2,     // day of month
+                            0,     // hour
+                            0,     // minute
+                            0,     // second
+                            0};    // msec
+  system_time.wMilliseconds = 0;
+  int32 time1 = SystemTimeToInt32(&system_time);
+  system_time.wMilliseconds = 999;
+  int32 time2 = SystemTimeToInt32(&system_time);
+
+  // Make sure result is independent of milliseconds value.
+  ASSERT_EQ(time1, time2);
+
+  // 00:00:00 on 1970/01/02 should return the number of seconds in 1 day.
+  ASSERT_EQ(time1, (60*60*24));
+}
+
+// Test an empirical value taken from running dumpbin.exe on a DLL.
+// (IMPORTANT: ran this *after* setting machine's time zone to GMT,
+//  without daylight savings).
+// 40AEE7AA time date stamp Sat May 22 05:39:54 2004
+TEST(TimeTest, SystemTimeToInt32) {
+  SYSTEMTIME system_time = {2004, 5, 6, 22, 5, 39, 54, 0};
+  int32 time = SystemTimeToInt32(&system_time);
+  ASSERT_EQ(time, 0x40AEE7AA);
+}
+
+// Test conversion between int32 and time64 values.
+// By testing SystemTimeToInt32 above, we've already checked Time64ToInt32
+// against empirical values, so it's okay to simply test back-and-forth
+// conversion here.
+TEST(TimeTest, Conversion) {
+  // Simple checks when starting with int32 values, because time64 has more
+  // precision.
+  ASSERT_EQ(Time64ToInt32(Int32ToTime64(0x12345678)), 0x12345678);
+  ASSERT_EQ(Time64ToInt32(Int32ToTime64(INT_MAX)), INT_MAX);
+  ASSERT_EQ(Time64ToInt32(Int32ToTime64(0)), 0);
+
+  // Extra conversions when going opposite direction because int32 has less
+  // precision.
+  ASSERT_EQ(Int32ToTime64(Time64ToInt32(Int32ToTime64(0x12345678))),
+            Int32ToTime64(0x12345678));
+  ASSERT_EQ(Int32ToTime64(Time64ToInt32(Int32ToTime64(INT_MAX))),
+            Int32ToTime64(INT_MAX));
+  ASSERT_EQ(Int32ToTime64(Time64ToInt32(Int32ToTime64(0))),
+            Int32ToTime64(0));
+}
+
+void TimeToStringTest(FILETIME *ft, bool daylight_savings_time) {
+  CTime t(*ft, daylight_savings_time);
+  CString date1(t.FormatGmt(_T("%a, %d %b %Y %H:%M:%S GMT")));
+  CString date2(ConvertTimeToGMTString(ft));
+
+  ASSERT_STREQ(date1, date2);
+}
+
+TEST(TimeTest, TimeToStringTest) {
+  bool daylight_savings_time = false;
+  TIME_ZONE_INFORMATION tz;
+  if (GetTimeZoneInformation(&tz) == TIME_ZONE_ID_DAYLIGHT) {
+    daylight_savings_time = true;
+  }
+
+  FILETIME file_time;
+  ::GetSystemTimeAsFileTime(&file_time);
+  TimeToStringTest(&file_time, daylight_savings_time);
+
+  uint64 t = FileTimeToTime64(file_time);
+
+  // months
+  for (int i = 0; i < 13; i++) {
+    t += (24 * kHoursTo100ns) * 28;
+    Time64ToFileTime(t, &file_time);
+    TimeToStringTest(&file_time, daylight_savings_time);
+  }
+
+  // days
+  for (int i = 0; i < 30; i++) {
+    t += (24 * kHoursTo100ns);
+    Time64ToFileTime(t, &file_time);
+    TimeToStringTest(&file_time, daylight_savings_time);
+  }
+
+  // hours
+  for (int i = 0; i < 24; i++) {
+    t += (24 * kHoursTo100ns);
+    Time64ToFileTime(t, &file_time);
+    TimeToStringTest(&file_time, daylight_savings_time);
+  }
+}
+
+TEST(TimeTest, RFC822TimeParsing) {
+  SYSTEMTIME time = {0};
+  ASSERT_TRUE(RFC822DateToSystemTime(_T("Mon, 16 May 2005 15:44:18 -0700"),
+                                     &time,
+                                     false));
+  ASSERT_EQ(time.wYear , 2005);
+  ASSERT_EQ(time.wMonth , 5);
+  ASSERT_EQ(time.wDay , 16);
+  ASSERT_EQ(time.wHour , 22);
+  ASSERT_EQ(time.wMinute , 44);
+  ASSERT_EQ(time.wSecond , 18);
+
+  ASSERT_TRUE(RFC822DateToSystemTime(_T("Mon, 16 May 2005 15:44:18 -0700"),
+                                     &time,
+                                     true));
+  ASSERT_EQ(time.wYear , 2005);
+  ASSERT_EQ(time.wMonth , 5);
+  ASSERT_EQ(time.wDay , 16);
+  ASSERT_TRUE(time.wHour == 15 || time.wHour == 14);  // daylight saving time
+  ASSERT_EQ(time.wMinute , 44);
+  ASSERT_EQ(time.wSecond , 18);
+
+  ASSERT_TRUE(RFC822DateToSystemTime(_T("Tue, 17 May 2005 02:56:18 +0400"),
+                                     &time,
+                                     false));
+  ASSERT_EQ(time.wYear , 2005);
+  ASSERT_EQ(time.wMonth , 5);
+  ASSERT_EQ(time.wDay , 16);
+  ASSERT_EQ(time.wHour , 22);
+  ASSERT_EQ(time.wMinute , 56);
+  ASSERT_EQ(time.wSecond , 18);
+
+  ASSERT_TRUE(RFC822DateToSystemTime(_T("Tue, 17 May 2005 02:56:18 +0400"),
+                                     &time,
+                                     true));
+  ASSERT_EQ(time.wYear , 2005);
+  ASSERT_EQ(time.wMonth , 5);
+  ASSERT_EQ(time.wDay , 16);
+  ASSERT_TRUE(time.wHour == 15 || time.wHour == 14);  // daylight saving time
+  ASSERT_EQ(time.wMinute , 56);
+  ASSERT_EQ(time.wSecond , 18);
+}
+
+TEST(TimeTest, FileTimeToInt64) {
+  {
+  FILETIME file_time = {0};
+  EXPECT_EQ(0, FileTimeToInt64(file_time));
+  }
+
+  {
+  FILETIME file_time = {LONG_MAX, 0};
+  EXPECT_EQ(LONG_MAX, FileTimeToInt64(file_time));
+  }
+
+  {
+  FILETIME file_time = {ULONG_MAX, 0};
+  EXPECT_EQ(ULONG_MAX, FileTimeToInt64(file_time));
+  }
+
+  {
+  FILETIME file_time = {ULONG_MAX, ULONG_MAX};
+  EXPECT_EQ(kuint64max, FileTimeToInt64(file_time));
+  }
+}
+
+}  // namespace omaha
+
diff --git a/base/timer.cc b/base/timer.cc
new file mode 100644
index 0000000..60ffa7e
--- /dev/null
+++ b/base/timer.cc
@@ -0,0 +1,218 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Timing
+
+#include "omaha/base/timer.h"
+#include "omaha/base/commontypes.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/string.h"
+#include "omaha/base/time.h"
+
+namespace omaha {
+
+// LowResTimer class is implemented on top of ::GetTickCount.
+// ::GetTickCount wraps around every 49.7 days. The code can't handle
+// this condition so there is a small probability that something can go
+// wrong.
+LowResTimer::LowResTimer(bool running)
+    : running_(false), iterations_(0), elapsed_(0), start_(0) {
+  if (running) {
+    Start();
+  }
+}
+
+LowResTimer::~LowResTimer() {
+}
+
+void LowResTimer::Reset() {
+  elapsed_ = 0;
+  running_ = 0;
+  iterations_ = 0;
+}
+
+void LowResTimer::Start() {
+  ASSERT1(!running_);
+
+  start_ = ::GetTickCount();
+  running_ = 1;
+}
+
+uint32 LowResTimer::Stop() {
+  ASSERT1(running_);
+
+  uint32 stop = ::GetTickCount();
+  ASSERT1(stop >= start_);
+  uint32 diff = stop - start_;
+  elapsed_ += diff;
+  iterations_++;
+  running_ = 0;
+  return diff;
+}
+
+uint32 LowResTimer::GetMilliseconds() const {
+  uint32 running_time = 0;
+  if (running_) {
+    uint32 now = ::GetTickCount();
+    ASSERT1(now >= start_);
+    running_time = now - start_;
+  }
+  return elapsed_ + running_time;
+}
+
+// statics
+// get the frequency only once
+SELECTANY time64 Timer::count_freq_ = 0;
+
+Timer::Timer(bool running)
+    : running_(0), iterations_(0), elapsed_(0), start_(0), split_(0) {
+  // initialize only once
+  if (count_freq_ == 0) {
+    count_freq_ = GetRdtscFrequency();
+    if (count_freq_ <= 1) {
+       UTIL_LOG(LEVEL_ERROR,
+          (_T("[Timer::Timer - high-res counter not supported]")));
+       count_freq_ = 1;
+    }
+  }
+  if (running) {
+    Start();
+  }
+}
+
+Timer::~Timer() {
+}
+
+void Timer::Reset() {
+  elapsed_ = 0;
+  running_ = 0;
+  iterations_ = 0;
+}
+
+void Timer::Start() {
+  ASSERT1(!running_);
+
+  start_ = GetRdtscCounter();
+  split_ = start_;
+  running_ = 1;
+}
+
+void Timer::Split(double* split_time_ms, double* total_time_ms) {
+  ASSERT1(running_);
+
+  time64 now = GetRdtscCounter();
+  if (split_time_ms) {
+    *split_time_ms = PerfCountToNanoSeconds(now - split_)/ 1000000;
+  }
+  if (total_time_ms) {
+    *total_time_ms =
+        PerfCountToNanoSeconds(elapsed_ + (now - start_)) / 1000000;
+  }
+  split_ = now;
+}
+
+time64 Timer::Stop() {
+  ASSERT1(running_);
+
+  time64 stop = GetRdtscCounter();
+  time64 diff = stop - start_;
+  elapsed_ += diff;
+  iterations_++;
+  running_ = 0;
+  return diff;
+}
+
+double Timer::GetNanoseconds() const {
+  time64 running_time = 0;
+  if (running_) {
+    time64 now = GetRdtscCounter();
+    running_time = now - start_;
+  }
+  return PerfCountToNanoSeconds(elapsed_ + running_time);
+}
+
+#ifdef _DEBUG
+CString Timer::DebugString() const {
+  CString s;
+  double seconds = GetSeconds();
+  if (iterations_) {
+    s.Format(_T("%s sec %d iterations %s sec/iteration"),
+             String_DoubleToString(seconds, 3), iterations_,
+             String_DoubleToString(seconds/iterations_, 3));
+  } else {
+    s.Format(_T("%s sec"), String_DoubleToString(seconds, 3));
+  }
+  return s;
+}
+#endif
+
+// Computes the frequency (ticks/sec) for the CPU tick-count timer (RDTSC)
+// Don't call this function frequently, because computing the frequency is slow
+// (relatively).
+//
+// TODO(omaha): check return values, and return 0 on failure.
+// But hard to imagine a machine where our program will install/run but this
+// will fail.
+time64 Timer::GetRdtscFrequency() {
+  //
+  // Get elapsed RDTSC and elapsed QPC over same time period
+  //
+
+  // compute length of time period to measure
+  time64 freq_qpc = 0;  // ticks per second
+  ::QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&freq_qpc));
+
+  // fraction of second to run timers for; tradeoff b/w speed and accuracy;
+  // 1/1000 (1 msec) seems like good tradeoff
+  time64 interval_qpc = freq_qpc / 1000;
+
+  // get timer values over same time period
+  time64 begin_qpc = 0;
+  time64 end_qpc = 0;
+
+  ::QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&begin_qpc));
+
+  time64 begin_rdtsc = Timer::GetRdtscCounter();
+
+  // spin and protect against infinite loop, if QPC does something wacky
+  int count = 0;
+  const int count_max = 200000;
+  do {
+    ::QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&end_qpc));
+    ++count;
+  } while ((end_qpc - begin_qpc) < interval_qpc  &&  count < count_max);
+
+  ::QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&end_qpc));
+  time64 end_rdtsc = Timer::GetRdtscCounter();
+
+  ASSERT(count < count_max, (_T("If this assert fires, your machine is either ")
+                             _T("very fast, or very broken.  Increase the ")
+                             _T("value of const_max to fix this assert.")));
+
+  //
+  // Compute RDTSC frequency from QPC frequency
+  //
+
+  time64 diff_qpc = end_qpc - begin_qpc;
+  time64 diff_rdtsc = end_rdtsc - begin_rdtsc;
+
+  time64 freq_rdtsc = freq_qpc * diff_rdtsc / diff_qpc;
+
+  return freq_rdtsc;
+}
+
+}  // namespace omaha
+
diff --git a/base/timer.h b/base/timer.h
new file mode 100644
index 0000000..3198d31
--- /dev/null
+++ b/base/timer.h
@@ -0,0 +1,186 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Timing
+
+#ifndef OMAHA_BASE_TIMER_H_
+#define OMAHA_BASE_TIMER_H_
+
+#include "omaha/base/time.h"
+
+namespace omaha {
+
+// Low resolution timer which can be used to time loops. The resolution depends
+// on the platform and can be expected to be about 10 ms on Windows 2000 and up.
+class LowResTimer {
+ public:
+  explicit LowResTimer(bool running);
+  ~LowResTimer();
+
+  // LowResTimer keeps track of elapsed time, which can consist of multiple
+  // Start()-Stop() intervals.
+  void Start();
+
+  // Returns time between Start and Stop call and increments the time elapsed.
+  uint32 Stop();
+  void Reset();
+
+  // Return time in seconds, milliseconds.
+  double GetSeconds() const;
+  uint32 GetMilliseconds() const;
+
+  // Gets the number of iteration this timer was started/stopped.
+  uint32 GetIterations() const { return iterations_; }
+  bool IsRunning() const { return running_; }
+
+ private:
+
+  bool running_;
+  uint32 start_;
+  uint32 elapsed_;
+  uint32 iterations_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(LowResTimer);
+};
+
+inline double LowResTimer::GetSeconds() const {
+  return static_cast<double>(GetMilliseconds()) / 1000;
+}
+
+// WARNING - Timer is implemented on top of RDTSC and
+// QueryPerformanceCounter which have undefined behavior when running
+// on multi-core, speedstep, bugs in HAL, certain chipsets, etc.
+// Do not use the Timer in production code where the execution flow
+// depends on timing. Timer is primarily intended to be used for
+// code profiling and performance measurements.
+class Timer {
+ public:
+  explicit Timer(bool running);
+  ~Timer();
+
+  // Timer keeps track of elapsed time, which can consist of multiple
+  // Start()-Stop() intervals.
+  void Start();
+
+  // returns time for last split (elapsed time since Start() or time since
+  // last Split(), whichever came last) as well as total elapsed time.
+  void Split(double* split_time_ms, double* total_time_ms);
+
+  // returns time elapsed (in hi-res perf-counts) between Start and Stop call
+  // and increments the time elapsed_
+  time64 Stop();
+  void Reset();
+
+  // return time in seconds, milliseconds, etc.
+  double GetSeconds() const;
+  double GetMilliseconds() const;
+  double GetMicroseconds() const;
+  double GetNanoseconds() const;
+  time64 Get100Nanoseconds() const;
+
+  // TODO(omaha): Probably should have been a static method, or even
+  // standalone func convert the high-perf counter to nano-seconds
+  double PerfCountToNanoSeconds(time64 perf_count) const;
+
+  // get the number of iteration this timer was started/stopped
+  uint32 GetIterations() const { return iterations_; }
+  bool IsRunning() const { return running_; }
+
+  // useful funcs beyond just the Timer class
+  static time64 GetRdtscCounter();  // return perf-counter value
+  static time64 GetRdtscFrequency();  // return perf-counter frequency
+
+    // outputs total time, number of iterations, and the average time
+#ifdef _DEBUG
+  CString DebugString() const;
+#endif
+
+ private:
+
+  bool running_;
+  time64 start_;
+  time64 split_;
+  time64 elapsed_;
+  uint32 iterations_;
+  static time64 count_freq_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(Timer);
+};
+
+// lint -e{533}  Function should return a value
+// lint -e{31}   Redefinition of symbol
+__forceinline time64 Timer::GetRdtscCounter() { __asm rdtsc }
+
+inline double Timer::PerfCountToNanoSeconds(time64 perf_count) const {
+  return (static_cast<double>(perf_count) / static_cast<double>(count_freq_)) *
+         static_cast<double>(1000000000.0);
+}
+
+inline double Timer::GetSeconds() const {
+  return GetNanoseconds() / 1000000000;
+}
+
+inline double Timer::GetMilliseconds() const {
+  return GetNanoseconds() / 1000000;
+}
+
+inline double Timer::GetMicroseconds() const {
+  return GetNanoseconds() / 1000;
+}
+
+inline time64 Timer::Get100Nanoseconds() const {
+  return (time64) GetNanoseconds() / 100;
+}
+
+// Helper class which starts the timer in its constructor and stops it
+// in its destructor.  This prevents accidentally leaving the timer running
+// if a function has an early exit.
+//
+// Usage:
+//
+// class A {
+//   Timer timer_;
+//
+//   void foo(){
+//     TimerScope (timer_);
+//   ......
+//   }  // end foo
+//
+// Everything is timed till the end of the function or when it returns
+// from any place.
+
+class TimerScope {
+ public:
+  explicit TimerScope(Timer *timer) : timer_(timer) {
+    if (timer_) {
+      timer_->Start();
+    }
+  }
+
+  ~TimerScope() {
+    if (timer_ && timer_->IsRunning()) {
+      timer_->Stop();
+    }
+  }
+
+ private:
+  Timer *timer_;
+  DISALLOW_EVIL_CONSTRUCTORS(TimerScope);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_TIMER_H_
+
diff --git a/base/timer_unittest.cc b/base/timer_unittest.cc
new file mode 100644
index 0000000..71058c4
--- /dev/null
+++ b/base/timer_unittest.cc
@@ -0,0 +1,235 @@
+// Copyright 2003-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Timer unittest
+
+#include <cmath>
+#include "omaha/base/time.h"
+#include "omaha/base/timer.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+// The accuracy of the unit test measurements is expected to be within 50 ms.
+// The error varies depending on how the unit test process gets scheduled.
+// The timer test is prone to failing when run by Pulse. Consider diabling the
+// test completely.
+const int kErrorMs = 50;
+
+// The tests that use the Timer class are flaky (see warning in timer.h),
+// and Timer isn't used in production Omaha code, so we leave out everything
+// but the LowResTimer test.
+// TODO(omaha): Is there a better way to do this? Maybe not run on build system?
+#if 0
+
+class TimerTest : public testing::Test {
+ protected:
+  // Set up a test so that we can measure the same time interval using the
+  // low and high resolution timers. If the difference between them is too
+  // big then we consider that the high resolution time is busted and we
+  // stop running the unit tests.
+  // The high resolution timer may have undefined behavior, see the header
+  // file for more comments.
+  static void SetUpTestCase() {
+    const int kSleepMs = 100;
+    const int kDiffMs = 1;
+    LowResTimer t(false);
+    Timer u(false);
+    t.Start();
+    u.Start();
+    ::Sleep(kSleepMs);
+    busted_ = abs(t.GetMilliseconds()- u.GetMilliseconds()) >= kErrorMs;
+  }
+
+  void PrintError() {
+    // This is going to print "Test Foo is busted but passed."
+    printf("is busted but ");
+  }
+
+  static bool busted_;
+};
+
+bool TimerTest::busted_ = false;
+
+#endif  // #if 0
+
+TEST(TimerTest, LowResTimer) {
+  // This test was flaky on the build machine.
+  // TODO(omaha): Is this still the case? Can we improve the test?
+  if (omaha::IsBuildSystem()) {
+    return;
+  }
+
+  LowResTimer t(false);
+
+  const int kSleep1 = 100;
+  t.Start();
+  ::Sleep(kSleep1);
+  uint32 elapsedMs = t.Stop();
+
+  // For the first run of the timer the elapsed value must be equal to
+  // the timer interval.
+  EXPECT_EQ(elapsedMs, t.GetMilliseconds());
+
+  // About 100 ms now.
+  EXPECT_NEAR(kSleep1, elapsedMs, kErrorMs);
+
+  // Test the accessors of different time units.
+  EXPECT_DOUBLE_EQ(t.GetSeconds() * 1000, t.GetMilliseconds());
+
+  const int kSleep2 = 10;
+  t.Start();
+  ::Sleep(kSleep2);
+  elapsedMs = t.Stop();
+  EXPECT_NEAR(kSleep2, elapsedMs, kErrorMs);
+
+  // About 110 ms now.
+  EXPECT_NEAR(kSleep1 + kSleep2, t.GetMilliseconds(), 2 * kErrorMs);
+
+  const int kSleep3 = 50;
+  t.Start();
+  ::Sleep(kSleep3);
+  elapsedMs = t.Stop();
+  EXPECT_NEAR(kSleep3, elapsedMs, kErrorMs);
+
+  // About 160 ms now.
+  EXPECT_NEAR(kSleep1 + kSleep2 + kSleep3, t.GetMilliseconds(), 3 * kErrorMs);
+
+  t.Reset();
+  EXPECT_EQ(0, t.GetMilliseconds());
+}
+
+// Tests disabled, see comment at top of file.
+#if 0
+// Test that values from RTDSC change quickly.
+TEST_F(TimerTest, RTDSC) {
+  uint32 last = 0;
+  for (int i = 0; i < 10; ++i) {
+    uint64 counter = Timer::GetRdtscCounter();
+    uint32 a = *(reinterpret_cast<uint32 *>(&counter));
+    ASSERT_NE(a, last);
+    last = a;
+  }
+}
+
+// Compare everything as ms units for uniformity.
+
+TEST_F(TimerTest, Timer) {
+  if (busted_) {
+    PrintError();
+    return;
+  }
+
+  Timer t(false);
+
+  const int kSleep1 = 100;
+  t.Start();
+  ::Sleep(kSleep1);
+  time64 elapsed = t.Stop();
+
+  // For the first run of the timer the elapsed value must be equal to
+  // the timer interval.
+  EXPECT_DOUBLE_EQ(t.PerfCountToNanoSeconds(elapsed) / 1000000,
+                   t.GetNanoseconds() / 1000000);
+
+  // About 100 ms now.
+  EXPECT_NEAR(kSleep1, t.PerfCountToNanoSeconds(elapsed) / 1000000, kErrorMs);
+
+  // Test the accessors of different time units.
+  EXPECT_DOUBLE_EQ(t.GetSeconds() * 1000, t.GetMilliseconds());
+  EXPECT_DOUBLE_EQ(t.GetMilliseconds() * 1000, t.GetMicroseconds());
+  EXPECT_DOUBLE_EQ(t.GetMicroseconds() * 1000, t.GetNanoseconds());
+
+  EXPECT_NEAR(t.Get100Nanoseconds() * 100.0, t.GetNanoseconds(), 100);
+
+  const int kSleep2 = 10;
+  t.Start();
+  ::Sleep(kSleep2);
+  elapsed = t.Stop();
+  EXPECT_NEAR(kSleep2, t.PerfCountToNanoSeconds(elapsed) / 1000000, kErrorMs);
+
+  // About 110 ms now.
+  EXPECT_NEAR(kSleep1 + kSleep2, t.GetMilliseconds(), 2 * kErrorMs);
+
+  const int kSleep3 = 50;
+  t.Start();
+  ::Sleep(kSleep3);
+  elapsed = t.Stop();
+  EXPECT_NEAR(kSleep3, t.PerfCountToNanoSeconds(elapsed) / 1000000, kErrorMs);
+
+  // About 160 ms now.
+  EXPECT_NEAR(kSleep1 + kSleep2 + kSleep3, t.GetMilliseconds(), 3 * kErrorMs);
+
+  t.Reset();
+  EXPECT_DOUBLE_EQ(0, t.GetMilliseconds());
+}
+
+TEST_F(TimerTest, TimerSplit) {
+  if (busted_) {
+    PrintError();
+    return;
+  }
+
+  const int kSleep1 = 50;
+  const int kSleep2 = 125;
+  const int kSleep3 = 25;
+
+  double split1(0), split2(0), split3(0);
+  double elapsed1(0), elapsed2(0);
+
+  Timer t(false);
+  t.Start();
+  ::Sleep(kSleep1);
+  t.Split(&split1, &elapsed1);
+  EXPECT_NEAR(split1, kSleep1, kErrorMs);
+  EXPECT_NEAR(elapsed1, kSleep1, kErrorMs);
+  EXPECT_DOUBLE_EQ(split1, elapsed1);
+
+  ::Sleep(kSleep2);
+  t.Split(&split2, &elapsed2);
+  EXPECT_NEAR(split2, kSleep2, kErrorMs);
+  EXPECT_DOUBLE_EQ(split1 + split2, elapsed2);
+
+  ::Sleep(kSleep3);
+  t.Split(&split3, NULL);
+  t.Stop();
+  EXPECT_NEAR(split3, kSleep3, kErrorMs);
+  EXPECT_NEAR(split1 + split2 + split3, t.GetMilliseconds(), kErrorMs);
+}
+
+// Time QueryPerformanceCounter
+TEST_F(TimerTest, QueryPerformanceCounter) {
+  if (busted_) {
+    PrintError();
+    return;
+  }
+
+  Timer t(false);
+  t.Start();
+
+  const int kIterations = 100;
+  LARGE_INTEGER count = {0};
+  for (int i = 0; i < kIterations; i++) {
+    ASSERT_TRUE(::QueryPerformanceCounter(&count));
+  }
+
+  t.Stop();
+
+  // Expect the call to take anywhere up to 1000 nano seconds.
+  EXPECT_NEAR(t.GetNanoseconds() / kIterations, 500, 500);
+}
+#endif  // #if 0
+
+}  // namespace omaha
diff --git a/base/tr_rand.cc b/base/tr_rand.cc
new file mode 100644
index 0000000..70ca496
--- /dev/null
+++ b/base/tr_rand.cc
@@ -0,0 +1,39 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+//
+// Simple pseudo-random number generator.
+// Not intended for anything but random-to-humans numbers.
+// Very fast, and has a period of 32768 for all seed values (including zero).
+// Returns values in the range 0..0xFFFF (inclusive).
+//
+
+#include "omaha/base/tr_rand.h"
+
+namespace omaha {
+
+static int rand_val = 0;
+
+void tr_srand(unsigned int seed) {
+  rand_val = seed & 0xFFFF;
+}
+
+int tr_rand() {
+  rand_val = ((rand_val * 75) + 1) & 0xFFFF;
+  return rand_val;
+}
+
+}  // namespace omaha
+
diff --git a/common/tr_rand.h b/base/tr_rand.h
similarity index 100%
rename from common/tr_rand.h
rename to base/tr_rand.h
diff --git a/base/tr_rand_unittest.cc b/base/tr_rand_unittest.cc
new file mode 100644
index 0000000..64a1500
--- /dev/null
+++ b/base/tr_rand_unittest.cc
@@ -0,0 +1,66 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+
+#include <cstring>
+#include "omaha/base/tr_rand.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(TRRandTest, TRRand) {
+  int min_period = +99999;
+  int max_period = -99999;
+
+  int min_period_at = -99999;
+  int max_period_at = -99999;
+
+  byte hits[65536] = {0};
+  memset(hits, 0, sizeof(hits));
+
+  // Compute minimum and maximum period by considering all possible seed values.
+  for (int seed = 0; seed < 65536; ++seed) {
+    // See if value is part of some known sequence we've traversed.
+    // If multiple values map to same next-val, this check could cause us to
+    // report a min_period that's too short. But a long min_period still
+    // indicates success.
+    if (hits[seed]) { continue; }
+
+    // Compute length of period starting at this seed.
+    tr_srand(seed);
+    int i = seed;
+    int period = 0;
+    do {
+      ++hits[i];
+      ++period;
+      i = tr_rand();
+      ASSERT_GE(i, 0);
+    } while (hits[i] == 0);
+
+    // Update stats.
+    if (period < min_period) {
+      min_period = period;
+      min_period_at = seed;
+    }
+    if (period > max_period) {
+      max_period = period;
+      max_period_at = seed;
+    }
+  }
+  ASSERT_GE(min_period, (0xFFFF / 2));
+}
+
+}  // namespace omaha
+
diff --git a/common/type_utils.h b/base/type_utils.h
similarity index 100%
rename from common/type_utils.h
rename to base/type_utils.h
diff --git a/base/user_info.cc b/base/user_info.cc
new file mode 100644
index 0000000..a02faf1
--- /dev/null
+++ b/base/user_info.cc
@@ -0,0 +1,124 @@
+// Copyright 2004-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include "omaha/base/user_info.h"
+
+#include <windows.h>
+#include <security.h>
+#include <secext.h>
+#include <sddl.h>
+#include <lmcons.h>
+#include <atlsecurity.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/scoped_any.h"
+
+namespace omaha {
+
+namespace user_info {
+
+HRESULT GetProcessUser(CString* name, CString* domain, CString* sid) {
+  CSid current_sid;
+
+  HRESULT hr = GetProcessUserSid(&current_sid);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (sid != NULL) {
+    *sid = current_sid.Sid();
+  }
+  if (name != NULL) {
+    *name = current_sid.AccountName();
+  }
+  if (domain != NULL) {
+    *domain = current_sid.Domain();
+  }
+  return S_OK;
+}
+
+HRESULT GetProcessUserSid(CSid* sid) {
+  ASSERT1(sid);
+
+  CAccessToken token;
+  if (!token.GetProcessToken(TOKEN_QUERY) || !token.GetUser(sid)) {
+    HRESULT hr = HRESULTFromLastError();
+
+    // Assert only if thread_sid is populated. This is to eliminate other
+    // reasons for GetProcessToken/GetUser to fail.
+    CString thread_sid;
+    ASSERT(FAILED(GetThreadUserSid(&thread_sid)),
+           (_T("[Did you mean to call GetThreadUserSid?][0x%x][%s]"),
+            hr, thread_sid));
+
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT IsLocalSystemUser(bool* is_local_system, CString* user_sid) {
+  ASSERT1(is_local_system);
+
+  CString sid;
+  HRESULT hr = GetProcessUser(NULL, NULL, &sid);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  *is_local_system = sid.CompareNoCase(kLocalSystemSid) == 0;
+  if (user_sid) {
+    user_sid->SetString(sid);
+  }
+  return S_OK;
+}
+
+HRESULT GetThreadUserSid(CString* sid) {
+  ASSERT1(sid);
+  CAccessToken access_token;
+  CSid user_sid;
+  if (access_token.GetThreadToken(TOKEN_READ) &&
+      access_token.GetUser(&user_sid)) {
+    sid->SetString(user_sid.Sid());
+    return S_OK;
+  } else {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(L2, (_T("[GetThreadUserSid failed][0x%x]"), hr));
+    return hr;
+  }
+}
+
+HRESULT GetEffectiveUserSid(CString* sid) {
+  HRESULT hr = GetThreadUserSid(sid);
+  return SUCCEEDED(hr) ? hr : GetProcessUser(NULL, NULL, sid);
+}
+
+bool IsRunningAsSystem() {
+  CString sid;
+  return SUCCEEDED(GetEffectiveUserSid(&sid)) ? IsLocalSystemSid(sid) : false;
+}
+
+bool IsThreadImpersonating() {
+  CAccessToken access_token;
+  return access_token.GetThreadToken(TOKEN_READ);
+}
+
+}  // namespace user_info
+
+}  // namespace omaha
+
diff --git a/base/user_info.h b/base/user_info.h
new file mode 100644
index 0000000..d94452c
--- /dev/null
+++ b/base/user_info.h
@@ -0,0 +1,66 @@
+// Copyright 2004-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Class UserInfo: Information related to the current user or other users on
+// this machine.
+//
+// TODO(omaha): seems we can merge this module with user_rights.
+
+#ifndef OMAHA_COMMON_USER_INFO_H__
+#define OMAHA_COMMON_USER_INFO_H__
+
+#include <windows.h>
+#include <atlsecurity.h>
+#include <atlstr.h>
+
+namespace omaha {
+
+namespace user_info {
+
+// Gets the user name, domain, and the SID associated with the access token
+// of the current process.
+HRESULT GetProcessUser(CString* name, CString* domain, CString* sid);
+
+// Gets SID associated with the access token of the current process.
+HRESULT GetProcessUserSid(CSid* sid);
+
+// Gets the user SID associated with the access token of the current thread if
+// the thread is impersonating. If the thread is not impersonating, the API
+// fails with ERROR_NO_TOKEN.
+HRESULT GetThreadUserSid(CString* sid);
+
+// Gets the user SID associated with the access token of the current thread if
+// the thread is impersonating. Otherwise, gets the SID associated with the
+// access token of the current process.
+HRESULT GetEffectiveUserSid(CString* sid);
+
+// TODO(omaha): deprecate weird API.
+// Looks at the current user SID and checks if it's the same as the
+// LocalSystem user.
+HRESULT IsLocalSystemUser(bool* is_local_system,
+                          CString* user_sid);     // optional.
+
+// Returns true if the caller's impersonation or process access token user
+// is LOCAL_SYSTEM.
+bool IsRunningAsSystem();
+
+// Returns true if the current thread is impersonating.
+bool IsThreadImpersonating();
+
+}  // namespace user_info
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_USER_INFO_H__
diff --git a/base/user_info_unittest.cc b/base/user_info_unittest.cc
new file mode 100644
index 0000000..c938d3d
--- /dev/null
+++ b/base/user_info_unittest.cc
@@ -0,0 +1,140 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/constants.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/scoped_impersonation.h"
+#include "omaha/base/user_info.h"
+#include "omaha/testing/unit_test.h"
+
+// We can't make any assumption about the context the unit test runs, however
+// we expect the calls to succeed.
+namespace omaha {
+
+namespace {
+
+const TCHAR kNtNonUniqueIdPrefix[] = _T("S-1-5-21-");
+const int kNtNonUniqueIdPrefixLength = arraysize(kNtNonUniqueIdPrefix) - 1;
+
+}  // namespace
+
+TEST(UserInfoTest, GetProcessUser) {
+  CString name, domain, sid;
+  EXPECT_HRESULT_SUCCEEDED(user_info::GetProcessUser(&name, &domain, &sid));
+
+  EXPECT_FALSE(name.IsEmpty());
+  EXPECT_FALSE(domain.IsEmpty());
+  if (user_info::IsRunningAsSystem()) {
+    EXPECT_STREQ(kLocalSystemSid, sid);
+  } else {
+    EXPECT_STREQ(kNtNonUniqueIdPrefix, sid.Left(kNtNonUniqueIdPrefixLength));
+  }
+}
+
+TEST(UserInfoTest, GetProcessUser_SidOnly) {
+  CString name, domain, sid1;
+  EXPECT_HRESULT_SUCCEEDED(user_info::GetProcessUser(&name, &domain, &sid1));
+
+  if (user_info::IsRunningAsSystem()) {
+    EXPECT_STREQ(kLocalSystemSid, sid1);
+  } else {
+    EXPECT_STREQ(kNtNonUniqueIdPrefix, sid1.Left(kNtNonUniqueIdPrefixLength));
+  }
+
+  CString sid2;
+  EXPECT_HRESULT_SUCCEEDED(user_info::GetProcessUser(NULL, NULL, &sid2));
+  EXPECT_STREQ(sid1, sid2);
+}
+
+
+TEST(UserInfoTest, GetProcessUserSid) {
+  CSid sid;
+  EXPECT_HRESULT_SUCCEEDED(user_info::GetProcessUserSid(&sid));
+
+  const CString name = sid.AccountName();
+  EXPECT_FALSE(name.IsEmpty());
+  const CString sid_string = sid.Sid();
+  if (user_info::IsRunningAsSystem()) {
+    EXPECT_STREQ(kLocalSystemSid, sid_string);
+  } else {
+    EXPECT_STREQ(kNtNonUniqueIdPrefix,
+                 sid_string.Left(kNtNonUniqueIdPrefixLength));
+  }
+
+  EXPECT_EQ(1, sid.GetPSID()->Revision);
+
+  const SID_IDENTIFIER_AUTHORITY kNtAuthority = SECURITY_NT_AUTHORITY;
+  const SID_IDENTIFIER_AUTHORITY* authority =
+      sid.GetPSID_IDENTIFIER_AUTHORITY();
+  for (int i = 0; i < arraysize(authority->Value); ++i) {
+    EXPECT_EQ(kNtAuthority.Value[i], authority->Value[i]);
+  }
+
+  UCHAR expected_auth_count = user_info::IsRunningAsSystem() ?  1 : 5;
+  EXPECT_EQ(expected_auth_count, sid.GetSubAuthorityCount());
+
+  DWORD expected_authority = user_info::IsRunningAsSystem() ?
+                                 SECURITY_LOCAL_SYSTEM_RID :
+                                 SECURITY_NT_NON_UNIQUE;
+  EXPECT_EQ(expected_authority, sid.GetSubAuthority(0));
+  EXPECT_LT(static_cast<DWORD>(DOMAIN_USER_RID_MAX), sid.GetSubAuthority(4));
+}
+
+// Expect the unit tests do not run impersonated.
+TEST(UserInfoTest, GetThreadUserSid) {
+  CString thread_sid;
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_NO_TOKEN),
+            user_info::GetThreadUserSid(&thread_sid));
+}
+
+// Expect the unit tests do not run impersonated.
+// TODO(omaha3): Assuming we are running as admin, is there anything we can
+// impersonate so that this important path gets tested?
+TEST(UserInfoTest, GetEffectiveUserSid) {
+  CString thread_sid;
+  EXPECT_HRESULT_SUCCEEDED(user_info::GetEffectiveUserSid(&thread_sid));
+  CSid process_sid;
+  EXPECT_HRESULT_SUCCEEDED(user_info::GetProcessUserSid(&process_sid));
+  EXPECT_STREQ(process_sid.Sid(), thread_sid);
+}
+
+TEST(UserInfoTest, IsLocalSystemUser) {
+  bool is_system = false;
+  CString sid;
+  EXPECT_HRESULT_SUCCEEDED(user_info::IsLocalSystemUser(&is_system, &sid));
+}
+
+TEST(UserInfoTest, IsThreadImpersonating) {
+  EXPECT_FALSE(user_info::IsThreadImpersonating());
+
+  scoped_handle process_token;
+  EXPECT_NE(0, ::OpenProcessToken(GetCurrentProcess(),
+                                  TOKEN_ALL_ACCESS,
+                                  address(process_token)));
+
+  scoped_handle restricted_token;
+  EXPECT_NE(0, ::CreateRestrictedToken(get(process_token),
+                                       DISABLE_MAX_PRIVILEGE,
+                                       0, NULL,
+                                       0, NULL,
+                                       0, NULL,
+                                       address(restricted_token)));
+
+  scoped_impersonation impersonate_user(get(restricted_token));
+
+  EXPECT_TRUE(user_info::IsThreadImpersonating());
+}
+
+}  // namespace omaha
diff --git a/base/user_rights.cc b/base/user_rights.cc
new file mode 100644
index 0000000..4526e92
--- /dev/null
+++ b/base/user_rights.cc
@@ -0,0 +1,231 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/user_rights.h"
+#include <lm.h>
+#include <wtsapi32.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/vistautil.h"
+
+namespace omaha {
+
+bool UserRights::TokenIsAdmin(HANDLE token) {
+  return BelongsToGroup(token, DOMAIN_ALIAS_RID_ADMINS);
+}
+
+bool UserRights::UserIsAdmin() {
+  return BelongsToGroup(NULL, DOMAIN_ALIAS_RID_ADMINS);
+}
+
+bool UserRights::UserIsUser() {
+  return BelongsToGroup(NULL, DOMAIN_ALIAS_RID_USERS);
+}
+
+bool UserRights::UserIsPowerUser() {
+  return BelongsToGroup(NULL, DOMAIN_ALIAS_RID_POWER_USERS);
+}
+
+bool UserRights::UserIsGuest() {
+  return BelongsToGroup(NULL, DOMAIN_ALIAS_RID_GUESTS);
+}
+
+bool UserRights::BelongsToGroup(HANDLE token, int group_id) {
+  SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY;
+  PSID group = NULL;
+
+  BOOL check = ::AllocateAndInitializeSid(&nt_authority,
+                                          2,
+                                          SECURITY_BUILTIN_DOMAIN_RID,
+                                          group_id,
+                                          0,
+                                          0,
+                                          0,
+                                          0,
+                                          0,
+                                          0,
+                                          &group);
+  if (check) {
+    if (!::CheckTokenMembership(token, group, &check)) {
+      check = false;
+    }
+    ::FreeSid(group);
+  }
+  return !!check;
+}
+
+bool UserRights::UserIsRestricted() {
+  scoped_handle token;
+  if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, address(token))) {
+    UTIL_LOG(LE, (_T("[UserRights::UserIsRestricted - OpenProcessToken failed]")
+                  _T("[0x%08x]"), HRESULTFromLastError()));
+    return true;
+  }
+
+  return !!::IsTokenRestricted(get(token));
+}
+
+bool UserRights::UserIsLowOrUntrustedIntegrity() {
+  if (SystemInfo::IsRunningOnVistaOrLater()) {
+    MANDATORY_LEVEL integrity_level = MandatoryLevelUntrusted;
+    if (FAILED(vista_util::GetProcessIntegrityLevel(0, &integrity_level)) ||
+        integrity_level == MandatoryLevelUntrusted ||
+        integrity_level == MandatoryLevelLow) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+HRESULT UserRights::UserIsLoggedOnInteractively(bool* is_logged_on) {
+  ASSERT1(is_logged_on);
+
+  *is_logged_on = false;
+
+  HRESULT hr = S_OK;
+
+  // Get the user associated with the current process.
+  WKSTA_USER_INFO_1* user_info = NULL;
+  NET_API_STATUS status = ::NetWkstaUserGetInfo(
+                              NULL,
+                              1,
+                              reinterpret_cast<uint8**>(&user_info));
+  if (status != NERR_Success || user_info == NULL) {
+    UTIL_LOG(LE, (_T("[NetWkstaUserGetInfo failed][%u]"), status));
+    return HRESULT_FROM_WIN32(status);
+  }
+  ON_SCOPE_EXIT(::NetApiBufferFree, user_info);
+
+  UTIL_LOG(L2, (_T("[wks domain=%s][wks user=%s]"),
+                user_info->wkui1_logon_domain, user_info->wkui1_username));
+
+  PWTS_SESSION_INFOW session_info = NULL;
+  const DWORD kVersion = 1;
+  DWORD num_sessions = 0;
+  if (!::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE,
+                              0,
+                              kVersion,
+                              &session_info,
+                              &num_sessions)) {
+    hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[WTSEnumerateSessions failed][0x%08x]"), hr));
+    return hr;
+  }
+  ON_SCOPE_EXIT(::WTSFreeMemory, session_info);
+
+  // Loop through all active sessions to see whether one of the sessions
+  // belongs to current user. If so, regard this user as "logged-on".
+  for (DWORD i = 0; i < num_sessions; ++i) {
+    TCHAR* domain_name = NULL;
+    DWORD domain_name_len = 0;
+    if (!::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,
+                                      session_info[i].SessionId,
+                                      WTSDomainName,
+                                      &domain_name,
+                                      &domain_name_len)) {
+      hr = HRESULTFromLastError();
+      UTIL_LOG(LE, (_T("[WTSQuerySessionInformation failed][0x%08x]"), hr));
+      continue;
+    }
+    ON_SCOPE_EXIT(::WTSFreeMemory, domain_name);
+
+    TCHAR* user_name = NULL;
+    DWORD user_name_len = 0;
+    if (!::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,
+                                      session_info[i].SessionId,
+                                      WTSUserName,
+                                      &user_name,
+                                      &user_name_len)) {
+      hr = HRESULTFromLastError();
+      UTIL_LOG(LE, (_T("[WTSQuerySessionInformation failed][0x%08x]"), hr));
+      continue;
+    }
+    ON_SCOPE_EXIT(::WTSFreeMemory, user_name);
+
+    UTIL_LOG(L2, (_T("[ts domain=%s][ts user=%s][station=%s]"),
+                  domain_name,
+                  user_name,
+                  session_info[i].pWinStationName));
+
+    // Occasionally, the domain name and user name could not be retrieved when
+    // the program is started just at logon time.
+    if (!(domain_name && *domain_name && user_name && *user_name)) {
+      hr = E_FAIL;
+      continue;
+    }
+
+    if (_tcsicmp(user_info->wkui1_logon_domain, domain_name) == 0 &&
+        _tcsicmp(user_info->wkui1_username, user_name) == 0) {
+      *is_logged_on = true;
+      return S_OK;
+    }
+  }
+
+  return hr;
+}
+
+// Returns a token with TOKEN_ALL_ACCESS rights. At the moment, we only require
+// TOKEN_QUERY | TOKEN_ASSIGN_PRIMARY, but requirements may change in the
+// future.
+HRESULT UserRights::GetCallerToken(CAccessToken* token) {
+  ASSERT1(token);
+
+  CComPtr<IUnknown> security_context;
+  HRESULT hr = ::CoGetCallContext(IID_PPV_ARGS(&security_context));
+  if (SUCCEEDED(hr)) {
+    return token->OpenCOMClientToken(TOKEN_ALL_ACCESS) ? S_OK :
+                                                         HRESULTFromLastError();
+  } else if (hr != RPC_E_CALL_COMPLETE) {
+    UTIL_LOG(LE, (_T("[::CoGetCallContext failed][0x%x]"), hr));
+    return hr;
+  }
+
+  // RPC_E_CALL_COMPLETE indicates an in-proc intra-apartment call. Return the
+  // current process token.
+  return token->OpenThreadToken(TOKEN_ALL_ACCESS) ? S_OK :
+                                                    HRESULTFromLastError();
+}
+
+bool UserRights::VerifyCallerIsAdmin() {
+  CAccessToken impersonated_token;
+  if (FAILED(GetCallerToken(&impersonated_token))) {
+    return false;
+  }
+  return TokenIsAdmin(impersonated_token.GetHandle());
+}
+
+bool UserRights::VerifyCallerIsSystem() {
+  CAccessToken impersonated_token;
+  if (FAILED(GetCallerToken(&impersonated_token))) {
+    return false;
+  }
+
+  CSid sid;
+  if (!impersonated_token.GetUser(&sid)) {
+    return false;
+  }
+
+  return sid == Sids::System();
+}
+
+}  // namespace omaha
+
diff --git a/base/user_rights.h b/base/user_rights.h
new file mode 100644
index 0000000..01aefd6
--- /dev/null
+++ b/base/user_rights.h
@@ -0,0 +1,77 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// This class finds out different user rights on the system.
+// For example it can find out if the user is an administrator.
+
+#ifndef OMAHA_BASE_USER_RIGHTS_H_
+#define OMAHA_BASE_USER_RIGHTS_H_
+
+#include <windows.h>
+#include <atlsecurity.h>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+class UserRights {
+ public:
+
+  // Returns true if token is a member of the local Administrators group.
+  static bool TokenIsAdmin(HANDLE token);
+
+  // Returns true if the user belongs to the local Administrators group.
+  static bool UserIsAdmin();
+
+  // Returns true if the user belongs to the Users group.
+  static bool UserIsUser();
+
+  // Returns true if the user belongs to the Power User group.
+  static bool UserIsPowerUser();
+
+  // Returns true if the user is a Guest.
+  static bool UserIsGuest();
+
+  // Returns true if the owner of the current process has a restricted token.
+  static bool UserIsRestricted();
+
+  // Returns true if the owner of the current process runs under low or
+  // untrusted integrity on Vista.
+  static bool UserIsLowOrUntrustedIntegrity();
+
+  // Returns true if the owner of the current process has an interactive
+  // session: console, terminal services, or fast user switching.
+  static HRESULT UserIsLoggedOnInteractively(bool* is_logged_on);
+
+  // Gets the COM caller's impersonation token. If not in an inter-apartment COM
+  // call, returns the current process token.
+  static HRESULT GetCallerToken(CAccessToken* token);
+
+  static bool VerifyCallerIsAdmin();
+
+  static bool VerifyCallerIsSystem();
+
+  // Returns true if the owner of the current process is the primary logon token
+  // for the current interactive session: console, terminal services, or fast
+  // user switching.
+  static bool BelongsToGroup(HANDLE token, int group_id);
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(UserRights);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_USER_RIGHTS_H_
+
diff --git a/base/user_rights_unittest.cc b/base/user_rights_unittest.cc
new file mode 100644
index 0000000..435507f
--- /dev/null
+++ b/base/user_rights_unittest.cc
@@ -0,0 +1,33 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/testing/unit_test.h"
+#include "omaha/base/user_rights.h"
+
+namespace omaha {
+
+TEST(UserRightsTest, UserIsLoggedOnInteractively) {
+  if (IsTestRunByLocalSystem()) {
+    return;
+  }
+
+  bool is_logged_on(false);
+  EXPECT_HRESULT_SUCCEEDED(
+    UserRights::UserIsLoggedOnInteractively(&is_logged_on));
+  EXPECT_TRUE(is_logged_on);
+}
+
+}  // namespace omaha
+
diff --git a/base/utils.cc b/base/utils.cc
new file mode 100644
index 0000000..fbd0f0a
--- /dev/null
+++ b/base/utils.cc
@@ -0,0 +1,2044 @@
+// Copyright 2003-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/utils.h"
+
+#include <ras.h>
+#include <regstr.h>
+#include <urlmon.h>
+#include <wincrypt.h>
+#include <ATLComTime.h>
+#include <atlpath.h>
+#include <map>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/const_config.h"
+#include "omaha/base/const_timeouts.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/process.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/shell.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/string.h"
+#include "omaha/base/system.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/time.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/user_rights.h"
+#include "omaha/base/vistautil.h"
+
+namespace omaha {
+
+namespace {
+
+// Private object namespaces for Vista processes.
+const TCHAR* const kGoopdateBoundaryDescriptor = _T("GoogleUpdate_BD");
+const TCHAR* const kGoopdatePrivateNamespace = _T("GoogleUpdate");
+const TCHAR* const kGoopdatePrivateNamespacePrefix = _T("GoogleUpdate\\");
+
+// Helper for IsPrivateNamespaceAvailable().
+// For simplicity, the handles opened here are leaked. We need these until
+// process exit, at which point they will be cleaned up automatically by the OS.
+bool EnsurePrivateNamespaceAvailable() {
+  HANDLE boundary_descriptor =
+      CreateBoundaryDescriptorWWrap(kGoopdateBoundaryDescriptor, 0);
+  if (NULL == boundary_descriptor) {
+    DWORD last_error(::GetLastError());
+    UTIL_LOG(LE, (_T("CreateBoundaryDescriptor failed[%d]"), last_error));
+    return false;
+  }
+
+  char sid[SECURITY_MAX_SID_SIZE] = {0};
+  DWORD size = sizeof(sid);
+  // Mark the boundary descriptor with the Admins Group SID. Consequently, all
+  // admins, including SYSTEM, will create objects in the same private
+  // namespace.
+  if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, sid, &size)) {
+    UTIL_LOG(LE, (_T("[::CreateWellKnownSid failed][%d]"), ::GetLastError()));
+    return false;
+  }
+  if (!AddSIDToBoundaryDescriptorWrap(&boundary_descriptor, sid)) {
+    UTIL_LOG(LE, (_T("[AddSIDToBoundaryDescriptor failed][%d]"),
+                  ::GetLastError()));
+    return false;
+  }
+
+  NamedObjectAttributes attr;
+  GetAdminDaclSecurityAttributes(&attr.sa, GENERIC_ALL);
+  // The private namespace created here will be used to create objects of the
+  // form "GoogleUpdate\xyz". As the article "Object Namespaces" on MSDN
+  // explains, these kernel objects are safe from squatting attacks from lower
+  // integrity processes.
+  HANDLE namespace_handle =
+      CreatePrivateNamespaceWWrap(&attr.sa,
+                                  boundary_descriptor,
+                                  kGoopdatePrivateNamespace);
+  if (namespace_handle) {
+    return true;
+  }
+  ASSERT(ERROR_ALREADY_EXISTS == ::GetLastError(),
+         (_T("CreatePrivateNamespaceW failed: %d"), ::GetLastError()));
+
+  // Another process has already created the namespace. Attempt to open.
+  namespace_handle = OpenPrivateNamespaceWWrap(boundary_descriptor,
+                                               kGoopdatePrivateNamespace);
+  if (namespace_handle || ::GetLastError() == ERROR_DUP_NAME) {
+    // ERROR_DUP_NAME indicates that we have called CreatePrivateNamespaceWWrap
+    // or OpenPrivateNamespaceWWrap before in the same process. Either way, we
+    // can now create objects prefixed with our private namespace.
+    return true;
+  }
+
+  ASSERT(namespace_handle, (_T("[Could not open private namespace][%d]"),
+                            ::GetLastError()));
+  return false;
+}
+
+}  // namespace
+
+// Returns 0 if an error occurs.
+ULONGLONG VersionFromString(const CString& s) {
+  int pos(0);
+  unsigned int quad[4] = {0, 0, 0, 0};
+
+  for (int i = 0; i < 4; ++i) {
+    CString q = s.Tokenize(_T("."), pos);
+    if (pos == -1) {
+      return 0;
+    }
+
+    int quad_value(0);
+    if (!String_StringToDecimalIntChecked(q, &quad_value)) {
+      return 0;
+    }
+
+    quad[i] = static_cast<unsigned int>(quad_value);
+
+    if (kuint16max < quad[i]) {
+      return 0;
+    }
+  }
+
+  if (s.GetLength() + 1 != pos) {
+    return 0;
+  }
+
+  return MAKEDLLVERULL(quad[0], quad[1], quad[2], quad[3]);
+}
+
+CString StringFromVersion(ULONGLONG version) {
+  const WORD version_major = HIWORD(version >> 32);
+  const WORD version_minor = LOWORD(version >> 32);
+  const WORD version_build = HIWORD(version);
+  const WORD version_patch = LOWORD(version);
+
+  CString version_string;
+  version_string.Format((_T("%u.%u.%u.%u")),
+                        version_major,
+                        version_minor,
+                        version_build,
+                        version_patch);
+  return version_string;
+}
+
+CString GetCurrentDir() {
+  TCHAR cur_dir[MAX_PATH] = {0};
+  if (!::GetCurrentDirectory(MAX_PATH, cur_dir)) {
+    return CString(_T('.'));
+  }
+  return CString(cur_dir);
+}
+
+HRESULT GetNewFileNameInDirectory(const CString& dir, CString* file_name) {
+  ASSERT1(file_name);
+
+  GUID guid = {0};
+  HRESULT hr = ::CoCreateGuid(&guid);
+  if (FAILED(hr)) {
+    CORE_LOG(LEVEL_WARNING, (_T("[CoCreateGuid failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  CString guid_file_name = GuidToString(guid);
+  CPath file_path(dir);
+  file_path.Append(guid_file_name);
+
+  *file_name = static_cast<const TCHAR*>(file_path);
+  return S_OK;
+}
+
+// determines if a time is in the distant past, present, or future
+TimeCategory GetTimeCategory(const time64 system_time) {
+  time64 now = GetCurrent100NSTime();
+
+  // Times more than a few days in the future are wrong [I will allow a little
+  // leeway, since it could be set in another future time zone, or a program
+  // that likes UNC]]
+  if (system_time > (now + kDaysTo100ns * 5)) {
+    return FUTURE;
+  }
+
+  // times more than 40 years ago are wrong
+  if (system_time < (now - kDaysTo100ns * 365 * 40)) {
+    return PAST;
+  }
+
+  return PRESENT;
+}
+
+// Determine if a given time is probably valid
+bool IsValidTime(const time64 t) {
+  return (GetTimeCategory(t) == PRESENT);
+}
+
+LARGE_INTEGER MSto100NSRelative(DWORD ms) {
+  const __int64 convert_ms_to_100ns_units = 1000 /*ms/us*/ * 10 /*us/100ns*/;
+  __int64 timeout_100ns = static_cast<__int64>(ms) * convert_ms_to_100ns_units;
+  LARGE_INTEGER timeout = {0};
+  timeout.QuadPart = -timeout_100ns;
+  return timeout;
+}
+
+// Local System and admins get admin_access_mask. Authenticated non-admins get
+// non_admin_access_mask access.
+void GetEveryoneDaclSecurityDescriptor(CSecurityDesc* sd,
+                                       ACCESS_MASK admin_access_mask,
+                                       ACCESS_MASK non_admin_access_mask) {
+  ASSERT1(sd);
+
+  CDacl dacl;
+  dacl.AddAllowedAce(Sids::System(), admin_access_mask);
+  dacl.AddAllowedAce(Sids::Admins(), admin_access_mask);
+  dacl.AddAllowedAce(Sids::Interactive(), non_admin_access_mask);
+
+  sd->SetDacl(dacl);
+  sd->MakeAbsolute();
+}
+
+void GetAdminDaclSecurityDescriptor(CSecurityDesc* sd, ACCESS_MASK accessmask) {
+  ASSERT1(sd);
+
+  CDacl dacl;
+  dacl.AddAllowedAce(Sids::System(), accessmask);
+  dacl.AddAllowedAce(Sids::Admins(), accessmask);
+
+  sd->SetOwner(Sids::Admins());
+  sd->SetGroup(Sids::Admins());
+  sd->SetDacl(dacl);
+  sd->MakeAbsolute();
+}
+
+void GetAdminDaclSecurityAttributes(CSecurityAttributes* sec_attr,
+                                    ACCESS_MASK accessmask) {
+  ASSERT1(sec_attr);
+  CSecurityDesc sd;
+  GetAdminDaclSecurityDescriptor(&sd, accessmask);
+  sec_attr->Set(sd);
+}
+
+HRESULT InitializeClientSecurity() {
+  return ::CoInitializeSecurity(
+      NULL,
+      -1,
+      NULL,   // Let COM choose what authentication services to register.
+      NULL,
+      RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  // Data integrity and encryption.
+      RPC_C_IMP_LEVEL_IMPERSONATE,    // Allow server to impersonate.
+      NULL,
+      EOAC_DYNAMIC_CLOAKING,
+      NULL);
+}
+
+HRESULT InitializeServerSecurity(bool allow_calls_from_medium) {
+  CSecurityDesc sd;
+  DWORD eole_auth_capabilities = EOAC_DYNAMIC_CLOAKING;
+  if (allow_calls_from_medium) {
+    GetEveryoneDaclSecurityDescriptor(&sd,
+                                      COM_RIGHTS_EXECUTE,
+                                      COM_RIGHTS_EXECUTE);
+    sd.SetOwner(Sids::Admins());
+    sd.SetGroup(Sids::Admins());
+  } else if (user_info::IsRunningAsSystem()) {
+    GetAdminDaclSecurityDescriptor(&sd, COM_RIGHTS_EXECUTE);
+  }
+
+  HRESULT hr = ::CoInitializeSecurity(
+      const_cast<SECURITY_DESCRIPTOR*>(sd.GetPSECURITY_DESCRIPTOR()),
+      -1,
+      NULL,
+      NULL,
+      RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
+      RPC_C_IMP_LEVEL_IDENTIFY,
+      NULL,
+      eole_auth_capabilities,
+      NULL);
+  ASSERT(SUCCEEDED(hr), (_T("[InitializeServerSecurity failed][0x%x]"), hr));
+  return hr;
+}
+
+// The IGlobalOptions interface is supported from Vista onwards. Callers
+// should probably ignore the HRESULT returned, since it is not a critical error
+// if turning off exception handling fails.
+HRESULT DisableCOMExceptionHandling() {
+  CComPtr<IGlobalOptions> options;
+  HRESULT hr = options.CoCreateInstance(CLSID_GlobalOptions);
+  if (SUCCEEDED(hr)) {
+    hr = options->Set(COMGLB_EXCEPTION_HANDLING, COMGLB_EXCEPTION_DONOT_HANDLE);
+  }
+
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[DisableCOMExceptionHandling failed][0x%x]"), hr));
+  }
+
+  return hr;
+}
+
+// This function is not thread-safe.
+bool IsPrivateNamespaceAvailable(bool is_machine) {
+  static bool is_initialized = false;
+  static bool is_available = false;
+
+  if (!is_machine) {
+    // TODO(Omaha): From a security viewpoint, private namespaces do not add
+    // much value for the User Omaha. But from a uniformity perspective, makes
+    // sense to use for both.
+    return false;
+  }
+
+  if (is_initialized) {
+    return is_available;
+  }
+
+  if (!SystemInfo::IsRunningOnVistaOrLater()) {
+    is_available = false;
+    is_initialized = true;
+    return false;
+  }
+
+  is_available = EnsurePrivateNamespaceAvailable();
+  is_initialized = true;
+  return is_available;
+}
+
+
+void GetNamedObjectAttributes(const TCHAR* base_name,
+                              bool is_machine,
+                              NamedObjectAttributes* attr) {
+  ASSERT1(base_name);
+  ASSERT1(attr);
+
+  // TODO(Omaha): Enable this code after we have a better understanding of
+  // Private Object Namespaces.
+#if 0
+  if (IsPrivateNamespaceAvailable(is_machine)) {
+    attr->name = kGoopdatePrivateNamespacePrefix;
+  } else {
+    ASSERT1(!SystemInfo::IsRunningOnVistaOrLater());
+#endif
+
+  attr->name = omaha::kGlobalPrefix;
+
+  if (!is_machine) {
+    CString user_sid;
+    VERIFY1(SUCCEEDED(omaha::user_info::GetProcessUser(NULL, NULL, &user_sid)));
+    attr->name += user_sid;
+  } else {
+    // Grant access to administrators and system.
+    GetAdminDaclSecurityAttributes(&attr->sa, GENERIC_ALL);
+  }
+
+  attr->name += base_name;
+  UTIL_LOG(L1, (_T("[GetNamedObjectAttributes][named_object=%s]"), attr->name));
+}
+
+// For now, required_ace_flags is only supported for SE_REGISTRY_KEY objects.
+// INHERITED_ACE may be added to the read ACE flags, so it is excluded from
+// the comparison with required_ace_flags.
+HRESULT AddAllowedAce(const TCHAR* object_name,
+                      SE_OBJECT_TYPE object_type,
+                      const CSid& sid,
+                      ACCESS_MASK required_permissions,
+                      uint8 required_ace_flags) {
+  ASSERT1(SE_REGISTRY_KEY == object_type || !required_ace_flags);
+  ASSERT1(0 == (required_ace_flags & INHERITED_ACE));
+
+  CDacl dacl;
+  if (!AtlGetDacl(object_name, object_type, &dacl)) {
+    return HRESULTFromLastError();
+  }
+
+  int ace_count = dacl.GetAceCount();
+  for (int i = 0; i < ace_count; ++i) {
+    CSid sid_entry;
+    ACCESS_MASK existing_permissions = 0;
+    BYTE existing_ace_flags = 0;
+    dacl.GetAclEntry(i,
+                     &sid_entry,
+                     &existing_permissions,
+                     NULL,
+                     &existing_ace_flags);
+    if (sid_entry == sid &&
+        required_permissions == (existing_permissions & required_permissions) &&
+        required_ace_flags == (existing_ace_flags & ~INHERITED_ACE)) {
+      return S_OK;
+    }
+  }
+
+  if (!dacl.AddAllowedAce(sid, required_permissions, required_ace_flags) ||
+      !AtlSetDacl(object_name, object_type, dacl)) {
+    return HRESULTFromLastError();
+  }
+
+  return S_OK;
+}
+
+HRESULT CreateDir(const TCHAR* in_dir,
+                  LPSECURITY_ATTRIBUTES security_attr) {
+  ASSERT1(in_dir);
+  CString path;
+  if (!PathCanonicalize(CStrBuf(path, MAX_PATH), in_dir)) {
+    return E_FAIL;
+  }
+  // Standardize path on backslash so Find works.
+  path.Replace(_T('/'), _T('\\'));
+  int next_slash = path.Find(_T('\\'));
+  while (true) {
+    int len = 0;
+    if (next_slash == -1) {
+      len = path.GetLength();
+    } else {
+      len = next_slash;
+    }
+    CString dir(path.Left(len));
+    // The check for File::Exists should not be needed. However in certain
+    // cases, i.e. when the program is run from a n/w drive or from the
+    // root drive location, the first CreateDirectory fails with an
+    // E_ACCESSDENIED instead of a ALREADY_EXISTS. Hence we protect the call
+    // with the exists.
+    if (!File::Exists(dir)) {
+      if (!::CreateDirectory(dir, security_attr)) {
+        DWORD error = ::GetLastError();
+        if (ERROR_FILE_EXISTS != error && ERROR_ALREADY_EXISTS != error) {
+          return HRESULT_FROM_WIN32(error);
+        }
+      }
+    }
+    if (next_slash == -1) {
+      break;
+    }
+    next_slash = path.Find(_T('\\'), next_slash + 1);
+  }
+
+  return S_OK;
+}
+
+HRESULT GetFolderPath(int csidl, CString* path) {
+  if (!path) {
+    return E_INVALIDARG;
+  }
+
+  TCHAR buffer[MAX_PATH] = {0};
+  HRESULT hr = ::SHGetFolderPath(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, buffer);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  *path = buffer;
+  return S_OK;
+}
+
+// Delete directory files. If failed, try to schedule deletion at next reboot
+HRESULT DeleteDirectoryFiles(const TCHAR* dir_name) {
+  ASSERT1(dir_name);
+  return DeleteWildcardFiles(dir_name, _T("*"));
+}
+
+// Delete a set of wildcards within dir_name.
+// If unable to delete immediately, try to schedule deletion at next reboot
+HRESULT DeleteWildcardFiles(const TCHAR* dir_name, const TCHAR* wildcard_name) {
+  ASSERT1(dir_name);
+  ASSERT1(wildcard_name);
+
+  HRESULT hr = S_OK;
+
+  WIN32_FIND_DATA find_data;
+  SetZero(find_data);
+
+  CString find_file(dir_name);
+  find_file += _T('\\');
+  find_file += wildcard_name;
+
+  scoped_hfind hfind(::FindFirstFile(find_file, &find_data));
+  if (!hfind) {
+    if (::GetLastError() == ERROR_NO_MORE_FILES) {
+      return S_OK;
+    } else {
+      hr = HRESULTFromLastError();
+      UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]")
+                             _T("[failed to get first file][0x%08x]"), hr));
+      return hr;
+    }
+  }
+
+  do {
+    if (!(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
+      CString specific_file_name(dir_name);
+      specific_file_name += _T('\\');
+      specific_file_name += find_data.cFileName;
+      if (!::DeleteFile(specific_file_name)) {
+        if (!SUCCEEDED(hr = File::DeleteAfterReboot(specific_file_name))) {
+          UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]")
+                                 _T("[failed to delete after reboot]")
+                                 _T("[%s][0x%08x]"), specific_file_name, hr));
+        }
+      }
+    }
+  } while (::FindNextFile(get(hfind), &find_data));
+
+  if (::GetLastError() != ERROR_NO_MORE_FILES) {
+    hr = HRESULTFromLastError();
+    UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]")
+                           _T("[failed to get next file][0x%08x]"), hr));
+  }
+
+  return hr;
+}
+
+// Delete directory and files within. If failed, try to schedule deletion at
+// next reboot
+// TODO(Omaha) - the code to delete the directory is complicated,
+// especially the way the result code is built from hr and hr1. I wonder if we
+// could simplify this by reimplementing it on top of SHFileOperation and
+// also save a few tens of bytes in the process.
+HRESULT DeleteDirectory(const TCHAR* dir_name) {
+  ASSERT1(dir_name);
+
+  if (!SafeDirectoryNameForDeletion(dir_name)) {
+    return E_FAIL;
+  }
+
+  // Make sure the directory exists (it is ok if it doesn't)
+  DWORD dir_attributes = ::GetFileAttributes(dir_name);
+  if (dir_attributes == INVALID_FILE_ATTRIBUTES) {
+    if (::GetLastError() == ERROR_FILE_NOT_FOUND)
+      return S_OK;  // Ok if directory is missing
+    else
+      return HRESULTFromLastError();
+  }
+  // Confirm it is a directory
+  if (!(dir_attributes & FILE_ATTRIBUTE_DIRECTORY)) {
+    return E_FAIL;
+  }
+
+  // Try to delete all files at best effort
+  // Return the first HRESULT error encountered
+
+  // First delete all the normal files
+  HRESULT hr = DeleteDirectoryFiles(dir_name);
+
+  // Recursively delete any subdirectories
+
+  WIN32_FIND_DATA find_data = {0};
+
+  CString find_file(dir_name);
+  find_file += _T("\\*");
+
+  // Note that the follows are enclosed in a block because we need to close the
+  // find handle before deleting the directorty itself
+  {
+    scoped_hfind hfind(::FindFirstFile(find_file, &find_data));
+    if (!hfind) {
+      if (::GetLastError() == ERROR_NO_MORE_FILES) {
+        return hr;
+      } else {
+        HRESULT hr1 = HRESULTFromLastError();
+        UTIL_LOG(LEVEL_ERROR, (_T("[DeleteDirectory]")
+                               _T("[failed to get first file][0x%08x]"), hr1));
+        return SUCCEEDED(hr) ? hr1 : hr;
+      }
+    }
+
+    do {
+      if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+        if (String_StrNCmp(find_data.cFileName, _T("."), 2, false) == 0 ||
+            String_StrNCmp(find_data.cFileName, _T(".."), 3, false) == 0) {
+          continue;
+        }
+
+        CString sub_dir(dir_name);
+        sub_dir += _T("\\");
+        sub_dir += find_data.cFileName;
+        HRESULT hr1 = DeleteDirectory(sub_dir);
+        if (SUCCEEDED(hr) && FAILED(hr1)) {
+          hr = hr1;
+        }
+      }
+    }
+    while (::FindNextFile(get(hfind), &find_data));
+  }
+
+  // Delete the empty directory itself
+  if (!::RemoveDirectory(dir_name)) {
+    HRESULT hr1 = E_FAIL;
+    if (FAILED(hr1 = File::DeleteAfterReboot(dir_name))) {
+      UTIL_LOG(LE, (_T("[DeleteDirectory][failed to delete after reboot]")
+                    _T("[%s][0x%08x]"), dir_name, hr1));
+    }
+
+    if (SUCCEEDED(hr) && FAILED(hr1)) {
+      hr = hr1;
+    }
+  }
+
+  return hr;
+}
+
+// Returns true if this directory name is 'safe' for deletion (doesn't contain
+// "..", doesn't specify a drive root)
+bool SafeDirectoryNameForDeletion(const TCHAR* dir_name) {
+  ASSERT1(dir_name);
+
+  // empty name isn't allowed
+  if (!(dir_name && *dir_name)) {
+    return false;
+  }
+
+  // require a character other than \/:. after the last :
+  // disallow anything with ".."
+  bool ok = false;
+  for (const TCHAR* s = dir_name; *s; ++s) {
+    if (*s != _T('\\') && *s != _T('/') && *s != _T(':') && *s != _T('.')) {
+      ok = true;
+    }
+    if (*s == _T('.') && s > dir_name && *(s-1) == _T('.')) {
+      return false;
+    }
+    if (*s == _T(':')) {
+      ok = false;
+    }
+  }
+  return ok;
+}
+
+// Utility function that deletes either a file or directory,
+// before or after reboot
+HRESULT DeleteBeforeOrAfterReboot(const TCHAR* targetname) {
+  if (!File::Exists(targetname)) {
+    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+  }
+
+  HRESULT hr = E_FAIL;
+  if (File::IsDirectory(targetname)) {
+    // DeleteDirectory will schedule deletion at next reboot if it cannot delete
+    // immediately.
+    hr = DeleteDirectory(targetname);
+  } else  {
+    hr = File::Remove(targetname);
+    // If failed, schedule deletion at next reboot
+    if (FAILED(hr)) {
+      UTIL_LOG(L1, (_T("[DeleteBeforeOrAfterReboot]")
+                    _T("[trying to delete after reboot][%s]"), targetname));
+      hr = File::DeleteAfterReboot(targetname);
+    }
+  }
+
+  if (FAILED(hr)) {
+    UTIL_LOG(L1, (_T("[DeleteBeforeOrAfterReboot]")
+                  _T("[failed to delete][%s][0x%08x]"), targetname, hr));
+  }
+
+  return hr;
+}
+
+
+// Internal implementation of the safe version of getting size of all files in
+// a directory. It is able to abort the counting if one of the maximum criteria
+// is reached.
+HRESULT InternalSafeGetDirectorySize(const TCHAR* dir_name,
+                                     uint64* size,
+                                     HANDLE shutdown_event,
+                                     uint64 max_size,
+                                     int curr_file_count,
+                                     int max_file_count,
+                                     int curr_depth,
+                                     int max_depth,
+                                     DWORD end_time_ms) {
+  ASSERT1(dir_name && *dir_name);
+  ASSERT1(size);
+
+  CString dir_find_name = String_MakeEndWith(dir_name, _T("\\"), false);
+  dir_find_name += _T("*");
+  WIN32_FIND_DATA find_data = {0};
+  scoped_hfind hfind(::FindFirstFile(dir_find_name, &find_data));
+  if (!hfind) {
+    return ::GetLastError() == ERROR_NO_MORE_FILES ? S_OK :
+                                                     HRESULTFromLastError();
+  }
+
+  do {
+    // Bail out if shutting down
+    if (shutdown_event && IsHandleSignaled(shutdown_event)) {
+      return E_ABORT;
+    }
+
+    // Bail out if reaching maximum running time
+    if (end_time_ms && ::GetTickCount() >= end_time_ms) {
+      UTIL_LOG(L6, (_T("[InternalSafeGetDirectorySize]")
+                    _T("[reaching max running time][%s][%u]"),
+                    dir_name, end_time_ms));
+      return E_ABORT;
+    }
+
+    // Skip reparse point since it might be a hard link which could cause an
+    // infinite recursive directory loop.
+    if (find_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+      continue;
+    }
+
+    if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+      // Skip . and ..
+      if (String_StrNCmp(find_data.cFileName, _T("."), 2, false) == 0 ||
+          String_StrNCmp(find_data.cFileName, _T(".."), 3, false) == 0) {
+        continue;
+      }
+
+      // Bail out if reaching maximum depth
+      if (max_depth && curr_depth + 1 >= max_depth) {
+        UTIL_LOG(L6, (_T("[InternalSafeGetDirectorySize]")
+                      _T("[reaching max depth][%s][%u]"), dir_name, max_depth));
+        return E_ABORT;
+      }
+
+      // Walk over sub-directory
+      CString sub_dir_name = String_MakeEndWith(dir_name, _T("\\"), false);
+      sub_dir_name += find_data.cFileName;
+      RET_IF_FAILED(InternalSafeGetDirectorySize(sub_dir_name,
+                                                 size,
+                                                 shutdown_event,
+                                                 max_size,
+                                                 curr_file_count,
+                                                 max_file_count,
+                                                 curr_depth + 1,
+                                                 max_depth,
+                                                 end_time_ms));
+    } else {
+      // Bail out if reaching maximum number of files
+      ++curr_file_count;
+      if (max_file_count && curr_file_count >= max_file_count) {
+        UTIL_LOG(L6, (_T("[InternalSafeGetDirectorySize]")
+                      _T("[reaching max file count][%s][%u]"),
+                      dir_name, max_file_count));
+        return E_ABORT;
+      }
+
+      // Count the file size
+      uint64 file_size =
+          ((static_cast<uint64>((find_data.nFileSizeHigh)) << 32)) +
+          static_cast<uint64>(find_data.nFileSizeLow);
+      *size += file_size;
+
+      // Bail out if reaching maximum size
+      if (max_size && *size >= max_size) {
+        UTIL_LOG(L6, (_T("[InternalSafeGetDirectorySize]")
+                      _T("[reaching max size][%s][%u]"), dir_name, max_size));
+        return E_ABORT;
+      }
+    }
+  } while (::FindNextFile(get(hfind), &find_data));
+
+  return ::GetLastError() == ERROR_NO_MORE_FILES ? S_OK :
+                                                   HRESULTFromLastError();
+}
+
+// The safe version of getting size of all files in a directory
+// It is able to abort the counting if one of the maximum criteria is reached
+HRESULT SafeGetDirectorySize(const TCHAR* dir_name,
+                             uint64* size,
+                             HANDLE shutdown_event,
+                             uint64 max_size,
+                             int max_file_count,
+                             int max_depth,
+                             int max_running_time_ms) {
+  ASSERT1(dir_name && *dir_name);
+  ASSERT1(size);
+
+  *size = 0;
+
+  DWORD end_time = 0;
+  if (max_running_time_ms > 0) {
+    end_time = ::GetTickCount() + max_running_time_ms;
+  }
+  return InternalSafeGetDirectorySize(dir_name,
+                                      size,
+                                      shutdown_event,
+                                      max_size,
+                                      0,
+                                      max_file_count,
+                                      0,
+                                      max_depth,
+                                      end_time);
+}
+
+// Get size of all files in a directory
+HRESULT GetDirectorySize(const TCHAR* dir_name, uint64* size) {
+  ASSERT1(dir_name && *dir_name);
+  ASSERT1(size);
+  return SafeGetDirectorySize(dir_name, size, NULL, 0, 0, 0, 0);
+}
+
+// Handles the logic to determine the handle that was signaled
+// as a result of calling *WaitForMultipleObjects.
+HRESULT GetSignaledObjectPosition(uint32 cnt, DWORD res, uint32* pos) {
+  ASSERT1(pos);
+
+  if (res == WAIT_FAILED) {
+    return S_FALSE;
+  }
+
+#pragma warning(disable : 4296)
+  // C4296: '>=' : expression is always true
+  if ((res >= WAIT_OBJECT_0) && (res < WAIT_OBJECT_0 + cnt)) {
+    *pos = res - WAIT_OBJECT_0;
+    return S_OK;
+  }
+#pragma warning(default : 4296)
+
+  if ((res >= WAIT_ABANDONED_0) && (res < WAIT_ABANDONED_0 + cnt)) {
+    *pos = res - WAIT_ABANDONED_0;
+    return S_OK;
+  }
+  return E_INVALIDARG;
+}
+
+// Supports all of the other WaitWithMessage* functions.
+//
+// Returns:
+//    S_OK when the message loop should continue.
+//    S_FALSE when it receives something that indicates the
+//      loop should quit.
+//    E_* only when GetMessage failed
+//
+// This function is not exposed outside of this file.  Only
+// friendly wrappers of it are.
+HRESULT WaitWithMessageLoopAnyInternal(
+    const HANDLE* phandles,
+    uint32 cnt,
+    uint32* pos,
+    MessageHandlerInternalInterface* message_handler) {
+  ASSERT1(pos && message_handler);
+  // cnt and phandles are either both zero or both not zero.
+  ASSERT1(!cnt == !phandles);
+
+  // Loop until an error happens or the wait is satisfied by a signaled
+  // object or an abandoned mutex.
+  for (;;) {
+    MSG msg = {0};
+
+    // Process the messages in the input queue.
+    while (::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) {
+      BOOL ret = false;
+      if ((ret = ::GetMessage(&msg, NULL, 0, 0)) != 0) {
+        if (ret == -1) {
+          HRESULT hr = HRESULTFromLastError();
+          UTIL_LOG(LE, (_T("[WaitWithMessageLoopAnyInternal]")
+                        _T("[GetMessage failed][0x%08x]"), hr));
+          return hr;
+        }
+        message_handler->Process(&msg, &phandles, &cnt);
+      } else {
+        // We need to re-post the quit message we retrieved so that it could
+        // propagate to the outer layer. Otherwise, the program will seem to
+        // "get stuck" in its shutdown code.
+        ::PostQuitMessage(msg.wParam);
+        return S_FALSE;
+      }
+
+      // WaitForMultipleObjects fails if cnt == 0.
+      if (cnt) {
+        // Briefly check the state of the handle array to see if something
+        // has signaled as we processed a message.
+        ASSERT1(phandles);
+        DWORD res = ::WaitForMultipleObjects(cnt, phandles, false, 0);
+        ASSERT1(res != WAIT_FAILED);
+        HRESULT hr = GetSignaledObjectPosition(cnt, res, pos);
+        if (SUCCEEDED(hr)) {
+          return hr;
+        }
+      }
+    }
+
+    // The wait with message. It is satisfied by either the objects getting
+    // signaled or when messages enter the message queue.
+    // TODO(omaha): implementing timeout is a little bit tricky since we
+    // want the timeout on the handles only and the native API does not
+    // have this semantic.
+    //
+    // TODO(omaha): use a waitable timer to implement the timeout.
+    //
+    // When cnt is zero then the execution flow waits here until messages
+    // arrive in the input queue. Unlike WaitForMultipleObjects,
+    // MsgWaitForMultipleObjects does not error out when cnt == 0.
+    const DWORD timeout = INFINITE;
+    DWORD res(::MsgWaitForMultipleObjects(cnt, phandles, false, timeout,
+                                          QS_ALLINPUT));
+    ASSERT((res != WAIT_FAILED),
+           (_T("[MsgWaitForMultipleObjects returned WAIT_FAILED][%u]"),
+            ::GetLastError()));
+
+    ASSERT1(res != WAIT_TIMEOUT);
+
+    HRESULT hr = GetSignaledObjectPosition(cnt, res, pos);
+    if (SUCCEEDED(hr)) {
+      return hr;
+    }
+  }
+}
+
+// The simplest implementation of a message processor
+void BasicMessageHandler::Process(MSG* msg) {
+  ASSERT1(msg);
+  ::TranslateMessage(msg);
+  ::DispatchMessage(msg);
+}
+
+class BasicMessageHandlerInternal : public BasicMessageHandler,
+                                    public MessageHandlerInternalInterface {
+ public:
+  BasicMessageHandlerInternal() {}
+  virtual void Process(MSG* msg, const HANDLE**, uint32*) {
+    BasicMessageHandler::Process(msg);
+  }
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(BasicMessageHandlerInternal);
+};
+
+
+bool WaitWithMessageLoopAny(const std::vector<HANDLE>& handles, uint32* pos) {
+  BasicMessageHandlerInternal msg_handler;
+  return WaitWithMessageLoopAnyInternal(&handles.front(), handles.size(), pos,
+                                        &msg_handler) != S_FALSE;
+}
+
+bool WaitWithMessageLoopAll(const std::vector<HANDLE>& handles) {
+  // make a copy of the vector, as objects must be removed from the
+  // wait array as they get signaled.
+  std::vector<HANDLE> h(handles);
+
+  // The function is mainly implemented in terms of WaitWithMessageLoopAny
+
+  // loop until all objects are signaled.
+  while (!h.empty()) {
+    uint32 pos(static_cast<uint32>(-1));
+    if (!WaitWithMessageLoopAny(h, &pos)) return false;
+    ASSERT1(pos < h.size());
+    h.erase(h.begin() + pos);   // remove the signaled object and loop
+  }
+
+  return true;
+}
+
+bool WaitWithMessageLoop(HANDLE h) {
+  BasicMessageHandlerInternal msg_handler;
+  uint32 pos(static_cast<uint32>(-1));
+  bool res =
+    WaitWithMessageLoopAnyInternal(&h, 1, &pos, &msg_handler) != S_FALSE;
+  if (res) {
+    // It's the first and the only handle that it is signaled.
+    ASSERT1(pos == 0);
+  }
+  return res;
+}
+
+// Wait with message loop for a certain period of time
+bool WaitWithMessageLoopTimed(DWORD ms) {
+  scoped_timer timer(::CreateWaitableTimer(NULL,
+                                           true,   // manual reset
+                                           NULL));
+  ASSERT1(get(timer));
+  LARGE_INTEGER timeout = MSto100NSRelative(ms);
+  BOOL timer_ok = ::SetWaitableTimer(get(timer),
+                                     &timeout,
+                                     0,
+                                     NULL,
+                                     NULL,
+                                     false);
+  ASSERT1(timer_ok);
+  return WaitWithMessageLoop(get(timer));
+}
+
+MessageLoopWithWait::MessageLoopWithWait() : message_handler_(NULL) {
+}
+
+void MessageLoopWithWait::set_message_handler(
+    MessageHandlerInterface* message_handler) {
+  message_handler_ = message_handler;
+}
+
+// The message loop and handle callback routine.
+HRESULT MessageLoopWithWait::Process() {
+  while (true) {
+    ASSERT1(callback_handles_.size() == callbacks_.size());
+
+    // The implementation allows for an empty array of handles. Taking the
+    // address of elements in an empty container is not allowed so we must
+    // deal with this case here.
+    size_t pos(0);
+    HRESULT hr = WaitWithMessageLoopAnyInternal(
+        callback_handles_.empty() ? NULL : &callback_handles_.front(),
+        callback_handles_.size(),
+        &pos,
+        this);
+
+    // In addition to E_*, S_FALSE should cause a return to happen here.
+    if (hr != S_OK) {
+      return hr;
+    }
+
+    ASSERT1(pos < callback_handles_.size());
+    ASSERT1(callback_handles_.size() == callbacks_.size());
+
+    HANDLE signaled_handle = callback_handles_[pos];
+    WaitCallbackInterface* callback_interface = callbacks_[pos];
+    RemoveHandleAt(pos);
+
+    if (!callback_interface->HandleSignaled(signaled_handle)) {
+      return S_OK;
+    }
+  }
+}
+
+// Handles one messgae and adjust the handles and cnt as appropriate after
+// handling the message.
+void MessageLoopWithWait::Process(MSG* msg, const HANDLE** handles,
+                                  uint32* cnt) {
+  ASSERT1(msg && handles && cnt);
+
+  if (message_handler_) {
+    message_handler_->Process(msg);
+  }
+
+  // Set the handles and count again because they may have changed
+  // while processing the message.
+  *handles = callback_handles_.empty() ? NULL : &callback_handles_.front();
+  *cnt = callback_handles_.size();
+}
+// Starts waiting on the given handle
+bool MessageLoopWithWait::RegisterWaitForSingleObject(
+    HANDLE handle, WaitCallbackInterface* callback) {
+  ASSERT1(callback_handles_.size() == callbacks_.size());
+  ASSERT1(callback != NULL);
+
+  if (callback_handles_.size() >= MAXIMUM_WAIT_OBJECTS - 1) {
+    return false;
+  }
+
+  // In case the user is registering a handle, that they previous added
+  // remove the previous one before adding it back into the array.
+  UnregisterWait(handle);
+  callback_handles_.push_back(handle);
+  callbacks_.push_back(callback);
+
+  ASSERT1(callback_handles_.size() == callbacks_.size());
+  return true;
+}
+
+// Finds the given handle and stops waiting on it
+bool MessageLoopWithWait::UnregisterWait(HANDLE handle) {
+  ASSERT1(callback_handles_.size() == callbacks_.size());
+
+  for (uint32 index = 0; index < callback_handles_.size() ; index++) {
+    if (callback_handles_[index] == handle) {
+      RemoveHandleAt(index);
+      return true;
+    }
+  }
+  return false;
+}
+
+// Removes the wait handle at the given position
+void MessageLoopWithWait::RemoveHandleAt(uint32 pos) {
+  ASSERT1(callback_handles_.size() == callbacks_.size());
+  ASSERT1(pos < callback_handles_.size());
+
+  callback_handles_.erase(callback_handles_.begin() + pos);
+  callbacks_.erase(callbacks_.begin() + pos);
+
+  ASSERT1(callback_handles_.size() == callbacks_.size());
+}
+
+HRESULT CallEntryPoint0(const TCHAR* dll_path,
+                        const char* function_name,
+                        HRESULT* result) {
+  ASSERT1(dll_path);
+  ASSERT1(::lstrlen(dll_path) > 0);
+  ASSERT1(function_name);
+  ASSERT1(::strlen(function_name) > 0);
+  ASSERT1(result);
+
+  scoped_library dll(::LoadLibrary(dll_path));
+  if (!dll) {
+    return HRESULTFromLastError();
+  }
+
+  HRESULT (*proc)() = reinterpret_cast<HRESULT (*)()>(
+      ::GetProcAddress(get(dll), function_name));
+  if (!proc) {
+    return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
+  }
+
+  *result = (proc)();
+  return S_OK;
+}
+
+// Register a DLL
+HRESULT RegisterDll(const TCHAR* dll_path) {
+  HRESULT hr = S_OK;
+  HRESULT hr_call = CallEntryPoint0(dll_path, "DllRegisterServer", &hr);
+  if (SUCCEEDED(hr_call)) {
+    return hr;
+  }
+  return hr_call;
+}
+
+// Unregister a DLL
+HRESULT UnregisterDll(const TCHAR* dll_path) {
+  HRESULT hr = S_OK;
+  HRESULT hr_call = CallEntryPoint0(dll_path, "DllUnregisterServer", &hr);
+  if (SUCCEEDED(hr_call)) {
+    return hr;
+  }
+  return hr_call;
+}
+
+// Register/unregister an EXE
+HRESULT RegisterOrUnregisterExe(const TCHAR* exe_path, const TCHAR* cmd_line) {
+  ASSERT1(exe_path);
+  ASSERT1(cmd_line);
+
+  // cmd_line parameter really contains the arguments to be passed
+  // on the process creation command line.
+  PROCESS_INFORMATION pi = {0};
+  HRESULT hr = System::StartProcessWithArgsAndInfo(exe_path, cmd_line, &pi);
+  if (FAILED(hr)) {
+    UTIL_LOG(LEVEL_WARNING, (_T("[RegisterOrUnregisterExe]")
+                             _T("[failed to start process]")
+                             _T("[%s][%s][0x%08x]"), exe_path, cmd_line, hr));
+    return hr;
+  }
+  // Take ownership of the handles for clean up.
+  scoped_thread thread(pi.hThread);
+  scoped_process process(pi.hProcess);
+
+  // ATL COM servers return an HRESULT on exit. There is a case in which they
+  // return -1 which seems like a bug in ATL. It appears there is no
+  // documented convention on what a local server would return for errors.
+  // There is a possibility that a server would return Windows errors.
+
+  // Wait on the process to exit and return the exit code of the process.
+  DWORD result(::WaitForSingleObject(get(process), INFINITE));
+  DWORD exit_code(0);
+  if (result == WAIT_OBJECT_0 &&
+      ::GetExitCodeProcess(get(process), &exit_code)) {
+    return static_cast<HRESULT>(exit_code);
+  } else {
+    return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
+  }
+}
+
+// Register a COM Local Server
+HRESULT RegisterServer(const TCHAR* exe_path) {
+  return RegisterOrUnregisterExe(exe_path, _T("/RegServer"));
+}
+
+// Unregister a COM Local Server
+HRESULT UnregisterServer(const TCHAR* exe_path) {
+  return RegisterOrUnregisterExe(exe_path, _T("/UnregServer"));
+}
+
+// Register a Service
+HRESULT RegisterService(const TCHAR* exe_path) {
+  return RegisterOrUnregisterExe(exe_path, _T("/Service"));
+}
+
+// Unregister a Service
+HRESULT UnregisterService(const TCHAR* exe_path) {
+  // Unregistering a service is via UnregServer
+  return RegisterOrUnregisterExe(exe_path, _T("/UnregServer"));
+}
+
+// Adapted from gds installer/install/work_list.cpp: InstallServiceExecutable
+HRESULT RunService(const TCHAR* service_name) {
+  scoped_service manager(::OpenSCManager(NULL,  // local machine
+                                         NULL,  // ServicesActive database
+                                         STANDARD_RIGHTS_READ));
+  ASSERT1(get(manager));
+  if (!get(manager)) {
+    return HRESULTFromLastError();
+  }
+
+  scoped_service service(::OpenService(get(manager), service_name,
+                                       SERVICE_START));
+  ASSERT1(get(service));
+  if (!get(service)) {
+    return HRESULTFromLastError();
+  }
+
+  UTIL_LOG(L2, (_T("start service")));
+  if (!::StartService(get(service), 0, NULL)) {
+    return HRESULTFromLastError();
+  }
+  return S_OK;
+}
+
+
+HRESULT ReadEntireFile(const TCHAR* filepath,
+                       uint32 max_len,
+                       std::vector<byte>* buffer_out) {
+  return ReadEntireFileShareMode(filepath, max_len, 0, buffer_out);
+}
+
+HRESULT ReadEntireFileShareMode(const TCHAR* filepath,
+                                uint32 max_len,
+                                DWORD share_mode,
+                                std::vector<byte>* buffer_out) {
+  ASSERT1(filepath);
+  ASSERT1(buffer_out);
+
+  File file;
+  HRESULT hr = file.OpenShareMode(filepath, false, false, share_mode);
+  if (FAILED(hr)) {
+    // File missing.
+    return hr;
+  }
+
+  ON_SCOPE_EXIT_OBJ(file, &File::Close);
+
+  uint32 file_len = 0;
+  hr = file.GetLength(&file_len);
+  if (FAILED(hr)) {
+    // Should never happen
+    return hr;
+  }
+
+  if (max_len != 0 && file_len > max_len) {
+    // Too large to consider
+    return MEM_E_INVALID_SIZE;
+  }
+
+  if (file_len == 0) {
+    buffer_out->clear();
+    return S_OK;
+  }
+
+  int old_size = buffer_out->size();
+  buffer_out->resize(old_size + file_len);
+
+  uint32 bytes_read = 0;
+  hr = file.ReadFromStartOfFile(file_len,
+                                &(*buffer_out)[old_size],
+                                &bytes_read);
+  if (FAILED(hr)) {
+    // I/O error of some kind
+    return hr;
+  }
+
+  if (bytes_read != file_len) {
+    // Unexpected length. This could happen when reading a file someone else
+    // is writing to such as log files.
+    ASSERT1(false);
+    return E_UNEXPECTED;
+  }
+
+  // All's well that ends well
+  return S_OK;
+}
+
+HRESULT WriteEntireFile(const TCHAR * filepath,
+                        const std::vector<byte>& buffer_in) {
+  ASSERT1(filepath);
+
+  // File::WriteAt doesn't implement clear-on-open-for-write semantics,
+  // so just delete the file if it exists instead of writing into it.
+
+  if (File::Exists(filepath)) {
+    HRESULT hr = File::Remove(filepath);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  File file;
+  HRESULT hr = file.Open(filepath, true, false);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  ON_SCOPE_EXIT_OBJ(file, &File::Close);
+
+  uint32 bytes_written = 0;
+  hr = file.WriteAt(0, &buffer_in.front(), buffer_in.size(), 0, &bytes_written);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  if (bytes_written != buffer_in.size()) {
+    // This shouldn't happen, caller needs to investigate what's up.
+    ASSERT1(false);
+    return E_UNEXPECTED;
+  }
+
+  return S_OK;
+}
+
+// Conversions between a byte stream and a std::string
+HRESULT BufferToString(const std::vector<byte>& buffer_in, CStringA* str_out) {
+  ASSERT1(str_out);
+  str_out->Append(reinterpret_cast<const char*>(&buffer_in.front()),
+                  buffer_in.size());
+  return S_OK;
+}
+
+HRESULT StringToBuffer(const CStringA& str_in, std::vector<byte>* buffer_out) {
+  ASSERT1(buffer_out);
+  buffer_out->assign(str_in.GetString(),
+                     str_in.GetString() + str_in.GetLength());
+  return S_OK;
+}
+
+HRESULT BufferToString(const std::vector<byte>& buffer_in, CString* str_out) {
+  ASSERT1(str_out);
+
+  size_t len2 = buffer_in.size();
+  ASSERT1(len2 % 2 == 0);
+  size_t len = len2 / 2;
+
+  str_out->Append(reinterpret_cast<const TCHAR*>(&buffer_in.front()), len);
+
+  return S_OK;
+}
+
+HRESULT StringToBuffer(const CString& str_in, std::vector<byte>* buffer_out) {
+  ASSERT1(buffer_out);
+
+  size_t len = str_in.GetLength();
+  size_t len2 = len * 2;
+
+  buffer_out->resize(len2);
+  ::memcpy(&buffer_out->front(), str_in.GetString(), len2);
+
+  return S_OK;
+}
+
+HRESULT RegSplitKeyvalueName(const CString& keyvalue_name,
+                             CString* key_name,
+                             CString* value_name) {
+  ASSERT1(key_name);
+  ASSERT1(value_name);
+
+  const TCHAR kDefault[] = _T("\\(default)");
+
+  if (String_EndsWith(keyvalue_name, _T("\\"), false)) {
+    key_name->SetString(keyvalue_name, keyvalue_name.GetLength() - 1);
+    value_name->Empty();
+  } else if (String_EndsWith(keyvalue_name, kDefault, true)) {
+    key_name->SetString(keyvalue_name,
+                        keyvalue_name.GetLength() - TSTR_SIZE(kDefault));
+    value_name->Empty();
+  } else {
+    int last_slash = String_ReverseFindChar(keyvalue_name, _T('\\'));
+    if (last_slash == -1) {
+      // No slash found - bizzare and wrong
+      return E_FAIL;
+    }
+    key_name->SetString(keyvalue_name, last_slash);
+    value_name->SetString(keyvalue_name.GetString() + last_slash + 1,
+                          keyvalue_name.GetLength() - last_slash - 1);
+  }
+
+  return S_OK;
+}
+
+HRESULT ExpandEnvLikeStrings(const TCHAR* src,
+                             const std::map<CString, CString>& keywords,
+                             CString* dest) {
+  ASSERT1(src);
+  ASSERT1(dest);
+
+  const TCHAR kMarker = _T('%');
+
+  dest->Empty();
+
+  // Loop while finding the marker in the string
+  HRESULT hr = S_OK;
+  int pos = 0;
+  int marker_pos1 = -1;
+  while ((marker_pos1 = String_FindChar(src, kMarker, pos)) != -1) {
+    // Try to find the right marker
+    int marker_pos2 = -1;
+    const TCHAR* s = src + marker_pos1 + 1;
+    for (; *s; ++s) {
+      if (*s == kMarker) {
+        marker_pos2 = s - src;
+        break;
+      }
+      if (!String_IsIdentifierChar(*s)) {
+        break;
+      }
+    }
+    if (marker_pos2 == -1) {
+      // Unmatched marker found, skip
+      dest->Append(src + pos, marker_pos1 - pos + 1);
+      pos = marker_pos1 + 1;
+      continue;
+    }
+
+    // Get the name - without the % markers on each end
+    CString name(src + marker_pos1 + 1, marker_pos2 - marker_pos1 - 1);
+
+    bool found = false;
+    for (std::map<CString, CString>::const_iterator it(keywords.begin());
+         it != keywords.end();
+         ++it) {
+      if (_tcsicmp(it->first, name) == 0) {
+        dest->Append(src + pos, marker_pos1 - pos);
+        dest->Append(it->second);
+        found = true;
+        break;
+      }
+    }
+    if (!found) {
+      // No mapping found
+      UTIL_LOG(LE, (_T("[ExpandEnvLikeStrings]")
+                    _T("[no mapping found for '%s' in '%s']"), name, src));
+      dest->Append(src + pos, marker_pos2 - pos + 1);
+      hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
+    }
+
+    pos = marker_pos2 + 1;
+  }
+
+  int len = _tcslen(src);
+  if (pos < len) {
+    dest->Append(src + pos, len - pos);
+  }
+
+  return hr;
+}
+
+bool IsRegistryPath(const TCHAR* path) {
+  return String_StartsWith(path, _T("HKLM\\"), false) ||
+         String_StartsWith(path, _T("HKCU\\"), false) ||
+         String_StartsWith(path, _T("HKCR\\"), false) ||
+         String_StartsWith(path, _T("HKEY_LOCAL_MACHINE\\"), false) ||
+         String_StartsWith(path, _T("HKEY_CURRENT_USER\\"), false) ||
+         String_StartsWith(path, _T("HKEY_CLASSES_ROOT\\"), false);
+}
+
+bool IsUrl(const TCHAR* path) {
+  // Currently we only check for "http://" and "https://"
+  return String_StartsWith(path, kHttpProto, true) ||
+         String_StartsWith(path, kHttpsProto, true);
+}
+
+
+CString GuidToString(const GUID& guid) {
+  TCHAR guid_str[40] = {0};
+  VERIFY1(::StringFromGUID2(guid, guid_str, arraysize(guid_str)));
+  String_ToUpper(guid_str);
+  return guid_str;
+}
+
+HRESULT StringToGuidSafe(const CString& str, GUID* guid) {
+  ASSERT1(guid);
+  TCHAR* s = const_cast<TCHAR*>(str.GetString());
+  return ::IIDFromString(s, guid);
+}
+
+// Helper function to convert a variant containing a list of strings
+void VariantToStringList(VARIANT var, std::vector<CString>* list) {
+  ASSERT1(list);
+
+  list->clear();
+
+  ASSERT1(V_VT(&var) == VT_DISPATCH);
+  CComPtr<IDispatch> obj = V_DISPATCH(&var);
+  ASSERT1(obj);
+
+  CComVariant var_length;
+  VERIFY1(SUCCEEDED(obj.GetPropertyByName(_T("length"), &var_length)));
+  ASSERT1(V_VT(&var_length) == VT_I4);
+  int length = V_I4(&var_length);
+
+  for (int i = 0; i < length; ++i) {
+    CComVariant value;
+    VERIFY1(SUCCEEDED(obj.GetPropertyByName(itostr(i), &value)));
+    if (V_VT(&value) == VT_BSTR) {
+      list->push_back(V_BSTR(&value));
+    } else {
+      ASSERT1(false);
+    }
+  }
+}
+
+
+HRESULT GetCurrentProcessHandle(HANDLE* handle) {
+  ASSERT1(handle);
+  scoped_process real_handle;
+  HANDLE pseudo_handle = ::GetCurrentProcess();
+  bool res = ::DuplicateHandle(
+    pseudo_handle,         // this process pseudo-handle
+    pseudo_handle,         // handle to duplicate
+    pseudo_handle,         // the process receiving the handle
+    address(real_handle),  // this process real handle
+    0,                     // ignored
+    false,                 // don't inherit this handle
+    DUPLICATE_SAME_ACCESS) != 0;
+
+  *handle = NULL;
+  if (!res) {
+    return HRESULTFromLastError();
+  }
+  *handle = release(real_handle);
+  return S_OK;
+}
+
+HRESULT DuplicateTokenIntoCurrentProcess(HANDLE source_process,
+                                         HANDLE token_to_duplicate,
+                                         CAccessToken* duplicated_token) {
+  ASSERT1(source_process);
+  ASSERT1(token_to_duplicate);
+  ASSERT1(duplicated_token);
+
+  scoped_handle alt_token;
+  bool res = ::DuplicateHandle(
+      source_process,                 // Process whose handle needs duplicating.
+      token_to_duplicate,             // Handle to duplicate.
+      ::GetCurrentProcess(),          // Current process receives the handle.
+      address(alt_token),             // Duplicated handle.
+      TOKEN_ALL_ACCESS,               // Access requested for the new handle.
+      false,                          // Do not inherit the new handle.
+      DUPLICATE_SAME_ACCESS) != 0;    // Same access as token_to_duplicate.
+
+  if (!res) {
+    HRESULT hr = HRESULTFromLastError();
+    CORE_LOG(LE, (_T("[DuplicateTokenIntoCurrentProcess failed][0x%x]"), hr));
+    return hr;
+  }
+
+  duplicated_token->Attach(release(alt_token));
+  return S_OK;
+}
+
+
+// get a time64 value
+// NOTE: If the value is greater than the
+// max value, then SetValue will be called using the max_value.
+HRESULT GetLimitedTimeValue(const TCHAR* full_key_name, const TCHAR* value_name,
+                            time64 max_time, time64* value,
+                            bool* limited_value) {
+  ASSERT1(full_key_name);
+  ASSERT1(value);
+  STATIC_ASSERT(sizeof(time64) == sizeof(DWORD64));
+
+  if (limited_value) {
+    *limited_value = false;
+  }
+  HRESULT hr = RegKey::GetValue(full_key_name, value_name, value);
+  if (SUCCEEDED(hr) && *value > max_time) {
+    *value = max_time;
+
+    // Use a different hr for the setting of the value b/c
+    // the returned hr should reflect the success/failure of reading the key
+    HRESULT set_value_hr = RegKey::SetValue(full_key_name, value_name, *value);
+    ASSERT(SUCCEEDED(set_value_hr),
+           (_T("GetLimitedTimeValue - failed when setting a value: 0x%08x]"),
+            set_value_hr));
+    if (SUCCEEDED(set_value_hr) && limited_value) {
+      *limited_value = true;
+    }
+  }
+  return hr;
+}
+
+// get a time64 value trying reg keys successively if there is a
+// failure in getting a value.
+HRESULT GetLimitedTimeValues(const TCHAR* full_key_names[],
+                             int key_names_length,
+                             const TCHAR* value_name,
+                             time64 max_time,
+                             time64* value,
+                             bool* limited_value) {
+  ASSERT1(full_key_names);
+  ASSERT1(value);
+  ASSERT1(key_names_length > 0);
+
+  HRESULT hr = E_FAIL;
+  for (int i = 0; i < key_names_length; ++i) {
+    hr = GetLimitedTimeValue(full_key_names[i], value_name, max_time, value,
+                             limited_value);
+    if (SUCCEEDED(hr)) {
+      return hr;
+    }
+  }
+  return hr;
+}
+
+// Wininet.dll (and especially the version that comes with IE7, with 01/12/07
+// timestamp) incorrectly initializes Rasman.dll. As a result, there is a race
+// condition that causes double-free on a memory from process heap.
+// This causes memory corruption in the heap that may later produce a variety
+// of ill effects, most frequently a crash with a callstack that contains
+// wininet and rasman, or ntdll!RtlAllocHeap. The root cause is that
+// Rasapi32!LoadRasmanDllAndInit is not thread safe and can start very involved
+// process of initialization on 2 threads at the same time. It's a bug.
+// Solution: in the begining of the program, trigger synchronous load of
+// rasman dll. The easy way is to call a public ras api that does synchronous
+// initialization, which is what we do here.
+void EnsureRasmanLoaded() {
+  RASENTRYNAME ras_entry_name = {0};
+  DWORD size_bytes = sizeof(ras_entry_name);
+  DWORD number_of_entries = 0;
+  ras_entry_name.dwSize = size_bytes;
+  // we don't really need results of this method,
+  // it simply triggers RASAPI32!LoadRasmanDllAndInit() internally.
+  ::RasEnumEntries(NULL,
+                   NULL,
+                   &ras_entry_name,
+                   &size_bytes,
+                   &number_of_entries);
+}
+
+// Appends two reg keys. Handles the situation where there are traling
+// back slashes in one and leading back slashes in two.
+CString AppendRegKeyPath(const CString& one, const CString& two) {
+  CString leftpart(one);
+  int length = leftpart.GetLength();
+  int i = 0;
+  for (i = length - 1; i >= 0; --i) {
+    if (leftpart[i] != _T('\\')) {
+      break;
+    }
+  }
+  leftpart = leftpart.Left(i+1);
+
+  CString rightpart(two);
+  int lengthr = rightpart.GetLength();
+  for (i = 0; i < lengthr; ++i) {
+    if (rightpart[i] != _T('\\')) {
+      break;
+    }
+  }
+  rightpart = rightpart.Right(lengthr - i);
+
+  CString result;
+  SafeCStringFormat(&result, _T("%s\\%s"), leftpart, rightpart);
+  return result;
+}
+
+CString AppendRegKeyPath(const CString& one, const CString& two,
+                         const CString& three) {
+  CString result = AppendRegKeyPath(one, two);
+  result = AppendRegKeyPath(result, three);
+  return result;
+}
+
+
+HRESULT GetUserKeysFromHkeyUsers(std::vector<CString>* key_names) {
+  ASSERT1(key_names);
+  CORE_LOG(L3, (_T("[GetUserKeysFromHkeyUsers]")));
+
+  TCHAR user_key_name[MAX_PATH] = {0};
+  int i = 0;
+  while (::RegEnumKey(HKEY_USERS, i++, user_key_name, MAX_PATH) !=
+                      ERROR_NO_MORE_ITEMS) {
+        byte sid_buffer[SECURITY_MAX_SID_SIZE] = {0};
+    PSID sid = reinterpret_cast<PSID>(sid_buffer);
+    if (::ConvertStringSidToSid(user_key_name, &sid) != 0) {
+      // We could convert the string SID into a real SID. If not
+      // we just ignore.
+      DWORD size = MAX_PATH;
+      DWORD size_domain = MAX_PATH;
+      SID_NAME_USE sid_type = SidTypeComputer;
+      TCHAR user_name[MAX_PATH] = {0};
+      TCHAR domain_name[MAX_PATH] = {0};
+
+      if (::LookupAccountSid(NULL, sid, user_name, &size,
+                             domain_name, &size_domain, &sid_type) == 0) {
+        HRESULT hr = HRESULTFromLastError();
+        CORE_LOG(LW, (_T("[GetUserKeysFromHkeyUsers LookupAccountSid failed]")
+                      _T("[0x%08x]"), hr));
+        continue;
+      }
+
+      if (sid_type == SidTypeUser) {
+        // Change the RunAs keys for the user goopdates to point to the
+        // machine install.
+        CString user_reg_key_name = AppendRegKeyPath(USERS_KEY, user_key_name);
+        key_names->push_back(user_reg_key_name);
+      }
+    }
+  }
+
+  return S_OK;
+}
+
+HRESULT IsSystemProcess(bool* is_system_process) {
+  CAccessToken current_process_token;
+  if (!current_process_token.GetProcessToken(TOKEN_QUERY,
+                                             ::GetCurrentProcess())) {
+    HRESULT hr = HRESULTFromLastError();
+    ASSERT(false, (_T("CAccessToken::GetProcessToken failed: 0x%08x"), hr));
+    return hr;
+  }
+  CSid logon_sid;
+  if (!current_process_token.GetUser(&logon_sid)) {
+    HRESULT hr = HRESULTFromLastError();
+    ASSERT(false, (_T("CAccessToken::GetUser failed: 0x%08x"), hr));
+    return hr;
+  }
+  *is_system_process = logon_sid == Sids::System();
+  return S_OK;
+}
+
+HRESULT IsUserLoggedOn(bool* is_logged_on) {
+  ASSERT1(is_logged_on);
+  bool is_local_system(false);
+  HRESULT hr = IsSystemProcess(&is_local_system);
+  if (SUCCEEDED(hr) && is_local_system) {
+    *is_logged_on = true;
+    return S_OK;
+  }
+  return UserRights::UserIsLoggedOnInteractively(is_logged_on);
+}
+
+bool IsClickOnceDisabled() {
+  CComPtr<IInternetZoneManager> zone_mgr;
+  HRESULT hr =  zone_mgr.CoCreateInstance(CLSID_InternetZoneManager);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[InternetZoneManager CreateInstance fail][0x%08x]"), hr));
+    return true;
+  }
+
+  DWORD policy = URLPOLICY_DISALLOW;
+  size_t policy_size = sizeof(policy);
+  hr = zone_mgr->GetZoneActionPolicy(URLZONE_INTERNET,
+                                     URLACTION_MANAGED_UNSIGNED,
+                                     reinterpret_cast<BYTE*>(&policy),
+                                     policy_size,
+                                     URLZONEREG_DEFAULT);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[GetZoneActionPolicy failed][0x%08x]"), hr));
+    return true;
+  }
+
+  return policy == URLPOLICY_DISALLOW;
+}
+
+// This function only uses kernel32, and it is safe to call from DllMain.
+HRESULT PinModuleIntoProcess(const CString& module_name) {
+  ASSERT1(!module_name.IsEmpty());
+  static HMODULE module_handle = NULL;
+  typedef BOOL (WINAPI *Fun)(DWORD flags,
+                             LPCWSTR module_name,
+                             HMODULE* module_handle);
+
+  HINSTANCE kernel_instance = ::GetModuleHandle(_T("kernel32.dll"));
+  ASSERT1(kernel_instance);
+  Fun pfn = NULL;
+  if (GPA(kernel_instance, "GetModuleHandleExW", &pfn)) {
+    if ((*pfn)(GET_MODULE_HANDLE_EX_FLAG_PIN, module_name, &module_handle)) {
+      return S_OK;
+    }
+    ASSERT(false, (_T("GetModuleHandleExW() failed: %d"), ::GetLastError()));
+  }
+
+  module_handle = ::LoadLibrary(module_name);
+  ASSERT(NULL != module_handle, (_T("LoadLibrary fail: %d"), ::GetLastError()));
+  if (NULL == module_handle) {
+    return HRESULTFromLastError();
+  }
+
+  return S_OK;
+}
+
+bool ShellExecuteExEnsureParent(LPSHELLEXECUTEINFO shell_exec_info) {
+  UTIL_LOG(L3, (_T("[ShellExecuteExEnsureParent]")));
+
+  ASSERT1(shell_exec_info);
+  bool shell_exec_succeeded(false);
+  DWORD last_error(ERROR_SUCCESS);
+
+  {
+    // hwnd_parent window is destroyed at the end of the scope when the
+    // destructor of scoped_window calls ::DestroyWindow.
+    scoped_window hwnd_parent;
+
+    if (!shell_exec_info->hwnd && vista_util::IsVistaOrLater()) {
+      reset(hwnd_parent, CreateForegroundParentWindowForUAC());
+
+      if (!hwnd_parent) {
+        last_error = ::GetLastError();
+        UTIL_LOG(LE, (_T("[CreateDummyOverlappedWindow failed]")));
+        // Restore last error in case the logging reset it.
+        ::SetLastError(last_error);
+        return false;
+      }
+
+      shell_exec_info->hwnd = get(hwnd_parent);
+
+      // If elevation is required on Vista, call ::SetForegroundWindow(). This
+      // will make sure that the elevation prompt, as well as the elevated
+      // process window comes up in the foreground. It will also ensure that in
+      // the case where the elevation prompt is cancelled, the error dialog
+      // shown from this process comes up in the foreground.
+      if (shell_exec_info->lpVerb &&
+          _tcsicmp(shell_exec_info->lpVerb, _T("runas")) == 0) {
+        if (!::SetForegroundWindow(get(hwnd_parent))) {
+          UTIL_LOG(LW, (_T("[SetForegroundWindow failed][%d]"),
+                        ::GetLastError()));
+        }
+      }
+    }
+
+    shell_exec_succeeded = !!::ShellExecuteEx(shell_exec_info);
+
+    if (shell_exec_succeeded) {
+      if (shell_exec_info->hProcess) {
+        DWORD pid = Process::GetProcessIdFromHandle(shell_exec_info->hProcess);
+        OPT_LOG(L1, (_T("[Started process][%u]"), pid));
+        if (!::AllowSetForegroundWindow(pid)) {
+          UTIL_LOG(LW, (_T("[AllowSetForegroundWindow failed][%d]"),
+                        ::GetLastError()));
+        }
+      } else {
+        OPT_LOG(L1, (_T("[Started process][PID unknown]")));
+      }
+    } else {
+      last_error = ::GetLastError();
+      UTIL_LOG(LE, (_T("[ShellExecuteEx failed][%s][%s][0x%08x]"),
+                    shell_exec_info->lpFile, shell_exec_info->lpParameters,
+                    last_error));
+    }
+  }
+
+  // The implicit ::DestroyWindow call from the scoped_window could have reset
+  // the last error, so restore it.
+  ::SetLastError(last_error);
+
+  return shell_exec_succeeded;
+}
+
+// Loads and unloads advapi32.dll for every call. If performance is an issue
+// consider keeping the dll always loaded and holding the pointer to the
+// RtlGenRandom in a static variable.
+// Use the function with care. While the function is documented, it may be
+// altered or made unavailable in future versions of the operating system.
+bool GenRandom(void* buffer, size_t buffer_length) {
+  ASSERT1(buffer);
+  scoped_library lib(::LoadLibrary(_T("ADVAPI32.DLL")));
+  if (lib) {
+    typedef BOOLEAN (APIENTRY *RtlGenRandomType)(void*, ULONG);
+    RtlGenRandomType rtl_gen_random = reinterpret_cast<RtlGenRandomType>(
+        ::GetProcAddress(get(lib), "SystemFunction036"));
+    return rtl_gen_random && rtl_gen_random(buffer, buffer_length);
+  }
+
+  // Use CAPI to generate randomness for systems which do not support
+  // RtlGenRandomType, for instance Windows 2000.
+  const uint32 kCspFlags = CRYPT_VERIFYCONTEXT | CRYPT_SILENT;
+  HCRYPTPROV csp = NULL;
+  if (::CryptAcquireContext(&csp, NULL, NULL, PROV_RSA_FULL, kCspFlags)) {
+    if (::CryptGenRandom(csp, buffer_length, static_cast<BYTE*>(buffer))) {
+      return true;
+    }
+  }
+  VERIFY1(::CryptReleaseContext(csp, 0));
+  return false;
+}
+
+// Assumes the path in command is properly enclosed if necessary.
+HRESULT ConfigureRunAtStartup(const CString& root_key_name,
+                              const CString& run_value_name,
+                              const CString& command,
+                              bool install) {
+  UTIL_LOG(L3, (_T("[ConfigureRunAtStartup]")));
+
+  const CString key_path = AppendRegKeyPath(root_key_name, REGSTR_PATH_RUN);
+  HRESULT hr(S_OK);
+
+  if (install) {
+    hr = RegKey::SetValue(key_path, run_value_name, command);
+  } else {
+    hr = RegKey::DeleteValue(key_path, run_value_name);
+  }
+
+  return hr;
+}
+
+HRESULT GetExePathFromCommandLine(const TCHAR* command_line,
+                                  CString* exe_path) {
+  ASSERT1(exe_path);
+  CString command_line_str(command_line);
+  command_line_str.Trim(_T(' '));
+  if (command_line_str.IsEmpty()) {
+    // ::CommandLineToArgvW parses the current process command line for blank
+    // strings. We do not want this behavior.
+    return E_INVALIDARG;
+  }
+
+  int argc = 0;
+  wchar_t** argv = ::CommandLineToArgvW(command_line_str, &argc);
+  if (argc == 0 || !argv) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[::CommandLineToArgvW failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  *exe_path = argv[0];
+  ::LocalFree(argv);
+  exe_path->Trim(_T(' '));
+  ASSERT1(!exe_path->IsEmpty());
+  return S_OK;
+}
+
+// Tries to open the _MSIExecute mutex and tests its state. MSI sets the
+// mutex when processing sequence tables. This indicates MSI is busy.
+// The function returns S_OK if the mutex is not owned by MSI or the mutex has
+// not been created.
+HRESULT WaitForMSIExecute(int timeout_ms) {
+  const TCHAR* mutex_name = _T("Global\\_MSIExecute");
+  scoped_mutex mutex(::OpenMutex(SYNCHRONIZE, false, mutex_name));
+  if (!mutex) {
+    DWORD error = ::GetLastError();
+    return (error == ERROR_FILE_NOT_FOUND) ? S_OK : HRESULT_FROM_WIN32(error);
+  }
+  UTIL_LOG(L3, (_T("[Wait for _MSIExecute]")));
+  switch (::WaitForSingleObject(get(mutex), timeout_ms)) {
+    case WAIT_OBJECT_0:
+    case WAIT_ABANDONED:
+      VERIFY1(::ReleaseMutex(get(mutex)));
+      return S_OK;
+    case WAIT_TIMEOUT:
+      return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
+    case WAIT_FAILED:
+      return HRESULTFromLastError();
+    default:
+      return E_FAIL;
+  }
+}
+
+CString GetEnvironmentVariableAsString(const TCHAR* name) {
+  CString value;
+  size_t value_length = ::GetEnvironmentVariable(name, NULL, 0);
+  if (value_length) {
+    VERIFY1(::GetEnvironmentVariable(name,
+                                     CStrBuf(value, value_length),
+                                     value_length));
+  }
+  return value;
+}
+
+// States are documented at
+// http://technet.microsoft.com/en-us/library/cc721913.aspx.
+bool IsWindowsInstalling() {
+  static const TCHAR kVistaSetupStateKey[] =
+      _T("Software\\Microsoft\\Windows\\CurrentVersion\\Setup\\State");
+  static const TCHAR kImageStateValueName[] = _T("ImageState");
+  static const TCHAR kImageStateUnuseableValue[] =
+      _T("IMAGE_STATE_UNDEPLOYABLE");
+  static const TCHAR kImageStateGeneralAuditValue[] =
+      _T("IMAGE_STATE_GENERALIZE_RESEAL_TO_AUDIT");
+  static const TCHAR kImageStateSpecialAuditValue[] =
+      _T("IMAGE_STATE_SPECIALIZE_RESEAL_TO_AUDIT");
+
+  static const TCHAR kXPSetupStateKey[] = _T("System\\Setup");
+  static const TCHAR kAuditFlagValueName[] = _T("AuditInProgress");
+
+  if (vista_util::IsVistaOrLater()) {
+    RegKey vista_setup_key;
+    HRESULT hr =
+        vista_setup_key.Open(HKEY_LOCAL_MACHINE, kVistaSetupStateKey, KEY_READ);
+    if (SUCCEEDED(hr)) {
+      CString state;
+      hr = vista_setup_key.GetValue(kImageStateValueName, &state);
+      if (SUCCEEDED(hr) &&
+          !state.IsEmpty() &&
+          (0 == state.CompareNoCase(kImageStateUnuseableValue) ||
+           0 == state.CompareNoCase(kImageStateGeneralAuditValue) ||
+           0 == state.CompareNoCase(kImageStateSpecialAuditValue)))
+        return true;  // Vista is still installing.
+    }
+  } else {
+    RegKey xp_setup_key;
+    HRESULT hr =
+        xp_setup_key.Open(HKEY_LOCAL_MACHINE, kXPSetupStateKey, KEY_READ);
+    if (SUCCEEDED(hr)) {
+      DWORD audit_flag(0);
+      hr = xp_setup_key.GetValue(kAuditFlagValueName, &audit_flag);
+      if (SUCCEEDED(hr) && 0 != audit_flag)
+        return true;  // XP is still installing.
+    }
+  }
+  return false;
+}
+
+HRESULT GetGuid(CString* guid) {
+  GUID guid_local = {0};
+  HRESULT hr = ::CoCreateGuid(&guid_local);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  *guid = GuidToString(guid_local);
+  return S_OK;
+}
+
+CString GetMessageForSystemErrorCode(DWORD error_code) {
+  CORE_LOG(L3, (_T("[GetMessageForSystemErrorCode][%u]"), error_code));
+
+  TCHAR* system_allocated_buffer = NULL;
+  const DWORD kFormatOptions = FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                               FORMAT_MESSAGE_FROM_SYSTEM |
+                               FORMAT_MESSAGE_IGNORE_INSERTS |
+                               FORMAT_MESSAGE_MAX_WIDTH_MASK;
+  DWORD tchars_written = ::FormatMessage(
+      kFormatOptions,
+      NULL,
+      error_code,
+      0,
+      reinterpret_cast<LPWSTR>(&system_allocated_buffer),
+      0,
+      NULL);
+
+  CString message;
+  if (tchars_written > 0) {
+    message = system_allocated_buffer;
+  } else {
+    UTIL_LOG(LW, (_T("[::FormatMessage failed][%u]"), ::GetLastError()));
+  }
+
+  VERIFY1(!::LocalFree(system_allocated_buffer));
+
+  return message;
+}
+
+}  // namespace omaha
diff --git a/base/utils.h b/base/utils.h
new file mode 100644
index 0000000..cfed08b
--- /dev/null
+++ b/base/utils.h
@@ -0,0 +1,903 @@
+// Copyright 2003-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_BASE_UTILS_H_
+#define OMAHA_BASE_UTILS_H_
+
+#include <windows.h>
+#include <accctrl.h>
+#include <aclapi.h>
+#include <sddl.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#include <atlstr.h>
+#include <atlsecurity.h>
+#include <atlwin.h>
+#include <memory.h>
+#include <map>
+#include <vector>
+#include "base/basictypes.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/static_assert.h"
+#include "omaha/base/user_info.h"
+
+namespace omaha {
+
+// These definitions are not in any current Microsoft SDK header file. Taken
+// from platformsdk/v6_1/files/Include/ObjIdl.h.
+MIDL_INTERFACE("0000015B-0000-0000-C000-000000000046")
+IGlobalOptions : public IUnknown {
+ public:
+    virtual HRESULT STDMETHODCALLTYPE Set(
+        /* [in] */ DWORD dwProperty,
+        /* [in] */ ULONG_PTR dwValue) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE Query(
+        /* [in] */ DWORD dwProperty,
+        /* [out] */ ULONG_PTR *pdwValue) = 0;
+};
+
+enum __MIDL___MIDL_itf_objidl_0000_0047_0001 {
+  COMGLB_EXCEPTION_HANDLING  = 1,
+  COMGLB_APPID  = 2
+};
+
+enum __MIDL___MIDL_itf_objidl_0000_0047_0002 {
+  COMGLB_EXCEPTION_HANDLE  = 0,
+  COMGLB_EXCEPTION_DONOT_HANDLE  = 1
+};
+
+// Determines whether to run ClickOnce components. This constant is not defined
+// in the SDK headers.
+#ifndef URLACTION_MANAGED_UNSIGNED
+#define URLACTION_MANAGED_UNSIGNED (0x00002004)
+#endif
+
+ULONGLONG VersionFromString(const CString& s);
+
+CString StringFromVersion(ULONGLONG version);
+
+// Gets current directory
+CString GetCurrentDir();
+
+// Creates a unique file name using a new guid. Does not check for
+// presence of this file in the directory.
+HRESULT GetNewFileNameInDirectory(const CString& dir, CString* file_name);
+
+// Gets security descriptor with a DACL that grants the admin_access_mask to
+// Admins and System, and the non_admin_access_mask to Interactive.
+void GetEveryoneDaclSecurityDescriptor(CSecurityDesc* sd,
+                                       ACCESS_MASK admin_access_mask,
+                                       ACCESS_MASK non_admin_access_mask);
+
+// Get security descriptor containing a DACL that grants the ACCESS_MASK access
+// to admins and system.
+void GetAdminDaclSecurityDescriptor(CSecurityDesc* sd, ACCESS_MASK accessmask);
+
+// Get security attributes containing a DACL that grant the ACCESS_MASK access
+// to admins and system.
+void GetAdminDaclSecurityAttributes(CSecurityAttributes* sec_attr,
+                                    ACCESS_MASK accessmask);
+
+// This method is intended to be called by same or lower integrity COM clients.
+// Calls ::CoInitializeSecurity with the default DACL. Servers are allowed to
+// impersonate the client.
+HRESULT InitializeClientSecurity();
+
+// This method calls ::CoInitializeSecurity with security settings and ACLs of
+// callers that are allowed access to the COM server. Clients can only identify
+// the server, and custom marshaling as well as activate-as-activator are
+// disallowed.
+// * If the bool is set, a DACL that provides COM_RIGHTS_EXECUTE access
+// for medium-integrity callers is set.
+// * If the bool is not set, but the caller is Local System, the function will
+// set a security descriptor that also allows the Administrators group access to
+// the COM server.
+HRESULT InitializeServerSecurity(bool allow_calls_from_medium);
+
+// Ensures that the COM marshaling machinery does not eat crashes.
+HRESULT DisableCOMExceptionHandling();
+
+// Merges an Allowed ACE into a named object. If the ACE already exists in the
+// DACL with the same permissions (or a superset) and the same ACE flags, the
+// merge is skipped.
+HRESULT AddAllowedAce(const TCHAR* object_name,
+                      SE_OBJECT_TYPE object_type,
+                      const CSid& sid,
+                      ACCESS_MASK required_permissions,
+                      uint8 required_ace_flags);
+
+struct NamedObjectAttributes {
+  CString name;
+  CSecurityAttributes sa;
+};
+
+// For machine and local system, the prefix would be "Global\G{obj_name}".
+// For user, the prefix would be "Global\G{user_sid}{obj_name}".
+// For machine objects, returns a security attributes that gives permissions to
+// both Admins and SYSTEM. This allows for cases where SYSTEM creates the named
+// object first. The default DACL for SYSTEM will not allow Admins access.
+void GetNamedObjectAttributes(const TCHAR* base_name,
+                              bool is_machine,
+                              NamedObjectAttributes* attr);
+
+// Returns true if the current process is running as SYSTEM.
+HRESULT IsSystemProcess(bool* is_system_process);
+
+// Returns true if the user of the current process is Local System or it has an
+// interactive session: console, terminal services, or fast user switching.
+HRESULT IsUserLoggedOn(bool* is_logged_on);
+
+// Returns true if URLACTION_MANAGED_UNSIGNED is disabled for the Internet zone
+// for the current user.
+bool IsClickOnceDisabled();
+
+// Wrapper around ::GetProcAddress().
+template <typename T>
+bool GPA(HMODULE module, const char* function_name, T* function_pointer) {
+  ASSERT1(module);
+  ASSERT1(function_name);
+  ASSERT1(function_pointer);
+
+  *function_pointer = reinterpret_cast<T>(::GetProcAddress(module,
+                                                           function_name));
+  if (NULL == *function_pointer) {
+    UTIL_LOG(LW, (_T("GetProcAddress failed [%s]"), CA2T(function_name)));
+  }
+  return NULL != *function_pointer;
+}
+
+#define GPA_WRAP(module,                                                    \
+                 function,                                                  \
+                 proto,                                                     \
+                 call,                                                      \
+                 calling_convention,                                        \
+                 result_type,                                               \
+                 result_error)                                              \
+typedef result_type (calling_convention *function##_pointer) proto;         \
+inline result_type function##Wrap proto {                                   \
+  HINSTANCE module_instance = ::GetModuleHandle(_T(#module));               \
+  ASSERT1(module_instance);                                                 \
+  if (!module_instance) {                                                   \
+    return result_error;                                                    \
+  }                                                                         \
+  function##_pointer fn = NULL;                                             \
+  return GPA(module_instance, #function, &fn) ? (*fn) call : result_error;  \
+}
+
+GPA_WRAP(kernel32.dll,
+         AttachConsole,
+         (DWORD process_id),
+         (process_id),
+         WINAPI,
+         BOOL,
+         0);
+
+// Private Object Namespaces for Vista and above. More information here:
+// http://msdn2.microsoft.com/en-us/library/ms684295(VS.85).aspx
+GPA_WRAP(kernel32.dll,
+         CreateBoundaryDescriptorW,
+         (LPCWSTR boundary_name, ULONG flags),
+         (boundary_name, flags),
+         WINAPI,
+         HANDLE,
+         NULL);
+GPA_WRAP(kernel32.dll,
+         AddSIDToBoundaryDescriptor,
+         (HANDLE* boundary_descriptor, PSID required_sid),
+         (boundary_descriptor, required_sid),
+         WINAPI,
+         BOOL,
+         FALSE);
+GPA_WRAP(kernel32.dll,
+         CreatePrivateNamespaceW,
+         (LPSECURITY_ATTRIBUTES private_namespace_attributes, LPVOID boundary_descriptor, LPCWSTR alias_prefix),  // NOLINT
+         (private_namespace_attributes, boundary_descriptor, alias_prefix),
+         WINAPI,
+         HANDLE,
+         NULL);
+GPA_WRAP(kernel32.dll,
+         OpenPrivateNamespaceW,
+         (LPVOID boundary_descriptor, LPCWSTR alias_prefix),
+         (boundary_descriptor, alias_prefix),
+         WINAPI,
+         HANDLE,
+         NULL);
+
+bool IsPrivateNamespaceAvailable();
+
+
+HRESULT PinModuleIntoProcess(const CString& module_name);
+
+// Creates a directory with default security.
+//   S_OK:    Created directory
+//   S_FALSE: Directory already existed
+//   E_FAIL:  Couldn't create
+HRESULT CreateDir(const TCHAR* dirname, LPSECURITY_ATTRIBUTES security_attr);
+
+// Gets the path for the specified special folder.
+HRESULT GetFolderPath(int csidl, CString* path);
+
+// Returns true if this directory name is 'safe' for deletion:
+//  - it doesn't contain ".."
+//  - it doesn't specify a drive root
+bool SafeDirectoryNameForDeletion(const TCHAR* dir_name);
+
+// Deletes a directory and its files.
+HRESULT DeleteDirectory(const TCHAR* ir_name);
+
+// Deletes all files in a directory.
+HRESULT DeleteDirectoryFiles(const TCHAR* dir_name);
+
+// Deletes all files in a directory matching a wildcard.
+HRESULT DeleteWildcardFiles(const TCHAR* dir_name, const TCHAR* wildcard_name);
+
+// Deletes either a file or directory before or after reboot.
+HRESULT DeleteBeforeOrAfterReboot(const TCHAR* targetname);
+
+// Gets the size of all files in a directory.
+// Aborts counting if one of the maximum criteria is reached.
+HRESULT SafeGetDirectorySize(const TCHAR* dir_name,
+                             uint64* size,
+                             HANDLE shutdown_event,
+                             uint64 max_size,
+                             int max_depth,
+                             int max_file_count,
+                             int max_running_time_ms);
+
+// Gets size of all files in a directory.
+HRESULT GetDirectorySize(const TCHAR* dir_name, uint64* size);
+
+enum TimeCategory {
+  PAST = 0,  // older than 40 years from now
+  FUTURE,    // in the future by > 1 day
+  PRESENT,   // neither ANCIENT nor FUTURE
+};
+
+TimeCategory GetTimeCategory(time64 t);
+
+// Returns true if a given time is likely to be valid.
+bool IsValidTime(time64 t);
+
+// Gets a time64 value.
+// If the value is greater than the
+// max value, then SetValue will be called using the max_value.
+//
+//  Args:
+//   full_key_name:   the reg keyto read to get the time value.
+//   value_name:      the name for the reg key value to be read.
+//                    (may be NULL to get the default value)
+//   max_time:        the maximum value for the reg key.
+//   value:           the time value read.  May not be set on failure.
+//   limited_value:   true iff will be set if the value was
+//                    changed (and resaved). (NULL is allowable.)
+HRESULT GetLimitedTimeValue(const TCHAR* full_key_name,
+                            const TCHAR* value_name,
+                            time64 max_time,
+                            time64* value,
+                            bool* limited_value);
+
+// Gets a time64 value trying reg keys successively if there is a
+// failure in getting a value.
+//
+// Typically used when there is a user value and a default value if the
+// user has none.
+//
+//  Args:
+//   full_key_names:  a list of reg keys to try successively (starting
+//                    with index 0) to read to get the time value.  The
+//                    attempts stops as soon as there is a successful read or
+//                    when all keys have been tried.
+//   key_names_length: number of keys in full_key_names.
+//   value_name:      the name for the reg key value to be read.
+//                    (may be NULL to get the default value)
+//   max_time:        the maximum value for the reg key.
+//   value:           the time value read.  May not be set on failure.
+//   limited_value:   true iff will be set if the value was
+//                    changed (and resaved). (NULL is allowable.)
+HRESULT GetLimitedTimeValues(const TCHAR* full_key_names[],
+                             int key_names_length,
+                             const TCHAR* value_name,
+                             time64 max_time,
+                             time64* value,
+                             bool* limited_value);
+
+// Convert milliseconds to a relative time in units of 100ns, suitable for use
+// with waitable timers
+LARGE_INTEGER MSto100NSRelative(DWORD ms);
+
+// TODO(omaha): remove from public interface.
+inline void WINAPI NullAPCFunc(ULONG_PTR) {}
+
+// Forces rasman.dll load to avoid a crash in wininet.
+void EnsureRasmanLoaded();
+
+// Returns if the HRESULT argument is a COM error
+// TODO(omaha): use an ANONYMOUS_VARIABLE to avoid the situation in which the
+// macro gets called like RET_IF_FAILED(hr);
+// For now, use a quick fix hr -> __hr. Leading underscore names are not to be
+// used in application code.
+#define RET_IF_FAILED(x)    \
+    do {                    \
+      HRESULT __hr(x);      \
+      if (FAILED(__hr)) {   \
+        return __hr;        \
+      }                     \
+    } while (false)
+
+// return error if the first argument evaluates to false
+#define RET_IF_FALSE(x, err)  \
+  do {                        \
+    if (!(x)) {               \
+      return err;             \
+    }                         \
+  } while (false)
+
+// return false if the HRESULT argument is a COM error
+#define RET_FALSE_IF_FAILED(x)  \
+  do {                          \
+    if (FAILED(x)) {            \
+      return false;             \
+    }                           \
+  } while (false)
+
+// return true if the HRESULT argument is a COM error
+#define RET_TRUE_IF_FAILED(x)   \
+  do {                          \
+    if (FAILED(x)) {            \
+      return true;              \
+    }                           \
+  } while (false)
+
+// return if the HRESULT argument evaluates to FAILED - but also assert
+// if failed
+#define RET_IF_FAILED_ASSERT(x, msg) \
+    do {                             \
+      HRESULT hr(x);                 \
+      if (FAILED(hr)) {              \
+        ASSERT(false, msg);          \
+        return hr;                   \
+      }                              \
+    } while (false)
+
+
+// return if the HRESULT argument evaluates to FAILED - but also log an error
+// message if failed
+#define RET_IF_FAILED_LOG(x, cat, msg) \
+    do {                               \
+      HRESULT hr(x);                   \
+      if (FAILED(hr)) {                \
+        LC_LOG(cat, LEVEL_ERROR, msg); \
+        return hr;                     \
+      }                                \
+    } while (false)
+
+// return if the HRESULT argument evaluates to FAILED - but also REPORT an error
+// message if failed
+#define RET_IF_FAILED_REPORT(x, msg, n) \
+    do {                                \
+      HRESULT hr(x);                    \
+      if (FAILED(hr)) {                 \
+        REPORT(false, R_ERROR, msg, n); \
+        return hr;                      \
+      }                                 \
+    } while (false)
+
+// Initializes a POD to zero.
+// Using this function requires discipline. Don't use for types that have a
+// v-table or virtual bases.
+template <typename T>
+inline void SetZero(T& p) {   // NOLINT
+  // Guard against the easy mistake of
+  //    foo(int *p) { SetZero(p); } instead of
+  //                  SetZero(*p);
+  // which it should be.
+  STATIC_ASSERT(sizeof(p) != sizeof(void*));    // NOLINT
+
+  // A POD (plain old data) object has one of these data types:
+  // a fundamental type, union, struct, array,
+  // or class--with no constructor. PODs don't have virtual functions or
+  // virtual bases.
+
+  // Test to see if the type has constructors.
+  union CtorTest {
+      T t;
+      int i;
+  };
+
+  // TODO(omaha): There might be a way to test if the type has virtuals
+  // For now, if we zero a type with virtuals by mistake, it is going to crash
+  // predictable at run-time when the virtuals are called.
+
+  memset(&p, 0, sizeof(T));
+}
+
+inline void SecureSetZero(CString* p) {
+  ASSERT1(p);
+  if (!p->IsEmpty()) {
+    ::SecureZeroMemory(p->GetBufferSetLength(p->GetLength()),
+                       p->GetLength() * sizeof(TCHAR));
+    p->ReleaseBuffer();
+  }
+}
+
+inline void SecureSetZero(CComVariant* p) {
+  ASSERT1(p);
+  ASSERT1(V_VT(p) == VT_BSTR);
+  uint32 byte_len = ::SysStringByteLen(V_BSTR(p));
+  if (byte_len > 0) {
+    ::SecureZeroMemory(V_BSTR(p), byte_len);
+  }
+}
+
+// CreateForegroundParentWindowForUAC creates a WS_POPUP | WS_VISIBLE with zero
+// size, of the STATIC WNDCLASS. It uses the default running EXE module
+// handle for creation.
+//
+// A visible centered foreground window is needed as the parent in Windows 7 and
+// above, to allow the UAC prompt to come up in the foreground, centered.
+// Otherwise, the elevation prompt will be minimized on the taskbar. A zero size
+// window works. A plain vanilla WS_POPUP allows the window to be free of
+// adornments. WS_EX_TOOLWINDOW prevents the task bar from showing the
+// zero-sized window.
+//
+// Returns NULL on failure. Call ::GetLastError() to get extended error
+// information on failure.
+inline HWND CreateForegroundParentWindowForUAC() {
+  CWindow foreground_parent;
+  if (foreground_parent.Create(_T("STATIC"), NULL, NULL, NULL,
+                               WS_POPUP | WS_VISIBLE, WS_EX_TOOLWINDOW)) {
+    foreground_parent.CenterWindow(NULL);
+    ::SetForegroundWindow(foreground_parent);
+  }
+  return foreground_parent.Detach();
+}
+
+// TODO(omaha): move the definition and ShellExecuteExEnsureParent function
+// below into shell.h
+
+#if (NTDDI_VERSION < NTDDI_WINXPSP1)
+// This value is not defined in the header, but has no effect on older OSes.
+#define SEE_MASK_NOZONECHECKS      0x00800000
+#endif
+
+// ShellExecuteExEnsureParent is a wrapper around ::ShellExecuteEx.
+// It ensures that we always have a parent window. In elevation scenarios, we
+// need to use the HWND property to be acknowledged as a foreground application
+// on Windows Vista. Otherwise, the elevation prompt will appear minimized on
+// the taskbar The UAC elevation mechanism uses the HWND as part of determining
+// whether the elevation is a foreground elevation.
+//
+// A better place for this might be in the Process class. However, to
+// reduce dependencies for the stub, placing this in utils.
+//
+// Return values:
+//   ShellExecuteExEnsureParent returns TRUE on success, and FALSE on failure.
+//   Call ::GetLastError() to get the extended error information on failure.
+//
+// Args:
+//   shell_exec_info structure pointer, filled in, with an optional HWND.
+//   The structure is not validated. If the HWND is NULL, it creates one.
+bool ShellExecuteExEnsureParent(LPSHELLEXECUTEINFO shell_exec_info);
+
+//
+// Wait with a message loop.
+// Returns true if the wait completed successfully and false in case of an
+// error.
+//
+
+// Waits with a message loop until any of the synchronization objects is
+// signaled. Return the index of the object that satisfied the wait.
+bool WaitWithMessageLoopAny(const std::vector<HANDLE>& handles, uint32* pos);
+
+// Waits with message loop until all the synchronization objects are signaled.
+bool WaitWithMessageLoopAll(const std::vector<HANDLE>& handles);
+
+// Waits with message loop until the synchronization object is signaled.
+bool WaitWithMessageLoop(HANDLE h);
+
+// Waits with message loop for a certain period of time
+bool WaitWithMessageLoopTimed(DWORD ms);
+
+//
+// TODO(omaha): message handler classes should go in other module.
+//
+// Handles windows messages
+class MessageHandlerInterface {
+ public:
+  virtual ~MessageHandlerInterface() {}
+  // Does the translate/dispatch for one window message
+  // msg is never NULL.
+  virtual void Process(MSG* msg) = 0;
+};
+
+// The simplest working implementation of a message handler.
+// It does TranslateMessage/DispatchMessage.
+class BasicMessageHandler : public MessageHandlerInterface {
+ public:
+  BasicMessageHandler() {}
+  virtual void Process(MSG* msg);
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(BasicMessageHandler);
+};
+
+// An internal detail (used to handle messages
+// and adjust the handles/cnt as needed).
+class MessageHandlerInternalInterface {
+ public:
+  virtual ~MessageHandlerInternalInterface() {}
+  // Does the translate/dispatch for one window message
+  // msg is never NULL and may change the handles and cnt (which are
+  // never NULL).
+  virtual void Process(MSG* msg, const HANDLE** handles, uint32* cnt) = 0;
+};
+
+// The callback for MessageLoopInterface::RegisterWaitForSingleObject
+class WaitCallbackInterface {
+ public:
+  virtual ~WaitCallbackInterface() {}
+  // Return false from this method to terminate the message loop.
+  virtual bool HandleSignaled(HANDLE handle) = 0;
+};
+
+// This is similar to a threadpool interface
+// but this is done on the main message loop instead.
+//
+// Important: These method calls are *not* threadsafe and
+// should only be done on the message loop thread.
+class MessageLoopInterface {
+ public:
+  virtual ~MessageLoopInterface() {}
+  // sets-up a callback for when the handle becomes signaled
+  //   * the callback happens on the main thread.
+  //   * the handle/callback is removed when
+  //     the callback is made.
+  //   * If the handle becomes invalid while it is being waited on,
+  //     the message loop will exit.
+  //   * If the handle has already been registered,
+  //     then adding it again will essentially replace the
+  //     previous callback with the new one.
+  virtual bool RegisterWaitForSingleObject(HANDLE handle,
+                                           WaitCallbackInterface* callback) = 0;
+
+  // stops watching for the handle to be signaled
+  virtual bool UnregisterWait(HANDLE handle) = 0;
+};
+
+// An implementation of the MessageLoopInterface
+class MessageLoopWithWait : public MessageLoopInterface,
+                            private MessageHandlerInternalInterface {
+ public:
+  MessageLoopWithWait();
+
+  // This method needs to be called before the "Process()"
+  // method or else "Process()" will crash.
+  //
+  // Args:
+  //    message_handler: handles any messages that occur
+  //       *Note* It is the callers responsibility to keep
+  //       message_handler around while this class exists
+  //       and the *caller* should free message_handler
+  //       after that.
+  void set_message_handler(MessageHandlerInterface* message_handler);
+
+  // sets-up a callback for when the handle becomes signaled
+  virtual bool RegisterWaitForSingleObject(HANDLE handle,
+                                           WaitCallbackInterface* callback);
+
+  // stops watching for the handle to be signaled
+  virtual bool UnregisterWait(HANDLE handle);
+
+  // The message loop and handle callback routine
+  HRESULT Process();
+
+ private:
+  // Handles one messgae and adjus tthe handles and cnt as appropriate after
+  // handling the message
+  virtual void Process(MSG* msg, const HANDLE** handles, uint32* cnt);
+
+  void RemoveHandleAt(uint32 pos);
+
+  MessageHandlerInterface* message_handler_;
+
+  // Handles that are checked for being signaled.
+  std::vector<HANDLE> callback_handles_;
+
+  // What to call when a handle is signaled.
+  std::vector<WaitCallbackInterface*> callbacks_;
+  DISALLOW_EVIL_CONSTRUCTORS(MessageLoopWithWait);
+};
+
+// Calls an entry point that may be exposed from a DLL
+//   It is an error if the DLL is missing or can't be loaded
+//   If the entry point is missing the error returned is
+//   ERROR_INVALID_FUNCTION (as an HRESULT).
+//   Otherwise, if the function is called successfully then
+//   CallEntryPoint0 return S_OK and the result parameter is set to the
+//   return value from the function call.
+HRESULT CallEntryPoint0(const TCHAR* dll_path,
+                        const char* function_name,
+                        HRESULT* result);
+
+// (Un)Registers a COM DLL with the system.  Returns S_FALSE if entry
+// point missing (so that it you can call (Un)RegisterDll on any DLL
+// without worrying whether the DLL is actually a COM server or not).
+HRESULT RegisterDll(const TCHAR* dll_path);
+HRESULT UnregisterDll(const TCHAR* dll_path);
+
+// (Un)Registers a COM Local Server with the system.
+HRESULT RegisterServer(const TCHAR* exe_path);
+HRESULT UnregisterServer(const TCHAR* exe_path);
+HRESULT RegisterOrUnregisterExe(const TCHAR* exe_path, const TCHAR* cmd_line);
+
+// (Un)Registers a COM Service with the system.
+HRESULT RegisterService(const TCHAR* exe_path);
+HRESULT UnregisterService(const TCHAR* exe_path);
+
+// Starts a service.
+HRESULT RunService(const TCHAR* service_name);
+
+// Read an entire file into a memory buffer. Use this function when you need
+// exclusive access to the file.
+// Returns MEM_E_INVALID_SIZE if the file size is larger than max_len (unless
+// max_len == 0, in which case it is ignored)
+HRESULT ReadEntireFile(const TCHAR* filepath,
+                       uint32 max_len,
+                       std::vector<byte>* buffer_out);
+
+// Allows specifying a sharing mode such as FILE_SHARE_READ. Otherwise,
+// this is identical to ReadEntireFile.
+HRESULT ReadEntireFileShareMode(const TCHAR* filepath,
+                                uint32 max_len,
+                                DWORD share_mode,
+                                std::vector<byte>* buffer_out);
+
+// Writes an entire file from a memory buffer
+HRESULT WriteEntireFile(const TCHAR * filepath,
+                        const std::vector<byte>& buffer_in);
+
+// Conversions between a byte stream and a std::string
+HRESULT BufferToString(const std::vector<byte>& buffer_in, CStringA* str_out);
+HRESULT BufferToString(const std::vector<byte>& buffer_in, CString* str_out);
+HRESULT StringToBuffer(const CStringA& str_in, std::vector<byte>* buffer_out);
+HRESULT StringToBuffer(const CString& str_in, std::vector<byte>* buffer_out);
+
+// Splits a "full regkey name" into a key name part and a value name part.
+// Handles "(default)" as a value name.  Treats a trailing "/" as "(default)".
+HRESULT RegSplitKeyvalueName(const CString& keyvalue_name,
+                             CString* key_name,
+                             CString* value_name);
+
+// Expands string with embedded special variables which are enclosed
+// in '%' pair. For example, "%PROGRAMFILES%\Google" expands to
+// "C:\Program Files\Google".
+// If any of the embedded variable can not be expanded, we will leave it intact
+// and return HRESULT_FROM_WIN32(ERROR_NOT_FOUND)
+HRESULT ExpandEnvLikeStrings(const TCHAR* src,
+                             const std::map<CString, CString>& keywords,
+                             CString* dest);
+
+// Returns true if the path represents a registry path.
+bool IsRegistryPath(const TCHAR* path);
+
+// Returns true if the path is a URL.
+bool IsUrl(const TCHAR* path);
+
+// Converts GUID to string.
+CString GuidToString(const GUID& guid);
+
+// Converts string to GUID.
+HRESULT StringToGuidSafe(const CString& str, GUID* guid);
+
+// Converts a variant containing a list of strings.
+void VariantToStringList(VARIANT var, std::vector<CString>* list);
+
+// Appends two registry key paths, takes care of extra separators in the
+// beginning or end of the key.
+CString AppendRegKeyPath(const CString& one, const CString& two);
+CString AppendRegKeyPath(const CString& one,
+                         const CString& two,
+                         const CString& three);
+
+// Returns the list of user keys that are present within the HKEY_USERS key
+// the method only returns the keys of the users and takes care of,
+// removing the well known sids. The returned values are the complete values
+// from the root of the registry
+HRESULT GetUserKeysFromHkeyUsers(std::vector<CString>* key_names);
+
+// Use when a function should be able to
+// be replaced with another implementation. Usually,
+// this is done for testing code only.
+//
+// Typical usage:
+//
+//  typedef bool BoolPreferenceFunctionType();
+//  CallInterceptor<BoolPreferenceFunctionType> should_send_stats_interceptor;
+//  BoolPreferenceFunctionType* ReplaceShouldSendStatsFunction(
+//      BoolPreferenceFunctionType* replacement) {
+//    return should_send_stats_interceptor.ReplaceFunction(replacement);
+//  }
+template <typename R>
+class CallInterceptor {
+ public:
+  CallInterceptor() {
+    interceptor_ = NULL;
+  }
+
+  R* ReplaceFunction(R* replacement) {
+    R* old = interceptor_;
+    interceptor_ = replacement;
+    return old;
+  }
+
+  R* interceptor() {
+    return interceptor_;
+  }
+
+ private:
+  R* interceptor_;
+  DISALLOW_EVIL_CONSTRUCTORS(CallInterceptor);
+};
+
+// Gets a handle of the current process. The handle is a real handle
+// and the caller must close it
+HRESULT GetCurrentProcessHandle(HANDLE* handle);
+
+// Duplicates the given token from the source_process into the current process.
+HRESULT DuplicateTokenIntoCurrentProcess(HANDLE source_process,
+                                         HANDLE token_to_duplicate,
+                                         CAccessToken* duplicated_token);
+
+// Helper class for an ATL module that registers a custom AccessPermission
+// to allow local calls from interactive users and the system account.
+// Derive from this class as well as CAtlModuleT (or a derivative).
+// Override RegisterAppId() and UnregisterAppId(), and delegate to the
+// corresponding functions in this class.
+template <class T>
+class LocalCallAccessPermissionHelper {
+ public:
+  HRESULT RegisterAppId() throw() {
+    // Local call permissions allowed for Interactive Users and Local System
+    static LPCTSTR ALLOW_LOCAL_CALL_SDDL =
+        _T("O:BAG:BAD:(A;;0x3;;;IU)(A;;0x3;;;SY)");
+
+    UTIL_LOG(L1, (_T("[LocalCallAccessPermissionHelper::RegisterAppId]")));
+
+    // First call the base ATL module implementation, so the AppId is registered
+    RET_IF_FAILED(T::UpdateRegistryAppId(TRUE));
+
+    // Next, write the AccessPermission value
+    RegKey key_app_id;
+    RET_IF_FAILED(key_app_id.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_WRITE));
+
+    RegKey key;
+    RET_IF_FAILED(key.Create(key_app_id.Key(), T::GetAppIdT()));
+    CSecurityDesc sd;
+    RET_IF_FALSE(sd.FromString(ALLOW_LOCAL_CALL_SDDL), HRESULTFromLastError());
+    RET_IF_FAILED(key.SetValue(
+        _T("AccessPermission"),
+        reinterpret_cast<const byte*>(sd.GetPSECURITY_DESCRIPTOR()),
+        sd.GetLength()));
+
+    UTIL_LOG(L1, (_T("[LocalCallAccessPermissionHelper::RegisterAppId]")
+                  _T("[succeeded]")));
+    return S_OK;
+  }
+
+  HRESULT UnregisterAppId() throw() {
+    // First remove the AccesPermission entry.
+    RegKey key_app_id;
+    RET_IF_FAILED(key_app_id.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_WRITE));
+
+    RegKey key;
+    RET_IF_FAILED(key.Open(key_app_id.Key(), T::GetAppIdT(), KEY_WRITE));
+    VERIFY1(SUCCEEDED(key.DeleteValue(_T("AccessPermission"))));
+
+    // Now, call the base ATL module implementation to unregister the AppId
+    RET_IF_FAILED(T::UpdateRegistryAppId(FALSE));
+
+    UTIL_LOG(L1, (_T("[LocalCallAccessPermissionHelper::UnregisterAppId'")
+                  _T("[succeeded]")));
+    return S_OK;
+  }
+};
+
+// Returns true if the argument is a guid.
+inline bool IsGuid(const TCHAR* s) {
+  if (!s) return false;
+  GUID guid = {0};
+  return SUCCEEDED(StringToGuidSafe(s, &guid));
+}
+
+inline bool IsLocalSystemSid(const TCHAR* sid) {
+  ASSERT1(sid);
+  return _tcsicmp(sid, kLocalSystemSid) == 0;
+}
+
+// Fills a buffer with cryptographically random bytes.
+bool GenRandom(void* buffer, size_t buffer_length);
+
+// Deletes an object. The functor is useful in for_each algorithms.
+struct DeleteFun {
+  template <class T> void operator()(T ptr) { delete ptr; }
+};
+
+// Sets or clears the specified value in the Run key to the specified command.
+HRESULT ConfigureRunAtStartup(const CString& root_key_name,
+                              const CString& run_value_name,
+                              const CString& command,
+                              bool install);
+
+// Cracks a command line and returns the program name, which is the first
+// whitespace separated token.
+HRESULT GetExePathFromCommandLine(const TCHAR* command_line,
+                                  CString* exe_path);
+
+// Waits for MSI to complete, if MSI is busy installing or uninstalling apps.
+HRESULT WaitForMSIExecute(int timeout_ms);
+
+// Returns the value of the specified environment variable.
+CString GetEnvironmentVariableAsString(const TCHAR* name);
+
+// Returns true if the OS is installing (e.g., Audit Mode at an OEM factory).
+// NOTE: This is unreliable on Windows Vista and later. Some computers remain in
+// one of the incomplete states even after OOBE. See http://b/1690617.
+bool IsWindowsInstalling();
+
+// TODO(omaha): unit test.
+inline uint64 GetGuidMostSignificantUint64(const GUID& guid) {
+  return (static_cast<uint64>(guid.Data1) << 32) +
+         (static_cast<uint64>(guid.Data2) << 16) +
+         static_cast<uint64>(guid.Data3);
+}
+
+// Calls either ATL::InterlockedExchangePointer if ATL headers are included,
+// or ::InterlockedExchangePointer. ATL slightly redefines the SDK function
+// with the same name.
+template <typename T>
+inline T* interlocked_exchange_pointer(T* volatile * target,
+                                       const T* value) {
+  return static_cast<T*>(InterlockedExchangePointer(
+      reinterpret_cast<void**>(const_cast<T**>(target)),
+      const_cast<T*>(value)));
+}
+
+// Gets a guid from the system (for user id, etc.)
+// The guid will be of the form: {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+HRESULT GetGuid(CString* guid);
+
+// Returns the message for an error code in the user's language or an
+// empty string if an error occurs. The function does not support
+// "insert sequences". The sequence will be returned in the string.
+CString GetMessageForSystemErrorCode(DWORD error_code);
+
+// Ceil function for integer types. Returns the quotient of the two
+// numbers (m/n) rounded upwards to the nearest integer.
+// T should be unsigned integer type, such as unsigned short, unsigned long,
+// unsigned int etc.
+template <typename T>
+inline T CeilingDivide(T m, T n) {
+  ASSERT1(n != 0);
+
+  return (m + n - 1) / n;
+}
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_UTILS_H_
diff --git a/base/utils_unittest.cc b/base/utils_unittest.cc
new file mode 100644
index 0000000..582a490
--- /dev/null
+++ b/base/utils_unittest.cc
@@ -0,0 +1,749 @@
+// Copyright 2003-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <ATLComTime.h>
+#include <atltypes.h>
+#include <atlwin.h>
+#include <map>
+#include <vector>
+#include "omaha/base/app_util.h"
+#include "omaha/base/atl_regexp.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/dynamic_link_kernel32.h"
+#include "omaha/base/file.h"
+#include "omaha/base/module_utils.h"
+#include "omaha/base/path.h"
+#include "omaha/base/shell.h"
+#include "omaha/base/string.h"
+#include "omaha/base/time.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+// Make sure that the time functions work.
+TEST(UtilsTest, Time) {
+  // TODO(omaha): - add a test from string to time and back again.
+  // Further test the time converters.
+  time64 now = GetCurrent100NSTime();
+  ASSERT_TRUE(StringToTime(TimeToString(now)) == now);
+
+  // Test GetTimeCategory.
+  ASSERT_EQ(PAST,
+            GetTimeCategory(static_cast<time64>(0)));
+  ASSERT_EQ(PRESENT,
+            GetTimeCategory(static_cast<time64>(now)));
+  ASSERT_EQ(PRESENT,
+            GetTimeCategory(static_cast<time64>(now - kDaysTo100ns)));
+  ASSERT_EQ(PRESENT,
+            GetTimeCategory(static_cast<time64>(now - 365 * kDaysTo100ns)));
+  // A little bit in the future is also considered present.
+  ASSERT_EQ(PRESENT,
+            GetTimeCategory(static_cast<time64>(now + kDaysTo100ns)));
+  ASSERT_EQ(PRESENT,
+            GetTimeCategory(static_cast<time64>(
+                now - 30 * 365 * kDaysTo100ns)));
+  ASSERT_EQ(PAST,
+            GetTimeCategory(static_cast<time64>(
+                now - 50 * 365 * kDaysTo100ns)));
+  ASSERT_EQ(FUTURE,
+            GetTimeCategory(static_cast<time64>(now + kDaysTo100ns * 6)));
+  ASSERT_EQ(FUTURE,
+            GetTimeCategory(static_cast<time64>(now + 365 * kDaysTo100ns)));
+
+  // Test IsValidTime.
+  ASSERT_FALSE(IsValidTime(static_cast<time64>(0)));
+  ASSERT_TRUE(IsValidTime(static_cast<time64>(now)));
+  ASSERT_TRUE(IsValidTime(static_cast<time64>(now - 365 * kDaysTo100ns)));
+  ASSERT_TRUE(IsValidTime(static_cast<time64>(now - 10 * 365 * kDaysTo100ns)));
+  ASSERT_TRUE(IsValidTime(static_cast<time64>(now + kDaysTo100ns)));
+  ASSERT_FALSE(IsValidTime(static_cast<time64>(now - 50 * 365 * kDaysTo100ns)));
+  ASSERT_FALSE(IsValidTime(static_cast<time64>(now + 50 * 365 * kDaysTo100ns)));
+  ASSERT_FALSE(IsValidTime(static_cast<time64>(now + kDaysTo100ns * 6)));
+}
+
+TEST(UtilsTest, GetFolderPath_Success) {
+  CString path;
+  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &path));
+  BOOL isWow64 = FALSE;
+  EXPECT_SUCCEEDED(Kernel32::IsWow64Process(GetCurrentProcess(), &isWow64));
+  CString expected_path = isWow64 ?
+      _T("C:\\Program Files (x86)") : _T("C:\\Program Files");
+  EXPECT_STREQ(expected_path, path);
+}
+
+TEST(UtilsTest, GetFolderPath_Errors) {
+  CString path;
+  EXPECT_EQ(E_INVALIDARG, GetFolderPath(0x7fff, &path));
+  EXPECT_TRUE(path.IsEmpty());
+  EXPECT_EQ(E_INVALIDARG, GetFolderPath(CSIDL_PROGRAM_FILES, NULL));
+}
+
+TEST(UtilsTest, CallEntryPoint0) {
+  HRESULT hr(E_FAIL);
+  ASSERT_FAILED(CallEntryPoint0(L"random-nonsense.dll", "foobar", &hr));
+}
+
+TEST(UtilsTest, ReadEntireFile) {
+  TCHAR directory[MAX_PATH] = {0};
+  ASSERT_TRUE(GetModuleDirectory(NULL, directory));
+  CString file_name;
+  file_name.Format(_T("%s\\unittest_support\\declaration.txt"), directory);
+
+  std::vector<byte> buffer;
+  ASSERT_FAILED(ReadEntireFile(L"C:\\F00Bar\\ImaginaryFile", 0, &buffer));
+
+  ASSERT_SUCCEEDED(ReadEntireFile(file_name, 0, &buffer));
+  ASSERT_EQ(9405, buffer.size());
+  buffer.resize(0);
+  ASSERT_FAILED(ReadEntireFile(L"C:\\WINDOWS\\Greenstone.bmp", 1000, &buffer));
+}
+
+// TODO(omaha): Need a test for WriteEntireFile
+// TEST(UtilsTest, WriteEntireFile) {
+// }
+
+TEST(UtilsTest, RegSplitKeyvalueName) {
+  CString key_name, value_name;
+  ASSERT_SUCCEEDED(RegSplitKeyvalueName(CString(L"HKLM\\Foo\\"),
+                                        &key_name,
+                                        &value_name));
+  ASSERT_STREQ(key_name, L"HKLM\\Foo");
+  ASSERT_TRUE(value_name.IsEmpty());
+
+  ASSERT_SUCCEEDED(RegSplitKeyvalueName(CString(L"HKLM\\Foo\\(default)"),
+                                        &key_name,
+                                        &value_name));
+  ASSERT_STREQ(key_name, L"HKLM\\Foo");
+  ASSERT_TRUE(value_name.IsEmpty());
+
+  ASSERT_SUCCEEDED(RegSplitKeyvalueName(CString(L"HKLM\\Foo\\Bar"),
+                                        &key_name,
+                                        &value_name));
+  ASSERT_STREQ(key_name, L"HKLM\\Foo");
+  ASSERT_STREQ(value_name, L"Bar");
+}
+
+TEST(UtilsTest, ExpandEnvLikeStrings) {
+  std::map<CString, CString> mapping;
+  ASSERT_SUCCEEDED(Shell::GetSpecialFolderKeywordsMapping(&mapping));
+
+  CString out;
+  ASSERT_SUCCEEDED(ExpandEnvLikeStrings(
+      L"Foo%WINDOWS%Bar%SYSTEM%Zebra%WINDOWS%%SYSTEM%", mapping, &out));
+
+  // This should work, but CmpHelperSTRCASEEQ is not overloaded for wchars.
+  // ASSERT_STRCASEEQ(out, L"FooC:\\WINDOWSBarC:\\WINDOWS\\system32Zebra"
+  //                       L"C:\\WINDOWSC:\\WINDOWS\\system32");
+  ASSERT_EQ(out.CompareNoCase(L"FooC:\\WINDOWSBarC:\\WINDOWS\\system32Zebra"
+                              L"C:\\WINDOWSC:\\WINDOWS\\system32"),
+            0);
+  ASSERT_FAILED(ExpandEnvLikeStrings(L"Foo%WINDOWS%%BAR%Zebra", mapping, &out));
+}
+
+TEST(UtilsTest, GetCurrentProcessHandle) {
+  scoped_process proc;
+  ASSERT_SUCCEEDED(GetCurrentProcessHandle(address(proc)));
+  ASSERT_TRUE(valid(proc));
+}
+
+TEST(UtilsTest, DuplicateTokenIntoCurrentProcess) {
+  CAccessToken process_token;
+  EXPECT_TRUE(process_token.GetProcessToken(TOKEN_ALL_ACCESS));
+
+  CAccessToken duplicated_token;
+  EXPECT_SUCCEEDED(DuplicateTokenIntoCurrentProcess(::GetCurrentProcess(),
+                                                    process_token.GetHandle(),
+                                                    &duplicated_token));
+
+  CSid process_sid;
+  EXPECT_TRUE(process_token.GetUser(&process_sid));
+
+  CSid duplicated_sid;
+  EXPECT_TRUE(duplicated_token.GetUser(&duplicated_sid));
+
+  EXPECT_STREQ(process_sid.Sid(), duplicated_sid.Sid());
+}
+
+TEST(UtilsTest, IsGuid) {
+  EXPECT_FALSE(IsGuid(NULL));
+  EXPECT_FALSE(IsGuid(_T("")));
+  EXPECT_FALSE(IsGuid(_T("{}")));
+  EXPECT_FALSE(IsGuid(_T("a")));
+  EXPECT_FALSE(IsGuid(_T("CA3045BFA6B14fb8A0EFA615CEFE452C")));
+
+  // Missing {}
+  EXPECT_FALSE(IsGuid(_T("CA3045BF-A6B1-4fb8-A0EF-A615CEFE452C")));
+
+  // Invalid char X
+  EXPECT_FALSE(IsGuid(_T("{XA3045BF-A6B1-4fb8-A0EF-A615CEFE452C}")));
+
+  // Invalid binary char 0x200
+  EXPECT_FALSE(IsGuid(_T("{\0x200a3045bf-a6b1-4fb8-a0ef-a615cefe452c}")));
+
+  // Missing -
+  EXPECT_FALSE(IsGuid(_T("{CA3045BFA6B14fb8A0EFA615CEFE452C}")));
+
+  // Double quotes
+  EXPECT_FALSE(IsGuid(_T("\"{ca3045bf-a6b1-4fb8-a0ef-a615cefe452c}\"")));
+
+  EXPECT_TRUE(IsGuid(_T("{00000000-0000-0000-0000-000000000000}")));
+  EXPECT_TRUE(IsGuid(_T("{CA3045BF-A6B1-4fb8-A0EF-A615CEFE452C}")));
+  EXPECT_TRUE(IsGuid(_T("{ca3045bf-a6b1-4fb8-a0ef-a615cefe452c}")));
+}
+
+// GUIDs cannot be compared in GTest because there is no << operator. Therefore,
+// we must treat them as strings. All these tests rely on GuidToString working.
+#define EXPECT_GUID_EQ(expected, actual) \
+    EXPECT_STREQ(GuidToString(expected), GuidToString(actual))
+
+TEST(UtilsTest, StringToGuidSafe_InvalidString) {
+  GUID guid = {0};
+
+  EXPECT_EQ(E_INVALIDARG, StringToGuidSafe(_T(""), &guid));
+  EXPECT_EQ(E_INVALIDARG, StringToGuidSafe(_T("{}"), &guid));
+  EXPECT_EQ(E_INVALIDARG, StringToGuidSafe(_T("a"), &guid));
+  EXPECT_EQ(E_INVALIDARG,
+      StringToGuidSafe(_T("CA3045BFA6B14fb8A0EFA615CEFE452C"), &guid));
+
+  // Missing {}
+  EXPECT_EQ(E_INVALIDARG,
+      StringToGuidSafe(_T("CA3045BF-A6B1-4fb8-A0EF-A615CEFE452C"), &guid));
+
+  // Invalid char X
+  EXPECT_EQ(CO_E_IIDSTRING,
+      StringToGuidSafe(_T("{XA3045BF-A6B1-4fb8-A0EF-A615CEFE452C}"), &guid));
+
+  // Invalid binary char 0x200
+  EXPECT_EQ(E_INVALIDARG,
+            StringToGuidSafe(_T("{\0x200a3045bf-a6b1-4fb8-a0ef-a615cefe452c}"),
+                             &guid));
+
+  // Missing -
+  EXPECT_EQ(E_INVALIDARG,
+            StringToGuidSafe(_T("{CA3045BFA6B14fb8A0EFA615CEFE452C}"), &guid));
+
+  // Double quotes
+  EXPECT_EQ(E_INVALIDARG,
+            StringToGuidSafe(_T("\"{ca3045bf-a6b1-4fb8-a0ef-a615cefe452c}\""),
+                             &guid));
+}
+
+TEST(UtilsTest, StringToGuidSafe_ValidString) {
+  const GUID kExpectedGuid = {0xCA3045BF, 0xA6B1, 0x4FB8,
+                              {0xA0, 0xEF, 0xA6, 0x15, 0xCE, 0xFE, 0x45, 0x2C}};
+  GUID guid = kExpectedGuid;
+
+  EXPECT_SUCCEEDED(
+      StringToGuidSafe(_T("{00000000-0000-0000-0000-000000000000}"), &guid));
+  EXPECT_GUID_EQ(GUID_NULL, guid);
+
+  guid = GUID_NULL;
+  EXPECT_SUCCEEDED(
+      StringToGuidSafe(_T("{CA3045BF-A6B1-4fb8-A0EF-A615CEFE452C}"), &guid));
+  EXPECT_GUID_EQ(kExpectedGuid, guid);
+
+  guid = GUID_NULL;
+  EXPECT_SUCCEEDED(
+      StringToGuidSafe(_T("{ca3045bf-a6b1-4fb8-a0ef-a615cefe452c}"), &guid));
+  EXPECT_GUID_EQ(kExpectedGuid, guid);
+}
+
+TEST(UtilsTest, VersionFromString_ValidVersion) {
+  EXPECT_EQ(MAKEDLLVERULL(42, 1, 21, 12345),
+            VersionFromString(_T("42.1.21.12345")));
+}
+
+TEST(UtilsTest, VersionFromString_VersionZero) {
+  EXPECT_EQ(0, VersionFromString(_T("0.0.0.0")));
+}
+
+TEST(UtilsTest, VersionFromString_VersionUpperLimits) {
+  EXPECT_EQ(MAKEDLLVERULL(0xffff, 0xffff, 0xffff, 0xffff),
+            VersionFromString(_T("65535.65535.65535.65535")));
+  EXPECT_EQ(0, VersionFromString(_T("65536.65536.65536.65536")));
+  EXPECT_EQ(0, VersionFromString(_T("1.2.65536.65536")));
+}
+
+TEST(UtilsTest, VersionFromString_IntegerOverflow) {
+  EXPECT_EQ(0, VersionFromString(_T("1.2.3.4294967296")));
+}
+
+TEST(UtilsTest, VersionFromString_NegativeVersion) {
+  EXPECT_EQ(0, VersionFromString(_T("1.2.3.-22")));
+}
+
+TEST(UtilsTest, VersionFromString_TooFewElements) {
+  EXPECT_EQ(0, VersionFromString(_T("1.1.1")));
+}
+
+TEST(UtilsTest, VersionFromString_ExtraPeriod) {
+  EXPECT_EQ(0, VersionFromString(_T("1.1.2.3.")));
+}
+
+TEST(UtilsTest, VersionFromString_TooManyElements) {
+  EXPECT_EQ(0, VersionFromString(_T("1.1.2.3.4")));
+}
+
+TEST(UtilsTest, VersionFromString_Char) {
+  EXPECT_EQ(0, VersionFromString(_T("1.B.3.4")));
+  EXPECT_EQ(0, VersionFromString(_T("1.2.3.B")));
+  EXPECT_EQ(0, VersionFromString(_T("1.2.3.9B")));
+}
+
+TEST(UtilsTest, StringFromVersion_ValidVersion) {
+  EXPECT_STREQ(_T("42.1.21.12345"),
+               StringFromVersion(MAKEDLLVERULL(42, 1, 21, 12345)));
+}
+
+TEST(UtilsTest, StringFromVersion_VersionZero) {
+  EXPECT_STREQ(_T("0.0.0.0"), StringFromVersion(0));
+}
+
+TEST(UtilsTest, StringFromVersion_VersionUpperLimits) {
+  EXPECT_STREQ(
+      _T("65535.65535.65535.65535"),
+      StringFromVersion(MAKEDLLVERULL(0xffff, 0xffff, 0xffff, 0xffff)));
+}
+
+TEST(UtilsTest, IsLocalSystemSid) {
+  EXPECT_TRUE(IsLocalSystemSid(kLocalSystemSid));
+  EXPECT_TRUE(IsLocalSystemSid(_T("S-1-5-18")));
+  EXPECT_TRUE(IsLocalSystemSid(_T("s-1-5-18")));
+
+  EXPECT_FALSE(IsLocalSystemSid(_T("")));
+  EXPECT_FALSE(IsLocalSystemSid(_T("S-1-5-17")));
+}
+
+// There is a very small probability the test could fail.
+TEST(UtilsTest, GenRandom) {
+  int random_int = 0;
+  EXPECT_TRUE(GenRandom(&random_int, sizeof(random_int)));
+  EXPECT_NE(random_int, 0);
+
+  int another_random_int = 0;
+  EXPECT_TRUE(GenRandom(&another_random_int, sizeof(another_random_int)));
+  EXPECT_NE(another_random_int, 0);
+
+  EXPECT_NE(random_int, another_random_int);
+}
+
+// Counts instances of the class.
+class Counter {
+ public:
+  Counter() {
+    ++instance_count_;
+  }
+  ~Counter() {
+    --instance_count_;
+  }
+  static int instance_count() { return instance_count_; }
+ private:
+  static int instance_count_;
+  DISALLOW_EVIL_CONSTRUCTORS(Counter);
+};
+
+int Counter::instance_count_ = 0;
+
+// Checks if the functor is actually calling the destructor of the type.
+TEST(UtilsTest, DeleteFun) {
+  EXPECT_EQ(Counter::instance_count(), 0);
+  Counter* counter = new Counter;
+  EXPECT_EQ(Counter::instance_count(), 1);
+  DeleteFun().operator()(counter);
+  EXPECT_EQ(Counter::instance_count(), 0);
+
+  // Checks if the template can be instantiated for some common built in types.
+  int* pointer_int = NULL;
+  DeleteFun().operator()(pointer_int);
+
+  const char* pointer_char = NULL;
+  DeleteFun().operator()(pointer_char);
+}
+
+TEST(UtilsTest, IsUserLoggedOn) {
+  bool is_logged_on(false);
+  ASSERT_HRESULT_SUCCEEDED(IsUserLoggedOn(&is_logged_on));
+  ASSERT_TRUE(is_logged_on);
+}
+
+TEST(UtilsTest, IsClickOnceDisabled) {
+  EXPECT_FALSE(IsClickOnceDisabled());
+}
+
+TEST(UtilsTest, ConfigureRunAtStartup) {
+  const TCHAR kRunKeyPath[] =
+      _T("HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run");
+
+  RegKey::DeleteKey(kRegistryHiveOverrideRoot, true);
+  OverrideRegistryHives(kRegistryHiveOverrideRoot);
+
+  EXPECT_FALSE(RegKey::HasKey(kRunKeyPath));
+
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            ConfigureRunAtStartup(USER_KEY_NAME, _T("FooApp"),
+                                  _T("\"foo.exe\""), false));
+  EXPECT_FALSE(RegKey::HasKey(kRunKeyPath));
+
+  EXPECT_SUCCEEDED(ConfigureRunAtStartup(USER_KEY_NAME, _T("FooApp"),
+                                         _T("\"C:\\foo.exe\" /x"), true));
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kRunKeyPath, _T("FooApp"), &value));
+  EXPECT_STREQ(_T("\"C:\\foo.exe\" /x"), value);
+
+  EXPECT_SUCCEEDED(ConfigureRunAtStartup(USER_KEY_NAME, _T("FooApp"),
+                                         _T("\"foo.exe\""), false));
+  EXPECT_FALSE(RegKey::HasValue(kRunKeyPath, _T("FooApp")));
+  EXPECT_TRUE(RegKey::HasKey(kRunKeyPath));
+
+  RestoreRegistryHives();
+  EXPECT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
+}
+
+TEST(UtilsTest, ValidPath) {
+  CString cmd_line =
+      _T("\"C:\\Program Files\\Internet Explorer\\iexplore.exe\" -nohome");
+  CString exe_path;
+  EXPECT_SUCCEEDED(GetExePathFromCommandLine(cmd_line, &exe_path));
+  EXPECT_STREQ(_T("C:\\Program Files\\Internet Explorer\\iexplore.exe"),
+               exe_path);
+}
+
+TEST(UtilsTest, InvalidPath) {
+  CString cmd_line = _T("");
+  CString exe_path;
+  EXPECT_FAILED(GetExePathFromCommandLine(cmd_line, &exe_path));
+  EXPECT_TRUE(exe_path.IsEmpty());
+}
+
+TEST(UtilsTest, PinModuleIntoProcess) {
+  const TCHAR module_name[] = _T("icmp.dll");
+  const void* kNullModule = NULL;
+
+  // The module should not be loaded at this time.
+  EXPECT_EQ(kNullModule, ::GetModuleHandle(module_name));
+
+  // Loads and unloads the module.
+  {
+    scoped_library module(::LoadLibrary(module_name));
+    EXPECT_TRUE(module);
+    EXPECT_NE(kNullModule, ::GetModuleHandle(module_name));
+  }
+  EXPECT_EQ(kNullModule, ::GetModuleHandle(module_name));
+
+  // Loads, pins, and unloads the module.
+  {
+    scoped_library module(::LoadLibrary(module_name));
+    EXPECT_TRUE(module);
+    EXPECT_NE(kNullModule, ::GetModuleHandle(module_name));
+    PinModuleIntoProcess(module_name);
+  }
+  EXPECT_NE(kNullModule, ::GetModuleHandle(module_name));
+}
+
+// Assumes Windows is installed on the C: drive.
+TEST(UtilsTest, GetEnvironmentVariableAsString) {
+  EXPECT_STREQ(_T("C:"), GetEnvironmentVariableAsString(_T("SystemDrive")));
+  EXPECT_STREQ(_T("Windows_NT"), GetEnvironmentVariableAsString(_T("OS")));
+  EXPECT_STREQ(_T(""), GetEnvironmentVariableAsString(_T("FOO")));
+}
+
+TEST(UtilsTest, IsWindowsInstalling_Normal) {
+  EXPECT_FALSE(IsWindowsInstalling());
+}
+
+TEST(UtilsTest, IsWindowsInstalling_Installing_Vista_InvalidValues) {
+  if (!vista_util::IsVistaOrLater()) {
+    return;
+  }
+
+  RegKey::DeleteKey(kRegistryHiveOverrideRoot, true);
+  OverrideRegistryHives(kRegistryHiveOverrideRoot);
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      _T("")));
+  EXPECT_FALSE(IsWindowsInstalling());
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      _T("foo")));
+  EXPECT_FALSE(IsWindowsInstalling());
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      static_cast<DWORD>(1)));
+  ExpectAsserts expect_asserts;  // RegKey asserts because value type is wrong.
+  EXPECT_FALSE(IsWindowsInstalling());
+
+  RestoreRegistryHives();
+  EXPECT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
+}
+
+TEST(UtilsTest, IsWindowsInstalling_Installing_Vista_ValidStates) {
+  if (!vista_util::IsVistaOrLater()) {
+    return;
+  }
+
+  RegKey::DeleteKey(kRegistryHiveOverrideRoot, true);
+  OverrideRegistryHives(kRegistryHiveOverrideRoot);
+
+  // These states return false in the original implementation.
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      _T("IMAGE_STATE_COMPLETE")));
+  EXPECT_FALSE(IsWindowsInstalling());
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      _T("IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE")));
+  EXPECT_FALSE(IsWindowsInstalling());
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      _T("IMAGE_STATE_SPECIALIZE_RESEAL_TO_OOBE")));
+  EXPECT_FALSE(IsWindowsInstalling());
+
+  // These states are specified in the original implementation.
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      _T("IMAGE_STATE_UNDEPLOYABLE")));
+  EXPECT_TRUE(IsWindowsInstalling());
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      _T("IMAGE_STATE_GENERALIZE_RESEAL_TO_AUDIT")));
+  EXPECT_TRUE(IsWindowsInstalling());
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      _T("IMAGE_STATE_SPECIALIZE_RESEAL_TO_AUDIT")));
+  EXPECT_TRUE(IsWindowsInstalling());
+
+  RestoreRegistryHives();
+  EXPECT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
+}
+
+TEST(UtilsTest, AddAllowedAce) {
+  CString test_file_path = ConcatenatePath(
+      app_util::GetCurrentModuleDirectory(), _T("TestAddAllowedAce.exe"));
+  EXPECT_SUCCEEDED(File::Remove(test_file_path));
+
+  EXPECT_SUCCEEDED(File::Copy(
+      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                      _T("GoogleUpdate.exe")),
+      test_file_path,
+      false));
+
+  CDacl dacl;
+  EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl));
+  const int original_ace_count = dacl.GetAceCount();
+
+  EXPECT_SUCCEEDED(AddAllowedAce(test_file_path,
+                                 SE_FILE_OBJECT,
+                                 Sids::Dialup(),
+                                 FILE_GENERIC_READ,
+                                 0));
+
+  dacl.SetEmpty();
+  EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl));
+  EXPECT_EQ(original_ace_count + 1, dacl.GetAceCount());
+
+  // Add the same access. No ACE is added.
+  EXPECT_SUCCEEDED(AddAllowedAce(test_file_path,
+                                 SE_FILE_OBJECT,
+                                 Sids::Dialup(),
+                                 FILE_GENERIC_READ,
+                                 0));
+  dacl.SetEmpty();
+  EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl));
+  EXPECT_EQ(original_ace_count + 1, dacl.GetAceCount());
+
+  // Add a subset of the existing access. No ACE is added.
+  EXPECT_EQ(FILE_READ_ATTRIBUTES, FILE_GENERIC_READ & FILE_READ_ATTRIBUTES);
+  EXPECT_SUCCEEDED(AddAllowedAce(test_file_path,
+                                 SE_FILE_OBJECT,
+                                 Sids::Dialup(),
+                                 FILE_READ_ATTRIBUTES,
+                                 0));
+  dacl.SetEmpty();
+  EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl));
+  EXPECT_EQ(original_ace_count + 1, dacl.GetAceCount());
+
+  // Add more access. An ACE is added.
+  EXPECT_SUCCEEDED(AddAllowedAce(test_file_path,
+                                 SE_FILE_OBJECT,
+                                 Sids::Dialup(),
+                                 FILE_ALL_ACCESS,
+                                 0));
+  dacl.SetEmpty();
+  EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl));
+  EXPECT_EQ(original_ace_count + 2, dacl.GetAceCount());
+
+  // TODO(omaha): An assert occurs because the ACE flags are being used on a
+  // file object. Add a new test to use a registry key.
+  ExpectAsserts expect_asserts;
+
+  // Different ACE flags. An ACE is added.
+  const BYTE kTestAce = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
+  const BYTE kTestAceSubset = CONTAINER_INHERIT_ACE;
+  EXPECT_SUCCEEDED(AddAllowedAce(test_file_path,
+                                 SE_FILE_OBJECT,
+                                 Sids::Dialup(),
+                                 FILE_ALL_ACCESS,
+                                 kTestAce));
+  dacl.SetEmpty();
+  EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl));
+  EXPECT_EQ(original_ace_count + 3, dacl.GetAceCount());
+
+  // Subset of existing ACE flags. An ACE is added because flags must be exact.
+  EXPECT_SUCCEEDED(AddAllowedAce(test_file_path,
+                                 SE_FILE_OBJECT,
+                                 Sids::Dialup(),
+                                 FILE_ALL_ACCESS,
+                                 kTestAceSubset));
+  dacl.SetEmpty();
+  EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl));
+  EXPECT_EQ(original_ace_count + 4, dacl.GetAceCount());
+
+  // Same flags. An ACE should not be added because all values match.
+  // TODO(omaha): This does not work, possibly because the object is a file.
+  // Try the test using a registry key.
+  EXPECT_SUCCEEDED(AddAllowedAce(test_file_path,
+                                 SE_FILE_OBJECT,
+                                 Sids::Dialup(),
+                                 FILE_ALL_ACCESS,
+                                 kTestAceSubset));
+  dacl.SetEmpty();
+  EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl));
+  EXPECT_EQ(original_ace_count + 5, dacl.GetAceCount());
+
+  EXPECT_SUCCEEDED(File::Remove(test_file_path));
+}
+
+TEST(UtilsTest, CreateForegroundParentWindowForUAC) {
+  CWindow foreground_parent;
+  foreground_parent.Attach(CreateForegroundParentWindowForUAC());
+  EXPECT_TRUE(foreground_parent.IsWindow());
+  EXPECT_TRUE(foreground_parent.IsWindowVisible());
+
+  CRect foreground_rect;
+  EXPECT_TRUE(foreground_parent.GetWindowRect(&foreground_rect));
+  EXPECT_EQ(0, foreground_rect.Width());
+  EXPECT_EQ(0, foreground_rect.Height());
+
+  EXPECT_TRUE((WS_POPUP | WS_VISIBLE) & foreground_parent.GetStyle());
+  EXPECT_TRUE(WS_EX_TOOLWINDOW & foreground_parent.GetExStyle());
+
+  EXPECT_TRUE(foreground_parent.DestroyWindow());
+}
+
+// Test the atomic exchange of pointer values.
+TEST(UtilsTest, interlocked_exchange_pointer) {
+  const int i = 10;
+  const int j = 20;
+
+  const int* volatile pi = &i;
+  const int* pj = &j;
+
+  const int* old_pi = pi;
+
+  // pi and pj point to i and j respectively.
+  EXPECT_EQ(10, *pi);
+  EXPECT_EQ(20, *pj);
+
+  // After the exchange pi<-pj, both pointers point to the same value, in this
+  // case j.
+  int* result = interlocked_exchange_pointer(const_cast<int**>(&pi), pj);
+  EXPECT_EQ(*pj, *pi);
+  EXPECT_EQ(old_pi, result);
+  EXPECT_EQ(10, *old_pi);
+  EXPECT_EQ(20, *pi);
+
+  // Exchanging a pointer with self is idempotent.
+  old_pi = interlocked_exchange_pointer(const_cast<int**>(&pi), pi);
+  EXPECT_EQ(pi, old_pi);
+  EXPECT_EQ(20, *pi);
+
+  // Exchanging a pointer with NULL.
+  interlocked_exchange_pointer(const_cast<int**>(&pi), static_cast<int*>(NULL));
+  EXPECT_EQ(NULL, pi);
+}
+
+TEST(UtilsTest, GetGuid)  {
+  CString guid;
+  EXPECT_HRESULT_SUCCEEDED(GetGuid(&guid));
+
+  // ATL regexp has many problems including:
+  // * not supporting {n} to repeat a previous item n times.
+  // * not allowing matching on - unless the items around the dash are
+  // enclosed in {}.
+  AtlRE guid_regex(_T("^{\\{{\\h\\h\\h\\h\\h\\h\\h\\h}-{\\h\\h\\h\\h}-{\\h\\h\\h\\h}-{\\h\\h\\h\\h}-{\\h\\h\\h\\h\\h\\h\\h\\h\\h\\h\\h\\h}\\}}$"));   // NOLINT
+
+  CString matched_guid;
+  EXPECT_TRUE(AtlRE::PartialMatch(guid, guid_regex, &matched_guid));
+  EXPECT_STREQ(guid, matched_guid);
+
+  // Missing {}.
+  guid = _T("5F5280C6-9674-429b-9FEB-551914EF96B8");
+  EXPECT_FALSE(AtlRE::PartialMatch(guid, guid_regex));
+
+  // Missing -.
+  guid = _T("{5F5280C6.9674-429b-9FEB-551914EF96B8}");
+  EXPECT_FALSE(AtlRE::PartialMatch(guid, guid_regex));
+
+  // Whitespaces.
+  guid = _T(" {5F5280C6.9674-429b-9FEB-551914EF96B8}");
+  EXPECT_FALSE(AtlRE::PartialMatch(guid, guid_regex));
+
+  guid = _T("{5F5280C6.9674-429b-9FEB-551914EF96B8} ");
+  EXPECT_FALSE(AtlRE::PartialMatch(guid, guid_regex));
+
+  // Empty string.
+  guid = _T("");
+  EXPECT_FALSE(AtlRE::PartialMatch(guid, guid_regex));
+}
+
+TEST(UtilsTest, GetMessageForSystemErrorCode) {
+  CString message = GetMessageForSystemErrorCode(99);
+  EXPECT_TRUE(message.IsEmpty());
+
+  message = GetMessageForSystemErrorCode(ERROR_TOO_MANY_SEMAPHORES);
+  EXPECT_FALSE(message.IsEmpty());
+}
+
+TEST(UtilsTest, CeilingDivide) {
+  EXPECT_EQ(0, CeilingDivide(0, 1));
+  EXPECT_EQ(1, CeilingDivide(1, 1));
+  EXPECT_EQ(1, CeilingDivide(1, 2));
+  EXPECT_EQ(2, CeilingDivide(6, 3));
+  EXPECT_EQ(4, CeilingDivide(7, 2));
+}
+
+}  // namespace omaha
+
diff --git a/base/vista_utils.cc b/base/vista_utils.cc
new file mode 100644
index 0000000..8a21507
--- /dev/null
+++ b/base/vista_utils.cc
@@ -0,0 +1,483 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/vista_utils.h"
+
+#include <vector>
+#include "base/scoped_ptr.h"
+#include "omaha/base/commontypes.h"
+#include "omaha/base/const_utils.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/proc_utils.h"
+#include "omaha/base/process.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/smart_handle.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/system.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/user_rights.h"
+#include "omaha/base/utils.h"
+
+#define LOW_INTEGRITY_SDDL_SACL_A     NOTRANSL("S:(ML;;NW;;;LW)")
+#define LOW_INTEGRITY_SID_W           NOTRANSL(L"S-1-16-4096")
+
+namespace omaha {
+
+namespace vista {
+
+namespace {
+
+// TODO(Omaha): Unit test for this method.
+HRESULT RunAsUser(const CString& command_line,
+                  HANDLE user_token,
+                  bool run_as_current_user) {
+  if (INVALID_HANDLE_VALUE == user_token) {
+    return E_INVALIDARG;
+  }
+
+  CString cmd(command_line);
+
+  STARTUPINFO startup_info = { sizeof(startup_info) };
+  PROCESS_INFORMATION process_info = {0};
+
+  DWORD creation_flags(0);
+  void* environment_block(NULL);
+  ON_SCOPE_EXIT(::DestroyEnvironmentBlock, environment_block);
+  if (::CreateEnvironmentBlock(&environment_block, user_token, FALSE)) {
+    creation_flags |= CREATE_UNICODE_ENVIRONMENT;
+  } else {
+    ASSERT(false, (_T("::CreateEnvironmentBlock failed %d"), ::GetLastError()));
+    environment_block = NULL;
+  }
+
+  // ::CreateProcessAsUser() does not work unless the caller is SYSTEM. Does not
+  // matter if the user token is for the current user.
+  BOOL success = run_as_current_user ?
+      ::CreateProcess(0, CStrBuf(cmd, MAX_PATH), 0, 0, false, creation_flags,
+                      environment_block, 0, &startup_info, &process_info) :
+      ::CreateProcessAsUser(user_token, 0, CStrBuf(cmd, MAX_PATH), 0, 0, false,
+                            creation_flags, environment_block, 0, &startup_info,
+                            &process_info);
+
+  if (!success) {
+    HRESULT hr(HRESULTFromLastError());
+    UTIL_LOG(LE, (_T("[RunAsUser failed][cmd=%s][hresult=0x%x]"), cmd, hr));
+    return hr;
+  }
+
+  VERIFY1(::CloseHandle(process_info.hThread));
+  VERIFY1(::CloseHandle(process_info.hProcess));
+
+  return S_OK;
+}
+
+}  // namespace
+
+bool IsProcessProtected() {
+  if (!SystemInfo::IsRunningOnVistaOrLater()) {
+    return false;
+  }
+
+  AutoHandle token;
+  VERIFY1(::OpenProcessToken(GetCurrentProcess(),
+                             TOKEN_QUERY | TOKEN_QUERY_SOURCE,
+                             &token.receive()));
+
+  // Get the Integrity level.
+  DWORD length_needed;
+  BOOL b = ::GetTokenInformation(token,
+                                 TokenIntegrityLevel,
+                                 NULL,
+                                 0,
+                                 &length_needed);
+  ASSERT1(b == FALSE);
+  if (b) {
+    return false;
+  }
+
+  // The first call to GetTokenInformation is just to get the buffer size
+  if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+    return false;
+  }
+
+  scoped_ptr<TOKEN_MANDATORY_LABEL> integration_level;
+
+  integration_level.reset(reinterpret_cast<TOKEN_MANDATORY_LABEL*>(
+      new char[length_needed]));
+  if (integration_level.get() == NULL) {
+    return false;
+  }
+
+  if (!::GetTokenInformation(token,
+                             TokenIntegrityLevel,
+                             integration_level.get(),
+                             length_needed,
+                             &length_needed)) {
+    return false;
+  }
+
+  wchar_t* sid_str = NULL;
+  VERIFY1(::ConvertSidToStringSid(integration_level->Label.Sid, &sid_str));
+  bool ret = ::lstrcmpW(sid_str, LOW_INTEGRITY_SID_W) == 0;
+  ::LocalFree(sid_str);
+
+  return ret;
+}
+
+HRESULT AllowProtectedProcessAccessToSharedObject(const TCHAR* name) {
+  if (!SystemInfo::IsRunningOnVistaOrLater()) {
+    return S_FALSE;
+  }
+
+  ASSERT1(name != NULL);
+
+  PSECURITY_DESCRIPTOR psd = NULL;
+  VERIFY1(::ConvertStringSecurityDescriptorToSecurityDescriptorA(
+              LOW_INTEGRITY_SDDL_SACL_A,
+              SDDL_REVISION_1,
+              &psd,
+              NULL));
+
+  BOOL sacl_present = FALSE;
+  BOOL sacl_defaulted = FALSE;
+  PACL sacl = NULL;
+  VERIFY1(::GetSecurityDescriptorSacl(psd,
+                                      &sacl_present,
+                                      &sacl,
+                                      &sacl_defaulted));
+
+  DWORD ret = ::SetNamedSecurityInfoW(const_cast<TCHAR*>(name),
+                                      SE_KERNEL_OBJECT,
+                                      LABEL_SECURITY_INFORMATION,
+                                      NULL,
+                                      NULL,
+                                      NULL,
+                                      sacl);
+
+  ::LocalFree(psd);
+
+  return HRESULT_FROM_WIN32(ret);
+}
+
+HRESULT RunAsCurrentUser(const CString& command_line) {
+  scoped_handle token;
+  if (!::OpenProcessToken(::GetCurrentProcess(),
+                          TOKEN_QUERY | TOKEN_DUPLICATE,
+                          address(token))) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[RunAsCurrentUser: OpenProcessToken failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return RunAsUser(command_line, get(token), true);
+}
+
+static HRESULT StartInternetExplorerAsUser(HANDLE user_token,
+                                           const CString& options) {
+  // Internet Explorer path
+  CString ie_file_path;
+  HRESULT result = RegKey::GetValue(kRegKeyIeClass,
+                                    kRegValueIeClass,
+                                    &ie_file_path);
+  ASSERT1(SUCCEEDED(result));
+
+  if (SUCCEEDED(result)) {
+    CString command_line(ie_file_path);
+    command_line += _T(' ');
+    command_line += options;
+    UTIL_LOG(L5, (_T("[StartInternetExplorerAsUser]")
+                  _T("[Running IExplore with command line][%s]"),
+                  command_line));
+    result = RunAsUser(command_line, user_token, false);
+  }
+  return result;
+}
+
+//
+// Constants used by RestartIEUser()
+//
+// The IEUser executable name
+const TCHAR* kIEUser = _T("IEUSER.EXE");
+
+// The maximum number of simultaneous
+// logged on users in FUS that we support
+const int kMaximumUsers = 16;
+
+
+// Restart IEUser processs. This is to allow for
+// IEUser.exe to refresh it's ElevationPolicy cache. Due to a bug
+// within IE7, IEUser.exe does not refresh it's cache unless it
+// is restarted in the manner below. If the cache is not refreshed
+// IEUser does not respect any new ElevationPolicies that a fresh
+// setup program installs for an ActiveX control or BHO. This code
+// is adapted from Toolbar.
+HRESULT RestartIEUser() {
+  // Use the service to restart IEUser.
+  // This allows us to restart IEUser for:
+  //   (a) Multiple users for the first-install case
+  //       (we currently only restart IEUser for the current interactive user)
+  //   (b) Even if we are started in an elevated mode
+
+  if (!SystemInfo::IsRunningOnVistaOrLater()) {
+    UTIL_LOG(L5, (_T("[RestartIEUser - not running on Vista - Exiting]")));
+    return S_OK;
+  }
+
+  // The restart should be attempted from the system account
+  bool is_system_process = false;
+  if (FAILED(IsSystemProcess(&is_system_process)) || !is_system_process) {
+    ASSERT1(false);
+    return E_ACCESSDENIED;
+  }
+
+  // Get the list of users currently running IEUser.exe processes.
+  scoped_handle ieuser_users[kMaximumUsers];
+  int number_of_users = 0;
+  Process::GetUsersOfProcesses(kIEUser, kMaximumUsers, ieuser_users,
+                               &number_of_users);
+
+  UTIL_LOG(L5, (_T("[RestartIEUser]")
+                _T("[number_of_users running IEUser %d]"), number_of_users));
+
+  if (!number_of_users) {
+    UTIL_LOG(L5, (_T("[RestartIEUser][No IEUser processes running]")));
+    return S_OK;
+  }
+
+  // Kill current IEUser processes.
+  ProcessTerminator pt(kIEUser);
+  const int kKillWaitTimeoutMs = 5000;
+  bool found = false;
+  const int kill_method = (ProcessTerminator::KILL_METHOD_4_TERMINATE_PROCESS);
+
+  RET_IF_FAILED(pt.KillTheProcess(kKillWaitTimeoutMs,
+                                  &found,
+                                  kill_method,
+                                  false));
+
+  // Restart them.
+  HRESULT result = S_OK;
+  for (int i = 0; i < number_of_users; i++) {
+    // To start a new ieuser.exe, simply start iexplore.exe as a normal user
+    // The -embedding prevents IE from opening a window
+    HRESULT restart_result = StartInternetExplorerAsUser(get(ieuser_users[i]),
+                                                         _T("-embedding"));
+    if (FAILED(restart_result)) {
+      UTIL_LOG(LEVEL_ERROR, (_T("[StartInternetExplorerAsUser failed][0x%x]"),
+                             restart_result));
+      result = restart_result;
+    }
+  }
+
+  return result;
+}
+
+HRESULT GetExplorerPidForCurrentUserOrSession(uint32* pid) {
+  ASSERT1(pid);
+  std::vector<uint32> pids;
+  HRESULT hr = GetProcessPidsForActiveUserOrSession(kExplorer, &pids);
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[Did not find explorer.exe processes][0x%x]"), hr));
+    return hr;
+  }
+
+  CORE_LOG(L1, (_T("[Found %u instance(s) of explorer.exe]"), pids.size()));
+
+  *pid = pids[0];   // Return only the first instance of explorer.exe.
+  return S_OK;
+}
+
+HRESULT GetExplorerTokenForLoggedInUser(HANDLE* token) {
+  UTIL_LOG(L3, (_T("[GetExplorerTokenForLoggedInUser]")));
+  ASSERT1(token);
+
+  // TODO(omaha): One can set the windows shell to be other than
+  // explorer.exe, handle this case. One way to handle this is to
+  // read the regkey
+  // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon
+  // The only problem with this is it can be overriden with the user reg keys.
+  // Need to figure out a method to do this.
+  // Also consider using the interactive user before picking the first explorer
+  // process i.e. the active user.
+  std::vector<uint32> processes;
+  DWORD flags = EXCLUDE_CURRENT_PROCESS;
+  std::vector<CString> command_lines;
+  CString explorer_file_name(kExplorer);
+  CString user_sid;
+
+  HRESULT hr = Process::FindProcesses(flags,
+                                      explorer_file_name,
+                                      true,
+                                      user_sid,
+                                      command_lines,
+                                      &processes);
+  if (FAILED(hr)) {
+    CORE_LOG(LEVEL_ERROR, (_T("[FindProcesses failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  std::vector<uint32>::const_iterator iter = processes.begin();
+  for (; iter != processes.end(); ++iter) {
+    uint32 explorer_pid = *iter;
+    scoped_handle exp(::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
+                                    false,
+                                    explorer_pid));
+    if (exp) {
+      if (::OpenProcessToken(get(exp),
+                             TOKEN_DUPLICATE | TOKEN_QUERY | TOKEN_IMPERSONATE,
+                             token)) {
+        // TODO(omaha): Consider using the GetWindowsAccountDomainSid
+        // method here. This method returns the domain SID associated
+        // with the passed in SID. This allows us to detect if the user is a
+        // domain user. We should prefer domain users over normal users,
+        // as in corporate environments, these users will be more likely to
+        // allow to be tunneled through a proxy.
+        return S_OK;
+      } else {
+        hr = HRESULTFromLastError();
+        CORE_LOG(LEVEL_WARNING, (_T("[OpenProcessToken failed][0x%08x]"), hr));
+      }
+    } else {
+      hr = HRESULTFromLastError();
+      CORE_LOG(LEVEL_WARNING, (_T("[OpenProcess failed][0x%08x]"), hr));
+    }
+  }
+
+  return hr;
+}
+
+HRESULT GetPidsInSession(const TCHAR* exe_name,
+                         const TCHAR* user_sid,
+                         DWORD session_id,
+                         std::vector<uint32>* pids) {
+  ASSERT1(pids);
+  ASSERT1(exe_name);
+  ASSERT1(*exe_name);
+  UTIL_LOG(L3, (_T("[GetPidsInSession][%s][sid=%s][session=%d]"),
+                exe_name, user_sid, session_id));
+
+  pids->clear();
+
+  DWORD flags = EXCLUDE_CURRENT_PROCESS;
+  if (user_sid != NULL) {
+    flags |= INCLUDE_ONLY_PROCESS_OWNED_BY_USER;
+  }
+  std::vector<CString> command_lines;
+  HRESULT hr = Process::FindProcessesInSession(session_id,
+                                               flags,
+                                               exe_name,
+                                               true,
+                                               user_sid,
+                                               command_lines,
+                                               pids);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return pids->empty() ? HRESULT_FROM_WIN32(ERROR_NOT_FOUND) : S_OK;
+}
+
+HRESULT GetProcessPidsForActiveUserOrSession(const TCHAR* exe_name,
+                                             std::vector<uint32>* pids) {
+  bool is_system = false;
+  HRESULT hr = IsSystemProcess(&is_system);
+  if (FAILED(hr)) {
+    NET_LOG(LE, (_T("[IsSystemProcess failed][0x%x]"), hr));
+    return hr;
+  }
+
+  if (is_system) {
+    return vista::GetPidsInSession(exe_name,
+                                   NULL,
+                                   System::GetActiveSessionId(),
+                                   pids);
+  }
+
+  CString user_sid;
+  // If this call fails, we are still ok.
+  omaha::user_info::GetProcessUser(NULL, NULL, &user_sid);
+  DWORD current_session = System::GetCurrentSessionId();
+  if (FAILED(vista::GetPidsInSession(exe_name,
+                                     user_sid,
+                                     current_session,
+                                     pids))) {
+    // In the case of RunAs, the processes may be under a different identity
+    // than the current sid. So if we are unable to find a process under the
+    // current user's sid, we search for processes running in the current
+    // session regardless of the sid they are running under.
+    return vista::GetPidsInSession(exe_name,
+                                   NULL,
+                                   current_session,
+                                   pids);
+  }
+
+  return S_OK;
+}
+
+
+
+HRESULT StartProcessWithTokenOfProcess(uint32 pid,
+                                       const CString& command_line) {
+  UTIL_LOG(L5, (_T("[StartProcessWithTokenOfProcess]")
+                _T("[pid %u][command_line '%s']"), pid, command_line));
+
+  // Get the token from process.
+  scoped_handle user_token;
+  HRESULT hr = Process::GetImpersonationToken(pid, address(user_token));
+  if (FAILED(hr)) {
+    CORE_LOG(LEVEL_ERROR, (_T("[GetImpersonationToken failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  // Start process using the token.
+  UTIL_LOG(L5, (_T("[StartProcessWithTokenOfProcess][Running process %s]"),
+                command_line));
+  hr = RunAsUser(command_line, get(user_token), false);
+
+  if (FAILED(hr)) {
+    UTIL_LOG(LEVEL_ERROR,
+      (_T("[Vista::StartProcessWithTokenOfProcess - RunAsUser failed][0x%x]"),
+      hr));
+  }
+
+  return hr;
+}
+
+HRESULT GetLoggedOnUserToken(HANDLE* token) {
+  ASSERT1(token);
+  *token = NULL;
+
+  uint32 pid = 0;
+  HRESULT hr = GetExplorerPidForCurrentUserOrSession(&pid);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  hr = Process::GetImpersonationToken(pid, token);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  ASSERT1(*token);
+  return S_OK;
+}
+
+}  // namespace vista
+
+}  // namespace omaha
+
diff --git a/base/vista_utils.h b/base/vista_utils.h
new file mode 100644
index 0000000..def9b4e
--- /dev/null
+++ b/base/vista_utils.h
@@ -0,0 +1,104 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_VISTA_UTILS_H__
+#define OMAHA_COMMON_VISTA_UTILS_H__
+
+#include <windows.h>
+#include <aclapi.h>
+#include <sddl.h>
+#include <userenv.h>
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+// Constants.
+const TCHAR* const kExplorer = _T("EXPLORER.EXE");
+const TCHAR* const kIExplore = _T("IEXPLORE.EXE");
+
+namespace vista {
+
+// Returns true if the current process is running in 'protected mode'.
+bool IsProcessProtected();
+
+// Allows processes that run under protected mode access to a shared kernel
+// object such as mapped memory.
+//
+// Returns S_OK if successful, S_FALSE if not running on vista, or an
+// error value.
+HRESULT AllowProtectedProcessAccessToSharedObject(const TCHAR* name);
+
+// Restarts IEUser process if we can. This is to allow for
+// IEUser.exe to refresh it's ElevationPolicy cache. Due to a bug
+// within IE7, IEUser.exe does not refresh it's cache unless it
+// is restarted in the manner below. If the cache is not refreshed
+// IEUser does not respect any new ElevationPolicies that a fresh
+// setup program installs for an ActiveX control or BHO. This code
+// is adapted from Toolbar.
+HRESULT RestartIEUser();
+
+// TODO(Omaha): Move these to a different utils file, since these are not
+// Vista-specific.
+// TODO(Omaha): rename for consistency with
+// GetProcessPidsForActiveUserOrSession.
+//
+// Gets current user's explorer.exe pid. If that fails, gets the pid of any
+// explorer.exe running in the current session.
+HRESULT GetExplorerPidForCurrentUserOrSession(uint32* pid);
+
+// Returns the TOKEN of the explorer process of any user that is logged in.
+HRESULT GetExplorerTokenForLoggedInUser(HANDLE* token);
+
+// Retrieves a primary token for one of the logged on users. The logged on
+// user is either the current user or a user logged on in the same session as
+// the current user. The caller must close the token handle.
+// If this function is called before the dependent services of Remote
+// Desktop Services have started, an RPC_S_INVALID_BINDING error code may
+// be returned.
+HRESULT GetLoggedOnUserToken(HANDLE* token);
+
+// Get PIDs for the processes running with the specified executable, user_sid,
+// and session_id. user_sid can be blank, in which case, the search will
+// encompass all processes with the given name in session_id. The session
+// always has to be a valid session, hence the name GetPidsInSession().
+HRESULT GetPidsInSession(const TCHAR* exe_name,
+                         const TCHAR* user_sid,
+                         DWORD session_id,
+                         std::vector<uint32>* pids);
+
+// Get the handle of exe_name running under the active user or active session.
+// If the call is made from the SYSTEM account, returns PIDs for exe_name
+// in the currently active user session. If the call is made from a user account
+// returns PIDs for that user, or if that cannot be found, in the current
+// session.
+HRESULT GetProcessPidsForActiveUserOrSession(const TCHAR* exe_name,
+                                             std::vector<uint32>* pids);
+
+// Starts process with the token obtained from the specified process.
+HRESULT StartProcessWithTokenOfProcess(uint32 pid,
+                                       const CString& command_line);
+
+// Runs the command on behalf of the current user. Creates a fresh environment
+// block based on the user's token.
+HRESULT RunAsCurrentUser(const CString& command_line);
+
+}  // namespace vista
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_VISTA_UTILS_H__
+
diff --git a/base/vista_utils_unittest.cc b/base/vista_utils_unittest.cc
new file mode 100644
index 0000000..675899c
--- /dev/null
+++ b/base/vista_utils_unittest.cc
@@ -0,0 +1,63 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/app_util.h"
+#include "omaha/base/path.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vista_utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace vista {
+
+// Exercises RunAsUser() with explorer token. For Vista, the call to
+// StartProcessWithTokenOfProcess() will succeed only if the caller is SYSTEM.
+TEST(VistaUtilsTest, StartProcessWithExplorerTokenTest) {
+  CString path = ConcatenatePath(app_util::GetSystemDir(), _T("cmd.exe"));
+  EnclosePath(&path);
+  path += _T(" /c exit 702");
+  uint32 pid(0);
+  EXPECT_SUCCEEDED(GetExplorerPidForCurrentUserOrSession(&pid));
+
+  HRESULT hr = StartProcessWithTokenOfProcess(pid, path);
+  if (!vista_util::IsVistaOrLater()) {
+    EXPECT_SUCCEEDED(hr);
+    return;
+  }
+
+  bool is_system = false;
+  EXPECT_SUCCEEDED(IsSystemProcess(&is_system));
+  if (is_system) {
+    EXPECT_SUCCEEDED(hr);
+    return;
+  }
+
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_PRIVILEGE_NOT_HELD), hr);
+}
+
+// Exercises RunAsUser() with current user token.
+TEST(VistaUtilsTest, RunAsCurrentUserTest) {
+  CString path = ConcatenatePath(app_util::GetSystemDir(), _T("cmd.exe"));
+  EnclosePath(&path);
+  path += _T(" /c exit 702");
+  EXPECT_SUCCEEDED(vista::RunAsCurrentUser(path));
+}
+
+}  // namespace vista
+
+}  // namespace omaha
+
diff --git a/base/vistautil.cc b/base/vistautil.cc
new file mode 100644
index 0000000..45aecea
--- /dev/null
+++ b/base/vistautil.cc
@@ -0,0 +1,585 @@
+// Copyright 2006-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/vistautil.h"
+#include <accctrl.h>
+#include <Aclapi.h>
+#include <Sddl.h>
+#include <ShellAPI.h>
+#include <shlobj.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/process.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vista_utils.h"
+#include "omaha/third_party/smartany/scoped_any.h"
+
+namespace omaha {
+
+namespace vista_util {
+
+static SID_IDENTIFIER_AUTHORITY mandatory_label_auth =
+    SECURITY_MANDATORY_LABEL_AUTHORITY;
+
+
+static HRESULT GetSidIntegrityLevel(PSID sid, MANDATORY_LEVEL* level) {
+  if (!IsValidSid(sid))
+    return E_FAIL;
+
+  SID_IDENTIFIER_AUTHORITY* authority = GetSidIdentifierAuthority(sid);
+  if (!authority)
+    return E_FAIL;
+
+  if (memcmp(authority, &mandatory_label_auth,
+      sizeof(SID_IDENTIFIER_AUTHORITY)))
+    return E_FAIL;
+
+  PUCHAR count = GetSidSubAuthorityCount(sid);
+  if (!count || *count != 1)
+    return E_FAIL;
+
+  DWORD* rid = GetSidSubAuthority(sid, 0);
+  if (!rid)
+    return E_FAIL;
+
+  if ((*rid & 0xFFF) != 0 || *rid > SECURITY_MANDATORY_PROTECTED_PROCESS_RID)
+    return E_FAIL;
+
+  *level = static_cast<MANDATORY_LEVEL>(*rid >> 12);
+  return S_OK;
+}
+
+// Will return S_FALSE and MandatoryLevelMedium if the acl is NULL
+static HRESULT GetAclIntegrityLevel(PACL acl, MANDATORY_LEVEL* level,
+    bool* and_children) {
+  *level = MandatoryLevelMedium;
+  if (and_children)
+    *and_children = false;
+  if (!acl) {
+    // This is the default label value if the acl was empty
+    return S_FALSE;
+  }
+
+  SYSTEM_MANDATORY_LABEL_ACE* mandatory_label_ace;
+  if (!GetAce(acl, 0, reinterpret_cast<void**>(&mandatory_label_ace)))
+    return S_FALSE;
+
+  if (mandatory_label_ace->Header.AceType != SYSTEM_MANDATORY_LABEL_ACE_TYPE)
+    return S_FALSE;
+
+  if (!(mandatory_label_ace->Mask & SYSTEM_MANDATORY_LABEL_NO_WRITE_UP)) {
+    // I have found that if this flag is not set, a low integrity label doesn't
+    // prevent writes from being virtualized.  MS provides zero documentation.
+    // I just did an MSDN search, a Google search, and a search of the Beta
+    // Vista SDKs, and no docs. TODO(omaha): Check docs again periodically.
+    // For now, act as if no label was set, and default to medium.
+    return S_FALSE;
+  }
+
+  if (and_children) {
+    *and_children = ((mandatory_label_ace->Header.AceFlags &
+        (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE))
+        == (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE));
+  }
+
+  return GetSidIntegrityLevel(reinterpret_cast<SID*>(&mandatory_label_ace->
+      SidStart), level);
+}
+
+// If successful, the caller needs to free the ACL using LocalFree()
+// on failure, returns NULL
+static ACL* CreateMandatoryLabelAcl(MANDATORY_LEVEL level, bool and_children) {
+  int ace_size = sizeof(SYSTEM_MANDATORY_LABEL_ACE)
+      - sizeof(DWORD) + GetSidLengthRequired(1);
+  int acl_size = sizeof(ACL) + ace_size;
+
+  ACL* acl = reinterpret_cast<ACL*>(LocalAlloc(LPTR, acl_size));
+  if (!acl)
+    return NULL;
+
+  bool failed = true;
+  if (InitializeAcl(acl, acl_size, ACL_REVISION)) {
+    if (level > 0) {
+      SYSTEM_MANDATORY_LABEL_ACE* ace = reinterpret_cast<
+          SYSTEM_MANDATORY_LABEL_ACE*>(LocalAlloc(LPTR, ace_size));
+      if (ace) {
+        ace->Header.AceType = SYSTEM_MANDATORY_LABEL_ACE_TYPE;
+        ace->Header.AceFlags = and_children ?
+            (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE) : 0;
+        ace->Header.AceSize = static_cast<WORD>(ace_size);
+        ace->Mask = SYSTEM_MANDATORY_LABEL_NO_WRITE_UP;
+
+        SID* sid = reinterpret_cast<SID*>(&ace->SidStart);
+
+        if (InitializeSid(sid, &mandatory_label_auth, 1)) {
+          *GetSidSubAuthority(sid, 0) = static_cast<DWORD>(level) << 12;
+          failed = !AddAce(acl, ACL_REVISION, 0, ace, ace_size);
+        }
+        LocalFree(ace);
+      }
+    }
+  }
+  if (failed) {
+    LocalFree(acl);
+    acl = NULL;
+  }
+  return acl;
+}
+
+
+TCHAR* AllocFullRegPath(HKEY root, const TCHAR* subkey) {
+  if (!subkey)
+    return NULL;
+
+  const TCHAR* root_string;
+
+  if (root == HKEY_CURRENT_USER)
+    root_string = _T("CURRENT_USER\\");
+  else if (root == HKEY_LOCAL_MACHINE)
+    root_string = _T("MACHINE\\");
+  else if (root == HKEY_CLASSES_ROOT)
+    root_string = _T("CLASSES_ROOT\\");
+  else if (root == HKEY_USERS)
+    root_string = _T("USERS\\");
+  else
+    return NULL;
+
+  size_t root_size = _tcslen(root_string);
+  size_t size = root_size + _tcslen(subkey) + 1;
+  TCHAR* result = reinterpret_cast<TCHAR*>(LocalAlloc(LPTR,
+      size * sizeof(TCHAR)));
+  if (!result)
+    return NULL;
+
+  memcpy(result, root_string, size * sizeof(TCHAR));
+  memcpy(result + root_size, subkey, (1 + size - root_size) * sizeof(TCHAR));
+  return result;
+}
+
+
+bool IsUserNonElevatedAdmin() {
+  // If pre-Vista return false;
+  if (!IsVistaOrLater()) {
+    return false;
+  }
+
+  bool non_elevated_admin = false;
+  scoped_handle token;
+  if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_READ, address(token))) {
+    TOKEN_ELEVATION_TYPE elevation_type = TokenElevationTypeDefault;
+    DWORD infoLen = 0;
+    if (::GetTokenInformation(get(token),
+                              TokenElevationType,
+                              reinterpret_cast<void*>(&elevation_type),
+                              sizeof(elevation_type),
+                              &infoLen)) {
+      if (elevation_type == TokenElevationTypeLimited) {
+        non_elevated_admin = true;
+      }
+    }
+  }
+
+  return non_elevated_admin;
+}
+
+bool IsUserAdmin() {
+  // Determine if the user is part of the adminstators group. This will return
+  // true in case of XP and 2K if the user belongs to admin group. In case of
+  // Vista, it only returns true if the admin is running elevated.
+  SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY;
+  PSID administrators_group = NULL;
+  BOOL result = ::AllocateAndInitializeSid(&nt_authority,
+                                           2,
+                                           SECURITY_BUILTIN_DOMAIN_RID,
+                                           DOMAIN_ALIAS_RID_ADMINS,
+                                           0, 0, 0, 0, 0, 0,
+                                           &administrators_group);
+  if (result) {
+    if (!::CheckTokenMembership(NULL, administrators_group, &result)) {
+      result = false;
+    }
+    ::FreeSid(administrators_group);
+  }
+  return !!result;
+}
+
+bool IsVistaOrLater() {
+  static bool known = false;
+  static bool is_vista = false;
+  if (!known) {
+    OSVERSIONINFOEX osvi = { 0 };
+    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+    osvi.dwMajorVersion = 6;
+    DWORDLONG conditional = 0;
+    VER_SET_CONDITION(conditional, VER_MAJORVERSION, VER_GREATER_EQUAL);
+    is_vista = !!VerifyVersionInfo(&osvi, VER_MAJORVERSION, conditional);
+    // If the Win32 API failed for some other reason, callers may incorrectly
+    // perform non-Vista operations. Assert we don't see any other failures.
+    ASSERT1(is_vista || ERROR_OLD_WIN_VERSION == ::GetLastError());
+    known = true;
+  }
+  return is_vista;
+}
+
+HRESULT IsUserRunningSplitToken(bool* is_split_token) {
+  ASSERT1(is_split_token);
+
+  if (!IsVistaOrLater()) {
+    *is_split_token = false;
+    return S_OK;
+  }
+
+  scoped_handle process_token;
+  if (!::OpenProcessToken(::GetCurrentProcess(),
+                          TOKEN_QUERY,
+                          address(process_token))) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(L1, (_T("[OpenProcessToken failed][0x%x]"), hr));
+    return hr;
+  }
+
+  TOKEN_ELEVATION_TYPE elevation_type = TokenElevationTypeDefault;
+  DWORD size_returned = 0;
+  if (!::GetTokenInformation(get(process_token),
+                             TokenElevationType,
+                             &elevation_type,
+                             sizeof(elevation_type),
+                             &size_returned)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(L1, (_T("[GetTokenInformation failed][0x%x]"), hr));
+    return hr;
+  }
+
+  *is_split_token = elevation_type == TokenElevationTypeFull ||
+                    elevation_type == TokenElevationTypeLimited;
+  ASSERT1(*is_split_token || elevation_type == TokenElevationTypeDefault);
+
+  return S_OK;
+}
+
+bool IsUACMaybeOn() {
+  ASSERT1(vista_util::IsVistaOrLater());
+
+  // The presence of a split token definitively indicates that UAC is on. But
+  // the absence does not necessarily indicate that UAC is off.
+  bool is_split_token = false;
+  if (SUCCEEDED(IsUserRunningSplitToken(&is_split_token)) && is_split_token) {
+    return true;
+  }
+
+  const TCHAR* key_name = _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\")
+                          _T("CurrentVersion\\Policies\\System");
+
+  DWORD enable_lua = 0;
+  return FAILED(RegKey::GetValue(key_name, _T("EnableLUA"), &enable_lua)) ||
+         enable_lua;
+}
+
+bool IsElevatedWithUACMaybeOn() {
+  return IsUserAdmin() && IsVistaOrLater() && IsUACMaybeOn();
+}
+
+HRESULT RunElevated(const TCHAR* file_path,
+                    const TCHAR* parameters,
+                    int show_window,
+                    DWORD* exit_code) {
+  UTIL_LOG(L1, (_T("[Running elevated][%s][%s]"), file_path, parameters));
+
+  ASSERT1(vista_util::IsVistaOrLater());
+  ASSERT1(!vista_util::IsUserAdmin());
+
+  SHELLEXECUTEINFO shell_execute_info;
+  shell_execute_info.cbSize = sizeof(SHELLEXECUTEINFO);
+  shell_execute_info.fMask = SEE_MASK_FLAG_NO_UI     |
+                             SEE_MASK_NOZONECHECKS   |
+                             SEE_MASK_NOASYNC;
+  if (exit_code != NULL) {
+    shell_execute_info.fMask |= SEE_MASK_NOCLOSEPROCESS;
+  }
+  shell_execute_info.hProcess = NULL;
+  shell_execute_info.hwnd = NULL;
+  shell_execute_info.lpVerb = L"runas";
+  shell_execute_info.lpFile = file_path;
+  shell_execute_info.lpParameters = parameters;
+  shell_execute_info.lpDirectory = NULL;
+  shell_execute_info.nShow = show_window;
+  shell_execute_info.hInstApp = NULL;
+
+  if (!ShellExecuteExEnsureParent(&shell_execute_info)) {
+    return AtlHresultFromLastError();
+  }
+
+  scoped_process process(shell_execute_info.hProcess);
+
+  // Wait for the end of the spawned process, if needed
+  if (exit_code) {
+    WaitForSingleObject(get(process), INFINITE);
+    VERIFY1(GetExitCodeProcess(get(process), exit_code));
+    UTIL_LOG(L1, (_T("[Elevated process exited][PID: %u][exit code: %u]"),
+                  Process::GetProcessIdFromHandle(get(process)), *exit_code));
+  } else {
+    UTIL_LOG(L1, (_T("[Elevated process exited][PID: %u]"),
+                  Process::GetProcessIdFromHandle(get(process))));
+  }
+
+  return S_OK;
+}
+
+
+HRESULT GetProcessIntegrityLevel(DWORD process_id, MANDATORY_LEVEL* level) {
+  if (!IsVistaOrLater())
+    return E_NOTIMPL;
+
+  if (process_id == 0)
+    process_id = ::GetCurrentProcessId();
+
+  HRESULT result = E_FAIL;
+  HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process_id);
+  if (process != NULL) {
+    HANDLE current_token;
+    if (OpenProcessToken(process,
+                         TOKEN_QUERY | TOKEN_QUERY_SOURCE,
+                         &current_token)) {
+      DWORD label_size = 0;
+      TOKEN_MANDATORY_LABEL* label;
+      GetTokenInformation(current_token, TokenIntegrityLevel,
+          NULL, 0, &label_size);
+      if (label_size && (label = reinterpret_cast<TOKEN_MANDATORY_LABEL*>
+          (LocalAlloc(LPTR, label_size))) != NULL) {
+        if (GetTokenInformation(current_token, TokenIntegrityLevel,
+            label, label_size, &label_size)) {
+          result = GetSidIntegrityLevel(label->Label.Sid, level);
+        }
+        LocalFree(label);
+      }
+      CloseHandle(current_token);
+    }
+    CloseHandle(process);
+  }
+  return result;
+}
+
+
+HRESULT GetFileOrFolderIntegrityLevel(const TCHAR* file,
+    MANDATORY_LEVEL* level, bool* and_children) {
+  if (!IsVistaOrLater())
+    return E_NOTIMPL;
+
+  PSECURITY_DESCRIPTOR descriptor;
+  PACL acl = NULL;
+
+  DWORD result = GetNamedSecurityInfo(const_cast<TCHAR*>(file), SE_FILE_OBJECT,
+      LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, &acl, &descriptor);
+  if (result != ERROR_SUCCESS)
+    return HRESULT_FROM_WIN32(result);
+
+  HRESULT hr = GetAclIntegrityLevel(acl, level, and_children);
+  LocalFree(descriptor);
+  return hr;
+}
+
+
+HRESULT SetFileOrFolderIntegrityLevel(const TCHAR* file,
+    MANDATORY_LEVEL level, bool and_children) {
+  if (!IsVistaOrLater())
+    return E_NOTIMPL;
+
+  ACL* acl = CreateMandatoryLabelAcl(level, and_children);
+  if (!acl)
+    return E_FAIL;
+
+  DWORD result = SetNamedSecurityInfo(const_cast<TCHAR*>(file), SE_FILE_OBJECT,
+      LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, acl);
+  LocalFree(acl);
+  return HRESULT_FROM_WIN32(result);
+}
+
+
+HRESULT GetRegKeyIntegrityLevel(HKEY root, const TCHAR* subkey,
+    MANDATORY_LEVEL* level, bool* and_children) {
+  if (!IsVistaOrLater())
+    return E_NOTIMPL;
+
+  TCHAR* reg_path = AllocFullRegPath(root, subkey);
+  if (!reg_path)
+    return E_FAIL;
+
+  PSECURITY_DESCRIPTOR descriptor;
+  PACL acl = NULL;
+
+  DWORD result = GetNamedSecurityInfo(reg_path, SE_REGISTRY_KEY,
+      LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, &acl, &descriptor);
+  if (result != ERROR_SUCCESS) {
+    LocalFree(reg_path);
+    return HRESULT_FROM_WIN32(result);
+  }
+
+  HRESULT hr = GetAclIntegrityLevel(acl, level, and_children);
+  LocalFree(descriptor);
+  LocalFree(reg_path);
+  return hr;
+}
+
+
+HRESULT SetRegKeyIntegrityLevel(HKEY root, const TCHAR* subkey,
+    MANDATORY_LEVEL level, bool and_children) {
+  if (!IsVistaOrLater())
+    return E_NOTIMPL;
+
+  TCHAR* reg_path = AllocFullRegPath(root, subkey);
+  if (!reg_path)
+    return E_FAIL;
+
+  ACL* acl = CreateMandatoryLabelAcl(level, and_children);
+  if (!acl) {
+    LocalFree(reg_path);
+    return E_FAIL;
+  }
+
+  DWORD result = SetNamedSecurityInfo(reg_path, SE_REGISTRY_KEY,
+      LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, acl);
+  LocalFree(acl);
+  LocalFree(reg_path);
+  return HRESULT_FROM_WIN32(result);
+}
+
+
+CSecurityDesc* BuildSecurityDescriptor(const TCHAR* sddl_sacl,
+                                       ACCESS_MASK mask) {
+  if (!IsVistaOrLater()) {
+    return NULL;
+  }
+
+  scoped_ptr<CSecurityDesc> security_descriptor(new CSecurityDesc);
+  security_descriptor->FromString(sddl_sacl);
+
+  // Fill out the rest of the security descriptor from the process token.
+  CAccessToken token;
+  if (!token.GetProcessToken(TOKEN_QUERY)) {
+    return NULL;
+  }
+
+  // The owner.
+  CSid sid_owner;
+  if (!token.GetOwner(&sid_owner)) {
+    return NULL;
+  }
+  security_descriptor->SetOwner(sid_owner);
+
+  // The group.
+  CSid sid_group;
+  if (!token.GetPrimaryGroup(&sid_group)) {
+    return NULL;
+  }
+  security_descriptor->SetGroup(sid_group);
+
+  // The discretionary access control list.
+  CDacl dacl;
+  if (!token.GetDefaultDacl(&dacl)) {
+    return NULL;
+  }
+
+  // Add an access control entry mask for the current user.
+  // This is what grants this user access from lower integrity levels.
+  CSid sid_user;
+  if (!token.GetUser(&sid_user)) {
+    return NULL;
+  }
+
+  if (!dacl.AddAllowedAce(sid_user, mask)) {
+    return NULL;
+  }
+
+  // Lastly, save the dacl to this descriptor.
+  security_descriptor->SetDacl(dacl);
+  return security_descriptor.release();
+};
+
+CSecurityDesc* CreateLowIntegritySecurityDesc(ACCESS_MASK mask) {
+  return BuildSecurityDescriptor(LOW_INTEGRITY_SDDL_SACL, mask);
+}
+
+CSecurityDesc* CreateMediumIntegritySecurityDesc(ACCESS_MASK mask) {
+  return BuildSecurityDescriptor(MEDIUM_INTEGRITY_SDDL_SACL, mask);
+}
+
+HRESULT AddLowIntegritySaclToExistingDesc(CSecurityDesc* sd) {
+  ASSERT1(sd);
+  ASSERT1(sd->GetPSECURITY_DESCRIPTOR());
+
+  if (!IsVistaOrLater()) {
+    return S_FALSE;
+  }
+
+  CSecurityDesc sd_low;
+  if (!sd_low.FromString(LOW_INTEGRITY_SDDL_SACL)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[Failed to parse LOW_INTEGRITY_SDDL_SACL][0x%x]"), hr));
+    return hr;
+  }
+
+  // Atl::CSacl does not support SYSTEM_MANDATORY_LABEL_ACE_TYPE.
+  BOOL sacl_present = FALSE;
+  BOOL sacl_defaulted = FALSE;
+  PACL sacl = NULL;
+  if (!::GetSecurityDescriptorSacl(
+             const_cast<SECURITY_DESCRIPTOR*>(sd_low.GetPSECURITY_DESCRIPTOR()),
+             &sacl_present,
+             &sacl,
+             &sacl_defaulted) ||
+      !sacl) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[Failed to get the low integrity SACL][0x%x]"), hr));
+    return hr;
+  }
+
+  ACL_SIZE_INFORMATION acl_size = {0};
+  if (!::GetAclInformation(sacl,
+                           &acl_size,
+                           sizeof(acl_size),
+                           AclSizeInformation)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[Failed to get AclSizeInformation][0x%x]"), hr));
+    return hr;
+  }
+
+  // The CSecurityDesc destructor expects the memory to have been malloced.
+  PACL new_sacl = static_cast<PACL>(malloc(acl_size.AclBytesInUse));
+  ::CopyMemory(new_sacl, sacl, acl_size.AclBytesInUse);
+
+  CSacl sacl_empty;
+  sd->SetSacl(sacl_empty);
+
+  if (!::SetSecurityDescriptorSacl(
+             const_cast<SECURITY_DESCRIPTOR*>(sd->GetPSECURITY_DESCRIPTOR()),
+             sacl_present,
+             new_sacl,
+             sacl_defaulted))  {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[Failed to set the low integrity SACL][0x%x]"), hr));
+    free(new_sacl);
+    return hr;
+  }
+
+  return S_OK;
+}
+
+}  // namespace vista_util
+
+}  // namespace omaha
+
diff --git a/base/vistautil.h b/base/vistautil.h
new file mode 100644
index 0000000..e0ebdb7
--- /dev/null
+++ b/base/vistautil.h
@@ -0,0 +1,165 @@
+// Copyright 2006-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_BASE_VISTAUTIL_H_
+#define OMAHA_BASE_VISTAUTIL_H_
+
+#include <windows.h>
+#include <tchar.h>
+#include <accctrl.h>
+#include <Aclapi.h>
+#include <Sddl.h>
+#include <WinNT.h>
+#include <atlsecurity.h>
+
+namespace omaha {
+
+// SACLs are normally used for auditing, but Vista also uses them to
+// determine integrity levels.
+// For more info, http://www.google.com/search?q=SDDL+for+Mandatory+Labels
+// S = SACL
+// ML = Mandatory label (aka integrity level)
+// NW = No write up (integrity levels less than low cannot gain access)
+// LW = Low Integrity Level (What IE normally runs in)
+
+// The LABEL_SECURITY_INFORMATION SDDL SACL for medium integrity.
+// L"S:(ML;;NW;;;ME)"
+#define MEDIUM_INTEGRITY_SDDL_SACL  SDDL_SACL             \
+                                    SDDL_DELIMINATOR      \
+                                    SDDL_ACE_BEGIN        \
+                                    SDDL_MANDATORY_LABEL  \
+                                    SDDL_SEPERATOR        \
+                                    SDDL_SEPERATOR        \
+                                    SDDL_NO_WRITE_UP      \
+                                    SDDL_SEPERATOR        \
+                                    SDDL_SEPERATOR        \
+                                    SDDL_SEPERATOR        \
+                                    SDDL_ML_MEDIUM        \
+                                    SDDL_ACE_END
+
+// The LABEL_SECURITY_INFORMATION SDDL SACL for low integrity.
+// L"S:(ML;;NW;;;LW)"
+#define LOW_INTEGRITY_SDDL_SACL     SDDL_SACL             \
+                                    SDDL_DELIMINATOR      \
+                                    SDDL_ACE_BEGIN        \
+                                    SDDL_MANDATORY_LABEL  \
+                                    SDDL_SEPERATOR        \
+                                    SDDL_SEPERATOR        \
+                                    SDDL_NO_WRITE_UP      \
+                                    SDDL_SEPERATOR        \
+                                    SDDL_SEPERATOR        \
+                                    SDDL_SEPERATOR        \
+                                    SDDL_ML_LOW           \
+                                    SDDL_ACE_END
+
+namespace vista_util {
+
+// This is fast, since it caches the answer after first run.
+bool IsVistaOrLater();
+
+// Is the user running on Vista or later with a split-token.
+HRESULT IsUserRunningSplitToken(bool* is_split_token);
+
+// Returns true if the user has a split token, or if the EnableLUA key is set
+// to 1. EnableLUA is only really effective after a reboot, and the value there
+// may not reflect the exact state of the running machine. So this function
+// needs to be used with care.
+bool IsUACMaybeOn();
+
+// Returns true if running at High integrity with UAC possibly enabled. As the
+// name indicates, UAC being on or off is not 100% accurate. So this function
+// needs to be used with care.
+bool IsElevatedWithUACMaybeOn();
+
+// Returns true if the process is running under credentials of an user
+// belonging to the admin group in case of pre-Vista and in case Vista
+// returns true if the user is running as an elevated admin.
+bool IsUserAdmin();
+
+// Returns true if the user is running as a non-elevated admin in case of
+// Vista. In case of XP always returns false.
+bool IsUserNonElevatedAdmin();
+
+// Determine the mandatory level of a process
+//   processID, the process to query, or (0) to use the current process
+//   On Vista, level should alwys be filled in with either
+//     MandatoryLevelLow (IE)
+//     MandatoryLevelMedium(user), or
+//     MandatoryLevelHigh( Elevated Admin)
+//   On error, level remains unchanged
+HRESULT GetProcessIntegrityLevel(DWORD processID, MANDATORY_LEVEL* level);
+
+// Elevated processes need to be careful how they launch child processes
+// to avoid having them inherit too many credentials or not being able to
+// elevate their own IE processes normally.  Microsoft's advice from
+// http://msdn.microsoft.com/library/en-us/ietechcol/dnwebgen/protectedmode.asp
+// will launch a low integrity IE, but that IE cannot elevate properly since
+// it was running from the wrong token. The best method I can gather is to find
+// an existing process on the machine running at normal user rights, and launch
+// this process impersonating that token rather than trying to adjust token
+// privileges of the elevated token.  TODO(omaha): Implement and test this.
+HRESULT CreateProcessAsNormalUserFromElevatedAdmin(const TCHAR* commandline,
+    STARTUPINFO* startup_info, PROCESS_INFORMATION* process_info);
+
+// Starts a new elevated process. file_path specifies the program to be run.
+// If exit_code is not null, the function waits until the spawned process has
+// completed. The exit code of the process is returned therein.
+// If exit_code is null, the function will return after spawning the program
+// and will not wait for completion.
+// show_window is one of the SW_* constants to specify how the window is
+// opened.
+HRESULT RunElevated(const TCHAR* file_path, const TCHAR* parameters,
+    int show_window, DWORD* exit_code);
+
+// If there is no specific integrity level defined, return S_FALSE (1) and set
+// level to MandatoryLevelMedium (the Vista default)
+HRESULT GetFileOrFolderIntegrityLevel(const TCHAR* file,
+    MANDATORY_LEVEL* level, bool* and_children);
+
+// A level of MandatoryLevelUntrusted (0) will remove the integrity level for
+// this file and all children
+HRESULT SetFileOrFolderIntegrityLevel(const TCHAR* file,
+    MANDATORY_LEVEL level, bool and_children);
+
+// If there is no specific integrity level defined, return S_FALSE (1) and set
+// level to MandatoryLevelMedium (the Vista default)
+// root must be one of the 4 pre-defined roots: HKLM, HKCU, HKCR, HCU
+HRESULT GetRegKeyIntegrityLevel(HKEY root, const TCHAR* subkey,
+    MANDATORY_LEVEL* level, bool* and_children);
+
+// A level of MandatoryLevelUntrusted (0) will remove the integrity label
+// root must be one of the 4 pre-defined roots: HKLM, HKCU, HKCR, HCU
+HRESULT SetRegKeyIntegrityLevel(HKEY root, const TCHAR* subkey,
+    MANDATORY_LEVEL level, bool and_children);
+
+// Creates a security descriptor that can be used to make an object accessible
+// from the specified integrity level. When not running on Windows Vista or
+// in case of errors, the function returns NULL, which results in using
+// the default security descriptor.
+// The caller must take ownership of the returned security descriptor.
+// Mask will be added as an allowed ACE of the DACL.
+// For example, use MUTEX_ALL_ACCESS for shared mutexes.
+CSecurityDesc* CreateLowIntegritySecurityDesc(ACCESS_MASK mask);
+CSecurityDesc* CreateMediumIntegritySecurityDesc(ACCESS_MASK mask);
+
+// For Vista or later, add the low integrity SACL to an existing CSecurityDesc.
+HRESULT AddLowIntegritySaclToExistingDesc(CSecurityDesc* sd);
+
+}  // namespace vista_util
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_VISTAUTIL_H_
+
diff --git a/base/vistautil_unittest.cc b/base/vistautil_unittest.cc
new file mode 100644
index 0000000..521c487
--- /dev/null
+++ b/base/vistautil_unittest.cc
@@ -0,0 +1,64 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <shlobj.h>
+#include "omaha/base/reg_key.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace vista_util {
+
+TEST(VistaUtilTest, IsUserAdmin) {
+  bool is_admin = !!::IsUserAnAdmin();
+  EXPECT_EQ(is_admin, IsUserAdmin());
+}
+
+// Tests the code returns true if Vista or later.
+TEST(VistaUtilTest, IsUACMaybeOn) {
+  if (!IsVistaOrLater()) {
+    std::wcout << _T("\tSkipping test because not running on Vista or later.")
+               << std::endl;
+    return;
+  }
+
+  bool is_uac_maybe_on = false;
+
+  bool is_split_token = false;
+  if (SUCCEEDED(IsUserRunningSplitToken(&is_split_token)) && is_split_token) {
+    is_uac_maybe_on = true;
+  } else {
+    const TCHAR* key_name = _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\")
+                            _T("CurrentVersion\\Policies\\System");
+
+    DWORD enable_lua = 0;
+    is_uac_maybe_on =
+        FAILED(RegKey::GetValue(key_name, _T("EnableLUA"), &enable_lua)) ||
+        enable_lua;
+  }
+
+  EXPECT_EQ(is_uac_maybe_on, IsUACMaybeOn());
+}
+
+TEST(VistaUtilTest, IsElevatedWithUACMaybeOn) {
+  EXPECT_EQ(IsUserAdmin() && IsVistaOrLater() && IsUACMaybeOn(),
+            IsElevatedWithUACMaybeOn());
+}
+
+}  // namespace vista_util
+
+}  // namespace omaha
+
diff --git a/base/window_utils.cc b/base/window_utils.cc
new file mode 100644
index 0000000..0c9f49d
--- /dev/null
+++ b/base/window_utils.cc
@@ -0,0 +1,147 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/window_utils.h"
+
+#include "base/basictypes.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/string.h"
+
+namespace omaha {
+
+namespace {
+
+struct FindProcessWindowsRecord {
+  uint32 process_id;
+  uint32 window_flags;
+  CSimpleArray<HWND>* windows;
+};
+
+BOOL CALLBACK FindProcessWindowsEnumProc(HWND hwnd, LPARAM lparam) {
+  FindProcessWindowsRecord* enum_record =
+      reinterpret_cast<FindProcessWindowsRecord*>(lparam);
+  ASSERT1(enum_record);
+
+  DWORD process_id = 0;
+  ::GetWindowThreadProcessId(hwnd, &process_id);
+
+  // Only count this window if it is in the right process
+  // and it satisfies all specified window requirements.
+  if (enum_record->process_id != process_id) {
+    return true;
+  }
+  if ((enum_record->window_flags & kWindowMustBeTopLevel) &&
+      ::GetParent(hwnd)) {
+    return true;
+  }
+
+  if ((enum_record->window_flags & kWindowMustHaveSysMenu) &&
+      !(GetWindowLong(hwnd, GWL_STYLE) & WS_SYSMENU)) {
+    return true;
+  }
+
+  if ((enum_record->window_flags & kWindowMustBeVisible) &&
+      !::IsWindowVisible(hwnd)) {
+    return true;
+  }
+
+  enum_record->windows->Add(hwnd);
+  return true;
+}
+
+}  // namespace
+
+bool WindowUtils::FindProcessWindows(uint32 process_id,
+                                     uint32 window_flags,
+                                     CSimpleArray<HWND>* windows) {
+  ASSERT1(windows);
+  windows->RemoveAll();
+  FindProcessWindowsRecord enum_record = {0};
+  enum_record.process_id = process_id;
+  enum_record.window_flags = window_flags;
+  enum_record.windows = windows;
+  ::EnumWindows(FindProcessWindowsEnumProc,
+                reinterpret_cast<LPARAM>(&enum_record));
+  int num_windows = enum_record.windows->GetSize();
+  return num_windows > 0;
+}
+
+void WindowUtils::MakeWindowForeground(HWND wnd) {
+  if (!IsWindowVisible(wnd)) {
+    // If the window is hidden and we call SetWindowPos with SWP_SHOWWINDOW
+    // then the window will be visible.
+    // If the caller wants it visible they should do it themselves first.
+    return;
+  }
+  if (!SetWindowPos(wnd,
+                    HWND_TOP,
+                    0,
+                    0,
+                    0,
+                    0,
+                    SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW)) {
+    UTIL_LOG(LE, (_T("[WindowUtils::MakeWindowForeground]")
+                  _T("[SetWindowPos failed][0x%08x]"), HRESULTFromLastError()));
+  }
+}
+
+bool WindowUtils::IsMainWindow(HWND wnd) {
+  return NULL == ::GetParent(wnd) && IsWindowVisible(wnd);
+}
+
+bool WindowUtils::HasSystemMenu(HWND wnd) {
+  return (GetWindowLong(wnd, GWL_STYLE) & WS_SYSMENU) != 0;
+}
+
+// The system displays the system large icon in the ALT+TAB dialog box.
+// We do not need any small icon in the window caption. However, setting
+// ICON_BIG has the side effect of the window displaying a scaled down
+// version of it in the window caption. We could not find any way to
+// hide that icon, including setting the icon to NULL or handling WM_GETICON
+// message.
+HRESULT WindowUtils::SetWindowIcon(HWND hwnd, WORD icon_id, HICON* hicon) {
+  ASSERT1(hwnd);
+  ASSERT1(hicon);
+  *hicon = NULL;
+
+  const int cx = ::GetSystemMetrics(SM_CXICON);
+  const int cy = ::GetSystemMetrics(SM_CYICON);
+  HINSTANCE exe_instance = static_cast<HINSTANCE>(::GetModuleHandle(NULL));
+  HICON icon = reinterpret_cast<HICON>(::LoadImage(exe_instance,
+                                                   MAKEINTRESOURCE(icon_id),
+                                                   IMAGE_ICON,
+                                                   cx,
+                                                   cy,
+                                                   LR_DEFAULTCOLOR));
+  if (!icon) {
+    HRESULT hr = HRESULTFromLastError();
+    CORE_LOG(LE, (_T("[SetWindowIcon - LoadImage failed][0x%x]"), hr));
+    return hr;
+  }
+
+  ::SendMessage(hwnd,
+                WM_SETICON,
+                ICON_BIG,
+                reinterpret_cast<LPARAM>(icon));
+  *hicon = icon;
+  return S_OK;
+}
+
+
+}  // namespace omaha
+
diff --git a/base/window_utils.h b/base/window_utils.h
new file mode 100644
index 0000000..798e066
--- /dev/null
+++ b/base/window_utils.h
@@ -0,0 +1,59 @@
+// Copyright 2004-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#ifndef OMAHA_BASE_WINDOW_UTILS_H_
+#define OMAHA_BASE_WINDOW_UTILS_H_
+
+#include <atlcoll.h>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+// Flags for window requirements.
+const uint32 kWindowMustBeTopLevel  =  0x00000001;
+const uint32 kWindowMustHaveSysMenu =  0x00000002;
+const uint32 kWindowMustBeVisible   =  0x00000004;
+
+class WindowUtils {
+ public:
+  // Finds all the primary windows owned by the given process. For the
+  // purposes of this function, primary windows are top-level, have a system
+  // menu, and are visible.
+  static bool FindProcessWindows(uint32 process_id,
+                                 uint32 window_flags,
+                                 CSimpleArray<HWND>* windows);
+
+  // Forces the window to the foreground.
+  static void MakeWindowForeground(HWND wnd);
+
+  // Returns true if the window is the "main window" of a process:
+  // if it's Visible, and Top Level
+  static bool IsMainWindow(HWND wnd);
+
+  // Returns true if the window has a System Menu
+  static bool HasSystemMenu(HWND wnd);
+
+  // Sets a window's icon to the specified icon embedded within the running exe
+  // module.
+  static HRESULT SetWindowIcon(HWND hwnd, WORD icon_id, HICON* hicon);
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(WindowUtils);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_WINDOW_UTILS_H_
diff --git a/base/wmi_query.cc b/base/wmi_query.cc
new file mode 100644
index 0000000..a124262
--- /dev/null
+++ b/base/wmi_query.cc
@@ -0,0 +1,204 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include "omaha/base/wmi_query.h"
+
+#include <atlcomcli.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+WmiQuery::WmiQuery() : at_end_(true) {
+}
+
+WmiQuery::~WmiQuery() {
+}
+
+HRESULT WmiQuery::Connect(const TCHAR* resource) {
+  UTIL_LOG(L6, (_T("[WmiQuery::Connect][resource=%s]"), resource));
+  ASSERT1(resource && *resource);
+
+  CComBSTR object_path;
+  HRESULT hr = object_path.Append(resource);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = wbem_.CoCreateInstance(__uuidof(WbemLocator));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Connect to WMI through the IWbemLocator::ConnectServer method. This
+  // call can block up to 2 minutes on XP or indefinitely on Windows 2000 if
+  // the server is broken.
+  hr = wbem_->ConnectServer(object_path,                      // object path
+                            NULL,                             // username
+                            NULL,                             // password
+                            NULL,                             // locale
+                            WBEM_FLAG_CONNECT_USE_MAX_WAIT,   // security flags
+                            NULL,                             // authority
+                            0,                                // context
+                            &service_);                       // namespace
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Set security levels on the proxy.
+  hr = ::CoSetProxyBlanket(service_,
+                           RPC_C_AUTHN_WINNT,
+                           RPC_C_AUTHZ_NONE,
+                           NULL,
+                           RPC_C_AUTHN_LEVEL_CALL,
+                           RPC_C_IMP_LEVEL_IMPERSONATE,
+                           NULL,
+                           EOAC_NONE);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT WmiQuery::Query(const TCHAR* query) {
+  UTIL_LOG(L6, (_T("[WmiQuery::Query][query=%s]"), query));
+  ASSERT1(query && *query);
+
+  CComBSTR query_language, query_string;
+  HRESULT hr = query_language.Append(_T("WQL"));
+  if (FAILED(hr)) {
+    return hr;
+  }
+  hr = query_string.Append(query);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  uint32 flags = WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY;
+  hr = service_->ExecQuery(query_language,
+                           query_string,
+                           flags,
+                           NULL,
+                           &enumerator_);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  at_end_ = false;
+  return Next();
+}
+
+HRESULT WmiQuery::Next() {
+  UTIL_LOG(L6, (_T("[WmiQuery::Next]")));
+
+  ASSERT1(!at_end_);
+
+  ULONG ret = 0;
+  HRESULT hr = enumerator_->Next(WBEM_INFINITE, 1, &obj_, &ret);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  at_end_ = ret == 0;
+  return S_OK;
+}
+
+bool WmiQuery::AtEnd() {
+  return at_end_;
+}
+
+HRESULT WmiQuery::GetValue(const TCHAR* name, CComVariant* value) {
+  ASSERT1(name && *name);
+  ASSERT1(value);
+  ASSERT1(!at_end_ && obj_);
+
+  value->Clear();
+
+  CComBSTR name_string;
+  HRESULT hr = name_string.Append(name);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  hr = obj_->Get(name_string, 0, value, 0, 0);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT WmiQuery::GetValue(const TCHAR* name, CString* value) {
+  ASSERT1(name && *name);
+  ASSERT1(value);
+
+  CComVariant var;
+  HRESULT hr = GetValue(name, &var);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  ASSERT1(V_VT(&var) == VT_BSTR);
+  value->SetString(var.bstrVal);
+  return S_OK;
+}
+
+HRESULT WmiQuery::GetValue(const TCHAR* name, bool* value) {
+  ASSERT1(name && *name);
+  ASSERT1(value);
+
+  CComVariant var;
+  HRESULT hr = GetValue(name, &var);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  ASSERT1(V_VT(&var) == VT_BOOL);
+  *value = var.boolVal != 0;
+  return S_OK;
+}
+
+HRESULT WmiQuery::GetValue(const TCHAR* name, int* value) {
+  ASSERT1(name && *name);
+  ASSERT1(value);
+
+  CComVariant var;
+  HRESULT hr = GetValue(name, &var);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  ASSERT1(V_VT(&var) == VT_I4);
+  *value = var.lVal;
+  return S_OK;
+}
+
+HRESULT WmiQuery::GetValue(const TCHAR* name, uint32* value) {
+  ASSERT1(name && *name);
+  ASSERT1(value);
+
+  CComVariant var;
+  HRESULT hr = GetValue(name, &var);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  ASSERT1(V_VT(&var) == VT_UI4);
+  *value = var.ulVal;
+  return S_OK;
+}
+
+}  // namespace omaha
+
diff --git a/common/wmi_query.h b/base/wmi_query.h
similarity index 100%
rename from common/wmi_query.h
rename to base/wmi_query.h
diff --git a/base/wmi_query_unittest.cc b/base/wmi_query_unittest.cc
new file mode 100644
index 0000000..acdf92a
--- /dev/null
+++ b/base/wmi_query_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/wmi_query.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(WmiQueryTest, WmiQuery) {
+  WmiQuery wq;
+  ASSERT_HRESULT_SUCCEEDED(wq.Connect(_T("root\\CIMV2")));
+  ASSERT_HRESULT_SUCCEEDED(wq.Query(_T("select * from Win32_OperatingSystem")));
+
+  CString manufacturer;
+  EXPECT_HRESULT_SUCCEEDED(wq.GetValue(_T("Manufacturer"), &manufacturer));
+  EXPECT_STREQ(_T("Microsoft Corporation"), manufacturer);
+
+  // Expect a retail build of the OS.
+  bool is_debug(true);
+  EXPECT_HRESULT_SUCCEEDED(wq.GetValue(_T("Debug"), &is_debug));
+  EXPECT_FALSE(is_debug);
+
+  int max_number_of_processes(0);
+  EXPECT_HRESULT_SUCCEEDED(wq.GetValue(_T("MaxNumberOfProcesses"),
+                           &max_number_of_processes));
+  EXPECT_EQ(-1, max_number_of_processes);
+}
+
+}  // namespace omaha
+
diff --git a/common/wtl_atlapp_wrapper.h b/base/wtl_atlapp_wrapper.h
similarity index 100%
rename from common/wtl_atlapp_wrapper.h
rename to base/wtl_atlapp_wrapper.h
diff --git a/base/xml_utils.cc b/base/xml_utils.cc
new file mode 100644
index 0000000..db7fe97
--- /dev/null
+++ b/base/xml_utils.cc
@@ -0,0 +1,874 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// xml_utils.cpp
+//
+// Utilities for working with XML files via MSXML.
+
+#include "omaha/base/xml_utils.h"
+
+#include <msxml2.h>
+#include <atlsafe.h>
+#include <vector>
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+XMLFQName::XMLFQName() {}
+
+XMLFQName::XMLFQName(const TCHAR* u, const TCHAR* b)
+    : uri(u && ::_tcslen(u) ? u : 0),
+      base(b && ::_tcslen(b) ? b : 0) {}
+
+XMLFQName::~XMLFQName() {}
+
+HRESULT CoCreateSafeDOMDocument(IXMLDOMDocument** my_xmldoc) {
+  ASSERT1(my_xmldoc && !*my_xmldoc);
+  if (!my_xmldoc) {
+    UTIL_LOG(LE, (L"[CoCreateSafeDOMDocument E_INVALIDARG]"));
+    return E_INVALIDARG;
+  }
+  *my_xmldoc = NULL;
+  CComPtr<IXMLDOMDocument> xml_doc;
+  HRESULT hr = xml_doc.CoCreateInstance(__uuidof(DOMDocument2));
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[xml_doc.CoCreateInstance failed][0x%x]"), hr));
+    return hr;
+  }
+  ASSERT1(xml_doc);
+  hr = xml_doc->put_resolveExternals(VARIANT_FALSE);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[put_resolveExternals failed][0x%x]"), hr));
+    return hr;
+  }
+  *my_xmldoc = xml_doc.Detach();
+  return S_OK;
+}
+
+HRESULT LoadXMLFromFile(const TCHAR* xmlfile,
+                        bool preserve_whitespace,
+                        IXMLDOMDocument** xmldoc) {
+  ASSERT1(xmlfile);
+  ASSERT1(xmldoc);
+  ASSERT1(!*xmldoc);
+
+  *xmldoc = NULL;
+  CComPtr<IXMLDOMDocument> my_xmldoc;
+  HRESULT hr = CoCreateSafeDOMDocument(&my_xmldoc);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[CoCreateSafeDOMDocument failed][0x%x]"), hr));
+    return hr;
+  }
+  hr = my_xmldoc->put_preserveWhiteSpace(VARIANT_BOOL(preserve_whitespace));
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[put_preserveWhiteSpace failed][0x%x]"), hr));
+    return hr;
+  }
+  CComBSTR my_xmlfile(xmlfile);
+  VARIANT_BOOL is_successful(VARIANT_FALSE);
+  hr = my_xmldoc->load(CComVariant(my_xmlfile), &is_successful);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[my_xmldoc->load failed][0x%x]"), hr));
+    return hr;
+  }
+  if (!is_successful) {
+    CComPtr<IXMLDOMParseError> error;
+    CString error_message;
+    hr = GetXMLParseError(my_xmldoc, &error);
+    if (FAILED(hr)) {
+      UTIL_LOG(LE, (_T("[GetXMLParseError failed][0x%x]"), hr));
+      return hr;
+    }
+    ASSERT1(error);
+    HRESULT error_code = 0;
+    hr = InterpretXMLParseError(error, &error_code, &error_message);
+    if (FAILED(hr)) {
+      UTIL_LOG(LE, (_T("[InterpretXMLParseError failed][0x%x]"), hr));
+      return hr;
+    }
+    UTIL_LOG(LE, (L"[LoadXMLFromFile '%s'][parse error: %s]",
+                  xmlfile, error_message));
+    ASSERT1(FAILED(error_code));
+    return FAILED(error_code) ? error_code : CI_E_XML_LOAD_ERROR;
+  }
+  *xmldoc = my_xmldoc.Detach();
+  return S_OK;
+}
+
+HRESULT LoadXMLFromMemory(const TCHAR* xmlstring,
+                          bool preserve_whitespace,
+                          IXMLDOMDocument** xmldoc) {
+  ASSERT1(xmlstring);
+  ASSERT1(xmldoc);
+  ASSERT1(!*xmldoc);
+
+  *xmldoc = NULL;
+  CComPtr<IXMLDOMDocument> my_xmldoc;
+  RET_IF_FAILED(CoCreateSafeDOMDocument(&my_xmldoc));
+  RET_IF_FAILED(my_xmldoc->put_preserveWhiteSpace(
+                               VARIANT_BOOL(preserve_whitespace)));
+  CComBSTR xmlmemory(xmlstring);
+  VARIANT_BOOL is_successful(VARIANT_FALSE);
+  RET_IF_FAILED(my_xmldoc->loadXML(xmlmemory, &is_successful));
+  if (!is_successful) {
+    CComPtr<IXMLDOMParseError> error;
+    CString error_message;
+    RET_IF_FAILED(GetXMLParseError(my_xmldoc, &error));
+    ASSERT1(error);
+    HRESULT error_code = 0;
+    RET_IF_FAILED(InterpretXMLParseError(error, &error_code, &error_message));
+    UTIL_LOG(LE, (L"[LoadXMLFromMemory][parse error: %s]", error_message));
+    ASSERT1(FAILED(error_code));
+    return FAILED(error_code) ? error_code : CI_E_XML_LOAD_ERROR;
+  }
+  *xmldoc = my_xmldoc.Detach();
+  return S_OK;
+}
+
+HRESULT LoadXMLFromRawData(const std::vector<byte>& xmldata,
+                           bool preserve_whitespace,
+                           IXMLDOMDocument** xmldoc) {
+  ASSERT1(xmldoc);
+  ASSERT1(!*xmldoc);
+
+  *xmldoc = NULL;
+  if (!xmldata.size()) {
+    return E_INVALIDARG;
+  }
+
+  CComPtr<IXMLDOMDocument> my_xmldoc;
+  RET_IF_FAILED(CoCreateSafeDOMDocument(&my_xmldoc));
+  RET_IF_FAILED(my_xmldoc->put_preserveWhiteSpace(
+                              VARIANT_BOOL(preserve_whitespace)));
+
+  CComSafeArray<byte> xmlsa;
+  xmlsa.Add(xmldata.size(), &xmldata.front());
+  CComVariant xmlvar(xmlsa);
+
+  VARIANT_BOOL is_successful(VARIANT_FALSE);
+  RET_IF_FAILED(my_xmldoc->load(xmlvar, &is_successful));
+  if (!is_successful) {
+    CComPtr<IXMLDOMParseError> error;
+    CString error_message;
+    RET_IF_FAILED(GetXMLParseError(my_xmldoc, &error));
+    ASSERT1(error);
+    HRESULT error_code = 0;
+    RET_IF_FAILED(InterpretXMLParseError(error, &error_code, &error_message));
+    UTIL_LOG(LE, (_T("[LoadXMLFromRawData][parse error: %s]"), error_message));
+    ASSERT1(FAILED(error_code));
+    return FAILED(error_code) ? error_code : CI_E_XML_LOAD_ERROR;
+  }
+  *xmldoc = my_xmldoc.Detach();
+  return S_OK;
+}
+
+HRESULT SaveXMLToFile(IXMLDOMDocument* xmldoc, const TCHAR* xmlfile) {
+  ASSERT1(xmldoc);
+  ASSERT1(xmlfile);
+
+  CComBSTR my_xmlfile(xmlfile);
+  RET_IF_FAILED(xmldoc->save(CComVariant(my_xmlfile)));
+  return S_OK;
+}
+
+HRESULT SaveXMLToMemory(IXMLDOMDocument* xmldoc, CString* xmlstring) {
+  ASSERT1(xmldoc);
+  ASSERT1(xmlstring);
+
+  CComBSTR xmlmemory;
+  RET_IF_FAILED(xmldoc->get_xml(&xmlmemory));
+  *xmlstring = xmlmemory;
+
+  return S_OK;
+}
+
+HRESULT SaveXMLToRawData(IXMLDOMDocument* xmldoc, std::vector<byte>* buffer) {
+  ASSERT1(xmldoc);
+  ASSERT1(buffer);
+
+  CComPtr<IStream> stream;
+  HRESULT hr = ::CreateStreamOnHGlobal(NULL, TRUE, &stream);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  ASSERT1(stream);
+
+  hr = xmldoc->save(CComVariant(stream));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // To get the exact size of the stream, we have to use the seek function.
+  LARGE_INTEGER li = {0, 0};
+  ULARGE_INTEGER uli = {0, 0};
+  hr = stream->Seek(li, STREAM_SEEK_END, &uli);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  buffer->resize(static_cast<size_t>(uli.QuadPart));
+
+  HGLOBAL hglobal = NULL;
+  hr = ::GetHGlobalFromStream(stream, &hglobal);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  memcpy_s(&buffer->front(),
+           buffer->size(),
+           ::GlobalLock(hglobal),
+           buffer->size());
+  ::GlobalUnlock(hglobal);
+
+  return S_OK;
+}
+
+HRESULT CanonicalizeXML(const TCHAR* xmlstring, CString* canonical_xmlstring) {
+  ASSERT1(xmlstring);
+  ASSERT1(canonical_xmlstring);
+
+  // Round-trip through MSXML, having it strip whitespace.
+
+  CComPtr<IXMLDOMDocument> xmldoc;
+  RET_IF_FAILED(CoCreateSafeDOMDocument(&xmldoc));
+  RET_IF_FAILED(xmldoc->put_preserveWhiteSpace(VARIANT_FALSE));
+  {
+    CComBSTR xmlmemory(StringAfterBOM(xmlstring));
+    VARIANT_BOOL is_successful(VARIANT_FALSE);
+    RET_IF_FAILED(xmldoc->loadXML(xmlmemory, &is_successful));
+    if (!is_successful) {
+      CComPtr<IXMLDOMParseError> error;
+      CString error_message;
+      RET_IF_FAILED(GetXMLParseError(xmldoc, &error));
+      ASSERT1(error);
+      HRESULT error_code = 0;
+      RET_IF_FAILED(InterpretXMLParseError(error, &error_code, &error_message));
+      UTIL_LOG(LE, (L"[CanonicalizeXML][parse error: %s]", error_message));
+      ASSERT1(FAILED(error_code));
+      return FAILED(error_code) ? error_code : CI_E_XML_LOAD_ERROR;
+    }
+  }
+  std::vector<CString> lines;
+  {
+    CComBSTR xmlmemory2;
+    RET_IF_FAILED(xmldoc->get_xml(&xmlmemory2));
+    TextToLines(CString(xmlmemory2), L"\r\n", &lines);
+  }
+  {
+    for (size_t i = 0; i < lines.size(); ++i) {
+      TrimString(lines[i], L" \t");
+    }
+    LinesToText(lines, L"", canonical_xmlstring);
+  }
+
+  return S_OK;
+}
+
+bool operator==(const XMLFQName& u, const XMLFQName& v) {
+  if (u.uri && v.uri) {
+    // Both uris are non-null -> compare all the components.
+    return !_tcscmp(u.uri, v.uri) && !_tcscmp(u.base, v.base);
+  } else if (!u.uri && !v.uri) {
+    // Both uris are null -> only compare the base names.
+    return !_tcscmp(u.base ? u.base : __T(""), v.base ? v.base : __T(""));
+  } else {
+    // Either uri is null -> the names are in different namespaces.
+    return false;
+  }
+}
+
+bool operator!=(const XMLFQName& u, const XMLFQName& v) {
+  return !(u == v);
+}
+
+bool operator<(const XMLFQName& u, const XMLFQName &v) {
+  if (u.uri && v.uri) {
+    return (_tcscmp(u.uri, v.uri) < 0) ||
+            ((_tcscmp(u.uri, v.uri) == 0) && (_tcscmp(u.base, v.base) < 0));
+  } else if (!u.uri && !v.uri) {
+    return _tcscmp(u.base, v.base) < 0;
+  } else {
+    return false;
+  }
+}
+
+bool operator>(const XMLFQName& u, const XMLFQName& v) {
+  return v < u;
+}
+
+bool operator<=(const XMLFQName& u, const XMLFQName& v) {
+  return !(v < u);
+}
+
+bool operator>=(const XMLFQName& u, const XMLFQName& v) {
+  return !(u < v);
+}
+
+bool EqualXMLName(const XMLFQName& u, const XMLFQName& v) {
+  return u == v;
+}
+
+// msxml returns a null uri for nodes that don't belong to a namespace.
+bool EqualXMLName(IXMLDOMNode* pnode, const XMLFQName& u) {
+  CComBSTR name;
+  CComBSTR uri;
+  if (FAILED(pnode->get_baseName(&name)) ||
+      FAILED(pnode->get_namespaceURI(&uri))) {
+    return false;
+  }
+  return EqualXMLName(XMLFQName(uri, name), u);
+}
+
+inline bool EqualXMLName(const XMLFQName& u, IXMLDOMNode* pnode) {
+  return EqualXMLName(pnode, u);
+}
+
+HRESULT GetXMLFQName(IXMLDOMNode* node, XMLFQName* name) {
+  ASSERT1(node);
+  ASSERT1(name);
+
+  CComBSTR basename, uri;
+  RET_IF_FAILED(node->get_baseName(&basename));
+  RET_IF_FAILED(node->get_namespaceURI(&uri));
+  *name = XMLFQName(uri, basename);
+  return S_OK;
+}
+
+CString XMLFQNameToString(const XMLFQName& fqname) {
+  CString name;
+  if (fqname.uri) {
+    name += fqname.uri;
+    name += L":";
+  }
+  if (fqname.base) {
+    name += fqname.base;
+  }
+  return name;
+}
+
+CString NodeToString(IXMLDOMNode* pnode) {
+  ASSERT1(pnode);
+
+  XMLFQName node_name;
+  if (SUCCEEDED(GetXMLFQName(pnode, &node_name))) {
+    return XMLFQNameToString(node_name);
+  }
+  return L"";
+}
+
+HRESULT CreateXMLNode(IXMLDOMDocument* xmldoc,
+                      int node_type,
+                      const TCHAR* node_name,
+                      const TCHAR* namespace_uri,
+                      const TCHAR* text,
+                      IXMLDOMNode** node_out) {
+  ASSERT1(xmldoc);
+  ASSERT1(node_name);
+  // namespace_uri can be NULL
+  // text can be NULL
+  ASSERT1(node_out);
+  ASSERT1(!*node_out);
+
+  *node_out = NULL;
+  CComPtr<IXMLDOMNode> new_node;
+  CComBSTR node_name_string, namespace_uri_string;
+  RET_IF_FAILED(node_name_string.Append(node_name));
+  RET_IF_FAILED(namespace_uri_string.Append(namespace_uri));
+  RET_IF_FAILED(xmldoc->createNode(CComVariant(node_type),
+                                   node_name_string,
+                                   namespace_uri_string,
+                                   &new_node));
+  ASSERT1(new_node);
+
+  // If any text was supplied, put it in the node
+  if (text && text[0]) {
+    RET_IF_FAILED(new_node->put_text(CComBSTR(text)));
+  }
+
+  *node_out = new_node.Detach();
+  return S_OK;
+}
+
+HRESULT AppendXMLNode(IXMLDOMNode* xmlnode, IXMLDOMNode* new_child) {
+  ASSERT1(xmlnode);
+  ASSERT1(new_child);
+
+  CComPtr<IXMLDOMNode> useless;
+  RET_IF_FAILED(xmlnode->appendChild(new_child, &useless));
+  return S_OK;
+}
+
+HRESULT AppendXMLNode(IXMLDOMNode* xmlnode, const TCHAR* text) {
+  ASSERT1(xmlnode);
+  // text can be NULL
+
+  if (text && text[0]) {
+    CComPtr<IXMLDOMDocument> xml_doc;
+    CComPtr<IXMLDOMText> text_node;
+    RET_IF_FAILED(xmlnode->get_ownerDocument(&xml_doc));
+    ASSERT1(xml_doc);
+    RET_IF_FAILED(xml_doc->createTextNode(CComBSTR(text), &text_node));
+    RET_IF_FAILED(AppendXMLNode(xmlnode, text_node));
+  }
+  return S_OK;
+}
+
+HRESULT AddXMLAttributeNode(IXMLDOMNode* xmlnode, IXMLDOMAttribute* new_child) {
+  ASSERT1(xmlnode);
+  ASSERT1(new_child);
+
+  CComPtr<IXMLDOMNamedNodeMap> attributes;
+  CComPtr<IXMLDOMNode> useless;
+  RET_IF_FAILED(xmlnode->get_attributes(&attributes));
+  RET_IF_FAILED(attributes->setNamedItem(new_child, &useless));
+  return S_OK;
+}
+
+HRESULT AddXMLAttributeNode(IXMLDOMElement* xmlelement,
+                            const TCHAR* attribute_name,
+                            const TCHAR* attribute_value) {
+  ASSERT1(xmlelement);
+  ASSERT1(attribute_name);
+  // attribute_value can be NULL
+
+  RET_IF_FAILED(xmlelement->setAttribute(CComBSTR(attribute_name),
+                                         CComVariant(attribute_value)));
+  return S_OK;
+}
+
+HRESULT AddXMLAttributeNode(IXMLDOMNode* xmlnode,
+                            const TCHAR* attribute_namespace,
+                            const TCHAR* attribute_name,
+                            const TCHAR* attribute_value) {
+  ASSERT1(xmlnode);
+  ASSERT1(attribute_name);
+  // attribute_namespace can be NULL
+  // attribute_value can be NULL
+
+  CComPtr<IXMLDOMDocument> xmldoc;
+  RET_IF_FAILED(xmlnode->get_ownerDocument(&xmldoc));
+  ASSERT1(xmldoc);
+
+  CComPtr<IXMLDOMNode> attribute_node;
+  RET_IF_FAILED(CreateXMLNode(xmldoc,
+                              NODE_ATTRIBUTE,
+                              attribute_name,
+                              attribute_namespace,
+                              attribute_value,
+                              &attribute_node));
+  CComQIPtr<IXMLDOMAttribute> attribute(attribute_node);
+  ASSERT1(attribute);
+  RET_IF_FAILED(AddXMLAttributeNode(xmlnode, attribute));
+  return S_OK;
+}
+
+HRESULT RemoveXMLChildrenByName(IXMLDOMNode* xmlnode, const XMLFQName& name) {
+  ASSERT1(xmlnode);
+
+  CComPtr<IXMLDOMNodeList> node_list;
+  RET_IF_FAILED(xmlnode->get_childNodes(&node_list));
+  ASSERT1(node_list);
+
+  bool found = false;
+  do {
+    found = false;
+    long count = 0;   // NOLINT
+    RET_IF_FAILED(node_list->get_length(&count));
+    RET_IF_FAILED(node_list->reset());
+
+    for (int i = 0; i < count; ++i) {
+      CComPtr<IXMLDOMNode> child_node, useless;
+      RET_IF_FAILED(node_list->get_item(i, &child_node));
+      ASSERT1(child_node);
+      if (EqualXMLName(child_node, name)) {
+        RET_IF_FAILED(xmlnode->removeChild(child_node, &useless));
+        // Start loop over: the list is "alive" and changes when you remove a
+        // node from it. Yes this seems to be n^2 but in fact we expect at
+        // most one each of <Hash> and/or <Size> nodes.
+        found = true;
+        break;
+      }
+    }
+  } while (found);
+
+  return S_OK;
+}
+
+HRESULT GetXMLChildByName(IXMLDOMElement* xmlnode,
+                          const TCHAR* child_name,
+                          IXMLDOMNode** xmlchild) {
+  ASSERT1(xmlnode);
+  ASSERT1(child_name);
+  ASSERT1(xmlchild);
+  ASSERT1(!*xmlchild);
+
+  *xmlchild = NULL;
+  CComPtr<IXMLDOMNodeList> node_list;
+  long node_list_length = 0;    // NOLINT
+  RET_IF_FAILED(xmlnode->getElementsByTagName(CComBSTR(child_name),
+                                              &node_list));
+  ASSERT1(node_list);
+  RET_IF_FAILED(node_list->get_length(&node_list_length));
+  if (node_list_length <= 0) {
+    return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
+  }
+  // Should only be one child node with name we're looking for.
+  if (node_list_length > 1) {
+    return CI_E_INVALID_MANIFEST;
+  }
+  RET_IF_FAILED(node_list->reset());
+  RET_IF_FAILED(node_list->get_item(0, xmlchild));
+  ASSERT1(*xmlchild);
+  return S_OK;
+}
+
+HRESULT InsertXMLBeforeItem(IXMLDOMNode* xmlnode,
+                            IXMLDOMNode* new_child,
+                            size_t item_number) {
+  ASSERT1(xmlnode);
+  ASSERT1(new_child);
+
+  CComPtr<IXMLDOMNodeList> child_list;
+  CComPtr<IXMLDOMNode> refchild, useless;
+
+  RET_IF_FAILED(xmlnode->get_childNodes(&child_list));
+  ASSERT1(child_list);
+  RET_IF_FAILED(child_list->get_item(item_number, &refchild));
+  ASSERT1(refchild);
+  RET_IF_FAILED(xmlnode->insertBefore(new_child,
+                                      CComVariant(refchild),
+                                      &useless));
+  return S_OK;
+}
+
+HRESULT GetXMLParseError(IXMLDOMDocument* xmldoc,
+                         IXMLDOMParseError** parse_error) {
+  ASSERT1(xmldoc);
+  ASSERT1(parse_error);
+  ASSERT1(!*parse_error);
+
+  *parse_error = NULL;
+  CComPtr<IXMLDOMParseError> error;
+  RET_IF_FAILED(xmldoc->get_parseError(&error));
+  HRESULT error_code = 0;
+  HRESULT hr = error->get_errorCode(&error_code);
+  if (hr == S_OK) {
+    *parse_error = error.Detach();
+    return S_OK;
+  } else if (hr == S_FALSE) {
+    // No parse error
+    return S_FALSE;
+  } else {
+    return hr;
+  }
+}
+
+HRESULT InterpretXMLParseError(IXMLDOMParseError* parse_error,
+                               HRESULT* error_code,
+                               CString* message) {
+  ASSERT1(parse_error);
+  ASSERT1(error_code);
+  ASSERT1(message);
+
+  long line = 0;      // NOLINT
+  long char_pos = 0;  // NOLINT
+  CComBSTR src_text, reason;
+  RET_IF_FAILED(parse_error->get_errorCode(error_code));
+  RET_IF_FAILED(parse_error->get_line(&line));
+  RET_IF_FAILED(parse_error->get_linepos(&char_pos));
+  RET_IF_FAILED(parse_error->get_srcText(&src_text));
+  RET_IF_FAILED(parse_error->get_reason(&reason));
+
+  // Wild guess.
+  size_t size_estimate = src_text.Length() + reason.Length() + 100;
+
+  // TODO(omaha): think about replacing this call to _snwprintf with a
+  // safestring function.
+  std::vector<TCHAR> s(size_estimate);
+  _snwprintf_s(&s.front(), size_estimate, _TRUNCATE,
+               L"%d(%d) : error 0x%08lx: %s\n  %s",
+               line, char_pos, *error_code,
+               reason ? reason : L"",
+               src_text ? src_text : L"<no source text>");
+  // _snwprintf doesn't terminate the string with a null if
+  // the formatted string fills the entire buffer.
+  s[s.size()- 1] = L'\0';
+  *message = &s.front();
+  return S_OK;
+}
+
+HRESULT GetNumChildren(IXMLDOMNode* node, int* num_children) {
+  ASSERT1(node);
+  ASSERT1(num_children);
+
+  *num_children = 0;
+  CComPtr<IXMLDOMNodeList> children;
+  RET_IF_FAILED(node->get_childNodes(&children));
+  ASSERT1(children);
+
+  long len = 0;   // NOLINT
+  RET_IF_FAILED(children->get_length(&len));
+  *num_children = len;
+  return S_OK;
+}
+
+int GetNumAttributes(IXMLDOMNode* node) {
+  ASSERT1(node);
+
+  CComPtr<IXMLDOMNamedNodeMap> attr_map;
+  if (FAILED(node->get_attributes(&attr_map))) {
+    return 0;
+  }
+  ASSERT1(attr_map);
+  long len = 0;   // NOLINT
+  if (FAILED(attr_map->get_length(&len))) {
+    return 0;
+  }
+  return len;
+}
+
+bool HasAttribute(IXMLDOMNode* node, const TCHAR* attr_name) {
+  ASSERT1(node);
+  ASSERT1(attr_name);
+
+  CComPtr<IXMLDOMNamedNodeMap> attr_map;
+  if (FAILED(node->get_attributes(&attr_map))) {
+    return false;
+  }
+  if (!attr_map) {
+    return false;
+  }
+
+  CComBSTR temp_attr_name(attr_name);
+  CComPtr<IXMLDOMNode> attribute_node;
+  if (FAILED(attr_map->getNamedItem(static_cast<BSTR>(temp_attr_name),
+                                    &attribute_node))) {
+    return false;
+  }
+
+  return attribute_node != NULL;
+}
+
+HRESULT ReadBooleanAttribute(IXMLDOMNode* node,
+                             const TCHAR* attr_name,
+                             bool* value) {
+  CORE_LOG(L4, (_T("[ReadBooleanAttribute][%s]"), attr_name));
+  ASSERT1(node);
+  ASSERT1(attr_name);
+  ASSERT1(value);
+
+  CComBSTR node_value;
+  HRESULT hr = ReadAttribute(node, attr_name, &node_value);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[ReadAttribute failed][%s][0x%x]"), attr_name, hr));
+    return hr;
+  }
+
+  hr = String_StringToBool(static_cast<TCHAR*>(node_value),
+                           value);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[String_StringToBool failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT ReadIntAttribute(IXMLDOMNode* node,
+                         const TCHAR* attr_name,
+                         int* value) {
+  CORE_LOG(L4, (_T("[ReadIntAttribute][%s]"), attr_name));
+  ASSERT1(node);
+  ASSERT1(attr_name);
+  ASSERT1(value);
+
+  CComBSTR node_value;
+  HRESULT hr = ReadAttribute(node, attr_name, &node_value);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[ReadAttribute failed][%s][0x%x]"), attr_name, hr));
+    return hr;
+  }
+
+  if (!String_StringToDecimalIntChecked(
+          static_cast<const TCHAR*>(node_value), value)) {
+          return GOOPDATEXML_E_STRTOUINT;
+  }
+  return S_OK;
+}
+
+HRESULT ReadGuidAttribute(IXMLDOMNode* node,
+                          const TCHAR* attr_name,
+                          GUID* value) {
+  CORE_LOG(L4, (_T("[ReadGuidAttribute][%s]"), attr_name));
+  ASSERT1(node);
+  ASSERT1(attr_name);
+  ASSERT1(value);
+
+  CComBSTR node_value;
+  HRESULT hr = ReadAttribute(node, attr_name, &node_value);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[ReadAttribute failed][%s][0x%x]"), attr_name, hr));
+    return hr;
+  }
+
+  hr = StringToGuidSafe(static_cast<TCHAR*>(node_value), value);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[StringToGuidSafe failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT ReadStringAttribute(IXMLDOMNode* node,
+                            const TCHAR* attr_name,
+                            CString* value) {
+  CORE_LOG(L4, (_T("[ReadStringAttribute][%s]"), attr_name));
+  ASSERT1(node);
+  ASSERT1(attr_name);
+  ASSERT1(value);
+
+  CComBSTR node_value;
+  HRESULT hr = ReadAttribute(node, attr_name, &node_value);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[ReadAttribute failed][%s][0x%x]"), attr_name, hr));
+    return hr;
+  }
+
+  // Will extract the underlying string.
+  *value = static_cast<TCHAR*>(node_value);
+
+  return S_OK;
+}
+
+HRESULT ReadAttribute(IXMLDOMNode* node,
+                      const TCHAR* attr_name,
+                      BSTR* value) {
+  CORE_LOG(L4, (_T("[ReadAttribute][%s]"), attr_name));
+  ASSERT1(node);
+  ASSERT1(attr_name);
+  ASSERT1(value);
+
+  // First read the attributes.
+  CComPtr<IXMLDOMNamedNodeMap> attributes;
+  HRESULT hr = node->get_attributes(&attributes);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[get_attributes failed][0x%x]"), hr));
+    return hr;
+  }
+
+  if (!attributes) {
+    CORE_LOG(LE, (_T("[Msxml S_FALSE return]")));
+    return E_FAIL;  // Protect against msxml S_FALSE return.
+  }
+
+  CComPtr<IXMLDOMNode> attribute_node;
+  CComVariant node_value;
+  CComBSTR temp_attr_name(attr_name);
+
+  // Get the attribute using a named node.
+  hr = attributes->getNamedItem(static_cast<BSTR>(temp_attr_name),
+                                &attribute_node);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[getNamedItem failed][0x%x]"), hr));
+    return hr;
+  }
+
+  if (!attribute_node) {
+    CORE_LOG(LE, (_T("[Msxml S_FALSE return]")));
+    return E_FAIL;  // Protect against msxml S_FALSE return.
+  }
+
+  hr = attribute_node->get_nodeValue(&node_value);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[get_nodeValue failed][0x%x]"), hr));
+    return hr;
+  }
+
+  if (node_value.vt == VT_EMPTY) {
+    CORE_LOG(LE, (_T("[node_value.vt == VT_EMPTY]")));
+    return E_FAIL;
+  }
+
+  // Extract the variant into a BSTR.
+  node_value.CopyTo(value);
+
+  return S_OK;
+}
+
+HRESULT ReadStringValue(IXMLDOMNode* node, CString* value) {
+  CORE_LOG(L4, (_T("[ReadStringValue]")));
+  ASSERT1(node);
+  ASSERT1(value);
+
+  CComPtr<IXMLDOMNodeList> child_nodes;
+  HRESULT hr = node->get_childNodes(&child_nodes);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[get_childNodes failed][0x%x]"), hr));
+    return hr;
+  }
+  if (!child_nodes) {
+    CORE_LOG(LE, (_T("[Msxml S_FALSE return]")));
+    return E_FAIL;  // Protect against msxml S_FALSE return.
+  }
+
+  long count = 0;  // NOLINT
+  hr = child_nodes->get_length(&count);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  ASSERT(count == 1, (_T("count: %u"), count));
+  CComPtr<IXMLDOMNode> child_node;
+  hr = child_nodes->nextNode(&child_node);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  DOMNodeType type = NODE_INVALID;
+  hr = child_node->get_nodeType(&type);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[get_nodeType failed][0x%x]"), hr));
+    return hr;
+  }
+
+  if (type != NODE_TEXT) {
+    CORE_LOG(LE, (_T("[Invalid nodeType][%d]"), type));
+    return E_INVALIDARG;
+  }
+
+  CComVariant node_value;
+  hr = child_node->get_nodeValue(&node_value);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[get_nodeValue failed][0x%x]"), hr));
+    return hr;
+  }
+
+  if (node_value.vt != VT_BSTR) {
+    CORE_LOG(LE, (_T("[node_value.vt != VT_BSTR][%d]"), node_value.vt));
+    return E_INVALIDARG;
+  }
+
+  *value = V_BSTR(&node_value);
+  return S_OK;
+}
+
+}  // namespace omaha
+
diff --git a/base/xml_utils.h b/base/xml_utils.h
new file mode 100644
index 0000000..b67bef8
--- /dev/null
+++ b/base/xml_utils.h
@@ -0,0 +1,288 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// xml_utils.h
+//
+// Utilities for working with XML files via MSXML.
+
+#ifndef OMAHA_BASE_XML_UTILS_H_
+#define OMAHA_BASE_XML_UTILS_H_
+
+#include <windows.h>
+#include <objbase.h>
+#include <msxml.h>
+#include <atlstr.h>
+#include <utility>
+#include <vector>
+
+namespace omaha {
+
+// Creates a DOMDocument that disallows external definitions to be included and
+// resolved as part of the XML document stream at parse time.
+HRESULT CoCreateSafeDOMDocument(IXMLDOMDocument** my_xmldoc);
+
+// xmlfile can be any specified encoding.
+HRESULT LoadXMLFromFile(const TCHAR* xmlfile,
+                        bool preserve_whitespace,
+                        IXMLDOMDocument** xmldoc);
+
+// xmlstring must be UTF-16 or UCS-2.
+HRESULT LoadXMLFromMemory(const TCHAR* xmlstring,
+                          bool preserve_whitespace,
+                          IXMLDOMDocument** xmldoc);
+
+// xmldata can be any raw data supported by xml parser
+HRESULT LoadXMLFromRawData(const std::vector<byte>& xmldata,
+                           bool preserve_whitespace,
+                           IXMLDOMDocument** xmldoc);
+
+// xmlfile is in encoding specified in the XML document.
+HRESULT SaveXMLToFile(IXMLDOMDocument* xmldoc, const TCHAR * xmlfile);
+
+// xmlstring is in UCS-2
+HRESULT SaveXMLToMemory(IXMLDOMDocument* xmldoc, CString* xmlstring);
+
+// buffer is in the encoding specified in the XML document.
+HRESULT SaveXMLToRawData(IXMLDOMDocument* xmldoc, std::vector<byte>* buffer);
+
+// Canonicalizes the XML string so you can compute a signature on it.
+// This is not the official canonicalization but a cheaper scheme which
+// depends on the whitespace stripping capability of MSXML.
+//
+// xmlstring is in UTF-16 or UCS-2
+HRESULT CanonicalizeXML(const TCHAR* xmlstring, CString* canonical_xmlstring);
+
+
+// Dealing with element/attribute names: the combination of a base name
+// and a namespace URI is a fully-qualified XML name, or: XMLFQName.
+
+// We can't just typedef a std::pair because we need proper comparison operators
+// in case we want to stick a XMLFQName into a standard collection.
+struct XMLFQName {
+  XMLFQName();
+  XMLFQName(const TCHAR* u, const TCHAR* b);
+  ~XMLFQName();
+
+  CString uri;
+  CString base;
+};
+
+bool operator==(const XMLFQName& u, const XMLFQName& v);
+bool operator!=(const XMLFQName& u, const XMLFQName& v);
+bool operator< (const XMLFQName& u, const XMLFQName& v);
+bool operator> (const XMLFQName& u, const XMLFQName& v);
+bool operator<=(const XMLFQName& u, const XMLFQName& v);
+bool operator>=(const XMLFQName& u, const XMLFQName& v);
+
+bool EqualXMLName(const XMLFQName& u, const XMLFQName& v);
+bool EqualXMLName(IXMLDOMNode* pnode, const XMLFQName& u);
+bool EqualXMLName(const XMLFQName& u, IXMLDOMNode* pnode);
+
+// Returns the FQ name from the node.
+HRESULT GetXMLFQName(IXMLDOMNode* node, XMLFQName* name);
+
+// Returns a string version of an XMLFQName suitable for debugging use.
+CString XMLFQNameToString(const XMLFQName& fqname);
+
+// Returns a string version of a node's name suitable for debugging use.
+CString NodeToString(IXMLDOMNode* pnode);
+
+//
+// Routines for dealing with fragments of DOM trees.
+//
+// Creates an XMLDOMNode of the given type with a given name and optional text.
+HRESULT CreateXMLNode(IXMLDOMDocument* xmldoc,
+                      int node_type,
+                      const TCHAR* node_name,
+                      const TCHAR* namespace_uri,
+                      const TCHAR* text,
+                      IXMLDOMNode** node_out);
+
+// Adds newchild as a child node of xmlnode after all existing children.
+HRESULT AppendXMLNode(IXMLDOMNode* xmlnode, IXMLDOMNode* new_child);
+
+// Adds text as a child node of xmlnode after all existing children.
+HRESULT AppendXMLNode(IXMLDOMNode* xmlnode, const TCHAR* text);
+
+// Adds newchild as an attribute node of xmlnode replacing existing
+// attribute with same name.
+HRESULT AddXMLAttributeNode(IXMLDOMNode* xmlnode, IXMLDOMAttribute* new_child);
+
+// Adds name/value pair as an attribute node of xmlnode replacing
+// existing attribute with same name.
+HRESULT AddXMLAttributeNode(IXMLDOMElement* xmlelement,
+                            const TCHAR* attribute_name,
+                            const TCHAR* attribute_value);
+
+// Adds name/value pair as an attribute node of xmlnode replacing
+// existing attribute with same name.
+// Can add attributes to nodes other than IXMLDOMElement.
+// Can add attributes with non-null namespaces.
+HRESULT AddXMLAttributeNode(IXMLDOMNode* xmlnode,
+                            const TCHAR* attribute_namespace,
+                            const TCHAR* attribute_name,
+                            const TCHAR* attribute_value);
+
+// Removes all children of the given node that have the specified name.
+HRESULT RemoveXMLChildrenByName(IXMLDOMNode* xmlnode, const XMLFQName& name);
+
+// Gets a child of a given node by name
+HRESULT GetXMLChildByName(IXMLDOMElement* xmlnode,
+                          const TCHAR* child_name,
+                          IXMLDOMNode** xmlchild);
+
+// Adds newchild as a child node of xmlnode, before the exiting
+// child item_number.
+HRESULT InsertXMLBeforeItem(IXMLDOMNode* xmlnode,
+                            IXMLDOMNode* new_child,
+                            size_t item_number);
+
+// Gets parse error information after a failed load.
+HRESULT GetXMLParseError(IXMLDOMDocument* xmldoc,
+                         IXMLDOMParseError** parse_error);
+
+// Interprets parse error.
+HRESULT InterpretXMLParseError(IXMLDOMParseError* parse_error,
+                               HRESULT* error_code,
+                               CString* message);
+
+// Gets the number of children of this node.
+HRESULT GetNumChildren(IXMLDOMNode* pnode, int* num_children);
+
+// Gets the number of attributes of this node.
+int GetNumAttributes(IXMLDOMNode* pnode);
+
+// Returns true if the specified attribute is in this node.
+bool HasAttribute(IXMLDOMNode* node, const TCHAR* attr_name);
+
+// Reads and parses attributes of nodes.
+HRESULT ReadBooleanAttribute(IXMLDOMNode* node,
+                             const TCHAR* attr_name,
+                             bool* value);
+HRESULT ReadIntAttribute(IXMLDOMNode* node,
+                         const TCHAR* attr_name,
+                         int* value);
+HRESULT ReadGuidAttribute(IXMLDOMNode* node,
+                          const TCHAR* attr_name,
+                          GUID* value);
+HRESULT ReadStringAttribute(IXMLDOMNode* node,
+                            const TCHAR* attr_name,
+                            CString* value);
+
+// Reads an attribute as a BSTR, given the node and the name of the attribute.
+// This is a helper for the other ReadXXXAttribute methods.
+HRESULT ReadAttribute(IXMLDOMNode* node,
+                      const TCHAR* attr_name,
+                      BSTR* value);
+
+// Reads the string value of a node element, either TEXT or CDATA.
+HRESULT ReadStringValue(IXMLDOMNode* node, CString* value);
+
+// Maps over a list of XML DOM nodes of some kind, executing a function or
+// a member function against each node in the list.
+// Passes a cookie along to each function call useful for accumulating results.
+// Template class List is usually a IXMLDOMNodeList or a IXMLDOMNamedNodeMap.
+template <class List, class Cookie>
+HRESULT ForEachNodeInList(List list,
+                          HRESULT (*fun)(IXMLDOMNode*, Cookie),
+                          Cookie cookie) {
+  ASSERT1(list);  // List assumed to be a pointer type or smart pointer type
+  ASSERT1(fun);
+
+  long len = 0;   // NOLINT
+  RET_IF_FAILED(list->get_length(&len));
+  for (long i = 0; i != len; ++i) {   // NOLINT
+    CComPtr<IXMLDOMNode> node;
+    RET_IF_FAILED(list->get_item(i, &node));
+    ASSERT1(node);
+    RET_IF_FAILED(fun(node, cookie));
+  }
+  return S_OK;
+}
+
+// Same as ForEachNodeInList but it calls a member function of an object.
+template <class List, class Object, class Cookie>
+HRESULT ForEachNodeInListObj(List list,
+                             Object* object,
+                             HRESULT (Object::*mem_fun)(IXMLDOMNode*, Cookie),
+                             Cookie cookie) {
+  ASSERT1(list);
+  ASSERT1(object);
+  ASSERT1(fun);
+
+  long len = 0;   // NOLINT
+  RET_IF_FAILED(list->get_length(&len));
+  for (long i = 0; i != len; ++i) {   // NOLINT
+    CComPtr<IXMLDOMNode> node;
+    RET_IF_FAILED(list->get_item(i, &node));
+    ASSERT1(node);
+    RET_IF_FAILED((object->*fun)(node, cookie));
+  }
+  return S_OK;
+}
+
+// Maps over the attributes of a node, executing a function against each
+// attribute. Passes a cookie along to each function call.
+template <typename Cookie>
+HRESULT ForEachAttribute(IXMLDOMNode* node,
+                         HRESULT (*fun)(IXMLDOMNode*, Cookie),
+                         Cookie cookie) {
+  ASSERT1(node);
+  ASSERT1(fun);
+
+  CComPtr<IXMLDOMNamedNodeMap> attr_list;
+  RET_IF_FAILED(node->get_attributes(&attr_list));
+  ASSERT1(attr_list);
+  RET_IF_FAILED(ForEachNodeInList(attr_list, fun, cookie));
+  return S_OK;
+}
+
+// Maps over the children nodes of a node, executing a function against
+// each child node. Passes a cookie along to each function call.
+template <typename Cookie>
+HRESULT ForEachChildNode(IXMLDOMNode* node,
+                         HRESULT (*fun)(IXMLDOMNode*, Cookie),
+                         Cookie cookie) {
+  ASSERT1(node);
+  ASSERT1(fun);
+
+  CComPtr<IXMLDOMNodeList> child_list;
+  RET_IF_FAILED(node->get_childNodes(&child_list));
+  ASSERT1(child_list);
+  RET_IF_FAILED(ForEachNodeInList(child_list, fun, cookie));
+  return S_OK;
+}
+
+// Same as ForEachChildNode but it calls a member function of an object.
+template <typename Object, typename Cookie>
+HRESULT ForEachChildNodeObj(IXMLDOMNode* node,
+                            Object* object,
+                            HRESULT (Object::*mem_fun)(IXMLDOMNode*, Cookie),
+                            Cookie cookie) {
+  ASSERT1(node);
+  ASSERT1(object);
+  ASSERT1(fun);
+
+  CComPtr<IXMLDOMNodeList> child_list;
+  RET_IF_FAILED(node->get_childNodes(&child_list));
+  ASSERT1(child_list);
+  RET_IF_FAILED(ForEachNodeInListObj(child_list, object, mem_fun, cookie));
+  return S_OK;
+}
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_XML_UTILS_H_
+
diff --git a/base/xml_utils_unittest.cc b/base/xml_utils_unittest.cc
new file mode 100644
index 0000000..0e7d865
--- /dev/null
+++ b/base/xml_utils_unittest.cc
@@ -0,0 +1,134 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <windows.h>
+#include <atlstr.h>
+#include <vector>
+#include "omaha/base/file.h"
+#include "omaha/base/module_utils.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/xml_utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+const TCHAR kTestXMLFile[] = _T("manifest.xml");
+const TCHAR kTempXMLFile[] = _T("foobar.xml");
+
+const XMLFQName fqLowNoURI(NULL, _T("Bar"));
+const XMLFQName fqLowNoURI2(NULL, _T("Bar"));
+const XMLFQName fqHighNoURI(NULL, _T("Foo"));
+const XMLFQName fqLowURI(_T("Zebra"), _T("Bar"));
+const XMLFQName fqLowURI2(_T("Zebra"), _T("Bar"));
+const XMLFQName fqHighURI(_T("Zebra"), _T("Foo"));
+const XMLFQName fqDifferentURI(_T("Xray"), _T("Bar"));
+
+TEST(XmlUtilsTest, XMLFQName) {
+  ASSERT_TRUE(fqLowNoURI == fqLowNoURI2);
+  ASSERT_TRUE(fqHighNoURI == fqHighNoURI);
+  ASSERT_TRUE(fqLowNoURI != fqHighNoURI);
+  ASSERT_TRUE(fqLowNoURI != fqLowURI);
+  ASSERT_TRUE(fqLowNoURI < fqHighNoURI);
+  ASSERT_TRUE(fqLowNoURI <= fqHighNoURI);
+  ASSERT_TRUE(fqHighNoURI > fqLowNoURI);
+  ASSERT_TRUE(fqHighNoURI >= fqLowNoURI);
+  ASSERT_TRUE(fqLowURI == fqLowURI2);
+  ASSERT_TRUE(fqHighURI == fqHighURI);
+  ASSERT_TRUE(fqLowURI != fqHighURI);
+  ASSERT_TRUE(fqLowURI < fqHighURI);
+  ASSERT_TRUE(fqLowURI <= fqHighURI);
+  ASSERT_TRUE(fqHighURI > fqLowURI);
+  ASSERT_TRUE(fqHighURI >= fqLowURI);
+  ASSERT_TRUE(fqLowURI != fqDifferentURI);
+}
+
+TEST(XmlUtilsTest, LoadSave) {
+  scoped_co_init co_init;
+
+  // Get some directory and file names to start with.
+  TCHAR directory[MAX_PATH] = {0};
+  ASSERT_TRUE(GetModuleDirectory(NULL, directory));
+  CString test_file;
+  test_file.Format(_T("%s\\unittest_support\\%s"), directory, kTestXMLFile);
+
+  TCHAR temp_path[MAX_PATH] = {0};
+  ASSERT_TRUE(::GetTempPath(MAX_PATH, temp_path));
+  CString temp_file;
+  temp_file.AppendFormat(_T("%s%s"), temp_path, kTempXMLFile);
+
+  // Test loading and storing to a file.
+  CComPtr<IXMLDOMDocument> xmldoc;
+  ASSERT_SUCCEEDED(LoadXMLFromFile(test_file, true, &xmldoc));
+  ASSERT_TRUE(xmldoc);
+  ASSERT_SUCCEEDED(SaveXMLToFile(xmldoc, temp_file));
+
+  // Test loading and storing raw UTF8 data to memory.
+  std::vector<byte> buffer_utf8;
+  ASSERT_SUCCEEDED(ReadEntireFile(temp_file, 0, &buffer_utf8));
+
+  CComPtr<IXMLDOMDocument> xmldoc2;
+  ASSERT_SUCCEEDED(LoadXMLFromRawData(buffer_utf8, true, &xmldoc2));
+  ASSERT_TRUE(xmldoc2);
+  std::vector<byte> xml_utf8;
+  ASSERT_SUCCEEDED(SaveXMLToRawData(xmldoc2, &xml_utf8));
+
+  CStringA input_utf8(reinterpret_cast<char*>(&buffer_utf8.front()),
+                      buffer_utf8.size());
+  CStringA output_utf8(reinterpret_cast<char*>(&xml_utf8.front()),
+                       xml_utf8.size());
+  ASSERT_STREQ(input_utf8, output_utf8);
+
+  // Test loading and storing Unicode to memory.
+  // The input must be Unicode for the calls below, but our test file is UTF-8.
+  // So read it and convert it to Unicode.
+  int len(::MultiByteToWideChar(CP_UTF8,
+                                0, /*flags*/
+                                reinterpret_cast<const char*>(&buffer_utf8[0]),
+                                buffer_utf8.size(),
+                                NULL,
+                                0));
+  std::vector<wchar_t> buffer_unicode(len+1);
+  int len2(::MultiByteToWideChar(CP_UTF8,
+                                 0, /*flags*/
+                                 reinterpret_cast<const char*>(&buffer_utf8[0]),
+                                 buffer_utf8.size(),
+                                 &buffer_unicode[0],
+                                 len));
+  ASSERT_EQ(len, len2);
+  buffer_unicode[len] = 0;  // null terminate the unicode string.
+
+  // Now round-trip the load from memory and save to memory.
+  xmldoc2 = NULL;
+  ASSERT_SUCCEEDED(LoadXMLFromMemory(&buffer_unicode.front(), true, &xmldoc2));
+  ASSERT_TRUE(xmldoc2);
+  CString xmlmemory;
+  ASSERT_SUCCEEDED(SaveXMLToMemory(xmldoc2, &xmlmemory));
+
+  // Now compare that the result of the round-trip is the same as the input.
+  CString input(&buffer_unicode.front());
+  CString output(xmlmemory);
+  // Except must first remove the " encoding="UTF-8"" attribute from the
+  // input string.
+  ReplaceCString(input, L" encoding=\"UTF-8\"", L"");
+  ASSERT_STREQ(input, output);
+
+  // Clean up.
+  ASSERT_SUCCEEDED(File::Remove(temp_file));
+}
+
+}  // namespace omaha
+
diff --git a/bho/bho_dll.cc b/bho/bho_dll.cc
deleted file mode 100644
index c263b3b..0000000
--- a/bho/bho_dll.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// DLL entry points for the Bho DLL
-
-#include <atlbase.h>
-#include <atlcom.h>
-#include <atlctl.h>
-#include "omaha/bho/bho_entrypoint.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-class BhoDllModule : public CAtlDllModuleT<BhoDllModule> {
-};
-
-// Add BhoEntry to class library table so IE can CoCreate it.
-OBJECT_ENTRY_AUTO(__uuidof(BhoEntrypointClass), BhoEntrypoint);
-
-BhoDllModule _AtlModule;
-
-}  // namespace omaha
-
-using omaha::_AtlModule;
-
-extern "C" BOOL WINAPI DllMain(HINSTANCE, DWORD reason, LPVOID reserved) {
-  if (reason == DLL_PROCESS_ATTACH) {
-    VERIFY1(SUCCEEDED(omaha::PinModuleIntoProcess(BHO_FILENAME)));
-  }
-
-  return _AtlModule.DllMain(reason, reserved);
-}
-
-STDAPI DllCanUnloadNow() {
-  // Do not allow unloading until the process terminates.
-  return S_FALSE;
-}
-
-STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) {
-  CORE_LOG(L2, (_T("[DllGetClassObject]")));
-  return _AtlModule.DllGetClassObject(rclsid, riid, ppv);
-}
-
-STDAPI DllRegisterServer(void) {
-  // Not registering the typelib.
-  return _AtlModule.DllRegisterServer(FALSE);
-}
-
-
-STDAPI DllUnregisterServer(void) {
-  // Not unregistering the typelib.
-  return _AtlModule.DllUnregisterServer(FALSE);
-}
-
diff --git a/bho/bho_dll.def b/bho/bho_dll.def
deleted file mode 100644
index f2e5579..0000000
--- a/bho/bho_dll.def
+++ /dev/null
@@ -1,23 +0,0 @@
-; Copyright 2007-2009 Google Inc.
-;
-; Licensed under the Apache License, Version 2.0 (the "License");
-; you may not use this file except in compliance with the License.
-; You may obtain a copy of the License at
-;
-;      http://www.apache.org/licenses/LICENSE-2.0
-;
-; Unless required by applicable law or agreed to in writing, software
-; distributed under the License is distributed on an "AS IS" BASIS,
-; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-; See the License for the specific language governing permissions and
-; limitations under the License.
-; ========================================================================
-
-LIBRARY      "GoopdateBho.dll"
-
-EXPORTS
-  DllCanUnloadNow     PRIVATE
-  DllGetClassObject   PRIVATE
-  DllRegisterServer   PRIVATE
-  DllUnregisterServer PRIVATE
-
diff --git a/bho/bho_dll.idl b/bho/bho_dll.idl
deleted file mode 100644
index a56fad1..0000000
--- a/bho/bho_dll.idl
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Template IDL file for the Bho DLL. This template is a complete IDL file in
-// all but one respect; it has one replaceable entry for the CLSID for
-// BhoEntrypointClass. This template is processed by generate_oneclick_idl.py,
-// which generates a GUID using UUIDGEN.EXE, and writes out a complete IDL
-// file with the new CLSID.
-//
-// Background Information:
-// We generate new CLSIDs for each fresh build of Omaha. This allows us to "hot
-// swap" the BHO, and we don't face the issue of IE using the older version of
-// the BHO unless it is restarted.
-
-import "oaidl.idl";
-import "ocidl.idl";
-
-// MK keeps rebuilding unless there is an interface in the IDL file. Adding in a
-// dummy interface.
-[
-  object,
-  uuid(A91B50B2-48B3-40b1-86A3-0FE0AF3AB5BF),
-]
-interface INotUsedInterface : IUnknown {
-  HRESULT NotUsedFunction([in] BSTR not_used);
-};
-
-
-[
-  uuid(B137D75E-D05F-4146-AD27-C106D89FB4F4),
-  version(1.0)
-]
-library BhoDllLib {
-  [
-    uuid(%s)
-  ]
-  coclass BhoEntrypointClass {
-    [default] interface IObjectWithSite;
-  };
-}
-
diff --git a/bho/bho_dll.rc b/bho/bho_dll.rc
deleted file mode 100644
index 5542498..0000000
--- a/bho/bho_dll.rc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// BHO resource file.
-
-#include "omaha/bho/resource.h"
-
-#include "goopdate/goopdate_version.rc"
-
-IDR_BHO_ENTRY  REGISTRY  "bho_dll.rgs"
-
diff --git a/bho/bho_dll.rgs b/bho/bho_dll.rgs
deleted file mode 100644
index 1fe543e..0000000
--- a/bho/bho_dll.rgs
+++ /dev/null
@@ -1,28 +0,0 @@
-HKCR {
-  NoRemove CLSID {
-    ForceRemove '%CLSID%' = s '%PRODUCT% Helper' {
-      InprocServer32 = s '%MODULE%' {
-        val ThreadingModel = s 'Apartment'
-      }
-    }
-  }
-}
-
-HKLM {
-  NoRemove SOFTWARE {
-    NoRemove Microsoft {
-      NoRemove Windows {
-        NoRemove CurrentVersion {
-          NoRemove Explorer {
-            NoRemove 'Browser Helper Objects' {
-              ForceRemove '%CLSID%' = s '%PRODUCT% Helper' {
-                val 'NoExplorer' = d '1'
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-}
-
diff --git a/bho/bho_entrypoint.cc b/bho/bho_entrypoint.cc
deleted file mode 100644
index 2b96178..0000000
--- a/bho/bho_entrypoint.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// The main BHO COM object that IE instantiates. See the .h file for detailed
-// description.
-
-#include "omaha/bho/bho_entrypoint.h"
-#include "omaha/bho/browser_http_request.h"
-
-namespace omaha {
-
-BhoEntrypoint::BhoEntrypoint() {
-  CORE_LOG(L2, (_T("[BhoEntrypoint::BhoEntrypoint]")));
-}
-
-BhoEntrypoint::~BhoEntrypoint() {
-  CORE_LOG(L2, (_T("[BhoEntrypoint::~BhoEntrypoint]")));
-}
-
-STDMETHODIMP BhoEntrypoint::SetSite(IUnknown* site) {
-  CORE_LOG(L2, (_T("[BhoEntrypoint::SetSite]")));
-  HRESULT hr = IObjectWithSiteImpl<BhoEntrypoint>::SetSite(site);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[IObjectWithSiteImpl::SetSite failed][0x%x]"), hr));
-    return hr;
-  }
-
-  if (site) {
-    return BrowserHttpRequest::Init();
-  }
-
-  return hr;
-}
-
-}  // namespace omaha
-
diff --git a/bho/bho_entrypoint.h b/bho/bho_entrypoint.h
deleted file mode 100644
index 8a2f16f..0000000
--- a/bho/bho_entrypoint.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// The main BHO COM object that IE instantiates. Creates a thread for
-// registering itself with GoogleUpdate.exe. Does http requests on
-// GoogleUpdate.exe's behalf.
-
-#ifndef OMAHA_BHO_BHO_ENTRYPOINT_H__
-#define OMAHA_BHO_BHO_ENTRYPOINT_H__
-
-#include <atlbase.h>
-#include <atlcom.h>
-#include <atlctl.h>
-#include "bho/bho_dll.h"
-#include "omaha/bho/resource.h"
-#include "omaha/common/ATLRegMapEx.h"
-#include "omaha/common/const_config.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-
-namespace omaha {
-
-class ATL_NO_VTABLE BhoEntrypoint
-  : public CComObjectRootEx<CComObjectThreadModel>,
-    public CComCoClass<BhoEntrypoint, &__uuidof(BhoEntrypointClass)>,
-    public IObjectWithSiteImpl<BhoEntrypoint> {
- public:
-
-  DECLARE_REGISTRY_RESOURCEID_EX(IDR_BHO_ENTRY)
-  DECLARE_NOT_AGGREGATABLE(BhoEntrypoint)
-  DECLARE_PROTECT_FINAL_CONSTRUCT()
-
-  #pragma warning(push)
-  // C4640: construction of local static object is not thread-safe
-  #pragma warning(disable : 4640)
-  BEGIN_REGISTRY_MAP()
-    REGMAP_ENTRY(_T("PRODUCT"),           kCiProgram)
-    REGMAP_ENTRY(_T("CLSID"),             __uuidof(BhoEntrypointClass))
-  END_REGISTRY_MAP()
-  #pragma warning(pop)
-
-  BEGIN_COM_MAP(BhoEntrypoint)
-    COM_INTERFACE_ENTRY(IObjectWithSite)
-  END_COM_MAP()
-
-  BhoEntrypoint();
-  virtual ~BhoEntrypoint();
-
-  STDMETHODIMP SetSite(IUnknown* site);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_BHO_BHO_ENTRYPOINT_H__
-
diff --git a/bho/browser_http_request.cc b/bho/browser_http_request.cc
deleted file mode 100644
index 8c96c4d..0000000
--- a/bho/browser_http_request.cc
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Browser Http Request class. Created by BhoEntrypoint. Creates a thread that
-// registers with GoogleUpdate.exe, and waits for calls into the Send() method.
-
-#include "omaha/bho/browser_http_request.h"
-#include <atlsecurity.h>
-#include <atlstr.h>
-#include "omaha/common/error.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/net/bind_status_callback.h"
-
-namespace omaha {
-
-CComPtr<IBrowserHttpRequest2> BrowserHttpRequest::instance_;
-LLock BrowserHttpRequest::lock_;
-scoped_thread BrowserHttpRequest::request_thread_;
-SharedBrowserRequestObj* BrowserHttpRequest::shared_obj_ = NULL;
-
-// Use Urlmon to download.
-STDMETHODIMP BrowserHttpRequest::Send(BSTR url,
-                                      BSTR post_data,
-                                      BSTR request_headers,
-                                      VARIANT response_headers_needed,
-                                      VARIANT* response_headers,
-                                      DWORD* response_code,
-                                      BSTR* cache_filename) {
-  CORE_LOG(L2, (_T("[BrowserHttpRequest::Send][url \"%s\"]"), url));
-  return BindStatusCallback::CreateAndSend(url,
-                                           post_data,
-                                           request_headers,
-                                           response_headers_needed,
-                                           response_headers,
-                                           response_code,
-                                           cache_filename);
-}
-
-// Creates and register static instance of the BrowserHttpRequest, if it does
-// not exist.
-HRESULT BrowserHttpRequest::Init() {
-  CORE_LOG(L2, (_T("[BrowserHttpRequest::Register]")));
-
-  __mutexBlock(lock_) {
-    if (!request_thread_) {
-      // Start thread that accepts incoming COM requests from GoogleUpdate.
-      DWORD thread_id = 0;
-      reset(request_thread_,
-            ::CreateThread(NULL, 0, ProxyThreadProc, NULL, 0, &thread_id));
-      if (!request_thread_) {
-        HRESULT hr = HRESULTFromLastError();
-        CORE_LOG(LE, (_T("::CreateThread failed 0x%x"), hr));
-        return hr;
-      }
-      CORE_LOG(L2, (_T("[Init][request_thread_ id=%d]"), thread_id));
-    }
-  }
-
-  return S_OK;
-}
-
-DWORD WINAPI BrowserHttpRequest::ProxyThreadProc(LPVOID /* parameter */) {
-  CORE_LOG(L2, (_T("[BrowserHttpRequest::ProxyThreadProc]")));
-
-  scoped_co_init init_com_apt;
-  ASSERT1(SUCCEEDED(init_com_apt.hresult()));
-
-  if (FAILED(init_com_apt.hresult()) || FAILED(CreateAndRegister())) {
-    __mutexBlock(lock_) {
-      // Try to recreate the request thread later.
-      reset(request_thread_);
-    }
-    return 1;
-  }
-
-  // Message Loop
-  MSG msg = {0};
-  while (::GetMessage(&msg, NULL, 0, 0)) {
-    if (WM_QUIT == msg.message)
-      break;
-    ::TranslateMessage(&msg);
-    ::DispatchMessage(&msg);
-  }
-
-  // Since nobody posts a WM_QUIT message for this thread, shared_obj_ is
-  // leaked. This is ok, because the DLL is not unloaded until the process
-  // terminates, since we pin it. At that point, not releasing the shared_obj_
-  // is inconsequential. The OS will release the file mapping associated with
-  // the shared memory.
-  return 0;
-}
-
-HRESULT BrowserHttpRequest::CreateAndRegister() {
-  CORE_LOG(L2, (_T("[BrowserHttpRequest::CreateAndRegister]")));
-  if (instance_ != NULL) {
-    // Already exists. Exit early.
-    return S_OK;
-  }
-
-  CComObject<BrowserHttpRequest>* http_request = NULL;
-  HRESULT hr = CComObject<BrowserHttpRequest>::CreateInstance(&http_request);
-  CORE_LOG(L2, (_T("[BrowserHttpRequest CreateInstance returned 0x%x]"), hr));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  instance_ = http_request;
-  hr = RegisterInSharedMemory(instance_);
-  CORE_LOG(L2, (_T("[RegisterInSharedMemory returned 0x%x]"), hr));
-  if (FAILED(hr)) {
-    instance_ = NULL;
-  }
-
-  return hr;
-}
-
-// Insert the IBrowserHttpRequest2 interface into shared memory.
-HRESULT BrowserHttpRequest::RegisterInSharedMemory(
-            IBrowserHttpRequest2* http_request) {
-  ASSERT1(http_request);
-
-  // There is one browser helper per browser process. Each helper registers
-  // itself into a separate shared memory suffixed with the PID of the process.
-  // This is to keep the registration of each object distinct.
-  // For example,"Local\\IBrowserRequest2_2229".
-  // * GoogleUpdate.exe running as SYSTEM will choose the browser helper objects
-  // registered and running in the currently active session.
-  // * GoogleUpdate.exe running as the  user, either elevated or not, will
-  // choose browser helpers registered in the same session and running as that
-  // user.
-  CString shared_memory_name(_T("Local\\"));
-  shared_memory_name.AppendFormat(_T("%s%d"),
-                                  omaha::kBrowserHttpRequestShareName,
-                                  ::GetCurrentProcessId());
-  omaha::SharedMemoryAttributes attr(shared_memory_name, CSecurityDesc());
-  shared_obj_ = new SharedBrowserRequestObj(false, &attr);
-  ASSERT1(shared_obj_ != NULL);
-  return shared_obj_->RegisterObject(http_request);
-}
-
-}  // namespace omaha
diff --git a/bho/browser_http_request.h b/bho/browser_http_request.h
deleted file mode 100644
index 4a73b63..0000000
--- a/bho/browser_http_request.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Browser Http Request class. Created by BhoEntrypoint. Creates a thread that
-// registers with GoogleUpdate.exe, and waits for calls into the Send() method.
-
-#ifndef OMAHA_BHO_BROWSER_HTTP_REQUEST_H__
-#define OMAHA_BHO_BROWSER_HTTP_REQUEST_H__
-
-#include <atlbase.h>
-#include <atlcom.h>
-#include <atlctl.h>
-#include "base/scoped_ptr.h"
-#include "goopdate/google_update_idl.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/google_update_proxy.h"
-
-namespace omaha {
-
-typedef omaha::SharedMemoryProxy<IBrowserHttpRequest2,
-                                 FakeGLock> SharedBrowserRequestObj;
-
-class ATL_NO_VTABLE BrowserHttpRequest
-  : public CComObjectRootEx<CComObjectThreadModel>,
-    public IBrowserHttpRequest2 {
- public:
-  BrowserHttpRequest() {}
-  virtual ~BrowserHttpRequest() {
-    CORE_LOG(L2, (_T("[BrowserHttpRequest::~BrowserHttpRequest]")));
-  }
-
-  BEGIN_COM_MAP(BrowserHttpRequest)
-    COM_INTERFACE_ENTRY(IBrowserHttpRequest2)
-  END_COM_MAP()
-
-  DECLARE_PROTECT_FINAL_CONSTRUCT()
-
-  STDMETHOD(Send)(BSTR url,
-                  BSTR post_data,
-                  BSTR request_headers,
-                  VARIANT response_headers_needed,
-                  VARIANT* response_headers,
-                  DWORD* response_code,
-                  BSTR* cache_filename);
-
-  static HRESULT Init();
-
- private:
-  static DWORD WINAPI ProxyThreadProc(void* parameter);
-  static HRESULT CreateAndRegister();
-  static HRESULT RegisterInSharedMemory(IBrowserHttpRequest2* http_request);
-
-  static CComPtr<IBrowserHttpRequest2> instance_;
-  static LLock lock_;
-  static scoped_thread request_thread_;
-
-  // The browser http request shared with other processes.
-  static SharedBrowserRequestObj* shared_obj_;
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_BHO_BROWSER_HTTP_REQUEST_H__
-
diff --git a/bho/build.scons b/bho/build.scons
deleted file mode 100644
index 1f4d94e..0000000
--- a/bho/build.scons
+++ /dev/null
@@ -1,203 +0,0 @@
-#!/usr/bin/python2.4
-#
-# Copyright 2009 Google Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# ========================================================================
-
-
-Import('env')
-
-_first = True
-for omaha_version_info in env['omaha_versions_info']:
-  temp_env = env.Clone(COMPONENT_STATIC=False)
-
-  if _first:
-    _first = False
-    prefix = ''
-  else:
-    prefix = 'TEST_'
-    temp_env['OBJPREFIX'] = temp_env['OBJPREFIX'] + 'test/'
-
-  temp_env.Append(
-      CPPPATH = [
-          '$OBJ_ROOT',    # Needed for generated files.
-          ],
-      CPPDEFINES = [
-          '_ATL_APARTMENT_THREADED',
-          ],
-      LIBS = [
-          '$LIB_DIR/bho_dll.lib',
-          '$LIB_DIR/bho_dll_no_pch.lib',
-          '$LIB_DIR/breakpad.lib',
-          '$LIB_DIR/common.lib',
-          '$LIB_DIR/google_update_recovery.lib',
-          '$LIB_DIR/goopdate_dll.lib',
-          '$LIB_DIR/logging.lib',
-          '$LIB_DIR/net.lib',
-          '$LIB_DIR/repair_goopdate.lib',
-          '$LIB_DIR/statsreport.lib',
-          ('atls.lib', 'atlsd.lib')[temp_env.Bit('debug')],
-          ('libcmt.lib', 'libcmtd.lib')[temp_env.Bit('debug')],
-          ('libcpmt.lib', 'libcpmtd.lib')[temp_env.Bit('debug')],
-          'comctl32.lib',
-          'crypt32.lib',
-          'delayimp.lib',
-          'Iphlpapi.lib',
-          'msi.lib',
-          'Mstask.lib',
-          'Netapi32.lib',
-          'psapi.lib',
-          'rasapi32.lib',
-          'rpcrt4.lib',
-          'shlwapi.lib',
-          'Version.lib',
-          'userenv.lib',
-          'wininet.lib',
-          'urlmon.lib',
-          'Wintrust.lib',
-          'WtsApi32.lib',
-          ],
-      LINKFLAGS = [
-          '/DELAYLOAD:ole32.dll',
-          '/DELAYLOAD:oleaut32.dll',
-          '/DELAYLOAD:psapi.dll',
-          '/DELAYLOAD:shell32.dll',
-          '/DELAYLOAD:shlwapi.dll',
-          '/DELAYLOAD:userenv.dll',
-          '/DELAYLOAD:version.dll',
-          ],
-      RCFLAGS = [
-          '/DVERSION_MAJOR=%d' % omaha_version_info.version_major,
-          '/DVERSION_MINOR=%d' % omaha_version_info.version_minor,
-          '/DVERSION_BUILD=%d' % omaha_version_info.version_build,
-          '/DVERSION_PATCH=%d' % omaha_version_info.version_patch,
-          '/DVERSION_NUMBER_STRING=\\"%s\\"' % (
-              omaha_version_info.GetVersionString()),
-          ],
-  )
-
-  bho_res = temp_env.RES(
-      target=prefix+'bho_dll.res',
-      source='bho_dll.rc'
-  )
-
-  # Force a rebuild when the version changes.
-  temp_env.Depends(bho_res, '$MAIN_DIR/VERSION')
-
-  target_name = (prefix +
-                 omaha_version_info.bho_signed_file_info.unsigned_filename)
-
-  temp_inputs = [
-      'bho_dll.cc',
-      'bho_dll.def',
-      bho_res,
-      ]
-  if env.Bit('use_precompiled_headers'):
-    temp_inputs += temp_env.EnablePrecompile(target_name)
-
-  unsigned_dll = temp_env.ComponentLibrary(
-      lib_name=target_name,
-      source=temp_inputs,
-  )
-
-  signed_dll = temp_env.SignedBinary(
-      target=prefix + omaha_version_info.bho_signed_file_info.filename,
-      source=unsigned_dll,
-  )
-
-  env.Replicate('$STAGING_DIR', signed_dll)
-  env.Replicate('$STAGING_DIR', [f for f in unsigned_dll if f.suffix == '.pdb'])
-
-#
-# Build a new idl from the template *before* building the type library itself.
-#
-env.Command(
-    target='bho_dll.idl',
-    # Need to specify a path for the input, or Hammer gets confused by
-    # the identical file names.
-    source='$MAIN_DIR/bho/bho_dll.idl',
-    action=('@python %s/plugins/generate_oneclick_idl.py --idl_template_file'
-        ' $SOURCE --idl_output_file $TARGET' % (env.Dir('$MAIN_DIR').abspath))
-)
-
-
-#
-# Generate the type library
-#
-midl_env = env.Clone()
-
-midl_env.Tool('midl')
-
-midl_env.Append(
-  MIDLFLAGS = [
-    '/Oicf',  # Generate optimized stubless proxy/stub code.
-    ],
-)
-
-# Create the type library.
-midl_output = midl_env.TypeLibrary(source='$OBJ_ROOT/bho/bho_dll.idl')
-
-
-#
-# Build bho_dll_no_pch.lib
-# Need to generate the GUIDs with no precompile, otherwise we get an error.
-#
-env_no_precomp = env.Clone()
-
-env_no_precomp.Append(
-    CCFLAGS = [
-        '/wd4255',  # no function prototype given: converting '()' to '(void)'
-        ],
-)
-
-env_no_precomp.ComponentLibrary(
-    lib_name='bho_dll_no_pch.lib',
-    source=[
-        '$OBJ_ROOT/bho/bho_dll_i.c',
-        '$OBJ_ROOT/bho/bho_dll_p.c',
-        ]
-)
-
-
-#
-# Build bho_dll.lib.
-#
-env_main = env.Clone()
-
-env_main.Append(
-    CPPPATH = [
-        '$OBJ_ROOT',
-        ],
-    CPPDEFINES = [
-        '_ATL_APARTMENT_THREADED',
-        ],
-)
-
-target_name = 'bho_dll.lib'
-
-# Set up inputs. Note one of the inputs is a file generated by the midl
-# compiler in the previous step.
-env_main_inputs = [
-    'bho_entrypoint.cc',
-    'browser_http_request.cc',
-    ]
-if env.Bit('use_precompiled_headers'):
-  env_main_inputs += env_main.EnablePrecompile(target_name)
-
-main_output = env_main.ComponentLibrary(
-    lib_name=target_name,
-    source=env_main_inputs
-)
-
-
diff --git a/bho/resource.h b/bho/resource.h
deleted file mode 100644
index d28a52d..0000000
--- a/bho/resource.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// symbol definitions for the BHO DLL resources
-
-#ifndef OMAHA_BHO_RESOURCE_H__
-#define OMAHA_BHO_RESOURCE_H__
-
-// The id of the ATL COM registration script
-#define IDR_BHO_ENTRY                       100
-
-#endif  // OMAHA_BHO_RESOURCE_H__
-
diff --git a/client/build.scons b/client/build.scons
new file mode 100644
index 0000000..a9e51a4
--- /dev/null
+++ b/client/build.scons
@@ -0,0 +1,109 @@
+# Copyright 2010 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========================================================================
+
+Import('env')
+
+local_env = env.Clone()
+
+local_env['CPPPATH'] += [
+    '$OBJ_ROOT',  # Needed for generated files.
+    ]
+
+inputs = [
+    'bundle_creator.cc',
+    'bundle_installer.cc',
+    'client_metrics.cc',
+    'client_utils.cc',
+    'help_url_builder.cc',
+    'install.cc',
+    'install_apps.cc',
+    'install_self.cc',
+    'shutdown_events.cc',
+    'ua.cc',
+    ]
+
+# Build these into a library.
+local_env.ComponentStaticLibrary('client', inputs)
+
+no_xml_parser_test_env = env.Clone()
+no_xml_parser_test_env.OmahaUnittest(
+    name='install_self_no_xml_parser_unittest',
+    source=[
+        'install_self_unittest_no_xml_parser.cc',
+    ],
+    LIBS=[
+        '$LIB_DIR/breakpad.lib',
+        '$LIB_DIR/client.lib',
+        '$LIB_DIR/common.lib',
+        '$LIB_DIR/google_update_recovery.lib',
+        '$LIB_DIR/logging.lib',       # Required by statsreport.
+        '$LIB_DIR/service.lib',
+        '$LIB_DIR/setup.lib',
+        '$LIB_DIR/statsreport.lib',   # Required by anything using metrics.
+        '$LIB_DIR/ui.lib',
+        'crypt32.lib',    # Required by base.
+        'msi.lib',        # Required by setup.
+        'mstask.lib',     # Required by common.
+        'taskschd.lib',
+        'wininet.lib',
+        'wintrust.lib',   # Required by base.
+
+        # TODO(omaha3): These are required because setup must include
+        # service_main.h, which includes all the COM headers.
+        # The test source also includes fake definitions for IIDs from
+        # omaha3_idl, bits, and iphlpapi.lib.
+        '$LIB_DIR/core.lib',
+        '$LIB_DIR/goopdate_lib.lib',
+        '$LIB_DIR/net.lib',
+        '$LIB_DIR/security.lib',
+    ],
+    all_in_one=False,
+    COMPONENT_TEST_SIZE='small',
+)
+
+utils_test_env = env.Clone()
+utils_test_env.OmahaUnittest(
+    name='client_utils_unittest',
+    source=[
+        'client_utils_unittest.cc',
+    ],
+    LIBS=[
+        '$LIB_DIR/client.lib',
+        '$LIB_DIR/common.lib',
+        '$LIB_DIR/logging.lib',       # Required by statsreport.
+        '$LIB_DIR/omaha3_idl.lib',
+        '$LIB_DIR/statsreport.lib',
+        '$LIB_DIR/ui.lib',
+    ],
+    all_in_one=False,
+    COMPONENT_TEST_SIZE='small',
+    is_small_tests_using_resources=True,
+)
+
+help_url_builder_test_env = env.Clone()
+help_url_builder_test_env.OmahaUnittest(
+    name='help_url_builder_unittest',
+    source=[
+        'help_url_builder_test.cc',
+    ],
+    LIBS=[
+        '$LIB_DIR/client.lib',
+        '$LIB_DIR/common.lib',
+        '$LIB_DIR/omaha3_idl.lib',  # Required by common
+    ],
+    all_in_one=False,
+    COMPONENT_TEST_SIZE='small',
+    is_small_tests_using_resources=True,
+)
diff --git a/client/bundle_creator.cc b/client/bundle_creator.cc
new file mode 100644
index 0000000..49495f3
--- /dev/null
+++ b/client/bundle_creator.cc
@@ -0,0 +1,475 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/client/bundle_creator.h"
+#include <atlsafe.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vista_utils.h"
+#include "omaha/client/client_utils.h"
+#include "omaha/common/command_line.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/lang.h"
+#include "omaha/common/update3_utils.h"
+#include "goopdate/omaha3_idl.h"
+
+namespace omaha {
+
+namespace bundle_creator {
+
+namespace internal {
+
+// display_language and install_source can be empty.
+HRESULT SetBundleProperties(const CString& display_language,
+                            const CString& display_name,
+                            const CString& install_source,
+                            const CString& session_id,
+                            IAppBundle* app_bundle) {
+  ASSERT1(!display_name.IsEmpty());
+  ASSERT1(app_bundle);
+
+  CString process_language = lang::GetLanguageForProcess(display_language);
+  HRESULT hr = app_bundle->put_displayLanguage(CComBSTR(process_language));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = app_bundle->put_displayName(CComBSTR(display_name));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = app_bundle->put_sessionId(CComBSTR(session_id));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (!install_source.IsEmpty()) {
+    hr = app_bundle->put_installSource(CComBSTR(install_source));
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  return S_OK;
+}
+
+// Do not use the apps member of extra_args. Those values are handled by
+// PopulateAppSpecificData.
+HRESULT PopulateCommonData(const CommandLineExtraArgs& extra_args,
+                           bool is_eula_accepted,
+                           IApp* app) {
+  ASSERT1(app);
+
+  HRESULT hr = S_OK;
+
+  // Set as soon as possible so pings can occur in error cases.
+  hr = app->put_isEulaAccepted(is_eula_accepted ? VARIANT_TRUE : VARIANT_FALSE);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (!extra_args.language.IsEmpty()) {
+    hr = app->put_language(CComBSTR(extra_args.language));
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (!IsEqualGUID(GUID_NULL, extra_args.installation_id)) {
+    hr = app->put_iid(CComBSTR(GuidToString(extra_args.installation_id)));
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (!extra_args.brand_code.IsEmpty()) {
+    hr = app->put_brandCode(CComBSTR(extra_args.brand_code));
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (!extra_args.client_id.IsEmpty()) {
+    hr = app->put_clientId(CComBSTR(extra_args.client_id));
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (!extra_args.referral_id.IsEmpty()) {
+    hr = app->put_referralId(CComBSTR(extra_args.referral_id));
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (extra_args.browser_type != BROWSER_UNKNOWN) {
+    hr = app->put_browserType(extra_args.browser_type);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  hr = app->put_usageStatsEnable(extra_args.usage_stats_enable);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT PopulateAppSpecificData(const CommandLineAppArgs& app_args,
+                                IApp* app) {
+  ASSERT1(app);
+
+  HRESULT hr = app->put_displayName(CComBSTR(app_args.app_name));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (!app_args.ap.IsEmpty()) {
+    hr = app->put_ap(CComBSTR(app_args.ap));
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (!app_args.tt_token.IsEmpty()) {
+    hr = app->put_ttToken(CComBSTR(app_args.tt_token));
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (!app_args.encoded_installer_data.IsEmpty()) {
+    CString decoded_installer_data;
+    HRESULT hr = Utf8UrlEncodedStringToWideString(
+                     app_args.encoded_installer_data,
+                     &decoded_installer_data);
+    ASSERT(SUCCEEDED(hr), (_T("[Utf8UrlEncodedStringToWideString][0x%x]"), hr));
+    if (FAILED(hr) || CString(decoded_installer_data).Trim().IsEmpty()) {
+      return GOOPDATE_E_INVALID_INSTALLER_DATA_IN_APPARGS;
+    }
+
+    hr = app->put_clientInstallData(CComBSTR(decoded_installer_data));
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (!app_args.install_data_index.IsEmpty()) {
+    hr = app->put_serverInstallDataIndex(CComBSTR(app_args.install_data_index));
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (!app_args.experiment_labels.IsEmpty()) {
+    hr = app->put_labels(CComBSTR(app_args.experiment_labels));
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  return S_OK;
+}
+
+// Obtains tokens and passes them to put_altTokens when running as Local System.
+// Does not do anything for per-user instances or when not run as Local System.
+HRESULT SetAltTokens(bool is_machine, IAppBundle* app_bundle) {
+  ASSERT1(app_bundle);
+
+  if (!is_machine) {
+    return S_OK;
+  }
+
+  bool is_local_system = false;
+  HRESULT hr = IsSystemProcess(&is_local_system);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (!is_local_system) {
+    // Do not need alternate tokens.
+    return S_OK;
+  }
+
+  CAccessToken primary_token;
+  hr = primary_token.GetProcessToken(TOKEN_ALL_ACCESS) ?
+           S_OK : HRESULTFromLastError();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[GetProcessToken failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CAccessToken impersonation_token;
+  HANDLE user_token = NULL;
+
+  vista::GetLoggedOnUserToken(&user_token);
+
+  if (user_token) {
+    impersonation_token.Attach(user_token);
+  } else if (!primary_token.CreateImpersonationToken(&impersonation_token)) {
+    hr = HRESULTFromLastError();
+    CORE_LOG(LE, (_T("[CreateImpersonationToken failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = app_bundle->put_altTokens(
+      reinterpret_cast<ULONG_PTR>(impersonation_token.GetHandle()),
+      reinterpret_cast<ULONG_PTR>(primary_token.GetHandle()),
+      ::GetCurrentProcessId());
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[put_altTokens failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+}  // namespace internal
+
+HRESULT Create(bool is_machine,
+               const CString& display_language,
+               const CString& install_source,
+               const CString& session_id,
+               bool is_interactive,
+               IAppBundle** app_bundle) {
+  CORE_LOG(L2, (_T("[bundle_creator::Create]")));
+  ASSERT1(app_bundle);
+
+  CComPtr<IGoogleUpdate3> server;
+  HRESULT hr = update3_utils::CreateGoogleUpdate3Class(is_machine, &server);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CreateGoogleUpdate3Class][0x%08x]"), hr));
+    return hr;
+  }
+
+  CComPtr<IAppBundle> app_bundle_ptr;
+  hr = update3_utils::CreateAppBundle(server, &app_bundle_ptr);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CreateAppBundle failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  hr = internal::SetBundleProperties(display_language,
+                                     client_utils::GetUpdateAllAppsBundleName(),
+                                     install_source,
+                                     session_id,
+                                     app_bundle_ptr);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = internal::SetAltTokens(is_machine, app_bundle_ptr);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = app_bundle_ptr->put_priority(is_interactive ?
+                                        INSTALL_PRIORITY_HIGH :
+                                        INSTALL_PRIORITY_LOW);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = app_bundle_ptr->initialize();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  *app_bundle = app_bundle_ptr.Detach();
+  return S_OK;
+}
+
+HRESULT CreateFromCommandLine(bool is_machine,
+                              bool is_eula_accepted,
+                              bool is_offline,
+                              const CString& offline_directory,
+                              const CommandLineExtraArgs& extra_args,
+                              const CString& install_source,
+                              const CString& session_id,
+                              bool is_interactive,
+                              IAppBundle** app_bundle) {
+  CORE_LOG(L2, (_T("[bundle_creator::CreateFromCommandLine]")));
+  ASSERT1(app_bundle);
+
+  CComPtr<IGoogleUpdate3> server;
+  HRESULT hr = update3_utils::CreateGoogleUpdate3Class(is_machine, &server);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CreateGoogleUpdate3Class][0x%08x]"), hr));
+    return hr;
+  }
+
+  CComPtr<IAppBundle> app_bundle_ptr;
+  hr = update3_utils::CreateAppBundle(server, &app_bundle_ptr);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CreateAppBundle failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  hr = internal::SetBundleProperties(extra_args.language,
+                                     extra_args.bundle_name,
+                                     install_source,
+                                     session_id,
+                                     app_bundle_ptr);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (is_offline) {
+    CString offline_dir(offline_directory);
+    if (offline_dir.IsEmpty()) {
+      // For Omaha2 compatibility.
+      offline_dir = is_machine ?
+          ConfigManager::Instance()->GetMachineSecureOfflineStorageDir() :
+          ConfigManager::Instance()->GetUserOfflineStorageDir();
+    }
+
+    hr = app_bundle_ptr->put_offlineDirectory(CComBSTR(offline_dir));
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  hr = internal::SetAltTokens(is_machine, app_bundle_ptr);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = app_bundle_ptr->put_priority(is_interactive ?
+                                        INSTALL_PRIORITY_HIGH :
+                                        INSTALL_PRIORITY_LOW);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = app_bundle_ptr->initialize();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  for (size_t i = 0; i < extra_args.apps.size(); ++i) {
+    const CommandLineAppArgs& app_args(extra_args.apps[i]);
+
+    const CComBSTR app_id(GuidToString(app_args.app_guid));
+
+    CComPtr<IApp> app;
+    hr = update3_utils::CreateApp(app_id, app_bundle_ptr, &app);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = internal::PopulateCommonData(extra_args, is_eula_accepted, app);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = internal::PopulateAppSpecificData(app_args, app);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  *app_bundle = app_bundle_ptr.Detach();
+  return S_OK;
+}
+
+HRESULT CreateForOnDemand(bool is_machine,
+                          const CString& app_id,
+                          const CString& install_source,
+                          const CString& session_id,
+                          HANDLE impersonation_token,
+                          HANDLE primary_token,
+                          IAppBundle** app_bundle) {
+  CORE_LOG(L2, (_T("[bundle_creator::CreateForOnDemand]")));
+  ASSERT1(app_bundle);
+
+  CComPtr<IGoogleUpdate3> server;
+  HRESULT hr = update3_utils::CreateGoogleUpdate3Class(is_machine, &server);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CreateGoogleUpdate3Class failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<IAppBundle> app_bundle_ptr;
+  hr = update3_utils::CreateAppBundle(server, &app_bundle_ptr);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CreateAppBundle failed][0x%x]"), hr));
+    return hr;
+  }
+
+  // ::CoSetProxyBlanket() settings are per proxy. For OnDemand, after
+  // unmarshaling the interface, we need to set the blanket on this new proxy.
+  // The proxy blanket on the IAppBundle interface are set explicitly only for
+  // OnDemand, because OnDemand is a unique case of being a COM server as well
+  // as a COM client. The default security settings set for the OnDemand COM
+  // server are more restrictive and rightly so, as compared to the settings
+  // that we set for a COM client such as our Omaha3 UI. Hence the need to
+  // explicitly set the proxy blanket settings and lower the security
+  // requirements only when calling out on this interface.
+  hr = update3_utils::SetProxyBlanketAllowImpersonate(app_bundle_ptr);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (is_machine) {
+    hr = app_bundle_ptr->put_altTokens(
+        reinterpret_cast<ULONG_PTR>(impersonation_token),
+        reinterpret_cast<ULONG_PTR>(primary_token),
+        ::GetCurrentProcessId());
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[put_altTokens failed][0x%x]"), hr));
+      return hr;
+    }
+  }
+
+  hr = internal::SetBundleProperties(CString(),
+                                     _T("On Demand Bundle"),
+                                     install_source,
+                                     session_id,
+                                     app_bundle_ptr);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = app_bundle_ptr->initialize();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CComPtr<IApp> app;
+  hr = update3_utils::CreateInstalledApp(CComBSTR(app_id),
+                                         app_bundle_ptr,
+                                         &app);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CreateInstalledApp failed][0x%x]"), hr));
+    return hr;
+  }
+
+  *app_bundle = app_bundle_ptr.Detach();
+  return S_OK;
+}
+
+}  // namespace bundle_creator
+
+}  // namespace omaha
diff --git a/client/bundle_creator.h b/client/bundle_creator.h
new file mode 100644
index 0000000..36d21d3
--- /dev/null
+++ b/client/bundle_creator.h
@@ -0,0 +1,66 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_CLIENT_BUNDLE_CREATOR_H_
+#define OMAHA_CLIENT_BUNDLE_CREATOR_H_
+
+#include <windows.h>
+#include <atlsafe.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "goopdate/omaha3_idl.h"
+
+namespace omaha {
+
+struct CommandLineExtraArgs;
+
+namespace bundle_creator {
+
+// Creates app bundle interface, initializes it with default properties and
+// the given install source.
+HRESULT Create(bool is_machine,
+               const CString& display_language,
+               const CString& install_source,
+               const CString& session_id,
+               bool is_interactive,
+               IAppBundle** app_bundle);
+
+// Creates app bundle interface that contains the apps specified in extra_args.
+// The apps properties are intialized based on the extra_args and other
+// arguments.
+HRESULT CreateFromCommandLine(bool is_machine,
+                              bool is_eula_accepted,
+                              bool is_offline,
+                              const CString& offline_directory,
+                              const CommandLineExtraArgs& extra_args,
+                              const CString& install_source,
+                              const CString& session_id,
+                              bool is_interactive,
+                              IAppBundle** app_bundle);
+
+// Creates app bundle interface that contains the given app (app_id).
+HRESULT CreateForOnDemand(bool is_machine,
+                          const CString& app_id,
+                          const CString& install_source,
+                          const CString& session_id,
+                          HANDLE impersonation_token,
+                          HANDLE primary_token,
+                          IAppBundle** app_bundle);
+
+}  // namespace bundle_creator
+
+}  // namespace omaha
+
+#endif  // OMAHA_CLIENT_BUNDLE_CREATOR_H_
diff --git a/client/bundle_creator_test.cc b/client/bundle_creator_test.cc
new file mode 100644
index 0000000..aa82c3a
--- /dev/null
+++ b/client/bundle_creator_test.cc
@@ -0,0 +1,386 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/app_util.h"
+#include "omaha/base/browser_utils.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/system.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/client/bundle_creator.h"
+#include "omaha/client/client_utils.h"
+#include "omaha/common/command_line.h"
+#include "omaha/common/command_line_builder.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/update3_utils.h"
+#include "omaha/goopdate/goopdate.h"
+#include "omaha/goopdate/resource_manager.h"
+#include "omaha/goopdate/worker.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+const TCHAR kUpdateDevKey[]               = MACHINE_REG_UPDATE;
+const TCHAR kMachineGoopdateClientsKey[]  = MACHINE_REG_CLIENTS_GOOPDATE;
+const TCHAR kMachineGoopdateStateKey[]    = MACHINE_REG_CLIENT_STATE_GOOPDATE;
+
+}   // namespace
+
+class BundleCreatorTest : public testing::Test {
+ protected:
+  BundleCreatorTest() {}
+
+  static void SetUpTestCase() {
+    // Goopdate instance is required by Worker instance.
+    goopdates_.reset(new Goopdate(true));
+
+    EXPECT_SUCCEEDED(ResourceManager::Create(
+        true, app_util::GetCurrentModuleDirectory(), _T("en")));
+
+    const CString shell_path = goopdate_utils::BuildGoogleUpdateExePath(true);
+    EXPECT_SUCCEEDED(RegKey::SetValue(kUpdateDevKey,
+                                      kRegValueInstalledPath,
+                                      shell_path));
+
+    EXPECT_SUCCEEDED(RegKey::SetValue(kUpdateDevKey,
+                                      kRegValueInstalledVersion,
+                                      GetVersionString()));
+
+    EXPECT_SUCCEEDED(RegKey::SetValue(kMachineGoopdateClientsKey,
+                                      kRegValueProductVersion,
+                                      GetVersionString()));
+
+    EXPECT_SUCCEEDED(RegKey::SetValue(kMachineGoopdateStateKey,
+                                      kRegValueProductVersion,
+                                      GetVersionString()));
+
+    CopyGoopdateFiles(GetGoogleUpdateMachinePath(), GetVersionString());
+  }
+
+  static void TearDownTestCase() {
+    ResourceManager::Delete();
+
+    // bundle_creator::Create*() methods indirectly creates Worker instance,
+    // delete it here (to avoid interference with other tests).
+    Worker::DeleteInstance();
+
+    goopdates_.reset();
+  }
+
+  virtual void SetUp() {
+    RegisterOrUnregisterGoopdateLocalServer(true);
+  }
+
+  virtual void TearDown() {
+    RegisterOrUnregisterGoopdateLocalServer(false);
+  }
+
+  static void VerifyAppMatchesCommandLineArguments(
+      const CommandLineExtraArgs extra_arg,
+      const CommandLineAppArgs& app_arg,
+      IApp* app) {
+    // Verify common data.
+    CComBSTR language;
+    EXPECT_SUCCEEDED(app->get_language(&language));
+    EXPECT_STREQ(extra_arg.language, language);
+
+    CComBSTR iid;
+    EXPECT_SUCCEEDED(app->get_iid(&iid));
+
+    GUID installation_id = {0};
+    EXPECT_SUCCEEDED(StringToGuidSafe(CString(iid), &installation_id));
+    EXPECT_TRUE(extra_arg.installation_id == installation_id);
+
+    CComBSTR brand_code;
+    EXPECT_SUCCEEDED(app->get_brandCode(&brand_code));
+    EXPECT_STREQ(extra_arg.brand_code, brand_code);
+
+    CComBSTR client_id;
+    EXPECT_SUCCEEDED(app->get_clientId(&client_id));
+    EXPECT_STREQ(extra_arg.client_id, client_id);
+
+    CComBSTR referral_id;
+    EXPECT_SUCCEEDED(app->get_referralId(&referral_id));
+    EXPECT_STREQ(extra_arg.referral_id, referral_id);
+
+    UINT browser_type = 0;
+    EXPECT_SUCCEEDED(app->get_browserType(&browser_type));
+    EXPECT_EQ(extra_arg.browser_type, browser_type);
+
+    // Verify app specific data.
+    CComBSTR app_name;
+    EXPECT_SUCCEEDED(app->get_displayName(&app_name));
+    EXPECT_STREQ(app_arg.app_name, app_name);
+
+    CComBSTR tt_token;
+    EXPECT_SUCCEEDED(app->get_ttToken(&tt_token));
+    EXPECT_STREQ(app_arg.tt_token, tt_token);
+
+    CComBSTR ap;
+    EXPECT_SUCCEEDED(app->get_ap(&ap));
+    EXPECT_STREQ(app_arg.ap, ap);
+  }
+
+  static void CreateAppRegistryState(const CString& app_id, bool is_machine) {
+    CString clients_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->registry_clients(is_machine),
+        app_id);
+    RegKey client_key;
+    ASSERT_SUCCEEDED(client_key.Create(clients_key_name));
+
+    CString current_version(_T("1.0.0.0"));
+    ASSERT_SUCCEEDED(client_key.SetValue(kRegValueProductVersion,
+                                         current_version));
+  }
+
+  static void RemoveAppRegistryState(const CString& app_id, bool is_machine) {
+    CString clients_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->registry_clients(is_machine),
+        app_id);
+    EXPECT_SUCCEEDED(RegKey::DeleteKey(clients_key_name));
+  }
+
+  static scoped_ptr<Goopdate> goopdates_;
+};
+
+scoped_ptr<Goopdate> BundleCreatorTest::goopdates_;
+
+TEST_F(BundleCreatorTest, Create) {
+  const CString kDisplayLanguage = _T("en");
+  const CString kInstallSource = _T("TestInstallSource");
+  const CString kSessionId = _T("{6cb069db-b073-4a40-9983-846a3819876a}");
+  const bool is_machine = true;
+  const bool is_interactive = false;
+
+  CComPtr<IAppBundle> app_bundle;
+  ASSERT_SUCCEEDED(bundle_creator::Create(is_machine,
+                                          kDisplayLanguage,
+                                          kInstallSource,
+                                          kSessionId,
+                                          is_interactive,
+                                          &app_bundle));
+  CComBSTR display_name;
+  EXPECT_SUCCEEDED(app_bundle->get_displayName(&display_name));
+  EXPECT_STREQ(client_utils::GetUpdateAllAppsBundleName(), display_name);
+
+  CComBSTR install_source;
+  EXPECT_SUCCEEDED(app_bundle->get_installSource(&install_source));
+  EXPECT_STREQ(kInstallSource, install_source);
+
+  CComBSTR session_id;
+  EXPECT_SUCCEEDED(app_bundle->get_sessionId(&session_id));
+  EXPECT_STREQ(kSessionId, session_id);
+
+  long num_apps = 0;  // NOLINT(runtime/int)
+  EXPECT_SUCCEEDED(app_bundle->get_Count(&num_apps));
+  EXPECT_EQ(0, num_apps);
+
+  long priority = INSTALL_PRIORITY_LOW;  // NOLINT(runtime/int)
+  EXPECT_SUCCEEDED(app_bundle->get_priority(&priority));
+  EXPECT_EQ(is_interactive ? INSTALL_PRIORITY_HIGH : INSTALL_PRIORITY_LOW,
+            priority);
+
+  CComBSTR display_language;
+  EXPECT_SUCCEEDED(app_bundle->get_displayLanguage(&display_language));
+  EXPECT_STREQ(kDisplayLanguage, display_language);
+}
+
+TEST_F(BundleCreatorTest, CreateFromCommandLine) {
+  const CString kDisplayLanguage = _T("en");
+  const CString kTestBundleName = _T("CommandLineTestBundle");
+  const GUID kInstallationId = {
+    0x9a67c0e6, 0xe6f6, 0x400d,
+    {0xa4, 0x30, 0xe3, 0xfe, 0x54, 0xcc, 0x10, 0x43}
+  };
+  const CString kBrandCode = _T("GOOG");
+  const CString kClientId = _T("TestClient");
+  const CString kReferralId = _T("TestReferral");
+  const BrowserType kBrowserType = BROWSER_CHROME;
+
+  const CString kInstallSource = _T("TestInstallSourceCmdLine");
+  const CString kSessionId = _T("{6cb069db-b073-4a40-9983-846a3819876a}");
+  const bool is_machine = true;
+  const bool is_interactive = true;
+  const bool is_eula_accepted = true;
+  const bool is_offline = true;
+  const CString offline_directory = _T("C:\\GoogleUpdateUnitTest");
+
+  const GUID kApp1Id = {
+    0x433bd902, 0x6c0d, 0x4115,
+    {0x97, 0xe8, 0x4f, 0xa8, 0x2e, 0x1f, 0x4b, 0x8f}
+  };
+  const CString kApp1Name = _T("Test App1");
+  const CString kApp1AdditionalParameter = _T("App1 AP");
+  const CString kApp1Tttoken = _T("T1");
+
+  const GUID kApp2Id = {
+    0x83ed8a95, 0xc4e2, 0x4da8,
+    {0xbd, 0x0c, 0x00, 0xb9, 0xdf, 0xac, 0x6c, 0x88}
+  };
+  const CString kApp2Name = _T("Test App2");
+  const CString kApp2AdditionalParameter = _T("App2 AP");
+  const CString kApp2Tttoken = _T("T2");
+
+  CommandLineAppArgs app1;
+  app1.app_guid = kApp1Id;
+  app1.app_name = kApp1Name;
+  app1.needs_admin = NEEDS_ADMIN_YES;
+  app1.ap = kApp1AdditionalParameter;
+  app1.tt_token = kApp1Tttoken;
+
+  CommandLineAppArgs app2;
+  app2.app_guid = kApp2Id;
+  app2.app_name = kApp2Name;
+  app2.needs_admin = NEEDS_ADMIN_NO;
+  app2.ap = kApp2AdditionalParameter;
+  app2.tt_token = kApp2Tttoken;
+
+  CommandLineExtraArgs extra_args;
+  extra_args.bundle_name = kTestBundleName;
+  extra_args.installation_id = kInstallationId;
+  extra_args.brand_code = kBrandCode;
+  extra_args.client_id = kClientId;
+  extra_args.referral_id = kReferralId;
+  extra_args.language = kDisplayLanguage;
+  extra_args.browser_type = kBrowserType;
+  extra_args.apps.push_back(app1);
+  extra_args.apps.push_back(app2);
+
+  CComPtr<IAppBundle> app_bundle;
+  ASSERT_SUCCEEDED(bundle_creator::CreateFromCommandLine(
+      is_machine,
+      is_eula_accepted,
+      is_offline,
+      offline_directory,
+      extra_args,
+      kInstallSource,
+      kSessionId,
+      is_interactive,
+      &app_bundle));
+
+  CComBSTR display_name;
+  EXPECT_SUCCEEDED(app_bundle->get_displayName(&display_name));
+  EXPECT_STREQ(kTestBundleName, display_name);
+
+  CComBSTR install_source;
+  EXPECT_SUCCEEDED(app_bundle->get_installSource(&install_source));
+  EXPECT_STREQ(kInstallSource, install_source);
+
+  CComBSTR session_id;
+  EXPECT_SUCCEEDED(app_bundle->get_sessionId(&session_id));
+  EXPECT_STREQ(kSessionId, session_id);
+
+  long priority = INSTALL_PRIORITY_LOW;  // NOLINT(runtime/int)
+  EXPECT_SUCCEEDED(app_bundle->get_priority(&priority));
+  EXPECT_EQ(is_interactive ? INSTALL_PRIORITY_HIGH : INSTALL_PRIORITY_LOW,
+            priority);
+
+  CComBSTR display_language;
+  EXPECT_SUCCEEDED(app_bundle->get_displayLanguage(&display_language));
+  EXPECT_STREQ(kDisplayLanguage, display_language);
+
+  long num_apps = 0;  // NOLINT(runtime/int)
+  EXPECT_SUCCEEDED(app_bundle->get_Count(&num_apps));
+  EXPECT_EQ(2, num_apps);
+
+  for (long i = 0; i < num_apps; ++i) {  // NOLINT(runtime/int)
+    CComPtr<IApp> app;
+    EXPECT_SUCCEEDED(update3_utils::GetApp(app_bundle, i, &app));
+
+    CComBSTR app_id;
+    EXPECT_SUCCEEDED(app->get_appId(&app_id));
+    GUID app_guid = {0};
+    EXPECT_SUCCEEDED(StringToGuidSafe(CString(app_id), &app_guid));
+
+    if (app_guid == kApp1Id) {
+      VerifyAppMatchesCommandLineArguments(extra_args, app1, app);
+    } else {
+      EXPECT_TRUE(kApp2Id == app_guid);
+      VerifyAppMatchesCommandLineArguments(extra_args, app2, app);
+    }
+  }
+}
+
+TEST_F(BundleCreatorTest, CreateForOnDemand) {
+  const CString& kAppId = _T("{5dace97e-9d8f-430b-acc7-ef04708b4725}");
+  const CString kInstallSource = _T("TestInstallSourceOnDemand");
+  const CString kSessionId = _T("{6cb069db-b073-4a40-9983-846a3819876a}");
+  const bool is_machine = true;
+
+  // Create app registry key to make it "installed".
+  CreateAppRegistryState(kAppId, is_machine);
+
+  CAccessToken process_token;
+  if (is_machine) {
+    process_token.GetEffectiveToken(TOKEN_ALL_ACCESS);
+  }
+
+  CComPtr<IAppBundle> app_bundle;
+  HRESULT hr = bundle_creator::CreateForOnDemand(is_machine,
+                                                 kAppId,
+                                                 kInstallSource,
+                                                 kSessionId,
+                                                 process_token.GetHandle(),
+                                                 process_token.GetHandle(),
+                                                 &app_bundle);
+  RemoveAppRegistryState(kAppId, is_machine);
+  ASSERT_SUCCEEDED(hr);
+
+  CComBSTR install_source;
+  EXPECT_SUCCEEDED(app_bundle->get_installSource(&install_source));
+  EXPECT_STREQ(kInstallSource, install_source);
+
+  CComBSTR session_id;
+  EXPECT_SUCCEEDED(app_bundle->get_sessionId(&session_id));
+  EXPECT_STREQ(kSessionId, session_id);
+
+  long num_apps = 0;  // NOLINT(runtime/int)
+  EXPECT_SUCCEEDED(app_bundle->get_Count(&num_apps));
+  EXPECT_EQ(1, num_apps);
+
+  long priority = INSTALL_PRIORITY_LOW;  // NOLINT(runtime/int)
+  EXPECT_SUCCEEDED(app_bundle->get_priority(&priority));
+  EXPECT_EQ(INSTALL_PRIORITY_HIGH, priority);
+}
+
+TEST_F(BundleCreatorTest, CreateForOnDemand_NonExistApp) {
+  const CString& kAppId = _T("{52e24bf9-d7d0-4b6e-b12d-9cef51fa45f2}");
+  const CString kInstallSource = _T("TestInstallSourceOnDemand");
+  const CString kSessionId = _T("{6cb069db-b073-4a40-9983-846a3819876a}");
+  const bool is_machine = true;
+
+  CAccessToken process_token;
+  if (is_machine) {
+    process_token.GetEffectiveToken(TOKEN_ALL_ACCESS);
+  }
+
+  CComPtr<IAppBundle> app_bundle;
+  EXPECT_FAILED(bundle_creator::CreateForOnDemand(
+      is_machine,
+      kAppId,
+      kInstallSource,
+      kSessionId,
+      process_token.GetHandle(),
+      process_token.GetHandle(),
+      &app_bundle));
+}
+
+}  // namespace omaha
diff --git a/client/bundle_installer.cc b/client/bundle_installer.cc
new file mode 100644
index 0000000..970e67a
--- /dev/null
+++ b/client/bundle_installer.cc
@@ -0,0 +1,1138 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/client/bundle_installer.h"
+#include <atlsafe.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/client/client_utils.h"
+#include "omaha/client/help_url_builder.h"
+#include "omaha/client/resource.h"
+#include "omaha/client/shutdown_events.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/update3_utils.h"
+#include "goopdate/omaha3_idl.h"
+
+namespace omaha {
+
+namespace internal {
+
+CString GetAppDisplayName(IApp* app) {
+  ASSERT1(app);
+
+  CComBSTR app_name;
+  HRESULT hr = app->get_displayName(&app_name);
+  if (SUCCEEDED(hr)) {
+    ASSERT1(app_name.Length());
+    if (app_name.Length()) {
+      return CString(app_name);
+    }
+  } else {
+    CORE_LOG(LW, (_T("[get_displayName failed][0x%08x]"), hr));
+  }
+  return client_utils::GetDefaultApplicationName();
+}
+
+CString BuildAppNameList(const std::vector<CString>& app_names) {
+  ASSERT1(!app_names.empty());
+
+  CString list = app_names[0];
+  for (size_t i = 1; i < app_names.size(); ++i) {
+    list.FormatMessage(IDS_APPLICATION_NAME_CONCATENATION, list, app_names[i]);
+  }
+
+  return list;
+}
+
+CompletionCodes ConvertPostInstallActionToCompletionCode(
+    PostInstallAction post_install_action,
+    bool is_browser_type_supported,
+    bool is_error_state) {
+  switch (post_install_action) {
+    case POST_INSTALL_ACTION_EXIT_SILENTLY_ON_LAUNCH_COMMAND:
+      return COMPLETION_CODE_EXIT_SILENTLY_ON_LAUNCH_COMMAND;
+
+    case POST_INSTALL_ACTION_LAUNCH_COMMAND:
+      return COMPLETION_CODE_LAUNCH_COMMAND;
+
+    case POST_INSTALL_ACTION_RESTART_BROWSER:
+      if (is_browser_type_supported) {
+        return COMPLETION_CODE_RESTART_BROWSER;
+      } else {
+        return COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY;
+      }
+
+    case POST_INSTALL_ACTION_RESTART_ALL_BROWSERS:
+      if (is_browser_type_supported) {
+        return COMPLETION_CODE_RESTART_ALL_BROWSERS;
+      } else {
+        return COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY;
+      }
+
+    case POST_INSTALL_ACTION_REBOOT:
+      // We don't support reboot, always notice_only
+      return COMPLETION_CODE_REBOOT_NOTICE_ONLY;
+
+    case POST_INSTALL_ACTION_EXIT_SILENTLY:
+      return COMPLETION_CODE_EXIT_SILENTLY;
+
+    case POST_INSTALL_ACTION_DEFAULT:
+      if (is_error_state) {
+        return COMPLETION_CODE_ERROR;
+      } else {
+        return COMPLETION_CODE_SUCCESS;
+      }
+
+    default:
+      ASSERT1(false);
+      return COMPLETION_CODE_SUCCESS;
+  }
+}
+
+HRESULT GetCompletionInformation(IApp* app,
+                                 CurrentState* current_state,
+                                 AppCompletionInfo* completion_info,
+                                 bool is_browser_type_supported) {
+  ASSERT1(app);
+  ASSERT1(current_state);
+  ASSERT1(completion_info);
+
+  *current_state = STATE_INIT;
+
+  // If the COM call fails, which may be why we encountered an error, this
+  // could be "Google Application", which is weird, especially in the mixed
+  // results UI.
+  completion_info->display_name = internal::GetAppDisplayName(app);
+
+  CComBSTR app_id;
+  HRESULT hr = app->get_appId(&app_id);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[get_appId failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  completion_info->app_id = app_id;
+
+  CComPtr<ICurrentState> icurrent_state;
+  hr = update3_utils::GetAppCurrentState(app, current_state, &icurrent_state);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[GetNextVersionState failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  ASSERT1(*current_state == STATE_INSTALL_COMPLETE ||
+          *current_state == STATE_NO_UPDATE ||
+          *current_state == STATE_ERROR);
+
+  hr = icurrent_state->get_errorCode(&completion_info->error_code);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  LONG extra_code1 = 0;
+  hr = icurrent_state->get_extraCode1(&extra_code1);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  completion_info->extra_code1 = static_cast<int>(extra_code1);
+
+  CComBSTR message;
+  hr = icurrent_state->get_completionMessage(&message);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  completion_info->completion_message = message;
+
+  LONG installer_result = 0;
+  hr = icurrent_state->get_installerResultCode(&installer_result);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  completion_info->installer_result_code = installer_result;
+
+  VARIANT_BOOL is_canceled = VARIANT_TRUE;
+  hr = icurrent_state->get_isCanceled(&is_canceled);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  completion_info->is_canceled = (is_canceled == VARIANT_TRUE);
+
+  CComBSTR post_install_launch_command_line;
+  hr = icurrent_state->get_postInstallLaunchCommandLine(
+           &post_install_launch_command_line);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  completion_info->post_install_launch_command_line =
+      post_install_launch_command_line;
+
+  CORE_LOG(L5, (_T("[GetCompletionInformation][%s][state: u][code: 0x%08x]")
+                _T("[extra code: %d][message: '%s'][installer: %u]"),
+                app_id, *current_state,
+                completion_info->error_code,
+                completion_info->extra_code1,
+                message, installer_result));
+
+  CComBSTR post_install_url;
+  hr = icurrent_state->get_postInstallUrl(&post_install_url);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  completion_info->post_install_url = post_install_url;
+
+  LONG post_install_action = static_cast<LONG>(POST_INSTALL_ACTION_DEFAULT);
+  hr = icurrent_state->get_postInstallAction(&post_install_action);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  completion_info->completion_code = ConvertPostInstallActionToCompletionCode(
+      static_cast<PostInstallAction>(post_install_action),
+      is_browser_type_supported,
+      *current_state == STATE_ERROR);
+
+  ASSERT1(SUCCEEDED(completion_info->error_code) ||
+          completion_info->installer_result_code == 0 ||
+          completion_info->error_code == GOOPDATEINSTALL_E_INSTALLER_FAILED);
+
+  return S_OK;
+}
+
+void GetAppCompletionMessage(IApp* app,
+                             AppCompletionInfo* app_info,
+                             bool is_browser_type_supported) {
+  ASSERT1(app);
+  ASSERT1(app_info);
+
+  CurrentState current_state = STATE_INIT;
+
+  HRESULT hr = GetCompletionInformation(app,
+                                        &current_state,
+                                        app_info,
+                                        is_browser_type_supported);
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[GetCompletionInformation failed][0x%08x]"), hr));
+    // Treat the failure as an app failure.
+    app_info->completion_message.FormatMessage(
+        IDS_INSTALL_FAILED_WITH_ERROR_CODE,
+        hr);
+    app_info->error_code = hr;
+    app_info->extra_code1 = 0;
+    return;
+  }
+
+  ASSERT1(current_state == STATE_INSTALL_COMPLETE ||
+          current_state == STATE_NO_UPDATE ||
+          current_state == STATE_ERROR);
+  ASSERT1(!app_info->completion_message.IsEmpty());
+  ASSERT1(current_state != STATE_NO_UPDATE ||
+          app_info->error_code == S_OK     ||
+          app_info->error_code == GOOPDATE_E_UPDATE_DEFERRED);
+
+  app_info->is_noupdate = current_state == STATE_NO_UPDATE;
+
+  // If it is not a success or noupdate, we must report a failure in error_code.
+  if (current_state != STATE_INSTALL_COMPLETE &&
+      current_state != STATE_NO_UPDATE) {
+    ASSERT1(FAILED(app_info->error_code));
+    if (SUCCEEDED(app_info->error_code)) {
+      app_info->error_code = E_FAIL;
+      app_info->extra_code1 = 0;
+    }
+  }
+
+  if (!app_info->completion_message.IsEmpty()) {
+    return;
+  }
+
+  ASSERT(false, (_T("There should always be a completion message.")));
+
+  // The message is empty. Return a default message.
+  if (current_state == STATE_INSTALL_COMPLETE) {
+    app_info->completion_message.LoadString(
+        IDS_APPLICATION_INSTALLED_SUCCESSFULLY);
+  } else if (current_state == STATE_NO_UPDATE) {
+    VERIFY1(app_info->completion_message.LoadString(IDS_NO_UPDATE_RESPONSE));
+  } else {
+    ASSERT1(current_state == STATE_ERROR);
+    app_info->completion_message.FormatMessage(
+        IDS_INSTALL_FAILED_WITH_ERROR_CODE,
+        E_FAIL);
+  }
+}
+
+CString BuildResultStringForApps(uint32 group_name_resource_id,
+                                 const std::vector<CString>& apps) {
+  CString app_result_string;
+  ASSERT1(!apps.empty());
+
+  // The header (i.e. "Failed:") should be in bold.
+  const TCHAR* const kAppListHeaderFormatOpen = _T("<b>");
+  const TCHAR* const kAppListHeaderFormatClose = _T("</b>");
+
+  CString apps_list = internal::BuildAppNameList(apps);
+  app_result_string.FormatMessage(group_name_resource_id,
+                                  kAppListHeaderFormatOpen,
+                                  kAppListHeaderFormatClose,
+                                  apps_list);
+  return app_result_string;
+}
+
+// TODO(omaha): If we end up leaving is_noupdate in AppCompletionInfo,
+// eliminate is_only_no_update parameter.
+CString GetBundleCompletionMessage(
+    const CString& bundle_name,
+    const std::vector<AppCompletionInfo>& apps_info,
+    bool is_only_no_update,
+    bool is_canceled) {
+  ASSERT1(!apps_info.empty());
+  // TODO(omaha): Enable this assert if GetUpdateAllAppsBundleName() is
+  // changed to a different string.
+  // ASSERT(bundle_name != SHORT_COMPANY_NAME _T(" Application"),
+  //        (_T("Do not pass default bundle name to this function.")));
+
+  CString bundle_message;
+
+  if (is_only_no_update) {
+    VERIFY1(bundle_message.LoadString(IDS_NO_UPDATE_RESPONSE));
+    return bundle_message;
+  }
+
+  std::vector<CString> succeeded_apps;
+  std::vector<CString> failed_apps;
+  std::vector<CString> canceled_apps;
+  CString first_failure_message;
+  for (size_t i = 0; i < apps_info.size(); ++i) {
+    const AppCompletionInfo& app_info = apps_info[i];
+    ASSERT1(!app_info.display_name.IsEmpty());
+    ASSERT1(!app_info.completion_message.IsEmpty());
+
+    if (SUCCEEDED(app_info.error_code)) {
+      succeeded_apps.push_back(app_info.display_name);
+    } else if (app_info.is_canceled) {
+      canceled_apps.push_back(app_info.display_name);
+    } else {
+      failed_apps.push_back(app_info.display_name);
+
+      // For now, we only display the first error message when all apps fail.
+      // Remember that message.
+      if (first_failure_message.IsEmpty()) {
+        first_failure_message = app_info.completion_message;
+      }
+    }
+  }
+
+  CString canceled_apps_str;
+  if (!canceled_apps.empty()) {
+    canceled_apps_str = BuildResultStringForApps(
+        IDS_BUNDLE_MIXED_RESULTS_CANCELED_APPS, canceled_apps);
+  }
+
+  CString succeeded_apps_str;
+  if (!succeeded_apps.empty()) {
+    succeeded_apps_str = BuildResultStringForApps(
+        IDS_BUNDLE_MIXED_RESULTS_SUCCEEDED_APPS, succeeded_apps);
+  }
+  CString failed_apps_str;
+  if (!failed_apps.empty()) {
+    failed_apps_str = BuildResultStringForApps(
+        IDS_BUNDLE_MIXED_RESULTS_FAILED_APPS, failed_apps);
+  }
+
+  // For mixed results, display the succeeded, failed and canceled app lists on
+  // their own lines below the main message with a newline between the message
+  // and lists.
+  const TCHAR* const kLayoutForTwoGroups = _T("%s\n\n%s\n%s");
+  const TCHAR* const kLayoutForThreeGroups = _T("%s\n\n%s\n%s\n%s");
+
+  if (!failed_apps_str.IsEmpty()) {
+    // At least one app fails to install, display a failure message.
+    uint32 message_id =
+        failed_apps.size() == 1 ?
+            IDS_BUNDLE_MIXED_RESULTS_MESSAGE_ONE_FAILURE :
+            IDS_BUNDLE_MIXED_RESULTS_MESSAGE_MULTIPLE_FAILURES;
+    CString message;
+    VERIFY1(message.LoadString(message_id));
+
+    if (!succeeded_apps.empty() && !canceled_apps.empty()) {
+      SafeCStringFormat(&bundle_message, kLayoutForThreeGroups,
+                        message,
+                        succeeded_apps_str,
+                        failed_apps_str,
+                        canceled_apps_str);
+    } else if (!succeeded_apps.empty()) {
+      SafeCStringFormat(&bundle_message, kLayoutForTwoGroups,
+                        message,
+                        succeeded_apps_str,
+                        failed_apps_str);
+    } else if (!canceled_apps.empty()) {
+      SafeCStringFormat(&bundle_message, kLayoutForTwoGroups,
+                        message,
+                        failed_apps_str,
+                        canceled_apps_str);
+    } else {
+      bundle_message = first_failure_message;
+    }
+  } else if (!canceled_apps_str.IsEmpty()) {
+    // No failed app, but some are canceled.
+    if (!succeeded_apps_str.IsEmpty()) {
+      CString message;
+      VERIFY1(message.LoadString(
+          IDS_BUNDLE_INSTALLED_SUCCESSFULLY_AFTER_CANCEL));
+      SafeCStringFormat(&bundle_message, kLayoutForTwoGroups,
+                        message,
+                        succeeded_apps_str,
+                        canceled_apps_str);
+    } else {
+      // Only canceled app, no UI will be displayed.
+      VERIFY1(bundle_message.LoadString(IDS_CANCELED));
+    }
+  } else {
+    // All successes. Display a client-specific completion message that includes
+    // the bundle name.
+    // There is no special handling of apps with noupdate.
+    ASSERT1(succeeded_apps.size() == apps_info.size());
+    ASSERT1(first_failure_message.IsEmpty());
+    if (is_canceled) {
+      VERIFY1(bundle_message.LoadString(
+          IDS_BUNDLE_INSTALLED_SUCCESSFULLY_AFTER_CANCEL));
+    } else if (bundle_name.IsEmpty()) {
+      VERIFY1(
+          bundle_message.LoadString(IDS_APPLICATION_INSTALLED_SUCCESSFULLY));
+    } else {
+      bundle_message.FormatMessage(IDS_BUNDLE_INSTALLED_SUCCESSFULLY,
+                                   bundle_name);
+    }
+  }
+
+  ASSERT1(!bundle_message.IsEmpty());
+  return bundle_message;
+}
+
+}  // namespace internal
+
+BundleInstaller::BundleInstaller(HelpUrlBuilder* help_url_builder,
+                                 bool is_update_all_apps,
+                                 bool is_update_check_only,
+                                 bool is_browser_type_supported)
+    : help_url_builder_(help_url_builder),
+      observer_(NULL),
+      parent_window_(NULL),
+      state_(kInit),
+      result_(E_UNEXPECTED),
+      is_canceled_(false),
+      is_update_all_apps_(is_update_all_apps),
+      is_update_check_only_(is_update_check_only),
+      is_browser_type_supported_(is_browser_type_supported) {
+}
+
+BundleInstaller::~BundleInstaller() {
+  Uninitialize();
+}
+
+LRESULT BundleInstaller::OnTimer(UINT msg,
+                                 WPARAM wparam,
+                                 LPARAM,
+                                 BOOL& handled) {  // NOLINT
+  VERIFY1(msg == WM_TIMER);
+  VERIFY1(wparam == kPollingTimerId);
+
+  // set_observer() must be called before starting the message loop.
+  ASSERT1(observer_);
+
+  if (!PollServer()) {
+    CORE_LOG(L1, (_T("[BundleInstaller::OnTimer][Stopping polling timer]")));
+
+    // Ignore return value. KillTimer does not remove WM_TIMER messages already
+    // posted to the message queue.
+    KillTimer(kPollingTimerId);
+  }
+
+  handled = true;
+  return 0;
+}
+
+HRESULT BundleInstaller::Initialize() {
+  CORE_LOG(L3, (_T("[BundleInstaller::Initialize]")));
+
+  // Create a message-only window for the timer. It is not visible,
+  // has no z-order, cannot be enumerated, and does not receive broadcast
+  // messages. The window simply dispatches messages.
+  const TCHAR kWndName[] = _T("{139455DE-14E2-4d54-93B5-9E6ADDC04B4E}");
+  if (!Create(HWND_MESSAGE, NULL, kWndName)) {
+    return HRESULTFromLastError();
+  }
+
+  if (!SetTimer(kPollingTimerId, kPollingTimerPeriodMs)) {
+    return HRESULTFromLastError();
+  }
+
+  return S_OK;
+}
+
+void BundleInstaller::Uninitialize() {
+  if (IsWindow()) {
+    // This may fail if it was already killed when the bundle completed.
+    KillTimer(kPollingTimerId);
+
+    DestroyWindow();
+  }
+}
+
+void BundleInstaller::SetBundleParentWindow(HWND parent_window) {
+  ASSERT1(parent_window);
+  parent_window_ = parent_window;
+}
+
+HRESULT BundleInstaller::ListenToShutdownEvent(bool is_machine) {
+  ASSERT1(!shutdown_callback_.get());
+  HRESULT hr = ShutdownEvents::CreateShutdownHandler(
+      is_machine, this, address(shutdown_callback_));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("CreateShutdownHandler failed][0x%08x]"), hr));
+  }
+
+  return hr;
+}
+
+void BundleInstaller::StopListenToShutdownEvent(bool is_machine) {
+  UNREFERENCED_PARAMETER(is_machine);
+  shutdown_callback_.reset();
+}
+
+HRESULT BundleInstaller::InstallBundle(bool is_machine,
+                                       bool listen_to_shutdown_event,
+                                       IAppBundle* app_bundle,
+                                       InstallProgressObserver* observer) {
+  ASSERT1(app_bundle);
+  ASSERT1(!app_bundle_);
+  app_bundle_.Attach(app_bundle);
+  app_bundle_->put_parentHWND(reinterpret_cast<ULONG_PTR>(parent_window_));
+
+  observer_ = observer;
+
+  if (listen_to_shutdown_event) {
+    ListenToShutdownEvent(is_machine);
+  }
+
+  _pAtlModule->Lock();
+
+  message_loop_.Run();
+  CORE_LOG(L2, (_T("[message_loop_.Run() returned]")));
+
+  if (listen_to_shutdown_event) {
+    StopListenToShutdownEvent(is_machine);
+  }
+
+  observer_ = NULL;
+
+  // Installer should not hold any reference to app bundle after installation.
+  ASSERT1(!app_bundle_);
+
+  CORE_LOG(L1, (_T("InstallBundle returning][0x%08x]"), result()));
+  return result();
+}
+
+// Shutdown() is called from a thread in the OS threadpool. The PostMessage
+// marshals the call over to the UI thread, which is where DoClose needs to be
+// (and is) called from.
+LRESULT BundleInstaller::OnClose(UINT,
+                                 WPARAM,
+                                 LPARAM,
+                                 BOOL& handled) {         // NOLINT
+  CORE_LOG(L3, (_T("[BundleInstaller::OnClose]")));
+
+  DoClose();
+  handled = true;
+  return 0;
+}
+
+// Assumes that we can call OnComplete multiple times.
+void BundleInstaller::DoClose() {
+  CORE_LOG(L1, (_T("[BundleInstaller::DoClose]")));
+  if (kComplete != state_) {
+    CORE_LOG(L1,
+             (_T("[UI closed before install completed. Likely canceled.]")));
+    DoCancel();
+  }
+}
+
+void BundleInstaller::DoExit() {
+  CORE_LOG(L1, (_T("[BundleInstaller::DoExit]")));
+  ASSERT(state_ == kComplete, (_T("[State not complete yet, cannot exit!]")));
+
+  _pAtlModule->Unlock();
+}
+
+void BundleInstaller::DoCancel() {
+  CancelBundle();
+  is_canceled_ = true;
+}
+
+// Unless a catastrophic/unrecoverable error occurs, bundle processing should
+// continue, resulting in NotifyBundleInstallComplete() being called and S_OK
+// being returned up the callstack.
+//
+// The following classes of errors can be returned by DoPollServer().
+// * Errors returned by methods in this class (e.g. E_FAIL).
+//   The state_ may already have been set to kComplete
+//   * (e.g. GOOPDATE_E_NO_UPDATE_RESPONSE) or not (e.g. E_FAIL).
+// * Errors returned by utility methods (e.g. by goopdate_utils methods).
+// * COM errors returned due to API or sever failures.
+// * Errors returned by COM methods (e.g. by install()).
+//
+// Specifically note that app errors are not returned up the call stack. These
+// are handled and reported by NotifyBundleInstallComplete().
+//
+// Thus, for all errors returned by DoPollServer() except those returned by COM
+// methods (not property methods), the client knows the error description.
+// TODO(omaha3): What should we do for the COM method errors? There is currently
+// no error API for the bundle. Also, errors returned by these calls may not
+// be Omaha-specific errors. They could be COM errors (e.g. server unavailable),
+// in which case calling a COM method would not work.
+//
+// DoPollServer() handles some client-side errors
+// (e.g. GOOPDATE_E_NO_UPDATE_RESPONSE) by setting state_ to kComplete. All
+// other errors must be handled by calling Complete().
+// In all error cases, the bundle must be canceled.
+bool BundleInstaller::PollServer() {
+  HRESULT hr = DoPollServer();
+
+  // Handle the error unless it has already been handled.
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[DoPollServer failed][0x%08x][%d]"), hr, state_));
+
+    CancelBundle();
+
+    if (state_ != kComplete) {
+      CString message;
+      message.FormatMessage(IDS_INSTALL_FAILED_WITH_ERROR_CODE, hr);
+      BundleCompletionInfo bundle_info(COMPLETION_CODE_ERROR, hr, message);
+      Complete(bundle_info);
+    }
+  }
+
+  return state_ != kComplete;
+}
+
+HRESULT BundleInstaller::result() {
+  ASSERT1(kComplete == state_);
+  return result_;
+}
+
+// Polls the server for the state of the job and updates the UI.
+// Not thread safe. There should only be one installation per process. Do we
+// need to worry about multiple WM_TIMER events at the same time or does the
+// message loop ensure this doesn't happen?
+HRESULT BundleInstaller::DoPollServer() {
+  CORE_LOG(L3, (_T("[BundleInstaller::DoPollServer][%u]"), state_));
+  ASSERT1(observer_);
+  switch (state_) {
+    case kInit:
+      return HandleInitState();
+    case kProcessing:
+      return HandleProcessingState();
+    case kComplete:
+      return S_OK;
+    default:
+      ASSERT1(false);
+      return E_FAIL;
+  }
+}
+
+// Checks whether the update check is complete, and if so, gets the number of
+// apps with updates available.
+HRESULT BundleInstaller::HandleUpdateAvailable() {
+  CORE_LOG(L3, (_T("[BundleInstaller::HandleUpdateAvailable]")));
+  ASSERT1(!apps_.empty());
+  ASSERT1(app_bundle_);
+
+  if (is_update_all_apps_) {
+    // Nothing to do. The bundle will automatically continue on to the download.
+    return S_OK;
+  }
+
+  // The bundle will not automatically continue. Initiate the download and
+  // install if appropriate.
+
+  VARIANT_BOOL is_busy = VARIANT_TRUE;
+  HRESULT hr = app_bundle_->isBusy(&is_busy);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (is_busy) {
+    // An update is available, but other apps may still be being processed.
+    // Wait until bundle is not busy to indicate all apps have been processed.
+    return S_OK;
+  }
+
+  // The only purpose of this call now is to call NotifyUpdateAvailable().
+  int num_updates = 0;
+  hr = HandleUpdateCheckResults(&num_updates);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  CORE_LOG(L2, (_T("[Update check complete][updates: %d]"), num_updates));
+  ASSERT1(num_updates);
+
+  if (is_update_check_only_) {
+    return NotifyBundleUpdateCheckOnlyComplete();
+  }
+
+  // TODO(omaha): Do we handle an unexpected number of apps correctly?
+  // (i.e. apps_.size() != num_updates)
+  // This includes one of n apps reporting no update during an install.
+
+  return app_bundle_->install();
+}
+
+// Populates apps_. This must be done here because updateAllApps() adds apps
+// to app_bundle_.
+HRESULT BundleInstaller::HandleInitState() {
+  CORE_LOG(L3, (_T("[BundleInstaller::HandleInitState]")));
+  ASSERT1(observer_);
+  ASSERT1(app_bundle_);
+
+  state_ = kProcessing;
+  HRESULT hr = is_update_all_apps_ ?
+                   app_bundle_->updateAllApps() :
+                   app_bundle_->checkForUpdate();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  observer_->OnCheckingForUpdate();
+
+  long count = 0;  // NOLINT
+  hr = app_bundle_->get_Count(&count);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  ASSERT1(count > 0);
+
+  for (long i = 0; i != count; ++i) {  // NOLINT
+    CComPtr<IApp> app;
+    hr = update3_utils::GetApp(app_bundle_, i, &app);
+    if (FAILED(hr)) {
+      return hr;
+    }
+    apps_.push_back(AdaptIApp(app));
+  }
+
+  ASSERT1(!apps_.empty());
+  return S_OK;
+}
+
+// Iterates through the apps until it finds one in a non-terminal state.
+// If all apps are in a terminal state, calls NotifyBundleInstallComplete().
+// Assumes that apps are processed serially in order. If this changes, we need
+// to change the algorithm to avoid a bad UI experience.
+// TODO(omaha): For things like creating a log that might be visible in the UI,
+// we would need to guarantee that we always report each major state for each
+// app. This would also require changing this algorithm. In addition, the COM
+// server would need to return information for all previous AppStates
+// (i.e. download progress while in the install phase).
+HRESULT BundleInstaller::HandleProcessingState() {
+  CORE_LOG(L3, (_T("[BundleInstaller::HandleProcessingState]")));
+  ASSERT1(observer_);
+  ASSERT1(!apps_.empty());
+
+  const ComPtrIApp* app_to_process = NULL;
+
+  for (size_t i = 0; i < apps_.size(); ++i) {
+    CurrentState current_state = STATE_INIT;
+    CComPtr<ICurrentState> icurrent_state;
+    const ComPtrIApp& app = apps_[i];
+    HRESULT hr = update3_utils::GetAppCurrentState(app,
+                                                   &current_state,
+                                                   &icurrent_state);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[GetNextVersionState failed][0x%08x]"), hr));
+      return hr;
+    }
+
+    switch (current_state) {
+      case STATE_INSTALL_COMPLETE:
+      case STATE_NO_UPDATE:
+      case STATE_ERROR:
+        // Terminal state - nothing to do for this app. Check the next app.
+        continue;
+      case STATE_WAITING_TO_CHECK_FOR_UPDATE:
+      case STATE_CHECKING_FOR_UPDATE:
+        return S_OK;
+      case STATE_UPDATE_AVAILABLE:
+        return HandleUpdateAvailable();
+      case STATE_WAITING_TO_DOWNLOAD:
+        observer_->OnWaitingToDownload(internal::GetAppDisplayName(app));
+        return S_OK;
+      case STATE_RETRYING_DOWNLOAD:
+        ASSERT(false, (_T("Unsupported")));
+        return S_OK;  // Keep checking in order to be forwards compatible.
+      case STATE_DOWNLOADING:
+      case STATE_DOWNLOAD_COMPLETE:
+      case STATE_EXTRACTING:
+      case STATE_APPLYING_DIFFERENTIAL_PATCH:
+      case STATE_READY_TO_INSTALL:
+        return NotifyDownloadProgress(app, icurrent_state);
+      case STATE_WAITING_TO_INSTALL:
+        return NotifyWaitingToInstall(app);
+      case STATE_INSTALLING:
+        return NotifyInstallProgress(app, icurrent_state);
+      case STATE_PAUSED:
+        ASSERT(false, (_T("Unsupported")));
+        return S_OK;  // Keep checking in order to be forwards compatible.
+      case STATE_INIT:
+      default:
+        ASSERT1(false);
+        return S_OK;  // Keep checking in order to be forwards compatible.
+                      // Cannot support new terminal states, though.
+    }
+  }
+
+  // No apps were in non-terminal states. The bundle may still be busy, though,
+  // because app states are updated separately from the bundle state.
+  // TODO(omaha): Should we wait for the bundle to complete? If not, we may need
+  // to add appropriate waits and checks for any completion UI bundle actions
+  // that require that the AppBundle is not busy.
+
+  return NotifyBundleInstallComplete();
+}
+
+HRESULT BundleInstaller::NotifyUpdateAvailable(IApp* app) {
+  CORE_LOG(L3, (_T("[BundleInstaller::NotifyUpdateAvailable]")));
+  ASSERT1(app);
+  ASSERT1(observer_);
+
+  CComPtr<IAppVersion> next_version;
+  HRESULT hr = update3_utils::GetNextAppVersion(app, &next_version);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CComBSTR ver;
+  hr = next_version->get_version(&ver);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CORE_LOG(L3, (_T("[Next Version Update Available][%s]"), CString(ver)));
+
+  // TODO(omaha3): Until we force app teams to provide a version, the string
+  // may be empty.
+  observer_->OnUpdateAvailable(internal::GetAppDisplayName(app), CString(ver));
+  return S_OK;
+}
+
+HRESULT BundleInstaller::NotifyDownloadProgress(IApp* app,
+                                                ICurrentState* icurrent_state) {
+  CORE_LOG(L3, (_T("[BundleInstaller::NotifyDownloadProgress]")));
+  ASSERT1(icurrent_state);
+  ASSERT1(observer_);
+
+  int time_remaining_ms = kCurrentStateProgressUnknown;
+  int percentage = 0;
+  time64 next_retry_time = 0;
+  GetAppDownloadProgress(icurrent_state,
+                         &time_remaining_ms,
+                         &percentage,
+                         &next_retry_time);
+  if (next_retry_time != 0) {
+    observer_->OnWaitingRetryDownload(internal::GetAppDisplayName(app),
+                                      next_retry_time);
+  } else {
+    observer_->OnDownloading(internal::GetAppDisplayName(app),
+                             time_remaining_ms,
+                             percentage);
+  }
+  return S_OK;
+}
+
+// Starts the install unless the UI prevents the install from starting, in which
+// case it remains in the same state to be checked again next cycle.
+HRESULT BundleInstaller::NotifyWaitingToInstall(IApp* app) {
+  CORE_LOG(L3, (_T("[BundleInstaller::NotifyWaitingToInstall]")));
+  ASSERT1(app);
+  ASSERT1(observer_);
+
+  // can_start_install is ignored because download and install are no longer
+  // discrete phases.
+  bool can_start_install = false;
+  observer_->OnWaitingToInstall(internal::GetAppDisplayName(app),
+                                &can_start_install);
+
+  return S_OK;
+}
+
+HRESULT BundleInstaller::NotifyInstallProgress(IApp* app,
+                                               ICurrentState* icurrent_state) {
+  CORE_LOG(L3, (_T("[BundleInstaller::NotifyInstallProgress]")));
+  ASSERT1(app);
+  ASSERT1(icurrent_state);
+  ASSERT1(observer_);
+
+  // TODO(omaha3): Get the install progress and time for the current app.
+  // Handle kCurrentStateProgressUnknown appropriately.
+  UNREFERENCED_PARAMETER(icurrent_state);
+
+  observer_->OnInstalling(internal::GetAppDisplayName(app));
+
+  return S_OK;
+}
+
+// Only used by legacy OnDemand.
+HRESULT BundleInstaller::NotifyBundleUpdateCheckOnlyComplete() {
+  CORE_LOG(L3, (_T("[BundleInstaller::NotifyBundleUpdateCheckOnlyComplete]")));
+
+  BundleCompletionInfo info(COMPLETION_CODE_SUCCESS,
+                            S_OK,
+                            _T("OK"));  // Not used by legacy OnDemand.
+
+  Complete(info);
+  return S_OK;
+}
+
+// Assumes the AppBundle has completed and all apps are in a terminal state.
+// In other words, AppBundle::Cancel does not need to be called.
+// For now, append the completion message(s) from each app. If any app failed,
+// display the failure UI and set this objects result to the app error.
+// TODO(omaha3): Improve the UI for bundles. It does not currently handle
+// restart browser, etc. Nor is the output production-ready as it just appends
+// the completion strings and assumes L2R. We at least need prettier printing,
+// maybe each message on its own line. Also, our error messages are inconsistent
+// in whether they specify the app's name.
+HRESULT BundleInstaller::NotifyBundleInstallComplete() {
+  CORE_LOG(L3, (_T("[BundleInstaller::NotifyBundleInstallComplete]")));
+  ASSERT1(!apps_.empty());
+  ASSERT1(app_bundle_);
+
+  bool is_only_no_update = true;
+
+  std::vector<AppCompletionInfo> apps_info;
+  HRESULT bundle_result = S_OK;
+
+  // Get the completion info for each app and set the bundle result.
+  for (size_t i = 0; i < apps_.size(); ++i) {
+    const ComPtrIApp& app = apps_[i];
+    AppCompletionInfo app_info;
+    internal::GetAppCompletionMessage(app,
+                                      &app_info,
+                                      is_browser_type_supported_);
+
+    CORE_LOG(L1, (_T("[App completion][%Iu][%s]"), i, app_info.ToString()));
+    apps_info.push_back(app_info);
+
+    is_only_no_update &= app_info.is_noupdate;
+
+    ASSERT1(bundle_result == S_OK || FAILED(bundle_result));
+    if (FAILED(app_info.error_code) && SUCCEEDED(bundle_result)) {
+      // This is the first app failure. Use this as the result.
+      bundle_result = app_info.error_code;
+    }
+  }
+
+  ASSERT1(bundle_result == S_OK || !is_only_no_update);
+
+  CComBSTR bundle_name;
+  if (FAILED(app_bundle_->get_displayName(&bundle_name))) {
+    bundle_name.Empty();
+  }
+
+  CString current_bundle_message = internal::GetBundleCompletionMessage(
+                                       CString(bundle_name),
+                                       apps_info,
+                                       is_only_no_update,
+                                       is_canceled_);
+  ASSERT1(!current_bundle_message.IsEmpty());
+  CompletionCodes completion_code = COMPLETION_CODE_SUCCESS;
+  if (FAILED(bundle_result)) {
+    completion_code = COMPLETION_CODE_ERROR;
+  } else if (is_canceled_) {
+    // User tried to cancel but bundle is installed.
+    completion_code = COMPLETION_CODE_INSTALL_FINISHED_BEFORE_CANCEL;
+  }
+  BundleCompletionInfo bundle_info(completion_code,
+                                   bundle_result,
+                                   current_bundle_message);
+  bundle_info.apps_info = apps_info;  // Copying simplifies the code above.
+
+  // The exit code will be non-zero if any app failed to install.
+  // TODO(omaha3): What if apps have different settings? It seems anyone calling
+  // Omaha would expect to get an error code in this case. We need to make sure
+  // this doesn't cause undesirable behavior in the parent process(es).
+  Complete(bundle_info);
+  return S_OK;
+}
+
+// If an update is available, this method also sets relevant information from
+// the update response.
+// This function, and thus, NotifyUpdateAvailable() is only called if using
+// a phased install where the bundle waits after the update check (in other
+// words, !is_update_all_apps_). Currently, NotifyUpdateAvailable() only does
+// something in the legacy OnDemand case, so this is okay.
+HRESULT BundleInstaller::HandleUpdateCheckResults(int* num_updates) {
+  CORE_LOG(L1, (_T("[BundleInstaller::HandleUpdateCheckResults]")));
+  ASSERT1(num_updates);
+
+  *num_updates = 0;
+
+  for (size_t i = 0; i < apps_.size(); ++i) {
+    CurrentState current_state = STATE_INIT;
+    CComPtr<ICurrentState> icurrent_state;
+    const ComPtrIApp& app = apps_[i];
+    HRESULT hr = update3_utils::GetAppCurrentState(app,
+                                                   &current_state,
+                                                   &icurrent_state);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[GetAppCurrentState failed][0x%08x]"), hr));
+      return hr;
+    }
+
+    if (current_state == STATE_NO_UPDATE || current_state == STATE_ERROR) {
+      // Continue to process other apps.
+      // The error information, if applicable, will be reported elsewhere.
+      continue;
+    } else if (current_state != STATE_UPDATE_AVAILABLE) {
+      // The update check may not be complete or may be in an unexpected state.
+      ASSERT1(false);
+      return E_FAIL;
+    }
+
+    ++*num_updates;
+    VERIFY1(SUCCEEDED(NotifyUpdateAvailable(app)));
+  }
+
+  return S_OK;
+}
+
+// Assumes icurrent_state represents an app in one of the downloading states.
+// TODO(omaha3): Since this method does not check the current state, it's
+// possible to be in Download Complete or later but not report 100%. The server
+// should ensure it reports 100% and 0 time in these cases.
+void BundleInstaller::GetAppDownloadProgress(ICurrentState* icurrent_state,
+                                             int* time_remaining_ms,
+                                             int* percentage,
+                                             time64* next_retry_time) {
+  ASSERT1(icurrent_state);
+  ASSERT1(time_remaining_ms);
+  ASSERT1(percentage);
+
+  LONG local_time_remaining_ms = kCurrentStateProgressUnknown;
+  if (FAILED(icurrent_state->get_downloadTimeRemainingMs(
+          &local_time_remaining_ms))) {
+    local_time_remaining_ms = kCurrentStateProgressUnknown;
+  }
+
+  int local_percentage = 0;
+  ULONG bytes = 0;
+  ULONG bytes_total = 0;
+  if (FAILED(icurrent_state->get_bytesDownloaded(&bytes)) ||
+      FAILED(icurrent_state->get_totalBytesToDownload(&bytes_total))) {
+    local_percentage = 0;
+  } else {
+    ASSERT1(bytes <= bytes_total);
+    local_percentage = static_cast<int>(100ULL * bytes / bytes_total);
+    ASSERT1(0 <= local_percentage && local_percentage <= 100);
+  }
+
+
+  ULONGLONG local_next_retry_time = 0;
+  if (FAILED(icurrent_state->get_nextRetryTime(&local_next_retry_time))) {
+    local_next_retry_time = 0;
+  }
+
+  *time_remaining_ms = local_time_remaining_ms;
+  *percentage = local_percentage;
+  *next_retry_time = static_cast<time64>(local_next_retry_time);
+
+  // TODO(omaha3): For now, this client treats extracting and patching as part
+  // of downloading. Add UI support for these phases.
+
+  CORE_LOG(L4, (_T("[AppDownloadProgress]")
+                _T("[bytes %u][bytes_total %u][percentage %d][ms %d]"),
+                bytes, bytes_total, *percentage, *time_remaining_ms));
+}
+
+void BundleInstaller::CancelBundle() {
+  CORE_LOG(L1, (_T("[BundleInstaller::CancelBundle]")));
+  if (app_bundle_) {
+    VERIFY1(SUCCEEDED(app_bundle_->stop()));
+  }
+}
+
+// error_code is copied to result_, which is the return code for this object.
+void BundleInstaller::Complete(const BundleCompletionInfo& bundle_info) {
+  CORE_LOG(L1, (_T("[BundleInstaller::Complete][%s]"), bundle_info.ToString()));
+  ASSERT1(observer_);
+  ASSERT1(!bundle_info.bundle_completion_message.IsEmpty());
+
+  CString help_url;
+  if (bundle_info.completion_code == COMPLETION_CODE_ERROR) {
+    std::vector<HelpUrlBuilder::AppResult> app_install_results;
+    for (size_t i = 0; i < bundle_info.apps_info.size(); ++i) {
+      const AppCompletionInfo& info = bundle_info.apps_info[i];
+      // TODO(omaha3): Pass info.extra_code to HelpUrlBuilder as well so that
+      // the help URL has both extra code and installer result code.
+      app_install_results.push_back(
+          HelpUrlBuilder::AppResult(info.app_id,
+                                    info.error_code,
+                                    info.installer_result_code));
+    }
+
+    if (help_url_builder_.get()) {
+      VERIFY1(SUCCEEDED(help_url_builder_->BuildUrl(app_install_results,
+                                                    &help_url)));
+      ASSERT1(!help_url.IsEmpty());
+    }
+  }
+
+  // Set result_ and state_ before calling OnComplete on the observer.
+  // Otherwise, we end up calling BundleInstaller::Complete() recursively from
+  // DoClose().
+  result_ = bundle_info.bundle_result;
+  state_ = kComplete;
+
+  ReleaseAppBundle();
+
+  // TODO(omaha3): We need to expose some more items to the observer, such as
+  // install_manifest.install_actions[].success_action. There are a lot of
+  // things we need to expose together: success action, restart browser,
+  // terminate all browsers, url, and maybe others. Let's take
+  // the opportunity to standardize these even if the registry and config APIs
+  // are not ideal (i.e. success_url should not imply an action as it does in
+  // the config).
+
+  ObserverCompletionInfo observer_info(bundle_info.completion_code);
+  // TODO(omaha3): Consider moving the creation of the bundle completion
+  // message from this class to the observer.
+  observer_info.completion_text = bundle_info.bundle_completion_message;
+  observer_info.help_url = help_url;
+  observer_info.apps_info = bundle_info.apps_info;
+
+  observer_->OnComplete(observer_info);
+}
+
+// Omaha event pings are sent in AppBundle destructor. Release app_bundle_ and
+// its related interfaces explicitly so that the pings can be sent sooner.
+void BundleInstaller::ReleaseAppBundle() {
+  CORE_LOG(L3, (_T("[ReleaseAppBundle]")));
+  apps_.clear();
+  app_bundle_ = NULL;
+}
+
+}  // namespace omaha
diff --git a/client/bundle_installer.h b/client/bundle_installer.h
new file mode 100644
index 0000000..44cc601
--- /dev/null
+++ b/client/bundle_installer.h
@@ -0,0 +1,242 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#ifndef OMAHA_CLIENT_BUNDLE_INSTALLER_H_
+#define OMAHA_CLIENT_BUNDLE_INSTALLER_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/wtl_atlapp_wrapper.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/client/install_progress_observer.h"
+
+namespace omaha {
+
+// TODO(omaha): These should be declared in their own file.
+namespace internal {
+
+CString GetAppDisplayName(IApp* app);
+
+// Builds a list of app names of the format "one, two, three".
+// TODO(omaha): If this does not end up using AppCompletionInfo, move it to
+// client_utils.
+CString BuildAppNameList(const std::vector<CString>& app_names);
+
+// Helper function that returns the error information from the ICurrentState.
+HRESULT GetCompletionInformation(IApp* app,
+                                 CurrentState* current_state,
+                                 AppCompletionInfo* app_info);
+
+// Gets the completion message for an app.
+void GetAppCompletionMessage(IApp* app,
+                             AppCompletionInfo* app_info);
+
+// Gets the completion message for the bundle.
+// If the bundle name cannot be obtained, pass an empty string in bundle_name.
+// TODO(omaha): If AppCompletionInfo is exposed, maybe move to client_utils.
+CString GetBundleCompletionMessage(
+    const CString& bundle_name,
+    const std::vector<AppCompletionInfo>& apps_info,
+    bool is_only_no_update,
+    bool is_canceled);
+
+}  // namespace internal
+
+class HelpUrlBuilder;
+class ShutdownCallback;
+
+class BundleInstaller
+    : public CWindowImpl<BundleInstaller,
+                         CWindow,
+                         CWinTraits<WS_OVERLAPPED, WS_EX_TOOLWINDOW> > {
+ public:
+  // Takes ownership of help_url_builder.
+  BundleInstaller(HelpUrlBuilder* help_url_builder,
+                  bool is_update_all_apps,
+                  bool is_update_check_only,
+                  bool is_browser_type_supported);
+  ~BundleInstaller();
+
+  HRESULT Initialize();
+  void Uninitialize();
+
+  // Installs a bundle. The installer takes the ownership of the bundle and
+  // releases the inteface before function returns.
+  HRESULT InstallBundle(bool is_machine,
+                        bool listen_to_shutdown_event,
+                        IAppBundle* app_bundle,
+                        InstallProgressObserver* observer);
+
+  void SetBundleParentWindow(HWND parent_window);
+
+  // Message loop that pumps messages during installation.
+  CMessageLoop* message_loop() { return &message_loop_; }
+
+  // Handles asynchronous requests for the application to close.
+  void DoClose();
+
+  // Handles requests to exit the BundleInstaller message loop. Should only be
+  // called after the BundleInstaller is in the kComplete state, i.e., after
+  // OnComplete().
+  void DoExit();
+
+  // Handles asynchronous requests to cancel install.
+  void DoCancel();
+
+  // Polls the COM server and advances the install state appropriately.
+  // Returns true if the caller should continue polling.
+  bool PollServer();
+
+  HRESULT result();
+
+ private:
+  enum State {
+    kInit,
+    kProcessing,
+    kComplete,
+  };
+
+  // Contains additional information about the bundle completion.
+  struct BundleCompletionInfo {
+    CompletionCodes completion_code;
+    HRESULT bundle_result;  // Result to return up call stack on completion.
+    CString bundle_completion_message;
+    std::vector<AppCompletionInfo> apps_info;
+
+    BundleCompletionInfo(CompletionCodes code,
+                         HRESULT result,
+                         const CString& message)
+        : completion_code(code),
+          bundle_result(result),
+          bundle_completion_message(message) {}
+
+#ifdef DEBUG
+    CString ToString() const {
+      CString result;
+      SafeCStringFormat(&result, _T("[BundleCompletionInfo][%d][0x%x][%s]"),
+                        completion_code,
+                        bundle_result,
+                        bundle_completion_message);
+      for (size_t i = 0; i < apps_info.size(); ++i) {
+        SafeCStringAppendFormat(&result, _T("[%s]"), apps_info[i].ToString());
+      }
+      return result;
+    }
+#endif
+  };
+
+  // Does the work for PollServer.
+  HRESULT DoPollServer();
+
+  // Performs the polling while checking for update.
+  HRESULT HandleUpdateAvailable();
+
+  // Handles the first call to PollServer().
+  HRESULT HandleInitState();
+
+  // Performs all subsequent calls to PollServer() until the state is complete.
+  HRESULT HandleProcessingState();
+
+  // Makes installer listen to the shutdown event.
+  HRESULT ListenToShutdownEvent(bool is_machine);
+
+  // Stops listening to the shutdown event if the installer is currently
+  // listening. Otherwise no effect.
+  void StopListenToShutdownEvent(bool is_machine);
+
+  // These functions update the UI during HandleProcessingState().
+  // TODO(omaha): Rename these to Notify*.
+  HRESULT NotifyUpdateAvailable(IApp* app);
+  HRESULT NotifyDownloadProgress(IApp* app, ICurrentState* icurrent_state);
+  HRESULT NotifyWaitingToInstall(IApp* app);
+  HRESULT NotifyInstallProgress(IApp* app, ICurrentState* icurrent_state);
+  HRESULT NotifyBundleUpdateCheckOnlyComplete();
+  HRESULT NotifyBundleInstallComplete();
+
+  // Helper functions for the Notify* functions.
+  HRESULT HandleUpdateCheckResults(int* num_updates);
+  void GetAppDownloadProgress(ICurrentState* icurrent_state,
+                              int* time_remaining_ms,
+                              int* percentage,
+                              time64* next_retry_time);
+
+  void CancelBundle();
+
+  // Sets the state to complete and informs the UI.
+  void Complete(const BundleCompletionInfo& bundle_info);
+
+  BEGIN_MSG_MAP(BundleInstaller)
+    MESSAGE_HANDLER(WM_CLOSE, OnClose)
+    MESSAGE_HANDLER(WM_TIMER, OnTimer)
+  END_MSG_MAP()
+
+  static const int kPollingTimerId = 1;
+  static const int kPollingTimerPeriodMs = 100;
+
+  // The main use case for this OnClose() handler is the shutdown handler via a
+  // PostMessage in the /UA scenario.
+  LRESULT OnClose(UINT msg,
+                  WPARAM wparam,
+                  LPARAM lparam,
+                  BOOL& handled);  // NOLINT
+
+  // Calls BundleInstaller::PollServer() at periodic intervals.
+  LRESULT OnTimer(UINT msg,
+                  WPARAM wparam,
+                  LPARAM lparam,
+                  BOOL& handled);  // NOLINT
+
+  void ReleaseAppBundle();
+
+  InstallProgressObserver* observer_;
+  scoped_ptr<HelpUrlBuilder> help_url_builder_;
+
+  // The bundle to be installed.
+  CComPtr<IAppBundle> app_bundle_;
+
+  // Bundle parent window.
+  HWND parent_window_;
+
+  // Message loop that pumps messages during installation.
+  CMessageLoop message_loop_;
+
+  // Shutdown event listener.
+  scoped_ptr<ShutdownCallback> shutdown_callback_;
+
+  // The apps in app_bundle_. Allows easier and quicker access to the apps than
+  // going through app_bundle_.
+  typedef CComPtr<IApp> ComPtrIApp;
+  typedef CAdapt<ComPtrIApp> AdaptIApp;
+  std::vector<AdaptIApp> apps_;
+
+  State state_;
+  HRESULT result_;
+  bool is_canceled_;
+
+  const bool is_update_all_apps_;
+  const bool is_update_check_only_;  // Only used by legacy OnDemand.
+  const bool is_browser_type_supported_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(BundleInstaller);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_CLIENT_BUNDLE_INSTALLER_H_
diff --git a/client/bundle_installer_unittest.cc b/client/bundle_installer_unittest.cc
new file mode 100644
index 0000000..d94110b
--- /dev/null
+++ b/client/bundle_installer_unittest.cc
@@ -0,0 +1,386 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/error.h"
+#include "omaha/client/bundle_installer.h"
+#include "omaha/client/resource.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+using internal::BuildAppNameList;
+
+class BuildAppNameListTest : public testing::Test {
+ public:
+ protected:
+  std::vector<CString> app_names_;
+};
+
+TEST_F(BuildAppNameListTest, OneApp) {
+  app_names_.push_back(_T("Test App1"));
+  EXPECT_STREQ(_T("Test App1"), BuildAppNameList(app_names_));
+}
+
+TEST_F(BuildAppNameListTest, TwoApps) {
+  app_names_.push_back(_T("Test App1"));
+  app_names_.push_back(_T("Next App2"));
+  EXPECT_STREQ(_T("Test App1, Next App2"), BuildAppNameList(app_names_));
+}
+
+TEST_F(BuildAppNameListTest, ManyApps) {
+  app_names_.push_back(_T("Test App1"));
+  app_names_.push_back(_T("Next App2"));
+  app_names_.push_back(_T("My App3"));
+  app_names_.push_back(_T("Your App4"));
+  app_names_.push_back(_T("Other App5"));
+  EXPECT_STREQ(_T("Test App1, Next App2, My App3, Your App4, Other App5"),
+               BuildAppNameList(app_names_));
+}
+
+// TODO(omaha): Load "ar" resources and enable after we get translations.
+TEST_F(BuildAppNameListTest, DISABLED_ManyApps_Bidi) {
+  app_names_.push_back(_T("Test App1"));
+  app_names_.push_back(_T("Next App2"));
+  app_names_.push_back(_T("My App3"));
+  app_names_.push_back(_T("Your App4"));
+  app_names_.push_back(_T("Other App5"));
+  EXPECT_STREQ(_T("Other App5, Your App4, My App3, Next App2, Test App1"),
+               BuildAppNameList(app_names_));
+}
+
+class GetBundleCompletionMessageTest : public testing::Test {
+ public:
+ protected:
+  void AddSucceededAppInfo(int id) {
+    AppCompletionInfo app_info;
+    app_info.display_name.Format(_T("AppSucceeded%d"), id);
+    app_info.app_id.Format(_T("app_id_s_%d"), id);
+    app_info.error_code = 0;
+    app_info.extra_code1 = 0;
+    app_info.completion_message = kSuccessAppCompletionMessage;
+    app_info.installer_result_code = 0;
+    app_info.is_canceled = false;
+    apps_info_.push_back(app_info);
+  }
+
+  void AddFailedAppInfo(int id, bool make_error_info_unique) {
+    AppCompletionInfo app_info;
+    app_info.display_name.Format(_T("AppFailed%d"), id);
+    app_info.app_id.Format(_T("app_id_f_%d"), id);
+    app_info.error_code = 0x80070001;
+    app_info.extra_code1 = 123;
+    app_info.completion_message = kFailedAppCompletionMessage;
+    app_info.installer_result_code = 111;
+    app_info.is_canceled = false;
+    if (make_error_info_unique) {
+      app_info.error_code += id;
+      app_info.completion_message.AppendFormat(_T(" AppName:%s."),
+                                               app_info.display_name);
+      app_info.extra_code1 += id;
+      app_info.installer_result_code += id;
+    }
+
+    apps_info_.push_back(app_info);
+  }
+
+  void AddCanceledAppInfo(int id) {
+    AppCompletionInfo app_info;
+    app_info.display_name.Format(_T("AppCanceled%d"), id);
+    app_info.app_id.Format(_T("app_id_f_%d"), id);
+    app_info.error_code = GOOPDATE_E_CANCELLED;
+    app_info.extra_code1 = 0;
+    app_info.completion_message = kCanceledAppCompletionMessage;
+    app_info.installer_result_code = 0;
+    app_info.is_canceled = true;
+
+    apps_info_.push_back(app_info);
+  }
+
+  static const TCHAR* kBundleDisplayName;
+  static const TCHAR* kSuccessAppCompletionMessage;
+  static const TCHAR* kFailedAppCompletionMessage;
+  static const TCHAR* kCanceledAppCompletionMessage;
+  std::vector<AppCompletionInfo> apps_info_;
+};
+
+const TCHAR* GetBundleCompletionMessageTest::kBundleDisplayName =
+    _T("TestBundle");
+const TCHAR* GetBundleCompletionMessageTest::kSuccessAppCompletionMessage =
+    _T("App is installed successfully and is ready to use.");
+const TCHAR* GetBundleCompletionMessageTest::kFailedAppCompletionMessage =
+    _T("Failed to install the app.");
+const TCHAR* GetBundleCompletionMessageTest::kCanceledAppCompletionMessage =
+    _T("Installation is canceled by user.");
+
+TEST_F(GetBundleCompletionMessageTest, SingleAppSucceeded) {
+  AddSucceededAppInfo(1);
+
+  CString bundle_message = internal::GetBundleCompletionMessage(
+                                       kBundleDisplayName,
+                                       apps_info_,
+                                       false,   // is_only_no_update
+                                       false);  // is_canceled
+  // Bundle install succeeded, the completion message should be based on
+  // IDS_BUNDLE_INSTALLED_SUCCESSFULLY.
+  CString expected_message;
+  expected_message.Format(_T("Thanks for installing %s."), kBundleDisplayName);
+
+  EXPECT_STREQ(expected_message, bundle_message);
+}
+
+TEST_F(GetBundleCompletionMessageTest, MultipleAppsSucceeded) {
+  AddSucceededAppInfo(1);
+  AddSucceededAppInfo(2);
+  AddSucceededAppInfo(3);
+
+  CString bundle_message = internal::GetBundleCompletionMessage(
+                                       kBundleDisplayName,
+                                       apps_info_,
+                                       false,   // is_only_no_update
+                                       false);  // is_canceled
+  // Bundle install succeeded, the completion message should be based on
+  // IDS_BUNDLE_INSTALLED_SUCCESSFULLY.
+  CString expected_message;
+  expected_message.Format(_T("Thanks for installing %s."), kBundleDisplayName);
+
+  EXPECT_STREQ(expected_message, bundle_message);
+}
+
+TEST_F(GetBundleCompletionMessageTest, OneFailedAppWithSuccessApps) {
+  AddSucceededAppInfo(1);
+  AddFailedAppInfo(2, false);
+  AddSucceededAppInfo(3);
+
+  CString bundle_message = internal::GetBundleCompletionMessage(
+                                       kBundleDisplayName,
+                                       apps_info_,
+                                       false,   // is_only_no_update
+                                       false);  // is_canceled
+
+  CString expected_message =
+      _T("An application failed to install.\n\n")
+      _T("<b>Succeeded:</b> AppSucceeded1, AppSucceeded3\n")
+      _T("<b>Failed:</b> AppFailed2");
+  EXPECT_STREQ(expected_message, bundle_message);
+}
+
+TEST_F(GetBundleCompletionMessageTest, MulitpleFailedAppsWithSuccessApps) {
+  AddSucceededAppInfo(1);
+  AddFailedAppInfo(2, false);
+  AddSucceededAppInfo(3);
+  AddFailedAppInfo(4, false);
+  AddFailedAppInfo(5, false);
+
+  CString bundle_message = internal::GetBundleCompletionMessage(
+                                       kBundleDisplayName,
+                                       apps_info_,
+                                       false,   // is_only_no_update
+                                       false);  // is_canceled
+
+  CString expected_message =
+      _T("Some applications failed to install.\n\n")
+      _T("<b>Succeeded:</b> AppSucceeded1, AppSucceeded3\n")
+      _T("<b>Failed:</b> AppFailed2, AppFailed4, AppFailed5");
+  EXPECT_STREQ(expected_message, bundle_message);
+}
+
+TEST_F(GetBundleCompletionMessageTest, OneFailedAppOnly) {
+  AddFailedAppInfo(1, false);
+
+  CString bundle_message = internal::GetBundleCompletionMessage(
+                                       kBundleDisplayName,
+                                       apps_info_,
+                                       false,   // is_only_no_update
+                                       false);  // is_canceled
+
+  EXPECT_STREQ(kFailedAppCompletionMessage, bundle_message);
+}
+
+TEST_F(GetBundleCompletionMessageTest, AllAppsFailWithSameError) {
+  AddFailedAppInfo(1, false);
+  AddFailedAppInfo(2, false);
+  AddFailedAppInfo(3, false);
+
+  CString bundle_message = internal::GetBundleCompletionMessage(
+                                       kBundleDisplayName,
+                                       apps_info_,
+                                       false,   // is_only_no_update
+                                       false);  // is_canceled
+
+  EXPECT_STREQ(kFailedAppCompletionMessage, bundle_message);
+}
+
+
+TEST_F(GetBundleCompletionMessageTest, AllAppsFailWithUniqueError) {
+  AddFailedAppInfo(1, true);
+  AddFailedAppInfo(2, true);
+  AddFailedAppInfo(3, true);
+
+  CString bundle_message = internal::GetBundleCompletionMessage(
+                                       kBundleDisplayName,
+                                       apps_info_,
+                                       false,   // is_only_no_update
+                                       false);  // is_canceled
+
+  CString expected_message = kFailedAppCompletionMessage;
+  expected_message.AppendFormat(_T(" AppName:%s."), apps_info_[0].display_name);
+  EXPECT_STREQ(expected_message, bundle_message);
+}
+
+TEST_F(GetBundleCompletionMessageTest, BundleCanceled) {
+  AddCanceledAppInfo(1);
+  AddCanceledAppInfo(4);
+  AddCanceledAppInfo(7);
+
+  CString bundle_message = internal::GetBundleCompletionMessage(
+                                       kBundleDisplayName,
+                                       apps_info_,
+                                       false,   // is_only_no_update
+                                       true);   // is_canceled
+
+  CString expected_message;
+  EXPECT_TRUE(expected_message.LoadString(IDS_CANCELED));
+  EXPECT_STREQ(expected_message, bundle_message);
+}
+
+TEST_F(GetBundleCompletionMessageTest, AppSucceededAfterCancel) {
+  AddSucceededAppInfo(1);
+
+  CString bundle_message = internal::GetBundleCompletionMessage(
+                                       kBundleDisplayName,
+                                       apps_info_,
+                                       false,   // is_only_no_update
+                                       true);   // is_canceled
+
+  EXPECT_STREQ(_T("Installation completed before it could be canceled."),
+               bundle_message);
+}
+
+TEST_F(GetBundleCompletionMessageTest, AppCanceledWithSuccesses) {
+  AddSucceededAppInfo(1);
+  AddSucceededAppInfo(2);
+  AddSucceededAppInfo(3);
+  AddCanceledAppInfo(4);
+  AddCanceledAppInfo(5);
+
+  CString bundle_message = internal::GetBundleCompletionMessage(
+                                       kBundleDisplayName,
+                                       apps_info_,
+                                       false,   // is_only_no_update
+                                       true);   // is_canceled
+
+  CString expected_message =
+      _T("Installation completed before it could be canceled.\n\n")
+      _T("<b>Succeeded:</b> AppSucceeded1, AppSucceeded2, AppSucceeded3\n")
+      _T("<b>Canceled:</b> AppCanceled4, AppCanceled5");
+  EXPECT_STREQ(expected_message, bundle_message);
+}
+
+TEST_F(GetBundleCompletionMessageTest, AppsCanceledWithSuccessesAndOneFailure) {
+  AddSucceededAppInfo(1);
+  AddSucceededAppInfo(2);
+  AddFailedAppInfo(3, false);
+  AddCanceledAppInfo(4);
+  AddCanceledAppInfo(5);
+  AddCanceledAppInfo(6);
+
+  CString bundle_message = internal::GetBundleCompletionMessage(
+                                       kBundleDisplayName,
+                                       apps_info_,
+                                       false,   // is_only_no_update
+                                       true);   // is_canceled
+  CString expected_message =
+      _T("An application failed to install.\n\n")
+      _T("<b>Succeeded:</b> AppSucceeded1, AppSucceeded2\n")
+      _T("<b>Failed:</b> AppFailed3\n")
+      _T("<b>Canceled:</b> AppCanceled4, AppCanceled5, AppCanceled6");
+  EXPECT_STREQ(expected_message, bundle_message);
+}
+
+TEST_F(GetBundleCompletionMessageTest,
+       AppsCanceledWithSuccessesAndMultipleFailures) {
+  AddSucceededAppInfo(1);
+  AddSucceededAppInfo(2);
+  AddFailedAppInfo(3, false);
+  AddFailedAppInfo(4, false);
+  AddCanceledAppInfo(5);
+  AddCanceledAppInfo(6);
+
+  CString bundle_message = internal::GetBundleCompletionMessage(
+                                       kBundleDisplayName,
+                                       apps_info_,
+                                       false,   // is_only_no_update
+                                       true);   // is_canceled
+
+  CString expected_message =
+      _T("Some applications failed to install.\n\n")
+      _T("<b>Succeeded:</b> AppSucceeded1, AppSucceeded2\n")
+      _T("<b>Failed:</b> AppFailed3, AppFailed4\n")
+      _T("<b>Canceled:</b> AppCanceled5, AppCanceled6");
+  EXPECT_STREQ(expected_message, bundle_message);
+}
+
+TEST_F(GetBundleCompletionMessageTest, AppsCanceledWithOneFailure) {
+  AddFailedAppInfo(1, false);
+  AddCanceledAppInfo(2);
+  AddCanceledAppInfo(3);
+
+  CString bundle_message = internal::GetBundleCompletionMessage(
+                                       kBundleDisplayName,
+                                       apps_info_,
+                                       false,   // is_only_no_update
+                                       true);   // is_canceled
+
+  CString expected_message =
+      _T("An application failed to install.\n\n")
+      _T("<b>Failed:</b> AppFailed1\n")
+      _T("<b>Canceled:</b> AppCanceled2, AppCanceled3");
+  EXPECT_STREQ(expected_message, bundle_message);
+}
+
+TEST_F(GetBundleCompletionMessageTest, AppFailedAfterCancel) {
+  AddFailedAppInfo(1, false);
+
+  CString bundle_message = internal::GetBundleCompletionMessage(
+                                       kBundleDisplayName,
+                                       apps_info_,
+                                       false,   // is_only_no_update
+                                       true);   // is_canceled
+
+  EXPECT_STREQ(kFailedAppCompletionMessage, bundle_message);
+}
+
+TEST_F(GetBundleCompletionMessageTest, AppsCanceledWithMultipleFailures) {
+  AddFailedAppInfo(1, false);
+  AddFailedAppInfo(2, false);
+  AddFailedAppInfo(3, false);
+  AddCanceledAppInfo(4);
+  AddCanceledAppInfo(5);
+  AddCanceledAppInfo(6);
+
+  CString bundle_message = internal::GetBundleCompletionMessage(
+                                       kBundleDisplayName,
+                                       apps_info_,
+                                       false,   // is_only_no_update
+                                       true);   // is_canceled
+
+  CString expected_message =
+      _T("Some applications failed to install.\n\n")
+      _T("<b>Failed:</b> AppFailed1, AppFailed2, AppFailed3\n")
+      _T("<b>Canceled:</b> AppCanceled4, AppCanceled5, AppCanceled6");
+  EXPECT_STREQ(expected_message, bundle_message);
+}
+
+}  // namespace omaha
diff --git a/client/client_metrics.cc b/client/client_metrics.cc
new file mode 100644
index 0000000..3225110
--- /dev/null
+++ b/client/client_metrics.cc
@@ -0,0 +1,23 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/client/client_metrics.h"
+
+namespace omaha {
+
+DEFINE_METRIC_count(client_another_install_in_progress);
+DEFINE_METRIC_count(client_another_update_in_progress);
+
+}  // namespace omaha
diff --git a/client/client_metrics.h b/client/client_metrics.h
new file mode 100644
index 0000000..606e0be
--- /dev/null
+++ b/client/client_metrics.h
@@ -0,0 +1,37 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Declares the usage metrics used by the client module.
+
+#ifndef OMAHA_CLIENT_CLIENT_METRICS_H_
+#define OMAHA_CLIENT_CLIENT_METRICS_H_
+
+#include "omaha/statsreport/metrics.h"
+
+namespace omaha {
+
+// How many times the install client encountered another /install process
+// for the same bundle already running.
+// This metric was named worker_another_install_in_progress in Omaha 2.
+DECLARE_METRIC_count(client_another_install_in_progress);
+
+// How many times the update client encountered another /ua process already
+// running.
+// This metric was named worker_another_install_in_progress in Omaha 2.
+DECLARE_METRIC_count(client_another_update_in_progress);
+
+}  // namespace omaha
+
+#endif  // OMAHA_CLIENT_CLIENT_METRICS_H_
diff --git a/client/client_utils.cc b/client/client_utils.cc
new file mode 100644
index 0000000..ab430d4
--- /dev/null
+++ b/client/client_utils.cc
@@ -0,0 +1,192 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/client/client_utils.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/client/help_url_builder.h"
+#include "omaha/common/goopdate_utils.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/ui/complete_wnd.h"
+#include "omaha/ui/yes_no_dialog.h"
+
+namespace omaha {
+
+namespace client_utils {
+
+namespace {
+
+// Assumes this is not a silent process.
+// Uses bundle_name in the title if provided; otherwise displays generic title.
+bool DisplayErrorInMessageBox(const CString& error_text,
+                              const CString& bundle_name) {
+  CString msg_box_title = GetInstallerDisplayName(bundle_name);
+  return (0 != ::MessageBox(NULL, error_text, msg_box_title, MB_OK));
+}
+
+class ErrorWndEvents : public CompleteWndEvents {
+ public:
+  explicit ErrorWndEvents(bool is_machine) : is_machine_(is_machine) {}
+
+  // TODO(omaha3): Not sure if we need to do anything for DoClose.
+  virtual void DoClose() {}
+
+  virtual void DoExit() {
+    ::PostQuitMessage(0);
+  }
+
+  // TODO(omaha3): Use the specified browser if available.
+  // TODO(omaha3): Need to address elevated Vista installs. We could ask the
+  // non-elevated /install instance to launch the browser for us using some
+  // type of notification pipe like we use for out-of-process crashes.
+  virtual bool DoLaunchBrowser(const CString& url) {
+    CORE_LOG(L2, (_T("[ErrorWndEvents::DoLaunchBrowser %s]"), url));
+    return SUCCEEDED(goopdate_utils::LaunchBrowser(
+        is_machine_, BROWSER_DEFAULT, url));
+  }
+
+ private:
+  bool is_machine_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ErrorWndEvents);
+};
+
+bool CanLaunchBrowser() {
+  if (!vista_util::IsElevatedWithUACMaybeOn()) {
+    return true;
+  }
+
+  CComPtr<IProcessLauncher> launcher;
+  return SUCCEEDED(launcher.CoCreateInstance(CLSID_ProcessLauncherClass,
+                                             NULL,
+                                             CLSCTX_LOCAL_SERVER));
+}
+
+}  // namespace
+
+
+// Assumes this is an interactive instance.
+// If the Omaha UI fails to initialize, displays the error in a message box.
+// Thus, some UI should always be displayed.
+bool DisplayError(bool is_machine,
+                  const CString& bundle_name,
+                  HRESULT error,
+                  int extra_code,
+                  const CString& error_text,
+                  const CString& app_id,
+                  const CString& language_id,
+                  const GUID& iid,
+                  const CString& brand_code) {
+  CMessageLoop message_loop;
+  CompleteWnd error_wnd(&message_loop, NULL);
+
+  error_wnd.set_is_machine(is_machine);
+  error_wnd.set_bundle_name(bundle_name);
+  CString help_url;
+  HelpUrlBuilder url_builder(is_machine, language_id, iid, brand_code);
+  std::vector<HelpUrlBuilder::AppResult> app_install_result;
+  app_install_result.push_back(HelpUrlBuilder::AppResult(app_id,
+                                                         error,
+                                                         extra_code));
+
+  // When running elevated and ProcessLauncherClass is not registered, the
+  // browser launch from the link will fail. Don't display a link that will not
+  // work.
+  if (CanLaunchBrowser()) {
+    VERIFY1(SUCCEEDED(url_builder.BuildUrl(app_install_result, &help_url)));
+  }
+
+  HRESULT hr = error_wnd.Initialize();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("UI initialize failed][0x%08x]"), hr));
+    bool result = DisplayErrorInMessageBox(error_text, bundle_name);
+    ASSERT1(result);
+    error_wnd.DestroyWindow();  // Window will not get to destroy itself.
+    return result;
+  }
+
+  ErrorWndEvents error_wnd_events(is_machine);
+  // error_wnd_events must not be destroyed until CMessageLoop::Run() returns.
+  error_wnd.SetEventSink(&error_wnd_events);
+
+  error_wnd.Show();
+  error_wnd.DisplayCompletionDialog(false, error_text, help_url);
+  message_loop.Run();
+  return true;
+}
+
+bool DisplayContinueAsNonAdmin(const CString& bundle_name,
+                               bool* should_continue) {
+  ASSERT1(should_continue);
+  *should_continue = false;
+
+  CString title(client_utils::GetInstallerDisplayName(bundle_name));
+  CString text;
+  text.FormatMessage(IDS_CONTINUE_AS_NONADMIN, bundle_name);
+
+  CMessageLoop message_loop;
+  YesNoDialog continue_dialog(&message_loop, NULL);
+  HRESULT hr = continue_dialog.Initialize(title, text);
+
+  if (FAILED(hr)) {
+    int button_id = ::MessageBox(NULL, text, title, MB_YESNO);
+    *should_continue = button_id == IDYES;
+    return button_id != 0;
+  }
+
+  VERIFY1(SUCCEEDED(continue_dialog.Show()));
+  message_loop.Run();
+
+  *should_continue = continue_dialog.yes_clicked();
+  return true;
+}
+
+CString GetDefaultApplicationName() {
+  CString company_name;
+  VERIFY1(company_name.LoadString(IDS_FRIENDLY_COMPANY_NAME));
+
+  CString default_app_name;
+  default_app_name.FormatMessage(IDS_DEFAULT_APP_DISPLAY_NAME, company_name);
+  return default_app_name;
+}
+
+CString GetDefaultBundleName() {
+  return GetDefaultApplicationName();
+}
+
+CString GetUpdateAllAppsBundleName() {
+  // TODO(omaha3): If we ever productize interactive updates, we will need a
+  // different string. This may be okay for the title bar, but it looks weird
+  // in the completion strings. The current implementation uses the same string
+  // for both.
+  return GetDefaultApplicationName();
+}
+
+// If bundle_name is empty, the friendly company name is used.
+CString GetInstallerDisplayName(const CString& bundle_name) {
+  CString display_name = bundle_name;
+  if (display_name.IsEmpty()) {
+    VERIFY1(display_name.LoadString(IDS_FRIENDLY_COMPANY_NAME));
+  }
+
+  CString installer_name;
+  installer_name.FormatMessage(IDS_INSTALLER_DISPLAY_NAME, display_name);
+  return installer_name;
+}
+
+}  // namespace client_utils
+
+}  // namespace omaha
diff --git a/client/client_utils.h b/client/client_utils.h
new file mode 100644
index 0000000..ccaf693
--- /dev/null
+++ b/client/client_utils.h
@@ -0,0 +1,57 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_CLIENT_CLIENT_UTILS_H_
+#define OMAHA_CLIENT_CLIENT_UTILS_H_
+
+#include <windows.h>
+#include <atlstr.h>
+
+namespace omaha {
+
+namespace client_utils {
+
+// Displays the error message in a new UI instance.
+// Returns whether a UI was successfully displayed. Resources must be loaded.
+bool DisplayError(bool is_machine,
+                  const CString& bundle_name,
+                  HRESULT error,
+                  int extra_code1,
+                  const CString& error_text,
+                  const CString& app_id,
+                  const CString& language_id,
+                  const GUID& iid,
+                  const CString& brand_code);
+
+// Displays a dialog prompting the user to choose to continue with installing
+// the bundle on a per-user basis. should_continue is set to true if the user
+// chooses to proceed.
+// Returns whether a UI was successfully displayed. Resources must be loaded.
+bool DisplayContinueAsNonAdmin(const CString& bundle_name,
+                               bool* should_continue);
+
+CString GetDefaultApplicationName();
+
+CString GetDefaultBundleName();
+
+CString GetUpdateAllAppsBundleName();
+
+CString GetInstallerDisplayName(const CString& bundle_name);
+
+}  // namespace client_utils
+
+}  // namespace omaha
+
+#endif  // OMAHA_CLIENT_CLIENT_UTILS_H_
diff --git a/client/client_utils_unittest.cc b/client/client_utils_unittest.cc
new file mode 100644
index 0000000..334ecc4
--- /dev/null
+++ b/client/client_utils_unittest.cc
@@ -0,0 +1,59 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/constants.h"
+#include "omaha/client/client_utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace client_utils {
+
+// TODO(omaha): These tests are of questionable value - they just check
+// that the string resources for the language of your dev/build box
+// matches a concatenation of the strings in main.scons.  This means
+// that they're only useful for certain languages, and they add extra
+// hurdles when you're attempting to customize Omaha in its open source
+// form.  We should decide later whether to keep these tests at all -
+// for now, we just enable them on official builds.
+
+#ifdef GOOGLE_UPDATE_BUILD
+TEST(ClientUtilsTest, GetDefaultApplicationName) {
+  EXPECT_STREQ(SHORT_COMPANY_NAME _T(" Application"),
+               GetDefaultApplicationName());
+}
+
+TEST(ClientUtilsTest, GetDefaultBundleName) {
+  EXPECT_STREQ(SHORT_COMPANY_NAME _T(" Application"), GetDefaultBundleName());
+}
+
+TEST(ClientUtilsTest, GetUpdateAllAppsBundleName) {
+  EXPECT_STREQ(SHORT_COMPANY_NAME _T(" Application"),
+               GetUpdateAllAppsBundleName());
+}
+
+TEST(ClientUtilsTest, GetInstallerDisplayName_EmptyBundleName) {
+  EXPECT_STREQ(SHORT_COMPANY_NAME _T(" Installer"),
+               GetInstallerDisplayName(CString()));
+}
+#endif  // GOOGLE_UPDATE_BUILD
+
+TEST(ClientUtilsTest, GetInstallerDisplayName_WithBundleName) {
+  EXPECT_STREQ(_T("My App Installer"), GetInstallerDisplayName(_T("My App")));
+}
+
+}  // namespace client_utils
+
+}  // namespace omaha
diff --git a/client/help_url_builder.cc b/client/help_url_builder.cc
new file mode 100644
index 0000000..dba65f2
--- /dev/null
+++ b/client/help_url_builder.cc
@@ -0,0 +1,159 @@
+// Copyright 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+#include "omaha/client/help_url_builder.h"
+#include <windows.h>
+#include <atlstr.h>
+#include <vector>
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/goopdate_utils.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/net/http_client.h"
+
+namespace omaha {
+
+namespace {
+
+// Query element name-value pair.
+typedef std::pair<CString, CString> QueryElement;
+
+// Builds a query string from the provided name-value pairs.
+// The string does not begin or end in a pair separator.
+HRESULT BuildQueryString(const std::vector<QueryElement>& elements,
+                         CString* query) {
+  ASSERT1(query);
+
+  query->Empty();
+
+  for (size_t i = 0; i < elements.size(); ++i) {
+    CString escaped_str;
+    HRESULT hr = StringEscape(elements[i].second, false, &escaped_str);
+    if (FAILED(hr)) {
+      CORE_LOG(LEVEL_WARNING, (_T("[StringEscape failed][0x%08x]"), hr));
+      return hr;
+    }
+
+    CString element;
+    element.FormatMessage(_T("%1=%2"), elements[i].first, escaped_str);
+
+    if (0 < i) {
+      query->Append(_T("&"));
+    }
+    query->Append(element);
+  }
+
+  return S_OK;
+}
+
+}   // namespace
+
+HRESULT HelpUrlBuilder::BuildUrl(const std::vector<AppResult>& app_results,
+                                 CString* help_url) const {
+  ASSERT1(help_url);
+  help_url->Empty();
+
+  CString more_info_url;
+  VERIFY1(SUCCEEDED(ConfigManager::Instance()->GetMoreInfoUrl(&more_info_url)));
+
+  const TCHAR* const kHelpLinkSourceId = _T("gethelp");
+  HRESULT hr = BuildHttpGetString(more_info_url,
+                                  app_results,
+                                  GetVersionString(),
+                                  kHelpLinkSourceId,
+                                  help_url);
+  if (FAILED(hr)) {
+    // Make sure a failed URL is not displayed.
+    help_url->Empty();
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT HelpUrlBuilder::BuildHttpGetString(
+    const CString& service_url,
+    const std::vector<AppResult>& app_results,
+    const CString& goopdate_version,
+    const CString& source_id,
+    CString* get_request) const {
+  ASSERT1(get_request);
+  if (service_url.IsEmpty()) {
+    return E_INVALIDARG;
+  }
+  ASSERT1(_T('?') == service_url.GetAt(service_url.GetLength() - 1) ||
+          _T('&') == service_url.GetAt(service_url.GetLength() - 1));
+
+  CString os_version;
+  CString service_pack;
+  HRESULT hr = goopdate_utils::GetOSInfo(&os_version, &service_pack);
+  if (FAILED(hr)) {
+    CORE_LOG(LEVEL_WARNING, (_T("[GetOSInfo failed][0x%08x]"), hr));
+  }
+  const CString iid_string =
+      ::IsEqualGUID(GUID_NULL, iid_) ? _T("") : GuidToString(iid_);
+
+  std::vector<QueryElement> elements;
+  elements.push_back(QueryElement(_T("hl"), language_));
+
+  CString error_code_str;
+  CString extra_code_str;
+  CString element_name;
+  for (std::vector<AppResult>::size_type i = 0; i < app_results.size(); ++i) {
+    error_code_str.Format(_T("0x%x"), app_results[i].error_code);
+    extra_code_str.Format(_T("%d"), app_results[i].extra_code);
+    element_name.Format(_T("app.%d"), i);
+    elements.push_back(QueryElement(element_name, app_results[i].guid));
+    element_name.Format(_T("ec.%d"), i);
+    elements.push_back(QueryElement(element_name, error_code_str));
+    element_name.Format(_T("ex.%d"), i);
+    elements.push_back(QueryElement(element_name, extra_code_str));
+  }
+
+  elements.push_back(QueryElement(_T("guver"), goopdate_version));
+  elements.push_back(QueryElement(_T("m"), is_machine_ ? _T("1") : _T("0")));
+  elements.push_back(QueryElement(_T("os"), os_version));
+  elements.push_back(QueryElement(_T("sp"), service_pack));
+  elements.push_back(QueryElement(_T("iid"), iid_string));
+  elements.push_back(QueryElement(_T("brand"), brand_));
+  elements.push_back(QueryElement(_T("source"), source_id));
+
+  CString test_source = ConfigManager::Instance()->GetTestSource();
+  if (!test_source.IsEmpty()) {
+    elements.push_back(QueryElement(_T("testsource"), test_source));
+  }
+
+  CString query;
+  hr = BuildQueryString(elements, &query);
+  if (FAILED(hr)) {
+    CORE_LOG(LEVEL_WARNING, (_T("[BuildQueryString failed][0x%08x]"), hr));
+    return hr;
+  }
+  get_request->FormatMessage(_T("%1%2"), service_url, query);
+
+  // The length should be smaller than the maximum allowed get length.
+  ASSERT1(get_request->GetLength() <= INTERNET_MAX_URL_LENGTH);
+  if (get_request->GetLength() > INTERNET_MAX_URL_LENGTH) {
+    return E_FAIL;
+  }
+
+  return S_OK;
+}
+
+}  // namespace omaha
diff --git a/client/help_url_builder.h b/client/help_url_builder.h
new file mode 100644
index 0000000..a230e47
--- /dev/null
+++ b/client/help_url_builder.h
@@ -0,0 +1,76 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_CLIENT_HELP_URL_BUILDER_H_
+#define OMAHA_CLIENT_HELP_URL_BUILDER_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include <vector>
+#include "omaha/base/debug.h"
+#include "omaha/common/goopdate_utils.h"
+
+namespace omaha {
+
+class HelpUrlBuilder {
+ public:
+  struct AppResult {
+    AppResult(CString app_guid,
+              HRESULT install_error_code,
+              int install_extra_code) :
+        guid(app_guid),
+        error_code(install_error_code),
+        extra_code(install_extra_code) {}
+
+    CString guid;
+    HRESULT error_code;
+    int extra_code;
+  };
+
+  HelpUrlBuilder(bool is_machine,
+                 const CString& language,
+                 const GUID& iid,
+                 const CString& brand) :
+    is_machine_(is_machine),
+    language_(language),
+    iid_(iid),
+    brand_(brand) {}
+
+  HRESULT BuildUrl(const std::vector<AppResult>& app_results,
+                   CString* help_url) const;
+
+ private:
+  // Creates the query string based on the passed service_url and arguments.
+  // service_url must end in a '?' or '&'.
+  HRESULT BuildHttpGetString(
+      const CString& service_url,
+      const std::vector<AppResult>& app_results,
+      const CString& goopdate_version,
+      const CString& source_id,
+      CString* get_request) const;
+
+ private:
+  const bool is_machine_;
+  const CString language_;
+  const GUID iid_;
+  const CString brand_;
+
+  friend class HelpUrlBuilderTest;
+  DISALLOW_COPY_AND_ASSIGN(HelpUrlBuilder);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_CLIENT_HELP_URL_BUILDER_H_
diff --git a/client/help_url_builder_test.cc b/client/help_url_builder_test.cc
new file mode 100644
index 0000000..d2e4901
--- /dev/null
+++ b/client/help_url_builder_test.cc
@@ -0,0 +1,454 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <windows.h>
+#include <atlpath.h>
+#include <atlsecurity.h>
+#include <atlstr.h>
+#include <vector>
+#include "omaha/base/error.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/client/help_url_builder.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/net/http_client.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+#define APP_GUID  _T("{B7BAF788-9D64-49c3-AFDC-B336AB12F332}")
+#define APP_GUID2 _T("{6D2DF75B-11F0-41CA-9874-79DE4568527C}")
+const TCHAR* const kAppGuid  = APP_GUID;
+const TCHAR* const kAppGuid2 = APP_GUID2;
+
+const TCHAR kStringAlmostTooLongForUrl[] =
+    _T("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");  // NOLINT
+
+// Verifies that one of the expected OS strings was found in the url.
+// Returns the position along with the length of the OS string.
+int VerifyOSInUrl(const CString& url, int* length) {
+  ASSERT1(length);
+  *length = 0;
+
+  // The strings are in descending version order to avoid breaking on a
+  // substring of the version we are looking for.
+  // TODO(omaha): This is a maintenance problem. Consider eliminating the
+  // "&sp=" at the very least.
+  const TCHAR* kExpectedOsStrings[] = {_T("6.1&sp=Service%20Pack%201"),
+                                       _T("6.1&sp="),
+                                       _T("6.0&sp=Service%20Pack%201"),
+                                       _T("6.0&sp="),
+                                       _T("5.2&sp=Service%20Pack%202"),
+                                       _T("5.2&sp=Service%20Pack%201"),
+                                       _T("5.1&sp=Service%20Pack%203"),
+                                       _T("5.1&sp=Service%20Pack%202"),
+                                      };
+
+  bool found = false;
+  int this_pos = 0;
+
+  for (int i = 0; i < arraysize(kExpectedOsStrings); ++i) {
+    this_pos = url.Find(kExpectedOsStrings[i]);
+    if (-1 != this_pos) {
+      found = true;
+      *length = _tcslen(kExpectedOsStrings[i]);
+      break;
+    }
+  }
+
+  EXPECT_TRUE(found);
+  return this_pos;
+}
+
+}  // namespace
+
+class HelpUrlBuilderTest : public testing::Test {
+ protected:
+  HRESULT BuildHttpGetString(
+      const CString& base_url,
+      const std::vector<HelpUrlBuilder::AppResult>& app_results,
+      const CString& goopdate_version,
+      bool is_machine,
+      const CString& language,
+      const GUID& iid,
+      const CString& brand_code,
+      const CString& source_id,
+      CString* get_request) const {
+    HelpUrlBuilder url_builder(is_machine, language, iid, brand_code);
+    return url_builder.BuildHttpGetString(base_url,
+                                          app_results,
+                                          goopdate_version,
+                                          source_id,
+                                          get_request);
+  }
+
+  HelpUrlBuilderTest() : hive_override_key_name_(kRegistryHiveOverrideRoot),
+                         module_version_(GetVersion()) {
+  }
+
+  virtual void SetUp() {
+    RegKey::DeleteKey(hive_override_key_name_, true);
+    OverrideRegistryHives(hive_override_key_name_);
+    InitializeVersion(kFakeVersion);
+  }
+
+  virtual void TearDown() {
+    InitializeVersion(module_version_);
+    RestoreRegistryHives();
+    ASSERT_SUCCEEDED(RegKey::DeleteKey(hive_override_key_name_, true));
+  }
+
+  CString hive_override_key_name_;
+  const ULONGLONG module_version_;
+
+  static const ULONGLONG kFakeVersion = 0x0005000600070008;
+};
+
+TEST_F(HelpUrlBuilderTest, BuildHttpGetString_MachineNoTestSource) {
+  CString expected_str_before_os(
+      _T("http://www.google.com/hello.py?code=123&hl=en&")
+      _T("app.0=%7BB7BAF788-9D64-49c3-AFDC-B336AB12F332%7D&")
+      _T("ec.0=0xa&ex.0=22&")
+      _T("guver=1.0.51.0&m=1&os="));
+  CString expected_str_after_os(
+      _T("&iid=%7B0F973A20-C484-462B-952C-5D9A459E3326%7D")  // Upper case 'B'.
+      _T("&brand=GoOG&source=click"));
+  bool expected_test_source = false;
+
+#if defined(DEBUG) || !OFFICIAL_BUILD
+  // TestSource is always set for these builds. It may be set for opt official
+  // builds but this is not guaranteed.
+  expected_str_after_os.Append(_T("&testsource="));
+  expected_test_source = true;
+#endif
+
+  CString url_req;
+  std::vector<HelpUrlBuilder::AppResult> app_results;
+  app_results.push_back(HelpUrlBuilder::AppResult(kAppGuid, 10, 22));
+  EXPECT_SUCCEEDED(BuildHttpGetString(
+      _T("http://www.google.com/hello.py?code=123&"),
+      app_results,
+      _T("1.0.51.0"),
+      true,
+      _T("en"),
+      StringToGuid(_T("{0F973A20-C484-462b-952C-5D9A459E3326}")),
+      _T("GoOG"),
+      _T("click"),
+      &url_req));
+
+  EXPECT_EQ(-1, url_req.FindOneOf(_T("{}")));
+
+  EXPECT_LE(expected_str_before_os.GetLength(), url_req.GetLength());
+  EXPECT_EQ(0, url_req.Find(expected_str_before_os)) <<
+      _T("Expected: ") << expected_str_before_os.GetString() << std::endl <<
+      _T("At beginning of: ") << url_req.GetString();
+  int os_fragment_len = 0;
+  EXPECT_EQ(expected_str_before_os.GetLength(),
+            VerifyOSInUrl(url_req, &os_fragment_len)) <<
+      _T("Expected OS string not found in: ") << url_req.GetString();
+
+  EXPECT_EQ(expected_str_before_os.GetLength() + os_fragment_len,
+            url_req.Find(expected_str_after_os)) <<
+      _T("Expected: ") << expected_str_after_os.GetString() << std::endl <<
+      _T("At end of: ") << url_req.GetString();
+
+  if (expected_test_source) {
+    CString expected_testsource_str =
+        ConfigManager::Instance()->GetTestSource();
+    int expected_testsource_start = expected_str_before_os.GetLength() +
+                                    os_fragment_len +
+                                    expected_str_after_os.GetLength();
+    EXPECT_EQ(expected_testsource_start, url_req.Find(expected_testsource_str));
+    EXPECT_EQ(expected_testsource_start + expected_testsource_str.GetLength(),
+              url_req.GetLength());
+  } else {
+    EXPECT_EQ(expected_str_before_os.GetLength() +
+              os_fragment_len +
+              expected_str_after_os.GetLength(),
+              url_req.GetLength());
+
+    EXPECT_EQ(-1, url_req.Find(_T("testsource")));
+  }
+}
+
+TEST_F(HelpUrlBuilderTest, BuildHttpGetString_UserWithTestSource) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueTestSource,
+                                    _T("dev")));
+
+  const CString expected_str_before_os(
+      _T("http://www.google.com/hello.py?hl=de&")
+      _T("app.0=%7BB7BAF788-9D64-49c3-AFDC-B336AB12F332%7D&")
+      _T("ec.0=0xffffffff&ex.0=99&")
+      _T("guver=foo%20bar&m=0&os="));
+  const CString expected_str_after_os(
+      _T("&iid=%7B0F973A20-C484-462B-952C-5D9A459E3326%7D")  // Upper case 'B'.
+      _T("&brand=GGLE&source=clack")
+      _T("&testsource="));
+
+  CString url_req;
+  std::vector<HelpUrlBuilder::AppResult> app_results;
+  app_results.push_back(HelpUrlBuilder::AppResult(kAppGuid, 0xffffffff, 99));
+  EXPECT_SUCCEEDED(BuildHttpGetString(
+      _T("http://www.google.com/hello.py?"),
+      app_results,
+      _T("foo bar"),
+      false,
+      _T("de"),
+      StringToGuid(_T("{0F973A20-C484-462b-952C-5D9A459E3326}")),
+      _T("GGLE"),
+      _T("clack"),
+      &url_req));
+  EXPECT_LE(expected_str_before_os.GetLength(), url_req.GetLength());
+  EXPECT_EQ(0, url_req.Find(expected_str_before_os));
+
+  int os_fragment_len = 0;
+  EXPECT_EQ(expected_str_before_os.GetLength(),
+            VerifyOSInUrl(url_req, &os_fragment_len)) <<
+      _T("Expected: ") << expected_str_before_os.GetString() << std::endl <<
+      _T("At beginning of: ") << url_req.GetString();
+
+  EXPECT_EQ(expected_str_before_os.GetLength() + os_fragment_len,
+            url_req.Find(expected_str_after_os)) <<
+      _T("Expected OS string not found in: ") << url_req.GetString();
+
+  const CString expected_testsource_str = _T("dev");
+
+  int expected_testsource_start = expected_str_before_os.GetLength() +
+                                  os_fragment_len +
+                                  expected_str_after_os.GetLength();
+  EXPECT_EQ(expected_testsource_start, url_req.Find(expected_testsource_str));
+  EXPECT_EQ(expected_testsource_start + expected_testsource_str.GetLength(),
+            url_req.GetLength());
+}
+
+// IID and brand code are emtpy if not present.
+TEST_F(HelpUrlBuilderTest, BuildHttpGetString_NoIidOrBrandCode) {
+  const CString expected_str_before_os(
+      _T("http://www.google.com/hello.py?hl=en&")
+      _T("app.0=%7BB7BAF788-9D64-49c3-AFDC-B336AB12F332%7D&")
+      _T("ec.0=0xffffffff&ex.0=99&")
+      _T("guver=foo%20bar&m=1&os="));
+  const CString expected_str_after_os(_T("&iid=&brand=&source=cluck"));
+
+  CString url_req;
+  std::vector<HelpUrlBuilder::AppResult> app_results;
+  app_results.push_back(HelpUrlBuilder::AppResult(
+      _T("{B7BAF788-9D64-49c3-AFDC-B336AB12F332}"), 0xffffffff, 99));
+  EXPECT_SUCCEEDED(BuildHttpGetString(
+      _T("http://www.google.com/hello.py?"),
+      app_results,
+      _T("foo bar"),
+      true,
+      _T("en"),
+      GUID_NULL,
+      _T(""),
+      _T("cluck"),
+      &url_req));
+
+  EXPECT_EQ(0, url_req.Find(expected_str_before_os)) <<
+      _T("Expected: ") << expected_str_before_os.GetString() << std::endl <<
+      _T("At beginning of: ") << url_req.GetString();
+
+  EXPECT_LT(0, url_req.Find(expected_str_after_os));
+
+  CString expected_test_src;
+#if defined(DEBUG) || !OFFICIAL_BUILD
+  expected_test_src = _T("&testsource=auto");
+#endif
+  const CString expected_iid_str(_T("&iid=&brand=&source=cluck"));
+  EXPECT_EQ(url_req.GetLength() -
+            expected_iid_str.GetLength() -
+            expected_test_src.GetLength(),
+            url_req.Find(expected_iid_str));
+}
+
+TEST_F(HelpUrlBuilderTest, BuildHttpGetString_UrlTooLong) {
+  EXPECT_LT(INTERNET_MAX_URL_LENGTH, arraysize(kStringAlmostTooLongForUrl) + 5);
+
+  ExpectAsserts expect_asserts;  // BuildHttpGetString asserts on URL length.
+  CString url_req;
+  std::vector<HelpUrlBuilder::AppResult> app_results;
+  app_results.push_back(HelpUrlBuilder::AppResult(
+      _T("{B7BAF788-9D64-49c3-AFDC-B336AB12F332}"), 0xffffffff, 99));
+  EXPECT_EQ(E_FAIL, BuildHttpGetString(
+      _T("http://www.google.com/hello.py?"),
+      app_results,
+      _T("foo bar"),
+      true,
+      _T("en"),
+      GUID_NULL,
+      _T(""),
+      kStringAlmostTooLongForUrl,
+      &url_req));
+}
+
+TEST_F(HelpUrlBuilderTest, BuildHttpGetString_MultipleApps) {
+  CString expected_str_before_os(
+      _T("http://www.google.com/hello.py?code=123&hl=en&")
+      _T("app.0=%7BB7BAF788-9D64-49c3-AFDC-B336AB12F332%7D&")
+      _T("ec.0=0x80000001&ex.0=1000&")
+      _T("app.1=%7B6D2DF75B-11F0-41CA-9874-79DE4568527C%7D&")
+      _T("ec.1=0x0&ex.1=0&")
+      _T("guver=1.0.51.22&m=1&os="));
+  CString expected_str_after_os(
+      _T("&iid=%7B0F973A20-C484-462B-952C-5D9A459E3326%7D")  // Upper case 'B'.
+      _T("&brand=TEST&source=click"));
+  bool expected_test_source = false;
+
+#if defined(DEBUG) || !OFFICIAL_BUILD
+  // TestSource is always set for these builds. It may be set for opt official
+  // builds but this is not guaranteed.
+  expected_str_after_os.Append(_T("&testsource="));
+  expected_test_source = true;
+#endif
+
+  CString url_req;
+  std::vector<HelpUrlBuilder::AppResult> app_results;
+  app_results.push_back(HelpUrlBuilder::AppResult(kAppGuid, 0x80000001, 1000));
+  app_results.push_back(HelpUrlBuilder::AppResult(kAppGuid2, 0, 0));
+  EXPECT_SUCCEEDED(BuildHttpGetString(
+      _T("http://www.google.com/hello.py?code=123&"),
+      app_results,
+      _T("1.0.51.22"),
+      true,
+      _T("en"),
+      StringToGuid(_T("{0F973A20-C484-462b-952C-5D9A459E3326}")),
+      _T("TEST"),
+      _T("click"),
+      &url_req));
+
+  EXPECT_EQ(-1, url_req.FindOneOf(_T("{}")));
+
+  EXPECT_LE(expected_str_before_os.GetLength(), url_req.GetLength());
+  EXPECT_EQ(0, url_req.Find(expected_str_before_os)) <<
+      _T("Expected: ") << expected_str_before_os.GetString() << std::endl <<
+      _T("At beginning of: ") << url_req.GetString();
+  int os_fragment_len = 0;
+  EXPECT_EQ(expected_str_before_os.GetLength(),
+            VerifyOSInUrl(url_req, &os_fragment_len)) <<
+      _T("Expected OS string not found in: ") << url_req.GetString();
+
+  EXPECT_EQ(expected_str_before_os.GetLength() + os_fragment_len,
+            url_req.Find(expected_str_after_os)) <<
+      _T("Expected: ") << expected_str_after_os.GetString() << std::endl <<
+      _T("At end of: ") << url_req.GetString();
+
+  if (expected_test_source) {
+    CString expected_testsource_str =
+        ConfigManager::Instance()->GetTestSource();
+    int expected_testsource_start = expected_str_before_os.GetLength() +
+                                    os_fragment_len +
+                                    expected_str_after_os.GetLength();
+    EXPECT_EQ(expected_testsource_start, url_req.Find(expected_testsource_str));
+    EXPECT_EQ(expected_testsource_start + expected_testsource_str.GetLength(),
+              url_req.GetLength());
+  } else {
+    EXPECT_EQ(expected_str_before_os.GetLength() +
+              os_fragment_len +
+              expected_str_after_os.GetLength(),
+              url_req.GetLength());
+
+    EXPECT_EQ(-1, url_req.Find(_T("testsource")));
+  }
+}
+
+// Machine ID must be set or it will be randomly generated in some cases.
+TEST_F(HelpUrlBuilderTest, BuildGetHelpUrl_User) {
+  // The URL has a begin, middle which is OS-specific and not checked, and end.
+  const CString kExpetedUrlBegin =
+      _T("http://www.google.com/support/installer/?hl=en-GB&")
+      _T("app.0=%7Btest-user-app-id%7D&ec.0=0x80004005&ex.0=-2147418113&")
+      _T("guver=5.6.7.8&m=0&os=");
+  const CString kExpectedUrlAfterOs = _T("iid=&brand=&source=gethelp")
+#if defined(DEBUG) || !OFFICIAL_BUILD
+      // TestSource is always set for these builds.
+      _T("&testsource=");
+#else
+      // TestSource never set for other builds because registry is overridden.
+      ;  // NOLINT
+#endif
+
+  CString url;
+  HelpUrlBuilder  url_builder(false, _T("en-GB"), GUID_NULL, _T(""));
+  std::vector<HelpUrlBuilder::AppResult> app_results;
+  app_results.push_back(
+      HelpUrlBuilder::AppResult(_T("{test-user-app-id}"),
+                                E_FAIL,
+                                static_cast<DWORD>(E_UNEXPECTED)));
+  EXPECT_SUCCEEDED(url_builder.BuildUrl(app_results, &url));
+
+  EXPECT_STREQ(kExpetedUrlBegin, url.Left(kExpetedUrlBegin.GetLength()));
+  EXPECT_NE(-1, url.Find(kExpectedUrlAfterOs))
+      << kExpectedUrlAfterOs.GetString() << std::endl
+      << _T(" not found in ") << std::endl << url.GetString();
+}
+
+TEST_F(HelpUrlBuilderTest, BuildGetHelpUrl_Machine) {
+  // The URL has a begin, middle which is OS-specific and not checked, and end.
+  const CString kExpetedUrlBegin =
+      _T("http://www.google.com/support/installer/?hl=en-GB&")
+      _T("app.0=%7Btest-machine-app-id%7D&ec.0=0x80004004&ex.0=99&")
+      _T("guver=5.6.7.8&m=1&os=");
+  const CString kExpectedUrlAfterOs =
+      _T("iid=%7B326ADA1D-06AA-4C16-8101-5FC3FEBC852A%7D&")  // Upper case 'C'.
+      _T("brand=GOOG&source=gethelp")
+#if defined(DEBUG) || !OFFICIAL_BUILD
+      // TestSource is always set for these builds.
+      _T("&testsource=");
+#else
+      // TestSource never set for other builds because registry is overridden.
+      ;  // NOLINT
+#endif
+
+  const GUID kIid = StringToGuid(_T("{326ADA1D-06AA-4c16-8101-5FC3FEBC852A}"));
+  CString url;
+  HelpUrlBuilder  url_builder(true, _T("en-GB"), kIid, _T("GOOG"));
+  std::vector<HelpUrlBuilder::AppResult> app_results;
+  app_results.push_back(HelpUrlBuilder::AppResult(_T("{test-machine-app-id}"),
+                                                  E_ABORT,
+                                                  99));
+  EXPECT_SUCCEEDED(url_builder.BuildUrl(app_results, &url));
+
+  EXPECT_STREQ(kExpetedUrlBegin, url.Left(kExpetedUrlBegin.GetLength()));
+  EXPECT_NE(-1, url.Find(kExpectedUrlAfterOs))
+      << kExpectedUrlAfterOs.GetString() << std::endl
+      << _T(" not found in ") << std::endl << url.GetString();
+}
+
+// Makes BuildHttpGetString fail by making the URL too long.
+// The call succeeds, but the url is empty.
+TEST_F(HelpUrlBuilderTest, BuildGetHelpUrl_BuildFails) {
+  EXPECT_LT(INTERNET_MAX_URL_LENGTH, arraysize(kStringAlmostTooLongForUrl) + 5);
+
+  ExpectAsserts expect_asserts;  // BuildHttpGetString asserts on URL length.
+  CString url;
+  HelpUrlBuilder  url_builder(false, _T("en-GB"), GUID_NULL, _T(""));
+  std::vector<HelpUrlBuilder::AppResult> app_results;
+  app_results.push_back(
+      HelpUrlBuilder::AppResult(kStringAlmostTooLongForUrl,
+                                E_FAIL,
+                                static_cast<DWORD>(E_UNEXPECTED)));
+  EXPECT_EQ(E_FAIL, url_builder.BuildUrl(app_results, &url));
+  EXPECT_TRUE(url.IsEmpty());
+}
+
+}  // namespace omaha
diff --git a/client/install.cc b/client/install.cc
new file mode 100644
index 0000000..7e27ce3
--- /dev/null
+++ b/client/install.cc
@@ -0,0 +1,830 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/client/install.h"
+#include "omaha/client/install_internal.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/path.h"
+#include "omaha/base/process.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/string.h"
+#include "omaha/base/time.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/client/client_utils.h"
+#include "omaha/client/install_self.h"
+#include "omaha/client/resource.h"
+#include "omaha/common/app_registry_utils.h"
+#include "omaha/common/command_line_builder.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/oem_install_utils.h"
+#include "omaha/common/ping.h"
+#include "omaha/setup/setup_metrics.h"
+#include "omaha/ui/splash_screen.h"
+
+namespace omaha {
+
+namespace {
+
+// Returns whether elevation is required.
+bool IsElevationRequired(bool is_machine) {
+  return is_machine && !vista_util::IsUserAdmin();
+}
+
+}  // namespace
+
+namespace internal {
+
+// TODO(omaha3): Make this elevate the metainstaller instead of
+// GoogleUpdate.exe so the files are extracted to a secure location.
+// May need to add all languages to the metainstaller's version resources so
+// the user sees the localized string in the UAC.
+// TODO(omaha3): We will need to save the metainstaller for OneClick
+// cross-installs. We may need to change the metainstaller behavior to not
+// use the tag if any command line args are provided. This will allow us to
+// reuse a tagged metainstaller that we saved for other purposes, such as
+// OneClick cross-installs.
+// TODO(omaha3): The "metainstaller" may also be some type of wrapper around
+// a differential update. We'll address that later. This wrapper should not
+// need localized resource strings since it always runs silently.
+HRESULT DoElevation(bool is_interactive,
+                    bool is_install_elevated_instance,
+                    const CString& cmd_line,
+                    DWORD* exit_code) {
+  ASSERT1(exit_code);
+  ASSERT1(IsElevationRequired(true));
+
+  if (!is_interactive) {
+    return GOOPDATE_E_SILENT_INSTALL_NEEDS_ELEVATION;
+  }
+
+  if (is_install_elevated_instance) {
+    // This can happen if UAC is disabled. See http://b/1187784.
+    CORE_LOG(LE, (_T("[Install elevated process requires elevation]")));
+    return GOOPDATE_E_INSTALL_ELEVATED_PROCESS_NEEDS_ELEVATION;
+  }
+
+  HRESULT hr = ElevateAndWait(cmd_line, exit_code);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[ElevateAndWait failed][%s][0x%08x]"), cmd_line, hr));
+  }
+
+  return hr;
+}
+
+// Assumes it is running with the necessary privileges.
+HRESULT DoInstall(bool is_machine,
+                  bool is_app_install,
+                  bool is_eula_required,
+                  bool is_oem_install,
+                  const CString& current_version,
+                  const CommandLineArgs& args,
+                  const CString& session_id,
+                  SplashScreen* splash_screen,
+                  int* extra_code1,
+                  bool* has_setup_succeeded,
+                  bool* has_launched_handoff,
+                  bool* has_ui_been_displayed) {
+  ASSERT1(!IsElevationRequired(is_machine));
+  ASSERT1(extra_code1);
+  ASSERT1(splash_screen);
+  ASSERT1(has_setup_succeeded);
+  ASSERT1(has_launched_handoff);
+  ASSERT1(has_ui_been_displayed);
+
+  *extra_code1 = 0;
+
+  // TODO(omaha3): We may need to take an "installing apps" lock here if
+  // is_app_install. There was code in Omaha 2 that relied on the fact that
+  // the Setup lock was held while the install worker was launched, even in
+  // handoff scenarios. This allowed checking for the Setup lock and running
+  // install workers to be sufficient to ensure that the number of Clients keys
+  // was stable. Note that Omaha 2 also held the shutdown event while the worker
+  // was launched.
+  // TODO(omaha3): Consider taking the Setup lock in this file (via a public
+  // method in Setup) and releasing it. This would allow us to address the above
+  // issue and maybe the EULA not accepted issue.
+  // TODO(omaha3): We may also want to call ShouldInstall after a Lock(0) to
+  // determine whether we even need to display the splash screen or we can skip
+  // it and Setup altogether and just launch the /handoff process.
+
+  HRESULT hr = install_self::InstallSelf(is_machine,
+                                         is_eula_required,
+                                         is_oem_install,
+                                         current_version,
+                                         args.install_source,
+                                         args.extra,
+                                         session_id,
+                                         extra_code1);
+  *has_setup_succeeded = SUCCEEDED(hr);
+  if (FAILED(hr)) {
+    OPT_LOG(LE, (_T("[InstallSelf failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  if (!is_app_install) {
+    return S_OK;
+  }
+
+  hr = InstallApplications(is_machine,
+                           is_eula_required,
+                           args,
+                           session_id,
+                           splash_screen,
+                           has_ui_been_displayed,
+                           has_launched_handoff);
+  if (FAILED(hr)) {
+    OPT_LOG(LE, (_T("[InstallApplications failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+};
+
+HRESULT InstallApplications(bool is_machine,
+                            bool is_eula_required,
+                            const CommandLineArgs& args,
+                            const CString& session_id,
+                            SplashScreen* splash_screen,
+                            bool* has_ui_been_displayed,
+                            bool* has_launched_handoff) {
+  ASSERT1(has_ui_been_displayed);
+  ASSERT1(has_launched_handoff);
+
+  *has_launched_handoff = false;
+
+  CString offline_dir;
+  const bool is_offline_install = CopyOfflineFiles(is_machine,
+                                                   args.extra.apps,
+                                                   &offline_dir);
+  if (!is_offline_install && oem_install_utils::IsOemInstalling(is_machine)) {
+    return GOOPDATE_E_OEM_WITH_ONLINE_INSTALLER;
+  }
+  if (!is_offline_install && is_eula_required) {
+    return GOOPDATE_E_EULA_REQURED_WITH_ONLINE_INSTALLER;
+  }
+
+  // Start the handoff to install the app.
+  scoped_process handoff_process;
+  HRESULT hr = LaunchHandoffProcess(is_machine,
+                                    offline_dir,
+                                    args,
+                                    session_id,
+                                    address(handoff_process));
+  if (FAILED(hr)) {
+    OPT_LOG(LE, (_T("[Failed to launch installed instance][0x%08x]"), hr));
+    return hr;
+  }
+
+  *has_launched_handoff = true;
+
+  OPT_LOG(L1, (_T("[Waiting for application install to complete]")));
+  uint32 exit_code(0);
+  hr = WaitForProcessExit(get(handoff_process),
+                          splash_screen,
+                          has_ui_been_displayed,
+                          &exit_code);
+
+  if (FAILED(hr)) {
+    OPT_LOG(LE, (_T("[Failed waiting for app install][0x%08x]"), hr));
+    return hr;
+  }
+  if (exit_code) {
+    OPT_LOG(LE, (_T("[Handoff exited with error][0x%08x]"), exit_code));
+    ASSERT1(FAILED(exit_code));
+    return exit_code;
+  }
+
+  return S_OK;
+}
+
+// The behavior depends on the OS:
+//  1. OS < Vista  : Fail.
+//  2. OS >= Vista : Try to elevate - causes a UAC dialog.
+// We should be here only in case of initial machine installs when the user is
+// not an elevated admin.
+HRESULT ElevateAndWait(const CString& cmd_line, DWORD* exit_code) {
+  OPT_LOG(L1, (_T("[Elevating][%s]"), cmd_line));
+  ASSERT1(!vista_util::IsUserAdmin());
+  ASSERT1(exit_code);
+
+  if (!vista_util::IsVistaOrLater()) {
+    // TODO(omaha): We could consider to ask for credentials here.
+    // This TODO existed in Omaha 1. How would we even do this?
+    CORE_LOG(LE, (_T("[Non Admin trying to install admin app]")));
+    ++metric_setup_machine_app_non_admin;
+    return GOOPDATE_E_NONADMIN_INSTALL_ADMIN_APP;
+  }
+
+  CString cmd_line_elevated(GetCmdLineTail(cmd_line));
+  cmd_line_elevated.AppendFormat(_T(" /%s"), kCmdLineInstallElevated);
+
+  HRESULT hr = goopdate_utils::StartElevatedSelfWithArgsAndWait(
+      cmd_line_elevated, exit_code);
+  if (FAILED(hr)) {
+    OPT_LOG(LE, (_T("[Starting elevated GoogleUpdate.exe failed][%s][0x%08x]"),
+                 cmd_line, hr));
+
+    // TODO(omaha3): Report hr somehow. Was reported in extra code in Omaha 2.
+    if (vista_util::IsUserNonElevatedAdmin()) {
+      return GOOPDATE_E_ELEVATION_FAILED_ADMIN;
+    } else {
+      return GOOPDATE_E_ELEVATION_FAILED_NON_ADMIN;
+    }
+  }
+
+  return S_OK;
+}
+
+bool CopyOfflineFiles(bool is_machine,
+                      const std::vector<CommandLineAppArgs>& apps,
+                      CString* offline_dir) {
+  ASSERT1(offline_dir);
+  offline_dir->Empty();
+
+  if (apps.empty()) {
+    return false;
+  }
+
+  GUID guid(GUID_NULL);
+  VERIFY1(SUCCEEDED(::CoCreateGuid(&guid)));
+  CString parent_offline_dir(
+      is_machine ?
+      ConfigManager::Instance()->GetMachineSecureOfflineStorageDir() :
+      ConfigManager::Instance()->GetUserOfflineStorageDir());
+  CString unique_offline_dir =
+      ConcatenatePath(parent_offline_dir, GuidToString(guid));
+  VERIFY1(SUCCEEDED(CreateDir(unique_offline_dir, NULL)));
+
+  HRESULT hr = CopyOfflineManifest(unique_offline_dir);
+  if (FAILED(hr)) {
+    CORE_LOG(L3, (_T("[CopyOfflineManifest failed][0x%08x]"), hr));
+    return false;
+  }
+
+  for (size_t i = 0; i < apps.size(); ++i) {
+    const GUID& app_id = apps[i].app_guid;
+    HRESULT hr = CopyOfflineFilesForApp(GuidToString(app_id),
+                                        unique_offline_dir);
+    if (FAILED(hr)) {
+      ASSERT(false, (_T("[CopyOfflineFilesForApp failed][0x%08x]"), hr));
+      return false;
+    }
+  }
+
+  CORE_LOG(L3, (_T("[CopyOfflineFiles done][%s]"), unique_offline_dir));
+  *offline_dir = unique_offline_dir;
+  return true;
+}
+
+HRESULT CopyOfflineManifest(const CString& offline_dir) {
+  CORE_LOG(L3, (_T("[CopyOfflineManifest][%s]"), offline_dir));
+
+  // Copy offline manifest into "<offline_dir>\<kOfflineManifestFileName>".
+  CString setup_temp_dir(app_util::GetCurrentModuleDirectory());
+  CString source_manifest_path = ConcatenatePath(setup_temp_dir,
+                                                 kOfflineManifestFileName);
+  if (!File::Exists(source_manifest_path)) {
+    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+  }
+  CString dest_manifest_path = ConcatenatePath(offline_dir,
+                                               kOfflineManifestFileName);
+  HRESULT hr = File::Copy(source_manifest_path, dest_manifest_path, true);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[File copy failed][%s][%s][0x%08x]"),
+                   source_manifest_path, dest_manifest_path, hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT CopyOfflineFilesForApp(const CString& app_id,
+                               const CString& offline_dir) {
+  CORE_LOG(L3, (_T("[CopyOfflineFilesForApp][%s][%s]"),
+                 app_id, offline_dir));
+
+  CString setup_temp_dir(app_util::GetCurrentModuleDirectory());
+  CString pattern;
+  // Copy the installer files that are named with the pattern "*.<app_id>" to
+  // the directory "<offline_dir>\<app_id>".
+  pattern.Format(_T("*.%s"), app_id);
+  std::vector<CString> files;
+  HRESULT hr = FindFiles(setup_temp_dir, pattern, &files);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[FindFiles failed][0x%08x]"), hr));
+    return hr;
+  }
+  if (files.empty()) {
+    CORE_LOG(LE, (_T("[FindFiles found no files]")));
+    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+  }
+
+  CString offline_app_dir = ConcatenatePath(offline_dir, app_id);
+  if (File::IsDirectory(offline_app_dir)) {
+    VERIFY1(SUCCEEDED(DeleteDirectoryFiles(offline_app_dir)));
+  } else {
+    hr = CreateDir(offline_app_dir, NULL);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[CreateDir failed][%s]"), offline_app_dir));
+      return hr;
+    }
+  }
+
+  for (size_t i = 0; i < files.size(); ++i) {
+    const CString& file_name = files[i];
+    ASSERT1(file_name.GetLength() > app_id.GetLength());
+
+    CPath renamed_file_name(file_name);
+    renamed_file_name.RemoveExtension();
+    ASSERT1(file_name.Left(file_name.GetLength() - app_id.GetLength() - 1) ==
+            static_cast<const CString&>(renamed_file_name));
+    CString new_file_path = ConcatenatePath(offline_app_dir, renamed_file_name);
+    CORE_LOG(L4, (_T("[new_file_path][%s]"), new_file_path));
+
+    CString source_file_path = ConcatenatePath(setup_temp_dir, file_name);
+    hr = File::Copy(source_file_path, new_file_path, true);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[Copy failed][%s][%s]"),
+                    source_file_path, new_file_path));
+      return hr;
+    }
+  }
+
+  return S_OK;
+}
+
+
+// TODO(omaha3): This implementation requires updating this code whenever a
+// new option is added. We could do a string replace of "/install" with
+// "/handoff", but we'd need to remove things such as /oem. Alternatively,
+// allow a CommandLineBuilder to be populated with existing args. Doing
+// replacement would allow us to only pass one copy of the command line to
+// Install() instead of passing both the string and struct. Be sure to handle
+// /appargs when making any changes.
+// TODO(omaha): Extract the command line building and unit test it.
+HRESULT LaunchHandoffProcess(bool is_machine,
+                             const CString& offline_dir,
+                             const CommandLineArgs& install_args,
+                             const CString& session_id,
+                             HANDLE* process) {   // process can be NULL.
+  CORE_LOG(L2, (_T("[LaunchHandoffProcess]")));
+
+  // The install source has been either specified or set to a default value by
+  // the time the execution flow reaches this function. The install source is
+  // propagated to the handoff process except in certain offline install cases,
+  // such as a tagged offline installer or an offline installer that did not
+  // have an install source.
+  //
+  // TODO(omaha): refactor so that the handling of the install source is
+  // encapsulated in just one module.
+  ASSERT1(!install_args.install_source.IsEmpty());
+  ASSERT1(!install_args.extra_args_str.IsEmpty());
+
+  CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL);
+
+  builder.set_is_silent_set(install_args.is_silent_set);
+  builder.set_is_eula_required_set(install_args.is_eula_required_set);
+  builder.set_extra_args(install_args.extra_args_str);
+  builder.set_app_args(install_args.app_args_str);
+  builder.set_install_source(install_args.install_source);
+  builder.set_session_id(session_id);
+
+  if (!offline_dir.IsEmpty()) {
+    builder.SetOfflineDir(offline_dir);
+    const CString& install_source(install_args.install_source);
+    if (install_source == kCmdLineInstallSource_InstallDefault ||
+        install_source == kCmdLineInstallSource_TaggedMetainstaller) {
+      builder.set_install_source(kCmdLineInstallSource_Offline);
+    }
+  }
+
+  CString cmd_line = builder.GetCommandLineArgs();
+
+  HRESULT hr = goopdate_utils::StartGoogleUpdateWithArgs(is_machine,
+                                                         cmd_line,
+                                                         process);
+  if (FAILED(hr)) {
+    OPT_LOG(LE, (_T("[Google Update hand off failed][%s][0x%08x]"),
+                 cmd_line, hr));
+    // TODO(omaha3): Report hr somehow. Was reported in extra code in Omaha 2.
+    return GOOPDATE_E_HANDOFF_FAILED;
+  }
+
+  return S_OK;
+}
+
+HRESULT WaitForProcessExit(HANDLE process,
+                           SplashScreen* splash_screen,
+                           bool* has_ui_been_displayed,
+                           uint32* exit_code) {
+  CORE_LOG(L3, (_T("[WaitForProcessExit]")));
+  ASSERT1(process);
+  ASSERT1(exit_code);
+  ASSERT1(has_ui_been_displayed);
+  *exit_code = 0;
+
+  int res = ::WaitForInputIdle(process, INFINITE);
+  if (res == 0) {
+    *has_ui_been_displayed = true;
+  }
+  if (splash_screen) {
+    splash_screen->Dismiss();
+  }
+
+  res = ::WaitForSingleObject(process, INFINITE);
+  ASSERT1(WAIT_OBJECT_0 == res);
+  if (WAIT_FAILED == res) {
+    DWORD error = ::GetLastError();
+    CORE_LOG(LE, (_T("[::WaitForMultipleObjects failed][%u]"), error));
+    return HRESULT_FROM_WIN32(error);
+  }
+
+  // Get the exit code.
+  DWORD local_exit_code = 0;
+  if (::GetExitCodeProcess(process, &local_exit_code)) {
+    CORE_LOG(L2, (_T("[process exited][PID %u][exit code 0x%08x]"),
+                  Process::GetProcessIdFromHandle(process), local_exit_code));
+    *exit_code = local_exit_code;
+  } else {
+    DWORD error = ::GetLastError();
+    CORE_LOG(LE, (_T("[::GetExitCodeProcess failed][%u]"), error));
+    return HRESULT_FROM_WIN32(error);
+  }
+
+  return S_OK;
+}
+
+// TODO(omaha): needs to handle errors when loading the strings.
+CString GetErrorText(HRESULT error, const CString& bundle_name) {
+  ASSERT1(!bundle_name.IsEmpty());
+
+  CString error_text;
+
+  switch (error) {
+    case GOOPDATE_E_INSTALL_ELEVATED_PROCESS_NEEDS_ELEVATION:
+    case GOOPDATE_E_NONADMIN_INSTALL_ADMIN_APP:
+      error_text.FormatMessage(IDS_NEED_ADMIN_TO_INSTALL, bundle_name);
+      break;
+    case GOOPDATE_E_ELEVATION_FAILED_ADMIN:
+    case GOOPDATE_E_ELEVATION_FAILED_NON_ADMIN:
+      error_text.FormatMessage(IDS_ELEVATION_FAILED, bundle_name);
+      break;
+    case GOOPDATE_E_FAILED_TO_GET_LOCK:
+    case GOOPDATE_E_FAILED_TO_GET_LOCK_MATCHING_INSTALL_PROCESS_RUNNING:
+    case GOOPDATE_E_FAILED_TO_GET_LOCK_NONMATCHING_INSTALL_PROCESS_RUNNING:
+    case GOOPDATE_E_FAILED_TO_GET_LOCK_UPDATE_PROCESS_RUNNING:
+      {
+        CString company_name;
+        VERIFY1(company_name.LoadString(IDS_FRIENDLY_COMPANY_NAME));
+        error_text.FormatMessage(IDS_APPLICATION_INSTALLING_GOOGLE_UPDATE,
+                                 company_name,
+                                 bundle_name);
+      }
+      break;
+    case GOOPDATE_E_INSTANCES_RUNNING:
+      {
+        CString company_name;
+        VERIFY1(company_name.LoadString(IDS_FRIENDLY_COMPANY_NAME));
+        error_text.FormatMessage(IDS_INSTANCES_RUNNING_AFTER_SHUTDOWN,
+                                 company_name,
+                                 bundle_name);
+      }
+      break;
+    case GOOPDATE_E_RUNNING_INFERIOR_MSXML:
+      error_text.FormatMessage(IDS_WINDOWS_IS_NOT_UP_TO_DATE, bundle_name);
+      break;
+    case GOOPDATE_E_HANDOFF_FAILED:
+      error_text.FormatMessage(IDS_HANDOFF_FAILED, bundle_name);
+      break;
+    default:
+      {
+        CString product_name;
+        VERIFY1(product_name.LoadString(IDS_PRODUCT_DISPLAY_NAME));
+        error_text.FormatMessage(IDS_SETUP_FAILED, product_name, error);
+      }
+      break;
+  }
+
+  return error_text;
+}
+
+// Displays an error in the Google Update UI and sends a ping if allowed.
+void HandleInstallError(HRESULT error,
+                        int extra_code1,
+                        const CString& session_id,
+                        bool is_machine,
+                        bool is_interactive,
+                        bool is_eula_required,
+                        bool is_oem_install,
+                        const CString& current_version,
+                        const CString& install_source,
+                        const CommandLineExtraArgs& extra_args,
+                        bool has_setup_succeeded,
+                        bool has_launched_handoff,
+                        bool* has_ui_been_displayed) {
+  ASSERT1(FAILED(error));
+  ASSERT1(has_ui_been_displayed);
+
+  const CString& bundle_name(extra_args.bundle_name);
+  const CString error_text(GetErrorText(error, bundle_name));
+
+  OPT_LOG(LE, (_T("[Failed to install][0x%08x][%s]"), error, error_text));
+
+  if (is_interactive && !*has_ui_been_displayed) {
+    CString primary_app_id;
+    if (!extra_args.apps.empty()) {
+      primary_app_id = GuidToString(extra_args.apps[0].app_guid);
+    }
+    *has_ui_been_displayed = client_utils::DisplayError(
+                                 is_machine,
+                                 bundle_name,
+                                 error,
+                                 extra_code1,
+                                 error_text,
+                                 primary_app_id,
+                                 extra_args.language,
+                                 extra_args.installation_id,
+                                 extra_args.brand_code);
+  }
+
+  // Send an EVENT_INSTALL_COMPLETE ping for Omaha and wait for the ping to be
+  // sent. This ping may cause a firewall prompt since the process sending the
+  // ping may run from a temporary directory.
+  //
+  // An EVENT_INSTALL_COMPLETE ping for the apps is also sent in the case the
+  /// handoff process did not launch successfully, otherwise, the handoff
+  // process is responsible for sending this ping.
+  //
+  // Setup can fail before setting either eula or oem states in the
+  // registry. Therefore, ConfigManager::CanUseNetwork can't be called yet.
+  if (is_eula_required || is_oem_install) {
+    return;
+  }
+  Ping ping(is_machine, session_id, install_source);
+  ping.LoadAppDataFromExtraArgs(extra_args);
+  if (!has_setup_succeeded) {
+    PingEventPtr setup_install_complete_ping_event(
+        new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE,        //  Type 2.
+                      PingEvent::EVENT_RESULT_ERROR,
+                      error,
+                      extra_code1));
+    const CString next_version(GetVersionString());
+    ping.BuildOmahaPing(current_version,
+                        next_version,
+                        setup_install_complete_ping_event);
+  }
+  if (!has_launched_handoff) {
+    PingEventPtr install_complete_ping_event(
+        new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE,         //  Type 2.
+                      PingEvent::EVENT_RESULT_ERROR,
+                      error,
+                      extra_code1));
+    ping.BuildAppsPing(install_complete_ping_event);
+  }
+  HRESULT hr = ping.Send(false);
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[Ping::Send failed][0x%x]"), hr));
+  }
+}
+
+}  // namespace internal
+
+// This function handles command-line installs. This is the only function that
+// can install Omaha in non-update cases. If elevation is required, the function
+// elevates an instance of Omaha and waits for that instance to exit before
+// returning. If needed, the function starts a handoff process to install the
+// applications and waits for the handoff process to exit.
+//
+// 'install_cmd_line' parameter is the command line for the current instance and
+// used to elevate if necessary.
+// 'args' parameter is the parsed args corresponding to install_cmd_line and it
+// is used to build the handoff command line if necessary.
+HRESULT Install(bool is_interactive,
+                bool is_app_install,
+                bool is_eula_required,
+                bool is_oem_install,
+                bool is_install_elevated_instance,
+                const CString& install_cmd_line,
+                const CommandLineArgs& args,
+                bool* is_machine,
+                bool* has_ui_been_displayed) {
+  ASSERT1(!install_cmd_line.IsEmpty());
+  ASSERT1(is_machine);
+  ASSERT1(has_ui_been_displayed);
+
+  CORE_LOG(L2, (_T("[Install][%d][%d][%s]"),
+                *is_machine, is_interactive, install_cmd_line));
+  ++metric_setup_install_total;
+  if (is_install_elevated_instance) {
+    ++metric_setup_uac_succeeded;
+  }
+
+  if (!*is_machine &&
+      vista_util::IsVistaOrLater() &&
+      vista_util::IsUserAdmin()) {
+    ++metric_setup_user_app_admin;
+  }
+
+  // Allocate a session ID to connect all update checks and pings involved
+  // with this run of Omaha.  This will need to be passed along to any child
+  // processes we create.
+  CString session_id;
+  VERIFY1(SUCCEEDED(GetGuid(&session_id)));
+
+  // 'current_version' corresponds to the value of 'pv' read from
+  // registry. This value is either empty, in the case of a new install or
+  // the version of the installed Omaha before the setup code ran.
+  CString current_version;
+  app_registry_utils::GetAppVersion(*is_machine,
+                                    kGoogleUpdateAppId,
+                                    &current_version);
+
+  bool has_setup_succeeded  = false;
+  bool has_launched_handoff = false;
+
+  if (IsElevationRequired(*is_machine)) {
+    ASSERT1(!ConfigManager::Instance()->IsWindowsInstalling());
+
+    DWORD exit_code = 0;
+    HRESULT hr = internal::DoElevation(is_interactive,
+                                       is_install_elevated_instance,
+                                       install_cmd_line,
+                                       &exit_code);
+
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[DoElevation failed][%s][0x%08x]"),
+                    install_cmd_line, hr));
+
+      bool attempt_per_user_install = false;
+      if (is_interactive &&
+          args.extra.apps[0].needs_admin == NEEDS_ADMIN_PREFERS) {
+        if (hr == GOOPDATE_E_NONADMIN_INSTALL_ADMIN_APP ||
+            hr == GOOPDATE_E_INSTALL_ELEVATED_PROCESS_NEEDS_ELEVATION) {
+          attempt_per_user_install = true;
+        } else {
+          *has_ui_been_displayed = client_utils::DisplayContinueAsNonAdmin(
+              args.extra.bundle_name, &attempt_per_user_install);
+        }
+      }
+
+      if (attempt_per_user_install) {
+        *is_machine = false;
+        CString no_admin_cmd_line(String_ReplaceIgnoreCase(install_cmd_line,
+                                                           kNeedsAdminPrefers,
+                                                           kNeedsAdminNo));
+        CommandLineArgs no_admin_args = args;
+        no_admin_args.extra.apps[0].needs_admin = NEEDS_ADMIN_NO;
+        no_admin_args.extra_args_str = String_ReplaceIgnoreCase(
+            no_admin_args.extra_args_str, kNeedsAdminPrefers, kNeedsAdminNo);
+
+        return Install(is_interactive,
+                       is_app_install,
+                       is_eula_required,
+                       is_oem_install,
+                       is_install_elevated_instance,
+                       no_admin_cmd_line,
+                       no_admin_args,
+                       is_machine,
+                       has_ui_been_displayed);
+      }
+
+      internal::HandleInstallError(hr,
+                                   0,
+                                   session_id,
+                                   *is_machine,
+                                   is_interactive,
+                                   is_eula_required,
+                                   is_oem_install,
+                                   current_version,
+                                   args.install_source,
+                                   args.extra,
+                                   has_setup_succeeded,
+                                   has_launched_handoff,
+                                   has_ui_been_displayed);
+      return hr;
+    }
+
+    // TODO(omaha): waiting for input idle of an elevated process fails if the
+    // caller is not elevated. The code assumes that the elevated process
+    // displays UI if the elevation succeeded. It is possible that the elevated
+    // process ran but had terminated before displaying UI. This is an uncommon
+    // case and for simplicity, it is not handled here.
+    *has_ui_been_displayed = true;
+
+    return exit_code;
+  }
+
+  SplashScreen splash_screen(args.extra.bundle_name);
+  if (is_interactive) {
+    splash_screen.Show();
+  }
+
+  int extra_code1 = 0;
+  HRESULT hr = internal::DoInstall(*is_machine,
+                                   is_app_install,
+                                   is_eula_required,
+                                   is_oem_install,
+                                   current_version,
+                                   args,
+                                   session_id,
+                                   &splash_screen,
+                                   &extra_code1,
+                                   &has_setup_succeeded,
+                                   &has_launched_handoff,
+                                   has_ui_been_displayed);
+  if (is_interactive) {
+    splash_screen.Dismiss();
+  }
+
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[DoInstall failed][0x%08x]"), hr));
+    internal::HandleInstallError(hr,
+                                 extra_code1,
+                                 session_id,
+                                 *is_machine,
+                                 is_interactive,
+                                 is_eula_required,
+                                 is_oem_install,
+                                 current_version,
+                                 args.install_source,
+                                 args.extra,
+                                 has_setup_succeeded,
+                                 has_launched_handoff,
+                                 has_ui_been_displayed);
+    return hr;
+  }
+
+  if (!is_app_install) {
+    // TODO(omaha): Display UI if we want to support interactive Omaha-only
+    // installs.
+    ASSERT1(!is_interactive);
+    // TODO(omaha3): Figure out a way to send a ping from the installed location
+    // for Omaha-only install success.
+  }
+
+  ++metric_setup_install_succeeded;
+  return S_OK;
+}
+
+HRESULT OemInstall(bool is_interactive,
+                   bool is_app_install,
+                   bool is_eula_required,
+                   bool is_install_elevated_instance,
+                   const CString& install_cmd_line,
+                   const CommandLineArgs& args,
+                   bool* is_machine,
+                   bool* has_ui_been_displayed) {
+  ASSERT1(is_machine);
+  ASSERT1(has_ui_been_displayed);
+
+  // OEM is handled as a special case, and the state must be correct before
+  // calling Install().
+  HRESULT hr = oem_install_utils::SetOemInstallState(*is_machine);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[SetOemInstallState failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = Install(is_interactive,
+               is_app_install,
+               is_eula_required,
+               true,
+               is_install_elevated_instance,
+               install_cmd_line,
+               args,
+               is_machine,
+               has_ui_been_displayed);
+
+  if (FAILED(hr)) {
+    VERIFY1(SUCCEEDED(oem_install_utils::ResetOemInstallState(*is_machine)));
+    return hr;
+  }
+
+  ASSERT1(oem_install_utils::IsOemInstalling(*is_machine));
+  return S_OK;
+}
+
+}  // namespace omaha
diff --git a/client/install.h b/client/install.h
new file mode 100644
index 0000000..666b453
--- /dev/null
+++ b/client/install.h
@@ -0,0 +1,52 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Functions related to the /install process.
+
+#ifndef OMAHA_CLIENT_INSTALL_H_
+#define OMAHA_CLIENT_INSTALL_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include "omaha/common/const_goopdate.h"
+
+namespace omaha {
+
+struct CommandLineArgs;
+
+// Elevates if necessary and then installs.
+HRESULT Install(bool is_interactive,
+                bool is_app_install,
+                bool is_eula_required,
+                bool is_oem_install,
+                bool is_install_elevated_instance,
+                const CString& install_cmd_line,
+                const CommandLineArgs& args,
+                bool* is_machine,
+                bool* has_ui_been_displayed);
+
+// Installs Omaha and/or apps in OEM state.
+HRESULT OemInstall(bool is_interactive,
+                   bool is_app_install,
+                   bool is_eula_required,
+                   bool is_install_elevated_instance,
+                   const CString& install_cmd_line,
+                   const CommandLineArgs& args,
+                   bool* is_machine,
+                   bool* has_ui_been_displayed);
+
+}  // namespace omaha
+
+#endif  // OMAHA_CLIENT_INSTALL_H_
diff --git a/client/install_apps.cc b/client/install_apps.cc
new file mode 100644
index 0000000..b0e0cc5
--- /dev/null
+++ b/client/install_apps.cc
@@ -0,0 +1,626 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/client/install_apps.h"
+#include <atlsafe.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/reactor.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/shutdown_callback.h"
+#include "omaha/base/shutdown_handler.h"
+#include "omaha/base/string.h"
+#include "omaha/base/time.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vista_utils.h"
+#include "omaha/client/bundle_creator.h"
+#include "omaha/client/bundle_installer.h"
+#include "omaha/client/client_metrics.h"
+#include "omaha/client/client_utils.h"
+#include "omaha/client/help_url_builder.h"
+#include "omaha/client/install_apps_internal.h"
+#include "omaha/client/install_progress_observer.h"
+#include "omaha/client/resource.h"
+#include "omaha/common/command_line.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/lang.h"
+#include "omaha/common/ping.h"
+#include "omaha/common/update3_utils.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/ui/progress_wnd.h"
+
+namespace omaha {
+
+namespace {
+
+// Implements the UI progress window.
+class SilentProgressObserver : public InstallProgressObserver {
+ public:
+  explicit SilentProgressObserver(BundleInstaller* installer)
+      : installer_(installer) {
+    ASSERT1(installer);
+  }
+
+  virtual void OnCheckingForUpdate() {
+    CORE_LOG(L3, (_T("[SilentProgressObserver::OnCheckingForUpdate]")));
+  }
+
+  virtual void OnUpdateAvailable(const CString& app_name,
+                                 const CString& version_string) {
+    CORE_LOG(L3, (_T("[SilentProgressObserver::OnUpdateAvailable][%s][%s]"),
+                  app_name, version_string));
+    UNREFERENCED_PARAMETER(app_name);
+    UNREFERENCED_PARAMETER(version_string);
+  }
+
+  virtual void OnWaitingToDownload(const CString& app_name) {
+    CORE_LOG(L3, (_T("[SilentProgressObserver::OnWaitingToDownload][%s]"),
+                  app_name));
+    UNREFERENCED_PARAMETER(app_name);
+  }
+
+  virtual void OnDownloading(const CString& app_name,
+                             int time_remaining_ms,
+                             int pos) {
+    CORE_LOG(L5, (_T("[SilentProgressObserver::OnDownloading]")
+                  _T("[%s][remaining ms=%d][pos=%d]"),
+                  app_name, time_remaining_ms, pos));
+    UNREFERENCED_PARAMETER(app_name);
+    UNREFERENCED_PARAMETER(time_remaining_ms);
+    UNREFERENCED_PARAMETER(pos);
+  }
+
+  virtual void OnWaitingRetryDownload(const CString& app_name,
+                                      time64 next_retry_time) {
+    CORE_LOG(L5, (_T("[SilentProgressObserver::OnWaitingRetryDownload]")
+                  _T("[%s][next retry time=%llu]"),
+                  app_name, next_retry_time));
+    UNREFERENCED_PARAMETER(app_name);
+    UNREFERENCED_PARAMETER(next_retry_time);
+  }
+
+  virtual void OnWaitingToInstall(const CString& app_name,
+                                  bool* can_start_install) {
+    CORE_LOG(L3, (_T("[SilentProgressObserver::OnWaitingToInstall][%s]"),
+                  app_name));
+    ASSERT1(can_start_install);
+    UNREFERENCED_PARAMETER(app_name);
+    UNREFERENCED_PARAMETER(can_start_install);
+  }
+
+  virtual void OnInstalling(const CString& app_name) {
+    CORE_LOG(L5, (_T("[SilentProgressObserver::OnInstalling][%s]"), app_name));
+    UNREFERENCED_PARAMETER(app_name);
+  }
+
+  virtual void OnPause() {
+    CORE_LOG(L3, (_T("[SilentProgressObserver::OnPause]")));
+  }
+
+  // Terminates the message loop.
+  virtual void OnComplete(const ObserverCompletionInfo& observer_info) {
+    CORE_LOG(L3, (_T("[SilentProgressObserver::OnComplete][%s]"),
+                  observer_info.ToString()));
+    UNREFERENCED_PARAMETER(observer_info);
+
+    installer_->DoExit();
+    CORE_LOG(L1, (_T("[SilentProgressObserver][DoExit() called]")));
+  }
+
+ private:
+  BundleInstaller *const installer_;
+};
+
+class OnDemandEvents : public OnDemandEventsInterface {
+ public:
+  explicit OnDemandEvents(BundleInstaller* installer)
+      : installer_(installer) {
+    ASSERT1(installer);
+  }
+  virtual void DoClose() {
+    installer_->DoClose();
+  }
+  virtual void DoExit() {
+    installer_->DoExit();
+  }
+
+ private:
+  BundleInstaller *const installer_;
+};
+
+// This ATL module is used for BundleInstaller message loop shutdown intrinsics.
+// It is also needed for cases where the Update3 server COM objects are created
+// inproc.
+class BundleAtlModule : public CAtlExeModuleT<BundleAtlModule> {
+ public:
+  explicit BundleAtlModule() : allow_post_quit_(false) {
+    // Disable the delay on shutdown mechanism in CAtlExeModuleT.
+    m_bDelayShutdown = false;
+  }
+  ~BundleAtlModule() {}
+
+  LONG Unlock() throw() {
+    LONG lRet = CComGlobalsThreadModel::Decrement(&m_nLockCnt);
+
+    if (lRet == 0 && allow_post_quit_) {
+      ::PostThreadMessage(m_dwMainThreadID, WM_QUIT, 0, 0);
+    }
+
+    return lRet;
+  }
+
+  // BundleAtlModule will only post WM_QUIT if enable_quit() is called, to avoid
+  // spurious WM_QUITs during bundle initialization.
+  void enable_quit() {
+    allow_post_quit_ = true;
+  }
+
+ private:
+  bool allow_post_quit_;
+
+  DISALLOW_COPY_AND_ASSIGN(BundleAtlModule);
+};
+
+}  // namespace
+
+namespace internal {
+
+bool IsBrowserRestartSupported(BrowserType browser_type) {
+  return (browser_type != BROWSER_UNKNOWN &&
+          browser_type != BROWSER_DEFAULT &&
+          browser_type < BROWSER_MAX);
+}
+
+InstallAppsWndEvents::InstallAppsWndEvents(bool is_machine,
+                                           BundleInstaller* installer,
+                                           BrowserType browser_type)
+    : is_machine_(is_machine),
+      installer_(installer),
+      browser_type_(browser_type) {
+  ASSERT1(installer_);
+}
+
+void InstallAppsWndEvents::DoClose() {
+  ASSERT1(installer_);
+  installer_->DoClose();
+}
+
+void InstallAppsWndEvents::DoExit() {
+  ASSERT1(installer_);
+  installer_->DoExit();
+}
+
+void InstallAppsWndEvents::DoCancel() {
+  ASSERT1(installer_);
+  installer_->DoCancel();
+}
+
+// TODO(omaha3): Need to address elevated Vista installs. Since we are doing
+// a handoff, we know that Omaha is installed and can do de-elevation.
+// However, BuildGetHelpUrl will return an empty string right now. Best to
+// just solve the general problem.
+bool InstallAppsWndEvents::DoLaunchBrowser(const CString& url) {
+  CORE_LOG(L2, (_T("[InstallAppsWndEvents::DoLaunchBrowser %s]"), url));
+  const BrowserType browser = BROWSER_UNKNOWN == browser_type_ ?
+                              BROWSER_DEFAULT :
+                              browser_type_;
+  return SUCCEEDED(goopdate_utils::LaunchBrowser(is_machine_, browser, url));
+}
+
+// Restarts the browser(s) and returns whether the browser was successfully
+// restarted.
+bool InstallAppsWndEvents::DoRestartBrowser(bool terminate_all_browsers,
+                                            const std::vector<CString>& urls) {
+  // UI should not trigger this call back if the browser type is unknown.
+  // Instead it should ask user to restart the browser(s) manually.
+  ASSERT1(IsBrowserRestartSupported(browser_type_));
+
+  BrowserType browser = browser_type_;
+  if (browser == BROWSER_DEFAULT) {
+    GetDefaultBrowserType(&browser);
+  }
+
+  TerminateBrowserResult browser_res;
+  TerminateBrowserResult default_res;
+  if (terminate_all_browsers) {
+    VERIFY1(SUCCEEDED(goopdate_utils::TerminateAllBrowsers(browser,
+                                                           &browser_res,
+                                                           &default_res)));
+  } else {
+    VERIFY1(SUCCEEDED(goopdate_utils::TerminateBrowserProcesses(browser,
+                                                                &browser_res,
+                                                                &default_res)));
+  }
+
+  BrowserType default_browser_type = BROWSER_UNKNOWN;
+  HRESULT hr = GetDefaultBrowserType(&default_browser_type);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[GetDefaultBrowserType failed][0x%08x]"), hr));
+  }
+
+  BrowserType browser_to_restart = BROWSER_UNKNOWN;
+  if (!goopdate_utils::GetBrowserToRestart(browser,
+                                           default_browser_type,
+                                           browser_res,
+                                           default_res,
+                                           &browser_to_restart)) {
+    CORE_LOG(LE, (_T("[GetBrowserToRestart returned false. Not launching.]")));
+    return false;
+  }
+  ASSERT1(IsBrowserRestartSupported(browser_to_restart));
+
+  bool succeeded = true;
+  for (size_t i = 0; i < urls.size(); ++i) {
+    succeeded &= SUCCEEDED(goopdate_utils::LaunchBrowser(is_machine_,
+                                                         browser_to_restart,
+                                                         urls[i]));
+  }
+
+  return succeeded;
+}
+
+// Initiates a reboot and returns whether it was iniated successfully.
+bool InstallAppsWndEvents::DoReboot() {
+  ASSERT(false, (_T("Not implemented.")));
+  return false;
+}
+
+CString GetBundleDisplayName(IAppBundle* app_bundle) {
+  if (!app_bundle) {
+    return client_utils::GetDefaultBundleName();
+  }
+
+  CComBSTR bundle_name;
+  HRESULT hr = app_bundle->get_displayName(&bundle_name);
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[get_displayName failed][0x%08x]"), hr));
+  }
+
+  return (SUCCEEDED(hr) && bundle_name.Length()) ?
+      CString(bundle_name) : client_utils::GetDefaultBundleName();
+}
+
+HRESULT CreateClientUI(bool is_machine,
+                       BrowserType browser_type,
+                       BundleInstaller* installer,
+                       IAppBundle* app_bundle,
+                       InstallProgressObserver** observer,
+                       OmahaWndEvents** ui_sink) {
+  ASSERT1(installer);
+  ASSERT1(observer);
+  ASSERT1(!*observer);
+  ASSERT1(ui_sink);
+  ASSERT1(!*ui_sink);
+
+  scoped_ptr<ProgressWnd> progress_wnd(
+      new ProgressWnd(installer->message_loop(), NULL));
+  ScopeGuard destroy_window_guard = MakeObjGuard(*progress_wnd,
+                                                 &ProgressWnd::DestroyWindow);
+
+  progress_wnd->set_is_machine(is_machine);
+  progress_wnd->set_bundle_name(internal::GetBundleDisplayName(app_bundle));
+
+  HRESULT hr = progress_wnd->Initialize();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  scoped_ptr<internal::InstallAppsWndEvents> progress_wnd_events(
+      new internal::InstallAppsWndEvents(is_machine, installer, browser_type));
+  progress_wnd->SetEventSink(progress_wnd_events.get());
+
+  progress_wnd->Show();
+  installer->SetBundleParentWindow(progress_wnd->m_hWnd);
+
+  destroy_window_guard.Dismiss();
+  *observer = progress_wnd.release();
+  *ui_sink = progress_wnd_events.release();
+  return S_OK;
+}
+
+// The order of construction is important because it ensures the objects are
+// deleted in a safe order (objects before their dependencies).
+// Any early returns before the message loop is run must call
+// progress_wnd.DestroyWindow(). Errors do not need to be reported in a UI
+// because they are handled further up the call stack.
+HRESULT DoInstallApps(BundleInstaller* installer,
+                      IAppBundle* app_bundle,
+                      bool is_machine,
+                      bool is_interactive,
+                      bool is_update_all_apps,
+                      BrowserType browser_type,
+                      bool* has_ui_been_displayed) {
+  CORE_LOG(L2, (_T("[DoInstallApps]")));
+  ASSERT1(installer);
+  ASSERT1(has_ui_been_displayed);
+
+  scoped_ptr<InstallProgressObserver> observer;
+  scoped_ptr<OmahaWndEvents> ui_sink;
+  bool listen_to_shutdown_event = false;
+
+  CComPtr<IAppBundle> app_bundle_ptr;
+  app_bundle_ptr.Attach(app_bundle);
+
+  HRESULT hr = S_OK;
+  if (is_interactive) {
+    hr = CreateClientUI(is_machine,
+                        browser_type,
+                        installer,
+                        app_bundle,
+                        address(observer),
+                        address(ui_sink));
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("CreateClientUI failed][0x%08x]"), hr));
+      return hr;
+    }
+    *has_ui_been_displayed = true;
+  } else {
+    observer.reset(new SilentProgressObserver(installer));
+    if (is_update_all_apps) {
+      listen_to_shutdown_event = true;
+    }
+  }
+
+  hr = installer->InstallBundle(is_machine,
+                                listen_to_shutdown_event,
+                                app_bundle_ptr.Detach(),
+                                observer.get());
+
+  observer.reset();
+
+  // ui_sink must be destroyed after observer and before installer.
+  ui_sink.reset();
+
+  CORE_LOG(L1, (_T("DoInstallApps returning][0x%08x]"), hr));
+  return hr;
+}
+
+void HandleInstallAppsError(HRESULT error,
+                            int extra_code1,
+                            bool is_machine,
+                            bool is_interactive,
+                            bool is_eula_accepted,
+                            bool is_oem_install,
+                            const CString& install_source,
+                            const CommandLineExtraArgs& extra_args,
+                            const CString& session_id,
+                            bool* has_ui_been_displayed) {
+  ASSERT1(FAILED(error));
+  ASSERT1(has_ui_been_displayed);
+
+  const CString& bundle_name = extra_args.bundle_name;
+  ASSERT1(!bundle_name.IsEmpty());
+
+  CString error_text;
+
+  switch (error) {
+    case GOOPDATE_E_USER_AND_ELEVATED_WITH_UAC_ON:
+      error_text.FormatMessage(IDS_USER_SHOULD_NOT_RUN_ELEVATED_WITH_UAC_ON,
+                               bundle_name);
+      break;
+    default: {
+      CString product_name;
+      VERIFY1(product_name.LoadString(IDS_PRODUCT_DISPLAY_NAME));
+      error_text.FormatMessage(IDS_SETUP_FAILED, product_name, error);
+      break;
+    }
+  }
+
+  OPT_LOG(LE, (_T("[Failed to install apps][0x%08x][%s]"), error, error_text));
+
+  if (is_interactive && !*has_ui_been_displayed) {
+    CString primary_app_id;
+    if (!extra_args.apps.empty()) {
+      primary_app_id = GuidToString(extra_args.apps[0].app_guid);
+    }
+
+    *has_ui_been_displayed = client_utils::DisplayError(
+                                 is_machine,
+                                 bundle_name,
+                                 error,
+                                 extra_code1,
+                                 error_text,
+                                 primary_app_id,
+                                 extra_args.language,
+                                 extra_args.installation_id,
+                                 extra_args.brand_code);
+  }
+
+  if (!is_eula_accepted || is_oem_install) {
+    return;
+  }
+
+  // Send an install complete ping and do not wait for the ping to be sent.
+  // Since Omaha has been installed at this point, it should be able to
+  // send this ping without blocking the user flow.
+  Ping ping(is_machine, session_id, install_source);
+  ping.LoadAppDataFromExtraArgs(extra_args);
+  PingEventPtr ping_event(
+      new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE,
+                    PingEvent::EVENT_RESULT_HANDOFF_ERROR,
+                    error,
+                    extra_code1));
+  ping.BuildAppsPing(ping_event);
+  HRESULT send_result = ping.Send(true);
+  if (FAILED(send_result)) {
+    CORE_LOG(LW, (_T("[Ping::Send failed][0x%x]"), send_result));
+  }
+}
+
+}  // namespace internal
+
+HRESULT UpdateAppOnDemand(bool is_machine,
+                          const CString& app_id,
+                          bool is_update_check_only,
+                          const CString& session_id,
+                          HANDLE impersonation_token,
+                          HANDLE primary_token,
+                          OnDemandObserver* observer) {
+  CORE_LOG(L2, (_T("[UpdateAppOnDemand][%d][%s][%d]"),
+                is_machine, app_id, is_update_check_only));
+
+  const TCHAR* install_source = is_update_check_only ?
+                                kCmdLineInstallSource_OnDemandCheckForUpdate :
+                                kCmdLineInstallSource_OnDemandUpdate;
+  CComPtr<IAppBundle> app_bundle;
+  HRESULT hr = bundle_creator::CreateForOnDemand(is_machine,
+                                                 app_id,
+                                                 install_source,
+                                                 session_id,
+                                                 impersonation_token,
+                                                 primary_token,
+                                                 &app_bundle);
+  if (SUCCEEDED(hr)) {
+    BundleInstaller installer(NULL,   // No help URL for on-demand.
+                              false,  // Is not update all apps.
+                              is_update_check_only,
+                              false);
+    hr = installer.Initialize();
+    if (SUCCEEDED(hr)) {
+      OnDemandEvents install_events(&installer);
+      observer->SetEventSink(&install_events);
+
+      // TODO(omaha3): Listen to shutdown event during installation?
+      return installer.InstallBundle(is_machine,
+                                     false,
+                                     app_bundle.Detach(),
+                                     observer);
+    }
+  }
+
+  // The observer must be notified that the bundle has completed with an error
+  // since the bundle will not be processed.
+  observer->OnComplete(ObserverCompletionInfo(COMPLETION_CODE_ERROR));
+  return hr;
+}
+
+HRESULT InstallApps(bool is_machine,
+                    bool is_interactive,
+                    bool is_eula_accepted,
+                    bool is_oem_install,
+                    bool is_offline,
+                    const CString& offline_directory,
+                    const CommandLineExtraArgs& extra_args,
+                    const CString& install_source,
+                    const CString& session_id,
+                    bool* has_ui_been_displayed) {
+  CORE_LOG(L2, (_T("[InstallApps][is_machine: %u][is_interactive: %u]")
+      _T("[is_eula_accepted: %u][is_oem_install: %u][is_offline: %u]")
+      _T("[offline_directory: %s]"), is_machine, is_interactive,
+      is_eula_accepted, is_oem_install, is_offline, offline_directory));
+  ASSERT1(has_ui_been_displayed);
+
+  BundleAtlModule atl_module;
+
+  CComPtr<IAppBundle> app_bundle;
+  HRESULT hr = bundle_creator::CreateFromCommandLine(is_machine,
+                                                     is_eula_accepted,
+                                                     is_offline,
+                                                     offline_directory,
+                                                     extra_args,
+                                                     install_source,
+                                                     session_id,
+                                                     is_interactive,
+                                                     &app_bundle);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[bundle_creator::CreateFromCommandLine][0x%08x]"), hr));
+    internal::HandleInstallAppsError(hr,
+                                     0,
+                                     is_machine,
+                                     is_interactive,
+                                     is_eula_accepted,
+                                     is_oem_install,
+                                     install_source,
+                                     extra_args,
+                                     session_id,
+                                     has_ui_been_displayed);
+    return hr;
+  }
+
+  BundleInstaller installer(
+      new HelpUrlBuilder(is_machine,
+                         extra_args.language,
+                         extra_args.installation_id,
+                         extra_args.brand_code),
+      false,  //  is_update_all_apps
+      false,  //  is_update_check_only
+      internal::IsBrowserRestartSupported(extra_args.browser_type));
+  hr = installer.Initialize();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  atl_module.enable_quit();
+  return internal::DoInstallApps(&installer,
+                                 app_bundle.Detach(),
+                                 is_machine,
+                                 is_interactive,
+                                 false,  // Is not update all apps.
+                                 extra_args.browser_type,
+                                 has_ui_been_displayed);
+}
+
+HRESULT UpdateAllApps(bool is_machine,
+                      bool is_interactive,
+                      const CString& install_source,
+                      const CString& display_language,
+                      const CString& session_id,
+                      bool* has_ui_been_displayed) {
+  CORE_LOG(L2, (_T("[UpdateAllApps][%u][%u]"), is_machine, is_interactive));
+  ASSERT1(has_ui_been_displayed);
+
+  BundleAtlModule atl_module;
+
+  CComPtr<IAppBundle> app_bundle;
+  HRESULT hr = bundle_creator::Create(is_machine,
+                                      display_language,
+                                      install_source,
+                                      session_id,
+                                      is_interactive,
+                                      &app_bundle);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[bundle_creator::Create failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  BundleInstaller installer(new HelpUrlBuilder(is_machine,
+                                               display_language,
+                                               GUID_NULL,
+                                               CString()),
+                            true,   //  is_update_all_apps
+                            false,  //  is_update_check_only
+                            BROWSER_UNKNOWN);
+  hr = installer.Initialize();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  atl_module.enable_quit();
+  return internal::DoInstallApps(&installer,
+                                 app_bundle.Detach(),
+                                 is_machine,
+                                 is_interactive,
+                                 true,        // Is update all apps.
+                                 BROWSER_UNKNOWN,
+                                 has_ui_been_displayed);
+}
+
+}  // namespace omaha
diff --git a/client/install_apps.h b/client/install_apps.h
new file mode 100644
index 0000000..46a91fd
--- /dev/null
+++ b/client/install_apps.h
@@ -0,0 +1,73 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Functions for installing applications.
+
+#ifndef OMAHA_CLIENT_INSTALL_APPS_H_
+#define OMAHA_CLIENT_INSTALL_APPS_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include "omaha/client/install_progress_observer.h"
+
+namespace omaha {
+
+struct CommandLineExtraArgs;
+
+// TODO(omaha): Similar definitions with just DoClose() appear elsewhere.
+// Is there a common name we can give these?
+class OnDemandEventsInterface {
+ public:
+  virtual ~OnDemandEventsInterface() {}
+  virtual void DoClose() = 0;
+  virtual void DoExit() = 0;
+};
+
+class OnDemandObserver : public InstallProgressObserver {
+ public:
+  virtual void SetEventSink(OnDemandEventsInterface* event_sink) = 0;
+};
+
+// TODO(omaha3): Should these be in a class or namespace?
+
+HRESULT UpdateAppOnDemand(bool is_machine,
+                          const CString& app_id,
+                          bool is_update_check_only,
+                          const CString& session_id,
+                          HANDLE impersonation_token,
+                          HANDLE primary_token,
+                          OnDemandObserver* observer);
+
+HRESULT InstallApps(bool is_machine,
+                    bool is_interactive,
+                    bool is_eula_accepted,
+                    bool is_oem_install,
+                    bool is_offline,
+                    const CString& offline_dir,
+                    const CommandLineExtraArgs& extra_args,
+                    const CString& install_source,
+                    const CString& session_id,
+                    bool* has_ui_been_displayed);
+
+HRESULT UpdateAllApps(bool is_machine,
+                      bool is_interactive,
+                      const CString& install_source,
+                      const CString& display_language,
+                      const CString& session_id,
+                      bool* has_ui_been_displayed);
+
+}  // namespace omaha
+
+#endif  // OMAHA_CLIENT_INSTALL_APPS_H_
diff --git a/client/install_apps_internal.h b/client/install_apps_internal.h
new file mode 100644
index 0000000..5eb41d6
--- /dev/null
+++ b/client/install_apps_internal.h
@@ -0,0 +1,92 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#ifndef OMAHA_CLIENT_INSTALL_APPS_INTERNAL_H_
+#define OMAHA_CLIENT_INSTALL_APPS_INTERNAL_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include <vector>
+#include "omaha/base/browser_utils.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/ui/progress_wnd.h"
+
+namespace omaha {
+
+class AppBundle;
+class BundleInstaller;
+struct CommandLineExtraArgs;
+
+namespace internal {
+
+// TODO(omaha): Figure out how to handle pause requests.
+class InstallAppsWndEvents : public ProgressWndEvents {
+ public:
+  InstallAppsWndEvents(bool is_machine,
+                       BundleInstaller* installer,
+                       BrowserType browser_type);
+
+  virtual void DoClose();
+  virtual void DoExit();
+  virtual void DoCancel();
+  virtual bool DoLaunchBrowser(const CString& url);
+
+  // When a valid browser type is specified in the command line, that type of
+  // browser will be restarted.
+  virtual bool DoRestartBrowser(bool restart_all_browsers,
+                                const std::vector<CString>& urls);
+  virtual bool DoReboot();
+
+ private:
+  bool is_machine_;
+  BundleInstaller* installer_;
+  BrowserType browser_type_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(InstallAppsWndEvents);
+};
+
+// On success, the caller takes ownership of observer and ui_sink.
+HRESULT CreateClientUI(bool is_machine,
+                       BrowserType browser_type,
+                       BundleInstaller* installer,
+                       AppBundle* app_bundle,
+                       InstallProgressObserver** observer,
+                       OmahaWndEvents** ui_sink);
+
+// Does the work for InstallApps, allowing the COM server to be mocked.
+HRESULT DoInstallApps(BundleInstaller* installer,
+                      IAppBundle* app_bundle,
+                      bool is_machine,
+                      bool is_interactive,
+                      bool is_update_all_apps,
+                      BrowserType browser_type,
+                      bool* has_ui_been_displayed);
+
+// Displays an error message and reports the error as appropriate.
+void HandleInstallAppsError(HRESULT error,
+                            int extra_code1,
+                            bool is_machine,
+                            bool is_interactive,
+                            bool is_eula_required,
+                            bool is_oem_install,
+                            const CommandLineExtraArgs& extra_args,
+                            bool* has_ui_been_displayed);
+
+}  // namespace internal
+
+}  // namespace omaha
+
+#endif  // OMAHA_CLIENT_INSTALL_APPS_INTERNAL_H_
diff --git a/client/install_apps_unittest.cc b/client/install_apps_unittest.cc
new file mode 100644
index 0000000..1367346
--- /dev/null
+++ b/client/install_apps_unittest.cc
@@ -0,0 +1,23 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include "omaha/client/install_apps.h"
+#include "omaha/client/install_apps_internal.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+}  // namespace omaha
diff --git a/client/install_internal.h b/client/install_internal.h
new file mode 100644
index 0000000..33a2211
--- /dev/null
+++ b/client/install_internal.h
@@ -0,0 +1,117 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_CLIENT_INSTALL_INTERNAL_H_
+#define OMAHA_CLIENT_INSTALL_INTERNAL_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "omaha/common/command_line.h"
+
+namespace omaha {
+
+class SplashScreen;
+
+namespace internal {
+
+// Elevates TBD and waits for it to exit.
+HRESULT DoElevation(bool is_interactive,
+                    bool is_install_elevated_instance,
+                    const CString& cmd_line,
+                    DWORD* exit_code);
+
+// Installs Omaha if necessary and app(s) if is_app_install.
+HRESULT DoInstall(bool is_machine,
+                  bool is_app_install,
+                  bool is_eula_required,
+                  bool is_oem_install,
+                  const CString& current_version,
+                  const CommandLineArgs& args,
+                  const CString& session_id,
+                  SplashScreen* splash_screen,
+                  int* extra_code1,
+                  bool* has_setup_succeeded,
+                  bool* has_launched_handoff,
+                  bool* has_ui_been_displayed);
+
+// Installs the specified applications.
+HRESULT InstallApplications(bool is_machine,
+                            bool is_eula_required,
+                            const CommandLineArgs& args,
+                            const CString& session_id,
+                            SplashScreen* splash_screen,
+                            bool* has_ui_been_displayed,
+                            bool* has_launched_handoff);
+
+// Starts Omaha elevated if possible and waits for it to exit.
+// The same arguments are passed to the elevated instance.
+HRESULT ElevateAndWait(const CString& cmd_line, DWORD* exit_code);
+
+// CopyOfflineManifest() and CopyOfflineFilesForApp() find and copy the offline
+// manifest and offline files respectively, from the current module directory to
+// the offline_dir. offline_dir is typically an unique directory under the
+// Google\Update\Offline\ directory.
+// The offline manifest is copied to offline_dir\<kOfflineManifestFileName>.
+// The binaries are in the format "file.<app_id>". Each file is copied to the
+// offline_dir under the subdirectory "<app_id>", as "file". For instance,
+// "Installer.msi.<app_id>" is copied as "<app_id>/Installer.msi".
+HRESULT CopyOfflineManifest(const CString& offline_dir);
+HRESULT CopyOfflineFilesForApp(const CString& app_id,
+                               const CString& offline_dir);
+
+// For all the applications that have been requested in the apps parameter, copy
+// the offline binaries.
+bool CopyOfflineFiles(bool is_machine,
+                      const std::vector<CommandLineAppArgs>& apps,
+                      CString* offline_dir);
+
+// Launches a /handoff process from the installed location to install the app.
+HRESULT LaunchHandoffProcess(bool is_machine,
+                             const CString& offline_dir,
+                             const CommandLineArgs& install_args,
+                             const CString& session_id,
+                             HANDLE* process);
+
+// Waits for the process to exit and returns the exit code.
+HRESULT WaitForProcessExit(HANDLE process,
+                           SplashScreen* splash_screen,
+                           bool* has_ui_been_displayed,
+                           uint32* exit_code);
+
+// Displays an error message and reports the error as appropriate.
+void HandleInstallError(HRESULT error,
+                        int extra_code1,
+                        const CString& session_id,
+                        bool is_machine,
+                        bool is_interactive,
+                        bool is_eula_required,
+                        bool is_oem_install,
+                        const CString& current_version,
+                        const CString& install_source,
+                        const CommandLineExtraArgs& extra_args,
+                        bool has_setup_succeeded,
+                        bool has_launched_handoff,
+                        bool* has_ui_been_displayed);
+
+// Returns the error text for the corresponding error value.
+CString GetErrorText(HRESULT error, const CString& bundle_name);
+
+}  // namespace internal
+
+}  // namespace omaha
+
+#endif  // OMAHA_CLIENT_INSTALL_INTERNAL_H_
diff --git a/client/install_progress_observer.h b/client/install_progress_observer.h
new file mode 100644
index 0000000..a3b2c1a
--- /dev/null
+++ b/client/install_progress_observer.h
@@ -0,0 +1,152 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_CLIENT_INSTALL_PROGRESS_OBSERVER_H_
+#define OMAHA_CLIENT_INSTALL_PROGRESS_OBSERVER_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include <vector>
+
+#include "omaha/base/safe_format.h"
+#include "omaha/base/time.h"
+
+namespace omaha {
+
+// TODO(omaha3): These specify the completion UI to display not the job
+// result as they did in Omaha 2.
+// Some of them may not be necessary depending on how information is
+// communicated to the observer. For example, should the COM API allow reboot/
+// restart browser AND launchcmd?
+// If we keep this enum, rename to something like CompletionTypes to make it
+// clear that it is describing the desired behavior of the observer. This also
+// differentiates the name from LegacyCompetionCodes.
+// TODO(omaha): If the codes below change, need a conversion method to convert
+// to LegacyCompletionCodes.
+typedef enum {
+  COMPLETION_CODE_SUCCESS = 1,
+  COMPLETION_CODE_EXIT_SILENTLY,
+  COMPLETION_CODE_ERROR = COMPLETION_CODE_SUCCESS + 2,
+  COMPLETION_CODE_RESTART_ALL_BROWSERS,
+  COMPLETION_CODE_REBOOT,
+  COMPLETION_CODE_RESTART_BROWSER,
+  COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY,
+  COMPLETION_CODE_REBOOT_NOTICE_ONLY,
+  COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY,
+  COMPLETION_CODE_LAUNCH_COMMAND,
+  COMPLETION_CODE_EXIT_SILENTLY_ON_LAUNCH_COMMAND,
+  COMPLETION_CODE_INSTALL_FINISHED_BEFORE_CANCEL,
+} CompletionCodes;
+
+inline bool IsCompletionCodeSuccess(CompletionCodes completion_code) {
+  switch (completion_code) {
+    case COMPLETION_CODE_SUCCESS:
+    case COMPLETION_CODE_EXIT_SILENTLY:
+    case COMPLETION_CODE_RESTART_ALL_BROWSERS:
+    case COMPLETION_CODE_REBOOT:
+    case COMPLETION_CODE_RESTART_BROWSER:
+    case COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY:
+    case COMPLETION_CODE_REBOOT_NOTICE_ONLY:
+    case COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY:
+    case COMPLETION_CODE_LAUNCH_COMMAND:
+    case COMPLETION_CODE_EXIT_SILENTLY_ON_LAUNCH_COMMAND:
+    case COMPLETION_CODE_INSTALL_FINISHED_BEFORE_CANCEL:
+      return true;
+
+    case COMPLETION_CODE_ERROR:
+    default:
+      return false;
+  }
+}
+
+struct AppCompletionInfo {
+  CString display_name;
+  CString app_id;
+  CString completion_message;
+  CompletionCodes completion_code;
+  HRESULT error_code;
+  int extra_code1;
+  uint32 installer_result_code;
+  bool is_canceled;
+  bool is_noupdate;  // noupdate response from server
+  CString post_install_launch_command_line;
+  CString post_install_url;
+
+  AppCompletionInfo() : completion_code(COMPLETION_CODE_SUCCESS),
+                        error_code(S_OK),
+                        extra_code1(0),
+                        installer_result_code(0),
+                        is_canceled(false),
+                        is_noupdate(false) {}
+
+#ifdef DEBUG
+  CString ToString() const {
+    CString result;
+    SafeCStringFormat(&result,
+        _T("[AppCompletionInfo][%s][%s][0x%x][%d][%d][%d][%d][cmd_line=%s]"),
+        app_id, completion_message, error_code, extra_code1,
+        installer_result_code, is_canceled, is_noupdate,
+        post_install_launch_command_line);
+    return result;
+  }
+#endif
+};
+
+struct ObserverCompletionInfo {
+  CompletionCodes completion_code;
+  CString completion_text;
+  CString help_url;
+  std::vector<AppCompletionInfo> apps_info;
+
+  explicit ObserverCompletionInfo(CompletionCodes code)
+      : completion_code(code) {}
+
+#ifdef DEBUG
+  CString ToString() const {
+    CString result;
+    SafeCStringFormat(&result, _T("[ObserverCompletionInfo][code=%d][text=%s]"),
+                      completion_code, completion_text);
+    for (size_t i = 0; i < apps_info.size(); ++i) {
+      result.AppendFormat(_T("[%s]"), apps_info[i].ToString());
+    }
+    return result;
+  }
+#endif
+};
+
+// TODO(omaha3): This is bundle-centric. Add support for individual app-updates
+// when we have a better idea of the new bundle-supporting UI.
+class InstallProgressObserver {
+ public:
+  virtual ~InstallProgressObserver() {}
+  virtual void OnCheckingForUpdate() = 0;
+  virtual void OnUpdateAvailable(const CString& app_name,
+                                 const CString& version_string) = 0;
+  virtual void OnWaitingToDownload(const CString& app_name) = 0;
+  virtual void OnDownloading(const CString& app_name,
+                             int time_remaining_ms,
+                             int pos) = 0;
+  virtual void OnWaitingRetryDownload(const CString& app_name,
+                                      time64 next_retry_time) = 0;
+  virtual void OnWaitingToInstall(const CString& app_name,
+                                  bool* can_start_install) = 0;
+  virtual void OnInstalling(const CString& app_name) = 0;
+  virtual void OnPause() = 0;
+  virtual void OnComplete(const ObserverCompletionInfo& observer_info) = 0;
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_CLIENT_INSTALL_PROGRESS_OBSERVER_H_
diff --git a/client/install_self.cc b/client/install_self.cc
new file mode 100644
index 0000000..601055b
--- /dev/null
+++ b/client/install_self.cc
@@ -0,0 +1,411 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/client/install_self.h"
+#include "omaha/client/install_self_internal.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/base/xml_utils.h"
+#include "omaha/client/client_utils.h"
+#include "omaha/common/app_registry_utils.h"
+#include "omaha/common/command_line.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/ping.h"
+#include "omaha/setup/setup.h"
+#include "omaha/setup/setup_metrics.h"
+
+namespace omaha {
+
+namespace install_self {
+
+namespace {
+
+// Returns whether elevation is required.
+bool IsElevationRequired(bool is_machine) {
+  return is_machine && !vista_util::IsUserAdmin();
+}
+
+}  // namespace
+
+namespace internal {
+
+HRESULT DoSelfUpdate(bool is_machine, int* extra_code1) {
+  ASSERT1(extra_code1);
+
+  *extra_code1 = 0;
+
+  HRESULT hr = DoInstallSelf(is_machine, true, false, false, extra_code1);
+  if (FAILED(hr)) {
+    PersistUpdateErrorInfo(is_machine, hr, *extra_code1, GetVersionString());
+    return hr;
+  }
+
+  return S_OK;
+}
+
+// Does not need to update the UI during Omaha install. This should be quick
+// with a simple throbbing UI. UI will transition when product install begins.
+HRESULT DoInstallSelf(bool is_machine,
+                      bool is_self_update,
+                      bool is_eula_required,
+                      bool set_keepalive,
+                      int* extra_code1) {
+  ASSERT1(extra_code1);
+  ASSERT1(!is_self_update || !is_eula_required);
+  ASSERT1(!IsElevationRequired(is_machine));
+
+  *extra_code1 = 0;
+
+  // TODO(omaha3): This needs to be in some type of lock(s) when
+  // !is_eula_required. See comments for SetEulaNotAccepted.
+  // TODO(omaha3): Integrate CL 11232530 - fix for bug 1866730 - from mainline.
+  // The Omaha 2 implementation of EULA [not] accepted was completely changed in
+  // this CL, which has not been integrated yet.
+  // TODO(omaha3): Code running in install-related processes before this point
+  // need to check for /eularequired before sending pings OR we need to move
+  // this before any pings can be sent. Even the latter is insufficient for
+  // the non-elevated machine install instance on Vista.
+  HRESULT hr = SetEulaRequiredState(is_machine, is_eula_required);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Checking system requirements here keeps requirements checks for other
+  // modules out of Setup.
+  // It is possible that an older metainstaller would fail the install for
+  // system requirements that are not required for the installed version when
+  // doing a handoff install.
+  hr = CheckSystemRequirements();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  Setup setup(is_machine);
+  setup.set_is_self_update(is_self_update);
+  hr = setup.Install(set_keepalive);
+  *extra_code1 = setup.extra_code1();
+
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Setup::Install failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  // All Omaha installs are "offline" because there is no update check.
+  const CString omaha_client_state_key_path =
+      ConfigManager::Instance()->registry_client_state_goopdate(is_machine);
+  app_registry_utils::PersistSuccessfulInstall(omaha_client_state_key_path,
+                                               is_self_update,
+                                               !is_self_update);  // is_offline
+
+  CORE_LOG(L1, (_T("[Setup successfully completed]")));
+
+  return S_OK;
+}
+
+HRESULT CheckSystemRequirements() {
+  // Validate that key OS components are installed.
+  if (!HasXmlParser()) {
+    return GOOPDATE_E_RUNNING_INFERIOR_MSXML;
+  }
+
+  return S_OK;
+}
+
+bool HasXmlParser() {
+  CComPtr<IXMLDOMDocument> my_xmldoc;
+  HRESULT hr = CoCreateSafeDOMDocument(&my_xmldoc);
+  const bool ret = SUCCEEDED(hr);
+  CORE_LOG(L3, (_T("[HasXmlParser returned %d][0x%08x]"), ret, hr));
+  return ret;
+}
+
+// Failing to set the state fails installation because this would prevent
+// updates or allow updates that should not be allowed.
+HRESULT SetEulaRequiredState(bool is_machine, bool is_eula_required) {
+  ASSERT1(!IsElevationRequired(is_machine));
+
+  const bool eula_accepted = !is_eula_required;
+  HRESULT hr = eula_accepted ? SetEulaAccepted(is_machine) :
+                               SetEulaNotAccepted(is_machine);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[set EULA accepted state failed][accepted=%d][0x%08x]"),
+                   eula_accepted, hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+// Does not write the registry if Google Update is already installed as
+// determined by the presence of 2 or more registered apps. In those cases, we
+// assume the existing EULA state is correct and do not want to disable updates
+// for an existing installation.
+// Assumes it is called with appropriate synchronization protection such that it
+// can reliably check the number of registered clients.
+// TODO(omaha3): How do we assure the above assumption?
+HRESULT SetEulaNotAccepted(bool is_machine) {
+  CORE_LOG(L4, (_T("[SetEulaNotAccepted][%d]"), is_machine));
+
+  size_t num_clients(0);
+  if (SUCCEEDED(app_registry_utils::GetNumClients(is_machine, &num_clients)) &&
+      num_clients >= 2) {
+    CORE_LOG(L4, (_T(" [Apps registered. Not setting eulaaccepted=0.]")));
+    return S_OK;
+  }
+
+  const ConfigManager* cm = ConfigManager::Instance();
+  return RegKey::SetValue(cm->registry_update(is_machine),
+                          kRegValueOmahaEulaAccepted,
+                          static_cast<DWORD>(0));
+}
+
+HRESULT SetInstallationId(const CString& omaha_client_state_key_path,
+                          const GUID& iid) {
+  if (GUID_NULL != iid) {
+    return RegKey::SetValue(omaha_client_state_key_path,
+                            kRegValueInstallationId,
+                            GuidToString(iid));
+  }
+
+  return S_OK;
+}
+
+// Persist experiment labels that are specific to Google Update itself during
+// an initial install.  These are specified in a tag using "omahaexperiments";
+// once it's on the machine, Google Update's experiment labels will be read
+// and modified like any other app on the system.
+HRESULT SetExperimentLabels(const CString& omaha_client_state_key_path,
+                            const CString& experiment_labels) {
+  if (!experiment_labels.IsEmpty()) {
+    return RegKey::SetValue(omaha_client_state_key_path,
+                            kRegValueExperimentLabels,
+                            experiment_labels);
+  }
+
+  return S_OK;
+}
+
+void PersistUpdateErrorInfo(bool is_machine,
+                            HRESULT error,
+                            int extra_code1,
+                            const CString& version) {
+  const TCHAR* update_key_name =
+      ConfigManager::Instance()->registry_update(is_machine);
+  VERIFY1(SUCCEEDED(RegKey::SetValue(update_key_name,
+                                     kRegValueSelfUpdateErrorCode,
+                                     static_cast<DWORD>(error))));
+  VERIFY1(SUCCEEDED(RegKey::SetValue(update_key_name,
+                                     kRegValueSelfUpdateExtraCode1,
+                                     static_cast<DWORD>(extra_code1))));
+  VERIFY1(SUCCEEDED(RegKey::SetValue(update_key_name,
+                                     kRegValueSelfUpdateVersion,
+                                     version)));
+}
+
+}  // namespace internal
+
+// Returns false if the values cannot be deleted to avoid skewing the log data
+// with a single user pinging repeatedly with the same data.
+bool ReadAndClearUpdateErrorInfo(bool is_machine,
+                                 DWORD* error_code,
+                                 DWORD* extra_code1,
+                                 CString* version) {
+  ASSERT1(error_code);
+  ASSERT1(extra_code1);
+  ASSERT1(version);
+
+  const TCHAR* update_key_name =
+      ConfigManager::Instance()->registry_update(is_machine);
+  RegKey update_key;
+  HRESULT hr = update_key.Open(update_key_name);
+  if (FAILED(hr)) {
+    ASSERT1(false);
+    return false;
+  }
+
+  if (!update_key.HasValue(kRegValueSelfUpdateErrorCode)) {
+    ASSERT1(!update_key.HasValue(kRegValueSelfUpdateExtraCode1));
+    return false;
+  }
+
+  VERIFY1(SUCCEEDED(update_key.GetValue(kRegValueSelfUpdateErrorCode,
+                                        error_code)));
+  ASSERT1(FAILED(*error_code));
+
+  VERIFY1(SUCCEEDED(update_key.GetValue(kRegValueSelfUpdateExtraCode1,
+                                        extra_code1)));
+
+  VERIFY1(SUCCEEDED(update_key.GetValue(kRegValueSelfUpdateVersion, version)));
+
+  if (FAILED(update_key.DeleteValue(kRegValueSelfUpdateErrorCode)) ||
+      FAILED(update_key.DeleteValue(kRegValueSelfUpdateExtraCode1)) ||
+      FAILED(update_key.DeleteValue(kRegValueSelfUpdateVersion))) {
+    ASSERT1(false);
+    return false;
+  }
+
+  return true;
+}
+
+HRESULT SetEulaAccepted(bool is_machine) {
+  CORE_LOG(L4, (_T("[SetEulaAccepted][%d]"), is_machine));
+  const TCHAR* update_key_name =
+      ConfigManager::Instance()->registry_update(is_machine);
+  return RegKey::HasKey(update_key_name) ?
+      RegKey::DeleteValue(update_key_name, kRegValueOmahaEulaAccepted) :
+      S_OK;
+}
+
+HRESULT InstallSelf(bool is_machine,
+                    bool is_eula_required,
+                    bool is_oem_install,
+                    const CString& current_version,
+                    const CString& install_source,
+                    const CommandLineExtraArgs& extra_args,
+                    const CString& session_id,
+                    int* extra_code1) {
+  CORE_LOG(L2, (_T("[InstallSelf]")));
+
+  HRESULT hr = internal::DoInstallSelf(is_machine,
+                                       false,
+                                       is_eula_required,
+                                       extra_args.runtime_only,
+                                       extra_code1);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[DoInstallSelf failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  // Set Omaha's optional IID, experiment labels, and branding.
+  // IID and experiment labels are always written on install; branding will
+  // only be written if no branding exists, which should only be true on the
+  // first install.
+  const CString omaha_client_state_key_path =
+      ConfigManager::Instance()->registry_client_state_goopdate(is_machine);
+
+  // TODO(omaha): move SetInstallationId to app_registry_utils
+  VERIFY1(SUCCEEDED(internal::SetInstallationId(omaha_client_state_key_path,
+                                                extra_args.installation_id)));
+  VERIFY1(SUCCEEDED(internal::SetExperimentLabels(
+      omaha_client_state_key_path,
+      extra_args.experiment_labels)));
+  VERIFY1(SUCCEEDED(app_registry_utils::SetGoogleUpdateBranding(
+      omaha_client_state_key_path,
+      extra_args.brand_code,
+      extra_args.client_id)));
+
+  if (is_eula_required || is_oem_install) {
+    return S_OK;
+  }
+
+  // Send a successful EVENT_INSTALL_COMPLETE ping and do not wait for the
+  // completion of the ping. This reduces the overall latency of Omaha
+  // installs. The 'curent_version' parameter represent the version of
+  // Omaha before the setup has run.
+  Ping install_ping(is_machine, session_id, install_source);
+  PingEventPtr setup_install_complete_ping_event(
+      new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE,
+                    PingEvent::EVENT_RESULT_SUCCESS,
+                    hr,
+                    *extra_code1));
+  const CString next_version(GetVersionString());
+  install_ping.LoadAppDataFromExtraArgs(extra_args);
+  install_ping.BuildOmahaPing(current_version,
+                              next_version,
+                              setup_install_complete_ping_event);
+  HRESULT send_result = install_ping.Send(true);
+  if (FAILED(send_result)) {
+    CORE_LOG(LW, (_T("[InstallPing::Send failed][0x%x]"), send_result));
+  }
+
+  return S_OK;
+}
+
+HRESULT UpdateSelf(bool is_machine, const CString& session_id) {
+  CORE_LOG(L2, (_T("[UpdateSelf]")));
+
+  ++metric_setup_update_self_total;
+
+  // 'current_version' corresponds to the value of 'pv' read from the registry.
+  CString current_version;
+  app_registry_utils::GetAppVersion(is_machine,
+                                    kGoogleUpdateAppId,
+                                    &current_version);
+
+  int extra_code1 = 0;
+  const HRESULT hr = internal::DoSelfUpdate(is_machine, &extra_code1);
+  if (SUCCEEDED(hr)) {
+    ++metric_setup_update_self_succeeded;
+  } else {
+    CORE_LOG(LE, (_T("[DoSelfUpdate failed][0x%08x]"), hr));
+  }
+
+  // If a self-update failed because an uninstall of that Omaha is in progress,
+  // don't bother with an update failure ping; the uninstall ping will suffice.
+  if (hr == GOOPDATE_E_FAILED_TO_GET_LOCK_UNINSTALL_PROCESS_RUNNING) {
+    return hr;
+  }
+
+  if (!ConfigManager::Instance()->CanUseNetwork(is_machine)) {
+    return hr;
+  }
+
+  // Send an update complete ping and wait for send to complete.
+  PingEvent::Results result = SUCCEEDED(hr) ?
+                              PingEvent::EVENT_RESULT_SUCCESS :
+                              PingEvent::EVENT_RESULT_ERROR;
+
+  const CString next_version(GetVersionString());
+
+  PingEventPtr update_complete_ping_event(
+      new PingEvent(PingEvent::EVENT_UPDATE_COMPLETE, result, hr, extra_code1));
+
+  Ping ping(is_machine, session_id, kCmdLineInstallSource_SelfUpdate);
+  ping.LoadOmahaDataFromRegistry();
+  ping.BuildOmahaPing(current_version,
+                      next_version,
+                      update_complete_ping_event);
+  ping.Send(false);
+
+  return hr;
+}
+
+HRESULT Repair(bool is_machine) {
+  CORE_LOG(L2, (_T("[Repair]")));
+  int extra_code1 = 0;
+  return internal::DoSelfUpdate(is_machine, &extra_code1);
+}
+
+void CheckInstallStateConsistency(bool is_machine) {
+  Setup::CheckInstallStateConsistency(is_machine);
+}
+
+HRESULT UninstallSelf(bool is_machine, bool send_uninstall_ping) {
+  CORE_LOG(L2, (_T("[UninstallSelf]")));
+  Setup setup(is_machine);
+  return setup.Uninstall(send_uninstall_ping);
+}
+
+}  // namespace install_self
+
+}  // namespace omaha
diff --git a/client/install_self.h b/client/install_self.h
new file mode 100644
index 0000000..9930413
--- /dev/null
+++ b/client/install_self.h
@@ -0,0 +1,68 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Functions related to the installing Omaha.
+
+#ifndef OMAHA_CLIENT_INSTALL_SELF_H_
+#define OMAHA_CLIENT_INSTALL_SELF_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include "omaha/common/ping_event.h"
+
+namespace omaha {
+
+struct CommandLineExtraArgs;
+
+namespace install_self {
+
+// Marks Omaha EULA as accepted by deleting the registry value.
+// Does not touch apps' EULA state.
+HRESULT SetEulaAccepted(bool is_machine);
+
+// Installs Omaha in the /install case. Does not install any applications.
+HRESULT InstallSelf(bool is_machine,
+                    bool is_eula_required,
+                    bool is_oem_install,
+                    const CString& current_version,
+                    const CString& install_source,
+                    const CommandLineExtraArgs& extra_args,
+                    const CString& session_id,
+                    int* extra_code1);
+
+// Updates Omaha.
+HRESULT UpdateSelf(bool is_machine, const CString& session_id);
+
+// Repairs Omaha. Used for Code Red recovery.
+HRESULT Repair(bool is_machine);
+
+// Verifies that Omaha is either properly installed or uninstalled completely.
+void CheckInstallStateConsistency(bool is_machine);
+
+// Uninstalls all Omaha versions if Omaha can be uninstalled.
+HRESULT UninstallSelf(bool is_machine, bool send_uninstall_ping);
+
+// Reads the error info for silent updates from the registry if present and
+// deletes it. Returns true if the data is valid.
+bool ReadAndClearUpdateErrorInfo(bool is_machine,
+                                 DWORD* error_code,
+                                 DWORD* extra_code1,
+                                 CString* version);
+
+}  // namespace install_self
+
+}  // namespace omaha
+
+#endif  // OMAHA_CLIENT_INSTALL_SELF_H_
diff --git a/client/install_self_internal.h b/client/install_self_internal.h
new file mode 100644
index 0000000..abee77f
--- /dev/null
+++ b/client/install_self_internal.h
@@ -0,0 +1,71 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_CLIENT_INSTALL_SELF_INTERNAL_H_
+#define OMAHA_CLIENT_INSTALL_SELF_INTERNAL_H_
+
+#include <windows.h>
+#include <atlstr.h>
+
+namespace omaha {
+
+namespace install_self {
+
+namespace internal {
+
+// Performs a self-update.
+HRESULT DoSelfUpdate(bool is_machine, int* extra_code1);
+
+// Does the actual work of installing Omaha for all cases.
+HRESULT DoInstallSelf(bool is_machine,
+                      bool is_self_update,
+                      bool is_eula_required,
+                      bool set_keepalive,
+                      int* extra_code1);
+
+// Checks that the Omaha system requirements are met. Returns an error if not.
+HRESULT CheckSystemRequirements();
+
+// Returns true if it can instantiate MSXML parser.
+bool HasXmlParser();
+
+// Sets or clears the flag that prevents Google Update from using the network
+// until the EULA has been accepted based on whether the eularequired flag
+// appears on the command line.
+HRESULT SetEulaRequiredState(bool is_machine, bool is_eula_required);
+
+// Marks Google Update EULA as not accepted if it is not already installed.
+// Does not touch apps' EULA state.
+HRESULT SetEulaNotAccepted(bool is_machine);
+
+// Sets Omaha's IID in registry if one is specified, replacing existing IID.
+HRESULT SetInstallationId(const CString& omaha_client_state_key_path,
+                          const GUID& iid);
+
+// TODO(omaha3): Maybe find a different home for these two methods.
+// Writes error info for silent updates to the registry so the installed Omaha
+// can send an update failed ping.
+void PersistUpdateErrorInfo(bool is_machine,
+                            HRESULT error,
+                            int extra_code1,
+                            const CString& version);
+
+}  // namespace internal
+
+}  // namespace install_self
+
+}  // namespace omaha
+
+#endif  // OMAHA_CLIENT_INSTALL_SELF_INTERNAL_H_
diff --git a/client/install_self_unittest.cc b/client/install_self_unittest.cc
new file mode 100644
index 0000000..43608dc
--- /dev/null
+++ b/client/install_self_unittest.cc
@@ -0,0 +1,878 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/constants.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/client/install_self.h"
+#include "omaha/client/install_self_internal.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace install_self {
+
+namespace {
+
+const TCHAR* const kAppMachineClientStatePath =
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\{50DA5C89-FF97-4536-BF3F-DF54C2F02EA8}\\");
+
+const TCHAR* const kAppUserClientStatePath =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\{50DA5C89-FF97-4536-BF3F-DF54C2F02EA8}\\");
+
+}  // namespace
+
+class RegistryProtectedInstallSelfTest
+    : public RegistryProtectedTest {
+};
+
+TEST(InstallTest, CheckSystemRequirements) {
+  EXPECT_SUCCEEDED(internal::CheckSystemRequirements());
+}
+
+TEST(InstallTest, HasXmlParser_True) {
+  EXPECT_TRUE(internal::HasXmlParser());
+}
+
+// A few tests for the public method. The bulk of the cases are covered by
+// SetEulaRequiredState tests.
+TEST_F(RegistryProtectedInstallSelfTest,
+       SetEulaAccepted_Machine_KeyDoesNotExist) {
+  EXPECT_EQ(S_OK, SetEulaAccepted(true));
+  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(RegistryProtectedInstallSelfTest,
+       SetEulaAccepted_Machine_ValueDoesNotExist) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(MACHINE_REG_UPDATE));
+  EXPECT_EQ(S_FALSE, SetEulaAccepted(true));
+  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(RegistryProtectedInstallSelfTest, SetEulaAccepted_Machine_ExistsZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+
+  EXPECT_SUCCEEDED(SetEulaAccepted(true));
+  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
+
+  // ClientState for Google Update (never used) and other apps is not affected.
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
+                   _T("eulaaccepted"),
+                   &value));
+  EXPECT_EQ(0, value);
+  value = UINT_MAX;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                   _T("eulaaccepted"),
+                   &value));
+  EXPECT_EQ(0, value);
+}
+
+TEST_F(RegistryProtectedInstallSelfTest, SetEulaAccepted_User_KeyDoesNotExist) {
+  EXPECT_EQ(S_OK, SetEulaAccepted(false));
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(RegistryProtectedInstallSelfTest,
+       SetEulaAccepted_User_ValueDoesNotExist) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(USER_REG_UPDATE));
+  EXPECT_EQ(S_FALSE, SetEulaAccepted(false));
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(RegistryProtectedInstallSelfTest, SetEulaAccepted_User_ExistsZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+
+  EXPECT_SUCCEEDED(SetEulaAccepted(false));
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
+
+  // ClientState for Google Update (never used) and other apps is not affected.
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                   _T("eulaaccepted"),
+                   &value));
+  EXPECT_EQ(0, value);
+  value = UINT_MAX;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppUserClientStatePath,
+                   _T("eulaaccepted"),
+                   &value));
+  EXPECT_EQ(0, value);
+}
+
+// TODO(omaha3): Enable once the EULA support is finalized. The tests were
+// significantly changed in the mainline.
+#if 0
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_NotRequired_KeyDoesNotExist) {
+  SetModeInstall();
+  args_.is_eula_required_set = false;
+  EXPECT_EQ(S_OK, SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_NotRequired_ValueDoesNotExist) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(MACHINE_REG_UPDATE));
+  SetModeInstall();
+  args_.is_eula_required_set = false;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_NotRequired_ExistsZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+
+  SetModeInstall();
+  args_.is_eula_required_set = false;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
+
+  // ClientState for Google Update (never used) and other apps is not affected.
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
+                   _T("eulaaccepted"),
+                   &value));
+  EXPECT_EQ(0, value);
+  value = UINT_MAX;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                   _T("eulaaccepted"),
+                   &value));
+  EXPECT_EQ(0, value);
+}
+
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_NotRequired_ExistsOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  SetModeInstall();
+  args_.is_eula_required_set = false;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_NotRequired_ExistsOther) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(8000)));
+  SetModeInstall();
+  args_.is_eula_required_set = false;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_NotRequired_ExistsString) {
+  EXPECT_SUCCEEDED(
+      RegKey::SetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), _T("0")));
+  SetModeInstall();
+  args_.is_eula_required_set = false;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_NotRequired_ValueDoesNotExistAlreadyInstalled) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  SetModeInstall();
+  args_.is_eula_required_set = false;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_NotRequired_ExistsZeroAlreadyInstalled) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  SetModeInstall();
+  args_.is_eula_required_set = false;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_Required_DoesNotExist) {
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(0, value);
+}
+
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_Required_ExistsZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(0, value);
+
+  // ClientState for Google Update (never used) and other apps is not affected.
+  value = UINT_MAX;
+  EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
+                   _T("eulaaccepted"),
+                   &value));
+  EXPECT_EQ(0, value);
+  value = UINT_MAX;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                   _T("eulaaccepted"),
+                   &value));
+  EXPECT_EQ(0, value);
+}
+
+// The existing value is ignored if there are not two registered apps. This is
+// an artifact of the implementation and not a requirement.
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_Required_ExistsOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(0, value);
+}
+
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_Required_ExistsOther) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(8000)));
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(0, value);
+}
+
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_Required_ExistsString) {
+  EXPECT_SUCCEEDED(
+      RegKey::SetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), _T("0")));
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(0, value);
+}
+
+// One app is not sufficient for detecting that Google Update is already
+// installed.
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_Required_ValueDoesNotExistOneAppRegistered) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientsPath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(0, value);
+}
+
+// Even Google Update registered is not sufficient for detecting that Google
+// Update is already installed.
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_Required_ValueDoesNotExistGoogleUpdateRegistered) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(0, value);
+}
+
+// Important: The existing state is not changed because two apps are registered.
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_Required_ValueDoesNotExistTwoAppsRegistered) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientsPath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2MachineClientsPath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
+}
+
+// The existing state is not changed because Google Update is already
+// installed, but there is no way to differentiate this from writing 0.
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_Required_ExistsZeroTwoAppsRegistered) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientsPath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2MachineClientsPath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(0, value);
+}
+
+// The existing state is not changed because Google Update is already installed.
+TEST_F(SetupRegistryProtectedMachineTest,
+       SetEulaRequiredState_Required_ExistsOneTwoAppsRegistered) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientsPath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2MachineClientsPath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(1, value);
+}
+
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_NotRequired_KeyDoesNotExist) {
+  SetModeInstall();
+  args_.is_eula_required_set = false;
+  EXPECT_EQ(S_OK, SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_NotRequired_ValueDoesNotExist) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(USER_REG_UPDATE));
+  SetModeInstall();
+  args_.is_eula_required_set = false;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_NotRequired_ExistsZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+
+  SetModeInstall();
+  args_.is_eula_required_set = false;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
+
+  // ClientState for Google Update (never used) and other apps is not affected.
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                   _T("eulaaccepted"),
+                   &value));
+  EXPECT_EQ(0, value);
+  value = UINT_MAX;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppUserClientStatePath,
+                   _T("eulaaccepted"),
+                   &value));
+  EXPECT_EQ(0, value);
+}
+
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_NotRequired_ExistsOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  SetModeInstall();
+  args_.is_eula_required_set = false;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_NotRequired_ExistsOther) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(8000)));
+  SetModeInstall();
+  args_.is_eula_required_set = false;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_NotRequired_ExistsString) {
+  EXPECT_SUCCEEDED(
+      RegKey::SetValue(USER_REG_UPDATE, _T("eulaaccepted"), _T("0")));
+  SetModeInstall();
+  args_.is_eula_required_set = false;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_NotRequired_ValueDoesNotExistAlreadyInstalled) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  SetModeInstall();
+  args_.is_eula_required_set = false;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_NotRequired_ExistsZeroAlreadyInstalled) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  SetModeInstall();
+  args_.is_eula_required_set = false;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
+}
+
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_Required_DoesNotExist) {
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(0, value);
+}
+
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_Required_ExistsZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(0, value);
+
+  // ClientState for Google Update (never used) and other apps is not affected.
+  value = UINT_MAX;
+  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                   _T("eulaaccepted"),
+                   &value));
+  EXPECT_EQ(0, value);
+  value = UINT_MAX;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppUserClientStatePath,
+                   _T("eulaaccepted"),
+                   &value));
+  EXPECT_EQ(0, value);
+}
+
+// The existing value is ignored if there are not two registered apps. This is
+// an artifact of the implementation and not a requirement.
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_Required_ExistsOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(0, value);
+}
+
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_Required_ExistsOther) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(8000)));
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(0, value);
+}
+
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_Required_ExistsString) {
+  EXPECT_SUCCEEDED(
+      RegKey::SetValue(USER_REG_UPDATE, _T("eulaaccepted"), _T("0")));
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(0, value);
+}
+
+// One app is not sufficient for detecting that Google Update is already
+// installed.
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_Required_ValueDoesNotExistOneAppRegistered) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientsPath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(0, value);
+}
+
+// Even Google Update registered is not sufficient for detecting that Google
+// Update is already installed.
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_Required_ValueDoesNotExistGoogleUpdateRegistered) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(0, value);
+}
+
+// Important: The existing state is not changed because two apps are registered.
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_Required_ValueDoesNotExistTwoAppsRegistered) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientsPath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2UserClientsPath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
+}
+
+// The existing state is not changed because Google Update is already
+// installed, but there is no way to differentiate this from writing 0.
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_Required_ExistsZeroTwoAppsRegistered) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientsPath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2UserClientsPath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(0, value);
+}
+
+// The existing state is not changed because Google Update is already installed.
+TEST_F(SetupRegistryProtectedUserTest,
+       SetEulaRequiredState_Required_ExistsOneTwoAppsRegistered) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientsPath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2UserClientsPath,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  SetModeInstall();
+  args_.is_eula_required_set = true;
+  EXPECT_SUCCEEDED(SetEulaRequiredState());
+  DWORD value = UINT_MAX;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
+  EXPECT_EQ(1, value);
+}
+#endif
+
+TEST_F(RegistryProtectedInstallSelfTest, PersistUpdateErrorInfo_User) {
+  internal::PersistUpdateErrorInfo(false, 0x98765432, 77, _T("1.2.3.4"));
+
+  DWORD value(0);
+  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_UPDATE,
+                                    kRegValueSelfUpdateErrorCode,
+                                    &value));
+  EXPECT_EQ(0x98765432, value);
+
+  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_UPDATE,
+                                    kRegValueSelfUpdateExtraCode1,
+                                    &value));
+  EXPECT_EQ(77, value);
+
+  CString version;
+  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_UPDATE,
+                                    kRegValueSelfUpdateVersion,
+                                    &version));
+  EXPECT_FALSE(version.IsEmpty());
+  EXPECT_STREQ(_T("1.2.3.4"), version);
+}
+
+TEST_F(RegistryProtectedInstallSelfTest, PersistUpdateErrorInfo_Machine) {
+  internal::PersistUpdateErrorInfo(true, 0x98765430, 0x12345678, _T("2.3.4.5"));
+
+  DWORD value(0);
+  EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE,
+                                    kRegValueSelfUpdateErrorCode,
+                                    &value));
+  EXPECT_EQ(0x98765430, value);
+
+  EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE,
+                                    kRegValueSelfUpdateExtraCode1,
+                                    &value));
+  EXPECT_EQ(0x12345678, value);
+
+  CString version;
+  EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE,
+                                    kRegValueSelfUpdateVersion,
+                                    &version));
+  EXPECT_FALSE(version.IsEmpty());
+  EXPECT_STREQ(_T("2.3.4.5"), version);
+}
+
+TEST_F(RegistryProtectedInstallSelfTest,
+       ReadAndClearUpdateErrorInfo_User_KeyDoesNotExist) {
+  DWORD self_update_error_code(0);
+  DWORD self_update_extra_code1(0);
+  CString self_update_version;
+  ExpectAsserts expect_asserts;
+  EXPECT_FALSE(ReadAndClearUpdateErrorInfo(false,
+                                           &self_update_error_code,
+                                           &self_update_extra_code1,
+                                           &self_update_version));
+}
+
+TEST_F(RegistryProtectedInstallSelfTest,
+       ReadAndClearUpdateErrorInfo_Machine_KeyDoesNotExist) {
+  DWORD self_update_error_code(0);
+  DWORD self_update_extra_code1(0);
+  CString self_update_version;
+  ExpectAsserts expect_asserts;
+  EXPECT_FALSE(ReadAndClearUpdateErrorInfo(true,
+                                           &self_update_error_code,
+                                           &self_update_extra_code1,
+                                           &self_update_version));
+}
+
+TEST_F(RegistryProtectedInstallSelfTest,
+       ReadAndClearUpdateErrorInfo_User_UpdateErrorCodeDoesNotExist) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(USER_REG_UPDATE));
+  DWORD self_update_error_code(0);
+  DWORD self_update_extra_code1(0);
+  CString self_update_version;
+  EXPECT_FALSE(ReadAndClearUpdateErrorInfo(false,
+                                           &self_update_error_code,
+                                           &self_update_extra_code1,
+                                           &self_update_version));
+}
+
+TEST_F(RegistryProtectedInstallSelfTest,
+       ReadAndClearUpdateErrorInfo_Machine_UpdateErrorCodeDoesNotExist) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(MACHINE_REG_UPDATE));
+  DWORD self_update_error_code(0);
+  DWORD self_update_extra_code1(0);
+  CString self_update_version;
+  EXPECT_FALSE(ReadAndClearUpdateErrorInfo(true,
+                                           &self_update_error_code,
+                                           &self_update_extra_code1,
+                                           &self_update_version));
+}
+
+TEST_F(RegistryProtectedInstallSelfTest,
+       ReadAndClearUpdateErrorInfo_User_AllValuesPresent) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    kRegValueSelfUpdateErrorCode,
+                                    static_cast<DWORD>(0x87654321)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    kRegValueSelfUpdateExtraCode1,
+                                    static_cast<DWORD>(55)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    kRegValueSelfUpdateVersion,
+                                    _T("0.2.4.8")));
+
+  DWORD self_update_error_code(0);
+  DWORD self_update_extra_code1(0);
+  CString self_update_version;
+  EXPECT_TRUE(ReadAndClearUpdateErrorInfo(false,
+                                          &self_update_error_code,
+                                          &self_update_extra_code1,
+                                          &self_update_version));
+
+  EXPECT_EQ(0x87654321, self_update_error_code);
+  EXPECT_EQ(55, self_update_extra_code1);
+  EXPECT_STREQ(_T("0.2.4.8"), self_update_version);
+}
+
+TEST_F(RegistryProtectedInstallSelfTest,
+       ReadAndClearUpdateErrorInfo_Machine_AllValuesPresent) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    kRegValueSelfUpdateErrorCode,
+                                    static_cast<DWORD>(0x87654321)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    kRegValueSelfUpdateExtraCode1,
+                                    static_cast<DWORD>(55)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    kRegValueSelfUpdateVersion,
+                                    _T("0.2.4.8")));
+
+  DWORD self_update_error_code(0);
+  DWORD self_update_extra_code1(0);
+  CString self_update_version;
+  EXPECT_TRUE(ReadAndClearUpdateErrorInfo(true,
+                                          &self_update_error_code,
+                                          &self_update_extra_code1,
+                                          &self_update_version));
+
+  EXPECT_EQ(0x87654321, self_update_error_code);
+  EXPECT_EQ(55, self_update_extra_code1);
+  EXPECT_STREQ(_T("0.2.4.8"), self_update_version);
+}
+
+TEST_F(RegistryProtectedInstallSelfTest,
+       ReadAndClearUpdateErrorInfo_User_ValuesPresentInMachineOnly) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    kRegValueSelfUpdateErrorCode,
+                                    static_cast<DWORD>(0x87654321)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    kRegValueSelfUpdateExtraCode1,
+                                    static_cast<DWORD>(55)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    kRegValueSelfUpdateVersion,
+                                    _T("0.2.4.8")));
+
+  DWORD self_update_error_code(0);
+  DWORD self_update_extra_code1(0);
+  CString self_update_version;
+  ExpectAsserts expect_asserts;
+  EXPECT_FALSE(ReadAndClearUpdateErrorInfo(false,
+                                           &self_update_error_code,
+                                           &self_update_extra_code1,
+                                           &self_update_version));
+}
+
+}  // namespace install_self
+
+}  // namespace omaha
diff --git a/client/install_self_unittest_no_xml_parser.cc b/client/install_self_unittest_no_xml_parser.cc
new file mode 100644
index 0000000..a5cb98d
--- /dev/null
+++ b/client/install_self_unittest_no_xml_parser.cc
@@ -0,0 +1,100 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/constants.h"
+#include "omaha/base/error.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/client/install_self_internal.h"
+#include "omaha/testing/omaha_unittest.h"
+#include "omaha/testing/unit_test.h"
+
+// Overriding HKLM causes HasXmlParser() to fail on Vista.
+// These tests must be run independently because other tests may use the XML
+// parser, making it available despite the HKLM overriding.
+
+// This test links against code that requires the following symbols but the test
+// does not use them. See the TODO in the build.scons. Rather than link against
+// more libs, define these symbols here.
+//
+// From omaha3_idl.lib.
+EXTERN_C const GUID IID_IGoogleUpdate3 = GUID_NULL;
+EXTERN_C const GUID IID_IAppBundle = GUID_NULL;
+EXTERN_C const GUID IID_IApp = GUID_NULL;
+EXTERN_C const GUID IID_IAppVersion = GUID_NULL;
+EXTERN_C const GUID IID_IPackage = GUID_NULL;
+EXTERN_C const GUID IID_ICurrentState = GUID_NULL;
+EXTERN_C const GUID IID_IGoogleUpdate3Web = GUID_NULL;
+EXTERN_C const GUID IID_IGoogleUpdate3WebSecurity = GUID_NULL;
+EXTERN_C const GUID IID_IAppBundleWeb = GUID_NULL;
+EXTERN_C const GUID IID_IAppWeb = GUID_NULL;
+EXTERN_C const GUID IID_IAppVersionWeb = GUID_NULL;
+EXTERN_C const GUID CLSID_ProcessLauncherClass = GUID_NULL;
+EXTERN_C const GUID IID_IGoogleUpdate = GUID_NULL;
+EXTERN_C const GUID IID_IGoogleUpdateCore = GUID_NULL;
+EXTERN_C const GUID IID_IProgressWndEvents = GUID_NULL;
+EXTERN_C const GUID LIBID_GoogleUpdate3Lib = GUID_NULL;
+EXTERN_C const GUID IID_ICoCreateAsync = GUID_NULL;
+EXTERN_C const GUID IID_ICoCreateAsyncStatus = GUID_NULL;
+EXTERN_C const GUID IID_IOneClickProcessLauncher = GUID_NULL;
+EXTERN_C const GUID IID_ICredentialDialog = GUID_NULL;
+EXTERN_C const GUID IID_IProcessLauncher = GUID_NULL;
+// From bits.lib.
+EXTERN_C const GUID IID_IBackgroundCopyCallback = GUID_NULL;
+// From iphlpapi.lib.
+#include <iphlpapi.h>  // NOLINT
+DWORD WINAPI GetIfTable(PMIB_IFTABLE, PULONG, BOOL) { return 0; }
+
+namespace omaha {
+
+TEST_F(RegistryProtectedTest, InstallOmaha_XmlParserNotPresent) {
+  if (!vista_util::IsVistaOrLater()) {
+    std::wcout << _T("\tTest did not run because it requires Vista or later.")
+               << std::endl;
+    return;
+  }
+  int extra_code1 = 0;
+  EXPECT_EQ(GOOPDATE_E_RUNNING_INFERIOR_MSXML,
+            install_self::internal::DoInstallSelf(false,
+                                                  false,
+                                                  false,
+                                                  false,
+                                                  &extra_code1));
+  EXPECT_EQ(0, extra_code1);
+  EXPECT_FALSE(RegKey::HasKey(USER_REG_GOOGLE));
+}
+
+// Overriding HKLM causes HasXmlParser() to fail on Vista.
+TEST_F(RegistryProtectedTest, CheckSystemRequirements_XmlParserNotPresent) {
+  if (!vista_util::IsVistaOrLater()) {
+    std::wcout << _T("\tTest did not run because it requires Vista or later.")
+               << std::endl;
+    return;
+  }
+  EXPECT_EQ(GOOPDATE_E_RUNNING_INFERIOR_MSXML,
+            install_self::internal::CheckSystemRequirements());
+}
+
+// Overriding HKLM causes HasXmlParser() to fail on Vista.
+TEST_F(RegistryProtectedTest, HasXmlParser_NotPresent) {
+  if (!vista_util::IsVistaOrLater()) {
+    std::wcout << _T("\tTest did not run because it requires Vista or later.")
+               << std::endl;
+    return;
+  }
+  EXPECT_FALSE(install_self::internal::HasXmlParser());
+}
+
+}  // namespace omaha
diff --git a/client/install_unittest.cc b/client/install_unittest.cc
new file mode 100644
index 0000000..6a14daa
--- /dev/null
+++ b/client/install_unittest.cc
@@ -0,0 +1,785 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <mstask.h>
+#include "omaha/base/app_util.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/shell.h"
+#include "omaha/base/time.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/client/install.h"
+#include "omaha/client/install_internal.h"
+#include "omaha/client/install_self_internal.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/scheduled_task_utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+const TCHAR* const kExpectedIid = _T("{A972BB39-CCA3-4F25-9737-3308F5FA19B5}");
+const TCHAR* const kExpectedBrand = _T("GOOG");
+const TCHAR* const kExpectedClientId = _T("some_partner");
+
+const TCHAR* const kXpSystemSetupKey = _T("HKLM\\System\\Setup");
+const TCHAR* const kVistaSetupStateKey =
+    _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State");
+
+// Assumes the fixture is inherited from RegistryProtectedInstallTest,
+// RegistryProtectedWithComInterfacesPrimedInstallTest if is_machine.
+// If is_machine, the test must run
+// scheduled_task_utils::UninstallGoopdateTasks(true)) upon completion.
+// The method used to prevent installing Omaha still results in Setup attempting
+// to start the Core. When is_machine is true, this includes starting the
+// service or scheduled task. If neither are installed, this fails with an
+// assert. To avoid the assert, install the scheduled tasks. This is simpler
+// than installing the service and copying files such that it will succeed.
+// TODO(omaha3): Use a different method of avoiding running Setup; maybe a mock.
+void PreventSetupFromRunning(bool is_machine) {
+  const TCHAR* const kFutureVersionString = _T("10.9.8.7");
+  EXPECT_SUCCEEDED(RegKey::SetValue(is_machine ?
+                                        MACHINE_REG_CLIENTS_GOOPDATE :
+                                        USER_REG_CLIENTS_GOOPDATE,
+                                    kRegValueProductVersion,
+                                    kFutureVersionString));
+
+  if (!is_machine) {
+    return;
+  }
+  const CString task_path = ConcatenatePath(
+                                app_util::GetCurrentModuleDirectory(),
+                                _T("unittest_support\\SaveArguments.exe"));
+  EXPECT_SUCCEEDED(scheduled_task_utils::InstallGoopdateTasks(task_path, true));
+}
+
+}  // namespace
+
+class InstallHandoffTest : public testing::Test {
+ protected:
+  explicit InstallHandoffTest(bool is_machine)
+      : omaha_path_(is_machine ? GetGoogleUpdateMachinePath() :
+                                 GetGoogleUpdateUserPath()),
+        path_(ConcatenatePath(omaha_path_, kVersionString)) {
+  }
+
+  virtual void SetUp() {
+    // Save the existing version if present.
+    RegKey::GetValue(USER_REG_CLIENTS_GOOPDATE,
+                     kRegValueProductVersion,
+                     &existing_version_);
+    InstallFiles();
+  }
+
+  virtual void TearDown() {
+    EXPECT_SUCCEEDED(DeleteDirectory(path_));
+    if (existing_version_.IsEmpty()) {
+      EXPECT_SUCCEEDED(RegKey::DeleteValue(USER_REG_CLIENTS_GOOPDATE,
+                                           kRegValueProductVersion));
+    } else {
+      EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENTS_GOOPDATE,
+                                        kRegValueProductVersion,
+                                        existing_version_));
+    }
+  }
+
+  void InstallFiles() {
+    DeleteDirectory(path_);
+    EXPECT_FALSE(File::IsDirectory(path_));
+
+    EXPECT_SUCCEEDED(CreateDir(path_, NULL));
+
+    EXPECT_SUCCEEDED(File::Copy(
+        ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                        kOmahaShellFileName),
+        omaha_path_ + kOmahaShellFileName,
+        false));
+
+    EXPECT_SUCCEEDED(File::Copy(
+        ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                        kOmahaDllName),
+        ConcatenatePath(path_, kOmahaDllName),
+        false));
+
+    EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENTS_GOOPDATE,
+                                      kRegValueProductVersion,
+                                      kVersionString));
+  }
+
+  CString existing_version_;  // Saves the existing version from the registry.
+  const CString omaha_path_;
+  const CString path_;
+  static const TCHAR* const kVersionString;
+  static const TCHAR* const kAppGuid_;
+  static const TCHAR* const kSessionId_;
+};
+
+const TCHAR* const InstallHandoffTest::kVersionString = _T("9.8.7.6");
+const TCHAR* const InstallHandoffTest::kAppGuid_ =
+    _T("{01D33078-BA95-4da6-A3FC-F31593FD4AA2}");
+const TCHAR* const InstallHandoffTest::kSessionId_ =
+    _T("{6cb069db-b073-4a40-9983-846a3819876a}");
+
+class InstallHandoffUserTest : public InstallHandoffTest {
+ protected:
+  InstallHandoffUserTest()
+      : InstallHandoffTest(false) {
+  }
+};
+
+class RegistryProtectedInstallTest
+    : public RegistryProtectedTest {
+ protected:
+  virtual void SetUp() {
+    RegistryProtectedTest::SetUp();
+
+    args_.extra.bundle_name = _T("bundle");  // Avoids assert in error cases.
+    args_.install_source    = _T("unittest");
+  }
+
+  CommandLineArgs args_;
+};
+
+// Primes the Task Scheduler and XML interfaces before overriding the registry
+// so the interfaces are available after overriding HKLM. This is necessary to
+// allow tests to be run individually.
+class RegistryProtectedWithComInterfacesPrimedInstallTest
+    : public RegistryProtectedInstallTest {
+ protected:
+  virtual void SetUp() {
+    CComPtr<ITaskScheduler> scheduler;
+    EXPECT_SUCCEEDED(scheduler.CoCreateInstance(CLSID_CTaskScheduler,
+                                                NULL,
+                                                CLSCTX_INPROC_SERVER));
+
+    EXPECT_TRUE(install_self::internal::HasXmlParser());
+
+    // Prime the special folder mapping. For some reason this works on
+    // Windows XP but calling functions like
+    // ConfigManager::GetMachineGoopdateInstallDir does not.
+    typedef std::map<CString, CString> mapping;
+    mapping folder_map;
+    ASSERT_SUCCEEDED(Shell::GetSpecialFolderKeywordsMapping(&folder_map));
+
+    RegistryProtectedInstallTest::SetUp();
+
+    // mpr.dll requires that the HwOrder key is present to be able to
+    // initialize (http://support.microsoft.com/kb/329316). On Windows Vista and
+    // later, its absence causes IPersistFile.Save() in
+    // scheduled_task_utils::CreateScheduledTask() to fail with
+    // ERROR_DLL_INIT_FAILED.
+    EXPECT_SUCCEEDED(RegKey::CreateKey(
+        _T("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\")
+        _T("NetworkProvider\\HwOrder")));
+  }
+};
+
+class InAuditModeTest
+    : public RegistryProtectedWithComInterfacesPrimedInstallTest {
+ protected:
+  virtual void SetUp() {
+    RegistryProtectedWithComInterfacesPrimedInstallTest::SetUp();
+
+    if (vista_util::IsVistaOrLater()) {
+      EXPECT_SUCCEEDED(RegKey::SetValue(kVistaSetupStateKey,
+                                        _T("ImageState"),
+                                        _T("IMAGE_STATE_UNDEPLOYABLE")));
+    } else {
+      EXPECT_SUCCEEDED(RegKey::SetValue(kXpSystemSetupKey,
+                                        _T("AuditInProgress"),
+                                        static_cast<DWORD>(1)));
+    }
+
+    EXPECT_TRUE(ConfigManager::Instance()->IsWindowsInstalling());
+  }
+};
+
+TEST_F(InAuditModeTest, OemInstall_NotOffline) {
+  if (!vista_util::IsUserAdmin()) {
+    std::wcout << _T("\tTest did not run because the user is not an admin.")
+               << std::endl;
+    return;
+  }
+
+  PreventSetupFromRunning(true);
+
+  bool is_machine = true;
+  bool has_ui_been_displayed = false;
+  EXPECT_EQ(GOOPDATE_E_OEM_WITH_ONLINE_INSTALLER,
+            OemInstall(false,          // is_interactive
+                       true,           // is_app_install
+                       false,          // is_eula_required
+                       false,          // is_install_elevated_instance
+                       _T("unused"),   // install_cmd_line
+                       args_,
+                       &is_machine,
+                       &has_ui_been_displayed));
+  EXPECT_FALSE(has_ui_been_displayed);
+
+  EXPECT_SUCCEEDED(scheduled_task_utils::UninstallGoopdateTasks(true));
+}
+
+TEST_F(RegistryProtectedWithComInterfacesPrimedInstallTest,
+       Install_NotAppInstall_User_NoBrandSpecified_NoExistingBrand) {
+  if (vista_util::IsElevatedWithUACMaybeOn()) {
+    std::wcout << _T("\tSkipping test because user is elevated with UAC on.")
+               << std::endl;
+    return;
+  }
+
+  PreventSetupFromRunning(false);
+
+  bool is_machine = false;
+  bool has_ui_been_displayed = false;
+  EXPECT_EQ(S_OK, Install(false,         // is_interactive
+                          false,         // is_app_install
+                          false,         // is_eula_required
+                          false,         // is_oem_install
+                          false,         // is_install_elevated_instance
+                          _T("foo"),     // install_cmd_line
+                          args_,
+                          &is_machine,
+                          &has_ui_been_displayed));
+  EXPECT_FALSE(has_ui_been_displayed);
+
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                kRegValueInstallationId));
+  EXPECT_STREQ(_T("GGLS"), GetSzValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                      kRegValueBrandCode));
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                kRegValueClientId));
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                kRegValueReferralId));
+
+  const DWORD install_time = GetDwordValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                           kRegValueInstallTimeSec);
+  EXPECT_GE(now, install_time);
+  EXPECT_GE(static_cast<uint32>(200), now - install_time);
+}
+
+TEST_F(RegistryProtectedWithComInterfacesPrimedInstallTest,
+       Install_NotAppInstall_User_BrandSpecified_NoExistingBrand) {
+  if (vista_util::IsElevatedWithUACMaybeOn()) {
+    std::wcout << _T("\tSkipping test because user is elevated with UAC on.")
+               << std::endl;
+    return;
+  }
+
+  PreventSetupFromRunning(false);
+
+  args_.extra.installation_id = StringToGuid(kExpectedIid);
+  args_.extra.brand_code = kExpectedBrand;
+  args_.extra.client_id = kExpectedClientId;
+
+  bool is_machine = false;
+  bool has_ui_been_displayed = false;
+  EXPECT_EQ(S_OK, Install(false,         // is_interactive
+                          false,         // is_app_install
+                          false,         // is_eula_required
+                          false,         // is_oem_install
+                          false,         // is_install_elevated_instance
+                          _T("foo"),     // install_cmd_line
+                          args_,
+                          &is_machine,
+                          &has_ui_been_displayed));
+  EXPECT_FALSE(has_ui_been_displayed);
+
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  EXPECT_STREQ(kExpectedIid, GetSzValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                        kRegValueInstallationId));
+  EXPECT_STREQ(kExpectedBrand, GetSzValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                          kRegValueBrandCode));
+  EXPECT_STREQ(kExpectedClientId, GetSzValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                             kRegValueClientId));
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                kRegValueReferralId));
+
+  const DWORD install_time = GetDwordValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                           kRegValueInstallTimeSec);
+  EXPECT_GE(now, install_time);
+  EXPECT_GE(static_cast<uint32>(200), now - install_time);
+}
+
+TEST_F(RegistryProtectedWithComInterfacesPrimedInstallTest,
+       Install_NotAppInstall_User_BrandSpecified_ExistingBrandAndInstallTime) {
+  if (vista_util::IsElevatedWithUACMaybeOn()) {
+    std::wcout << _T("\tSkipping test because user is elevated with UAC on.")
+               << std::endl;
+    return;
+  }
+
+  PreventSetupFromRunning(false);
+
+  const TCHAR* const kExistingBrand = _T("GOOG");
+  const DWORD kExistingInstallTime = 1234567;
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                    kRegValueBrandCode,
+                                    kExistingBrand));
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                    kRegValueInstallTimeSec,
+                                    kExistingInstallTime));
+
+  args_.extra.installation_id = StringToGuid(kExpectedIid);
+  args_.extra.brand_code = kExpectedBrand;
+  args_.extra.client_id = kExpectedClientId;
+
+  bool is_machine = false;
+  bool has_ui_been_displayed = false;
+  EXPECT_EQ(S_OK, Install(false,         // is_interactive
+                          false,         // is_app_install
+                          false,         // is_eula_required
+                          false,         // is_oem_install
+                          false,         // is_install_elevated_instance
+                          _T("foo"),     // install_cmd_line
+                          args_,
+                          &is_machine,
+                          &has_ui_been_displayed));
+  EXPECT_FALSE(has_ui_been_displayed);
+
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  EXPECT_STREQ(kExpectedIid, GetSzValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                        kRegValueInstallationId));
+  EXPECT_STREQ(kExistingBrand, GetSzValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                          kRegValueBrandCode));
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                kRegValueClientId));
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                kRegValueReferralId));
+
+  EXPECT_EQ(kExistingInstallTime, GetDwordValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                                kRegValueInstallTimeSec));
+}
+
+TEST_F(RegistryProtectedWithComInterfacesPrimedInstallTest,
+       Install_NotAppInstall_Machine_BrandSpecified_NoExistingBrand) {
+  PreventSetupFromRunning(true);
+
+  args_.extra.installation_id = StringToGuid(kExpectedIid);
+  args_.extra.brand_code = kExpectedBrand;
+  args_.extra.client_id = kExpectedClientId;
+
+  bool is_machine = true;
+  bool has_ui_been_displayed = false;
+  EXPECT_SUCCEEDED(Install(false,         // is_interactive
+                           false,         // is_app_install
+                           false,         // is_eula_required
+                           false,         // is_oem_install
+                           false,         // is_install_elevated_instance
+                           _T("foo"),     // install_cmd_line
+                           args_,
+                           &is_machine,
+                           &has_ui_been_displayed));
+  EXPECT_FALSE(has_ui_been_displayed);
+
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  CString iid;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE, _T("iid"), &iid));
+  EXPECT_STREQ(kExpectedIid, iid);
+
+  CString brand;
+  EXPECT_SUCCEEDED(
+      RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE, _T("brand"), &brand));
+  EXPECT_STREQ(kExpectedBrand, brand);
+
+  CString client_id;
+  EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
+                                    _T("client"),
+                                    &client_id));
+  EXPECT_STREQ(kExpectedClientId, client_id);
+
+  EXPECT_FALSE(
+      RegKey::HasValue(MACHINE_REG_CLIENT_STATE_GOOPDATE, _T("referral")));
+
+  DWORD install_time(0);
+  EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
+                                    _T("InstallTime"),
+                                    &install_time));
+  EXPECT_GE(now, install_time);
+  EXPECT_GE(static_cast<uint32>(200), now - install_time);
+
+  EXPECT_SUCCEEDED(scheduled_task_utils::UninstallGoopdateTasks(true));
+}
+
+
+TEST_F(RegistryProtectedWithComInterfacesPrimedInstallTest,
+       Install_EulaRequiredNotOffline_User) {
+  if (vista_util::IsElevatedWithUACMaybeOn()) {
+    std::wcout << _T("\tSkipping test because user is elevated with UAC on.")
+               << std::endl;
+    return;
+  }
+
+  PreventSetupFromRunning(false);
+
+  bool is_machine = false;
+  bool has_ui_been_displayed = false;
+  EXPECT_EQ(GOOPDATE_E_EULA_REQURED_WITH_ONLINE_INSTALLER,
+            Install(false,          // is_interactive
+                    true,           // is_app_install
+                    true,           // is_eula_required
+                    false,          // is_oem_install
+                    false,          // is_install_elevated_instance
+                    _T("unused"),   // install_cmd_line
+                    args_,
+                    &is_machine,
+                    &has_ui_been_displayed));
+  EXPECT_FALSE(has_ui_been_displayed);
+}
+
+TEST_F(RegistryProtectedWithComInterfacesPrimedInstallTest,
+       Install_EulaRequiredNotOffline_Machine) {
+  if (!vista_util::IsUserAdmin()) {
+    std::wcout << _T("\tTest did not run because the user is not an admin.")
+               << std::endl;
+    return;
+  }
+
+  PreventSetupFromRunning(true);
+
+  bool is_machine = true;
+  bool has_ui_been_displayed = false;
+  EXPECT_EQ(GOOPDATE_E_EULA_REQURED_WITH_ONLINE_INSTALLER,
+            Install(false,          // is_interactive
+                    true,           // is_app_install
+                    true,           // is_eula_required
+                    false,          // is_oem_install
+                    false,          // is_install_elevated_instance
+                    _T("unused"),   // install_cmd_line
+                    args_,
+                    &is_machine,
+                    &has_ui_been_displayed));
+  EXPECT_FALSE(has_ui_been_displayed);
+
+  EXPECT_SUCCEEDED(scheduled_task_utils::UninstallGoopdateTasks(true));
+}
+
+TEST_F(RegistryProtectedInstallTest, Install_NeedsElevation_Silent) {
+  if (vista_util::IsUserAdmin()) {
+    std::wcout << _T("\tTest did not run because the user IS an admin.")
+               << std::endl;
+    return;
+  }
+
+  bool is_machine = true;
+  bool has_ui_been_displayed = false;
+  EXPECT_EQ(GOOPDATE_E_SILENT_INSTALL_NEEDS_ELEVATION,
+            Install(false,          // is_interactive
+                    true,           // is_app_install
+                    false,          // is_eula_required
+                    false,          // is_oem_install
+                    false,          // is_install_elevated_instance
+                    _T("unused"),   // install_cmd_line
+                    args_,
+                    &is_machine,
+                    &has_ui_been_displayed));
+  EXPECT_FALSE(has_ui_been_displayed);
+}
+
+// Tests that non-app installs can request elevation.
+// TODO(omaha3): Once the elevation code is finalized, figure out a way to cause
+// it to fail without a UAC prompt. Then change the expected error code to a
+// list of possible values.
+TEST_F(RegistryProtectedInstallTest, Install_NeedsElevation_NotAppInstall) {
+  if (vista_util::IsUserAdmin()) {
+    std::wcout << _T("\tTest did not run because the user IS an admin.")
+               << std::endl;
+    return;
+  }
+
+  bool is_machine = true;
+  bool has_ui_been_displayed = false;
+  EXPECT_NE(GOOPDATE_E_SILENT_INSTALL_NEEDS_ELEVATION,
+            Install(true,           // is_interactive
+                    false,          // is_app_install
+                    false,          // is_eula_required
+                    false,          // is_oem_install
+                    false,          // is_install_elevated_instance
+                    _T("unused"),   // install_cmd_line
+                    args_,
+                    &is_machine,
+                    &has_ui_been_displayed));
+  EXPECT_FALSE(has_ui_been_displayed);
+}
+
+TEST_F(RegistryProtectedInstallTest, Install_NeedsElevation_ElevatedInstance) {
+  if (vista_util::IsUserAdmin()) {
+    std::wcout << _T("\tTest did not run because the user IS an admin.")
+               << std::endl;
+    return;
+  }
+
+  bool is_machine = true;
+
+  // is_interactive is true to get past that check.
+  bool has_ui_been_displayed = false;
+  EXPECT_EQ(GOOPDATE_E_INSTALL_ELEVATED_PROCESS_NEEDS_ELEVATION,
+            Install(true,           // is_interactive
+                    true,           // is_app_install
+                    false,          // is_eula_required
+                    false,          // is_oem_install
+                    true,           // is_install_elevated_instance
+                    _T("unused"),   // install_cmd_line
+                    args_,
+                    &is_machine,
+                    &has_ui_been_displayed));
+  EXPECT_FALSE(has_ui_been_displayed);
+}
+
+// This test will never run because developers do not run XP as non-admin.
+// TODO(omaha3): Implement some way to fake/mock IsUserAdmin(), etc.
+TEST_F(RegistryProtectedInstallTest, Install_NeedsElevation_XpNonAdmin) {
+  if (vista_util::IsVistaOrLater()) {
+    std::wcout << _T("\tTest did not run because OS is Vista or later.")
+               << std::endl;
+    return;
+  }
+  if (vista_util::IsUserAdmin()) {
+    std::wcout << _T("\tTest did not run because the user IS an admin.")
+               << std::endl;
+    return;
+  }
+
+  bool is_machine = true;
+
+  // is_interactive is true to get past that check.
+  bool has_ui_been_displayed = false;
+  EXPECT_EQ(GOOPDATE_E_NONADMIN_INSTALL_ADMIN_APP,
+            Install(true,           // is_interactive
+                    true,           // is_app_install
+                    false,          // is_eula_required
+                    false,          // is_oem_install
+                    false,          // is_install_elevated_instance
+                    _T("unused"),   // install_cmd_line
+                    args_,
+                    &is_machine,
+                    &has_ui_been_displayed));
+  EXPECT_FALSE(has_ui_been_displayed);
+}
+
+
+// TODO(omaha3): Once the elevation code is finalized, figure out a way to cause
+// it to fail before the UAC prompt and test GOOPDATE_E_ELEVATION_FAILED_ADMIN
+// and GOOPDATE_E_ELEVATION_FAILED_NON_ADMIN. Check the command line used for
+// elevation if possible.
+
+// TODO(omaha3): If UI ends up not being handled in this method, test
+// is_interactive variations too.
+
+// TODO(omaha3): Test more success cases, including Setup and handoff.
+
+// TODO(omaha3): Enable when support for offline builds is finalized.
+#if 0
+class SetupOfflineInstallerTest : public testing::Test {
+ protected:
+  static bool CallCopyOfflineFiles(const CommandLineArgs& args,
+                                   const CString& target_location) {
+    omaha::Setup setup(false, &args);
+    return setup.CopyOfflineFiles(target_location);
+  }
+
+  static HRESULT CallCopyOfflineFilesForGuid(const CString& app_guid,
+                                             const CString& target_location) {
+    return omaha::Setup::CopyOfflineFilesForGuid(app_guid, target_location);
+  }
+};
+
+TEST_F(SetupOfflineInstallerTest, ValidOfflineInstaller) {
+  CString guid_string = _T("{CDABE316-39CD-43BA-8440-6D1E0547AEE6}");
+
+  CString offline_manifest_path(guid_string);
+  offline_manifest_path += _T(".gup");
+  offline_manifest_path = ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                                          offline_manifest_path);
+  EXPECT_SUCCEEDED(File::Copy(
+      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                      _T("server_manifest_one_app.xml")),
+      offline_manifest_path,
+      false));
+
+  CString installer_exe = _T("foo_installer.exe");
+  CString tarred_installer_path;
+  tarred_installer_path.Format(_T("%s.%s"), installer_exe, guid_string);
+  tarred_installer_path = ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                                          tarred_installer_path);
+
+  EXPECT_SUCCEEDED(File::Copy(
+      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                      kOmahaShellFileName),
+      tarred_installer_path,
+      false));
+
+  CommandLineArgs args;
+  CommandLineAppArgs app1;
+  app1.app_guid = StringToGuid(guid_string);
+  args.extra.apps.push_back(app1);
+  CString target_location = ConcatenatePath(
+                                app_util::GetCurrentModuleDirectory(),
+                                _T("offline_test"));
+
+  EXPECT_TRUE(CallCopyOfflineFiles(args, target_location));
+
+  CString target_manifest = ConcatenatePath(target_location,
+                                            guid_string + _T(".gup"));
+  EXPECT_TRUE(File::Exists(target_manifest));
+  CString target_file = ConcatenatePath(
+      ConcatenatePath(target_location, guid_string), installer_exe);
+  EXPECT_TRUE(File::Exists(target_file));
+
+  EXPECT_SUCCEEDED(DeleteDirectory(target_location));
+  EXPECT_SUCCEEDED(File::Remove(tarred_installer_path));
+  EXPECT_SUCCEEDED(File::Remove(offline_manifest_path));
+}
+
+TEST_F(SetupOfflineInstallerTest, NoOfflineInstaller) {
+  CString guid_string = _T("{CDABE316-39CD-43BA-8440-6D1E0547AEE6}");
+  CommandLineArgs args;
+  CommandLineAppArgs app1;
+  app1.app_guid = StringToGuid(guid_string);
+  args.extra.apps.push_back(app1);
+  CString target_location = ConcatenatePath(
+                                app_util::GetCurrentModuleDirectory(),
+                                _T("offline_test"));
+
+  EXPECT_FALSE(CallCopyOfflineFiles(args, target_location));
+}
+
+TEST_F(SetupOfflineInstallerTest, ValidCopyOfflineFilesForGuid) {
+  CString guid_string = _T("{CDABE316-39CD-43BA-8440-6D1E0547AEE6}");
+
+  CString offline_manifest_path(guid_string);
+  offline_manifest_path += _T(".gup");
+  offline_manifest_path = ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                                          offline_manifest_path);
+  EXPECT_SUCCEEDED(File::Copy(
+      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                      _T("server_manifest_one_app.xml")),
+      offline_manifest_path,
+      false));
+
+  CString installer_exe = _T("foo_installer.exe");
+  CString tarred_installer_path;
+  tarred_installer_path.Format(_T("%s.%s"), installer_exe, guid_string);
+  tarred_installer_path = ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                                          tarred_installer_path);
+
+  EXPECT_SUCCEEDED(File::Copy(
+      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                      kOmahaShellFileName),
+      tarred_installer_path,
+      false));
+
+  CString target_location = ConcatenatePath(
+                                app_util::GetCurrentModuleDirectory(),
+                                _T("offline_test"));
+
+  EXPECT_SUCCEEDED(CallCopyOfflineFilesForGuid(guid_string, target_location));
+
+  CString target_manifest = ConcatenatePath(target_location,
+                                            guid_string + _T(".gup"));
+  EXPECT_TRUE(File::Exists(target_manifest));
+  CString target_file = ConcatenatePath(
+      ConcatenatePath(target_location, guid_string), installer_exe);
+  EXPECT_TRUE(File::Exists(target_file));
+
+  EXPECT_SUCCEEDED(DeleteDirectory(target_location));
+  EXPECT_SUCCEEDED(File::Remove(tarred_installer_path));
+  EXPECT_SUCCEEDED(File::Remove(offline_manifest_path));
+}
+
+TEST_F(SetupOfflineInstallerTest, NoCopyOfflineFilesForGuid) {
+  CString guid_string = _T("{CDABE316-39CD-43BA-8440-6D1E0547AEE6}");
+  CString target_location = ConcatenatePath(
+                                app_util::GetCurrentModuleDirectory(),
+                                _T("offline_test"));
+
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            CallCopyOfflineFilesForGuid(guid_string, target_location));
+}
+#endif
+
+// TODO(omaha): Duplicate these tests for machine.
+TEST_F(InstallHandoffUserTest, InstallApplications_HandoffWithShellMissing) {
+  CString shell_path = ConcatenatePath(omaha_path_, kOmahaShellFileName);
+  EXPECT_TRUE(SUCCEEDED(File::DeleteAfterReboot(shell_path)) ||
+              !vista_util::IsUserAdmin());
+  EXPECT_FALSE(File::Exists(shell_path));
+
+  CommandLineArgs args;
+  args.extra_args_str = _T("appguid={BF85992F-2E0F-4700-9A6C-FEC9126CEE4B}&")
+                        _T("appname=Foo&needsadmin=False&");
+  args.install_source = _T("unittest");
+  bool ui_displayed = false;
+  bool has_launched_handoff = false;
+  EXPECT_EQ(GOOPDATE_E_HANDOFF_FAILED,
+            internal::InstallApplications(false,
+                                          false,
+                                          args,
+                                          kSessionId_,
+                                          NULL,
+                                          &has_launched_handoff,
+                                          &ui_displayed));
+// TODO(omaha3): Verify the actual error when this is implemented.
+#if 0
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), extra_code1);
+#endif
+  EXPECT_FALSE(ui_displayed);
+}
+
+TEST_F(InstallHandoffUserTest,
+       InstallApplications_HandoffWithGoopdateDllMissing) {
+  CString dll_path = ConcatenatePath(path_, kOmahaDllName);
+  EXPECT_SUCCEEDED(File::Remove(dll_path));
+  EXPECT_FALSE(File::Exists(dll_path));
+
+  CommandLineArgs args;
+  args.extra_args_str = _T("appguid={BF85992F-2E0F-4700-9A6C-FEC9126CEE4B}&")
+                        _T("appname=Foo&needsadmin=False&");
+  args.install_source = _T("unittest");
+  bool ui_displayed = false;
+  bool has_launched_handoff = false;
+  EXPECT_EQ(GOOGLEUPDATE_E_DLL_NOT_FOUND,
+            internal::InstallApplications(false,
+                                          false,
+                                          args,
+                                          kSessionId_,
+                                          NULL,
+                                          &ui_displayed,
+                                          &has_launched_handoff));
+// TODO(omaha3): Verify the actual error when this is implemented.
+#if 0
+  EXPECT_EQ(0, setup_->extra_code1());
+#endif
+// TODO(omaha3): ui_displayed is temporarily always set to true.
+#if 0
+  EXPECT_FALSE(ui_displayed);
+#else
+  EXPECT_TRUE(ui_displayed);
+#endif
+}
+
+}  // namespace omaha
diff --git a/client/resource.h b/client/resource.h
new file mode 100644
index 0000000..46e1241
--- /dev/null
+++ b/client/resource.h
@@ -0,0 +1,21 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_CLIENT_RESOURCE_H_
+#define OMAHA_CLIENT_RESOURCE_H_
+
+#include "omaha/goopdate/resources/goopdateres/goopdate.grh"
+
+#endif  // OMAHA_CLIENT_RESOURCE_H_
diff --git a/client/shutdown_events.cc b/client/shutdown_events.cc
new file mode 100644
index 0000000..aa33021
--- /dev/null
+++ b/client/shutdown_events.cc
@@ -0,0 +1,86 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/client/shutdown_events.h"
+#include <atlsafe.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/reactor.h"
+#include "omaha/base/shutdown_handler.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/client/bundle_installer.h"
+
+namespace omaha {
+
+ShutdownEvents::ShutdownEvents(BundleInstaller* installer)
+    : installer_(installer) {
+  ASSERT1(installer);
+}
+
+ShutdownEvents::~ShutdownEvents() {
+  CORE_LOG(L2, (_T("[ShutdownEvents::~ShutdownEvents]")));
+  __mutexScope(lock_);
+
+  CORE_LOG(L2, (_T("[Destroying shutdown handler]")));
+  shutdown_handler_.reset();
+  reactor_.reset();
+}
+
+HRESULT ShutdownEvents::InitializeShutdownHandler(bool is_machine) {
+  CORE_LOG(L3, (_T("[InitializeShutdownHandler]")));
+
+  reactor_.reset(new Reactor);
+  shutdown_handler_.reset(new ShutdownHandler);
+  return shutdown_handler_->Initialize(reactor_.get(), this, is_machine);
+}
+
+// Shutdown() is called from a thread in the OS threadpool. The PostMessage
+// marshals the call over to the UI thread, which is where DoClose needs to be
+// (and is) called from.
+HRESULT ShutdownEvents::Shutdown() {
+  CORE_LOG(L2, (_T("[Shutdown]")));
+
+  __mutexScope(lock_);
+
+  CORE_LOG(L2, (_T("[Shutdown][IsWindow: %d]"), installer_->IsWindow()));
+  if (installer_->IsWindow()) {
+    installer_->PostMessage(WM_CLOSE, 0, 0);
+  }
+
+  return S_OK;
+}
+
+HRESULT ShutdownEvents::CreateShutdownHandler(
+    bool is_machine,
+    BundleInstaller* installer,
+    ShutdownCallback** shutdown_callback) {
+  ASSERT1(installer);
+  ASSERT1(shutdown_callback);
+  ASSERT1(!*shutdown_callback);
+
+  scoped_ptr<ShutdownEvents> shutdown_events(new ShutdownEvents(installer));
+  HRESULT hr = shutdown_events->InitializeShutdownHandler(is_machine);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[InitializeShutDownHandler failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  *shutdown_callback = shutdown_events.release();
+  return S_OK;
+}
+
+}  // namespace omaha
diff --git a/client/shutdown_events.h b/client/shutdown_events.h
new file mode 100644
index 0000000..04fef05
--- /dev/null
+++ b/client/shutdown_events.h
@@ -0,0 +1,54 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_CLIENT_SHUTDOWN_EVENTS_H_
+#define OMAHA_CLIENT_SHUTDOWN_EVENTS_H_
+
+#include <windows.h>
+#include <atlsafe.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/shutdown_callback.h"
+#include "omaha/base/synchronized.h"
+
+namespace omaha {
+
+class BundleInstaller;
+class Reactor;
+class ShutdownHandler;
+
+class ShutdownEvents : public ShutdownCallback {
+ public:
+  explicit ShutdownEvents(BundleInstaller* installer);
+  virtual ~ShutdownEvents();
+
+  HRESULT InitializeShutdownHandler(bool is_machine);
+  HRESULT Shutdown();
+  static HRESULT CreateShutdownHandler(bool is_machine,
+                                       BundleInstaller* installer,
+                                       ShutdownCallback** shutdown_callback);
+
+ private:
+  BundleInstaller* installer_;
+
+  LLock lock_;
+  scoped_ptr<Reactor> reactor_;
+  scoped_ptr<ShutdownHandler> shutdown_handler_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(ShutdownEvents);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_CLIENT_SHUTDOWN_EVENTS_H_
diff --git a/client/ua.cc b/client/ua.cc
new file mode 100644
index 0000000..20e0d91
--- /dev/null
+++ b/client/ua.cc
@@ -0,0 +1,197 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// TODO(omaha): Dig out the RefHolder in scope_guard.h so we can use const
+// references instead pointers. This TODO was added for some code that no longer
+// exists, but it is still a good idea.
+
+#include "omaha/client/ua.h"
+#include "omaha/client/ua_internal.h"
+#include <windows.h>
+#include <atlstr.h>
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/program_instance.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/utils.h"
+#include "omaha/client/install_apps.h"
+#include "omaha/client/install_self.h"
+#include "omaha/client/client_metrics.h"
+#include "omaha/common/app_registry_utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/event_logger.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/ping.h"
+
+// Design Notes:
+// Following are the mutexes that are taken by the worker
+// 1. SingleUpdateWorker. Only taken by the update worker.
+// 2. SingleInstallWorker. This is application specific. Only taken by the
+//    install worker and for the specific application.
+// 3. Before install, the install manager takes the global install lock.
+// 4. A key thing to add to this code is after taking the install lock,
+//    to validate that the version of the applicaion that is present in the
+//    registry is the same as that we queried for. The reason to do this
+//    is to ensure that there are no races between update and install workers.
+// 5. Termination of the worker happens because of four reasons:
+//    a. Shutdown event - Only applicable to the update worker. When this event
+//       is signalled, the main thread comes out of the wait. It then tries to
+//       destroy the contained thread pool, which causes a timed wait for the
+//       worker thread. The worker thread is notified by setting a
+//       cancelled flag on the worker.
+//    b. Install completes, user closes UI - Only applicable for the
+//       interactive installs. In this case the main thread comes out of
+//       the message loop and deletes the thread pool. The delete happens
+//       immediately, since the worker is doing nothing.
+//    c. User cancels install - Only applicable in case if interactive installs.
+//       The main thread sets the cancelled flag on the workerjob and comes out
+//       of the message loop. It then tries to delete the thread pool, causing
+//       a timed wait. The worker job queries the cancelled flag periodically
+//       and quits as soon as possible.
+//    d. The update worker completes - In this case we do not run on a thread
+//       pool.
+
+namespace omaha {
+
+namespace {
+
+void WriteUpdateAppsStartEvent(bool is_machine) {
+  GoogleUpdateLogEvent update_event(EVENTLOG_INFORMATION_TYPE,
+                                    kWorkerStartEventId,
+                                    is_machine);
+  update_event.set_event_desc(_T("Update Apps start"));
+
+  ConfigManager& cm = *ConfigManager::Instance();
+
+  int au_check_period_ms = cm.GetAutoUpdateTimerIntervalMs();
+  int time_since_last_checked_sec = cm.GetTimeSinceLastCheckedSec(is_machine);
+  bool is_period_overridden = false;
+  int last_check_period_ms = cm.GetLastCheckPeriodSec(&is_period_overridden);
+
+  CString event_text;
+  SafeCStringFormat(&event_text,
+      _T("AuCheckPeriodMs=%d, TimeSinceLastCheckedSec=%d, ")
+      _T("LastCheckedPeriodSec=%d"),
+      au_check_period_ms, time_since_last_checked_sec, last_check_period_ms);
+
+  update_event.set_event_text(event_text);
+  update_event.WriteEvent();
+}
+
+}  // namespace
+
+namespace internal {
+
+// Ensures there is only one instance of /ua per session per Omaha instance.
+bool EnsureSingleUAProcess(bool is_machine, ProgramInstance** instance) {
+  ASSERT1(instance);
+  ASSERT1(!*instance);
+  NamedObjectAttributes single_ua_process_attr;
+  GetNamedObjectAttributes(kUpdateAppsSingleInstance,
+                           is_machine,
+                           &single_ua_process_attr);
+
+  *instance = new ProgramInstance(single_ua_process_attr.name);
+  return !(*instance)->EnsureSingleInstance();
+}
+
+}  // namespace internal
+
+// Always checks whether it should uninstall.
+// Checks for updates of all apps if the required period has elapsed, it is
+// being run on-demand, or an uninstall seems necessary. It will also send a
+// self-update failure ping in these cases if necessary.
+//
+// Calls UpdateAllApps(), which will call IAppBundle::updateAllApps(), even in
+// cases where an uninstall seems necessary. This allows an uninstall ping to
+// be sent for any uninstalled apps. Because the COM server does not know about
+// uninstall, the COM server will also do a final update check for the remaining
+// app - should be Omaha. It's possible that this update check could result in
+// a self-update, in which case the uninstall may fail and be delayed an hour.
+// See http://b/2814535.
+// Since all pings are sent by the AppBundle destructor, if the bundle has
+// normal or uninstall pings need, the network request could delay the exiting
+// of the COM server beyond the point where this client releases the IAppBundle.
+// and launches /uninstall. This could cause uninstall to fail if the ping takes
+// a long time.
+//
+// TODO(omaha): Test this method as it is very important.
+HRESULT UpdateApps(bool is_machine,
+                   bool is_interactive,
+                   bool is_on_demand,
+                   const CString& install_source,
+                   const CString& display_language,
+                   bool* has_ui_been_displayed) {
+  CORE_LOG(L1, (_T("[UpdateApps]")));
+  ASSERT1(has_ui_been_displayed);
+
+  WriteUpdateAppsStartEvent(is_machine);
+
+  scoped_ptr<ProgramInstance> single_ua_process;
+
+  if (internal::EnsureSingleUAProcess(is_machine, address(single_ua_process))) {
+    OPT_LOG(L1, (_T("[Another worker is already running. Exiting.]")));
+    ++metric_client_another_update_in_progress;
+    return GOOPDATE_E_UA_ALREADY_RUNNING;
+  }
+
+  if (ConfigManager::Instance()->CanUseNetwork(is_machine)) {
+    VERIFY1(SUCCEEDED(Ping::SendPersistedPings(is_machine)));
+  }
+
+  // Generate a session ID for network accesses.
+  CString session_id;
+  VERIFY1(SUCCEEDED(GetGuid(&session_id)));
+
+  // A tentative uninstall check is done here. There are stronger checks,
+  // protected by locks, which are done by Setup.
+  size_t num_clients(0);
+  const bool is_uninstall =
+      FAILED(app_registry_utils::GetNumClients(is_machine, &num_clients)) ||
+      num_clients <= 1;
+  CORE_LOG(L4, (_T("[UpdateApps][registered apps: %u]"), num_clients));
+
+  const bool should_check_for_updates =
+      goopdate_utils::ShouldCheckForUpdates(is_machine);
+
+  if (is_uninstall) {
+    // TODO(omaha3): The interactive /ua process will not exit without user
+    // interaction. This could cause the uninstall to fail.
+    CORE_LOG(L1, (_T("[/ua launching /uninstall]")));
+    return goopdate_utils::LaunchUninstallProcess(is_machine);
+  }
+
+  if (!(is_on_demand || should_check_for_updates)) {
+    OPT_LOG(L1, (_T("[Update check not needed at this time]")));
+    return S_OK;
+  }
+
+  HRESULT hr = UpdateAllApps(is_machine,
+                             is_interactive,
+                             install_source,
+                             display_language,
+                             session_id,
+                             has_ui_been_displayed);
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[UpdateAllApps failed][0x%08x]"), hr));
+  }
+  return hr;
+}
+
+}  // namespace omaha
diff --git a/client/ua.h b/client/ua.h
new file mode 100644
index 0000000..b0392a5
--- /dev/null
+++ b/client/ua.h
@@ -0,0 +1,38 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Functions related to the /ua "update apps" process.
+
+#ifndef OMAHA_CLIENT_UA_H_
+#define OMAHA_CLIENT_UA_H_
+
+#include <windows.h>
+#include <atlstr.h>
+
+namespace omaha {
+
+// TODO(omaha3): Should this be in a class or namespace?
+
+// Performs the duties of the silent auto-update process /ua.
+HRESULT UpdateApps(bool is_machine,
+                   bool is_interactive,
+                   bool is_on_demand,
+                   const CString& install_source,
+                   const CString& display_language,
+                   bool* has_ui_been_displayed);
+
+}  // namespace omaha
+
+#endif  // OMAHA_CLIENT_UA_H_
diff --git a/client/ua_internal.h b/client/ua_internal.h
new file mode 100644
index 0000000..0e3de1b
--- /dev/null
+++ b/client/ua_internal.h
@@ -0,0 +1,33 @@
+// Copyright 2008-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_CLIENT_UA_INTERNAL_H_
+#define OMAHA_CLIENT_UA_INTERNAL_H_
+
+#include <windows.h>
+
+namespace omaha {
+
+class ProgramInstance;
+
+namespace internal {
+
+bool EnsureSingleUAProcess(bool is_machine, ProgramInstance** instance);
+
+}  // namespace internal
+
+}  // namespace omaha
+
+#endif  // OMAHA_CLIENT_UA_INTERNAL_H_
diff --git a/common/ATLRegMapEx.h b/common/ATLRegMapEx.h
deleted file mode 100644
index 2a71575..0000000
--- a/common/ATLRegMapEx.h
+++ /dev/null
@@ -1,230 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Extension to DECLARE_REGISTRY_RESOURCEID that makes adding stuff to
-// your reg file as simple as using an atl macro map.
-//
-// Adapted from http://thecodeproject.com/atl/RegistryMap.asp
-/*
- * Defines a 'registry' map for adding variables to rgs files.
- * Original Code Copyright 2001-2003 Michael Geddes.  All rights reserved.
- * Modified Code Copyright 2005 Google Inc.
- */
-
-/* use this as your RGS file -- remove or add parameters as you see fit
-
-HKCR
-{
-  %PROGID%.%VERSION% = s '%DESCRIPTION%'
-  {
-    CLSID = s '%CLSID%'
-  }
-  %PROGID% = s '%DESCRIPTION%'
-  {
-    CLSID = s '%CLSID%'
-    CurVer = s '%PROGID%.%VERSION%'
-  }
-  NoRemove CLSID
-  {
-    ForceRemove %CLSID% = s '%DESCRIPTION%'
-    {
-      ProgID = s '%PROGID%.%VERSION%'
-       VersionIndependentProgID = s '%PROGID%'
-      ForceRemove 'Programmable'
-      InprocServer32 = s '%MODULE%'
-      {
-        val ThreadingModel = s '%THREADING%'
-      }
-      'TypeLib' = s '%LIBID%'
-    }
-  }
-}
-
-*/
-
-#ifndef OMAHA_COMMON_ATLREGMAPEX_H__
-#define OMAHA_COMMON_ATLREGMAPEX_H__
-
-#include <atlbase.h>
-#include <atlconv.h>
-#include <atlstr.h>
-#include "omaha/common/app_util.h"
-#include "omaha/common/path.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-struct _ATL_REGMAP_ENTRYKeeper : public _ATL_REGMAP_ENTRY {
-  // Returns a new Olestr that needs to be freed by caller.
-  LPCOLESTR NewOlestrFromTstr(LPCTSTR tstr)  {
-    CT2COLE olestr(tstr);
-    int alloc_length = lstrlen(olestr) + 1;
-    LPOLESTR new_olestr =  new OLECHAR[alloc_length];
-    if (new_olestr) {
-      lstrcpyn(new_olestr, olestr, alloc_length);
-    }
-    return new_olestr;
-  }
-
-  _ATL_REGMAP_ENTRYKeeper() {
-    szKey = NULL;
-    szData = NULL;
-  }
-
-  // REGMAP_ENTRY(x, y)
-  _ATL_REGMAP_ENTRYKeeper(LPCTSTR key_tstr, LPCWSTR data_tstr)  {
-    szKey = NewOlestrFromTstr(key_tstr);
-    szData = NewOlestrFromTstr(CW2T(data_tstr));
-  }
-
-  // REGMAP_ENTRY(x, y)
-  _ATL_REGMAP_ENTRYKeeper(LPCTSTR key_tstr, LPCSTR data_tstr)  {
-    szKey = NewOlestrFromTstr(key_tstr);
-    szData = NewOlestrFromTstr(CA2T(data_tstr));
-  }
-
-  // REGMAP_MODULE(x)
-  _ATL_REGMAP_ENTRYKeeper(LPCTSTR key_tstr) {
-    szKey = NewOlestrFromTstr(key_tstr);
-    szData =
-        NewOlestrFromTstr(EnclosePathIfExe(app_util::GetCurrentModulePath()));
-  }
-
-  // REGMAP_MODULE2(x, modulename)
-  _ATL_REGMAP_ENTRYKeeper(LPCTSTR key_tstr,
-                          LPCTSTR module_name_tstr,
-                          bool /* is_relative_to_current_module */)  {
-    szKey = NewOlestrFromTstr(key_tstr);
-    szData = NULL;
-
-    CStringW full_module_name(app_util::GetCurrentModuleDirectory());
-    full_module_name += _T("\\");
-    full_module_name += module_name_tstr;
-    szData = NewOlestrFromTstr(EnclosePathIfExe(full_module_name));
-  }
-
-  // REGMAP_EXE_MODULE(x)
-  _ATL_REGMAP_ENTRYKeeper(LPCTSTR key_tstr,
-                          bool /* is_current_exe_module */)  {
-    szKey = NewOlestrFromTstr(key_tstr);
-    szData = NewOlestrFromTstr(EnclosePathIfExe(app_util::GetModulePath(NULL)));
-  }
-
-  // REGMAP_RESOURCE(x, resid)
-  _ATL_REGMAP_ENTRYKeeper(LPCTSTR key_tstr, UINT resid, bool /* resource */)  {
-    szKey = NewOlestrFromTstr(key_tstr);
-    CStringW res_name;
-    BOOL success = res_name.LoadString(resid);
-    ATLASSERT(success);
-    szData = NewOlestrFromTstr(res_name);
-  }
-
-  // REGMAP_UUID(x, clsid)
-  _ATL_REGMAP_ENTRYKeeper(LPCTSTR key_tstr, REFGUID guid)  {
-    szKey = NewOlestrFromTstr(key_tstr);
-    szData = NewOlestrFromTstr(GuidToString(guid));
-  }
-
-  ~_ATL_REGMAP_ENTRYKeeper()  {
-    delete [] szKey;
-    delete [] szData;
-  }
-};
-
-// This now supports DECLARE_OLEMISC_STATUS()
-#define BEGIN_REGISTRY_MAP()                                                \
-  __if_exists(_GetMiscStatus) {                                             \
-    static LPCTSTR _GetMiscStatusString()  {                                \
-      static TCHAR misc_string[32] = {0}                                    \
-      if (!misc_string[0])  {                                               \
-        wsprintf(misc_string, _T("%d"), _GetMiscStatus());                  \
-      }                                                                     \
-                                                                            \
-      return misc_string;                                                   \
-    }                                                                       \
-  }                                                                         \
-                                                                            \
-  static struct _ATL_REGMAP_ENTRY *_GetRegistryMap() {                      \
-    static const _ATL_REGMAP_ENTRYKeeper map[] = {                          \
-      __if_exists(_GetMiscStatusString) {                                   \
-        _ATL_REGMAP_ENTRYKeeper(_T("OLEMISC"), _GetMiscStatusString()),     \
-      }                                                                     \
-      __if_exists(GetAppIdT) {                                              \
-        _ATL_REGMAP_ENTRYKeeper(_T("APPID"), GetAppIdT()),                  \
-      }                                                                     \
-
-#define REGMAP_ENTRY(x, y) _ATL_REGMAP_ENTRYKeeper((x), (y)),
-
-#define REGMAP_RESOURCE(x, resid) _ATL_REGMAP_ENTRYKeeper((x), (resid), true),
-
-#define REGMAP_UUID(x, clsid) _ATL_REGMAP_ENTRYKeeper((x), (clsid)),
-
-// Add in an entry with key x, and value being the current module path.
-// For example, REGMAP_MODULE("foo"), with the current module being
-// "goopdate.dll" will result in the entry:
-// "foo", "{blah}\\Google\\Update\\1.2.71.7\\goopdate.dll"
-#define REGMAP_MODULE(x) _ATL_REGMAP_ENTRYKeeper((x)),
-
-// Add in an entry with key x, and value being modulename, fully qualified with
-// the current module path. For example, REGMAP_MODULE2("foo", "npClick7.dll")
-// with the current module being "goopdate.dll" will result in the entry:
-// "foo", "{blah}\\Google\\Update\\1.2.71.7\\npClick7.dll"
-#define REGMAP_MODULE2(x, modulename)                                       \
-    _ATL_REGMAP_ENTRYKeeper((x), (modulename), true),
-
-// Add in an entry with key x, and value being the currently running EXE's
-// module path. For example, REGMAP_EXE_MODULE("foo"), with the current process
-// being googleupdate.exe will result in the entry:
-// "foo", "{blah}\\Google\\Update\\googleupdate.exe"
-#define REGMAP_EXE_MODULE(x) _ATL_REGMAP_ENTRYKeeper((x), true),
-
-#define END_REGISTRY_MAP() _ATL_REGMAP_ENTRYKeeper()                        \
-    };                                                                      \
-    return (_ATL_REGMAP_ENTRY *)map;                                        \
-  }
-
-#define DECLARE_REGISTRY_RESOURCEID_EX(x)                                   \
-  static HRESULT WINAPI UpdateRegistry(BOOL reg) {                          \
-  __if_exists(_Module) {                                                    \
-    return _Module.UpdateRegistryFromResource((UINT)(x),                    \
-                                               (reg),                       \
-                                               _GetRegistryMap());          \
-  }                                                                         \
-  __if_not_exists(_Module) {                                                \
-    return ATL::_pAtlModule->UpdateRegistryFromResource((UINT)(x),          \
-                                                        (reg),              \
-                                                        _GetRegistryMap()); \
-  }                                                                         \
-}
-
-#define DECLARE_REGISTRY_APPID_RESOURCEID_EX(resid, appid)                  \
-  static LPCOLESTR GetAppId() throw() {                                     \
-    static const CString app_id(appid);                                     \
-    return app_id;                                                          \
-  }                                                                         \
-  static LPCTSTR GetAppIdT() throw() {                                      \
-    return GetAppId();                                                      \
-  }                                                                         \
-  static HRESULT WINAPI UpdateRegistryAppId(BOOL reg) throw() {             \
-    return ATL::_pAtlModule->UpdateRegistryFromResource(resid,              \
-                                                        (reg),              \
-                                                        _GetRegistryMap()); \
-  }
-// END registry map
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_ATLREGMAPEX_H__
-
diff --git a/common/accounts.cc b/common/accounts.cc
deleted file mode 100644
index 95a1727..0000000
--- a/common/accounts.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Enumeration of the user accounts on the PC.
-
-#include "omaha/common/accounts.h"
-
-#include <sddl.h>
-#include "base/basictypes.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/reg_key.h"
-
-namespace omaha {
-
-namespace accounts {
-
-const wchar_t kActiveProfilesKey[] =
-    L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList";
-
-HRESULT GetAllUserSids(CSimpleArray<CString> *sid_array) {
-  ASSERT(sid_array, (L""));
-
-  RegKey key_profiles;
-  HRESULT hr = key_profiles.Open(HKEY_LOCAL_MACHINE, kActiveProfilesKey);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  sid_array->RemoveAll();
-
-  uint32 total_keys = key_profiles.GetSubkeyCount();
-
-  for (uint32 i = 0 ; i < total_keys ; ++i) {
-    CString possible_user_sid, name, domain;
-    SID_NAME_USE user_type;
-
-    if (SUCCEEDED(key_profiles.GetSubkeyNameAt(i, &possible_user_sid))) {
-      if (SUCCEEDED(GetUserInfo(possible_user_sid, &name,
-                                &domain, &user_type)) &&
-          user_type == SidTypeUser) {
-        sid_array->Add(possible_user_sid);
-      }
-    }
-  }
-
-  return hr;
-}
-
-HRESULT GetUserInfo(const wchar_t *sid_str, CString *name,
-                    CString *domain, SID_NAME_USE *user_type) {
-  ASSERT(sid_str, (L""));
-  ASSERT(name, (L""));
-  ASSERT(domain, (L""));
-  ASSERT(user_type, (L""));
-
-  PSID sid = NULL;
-  HRESULT ret = E_FAIL;
-  if (ConvertStringSidToSid(sid_str, &sid)) {
-    DWORD name_size = 0, domain_size = 0;
-    if (!LookupAccountSid(NULL, sid, NULL, &name_size, NULL,
-                          &domain_size, user_type) &&
-        ERROR_INSUFFICIENT_BUFFER != GetLastError()) {
-      ret = GetCurError();
-      LocalFree(sid);
-      return ret;
-    }
-
-    ASSERT(name_size, (L""));
-    ASSERT(domain_size, (L""));
-    if (!domain_size || !name_size) {
-      LocalFree(sid);
-      return E_UNEXPECTED;
-    }
-
-    wchar_t* c_name = new wchar_t[name_size];
-    ASSERT(c_name, (L""));
-    if (!c_name) {
-      LocalFree(sid);
-      return E_OUTOFMEMORY;
-    }
-
-    wchar_t* c_domain = new wchar_t[domain_size];
-    ASSERT(c_domain, (L""));
-    if (!c_domain) {
-      delete[] c_name;
-      LocalFree(sid);
-      return E_OUTOFMEMORY;
-    }
-
-    if (LookupAccountSid(NULL, sid, c_name, &name_size, c_domain,
-                         &domain_size, user_type)) {
-      ret = S_OK;
-      name->SetString(c_name);
-      domain->SetString(c_domain);
-    } else {
-      ret = GetCurError();
-    }
-
-    delete[] c_name;
-    delete[] c_domain;
-    LocalFree(sid);
-  }
-
-  return ret;
-}
-
-}  // namespace accounts
-
-}  // namespace omaha
-
diff --git a/common/app_registry_utils.cc b/common/app_registry_utils.cc
new file mode 100644
index 0000000..2b31846
--- /dev/null
+++ b/common/app_registry_utils.cc
@@ -0,0 +1,488 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/app_registry_utils.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/experiment_labels.h"
+
+namespace omaha {
+
+namespace app_registry_utils {
+
+CString GetAppClientsKey(bool is_machine, const CString& app_guid) {
+  return AppendRegKeyPath(
+      ConfigManager::Instance()->registry_clients(is_machine),
+      app_guid);
+}
+
+CString GetAppClientStateKey(bool is_machine, const CString& app_guid) {
+  return AppendRegKeyPath(
+      ConfigManager::Instance()->registry_client_state(is_machine),
+      app_guid);
+}
+
+CString GetAppClientStateMediumKey(bool is_machine, const CString& app_guid) {
+  ASSERT1(is_machine);
+  UNREFERENCED_PARAMETER(is_machine);
+  return AppendRegKeyPath(
+      ConfigManager::Instance()->machine_registry_client_state_medium(),
+      app_guid);
+}
+
+// The EULA is assumed to be accepted unless eualaccepted=0 in the ClientState
+// key. For machine apps in this case, eulaccepted=1 in ClientStateMedium also
+// indicates acceptance and the value in ClientState is updated.
+bool IsAppEulaAccepted(bool is_machine,
+                       const CString& app_guid,
+                       bool require_explicit_acceptance) {
+  const CString state_key = GetAppClientStateKey(is_machine, app_guid);
+
+  DWORD eula_accepted = 0;
+  if (SUCCEEDED(RegKey::GetValue(state_key,
+                                 kRegValueEulaAccepted,
+                                 &eula_accepted))) {
+    if (0 != eula_accepted) {
+      return true;
+    }
+  } else {
+    if (!require_explicit_acceptance) {
+      return true;
+    }
+  }
+
+  if (!is_machine) {
+    return false;
+  }
+
+  eula_accepted = 0;
+  if (SUCCEEDED(RegKey::GetValue(
+                    GetAppClientStateMediumKey(is_machine, app_guid),
+                    kRegValueEulaAccepted,
+                    &eula_accepted))) {
+    if (0 == eula_accepted) {
+      return false;
+    }
+  } else {
+    return false;
+  }
+
+  VERIFY1(SUCCEEDED(RegKey::SetValue(state_key,
+                                     kRegValueEulaAccepted,
+                                     eula_accepted)));
+  return true;
+}
+
+// Does not need to set ClientStateMedium.
+HRESULT SetAppEulaNotAccepted(bool is_machine, const CString& app_guid) {
+  return RegKey::SetValue(GetAppClientStateKey(is_machine, app_guid),
+                          kRegValueEulaAccepted,
+                          static_cast<DWORD>(0));
+}
+
+// Deletes eulaaccepted from ClientState and ClientStateMedium.
+HRESULT ClearAppEulaNotAccepted(bool is_machine, const CString& app_guid) {
+  const CString state_key = GetAppClientStateKey(is_machine, app_guid);
+  if (RegKey::HasKey(state_key)) {
+    HRESULT hr = RegKey::DeleteValue(state_key, kRegValueEulaAccepted);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (!is_machine) {
+    return S_OK;
+  }
+
+  const CString state_medium_key =
+      GetAppClientStateMediumKey(is_machine, app_guid);
+  if (RegKey::HasKey(state_medium_key)) {
+    HRESULT hr = RegKey::DeleteValue(state_medium_key, kRegValueEulaAccepted);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  return S_OK;
+}
+
+// For machine apps, ClientStateMedium takes precedence.
+// Does not propogate the ClientStateMedium value to ClientState.
+bool AreAppUsageStatsEnabled(bool is_machine, const CString& app_guid) {
+  if (is_machine) {
+    DWORD stats_enabled = 0;
+    if (SUCCEEDED(RegKey::GetValue(GetAppClientStateMediumKey(is_machine,
+                                                              app_guid),
+                                   kRegValueUsageStats,
+                                   &stats_enabled))) {
+      return (TRISTATE_TRUE == stats_enabled);
+    }
+  }
+
+  DWORD stats_enabled = 0;
+  if (SUCCEEDED(RegKey::GetValue(GetAppClientStateKey(is_machine, app_guid),
+                              kRegValueUsageStats,
+                              &stats_enabled))) {
+    return (TRISTATE_TRUE == stats_enabled);
+  }
+
+  return false;
+}
+
+// Does nothing if usage_stats_enable is TRISTATE_NONE.
+// For machine apps, clears ClientStateMedium because the app may be reading it
+// if present.
+HRESULT SetUsageStatsEnable(bool is_machine,
+                            const CString& app_guid,
+                            Tristate usage_stats_enable) {
+  if (TRISTATE_NONE == usage_stats_enable) {
+    return S_OK;
+  }
+
+  const DWORD stats_enabled = (TRISTATE_TRUE == usage_stats_enable) ? 1 : 0;
+
+  HRESULT hr = RegKey::SetValue(GetAppClientStateKey(is_machine, app_guid),
+                                kRegValueUsageStats,
+                                stats_enabled);
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[Failed to set usagestats][0x%08x]"), hr));
+    return hr;
+  }
+
+  if (!is_machine) {
+    return S_OK;
+  }
+
+  const CString state_medium_key =
+      GetAppClientStateMediumKey(is_machine, app_guid);
+  if (RegKey::HasKey(state_medium_key)) {
+    hr = RegKey::DeleteValue(state_medium_key, kRegValueUsageStats);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  return S_OK;
+}
+
+// Google Update does not have a referral_id. Everything else is the same as for
+// apps.
+HRESULT SetGoogleUpdateBranding(const CString& client_state_key_path,
+                                const CString& brand_code,
+                                const CString& client_id) {
+  HRESULT hr(SetAppBranding(client_state_key_path,
+                            brand_code,
+                            client_id,
+                            CString()));
+
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  RegKey state_key;
+  hr = state_key.Open(client_state_key_path);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Legacy support for older versions that do not write the FirstInstallTime.
+  // This code ensures that FirstInstallTime always has a valid non-zero value.
+  DWORD install_time(0);
+  if (FAILED(state_key.GetValue(kRegValueInstallTimeSec, &install_time)) ||
+      !install_time) {
+    const DWORD now = Time64ToInt32(GetCurrent100NSTime());
+    VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueInstallTimeSec, now)));
+    CORE_LOG(L3, (_T("[InstallTime missing. Setting it here.][%u]"), now));
+  }
+
+  return S_OK;
+}
+
+// Branding information is only written if a brand code is not already present.
+// We should only write it if this is the first install of Omaha to avoid giving
+// undue credit to a later installer source. Writing a default brand code
+// prevents future branded installations from setting their brand.
+// As suggested by PSO, there is no default client ID.
+// Assumes the specified Client State key has been created.
+HRESULT SetAppBranding(const CString& client_state_key_path,
+                       const CString& brand_code,
+                       const CString& client_id,
+                       const CString& referral_id) {
+  CORE_LOG(L3, (_T("[app_registry_utils::SetAppBranding][%s][%s][%s][%s]"),
+                client_state_key_path, brand_code, client_id, referral_id));
+
+  if (brand_code.GetLength() > kBrandIdLength) {
+    return E_INVALIDARG;
+  }
+
+  RegKey state_key;
+  HRESULT hr = state_key.Create(client_state_key_path);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CString existing_brand_code;
+  hr = state_key.GetValue(kRegValueBrandCode, &existing_brand_code);
+  if (!existing_brand_code.IsEmpty()) {
+    ASSERT1(SUCCEEDED(hr));
+    if (existing_brand_code.GetLength() > kBrandIdLength) {
+      // Bug 1358852: Brand code garbled with one click.
+      VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueBrandCode,
+                            existing_brand_code.Left(kBrandIdLength))));
+    }
+    return S_OK;
+  }
+
+  const TCHAR* brand_code_to_write = brand_code.IsEmpty() ?
+                                     kDefaultGoogleUpdateBrandCode :
+                                     brand_code;
+  hr = state_key.SetValue(kRegValueBrandCode, brand_code_to_write);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (!client_id.IsEmpty()) {
+    hr = state_key.SetValue(kRegValueClientId, client_id);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (!referral_id.IsEmpty()) {
+    hr = state_key.SetValue(kRegValueReferralId, referral_id);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  const DWORD now = Time64ToInt32(GetCurrent100NSTime());
+  VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueInstallTimeSec, now)));
+
+  return S_OK;
+}
+
+void PersistSuccessfulInstall(const CString& client_state_key_path,
+                              bool is_update,
+                              bool is_offline) {
+  CORE_LOG(L3,
+           (_T("[app_registry_utils::PersistSuccessfulInstall][%s][%d][%d]"),
+            client_state_key_path, is_update, is_offline));
+  ASSERT1(!is_update || !is_offline);
+
+  ClearUpdateAvailableStats(client_state_key_path);
+
+  if (!is_offline) {
+    // TODO(omaha): the semantics of this function are confusing in the
+    // case of recording a successful update check. Omaha knows
+    // precisely when an update check with the server is being made and it
+    // can call PersistSuccessfulUpdateCheck without making any other
+    // assumptions.
+    //
+    // Assumes that all updates are online.
+    PersistSuccessfulUpdateCheck(client_state_key_path);
+  }
+
+  if (is_update) {
+    const DWORD now = Time64ToInt32(GetCurrent100NSTime());
+    VERIFY1(SUCCEEDED(RegKey::SetValue(client_state_key_path,
+                                       kRegValueLastUpdateTimeSec,
+                                       now)));
+  }
+}
+
+void PersistSuccessfulUpdateCheck(const CString& client_state_key_path) {
+  CORE_LOG(L3, (_T("[app_registry_utils::PersistSuccessfulUpdateCheck][%s]"),
+                client_state_key_path));
+  const DWORD now = Time64ToInt32(GetCurrent100NSTime());
+  VERIFY1(SUCCEEDED(RegKey::SetValue(client_state_key_path,
+                                     kRegValueLastSuccessfulCheckSec,
+                                     now)));
+}
+
+void ClearUpdateAvailableStats(const CString& client_state_key_path) {
+  CORE_LOG(L3, (_T("[app_registry_utils::ClearUpdateAvailableStats][%s]"),
+                client_state_key_path));
+
+  RegKey state_key;
+  HRESULT hr = state_key.Open(client_state_key_path);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  VERIFY1(SUCCEEDED(state_key.DeleteValue(kRegValueUpdateAvailableCount)));
+  VERIFY1(SUCCEEDED(state_key.DeleteValue(kRegValueUpdateAvailableSince)));
+}
+
+HRESULT GetNumClients(bool is_machine, size_t* num_clients) {
+  ASSERT1(num_clients);
+  RegKey reg_key;
+  HKEY root_key = is_machine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+  HRESULT hr = reg_key.Open(root_key, GOOPDATE_REG_RELATIVE_CLIENTS, KEY_READ);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  DWORD num_subkeys(0);
+  LONG error = ::RegQueryInfoKey(reg_key.Key(), NULL, NULL, NULL, &num_subkeys,
+                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+  if (error != ERROR_SUCCESS) {
+    return HRESULT_FROM_WIN32(error);
+  }
+  *num_clients = num_subkeys;
+  return S_OK;
+}
+
+// Reads pv value from Clients key.
+void GetAppVersion(bool is_machine, const CString& app_id, CString* pv) {
+  ASSERT1(pv);
+  RegKey key;
+  CString key_name = GetAppClientsKey(is_machine, app_id);
+  HRESULT hr = key.Open(key_name, KEY_READ);
+  if (SUCCEEDED(hr)) {
+    key.GetValue(kRegValueProductVersion, pv);
+  }
+}
+
+// Reads the following values from the registry:
+//  ClientState key
+//    pv
+//    ap
+//    lang
+//    brand
+//    client
+//    iid
+//    experiment
+void GetClientStateData(bool is_machine,
+                        const CString& app_id,
+                        CString* pv,
+                        CString* ap,
+                        CString* lang,
+                        CString* brand_code,
+                        CString* client_id,
+                        CString* iid,
+                        CString* experiment_labels) {
+  RegKey key;
+
+  CString key_name = GetAppClientStateKey(is_machine, app_id);
+  HRESULT hr = key.Open(key_name, KEY_READ);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  if (pv) {
+    key.GetValue(kRegValueProductVersion, pv);
+  }
+  if (ap) {
+    key.GetValue(kRegValueAdditionalParams, ap);
+  }
+  if (lang) {
+    key.GetValue(kRegValueLanguage, lang);
+  }
+  if (brand_code) {
+    key.GetValue(kRegValueBrandCode, brand_code);
+  }
+  if (client_id) {
+    key.GetValue(kRegValueClientId, client_id);
+  }
+  if (iid) {
+    key.GetValue(kRegValueInstallationId, iid);
+  }
+  if (experiment_labels) {
+    key.GetValue(kRegValueExperimentLabels, experiment_labels);
+  }
+}
+
+HRESULT GetUninstalledApps(bool is_machine,
+                           std::vector<CString>* app_ids) {
+  ASSERT1(app_ids);
+
+  RegKey client_state_key;
+  HRESULT hr = client_state_key.Open(
+      ConfigManager::Instance()->registry_client_state(is_machine),
+      KEY_READ);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  int num_sub_keys = client_state_key.GetSubkeyCount();
+  for (int i = 0; i < num_sub_keys; ++i) {
+    CString app_id;
+    hr = client_state_key.GetSubkeyNameAt(i, &app_id);
+    if (FAILED(hr)) {
+      continue;
+    }
+
+    // If the app is not registered, treat it as uninstalled.
+    if (!RegKey::HasValue(
+        app_registry_utils::GetAppClientsKey(is_machine, app_id),
+        kRegValueProductVersion)) {
+      app_ids->push_back(app_id);
+    }
+  }
+
+  return S_OK;
+}
+
+HRESULT RemoveClientState(bool is_machine, const CString& app_guid) {
+  const CString state_key = GetAppClientStateKey(is_machine, app_guid);
+  HRESULT state_hr = RegKey::DeleteKey(state_key, true);
+  if (!is_machine) {
+    return state_hr;
+  }
+
+  const CString state_medium_key = GetAppClientStateMediumKey(is_machine,
+                                                              app_guid);
+  HRESULT state_medium_hr = RegKey::DeleteKey(state_medium_key, true);
+  return FAILED(state_hr) ? state_hr : state_medium_hr;
+}
+
+void RemoveClientStateForApps(bool is_machine,
+                              const std::vector<CString>& apps) {
+  std::vector<CString>::const_iterator it;
+  for (it = apps.begin(); it != apps.end(); ++it) {
+    RemoveClientState(is_machine, *it);
+  }
+}
+
+HRESULT GetExperimentLabels(bool is_machine, const CString& app_id,
+                            CString* labels_out) {
+  ASSERT1(!app_id.IsEmpty());
+  ASSERT1(labels_out);
+
+  const CString state_key = GetAppClientStateKey(is_machine, app_id);
+  if (!RegKey::HasValue(state_key, kRegValueExperimentLabels)) {
+    return S_OK;
+  }
+
+  return RegKey::GetValue(state_key, kRegValueExperimentLabels, labels_out);
+}
+
+HRESULT SetExperimentLabels(bool is_machine, const CString& app_id,
+                            const CString& new_labels) {
+  ASSERT1(!app_id.IsEmpty());
+  ASSERT1(ExperimentLabels::IsStringValidLabelSet(new_labels));
+
+  return RegKey::SetValue(GetAppClientStateKey(is_machine, app_id),
+                          kRegValueExperimentLabels,
+                          new_labels);
+}
+
+}  // namespace app_registry_utils
+
+}  // namespace omaha
diff --git a/common/app_registry_utils.h b/common/app_registry_utils.h
new file mode 100644
index 0000000..091d7f0
--- /dev/null
+++ b/common/app_registry_utils.h
@@ -0,0 +1,131 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_APP_REGISTRY_UTILS_H_
+#define OMAHA_COMMON_APP_REGISTRY_UTILS_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include <vector>
+#include "omaha/base/constants.h"
+
+// Functions that modify the application state in the registry. This file should
+// only be included by AppManager, which manages the persisting of all
+// application information, ApplicationUsageData for similar reasons, and
+// self-install code, which must modify these values directly in some cases.
+
+namespace omaha {
+
+namespace app_registry_utils {
+
+// Returns the application registration path for the specified app.
+CString GetAppClientsKey(bool is_machine, const CString& app_guid);
+
+// Returns the application state path for the specified app.
+CString GetAppClientStateKey(bool is_machine, const CString& app_guid);
+
+// Returns the medium integrity application state path for the specified app.
+CString GetAppClientStateMediumKey(bool is_machine, const CString& app_guid);
+
+// Returns whether the EULA is accepted for the app.
+bool IsAppEulaAccepted(bool is_machine,
+                       const CString& app_guid,
+                       bool require_explicit_acceptance);
+
+// Sets eulaaccepted=0 in the app's ClientState.
+HRESULT SetAppEulaNotAccepted(bool is_machine, const CString& app_guid);
+
+// Clears any eulaaccepted=0 values for the app.
+HRESULT ClearAppEulaNotAccepted(bool is_machine, const CString& app_guid);
+
+// Determines whether usage stats are enabled for a specific app.
+bool AreAppUsageStatsEnabled(bool is_machine, const CString& app_guid);
+
+// Configures Omaha's collection of usage stats and crash reports.
+HRESULT SetUsageStatsEnable(bool is_machine,
+                            const CString& app_guid,
+                            Tristate usage_stats_enable);
+
+// Writes branding information for Google Update in the registry if it does not
+// already exist. Otherwise, the information remains unchanged.
+// Writes a default Omaha-specific brand code if one is not specified in args.
+HRESULT SetGoogleUpdateBranding(const CString& client_state_key_path,
+                                const CString& brand_code,
+                                const CString& client_id);
+
+// Writes branding information for apps in the registry if it does not
+// already exist. Otherwise, the information remains unchanged.
+// Writes a default Omaha-specific brand code if one is not specified in args.
+HRESULT SetAppBranding(const CString& client_state_key_path,
+                       const CString& brand_code,
+                       const CString& client_id,
+                       const CString& referral_id);
+
+// Updates the application state after a successful install or update.
+void PersistSuccessfulInstall(const CString& client_state_key_path,
+                              bool is_update,
+                              bool is_offline);
+
+// Updates the application state after a successful update check event, which
+// is either a "noupdate" response or a successful online update.
+void PersistSuccessfulUpdateCheck(const CString& client_state_key_path);
+
+// Clears the stored information about update available events for the app.
+// Call when an update has succeeded.
+void ClearUpdateAvailableStats(const CString& client_state_key_path);
+
+// Returns the number of clients registered under the "Clients" sub key.
+// Does not guarantee a consistent state. Caller should use appropriate locks if
+// necessary.
+HRESULT GetNumClients(bool is_machine, size_t* num_clients);
+
+// Reads app version from Clients key.
+void GetAppVersion(bool is_machine, const CString& app_id, CString* pv);
+
+// Reads persistent data for an application. The parameters can be NULL to
+// indicate that value is not required.
+void GetClientStateData(bool is_machine,
+                        const CString& app_id,
+                        CString* pv,
+                        CString* ap,
+                        CString* lang,
+                        CString* brand_code,
+                        CString* client_id,
+                        CString* iid,
+                        CString* experiment_labels);
+
+// Reads all uninstalled apps from the registry.
+HRESULT GetUninstalledApps(bool is_machine, std::vector<CString>* app_ids);
+
+// Removes the client state for the given app.
+HRESULT RemoveClientState(bool is_machine, const CString& app_guid);
+
+// Removes the client state for the apps.
+void RemoveClientStateForApps(bool is_machine,
+                              const std::vector<CString>& apps);
+
+// Retrieves experiment labels for an app from the Registry.
+HRESULT GetExperimentLabels(bool is_machine, const CString& app_id,
+                            CString* labels_out);
+
+// Overwrites the experiment labels for an app in the Registry.
+HRESULT SetExperimentLabels(bool is_machine, const CString& app_id,
+                            const CString& new_labels);
+
+}  // namespace app_registry_utils
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_APP_REGISTRY_UTILS_H_
diff --git a/common/app_registry_utils_unittest.cc b/common/app_registry_utils_unittest.cc
new file mode 100644
index 0000000..e0e628d
--- /dev/null
+++ b/common/app_registry_utils_unittest.cc
@@ -0,0 +1,2451 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/reg_key.h"
+#include "omaha/common/app_registry_utils.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+#define APP_GUID _T("{B7BAF788-9D64-49c3-AFDC-B336AB12F332}")
+const TCHAR* const kAppGuid = APP_GUID;
+const TCHAR* const kAppMachineClientStatePath =
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\") APP_GUID;
+const TCHAR* const kAppUserClientStatePath =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\") APP_GUID;
+const TCHAR* const kAppMachineClientStateMediumPath =
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientStateMedium\\") APP_GUID;
+
+// This should never exist. This contant is only used to verify it is not used.
+const TCHAR* const kAppUserClientStateMediumPath =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientStateMedium\\") APP_GUID;
+
+const TCHAR* const kOmahaMachineClientsPath =
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\Clients\\") GOOPDATE_APP_ID;
+
+const TCHAR* const kOmahaUserClientsPath =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\Clients\\") GOOPDATE_APP_ID;
+
+const TCHAR* const kOmahaMachineClientStatePath =
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\") GOOPDATE_APP_ID;
+
+const TCHAR* const kOmahaUserClientStatePath =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\") GOOPDATE_APP_ID;
+
+}  // namespace
+
+namespace app_registry_utils {
+
+TEST(AppRegistryUtilsTest, GetAppClientsKey) {
+  const TCHAR kAppGuid[] = _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}");
+
+  EXPECT_STREQ(_T("HKCU\\Software\\") SHORT_COMPANY_NAME
+               _T("\\") PRODUCT_NAME _T("\\Clients\\")
+               _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"),
+               GetAppClientsKey(false, kAppGuid));
+  EXPECT_STREQ(_T("HKLM\\Software\\") SHORT_COMPANY_NAME
+               _T("\\") PRODUCT_NAME _T("\\Clients\\")
+               _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"),
+               GetAppClientsKey(true, kAppGuid));
+}
+
+TEST(AppRegistryUtilsTest, GetAppClientStateKey) {
+  const TCHAR kAppGuid[] = _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}");
+
+  EXPECT_STREQ(_T("HKCU\\Software\\") SHORT_COMPANY_NAME
+               _T("\\") PRODUCT_NAME _T("\\ClientState\\")
+               _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"),
+               GetAppClientStateKey(false, kAppGuid));
+  EXPECT_STREQ(_T("HKLM\\Software\\") SHORT_COMPANY_NAME
+               _T("\\") PRODUCT_NAME _T("\\ClientState\\")
+               _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"),
+               GetAppClientStateKey(true, kAppGuid));
+}
+
+// This is an invalid case and causes an assert. Always returns HKLM path.
+TEST(AppRegistryUtilsTest, GetAppClientStateMediumKey_User) {
+  const TCHAR kAppGuid[] = _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}");
+  ExpectAsserts expect_asserts;
+  EXPECT_STREQ(_T("HKLM\\Software\\") SHORT_COMPANY_NAME
+               _T("\\") PRODUCT_NAME _T("\\ClientStateMedium\\")
+               _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"),
+               GetAppClientStateMediumKey(false, kAppGuid));
+}
+
+TEST(AppRegistryUtilsTest, GetAppClientStateMediumKey_Machine) {
+  const TCHAR kAppGuid[] = _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}");
+  EXPECT_STREQ(_T("HKLM\\Software\\") SHORT_COMPANY_NAME
+               _T("\\") PRODUCT_NAME _T("\\ClientStateMedium\\")
+               _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"),
+               GetAppClientStateMediumKey(true, kAppGuid));
+}
+
+// This is an invalid case and causes an assert.
+TEST(AppRegistryUtilsTest, GetAppClientStateMediumKey_UserAndMachineAreSame) {
+  const TCHAR kAppGuid[] = _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}");
+  ExpectAsserts expect_asserts;
+  EXPECT_STREQ(GetAppClientStateMediumKey(true, kAppGuid),
+               GetAppClientStateMediumKey(false, kAppGuid));
+}
+
+class AppRegistryUtilsRegistryProtectedTest : public testing::Test {
+ protected:
+  AppRegistryUtilsRegistryProtectedTest()
+      : hive_override_key_name_(kRegistryHiveOverrideRoot) {
+  }
+
+  CString hive_override_key_name_;
+
+  virtual void SetUp() {
+    RegKey::DeleteKey(hive_override_key_name_, true);
+    OverrideRegistryHives(hive_override_key_name_);
+  }
+
+  virtual void TearDown() {
+    RestoreRegistryHives();
+    ASSERT_SUCCEEDED(RegKey::DeleteKey(hive_override_key_name_, true));
+  }
+};
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_NotExplicit_NoKey) {
+  EXPECT_TRUE(IsAppEulaAccepted(true, kAppGuid, false));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_NotExplicit_ClientStateExists) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
+  EXPECT_TRUE(IsAppEulaAccepted(true, kAppGuid, false));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_NotExplicit_ClientStateOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(IsAppEulaAccepted(true, kAppGuid, false));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_NotExplicit_ClientStateNegativeOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(-1)));
+  EXPECT_TRUE(IsAppEulaAccepted(true, kAppGuid, false));
+  EXPECT_EQ(-1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_NotExplicit_ClientStateZero_MediumNotExist) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_FALSE(IsAppEulaAccepted(true, kAppGuid, false));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_NotExplicit_ClientStateZero_MediumExists) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStateMediumPath));
+  EXPECT_FALSE(IsAppEulaAccepted(true, kAppGuid, false));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_NotExplicit_ClientStateZero_MediumOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(IsAppEulaAccepted(true, kAppGuid, false));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+    IsAppEulaAccepted_Machine_NotExplicit_ClientStateZero_MediumNegativeOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(-1)));
+  EXPECT_TRUE(IsAppEulaAccepted(true, kAppGuid, false));
+  EXPECT_EQ(-1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(-1, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+// Also tests that user values are not used.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_NotExplicit_ClientStateZero_MediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_FALSE(IsAppEulaAccepted(true, kAppGuid, false));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+// ClientStateMedium does not override ClientState.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_NotExplicit_ClientStateOne_MediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_TRUE(IsAppEulaAccepted(true, kAppGuid, false));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+// Implicitly accepted because of the absence of eualaccepted=0.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_NotExplicit_ClientStateNotExist_MediumOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(IsAppEulaAccepted(true, kAppGuid, false));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+// Implicitly accepted because of the absence of eualaccepted=0.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_NotExplicit_ClientStateNotExist_MediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_TRUE(IsAppEulaAccepted(true, kAppGuid, false));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_Explicit_NoKey) {
+  EXPECT_FALSE(IsAppEulaAccepted(true, kAppGuid, true));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_Explicit_ClientStateExists) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
+  EXPECT_FALSE(IsAppEulaAccepted(true, kAppGuid, true));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_Explicit_ClientStateOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(IsAppEulaAccepted(true, kAppGuid, true));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_Explicit_ClientStateNegativeOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(-1)));
+  EXPECT_TRUE(IsAppEulaAccepted(true, kAppGuid, true));
+  EXPECT_EQ(-1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_Explicit_ClientStateZero_MediumNotExist) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_FALSE(IsAppEulaAccepted(true, kAppGuid, true));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_Explicit_ClientStateZero_MediumExists) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStateMediumPath));
+  EXPECT_FALSE(IsAppEulaAccepted(true, kAppGuid, true));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_Explicit_ClientStateZero_MediumOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(IsAppEulaAccepted(true, kAppGuid, true));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_Explicit_ClientStateZero_MediumNegativeOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(-1)));
+  EXPECT_TRUE(IsAppEulaAccepted(true, kAppGuid, true));
+  EXPECT_EQ(-1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(-1, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+// Also tests that user values are not used.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_Explicit_ClientStateZero_MediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_FALSE(IsAppEulaAccepted(true, kAppGuid, true));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+// ClientStateMedium does not override ClientState.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_Explicit_ClientStateOne_MediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_TRUE(IsAppEulaAccepted(true, kAppGuid, true));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_Explicit_ClientStateNotExist_MediumOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(IsAppEulaAccepted(true, kAppGuid, true));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_Machine_Explicit_ClientStateNotExist_MediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_FALSE(IsAppEulaAccepted(true, kAppGuid, true));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+// ClientStateMedium is not supported for user apps.
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_User_NotExplicit_NoKey) {
+  EXPECT_TRUE(IsAppEulaAccepted(false, kAppGuid, false));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_User_NotExplicit_ClientStateExists) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStatePath));
+  EXPECT_TRUE(IsAppEulaAccepted(false, kAppGuid, false));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_User_NotExplicit_ClientStateOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(IsAppEulaAccepted(false, kAppGuid, false));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_User_NotExplicit_ClientStateNegativeOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(-1)));
+  EXPECT_TRUE(IsAppEulaAccepted(false, kAppGuid, false));
+  EXPECT_EQ(-1, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_User_NotExplicit_ClientStateZero_MediumNotExist) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_FALSE(IsAppEulaAccepted(false, kAppGuid, false));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_User_NotExplicit_ClientStateZero_MediumExists) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStateMediumPath));
+  EXPECT_FALSE(IsAppEulaAccepted(false, kAppGuid, false));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_User_NotExplicit_ClientStateZero_MediumOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_FALSE(IsAppEulaAccepted(false, kAppGuid, false));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_User_NotExplicit_ClientStateZero_MediumNegativeOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(-1)));
+  EXPECT_FALSE(IsAppEulaAccepted(false, kAppGuid, false));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(-1, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+// Also tests that machine values are not used.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_User_NotExplicit_ClientStateZero_MediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_FALSE(IsAppEulaAccepted(false, kAppGuid, false));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+// ClientStateMedium is not used.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_User_NotExplicit_ClientStateOne_MediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_TRUE(IsAppEulaAccepted(false, kAppGuid, false));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+// Implicitly accepted because of the absence of eualaccepted=0.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_User_NotExplicit_ClientStateNotExist_MediumOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(IsAppEulaAccepted(false, kAppGuid, false));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+// Implicitly accepted because of the absence of eualaccepted=0.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_User_NotExplicit_ClientStateNotExist_MediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_TRUE(IsAppEulaAccepted(false, kAppGuid, false));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_User_Explicit_NoKey) {
+  EXPECT_FALSE(IsAppEulaAccepted(false, kAppGuid, true));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_User_Explicit_ClientStateExists) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStatePath));
+  EXPECT_FALSE(IsAppEulaAccepted(false, kAppGuid, true));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_User_Explicit_ClientStateOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(IsAppEulaAccepted(false, kAppGuid, true));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_User_Explicit_ClientStateNotExist_MediumOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_FALSE(IsAppEulaAccepted(false, kAppGuid, true));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       IsAppEulaAccepted_User_Explicit_ClientStateNotExist_MediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_FALSE(IsAppEulaAccepted(false, kAppGuid, true));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppEulaNotAccepted_Machine_NoKey) {
+  EXPECT_SUCCEEDED(SetAppEulaNotAccepted(true, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppEulaNotAccepted_Machine_ClientStateExists) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
+  EXPECT_SUCCEEDED(SetAppEulaNotAccepted(true, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppEulaNotAccepted_Machine_ClientStateOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(SetAppEulaNotAccepted(true, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppEulaNotAccepted_Machine_ClientStateZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(SetAppEulaNotAccepted(true, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppEulaNotAccepted_Machine_ClientStateZero_ClientStateMediumOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(SetAppEulaNotAccepted(true, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+// Also tests that user values are not affected.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppEulaNotAccepted_Machine_ClientStateZero_ClientStateMediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(SetAppEulaNotAccepted(true, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppEulaNotAccepted_User_NoKey) {
+  EXPECT_SUCCEEDED(SetAppEulaNotAccepted(false, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppEulaNotAccepted_User_ClientStateExists) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStatePath));
+  EXPECT_SUCCEEDED(SetAppEulaNotAccepted(false, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppEulaNotAccepted_User_ClientStateOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(SetAppEulaNotAccepted(false, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppEulaNotAccepted_User_ClientStateZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(SetAppEulaNotAccepted(false, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppEulaNotAccepted_User_ClientStateZero_ClientStateMediumOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(SetAppEulaNotAccepted(false, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+// Also tests that machine values are not affected.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppEulaNotAccepted_User_ClientStateZero_ClientStateMediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(SetAppEulaNotAccepted(false, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       ClearAppEulaNotAccepted_Machine_NoKey) {
+  EXPECT_SUCCEEDED(ClearAppEulaNotAccepted(true, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       ClearAppEulaNotAccepted_Machine_ClientStateExists) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
+  EXPECT_SUCCEEDED(ClearAppEulaNotAccepted(true, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       ClearAppEulaNotAccepted_Machine_ClientStateOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(ClearAppEulaNotAccepted(true, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       ClearAppEulaNotAccepted_Machine_ClientStateZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(ClearAppEulaNotAccepted(true, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       ClearAppEulaNotAccepted_Machine_ClientStateZero_ClientStateMediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(ClearAppEulaNotAccepted(true, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+}
+
+// Also tests that user values are not affected.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       ClearAppEulaNotAccepted_Machine_ClientStateNone_ClientStateMediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(ClearAppEulaNotAccepted(true, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
+  EXPECT_EQ(0,
+            GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(0,
+            GetDwordValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       ClearAppEulaNotAccepted_User_NoKey) {
+  EXPECT_SUCCEEDED(ClearAppEulaNotAccepted(false, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       ClearAppEulaNotAccepted_User_ClientStateExists) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStatePath));
+  EXPECT_SUCCEEDED(ClearAppEulaNotAccepted(false, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       ClearAppEulaNotAccepted_User_ClientStateOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(ClearAppEulaNotAccepted(false, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       ClearAppEulaNotAccepted_User_ClientStateZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(ClearAppEulaNotAccepted(false, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       ClearAppEulaNotAccepted_User_ClientStateZero_ClientStateMediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(ClearAppEulaNotAccepted(false, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(0,
+            GetDwordValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+}
+
+// Also tests that machine values are not affected.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       ClearAppEulaNotAccepted_User_ClientStateNone_ClientStateMediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(ClearAppEulaNotAccepted(false, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(0,
+            GetDwordValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
+  EXPECT_EQ(0,
+            GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("eulaaccepted")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_Machine_NoKey) {
+  EXPECT_FALSE(AreAppUsageStatsEnabled(true, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("usagestats")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_Machine_ClientStateExists) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(true, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("usagestats")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_Machine_ClientStateOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(AreAppUsageStatsEnabled(true, kAppGuid));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_Machine_ClientStateNegativeOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(-1)));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(true, kAppGuid));
+  EXPECT_EQ(-1, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_Machine_ClientStateZero_MediumNotExist) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(true, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_Machine_ClientStateZero_MediumExists) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStateMediumPath));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(true, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
+}
+
+// ClientStateMedium overrides ClientState.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_Machine_ClientStateZero_MediumOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(AreAppUsageStatsEnabled(true, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+    AreAppUsageStatsEnabled_Machine_ClientStateZero_MediumNegativeOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(-1)));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(true, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
+  EXPECT_EQ(-1, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_Machine_ClientStateZero_MediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(true, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("usagestats")));
+}
+
+// ClientStateMedium overrides ClientState.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_Machine_ClientStateOne_MediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(true, kAppGuid));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_Machine_ClientStateNotExist_MediumOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(AreAppUsageStatsEnabled(true, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("usagestats")));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_Machine_ClientStateNotExist_MediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(true, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("usagestats")));
+  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("usagestats")));
+}
+
+// User does not affect machine.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_Machine_UserOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(true, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStatePath, _T("usagestats")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("usagestats")));
+}
+
+// ClientStateMedium is not supported for user apps.
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_User_NoKey) {
+  EXPECT_FALSE(AreAppUsageStatsEnabled(false, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("usagestats")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_User_ClientStateExists) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStatePath));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(false, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("usagestats")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_User_ClientStateOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(AreAppUsageStatsEnabled(false, kAppGuid));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_User_ClientStateNegativeOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(-1)));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(false, kAppGuid));
+  EXPECT_EQ(-1, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_User_ClientStateZero_MediumNotExist) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(false, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_User_ClientStateZero_MediumExists) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStateMediumPath));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(false, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("usagestats")));
+}
+
+// ClientStateMedium is not used.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_User_ClientStateZero_MediumOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(false, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_User_ClientStateZero_MediumNegativeOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(-1)));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(false, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
+  EXPECT_EQ(-1, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_User_ClientStateZero_MediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(false, kAppGuid));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("usagestats")));
+}
+
+// ClientStateMedium is not used.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_User_ClientStateOne_MediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_TRUE(AreAppUsageStatsEnabled(false, kAppGuid));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_User_ClientStateNotExist_MediumOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(false, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("usagestats")));
+  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_User_ClientStateNotExist_MediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(false, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("usagestats")));
+  EXPECT_EQ(0, GetDwordValue(kAppUserClientStateMediumPath,
+                             _T("usagestats")));
+}
+
+// Machine does not affect user.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       AreAppUsageStatsEnabled_User_MachineOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_FALSE(AreAppUsageStatsEnabled(false, kAppGuid));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStatePath, _T("usagestats")));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppUserClientStateMediumPath, _T("usagestats")));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
+  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
+                             _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest, SetUsageStatsEnable_Machine_Off) {
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(true, kAppGuid, TRISTATE_FALSE));
+
+  ASSERT_TRUE(RegKey::HasKey(kAppMachineClientStatePath));
+  ASSERT_TRUE(RegKey::HasValue(kAppMachineClientStatePath,
+                               _T("usagestats")));
+  ASSERT_FALSE(RegKey::HasKey(kAppUserClientStatePath));
+
+  DWORD enable_value = 1;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    &enable_value));
+  EXPECT_EQ(0, enable_value);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest, SetUsageStatsEnable_User_Off) {
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(false, kAppGuid, TRISTATE_FALSE));
+
+  ASSERT_TRUE(RegKey::HasKey(kAppUserClientStatePath));
+  ASSERT_TRUE(RegKey::HasValue(kAppUserClientStatePath,
+                               _T("usagestats")));
+  ASSERT_FALSE(RegKey::HasKey(kAppMachineClientStatePath));
+
+  DWORD enable_value = 1;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppUserClientStatePath,
+                                    _T("usagestats"),
+                                    &enable_value));
+  EXPECT_EQ(0, enable_value);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest, SetUsageStatsEnable_Machine_On) {
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(true, kAppGuid, TRISTATE_TRUE));
+
+  ASSERT_TRUE(RegKey::HasKey(kAppMachineClientStatePath));
+  ASSERT_TRUE(RegKey::HasValue(kAppMachineClientStatePath,
+                               _T("usagestats")));
+  ASSERT_FALSE(RegKey::HasKey(kAppUserClientStatePath));
+
+  DWORD enable_value = 0;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    &enable_value));
+  EXPECT_EQ(1, enable_value);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest, SetUsageStatsEnable_User_On) {
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(false, kAppGuid, TRISTATE_TRUE));
+
+  ASSERT_TRUE(RegKey::HasKey(kAppUserClientStatePath));
+  ASSERT_TRUE(RegKey::HasValue(kAppUserClientStatePath,
+                               _T("usagestats")));
+  ASSERT_FALSE(RegKey::HasKey(kAppMachineClientStatePath));
+
+  DWORD enable_value = 0;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppUserClientStatePath,
+                                    _T("usagestats"),
+                                    &enable_value));
+  EXPECT_EQ(1, enable_value);
+}
+
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetUsageStatsEnable_Machine_None) {
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(true, kAppGuid, TRISTATE_NONE));
+  ASSERT_FALSE(RegKey::HasKey(MACHINE_REG_UPDATE));
+  ASSERT_FALSE(RegKey::HasKey(USER_REG_UPDATE));
+
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(false, kAppGuid, TRISTATE_NONE));
+  ASSERT_FALSE(RegKey::HasKey(USER_REG_UPDATE));
+  ASSERT_FALSE(RegKey::HasKey(MACHINE_REG_UPDATE));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetUsageStatsEnable_Machine_Overwrite) {
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(true, kAppGuid, TRISTATE_FALSE));
+
+  ASSERT_TRUE(RegKey::HasKey(kAppMachineClientStatePath));
+  ASSERT_TRUE(RegKey::HasValue(kAppMachineClientStatePath,
+                               _T("usagestats")));
+  ASSERT_FALSE(RegKey::HasKey(kAppUserClientStatePath));
+
+  DWORD enable_value = 1;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    &enable_value));
+  EXPECT_EQ(0, enable_value);
+
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(true, kAppGuid, TRISTATE_TRUE));
+
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    &enable_value));
+  EXPECT_EQ(1, enable_value);
+
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(true, kAppGuid, TRISTATE_FALSE));
+
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    &enable_value));
+  EXPECT_EQ(0, enable_value);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetUsageStatsEnable_Machine_NoneDoesNotOverwrite) {
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(true, kAppGuid, TRISTATE_FALSE));
+
+  ASSERT_TRUE(RegKey::HasKey(kAppMachineClientStatePath));
+  ASSERT_TRUE(RegKey::HasValue(kAppMachineClientStatePath,
+                               _T("usagestats")));
+  ASSERT_FALSE(RegKey::HasKey(kAppUserClientStatePath));
+
+  DWORD enable_value = 1;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    &enable_value));
+  EXPECT_EQ(0, enable_value);
+
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(true, kAppGuid, TRISTATE_NONE));
+
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    &enable_value));
+  EXPECT_EQ(0, enable_value);
+
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(true, kAppGuid, TRISTATE_TRUE));
+
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    &enable_value));
+  EXPECT_EQ(1, enable_value);
+
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(true, kAppGuid, TRISTATE_NONE));
+
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    _T("usagestats"),
+                                    &enable_value));
+  EXPECT_EQ(1, enable_value);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetUsageStatsEnable_Machine_ClientStateMediumCleared) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(true, kAppGuid, TRISTATE_TRUE));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(true, kAppGuid, TRISTATE_FALSE));
+  EXPECT_FALSE(
+      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetUsageStatsEnable_Machine_NoneDoesNotClearClientStateMedium) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(true, kAppGuid, TRISTATE_NONE));
+
+  DWORD enable_value = 0;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStateMediumPath,
+                                    _T("usagestats"),
+                                    &enable_value));
+  EXPECT_EQ(1, enable_value);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetUsageStatsEnable_User_ClientStateMediumNotCleared) {
+  // User and machine values should not be cleared.
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+
+  // True does not clear them.
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(false, kAppGuid, TRISTATE_TRUE));
+  DWORD enable_value = 1;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppUserClientStateMediumPath,
+                                    _T("usagestats"),
+                                    &enable_value));
+  EXPECT_EQ(0, enable_value);
+  enable_value = 1;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStateMediumPath,
+                                    _T("usagestats"),
+                                    &enable_value));
+  EXPECT_EQ(0, enable_value);
+
+  // False does not clear them.
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(SetUsageStatsEnable(false, kAppGuid, TRISTATE_FALSE));
+  enable_value = 0;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppUserClientStateMediumPath,
+                                    _T("usagestats"),
+                                    &enable_value));
+  EXPECT_EQ(1, enable_value);
+  enable_value = 0;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStateMediumPath,
+                                    _T("usagestats"),
+                                    &enable_value));
+  EXPECT_EQ(1, enable_value);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest, GetNumClients) {
+  size_t num_clients(0);
+
+  // Fails when no "Clients" key.
+  EXPECT_HRESULT_FAILED(GetNumClients(true, &num_clients));
+  EXPECT_HRESULT_FAILED(GetNumClients(false, &num_clients));
+
+  // Tests no subkeys.
+  const TCHAR* keys_to_create[] = { MACHINE_REG_CLIENTS, USER_REG_CLIENTS };
+  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKeys(keys_to_create,
+                                              arraysize(keys_to_create)));
+  EXPECT_HRESULT_SUCCEEDED(GetNumClients(true, &num_clients));
+  EXPECT_EQ(0, num_clients);
+  EXPECT_HRESULT_SUCCEEDED(GetNumClients(false, &num_clients));
+  EXPECT_EQ(0, num_clients);
+
+  // Subkeys should be counted. Values should not be counted.
+  RegKey machine_key;
+  EXPECT_HRESULT_SUCCEEDED(machine_key.Open(HKEY_LOCAL_MACHINE,
+                                            GOOPDATE_REG_RELATIVE_CLIENTS));
+  EXPECT_HRESULT_SUCCEEDED(machine_key.SetValue(_T("name"), _T("value")));
+  EXPECT_HRESULT_SUCCEEDED(GetNumClients(true, &num_clients));
+  EXPECT_EQ(0, num_clients);
+
+  const TCHAR* app_id = _T("{AA5523E3-40C0-4b85-B074-4BBA09559CCD}");
+  EXPECT_HRESULT_SUCCEEDED(machine_key.Create(machine_key.Key(), app_id));
+  EXPECT_HRESULT_SUCCEEDED(GetNumClients(true, &num_clients));
+  EXPECT_EQ(1, num_clients);
+
+  // Tests user scenario.
+  RegKey user_key;
+  EXPECT_HRESULT_SUCCEEDED(user_key.Open(HKEY_CURRENT_USER,
+                                         GOOPDATE_REG_RELATIVE_CLIENTS));
+  EXPECT_HRESULT_SUCCEEDED(user_key.SetValue(_T("name"), _T("value")));
+  EXPECT_HRESULT_SUCCEEDED(GetNumClients(false, &num_clients));
+  EXPECT_EQ(0, num_clients);
+
+  EXPECT_HRESULT_SUCCEEDED(user_key.Create(user_key.Key(), app_id));
+  EXPECT_HRESULT_SUCCEEDED(GetNumClients(false, &num_clients));
+  EXPECT_EQ(1, num_clients);
+}
+
+// This test verifies that InstallTime is created afresh for Omaha if it does
+// not exist, and even if the brand code is already set.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetGoogleUpdateBranding_BrandAlreadyExistsAllEmpty) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    _T("EFGH")));
+
+  EXPECT_SUCCEEDED(SetGoogleUpdateBranding(kAppMachineClientStatePath,
+                                           _T(""),
+                                           _T("")));
+
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("EFGH"), value);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueClientId,
+                             &value));
+  DWORD install_time = 0;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueInstallTimeSec,
+                                    &install_time));
+  EXPECT_GE(now, install_time);
+  EXPECT_GE(static_cast<uint32>(200), now - install_time);
+}
+
+// This test verifies that InstallTime remains unchanged for Omaha if it already
+// exists and the brand code is already set.
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetGoogleUpdateBranding_AllAlreadyExistAllEmpty) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    _T("EFGH")));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    _T("existing_partner")));
+  const DWORD kInstallTime = 1234567890;
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueInstallTimeSec,
+                                    kInstallTime));
+
+  EXPECT_SUCCEEDED(SetGoogleUpdateBranding(kAppMachineClientStatePath,
+                                           _T(""),
+                                           _T("")));
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("EFGH"), value);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    &value));
+  EXPECT_STREQ(_T("existing_partner"), value);
+  EXPECT_EQ(kInstallTime,
+            GetDwordValue(kAppMachineClientStatePath, kRegValueInstallTimeSec));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest, SetAppBranding_KeyDoesNotExist) {
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T("ABCD"),
+                                  _T("some_partner"),
+                                  _T("referrer")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest, SetAppBranding_AllEmpty) {
+  ASSERT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T(""),
+                                  _T(""),
+                                  _T("")));
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("GGLS"), value);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueClientId,
+                             &value));
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueReferralId,
+                             &value));
+  DWORD install_time = 0;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueInstallTimeSec,
+                                    &install_time));
+  EXPECT_GE(now, install_time);
+  EXPECT_GE(static_cast<uint32>(200), now - install_time);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest, SetAppBranding_BrandCodeOnly) {
+  ASSERT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T("ABCD"),
+                                  _T(""),
+                                  _T("")));
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("ABCD"), value);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueClientId,
+                             &value));
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueReferralId,
+                             &value));
+  DWORD install_time = 0;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueInstallTimeSec,
+                                    &install_time));
+  EXPECT_GE(now, install_time);
+  EXPECT_GE(static_cast<uint32>(200), now - install_time);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest, SetAppBranding_BrandCodeTooLong) {
+  EXPECT_EQ(E_INVALIDARG, SetAppBranding(kAppMachineClientStatePath,
+                                         _T("CHMGon.href)}"),
+                                         _T(""),
+                                         _T("")));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest, SetAppBranding_ClientIdOnly) {
+  ASSERT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T(""),
+                                  _T("some_partner"),
+                                  _T("")));
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("GGLS"), value);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    &value));
+  EXPECT_STREQ(_T("some_partner"), value);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueReferralId,
+                             &value));
+  DWORD install_time = 0;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueInstallTimeSec,
+                                    &install_time));
+  EXPECT_GE(now, install_time);
+  EXPECT_GE(static_cast<uint32>(200), now - install_time);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest, SetAppBranding_AllValid) {
+  ASSERT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T("ABCD"),
+                                  _T("some_partner"),
+                                  _T("referrer")));
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("ABCD"), value);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    &value));
+  EXPECT_STREQ(_T("some_partner"), value);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueReferralId,
+                                    &value));
+  EXPECT_STREQ(_T("referrer"), value);
+  DWORD install_time(0);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueInstallTimeSec,
+                                    &install_time));
+  EXPECT_GE(now, install_time);
+  EXPECT_GE(static_cast<uint32>(200), now - install_time);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppBranding_BrandAlreadyExistsAllEmpty) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    _T("EFGH")));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T(""),
+                                  _T(""),
+                                  _T("")));
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("EFGH"), value);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueClientId,
+                             &value));
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueReferralId,
+                             &value));
+  DWORD dword_value(0);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueInstallTimeSec,
+                             &dword_value));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppBranding_BrandAlreadyExistsBrandCodeOnly) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    _T("EFGH")));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T("ABCD"),
+                                  _T(""),
+                                  _T("")));
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("EFGH"), value);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueClientId,
+                             &value));
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueReferralId,
+                             &value));
+  DWORD dword_value(0);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueInstallTimeSec,
+                             &dword_value));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppBranding_ExistingBrandTooLong) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    _T("CHMG4CUTNt")));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T("ABCD"),
+                                  _T(""),
+                                  _T("")));
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("CHMG"), value);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueClientId,
+                             &value));
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueReferralId,
+                             &value));
+  DWORD dword_value(0);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueInstallTimeSec,
+                             &dword_value));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppBranding_BrandAlreadyExistsCliendIdOnly) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    _T("EFGH")));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T(""),
+                                  _T("some_partner"),
+                                  _T("")));
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("EFGH"), value);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueClientId,
+                             &value));
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueReferralId,
+                             &value));
+  DWORD dword_value(0);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueInstallTimeSec,
+                             &dword_value));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppBranding_BrandAlreadyExistsBothValid) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    _T("EFGH")));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T("ABCD"),
+                                  _T("some_partner"),
+                                  _T("")));
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("EFGH"), value);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueClientId,
+                             &value));
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueReferralId,
+                             &value));
+  DWORD dword_value(0);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueInstallTimeSec,
+                             &dword_value));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppBranding_ClientIdAlreadyExistsAllEmtpy) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    _T("existing_partner")));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T(""),
+                                  _T(""),
+                                  _T("")));
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("GGLS"), value);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    &value));
+  EXPECT_STREQ(_T("existing_partner"), value);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueReferralId,
+                             &value));
+  DWORD install_time = 0;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueInstallTimeSec,
+                                    &install_time));
+  EXPECT_GE(now, install_time);
+  EXPECT_GE(static_cast<uint32>(200), now - install_time);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppBranding_ClientIdAlreadyExistsBrandCodeOnly) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    _T("existing_partner")));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T("ABCE"),
+                                  _T(""),
+                                  _T("")));
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("ABCE"), value);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    &value));
+  EXPECT_STREQ(_T("existing_partner"), value);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueReferralId,
+                             &value));
+  DWORD install_time = 0;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueInstallTimeSec,
+                                    &install_time));
+  EXPECT_GE(now, install_time);
+  EXPECT_GE(static_cast<uint32>(200), now - install_time);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppBranding_ClientIdAlreadyExistsCliendIdOnly) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    _T("existing_partner")));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T(""),
+                                  _T("some_partner"),
+                                  _T("")));
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("GGLS"), value);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    &value));
+  EXPECT_STREQ(_T("some_partner"), value);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueReferralId,
+                             &value));
+  DWORD install_time = 0;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueInstallTimeSec,
+                                    &install_time));
+  EXPECT_GE(now, install_time);
+  EXPECT_GE(static_cast<uint32>(200), now - install_time);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppBranding_ClientIdAlreadyExistsBothValid) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    _T("existing_partner")));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T("ABCD"),
+                                  _T("some_partner"),
+                                  _T("")));
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("ABCD"), value);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    &value));
+  EXPECT_STREQ(_T("some_partner"), value);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueReferralId,
+                             &value));
+  DWORD install_time = 0;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueInstallTimeSec,
+                                    &install_time));
+  EXPECT_GE(now, install_time);
+  EXPECT_GE(static_cast<uint32>(200), now - install_time);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppBranding_AllAlreadyExistAllEmpty) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    _T("EFGH")));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    _T("existing_partner")));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueReferralId,
+                                    __T("existingreferrerid")));
+  const DWORD kInstallTime = 1234567890;
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueInstallTimeSec,
+                                    kInstallTime));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T(""),
+                                  _T(""),
+                                  _T("")));
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("EFGH"), value);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    &value));
+  EXPECT_STREQ(_T("existing_partner"), value);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueReferralId,
+                                    &value));
+  EXPECT_STREQ(__T("existingreferrerid"), value);
+  EXPECT_EQ(kInstallTime,
+            GetDwordValue(kAppMachineClientStatePath, kRegValueInstallTimeSec));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppBranding_AllAlreadyExistBrandCodeOnly) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    _T("EFGH")));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    _T("existing_partner")));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueReferralId,
+                                    __T("existingreferrerid")));
+  const DWORD kInstallTime = 1234567890;
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueInstallTimeSec,
+                                    kInstallTime));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T("ABCD"),
+                                  _T(""),
+                                  _T("")));
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("EFGH"), value);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    &value));
+  EXPECT_STREQ(_T("existing_partner"), value);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueReferralId,
+                                    &value));
+  EXPECT_STREQ(__T("existingreferrerid"), value);
+  EXPECT_EQ(kInstallTime,
+            GetDwordValue(kAppMachineClientStatePath, kRegValueInstallTimeSec));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppBranding_BothAlreadyExistCliendIdOnly) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    _T("EFGH")));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    _T("existing_partner")));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueReferralId,
+                                    __T("existingreferrerid")));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T(""),
+                                  _T("some_partner"),
+                                  _T("")));
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("EFGH"), value);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    &value));
+  EXPECT_STREQ(_T("existing_partner"), value);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueReferralId,
+                                    &value));
+  EXPECT_STREQ(__T("existingreferrerid"), value);
+  DWORD dword_value(0);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueInstallTimeSec,
+                             &dword_value));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppBranding_BothAlreadyExistBothValid) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    _T("EFGH")));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    _T("existing_partner")));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueReferralId,
+                                    _T("existingreferrerid")));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T("ABCD"),
+                                  _T("some_partner"),
+                                  _T("")));
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("EFGH"), value);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueClientId,
+                                    &value));
+  EXPECT_STREQ(_T("existing_partner"), value);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueReferralId,
+                                    &value));
+  EXPECT_STREQ(_T("existingreferrerid"), value);
+  DWORD dword_value(0);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueInstallTimeSec,
+                             &dword_value));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       SetAppBranding_InstallTimeAlreadyExistsBrandCodeOnly) {
+  const DWORD kExistingInstallTime = 1234567890;
+  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                    kRegValueInstallTimeSec,
+                                    kExistingInstallTime));
+
+  EXPECT_SUCCEEDED(SetAppBranding(kAppMachineClientStatePath,
+                                  _T("ABCE"),
+                                  _T(""),
+                                  _T("")));
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueBrandCode,
+                                    &value));
+  EXPECT_STREQ(_T("ABCE"), value);
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueClientId,
+                             &value));
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            RegKey::GetValue(kAppMachineClientStatePath,
+                             kRegValueReferralId,
+                             &value));
+  DWORD install_time = 0;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
+                                    kRegValueInstallTimeSec,
+                                    &install_time));
+  EXPECT_NE(kExistingInstallTime, install_time);
+  EXPECT_GE(now, install_time);
+  EXPECT_GE(static_cast<uint32>(200), now - install_time);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest, GetAppVersion_User) {
+  const CString expected_pv = _T("1.0");
+
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientsPath,
+                                            kRegValueProductVersion,
+                                            expected_pv));
+
+  CString actual_pv;
+  GetAppVersion(false, kGoogleUpdateAppId, &actual_pv);
+  EXPECT_STREQ(expected_pv, actual_pv);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest, GetAppVersion_Machine) {
+  const CString expected_pv = _T("1.0");
+
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaMachineClientsPath,
+                                            kRegValueProductVersion,
+                                            expected_pv));
+
+  CString actual_pv;
+  GetAppVersion(true, kGoogleUpdateAppId, &actual_pv);
+  EXPECT_STREQ(expected_pv, actual_pv);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       GetClientStateData_User) {
+  const CString expected_pv           = _T("1.0");
+  const CString expected_ap           = _T("additional parameters");
+  const CString expected_lang         = _T("some lang");
+  const CString expected_brand_code   = _T("some brand");
+  const CString expected_client_id    = _T("some client id");
+  const CString expected_iid          =
+      _T("{7C0B6E56-B24B-436b-A960-A6EA201E886F}");
+  const CString expected_experiment_label =
+      _T("some_experiment=a|Fri, 14 Aug 2015 16:13:03 GMT");
+
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                            kRegValueProductVersion,
+                                            expected_pv));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                            kRegValueAdditionalParams,
+                                            expected_ap));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                            kRegValueLanguage,
+                                            expected_lang));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                            kRegValueBrandCode,
+                                            expected_brand_code));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                            kRegValueClientId,
+                                            expected_client_id));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                            kRegValueInstallationId,
+                                            expected_iid));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                            kRegValueExperimentLabels,
+                                            expected_experiment_label));
+
+  CString actual_pv, actual_ap, actual_lang, actual_brand_code,
+      actual_client_id, actual_experiment_label, actual_iid;
+
+  GetClientStateData(false,
+                     kGoogleUpdateAppId,
+                     &actual_pv,
+                     &actual_ap,
+                     &actual_lang,
+                     &actual_brand_code,
+                     &actual_client_id,
+                     &actual_iid,
+                     &actual_experiment_label);
+
+  EXPECT_STREQ(expected_pv, actual_pv);
+  EXPECT_STREQ(expected_ap, actual_ap);
+  EXPECT_STREQ(expected_lang, actual_lang);
+  EXPECT_STREQ(expected_brand_code, actual_brand_code);
+  EXPECT_STREQ(expected_client_id, actual_client_id);
+  EXPECT_STREQ(expected_iid, actual_iid);
+  EXPECT_STREQ(expected_experiment_label, actual_experiment_label);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest,
+       GetGoogleUpdatePersistentData_Machine) {
+  const CString expected_pv           = _T("1.0");
+  const CString expected_ap           = _T("additional parameters");
+  const CString expected_lang         = _T("some lang");
+  const CString expected_brand_code   = _T("some brand");
+  const CString expected_client_id    = _T("some client id");
+  const CString expected_iid          =
+      _T("{7C0B6E56-B24B-436b-A960-A6EA201E886F}");
+  const CString expected_experiment_label =
+      _T("some_experiment=a|Fri, 14 Aug 2015 16:13:03 GMT");
+
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaMachineClientStatePath,
+                                            kRegValueProductVersion,
+                                            expected_pv));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaMachineClientStatePath,
+                                            kRegValueAdditionalParams,
+                                            expected_ap));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaMachineClientStatePath,
+                                            kRegValueLanguage,
+                                            expected_lang));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaMachineClientStatePath,
+                                            kRegValueBrandCode,
+                                            expected_brand_code));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaMachineClientStatePath,
+                                            kRegValueClientId,
+                                            expected_client_id));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaMachineClientStatePath,
+                                            kRegValueInstallationId,
+                                            expected_iid));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaMachineClientStatePath,
+                                            kRegValueExperimentLabels,
+                                            expected_experiment_label));
+
+  CString actual_pv, actual_ap, actual_lang, actual_brand_code,
+      actual_client_id, actual_experiment_label, actual_iid;
+
+  GetClientStateData(true,
+                     kGoogleUpdateAppId,
+                     &actual_pv,
+                     &actual_ap,
+                     &actual_lang,
+                     &actual_brand_code,
+                     &actual_client_id,
+                     &actual_iid,
+                     &actual_experiment_label);
+
+  EXPECT_STREQ(expected_pv, actual_pv);
+  EXPECT_STREQ(expected_ap, actual_ap);
+  EXPECT_STREQ(expected_lang, actual_lang);
+  EXPECT_STREQ(expected_brand_code, actual_brand_code);
+  EXPECT_STREQ(expected_client_id, actual_client_id);
+  EXPECT_STREQ(expected_iid, actual_iid);
+  EXPECT_STREQ(expected_experiment_label, actual_experiment_label);
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest, RemoveClientState_Machine) {
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                            kRegValueProductVersion,
+                                            _T("1.1")));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
+                                            kRegValueProductVersion,
+                                            _T("1.1")));
+  RemoveClientState(true, GOOPDATE_APP_ID);
+  EXPECT_TRUE(RegKey::HasValue(kAppMachineClientStatePath,
+                               kRegValueProductVersion));
+  EXPECT_TRUE(RegKey::HasValue(kAppMachineClientStateMediumPath,
+                               kRegValueProductVersion));
+  EXPECT_HRESULT_SUCCEEDED(RemoveClientState(true, kAppGuid));
+  EXPECT_FALSE(RegKey::HasValue(kAppMachineClientStatePath,
+                                kRegValueProductVersion));
+  EXPECT_FALSE(RegKey::HasValue(kAppMachineClientStateMediumPath,
+                                kRegValueProductVersion));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest, RemoveClientState_User) {
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                            kRegValueProductVersion,
+                                            _T("1.1")));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
+                                            kRegValueProductVersion,
+                                            _T("1.1")));
+  RemoveClientState(false, GOOPDATE_APP_ID);
+  EXPECT_TRUE(RegKey::HasValue(kAppUserClientStatePath,
+                               kRegValueProductVersion));
+  EXPECT_HRESULT_SUCCEEDED(RemoveClientState(false, kAppGuid));
+  EXPECT_FALSE(RegKey::HasValue(kAppUserClientStatePath,
+                                kRegValueProductVersion));
+
+  // For user case, StateMedium key is not deleted.
+  EXPECT_TRUE(RegKey::HasValue(kAppUserClientStateMediumPath,
+                               kRegValueProductVersion));
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest, GetUninstalledApps_Machine) {
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaMachineClientsPath,
+                                            kRegValueProductVersion,
+                                            _T("1.0")));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaMachineClientStatePath,
+                                            kRegValueProductVersion,
+                                            _T("1.0")));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
+                                            kRegValueProductVersion,
+                                            _T("1.1")));
+
+  std::vector<CString> uninstalled_apps;
+  GetUninstalledApps(true, &uninstalled_apps);
+  EXPECT_EQ(1, uninstalled_apps.size());
+  EXPECT_STREQ(kAppGuid, uninstalled_apps[0]);
+
+  RemoveClientStateForApps(true, uninstalled_apps);
+  uninstalled_apps.clear();
+  GetUninstalledApps(true, &uninstalled_apps);
+  EXPECT_EQ(0, uninstalled_apps.size());
+}
+
+TEST_F(AppRegistryUtilsRegistryProtectedTest, GetUninstalledApps_User) {
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientsPath,
+                                            kRegValueProductVersion,
+                                            _T("1.0")));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                            kRegValueProductVersion,
+                                            _T("1.0")));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
+                                            kRegValueProductVersion,
+                                            _T("1.1")));
+
+  std::vector<CString> uninstalled_apps;
+  GetUninstalledApps(false, &uninstalled_apps);
+  EXPECT_EQ(1, uninstalled_apps.size());
+  EXPECT_STREQ(kAppGuid, uninstalled_apps[0]);
+
+  RemoveClientStateForApps(false, uninstalled_apps);
+  uninstalled_apps.clear();
+  GetUninstalledApps(false, &uninstalled_apps);
+  EXPECT_EQ(0, uninstalled_apps.size());
+}
+
+}  // namespace app_registry_utils
+
+}  // namespace omaha
diff --git a/common/app_util.cc b/common/app_util.cc
deleted file mode 100644
index 8d1d540..0000000
--- a/common/app_util.cc
+++ /dev/null
@@ -1,242 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include "omaha/common/app_util.h"
-
-#include <vector>
-#include "omaha/common/cgi.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/file_ver.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/path.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/string.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-namespace app_util {
-
-HRESULT GetGuid(CString* guid) {
-  GUID guid_local = {0};
-  CString guid_str_local;
-  HRESULT hr = ::CoCreateGuid(&guid_local);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  guid_str_local = GuidToString(guid_local);
-
-  // Strip the braces around the guid.
-  guid_str_local.Delete(0);
-  guid_str_local.Delete(guid_str_local.GetLength() - 1);
-
-  *guid = guid_str_local;
-  return S_OK;
-}
-
-HMODULE GetModuleHandleFromAddress(void* address) {
-  MEMORY_BASIC_INFORMATION mbi = {0};
-  DWORD result = ::VirtualQuery(address, &mbi, sizeof(mbi));
-  ASSERT1(result == sizeof(mbi));
-  return static_cast<HMODULE>(mbi.AllocationBase);
-}
-
-HMODULE GetCurrentModuleHandle() {
-  return GetModuleHandleFromAddress(GetCurrentModuleHandle);
-}
-
-
-CString GetModulePath(HMODULE module_handle) {
-  ASSERT1(IsModuleHandleValid(module_handle));
-  CString mod_path;
-
-  DWORD result = ::GetModuleFileName(module_handle,
-                                     mod_path.GetBufferSetLength(MAX_PATH),
-                                     MAX_PATH);
-  mod_path.ReleaseBuffer();
-  ASSERT1(result == static_cast<DWORD>(mod_path.GetLength()));
-
-  return mod_path;
-}
-
-CString GetModuleDirectory(HMODULE module_handle) {
-  ASSERT1(IsModuleHandleValid(module_handle));
-  CString mod_dir(GetDirectoryFromPath(GetModulePath(module_handle)));
-
-  UTIL_LOG(L5, (_T("[GetModuleDirectory]")
-                _T("[module 0x%08x][path '%s'][directory '%s']"),
-                module_handle, GetModulePath(module_handle), mod_dir));
-
-  return mod_dir;
-}
-
-CString GetModuleName(HMODULE module_handle) {
-  ASSERT1(IsModuleHandleValid(module_handle));
-  CString app_name(GetFileFromPath(GetModulePath(module_handle)));
-
-  UTIL_LOG(L5, (_T("[GetModuleName][module 0x%08x][path '%s'][name '%s']"),
-                module_handle, GetModulePath(module_handle), app_name));
-
-  return app_name;
-}
-
-CString GetModuleNameWithoutExtension(HMODULE module_handle) {
-  ASSERT1(IsModuleHandleValid(module_handle));
-  CString module_name(GetPathRemoveExtension(GetModuleName(module_handle)));
-
-  UTIL_LOG(L5, (_T("[GetModuleNameWithoutExtension]")
-                _T("[module 0x%08x][module '%s'][name '%s']"),
-                module_handle, GetModulePath(module_handle), module_name));
-
-  return module_name;
-}
-
-CString GetCurrentModulePath() {
-  return GetModulePath(GetCurrentModuleHandle());
-}
-
-CString GetCurrentModuleDirectory() {
-  return GetModuleDirectory(GetCurrentModuleHandle());
-}
-
-CString GetCurrentModuleName() {
-  return GetModuleName(GetCurrentModuleHandle());
-}
-
-CString GetCurrentModuleNameWithoutExtension() {
-  return GetModuleNameWithoutExtension(GetCurrentModuleHandle());
-}
-
-bool IsCurrentModuleDll() {
-  return IsModuleDll(GetCurrentModuleHandle());
-}
-
-bool IsAddressInCurrentModule(void* address) {
-  return GetCurrentModuleHandle() == GetModuleHandleFromAddress(address);
-}
-
-bool IsModuleDll(HMODULE module_handle) {
-  ASSERT1(IsModuleHandleValid(module_handle));
-  const HMODULE kModuleExe = reinterpret_cast<HMODULE>(kExeLoadingAddress);
-  return (module_handle != kModuleExe) ? true : false;
-}
-
-CString GetHostName() {
-  CString hostName;
-  DWORD name_len = MAX_COMPUTERNAME_LENGTH + 1;
-  bool result = !!::GetComputerName(hostName.GetBufferSetLength(name_len),
-                                    &name_len);
-  ASSERT1(result);
-  hostName.ReleaseBuffer();
-  ASSERT1(name_len == static_cast<DWORD>(hostName.GetLength()));
-
-  return hostName;
-}
-
-CString GetWindowsDir() {
-  CString windows_path;
-
-  DWORD result = ::GetWindowsDirectory(
-      windows_path.GetBufferSetLength(MAX_PATH), MAX_PATH);
-  windows_path.ReleaseBuffer();
-  ASSERT1(result == static_cast<DWORD>(windows_path.GetLength()));
-
-  return windows_path;
-}
-
-CString GetSystemDir() {
-  CString systemPath;
-
-  DWORD result = ::GetSystemDirectory(systemPath.GetBufferSetLength(MAX_PATH),
-                                      MAX_PATH);
-  systemPath.ReleaseBuffer();
-  ASSERT1(result == static_cast<DWORD>(systemPath.GetLength()));
-
-  return systemPath;
-}
-
-CString GetTempDir() {
-  CString tempPath;
-
-  DWORD result = ::GetTempPath(MAX_PATH, tempPath.GetBufferSetLength(MAX_PATH));
-  tempPath.ReleaseBuffer();
-  ASSERT1(result == static_cast<DWORD>(tempPath.GetLength()));
-
-  return tempPath;
-}
-
-bool IsModuleHandleValid(HMODULE module_handle) {
-  if (!module_handle) {
-    return true;
-  }
-  return module_handle == GetModuleHandleFromAddress(module_handle);
-}
-
-DWORD DllGetVersion(const CString& dll_path)  {
-  HINSTANCE hInst = ::GetModuleHandle(dll_path);
-  ASSERT(hInst,
-         (_T("[GetModuleHandle failed][%s][%d]"), dll_path, ::GetLastError()));
-  DWORD dwVersion = 0;
-  DLLGETVERSIONPROC pfn = reinterpret_cast<DLLGETVERSIONPROC>(
-                              ::GetProcAddress(hInst, "DllGetVersion"));
-  if (pfn != NULL) {
-    DLLVERSIONINFO dvi = {0};
-    dvi.cbSize = sizeof(dvi);
-    HRESULT hr = (*pfn)(&dvi);
-    if (SUCCEEDED(hr)) {
-      // Since we're fitting both the major and minor versions into a DWORD,
-      // let's sanity check that we're not in an overflow situation here
-      ASSERT1(dvi.dwMajorVersion <= 0xFFFF);
-      ASSERT1(dvi.dwMinorVersion <= 0xFFFF);
-      dwVersion = MAKELONG(dvi.dwMinorVersion, dvi.dwMajorVersion);
-    }
-  }
-  return dwVersion;
-}
-
-DWORD SystemDllGetVersion(const TCHAR* dll_name)  {
-  ASSERT1(dll_name);
-  CString full_dll_path(String_MakeEndWith(GetSystemDir(), _T("\\"), false) +
-                        dll_name);
-  ASSERT1(File::Exists(full_dll_path));
-  return DllGetVersion(full_dll_path);
-}
-
-ULONGLONG GetVersionFromModule(HMODULE instance) {
-  TCHAR module_path[MAX_PATH] = {0};
-  if (!::GetModuleFileName(instance, module_path, MAX_PATH) != 0) {
-    return 0;
-  }
-
-  return GetVersionFromFile(module_path);
-}
-
-ULONGLONG GetVersionFromFile(const CString& file_path) {
-  FileVer existing_file_ver;
-  if (!existing_file_ver.Open(file_path)) {
-    return 0;
-  }
-
-  return existing_file_ver.GetFileVersionAsULONGLONG();
-}
-
-}  // namespace app_util
-
-}  // namespace omaha
-
diff --git a/common/app_util.h b/common/app_util.h
deleted file mode 100644
index 8ff9f55..0000000
--- a/common/app_util.h
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Utility functions for getting app and module information
-
-#ifndef OMAHA_COMMON_APP_UTIL_H__
-#define OMAHA_COMMON_APP_UTIL_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include "base/basictypes.h"
-#include "omaha/common/constants.h"
-
-namespace omaha {
-
-namespace app_util {
-
-// Gets the handle to the module containing the given executing address.
-HMODULE GetModuleHandleFromAddress(void* address);
-
-// Gets the handle to the currently executing module.
-HMODULE GetCurrentModuleHandle();
-
-// Gets the path of the loaded module.
-// If module_handle == NULL returns the path of the current executable.
-CString GetModulePath(HMODULE module_handle);
-
-// Gets the directory of the specified module
-// Returns the part of the module path, before the last '\'.
-// Returns the dir from where the module was loaded (could be exe or dll).
-CString GetModuleDirectory(HMODULE module_handle);
-
-// Gets the name of the specified module
-// Returns the part of the module path, after the last '\'.
-CString GetModuleName(HMODULE module_handle);
-
-// Gets the name of the specified module without the extension.
-CString GetModuleNameWithoutExtension(HMODULE module_handle);
-
-// Gets the current app name (i.e. exe name).
-CString GetAppName();
-
-// Gets the current app name without the extension.
-CString GetAppNameWithoutExtension();
-
-// Gets the current module path
-// returns the path from where the module was loaded (could be exe or dll).
-CString GetCurrentModulePath();
-
-// Gets the current module directory
-// returns the dir from where the module was loaded (could be exe or dll).
-CString GetCurrentModuleDirectory();
-
-// Gets the current module name.
-CString GetCurrentModuleName();
-
-// Gets the current module name without the extension.
-CString GetCurrentModuleNameWithoutExtension();
-
-// Checks if the given module is a dll or an exe.
-// Assumes a correct HMODULE.
-bool IsModuleDll(HMODULE module_handle);
-
-// Checks if the current module is a dll or an exe.
-bool IsCurrentModuleDll();
-
-// Checks if the given address is in the current module.
-bool IsAddressInCurrentModule(void* address);
-
-// Gets the host machine name.
-CString GetHostName();
-
-// Gets the Windows directory.
-CString GetWindowsDir();
-
-// Gets the System directory.
-CString GetSystemDir();
-
-// Gets the TEMP directory for the current user.
-CString GetTempDir();
-
-// Helper that gets us the version of a DLL in a DWORD format,
-// with the major and minor versions squeezed into it.
-DWORD DllGetVersion(const CString& dll_path);
-
-// Helper that gets us the version of a System DLL in a DWORD format,
-// with the major and minor versions squeezed into it. The assumption
-// is that the dll_name is only a name, and not a path. Using this
-// function (over DllGetVersion directly) for System DLLs is recommended
-// from a security perspective.
-// However, this may not work for DLLs that are loaded from the side-by-side
-// location (WinSxS) instead of the system directory.
-DWORD SystemDllGetVersion(const TCHAR* dll_name);
-
-// Gets the version from a module.
-ULONGLONG GetVersionFromModule(HMODULE instance);
-
-// Gets the version from a file path.
-ULONGLONG GetVersionFromFile(const CString& file_path);
-
-// Gets a guid from the system (for user id, etc.)
-// The guid will be of the form: 00000000-0000-0000-0000-000000000000
-HRESULT GetGuid(CString* guid);
-
-// Helper to check if a module handle is valid.
-bool IsModuleHandleValid(HMODULE module_handle);
-
-inline CString GetAppName() {
-  return GetModuleName(NULL);
-}
-
-inline CString GetAppNameWithoutExtension() {
-  return GetModuleNameWithoutExtension(NULL);
-}
-
-}  // namespace app_util
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_APP_UTIL_H__
-
diff --git a/common/app_util_unittest.cc b/common/app_util_unittest.cc
deleted file mode 100644
index fc13f89..0000000
--- a/common/app_util_unittest.cc
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <atlpath.h>
-#include "omaha/common/app_util.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/file.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/testing/unit_test.h"
-
-const TCHAR* const kKernel32Name  = L"kernel32.dll";
-const TCHAR* const kShell32Name   = L"shell32.dll";
-const TCHAR* const kComCtl32Name  = L"comctl32.dll";
-
-namespace omaha {
-
-namespace app_util {
-
-TEST(AppUtilTest, AppUtil) {
-  HMODULE module = NULL;
-  CString name;
-
-  // First test the functionality for EXE applications.
-
-  // Test the exe module handle.
-  module = GetCurrentModuleHandle();
-  EXPECT_EQ(reinterpret_cast<HMODULE>(kExeLoadingAddress), module);
-
-  // Test the exe module handle using an address inside this module.
-  struct Local {
-    static void Func() {}
-  };
-  module = GetModuleHandleFromAddress(&Local::Func);
-  EXPECT_EQ(reinterpret_cast<HMODULE>(kExeLoadingAddress), module);
-
-  // Test the app name.
-  name = GetAppName();
-  EXPECT_STREQ(kUnittestName, name);
-
-  // Test the module name.
-  name = GetCurrentModuleName();
-  EXPECT_STREQ(kUnittestName, name);
-
-  // Test the app name w/o extension.
-  name = GetAppNameWithoutExtension() + L".exe";
-  EXPECT_STREQ(kUnittestName, name);
-
-  // Test the module name w/o extension.
-  name = GetCurrentModulePath();
-  EXPECT_STREQ(GetCurrentModuleDirectory() + L"\\" + kUnittestName, name);
-
-  // Test the module path and directory.
-  name = GetCurrentModuleName();
-  EXPECT_STREQ(kUnittestName, name);
-
-  // Test that the module is an exe.
-  module = GetCurrentModuleHandle();
-  EXPECT_FALSE(IsModuleDll(module));
-  EXPECT_FALSE(IsCurrentModuleDll());
-
-  // Test an address.
-  module = GetCurrentModuleHandle();
-  EXPECT_TRUE(IsAddressInCurrentModule(module + 0x1));
-  EXPECT_TRUE(IsAddressInCurrentModule(&Local::Func));
-  EXPECT_FALSE(IsAddressInCurrentModule(reinterpret_cast<void*>(0x1)));
-
-  // Test the functionality for DLL modules.
-  // Use kernel32.dll
-
-  // Get the loading address of kernel32.
-  HMODULE kernel32Module = ::LoadLibrary(kKernel32Name);
-  EXPECT_TRUE(kernel32Module != NULL);
-
-  // Test the dll module handle using an address.
-  module = GetModuleHandleFromAddress(::ReadFile);
-  EXPECT_EQ(kernel32Module, module);
-
-  // Check that the module is a dll.
-  EXPECT_TRUE(IsModuleDll(module));
-
-  CString system_path;
-  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_SYSTEMX86, &system_path));
-
-  // Test the dll module directory.
-  name = GetModuleDirectory(module);
-  EXPECT_EQ(0, name.CompareNoCase(system_path));
-
-  // Test the dll module path.
-  name = GetModulePath(module);
-  EXPECT_EQ(0, name.CompareNoCase(system_path + L"\\" + kKernel32Name));
-
-  // Test the dll module name.
-  name = GetModuleName(module);
-  EXPECT_EQ(0, name.CompareNoCase(kKernel32Name));
-
-  // Other checks.
-  EXPECT_FALSE(GetWindowsDir().IsEmpty());
-  EXPECT_FALSE(GetHostName().IsEmpty());
-  EXPECT_FALSE(GetTempDir().IsEmpty());
-
-  // DLL versioning.
-  // For the tests to succeed, shell32.dll must be loaded in memory.
-  HMODULE shell32Module = ::LoadLibrary(kShell32Name);
-  EXPECT_NE(0, DllGetVersion(GetSystemDir() + L"\\" + kShell32Name));
-  EXPECT_NE(0, DllGetVersion(kShell32Name));
-  EXPECT_NE(0, SystemDllGetVersion(kShell32Name));
-
-  // For the tests to succeed, comctl32.dll must be loaded in memory.
-  // ComCtl32 may be loaded from a side-by-side (WinSxS) directory, so it is not
-  // practical to do a full-path or SystemDllGetVersion test with it.
-  HMODULE comctl32_module = ::LoadLibrary(kComCtl32Name);
-  EXPECT_NE(0, DllGetVersion(kComCtl32Name));
-
-  // kernel32 does not export DllGetVersion.
-  EXPECT_EQ(0, SystemDllGetVersion(kKernel32Name));
-
-  // Module clean-up.
-  EXPECT_TRUE(::FreeLibrary(comctl32_module));
-  EXPECT_TRUE(::FreeLibrary(shell32Module));
-  EXPECT_TRUE(::FreeLibrary(kernel32Module));
-}
-
-TEST(AppUtilTest, GetVersionFromModule) {
-  EXPECT_EQ(OMAHA_BUILD_VERSION, GetVersionFromModule(NULL));
-}
-
-TEST(AppUtilTest, GetVersionFromFile) {
-  CPath goopdate_path(app_util::GetCurrentModuleDirectory());
-  ASSERT_TRUE(goopdate_path.Append(kGoopdateDllName));
-  ASSERT_TRUE(File::Exists(goopdate_path));
-
-  EXPECT_EQ(OMAHA_BUILD_VERSION, GetVersionFromFile(goopdate_path));
-}
-
-}  // namespace app_util
-
-}  // namespace omaha
-
diff --git a/common/apply_tag.cc b/common/apply_tag.cc
deleted file mode 100644
index 48d13e2..0000000
--- a/common/apply_tag.cc
+++ /dev/null
@@ -1,244 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Applies a tag to a signed file.
-
-#include "omaha/common/apply_tag.h"
-#include <atlrx.h>
-#include <vector>
-#include "base/scoped_ptr.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/extractor.h"
-
-namespace omaha {
-
-const char kMagicBytes[] = "Gact";
-const uint32 kPEHeaderOffset = 60;
-
-ApplyTag::ApplyTag()
-    : prev_tag_string_length_(0),
-      prev_cert_length_(0),
-      append_(0) {}
-
-bool ApplyTag::IsValidTagString(const char* tag_string) {
-  ASSERT1(tag_string);
-
-  CAtlRegExp<CAtlRECharTraitsA> regex;
-  REParseError error = regex.Parse(kValidTagStringRegEx);
-  if (error != REPARSE_ERROR_OK) {
-    return false;
-  }
-
-  CAtlREMatchContext<CAtlRECharTraitsA> context;
-  return !!regex.Match(tag_string, &context);
-}
-
-HRESULT ApplyTag::Init(const TCHAR* signed_exe_file,
-                       const char* tag_string,
-                       int tag_string_length,
-                       const TCHAR* tagged_file,
-                       bool append) {
-  ASSERT1(signed_exe_file);
-  ASSERT1(tag_string);
-  ASSERT1(tagged_file);
-
-  signed_exe_file_ = signed_exe_file;
-  tagged_file_ = tagged_file;
-  append_ = append;
-
-  // Check the tag_string for invalid characters.
-  if (!IsValidTagString(tag_string)) {
-    return E_INVALIDARG;
-  }
-
-  for (int i = 0; i < tag_string_length; ++i) {
-    tag_string_.push_back(tag_string[i]);
-  }
-
-  return S_OK;
-}
-
-HRESULT ApplyTag::EmbedTagString() {
-  std::vector<byte> input_file_buffer;
-  HRESULT hr = ReadEntireFile(signed_exe_file_, 0, &input_file_buffer);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  ASSERT1(!input_file_buffer.empty());
-  VERIFY1(ReadExistingTag(&input_file_buffer));
-  if (!append_ && prev_tag_string_length_) {
-    // If there is a previous tag and the append flag is not set, then
-    // we should error out.
-    return APPLYTAG_E_ALREADY_TAGGED;
-  }
-
-  if (!CreateBufferToWrite()) {
-    return E_FAIL;
-  }
-
-  // The input_file_buffer might contain the previously read tag, in which
-  // case the buffer_data_ is larger than the actual output buffer length.
-  // The real output buffer length is returned by the ApplyTagToBuffer
-  // method.
-  buffer_data_.resize(input_file_buffer.size() + tag_buffer_.size());
-
-  copy(input_file_buffer.begin(),
-       input_file_buffer.end(),
-       buffer_data_.begin());
-
-  int output_length = 0;
-  if (!ApplyTagToBuffer(&output_length))
-    return E_FAIL;
-
-  std::vector<byte> output_buffer(output_length);
-  ASSERT1(static_cast<size_t>(output_length) <= buffer_data_.size());
-  copy(buffer_data_.begin(),
-       buffer_data_.begin() + output_length,
-       output_buffer.begin());
-  return WriteEntireFile(tagged_file_, output_buffer);
-}
-
-uint32 ApplyTag::GetUint32(const void* p) {
-  ASSERT1(p);
-
-  const uint32* pu = reinterpret_cast<const uint32*>(p);
-  return *pu;
-}
-
-void ApplyTag::PutUint32(uint32 i, void* p) {
-  ASSERT1(p);
-
-  uint32* pu = reinterpret_cast<uint32*>(p);
-  *pu = i;
-}
-
-bool ApplyTag::ReadExistingTag(std::vector<byte>* binary) {
-  ASSERT1(binary);
-
-  int len = 0;
-  TagExtractor tag;
-  char* bin = reinterpret_cast<char*>(&binary->front());
-  ASSERT1(bin);
-  if (tag.ExtractTag(bin, binary->size(), NULL, &len)) {
-    prev_tag_string_.resize(len);
-    if (tag.ExtractTag(bin, binary->size(), &prev_tag_string_.front(), &len)) {
-      // The extractor returns the actual length
-      // of the string + 1 for the terminating null.
-      prev_tag_string_length_ = len - 1;
-    }
-  }
-
-  // Set the existing certificate length even if previous
-  // tag does not exist.
-  prev_cert_length_ = tag.cert_length();
-  return true;
-}
-
-bool ApplyTag::CreateBufferToWrite() {
-  ASSERT1(!append_ && !prev_tag_string_length_ || append_);
-  ASSERT1(!tag_string_.empty());
-  ASSERT1(!prev_tag_string_.size() ||
-          prev_tag_string_.size() ==
-          static_cast<size_t>(prev_tag_string_length_ + 1));
-
-  // Build the tag buffer.
-  // The format of the tag buffer is:
-  // 000000-000003: 4-byte magic (big-endian)
-  // 000004-000005: unsigned 16-bit int string length (big-endian)
-  // 000006-??????: ASCII string
-  int tag_string_len = tag_string_.size() + prev_tag_string_length_;
-  int kMagicBytesLen = ::lstrlenA(kMagicBytes);
-  int tag_header_len = kMagicBytesLen + 2;
-  int unpadded_tag_buffer_len = tag_string_len + tag_header_len;
-  // The tag buffer should be padded to multiples of 8, otherwise it will
-  // break the signature of the executable file.
-  int padded_tag_buffer_length = (unpadded_tag_buffer_len + 15) & (-8);
-
-  tag_buffer_.clear();
-  tag_buffer_.resize(padded_tag_buffer_length, 0);
-  memcpy(&tag_buffer_.front(), kMagicBytes, kMagicBytesLen);
-  tag_buffer_[kMagicBytesLen] =
-      static_cast<char>((tag_string_len & 0xff00) >> 8);
-  tag_buffer_[kMagicBytesLen+1] = static_cast<char>(tag_string_len & 0xff);
-
-  if (prev_tag_string_length_ > 0) {
-    copy(prev_tag_string_.begin(),
-         prev_tag_string_.end(),
-         tag_buffer_.begin() + tag_header_len);
-  }
-
-  copy(tag_string_.begin(),
-       tag_string_.end(),
-       tag_buffer_.begin() + tag_header_len + prev_tag_string_length_);
-  ASSERT1(static_cast<int>(tag_buffer_.size()) == padded_tag_buffer_length);
-
-  return true;
-}
-
-bool ApplyTag::ApplyTagToBuffer(int* output_len) {
-  ASSERT1(output_len);
-
-  uint32 original_data_len = buffer_data_.size() - tag_buffer_.size();
-  uint32 peheader = GetUint32(&buffer_data_.front() + kPEHeaderOffset);
-  uint32 kCertDirAddressOffset = 152;
-  uint32 kCertDirInfoSize = 4 + 4;
-
-  ASSERT1(peheader + kCertDirAddressOffset + kCertDirInfoSize <=
-          original_data_len);
-
-  // Read certificate directory info.
-  uint32 cert_dir_offset = GetUint32(&buffer_data_.front() + peheader +
-                                     kCertDirAddressOffset);
-  if (cert_dir_offset == 0)
-    return false;
-  uint32 cert_dir_len = GetUint32(&buffer_data_.front() + peheader +
-                                  kCertDirAddressOffset + 4);
-  ASSERT1(cert_dir_offset + cert_dir_len <= original_data_len);
-
-  // Calculate the new output length.
-  int prev_pad_length = cert_dir_len - prev_cert_length_ -
-                        prev_tag_string_length_;
-  ASSERT1(prev_pad_length >= 0);
-  int orig_dir_len = cert_dir_len - prev_tag_string_length_ -
-                     prev_pad_length;
-  ASSERT1(orig_dir_len == prev_cert_length_);
-  int output_length = original_data_len - prev_tag_string_length_ -
-                      prev_pad_length + tag_buffer_.size();
-  *output_len = output_length;
-  ASSERT1(static_cast<size_t>(output_length) <= buffer_data_.size());
-  ASSERT1(output_length >= orig_dir_len);
-
-  // Increase the size of certificate directory.
-  int new_cert_len = prev_cert_length_ + tag_buffer_.size();
-  PutUint32(new_cert_len,
-            &buffer_data_.front() + peheader + kCertDirAddressOffset + 4);
-
-  // Read certificate struct info.
-  uint32 cert_struct_len = GetUint32(&buffer_data_.front() + cert_dir_offset);
-  ASSERT1(!(cert_struct_len > cert_dir_len ||
-            cert_struct_len < cert_dir_len - 8));
-
-  // Increase the certificate struct size.
-  PutUint32(new_cert_len, &buffer_data_.front() + cert_dir_offset);
-
-  // Copy the tag buffer.
-  copy(tag_buffer_.begin(), tag_buffer_.end(),
-       buffer_data_.begin() + cert_dir_offset + prev_cert_length_);
-
-  return true;
-}
-
-}  // namespace omaha.
diff --git a/common/apply_tag.h b/common/apply_tag.h
deleted file mode 100644
index b0c6eb1..0000000
--- a/common/apply_tag.h
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_COMMON_APPLY_TAG_H__
-#define OMAHA_COMMON_APPLY_TAG_H__
-
-#include <atlbase.h>
-#include <atlstr.h>
-
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "omaha/common/error.h"
-
-namespace omaha {
-
-// This regular expression represents the valid characters allowed in the
-// binary tag.
-// When changing this regular expression make sure that string patterns on
-// the server are also updated.
-const char* const kValidTagStringRegEx = "^[-%{}/&=.,_a-zA-Z0-9_]*$";
-
-// Stamps the tag_string into the signed_exe_file.
-// Appends the tag_string to the existing tag if append is
-// true, else errors out. Note that we do not support a
-// overwrite.
-// The tagging is done by adding bytes to the signature
-// directory in the PE flie.
-// The modified signature directory looks something like this
-// <Signature>Gact.<tag_len><tag_string>
-// There are no restrictions on the tag_string, it is just treated
-// as a sequence of bytes.
-class ApplyTag {
- public:
-  ApplyTag();
-  HRESULT Init(const TCHAR* signed_exe_file,
-               const char* tag_string,
-               int tag_string_length,
-               const TCHAR* tagged_file,
-               bool append);
-  HRESULT EmbedTagString();
-
- private:
-  static uint32 GetUint32(const void* p);
-  static void PutUint32(uint32 i, void* p);
-  bool ReadExistingTag(std::vector<byte>* binary);
-  bool CreateBufferToWrite();
-  bool ApplyTagToBuffer(int* output_len);
-  bool IsValidTagString(const char* tag_string);
-
-  // The string to be tagged into the binary.
-  std::vector<char> tag_string_;
-
-  // Existing tag string inside the binary.
-  std::vector<char> prev_tag_string_;
-
-  // This is prev_tag_string_.size - 1, to exclude the terminating null.
-  int prev_tag_string_length_;
-
-  // Length of the certificate inside the binary.
-  int prev_cert_length_;
-
-  // The input binary to be tagged.
-  CString signed_exe_file_;
-
-  // The output binary name.
-  CString tagged_file_;
-
-  // Whether to append the tag string to the existing one.
-  bool append_;
-
-  // Internal buffer to hold the appended string.
-  std::vector<char> tag_buffer_;
-
-  // The output buffer that contains the original binary
-  // data with the tagged information.
-  std::vector<char> buffer_data_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(ApplyTag);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_APPLY_TAG_H__
diff --git a/common/atl_regexp.cc b/common/atl_regexp.cc
deleted file mode 100644
index 910939b..0000000
--- a/common/atl_regexp.cc
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-
-#include "omaha/common/atl_regexp.h"
-
-namespace omaha {
-
-const int kMaxArgs  = 16;
-
-AtlRE::AtlRE(const TCHAR* pattern, bool case_sensitive) {
-  ASSERT(pattern, (L""));
-  REParseError status = re_.Parse(pattern, case_sensitive);
-  ASSERT(status == REPARSE_ERROR_OK, (L""));
-}
-
-AtlRE::~AtlRE() {
-}
-
-bool AtlRE::DoMatchImpl(const TCHAR* text,
-                        CString* args[],
-                        int n,
-                        const TCHAR** match_end) const {
-  // text may be NULL.
-  ASSERT(args, (L""));
-
-  if (!text) {
-    return false;
-  }
-
-  AtlMatchContext matches;
-  BOOL b = re_.Match(text, &matches, match_end);
-  if (!b || matches.m_uNumGroups < static_cast<uint32>(n)) {
-    return false;
-  }
-
-  // Oddly enough, the Match call will make match_end
-  // point off the end of the string if the result is at the
-  // end of the string. We check this and handle it.
-  if (match_end) {
-    if ((*match_end - text) >= lstrlen(text)) {
-      *match_end = NULL;
-    }
-  }
-
-  const TCHAR* start = 0;
-  const TCHAR* end = 0;
-  for (int i = 0; i < n; ++i) {
-    matches.GetMatch(i, &start, &end);
-    ptrdiff_t len = end - start;
-    ASSERT(args[i], (L""));
-    // len+1 for the NULL character that's placed by lstrlen
-    VERIFY1(lstrcpyn(args[i]->GetBufferSetLength(len), start, len + 1) != NULL);
-  }
-  return true;
-}
-
-}  // namespace omaha
diff --git a/common/atl_regexp.h b/common/atl_regexp.h
deleted file mode 100644
index 675dde8..0000000
--- a/common/atl_regexp.h
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// ATL Regular expression class that implements the RE interface.
-// See MSDN help for example patterns (lookup help on CAtlRegExp)
-// NOTE: This class adds about 8k to release builds.
-// TODO(omaha): add a unit test, showing examples, testing functionality
-// and perf.
-
-#ifndef OMAHA_COMMON_ATL_REGEXP_H__
-#define OMAHA_COMMON_ATL_REGEXP_H__
-
-#include <atlstr.h>
-#include <atlrx.h>
-#include "base/basictypes.h"
-#include "omaha/common/regexp.h"
-#include "omaha/common/string.h"
-
-namespace omaha {
-
-// This class is essentially a copy of CAtlRECharTraitsWide from <atlrx.h>, but
-// I've replaced CRT functions that are not in our minicrt.
-//
-// TODO(omaha): do we need this?
-class CAtlRECharTraitsWideNoCrt
-{
-public:
-  typedef WCHAR RECHARTYPE;
-
-  // ATL80 addition.
-  static size_t GetBitFieldForRangeArrayIndex(const RECHARTYPE *sz) throw()
-  {
-#ifndef ATL_NO_CHECK_BIT_FIELD
-    ATLASSERT(UseBitFieldForRange());
-#endif
-    return static_cast<size_t>(*sz);
-  }
-
-  static RECHARTYPE *Next(const RECHARTYPE *sz) throw()
-  {
-    return (RECHARTYPE *) (sz+1);
-  }
-
-  static int Strncmp(const RECHARTYPE *szLeft,
-                     const RECHARTYPE *szRight, size_t nCount) throw()
-  {
-    return String_StrNCmp(szLeft, szRight, nCount,false);
-  }
-
-  static int Strnicmp(const RECHARTYPE *szLeft,
-                      const RECHARTYPE *szRight, size_t nCount) throw()
-  {
-    return String_StrNCmp(szLeft, szRight, nCount,true);
-  }
-
-  static RECHARTYPE *Strlwr(RECHARTYPE *sz) throw()
-  {
-    return String_FastToLower(sz);
-  }
-
-  // In ATL 80 Strlwr must be passed a buffer size for security reasons.
-  // TODO(omaha): Implement the function to consider the nSize param.
-  static RECHARTYPE *Strlwr(RECHARTYPE *sz, int) throw()
-  {
-    return Strlwr(sz);
-  }
-
-  static long Strtol(const RECHARTYPE *sz,
-                     RECHARTYPE **szEnd, int nBase) throw()
-  {
-    return Wcstol(sz, szEnd, nBase);
-  }
-
-  static int Isdigit(RECHARTYPE ch) throw()
-  {
-    return String_IsDigit(ch) ? 1 : 0;
-  }
-
-  static const RECHARTYPE** GetAbbrevs()
-  {
-    static const RECHARTYPE *s_szAbbrevs[] =
-    {
-      L"a([a-zA-Z0-9])",  // alpha numeric
-        L"b([ \\t])",    // white space (blank)
-        L"c([a-zA-Z])",  // alpha
-        L"d([0-9])",    // digit
-        L"h([0-9a-fA-F])",  // hex digit
-        L"n(\r|(\r?\n))",  // newline
-        L"q(\"[^\"]*\")|(\'[^\']*\')",  // quoted string
-        L"w([a-zA-Z]+)",  // simple word
-        L"z([0-9]+)",    // integer
-        NULL
-    };
-
-    return s_szAbbrevs;
-  }
-
-  static BOOL UseBitFieldForRange() throw()
-  {
-    return FALSE;
-  }
-
-  static int ByteLen(const RECHARTYPE *sz) throw()
-  {
-    return int(lstrlen(sz)*sizeof(WCHAR));
-  }
-};
-
-typedef CAtlRegExp<CAtlRECharTraitsWideNoCrt> AtlRegExp;
-typedef CAtlREMatchContext<CAtlRECharTraitsWideNoCrt> AtlMatchContext;
-
-// implements the RE class using the ATL Regular Expressions class
-class AtlRE : public RE {
- public:
-
-  AtlRE(const TCHAR* pattern, bool case_sensitive = true);
-  virtual ~AtlRE();
-
- protected:
-  // See regexp.h for an explanation.
-  virtual bool DoMatchImpl(const TCHAR* text,
-                           CString* args[],
-                           int n,
-                           const TCHAR** match_end) const;
-
- private:
-  mutable AtlRegExp re_;
-  DISALLOW_EVIL_CONSTRUCTORS(AtlRE);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_ATL_REGEXP_H__
diff --git a/common/atl_regexp_unittest.cc b/common/atl_regexp_unittest.cc
deleted file mode 100644
index 6ca3cec..0000000
--- a/common/atl_regexp_unittest.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/atl_regexp.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(AtlRETest, AtlRE) {
-  AtlRE newline_test_re(_T("ab{\\n}cd"));
-  const TCHAR* newline_strings[] = { _T("\n"), _T("\r"), _T("\r\n") };
-  for (size_t i = 0; i < arraysize(newline_strings); ++i) {
-    CString content(_T("ab"));
-    content.Append(newline_strings[i]);
-    content.Append(_T("cd"));
-    const TCHAR* content_ptr = content.GetString();
-    CString newline;
-    EXPECT_TRUE(RE::FindAndConsume(&content_ptr, newline_test_re, &newline));
-    EXPECT_STREQ(newline, newline_strings[i]);
-  }
-
-  // Check that AtlRE works with Unicode characters.
-  AtlRE one_two_three_four(_T("\x1234"));
-  EXPECT_TRUE(RE::PartialMatch(_T("\x4321\x1234\x4321"), one_two_three_four));
-}
-
-}  // namespace omaha
diff --git a/common/atlassert.h b/common/atlassert.h
deleted file mode 100644
index 069c0b7..0000000
--- a/common/atlassert.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Take over ATLASSERT
-//
-
-#ifndef OMAHA_COMMON_ATLASSERT_H__
-#define OMAHA_COMMON_ATLASSERT_H__
-
-#include <tchar.h>
-
-#ifdef _DEBUG
-#ifndef DEBUG
-#error DEBUG and _DEBUG must be in sync
-#endif
-#endif
-
-namespace omaha {
-
-enum ReportType {
-  R_INFO = 1,   // Not an error, used for accumulating statistics.
-  R_WARNING,    // May or may not be an error.
-  R_ERROR,      // Definitely an error.
-  R_FATAL       // halt program == ASSERT for release mode.
-};
-
-enum DebugReportKind {
-  DEBUGREPORT_NONE   = 0,
-  DEBUGREPORT_ASSERT = 1,
-  DEBUGREPORT_REPORT = 2,
-  DEBUGREPORT_ABORT  = 3
-};
-
-#ifdef DEBUG
-extern "C" bool DebugReport(unsigned int id,
-                            omaha::ReportType type,
-                            const char* expr,
-                            const TCHAR* message,
-                            const char* filename,
-                            int linenumber,
-                            omaha::DebugReportKind debug_report_kind);
-  #ifndef ATLASSERT
-  #define ATLASSERT(expr)                         \
-    do {                                          \
-      if (!(expr)) {                              \
-        DebugReport(0,                            \
-                    omaha::R_FATAL,               \
-                    #expr,                        \
-                    _T("ATL assertion"),          \
-                    __FILE__,                     \
-                    __LINE__,                     \
-                    omaha::DEBUGREPORT_ASSERT);   \
-      }                                           \
-    } while (0)
-  #endif
-#else
-  #ifndef ATLASSERT
-  #define ATLASSERT(expr) ((void)0)
-  #endif
-#endif
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_ATLASSERT_H__
diff --git a/common/atlassert_unittest.cc b/common/atlassert_unittest.cc
deleted file mode 100644
index 2e1deb9..0000000
--- a/common/atlassert_unittest.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/debug.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-// Test what happens when we hit an ATLASSERT within ATL code.
-// The CComPtr expects the parameter to be 0.
-TEST(AtlAssertTest, AtlAssert) {
-  ExpectAsserts expect_asserts;
-  CComPtr<IUnknown> p(1);
-}
-
-}  // namespace omaha
diff --git a/common/auto_any.h b/common/auto_any.h
deleted file mode 100644
index 4b550a2..0000000
--- a/common/auto_any.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// See the comments in omaha/common/scoped_any.h for details.
-
-#ifndef OMAHA_COMMON_AUTO_ANY_H__
-#define OMAHA_COMMON_AUTO_ANY_H__
-
-#pragma warning(push)
-// C4640: construction of local static object is not thread-safe
-#pragma warning(disable : 4640)
-#include "omaha/third_party/smartany/scoped_any.h"
-#pragma warning(pop)
-
-#endif  // OMAHA_COMMON_AUTO_ANY_H__
-
diff --git a/common/browser_utils.cc b/common/browser_utils.cc
deleted file mode 100644
index e5451d7..0000000
--- a/common/browser_utils.cc
+++ /dev/null
@@ -1,593 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/browser_utils.h"
-#include "base/basictypes.h"
-#include "omaha/common/commands.h"
-#include "omaha/common/const_utils.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/path.h"
-#include "omaha/common/proc_utils.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/common/shell.h"
-#include "omaha/common/string.h"
-#include "omaha/common/system.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/common/vista_utils.h"
-
-namespace omaha {
-
-HRESULT GetLegacyDefaultBrowserInfo(CString* name, CString* path) {
-  UTIL_LOG(L3, (_T("[GetLegacyDefaultBrowserInfo]")));
-  ASSERT1(name);
-  ASSERT1(path);
-  name->Empty();
-  path->Empty();
-
-  CString browser_command_line;
-  HRESULT hr = RegKey::GetValue(kRegKeyLegacyDefaultBrowserCommand,
-                                NULL,
-                                &browser_command_line);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[Read failed][%s]"), kRegKeyLegacyDefaultBrowserCommand));
-    return hr;
-  }
-
-  UTIL_LOG(L5, (_T("[browser_command_line][%s]"), browser_command_line));
-  browser_command_line.Trim(_T(" "));
-  if (browser_command_line.IsEmpty()) {
-    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
-  }
-
-  CString browser_path;
-  if (File::Exists(browser_command_line)) {
-    if (File::IsDirectory(browser_command_line)) {
-      UTIL_LOG(LE, (_T("[Unexpected. Command line should not be directory]")));
-      return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
-    }
-
-    browser_path = browser_command_line;
-  } else {
-    hr = GetExePathFromCommandLine(browser_command_line, &browser_path);
-    if (FAILED(hr)) {
-      return hr;
-    }
-
-    if (!File::Exists(browser_path) ||
-        File::IsDirectory(browser_path)) {
-      UTIL_LOG(LE, (_T("[browser_path invalid][%s]"), browser_path));
-      return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
-    }
-  }
-
-  CString browser_name = ::PathFindFileName(browser_path);
-  if (browser_name.IsEmpty() ||
-      !String_EndsWith(browser_name, _T(".exe"), true)) {
-    UTIL_LOG(LE, (_T("[browser name invalid][%s]"), browser_name));
-    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
-  }
-
-  *path = browser_path;
-  *name = browser_name;
-  return S_OK;
-}
-
-HRESULT GetDefaultBrowserName(CString* name) {
-  ASSERT1(name);
-  name->Empty();
-
-  // Get the default browser name from current user registry.
-  HKEY user_root_key = NULL;
-  VERIFY1(::RegOpenCurrentUser(KEY_READ, &user_root_key) == ERROR_SUCCESS);
-  RegKey user_default_browser;
-  HRESULT hr = user_default_browser.Open(
-      user_root_key ? user_root_key : HKEY_CURRENT_USER,
-      kRegKeyDefaultBrowser,
-      KEY_READ);
-  if (SUCCEEDED(hr)) {
-    hr = user_default_browser.GetValue(NULL, name);
-  }
-  if (user_root_key) {
-    LONG error(::RegCloseKey(user_root_key));
-
-    // See bug http://b/1231862 for details when RegCloseKey can
-    // return ERROR_INVALID_HANDLE.
-    ASSERT1(error == ERROR_SUCCESS ||
-            error == ERROR_INVALID_HANDLE);
-  }
-
-  if (SUCCEEDED(hr) && !name->IsEmpty()) {
-    UTIL_LOG(L3, (_T("[Default browser for the user is %s]"), *name));
-    return S_OK;
-  }
-
-  // Try to get from local machine registry.
-  hr = RegKey::GetValue(kRegKeyMachineDefaultBrowser, NULL, name);
-  if (SUCCEEDED(hr) && !name->IsEmpty()) {
-    UTIL_LOG(L3, (_T("[Default browser for the machine is %s]"), *name));
-    return S_OK;
-  }
-
-  // Try to get from legacy default browser location.
-  CString browser_path;
-  hr = GetLegacyDefaultBrowserInfo(name, &browser_path);
-  if (SUCCEEDED(hr) && !name->IsEmpty()) {
-    UTIL_LOG(L3, (_T("[Legacy default browser is %s]"), *name));
-    return S_OK;
-  }
-
-  // If still failed, we don't want to break in this case so we default to
-  // IEXPLORE.EXE. A scenario where this can happen is when the user installs
-  // a browser that configures itself to be default. Then the user uninstalls
-  // or somehow removes that browser and the registry is not updated correctly.
-  *name = kIeExeName;
-  return S_OK;
-}
-
-HRESULT GetDefaultBrowserType(BrowserType* type) {
-  ASSERT1(type);
-
-  CString name;
-  HRESULT hr = GetDefaultBrowserName(&name);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  if (name.CompareNoCase(kIeExeName) == 0) {
-    *type = BROWSER_IE;
-  } else if (name.CompareNoCase(kFirefoxExeName) == 0) {
-    *type = BROWSER_FIREFOX;
-  } else if (name.CompareNoCase(kChromeExeName) == 0) {
-    *type = BROWSER_CHROME;
-  } else {
-    *type = BROWSER_UNKNOWN;
-  }
-
-  return S_OK;
-}
-
-// Returns a path that is always unenclosed. The method could return a short or
-// long form path.
-HRESULT GetDefaultBrowserPath(CString* path) {
-  ASSERT1(path);
-  path->Empty();
-  ON_SCOPE_EXIT(UnenclosePath, path);
-
-  // Get the default browser name.
-  CString name;
-  if (FAILED(GetDefaultBrowserName(&name))) {
-    // Try getting IE's path through COM registration and app path entries.
-    return GetBrowserImagePath(BROWSER_IE, path);
-  }
-
-  CString shell_open_path(_T("\\"));
-  shell_open_path += name;
-  shell_open_path += kRegKeyShellOpenCommand;
-
-  // Read the path corresponding to it from current user registry.
-  CString browser_key(kRegKeyUserDefaultBrowser);
-  browser_key += shell_open_path;
-  HRESULT hr = RegKey::GetValue(browser_key, NULL, path);
-
-  CString cmd_line(*path);
-  CString args;
-  if (SUCCEEDED(hr) &&
-      !path->IsEmpty() &&
-      SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(cmd_line,
-                                                           path,
-                                                           &args))) {
-    return S_OK;
-  }
-
-  // If failed, try to get from local machine registry.
-  browser_key = kRegKeyMachineDefaultBrowser;
-  browser_key += shell_open_path;
-  hr = RegKey::GetValue(browser_key, NULL, path);
-
-  cmd_line = *path;
-  args.Empty();
-  if (SUCCEEDED(hr) &&
-      !path->IsEmpty() &&
-      SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(cmd_line,
-                                                           path,
-                                                           &args))) {
-    return S_OK;
-  }
-
-  // Try to get from legacy default browser location.
-  hr = GetLegacyDefaultBrowserInfo(&name, path);
-  if (SUCCEEDED(hr) && !path->IsEmpty()) {
-    return S_OK;
-  }
-
-  // If failed and the default browser is not IE, try IE once again.
-  if (name.CompareNoCase(kIeExeName) != 0) {
-    browser_key = kRegKeyMachineDefaultBrowser
-                  _T("\\") kIeExeName kRegKeyShellOpenCommand;
-    hr = RegKey::GetValue(browser_key, NULL, path);
-
-    cmd_line = *path;
-    args.Empty();
-    if (SUCCEEDED(hr) &&
-        !path->IsEmpty() &&
-        SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(cmd_line,
-                                                             path,
-                                                             &args))) {
-      return S_OK;
-    }
-  }
-
-  // Try getting the default browser's path through COM registration and app
-  // path entries.
-  BrowserType default_type = BROWSER_UNKNOWN;
-  hr = GetDefaultBrowserType(&default_type);
-  if (FAILED(hr) ||
-      default_type == BROWSER_UNKNOWN ||
-      default_type == BROWSER_DEFAULT) {
-    default_type = BROWSER_IE;
-  }
-
-  return GetBrowserImagePath(default_type, path);
-}
-
-HRESULT GetFirefoxDefaultProfile(CString* name, CString* path) {
-  ASSERT1(name);
-  ASSERT1(path);
-
-  const TCHAR kFirefoxAppDataPath[] = _T("\\Mozilla\\Firefox\\");
-  const TCHAR kFirefoxProfileIni[] = _T("profiles.ini");
-  const TCHAR kFirefoxDefaultProfileSecName[] = _T("Profile0");
-  const TCHAR kFirefoxProfileIniNameKey[] = _T("Name");
-  const TCHAR kFirefoxProfileIniIsRelativeKey[] = _T("IsRelative");
-  const TCHAR kFirefoxProfileIniPathKey[] = _T("Path");
-  const TCHAR kFirefoxProfileIniDefaultKey[] = _T("Default");
-
-  name->Empty();
-  path->Empty();
-
-  // Get appdata path for storing Firefox settings.
-  CString appdata_path;
-  RET_IF_FAILED(Shell::GetSpecialFolder(CSIDL_APPDATA, false, &appdata_path));
-  appdata_path += kFirefoxAppDataPath;
-
-  // Get profile.ini.
-  CString profile_ini = appdata_path + kFirefoxProfileIni;
-  UTIL_LOG(L3, (_T("[FireFox profile.ini][%s]"), profile_ini));
-
-  if (!File::Exists(profile_ini)) {
-    UTIL_LOG(LE, (_T("[File does not exist][%s]"), profile_ini));
-    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
-  }
-
-  // Read all section names in profile.ini.
-  // The buffer is filled with one or more null-terminated strings; the last
-  // string is followed by a second null character.
-  const int kMaxProfileSecNamesLength = 2048;
-  CString profile_sec_names;
-  DWORD char_returned = ::GetPrivateProfileSectionNames(
-      profile_sec_names.GetBufferSetLength(kMaxProfileSecNamesLength),
-      kMaxProfileSecNamesLength,
-      profile_ini);
-  if (char_returned == kMaxProfileSecNamesLength - 2) {
-    UTIL_LOG(LW, (_T("[FireFox profile.ini contains too many sections]")));
-  }
-  profile_sec_names.ReleaseBuffer(char_returned);
-
-  // Iterate through all the sections to find the default profile.
-  const TCHAR* default_profile_sec = NULL;
-  const TCHAR* ptr = profile_sec_names.GetString();
-  const TCHAR* end = ptr + char_returned;
-  while (ptr < end && *ptr) {
-    if (::GetPrivateProfileInt(ptr,
-                               kFirefoxProfileIniDefaultKey,
-                               0,
-                               profile_ini)) {
-      default_profile_sec = ptr;
-      break;
-    }
-
-    for (; ptr < end && *ptr; ++ptr) {
-    }
-    ++ptr;
-  }
-
-  if (!default_profile_sec) {
-    default_profile_sec = kFirefoxDefaultProfileSecName;
-  }
-
-  DWORD name_len = ::GetPrivateProfileString(default_profile_sec,
-                                             kFirefoxProfileIniNameKey, _T(""),
-                                             name->GetBufferSetLength(256),
-                                             256,
-                                             profile_ini);
-  name->ReleaseBuffer(name_len);
-
-  DWORD path_len = ::GetPrivateProfileString(default_profile_sec,
-                                             kFirefoxProfileIniPathKey,
-                                             _T(""),
-                                             path->GetBufferSetLength(1024),
-                                             1024,
-                                             profile_ini);
-  path->ReleaseBuffer(path_len);
-  path->Replace(_T('/'), _T('\\'));
-
-  bool is_relative = ::GetPrivateProfileInt(default_profile_sec,
-                                            kFirefoxProfileIniIsRelativeKey,
-                                            0,
-                                            profile_ini) != 0;
-
-  if (is_relative && !path->IsEmpty()) {
-    path->Insert(0, appdata_path);
-  }
-
-  return S_OK;
-}
-
-HRESULT BrowserTypeToProcessName(BrowserType type, CString* exe_name) {
-  ASSERT1(exe_name);
-  ASSERT1(type < BROWSER_MAX);
-
-  switch (type) {
-    case BROWSER_IE:
-      *exe_name = kIeExeName;
-      break;
-    case BROWSER_FIREFOX:
-      *exe_name = kFirefoxExeName;
-      break;
-    case BROWSER_CHROME:
-      *exe_name = kChromeExeName;
-      break;
-    case BROWSER_DEFAULT:
-      return GetDefaultBrowserName(exe_name);
-    case BROWSER_UNKNOWN:
-      // Fall through.
-    case BROWSER_MAX:
-      // Fall through.
-    default:
-      return E_FAIL;
-  }
-
-  return S_OK;
-}
-
-// Returns a path that is always unenclosed. The method could return a short or
-// long form path.
-HRESULT GetBrowserImagePath(BrowserType type, CString* path) {
-  ASSERT1(path);
-  ASSERT1(type < BROWSER_MAX);
-
-  HRESULT hr = E_FAIL;
-  switch (type) {
-    case BROWSER_IE: {
-      hr = RegKey::GetValue(kRegKeyIeClass, kRegValueIeClass, path);
-      break;
-    }
-    case BROWSER_FIREFOX: {
-      hr = RegKey::GetValue(kRegKeyFirefox, kRegValueFirefox, path);
-      if (SUCCEEDED(hr) && !path->IsEmpty()) {
-        // The Firefox registry key contains a -url %1 value. Remove this
-        // because we only want to return the path.
-        ReplaceCString(*path, _T("-url \"%1\""), _T(""));
-      }
-      break;
-    }
-    case BROWSER_CHROME: {
-      hr = RegKey::GetValue(kRegKeyChrome, kRegValueChrome, path);
-      if (SUCCEEDED(hr) && !path->IsEmpty()) {
-        // The Chrome registry key contains a -- "%1" value. Remove this because
-        // we only want to return the path.
-        ReplaceCString(*path, _T("-- \"%1\""), _T(""));
-      }
-      break;
-    }
-    case BROWSER_DEFAULT: {
-      hr = GetDefaultBrowserPath(path);
-      if (FAILED(hr)) {
-        UTIL_LOG(LE, (_T("[GetDefaultBrowserPath failed.][0x%08x]"), hr));
-      }
-      path->Trim();
-      UnenclosePath(path);
-      return hr;
-    }
-    case BROWSER_UNKNOWN:
-      // Fall through.
-    case BROWSER_MAX:
-      // Fall through.
-    default:
-      return E_FAIL;
-  }
-
-  CString cmd_line(*path);
-  CString args;
-  if (SUCCEEDED(hr) &&
-      !path->IsEmpty() &&
-      SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(cmd_line,
-                                                           path,
-                                                           &args))) {
-    return S_OK;
-  }
-
-  // If the above did not work, then we try to read the value from
-  // "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths".
-  CString browser_exe_name;
-  hr = BrowserTypeToProcessName(type, &browser_exe_name);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[BrowserTypeToProcessName failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  CString exe_path;
-  hr = Shell::GetApplicationExecutablePath(browser_exe_name, &exe_path);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[GetApplicationExecutablePath failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  *path = ConcatenatePath(exe_path, browser_exe_name);
-  ASSERT1(!path->IsEmpty());
-
-  path->Trim();
-  UnenclosePath(path);
-  return S_OK;
-}
-
-HRESULT TerminateBrowserProcess(BrowserType type,
-                                const CString& sid,
-                                int timeout_msec,
-                                bool* found) {
-  UTIL_LOG(L3, (_T("[TerminateBrowserProcess][%d]"), type));
-  ASSERT1(found);
-  ASSERT1(type < BROWSER_MAX);
-
-  *found = false;
-  if (type == BROWSER_UNKNOWN || type >= BROWSER_MAX) {
-    return E_FAIL;
-  }
-
-  CString browser_exe_name;
-  HRESULT hr = BrowserTypeToProcessName(type, &browser_exe_name);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[BrowserTypeToProcessName failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  DWORD current_session = System::GetCurrentSessionId();
-  uint32 method_mask = ProcessTerminator::KILL_METHOD_1_WINDOW_MESSAGE;
-  ProcessTerminator process(browser_exe_name, sid, current_session);
-  hr = process.KillTheProcess(timeout_msec, found, method_mask, true);
-  if (FAILED(hr)) {
-    UTIL_LOG(LEVEL_WARNING, (_T("[KillTheProcess failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT WaitForBrowserToDie(BrowserType type,
-                            const CString& sid,
-                            int timeout_msec) {
-  UTIL_LOG(L3, (_T("[WaitForBrowserToDie][%d]"), type));
-  ASSERT1(type < BROWSER_MAX);
-
-  if (type == BROWSER_UNKNOWN || type >= BROWSER_MAX) {
-    return E_FAIL;
-  }
-
-  CString browser_exe_name;
-  HRESULT hr = BrowserTypeToProcessName(type, &browser_exe_name);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[BrowserTypeToProcessName failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  DWORD current_session = System::GetCurrentSessionId();
-  ProcessTerminator process(browser_exe_name, sid, current_session);
-  hr = process.WaitForAllToDie(timeout_msec);
-  if (FAILED(hr)) {
-    UTIL_LOG(LEVEL_WARNING, (_T("[WaitForAllToDie failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT ShellExecuteBrowser(BrowserType type, const CString& url) {
-  UTIL_LOG(L3, (_T("[ShellExecuteBrowser][%d][%s]"), type, url));
-  ASSERT1(type < BROWSER_MAX);
-
-  if (type == BROWSER_UNKNOWN || type >= BROWSER_MAX) {
-    return E_FAIL;
-  }
-
-  CString path;
-  HRESULT hr =  GetBrowserImagePath(type, &path);
-  if (FAILED(hr)) {
-    UTIL_LOG(LW, (_T("[GetBrowserImagePath failed][0x%08x]"), hr));
-    // ShellExecute the url directly as a last resort.
-    return Shell::Execute(url);
-  }
-  EnclosePath(&path);
-
-  UTIL_LOG(L3, (_T("[Execute browser][%s][%s]"), path, url));
-
-  // http://b/1219313: For Vista, in some cases, using ShellExecuteEx does not
-  // re-launch the process. So, using CreateProcess instead.
-  // http://b/1223658: For Vista, especially in the impersonated case, need to
-  // create a fresh environment block. Otherwise, the environment is tainted.
-  hr = vista_util::IsVistaOrLater() ?
-           vista::RunAsCurrentUser(path + _T(' ') + url) :
-           System::ShellExecuteProcess(path, url, NULL, NULL);
-
-  if (FAILED(hr)) {
-    UTIL_LOG(LW, (_T("[%s failed][0x%08x]"),
-                  vista_util::IsVistaOrLater() ? _T("RunAsCurrentUser") :
-                                                 _T("ShellExecuteProcess"),
-                  hr));
-    // ShellExecute the url directly as a last resort.
-    return Shell::Execute(url);
-  }
-
-  return S_OK;
-}
-
-// Gets the font size of IE. This is the value that corresponds to what IE
-// displays in "Page/Text Size" menu option. There are 5 values and the default
-// is "Medium", for which the numeric value is 2. The "IEFontSize" is only
-// present after the user has modified the default text size in IE, therefore
-// the absence of the value indicates "Medium" text size.
-HRESULT GetIeFontSize(uint32* font_size) {
-  ASSERT1(font_size);
-
-  const TCHAR ie_scripts_key[] =
-    _T("HKCU\\Software\\Microsoft\\Internet Explorer\\International\\Scripts\\3");  // NOLINT
-  const TCHAR ie_font_size[] = _T("IEFontSize");
-  const uint32 kDefaultFontSize = 2;
-
-  // We expect the scripts key to be there in all cases. The "IEFontSize" value
-  // is optional but we want to fail if the key is not there.
-  if (!RegKey::HasKey(ie_scripts_key)) {
-    return E_UNEXPECTED;
-  }
-
-  scoped_array<byte> buf;   // The font size is a binary registry value.
-  DWORD buf_size(0);
-  if (FAILED(RegKey::GetValue(ie_scripts_key, ie_font_size,
-                              address(buf), &buf_size))) {
-    *font_size = kDefaultFontSize;
-    return S_OK;
-  }
-
-  ASSERT1(buf_size == sizeof(uint32));  // NOLINT
-  if (buf_size != sizeof(uint32)) {     // NOLINT
-    return E_UNEXPECTED;
-  }
-
-  uint32 val = *reinterpret_cast<uint32*>(buf.get());
-  ASSERT1(val <= 4);
-  if (val > 4) {
-    return E_UNEXPECTED;
-  }
-
-  *font_size = val;
-  return S_OK;
-}
-
-}  // namespace omaha
diff --git a/common/browser_utils.h b/common/browser_utils.h
deleted file mode 100644
index 400b123..0000000
--- a/common/browser_utils.h
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// TODO(omaha): namespaces
-
-#ifndef OMAHA_COMMON_BROWSER_UTILS_H__
-#define OMAHA_COMMON_BROWSER_UTILS_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include "base/basictypes.h"
-
-namespace omaha {
-
-// Do not move or remove existing elements as this is used by applications.
-enum BrowserType {
-  BROWSER_UNKNOWN = 0,
-  BROWSER_DEFAULT = 1,
-  BROWSER_IE      = 2,
-  BROWSER_FIREFOX = 3,
-  BROWSER_CHROME  = 4,
-  // Add all browsers above this.
-  BROWSER_MAX
-};
-
-// Read the browser information from legacy keys. See
-// http://support.microsoft.com/kb/224816.
-HRESULT GetLegacyDefaultBrowserInfo(CString* name, CString* browser_path);
-
-// Gets the default browser name.
-// When calling this method from the local system account, the caller must
-// impersonate the user first. This assumes the user profile is loaded under
-// HKEY_USERS, which is true if the user has an interactive session.
-// Otherwise, the caller must load the profile for the user before
-// calling the function.
-HRESULT GetDefaultBrowserName(CString* name);
-
-// Returns the default browser type.
-HRESULT GetDefaultBrowserType(BrowserType* type);
-
-// Get the default browser path
-HRESULT GetDefaultBrowserPath(CString* path);
-
-// Get the default profile of Firefox
-HRESULT GetFirefoxDefaultProfile(CString* name, CString* path);
-
-// Returns the executable name corresponding to the browser type.
-HRESULT BrowserTypeToProcessName(BrowserType type, CString* exe_name);
-
-// Returns the absolute filename of the browser executable.
-HRESULT GetBrowserImagePath(BrowserType type, CString* path);
-
-// Terminates all the browsers identified by type for a user.
-HRESULT TerminateBrowserProcess(BrowserType type,
-                                const CString& sid,
-                                int timeout_msec,
-                                bool* found);
-
-// Waits for all instances of browser to die.
-HRESULT WaitForBrowserToDie(BrowserType type,
-                            const CString& sid,
-                            int timeout_msec);
-
-// Launches the browser using shell execute.
-HRESULT ShellExecuteBrowser(BrowserType type, const CString& url);
-
-// Returns the current font size selection in Internet Explorer.
-// Possible font size values:
-// 0 : smallest font
-// 1 : small font
-// 2 : medium font
-// 3 : large font
-// 4 : largest font
-HRESULT GetIeFontSize(uint32* font_size);
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_BROWSER_UTILS_H__
diff --git a/common/browser_utils_unittest.cc b/common/browser_utils_unittest.cc
deleted file mode 100644
index ce93b39..0000000
--- a/common/browser_utils_unittest.cc
+++ /dev/null
@@ -1,380 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/browser_utils.h"
-#include "omaha/common/const_utils.h"
-#include "omaha/common/path.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-const CString kRegistryHiveOverrideClasses =
-    CString(kRegistryHiveOverrideRoot) + _T("HKCR");
-
-// GetDefaultBrowserName() uses ::RegOpenCurrentUser, which does not appear to
-// be affected by registry hive overrides. Therefore, in order to test methods
-// that rely on it, the actual value must be replaced. This class saves the
-// value and restores it. If the test is interrupted before TearDown, the
-// default browser may not be correctly registered.
-class BrowserUtilsDefaultBrowserSavedTest : public testing::Test {
- protected:
-  virtual void SetUp() {
-    if (!RegKey::HasKey(kRegKeyUserDefaultBrowser)) {
-      return;
-    }
-
-    EXPECT_SUCCEEDED(RegKey::GetValue(kRegKeyUserDefaultBrowser,
-                                      NULL,
-                                      &default_browser_name_));
-  }
-
-  virtual void TearDown() {
-    if (default_browser_name_.IsEmpty()) {
-      RegKey::DeleteKey(kRegKeyUserDefaultBrowser);
-      return;
-    }
-
-    EXPECT_SUCCEEDED(RegKey::SetValue(kRegKeyUserDefaultBrowser,
-                                      NULL,
-                                      default_browser_name_));
-  }
-
-  CString default_browser_name_;
-};
-
-class GetLegacyDefaultBrowserInfoTest : public testing::Test {
- protected:
-  virtual void SetUp() {
-    RegKey::DeleteKey(kRegistryHiveOverrideClasses, true);
-    RegKey classes_key;
-    ASSERT_HRESULT_SUCCEEDED(classes_key.Create(kRegistryHiveOverrideClasses));
-    ASSERT_HRESULT_SUCCEEDED(::RegOverridePredefKey(HKEY_CLASSES_ROOT,
-                                                    classes_key.Key()));
-  }
-
-  virtual void TearDown() {
-    ASSERT_HRESULT_SUCCEEDED(::RegOverridePredefKey(HKEY_CLASSES_ROOT, NULL));
-    ASSERT_HRESULT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideClasses,
-                             true));
-  }
-};
-
-TEST_F(BrowserUtilsDefaultBrowserSavedTest, GetDefaultBrowserType_IE) {
-  if (!ShouldRunLargeTest()) {
-    return;
-  }
-  EXPECT_SUCCEEDED(
-      RegKey::SetValue(kRegKeyUserDefaultBrowser, NULL, _T("IeXpLoRe.ExE")));
-  BrowserType type = BROWSER_UNKNOWN;
-  EXPECT_SUCCEEDED(GetDefaultBrowserType(&type));
-  EXPECT_EQ(BROWSER_IE, type);
-}
-
-TEST_F(BrowserUtilsDefaultBrowserSavedTest, GetDefaultBrowserType_Firefox) {
-  if (!ShouldRunLargeTest()) {
-    return;
-  }
-  EXPECT_SUCCEEDED(
-      RegKey::SetValue(kRegKeyUserDefaultBrowser, NULL, _T("FiReFoX.ExE")));
-  BrowserType type = BROWSER_UNKNOWN;
-  EXPECT_SUCCEEDED(GetDefaultBrowserType(&type));
-  EXPECT_EQ(BROWSER_FIREFOX, type);
-}
-
-TEST_F(BrowserUtilsDefaultBrowserSavedTest, GetDefaultBrowserType_Chrome) {
-  if (!ShouldRunLargeTest()) {
-    return;
-  }
-  EXPECT_SUCCEEDED(
-      RegKey::SetValue(kRegKeyUserDefaultBrowser, NULL, _T("ChRoMe.ExE")));
-  BrowserType type = BROWSER_UNKNOWN;
-  EXPECT_SUCCEEDED(GetDefaultBrowserType(&type));
-  EXPECT_EQ(BROWSER_CHROME, type);
-}
-
-TEST_F(BrowserUtilsDefaultBrowserSavedTest, GetDefaultBrowserType_Unsupported) {
-  if (!ShouldRunLargeTest()) {
-    return;
-  }
-  EXPECT_SUCCEEDED(
-      RegKey::SetValue(kRegKeyUserDefaultBrowser, NULL, _T("FoO.ExE")));
-  BrowserType type = BROWSER_UNKNOWN;
-  EXPECT_SUCCEEDED(GetDefaultBrowserType(&type));
-  EXPECT_EQ(BROWSER_UNKNOWN, type);
-}
-
-TEST(BrowserUtilsTest, BrowserTypeToProcessName_Unknown) {
-  CString exe_name;
-  EXPECT_EQ(E_FAIL, BrowserTypeToProcessName(BROWSER_UNKNOWN, &exe_name));
-  EXPECT_TRUE(exe_name.IsEmpty());
-}
-
-// Writes the default browser to ensure consistent results.
-TEST_F(BrowserUtilsDefaultBrowserSavedTest, BrowserTypeToProcessName_Default) {
-  if (!ShouldRunLargeTest()) {
-    return;
-  }
-  EXPECT_SUCCEEDED(
-      RegKey::SetValue(kRegKeyUserDefaultBrowser, NULL, _T("IeXpLoRe.ExE")));
-
-  CString default_exe_name;
-  EXPECT_SUCCEEDED(GetDefaultBrowserName(&default_exe_name));
-  EXPECT_STREQ(_T("IeXpLoRe.ExE"), default_exe_name);
-
-  CString exe_name;
-  EXPECT_SUCCEEDED(BrowserTypeToProcessName(BROWSER_DEFAULT, &exe_name));
-  EXPECT_STREQ(_T("IeXpLoRe.ExE"), exe_name);
-}
-
-TEST(BrowserUtilsTest, BrowserTypeToProcessName_Browsers) {
-  CString exe_name;
-  EXPECT_SUCCEEDED(BrowserTypeToProcessName(BROWSER_IE, &exe_name));
-  EXPECT_STREQ(_T("IEXPLORE.EXE"), exe_name);
-  EXPECT_SUCCEEDED(BrowserTypeToProcessName(BROWSER_FIREFOX, &exe_name));
-  EXPECT_STREQ(_T("FIREFOX.EXE"), exe_name);
-  EXPECT_SUCCEEDED(BrowserTypeToProcessName(BROWSER_CHROME, &exe_name));
-  EXPECT_STREQ(_T("CHROME.EXE"), exe_name);
-}
-
-TEST(BrowserUtilsTest, BrowserTypeToProcessName_Invalid) {
-  CString exe_name;
-  ExpectAsserts expect_asserts;
-  EXPECT_EQ(E_FAIL, BrowserTypeToProcessName(BROWSER_MAX, &exe_name));
-  EXPECT_TRUE(exe_name.IsEmpty());
-  EXPECT_EQ(E_FAIL,
-            BrowserTypeToProcessName(static_cast<BrowserType>(9), &exe_name));
-  EXPECT_TRUE(exe_name.IsEmpty());
-  EXPECT_EQ(E_FAIL,
-            BrowserTypeToProcessName(static_cast<BrowserType>(-1), &exe_name));
-  EXPECT_TRUE(exe_name.IsEmpty());
-}
-
-TEST(BrowserUtilsTest, GetBrowserImagePath_DefaultBrowser) {
-  CString browser;
-  ASSERT_SUCCEEDED(GetDefaultBrowserName(&browser));
-
-  BrowserType default_type = BROWSER_UNKNOWN;
-  if (browser.CompareNoCase(kIeExeName) == 0) {
-    default_type = BROWSER_IE;
-  } else if (browser.CompareNoCase(kFirefoxExeName) == 0) {
-    default_type = BROWSER_FIREFOX;
-  } else if (browser.CompareNoCase(kChromeExeName) == 0) {
-    default_type = BROWSER_CHROME;
-  }
-
-  CString exp_browser_path;
-  ASSERT_SUCCEEDED(GetDefaultBrowserPath(&exp_browser_path));
-
-  if (default_type == BROWSER_IE) {
-    CString path;
-    ASSERT_SUCCEEDED(GetBrowserImagePath(BROWSER_IE, &path));
-
-    CString long_name;
-    ASSERT_SUCCEEDED(ShortPathToLongPath(path, &long_name));
-    CString exp_long_name;
-    ASSERT_SUCCEEDED(ShortPathToLongPath(exp_browser_path, &exp_long_name));
-    ASSERT_STREQ(exp_long_name.MakeLower(), long_name.MakeLower());
-  } else if (default_type == BROWSER_FIREFOX) {
-    CString path;
-    ASSERT_SUCCEEDED(GetBrowserImagePath(BROWSER_FIREFOX, &path));
-
-    CString long_name;
-    ASSERT_SUCCEEDED(ShortPathToLongPath(path, &long_name));
-    CString exp_long_name;
-    ASSERT_SUCCEEDED(ShortPathToLongPath(exp_browser_path, &exp_long_name));
-    ASSERT_STREQ(exp_long_name.MakeLower(), long_name.MakeLower());
-  } else if (default_type == BROWSER_CHROME) {
-    CString path;
-    ASSERT_SUCCEEDED(GetBrowserImagePath(BROWSER_CHROME, &path));
-
-    CString long_name;
-    ASSERT_SUCCEEDED(ShortPathToLongPath(path, &long_name));
-    CString exp_long_name;
-    ASSERT_SUCCEEDED(ShortPathToLongPath(exp_browser_path, &exp_long_name));
-    ASSERT_STREQ(exp_long_name.MakeLower(), long_name.MakeLower());
-  }
-
-  CString path;
-  ASSERT_SUCCEEDED(GetBrowserImagePath(BROWSER_DEFAULT, &path));
-  CString long_name;
-  ASSERT_SUCCEEDED(ShortPathToLongPath(path, &long_name));
-  CString exp_long_name;
-  ASSERT_SUCCEEDED(ShortPathToLongPath(exp_browser_path, &exp_long_name));
-  ASSERT_STREQ(exp_long_name.MakeLower(), long_name.MakeLower());
-
-  ASSERT_FAILED(GetBrowserImagePath(BROWSER_UNKNOWN, &path));
-}
-
-// Try all browsers to get more test coverage.
-TEST(BrowserUtilsTest, GetBrowserImagePath_AllSupportedBrowsers) {
-  CString path;
-
-  HRESULT hr = GetBrowserImagePath(BROWSER_IE, &path);
-  if (SUCCEEDED(hr)) {
-    EXPECT_EQ(0, path.CompareNoCase(
-                     _T("C:\\Program Files\\Internet Explorer\\iexplore.exe")))
-        << _T("Actual path: ") << path.GetString();
-  } else {
-    EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), hr);
-  }
-
-  hr = GetBrowserImagePath(BROWSER_FIREFOX, &path);
-  if (SUCCEEDED(hr)) {
-    EXPECT_TRUE(
-        0 == path.CompareNoCase(
-            _T("C:\\Program Files\\Mozilla Firefox\\firefox.exe")) ||
-        0 == path.CompareNoCase(
-            _T("C:\\PROGRA~1\\MOZILL~1\\FIREFOX.EXE")) ||
-        0 == path.CompareNoCase(
-            _T("C:\\Program Files\\Minefield\\FIREFOX.EXE")))  // Trunk build.
-        << _T("Actual path: ") << path.GetString();
-  } else {
-    EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), hr);
-  }
-
-  hr = GetBrowserImagePath(BROWSER_CHROME, &path);
-  if (SUCCEEDED(hr)) {
-    EXPECT_TRUE(
-        0 == path.CompareNoCase(
-            _T("C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe")) ||
-        0 == path.CompareNoCase(
-            GetLocalAppDataPath() +
-            _T("Google\\Chrome\\Application\\chrome.exe")))
-        << _T("Actual path: ") << path.GetString();
-  } else {
-    EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), hr);
-  }
-}
-
-TEST(BrowserUtilsTest, GetLegacyDefaultBrowserInfo) {
-  CString name;
-  CString browser_path;
-  EXPECT_SUCCEEDED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
-  EXPECT_FALSE(browser_path.IsEmpty());
-  EXPECT_FALSE(name.IsEmpty());
-}
-
-TEST(BrowserUtilsTest, GetIeFontSize) {
-  // The build server might not have IE configured.
-  if (!IsBuildSystem()) {
-    uint32 font_size = 0;
-    const uint32 kMaxFontSize = 4;
-    EXPECT_SUCCEEDED(GetIeFontSize(&font_size));
-    EXPECT_LE(font_size, kMaxFontSize);
-  }
-}
-
-TEST_F(GetLegacyDefaultBrowserInfoTest, ValidQuotedIE) {
-  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(
-      kRegKeyLegacyDefaultBrowserCommand,
-      NULL,
-      _T("\"C:\\Program Files\\Internet Explorer\\iexplore.exe\" -nohome")));
-
-  CString name;
-  CString browser_path;
-  EXPECT_SUCCEEDED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
-  EXPECT_STREQ(_T("C:\\Program Files\\Internet Explorer\\iexplore.exe"),
-               browser_path);
-  EXPECT_STREQ(_T("iexplore.exe"), name);
-}
-
-TEST_F(GetLegacyDefaultBrowserInfoTest, ValidUnquotedPath) {
-  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(
-      kRegKeyLegacyDefaultBrowserCommand,
-      NULL,
-      _T("C:\\Program Files\\Internet Explorer\\iexplore.exe")));
-
-  CString name;
-  CString browser_path;
-  EXPECT_SUCCEEDED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
-  EXPECT_STREQ(_T("C:\\Program Files\\Internet Explorer\\iexplore.exe"),
-               browser_path);
-  EXPECT_STREQ(_T("iexplore.exe"), name);
-}
-
-TEST_F(GetLegacyDefaultBrowserInfoTest, InvalidNoKey) {
-  CString name;
-  CString browser_path;
-  EXPECT_FAILED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
-  EXPECT_TRUE(browser_path.IsEmpty());
-  EXPECT_TRUE(name.IsEmpty());
-}
-
-TEST_F(GetLegacyDefaultBrowserInfoTest, InvalidNoValue) {
-  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey(
-      kRegKeyLegacyDefaultBrowserCommand));
-
-  CString name;
-  CString browser_path;
-  EXPECT_FAILED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
-  EXPECT_TRUE(browser_path.IsEmpty());
-  EXPECT_TRUE(name.IsEmpty());
-}
-
-TEST_F(GetLegacyDefaultBrowserInfoTest, InvalidPath) {
-  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(
-      kRegKeyLegacyDefaultBrowserCommand,
-      NULL,
-      _T("\"C:\\Program File\\iexplore.exe\" -nohome")));
-
-  CString name;
-  CString browser_path;
-  EXPECT_FAILED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
-  EXPECT_TRUE(browser_path.IsEmpty());
-  EXPECT_TRUE(name.IsEmpty());
-}
-
-TEST_F(GetLegacyDefaultBrowserInfoTest, InvalidUnquotedPath) {
-  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(
-      kRegKeyLegacyDefaultBrowserCommand,
-      NULL,
-      _T("C:\\Program Files\\Internet Explorer\\iexplore.exe -nohome")));
-
-  CString name;
-  CString browser_path;
-  EXPECT_FAILED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
-  EXPECT_TRUE(browser_path.IsEmpty());
-  EXPECT_TRUE(name.IsEmpty());
-}
-
-TEST_F(GetLegacyDefaultBrowserInfoTest, InvalidUnquotedDirectory) {
-  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(
-      kRegKeyLegacyDefaultBrowserCommand,
-      NULL,
-      _T("C:\\Program Files\\Internet Explorer\\")));
-
-  CString name;
-  CString browser_path;
-  EXPECT_FAILED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
-  EXPECT_TRUE(browser_path.IsEmpty());
-  EXPECT_TRUE(name.IsEmpty());
-}
-
-TEST_F(GetLegacyDefaultBrowserInfoTest, InvalidQuotedDirectory) {
-  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(
-      kRegKeyLegacyDefaultBrowserCommand,
-      NULL,
-      _T("\"C:\\Program Files\\Internet Explorer\\\" -nohome")));
-
-  CString name;
-  CString browser_path;
-  EXPECT_FAILED(GetLegacyDefaultBrowserInfo(&name, &browser_path));
-  EXPECT_TRUE(browser_path.IsEmpty());
-  EXPECT_TRUE(name.IsEmpty());
-}
-
-}  // namespace omaha
-
diff --git a/common/build.scons b/common/build.scons
index 5bee199..a1d601d 100644
--- a/common/build.scons
+++ b/common/build.scons
@@ -1,4 +1,4 @@
-# Copyright 2009 Google Inc.
+# Copyright 2009-2010 Google Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -15,85 +15,52 @@
 
 Import('env')
 
-env.BuildSConscript('logging')
-env.BuildSConscript('security')
-
-inputs = [
-    'accounts.cc',
-    'app_util.cc',
-    'atl_regexp.cc',
-    'browser_utils.cc',
-    'cgi.cc',
-    'clipboard.cc',
-    'commands.cc',
-    'crc.cc',
-    'debug.cc',
-    'disk.cc',
-    'dynamic_link_dbghelp.cc',
-    'dynamic_link_kernel32.cc',
-    'encrypt.cc',
-    'error.cc',
-    'exception_barrier.cc',
-    'exception_barrier_lowlevel.asm',
-    'exception_utils.cc',
-    'extractor.cc',
-    'file.cc',
-    'file_reader.cc',
-    'file_store.cc',
-    'file_ver.cc',
-    'firewall_product_detection.cc',
-    'highres_timer-win32.cc',
-    'localization.cc',
-    'logging.cc',
-    'md5.cc',
-    'module_utils.cc',
-    'omaha_version.cc',
-    'path.cc',
-    'pe_utils.cc',
-    'popup_menu.cc',
-    'process.cc',
-    'proc_utils.cc',
-    'queue_timer.cc',
-    'reactor.cc',
-    'reg_key.cc',
-    'regexp.cc',
-    'registry_hive.cc',
-    'registry_monitor_manager.cc',
-    'registry_store.cc',
-    'serializable_object.cc',
-    'service_utils.cc',
-    'sha.cc',
-    'shell.cc',
-    'shutdown_handler.cc',
-    'signatures.cc',
-    'signaturevalidator.cc',
-    'single_instance.cc',
-    'sta.cc',
-    'string.cc',
-    'synchronized.cc',
-    'system.cc',
-    'system_info.cc',
-    '../third_party/smartany/shared_any.cc',
-    'thread.cc',
-    'thread_pool.cc',
-    'time.cc',
-    'timer.cc',
-    'tr_rand.cc',
-    'unittest_utils.cc',
-    'user_info.cc',
-    'user_rights.cc',
-    'utils.cc',
-    'vista_utils.cc',
-    'vistautil.cc',
-    'window_utils.cc',
-    'wmi_query.cc',
-    'xml_utils.cc',
-    ]
-
 local_env = env.Clone()
 
-# Required by the exception barrier code.
-local_env.Append(ASFLAGS = ['/safeseh'])
+local_env['CPPPATH'] += [
+    '$OBJ_ROOT',  # Needed for generated files.
+    ]
+
+inputs = [
+    'app_registry_utils.cc',
+    'command_line.cc',
+    'command_line_builder.cc',
+    'config_manager.cc',
+    'event_logger.cc',
+    'experiment_labels.cc',
+    'extra_args_parser.cc',
+    'goopdate_command_line_validator.cc',
+    'goopdate_utils.cc',
+    'lang.cc',
+    'oem_install_utils.cc',
+    'ping.cc',
+    'ping_event.cc',
+    'scheduled_task_utils.cc',
+    'stats_uploader.cc',
+    'update3_utils.cc',
+    'update_request.cc',
+    'update_response.cc',
+    'webplugin_utils.cc',
+    'web_services_client.cc',
+    'xml_const.cc',
+    'xml_parser.cc',
+    '$LIB_DIR/logging.lib',       # Required by statsreport below
+    '$LIB_DIR/omaha3_idl.lib',    # Required by common
+    '$LIB_DIR/statsreport.lib',   # Required by common
+    ]
 
 # Build these into a library.
-local_env.ComponentLibrary('common', inputs)
+local_env.ComponentStaticLibrary('common', inputs)
+
+customization_test_env = env.Clone()
+customization_test_env.OmahaUnittest(
+    name='omaha_customization_unittest',
+    source=[
+        'omaha_customization_unittest.cc',
+    ],
+    LIBS=[
+        '$LIB_DIR/common',
+    ],
+    all_in_one=False,
+    COMPONENT_TEST_SIZE='small',
+)
diff --git a/common/cgi.cc b/common/cgi.cc
deleted file mode 100644
index 28aa4d1..0000000
--- a/common/cgi.cc
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/cgi.h"
-
-#include <tchar.h>
-#include "base/basictypes.h"
-#include "omaha/common/debug.h"
-
-namespace omaha {
-
-static uint32 _needs_escape[8] = {
-  0xffffffffL,
-  0xf80008fdL,
-  0x78000001L,
-  0xb8000001L,
-  0xffffffffL,
-  0xffffffffL,
-  0xffffffffL,
-  0xffffffffL
-};
-#define needs_escape(c) (_needs_escape[(c)>>5]&(1<<((c)&31)))
-
-// Here are a couple utility methods to change ints to hex chars & back.
-inline int int_to_hex_digit(int i) {
-  ASSERT((i >= 0) && (i <= 16), (_T("")));
-  return ((i < 10) ? (i + '0') : ((i - 10) + 'A'));
-}
-
-inline int hex_digit_to_int(TCHAR c) {
-  ASSERT(isxdigit(c), (_T("")));
-  return ((c >= 'a') ? ((c - 'a') + 10) :
-          (c >= 'A') ? ((c - 'A') + 10) :
-          (c - '0'));
-}
-
-bool CGI::EscapeString(const TCHAR* src, int srcn, TCHAR* dst, int dstn) {
-  ASSERT1(src != dst);  // In-place escaping will fail.
-  ASSERT1(srcn >= 0);
-  ASSERT1(dstn >= 1);
-  dstn--;   // Number of characters we can write, not including null terminator.
-
-  int i, j;
-  for (i = 0, j = 0; i < srcn && j < dstn; i++) {
-    TCHAR c = src[i];
-    if (c == ' ') {
-      dst[j++] = '+';
-    } else if (!needs_escape(c)) {
-      dst[j++] = c;
-    } else if (j + 3 > dstn) {
-      break;  // Escape sequence will not fit.
-    } else {
-      dst[j++] = '%';
-      dst[j++] = static_cast<TCHAR>(int_to_hex_digit((c >> 4) & 0xf));
-      dst[j++] = static_cast<TCHAR>(int_to_hex_digit(c & 0xf));
-    }
-  }
-  dst[j] = '\0';
-  return i == srcn;
-}
-
-bool CGI::UnescapeString(const TCHAR* src, int srcn, TCHAR* dst, int dstn) {
-  ASSERT1(srcn >= 0);
-  ASSERT1(dstn >= 1);
-  dstn--;   // Number of characters we can write, not including null terminator.
-
-  int i, j;
-  for (i = 0, j = 0; i < srcn && j < dstn; ++j) {
-    TCHAR c = src[i++];
-    if (c == '+') {
-      dst[j] = ' ';
-    } else if (c != '%') {
-      dst[j] = c;
-    } else if (i + 2 > srcn) {
-      break;  // Escape sequence is incomplete.
-    } else if (!isxdigit(src[i]) || !isxdigit(src[i + 1])) {
-      break;  // Escape sequence isn't hex.
-    } else {
-      int num = hex_digit_to_int(src[i++]) << 4;
-      num += hex_digit_to_int(src[i++]);
-      dst[j] = static_cast<TCHAR>(num);
-    }
-  }
-  dst[j] = '\0';
-  return i == srcn;
-}
-
-}  // namespace omaha
-
diff --git a/common/cgi_unittest.cc b/common/cgi_unittest.cc
deleted file mode 100644
index a4f1394..0000000
--- a/common/cgi_unittest.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Unit test for the CGI escape/unescape string..
-
-#include "base/scoped_ptr.h"
-#include "omaha/common/cgi.h"
-#include "omaha/common/string.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-void TestEscapeUnescape(const TCHAR* origin, const TCHAR* escaped) {
-  int origin_len = lstrlen(origin);
-  int buffer_len = origin_len * CGI::kEscapeFactor + 1;
-  scoped_array<TCHAR> escaped_buffer(new TCHAR[buffer_len]);
-  ASSERT_TRUE(CGI::EscapeString(origin, origin_len,
-                                escaped_buffer.get(), buffer_len));
-  ASSERT_STREQ(escaped_buffer.get(), escaped);
-
-  scoped_array<TCHAR> origin_buffer(new TCHAR[buffer_len]);
-  ASSERT_TRUE(CGI::UnescapeString(escaped_buffer.get(),
-                                  lstrlen(escaped_buffer.get()),
-                                  origin_buffer.get(), buffer_len));
-  ASSERT_STREQ(origin_buffer.get(), origin);
-}
-
-TEST(CGITEST, EscapeUnescape) {
-  // Regular chars.
-  TCHAR origin1[] = _T("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
-  TestEscapeUnescape(origin1, origin1);
-
-  String_ToLower(origin1);
-  TestEscapeUnescape(origin1, origin1);
-
-  // Special chars.
-  TCHAR origin2[] =  _T("^&`{}|][\"<>\\");    // NOLINT
-  TCHAR escaped2[] = _T("%5E%26%60%7B%7D%7C%5D%5B%22%3C%3E%5C");
-  TestEscapeUnescape(origin2, escaped2);
-
-  // Real case.
-  TCHAR origin3[] = _T("http://foo2.bar.google.com:80/pagead/conversion/1067912086/?ai=123&gclid=456&label=installation&value=0.0");                    // NOLINT
-  TCHAR escaped3[] = _T("http://foo2.bar.google.com:80/pagead/conversion/1067912086/%3Fai%3D123%26gclid%3D456%26label%3Dinstallation%26value%3D0.0");   // NOLINT
-  TestEscapeUnescape(origin3, escaped3);
-}
-
-}  // namespace omaha
-
diff --git a/common/clipboard.cc b/common/clipboard.cc
deleted file mode 100644
index 1302d9a..0000000
--- a/common/clipboard.cc
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/clipboard.h"
-#include "base/basictypes.h"
-#include "omaha/common/debug.h"
-
-namespace omaha {
-
-// Put the given string on the system clipboard
-void SetClipboard(const TCHAR *string_to_set) {
-  ASSERT(string_to_set, (L""));
-
-  int len = lstrlen(string_to_set);
-
-  //
-  // Note to developer: It is not always possible to step through this code
-  //  since the debugger will possibly steal the clipboard.  E.g. OpenClipboard
-  //  might succeed and EmptyClipboard might fail with "Thread does not have
-  //  clipboard open".
-  //
-
-  // Actual clipboard processing
-  if (::OpenClipboard(NULL)) {
-    BOOL b = ::EmptyClipboard();
-    ASSERT(b, (L"EmptyClipboard failed"));
-
-    // Include the terminating null
-    len++;
-
-    HANDLE copy_handle = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,
-                                       len * sizeof(TCHAR));
-    ASSERT(copy_handle, (L""));
-
-    byte* copy_data = reinterpret_cast<byte*>(::GlobalLock(copy_handle));
-    memcpy(copy_data, string_to_set, len * sizeof(TCHAR));
-    ::GlobalUnlock(copy_handle);
-
-#ifdef _UNICODE
-    HANDLE h = ::SetClipboardData(CF_UNICODETEXT, copy_handle);
-#else
-    HANDLE h = ::SetClipboardData(CF_TEXT, copy_handle);
-#endif
-
-    ASSERT(h != NULL, (L"SetClipboardData failed"));
-    if (!h) {
-      ::GlobalFree(copy_handle);
-    }
-
-    VERIFY(::CloseClipboard(), (L""));
-  } else {
-    ASSERT(false, (L"OpenClipboard failed - %i", ::GetLastError()));
-  }
-}
-
-}  // namespace omaha
-
diff --git a/common/command_line.cc b/common/command_line.cc
new file mode 100644
index 0000000..c7e78f6
--- /dev/null
+++ b/common/command_line.cc
@@ -0,0 +1,86 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/command_line.h"
+#include "omaha/base/command_line_parser.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/common/goopdate_command_line_validator.h"
+
+namespace omaha {
+
+// Returns a pointer to the second token in the cmd_line parameter or an
+// empty string. See the implementation in vc7\crt\src\wincmdln.c
+//
+// TODO(omaha): consider moving this function into the tiny shell, as it
+// is logically part of our modified runtime environment.
+TCHAR* GetCmdLineTail(const TCHAR* cmd_line) {
+  ASSERT1(cmd_line);
+  bool in_quote = false;
+
+  // Skip past program name (first token in command line).
+  // Check for and handle quoted program name.
+
+  while ((*cmd_line > _T(' ')) ||
+         (*cmd_line && in_quote)) {
+    // Flip the in_quote if current character is '"'.
+    if (*cmd_line == _T('"')) {
+      in_quote = !in_quote;
+    }
+    ++cmd_line;
+  }
+
+  // Skip past any white space preceeding the second token.
+  while (*cmd_line && (*cmd_line <= _T(' '))) {
+    cmd_line++;
+  }
+
+  return const_cast<TCHAR*>(cmd_line);
+}
+
+// Assumption: The metainstaller has verified that space ' ' and
+// double quote '"' characters do not appear in the "extra" arguments.
+// This is important so that attackers can't create tags that provide a valid
+// extra argument string followed by other commands.
+HRESULT ParseCommandLine(const TCHAR* cmd_line, CommandLineArgs* args) {
+  ASSERT1(cmd_line);
+  ASSERT1(args);
+  CORE_LOG(L3, (_T("[ParseCommandLine][%s]"), cmd_line));
+
+  CommandLineParser parser;
+  HRESULT hr = parser.ParseFromString(cmd_line);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[ParseCommandLine][ParseFromString failed][0x%x]"), hr));
+    return hr;
+  }
+
+  GoopdateCommandLineValidator validator;
+  hr = validator.Setup();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[ParseCommandLine][Validator Setup failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = validator.Validate(&parser, args);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[ParseCommandLine][Validate failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+}  // namespace omaha
+
diff --git a/common/command_line.h b/common/command_line.h
new file mode 100644
index 0000000..a465f9f
--- /dev/null
+++ b/common/command_line.h
@@ -0,0 +1,151 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// TODO(omaha): consider making all what can be passed on the command line
+// "arguments". Our terminology to separate them in commands and options is not
+// consistent.
+
+#ifndef OMAHA_COMMON_COMMAND_LINE_H_
+#define OMAHA_COMMON_COMMAND_LINE_H_
+
+#include <tchar.h>
+#include <atlstr.h>
+#include <vector>
+#include "omaha/base/constants.h"
+#include "omaha/base/browser_utils.h"
+#include "omaha/common/const_goopdate.h"
+
+namespace omaha {
+
+// Replacement for the C runtime function to process the command line.
+// The first token of the command line in Windows is the process name.
+// What gets passed to WinMain by the C runtime must not include the first
+// token. Since our tiny shell does not use the C runtime we must handle
+// the command line by ourselves.
+TCHAR* GetCmdLineTail(const TCHAR* cmd_line);
+
+struct CommandLineAppArgs {
+  CommandLineAppArgs()
+      : app_guid(GUID_NULL),
+        needs_admin(NEEDS_ADMIN_NO) {}
+
+  GUID app_guid;
+  CString app_name;
+  NeedsAdmin needs_admin;
+  CString ap;
+  CString tt_token;
+  CString encoded_installer_data;
+  CString install_data_index;
+  CString experiment_labels;
+};
+
+// Values may be sent in pings or stats. Do not remove or reuse existing values.
+typedef enum CommandLineMode {
+  COMMANDLINE_MODE_UNKNOWN = 0,
+  COMMANDLINE_MODE_NOARGS = 1,
+  COMMANDLINE_MODE_CORE = 2,
+  COMMANDLINE_MODE_SERVICE = 3,
+  COMMANDLINE_MODE_REGSERVER = 4,
+  COMMANDLINE_MODE_UNREGSERVER = 5,
+  COMMANDLINE_MODE_NETDIAGS = 6,
+  COMMANDLINE_MODE_CRASH = 7,
+  COMMANDLINE_MODE_REPORTCRASH = 8,
+  COMMANDLINE_MODE_INSTALL = 9,
+  COMMANDLINE_MODE_UPDATE = 10,
+  // Obsolete: COMMANDLINE_MODE_IG = 11,
+  COMMANDLINE_MODE_HANDOFF_INSTALL = 12,
+  // Obsolete: COMMANDLINE_MODE_UG = 13,
+  COMMANDLINE_MODE_UA = 14,
+  COMMANDLINE_MODE_RECOVER = 15,
+  COMMANDLINE_MODE_WEBPLUGIN = 16,
+  COMMANDLINE_MODE_CODE_RED_CHECK = 17,
+  COMMANDLINE_MODE_COMSERVER = 18,
+  // Obsolete: COMMANDLINE_MODE_LEGACYUI = 19,
+  // Obsolete: COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF = 20,
+  COMMANDLINE_MODE_REGISTER_PRODUCT = 21,
+  COMMANDLINE_MODE_UNREGISTER_PRODUCT = 22,
+  COMMANDLINE_MODE_SERVICE_REGISTER = 23,
+  COMMANDLINE_MODE_SERVICE_UNREGISTER = 24,
+  COMMANDLINE_MODE_CRASH_HANDLER = 25,
+  COMMANDLINE_MODE_COMBROKER = 26,
+  COMMANDLINE_MODE_ONDEMAND = 27,
+  COMMANDLINE_MODE_MEDIUM_SERVICE = 28,
+  COMMANDLINE_MODE_UNINSTALL = 29,
+  COMMANDLINE_MODE_PING = 30,
+};
+
+struct CommandLineExtraArgs {
+  CommandLineExtraArgs()
+      : installation_id(GUID_NULL),
+        browser_type(BROWSER_UNKNOWN),
+        usage_stats_enable(TRISTATE_NONE),
+        runtime_only(false) {}
+
+  CString bundle_name;
+  GUID installation_id;
+  CString brand_code;
+  CString client_id;
+  CString experiment_labels;
+  CString referral_id;
+  CString language;
+  BrowserType browser_type;
+  Tristate usage_stats_enable;
+  bool runtime_only;
+
+  std::vector<CommandLineAppArgs> apps;
+};
+
+struct CommandLineArgs {
+  CommandLineArgs()
+      : mode(COMMANDLINE_MODE_UNKNOWN),
+        is_interactive_set(false),
+        is_machine_set(false),
+        is_crash_handler_disabled(false),
+        is_install_elevated(false),
+        is_silent_set(false),
+        is_eula_required_set(false),
+        is_offline_set(false),
+        is_oem_set(false) {}
+
+  CommandLineMode mode;
+  bool is_interactive_set;
+  bool is_machine_set;
+  bool is_crash_handler_disabled;
+  bool is_install_elevated;
+  bool is_silent_set;
+  bool is_eula_required_set;
+  bool is_offline_set;
+  bool is_oem_set;
+  CString extra_args_str;
+  CString app_args_str;
+  CString install_source;
+  CString crash_filename;
+  CString custom_info_filename;
+  CString legacy_manifest_path;
+  CString webplugin_urldomain;
+  CString webplugin_args;
+  CString code_red_metainstaller_path;
+  CString ping_string;
+  CString offline_dir;
+  CString session_id;
+  CommandLineExtraArgs extra;
+};
+
+// Parses the goopdate command line.
+HRESULT ParseCommandLine(const TCHAR* cmd_line, CommandLineArgs* args);
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_COMMAND_LINE_H_
diff --git a/common/command_line_builder.cc b/common/command_line_builder.cc
new file mode 100644
index 0000000..8c24041
--- /dev/null
+++ b/common/command_line_builder.cc
@@ -0,0 +1,496 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/command_line_builder.h"
+#include <shellapi.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/common/command_line.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/const_goopdate.h"
+
+namespace omaha {
+
+CommandLineBuilder::CommandLineBuilder(CommandLineMode mode)
+    : mode_(mode),
+      is_interactive_set_(false),
+      is_machine_set_(false),
+      is_silent_set_(false),
+      is_eula_required_set_(false) {
+}
+
+CommandLineBuilder::~CommandLineBuilder() {
+}
+
+void CommandLineBuilder::set_is_interactive_set(bool is_interactive_set) {
+  ASSERT1(mode_ == COMMANDLINE_MODE_REPORTCRASH);
+  is_interactive_set_ = is_interactive_set;
+}
+
+void CommandLineBuilder::set_is_machine_set(bool is_machine_set) {
+  ASSERT1(mode_ == COMMANDLINE_MODE_REPORTCRASH);
+  is_machine_set_ = is_machine_set;
+}
+
+void CommandLineBuilder::set_is_silent_set(bool is_silent_set) {
+  ASSERT1(mode_ == COMMANDLINE_MODE_INSTALL ||
+          mode_ == COMMANDLINE_MODE_HANDOFF_INSTALL);
+  is_silent_set_ = is_silent_set;
+}
+
+void CommandLineBuilder::set_is_eula_required_set(bool is_eula_required_set) {
+  ASSERT1(mode_ == COMMANDLINE_MODE_INSTALL ||
+          mode_ == COMMANDLINE_MODE_HANDOFF_INSTALL);
+  is_eula_required_set_ = is_eula_required_set;
+}
+
+void CommandLineBuilder::set_extra_args(const CString& extra_args) {
+  ASSERT1(mode_ == COMMANDLINE_MODE_INSTALL ||
+          mode_ == COMMANDLINE_MODE_HANDOFF_INSTALL ||
+          mode_ == COMMANDLINE_MODE_REGISTER_PRODUCT ||
+          mode_ == COMMANDLINE_MODE_UNREGISTER_PRODUCT);
+  extra_args_ = extra_args;
+}
+
+void CommandLineBuilder::set_app_args(const CString& app_args) {
+  ASSERT1(mode_ == COMMANDLINE_MODE_INSTALL ||
+          mode_ == COMMANDLINE_MODE_HANDOFF_INSTALL);
+  app_args_ = app_args;
+}
+
+void CommandLineBuilder::set_install_source(const CString& install_source) {
+  ASSERT1(mode_ == COMMANDLINE_MODE_WEBPLUGIN ||
+          mode_ == COMMANDLINE_MODE_INSTALL ||
+          mode_ == COMMANDLINE_MODE_HANDOFF_INSTALL ||
+          mode_ == COMMANDLINE_MODE_UA);
+  install_source_ = install_source;
+}
+
+void CommandLineBuilder::set_session_id(const CString& session_id) {
+  ASSERT1(mode_ == COMMANDLINE_MODE_INSTALL ||
+          mode_ == COMMANDLINE_MODE_HANDOFF_INSTALL ||
+          mode_ == COMMANDLINE_MODE_UPDATE);
+  session_id_ = session_id;
+}
+
+void CommandLineBuilder::set_crash_filename(const CString& crash_filename) {
+  ASSERT1(mode_ == COMMANDLINE_MODE_REPORTCRASH);
+  crash_filename_ = crash_filename;
+}
+
+void CommandLineBuilder::set_custom_info_filename(
+    const CString& custom_info_filename) {
+  ASSERT1(mode_ == COMMANDLINE_MODE_REPORTCRASH);
+  custom_info_filename_ = custom_info_filename;
+}
+
+void CommandLineBuilder::set_webplugin_url_domain(
+    const CString& webplugin_url_domain) {
+  ASSERT1(mode_ == COMMANDLINE_MODE_WEBPLUGIN);
+  webplugin_url_domain_ = webplugin_url_domain;
+}
+
+void CommandLineBuilder::set_webplugin_args(const CString& webplugin_args) {
+  ASSERT1(mode_ == COMMANDLINE_MODE_WEBPLUGIN);
+  webplugin_args_ = webplugin_args;
+}
+
+void CommandLineBuilder::set_code_red_metainstaller_path(
+    const CString& code_red_metainstaller_path) {
+  ASSERT1(mode_ == COMMANDLINE_MODE_RECOVER);
+  code_red_metainstaller_path_ = code_red_metainstaller_path;
+}
+
+void CommandLineBuilder::set_ping_string(const CString& ping_string) {
+  ASSERT1(mode_ == COMMANDLINE_MODE_PING);
+  ping_string_ = ping_string;
+}
+
+void CommandLineBuilder::SetOfflineDir(const CString& offline_dir) {
+  ASSERT1(mode_ == COMMANDLINE_MODE_HANDOFF_INSTALL);
+  offline_dir_ = offline_dir;
+  ::PathRemoveBackslash(CStrBuf(offline_dir_, MAX_PATH));
+}
+
+CString CommandLineBuilder::GetCommandLineArgs() const {
+  CString cmd_line_args;
+
+  switch (mode_) {
+    case COMMANDLINE_MODE_NOARGS:
+      cmd_line_args.Empty();
+      break;
+    case COMMANDLINE_MODE_CORE:
+      cmd_line_args = GetCore();
+      break;
+    case COMMANDLINE_MODE_SERVICE:
+      cmd_line_args = GetService();
+      break;
+    case COMMANDLINE_MODE_REGSERVER:
+      cmd_line_args = GetRegServer();
+      break;
+    case COMMANDLINE_MODE_UNREGSERVER:
+      cmd_line_args = GetUnregServer();
+      break;
+    case COMMANDLINE_MODE_NETDIAGS:
+      cmd_line_args = GetNetDiags();
+      break;
+    case COMMANDLINE_MODE_CRASH:
+      cmd_line_args = GetCrash();
+      break;
+    case COMMANDLINE_MODE_REPORTCRASH:
+      cmd_line_args = GetReportCrash();
+      break;
+    case COMMANDLINE_MODE_INSTALL:
+      cmd_line_args = GetInstall();
+      break;
+    case COMMANDLINE_MODE_UPDATE:
+      cmd_line_args = GetUpdate();
+      break;
+    case COMMANDLINE_MODE_HANDOFF_INSTALL:
+      cmd_line_args = GetHandoffInstall();
+      break;
+    case COMMANDLINE_MODE_UA:
+      cmd_line_args = GetUA();
+      break;
+    case COMMANDLINE_MODE_RECOVER:
+      cmd_line_args = GetRecover();
+      break;
+    case COMMANDLINE_MODE_WEBPLUGIN:
+      cmd_line_args = GetWebPlugin();
+      break;
+    case COMMANDLINE_MODE_CODE_RED_CHECK:
+      cmd_line_args = GetCodeRedCheck();
+      break;
+    case COMMANDLINE_MODE_COMSERVER:
+      cmd_line_args = GetComServer();
+      break;
+    case COMMANDLINE_MODE_REGISTER_PRODUCT:
+      cmd_line_args = GetRegisterProduct();
+      break;
+    case COMMANDLINE_MODE_UNREGISTER_PRODUCT:
+      cmd_line_args = GetUnregisterProduct();
+      break;
+    case COMMANDLINE_MODE_SERVICE_REGISTER:
+      cmd_line_args = GetServiceRegister();
+      break;
+    case COMMANDLINE_MODE_SERVICE_UNREGISTER:
+      cmd_line_args = GetServiceUnregister();
+      break;
+    case COMMANDLINE_MODE_CRASH_HANDLER:
+      cmd_line_args = GetCrashHandler();
+      break;
+    case COMMANDLINE_MODE_COMBROKER:
+      cmd_line_args = GetComBroker();
+      break;
+    case COMMANDLINE_MODE_ONDEMAND:
+      cmd_line_args = GetOnDemand();
+      break;
+    case COMMANDLINE_MODE_MEDIUM_SERVICE:
+      cmd_line_args = GetMediumService();
+      break;
+    case COMMANDLINE_MODE_UNINSTALL:
+      cmd_line_args = GetUninstall();
+      break;
+    case COMMANDLINE_MODE_PING:
+      cmd_line_args = GetPing();
+      break;
+    case COMMANDLINE_MODE_UNKNOWN:
+    default:
+      ASSERT1(false);
+      break;
+  }
+
+#ifdef _DEBUG
+  CString full_command_line;
+  SafeCStringFormat(&full_command_line, _T("gu.exe %s"), cmd_line_args);
+  CommandLineArgs args;
+  ASSERT1(SUCCEEDED(ParseCommandLine(full_command_line, &args)));
+#endif
+
+  return cmd_line_args;
+}
+
+CString CommandLineBuilder::GetCommandLine(const CString& program_name) const {
+  // Do not pass the results of the the /update builder to GoogleUpdate.exe.
+  // The command line for /update is intended to be passed to a metainstaller.
+  // See GetUpdate() for more information.
+  ASSERT1(COMMANDLINE_MODE_UPDATE != mode_ ||
+          -1 == program_name.Find(kOmahaShellFileName));
+
+  // Always enclose the program name in double quotes.
+  CString enclosed_program_name(program_name);
+  EnclosePath(&enclosed_program_name);
+  CString cmd_line;
+  SafeCStringFormat(&cmd_line, _T("%s %s"),
+                    enclosed_program_name,
+                    GetCommandLineArgs());
+  return cmd_line;
+}
+
+CString CommandLineBuilder::GetSingleSwitch(const CString& switch_name) const {
+  CString cmd_line;
+  cmd_line.Format(_T("/%s"), switch_name);
+  return cmd_line;
+}
+
+CString CommandLineBuilder::GetCore() const {
+  return GetSingleSwitch(kCmdLineCore);
+}
+
+CString CommandLineBuilder::GetCrashHandler() const {
+  return GetSingleSwitch(kCmdLineCrashHandler);
+}
+
+CString CommandLineBuilder::GetService() const {
+  return GetSingleSwitch(kCmdLineService);
+}
+
+CString CommandLineBuilder::GetServiceRegister() const {
+  return GetSingleSwitch(kCmdLineRegisterService);
+}
+
+CString CommandLineBuilder::GetServiceUnregister() const {
+  return GetSingleSwitch(kCmdLineUnregisterService);
+}
+
+CString CommandLineBuilder::GetRegServer() const {
+  return GetSingleSwitch(kCmdRegServer);
+}
+
+CString CommandLineBuilder::GetUnregServer() const {
+  return GetSingleSwitch(kCmdUnregServer);
+}
+
+CString CommandLineBuilder::GetNetDiags() const {
+  return GetSingleSwitch(kCmdLineNetDiags);
+}
+
+CString CommandLineBuilder::GetCrash() const {
+  CString cmd_line = GetSingleSwitch(kCmdLineCrash);
+  return cmd_line;
+}
+
+CString CommandLineBuilder::GetReportCrash() const {
+  ASSERT1(!crash_filename_.IsEmpty());
+  if (crash_filename_.IsEmpty()) {
+    return CString();
+  }
+  CString cmd_line = GetSingleSwitch(kCmdLineReport);
+  if (is_interactive_set_) {
+    SafeCStringAppendFormat(&cmd_line, _T(" /%s"), kCmdLineInteractive);
+  }
+  CString enclosed_crash_filename_(crash_filename_);
+  EnclosePath(&enclosed_crash_filename_);
+  SafeCStringAppendFormat(&cmd_line, _T(" %s"), enclosed_crash_filename_);
+  if (is_machine_set()) {
+    SafeCStringAppendFormat(&cmd_line, _T(" /%s"), kCmdLineMachine);
+  }
+
+  if (!custom_info_filename_.IsEmpty()) {
+    CString enclosed_custom_info_filename_(custom_info_filename_);
+    EnclosePath(&enclosed_custom_info_filename_);
+    SafeCStringAppendFormat(&cmd_line, _T(" /%s %s"),
+                            kCmdLineCustomInfoFileName,
+                            enclosed_custom_info_filename_);
+  }
+
+  return cmd_line;
+}
+
+CString CommandLineBuilder::GetExtraAndAppArgs(
+    const TCHAR* extra_switch_name) const {
+  ASSERT1(extra_switch_name && *extra_switch_name);
+  ASSERT1(!extra_args_.IsEmpty());
+  if (extra_args_.IsEmpty()) {
+    return CString();
+  }
+
+  CString cmd_line;
+  CString enclosed_extra_args_(extra_args_);
+  EnclosePath(&enclosed_extra_args_);
+  SafeCStringFormat(&cmd_line, _T("/%s %s"),
+                    extra_switch_name,
+                    enclosed_extra_args_);
+
+  if (!app_args_.IsEmpty()) {
+    CString enclosed_app_args = app_args_;
+    EnclosePath(&enclosed_app_args);
+    SafeCStringAppendFormat(&cmd_line, _T(" /%s %s"),
+                            kCmdLineAppArgs,
+                            enclosed_app_args);
+  }
+
+  return cmd_line;
+}
+
+// Does not support /oem or /eularequired because we would never build that
+// internally.
+CString CommandLineBuilder::GetInstall() const {
+  CString cmd_line(GetExtraAndAppArgs(kCmdLineInstall));
+  if (cmd_line.IsEmpty()) {
+    return CString();
+  }
+
+  if (!install_source_.IsEmpty()) {
+    SafeCStringAppendFormat(&cmd_line, _T(" /%s %s"),
+                            kCmdLineInstallSource,
+                            install_source_);
+  }
+  if (!session_id_.IsEmpty()) {
+    SafeCStringAppendFormat(&cmd_line, _T(" /%s \"%s\""),
+                            kCmdLineSessionId,
+                            session_id_);
+  }
+  if (is_silent_set_) {
+    SafeCStringAppendFormat(&cmd_line, _T(" /%s"), kCmdLineSilent);
+  }
+  return cmd_line;
+}
+
+CString CommandLineBuilder::GetUpdate() const {
+  CString cmd_line;
+  cmd_line.Format(_T("/%s"), kCmdLineUpdate);
+  if (!session_id_.IsEmpty()) {
+    SafeCStringAppendFormat(&cmd_line, _T(" /%s \"%s\""),
+                            kCmdLineSessionId,
+                            session_id_);
+  }
+  return cmd_line;
+}
+
+CString CommandLineBuilder::GetHandoffInstall() const {
+  CString cmd_line(GetExtraAndAppArgs(kCmdLineAppHandoffInstall));
+  if (cmd_line.IsEmpty()) {
+    return CString();
+  }
+
+  if (!install_source_.IsEmpty()) {
+    SafeCStringAppendFormat(&cmd_line, _T(" /%s %s"),
+                            kCmdLineInstallSource,
+                            install_source_);
+  }
+  if (!session_id_.IsEmpty()) {
+    SafeCStringAppendFormat(&cmd_line, _T(" /%s \"%s\""),
+                            kCmdLineSessionId,
+                            session_id_);
+  }
+  if (is_silent_set_) {
+    SafeCStringAppendFormat(&cmd_line, _T(" /%s"), kCmdLineSilent);
+  }
+  if (is_eula_required_set_) {
+    SafeCStringAppendFormat(&cmd_line, _T(" /%s"), kCmdLineEulaRequired);
+  }
+  if (!offline_dir_.IsEmpty()) {
+    SafeCStringAppendFormat(&cmd_line, _T(" /%s \"%s\""),
+                            kCmdLineOfflineDir,
+                            offline_dir_);
+  }
+  return cmd_line;
+}
+
+CString CommandLineBuilder::GetUA() const {
+  ASSERT1(!install_source_.IsEmpty());
+  if (install_source_.IsEmpty()) {
+    return CString();
+  }
+
+  CString cmd_line(GetSingleSwitch(kCmdLineUpdateApps));
+  SafeCStringAppendFormat(&cmd_line, _T(" /%s %s"),
+                          kCmdLineInstallSource,
+                          install_source_);
+
+  return cmd_line;
+}
+
+CString CommandLineBuilder::GetRecover() const {
+  ASSERT1(!code_red_metainstaller_path_.IsEmpty());
+  if (code_red_metainstaller_path_.IsEmpty()) {
+    return CString();
+  }
+  CString cmd_line;
+  SafeCStringFormat(&cmd_line, _T("/%s %s"),
+                    kCmdLineRecover,
+                    code_red_metainstaller_path_);
+  return cmd_line;
+}
+
+CString CommandLineBuilder::GetWebPlugin() const {
+  ASSERT1(!webplugin_url_domain_.IsEmpty());
+  ASSERT1(!webplugin_args_.IsEmpty());
+  ASSERT1(!install_source_.IsEmpty());
+  if (webplugin_url_domain_.IsEmpty() ||
+      webplugin_args_.IsEmpty() ||
+      install_source_.IsEmpty()) {
+    return CString();
+  }
+  CString cmd_line;
+  CString enclosed_webplugin_url_domain_(webplugin_url_domain_);
+  CString enclosed_webplugin_args_(webplugin_args_);
+  EnclosePath(&enclosed_webplugin_url_domain_);
+  EnclosePath(&enclosed_webplugin_args_);
+  // TODO(omaha): Do we want this to handle the urlencoding for us?
+  SafeCStringFormat(&cmd_line, _T("/%s %s %s /%s %s"),
+                    kCmdLineWebPlugin,
+                    enclosed_webplugin_url_domain_,
+                    enclosed_webplugin_args_,
+                    kCmdLineInstallSource,
+                    install_source_);
+  return cmd_line;
+}
+
+CString CommandLineBuilder::GetCodeRedCheck() const {
+  return GetSingleSwitch(kCmdLineCodeRedCheck);
+}
+
+CString CommandLineBuilder::GetComServer() const {
+  return kCmdLineComServerDash;
+}
+
+CString CommandLineBuilder::GetComBroker() const {
+  return kCmdLineComBroker;
+}
+
+CString CommandLineBuilder::GetOnDemand() const {
+  return kCmdLineOnDemand;
+}
+
+CString CommandLineBuilder::GetMediumService() const {
+  return GetSingleSwitch(kCmdLineMediumService);
+}
+
+CString CommandLineBuilder::GetUninstall() const {
+  return GetSingleSwitch(kCmdLineUninstall);
+}
+
+CString CommandLineBuilder::GetRegisterProduct() const {
+  ASSERT1(app_args_.IsEmpty());
+  return GetExtraAndAppArgs(kCmdLineRegisterProduct);
+}
+
+CString CommandLineBuilder::GetUnregisterProduct() const {
+  ASSERT1(app_args_.IsEmpty());
+  return GetExtraAndAppArgs(kCmdLineUnregisterProduct);
+}
+
+CString CommandLineBuilder::GetPing() const {
+  CString cmd_line;
+  SafeCStringFormat(&cmd_line, _T("/%s %s"), kCmdLinePing, ping_string_);
+  return cmd_line;
+}
+
+}  // namespace omaha
diff --git a/common/command_line_builder.h b/common/command_line_builder.h
new file mode 100644
index 0000000..bdd9e68
--- /dev/null
+++ b/common/command_line_builder.h
@@ -0,0 +1,145 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_COMMAND_LINE_BUILDER_H__
+#define OMAHA_COMMON_COMMAND_LINE_BUILDER_H__
+
+#include <windows.h>
+#include <atlstr.h>
+
+#include "base/basictypes.h"
+#include "omaha/common/command_line.h"
+
+namespace omaha {
+
+// This class builds a GoogleUpdate.exe command line and makes sure it's
+// valid against the GoopdateCommandLineValidator.
+class CommandLineBuilder {
+ public:
+  explicit CommandLineBuilder(CommandLineMode mode);
+  ~CommandLineBuilder();
+
+  CommandLineMode mode() const { return mode_; }
+
+  bool is_interactive_set() const { return is_interactive_set_; }
+  void set_is_interactive_set(bool is_interactive_set);
+
+  bool is_machine_set() const { return is_machine_set_; }
+  void set_is_machine_set(bool is_machine_set);
+
+  bool is_silent_set() const { return is_silent_set_; }
+  void set_is_silent_set(bool is_silent_set);
+
+  bool is_eula_required_set() const { return is_eula_required_set_; }
+  void set_is_eula_required_set(bool is_eula_required_set);
+
+  CString extra_args() const { return extra_args_; }
+  void set_extra_args(const CString& extra_args);
+
+  CString app_args() const { return app_args_; }
+  void set_app_args(const CString& app_args);
+
+  CString install_source() const { return install_source_; }
+  void set_install_source(const CString& install_source);
+
+  CString session_id() const { return session_id_; }
+  void set_session_id(const CString& session_id);
+
+  CString crash_filename() const { return crash_filename_; }
+  void set_crash_filename(const CString& crash_filename);
+
+  CString custom_info_filename() const { return custom_info_filename_; }
+  void set_custom_info_filename(const CString& custom_info_filename);
+
+  CString webplugin_url_domain() const { return webplugin_url_domain_; }
+  void set_webplugin_url_domain(const CString& webplugin_url_domain);
+
+  CString webplugin_args() const { return webplugin_args_; }
+  void set_webplugin_args(const CString& webplugin_args);
+
+  CString code_red_metainstaller_path() const {
+    return code_red_metainstaller_path_;
+  }
+  void set_code_red_metainstaller_path(
+      const CString& code_red_metainstaller_path);
+
+  CString ping_string() const { return ping_string_; }
+  void set_ping_string(const CString& ping_string);
+
+  CString offline_dir() const { return offline_dir_; }
+
+  // Sets the offline directory after removing any trailing backslash.
+  void SetOfflineDir(const CString& offline_dir);
+
+  // Outputs the proper command line string for the properties that are set.
+  // If the properties aren't in a valid combination, function will assert.
+  CString GetCommandLineArgs()  const;
+
+  CString GetCommandLine(const CString& program_name) const;
+
+ private:
+  CString GetSingleSwitch(const CString& switch_name) const;
+  CString GetExtraAndAppArgs(const TCHAR* extra_switch_name) const;
+
+  CString GetCore() const;
+  CString GetCrashHandler() const;
+  CString GetService() const;
+  CString GetServiceRegister() const;
+  CString GetServiceUnregister() const;
+  CString GetRegServer() const;
+  CString GetUnregServer() const;
+  CString GetNetDiags() const;
+  CString GetCrash() const;
+  CString GetReportCrash() const;
+  CString GetInstall() const;
+  CString GetUpdate() const;
+  CString GetHandoffInstall() const;
+  CString GetUA() const;
+  CString GetRecover() const;
+  CString GetWebPlugin() const;
+  CString GetCodeRedCheck() const;
+  CString GetComServer() const;
+  CString GetComBroker() const;
+  CString GetOnDemand() const;
+  CString GetMediumService() const;
+  CString GetUninstall() const;
+  CString GetRegisterProduct() const;
+  CString GetUnregisterProduct() const;
+  CString GetPing() const;
+
+  const CommandLineMode mode_;
+  bool is_interactive_set_;
+  bool is_machine_set_;
+  bool is_silent_set_;
+  bool is_eula_required_set_;
+  CString extra_args_;
+  CString app_args_;
+  CString install_source_;
+  CString crash_filename_;
+  CString custom_info_filename_;
+  CString webplugin_url_domain_;
+  CString webplugin_args_;
+  CString code_red_metainstaller_path_;
+  CString ping_string_;
+  CString offline_dir_;
+  CString session_id_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(CommandLineBuilder);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_COMMAND_LINE_BUILDER_H__
+
diff --git a/common/command_line_builder_unittest.cc b/common/command_line_builder_unittest.cc
new file mode 100644
index 0000000..41f053d
--- /dev/null
+++ b/common/command_line_builder_unittest.cc
@@ -0,0 +1,446 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/command_line_builder.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(CommandLineBuilder, BuildUnknown) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_UNKNOWN);
+  ExpectAsserts expect_asserts;
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T(""), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildNoArgs) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_NOARGS);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T(""), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildCore) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_CORE);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/c"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildService) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_SERVICE);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/svc"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildMediumService) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_MEDIUM_SERVICE);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/medsvc"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildRegServer) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_REGSERVER);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/regserver"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildUnregServer) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_UNREGSERVER);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/unregserver"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildNetDiags) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_NETDIAGS);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/netdiags"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildCrashNoFilename) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_CRASH);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/crash"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildReportCrash) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
+  ExpectAsserts expect_asserts;  // Missing filename.
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T(""), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildReportCrashWithFilename) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
+  builder.set_crash_filename(_T("foo.dmp"));
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/report \"foo.dmp\""), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildReportCrashWithFilenameMachine) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
+  builder.set_crash_filename(_T("foo.dmp"));
+  builder.set_is_machine_set(true);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/report \"foo.dmp\" /machine"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildReportCrashWithEnclosedFilename) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
+  builder.set_crash_filename(_T("\"foo.dmp\""));
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/report \"foo.dmp\""), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildReportCrashWithCustomInfo) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
+  ExpectAsserts expect_asserts;  // Missing filename.
+  builder.set_custom_info_filename(_T("foo.txt"));
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T(""), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildReportCrashWithFileanameWithCustomInfo) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
+  builder.set_crash_filename(_T("foo.dmp"));
+  builder.set_custom_info_filename(_T("foo.txt"));
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/report \"foo.dmp\" /custom_info_filename \"foo.txt\""),
+               cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildReportCrashWithFileanameWithCustomInfoMachine) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
+  builder.set_crash_filename(_T("foo.dmp"));
+  builder.set_custom_info_filename(_T("foo.txt"));
+  builder.set_is_machine_set(true);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(
+      _T("/report \"foo.dmp\" /machine /custom_info_filename \"foo.txt\""),
+      cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildReportCrashWithEnclosedFileanameWithCustomInfo) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
+  builder.set_crash_filename(_T("\"foo.dmp\""));
+  builder.set_custom_info_filename(_T("\"foo.txt\""));
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/report \"foo.dmp\" /custom_info_filename \"foo.txt\""),
+               cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildReportCrashInteractiveWithFilename) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
+  builder.set_crash_filename(_T("foo.dmp"));
+  builder.set_is_interactive_set(true);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/report /i \"foo.dmp\""), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildReportCrashMachineInteractiveWithFilename) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
+  builder.set_crash_filename(_T("foo.dmp"));
+  builder.set_is_machine_set(true);
+  builder.set_is_interactive_set(true);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/report /i \"foo.dmp\" /machine"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildInstall) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_INSTALL);
+  ExpectAsserts expect_asserts;  // Missing parameters.
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T(""), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildInstallWithExtraArgs) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_INSTALL);
+  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+                         _T("appname=YouTubeUploader&needsadmin=False&")
+                         _T("lang=en"));
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/install \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+               _T("appname=YouTubeUploader&needsadmin=False&lang=en\""),
+               cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildInstallWithExtraArgsSilent) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_INSTALL);
+  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+                         _T("appname=YouTubeUploader&needsadmin=False&")
+                         _T("lang=en"));
+  builder.set_is_silent_set(true);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/install \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+               _T("appname=YouTubeUploader&needsadmin=False&lang=en\" /silent"),
+               cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildInstallWithExtraArgsSessionId) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_INSTALL);
+  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+                         _T("appname=YouTubeUploader&needsadmin=False&")
+                         _T("lang=en"));
+  builder.set_session_id(_T("{756dfdc2-0ef0-44b7-bfb1-21a4be6a1213}"));
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/install \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+               _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
+               _T(" /sessionid \"{756dfdc2-0ef0-44b7-bfb1-21a4be6a1213}\""),
+               cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildUpdate) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_UPDATE);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/update"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildUpdateWithSessionId) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_UPDATE);
+  builder.set_session_id(_T("{756dfdc2-0ef0-44b7-bfb1-21a4be6a1213}"));
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(
+      _T("/update /sessionid \"{756dfdc2-0ef0-44b7-bfb1-21a4be6a1213}\""),
+      cmd_line);
+}
+
+// The /update builder works when not used with GoogleUpdate.exe.
+TEST(CommandLineBuilder, BuildUpdateAndGetCommandLineWithNonGoogleUpdateExe) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_UPDATE);
+  CString cmd_line = builder.GetCommandLine(_T("C:\\GoogleUpdateSetup_en.exe"));
+  EXPECT_STREQ(_T("\"C:\\GoogleUpdateSetup_en.exe\" /update"), cmd_line);
+}
+
+// The /update builder should not be used with GoogleUpdate.exe directly.
+TEST(CommandLineBuilder, BuildUpdateAndGetCommandLineWithGoogleUpdateExe) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_UPDATE);
+  ExpectAsserts expect_asserts;
+  CString cmd_line = builder.GetCommandLine(_T("C:\\GoogleUpdate.exe"));
+  EXPECT_STREQ(_T("\"C:\\GoogleUpdate.exe\" /update"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildComServer) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_COMSERVER);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("-Embedding"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildComBroker) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_COMBROKER);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/broker"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildOnDemand) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_ONDEMAND);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/ondemand"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildCodeRedCheck) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_CODE_RED_CHECK);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/cr"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildWebPlugin) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_WEBPLUGIN);
+  ExpectAsserts expect_asserts;
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T(""), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildWebPluginWithUrlArgsAndInstallSource) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_WEBPLUGIN);
+  builder.set_webplugin_args(_T("piargs"));
+  builder.set_webplugin_url_domain(_T("http://www.google.com/"));
+  builder.set_install_source(_T("oneclick"));
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/pi \"http://www.google.com/\" \"piargs\"")
+               _T(" /installsource oneclick"),
+               cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildRecover) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_RECOVER);
+  ExpectAsserts expect_asserts;
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T(""), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildRecoverWithMIPath) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_RECOVER);
+  builder.set_code_red_metainstaller_path(_T("foo.exe"));
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/recover foo.exe"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildUA) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_UA);
+  ExpectAsserts expect_asserts;
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T(""), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildUAWithInstallSource) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_UA);
+  builder.set_install_source(_T("blah"));
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/ua /installsource blah"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildHandoffInstall) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL);
+  ExpectAsserts expect_asserts;  // Missing extra args.
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T(""), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildHandoffInstallWithExtraArgs) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL);
+  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+                         _T("appname=YouTubeUploader&needsadmin=False&")
+                         _T("lang=en"));
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/handoff \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+               _T("appname=YouTubeUploader&needsadmin=False&lang=en\""),
+               cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildHandoffInstallWithExtraArgsSessionId) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL);
+  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+                         _T("appname=YouTubeUploader&needsadmin=False&")
+                         _T("lang=en"));
+  builder.set_session_id(_T("{756dfdc2-0ef0-44b7-bfb1-21a4be6a1213}"));
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/handoff \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+               _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
+               _T(" /sessionid \"{756dfdc2-0ef0-44b7-bfb1-21a4be6a1213}\""),
+               cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildHandoffInstallWithExtraArgsOffline) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL);
+  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+                         _T("appname=YouTubeUploader&needsadmin=False&")
+                         _T("lang=en"));
+  builder.set_install_source(_T("offline"));
+  builder.SetOfflineDir(_T("c:\\offline_dir\\"));
+
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/handoff \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+               _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
+               _T(" /installsource offline")
+               _T(" /offlinedir \"c:\\offline_dir\""),
+               cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildHandoffInstallWithExtraArgsSilentOffline) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL);
+  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+                         _T("appname=YouTubeUploader&needsadmin=False&")
+                         _T("lang=en"));
+  builder.set_install_source(_T("offline"));
+  builder.set_is_silent_set(true);
+  builder.SetOfflineDir(_T("c:\\offline dir"));
+
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/handoff \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+               _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
+               _T(" /installsource offline")
+               _T(" /silent /offlinedir \"c:\\offline dir\""),
+               cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildHandoffWithAppArgsSilentOffline) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL);
+  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+                         _T("appname=YouTubeUploader&needsadmin=False&")
+                         _T("lang=en"));
+  builder.set_app_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+                       _T("installerdata=foobar%45"));
+  builder.set_install_source(_T("offline"));
+  builder.set_is_silent_set(true);
+  builder.SetOfflineDir(_T("c:\\offline dir"));
+
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/handoff \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+               _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
+               _T(" /appargs \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+               _T("installerdata=foobar%45\"")
+               _T(" /installsource offline")
+               _T(" /silent /offlinedir \"c:\\offline dir\""),
+               cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildHandoffWithAppArgsSilentOfflineEulaRequired) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL);
+  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+                         _T("appname=YouTubeUploader&needsadmin=False&")
+                         _T("lang=en"));
+  builder.set_app_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+                       _T("installerdata=foobar%45"));
+  builder.set_install_source(_T("offline"));
+  builder.set_is_silent_set(true);
+  builder.set_is_eula_required_set(true);
+  builder.SetOfflineDir(_T("c:\\offline dir"));
+
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/handoff \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+               _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
+               _T(" /appargs \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+               _T("installerdata=foobar%45\"")
+               _T(" /installsource offline")
+               _T(" /silent /eularequired /offlinedir \"c:\\offline dir\""),
+               cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildRegisterProduct) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_REGISTER_PRODUCT);
+  builder.set_extra_args(_T("appguid={7DD3DAE3-87F1-4CFE-8BF4-452C74421401}&")
+                         _T("appname=Google Toolbar&needsadmin=True&")
+                         _T("lang=en"));
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/registerproduct ")
+               _T("\"appguid={7DD3DAE3-87F1-4CFE-8BF4-452C74421401}&")
+               _T("appname=Google Toolbar&needsadmin=True&lang=en\""),
+               cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildUnregisterProduc) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_UNREGISTER_PRODUCT);
+  builder.set_extra_args(_T("appguid={7DD3DAE3-87F1-4CFE-8BF4-452C74421401}&")
+                         _T("needsadmin=True&lang=en"));
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/unregisterproduct ")
+               _T("\"appguid={7DD3DAE3-87F1-4CFE-8BF4-452C74421401}&")
+               _T("needsadmin=True&lang=en\""),
+               cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildUninstall) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_UNINSTALL);
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/uninstall"), cmd_line);
+}
+
+TEST(CommandLineBuilder, BuildPing) {
+  CommandLineBuilder builder(COMMANDLINE_MODE_PING);
+  builder.set_ping_string(_T("foo"));
+  CString cmd_line = builder.GetCommandLineArgs();
+  EXPECT_STREQ(_T("/ping foo"), cmd_line);
+}
+
+}  // namespace omaha
+
diff --git a/common/command_line_unittest.cc b/common/command_line_unittest.cc
new file mode 100644
index 0000000..758bdb8
--- /dev/null
+++ b/common/command_line_unittest.cc
@@ -0,0 +1,1233 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/utils.h"
+#include "omaha/common/command_line.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+// Used by extra_args_parser_unittest.cc.
+void VerifyCommandLineExtraArgs(const CommandLineExtraArgs& expected_val,
+                                const CommandLineExtraArgs& actual_val);
+
+namespace {
+
+#define YOUTUBEUPLOADEREN_TAG_WITHOUT_QUOTES \
+    _T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&") \
+    _T("appname=YouTubeUploader&needsadmin=False&lang=en")
+
+#define YOUTUBEUPLOADEREN_APP_ARGS_WITHOUT_QUOTES \
+    _T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&") \
+    _T("installerdata=YouTube%20Uploader%20Data")
+
+#define YOUTUBEUPLOADEREN_TAG \
+    _T("\"") YOUTUBEUPLOADEREN_TAG_WITHOUT_QUOTES _T("\"")
+
+#define YOUTUBEUPLOADEREN_APP_ARGS \
+    _T("\"") YOUTUBEUPLOADEREN_APP_ARGS_WITHOUT_QUOTES _T("\"")
+
+void VerifyCommandLineArgs(const CommandLineArgs& expected,
+                           const CommandLineArgs& actual) {
+  EXPECT_EQ(expected.mode, actual.mode);
+
+  EXPECT_EQ(expected.is_interactive_set, actual.is_interactive_set);
+  EXPECT_EQ(expected.is_machine_set, actual.is_machine_set);
+  EXPECT_EQ(expected.is_install_elevated, actual.is_install_elevated);
+  EXPECT_EQ(expected.is_silent_set, actual.is_silent_set);
+  EXPECT_EQ(expected.is_eula_required_set, actual.is_eula_required_set);
+  EXPECT_EQ(expected.is_offline_set, actual.is_offline_set);
+  EXPECT_EQ(expected.is_oem_set, actual.is_oem_set);
+
+  EXPECT_STREQ(expected.extra_args_str, actual.extra_args_str);
+  EXPECT_STREQ(expected.app_args_str, actual.app_args_str);
+  EXPECT_STREQ(expected.install_source, actual.install_source);
+  EXPECT_STREQ(expected.crash_filename, actual.crash_filename);
+  EXPECT_STREQ(expected.custom_info_filename, actual.custom_info_filename);
+  EXPECT_STREQ(expected.legacy_manifest_path, actual.legacy_manifest_path);
+  EXPECT_STREQ(expected.webplugin_urldomain, actual.webplugin_urldomain);
+  EXPECT_STREQ(expected.webplugin_args, actual.webplugin_args);
+  EXPECT_STREQ(expected.code_red_metainstaller_path,
+               actual.code_red_metainstaller_path);
+  EXPECT_STREQ(expected.offline_dir, actual.offline_dir);
+
+  VerifyCommandLineExtraArgs(expected.extra, actual.extra);
+}
+
+void VerifyArgsWithSingleYouTubeUploaderEnApp(
+    const CommandLineArgs& expected_without_app,
+    const CommandLineArgs& actual,
+    bool expect_app_args,
+    bool expect_language) {
+  CommandLineArgs expected(expected_without_app);
+
+  const GUID expected_guid = {0xA4F7B07B, 0xB9BD, 0x4A33,
+                              {0xB1, 0x36, 0x96, 0xD2, 0xAD, 0xFB, 0x60, 0xCB}};
+  CommandLineAppArgs app_args;
+  app_args.app_guid = expected_guid;
+  app_args.app_name = _T("YouTubeUploader");
+  app_args.needs_admin = NEEDS_ADMIN_NO;
+  if (expected.extra_args_str.IsEmpty()) {
+    expected.extra_args_str = YOUTUBEUPLOADEREN_TAG_WITHOUT_QUOTES;
+  }
+  if (expect_language) {
+    expected.extra.language = _T("en");
+  }
+  if (expect_app_args) {
+    expected.app_args_str =
+        YOUTUBEUPLOADEREN_APP_ARGS_WITHOUT_QUOTES;
+    app_args.encoded_installer_data = _T("YouTube%20Uploader%20Data");
+  }
+
+  expected.extra.apps.push_back(app_args);
+  expected.extra.bundle_name = _T("YouTubeUploader");
+  VerifyCommandLineArgs(expected, actual);
+}
+
+}  // namespace
+
+void VerifyCommandLineExtraArgs(const CommandLineExtraArgs& expected_val,
+                                const CommandLineExtraArgs& actual_val) {
+  EXPECT_STREQ(expected_val.bundle_name, actual_val.bundle_name);
+  EXPECT_STREQ(GuidToString(expected_val.installation_id),
+               GuidToString(actual_val.installation_id));
+  EXPECT_STREQ(expected_val.brand_code, actual_val.brand_code);
+  EXPECT_STREQ(expected_val.client_id, actual_val.client_id);
+  EXPECT_STREQ(expected_val.experiment_labels,
+               actual_val.experiment_labels);
+  EXPECT_STREQ(expected_val.referral_id, actual_val.referral_id);
+  EXPECT_STREQ(expected_val.language, actual_val.language);
+  EXPECT_EQ(expected_val.browser_type, actual_val.browser_type);
+  EXPECT_EQ(expected_val.usage_stats_enable, actual_val.usage_stats_enable);
+  EXPECT_EQ(expected_val.runtime_only, actual_val.runtime_only);
+
+  EXPECT_EQ(expected_val.apps.size(), actual_val.apps.size());
+
+  for (size_t i = 0; i < actual_val.apps.size(); ++i) {
+    CommandLineAppArgs expected = expected_val.apps[i];
+    CommandLineAppArgs actual = actual_val.apps[i];
+
+    EXPECT_STREQ(GuidToString(expected.app_guid),
+                 GuidToString(actual.app_guid));
+    EXPECT_STREQ(expected.app_name, actual.app_name);
+    EXPECT_EQ(expected.needs_admin, actual.needs_admin);
+    EXPECT_STREQ(expected.ap, actual.ap);
+    EXPECT_STREQ(expected.tt_token, actual.tt_token);
+    EXPECT_STREQ(expected.encoded_installer_data,
+                 actual.encoded_installer_data);
+    EXPECT_STREQ(expected.install_data_index, actual.install_data_index);
+    EXPECT_STREQ(expected.experiment_labels, actual.experiment_labels);
+  }
+}
+
+class CommandLineTest : public testing::Test {
+ protected:
+  CommandLineArgs args_;
+  CommandLineArgs expected_;
+};
+
+TEST(CommandLineSimpleTest, GetCmdLineTail1) {
+  EXPECT_STREQ(_T(""),  GetCmdLineTail(_T("")));
+}
+
+TEST(CommandLineSimpleTest, GetCmdLineTail2) {
+  EXPECT_STREQ(_T(""), GetCmdLineTail(_T("a")));
+}
+TEST(CommandLineSimpleTest, GetCmdLineTail3) {
+  EXPECT_STREQ(_T(""), GetCmdLineTail(_T("goopdate.exe")));
+}
+
+TEST(CommandLineSimpleTest, GetCmdLineTail4) {
+  // Double quotes.
+  EXPECT_STREQ(_T(""), GetCmdLineTail(_T("\"Google Update.exe\"")));
+}
+
+TEST(CommandLineSimpleTest, GetCmdLineTail5) {
+  // Argument.
+  EXPECT_STREQ(_T("foobar"), GetCmdLineTail(_T("goopdate.exe foobar")));
+}
+
+TEST(CommandLineSimpleTest, GetCmdLineTail6) {
+  // Double quotes and argument.
+  EXPECT_STREQ(_T("foobar"),
+               GetCmdLineTail(_T("\"Google Update.exe\" foobar")));
+}
+
+TEST(CommandLineSimpleTest, GetCmdLineTail7) {
+  // Double quotes and inner double quote and argument.
+  EXPECT_STREQ(_T("foobar"),
+               GetCmdLineTail(_T("\"Google\"\" Update.exe\" foobar")));
+}
+
+TEST(CommandLineSimpleTest, GetCmdLineTail8) {
+  // Double quotes and two arguments.
+  EXPECT_STREQ(_T("foo bar"),
+               GetCmdLineTail(_T("\"Google Update.exe\" foo bar")));
+}
+
+TEST(CommandLineSimpleTest, GetCmdLineTail9) {
+  // Double quotes and one argument with quotes.
+  EXPECT_STREQ(_T("\"foo bar\""),
+               GetCmdLineTail(_T("\"Google Update.exe\" \"foo bar\"")));
+}
+
+TEST(CommandLineSimpleTest, GetCmdLineTail10) {
+  // \t as white space.
+  EXPECT_STREQ(_T("foo bar"),
+               GetCmdLineTail(_T("\"Google Update.exe\"\tfoo bar")));
+}
+
+TEST(CommandLineSimpleTest, GetCmdLineTail11) {
+  // Trailing space.
+  EXPECT_STREQ(_T("foo bar "),
+               GetCmdLineTail(_T("\"Google Update.exe\" foo bar ")));
+}
+
+//
+// This block contains the positive test cases for each of the command lines.
+// If you add a new command line parameter permutation, add a test case here.
+//
+
+// TODO(omaha): Add some negative failure cases to the command lines (like
+// /install without "extraargs").
+
+// TODO(omaha): This is an Omaha1 back-compat issue.  Omaha2 should _never_
+// call googleupdate.exe with no arguments.  So when we stop supporting Omaha1
+// handoffs we should remove this support.
+  // Parse empty command line.
+TEST_F(CommandLineTest, ParseCommandLine_Empty) {
+  const TCHAR* kCmdLine = _T("");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_NOARGS;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path>
+// Remember that by convention the OS is passing us the program executable
+// name as the first token in the command line and the parsing code skips that.
+TEST_F(CommandLineTest, ParseCommandLine_ProgramNameOnly) {
+  const TCHAR* kCmdLine = _T("goopdate.exe");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_NOARGS;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /svc
+TEST_F(CommandLineTest, ParseCommandLine_Svc) {
+  const TCHAR* kCmdLine = _T("\"C:\\Program Files\\Google\\Common\\Update\\")
+                           _T("1.0.18.0\\goopdate.exe\" /svc");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_SERVICE;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /medsvc
+TEST_F(CommandLineTest, ParseCommandLine_MedSvc) {
+  const TCHAR* kCmdLine = _T("\"C:\\Program Files\\Google\\Common\\Update\\")
+                           _T("1.0.18.0\\goopdate.exe\" /medsvc");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_MEDIUM_SERVICE;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> -Embedding. The -Embedding text is injected via COM.
+TEST_F(CommandLineTest, ParseCommandLine_Server) {
+  const TCHAR* kCmdLine = _T("goopdate.exe -Embedding");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_COMSERVER;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /broker. Used for the special COM broker mode.
+TEST_F(CommandLineTest, ParseCommandLine_Broker) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /broker");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_COMBROKER;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /ondemand.
+TEST_F(CommandLineTest, ParseCommandLine_OnDemand) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /ondemand");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_ONDEMAND;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /install "extraargs"
+// Also tests that bundle_name takes the first app's appname when no bundlename
+// is specified.
+TEST_F(CommandLineTest, ParseCommandLine_Install) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /install ")
+      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+      _T("appname=YouTubeUploader&needsadmin=False&")
+      _T("appguid={C7A9A2F5-C4F9-42d3-8A8B-55086A205468}&")
+      _T("appname=TestApp&needsadmin=true&lang=en\"");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+
+  expected_.extra_args_str = _T("appguid=")
+                             _T("{A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+                             _T("appname=YouTubeUploader&needsadmin=False&")
+                             _T("appguid=")
+                             _T("{C7A9A2F5-C4F9-42d3-8A8B-55086A205468}&")
+                             _T("appname=TestApp&needsadmin=true&lang=en");
+  CommandLineAppArgs app_args;
+  const GUID expected_guid = {0xA4F7B07B, 0xB9BD, 0x4A33,
+                              {0xB1, 0x36, 0x96, 0xD2, 0xAD, 0xFB, 0x60, 0xCB}};
+  app_args.app_guid = expected_guid;
+  app_args.app_name = _T("YouTubeUploader");
+  app_args.needs_admin = NEEDS_ADMIN_NO;
+  expected_.extra.apps.push_back(app_args);
+
+  CommandLineAppArgs app_args1;
+  app_args1.app_guid =
+      StringToGuid(_T("{C7A9A2F5-C4F9-42d3-8A8B-55086A205468}"));
+  app_args1.app_name = _T("TestApp");
+  app_args1.needs_admin = NEEDS_ADMIN_YES;
+  expected_.extra.apps.push_back(app_args1);
+
+  expected_.extra.bundle_name = _T("YouTubeUploader");
+  expected_.extra.language = _T("en");
+
+  VerifyCommandLineArgs(expected_, args_);
+
+  EXPECT_STREQ(_T("YouTubeUploader"), args_.extra.bundle_name);
+}
+
+// Parse: <path> /install "bundlename=My%20Bundle&extraargs"
+// Tests bundlename override of appname.
+TEST_F(CommandLineTest, ParseCommandLine_Install_BundleName) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /install ")
+      _T("\"bundlename=My%20Bundle&")
+      _T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+      _T("appname=YouTubeUploader&needsadmin=False&\"");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+
+  expected_.extra_args_str = _T("bundlename=My%20Bundle&appguid=")
+                             _T("{A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+                             _T("appname=YouTubeUploader&needsadmin=False&");
+  CommandLineAppArgs app_args;
+  const GUID expected_guid = {0xA4F7B07B, 0xB9BD, 0x4A33,
+                              {0xB1, 0x36, 0x96, 0xD2, 0xAD, 0xFB, 0x60, 0xCB}};
+  app_args.app_guid = expected_guid;
+  app_args.app_name = _T("YouTubeUploader");
+  app_args.needs_admin = NEEDS_ADMIN_NO;
+  expected_.extra.apps.push_back(app_args);
+
+  expected_.extra.bundle_name = _T("My Bundle");
+
+  VerifyCommandLineArgs(expected_, args_);
+
+  EXPECT_STREQ(_T("My Bundle"), args_.extra.bundle_name);
+}
+
+// Parse: <path> /install "extraargs" /oem
+TEST_F(CommandLineTest, ParseCommandLine_InstallWithOem) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /oem");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+  expected_.is_oem_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /install "extraargs" [/oem
+// This tests how we handle a switch with a bracket, which represents optional
+// parameters in a rule, when it appears in an actual command line.
+TEST_F(CommandLineTest, ParseCommandLine_InstallWithOemIgnored) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
+                          _T(" [/oem");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+  expected_.is_oem_set = false;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /install "extraargs" /appargs <appargs>
+TEST_F(CommandLineTest, ParseCommandLine_InstallWithAppArgs) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS;
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
+}
+
+// Parse: <path> /install "extraargs" /oem /appargs <appargs>
+TEST_F(CommandLineTest, ParseCommandLine_InstallWithOemAppArgs) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /oem")
+                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS;
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+  expected_.is_oem_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
+}
+
+// Parse: <path> /install "extraargs" /appargs <appargs> /silent
+TEST_F(CommandLineTest, ParseCommandLine_InstallWithAppArgsSilent) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
+                          _T(" /silent");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+  expected_.is_silent_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
+}
+
+// Parse: <path> /install "extraargs" /oem /appargs <appargs> /silent
+TEST_F(CommandLineTest, ParseCommandLine_InstallWithOemAppArgsSilent) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /oem")
+                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
+                          _T(" /silent");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+  expected_.is_oem_set = true;
+  expected_.is_silent_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
+}
+
+// Parse:
+//  <path> /install "extraargs" /oem /appargs <appargs> /silent /eularequired
+TEST_F(CommandLineTest,
+       ParseCommandLine_InstallWithOemAppArgsSilentEulaRequired) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
+      _T(" /oem")
+      _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
+      _T(" /silent")
+      _T(" /eularequired");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+  expected_.is_oem_set = true;
+  expected_.is_silent_set = true;
+  expected_.is_eula_required_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
+}
+
+// Parse: <path> /install "extraargs" /eularequired
+TEST_F(CommandLineTest, ParseCommandLine_InstallEulaRequired) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
+      _T(" /eularequired");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+  expected_.is_eula_required_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /install "extraargs" /oem /installsource oneclick
+TEST_F(CommandLineTest, ParseCommandLine_InstallWithOemAndSource) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /oem")
+                          _T(" /installsource oneclick");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+  expected_.is_oem_set = true;
+  expected_.install_source = _T("oneclick");
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /install "extraargs" /installsource oneclick
+TEST_F(CommandLineTest, ParseCommandLine_InstallWithSource) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /installsource oneclick");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+  expected_.install_source = _T("oneclick");
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /install "extraargs" /silent
+TEST_F(CommandLineTest, ParseCommandLine_InstallSilent) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /silent");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+  expected_.is_silent_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /install "extraargs" /silent /oem
+TEST_F(CommandLineTest, ParseCommandLine_InstallSilentWithOem) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /silent")
+                          _T(" /oem");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+  expected_.is_silent_set = true;
+  expected_.is_oem_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /install "extraargs" /installsource oneclick /silent
+TEST_F(CommandLineTest, ParseCommandLine_InstallSilentWithSource) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /installsource oneclick")
+                          _T(" /silent");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+  expected_.install_source = _T("oneclick");
+  expected_.is_silent_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /install "extraargs" /installelevated
+TEST_F(CommandLineTest, ParseCommandLine_InstallElevated) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /installelevated");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+  expected_.is_install_elevated = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /install "extraargs" /installelevated /installsource oneclick
+TEST_F(CommandLineTest, ParseCommandLine_InstallElevatedWithSource) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /installelevated /installsource oneclick");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+  expected_.is_install_elevated = true;
+  expected_.install_source = _T("oneclick");
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /install "extraargs" /sessionid "guid"
+TEST_F(CommandLineTest, ParseCommandLine_InstallSessionId) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
+      _T(" /sessionid \"{756dfdc2-0ef0-44b7-bfb1-21a4be6a1213}\"");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+  expected_.session_id = _T("{756dfdc2-0ef0-44b7-bfb1-21a4be6a1213}");
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /ig "extraargs"
+TEST_F(CommandLineTest, ParseCommandLine_LegacyOmaha2Ig) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /ig ") YOUTUBEUPLOADEREN_TAG;
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+// Parse: <path> /handoff "extraargs"
+TEST_F(CommandLineTest, ParseCommandLine_Handoff) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG;
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /handoff "extraargs" /offlinedir "c:\dir\ "
+TEST_F(CommandLineTest, ParseCommandLine_OfflineDirTrailingBackslashSpace) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /offlinedir \"c:\\offline dir\\ \"");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  expected_.is_offline_set = true;
+  expected_.offline_dir = _T("c:\\offline dir");
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /handoff "extraargs" /offlinedir "c:\dir\\"
+TEST_F(CommandLineTest,
+       ParseCommandLine_OfflineDirTrailingDoubleBackslashNoSpace) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /offlinedir \"c:\\offline dir\\\\\"");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  expected_.is_offline_set = true;
+  expected_.offline_dir = _T("c:\\offline dir");
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /handoff "extraargs" /offlinedir "c:\dir\"
+TEST_F(CommandLineTest, ParseCommandLine_OfflineDirTrailingBackslashNoSpace) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /offlinedir \"c:\\offline dir\\\"");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  expected_.is_offline_set = true;
+  expected_.offline_dir = _T("c:\\offline dir");
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /handoff "extraargs" /installsource "asd"
+TEST_F(CommandLineTest, ParseCommandLine_HandoffWithSource) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /installsource oneclick");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  expected_.install_source = _T("oneclick");
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /handoff "extraargs" /installsource offline /offlineinstall
+TEST_F(CommandLineTest, ParseCommandLine_HandoffWithSourceOffline) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /installsource offline")
+                          _T(" /offlineinstall");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  expected_.install_source = _T("offline");
+  expected_.is_offline_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /handoff "extraargs" /appargs <appargs>
+//               /installsource offline /offlinedir "dir"
+TEST_F(CommandLineTest, ParseCommandLine_HandoffWithAppArgsSourceOffline) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
+                          _T(" /installsource offline")
+                          _T(" /offlinedir \"c:\\offline_dir\"");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  expected_.install_source = _T("offline");
+  expected_.is_offline_set = true;
+  expected_.offline_dir = _T("c:\\offline_dir");
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
+}
+
+// Parse: <path> /handoff "extraargs" /silent
+TEST_F(CommandLineTest, ParseCommandLine_HandoffSilent) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /silent");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  expected_.is_silent_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /handoff "extraargs" /silent /offlineinstall
+TEST_F(CommandLineTest,
+       ParseCommandLine_HandoffSilentOfflineWithoutInstallSource) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /silent")
+                          _T(" /offlineinstall");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  expected_.is_silent_set = true;
+  expected_.is_offline_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /handoff "extraargs" /installsource "asd" /silent
+TEST_F(CommandLineTest, ParseCommandLine_HandoffSilentWithSource) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /installsource oneclick")
+                          _T(" /silent");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  expected_.install_source = _T("oneclick");
+  expected_.is_silent_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse:
+//   <path> /handoff "extraargs" /installsource offline /silent /offlineinstall
+TEST_F(CommandLineTest, ParseCommandLine_HandoffSilentWithSourceOffline) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /installsource offline")
+                          _T(" /silent")
+                          _T(" /offlineinstall");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  expected_.install_source = _T("offline");
+  expected_.is_silent_set = true;
+  expected_.is_offline_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /handoff "extraargs" /appargs <appargs>
+TEST_F(CommandLineTest, ParseCommandLine_HandoffWithAppArgs) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS;
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
+}
+
+// Parse: <path> /handoff "extraargs" /appargs <appargs> /silent
+TEST_F(CommandLineTest, ParseCommandLine_HandoffSilentWithAppArgs) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
+                          _T(" /silent");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  expected_.is_silent_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
+}
+
+// Parse: <path> /handoff "extraargs" /appargs <appargs>
+//               /installsource offline /silent /offlineinstall
+TEST_F(CommandLineTest,
+       ParseCommandLine_HandoffSilentWithAppArgsSourceOffline) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
+                          _T(" /installsource offline")
+                          _T(" /silent")
+                          _T(" /offlineinstall");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  expected_.install_source = _T("offline");
+  expected_.is_silent_set = true;
+  expected_.is_offline_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
+}
+
+// Parse: <path> /handoff "extraargs" /appargs <appargs>
+//               /installsource offline /silent /offlineinstall /eularequired
+TEST_F(CommandLineTest,
+       ParseCommandLine_HandoffSilentWithAppArgsSourceOfflineEulaRequired) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
+                          _T(" /installsource offline")
+                          _T(" /silent")
+                          _T(" /offlineinstall")
+                          _T(" /eularequired");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  expected_.install_source = _T("offline");
+  expected_.is_silent_set = true;
+  expected_.is_offline_set = true;
+  expected_.is_eula_required_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
+}
+
+// Parse: <path> /handoff "extraargs" /eularequired
+TEST_F(CommandLineTest, ParseCommandLine_HandoffEulaRequired) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
+                          _T(" /eularequired");
+
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  expected_.is_eula_required_set = true;
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /handoff "extraargs" /sessionid "guid"
+TEST_F(CommandLineTest, ParseCommandLine_HandoffSessionId) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
+      _T(" /sessionid \"{756dfdc2-0ef0-44b7-bfb1-21a4be6a1213}\"");
+
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  expected_.session_id = _T("{756dfdc2-0ef0-44b7-bfb1-21a4be6a1213}");
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
+}
+
+// Parse: <path> /ug
+TEST_F(CommandLineTest, ParseCommandLine_LegacyOmaha2Ug) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /ug");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+// Parse: <path> /ua
+TEST_F(CommandLineTest, ParseCommandLine_UaNoInstallSource) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /ua");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_UA;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /ua /installsource core
+TEST_F(CommandLineTest, ParseCommandLine_Ua) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /ua /installsource core");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_UA;
+  expected_.install_source = _T("core");
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /update
+TEST_F(CommandLineTest, ParseCommandLine_Update) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /update");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_UPDATE;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /update /sessionid "guid"
+TEST_F(CommandLineTest, ParseCommandLine_UpdateSessionId) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /update");
+      _T(" /sessionid \"{756dfdc2-0ef0-44b7-bfb1-21a4be6a1213}\"");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_UPDATE;
+  expected_.session_id = _T("{756dfdc2-0ef0-44b7-bfb1-21a4be6a1213}");
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /netdiags
+TEST_F(CommandLineTest, ParseCommandLine_NetDiags) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /netdiags");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_NETDIAGS;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /regserver
+TEST_F(CommandLineTest, ParseCommandLine_Regserver) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /regserver");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_REGSERVER;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /unregserver
+TEST_F(CommandLineTest, ParseCommandLine_Unregserver) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /unregserver");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_UNREGSERVER;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /registerproduct
+TEST_F(CommandLineTest, ParseCommandLine_RegisterProduct) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /registerproduct ")
+      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+      _T("appname=YouTubeUploader&needsadmin=False\"");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_REGISTER_PRODUCT;
+  expected_.extra_args_str = _T("appguid=")
+                             _T("{A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+                             _T("appname=YouTubeUploader&needsadmin=False");
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, false);
+}
+
+// Parse: <path> /registerproduct /installsource enterprisemsi
+TEST_F(CommandLineTest, ParseCommandLine_RegisterProductWithInstallSource) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /registerproduct ")
+      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+      _T("appname=YouTubeUploader&needsadmin=False\"")
+      _T(" /installsource enterprisemsi");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+  expected_.mode = COMMANDLINE_MODE_REGISTER_PRODUCT;
+  expected_.install_source = _T("enterprisemsi");
+  expected_.extra_args_str = _T("appguid=")
+                             _T("{A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+                             _T("appname=YouTubeUploader&needsadmin=False");
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, false);
+}
+
+// Parse: <path> /unregisterproduct
+TEST_F(CommandLineTest, ParseCommandLine_UnregisterProduct) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /unregisterproduct ")
+      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+      _T("appname=YouTubeUploader&needsadmin=False\"");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_UNREGISTER_PRODUCT;
+  expected_.extra_args_str = _T("appguid=")
+                             _T("{A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+                             _T("appname=YouTubeUploader&needsadmin=False");
+  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, false);
+}
+
+// Parse: <path> /c
+TEST_F(CommandLineTest, ParseCommandLine_Core) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /c");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_CORE;
+  expected_.is_crash_handler_disabled = false;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /c /nocrashserver
+TEST_F(CommandLineTest, ParseCommandLine_CoreNoCrashHandler) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /c /nocrashserver");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_CORE;
+  expected_.is_crash_handler_disabled = true;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /crash
+TEST_F(CommandLineTest, ParseCommandLine_Crash) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /crash");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_CRASH;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /report crash_file
+TEST_F(CommandLineTest, ParseCommandLine_Report) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /report C:\\foo\\crash.dmp");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_REPORTCRASH;
+  expected_.crash_filename = _T("C:\\foo\\crash.dmp");
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /report crash_file /machine
+TEST_F(CommandLineTest, ParseCommandLine_ReportMachine) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /report C:\\foo\\crash.dmp /machine");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_REPORTCRASH;
+  expected_.crash_filename = _T("C:\\foo\\crash.dmp");
+  expected_.is_machine_set = true;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /report crash_file
+TEST_F(CommandLineTest, ParseCommandLine_ReportWithCustomInfo) {
+  const TCHAR* kCmdLine =
+    _T("goopdate.exe /report C:\\foo.dmp /custom_info_filename C:\\foo.txt");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_REPORTCRASH;
+  expected_.crash_filename = _T("C:\\foo.dmp");
+  expected_.custom_info_filename = _T("C:\\foo.txt");
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /report /i crash_file
+TEST_F(CommandLineTest, ParseCommandLine_ReportInteractive) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /report /i C:\\foo\\crash.dmp");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_REPORTCRASH;
+  expected_.is_interactive_set = true;
+  expected_.crash_filename = _T("C:\\foo\\crash.dmp");
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /report /i crash_file /machine
+TEST_F(CommandLineTest, ParseCommandLine_ReportMachineInteractive) {
+  const TCHAR*
+      kCmdLine = _T("goopdate.exe /report /i C:\\foo\\crash.dmp /machine");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_REPORTCRASH;
+  expected_.is_machine_set = true;
+  expected_.is_interactive_set = true;
+  expected_.crash_filename = _T("C:\\foo\\crash.dmp");
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+TEST_F(CommandLineTest, ParseCommandLine_CodeRedCheck) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /cr");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_CODE_RED_CHECK;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+TEST_F(CommandLineTest, ParseCommandLine_WebPlugin) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /pi \"http://gears.google.com/\" ")
+                          _T("\"/install foo\" /installsource oneclick ");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_WEBPLUGIN;
+  expected_.webplugin_urldomain = _T("http://gears.google.com/");
+  expected_.webplugin_args = _T("/install foo");
+  expected_.install_source = _T("oneclick");
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+TEST_F(CommandLineTest, ParseCommandLine_WebPluginUrlEscaped) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /pi \"http://gears.google.com/\" ")
+                          _T("\"/install%20foo\" /installsource oneclick ");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_WEBPLUGIN;
+  expected_.webplugin_urldomain = _T("http://gears.google.com/");
+  expected_.webplugin_args = _T("/install foo");
+  expected_.install_source = _T("oneclick");
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+TEST_F(CommandLineTest, ParseCommandLine_WebPluginTestStringTrim) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /pi ")
+                          _T("\"  http://gears.google.com/   \"  ")
+                          _T("\"/install foo\" /installsource oneclick ");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_WEBPLUGIN;
+  expected_.webplugin_urldomain = _T("http://gears.google.com/");
+  expected_.webplugin_args = _T("/install foo");
+  expected_.install_source = _T("oneclick");
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+TEST_F(CommandLineTest, ParseCommandLine_LegacyOmaha1UiNoLanguage) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /ui \"manifestfilename.xml\"");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+TEST_F(CommandLineTest, ParseCommandLine_LegacyOmaha1UiWithLanguage) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /ui /lang fr \"manifestfilename.xml\"");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+TEST_F(CommandLineTest, ParseCommandLine_LegacyOmaha1UiUser) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /uiuser file.gup");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+TEST_F(CommandLineTest, ParseCommandLine_Recover) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /recover repairfile.exe");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_RECOVER;
+  expected_.code_red_metainstaller_path = _T("repairfile.exe");
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+TEST_F(CommandLineTest, ParseCommandLine_RecoverMachine) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /recover /machine repfile.exe");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_RECOVER;
+  expected_.is_machine_set = true;
+  expected_.code_red_metainstaller_path = _T("repfile.exe");
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /uninstall
+TEST_F(CommandLineTest, ParseCommandLine_Uninstall) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /uninstall");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_UNINSTALL;
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+// Parse: <path> /ping foo
+TEST_F(CommandLineTest, ParseCommandLine_Ping) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /ping foo");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_PING;
+  expected_.ping_string = _T("foo");
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+//
+// These are additional failure cases against the command line parsing.
+// Everything from here on down should fail ParseCommandLine().
+//
+
+
+// Parse: <path> manifest_file
+TEST_F(CommandLineTest, ParseCommandLine_GoopdateJustArg) {
+  const TCHAR* kCmdLine = _T("goopdate.exe \"foo bar\"");
+  ExpectAsserts expect_asserts;
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+// Parse: <path> /install manifest_file manifest_file
+// Fails since this is an invalid command line set.
+TEST_F(CommandLineTest, ParseCommandLine_Invalid) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /install \"foo bar\" foobar");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+// Parse: <path> /recover
+TEST_F(CommandLineTest, Recover_WithoutFile) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /recover");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+// Parse: <path> /machine
+TEST_F(CommandLineTest, MachineWithoutRecover) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /machine");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+TEST_F(CommandLineTest, ExtraArgsHasDoubleQuoteInTheMiddle) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /install \"some_\"file\"");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+TEST_F(CommandLineTest, CommandsNotSeparatedBySpaces) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /recover/machine");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+TEST_F(CommandLineTest, CommandsDoNotHaveForwardSlashes) {
+  const TCHAR* kCmdLine = _T("goopdate.exe recover machine");
+  ExpectAsserts expect_asserts;
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+TEST_F(CommandLineTest, UnknownParameter) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /someunknowncommand");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+TEST_F(CommandLineTest, UiWithLangNoLanguage) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /ui /lang \"manifestfilename.xml\"");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+TEST_F(CommandLineTest, WebPluginInstallSourceInvalid_IncorrectValue) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /installsource invalid /pi ")
+                          _T("\"  http://gears.google.com/   \"  ");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+TEST_F(CommandLineTest, WebPluginInstallSourceInvalid_Empty) {
+  const TCHAR* kCmdLine = _T("goopdate.exe /installsource /pi ")
+                          _T("\"  http://gears.google.com/   \"  ");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+// Parse: <path> /handoff "extraargs" /lang "en"
+TEST_F(CommandLineTest, ParseCommandLine_HandoffLegacyOmaha1ToOmaha2) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /handoff ")
+      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+      _T("appname=YouTubeUploader&needsadmin=False\"")
+      _T(" /lang en");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+// Parse: <path> /handoff "extraargs" /installsource "asd" /lang "en"
+TEST_F(CommandLineTest,
+       ParseCommandLine_HandoffWithSourceLegacyOmaha1ToOmaha2) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /handoff ")
+      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+      _T("appname=YouTubeUploader&needsadmin=False\"")
+      _T(" /installsource oneclick /lang en");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+// Parse: <path> /handoff "extraargs" /installsource "oneclick" /lang "en"
+TEST_F(CommandLineTest,
+       ParseCommandLine_HandoffWithSourceLegacyOmaha1ToOmaha2Both) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /handoff ")
+      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+      _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
+      _T(" /installsource oneclick /lang en");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+// Parse: <path> /install "extraargs" /lang en
+TEST_F(CommandLineTest, ParseCommandLine_InstallLegacyOmaha1) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /install ")
+      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+      _T("appname=YouTubeUploader&needsadmin=False&")
+      _T("appguid={C7A9A2F5-C4F9-42d3-8A8B-55086A205468}&")
+      _T("appname=TestApp&needsadmin=true\" /lang en");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+// Parse: <path> /install "extraargs" /installsource oneclick /lang en
+TEST_F(CommandLineTest, ParseCommandLine_InstallWithSourceLegacyOmaha1) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /install ")
+      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+      _T("appname=YouTubeUploader&needsadmin=False\"")
+      _T(" /installsource oneclick /lang en");
+  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
+}
+
+TEST_F(CommandLineTest, ParseCommandLine_NeedsAdmin_Prefers) {
+  const TCHAR* kCmdLine =
+      _T("goopdate.exe /install ")
+      _T("\"bundlename=My%20Bundle&")
+      _T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+      _T("appname=YouTubeUploader&needsadmin=Prefers&\"");
+  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
+
+  expected_.mode = COMMANDLINE_MODE_INSTALL;
+
+  expected_.extra_args_str = _T("bundlename=My%20Bundle&appguid=")
+                             _T("{A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
+                             _T("appname=YouTubeUploader&needsadmin=Prefers&");
+  CommandLineAppArgs app_args;
+  const GUID expected_guid = {0xA4F7B07B, 0xB9BD, 0x4A33,
+                              {0xB1, 0x36, 0x96, 0xD2, 0xAD, 0xFB, 0x60, 0xCB}};
+  app_args.app_guid = expected_guid;
+  app_args.app_name = _T("YouTubeUploader");
+  app_args.needs_admin = NEEDS_ADMIN_PREFERS;
+  expected_.extra.apps.push_back(app_args);
+
+  expected_.extra.bundle_name = _T("My Bundle");
+
+  VerifyCommandLineArgs(expected_, args_);
+}
+
+}  // namespace omaha
diff --git a/common/commands.cc b/common/commands.cc
deleted file mode 100644
index e936360..0000000
--- a/common/commands.cc
+++ /dev/null
@@ -1,578 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Parse command-line options
-
-#include "omaha/common/commands.h"
-#include <cstdlib>
-#include "base/scoped_ptr.h"
-#include "omaha/common/cgi.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/string.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-#define kNameValueChar  _T('=')
-#define kTrueValue      _T("true")
-#define kFalseValue     _T("false")
-#define kOnValue        _T("on")
-#define kOffValue       _T("off")
-
-
-//
-// Helper functions
-//
-
-template<class T>
-HRESULT ConvertValue(const TCHAR* str_value, T* value);
-
-// Convert the three-valued value from the string representation
-template<>
-HRESULT ConvertValue<ThreeValue>(const TCHAR* str_value, ThreeValue* value) {
-  ASSERT1(value);
-
-  *value = VALUE_NOT_SET;
-  if (str_value && *str_value) {
-    if (String_StrNCmp(str_value,
-                       kTrueValue,
-                       TSTR_SIZE(kTrueValue) + 1,
-                       true) == 0 ||
-        String_StrNCmp(str_value,
-                       kOnValue,
-                       TSTR_SIZE(kOnValue) + 1,
-                       true) == 0) {
-      *value = TRUE_VALUE;
-    } else if (String_StrNCmp(str_value,
-                              kFalseValue,
-                              TSTR_SIZE(kFalseValue) + 1,
-                              true) == 0 ||
-               String_StrNCmp(str_value,
-                              kOffValue,
-                              TSTR_SIZE(kOffValue) + 1,
-                              true) == 0) {
-      *value = FALSE_VALUE;
-    } else {
-      return CI_E_INVALID_ARG;
-    }
-  }
-  return S_OK;
-}
-
-// Convert the int value from the string representation
-template<>
-HRESULT ConvertValue<int>(const TCHAR* str_value, int* value) {
-  ASSERT1(str_value && *str_value);
-  ASSERT1(value);
-
-  if (_set_errno(0)) {
-    return E_FAIL;
-  }
-
-  *value = _tcstol(str_value, NULL, 0);
-  if (errno == ERANGE) {
-    return CI_E_INVALID_ARG;
-  }
-  return S_OK;
-}
-
-// Convert the unsigned int value from the string representation
-template<>
-HRESULT ConvertValue<uint32>(const TCHAR* str_value, uint32* value) {
-  ASSERT1(str_value && *str_value);
-  ASSERT1(value);
-
-  if (_set_errno(0)) {
-    return E_FAIL;
-  }
-
-  *value = _tcstoul(str_value, NULL, 0);
-  if (errno == ERANGE) {
-    return CI_E_INVALID_ARG;
-  }
-  return S_OK;
-}
-
-// Convert the string value from the string representation
-HRESULT ConvertValue(const TCHAR* str_value, CString* value, bool to_unescape) {
-  ASSERT1(str_value && *str_value);
-  ASSERT1(value);
-
-  *value = str_value;
-
-  if (to_unescape) {
-    int length = value->GetLength();
-    scoped_array<TCHAR> unescaped_value(new TCHAR[length + 1]);
-    RET_IF_FALSE(CGI::UnescapeString(*value, length, unescaped_value.get(),
-      length + 1), CI_E_INVALID_ARG);
-    *value = unescaped_value.get();
-  }
-
-  return S_OK;
-}
-
-//
-// Struct CommandOption
-//
-void CommandOption::Init(const TCHAR* name, CommandOptionType type,
-                         void* value, int max_value_len) {
-  this->name = name;
-  this->type = type;
-  this->value = value;
-  this->max_value_len = max_value_len;
-}
-
-void CommandOption::Copy(const CommandOption& option) {
-  Init(option.name, option.type, option.value, option.max_value_len);
-}
-
-//
-// Class CommandParsingSimple
-//
-
-// Constructor
-CommandParsingSimple::CommandParsingSimple()
-    : separator_(_T(' ')) {
-}
-
-// Constructor
-CommandParsingSimple::CommandParsingSimple(TCHAR separator)
-    : separator_(separator) {
-}
-
-// Parse a command line string into args
-HRESULT CommandParsingSimple::ParseSimple(const TCHAR* cmd_line) {
-  ASSERT1(cmd_line);
-
-  UTIL_LOG(L3, (_T("[CommandParsingSimple::ParseSimple][%s]"), cmd_line));
-
-  args_.clear();
-
-  // Split command line string into list of arguments
-  for (const TCHAR* s = cmd_line; *s; ++s) {
-    // Handle separator
-    if (*s == separator_) {
-      continue;
-    }
-
-    // Handle single/double quote
-    if (*s == _T('"') || *s == _T('\'')) {
-      int right_quote = String_FindChar(s + 1, *s);
-      if (right_quote == -1) {
-        UTIL_LOG(LE, (_T("[CommandParsingSimple::ParseSimple]")
-                      _T("[single/double quote mismatches]")));
-        return CI_E_INVALID_ARG;
-      }
-      args_.push_back(CString(s + 1, right_quote));
-      s += right_quote + 1;
-      continue;
-    }
-
-    // Handle all other char
-    int next_space = String_FindChar(s + 1, separator_);
-    if (next_space == -1) {
-      args_.push_back(CString(s));
-      break;
-    } else {
-      args_.push_back(CString(s, next_space + 1));
-      s += next_space + 1;
-    }
-  }
-
-  return S_OK;
-}
-
-// Get the arg at specified position from the command line
-HRESULT CommandParsingSimple::GetAt(uint32 position, CString* arg) {
-  ASSERT1(arg);
-  ASSERT1(position < args_.size());
-
-  if (!arg || position >= args_.size()) {
-    return E_INVALIDARG;
-  }
-
-  *arg = args_[position];
-  return S_OK;
-}
-
-// Remove the arg at specified position from the command line
-HRESULT CommandParsingSimple::RemoveAt(uint32 position) {
-  ASSERT1(position < args_.size());
-
-  if (position >= args_.size()) {
-    return E_INVALIDARG;
-  }
-
-  uint32 i = 0;
-  std::vector<CString>::iterator it(args_.begin());
-  for (; i < position; ++it, ++i) {
-    ASSERT1(it != args_.end());
-  }
-  args_.erase(it);
-  return S_OK;
-}
-
-// Converted to the string
-HRESULT CommandParsingSimple::ToString(CString* cmd_line) {
-  ASSERT1(cmd_line);
-
-  bool is_first = true;
-  cmd_line->Empty();
-  for (std::vector<CString>::const_iterator it(args_.begin());
-       it != args_.end();
-       ++it) {
-    if (is_first) {
-      is_first = false;
-    } else {
-      cmd_line->AppendChar(separator_);
-    }
-    const TCHAR* arg = it->GetString();
-    if (String_FindChar(arg, separator_) != -1) {
-      cmd_line->AppendChar(_T('"'));
-      cmd_line->Append(arg);
-      cmd_line->AppendChar(_T('"'));
-    } else {
-      cmd_line->Append(arg);
-    }
-  }
-
-  return S_OK;
-}
-
-// Static Helper function that splits a command line
-// string into executable and any arguments
-HRESULT CommandParsingSimple::SplitExeAndArgs(const TCHAR* cmd_line,
-                                              CString* exe,
-                                              CString* args) {
-  ASSERT1(cmd_line);
-  ASSERT1(exe);
-  ASSERT1(args);
-
-  // Do the parsing
-  CommandParsingSimple cmd_parsing_simple;
-
-  RET_IF_FAILED(cmd_parsing_simple.ParseSimple(cmd_line));
-  RET_IF_FAILED(cmd_parsing_simple.GetAt(0, exe));
-  exe->Trim();
-  RET_IF_FAILED(cmd_parsing_simple.RemoveAt(0));
-  return (cmd_parsing_simple.ToString(args));
-}
-
-HRESULT CommandParsingSimple::SplitExeAndArgsGuess(const TCHAR* cmd_line,
-                                                   CString* exe,
-                                                   CString* args) {
-  ASSERT1(cmd_line);
-  ASSERT1(exe);
-  ASSERT1(args);
-
-  if (File::Exists(cmd_line)) {
-    // Optimization for the single executable case.
-    // Fill the [out] parameters and return.
-    *exe = cmd_line;
-    exe->Trim();
-    args->Empty();
-    return S_OK;
-  }
-
-  CString command_line(cmd_line);
-  // Check if the command line is properly enclosed, or that it does not have
-  // spaces
-  if (command_line.GetAt(0) != _T('"') && command_line.Find(_T(' ')) != -1) {
-    // File::Exists() does not handle leading spaces so remove it.
-    command_line.Trim();
-
-    // If not, need to find the executable, and if valid, enclose it in
-    // double quotes
-    const TCHAR* index_dot_exe = stristrW(command_line.GetString(), _T(".EXE"));
-
-    if (index_dot_exe != NULL) {
-      int dot_exe_end = (index_dot_exe - command_line.GetString())
-                         + arraysize(_T(".EXE")) - 1;
-      if (File::Exists(CString(command_line, dot_exe_end))) {
-        // Enclose the EXE in double quotes
-        command_line.Insert(dot_exe_end, _T('"'));
-        command_line.Insert(0, _T('"'));
-      } else {
-        UTIL_LOG(L1, (_T("[CommandParsing::SplitExeAndArgsGuess]")
-                      _T("[Could not guess the Executable file within [%s]. ")
-                      _T("Passing on to SplitExeAndArgs as-is."),
-                      command_line));
-      }
-    }
-  }
-
-  // Do the parsing
-  return SplitExeAndArgs(command_line, exe, args);
-}
-
-
-// Static Helper function that returns the number of arguments
-// in the passed in cmd_line
-HRESULT CommandParsingSimple::GetNumberOfArgs(const TCHAR* cmd_line,
-                                              uint32* number_of_args) {
-  ASSERT1(cmd_line);
-  ASSERT1(number_of_args);
-
-  // Do the parsing
-  CommandParsingSimple cmd_parsing_simple;
-
-  RET_IF_FAILED(cmd_parsing_simple.ParseSimple(cmd_line));
-  *number_of_args = cmd_parsing_simple.args_.size();
-  return S_OK;
-}
-
-
-//
-// Class CommandParsing
-//
-
-// Constructor
-CommandParsing::CommandParsing(CommandOption* options, int options_count)
-    : CommandParsingSimple(),
-      options_(options),
-      options_count_(options_count),
-      as_name_value_pair_(false) {
-}
-
-// Constructor
-CommandParsing::CommandParsing(CommandOption* options, int options_count,
-                               TCHAR separator, bool as_name_value_pair)
-    : CommandParsingSimple(separator),
-      options_(options),
-      options_count_(options_count),
-      as_name_value_pair_(as_name_value_pair) {
-}
-
-// Parse a command line string
-HRESULT CommandParsing::Parse(const TCHAR* cmd_line, bool ignore_unknown_args) {
-  ASSERT1(cmd_line);
-
-  UTIL_LOG(L3, (_T("[CommandParsing::Parse][%s][%d]"),
-                cmd_line, ignore_unknown_args));
-
-  // Parse into args_ vector
-  RET_IF_FAILED(ParseSimple(cmd_line));
-
-  // Do the internal parsing
-  return InternalParse(ignore_unknown_args);
-}
-
-// Parse a list of command line arguments
-HRESULT CommandParsing::ParseArguments(int argc, TCHAR* argv[]) {
-  if (argc <= 1) {
-    return S_OK;
-  }
-
-  // Push each argument
-  args_.clear();
-  for (int i = 1; i < argc; ++i) {
-    args_.push_back(CString(argv[i]));
-  }
-
-  // Do the internal parsing
-  return InternalParse(false);
-}
-
-// Internal parsing
-HRESULT CommandParsing::InternalParse(bool ignore_unknown_args) {
-  CString name, value;
-  for (std::vector<CString>::const_iterator it(args_.begin());
-       it != args_.end();
-       ++it) {
-    RET_IF_FAILED(ExtractName(&name, &it));
-
-    int i = FindOption(name);
-    if (i == -1) {
-      if (ignore_unknown_args) {
-        UTIL_LOG(L3, (_T("[CommandParsing::Parse][unknown arg %s]"), name));
-        continue;
-      } else {
-        UTIL_LOG(LE, (_T("[CommandParsing::Parse][invalid arg %s]"), name));
-        return CI_E_INVALID_ARG;
-      }
-    }
-
-    if (options_[i].type != COMMAND_OPTION_BOOL) {
-      RET_IF_FAILED(ExtractValue(options_[i], &value, &it, args_.end()));
-    }
-
-    switch (options_[i].type & COMMAND_OPTION_FLAGS_MASK) {
-      case COMMAND_OPTION_BOOL: {
-        bool bool_value = true;
-        SetParsedValue(options_[i], bool_value);
-        break;
-      }
-
-      case COMMAND_OPTION_THREE: {
-        ThreeValue three_value = VALUE_NOT_SET;
-        RET_IF_FAILED(ConvertValue(value, &three_value));
-        SetParsedValue(options_[i], three_value);
-        break;
-      }
-
-      case COMMAND_OPTION_INT: {
-        int int_value = 0;
-        RET_IF_FAILED(ConvertValue(value, &int_value));
-        SetParsedValue(options_[i], int_value);
-        break;
-      }
-
-      case COMMAND_OPTION_UINT: {
-        int uint_value = 0;
-        RET_IF_FAILED(ConvertValue(value, &uint_value));
-        SetParsedValue(options_[i], uint_value);
-        break;
-      }
-
-      case COMMAND_OPTION_STRING: {
-        CString str_value;
-        bool is_unescape = (options_[i].type & COMMAND_OPTION_UNESCAPE) != 0;
-        RET_IF_FAILED(ConvertValue(value, &str_value, is_unescape));
-        SetParsedValue(options_[i], str_value);
-        break;
-      }
-
-      default:
-        ASSERT1(false);
-        break;
-    }
-  }
-
-  return S_OK;
-}
-
-// Extract the name
-HRESULT CommandParsing::ExtractName(CString* name,
-                                    std::vector<CString>::const_iterator* it) {
-  ASSERT1(name);
-  ASSERT1(it);
-
-  if (as_name_value_pair_) {
-    int idx = (*it)->Find(kNameValueChar);
-    if (idx == -1) {
-      return CI_E_INVALID_ARG;
-    } else {
-      *name = (*it)->Left(idx);
-    }
-  } else {
-    *name = (*it)->GetString();
-  }
-  return S_OK;
-}
-
-// Extract the value
-// Also validate the value length if necessary
-HRESULT CommandParsing::ExtractValue(
-    const CommandOption& option,
-    CString* value,
-    std::vector<CString>::const_iterator* it,
-    const std::vector<CString>::const_iterator& end) {
-  ASSERT1(value);
-  ASSERT1(it);
-
-  if (as_name_value_pair_) {
-    int idx = (*it)->Find(kNameValueChar);
-    if (idx == -1) {
-      return CI_E_INVALID_ARG;
-    } else {
-      *value = (*it)->Right((*it)->GetLength() - idx - 1);
-    }
-  } else {
-    ++(*it);
-    if (*it == end) {
-      UTIL_LOG(LE, (_T("[CommandParsing::ExtractValue]")
-                    _T("[argument %s missing value]"), option.name));
-      return CI_E_INVALID_ARG;
-    }
-    *value = (*it)->GetString();
-  }
-
-  if (option.max_value_len >= 0) {
-    if (value->GetLength() > option.max_value_len) {
-      return CI_E_INVALID_ARG;
-    }
-  }
-
-  return S_OK;
-}
-
-// Set the parsed value
-template<class T>
-void CommandParsing::SetParsedValue(const CommandOption& option,
-                                    const T& value) {
-  if (option.type & COMMAND_OPTION_MULTIPLE) {
-    ASSERT((option.type & COMMAND_OPTION_FLAGS_MASK) != COMMAND_OPTION_BOOL,
-      (_T("COMMAND_OPTION_BOOL can't be used with COMMAND_OPTION_MULTIPLE")));
-    ASSERT((option.type & COMMAND_OPTION_FLAGS_MASK) != COMMAND_OPTION_THREE,
-      (_T("COMMAND_OPTION_THREE can't be used with COMMAND_OPTION_MULTIPLE")));
-
-    std::vector<T>* ptr = reinterpret_cast<std::vector<T>*>(option.value);
-    ptr->push_back(value);
-  } else {
-    T* ptr = reinterpret_cast<T*>(option.value);
-    *ptr = value;
-  }
-}
-
-// Helper function to find an option in the CommandOption list
-int CommandParsing::FindOption(const TCHAR* option_name) {
-  ASSERT1(option_name);
-
-  for (int i = 0; i < options_count_; ++i) {
-    if (String_StrNCmp(option_name,
-                       options_[i].name,
-                       options_[i].name.GetLength() + 1,
-                       false) == 0) {
-      return i;
-    }
-  }
-
-  return -1;
-}
-
-// Remove an option from the command line
-HRESULT CommandParsing::Remove(const TCHAR* option_name) {
-  ASSERT1(option_name);
-
-  for (std::vector<CString>::iterator it(args_.begin());
-       it != args_.end();
-       ++it) {
-    if (*it == option_name) {
-      int i = FindOption(option_name);
-      if (i == -1) {
-        return E_FAIL;
-      }
-      args_.erase(it);
-      if (!as_name_value_pair_) {
-        if (options_[i].type != COMMAND_OPTION_BOOL) {
-          if (it == args_.end()) {
-            return E_FAIL;
-          }
-          args_.erase(it);
-        }
-      }
-
-      return S_OK;
-    }
-  }
-
-  return E_FAIL;
-}
-
-}  // namespace omaha
-
diff --git a/common/commands_unittest.cc b/common/commands_unittest.cc
deleted file mode 100644
index 8071432..0000000
--- a/common/commands_unittest.cc
+++ /dev/null
@@ -1,430 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Unit tests of command line options parsing
-
-#include <cstdio>
-#include "omaha/common/commands.h"
-#include "omaha/common/file.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-#define kDash         _T("-")
-#define kBoolOption   _T("bool")
-#define kThreeOption  _T("three")
-#define kIntOption    _T("int")
-#define kUintOption   _T("uint")
-#define kStrOption    _T("str")
-
-#define kIEBrowserExe \
-  _T("C:\\PROGRAM FILES\\Internet Explorer\\iexplore.exe")
-
-#define kIEBrowserQuotedExe \
-  _T("\"") kIEBrowserExe _T("\"")
-
-#define kIEBrowserQuotedArgs          _T("-h \"%1\"")
-#define kIEBrowserQuotedCommandLine \
-    kIEBrowserQuotedExe _T(" ") kIEBrowserQuotedArgs
-
-#define kIEBrowserQuotedExeResult \
-    _T("C:\\PROGRAM FILES\\Internet Explorer\\iexplore.exe")
-#define kIEBrowserQuotedArgsResult    _T("-h %1")
-
-#define kIEBrowserUnquotedCommandLine \
-    _T("C:\\Program Files\\Internet Explorer\\iexplore.exe -nohome")
-#define kIEBrowserUnquotedExe \
-    _T("C:\\Program Files\\Internet Explorer\\iexplore.exe")
-#define kIEBrowserUnquotedArgs        _T("-nohome")
-
-#define kGEUninstallCommandLine       _T("RunDll32 C:\\PROGRA~1\\COMMON~1\\INSTAL~1\\PROFES~1\\RunTime\\10\\01\\Intel32\\Ctor.dll,LaunchSetup \"C:\\Program Files\\InstallShield Installation Information\\{3DE5E7D4-7B88-403C-A3FD-2017A8240C5B}\\setup.exe\" -l0x9  -removeonly")  // NOLINT
-#define kGEUninstallExe               _T("RunDll32")
-#define kGEUninstallArgs              _T("C:\\PROGRA~1\\COMMON~1\\INSTAL~1\\PROFES~1\\RunTime\\10\\01\\Intel32\\Ctor.dll,LaunchSetup \"C:\\Program Files\\InstallShield Installation Information\\{3DE5E7D4-7B88-403C-A3FD-2017A8240C5B}\\setup.exe\" -l0x9 -removeonly")            // NOLINT
-
-
-struct TestData {
-  TestData() {
-    Clear();
-  }
-
-  void Clear() {
-    bool_value = false;
-    three_value = VALUE_NOT_SET;
-    int_value = 0;
-    uint_value = 0;
-    str_value.Empty();
-  }
-
-  bool bool_value;
-  ThreeValue three_value;
-  int int_value;
-  uint32 uint_value;
-  CString str_value;
-};
-
-void FillTestData(TestData* data) {
-  data->bool_value = true;
-  data->three_value = FALSE_VALUE;
-  data->int_value = -128;
-  data->uint_value = 256;
-  data->str_value = _T("Foo");
-}
-
-void CheckTestData(const TestData& d1, const TestData& d2) {
-  EXPECT_EQ(d1.bool_value, d2.bool_value);
-  EXPECT_EQ(d1.three_value, d2.three_value);
-  EXPECT_EQ(d1.int_value, d2.int_value);
-  EXPECT_EQ(d1.uint_value, d2.uint_value);
-  EXPECT_STREQ(d1.str_value, d2.str_value);
-}
-
-TEST(CommandsTest, TraditionalCommandLineOptionsParsingTest) {
-  TestData data;
-  FillTestData(&data);
-  CString cmd_line;
-  cmd_line.Format(_T("%s%s %s ")
-                  _T("%s%s %d ")
-                  _T("%s%s %u ")
-                  _T("%s%s %s "),
-                  kDash, kThreeOption, (data.three_value == TRUE_VALUE) ? _T("on") : _T("off"),   // NOLINT
-                  kDash, kIntOption, data.int_value,
-                  kDash, kUintOption, data.uint_value,
-                  kDash, kStrOption, data.str_value);
-  if (data.bool_value) {
-    cmd_line.AppendFormat(_T("%s%s"), kDash, kBoolOption);
-  }
-
-  TestData option_data;
-  CommandOption cmd_options[] = {
-    { kDash kBoolOption,  COMMAND_OPTION_BOOL,   &option_data.bool_value,  -1 },
-    { kDash kThreeOption, COMMAND_OPTION_THREE,  &option_data.three_value, -1 },
-    { kDash kIntOption,   COMMAND_OPTION_INT,    &option_data.int_value,   -1 },
-    { kDash kUintOption,  COMMAND_OPTION_UINT,   &option_data.uint_value,  -1 },
-    { kDash kStrOption,   COMMAND_OPTION_STRING, &option_data.str_value,   -1 }
-  };
-
-  option_data.Clear();
-  CommandParsing cmd_parsing(cmd_options, arraysize(cmd_options));
-  ASSERT_SUCCEEDED(cmd_parsing.Parse(cmd_line, false));
-
-  CheckTestData(option_data, data);
-}
-
-TEST(CommandsTest, TraditionalIgnoreUnknownArgsParsingTest) {
-  TestData data;
-  FillTestData(&data);
-  CString cmd_line;
-  cmd_line.Format(_T("%s%s %s ")
-                  _T("%s%s %d ")
-                  _T("%s%s %u ")
-                  _T("%s%s %s "),
-                  kDash, kThreeOption, (data.three_value == TRUE_VALUE) ? _T("on") : _T("off"),   // NOLINT
-                  kDash, kIntOption, data.int_value,
-                  kDash, kUintOption, data.uint_value,
-                  kDash, kStrOption, data.str_value);
-  if (data.bool_value) {
-    cmd_line.AppendFormat(_T("%s%s"), kDash, kBoolOption);
-  }
-
-  TestData option_data;
-  CommandOption cmd_options[] = {
-    { kDash kThreeOption, COMMAND_OPTION_THREE,  &option_data.three_value, -1 },
-    { kDash kIntOption,   COMMAND_OPTION_INT,    &option_data.int_value,   -1 },
-    { kDash kUintOption,  COMMAND_OPTION_UINT,   &option_data.uint_value,  -1 },
-    { kDash kStrOption,   COMMAND_OPTION_STRING, &option_data.str_value,   -1 }
-  };
-
-  option_data.Clear();
-  CommandParsing cmd_parsing(cmd_options, arraysize(cmd_options));
-  ASSERT_FAILED(cmd_parsing.Parse(cmd_line, false));
-  ASSERT_SUCCEEDED(cmd_parsing.Parse(cmd_line, true));
-
-  option_data.bool_value = data.bool_value;
-  CheckTestData(option_data, data);
-}
-
-TEST(CommandsTest, NameValuePairCommandLineOptionsParsingTest) {
-  TestData data;
-  FillTestData(&data);
-  CString cmd_line;
-  cmd_line.Format(
-      _T("%s=%s&")
-      _T("%s=%d&")
-      _T("%s=%u&")
-      _T("%s=%s"),
-      kThreeOption, (data.three_value == TRUE_VALUE) ? _T("on") : _T("off"),
-      kIntOption, data.int_value,
-      kUintOption, data.uint_value,
-      kStrOption, data.str_value);
-  if (data.bool_value) {
-    cmd_line.AppendFormat(_T("&%s="), kBoolOption);
-  }
-
-  TestData option_data;
-  CommandOption cmd_options[] = {
-    { kBoolOption,  COMMAND_OPTION_BOOL,   &option_data.bool_value,    -1 },
-    { kThreeOption, COMMAND_OPTION_THREE,  &option_data.three_value,   -1 },
-    { kIntOption,   COMMAND_OPTION_INT,    &option_data.int_value,     -1 },
-    { kUintOption,  COMMAND_OPTION_UINT,   &option_data.uint_value,    -1 },
-    { kStrOption,   COMMAND_OPTION_STRING, &option_data.str_value,     -1 }
-  };
-
-  option_data.Clear();
-  CommandParsing cmd_parsing(cmd_options,
-                             arraysize(cmd_options),
-                             _T('&'),
-                             true);
-  ASSERT_SUCCEEDED(cmd_parsing.Parse(cmd_line, false));
-
-  CheckTestData(option_data, data);
-}
-
-TEST(CommandsTest, NameValuePairIgnoreUnknownArgsParsingTest) {
-  TestData data;
-  FillTestData(&data);
-  CString cmd_line;
-  cmd_line.Format(
-      _T("%s=%s&")
-      _T("%s=%d&")
-      _T("%s=%u&")
-      _T("%s=%s"),
-      kThreeOption, (data.three_value == TRUE_VALUE) ? _T("on") : _T("off"),
-      kIntOption, data.int_value,
-      kUintOption, data.uint_value,
-      kStrOption, data.str_value);
-  if (data.bool_value) {
-    cmd_line.AppendFormat(_T("&%s="), kBoolOption);
-  }
-
-  TestData option_data;
-  CommandOption cmd_options[] = {
-    { kBoolOption,  COMMAND_OPTION_BOOL,   &option_data.bool_value,    -1 },
-    { kThreeOption, COMMAND_OPTION_THREE,  &option_data.three_value,   -1 },
-    { kUintOption,  COMMAND_OPTION_UINT,   &option_data.uint_value,    -1 },
-    { kStrOption,   COMMAND_OPTION_STRING, &option_data.str_value,     -1 }
-  };
-
-  option_data.Clear();
-  CommandParsing cmd_parsing(cmd_options,
-                             arraysize(cmd_options),
-                             _T('&'),
-                             true);
-  ASSERT_FAILED(cmd_parsing.Parse(cmd_line, false));
-  ASSERT_SUCCEEDED(cmd_parsing.Parse(cmd_line, true));
-
-  option_data.int_value = data.int_value;
-  CheckTestData(option_data, data);
-}
-
-TEST(CommandsTest, CommandParsingSimpleSplitTest) {
-  CString exe;
-  CString args;
-
-  // Test to make sure SplitExeAndArgs correctly splits
-  // a properly constructed command line
-  ASSERT_SUCCEEDED(
-    CommandParsingSimple::SplitExeAndArgs(kIEBrowserQuotedCommandLine,
-                                          &exe,
-                                          &args));
-
-  EXPECT_STREQ(kIEBrowserQuotedExeResult, exe);
-  EXPECT_STREQ(kIEBrowserQuotedArgsResult, args);
-}
-
-TEST(CommandsTest, CommandParsingGuessSplitTest) {
-  CString exe;
-  CString args;
-
-  // Test to make sure SplitExeAndArgsGuess correctly splits
-  // a properly constructed command line
-  ASSERT_SUCCEEDED(
-    CommandParsingSimple::SplitExeAndArgsGuess(kIEBrowserQuotedCommandLine,
-                                               &exe,
-                                               &args));
-
-  EXPECT_STREQ(kIEBrowserQuotedExeResult, exe);
-  EXPECT_STREQ(kIEBrowserQuotedArgsResult, args);
-
-  // Test to make sure SplitExeAndArgsGuess correctly splits
-  // an improperly constructed "Uninstall" command line
-  ASSERT_SUCCEEDED(
-    CommandParsingSimple::SplitExeAndArgsGuess(kIEBrowserUnquotedCommandLine,
-                                               &exe,
-                                               &args));
-
-  EXPECT_STREQ(kIEBrowserUnquotedExe, exe);
-  EXPECT_STREQ(kIEBrowserUnquotedArgs, args);
-
-  // Test to make sure SplitExeAndArgsGuess correctly splits
-  // a properly constructed "Uninstall" command line, where
-  // the executable does not have a ".EXE" extension, and
-  // where there happens to be an argument which happens to
-  // be an executable
-  ASSERT_SUCCEEDED(
-    CommandParsingSimple::SplitExeAndArgsGuess(kGEUninstallCommandLine,
-                                               &exe,
-                                               &args));
-
-  EXPECT_STREQ(kGEUninstallExe, exe);
-  EXPECT_STREQ(kGEUninstallArgs, args);
-}
-
-TEST(CommandsTest, CommandParsingGuessSplit_ExtraWhiteSpace) {
-  CString exe;
-  CString args;
-
-  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
-      kIEBrowserUnquotedCommandLine _T(" "),
-      &exe,
-      &args));
-  EXPECT_STREQ(kIEBrowserUnquotedExe, exe);
-  EXPECT_STREQ(kIEBrowserUnquotedArgs, args);
-
-  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
-      kIEBrowserUnquotedCommandLine _T("\t"),
-      &exe,
-      &args));
-  EXPECT_STREQ(kIEBrowserUnquotedExe, exe);
-  EXPECT_STREQ(kIEBrowserUnquotedArgs, args);
-
-  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
-      _T(" ") kIEBrowserUnquotedCommandLine,
-      &exe,
-      &args));
-  EXPECT_STREQ(kIEBrowserUnquotedExe, exe);
-  EXPECT_STREQ(kIEBrowserUnquotedArgs, args);
-
-  // The following cases have unexpected results.
-  // Quoting a command line with args is not handled correctly.
-  // The entire thing is interpreted as an EXE.
-
-  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
-      _T("\" ") kIEBrowserUnquotedCommandLine _T("\""),
-      &exe,
-      &args));
-  EXPECT_STREQ(kIEBrowserUnquotedCommandLine, exe);
-  EXPECT_TRUE(args.IsEmpty());
-
-  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
-      _T("\"") kIEBrowserUnquotedCommandLine _T(" \""),
-      &exe,
-      &args));
-  EXPECT_STREQ(kIEBrowserUnquotedCommandLine, exe);
-  EXPECT_TRUE(args.IsEmpty());
-
-  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
-      _T("\"") kIEBrowserUnquotedCommandLine _T("\t\""),
-      &exe,
-      &args));
-  EXPECT_STREQ(kIEBrowserUnquotedCommandLine, exe);
-  EXPECT_TRUE(args.IsEmpty());
-}
-
-TEST(CommandsTest, CommandParsingGuessSplit_CommandLineIsExistingFile) {
-  CString exe;
-  CString args;
-
-  EXPECT_TRUE(File::Exists(kIEBrowserExe));
-  EXPECT_SUCCEEDED(
-    CommandParsingSimple::SplitExeAndArgsGuess(kIEBrowserExe, &exe, &args));
-  EXPECT_STREQ(kIEBrowserExe, exe);
-  EXPECT_TRUE(args.IsEmpty());
-
-  // File::Exists does not handle enclosed paths.
-  EXPECT_FALSE(File::Exists(kIEBrowserQuotedExe));
-  EXPECT_SUCCEEDED(
-    CommandParsingSimple::SplitExeAndArgsGuess(kIEBrowserQuotedExe,
-                                               &exe,
-                                               &args));
-  EXPECT_STREQ(kIEBrowserExe, exe);
-  EXPECT_TRUE(args.IsEmpty());
-}
-
-TEST(CommandsTest,
-     CommandParsingGuessSplit_CommandLineIsExistingFileWithExtraWhiteSpace) {
-  CString exe;
-  CString args;
-
-  EXPECT_TRUE(File::Exists(kIEBrowserExe));
-  // File::Exists does not handle enclosed paths.
-  EXPECT_FALSE(File::Exists(kIEBrowserQuotedExe));
-
-  EXPECT_SUCCEEDED(
-    CommandParsingSimple::SplitExeAndArgsGuess(kIEBrowserExe _T(" "),
-                                               &exe,
-                                               &args));
-  EXPECT_STREQ(kIEBrowserExe, exe);
-  EXPECT_TRUE(args.IsEmpty());
-
-  EXPECT_SUCCEEDED(
-    CommandParsingSimple::SplitExeAndArgsGuess(kIEBrowserQuotedExe _T(" "),
-                                               &exe,
-                                               &args));
-  EXPECT_STREQ(kIEBrowserExe, exe);
-  EXPECT_TRUE(args.IsEmpty());
-
-  EXPECT_SUCCEEDED(
-    CommandParsingSimple::SplitExeAndArgsGuess(kIEBrowserExe _T("\t"),
-                                               &exe,
-                                               &args));
-  EXPECT_STREQ(kIEBrowserExe, exe);
-  EXPECT_TRUE(args.IsEmpty());
-
-  EXPECT_SUCCEEDED(
-    CommandParsingSimple::SplitExeAndArgsGuess(kIEBrowserQuotedExe _T("\t"),
-                                               &exe,
-                                               &args));
-  EXPECT_STREQ(kIEBrowserExe, exe);
-  // SplitExeAndArgs does not treat tab like whitespace and args aren't trimmed.
-  EXPECT_STREQ(_T("\t"), args);
-
-  EXPECT_SUCCEEDED(
-    CommandParsingSimple::SplitExeAndArgsGuess(_T(" ") kIEBrowserExe,
-                                               &exe,
-                                               &args));
-  EXPECT_STREQ(kIEBrowserExe, exe);
-  EXPECT_TRUE(args.IsEmpty());
-
-  EXPECT_SUCCEEDED(
-    CommandParsingSimple::SplitExeAndArgsGuess(_T(" ") kIEBrowserQuotedExe,
-                                               &exe,
-                                               &args));
-  EXPECT_STREQ(kIEBrowserExe, exe);
-  EXPECT_TRUE(args.IsEmpty());
-
-  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
-                       _T("\" ") kIEBrowserExe _T("\""),
-                       &exe,
-                       &args));
-  EXPECT_STREQ(kIEBrowserExe, exe);
-  EXPECT_TRUE(args.IsEmpty());
-
-  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
-                       _T("\"") kIEBrowserExe _T(" \""),
-                       &exe,
-                       &args));
-  EXPECT_STREQ(kIEBrowserExe, exe);
-  EXPECT_TRUE(args.IsEmpty());
-
-  EXPECT_SUCCEEDED(CommandParsingSimple::SplitExeAndArgsGuess(
-                       _T("\"") kIEBrowserExe _T("\t\""),
-                       &exe,
-                       &args));
-  EXPECT_STREQ(kIEBrowserExe, exe);
-  EXPECT_TRUE(args.IsEmpty());
-}
-
-}  // namespace omaha
-
diff --git a/common/commontypes.h b/common/commontypes.h
deleted file mode 100644
index bcc7bfe..0000000
--- a/common/commontypes.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// TODO(omaha): refactor to eliminate this file.
-
-#ifndef OMAHA_COMMON_COMMONTYPES_H_
-#define OMAHA_COMMON_COMMONTYPES_H_
-
-#include "base/basictypes.h"
-
-namespace omaha {
-
-// ---------------------- Defines -------------------------------
-// Define some variable types, so that we can change them under the hood
-// easier if need be.
-typedef unsigned char      uchar;
-
-typedef uint32 flags32;
-typedef uint16 time16;
-typedef uint64 time64;
-
-#define kInt32Max 2147483647
-#define kUint32Max 4294967295U
-#define kUint64Max 18446744073709551615U
-#define kTime64Max kUint64Max
-
-
-template <typename T>
-inline T ABS(T val) {
-  return val < (T)0 ? -val : val;
-}
-
-// Isolate some VisualC++-isms to macros for easy redefinition
-#define SELECTANY __declspec(selectany)
-#define DLLIMPORT __declspec(dllimport)
-#define DLLEXPORT __declspec(dllexport)
-
-// Put this around string literals that don't need to be localized
-// to indicate this fact.  Note that you don't need to do this for string
-// literals used in functions where they obviously don't need to be localized,
-// such as REPORT(), XXX_LOG(), CHK(), ASSERT(), VERIFY(), TRACE(), dbgprint(),
-// OutputDebugString(), GetProcAddress(), GetModuleHandle(), etc.
-//
-// For large blocks of non-localizable string literals, you can use a comment
-// line including "SKIP_LOC_BEGIN" to start a non-localizable section of
-// your file, and "SKIP_LOC_END" to end the section.
-//
-// Don't worry about NOTRANSL or the SKIP_LOC blocks in unit tests, experimental
-// code, etc. as they are ignored when checking for localizable string literals.
-#define NOTRANSL(x) x
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_COMMONTYPES_H_
diff --git a/common/config_manager.cc b/common/config_manager.cc
new file mode 100644
index 0000000..7524539
--- /dev/null
+++ b/common/config_manager.cc
@@ -0,0 +1,817 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/config_manager.h"
+#include <lm.h>
+#include <shlobj.h>
+#include <shlwapi.h>
+#include <wininet.h>
+#include <atlstr.h>
+#include <atlsecurity.h>
+#include <math.h>
+#include "omaha/base/app_util.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/string.h"
+#include "omaha/base/time.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/app_registry_utils.h"
+#include "omaha/common/const_group_policy.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/oem_install_utils.h"
+
+namespace omaha {
+
+namespace {
+
+HRESULT GetDir(int csidl,
+               const CString& path_tail,
+               bool create_dir,
+               CString* dir) {
+  ASSERT1(dir);
+
+  CString path;
+  HRESULT hr = GetFolderPath(csidl | CSIDL_FLAG_DONT_VERIFY, &path);
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("GetDir failed to find path][%d][0x%08x]"), csidl, hr));
+    return hr;
+  }
+  if (!::PathAppend(CStrBuf(path, MAX_PATH), path_tail)) {
+    CORE_LOG(LW, (_T("GetDir failed to append path][%s][%s]"), path, path_tail));
+    return GOOPDATE_E_PATH_APPEND_FAILED;
+  }
+  dir->SetString(path);
+
+  // Try to create the directory. Continue if the directory can't be created.
+  if (create_dir) {
+    hr = CreateDir(path, NULL);
+    if (FAILED(hr)) {
+      CORE_LOG(LW, (_T("[GetDir failed to create dir][%s][0x%08x]"), path, hr));
+    }
+  }
+  return S_OK;
+}
+
+// The app-specific value overrides the disable all value so read the former
+// first. If it doesn't exist, read the "disable all" value.
+bool GetEffectivePolicyForApp(const TCHAR* apps_default_value_name,
+                              const TCHAR* app_prefix_name,
+                              const GUID& app_guid,
+                              DWORD* effective_policy) {
+  ASSERT1(apps_default_value_name);
+  ASSERT1(app_prefix_name);
+  ASSERT1(effective_policy);
+
+  CString app_value_name(app_prefix_name);
+  app_value_name.Append(GuidToString(app_guid));
+
+  HRESULT hr = RegKey::GetValue(kRegKeyGoopdateGroupPolicy,
+                                app_value_name,
+                                effective_policy);
+  if (SUCCEEDED(hr)) {
+    return true;
+  } else {
+    CORE_LOG(L4, (_T("[Failed to read Group Policy value][%s]"),
+                  app_value_name));
+  }
+
+  hr = RegKey::GetValue(kRegKeyGoopdateGroupPolicy,
+                        apps_default_value_name,
+                        effective_policy);
+  if (SUCCEEDED(hr)) {
+    return true;
+  } else {
+    CORE_LOG(L4, (_T("[Failed to read Group Policy value][%s]"),
+                  apps_default_value_name));
+  }
+
+  return false;
+}
+
+// Gets the raw update check period override value in seconds from the registry.
+// The value must be processed for limits and overflow before using.
+// Checks UpdateDev and Group Policy.
+// Returns true if either override was successefully read.
+bool GetLastCheckPeriodSecFromRegistry(DWORD* period_sec) {
+  ASSERT1(period_sec);
+
+  DWORD update_dev_sec = 0;
+  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                                 kRegValueLastCheckPeriodSec,
+                                 &update_dev_sec))) {
+    CORE_LOG(L5, (_T("['LastCheckPeriodSec' override %d]"), update_dev_sec));
+    *period_sec = update_dev_sec;
+    return true;
+  }
+
+  DWORD group_policy_minutes = 0;
+  if (SUCCEEDED(RegKey::GetValue(kRegKeyGoopdateGroupPolicy,
+                                 kRegValueAutoUpdateCheckPeriodOverrideMinutes,
+                                 &group_policy_minutes))) {
+    CORE_LOG(L5, (_T("[Group Policy check period override %d]"),
+                  group_policy_minutes));
+
+
+    *period_sec = (group_policy_minutes > UINT_MAX / 60) ?
+                  UINT_MAX :
+                  group_policy_minutes * 60;
+
+    return true;
+  }
+
+  return false;
+}
+
+}  // namespace
+
+LLock ConfigManager::lock_;
+ConfigManager* ConfigManager::config_manager_ = NULL;
+
+ConfigManager* ConfigManager::Instance() {
+  __mutexScope(lock_);
+  if (!config_manager_) {
+    config_manager_ = new ConfigManager();
+  }
+  return config_manager_;
+}
+
+void ConfigManager::DeleteInstance() {
+  delete config_manager_;
+}
+
+ConfigManager::ConfigManager() {
+  CString current_module_directory(app_util::GetCurrentModuleDirectory());
+
+  CString path;
+  HRESULT hr = GetDir(CSIDL_LOCAL_APPDATA,
+                      CString(OMAHA_REL_GOOPDATE_INSTALL_DIR),
+                      false,
+                      &path);
+
+  is_running_from_official_user_dir_ =
+      SUCCEEDED(hr) ? (String_StrNCmp(path,
+                                      current_module_directory,
+                                      path.GetLength(),
+                                      true) == 0) :
+                      false;
+
+  hr = GetDir(CSIDL_PROGRAM_FILES,
+              CString(OMAHA_REL_GOOPDATE_INSTALL_DIR),
+              false,
+              &path);
+
+  is_running_from_official_machine_dir_ =
+      SUCCEEDED(hr) ? (String_StrNCmp(path,
+                                      current_module_directory,
+                                      path.GetLength(),
+                                      true) == 0) :
+                      false;
+}
+
+CString ConfigManager::GetUserDownloadStorageDir() const {
+  CString path;
+  VERIFY1(SUCCEEDED(GetDir(CSIDL_LOCAL_APPDATA,
+                           CString(OMAHA_REL_DOWNLOAD_STORAGE_DIR),
+                           true,
+                           &path)));
+  return path;
+}
+
+CString ConfigManager::GetUserInstallWorkingDir() const {
+  CString path;
+  VERIFY1(SUCCEEDED(GetDir(CSIDL_LOCAL_APPDATA,
+                           CString(OMAHA_REL_INSTALL_WORKING_DIR),
+                           true,
+                           &path)));
+  return path;
+}
+
+CString ConfigManager::GetUserOfflineStorageDir() const {
+  CString path;
+  VERIFY1(SUCCEEDED(GetDir(CSIDL_LOCAL_APPDATA,
+                           CString(OMAHA_REL_OFFLINE_STORAGE_DIR),
+                           true,
+                           &path)));
+  return path;
+}
+
+CString ConfigManager::GetUserGoopdateInstallDirNoCreate() const {
+  CString path;
+  VERIFY1(SUCCEEDED(GetDir(CSIDL_LOCAL_APPDATA,
+                           CString(OMAHA_REL_GOOPDATE_INSTALL_DIR),
+                           false,
+                           &path)));
+  return path;
+}
+
+CString ConfigManager::GetUserGoopdateInstallDir() const {
+  CString path;
+  VERIFY1(SUCCEEDED(GetDir(CSIDL_LOCAL_APPDATA,
+                           CString(OMAHA_REL_GOOPDATE_INSTALL_DIR),
+                           true,
+                           &path)));
+  return path;
+}
+
+bool ConfigManager::IsRunningFromUserGoopdateInstallDir() const {
+  return is_running_from_official_user_dir_;
+}
+
+CString ConfigManager::GetUserCrashReportsDir() const {
+  CString path;
+  VERIFY1(SUCCEEDED(GetDir(CSIDL_LOCAL_APPDATA,
+                           CString(OMAHA_REL_CRASH_DIR),
+                           true,
+                           &path)));
+  return path;
+}
+
+CString ConfigManager::GetMachineCrashReportsDir() const {
+  CString path;
+  VERIFY1(SUCCEEDED(GetDir(CSIDL_PROGRAM_FILES,
+                           CString(OMAHA_REL_CRASH_DIR),
+                           true,
+                           &path)));
+  return path;
+}
+
+CString ConfigManager::GetMachineSecureDownloadStorageDir() const {
+  CString path;
+  VERIFY1(SUCCEEDED(GetDir(CSIDL_PROGRAM_FILES,
+                           CString(OMAHA_REL_DOWNLOAD_STORAGE_DIR),
+                           true,
+                           &path)));
+  return path;
+}
+
+CString ConfigManager::GetMachineInstallWorkingDir() const {
+  CString path;
+  VERIFY1(SUCCEEDED(GetDir(CSIDL_PROGRAM_FILES,
+                           CString(OMAHA_REL_INSTALL_WORKING_DIR),
+                           true,
+                           &path)));
+  return path;
+}
+
+CString ConfigManager::GetMachineSecureOfflineStorageDir() const {
+  CString path;
+  VERIFY1(SUCCEEDED(GetDir(CSIDL_PROGRAM_FILES,
+                           CString(OMAHA_REL_OFFLINE_STORAGE_DIR),
+                           true,
+                           &path)));
+  return path;
+}
+
+CString ConfigManager::GetTempDownloadDir() const {
+  CString temp_download_dir(app_util::GetTempDirForImpersonatedOrCurrentUser());
+  ASSERT1(temp_download_dir);
+  HRESULT hr = CreateDir(temp_download_dir, NULL);
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[GetDir failed to create dir][%s][0x%08x]"),
+        temp_download_dir, hr));
+  }
+  return temp_download_dir;
+}
+
+int ConfigManager::GetPackageCacheSizeLimitMBytes() const {
+  DWORD kDefaultCacheStorageLimit = 500;  // 500 MB
+  DWORD kMaxCacheStorageLimit = 5000;     // 5 GB
+
+  DWORD cache_size_limit = 0;
+  if (FAILED(RegKey::GetValue(kRegKeyGoopdateGroupPolicy,
+                              kRegValueCacheSizeLimitMBytes,
+                              &cache_size_limit)) ||
+      cache_size_limit > kMaxCacheStorageLimit ||
+      cache_size_limit == 0) {
+    cache_size_limit = kDefaultCacheStorageLimit;
+  }
+
+  return static_cast<int>(cache_size_limit);
+}
+
+int ConfigManager::GetPackageCacheExpirationTimeDays() const {
+  DWORD kDefaultCacheLifeTimeInDays = 180;  // 180 days.
+  DWORD kMaxCacheLifeTimeInDays = 1800;     // Roughly 5 years.
+
+  DWORD cache_life_limit = 0;
+  if (FAILED(RegKey::GetValue(kRegKeyGoopdateGroupPolicy,
+                              kRegValueCacheLifeLimitDays,
+                              &cache_life_limit)) ||
+      cache_life_limit > kMaxCacheLifeTimeInDays ||
+      cache_life_limit == 0) {
+    cache_life_limit = kDefaultCacheLifeTimeInDays;
+  }
+
+  return static_cast<int>(cache_life_limit);
+}
+
+CString ConfigManager::GetMachineGoopdateInstallDirNoCreate() const {
+  CString path;
+  VERIFY1(SUCCEEDED(GetDir(CSIDL_PROGRAM_FILES,
+                           CString(OMAHA_REL_GOOPDATE_INSTALL_DIR),
+                           false,
+                           &path)));
+  return path;
+}
+
+CString ConfigManager::GetMachineGoopdateInstallDir() const {
+  CString path;
+  VERIFY1(SUCCEEDED(GetDir(CSIDL_PROGRAM_FILES,
+                           CString(OMAHA_REL_GOOPDATE_INSTALL_DIR),
+                           true,
+                           &path)));
+  return path;
+}
+
+bool ConfigManager::IsRunningFromMachineGoopdateInstallDir() const {
+  return is_running_from_official_machine_dir_;
+}
+
+HRESULT ConfigManager::GetPingUrl(CString* url) const {
+  ASSERT1(url);
+
+  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                                 kRegValueNamePingUrl,
+                                 url))) {
+    CORE_LOG(L5, (_T("['ping url' override %s]"), *url));
+    return S_OK;
+  }
+
+  *url = kUrlPing;
+  return S_OK;
+}
+
+HRESULT ConfigManager::GetUpdateCheckUrl(CString* url) const {
+  ASSERT1(url);
+
+  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                                 kRegValueNameUrl,
+                                 url))) {
+    CORE_LOG(L5, (_T("['update check url' override %s]"), *url));
+    return S_OK;
+  }
+
+  *url = kUrlUpdateCheck;
+  return S_OK;
+}
+
+HRESULT ConfigManager::GetCrashReportUrl(CString* url) const {
+  ASSERT1(url);
+
+  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                                 kRegValueNameCrashReportUrl,
+                                 url))) {
+    CORE_LOG(L5, (_T("['crash report url' override %s]"), *url));
+    return S_OK;
+  }
+
+  *url = kUrlCrashReport;
+  return S_OK;
+}
+
+HRESULT ConfigManager::GetMoreInfoUrl(CString* url) const {
+  ASSERT1(url);
+
+  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                                 kRegValueNameGetMoreInfoUrl,
+                                 url))) {
+    CORE_LOG(L5, (_T("['more info url' override %s]"), *url));
+    return S_OK;
+  }
+
+  *url = kUrlMoreInfo;
+  return S_OK;
+}
+
+HRESULT ConfigManager::GetUsageStatsReportUrl(CString* url) const {
+  ASSERT1(url);
+
+  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                                 kRegValueNameUsageStatsReportUrl,
+                                 url))) {
+    CORE_LOG(L5, (_T("['usage stats report url' override %s]"), *url));
+    return S_OK;
+  }
+
+  *url = kUrlUsageStatsReport;
+  return S_OK;
+}
+
+// Returns the override from the registry locations if present. Otherwise,
+// returns the default value.
+// Default value is different value for internal users to make update checks
+// more aggresive.
+// Ensures returned value is between kMinLastCheckPeriodSec and INT_MAX except
+// when the override is 0, which indicates updates are disabled.
+int ConfigManager::GetLastCheckPeriodSec(bool* is_overridden) const {
+  ASSERT1(is_overridden);
+  DWORD registry_period_sec = 0;
+  *is_overridden = GetLastCheckPeriodSecFromRegistry(&registry_period_sec);
+  if (*is_overridden) {
+    if (0 == registry_period_sec) {
+      return 0;
+    }
+    const int period_sec = registry_period_sec > INT_MAX ?
+                           INT_MAX :
+                           static_cast<int>(registry_period_sec);
+
+    if (period_sec < kMinLastCheckPeriodSec) {
+      return kMinLastCheckPeriodSec;
+    }
+    return period_sec;
+  }
+
+  // Returns a lower value for internal users.
+  if (IsInternalUser()) {
+    return kLastCheckPeriodInternalUserSec;
+  }
+
+  return kLastCheckPeriodSec;
+}
+
+// All time values are in seconds.
+int ConfigManager::GetTimeSinceLastCheckedSec(bool is_machine) const {
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+  const uint32 last_checked = GetLastCheckedTime(is_machine);
+  if (now < last_checked) {
+    CORE_LOG(LW, (_T("[possible time warp detected]")
+                  _T("[now %u][last checked %u]"), now, last_checked));
+  }
+  const int time_difference = abs(static_cast<int>(now - last_checked));
+  bool is_period_overridden = false;
+  CORE_LOG(L3, (_T("[now %u][last checked %u][update interval %u]")
+                _T("[time difference %u]"),
+                now, last_checked, GetLastCheckPeriodSec(&is_period_overridden),
+                time_difference));
+  return time_difference;
+}
+
+DWORD ConfigManager::GetLastCheckedTime(bool is_machine) const {
+  const TCHAR* reg_update_key = is_machine ? MACHINE_REG_UPDATE:
+                                             USER_REG_UPDATE;
+  DWORD last_checked_time = 0;
+  if (SUCCEEDED(RegKey::GetValue(reg_update_key,
+                                 kRegValueLastChecked,
+                                 &last_checked_time))) {
+    return last_checked_time;
+  }
+  return 0;
+}
+
+HRESULT ConfigManager::SetLastCheckedTime(bool is_machine, DWORD time) const {
+  const TCHAR* reg_update_key = is_machine ? MACHINE_REG_UPDATE:
+                                             USER_REG_UPDATE;
+  return RegKey::SetValue(reg_update_key, kRegValueLastChecked, time);
+}
+
+DWORD ConfigManager::GetInstallTime(bool is_machine) {
+  const CString client_state_key_name =
+      ConfigManager::Instance()->registry_client_state_goopdate(is_machine);
+  DWORD update_time(0);
+  if (SUCCEEDED(RegKey::GetValue(client_state_key_name,
+                                 kRegValueLastUpdateTimeSec,
+                                 &update_time))) {
+    return update_time;
+  }
+
+  DWORD install_time(0);
+  if (SUCCEEDED(RegKey::GetValue(client_state_key_name,
+                                 kRegValueInstallTimeSec,
+                                 &install_time))) {
+    return install_time;
+  }
+
+  return 0;
+}
+
+bool ConfigManager::Is24HoursSinceInstall(bool is_machine) {
+  const int kDaySec = 24 * 60 * 60;
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  const uint32 install_time = GetInstallTime(is_machine);
+  if (now < install_time) {
+    CORE_LOG(LW, (_T("[Incorrect clock time detected]")
+                  _T("[now %u][install_time %u]"), now, install_time));
+  }
+  const int time_difference = abs(static_cast<int>(now - install_time));
+  return time_difference >= kDaySec;
+}
+
+// Uses app_registry_utils because this needs to be called in the server and
+// client and it is a best effort so locking isn't necessary.
+bool ConfigManager::CanCollectStats(bool is_machine) const {
+  if (RegKey::HasValue(MACHINE_REG_UPDATE_DEV, kRegValueForceUsageStats)) {
+    return true;
+  }
+
+  // TODO(omaha): This should actually be iterating over registered products
+  // rather than present ClientState keys. These are identical in most cases.
+  const TCHAR* state_key_name = registry_client_state(is_machine);
+
+  RegKey state_key;
+  HRESULT hr = state_key.Open(state_key_name, KEY_READ);
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  int num_sub_keys = state_key.GetSubkeyCount();
+  for (int i = 0; i < num_sub_keys; ++i) {
+    CString sub_key_name;
+    if (FAILED(state_key.GetSubkeyNameAt(i, &sub_key_name))) {
+      continue;
+    }
+
+    if (app_registry_utils::AreAppUsageStatsEnabled(is_machine, sub_key_name)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+// Overrides OverInstall in debug builds.
+bool ConfigManager::CanOverInstall() const {
+#ifdef DEBUG
+  DWORD value = 0;
+  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                                 kRegValueNameOverInstall,
+                                 &value))) {
+    CORE_LOG(L5, (_T("['OverInstall' override %d]"), value));
+    return value != 0;
+  }
+#endif
+  return !OFFICIAL_BUILD;
+}
+
+// Overrides AuCheckPeriodMs. Implements a lower bound value. Returns INT_MAX
+// if the registry value exceeds INT_MAX.
+int ConfigManager::GetAutoUpdateTimerIntervalMs() const {
+  DWORD interval(0);
+  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                                 kRegValueAuCheckPeriodMs,
+                                 &interval))) {
+    int ret_val = 0;
+    if (interval > INT_MAX) {
+      ret_val = INT_MAX;
+    } else if (interval < kMinAUCheckPeriodMs) {
+      ret_val = kMinAUCheckPeriodMs;
+    } else {
+      ret_val = interval;
+    }
+    ASSERT1(ret_val >= kMinAUCheckPeriodMs);
+    CORE_LOG(L5, (_T("['AuCheckPeriodMs' override %d]"), interval));
+    return ret_val;
+  }
+
+  // Returns a lower value for internal users.
+  if (IsInternalUser()) {
+    return kAUCheckPeriodInternalUserMs;
+  }
+
+  return kAUCheckPeriodMs;
+}
+
+int ConfigManager::GetUpdateWorkerStartUpDelayMs() const {
+  int au_timer_interval_ms = GetAutoUpdateTimerIntervalMs();
+
+  // If the AuCheckPeriod is overriden then use that as the delay.
+  if (RegKey::HasValue(MACHINE_REG_UPDATE_DEV, kRegValueAuCheckPeriodMs)) {
+    return au_timer_interval_ms;
+  }
+
+  int random_delay = 0;
+  if (!GenRandom(&random_delay, sizeof(random_delay))) {
+    return au_timer_interval_ms;
+  }
+
+  // Scale the au_check_period number to be between
+  // kUpdateTimerStartupDelayMinMs and kUpdateTimerStartupDelayMaxMs.
+  int scale = kUpdateTimerStartupDelayMaxMs - kUpdateTimerStartupDelayMinMs;
+  ASSERT1(scale >= 0);
+
+  int random_addition = abs(random_delay) % scale;
+  ASSERT1(random_addition < scale);
+
+  au_timer_interval_ms = kUpdateTimerStartupDelayMinMs + random_addition;
+  ASSERT1(au_timer_interval_ms >= kUpdateTimerStartupDelayMinMs &&
+          au_timer_interval_ms <= kUpdateTimerStartupDelayMaxMs);
+
+  return au_timer_interval_ms;
+}
+
+// Overrides CodeRedCheckPeriodMs. Implements a lower bound value. Returns
+// INT_MAX if the registry value exceeds INT_MAX.
+int ConfigManager::GetCodeRedTimerIntervalMs() const {
+  DWORD interval(0);
+  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                                 kRegValueCrCheckPeriodMs,
+                                 &interval))) {
+    int ret_val = 0;
+    if (interval > INT_MAX) {
+      ret_val = INT_MAX;
+    } else if (interval < kMinCodeRedCheckPeriodMs) {
+      ret_val = kMinCodeRedCheckPeriodMs;
+    } else {
+      ret_val = interval;
+    }
+    ASSERT1(ret_val >= kMinCodeRedCheckPeriodMs);
+    CORE_LOG(L5, (_T("['CrCheckPeriodMs' override %d]"), interval));
+    return ret_val;
+  }
+  return kCodeRedCheckPeriodMs;
+}
+
+// Returns true if logging is enabled for the event type.
+// Logging of errors and warnings is enabled by default.
+bool ConfigManager::CanLogEvents(WORD event_type) const {
+  const TCHAR* reg_update_key = MACHINE_REG_UPDATE_DEV;
+  DWORD log_events_level = LOG_EVENT_LEVEL_NONE;
+  if (SUCCEEDED(RegKey::GetValue(reg_update_key,
+                                 kRegValueEventLogLevel,
+                                 &log_events_level))) {
+    switch (log_events_level) {
+      case LOG_EVENT_LEVEL_ALL:
+        return true;
+      case LOG_EVENT_LEVEL_WARN_AND_ERROR:
+        return event_type == EVENTLOG_ERROR_TYPE ||
+               event_type == EVENTLOG_WARNING_TYPE;
+      case LOG_EVENT_LEVEL_NONE:
+      default:
+        return false;
+    }
+  }
+
+  return event_type == EVENTLOG_ERROR_TYPE ||
+         event_type == EVENTLOG_WARNING_TYPE;
+}
+
+CString ConfigManager::GetTestSource() const {
+  CString test_source;
+  HRESULT hr = RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                                kRegValueTestSource,
+                                &test_source);
+  if (SUCCEEDED(hr)) {
+    if (test_source.IsEmpty()) {
+      test_source = kRegValueTestSourceAuto;
+    }
+    return test_source;
+  }
+
+  DWORD interval = 0;
+  hr = RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                        kRegValueAuCheckPeriodMs,
+                        &interval);
+  if (SUCCEEDED(hr)) {
+    return kRegValueTestSourceAuto;
+  }
+
+#if defined(DEBUG) || !OFFICIAL_BUILD
+  test_source = kRegValueTestSourceAuto;
+#endif
+
+  return test_source;
+}
+
+HRESULT ConfigManager::GetNetConfig(CString* net_config) {
+  ASSERT1(net_config);
+  CString val;
+  HRESULT hr = RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                                kRegValueNetConfig,
+                                &val);
+  if (SUCCEEDED(hr)) {
+    *net_config = val;
+  }
+  return hr;
+}
+
+// Returns false if running in the context of an OEM install or waiting for a
+// EULA to be accepted.
+bool ConfigManager::CanUseNetwork(bool is_machine) const {
+  DWORD eula_accepted(0);
+  HRESULT hr = RegKey::GetValue(registry_update(is_machine),
+                                kRegValueOmahaEulaAccepted,
+                                &eula_accepted);
+  if (SUCCEEDED(hr) && 0 == eula_accepted) {
+    CORE_LOG(L3, (_T("[CanUseNetwork][eulaaccepted=0][false]")));
+    return false;
+  }
+
+  if (oem_install_utils::IsOemInstalling(is_machine)) {
+    CORE_LOG(L3, (_T("[CanUseNetwork][OEM installing][false]")));
+    return false;
+  }
+
+  return true;
+}
+
+// USE oem_install_utils::IsOemInstalling() INSTEAD in most cases.
+bool ConfigManager::IsWindowsInstalling() const {
+#if !OFFICIAL_BUILD
+  DWORD value = 0;
+  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                                 kRegValueNameWindowsInstalling,
+                                 &value))) {
+    CORE_LOG(L3, (_T("['WindowsInstalling' override %d]"), value));
+    return value != 0;
+  }
+#endif
+
+  return omaha::IsWindowsInstalling();
+}
+
+// Checks if the computer name ends with ".google.com" or the NetBIOS domain is
+// "google".
+bool ConfigManager::IsInternalUser() const {
+  CORE_LOG(L4, (_T("[ConfigManager::IsInternalUser]")));
+  TCHAR dns_name[INTERNET_MAX_HOST_NAME_LENGTH] = {0};
+  DWORD dns_name_size(arraysize(dns_name));
+  if (::GetComputerNameEx(ComputerNameDnsFullyQualified,
+                          dns_name, &dns_name_size)) {
+     CORE_LOG(L4, (_T("[dns name %s]"), dns_name));
+     if (String_EndsWith(dns_name, kCompanyInternalDnsName, true)) {
+       return true;
+     }
+  }
+
+  WKSTA_INFO_100* info = NULL;
+  int kInformationLevel = 100;
+  NET_API_STATUS status = ::NetWkstaGetInfo(NULL,
+                                            kInformationLevel,
+                                            reinterpret_cast<BYTE**>(&info));
+  ON_SCOPE_EXIT(::NetApiBufferFree, info);
+  if (status == NERR_Success) {
+    CORE_LOG(L4, (_T("[netbios name %s]"), info->wki100_langroup));
+    if (info->wki100_langroup &&
+        _tcsicmp(info->wki100_langroup, kCompanyInternalLanGroupName) == 0) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool ConfigManager::CanInstallApp(const GUID& app_guid) const {
+  // Google Update should never be checking whether it can install itself.
+  ASSERT1(!::IsEqualGUID(kGoopdateGuid, app_guid));
+
+  DWORD effective_policy = 0;
+  if (!GetEffectivePolicyForApp(kRegValueInstallAppsDefault,
+                                kRegValueInstallAppPrefix,
+                                app_guid,
+                                &effective_policy)) {
+    return kInstallPolicyDefault;
+  }
+
+  return kPolicyDisabled != effective_policy;
+}
+
+// Self-updates cannot be disabled.
+bool ConfigManager::CanUpdateApp(const GUID& app_guid,
+                                 bool is_manual) const {
+  if (::IsEqualGUID(kGoopdateGuid, app_guid)) {
+    return true;
+  }
+
+  DWORD effective_policy = 0;
+  if (!GetEffectivePolicyForApp(kRegValueUpdateAppsDefault,
+                                kRegValueUpdateAppPrefix,
+                                app_guid,
+                                &effective_policy)) {
+    return kUpdatePolicyDefault;
+  }
+
+  if (kPolicyDisabled == effective_policy) {
+    return false;
+  }
+  if ((kPolicyManualUpdatesOnly == effective_policy) && !is_manual) {
+    return false;
+  }
+
+  return kUpdatePolicyDefault;
+}
+
+bool ConfigManager::AlwaysAllowCrashUploads() const {
+  DWORD always_allow_crash_uploads = 0;
+  RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                   kRegValueAlwaysAllowCrashUploads,
+                   &always_allow_crash_uploads);
+  return always_allow_crash_uploads != 0;
+}
+
+}  // namespace omaha
diff --git a/common/config_manager.h b/common/config_manager.h
new file mode 100644
index 0000000..ece20a2
--- /dev/null
+++ b/common/config_manager.h
@@ -0,0 +1,256 @@
+// Copyright 2005-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// The configuration manager that is used to provide the locations of the
+// directory and the registration entries that are to be used by goopdate.
+
+// TODO(omaha): consider removing some of the functions below and have a
+// parameter is_machine instead. This is consistent with the rest of the code
+// and it reduces the number of functions in the public interface.
+
+#ifndef OMAHA_COMMON_CONFIG_MANAGER_H_
+#define OMAHA_COMMON_CONFIG_MANAGER_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/synchronized.h"
+
+namespace omaha {
+
+class ConfigManager {
+ public:
+  const TCHAR* user_registry_clients() const { return USER_REG_CLIENTS; }
+  const TCHAR* user_registry_clients_goopdate() const {
+    return USER_REG_CLIENTS_GOOPDATE;
+  }
+  const TCHAR* user_registry_client_state() const {
+    return USER_REG_CLIENT_STATE;
+  }
+  const TCHAR* user_registry_client_state_goopdate() const {
+    return USER_REG_CLIENT_STATE_GOOPDATE;
+  }
+  const TCHAR* user_registry_update() const { return USER_REG_UPDATE; }
+  const TCHAR* user_registry_google() const { return USER_REG_GOOGLE; }
+
+  const TCHAR* machine_registry_clients() const { return MACHINE_REG_CLIENTS; }
+  const TCHAR* machine_registry_clients_goopdate() const {
+    return MACHINE_REG_CLIENTS_GOOPDATE;
+  }
+  const TCHAR* machine_registry_client_state() const {
+    return MACHINE_REG_CLIENT_STATE;
+  }
+  const TCHAR* machine_registry_client_state_goopdate() const {
+    return MACHINE_REG_CLIENT_STATE_GOOPDATE;
+  }
+  const TCHAR* machine_registry_client_state_medium() const {
+    return MACHINE_REG_CLIENT_STATE_MEDIUM;
+  }
+  const TCHAR* machine_registry_update() const { return MACHINE_REG_UPDATE; }
+  const TCHAR* machine_registry_google() const { return MACHINE_REG_GOOGLE; }
+
+  const TCHAR* registry_clients(bool is_machine) const {
+    return is_machine ? machine_registry_clients() : user_registry_clients();
+  }
+  const TCHAR* registry_clients_goopdate(bool is_machine) const {
+    return is_machine ? machine_registry_clients_goopdate() :
+                        user_registry_clients_goopdate();
+  }
+  const TCHAR* registry_client_state(bool is_machine) const {
+    return is_machine ? machine_registry_client_state() :
+                        user_registry_client_state();
+  }
+  const TCHAR* registry_client_state_goopdate(bool is_machine) const {
+    return is_machine ? machine_registry_client_state_goopdate() :
+                        user_registry_client_state_goopdate();
+  }
+  const TCHAR* registry_update(bool is_machine) const {
+    return is_machine ? machine_registry_update() : user_registry_update();
+  }
+  const TCHAR* registry_google(bool is_machine) const {
+    return is_machine ? machine_registry_google() : user_registry_google();
+  }
+
+  // Gets the temporary download dir for the current thread token:
+  // %UserProfile%/AppData/Local/Temp
+  CString GetTempDownloadDir() const;
+
+  // Gets the total disk size limit for cached packages. When this limit is hit,
+  // packages should be deleted from oldest until total size is below the limit.
+  int GetPackageCacheSizeLimitMBytes() const;
+
+  // Gets the package cache life limit. If a cached package is older than this
+  // limit, it should be removed.
+  int GetPackageCacheExpirationTimeDays() const;
+
+  // Creates download data dir:
+  // %UserProfile%/Application Data/Google/Update/Download
+  // This is the root of the package cache for the user.
+  // TODO(omaha): consider renaming.
+  CString GetUserDownloadStorageDir() const;
+
+  // Creates install data dir:
+  // %UserProfile%/Application Data/Google/Update/Install
+  // Files pending user installs are copied in this directory.
+  CString GetUserInstallWorkingDir() const;
+
+  // Creates offline data dir:
+  // %UserProfile%/Application Data/Google/Update/Offline
+  CString GetUserOfflineStorageDir() const;
+
+  // Returns goopdate install dir:
+  // %UserProfile%/Application Data/Google/Update
+  CString GetUserGoopdateInstallDirNoCreate() const;
+
+  // Creates goopdate install dir:
+  // %UserProfile%/Application Data/Google/Update
+  CString GetUserGoopdateInstallDir() const;
+
+  // Checks if the running program is executing from the User Goopdate dir.
+  bool IsRunningFromUserGoopdateInstallDir() const;
+
+  // Creates crash reports dir:
+  // %UserProfile%/Local Settings/Application Data/Google/CrashReports
+  CString GetUserCrashReportsDir() const;
+
+  // Creates crash reports dir: %ProgramFiles%/Google/CrashReports
+  CString GetMachineCrashReportsDir() const;
+
+  // Creates machine download data dir:
+  // %ProgramFiles%/Google/Update/Download
+  // This directory is the root of the package cache for the machine.
+  // TODO(omaha): consider renaming.
+  CString GetMachineSecureDownloadStorageDir() const;
+
+  // Creates install data dir:
+  // %ProgramFiles%/Google/Update/Install
+  // Files pending machine installs are copied in this directory.
+  CString GetMachineInstallWorkingDir() const;
+
+  // Creates machine offline data dir:
+  // %ProgramFiles%/Google/Update/Offline
+  CString GetMachineSecureOfflineStorageDir() const;
+
+  // Creates machine Gogole Update install dir:
+  // %ProgramFiles%/Google/Update
+  CString GetMachineGoopdateInstallDirNoCreate() const;
+
+  // Creates machine Gogole Update install dir:
+  // %ProgramFiles%/Google/Update
+  CString GetMachineGoopdateInstallDir() const;
+
+  // Checks if the running program is executing from the User Goopdate dir.
+  bool IsRunningFromMachineGoopdateInstallDir() const;
+
+  // Returns the service endpoint where the install/update/uninstall pings
+  // are being sent.
+  HRESULT GetPingUrl(CString* url) const;
+
+  // Returns the service endpoint where the update checks are sent.
+  HRESULT GetUpdateCheckUrl(CString* url) const;
+
+  // Returns the service endpoint where the crashes are sent.
+  HRESULT GetCrashReportUrl(CString* url) const;
+
+  // Returns the web page url where the 'Get Help' requests are sent.
+  HRESULT GetMoreInfoUrl(CString* url) const;
+
+  // Returns the service endpoint where the usage stats requests are sent.
+  HRESULT GetUsageStatsReportUrl(CString* url) const;
+
+  // Returns the time interval between update checks in seconds.
+  // 0 indicates updates are disabled.
+  int GetLastCheckPeriodSec(bool* is_overridden) const;
+
+  // Returns the number of seconds since the last successful update check.
+  int GetTimeSinceLastCheckedSec(bool is_machine) const;
+
+  // Gets and sets the last time a successful server update check was made.
+  DWORD GetLastCheckedTime(bool is_machine) const;
+  HRESULT SetLastCheckedTime(bool is_machine, DWORD time) const;
+
+  // Checks registry to see if user has enabled us to collect anonymous
+  // usage stats.
+  bool CanCollectStats(bool is_machine) const;
+
+  // Returns true if over-installing with the same version is allowed.
+  bool CanOverInstall() const;
+
+  // Returns the Autoupdate timer interval. This is the frequency of the
+  // auto update timer run by the core.
+  int GetAutoUpdateTimerIntervalMs() const;
+
+  // Returns the wait time in ms to start the first worker.
+  int GetUpdateWorkerStartUpDelayMs() const;
+
+  // Returns the Code Red timer interval. This is the frequency of the
+  // code red timer run by the core.
+  int GetCodeRedTimerIntervalMs() const;
+
+  // Returns true if event logging to the Windows Event Log is enabled.
+  bool CanLogEvents(WORD event_type) const;
+
+  // Retrieves TestSource which is to be set on dev, qa, and prober machines.
+  CString GetTestSource() const;
+
+  // Returns true if it is okay to do update checks and send pings.
+  bool CanUseNetwork(bool is_machine) const;
+
+  // Returns true if running in Windows Audit mode (OEM install).
+  // USE OemInstall::IsOemInstalling() INSTEAD in most cases.
+  bool IsWindowsInstalling() const;
+
+  // Returns true if the user is considered an internal user.
+  bool IsInternalUser() const;
+
+  // Returns true if installation of the specified app is allowed.
+  bool CanInstallApp(const GUID& app_guid) const;
+
+  // Returns true if updates are allowed for the specified app.
+  bool CanUpdateApp(const GUID& app_guid, bool is_manual) const;
+
+  // Returns true if crash uploading is allowed all the time, no matter the
+  // build flavor or other configuration parameters.
+  bool AlwaysAllowCrashUploads() const;
+
+  // Returns the network configuration override as a string.
+  static HRESULT GetNetConfig(CString* configuration_override);
+
+  // Gets the time when Goopdate was last updated or installed.
+  static DWORD GetInstallTime(bool is_machine);
+
+  // Returns true if it has been more than 24 hours since Goopdate was updated
+  // or installed.
+  static bool Is24HoursSinceInstall(bool is_machine);
+
+  static ConfigManager* Instance();
+  static void DeleteInstance();
+
+ private:
+  static LLock lock_;
+  static ConfigManager* config_manager_;
+
+  ConfigManager();
+
+  bool is_running_from_official_user_dir_;
+  bool is_running_from_official_machine_dir_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ConfigManager);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_CONFIG_MANAGER_H_
diff --git a/common/config_manager_unittest.cc b/common/config_manager_unittest.cc
new file mode 100644
index 0000000..73bcc7b
--- /dev/null
+++ b/common/config_manager_unittest.cc
@@ -0,0 +1,1581 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <limits.h>
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/file.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/string.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/time.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+// OMAHA_KEY_REL == "Software\Google\Update"
+#define OMAHA_KEY_REL \
+    _T("Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+
+
+#define APP_GUID1 _T("{6762F466-8863-424f-817C-5757931F346E}")
+const TCHAR* const kAppGuid1 = APP_GUID1;
+const TCHAR* const kAppMachineClientStatePath1 =
+    _T("HKLM\\") OMAHA_KEY_REL _T("\\ClientState\\") APP_GUID1;
+const TCHAR* const kAppUserClientStatePath1 =
+    _T("HKCU\\") OMAHA_KEY_REL _T("\\ClientState\\") APP_GUID1;
+const TCHAR* const kAppMachineClientStateMediumPath1 =
+    _T("HKLM\\") OMAHA_KEY_REL _T("\\ClientStateMedium\\") APP_GUID1;
+
+#define APP_GUID2 _T("{8A0FDD16-D4B7-4167-893F-1386F2A2F0FB}")
+const TCHAR* const kAppGuid2 = APP_GUID2;
+const TCHAR* const kAppMachineClientStatePath2 =
+    _T("HKLM\\") OMAHA_KEY_REL _T("\\ClientState\\") APP_GUID2;
+const TCHAR* const kAppUserClientStatePath2 =
+    _T("HKCU\\") OMAHA_KEY_REL _T("\\ClientState\\") APP_GUID2;
+
+const TCHAR* const kPolicyKey =
+    _T("HKLM\\Software\\Policies\\")
+    SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\");
+const TCHAR* const kInstallPolicyApp1 = _T("Install") APP_GUID1;
+const TCHAR* const kInstallPolicyApp2 = _T("Install") APP_GUID2;
+const TCHAR* const kUpdatePolicyApp1 = _T("Update") APP_GUID1;
+const TCHAR* const kUpdatePolicyApp2 = _T("Update") APP_GUID2;
+
+// Helper to write policies to the registry. Eliminates ambiguity of which
+// overload of SetValue to use without the need for static_cast.
+HRESULT SetPolicy(const TCHAR* policy_name, DWORD value) {
+  return RegKey::SetValue(kPolicyKey, policy_name, value);
+}
+
+// DeleteDirectory can fail with ERROR_PATH_NOT_FOUND if the parent directory
+// does not exist. Consider this a success for testing purposes.
+HRESULT DeleteTestDirectory(const TCHAR* dir) {
+  HRESULT hr = DeleteDirectory(dir);
+  if (hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) {
+    return S_OK;
+  }
+  return hr;
+}
+
+}  // namespace
+
+class ConfigManagerNoOverrideTest : public testing::Test {
+ protected:
+  ConfigManagerNoOverrideTest()
+      : cm_(ConfigManager::Instance()) {
+  }
+
+  bool CanInstallApp(const TCHAR* guid) {
+    return cm_->CanInstallApp(StringToGuid(guid));
+  }
+
+  bool CanUpdateApp(const TCHAR* guid, bool is_manual) {
+    return cm_->CanUpdateApp(StringToGuid(guid), is_manual);
+  }
+
+  ConfigManager* cm_;
+};
+
+class ConfigManagerTest : public ConfigManagerNoOverrideTest {
+ protected:
+  ConfigManagerTest()
+      : hive_override_key_name_(kRegistryHiveOverrideRoot) {
+  }
+
+  virtual void SetUp() {
+    RegKey::DeleteKey(hive_override_key_name_, true);
+    OverrideRegistryHives(hive_override_key_name_);
+  }
+
+  virtual void TearDown() {
+    RestoreRegistryHives();
+    EXPECT_SUCCEEDED(RegKey::DeleteKey(hive_override_key_name_, true));
+  }
+
+  void CanCollectStatsHelper(bool is_machine);
+  void CanCollectStatsIgnoresOppositeHiveHelper(bool is_machine);
+  HRESULT SetFirstInstallTime(bool is_machine, DWORD time);
+  HRESULT DeleteFirstInstallTime(bool is_machine);
+  HRESULT SetUpdateTime(bool is_machine, DWORD time);
+  HRESULT DeleteUpdateTime(bool is_machine);
+
+  CString hive_override_key_name_;
+};
+
+void ConfigManagerTest::CanCollectStatsHelper(bool is_machine) {
+  const TCHAR* app1_state_key_name = is_machine ? kAppMachineClientStatePath1 :
+                                                  kAppUserClientStatePath1;
+
+  EXPECT_FALSE(cm_->CanCollectStats(is_machine));
+
+  // Test the 'UsageStats' override.
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueForceUsageStats,
+                                    _T("")));
+  EXPECT_TRUE(cm_->CanCollectStats(is_machine));
+  EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV,
+                                       kRegValueForceUsageStats));
+
+  DWORD val = 1;
+  EXPECT_SUCCEEDED(RegKey::SetValue(app1_state_key_name,
+                                    _T("usagestats"),
+                                    val));
+  EXPECT_TRUE(cm_->CanCollectStats(is_machine));
+
+  val = 2;  // invalid value
+  EXPECT_SUCCEEDED(RegKey::SetValue(app1_state_key_name,
+                                    _T("usagestats"),
+                                    val));
+  EXPECT_FALSE(cm_->CanCollectStats(is_machine));
+
+  val = 0;
+  EXPECT_SUCCEEDED(RegKey::SetValue(app1_state_key_name,
+                                    _T("usagestats"),
+                                    val));
+  EXPECT_FALSE(cm_->CanCollectStats(is_machine));
+
+  // One 0 and one 1 results in true. The alphabetical order of the GUIDs is
+  // important assuming GetSubkeyNameAt returns subkeys in alphabetical order.
+  const TCHAR* app2_state_key_name = is_machine ? kAppMachineClientStatePath2 :
+                                                  kAppUserClientStatePath2;
+  val = 1;
+  EXPECT_SUCCEEDED(RegKey::SetValue(app2_state_key_name,
+                                    _T("usagestats"),
+                                    val));
+  EXPECT_TRUE(cm_->CanCollectStats(is_machine));
+}
+
+void ConfigManagerTest::CanCollectStatsIgnoresOppositeHiveHelper(
+    bool is_machine) {
+  const TCHAR* app1_state_key_name = is_machine ? kAppMachineClientStatePath1 :
+                                                  kAppUserClientStatePath1;
+
+  EXPECT_FALSE(cm_->CanCollectStats(is_machine));
+
+  DWORD val = 1;
+  EXPECT_SUCCEEDED(RegKey::SetValue(app1_state_key_name,
+                                    _T("usagestats"),
+                                    val));
+  EXPECT_TRUE(cm_->CanCollectStats(is_machine));
+  EXPECT_FALSE(cm_->CanCollectStats(!is_machine));
+}
+
+HRESULT ConfigManagerTest::SetFirstInstallTime(bool is_machine, DWORD time) {
+  return RegKey::SetValue(cm_->registry_client_state_goopdate(is_machine),
+                          kRegValueInstallTimeSec,
+                          time);
+}
+
+HRESULT ConfigManagerTest::DeleteFirstInstallTime(bool is_machine) {
+  if (!RegKey::HasValue(cm_->registry_client_state_goopdate(is_machine),
+                        kRegValueInstallTimeSec)) {
+    return S_OK;
+  }
+
+  return RegKey::DeleteValue(cm_->registry_client_state_goopdate(is_machine),
+                             kRegValueInstallTimeSec);
+}
+
+HRESULT ConfigManagerTest::SetUpdateTime(bool is_machine, DWORD time) {
+  return RegKey::SetValue(cm_->registry_client_state_goopdate(is_machine),
+                          kRegValueLastUpdateTimeSec,
+                          time);
+}
+
+HRESULT ConfigManagerTest::DeleteUpdateTime(bool is_machine) {
+  if (!RegKey::HasValue(cm_->registry_client_state_goopdate(is_machine),
+                        kRegValueLastUpdateTimeSec)) {
+    return S_OK;
+  }
+
+  return RegKey::DeleteValue(cm_->registry_client_state_goopdate(is_machine),
+                             kRegValueLastUpdateTimeSec);
+}
+
+TEST_F(ConfigManagerNoOverrideTest, RegistryKeys) {
+  EXPECT_STREQ(_T("HKCU\\") OMAHA_KEY_REL _T("\\Clients\\"),
+               cm_->user_registry_clients());
+  EXPECT_STREQ(_T("HKLM\\") OMAHA_KEY_REL _T("\\Clients\\"),
+               cm_->machine_registry_clients());
+  EXPECT_STREQ(_T("HKCU\\") OMAHA_KEY_REL _T("\\Clients\\"),
+               cm_->registry_clients(false));
+  EXPECT_STREQ(_T("HKLM\\") OMAHA_KEY_REL _T("\\Clients\\"),
+               cm_->registry_clients(true));
+
+  EXPECT_STREQ(_T("HKCU\\") OMAHA_KEY_REL _T("\\Clients\\") GOOPDATE_APP_ID,
+               cm_->user_registry_clients_goopdate());
+  EXPECT_STREQ(_T("HKLM\\") OMAHA_KEY_REL _T("\\Clients\\") GOOPDATE_APP_ID,
+               cm_->machine_registry_clients_goopdate());
+  EXPECT_STREQ(_T("HKCU\\") OMAHA_KEY_REL _T("\\Clients\\") GOOPDATE_APP_ID,
+               cm_->registry_clients_goopdate(false));
+  EXPECT_STREQ(_T("HKLM\\") OMAHA_KEY_REL _T("\\Clients\\") GOOPDATE_APP_ID,
+               cm_->registry_clients_goopdate(true));
+
+  EXPECT_STREQ(_T("HKCU\\") OMAHA_KEY_REL _T("\\ClientState\\"),
+               cm_->user_registry_client_state());
+  EXPECT_STREQ(_T("HKLM\\") OMAHA_KEY_REL _T("\\ClientState\\"),
+               cm_->machine_registry_client_state());
+  EXPECT_STREQ(_T("HKCU\\") OMAHA_KEY_REL _T("\\ClientState\\"),
+               cm_->registry_client_state(false));
+  EXPECT_STREQ(_T("HKLM\\") OMAHA_KEY_REL _T("\\ClientState\\"),
+               cm_->registry_client_state(true));
+
+  EXPECT_STREQ(_T("HKCU\\") OMAHA_KEY_REL _T("\\ClientState\\") GOOPDATE_APP_ID,
+               cm_->user_registry_client_state_goopdate());
+  EXPECT_STREQ(_T("HKLM\\") OMAHA_KEY_REL _T("\\ClientState\\") GOOPDATE_APP_ID,
+               cm_->machine_registry_client_state_goopdate());
+  EXPECT_STREQ(_T("HKCU\\") OMAHA_KEY_REL _T("\\ClientState\\") GOOPDATE_APP_ID,
+               cm_->registry_client_state_goopdate(false));
+  EXPECT_STREQ(_T("HKLM\\") OMAHA_KEY_REL _T("\\ClientState\\") GOOPDATE_APP_ID,
+               cm_->registry_client_state_goopdate(true));
+
+  EXPECT_STREQ(_T("HKLM\\") OMAHA_KEY_REL _T("\\ClientStateMedium\\"),
+               cm_->machine_registry_client_state_medium());
+
+  EXPECT_STREQ(_T("HKCU\\") OMAHA_KEY_REL _T("\\"),
+               cm_->user_registry_update());
+  EXPECT_STREQ(_T("HKLM\\") OMAHA_KEY_REL _T("\\"),
+               cm_->machine_registry_update());
+  EXPECT_STREQ(_T("HKCU\\") OMAHA_KEY_REL _T("\\"),
+               cm_->registry_update(false));
+  EXPECT_STREQ(_T("HKLM\\") OMAHA_KEY_REL _T("\\"),
+               cm_->registry_update(true));
+
+  EXPECT_STREQ(_T("HKCU\\Software\\") COMPANY_NAME_IDENTIFIER _T("\\"),
+               cm_->user_registry_google());
+  EXPECT_STREQ(_T("HKLM\\Software\\") COMPANY_NAME_IDENTIFIER _T("\\"),
+               cm_->machine_registry_google());
+  EXPECT_STREQ(_T("HKCU\\Software\\") COMPANY_NAME_IDENTIFIER _T("\\"),
+               cm_->registry_google(false));
+  EXPECT_STREQ(_T("HKLM\\Software\\") COMPANY_NAME_IDENTIFIER _T("\\"),
+               cm_->registry_google(true));
+}
+
+TEST_F(ConfigManagerNoOverrideTest, GetUserCrashReportsDir) {
+  const CString expected_path = GetGoogleUserPath() + _T("CrashReports");
+  EXPECT_SUCCEEDED(DeleteTestDirectory(expected_path));
+  EXPECT_STREQ(expected_path, cm_->GetUserCrashReportsDir());
+  EXPECT_TRUE(File::Exists(expected_path));
+}
+
+// Should run before the subdirectory tests to ensure the directory is created.
+TEST_F(ConfigManagerNoOverrideTest, GetUserGoopdateInstallDir) {
+  const CString expected_path = GetGoogleUserPath() + _T("Update");
+  EXPECT_STREQ(expected_path, cm_->GetUserGoopdateInstallDir());
+  EXPECT_TRUE(File::Exists(expected_path));
+}
+
+TEST_F(ConfigManagerNoOverrideTest, GetUserDownloadStorageDir) {
+  const CString expected_path = GetGoogleUpdateUserPath() + _T("Download");
+  EXPECT_SUCCEEDED(DeleteTestDirectory(expected_path));
+  EXPECT_STREQ(expected_path, cm_->GetUserDownloadStorageDir());
+  EXPECT_TRUE(File::Exists(expected_path));
+}
+
+TEST_F(ConfigManagerNoOverrideTest, GetUserInstallWorkingDir) {
+  const CString expected_path = GetGoogleUpdateUserPath() + _T("Install");
+  EXPECT_SUCCEEDED(DeleteTestDirectory(expected_path));
+  EXPECT_STREQ(expected_path, cm_->GetUserInstallWorkingDir());
+  EXPECT_TRUE(File::Exists(expected_path));
+}
+
+TEST_F(ConfigManagerNoOverrideTest, GetUserOfflineStorageDir) {
+  const CString expected_path = GetGoogleUpdateUserPath() + _T("Offline");
+  EXPECT_SUCCEEDED(DeleteTestDirectory(expected_path));
+  EXPECT_STREQ(expected_path, cm_->GetUserOfflineStorageDir());
+  EXPECT_TRUE(File::Exists(expected_path));
+}
+
+TEST_F(ConfigManagerNoOverrideTest, IsRunningFromUserGoopdateInstallDir) {
+  EXPECT_FALSE(cm_->IsRunningFromUserGoopdateInstallDir());
+}
+
+TEST_F(ConfigManagerNoOverrideTest, GetTempDownloadDir) {
+  TCHAR expected_path[MAX_PATH] = {0};
+  ASSERT_NE(0, ::GetTempPath(MAX_PATH, expected_path));
+
+  EXPECT_STREQ(expected_path, cm_->GetTempDownloadDir());
+  EXPECT_TRUE(File::Exists(expected_path));
+}
+
+TEST_F(ConfigManagerNoOverrideTest, GetMachineCrashReportsDir) {
+  CString program_files;
+  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &program_files));
+  const CString expected_path =
+      program_files + _T("\\") + SHORT_COMPANY_NAME + _T("\\CrashReports");
+  EXPECT_SUCCEEDED(DeleteTestDirectory(expected_path));
+  EXPECT_STREQ(expected_path, cm_->GetMachineCrashReportsDir());
+  EXPECT_TRUE(File::Exists(expected_path) || !vista_util::IsUserAdmin());
+}
+
+// Should run before the subdirectory tests to ensure the directory is created.
+TEST_F(ConfigManagerNoOverrideTest, GetMachineGoopdateInstallDir) {
+  CString expected_path = GetGoogleUpdateMachinePath();
+  EXPECT_STREQ(expected_path, cm_->GetMachineGoopdateInstallDir());
+  EXPECT_TRUE(File::Exists(expected_path) || !vista_util::IsUserAdmin());
+}
+
+TEST_F(ConfigManagerNoOverrideTest, GetMachineSecureDownloadStorageDir) {
+  CString expected_path = GetGoogleUpdateMachinePath() + _T("\\Download");
+  EXPECT_SUCCEEDED(DeleteTestDirectory(expected_path));
+  EXPECT_STREQ(expected_path, cm_->GetMachineSecureDownloadStorageDir());
+  EXPECT_TRUE(File::Exists(expected_path) || !vista_util::IsUserAdmin());
+}
+
+TEST_F(ConfigManagerNoOverrideTest, GetMachineInstallWorkingDir) {
+  CString expected_path = GetGoogleUpdateMachinePath() + _T("\\Install");
+  EXPECT_SUCCEEDED(DeleteTestDirectory(expected_path));
+  EXPECT_STREQ(expected_path, cm_->GetMachineInstallWorkingDir());
+  EXPECT_TRUE(File::Exists(expected_path) || !vista_util::IsUserAdmin());
+}
+
+TEST_F(ConfigManagerNoOverrideTest, GetMachineSecureOfflineStorageDir) {
+  CString expected_path = GetGoogleUpdateMachinePath() + _T("\\Offline");
+  EXPECT_SUCCEEDED(DeleteTestDirectory(expected_path));
+  EXPECT_STREQ(expected_path, cm_->GetMachineSecureOfflineStorageDir());
+  EXPECT_TRUE(File::Exists(expected_path) || !vista_util::IsUserAdmin());
+}
+
+TEST_F(ConfigManagerNoOverrideTest, IsRunningFromMachineGoopdateInstallDir) {
+  EXPECT_FALSE(cm_->IsRunningFromMachineGoopdateInstallDir());
+}
+
+// Tests the GetUpdateCheckUrl override.
+TEST_F(ConfigManagerTest, GetUpdateCheckUrl) {
+  CString url;
+  EXPECT_SUCCEEDED(cm_->GetUpdateCheckUrl(&url));
+  EXPECT_STREQ(url, kUrlUpdateCheck);
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueNameUrl,
+                                    _T("http://updatecheck/")));
+  url.Empty();
+  EXPECT_SUCCEEDED(cm_->GetUpdateCheckUrl(&url));
+  EXPECT_STREQ(url, _T("http://updatecheck/"));
+}
+
+// Tests the GetPingUrl override.
+TEST_F(ConfigManagerTest, GetPingUrl) {
+  CString url;
+  EXPECT_SUCCEEDED(cm_->GetPingUrl(&url));
+  EXPECT_STREQ(url, kUrlPing);
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueNamePingUrl,
+                                    _T("http://ping/")));
+  url.Empty();
+  EXPECT_SUCCEEDED(cm_->GetPingUrl(&url));
+  EXPECT_STREQ(url, _T("http://ping/"));
+}
+
+// Tests the GetCrashReportUrl override.
+TEST_F(ConfigManagerTest, GetCrashReportUrl) {
+  CString url;
+  EXPECT_SUCCEEDED(cm_->GetCrashReportUrl(&url));
+  EXPECT_STREQ(url, kUrlCrashReport);
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueNameCrashReportUrl,
+                                    _T("http://crashreport/")));
+  url.Empty();
+  EXPECT_SUCCEEDED(cm_->GetCrashReportUrl(&url));
+  EXPECT_STREQ(url, _T("http://crashreport/"));
+}
+
+// Tests the GetMoreInfoUrl override.
+TEST_F(ConfigManagerTest, GetMoreInfoUrl) {
+  CString url;
+  EXPECT_SUCCEEDED(cm_->GetMoreInfoUrl(&url));
+  EXPECT_STREQ(url, kUrlMoreInfo);
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueNameGetMoreInfoUrl,
+                                    _T("http://moreinfo/")));
+  url.Empty();
+  EXPECT_SUCCEEDED(cm_->GetMoreInfoUrl(&url));
+  EXPECT_STREQ(url, _T("http://moreinfo/"));
+}
+
+// Tests the GetUsageStatsReportUrl override.
+TEST_F(ConfigManagerTest, GetUsageStatsReportUrl) {
+  CString url;
+  EXPECT_SUCCEEDED(cm_->GetUsageStatsReportUrl(&url));
+  EXPECT_STREQ(url, kUrlUsageStatsReport);
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueNameUsageStatsReportUrl,
+                                    _T("http://usagestatsreport/")));
+  url.Empty();
+  EXPECT_SUCCEEDED(cm_->GetUsageStatsReportUrl(&url));
+  EXPECT_STREQ(url, _T("http://usagestatsreport/"));
+}
+
+// Tests LastCheckPeriodSec override.
+TEST_F(ConfigManagerTest, GetLastCheckPeriodSec_Default) {
+  bool is_overridden = true;
+  if (cm_->IsInternalUser()) {
+    EXPECT_EQ(kLastCheckPeriodInternalUserSec,
+              cm_->GetLastCheckPeriodSec(&is_overridden));
+  } else {
+    EXPECT_EQ(kLastCheckPeriodSec, cm_->GetLastCheckPeriodSec(&is_overridden));
+  }
+  EXPECT_FALSE(is_overridden);
+}
+
+TEST_F(ConfigManagerTest, GetLastCheckPeriodSec_UpdateDevOverride) {
+  // Zero is a special value meaning disabled.
+  DWORD val = 0;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueLastCheckPeriodSec,
+                                    val));
+  bool is_overridden = false;
+  EXPECT_EQ(0, cm_->GetLastCheckPeriodSec(&is_overridden));
+  EXPECT_TRUE(is_overridden);
+
+  val = kMinLastCheckPeriodSec - 1;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueLastCheckPeriodSec,
+                                    val));
+  is_overridden = false;
+  EXPECT_EQ(kMinLastCheckPeriodSec, cm_->GetLastCheckPeriodSec(&is_overridden));
+  EXPECT_TRUE(is_overridden);
+
+  val = INT_MAX + static_cast<uint32>(1);
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueLastCheckPeriodSec,
+                                    val));
+  is_overridden = false;
+  EXPECT_EQ(INT_MAX, cm_->GetLastCheckPeriodSec(&is_overridden));
+  EXPECT_TRUE(is_overridden);
+
+  val = 1000;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueLastCheckPeriodSec,
+                                    val));
+  is_overridden = false;
+  EXPECT_EQ(1000, cm_->GetLastCheckPeriodSec(&is_overridden));
+  EXPECT_TRUE(is_overridden);
+
+  EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV,
+                                       kRegValueLastCheckPeriodSec));
+  is_overridden = true;
+  if (cm_->IsInternalUser()) {
+    EXPECT_EQ(kLastCheckPeriodInternalUserSec,
+              cm_->GetLastCheckPeriodSec(&is_overridden));
+  } else {
+    EXPECT_EQ(kLastCheckPeriodSec, cm_->GetLastCheckPeriodSec(&is_overridden));
+  }
+  EXPECT_FALSE(is_overridden);
+}
+
+TEST_F(ConfigManagerTest, GetLastCheckPeriodSec_GroupPolicyOverride) {
+  const DWORD kOverrideMinutes = 16000;
+  const DWORD kExpectedSeconds = kOverrideMinutes * 60;
+  EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"),
+                             kOverrideMinutes));
+  bool is_overridden = false;
+  EXPECT_EQ(kExpectedSeconds, cm_->GetLastCheckPeriodSec(&is_overridden));
+  EXPECT_TRUE(is_overridden);
+}
+
+TEST_F(ConfigManagerTest, GetLastCheckPeriodSec_GroupPolicyOverride_TooLow) {
+  const DWORD kOverrideMinutes = 1;
+  EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"),
+                             kOverrideMinutes));
+  bool is_overridden = false;
+  EXPECT_EQ(kMinLastCheckPeriodSec, cm_->GetLastCheckPeriodSec(&is_overridden));
+  EXPECT_TRUE(is_overridden);
+}
+
+TEST_F(ConfigManagerTest, GetLastCheckPeriodSec_GroupPolicyOverride_Zero) {
+  const DWORD kOverrideMinutes = 0;
+  EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"),
+                             kOverrideMinutes));
+  bool is_overridden = false;
+  EXPECT_EQ(0, cm_->GetLastCheckPeriodSec(&is_overridden));
+  EXPECT_TRUE(is_overridden);
+}
+
+TEST_F(ConfigManagerTest,
+       GetLastCheckPeriodSec_GroupPolicyOverride_Overflow_SecondsConversion) {
+  const DWORD kOverrideMinutes = UINT_MAX;
+  EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"),
+                             kOverrideMinutes));
+  bool is_overridden = false;
+  EXPECT_EQ(INT_MAX, cm_->GetLastCheckPeriodSec(&is_overridden));
+  EXPECT_TRUE(is_overridden);
+
+  const DWORD kOverrideMinutes2 = INT_MAX + static_cast<uint32>(1);
+  EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"),
+                             kOverrideMinutes2));
+  is_overridden = false;
+  EXPECT_EQ(INT_MAX, cm_->GetLastCheckPeriodSec(&is_overridden));
+  EXPECT_TRUE(is_overridden);
+
+  const DWORD kOverrideMinutes3 = 0xf0000000;
+  EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"),
+                             kOverrideMinutes3));
+  is_overridden = false;
+  EXPECT_EQ(INT_MAX, cm_->GetLastCheckPeriodSec(&is_overridden));
+  EXPECT_TRUE(is_overridden);
+}
+
+// Overflow the integer but not the minutes to seconds conversion.
+TEST_F(ConfigManagerTest,
+       GetLastCheckPeriodSec_GroupPolicyOverride_Overflow_Int) {
+  const DWORD kOverrideMinutes = UINT_MAX / 60;
+  EXPECT_GT(UINT_MAX, kOverrideMinutes);
+
+  EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"),
+                             kOverrideMinutes));
+  bool is_overridden = false;
+  EXPECT_EQ(INT_MAX, cm_->GetLastCheckPeriodSec(&is_overridden));
+  EXPECT_TRUE(is_overridden);
+}
+
+// UpdateDev takes precedence over the Group Policy override.
+TEST_F(ConfigManagerTest,
+       GetLastCheckPeriodSec_GroupPolicyAndUpdateDevOverrides) {
+  const DWORD kGroupPolicyOverrideMinutes = 100;
+  EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"),
+                             kGroupPolicyOverrideMinutes));
+  const DWORD kUpdateDevOverrideSeconds = 70;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueLastCheckPeriodSec,
+                                    kUpdateDevOverrideSeconds));
+
+  bool is_overridden = false;
+  EXPECT_EQ(kUpdateDevOverrideSeconds,
+            cm_->GetLastCheckPeriodSec(&is_overridden));
+  EXPECT_TRUE(is_overridden);
+}
+
+TEST_F(ConfigManagerTest, CanCollectStats_LegacyLocationNewName) {
+  DWORD val = 1;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("usagestats"),
+                                    val));
+  EXPECT_FALSE(cm_->CanCollectStats(true));
+}
+
+TEST_F(ConfigManagerTest, CanCollectStats_MachineOnly) {
+  CanCollectStatsHelper(true);
+}
+
+TEST_F(ConfigManagerTest, CanCollectStats_UserOnly) {
+  CanCollectStatsHelper(false);
+}
+
+// This tests that the legacy conversion is honored.
+TEST_F(ConfigManagerTest, CanCollectStats_GoopdateGuidIsChecked) {
+  EXPECT_FALSE(cm_->CanCollectStats(true));
+
+  DWORD val = 1;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
+                                    _T("usagestats"),
+                                    val));
+  EXPECT_TRUE(cm_->CanCollectStats(true));
+}
+
+TEST_F(ConfigManagerTest, CanCollectStats_MachineIgnoresUser) {
+  CanCollectStatsIgnoresOppositeHiveHelper(true);
+}
+
+TEST_F(ConfigManagerTest, CanCollectStats_UserIgnoresMachine) {
+  CanCollectStatsIgnoresOppositeHiveHelper(false);
+}
+// Unfortunately, the app's ClientStateMedium key is not checked if there is no
+// corresponding ClientState key.
+TEST_F(ConfigManagerTest,
+       CanCollectStats_Machine_ClientStateMediumOnly_AppClientStateKeyMissing) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath1,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_FALSE(cm_->CanCollectStats(true));
+}
+
+TEST_F(ConfigManagerTest,
+       CanCollectStats_Machine_ClientStateMediumOnly_AppClientStateKeyExists) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath1));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath1,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(cm_->CanCollectStats(true));
+}
+
+TEST_F(ConfigManagerTest,
+       CanCollectStats_Machine_ClientStateMediumInvalid) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath1));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath1,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(2)));
+  EXPECT_FALSE(cm_->CanCollectStats(true));
+}
+
+TEST_F(ConfigManagerTest, CanCollectStats_User_ClientStateMediumOnly) {
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStatePath1));
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKCU\\") OMAHA_KEY_REL _T("\\ClientStateMedium\\") APP_GUID1,
+      _T("usagestats"),
+      static_cast<DWORD>(1)));
+  EXPECT_FALSE(cm_->CanCollectStats(false));
+}
+
+TEST_F(ConfigManagerTest,
+       CanCollectStats_Machine_ClientStateZeroClientStateMediumOne) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath1,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_FALSE(cm_->CanCollectStats(true));
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath1,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(cm_->CanCollectStats(true));
+}
+
+TEST_F(ConfigManagerTest,
+       CanCollectStats_Machine_ClientStateOneClientStateMediumZero) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath1,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(cm_->CanCollectStats(true));
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath1,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_FALSE(cm_->CanCollectStats(true));
+}
+
+// Tests OverInstall override.
+TEST_F(ConfigManagerTest, CanOverInstall) {
+  EXPECT_EQ(cm_->CanOverInstall(), !OFFICIAL_BUILD);
+
+  DWORD val = 1;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueNameOverInstall,
+                                    val));
+#ifdef DEBUG
+  EXPECT_TRUE(cm_->CanOverInstall());
+#else
+  EXPECT_EQ(!OFFICIAL_BUILD, cm_->CanOverInstall());
+#endif
+
+  val = 0;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueNameOverInstall,
+                                    val));
+#ifdef DEBUG
+  EXPECT_FALSE(cm_->CanOverInstall());
+#else
+  EXPECT_EQ(!OFFICIAL_BUILD, cm_->CanOverInstall());
+#endif
+}
+
+// Tests AuCheckPeriodMs override.
+TEST_F(ConfigManagerTest, GetAutoUpdateTimerIntervalMs) {
+  EXPECT_EQ(cm_->IsInternalUser() ? kAUCheckPeriodInternalUserMs :
+                                    kAUCheckPeriodMs,
+            cm_->GetAutoUpdateTimerIntervalMs());
+
+  DWORD val = 0;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueAuCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(kMinAUCheckPeriodMs, cm_->GetAutoUpdateTimerIntervalMs());
+
+  val = kMinAUCheckPeriodMs - 1;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueAuCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(kMinAUCheckPeriodMs, cm_->GetAutoUpdateTimerIntervalMs());
+
+  val = 30000;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueAuCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(val, cm_->GetAutoUpdateTimerIntervalMs());
+
+  val = INT_MAX;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueAuCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(val, cm_->GetAutoUpdateTimerIntervalMs());
+
+  // Tests overflow with large positive numbers.
+  val = INT_MAX;
+  ++val;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueAuCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(INT_MAX, cm_->GetAutoUpdateTimerIntervalMs());
+
+  val = UINT_MAX;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueAuCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(INT_MAX, cm_->GetAutoUpdateTimerIntervalMs());
+}
+
+// Tests CrCheckPeriodMs override.
+TEST_F(ConfigManagerTest, GetCodeRedTimerIntervalMs) {
+  EXPECT_EQ(kCodeRedCheckPeriodMs, cm_->GetCodeRedTimerIntervalMs());
+
+  DWORD val = 0;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueCrCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(kMinCodeRedCheckPeriodMs, cm_->GetCodeRedTimerIntervalMs());
+
+  val = kMinCodeRedCheckPeriodMs - 1;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueCrCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(kMinCodeRedCheckPeriodMs, cm_->GetCodeRedTimerIntervalMs());
+
+  val = 60000;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueCrCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(val, cm_->GetCodeRedTimerIntervalMs());
+
+  val = INT_MAX;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueCrCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(val, cm_->GetCodeRedTimerIntervalMs());
+
+  // Tests overflow with large positive numbers.
+  val = INT_MAX;
+  ++val;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueCrCheckPeriodMs,
+                                    val));
+  EXPECT_EQ(INT_MAX, cm_->GetCodeRedTimerIntervalMs());
+
+  val = UINT_MAX;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                      kRegValueCrCheckPeriodMs,
+                                      val));
+  EXPECT_EQ(INT_MAX, cm_->GetCodeRedTimerIntervalMs());
+}
+
+// Tests CanLogEvents override.
+TEST_F(ConfigManagerTest, CanLogEvents_WithOutOverride) {
+  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_SUCCESS));
+  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_ERROR_TYPE));
+  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_WARNING_TYPE));
+  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_INFORMATION_TYPE));
+  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_AUDIT_SUCCESS));
+  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_AUDIT_FAILURE));
+}
+
+TEST_F(ConfigManagerTest, CanLogEvents) {
+  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_INFORMATION_TYPE));
+
+  DWORD val = LOG_EVENT_LEVEL_ALL;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueEventLogLevel,
+                                    val));
+  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_SUCCESS));
+  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_ERROR_TYPE));
+  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_WARNING_TYPE));
+  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_INFORMATION_TYPE));
+  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_AUDIT_SUCCESS));
+  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_AUDIT_FAILURE));
+
+  val = LOG_EVENT_LEVEL_WARN_AND_ERROR;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueEventLogLevel,
+                                    val));
+  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_SUCCESS));
+  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_ERROR_TYPE));
+  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_WARNING_TYPE));
+  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_INFORMATION_TYPE));
+  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_AUDIT_SUCCESS));
+  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_AUDIT_FAILURE));
+}
+
+// Tests GetTestSource override.
+TEST_F(ConfigManagerTest, GetTestSource_Dev) {
+  CString expected_value;
+#if DEBUG || !OFFICIAL_BUILD
+  expected_value = kRegValueTestSourceAuto;
+#endif
+
+  CString test_source = cm_->GetTestSource();
+  EXPECT_STREQ(expected_value, test_source);
+  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueTestSource,
+                                   _T("dev")));
+  test_source = cm_->GetTestSource();
+  EXPECT_STREQ(_T("dev"), test_source);
+}
+
+TEST_F(ConfigManagerTest, GetTestSource_EmptyRegKey) {
+  CString expected_value;
+
+#if DEBUG || !OFFICIAL_BUILD
+  expected_value = kRegValueTestSourceAuto;
+#endif
+
+  CString test_source = cm_->GetTestSource();
+  EXPECT_STREQ(expected_value, test_source);
+  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueTestSource,
+                                   _T("")));
+  test_source = cm_->GetTestSource();
+  EXPECT_STREQ(kRegValueTestSourceAuto, test_source);
+}
+
+//
+// CanUseNetwork tests.
+//
+
+// Covers UpdateEulaAccepted case.
+TEST_F(ConfigManagerTest, CanUseNetwork_Machine_Normal) {
+  EXPECT_TRUE(cm_->CanUseNetwork(true));
+}
+
+// Covers UpdateEulaAccepted case.
+TEST_F(ConfigManagerTest, CanUseNetwork_User_Normal) {
+  EXPECT_TRUE(cm_->CanUseNetwork(false));
+}
+
+// These cover the not OEM install mode cases.
+TEST_F(ConfigManagerTest, CanUseNetwork_Machine_UpdateEulaNotAccepted) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_FALSE(cm_->CanUseNetwork(true));
+}
+
+TEST_F(ConfigManagerTest,
+       CanUseNetwork_Machine_UpdateEulaNotAccepted_AppEulaAccepted) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath1,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_FALSE(cm_->CanUseNetwork(true));
+}
+
+TEST_F(ConfigManagerTest, CanUseNetwork_Machine_AppEulaNotAccepted) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath1,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_TRUE(cm_->CanUseNetwork(true));
+}
+
+TEST_F(ConfigManagerTest, CanUseNetwork_Machine_AppEulaAccepted) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath1,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(cm_->CanUseNetwork(true));
+}
+
+TEST_F(ConfigManagerTest, CanUseNetwork_Machine_UserUpdateEulaNotAccepted) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_TRUE(cm_->CanUseNetwork(true));
+}
+
+TEST_F(ConfigManagerTest, CanUseNetwork_User_UpdateEulaNotAccepted) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_FALSE(cm_->CanUseNetwork(false));
+}
+
+TEST_F(ConfigManagerTest,
+       CanUseNetwork_User_UpdateEulaNotAccepted_AppEulaAccepted) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath1,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_FALSE(cm_->CanUseNetwork(false));
+}
+
+TEST_F(ConfigManagerTest, CanUseNetwork_User_AppEulaNotAccepted) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath1,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_TRUE(cm_->CanUseNetwork(false));
+}
+
+TEST_F(ConfigManagerTest, CanUseNetwork_User_AppEulaAccepted) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath1,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_TRUE(cm_->CanUseNetwork(false));
+}
+
+TEST_F(ConfigManagerTest, CanUseNetwork_User_MachineUpdateEulaNotAccepted) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_TRUE(cm_->CanUseNetwork(false));
+}
+
+// TODO(omaha): Figure out a way to test the result.
+TEST_F(ConfigManagerTest, IsInternalUser) {
+  cm_->IsInternalUser();
+}
+
+TEST_F(ConfigManagerTest, IsWindowsInstalling_Normal) {
+  EXPECT_FALSE(cm_->IsWindowsInstalling());
+}
+
+// While this test passes, the return value of IsWindowsInstalling() is not
+// fully tested because the account is not Administrator.
+TEST_F(ConfigManagerTest, IsWindowsInstalling_Installing_Vista_InvalidValues) {
+  if (!vista_util::IsVistaOrLater()) {
+    return;
+  }
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      _T("")));
+  EXPECT_FALSE(cm_->IsWindowsInstalling());
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      _T("foo")));
+  EXPECT_FALSE(cm_->IsWindowsInstalling());
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      static_cast<DWORD>(1)));
+  ExpectAsserts expect_asserts;  // RegKey asserts because value type is wrong.
+  EXPECT_FALSE(cm_->IsWindowsInstalling());
+}
+
+// TODO(omaha): This test fails because the account is not Administrator. Maybe
+// just delete them if this is the final implementation of Audit Mode detection.
+TEST_F(ConfigManagerTest, IsWindowsInstalling_Installing_Vista_ValidStates) {
+  if (!vista_util::IsVistaOrLater()) {
+    return;
+  }
+
+  // These states return false in the original implementation.
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      _T("IMAGE_STATE_COMPLETE")));
+  EXPECT_FALSE(cm_->IsWindowsInstalling());
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      _T("IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE")));
+  EXPECT_FALSE(cm_->IsWindowsInstalling());
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      _T("IMAGE_STATE_SPECIALIZE_RESEAL_TO_OOBE")));
+  EXPECT_FALSE(cm_->IsWindowsInstalling());
+
+  // These states are specified in the original implementation.
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      _T("IMAGE_STATE_UNDEPLOYABLE")));
+  EXPECT_TRUE(cm_->IsWindowsInstalling());
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      _T("IMAGE_STATE_GENERALIZE_RESEAL_TO_AUDIT")));
+  EXPECT_TRUE(cm_->IsWindowsInstalling());
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(
+      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+      _T("ImageState"),
+      _T("IMAGE_STATE_SPECIALIZE_RESEAL_TO_AUDIT")));
+  EXPECT_TRUE(cm_->IsWindowsInstalling());
+}
+
+TEST_F(ConfigManagerTest, CanInstallApp_NoGroupPolicy) {
+  EXPECT_TRUE(CanInstallApp(kAppGuid1));
+}
+
+TEST_F(ConfigManagerTest, CanInstallApp_DifferentAppDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp2, 0));
+  EXPECT_TRUE(CanInstallApp(kAppGuid1));
+}
+
+TEST_F(ConfigManagerTest, CanInstallApp_NoDefaultValue_AppDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 0));
+  EXPECT_FALSE(CanInstallApp(kAppGuid1));
+}
+
+TEST_F(ConfigManagerTest, CanInstallApp_NoDefaultValue_AppEnabled) {
+  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 1));
+  EXPECT_TRUE(CanInstallApp(kAppGuid1));
+}
+
+TEST_F(ConfigManagerTest, CanInstallApp_NoDefaultValue_AppInvalid) {
+  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 2));
+  EXPECT_TRUE(CanInstallApp(kAppGuid1));
+}
+
+TEST_F(ConfigManagerTest, CanInstallApp_DefaultDisabled_NoAppValue) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 0));
+  EXPECT_FALSE(CanInstallApp(kAppGuid1));
+}
+
+TEST_F(ConfigManagerTest, CanInstallApp_DefaultDisabled_AppDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 0));
+  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 0));
+  EXPECT_FALSE(CanInstallApp(kAppGuid1));
+}
+
+TEST_F(ConfigManagerTest, CanInstallApp_DefaultDisabled_AppEnabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 0));
+  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 1));
+  EXPECT_TRUE(CanInstallApp(kAppGuid1));
+}
+
+// Invalid value defaulting to true overrides the InstallDefault disable.
+TEST_F(ConfigManagerTest, CanInstallApp_DefaultDisabled_AppInvalid) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 0));
+  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 2));
+  EXPECT_TRUE(CanInstallApp(kAppGuid1));
+}
+
+TEST_F(ConfigManagerTest, CanInstallApp_DefaultEnabled_NoAppValue) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 1));
+  EXPECT_TRUE(CanInstallApp(kAppGuid1));
+}
+
+TEST_F(ConfigManagerTest, CanInstallApp_DefaultEnabled_AppDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 1));
+  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 0));
+  EXPECT_FALSE(CanInstallApp(kAppGuid1));
+}
+
+TEST_F(ConfigManagerTest, CanInstallApp_DefaultEnabled_AppEnabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 1));
+  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 1));
+  EXPECT_TRUE(CanInstallApp(kAppGuid1));
+}
+
+TEST_F(ConfigManagerTest, CanInstallApp_DefaultEnabled_AppInvalid) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 1));
+  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 2));
+  EXPECT_TRUE(CanInstallApp(kAppGuid1));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_NoGroupPolicy) {
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DifferentAppDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp2, 0));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DifferentAppManualOnly) {
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp2, 2));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_NoDefaultValue_AppDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 0));
+  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_NoDefaultValue_AppEnabled) {
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 1));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_NoDefaultValue_AppManualOnly) {
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 2));
+  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultDisabled_NoAppValue) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
+  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultDisabled_AppDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 0));
+  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultDisabled_AppEnabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 1));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultDisabled_AppManualOnly) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 2));
+  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
+}
+
+// Invalid value defaulting to true overrides the UpdateDefault disable.
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultDisabled_AppInvalid) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 3));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultEnabled_NoAppValue) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultEnabled_AppDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 0));
+  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultEnabled_AppEnabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 1));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultEnabled_AppManualOnly) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 2));
+  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultEnabled_AppInvalid) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 3));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultManualOnly_NoAppValue) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
+  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultManualOnly_AppDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 0));
+  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultManualOnly_AppEnabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 1));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultManualOnly_AppManualOnly) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 2));
+  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultManualOnly_AppInvalid) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 3));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultInvalid_NoAppValue) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 3));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_Omaha_DefaultDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
+  EXPECT_TRUE(CanUpdateApp(kGoogleUpdateAppId, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_Omaha_DefaultManualOnly) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
+  EXPECT_TRUE(CanUpdateApp(kGoogleUpdateAppId, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Auto_Omaha_AppDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("Update") GOOPDATE_APP_ID, 0));
+  EXPECT_TRUE(CanUpdateApp(kGoogleUpdateAppId, false));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_NoGroupPolicy) {
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DifferentAppDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp2, 0));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DifferentAppManualOnly) {
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp2, 2));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_NoDefaultValue_AppDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 0));
+  EXPECT_FALSE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_NoDefaultValue_AppEnabled) {
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 1));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_NoDefaultValue_AppManualOnly) {
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 2));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultDisabled_NoAppValue) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
+  EXPECT_FALSE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultDisabled_AppDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 0));
+  EXPECT_FALSE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultDisabled_AppEnabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 1));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultDisabled_AppManualOnly) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 2));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
+}
+
+// Invalid value defaulting to true overrides the UpdateDefault disable.
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultDisabled_AppInvalid) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 3));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultEnabled_NoAppValue) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultEnabled_AppDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 0));
+  EXPECT_FALSE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultEnabled_AppEnabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 1));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultEnabled_AppManualOnly) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 2));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultEnabled_AppInvalid) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 3));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultManualOnly_NoAppValue) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultManualOnly_AppDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 0));
+  EXPECT_FALSE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultManualOnly_AppEnabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 1));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultManualOnly_AppManualOnly) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 2));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Maual_DefaultManualOnly_AppInvalid) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
+  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 3));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultInvalid_NoAppValue) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 3));
+  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_Omaha_DefaultDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
+  EXPECT_TRUE(CanUpdateApp(kGoogleUpdateAppId, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_Omaha_DefaultManualOnly) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
+  EXPECT_TRUE(CanUpdateApp(kGoogleUpdateAppId, true));
+}
+
+TEST_F(ConfigManagerTest, CanUpdateApp_Manual_Omaha_AppDisabled) {
+  EXPECT_SUCCEEDED(SetPolicy(_T("Update") GOOPDATE_APP_ID, 0));
+  EXPECT_TRUE(CanUpdateApp(kGoogleUpdateAppId, true));
+}
+
+TEST_F(ConfigManagerTest, GetPackageCacheSizeLimitMBytes_Default) {
+  EXPECT_EQ(500, cm_->GetPackageCacheSizeLimitMBytes());
+}
+
+TEST_F(ConfigManagerTest, GetPackageCacheSizeLimitMBytes_Override_TooBig) {
+  EXPECT_SUCCEEDED(SetPolicy(kRegValueCacheSizeLimitMBytes, 8192));
+  EXPECT_EQ(500, cm_->GetPackageCacheSizeLimitMBytes());
+}
+
+TEST_F(ConfigManagerTest, GetPackageCacheSizeLimitMBytes_Override_TooSmall) {
+  EXPECT_SUCCEEDED(SetPolicy(kRegValueCacheSizeLimitMBytes, 0));
+  EXPECT_EQ(500, cm_->GetPackageCacheSizeLimitMBytes());
+}
+
+TEST_F(ConfigManagerTest, GetPackageCacheSizeLimitMBytes_Override_Valid) {
+  EXPECT_SUCCEEDED(SetPolicy(kRegValueCacheSizeLimitMBytes, 250));
+  EXPECT_EQ(250, cm_->GetPackageCacheSizeLimitMBytes());
+}
+
+TEST_F(ConfigManagerTest, GetPackageCacheExpirationTimeDays_Default) {
+  EXPECT_EQ(180, cm_->GetPackageCacheExpirationTimeDays());
+}
+
+TEST_F(ConfigManagerTest, GetPackageCacheExpirationTimeDays_Override_TooBig) {
+  EXPECT_SUCCEEDED(SetPolicy(kRegValueCacheLifeLimitDays, 3600));
+  EXPECT_EQ(180, cm_->GetPackageCacheExpirationTimeDays());
+}
+
+TEST_F(ConfigManagerTest, GetPackageCacheExpirationTimeDays_Override_TooSmall) {
+  EXPECT_SUCCEEDED(SetPolicy(kRegValueCacheLifeLimitDays, 0));
+  EXPECT_EQ(180, cm_->GetPackageCacheExpirationTimeDays());
+}
+
+TEST_F(ConfigManagerTest, GetPackageCacheExpirationTimeDays_Override_Valid) {
+  EXPECT_SUCCEEDED(SetPolicy(kRegValueCacheLifeLimitDays, 60));
+  EXPECT_EQ(60, cm_->GetPackageCacheExpirationTimeDays());
+}
+
+TEST_F(ConfigManagerTest, LastCheckedTime) {
+  DWORD time = 500;
+  EXPECT_SUCCEEDED(cm_->SetLastCheckedTime(true, time));
+  EXPECT_EQ(time, cm_->GetLastCheckedTime(true));
+
+  time = 77003;
+  EXPECT_SUCCEEDED(cm_->SetLastCheckedTime(false, time));
+  EXPECT_EQ(time, cm_->GetLastCheckedTime(false));
+}
+
+// Tests GetDir indirectly.
+TEST_F(ConfigManagerTest, GetDir) {
+  RestoreRegistryHives();
+
+  CString user_install_dir = cm_->GetUserGoopdateInstallDir();
+  CString user_profile;
+  ASSERT_NE(0, ::GetEnvironmentVariable(_T("USERPROFILE"),
+                                        CStrBuf(user_profile, MAX_PATH),
+                                        MAX_PATH));
+  ASSERT_TRUE(String_StartsWith(user_install_dir, user_profile, true));
+}
+
+TEST_F(ConfigManagerTest, GetUpdateWorkerStartUpDelayMs_Repeated) {
+  if (!SystemInfo::IsRunningOnXPOrLater()) {
+    std::wcout << _T("\tTest did not run because GenRandom breaks on Windows ")
+               << _T("2000 if the registry keys are overridden.") << std::endl;
+    return;
+  }
+
+  // Test the UpdateDelay multiple times.
+  for (int i = 0; i < 10; ++i) {
+    int random = cm_->GetUpdateWorkerStartUpDelayMs();
+    EXPECT_GE(random, kUpdateTimerStartupDelayMinMs);
+    EXPECT_LE(random, kUpdateTimerStartupDelayMaxMs);
+  }
+}
+
+TEST_F(ConfigManagerTest, GetUpdateWorkerStartUpDelayMs) {
+  if (!SystemInfo::IsRunningOnXPOrLater()) {
+    std::wcout << _T("\tTest did not run because GenRandom breaks on Windows ")
+               << _T("2000 if the registry keys are overridden.") << std::endl;
+    return;
+  }
+
+  int random = cm_->GetUpdateWorkerStartUpDelayMs();
+  EXPECT_GE(random, kUpdateTimerStartupDelayMinMs);
+  EXPECT_LE(random, kUpdateTimerStartupDelayMaxMs);
+
+  int num_times_to_try_for_diff_number = 3;
+  // We run the method num_times_to_try_for_diff_number times to make
+  // sure that at least one of these returns a number that is different
+  // from the one that is returned above. This is needed, since the
+  // method returns a number between kUpdateTimerStartupDelayMinMs and
+  // kUpdateTimerStartupDelayMaxMs.
+  // If this fails a lot we should disable the if check below.
+  bool found_one_not_equal = false;
+  for (int i = 0; i < num_times_to_try_for_diff_number; ++i) {
+    int random_compare = cm_->GetUpdateWorkerStartUpDelayMs();
+
+    EXPECT_GE(random_compare, kUpdateTimerStartupDelayMinMs);
+    EXPECT_LE(random_compare, kUpdateTimerStartupDelayMaxMs);
+
+    if (random_compare != random) {
+      found_one_not_equal = true;
+      break;
+    }
+  }
+
+  EXPECT_TRUE(found_one_not_equal);
+}
+
+TEST_F(ConfigManagerTest, GetUpdateWorkerStartUpDelayMs_Override) {
+  // Test that the initial delay time to launch a worker can be overriden.
+  DWORD val = 3320;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueAuCheckPeriodMs,
+                                    val));
+
+  int random = cm_->GetUpdateWorkerStartUpDelayMs();
+  EXPECT_EQ(val, random);
+}
+
+TEST_F(ConfigManagerTest, GetTimeSinceLastCheckedSec_User) {
+  // First, there is no value present in the registry.
+  uint32 now_sec = Time64ToInt32(GetCurrent100NSTime());
+  int time_since_last_checked_sec = cm_->GetTimeSinceLastCheckedSec(false);
+  EXPECT_EQ(now_sec, time_since_last_checked_sec);
+
+  // Second, write the 'now' time.
+  EXPECT_HRESULT_SUCCEEDED(cm_->SetLastCheckedTime(false, now_sec));
+  time_since_last_checked_sec = cm_->GetTimeSinceLastCheckedSec(false);
+  EXPECT_EQ(0, time_since_last_checked_sec);
+}
+
+TEST_F(ConfigManagerTest, GetTimeSinceLastCheckedSec_Machine) {
+  uint32 now_sec = Time64ToInt32(GetCurrent100NSTime());
+  int time_since_last_checked_sec = cm_->GetTimeSinceLastCheckedSec(true);
+  EXPECT_EQ(now_sec, time_since_last_checked_sec);
+
+  EXPECT_HRESULT_SUCCEEDED(cm_->SetLastCheckedTime(true, now_sec));
+  time_since_last_checked_sec = cm_->GetTimeSinceLastCheckedSec(true);
+  EXPECT_EQ(0, time_since_last_checked_sec);
+}
+
+TEST_F(ConfigManagerTest, GetNetConfig) {
+  CString actual_value;
+  EXPECT_HRESULT_FAILED(cm_->GetNetConfig(&actual_value));
+
+  const CString expected_value = _T("proxy:8080");
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueNetConfig,
+                                    expected_value));
+
+  EXPECT_HRESULT_SUCCEEDED(cm_->GetNetConfig(&actual_value));
+  EXPECT_STREQ(expected_value, actual_value);
+}
+
+TEST_F(ConfigManagerTest, GetInstallTime) {
+  EXPECT_SUCCEEDED(DeleteUpdateTime(false));
+  EXPECT_SUCCEEDED(DeleteFirstInstallTime(false));
+  EXPECT_EQ(0, ConfigManager::GetInstallTime(false));
+
+  DWORD time = 500;
+  EXPECT_SUCCEEDED(SetFirstInstallTime(false, time));
+  EXPECT_EQ(time, ConfigManager::GetInstallTime(false));
+
+  time = 1000;
+  EXPECT_SUCCEEDED(SetUpdateTime(false, time));
+  EXPECT_EQ(time, ConfigManager::GetInstallTime(false));
+
+  EXPECT_SUCCEEDED(DeleteFirstInstallTime(false));
+  EXPECT_EQ(time, ConfigManager::GetInstallTime(false));
+}
+
+TEST_F(ConfigManagerTest, Is24HoursSinceInstall) {
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+  const int k12HourPeriodSec = 12 * 60 * 60;
+  const int k48HourPeriodSec = 48 * 60 * 60;
+
+  const uint32 first_install_12 = now - k12HourPeriodSec;
+  const uint32 first_install_48 = now - k48HourPeriodSec;
+
+  EXPECT_SUCCEEDED(SetFirstInstallTime(false, first_install_12));
+  EXPECT_FALSE(ConfigManager::Is24HoursSinceInstall(false));
+
+  EXPECT_SUCCEEDED(SetFirstInstallTime(false, first_install_48));
+  EXPECT_TRUE(ConfigManager::Is24HoursSinceInstall(false));
+
+  EXPECT_SUCCEEDED(SetUpdateTime(false, first_install_12));
+  EXPECT_FALSE(ConfigManager::Is24HoursSinceInstall(false));
+
+  EXPECT_SUCCEEDED(SetUpdateTime(false, first_install_48));
+  EXPECT_TRUE(ConfigManager::Is24HoursSinceInstall(false));
+}
+
+TEST_F(ConfigManagerTest, AlwaysAllowCrashUploads) {
+  EXPECT_FALSE(cm_->AlwaysAllowCrashUploads());
+
+  DWORD value = 1;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueAlwaysAllowCrashUploads,
+                                    value));
+
+  EXPECT_TRUE(cm_->AlwaysAllowCrashUploads());
+
+  value = 0;
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueAlwaysAllowCrashUploads,
+                                    value));
+
+  EXPECT_FALSE(cm_->AlwaysAllowCrashUploads());
+}
+
+}  // namespace omaha
diff --git a/common/const_addresses.h b/common/const_addresses.h
deleted file mode 100644
index c1a6b3e..0000000
--- a/common/const_addresses.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Constants for dealing with machines.
-//
-// Manifests are requested over HTTPS. All other network communication goes
-// over HTTP.
-
-#ifndef OMAHA_COMMON_CONST_ADDRESSES_H__
-#define OMAHA_COMMON_CONST_ADDRESSES_H__
-
-#include <tchar.h>
-
-namespace omaha {
-
-// Static string that gives the main Google website address
-#define kGoogleHttpServer         _T("www.google.com")
-
-// Static string used as an identity for the "Omaha" Google domain.
-#define kGoopdateServer           _T("tools.google.com")
-
-// HTTP protocol prefix
-#define kProtoSuffix              _T("://")
-#define kFileProtoScheme          _T("file")
-#define kHttpProtoScheme          _T("http")
-#define kHttpsProtoScheme         _T("https")
-#define kHttpProto                kHttpProtoScheme kProtoSuffix
-#define kHttpsProto               kHttpsProtoScheme kProtoSuffix
-#define kFileProto                kFileProtoScheme kProtoSuffix
-
-// Default ports for proxies
-#define kDefaultHttpProxyPort     80
-#define kDefaultSslProxyPort      443
-
-// Update checks and manifest requests.
-const TCHAR* const kUrlUpdateCheck =
-    _T("https://tools.google.com/service/update2");
-
-// Pings.
-const TCHAR* const kUrlPing = _T("http://tools.google.com/service/update2");
-
-// Crash reports.
-const TCHAR* const kUrlCrashReport =
-    _T("http://clients2.google.com/cr/report");
-
-// More information url.
-const TCHAR* const kUrlMoreInformation =
-    _T("http://www.google.com/support/installer/?");
-
-// Code Red check url.
-const TCHAR* const kUrlCodeRedCheck =
-    _T("http://cr-tools.clients.google.com/service/check2");
-
-// Usage stats url.
-const TCHAR* const kUrlUsageStatsReport =
-    _T("http://clients5.google.com/tbproxy/usagestats");
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_CONST_ADDRESSES_H__
diff --git a/common/const_cmd_line.h b/common/const_cmd_line.h
index fa2b117..1a96083 100644
--- a/common/const_cmd_line.h
+++ b/common/const_cmd_line.h
@@ -25,16 +25,16 @@
 // These modes are invoked by or on metainstallers or by the OneClick plugin  .
 //
 
-// The "install" switch indicates installing Google Update and the app.
+// The "install" switch indicates installing Omaha and the app.
 const TCHAR* const kCmdLineInstall = _T("install");
 
 // The "installelevated" switch indicates installing after elevating.
 const TCHAR* const kCmdLineInstallElevated = _T("installelevated");
 
-// The "update" switch indicates a self-update Google Update.
+// The "update" switch indicates an Omaha self-update.
 const TCHAR* const kCmdLineUpdate = _T("update");
 
-// The "recover" switch indicates Google Update is to be repaired due to a
+// The "recover" switch indicates Omaha is to be repaired due to a
 // Code Red scenario.
 const TCHAR* const kCmdLineRecover = _T("recover");
 
@@ -63,27 +63,22 @@
 // an application.
 const TCHAR* const kCmdLineAppHandoffInstall = _T("handoff");
 
-// The "ig" switch indicates a worker to finish installing Google Update and
-// perform an interactive install of an application.
+// The "ig" switch was used in Omaha 2 to indicate a worker to finish installing
+// Omaha and perform an interactive install of an application.
 // ig = Install Google Update.
-const TCHAR* const kCmdLineFinishGoogleUpdateInstall = _T("ig");
+const TCHAR* const kCmdLineLegacyFinishGoogleUpdateInstall = _T("ig");
 
 // The "ua" switch indicates a worker to perform an update check for all
 // applications.
 // ua = Update Applications.
 const TCHAR* const kCmdLineUpdateApps = _T("ua");
 
-// The "ug" switch indicates a worker to finish updating Google Update.
-// No application update checks are performed.
-// ug = Update Google Update.
-const TCHAR* const kCmdLineFinishGoogleUpdateUpdate = _T("ug");
-
 // The "cr" switch indicates that the worker has been invoked to perform a Code
 // Red check.
 const TCHAR* const kCmdLineCodeRedCheck = _T("cr");
 
 // The "registerproduct" switch will register a product GUID in Clients and
-// install only goopdate.
+// install only Omaha.
 const TCHAR* const kCmdLineRegisterProduct = _T("registerproduct");
 
 // The "unregisterproduct" switch will unregister a product GUID from Clients.
@@ -93,9 +88,14 @@
 // Minor modes
 //
 
-// The "svc" switch indicates that goopdate runs as a service.
+// The "svc" switch indicates that Omaha runs as a service that only accepts
+// calls from high integrity COM callers.
 const TCHAR* const kCmdLineService = _T("svc");
 
+// The "medsvc" switch indicates that Omaha runs as a service that accepts
+// calls from medium integrity COM callers.
+const TCHAR* const kCmdLineMediumService = _T("medsvc");
+
 // The "regsvc" switch is used to register the service. Only used by unit
 // tests at the moment.
 const TCHAR* const kCmdLineRegisterService = _T("regsvc");
@@ -108,11 +108,11 @@
 // This switch will be passed in via ServiceParameters.
 const TCHAR* const kCmdLineServiceComServer = _T("/comsvc");
 
-// The "regserver" switch indicates that goopdate should do its Windows
+// The "regserver" switch indicates that Omaha should do its Windows
 // service COM server registration including coclasses and its APPID.
 const TCHAR* const kCmdRegServer = _T("regserver");
 
-// The "unregserver" switch indicates that goopdate should undo its
+// The "unregserver" switch indicates that Omaha should undo its
 // COM registration.
 const TCHAR* const kCmdUnregServer = _T("unregserver");
 
@@ -129,26 +129,20 @@
 const TCHAR* const kCmdLineComServer = _T("Embedding");
 const TCHAR* const kCmdLineComServerDash = _T("-Embedding");
 
-//
-// Legacy support modes
-//
+// Activates broker mode. Broker mode is intended to facilitate communication
+// between low-integrity clients and the high-integrity Omaha DCOM service.
+const TCHAR* const kCmdLineComBroker = _T("/broker");
 
-// The legacy "UI" switch supports hand-off machine installs from Omaha 1.
-const TCHAR* const kCmdLineLegacyUi = _T("ui");
+// Activates OnDemand mode.
+const TCHAR* const kCmdLineOnDemand = _T("/ondemand");
 
-// The legacy "lang" switch supports /lang for the /ui command.  No one else
-// should be sending /lang as a command line switch to goopdate.
-const TCHAR* const kCmdLineLegacyLang = _T("lang");
+// The "uninstall" switch indicates that Omaha should uninstall if appropriate.
+const TCHAR* const kCmdLineUninstall = _T("uninstall");
 
-// The "uiuser" switch is used to support hand off of of Omaha 1 user manifests.
-const TCHAR* const kCmdLineLegacyUserManifest = _T("uiuser");
-
-// The "extra" switch was used to provide a string containing additional
-// arguments. Extra args are now passed as an argument to the respective switch.
-const TCHAR* const kCmdLineLegacyExtra = _T("extra");
-
-// Passed to the new instance when launching it elevated.
-const TCHAR* const kCmdLineLegacyVistaInstall = _T("installelevated");
+// The "ping" switch makes Omaha send a ping with the specified string. The
+// string is expected to be web safe base64 encoded and it will be decoded
+// before sending it to the server.
+const TCHAR* const kCmdLinePing = _T("ping");
 
 //
 // Non-product modes
@@ -158,7 +152,7 @@
 // Run network diagnostics.
 const TCHAR* const kCmdLineNetDiags = _T("netdiags");
 
-// The "crash" switch indicates that goopdate should crash upon startup.
+// The "crash" switch indicates that Omaha should crash upon startup.
 // This option is used to test the crash reporting system.
 const TCHAR* const kCmdLineCrash = _T("crash");
 
@@ -170,7 +164,8 @@
 // silently.
 const TCHAR* const kCmdLineSilent = _T("silent");
 
-const TCHAR* const kCmdLineOfflineInstall = _T("offlineinstall");
+const TCHAR* const kCmdLineLegacyOfflineInstall = _T("offlineinstall");
+const TCHAR* const kCmdLineOfflineDir = _T("offlinedir");
 
 // The "oem" switch specifies that this is an OEM install in Sysprep mode in an
 // OEM factory.
@@ -185,37 +180,53 @@
 // when doing a recover setup.
 const TCHAR* const kCmdLineMachine = _T("machine");
 
-// The "uninstall" switch is an option to /ua to tell it to skip the update
-// check and proceed with an uninstall.
-const TCHAR* const kCmdLineUninstall = _T("uninstall");
-
 // The "i" switch indicates that the crash has happend in an
 // interactive process which has a UI up. The switch is an option for
 // the "report" switch.
 const TCHAR* const kCmdLineInteractive = _T("i");
 
+// The "sessionid" switch indicates that a specific session ID should be used by
+// this instance of Omaha for network requests.  This switch is an option for
+// the "install", "handoff", and "update" modes.
+const TCHAR* const kCmdLineSessionId = _T("sessionid");
+
 // The "installsource" switch that is used to pass the source of installation
 // for ping tracking.  For example:  "/installsource OneClick".
 const TCHAR* const kCmdLineInstallSource = _T("installsource");
 
-// This is a valid value for installsource that means it's a OneClick install.
+// installsource values generated internally by Omaha.
+const TCHAR* const kCmdLineInstallSource_TaggedMetainstaller = _T("taggedmi");
 const TCHAR* const kCmdLineInstallSource_OneClick = _T("oneclick");
-
+const TCHAR* const kCmdLineInstallSource_ClickOnce = _T("clickonce");
+const TCHAR* const kCmdLineInstallSource_Offline = _T("offline");
+const TCHAR* const kCmdLineInstallSource_InstallDefault = _T("otherinstallcmd");
+const TCHAR* const kCmdLineInstallSource_Scheduler = _T("scheduler");
+const TCHAR* const kCmdLineInstallSource_Core = _T("core");
 const TCHAR* const kCmdLineInstallSource_OnDemandUpdate = _T("ondemandupdate");
 const TCHAR* const kCmdLineInstallSource_OnDemandCheckForUpdate =
     _T("ondemandcheckforupdate");
+const TCHAR* const kCmdLineInstallSource_OnDemandUA = _T("ondemandua");
+const TCHAR* const kCmdLineInstallSource_SelfUpdate = _T("selfupdate");
+const TCHAR* const kCmdLineInstallSource_Update3Web = _T("update3web");
+const TCHAR* const kCmdLineInstallSource_Update3Web_NewApps =
+    _T("update3web-newapps");
+const TCHAR* const kCmdLineInstallSource_Update3Web_OnDemand =
+    _T("update3web-ondemand");
+const TCHAR* const kCmdLineInstallSource_Update3Web_Components =
+    _T("update3web-components");
 
-const TCHAR* const kCmdLineInstallSource_ClickOnce = _T("clickonce");
-
-const TCHAR* const kCmdLineInstallSource_Offline = _T("offline");
-
-const TCHAR* const kCmdLineInstallSourceScheduler = _T("scheduler");
-const TCHAR* const kCmdLineInstallSourceCore = _T("core");
+// This install source is not used as a command line argument but internally
+// created by Omaha.
+const TCHAR* const kInstallSource_Uninstall = _T("uninstall");
 
 //
 // "Extra" arguments provided in the metainstaller tag.
 //
 
+// "bundlename" extra argument is the name of the bundle being installed. If not
+// specified, the first app's appname is used.
+const TCHAR* const kExtraArgBundleName = _T("bundlename");
+
 // "lang" extra argument tells Omaha the language of the product the user is
 // installing.
 const TCHAR* const kExtraArgLanguage = _T("lang");
@@ -239,20 +250,31 @@
 // This value is used to set the initial client for Omaha and the client app.
 const TCHAR* const kExtraArgClientId = _T("client");
 
+// "experiments" extra argument is a set of experiment labels used to track
+// installs that are included in experiments.  Use "experiments" for
+// per-app arguments; use "omahaexperiments" for Omaha-specific labels.
+const TCHAR* const kExtraArgExperimentLabels = _T("experiments");
+const TCHAR* const kExtraArgOmahaExperimentLabels = _T("omahaexperiments");
+
 // "referral" extra argument is a referral ID used for tracking referrals.
 const TCHAR* const kExtraArgReferralId = _T("referral");
 
-// '" extra argument tells Omaha to set the ap value in the registry.
+// "ap" extra argument tells Omaha to set the ap value in the registry.
 const TCHAR* const kExtraArgAdditionalParameters = _T("ap");
 
-// "tt_token" extra argument tells Omaha to set the TT value in the registry.
+// "tttoken" extra argument tells Omaha to set the TT value in the registry.
 const TCHAR* const kExtraArgTTToken = _T("tttoken");
 
-
 // "browser" extra argument tells Omaha which browser to restart on
 // successful install.
 const TCHAR* const kExtraArgBrowserType = _T("browser");
 
+// "runtime" extra argument tells Omaha to only install itself, staying on
+// the system without any associated application for at least 24 hours.
+// This is used to expose our COM API to a process that will install
+// applications via that API after the meta-installer exits.
+const TCHAR* const kExtraArgRuntime = _T("runtime");
+
 // The list of arguments that are needed for a meta-installer, to
 // indicate which application is being installed. These are stamped
 // inside the meta-installer binary.
diff --git a/common/const_config.h b/common/const_config.h
deleted file mode 100644
index 14e24ad..0000000
--- a/common/const_config.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_COMMON_CONST_CONFIG_H__
-#define OMAHA_COMMON_CONST_CONFIG_H__
-
-namespace omaha {
-
-// Root Registry keys
-//
-// In this version, everything is put under HKLM.
-// NOTE: PUBLISHER_NAME_ANSI and PRODUCT_NAME_ANSI are defined in mk_common
-// kProgramAnsi is "Google Update"
-#define kProgramAnsi      PUBLISHER_NAME_ANSI " " PRODUCT_NAME_ANSI
-#define kCiProgram        _T(PUBLISHER_NAME_ANSI) _T(" ") _T(PRODUCT_NAME_ANSI)
-#define kGoogleRegKey     _T("Software\\") _T(PUBLISHER_NAME_ANSI) _T("\\")
-#define kGoogleFullRegKey _T("HKLM\\") kGoogleRegKey
-
-// kProductRegKey is _T("Software\\Google\\Update")
-#define kProductRegKey       kGoogleRegKey _T(PRODUCT_NAME_ANSI)
-#define kCiFullRegKey        _T("HKLM\\") kProductRegKey
-#define kRegKeyConfig        _T("Config")
-#define kRegKeyConfigPrefix  kRegKeyConfig
-#define kCiFullRegKeyConfig  kCiFullRegKey _T("\\") kRegKeyConfig
-#define kRegKeyShared        _T("Shared")
-#define kCiRegKeyShared      kProductRegKey _T("\\") kRegKeyShared
-#define kRegValueReportIds   _T("report_ids")
-
-// NOTE: ACTIVEX_VERSION_ANSI is defined in mk_common
-// For example: kOneClickProgIdAnsi == "Google.OneClickCtrl.1"
-#define kOneClickProgIdAnsi  PUBLISHER_NAME_ANSI \
-                             ".OneClickCtrl." \
-                             ACTIVEX_VERSION_ANSI
-#define kOneClickProgId      _T(PUBLISHER_NAME_ANSI) \
-                             _T(".OneClickCtrl.") \
-                             _T(ACTIVEX_VERSION_ANSI)
-
-// The plug-in MIME type.
-// For example:
-//     kOneClickPluginMimeTypeAnsi == "application/x-vnd.google.oneclickctrl.1"
-#define kOneClickPluginMimeTypeAnsi  "application/x-vnd.google.oneclickctrl." \
-                                     ACTIVEX_VERSION_ANSI
-
-// .oii is just an arbitrary extension.
-#define kOneClickPluginMimeDescriptionAnsi  kOneClickPluginMimeTypeAnsi \
-                                            ":.oii:" \
-                                            kProgramAnsi
-
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_CONST_CONFIG_H__
-
diff --git a/common/const_debug.h b/common/const_debug.h
deleted file mode 100644
index d2cf292..0000000
--- a/common/const_debug.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_COMMON_CONST_DEBUG_H_
-#define OMAHA_COMMON_CONST_DEBUG_H_
-
-namespace omaha {
-
-#include "omaha/common/constants.h"
-
-// kCiDebugDirectory is relative to the system drive if available or the
-// current directory otherwise. The expectation is that %SystemDrive% is
-// always avaialable, including for the code running as system.
-#define kCiDebugDirectory     kFilePrefix _T("-debug")
-
-// TODO(omaha): unify the debugging and logging support so that these files
-// are created under the same directory as the log file.
-#define kCiDebugLogFile                   _T("debug.log")
-#define kCiAssertOccurredFile             _T("assert.log")
-#define kCiAbortOccurredFile              _T("abort.log")
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_CONST_DEBUG_H_
diff --git a/common/const_goopdate.h b/common/const_goopdate.h
new file mode 100644
index 0000000..d05dbf4
--- /dev/null
+++ b/common/const_goopdate.h
@@ -0,0 +1,294 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Goopdate constants - Reduces dependencies on goopdate.h for names that
+// are needed outside Goopdate.
+//
+// TODO(omaha): it appears that the string constants below are not
+// optimized out the program image even if not used. Figure out why they still
+// show up in the tiny shell in optimized builds.
+
+#ifndef OMAHA_COMMON_CONST_GOOPDATE_H_
+#define OMAHA_COMMON_CONST_GOOPDATE_H_
+
+#include <tchar.h>
+#include "omaha/base/constants.h"
+
+// TODO(omaha3): Many of these values are specific to the COM server and a few
+// may apply to the client. Move most of them to the goopdate directory.
+
+namespace omaha {
+
+enum ActiveStates {
+  ACTIVE_NOTRUN = 0,
+  ACTIVE_RUN,
+  ACTIVE_UNKNOWN
+};
+
+// Specifies what Omaha should do on successful installation.
+enum SuccessfulInstallAction {
+  SUCCESS_ACTION_DEFAULT = 0,
+  SUCCESS_ACTION_EXIT_SILENTLY,
+  SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD,
+};
+
+// Specifies the type of result the app installer returned.
+enum InstallerResultType {
+  INSTALLER_RESULT_UNKNOWN,
+  INSTALLER_RESULT_SUCCESS,
+  INSTALLER_RESULT_ERROR_MSI,
+  INSTALLER_RESULT_ERROR_SYSTEM,
+  INSTALLER_RESULT_ERROR_OTHER,
+};
+
+// The enumeration of the events. Some of the events are used in IPC,
+// these are named events, the rest are not named. Of the named events there
+// are two types the global and the local events.
+// Global Events:
+// The events which are global are indicated with the Global suffix in the
+// enum. These events have the Global prefix attached to the event name and
+// are used for IPC across terminal server sessions, these events are used
+// as barriers, i.e. all waiting threads are release on these events.
+// Local Events:
+// The local counter parts of these events have the Local prefix attached
+// to their names, and dont have any suffix in the enum names. The local events
+// are used to release only one thread, this works as we know that there is
+// only one user goopdate process in a user session and one machine goopdate.
+// The local events also have the user sid added to the event name. This is to
+// work around a bug in win2K and on XP where in case of TS, the event names
+// will collide, inspite of the name changes.
+enum GoopdateEvents {
+  EVENT_INVALID = -1,
+  EVENT_KILL_MESSAGE_LOOP = 0,
+  EVENT_UPDATE_TIMER,
+  EVENT_NEW_MANIFEST,          // Used in IPC.
+  EVENT_QUIET_MODE,            // Used in IPC.
+  EVENT_LEGACY_QUIET_MODE,     // Only used to shut down pre-i18n goopdates.
+  EVENT_CODE_RED_TIMER,
+};
+
+// Represents the values that are used by the application to indicate its
+// requirement for admin.
+enum NeedsAdmin {
+  NEEDS_ADMIN_NO = 0,   // The application will install per user.
+  NEEDS_ADMIN_YES,      // The application will install machine-wide.
+  NEEDS_ADMIN_PREFERS,  // The application will install machine-wide if
+                        // permissions allow, else will install per-user.
+};
+
+// Using extern or intern linkage for these strings yields the same code size
+// for the executable DLL.
+
+// The string representation of the NeedsAdmin enum as specified in the tag or
+// command line. This is eventually parsed into the needs_admin member of
+// CommandLineAppArgs.
+const TCHAR* const kNeedsAdminNo = _T("&needsadmin=false");
+const TCHAR* const kNeedsAdminYes = _T("&needsadmin=true");
+const TCHAR* const kNeedsAdminPrefers = _T("&needsadmin=prefers");
+
+// Environment variable inherited by an installer child process that indicates
+// whether GoogleUpdate is running as user or machine.
+const TCHAR* const kEnvVariableIsMachine = APP_NAME_IDENTIFIER _T("IsMachine");
+
+// Registry values read from the Clients key for transmitting custom install
+// errors, messages, etc. On an update, the InstallerXXX values are renamed to
+// LastInstallerXXX values. The LastInstallerXXX values remain around until the
+// next update.
+const TCHAR* const kRegValueInstallerResult      = _T("InstallerResult");
+const TCHAR* const kRegValueInstallerError       = _T("InstallerError");
+const TCHAR* const kRegValueInstallerExtraCode1  = _T("InstallerExtraCode1");
+const TCHAR* const kRegValueInstallerResultUIString =
+    _T("InstallerResultUIString");
+const TCHAR* const kRegValueInstallerSuccessLaunchCmdLine =
+    _T("InstallerSuccessLaunchCmdLine");
+
+const TCHAR* const kRegValueLastInstallerResult =
+    _T("LastInstallerResult");
+const TCHAR* const kRegValueLastInstallerError =
+    _T("LastInstallerError");
+const TCHAR* const kRegValueLastInstallerExtraCode1 =
+    _T("LastInstallerExtraCode1");
+const TCHAR* const kRegValueLastInstallerResultUIString =
+    _T("LastInstallerResultUIString");
+const TCHAR* const kRegValueLastInstallerSuccessLaunchCmdLine =
+    _T("LastInstallerSuccessLaunchCmdLine");
+
+
+// Registry subkey in an app's Clients key that contains its commands.
+const TCHAR* const kCommandsRegKeyName       = _T("Commands");
+
+// Registry values read from the Clients commands key.
+const TCHAR* const kRegValueCommandLine      = _T("CommandLine");
+const TCHAR* const kRegValueSendsPings       = _T("SendsPings");
+const TCHAR* const kRegValueWebAccessible    = _T("WebAccessible");
+const TCHAR* const kRegValueReportingId      = _T("ReportingId");
+
+// Registry value in an app's Clients key that contains a registration update
+// hook CLSID.
+const TCHAR* const kRegValueUpdateHookClsid  = _T("RegistrationUpdateHook");
+
+// Registry values read from the Clients key and stored in the ClientState key.
+const TCHAR* const kRegValueLanguage         = _T("lang");
+const TCHAR* const kRegValueAppName          = _T("name");
+const TCHAR* const kRegValueProductVersion   = _T("pv");
+
+// Registry values stored in the ClientState key.
+const TCHAR* const kRegValueAdditionalParams = _T("ap");
+const TCHAR* const kRegValueBrandCode        = _T("brand");
+const TCHAR* const kRegValueBrowser          = _T("browser");
+const TCHAR* const kRegValueClientId         = _T("client");
+const TCHAR* const kRegValueDidRun           = _T("dr");
+const TCHAR* const kRegValueExperimentLabels = _T("experiment_labels");
+const TCHAR* const kRegValueInstallationId   = _T("iid");
+const TCHAR* const kRegValueOemInstall       = _T("oeminstall");
+const TCHAR* const kRegValueReferralId       = _T("referral");
+
+// This two registries hold client UTC timestamp of server's midnight of the day
+// that last active ping/roll call happened.
+const TCHAR* const kRegValueActivePingDayStartSec = _T("ActivePingDayStartSec");
+const TCHAR* const kRegValueRollCallDayStartSec   = _T("RollCallDayStartSec");
+
+// Registry values stored in the ClientState key related to Omaha's actions.
+// A "successful check" means "noupdate" received from the server or an update
+// was successfully applied.
+const TCHAR* const kRegValueInstallTimeSec          = _T("InstallTime");
+const TCHAR* const kRegValueLastSuccessfulCheckSec  = _T("LastCheckSuccess");
+const TCHAR* const kRegValueLastUpdateTimeSec       = _T("UpdateTime");
+
+// Registry values stored in the ClientState or ClientStateMedium keys.
+// Use accessor methods rather than reading them directly.
+const TCHAR* const kRegValueEulaAccepted     = _T("eulaaccepted");
+const TCHAR* const kRegValueUsageStats       = _T("usagestats");
+
+// Registry values stored in the ClientState key for Omaha's internal use.
+const TCHAR* const kRegValueTTToken               = _T("tttoken");
+const TCHAR* const kRegValueUpdateAvailableCount  = _T("UpdateAvailableCount");
+const TCHAR* const kRegValueUpdateAvailableSince  = _T("UpdateAvailableSince");
+
+// Registry values stored in the Update key.
+const TCHAR* const kRegValueDelayOmahaUninstall   = _T("DelayUninstall");
+const TCHAR* const kRegValueOmahaEulaAccepted     = _T("eulaaccepted");
+// TODO(omaha3): Consider renaming these if there is not a upgrade problem.
+// If we can't consider moving all "gupdate" values to the customization file.
+const TCHAR* const kRegValueServiceName           = _T("omaha_service_name");
+const TCHAR* const kRegValueMediumServiceName     = _T("omaham_service_name");
+const TCHAR* const kRegValueTaskNameC             = _T("omaha_task_name_c");
+const TCHAR* const kRegValueTaskNameUA            = _T("omaha_task_name_ua");
+const TCHAR* const kRegValueLastChecked           = _T("LastChecked");
+const TCHAR* const kRegValueOemInstallTimeSec     = _T("OemInstallTime");
+const TCHAR* const kRegValueCacheSizeLimitMBytes  = _T("PackageCacheSizeLimit");
+const TCHAR* const kRegValueCacheLifeLimitDays    = _T("PackageCacheLifeLimit");
+const TCHAR* const kRegValueInstalledPath         = _T("path");
+const TCHAR* const kRegValueUserId                = _T("uid");
+const TCHAR* const kRegValueSelfUpdateExtraCode1  = _T("UpdateCode1");
+const TCHAR* const kRegValueSelfUpdateErrorCode   = _T("UpdateError");
+const TCHAR* const kRegValueSelfUpdateVersion     = _T("UpdateVersion");
+const TCHAR* const kRegValueInstalledVersion      = _T("version");
+
+// TODO(omaha3): Consider moving all "gupdate" values to the customization file.
+// Use a non-gupdate name for the new medium service.
+const TCHAR* const kServicePrefix               = _T("omaha");
+const TCHAR* const kMediumServicePrefix         = _T("omaham");
+
+const TCHAR* const kScheduledTaskNameUserPrefix =
+    APP_NAME_IDENTIFIER _T("TaskUser");
+const TCHAR* const kScheduledTaskNameMachinePrefix =
+    APP_NAME_IDENTIFIER _T("TaskMachine");
+const TCHAR* const kScheduledTaskNameCoreSuffix = _T("Core");
+const TCHAR* const kScheduledTaskNameUASuffix   = _T("UA");
+
+const TCHAR* const kServiceFileName              = kOmahaShellFileName;
+const char*  const kGoopdateDllEntryAnsi         = "DllEntry";
+
+
+// Event Id's used for reporting in the event log.
+// Crash Report events.
+const int kCrashReportEventId        = 1;
+const int kCrashUploadEventId        = 2;
+
+// Update Check events.
+const int kUpdateCheckEventId        = 11;
+const int kUpdateEventId             = 12;
+const int kUninstallEventId          = 13;
+const int kWorkerStartEventId        = 14;
+const int kDownloadEventId           = 15;
+
+// Network Request events.
+const int kNetworkRequestEventId     = 20;
+
+// Maximum value the server can respond for elapsed_seconds attribute in
+// <daystart ...> element. The value is one day plus an hour ("fall back"
+// daylight savings).
+const int kMaxTimeSinceMidnightSec   = ((24 + 1) * 60 * 60);
+
+// Maximum time to keep the Installation ID. If the app was installed longer
+// than this time ago, the Installation ID will be deleted regardless of
+// whether the application has been run or not.
+const int kMaxLifeOfInstallationIDSec = (7 * 24 * 60 * 60);  // 7 days
+
+// Documented in the IDL for certain properties of ICurrentState.
+const int kCurrentStateProgressUnknown = -1;
+
+// COM ProgIDs.
+#define kProgIDUpdate3COMClassUser \
+    APP_NAME_IDENTIFIER _T(".Update3COMClassUser")
+#define kProgIDUpdate3COMClassService \
+    APP_NAME_IDENTIFIER _T(".Update3COMClassService")
+
+const TCHAR* const kProgIDOnDemandUser =
+    APP_NAME_IDENTIFIER _T(".OnDemandCOMClassUser");
+#define kProgIDOnDemandMachine \
+    APP_NAME_IDENTIFIER _T(".OnDemandCOMClassMachine")
+const TCHAR* const kProgIDOnDemandMachineFallback =
+    APP_NAME_IDENTIFIER _T(".OnDemandCOMClassMachineFallback");
+const TCHAR* const kProgIDOnDemandSvc =
+    APP_NAME_IDENTIFIER _T(".OnDemandCOMClassSvc");
+
+const TCHAR* const kProgIDUpdate3WebUser =
+    APP_NAME_IDENTIFIER _T(".Update3WebUser");
+#define kProgIDUpdate3WebMachine \
+    APP_NAME_IDENTIFIER _T(".Update3WebMachine")
+const TCHAR* const kProgIDUpdate3WebMachineFallback =
+    APP_NAME_IDENTIFIER _T(".Update3WebMachineFallback");
+const TCHAR* const kProgIDUpdate3WebSvc =
+    APP_NAME_IDENTIFIER _T(".Update3WebSvc");
+
+const TCHAR* const kProgIDGoogleUpdateCoreService =
+    APP_NAME_IDENTIFIER _T(".CoreClass");
+const TCHAR* const kProgIDGoogleUpdateCoreMachine =
+    APP_NAME_IDENTIFIER _T(".CoreMachineClass");
+
+const TCHAR* const kProgIDProcessLauncher =
+    APP_NAME_IDENTIFIER _T(".ProcessLauncher");
+
+const TCHAR* const kProgIDOneClickProcessLauncherUser =
+    _T(SHORT_COMPANY_NAME_ANSI) _T(".OneClickProcessLauncherUser");
+const TCHAR* const kProgIDOneClickProcessLauncherMachine =
+    _T(SHORT_COMPANY_NAME_ANSI) _T(".OneClickProcessLauncherMachine");
+
+const TCHAR* const kProgIDCoCreateAsync =
+    APP_NAME_IDENTIFIER _T(".CoCreateAsync");
+
+const TCHAR* const kProgIDCredentialDialogUser =
+    APP_NAME_IDENTIFIER _T(".CredentialDialogUser");
+const TCHAR* const kProgIDCredentialDialogMachine =
+    APP_NAME_IDENTIFIER _T(".CredentialDialogMachine");
+
+// Offline v3 manifest name.
+const TCHAR* const kOfflineManifestFileName = _T("OfflineManifest.gup");
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_CONST_GOOPDATE_H_
diff --git a/common/const_group_policy.h b/common/const_group_policy.h
new file mode 100644
index 0000000..903b2d5
--- /dev/null
+++ b/common/const_group_policy.h
@@ -0,0 +1,49 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_CONST_GROUP_POLICY_H_
+#define OMAHA_COMMON_CONST_GROUP_POLICY_H_
+
+#include <tchar.h>
+#include "omaha/base/constants.h"
+
+namespace omaha {
+
+// Key containing Omaha Group Policy settings. All policies are in HKLM.
+// TODO(omaha): Rename Omaha.
+const TCHAR* const kRegKeyGoopdateGroupPolicy =
+    MACHINE_KEY GOOPDATE_POLICIES_RELATIVE;
+
+// Preferences Categroy.
+const TCHAR* const kRegValueAutoUpdateCheckPeriodOverrideMinutes =
+    _T("AutoUpdateCheckPeriodMinutes");
+
+// Applications Categroy.
+// The prefix strings have the app's GUID appended to them.
+const TCHAR* const kRegValueInstallAppsDefault  = _T("InstallDefault");
+const TCHAR* const kRegValueInstallAppPrefix    = _T("Install");
+const TCHAR* const kRegValueUpdateAppsDefault   = _T("UpdateDefault");
+const TCHAR* const kRegValueUpdateAppPrefix     = _T("Update");
+
+const bool kInstallPolicyDefault    = true;
+const bool kUpdatePolicyDefault     = true;
+
+const int kPolicyDisabled           = 0;
+const int kPolicyEnabled            = 1;
+const int kPolicyManualUpdatesOnly  = 2;
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_CONST_GROUP_POLICY_H_
diff --git a/common/const_object_names.h b/common/const_object_names.h
deleted file mode 100644
index 80d02c0..0000000
--- a/common/const_object_names.h
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Kernel object names.
-//
-// TODO(omaha): rename MutexPrefix to ObjectPrefix
-//              remove the similar names from constants.h
-
-#ifndef OMAHA_COMMON_CONST_OBJECT_NAMES_H__
-#define OMAHA_COMMON_CONST_OBJECT_NAMES_H__
-
-#include <tchar.h>
-
-namespace omaha {
-
-// The prefix to use for global names in the win32 API's.
-const TCHAR* const kGlobalPrefix = _T("Global\\G");
-
-// Preserve legacy prefixes
-const TCHAR* const kOmaha10GlobalPrefix = _T("Global\\");
-const TCHAR* const kOmaha11GlobalPrefix = _T("Global\\G");
-const TCHAR* const kOmaha11LocalPrefix = _T("Local\\L");
-
-// Ensures that only one instance of machine or user Omaha is trying to setup at
-// a time.
-const TCHAR* const kSetupMutex = _T("{A9A86B93-B54E-4570-BE89-42418507707B}");
-
-// Signals the process to exit. Currently the core and the worker listen to
-// this event.
-// This same name was used in Omaha 1 (post-i18n).
-// TODO(omaha): Consider making all our processes listen to it. Maybe not the
-// service, since the SCM controls the life time of the service.
-const TCHAR* const kShutdownEvent =
-    _T("{A0C1F415-D2CE-4ddc-9B48-14E56FD55162}");
-
-// Only used to shut down older (pre-i18n) Goopdates.
-const TCHAR* const kEventLegacyQuietModeName =
-    _T("Global\\%s{B048F41D-5515-40eb-B4A6-B7F460379454}");
-
-// The installed setup worker sends an event to the setup worker running from
-// the temp directory to tell it to release the Setup Lock. The event's name
-// is passed in this environment variable name.
-const TCHAR* const kSetupCompleteEventEnvironmentVariableName =
-    _T("GOOGLE_UPDATE_SETUP_COMPLETE_EVENT_NAME");
-
-// The installed setup worker sends an event to the setup worker running from
-// the temp directory to tell it a UI has been displayed so it will not display
-// a second UI on error. The event's name is passed in this environment variable
-// name.
-const TCHAR* const kUiDisplayedEventEnvironmentVariableName =
-    _T("GOOGLE_UPDATE_UI_DISPLAYED_EVENT_NAME");
-
-// It enforces the Core only runs one instance per machine and one instance per
-// each user session.
-const TCHAR* const kCoreSingleInstance =
-    _T("{B5665124-2B19-40e2-A7BC-B44321E72C4B}");
-
-// It enforces the Crash Handler only runs one instance per machine and one
-// instance per each user session.
-const TCHAR* const kCrashHandlerSingleInstance =
-    _T("{C4F406E5-F024-4e3f-89A7-D5AB7663C3CD}");
-
-// The mutex names for ensuring single instances of the worker.
-// We allow only one instance of the update worker per session,
-// and only one instance of the install worker per application.
-// However since user omaha cannot create global names, the interactive
-// worker is also per session. I.e. two users can simultaneously install
-// the same application.
-const TCHAR* const kSingleupdateWorker =
-    _T("{D0BB2EF1-C183-4cdb-B218-040922092869}");
-
-// Mutex name for ensuring only one installer for an app is running in a
-// session. The %s is replaced with the application guid.
-const TCHAR* const kSingleInstallWorker =
-    _T("%s-{F707E94F-D66B-4525-AD84-B1DA87D6A971}");
-
-// Base name of job object for Setup phase 1 processes except self updates.
-// These may not be running as Local System for machine installs like
-// self-updates do.
-const TCHAR* const kSetupPhase1NonSelfUpdateJobObject =
-    _T("{5A913EF1-4160-48bc-B688-4D67EAEB698A}");
-
-// Base name of job object for interactive install processes except /install.
-const TCHAR* const kAppInstallJobObject =
-    _T("{8AD051DB-4FE6-458b-B103-7DCC78D56013}");
-
-// Base name of job object for silent processes that are okay to kill.
-const TCHAR* const kSilentJobObject =
-    _T("{A2300FD6-CBED-48a6-A3CB-B35C38A42F8E}");
-
-// Base name of job object for silent processes that should not be killed.
-const TCHAR* const kSilentDoNotKillJobObject =
-    _T("{D33A8A53-F57D-4fd9-A32D-238FD69B4BC4}");
-
-// The global lock to ensure that a single app is being installed for this
-// user/machine at a given time.
-const TCHAR* const kInstallManagerSerializer =
-    _T("{0A175FBE-AEEC-4fea-855A-2AA549A88846}");
-
-// Serializes access to metrics stores, machine and user, respectively.
-const TCHAR* const kMetricsSerializer =
-    _T("{C68009EA-1163-4498-8E93-D5C4E317D8CE}");
-
-// Serializes access to the global network configuration, such as the CUP keys.
-const TCHAR* const kNetworkConfigLock =
-    _T("{0E900C7B-04B0-47f9-81B0-F8D94F2DF01B}");
-
-// The name of the shared memory object containing the serialized COM
-// interface pointer exposed by the machine core.
-const TCHAR* const kGoogleUpdateCoreSharedMemoryName =
-    _T("Global\\GoogleUpdateCore");
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_CONST_OBJECT_NAMES_H__
-
diff --git a/common/const_timeouts.h b/common/const_timeouts.h
deleted file mode 100644
index 60c29bf..0000000
--- a/common/const_timeouts.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Constants used for different timeouts in Common Installer
-
-#ifndef OMAHA_COMMON_CONST_TIMEOUTS_H__
-#define OMAHA_COMMON_CONST_TIMEOUTS_H__
-
-namespace omaha {
-
-// Timeout for tearing down thread
-const int kMaxThreadDestructionTimeMs = 2000;
-
-const int kRegisterExeTimeoutMs =                  30000;      // 30 seconds
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_CONST_TIMEOUTS_H__
-
diff --git a/common/const_ui.h b/common/const_ui.h
deleted file mode 100644
index b41be7a..0000000
--- a/common/const_ui.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// UI constants
-
-#ifndef OMAHA_COMMON_CONST_UI_H_
-#define OMAHA_COMMON_CONST_UI_H_
-
-namespace omaha {
-}
-
-#endif  // OMAHA_COMMON_CONST_UI_H_
diff --git a/common/const_utils.h b/common/const_utils.h
deleted file mode 100644
index deb1540..0000000
--- a/common/const_utils.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2004-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Constants used in util functions
-
-#ifndef OMAHA_COMMON_CONST_UTILS_H__
-#define OMAHA_COMMON_CONST_UTILS_H__
-
-namespace omaha {
-
-// The registry key for the registered application path. Take a look at
-// http://msdn2.microsoft.com/en-us/library/ms997545.aspx for more information.
-#define kRegKeyApplicationPath \
-    _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths")
-#define kRegKeyPathValue _T("Path")
-
-
-// TODO(omaha): We could probably move most of these to browser_utils.
-
-// The regkey for the default browser the Windows Shell opens.
-#define kRegKeyDefaultBrowser _T("SOFTWARE\\Clients\\StartMenuInternet")
-#define kRegKeyUserDefaultBrowser _T("HKCU\\") kRegKeyDefaultBrowser
-#define kRegKeyMachineDefaultBrowser _T("HKLM\\") kRegKeyDefaultBrowser
-#define kRegKeyShellOpenCommand _T("\\shell\\open\\command")
-#define kRegKeyLegacyDefaultBrowser _T("HKCR\\http")
-#define kRegKeyLegacyDefaultBrowserCommand \
-            kRegKeyLegacyDefaultBrowser kRegKeyShellOpenCommand
-
-#define kIeExeName _T("IEXPLORE.EXE")
-#define kFirefoxExeName _T("FIREFOX.EXE")
-#define kChromeExeName _T("CHROME.EXE")
-
-// The regkey for proxy settings for IE.
-const TCHAR* const kRegKeyIESettings =
-    _T("HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings");
-const TCHAR* const kRegKeyIESettingsConnections = _T("Connections");
-const TCHAR* const kRegValueIEDefaultConnectionSettings =
-    _T("DefaultConnectionSettings");
-const TCHAR* const kRegValueIEProxyEnable = _T("ProxyEnable");
-const TCHAR* const kRegValueIEProxyServer = _T("ProxyServer");
-const TCHAR* const kRegValueIEAutoConfigURL = _T("AutoConfigURL");
-
-// Internet Explorer.
-#define kRegKeyIeClass \
-    _T("HKCR\\CLSID\\{0002DF01-0000-0000-C000-000000000046}\\LocalServer32")
-#define kRegValueIeClass _T("")
-
-// Firefox.
-#define kRegKeyFirefox \
-    _T("HKCR\\Applications\\FIREFOX.EXE\\shell\\open\\command")
-#define kRegValueFirefox        _T("")
-#define kFullRegKeyFirefox      _T("HKLM\\SOFTWARE\\Mozilla\\Mozilla Firefox")
-#define kRegKeyFirefoxPlugins   _T("plugins")
-#define kFirefoxCurrentVersion  _T("CurrentVersion")
-#define kFirefoxInstallDir      _T("Install Directory")
-
-// Chrome.
-#define kRegKeyChrome _T("HKCR\\Applications\\chrome.exe\\shell\\open\\command")
-#define kRegValueChrome _T("")
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_CONST_UTILS_H__
diff --git a/common/constants.h b/common/constants.h
deleted file mode 100644
index e244990..0000000
--- a/common/constants.h
+++ /dev/null
@@ -1,330 +0,0 @@
-// Copyright 2003-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// Constants used by Omaha project
-
-#ifndef OMAHA_COMMON_CONSTANTS_H__
-#define OMAHA_COMMON_CONSTANTS_H__
-
-#include <windows.h>
-#include <tchar.h>
-
-namespace omaha {
-
-// Exe loading address for Windows executables
-// (also the HMODULE for an exe app)
-#define kExeLoadingAddress      0x00400000
-
-#define kPublisherName _T(PUBLISHER_NAME_ANSI)
-
-// application name (for debugging messages)
-// kAppName == "Google Update"
-#define kAppName          kPublisherName _T(" ") _T(PRODUCT_NAME_ANSI)
-
-// Product name to report
-// kProductNameToReport == "Google Update"
-#define kProductNameToReport    kAppName
-
-// prefix for any created files
-#define kFilePrefix  _T("GoogleUpdate")
-
-// prefix for any Win32 objects (mutexes, events, etc.)
-#define kLockPrefix   _T("_GOOGLE_UPDATE_")
-
-//
-// Omaha's app ID
-//
-#define GOOPDATE_APP_ID _T("{430FD4D0-B729-4F61-AA34-91526481799D}")
-const TCHAR* const kGoogleUpdateAppId = GOOPDATE_APP_ID;
-const GUID kGoopdateGuid = {0x430FD4D0, 0xB729, 0x4F61,
-                            {0xAA, 0x34, 0x91, 0x52, 0x64, 0x81, 0x79, 0x9D}};
-
-//
-// Directory names
-//
-#define OFFLINE_DIR_NAME _T("Offline")
-
-#define OMAHA_REL_GOOGLE_DIR _T("Google")
-#define OMAHA_REL_CRASH_DIR OMAHA_REL_GOOGLE_DIR _T("\\CrashReports")
-#define OMAHA_REL_GOOPDATE_INSTALL_DIR OMAHA_REL_GOOGLE_DIR _T("\\Update")
-#define OMAHA_REL_LOG_DIR OMAHA_REL_GOOPDATE_INSTALL_DIR _T("\\Log")
-#define OMAHA_REL_OFFLINE_STORAGE_DIR \
-    OMAHA_REL_GOOPDATE_INSTALL_DIR _T("\\") OFFLINE_DIR_NAME
-#define OMAHA_REL_DOWNLOAD_STORAGE_DIR \
-    OMAHA_REL_GOOPDATE_INSTALL_DIR _T("\\Download")
-#define OMAHA_REL_TEMP_DOWNLOAD_DIR  _T("\\Temp")
-#define OMAHA_REL_MANIEFST_DIR OMAHA_REL_GOOPDATE_INSTALL_DIR _T("\\Manifest")
-#define OMAHA_REL_INITIAL_MANIFEST_DIR OMAHA_REL_MANIEFST_DIR _T("\\Initial")
-#define OMAHA_REL_MANIFEST_INPROGRESS_DIR \
-    OMAHA_REL_MANIEFST_DIR _T("\\InProgress")
-
-//
-// Registry keys and values
-//
-#define MACHINE_KEY_NAME _T("HKLM")
-#define MACHINE_KEY MACHINE_KEY_NAME _T("\\")
-#define USER_KEY_NAME _T("HKCU")
-#define USER_KEY USER_KEY_NAME _T("\\")
-#define USERS_KEY _T("HKU\\")
-#define GOOGLE_MAIN_KEY _T("Software\\Google\\")
-#define GOOPDATE_MAIN_KEY GOOGLE_MAIN_KEY _T("Update\\")
-#define GOOPDATE_REG_RELATIVE_CLIENTS GOOPDATE_MAIN_KEY _T("Clients\\")
-#define GOOPDATE_REG_RELATIVE_CLIENT_STATE GOOPDATE_MAIN_KEY _T("ClientState\\")
-#define GOOPDATE_REG_RELATIVE_CLIENT_STATE_MEDIUM \
-    GOOPDATE_MAIN_KEY _T("ClientStateMedium\\")
-#define GOOGLE_POLICIES_MAIN_KEY _T("Software\\Policies\\Google\\")
-#define GOOPDATE_POLICIES_RELATIVE GOOGLE_POLICIES_MAIN_KEY _T("Update\\")
-
-#define USER_REG_GOOGLE USER_KEY GOOGLE_MAIN_KEY
-#define USER_REG_UPDATE USER_KEY GOOPDATE_MAIN_KEY
-#define USER_REG_CLIENTS USER_KEY GOOPDATE_REG_RELATIVE_CLIENTS
-#define USER_REG_CLIENTS_GOOPDATE  USER_REG_CLIENTS GOOPDATE_APP_ID
-#define USER_REG_CLIENT_STATE USER_KEY GOOPDATE_REG_RELATIVE_CLIENT_STATE
-#define USER_REG_CLIENT_STATE_GOOPDATE USER_REG_CLIENT_STATE GOOPDATE_APP_ID
-
-#define MACHINE_REG_GOOGLE MACHINE_KEY GOOGLE_MAIN_KEY
-#define MACHINE_REG_UPDATE MACHINE_KEY GOOPDATE_MAIN_KEY
-#define MACHINE_REG_CLIENTS MACHINE_KEY GOOPDATE_REG_RELATIVE_CLIENTS
-#define MACHINE_REG_CLIENTS_GOOPDATE  MACHINE_REG_CLIENTS GOOPDATE_APP_ID
-#define MACHINE_REG_CLIENT_STATE MACHINE_KEY GOOPDATE_REG_RELATIVE_CLIENT_STATE
-#define MACHINE_REG_CLIENT_STATE_GOOPDATE \
-    MACHINE_REG_CLIENT_STATE GOOPDATE_APP_ID
-#define MACHINE_REG_CLIENT_STATE_MEDIUM \
-    MACHINE_KEY GOOPDATE_REG_RELATIVE_CLIENT_STATE_MEDIUM
-
-#define USER_REG_VISTA_LOW_INTEGRITY_HKCU \
-    _T("Software\\Microsoft\\Internet Explorer\\") \
-    _T("InternetRegistry\\REGISTRY\\USER")
-
-#define MACHINE_REG_UPDATE_DEV MACHINE_KEY GOOGLE_MAIN_KEY _T("UpdateDev\\")
-
-//
-// Compatible shell versions
-// The following shell versions are compatible with the current version of
-// goopdate.dll and do not need be replaced: 1.2.131.7 and 1.2.183.9.
-//
-const ULONGLONG kCompatibleOlderShellVersions[] = { 0x0001000200830007,
-                                                    0x0001000200B70009,
-                                                  };
-
-//
-// Registry values under MACHINE_REG_UPDATE_DEV allow customization of the
-// default behavior. The overrides apply for both user and machine
-// instances of omaha.
-//
-// The values below can only be overriden in debug builds.
-const TCHAR* const kRegValueNameOverInstall    = _T("OverInstall");
-const TCHAR* const kRegValueNameUrl            = _T("url");
-const TCHAR* const kRegValueNamePingUrl        = _T("PingUrl");
-const TCHAR* const kRegValueNameWebPluginUrl   = _T("WebPluginUrl");
-
-// The values below can be overriden in both debug and opt builds.
-const TCHAR* const kRegValueTestSource         = _T("TestSource");
-const TCHAR* const kRegValueAuCheckPeriodMs    = _T("AuCheckPeriodMs");
-const TCHAR* const kRegValueCrCheckPeriodMs    = _T("CrCheckPeriodMs");
-const TCHAR* const kRegValueProxyHost          = _T("ProxyHost");
-const TCHAR* const kRegValueProxyPort          = _T("ProxyPort");
-
-// The values below can be overriden in unofficial builds.
-const TCHAR* const kRegValueNameWindowsInstalling = _T("WindowsInstalling");
-
-// Allows Google Update to log events in the Windows Event Log. This is
-// a DWORD value 0: Log nothing, 1: Log warnings and errors, 2: Log everything.
-const TCHAR* const kRegValueEventLogLevel      = _T("LogEventLevel");
-
-enum LogEventLevel {
-  LOG_EVENT_LEVEL_NONE           = 0,
-  LOG_EVENT_LEVEL_WARN_AND_ERROR = 1,
-  LOG_EVENT_LEVEL_ALL            = 2
-};
-
-// How often Google Update checks the server for updates.
-const TCHAR* const kRegValueLastCheckPeriodSec = _T("LastCheckPeriodSec");
-
-// Uses the production or the test cup keys. Once the client has negotiated
-// CUP credentials {sk, c} and it has saved them under the corresponding
-// Google\Update\network key then the client does not need any of the CUP keys.
-// To force the client to use test or production keys, {sk, c} credentials must
-// be cleared too.
-const TCHAR* const kRegValueCupKeys            = _T("TestKeys");
-
-// Allow a custom host pattern to be specified. For example,
-// "^https?://some_test_server\.google\.com/". For other examples, look at
-// site_lock_pattern_strings in oneclick_worker.cc. The detailed regular
-// expression syntax is documented in the MSDN documentation for the CAtlRegExp
-// class: http://msdn.microsoft.com/en-us/library/k3zs4axe(VS.80).aspx
-const TCHAR* const kRegValueOneClickHostPattern = _T("OneClickHostPattern");
-
-// Disables the Code Red check.
-const TCHAR* const kRegValueNoCodeRedCheck     = _T("NoCrCheck");
-
-// Enables sending usage stats always if the value is present.
-const TCHAR* const kRegValueForceUsageStats    = _T("UsageStats");
-
-// Enables monitoring the 'LastChecked' value for testing purposes. When
-// the 'LastChecked' is deleted, the core starts a worker process to do an
-// update check. This value must be set before the core process starts.
-const TCHAR* const kRegValueMonitorLastChecked = _T("MonitorLastChecked");
-
-// The test_source value to use for googleupdate instances that have
-// customizations, and hence should be discarded from metrics on the dashboard.
-const TCHAR* const kRegValueTestSourceAuto     = _T("auto");
-
-// The network configuration to override the network detection.
-// The corresponding value must have the following format:
-// wpad=[false|true];script=script_url;proxy=host:port
-const TCHAR* const kRegValueNetConfig          = _T("NetConfig");
-
-const int kMaxAppNameLength = 512;
-
-// Specifies whether a tristate item has a value and if so what the value is.
-enum Tristate {
-  TRISTATE_FALSE,
-  TRISTATE_TRUE,
-  TRISTATE_NONE
-};
-
-// Number of periods to use when abbreviating URLs
-#define kAbbreviationPeriodLength 3
-
-// The Unicode "Byte Order Marker" character.  This is the native
-// encoding, i.e. after conversion from UTF-8 or whatever.
-const wchar_t kUnicodeBom = 0xFEFF;
-
-// Using these constants will make ATL load the
-// typelib directly from a DLL instead of looking up typelib
-// registration in registry.
-const DWORD kMajorTypeLibVersion = 0xFFFF;
-const DWORD kMinorTypeLibVersion = 0xFFFF;
-
-// Brand id length
-const int kBrandIdLength = 4;
-
-// Country code length according to ISO 3166-3.
-const int kCountryCodeMaxLength = 5;
-
-// Language code length.
-const int kLangMaxLength = 10;
-
-// When not specified, the country code defaults to USA
-const TCHAR* const kDefaultCountryCode = _T("us");
-
-// the max length of the extra info we can store inside the install stubs.
-const int kExtraMaxLength = 64 * 1024;  // 64 KB
-
-// The value that is used in the run key.
-const TCHAR* const kRunValueName = _T("Google Update");
-
-// Default brand code value when one is not specified.
-// This has been specifically assigned to Omaha.
-const TCHAR* const kDefaultGoogleUpdateBrandCode = _T("GGLS");
-
-// The platform named used for Windows.
-const TCHAR* const kPlatformWin = _T("win");
-
-// The following are response strings returned by the server.
-// They must exactly match the strings returned by the server.
-const TCHAR* const kResponseStatusOkValue = _T("ok");
-const TCHAR* const kResponseStatusNoUpdate = _T("noupdate");
-const TCHAR* const kResponseStatusRestrictedExportCountry = _T("restricted");
-const TCHAR* const kResponseStatusOsNotSupported = _T("error-osnotsupported");
-const TCHAR* const kResponseStatusUnKnownApplication =
-    _T("error-UnKnownApplication");
-const TCHAR* const kResponseStatusHashError = _T("error-hash");
-const TCHAR* const kResponseStatusInternalError = _T("error-internal");
-const TCHAR* const kResponseStatusUnsupportedProtocol =
-    _T("error-unsupportedprotocol");
-
-const TCHAR* const kLocalSystemSid = _T("S-1-5-18");
-
-// Defines LastCheckPeriodSec: the time interval between actual server
-// update checks. Opt builds have an aggressive check for updates every 5 hours.
-// This introduces some time shift for computers connected all the time, for
-// example, the update checks occur at: 12, 17, 22, 3, 8, 13, 18, etc...
-const int kMsPerSec           = 1000;
-const int kMinPerHour         = 60;
-const int kSecondsPerHour     = 60 * 60;
-const int kSecondsPerDay      = 24 * kSecondsPerHour;
-
-// Since time computation for LastChecked is done in seconds, sometimes it
-// can miss an update check, depending on arithmetic truncations.
-// Adjust down the LastCheckPeriod so that the update worker does not miss it.
-//
-// Almost 5 hours for production users and almost hourly for Googlers.
-const int kLastCheckPeriodSec         = 5 * 59 * kMinPerHour;
-const int kLastCheckPeriodGooglerSec  = 1 * 59 * kMinPerHour;
-
-
-const int kMinLastCheckPeriodSec = 60;  // 60 seconds minimum.
-
-// Defines the time interval when the core is kicking off silent workers. When
-// there is nothing to do, a worker does not take more than 200 ms to run.
-// Googlers are supposed to update at least once every hour. Therefore, start
-// workers every 30 minutes.
-const int kAUCheckPeriodMs        = 60 * 60 * 1000;   // Hourly.
-const int kAUCheckPeriodGooglerMs = 30 * 60 * 1000;   // 30 minutes.
-
-// Avoids starting workers too soon. This helps reduce disk thrashing at
-// boot or logon, as well as needlessly starting a worker after setting up.
-const int kUpdateTimerStartupDelayMinMs = 5 * 60 * 1000;   // 5 minutes.
-
-// Maximum amount of time to wait before starting an update worker.
-const int kUpdateTimerStartupDelayMaxMs = 15 * 60 * 1000;   // 15 minutes.
-
-// Minimum AU check interval is lowered to 3 seconds to speed up test
-// automation.
-const int kMinAUCheckPeriodMs = 1000 * 3;   // 3 seconds.
-
-// The Code Red check frequency.
-const int kCodeRedCheckPeriodMs     = 24 * 60 * 60 * 1000;    // 24 hours.
-const int kMinCodeRedCheckPeriodMs  = 60 * 1000;              // 1 minute.
-
-// The minimum amount of time after a /oem install that Google Update is
-// considered to be in OEM mode regardless of audit mode.
-const int kMinOemModeSec = 72 * 60 * 60;  // 72 hours.
-
-// The amount of time to wait for the setup lock before giving up.
-const int kSetupLockWaitMs = 1000;  // 1 second.
-
-// The amount of time to wait for other instances to shutdown before giving up.
-// There is no UI during this time, so the interactive value cannot be too long.
-// Even the silent value cannot be too long because Setup Lock is being held.
-const int kSetupShutdownWaitMsInteractiveNoUi = 2000;   // 2 seconds.
-const int kSetupShutdownWaitMsSilent          = 15000;  // 15 seconds.
-
-// Time to wait for the busy MSI when uninstalling. If MSI is found busy,
-// Omaha won't uninstall. The timeout should be high enough so that it allows
-// a normal application uninstall to finish and trigger an Omaha uninstall
-// when needed.
-const int kWaitForMSIExecuteMs                = 5 * 60000;  // 5 minutes.
-
-// Name of the language key-value pair inside the version resource.
-const TCHAR* const kLanguageVersionName = _T("LanguageId");
-
-// These must be in sync with the WiX files.
-const TCHAR* const kHelperInstallerName = _T("GoogleUpdateHelper.msi");
-const TCHAR* const kHelperInstallerProductGuid =
-    _T("{A92DAB39-4E2C-4304-9AB6-BC44E68B55E2}");
-const TCHAR* const kHelperPatchName = _T("GoogleUpdateHelperPatch.msp");
-const TCHAR* const kHelperPatchGuid =
-    _T("{E0D0D2C9-5836-4023-AB1D-54EC3B90AD03}");
-
-// Group name to use to read/write INI files for custom crash client info.
-const TCHAR* const kCustomClientInfoGroup = _T("ClientCustomData");
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_CONSTANTS_H__
diff --git a/common/controlled_object.h b/common/controlled_object.h
new file mode 100644
index 0000000..26431ae
--- /dev/null
+++ b/common/controlled_object.h
@@ -0,0 +1,80 @@
+// Copyright 2005-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// This class is similar to CComContainedObject. The primary difference is with
+// respect to QueryInterface, which this class handles itself, as opposed to
+// CComContainedObject that delegates that to the outer unknown.
+
+#ifndef OMAHA_COMMON_CONTROLLED_OBJECT_H_
+#define OMAHA_COMMON_CONTROLLED_OBJECT_H_
+
+#include <atlcom.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_ptr_address.h"
+
+namespace omaha {
+
+template <class Base>
+class ControlledObject : public CComContainedObject<Base> {
+ public:
+  typedef CComContainedObject<Base> BaseClass;
+  typedef ControlledObject<Base> ControlledObj;
+
+  // The base class CComContainedObject stores pv, and delegates to this
+  // controlling unknown subsequent calls to the lifetime methods
+  // AddRef/Release.
+  explicit ControlledObject(void* pv) : BaseClass(pv) {}
+  virtual ~ControlledObject() {}
+
+  STDMETHOD(QueryInterface)(REFIID iid, void** ppv) throw() {
+    return _InternalQueryInterface(iid, ppv);
+  }
+
+  // TODO(omaha): ASSERT on controlling_unknown. The unit tests need to be
+  // fixed for this.
+  static HRESULT WINAPI CreateInstance(IUnknown* controlling_unknown,
+                                       ControlledObj** pp) throw() {
+    ASSERT1(pp);
+    if (!controlling_unknown) {
+      CORE_LOG(LW, (_T("[CreateInstance - controlling_unknown is NULL]")));
+    }
+
+    *pp = NULL;
+    scoped_ptr<ControlledObj> p(new ControlledObj(controlling_unknown));
+    if (!p.get()) {
+      return E_OUTOFMEMORY;
+    }
+
+    HRESULT hr = p->FinalConstruct();
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    *pp = p.release();
+    return S_OK;
+  }
+
+  template <class Q>
+  HRESULT STDMETHODCALLTYPE QueryInterface(Q** pp) throw() {
+    return QueryInterface(__uuidof(Q), reinterpret_cast<void**>(pp));
+  }
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_CONTROLLED_OBJECT_H_
+
diff --git a/common/crc.cc b/common/crc.cc
deleted file mode 100644
index 94341f0..0000000
--- a/common/crc.cc
+++ /dev/null
@@ -1,898 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Implemetation of CRCs (aka Rabin Fingerprints).
-// Treats the input as a polynomial with coefficients in Z(2),
-// and finds the remainder when divided by an irreducible polynomial
-// of the appropriate length.
-// It handles all CRC sizes from 8 to 128 bits.
-// It's somewhat complicated by having separate implementations optimized for
-// CRC's <=32 bits, <= 64 bits, and <= 128 bits.
-// The input string is prefixed with a "1" bit, and has "degree" "0" bits
-// appended to it before the remainder is found.   This ensures that
-// short strings are scrambled somewhat and that strings consisting
-// of all nulls have a non-zero CRC.
-
-#include <stddef.h>
-#include "omaha/common/crc.h"
-#include "omaha/common/debug.h"
-
-namespace omaha {
-
-static const int SMALL_BITS = 8;
-                   // When extending an input with a string of zeroes,
-                   // if the number of zeroes is less than 2**SMALL_BITS,
-                   // a normal Extend is done, rather than a polynomial
-                   // multiplication.
-static const char zeroes[1 << SMALL_BITS] = { 0 };  // an array of zeroes
-
-static const uint8 *zero_ptr = 0;   // The 0 pointer---used for alignment
-
-// These are used to index a 2-entry array of words that together
-// for a longer integer.  LO indexes the low-order half.
-#define LO 0
-#define HI (1-LO)
-
-// Constructor and destructor for baseclase CRC.
-CRC::~CRC() {}
-CRC::CRC() {}
-
-struct CRC_pair {             // Used to represent a 128-bit value
-  uint64 lo;
-  uint64 hi;
-};
-
-class CRCImpl : public CRC {    // Implemention of the abstract class CRC
- public:
-  CRCImpl() {}
-  virtual ~CRCImpl() {}
-
-  // The internal version of CRC::New().
-  static CRCImpl *NewInternal(uint64 lo, uint64 hi,
-                              int degree, size_t roll_length);
-
-  virtual void Empty(uint64 *lo, uint64 *hi) const;
-
-  size_t roll_length_;    // length of window in rolling CRC
-  int degree_;            // bits in the CRC
-  uint64 poly_lo_;        // The CRC of the empty string, low part
-  uint64 poly_hi_;        // The CRC of the empty string, high part
-
- private:
-  DISALLOW_EVIL_CONSTRUCTORS(CRCImpl);
-};
-
-// This is the 32-bit implementation.  It handles all sizes from 8 to 32.
-class CRC32 : public CRCImpl {
- public:
-  CRC32() {}
-  virtual ~CRC32() {}
-
-  virtual void Extend(uint64 *lo, uint64 *hi,
-                      const void *bytes, size_t length) const;
-  virtual void ExtendByZeroes(uint64 *lo, uint64 *hi, size_t length) const;
-  virtual void Roll(uint64 *lo, uint64 *hi, uint8 o_byte, uint8 i_byte) const;
-
-  uint32 table0_[256];  // table of byte extensions
-  uint32 table1_[256];  // table of byte extensions, shifted by 1 byte
-  uint32 table2_[256];  // table of byte extensions, shifted by 2 bytes
-  uint32 table3_[256];  // table of byte extensions, shifted by 3 bytes
-  uint32 roll_[256];    // table of byte roll values
-  uint32 zeroes_[256];  // table of zero extensions
-
- private:
-  DISALLOW_EVIL_CONSTRUCTORS(CRC32);
-};
-
-static const uint64 UINT64_ZERO = 0;    // a 64-bit zero
-static const uint64 UINT64_ONE = 1;     // a 64-bit 1
-
-// The B() macro sets the bit corresponding to X**(_x) in the polynomial
-#define B(_x) (UINT64_ONE << ((_x) < 64?(63-(_x)):(127-(_x))))
-
-// Used to initialize polynomials.
-// The redundant tests on _len are to avoid warnings from the
-// compiler about inappropriate shift lengths.   These shifts
-// occur on not-taken branch of the ?: in some cases.
-#define kDefPoly(_h,_l,_len) \
- { ((_len) <= 64  ? (_l) >> ((_len) <= 64? 64 - (_len): 0) : \
-    (_len) == 128 ? (_h) : \
-                     ((_h) >> ((_len) > 64? 128 - (_len): 0)) | \
-                     ((_l) << ((_len) > 64 && (_len) < 128? (_len)-64: 0))), \
-   ((_len) <= 64  ? 0 : \
-    (_len) == 128 ? (_l) :  \
-                    (_l) >> ((_len) > 64? 128 - (_len): 0)), \
-   (_len) }
-
-// A table of irreducible polynomials suitable for use with the implementation.
-// Indexes 0...1 have degree 32 polynomials.
-// Indexes 2...3 have degree 64 polynomials.
-// Indexes 4...5 have degree 96 polynomials.
-// Indexes 6...7 have degree 128 polynomials.
-// Index i=8...128 has a degree i polynomial.
-// All polynomials in the table are guaranteed distinct.
-// lint -save -e572 -e648 -e778   Excessive shift value, expression evaluates to 0
-static const struct CRC::Poly poly_list[] = {
- kDefPoly(UINT64_ZERO, B(30)+B(27)+B(26)+B(25)+B(23)+B(20)+B(17)+B(15)+B(14)+
-          B(12)+B(6)+B(5)+B(2)+B(0), 32),
- kDefPoly(UINT64_ZERO, B(31)+B(28)+B(27)+B(26)+B(24)+B(22)+B(19)+B(18)+B(16)+
-          B(13)+B(11)+B(10)+B(9)+B(4)+B(2)+B(0), 32),
- kDefPoly(UINT64_ZERO, B(60)+B(59)+B(58)+B(56)+B(55)+B(54)+B(51)+B(50)+B(49)+
-          B(48)+B(47)+B(45)+B(44)+B(42)+B(40)+B(39)+B(38)+B(36)+B(34)+B(33)+
-          B(32)+B(31)+B(30)+B(27)+B(25)+B(23)+B(22)+B(21)+B(20)+B(19)+
-          B(17)+B(16)+B(15)+B(8)+B(7)+B(6)+B(5)+B(0), 64),
- kDefPoly(UINT64_ZERO, B(63)+B(62)+B(60)+B(58)+B(57)+B(56)+B(54)+B(52)+B(46)+
-          B(45)+B(43)+B(40)+B(37)+B(36)+B(34)+B(33)+B(32)+B(31)+B(30)+B(29)+
-          B(28)+B(27)+B(26)+B(23)+B(19)+B(18)+B(15)+B(14)+B(13)+B(9)+B(8)+
-          B(0), 64),
- kDefPoly(B(95)+B(94)+B(91)+B(90)+B(89)+B(88)+B(87)+B(86)+B(79)+B(78)+
-          B(77)+B(76)+B(75)+B(74)+B(73)+B(69)+B(68)+B(66), B(63)+B(61)+
-          B(59)+B(57)+B(53)+B(51)+B(50)+B(47)+B(40)+B(39)+B(38)+B(36)+
-          B(35)+B(33)+B(29)+B(28)+B(27)+B(25)+B(24)+B(23)+B(21)+B(19)+
-          B(18)+B(17)+B(16)+B(13)+B(12)+B(10)+B(9)+B(7)+B(4)+B(2)+B(1)+
-          B(0), 96),
- kDefPoly(B(95)+B(92)+B(89)+B(88)+B(87)+B(85)+B(84)+B(82)+B(81)+B(80)+
-          B(79)+B(78)+B(76)+B(75)+B(70)+B(69)+B(66)+B(65), B(60)+B(56)+
-          B(55)+B(52)+B(51)+B(49)+B(48)+B(46)+B(44)+B(42)+B(41)+B(39)+
-          B(38)+B(37)+B(35)+B(33)+B(32)+B(30)+B(28)+B(27)+B(25)+B(22)+
-          B(19)+B(17)+B(14)+B(12)+B(10)+B(0), 96),
- kDefPoly(B(122)+B(121)+B(120)+B(119)+B(117)+B(116)+B(114)+B(113)+B(112)+
-          B(111)+B(109)+B(107)+B(104)+B(102)+B(100)+B(98)+B(96)+B(94)+
-          B(93)+B(92)+B(91)+B(90)+B(88)+B(87)+B(86)+B(84)+B(82)+B(80)+
-          B(75)+B(74)+B(73)+B(69), B(62)+B(61)+B(58)+B(52)+B(48)+B(47)+
-          B(46)+B(45)+B(42)+B(41)+B(38)+B(37)+B(35)+B(33)+B(32)+B(31)+
-          B(30)+B(28)+B(26)+B(24)+B(22)+B(21)+B(20)+B(19)+B(18)+B(17)+
-          B(10)+B(9)+B(8)+B(7)+B(5)+B(2)+B(1)+B(0), 128),
- kDefPoly(B(127)+B(126)+B(124)+B(121)+B(117)+B(116)+B(115)+B(113)+B(112)+
-          B(111)+B(108)+B(105)+B(104)+B(103)+B(100)+B(98)+B(96)+B(93)+
-          B(92)+B(90)+B(89)+B(88)+B(86)+B(85)+B(80)+B(77)+B(76)+B(72)+
-          B(70)+B(69)+B(68)+B(65)+B(64), B(62)+B(61)+B(59)+B(58)+B(56)+
-          B(53)+B(52)+B(51)+B(50)+B(48)+B(46)+B(39)+B(35)+B(34)+B(33)+
-          B(32)+B(30)+B(29)+B(28)+B(22)+B(21)+B(19)+B(18)+B(17)+B(14)+
-          B(10)+B(9)+B(7)+B(5)+B(4)+B(3)+B(2)+B(0), 128),
- kDefPoly(UINT64_ZERO, B(7)+B(6)+B(5)+B(4)+B(2)+B(0), 8),
- kDefPoly(UINT64_ZERO, B(8)+B(4)+B(3)+B(2)+B(1)+B(0), 9),
- kDefPoly(UINT64_ZERO, B(8)+B(6)+B(5)+B(3)+B(1)+B(0), 10),
- kDefPoly(UINT64_ZERO, B(10)+B(9)+B(7)+B(5)+B(1)+B(0), 11),
- kDefPoly(UINT64_ZERO, B(11)+B(10)+B(5)+B(2)+B(1)+B(0), 12),
- kDefPoly(UINT64_ZERO, B(12)+B(11)+B(10)+B(8)+B(5)+B(3)+B(2)+B(0), 13),
- kDefPoly(UINT64_ZERO, B(11)+B(10)+B(9)+B(8)+B(7)+B(5)+B(4)+B(2)+B(1)+
-          B(0), 14),
- kDefPoly(UINT64_ZERO, B(14)+B(12)+B(11)+B(10)+B(9)+B(5)+B(3)+B(2)+B(1)+
-          B(0), 15),
- kDefPoly(UINT64_ZERO, B(12)+B(11)+B(7)+B(6)+B(5)+B(4)+B(3)+B(0), 16),
- kDefPoly(UINT64_ZERO, B(16)+B(14)+B(11)+B(10)+B(8)+B(3)+B(1)+B(0), 17),
- kDefPoly(UINT64_ZERO, B(12)+B(11)+B(9)+B(8)+B(7)+B(4)+B(3)+B(0), 18),
- kDefPoly(UINT64_ZERO, B(12)+B(11)+B(8)+B(7)+B(6)+B(5)+B(2)+B(0), 19),
- kDefPoly(UINT64_ZERO, B(18)+B(15)+B(14)+B(12)+B(9)+B(6)+B(3)+B(0), 20),
- kDefPoly(UINT64_ZERO, B(20)+B(19)+B(14)+B(13)+B(12)+B(11)+B(8)+B(7)+B(6)+
-          B(5)+B(2)+B(0), 21),
- kDefPoly(UINT64_ZERO, B(21)+B(20)+B(18)+B(16)+B(15)+B(14)+B(12)+B(9)+B(7)+
-          B(2)+B(1)+B(0), 22),
- kDefPoly(UINT64_ZERO, B(22)+B(21)+B(17)+B(16)+B(15)+B(14)+B(12)+B(10)+B(7)+
-          B(4)+B(1)+B(0), 23),
- kDefPoly(UINT64_ZERO, B(23)+B(22)+B(21)+B(18)+B(17)+B(15)+B(14)+B(12)+B(4)+
-          B(0), 24),
- kDefPoly(UINT64_ZERO, B(24)+B(23)+B(22)+B(20)+B(18)+B(17)+B(14)+B(13)+B(9)+
-          B(0), 25),
- kDefPoly(UINT64_ZERO, B(25)+B(22)+B(21)+B(19)+B(17)+B(15)+B(14)+B(12)+B(11)+
-          B(10)+B(6)+B(4)+B(3)+B(0), 26),
- kDefPoly(UINT64_ZERO, B(26)+B(25)+B(19)+B(17)+B(16)+B(13)+B(5)+B(4)+B(1)+
-          B(0), 27),
- kDefPoly(UINT64_ZERO, B(23)+B(22)+B(21)+B(20)+B(19)+B(18)+B(13)+B(12)+B(10)+
-          B(9)+B(8)+B(6)+B(5)+B(3)+B(1)+B(0), 28),
- kDefPoly(UINT64_ZERO, B(27)+B(26)+B(25)+B(23)+B(22)+B(20)+B(19)+B(15)+B(14)+
-          B(11)+B(10)+B(8)+B(7)+B(6)+B(4)+B(0), 29),
- kDefPoly(UINT64_ZERO, B(29)+B(27)+B(25)+B(23)+B(20)+B(19)+B(18)+B(17)+B(16)+
-          B(14)+B(11)+B(10)+B(9)+B(7)+B(6)+B(5)+B(4)+B(0), 30),
- kDefPoly(UINT64_ZERO, B(30)+B(29)+B(28)+B(27)+B(25)+B(23)+B(22)+B(21)+B(20)+
-          B(19)+B(18)+B(16)+B(15)+B(10)+B(9)+B(8)+B(4)+B(3)+B(1)+B(0), 31),
- kDefPoly(UINT64_ZERO, B(31)+B(29)+B(28)+B(27)+B(21)+B(20)+B(15)+B(13)+B(10)+
-          B(9)+B(8)+B(7)+B(4)+B(3)+B(2)+B(0), 32),
- kDefPoly(UINT64_ZERO, B(32)+B(31)+B(30)+B(29)+B(27)+B(25)+B(24)+B(22)+B(21)+
-          B(19)+B(15)+B(10)+B(4)+B(3)+B(2)+B(0), 33),
- kDefPoly(UINT64_ZERO, B(30)+B(27)+B(26)+B(25)+B(24)+B(20)+B(19)+B(18)+B(16)+
-          B(15)+B(14)+B(12)+B(9)+B(8)+B(7)+B(5)+B(1)+B(0), 34),
- kDefPoly(UINT64_ZERO, B(34)+B(32)+B(28)+B(27)+B(26)+B(22)+B(21)+B(20)+B(19)+
-          B(14)+B(13)+B(12)+B(10)+B(6)+B(5)+B(4)+B(3)+B(0), 35),
- kDefPoly(UINT64_ZERO, B(35)+B(34)+B(33)+B(32)+B(31)+B(28)+B(26)+B(24)+B(22)+
-          B(21)+B(20)+B(19)+B(18)+B(14)+B(13)+B(12)+B(10)+B(9)+B(8)+B(6)+B(5)+
-          B(4)+B(3)+B(0), 36),
- kDefPoly(UINT64_ZERO, B(36)+B(35)+B(31)+B(30)+B(28)+B(26)+B(25)+B(23)+B(22)+
-          B(20)+B(19)+B(18)+B(16)+B(13)+B(12)+B(7)+B(6)+B(4)+B(3)+B(0), 37),
- kDefPoly(UINT64_ZERO, B(37)+B(34)+B(33)+B(32)+B(31)+B(30)+B(29)+B(28)+B(27)+
-          B(26)+B(25)+B(23)+B(18)+B(16)+B(15)+B(13)+B(12)+B(11)+B(10)+B(9)+
-          B(8)+B(7)+B(6)+B(2)+B(1)+B(0), 38),
- kDefPoly(UINT64_ZERO, B(38)+B(37)+B(33)+B(32)+B(31)+B(27)+B(25)+B(24)+B(21)+
-          B(20)+B(19)+B(18)+B(17)+B(15)+B(14)+B(8)+B(7)+B(6)+B(3)+B(2)+B(1)+
-          B(0), 39),
- kDefPoly(UINT64_ZERO, B(38)+B(37)+B(35)+B(34)+B(32)+B(31)+B(30)+B(27)+B(24)+
-          B(21)+B(20)+B(14)+B(13)+B(11)+B(8)+B(4)+B(2)+B(0), 40),
- kDefPoly(UINT64_ZERO, B(38)+B(36)+B(35)+B(34)+B(33)+B(31)+B(30)+B(29)+B(28)+
-          B(27)+B(23)+B(22)+B(20)+B(19)+B(18)+B(17)+B(15)+B(14)+B(11)+B(5)+
-          B(4)+B(0), 41),
- kDefPoly(UINT64_ZERO, B(41)+B(37)+B(36)+B(35)+B(32)+B(31)+B(30)+B(29)+B(28)+
-          B(25)+B(19)+B(18)+B(14)+B(13)+B(12)+B(7)+B(6)+B(4)+B(2)+B(0), 42),
- kDefPoly(UINT64_ZERO, B(42)+B(40)+B(38)+B(37)+B(36)+B(35)+B(34)+B(33)+B(31)+
-          B(29)+B(27)+B(26)+B(25)+B(23)+B(21)+B(20)+B(19)+B(15)+B(11)+B(10)+
-          B(9)+B(8)+B(6)+B(5)+B(3)+B(0), 43),
- kDefPoly(UINT64_ZERO, B(43)+B(42)+B(40)+B(39)+B(37)+B(35)+B(32)+B(30)+B(26)+
-          B(25)+B(24)+B(20)+B(16)+B(13)+B(12)+B(11)+B(8)+B(6)+B(5)+B(4)+B(1)+
-          B(0), 44),
- kDefPoly(UINT64_ZERO, B(43)+B(42)+B(41)+B(40)+B(39)+B(38)+B(33)+B(32)+B(27)+
-          B(26)+B(25)+B(23)+B(20)+B(18)+B(17)+B(16)+B(14)+B(11)+B(10)+B(9)+
-          B(6)+B(5)+B(1)+B(0), 45),
- kDefPoly(UINT64_ZERO, B(45)+B(43)+B(42)+B(41)+B(40)+B(39)+B(32)+B(31)+B(30)+
-          B(29)+B(27)+B(25)+B(23)+B(18)+B(17)+B(16)+B(10)+B(9)+B(7)+B(6)+B(4)+
-          B(3)+B(2)+B(0), 46),
- kDefPoly(UINT64_ZERO, B(45)+B(44)+B(43)+B(41)+B(40)+B(39)+B(38)+B(37)+B(32)+
-          B(30)+B(23)+B(21)+B(20)+B(17)+B(15)+B(13)+B(11)+B(10)+B(7)+B(5)+
-          B(3)+B(0), 47),
- kDefPoly(UINT64_ZERO, B(46)+B(42)+B(41)+B(39)+B(37)+B(36)+B(35)+B(29)+B(28)+
-          B(25)+B(24)+B(21)+B(20)+B(18)+B(17)+B(13)+B(12)+B(11)+B(10)+B(9)+
-          B(8)+B(5)+B(1)+B(0), 48),
- kDefPoly(UINT64_ZERO, B(48)+B(44)+B(41)+B(40)+B(39)+B(38)+B(37)+B(36)+B(35)+
-          B(34)+B(30)+B(28)+B(27)+B(24)+B(21)+B(18)+B(17)+B(8)+B(3)+B(0), 49),
- kDefPoly(UINT64_ZERO, B(48)+B(47)+B(46)+B(45)+B(44)+B(43)+B(42)+B(35)+B(33)+
-          B(29)+B(26)+B(24)+B(23)+B(21)+B(18)+B(16)+B(14)+B(13)+B(12)+B(9)+
-          B(7)+B(6)+B(5)+B(4)+B(3)+B(0), 50),
- kDefPoly(UINT64_ZERO, B(47)+B(46)+B(45)+B(44)+B(43)+B(40)+B(39)+B(38)+B(36)+
-          B(35)+B(30)+B(29)+B(28)+B(26)+B(25)+B(24)+B(23)+B(22)+B(20)+B(19)+
-          B(18)+B(17)+B(15)+B(11)+B(7)+B(4)+B(3)+B(0), 51),
- kDefPoly(UINT64_ZERO, B(51)+B(46)+B(43)+B(38)+B(37)+B(36)+B(34)+B(31)+B(27)+
-          B(26)+B(20)+B(17)+B(16)+B(15)+B(13)+B(12)+B(11)+B(9)+B(7)+B(5)+B(1)+
-          B(0), 52),
- kDefPoly(UINT64_ZERO, B(50)+B(49)+B(47)+B(46)+B(44)+B(42)+B(41)+B(37)+B(36)+
-          B(35)+B(33)+B(29)+B(28)+B(26)+B(24)+B(23)+B(21)+B(20)+B(14)+B(13)+
-          B(12)+B(11)+B(10)+B(9)+B(8)+B(6)+B(3)+B(2)+B(1)+B(0), 53),
- kDefPoly(UINT64_ZERO, B(52)+B(47)+B(46)+B(44)+B(43)+B(42)+B(40)+B(36)+B(32)+
-          B(31)+B(30)+B(29)+B(28)+B(26)+B(25)+B(24)+B(23)+B(22)+B(20)+B(19)+
-          B(17)+B(16)+B(15)+B(14)+B(13)+B(12)+B(11)+B(10)+B(7)+B(4)+B(2)+
-          B(0), 54),
- kDefPoly(UINT64_ZERO, B(53)+B(50)+B(48)+B(47)+B(37)+B(35)+B(31)+B(30)+B(25)+
-          B(22)+B(21)+B(20)+B(19)+B(18)+B(15)+B(10)+B(8)+B(6)+B(3)+B(2)+B(1)+
-          B(0), 55),
- kDefPoly(UINT64_ZERO, B(54)+B(52)+B(51)+B(49)+B(48)+B(42)+B(38)+B(37)+B(31)+
-          B(30)+B(27)+B(26)+B(24)+B(23)+B(22)+B(19)+B(16)+B(12)+B(11)+B(8)+
-          B(6)+B(4)+B(3)+B(0), 56),
- kDefPoly(UINT64_ZERO, B(55)+B(54)+B(51)+B(49)+B(48)+B(47)+B(46)+B(44)+B(43)+
-          B(42)+B(41)+B(40)+B(39)+B(38)+B(32)+B(29)+B(27)+B(26)+B(23)+B(21)+
-          B(20)+B(15)+B(12)+B(7)+B(6)+B(5)+B(3)+B(0), 57),
- kDefPoly(UINT64_ZERO, B(57)+B(54)+B(52)+B(47)+B(45)+B(42)+B(41)+B(40)+B(39)+
-          B(36)+B(34)+B(33)+B(31)+B(28)+B(26)+B(21)+B(20)+B(18)+B(17)+B(16)+
-          B(13)+B(11)+B(8)+B(7)+B(4)+B(2)+B(1)+B(0), 58),
- kDefPoly(UINT64_ZERO, B(58)+B(56)+B(54)+B(49)+B(47)+B(46)+B(43)+B(40)+B(38)+
-          B(36)+B(35)+B(33)+B(32)+B(31)+B(30)+B(27)+B(24)+B(22)+B(21)+B(19)+
-          B(17)+B(16)+B(11)+B(10)+B(9)+B(8)+B(7)+B(4)+B(3)+B(2)+B(1)+B(0),
-          59),
- kDefPoly(UINT64_ZERO, B(56)+B(54)+B(51)+B(46)+B(43)+B(42)+B(40)+B(39)+B(37)+
-          B(35)+B(34)+B(33)+B(32)+B(31)+B(30)+B(29)+B(27)+B(25)+B(22)+B(21)+
-          B(20)+B(19)+B(17)+B(16)+B(15)+B(14)+B(13)+B(12)+B(9)+B(7)+B(4)+
-          B(3)+B(1)+B(0), 60),
- kDefPoly(UINT64_ZERO, B(59)+B(58)+B(57)+B(56)+B(54)+B(53)+B(50)+B(49)+B(47)+
-          B(44)+B(42)+B(41)+B(40)+B(37)+B(35)+B(34)+B(32)+B(30)+B(29)+B(27)+
-          B(26)+B(22)+B(21)+B(20)+B(17)+B(14)+B(13)+B(12)+B(8)+B(5)+B(4)+
-          B(0), 61),
- kDefPoly(UINT64_ZERO, B(61)+B(59)+B(57)+B(55)+B(54)+B(53)+B(52)+B(51)+B(50)+
-          B(49)+B(48)+B(45)+B(44)+B(40)+B(37)+B(35)+B(32)+B(31)+B(29)+B(25)+
-          B(24)+B(23)+B(20)+B(17)+B(16)+B(15)+B(13)+B(12)+B(11)+B(10)+B(6)+
-          B(5)+B(2)+B(0), 62),
- kDefPoly(UINT64_ZERO, B(62)+B(57)+B(56)+B(53)+B(52)+B(51)+B(50)+B(46)+B(41)+
-          B(38)+B(35)+B(34)+B(33)+B(31)+B(27)+B(25)+B(23)+B(21)+B(19)+B(18)+
-          B(17)+B(16)+B(13)+B(11)+B(7)+B(5)+B(1)+B(0), 63),
- kDefPoly(UINT64_ZERO, B(62)+B(61)+B(60)+B(57)+B(55)+B(54)+B(53)+B(49)+B(48)+
-          B(46)+B(44)+B(42)+B(40)+B(39)+B(37)+B(36)+B(28)+B(27)+B(25)+B(23)+
-          B(22)+B(21)+B(17)+B(15)+B(13)+B(7)+B(6)+B(4)+B(2)+B(0), 64),
- kDefPoly(UINT64_ZERO, B(63)+B(62)+B(59)+B(57)+B(54)+B(53)+B(51)+B(48)+
-          B(47)+B(46)+B(45)+B(44)+B(41)+B(40)+B(38)+B(36)+B(35)+B(28)+
-          B(25)+B(24)+B(21)+B(20)+B(18)+B(16)+B(15)+B(13)+B(11)+B(8)+B(7)+
-          B(3)+B(1)+B(0), 65),
- kDefPoly(UINT64_ZERO, B(63)+B(58)+B(57)+B(56)+B(52)+B(51)+B(50)+B(44)+
-          B(41)+B(40)+B(36)+B(34)+B(32)+B(31)+B(27)+B(25)+B(23)+B(21)+
-          B(20)+B(19)+B(18)+B(17)+B(15)+B(14)+B(12)+B(11)+B(10)+B(8)+B(5)+
-          B(4)+B(3)+B(0), 66),
- kDefPoly(B(66), B(62)+B(60)+B(59)+B(58)+B(57)+B(56)+B(55)+B(54)+B(52)+
-          B(50)+B(47)+B(46)+B(45)+B(43)+B(42)+B(41)+B(38)+B(37)+B(36)+
-          B(33)+B(32)+B(31)+B(30)+B(28)+B(27)+B(26)+B(24)+B(21)+B(18)+
-          B(17)+B(14)+B(13)+B(12)+B(11)+B(10)+B(7)+B(4)+B(3)+B(0), 67),
- kDefPoly(B(67)+B(66), B(63)+B(61)+B(57)+B(55)+B(51)+B(47)+B(45)+B(43)+
-          B(42)+B(41)+B(40)+B(39)+B(32)+B(31)+B(30)+B(28)+B(27)+B(25)+
-          B(19)+B(18)+B(17)+B(15)+B(11)+B(9)+B(8)+B(7)+B(6)+B(5)+B(4)+B(3)+
-          B(1)+B(0), 68),
- kDefPoly(B(68), B(60)+B(57)+B(55)+B(54)+B(52)+B(50)+B(49)+B(48)+B(44)+
-          B(40)+B(38)+B(37)+B(33)+B(31)+B(28)+B(25)+B(22)+B(21)+B(20)+
-          B(19)+B(18)+B(17)+B(13)+B(12)+B(9)+B(8)+B(6)+B(5)+B(4)+B(1)+
-          B(0), 69),
- kDefPoly(B(69)+B(68)+B(67)+B(66), B(63)+B(62)+B(61)+B(59)+B(51)+B(49)+
-          B(48)+B(46)+B(45)+B(42)+B(40)+B(38)+B(36)+B(35)+B(33)+B(32)+
-          B(30)+B(29)+B(27)+B(23)+B(22)+B(21)+B(16)+B(12)+B(5)+B(4)+B(1)+
-          B(0), 70),
- kDefPoly(B(70)+B(69)+B(68)+B(64), B(63)+B(62)+B(61)+B(60)+B(59)+B(57)+
-          B(56)+B(55)+B(54)+B(53)+B(51)+B(50)+B(47)+B(44)+B(43)+B(41)+
-          B(39)+B(37)+B(36)+B(33)+B(32)+B(26)+B(25)+B(24)+B(23)+B(21)+
-          B(20)+B(19)+B(17)+B(12)+B(11)+B(10)+B(8)+B(6)+B(5)+B(4)+B(2)+
-          B(0), 71),
- kDefPoly(B(71)+B(69)+B(68)+B(65)+B(64), B(62)+B(61)+B(59)+B(58)+B(55)+
-          B(53)+B(51)+B(49)+B(48)+B(47)+B(43)+B(40)+B(38)+B(37)+B(36)+
-          B(35)+B(33)+B(32)+B(31)+B(30)+B(29)+B(26)+B(24)+B(19)+B(18)+
-          B(15)+B(13)+B(9)+B(7)+B(6)+B(3)+B(1)+B(0), 72),
- kDefPoly(B(71)+B(70)+B(69)+B(67)+B(65), B(63)+B(62)+B(61)+B(58)+B(57)+
-          B(56)+B(55)+B(52)+B(51)+B(50)+B(49)+B(46)+B(45)+B(44)+B(43)+
-          B(41)+B(37)+B(36)+B(34)+B(33)+B(27)+B(26)+B(25)+B(21)+B(19)+
-          B(18)+B(16)+B(15)+B(14)+B(13)+B(9)+B(8)+B(6)+B(5)+B(2)+B(1)+
-          B(0), 73),
- kDefPoly(B(73)+B(71)+B(70)+B(65)+B(64), B(62)+B(60)+B(55)+B(54)+B(52)+
-          B(50)+B(48)+B(47)+B(46)+B(44)+B(41)+B(40)+B(31)+B(29)+B(28)+
-          B(27)+B(26)+B(24)+B(23)+B(22)+B(20)+B(16)+B(12)+B(9)+B(6)+B(5)+
-          B(4)+B(2)+B(0), 74),
- kDefPoly(B(74)+B(73)+B(72)+B(67)+B(64), B(63)+B(61)+B(60)+B(58)+B(57)+
-          B(56)+B(54)+B(52)+B(51)+B(50)+B(44)+B(43)+B(42)+B(41)+B(40)+
-          B(39)+B(38)+B(36)+B(35)+B(33)+B(32)+B(31)+B(29)+B(28)+B(26)+
-          B(23)+B(21)+B(19)+B(18)+B(16)+B(15)+B(13)+B(12)+B(11)+B(7)+B(6)+
-          B(5)+B(4)+B(3)+B(2)+B(0), 75),
- kDefPoly(B(75)+B(74)+B(71)+B(70)+B(66), B(63)+B(61)+B(59)+B(57)+B(53)+
-          B(50)+B(49)+B(48)+B(44)+B(43)+B(42)+B(37)+B(33)+B(30)+B(27)+
-          B(24)+B(23)+B(20)+B(18)+B(15)+B(12)+B(11)+B(9)+B(7)+B(6)+B(4)+
-          B(3)+B(2)+B(0), 76),
- kDefPoly(B(73)+B(71)+B(70)+B(68)+B(67)+B(66)+B(65), B(63)+B(60)+B(59)+
-          B(58)+B(57)+B(54)+B(49)+B(47)+B(46)+B(45)+B(43)+B(41)+B(38)+
-          B(34)+B(33)+B(31)+B(30)+B(29)+B(27)+B(25)+B(24)+B(21)+B(20)+
-          B(19)+B(16)+B(15)+B(14)+B(13)+B(10)+B(8)+B(6)+B(5)+B(4)+B(2)+
-          B(0), 77),
- kDefPoly(B(77)+B(76)+B(75)+B(74)+B(70)+B(66)+B(65)+B(64), B(63)+B(62)+
-          B(60)+B(58)+B(57)+B(55)+B(52)+B(51)+B(44)+B(41)+B(39)+B(38)+
-          B(35)+B(31)+B(30)+B(29)+B(26)+B(22)+B(21)+B(20)+B(19)+B(15)+
-          B(13)+B(11)+B(6)+B(4)+B(1)+B(0), 78),
- kDefPoly(B(78)+B(76)+B(75)+B(71)+B(68)+B(67)+B(65), B(63)+B(61)+B(60)+
-          B(55)+B(54)+B(51)+B(50)+B(48)+B(44)+B(42)+B(41)+B(40)+B(38)+
-          B(35)+B(34)+B(32)+B(28)+B(26)+B(23)+B(22)+B(19)+B(15)+B(13)+
-          B(12)+B(8)+B(7)+B(5)+B(2)+B(0), 79),
- kDefPoly(B(77)+B(76)+B(75)+B(73)+B(70)+B(66), B(63)+B(61)+B(60)+B(59)+
-          B(56)+B(54)+B(53)+B(52)+B(50)+B(44)+B(43)+B(40)+B(39)+B(38)+
-          B(35)+B(34)+B(33)+B(29)+B(28)+B(27)+B(26)+B(25)+B(24)+B(23)+
-          B(22)+B(21)+B(20)+B(18)+B(16)+B(13)+B(12)+B(11)+B(10)+B(8)+B(7)+
-          B(6)+B(3)+B(2)+B(1)+B(0), 80),
- kDefPoly(B(78)+B(77)+B(76)+B(75)+B(73)+B(71)+B(67)+B(66)+B(65)+
-          B(64), B(61)+B(54)+B(53)+B(52)+B(49)+B(47)+B(44)+B(41)+B(40)+
-          B(35)+B(33)+B(31)+B(30)+B(28)+B(27)+B(26)+B(25)+B(22)+B(21)+
-          B(20)+B(16)+B(15)+B(13)+B(12)+B(11)+B(0), 81),
- kDefPoly(B(81)+B(80)+B(79)+B(77)+B(76)+B(74)+B(73)+B(72)+B(68)+B(67)+
-          B(66)+B(64), B(62)+B(51)+B(50)+B(49)+B(47)+B(46)+B(45)+B(43)+
-          B(41)+B(38)+B(37)+B(34)+B(32)+B(30)+B(27)+B(26)+B(25)+B(24)+
-          B(23)+B(22)+B(20)+B(19)+B(16)+B(15)+B(13)+B(12)+B(9)+B(7)+B(5)+
-          B(4)+B(1)+B(0), 82),
- kDefPoly(B(82)+B(81)+B(79)+B(78)+B(77)+B(75)+B(72)+B(71)+B(69)+B(68)+
-          B(67)+B(66)+B(65)+B(64), B(60)+B(58)+B(57)+B(56)+B(53)+B(52)+
-          B(51)+B(49)+B(48)+B(45)+B(43)+B(41)+B(40)+B(39)+B(38)+B(37)+
-          B(36)+B(35)+B(33)+B(26)+B(24)+B(21)+B(19)+B(16)+B(13)+B(12)+
-          B(11)+B(9)+B(7)+B(5)+B(4)+B(3)+B(1)+B(0), 83),
- kDefPoly(B(79)+B(77)+B(73)+B(72)+B(71)+B(66)+B(64), B(62)+B(61)+B(59)+
-          B(58)+B(57)+B(56)+B(53)+B(52)+B(51)+B(48)+B(47)+B(46)+B(45)+
-          B(43)+B(42)+B(41)+B(38)+B(37)+B(35)+B(33)+B(32)+B(29)+B(24)+
-          B(22)+B(17)+B(16)+B(15)+B(13)+B(11)+B(10)+B(9)+B(7)+B(6)+B(5)+
-          B(0), 84),
- kDefPoly(B(83)+B(78)+B(76)+B(73)+B(70)+B(69)+B(68)+B(67)+B(66)+
-          B(64), B(62)+B(61)+B(60)+B(59)+B(54)+B(51)+B(50)+B(48)+B(47)+
-          B(42)+B(41)+B(40)+B(38)+B(37)+B(36)+B(34)+B(31)+B(30)+B(28)+
-          B(27)+B(26)+B(24)+B(22)+B(21)+B(20)+B(19)+B(18)+B(16)+B(15)+
-          B(14)+B(13)+B(12)+B(10)+B(6)+B(4)+B(0), 85),
- kDefPoly(B(84)+B(77)+B(76)+B(75)+B(71)+B(70)+B(69)+B(67)+B(65), B(63)+
-          B(62)+B(59)+B(58)+B(57)+B(55)+B(53)+B(52)+B(51)+B(48)+B(47)+
-          B(45)+B(43)+B(40)+B(38)+B(36)+B(34)+B(33)+B(31)+B(27)+B(25)+
-          B(24)+B(23)+B(22)+B(19)+B(15)+B(13)+B(12)+B(11)+B(8)+B(6)+B(4)+
-          B(0), 86),
- kDefPoly(B(85)+B(84)+B(83)+B(81)+B(80)+B(78)+B(73)+B(72)+B(70)+B(68)+
-          B(67)+B(64), B(61)+B(60)+B(58)+B(57)+B(55)+B(52)+B(50)+B(49)+
-          B(47)+B(44)+B(37)+B(36)+B(35)+B(34)+B(32)+B(31)+B(30)+B(25)+
-          B(24)+B(23)+B(20)+B(13)+B(12)+B(11)+B(10)+B(9)+B(7)+B(6)+B(4)+
-          B(3)+B(2)+B(0), 87),
- kDefPoly(B(86)+B(85)+B(84)+B(83)+B(82)+B(80)+B(77)+B(74)+B(70)+B(69)+
-          B(65), B(63)+B(60)+B(59)+B(57)+B(56)+B(55)+B(53)+B(50)+B(49)+
-          B(48)+B(45)+B(42)+B(41)+B(40)+B(39)+B(38)+B(37)+B(36)+B(25)+
-          B(21)+B(19)+B(13)+B(11)+B(8)+B(5)+B(4)+B(2)+B(1)+B(0), 88),
- kDefPoly(B(86)+B(85)+B(83)+B(82)+B(81)+B(78)+B(77)+B(74)+B(73)+B(72)+
-          B(70)+B(69)+B(68)+B(65)+B(64), B(59)+B(57)+B(55)+B(54)+B(51)+
-          B(50)+B(46)+B(45)+B(44)+B(43)+B(42)+B(40)+B(38)+B(37)+B(33)+
-          B(31)+B(30)+B(29)+B(28)+B(27)+B(23)+B(22)+B(21)+B(20)+B(18)+
-          B(17)+B(16)+B(15)+B(10)+B(9)+B(3)+B(1)+B(0), 89),
- kDefPoly(B(86)+B(83)+B(82)+B(80)+B(79)+B(73)+B(70)+B(69)+B(67)+
-          B(64), B(63)+B(62)+B(61)+B(57)+B(56)+B(54)+B(51)+B(49)+B(47)+
-          B(46)+B(45)+B(40)+B(39)+B(37)+B(35)+B(33)+B(32)+B(29)+B(28)+
-          B(27)+B(25)+B(24)+B(23)+B(22)+B(21)+B(20)+B(19)+B(18)+B(17)+
-          B(15)+B(9)+B(8)+B(7)+B(3)+B(2)+B(0), 90),
- kDefPoly(B(90)+B(89)+B(84)+B(81)+B(80)+B(78)+B(74)+B(73)+B(71)+B(68)+
-          B(64), B(60)+B(59)+B(58)+B(57)+B(55)+B(54)+B(52)+B(50)+B(49)+
-          B(47)+B(45)+B(42)+B(41)+B(39)+B(38)+B(36)+B(32)+B(28)+B(25)+
-          B(21)+B(20)+B(19)+B(15)+B(12)+B(11)+B(9)+B(8)+B(3)+
-          B(0), 91),
- kDefPoly(B(91)+B(89)+B(88)+B(87)+B(86)+B(85)+B(84)+B(83)+B(80)+B(78)+
-          B(76)+B(72)+B(70)+B(68), B(63)+B(62)+B(61)+B(59)+B(57)+B(56)+
-          B(52)+B(51)+B(50)+B(49)+B(43)+B(40)+B(39)+B(37)+B(36)+B(35)+
-          B(34)+B(33)+B(32)+B(26)+B(25)+B(24)+B(23)+B(22)+B(18)+B(15)+
-          B(12)+B(11)+B(9)+B(7)+B(6)+B(3)+B(1)+B(0), 92),
- kDefPoly(B(86)+B(85)+B(83)+B(82)+B(79)+B(78)+B(77)+B(75)+B(74)+B(73)+
-          B(66)+B(64), B(59)+B(57)+B(56)+B(55)+B(54)+B(52)+B(51)+B(40)+
-          B(38)+B(36)+B(34)+B(33)+B(28)+B(27)+B(26)+B(25)+B(23)+B(22)+
-          B(21)+B(20)+B(19)+B(18)+B(16)+B(15)+B(14)+B(13)+B(12)+B(11)+B(8)+
-          B(7)+B(6)+B(5)+B(4)+B(0), 93),
- kDefPoly(B(93)+B(92)+B(91)+B(89)+B(88)+B(87)+B(86)+B(81)+B(80)+B(75)+
-          B(66)+B(64), B(62)+B(61)+B(60)+B(59)+B(58)+B(57)+B(56)+B(54)+
-          B(48)+B(47)+B(46)+B(45)+B(44)+B(42)+B(41)+B(38)+B(37)+B(36)+
-          B(34)+B(33)+B(31)+B(30)+B(27)+B(26)+B(25)+B(22)+B(13)+B(12)+
-          B(11)+B(10)+B(8)+B(7)+B(4)+B(0), 94),
- kDefPoly(B(94)+B(88)+B(87)+B(82)+B(79)+B(78)+B(76)+B(73)+B(65)+
-          B(64), B(62)+B(61)+B(60)+B(59)+B(58)+B(57)+B(53)+B(51)+B(50)+
-          B(49)+B(48)+B(47)+B(46)+B(44)+B(40)+B(36)+B(34)+B(33)+B(30)+
-          B(28)+B(27)+B(25)+B(22)+B(19)+B(18)+B(17)+B(16)+B(14)+B(7)+B(5)+
-          B(3)+B(2)+B(1)+B(0), 95),
- kDefPoly(B(92)+B(89)+B(88)+B(86)+B(83)+B(79)+B(78)+B(76)+B(75)+B(74)+
-          B(72)+B(70)+B(67)+B(66), B(63)+B(60)+B(57)+B(55)+B(53)+B(51)+
-          B(47)+B(46)+B(44)+B(43)+B(42)+B(39)+B(38)+B(36)+B(34)+B(32)+
-          B(31)+B(30)+B(27)+B(26)+B(25)+B(22)+B(21)+B(19)+B(17)+B(13)+
-          B(11)+B(10)+B(9)+B(8)+B(7)+B(4)+B(1)+B(0), 96),
- kDefPoly(B(96)+B(94)+B(93)+B(91)+B(89)+B(87)+B(85)+B(83)+B(81)+B(78)+
-          B(76)+B(74)+B(73)+B(68)+B(67)+B(64), B(62)+B(61)+B(57)+B(55)+
-          B(54)+B(53)+B(49)+B(47)+B(41)+B(38)+B(35)+B(33)+B(28)+B(27)+
-          B(24)+B(23)+B(21)+B(19)+B(18)+B(17)+B(15)+B(13)+B(12)+B(11)+B(8)+
-          B(6)+B(4)+B(3)+B(1)+B(0), 97),
- kDefPoly(B(97)+B(93)+B(92)+B(91)+B(90)+B(87)+B(83)+B(82)+B(80)+B(77)+
-          B(76)+B(75)+B(74)+B(73)+B(72)+B(70)+B(69)+B(68)+B(66)+B(65)+
-          B(64), B(63)+B(62)+B(61)+B(60)+B(59)+B(57)+B(55)+B(53)+B(50)+
-          B(49)+B(48)+B(45)+B(44)+B(43)+B(42)+B(40)+B(38)+B(36)+B(35)+
-          B(34)+B(28)+B(27)+B(24)+B(22)+B(21)+B(18)+B(17)+B(16)+B(15)+
-          B(14)+B(12)+B(11)+B(9)+B(8)+B(2)+B(1)+B(0), 98),
- kDefPoly(B(96)+B(94)+B(92)+B(86)+B(85)+B(84)+B(78)+B(77)+B(76)+B(75)+
-          B(73)+B(71)+B(69)+B(68)+B(65), B(61)+B(59)+B(57)+B(56)+B(54)+
-          B(50)+B(47)+B(46)+B(44)+B(41)+B(38)+B(36)+B(35)+B(34)+B(33)+
-          B(32)+B(29)+B(27)+B(26)+B(25)+B(23)+B(22)+B(21)+B(19)+B(17)+
-          B(16)+B(11)+B(9)+B(7)+B(6)+B(3)+B(2)+B(0), 99),
- kDefPoly(B(99)+B(96)+B(95)+B(93)+B(92)+B(88)+B(87)+B(83)+B(78)+B(77)+
-          B(76)+B(75)+B(74)+B(73)+B(70)+B(66)+B(64), B(63)+B(62)+B(60)+
-          B(59)+B(57)+B(56)+B(53)+B(50)+B(47)+B(41)+B(39)+B(38)+B(37)+
-          B(34)+B(25)+B(23)+B(21)+B(20)+B(19)+B(18)+B(17)+B(16)+B(13)+B(9)+
-          B(8)+B(6)+B(5)+B(1)+B(0), 100),
- kDefPoly(B(100)+B(98)+B(97)+B(95)+B(93)+B(92)+B(91)+B(89)+B(87)+B(85)+
-          B(84)+B(82)+B(81)+B(80)+B(79)+B(76)+B(68)+B(66)+B(65), B(63)+
-          B(62)+B(59)+B(57)+B(52)+B(51)+B(50)+B(47)+B(46)+B(45)+B(42)+
-          B(41)+B(40)+B(39)+B(38)+B(37)+B(36)+B(34)+B(32)+B(31)+B(30)+
-          B(24)+B(22)+B(21)+B(20)+B(18)+B(17)+B(16)+B(14)+B(12)+B(11)+
-          B(10)+B(8)+B(7)+B(5)+B(4)+B(2)+B(1)+B(0), 101),
- kDefPoly(B(101)+B(99)+B(97)+B(96)+B(92)+B(89)+B(88)+B(87)+B(86)+B(84)+
-          B(82)+B(81)+B(80)+B(78)+B(77)+B(76)+B(75)+B(74)+B(73)+
-          B(69), B(60)+B(59)+B(57)+B(56)+B(55)+B(54)+B(53)+B(51)+B(50)+
-          B(49)+B(47)+B(45)+B(43)+B(41)+B(35)+B(34)+B(32)+B(31)+B(29)+
-          B(27)+B(26)+B(25)+B(24)+B(21)+B(13)+B(12)+B(9)+B(8)+B(6)+B(5)+
-          B(3)+B(0), 102),
- kDefPoly(B(101)+B(98)+B(97)+B(96)+B(94)+B(93)+B(92)+B(90)+B(89)+B(88)+
-          B(87)+B(85)+B(83)+B(81)+B(80)+B(79)+B(76)+B(75)+B(71)+B(70)+
-          B(69)+B(66), B(63)+B(62)+B(60)+B(59)+B(58)+B(56)+B(54)+B(53)+
-          B(48)+B(45)+B(43)+B(42)+B(41)+B(37)+B(36)+B(32)+B(31)+B(30)+
-          B(27)+B(25)+B(23)+B(22)+B(19)+B(16)+B(15)+B(11)+B(9)+B(5)+B(3)+
-          B(0), 103),
- kDefPoly(B(98)+B(97)+B(95)+B(94)+B(91)+B(89)+B(88)+B(86)+B(85)+B(84)+
-          B(81)+B(79)+B(78)+B(76)+B(74)+B(73)+B(70)+B(69)+B(68)+B(67)+
-          B(66)+B(64), B(59)+B(53)+B(52)+B(51)+B(48)+B(46)+B(45)+B(43)+
-          B(37)+B(34)+B(33)+B(31)+B(30)+B(28)+B(25)+B(22)+B(21)+B(20)+
-          B(19)+B(14)+B(10)+B(8)+B(4)+B(2)+B(1)+B(0), 104),
- kDefPoly(B(103)+B(100)+B(99)+B(98)+B(94)+B(90)+B(89)+B(86)+B(84)+B(82)+
-          B(79)+B(76)+B(74)+B(73)+B(72)+B(71)+B(70)+B(69)+B(67)+
-          B(66), B(63)+B(62)+B(59)+B(58)+B(57)+B(55)+B(51)+B(49)+B(48)+
-          B(47)+B(46)+B(43)+B(42)+B(38)+B(36)+B(34)+B(33)+B(31)+B(30)+
-          B(29)+B(28)+B(27)+B(24)+B(21)+B(20)+B(18)+B(17)+B(16)+B(14)+
-          B(13)+B(11)+B(9)+B(7)+B(6)+B(5)+B(0), 105),
- kDefPoly(B(105)+B(104)+B(103)+B(102)+B(100)+B(98)+B(94)+B(93)+B(92)+B(91)+
-          B(90)+B(89)+B(87)+B(86)+B(85)+B(83)+B(82)+B(81)+B(79)+B(77)+
-          B(69)+B(68)+B(67)+B(64), B(61)+B(60)+B(59)+B(58)+B(56)+B(55)+
-          B(53)+B(50)+B(48)+B(44)+B(40)+B(38)+B(37)+B(36)+B(35)+B(34)+
-          B(33)+B(30)+B(29)+B(26)+B(22)+B(20)+B(13)+B(10)+B(8)+B(7)+B(5)+
-          B(0), 106),
- kDefPoly(B(105)+B(101)+B(100)+B(98)+B(97)+B(96)+B(93)+B(92)+B(91)+B(90)+
-          B(87)+B(86)+B(81)+B(79)+B(77)+B(75)+B(74)+B(72)+B(68)+B(67)+
-          B(64), B(63)+B(62)+B(61)+B(60)+B(59)+B(58)+B(54)+B(53)+B(52)+
-          B(50)+B(48)+B(47)+B(45)+B(42)+B(41)+B(38)+B(32)+B(29)+B(27)+
-          B(26)+B(24)+B(21)+B(19)+B(18)+B(16)+B(15)+B(14)+B(13)+B(12)+
-          B(10)+B(7)+B(6)+B(4)+B(1)+B(0), 107),
- kDefPoly(B(106)+B(105)+B(102)+B(100)+B(97)+B(95)+B(90)+B(89)+B(88)+B(86)+
-          B(83)+B(82)+B(81)+B(79)+B(78)+B(75)+B(72)+B(66)+B(64), B(63)+
-          B(62)+B(59)+B(58)+B(56)+B(54)+B(52)+B(51)+B(50)+B(48)+B(46)+
-          B(45)+B(44)+B(42)+B(40)+B(37)+B(36)+B(35)+B(33)+B(29)+B(27)+
-          B(22)+B(19)+B(17)+B(14)+B(12)+B(11)+B(10)+B(9)+B(8)+B(7)+B(6)+
-          B(5)+B(3)+B(0), 108),
- kDefPoly(B(108)+B(102)+B(101)+B(100)+B(99)+B(98)+B(96)+B(95)+B(94)+B(90)+
-          B(89)+B(88)+B(87)+B(84)+B(83)+B(81)+B(80)+B(77)+B(76)+B(75)+
-          B(71)+B(67)+B(65), B(63)+B(61)+B(60)+B(54)+B(50)+B(49)+B(48)+
-          B(43)+B(40)+B(39)+B(38)+B(36)+B(34)+B(29)+B(28)+B(27)+B(22)+
-          B(21)+B(19)+B(16)+B(14)+B(13)+B(12)+B(10)+B(9)+B(7)+B(6)+B(5)+
-          B(3)+B(2)+B(0), 109),
- kDefPoly(B(109)+B(108)+B(107)+B(102)+B(101)+B(98)+B(97)+B(96)+B(94)+B(92)+
-          B(91)+B(90)+B(88)+B(87)+B(85)+B(84)+B(83)+B(82)+B(81)+B(80)+
-          B(79)+B(78)+B(74)+B(73)+B(71)+B(70)+B(69)+B(66)+B(64), B(61)+
-          B(58)+B(57)+B(56)+B(50)+B(49)+B(46)+B(44)+B(43)+B(41)+B(36)+
-          B(35)+B(34)+B(30)+B(29)+B(26)+B(25)+B(24)+B(22)+B(21)+B(17)+
-          B(13)+B(11)+B(9)+B(4)+B(1)+B(0), 110),
- kDefPoly(B(110)+B(109)+B(105)+B(98)+B(97)+B(95)+B(94)+B(93)+B(92)+B(90)+
-          B(88)+B(84)+B(83)+B(82)+B(80)+B(77)+B(75)+B(72)+B(71)+B(70)+
-          B(69)+B(66), B(63)+B(61)+B(60)+B(59)+B(57)+B(56)+B(55)+B(52)+
-          B(51)+B(50)+B(49)+B(47)+B(43)+B(40)+B(36)+B(35)+B(34)+B(33)+
-          B(31)+B(27)+B(26)+B(21)+B(20)+B(19)+B(17)+B(16)+B(12)+B(8)+B(6)+
-          B(4)+B(3)+B(2)+B(1)+B(0), 111),
- kDefPoly(B(109)+B(107)+B(106)+B(104)+B(100)+B(98)+B(96)+B(95)+B(94)+B(92)+
-          B(91)+B(90)+B(89)+B(88)+B(86)+B(84)+B(81)+B(79)+B(78)+B(77)+
-          B(75)+B(73)+B(71)+B(70)+B(69)+B(67)+B(64), B(63)+B(62)+B(61)+
-          B(60)+B(58)+B(56)+B(54)+B(52)+B(51)+B(49)+B(48)+B(45)+B(44)+
-          B(39)+B(38)+B(37)+B(36)+B(35)+B(34)+B(32)+B(30)+B(26)+B(25)+
-          B(24)+B(23)+B(22)+B(21)+B(19)+B(16)+B(15)+B(11)+B(10)+B(9)+B(8)+
-          B(3)+B(1)+B(0), 112),
- kDefPoly(B(111)+B(107)+B(102)+B(100)+B(99)+B(98)+B(97)+B(96)+B(95)+B(94)+
-          B(93)+B(92)+B(87)+B(86)+B(82)+B(81)+B(80)+B(79)+B(77)+B(76)+
-          B(75)+B(72)+B(69)+B(64), B(61)+B(58)+B(56)+B(54)+B(53)+B(52)+
-          B(51)+B(49)+B(46)+B(43)+B(40)+B(39)+B(37)+B(36)+B(35)+B(34)+
-          B(33)+B(31)+B(29)+B(24)+B(22)+B(21)+B(20)+B(15)+B(14)+B(12)+
-          B(10)+B(6)+B(1)+B(0), 113),
- kDefPoly(B(112)+B(111)+B(110)+B(104)+B(102)+B(101)+B(100)+B(92)+B(89)+
-          B(87)+B(83)+B(82)+B(80)+B(79)+B(75)+B(74)+B(73)+B(72)+B(71)+
-          B(70)+B(68)+B(67)+B(65), B(60)+B(59)+B(57)+B(56)+B(55)+B(52)+
-          B(50)+B(47)+B(44)+B(41)+B(36)+B(35)+B(30)+B(29)+B(26)+B(25)+
-          B(24)+B(21)+B(18)+B(17)+B(16)+B(14)+B(12)+B(10)+B(7)+B(6)+
-          B(0), 114),
- kDefPoly(B(114)+B(112)+B(111)+B(110)+B(108)+B(107)+B(103)+B(102)+B(98)+
-          B(97)+B(96)+B(90)+B(88)+B(87)+B(86)+B(83)+B(82)+B(80)+B(79)+
-          B(77)+B(75)+B(70)+B(66)+B(65)+B(64), B(61)+B(60)+B(59)+B(58)+
-          B(57)+B(53)+B(52)+B(51)+B(50)+B(47)+B(45)+B(43)+B(39)+B(38)+
-          B(33)+B(32)+B(31)+B(29)+B(27)+B(21)+B(17)+B(14)+B(12)+B(10)+B(7)+
-          B(4)+B(2)+B(1)+B(0), 115),
- kDefPoly(B(113)+B(110)+B(108)+B(106)+B(105)+B(102)+B(101)+B(100)+B(98)+
-          B(96)+B(92)+B(89)+B(87)+B(86)+B(84)+B(81)+B(79)+B(78)+B(76)+
-          B(75)+B(73)+B(72)+B(71)+B(70)+B(67)+B(64), B(63)+B(62)+B(61)+
-          B(52)+B(47)+B(45)+B(44)+B(42)+B(40)+B(39)+B(35)+B(34)+B(33)+
-          B(31)+B(29)+B(25)+B(18)+B(15)+B(14)+B(10)+B(8)+B(6)+B(1)+
-          B(0), 116),
- kDefPoly(B(113)+B(111)+B(110)+B(109)+B(107)+B(106)+B(103)+B(102)+B(100)+
-          B(96)+B(95)+B(94)+B(91)+B(90)+B(89)+B(86)+B(82)+B(81)+B(78)+
-          B(77)+B(76)+B(75)+B(74)+B(73)+B(70)+B(67)+B(66), B(63)+B(61)+
-          B(59)+B(57)+B(56)+B(55)+B(53)+B(52)+B(51)+B(50)+B(47)+B(45)+
-          B(42)+B(40)+B(37)+B(35)+B(32)+B(30)+B(29)+B(25)+B(22)+B(21)+
-          B(20)+B(19)+B(16)+B(15)+B(14)+B(12)+B(8)+B(5)+B(0), 117),
- kDefPoly(B(117)+B(113)+B(110)+B(108)+B(105)+B(104)+B(103)+B(102)+B(99)+
-          B(98)+B(97)+B(94)+B(93)+B(91)+B(90)+B(89)+B(85)+B(84)+B(82)+
-          B(81)+B(79)+B(78)+B(77)+B(74)+B(73)+B(69)+B(67)+B(64), B(63)+
-          B(62)+B(61)+B(57)+B(55)+B(51)+B(50)+B(46)+B(45)+B(43)+B(42)+
-          B(41)+B(37)+B(33)+B(32)+B(30)+B(27)+B(26)+B(21)+B(19)+B(18)+
-          B(17)+B(15)+B(14)+B(12)+B(10)+B(8)+B(7)+B(3)+B(2)+B(1)+
-          B(0), 118),
- kDefPoly(B(118)+B(111)+B(109)+B(107)+B(106)+B(105)+B(104)+B(101)+B(99)+
-          B(98)+B(97)+B(94)+B(92)+B(91)+B(89)+B(83)+B(82)+B(80)+B(79)+
-          B(67)+B(66), B(62)+B(61)+B(60)+B(58)+B(57)+B(52)+B(48)+B(46)+
-          B(44)+B(42)+B(40)+B(39)+B(38)+B(36)+B(34)+B(33)+B(32)+B(29)+
-          B(23)+B(22)+B(20)+B(19)+B(18)+B(15)+B(13)+B(12)+B(11)+B(6)+B(5)+
-          B(4)+B(3)+B(1)+B(0), 119),
- kDefPoly(B(116)+B(115)+B(113)+B(112)+B(110)+B(107)+B(106)+B(104)+B(103)+
-          B(101)+B(100)+B(99)+B(98)+B(90)+B(89)+B(88)+B(87)+B(82)+B(80)+
-          B(79)+B(77)+B(76)+B(75)+B(74)+B(73)+B(71)+B(70)+B(68)+B(65)+
-          B(64), B(63)+B(62)+B(59)+B(55)+B(54)+B(48)+B(47)+B(45)+B(44)+
-          B(40)+B(39)+B(38)+B(35)+B(33)+B(29)+B(27)+B(26)+B(25)+B(24)+
-          B(23)+B(22)+B(21)+B(18)+B(17)+B(15)+B(13)+B(12)+B(10)+B(8)+B(3)+
-          B(2)+B(0), 120),
- kDefPoly(B(118)+B(117)+B(114)+B(113)+B(112)+B(110)+B(109)+B(104)+B(103)+
-          B(101)+B(99)+B(97)+B(96)+B(95)+B(93)+B(92)+B(91)+B(90)+B(89)+
-          B(87)+B(85)+B(84)+B(82)+B(81)+B(79)+B(73)+B(72)+B(68)+B(67)+
-          B(66)+B(64), B(60)+B(58)+B(57)+B(56)+B(54)+B(53)+B(52)+B(51)+
-          B(49)+B(48)+B(47)+B(45)+B(44)+B(38)+B(37)+B(36)+B(35)+B(33)+
-          B(32)+B(31)+B(30)+B(27)+B(26)+B(24)+B(23)+B(22)+B(20)+B(19)+
-          B(18)+B(16)+B(15)+B(12)+B(6)+B(5)+B(4)+B(2)+B(0), 121),
- kDefPoly(B(121)+B(118)+B(114)+B(112)+B(109)+B(106)+B(103)+B(102)+B(101)+
-          B(100)+B(97)+B(95)+B(90)+B(89)+B(87)+B(83)+B(81)+B(80)+B(79)+
-          B(78)+B(77)+B(76)+B(75)+B(74)+B(72)+B(71)+B(70)+B(69)+B(68)+
-          B(66)+B(64), B(61)+B(57)+B(51)+B(50)+B(47)+B(46)+B(43)+B(39)+
-          B(38)+B(37)+B(36)+B(34)+B(33)+B(32)+B(30)+B(28)+B(27)+B(24)+
-          B(22)+B(20)+B(18)+B(17)+B(14)+B(12)+B(11)+B(9)+B(7)+B(2)+
-          B(0), 122),
- kDefPoly(B(122)+B(121)+B(120)+B(119)+B(118)+B(117)+B(116)+B(113)+B(112)+
-          B(111)+B(109)+B(106)+B(105)+B(103)+B(100)+B(98)+B(97)+B(95)+
-          B(93)+B(92)+B(90)+B(87)+B(86)+B(85)+B(83)+B(81)+B(78)+B(77)+
-          B(75)+B(74)+B(73)+B(72)+B(71)+B(70)+B(69)+B(68)+B(67)+B(65)+
-          B(64), B(63)+B(62)+B(60)+B(55)+B(52)+B(51)+B(49)+B(47)+B(45)+
-          B(43)+B(42)+B(41)+B(37)+B(36)+B(35)+B(34)+B(32)+B(28)+B(27)+
-          B(26)+B(24)+B(23)+B(21)+B(20)+B(16)+B(13)+B(10)+B(9)+B(8)+B(7)+
-          B(5)+B(2)+B(0), 123),
- kDefPoly(B(123)+B(121)+B(120)+B(118)+B(117)+B(116)+B(115)+B(112)+B(111)+
-          B(110)+B(109)+B(107)+B(104)+B(102)+B(101)+B(100)+B(99)+B(98)+
-          B(97)+B(94)+B(90)+B(87)+B(86)+B(84)+B(83)+B(82)+B(79)+B(75)+
-          B(72)+B(71)+B(70)+B(64), B(63)+B(56)+B(54)+B(51)+B(50)+B(47)+
-          B(45)+B(44)+B(42)+B(39)+B(38)+B(36)+B(34)+B(33)+B(29)+B(26)+
-          B(24)+B(20)+B(16)+B(14)+B(11)+B(10)+B(8)+B(7)+B(6)+B(4)+B(2)+
-          B(0), 124),
- kDefPoly(B(124)+B(123)+B(121)+B(119)+B(118)+B(116)+B(115)+B(114)+B(107)+
-          B(105)+B(104)+B(103)+B(102)+B(99)+B(98)+B(96)+B(94)+B(93)+B(89)+
-          B(83)+B(82)+B(81)+B(80)+B(79)+B(78)+B(75)+B(74)+B(73)+B(72)+
-          B(70)+B(69)+B(68)+B(64), B(63)+B(59)+B(56)+B(55)+B(52)+B(51)+
-          B(50)+B(49)+B(48)+B(44)+B(42)+B(38)+B(37)+B(36)+B(33)+B(31)+
-          B(29)+B(27)+B(26)+B(25)+B(23)+B(21)+B(19)+B(18)+B(16)+B(14)+
-          B(11)+B(8)+B(7)+B(6)+B(4)+B(1)+B(0), 125),
- kDefPoly(B(124)+B(122)+B(121)+B(120)+B(119)+B(117)+B(113)+B(110)+B(108)+
-          B(105)+B(103)+B(102)+B(101)+B(97)+B(93)+B(91)+B(90)+B(88)+B(86)+
-          B(84)+B(82)+B(81)+B(79)+B(77)+B(76)+B(75)+B(73)+B(72)+B(71)+
-          B(69)+B(67)+B(64), B(63)+B(62)+B(61)+B(60)+B(58)+B(56)+B(55)+
-          B(52)+B(51)+B(48)+B(47)+B(45)+B(44)+B(42)+B(41)+B(40)+B(39)+
-          B(37)+B(33)+B(32)+B(30)+B(29)+B(28)+B(27)+B(26)+B(25)+B(24)+
-          B(23)+B(19)+B(18)+B(17)+B(16)+B(14)+B(13)+B(11)+B(9)+B(8)+B(7)+
-          B(4)+B(2)+B(1)+B(0), 126),
- kDefPoly(B(125)+B(124)+B(121)+B(116)+B(115)+B(105)+B(103)+B(101)+B(94)+
-          B(93)+B(91)+B(90)+B(88)+B(87)+B(86)+B(85)+B(77)+B(73)+B(72)+
-          B(70)+B(68)+B(67), B(63)+B(62)+B(61)+B(59)+B(57)+B(53)+B(52)+
-          B(51)+B(49)+B(48)+B(46)+B(44)+B(41)+B(39)+B(38)+B(36)+B(35)+
-          B(30)+B(27)+B(25)+B(23)+B(20)+B(19)+B(13)+B(12)+B(11)+B(10)+B(8)+
-          B(7)+B(5)+B(4)+B(3)+B(2)+B(0), 127),
- kDefPoly(B(127)+B(122)+B(121)+B(118)+B(117)+B(116)+B(109)+B(108)+B(107)+
-          B(106)+B(104)+B(103)+B(102)+B(101)+B(96)+B(93)+B(92)+B(91)+B(89)+
-          B(86)+B(85)+B(80)+B(78)+B(77)+B(76)+B(75)+B(74)+B(73)+B(72)+
-          B(71)+B(66), B(60)+B(56)+B(53)+B(52)+B(50)+B(47)+B(45)+B(41)+
-          B(39)+B(38)+B(37)+B(35)+B(34)+B(33)+B(30)+B(28)+B(25)+B(24)+
-          B(23)+B(21)+B(20)+B(19)+B(14)+B(13)+B(10)+B(8)+B(5)+B(4)+B(2)+
-          B(1)+B(0), 128),
-};
-// lint -restore
-
-// The number of polynomials in POLYS[].
-SELECTANY const int CRC::N_POLYS = sizeof (poly_list) / sizeof (poly_list[0]);
-
-// The externally visible name of poly_list.
-// This guarantees that the size of poly_list is opaque.
-SELECTANY const struct CRC::Poly *const CRC::POLYS = poly_list;
-
-// The "constructor" for a CRC with an default polynomial.
-CRC *CRC::Default(int degree, size_t roll_length) {
-  ASSERT1(32 == degree);
-
-  CRC *crc = CRCImpl::NewInternal(CRC::POLYS[degree].lo, CRC::POLYS[degree].hi,
-                                  degree, roll_length);  // Build the table
-  return crc;
-}
-
-// The "constructor" for a CRC with an arbitrary polynomial.
-CRC *CRC::New(uint64 lo, uint64 hi, int degree, size_t roll_length) {
-  return CRCImpl::NewInternal(lo, hi, degree, roll_length);
-}
-
-// Internal version of the "constructor".
-CRCImpl *CRCImpl::NewInternal(uint64 lo, uint64 hi,
-                             int degree, size_t roll_length) {
-  ASSERT1(8 <= degree && degree <= 64);  // precondition
-  ASSERT1(lo != 0 || hi != 0);            // precondition
-  // Generate the tables for extending a CRC by 4 bytes at a time.
-  // Why 4 and not 8?  Because Pentium 4 has such small caches.
-  struct CRC_pair t[4][256];
-  for (int j = 0; j != 4; j++) {      // for each byte of extension....
-    t[j][0].lo = 0;                   // a zero has no effect
-    t[j][0].hi = 0;
-    for (int i = 128; i != 0;  i >>= 1) {  // fill in entries for powers of 2
-      if (j == 0 && i == 128) {
-        t[j][i].lo = lo;  // top bit in first byte is easy---it's the polynomial
-        t[j][i].hi = hi;
-      } else {
-                  // each successive power of two is derive from the previous
-                  // one, either in this table, or the last table
-        struct CRC_pair pred;
-        if (i == 128) {
-          pred = t[j-1][1];
-        } else {
-          pred = t[j][i << 1];
-        }
-        // Advance the CRC by one bit (multiply by X, and take remainder
-        // through one step of polynomial long division)
-        if (pred.lo & 1) {
-          t[j][i].lo = (pred.lo >> 1) ^ (pred.hi << 63) ^ lo;
-          t[j][i].hi = (pred.hi >> 1) ^ hi;
-        } else {
-          t[j][i].lo = (pred.lo >> 1) ^ (pred.hi << 63);
-          t[j][i].hi = pred.hi >> 1;
-        }
-      }
-    }
-    // CRCs have the property that CRC(a xor b) == CRC(a) xor CRC(b)
-    // so we can make all the tables for non-powers of two by
-    // xoring previously created entries.
-    for (int i = 2; i != 256;  i <<= 1) {
-      for (int k = i+1; k != (i << 1); k++) {
-        t[j][k].lo = t[j][i].lo ^ t[j][k-i].lo;
-        t[j][k].hi = t[j][i].hi ^ t[j][k-i].hi;
-      }
-    }
-  }
-
-  // Copy the newly built tables in t[] into an appropriate
-  // CRC implenentation object.
-  CRCImpl *result = 0;
-  CRC32 *crc32 = 0;
-  crc32 = new CRC32();
-  for (int i = 0; i != 256; i++) {
-    crc32->table0_[i] = static_cast<uint32>(t[0][i].lo);
-    crc32->table1_[i] = static_cast<uint32>(t[1][i].lo);
-    crc32->table2_[i] = static_cast<uint32>(t[2][i].lo);
-    crc32->table3_[i] = static_cast<uint32>(t[3][i].lo);
-  }
-  result = crc32;
-
-  // "result" is now a CRC object of the right type to handle
-  // the polynomial of the right degree.
-
-  result->roll_length_ = roll_length;
-  result->degree_ = degree;
-  result->poly_lo_ = lo;
-  result->poly_hi_ = hi;
-
-  // Build the table for extending by zeroes.
-  // Entry i=a-1+3*b (a in {1, 2, 3}, b in {0, 1, 2, 3, ...}
-  // contains a polynomial Pi such that multiplying
-  // a CRC by Pi mod P, where P is the CRC polynomial, is equivalent to
-  // appending a*2**(2*b+SMALL_BITS) zero bytes to the original string.
-  // Entry is generated by calling ExtendByZeroes() twice using
-  // half the length from the previous entry.
-  int pos = 0;
-  for (uint64 inc_len = (1 << SMALL_BITS); inc_len != 0; inc_len <<= 2) {
-    result->Empty(&lo, &hi);
-    for (int k = 0; k != 3; k++) {
-      result->ExtendByZeroes(&lo, &hi, (size_t) (inc_len >> 1));
-      result->ExtendByZeroes(&lo, &hi, (size_t) (inc_len >> 1));
-      crc32->zeroes_[pos] = static_cast<uint32>(lo);
-      pos++;
-    }
-  }
-
-  // Calculate the entries in the roll table, used for rolling checksums
-  // of a fixed length.
-  // Extend the powers of two in the one-byte extension table by the roll
-  // length.
-  int bit = 256;
-  do {
-    bit >>= 1;
-    result->ExtendByZeroes(&t[0][bit].lo, &t[0][bit].hi, roll_length);
-  } while (bit != 0);
-  // Calculate the non-powers of two using CRC(a xor b) == CRC(a) xor CRC(b)
-  for (int i = 2; i != 256;  i <<= 1) {
-    for (int j = i+1; j != (i << 1); j++) {
-      t[0][j].lo = t[0][i].lo ^ t[0][j-i].lo;
-      t[0][j].hi = t[0][i].hi ^ t[0][j-i].hi;
-    }
-  }
-  // Now xor the CRC of (binary) 100000001 followed by
-  // the roll length of zeroes.   This will be xored into every
-  // entry.   This will simultaneously roll out the CRC
-  // of the empty string that's been pushed one byte too far,
-  // and roll in the CRC of the empty string in the correct place again.
-  result->Empty(&lo, &hi);
-  const uint8 x = 0x80;
-  result->Extend(&lo, &hi, &x, 1);
-  result->ExtendByZeroes(&lo, &hi, roll_length);
-  for (int i = 0; i != 256; i++) {
-    t[0][i].lo ^= lo;
-    t[0][i].hi ^= hi;
-  }
-
-  // Put the roll table into the object.
-  for (int i = 0; i != 256; i++) {
-    crc32->roll_[i] = static_cast<uint32>(t[0][i].lo);
-  }
-
-  return result;
-}
-
-// The CRC of the empty string is always the CRC polynomial itself.
-void CRCImpl::Empty(uint64 *lo, uint64 *hi) const {
-  ASSERT1(hi);
-  ASSERT1(lo);
-
-  *lo = this->poly_lo_;
-  *hi = this->poly_hi_;
-}
-
-//  The 32-bit implementation
-
-void CRC32::Extend(uint64 *lo, uint64 *hi, const void *bytes, size_t length)
-                      const {
-  ASSERT1(hi);
-  ASSERT1(lo);
-
-  hi;   // unreferenced formal parameter
-
-  const uint8 *p = static_cast<const uint8 *>(bytes);
-  const uint8 *e = p + length;
-  uint32 l = static_cast<uint32>(*lo);
-  // point x at MIN(first 4-byte aligned byte in string, end of string)
-  const uint8 *x = p + ((zero_ptr - p) & 3);
-  if (x > e) {
-    x = e;
-  }
-  // Process bytes until finished or p is 4-byte aligned
-  while (p != x) {
-    int c = (l & 0xff) ^ *p++;
-    l = this->table0_[c] ^ (l >> 8);
-  }
-  // point x at MIN(last 4-byte aligned byte in string, end of string)
-  x = e - ((e - zero_ptr) & 3);
-  // Process bytes 4 at a time
-  while (p < x) {
-    uint32 c = l ^ *reinterpret_cast<const uint32*>(p);
-    p += 4;
-    l = this->table3_[c & 0xff] ^
-        this->table2_[(c >> 8) & 0xff] ^
-        this->table1_[(c >> 16) & 0xff] ^
-        this->table0_[c >> 24];
-  }
-
-  // Process the last few bytes
-  while (p != e) {
-    int c = (l & 0xff) ^ *p++;
-    l = this->table0_[c] ^ (l >> 8);
-  }
-  *lo = l;
-}
-
-void CRC32::ExtendByZeroes(uint64 *lo, uint64 *hi, size_t length) const {
-  ASSERT1(hi);
-  ASSERT1(lo);
-
-  // Process the low order SMALL_BITS of the length by simply
-  // using Extend() on an array of bytes that are zero.
-  int small_part = (length & ((1 << SMALL_BITS)-1));
-  if (small_part != 0) {
-    this->Extend(lo, hi, zeroes, small_part);
-  }
-  length >>= SMALL_BITS;
-  if (length != 0) {          // if the length was at least 2**SMALL_BITS
-    uint32 l = static_cast<uint32>(*lo);
-    uint32 onebit = 1;
-    onebit <<= this->degree_ - 1;
-    // For each pair of bits in length
-    // (after the low-oder bits have been removed)
-    // we lookup the appropriate polynomial in the zeroes_ array
-    // and do a polynomial long multiplication (mod the CRC polynomial)
-    // to extend the CRC by the appropriate number of bits.
-    for (int i = 0; length != 0; i += 3, length >>= 2) {
-      int c = length & 3;       // pick next two bits
-      if (c != 0) {             // if they are not zero,
-                                // multiply by entry in table
-        uint32 m = this->zeroes_[c+i-1];
-        uint32 result = 0;
-        for (uint32 one = onebit; one != 0; one >>= 1) {
-          if ((l & one) != 0) {
-            result ^= m;
-          }
-          if (m & 1) {
-            m = (m >> 1) ^ static_cast<uint32>(poly_lo_);
-          } else {
-            m = (m >> 1);
-          }
-        }
-        l = result;
-      }
-    }
-    *lo = l;
-  }
-}
-
-void CRC32::Roll(uint64 *lo, uint64 *hi, uint8 o_byte, uint8 i_byte) const {
-  ASSERT1(hi);
-  ASSERT1(lo);
-
-  hi;   // unreferenced formal parameter
-
-  uint32 l = static_cast<uint32>(*lo);
-  // Roll in i_byte and out o_byte
-  *lo = this->table0_[(l & 0xff) ^ i_byte] ^ (l >> 8) ^ this->roll_[o_byte];
-}
-
-}  // namespace omaha
-
diff --git a/common/debug.cc b/common/debug.cc
deleted file mode 100644
index 96b6e70..0000000
--- a/common/debug.cc
+++ /dev/null
@@ -1,1183 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Debug functions
-
-#include "omaha/common/debug.h"
-
-#include <dbghelp.h>
-#include <wtsapi32.h>
-#include <atlstr.h>
-#ifdef _DEBUG
-#include <atlcom.h>
-#define STRSAFE_NO_DEPRECATE
-#include <strsafe.h>
-#endif
-#include <stdlib.h>
-#include <signal.h>
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/clipboard.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/const_config.h"
-#include "omaha/common/const_debug.h"
-#include "omaha/common/const_timeouts.h"
-#include "omaha/common/file.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/module_utils.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/common/string.h"
-#include "omaha/common/system.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/common/time.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/common/vista_utils.h"
-
-namespace omaha {
-
-#ifdef _DEBUG
-#define kSprintfBuffers (100)  // number of buffers for SPRINTF
-#else
-#define kSprintfBuffers (3)  // number of buffers for SPRINTF
-#endif
-
-// pad SPRINTF buffer to check for overruns
-#if SHIPPING
-#define kSprintfBufferOverrunPadding 0
-#else  // !SHIPPING
-#ifdef DEBUG
-#define kSprintfBufferOverrunPadding 20000
-#else
-#define kSprintfBufferOverrunPadding 1024
-#endif  // DEBUG
-#endif  // SHIPPING
-
-#define kErrorRequestToSend                                                    \
-    _T("*** Please hit Ignore to continue and send error information to the ") \
-    kAppName                                                                   \
-    _T(" team ***\n*** These details have been pasted to the clipboard ***")
-
-#define kMaxReportSummaryLen (1024*100)  // max length of report summary string
-
-#define kReportIdsLock kLockPrefix                                             \
-    _T("Report_Ids_Lock_57146B01-6A07-4b8d-A1D8-0C3AFC3B2F9B")
-
-SELECTANY bool g_always_assert = false;
-SELECTANY TCHAR *g_additional_status_ping_info = NULL;
-
-#define kSprintfMaxLen (1024 + 2)  // max length that wvsprintf writes is 1024
-static bool g_initialized_sprintf = false;
-static volatile LONG g_sprintf_interlock = 0;
-static int g_current_sprintf_buffer = 0;
-static TCHAR *g_sprintf_buffer = NULL;
-static TCHAR *g_sprintf_buffers[kSprintfBuffers];
-SELECTANY volatile LONG g_debugassertrecursioncheck = 0;
-static int g_total_reports = 0;
-
-SELECTANY ReportIds g_report_ids;
-
-// Builds a full path name out of the given filename. If the filename is
-// a relative path, it is appended to the debug directory. Otherwise, if the
-// filename is a full path, it returns it as the full debug filename.
-static CString MakeFullDebugFilename(const TCHAR *filename) {
-  CString full_name;
-  if (lstrlen(filename) <= 2 || filename[1] != _T(':')) {
-    full_name = GetDebugDirectory();
-    full_name += L"\\";
-  }
-  full_name += filename;
-  return full_name;
-}
-
-
-// Displays the assert box. Due to session isolation, MB_SERVICE_NOTIFICATION
-// flag does not work for Vista services. In this case, use WTS to display
-// a message box in the active console session.
-void ShowAssertDialog(const TCHAR *message, const TCHAR *title) {
-  int ret = 0;
-  OSVERSIONINFOEX osviex = {sizeof(OSVERSIONINFOEX), 0};
-  const bool is_vista_or_greater =
-     ::GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&osviex)) &&
-     osviex.dwMajorVersion >= 6;
-  bool is_system_process = false;
-  if (is_vista_or_greater &&
-      SUCCEEDED(IsSystemProcess(&is_system_process)) &&
-      is_system_process) {
-    DWORD session_id = System::WTSGetActiveConsoleSessionId();
-    if (session_id == kInvalidSessionId) {
-      session_id = WTS_CURRENT_SESSION;
-    }
-    DWORD response = 0;
-    ::WTSSendMessage(WTS_CURRENT_SERVER_HANDLE,
-                     session_id,
-                     const_cast<TCHAR*>(title),
-                     _tcslen(title) * sizeof(TCHAR),
-                     const_cast<TCHAR*>(message),
-                     _tcslen(message) * sizeof(TCHAR),
-                     MB_ABORTRETRYIGNORE | MB_ICONERROR,
-                     0,
-                     &response,
-                     true);
-    ret = response;
-  } else {
-    ret = ::MessageBoxW(NULL,
-                        message,
-                        title,
-                        MB_ABORTRETRYIGNORE |
-                        MB_ICONERROR        |
-                        MB_SERVICE_NOTIFICATION);
-  }
-
-  switch (ret) {
-    case IDABORT:
-      // Terminate the process if the user chose 'Abort'. Calling ExitProcess
-      // here results in calling the destructors for static objects which can
-      // result in deadlocks.
-      raise(SIGABRT);
-      break;
-
-    case IDRETRY:
-      // Break if the user chose "Retry".
-      __debugbreak();
-      break;
-    default:
-      // By default we ignore the message.
-      break;
-  }
-}
-
-DebugObserver* g_debug_observer = NULL;
-
-// replaces the debug observer, returns the previous value.
-DebugObserver* SetDebugObserver(DebugObserver* observer) {
-  DebugObserver* old_value = g_debug_observer;
-  g_debug_observer = observer;
-  return old_value;
-}
-
-DebugObserver* PeekDebugObserver() {
-  return g_debug_observer;
-}
-
-int SehSendMinidump(unsigned int code,
-                    struct _EXCEPTION_POINTERS *ep,
-                    time64 time_between_minidumps) {
-    if (code == EXCEPTION_BREAKPOINT)
-    return EXCEPTION_CONTINUE_SEARCH;
-
-  if (::IsDebuggerPresent())
-    return EXCEPTION_CONTINUE_SEARCH;
-
-  OutputDebugString(L"**SehSendMinidump**\r\n");
-
-  if (g_debug_observer) {
-    return g_debug_observer->SehSendMinidump(code, ep, time_between_minidumps);
-  }
-
-  return EXCEPTION_EXECUTE_HANDLER;
-}
-
-#if defined(_DEBUG) || defined(ASSERT_IN_RELEASE)
-CallInterceptor<DebugAssertFunctionType> debug_assert_interceptor;
-
-// Replaces the debug assert function; returns the old value.
-DebugAssertFunctionType* ReplaceDebugAssertFunction(
-    DebugAssertFunctionType* replacement) {
-  return debug_assert_interceptor.ReplaceFunction(replacement);
-}
-
-void OnAssert(const char *expr, const TCHAR *msg,
-              const char *filename, int32 linenumber) {
-  if (g_debug_observer) {
-    g_debug_observer->OnAssert(expr, msg, filename, linenumber);
-  }
-}
-#endif
-
-#if defined(_DEBUG)
-CString OnDebugReport(uint32 id,
-                      bool is_report,
-                      ReportType type,
-                      const char *expr,
-                      const TCHAR *message,
-                      const char *filename,
-                      int32 linenumber,
-                      DebugReportKind debug_report_kind) {
-  CString trace;
-  if (g_debug_observer) {
-    trace = g_debug_observer->OnDebugReport(id, is_report,
-                                            type, expr, message, filename,
-                                            linenumber, debug_report_kind);
-  }
-  return trace;
-}
-
-void SendExceptionReport(const TCHAR *log_file, const TCHAR *filename, int line,
-                         const TCHAR *type, uint32 id, bool offline) {
-  if (g_debug_observer) {
-    g_debug_observer->SendExceptionReport(log_file, filename, line,
-                                          type, id, offline);
-  }
-}
-#endif
-
-
-#ifdef _DEBUG  // won't compile since _CrtDbgReport isn't defined.
-
-#include <crtdbg.h>    // NOLINT
-static CString g_report_summary;
-
-// dump summary of reports on exit
-SELECTANY ReportSummaryGenerator g_report_summary_generator;
-
-ReportSummaryGenerator::~ReportSummaryGenerator() {
-  DumpReportSummary();
-}
-
-void ReportSummaryGenerator::DumpReportSummary() {
-  if (g_total_reports) {
-    ::OutputDebugString(L"REPORT SUMMARY:\r\n");
-    ::OutputDebugString(SPRINTF(L"%d total reports\r\n", g_total_reports));
-    ::OutputDebugString(g_report_summary);
-  } else {
-    ::OutputDebugString(L"NO REPORTS!!\r\n");
-  }
-}
-
-TCHAR *ReportSummaryGenerator::GetReportSummary() {
-  TCHAR *s = new TCHAR[kMaxReportSummaryLen];
-  if (s) {
-    s[0] = 0;
-    if (g_total_reports) {
-      SafeStrCat(s, L"REPORT SUMMARY:\r\n\r\n", kMaxReportSummaryLen);
-      SafeStrCat(s,
-                 SPRINTF(L"%d total reports\r\n\r\n", g_total_reports),
-                 kMaxReportSummaryLen);
-      SafeStrCat(s,
-                 g_report_summary.
-                     Left(kMaxReportSummaryLen - lstrlen(s) - 1).GetString(),
-                 kMaxReportSummaryLen);
-      CString report_string = g_report_ids.DebugReportString();
-      ReplaceCString(report_string, L"&", L"\r\n");
-      SafeStrCat(s,
-                 report_string.
-                     Left(kMaxReportSummaryLen - lstrlen(s) - 1).GetString(),
-                 kMaxReportSummaryLen);
-    } else {
-      SafeStrCat(s, L"NO REPORTS!!\r\n", kMaxReportSummaryLen);
-    }
-  }
-
-  return s;
-}
-
-static CAtlMap<CString, uint32> g_reports_done;
-
-#endif  // _DEBUG
-
-#ifdef _DEBUG
-
-void TraceError(DWORD error) {
-  HLOCAL mem = NULL;
-  ::FormatMessage(
-    FORMAT_MESSAGE_ALLOCATE_BUFFER |
-    FORMAT_MESSAGE_FROM_SYSTEM |
-    FORMAT_MESSAGE_IGNORE_INSERTS,
-    static_cast<LPVOID>(_AtlBaseModule.GetResourceInstance()),
-    error,
-    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),  // default language
-    reinterpret_cast<TCHAR*>(&mem),
-    0,
-    NULL);
-
-  TCHAR* str = reinterpret_cast<TCHAR*>(::LocalLock(mem));
-  ::OutputDebugString(str);
-  REPORT(false, R_ERROR, (str), 3968294226);
-  ::LocalFree(mem);
-}
-
-// ban ASSERT/VERIFY/REPORT to prevent recursion
-#undef ASSERT
-#undef VERIFY
-#undef REPORT
-
-// TODO(omaha): fix static initialization order below.
-// The initialization order of static variables is not deterministic per C++
-// standard and it depends completely on the compiler implementation.
-// For VC++ compiler we are using, it seems to work as expected.
-// One real fix is to put all definitons of static variables inside a class and
-// define a boolean variable in this class to indicate that all necessary
-// static initializations have been done.
-//
-// The follow definition is used to detect whether we get the exception
-// during initializing static variables. If this is the case, DebugReport()
-// will not function and will throw an exception because some of its refering
-// static variables are not initialized yet (i.e. g_reports_done).
-const int kTestInitStaticVariablesDoneValue = 1234;
-struct TestInitStaticVariablesDone {
-  int value;
-  TestInitStaticVariablesDone() : value(kTestInitStaticVariablesDoneValue) {}
-};
-static TestInitStaticVariablesDone test_var;
-
-bool DebugReport(unsigned int id,
-                 ReportType type,
-                 const char *expr,
-                 const TCHAR *message,
-                 const char *filename,
-                 int linenumber,
-                 DebugReportKind debug_report_kind) {
-  int recursion_count = ::InterlockedIncrement(&g_debugassertrecursioncheck);
-  ON_SCOPE_EXIT(::InterlockedDecrement, &g_debugassertrecursioncheck);
-  if (recursion_count > 1) {
-    ::OutputDebugString(_T("recursive debugreport skipped\n"));
-    return 1;
-  }
-
-  if (debug_assert_interceptor.interceptor()) {
-    // call replacement function (typically used for unit tests)
-    // Note that I'm doing this inside the in_assert block for paranoia;
-    // it's not really necessary and perhaps the wrong choice.
-    debug_assert_interceptor.interceptor()(expr, CT2A(message), filename,
-                                            linenumber);
-    return true;
-  }
-
-
-  // Check whether we have already finished initializing all static variables
-  // needed for executing DebugReport(). If not, bail out.
-  if (test_var.value != kTestInitStaticVariablesDoneValue) {
-    CString debug_msg;
-    debug_msg.Format(_T("%hs:%d - %s - %S"),
-                     filename, linenumber, message, expr);
-    debug_msg.Append(_T("\n\nException occurs while initializing ")
-                     _T("static variables needed for DebugReport"));
-    ShowAssertDialog(debug_msg, _T("DebugReport"));
-    return true;
-  }
-
-  bool is_assert = debug_report_kind == DEBUGREPORT_ASSERT;
-  bool is_report = debug_report_kind == DEBUGREPORT_REPORT;
-  bool is_abort  = debug_report_kind == DEBUGREPORT_ABORT;
-
-  if (is_report)
-    g_total_reports++;
-
-  g_report_ids.ReleaseReport(id);
-
-  if (type == R_FATAL) {
-    if (is_report) {
-      // Treat as ASSERT
-      is_report = false;
-      is_assert = true;
-    }
-  }
-
-  bool always_assert = g_always_assert;
-
-  if (always_assert) {
-    is_report = false;
-    is_assert = true;
-  }
-
-  if (!message) {
-    message = _T("");
-  }
-
-  // log to debugger
-  TCHAR *debug_string;
-  // ::OutputDebugString(DEBUG_LOG_SEPARATOR);
-  ::OutputDebugString(is_report ? _T("REPORT: ") :
-                                  (is_assert ? _T("ASSERT: ") : _T("ABORT: ")));
-
-  CFixedStringT<CString, 1024> proc_name = app_util::GetAppName();
-
-  // last %s now %s skip %d
-  const TCHAR* format = message && *message ?
-                        _T("[%hs:%d][%hs][%s]") : _T("[%hs:%d][%hs]");
-  debug_string = SPRINTF(format, filename, linenumber, expr, message);
-
-  // String_Int64ToString(g_last_report_time, 10),
-  // String_Int64ToString(time, 10), skip_report));
-
-  // ::OutputDebugString(DEBUG_LOG_SEPARATOR);
-  // ::OutputDebugString(_T("\n"));
-
-#ifdef LOGGING
-  // Log the reports via the logging system to all loggers.
-  CString what = is_report ? _T("REPORT") :
-                             is_assert ? _T("ASSERT") : _T("ABORT");
-  LC_LOG(LC_LOGGING, LEVEL_ERROR, (_T("[%s]%s"), what, debug_string));
-#else
-  ::OutputDebugString(debug_string);
-  ::OutputDebugString(_T("\n"));
-#endif
-
-  // skip sending strack trace for duplicate reports
-  CString report_id;
-  report_id.Format(_T("%hs:%d"), filename, linenumber);
-
-  uint32 prev_reports = 0;
-  if (g_reports_done.Lookup(report_id, prev_reports) && is_report) {
-    prev_reports++;
-    g_reports_done.SetAt(report_id, prev_reports);
-    ::OutputDebugString(SPRINTF(_T("skipping duplicate report %s %d\n"),
-                                report_id.GetString(),
-                                prev_reports));
-    return 1;
-  }
-
-  prev_reports++;
-  g_reports_done.SetAt(report_id, prev_reports);
-
-  g_report_summary.Append(debug_string);
-  g_report_summary.Append(L" (");
-  g_report_summary.Append(itostr(id));
-  g_report_summary.Append(L")");
-  g_report_summary.Append(L"\r\n");
-
-  // ::OutputDebugString(_T("log to file\n"));
-
-  // log to file
-  CString path_name(MakeFullDebugFilename(kCiDebugLogFile));
-  HANDLE h = CreateFile(path_name,
-                        GENERIC_WRITE | GENERIC_READ,
-                        FILE_SHARE_READ | FILE_SHARE_WRITE,
-                        NULL,
-                        OPEN_ALWAYS,
-                        FILE_ATTRIBUTE_NORMAL,
-                        NULL);
-
-  HANDLE assert_file = INVALID_HANDLE_VALUE;
-  if (is_assert) {
-    path_name = MakeFullDebugFilename(kCiAssertOccurredFile);
-    assert_file = CreateFile(path_name,
-                             GENERIC_WRITE,
-                             0,
-                             0,
-                             OPEN_ALWAYS,
-                             FILE_FLAG_WRITE_THROUGH,
-                             NULL);
-  }
-
-  HANDLE abort_file = INVALID_HANDLE_VALUE;
-  if (is_abort) {
-    path_name = MakeFullDebugFilename(kCiAbortOccurredFile);
-    abort_file = CreateFile(path_name,
-                            GENERIC_WRITE,
-                            0,
-                            0,
-                            OPEN_ALWAYS,
-                            FILE_FLAG_WRITE_THROUGH,
-                            NULL);
-  }
-
-  if (h != INVALID_HANDLE_VALUE ||
-      assert_file != INVALID_HANDLE_VALUE ||
-      abort_file != INVALID_HANDLE_VALUE) {
-    // more convenient for now to have this in UTF8
-    char *utf8_buffer = new char[(lstrlen(debug_string)*2) + 1];
-    if (utf8_buffer) {
-        int conv_bytes = WideCharToMultiByte(CP_UTF8,
-                                             0,
-                                             debug_string,
-                                             lstrlen(debug_string),
-                                             utf8_buffer,
-                                             (lstrlen(debug_string) * 2) + 1,
-                                             NULL,
-                                             NULL);
-
-        if (conv_bytes) {
-            DWORD bytes_written;
-            BOOL result;
-
-            if (h != INVALID_HANDLE_VALUE) {
-              SetFilePointer(h, 0, NULL, FILE_END);
-              result = ::WriteFile(h,
-                                   (LPCVOID)utf8_buffer,
-                                   conv_bytes,
-                                   &bytes_written,
-                                   NULL);
-              result = ::WriteFile(h,
-                                   (LPCVOID)DEBUG_LOG_SEPARATOR_CHAR,
-                                   strlen(DEBUG_LOG_SEPARATOR_CHAR),
-                                   &bytes_written,
-                                   NULL);
-            }
-            if (assert_file != INVALID_HANDLE_VALUE) {
-              result = ::WriteFile(assert_file,
-                                   (LPCVOID)utf8_buffer,
-                                   conv_bytes,
-                                   &bytes_written,
-                                   NULL);
-            }
-            if (abort_file != INVALID_HANDLE_VALUE) {
-              result = ::WriteFile(abort_file,
-                                   (LPCVOID)utf8_buffer,
-                                   conv_bytes,
-                                   &bytes_written,
-                                   NULL);
-            }
-        }
-
-        delete [] utf8_buffer;
-    }
-  }
-
-  if (h != INVALID_HANDLE_VALUE) {
-    ::CloseHandle(h);
-  }
-  if (assert_file != INVALID_HANDLE_VALUE) {
-    ::CloseHandle(assert_file);
-  }
-  if (abort_file != INVALID_HANDLE_VALUE) {
-    ::CloseHandle(abort_file);
-  }
-
-  CString stack_trace = OnDebugReport(id, is_report, type, expr, message,
-                                      filename, linenumber, debug_report_kind);
-
-  if (is_report) {
-    return 1;
-  }
-
-  ::OutputDebugString(L"show assert dialog\r\n");
-  ::OutputDebugString(stack_trace.GetString());
-
-  CString process_path;
-  GetModuleFileName(NULL, &process_path);
-
-  static TCHAR clipboard_string[4096] = {0};
-  lstrcpyn(clipboard_string,
-           SPRINTF(L"%ls (pid=%i)\r\n%hs:%d\r\n\r\n%hs\r\n%s\r\n\r\n",
-                   process_path,
-                   ::GetCurrentProcessId(),
-                   filename,
-                   linenumber,
-                   expr,
-                   message),
-           arraysize(clipboard_string));
-  stack_trace = stack_trace.Left(
-      arraysize(clipboard_string) - lstrlen(clipboard_string) - 1);
-  SafeStrCat(clipboard_string, stack_trace, arraysize(clipboard_string));
-  SetClipboard(clipboard_string);
-
-  stack_trace = stack_trace.Left(kMaxStackTraceDialogLen);
-
-  CString assert_text;
-  assert_text.Format(_T("Assertion (%ls) failed!\r\n\r\nProcess: %d ")
-    _T("(0x%08X)\r\nThread %d (0x%08X)\r\nProgram: %ls\r\n")
-    _T("Version: %s\r\nFile: %hs\r\nLine: %d\r\n\r\n"),
-    debug_report_kind == DEBUGREPORT_ASSERT ?
-      L"Assert" : (debug_report_kind == DEBUGREPORT_ABORT ?
-      L"Abort" : L"Report"),
-    ::GetCurrentProcessId(), ::GetCurrentProcessId(), ::GetCurrentThreadId(),
-    ::GetCurrentThreadId(), process_path, omaha::GetVersionString(), filename,
-    linenumber);
-
-  if (lstrlen(message) > 0) {
-    assert_text.AppendFormat(
-        _T("Expression: %hs\r\nMessage: %s\r\n\r\n%s\r\n\r\n%hs"),
-        expr,
-        message,
-        stack_trace,
-        kErrorRequestToSend);
-  } else {
-    assert_text.AppendFormat(_T("Expression: %hs\r\n\r\n%s\r\n\r\n%hs"),
-                             expr, stack_trace, kErrorRequestToSend);
-  }
-
-  ShowAssertDialog(assert_text, CString(filename));
-  return 1;
-}
-
-#endif  // #ifdef _DEBUG
-
-
-ReportIds::ReportIds() {
-  data_.report_counts_num = 0;
-  NamedObjectAttributes lock_attr;
-  GetNamedObjectAttributes(kReportIdsLock,
-                           vista_util::IsUserAdmin(),
-                           &lock_attr);
-  InitializeWithSecAttr(lock_attr.name, &lock_attr.sa);
-}
-
-ReportIds::~ReportIds() {
-  // don't attempt to write out reports from low integrity mode.
-  //
-  // TODO(omaha): save reports from a low integrity process (specifically IE
-  // which does some extra special magic to thwart this) --
-  // possible by launch a process or a broker, etc.
-  if (vista::IsProcessProtected()) {
-    return;
-  }
-
-  if (data_.report_counts_num != 0) {
-    // Back the report IDs to the registry
-    __mutexBlock(this) {
-      ReportData *reports_in_config = NULL;
-      if (LoadReportData(&reports_in_config)) {
-        MergeReports(reports_in_config, &data_);
-        SaveReportData(reports_in_config);
-
-        byte *data = reinterpret_cast<byte*>(reports_in_config);
-        delete [] data;
-      } else {
-        // There's no data in the registry, so just fill it up with this
-        // component's data.
-        SaveReportData(&data_);
-      }
-    }
-  }
-}
-
-const TCHAR* const GetRegKeyShared() {
-  return vista_util::IsUserAdmin() ? _T("HKLM\\") kCiRegKeyShared :
-                                     _T("HKCU\\") kCiRegKeyShared;
-}
-
-void ReportIds::ResetReportsAfterPing() {
-  // We will lose reports from TRS between the time DebugReportString was called
-  // and now. We'll also lose reports from non TRS components if they exit in
-  // between this time.  Not important.
-  data_.report_counts_num = 0;
-  __mutexBlock(this) {
-    RegKey::DeleteValue(GetRegKeyShared(), kRegValueReportIds);
-  }
-}
-
-void ReportIds::MergeReports(ReportData *data1, const ReportData *data2) {
-  // Loop through each report ID from data2.  If we find it already, increment
-  // the report's count in data1. Otherwise, if there's enough space, add the
-  // report ID to data1.
-  uint32 i, j;
-  for (i = 0; i < data2->report_counts_num; ++i) {
-    bool duplicate_report = false;
-    for (j = 0; j < data1->report_counts_num; ++j) {
-      if (data1->report_ids[j] == data2->report_ids[i]) {
-        // uint16 is promoted to int.
-        data1->report_counts[j] = static_cast<uint16>(
-            data1->report_counts[j] + data2->report_counts[i]);
-        duplicate_report = true;
-      }
-    }
-
-    if (!duplicate_report && j < kMaxUniqueReports) {
-      data1->report_ids[j] = data2->report_ids[i];
-      data1->report_counts[j] = data2->report_counts[i];
-      data1->report_counts_num++;
-    }
-  }
-}
-
-bool ReportIds::LoadReportData(ReportData **data) {
-  DWORD byte_count = 0;
-  *data = NULL;
-  HRESULT hr = RegKey::GetValue(GetRegKeyShared(),
-                                kRegValueReportIds,
-                                reinterpret_cast<byte**>(data),
-                                &byte_count);
-  if (SUCCEEDED(hr)) {
-    if (byte_count == sizeof(ReportData)) {
-      return true;
-    } else {
-      delete[] data;
-      data = NULL;
-      return false;
-    }
-  } else {
-    return false;
-  }
-}
-
-void ReportIds::SaveReportData(ReportData *data) {
-  if (data->report_counts_num) {
-    RegKey::SetValue(GetRegKeyShared(),
-                     kRegValueReportIds,
-                     reinterpret_cast<byte*>(data),
-                     sizeof(ReportData));
-  }
-}
-
-bool ReportIds::ReleaseReport(uint32 id) {
-  uint32 max = data_.report_counts_num;
-
-  // If two threads call simultaneously, might miss one of an existing report
-  // here; not important.
-
-  uint32 i = 0;
-  for (i = 0; i < max; ++i) {
-    if (data_.report_ids[i] == id) {
-      data_.report_counts[i]++;
-      return true;
-      }
-  }
-
-  // If two threads call simultaneously, might overwrite first of another
-  // report; not important.
-
-  if (i < kMaxUniqueReports) {
-    data_.report_ids[i] = id;
-    data_.report_counts[i] = 1;
-    data_.report_counts_num = i + 1;  // Set only after setting ids and count;
-                                      // don't use ++
-  }
-
-#ifdef _DEBUG
-  OutputDebugString(SPRINTF(_T("release report %u\n"), id));
-#endif
-  // must return true (return value of REPORT)
-  return true;
-}
-
-// caller deletes the string
-TCHAR *ReportIds::DebugReportString() {
-  TCHAR *s = new TCHAR[(kMaxUniqueReports * kMaxReportCountString) + 1];
-  if (!s) { return NULL; }
-  s[0] = '\0';
-
-#if 0
-  // this version if we use a hash table for the report counts:
-  uint32 id;
-  uint16 count;
-  hr = g_reports->First(&found, &id, &count);
-  while (SUCCEEDED(hr) && found) {
-    if (count) {
-        status_info.AppendFormat(_T("%d=%d"), id, static_cast<uint32>(count));
-    }
-
-    hr = g_reports->Next(&found, &id, &count);
-  }
-#endif
-
-  // The registry will contain the REPORTs from other components, so get that
-  // list and merge it with TRS'.
-  __mutexBlock(this) {
-    ReportData *reports_in_config = NULL;
-    if (LoadReportData(&reports_in_config)) {
-      MergeReports(&data_, reports_in_config);
-
-      byte *data = reinterpret_cast<byte*>(reports_in_config);
-      delete[] data;
-    }
-  }
-
-  TCHAR *current_pos = s;
-  for (uint32 i = 0; i < data_.report_counts_num; i++) {
-    if (data_.report_counts[i]) {
-      // should be no chance of overflow, ok to use wsprintf
-      int n = wsprintf(current_pos,
-                       _T("%u:%u,"),
-                       data_.report_ids[i],
-                       static_cast<uint32>(data_.report_counts[i]));
-      current_pos += n;
-    }
-  }
-
-  return s;
-}
-
-// A simple helper function whose sole purpose is
-// to isolate the dtor from SPRINTF which uses try/except.
-// app_util::GetAppName returns a CString and dtor's
-// aren't allowed in functions with try/except when
-// the /EHsc flag is set.
-void FillInSprintfErrorString(const TCHAR * format, TCHAR * error_string) {
-  wsprintf(error_string, L"SPRINTF buffer overrun %ls %hs %ls",
-    app_util::GetAppName(), omaha::GetVersionString(), format);
-}
-
-// following is currently included in release build
-// return string from format+arglist; for debugging
-TCHAR * __cdecl SPRINTF(const TCHAR * format, ...) {
-  while (::InterlockedCompareExchange(&g_sprintf_interlock, 1, 0) == 1) {
-  // while (::InterlockedIncrement(&g_sprintf_interlock)>1) {
-    // ::InterlockedDecrement(&g_sprintf_interlock);
-    // Don't process APCs here.
-    // Can lead to infinite recursion, for example: filecap->logging->filecap...
-    Sleep(0);
-  }
-
-  g_current_sprintf_buffer++;
-  if (g_current_sprintf_buffer >= kSprintfBuffers) {
-    g_current_sprintf_buffer = 0;
-  }
-
-  TCHAR *sprintf_buf = NULL;
-
-  if (!g_initialized_sprintf) {  // initialize buffers
-    g_sprintf_buffer = new TCHAR[
-        ((kSprintfMaxLen + 1) * kSprintfBuffers) +
-        kSprintfBufferOverrunPadding];
-    TCHAR* buffer = g_sprintf_buffer;
-    if (!buffer) { goto cleanup; }
-    for (int i = 0; i < kSprintfBuffers; ++i) {
-      g_sprintf_buffers[i] = buffer;
-      buffer += kSprintfMaxLen + 1;
-    }
-
-#if !SHIPPING
-    for (int i = ((kSprintfMaxLen+1) * kSprintfBuffers);
-         i < ((kSprintfMaxLen + 1) * kSprintfBuffers) +
-            kSprintfBufferOverrunPadding;
-         ++i) {
-      g_sprintf_buffer[i] = 1;
-    }
-#endif
-
-    // InitializeCriticalSection(&g_sprintf_critical_section);
-    g_initialized_sprintf = true;
-  }
-
-  sprintf_buf = g_sprintf_buffers[g_current_sprintf_buffer];
-
-  // EnterCriticalSection(&g_sprintf_critical_section);
-
-  __try {
-    // create the formatted CString
-    va_list vl;
-    va_start(vl, format);
-#ifdef DEBUG
-    StringCbVPrintfW(sprintf_buf, kSprintfMaxLen, format, vl);
-#else
-    wvsprintfW(sprintf_buf, format, vl);
-#endif
-    va_end(vl);
-
-#if !SHIPPING
-    for (int i = ((kSprintfMaxLen+1) * kSprintfBuffers);
-         i < ((kSprintfMaxLen+1) * kSprintfBuffers) +
-            kSprintfBufferOverrunPadding;
-         ++i) {
-      if (g_sprintf_buffer[i] != 1) {
-        TCHAR error_string[1024];
-        FillInSprintfErrorString(format, error_string);
-        MessageBox(NULL,
-                   error_string,
-                   error_string,
-                   MB_OK | MB_SETFOREGROUND | MB_TOPMOST);
-        break;
-      }
-    }
-#endif
-  }
-  __except(EXCEPTION_EXECUTE_HANDLER) {
-    lstrcpyn(sprintf_buf, _T("sprintf failure"), kSprintfMaxLen);
-  }
-
-  // LeaveCriticalSection(&g_sprintf_critical_section);
-
-  cleanup:
-
-  ::InterlockedDecrement(&g_sprintf_interlock);
-  return sprintf_buf;
-}
-
-#if 0
-  TCHAR * __cdecl SPRINTF(const TCHAR * format, ...) {
-  ASSERT(format, (L""));
-
-  g_current_sprintf_buffer++;
-  if (g_current_sprintf_buffer >= kSprintfBuffers) {
-    g_current_sprintf_buffer = 0;
-    }
-
-  TCHAR *sprintf_buf = sprintf_buffers[g_current_sprintf_buffer];
-  CFixedStringT<CString, kSprintfMaxLen> out;
-
-  va_list argptr;
-  va_start(argptr, format);
-  out.FormatV(format, argptr);
-  va_end(argptr);
-
-  // copy to fixed return buffers
-  SafeStrCat(sprintf_buf,
-             out.GetBufferSetLength(kSprintfMaxLen),
-             g_current_sprintf_buffer);
-  sprintf_buf[kSprintfMaxLen] = '\0';
-
-  return sprintf_buf;
-}
-#endif
-
-// Cleanup allocated memory
-class SprintfCleaner {
- public:
-  SprintfCleaner() {}
-
-  ~SprintfCleaner() {
-    while (::InterlockedCompareExchange(&g_sprintf_interlock, 1, 0) == 1) {
-      Sleep(0);
-    }
-
-    if (g_initialized_sprintf) {
-      delete[] g_sprintf_buffer;
-      for (int i = 0; i < kSprintfBuffers; ++i) {
-        g_sprintf_buffers[i] = NULL;
-      }
-      g_initialized_sprintf = false;
-    }
-
-    ::InterlockedDecrement(&g_sprintf_interlock);
-  }
-
- private:
-  DISALLOW_EVIL_CONSTRUCTORS(SprintfCleaner);
-};
-
-static SprintfCleaner cleaner;
-
-// This is for our testers to find asserts in release mode.
-#if !defined(_DEBUG) && defined(ASSERT_IN_RELEASE)
-bool ReleaseAssert(const char *expr,
-                   const TCHAR *msg,
-                   const char *filename,
-                   int32 linenumber) {
-  ASSERT(filename, (L""));
-  ASSERT(msg, (L""));
-  ASSERT(expr, (L""));
-
-  if (debug_assert_interceptor.interceptor()) {
-    // call replacement function (typically used for unit tests)
-    // Note that I'm doing this inside the in_assert block for paranoia;
-    // it's not really necessary and perhaps the wrong choice.
-    debug_assert_interceptor.interceptor()(expr,
-                                           CT2CA(msg),
-                                           filename,
-                                           linenumber);
-    return true;
-  }
-
-  OnAssert(expr, msg, filename, linenumber);
-
-  // Also put up a message box.
-  TCHAR error_string[1024] = {0};
-  wsprintf(error_string,
-           L"App: %ls\r\n"
-           L"Expr: %hs\r\n"
-           L"File: %hs\r\n"
-           L"Line: %d\r\n"
-           L"Version: %hs\r\n"
-           L"Message: ",
-           app_util::GetAppName(),
-           expr,
-           filename,
-           linenumber,
-           VER_TIMESTAMP_STR_FILE);
-  SafeStrCat(error_string, msg, arraysize(error_string));
-  SafeStrCat(error_string,
-             L"\r\n\r\n*** This message has been copied to the clipboard. ***",
-             arraysize(error_string));
-  SetClipboard(error_string);
-
-  TCHAR title_string[1024];
-  wsprintf(title_string, kAppName L" ASSERT %ls %hs",
-    app_util::GetAppName(), VER_TIMESTAMP_STR_FILE);
-  MessageBox(NULL,
-             error_string,
-             title_string,
-             MB_OK | MB_SETFOREGROUND | MB_TOPMOST);
-  return true;
-}
-#endif
-
-#if defined(_DEBUG)
-void DebugAbort(const TCHAR *msg,
-                const char* filename,
-                int32 linenumber,
-                bool do_abort) {
-  DebugReport(0, R_FATAL, "", msg, filename, linenumber, DEBUGREPORT_ABORT);
-  if (do_abort) {
-    abort();
-  }
-}
-#else
-void ReleaseAbort(const TCHAR *msg,
-                  const char* filename,
-                  int32 linenumber,
-                  bool do_abort) {
-  // Send info to the server.
-#if defined(ASSERT_IN_RELEASE)
-  OnAssert("", msg, filename, linenumber);
-#endif
-
-  // Also put up a message box.
-  TCHAR error_string[1024] = {0};
-  wsprintf(error_string,
-           L"App: %ls\r\n"
-           L"File: %hs\r\n"
-           L"Line: %d\r\n"
-           L"Version: %hs\r\n"
-           L"Message: ",
-           app_util::GetAppName(),
-           filename,
-           linenumber,
-           omaha::GetVersionString());
-  SafeStrCat(error_string, msg, arraysize(error_string));
-  SafeStrCat(error_string,
-             L"\r\n\r\n*** This message has been copied to the clipboard. ***",
-             arraysize(error_string));
-  SetClipboard(error_string);
-
-  TCHAR title_string[1024];
-  wsprintf(title_string,
-           kAppName L" ABORT %ls %hs",
-           app_util::GetAppName(),
-           omaha::GetVersionString());
-  MessageBox(NULL,
-             error_string,
-             title_string,
-             MB_OK | MB_SETFOREGROUND | MB_TOPMOST);
-
-  if (do_abort) {
-    abort();
-  }
-}
-#endif
-
-
-#ifdef _DEBUG
-
-void DumpInterface(IUnknown* unknown) {
-  if (!unknown)
-    return;
-
-  OutputDebugString(_T("------------------------------------------------\r\n"));
-
-  // Open the HKCR\Interfaces key where the IIDs of marshalable interfaces
-  // are stored.
-  RegKey key;
-  if (SUCCEEDED(key.Open(HKEY_CLASSES_ROOT, _T("Interface"), KEY_READ))) {
-    TCHAR name[_MAX_PATH + 1] = {0};
-    DWORD name_size = _MAX_PATH;
-    DWORD index = 0;
-    FILETIME last_written;
-
-    //
-    // Enumerate through the IIDs and see if the object supports it
-    // by calling QueryInterface.
-    //
-    while (::RegEnumKeyEx(key.Key(),
-                          index++,
-                          name,
-                          &name_size,
-                          NULL,
-                          NULL,
-                          NULL,
-                          &last_written) == ERROR_SUCCESS) {
-      // Convert the string to an IID
-      IID iid;
-      HRESULT hr = ::CLSIDFromString(name, &iid);
-
-      CComPtr<IUnknown> test;
-      if (unknown->QueryInterface(iid,
-                                  reinterpret_cast<void**>(&test)) == S_OK) {
-        //
-        // The object supports this interface.
-        // See if we can get a human readable name for the interface
-        // If not, the name buffer already contains the string
-        // representation of the IID, which we'll use as a fallback.
-        //
-        RegKey sub_key;
-        if (sub_key.Open(key.Key(), name, KEY_READ) == S_OK) {
-          scoped_array<TCHAR> display;
-          // If this fails, we should still have the IID
-          if (sub_key.GetValue(NULL, address(display)) == S_OK)
-            lstrcpyn(name, display.get(), _MAX_PATH);
-        }
-
-        CString fmt;
-        fmt.Format(_T("  %s\r\n"), name);
-        OutputDebugString(fmt);
-      }
-
-      ZeroMemory(name, arraysize(name));
-      name_size = _MAX_PATH;
-    }
-  }
-
-  OutputDebugString(_T("------------------------------------------------\r\n"));
-}
-#endif
-
-// TODO(omaha): the implementation below is using CStrings so it is not very
-// conservative in terms of memory allocations.
-int SehNoMinidump(unsigned int code, struct _EXCEPTION_POINTERS *,
-                  const char *filename, int32 linenumber, bool show_message) {
-  if (code == EXCEPTION_BREAKPOINT)
-    return EXCEPTION_CONTINUE_SEARCH;
-
-  uint32 latest_cl = 0;
-#ifdef VERSION_LATEST_CL
-  latest_cl = VERSION_LATEST_CL;
-#endif
-
-  if (show_message) {
-    TCHAR message[1025] = {0};
-    wsprintf(message,
-             _T("Exception %x in %s %s %u\r\n\r\n%hs:%d\r\n"),
-             code,
-             app_util::GetAppName(),
-             omaha::GetVersionString(),
-             latest_cl,
-             filename,
-             linenumber);
-
-    SetClipboard(message);
-    uint32 type = MB_ABORTRETRYIGNORE |
-                  MB_ICONERROR |
-                  MB_SERVICE_NOTIFICATION |
-                  MB_SETFOREGROUND |
-                  MB_TOPMOST;
-    int ret = ::MessageBox(NULL, message, _T("Exception"), type);
-    switch (ret) {
-      case IDABORT:
-        // Kamikaze if the user chose 'abort'
-        ::ExitProcess(static_cast<UINT>(-1));
-        break;
-
-      case IDRETRY:
-        // Break if the user chose "retry"
-        __debugbreak();
-        break;
-
-      default:
-        // By default we ignore the message
-      break;
-    }
-  }
-  return EXCEPTION_EXECUTE_HANDLER;
-}
-
-CString GetDebugDirectory() {
-  CString debug_dir;
-  CString system_drive = GetEnvironmentVariableAsString(_T("SystemDrive"));
-  if (!system_drive.IsEmpty()) {
-    debug_dir += system_drive;
-    debug_dir += L"\\";
-  }
-  debug_dir += kCiDebugDirectory;
-  return debug_dir;
-}
-
-}  // namespace omaha
-
diff --git a/common/debug.h b/common/debug.h
deleted file mode 100644
index 6f0679f..0000000
--- a/common/debug.h
+++ /dev/null
@@ -1,316 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Debug functions
-
-#ifndef OMAHA_COMMON_DEBUG_H__
-#define OMAHA_COMMON_DEBUG_H__
-
-// To create a release build with asserts turned on, uncomment the
-// following line, and uncomment the linking with atls.lib in api.
-//
-// #define ASSERT_IN_RELEASE
-
-#include "omaha/common/atlassert.h"
-#include "omaha/common/commontypes.h"
-#include "omaha/common/synchronized.h"
-
-namespace omaha {
-
-// hash table for counts of the number of times REPORTs occur
-// template<class K, class V>
-// class HashTable;
-// extern HashTable<uint32, uint16> *g_reports;
-
-#define kMaxUniqueReports (20)
-#define kMaxReportCountString (20)
-
-class ReportIds;
-extern ReportIds g_report_ids;
-extern volatile LONG g_debugassertrecursioncheck;
-extern bool g_always_assert;
-
-const int kMaxStackTraceDialogLen = 512;  // too long and dialog box fails
-
-// TODO(omaha): consider merging this into DebugObserver.
-//
-// For automated testing, we don't (always) want asserts to fire. So
-// we allow the unit test system to handle asserts instead.
-//
-// Give a function matching the prototype to REPLACE_ASSERT_FUNCTION to
-// have your function called instead of the normal assert function.
-typedef int DebugAssertFunctionType(const char *expression,
-  const char *message, const char *file, int line);
-
-enum ReportType;
-enum DebugReportKind;
-class DebugObserver {
- public:
-  virtual ~DebugObserver() {}
-  virtual int SehSendMinidump(unsigned int code, struct _EXCEPTION_POINTERS *ep,
-                              time64 time_between_minidumps) = 0;
-
-#if defined(_DEBUG) || defined(ASSERT_IN_RELEASE)
-  virtual void OnAssert(const char *expr, const TCHAR *msg,
-                        const char *filename, int32 linenumber) = 0;
-#endif
-
-#if defined(_DEBUG)
-  virtual void SendExceptionReport(const TCHAR *log_file, const TCHAR *filename,
-                                   int line, const TCHAR *type, uint32 id,
-                                   bool offline) = 0;
-  virtual CString OnDebugReport(uint32 id, bool is_report, ReportType type,
-                                const char *expr, const TCHAR *message,
-                                const char *filename, int32 linenumber,
-                                DebugReportKind debug_report_kind) = 0;
-#endif
-};
-
-// replaces the debug observer, returns the previous value.
-DebugObserver* SetDebugObserver(DebugObserver* observer);
-DebugObserver* PeekDebugObserver();
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int SehSendMinidump(unsigned int code,
-                    struct _EXCEPTION_POINTERS *ep,
-                    time64 time_between_minidumps);
-
-#define kMinReportInterval100ns (60 * kSecsTo100ns)
-#define kMinStackReportInterval100ns (60 * kSecsTo100ns)
-#define DEBUG_LOG_SEPARATOR_CHAR "-------------------------------------------\n"
-#define DEBUG_LOG_SEPARATOR     L"-------------------------------------------\n"
-#define kExceptionReportHeaders L"Content-Type: binary"
-
-// TODO(omaha): disable the exception reporting feature to save .data space
-#define kMaxExceptionReportLen 1
-
-#define kMinidumpRequestToSendMessage                               \
-    kAppName L" has encountered a problem.\n***"                    \
-    L"Please hit OK to debug if you have a debugger installed, "    \
-    L"or cancel to continue."                                       \
-
-#define kMinidumpRequestToSendTitle kAppName L" quality assurance"
-
-#undef ASSERT
-#undef VERIFY
-#undef TRACE
-
-// Holds information about REPORTS and their frequency
-struct ReportData {
-  uint32 report_counts_num;
-  uint32 report_ids[kMaxUniqueReports];
-  uint16 report_counts[kMaxUniqueReports];
-};
-
-// Used to hold REPORT IDs and to back them to the registry, where they'll be
-// read and sent in a ping.
-class ReportIds : public GLock {
- public:
-  ReportIds();
-  ~ReportIds();
-
-  // Call this after a successful ping to clear the report IDs from the registry
-  // and from this component (TRS).
-  void ResetReportsAfterPing();
-
-  // Adds a report ID to our list, if there's enough space.
-  bool ReleaseReport(uint32 id);
-
-  // Creates a string with the report IDs and their frequency.
-  // Caller deletes string.
-  TCHAR *DebugReportString();
-
- private:
-  ReportData data_;
-
-  // Merges the report data from data2 with data1.
-  void MergeReports(ReportData *data1, const ReportData *data2);
-
-  // We have to use RegKey directly, because we can't depend on the global
-  // Config object during destruction.
-  bool LoadReportData(ReportData **data);
-  void SaveReportData(ReportData *data);
-
-  DISALLOW_EVIL_CONSTRUCTORS(ReportIds);
-};
-
-#if defined(_DEBUG) || defined(ASSERT_IN_RELEASE)
-  // Replaces the debug assert function; returns the old value.
-  DebugAssertFunctionType *ReplaceDebugAssertFunction(DebugAssertFunctionType
-                                                      *replacement);
-  #define REPLACE_ASSERT_FUNCTION(replacement) \
-      ReplaceDebugAssertFunction(replacement)
-#else
-  #define REPLACE_ASSERT_FUNCTION(replacement) NULL
-#endif
-
-#ifdef _DEBUG
-  void SendExceptionReport(const TCHAR *log_file,
-                           const TCHAR *filename,
-                           int line,
-                           const TCHAR *type,
-                           uint32 id,
-                           bool offline);
-
-  void SendStackTrace(const TCHAR *filename,
-                      int line,
-                      const TCHAR *type,
-                      bool all_threads,
-                      uint32 id);
-
-  // DEBUG MODE
-
-  extern bool g_LSPMode;
-
-  bool DebugReport(unsigned int id,
-                   ReportType type,
-                   const char *expr,
-                   const TCHAR *message,
-                   const char *filename,
-                   int linenumber,
-                   DebugReportKind debug_report_kind);
-
-  #define VERIFY(expr, msg) ASSERT(expr, msg)  // VERIFY is ASSERT
-
-  #define ASSERT(expr, msg)                                                    \
-      do {                                                                     \
-        ((expr) ? 0 : omaha::DebugReport(0, omaha::R_FATAL, #expr,             \
-          omaha::SPRINTF msg, __FILE__, __LINE__, omaha::DEBUGREPORT_ASSERT)); \
-      } while (0)
-
-  #define REPORT(expr, type, msg, id)                           \
-    ((expr) ? 0 : omaha::DebugReport(id,                        \
-                                     type,                      \
-                                     #expr,                     \
-                                     omaha::SPRINTF msg,        \
-                                     __FILE__,                  \
-                                     __LINE__,                  \
-                                     omaha::DEBUGREPORT_REPORT))
-  void DebugAbort(const TCHAR* msg,
-                  const char* filename,
-                  int32 linenumber,
-                  bool do_abort);
-  #define ABORT(msg)                                            \
-      omaha::DebugAbort(omaha::SPRINTF msg, __FILE__, __LINE__, true)
-
-  void TraceError(DWORD error);
-  inline void TraceLastError() { TraceError(GetLastError()); }
-
-  /**
-  * Iterates through HKEY_CLASSES_ROOT\Interface and calls QI for
-  * all the interfaces there.  Useful for finding out what type of
-  * object you're dealing with :-)
-  */
-  void DumpInterface(IUnknown* unknown);
-
-#else  // #ifdef _DEBUG
-
-  #ifdef ASSERT_IN_RELEASE
-    bool ReleaseAssert(const char *expr,
-                       const TCHAR *msg,
-                       const char *filename,
-                       int32 linenumber);
-    #define ASSERT(expr, msg) \
-        ((expr) ? 0 : ReleaseAssert(#expr, SPRINTF msg, __FILE__, __LINE__))
-  #else
-    #define ASSERT(expr, msg) 0
-  #endif
-
-  // VERIFY executes but does not check expression
-  #define VERIFY(expr, msg) \
-    do {                   \
-      (expr);              \
-    } while (0)
-  #define REPORT(expr, type, msg, id) \
-      ((expr) ? 0 : g_report_ids.ReleaseReport(id))
-  void ReleaseAbort(const TCHAR* msg,
-                    const char* filename,
-                    int32 linenumber,
-                    bool do_abort);
-  #define ABORT(msg) ReleaseAbort(SPRINTF msg, __FILE__, __LINE__, true)
-
-#endif  // #ifdef _DEBUG
-
-#define ASSERT1(expr) ASSERT(expr, (_T("")))
-#define VERIFY1(expr) VERIFY(expr, (_T("")))
-
-#ifdef __cplusplus
-}  // extern "C"{
-#endif
-
-#ifdef _DEBUG
-void ShowAssertDialog(const TCHAR *message, const TCHAR *title);
-
-// used to automatically dump the global summary of reports when the
-// program exits
-class ReportSummaryGenerator {
- public:
-  ReportSummaryGenerator() {}
-  // calls DumpReportSummary()
-  ~ReportSummaryGenerator();
-  // some programs exit without calling destructors, they can use this function
-  // to dump the report summary
-  void DumpReportSummary();
-  // get text summary of reports
-  // caller is responsible for deleting the string returned
-  TCHAR *GetReportSummary();
-  DISALLOW_EVIL_CONSTRUCTORS(ReportSummaryGenerator);
-};
-
-extern ReportSummaryGenerator g_report_summary_generator;
-#endif
-
-// return string from format+arglist; for debugging; not thread-safe
-TCHAR * __cdecl SPRINTF(const TCHAR * format, ...);
-bool DebugError(const char * expr,
-                const TCHAR * message,
-                const char * filename,
-                INT linenumber,
-                BOOL report_only);
-
-// shows an error dialog in DEBUG and when g_release_debug is true
-//
-// example usage:
-//
-//  __try {
-//    do something that sometimes causes a known exception (e.g.,
-//    calling third party code)
-//  } __except(SehNoMinidump(GetExceptionCode(), GetExceptionInformation(),
-//      __FILE__, __LINE__)) {
-//    REPORT(false, R_ERROR, (L"exception doing something"), 103178920);
-//  }
-// show_message - show an error dialog in DEBUG and when g_release_debug is true
-int SehNoMinidump(unsigned int code, struct _EXCEPTION_POINTERS *ep,
-  const char *filename, int32 linenumber, bool show_message);
-
-/**
-* @return Always returns an error value.  If GetLastError is not ERROR_SUCCESS
-*   the function returns an HRESULT value derived from GetLastError()
-*/
-inline HRESULT GetCurError() {
-  return ::GetLastError() == ERROR_SUCCESS ?
-      E_FAIL : HRESULT_FROM_WIN32(::GetLastError());
-}
-
-// Returns the directory where the debugging module stores debug-related files.
-CString GetDebugDirectory();
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_DEBUG_H__
-
diff --git a/common/debug_unittest.cc b/common/debug_unittest.cc
deleted file mode 100644
index 634a79e..0000000
--- a/common/debug_unittest.cc
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Debug unittest
-
-#include <stdexcept>
-
-#include "omaha/common/debug.h"
-#include "omaha/common/test.h"
-#include "omaha/common/time.h"
-
-namespace omaha {
-
-// test what happens when we hit an exception
-int SEHExceptionTest(int level, int reserved) {
-  const uint32 kflags = MB_SETFOREGROUND  |
-                        MB_TOPMOST        |
-                        MB_ICONWARNING    |
-                        MB_OKCANCEL;
-  if (::MessageBox(NULL,
-                   L"do exception?",
-                   L"exception test",
-                   kflags) == IDOK) {
-    int *a1 = static_cast<int *>(1);
-    *a1 = 2;
-
-    // if that does not work try:
-    // simulate a divide by zero
-    int a = 10;
-    int b2 = a;
-    b2 /= 2;
-    b2 -= 5;
-    // int c = a/b2;
-    int c = 0;
-    TCHAR *s = 0;
-    s++;
-    *s = 0;
-
-    RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
-  }
-
-  return 0;
-}
-
-int SEHCatchExceptionTest(int level, int reserved) {
-  __try {
-    SEHExceptionTest(level, reserved);
-  } __except (SehSendMinidump(GetExceptionCode(),
-                              GetExceptionInformation(),
-                              kMinsTo100ns)) {
-  }
-
-  return 0;
-}
-
-#pragma warning(push)
-#pragma warning(disable:4702)
-// test what happens when we do a C++ exception
-int CppExceptionTest(int level, int reserved) {
-  _TRY_BEGIN
-#if 0
-    _THROW(std::logic_error, "throwing a fake logic_error");
-#else
-//    std::logic_error e("throwing a fake logic_error");
-  //    e._Raise();
-  std::runtime_error(std::string("throwing a fake logic_error"))._Raise();
-#endif
-  _CATCH(std::logic_error e)
-    ASSERT(false, (L"caught exception"));
-  _CATCH_END
-  return 0;
-}
-#pragma warning(pop)
-
-// test what happens when we do a REPORT
-int ReportTest(int level, int reserved) {
-  REPORT(false, R_ERROR, (L"test REPORT"), 592854117);
-  return 0;
-}
-
-// test what happens when we hit an ASSERT
-int AssertTest(int level, int reserved) {
-  ASSERT(false, (L"test ASSERT"));
-  return 0;
-}
-
-// test what happens when we hit an ABORT
-int AbortTest(int level, int reserved) {
-  ABORT((L"test ABORT"));
-  ASSERT(false, (L"returned from ABORT"));
-  return 0;
-}
-
-}  // namespace omaha
-
diff --git a/common/disk.cc b/common/disk.cc
deleted file mode 100644
index 242743a..0000000
--- a/common/disk.cc
+++ /dev/null
@@ -1,400 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Disk functions
-
-#include "omaha/common/disk.h"
-
-#include <winioctl.h>
-#include "omaha/common/const_config.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/localization.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/shell.h"
-#include "omaha/common/string.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/common/system.h"
-#include "omaha/common/timer.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-#define kNetdiskVendorId "netdisk"
-
-// see also: http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q264203
-
-#if _MSC_VER < 1400
-// Not defined in the headers we have; from MSDN:
-#define IOCTL_STORAGE_QUERY_PROPERTY \
-          CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
-
-typedef struct _STORAGE_DEVICE_DESCRIPTOR {
-  ULONG  Version;
-  ULONG  Size;
-  UCHAR  DeviceType;
-  UCHAR  DeviceTypeModifier;
-  BOOLEAN  RemovableMedia;
-  BOOLEAN  CommandQueueing;
-  ULONG  VendorIdOffset;
-  ULONG  ProductIdOffset;
-  ULONG  ProductRevisionOffset;
-  ULONG  SerialNumberOffset;
-  STORAGE_BUS_TYPE  BusType;
-  ULONG  RawPropertiesLength;
-  UCHAR  RawDeviceProperties[1];
-} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;
-
-typedef enum _STORAGE_QUERY_TYPE {
-  PropertyStandardQuery = 0,
-  PropertyExistsQuery,
-  PropertyMaskQuery,
-  PropertyQueryMaxDefined
-} STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE;
-
-typedef enum _STORAGE_PROPERTY_ID {
-  StorageDeviceProperty = 0,
-  StorageAdapterProperty,
-  StorageDeviceIdProperty
-} STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID;
-
-typedef struct _STORAGE_PROPERTY_QUERY {
-  STORAGE_PROPERTY_ID  PropertyId;
-  STORAGE_QUERY_TYPE  QueryType;
-  UCHAR  AdditionalParameters[1];
-} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
-
-// -------
-#endif
-
-#define kIoctlBufferSize 1024
-
-#define kMaxDrivesCached 3
-#define kMaxDriveLen 20
-static TCHAR g_cache_drive[kMaxDrivesCached][kMaxDriveLen+1];
-static bool g_cache_external[kMaxDrivesCached];
-static int g_cache_pos;
-static LLock g_cache_lock;
-
-bool IsDiskExternal(const TCHAR *drive) {
-  ASSERT(drive, (L""));
-  ASSERT(lstrlen(drive) < kMaxDriveLen, (L""));
-
-  DisableThreadErrorUI disable_error_dialog_box;
-
-  {
-    __mutexScope(g_cache_lock);
-    for (int i = 0; i < kMaxDrivesCached; i++)
-      if (!lstrcmp(drive, g_cache_drive[i])) {
-        UTIL_LOG(L1, (L"cached disk ext %s %d", drive, g_cache_external[i]));
-        return g_cache_external[i];
-      }
-  }
-
-#ifdef _DEBUG
-  Timer timer(true);
-#endif
-
-  byte buffer[kIoctlBufferSize+1];
-
-  bool external = false;
-  HANDLE device = ::CreateFile(drive,
-                               GENERIC_READ,
-                               FILE_SHARE_READ | FILE_SHARE_WRITE,
-                               NULL,
-                               OPEN_EXISTING,
-                               NULL,
-                               NULL);
-  if (device == INVALID_HANDLE_VALUE) {
-      UTIL_LOG(L1, (L"disk external could not open drive %s", drive));
-      goto done;
-  }
-  STORAGE_DEVICE_DESCRIPTOR *device_desc;
-  STORAGE_PROPERTY_QUERY query;
-  DWORD out_bytes;
-  query.PropertyId = StorageDeviceProperty;
-  query.QueryType = PropertyStandardQuery;
-  *(query.AdditionalParameters) = 0;
-
-  device_desc = reinterpret_cast<STORAGE_DEVICE_DESCRIPTOR*>(buffer);
-  // should not be needed, but just to be safer
-  ZeroMemory(buffer, kIoctlBufferSize);
-
-  BOOL ok = ::DeviceIoControl(device,
-                              IOCTL_STORAGE_QUERY_PROPERTY,
-                              &query,
-                              sizeof(STORAGE_PROPERTY_QUERY),
-                              buffer,
-                              kIoctlBufferSize,
-                              &out_bytes,
-                              (LPOVERLAPPED)NULL);
-
-  if (ok &&
-      device_desc->VendorIdOffset &&
-      stristr(reinterpret_cast<char*>(buffer + device_desc->VendorIdOffset),
-              kNetdiskVendorId)) {
-    external = true;
-    UTIL_LOG(L1, (L"ximeta netdisk %s", drive));
-  }
-
-  if (ok &&
-      (device_desc->BusType == BusTypeUsb ||
-       device_desc->BusType == BusType1394)) {
-    external = true;
-  }
-  if (!ok) {
-    UTIL_LOG(L1, (L"disk external ioctl failed %s", drive));
-  }
-  CloseHandle(device);
-  done:
-  UTIL_LOG(L1, (L"disk external %s %d time %s",
-      drive, external, String_DoubleToString(timer.GetMilliseconds(), 3)));
-
-  {
-    __mutexScope(g_cache_lock);
-    lstrcpyn(g_cache_drive[g_cache_pos], drive, kMaxDriveLen+1);
-    g_cache_external[g_cache_pos] = external;
-    if (++g_cache_pos >= kMaxDrivesCached) g_cache_pos = 0;
-  }
-
-  return external;
-}
-
-// find the first fixed local disk with at least the space requested
-// confirms that we can create a directory on the drive
-// returns the drive in the drive parameter
-// returns E_FAIL if no drive with enough space could be found
-HRESULT FindFirstLocalDriveWithEnoughSpace(const uint64 space_required,
-                                           CString *drive) {
-  ASSERT1(drive);
-
-  DisableThreadErrorUI disable_error_dialog_box;
-
-  const int kMaxNumDrives = 26;
-  static const size_t kBufLen = (STR_SIZE("c:\\\0") * kMaxNumDrives) + 1;
-
-  // obtain the fixed system drives
-  TCHAR buf[kBufLen];
-  DWORD str_len = ::GetLogicalDriveStrings(kBufLen, buf);
-  if (str_len > 0 && str_len < kBufLen) {
-    for (TCHAR* ptr = buf; *ptr != L'\0'; ptr += (lstrlen(ptr) + 1)) {
-      UINT drive_type = GetDriveType(ptr);
-      if (drive_type == DRIVE_FIXED) {
-        CString test_drive(ptr);
-        if (!IsDiskExternal(CString(L"\\\\?\\") + test_drive.Left(2))) {
-          uint64 free_disk_space = 0;
-          HRESULT hr = GetFreeDiskSpace(test_drive, &free_disk_space);
-
-          if (SUCCEEDED(hr) && space_required <= free_disk_space) {
-            CString temp_dir;
-            // confirm that we can create a directory on this drive
-            bool found = false;
-            while (!found) {
-              temp_dir = test_drive +
-                         NOTRANSL(L"test") +
-                         itostr(static_cast<uint32>(::GetTickCount()));
-              if (!File::Exists (temp_dir)) found = true;
-            }
-
-            if (SUCCEEDED(CreateDir(temp_dir, NULL))) {
-              VERIFY1(SUCCEEDED(DeleteDirectory(temp_dir)));
-              *drive = test_drive;
-              UTIL_LOG(L1, (L"drive %s enough space %d", test_drive.GetString(),
-                            free_disk_space));
-              return S_OK;
-            }
-          }
-        }
-      }
-    }
-  }
-
-  return E_FAIL;
-}
-
-// Get free disk space of a drive containing the specified folder
-HRESULT GetFreeDiskSpace(uint32 csidl, uint64* free_disk_space) {
-  ASSERT1(free_disk_space);
-
-  CString path;
-  RET_IF_FAILED(Shell::GetSpecialFolder(csidl, false, &path));
-
-  return GetFreeDiskSpace(path, free_disk_space);
-}
-
-// Get free disk space of a drive containing the specified folder
-HRESULT GetFreeDiskSpace(const TCHAR* folder, uint64* free_disk_space) {
-  ASSERT1(folder && *folder);
-  ASSERT1(free_disk_space);
-
-  DisableThreadErrorUI disable_error_dialog_box;
-
-  CString drive(folder);
-
-  // (Stupid API used by System::GetDiskStatistics will work with any folder -
-  // as long as it EXISTS.  Since the data storage folder might not exist yet
-  // (e.g., on a clean install) we'll just truncate it down to a drive letter.)
-  drive = drive.Left(3);  // "X:\"
-  ASSERT1(String_EndsWith(drive, _T(":\\"), false));
-
-  // Get the free disk space available to this user on this drive
-  uint64 free_bytes_current_user = 0LL;
-  uint64 total_bytes_current_user = 0LL;
-  uint64 free_bytes_all_users = 0LL;
-  RET_IF_FAILED(System::GetDiskStatistics(drive,
-                                          &free_bytes_current_user,
-                                          &total_bytes_current_user,
-                                          &free_bytes_all_users));
-
-  *free_disk_space = std::min(free_bytes_current_user, free_bytes_all_users);
-
-  return S_OK;
-}
-
-// Has enough free disk space on a drive containing the specified folder
-HRESULT HasEnoughFreeDiskSpace(uint32 csidl, uint64 disk_space_needed) {
-  uint64 free_disk_space = 0;
-  if (SUCCEEDED(GetFreeDiskSpace(csidl, &free_disk_space))) {
-    return (disk_space_needed <= free_disk_space) ?
-           S_OK : CI_E_NOT_ENOUGH_DISK_SPACE;
-  }
-  return S_OK;
-}
-
-// Has enough free disk space on a drive containing the specified folder
-HRESULT HasEnoughFreeDiskSpace(const TCHAR* folder, uint64 disk_space_needed) {
-  uint64 free_disk_space = 0;
-  if (SUCCEEDED(GetFreeDiskSpace(folder, &free_disk_space))) {
-    return (disk_space_needed <= free_disk_space) ?
-           S_OK : CI_E_NOT_ENOUGH_DISK_SPACE;
-  }
-  return S_OK;
-}
-
-// The ::CreateFile() call will fail for mapped local drives (subst).
-bool IsHotPluggable(const TCHAR* drive) {
-  ASSERT(drive, (L""));
-
-  // Disable potential error dialogs during this check
-  DisableThreadErrorUI disable_error_dialog_box;
-
-  //
-  // We set the default return value to true so that
-  // we treat the disk as hot-pluggable in case we
-  // don't know.
-  //
-  bool ret = true;
-
-  if (drive && lstrlen(drive) >= 2) {
-    CString volume_path(_T("\\\\.\\"));
-    // We don't want the trailing backslash.
-    volume_path.Append(drive, 2);
-
-    CHandle volume(::CreateFile(volume_path, GENERIC_READ,
-                                FILE_SHARE_READ | FILE_SHARE_WRITE,
-                                NULL, OPEN_EXISTING, 0, NULL));
-
-    if (volume != INVALID_HANDLE_VALUE) {
-      STORAGE_HOTPLUG_INFO shi = {0};
-      shi.Size = sizeof(shi);
-      DWORD bytes_returned = 0;
-      if (::DeviceIoControl(volume, IOCTL_STORAGE_GET_HOTPLUG_INFO,  NULL, 0,
-                            &shi, sizeof(STORAGE_HOTPLUG_INFO), &bytes_returned,
-                            NULL)) {
-          ret = (shi.DeviceHotplug != false);
-      } else {
-        UTIL_LOG(LW, (_T("[::DeviceIoControl failed][%u]"), ::GetLastError()));
-      }
-    } else {
-      UTIL_LOG(LW, (_T("[::CreateFile failed][%u]"), ::GetLastError()));
-    }
-  } else {
-    ASSERT(false, (L"Invalid path"));
-  }
-
-  return ret;
-}
-
-bool IsLargeDrive(const TCHAR* drive) {
-  ASSERT1(drive && *drive);
-
-  DisableThreadErrorUI disable_error_dialog_box;
-
-  ULARGE_INTEGER caller_free_bytes = {0};
-  ULARGE_INTEGER total_bytes = {0};
-  ULARGE_INTEGER total_free_bytes = {0};
-
-  if (!::GetDiskFreeSpaceEx(drive,
-                            &caller_free_bytes,
-                            &total_bytes,
-                            &total_free_bytes)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[IsLargeDrive - failed to GetDiskFreeSpaceEx][0x%x]"), hr));
-    return false;
-  }
-
-  return (total_bytes.QuadPart > kLargeDriveSize);
-}
-
-HRESULT DevicePathToDosPath(const TCHAR* device_path, CString* dos_path) {
-  ASSERT1(device_path);
-  ASSERT1(dos_path);
-  UTIL_LOG(L4, (_T("[DevicePathToDosPath][device_path=%s]"), device_path));
-
-  dos_path->Empty();
-
-  TCHAR drive_strings[MAX_PATH] = _T("");
-  if (!::GetLogicalDriveStrings(arraysize(drive_strings), drive_strings)) {
-    UTIL_LOG(L4, (_T("[DevicePathToDosPath-GetLogicalDriveStrings fail][0x%x]"),
-                  HRESULTFromLastError()));
-    return HRESULTFromLastError();
-  }
-
-  // Drive strings are stored as a set of null terminated strings, with an
-  // extra null after the last string. Each drive string is of the form "C:\".
-  // We convert it to the form "C:", which is the format expected by
-  // ::QueryDosDevice().
-  TCHAR drive_colon[3] = _T(" :");
-  for (const TCHAR* next_drive_letter = drive_strings;
-       *next_drive_letter;
-       next_drive_letter += _tcslen(next_drive_letter) + 1) {
-    // Dos device of the form "C:".
-    *drive_colon = *next_drive_letter;
-    TCHAR device_name[MAX_PATH] = _T("");
-    if (!::QueryDosDevice(drive_colon, device_name, arraysize(device_name))) {
-      UTIL_LOG(LEVEL_ERROR, (_T("[QueryDosDevice failed][0x%x]"),
-                             HRESULTFromLastError()));
-      continue;
-    }
-
-    UTIL_LOG(L4, (_T("[DevicePathToDosPath found drive]")
-                  _T("[logical drive %s][device name %s]"),
-                  drive_colon, device_name));
-
-    size_t name_length = _tcslen(device_name);
-    if (_tcsnicmp(device_path, device_name, name_length) == 0) {
-      // Construct DOS path.
-      dos_path->Format(_T("%s%s"), drive_colon, device_path + name_length);
-      UTIL_LOG(L4, (_T("[DevicePathToDosPath][dos_path=%s]"), *dos_path));
-      return S_OK;
-    }
-  }
-
-  return HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE);
-}
-
-}  // namespace omaha
diff --git a/common/disk_unittest.cc b/common/disk_unittest.cc
deleted file mode 100644
index 149d13d..0000000
--- a/common/disk_unittest.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2004-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <shlobj.h>
-#include <psapi.h>
-
-#include "omaha/common/app_util.h"
-#include "omaha/common/const_utils.h"
-#include "omaha/common/disk.h"
-#include "omaha/common/file.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(DiskTest, GetFreeDiskSpace) {
-  uint64 bytes_available = 0;
-  EXPECT_SUCCEEDED(GetFreeDiskSpace(_T("C:\\"), &bytes_available));
-
-  bytes_available = 0;
-  EXPECT_SUCCEEDED(GetFreeDiskSpace(CSIDL_PROGRAM_FILES, &bytes_available));
-}
-
-TEST(DiskTest, Disk_SystemDrive) {
-  TCHAR system_drive[MAX_PATH] = _T("%SystemDrive%");
-  EXPECT_TRUE(::ExpandEnvironmentStrings(system_drive, system_drive, MAX_PATH));
-  EXPECT_TRUE(::PathAddBackslash(system_drive));
-
-  if (vista_util::IsUserAdmin()) {
-    // System drive should not be hot-pluggable
-    EXPECT_FALSE(IsHotPluggable(system_drive));
-  }
-
-  // System drive is expected to be > 4GB.
-  EXPECT_TRUE(IsLargeDrive(system_drive));
-}
-
-TEST(DiskTest, Disk_FirstLargeLocalDrive) {
-  // Preferred amount of disk space for data (choose first location if found).
-  // Ideally this would be 1 GB, but some test VMs have little free space.
-  const int kDesiredSpace = 100 * 1000 * 1000;  // 100 MB
-
-  CString drive;
-  EXPECT_SUCCEEDED(FindFirstLocalDriveWithEnoughSpace(kDesiredSpace, &drive));
-  EXPECT_TRUE(IsLargeDrive(drive));
-}
-
-// TODO(omaha): Make this work on mapped drives. See http://b/1076675.
-TEST(DiskTest, DISABLED_DevicePathToDosPath) {
-  TCHAR image_name[MAX_PATH] = _T("");
-  EXPECT_TRUE(::GetProcessImageFileName(::GetCurrentProcess(),
-                                        image_name,
-                                        arraysize(image_name)) != 0);
-
-  CString dos_name;
-  EXPECT_SUCCEEDED(DevicePathToDosPath(image_name, &dos_name));
-  EXPECT_TRUE(File::Exists(dos_name));
-
-  EXPECT_EQ(dos_name.CompareNoCase(app_util::GetCurrentModulePath()), 0);
-}
-
-}  // namespace omaha
diff --git a/common/dynamic_link_dbghelp.cc b/common/dynamic_link_dbghelp.cc
deleted file mode 100644
index 1d54d93..0000000
--- a/common/dynamic_link_dbghelp.cc
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// dynamic loading of dbghelp dll functions
-
-#include "omaha/common/debug.h"
-#include "omaha/common/dynamic_link_dbghelp.h"
-
-namespace omaha {
-
-BOOL (CALLBACK *Dbghelp::SymInitialize)(HANDLE, PCSTR, BOOL);
-BOOL (CALLBACK *Dbghelp::SymCleanup)(HANDLE);
-BOOL (CALLBACK *Dbghelp::SymEnumSymbols)(HANDLE, ULONG64, PCSTR, PSYM_ENUMERATESYMBOLS_CALLBACK, PVOID);
-DWORD (CALLBACK *Dbghelp::SymSetOptions)(DWORD);
-BOOL (CALLBACK *Dbghelp::SymSetContext)(HANDLE, PIMAGEHLP_STACK_FRAME, PIMAGEHLP_CONTEXT);
-BOOL (CALLBACK *Dbghelp::SymGetLineFromAddr)(HANDLE, DWORD, PDWORD, PIMAGEHLP_LINE);
-BOOL (CALLBACK *Dbghelp::SymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
-BOOL (CALLBACK *Dbghelp::SymFromAddr)(HANDLE, DWORD64, PDWORD64, PSYMBOL_INFO);
-BOOL (CALLBACK *Dbghelp::StackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64,
-        PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64);
-BOOL (CALLBACK *Dbghelp::StackWalk)(DWORD, HANDLE, HANDLE, LPSTACKFRAME, PVOID, PREAD_PROCESS_MEMORY_ROUTINE,
-        PFUNCTION_TABLE_ACCESS_ROUTINE, PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE);
-PVOID (CALLBACK *Dbghelp::SymFunctionTableAccess64)(HANDLE, DWORD64);
-PVOID (CALLBACK *Dbghelp::SymFunctionTableAccess)(HANDLE, DWORD);
-DWORD64 (CALLBACK *Dbghelp::SymGetModuleBase64)(HANDLE, DWORD64);
-DWORD64 (CALLBACK *Dbghelp::SymGetModuleBase)(HANDLE, DWORD);
-BOOL (CALLBACK *Dbghelp::SymGetTypeInfo)(HANDLE, DWORD64, ULONG, IMAGEHLP_SYMBOL_TYPE_INFO, PVOID);
-BOOL (CALLBACK *Dbghelp::MiniDumpWriteDump)(HANDLE, DWORD, HANDLE, MINIDUMP_TYPE, PMINIDUMP_EXCEPTION_INFORMATION,
-        PMINIDUMP_USER_STREAM_INFORMATION, PMINIDUMP_CALLBACK_INFORMATION);
-
-Dbghelp::LoadedState Dbghelp::loaded_state_ = Dbghelp::NOT_LOADED;
-HINSTANCE Dbghelp::library_ = NULL;
-
-template <typename T>
-bool Dbghelp::GPA(const char * function_name, T& function_pointer) {
-  ASSERT(function_name, (L""));
-
-  function_pointer = reinterpret_cast<T>(::GetProcAddress(library_,
-                                                          function_name));
-  return function_pointer != NULL;
-}
-
-HRESULT Dbghelp::Load() {
-  // If we've already tried to load, don't try again.
-  if (loaded_state_ != NOT_LOADED) goto Exit;
-
-  Clear();
-
-  // UTIL_LOG((L2, _T("dbghelp loading")));
-  library_ = ::LoadLibrary(_T("dbghelp"));
-  // UTIL_LOG((L2, _T("dbghelp loaded")));
-
-  if (!library_) return E_FAIL;
-
-  bool all_valid = (GPA("SymInitialize", SymInitialize))
-    & (GPA("SymCleanup", SymCleanup))
-    & (GPA("SymSetOptions", SymSetOptions))
-    & (GPA("SymGetLineFromAddr", SymGetLineFromAddr))
-    & (GPA("SymGetLineFromAddr64", SymGetLineFromAddr64))
-    & (GPA("StackWalk", StackWalk))
-    & (GPA("StackWalk64", StackWalk64))
-    & (GPA("SymFunctionTableAccess", SymFunctionTableAccess))
-    & (GPA("SymFunctionTableAccess64", SymFunctionTableAccess64))
-    & (GPA("SymGetModuleBase", SymGetModuleBase))
-    & (GPA("SymGetModuleBase64", SymGetModuleBase64))
-    & (GPA("MiniDumpWriteDump", MiniDumpWriteDump));
-
-  // These are not supported in the Win2k version of DbgHelp;
-  // failing to load them is not an error.
-  GPA("SymEnumSymbols", SymEnumSymbols);
-  GPA("SymGetTypeInfo", SymGetTypeInfo);
-  GPA("SymSetContext", SymSetContext);
-  GPA("SymFromAddr", SymFromAddr);
-
-  if (!all_valid) Unload();
-  loaded_state_ = all_valid ? LOAD_SUCCEEDED : LOAD_FAILED;
-
- Exit:
-  return (loaded_state_ == LOAD_SUCCEEDED) ? S_OK : E_FAIL;
-}
-
-void Dbghelp::Unload() {
-  if (library_) {
-    VERIFY(::FreeLibrary(library_), (L""));
-
-    // Must set library_ to NULL so that Loaded() will return FALSE.
-    library_ = NULL;
-    }
-  Clear();
-}
-
-void Dbghelp::Clear() {
-  // just clear the main entry points
-  SymInitialize = NULL;
-  MiniDumpWriteDump = NULL;
-}
-
-}  // namespace omaha
-
diff --git a/common/dynamic_link_kernel32.cc b/common/dynamic_link_kernel32.cc
deleted file mode 100644
index c440479..0000000
--- a/common/dynamic_link_kernel32.cc
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// dynamic loading of Windows kernel32.dll API dll functions
-// wrappers for win32 functions not supported on windows 95/98/ME
-
-#include "omaha/common/debug.h"
-#include "omaha/common/dynamic_link_kernel32.h"
-
-namespace omaha {
-
-typedef BOOL (WINAPI * Module32FirstFunc)(HANDLE, LPMODULEENTRY32);
-typedef BOOL (WINAPI * Module32NextFunc)(HANDLE, LPMODULEENTRY32);
-typedef BOOL (WINAPI * Process32FirstFunc)(HANDLE, LPPROCESSENTRY32);
-typedef BOOL (WINAPI * Process32NextFunc)(HANDLE, LPPROCESSENTRY32);
-typedef BOOL (WINAPI * IsWow64ProcessFunc)(HANDLE, PBOOL);
-
-#define kKernel32Module L"kernel32"
-
-BOOL WINAPI Kernel32::Module32First(HANDLE hSnapshot, LPMODULEENTRY32 lpme) {
-  static Module32FirstFunc f = NULL;
-
-  if (f == NULL) {
-    HMODULE handle = GetModuleHandle(kKernel32Module);
-    ASSERT(handle, (L""));
-    f = (Module32FirstFunc) GetProcAddress(handle, "Module32FirstW");
-  }
-
-  if (f == NULL)
-    return FALSE;
-
-  return f(hSnapshot, lpme);
-}
-
-BOOL WINAPI Kernel32::Module32Next(HANDLE hSnapshot, LPMODULEENTRY32 lpme) {
-  static Module32NextFunc f = NULL;
-
-  if (f == NULL) {
-    HMODULE handle = GetModuleHandle(kKernel32Module);
-    ASSERT(handle, (L""));
-    f = (Module32NextFunc) GetProcAddress(handle, "Module32NextW");
-  }
-
-  if (f == NULL)
-    return FALSE;
-
-  return f(hSnapshot, lpme);
-}
-
-BOOL WINAPI Kernel32::Process32First(HANDLE hSnapshot, LPPROCESSENTRY32 lppe) {
-  static Process32FirstFunc f = NULL;
-
-  if (f == NULL) {
-    HMODULE handle = GetModuleHandle(kKernel32Module);
-    ASSERT(handle, (L""));
-    f = (Process32FirstFunc) GetProcAddress(handle, "Process32FirstW");
-  }
-
-  if (f == NULL)
-    return FALSE;
-
-  return f(hSnapshot, lppe);
-}
-
-BOOL WINAPI Kernel32::Process32Next(HANDLE hSnapshot, LPPROCESSENTRY32 lppe) {
-  static Process32NextFunc f = NULL;
-
-  if (f == NULL) {
-    HMODULE handle = GetModuleHandle(kKernel32Module);
-    ASSERT(handle, (L""));
-    f = (Process32NextFunc) GetProcAddress(handle, "Process32NextW");
-  }
-
-  if (f == NULL)
-    return FALSE;
-
-  return f(hSnapshot, lppe);
-}
-
-BOOL WINAPI Kernel32::IsWow64Process(HANDLE hProcess, PBOOL Wow64Process) {
-  static IsWow64ProcessFunc f = NULL;
-
-  if (f == NULL) {
-    HMODULE handle = GetModuleHandle(kKernel32Module);
-    ASSERT(handle, (L""));
-    f = (IsWow64ProcessFunc) GetProcAddress(handle, "IsWow64Process");
-  }
-
-  if (f == NULL)
-    return FALSE;
-
-  return f(hProcess, Wow64Process);
-}
-
-}  // namespace omaha
-
diff --git a/common/dynamic_link_kernel32_unittest.cc b/common/dynamic_link_kernel32_unittest.cc
deleted file mode 100644
index 6df570c..0000000
--- a/common/dynamic_link_kernel32_unittest.cc
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// dynamic link kernel 32 unittest
-
-#include "omaha/common/dynamic_link_kernel32.h"
-#include "omaha/common/system_info.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(DynamicLinkKernel32Test, DynamicLinkKernel32) {
-  BOOL is64(0);
-  ASSERT_TRUE(Kernel32::IsWow64Process(GetCurrentProcess(), &is64));
-  ASSERT_EQ(static_cast<BOOL>(SystemInfo::IsRunningOn64Bit()), is64);
-}
-
-}  // namespace omaha
diff --git a/common/encrypt.cc b/common/encrypt.cc
deleted file mode 100644
index 4d5a758..0000000
--- a/common/encrypt.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include "omaha/common/encrypt.h"
-
-#include <vector>
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-
-namespace omaha {
-
-namespace encrypt {
-
-// TODO(omaha): consider loading crypt32.dll dynamically, as these functions
-// are used infrequently.
-
-HRESULT EncryptData(const void* key, size_t key_len,
-                    const void* data, size_t data_len,
-                    std::vector<uint8>* data_out) {
-  // key may be null.
-  ASSERT1(data);
-  ASSERT1(data_out);
-  DATA_BLOB blob_out = {0, NULL};
-  DATA_BLOB blob_in = { data_len, static_cast<BYTE*>(const_cast<void*>(data)) };
-  DATA_BLOB entropy = { 0, NULL };
-  if (key != NULL && key_len != 0) {
-    entropy.cbData = key_len;
-    entropy.pbData = static_cast<BYTE*>(const_cast<void*>(key));
-  }
-
-  // The description parameter is required on W2K.
-  if (!::CryptProtectData(&blob_in, _T("gupdate"), &entropy, NULL, NULL,
-                          CRYPTPROTECT_UI_FORBIDDEN, &blob_out)) {
-    return HRESULTFromLastError();
-  }
-
-  data_out->clear();
-  const uint8* first = reinterpret_cast<const uint8*>(blob_out.pbData);
-  const uint8* last = first + blob_out.cbData;
-  data_out->insert(data_out->begin(), first, last);
-  ::LocalFree(blob_out.pbData);
-
-  ASSERT1(data_out->size() == blob_out.cbData);
-  return S_OK;
-}
-
-HRESULT DecryptData(const void* key, size_t key_len,
-                    const void* data, size_t data_len,
-                    std::vector<uint8>* data_out) {
-  // key may be null.
-  ASSERT1(data);
-  ASSERT1(data_out);
-
-  DATA_BLOB blob_out = {0, NULL};
-  DATA_BLOB blob_in = { data_len, static_cast<BYTE*>(const_cast<void*>(data)) };
-  DATA_BLOB entropy = { 0, NULL };
-
-  if (key != NULL && key_len != 0) {
-    entropy.cbData = key_len;
-    entropy.pbData = static_cast<BYTE*>(const_cast<void*>(key));
-  }
-
-  if (!::CryptUnprotectData(&blob_in, NULL, &entropy, NULL, NULL,
-                            CRYPTPROTECT_UI_FORBIDDEN, &blob_out)) {
-    return (::GetLastError() != ERROR_SUCCESS) ?
-        HRESULTFromLastError() : HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
-  }
-
-  data_out->clear();
-  const uint8* first = reinterpret_cast<const uint8*>(blob_out.pbData);
-  const uint8* last = first + blob_out.cbData;
-  data_out->insert(data_out->begin(), first, last);
-  ::LocalFree(blob_out.pbData);
-
-  ASSERT1(data_out->size() == blob_out.cbData);
-  return S_OK;
-}
-
-}  // namespace encrypt
-
-}  // namespace omaha
-
diff --git a/common/encrypt_test.cc b/common/encrypt_test.cc
deleted file mode 100644
index 55ca9b2..0000000
--- a/common/encrypt_test.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <omaha/common/encrypt.h>
-#include <cstring>
-#include <vector>
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-namespace encrypt {
-
-TEST(EncryptTest, Test) {
-  const char* plaintext = "the quick brown fox jumps over the lazy dog";
-  std::vector<uint8> ciphertext;
-  EXPECT_HRESULT_SUCCEEDED(EncryptData(NULL, 0,
-                                       plaintext, strlen(plaintext),
-                                       &ciphertext));
-  std::vector<uint8> decrypted_text;
-  EXPECT_HRESULT_SUCCEEDED(DecryptData(NULL, 0,
-                                       &ciphertext.front(), ciphertext.size(),
-                                       &decrypted_text));
-  EXPECT_EQ(decrypted_text.size(), strlen(plaintext));
-  decrypted_text.push_back(0);
-  EXPECT_STREQ(reinterpret_cast<char*>(&decrypted_text.front()), plaintext);
-
-
-  const char* key = "foobar";
-  ciphertext.clear();
-  EXPECT_HRESULT_SUCCEEDED(EncryptData(key, strlen(key),
-                                       plaintext, strlen(plaintext),
-                                       &ciphertext));
-  decrypted_text.clear();
-  EXPECT_HRESULT_SUCCEEDED(DecryptData(key, strlen(key),
-                                       &ciphertext.front(), ciphertext.size(),
-                                       &decrypted_text));
-  EXPECT_EQ(decrypted_text.size(), strlen(plaintext));
-  decrypted_text.push_back(0);
-  EXPECT_STREQ(reinterpret_cast<char*>(&decrypted_text.front()), plaintext);
-}
-
-}  // namespace encrypt
-
-}  // namespace omaha
-
diff --git a/common/error.cc b/common/error.cc
deleted file mode 100644
index 1838882..0000000
--- a/common/error.cc
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/error.h"
-#include <winhttp.h>
-#include "base/basictypes.h"
-#include "omaha/common/debug.h"
-
-namespace omaha {
-
-HRESULT HRESULTFromLastError() {
-  DWORD error_code = ::GetLastError();
-  ASSERT1(error_code != NO_ERROR);
-  return (error_code != NO_ERROR) ? HRESULT_FROM_WIN32(error_code) : E_FAIL;
-}
-
-HRESULT HRESULTFromHttpStatusCode(int status_code) {
-  COMPILE_ASSERT(GOOPDATE_E_NETWORK_FIRST + HTTP_STATUS_LAST <
-                 GOOPDATE_E_NETWORK_LAST,
-                 GOOPDATE_E_NETWORK_LAST_too_small);
-  HRESULT hr = S_OK;
-  if (HTTP_STATUS_FIRST <= status_code && status_code <= HTTP_STATUS_LAST) {
-    hr = GOOPDATE_E_NETWORK_FIRST + status_code;
-  } else {
-    hr = GOOPDATE_E_NETWORK_LAST;
-  }
-  ASSERT1(HRESULT_FACILITY(hr) == FACILITY_ITF);
-  return hr;
-}
-
-}  // namespace omaha
-
diff --git a/common/error.h b/common/error.h
deleted file mode 100644
index c83c6f6..0000000
--- a/common/error.h
+++ /dev/null
@@ -1,434 +0,0 @@
-// Copyright 2003-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Error codes and HRESULTS
-//
-// TODO(omaha): reduce the number of custom error codes below by searching and
-// seeing what is handled and what is not.
-//
-// TODO(omaha): rename CI and GOOPDATE to OMAHA.
-
-#ifndef OMAHA_COMMON_ERROR_H_
-#define OMAHA_COMMON_ERROR_H_
-
-#include <windows.h>
-
-namespace omaha {
-
-// Returns the last error as an HRESULT or E_FAIL if last error is NO_ERROR.
-// This is not a drop in replacement for the HRESULT_FROM_WIN32 macro.
-// The macro maps a NO_ERROR to S_OK, whereas the function below maps a
-// NO_ERROR to E_FAIL. Also, the macro is evaluating arguments multiple times.
-HRESULT HRESULTFromLastError();
-
-// Returns the http status_code as an HRESULT.
-HRESULT HRESULTFromHttpStatusCode(int status_code);
-
-// HRESULTs
-//
-// Top bit indicates success (0) or failure (1)
-// 16 bits available for 'code' field at end
-
-#define kFacilityCi 67
-
-#define CI_E_NOT_ENOUGH_DISK_SPACE                \
-    MAKE_HRESULT(SEVERITY_ERROR, kFacilityCi, 0x0003)
-
-// CI_E_INVALID_MANIFEST is returned when the manifest file is missing a
-// required field or has some other kind of semantic error
-#define CI_E_INVALID_MANIFEST                     \
-    MAKE_HRESULT(SEVERITY_ERROR, kFacilityCi, 0x0012)
-
-// CI_E_XML_LOAD_ERROR is returned when MSXML can't load the document into DOM.
-// It may be because the document is not well formed (tags, namespaces, etc)
-#define CI_E_XML_LOAD_ERROR                       \
-    MAKE_HRESULT(SEVERITY_ERROR, kFacilityCi, 0x0013)
-
-// CI_E_INVALID_ARG is returned when invalid command line arugment is specified
-#define CI_E_INVALID_ARG                          \
-    MAKE_HRESULT(SEVERITY_ERROR, kFacilityCi, 0x001B)
-
-// CI_E_PROXY_AUTH_REQUIRED is returned when proxy authentication is required
-#define CI_E_PROXY_AUTH_REQUIRED                  \
-    MAKE_HRESULT(SEVERITY_ERROR, kFacilityCi, 0x0028)
-
-// CI_E_INVALID_PROXY_AUTH_SCHEME is returned when the proxy authentication
-// scheme is unknown or not supported
-#define CI_E_INVALID_PROXY_AUTH_SCHEME            \
-    MAKE_HRESULT(SEVERITY_ERROR, kFacilityCi, 0x0029)
-
-// CI_E_BITS_DISABLED is returned by the download manager when the BITS service
-// is not enabled.
-#define CI_E_BITS_DISABLED                        \
-    MAKE_HRESULT(SEVERITY_ERROR, kFacilityCi, 0x0030)
-
-// CI_E_HTTPS_CERT_FAILURE is returned when the https connection fails.
-// One cause of this is when the system clock is off by a significant
-// amount which makes the server certificate appear invalid.
-#define CI_E_HTTPS_CERT_FAILURE                   \
-    MAKE_HRESULT(SEVERITY_ERROR, kFacilityCi, 0x0035)
-
-// Return values from Process::WaitUntilDeadOrInterrupt
-// TODO(omaha) Move this constants to the Process class. They do not look like
-// error codes.
-#define CI_S_PROCESSWAIT_DEAD                     \
-    MAKE_HRESULT(SEVERITY_SUCCESS, kFacilityCi, 0x0100)
-#define CI_S_PROCESSWAIT_TIMEOUT                  \
-    MAKE_HRESULT(SEVERITY_SUCCESS, kFacilityCi, 0x0101)
-#define CI_S_PROCESSWAIT_MESSAGE                  \
-    MAKE_HRESULT(SEVERITY_SUCCESS, kFacilityCi, 0x0102)
-
-// Signatures error codes.
-#define SIGS_E_INVALID_PFX_CERTIFICATE            \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x0200)
-#define SIGS_E_INVALID_DER_CERTIFICATE            \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x0201)
-#define SIGS_E_INVALID_PASSWORD                   \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x0202)
-#define SIGS_E_INVALID_KEY_TYPE                   \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x0203)
-#define SIGS_E_INVALID_SIGNATURE                  \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x0204)
-
-// Goopdate XML parser error codes.
-#define GOOPDATEXML_E_STRTOUINT                   \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x300)
-#define GOOPDATEXML_E_RESPONSESNODE               \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x301)
-#define GOOPDATEXML_E_XMLVERSION                  \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x302)
-#define GOOPDATEXML_E_NEEDSADMIN                  \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x303)
-#define GOOPDATEXML_E_UNEXPECTED_URI              \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x304)
-#define GOOPDATEXML_E_TOO_MANY_APPS               \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x305)
-
-// Goopdate job queue error codes.
-// Errors 0x401 - 0x407 are legacy codes and should not be reused.
-
-// Download Manager custom error codes.
-// Obsolete: GOOPDATEDOWNLOAD_E_FILE_ALREADY_DOWNLOADED - 0x500.
-// Obsolete: GOOPDATEDOWNLOAD_E_FAILED_GET_ERROR - 0x501.
-#define GOOPDATEDOWNLOAD_E_INVALID_PATH             \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x502)
-#define GOOPDATEDOWNLOAD_E_CRACKURL_FAILED          \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x503)
-#define GOOPDATEDOWNLOAD_E_FILE_NAME_EMPTY          \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x504)
-#define GOOPDATEDOWNLOAD_E_DEST_FILE_PATH_EMPTY     \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x505)
-#define GOOPDATEDOWNLOAD_E_STORAGE_DIR_NOT_EXIST    \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x506)
-#define GOOPDATEDOWNLOAD_E_FILE_SIZE_ZERO           \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x507)
-#define GOOPDATEDOWNLOAD_E_FILE_SIZE_SMALLER        \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x508)
-#define GOOPDATEDOWNLOAD_E_FILE_SIZE_LARGER         \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x509)
-#define GOOPDATEDOWNLOAD_E_UNIQUE_FILE_PATH_EMPTY   \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x50A)
-#define GOOPDATEDOWNLOAD_E_DEST_PATH_EMPTY          \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x50B)
-#define GOOPDATEDOWNLOAD_E_DEST_FILENAME_EMPTY      \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x50C)
-#define GOOPDATEDOWNLOAD_E_FAILED_MOVE              \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x5FF)
-
-// Goopdate custom error codes.
-// Obsolete this error when legacy support is removed.
-#define GOOPDATE_E_NON_ADMINS_CANNOT_INSTALL_ADMIN  \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x600)
-// Obsolete: GOOPDATE_S_WORKER_ALREADY_RUNNING - 0x601.
-// Obsolete: GOOPDATE_E_INVALID_ARG_COMBINATION - 0x602.
-#define GOOPDATE_E_WORKER_ALREADY_RUNNING           \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x603)
-#define GOOPDATE_E_APP_BEING_INSTALLED              \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x604)
-#define GOOPDATE_E_RESOURCE_DLL_PATH_EMPTY          \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x605)
-
-// Setup and metainstaller custom error codes.
-#define GOOPDATE_E_NONADMIN_INSTALL_ADMIN_APP       \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x700)
-// Obsolete: GOOPDATE_E_MANIFEST_FILENAME_EMPTY - 0x701.
-// Obsolete: GOOPDATE_E_MANIFEST_FILE_DOES_NOT_EXIST - 0x702.
-// This error appears in the metainstaller.
-#define GOOPDATE_E_RUNNING_INFERIOR_WINDOWS         \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x703)
-#define GOOPDATE_E_RUNNING_INFERIOR_MSXML           \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x704)
-// Obsolete: GOOPDATE_E_ELEVATION_FAILED - 0x705
-#define GOOPDATE_E_FAILED_TO_GET_LOCK               \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x706)
-#define GOOPDATE_E_INSTANCES_RUNNING                \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x707)
-#define GOOPDATE_E_HANDOFF_FAILED                   \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x708)
-#define GOOPDATE_E_PATH_APPEND_FAILED               \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x709)
-#define GOOPDATE_E_SERVICE_NAME_EMPTY               \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x70a)
-#define GOOPDATE_E_SETUP_LOCK_INIT_FAILED           \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x70b)
-#define GOOPDATE_E_ACCESSDENIED_COPYING_CORE_FILES  \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x70c)
-#define GOOPDATE_E_ACCESSDENIED_COPYING_SHELL       \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x70d)
-#define GOOPDATE_E_ACCESSDENIED_STOP_PROCESSES      \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x70e)
-#define GOOPDATE_E_ACCESSDENIED_SETUP_REG_ACCESS    \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x70f)
-// User may have accidentally run the metainstaller twice.
-#define GOOPDATE_E_FAILED_TO_GET_LOCK_MATCHING_INSTALL_PROCESS_RUNNING  \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x710)
-#define GOOPDATE_E_FAILED_TO_GET_LOCK_NONMATCHING_INSTALL_PROCESS_RUNNING \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x711)
-#define GOOPDATE_E_FAILED_TO_GET_LOCK_UPDATE_PROCESS_RUNNING  \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x712)
-#define GOOPDATE_E_VERIFY_SIGNEE_IS_GOOGLE_FAILED   \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x713)
-// Obsolete: GOOPDATE_E_VERIFY_SIGNEE_IS_GOOGLE_FAILED_TIMESTAMP_CHECK - 0x714.
-#define GOOPDATE_E_INSTALL_ELEVATED_PROCESS_NEEDS_ELEVATION \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x715)
-#define GOOPDATE_E_ELEVATION_FAILED_ADMIN           \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x716)
-#define GOOPDATE_E_ELEVATION_FAILED_NON_ADMIN       \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x717)
-#define GOOPDATE_E_CANT_UNINSTALL                   \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x718)
-#define GOOPDATE_E_SILENT_INSTALL_NEEDS_ELEVATION   \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x719)
-#define GOOPDATE_E_OEM_NOT_MACHINE_AND_PRIVILEGED_AND_AUDIT_MODE  \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x71a)
-#define GOOPDATE_E_OEM_WITH_ONLINE_INSTALLER        \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x71b)
-// Obsolete: GOOPDATE_E_NON_OEM_INSTALL_IN_AUDIT_MODE - 0x71c
-#define GOOPDATE_E_OEM_INSTALL_SUCCEEDED_BUT_NOT_IN_OEM_INSTALLING_MODE \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x71d)
-#define GOOPDATE_E_EULA_REQURED_WITH_ONLINE_INSTALLER \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x71e)
-
-// Metainstaller custom error codes.
-#define GOOPDATE_E_UNTAGGED_METAINSTALLER           \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x750)
-
-
-// Obsolete: GOOPDATE_E_NO_SERVER_RESPONSE - 0x800.
-#define GOOPDATE_E_NO_NETWORK \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x801)
-// Obsolete: GOOPDATE_E_UPDATE_CHECK_FAILED - 0x802.
-// Obsolete: GOOPDATE_COULD_NOT_GET_IGOOGLEUPDATE - 0x803.
-// Obsolete: GOOPDATE_E_BAD_SERVER_RESPONSE - 0x804.
-#define GOOPDATE_E_UNKNOWN_SERVER_RESPONSE          \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x805)
-#define GOOPDATE_E_RESTRICTED_SERVER_RESPONSE       \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x806)
-// Obsolete: GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED - 0x807.
-// Obsolete: GOOPDATE_E_ABANDON_UPDATE            - 0x808.
-#define GOOPDATE_E_NO_UPDATE_RESPONSE               \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x809)
-#define GOOPDATE_E_BUNDLE_ERROR                     \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x80A)
-#define GOOPDATE_E_APP_UNINSTALLED                  \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x80B)
-#define GOOPDATE_E_CALL_INPROGRESS                  \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x80C)
-#define GOOPDATE_E_UNKNOWN_APP_SERVER_RESPONSE      \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x80D)
-#define GOOPDATE_E_INTERNAL_ERROR_SERVER_RESPONSE   \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x80E)
-#define GOOPDATE_E_NO_SERVER_RESPONSE               \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x80F)
-#define GOOPDATE_E_WORKER_CANCELLED                 \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x810)
-#define GOOPDATE_E_OS_NOT_SUPPORTED                 \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x811)
-#define GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY   \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x812)
-#define GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY    \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x813)
-#define GOOPDATE_E_APP_NOT_REGISTERED               \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x814)
-#define GOOPDATE_E_CANNOT_USE_NETWORK               \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x815)
-#define GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED  \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x816)
-// 0x817-9: reserved.
-// TODO(omaha): Rename other SERVER_RESPONSE errors to match these.
-#define GOOPDATE_E_SERVER_RESPONSE_NO_HASH \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x81a)
-#define GOOPDATE_E_SERVER_RESPONSE_UNSUPPORTED_PROTOCOL \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x81b)
-
-//
-// Network stack error codes.
-//
-
-// The CUP response is missing the ETag header containing the server proof.
-#define OMAHA_NET_E_CUP_NO_SERVER_PROOF            \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x880)
-
-// The CUP response is not trusted.
-#define OMAHA_NET_E_CUP_NOT_TRUSTED                \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x881)
-#define OMAHA_NET_E_REQUEST_CANCELLED              \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x882)
-
-// CUP could not instantiate an http client.
-#define OMAHA_NET_E_CUP_NO_HTTP_CLIENT              \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x883)
-
-// CUP could not generate random bytes.
-#define OMAHA_NET_E_CUP_NO_ENTROPY                  \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x884)
-
-// Non-CUP network errors.
-#define OMAHA_NET_E_WINHTTP_NOT_AVAILABLE           \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x890)
-
-// Install Manager custom error codes.
-#define GOOPDATEINSTALL_E_FILENAME_INVALID         \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x900)
-#define GOOPDATEINSTALL_E_INSTALLER_FAILED_START   \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x901)
-#define GOOPDATEINSTALL_E_INSTALLER_FAILED         \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x902)
-#define GOOPDATEINSTALL_E_INSTALLER_INTERNAL_ERROR \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x903)
-#define GOOPDATEINSTALL_E_INSTALLER_TIMED_OUT      \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x904)
-#define GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENT_KEY  \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x905)
-#define GOOPDATEINSTALL_E_CANNOT_GET_INSTALLER_LOCK           \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x906)
-#define GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING         \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x907)
-#define GOOPDATEINSTALL_E_INSTALLER_DID_NOT_CHANGE_VERSION    \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x908)
-#define GOOPDATE_E_INVALID_INSTALL_DATA_INDEX                 \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x909)
-#define GOOPDATE_E_INVALID_INSTALLER_DATA_IN_APPARGS          \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x90A)
-
-// GoopdateUtils custom error codes.
-#define GOOPDATEUTILS_E_BROWSERTYPE                 \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xA00)
-
-// GoogleUpdate.exe shell custom error codes.
-#define GOOGLEUPDATE_E_DLL_NOT_FOUND           \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xB00)
-#define GOOGLEUPDATE_E_VERIFY_SIGNEE_IS_GOOGLE_FAILED \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xB01)
-
-// Command line parse custom error codes.
-#define GOOGLEUPDATE_COMMANDLINE_E_NO_SCENARIO_HANDLER \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xC00)
-#define GOOGLEUPDATE_COMMANDLINE_E_NO_SCENARIO_HANDLER_MATCHED \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xC01)
-
-// OneClick custom error codes
-#define GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED        \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xD01)
-#define GOOPDATE_E_ONECLICK_NO_LANGUAGE_RESOURCE    \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xD02)
-
-// Usage stats / metrics error codes
-#define GOOPDATE_E_METRICS_LOCK_INIT_FAILED         \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xD80)
-#define GOOPDATE_E_METRICS_AGGREGATE_FAILED         \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xD81)
-
-// Core error codes
-#define GOOPDATE_E_CORE_INTERNAL_ERROR              \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xE00)
-#define GOOPDATE_E_CORE_MISSING_CMD                 \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xE01)
-
-// UI & Observer error codes
-#define GOOPDATE_E_UI_INTERNAL_ERROR                \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xE80)
-#define GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL  \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xE81)
-
-// ApplyTagTool error codes.
-#define APPLYTAG_E_ALREADY_TAGGED                   \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x1000)
-
-// The range [0x2000, 0x2400) is reserved for certain network stack errors
-// when the server returns an HTTP result code that is not a success code.
-// The size of the range is 1024, which is enough to map all the HTTP result
-// codes.
-#define GOOPDATE_E_NETWORK_FIRST                            \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x2000)
-
-// Http Status Code 401 -- Unauthorized.
-#define GOOPDATE_E_NETWORK_UNAUTHORIZED                     \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x2191)
-
-// Http Status Code 403 -- Forbidden.
-#define GOOPDATE_E_NETWORK_FORBIDDEN                        \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x2193)
-
-// Http Status Code 407 -- Proxy Authentication Required.
-#define GOOPDATE_E_NETWORK_PROXYAUTHREQUIRED                \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x2197)
-
-#define GOOPDATE_E_NETWORK_LAST                             \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x23FF)
-
-// Shared Memory Proxy error codes.
-#define GOOPDATE_E_INVALID_SHARED_MEMORY_PTR                \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x2401)
-#define GOOPDATE_E_INVALID_INTERFACE_MARSHAL_SIZE           \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x2402)
-
-// Crash handling error codes.
-
-// The crash reporting cannot start the crash server for
-// out-of-process crash handling.
-#define GOOPDATE_E_CRASH_START_SERVER_FAILED                 \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xFFFA)
-
-// The crash reporting cannot set the security descriptors for
-// a securable object.
-#define GOOPDATE_E_CRASH_SECURITY_FAILED                     \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xFFFB)
-
-// The crash reporting could not get the crash reports dir.
-#define GOOPDATE_E_CRASH_NO_DIR                             \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xFFFC)
-
-// The crash reporting failed due to the client side metering of the crashes.
-#define GOOPDATE_E_CRASH_THROTTLED                          \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xFFFD)
-
-// The crash reporting failed due to the server rejecting the crash.
-#define GOOPDATE_E_CRASH_REJECTED                           \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xFFFE)
-
-// Goopdate crash. The error code is returned when the process is terminated
-// due to a crash handled by breakpad.
-#define GOOPDATE_E_CRASH                            \
-    MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xFFFF)
-
-
-// This is the end of the range for FACILITY_ITF errors. Do not define errors
-// below.
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_ERROR_H_
-
diff --git a/common/error_unittest.cc b/common/error_unittest.cc
deleted file mode 100644
index b9df549..0000000
--- a/common/error_unittest.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include "omaha/common/commontypes.h"
-#include "omaha/common/error.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(ErrorTest, HRESULTFromLastError) {
-  ::SetLastError(ERROR_ACCESS_DENIED);
-  EXPECT_EQ(HRESULTFromLastError(), E_ACCESSDENIED);
-
-  ::SetLastError(static_cast<DWORD>(E_INVALIDARG));
-  EXPECT_EQ(HRESULTFromLastError(), E_INVALIDARG);
-}
-
-TEST(ErrorTest, HRESULTFromLastErrorAssert) {
-  ExpectAsserts expect_asserts;
-  ::SetLastError(0);
-  EXPECT_EQ(HRESULTFromLastError(), E_FAIL);
-}
-
-}  // namespace omaha
-
diff --git a/common/event_logger.cc b/common/event_logger.cc
new file mode 100644
index 0000000..54b1634
--- /dev/null
+++ b/common/event_logger.cc
@@ -0,0 +1,225 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include "omaha/common/event_logger.h"
+
+#include <sddl.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/user_info.h"
+#include "omaha/common/config_manager.h"
+
+namespace omaha {
+
+void LogEventHelper(WORD type, DWORD id, size_t count, const TCHAR** strings,
+                    const TCHAR* ctx) {
+  ASSERT1(count <= kint16max);
+  if (!ConfigManager::Instance()->CanLogEvents(type)) {
+    return;
+  }
+
+  // Include the circular logging buffer in the event log if the type is a
+  // warning or an error.
+  CStringA data(ctx);
+  CString context = GetLogging()->GetHistory();
+  if (!context.IsEmpty()) {
+    SafeCStringAAppendFormat(&data, "\n[More context: %S]", context);
+  }
+
+  HRESULT hr = EventLogger::ReportEvent(EventLogger::kSourceName,
+                                        type,
+                                        EventLogger::kDefaultCategory,
+                                        id,
+                                        static_cast<WORD>(count),
+                                        strings,
+                                        data.GetLength(),
+                                        data.GetBuffer());
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[Failed to log event][0x%08x]"), hr));
+  }
+}
+
+CString BuildEventSourceRegistryKeyName(const TCHAR* src_name) {
+  ASSERT1(src_name);
+  CString key_name;
+  SafeCStringFormat(&key_name,
+                    _T("HKLM\\SYSTEM\\CurrentControlSet\\Services\\EventLog\\")
+                    _T("Application\\%s"),
+                    src_name);
+  return key_name;
+}
+
+HRESULT EventLogger::AddEventSource(const TCHAR* src_name,
+                                    const TCHAR* msg_dll_path) {
+  ASSERT1(src_name);
+  if (!src_name) return E_INVALIDARG;
+  ASSERT1(msg_dll_path);
+  if (!msg_dll_path) return E_INVALIDARG;
+
+  // Create the event source as a subkey of the "Application" log.
+  RegKey reg_key;
+  HRESULT hr = reg_key.Create(BuildEventSourceRegistryKeyName(src_name));
+  if (FAILED(hr)) return hr;
+
+  // Set the name of the message file. RegKey class can't set REG_EXPAND_SZ
+  // values so we must use the low level OS call.
+  int result = ::RegSetValueEx(reg_key.Key(),
+                               _T("EventMessageFile"),
+                               0,
+                               REG_EXPAND_SZ,
+                               reinterpret_cast<const byte*>(msg_dll_path),
+                               (_tcslen(msg_dll_path) + 1) * sizeof(TCHAR));
+  if (result != ERROR_SUCCESS) return HRESULT_FROM_WIN32(result);
+
+  // Set the supported event types.
+  DWORD types = EVENTLOG_ERROR_TYPE |
+                EVENTLOG_WARNING_TYPE |
+                EVENTLOG_INFORMATION_TYPE;
+  hr = reg_key.SetValue(_T("TypesSupported"), types);
+  if (FAILED(hr)) return hr;
+
+  return S_OK;
+}
+
+HRESULT EventLogger::RemoveEventSource(const TCHAR* src_name) {
+  ASSERT1(src_name);
+  if (!src_name) return E_INVALIDARG;
+
+  // RegKey::DeleteKey  returns S_FALSE when attempting to delete
+  // a key that is not there.
+  HRESULT hr = RegKey::DeleteKey(BuildEventSourceRegistryKeyName(src_name),
+                                 false);
+  return SUCCEEDED(hr) ? S_OK : hr;
+}
+
+
+HRESULT EventLogger::ReportEvent(const TCHAR* src_name,
+                                 WORD type,
+                                 WORD category,
+                                 DWORD id,
+                                 WORD count,
+                                 const TCHAR** strings,
+                                 size_t buf_size,
+                                 void* buffer) {
+  ASSERT1(src_name);
+  ASSERT1(type == EVENTLOG_SUCCESS ||
+          type == EVENTLOG_ERROR_TYPE ||
+          type == EVENTLOG_WARNING_TYPE ||
+          type == EVENTLOG_INFORMATION_TYPE);
+
+  //  Opens the log on the local computer.
+  HANDLE hlog = ::RegisterEventSource(NULL, src_name);
+  if (!hlog) {
+    return HRESULTFromLastError();
+  }
+
+  // Best effort to get the sid for the current effective user. The event
+  // logging provides for logging the sid at no cost so that the user shows up
+  // in the event log.
+  CString sid_string;
+  VERIFY1(SUCCEEDED(user_info::GetEffectiveUserSid(&sid_string)));
+  PSID psid = NULL;
+  if (!sid_string.IsEmpty()) {
+    VERIFY1(::ConvertStringSidToSid(sid_string, &psid));
+    ASSERT1(psid);
+  }
+
+  HRESULT hr = E_FAIL;
+  if (::ReportEvent(hlog,       // Event log handle.
+                    type,       // Event type.
+                    category,   // Event category.
+                    id,         // Event identifier.
+                    psid,       // User security identifier.
+                    count,      // Number of substitution strings.
+                    buf_size,   // Size of binary data.
+                    strings,    // Pointer to strings.
+                    buffer)) {  // Binary data.
+    hr = S_OK;
+  } else {
+    hr = HRESULTFromLastError();
+  }
+
+  ::LocalFree(psid);
+  VERIFY1(::DeregisterEventSource(hlog));
+  return hr;
+}
+
+HRESULT EventLogger::ReadLastEvent(const TCHAR* src_name, EVENTLOGRECORD* rec) {
+  if (!(rec && src_name)) {
+    return E_INVALIDARG;
+  }
+  HANDLE hlog = ::OpenEventLog(NULL, src_name);
+  if (!hlog) {
+    return HRESULTFromLastError();
+  }
+  HRESULT hr = E_FAIL;
+  DWORD bytes_read(0), bytes_needed(0);
+  const DWORD read_flags = EVENTLOG_BACKWARDS_READ | EVENTLOG_SEQUENTIAL_READ;
+  if (::ReadEventLog(hlog,              // Event log handle.
+                     read_flags,        // Reverse chronological order.
+                     0,                 // Not used.
+                     rec,               // Read buffer.
+                     rec->Length,       // Size of read buffer.
+                     &bytes_read,       // Number of bytes read.
+                     &bytes_needed)) {  // Number of bytes required.
+    hr = S_OK;
+  } else {
+    hr = HRESULTFromLastError();
+  }
+  ::CloseEventLog(hlog);
+  return hr;
+}
+
+// TODO(omaha): Decide whether to use IDS_PRODUCT_DISPLAY_NAME instead.
+// On one hand this string makes it to the event viewer, on
+// the other hand the same string is used to register an event log for an
+// application in registry. We do not expect the mapping to change when the user
+// changes languages or there are multiple users for a per-machine install that
+// are using different languages., however we may decide to do so.
+const TCHAR* const EventLogger::kSourceName = kAppName;
+
+void GoogleUpdateLogEvent::WriteEvent() {
+  ASSERT1(!event_desc_.IsEmpty());
+  ASSERT1(type_ != 0);
+  ASSERT1(id_ != 0);
+
+  const DWORD pid(::GetCurrentProcessId());
+  const TCHAR* ver = GetVersionString();
+
+  const ConfigManager& cm = *ConfigManager::Instance();
+  CString msg;
+  SafeCStringFormat(&msg, _T("\n%s.\npid=%d, ver=%s, machine=%d, extern=%d"),
+                    event_desc_, pid, ver, is_machine_, !cm.IsInternalUser());
+#if DEBUG
+  msg.Append(_T(", debug"));
+#endif
+#if !OFFICIAL_BUILD
+  msg.Append(_T(", private"));
+#endif
+
+  if (!event_text_.IsEmpty()) {
+    SafeCStringAppendFormat(&msg, _T("\n%s"), event_text_);
+  }
+
+  LogEvent(static_cast<WORD>(type_), id_, msg);
+}
+
+}  // namespace omaha
+
diff --git a/common/event_logger.h b/common/event_logger.h
new file mode 100644
index 0000000..8a2c89f
--- /dev/null
+++ b/common/event_logger.h
@@ -0,0 +1,151 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Event Logger provides a simple mechanism to log events to Windows
+// Event Log. A few overloads are defined to simplify logging by reducing
+// the number of parameters that must be provided. The overloads are
+// implemented in terms of the EventLogger class.
+//
+// The event logging works in both debug and optimized builds. This is not
+// a substitute for the debug log. Instead it is a way to provide some level
+// of transparency into what Google Update is doing at runtime and to help
+// diagnosing end user issues.
+//
+// Familiarity with Windows Event Log is helpful in understanding how
+// these wrappers are to be used. Windows Event Log uses localized strings
+// in a message file and it substitutes string insterts that correspond to
+// formatting characters in the message string. In addtion, the log is able
+// to record raw data, herein provided by a context string, which may be
+// useful to provide some context around the formatted message.
+
+// TODO(omaha): Provide some control for the verbosity level in the log.
+// TODO(omaha): Perhaps there is a better way to define the overloaded
+// wrappers below. I chose a compromise between the easy of use while not
+// mixing up different string parameters that have different meanings.
+
+#ifndef OMAHA_COMMON_EVENT_LOGGER_H_
+#define OMAHA_COMMON_EVENT_LOGGER_H_
+
+#include <atlstr.h>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+void LogEventHelper(WORD type, DWORD id, size_t count, const TCHAR** strings,
+                    const TCHAR* ctx);
+
+// Logs an event to the Application log
+inline void LogEvent(WORD type, DWORD id) {
+  LogEventHelper(type, id, 0, NULL, NULL);
+}
+
+inline void LogEvent(WORD type, DWORD id, const TCHAR* s) {
+  const TCHAR* strings[] = {s};
+  LogEventHelper(type, id, arraysize(strings), strings, NULL);
+}
+
+inline void LogEvent(WORD type, DWORD id, const TCHAR* s1, const TCHAR* s2) {
+  const TCHAR* strings[] = {s1, s2};
+  LogEventHelper(type, id, arraysize(strings), strings, NULL);
+}
+
+inline void LogEvent(WORD type, DWORD id, const TCHAR* s1, const TCHAR* s2,
+                     const TCHAR* s3) {
+  const TCHAR* strings[] = {s1, s2, s3};
+  LogEventHelper(type, id, arraysize(strings), strings, NULL);
+}
+
+// Logs an event to the Application log with a context string.
+inline void LogEventContext(WORD type, DWORD id, const TCHAR* ctx) {
+  LogEventHelper(type, id, 0, NULL, ctx);
+}
+
+inline void LogEventContext(WORD type, DWORD id, const TCHAR* s,
+                            const TCHAR* ctx) {
+  const TCHAR* strings[] = {s};
+  LogEventHelper(type, id, arraysize(strings), strings, ctx);
+}
+
+inline void LogEventContext(WORD type, DWORD id, const TCHAR* s1,
+                            const TCHAR* s2, const TCHAR* ctx) {
+  const TCHAR* strings[] = {s1, s2};
+  LogEventHelper(type, id, arraysize(strings), strings, ctx);
+}
+
+inline void LogEventContext(WORD type, DWORD id, const TCHAR* s1,
+                            const TCHAR* s2, const TCHAR* s3,
+                            const TCHAR* ctx) {
+  const TCHAR* strings[] = {s1, s2, s3};
+  LogEventHelper(type, id, arraysize(strings), strings, ctx);
+}
+
+class EventLogger {
+ public:
+  // Creates an event source for the "Application" log so that EventViewer can
+  // map event identifier codes to message strings.
+  static HRESULT AddEventSource(
+      const TCHAR* src_name,       // Event source name.
+      const TCHAR* msg_dll_path);  // Path for message DLL.
+
+  static HRESULT RemoveEventSource(
+      const TCHAR* src_name);      // Event source name.
+
+  // Writes an entry at the end of event log that contains the source name.
+  static HRESULT ReportEvent(
+      const TCHAR* src_name,       // Event source name.
+      WORD type,                   // Type of the event to be logged.
+      WORD category,               // Event category.
+      DWORD id,                    // Event identifier.
+      WORD count,                  // Count of insert strings.
+      const TCHAR** strings,       // Insert strings.
+      size_t buf_size,             // Size of binary data to append.
+      void* buffer);               // Buffer containing the binary data.
+
+  // Reads the topmost event log record.
+  static HRESULT ReadLastEvent(const TCHAR* src_name, EVENTLOGRECORD* rec);
+
+  // Default name for the event source.
+  static const TCHAR* const kSourceName;
+
+  // Default event category.
+  static const WORD kDefaultCategory = 0;
+};
+
+class GoogleUpdateLogEvent {
+ public:
+  GoogleUpdateLogEvent(int type, int id, bool is_machine)
+      : type_(type),
+        id_(id),
+        is_machine_(is_machine) {}
+  GoogleUpdateLogEvent() : type_(0), id_(0), is_machine_(false) {}
+  ~GoogleUpdateLogEvent() {}
+  void WriteEvent();
+  void set_event_desc(const CString& desc) { event_desc_ = desc; }
+  void set_event_text(const CString& text) { event_text_ = text; }
+
+ private:
+  CString event_desc_;
+  CString event_text_;
+  int type_;
+  int id_;
+  bool is_machine_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(GoogleUpdateLogEvent);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_EVENT_LOGGER_H_
+
diff --git a/common/event_logger_unittest.cc b/common/event_logger_unittest.cc
new file mode 100644
index 0000000..175a0e1
--- /dev/null
+++ b/common/event_logger_unittest.cc
@@ -0,0 +1,182 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "base/basictypes.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/error.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/event_logger.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+class EventLoggerTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    EXPECT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
+    OverrideRegistryHives(kRegistryHiveOverrideRoot);
+
+    // Enable logging of events.
+    DWORD log_events = LOG_EVENT_LEVEL_ALL;
+    EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                      kRegValueEventLogLevel, log_events));
+  }
+
+  virtual void TearDown() {
+    RestoreRegistryHives();
+    EXPECT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
+  }
+};
+
+TEST_F(EventLoggerTest, AddEventSource) {
+  if (!vista_util::IsUserAdmin()) {
+    std::wcout << _T("\tTest did not run because the user is not an admin.")
+               << std::endl;
+    return;
+  }
+
+  // Registers the "Google Update" event source for the "Application" log
+  EXPECT_SUCCEEDED(EventLogger::AddEventSource(EventLogger::kSourceName,
+                                               _T("path")));
+
+  const TCHAR key_name[] = _T("HKLM\\SYSTEM\\CurrentControlSet\\Services\\")
+                           _T("EventLog\\Application\\")
+                           _T(OMAHA_APP_NAME_ANSI);
+  EXPECT_TRUE(RegKey::HasKey(key_name));
+
+  CString s;
+  EXPECT_SUCCEEDED(RegKey::GetValue(key_name, _T("EventMessageFile"), &s));
+  EXPECT_STREQ(s.GetString(), _T("path"));
+
+  DWORD types(0);
+  EXPECT_SUCCEEDED(RegKey::GetValue(key_name, _T("TypesSupported"), &types));
+  EXPECT_EQ(types,
+      EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE);
+
+  // Removes the "OmahaUnitTest" event source.
+  EXPECT_SUCCEEDED(EventLogger::RemoveEventSource(EventLogger::kSourceName));
+  EXPECT_FALSE(RegKey::HasKey(key_name));
+  EXPECT_TRUE(RegKey::HasKey(
+      _T("HKLM\\SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application")));
+}
+
+TEST_F(EventLoggerTest, ReportEvent) {
+  if (!vista_util::IsUserAdmin()) {
+    std::wcout << _T("\tTest did not run because the user is not an admin.")
+               << std::endl;
+    return;
+  }
+
+  EXPECT_SUCCEEDED(EventLogger::AddEventSource(EventLogger::kSourceName,
+                                               _T("path")));
+
+  const TCHAR* strings[] = {_T("foo"), _T("bar")};
+  byte buf[] = {0xaa, 0x55, 0};
+
+  const int kEventId = 100;
+  EXPECT_SUCCEEDED(EventLogger::ReportEvent(EventLogger::kSourceName,
+                                            EVENTLOG_WARNING_TYPE,
+                                            0,
+                                            kEventId,
+                                            arraysize(strings),
+                                            strings,
+                                            arraysize(buf),
+                                            buf));
+  // Read the record at the top to do a brief sanity check.
+  const size_t kBufferSize = 1024 * 64;
+  byte buffer[kBufferSize] = {0};
+  EVENTLOGRECORD* rec = reinterpret_cast<EVENTLOGRECORD*>(buffer);
+  rec->Length = kBufferSize;
+  EXPECT_SUCCEEDED(EventLogger::ReadLastEvent(EventLogger::kSourceName, rec));
+  EXPECT_EQ(rec->EventID, kEventId);
+  EXPECT_EQ(rec->EventType, EVENTLOG_WARNING_TYPE);
+  EXPECT_EQ(rec->EventCategory, 0);
+  EXPECT_EQ(rec->NumStrings, 2);
+  const TCHAR* src = reinterpret_cast<const TCHAR*>(
+      reinterpret_cast<byte*>(rec) + sizeof EVENTLOGRECORD);
+  EXPECT_STREQ(src, EventLogger::kSourceName);
+  const TCHAR* s2 = (LPTSTR) ((LPBYTE) rec + rec->StringOffset);
+  EXPECT_SUCCEEDED(EventLogger::RemoveEventSource(EventLogger::kSourceName));
+}
+
+TEST_F(EventLoggerTest, LogEvent_LoggingDisabled) {
+  // Disable logging.
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueEventLogLevel,
+                                    static_cast<DWORD>(0)));
+
+  const size_t kBufferSize = 1024 * 64;
+  byte buffer[kBufferSize] = {0};
+  EVENTLOGRECORD* rec = reinterpret_cast<EVENTLOGRECORD*>(buffer);
+  rec->Length = kBufferSize;
+  EXPECT_SUCCEEDED(EventLogger::ReadLastEvent(EventLogger::kSourceName, rec));
+  int record_number = rec->RecordNumber;
+
+  // Logging is disabled, expect no event is logged.
+  LogEvent(EVENTLOG_INFORMATION_TYPE, 10);
+  rec->Length = kBufferSize;
+  EXPECT_SUCCEEDED(EventLogger::ReadLastEvent(EventLogger::kSourceName, rec));
+  EXPECT_EQ(record_number, rec->RecordNumber);
+}
+
+TEST_F(EventLoggerTest, LogEvent) {
+  const size_t kBufferSize = 1024 * 64;
+  byte buffer[kBufferSize] = {0};
+  EVENTLOGRECORD* rec = reinterpret_cast<EVENTLOGRECORD*>(buffer);
+
+  LogEvent(EVENTLOG_INFORMATION_TYPE, 10);
+  rec->Length = kBufferSize;
+  EXPECT_SUCCEEDED(EventLogger::ReadLastEvent(EventLogger::kSourceName, rec));
+  EXPECT_EQ(10, rec->EventID);
+
+  LogEvent(EVENTLOG_INFORMATION_TYPE, 11, _T("s1"));
+  rec->Length = kBufferSize;
+  EXPECT_SUCCEEDED(EventLogger::ReadLastEvent(EventLogger::kSourceName, rec));
+  EXPECT_EQ(11, rec->EventID);
+
+  LogEvent(EVENTLOG_INFORMATION_TYPE, 12, _T("s1"), _T("s2"));
+  rec->Length = kBufferSize;
+  EXPECT_SUCCEEDED(EventLogger::ReadLastEvent(EventLogger::kSourceName, rec));
+  EXPECT_EQ(12, rec->EventID);
+}
+
+TEST_F(EventLoggerTest, LogEventContext) {
+  const size_t kBufferSize = 1024 * 64;
+  byte buffer[kBufferSize] = {0};
+  EVENTLOGRECORD* rec = reinterpret_cast<EVENTLOGRECORD*>(buffer);
+
+  LogEventContext(EVENTLOG_INFORMATION_TYPE, 20, _T("foo"));
+  rec->Length = kBufferSize;
+  EXPECT_SUCCEEDED(EventLogger::ReadLastEvent(EventLogger::kSourceName, rec));
+  EXPECT_EQ(20, rec->EventID);
+
+  LogEventContext(EVENTLOG_INFORMATION_TYPE, 21, _T("s1"), _T("bar"));
+  rec->Length = kBufferSize;
+  EXPECT_SUCCEEDED(EventLogger::ReadLastEvent(EventLogger::kSourceName, rec));
+  EXPECT_EQ(21, rec->EventID);
+
+  LogEventContext(EVENTLOG_INFORMATION_TYPE, 22,
+                  _T("s1"), _T("s2"), _T("foobar"));
+  rec->Length = kBufferSize;
+  EXPECT_SUCCEEDED(EventLogger::ReadLastEvent(EventLogger::kSourceName, rec));
+  EXPECT_EQ(22, rec->EventID);
+}
+
+}  // namespace omaha
diff --git a/common/exception_barrier.cc b/common/exception_barrier.cc
deleted file mode 100644
index 526cc55..0000000
--- a/common/exception_barrier.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// A class to make it easy to tag exception propagation boundaries and
-// get crash reports of exceptions that pass over same.
-#include "omaha/common/exception_barrier.h"
-
-enum {
-  // Flag set by exception handling machinery when unwinding
-  EH_UNWINDING = 0x00000002
-};
-
-// TODO(omaha): How to make this statically parameterizable?
-ExceptionBarrier::ExceptionHandler ExceptionBarrier::s_handler_ = NULL;
-
-// This function must be extern "C" to match up with the SAFESEH
-// declaration in our corresponding ASM file
-extern "C" EXCEPTION_DISPOSITION __cdecl
-ExceptionBarrierHandler(struct _EXCEPTION_RECORD *exception_record,
-                        void * establisher_frame,
-                        struct _CONTEXT *context,
-                        void * reserved) {
-  establisher_frame;  // unreferenced formal parameter
-  reserved;
-  if (!(exception_record->ExceptionFlags & EH_UNWINDING)) {
-    // When the exception is really propagating through us, we'd like to be
-    // called before the state of the program has been modified by the stack
-    // unwinding. In the absence of an exception handler, the unhandled
-    // exception filter gets called between the first chance and the second
-    // chance exceptions, so Windows pops either the JIT debugger or WER UI.
-    // This is not desirable in most of the cases.
-    ExceptionBarrier::ExceptionHandler handler = ExceptionBarrier::handler();
-    if (handler) {
-      EXCEPTION_POINTERS ptrs = { exception_record, context };
-
-      handler(&ptrs);
-    }
-  }
-
-  return ExceptionContinueSearch;
-}
diff --git a/common/experiment_labels.cc b/common/experiment_labels.cc
new file mode 100644
index 0000000..ecf270e
--- /dev/null
+++ b/common/experiment_labels.cc
@@ -0,0 +1,286 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Utility class to manage a set of experiment labels.  Experiment labels
+// are a set of key/value pairs used to track an install's "membership" in
+// A/B experiment groups issued by the Omaha server.  Keys are strings with
+// a limited character set (Perl \w, plus a few characters), while values
+// consist of a string and an expiration date.  Label sets are stored per-app
+// and transmitted to the server as part of requests (pings / update checks),
+// and a delta is potentially returned from the server with each response.
+//
+// Experiment labels are serialized with key/value separated by an equals, and
+// value/expiration by a pipe symbol; the expiration is represented in RFC822
+// format.  For example: "test_key=test_value|Fri, 14 Aug 2015 16:13:03 GMT".
+// If an app participates in multiple experiments simultaneously, labels are
+// concatenated with semicolon delimiters.
+
+#include "omaha/common/experiment_labels.h"
+
+#include "omaha/base/debug.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/common/app_registry_utils.h"
+
+namespace omaha {
+
+ExperimentLabels::ExperimentLabels() : labels_(), preserve_expired_(false) {}
+
+ExperimentLabels::~ExperimentLabels() {}
+
+int ExperimentLabels::NumLabels() const {
+  return labels_.size();
+}
+
+bool ExperimentLabels::ContainsKey(const CString& key) const {
+  ASSERT1(!key.IsEmpty());
+  return labels_.find(key) != labels_.end();
+}
+
+void ExperimentLabels::GetLabelByIndex(int index, CString* key, CString* value,
+                                       time64* expiration) const {
+  ASSERT1(index >= 0);
+  ASSERT1(static_cast<LabelMap::size_type>(index) < labels_.size());
+
+  LabelMap::const_iterator cit = labels_.begin();
+  std::advance(cit, static_cast<LabelMap::size_type>(index));
+  if (key) {
+    *key = cit->first;
+  }
+  if (value) {
+    *value = cit->second.first;
+  }
+  if (expiration) {
+    *expiration = cit->second.second;
+  }
+}
+
+bool ExperimentLabels::FindLabelByKey(const CString& key, CString* value,
+                                      time64* expiration) const {
+  ASSERT1(!key.IsEmpty());
+  LabelMap::const_iterator cit = labels_.find(key);
+  if (labels_.end() == cit) {
+    return false;
+  }
+
+  if (value) {
+    *value = cit->second.first;
+  }
+  if (expiration) {
+    *expiration = cit->second.second;
+  }
+  return true;
+}
+
+bool ExperimentLabels::SetLabel(const CString& key, const CString& value,
+                                time64 expiration) {
+  if (!IsLabelContentValid(key) || !IsLabelContentValid(value)) {
+    return false;
+  }
+  if (expiration < GetCurrent100NSTime() && !preserve_expired_) {
+    return false;
+  }
+  labels_[key] = std::make_pair(value, expiration);
+  return true;
+}
+
+bool ExperimentLabels::ClearLabel(const CString& key) {
+  LabelMap::iterator it = labels_.find(key);
+  if (labels_.end() == it) {
+    return false;
+  }
+  labels_.erase(it);
+  return true;
+}
+
+void ExperimentLabels::ExpireLabels() {
+  time64 current_time = GetCurrent100NSTime();
+  for (LabelMap::iterator it = labels_.begin(); it != labels_.end(); ++it) {
+    if (it->second.second < current_time) {
+      it = labels_.erase(it);
+      if (it == labels_.end()) {
+        break;
+      }
+    }
+  }
+}
+
+void ExperimentLabels::ClearAllLabels() {
+  labels_.clear();
+}
+
+CString ExperimentLabels::Serialize() const {
+  CString serialized;
+  time64 current_time = GetCurrent100NSTime();
+  for (LabelMap::const_iterator cit = labels_.begin();
+       cit != labels_.end();
+       ++cit) {
+    if (preserve_expired_ || cit->second.second >= current_time) {
+      if (!serialized.IsEmpty()) {
+        serialized.Append(L";");
+      }
+      FILETIME ft = {};
+      Time64ToFileTime(cit->second.second, &ft);
+      SafeCStringAppendFormat(&serialized, L"%s=%s|%s",
+                              cit->first,
+                              cit->second.first,
+                              ConvertTimeToGMTString(&ft));
+    }
+  }
+  return serialized;
+}
+
+bool ExperimentLabels::Deserialize(const CString& label_list) {
+  LabelMap new_labels;
+  if (DoDeserialize(&new_labels, label_list, preserve_expired_)) {
+    std::swap(labels_, new_labels);
+    return true;
+  }
+  return false;
+}
+
+bool ExperimentLabels::DeserializeAndApplyDelta(const CString& label_list) {
+  LabelMap merged_labels = labels_;
+  if (DoDeserialize(&merged_labels, label_list, false)) {
+    std::swap(labels_, merged_labels);
+    return true;
+  }
+  return false;
+}
+
+void ExperimentLabels::SetPreserveExpiredLabels(bool preserve) {
+  preserve_expired_ = preserve;
+}
+
+HRESULT ExperimentLabels::WriteToRegistry(bool is_machine,
+                                          const CString& app_id) {
+  return app_registry_utils::SetExperimentLabels(is_machine, app_id,
+                                                 Serialize());
+}
+
+HRESULT ExperimentLabels::ReadFromRegistry(bool is_machine,
+                                           const CString& app_id) {
+  CString label_list;
+  HRESULT hr = app_registry_utils::GetExperimentLabels(is_machine, app_id,
+                                                       &label_list);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return Deserialize(label_list) ? S_OK : E_FAIL;
+}
+
+bool ExperimentLabels::IsStringValidLabelSet(const CString& label_list) {
+  ExperimentLabels labels;
+  return labels.Deserialize(label_list);
+}
+
+bool ExperimentLabels::IsLabelContentValid(const CString& str) {
+  if (str.IsEmpty()) {
+    return false;
+  }
+  for (int i = 0; i < str.GetLength(); ++i) {
+    wchar_t ch = str[i];
+    if (!((ch >= L'+' && ch <= L'-') ||
+          (ch >= L'0' && ch <= L':') ||
+          (ch >= L'A' && ch <= L'Z') ||
+          (ch >= L'a' && ch <= L'z') ||
+          (ch == L'_') || (ch == L' '))) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool ExperimentLabels::SplitCombinedLabel(const CString& combined, CString* key,
+                                          CString* value, time64* expiration) {
+  ASSERT1(!combined.IsEmpty());
+  ASSERT1(key);
+  ASSERT1(value);
+  ASSERT1(expiration);
+
+  int value_offset = combined.Find(L'=');
+  if (value_offset <= 0 || value_offset == combined.GetLength()) {
+    return false;
+  }
+  *key = combined.Left(value_offset);
+  if (!IsLabelContentValid(*key)) {
+    return false;
+  }
+  ++value_offset;
+
+  int expiration_offset = combined.Find(L'|', value_offset);
+  if (expiration_offset <= value_offset ||
+      expiration_offset == combined.GetLength()) {
+    return false;
+  }
+  *value = combined.Mid(value_offset, expiration_offset - value_offset);
+  if (!IsLabelContentValid(*value)) {
+    return false;
+  }
+  ++expiration_offset;
+
+  CString expiration_string = combined.Mid(expiration_offset);
+  if (!IsLabelContentValid(expiration_string)) {
+    return false;
+  }
+  SYSTEMTIME system_time = {};
+  if (!RFC822DateToSystemTime(expiration_string, &system_time, false)) {
+    return false;
+  }
+  *expiration = SystemTimeToTime64(&system_time);
+
+  return true;
+}
+
+bool ExperimentLabels::DoDeserialize(LabelMap* map, const CString& label_list,
+                                     bool accept_expired) {
+  ASSERT1(map);
+
+  if (label_list.IsEmpty()) {
+    return true;
+  }
+
+  time64 current_time = GetCurrent100NSTime();
+
+  for (int offset = 0;;) {
+    CString combined_label = label_list.Tokenize(L";", offset);
+    if (combined_label.IsEmpty()) {
+      if (offset < 0) {
+        break;  // Natural end-of-string reached.
+      }
+      return false;
+    }
+
+    CString key;
+    CString value;
+    time64 expiration = 0;
+    if (!SplitCombinedLabel(combined_label, &key, &value, &expiration)) {
+      return false;
+    }
+
+    // If the label is well-formatted but expired, we accept the input, but
+    // do not add it to the map and do not emit an error.  If there is already
+    // a label in the map with that key, delete it.
+    if (accept_expired || expiration > current_time) {
+      (*map)[key] = std::make_pair(value, expiration);
+    } else {
+      map->erase(key);
+    }
+  }
+
+  return true;
+}
+
+}  // namespace omaha
+
diff --git a/common/experiment_labels.h b/common/experiment_labels.h
new file mode 100644
index 0000000..66e3dfe
--- /dev/null
+++ b/common/experiment_labels.h
@@ -0,0 +1,151 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Utility class to manage a set of experiment labels.  Experiment labels
+// are a set of key/value pairs used to track an install's "membership" in
+// A/B experiment groups issued by the Omaha server.  Keys are strings with
+// a limited character set (Perl \w, plus a few characters), while values
+// consist of a string and an expiration date.  Label sets are stored per-app
+// and transmitted to the server as part of requests (pings / update checks),
+// and a delta is potentially returned from the server with each response.
+//
+// Experiment labels are serialized with key/value separated by an equals, and
+// value/expiration by a pipe symbol; the expiration is represented in RFC822
+// format.  For example: "test_key=test_value|Fri, 14 Aug 2015 16:13:03 GMT".
+// If an app participates in multiple experiments simultaneously, labels are
+// concatenated with semicolon delimiters.
+
+#ifndef OMAHA_COMMON_EXPERIMENT_LABELS_H_
+#define OMAHA_COMMON_EXPERIMENT_LABELS_H_
+
+#include <atlstr.h>
+
+#include <map>
+#include <utility>
+
+#include "base/basictypes.h"
+#include "omaha/base/time.h"
+
+namespace omaha {
+
+class ExperimentLabels {
+ public:
+  ExperimentLabels();
+  ~ExperimentLabels();
+
+  // Returns the number of labels in the store.
+  int NumLabels() const;
+
+  // Returns true if a label with this key exists in the store.
+  bool ContainsKey(const CString& key) const;
+
+  // Returns the contents of the Nth label in the experiment set.  Output
+  // parameters are allowed to be NULL.  If index is out of range, asserts
+  // on a debug build.
+  //
+  // NOTE: This method is O(n), and indexes are not stable with respect to
+  // insertion order; it should not be called from any hot path.  Unit tests
+  // and diagnostic tools should use this to enumerate keys.
+  void GetLabelByIndex(int index,
+                       CString* key,
+                       CString* value,
+                       time64* expiration) const;
+
+  // If a label with this key exists, returns true and writes the value and
+  // expiration to the output parameters.  Output parameters are allowed
+  // to be NULL.  If no such key exists, returns false and leaves the outputs
+  // unchanged.
+  bool FindLabelByKey(const CString& key,
+                      CString* value,
+                      time64* expiration) const;
+
+  // Attempts to set a label in the store, either adding a new one or updating
+  // the value/expiration of an existing one.  Returns true on success.
+  bool SetLabel(const CString& key, const CString& value, time64 expiration);
+
+  // Clears a label in the store.  Returns true if a label with that key exists
+  // (it is removed); returns false if no label with that key exists.
+  bool ClearLabel(const CString& key);
+
+  // Removes any labels from the store whose expiration dates have passed.
+  void ExpireLabels();
+
+  // Removes all labels from the store.
+  void ClearAllLabels();
+
+  // Concatenates and emits all labels in the store in an XML-friendly format.
+  CString Serialize() const;
+
+  // Replaces the current contents of the store with new labels from an
+  // XML-friendly string.  On success, returns true; the store reflects the
+  // input exactly, with all prior contents are lost.  On failure, returns
+  // false and the store's contents are unchanged.
+  bool Deserialize(const CString& label_list);
+
+  // Applies the contents of a label list as a delta against the current
+  // contents of the store.  On success, returns true, and:
+  // * Any expired labels in the list are deleted from the store.
+  // * Unexpired labels in the list will be added to the store.  If any keys
+  //   already exist in the store, they are overwritten with the input.
+  // * Labels that are in the store but not in the input are left unmodified.
+  // On failure, returns false, and the store's contents are unchanged.
+  // This function's behavior is not modified by SetPreserveExpiredLabels()!
+  bool DeserializeAndApplyDelta(const CString& label_list);
+
+  // Modifies the store's handling of labels with expiration dates in the past.
+  // By default, SetLabel() will fail to add expired labels, Deserialize() will
+  // silently discard them, and Serialize() will not emit them.  Following a
+  // call to SetPreserveExpiredLabels(true), these functions will accept and/or
+  // include expired labels.
+  void SetPreserveExpiredLabels(bool preserve);
+
+  // Serializes a set of experiment labels to application data in the Registry.
+  HRESULT WriteToRegistry(bool is_machine, const CString& app_id);
+
+  // Deserializes a set of experiment labels from the Registry.
+  HRESULT ReadFromRegistry(bool is_machine, const CString& app_id);
+
+  // Returns true if the supplied string is a valid experiment label set.
+  static bool IsStringValidLabelSet(const CString& label_list);
+
+ private:
+  typedef std::map<CString, std::pair<CString, time64> > LabelMap;
+
+  // Returns true if all characters are in the range [a-zA-Z0-9_\-+,: ].
+  // (Perl \w, plus the punctuation necessary for RFC822 dates.)
+  static bool IsLabelContentValid(const CString& str);
+
+  // Given an input string of the form "key=value|rfc822_date", converts it
+  // into separate key, value, and integer expiration dates.
+  static bool SplitCombinedLabel(const CString& combined,
+                                 CString* key,
+                                 CString* value,
+                                 time64* expiration);
+
+  // Splits an input string and applies it against an existing internal map.
+  static bool DoDeserialize(LabelMap* map,
+                            const CString& label_list,
+                            bool accept_expired);
+
+  LabelMap labels_;
+  bool preserve_expired_;
+
+  DISALLOW_COPY_AND_ASSIGN(ExperimentLabels);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_EXPERIMENT_LABELS_H_
+
diff --git a/common/experiment_labels_unittest.cc b/common/experiment_labels_unittest.cc
new file mode 100644
index 0000000..1cc0e50
--- /dev/null
+++ b/common/experiment_labels_unittest.cc
@@ -0,0 +1,729 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/experiment_labels.h"
+#include "omaha/testing/resource.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+#define LABEL_DELIMITER_KV _T("=")
+#define LABEL_DELIMITER_EX _T("|")
+#define LABEL_DELIMITER_LA _T(";")
+
+#define LABELONE_KEY      _T("test_key_1")
+#define LABELONE_VALUE    _T("test_value_1")
+#define LABELONE_EXP_STR  _T("Fri, 14 Aug 2015 16:13:03 GMT")
+#define LABELONE_EXP_INT  130840423830000000uI64
+#define LABELONE_COMBINED LABELONE_KEY \
+                          LABEL_DELIMITER_KV \
+                          LABELONE_VALUE \
+                          LABEL_DELIMITER_EX \
+                          LABELONE_EXP_STR
+
+#define LABELTWO_KEY      _T("test_key_2")
+#define LABELTWO_VALUE    _T("test_value_2")
+#define LABELTWO_EXP_STR  _T("Thu, 27 Nov 2014 23:59:59 GMT")
+#define LABELTWO_EXP_INT  130616063990000000uI64
+#define LABELTWO_COMBINED LABELTWO_KEY \
+                          LABEL_DELIMITER_KV \
+                          LABELTWO_VALUE \
+                          LABEL_DELIMITER_EX \
+                          LABELTWO_EXP_STR
+
+#define LABELOLD_KEY      _T("test_key_old")
+#define LABELOLD_VALUE    _T("test_value_old")
+#define LABELOLD_EXP_STR  _T("Mon, 04 Jan 2010 08:00:00 GMT")
+#define LABELOLD_EXP_INT  129070656000000000uI64
+#define LABELOLD_COMBINED LABELOLD_KEY \
+                          LABEL_DELIMITER_KV \
+                          LABELOLD_VALUE \
+                          LABEL_DELIMITER_EX \
+                          LABELOLD_EXP_STR
+
+
+const TCHAR* const kLabelOneKey =      LABELONE_KEY;
+const TCHAR* const kLabelOneValue =    LABELONE_VALUE;
+const TCHAR* const kLabelOneExpStr =   LABELONE_EXP_STR;
+const time64       kLabelOneExpInt =   LABELONE_EXP_INT;
+const TCHAR* const kLabelOneCombined = LABELONE_COMBINED;
+
+const TCHAR* const kLabelTwoKey =      LABELTWO_KEY;
+const TCHAR* const kLabelTwoValue =    LABELTWO_VALUE;
+const TCHAR* const kLabelTwoExpStr =   LABELTWO_EXP_STR;
+const time64       kLabelTwoExpInt =   LABELTWO_EXP_INT;
+const TCHAR* const kLabelTwoCombined = LABELTWO_COMBINED;
+
+const TCHAR* const kLabelOldKey =      LABELOLD_KEY;
+const TCHAR* const kLabelOldValue =    LABELOLD_VALUE;
+const TCHAR* const kLabelOldExpStr =   LABELOLD_EXP_STR;
+const time64       kLabelOldExpInt =   LABELOLD_EXP_INT;
+const TCHAR* const kLabelOldCombined = LABELOLD_COMBINED;
+
+const TCHAR* const kLabelNewCombined = LABELONE_COMBINED
+                                       LABEL_DELIMITER_LA
+                                       LABELTWO_COMBINED;
+
+const TCHAR* const kLabelAllCombined = LABELONE_COMBINED
+                                       LABEL_DELIMITER_LA
+                                       LABELTWO_COMBINED
+                                       LABEL_DELIMITER_LA
+                                       LABELOLD_COMBINED;
+
+}  // end namespace
+
+TEST(ExperimentLabelsTest, Empty) {
+  ExperimentLabels el;
+
+  EXPECT_EQ(0, el.NumLabels());
+  EXPECT_FALSE(el.ContainsKey(kLabelOneKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelTwoKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelOldKey));
+  EXPECT_FALSE(el.FindLabelByKey(kLabelOneKey, NULL, NULL));
+  EXPECT_FALSE(el.FindLabelByKey(kLabelTwoKey, NULL, NULL));
+  EXPECT_FALSE(el.FindLabelByKey(kLabelOldKey, NULL, NULL));
+}
+
+TEST(ExperimentLabelsTest, BasicOperations) {
+  ExperimentLabels el;
+
+  EXPECT_EQ(0, el.NumLabels());
+  EXPECT_FALSE(el.ContainsKey(kLabelOneKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelTwoKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelOldKey));
+
+  // Start by adding a single label.
+  EXPECT_TRUE(el.SetLabel(kLabelOneKey, kLabelOneValue, kLabelOneExpInt));
+  EXPECT_EQ(1, el.NumLabels());
+  EXPECT_TRUE(el.ContainsKey(kLabelOneKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelTwoKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelOldKey));
+
+  // Add both future labels now.
+  EXPECT_TRUE(el.SetLabel(kLabelTwoKey, kLabelTwoValue, kLabelTwoExpInt));
+  EXPECT_EQ(2, el.NumLabels());
+  EXPECT_TRUE(el.ContainsKey(kLabelOneKey));
+  EXPECT_TRUE(el.ContainsKey(kLabelTwoKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelOldKey));
+
+  // Clear the first label; it should not appear.
+  EXPECT_TRUE(el.ClearLabel(kLabelOneKey));
+  EXPECT_EQ(1, el.NumLabels());
+  EXPECT_FALSE(el.ContainsKey(kLabelOneKey));
+  EXPECT_TRUE(el.ContainsKey(kLabelTwoKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelOldKey));
+
+  // ClearLabel should return false if the key isn't present.
+  EXPECT_FALSE(el.ClearLabel(kLabelOldKey));
+
+  // ClearAllLabels should clear all labels.
+  el.ClearAllLabels();
+  EXPECT_EQ(0, el.NumLabels());
+  EXPECT_FALSE(el.ContainsKey(kLabelOneKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelTwoKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelOldKey));
+}
+
+TEST(ExperimentLabelsTest, SetInvalidParameters) {
+  ExperimentLabels el;
+
+  // Verify that our core test parameters are okay by adding a key, then
+  // clearing all.
+  EXPECT_TRUE(el.SetLabel(kLabelOneKey, kLabelOneValue, kLabelOneExpInt));
+  el.ClearAllLabels();
+
+  // Don't allow zero length names or values, or expirations in the past.
+  EXPECT_FALSE(el.SetLabel(_T(""), kLabelOneValue, kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(kLabelOneKey, _T(""), kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(kLabelOneKey, kLabelOneValue, 0));
+
+  // Explicitly validate that names and values do not contain delimiters,
+  // quotes, angle brackets, question marks, or ampersands, as these could
+  // break parsing at the label-string, extraargs, or XML levels.
+  EXPECT_FALSE(el.SetLabel(_T("=test"), kLabelOneValue, kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(_T("te=st"), kLabelOneValue, kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(_T("test="), kLabelOneValue, kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(_T("|test"), kLabelOneValue, kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(_T("te|st"), kLabelOneValue, kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(_T("test|"), kLabelOneValue, kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(_T("test<"), kLabelOneValue, kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(_T("test>"), kLabelOneValue, kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(_T("test&"), kLabelOneValue, kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(_T("test?"), kLabelOneValue, kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(_T("test^"), kLabelOneValue, kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(_T("test'"), kLabelOneValue, kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(_T("test\""), kLabelOneValue, kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(kLabelOneKey, _T("=test"), kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(kLabelOneKey, _T("te=st"), kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(kLabelOneKey, _T("test="), kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(kLabelOneKey, _T("|test"), kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(kLabelOneKey, _T("te|st"), kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(kLabelOneKey, _T("test|"), kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(kLabelOneKey, _T("test<"), kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(kLabelOneKey, _T("test>"), kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(kLabelOneKey, _T("test&"), kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(kLabelOneKey, _T("test?"), kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(kLabelOneKey, _T("test^"), kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(kLabelOneKey, _T("test'"), kLabelOneExpInt));
+  EXPECT_FALSE(el.SetLabel(kLabelOneKey, _T("test\""), kLabelOneExpInt));
+  EXPECT_EQ(0, el.NumLabels());
+
+  // Allow an expiration in the past if and only if we override the default
+  // preserve-expired-labels setting.
+  el.SetPreserveExpiredLabels(true);
+  EXPECT_TRUE(el.SetLabel(kLabelOneKey, kLabelOneValue, kLabelOldExpInt));
+  EXPECT_EQ(1, el.NumLabels());
+}
+
+TEST(ExperimentLabelsTest, SetWillOverwrite) {
+  ExperimentLabels el;
+  CString value;
+  time64 expiration;
+
+  EXPECT_TRUE(el.SetLabel(kLabelOneKey, kLabelOneValue, kLabelOneExpInt));
+
+  EXPECT_EQ(1, el.NumLabels());
+  EXPECT_TRUE(el.FindLabelByKey(kLabelOneKey, &value, &expiration));
+  EXPECT_STREQ(value, kLabelOneValue);
+  EXPECT_EQ(expiration, kLabelOneExpInt);
+
+  EXPECT_TRUE(el.SetLabel(kLabelOneKey, kLabelTwoValue, kLabelTwoExpInt));
+
+  EXPECT_EQ(1, el.NumLabels());
+  EXPECT_TRUE(el.FindLabelByKey(kLabelOneKey, &value, &expiration));
+  EXPECT_STREQ(value, kLabelTwoValue);
+  EXPECT_EQ(expiration, kLabelTwoExpInt);
+}
+
+TEST(ExperimentLabelsTest, FindLabelByKey) {
+  ExperimentLabels el;
+  CString value;
+  time64 expiration;
+
+  EXPECT_TRUE(el.SetLabel(kLabelOneKey, kLabelOneValue, kLabelOneExpInt));
+  EXPECT_TRUE(el.SetLabel(kLabelTwoKey, kLabelTwoValue, kLabelTwoExpInt));
+  EXPECT_EQ(2, el.NumLabels());
+
+  // Safely return partial values if either or both out parameters are NULL.
+  EXPECT_TRUE(el.FindLabelByKey(kLabelOneKey, NULL, NULL));
+  EXPECT_TRUE(el.FindLabelByKey(kLabelOneKey, &value, NULL));
+  EXPECT_STREQ(value, kLabelOneValue);
+  EXPECT_TRUE(el.FindLabelByKey(kLabelOneKey, NULL, &expiration));
+  EXPECT_EQ(expiration, kLabelOneExpInt);
+
+  // Overwrite values if they already have values.
+  EXPECT_TRUE(el.FindLabelByKey(kLabelTwoKey, &value, &expiration));
+  EXPECT_STREQ(value, kLabelTwoValue);
+  EXPECT_EQ(expiration, kLabelTwoExpInt);
+
+  // If the key isn't present, return false; both out-params are unmodified.
+  EXPECT_FALSE(el.FindLabelByKey(kLabelOldKey, &value, &expiration));
+  EXPECT_STREQ(value, kLabelTwoValue);
+  EXPECT_EQ(expiration, kLabelTwoExpInt);
+}
+
+TEST(ExperimentLabelsTest, Serialize_Empty) {
+  ExperimentLabels el;
+
+  CString serialized = el.Serialize();
+  EXPECT_STREQ(serialized, _T(""));
+}
+
+TEST(ExperimentLabelsTest, Serialize_Single_Valid) {
+  ExperimentLabels el;
+
+  EXPECT_TRUE(el.SetLabel(kLabelOneKey, kLabelOneValue, kLabelOneExpInt));
+  EXPECT_EQ(1, el.NumLabels());
+
+  CString serialized = el.Serialize();
+  EXPECT_STREQ(serialized, kLabelOneCombined);
+}
+
+TEST(ExperimentLabelsTest, Serialize_Single_Expired) {
+  ExperimentLabels el;
+
+  el.SetPreserveExpiredLabels(true);
+  EXPECT_TRUE(el.SetLabel(kLabelOldKey, kLabelOldValue, kLabelOldExpInt));
+  EXPECT_EQ(1, el.NumLabels());
+
+  el.SetPreserveExpiredLabels(false);
+  CString serialized = el.Serialize();
+  EXPECT_STREQ(serialized, _T(""));
+
+  el.SetPreserveExpiredLabels(true);
+  serialized = el.Serialize();
+  EXPECT_STREQ(serialized, kLabelOldCombined);
+}
+
+TEST(ExperimentLabelsTest, Serialize_Multi_Valid) {
+  ExperimentLabels el;
+
+  EXPECT_TRUE(el.SetLabel(kLabelOneKey, kLabelOneValue, kLabelOneExpInt));
+  EXPECT_TRUE(el.SetLabel(kLabelTwoKey, kLabelTwoValue, kLabelTwoExpInt));
+  EXPECT_EQ(2, el.NumLabels());
+
+  CString serialized = el.Serialize();
+  EXPECT_STREQ(serialized, kLabelNewCombined);
+}
+
+TEST(ExperimentLabelsTest, Serialize_Multi_Valid_Expired) {
+  ExperimentLabels el;
+
+  el.SetPreserveExpiredLabels(true);
+  EXPECT_TRUE(el.SetLabel(kLabelOneKey, kLabelOneValue, kLabelOneExpInt));
+  EXPECT_TRUE(el.SetLabel(kLabelTwoKey, kLabelTwoValue, kLabelTwoExpInt));
+  EXPECT_TRUE(el.SetLabel(kLabelOldKey, kLabelOldValue, kLabelOldExpInt));
+  EXPECT_EQ(3, el.NumLabels());
+
+  el.SetPreserveExpiredLabels(false);
+  CString serialized = el.Serialize();
+  EXPECT_STREQ(serialized, kLabelNewCombined);
+
+  el.SetPreserveExpiredLabels(true);
+  serialized = el.Serialize();
+  EXPECT_STREQ(serialized, kLabelAllCombined);
+}
+
+
+TEST(ExperimentLabelsTest, Deserialize_EmptyString) {
+  ExperimentLabels el;
+
+  EXPECT_TRUE(el.Deserialize(_T("")));
+  EXPECT_EQ(0, el.NumLabels());
+}
+
+TEST(ExperimentLabelsTest, Deserialize_Single_Valid) {
+  ExperimentLabels el;
+
+  EXPECT_TRUE(el.Deserialize(kLabelOneCombined));
+  EXPECT_EQ(1, el.NumLabels());
+
+  CString key;
+  CString value;
+  time64 expiration;
+  el.GetLabelByIndex(0, &key, &value, &expiration);
+  EXPECT_STREQ(key, kLabelOneKey);
+  EXPECT_STREQ(value, kLabelOneValue);
+  EXPECT_EQ(expiration, kLabelOneExpInt);
+}
+
+TEST(ExperimentLabelsTest, Deserialize_Multi_Valid) {
+  ExperimentLabels el;
+
+  EXPECT_TRUE(el.Deserialize(kLabelNewCombined));
+  EXPECT_EQ(2, el.NumLabels());
+
+  CString key;
+  CString value;
+  time64 expiration;
+  el.GetLabelByIndex(0, &key, &value, &expiration);
+  EXPECT_STREQ(key, kLabelOneKey);
+  EXPECT_STREQ(value, kLabelOneValue);
+  EXPECT_EQ(expiration, kLabelOneExpInt);
+  el.GetLabelByIndex(1, &key, &value, &expiration);
+  EXPECT_STREQ(key, kLabelTwoKey);
+  EXPECT_STREQ(value, kLabelTwoValue);
+  EXPECT_EQ(expiration, kLabelTwoExpInt);
+}
+
+TEST(ExperimentLabelsTest, Deserialize_Single_Valid_Expired) {
+  ExperimentLabels el;
+
+  EXPECT_TRUE(el.Deserialize(kLabelOldCombined));
+  EXPECT_EQ(0, el.NumLabels());
+
+  el.SetPreserveExpiredLabels(true);
+  EXPECT_TRUE(el.Deserialize(kLabelOldCombined));
+  EXPECT_EQ(1, el.NumLabels());
+
+  CString key;
+  CString value;
+  time64 expiration;
+  el.GetLabelByIndex(0, &key, &value, &expiration);
+  EXPECT_STREQ(key, kLabelOldKey);
+  EXPECT_STREQ(value, kLabelOldValue);
+  EXPECT_EQ(expiration, kLabelOldExpInt);
+}
+
+TEST(ExperimentLabelsTest, Deserialize_Multi_Valid_Expired) {
+  ExperimentLabels el;
+
+  EXPECT_TRUE(el.Deserialize(kLabelAllCombined));
+  EXPECT_EQ(2, el.NumLabels());
+  EXPECT_TRUE(el.ContainsKey(kLabelOneKey));
+  EXPECT_TRUE(el.ContainsKey(kLabelTwoKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelOldKey));
+
+  el.ClearAllLabels();
+
+  el.SetPreserveExpiredLabels(true);
+  EXPECT_TRUE(el.Deserialize(kLabelAllCombined));
+  EXPECT_EQ(3, el.NumLabels());
+  EXPECT_TRUE(el.ContainsKey(kLabelOneKey));
+  EXPECT_TRUE(el.ContainsKey(kLabelTwoKey));
+  EXPECT_TRUE(el.ContainsKey(kLabelOldKey));
+}
+
+TEST(ExperimentLabelsTest, Deserialize_Invalid_ZeroLengthKey) {
+  ExperimentLabels el;
+
+  // Base case: "=valid_value|valid_exp"
+  const TCHAR* const invalid_segment = LABEL_DELIMITER_KV
+                                       LABELONE_VALUE
+                                       LABEL_DELIMITER_EX
+                                       LABELONE_EXP_STR;
+  EXPECT_FALSE(el.Deserialize(invalid_segment));
+  EXPECT_EQ(0, el.NumLabels());
+
+  // Variant case 1: "=valid_value|valid_exp;valid_key=valid_value|valid_exp"
+  CString variant1 = invalid_segment;
+  variant1.Append(LABEL_DELIMITER_LA);
+  variant1.Append(LABELTWO_COMBINED);
+  EXPECT_FALSE(el.Deserialize(variant1));
+  EXPECT_EQ(0, el.NumLabels());
+
+  // Variant case 2: "valid_key=valid_value|valid_exp;=valid_value|valid_exp"
+  CString variant2 = LABELTWO_COMBINED;
+  variant2.Append(LABEL_DELIMITER_LA);
+  variant2.Append(invalid_segment);
+  EXPECT_FALSE(el.Deserialize(variant2));
+  EXPECT_EQ(0, el.NumLabels());
+}
+
+TEST(ExperimentLabelsTest, Deserialize_Invalid_ZeroLengthValue) {
+  ExperimentLabels el;
+
+  // Base case: "valid_key=|valid_exp"
+  const TCHAR* const invalid_segment = LABELONE_KEY
+                                       LABEL_DELIMITER_KV
+                                       LABEL_DELIMITER_EX
+                                       LABELONE_EXP_STR;
+  EXPECT_FALSE(el.Deserialize(invalid_segment));
+  EXPECT_EQ(0, el.NumLabels());
+
+  // Variant case 1: "valid_key=|valid_exp;valid_key=valid_value|valid_exp"
+  CString variant1 = invalid_segment;
+  variant1.Append(LABEL_DELIMITER_LA);
+  variant1.Append(LABELTWO_COMBINED);
+  EXPECT_FALSE(el.Deserialize(variant1));
+  EXPECT_EQ(0, el.NumLabels());
+
+  // Variant case 2: "valid_key=valid_value|valid_exp;valid_key=|valid_exp"
+  el.ClearAllLabels();
+  CString variant2 = LABELTWO_COMBINED;
+  variant2.Append(LABEL_DELIMITER_LA);
+  variant2.Append(invalid_segment);
+  EXPECT_FALSE(el.Deserialize(variant2));
+  EXPECT_EQ(0, el.NumLabels());
+}
+
+TEST(ExperimentLabelsTest, Deserialize_Invalid_ZeroLengthDate) {
+  ExperimentLabels el;
+
+  // Base case: "valid_key=valid_value|"
+  const TCHAR* const invalid_segment = LABELONE_KEY
+                                       LABEL_DELIMITER_KV
+                                       LABELONE_VALUE
+                                       LABEL_DELIMITER_EX;
+  EXPECT_FALSE(el.Deserialize(invalid_segment));
+  EXPECT_EQ(0, el.NumLabels());
+
+  // Variant case 1: "valid_key=valid_value|;valid_key=valid_value|valid_exp"
+  CString variant1 = invalid_segment;
+  variant1.Append(LABEL_DELIMITER_LA);
+  variant1.Append(LABELTWO_COMBINED);
+  EXPECT_FALSE(el.Deserialize(variant1));
+  EXPECT_EQ(0, el.NumLabels());
+
+  // Variant case 2: "valid_key=valid_value|valid_exp;valid_key=valid_value|"
+  CString variant2 = LABELTWO_COMBINED;
+  variant2.Append(LABEL_DELIMITER_LA);
+  variant2.Append(invalid_segment);
+  EXPECT_FALSE(el.Deserialize(variant2));
+  EXPECT_EQ(0, el.NumLabels());
+}
+
+TEST(ExperimentLabelsTest, Deserialize_Invalid_ExtraDelimiters) {
+  ExperimentLabels el;
+
+  // Repeated equals: "k==v|e"
+  EXPECT_FALSE(el.Deserialize(LABELONE_KEY
+                              LABEL_DELIMITER_KV
+                              LABEL_DELIMITER_KV
+                              LABELONE_VALUE
+                              LABEL_DELIMITER_EX
+                              LABELONE_EXP_STR));
+
+  // Repeated pipe: "k=v||e"
+  EXPECT_FALSE(el.Deserialize(LABELONE_KEY
+                              LABEL_DELIMITER_KV
+                              LABELONE_VALUE
+                              LABEL_DELIMITER_EX
+                              LABEL_DELIMITER_EX
+                              LABELONE_EXP_STR));
+
+  // Degenerate: "=|;=|"
+  EXPECT_FALSE(el.Deserialize(LABEL_DELIMITER_KV
+                              LABEL_DELIMITER_EX
+                              LABEL_DELIMITER_LA
+                              LABEL_DELIMITER_KV
+                              LABEL_DELIMITER_EX));
+
+  EXPECT_EQ(0, el.NumLabels());
+}
+
+TEST(ExperimentLabelsTest, Deserialize_Valid_ExtraLabelDelimiters) {
+  ExperimentLabels el;
+
+  // We support (but discourage) leading/trailing/repeated separators;
+  // for example, "k=v|e;;;;k=v|e" should cleanly parse (two keys), as
+  // should ";;;k=v|e" and "k=v|e;;;" (one key each) and ";;;;;" (no keys).
+
+  // Repeated separator: "k=v|e;;k=v|e"
+  EXPECT_TRUE(el.Deserialize(LABELONE_COMBINED
+                             LABEL_DELIMITER_LA
+                             LABEL_DELIMITER_LA
+                             LABELTWO_COMBINED));
+  EXPECT_EQ(2, el.NumLabels());
+  el.ClearAllLabels();
+
+  // Leading separator: ";k=v|e"
+  EXPECT_TRUE(el.Deserialize(LABEL_DELIMITER_LA LABELONE_COMBINED));
+  EXPECT_EQ(1, el.NumLabels());
+  el.ClearAllLabels();
+
+  // Trailing separator: "k=v|e;"
+  EXPECT_TRUE(el.Deserialize(LABELONE_COMBINED LABEL_DELIMITER_LA));
+  EXPECT_EQ(1, el.NumLabels());
+  el.ClearAllLabels();
+
+  // Degenerate: ";;;"
+  EXPECT_TRUE(el.Deserialize(LABEL_DELIMITER_LA
+                             LABEL_DELIMITER_LA
+                             LABEL_DELIMITER_LA));
+  EXPECT_EQ(0, el.NumLabels());
+}
+
+TEST(ExperimentLabelsTest, Deserialize_Invalid_MissingDelimiters) {
+  ExperimentLabels el;
+
+  // Missing all delimiters, single string (kve)
+  EXPECT_FALSE(el.Deserialize(LABELONE_KEY
+                              LABELONE_VALUE
+                              LABELONE_EXP_STR));
+
+  // Missing expiration delimiter, single string (k=ve)
+  EXPECT_FALSE(el.Deserialize(LABELONE_KEY
+                              LABEL_DELIMITER_KV
+                              LABELONE_VALUE
+                              LABELONE_EXP_STR));
+
+  // Missing value delimiter, single string (kv|e)
+  EXPECT_FALSE(el.Deserialize(LABELONE_KEY
+                              LABELONE_VALUE
+                              LABEL_DELIMITER_EX
+                              LABELONE_EXP_STR));
+
+  // Missing label delimiter, multi-label (k=v|ek=v|e)
+  EXPECT_FALSE(el.Deserialize(LABELONE_COMBINED LABELTWO_COMBINED));
+
+  // Missing value+exp delimiter, multi-label, first (kve;k=v|e)
+  EXPECT_FALSE(el.Deserialize(LABELONE_KEY
+                              LABELONE_VALUE
+                              LABELONE_EXP_STR
+                              LABEL_DELIMITER_LA
+                              LABELTWO_COMBINED));
+
+  // Missing value+exp delimiter, multi-label, second (k=v|e;kve)
+  EXPECT_FALSE(el.Deserialize(LABELONE_COMBINED
+                              LABEL_DELIMITER_LA
+                              LABELTWO_KEY
+                              LABELTWO_VALUE
+                              LABELTWO_EXP_STR));
+
+  // Missing value delimiter, multi-label, first (kv|e;k=v|e)
+  EXPECT_FALSE(el.Deserialize(LABELONE_KEY
+                              LABELONE_VALUE
+                              LABEL_DELIMITER_EX
+                              LABELONE_EXP_STR
+                              LABEL_DELIMITER_LA
+                              LABELTWO_COMBINED));
+
+  // Missing value delimiter, multi-label, second (k=v|e;kv|e)
+  EXPECT_FALSE(el.Deserialize(LABELONE_COMBINED
+                              LABEL_DELIMITER_LA
+                              LABELTWO_KEY
+                              LABELTWO_VALUE
+                              LABEL_DELIMITER_EX
+                              LABELTWO_EXP_STR));
+
+  // Missing expiration delimiter, multi-label, first (k=ve;k=v|e)
+  EXPECT_FALSE(el.Deserialize(LABELONE_KEY
+                              LABEL_DELIMITER_KV
+                              LABELONE_VALUE
+                              LABELONE_EXP_STR
+                              LABEL_DELIMITER_LA
+                              LABELTWO_COMBINED));
+
+  // Missing expiration delimiter, multi-label, second (k=v|e;k=ve)
+  EXPECT_FALSE(el.Deserialize(LABELONE_COMBINED
+                              LABEL_DELIMITER_LA
+                              LABELTWO_KEY
+                              LABEL_DELIMITER_KV
+                              LABELTWO_VALUE
+                              LABELTWO_EXP_STR));
+
+  EXPECT_EQ(0, el.NumLabels());
+}
+
+TEST(ExperimentLabelsTest, DeserializeAndApplyDelta_Append) {
+  ExperimentLabels el;
+
+  EXPECT_TRUE(el.SetLabel(kLabelOneKey, kLabelOneValue, kLabelOneExpInt));
+  EXPECT_EQ(1, el.NumLabels());
+  EXPECT_TRUE(el.DeserializeAndApplyDelta(kLabelTwoCombined));
+  EXPECT_EQ(2, el.NumLabels());
+
+  CString value;
+  time64 expiration;
+  EXPECT_TRUE(el.FindLabelByKey(kLabelOneKey, &value, &expiration));
+  EXPECT_STREQ(value, kLabelOneValue);
+  EXPECT_EQ(expiration, kLabelOneExpInt);
+  EXPECT_TRUE(el.FindLabelByKey(kLabelTwoKey, &value, &expiration));
+  EXPECT_STREQ(value, kLabelTwoValue);
+  EXPECT_EQ(expiration, kLabelTwoExpInt);
+}
+
+TEST(ExperimentLabelsTest, DeserializeAndApplyDelta_Append_Expired) {
+  ExperimentLabels el;
+
+  EXPECT_TRUE(el.SetLabel(kLabelOneKey, kLabelOneValue, kLabelOneExpInt));
+  EXPECT_EQ(1, el.NumLabels());
+  EXPECT_TRUE(el.ContainsKey(kLabelOneKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelOldKey));
+
+  EXPECT_TRUE(el.DeserializeAndApplyDelta(kLabelOldCombined));
+  EXPECT_EQ(1, el.NumLabels());
+  EXPECT_TRUE(el.ContainsKey(kLabelOneKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelOldKey));
+}
+
+TEST(ExperimentLabelsTest, DeserializeAndApplyDelta_Overwrite_Single) {
+  ExperimentLabels el;
+
+  EXPECT_TRUE(el.SetLabel(kLabelOneKey, kLabelOldValue, kLabelOneExpInt));
+  EXPECT_EQ(1, el.NumLabels());
+  EXPECT_TRUE(el.ContainsKey(kLabelOneKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelTwoKey));
+
+  CString value;
+  time64 expiration;
+  EXPECT_TRUE(el.FindLabelByKey(kLabelOneKey, &value, &expiration));
+  EXPECT_STREQ(value, kLabelOldValue);
+  EXPECT_EQ(expiration, kLabelOneExpInt);
+
+  EXPECT_TRUE(el.DeserializeAndApplyDelta(kLabelOneCombined));
+  EXPECT_EQ(1, el.NumLabels());
+  EXPECT_TRUE(el.ContainsKey(kLabelOneKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelTwoKey));
+
+  EXPECT_TRUE(el.FindLabelByKey(kLabelOneKey, &value, &expiration));
+  EXPECT_STREQ(value, kLabelOneValue);
+  EXPECT_EQ(expiration, kLabelOneExpInt);
+}
+
+TEST(ExperimentLabelsTest, DeserializeAndApplyDelta_Overwrite_Multi) {
+  ExperimentLabels el;
+
+  EXPECT_TRUE(el.SetLabel(kLabelOneKey, kLabelOldValue, kLabelOneExpInt));
+  EXPECT_EQ(1, el.NumLabels());
+  EXPECT_TRUE(el.ContainsKey(kLabelOneKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelTwoKey));
+
+  EXPECT_TRUE(el.DeserializeAndApplyDelta(kLabelNewCombined));
+  EXPECT_EQ(2, el.NumLabels());
+  EXPECT_TRUE(el.ContainsKey(kLabelOneKey));
+  EXPECT_TRUE(el.ContainsKey(kLabelTwoKey));
+
+  CString value;
+  time64 expiration;
+  EXPECT_TRUE(el.FindLabelByKey(kLabelOneKey, &value, &expiration));
+  EXPECT_STREQ(value, kLabelOneValue);
+  EXPECT_EQ(expiration, kLabelOneExpInt);
+  EXPECT_TRUE(el.FindLabelByKey(kLabelTwoKey, &value, &expiration));
+  EXPECT_STREQ(value, kLabelTwoValue);
+  EXPECT_EQ(expiration, kLabelTwoExpInt);
+}
+
+TEST(ExperimentLabelsTest, DeserializeAndApplyDelta_Overwrite_Single_Expired) {
+  ExperimentLabels el;
+
+  EXPECT_TRUE(el.SetLabel(kLabelOldKey, kLabelOldValue, kLabelOneExpInt));
+  EXPECT_TRUE(el.SetLabel(kLabelTwoKey, kLabelTwoValue, kLabelTwoExpInt));
+  EXPECT_EQ(2, el.NumLabels());
+  EXPECT_TRUE(el.ContainsKey(kLabelOldKey));
+  EXPECT_TRUE(el.ContainsKey(kLabelTwoKey));
+
+  EXPECT_TRUE(el.DeserializeAndApplyDelta(kLabelOldCombined));
+  EXPECT_EQ(1, el.NumLabels());
+  EXPECT_FALSE(el.ContainsKey(kLabelOldKey));
+  EXPECT_TRUE(el.ContainsKey(kLabelTwoKey));
+}
+
+TEST(ExperimentLabelsTest, DeserializeAndApplyDelta_Overwrite_Multi_Expired) {
+  ExperimentLabels el;
+
+  EXPECT_TRUE(el.SetLabel(kLabelOneKey, kLabelTwoValue, kLabelOneExpInt));
+  EXPECT_TRUE(el.SetLabel(kLabelTwoKey, kLabelOneValue, kLabelTwoExpInt));
+  EXPECT_TRUE(el.SetLabel(kLabelOldKey, kLabelOldValue, kLabelOneExpInt));
+  EXPECT_EQ(3, el.NumLabels());
+  EXPECT_TRUE(el.ContainsKey(kLabelOneKey));
+  EXPECT_TRUE(el.ContainsKey(kLabelTwoKey));
+  EXPECT_TRUE(el.ContainsKey(kLabelOldKey));
+
+  EXPECT_TRUE(el.DeserializeAndApplyDelta(kLabelAllCombined));
+  EXPECT_EQ(2, el.NumLabels());
+  EXPECT_TRUE(el.ContainsKey(kLabelOneKey));
+  EXPECT_TRUE(el.ContainsKey(kLabelTwoKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelOldKey));
+}
+
+TEST(ExperimentLabelsTest, Expire) {
+  ExperimentLabels el;
+
+  el.SetPreserveExpiredLabels(true);
+  EXPECT_TRUE(el.SetLabel(kLabelOneKey, kLabelOneValue, kLabelOneExpInt));
+  EXPECT_TRUE(el.SetLabel(kLabelTwoKey, kLabelTwoValue, kLabelTwoExpInt));
+  EXPECT_TRUE(el.SetLabel(kLabelOldKey, kLabelOldValue, kLabelOldExpInt));
+  EXPECT_EQ(3, el.NumLabels());
+
+  el.ExpireLabels();
+  EXPECT_EQ(2, el.NumLabels());
+  EXPECT_TRUE(el.ContainsKey(kLabelOneKey));
+  EXPECT_TRUE(el.ContainsKey(kLabelTwoKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelOldKey));
+
+  EXPECT_TRUE(el.SetLabel(kLabelOneKey, kLabelOneValue, kLabelOldExpInt));
+  el.ExpireLabels();
+  EXPECT_EQ(1, el.NumLabels());
+  EXPECT_FALSE(el.ContainsKey(kLabelOneKey));
+  EXPECT_TRUE(el.ContainsKey(kLabelTwoKey));
+  EXPECT_FALSE(el.ContainsKey(kLabelOldKey));
+}
+
+}  // namespace omaha
+
diff --git a/common/extra_args_parser.cc b/common/extra_args_parser.cc
new file mode 100644
index 0000000..f6658a5
--- /dev/null
+++ b/common/extra_args_parser.cc
@@ -0,0 +1,319 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/extra_args_parser.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/command_line.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/goopdate_utils.h"
+
+namespace omaha {
+
+namespace {
+
+HRESULT ConvertUtf8UrlEncodedString(const CString& encoded_string,
+                                    CString* unencoded_string) {
+  CString trimmed_val = encoded_string;
+  trimmed_val.Trim();
+  if (trimmed_val.IsEmpty()) {
+    return E_INVALIDARG;
+  }
+
+  // The value is a utf8 encoded url escaped string that is stored as a
+  // unicode string, convert it into a wide string.
+  CString app_name;
+  HRESULT hr = Utf8UrlEncodedStringToWideString(trimmed_val, unencoded_string);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT Validate(const TCHAR* extra_args) {
+  CString extra_args_str(extra_args);
+  if (extra_args_str.IsEmpty()) {
+    return E_INVALIDARG;
+  }
+
+  if (-1 != extra_args_str.FindOneOf(kDisallowedCharsInExtraArgs)) {
+    // A '/' was found in the "extra" arguments or "extra" arguments were
+    // not specified before the next command.
+    return E_INVALIDARG;
+  }
+
+  return S_OK;
+}
+
+// Handles tokens from the app arguments string.
+HRESULT HandleAppArgsToken(const CString& token,
+                           CommandLineExtraArgs* args,
+                           int* cur_app_args_index) {
+  ASSERT1(args);
+  ASSERT1(cur_app_args_index);
+  ASSERT1(*cur_app_args_index < static_cast<int>(args->apps.size()));
+
+  CString name;
+  CString value;
+  if (!ParseNameValuePair(token, kNameValueSeparatorChar, &name, &value)) {
+    return E_INVALIDARG;
+  }
+
+  if (name.CompareNoCase(kExtraArgAppGuid) == 0) {
+    *cur_app_args_index = -1;
+    for (size_t i = 0; i < args->apps.size(); ++i) {
+      if (!value.CompareNoCase(GuidToString(args->apps[i].app_guid))) {
+        *cur_app_args_index = i;
+        break;
+      }
+    }
+
+    if (-1 == *cur_app_args_index) {
+      return E_INVALIDARG;
+    }
+  } else if (name.CompareNoCase(kExtraArgInstallerData) == 0) {
+    if (-1 == *cur_app_args_index) {
+      return E_INVALIDARG;
+    }
+    args->apps[*cur_app_args_index].encoded_installer_data = value;
+  } else {
+    // Unrecognized token
+    return E_INVALIDARG;
+  }
+
+  return S_OK;
+}
+
+HRESULT ParseAppArgs(const TCHAR* app_args, CommandLineExtraArgs* args) {
+  if (!app_args || !*app_args) {
+    return S_OK;
+  }
+
+  HRESULT hr = Validate(app_args);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  int cur_app_args_index = -1;
+  int pos = 0;
+  CString input_str = app_args;
+  CString token = input_str.Tokenize(kExtraArgsSeparators, pos);
+  while (!token.IsEmpty()) {
+    hr = HandleAppArgsToken(token, args, &cur_app_args_index);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    token = input_str.Tokenize(kExtraArgsSeparators, pos);
+  }
+
+  return S_OK;
+}
+
+HRESULT StringToNeedsAdmin(const TCHAR* str, NeedsAdmin* value) {
+  ASSERT1(str);
+  ASSERT1(value);
+
+  const TCHAR* const kFalse   = _T("false");
+  const TCHAR* const kTrue    = _T("true");
+  const TCHAR* const kPrefers = _T("prefers");
+
+  if (_tcsicmp(kFalse, str) == 0) {
+    *value = NEEDS_ADMIN_NO;
+  } else if (_tcsicmp(kTrue, str) == 0) {
+    *value = NEEDS_ADMIN_YES;
+  } else if (_tcsicmp(kPrefers, str) == 0) {
+    *value = NEEDS_ADMIN_PREFERS;
+  } else {
+    return E_INVALIDARG;
+  }
+  return S_OK;
+}
+
+}  // namespace
+
+// If no bundle name is specified, the first app's name is used.
+// TODO(omaha): There is no enforcement of required or optional values of
+// the extra arguments. Come up with a way to enforce this.
+// TODO(omaha): If we prefix extra_args with '/' and replace all '=' with ' '
+// and all & with '/' then we should be able to use the CommandLineParser
+// class to pull out the values here.  We'd need to define scenarios for all
+// permutations of ExtraArgs, but this shouldn't be difficult to get the right
+// ones.
+
+HRESULT ExtraArgsParser::Parse(const TCHAR* extra_args,
+                               const TCHAR* app_args,
+                               CommandLineExtraArgs* args) {
+  HRESULT hr = ParseExtraArgs(extra_args, args);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return ParseAppArgs(app_args, args);
+}
+
+HRESULT ExtraArgsParser::ParseExtraArgs(const TCHAR* extra_args,
+                                        CommandLineExtraArgs* args) {
+  HRESULT hr = Validate(extra_args);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  first_app_ = true;
+  int pos = 0;
+  CString input_str(extra_args);
+  CString token = input_str.Tokenize(kExtraArgsSeparators, pos);
+  while (!token.IsEmpty()) {
+    CORE_LOG(L2, (_T("[ExtraArgsParser::Parse][token=%s]"), token));
+    hr = HandleToken(token, args);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    // Continue parsing
+    token = input_str.Tokenize(kExtraArgsSeparators, pos);
+  }
+
+  // Save the arguments for the last application.
+  args->apps.push_back(cur_extra_app_args_);
+
+  ASSERT1(!(args->runtime_only && args->apps[0].app_guid != GUID_NULL));
+
+  if (args->bundle_name.IsEmpty()) {
+    ASSERT1(!args->apps.empty());
+    args->bundle_name = args->apps[0].app_name;
+  }
+
+  return S_OK;
+}
+
+// Handles tokens from the extra arguments string.
+HRESULT ExtraArgsParser::HandleToken(const CString& token,
+                                     CommandLineExtraArgs* args) {
+  CString name;
+  CString value;
+  if (!ParseNameValuePair(token, kNameValueSeparatorChar, &name, &value)) {
+    return E_INVALIDARG;
+  }
+
+  // The first set of args apply to all apps. They may occur at any point, but
+  // only the last occurrence is recorded.
+  if (name.CompareNoCase(kExtraArgBundleName) == 0) {
+    if (value.GetLength() > kMaxNameLength) {
+      return E_INVALIDARG;
+    }
+    HRESULT hr = ConvertUtf8UrlEncodedString(value,
+                                             &args->bundle_name);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  } else if (name.CompareNoCase(kExtraArgInstallationId) == 0) {
+    ASSERT1(!value.IsEmpty());
+    if (FAILED(StringToGuidSafe(value, &args->installation_id))) {
+      return E_INVALIDARG;
+    }
+  } else if (name.CompareNoCase(kExtraArgBrandCode) == 0) {
+    if (value.GetLength() > kBrandIdLength) {
+      return E_INVALIDARG;
+    }
+    args->brand_code = value;
+  } else if (name.CompareNoCase(kExtraArgClientId) == 0) {
+    args->client_id = value;
+  } else if (name.CompareNoCase(kExtraArgOmahaExperimentLabels) == 0) {
+    HRESULT hr = ConvertUtf8UrlEncodedString(value,
+                                             &args->experiment_labels);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  } else if (name.CompareNoCase(kExtraArgReferralId) == 0) {
+    args->referral_id = value;
+  } else if (name.CompareNoCase(kExtraArgBrowserType) == 0) {
+    BrowserType type = BROWSER_UNKNOWN;
+    if (SUCCEEDED(goopdate_utils::ConvertStringToBrowserType(value, &type))) {
+      args->browser_type = type;
+    }
+  } else if (name.CompareNoCase(kExtraArgLanguage) == 0) {
+    if (value.GetLength() > kLangMaxLength) {
+      return E_INVALIDARG;
+    }
+    // Even if we don't support the language, we want to pass it to the
+    // installer. Omaha will pick its language later. See http://b/1336966.
+    args->language = value;
+  } else if (name.CompareNoCase(kExtraArgUsageStats) == 0) {
+    if (!String_StringToTristate(value, &args->usage_stats_enable)) {
+      return E_INVALIDARG;
+    }
+  } else if (name.CompareNoCase(kExtraArgRuntime) == 0) {
+    if (!args->apps.empty() || cur_extra_app_args_.app_guid != GUID_NULL) {
+      return E_INVALIDARG;
+    }
+    args->runtime_only = true;
+
+  // The following args are per-app.
+  } else if (name.CompareNoCase(kExtraArgAdditionalParameters) == 0) {
+    cur_extra_app_args_.ap = value;
+  } else if (name.CompareNoCase(kExtraArgTTToken) == 0) {
+    cur_extra_app_args_.tt_token = value;
+  } else if (name.CompareNoCase(kExtraArgExperimentLabels) == 0) {
+    HRESULT hr = ConvertUtf8UrlEncodedString(
+        value,
+        &cur_extra_app_args_.experiment_labels);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  } else if (name.CompareNoCase(kExtraArgAppGuid) == 0) {
+    if (!first_app_) {
+      // Save the arguments for the application we have been processing.
+      args->apps.push_back(cur_extra_app_args_);
+    }
+    cur_extra_app_args_ = CommandLineAppArgs();
+
+    HRESULT hr = StringToGuidSafe(value, &cur_extra_app_args_.app_guid);
+    if (FAILED(hr)) {
+      return hr;
+    }
+    if (cur_extra_app_args_.app_guid == GUID_NULL || args->runtime_only) {
+      return E_INVALIDARG;
+    }
+    first_app_ = false;
+  } else if (name.CompareNoCase(kExtraArgAppName) == 0) {
+    if (value.GetLength() > kMaxNameLength) {
+      return E_INVALIDARG;
+    }
+    HRESULT hr = ConvertUtf8UrlEncodedString(value,
+                                             &cur_extra_app_args_.app_name);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  } else if (name.CompareNoCase(kExtraArgNeedsAdmin) == 0) {
+    if (FAILED(StringToNeedsAdmin(value, &cur_extra_app_args_.needs_admin))) {
+      return E_INVALIDARG;
+    }
+  } else if (name.CompareNoCase(kExtraArgInstallDataIndex) == 0) {
+    cur_extra_app_args_.install_data_index = value;
+  } else {
+    // Unrecognized token
+    return E_INVALIDARG;
+  }
+
+  return S_OK;
+}
+
+}  // namespace omaha
diff --git a/common/extra_args_parser.h b/common/extra_args_parser.h
new file mode 100644
index 0000000..797ce72
--- /dev/null
+++ b/common/extra_args_parser.h
@@ -0,0 +1,57 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_EXTRA_ARGS_PARSER_H__
+#define OMAHA_COMMON_EXTRA_ARGS_PARSER_H__
+
+#include <windows.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+#include "omaha/common/command_line.h"
+
+namespace omaha {
+
+// This class handles tokenizing and parsing the ExtraArgs portion of the
+// command line.
+// The format of the extra arguments is as follows:
+// appguid={}&.....appguid={}....
+// appguid has to be the first value of the extra arguments.
+// Each appguid defines one product.
+class ExtraArgsParser {
+ public:
+  ExtraArgsParser() : first_app_(false) {}
+
+  // Parses the extra_args (the tag) and app_args (/appargs) strings, storing
+  // the results in args.
+  HRESULT Parse(const TCHAR* extra_args,
+                const TCHAR* app_args,
+                CommandLineExtraArgs* args);
+
+ private:
+  HRESULT ParseExtraArgs(const TCHAR* extra_args, CommandLineExtraArgs* args);
+
+  // Performs validation against extra_args and if it's valid, stores the
+  // extra_args value into args->extra_args.
+  HRESULT HandleToken(const CString& token, CommandLineExtraArgs* args);
+
+  CommandLineAppArgs cur_extra_app_args_;
+  bool first_app_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(ExtraArgsParser);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_EXTRA_ARGS_PARSER_H__
diff --git a/common/extra_args_parser_unittest.cc b/common/extra_args_parser_unittest.cc
new file mode 100644
index 0000000..00f96a2
--- /dev/null
+++ b/common/extra_args_parser_unittest.cc
@@ -0,0 +1,1663 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/command_line.h"
+#include "omaha/common/extra_args_parser.h"
+#include "omaha/testing/resource.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+extern void VerifyCommandLineArgs(const CommandLineArgs& expected,
+                                  const CommandLineArgs& actual);
+
+extern void VerifyCommandLineExtraArgs(const CommandLineExtraArgs& expected,
+                                       const CommandLineExtraArgs& actual);
+
+void VerifyBrowserType(const CommandLineExtraArgs& args,
+                       const CString& app_guid,
+                       BrowserType type) {
+  CommandLineAppArgs app_args;
+  app_args.app_guid = StringToGuid(app_guid);
+
+  CommandLineExtraArgs expected;
+  expected.browser_type = type;
+  expected.apps.push_back(app_args);
+
+  VerifyCommandLineExtraArgs(expected, args);
+}
+
+// TODO(omaha): Consider eliminating most uses of this method. The tests will
+// actually be less lines now that the parameters have grown so much.
+void VerifyExtraArgsHaveSpecificValues(
+         const CommandLineExtraArgs& args,
+         const CString& expected_app_guid,
+         const CString& expected_app_name,
+         const CString& expected_app_experiment_labels,
+         NeedsAdmin expected_needs_admin,
+         Tristate expected_usage_stats_enable,
+         bool expected_runtime_only,
+         const CString& expected_bundle_name,
+         const GUID& expected_installation_id,
+         const CString& expected_brand_code,
+         const CString& expected_client_id,
+         const CString& expected_omaha_experiment_labels,
+         const CString& expected_referral_id,
+         const CString& expected_ap,
+         const CString& expected_tt,
+         const CString& expected_encoded_installer_data,
+         const CString& expected_install_data_index) {
+  CommandLineAppArgs app_args;
+  app_args.app_guid = StringToGuid(expected_app_guid);
+  app_args.app_name = expected_app_name;
+  app_args.needs_admin = expected_needs_admin;
+  app_args.ap = expected_ap;
+  app_args.tt_token = expected_tt;
+  app_args.encoded_installer_data = expected_encoded_installer_data;
+  app_args.install_data_index = expected_install_data_index;
+  app_args.experiment_labels = expected_app_experiment_labels;
+
+  CommandLineExtraArgs expected;
+  expected.apps.push_back(app_args);
+  expected.bundle_name = expected_bundle_name;
+  expected.installation_id = expected_installation_id;
+  expected.brand_code = expected_brand_code;
+  expected.client_id = expected_client_id;
+  expected.experiment_labels = expected_omaha_experiment_labels;
+  expected.referral_id = expected_referral_id;
+  expected.usage_stats_enable = expected_usage_stats_enable;
+  expected.runtime_only = expected_runtime_only;
+
+  VerifyCommandLineExtraArgs(expected, args);
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsInvalidValueNameIsSupersetOfValidName) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
+                       _T("appname1=Hello");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsAppNameSpaceForValue) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
+                       _T("appname= ");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsAppNameValid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+
+  CString app_guid = _T("{D0324988-DA8A-49e5-BCE5-925FCD04EAB7}");
+  CString extra_args = _T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
+                       _T("appname=Test");
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+
+  CommandLineAppArgs app_args;
+
+  app_args.app_name = _T("Test");
+  app_args.app_guid = StringToGuid(app_guid);
+  CommandLineExtraArgs expected;
+  expected.apps.push_back(app_args);
+  expected.bundle_name = _T("Test");
+
+  VerifyCommandLineExtraArgs(expected, args);
+}
+
+// This must work because the enterprise MSI code assumes spaces are allowed.
+TEST(ExtraArgsParserTest, ExtraArgumentsAppNameWithSpace) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
+                       _T("appname=Test App");
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+
+  CommandLineAppArgs app_args;
+  app_args.app_guid =
+      StringToGuid(_T("{D0324988-DA8A-49e5-BCE5-925FCD04EAB7}"));
+  app_args.app_name = _T("Test App");
+
+  CommandLineExtraArgs expected;
+  expected.apps.push_back(app_args);
+  expected.bundle_name = _T("Test App");
+
+  VerifyCommandLineExtraArgs(expected, args);
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsAppNameWithSpaceAtEnd) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
+                       _T("appname= T Ap p ");
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+
+  CommandLineAppArgs app_args;
+  app_args.app_guid =
+      StringToGuid(_T("{D0324988-DA8A-49e5-BCE5-925FCD04EAB7}"));
+  app_args.app_name = _T("T Ap p");
+
+  CommandLineExtraArgs expected;
+  expected.apps.push_back(app_args);
+  expected.bundle_name = _T("T Ap p");
+
+  VerifyCommandLineExtraArgs(expected, args);
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsAppNameWithMultipleSpaces) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
+                       _T("appname= T Ap p");
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+
+  CommandLineAppArgs app_args;
+  app_args.app_guid =
+      StringToGuid(_T("{D0324988-DA8A-49e5-BCE5-925FCD04EAB7}"));
+  app_args.app_name = _T("T Ap p");
+
+  CommandLineExtraArgs expected;
+  expected.apps.push_back(app_args);
+  expected.bundle_name = _T("T Ap p");
+
+  VerifyCommandLineExtraArgs(expected, args);
+}
+
+// The size of the application name is limited to 512 wide chars.
+TEST(ExtraArgsParserTest, ExtraArgumentsAppNameTooLong) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString str(_T('a'), 513);
+  CString extra_args;
+  extra_args.Format(_T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
+                    _T("appname=%s"), str);
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsAppNameUnicode) {
+  // Read the non-ascii string from the resources, and convert
+  // it into a utf8 encoded, url escaped string.
+  CString non_ascii_name;
+  ASSERT_TRUE(non_ascii_name.LoadString(IDS_ESCAPE_TEST));
+
+  CString wide_tag;
+  ASSERT_HRESULT_SUCCEEDED(WideStringToUtf8UrlEncodedString(non_ascii_name,
+                                                            &wide_tag));
+
+  ExtraArgsParser parser;
+  CommandLineExtraArgs args;
+  CString extra_args;
+  extra_args.Format(_T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
+                    _T("appname=%s"), wide_tag);
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+
+  CommandLineAppArgs app_args;
+  app_args.app_name = non_ascii_name;
+  app_args.app_guid =
+      StringToGuid(_T("{D0324988-DA8A-49e5-BCE5-925FCD04EAB7}"));
+
+  CommandLineExtraArgs expected;
+  expected.apps.push_back(app_args);
+  expected.bundle_name = non_ascii_name;
+
+  VerifyCommandLineExtraArgs(expected, args);
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsAppNameUnicode2) {
+  // Read the non-ascii string from the resources, and convert
+  // it into a utf8 encoded, url escaped string.
+  CString non_ascii_name;
+  ASSERT_TRUE(non_ascii_name.LoadString(IDS_ESCAPE_TEST1));
+
+  CString escaped(_T("%E0%A4%B8%E0%A5%8D%E0%A4%A5%E0%A4%BE%E0%A4%AA%E0%A4%BF")
+                  _T("%E0%A4%A4%20%E0%A4%95%E0%A4%B0%20%E0%A4%B0%E0%A4%B9%E0")
+                  _T("%A4%BE%20%E0%A4%B9%E0%A5%88%E0%A5%A4"));
+
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+
+  CString extra_args;
+  extra_args.Format(_T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
+                    _T("appname=%s"), escaped);
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+
+  CommandLineAppArgs app_args;
+  app_args.app_name = non_ascii_name;
+  app_args.app_guid =
+      StringToGuid(_T("{D0324988-DA8A-49e5-BCE5-925FCD04EAB7}"));
+
+  CommandLineExtraArgs expected;
+  expected.apps.push_back(app_args);
+  expected.bundle_name = non_ascii_name;
+
+  VerifyCommandLineExtraArgs(expected, args);
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsAppGuidValid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}");
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+
+  CommandLineAppArgs app_args;
+  const GUID guid = {0x8617EE50, 0xF91C, 0x4DC1,
+                          {0xB9, 0x37, 0x09, 0x69, 0xEE, 0xF5, 0x9B, 0x0B}};
+  app_args.app_guid = guid;
+
+  CommandLineExtraArgs expected;
+  expected.apps.push_back(app_args);
+
+  VerifyCommandLineExtraArgs(expected, args);
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsAppGuidNotAValidGuid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0}");
+  EXPECT_EQ(E_INVALIDARG, parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsAppGuidNotAGuid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid=myguid");
+  EXPECT_EQ(E_INVALIDARG, parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsNeedsAdminInvalid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("needsadmin=Hello");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsNeedsAdminSpaceForValue) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("needsadmin= ");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsNeedsAdminTrueUpperCaseT) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("")
+                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("needsadmin=True");
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+
+  CommandLineAppArgs app_args;
+  app_args.needs_admin = NEEDS_ADMIN_YES;
+  app_args.app_guid =
+      StringToGuid(_T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"));
+
+  CommandLineExtraArgs expected;
+  expected.apps.push_back(app_args);
+
+  VerifyCommandLineExtraArgs(expected, args);
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsNeedsAdminTrueLowerCaseT) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("")
+                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("needsadmin=true");
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+
+  CommandLineAppArgs app_args;
+  app_args.needs_admin = NEEDS_ADMIN_YES;
+  app_args.app_guid =
+      StringToGuid(_T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"));
+
+  CommandLineExtraArgs expected;
+  expected.apps.push_back(app_args);
+
+  VerifyCommandLineExtraArgs(expected, args);
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsNeedsFalseUpperCaseF) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("")
+                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("needsadmin=False");
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+
+  CommandLineAppArgs app_args;
+  app_args.needs_admin = NEEDS_ADMIN_NO;
+  app_args.app_guid =
+      StringToGuid(_T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"));
+
+  CommandLineExtraArgs expected;
+  expected.apps.push_back(app_args);
+
+  VerifyCommandLineExtraArgs(expected, args);
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsNeedsAdminFalseLowerCaseF) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("")
+                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("needsadmin=false");
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+
+  CommandLineAppArgs app_args;
+  app_args.needs_admin = NEEDS_ADMIN_NO;
+  app_args.app_guid =
+      StringToGuid(_T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"));
+
+  CommandLineExtraArgs expected;
+  expected.apps.push_back(app_args);
+
+  VerifyCommandLineExtraArgs(expected, args);
+}
+
+//
+// Test the handling of the contents of the extra arguments.
+//
+
+TEST(ExtraArgsParserTest, ExtraArgumentsAssignmentOnly) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("=");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsExtraAssignment1) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=1=");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsExtraAssignment2) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("=usagestats=1");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsExtraAssignment3) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=1&=");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsExtraAssignment4) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("=&usagestats=1");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsValueWithoutName) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("=hello");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+
+// Also tests ending extra arguments with '='.
+TEST(ExtraArgsParserTest, ExtraArgumentsNameWithoutValue) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsNameWithoutValueBeforeNextArgument) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("")
+                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=&client=hello");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest,
+     ExtraArgumentsNameWithoutArgumentSeparatorAfterIntValue) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("")
+                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=1client=hello");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest,
+     ExtraArgumentsNameWithoutArgumentSeparatorAfterStringValue) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("")
+                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=yesclient=hello");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsHaveDoubleAmpersand) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("")
+                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=1&&client=hello");
+
+  CString app_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                     _T("installerdata=foobar");
+
+  // Ideally, this would flag an error.
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, app_args, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_TRUE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T("hello"),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T("foobar"),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsAmpersandOnly) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("&");
+
+  // Ideally, this would flag an error.
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{00000000-0000-0000-0000-000000000000}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_NONE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsBeginInAmpersand) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("&appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=1");
+
+  // Ideally, this would flag an error.
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_TRUE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsEndInAmpersand) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("&appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=1&");
+
+  // Ideally, this would flag an error.
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_TRUE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsEmptyString) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceOnly1) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T(" ");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceOnly2) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("\t");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceOnly3) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("\r");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceOnly4) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("\n");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceOnly5) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("\r\n");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+
+//
+// Test the parsing of the extra command and its arguments into a string.
+//
+
+TEST(ExtraArgsParserTest, ExtraArgumentsOneValid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("&appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=1");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_TRUE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsTwoValid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("&appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=1&client=hello");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_TRUE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T("hello"),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsHaveSwitchInTheMiddle) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=1/other_value=9");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsHaveDoubleQuoteInTheMiddle) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=1\"/other_value=9");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest,
+       ExtraArgumentsHaveDoubleQuoteInTheMiddleAndNoForwardSlash) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=1\"other_value=9");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsHaveSpaceAndForwardSlashBeforeQuote) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=1 /other_value=9");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsHaveForwardSlashBeforeQuote) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=1/other_value=9");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsSpecifiedTwice) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=1\" \"client=10");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceBeforeArgs1) {
+  CommandLineExtraArgs args;
+  // TODO(omaha): This one passes now due to different whitespace
+  // handling.  Remove it?  Is this really a problem to have?
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T(" usagestats=1");
+  ExtraArgsParser parser;
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceBeforeArgs2) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("\tappguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=1");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceBeforeArgs3) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("\rappguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=1");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceBeforeArgs4) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("\nappguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}")
+                       _T("&usagestats=1");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceBeforeArgs5) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("\r\nusagestats=1");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsForwardSlash1) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("/");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsForwardSlash2) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("/ appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsBackwardSlash1) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("\\");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsBackwardSlash2) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("\\appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ExtraArgumentsBackwardSlash3) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("\\ appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+//
+// Test specific extra commands.
+//
+
+TEST(ExtraArgsParserTest, UsageStatsOutsideExtraCommand) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("/usagestats");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, UsageStatsOn) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=1");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_TRUE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, UsageStatsOff) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=0");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_FALSE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+// This commandline has no effect, but it's permitted.
+TEST(ExtraArgsParserTest, UsageStatsNone) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=2");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_NONE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, UsageStatsInvalidPositiveValue) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=3");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, UsageStatsInvalidNegativeValue) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=-1");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, UsageStatsValueIsString) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("usagestats=true");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, BundleNameValid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("bundlename=Google%20Bundle");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_NONE,
+      false,
+      _T("Google Bundle"),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, BundleNameNotPresentButAppNameIs) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("appname=Google%20Chrome");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T("Google Chrome"),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_NONE,
+      false,
+      _T("Google Chrome"),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+TEST(ExtraArgsParserTest, BundleNameNorAppNamePresent) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_NONE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, BundleNameNotPresentAndNoApp) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("browser=0");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{00000000-0000-0000-0000-000000000000}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_NONE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, InstallationGuidValid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("iid={98CEC468-9429-4984-AEDE-4F53C6A14869}");
+  const GUID expected_guid = {0x98CEC468, 0x9429, 0x4984,
+                              {0xAE, 0xDE, 0x4F, 0x53, 0xC6, 0xA1, 0x48, 0x69}};
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_NONE,
+      false,
+      _T(""),
+      expected_guid,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, InstallationGuidMissingBraces) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("iid=98CEC468-9429-4984-AEDE-4F53C6A14869");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, InstallationGuidMissingDashes) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("iid=98CEC46894294984AEDE4F53C6A14869");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, InstallationGuidMissingCharacter) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("iid=98CEC468-9429-4984-AEDE-4F53C6A1486");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, InstallationGuidIsString) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("iid=hello");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, BrandCodeValid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("brand=GOOG");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_NONE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T("GOOG"),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, BrandCodeTooLong) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("brand=CHMI\xe3\x83\xbb");
+
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, ClientIdValid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("client=some_partner");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_NONE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T("some_partner"),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, OmahaExperimentIdValid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("omahaexperiments=experiment%3DgroupA%7Cexpir");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_NONE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T("experiment=groupA|expir"),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, AppExperimentIdValid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("experiments=experiment%3DgroupA%7Cexpir");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T("experiment=groupA|expir"),
+      NEEDS_ADMIN_NO,
+      TRISTATE_NONE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, ReferralIdValid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("referral=ABCD123");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_NONE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T("ABCD123"),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, ApValid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("ap=developer");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_NONE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T("developer"),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, TTValid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("tttoken=7839g93");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_NONE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T("7839g93"),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, AppArgsValid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("tttoken=7839g93");
+
+  CString app_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                     _T("installerdata=%E0%A4foobar");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, app_args, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_NONE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T("7839g93"),
+      _T("%E0%A4foobar"),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, AppArgsInvalidAppGuid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("tttoken=7839g93");
+
+  CString app_args = _T("appguid={E135384F-85A2-4328-B07D-2CF70313D505}&")
+                     _T("installerdata=%E0%A4foobar");
+
+  EXPECT_EQ(E_INVALIDARG, parser.Parse(extra_args, app_args, &args));
+}
+
+TEST(ExtraArgsParserTest, AppArgsInvalidAttribute) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("tttoken=7839g93");
+
+  CString app_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                     _T("tttoken=foobar");
+
+  EXPECT_EQ(E_INVALIDARG, parser.Parse(extra_args, app_args, &args));
+}
+
+TEST(ExtraArgsParserTest, InstallerDataNotAllowedInExtraArgs) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("appname=TestApp2&")
+                       _T("needsadmin=true&")
+                       _T("installerdata=Hello%20World");
+
+  EXPECT_EQ(E_INVALIDARG, parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, InstallDataIndexValid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("installdataindex=foobar");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_NONE,
+      false,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T("foobar"));
+}
+
+TEST(ExtraArgsParserTest, BrowserTypeValid_0) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("browser=0");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyBrowserType(args,
+                    _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+                    BROWSER_UNKNOWN);
+}
+
+TEST(ExtraArgsParserTest, BrowserTypeValid_1) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("browser=1");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyBrowserType(args,
+                    _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+                    BROWSER_DEFAULT);
+}
+
+TEST(ExtraArgsParserTest, BrowserTypeValid_2) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("browser=2");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyBrowserType(args,
+                    _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+                    BROWSER_IE);
+}
+
+TEST(ExtraArgsParserTest, BrowserTypeValid_3) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("browser=3");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyBrowserType(args,
+                    _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+                    BROWSER_FIREFOX);
+}
+
+TEST(ExtraArgsParserTest, BrowserTypeValid_4) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("browser=4");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyBrowserType(args,
+                    _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+                    BROWSER_CHROME);
+}
+
+TEST(ExtraArgsParserTest, BrowserTypeInvalid) {
+  EXPECT_EQ(5, BROWSER_MAX) <<
+      _T("Browser type may have been added. Add new Valid_n test and change ")
+      _T("browser values in extra args strings below.");
+
+  CommandLineExtraArgs args1;
+  ExtraArgsParser parser1;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("browser=5");
+
+  EXPECT_SUCCEEDED(parser1.Parse(extra_args, NULL, &args1));
+  VerifyBrowserType(args1,
+                    _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+                    BROWSER_UNKNOWN);
+
+  CommandLineExtraArgs args2;
+  ExtraArgsParser parser2;
+  extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+               _T("browser=9");
+
+  EXPECT_SUCCEEDED(parser2.Parse(extra_args, NULL, &args2));
+  VerifyBrowserType(args2,
+                    _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
+                    BROWSER_UNKNOWN);
+}
+
+TEST(ExtraArgsParserTest, ValidLang) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("lang=en");
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  EXPECT_STREQ(_T("en"), args.language);
+}
+
+// Language must be passed even if not supported. See http://b/1336966.
+TEST(ExtraArgsParserTest, UnsupportedLang) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("lang=foobar");
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  EXPECT_STREQ(_T("foobar"), args.language);
+}
+
+TEST(ExtraArgsParserTest, LangTooLong) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("lang=morethan10chars");
+  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, RuntimeValid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("runtime=true");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{00000000-0000-0000-0000-000000000000}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_NONE,
+      true,
+      _T(""),
+      GUID_NULL,
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+TEST(ExtraArgsParserTest, RuntimeWithExtraArgs) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("runtime=true&")
+                       _T("bundlename=Google%20Bundle&")
+                       _T("brand=GOOG&")
+                       _T("usagestats=1");
+
+  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+  VerifyExtraArgsHaveSpecificValues(
+      args,
+      _T("{00000000-0000-0000-0000-000000000000}"),
+      _T(""),
+      _T(""),
+      NEEDS_ADMIN_NO,
+      TRISTATE_TRUE,
+      true,
+      _T("Google Bundle"),
+      GUID_NULL,
+      _T("GOOG"),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""),
+      _T(""));
+}
+
+
+TEST(ExtraArgsParserTest, RuntimeBeforeAppGuid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("runtime=true&")
+                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}");
+
+  EXPECT_EQ(E_INVALIDARG, parser.Parse(extra_args, NULL, &args));
+}
+
+TEST(ExtraArgsParserTest, RuntimeAfterAppGuid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("runtime=true");
+
+  EXPECT_EQ(E_INVALIDARG, parser.Parse(extra_args, NULL, &args));
+}
+
+//
+// Test multiple applications in the extra arguments
+//
+TEST(ExtraArgsParserTestMultipleEntries, TestNotStartingWithAppGuid) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appname=TestApp&")
+                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("appname=TestApp&")
+                       _T("appname=false&")
+                       _T("iid={98CEC468-9429-4984-AEDE-4F53C6A14869}&")
+                       _T("ap=test_ap&")
+                       _T("tttoken=foobar&")
+                       _T("usagestats=1&")
+                       _T("browser=2&");
+  EXPECT_HRESULT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
+}
+
+// This also tests that the last occurrence of a global extra arg is the one
+// that is saved.
+TEST(ExtraArgsParserTestMultipleEntries, ThreeApplications) {
+  CommandLineExtraArgs args;
+  ExtraArgsParser parser;
+  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                       _T("appname=TestApp&")
+                       _T("needsadmin=false&")
+                       _T("iid={98CEC468-9429-4984-AEDE-4F53C6A14869}&")
+                       _T("ap=test_ap&")
+                       _T("tttoken=foobar&")
+                       _T("usagestats=1&")
+                       _T("browser=2&")
+                       _T("brand=GOOG&")
+                       _T("client=_some_client&")
+                       _T("experiments=_experiment_a&")
+                       _T("referral=A123456789&")
+                       _T("appguid={5E46DE36-737D-4271-91C1-C062F9FE21D9}&")
+                       _T("appname=TestApp2&")
+                       _T("needsadmin=true&")
+                       _T("experiments=_experiment_b&")
+                       _T("iid={98CEC468-9429-4984-AEDE-4F53C6A14869}&")
+                       _T("ap=test_ap2&")
+                       _T("tttoken=foobar2&")
+                       _T("usagestats=0&")
+                       _T("browser=3&")
+                       _T("brand=g00g&")
+                       _T("client=_different_client&")
+                       _T("appguid={5F46DE36-737D-4271-91C1-C062F9FE21D9}&")
+                       _T("appname=TestApp3&")
+                       _T("needsadmin=prefers&");
+
+  CString app_args = _T("appguid={5F46DE36-737D-4271-91C1-C062F9FE21D9}&")
+                     _T("installerdata=installerdata_app3&")
+                     _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
+                     _T("installerdata=installerdata_app1");
+
+  EXPECT_HRESULT_SUCCEEDED(parser.Parse(extra_args, app_args, &args));
+
+  CommandLineAppArgs input1;
+  input1.app_guid = StringToGuid(_T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"));
+  input1.app_name = _T("TestApp");
+  input1.needs_admin = NEEDS_ADMIN_NO;
+  input1.ap = _T("test_ap");
+  input1.tt_token = _T("foobar");
+  input1.encoded_installer_data = _T("installerdata_app1");
+  input1.experiment_labels = _T("_experiment_a");
+
+  CommandLineAppArgs input2;
+  input2.app_guid = StringToGuid(_T("{5E46DE36-737D-4271-91C1-C062F9FE21D9}"));
+  input2.app_name = _T("TestApp2");
+  input2.needs_admin = NEEDS_ADMIN_YES;
+  input2.ap = _T("test_ap2");
+  input2.tt_token = _T("foobar2");
+  input2.experiment_labels = _T("_experiment_b");
+
+  CommandLineAppArgs input3;
+  input3.app_guid = StringToGuid(_T("{5F46DE36-737D-4271-91C1-C062F9FE21D9}"));
+  input3.app_name = _T("TestApp3");
+  input3.encoded_installer_data = _T("installerdata_app3");
+  input3.needs_admin = NEEDS_ADMIN_PREFERS;
+
+  CommandLineExtraArgs expected;
+  expected.apps.push_back(input1);
+  expected.apps.push_back(input2);
+  expected.apps.push_back(input3);
+  expected.bundle_name = _T("TestApp");
+  expected.installation_id = StringToGuid(
+      _T("{98CEC468-9429-4984-AEDE-4F53C6A14869}"));
+  expected.brand_code = _T("g00g");
+  expected.client_id = _T("_different_client");
+  expected.referral_id = _T("A123456789");
+  expected.browser_type = BROWSER_FIREFOX;
+  expected.usage_stats_enable = TRISTATE_FALSE;
+
+  VerifyCommandLineExtraArgs(expected, args);
+}
+
+}  // namespace omaha
diff --git a/common/extractor.cc b/common/extractor.cc
deleted file mode 100644
index a315ab5..0000000
--- a/common/extractor.cc
+++ /dev/null
@@ -1,257 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/extractor.h"
-
-#include <windows.h>
-#include <wintrust.h>
-#include <crtdbg.h>
-#pragma warning(push)
-// C4100: unreferenced formal parameter
-// C4310: cast truncates constant value
-// C4548: expression before comma has no effect
-#pragma warning(disable : 4100 4310 4548)
-#include "base/basictypes.h"
-#pragma warning(pop)
-
-namespace omaha {
-
-#define AFFILIATE_ID_MAGIC "Gact"
-
-TagExtractor::TagExtractor()
-    : file_handle_(INVALID_HANDLE_VALUE),
-      file_mapping_(NULL),
-      file_base_(NULL),
-      file_length_(0),
-      cert_length_(0),
-      cert_dir_base_(NULL) {
-}
-
-TagExtractor::~TagExtractor() {
-  CloseFile();
-}
-
-bool TagExtractor::OpenFile(const TCHAR* filename) {
-  CloseFile();
-  file_handle_ = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
-    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-  if (IsFileOpen()) {
-    file_mapping_ = CreateFileMapping(file_handle_, NULL, PAGE_READONLY,
-      0, 0, NULL);
-    if (file_mapping_ != NULL) {
-      file_base_ = MapViewOfFile(file_mapping_, FILE_MAP_READ, 0, 0, 0);
-      if (file_base_ != NULL) {
-        MEMORY_BASIC_INFORMATION info = {0};
-        if (::VirtualQuery(file_base_, &info, sizeof(info))) {
-          file_length_ = info.RegionSize;
-          return true;
-        }
-      }
-      CloseHandle(file_mapping_);
-    }
-    CloseFile();
-  }
-  return false;
-}
-
-bool TagExtractor::IsFileOpen() const {
-  return file_handle_ != INVALID_HANDLE_VALUE;
-}
-
-void TagExtractor::CloseFile() {
-  if (file_base_ != NULL) {
-    UnmapViewOfFile(file_base_);
-    file_base_ = NULL;
-  }
-  if (file_mapping_ != NULL) {
-    CloseHandle(file_mapping_);
-    file_mapping_ = NULL;
-  }
-  if (IsFileOpen()) {
-    CloseHandle(file_handle_);
-    file_handle_ = INVALID_HANDLE_VALUE;
-  }
-}
-
-bool TagExtractor::ExtractTag(const char* binary_file,
-                              size_t binary_file_length,
-                              char* tag_buffer,
-                              int* tag_buffer_len) {
-  file_length_ = binary_file_length;
-  return InternalExtractTag(binary_file, tag_buffer, tag_buffer_len);
-}
-
-bool TagExtractor::ExtractTag(char* tag_buffer, int* tag_buffer_len) {
-  if (tag_buffer_len == NULL) {
-    return false;
-  }
-  if (!IsFileOpen()) {
-    return false;
-  }
-
-  return InternalExtractTag(static_cast<char*>(file_base_),
-                            tag_buffer,
-                            tag_buffer_len);
-}
-
-bool TagExtractor::InternalReadCertificate(const char* file_buffer) {
-  if (!file_buffer) {
-    return false;
-  }
-
-  const void* certificate_directory_pointer =
-    GetCertificateDirectoryPointer(file_buffer);
-  if (NULL == certificate_directory_pointer) {
-    return false;
-  }
-  const void* asn1_signature_pointer =
-    GetASN1SignaturePointer(certificate_directory_pointer);
-  if (NULL == asn1_signature_pointer) {
-    return false;
-  }
-  DWORD asn1_signature_length =
-    GetASN1SignatureLength(asn1_signature_pointer);
-  if (0 == asn1_signature_length) {
-    return false;
-  }
-
-  cert_length_ = asn1_signature_length;
-  cert_dir_base_ = certificate_directory_pointer;
-
-  return true;
-}
-
-bool TagExtractor::InternalExtractTag(const char* file_buffer,
-                                      char* tag_buffer,
-                                      int* tag_buffer_len) {
-  if (!file_buffer) {
-    return false;
-  }
-
-  if (!InternalReadCertificate(file_buffer)) {
-    return false;
-  }
-
-  const char* read_base = static_cast<const char*>(cert_dir_base_) +
-                          cert_length_;
-  if (read_base >= file_buffer + file_length_) {
-    // The file is not tagged.
-    return false;
-  }
-
-  return ReadTag(read_base, tag_buffer, tag_buffer_len);
-}
-
-bool TagExtractor::ReadTag(const char* tag_pointer,
-                           char* tag_buffer,
-                           int* tag_buffer_len) const {
-  int mc = memcmp(tag_pointer,
-                  AFFILIATE_ID_MAGIC,
-                  arraysize(AFFILIATE_ID_MAGIC) - 1);
-  if (0 != mc) {
-    return false;
-  }
-  tag_pointer += arraysize(AFFILIATE_ID_MAGIC) - 1;
-
-  uint16 id_len = 0;
-  const unsigned char* id_len_serialized =
-    reinterpret_cast<const unsigned char*>(tag_pointer);
-  id_len = id_len_serialized[0] << 8;
-  // unsigned char and uint16 get promoted to int.
-  id_len = static_cast<uint16>(id_len + id_len_serialized[1]);
-
-  int buffer_size_required = id_len + 1;
-  if (tag_buffer == NULL) {
-    *tag_buffer_len = buffer_size_required;
-    return true;
-  }
-  if (*tag_buffer_len < buffer_size_required) {
-    return false;
-  }
-  tag_pointer += sizeof(id_len);
-  memcpy(tag_buffer, tag_pointer, id_len);
-  tag_buffer[id_len] = '\0';
-  return true;
-}
-
-const void* TagExtractor::GetCertificateDirectoryPointer(
-    const void* base) const {
-  const char* image_base = reinterpret_cast<const char*>(base);
-
-  // Is this a PEF?
-  const IMAGE_DOS_HEADER* dos_header =
-    reinterpret_cast<const IMAGE_DOS_HEADER *>(image_base);
-  if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
-    return NULL;
-  }
-
-  // Get PE header.
-  const IMAGE_NT_HEADERS* nt_headers = reinterpret_cast<const IMAGE_NT_HEADERS*>
-      (image_base + dos_header->e_lfanew);
-
-  // Again, is this a PEF? This code should get an F for not being endian-
-  // safe, but it gets an A for working in the real world.
-  if (nt_headers->Signature != IMAGE_NT_SIGNATURE) {
-    return NULL;
-  }
-
-  const IMAGE_DATA_DIRECTORY* idd =
-    reinterpret_cast<const IMAGE_DATA_DIRECTORY *>
-    (&nt_headers->
-    OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]);
-  if (idd->VirtualAddress != NULL) {
-    return image_base + idd->VirtualAddress;
-  }
-  return NULL;
-}
-
-const void* TagExtractor::GetASN1SignaturePointer(const void* base) const {
-  const WIN_CERTIFICATE* cert =
-    reinterpret_cast<const WIN_CERTIFICATE *>(base);
-  return cert->bCertificate;
-}
-
-int TagExtractor::GetASN1SignatureLength(const void* base) const {
-  const unsigned char* sig_base =
-    reinterpret_cast<const unsigned char*>(base);
-
-  // No, this isn't a full ASN.1 parser. We're just doing the very bare
-  // minimum to extract a length.
-  if (*sig_base++ == 0x30 && *sig_base++ == 0x82) {
-    int len = (*sig_base++ << 8);
-    len += *sig_base++;
-    // Windows pads the certificate directory to align at a 8-byte boundary.
-    // This piece of code it trying to replicate the logic that is used to
-    // calculate the padding to be added to the certificate. It returns
-    // the entire length of the certificate directory from the windows
-    // certificate directory start to the end of padding.
-    // The windows certificate directory has the following structure
-    // <WIN_CERTIFICATE><Certificate><Padding>.
-    // WIN_CERTIFICATE is the windows certificate directory structure.
-    // <Certificate> has the following format:
-    // <Magic(2 bytes)><Cert length(2 bytes)><Certificate Data>
-    // Note that the "Cert length" does not include the magic bytes or
-    // the length.
-    //
-    // Hence the total length of the certificate is:
-    // cert_length + "WIN_CERTIFICATE header size" + magic + length
-    // + padding = (cert length + 8 + 2 + 2 + 7) & (0-8)
-    return (len + 8 + 2 + 2 + 7) & 0xffffff8;
-  }
-  return 0;
-}
-
-}  // namespace omaha
-
diff --git a/common/extractor_unittest.cc b/common/extractor_unittest.cc
deleted file mode 100644
index 9b46089..0000000
--- a/common/extractor_unittest.cc
+++ /dev/null
@@ -1,246 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Unit test for the extractor and the ApplyTag class.
-//
-// TODO(omaha): eliminate the dependency on the hardcoded "GoogleUpdate.exe"
-// program name.
-
-#include <shlobj.h>
-#include "base/scoped_ptr.h"
-#include "omaha/common/apply_tag.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/extractor.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/utils.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-const TCHAR kFilePath[] = _T(".");
-const TCHAR kFileName[] = _T("GoogleUpdate.exe");
-const char kTagString[] = "1234567890abcdefg";
-const char kAppendTagString[] = "..AppendedStr";
-
-TEST(ExtractorTest, EmbedExtract) {
-  // Test the extractor.
-  TagExtractor extractor;
-  ASSERT_FALSE(extractor.IsFileOpen());
-
-  CString signed_exe_file;
-  signed_exe_file.Format(_T("%s\\%s\\%s"),
-                         app_util::GetCurrentModuleDirectory(),
-                         kFilePath, kFileName);
-  ASSERT_TRUE(extractor.OpenFile(signed_exe_file));
-
-  // No tag string in the original exe file.
-  int tag_buffer_size = 0;
-  ASSERT_FALSE(extractor.ExtractTag(NULL, &tag_buffer_size));
-  ASSERT_EQ(tag_buffer_size, 0);
-  extractor.CloseFile();
-
-  // Create a temp dir.
-  TCHAR temp_path[MAX_PATH] = {0};
-  *temp_path = 0;
-  ASSERT_NE(::GetTempPath(MAX_PATH, temp_path), 0);
-
-  // Embed the tag string.
-  CString tagged_file;
-  tagged_file.Format(_T("%s%s"), temp_path, kFileName);
-  omaha::ApplyTag tag;
-  ASSERT_HRESULT_SUCCEEDED(tag.Init(signed_exe_file,
-                                    kTagString,
-                                    strlen(kTagString),
-                                    tagged_file,
-                                    false));
-  ASSERT_SUCCEEDED(tag.EmbedTagString());
-  ON_SCOPE_EXIT(::DeleteFile, tagged_file);
-
-  // Extract the tag string.
-  tag_buffer_size = 0;
-  ASSERT_TRUE(extractor.OpenFile(tagged_file));
-  ASSERT_TRUE(extractor.ExtractTag(NULL, &tag_buffer_size));
-  ASSERT_EQ(tag_buffer_size, arraysize(kTagString));
-
-  char tag_buffer[arraysize(kTagString)] = {0};
-  ASSERT_TRUE(extractor.ExtractTag(tag_buffer, &tag_buffer_size));
-  ASSERT_EQ(tag_buffer_size, arraysize(kTagString));
-  ASSERT_EQ(memcmp(tag_buffer, kTagString, arraysize(kTagString)), 0);
-  extractor.CloseFile();
-}
-
-TEST(ExtractorTest, EmbedAppendExtract) {
-  // Test the extractor.
-  TagExtractor extractor;
-  ASSERT_FALSE(extractor.IsFileOpen());
-
-  CString signed_exe_file;
-  signed_exe_file.Format(_T("%s\\%s\\%s"),
-                         app_util::GetCurrentModuleDirectory(),
-                         kFilePath, kFileName);
-  ASSERT_TRUE(extractor.OpenFile(signed_exe_file));
-
-  // No tag string in the original exe file.
-  int tag_buffer_size = 0;
-  ASSERT_FALSE(extractor.ExtractTag(NULL, &tag_buffer_size));
-  ASSERT_GT(extractor.cert_length(), 0);
-  ASSERT_EQ(tag_buffer_size, 0);
-  extractor.CloseFile();
-
-  // Create a temp dir.
-  TCHAR temp_path[MAX_PATH] = {0};
-  *temp_path = 0;
-  ASSERT_NE(::GetTempPath(MAX_PATH, temp_path), 0);
-
-  // Embed the tag string.
-  CString tagged_file;
-  tagged_file.Format(_T("%s%d%s"), temp_path, 1, kFileName);
-  omaha::ApplyTag tag;
-  ASSERT_HRESULT_SUCCEEDED(tag.Init(signed_exe_file,
-                                    kTagString,
-                                    strlen(kTagString),
-                                    tagged_file,
-                                    false));
-  ASSERT_SUCCEEDED(tag.EmbedTagString());
-  ON_SCOPE_EXIT(::DeleteFile, tagged_file);
-
-  // Append another tag string.
-  CString tagged_appended_file;
-  tagged_appended_file.Format(_T("%s%d%s"), temp_path, 2, kFileName);
-  omaha::ApplyTag tag1;
-
-  ASSERT_HRESULT_SUCCEEDED(tag1.Init(tagged_file,
-                                     kAppendTagString,
-                                     strlen(kAppendTagString),
-                                     tagged_appended_file,
-                                     true));
-  ASSERT_SUCCEEDED(tag1.EmbedTagString());
-  ON_SCOPE_EXIT(::DeleteFile, tagged_appended_file);
-
-  // Append another tag string.
-  CString tagged_appended_file2;
-  tagged_appended_file2.Format(_T("%s%d%s"), temp_path, 3, kFileName);
-  omaha::ApplyTag tag2;
-  ASSERT_HRESULT_SUCCEEDED(tag2.Init(tagged_appended_file,
-                                     kAppendTagString,
-                                     strlen(kAppendTagString),
-                                     tagged_appended_file2,
-                                     true));
-  ASSERT_SUCCEEDED(tag2.EmbedTagString());
-  ON_SCOPE_EXIT(::DeleteFile, tagged_appended_file2);
-
-  // Extract the tag string.
-  tag_buffer_size = 0;
-  CStringA expected_tag_string(kTagString);
-  expected_tag_string += kAppendTagString;
-  expected_tag_string += kAppendTagString;
-  int expected_tag_string_len = expected_tag_string.GetLength() + 1;
-  ASSERT_TRUE(extractor.OpenFile(tagged_appended_file2));
-  ASSERT_TRUE(extractor.ExtractTag(NULL, &tag_buffer_size));
-  ASSERT_EQ(tag_buffer_size, expected_tag_string_len);
-
-  scoped_array<char> tag_buffer(new char[expected_tag_string_len]);
-  ASSERT_TRUE(extractor.ExtractTag(tag_buffer.get(), &tag_buffer_size));
-  ASSERT_EQ(tag_buffer_size, expected_tag_string_len);
-  ASSERT_EQ(memcmp(tag_buffer.get(),
-                   expected_tag_string,
-                   expected_tag_string_len),
-            0);
-  extractor.CloseFile();
-}
-
-TEST(ExtractorTest, AlreadyTaggedError) {
-  // Test the extractor.
-  TagExtractor extractor;
-  ASSERT_FALSE(extractor.IsFileOpen());
-
-  CString signed_exe_file;
-  signed_exe_file.Format(_T("%s\\%s\\%s"),
-                         app_util::GetCurrentModuleDirectory(),
-                         kFilePath, kFileName);
-  ASSERT_TRUE(extractor.OpenFile(signed_exe_file));
-
-  // No tag string in the original exe file.
-  int tag_buffer_size = 0;
-  ASSERT_FALSE(extractor.ExtractTag(NULL, &tag_buffer_size));
-  ASSERT_GT(extractor.cert_length(), 0);
-  ASSERT_EQ(tag_buffer_size, 0);
-  extractor.CloseFile();
-
-  // Create a temp dir.
-  TCHAR temp_path[MAX_PATH] = {0};
-  *temp_path = 0;
-  ASSERT_NE(::GetTempPath(MAX_PATH, temp_path), 0);
-
-  // Embed the tag string.
-  CString tagged_file;
-  tagged_file.Format(_T("%s%d%s"), temp_path, 1, kFileName);
-  omaha::ApplyTag tag1;
-  ASSERT_HRESULT_SUCCEEDED(tag1.Init(signed_exe_file,
-                                     kTagString,
-                                     strlen(kTagString),
-                                     tagged_file,
-                                     false));
-  ASSERT_SUCCEEDED(tag1.EmbedTagString());
-  ON_SCOPE_EXIT(::DeleteFile, tagged_file);
-
-  CString tagged_appended_file;
-  tagged_appended_file.Format(_T("%s%d%s"), temp_path, 2, kFileName);
-  omaha::ApplyTag tag2;
-  ASSERT_HRESULT_SUCCEEDED(tag2.Init(tagged_file,
-                                     kAppendTagString,
-                                     strlen(kAppendTagString),
-                                     tagged_appended_file,
-                                     false));
-  ASSERT_EQ(tag2.EmbedTagString(), APPLYTAG_E_ALREADY_TAGGED);
-  ON_SCOPE_EXIT(::DeleteFile, tagged_appended_file);
-  extractor.CloseFile();
-}
-
-TEST(ApplyTagTest, InvalidCharsTest) {
-  // Accepted Regex = [-%{}/\a&=._]*
-  CString signed_exe_file;
-  signed_exe_file.Format(_T("%s\\%s\\%s"),
-                         app_util::GetCurrentModuleDirectory(),
-                         kFilePath, kFileName);
-  CString tagged_file(_T("out.txt"));
-
-  const char* const input_str = "abcd";
-  omaha::ApplyTag tag1;
-  ASSERT_HRESULT_SUCCEEDED(tag1.Init(signed_exe_file,
-                                     input_str,
-                                     strlen(input_str),
-                                     tagged_file,
-                                     false));
-
-  const char* const input_str2 = "abcd$%#";
-  omaha::ApplyTag tag2;
-  ASSERT_HRESULT_FAILED(tag2.Init(signed_exe_file,
-                                  input_str2,
-                                  strlen(input_str2),
-                                  tagged_file,
-                                  false));
-
-  const char* const input_str3 = "abcd asdf";
-  omaha::ApplyTag tag3;
-  ASSERT_HRESULT_FAILED(tag3.Init(signed_exe_file,
-                                  input_str3,
-                                  strlen(input_str3),
-                                  tagged_file,
-                                  false));
-}
-
-}  // namespace omaha
-
diff --git a/common/file.cc b/common/file.cc
deleted file mode 100644
index b4effa3..0000000
--- a/common/file.cc
+++ /dev/null
@@ -1,1344 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-// File handling routines
-//
-// Possible performance improvement: make a subclass or alternate class
-// that reads an entire file into memory and fulfills read/write requests
-// in memory (or equivalently, use a memory mapped file). This can greatly
-// improve performance if there are files we do a lot of read/write requests on.
-//
-// We generally are dealing with files that can be very large and want to
-// minimize memory usage, so this is not high priority
-//
-// this has the beginnings of asynchronous access support
-//
-// Unfortunately, doing asynchronous reads with FILE_FLAG_OVERLAPPED buys us
-// nothing because the system cache manager enforces serial requests.
-//
-// Hence, we need to also use FILE_FLAG_NO_BUFFERING. this has a number of
-// constraints:
-// - file read/write position must be aligned on multiples of the disk sector
-//   size
-// - file read/write length must be a multiple of the sector size
-// - read/write buffer must be aligned on multiples of the disk sector size
-//
-// In particular, this means that we cannot write 8 bytes, for example, because
-// we have to write an entire sector.
-//
-// Currently, the implementation only supports enough to do some simple read
-// tests
-//
-// The general idea is code that wants to to a sequence of asynchronous actions
-// will look like the following, for an example of reading multiple event
-// records asynchronously:
-//
-// uint32 async_id = File::GetNextAsyncId()
-// while (!done) {
-//   for (everything_to_do, e.g., for each event to read) {
-//     call File::Read to read items needed;
-//     returns TR_E_FILE_ASYNC_PENDING if queued; or returns data if done
-//     process the item (e.g., event) if desired
-//   }
-//   call some routine to process pending completions; initiate delayed action
-// }
-// call some cleanup routine
-
-#include "omaha/common/file.h"
-#include <algorithm>
-#include "base/scoped_ptr.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/const_config.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/path.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/common/string.h"
-#include "omaha/common/system.h"
-#include "omaha/common/timer.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-// Constants
-const uint32 kZeroSize = 4096;  // Buffer size used for clearing data in a file.
-
-// The moves-pending-reboot is a MULTISZ registry key in the HKLM part of the
-// registry.
-static const TCHAR* kSessionManagerKey =
-    _T("HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager");
-static const TCHAR* kPendingFileRenameOps = _T("PendingFileRenameOperations");
-
-File::File()
-    : handle_(INVALID_HANDLE_VALUE), read_only_(false), sequence_id_(0) {
-}
-
-File::~File() {
-  if (handle_ != INVALID_HANDLE_VALUE) {
-    VERIFY1(SUCCEEDED(Close()));
-  }
-}
-
-// open for reading only if write == false, otherwise both reading and writing
-// allow asynchronous operations if async == true. Use this function when you
-// need exclusive access to the file.
-HRESULT File::Open(const TCHAR* file_name, bool write, bool async) {
-  return OpenShareMode(file_name, write, async, 0);
-}
-
-// Allows specifying a sharing mode such as FILE_SHARE_READ. Otherwise,
-// this is identical to File::Open().
-HRESULT File::OpenShareMode(const TCHAR* file_name,
-                            bool write,
-                            bool async,
-                            DWORD share_mode) {
-  ASSERT1(file_name && *file_name);
-  ASSERT1(handle_ == INVALID_HANDLE_VALUE);
-  VERIFY1(!async);
-
-  file_name_ = file_name;
-
-  // there are restrictions on what we can do if using FILE_FLAG_NO_BUFFERING
-  // if (!buffer) { flags |= FILE_FLAG_NO_BUFFERING; }
-  // FILE_FLAG_WRITE_THROUGH
-  // how efficient is NTFS encryption? FILE_ATTRIBUTE_ENCRYPTED
-  // FILE_ATTRIBUTE_TEMPORARY
-  // FILE_FLAG_RANDOM_ACCESS
-  // FILE_FLAG_SEQUENTIAL_SCAN
-
-  handle_ = ::CreateFile(file_name,
-                         write ? (FILE_WRITE_DATA       |
-                                  FILE_WRITE_ATTRIBUTES |
-                                  FILE_READ_DATA) : FILE_READ_DATA,
-                         share_mode,
-                         NULL,
-                         write ? OPEN_ALWAYS : OPEN_EXISTING,
-                         FILE_FLAG_RANDOM_ACCESS,
-                         NULL);
-
-  if (handle_ == INVALID_HANDLE_VALUE) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR,
-            (_T("[File::OpenShareMode - CreateFile failed][%s][%d][%d][0x%x]"),
-             file_name, write, async, hr));
-    return hr;
-  }
-
-  // This attribute is not supported directly by the CreateFile function.
-  if (!::SetFileAttributes(file_name, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR,
-            (_T("[File::OpenShareMode - SetFileAttributes failed][0x%x]"), hr));
-    return hr;
-  }
-
-  read_only_ = !write;
-  pos_ = 0;
-  return S_OK;
-}
-
-// The path must not be enclosed in quotes. This is the Windows standard.
-// ::GetFileAttributesEx() returns ERROR_INVALID_NAME for quoted paths.
-bool File::Exists(const TCHAR* file_name) {
-  ASSERT1(file_name && *file_name);
-  ASSERT1(lstrlen(file_name) > 0);
-
-  // NOTE: This is the fastest implementation I found.  The results were:
-  //   CreateFile           1783739 avg ticks/call
-  //   FindFirstFile         634148 avg ticks/call
-  //   GetFileAttributes     428714 avg ticks/call
-  //   GetFileAttributesEx   396324 avg ticks/call
-  WIN32_FILE_ATTRIBUTE_DATA attrs = {0};
-  return 0 != ::GetFileAttributesEx(file_name, ::GetFileExInfoStandard, &attrs);
-}
-
-bool File::IsDirectory(const TCHAR* file_name) {
-  ASSERT1(file_name && *file_name);
-
-  WIN32_FILE_ATTRIBUTE_DATA attrs;
-  SetZero(attrs);
-  if (!::GetFileAttributesEx(file_name, ::GetFileExInfoStandard, &attrs)) {
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[File::IsDirectory - GetFileAttributesEx failed][%s][0x%x]"),
-              file_name, HRESULTFromLastError()));
-    return false;
-  }
-
-  return (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
-}
-
-HRESULT File::GetWildcards(const TCHAR* dir,
-                           const TCHAR* wildcard,
-                           std::vector<CString>* matching_paths) {
-  ASSERT1(dir && *dir);
-  ASSERT1(wildcard && *wildcard);
-  ASSERT1(matching_paths);
-
-  matching_paths->clear();
-
-  // Make sure directory name ends with "\"
-  CString directory = String_MakeEndWith(dir, _T("\\"), false);
-
-  WIN32_FIND_DATA find_data;
-  SetZero(find_data);
-  scoped_hfind hfind(::FindFirstFile(directory + wildcard, &find_data));
-  if (!hfind) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(L5, (_T("[File::GetWildcards - FindFirstFile failed][0x%x]"), hr));
-    return hr;
-  }
-  do {
-    if (find_data.dwFileAttributes == FILE_ATTRIBUTE_NORMAL ||
-        !(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
-      CString to_file(directory + find_data.cFileName);
-      matching_paths->push_back(to_file);
-    }
-  } while (::FindNextFile(get(hfind), &find_data));
-
-  HRESULT hr = HRESULTFromLastError();
-  if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) {
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[File::GetWildcards - FindNextFile failed][0x%x]"), hr));
-    return hr;
-  }
-  return S_OK;
-}
-
-// returns error if cannot remove
-// returns success if removed or already removed
-HRESULT File::Remove(const TCHAR* file_name) {
-  ASSERT1(file_name && *file_name);
-
-  if (!Exists(file_name)) {
-    return S_OK;
-  }
-
-  if (!::DeleteFile(file_name)) {
-    return HRESULTFromLastError();
-  }
-
-  return S_OK;
-}
-
-HRESULT File::CopyWildcards(const TCHAR* from_dir,
-                            const TCHAR* to_dir,
-                            const TCHAR* wildcard,
-                            bool replace_existing_files) {
-  ASSERT1(from_dir && *from_dir);
-  ASSERT1(to_dir && *to_dir);
-  ASSERT1(wildcard && *wildcard);
-
-  // Make sure dir names end with a "\"
-  CString from_directory = String_MakeEndWith(from_dir, _T("\\"), false);
-  CString to_directory = String_MakeEndWith(to_dir, _T("\\"), false);
-
-  // Get full path to source files (which is a wildcard)
-  CString from_files(from_directory + wildcard);
-
-  // Run over all files that match wildcard
-  WIN32_FIND_DATA find_data;
-  SetZero(find_data);
-
-  scoped_hfind hfind(::FindFirstFile(from_files, &find_data));
-  if (!hfind) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[File::CopyWildcards - FindFirstFile failed][0x%x]"), hr));
-    return hr;
-  }
-  do {
-    // Copy files
-    if (find_data.dwFileAttributes == FILE_ATTRIBUTE_NORMAL ||
-        !(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
-      CString from_file(from_directory + find_data.cFileName);
-      CString to_file(to_directory + find_data.cFileName);
-
-      if (!replace_existing_files && Exists(to_file)) {
-        // Continue, since the caller has explicitly asked us to not replace an
-        // existing file
-        continue;
-      }
-
-      RET_IF_FAILED(Copy(from_file, to_file, replace_existing_files));
-    }
-  } while (::FindNextFile(get(hfind), &find_data));
-
-  HRESULT hr = HRESULTFromLastError();
-  if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) {
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[File::CopyWildcards - FindNextFile failed][0x%x]"), hr));
-    return hr;
-  }
-  return S_OK;
-}
-
-HRESULT File::CopyTree(const TCHAR* from_dir,
-                       const TCHAR* to_dir,
-                       bool replace_existing_files) {
-  ASSERT1(from_dir && *from_dir);
-  ASSERT1(to_dir && *to_dir);
-
-  UTIL_LOG(L3, (L"[File::CopyTree][from_dir %s][to_dir %s][replace %d]",
-                from_dir, to_dir, replace_existing_files));
-
-  // Make sure dir names end with a "\"
-  CString from_directory(String_MakeEndWith(from_dir, L"\\", false));
-  CString to_directory(String_MakeEndWith(to_dir, L"\\", false));
-
-  RET_IF_FAILED(CreateDir(to_directory, NULL));
-  RET_IF_FAILED(CopyWildcards(from_directory,
-                              to_directory,
-                              L"*.*",
-                              replace_existing_files));
-
-  // Run over all directories
-  WIN32_FIND_DATA find_data;
-  SetZero(find_data);
-
-  CString from_files(from_directory);
-  from_files += _T("*.*");
-
-  scoped_hfind hfind(::FindFirstFile(from_files, &find_data));
-  if (!hfind) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[File::CopyTree - FindFirstFile failed][0x%x]"), hr));
-    return hr;
-  }
-  do {
-    // Copy files
-    if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 &&
-        String_StrNCmp(find_data.cFileName, L"..", 2, false) &&
-        String_StrNCmp(find_data.cFileName, L".", 2, false)) {
-      CString from_subdir(from_directory + find_data.cFileName);
-      CString to_subdir(to_directory + find_data.cFileName);
-      RET_IF_FAILED(CopyTree(from_subdir, to_subdir, replace_existing_files));
-    }
-  } while (::FindNextFile(get(hfind), &find_data));
-
-  HRESULT hr = HRESULTFromLastError();
-  if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_FILES)) {
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[File::CopyTree - FindNextFile failed][0x%x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT File::Copy(const TCHAR* from,
-                   const TCHAR* to,
-                   bool replace_existing_file) {
-  ASSERT1(from && *from);
-  ASSERT1(to && *to);
-
-  if (!replace_existing_file && Exists(to)) {
-    // Return success, since the caller has explicitly asked us to not replace
-    // an existing file
-    return S_OK;
-  }
-
-  if (!::CopyFile(from, to, !replace_existing_file)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[File::Copy - CopyFile failed]")
-                           _T("[from=%s][to=%s][replace=%u][0x%x]"),
-                           from, to, replace_existing_file, hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-// TODO(omaha): Combine common code in Move/MoveAfterReboot
-HRESULT File::Move(const TCHAR* from,
-                   const TCHAR* to,
-                   bool replace_existing_file) {
-  ASSERT1(from && *from);
-  ASSERT1(to && *to);
-
-  DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH;
-  if (replace_existing_file) {
-    flags |= MOVEFILE_REPLACE_EXISTING;
-  }
-
-  if (!::MoveFileEx(from, to, flags)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[File::Move - MoveFileEx failed]")
-              _T("[from=%s][to=%s][replace=%u][0x%x]"),
-              from, to, replace_existing_file, hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-// DeleteAfterReboot tries to delete the files by either moving them to the TEMP
-// directory and deleting them on reboot, or if that fails, by trying to delete
-// them in-place on reboot
-HRESULT File::DeleteAfterReboot(const TCHAR* from) {
-  ASSERT1(from && *from);
-
-  if (File::Exists(from)) {
-    HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
-    CString from_temp;
-
-    // No point in moving into TEMP if we're already there
-    if (!String_StartsWith(from, app_util::GetTempDir(), true)) {
-      // Try to move to the TEMP directory first
-      CString temp_dir(String_MakeEndWith(app_util::GetTempDir(),
-                                          _T("\\"),
-                                          false));
-      // Of the form "C:\\Windows\\Temp\\FROM.EXE1f4c0b7f"
-      from_temp.Format(_T("%s%s%x"),
-                       temp_dir,
-                       GetFileFromPath(from),
-                       ::GetTickCount());
-
-      hr = File::Move(from, from_temp, true);
-      UTIL_LOG(L2, (_T("[File::DeleteAfterReboot - move %s to %s][0x%x]"),
-                    from, from_temp, hr));
-    }
-
-    if (SUCCEEDED(hr)) {
-      UTIL_LOG(L2, (_T("[File::DeleteAfterReboot - delete %s after reboot]"),
-                    from_temp));
-      // Move temp file after reboot
-      if (FAILED(hr = File::MoveAfterReboot(from_temp, NULL))) {
-        UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]")
-                               _T("[failed to delete after reboot %s][0x%x]"),
-                               from_temp, hr));
-      }
-    } else  {
-      // Move original file after reboot
-      if (FAILED(hr = File::MoveAfterReboot(from, NULL))) {
-        UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]")
-                               _T("[failed to delete after reboot %s][0x%x]"),
-                               from, hr));
-      }
-    }
-
-    return hr;
-  }
-
-  return S_OK;
-}
-
-
-HRESULT File::MoveAfterReboot(const TCHAR* from, const TCHAR* to) {
-  ASSERT1(from && *from);
-
-  if (!File::Exists(from)) {
-    // File/directory doesn't exist, should this return failure or success?
-    // Decision:  Failure.  Because the caller can decide if it is really
-    // failure or not in his specific case.
-    UTIL_LOG(LEVEL_WARNING, (_T("[File::MoveAfterReboot]")
-                             _T("[file doesn't exist][from %s]"), from));
-    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
-  }
-
-  DWORD flags = MOVEFILE_DELAY_UNTIL_REBOOT;
-  if (!File::IsDirectory(from)) {
-    // This flag valid only for files
-    flags |= MOVEFILE_REPLACE_EXISTING;
-  }
-
-  if (!::MoveFileEx(from, to, flags)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[File::MoveAfterReboot]")
-                           _T("[failed to MoveFileEx from '%s' to '%s'][0x%x]"),
-                           from, to, hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-// See if we have any moves pending a reboot. Return SUCCESS if we do
-// not encounter errors (not finding a move is not an error). We need to
-// also check the value of *found_ptr for whether we actually found a move.
-// On return, *value_multisz_ptr is the value within
-// "PendingFileRenameOperations", but with any moves for in_directory removed
-// from it.
-// The prefix_match boolean controls whether we do an exact match on
-// in_directory, or remove all entries with the in_directory prefix.
-// NOTE: If the only values found were our own keys, the whole
-// PendingFileRenameOperations MULTISZ needs to be deleted.
-// This is signified by a returned *value_size_chars_ptr of 0.
-HRESULT File::GetPendingRenamesValueMinusDir(const TCHAR* in_directory,
-                                             bool prefix_match,
-                                             TCHAR** value_multisz_ptr,
-                                             DWORD* value_size_chars_ptr,
-                                             bool* found_ptr) {
-  ASSERT1(in_directory && *in_directory);
-
-  // Convert to references for easier-to-read-code:
-  TCHAR*& value_multisz = *value_multisz_ptr;
-  DWORD& value_size_chars = *value_size_chars_ptr;
-  bool& found = *found_ptr;
-
-  // Initialize [out] parameters
-  value_multisz = NULL;
-  value_size_chars = 0;
-  found = false;
-
-  // Locals mirroring the [out] parameters.
-  // We will only set the corresponding [out] parameters when we have something
-  // meaningful to return to the caller
-  scoped_array<TCHAR> value_multisz_local;
-  DWORD value_size_chars_local = 0;
-
-  DWORD value_size_bytes = 0;
-  // Get the current value of the key
-  // If the Key is missing, that's totally acceptable.
-  RET_IF_FALSE(
-      RegKey::HasValue(kSessionManagerKey, kPendingFileRenameOps) &&
-      SUCCEEDED(RegKey::GetValue(kSessionManagerKey,
-                                 kPendingFileRenameOps,
-                                 reinterpret_cast<byte**>(&value_multisz_local),
-                                 &value_size_bytes)),
-      S_OK);
-
-  ASSERT1(value_multisz_local.get() || value_size_bytes == 0);
-  UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]")
-                _T("[read multisz %d bytes]"),
-                value_size_bytes));
-  RET_IF_FALSE(value_size_bytes > 0, S_OK);
-
-  // The size should always be aligned to a TCHAR boundary, otherwise the key
-  // is corrupted.
-  ASSERT1((value_size_bytes % sizeof(TCHAR)) == 0);
-  RET_IF_FALSE((value_size_bytes % sizeof(TCHAR)) == 0,
-               HRESULT_FROM_WIN32(ERROR_BADKEY));
-  // Valid size, so convert to TCHARs:
-  value_size_chars_local = value_size_bytes / sizeof(TCHAR);
-
-  // Buffer must terminate with two nulls
-  ASSERT(value_size_chars_local >= 2 &&
-         !value_multisz_local[value_size_chars_local - 1] &&
-         !value_multisz_local[value_size_chars_local - 2],
-         (_T("buffer must terminate with two nulls")));
-  RET_IF_FALSE(value_size_chars_local >= 2 &&
-               !value_multisz_local[value_size_chars_local - 1] &&
-               !value_multisz_local[value_size_chars_local - 2],
-               HRESULT_FROM_WIN32(ERROR_BADKEY));
-  // Mark the end of the string.
-  // multisz_end will point at the character past end of buffer:
-  TCHAR* multisz_end = value_multisz_local.get() + value_size_chars_local;
-
-  // We're looking for \??\C:\...  The \??\ was
-  // added by the OS to the directory name we specified.
-  CString from_dir(_T("\\??\\"));
-  from_dir += in_directory;
-  DWORD from_dir_len = from_dir.GetLength();
-
-  // A MULTISZ is a list of null terminated strings, terminated by a double
-  // null.  We keep two pointers marching along the string in parallel.
-  TCHAR* str_read = value_multisz_local.get();
-  TCHAR* str_write = str_read;
-
-  while ((str_read < multisz_end) && *str_read) {
-    size_t str_len = ::lstrlen(str_read);
-  // A FALSE here indicates a corrupt PendingFileRenameOperations
-    RET_IF_FALSE((str_read + str_len + 1) < multisz_end,
-      HRESULT_FROM_WIN32(ERROR_BADKEY));
-    if (0 == String_StrNCmp(str_read,
-                            from_dir,
-                            from_dir_len + (prefix_match ? 0 : 1),
-                            true)) {
-      // String matches, we want to remove this string, so advance only the
-      // read pointer - past this string and the replacement string.
-      UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]")
-                    _T("[skips past match '%s']"),
-                    str_read));
-      str_read += str_len + 1;
-      str_read += ::lstrlen(str_read) + 1;
-      continue;
-    }
-    // String doesn't match, we want to keep it.
-    if (str_read != str_write) {
-      // Here we're not in sync in the buffer, we've got to move two
-      // strings down.
-      UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]")
-                    _T("[copying some other deletion][%s][%s]"),
-                    str_read, str_read + ::lstrlen(str_read) + 1));
-      ASSERT1(str_write < str_read);
-      String_StrNCpy(str_write, str_read, str_len+1);
-      str_read += str_len + 1;
-      str_write += str_len + 1;
-      str_len = ::lstrlen(str_read);
-      String_StrNCpy(str_write, str_read, str_len+1);
-      str_read += str_len + 1;
-      str_write += str_len + 1;
-    } else {
-      // We're in sync in the buffer, advance both pointers past two strings
-      UTIL_LOG(L5, (_T("[File::GetPendingRenamesValueMinusDir]")
-                    _T("[skipping past some other deletion][%s][%s]"),
-                    str_read, str_read + ::lstrlen(str_read) + 1));
-      str_read += str_len + 1;
-      str_read += ::lstrlen(str_read) + 1;
-      str_write = str_read;
-    }
-  }
-
-  // A FALSE here indicates a corrupt PendingFileRenameOperations
-  RET_IF_FALSE(str_read < multisz_end,
-    HRESULT_FROM_WIN32(ERROR_BADKEY));
-
-  if (str_read != str_write) {
-    // We found some values
-    found = true;
-
-    if (str_write == value_multisz_local.get()) {
-      // The only values were our own keys,
-      // and the whole PendingFileRenameOperations
-      // value needs to be deleted. We do not populate
-      // value_size_chars or value_multisz in this case.
-      ASSERT1(!value_size_chars);
-      ASSERT1(!value_multisz);
-    } else  {
-      // The last string should have a NULL terminator:
-      ASSERT1(str_write[-1] == '\0');
-      RET_IF_FALSE(str_write[-1] == '\0',
-        HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
-      // a REG_MULTI_SZ needs to be terminated with an extra NULL.
-      *str_write = '\0';
-      ++str_write;
-
-      // Populate value_size_chars and value_multisz in this case.
-      value_multisz = value_multisz_local.release();
-      value_size_chars = str_write - value_multisz;
-    }
-  }
-
-  return S_OK;
-}
-
-// Remove any moves pending a reboot from the PendingFileRenameOperations
-// in the registry.
-// The prefix_match boolean controls whether we do an exact match on
-// in_directory, or remove all entries with the in_directory prefix.
-HRESULT File::RemoveFromMovesPendingReboot(const TCHAR* in_directory,
-                                           bool prefix_match) {
-  ASSERT1(in_directory && *in_directory);
-
-  bool found = false;
-  // scoped_array will free the value_multisz buffer on stack unwind:
-  scoped_array<TCHAR> value_multisz;
-  DWORD value_size_chars = 0;
-  HRESULT hr = GetPendingRenamesValueMinusDir(in_directory,
-                                              prefix_match,
-                                              address(value_multisz),
-                                              &value_size_chars,
-                                              &found);
-  if (SUCCEEDED(hr) && found) {
-    if (value_multisz.get() == NULL)  {
-      // There's no point in writing an empty value_multisz.
-      // Let's delete the PendingFileRenameOperations value
-      UTIL_LOG(L5, (_T("[File::RemoveFromMovesPendingReboot]")
-                    _T("[deleting PendingFileRenameOperations value]")));
-      RET_IF_FAILED(RegKey::DeleteValue(kSessionManagerKey,
-                    kPendingFileRenameOps));
-    } else  {
-      // Let's write the modified value_multisz into the
-      // PendingFileRenameOperations value
-      UTIL_LOG(L5, (_T("[File::RemoveFromMovesPendingReboot]")
-                    _T("[rewriting multisz %d bytes]"),
-                    value_size_chars * sizeof(TCHAR)));
-      RET_IF_FAILED(RegKey::SetValueMultiSZ(
-          kSessionManagerKey,
-          kPendingFileRenameOps,
-          reinterpret_cast<byte*>(value_multisz.get()),
-          value_size_chars * sizeof(TCHAR)));
-    }
-  }
-
-  // Failure of GetPendingRenamesValueMinusDir() may indicate something
-  // seriously wrong with the system. Propogate error.
-  return hr;
-}
-
-// Did the user try to uninstall a previous install of the same version, and
-// we couldn't clean up without a reboot?
-// We check if there are any moves pending a reboot from the
-// PendingFileRenameOperations in the registry.
-// The prefix_match boolean controls whether we do an exact match on
-// in_directory, or check all entries with the in_directory prefix.
-bool File::AreMovesPendingReboot(const TCHAR* in_directory, bool prefix_match) {
-  ASSERT1(in_directory && *in_directory);
-
-  bool found = false;
-  // scoped_array will free the value_multisz buffer on stack unwind:
-  scoped_array<TCHAR> value_multisz;
-  DWORD value_size_chars = 0;
-
-  if (SUCCEEDED(GetPendingRenamesValueMinusDir(in_directory,
-                                               prefix_match,
-                                               address(value_multisz),
-                                               &value_size_chars,
-                                               &found)) && found) {
-    return true;
-  }
-
-  return false;
-}
-
-HRESULT File::GetFileTime(const TCHAR* file_name,
-                          FILETIME* created,
-                          FILETIME* accessed,
-                          FILETIME* modified) {
-  ASSERT1(file_name && *file_name);
-
-  bool is_dir = IsDirectory(file_name);
-  // To obtain a handle to a directory, call the CreateFile function with
-  // the FILE_FLAG_BACKUP_SEMANTICS flag
-  scoped_hfile file_handle(
-      ::CreateFile(file_name,
-                   FILE_READ_DATA,
-                   FILE_SHARE_READ,
-                   NULL,
-                   OPEN_EXISTING,
-                   is_dir ? FILE_FLAG_BACKUP_SEMANTICS : NULL,
-                   NULL));
-  HRESULT hr = S_OK;
-
-  if (!file_handle) {
-    hr = HRESULTFromLastError();
-    UTIL_LOG(LE, (_T("[File::GetFileTime]")
-                  _T("[failed to open file][%s][0x%x]"), file_name, hr));
-  } else {
-    if (!::GetFileTime(get(file_handle), created, accessed, modified)) {
-      hr = HRESULTFromLastError();
-      UTIL_LOG(LEVEL_ERROR, (_T("[File::GetFileTime]")
-                             _T("[failed to get file time][%s][0x%x]"),
-                             file_name, hr));
-    }
-  }
-
-  return hr;
-}
-
-HRESULT File::SetFileTime(const TCHAR* file_name,
-                          const FILETIME* created,
-                          const FILETIME* accessed,
-                          const FILETIME* modified) {
-  ASSERT1(file_name && *file_name);
-
-  bool is_dir = IsDirectory(file_name);
-  // To obtain a handle to a directory, call the CreateFile function with
-  // the FILE_FLAG_BACKUP_SEMANTICS flag
-  scoped_hfile file_handle(
-      ::CreateFile(file_name,
-                   FILE_WRITE_ATTRIBUTES,
-                   FILE_SHARE_WRITE,
-                   NULL,
-                   OPEN_EXISTING,
-                   is_dir ? FILE_FLAG_BACKUP_SEMANTICS : NULL,
-                   NULL));
-  HRESULT hr = S_OK;
-
-  if (!file_handle) {
-    hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[File::GetFileTime]")
-                           _T("[failed to open file][%s][0x%x]"),
-                           file_name, hr));
-  } else {
-    BOOL res = ::SetFileTime(get(file_handle), created, accessed, modified);
-    if (!res) {
-      hr = HRESULTFromLastError();
-      UTIL_LOG(LEVEL_ERROR, (_T("[File::SetFileTime]")
-                             _T("[failed to set file time][%s][0x%x]"),
-                             file_name, hr));
-    }
-  }
-
-  return hr;
-}
-
-HRESULT File::Sync() {
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-
-  if (!::FlushFileBuffers(handle_)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[File::Sync]")
-                           _T("[FlushFileBuffers failed][%s][0x%x]"),
-                           file_name_, hr));
-    return hr;
-  }
-  return S_OK;
-}
-
-HRESULT File::SeekToBegin() {
-  return SeekFromBegin(0);
-}
-
-HRESULT File::SeekFromBegin(uint32 n) {
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-
-  if (::SetFilePointer(handle_, n, NULL, FILE_BEGIN) ==
-      INVALID_SET_FILE_POINTER) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[File::SeekFromBegin]")
-                           _T("[SetFilePointer failed][%s][0x%x]"),
-                           file_name_, hr));
-    return hr;
-  }
-  pos_ = n;
-  return S_OK;
-}
-
-// read nLen bytes starting at position n
-// returns number of bytes read
-//
-// async operations:
-//
-// async_id - identifier of a sequence of async operations, 0 for synchronous
-//
-// if the async operation has not been initiated, we initiate it
-// if it is in progress we do nothing
-// if it has been completed we return the data
-// does not delete async data entry
-HRESULT File::ReadAt(const uint32 offset, byte* buf, const uint32 len,
-                     const uint32, uint32* bytes_read) {
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-  ASSERT1(buf);
-  ASSERT1(len);  // reading 0 bytes is not valid (differs from CRT API)
-
-  RET_IF_FAILED(SeekFromBegin(offset));
-
-  DWORD read = 0;
-  if (!::ReadFile(handle_, buf, len, &read, NULL)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[File::ReadAt]")
-                           _T("[ReadFile failed][%s][0x%x]"), file_name_, hr));
-    return hr;
-  }
-
-  if (bytes_read) {
-    *bytes_read = read;
-  }
-
-  return (read == len) ? S_OK : E_FAIL;
-}
-
-
-// reads up to max_len bytes from the start of the file
-// not considered an error if there are less than max_len bytes read
-// returns number of bytes read
-HRESULT File::ReadFromStartOfFile(const uint32 max_len,
-                                  byte* buf,
-                                  uint32* bytes_read) {
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-  ASSERT1(buf);
-  ASSERT1(max_len);
-
-  RET_IF_FAILED(SeekFromBegin(0));
-
-  uint32 file_len = 0;
-  RET_IF_FAILED(GetLength(&file_len));
-
-  if (!file_len) {
-    if (bytes_read) {
-      *bytes_read = 0;
-    }
-    return S_OK;
-  }
-
-  uint32 len = max_len;
-  if (len > file_len) {
-    len = static_cast<uint32>(file_len);
-  }
-
-  return Read(len, buf, bytes_read);
-}
-
-// this function handles lines terminated with LF or CRLF
-// all CR characters are removed from each line, and LF is assumed
-// to be the end of line and is removed
-HRESULT File::ReadLineAnsi(uint32 max_len, char* line, uint32* len) {
-  ASSERT1(line);
-  ASSERT1(max_len);
-
-  char c = 0;
-  uint32 len_read = 0;
-  uint32 total_len = 0;
-
-  while (SUCCEEDED(Read(1, reinterpret_cast<byte *>(&c), &len_read)) &&
-         len_read  &&
-         c != '\n') {
-    if (total_len < max_len - 1 && c != '\r') {
-      line[total_len++] = c;
-    }
-  }
-
-  ASSERT1(total_len < max_len);
-  line[total_len] = '\0';
-
-  if (len) {
-    *len = total_len;
-  }
-
-  return (len_read || total_len) ? S_OK : E_FAIL;
-}
-
-// used by ReadFromStartOfFile and ReadLineAnsi; not reading all requested bytes
-// is not considered fatal
-HRESULT File::Read(const uint32 len, byte* buf, uint32* bytes_read) {
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-  ASSERT1(buf);
-  ASSERT1(len);
-
-  DWORD read = 0;
-  if (!::ReadFile(handle_, buf, len, &read, NULL)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[File::ReadAt]")
-                           _T("[ReadFile failed][%s][0x%x]"),
-                           file_name_, hr));
-    return hr;
-  }
-
-  if (bytes_read) {
-    *bytes_read = read;
-  }
-
-  return S_OK;
-}
-
-
-// returns number of bytes written
-HRESULT File::WriteAt(const uint32 offset,
-                      const byte* buf,
-                      const uint32 len,
-                      uint32,
-                      uint32* bytes_written) {
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-  ASSERT1(!read_only_);
-  ASSERT1(buf);
-  ASSERT1(len);
-
-  RET_IF_FAILED(SeekFromBegin(offset));
-
-  return Write(buf, len, bytes_written);
-}
-
-
-// write buffer n times
-HRESULT File::WriteN(const byte* buf,
-                     const uint32 len,
-                     const uint32 n,
-                     uint32* bytes_written) {
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-  ASSERT1(!read_only_);
-  ASSERT1(buf);
-  ASSERT1(len);
-  ASSERT1(n);
-
-  HRESULT hr = S_OK;
-
-  uint32 total_wrote = 0;
-
-  byte* temp_buf = const_cast<byte*>(buf);
-
-  scoped_array<byte> encrypt_buf;
-
-  uint32 to_go = n;
-  while (to_go) {
-    uint32 wrote = 0;
-    hr = Write(temp_buf, len, &wrote);
-    if (FAILED(hr)) {
-      if (bytes_written) {
-        *bytes_written = total_wrote;
-      }
-      return hr;
-    }
-
-    total_wrote += wrote;
-    to_go--;
-  }
-
-  if (bytes_written) {
-    *bytes_written = total_wrote;
-  }
-  return hr;
-}
-
-// returns number of bytes written
-HRESULT File::Write(const byte* buf, const uint32 len, uint32* bytes_written) {
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-  ASSERT1(!read_only_);
-  ASSERT1(buf);
-  ASSERT1(len);  // writing 0 bytes is not valid (differs from CRT API)
-
-  byte* b = const_cast<byte*>(buf);
-
-  scoped_array<byte> encrypt_buf;
-
-  DWORD wrote = 0;
-  if (!::WriteFile(handle_, b, len, &wrote, NULL)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[File::Write]")
-                           _T("[WriteFile failed][%s][0x%x]"),
-                           file_name_, hr));
-    return hr;
-  }
-
-  if (bytes_written) {
-    *bytes_written = wrote;
-  }
-  pos_ += wrote;
-
-  return (wrote == len) ? S_OK : E_FAIL;
-}
-
-HRESULT File::ClearAt(const uint32 offset,
-                      const uint32 len,
-                      uint32* bytes_written) {
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-  ASSERT1(!read_only_);
-  ASSERT1(len);
-
-  byte zero[kZeroSize] = {0};
-  uint32 to_go = len;
-  uint32 written = 0;
-  uint32 pos = offset;
-
-  while (to_go) {
-    uint32 wrote = 0;
-    uint32 write_len = std::min(to_go, kZeroSize);
-    RET_IF_FAILED(WriteAt(pos, zero, write_len, 0, &wrote));
-
-    if (wrote != write_len) {
-      return E_FAIL;
-    }
-    pos += wrote;
-    written += wrote;
-    to_go -= write_len;
-  }
-
-  if (bytes_written) {
-    *bytes_written = written;
-  }
-  return S_OK;
-}
-
-// returns true on failure
-// zeros new data if zero_data == true
-HRESULT File::SetLength(const uint32 n, bool zero_data) {
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-  ASSERT1(!read_only_);
-  ASSERT1(n <= kMaxFileSize);
-
-  HRESULT hr = S_OK;
-
-  uint32 len = 0;
-  VERIFY1(SUCCEEDED(GetLength(&len)));
-
-  if (len == n) {
-    return S_OK;
-  }
-
-  // according to the documentation, the
-  // new space will not be initialized
-  if (n > len) {
-    if (zero_data) {
-      uint32 bytes_written = 0;
-      RET_IF_FAILED(ClearAt(len, n - len, &bytes_written));
-      if (bytes_written != n - len) {
-        return E_FAIL;
-      }
-    } else {
-      byte zero = 0;
-      uint32 bytes_written = 0;
-      RET_IF_FAILED(WriteAt(n - 1, &zero, 1, 0, &bytes_written));
-      if (bytes_written != 1) {
-        return E_FAIL;
-      }
-    }
-  } else {
-    SeekFromBegin(n);
-    SetEndOfFile(handle_);
-  }
-
-  ASSERT1(SUCCEEDED(GetLength(&len)) && len == n);
-
-  return S_OK;
-}
-
-HRESULT File::ExtendInBlocks(const uint32 block_size, uint32 size_needed,
-                             uint32* new_size, bool clear_new_space) {
-  ASSERT1(new_size);
-
-  *new_size = size_needed;
-
-  if (*new_size % block_size) {
-    *new_size += block_size - (*new_size % block_size);
-  }
-
-  // is zero_data needed? may reduce fragmentation by causing the block to
-  // be written
-  return SetLength(*new_size, clear_new_space);
-}
-
-// returns S_OK on success
-HRESULT File::GetLength(uint32* length) {
-  ASSERT1(length);
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-
-  DWORD len = GetFileSize(handle_, NULL);
-  if (len == INVALID_FILE_SIZE) {
-    ASSERT(false, (_T("cannot get file length")));
-    return E_FAIL;
-  }
-  *length = len;
-  return S_OK;
-}
-
-HRESULT File::Touch() {
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-
-  FILETIME file_time;
-  SetZero(file_time);
-
-  ::GetSystemTimeAsFileTime(&file_time);
-
-  if (!::SetFileTime(handle_, NULL, NULL, &file_time)) {
-    return HRESULTFromLastError();
-  }
-  return S_OK;
-}
-
-HRESULT File::Close() {
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-
-  HRESULT hr = S_OK;
-  if (!::CloseHandle(handle_)) {
-    hr = HRESULTFromLastError();
-  }
-
-  handle_ = INVALID_HANDLE_VALUE;
-
-  return hr;
-}
-
-// this is just for consistency with other classes; does not do anything
-HRESULT File::Reload(uint32* number_errors) {
-  ASSERT1(number_errors);
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-
-  *number_errors = 0;
-  return S_OK;
-}
-
-// this is just for consistency with other classes; does not do anything
-HRESULT File::Verify(uint32* number_errors) {
-  ASSERT1(number_errors);
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-
-  *number_errors = 0;
-  return S_OK;
-}
-
-// this is just for consistency with other classes; does not do anything
-HRESULT File::Dump() {
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-  return S_OK;
-}
-
-// for consistency with other classes
-HRESULT File::GetSizeOnDisk(uint64* size_on_disk) {
-  ASSERT1(size_on_disk);
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-
-  uint32 len = 0;
-  RET_IF_FAILED(GetLength(&len));
-
-  *size_on_disk = len;
-  return S_OK;
-}
-
-// for consistency with other classes
-HRESULT File::GetReloadDiskSpaceNeeded(uint64* bytes_needed) {
-  ASSERT1(bytes_needed);
-  ASSERT1(handle_ != INVALID_HANDLE_VALUE);
-
-  uint32 len = 0;
-  RET_IF_FAILED(GetLength(&len));
-
-  *bytes_needed = len;
-  return S_OK;
-}
-
-// Get the file size
-HRESULT File::GetFileSizeUnopen(const TCHAR* filename, uint32* out_size) {
-  ASSERT1(filename);
-  ASSERT1(out_size);
-
-  WIN32_FILE_ATTRIBUTE_DATA data;
-  SetZero(data);
-
-  if (!::GetFileAttributesEx(filename, ::GetFileExInfoStandard, &data)) {
-    return HRESULTFromLastError();
-  }
-
-  *out_size = data.nFileSizeLow;
-
-  return S_OK;
-}
-
-// Get the last time with a file was written to, and the size
-HRESULT File::GetLastWriteTimeAndSize(const TCHAR* file_path,
-                                      SYSTEMTIME* out_time,
-                                      unsigned int* out_size) {
-  ASSERT1(file_path);
-
-  WIN32_FIND_DATA wfd;
-  SetZero(wfd);
-
-  HANDLE find = ::FindFirstFile(file_path, &wfd);
-  if (find == INVALID_HANDLE_VALUE) {
-    return HRESULTFromLastError();
-  }
-
-  ::FindClose(find);
-
-  if (out_size) {
-    *out_size = wfd.nFileSizeLow;
-  }
-
-  if (out_time) {
-    // If created time is newer than write time, then use that instead
-    // [it tends to be more relevant when copying files around]
-    FILETIME* latest_time = NULL;
-    if (::CompareFileTime(&wfd.ftCreationTime, &wfd.ftLastWriteTime) > 0) {
-      latest_time = &wfd.ftCreationTime;
-    } else {
-      latest_time = &wfd.ftLastWriteTime;
-    }
-
-    if (!::FileTimeToSystemTime(latest_time, out_time)) {
-      return HRESULTFromLastError();
-    }
-  }
-
-  return S_OK;
-}
-
-FileLock::FileLock() {
-}
-
-FileLock::~FileLock() {
-  Unlock();
-}
-
-HRESULT FileLock::Lock(const TCHAR* file) {
-  std::vector<CString> files;
-  files.push_back(file);
-  return Lock(files);
-}
-
-HRESULT FileLock::Lock(const std::vector<CString>& files) {
-  ASSERT1(!files.empty());
-
-  // Try to lock all files
-  size_t curr_size = handles_.size();
-  for (size_t i = 0; i < files.size(); ++i) {
-    scoped_hfile handle(::CreateFile(files[i],
-                                     GENERIC_READ,
-                                     FILE_SHARE_READ,
-                                     NULL,
-                                     OPEN_EXISTING,
-                                     FILE_ATTRIBUTE_NORMAL,
-                                     NULL));
-    if (!handle) {
-      UTIL_LOG(LEVEL_ERROR,
-               (_T("[FileLock::Lock - failed to lock file][%s][0x%x]"),
-                files[i], HRESULTFromLastError()));
-      break;
-    }
-    handles_.push_back(release(handle));
-  }
-
-  // Cleanup if we fail to lock all the files
-  if (curr_size +  files.size() < handles_.size()) {
-    for (size_t i = handles_.size() - 1; i >= curr_size; --i) {
-      VERIFY(::CloseHandle(handles_[i]), (_T("")));
-      handles_.pop_back();
-    }
-    return E_FAIL;
-  }
-
-  return S_OK;
-}
-
-HRESULT FileLock::Unlock() {
-  for (size_t i = 0; i < handles_.size(); ++i) {
-    VERIFY(::CloseHandle(handles_[i]), (_T("")));
-  }
-  handles_.clear();
-  return S_OK;
-}
-
-
-// path_name: the directory to watch
-// watch_subtree: watch all subdirectory changes  or
-//                only immediate child values
-// notify_filter: See the documentation for FindFirstChangeNotification
-FileWatcher::FileWatcher(const TCHAR* path_name, bool watch_subtree,
-                         DWORD notify_filter)
-    : path_name_(path_name),
-      watch_subtree_(watch_subtree),
-      notify_filter_(notify_filter) {
-  ASSERT1(path_name && *path_name);
-  UTIL_LOG(L3, (_T("[FileWatcher::FileWatcher][%s]"), path_name));
-}
-
-// Get the event that is signaled on store changes.
-HANDLE FileWatcher::change_event() const {
-  ASSERT(valid(change_event_), (_T("call FileWatcher::SetupEvent first")));
-  return get(change_event_);
-}
-
-
-// Called to create/reset the event that gets signaled
-// any time the store changes.  Access the created
-// event using change_event().
-HRESULT FileWatcher::EnsureEventSetup() {
-  UTIL_LOG(L3, (_T("[FileWatcher::EnsureEventSetup]")));
-  if (!valid(change_event_)) {
-    reset(change_event_, ::FindFirstChangeNotification(path_name_,
-                                                       watch_subtree_,
-                                                       notify_filter_));
-    if (!valid(change_event_)) {
-      ASSERT(false, (_T("unable to get file change notification")));
-      return E_FAIL;
-    }
-    // path name was only needed to set-up the event and now that is done....
-    path_name_.Empty();
-    return S_OK;
-  }
-
-  // if the event is set-up and no changes have occurred,
-  // then there is no need to re-setup the event.
-  if (valid(change_event_) && !HasChangeOccurred()) {
-    return NOERROR;
-  }
-
-  return ::FindNextChangeNotification(get(change_event_)) ? S_OK : E_FAIL;
-}
-
-}  // namespace omaha
-
diff --git a/common/file.h b/common/file.h
deleted file mode 100644
index 060b63c..0000000
--- a/common/file.h
+++ /dev/null
@@ -1,251 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-// File handling routines
-//
-// encryption is not currently active
-//
-#ifndef OMAHA_COMMON_FILE_H__
-#define OMAHA_COMMON_FILE_H__
-
-#include <windows.h>
-#include <vector>
-#include "base/basictypes.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/store_watcher.h"
-
-namespace omaha {
-
-#define kMinimumDiskSpaceRequired 100000000
-// if we hit the minimum at some point, then this is the amount of space we
-// require before restarting indexing (at which point we'll lower the minimum
-// back to the usual level)
-#define kMinimumDiskSpaceRequiredToRestartIndexing \
-        (kMinimumDiskSpaceRequired + 100000000)
-#define kMaxFileSize kInt32Max
-
-class File {
- public:
-
-    File();
-    ~File();
-
-    HRESULT Open(const TCHAR* file_name, bool write, bool async);
-    HRESULT OpenShareMode(const TCHAR* file_name,
-                          bool write,
-                          bool async,
-                          DWORD share_mode);
-
-    HRESULT Close();
-
-    static bool Exists(const TCHAR* file_name);
-    static bool IsDirectory(const TCHAR *file_name);
-    static HRESULT GetWildcards(const TCHAR* dir, const TCHAR* wildcard,
-                                std::vector<CString>* matching_paths);
-    // returns S_OK on successful removal or if not existing
-    static HRESULT Remove(const TCHAR* file_name);
-    // CopyWildcards doesn't work recursively
-    static HRESULT CopyWildcards(const TCHAR* from_dir, const TCHAR* to_dir,
-                                 const TCHAR* wildcard,
-                                 bool replace_existing_files);
-    static HRESULT CopyTree(const TCHAR* from_dir, const TCHAR* to_dir,
-                            bool replace_existing_files);
-                                                    // to_dir need not exist
-    static HRESULT Copy(const TCHAR* from, const TCHAR* to,
-                        bool replace_existing_file);
-    static HRESULT Move(const TCHAR* from, const TCHAR* to,
-                        bool replace_existing_file);
-    // DeleteAfterReboot tries to delete the files by either moving them to
-    // the TEMP directory and deleting them on reboot, or if that fails, by
-    // trying to delete them in-place on reboot
-    static HRESULT DeleteAfterReboot(const TCHAR* from);
-    static HRESULT MoveAfterReboot(const TCHAR* from, const TCHAR* to);
-    // Remove any moves pending a reboot from the PendingFileRenameOperations
-    // in the registry.
-    // The prefix_match boolean controls whether we do an exact match on
-    // in_directory, or remove all entries with the in_directory prefix.
-    static HRESULT RemoveFromMovesPendingReboot(const TCHAR* in_directory,
-                                                bool prefix_match);
-    // Did the user try to uninstall a previous install of the same version,
-    // and we couldn't clean up without a reboot?
-    // We check if there are any moves pending a reboot from the
-    // PendingFileRenameOperations in the registry.
-    // The prefix_match boolean controls whether we do an exact match on
-    // in_directory, or check all entries with the in_directory prefix.
-    static bool AreMovesPendingReboot(const TCHAR* in_directory,
-                                      bool prefix_match);
-
-    // The GetFileTime function retrieves the date and time that a file was
-    // created, last accessed, and last modified. The parameters 'created',
-    // 'accessed', 'modified' can be null if the caller does not require that
-    // information. All times are utc
-    // (http://support.microsoft.com/default.aspx?scid=kb;%5BLN%5D;158588)
-    // To compare FILETIME values, use CompareFileTime API.
-    static HRESULT GetFileTime(const TCHAR* file_name, FILETIME* created,
-                               FILETIME* accessed, FILETIME* modified);
-
-    // Sets the file time
-    static HRESULT SetFileTime(const TCHAR* file_name,
-                               const FILETIME* created,
-                               const FILETIME* accessed,
-                               const FILETIME* modified);
-
-    // sync flushes any pending writes to disk
-    HRESULT Sync();
-    // static HRESULT SyncAllFiles();
-
-    HRESULT SeekToBegin();
-    HRESULT SeekFromBegin(uint32 n);
-
-    HRESULT ReadFromStartOfFile(const uint32 max_len, byte *buf,
-                                uint32 *bytes_read);
-    HRESULT ReadLineAnsi(uint32 max_len, char *line, uint32 *len);
-
-    // read len bytes, reading 0 bytes is invalid
-    HRESULT Read(const uint32 len, byte *buf, uint32 *bytes_read);
-    // read len bytes starting at position n, reading 0 bytes is invalid
-    HRESULT ReadAt(const uint32 offset, byte *buf, const uint32 len,
-                    const uint32 async_id, uint32 *bytes_read);
-
-    // write len bytes, writing 0 bytes is invalid
-    HRESULT Write(const byte *buf, const uint32 len, uint32 *bytes_written);
-    // write len bytes, writing 0 bytes is invalid
-    HRESULT WriteAt(const uint32 offset, const byte *buf, const uint32 len,
-                     const uint32 async_id, uint32 *bytes_written);
-
-    // write buffer n times
-    HRESULT WriteN(const byte *buf, const uint32 len, const uint32 n,
-                    uint32 *bytes_written);
-
-    // zeros section of file
-    HRESULT ClearAt(const uint32 offset, const uint32 len,
-                     uint32 *bytes_written);
-
-    // set length of file
-    // if new length is greater than current length, new data is undefined
-    // unless zero_data == true in which case the new data is zeroed.
-    HRESULT SetLength(const uint32 n, bool zero_data);
-    HRESULT ExtendInBlocks(const uint32 block_size, uint32 size_needed,
-                            uint32 *new_size, bool clear_new_space);
-    HRESULT GetLength(uint32 *len);
-
-    // Sets the last write time to the current time
-    HRESULT Touch();
-
-    // all the data storage classes contain these functions
-    // we implemenent them here for consistency
-    // e.g., so we can do object->GetSizeOnDisk independent of the object type
-    HRESULT GetSizeOnDisk(uint64 *size_on_disk);
-    HRESULT GetReloadDiskSpaceNeeded(uint64 *bytes_needed);
-    HRESULT Reload(uint32 *number_errors);
-    HRESULT Verify(uint32 *number_errors);
-    HRESULT Dump();
-
-    // Gets the size of a file, without opening it [the regular GetFileSize
-    // requires a file handle, which conflicts if the file is already opened
-    // and locked]
-    static HRESULT GetFileSizeUnopen(const TCHAR * filename,
-                                     uint32 * out_size);
-
-    // Optimized function that gets the last write time and size
-    static HRESULT GetLastWriteTimeAndSize(const TCHAR* file_path,
-                                           SYSTEMTIME* out_time,
-                                           unsigned int* out_size);
-
- private:
-    // See if we have any moves pending a reboot. Return SUCCESS if we do
-    // not encounter errors (not finding a move is not an error). We need to
-    // also check the value of *found_ptr for whether we actually found a move.
-    // On return, *value_multisz_ptr is the value within
-    // "PendingFileRenameOperations", but with any moves for in_directory
-    // removed from it.
-    // The prefix_match boolean controls whether we do an exact match on
-    // in_directory, or remove all entries with the in_directory prefix.
-    // NOTE: If the only values found were our own keys, the whole
-    // PendingFileRenameOperations MULTISZ needs to be deleted. This is
-    // signified by a returned *value_size_chars_ptr of 0.
-    static HRESULT GetPendingRenamesValueMinusDir(const TCHAR* in_directory,
-      bool prefix_match, TCHAR** value_multisz_ptr, DWORD* value_size_chars_ptr,
-      bool* found_ptr);
-
-    HANDLE handle_;
-    CString file_name_;
-    bool read_only_;
-    bool sync_write_done_;
-    uint32 pos_;
-    uint32 encryption_seed_;
-    uint32 sequence_id_;
-    enum EncryptionTypes encryption_;
-
-    DISALLOW_EVIL_CONSTRUCTORS(File);
-};
-
-// File lock
-class FileLock {
- public:
-  // Default constructor
-  FileLock();
-
-  // Destructor
-  ~FileLock();
-
-  // Lock a single file
-  HRESULT Lock(const TCHAR* file);
-
-  // Lock multiple files (atomic)
-  HRESULT Lock(const std::vector<CString>& files);
-
-  // Unlock all
-  HRESULT Unlock();
-
- private:
-  std::vector<HANDLE> handles_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(FileLock);
-};
-
-
-// Does the common things necessary for watching
-// changes in a directory.  If there are file change or other watchers,
-// there could be a common interface for the three methods to decouple
-// the code that is doing the watching from the code that owns the store.
-class FileWatcher : public StoreWatcher {
- public:
-  // path_name: the directory to watch
-  // watch_subtree: watch all subdirectory changes  or
-  //                only immediate child values
-  // notify_filter: See the documentation for FindFirstChangeNotification
-  FileWatcher(const TCHAR* path_name, bool watch_subtree, DWORD notify_filter);
-
-  // Called to create/reset the event that gets signaled
-  // any time the store changes.  Access the created
-  // event using change_event().
-  virtual HRESULT EnsureEventSetup();
-
-  // Get the event that is signaled on store changes.
-  virtual HANDLE change_event() const;
-
- private:
-  scoped_hfind_change_notification change_event_;
-  CString path_name_;
-  bool watch_subtree_;
-  DWORD notify_filter_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(FileWatcher);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_FILE_H__
-
diff --git a/common/file_reader.cc b/common/file_reader.cc
deleted file mode 100644
index 4f3304c..0000000
--- a/common/file_reader.cc
+++ /dev/null
@@ -1,248 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/file_reader.h"
-#include "omaha/common/debug.h"
-
-namespace omaha {
-
-FileReader::FileReader()
-    : file_is_open_(false),
-      buffered_byte_count_(0),
-      current_position_(0),
-      file_buffer_size_(0),
-      is_unicode_(false) {}
-
-FileReader::~FileReader() {
-  if (file_is_open_) {
-    file_.Close();
-    file_is_open_ = false;
-  }
-}
-
-HRESULT FileReader::Init(const TCHAR* file_name, size_t buffer_size) {
-  ASSERT1(file_name);
-  ASSERT1(buffer_size);
-  file_buffer_size_ = buffer_size;
-  file_buffer_.reset(new byte[file_buffer_size()]);
-  HRESULT hr = file_.OpenShareMode(file_name, false, false, FILE_SHARE_WRITE |
-                                                            FILE_SHARE_READ);
-  file_is_open_ = SUCCEEDED(hr);
-  is_unicode_ = false;
-
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = file_.SeekToBegin();
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  const int unicode_header_length = 2;
-
-  char buf[unicode_header_length] = {0};
-  uint32 bytes_read = 0;
-  hr = file_.Read(sizeof(buf), reinterpret_cast<byte*>(buf), &bytes_read);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  if (bytes_read == sizeof(buf)) {
-    char unicode_buf[unicode_header_length] = {0xff, 0xfe};
-    is_unicode_ = (memcmp(buf, unicode_buf, sizeof(buf)) == 0);
-  }
-
-  if (!is_unicode_) {
-    file_.SeekToBegin();
-  }
-
-  if (is_unicode_ && (buffer_size < sizeof(WCHAR))) {
-    return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
-  }
-
-  return S_OK;
-}
-
-HRESULT FileReader::GetNextChar(bool peek, CString* next_char) {
-  ASSERT1(next_char);
-  next_char->Empty();
-
-  // Do we need to read in more of the file?
-  if (current_position_ >= buffered_byte_count_) {
-    current_position_ = 0;
-    if (FAILED(file_.Read(file_buffer_size(),
-                          file_buffer_.get(),
-                          &buffered_byte_count_))) {
-      // There is no more of the file buffered.
-      buffered_byte_count_ = 0;
-    }
-  }
-
-  // Have we gone past the end of the file?
-  if (current_position_ >= buffered_byte_count_) {
-    return E_FAIL;
-  }
-
-  if (is_unicode_) {
-    // Need to make sure there are at least 2 characters still in the buffer.
-    // If we're right at the end of the buffer and there's only one character,
-    // then we will need to read more from the file.
-
-    if (current_position_ + 1 >= buffered_byte_count_) {
-      // We need one more byte to make a WCHAR.
-      // Due to the need to peek, we're going to take that byte and put it at
-      // the beginning of the file_buffer_ and read in as many remaining bytes
-      // as we can from the file.
-
-      // Copy current (and last) byte to the beginning of the buffer.
-      file_buffer_[0] = file_buffer_[current_position_];
-
-      // Reset current_position.
-      current_position_ = 0;
-
-      if (SUCCEEDED(file_.Read(file_buffer_size() - 1,
-                               file_buffer_.get() + 1,
-                               &buffered_byte_count_))) {
-        // Incrememt count to deal with byte we pre-filled at offset 0.
-        buffered_byte_count_++;
-      } else {
-        // We've got a Unicode file with an extra byte.  We're going to drop the
-        // byte and call it end of file.
-        buffered_byte_count_ = 0;
-        return E_FAIL;
-      }
-    }
-
-    // Get the next character.
-    char c1 = file_buffer_[current_position_];
-    ++current_position_;
-    char c2 = file_buffer_[current_position_];
-    ++current_position_;
-
-    if (peek) {
-      // Reset the current position pointer backwards if we're peeking.
-      current_position_ -= 2;
-    }
-
-    WCHAR c = (static_cast<WCHAR>(c2) << 8) | static_cast<WCHAR>(c1);
-
-    *next_char = c;
-  } else {
-    char c = file_buffer_[current_position_];
-    if (!peek) {
-      ++current_position_;
-    }
-    *next_char = c;
-  }
-
-  return S_OK;
-}
-
-HRESULT FileReader::ReadLineString(CString* line) {
-  ASSERT1(line);
-
-  line->Empty();
-
-  while (true) {
-    CString current_char;
-    HRESULT hr = GetNextChar(false, &current_char);
-    // If we failed to get the next char, we're at the end of the file.
-    // If the current line is empty, then fail out signalling we're done.
-    // Otherwise, return the current line and we'll fail out on the next call to
-    // ReadLine().
-    if (FAILED(hr)) {
-      if (line->IsEmpty()) {
-        return hr;
-      } else {
-        return S_OK;
-      }
-    }
-
-    // Have we reached end of line?
-    if (current_char.Compare(_T("\r")) == 0) {
-      // Seek ahead to see if the next char is "\n"
-      CString next_char;
-      GetNextChar(true, &next_char);
-      if (next_char.Compare(_T("\n")) == 0) {
-        // Get in the next char too.
-        GetNextChar(false, &next_char);
-      }
-      break;
-    } else if (current_char.Compare(_T("\n")) == 0) {
-      break;
-    }
-
-    line->Append(current_char);
-  }
-
-  return S_OK;
-}
-
-HRESULT FileReader::ReadLineAnsi(size_t max_len, char* line) {
-  ASSERT1(line);
-  ASSERT1(max_len);
-
-  size_t total_len = 0;
-
-  while (true) {
-    // Do we need to read in more of the file?
-    if (current_position_ >= buffered_byte_count_) {
-      current_position_ = 0;
-      if (FAILED(file_.Read(file_buffer_size(),
-                            file_buffer_.get(),
-                            &buffered_byte_count_))) {
-        // There is no more of the file buffered.
-        buffered_byte_count_ = 0;
-      }
-    }
-
-    // Have we gone past the end of the file?
-    if (current_position_ >= buffered_byte_count_) {
-      break;
-    }
-
-    // Get the next character.
-    char c = file_buffer_[current_position_];
-    ++current_position_;
-
-    // Have we reached end of line?
-    // TODO(omaha): if the line is terminated with a \r\n pair then perhaps
-    // the code should skip the whole pair not only half of it.
-    if (c == '\n' || c == '\r') {
-      break;
-    }
-
-    // Fill up the passed in buffer for the line.
-    if (total_len < max_len - 1) {
-      line[total_len] = c;
-      ++total_len;
-    }
-  }
-  // Terminate the passed in buffer.
-  ASSERT1(total_len < max_len);
-  line[total_len] = '\0';
-
-  // If we are out of bytes and we didn't read in any bytes.
-  // then fail signaling end of file.
-  if (!buffered_byte_count_ && !total_len) {
-    return E_FAIL;
-  }
-
-  return S_OK;
-}
-
-}  // namespace omaha
-
diff --git a/common/file_reader.h b/common/file_reader.h
deleted file mode 100644
index c97c3e9..0000000
--- a/common/file_reader.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_COMMON_FILE_READER_H_
-#define OMAHA_COMMON_FILE_READER_H_
-
-#include <windows.h>
-#include <tchar.h>
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "omaha/common/file.h"
-
-namespace omaha {
-
-// Allows one to read files quickly and easily line by line.
-class FileReader {
- public:
-  FileReader();
-  ~FileReader();
-
-  // Specifies the underlying file name and the size of internal read buffer.
-  HRESULT Init(const TCHAR* file_name, size_t buffer_size);
-
-  // Reads one line of text from the file, taking advantage of the internal
-  // file buffer to optimize I/O reads. It reads at most max_len - 1 characters
-  // and it always terminates the line.
-  HRESULT ReadLineAnsi(size_t max_len, char* line);
-  HRESULT ReadLineString(CString* line);
-
- private:
-  HRESULT GetNextChar(bool peek, CString* next_char);
-  size_t file_buffer_size() const { return file_buffer_size_; }
-
-  File file_;
-  bool file_is_open_;
-  size_t buffered_byte_count_;          // How many bytes are in the buffer.
-  size_t current_position_;             // An index into the buffer.
-  scoped_array<byte> file_buffer_;      // A buffer (cache) of the file.
-  size_t file_buffer_size_;             // How much of the file to slurp
-                                        // in on each read.
-  bool is_unicode_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(FileReader);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_FILE_READER_H_
diff --git a/common/file_reader_unittest.cc b/common/file_reader_unittest.cc
deleted file mode 100644
index af62b00..0000000
--- a/common/file_reader_unittest.cc
+++ /dev/null
@@ -1,304 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// TODO(omaha): improve unit test. For example, test we are handling correctly
-// different types of line termination.
-
-#include <vector>
-#include "base/scoped_ptr.h"
-#include "omaha/common/file_reader.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-class ReadingFilesTest : public testing::Test {
- protected:
-  ReadingFilesTest() {
-    temp_file_[0] = '\0';
-  }
-
-  virtual void SetUp() {
-    // create a temporary file
-    TCHAR temp_path[MAX_PATH] = {0};
-    EXPECT_LT(::GetTempPath(arraysize(temp_path), temp_path),
-              arraysize(temp_path));
-    EXPECT_NE(::GetTempFileName(temp_path, _T("ut_"), 0, temp_file_), 0);
-  }
-
-  virtual void TearDown() {
-    // remove the temporary file
-    if (::lstrlen(temp_file_) > 0) {
-      ASSERT_SUCCEEDED(File::Remove(temp_file_));
-    }
-  }
-
-  void Compare(const std::vector<char*>& lines1,
-               const std::vector<char*>& lines2,
-               byte** orig_lines, int* lengths, int* copies) {
-    // verify that all three things match
-    //  * The lines read using the File class,
-    //  * The lines read using the FileReader class,
-    //  * The original data used to write the files
-    EXPECT_EQ(lines1.size(), lines2.size());
-
-    for (uint32 line_number = 0; line_number < lines1.size(); line_number++) {
-      int index = 0;
-      int data_length = lengths[line_number];
-      int data_copies = copies[line_number];
-      for (int copy = 0; copy < data_copies; copy++) {
-        for (int index_data = 0; index_data < data_length; index_data++) {
-          EXPECT_EQ(lines1[line_number][index], lines2[line_number][index]);
-          EXPECT_EQ(lines1[line_number][index],
-                    orig_lines[line_number][index_data]);
-          index++;
-        }
-      }
-      EXPECT_EQ(lines1[line_number][index], lines2[line_number][index]);
-      EXPECT_EQ(lines1[line_number][index], '\0');
-    }
-  }
-
-  void DeleteContainedArrays(std::vector<char*>* lines) {
-    for (uint32 line_number = 0; line_number < lines->size(); line_number++) {
-      delete[] (*lines)[line_number];
-      (*lines)[line_number] = NULL;
-    }
-  }
-
-  HRESULT WriteCStringW(File* file, CStringW* str) {
-    return file->Write(reinterpret_cast<const byte*>(str->GetBuffer()),
-                       str->GetLength() * sizeof(WCHAR),
-                       NULL);
-  }
-
-  void TestReadFileStringUnicode(int buffer_size) {
-    CStringW line1 = L"hello there, here's some data";
-    CStringW line2 = L"i've got more\tdata over here.";
-    CStringW eol1 = L"\r";
-    CStringW eol2 = L"\r\n";
-    CStringW eol3 = L"\n";
-
-    std::vector<CString> expected_lines;
-    expected_lines.push_back(CString(line1));
-    expected_lines.push_back(CString(line2));
-    expected_lines.push_back(CString(line1));
-    expected_lines.push_back(CString(line1));
-    expected_lines.push_back(CString(line2));
-
-    File file_write;
-    EXPECT_SUCCEEDED(file_write.Open(temp_file_, true, false));
-
-    // Write the unicode marker to the beginning of the file.
-    byte buf[2] = {0xff, 0xfe};
-    EXPECT_SUCCEEDED(file_write.Write(buf, sizeof(buf), NULL));
-
-    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &line1));
-    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &eol1));
-    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &line2));
-    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &eol1));
-    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &line1));
-    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &eol3));
-    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &line1));
-    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &eol2));
-    EXPECT_SUCCEEDED(WriteCStringW(&file_write, &line2));
-    file_write.Close();
-
-    FileReader reader;
-    EXPECT_SUCCEEDED(reader.Init(temp_file_, buffer_size));
-
-    std::vector<CString> read_lines;
-    CString current_string;
-    while (SUCCEEDED(reader.ReadLineString(&current_string))) {
-      read_lines.push_back(current_string);
-    }
-
-    ASSERT_EQ(expected_lines.size(), read_lines.size());
-
-    if (expected_lines.size() == read_lines.size()) {
-      for (size_t i = 0; i < expected_lines.size(); ++i) {
-        CString expected_str = expected_lines[i];
-        CString read_str = read_lines[i];
-        ASSERT_STREQ(expected_str, read_str);
-      }
-    }
-  }
-
-  TCHAR temp_file_[MAX_PATH];
-
-  DISALLOW_EVIL_CONSTRUCTORS(ReadingFilesTest);
-};
-
-
-TEST_F(ReadingFilesTest, ReadFile1) {
-  // write the data to the file
-  File file_write;
-  EXPECT_SUCCEEDED(file_write.Open(temp_file_, true, false));
-  byte data1[] = {'a', 'b', 'c', 'z', '!', '&', '\t'};
-  int data1_copies = 1;
-  EXPECT_SUCCEEDED(file_write.WriteN(data1, arraysize(data1), data1_copies,
-                                     NULL));
-  byte return_data = '\n';
-  EXPECT_SUCCEEDED(file_write.WriteN(&return_data, 1, 1, NULL));
-
-  byte data2[] = {'d', 'a', 'v', 'e', ' ', ' ', '\t', '\\'};
-  int data2_copies = 2;
-  EXPECT_SUCCEEDED(file_write.WriteN(data2, arraysize(data2), data2_copies,
-                                     NULL));
-  file_write.Close();
-
-  // read in the file line by line using the File class
-  std::vector<char*> lines1;
-  File file_read1;
-  ASSERT_SUCCEEDED(file_read1.Open(temp_file_, false, false));
-  while (true) {
-    uint32 bytes_read;
-    scoped_array<char> line(new char[256]);
-    if (FAILED(file_read1.ReadLineAnsi(256, line.get(), &bytes_read))) {
-      break;
-    }
-    lines1.push_back(line.release());
-  }
-
-  file_read1.Close();
-
-  // read in the file line by line using the FileReader class
-  std::vector<char*> lines2;
-  FileReader file_read2;
-  size_t buffer_size = 512;
-  ASSERT_SUCCEEDED(file_read2.Init(temp_file_, buffer_size));
-  while (true) {
-    scoped_array<char> line(new char[256]);
-    if (FAILED(file_read2.ReadLineAnsi(256, line.get()))) {
-      break;
-    }
-    lines2.push_back(line.release());
-  }
-
-  // Verify that everything matches
-  byte* (orig_lines[]) = {data1, data2};
-  int copies[] = {data1_copies, data2_copies};
-  int lengths[] = {arraysize(data1), arraysize(data2)};
-  Compare(lines1, lines2, orig_lines, lengths, copies);
-
-  // Free the allocated memory
-  DeleteContainedArrays(&lines1);
-  DeleteContainedArrays(&lines1);
-}
-
-// Two readers should be able to read from the same file.
-TEST_F(ReadingFilesTest, ReadFileShare) {
-  File file;
-  EXPECT_SUCCEEDED(file.Open(temp_file_, true, false));
-  file.Close();
-
-  const size_t kBufferSize = 0x100;
-
-  FileReader reader1;
-  EXPECT_SUCCEEDED(reader1.Init(temp_file_, kBufferSize));
-
-  FileReader reader2;
-  EXPECT_SUCCEEDED(reader2.Init(temp_file_, kBufferSize));
-}
-
-HRESULT WriteCStringA(File* file, CStringA* str) {
-  return file->Write(reinterpret_cast<const byte*>(str->GetBuffer()),
-                     str->GetLength(),
-                     NULL);
-}
-
-TEST_F(ReadingFilesTest, ReadFileStringAnsi) {
-  CStringA line1 = "hello there, here's some data";
-  CStringA line2 = "i've got more\tdata over here.";
-  CStringA eol1 = "\r";
-  CStringA eol2 = "\r\n";
-  CStringA eol3 = "\n";
-
-  std::vector<CString> expected_lines;
-  expected_lines.push_back(CString(line1));
-  expected_lines.push_back(CString(line2));
-  expected_lines.push_back(CString(line1));
-  expected_lines.push_back(CString(line1));
-  expected_lines.push_back(CString(line2));
-
-  File file_write;
-  EXPECT_SUCCEEDED(file_write.Open(temp_file_, true, false));
-  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &line1));
-  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &eol1));
-  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &line2));
-  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &eol1));
-  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &line1));
-  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &eol3));
-  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &line1));
-  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &eol2));
-  EXPECT_SUCCEEDED(WriteCStringA(&file_write, &line2));
-  file_write.Close();
-
-  FileReader reader;
-  EXPECT_SUCCEEDED(reader.Init(temp_file_, 20));
-
-  std::vector<CString> read_lines;
-  CString current_string;
-  while (SUCCEEDED(reader.ReadLineString(&current_string))) {
-    read_lines.push_back(current_string);
-  }
-
-  ASSERT_EQ(expected_lines.size(), read_lines.size());
-
-  if (expected_lines.size() == read_lines.size()) {
-    for (size_t i = 0; i < expected_lines.size(); ++i) {
-      CString expected_str = expected_lines[i];
-      CString read_str = read_lines[i];
-      ASSERT_STREQ(expected_str, read_str);
-    }
-  }
-}
-
-TEST_F(ReadingFilesTest, ReadFileStringUnicodeSmallBuffer) {
-  TestReadFileStringUnicode(20);
-}
-
-TEST_F(ReadingFilesTest, ReadFileStringUnicodeHugeBuffer) {
-  TestReadFileStringUnicode(4096);
-}
-
-TEST_F(ReadingFilesTest, ReadFileStringUnicodeSmallOddBufferBuffer) {
-  TestReadFileStringUnicode(19);
-}
-
-TEST_F(ReadingFilesTest, ReadFileStringUnicodeTinyOddBuffer) {
-  TestReadFileStringUnicode(3);
-}
-
-TEST_F(ReadingFilesTest, ReadFileStringUnicodeTinyEvenBuffer) {
-  TestReadFileStringUnicode(2);
-}
-
-TEST_F(ReadingFilesTest, ReadFileStringUnicodeOneByteBuffer) {
-  File file_write;
-  EXPECT_SUCCEEDED(file_write.Open(temp_file_, true, false));
-
-  // Write the unicode marker to the beginning of the file.
-  byte buf[2] = {0xff, 0xfe};
-  EXPECT_SUCCEEDED(file_write.Write(buf, sizeof(buf), NULL));
-
-  file_write.Close();
-
-  FileReader reader;
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER),
-            reader.Init(temp_file_, 1));
-}
-
-}  // namespace omaha
-
diff --git a/common/file_store.cc b/common/file_store.cc
deleted file mode 100644
index 85b2dc0..0000000
--- a/common/file_store.cc
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Defines class FileStore
-
-#include <vector>
-#include "omaha/common/debug.h"
-#include "omaha/common/file.h"
-#include "omaha/common/file_store.h"
-#include "omaha/common/string.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-// Open the store
-bool FileStore::Open(const TCHAR* file_path) {
-  file_path_ = String_MakeEndWith(file_path, _T("\\"), false);
-  return true;
-}
-
-// Close the store
-bool FileStore::Close() {
-  file_path_.Empty();
-  return true;
-}
-
-// Clear the store
-bool FileStore::Clear() {
-  if (File::Exists(file_path_) && File::IsDirectory(file_path_)) {
-    return SUCCEEDED(DeleteDirectoryFiles(file_path_));
-  } else {
-    return true;
-  }
-}
-
-// Read a value from the store
-bool FileStore::Read(const TCHAR* name, std::vector<byte>* data) const {
-  ASSERT1(name);
-  ASSERT1(data);
-
-  return Exists(name) && SUCCEEDED(ReadEntireFile(file_path_ + name, 0, data));
-}
-
-// Write a value to the store
-bool FileStore::Write(const TCHAR* name, byte* data, int data_size) {
-  ASSERT1(name);
-  ASSERT1(data);
-  ASSERT1(data_size);
-
-  std::vector<byte> buffer(data_size);
-  memcpy(&buffer.front(), data, data_size);
-
-  return SUCCEEDED(WriteEntireFile(file_path_ + name, buffer));
-}
-
-// Check to see a named value exists in the store
-bool FileStore::Exists(const TCHAR* name) const {
-  ASSERT1(name);
-
-  return File::Exists(file_path_ + name);
-}
-
-// Remove a value from the store
-bool FileStore::Remove(const TCHAR* name) {
-  ASSERT1(name);
-
-  return SUCCEEDED(File::Remove(file_path_ + name));
-}
-
-// Get the number of values for this store
-bool FileStore::GetValueCount(uint32* value_count) {
-  ASSERT1(value_count);
-
-  std::vector<CString> matching_paths;
-
-  if (FAILED(File::GetWildcards(file_path_, _T("*"), &matching_paths))) {
-    return false;
-  }
-
-  *value_count = matching_paths.size();
-
-  return true;
-}
-
-// Get the value name for the given value name index
-bool FileStore::GetValueNameAt(uint32 index, CString* value_name) {
-  ASSERT1(value_name);
-
-  std::vector<CString> matching_paths;
-
-  if (FAILED(File::GetWildcards(file_path_, _T("*"), &matching_paths))) {
-    return false;
-  }
-  if (index >= matching_paths.size()) {
-    return false;
-  }
-
-  *value_name = matching_paths[index].Mid(file_path_.GetLength());
-
-  return true;
-}
-
-}  // namespace omaha
-
diff --git a/common/file_store_unittest.cc b/common/file_store_unittest.cc
deleted file mode 100644
index eb127de..0000000
--- a/common/file_store_unittest.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Unit test for the file_store.
-
-
-
-#include <shlobj.h>
-#include "base/basictypes.h"
-#include "omaha/common/file_store.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/utils.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-const TCHAR kFilePathPrefix[] = _T("unittest");
-const TCHAR kFileName1[] = _T("fname1");
-const TCHAR kFileName2[] = _T("fname2");
-char kFileContent[] = "1234567890abcdefg";
-
-TEST(FileStoreTest, FileStore) {
-  // Create a temp dir
-  TCHAR temp_path[MAX_PATH];
-  *temp_path = 0;
-  ASSERT_LT(0u, ::GetTempPath(MAX_PATH, temp_path));
-
-  CString temp_dir;
-  temp_dir.Format(_T("%s%s%x"), temp_path, kFilePathPrefix, ::GetTickCount());
-  ASSERT_EQ(::SHCreateDirectoryEx(0, temp_dir, 0), ERROR_SUCCESS);
-  ON_SCOPE_EXIT(DeleteDirectory, temp_dir);
-
-  // Test the file store
-  FileStore file_store;
-  ASSERT_TRUE(file_store.Open(temp_dir));
-
-  // Make sure the folder is empty
-  uint32 value_count;
-  ASSERT_TRUE(file_store.GetValueCount(&value_count));
-  ASSERT_EQ(value_count, 0);
-  CString value_name;
-  ASSERT_FALSE(file_store.GetValueNameAt(0, &value_name));
-
-  // Write 2 files
-  std::vector<byte> buffer;
-  ASSERT_TRUE(file_store.Write(kFileName1,
-                               reinterpret_cast<byte*>(kFileContent),
-                               arraysize(kFileContent)));
-  ASSERT_TRUE(file_store.Exists(kFileName1));
-  ASSERT_TRUE(file_store.GetValueCount(&value_count));
-  ASSERT_EQ(value_count, 1);
-  ASSERT_TRUE(file_store.GetValueNameAt(0, &value_name));
-  ASSERT_TRUE(value_name == kFileName1);
-  ASSERT_TRUE(file_store.Read(kFileName1, &buffer));
-  ASSERT_TRUE(memcmp(kFileContent,
-                     &buffer.front(),
-                     arraysize(kFileContent)) == 0);
-
-  ASSERT_TRUE(file_store.Write(kFileName2,
-                               reinterpret_cast<byte*>(kFileContent),
-                               arraysize(kFileContent)));
-  ASSERT_TRUE(file_store.Exists(kFileName2));
-  ASSERT_TRUE(file_store.GetValueCount(&value_count));
-  ASSERT_EQ(value_count, 2);
-  ASSERT_TRUE(file_store.GetValueNameAt(1, &value_name));
-  ASSERT_TRUE(value_name == kFileName2);
-  ASSERT_TRUE(file_store.Read(kFileName2, &buffer));
-  ASSERT_TRUE(memcmp(kFileContent,
-                     &buffer.front(),
-                     arraysize(kFileContent)) == 0);
-
-  // Remove files
-  ASSERT_TRUE(file_store.Remove(kFileName1));
-  ASSERT_FALSE(file_store.Exists(kFileName1));
-  ASSERT_TRUE(file_store.GetValueCount(&value_count));
-  ASSERT_EQ(value_count, 1);
-  ASSERT_TRUE(file_store.Remove(kFileName2));
-  ASSERT_FALSE(file_store.Exists(kFileName2));
-  ASSERT_TRUE(file_store.GetValueCount(&value_count));
-  ASSERT_EQ(value_count, 0);
-
-  ASSERT_TRUE(file_store.Close());
-}
-
-}  // namespace omaha
-
diff --git a/common/file_unittest.cc b/common/file_unittest.cc
deleted file mode 100644
index 4607509..0000000
--- a/common/file_unittest.cc
+++ /dev/null
@@ -1,418 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// File unittest
-
-#include "omaha/common/file.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/string.h"
-#include "omaha/common/timer.h"
-#include "omaha/common/tr_rand.h"
-#include "omaha/common/utils.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-// TODO(omaha): test error-prone functions such as ReadLineAnsi
-
-namespace {
-
-#define kIEBrowserExe \
-  _T("C:\\PROGRAM FILES\\Internet Explorer\\iexplore.exe")
-
-#define kIEBrowserQuotedExe \
-  _T("\"") kIEBrowserExe _T("\"")
-
-}  // namespace
-
-void SimpleTest(bool async) {
-  File f;
-
-  char buf[1000] = "test";
-  uint32 len1 = 4;
-
-  char buf2[1000] = "aaaa";
-  uint32 len2 = 4;
-
-  char buf3[1000] = "bbbb";
-  uint32 len3 = 4;
-
-  char buf4[1000] = "    ";
-  uint32 len4 = 4;
-
-  CString s(_T("test"));
-  CString testfile(_T("testfile.1"));
-
-  ASSERT_SUCCEEDED(f.Open(testfile, true, async));
-  uint32 pos = 0;
-  ASSERT_SUCCEEDED(f.WriteAt(pos,
-                             reinterpret_cast<byte*>(buf),
-                             len1,
-                             0,
-                             NULL));
-  pos += len1;
-
-  ASSERT_SUCCEEDED(f.WriteAt(pos,
-                             reinterpret_cast<byte*>(buf3),
-                             len3,
-                             0,
-                             NULL));
-  pos += len3;
-
-  ASSERT_SUCCEEDED(f.WriteAt(pos,
-                             reinterpret_cast<byte*>(buf3),
-                             len3,
-                             0,
-                             NULL));
-  pos += len3;
-
-  ASSERT_SUCCEEDED(f.WriteAt(pos,
-                             reinterpret_cast<byte*>(buf3),
-                             len3,
-                             0,
-                             NULL));
-  pos += len3;
-
-  ASSERT_SUCCEEDED(f.WriteAt(pos,
-                             reinterpret_cast<byte*>(buf3),
-                             len3,
-                             0,
-                             NULL));
-  pos += len3;
-
-  ASSERT_SUCCEEDED(f.WriteAt(pos,
-                             reinterpret_cast<byte*>(buf3),
-                             len3,
-                             0,
-                             NULL));
-  pos += len3;
-
-  ASSERT_SUCCEEDED(f.WriteAt(pos,
-                             reinterpret_cast<byte*>(buf3),
-                             len3,
-                             0,
-                             NULL));
-  pos += len3;
-
-  ASSERT_SUCCEEDED(f.ReadAt(0, reinterpret_cast<byte*>(buf4), len1, 0, NULL));
-  ASSERT_STREQ(buf, buf4);
-
-  ASSERT_SUCCEEDED(f.ReadAt(4, reinterpret_cast<byte*>(buf4), len3, 0, NULL));
-  ASSERT_STREQ(buf3, buf4);
-
-  ASSERT_SUCCEEDED(f.WriteAt(1, reinterpret_cast<byte*>(buf3), 2, 0, NULL));
-
-  ASSERT_SUCCEEDED(f.WriteAt(20, reinterpret_cast<byte*>(buf), 2, 0, NULL));
-  ASSERT_SUCCEEDED(f.ReadAt(20, reinterpret_cast<byte*>(buf), 2, 0, NULL));
-
-  ASSERT_SUCCEEDED(f.WriteAt(30, reinterpret_cast<byte*>(buf), 2, 0, NULL));
-
-  ASSERT_SUCCEEDED(f.SeekFromBegin(0));
-
-  ASSERT_SUCCEEDED(f.ReadAt(30, reinterpret_cast<byte*>(buf), 2, 0, NULL));
-
-  ASSERT_SUCCEEDED(f.Close());
-
-  ASSERT_SUCCEEDED(f.Open(L"testfile.1", false, false));
-  ASSERT_SUCCEEDED(f.ReadAt(0, reinterpret_cast<byte*>(buf), 16, 0, NULL));
-  buf[17] = '\0';
-
-  uint64 size_on_disk = 0;
-  ASSERT_SUCCEEDED(f.GetSizeOnDisk(&size_on_disk));
-  ASSERT_EQ(size_on_disk, 32);
-
-  ASSERT_SUCCEEDED(f.Close());
-
-  ASSERT_TRUE(File::Exists(testfile));
-  ASSERT_SUCCEEDED(File::Remove(testfile));
-  ASSERT_FALSE(File::Exists(testfile));
-}
-
-void FileWriteCreate(uint32 file_size) {
-  Timer time(true);
-  CString testfile;
-  testfile.Format(L"testfile%u", file_size);
-
-  File f;
-  ASSERT_SUCCEEDED(f.Open(testfile, true, false));
-  ASSERT_SUCCEEDED(f.SetLength(file_size, false));
-
-  uint32 write_size = 512;
-
-  byte *buf2 = new byte[write_size];
-
-  for (uint32 j = 0; j < file_size - write_size; j += write_size) {
-      for (uint32 i = 0; i < write_size; i++) {
-        buf2[i] = static_cast<byte>(tr_rand() % 255);
-      }
-      ASSERT_SUCCEEDED(f.WriteAt(j, buf2, write_size, 0, NULL));
-  }
-
-  f.Sync();
-  f.Close();
-}
-
-void FileWriteTimeTest(uint32 file_size,
-                       uint32 number_writes,
-                       uint32 write_size) {
-  Timer time(true);
-  CString testfile;
-  testfile.Format(L"testfile%u", file_size);
-
-  File f;
-  ASSERT_SUCCEEDED(f.Open(testfile, true, false));
-
-  byte *buf = new byte[write_size];
-
-  for (uint32 i = 0; i < number_writes; i++) {
-    for (uint32 j = 0; j < write_size; j++) {
-      buf[j] = static_cast<byte>(tr_rand() % 255);
-    }
-    uint32 pos = (tr_rand() * 65536 + tr_rand()) % (file_size - write_size);
-    ASSERT_SUCCEEDED(f.WriteAt(pos, buf, write_size, 0, NULL));
-  }
-
-  delete[] buf;
-
-  ASSERT_SUCCEEDED(f.Sync());
-  ASSERT_SUCCEEDED(f.Close());
-}
-
-TEST(FileTest, File) {
-  SimpleTest(false);
-
-  int header = 123;
-  int header2 = 0;
-
-  File f2;
-  ASSERT_SUCCEEDED(f2.Open(L"testfile.3", true, false));
-  ASSERT_SUCCEEDED(f2.WriteAt(0,
-                              reinterpret_cast<byte*>(&header),
-                              sizeof(header),
-                              0,
-                              NULL));
-  ASSERT_SUCCEEDED(f2.ReadAt(0,
-                             reinterpret_cast<byte*>(&header2),
-                             sizeof(header),
-                             0,
-                             NULL));
-  ASSERT_EQ(header, header2);
-
-  uint64 size_on_disk = 0;
-
-  ASSERT_SUCCEEDED(f2.GetSizeOnDisk(&size_on_disk));
-  ASSERT_EQ(size_on_disk, sizeof(header));
-  ASSERT_SUCCEEDED(f2.Close());
-
-  const int kLevel = 1;
-  for (uint32 file_size = 2 * 1024 * 1024;
-       file_size <= 2 * 1024 * 1024;
-       file_size *= 2) {
-    for (uint32 number_writes = 100;
-         number_writes <= (300 * static_cast<uint32>(kLevel));
-         number_writes *= 2) {
-      uint32 write_size = 128;
-      FileWriteTimeTest(file_size, number_writes, write_size);
-    }
-  }
-
-  // Test File::Copy, File::Move, File::CopyWildcards
-  {
-    CString windows_dir;
-    CString temp_dir;
-    DWORD dw = ::GetEnvironmentVariable(
-        L"SystemRoot",
-        windows_dir.GetBufferSetLength(MAX_PATH),
-        MAX_PATH);
-    windows_dir.ReleaseBuffer();
-    ASSERT_TRUE(dw);
-    dw = ::GetEnvironmentVariable(L"TEMP",
-                                  temp_dir.GetBufferSetLength(MAX_PATH),
-                                  MAX_PATH);
-    temp_dir.ReleaseBuffer();
-    ASSERT_TRUE(dw);
-    CString known_file1(windows_dir + L"\\NOTEPAD.EXE");
-    CString known_file2(windows_dir + L"\\REGEDIT.EXE");
-    CString temp_file1(temp_dir + L"\\FOO.TMP");
-    CString temp_file2(temp_dir + L"\\BAR.TMP");
-    uint32 known_size1 = 0;
-    uint32 known_size2 = 0;
-    uint32 temp_size1 = 0;
-
-    // Start with neither file existing
-    if (File::Exists(temp_file1))
-      File::Remove(temp_file1);
-    if (File::Exists(temp_file2))
-      File::Remove(temp_file2);
-    ASSERT_FALSE(File::Exists(temp_file1));
-    ASSERT_FALSE(File::Exists(temp_file2));
-    ASSERT_SUCCEEDED(File::GetFileSizeUnopen(known_file1, &known_size1));
-    ASSERT_SUCCEEDED(File::GetFileSizeUnopen(known_file2, &known_size2));
-    ASSERT_NE(known_size1, known_size2);
-
-    // Copy to create a file, move it to 2nd file, then remove 2nd file
-    ASSERT_SUCCEEDED(File::Copy(known_file1, temp_file1, false));
-    ASSERT_TRUE(File::Exists(temp_file1));
-    ASSERT_SUCCEEDED(File::Move(temp_file1, temp_file2, false));
-    ASSERT_FALSE(File::Exists(temp_file1));
-    ASSERT_TRUE(File::Exists(temp_file2));
-    ASSERT_SUCCEEDED(File::Remove(temp_file2));
-    ASSERT_FALSE(File::Exists(temp_file2));
-
-    // Try copying a file on top of a file - with and without
-    // replace_existing_file=true
-    ASSERT_SUCCEEDED(File::Copy(known_file1, temp_file1, false));
-    ASSERT_SUCCEEDED(File::GetFileSizeUnopen(temp_file1, &temp_size1));
-    ASSERT_EQ(temp_size1, known_size1);
-    ASSERT_SUCCEEDED(File::Copy(known_file2, temp_file1, false));
-    ASSERT_TRUE(File::Exists(temp_file1));
-    ASSERT_SUCCEEDED(File::GetFileSizeUnopen(temp_file1, &temp_size1));
-    ASSERT_EQ(temp_size1, known_size1);
-    ASSERT_SUCCEEDED(File::Copy(known_file2, temp_file1, true));
-    ASSERT_TRUE(File::Exists(temp_file1));
-    ASSERT_SUCCEEDED(File::GetFileSizeUnopen(temp_file1, &temp_size1));
-    ASSERT_EQ(temp_size1, known_size2);
-    ASSERT_SUCCEEDED(File::Remove(temp_file1));
-
-    // Try copying a bunch of files
-    CString known_file3(windows_dir + L"\\twunk_32.exe");
-    CString known_file4(windows_dir + L"\\twunk_16.exe");
-    CString temp_file3(temp_dir + L"\\twunk_32.exe");
-    CString temp_file4(temp_dir + L"\\twunk_16.exe");
-    if (File::Exists(temp_file3))
-      File::Remove(temp_file3);
-    if (File::Exists(temp_file4))
-      File::Remove(temp_file4);
-    ASSERT_TRUE(File::Exists(known_file3));
-    ASSERT_TRUE(File::Exists(known_file4));
-    ASSERT_FALSE(File::Exists(temp_file3));
-    ASSERT_FALSE(File::Exists(temp_file4));
-    ASSERT_SUCCEEDED(File::CopyWildcards(windows_dir,
-                                         temp_dir,
-                                         L"twunk*.exe",
-                                         true));
-    ASSERT_TRUE(File::Exists(temp_file3));
-    ASSERT_TRUE(File::Exists(temp_file4));
-    ASSERT_SUCCEEDED(File::Remove(temp_file3));
-    ASSERT_SUCCEEDED(File::Remove(temp_file4));
-
-    std::vector<CString> matching_files;
-    ASSERT_SUCCEEDED(File::GetWildcards(windows_dir,
-                                        L"twunk*.exe",
-                                        &matching_files));
-    ASSERT_EQ(matching_files.size(), 2);
-    ASSERT_TRUE(matching_files[0] == known_file3 ||
-                matching_files[0] == known_file4);
-    ASSERT_TRUE(matching_files[1] == known_file3 ||
-                matching_files[1] == known_file4);
-    ASSERT_TRUE(matching_files[0] != matching_files[1]);
-  }
-}
-
-
-TEST(FileTest, FileChangeWatcher) {
-  CString temp_dir;
-  ASSERT_TRUE(::GetEnvironmentVariable(L"TEMP",
-                                       temp_dir.GetBufferSetLength(MAX_PATH),
-                                       MAX_PATH) != 0);
-  temp_dir.ReleaseBuffer();
-  temp_dir = String_MakeEndWith(temp_dir, _T("\\"), false /* ignore_case */);
-  temp_dir = temp_dir + _T("omaha_unittest") + itostr(tr_rand() % 255);
-  EXPECT_SUCCEEDED(CreateDir(temp_dir, 0));
-
-  // watch the directory for changes
-  FileWatcher watcher(temp_dir, false, FILE_NOTIFY_CHANGE_LAST_WRITE);
-  EXPECT_SUCCEEDED(watcher.EnsureEventSetup());
-  EXPECT_FALSE(watcher.HasChangeOccurred());
-
-  //
-  // verify that the watcher got set-up correctly the first time
-  //
-
-  // do something in the dir
-  File f;
-  int header1 = 94;
-  ASSERT_SUCCEEDED(f.Open(temp_dir + _T("\\testfile.1"), true, false));
-  ASSERT_SUCCEEDED(f.WriteAt(0, reinterpret_cast<byte*>(&header1),
-                             sizeof(header1), 0, NULL));
-  ASSERT_SUCCEEDED(f.Sync());
-  ASSERT_SUCCEEDED(f.Close());
-
-  // Did we noticed that something happened?
-  EXPECT_TRUE(watcher.HasChangeOccurred());
-  EXPECT_SUCCEEDED(watcher.EnsureEventSetup());
-  EXPECT_FALSE(watcher.HasChangeOccurred());
-
-  //
-  // verify that the watcher got set-up correctly the second time
-  //
-
-  // do something in the dir
-  byte header2 = 2;
-  ASSERT_SUCCEEDED(f.Open(temp_dir + _T("\\testfile.2"), true, false));
-  ASSERT_SUCCEEDED(f.WriteAt(0, reinterpret_cast<byte*>(&header2),
-                             sizeof(header2), 0, NULL));
-  ASSERT_SUCCEEDED(f.Sync());
-  ASSERT_SUCCEEDED(f.Close());
-
-  // Did we noticed that something happened?
-  EXPECT_TRUE(watcher.HasChangeOccurred());
-  EXPECT_SUCCEEDED(watcher.EnsureEventSetup());
-  EXPECT_FALSE(watcher.HasChangeOccurred());
-
-  EXPECT_SUCCEEDED(DeleteDirectory(temp_dir));
-}
-
-TEST(FileTest, Exists_UnQuoted) {
-  EXPECT_TRUE(File::Exists(kIEBrowserExe));
-  EXPECT_FALSE(File::Exists(_T("C:\\foo\\does not exist.exe")));
-  EXPECT_FALSE(File::Exists(_T("Z:\\foo\\does not exist.exe")));
-  if (ShouldRunLargeTest()) {
-    EXPECT_FALSE(File::Exists(_T("\\\\foo\\does not exist.exe")));
-  }
-}
-
-// File::Exists() expects unquoted paths.
-TEST(FileTest, Exists_Quoted) {
-  EXPECT_FALSE(File::Exists(kIEBrowserQuotedExe));
-  EXPECT_FALSE(File::Exists(_T("\"C:\\foo\\does not exist.exe\"")));
-  EXPECT_FALSE(File::Exists(_T("\"Z:\\foo\\does not exist.exe\"")));
-  if (ShouldRunLargeTest()) {
-    EXPECT_FALSE(File::Exists(_T("\"\\\\foo\\does not exist.exe\"")));
-  }
-}
-
-// File::Exists() handles trailing spaces but not leading whitespace, tabs, or
-// enclosed paths.
-TEST(FileTest, Exists_ExtraWhitespace) {
-  EXPECT_TRUE(File::Exists(kIEBrowserExe _T(" ")));
-  EXPECT_TRUE(File::Exists(kIEBrowserExe _T("    ")));
-  EXPECT_FALSE(File::Exists(_T(" ") kIEBrowserExe));
-  EXPECT_FALSE(File::Exists(kIEBrowserExe _T("\t")));
-
-  EXPECT_FALSE(File::Exists(kIEBrowserQuotedExe _T(" ")));
-  EXPECT_FALSE(File::Exists(kIEBrowserQuotedExe _T("    ")));
-  EXPECT_FALSE(File::Exists(_T(" ") kIEBrowserQuotedExe));
-  EXPECT_FALSE(File::Exists(kIEBrowserQuotedExe _T("\t")));
-
-  EXPECT_FALSE(File::Exists(_T("\"") kIEBrowserExe _T(" \"")));
-  EXPECT_FALSE(File::Exists(_T("\"") kIEBrowserExe _T("    \"")));
-  EXPECT_FALSE(File::Exists(_T("\" ") kIEBrowserExe _T("\"") ));
-  EXPECT_FALSE(File::Exists(_T("\"") kIEBrowserExe _T("\t\"")));
-
-  EXPECT_FALSE(File::Exists(_T("\"") kIEBrowserExe _T(" \" ")));
-}
-
-}  // namespace omaha
diff --git a/common/file_ver.cc b/common/file_ver.cc
deleted file mode 100644
index 6aed887..0000000
--- a/common/file_ver.cc
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/file_ver.h"
-#include "omaha/common/debug.h"
-
-namespace omaha {
-
-// TODO(omaha): Write unittest for this class.
-
-FileVer::FileVer() {
-  file_ver_data_ = NULL;
-  lang_charset_ = 0;
-}
-
-FileVer::~FileVer() {
-  Close();
-}
-
-void FileVer::Close() {
-  delete[] file_ver_data_;
-  file_ver_data_ = NULL;
-  lang_charset_ = 0;
-}
-
-BOOL FileVer::Open(const TCHAR* lpszModuleName) {
-  ASSERT1(lpszModuleName);
-
-  // Get the version information size and allocate the buffer.
-  DWORD handle;
-  DWORD ver_info_size =
-      ::GetFileVersionInfoSize(const_cast<TCHAR*>(lpszModuleName), &handle);
-  if (ver_info_size == 0) {
-    return FALSE;
-  }
-
-  // Get version information.
-  // file_ver_data_ is allocated here and deleted in Close() (or implicitly
-  // in the destructor).
-  file_ver_data_ = new byte[ver_info_size];
-  ASSERT1(file_ver_data_);
-  if (!file_ver_data_) {
-    return FALSE;
-  }
-
-  if (!::GetFileVersionInfo(const_cast<TCHAR*>(lpszModuleName), handle,
-                            ver_info_size,
-                            reinterpret_cast<void**>(file_ver_data_))) {
-    Close();
-    return FALSE;
-  }
-
-  // Get the first language and character-set identifier.
-  UINT query_size = 0;
-  DWORD* translation_table = NULL;
-  if (!::VerQueryValue(file_ver_data_,
-                       _T("\\VarFileInfo\\Translation"),
-                       reinterpret_cast<void**>(&translation_table),
-                       &query_size) ||
-      query_size == 0) {
-    Close();
-    return FALSE;
-  }
-
-  ASSERT1(query_size != 0);
-  ASSERT1(translation_table);
-
-  // Create charset.
-  lang_charset_ = MAKELONG(HIWORD(translation_table[0]),
-                           LOWORD(translation_table[0]));
-  return TRUE;
-}
-
-CString FileVer::QueryValue(const TCHAR* lpszValueName) const {
-  ASSERT1(lpszValueName);
-
-  if (file_ver_data_ == NULL) {
-    return (CString)_T("");
-  }
-
-  // Query version information value.
-  UINT query_size = 0;
-  LPVOID query_data = NULL;
-  CString str_query_value, str_block_name;
-  str_block_name.Format(_T("\\StringFileInfo\\%08lx\\%s"),
-                        lang_charset_,
-                        lpszValueName);
-
-  if (::VerQueryValue(reinterpret_cast<void**>(file_ver_data_),
-                      str_block_name.GetBuffer(0),
-                      &query_data,
-                      &query_size) &&
-      query_size != 0 &&
-      query_data) {
-    str_query_value = reinterpret_cast<const TCHAR*>(query_data);
-  }
-
-  str_block_name.ReleaseBuffer();
-
-  return str_query_value;
-}
-
-BOOL FileVer::GetFixedInfo(VS_FIXEDFILEINFO& vsffi) const {   // NOLINT
-  if (file_ver_data_ == NULL) {
-    return FALSE;
-  }
-
-  UINT query_size = 0;
-  VS_FIXEDFILEINFO* pVsffi = NULL;
-  if (::VerQueryValue(reinterpret_cast<void**>(file_ver_data_),
-                      _T("\\"),
-                      reinterpret_cast<void**>(&pVsffi),
-                      &query_size) &&
-      query_size != 0 &&
-      pVsffi) {
-    vsffi = *pVsffi;
-    return TRUE;
-  }
-
-  return FALSE;
-}
-
-CString FileVer::FormatFixedFileVersion() const {
-  CString str_version;
-  VS_FIXEDFILEINFO vsffi = {0};
-
-  if (GetFixedInfo(vsffi)) {
-    str_version.Format(NOTRANSL(_T("%u.%u.%u.%u")),
-                       HIWORD(vsffi.dwFileVersionMS),
-                       LOWORD(vsffi.dwFileVersionMS),
-                       HIWORD(vsffi.dwFileVersionLS),
-                       LOWORD(vsffi.dwFileVersionLS));
-  }
-  return str_version;
-}
-
-CString FileVer::FormatFixedProductVersion() const {
-  CString str_version;
-  VS_FIXEDFILEINFO vsffi = {0};
-
-  if (GetFixedInfo(vsffi)) {
-    str_version.Format(NOTRANSL(_T("%u.%u.%u.%u")),
-                       HIWORD(vsffi.dwProductVersionMS),
-                       LOWORD(vsffi.dwProductVersionMS),
-                       HIWORD(vsffi.dwProductVersionLS),
-                       LOWORD(vsffi.dwProductVersionLS));
-  }
-  return str_version;
-}
-
-ULONGLONG FileVer::GetFileVersionAsULONGLONG() const {
-  ULONGLONG version = 0;
-  VS_FIXEDFILEINFO vsffi = {0};
-
-  if (GetFixedInfo(vsffi)) {
-    version = MAKEDLLVERULL(HIWORD(vsffi.dwProductVersionMS),
-                            LOWORD(vsffi.dwProductVersionMS),
-                            HIWORD(vsffi.dwProductVersionLS),
-                            LOWORD(vsffi.dwProductVersionLS));
-  }
-  return version;
-}
-
-}  // namespace omaha
-
diff --git a/common/firewall_product_detection.cc b/common/firewall_product_detection.cc
deleted file mode 100644
index cc32cfa..0000000
--- a/common/firewall_product_detection.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/firewall_product_detection.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/wmi_query.h"
-
-namespace omaha {
-
-namespace firewall_detection {
-
-namespace {
-
-const TCHAR kWmiSecurityCenter[]        = _T("root\\SecurityCenter");
-const TCHAR kWmiQueryFirewallProduct[]  = _T("select * from FirewallProduct");
-const TCHAR kWmiPropDisplayName[]       = _T("displayName");
-const TCHAR kWmiPropVersionNumber[]     = _T("versionNumber");
-
-}  // namespace
-
-
-HRESULT Detect(CString* name, CString* version) {
-  ASSERT1(name);
-  ASSERT1(version);
-
-  name->Empty();
-  version->Empty();
-
-  WmiQuery wmi_query;
-  HRESULT hr = wmi_query.Connect(kWmiSecurityCenter);
-  if FAILED(hr) {
-    return hr;
-  }
-  hr = wmi_query.Query(kWmiQueryFirewallProduct);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  if (wmi_query.AtEnd()) {
-    return E_FAIL;
-  }
-  hr = wmi_query.GetValue(kWmiPropDisplayName, name);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  wmi_query.GetValue(kWmiPropVersionNumber, version);
-  return S_OK;
-}
-
-}  // namespace firewall_detection
-
-}  // namespace omaha
-
diff --git a/common/firewall_product_detection_unittest.cc b/common/firewall_product_detection_unittest.cc
deleted file mode 100644
index fe440ee..0000000
--- a/common/firewall_product_detection_unittest.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include "omaha/common/firewall_product_detection.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-namespace firewall_detection {
-
-TEST(FirewallProductDetection, Detect) {
-  CString name, version;
-  HRESULT hr = Detect(&name, &version);
-  if (SUCCEEDED(hr)) {
-    EXPECT_FALSE(name.IsEmpty());
-    EXPECT_FALSE(version.IsEmpty());
-  }
-}
-
-}  // namespace firewall_detection
-
-}  // namespace omaha
-
diff --git a/common/generic_reg_file_elevation_localserver.rgs b/common/generic_reg_file_elevation_localserver.rgs
deleted file mode 100644
index 15aeb12..0000000
--- a/common/generic_reg_file_elevation_localserver.rgs
+++ /dev/null
@@ -1,35 +0,0 @@
-HKLM
-{
-  NoRemove SOFTWARE
-  {
-    NoRemove Classes
-    {
-      %PROGID%.%VERSION% = s '%DESCRIPTION%'
-      {
-        CLSID = s '%CLSID%'
-      }
-      %PROGID% = s '%DESCRIPTION%'
-      {
-        CLSID = s '%CLSID%'
-        CurVer = s '%PROGID%.%VERSION%'
-      }
-      NoRemove CLSID
-      {
-        ForceRemove %CLSID% = s '%DESCRIPTION%'
-        {
-          ProgID = s '%PROGID%.%VERSION%'
-          VersionIndependentProgID = s '%PROGID%'
-          LocalServer32 = s '%MODULE%'
-          'TypeLib' = s '%LIBID%'
-          val LocalizedString = s '@%MODULE_RAW%,-%STRINGRESID%'
-          Elevation
-          {
-            val Enabled = d '1'
-            val IconReference = s '@%MODULE_RAW%,-%ICONRESID%'
-          }
-        }
-      }
-    }
-  }
-}
-
diff --git a/common/generic_reg_file_local_server.rgs b/common/generic_reg_file_local_server.rgs
deleted file mode 100644
index d98b665..0000000
--- a/common/generic_reg_file_local_server.rgs
+++ /dev/null
@@ -1,29 +0,0 @@
-%HKROOT%
-{
-  NoRemove SOFTWARE
-  {
-    NoRemove Classes
-    {
-      %PROGID%.%VERSION% = s '%DESCRIPTION%'
-      {
-        CLSID = s '%CLSID%'
-      }
-      %PROGID% = s '%DESCRIPTION%'
-      {
-        CLSID = s '%CLSID%'
-        CurVer = s '%PROGID%.%VERSION%'
-      }
-      NoRemove CLSID
-      {
-        ForceRemove %CLSID% = s '%DESCRIPTION%'
-        {
-          ProgID = s '%PROGID%.%VERSION%'
-          VersionIndependentProgID = s '%PROGID%'
-          LocalServer32 = s '%MODULE%'
-          'TypeLib' = s '%LIBID%'
-        }
-      }
-    }
-  }
-}
-
diff --git a/common/generic_reg_file_local_server_w_appid.rgs b/common/generic_reg_file_local_server_w_appid.rgs
deleted file mode 100644
index f3be278..0000000
--- a/common/generic_reg_file_local_server_w_appid.rgs
+++ /dev/null
@@ -1,62 +0,0 @@
-HKCR
-{
-  %PROGID%.%VERSION% = s '%DESCRIPTION%'
-  {
-    CLSID = s '%CLSID%'
-  }
-  %PROGID% = s '%DESCRIPTION%'
-  {
-    CLSID = s '%CLSID%'
-    CurVer = s '%PROGID%.%VERSION%'
-  }
-  NoRemove CLSID
-  {
-    ForceRemove %CLSID% = s '%DESCRIPTION%'
-    {
-      ProgID = s '%PROGID%.%VERSION%'
-      VersionIndependentProgID = s '%PROGID%'
-      ForceRemove 'Programmable'
-      LocalServer32 = s '%MODULE%'
-      {
-        val ThreadingModel = s '%THREADING%'
-      }
-      val AppID = s '%APPID%'
-      'TypeLib' = s '%LIBID%'
-    }
-  }
-  NoRemove AppID
-  {
-    ForceRemove %APPID% = s '%DESCRIPTION%'
-    {
-    
-    }
-    ForceRemove %OBJECTFILENAME%
-    {
-      val AppID = s '%APPID%'
-    }    
-  }	
-}
-HKLM
-{
-  NoRemove SOFTWARE
-  {
-    NoRemove Microsoft
-    {
-      NoRemove 'Internet Explorer'
-      {
-        NoRemove 'Low Rights'
-        {
-          NoRemove ElevationPolicy
-          {
-            ForceRemove %APPID%
-            {
-              val AppName = s '%OBJECTFILENAME%'
-              val AppPath = s '%MODULEPATH%'
-              val Policy = d '3'
-            }          
-          }      
-        }  
-      }  
-    }  
-  }
-}
\ No newline at end of file
diff --git a/common/generic_reg_file_localservice.rgs b/common/generic_reg_file_localservice.rgs
deleted file mode 100644
index 77eaf13..0000000
--- a/common/generic_reg_file_localservice.rgs
+++ /dev/null
@@ -1,28 +0,0 @@
-HKLM
-{
-  NoRemove SOFTWARE
-  {
-    NoRemove Classes
-    {
-      %PROGID%.%VERSION% = s '%DESCRIPTION%'
-      {
-        CLSID = s '%CLSID%'
-      }
-      %PROGID% = s '%DESCRIPTION%'
-      {
-        CLSID = s '%CLSID%'
-        CurVer = s '%PROGID%.%VERSION%'
-      }
-      NoRemove CLSID
-      {
-        ForceRemove %CLSID% = s '%DESCRIPTION%'
-        {
-          ProgID = s '%PROGID%.%VERSION%'
-          VersionIndependentProgID = s '%PROGID%'
-          val AppID = s '%APPID%'
-        }
-      }
-    }
-  }
-}
-
diff --git a/common/generic_reg_file_typelib.rgs b/common/generic_reg_file_typelib.rgs
deleted file mode 100644
index 909aa96..0000000
--- a/common/generic_reg_file_typelib.rgs
+++ /dev/null
@@ -1,10 +0,0 @@
-HKCR
-{
-  NoRemove CLSID
-  {
-    ForceRemove %CLSID% = s '%DESCRIPTION%'
-    {
-      'TypeLib' = s '%LIBID%'
-    }
-  }
-}
diff --git a/common/google_update_recovery.cc b/common/google_update_recovery.cc
deleted file mode 100644
index 04547a1..0000000
--- a/common/google_update_recovery.cc
+++ /dev/null
@@ -1,615 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// Implementation of the Google Update recovery mechanism to be included in
-// Google apps.
-
-#include "omaha/common/google_update_recovery.h"
-#include <shellapi.h>
-#include <wininet.h>
-#include <atlstr.h>
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/signaturevalidator.h"
-// TODO(omaha): Move this file to common.
-#include "omaha/enterprise/const_group_policy.h"
-#include "omaha/third_party/smartany/scoped_any.h"
-
-namespace omaha {
-
-namespace {
-
-const int kRollbackWindowDays = 100;
-
-const TCHAR* const kMachineRepairArgs = _T("/recover /machine");
-const TCHAR* const kUserRepairArgs = _T("/recover");
-
-// TODO(omaha): Add a Code Red lib version that is manually updated when
-// we check in the lib.
-const TCHAR* const kQueryStringFormat =
-    _T("?appid=%s&appversion=%s&applang=%s&machine=%u")
-    _T("&version=%s&osversion=%s&servicepack=%s");
-
-// Information about where to obtain Omaha info.
-// This must never change in Omaha.
-const TCHAR* const kRegValueProductVersion  = _T("pv");
-const TCHAR* const kRelativeGoopdateRegPath = _T("Software\\Google\\Update\\");
-const TCHAR* const kRelativeClientsGoopdateRegPath =
-    _T("Software\\Google\\Update\\Clients\\")
-    _T("{430FD4D0-B729-4F61-AA34-91526481799D}");
-
-// Starts another process via ::CreateProcess.
-HRESULT StartProcess(const TCHAR* process_name, TCHAR* command_line) {
-  if (!process_name && !command_line) {
-    return E_INVALIDARG;
-  }
-
-  PROCESS_INFORMATION pi = {0};
-  STARTUPINFO si = {sizeof(si), 0};
-
-  // Feedback cursor is off while the process is starting.
-  si.dwFlags = STARTF_FORCEOFFFEEDBACK;
-
-  BOOL success = ::CreateProcess(
-      process_name,     // Module name
-      command_line,     // Command line
-      NULL,             // Process handle not inheritable
-      NULL,             // Thread handle not inheritable
-      FALSE,            // Set handle inheritance to FALSE
-      0,                // No creation flags
-      NULL,             // Use parent's environment block
-      NULL,             // Use parent's starting directory
-      &si,              // Pointer to STARTUPINFO structure
-      &pi);             // Pointer to PROCESS_INFORMATION structure
-
-  if (!success) {
-    return HRESULT_FROM_WIN32(::GetLastError());
-  }
-
-  ::CloseHandle(pi.hProcess);
-  ::CloseHandle(pi.hThread);
-
-  return S_OK;
-}
-
-// Check if a string starts with another string. Case-sensitive.
-bool StringStartsWith(const TCHAR *str, const TCHAR *start_str) {
-  if (!start_str || !str) {
-    return false;
-  }
-
-  while (0 != *str) {
-    // Check for matching characters
-    TCHAR c1 = *str;
-    TCHAR c2 = *start_str;
-
-    // Reached the end of start_str?
-    if (0 == c2)
-      return true;
-
-    if (c1 != c2)
-      return false;
-
-    ++str;
-    ++start_str;
-  }
-
-  // If str is shorter than start_str, no match.  If equal size, match.
-  return 0 == *start_str;
-}
-
-// Escape and unescape strings (shlwapi-based implementation).
-// The intended usage for these APIs is escaping strings to make up
-// URLs, for example building query strings.
-//
-// Pass false to the flag segment_only to escape the url. This will not
-// cause the conversion of the # (%23), ? (%3F), and / (%2F) characters.
-
-// Characters that must be encoded include any characters that have no
-// corresponding graphic character in the US-ASCII coded character
-// set (hexadecimal 80-FF, which are not used in the US-ASCII coded character
-// set, and hexadecimal 00-1F and 7F, which are control characters),
-// blank spaces, "%" (which is used to encode other characters),
-// and unsafe characters (<, >, ", #, {, }, |, \, ^, ~, [, ], and ').
-//
-// The input and output strings can't be longer than INTERNET_MAX_URL_LENGTH
-
-HRESULT StringEscape(const CString& str_in,
-                     bool segment_only,
-                     CString* escaped_string) {
-  if (!escaped_string) {
-    return E_INVALIDARG;
-  }
-
-  DWORD buf_len = INTERNET_MAX_URL_LENGTH + 1;
-  HRESULT hr = ::UrlEscape(str_in,
-                           escaped_string->GetBufferSetLength(buf_len),
-                           &buf_len,
-                           segment_only ?
-                           URL_ESCAPE_PERCENT | URL_ESCAPE_SEGMENT_ONLY :
-                           URL_ESCAPE_PERCENT);
-  if (SUCCEEDED(hr)) {
-    escaped_string->ReleaseBuffer();
-  }
-  return hr;
-}
-
-// Gets the temporary files directory for the current user.
-// The directory returned may not exist.
-// The returned path ends with a '\'.
-// Fails if the path is longer than MAX_PATH.
-HRESULT GetTempDir(CString* temp_path) {
-  if (!temp_path) {
-    return E_INVALIDARG;
-  }
-
-  temp_path->Empty();
-
-  TCHAR buffer[MAX_PATH] = {0};
-  DWORD num_chars = ::GetTempPath(MAX_PATH, buffer);
-  if (!num_chars) {
-    return HRESULT_FROM_WIN32(::GetLastError());
-  } else if (num_chars >= MAX_PATH) {
-    return E_FAIL;
-  }
-
-  *temp_path = buffer;
-  return S_OK;
-}
-
-// Creates the specified directory.
-HRESULT CreateDir(const CString& dir) {
-  if (!::CreateDirectory(dir, NULL)) {
-    DWORD error = ::GetLastError();
-    if (ERROR_FILE_EXISTS != error && ERROR_ALREADY_EXISTS != error) {
-      return HRESULT_FROM_WIN32(error);
-    }
-  }
-  return S_OK;
-}
-
-HRESULT GetAndCreateTempDir(CString* temp_path) {
-  if (!temp_path) {
-    return E_INVALIDARG;
-  }
-
-  HRESULT hr = GetTempDir(temp_path);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  if (temp_path->IsEmpty()) {
-    return E_FAIL;
-  }
-
-  // Create this dir if it doesn't already exist.
-  return CreateDir(*temp_path);
-}
-
-
-// Create a unique temporary file and returns the full path.
-HRESULT CreateUniqueTempFile(const CString& user_temp_dir,
-                             CString* unique_temp_file_path) {
-  if (user_temp_dir.IsEmpty() || !unique_temp_file_path) {
-    return E_INVALIDARG;
-  }
-
-  TCHAR unique_temp_filename[MAX_PATH] = {0};
-  if (!::GetTempFileName(user_temp_dir,
-                         _T("GUR"),  // prefix
-                         0,          // form a unique filename
-                         unique_temp_filename)) {
-    return HRESULT_FROM_WIN32(::GetLastError());
-  }
-
-  *unique_temp_file_path = unique_temp_filename;
-  if (unique_temp_file_path->IsEmpty()) {
-    return E_FAIL;
-  }
-
-  return S_OK;
-}
-
-// Obtains the OS version and service pack.
-HRESULT GetOSInfo(CString* os_version, CString* service_pack) {
-  if (!os_version || !service_pack) {
-    return E_INVALIDARG;
-  }
-
-  OSVERSIONINFO os_version_info = { 0 };
-  os_version_info.dwOSVersionInfoSize = sizeof(os_version_info);
-  if (!::GetVersionEx(&os_version_info)) {
-    HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
-    return hr;
-  } else {
-    os_version->Format(_T("%d.%d"),
-                       os_version_info.dwMajorVersion,
-                       os_version_info.dwMinorVersion);
-    *service_pack = os_version_info.szCSDVersion;
-  }
-  return S_OK;
-}
-
-// Reads the specified string value from the specified registry key.
-// Only supports value types REG_SZ and REG_EXPAND_SZ.
-// REG_EXPAND_SZ strings are not expanded.
-HRESULT GetRegStringValue(bool is_machine_key,
-                          const CString& relative_key_path,
-                          const CString& value_name,
-                          CString* value) {
-  if (!value) {
-    return E_INVALIDARG;
-  }
-
-  value->Empty();
-  HKEY root_key = is_machine_key ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
-  HKEY key = NULL;
-  LONG res = ::RegOpenKeyEx(root_key, relative_key_path, 0, KEY_READ, &key);
-  if (res != ERROR_SUCCESS) {
-    return HRESULT_FROM_WIN32(res);
-  }
-
-  // First get the size of the string buffer.
-  DWORD type = 0;
-  DWORD byte_count = 0;
-  res = ::RegQueryValueEx(key, value_name, NULL, &type, NULL, &byte_count);
-  if (ERROR_SUCCESS != res) {
-    ::RegCloseKey(key);
-    return HRESULT_FROM_WIN32(res);
-  }
-  if ((type != REG_SZ && type != REG_EXPAND_SZ) || (0 == byte_count)) {
-    ::RegCloseKey(key);
-    return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
-  }
-
-  CString local_value;
-  // GetBuffer throws when not able to allocate the requested buffer.
-  TCHAR* buffer = local_value.GetBuffer(byte_count / sizeof(TCHAR));
-  res = ::RegQueryValueEx(key,
-                          value_name,
-                          NULL,
-                          NULL,
-                          reinterpret_cast<BYTE*>(buffer),
-                          &byte_count);
-  ::RegCloseKey(key);
-  if (ERROR_SUCCESS == res) {
-    local_value.ReleaseBufferSetLength(byte_count / sizeof(TCHAR));
-    *value = local_value;
-  }
-
-  return HRESULT_FROM_WIN32(res);
-}
-
-// Reads the specified DWORD value from the specified registry key.
-// Only supports value types REG_DWORD.
-// Assumes DWORD is sufficient buffer, which must be true for valid value type.
-HRESULT GetRegDwordValue(bool is_machine_key,
-                         const CString& relative_key_path,
-                         const CString& value_name,
-                         DWORD* value) {
-  if (!value) {
-    return E_INVALIDARG;
-  }
-
-  HKEY root_key = is_machine_key ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
-  HKEY key = NULL;
-  LONG res = ::RegOpenKeyEx(root_key, relative_key_path, 0, KEY_READ, &key);
-  if (res != ERROR_SUCCESS) {
-    return HRESULT_FROM_WIN32(res);
-  }
-
-  DWORD type = 0;
-  DWORD byte_count = sizeof(*value);
-  res = ::RegQueryValueEx(key,
-                          value_name,
-                          NULL,
-                          &type,
-                          reinterpret_cast<BYTE*>(value),
-                          &byte_count);
-  ::RegCloseKey(key);
-  if (ERROR_SUCCESS != res) {
-    return HRESULT_FROM_WIN32(res);
-  }
-  if ((type != REG_DWORD) || (0 == byte_count)) {
-    return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
-  }
-
-  return S_OK;
-}
-
-// Obtains information about the current Omaha installation.
-// Attempts to obtain as much information as possible even if errors occur.
-// Therefore, return values of GetRegStringValue are ignored.
-HRESULT GetOmahaInformation(bool is_machine_app,
-                            CString* omaha_version) {
-  if (!omaha_version) {
-    return E_INVALIDARG;
-  }
-
-  if (FAILED(GetRegStringValue(is_machine_app,
-                               kRelativeClientsGoopdateRegPath,
-                               kRegValueProductVersion,
-                               omaha_version))) {
-    *omaha_version = _T("0.0.0.0");
-  }
-
-  return S_OK;
-}
-
-// Builds the query portion of the recovery url.
-// This method obtains values necessary to build the query that are not provided
-// as parameters.
-// Attempts to build with as much information as possible even if errors occur.
-HRESULT BuildUrlQueryPortion(const CString& app_guid,
-                             const CString& app_version,
-                             const CString& app_language,
-                             bool is_machine_app,
-                             CString* query) {
-  if (!query) {
-    return E_INVALIDARG;
-  }
-
-  CString omaha_version;
-  GetOmahaInformation(is_machine_app, &omaha_version);
-
-  CString os_version;
-  CString os_service_pack;
-  GetOSInfo(&os_version, &os_service_pack);
-
-  // All parameters must be escaped individually before building the query.
-  CString app_guid_escaped;
-  CString app_version_escaped;
-  CString app_language_escaped;
-  CString omaha_version_escaped;
-  CString os_version_escaped;
-  CString os_service_pack_escaped;
-  StringEscape(app_guid, true, &app_guid_escaped);
-  StringEscape(app_version, true, &app_version_escaped);
-  StringEscape(app_language, true, &app_language_escaped);
-  StringEscape(omaha_version, true, &omaha_version_escaped);
-  StringEscape(os_version, true, &os_version_escaped);
-  StringEscape(os_service_pack, true, &os_service_pack_escaped);
-
-  query->Format(kQueryStringFormat,
-                app_guid_escaped,
-                app_version_escaped,
-                app_language_escaped,
-                is_machine_app ? 1 : 0,
-                omaha_version_escaped,
-                os_version_escaped,
-                os_service_pack_escaped);
-
-  return S_OK;
-}
-
-// Returns the full path to save the downloaded file to.
-// The path is based on a unique temporary filename to avoid a conflict
-// between multiple apps downloading to the same location.
-// The path to this file is also returned. The caller is responsible for
-// deleting the temporary file after using the download target path.
-// If it cannot create the unique directory, it attempts to use the user's
-// temporary directory and a constant filename.
-HRESULT GetDownloadTargetPath(CString* download_target_path,
-                              CString* temp_file_path) {
-  if (!download_target_path || !temp_file_path) {
-    return E_INVALIDARG;
-  }
-
-  CString user_temp_dir;
-  HRESULT hr = GetAndCreateTempDir(&user_temp_dir);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = CreateUniqueTempFile(user_temp_dir, temp_file_path);
-  if (SUCCEEDED(hr) && !temp_file_path->IsEmpty()) {
-    *download_target_path = *temp_file_path;
-    // Ignore the return value. A .tmp filename is better than none.
-    download_target_path->Replace(_T(".tmp"), _T(".exe"));
-  } else {
-    // Try a static filename in the temp directory as a fallback.
-    *download_target_path = user_temp_dir + _T("GoogleUpdateSetup.exe");
-    *temp_file_path = _T("");
-  }
-
-  return S_OK;
-}
-
-HRESULT DownloadRepairFile(const CString& download_target_path,
-                           const CString& app_guid,
-                           const CString& app_version,
-                           const CString& app_language,
-                           bool is_machine_app,
-                           DownloadCallback download_callback,
-                           void* context) {
-  CString query;
-  BuildUrlQueryPortion(app_guid,
-                       app_version,
-                       app_language,
-                       is_machine_app,
-                       &query);
-
-  CString url = omaha::kUrlCodeRedCheck + query;
-
-  return download_callback(url, download_target_path, context);
-}
-
-// Makes sure the path is enclosed with double quotation marks.
-void EnclosePath(CString* path) {
-  if (path) {
-    return;
-  }
-
-  if (!path->IsEmpty() && path->GetAt(0) != _T('"')) {
-    path->Insert(0, _T('"'));
-    path->AppendChar(_T('"'));
-  }
-}
-
-HRESULT RunRepairFile(const CString& file_path, bool is_machine_app) {
-  const TCHAR* repair_file_args = is_machine_app ? kMachineRepairArgs :
-                                                   kUserRepairArgs;
-
-  CString command_line(file_path);
-  EnclosePath(&command_line);
-  command_line.AppendChar(_T(' '));
-  command_line.Append(repair_file_args);
-
-  return StartProcess(NULL, command_line.GetBuffer());
-}
-
-}  // namespace
-
-// Verifies the file's integrity and that it is signed by Google.
-// We cannot prevent rollback attacks by using a version because the client
-// may not be able to determine the current version if the files and/or
-// registry entries have been deleted/corrupted.
-// Therefore, we check that the file was signed recently.
-HRESULT VerifyFileSignature(const CString& filename) {
-  // Use Authenticode/WinVerifyTrust to verify the file.
-  // Allow the revocation check to use the network.
-  HRESULT hr = VerifySignature(filename, true);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // Verify that there is a Google certificate.
-  if (!VerifySigneeIsGoogle(filename)) {
-    return CERT_E_CN_NO_MATCH;
-  }
-
-  // Check that the file was signed recently to limit the window for
-  // rollback attacks.
-  return VerifyFileSignedWithinDays(filename, kRollbackWindowDays);
-}
-
-// Verifies the file contains the special markup resource for repair files.
-HRESULT VerifyRepairFileMarkup(const CString& filename) {
-  const TCHAR* kMarkupResourceName = MAKEINTRESOURCE(1);
-  const TCHAR* kMarkupResourceType = _T("GOOGLEUPDATEREPAIR");
-  const DWORD kMarkupResourceExpectedValue = 1;
-
-  scoped_library module(::LoadLibraryEx(filename, 0, LOAD_LIBRARY_AS_DATAFILE));
-  if (!module) {
-    return HRESULT_FROM_WIN32(::GetLastError());
-  }
-
-  HRSRC resource(::FindResource(get(module),
-                                kMarkupResourceName,
-                                kMarkupResourceType));
-  if (!resource) {
-    return HRESULT_FROM_WIN32(::GetLastError());
-  }
-
-  if (sizeof(kMarkupResourceExpectedValue) !=
-      ::SizeofResource(get(module), resource)) {
-    return E_UNEXPECTED;
-  }
-
-  HGLOBAL loaded_resource(::LoadResource(get(module), resource));
-  if (!loaded_resource) {
-    return HRESULT_FROM_WIN32(::GetLastError());
-  }
-
-  const DWORD* value = static_cast<DWORD*>(::LockResource(loaded_resource));
-  if (!value) {
-    return E_HANDLE;
-  }
-
-  if (kMarkupResourceExpectedValue != *value) {
-    return E_UNEXPECTED;
-  }
-
-  return S_OK;
-}
-
-// Verifies the filename is not UNC name, the file exists, has a valid signature
-// chain, is signed by Google, and contains the special markup resource for
-// repair files.
-HRESULT VerifyIsValidRepairFile(const CString& filename) {
-  // Make sure file exists.
-  if (!::PathFileExists(filename)) {
-    return HRESULT_FROM_WIN32(::GetLastError());
-  }
-
-  HRESULT hr = VerifyFileSignature(filename);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return VerifyRepairFileMarkup(filename);
-}
-
-}  // namespace omaha
-
-// If a repair file is run, the file will not be deleted until reboot. Delete
-// after reboot will only succeed when executed by an admin or LocalSystem.
-// Returns HRESULT_FROM_WIN32(ERROR_ACCESS_DISABLED_BY_POLICY) if automatic
-// update checks are disabled.
-HRESULT FixGoogleUpdate(const TCHAR* app_guid,
-                        const TCHAR* app_version,
-                        const TCHAR* app_language,
-                        bool is_machine_app,
-                        DownloadCallback download_callback,
-                        void* context) {
-  if (!app_guid || !app_version || !app_language || !download_callback) {
-    return E_INVALIDARG;
-  }
-
-  DWORD update_check_period_override_minutes(UINT_MAX);
-  HRESULT hr = omaha::GetRegDwordValue(
-                   true,
-                   GOOPDATE_POLICIES_RELATIVE,
-                   omaha::kRegValueAutoUpdateCheckPeriodOverrideMinutes,
-                   &update_check_period_override_minutes);
-  if (SUCCEEDED(hr) && (0 == update_check_period_override_minutes)) {
-    return HRESULT_FROM_WIN32(ERROR_ACCESS_DISABLED_BY_POLICY);
-  }
-
-  CString download_target_path;
-  CString temp_file_path;
-  hr = omaha::GetDownloadTargetPath(&download_target_path, &temp_file_path);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  if (download_target_path.IsEmpty()) {
-    hr = E_FAIL;
-  }
-
-  // After calling DownloadRepairFile, don't return until the repair file and
-  // temp file have been deleted.
-  hr = omaha::DownloadRepairFile(download_target_path,
-                                 app_guid,
-                                 app_version,
-                                 app_language,
-                                 is_machine_app,
-                                 download_callback,
-                                 context);
-
-  if (SUCCEEDED(hr)) {
-    hr = omaha::VerifyIsValidRepairFile(download_target_path);
-  }
-
-  if (FAILED(hr)) {
-    ::DeleteFile(download_target_path);
-    ::DeleteFile(temp_file_path);
-    return hr;
-  }
-
-  hr = omaha::RunRepairFile(download_target_path, is_machine_app);
-  ::MoveFileEx(download_target_path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
-  ::DeleteFile(temp_file_path);
-
-  return hr;
-}
diff --git a/common/google_update_recovery.h b/common/google_update_recovery.h
deleted file mode 100644
index a096c4e..0000000
--- a/common/google_update_recovery.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// Defines the interface to the Google Update recovery mechanism to be included
-// in Google apps.
-
-#ifndef OMAHA_COMMON_GOOGLE_UPDATE_RECOVERY_H__
-#define OMAHA_COMMON_GOOGLE_UPDATE_RECOVERY_H__
-
-#include <tchar.h>
-#include <windows.h>
-
-#ifndef UNICODE
-#error The distributed library only supports UNICODE.
-#endif
-
-extern "C" {
-typedef HRESULT (*DownloadCallback)(const TCHAR* url,
-                                    const TCHAR* file_path,
-                                    void* context);
-
-// Determines whether there is a Code Red event for the current installation
-// and repairs it if necessary.
-// app_language should follow the external Internet standard
-// Best Common Practice (BCP) 47: http://www.rfc-editor.org/rfc/bcp/bcp47.txt
-// context can be NULL if download_callback does not use it.
-HRESULT FixGoogleUpdate(const TCHAR* app_guid,
-                        const TCHAR* app_version,
-                        const TCHAR* app_language,
-                        bool is_machine_app,
-                        DownloadCallback download_callback,
-                        void* context);
-}  // extern "C"
-
-#endif  // OMAHA_COMMON_GOOGLE_UPDATE_RECOVERY_H__
diff --git a/common/google_update_recovery_unittest.cc b/common/google_update_recovery_unittest.cc
deleted file mode 100644
index 8f54ffa..0000000
--- a/common/google_update_recovery_unittest.cc
+++ /dev/null
@@ -1,944 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// Unit tests for the Google Update recovery mechanism.
-// All apps that are using the mechanism must also run this test.
-//
-// Unlike the mechanism code, this code relies on code from common because it
-// makes writing the tests much simpler and size is not a concern.
-
-#include <windows.h>
-#include <atlstr.h>
-#include "omaha/common/app_util.h"
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/google_update_recovery.h"
-#include "omaha/common/path.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/signaturevalidator.h"
-#include "omaha/common/system_info.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/enterprise/const_group_policy.h"
-#include "omaha/net/browser_request.h"
-#include "omaha/net/network_config.h"
-#include "omaha/net/network_request.h"
-#include "omaha/net/simple_request.h"
-#include "omaha/third_party/gtest/include/gtest/gtest.h"
-
-// TODO(omaha): Replicate some of these tests in signaturevalidator_unittest.cc.
-
-// As of Google Test 1.4.0, expressions get converted to 'bool', resulting in
-// "warning C4800: 'BOOL' : forcing value to bool 'true' or 'false' (performance
-// warning)" in some uses.
-// These must be kept in sync with gtest.h.
-// TODO(omaha): Try to get this fixed in Google Test.
-#undef EXPECT_TRUE
-#define EXPECT_TRUE(condition) \
-  GTEST_TEST_BOOLEAN_(!!(condition), #condition, false, true, \
-                      GTEST_NONFATAL_FAILURE_)
-#undef ASSERT_TRUE
-#define ASSERT_TRUE(condition) \
-  GTEST_TEST_BOOLEAN_(!!(condition), #condition, false, true, \
-                      GTEST_FATAL_FAILURE_)
-
-namespace omaha {
-
-namespace {
-
-const TCHAR kDummyAppGuid[] = _T("{8E472B0D-3E8B-43b1-B89A-E8506AAF1F16}");
-const TCHAR kDummyAppVersion[] = _T("3.4.5.6");
-const TCHAR kDummyAppLang[] = _T("en-us");
-
-const TCHAR kTempDirectory[] = _T("C:\\WINDOWS\\Temp");
-
-const TCHAR kFullMachineOmahaMainKeyPath[] =
-    _T("HKLM\\Software\\Google\\Update\\");
-const TCHAR kFullUserOmahaMainKeyPath[] =
-    _T("HKCU\\Software\\Google\\Update\\");
-const TCHAR kFullMachineOmahaClientKeyPath[] =
-    _T("HKLM\\Software\\Google\\Update\\Clients\\")
-    _T("{430FD4D0-B729-4f61-AA34-91526481799D}");
-const TCHAR kFullUserOmahaClientKeyPath[] =
-    _T("HKCU\\Software\\Google\\Update\\Clients\\")
-    _T("{430FD4D0-B729-4f61-AA34-91526481799D}");
-
-const HRESULT kDummyNoFileError = 0x80041234;
-
-const TCHAR kArgumentSavingExecutableRelativePath[] =
-    _T("unittest_support\\SaveArguments.exe");
-const TCHAR kSavedArgumentsFileName[] = _T("saved_arguments.txt");
-const TCHAR* const kInvalidFileAddress = _T("http://www.google.com/robots.txt");
-
-#define MACHINE_KEY_NAME _T("HKLM")
-#define MACHINE_KEY MACHINE_KEY_NAME _T("\\")
-#define USER_KEY_NAME _T("HKCU")
-#define USER_KEY USER_KEY_NAME _T("\\")
-
-// These methods were copied from omaha/testing/omaha_unittest.cpp.
-const TCHAR kRegistryHiveOverrideRoot[] =
-    _T("HKCU\\Software\\Google\\Update\\UnitTest\\");
-
-const TCHAR kExpectedUrlForDummyAppAndNoOmahaValues[] = _T("http://cr-tools.clients.google.com/service/check2?appid=%7B8E472B0D-3E8B-43b1-B89A-E8506AAF1F16%7D&appversion=3.4.5.6&applang=en-us&machine=1&version=0.0.0.0&osversion=");  // NOLINT
-const int kExpectedUrlForDummyAppAndNoOmahaValuesLength =
-    arraysize(kExpectedUrlForDummyAppAndNoOmahaValues) - 1;
-
-// Overrides the HKLM and HKCU registry hives so that accesses go to the
-// specified registry key instead.
-// This method is most often used in SetUp().
-void OverrideRegistryHives(const CString& hive_override_key_name) {
-  // Override the destinations of HKLM and HKCU to use a special location
-  // for the unit tests so that we don't disturb the actual Omaha state.
-  RegKey machine_key;
-  RegKey user_key;
-  EXPECT_HRESULT_SUCCEEDED(
-      machine_key.Create(hive_override_key_name + MACHINE_KEY));
-  EXPECT_HRESULT_SUCCEEDED(user_key.Create(hive_override_key_name + USER_KEY));
-  EXPECT_HRESULT_SUCCEEDED(::RegOverridePredefKey(HKEY_LOCAL_MACHINE,
-                                                  machine_key.Key()));
-  EXPECT_HRESULT_SUCCEEDED(::RegOverridePredefKey(HKEY_CURRENT_USER,
-                                                  user_key.Key()));
-}
-
-// Restores HKLM and HKCU registry accesses to the real hives.
-// This method is most often used in TearDown().
-void RestoreRegistryHives() {
-  EXPECT_HRESULT_SUCCEEDED(::RegOverridePredefKey(HKEY_LOCAL_MACHINE, NULL));
-  EXPECT_HRESULT_SUCCEEDED(::RegOverridePredefKey(HKEY_CURRENT_USER, NULL));
-}
-
-CString GetTmp() {
-  TCHAR temp_dir[MAX_PATH] = {0};
-  EXPECT_NE(0, ::GetEnvironmentVariable(_T("TMP"), temp_dir, MAX_PATH));
-  return temp_dir;
-}
-
-}  // namespace
-
-HRESULT VerifyFileSignature(const CString& filename);
-HRESULT VerifyRepairFileMarkup(const CString& filename);
-
-class GoogleUpdateRecoveryTest : public testing::Test {
- public:
-  static void set_saved_url(const CString& saved_url) {
-    saved_url_ = saved_url;
-  }
-
-  static void set_saved_file_path(const CString& saved_file_path) {
-    saved_file_path_ = saved_file_path;
-  }
-
-  static void set_saved_context(void* context) {
-    saved_context_ = context;
-  }
-
- protected:
-  GoogleUpdateRecoveryTest() {
-    saved_url_.Empty();
-    saved_file_path_.Empty();
-    saved_context_ = NULL;
-  }
-
-  virtual void SetUp() {
-  }
-
-  virtual void TearDown() {
-  }
-
-  void CheckSavedUrlOSFragment() {
-    const TCHAR kExpectedOSXpSp2[] = _T("5.1&servicepack=Service%20Pack%202");
-    const TCHAR kExpectedOSXpSp3[] = _T("5.1&servicepack=Service%20Pack%203");
-    const TCHAR kExpectedOS2003Sp1[] = _T("5.2&servicepack=Service%20Pack%201");
-    const TCHAR kExpectedOS2003Sp2[] = _T("5.2&servicepack=Service%20Pack%202");
-    const TCHAR kExpectedOSVistaRtm[] = _T("6.0&servicepack=");
-    const TCHAR kExpectedOSVistaSp1[] =
-        _T("6.0&servicepack=Service%20Pack%201");
-
-    EXPECT_TRUE((kExpectedOSXpSp2 ==
-                 saved_url_.Right(arraysize(kExpectedOSXpSp2) - 1)) ||
-                (kExpectedOSXpSp3 ==
-                 saved_url_.Right(arraysize(kExpectedOSXpSp3) - 1)) ||
-                (kExpectedOS2003Sp1 ==
-                 saved_url_.Right(arraysize(kExpectedOS2003Sp1) - 1)) ||
-                (kExpectedOS2003Sp2 ==
-                 saved_url_.Right(arraysize(kExpectedOS2003Sp2) - 1)) ||
-                (kExpectedOSVistaRtm ==
-                 saved_url_.Right(arraysize(kExpectedOSVistaRtm) - 1)) ||
-                (kExpectedOSVistaSp1 ==
-                 saved_url_.Right(arraysize(kExpectedOSVistaSp1) - 1)));
-  }
-
-  void VerifySavedArgumentsFile(const CString& expected_string) {
-    CString saved_arguments_path = ConcatenatePath(
-                                       GetDirectoryFromPath(saved_file_path_),
-                                       kSavedArgumentsFileName);
-    bool is_found = false;
-    for (int tries = 0; tries < 100 && !is_found; ++tries) {
-      ::Sleep(50);
-      is_found = File::Exists(saved_arguments_path);
-    }
-    EXPECT_TRUE(is_found);
-
-    scoped_hfile file(::CreateFile(saved_arguments_path,
-                                   GENERIC_READ,
-                                   0,                     // do not share
-                                   NULL,                  // default security
-                                   OPEN_EXISTING,         // existing file only
-                                   FILE_ATTRIBUTE_NORMAL,
-                                   NULL));                // no template
-    EXPECT_NE(INVALID_HANDLE_VALUE, get(file));
-
-    const int kBufferLen = 50;
-    TCHAR buffer[kBufferLen + 1] = {0};
-    DWORD bytes_read = 0;
-
-    EXPECT_TRUE(::ReadFile(get(file),
-                           buffer,
-                           kBufferLen * sizeof(TCHAR),
-                           &bytes_read,
-                           NULL));
-    EXPECT_EQ(0, bytes_read % sizeof(TCHAR));
-    buffer[bytes_read / sizeof(TCHAR)] = _T('\0');
-
-    EXPECT_STREQ(expected_string, buffer);
-  }
-
-  void VerifyExpectedSavedFilePath(const CString& expected_temp_directory) {
-    const int kMaxUniqueChars = 4;
-    const CString expected_path_part_a = expected_temp_directory + _T("\\GUR");
-    const CString expected_path_part_b = _T(".exe");
-    EXPECT_STREQ(expected_path_part_a,
-                 saved_file_path_.Left(expected_path_part_a.GetLength()));
-    EXPECT_STREQ(expected_path_part_b,
-                 saved_file_path_.Right(expected_path_part_b.GetLength()));
-    const int constant_chars = expected_path_part_a.GetLength() +
-                               expected_path_part_b.GetLength();
-    EXPECT_GT(saved_file_path_.GetLength(), constant_chars);
-    EXPECT_LE(saved_file_path_.GetLength(), constant_chars + kMaxUniqueChars);
-  }
-
-  static CString saved_url_;
-  static CString saved_file_path_;
-  static void* saved_context_;
-
- protected:
-  // Copies SaveArguments.exe to the specified location.
-  static HRESULT DownloadArgumentSavingFile(const TCHAR* url,
-                                            const TCHAR* file_path,
-                                            void* context) {
-    ASSERT1(url);
-    ASSERT1(file_path);
-
-    GoogleUpdateRecoveryTest::set_saved_url(url);
-    GoogleUpdateRecoveryTest::set_saved_file_path(file_path);
-    GoogleUpdateRecoveryTest::set_saved_context(context);
-
-    CString executable_full_path(app_util::GetCurrentModuleDirectory());
-    VERIFY1(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
-                         kArgumentSavingExecutableRelativePath));
-
-    if (!::CopyFile(executable_full_path, file_path, false)) {
-      HRESULT hr = HRESULTFromLastError();
-      return hr;
-    }
-
-    return S_OK;
-  }
-
-  // Returns kDummyNoFileError, simulating no file to download.
-  static HRESULT DownloadFileNoFile(const TCHAR* url,
-                                    const TCHAR* file_path,
-                                    void* context) {
-    ASSERT1(url);
-    ASSERT1(file_path);
-
-    GoogleUpdateRecoveryTest::set_saved_url(url);
-    GoogleUpdateRecoveryTest::set_saved_file_path(file_path);
-    GoogleUpdateRecoveryTest::set_saved_context(context);
-
-    return kDummyNoFileError;
-  }
-
-  // Overrides the address to cause a file to be downloaded via HTTP.
-  // Uses a real HTTP stack, so it is similar to a real implementation.
-  // The file is invalid, so signature verification should return
-  // TRUST_E_SUBJECT_FORM_UNKNOWN.
-  static HRESULT DownloadFileInvalidFile(const TCHAR* url,
-                                         const TCHAR* file_path,
-                                         void* context) {
-    ASSERT1(url);
-    CString test_url(url);
-
-    VERIFY1(1 == test_url.Replace(kUrlCodeRedCheck, kInvalidFileAddress));
-
-    return DownloadFileFromServer(test_url, file_path, context);
-  }
-
-  // Uses a real HTTP stack, so it is similar to a real implementation.
-  static HRESULT DownloadFileFromServer(const TCHAR* url,
-                                        const TCHAR* file_path,
-                                        void* context) {
-    UTIL_LOG(L2, (_T("[DownloadFileFromServer][%s][%s]"), url, file_path));
-
-    ASSERT1(url);
-    ASSERT1(file_path);
-
-    GoogleUpdateRecoveryTest::set_saved_url(url);
-    GoogleUpdateRecoveryTest::set_saved_file_path(file_path);
-    GoogleUpdateRecoveryTest::set_saved_context(context);
-
-    NetworkConfig& network_config = NetworkConfig::Instance();
-    ON_SCOPE_EXIT_OBJ(network_config, &NetworkConfig::Clear);
-
-    network_config.Clear();
-    network_config.Add(new GoogleProxyDetector(MACHINE_REG_UPDATE_DEV));
-    network_config.Add(new FirefoxProxyDetector());
-    network_config.Add(new IEProxyDetector());
-
-    NetworkRequest network_request(network_config.session());
-    network_request.AddHttpRequest(new SimpleRequest);
-    network_request.AddHttpRequest(new BrowserRequest);
-
-    HRESULT hr = network_request.DownloadFile(url, CString(file_path));
-    if (FAILED(hr)) {
-      UTIL_LOG(LE, (_T("[DownloadFile failed][%s][0x%08x]"), url, hr));
-      return hr;
-    }
-
-    int status_code = network_request.http_status_code();
-    UTIL_LOG(L2, (_T("[HTTP status][%u]"), status_code));
-
-    if (HTTP_STATUS_OK == status_code) {
-      return S_OK;
-    } else if (HTTP_STATUS_NO_CONTENT == status_code) {
-      return kDummyNoFileError;
-    } else {
-      // Apps would not have this assumption.
-      ASSERT(false, (_T("Status code %i received. Expected 200 or 204."),
-                     status_code));
-      return E_FAIL;
-    }
-  }
-};
-
-CString GoogleUpdateRecoveryTest::saved_url_;
-CString GoogleUpdateRecoveryTest::saved_file_path_;
-void* GoogleUpdateRecoveryTest::saved_context_;
-
-class GoogleUpdateRecoveryRegistryProtectedTest
-    : public GoogleUpdateRecoveryTest {
- protected:
-  GoogleUpdateRecoveryRegistryProtectedTest()
-      : hive_override_key_name_(kRegistryHiveOverrideRoot) {
-  }
-
-  CString hive_override_key_name_;
-
-  virtual void SetUp() {
-    GoogleUpdateRecoveryTest::SetUp();
-    RegKey::DeleteKey(hive_override_key_name_, true);
-    OverrideRegistryHives(hive_override_key_name_);
-  }
-
-  virtual void TearDown() {
-    RestoreRegistryHives();
-    EXPECT_HRESULT_SUCCEEDED(RegKey::DeleteKey(hive_override_key_name_, true));
-    GoogleUpdateRecoveryTest::TearDown();
-  }
-};
-
-//
-// FixGoogleUpdate Tests
-//
-
-TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_UseRealHttpClient) {
-  EXPECT_EQ(TRUST_E_SUBJECT_FORM_UNKNOWN,
-            FixGoogleUpdate(kDummyAppGuid,
-                            kDummyAppVersion,
-                            kDummyAppLang,
-                            true,
-                            DownloadFileInvalidFile,
-                            NULL));
-}
-
-TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_FileReturned_Machine) {
-  CString saved_arguments_path = ConcatenatePath(app_util::GetTempDir(),
-                                                 kSavedArgumentsFileName);
-
-  ::DeleteFile(saved_arguments_path);
-  EXPECT_FALSE(File::Exists(saved_arguments_path));
-
-  CString context_string(_T("some context"));
-  EXPECT_HRESULT_SUCCEEDED(FixGoogleUpdate(kDummyAppGuid,
-                                           kDummyAppVersion,
-                                           kDummyAppLang,
-                                           true,
-                                           DownloadArgumentSavingFile,
-                                           &context_string));
-
-  EXPECT_EQ(&context_string, saved_context_);
-  EXPECT_STREQ(_T("some context"), *static_cast<CString*>(saved_context_));
-
-  ::Sleep(200);
-  EXPECT_TRUE(File::Exists(saved_file_path_));
-  VerifySavedArgumentsFile(_T("/recover /machine"));
-
-  EXPECT_TRUE(::DeleteFile(saved_file_path_));
-  EXPECT_TRUE(::DeleteFile(saved_arguments_path));
-}
-
-TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_FileReturned_User) {
-  CString saved_arguments_path = ConcatenatePath(app_util::GetTempDir(),
-                                                 kSavedArgumentsFileName);
-
-  ::DeleteFile(saved_arguments_path);
-  EXPECT_FALSE(File::Exists(saved_arguments_path));
-
-  CString context_string(_T("more context"));
-  EXPECT_HRESULT_SUCCEEDED(FixGoogleUpdate(kDummyAppGuid,
-                                           kDummyAppVersion,
-                                           kDummyAppLang,
-                                           false,
-                                           DownloadArgumentSavingFile,
-                                           &context_string));
-
-  EXPECT_EQ(&context_string, saved_context_);
-  EXPECT_STREQ(_T("more context"), *static_cast<CString*>(saved_context_));
-
-  ::Sleep(200);
-  EXPECT_TRUE(File::Exists(saved_file_path_));
-  VerifySavedArgumentsFile(_T("/recover"));
-
-  EXPECT_TRUE(::DeleteFile(saved_file_path_));
-  EXPECT_TRUE(::DeleteFile(saved_arguments_path));
-}
-
-TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_NoFile_Machine) {
-  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
-                                               kDummyAppVersion,
-                                               kDummyAppLang,
-                                               true,
-                                               DownloadFileNoFile,
-                                               NULL));
-
-  EXPECT_EQ(static_cast<void*>(NULL), saved_context_);
-  EXPECT_FALSE(File::Exists(saved_file_path_));
-
-  TCHAR temp_dir[MAX_PATH] = {0};
-  EXPECT_TRUE(::GetEnvironmentVariable(_T("TMP"), temp_dir, MAX_PATH));
-  EXPECT_TRUE(File::Exists(temp_dir))
-      << _T("The temp directory was deleted or not created.");
-}
-
-TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_NoFile_User) {
-  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
-                                               kDummyAppVersion,
-                                               kDummyAppLang,
-                                               false,
-                                               DownloadFileNoFile,
-                                               NULL));
-
-  EXPECT_EQ(static_cast<void*>(NULL), saved_context_);
-  EXPECT_FALSE(File::Exists(saved_file_path_));
-
-  TCHAR temp_dir[MAX_PATH] = {0};
-  EXPECT_TRUE(::GetEnvironmentVariable(_T("TMP"), temp_dir, MAX_PATH));
-  EXPECT_TRUE(File::Exists(temp_dir))
-      << _T("The temp directory was deleted or not created.");
-}
-
-TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
-       FixGoogleUpdate_AllValues_MachineApp) {
-  const TCHAR kExpectedUrl[] = _T("http://cr-tools.clients.google.com/service/check2?appid=%7B8E472B0D-3E8B-43b1-B89A-E8506AAF1F16%7D&appversion=3.4.5.6&applang=en-us&machine=1&version=5.6.78.1&osversion=");  // NOLINT
-
-  const CString prev_tmp = GetTmp();
-  EXPECT_TRUE(::SetEnvironmentVariable(_T("TMP"), kTempDirectory));
-
-  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kFullMachineOmahaClientKeyPath,
-                                            _T("pv"),
-                                            _T("5.6.78.1")));
-
-  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
-                                               kDummyAppVersion,
-                                               kDummyAppLang,
-                                               true,
-                                               DownloadFileNoFile,
-                                               NULL));
-
-  EXPECT_STREQ(kExpectedUrl, saved_url_.Left(arraysize(kExpectedUrl) - 1));
-  CheckSavedUrlOSFragment();
-  VerifyExpectedSavedFilePath(kTempDirectory);
-
-  EXPECT_TRUE(::SetEnvironmentVariable(_T("TMP"), prev_tmp));
-}
-
-TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
-       FixGoogleUpdate_AllValues_UserApp) {
-  const TCHAR kExpectedUrl[] = _T("http://cr-tools.clients.google.com/service/check2?appid=%7B8E472B0D-3E8B-43b1-B89A-E8506AAF1F16%7D&appversion=3.4.5.6&applang=en-us&machine=0&version=5.6.78.1&osversion=");  // NOLINT
-
-  const CString prev_tmp = GetTmp();
-  EXPECT_TRUE(::SetEnvironmentVariable(_T("TMP"), kTempDirectory));
-
-  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kFullUserOmahaClientKeyPath,
-                                            _T("pv"),
-                                            _T("5.6.78.1")));
-
-  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
-                                               kDummyAppVersion,
-                                               kDummyAppLang,
-                                               false,
-                                               DownloadFileNoFile,
-                                               NULL));
-
-  EXPECT_STREQ(kExpectedUrl, saved_url_.Left(arraysize(kExpectedUrl) - 1));
-  CheckSavedUrlOSFragment();
-  VerifyExpectedSavedFilePath(kTempDirectory);
-
-  EXPECT_TRUE(::SetEnvironmentVariable(_T("TMP"), prev_tmp));
-}
-
-TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
-       FixGoogleUpdate_NoOmahaRegKeys) {
-  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
-                                               kDummyAppVersion,
-                                               kDummyAppLang,
-                                               true,
-                                               DownloadFileNoFile,
-                                               NULL));
-  EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues,
-               saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength));
-  CheckSavedUrlOSFragment();
-}
-
-TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
-       FixGoogleUpdate_EmptyAppInfo) {
-  const TCHAR kExpectedUrl[] = _T("http://cr-tools.clients.google.com/service/check2?appid=&appversion=&applang=&machine=1&version=0.0.0.0&osversion=");  // NOLINT
-
-  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(_T(""),
-                                               _T(""),
-                                               _T(""),
-                                               true,
-                                               DownloadFileNoFile,
-                                               NULL));
-  EXPECT_STREQ(kExpectedUrl, saved_url_.Left(arraysize(kExpectedUrl) - 1));
-  CheckSavedUrlOSFragment();
-}
-
-TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
-       FixGoogleUpdate_NullArgs) {
-  EXPECT_EQ(E_INVALIDARG, FixGoogleUpdate(NULL,
-                                          _T(""),
-                                          _T(""),
-                                          true,
-                                          DownloadFileNoFile,
-                                          NULL));
-  EXPECT_EQ(E_INVALIDARG, FixGoogleUpdate(_T(""),
-                                          NULL,
-                                          _T(""),
-                                          true,
-                                          DownloadFileNoFile,
-                                          NULL));
-  EXPECT_EQ(E_INVALIDARG, FixGoogleUpdate(_T(""),
-                                          _T(""),
-                                          NULL,
-                                          true,
-                                          DownloadFileNoFile,
-                                          NULL));
-  EXPECT_EQ(E_INVALIDARG, FixGoogleUpdate(_T(""),
-                                          _T(""),
-                                          _T(""),
-                                          true,
-                                          NULL,
-                                          NULL));
-}
-
-// Setting kRegValueAutoUpdateCheckPeriodOverrideMinutes to zero disables
-// Code Red checks just as it does regular update checks.
-TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
-       FixGoogleUpdate_AutoUpdateCheckPeriodMinutesIsZeroDword) {
-  EXPECT_HRESULT_SUCCEEDED(
-      RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
-                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
-                       static_cast<DWORD>(0)));
-
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_ACCESS_DISABLED_BY_POLICY),
-            FixGoogleUpdate(kDummyAppGuid,
-                            kDummyAppVersion,
-                            kDummyAppLang,
-                            true,
-                            DownloadFileNoFile,
-                            NULL));
-  EXPECT_TRUE(saved_url_.IsEmpty());
-}
-
-TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
-       FixGoogleUpdate_AutoUpdateCheckPeriodMinutesIsZeroDwordInHkcu) {
-  EXPECT_HRESULT_SUCCEEDED(
-      RegKey::SetValue(USER_KEY GOOPDATE_POLICIES_RELATIVE,
-                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
-                       static_cast<DWORD>(0)));
-
-  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
-                                               kDummyAppVersion,
-                                               kDummyAppLang,
-                                               true,
-                                               DownloadFileNoFile,
-                                               NULL));
-  EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues,
-               saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength));
-  CheckSavedUrlOSFragment();
-}
-
-TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
-       FixGoogleUpdate_AutoUpdateCheckPeriodMinutesIsNonZeroDword) {
-  EXPECT_HRESULT_SUCCEEDED(
-      RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
-                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
-                       static_cast<DWORD>(1400)));
-
-  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
-                                               kDummyAppVersion,
-                                               kDummyAppLang,
-                                               true,
-                                               DownloadFileNoFile,
-                                               NULL));
-  EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues,
-               saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength));
-  CheckSavedUrlOSFragment();
-}
-
-TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
-       FixGoogleUpdate_AutoUpdateCheckPeriodMinutesIsZeroDword64) {
-  EXPECT_HRESULT_SUCCEEDED(
-      RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
-                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
-                       static_cast<DWORD64>(0)));
-
-  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
-                                               kDummyAppVersion,
-                                               kDummyAppLang,
-                                               true,
-                                               DownloadFileNoFile,
-                                               NULL));
-  EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues,
-               saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength));
-  CheckSavedUrlOSFragment();
-}
-
-TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
-       FixGoogleUpdate_AutoUpdateCheckPeriodMinutesIsNonZeroDword64) {
-  EXPECT_HRESULT_SUCCEEDED(
-      RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
-                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
-                       static_cast<DWORD64>(1400)));
-
-  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
-                                               kDummyAppVersion,
-                                               kDummyAppLang,
-                                               true,
-                                               DownloadFileNoFile,
-                                               NULL));
-  EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues,
-               saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength));
-  CheckSavedUrlOSFragment();
-}
-
-TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
-       FixGoogleUpdate_AutoUpdateCheckPeriodMinutesIsZeroAsString) {
-  EXPECT_HRESULT_SUCCEEDED(
-      RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
-                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
-                       _T("0")));
-
-  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
-                                               kDummyAppVersion,
-                                               kDummyAppLang,
-                                               true,
-                                               DownloadFileNoFile,
-                                               NULL));
-  EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues,
-               saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength));
-  CheckSavedUrlOSFragment();
-}
-
-TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
-       FixGoogleUpdate_AutoUpdateCheckPeriodMinutesIsZeroAsBinary) {
-  const byte zero = 0;
-  EXPECT_HRESULT_SUCCEEDED(
-      RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
-                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
-                       &zero,
-                       sizeof(zero)));
-
-  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
-                                               kDummyAppVersion,
-                                               kDummyAppLang,
-                                               true,
-                                               DownloadFileNoFile,
-                                               NULL));
-  EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues,
-               saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength));
-  CheckSavedUrlOSFragment();
-}
-
-TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
-       FixGoogleUpdate_GroupPolicyKeyExistsButNoAutoUpdateCheckPeriodMinutes) {
-  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey(kRegKeyGoopdateGroupPolicy));
-
-  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
-                                               kDummyAppVersion,
-                                               kDummyAppLang,
-                                               true,
-                                               DownloadFileNoFile,
-                                               NULL));
-  EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues,
-               saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength));
-  CheckSavedUrlOSFragment();
-}
-
-// Verifies that the file is saved even if the temp directory doesn't exist.
-TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_SaveToNonExistantDirectory) {
-  const TCHAR kNonExistantDirectory[] = _T("c:\\directory_does_not_exist");
-  DeleteDirectory(kNonExistantDirectory);
-  EXPECT_FALSE(File::Exists(kNonExistantDirectory));
-
-  const CString prev_tmp = GetTmp();
-  EXPECT_TRUE(::SetEnvironmentVariable(_T("TMP"), kNonExistantDirectory));
-
-  EXPECT_EQ(TRUST_E_SUBJECT_FORM_UNKNOWN,
-            FixGoogleUpdate(kDummyAppGuid,
-                            kDummyAppVersion,
-                            kDummyAppLang,
-                            true,
-                            DownloadFileInvalidFile,
-                            NULL));
-
-  VerifyExpectedSavedFilePath(kNonExistantDirectory);
-
-  EXPECT_TRUE(::SetEnvironmentVariable(_T("TMP"), prev_tmp));
-  EXPECT_HRESULT_SUCCEEDED(DeleteDirectory(kNonExistantDirectory));
-}
-
-TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_FileCollision) {
-  const CString prev_tmp = GetTmp();
-  EXPECT_TRUE(::SetEnvironmentVariable(_T("TMP"), kTempDirectory));
-
-  CString saved_arguments_path = ConcatenatePath(app_util::GetTempDir(),
-                                                 kSavedArgumentsFileName);
-
-  EXPECT_HRESULT_SUCCEEDED(FixGoogleUpdate(kDummyAppGuid,
-                                           kDummyAppVersion,
-                                           kDummyAppLang,
-                                           false,
-                                           DownloadArgumentSavingFile,
-                                           NULL));
-
-  EXPECT_TRUE(File::Exists(saved_file_path_));
-  VerifyExpectedSavedFilePath(kTempDirectory);
-
-  CString first_saved_file_path = saved_file_path_;
-
-  // Ensure that the first downloaded file is in use.
-  FileLock lock;
-  EXPECT_HRESULT_SUCCEEDED(lock.Lock(first_saved_file_path));
-
-  EXPECT_HRESULT_SUCCEEDED(FixGoogleUpdate(kDummyAppGuid,
-                                           kDummyAppVersion,
-                                           kDummyAppLang,
-                                           false,
-                                           DownloadArgumentSavingFile,
-                                           NULL));
-  EXPECT_TRUE(File::Exists(saved_file_path_));
-  VerifyExpectedSavedFilePath(kTempDirectory);
-
-  EXPECT_STRNE(first_saved_file_path, saved_file_path_);
-
-  EXPECT_HRESULT_SUCCEEDED(lock.Unlock());
-
-  bool is_deleted = false;
-  for (int tries = 0; tries < 100 && !is_deleted; ++tries) {
-    ::Sleep(50);
-    is_deleted = !!::DeleteFile(saved_file_path_);
-  }
-  EXPECT_TRUE(is_deleted);
-
-  EXPECT_TRUE(::DeleteFile(first_saved_file_path));
-  EXPECT_TRUE(::DeleteFile(saved_arguments_path));
-
-  EXPECT_TRUE(::SetEnvironmentVariable(_T("TMP"), prev_tmp));
-}
-
-//
-// VerifyFileSignature Tests
-//
-TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_SignedValid) {
-  CString executable_full_path(app_util::GetCurrentModuleDirectory());
-  EXPECT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
-                           kArgumentSavingExecutableRelativePath));
-  EXPECT_TRUE(File::Exists(executable_full_path));
-  EXPECT_HRESULT_SUCCEEDED(VerifyFileSignature(executable_full_path));
-}
-
-TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_NotSigned) {
-  const TCHAR kUnsignedExecutable[] = _T("GoogleUpdate_unsigned.exe");
-
-  CString executable_full_path(app_util::GetCurrentModuleDirectory());
-  EXPECT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
-                           kUnsignedExecutable));
-  EXPECT_TRUE(File::Exists(executable_full_path));
-  EXPECT_EQ(TRUST_E_NOSIGNATURE, VerifyFileSignature(executable_full_path));
-}
-
-// The certificate is still valid, but the executable was signed more than N
-// days ago.
-TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_SignedOldWithValidCert) {
-  const TCHAR kRelativePath[] =
-      _T("unittest_support\\GoogleUpdate_old_signature.exe");
-
-  CString executable_full_path(app_util::GetCurrentModuleDirectory());
-  EXPECT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
-                           kRelativePath));
-  EXPECT_TRUE(File::Exists(executable_full_path));
-  EXPECT_EQ(TRUST_E_TIME_STAMP, VerifyFileSignature(executable_full_path));
-}
-
-// The certificate was valid when it was used to sign the executable, but it has
-// since expired.
-// TRUST_E_TIME_STAMP is returned because the file was signed more than the
-// allowable number of dates ago for the repair file. Otherwise, the signature
-// is fine.
-TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_SignedWithNowExpiredCert) {
-  const TCHAR kRelativePath[] =
-      _T("unittest_support\\GoogleUpdate_now_expired_cert.exe");
-
-  CString executable_full_path(app_util::GetCurrentModuleDirectory());
-  EXPECT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
-                           kRelativePath));
-  EXPECT_TRUE(File::Exists(executable_full_path));
-  EXPECT_EQ(TRUST_E_TIME_STAMP, VerifyFileSignature(executable_full_path));
-}
-
-TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_UntrustedChain) {
-  const TCHAR kUntrustedChainExecutable[] =
-      _T("unittest_support\\SaveArguments_OmahaTestSigned.exe");
-
-  CString executable_full_path(app_util::GetCurrentModuleDirectory());
-  EXPECT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
-                           kUntrustedChainExecutable));
-  EXPECT_TRUE(File::Exists(executable_full_path));
-  EXPECT_EQ(CERT_E_UNTRUSTEDROOT, VerifyFileSignature(executable_full_path));
-}
-
-TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_HashFails) {
-  const TCHAR kCorruptedExecutable[] =
-      _T("unittest_support\\GoogleUpdate_corrupted.exe");
-
-  CString executable_full_path(app_util::GetCurrentModuleDirectory());
-  EXPECT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
-                           kCorruptedExecutable));
-  EXPECT_TRUE(File::Exists(executable_full_path));
-  EXPECT_EQ(TRUST_E_BAD_DIGEST, VerifyFileSignature(executable_full_path));
-}
-
-// The file for Windows Vista and later may not exist on all systems.
-TEST_F(GoogleUpdateRecoveryTest,
-       VerifyFileSignature_NonGoogleSignature) {
-  CString file_path = SystemInfo::IsRunningOnVistaOrLater() ?
-      _T("%SYSTEM%\\rcagent.exe") : _T("%SYSTEM%\\wuauclt.exe");
-  if (!File::Exists(file_path) && SystemInfo::IsRunningOnVistaOrLater()) {
-    std::wcout << _T("\tTest did not run because '") << file_path
-               << _T("' was not found.") << std::endl;
-    return;
-  }
-  EXPECT_HRESULT_SUCCEEDED(ExpandStringWithSpecialFolders(&file_path));
-  EXPECT_TRUE(File::Exists(file_path));
-  EXPECT_TRUE(SignatureIsValid(file_path, false));
-  EXPECT_EQ(CERT_E_CN_NO_MATCH, VerifyFileSignature(file_path));
-}
-
-TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_BadFilenames) {
-  EXPECT_EQ(CRYPT_E_FILE_ERROR, VerifyFileSignature(_T("NoSuchFile.exe")));
-
-  EXPECT_EQ(CRYPT_E_FILE_ERROR, VerifyFileSignature(NULL));
-
-  EXPECT_EQ(CRYPT_E_FILE_ERROR, VerifyFileSignature(_T("")));
-}
-
-//
-// VerifyRepairFileMarkup Tests
-//
-TEST_F(GoogleUpdateRecoveryTest, VerifyRepairFileMarkup_ValidMarkup) {
-  const TCHAR kExecutableWithMarkup[] =
-      _T("unittest_support\\SaveArguments.exe");
-  EXPECT_HRESULT_SUCCEEDED(VerifyRepairFileMarkup(kExecutableWithMarkup));
-}
-
-TEST_F(GoogleUpdateRecoveryTest, VerifyRepairFileMarkup_InvalidMarkups) {
-  const TCHAR kNoResourcesExecutable[] =
-      _T("unittest_support\\SaveArguments_unsigned_no_resources.exe");
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND),
-            VerifyRepairFileMarkup(kNoResourcesExecutable));
-
-  const TCHAR kResourcesButNoMarkupExecutable[] = _T("GoogleUpdate.exe");
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_RESOURCE_TYPE_NOT_FOUND),
-            VerifyRepairFileMarkup(kResourcesButNoMarkupExecutable));
-
-  const TCHAR kWrongMarkupResourceNameExecutable[] =
-      _T("unittest_support\\SaveArguments_unsigned_wrong_resource_name.exe");
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_RESOURCE_NAME_NOT_FOUND),
-            VerifyRepairFileMarkup(kWrongMarkupResourceNameExecutable));
-
-  const TCHAR kWrongMarkupSizeExecutable[] =
-      _T("unittest_support\\SaveArguments_unsigned_wrong_markup_size.exe");
-  EXPECT_EQ(E_UNEXPECTED, VerifyRepairFileMarkup(kWrongMarkupSizeExecutable));
-
-  const TCHAR kWrongMarkupValueExecutable[] =
-      _T("unittest_support\\SaveArguments_unsigned_wrong_markup_value.exe");
-  EXPECT_EQ(E_UNEXPECTED, VerifyRepairFileMarkup(kWrongMarkupValueExecutable));
-}
-
-TEST_F(GoogleUpdateRecoveryTest, VerifyRepairFileMarkup_BadFilenames) {
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            VerifyRepairFileMarkup(_T("NoSuchFile.exe")));
-
-  EXPECT_EQ(E_INVALIDARG, VerifyRepairFileMarkup(NULL));
-
-  EXPECT_EQ(E_INVALIDARG, VerifyRepairFileMarkup(_T("")));
-}
-
-//
-// VerifyRepairFileMarkup Tests
-//
-// TODO(omaha): Unit test VerifyIsValidRepairFile.
-
-//
-// Production Server Response Tests Tests
-//
-TEST_F(GoogleUpdateRecoveryTest, ProductionServerResponseTest) {
-  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
-                                               kDummyAppVersion,
-                                               kDummyAppLang,
-                                               true,
-                                               DownloadFileFromServer,
-                                               NULL)) <<
-      _T("The production server did not return 204. This may indicate network ")
-      _T("issues or that the Code Red server is configured incorrectly");
-}
-
-}  // namespace omaha
-
diff --git a/common/goopdate_command_line_validator.cc b/common/goopdate_command_line_validator.cc
new file mode 100644
index 0000000..a115c81
--- /dev/null
+++ b/common/goopdate_command_line_validator.cc
@@ -0,0 +1,485 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/goopdate_command_line_validator.h"
+#include "omaha/base/command_line_parser.h"
+#include "omaha/base/command_line_validator.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/string.h"
+#include "omaha/common/command_line.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/extra_args_parser.h"
+
+namespace omaha {
+
+GoopdateCommandLineValidator::GoopdateCommandLineValidator() {
+}
+
+GoopdateCommandLineValidator::~GoopdateCommandLineValidator() {
+}
+
+HRESULT GoopdateCommandLineValidator::Setup() {
+  validator_.reset(new CommandLineValidator);
+
+  CString cmd_line;
+
+  // gu.exe
+  cmd_line.Empty();
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnNoArgs);
+
+  // gu.exe /c [/nocrashserver
+  cmd_line.Format(_T("/%s [/%s"), kCmdLineCore, kCmdLineNoCrashHandler);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnCore);
+
+  // gu.exe /crashhandler
+  cmd_line.Format(_T("/%s"), kCmdLineCrashHandler);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnCrashHandler);
+
+  // gu.exe /svc
+  cmd_line.Format(_T("/%s"), kCmdLineService);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnService);
+
+  // gu.exe /medsvc
+  cmd_line.Format(_T("/%s"), kCmdLineMediumService);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnMediumService);
+
+  // gu.exe /regsvc
+  cmd_line.Format(_T("/%s"), kCmdLineRegisterService);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnServiceRegister);
+
+  // gu.exe /unregsvc
+  cmd_line.Format(_T("/%s"), kCmdLineUnregisterService);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnServiceUnregister);
+
+  // gu.exe /regserver
+  cmd_line.Format(_T("/%s"), kCmdRegServer);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnRegServer);
+
+  // gu.exe /unregserver
+  cmd_line.Format(_T("/%s"), kCmdUnregServer);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnUnregServer);
+
+  // gu.exe /netdiags
+  cmd_line.Format(_T("/%s"), kCmdLineNetDiags);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnNetDiags);
+
+  // gu.exe /crash
+  cmd_line.Format(_T("/%s"), kCmdLineCrash);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnCrash);
+
+  // gu.exe -Embedding. The -Embedding text is injected via COM.
+  CreateScenario(kCmdLineComServerDash,
+                 &GoopdateCommandLineValidator::OnComServer);
+
+  // COM server mode, but only for the broker.
+  CreateScenario(kCmdLineComBroker, &GoopdateCommandLineValidator::OnComBroker);
+
+  // COM server mode, but only for the OnDemand.
+  CreateScenario(kCmdLineOnDemand, &GoopdateCommandLineValidator::OnDemand);
+
+  // gu.exe /install <extraargs> [/appargs <appargs> [/installsource source
+  //        [/silent [/eularequired [/oem [/installelevated [/sessionid <sid>
+  cmd_line.Format(_T("/%s extra [/%s appargs [/%s src [/%s [/%s [/%s [/%s ")
+                  _T("[/%s sid"),
+                  kCmdLineInstall,
+                  kCmdLineAppArgs,
+                  kCmdLineInstallSource,
+                  kCmdLineSilent,
+                  kCmdLineEulaRequired,
+                  kCmdLineOem,
+                  kCmdLineInstallElevated,
+                  kCmdLineSessionId);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnInstall);
+
+  // gu.exe /update [/sessionid <sid>
+  cmd_line.Format(_T("/%s [/%s sid"), kCmdLineUpdate, kCmdLineSessionId);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnUpdate);
+
+  // gu.exe /handoff <extraargs> [/appargs <appargs> [/installsource source
+  //        [/silent [/eularequired [/offlineinstall [/offlinedir <dir>
+  //        [/sessionid <sid>
+  cmd_line.Format(_T("/%s extra [/%s appargs [/%s src [/%s [/%s [/%s [/%s dir ")
+                  _T("[/%s sid"),
+                  kCmdLineAppHandoffInstall,
+                  kCmdLineAppArgs,
+                  kCmdLineInstallSource,
+                  kCmdLineSilent,
+                  kCmdLineEulaRequired,
+                  kCmdLineLegacyOfflineInstall,
+                  kCmdLineOfflineDir,
+                  kCmdLineSessionId);
+  CreateScenario(cmd_line,
+                 &GoopdateCommandLineValidator::OnInstallHandoffWorker);
+
+  // gu.exe /ua [/installsource source [/machine
+  cmd_line.Format(_T("/%s [/%s source [/%s"),
+                  kCmdLineUpdateApps, kCmdLineInstallSource, kCmdLineMachine);
+  CreateScenario(cmd_line,
+                 &GoopdateCommandLineValidator::OnUpdateApps);
+
+  // gu.exe /report <crash_filename> [/machine
+  //        [/custom_info <custom_info_filename>
+  cmd_line.Format(_T("/%s filename [/%s [/%s customfilename"),
+                  kCmdLineReport,
+                  kCmdLineMachine,
+                  kCmdLineCustomInfoFileName);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnReportCrash);
+
+  // gu.exe /report /i <crash_filename> [/machine
+  cmd_line.Format(_T("/%s /%s filename [/%s"),
+                  kCmdLineReport,
+                  kCmdLineInteractive,
+                  kCmdLineMachine);
+  CreateScenario(cmd_line,
+                 &GoopdateCommandLineValidator::OnReportCrashInteractive);
+
+  // gu.exe /pi <domainurl> <args> /installsource <oneclick|update3web>
+  cmd_line.Format(_T("/%s domainurl args /%s src"),
+                  kCmdLineWebPlugin,
+                  kCmdLineInstallSource);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnWebPlugin);
+
+  // gu.exe /cr
+  cmd_line.Format(_T("/%s"), kCmdLineCodeRedCheck);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnCodeRed);
+
+  // gu.exe /recover <repair_file>
+  cmd_line.Format(_T("/%s repairfile"), kCmdLineRecover);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnRecover);
+
+  // gu.exe /recover /machine <repair_file>
+  cmd_line.Format(_T("/%s /%s repairfile"), kCmdLineRecover, kCmdLineMachine);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnRecoverMachine);
+
+  // gu.exe /uninstall
+  cmd_line.Format(_T("/%s"), kCmdLineUninstall);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnUninstall);
+
+  // gu.exe /registerproduct "extraargs" [/installsource source
+  cmd_line.Format(_T("/%s extraargs [/%s source"),
+                  kCmdLineRegisterProduct,
+                  kCmdLineInstallSource);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnRegisterProduct);
+
+  // gu.exe /unregisterproduct "extraargs"
+  cmd_line.Format(_T("/%s extraargs"), kCmdLineUnregisterProduct);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnUnregisterProduct);
+
+  // gu.exe /ping pingstring
+  cmd_line.Format(_T("/%s pingstring"), kCmdLinePing);
+  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnPing);
+
+  return S_OK;
+}
+
+// TODO(Omaha): Add check that each scenario is unique and does not overlap an
+// existing one in DBG builds.
+HRESULT GoopdateCommandLineValidator::Validate(const CommandLineParser* parser,
+                                               CommandLineArgs* args) {
+  ASSERT1(parser);
+  ASSERT1(args);
+
+  parser_ = parser;
+  args_ = args;
+
+  CString scenario_name;
+  HRESULT hr = validator_->Validate(*parser_, &scenario_name);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[GoopdateCommandLineValidator::Validate Failed][0x%x]"),
+                  hr));
+    return hr;
+  }
+
+  MapScenarioHandlersIter iter = scenario_handlers_.find(scenario_name);
+  if (iter == scenario_handlers_.end()) {
+    ASSERT1(false);
+    return GOOGLEUPDATE_COMMANDLINE_E_NO_SCENARIO_HANDLER;
+  }
+
+  ScenarioHandler handler = (*iter).second;
+  return (this->*handler)();
+}
+
+void GoopdateCommandLineValidator::CreateScenario(const TCHAR* cmd_line,
+                                                  ScenarioHandler handler) {
+  // Prepend the program name onto the cmd_line.
+  CString scenario_cmd_line;
+  scenario_cmd_line.Format(_T("prog.exe %s"), cmd_line);
+
+  CString scenario_name;
+  validator_->CreateScenarioFromCmdLine(scenario_cmd_line, &scenario_name);
+  // TODO(omaha): Make sure it doesn't already exist.
+  scenario_handlers_[scenario_name] = handler;
+}
+
+HRESULT GoopdateCommandLineValidator::GetExtraAndAppArgs(const CString& name) {
+  HRESULT hr = parser_->GetSwitchArgumentValue(name,
+                                               0,
+                                               &args_->extra_args_str);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = parser_->GetSwitchArgumentValue(kCmdLineAppArgs,
+                                       0,
+                                       &args_->app_args_str);
+  if (FAILED(hr)) {
+    args_->app_args_str.Empty();
+  }
+
+  ExtraArgsParser extra_args_parser;
+  return extra_args_parser.Parse(args_->extra_args_str,
+                                 args_->app_args_str,
+                                 &args_->extra);
+}
+
+HRESULT GoopdateCommandLineValidator::OnNoArgs() {
+  args_->mode = COMMANDLINE_MODE_NOARGS;
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnCore() {
+  args_->mode = COMMANDLINE_MODE_CORE;
+  args_->is_crash_handler_disabled = parser_->HasSwitch(kCmdLineNoCrashHandler);
+
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnCrashHandler() {
+  args_->mode = COMMANDLINE_MODE_CRASH_HANDLER;
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnService() {
+  args_->mode = COMMANDLINE_MODE_SERVICE;
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnMediumService() {
+  args_->mode = COMMANDLINE_MODE_MEDIUM_SERVICE;
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnServiceRegister() {
+  args_->mode = COMMANDLINE_MODE_SERVICE_REGISTER;
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnServiceUnregister() {
+  args_->mode = COMMANDLINE_MODE_SERVICE_UNREGISTER;
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnRegServer() {
+  args_->mode = COMMANDLINE_MODE_REGSERVER;
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnUnregServer() {
+  args_->mode = COMMANDLINE_MODE_UNREGSERVER;
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnNetDiags() {
+  args_->mode = COMMANDLINE_MODE_NETDIAGS;
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnCrash() {
+  args_->mode = COMMANDLINE_MODE_CRASH;
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnComServer() {
+  args_->mode = COMMANDLINE_MODE_COMSERVER;
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnComBroker() {
+  args_->mode = COMMANDLINE_MODE_COMBROKER;
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnDemand() {
+  args_->mode = COMMANDLINE_MODE_ONDEMAND;
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnInstall() {
+  args_->mode = COMMANDLINE_MODE_INSTALL;
+  parser_->GetSwitchArgumentValue(kCmdLineInstallSource,
+                                  0,
+                                  &args_->install_source);
+  parser_->GetSwitchArgumentValue(kCmdLineSessionId,
+                                  0,
+                                  &args_->session_id);
+  args_->is_silent_set = parser_->HasSwitch(kCmdLineSilent);
+  args_->is_eula_required_set = parser_->HasSwitch(kCmdLineEulaRequired);
+  args_->is_oem_set = parser_->HasSwitch(kCmdLineOem);
+  args_->is_install_elevated = parser_->HasSwitch(kCmdLineInstallElevated);
+  return GetExtraAndAppArgs(kCmdLineInstall);
+}
+
+HRESULT GoopdateCommandLineValidator::OnUpdate() {
+  args_->mode = COMMANDLINE_MODE_UPDATE;
+  parser_->GetSwitchArgumentValue(kCmdLineSessionId,
+                                  0,
+                                  &args_->session_id);
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnInstallHandoffWorker() {
+  args_->mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
+  parser_->GetSwitchArgumentValue(kCmdLineInstallSource,
+                                  0,
+                                  &args_->install_source);
+  parser_->GetSwitchArgumentValue(kCmdLineSessionId,
+                                  0,
+                                  &args_->session_id);
+  args_->is_silent_set = parser_->HasSwitch(kCmdLineSilent);
+  args_->is_eula_required_set = parser_->HasSwitch(kCmdLineEulaRequired);
+  args_->is_offline_set = parser_->HasSwitch(kCmdLineLegacyOfflineInstall) ||
+                          parser_->HasSwitch(kCmdLineOfflineDir);
+
+  if (SUCCEEDED(parser_->GetSwitchArgumentValue(kCmdLineOfflineDir,
+                                                0,
+                                                &args_->offline_dir))) {
+    RemoveMismatchedEndQuoteInDirectoryPath(&args_->offline_dir);
+    ::PathRemoveBackslash(CStrBuf(args_->offline_dir, MAX_PATH));
+  }
+
+  return GetExtraAndAppArgs(kCmdLineAppHandoffInstall);
+}
+
+HRESULT GoopdateCommandLineValidator::OnUpdateApps() {
+  args_->mode = COMMANDLINE_MODE_UA;
+  args_->is_machine_set = parser_->HasSwitch(kCmdLineMachine);
+  parser_->GetSwitchArgumentValue(kCmdLineInstallSource,
+                                  0,
+                                  &args_->install_source);
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnReportCrash() {
+  args_->mode = COMMANDLINE_MODE_REPORTCRASH;
+  args_->is_machine_set = parser_->HasSwitch(kCmdLineMachine);
+  parser_->GetSwitchArgumentValue(kCmdLineCustomInfoFileName,
+                                  0,
+                                  &args_->custom_info_filename);
+  return parser_->GetSwitchArgumentValue(kCmdLineReport,
+                                         0,
+                                         &args_->crash_filename);
+}
+
+HRESULT GoopdateCommandLineValidator::OnReportCrashInteractive() {
+  args_->mode = COMMANDLINE_MODE_REPORTCRASH;
+  args_->is_interactive_set = true;
+  args_->is_machine_set = parser_->HasSwitch(kCmdLineMachine);
+  return parser_->GetSwitchArgumentValue(kCmdLineInteractive,
+                                         0,
+                                         &args_->crash_filename);
+}
+
+HRESULT GoopdateCommandLineValidator::OnWebPlugin() {
+  HRESULT hr = parser_->GetSwitchArgumentValue(kCmdLineInstallSource,
+                                               0,
+                                               &args_->install_source);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  // Validate install_source value.
+  args_->install_source.MakeLower();
+  if ((args_->install_source.Compare(kCmdLineInstallSource_OneClick) != 0) &&
+      (args_->install_source.Compare(kCmdLineInstallSource_Update3Web) != 0)) {
+    args_->install_source.Empty();
+    return E_INVALIDARG;
+  }
+
+  args_->mode = COMMANDLINE_MODE_WEBPLUGIN;
+
+  CString urldomain;
+  hr = parser_->GetSwitchArgumentValue(kCmdLineWebPlugin,
+                                       0,
+                                       &urldomain);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  hr = StringUnescape(urldomain, &args_->webplugin_urldomain);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CString webplugin_args;
+  hr = parser_->GetSwitchArgumentValue(kCmdLineWebPlugin,
+                                       1,
+                                       &webplugin_args);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return StringUnescape(webplugin_args, &args_->webplugin_args);
+}
+
+HRESULT GoopdateCommandLineValidator::OnCodeRed() {
+  args_->mode = COMMANDLINE_MODE_CODE_RED_CHECK;
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnRecover() {
+  args_->mode = COMMANDLINE_MODE_RECOVER;
+  return parser_->GetSwitchArgumentValue(
+      kCmdLineRecover,
+      0,
+      &args_->code_red_metainstaller_path);
+}
+
+HRESULT GoopdateCommandLineValidator::OnRecoverMachine() {
+  args_->mode = COMMANDLINE_MODE_RECOVER;
+  args_->is_machine_set = true;
+  return parser_->GetSwitchArgumentValue(
+      kCmdLineMachine,
+      0,
+      &args_->code_red_metainstaller_path);
+}
+
+HRESULT GoopdateCommandLineValidator::OnUninstall() {
+  args_->mode = COMMANDLINE_MODE_UNINSTALL;
+  return S_OK;
+}
+
+HRESULT GoopdateCommandLineValidator::OnRegisterProduct() {
+  args_->mode = COMMANDLINE_MODE_REGISTER_PRODUCT;
+  parser_->GetSwitchArgumentValue(kCmdLineInstallSource,
+                                  0,
+                                  &args_->install_source);
+  return GetExtraAndAppArgs(kCmdLineRegisterProduct);
+}
+
+HRESULT GoopdateCommandLineValidator::OnUnregisterProduct() {
+  args_->mode = COMMANDLINE_MODE_UNREGISTER_PRODUCT;
+  return GetExtraAndAppArgs(kCmdLineUnregisterProduct);
+}
+
+HRESULT GoopdateCommandLineValidator::OnPing() {
+  args_->mode = COMMANDLINE_MODE_PING;
+  return parser_->GetSwitchArgumentValue(kCmdLinePing,
+                                         0,
+                                         &args_->ping_string);
+}
+
+}  // namespace omaha
+
diff --git a/common/goopdate_command_line_validator.h b/common/goopdate_command_line_validator.h
new file mode 100644
index 0000000..0b1c1f4
--- /dev/null
+++ b/common/goopdate_command_line_validator.h
@@ -0,0 +1,96 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_GOOPDATE_COMMAND_LINE_VALIDATOR_H__
+#define OMAHA_COMMON_GOOPDATE_COMMAND_LINE_VALIDATOR_H__
+
+#include <windows.h>
+#include <atlstr.h>
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+
+namespace omaha {
+
+struct CommandLineArgs;
+class CommandLineParser;
+class CommandLineValidator;
+
+// Validates all of the command line permutations for googleupdate.exe.
+class GoopdateCommandLineValidator {
+ public:
+  typedef HRESULT (GoopdateCommandLineValidator::*ScenarioHandler)();
+  typedef std::map<CString, ScenarioHandler> MapScenarioHandlers;
+  typedef MapScenarioHandlers::iterator MapScenarioHandlersIter;
+
+  GoopdateCommandLineValidator();
+  ~GoopdateCommandLineValidator();
+
+  // Sets up the scenarios.
+  HRESULT Setup();
+
+  // Validates a pre-parsed parser against the scenarios and returns a
+  // CommandLineArgs structure filled in with the proper values.
+  HRESULT Validate(const CommandLineParser* parser, CommandLineArgs* args);
+
+ private:
+  // Specific command-line scenario handlers.
+  HRESULT OnNoArgs();
+  HRESULT OnCore();
+  HRESULT OnCrashHandler();
+  HRESULT OnService();
+  HRESULT OnMediumService();
+  HRESULT OnServiceRegister();
+  HRESULT OnServiceUnregister();
+  HRESULT OnRegServer();
+  HRESULT OnUnregServer();
+  HRESULT OnNetDiags();
+  HRESULT OnCrash();
+  HRESULT OnComServer();
+  HRESULT OnComBroker();
+  HRESULT OnDemand();
+  HRESULT OnInstall();
+  HRESULT OnUpdate();
+  HRESULT OnInstallHandoffWorker();
+  HRESULT OnUpdateApps();
+  HRESULT OnReportCrash();
+  HRESULT OnReportCrashInteractive();
+  HRESULT OnWebPlugin();
+  HRESULT OnCodeRed();
+  HRESULT OnRecover();
+  HRESULT OnRecoverMachine();
+  HRESULT OnUninstall();
+  HRESULT OnRegisterProduct();
+  HRESULT OnUnregisterProduct();
+  HRESULT OnPing();
+
+  void CreateScenario(const TCHAR* cmd_line, ScenarioHandler handler);
+
+  HRESULT GetExtraAndAppArgs(const CString& switch_name);
+
+  const CommandLineParser* parser_;
+  CommandLineArgs* args_;
+  scoped_ptr<CommandLineValidator> validator_;
+  MapScenarioHandlers scenario_handlers_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(GoopdateCommandLineValidator);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_GOOPDATE_COMMAND_LINE_VALIDATOR_H__
+
diff --git a/common/goopdate_utils.cc b/common/goopdate_utils.cc
new file mode 100644
index 0000000..bb350af
--- /dev/null
+++ b/common/goopdate_utils.cc
@@ -0,0 +1,1393 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/goopdate_utils.h"
+#include <atlsecurity.h>
+#include "omaha/base/app_util.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/const_utils.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/path.h"
+#include "omaha/base/proc_utils.h"
+#include "omaha/base/process.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/scoped_impersonation.h"
+#include "omaha/base/service_utils.h"
+#include "omaha/base/string.h"
+#include "omaha/base/system.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vista_utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/command_line_builder.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/oem_install_utils.h"
+#include "omaha/statsreport/metrics.h"
+#include "goopdate/omaha3_idl.h"
+
+namespace omaha {
+
+namespace goopdate_utils {
+
+namespace {
+
+const int kTerminateBrowserTimeoutMs = 60000;
+
+bool IsMachineProcessWithoutPrivileges(bool is_machine_process) {
+  return is_machine_process && !vista_util::IsUserAdmin();
+}
+
+HRESULT LaunchImpersonatedCmdLine(const CString& cmd_line) {
+  CORE_LOG(L3, (_T("[LaunchImpersonatedCmdLine][%s]"), cmd_line));
+
+  scoped_handle impersonation_token;
+  HRESULT hr = vista::GetLoggedOnUserToken(address(impersonation_token));
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[GetLoggedOnUserToken failed][0x%x]"), hr));
+    return hr;
+  }
+
+  scoped_impersonation impersonate_user(get(impersonation_token));
+  hr = HRESULT_FROM_WIN32(impersonate_user.result());
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[impersonation failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<IProcessLauncher> launcher;
+  hr = launcher.CoCreateInstance(CLSID_ProcessLauncherClass,
+                                 NULL,
+                                 CLSCTX_LOCAL_SERVER);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[CoCreateInstance IProcessLauncher failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = launcher->LaunchCmdLine(cmd_line);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[IProcessLauncher.LaunchBrowser failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT LaunchImpersonatedBrowser(BrowserType type, const CString& url) {
+  CORE_LOG(L3, (_T("[LaunchImpersonatedBrowser][%u][%s]"), type, url));
+
+  scoped_handle impersonation_token;
+  HRESULT hr = vista::GetLoggedOnUserToken(address(impersonation_token));
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[GetLoggedOnUserToken failed][0x%x]"), hr));
+    return hr;
+  }
+
+  scoped_impersonation impersonate_user(get(impersonation_token));
+  hr = HRESULT_FROM_WIN32(impersonate_user.result());
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[impersonation failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<IProcessLauncher> launcher;
+  hr = launcher.CoCreateInstance(CLSID_ProcessLauncherClass,
+                                 NULL,
+                                 CLSCTX_LOCAL_SERVER);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[CoCreateInstance IProcessLauncher failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = launcher->LaunchBrowser(type, url);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[IProcessLauncher.LaunchBrowser failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+}  // namespace
+
+HRESULT LaunchCmdLine(bool is_machine, const CString& cmd_line) {
+  CORE_LOG(L3, (_T("[LaunchCmdLine][%d][%s]"), is_machine, cmd_line));
+
+  if (is_machine && vista_util::IsVistaOrLater() && vista_util::IsUserAdmin()) {
+    return LaunchImpersonatedCmdLine(cmd_line);
+  }
+
+  HRESULT hr = System::ShellExecuteCommandLine(cmd_line, NULL, NULL);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ShellExecuteCommandLine failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT LaunchBrowser(bool is_machine, BrowserType type, const CString& url) {
+  CORE_LOG(L3, (_T("[LaunchBrowser][%d][%u][%s]"), is_machine, type, url));
+
+  if (is_machine && vista_util::IsVistaOrLater() && vista_util::IsUserAdmin()) {
+    // Other than having a service launch the browser using CreateProcessAsUser,
+    // there is no easy solution if we are unable to launch the browser
+    // impersonated.
+    return LaunchImpersonatedBrowser(type, url);
+  }
+
+  HRESULT hr = RunBrowser(type, url);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[RunBrowser failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+CString BuildGoogleUpdateExeDir(bool is_machine) {
+  ConfigManager& cm = *ConfigManager::Instance();
+  return is_machine ? cm.GetMachineGoopdateInstallDir() :
+                      cm.GetUserGoopdateInstallDir();
+}
+
+CString BuildGoogleUpdateExePath(bool is_machine) {
+  CORE_LOG(L3, (_T("[BuildGoogleUpdateExePath][%d]"), is_machine));
+
+  CPath full_file_path(BuildGoogleUpdateExeDir(is_machine));
+  VERIFY1(full_file_path.Append(kOmahaShellFileName));
+
+  return full_file_path;
+}
+
+CString BuildGoogleUpdateServicesPath(bool is_machine) {
+  CORE_LOG(L3, (_T("[BuildGoogleUpdateServicesPath][%d]"), is_machine));
+
+  CPath full_file_path(BuildInstallDirectory(is_machine, GetVersionString()));
+  VERIFY1(full_file_path.Append(kCrashHandlerFileName));
+
+  return full_file_path;
+}
+
+HRESULT StartElevatedSelfWithArgsAndWait(const TCHAR* args, DWORD* exit_code) {
+  ASSERT1(args);
+  ASSERT1(exit_code);
+  CORE_LOG(L3, (_T("[StartElevatedSelfWithArgsAndWait]")));
+
+  // Get the process executable.
+  TCHAR filename[MAX_PATH] = {0};
+  if (::GetModuleFileName(NULL, filename, MAX_PATH) == 0) {
+    HRESULT hr = HRESULTFromLastError();
+    CORE_LOG(LEVEL_ERROR, (_T("[GetModuleFileName failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  // Launch self elevated and wait.
+  *exit_code = 0;
+  CORE_LOG(L1,
+      (_T("[RunElevated filename='%s'][arguments='%s']"), filename, args));
+  // According to the MSDN documentation for ::ShowWindow: "nCmdShow. This
+  // parameter is ignored the first time an application calls ShowWindow, if
+  // the program that launched the application provides a STARTUPINFO
+  // structure.". We want to force showing the UI window. So we pass in
+  // SW_SHOWNORMAL.
+  HRESULT hr(vista_util::RunElevated(filename, args, SW_SHOWNORMAL, exit_code));
+  CORE_LOG(L2, (_T("[elevated instance exit code][%u]"), *exit_code));
+  if (FAILED(hr)) {
+    CORE_LOG(LEVEL_ERROR, (_T("[RunElevated failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT StartGoogleUpdateWithArgs(bool is_machine,
+                                  const TCHAR* args,
+                                  HANDLE* process) {
+  CORE_LOG(L3, (_T("[StartGoogleUpdateWithArgs][%d][%s]"),
+                is_machine, args ? args : _T("")));
+
+  CString exe_path = BuildGoogleUpdateExePath(is_machine);
+
+  CORE_LOG(L3, (_T("[command line][%s][%s]"), exe_path, args ? args : _T("")));
+
+  HRESULT hr = System::ShellExecuteProcess(exe_path, args, NULL, process);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[can't start process][%s][0x%08x]"), exe_path, hr));
+    return hr;
+  }
+  return S_OK;
+}
+
+HRESULT StartCrashHandler(bool is_machine) {
+  CORE_LOG(L3, (_T("[StartCrashHandler]")));
+
+  ASSERT1(!is_machine || user_info::IsRunningAsSystem());
+
+  CString exe_path = BuildGoogleUpdateServicesPath(is_machine);
+  CommandLineBuilder builder(COMMANDLINE_MODE_CRASH_HANDLER);
+  CString cmd_line = builder.GetCommandLineArgs();
+  return System::StartProcessWithArgs(exe_path, cmd_line);
+}
+
+bool IsRunningFromOfficialGoopdateDir(bool is_machine) {
+  const ConfigManager& cm = *ConfigManager::Instance();
+  bool is_official_dir = is_machine ?
+                         cm.IsRunningFromMachineGoopdateInstallDir() :
+                         cm.IsRunningFromUserGoopdateInstallDir();
+  CORE_LOG(L3, (_T("[running from official dir][%d]"), is_official_dir));
+  return is_official_dir;
+}
+
+CString GetHKRoot() {
+  return IsRunningFromOfficialGoopdateDir(true) ? _T("HKLM") : _T("HKCU");
+}
+
+HRESULT InitializeSecurity() {
+  // Creates a security descriptor in absolute format and includes the owner
+  // and the primary group.  We grant access to admins and system.
+  CSecurityDesc security_descriptor;
+  if (SystemInfo::IsRunningOnVistaOrLater()) {
+    // To allow for low-integrity IE to call into IGoogleUpdate.
+    security_descriptor.FromString(LOW_INTEGRITY_SDDL_SACL);
+  }
+  security_descriptor.SetOwner(Sids::Admins());
+  security_descriptor.SetGroup(Sids::Admins());
+  CDacl dacl;
+  dacl.AddAllowedAce(Sids::System(), COM_RIGHTS_EXECUTE);
+  dacl.AddAllowedAce(Sids::Admins(), COM_RIGHTS_EXECUTE);
+  dacl.AddAllowedAce(Sids::AuthenticatedUser(), COM_RIGHTS_EXECUTE);
+
+  security_descriptor.SetDacl(dacl);
+  security_descriptor.MakeAbsolute();
+
+  SECURITY_DESCRIPTOR* sd = const_cast<SECURITY_DESCRIPTOR*>(
+      security_descriptor.GetPSECURITY_DESCRIPTOR());
+
+  return ::CoInitializeSecurity(
+      sd,
+      -1,
+      NULL,   // Let COM choose what authentication services to register.
+      NULL,
+      RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  // Data integrity and encryption.
+      RPC_C_IMP_LEVEL_IDENTIFY,       // Only allow a server to identify.
+      NULL,
+      EOAC_DYNAMIC_CLOAKING | EOAC_NO_CUSTOM_MARSHAL,
+      NULL);
+}
+
+// This is only used for legacy handoff support.
+CString GetProductName(const CString& app_guid) {
+  const TCHAR* product_name = NULL;
+  const TCHAR gears_guid[]   = _T("{283EAF47-8817-4c2b-A801-AD1FADFB7BAA}");
+  const TCHAR google_talk_plugin[]  =
+      _T("{D0AB2EBC-931B-4013-9FEB-C9C4C2225C8C}");
+  const TCHAR youtube_uploader_guid[] =
+      _T("{A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}");
+
+  if (app_guid.CompareNoCase(gears_guid) == 0) {
+    product_name = _T("Gears");
+  } else if (app_guid.CompareNoCase(google_talk_plugin) == 0) {
+      product_name = _T("Google Talk Plugin");
+  } else if (app_guid.CompareNoCase(youtube_uploader_guid) == 0) {
+      product_name = _T("YouTube Uploader");
+  } else {
+      product_name = _T("Google App");
+  }
+  return product_name;
+}
+
+HRESULT RedirectHKCR(bool is_machine) {
+  RegKey classes_key;
+  HRESULT hr = classes_key.Open(is_machine ?
+                                HKEY_LOCAL_MACHINE :
+                                HKEY_CURRENT_USER,
+                                _T("Software\\Classes"),
+                                KEY_ALL_ACCESS);
+  if (FAILED(hr)) {
+    ASSERT(FALSE, (_T("RedirectHKCR - key.Open(%d) fail %d"), is_machine, hr));
+    return hr;
+  }
+
+  LONG result = ::RegOverridePredefKey(HKEY_CLASSES_ROOT, classes_key.Key());
+  if (result != ERROR_SUCCESS) {
+    ASSERT(false, (_T("RedirectHKCR - RegOverridePredefKey fail %d"), result));
+    return HRESULT_FROM_WIN32(result);
+  }
+
+  return S_OK;
+}
+
+HRESULT RemoveRedirectHKCR() {
+  LONG result = ::RegOverridePredefKey(HKEY_CLASSES_ROOT, NULL);
+  if (result != ERROR_SUCCESS) {
+    ASSERT(FALSE, (_T("RemoveRedirectHKCR - RegOverridePredefKey %d"), result));
+    return HRESULT_FROM_WIN32(result);
+  }
+
+  return S_OK;
+}
+
+HRESULT RegisterTypeLib(bool is_admin,
+                        const CComBSTR& path,
+                        ITypeLib* type_lib) {
+  // Typelib registration.
+  CORE_LOG(L3, (_T("[Registering TypeLib]")));
+  HRESULT hr = S_OK;
+  if (!is_admin &&
+      SUCCEEDED(goopdate_utils::RegisterTypeLibForUser(type_lib, path, NULL))) {
+    return S_OK;
+  }
+
+  // For Admin cases, we use ::RegisterTypeLib().
+  // For platforms where ::RegisterTypeLibForUser is not available, we register
+  // with ::RegisterTypeLib, and rely on HKCR=>HKCU redirection.
+  hr = ::RegisterTypeLib(type_lib, path, NULL);
+  ASSERT(SUCCEEDED(hr), (_T("[TypeLib registration failed][0x%08x]"), hr));
+  return hr;
+}
+
+HRESULT UnRegisterTypeLib(bool is_admin, const CComBSTR&, ITypeLib* type_lib) {
+  // Typelib unregistration.
+  CORE_LOG(L3, (_T("[Unregistering Typelib]")));
+  TLIBATTR* tlib_attr = NULL;
+  HRESULT hr = type_lib->GetLibAttr(&tlib_attr);
+  ASSERT(SUCCEEDED(hr), (_T("[GetLibAttr failed][0x%08x]"), hr));
+  if (FAILED(hr)) {
+    return hr;
+  }
+  ON_SCOPE_EXIT_OBJ(*type_lib, &ITypeLib::ReleaseTLibAttr, tlib_attr);
+
+  if (!is_admin &&
+      SUCCEEDED(goopdate_utils::UnRegisterTypeLibForUser(
+          tlib_attr->guid,
+          tlib_attr->wMajorVerNum,
+          tlib_attr->wMinorVerNum,
+          tlib_attr->lcid,
+          tlib_attr->syskind))) {
+    return S_OK;
+  }
+
+  // For Admin cases, we use ::UnRegisterTypeLib().
+  // For platforms where ::UnRegisterTypeLibForUser is not available, we
+  // unregister with ::UnRegisterTypeLib, and rely on HKCR=>HKCU redirection.
+  hr = ::UnRegisterTypeLib(tlib_attr->guid,
+                           tlib_attr->wMajorVerNum,
+                           tlib_attr->wMinorVerNum,
+                           tlib_attr->lcid,
+                           tlib_attr->syskind);
+
+  // We assert before the check for TYPE_E_REGISTRYACCESS below because we want
+  // to catch the case where we're trying to unregister more than once because
+  // that would be a bug.
+  ASSERT(SUCCEEDED(hr),
+         (_T("[UnRegisterTypeLib failed.  ")
+          _T("This is likely a multiple unregister bug.][0x%08x]"), hr));
+
+  // If you try to unregister a type library that's already unregistered,
+  // it will return with this failure, which is OK.
+  if (hr == TYPE_E_REGISTRYACCESS) {
+    hr = S_OK;
+  }
+
+  return hr;
+}
+
+HRESULT RegisterOrUnregisterModule(bool is_machine,
+                                   bool register_server,
+                                   RegisterOrUnregisterFunction registrar,
+                                   void* data) {
+  ASSERT1(registrar);
+
+  // ATL by default registers the control to HKCR and we want to register
+  // either in HKLM, or in HKCU, depending on whether we are laying down
+  // the system googleupdate, or the user googleupdate.
+  // We solve this for the user goopdate case by:
+  // * Having the RGS file take a HKROOT parameter that translates to either
+  //   HKLM or HKCU.
+  // * Redirecting HKCR to HKCU\software\classes, for a user installation, to
+  //   cover Proxy registration.
+  // For the machine case, we still redirect HKCR to HKLM\\Software\\Classes,
+  // to ensure that Proxy registration happens in HKLM.
+  HRESULT hr = RedirectHKCR(is_machine);
+  ASSERT1(SUCCEEDED(hr));
+  if (FAILED(hr)) {
+    return hr;
+  }
+  // We need to stop redirecting at the end of this function.
+  ON_SCOPE_EXIT(RemoveRedirectHKCR);
+
+  hr = (*registrar)(data, register_server);
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[RegisterOrUnregisterModule failed][%d][0x%08x]"),
+                  register_server, hr));
+    ASSERT1(!register_server);
+  }
+
+  return hr;
+}
+
+HRESULT RegisterOrUnregisterModuleWithTypelib(
+    bool is_machine,
+    bool register_server,
+    RegisterOrUnregisterFunction registrar,
+    void* data) {
+  ASSERT1(registrar);
+
+  // By default, ATL registers the control to HKCR and we want to register
+  // either in HKLM, or in HKCU, depending on whether we are laying down
+  // the machine googleupdate, or the user googleupdate.
+  // We solve this for the user goopdate case by:
+  // * Having the RGS file take a HKROOT parameter that translates to either
+  //   HKLM or HKCU.
+  // * Redirecting HKCR to HKCU\software\classes, for a user installation, to
+  //   cover AppId and TypeLib registration
+  // * All the above makes ATL work correctly for 2K/XP. However on Win2K3
+  //   and Vista, redirection does not work by itself, because in these
+  //   platforms, RegisterTypeLib writes explicitly to HKLM\Software\Classes.
+  //   We need to specifically call the new RegisterTypeLibForUser() API.
+  //   So, we do that as well.
+  // For the machine case, we still redirect HKCR to HKLM\\Software\\Classes,
+  // because otherwise RegisterTypeLib ends up overwriting HKCU if the key
+  // already exists in HKCU.
+  HRESULT hr = RedirectHKCR(is_machine);
+  ASSERT1(SUCCEEDED(hr));
+  if (FAILED(hr)) {
+    return hr;
+  }
+  // We need to stop redirecting at the end of this function.
+  ON_SCOPE_EXIT(RemoveRedirectHKCR);
+
+  // load the type library.
+  CComPtr<ITypeLib> type_lib;
+  CComBSTR path;
+  hr = ::AtlLoadTypeLib(_AtlBaseModule.GetModuleInstance(), NULL, &path,
+                        &type_lib);
+  if (FAILED(hr)) {
+    ASSERT(false, (_T("[AtlLoadTypeLib failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  if (register_server) {
+    hr = (*registrar)(data, register_server);
+    if (FAILED(hr)) {
+      ASSERT(false, (_T("[Module registration failed][0x%08x]"), hr));
+      return hr;
+    }
+
+    return RegisterTypeLib(is_machine, path, type_lib);
+  } else {
+    hr = UnRegisterTypeLib(is_machine, path, type_lib);
+    if (FAILED(hr)) {
+      ASSERT(false, (_T("[UnRegisterTypeLib failed][0x%08x]"), hr));
+      return hr;
+    }
+
+    return (*registrar)(data, register_server);
+  }
+}
+
+HRESULT RegisterTypeLibForUser(ITypeLib* lib,
+                               OLECHAR* path,
+                               OLECHAR* help_dir) {
+  CORE_LOG(L3, (_T("[RegisterTypeLibForUser]")));
+  ASSERT1(lib);
+  ASSERT1(path);
+  // help_dir can be NULL.
+
+  const TCHAR* library_name = _T("oleaut32.dll");
+  scoped_library module(static_cast<HINSTANCE>(::LoadLibrary(library_name)));
+  if (!module) {
+    HRESULT hr = HRESULTFromLastError();
+    CORE_LOG(LEVEL_ERROR,
+        (_T("[LoadLibrary failed][%s][0x%08x]"), library_name, hr));
+    return hr;
+  }
+
+  // RegisterTypeLibForUser function from oleaut32.dll.
+  typedef HRESULT(__stdcall *PF)(ITypeLib*, OLECHAR*, OLECHAR*);
+
+  const char* function_name = "RegisterTypeLibForUser";
+  PF fp = reinterpret_cast<PF>(::GetProcAddress(get(module), function_name));
+  if (!fp) {
+    HRESULT hr = HRESULTFromLastError();
+    CORE_LOG(LEVEL_ERROR,
+             (_T("[GetProcAddress failed][%s][0x%08x]"),
+              function_name, library_name, hr));
+    return hr;
+  }
+
+  CORE_LOG(L3, (_T("[Calling RegisterTypelibForUser in oleaut]")));
+  HRESULT hr = fp(lib, path, help_dir);
+  if (FAILED(hr)) {
+    CORE_LOG(LEVEL_ERROR, (_T("[regtypelib_for_user failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT UnRegisterTypeLibForUser(REFGUID lib_id,
+                                 WORD major_ver_num,
+                                 WORD minor_ver_num,
+                                 LCID lcid,
+                                 SYSKIND syskind) {
+  CORE_LOG(L3, (_T("[UnRegisterTypeLibForUser]")));
+
+  const TCHAR* library_name = _T("oleaut32.dll");
+  scoped_library module(static_cast<HINSTANCE>(::LoadLibrary(library_name)));
+  if (!module) {
+    HRESULT hr = HRESULTFromLastError();
+    CORE_LOG(LEVEL_ERROR,
+        (_T("[LoadLibrary failed][%s][0x%08x]"), library_name, hr));
+    return hr;
+  }
+
+  // UnRegisterTypeLibForUser function from oleaut32.dll.
+  typedef HRESULT (__stdcall *PF)(REFGUID, WORD, WORD, LCID, SYSKIND);
+
+  const char* function_name = "UnRegisterTypeLibForUser";
+  PF fp = reinterpret_cast<PF>(::GetProcAddress(get(module), function_name));
+  if (!fp) {
+    HRESULT hr = HRESULTFromLastError();
+    CORE_LOG(LEVEL_ERROR,
+             (_T("[GetProcAddress failed][%s][0x%08x]"),
+              function_name, library_name, hr));
+    return hr;
+  }
+
+  CORE_LOG(L3, (_T("[Calling UnRegisterTypeLibForUser in oleaut]")));
+  HRESULT hr = fp(lib_id, major_ver_num, minor_ver_num, lcid, syskind);
+  if (FAILED(hr)) {
+    CORE_LOG(LEVEL_ERROR, (_T("[unregtypelib_for_user failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+// TODO(omaha): This method's name is much more specific than what it does. Can
+// we just copy the code to scheduled task and service code and eliminate it?
+// Reads the current value under {HKLM|HKCU}\Google\Update\value_name. Returns
+// default_val if value_name does not exist.
+CString GetCurrentVersionedName(bool is_machine,
+                                const TCHAR* value_name,
+                                const TCHAR* default_val) {
+  CORE_LOG(L3, (_T("[ConfigManager::GetCurrentVersionedName]")));
+  ASSERT1(value_name && *value_name);
+  ASSERT1(default_val && *default_val);
+
+  const TCHAR* key_name = is_machine ? MACHINE_REG_UPDATE : USER_REG_UPDATE;
+  CString name;
+  HRESULT hr(RegKey::GetValue(key_name, value_name, &name));
+  if (FAILED(hr)) {
+    CORE_LOG(L4, (_T("[GetValue failed][%s][0x%x][Using default name][%s]"),
+                  value_name, hr, default_val));
+    name = default_val;
+  }
+
+  CORE_LOG(L3, (_T("[Versioned Name][%s]"), name));
+  return name;
+}
+
+// Creates a unique name of the form "{prefix}1c9b3d6baf90df3" and stores it in
+// the registry under HKLM/HKCU\Google\Update\value_name. Subsequent
+// invocations of GetCurrentTaskName() will return this new value.
+HRESULT CreateAndSetVersionedNameInRegistry(bool is_machine,
+                                            const TCHAR* prefix,
+                                            const TCHAR* value_name) {
+  ASSERT1(prefix && *prefix);
+  ASSERT1(value_name && *value_name);
+
+  // TODO(omaha): Move from service_utils.h since it is used for other purposes.
+  CString name(ServiceInstall::GenerateServiceName(prefix));
+  CORE_LOG(L3, (_T("Versioned name[%s][%s][%s]"), prefix, value_name, name));
+
+  const TCHAR* key_name = is_machine ? MACHINE_REG_UPDATE : USER_REG_UPDATE;
+  return RegKey::SetValue(key_name, value_name, name);
+}
+
+HRESULT TerminateAllBrowsers(
+    BrowserType type,
+    TerminateBrowserResult* browser_res,
+    TerminateBrowserResult* default_res) {
+  UTIL_LOG(L3, (_T("[TerminateAllBrowsers][%d]"), type));
+  ASSERT1(default_res);
+  ASSERT1(browser_res);
+
+  if (type == BROWSER_UNKNOWN ||
+      type == BROWSER_DEFAULT ||
+      type >= BROWSER_MAX) {
+    ASSERT1(false);
+    return E_INVALIDARG;
+  }
+
+  const BrowserType kFirstBrowser = BROWSER_IE;
+  const int kNumSupportedBrowsers = BROWSER_MAX - kFirstBrowser;
+
+  BrowserType default_type = BROWSER_UNKNOWN;
+  HRESULT hr = GetDefaultBrowserType(&default_type);
+  if (FAILED(hr)) {
+    UTIL_LOG(LW, (_T("[GetDefaultBrowserType failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  TerminateBrowserResult terminate_results[kNumSupportedBrowsers];
+
+  for (int browser = 0; browser < kNumSupportedBrowsers; ++browser) {
+    const BrowserType browser_type =
+        static_cast<BrowserType>(kFirstBrowser + browser);
+    hr = TerminateBrowserProcess(browser_type,
+                                 CString(),
+                                 0,
+                                 &terminate_results[browser].found);
+    if (FAILED(hr)) {
+      UTIL_LOG(LW, (_T("[TerminateBrowserProcess failed][%u][0x%08x]"),
+                    browser_type, hr));
+    }
+  }
+
+  // Now wait for the all browser instances to die.
+  // TODO(omaha): Wait for all processes at once rather than waiting for
+  // (kTerminateBrowserTimeoutMs * # supported browsers) ms.
+  for (int browser = 0; browser < kNumSupportedBrowsers; ++browser) {
+    const BrowserType browser_type =
+        static_cast<BrowserType>(kFirstBrowser + browser);
+    hr = WaitForBrowserToDie(browser_type,
+                             CString(),
+                             kTerminateBrowserTimeoutMs);
+    if (FAILED(hr)) {
+      UTIL_LOG(LW, (_T("[WaitForBrowserToDie failed][%u][0x%08x]"),
+                    browser_type, hr));
+    } else {
+      terminate_results[browser].could_terminate = true;
+    }
+  }
+
+  *browser_res = terminate_results[type - kFirstBrowser];
+  *default_res = terminate_results[default_type - kFirstBrowser];
+
+  return S_OK;
+}
+
+// default_type can be BROWSER_UNKNOWN.
+// If browsers that must be closed could not be terminated, false is returned.
+// This method and TerminateBrowserProcesses assume the user did not shutdown
+// the specified browser. They restart and shutdown, respectively, the default
+// browser when the specified browser is not found. The reason for this may have
+// been that the the specified (stamped) browser could be in a bad state on the
+// machine and trying to start it would fail.
+// This may also be required to support hosted cases (i.e. AOL and Maxthon).
+// TODO(omaha): If we assume the stamped browser is okay, check whether the
+// specified browser is installed rather than relying on whether the browser was
+// running. It is perfectly valid for the browser to not be running.
+// TODO(omaha): Why not try the default browser if browsers that require
+// shutdown failed to terminate.
+bool GetBrowserToRestart(BrowserType type,
+                         BrowserType default_type,
+                         const TerminateBrowserResult& res,
+                         const TerminateBrowserResult& def_res,
+                         BrowserType* browser_type) {
+  ASSERT1(browser_type);
+  ASSERT1(type != BROWSER_UNKNOWN &&
+          type != BROWSER_DEFAULT &&
+          type < BROWSER_MAX);
+  ASSERT1(default_type != BROWSER_DEFAULT && default_type < BROWSER_MAX);
+  UTIL_LOG(L3, (_T("[GetBrowserToRestart][%d]"), type));
+
+  *browser_type = BROWSER_UNKNOWN;
+
+  if (res.found) {
+    switch (type) {
+      case BROWSER_IE:
+        *browser_type = BROWSER_IE;
+        return true;
+      case BROWSER_FIREFOX:   // Only one process.
+      case BROWSER_CHROME:    // One process per plug-in, even for upgrades.
+        if (res.could_terminate) {
+          *browser_type = type;
+          return true;
+        }
+        return false;
+      case BROWSER_UNKNOWN:
+      case BROWSER_DEFAULT:
+      case BROWSER_MAX:
+      default:
+        break;
+    }
+  }
+
+  // We did not find the browser that we wanted to restart. Hence we need to
+  // determine if we could shutdown the default browser.
+  switch (default_type) {
+    case BROWSER_IE:
+      *browser_type = BROWSER_IE;
+      return true;
+    case BROWSER_FIREFOX:
+    case BROWSER_CHROME:
+      if (!def_res.found || def_res.found && def_res.could_terminate) {
+        *browser_type = default_type;
+        return true;
+      }
+      break;
+    case BROWSER_UNKNOWN:
+    case BROWSER_DEFAULT:
+    case BROWSER_MAX:
+    default:
+      break;
+  }
+
+  return false;
+}
+
+// See the comments about the default browser above GetBrowserToRestart.
+HRESULT TerminateBrowserProcesses(BrowserType type,
+                                  TerminateBrowserResult* browser_res,
+                                  TerminateBrowserResult* default_res) {
+  UTIL_LOG(L3, (_T("[TerminateBrowserProcesses][%d]"), type));
+  ASSERT1(browser_res);
+  ASSERT1(default_res);
+
+  browser_res->could_terminate = false;
+  default_res->could_terminate = false;
+
+  if (type == BROWSER_UNKNOWN ||
+      type == BROWSER_DEFAULT ||
+      type >= BROWSER_MAX) {
+    ASSERT1(false);
+    return E_UNEXPECTED;
+  }
+
+  CString sid;
+  HRESULT hr = user_info::GetProcessUser(NULL, NULL, &sid);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[GetProcessUser failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  hr = TerminateBrowserProcess(type,
+                               sid,
+                               kTerminateBrowserTimeoutMs,
+                               &browser_res->found);
+  if (FAILED(hr)) {
+    UTIL_LOG(LW, (_T("[TerminateBrowserProcess failed][0x%08x]"), hr));
+  } else {
+    browser_res->could_terminate = true;
+  }
+
+  // Since no instances of the browser type exist, we try to find and kill
+  // all instances of the default browser.
+  if (!browser_res->found) {
+    // We dont want to try and terminate the default browser, if it is the
+    // same as the browser that we tried above.
+
+    BrowserType default_type = BROWSER_UNKNOWN;
+    hr = GetDefaultBrowserType(&default_type);
+    if (FAILED(hr)) {
+      UTIL_LOG(LW, (_T("[GetDefaultBrowserType failed][0x%08x]"), hr));
+    }
+
+    UTIL_LOG(L3, (_T("[Trying to kill the default browser %d]"), default_type));
+    if (default_type != type) {
+      hr = TerminateBrowserProcess(BROWSER_DEFAULT,
+                                   sid,
+                                   kTerminateBrowserTimeoutMs,
+                                   &default_res->found);
+      if (FAILED(hr)) {
+        UTIL_LOG(LW, (_T("[TerminateBrowserProcess failed][0x%08x]"), hr));
+      } else {
+        default_res->could_terminate = true;
+      }
+    }
+  }
+
+  return hr;
+}
+
+HRESULT GetBrowserImagePathFromProcess(BrowserType type,
+                                       uint32 explorer_pid,
+                                       CString* path) {
+  ASSERT1(path);
+
+  if (type == BROWSER_UNKNOWN || type >= BROWSER_MAX) {
+    ASSERT1(false);
+    return E_UNEXPECTED;
+  }
+
+  if (type == BROWSER_DEFAULT) {
+    return GetDefaultBrowserPath(path);
+  }
+
+  CString user_sid;
+  HRESULT hr = Process::GetProcessOwner(explorer_pid, &user_sid);
+  if (FAILED(hr)) {
+    UTIL_LOG(LEVEL_WARNING, (_T("[GetProcessOwner failed.][0x%08x]"), hr));
+    return hr;
+  }
+
+  CString browser_name;
+  hr = BrowserTypeToProcessName(type, &browser_name);
+  if (FAILED(hr)) {
+    UTIL_LOG(LW, (_T("[BrowserTypeToProcessName failed.][0x%08x]"), hr));
+    return hr;
+  }
+
+  hr = Process::GetImagePath(browser_name, user_sid, path);
+  if (FAILED(hr)) {
+    UTIL_LOG(LW, (_T("[GetImagePath failed.][0x%08x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT ConvertStringToBrowserType(const CString& text, BrowserType* type) {
+  ASSERT1(type != NULL);
+
+  if (text.GetLength() != 1) {
+    return GOOPDATEUTILS_E_BROWSERTYPE;
+  }
+
+  int browser_type = 0;
+  if (!String_StringToDecimalIntChecked(text, &browser_type)) {
+    return GOOPDATEUTILS_E_BROWSERTYPE;
+  }
+
+  if (browser_type >= BROWSER_MAX) {
+    return GOOPDATEUTILS_E_BROWSERTYPE;
+  }
+
+  *type = static_cast<BrowserType>(browser_type);
+  return S_OK;
+}
+
+CString ConvertBrowserTypeToString(BrowserType type) {
+  CString text = itostr(static_cast<int>(type));
+  ASSERT1(!text.IsEmpty());
+  return text;
+}
+
+HRESULT GetOSInfo(CString* os_version, CString* service_pack) {
+  ASSERT1(os_version);
+  ASSERT1(service_pack);
+
+  OSVERSIONINFO os_version_info = { 0 };
+  os_version_info.dwOSVersionInfoSize = sizeof(os_version_info);
+  if (!::GetVersionEx(&os_version_info)) {
+    HRESULT hr = HRESULTFromLastError();
+    UTIL_LOG(LW, (_T("[GetVersionEx failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  os_version->Format(_T("%d.%d"),
+                     os_version_info.dwMajorVersion,
+                     os_version_info.dwMinorVersion);
+  *service_pack = os_version_info.szCSDVersion;
+  return S_OK;
+}
+
+CPath BuildInstallDirectory(bool is_machine, const CString& version) {
+  ConfigManager& cm = *ConfigManager::Instance();
+  CPath install_dir(is_machine ? cm.GetMachineGoopdateInstallDir() :
+                                 cm.GetUserGoopdateInstallDir());
+  VERIFY1(install_dir.Append(version));
+
+  return install_dir;
+}
+
+// This method does a very specific job of searching for install workers,
+// for the user and machine omaha. It also includes the on-demand updates COM
+// server, because we treat it similar to interactive installs, and selfupdate.
+//
+// In machine case we search in all the accounts since the install worker can be
+// running in any admin account and the machine update worker runs as SYSTEM.
+// In the user case, we only search the user's account.
+// In both cases, the Needsadmin command line parameter is checked for
+// true/false in the machine/user case, respectively.
+//
+// Only adds processes to the input vector; does not clear it.
+//
+// TODO(omaha): For now we search for the needs_admin=true in the command
+// line to determine a machine install. Another option of identifying omaha's
+// is to use the presence of a named mutex. So the user omaha will create
+// Global\<sid>\Mutex and the machine will create Global\Mutex, in here then
+// we can test for the presence of the name to decide if an interactive
+// omaha is running.
+// TODO(omaha): Consider further filtering the processes based on whether
+// the owner is elevated in case of machine omaha.
+// Looks for the /ig command line used in Omaha 2.
+HRESULT GetInstallWorkerProcesses(bool is_machine,
+                                  std::vector<uint32>* processes) {
+  ASSERT1(processes);
+
+  CString user_sid;
+  DWORD flags = EXCLUDE_CURRENT_PROCESS |
+                EXCLUDE_PARENT_PROCESS  |
+                INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING;
+
+  std::vector<CString> command_lines;
+  CString command_line_to_include;
+  command_line_to_include.Format(_T("/%s"), kCmdLineInstall);
+  command_lines.push_back(command_line_to_include);
+  command_line_to_include.Format(_T("/%s"), kCmdLineInstallElevated);
+  command_lines.push_back(command_line_to_include);
+  command_line_to_include.Format(_T("/%s"), kCmdLineAppHandoffInstall);
+  command_lines.push_back(command_line_to_include);
+  command_line_to_include.Format(_T("/%s"), kCmdLineUpdate);
+  command_lines.push_back(command_line_to_include);
+  command_line_to_include.Format(_T("/%s"),
+                                 kCmdLineLegacyFinishGoogleUpdateInstall);
+  command_lines.push_back(command_line_to_include);
+  command_lines.push_back(kCmdLineComServerDash);
+
+  if (!is_machine) {
+    // Search only the same sid as the current user.
+    flags |= INCLUDE_ONLY_PROCESS_OWNED_BY_USER;
+
+    HRESULT hr = user_info::GetProcessUser(NULL, NULL, &user_sid);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[GetProcessUser failed][0x%08x]"), hr));
+      return hr;
+    }
+  }
+
+  std::vector<uint32> all_install_worker_processes;
+  HRESULT hr = Process::FindProcesses(flags,
+                                      kOmahaShellFileName,
+                                      true,
+                                      user_sid,
+                                      command_lines,
+                                      &all_install_worker_processes);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[FindProcesses failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  CString official_path;
+  hr = GetFolderPath(is_machine ? CSIDL_PROGRAM_FILES : CSIDL_LOCAL_APPDATA,
+                     &official_path);
+  ASSERT1(SUCCEEDED(hr));
+  ASSERT1(!official_path.IsEmpty());
+
+  for (size_t i = 0; i < all_install_worker_processes.size(); ++i) {
+    CString cmd_line;
+    const uint32 process = all_install_worker_processes[i];
+    if (SUCCEEDED(Process::GetCommandLine(process, &cmd_line))) {
+      cmd_line.MakeLower();
+      // TODO(omaha): FindProcess method does not allow regex's to be specified
+      // along with the include command line. Change Process to allow this.
+      if (cmd_line.Find(is_machine ? kNeedsAdminYes : kNeedsAdminNo) != -1) {
+        CORE_LOG(L4, (_T("[Including process][%s]"), cmd_line));
+        processes->push_back(process);
+      }
+
+      // A needsadmin=prefers instance could be installing either for machine or
+      // for user.
+      if (cmd_line.Find(kNeedsAdminPrefers) != -1) {
+        CORE_LOG(L4, (_T("[Including process][%s]"), cmd_line));
+        processes->push_back(process);
+      }
+
+      // The -Embedding does not have a needsAdmin. Decide whether to include it
+      // if it matches the official path for the requested instance type.
+      CString exe_path;
+      if (cmd_line.Find(kCmdLineComServerDash) != -1 &&
+          SUCCEEDED(GetExePathFromCommandLine(cmd_line, &exe_path)) &&
+          String_StrNCmp(official_path, exe_path, official_path.GetLength(),
+                         true) == 0) {
+        CORE_LOG(L4, (_T("[Including process][%s]"), cmd_line));
+        processes->push_back(process);
+      }
+    }
+  }
+
+  return S_OK;
+}
+
+// The event name saved to the environment variable does not contain the
+// decoration added by GetNamedObjectAttributes.
+HRESULT CreateUniqueEventInEnvironment(const CString& var_name,
+                                       bool is_machine,
+                                       HANDLE* unique_event) {
+  ASSERT1(unique_event);
+
+  GUID event_guid = GUID_NULL;
+  HRESULT hr = ::CoCreateGuid(&event_guid);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[::CoCreateGuid failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  CString event_name(GuidToString(event_guid));
+  NamedObjectAttributes attr;
+  GetNamedObjectAttributes(event_name, is_machine, &attr);
+
+  hr = CreateEvent(&attr, unique_event);
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[CreateEvent failed in CreateUniqueEventInEnvironment]"),
+                  _T("[%s][0x%08x]"), var_name, hr));
+    return hr;
+  }
+
+  CORE_LOG(L3, (_T("[created unique event][%s][%s]"), var_name, event_name));
+
+  if (!::SetEnvironmentVariable(var_name, event_name)) {
+    DWORD error = ::GetLastError();
+    CORE_LOG(LE, (_T("[::SetEnvironmentVariable failed][%d]"), error));
+    return HRESULT_FROM_WIN32(error);
+  }
+
+  return S_OK;
+}
+
+HRESULT OpenUniqueEventFromEnvironment(const CString& var_name,
+                                       bool is_machine,
+                                       HANDLE* unique_event) {
+  ASSERT1(unique_event);
+
+  TCHAR event_name[MAX_PATH] = {0};
+  if (!::GetEnvironmentVariable(var_name, event_name, arraysize(event_name))) {
+    DWORD error = ::GetLastError();
+    CORE_LOG(LW, (_T("[Failed to read environment variable][%s][%d]"),
+                  var_name, error));
+    return HRESULT_FROM_WIN32(error);
+  }
+
+  CORE_LOG(L3, (_T("[read unique event][%s][%s]"), var_name, event_name));
+
+  NamedObjectAttributes attr;
+  GetNamedObjectAttributes(event_name, is_machine, &attr);
+  *unique_event = ::OpenEvent(EVENT_ALL_ACCESS, false, attr.name);
+
+  if (!*unique_event) {
+    DWORD error = ::GetLastError();
+    CORE_LOG(LW, (_T("[::OpenEvent failed][%s][%d]"), attr.name, error));
+    return HRESULT_FROM_WIN32(error);
+  }
+
+  return S_OK;
+}
+
+// The caller is responsible for reseting the event and closing the handle.
+HRESULT CreateEvent(NamedObjectAttributes* event_attr, HANDLE* event_handle) {
+  ASSERT1(event_handle);
+  ASSERT1(event_attr);
+  ASSERT1(!event_attr->name.IsEmpty());
+  *event_handle = ::CreateEvent(&event_attr->sa,
+                                true,   // manual reset
+                                false,  // not signaled
+                                event_attr->name);
+
+  if (!*event_handle) {
+    DWORD error = ::GetLastError();
+    CORE_LOG(LEVEL_ERROR, (_T("[::CreateEvent failed][%d]"), error));
+    return HRESULT_FROM_WIN32(error);
+  }
+
+  return S_OK;
+}
+
+bool IsTestSource() {
+  return !ConfigManager::Instance()->GetTestSource().IsEmpty();
+}
+
+HRESULT ReadNameValuePairsFromFile(const CString& file_path,
+                                   const CString& group_name,
+                                   std::map<CString, CString>* pairs) {
+  ASSERT1(pairs);
+
+  if (!File::Exists(file_path)) {
+    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+  }
+
+  pairs->clear();
+
+  TCHAR str_buf[32768] = {0};
+
+  // Retrieve all key names in the section requested.
+  DWORD buf_count = ::GetPrivateProfileString(group_name,
+                                              NULL,
+                                              NULL,
+                                              str_buf,
+                                              arraysize(str_buf),
+                                              file_path);
+
+  DWORD offset = 0;
+  while (offset < buf_count) {
+    TCHAR val_buf[1024] = {0};
+    CString current_key = &(str_buf[offset]);
+    DWORD val_count = ::GetPrivateProfileString(group_name,
+                                                current_key,
+                                                NULL,
+                                                val_buf,
+                                                arraysize(val_buf),
+                                                file_path);
+    (*pairs)[current_key] = val_buf;
+    offset += current_key.GetLength() + 1;
+  }
+
+  return S_OK;
+}
+
+HRESULT WriteNameValuePairsToFile(const CString& file_path,
+                                  const CString& group_name,
+                                  const std::map<CString, CString>& pairs) {
+  std::map<CString, CString>::const_iterator it = pairs.begin();
+  for (; it != pairs.end(); ++it) {
+    if (!::WritePrivateProfileString(group_name,
+                                     it->first,
+                                     it->second,
+                                     file_path)) {
+      return HRESULTFromLastError();
+    }
+  }
+
+  return S_OK;
+}
+
+bool IsAppInstallWorkerRunning(bool is_machine) {
+  CORE_LOG(L3, (_T("[IsAppInstallWorkerRunning][%d]"), is_machine));
+  std::vector<uint32> processes;
+  VERIFY1(SUCCEEDED(GetInstallWorkerProcesses(is_machine, &processes)));
+  return !processes.empty();
+}
+
+// Returns true if the version does not begin with "1.0." or "1.1.".
+bool IsGoogleUpdate2OrLater(const CString& version) {
+  const ULONGLONG kFirstOmaha2Version = MAKEDLLVERULL(1, 2, 0, 0);
+  ULONGLONG version_number = VersionFromString(version);
+  ASSERT1(0 != version_number);
+
+  if (kFirstOmaha2Version <= version_number) {
+    return true;
+  }
+
+  return false;
+}
+
+HRESULT WriteInstallerDataToTempFile(const CString& installer_data,
+                                     CString* installer_data_file_path) {
+  ASSERT1(installer_data_file_path);
+
+  // TODO(omaha): consider eliminating the special case and simply create an
+  // empty file.
+  CORE_LOG(L2, (_T("[WriteInstallerDataToTempFile][data=%s]"), installer_data));
+  if (installer_data.IsEmpty()) {
+    return S_FALSE;
+  }
+
+  CString temp_file;
+  if (!::GetTempFileName(app_util::GetTempDir(),
+                         _T("gui"),
+                         0,
+                         CStrBuf(temp_file, MAX_PATH))) {
+    HRESULT hr = HRESULTFromLastError();
+    CORE_LOG(LE, (_T("[::GetTempFileName failed][0x08%x]"), hr));
+    return hr;
+  }
+
+  scoped_hfile file_handle(::CreateFile(temp_file,
+                                        GENERIC_WRITE,
+                                        FILE_SHARE_READ,
+                                        NULL,
+                                        CREATE_ALWAYS,
+                                        FILE_ATTRIBUTE_NORMAL,
+                                        NULL));
+  if (!file_handle) {
+    HRESULT hr = HRESULTFromLastError();
+    CORE_LOG(LE, (_T("[::CreateFile failed][0x08%x]"), hr));
+    return hr;
+  }
+
+  CStringA installer_data_utf8_bom;
+  SafeCStringAFormat(&installer_data_utf8_bom, "%c%c%c%s",
+                     0xEF, 0xBB, 0xBF, WideToUtf8(installer_data));
+
+  DWORD bytes_written = 0;
+  if (!::WriteFile(get(file_handle),
+                   installer_data_utf8_bom,
+                   installer_data_utf8_bom.GetLength(),
+                   &bytes_written,
+                   NULL)) {
+    HRESULT hr = HRESULTFromLastError();
+    CORE_LOG(LE, (_T("[::WriteFile failed][0x08%x]"), hr));
+    return hr;
+  }
+
+  *installer_data_file_path = temp_file;
+  return S_OK;
+}
+
+// Returns true if the absolute difference between time moments is greater than
+// the interval between update checks.
+// Deals with clocks rolling backwards, in scenarios where the clock indicates
+// some time in the future, for example next year, last_checked_ is updated to
+// reflect that time, and then the clock is adjusted back to present.
+bool ShouldCheckForUpdates(bool is_machine) {
+  ConfigManager* cm = ConfigManager::Instance();
+  bool is_period_overridden = false;
+  const int update_interval = cm->GetLastCheckPeriodSec(&is_period_overridden);
+  if (0 == update_interval) {
+    ASSERT1(is_period_overridden);
+    OPT_LOG(L1, (_T("[ShouldCheckForUpdates returned 0][checks disabled]")));
+    return false;
+  }
+
+  const int time_difference = cm->GetTimeSinceLastCheckedSec(is_machine);
+
+  const bool result = time_difference >= update_interval ? true : false;
+  CORE_LOG(L3, (_T("[ShouldCheckForUpdates returned %d][%u]"),
+                result, is_period_overridden));
+  return result;
+}
+
+HRESULT UpdateLastChecked(bool is_machine) {
+  // Set the last check value to the current value.
+  DWORD now = Time64ToInt32(GetCurrent100NSTime());
+  CORE_LOG(L3, (_T("[UpdateLastChecked][now %d]"), now));
+  HRESULT hr = ConfigManager::Instance()->SetLastCheckedTime(is_machine, now);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[SetLastCheckedTime failed][0x%08x]"), hr));
+    return hr;
+  }
+  return S_OK;
+}
+
+HRESULT LaunchUninstallProcess(bool is_machine) {
+  CORE_LOG(L2, (_T("[LaunchUninstallProcess]")));
+  CString exe_path = BuildGoogleUpdateExePath(is_machine);
+  CommandLineBuilder builder(COMMANDLINE_MODE_UNINSTALL);
+  CString cmd_line = builder.GetCommandLineArgs();
+  return System::StartProcessWithArgs(exe_path, cmd_line);
+}
+
+HANDLE GetImpersonationTokenForMachineProcess(bool is_machine) {
+  if (!is_machine) {
+    return NULL;
+  }
+
+  CAccessToken access_token;
+  if (access_token.GetThreadToken(TOKEN_READ)) {
+    return NULL;
+  }
+
+  bool is_local_system(false);
+  VERIFY1(SUCCEEDED(IsSystemProcess(&is_local_system)));
+  if (!is_local_system) {
+    return NULL;
+  }
+
+  HANDLE handle = NULL;
+  HRESULT hr = vista::GetLoggedOnUserToken(&handle);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[GetLoggedOnUserToken failed][0x%x]"), hr));
+    return NULL;
+  }
+
+  return handle;
+}
+
+HRESULT EnableSEHOP(bool enable) {
+  CORE_LOG(L3, (_T("[EnableSEHOP][%d]"), enable));
+  CString omaha_ifeo_key_path;
+  omaha_ifeo_key_path.Format(_T("%s\\%s"),
+                             kRegKeyImageFileExecutionOptions,
+                             kOmahaShellFileName);
+  return enable ?
+      RegKey::SetValue(omaha_ifeo_key_path, kRegKeyDisableSEHOPValue,
+                       static_cast<DWORD>(0)) :
+      RegKey::DeleteValue(omaha_ifeo_key_path, kRegKeyDisableSEHOPValue);
+}
+
+DEFINE_METRIC_count(opt_in_uid_generated);
+HRESULT CreateUserId(bool is_machine) {
+  // Do not create user ID when doing OEM installation - to avoid a large
+  // number of machines have the same ID.
+  if (oem_install_utils::IsOemInstalling(is_machine)) {
+    return E_FAIL;
+  }
+
+  GLock user_id_lock;
+  NamedObjectAttributes lock_attr;
+  GetNamedObjectAttributes(kOptUserIdLock, is_machine, &lock_attr);
+  if (!user_id_lock.InitializeWithSecAttr(lock_attr.name, &lock_attr.sa)) {
+    return E_FAIL;
+  }
+
+  __mutexScope(user_id_lock);
+  RegKey update_key;
+  const ConfigManager& config_manager = *ConfigManager::Instance();
+  HRESULT hr = update_key.Create(config_manager.registry_update(is_machine));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (update_key.HasValue(kRegValueUserId)) {
+    return S_OK;
+  }
+
+  CString user_id;
+  hr = GetGuid(&user_id);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = update_key.SetValue(kRegValueUserId, user_id);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  ++metric_opt_in_uid_generated;
+  CORE_LOG(L3, (_T("[Create unique user ID: %s]"), user_id));
+  return S_OK;
+}
+
+void DeleteUserId(bool is_machine) {
+  RegKey::DeleteValue(ConfigManager::Instance()->registry_update(is_machine),
+                      kRegValueUserId);
+}
+
+CString GetUserIdLazyInit(bool is_machine) {
+  const ConfigManager& config_manager = *ConfigManager::Instance();
+  if (oem_install_utils::IsOemInstalling(is_machine) ||
+      !config_manager.CanCollectStats(is_machine)) {
+    DeleteUserId(is_machine);
+    return CString();
+  }
+
+  if (!RegKey::HasValue(config_manager.registry_update(is_machine),
+                        kRegValueUserId)) {
+    VERIFY1(SUCCEEDED(CreateUserId(is_machine)));
+  }
+
+  CString user_id;
+  RegKey::GetValue(config_manager.registry_update(is_machine),
+                   kRegValueUserId,
+                   &user_id);
+  return user_id;
+}
+
+}  // namespace goopdate_utils
+
+}  // namespace omaha
diff --git a/common/goopdate_utils.h b/common/goopdate_utils.h
new file mode 100644
index 0000000..2cbdf9d
--- /dev/null
+++ b/common/goopdate_utils.h
@@ -0,0 +1,265 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_GOOPDATE_UTILS_H_
+#define OMAHA_COMMON_GOOPDATE_UTILS_H_
+
+#include <windows.h>
+#include <atlpath.h>
+#include <atlstr.h>
+#include <map>
+#include <vector>
+// TODO(omaha3): Move all browser related functions into browser_utils or some
+// similar file so we can avoid including browser_utils.h in this header. This
+// is especially important because of the duplicate BrowserType definition.
+#include "omaha/base/browser_utils.h"
+
+namespace omaha {
+
+class NetworkRequest;
+class UpdateResponse;
+struct NamedObjectAttributes;
+
+// Represents the Result of an attempt to terminate the browser.
+struct TerminateBrowserResult {
+  TerminateBrowserResult()
+      : found(false),
+        could_terminate(false) {
+  }
+
+  TerminateBrowserResult(bool f, bool terminate)
+      : found(f),
+        could_terminate(terminate) {
+  }
+
+  bool found;
+  bool could_terminate;
+};
+
+namespace goopdate_utils {
+
+typedef HRESULT (*RegisterOrUnregisterFunction)(void* data, bool is_register);
+
+// Builds the directory of the Google Update executable.
+CString BuildGoogleUpdateExeDir(bool is_machine);
+
+// Builds the path of the Google Update version found in the registry. The
+// command line is of the form "<install location>\googleupdate.exe"
+CString BuildGoogleUpdateExePath(bool is_machine);
+
+CString BuildGoogleUpdateServicesPath(bool is_machine);
+
+// Returns true if the currently executing binary is running from the
+// Machine/User Goopdate directory, or a directory under it.
+bool IsRunningFromOfficialGoopdateDir(bool is_machine);
+
+// If running the installed machine instance, returns HKLM. Else returns HKCU.
+CString GetHKRoot();
+
+// Starts an instance of the Google Update version found in the registry.
+// Only use to start interactive processes because it uses ::ShellExecuteEx().
+// args can be NULL.
+// process can be NULL. If not NULL, caller is responsible for closing handle.
+HRESULT StartGoogleUpdateWithArgs(bool is_machine,
+                                  const TCHAR* args,
+                                  HANDLE* process);
+
+// Starts an instance of GoogleCrashHandler.exe.
+HRESULT StartCrashHandler(bool is_machine);
+
+// Starts self in an elevated mode using the "Runas" verb.
+HRESULT StartElevatedSelfWithArgsAndWait(const TCHAR* args, DWORD* exit_code);
+
+// Registers security and sets the security values for the GoogleUpdate
+// process when running as a COM server.
+HRESULT InitializeSecurity();
+
+// GetProductName is temporary and must be removed after the TT release.
+// Gets the product name for a app guid.
+CString GetProductName(const CString& app_guid);
+
+// Returns true if it is a development or test machine.
+bool IsTestSource();
+
+HRESULT RedirectHKCR(bool is_machine);
+
+HRESULT RemoveRedirectHKCR();
+
+HRESULT RegisterTypeLib(bool is_admin,
+                        const CComBSTR& path,
+                        ITypeLib* type_lib);
+HRESULT UnRegisterTypeLib(bool is_admin,
+                          const CComBSTR&,
+                          ITypeLib* type_lib);
+
+HRESULT RegisterOrUnregisterModule(bool is_machine,
+                                   bool register_server,
+                                   RegisterOrUnregisterFunction registrar,
+                                   void* data);
+
+HRESULT RegisterOrUnregisterModuleWithTypelib(
+    bool is_machine,
+    bool register_server,
+    RegisterOrUnregisterFunction registrar,
+    void* data);
+
+// Registers the typelib that is passed in.
+// Wrapper for the RegisterTypeLibForUser that is defined in the
+// Vista oleaut32. Uses GetProcAddress to call into the method.
+HRESULT RegisterTypeLibForUser(ITypeLib* lib,
+                               OLECHAR* path,
+                               OLECHAR* help_dir);
+
+// Unregisters the typelib that is passed in.
+// Wrapper for the UnRegisterTypeLibForUser in Vista ole. Uses GetProcAddress
+// to call into the real method.
+HRESULT UnRegisterTypeLibForUser(REFGUID lib_id,
+                                 WORD major_ver_num,
+                                 WORD minor_ver_num,
+                                 LCID lcid,
+                                 SYSKIND syskind);
+
+CString GetCurrentVersionedName(bool is_machine,
+                                const TCHAR* value_name,
+                                const TCHAR* default_val);
+
+HRESULT CreateAndSetVersionedNameInRegistry(bool is_machine,
+                                            const TCHAR* prefix,
+                                            const TCHAR* value_name);
+
+// Returns the absolute path of the browser image.
+HRESULT GetBrowserImagePathFromProcess(BrowserType type,
+                                       uint32 explorer_pid,
+                                       CString* path);
+
+// Terminates all browser processes for the current user.
+HRESULT TerminateBrowserProcesses(BrowserType type,
+                                  TerminateBrowserResult* browser_res,
+                                  TerminateBrowserResult* default_res);
+
+// Terminates instances of all known browsers. Currently, the known browsers are
+// Firefox, IE and Chrome.
+HRESULT TerminateAllBrowsers(BrowserType type,
+                             TerminateBrowserResult* browser_res,
+                             TerminateBrowserResult* default_res);
+
+
+// Converts from string to the BrowserType enum.
+HRESULT ConvertStringToBrowserType(const CString& text, BrowserType* type);
+
+// Converts from BrowserType to string.
+CString ConvertBrowserTypeToString(BrowserType type);
+
+// Returns the browser to restart.
+bool GetBrowserToRestart(BrowserType type,
+                         BrowserType default_type,
+                         const TerminateBrowserResult& res,
+                         const TerminateBrowserResult& def_res,
+                         BrowserType* browser_type);
+
+// Obtains the OS version and service pack.
+HRESULT GetOSInfo(CString* os_version, CString* service_pack);
+
+// Returns the install directory for the specified version.
+CPath BuildInstallDirectory(bool is_machine, const CString& version);
+
+// Launches the command line. On Vista and later, for a machine install, this
+// method will launch the process at medium/low integrity, by impersonating the
+// medium integrity token of the active user.
+HRESULT LaunchCmdLine(bool is_machine, const CString& cmd_line);
+
+// Launches the browser. On Vista and later, for a machine install, this method
+// will launch the browser at medium/low integrity, by impersonating the medium
+// integrity token of the active user.
+HRESULT LaunchBrowser(bool is_machine, BrowserType type, const CString& url);
+
+// Gets a list of install worker processes relevant to user/machine instances.
+HRESULT GetInstallWorkerProcesses(bool is_machine,
+                                  std::vector<uint32>* processes);
+
+// Creates a unique event name and stores it in the specified environment var.
+HRESULT CreateUniqueEventInEnvironment(const CString& var_name,
+                                       bool is_machine,
+                                       HANDLE* unique_event);
+
+// Obtains a unique event name from specified environment var and opens it.
+HRESULT OpenUniqueEventFromEnvironment(const CString& var_name,
+                                       bool is_machine,
+                                       HANDLE* unique_event);
+
+// Creates an event based on the provided attributes.
+HRESULT CreateEvent(NamedObjectAttributes* event_attr, HANDLE* event_handle);
+
+HRESULT ReadNameValuePairsFromFile(const CString& file_path,
+                                   const CString& group_name,
+                                   std::map<CString, CString>* pairs);
+
+HRESULT WriteNameValuePairsToFile(const CString& file_path,
+                                  const CString& group_name,
+                                  const std::map<CString, CString>& pairs);
+
+// Returns true is any of the install workers is running.
+bool IsAppInstallWorkerRunning(bool is_machine);
+
+// Returns whether the version is an "Omaha 2" version or later.
+bool IsGoogleUpdate2OrLater(const CString& version);
+
+// Converts the installer_data value to UTF8. Then writes this UTF8 data
+// prefixed with the UTF8 BOM of EF BB BF to a temp file. Returns the path to
+// temp file that was created.  The returned path will be quote-enclosed by
+// EnclosePath().
+HRESULT WriteInstallerDataToTempFile(const CString& installer_data,
+                                     CString* installer_data_file_path);
+
+// TODO(omaha): Move these two to ua_internal.h.
+// Returns true if a server update check is due.
+bool ShouldCheckForUpdates(bool is_machine);
+
+// Updates LastChecked to now. Call after successful update check for all apps.
+HRESULT UpdateLastChecked(bool is_machine);
+
+// Launches the /uninstall process.
+HRESULT LaunchUninstallProcess(bool is_machine);
+
+// Returns a token that can be used to impersonate in the case of a
+// machine process. The caller has ownership of the token that is returned and
+// it must close the handle. The token corresponds to the primary token for
+// the current or one of the logged on users but only if the caller is a
+// machine process running as local system and not impersonated.
+// This is a very specialized function,intended to be called by local system
+// processes making network calls where the caller is not impersonated.
+HANDLE GetImpersonationTokenForMachineProcess(bool is_machine);
+
+// Enables or disables Structured Exception Handler Overwrite Protection a.k.a
+// SEHOP for machine Omaha. More information on SEHOP: http://goo.gl/1hfD.
+HRESULT EnableSEHOP(bool enable);
+
+// Creates a user unique id and saves it in registry if the machine is not in
+// the OEM install mode.
+HRESULT CreateUserId(bool is_machine);
+
+// Deletes the user id from registry.
+void DeleteUserId(bool is_machine);
+
+// Lazy creates (if necessary) and returns the user ID in registry if the
+// machine is NOT in OEM install state and current user opts in usage stats.
+// Otherwise deletes the user ID from the registry and returns empty string.
+CString GetUserIdLazyInit(bool is_machine);
+
+}  // namespace goopdate_utils
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_GOOPDATE_UTILS_H_
diff --git a/common/goopdate_utils_unittest.cc b/common/goopdate_utils_unittest.cc
new file mode 100644
index 0000000..0b2434c
--- /dev/null
+++ b/common/goopdate_utils_unittest.cc
@@ -0,0 +1,1630 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <windows.h>
+#include <atlpath.h>
+#include <atlsecurity.h>
+#include <atlstr.h>
+#include <map>
+#include <vector>
+#include "omaha/base/app_util.h"
+#include "omaha/base/browser_utils.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/const_utils.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/scoped_ptr_cotask.h"
+#include "omaha/base/string.h"
+#include "omaha/base/time.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/base/vista_utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/const_group_policy.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/oem_install_utils.h"
+#include "omaha/testing/resource.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+#define DUMMY_CLSID  _T("{6FC94136-0D4C-450e-99C2-BCDA72A9C8F0}")
+const TCHAR* hkcr_key_name = _T("HKCR\\CLSID\\") DUMMY_CLSID;
+const TCHAR* hklm_key_name = _T("HKLM\\Software\\Classes\\CLSID\\") DUMMY_CLSID;
+const TCHAR* hkcu_key_name = _T("HKCU\\Software\\Classes\\CLSID\\") DUMMY_CLSID;
+
+const TCHAR* kAppId = _T("{3DAE8C13-C394-481E-8163-4E7A7699084F}");
+
+}  // namespace
+
+namespace goopdate_utils {
+
+static void Cleanup() {
+  ASSERT_SUCCEEDED(RemoveRedirectHKCR());
+
+  RegKey::DeleteKey(hkcr_key_name, true);
+  RegKey::DeleteKey(hklm_key_name, true);
+  RegKey::DeleteKey(hkcu_key_name, true);
+}
+
+static void TestGetBrowserToRestart(BrowserType stamped,
+                                    bool found1,
+                                    bool killed1,
+                                    BrowserType def_browser,
+                                    bool found2,
+                                    bool killed2,
+                                    BrowserType expected) {
+  TerminateBrowserResult res(found1, killed1);
+  TerminateBrowserResult def(found2, killed2);
+
+  BrowserType type = BROWSER_UNKNOWN;
+  if (expected == BROWSER_UNKNOWN) {
+    EXPECT_FALSE(GetBrowserToRestart(stamped,
+                                     def_browser,
+                                     res,
+                                     def,
+                                     &type))
+        << _T("stamped: ") << stamped << _T(" ") << found1 << _T(" ") << killed1
+        << _T("   default: ") << def_browser << _T(" ") << found2 << _T(" ")
+        << killed2;
+  } else {
+    EXPECT_TRUE(GetBrowserToRestart(stamped,
+                                    def_browser,
+                                    res,
+                                    def,
+                                    &type))
+        << _T("stamped: ") << stamped << _T(" ") << found1 << _T(" ") << killed1
+        << _T("   default: ") << def_browser << _T(" ") << found2 << _T(" ")
+        << killed2;
+  }
+  EXPECT_EQ(expected, type)
+      << _T("stamped: ") << stamped << _T(" ") << found1 << _T(" ") << killed1
+      << _T("   default: ") << def_browser << _T(" ") << found2 << _T(" ")
+      << killed2;
+}
+
+// TerminateAllBrowsers is not tested with valid browser values because the
+// tests would terminate developers' browsers.
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_StampedUnknown) {
+  ExpectAsserts expect_asserts;
+  TestGetBrowserToRestart(BROWSER_UNKNOWN, false, false,
+                          BROWSER_IE, false, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_UNKNOWN, true, false,
+                          BROWSER_IE, false, false,
+                          BROWSER_IE);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_DefaultUnknown) {
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_UNKNOWN, false, false,
+                          BROWSER_UNKNOWN);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_StampedAndDefaultUnknown) {
+  ExpectAsserts expect_asserts;
+  TestGetBrowserToRestart(BROWSER_UNKNOWN, false, false,
+                          BROWSER_UNKNOWN, false, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_UNKNOWN, true, false,
+                          BROWSER_UNKNOWN, false, false,
+                          BROWSER_UNKNOWN);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_StampedDefault) {
+  ExpectAsserts expect_asserts;
+  TestGetBrowserToRestart(BROWSER_DEFAULT, false, false,
+                          BROWSER_IE, false, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_DEFAULT, true, false,
+                          BROWSER_IE, false, false,
+                          BROWSER_IE);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_DefaultDefault) {
+  ExpectAsserts expect_asserts;
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_DEFAULT, false, false,
+                          BROWSER_UNKNOWN);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_StampedAndDefaultDefault) {
+  ExpectAsserts expect_asserts;
+  TestGetBrowserToRestart(BROWSER_DEFAULT, false, false,
+                          BROWSER_DEFAULT, false, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_DEFAULT, true, false,
+                          BROWSER_DEFAULT, false, false,
+                          BROWSER_UNKNOWN);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_StampedMax) {
+  ExpectAsserts expect_asserts;
+  TestGetBrowserToRestart(BROWSER_MAX, false, false,
+                          BROWSER_IE, false, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_MAX, true, false,
+                          BROWSER_IE, false, false,
+                          BROWSER_IE);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_DefaultMax) {
+  ExpectAsserts expect_asserts;
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_MAX, false, false,
+                          BROWSER_UNKNOWN);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_StampedAndDefaultMax) {
+  ExpectAsserts expect_asserts;
+  TestGetBrowserToRestart(BROWSER_MAX, false, false,
+                          BROWSER_MAX, false, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_MAX, true, false,
+                          BROWSER_MAX, false, false,
+                          BROWSER_UNKNOWN);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeIE_DefaultIE) {
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_IE, false, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_IE, false, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_IE, true, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_IE, true, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, false, true,
+                          BROWSER_IE, false, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, false, true,
+                          BROWSER_IE, false, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, false, true,
+                          BROWSER_IE, true, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, false, true,
+                          BROWSER_IE, true, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, false,
+                          BROWSER_IE, false, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, false,
+                          BROWSER_IE, false, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, false,
+                          BROWSER_IE, true, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, false,
+                          BROWSER_IE, true, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, true,
+                          BROWSER_IE, false, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, true,
+                          BROWSER_IE, false, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, true,
+                          BROWSER_IE, true, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, true,
+                          BROWSER_IE, true, true,
+                          BROWSER_IE);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeIE_DefaultFirefox) {
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_FIREFOX, false, false,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_FIREFOX, false, true,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_FIREFOX, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_FIREFOX, true, true,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_IE, false, true,
+                          BROWSER_FIREFOX, false, false,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_IE, false, true,
+                          BROWSER_FIREFOX, false, true,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_IE, false, true,
+                          BROWSER_FIREFOX, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_IE, false, true,
+                          BROWSER_FIREFOX, true, true,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_IE, true, false,
+                          BROWSER_FIREFOX, false, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, false,
+                          BROWSER_FIREFOX, false, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, false,
+                          BROWSER_FIREFOX, true, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, false,
+                          BROWSER_FIREFOX, true, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, true,
+                          BROWSER_FIREFOX, false, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, true,
+                          BROWSER_FIREFOX, false, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, true,
+                          BROWSER_FIREFOX, true, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, true,
+                          BROWSER_FIREFOX, true, true,
+                          BROWSER_IE);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeIE_DefaultChrome) {
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_CHROME, false, false,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_CHROME, false, true,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_CHROME, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_CHROME, true, true,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_IE, false, true,
+                          BROWSER_CHROME, false, false,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_IE, false, true,
+                          BROWSER_CHROME, false, true,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_IE, false, true,
+                          BROWSER_CHROME, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_IE, false, true,
+                          BROWSER_CHROME, true, true,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_IE, true, false,
+                          BROWSER_CHROME, false, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, false,
+                          BROWSER_CHROME, false, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, false,
+                          BROWSER_CHROME, true, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, false,
+                          BROWSER_CHROME, true, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, true,
+                          BROWSER_CHROME, false, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, true,
+                          BROWSER_CHROME, false, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, true,
+                          BROWSER_CHROME, true, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, true,
+                          BROWSER_CHROME, true, true,
+                          BROWSER_IE);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeIE_DefaultUnknown) {
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_UNKNOWN, false, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_UNKNOWN, false, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_UNKNOWN, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_IE, false, false,
+                          BROWSER_UNKNOWN, true, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_IE, false, true,
+                          BROWSER_UNKNOWN, false, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_IE, false, true,
+                          BROWSER_UNKNOWN, false, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_IE, false, true,
+                          BROWSER_UNKNOWN, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_IE, false, true,
+                          BROWSER_UNKNOWN, true, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_IE, true, false,
+                          BROWSER_UNKNOWN, false, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, false,
+                          BROWSER_UNKNOWN, false, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, false,
+                          BROWSER_UNKNOWN, true, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, false,
+                          BROWSER_UNKNOWN, true, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, true,
+                          BROWSER_UNKNOWN, false, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, true,
+                          BROWSER_UNKNOWN, false, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, true,
+                          BROWSER_UNKNOWN, true, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_IE, true, true,
+                          BROWSER_UNKNOWN, true, true,
+                          BROWSER_IE);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeFirefox_DefaultIE) {
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
+                          BROWSER_IE, false, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
+                          BROWSER_IE, false, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
+                          BROWSER_IE, true, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
+                          BROWSER_IE, true, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
+                          BROWSER_IE, false, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
+                          BROWSER_IE, false, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
+                          BROWSER_IE, true, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
+                          BROWSER_IE, true, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
+                          BROWSER_IE, false, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
+                          BROWSER_IE, false, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
+                          BROWSER_IE, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
+                          BROWSER_IE, true, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
+                          BROWSER_IE, false, false,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
+                          BROWSER_IE, false, true,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
+                          BROWSER_IE, true, false,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
+                          BROWSER_IE, true, true,
+                          BROWSER_FIREFOX);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeFirefox_DefaultFirefox) {
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
+                          BROWSER_FIREFOX, false, false,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
+                          BROWSER_FIREFOX, false, true,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
+                          BROWSER_FIREFOX, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
+                          BROWSER_FIREFOX, true, true,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
+                          BROWSER_FIREFOX, false, false,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
+                          BROWSER_FIREFOX, false, true,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
+                          BROWSER_FIREFOX, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
+                          BROWSER_FIREFOX, true, true,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
+                          BROWSER_FIREFOX, false, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
+                          BROWSER_FIREFOX, false, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
+                          BROWSER_FIREFOX, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
+                          BROWSER_FIREFOX, true, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
+                          BROWSER_FIREFOX, false, false,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
+                          BROWSER_FIREFOX, false, true,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
+                          BROWSER_FIREFOX, true, false,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
+                          BROWSER_FIREFOX, true, true,
+                          BROWSER_FIREFOX);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeFirefox_DefaultChrome) {
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
+                          BROWSER_CHROME, false, false,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
+                          BROWSER_CHROME, false, true,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
+                          BROWSER_CHROME, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
+                          BROWSER_CHROME, true, true,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
+                          BROWSER_CHROME, false, false,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
+                          BROWSER_CHROME, false, true,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
+                          BROWSER_CHROME, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
+                          BROWSER_CHROME, true, true,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
+                          BROWSER_CHROME, false, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
+                          BROWSER_CHROME, false, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
+                          BROWSER_CHROME, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
+                          BROWSER_CHROME, true, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
+                          BROWSER_CHROME, false, false,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
+                          BROWSER_CHROME, false, true,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
+                          BROWSER_CHROME, true, false,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
+                          BROWSER_CHROME, true, true,
+                          BROWSER_FIREFOX);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeFirefox_DefaultUnknown) {
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
+                          BROWSER_UNKNOWN, false, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
+                          BROWSER_UNKNOWN, false, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
+                          BROWSER_UNKNOWN, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
+                          BROWSER_UNKNOWN, true, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
+                          BROWSER_UNKNOWN, false, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
+                          BROWSER_UNKNOWN, false, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
+                          BROWSER_UNKNOWN, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
+                          BROWSER_UNKNOWN, true, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
+                          BROWSER_UNKNOWN, false, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
+                          BROWSER_UNKNOWN, false, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
+                          BROWSER_UNKNOWN, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
+                          BROWSER_UNKNOWN, true, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
+                          BROWSER_UNKNOWN, false, false,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
+                          BROWSER_UNKNOWN, false, true,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
+                          BROWSER_UNKNOWN, true, false,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
+                          BROWSER_UNKNOWN, true, true,
+                          BROWSER_FIREFOX);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeChrome_DefaultIE) {
+  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
+                          BROWSER_IE, false, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
+                          BROWSER_IE, false, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
+                          BROWSER_IE, true, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
+                          BROWSER_IE, true, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
+                          BROWSER_IE, false, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
+                          BROWSER_IE, false, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
+                          BROWSER_IE, true, false,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
+                          BROWSER_IE, true, true,
+                          BROWSER_IE);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
+                          BROWSER_IE, false, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
+                          BROWSER_IE, false, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
+                          BROWSER_IE, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
+                          BROWSER_IE, true, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
+                          BROWSER_IE, false, false,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
+                          BROWSER_IE, false, true,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
+                          BROWSER_IE, true, false,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
+                          BROWSER_IE, true, true,
+                          BROWSER_CHROME);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeChrome_DefaultFirefox) {
+  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
+                          BROWSER_FIREFOX, false, false,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
+                          BROWSER_FIREFOX, false, true,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
+                          BROWSER_FIREFOX, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
+                          BROWSER_FIREFOX, true, true,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
+                          BROWSER_FIREFOX, false, false,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
+                          BROWSER_FIREFOX, false, true,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
+                          BROWSER_FIREFOX, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
+                          BROWSER_FIREFOX, true, true,
+                          BROWSER_FIREFOX);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
+                          BROWSER_FIREFOX, false, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
+                          BROWSER_FIREFOX, false, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
+                          BROWSER_FIREFOX, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
+                          BROWSER_FIREFOX, true, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
+                          BROWSER_FIREFOX, false, false,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
+                          BROWSER_FIREFOX, false, true,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
+                          BROWSER_FIREFOX, true, false,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
+                          BROWSER_FIREFOX, true, true,
+                          BROWSER_CHROME);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeChrome_DefaultChrome) {
+  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
+                          BROWSER_CHROME, false, false,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
+                          BROWSER_CHROME, false, true,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
+                          BROWSER_CHROME, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
+                          BROWSER_CHROME, true, true,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
+                          BROWSER_CHROME, false, false,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
+                          BROWSER_CHROME, false, true,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
+                          BROWSER_CHROME, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
+                          BROWSER_CHROME, true, true,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
+                          BROWSER_CHROME, false, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
+                          BROWSER_CHROME, false, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
+                          BROWSER_CHROME, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
+                          BROWSER_CHROME, true, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
+                          BROWSER_CHROME, false, false,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
+                          BROWSER_CHROME, false, true,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
+                          BROWSER_CHROME, true, false,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
+                          BROWSER_CHROME, true, true,
+                          BROWSER_CHROME);
+}
+
+TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeChrome_DefaultUnknown) {
+  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
+                          BROWSER_UNKNOWN, false, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
+                          BROWSER_UNKNOWN, false, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
+                          BROWSER_UNKNOWN, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
+                          BROWSER_UNKNOWN, true, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
+                          BROWSER_UNKNOWN, false, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
+                          BROWSER_UNKNOWN, false, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
+                          BROWSER_UNKNOWN, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
+                          BROWSER_UNKNOWN, true, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
+                          BROWSER_UNKNOWN, false, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
+                          BROWSER_UNKNOWN, false, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
+                          BROWSER_UNKNOWN, true, false,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
+                          BROWSER_UNKNOWN, true, true,
+                          BROWSER_UNKNOWN);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
+                          BROWSER_UNKNOWN, false, false,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
+                          BROWSER_UNKNOWN, false, true,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
+                          BROWSER_UNKNOWN, true, false,
+                          BROWSER_CHROME);
+  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
+                          BROWSER_UNKNOWN, true, true,
+                          BROWSER_CHROME);
+}
+
+TEST(GoopdateUtilsTest, ConvertStringToBrowserType) {
+  BrowserType type = BROWSER_UNKNOWN;
+  ASSERT_SUCCEEDED(ConvertStringToBrowserType(_T("0"), &type));
+  ASSERT_EQ(BROWSER_UNKNOWN, type);
+
+  ASSERT_SUCCEEDED(ConvertStringToBrowserType(_T("1"), &type));
+  ASSERT_EQ(BROWSER_DEFAULT, type);
+
+  ASSERT_SUCCEEDED(ConvertStringToBrowserType(_T("2"), &type));
+  ASSERT_EQ(BROWSER_IE, type);
+
+  ASSERT_SUCCEEDED(ConvertStringToBrowserType(_T("3"), &type));
+  ASSERT_EQ(BROWSER_FIREFOX, type);
+
+  ASSERT_SUCCEEDED(ConvertStringToBrowserType(_T("4"), &type));
+  ASSERT_EQ(BROWSER_CHROME, type);
+
+  ASSERT_FAILED(ConvertStringToBrowserType(_T("5"), &type));
+  ASSERT_FAILED(ConvertStringToBrowserType(_T("asdf"), &type));
+  ASSERT_FAILED(ConvertStringToBrowserType(_T("234"), &type));
+  ASSERT_FAILED(ConvertStringToBrowserType(_T("-1"), &type));
+}
+
+TEST(GoopdateUtilsTest, RedirectHKCRTest) {
+  RegKey key;
+  Cleanup();
+
+  if (vista_util::IsUserAdmin()) {
+    // Only run this part of the test for Admins, because non-admins cannot
+    // write to HKLM.
+
+    // Without redirection, a HKCR write should write HKLM\Software\Classes,
+    // assuming that the key does not already exist in HKCU.
+    ASSERT_SUCCEEDED(key.Create(hkcr_key_name));
+    ASSERT_TRUE(RegKey::HasKey(hklm_key_name));
+    ASSERT_FALSE(RegKey::HasKey(hkcu_key_name));
+
+    Cleanup();
+
+    ASSERT_SUCCEEDED(RedirectHKCR(true));
+
+    // With HKLM redirection, a HKCR write should write HKLM\Software\Classes.
+    ASSERT_SUCCEEDED(key.Create(hkcr_key_name));
+    ASSERT_TRUE(RegKey::HasKey(hklm_key_name));
+    ASSERT_FALSE(RegKey::HasKey(hkcu_key_name));
+
+    Cleanup();
+  } else {
+    std::wcout << _T("\tPart of this test did not run because the user ")
+                  _T("is not an admin.") << std::endl;
+  }
+
+  ASSERT_SUCCEEDED(RedirectHKCR(false));
+
+  // With HKCU redirection, a HKCR write should write HKCU\Software\Classes.
+  ASSERT_SUCCEEDED(key.Create(hkcr_key_name));
+  ASSERT_FALSE(RegKey::HasKey(hklm_key_name));
+  ASSERT_TRUE(RegKey::HasKey(hkcu_key_name));
+
+  ASSERT_SUCCEEDED(RemoveRedirectHKCR());
+
+  if (vista_util::IsUserAdmin()) {
+    // Without redirection, the following HKCR writes should write
+    // HKCU\Software\Classes.
+    // This is because the key already exists in HKCU from the writes above.
+    ASSERT_SUCCEEDED(key.Create(hkcr_key_name));
+    ASSERT_EQ(user_info::IsRunningAsSystem(), RegKey::HasKey(hklm_key_name));
+    ASSERT_TRUE(RegKey::HasKey(hkcu_key_name));
+  } else {
+    std::wcout << _T("\tPart of this test did not run because the user ")
+                  _T("is not an admin.") << std::endl;
+  }
+
+  Cleanup();
+}
+
+TEST(GoopdateUtilsTest, GetOSInfo) {
+  CString os_version;
+  CString service_pack;
+  EXPECT_SUCCEEDED(GetOSInfo(&os_version, &service_pack));
+  EXPECT_TRUE(!os_version.IsEmpty());
+}
+
+class GoopdateUtilsRegistryProtectedTest : public testing::Test {
+ protected:
+  GoopdateUtilsRegistryProtectedTest()
+      : hive_override_key_name_(kRegistryHiveOverrideRoot) {
+  }
+
+  CString hive_override_key_name_;
+
+  virtual void SetUp() {
+    RegKey::DeleteKey(hive_override_key_name_, true);
+    OverrideRegistryHives(hive_override_key_name_);
+  }
+
+  virtual void TearDown() {
+    RestoreRegistryHives();
+    ASSERT_SUCCEEDED(RegKey::DeleteKey(hive_override_key_name_, true));
+  }
+};
+
+class GoopdateUtilsRegistryProtectedBooleanTest
+    : public ::testing::TestWithParam<bool> {
+ protected:
+  GoopdateUtilsRegistryProtectedBooleanTest()
+      : hive_override_key_name_(kRegistryHiveOverrideRoot) {
+  }
+
+  CString hive_override_key_name_;
+
+  virtual void SetUp() {
+    RegKey::DeleteKey(hive_override_key_name_, true);
+    OverrideRegistryHives(hive_override_key_name_);
+  }
+
+  virtual void TearDown() {
+    RestoreRegistryHives();
+    ASSERT_SUCCEEDED(RegKey::DeleteKey(hive_override_key_name_, true));
+  }
+};
+
+// Some methods used by goopdate_utils rely on registry entries that are
+// overridden in the registry, so we need to write it.
+class GoopdateUtilsRegistryProtectedWithMachineFolderPathsTest
+    : public GoopdateUtilsRegistryProtectedTest {
+ protected:
+  virtual void SetUp() {
+    // The tests start GoogleUpdate processes running as user and these
+    // processes need the following registry value.
+    ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                      kRegValueInstalledVersion,
+                                      GetVersionString()));
+
+    GoopdateUtilsRegistryProtectedTest::SetUp();
+
+    // Creates a registry value for the Windows shell functions to work when
+    // the registry hives are redirected.
+    const TCHAR kWindowsCurrentVersionKeyPath[] =
+        _T("HKLM\\Software\\Microsoft\\Windows\\CurrentVersion");
+    const TCHAR kProgramFilesDirValueName[] = _T("ProgramFilesDir");
+    const TCHAR kProgramFilesPath[] = _T("C:\\Program Files");
+    ASSERT_SUCCEEDED(RegKey::SetValue(kWindowsCurrentVersionKeyPath,
+                                      kProgramFilesDirValueName,
+                                      kProgramFilesPath));
+  }
+};
+
+// Some methods used by goopdate_utils rely on registry entries that are
+// overridden in the registry, so we need to write it.
+class GoopdateUtilsRegistryProtectedWithUserFolderPathsTest
+    : public GoopdateUtilsRegistryProtectedTest {
+ protected:
+  virtual void SetUp() {
+  const TCHAR kUserShellKeyPath[] =
+        _T("HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\")
+        _T("User Shell Folders");
+    const TCHAR kLocalAppDataValueDirName[] = _T("Local AppData");
+    const TCHAR kLocalAppDataPath[] =
+        _T("%USERPROFILE%\\Local Settings\\Application Data");
+
+    GoopdateUtilsRegistryProtectedTest::SetUp();
+    ASSERT_SUCCEEDED(RegKey::SetValueExpandSZ(kUserShellKeyPath,
+                                              kLocalAppDataValueDirName,
+                                              kLocalAppDataPath));
+  }
+};
+
+class VersionProtectedTest : public RegistryProtectedTest {
+ protected:
+  VersionProtectedTest()
+      : RegistryProtectedTest(),
+        module_version_(GetVersion()) {
+  }
+
+  virtual void SetUp() {
+    RegistryProtectedTest::SetUp();
+    InitializeVersion(kFakeVersion);
+  }
+
+  virtual void TearDown() {
+    InitializeVersion(module_version_);
+    RegistryProtectedTest::TearDown();
+  }
+
+  const ULONGLONG module_version_;
+  static const ULONGLONG kFakeVersion = 0x0005000600070008;
+};
+
+// pv should be ignored.
+TEST_F(GoopdateUtilsRegistryProtectedWithMachineFolderPathsTest,
+       BuildGoogleUpdateExePath_MachineVersionFound) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENTS_GOOPDATE,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  CString path = BuildGoogleUpdateExePath(true);
+  CString program_files_path;
+  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &program_files_path));
+  EXPECT_STREQ(program_files_path + _T("\\") + SHORT_COMPANY_NAME +
+               _T("\\") + PRODUCT_NAME + _T("\\GoogleUpdate.exe"),
+               path);
+}
+
+TEST_F(GoopdateUtilsRegistryProtectedWithMachineFolderPathsTest,
+       BuildGoogleUpdateExePath_MachineVersionNotFound) {
+  // Test when the key doesn't exist.
+  CString path = BuildGoogleUpdateExePath(true);
+  CString program_files_path;
+  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &program_files_path));
+  EXPECT_STREQ(program_files_path + _T("\\") + SHORT_COMPANY_NAME +
+               _T("\\") + PRODUCT_NAME + _T("\\GoogleUpdate.exe"),
+               path);
+
+  // Test when the key exists but the value doesn't.
+  ASSERT_SUCCEEDED(RegKey::CreateKey(MACHINE_REG_CLIENTS_GOOPDATE));
+  path = BuildGoogleUpdateExePath(true);
+  EXPECT_STREQ(program_files_path + _T("\\") + SHORT_COMPANY_NAME +
+               _T("\\") + PRODUCT_NAME + _T("\\GoogleUpdate.exe"),
+               path);
+}
+
+// pv should be ignored.
+TEST_F(GoopdateUtilsRegistryProtectedWithUserFolderPathsTest,
+       BuildGoogleUpdateExePath_UserVersionFound) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENTS_GOOPDATE,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  CString path = BuildGoogleUpdateExePath(false);
+
+  CString user_appdata;
+  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_LOCAL_APPDATA, &user_appdata));
+  CString expected_path;
+  expected_path.Format(_T("%s\\") SHORT_COMPANY_NAME _T("\\")
+                       PRODUCT_NAME _T("\\GoogleUpdate.exe"),
+                       user_appdata);
+  EXPECT_STREQ(expected_path, path);
+}
+
+TEST_F(GoopdateUtilsRegistryProtectedWithUserFolderPathsTest,
+       BuildGoogleUpdateExePath_UserVersionNotFound) {
+  CString user_appdata;
+  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_LOCAL_APPDATA, &user_appdata));
+  CString expected_path;
+  expected_path.Format(_T("%s\\") SHORT_COMPANY_NAME _T("\\")
+                       PRODUCT_NAME _T("\\GoogleUpdate.exe"),
+                       user_appdata);
+
+  // Test when the key doesn't exist.
+  CString path = BuildGoogleUpdateExePath(false);
+  EXPECT_STREQ(expected_path, path);
+
+  // Test when the key exists but the value doesn't.
+  ASSERT_SUCCEEDED(RegKey::CreateKey(USER_REG_CLIENTS_GOOPDATE));
+  path = BuildGoogleUpdateExePath(false);
+  EXPECT_STREQ(expected_path, path);
+}
+
+// The version is no longer used by StartGoogleUpdateWithArgs, so the return
+// value depends on whether program_files\Google\Update\GoogleUpdate.exe exists.
+// The arguments must be valid to avoid displaying invalid command line error.
+TEST_F(GoopdateUtilsRegistryProtectedWithMachineFolderPathsTest,
+       StartGoogleUpdateWithArgs_MachineVersionVersionDoesNotExist) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENTS_GOOPDATE,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+  const TCHAR* kArgs = _T("/cr");
+  HRESULT hr = StartGoogleUpdateWithArgs(true, kArgs, NULL);
+  EXPECT_TRUE(S_OK == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr);
+}
+
+// The version is no longer used by StartGoogleUpdateWithArgs, so the return
+// value depends on whether <user_folder>\Google\Update\GoogleUpdate.exe exists.
+// The arguments must be valid to avoid displaying invalid command line error.
+TEST_F(GoopdateUtilsRegistryProtectedWithUserFolderPathsTest,
+       StartGoogleUpdateWithArgs_UserVersionVersionDoesNotExist) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENTS_GOOPDATE,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+  const TCHAR* kArgs = _T("/cr");
+  HRESULT hr = StartGoogleUpdateWithArgs(false, kArgs, NULL);
+  EXPECT_TRUE(S_OK == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr);
+}
+
+TEST(GoopdateUtilsTest, BuildInstallDirectory_Machine) {
+  const CPath dir = BuildInstallDirectory(true, _T("1.2.3.0"));
+  CString program_files_path;
+  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &program_files_path));
+  EXPECT_STREQ(program_files_path + _T("\\") + SHORT_COMPANY_NAME +
+               _T("\\") + PRODUCT_NAME + _T("\\1.2.3.0"), dir);
+}
+
+TEST(GoopdateUtilsTest, BuildInstallDirectory_User) {
+  CPath expected_path(GetGoogleUpdateUserPath());
+  expected_path.Append(_T("4.5.6.7"));
+  EXPECT_STREQ(expected_path,
+               BuildInstallDirectory(false, _T("4.5.6.7")));
+}
+
+TEST(GoopdateUtilsTest, ConvertBrowserTypeToString) {
+  for (int i = 0; i < BROWSER_MAX; ++i) {
+    CString str_type = ConvertBrowserTypeToString(
+        static_cast<BrowserType>(i));
+    BrowserType type = BROWSER_UNKNOWN;
+    ASSERT_HRESULT_SUCCEEDED(
+        ConvertStringToBrowserType(str_type, &type));
+    ASSERT_EQ(static_cast<int>(type), i);
+  }
+}
+
+TEST(GoopdateUtilsTest, UniqueEventInEnvironment_User) {
+  const TCHAR* kEnvVarName = _T("SOME_ENV_VAR_FOR_TEST");
+  scoped_event created_event;
+  scoped_event opened_event;
+
+  ASSERT_HRESULT_SUCCEEDED(CreateUniqueEventInEnvironment(
+      kEnvVarName,
+      false,
+      address(created_event)));
+  ASSERT_TRUE(created_event);
+  EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(get(created_event), 0));
+
+  TCHAR event_name[MAX_PATH] = {0};
+  EXPECT_TRUE(
+      ::GetEnvironmentVariable(kEnvVarName, event_name, arraysize(event_name)));
+
+  ASSERT_HRESULT_SUCCEEDED(OpenUniqueEventFromEnvironment(
+      kEnvVarName,
+      false,
+      address(opened_event)));
+  ASSERT_TRUE(opened_event);
+
+  EXPECT_TRUE(::SetEvent(get(opened_event)));
+  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(created_event), 0));
+
+  EXPECT_TRUE(::SetEnvironmentVariable(kEnvVarName, NULL));
+}
+
+TEST(GoopdateUtilsTest, UniqueEventInEnvironment_Machine) {
+  const TCHAR* kEnvVarName = _T("OTHER_ENV_VAR_FOR_TEST");
+  scoped_event created_event;
+  scoped_event opened_event;
+  TCHAR event_name[MAX_PATH] = {0};
+
+  if (!vista_util::IsUserAdmin()) {
+    EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INVALID_OWNER),
+              CreateUniqueEventInEnvironment(kEnvVarName,
+                                             true,
+                                             address(created_event)));
+    EXPECT_FALSE(created_event);
+
+    EXPECT_FALSE(::GetEnvironmentVariable(kEnvVarName,
+                                          event_name,
+                                          arraysize(event_name)));
+    return;
+  }
+
+  ASSERT_HRESULT_SUCCEEDED(CreateUniqueEventInEnvironment(
+      kEnvVarName,
+      true,
+      address(created_event)));
+  ASSERT_TRUE(created_event);
+  EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(get(created_event), 0));
+
+  EXPECT_TRUE(
+      ::GetEnvironmentVariable(kEnvVarName, event_name, arraysize(event_name)));
+
+  ASSERT_HRESULT_SUCCEEDED(OpenUniqueEventFromEnvironment(
+      kEnvVarName,
+      true,
+      address(opened_event)));
+  ASSERT_TRUE(opened_event);
+
+  EXPECT_TRUE(::SetEvent(get(opened_event)));
+  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(created_event), 0));
+
+  EXPECT_TRUE(::SetEnvironmentVariable(kEnvVarName, NULL));
+}
+
+TEST(GoopdateUtilsTest, UniqueEventInEnvironment_UserMachineMismatch) {
+  const TCHAR* kEnvVarName = _T("ENV_VAR_FOR_MIXED_TEST");
+  scoped_event created_event;
+  scoped_event opened_event;
+
+  ASSERT_HRESULT_SUCCEEDED(CreateUniqueEventInEnvironment(
+      kEnvVarName,
+      false,
+      address(created_event)));
+  ASSERT_TRUE(created_event);
+  EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(get(created_event), 0));
+
+  TCHAR event_name[MAX_PATH] = {0};
+  EXPECT_TRUE(
+      ::GetEnvironmentVariable(kEnvVarName, event_name, arraysize(event_name)));
+
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            OpenUniqueEventFromEnvironment(kEnvVarName,
+                                           true,
+                                           address(opened_event)));
+
+  EXPECT_TRUE(::SetEnvironmentVariable(kEnvVarName, NULL));
+}
+
+TEST(GoopdateUtilsTest, OpenUniqueEventFromEnvironment_EnvVarDoesNotExist) {
+  const TCHAR* kEnvVarName = _T("ANOTHER_ENV_VAR_FOR_TEST");
+  scoped_event opened_event;
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND),
+            OpenUniqueEventFromEnvironment(kEnvVarName,
+                                           false,
+                                           address(opened_event)));
+}
+
+TEST(GoopdateUtilsTest, OpenUniqueEventFromEnvironment_EventDoesNotExist) {
+  const TCHAR* kEnvVarName = _T("YET_ANOTHER_ENV_VAR_FOR_TEST");
+
+  EXPECT_TRUE(::SetEnvironmentVariable(kEnvVarName, _T("foo")));
+
+  scoped_event opened_event;
+    EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+              OpenUniqueEventFromEnvironment(kEnvVarName,
+                                             false,
+                                             address(opened_event)));
+
+  EXPECT_TRUE(::SetEnvironmentVariable(kEnvVarName, NULL));
+}
+
+
+CString GetTempFile() {
+  TCHAR temp_path[MAX_PATH] = {0};
+  TCHAR temp_file[MAX_PATH] = {0};
+
+  EXPECT_LT(::GetTempPath(arraysize(temp_path), temp_path),
+            arraysize(temp_path));
+  EXPECT_NE(0, ::GetTempFileName(temp_path, _T("ut_"), 0, temp_file));
+  return CString(temp_file);
+}
+
+typedef std::map<CString, CString> StringMap;
+typedef StringMap::const_iterator StringMapIter;
+
+TEST(GoopdateUtilsTest, ReadNameValuePairsFromFileTest_MissingFile) {
+  CString temp_file = GetTempFile();
+  ::DeleteFile(temp_file);
+
+  ASSERT_FALSE(File::Exists(temp_file));
+
+  StringMap pairs_read;
+  ASSERT_FAILED(ReadNameValuePairsFromFile(temp_file,
+                                           _T("my_group"),
+                                           &pairs_read));
+  ASSERT_EQ(0, pairs_read.size());
+}
+
+TEST(GoopdateUtilsTest, ReadNameValuePairsFromFileTest_ReadEmpty) {
+  CString temp_file = GetTempFile();
+  ON_SCOPE_EXIT(::DeleteFile, temp_file.GetString());
+  File file_write;
+  EXPECT_SUCCEEDED(file_write.Open(temp_file, true, false));
+  file_write.Close();
+
+  StringMap pairs_read;
+  ASSERT_SUCCEEDED(ReadNameValuePairsFromFile(temp_file,
+                                              _T("my_group"),
+                                              &pairs_read));
+  ASSERT_EQ(0, pairs_read.size());
+}
+
+void ValidateStringMapEquality(const StringMap& expected,
+                               const StringMap& actual) {
+  ASSERT_EQ(expected.size(), actual.size());
+
+  StringMapIter it_expected = expected.begin();
+  for (; it_expected != expected.end(); ++it_expected) {
+    StringMapIter it_actual = actual.find(it_expected->first);
+    ASSERT_TRUE(it_actual != actual.end());
+    ASSERT_STREQ(it_expected->second, it_actual->second);
+  }
+}
+
+TEST(GoopdateUtilsTest, ReadNameValuePairsFromFileTest_ReadOnePair) {
+  CString group = _T("my_group");
+
+  StringMap pairs_write;
+  pairs_write[_T("some_name")] = _T("some_value");
+
+  CString temp_file = GetTempFile();
+  ON_SCOPE_EXIT(::DeleteFile, temp_file.GetString());
+  ASSERT_SUCCEEDED(WriteNameValuePairsToFile(temp_file, group, pairs_write));
+  ASSERT_TRUE(File::Exists(temp_file));
+
+  StringMap pairs_read;
+  ASSERT_SUCCEEDED(ReadNameValuePairsFromFile(temp_file, group, &pairs_read));
+
+  ValidateStringMapEquality(pairs_write, pairs_read);
+}
+
+TEST(GoopdateUtilsTest, ReadNameValuePairsFromFileTest_ReadManyPairs) {
+  CString group = _T("my_group");
+
+  StringMap pairs_write;
+  const int kCountPairs = 10;
+  for (int i = 1; i <= kCountPairs; ++i) {
+    CString name;
+    name.Format(_T("name%d"), i);
+    CString value;
+    value.Format(_T("value%d"), i);
+    pairs_write[name] = value;
+  }
+
+  CString temp_file = GetTempFile();
+  ON_SCOPE_EXIT(::DeleteFile, temp_file.GetString());
+  ASSERT_SUCCEEDED(WriteNameValuePairsToFile(temp_file, group, pairs_write));
+  ASSERT_TRUE(File::Exists(temp_file));
+
+  StringMap pairs_read;
+  ASSERT_SUCCEEDED(ReadNameValuePairsFromFile(temp_file, group, &pairs_read));
+
+  ValidateStringMapEquality(pairs_write, pairs_read);
+}
+
+TEST(GoopdateUtilsTest, IsGoogleUpdate2OrLater_LegacyVersions) {
+  EXPECT_FALSE(IsGoogleUpdate2OrLater(_T("1.0.0.0")));
+  EXPECT_FALSE(IsGoogleUpdate2OrLater(_T("1.1.103.9")));
+  EXPECT_FALSE(IsGoogleUpdate2OrLater(_T("1.1.65535.65535")));
+}
+
+TEST(GoopdateUtilsTest, IsGoogleUpdate2OrLater_Omaha2AndLater) {
+  EXPECT_TRUE(IsGoogleUpdate2OrLater(_T("1.2.0.0")));
+  EXPECT_TRUE(IsGoogleUpdate2OrLater(_T("1.2.0111.2222")));
+  EXPECT_TRUE(IsGoogleUpdate2OrLater(_T("1.3.456.7890")));
+  EXPECT_TRUE(IsGoogleUpdate2OrLater(_T("2.0.0.0")));
+}
+
+TEST(GoopdateUtilsTest, IsGoogleUpdate2OrLater_VersionZero) {
+  ExpectAsserts expect_asserts;
+  EXPECT_FALSE(IsGoogleUpdate2OrLater(_T("0.0.0.0")));
+}
+
+TEST(GoopdateUtilsTest, IsGoogleUpdate2OrLater_VersionUpperLimits) {
+  EXPECT_TRUE(IsGoogleUpdate2OrLater(_T("65535.65535.65535.65535")));
+
+  ExpectAsserts expect_asserts;
+  EXPECT_FALSE(IsGoogleUpdate2OrLater(_T("65536.65536.65536.65536")));
+  EXPECT_FALSE(IsGoogleUpdate2OrLater(_T("1.2.65536.65536")));
+  EXPECT_FALSE(IsGoogleUpdate2OrLater(_T("1.1.65536.65536")));
+}
+
+TEST(GoopdateUtilsTest, IsGoogleUpdate2OrLater_TooFewElements) {
+  ExpectAsserts expect_asserts;
+  EXPECT_FALSE(IsGoogleUpdate2OrLater(_T("1.1.1")));
+}
+
+TEST(GoopdateUtilsTest, IsGoogleUpdate2OrLater_ExtraPeriod) {
+  ExpectAsserts expect_asserts;
+  EXPECT_FALSE(IsGoogleUpdate2OrLater(_T("1.1.2.3.")));
+}
+
+TEST(GoopdateUtilsTest, IsGoogleUpdate2OrLater_TooManyElements) {
+  ExpectAsserts expect_asserts;
+  EXPECT_FALSE(IsGoogleUpdate2OrLater(_T("1.1.2.3.4")));
+}
+
+TEST(GoopdateUtilsTest, IsGoogleUpdate2OrLater_Char) {
+  ExpectAsserts expect_asserts;
+  EXPECT_FALSE(IsGoogleUpdate2OrLater(_T("1.B.3.4")));
+  EXPECT_FALSE(IsGoogleUpdate2OrLater(_T("1.2.3.B")));
+  EXPECT_FALSE(IsGoogleUpdate2OrLater(_T("1.2.3.9B")));
+}
+
+TEST(GoopdateUtilsTest, WriteInstallerDataToTempFile) {
+  CStringA utf8_bom;
+  utf8_bom.Format("%c%c%c", 0xEF, 0xBB, 0xBF);
+
+  std::vector<CString> list_installer_data;
+  list_installer_data.push_back(_T(""));
+  list_installer_data.push_back(_T("hello\n"));
+  list_installer_data.push_back(_T("good bye"));
+  list_installer_data.push_back(_T("  there  you\n     go "));
+  list_installer_data.push_back(_T("\"http://foo.bar.org/?q=stuff&h=other\""));
+  list_installer_data.push_back(_T("foo\r\nbar\n"));
+  list_installer_data.push_back(_T("foo\n\rbar"));    // LFCR is not recognized.
+  list_installer_data.push_back(_T("this is a string over 1024 characters.  ------------------------------01------------------------------02------------------------------03------------------------------04------------------------------05------------------------------06------------------------------07------------------------------08------------------------------09------------------------------10------------------------------11------------------------------12------------------------------13------------------------------14------------------------------15------------------------------16------------------------------17------------------------------18------------------------------19------------------------------20------------------------------21------------------------------22------------------------------23------------------------------24------------------------------25------------------------------26------------------------------27------------------------------28------------------------------29------------------------------30------------------------------31------------------------------32------------------------------33------------------------------34------------------------------35------------------------------36------------------------------37------------------------------38------------------------------39------------------------------40 end.")); //NOLINT
+
+  std::vector<CStringA> expected_installer_data;
+  expected_installer_data.push_back("");
+  expected_installer_data.push_back("hello\n");
+  expected_installer_data.push_back("good bye");
+  expected_installer_data.push_back("  there  you\n     go ");
+  expected_installer_data.push_back("\"http://foo.bar.org/?q=stuff&h=other\"");
+  expected_installer_data.push_back("foo\r\nbar\n");
+  expected_installer_data.push_back("foo\n\rbar");
+  expected_installer_data.push_back("this is a string over 1024 characters.  ------------------------------01------------------------------02------------------------------03------------------------------04------------------------------05------------------------------06------------------------------07------------------------------08------------------------------09------------------------------10------------------------------11------------------------------12------------------------------13------------------------------14------------------------------15------------------------------16------------------------------17------------------------------18------------------------------19------------------------------20------------------------------21------------------------------22------------------------------23------------------------------24------------------------------25------------------------------26------------------------------27------------------------------28------------------------------29------------------------------30------------------------------31------------------------------32------------------------------33------------------------------34------------------------------35------------------------------36------------------------------37------------------------------38------------------------------39------------------------------40 end."); //NOLINT
+
+  ASSERT_EQ(expected_installer_data.size(), list_installer_data.size());
+
+  for (size_t i = 0; i < list_installer_data.size(); ++i) {
+    CString installer_data = list_installer_data[i];
+    SCOPED_TRACE(installer_data);
+
+    CString file_path;
+    HRESULT hr = WriteInstallerDataToTempFile(installer_data, &file_path);
+    ON_SCOPE_EXIT(::DeleteFile, file_path.GetString());
+    EXPECT_SUCCEEDED(hr);
+
+    // TODO(omaha): consider eliminating the special case.
+    // WriteInstallerDataToTempFile() will return S_FALSE with "" data.
+    if (S_OK == hr) {
+      File file;
+      const int kBufferLen = 4096;
+      std::vector<byte> data_line(kBufferLen);
+      EXPECT_SUCCEEDED(file.Open(file_path, false, false));
+      uint32 bytes_read(0);
+      EXPECT_SUCCEEDED(file.Read(data_line.size(),
+                                 &data_line.front(),
+                                 &bytes_read));
+      data_line.resize(bytes_read);
+      data_line.push_back(0);
+      EXPECT_STREQ(utf8_bom + expected_installer_data[i],
+                   reinterpret_cast<const char*>(&data_line.front()));
+      EXPECT_SUCCEEDED(file.Close());
+    } else {
+      EXPECT_TRUE(installer_data.IsEmpty());
+    }
+  }
+}
+
+TEST_F(GoopdateUtilsRegistryProtectedTest, UpdateLastChecked) {
+  EXPECT_SUCCEEDED(UpdateLastChecked(false));
+  EXPECT_FALSE(ShouldCheckForUpdates(false));
+
+  ConfigManager::Instance()->SetLastCheckedTime(false, 0);
+  EXPECT_TRUE(ShouldCheckForUpdates(false));
+}
+
+TEST_F(GoopdateUtilsRegistryProtectedTest,
+       ShouldCheckForUpdates_NoLastCheckedPresent) {
+  EXPECT_TRUE(ShouldCheckForUpdates(false));
+}
+
+TEST_F(GoopdateUtilsRegistryProtectedTest,
+       ShouldCheckForUpdates_LastCheckedPresent) {
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  ConfigManager::Instance()->SetLastCheckedTime(false, now - 10);
+  EXPECT_FALSE(ShouldCheckForUpdates(false));
+
+  ConfigManager::Instance()->SetLastCheckedTime(false,
+                                                now - kLastCheckPeriodSec - 1);
+  EXPECT_TRUE(ShouldCheckForUpdates(false));
+}
+
+TEST_F(GoopdateUtilsRegistryProtectedTest,
+       ShouldCheckForUpdates_LastCheckedInFuture) {
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  // The absolute difference is within the check period.
+  ConfigManager::Instance()->SetLastCheckedTime(false, now + 600);
+  EXPECT_FALSE(ShouldCheckForUpdates(false));
+
+  // The absolute difference is greater than the check period.
+  ConfigManager::Instance()->SetLastCheckedTime(false,
+                                                now + kLastCheckPeriodSec + 1);
+  EXPECT_TRUE(ShouldCheckForUpdates(false));
+}
+
+TEST_F(GoopdateUtilsRegistryProtectedTest,
+       ShouldCheckForUpdates_PeriodZero) {
+  EXPECT_SUCCEEDED(
+      RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
+                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
+                       static_cast<DWORD>(0)));
+
+  EXPECT_FALSE(ShouldCheckForUpdates(false));
+}
+
+TEST_F(GoopdateUtilsRegistryProtectedTest,
+       ShouldCheckForUpdates_PeriodOverride) {
+  const DWORD kOverrideMinutes = 10;
+  const DWORD kOverrideSeconds = kOverrideMinutes * 60;
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  EXPECT_SUCCEEDED(
+      RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
+                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
+                       kOverrideMinutes));
+
+  ConfigManager::Instance()->SetLastCheckedTime(false, now - 10);
+  EXPECT_FALSE(ShouldCheckForUpdates(false));
+
+  ConfigManager::Instance()->SetLastCheckedTime(false,
+                                                now - kOverrideSeconds - 1);
+  EXPECT_TRUE(ShouldCheckForUpdates(false));
+}
+
+TEST_P(GoopdateUtilsRegistryProtectedBooleanTest, CreateUserId) {
+  const bool is_machine = GetParam();
+  CString user_id1, user_id2;
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueForceUsageStats,
+                                    _T("1")));
+
+  EXPECT_SUCCEEDED(goopdate_utils::CreateUserId(is_machine));
+  user_id1 = goopdate_utils::GetUserIdLazyInit(is_machine);
+
+  // If user id exists, CreateUserId() should not create a new one.
+  EXPECT_SUCCEEDED(goopdate_utils::CreateUserId(is_machine));
+  user_id2 = goopdate_utils::GetUserIdLazyInit(is_machine);
+  EXPECT_STREQ(user_id1, user_id2);
+
+  goopdate_utils::DeleteUserId(is_machine);
+
+  // Recreate user id should result in a different id.
+  user_id2 = goopdate_utils::GetUserIdLazyInit(is_machine);
+  EXPECT_STRNE(user_id1, user_id2);
+
+  // Id generation should fail if machine is in OEM install state.
+  const DWORD now = Time64ToInt32(GetCurrent100NSTime());
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    now));
+  if (is_machine) {
+    ASSERT_TRUE(oem_install_utils::IsOemInstalling(is_machine));
+    EXPECT_FAILED(goopdate_utils::CreateUserId(is_machine));
+  } else {
+    ASSERT_FALSE(oem_install_utils::IsOemInstalling(is_machine));
+    EXPECT_SUCCEEDED(goopdate_utils::CreateUserId(is_machine));
+  }
+}
+
+TEST_F(GoopdateUtilsRegistryProtectedTest,
+       GenerateUserId_EachUserShouldHaveHisOwnHive) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueForceUsageStats,
+                                    _T("1")));
+
+  CString machine_user_id = goopdate_utils::GetUserIdLazyInit(true);
+  CString user_id = goopdate_utils::GetUserIdLazyInit(false);
+  EXPECT_STRNE(machine_user_id, user_id);
+}
+
+TEST_P(GoopdateUtilsRegistryProtectedBooleanTest, GetOptInUserId_UpdateDev) {
+  const bool is_machine = GetParam();
+
+  CString user_id = goopdate_utils::GetUserIdLazyInit(is_machine);
+  EXPECT_TRUE(user_id.IsEmpty());
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueForceUsageStats,
+                                    _T("1")));
+
+  user_id = goopdate_utils::GetUserIdLazyInit(is_machine);
+  EXPECT_FALSE(user_id.IsEmpty());
+
+  EXPECT_STREQ(user_id, goopdate_utils::GetUserIdLazyInit(is_machine));
+
+  GUID guid = GUID_NULL;
+  EXPECT_SUCCEEDED(StringToGuidSafe(user_id, &guid));
+}
+
+TEST_P(GoopdateUtilsRegistryProtectedBooleanTest, GetOptInUserId_AppOptIn) {
+  const bool is_machine = GetParam();
+  CString user_id = goopdate_utils::GetUserIdLazyInit(is_machine);
+  EXPECT_TRUE(user_id.IsEmpty());
+
+  CString key_path =
+      ConfigManager::Instance()->registry_client_state(is_machine);
+  key_path = AppendRegKeyPath(key_path, kAppId);
+  EXPECT_SUCCEEDED(RegKey::SetValue(key_path,
+                                    kRegValueForceUsageStats,
+                                    static_cast<DWORD>(1)));
+
+  user_id = goopdate_utils::GetUserIdLazyInit(is_machine);
+  EXPECT_FALSE(user_id.IsEmpty());
+
+  GUID guid = GUID_NULL;
+  EXPECT_SUCCEEDED(StringToGuidSafe(user_id, &guid));
+}
+
+TEST_F(GoopdateUtilsRegistryProtectedTest, GetOptInUserId_UserNotOptIn) {
+  const bool is_machine = false;
+
+  EXPECT_TRUE(goopdate_utils::GetUserIdLazyInit(is_machine).IsEmpty());
+
+  EXPECT_SUCCEEDED(goopdate_utils::CreateUserId(is_machine));
+  EXPECT_TRUE(
+      RegKey::HasValue(ConfigManager::Instance()->registry_update(is_machine),
+                       kRegValueUserId));
+  EXPECT_TRUE(goopdate_utils::GetUserIdLazyInit(is_machine).IsEmpty());
+
+  // ID should be removed.
+  EXPECT_FALSE(
+      RegKey::HasValue(ConfigManager::Instance()->registry_update(is_machine),
+                       kRegValueUserId));
+}
+
+TEST_P(GoopdateUtilsRegistryProtectedBooleanTest,
+       GetOptInUserId_OemInstalling) {
+  const bool is_machine = GetParam();
+  EXPECT_SUCCEEDED(goopdate_utils::CreateUserId(is_machine));
+
+  const DWORD now = Time64ToInt32(GetCurrent100NSTime());
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    now));
+  EXPECT_EQ(is_machine, oem_install_utils::IsOemInstalling(is_machine));
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                    kRegValueForceUsageStats,
+                                    _T("1")));
+
+  if (is_machine) {
+    EXPECT_TRUE(goopdate_utils::GetUserIdLazyInit(is_machine).IsEmpty());
+
+    EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE,
+                                         _T("OemInstallTime")));
+    EXPECT_FALSE(goopdate_utils::GetUserIdLazyInit(is_machine).IsEmpty());
+  } else {
+    EXPECT_FALSE(goopdate_utils::GetUserIdLazyInit(is_machine).IsEmpty());
+  }
+}
+
+INSTANTIATE_TEST_CASE_P(MachineOrUser,
+                        GoopdateUtilsRegistryProtectedBooleanTest,
+                        ::testing::Bool());
+
+}  // namespace goopdate_utils
+
+}  // namespace omaha
diff --git a/common/highres_timer-win32.cc b/common/highres_timer-win32.cc
deleted file mode 100644
index f4597f3..0000000
--- a/common/highres_timer-win32.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-#include "omaha/common/highres_timer-win32.h"
-
-namespace omaha {
-
-bool HighresTimer::perf_freq_collected_ = false;
-ULONGLONG HighresTimer::perf_freq_ = 0;
-
-ULONGLONG HighresTimer::GetElapsedMs() const {
-  ULONGLONG end_time = GetCurrentTicks();
-
-  // Scale to ms and round to nearerst ms - rounding is important
-  // because otherwise the truncation error may accumulate e.g. in sums.
-  //
-  // Given infinite resolution, this expression could be written as:
-  //  trunc((end - start (units:freq*sec))/freq (units:sec) *
-  //                1000 (unit:ms) + 1/2 (unit:ms))
-  ULONGLONG freq = GetTimerFrequency();
-  return ((end_time - start_ticks_) * 1000L + freq / 2) / freq;
-}
-
-ULONGLONG HighresTimer::GetElapsedSec() const {
-  ULONGLONG end_time = GetCurrentTicks();
-
-  // Scale to ms and round to nearerst ms - rounding is important
-  // because otherwise the truncation error may accumulate e.g. in sums.
-  //
-  // Given infinite resolution, this expression could be written as:
-  //  trunc((end - start (units:freq*sec))/freq (unit:sec) + 1/2 (unit:sec))
-  ULONGLONG freq = GetTimerFrequency();
-  return ((end_time - start_ticks_) + freq / 2) / freq;
-}
-
-void HighresTimer::CollectPerfFreq() {
-  LARGE_INTEGER freq;
-
-  // Note that this is racy.
-  // It's OK, however, because even concurrent executions of this
-  // are idempotent.
-  if (::QueryPerformanceFrequency(&freq)) {
-    perf_freq_ = freq.QuadPart;
-    perf_freq_collected_ = true;
-  }
-}
-
-}  // namespace omaha
-
diff --git a/common/highres_timer_unittest.cc b/common/highres_timer_unittest.cc
deleted file mode 100644
index 09ad671..0000000
--- a/common/highres_timer_unittest.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-#include "base/basictypes.h"
-#include "omaha/common/highres_timer-win32.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-// Timing tests are very flaky on Pulse.
-TEST(HighresTimer, MillisecondClock) {
-  if (omaha::IsBuildSystem()) {
-    return;
-  }
-
-  HighresTimer timer;
-
-  // note: this could fail if we context switch between initializing the timer
-  // and here. Very unlikely however.
-  EXPECT_EQ(0, timer.GetElapsedMs());
-  timer.Start();
-  uint64 half_ms = HighresTimer::GetTimerFrequency() / 2000;
-  // busy wait for half a millisecond
-  while (timer.start_ticks() + half_ms > HighresTimer::GetCurrentTicks()) {
-    // Nothing
-  }
-  EXPECT_EQ(1, timer.GetElapsedMs());
-}
-
-TEST(HighresTimer, SecondClock) {
-  if (omaha::IsBuildSystem()) {
-    return;
-  }
-
-  HighresTimer timer;
-
-  EXPECT_EQ(0, timer.GetElapsedSec());
-#ifdef OS_WINDOWS
-  ::Sleep(250);
-#else
-  struct timespec ts1 = {0, 250000000};
-  nanosleep(&ts1, 0);
-#endif
-  EXPECT_EQ(0, timer.GetElapsedSec());
-  EXPECT_LE(230, timer.GetElapsedMs());
-  EXPECT_GE(270, timer.GetElapsedMs());
-#ifdef OS_WINDOWS
-  ::Sleep(251);
-#else
-  struct timespec ts2 = {0, 251000000};
-  nanosleep(&ts2, 0);
-#endif
-  EXPECT_EQ(1, timer.GetElapsedSec());
-}
-
-}  // namespace omaha
-
diff --git a/common/install_manifest.h b/common/install_manifest.h
new file mode 100644
index 0000000..6333f68
--- /dev/null
+++ b/common/install_manifest.h
@@ -0,0 +1,91 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Defines the installation manifest, which is specific to each application and
+// version. This is a quasi-static data structure returned by the server in
+// the update response.
+
+#ifndef OMAHA_COMMON_INSTALL_MANIFEST_H_
+#define OMAHA_COMMON_INSTALL_MANIFEST_H_
+
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "omaha/common/const_goopdate.h"
+
+namespace omaha {
+
+namespace xml {
+
+struct InstallPackage {
+  InstallPackage() : is_required(false), size(0) {}
+
+  CString name;
+  CString version;
+  bool is_required;
+  int size;
+  CString hash;
+};
+
+struct InstallAction {
+  InstallAction()
+      : install_event(static_cast<InstallEvent>(0)),
+        needs_admin(NEEDS_ADMIN_NO),
+        terminate_all_browsers(false),
+        success_action(SUCCESS_ACTION_DEFAULT) {}
+
+  enum InstallEvent { kPreInstall = 1, kInstall, kUpdate, kPostInstall };
+
+  InstallEvent install_event;
+
+  // Whether the action should be run as admin. This may differ from whether the
+  // app is_machine.
+  // TODO(omaha3): This value is defined in the protocol but not implemented:
+  //  * It is not yet set by the XML parser.
+  //  * This should probably be renamed run_elevated since it is more likely to
+  //    be used to run de-elevated from an elevated Omaha instance.
+  //  * What should machine Omaha do if there is no logged in user and the
+  //    action cannot be de-elevated?
+  //  * This should default to the bundle's is_machine.
+  //  * Assert if !is_machine and needs_admin == NEEDS_ADMIN_YES.
+  NeedsAdmin needs_admin;
+
+  // TODO(omaha3): Need some more thinking here. On one hand, overloading this
+  // is tempting. On the other hand, it may be hard to read.
+  // This could also be SuccessfulInstallActions such as "launch_browser", or
+  // "terminate_all_browsers". The program_params in the case of
+  // "launch_browser" would be the URL to navigate to.
+  CString program_to_run;
+  CString program_arguments;
+
+  CString success_url;       // URL to launch the browser on success.
+  bool terminate_all_browsers;
+  SuccessfulInstallAction success_action;  // Action after install success.
+};
+
+// TODO(omaha3): Should all these really be public members?
+struct InstallManifest {
+  CString name;       // TBD.
+  CString version;
+  std::vector<InstallPackage> packages;
+  // TODO(omaha3): Maybe this should be a map or array of kPostInstall + 1 ptrs.
+  std::vector<InstallAction> install_actions;
+};
+
+}  // namespace xml
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_INSTALL_MANIFEST_H_
diff --git a/common/lang.cc b/common/lang.cc
new file mode 100644
index 0000000..a14ca6f
--- /dev/null
+++ b/common/lang.cc
@@ -0,0 +1,381 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/lang.h"
+#include <windows.h>
+#include <set>
+#include <map>
+#include <vector>
+#include "omaha/base/constants.h"
+#include "omaha/base/commontypes.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/file_ver.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/path.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/goopdate_utils.h"
+
+namespace omaha {
+
+namespace lang {
+
+namespace {
+
+const LANGID kFallbackLangID = 0x0409;
+const TCHAR* const kFallbackLanguage = _T("en");
+
+// This is the structure of the table which contains the language identifier
+// and the associated language string.
+struct LangIDAndPath {
+  LANGID langid;
+  TCHAR lang[12];
+};
+const LangIDAndPath kLanguageTranslationTable[] = {
+  // First we collect all main languages here which need special treatment.
+  // Special treatment might be necessary because of:
+  //  1.) Google has a special region code which is "Google specific". They
+  //      trump everything and should be at the beginning. (e.g. "iw")
+  //  2.) Usually a lot of languages are like "en-US", "en-GB",... and start
+  //      with the main language. However some languages like Norwegian do not
+  //      follow this scheme and we put them therefore at the top.
+  // The matching algorithm will look first for the full match, but at the same
+  // time it will look for a partial match. We want to add therefore the partial
+  // matches as high as possible to get them as a fallback.
+  { 0x0409, _T("en")         },  // _T("English (United States)")
+  { 0x040d, _T("iw")         },  // _T("Hebrew")
+  { 0x0464, _T("fil")        },  // _T("Tagalog")
+  { 0x0414, _T("no")         },  // _T("Norwegian (Bokmal, Norway)")
+
+  // and then we have all dialects here:
+  // { 0x041c, _T("sq-AL")      },  // _T("Albanian (Albania)")")
+  // { 0x0484, _T("gsw-FR")     },  // _T("Alsatian (France)")
+  { 0x045e, _T("am")  },  // _T("am-ET")      },  // _T("Amharic (Ethiopia)")
+  { 0x1401, _T("ar")  },  // _T("ar-DZ")      },  // _T("Arabic (Algeria)")
+  { 0x3c01, _T("ar")  },  // _T("ar-BH")      },  // _T("Arabic (Bahrain)")
+  { 0x0c01, _T("ar")  },  // _T("ar-EG")      },  // _T("Arabic (Egypt)")
+  { 0x0801, _T("ar")  },  // _T("ar-IQ")      },  // _T("Arabic (Iraq)")
+  { 0x2c01, _T("ar")  },  // _T("ar-JO")      },  // _T("Arabic (Jordan)")
+  { 0x3401, _T("ar")  },  // _T("ar-KW")      },  // _T("Arabic (Kuwait)")
+  { 0x3001, _T("ar")  },  // _T("ar-LB")      },  // _T("Arabic (Lebanon)")
+  { 0x1001, _T("ar")  },  // _T("ar-LY")      },  // _T("Arabic (Libya)")
+  { 0x1801, _T("ar")  },  // _T("ar-MA")      },  // _T("Arabic (Morocco)")
+  { 0x2001, _T("ar")  },  // _T("ar-OM")      },  // _T("Arabic (Oman)")
+  { 0x4001, _T("ar")  },  // _T("ar-QA")      },  // _T("Arabic (Qatar)")
+  { 0x0401, _T("ar")  },  // _T("ar-SA")      },  // _T("Arabic (Saudi Arabia)")
+  { 0x2801, _T("ar")  },  // _T("ar-SY")      },  // _T("Arabic (Syria)")
+  { 0x1c01, _T("ar")  },  // _T("ar-TN")      },  // _T("Arabic (Tunisia)")
+  { 0x3801, _T("ar")  },  // _T("ar-AE")      },  // _T("Arabic (U.A.E.)")
+  { 0x2401, _T("ar")  },  // _T("ar-YE")      },  // _T("Arabic (Yemen)")
+  { 0x042b, _T("ar")  },  // _T("hy-AM")      },  // _T("Armenian (Armenia)")
+  // { 0x044d, _T("as-IN")      },  // _T("Assamese (India)")
+  // { 0x082c, _T("az-Cyrl-AZ") },  // _T("Azeri (Azerbaijan, Cyrillic)")
+  // { 0x042c, _T("az-Latn-AZ") },  // _T("Azeri (Azerbaijan, Latin)")
+  // { 0x046d, _T("ba-RU")      },  // _T("Bashkir (Russia)")
+  // { 0x042d, _T("eu-ES")      },  // _T("Basque (Basque)")
+  // { 0x0423, _T("be-BY")      },  // _T("Belarusian (Belarus)")
+  { 0x0445, _T("bn")  },  // _T("bn-IN")      },  // _T("Bengali (India)")
+  // { 0x201a, _T("bs-Cyrl-BA") },  // _T("Bosnian (Bosnia and Herzegovina,
+  //                                       Cyrillic)")
+  // { 0x141a, _T("bs-Latn-BA") },  // _T("Bosnian (Bosnia and Herzegovina,
+  //                                       Latin)")
+  // { 0x047e, _T("br-FR")      },  // _T("Breton (France)")
+  { 0x0402, _T("bg")  },  // _T("bg-BG")   },  // _T("Bulgarian (Bulgaria)")
+  { 0x0403, _T("ca")  },  // _T("ca-ES")   },  // _T("Catalan (Catalan)")
+  { 0x0c04, _T("zh-HK") },  // _T("zh-HK")     // _T("Chinese
+                            //   (Hong Kong SAR, PRC)")
+  { 0x1404, _T("zh-TW") },  // _T("zh-MO")   },  // _T("Chinese (Macao SAR)")
+  { 0x0804, _T("zh-CN") },  // _T("zh-CN")   },  // _T("Chinese (PRC)")
+  { 0x1004, _T("zh-CN") },  // _T("zh-SG")   },  // _T("Chinese (Singapore)")
+  { 0x0404, _T("zh-TW") },  // _T("Chinese (Taiwan)")
+  { 0x101a, _T("hr")    },  // _T("hr-BA")   },  // _T("Croatian
+                            //   (Bosnia and Herzegovina, Latin)")
+  { 0x041a, _T("hr")    },  // _T("hr-HR")   },  // _T("Croatian (Croatia)")
+  { 0x0405, _T("cs")    },  // _T("cs-CZ")   },  // _T("Czech (Czech Republic)")
+  { 0x0406, _T("da")    },  // _T("da-DK")   },  // _T("Danish (Denmark)")
+  // { 0x048c, _T("gbz-AF")     },  // _T("Dari (Afghanistan)")
+  // { 0x0465, _T("dv-MV")      },  // _T("Divehi (Maldives)")
+  { 0x0813, _T("nl")  },  // _T("nl-BE")   },  // _T("Dutch (Belgium)")
+  { 0x0413, _T("nl")  },  // _T("nl-NL")   },  // _T("Dutch (Netherlands)")
+  { 0x0c09, _T("en")  },  // _T("en-AU")   },  // _T("English (Australia)")
+  { 0x2809, _T("en")  },  // _T("en-BZ")   },  // _T("English (Belize)")
+  { 0x1009, _T("en")  },  // _T("en-CA")   },  // _T("English (Canada)")
+  { 0x2409, _T("en")  },  // _T("en-029")  },  // _T("English (Caribbean)")
+  { 0x4009, _T("en")  },  // _T("en-IN")   },  // _T("English (India)")
+  { 0x1809, _T("en")  },  // _T("en-IE")   },  // _T("English (Ireland)")
+  { 0x2009, _T("en")  },  // _T("en-JM")   },  // _T("English (Jamaica)")
+  { 0x4409, _T("en")  },  // _T("en-MY")   },  // _T("English (Malaysia)")
+  { 0x1409, _T("en")  },  // _T("en-NZ")   },  // _T("English (New Zealand)")
+  { 0x3409, _T("en")  },  // _T("en-PH")   },  // _T("English (Philippines)")
+  { 0x4809, _T("en")  },  // _T("en-SG")   },  // _T("English (Singapore)")
+  { 0x1c09, _T("en")  },  // _T("en-ZA")   },  // _T("English (South Africa)")
+  { 0x2c09, _T("en")  },  // _T("en-TT")   },  // _T("English
+                          //   (Trinidad and Tobago)")
+  { 0x0809, _T("en-GB") },  // _T("English (United Kingdom)")
+  { 0x3009, _T("en")    },  // _T("en-ZW")      },  // _T("English (Zimbabwe)")
+  { 0x0425, _T("et")  },  // _T("et-EE")   },  // _T("Estonian (Estonia)")
+  // { 0x0438, _T("fo-FO")  },  // _T("Faroese (Faroe Islands)")
+  { 0x0464, _T("fil") },  // _T("fil-PH")    },  // _T("Filipino (Philippines)")
+  { 0x040b, _T("fi")  },  // _T("fi-FI")      },  // _T("Finnish (Finland)")
+  { 0x080c, _T("fr")  },  // _T("fr-BE")      },  // _T("French (Belgium)")
+  { 0x0c0c, _T("fr")  },  // _T("fr-CA")      },  // _T("French (Canada)")
+  { 0x040c, _T("fr")  },  // _T("fr-FR")      },  // _T("French (France)")
+  { 0x140c, _T("fr")  },  // _T("fr-LU")      },  // _T("French (Luxembourg)")
+  { 0x180c, _T("fr")  },  // _T("fr-MC")      },  // _T("French (Monaco)")
+  { 0x100c, _T("fr")  },  // _T("fr-CH")      },  // _T("French (Switzerland)")
+  // { 0x0462, _T("fy-NL")      },  // _T("Frisian (Netherlands)")
+  // { 0x0456, _T("gl-ES")      },  // _T("Galician (Spain)")
+  // { 0x0437, _T("ka-GE")      },  // _T("Georgian (Georgia)")
+  { 0x0c07, _T("de")  },  // _T("de-AT")   },  // _T("German (Austria)")
+  { 0x0407, _T("de")  },  // _T("de-DE")   },  // _T("German (Germany)")
+  { 0x1407, _T("de")  },  // _T("de-LI")   },  // _T("German (Liechtenstein)")
+  { 0x1007, _T("de")  },  // _T("de-LU")   },  // _T("German (Luxembourg)")
+  { 0x0807, _T("de")  },  // _T("de-CH")   },  // _T("German (Switzerland)")
+  { 0x0408, _T("el")  },  // _T("el-GR")   },  // _T("Greek (Greece)")
+  // { 0x046f, _T("kl-GL")      },  // _T("Greenlandic (Greenland)")
+  { 0x0447, _T("gu")  },  // _T("gu-IN")      },  // _T("Gujarati (India)")
+  // { 0x0468, _T("ha-Latn-NG") },  // _T("Hausa (Nigeria, Latin)")
+  // { 0x040d, _T("he-IL")      },  // _T("Hebrew (Israel)")
+  { 0x0439, _T("hi")  },  // _T("hi-IN")   },  // _T("Hindi (India)")
+  { 0x040e, _T("hu")  },  // _T("hu-HU")   },  // _T("Hungarian (Hungary)")
+  { 0x040f, _T("is")  },  // _T("is-IS")      },  // _T("Icelandic (Iceland)")
+  // { 0x0470, _T("ig-NG")      },  // _T("Igbo (Nigeria)")
+  { 0x0421, _T("id")  },  // _T("id-ID")    },  // _T("Indonesian (Indonesia)")
+  // { 0x085d, _T("iu-Latn-CA") },  // _T("Inuktitut (Canada, Latin)")
+  // { 0x045d, _T("iu-Cans-CA") },  // _T("Inuktitut (Canada, Syllabics)")
+  // { 0x083c, _T("ga-IE")      },  // _T("Irish (Ireland)")
+  { 0x0410, _T("it")  },  // _T("it-IT")      },  // _T("Italian (Italy)")
+  { 0x0810, _T("it")  },  // _T("it-CH")      },  // _T("Italian (Switzerland)")
+  { 0x0411, _T("ja")  },  // _T("ja-JP")      },  // _T("Japanese (Japan)")
+  { 0x044b, _T("kn")  },  // _T("kn-IN")      },  // _T("Kannada (India)")
+  // { 0x043f, _T("kk-KZ")      },  // _T("Kazakh (Kazakhstan)")
+  // { 0x0453, _T("kh-KH")      },  // _T("Khmer (Cambodia)")
+  // { 0x0486, _T("qut-GT")     },  // _T("K'iche (Guatemala)")
+  // { 0x0487, _T("rw-RW")      },  // _T("Kinyarwanda (Rwanda)")
+  // { 0x0457, _T("kok-IN")     },  // _T("Konkani (India)")
+  { 0x0812, _T("ko")         },  // _T("ko-Jo")      },  // _T("Korean (Johab)")
+  { 0x0412, _T("ko")         },  // _T("ko-KR")      },  // _T("Korean (Korea)")
+  // { 0x0440, _T("ky-KG")      },  // _T("Kyrgyz (Kyrgyzstan)")
+  // { 0x0454, _T("lo-LA")      },  // _T("Lao (Lao PDR)")
+  { 0x0426, _T("lv")  },  // _T("lv-LV")   },  // _T("Latvian (Latvia)")
+  { 0x0427, _T("lt")  },  // _T("lt-LT")   },  // _T("Lithuanian (Lithuania)")
+  // { 0x082e, _T("dsb-DE")     },  // _T("Lower Sorbian (Germany)")
+  // { 0x046e, _T("lb-LU")      },  // _T("Luxembourgish (Luxembourg)")
+  // { 0x042f, _T("mk-MK")      },  // _T("Macedonian (Macedonia, FYROM)")
+  { 0x083e, _T("ms")  },  // _T("ms-BN")  },  // _T("Malay (Brunei Darussalam)")
+  { 0x043e, _T("ms")  },  // _T("ms-MY")      },  // _T("Malay (Malaysia)")
+  { 0x044c, _T("ml")  },  // _T("ml-IN")      },  // _T("Malayalam (India)")
+  // { 0x043a, _T("mt-MT")      },  // _T("Maltese (Malta)")
+  // { 0x0481, _T("mi-NZ")      },  // _T("Maori (New Zealand)")
+  // { 0x047a, _T("arn-CL")     },  // _T("Mapudungun (Chile)")
+  { 0x044e, _T("mr")  },  // _T("mr-IN")      },  // _T("Marathi (India)")
+  // { 0x047c, _T("moh-CA")     },  // _T("Mohawk (Canada)")
+  // { 0x0450, _T("mn-Cyrl-MN") },  // _T("Mongolian (Mongolia)")
+  // { 0x0850, _T("mn-Mong-CN") },  // _T("Mongolian (PRC)")
+  // { 0x0461, _T("ne-NP")      },  // _T("Nepali (Nepal)")
+  // { 0x0414, _T("nb-NO")      },  // _T("Norwegian (Bokmal, Norway)")
+  // { 0x0814, _T("nn-NO")      },  // _T("Norwegian (Nynorsk, Norway)")
+  // { 0x0482, _T("oc-FR")      },  // _T("Occitan (France)")
+  // { 0x0448, _T("or-IN") },  // _T("Oriya (India)")
+  // { 0x0463, _T("ps-AF") },  // _T("Pashto (Afghanistan)")
+  { 0x0429, _T("fa")  },  // _T("fa-IR")      },  // _T("Persian (Iran)")
+  { 0x0415, _T("pl")  },  // _T("pl-PL")      },  // _T("Polish (Poland)")
+  { 0x0416, _T("pt-BR")      },  // _T("Portuguese (Brazil)")
+  { 0x0816, _T("pt-PT")      },  // _T("Portuguese (Portugal)")
+  // { 0x0446, _T("pa-IN")      },  // _T("Punjabi (India)")
+  // { 0x046b, _T("quz-BO")     },  // _T("Quechua (Bolivia)")
+  // { 0x086b, _T("quz-EC")     },  // _T("Quechua (Ecuador)")
+  // { 0x0c6b, _T("quz-PE")     },  // _T("Quechua (Peru)")
+  { 0x0418, _T("ro")  },  // _T("ro-RO")      },  // _T("Romanian (Romania)")
+  // { 0x0417, _T("rm-CH")      },  // _T("Romansh (Switzerland)")
+  { 0x0419, _T("ru")  },  // _T("ru-RU")      },  // _T("Russian (Russia)")
+  // { 0x243b, _T("smn-FI")     },  // _T("Sami (Inari, Finland)")
+  // { 0x103b, _T("smj-NO")     },  // _T("Sami (Lule, Norway)")
+  // { 0x143b, _T("smj-SE")     },  // _T("Sami (Lule, Sweden)")
+  // { 0x0c3b, _T("se-FI")      },  // _T("Sami (Northern, Finland)")
+  // { 0x043b, _T("se-NO")      },  // _T("Sami (Northern, Norway)")
+  // { 0x083b, _T("se-SE")      },  // _T("Sami (Northern, Sweden)")
+  // { 0x203b, _T("sms-FI")     },  // _T("Sami (Skolt, Finland)")
+  // { 0x183b, _T("sma-NO")     },  // _T("Sami (Southern, Norway)")
+  // { 0x1c3b, _T("sma-SE")     },  // _T("Sami (Southern, Sweden)")
+  // { 0x044f, _T("sa-IN")      },  // _T("Sanskrit (India)")
+  { 0x1c1a, _T("sr")  },  // _T("sr-Cyrl-BA") },  // _T("Serbian
+                          //   (Bosnia and Herzegovina, Cyrillic)")
+  { 0x181a, _T("sr")  },  // _T("sr-Latn-BA") },  // _T("Serbian
+                          //   (Bosnia and Herzegovina, Latin)")
+  { 0x0c1a, _T("sr")  },  // _T("sr-Cyrl-CS") },  // _T("Serbian
+                          //   (Serbia and Montenegro, Cyrillic)")
+  { 0x081a, _T("sr")  },  // _T("sr-Latn-CS") },  // _T("Serbian
+                          //   (Serbia and Montenegro, Latin)")
+  // { 0x046c, _T("ns-ZA")      },  // _T("Sesotho sa Leboa/Northern Sotho
+  //                                       (South Africa)")
+  // { 0x0432, _T("tn-ZA")      },  // _T("Setswana/Tswana (South Africa)")
+  // { 0x045b, _T("si-LK")      },  // _T("Sinhala (Sri Lanka)")
+  { 0x041b, _T("sk")  },  // _T("sk-SK")   },  // _T("Slovak (Slovakia)")
+  { 0x0424, _T("sl")  },  // _T("sl-SI")   },  // _T("Slovenian (Slovenia)")
+  { 0x2c0a, _T("es-419") },  // _T("es-AR")   },  // _T("Spanish (Argentina)")
+  { 0x400a, _T("es-419") },  // _T("es-BO")   },  // _T("Spanish (Bolivia)")
+  { 0x340a, _T("es-419") },  // _T("es-CL")   },  // _T("Spanish (Chile)")
+  { 0x240a, _T("es-419") },  // _T("es-CO")   },  // _T("Spanish (Colombia)")
+  { 0x140a, _T("es-419") },  // _T("es-CR")   },  // _T("Spanish (Costa Rica)")
+  { 0x1c0a, _T("es-419") },  // _T("es-DO")   },  // _T("Spanish
+                                                  //     (Dominican Republic)")
+  { 0x300a, _T("es-419") },  // _T("es-EC")   },  // _T("Spanish (Ecuador)")
+  { 0x440a, _T("es-419") },  // _T("es-SV")   },  // _T("Spanish (El Salvador)")
+  { 0x100a, _T("es-419") },  // _T("es-GT")   },  // _T("Spanish (Guatemala)")
+  { 0x480a, _T("es-419") },  // _T("es-HN")   },  // _T("Spanish (Honduras)")
+  { 0x080a, _T("es-419") },  // _T("es-MX")   },  // _T("Spanish (Mexico)")
+  { 0x4c0a, _T("es-419") },  // _T("es-NI")   },  // _T("Spanish (Nicaragua)")
+  { 0x180a, _T("es-419") },  // _T("es-PA")   },  // _T("Spanish (Panama)")
+  { 0x3c0a, _T("es-419") },  // _T("es-PY")   },  // _T("Spanish (Paraguay)")
+  { 0x280a, _T("es-419") },  // _T("es-PE")   },  // _T("Spanish (Peru)")
+  { 0x500a, _T("es-419") },  // _T("es-PR")   },  // _T("Spanish (Puerto Rico)")
+  { 0x0c0a, _T("es")     },  // _T("es-ES")   },  // _T("Spanish (Spain)")
+  { 0x040a, _T("es")     },  // _T("es-ES_tradnl")   },
+                             // _T("Spanish (Spain, Traditional Sort)")
+  { 0x540a, _T("es-419") },  // _T("es-US")   },  // _T("Spanish
+                                                  //     (United States)")
+  { 0x380a, _T("es-419") },  // _T("es-UY")   },  // _T("Spanish (Uruguay)")
+  { 0x200a, _T("es-419") },  // _T("es-VE")   },  // _T("Spanish (Venezuela)")
+  { 0x0441, _T("sw")  },  // _T("sw-KE")      },  // _T("Swahili (Kenya)")
+  { 0x081d, _T("sv")  },  // _T("sv-FI")      },  // _T("Swedish (Finland)")
+  { 0x041d, _T("sv")  },  // _T("sv-SE")      },  // _T("Swedish (Sweden)")
+  // { 0x045a, _T("syr-SY")     },  // _T("Syriac (Syria)")
+  // { 0x0428, _T("tg-Cyrl-TJ") },  // _T("Tajik (Tajikistan)")
+  // { 0x085f, _T("tmz-Latn-DZ")},  // _T("Tamazight (Algeria, Latin)")
+  { 0x0449, _T("ta")  },  // _T("ta-IN")      },  // _T("Tamil (India)")
+  // { 0x0444, _T("tt-RU") },  // _T("Tatar (Russia)")
+  { 0x044a, _T("te")  },  // _T("te-IN") }, // _T("Telugu (India)")
+  { 0x041e, _T("th")  },  // _T("th-TH")      },  // _T("Thai (Thailand)")
+  // { 0x0851, _T("bo-BT")      },  // _T("Tibetan (Bhutan)")
+  // { 0x0451, _T("bo-CN")      },  // _T("Tibetan (PRC)")
+  { 0x041f, _T("tr")  },  // _T("tr-TR")      },  // _T("Turkish (Turkey)")
+  // { 0x0442, _T("tk-TM")      },  // _T("Turkmen (Turkmenistan)")
+  // { 0x0480, _T("ug-CN")      },  // _T("Uighur (PRC)")
+  { 0x0422, _T("uk")  },  // _T("uk-UA")      },  // _T("Ukrainian (Ukraine)")
+  // { 0x042e, _T("wen-DE")     },  // _T("Upper Sorbian (Germany)")
+  // { 0x0820,  _T("tr-IN")      },  // _T("Urdu(India)")
+  { 0x0420, _T("ur")  },  // _T("ur-PK") },  // _T("Urdu (Pakistan)")
+  // { 0x0843, _T("uz-Cyrl-UZ") }, // _T("Uzbek (Uzbekistan, Cyrillic)")
+  // { 0x0443, _T("uz-Latn-UZ") },  // _T("Uzbek (Uzbekistan, Latin)")
+  { 0x042a, _T("vi")  },  // _T("vi-VN")      },  // _T("Vietnamese (Vietnam)")
+  // { 0x0452, _T("cy-GB")      },  // _T("Welsh (United Kingdom)")
+  // { 0x0488, _T("wo-SN")      },  // _T("Wolof (Senegal)")
+  // { 0x0434, _T("xh-ZA")      },  // _T("Xhosa/isiXhosa (South Africa)")
+  // { 0x0485, _T("sah-RU")     },  // _T("Yakut (Russia)")
+  // { 0x0478, _T("ii-CN")      },  // _T("Yi (PRC)")
+  // { 0x046a, _T("yo-NG") }, // _T("Yoruba (Nigeria)")
+  // { 0x0435, _T("zu-ZA") }, // _T("Zulu/isiZulu (South Africa)")
+  { 0x0000, _T("")           }   // The list termination.
+};
+}
+
+CString GetDefaultLanguage(bool is_system) {
+  return GetLanguageForLangID(GetDefaultLanguageID(is_system));
+}
+
+LANGID GetDefaultLanguageID(bool is_system) {
+  LANGID langid = is_system ? ::GetSystemDefaultLangID()
+                            : ::GetUserDefaultLangID();
+  if (IsLanguageIDSupported(langid)) {
+    return langid;
+  }
+
+  return kFallbackLangID;
+}
+
+// If an exact match is not found, returns "en".
+CString GetLanguageForLangID(LANGID langid) {
+  ASSERT1(langid != 0);
+
+  for (int i = 0; kLanguageTranslationTable[i].langid; ++i) {
+    if (kLanguageTranslationTable[i].langid == langid) {
+      return kLanguageTranslationTable[i].lang;
+    }
+  }
+
+  return kFallbackLanguage;
+}
+
+bool IsLanguageIDSupported(LANGID langid) {
+  for (int i = 0; kLanguageTranslationTable[i].langid; ++i) {
+    if (langid == kLanguageTranslationTable[i].langid) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool IsLanguageSupported(const CString& language) {
+  for (int i = 0; kLanguageTranslationTable[i].langid; ++i) {
+    if (language.CompareNoCase(kLanguageTranslationTable[i].lang) == 0) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void GetSupportedLanguages(std::vector<CString>* codes) {
+  ASSERT1(codes);
+  std::set<CString> languages;
+
+  for (int i = 0; kLanguageTranslationTable[i].langid; ++i) {
+    languages.insert(CString(kLanguageTranslationTable[i].lang));
+  }
+
+  for (std::set<CString>::const_iterator it = languages.begin();
+       it != languages.end();
+       ++it) {
+    codes->push_back(*it);
+  }
+}
+
+// Returns user default language if requested language is not supported.
+CString GetLanguageForProcess(const CString& requested_language) {
+  if (!requested_language.IsEmpty() &&
+      IsLanguageSupported(requested_language)) {
+    return requested_language;
+  }
+
+  return GetDefaultLanguage(false);
+}
+
+CString GetWrittenLanguage(const CString& requested_language) {
+  CString written_language(requested_language);
+
+  // Handle specific special cases
+  // * Chinese (Hong Kong) - separate language but uses the zh-TW as written
+  //   language.
+  // * Hebrew synonyms - convert he to iw.
+  if (!requested_language.CompareNoCase(_T("zh-HK"))) {
+    written_language = _T("zh-TW");
+  } else if (!requested_language.CompareNoCase(_T("he"))) {
+    written_language = _T("iw");
+  }
+
+  ASSERT1(IsLanguageSupported(written_language));
+  return written_language;
+}
+
+bool DoesSupportedLanguageUseDifferentId(const CString& requested_language) {
+  return (requested_language.CompareNoCase(_T("zh-HK")) == 0);
+}
+
+}  // namespace lang
+
+}  // namespace omaha
diff --git a/common/lang.h b/common/lang.h
new file mode 100644
index 0000000..83cb8a9
--- /dev/null
+++ b/common/lang.h
@@ -0,0 +1,54 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_LANG_H_
+#define OMAHA_COMMON_LANG_H_
+
+#include <atlstr.h>
+#include <map>
+#include <vector>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+namespace lang {
+
+CString GetDefaultLanguage(bool is_system);
+LANGID GetDefaultLanguageID(bool is_system);
+
+CString GetLanguageForLangID(LANGID langid);
+
+bool IsLanguageIDSupported(LANGID langid);
+bool IsLanguageSupported(const CString& language);
+void GetSupportedLanguages(std::vector<CString>* codes);
+
+// Returnes requested_language if supported. Otherwise returns current user's
+// language if supported or "en" if both of them are not supported.
+CString GetLanguageForProcess(const CString& requested_language);
+
+// Returns the written language name for a given language. For most languages,
+// the names are exactly same. There are very few exceptions. For example, the
+// written language of zh-HK is zh-TW.
+CString GetWrittenLanguage(const CString& requested_language);
+
+// Returns whether a language and its corresponding supported language have
+// different names.
+bool DoesSupportedLanguageUseDifferentId(const CString& requested_language);
+
+}  // namespace lang
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_LANG_H_
diff --git a/common/lang_enc.h b/common/lang_enc.h
deleted file mode 100644
index e6df865..0000000
--- a/common/lang_enc.h
+++ /dev/null
@@ -1,299 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// This file is for i18n. It contains two enums, namely Language and
-// Encoding, where Language is the linguistic convention, and Encoding
-// contains information on both language encoding and character set.
-//
-// The language and encoding are both based on Teragram's conventions,
-// except for some common ISO-8859 encodings that are not detected by
-// Teragram but might be in the future.
-//
-// This file also includes functions that do mappings among
-// Language/Encoding enums, language/encoding string names (typically
-// the output from Language Encoding identifier), and language codes
-// (iso 639), and two-letter country codes (iso 3166)
-//
-// NOTE: Both Language and Encoding enums should always start from
-// zero value. This assumption has been made and used.
-
-#ifndef  OMAHA_COMMON_LANG_ENC_H_
-#define  OMAHA_COMMON_LANG_ENC_H_
-
-#include "omaha/common/commontypes.h"
-
-// some of the popular encoding aliases
-#define LATIN1     ISO_8859_1
-#define LATIN2     ISO_8859_2
-#define LATIN3     ISO_8859_3
-#define LATIN4     ISO_8859_4
-#define CYRILLIC   ISO_8859_5
-#define ARABIC_ENCODING  ISO_8859_6     // avoiding the same name as language
-#define GREEK_ENCODING   ISO_8859_7     // avoiding the same name as language
-#define HEBREW_ENCODING  ISO_8859_8     // avoiding the same name as language
-#define LATIN5     ISO_8859_9
-#define LATIN6     ISO_8859_10
-#define KOREAN_HANGUL  KOREAN_EUC_KR
-
-// NOTE: Only add new languages to the end of this list (but before
-// NUM_LANGUAGES).
-enum Language {
-  ENGLISH = 0,  /* 0 */
-  DANISH,       /* 1 */
-  DUTCH,        /* 2 */
-  FINNISH,      /* 3 */
-  FRENCH,       /* 4 */
-  GERMAN,       /* 5 */
-  HEBREW,       /* 6 */
-  ITALIAN,      /* 7 */
-  JAPANESE,     /* 8 */
-  KOREAN,       /* 9 */
-  NORWEGIAN,    /* 10 */
-  POLISH,       /* 11 */
-  PORTUGUESE,   /* 12 */
-  RUSSIAN,      /* 13 */
-  SPANISH,      /* 14 */
-  SWEDISH,      /* 15 */
-  CHINESE,      /* 16 */
-  CZECH,        /* 17 */
-  GREEK,        /* 18 */
-  ICELANDIC,    /* 19 */
-  LATVIAN,      /* 20 */
-  LITHUANIAN,   /* 21 */
-  ROMANIAN,     /* 22 */
-  HUNGARIAN,    /* 23 */
-  ESTONIAN,     /* 24 */
-  TG_UNKNOWN_LANGUAGE,  /* 25 */
-  UNKNOWN_LANGUAGE,     /* 26 */
-  BULGARIAN,    /* 27 */
-  CROATIAN,     /* 28 */
-  SERBIAN,      /* 29 */
-  IRISH,        /* 30 */
-  GALICIAN,     /* 31 */
-  TAGALOG,      /* 32 */
-  TURKISH,      /* 33 */
-  UKRAINIAN,    /* 34 */
-  HINDI,        /* 35 */
-  MACEDONIAN,   /* 36 */
-  BENGALI,      /* 37 */
-  INDONESIAN,   /* 38 */
-  LATIN,        /* 39 */
-  MALAY,        /* 40 */
-  MALAYALAM,    /* 41 */
-  WELSH,        /* 42 */
-  NEPALI,       /* 43 */
-  TELUGU,       /* 44 */
-  ALBANIAN,     /* 45 */
-  TAMIL,        /* 46 */
-  BELARUSIAN,   /* 47 */
-  JAVANESE,     /* 48 */
-  OCCITAN,      /* 49 */
-  URDU,         /* 50 */
-  BIHARI,       /* 51 */
-  GUJARATI,     /* 52 */
-  THAI,         /* 53 */
-  ARABIC,       /* 54 */
-  CATALAN,      /* 55 */
-  ESPERANTO,    /* 56 */
-  BASQUE,       /* 57 */
-  INTERLINGUA,  /* 58 */
-  KANNADA,      /* 59 */
-  PUNJABI,      /* 60 */
-  SCOTS_GAELIC, /* 61 */
-  SWAHILI,      /* 62 */
-  SLOVENIAN,    /* 63 */
-  MARATHI,      /* 64 */
-  MALTESE,      /* 65 */
-  VIETNAMESE,   /* 66 */
-  FRISIAN,      /* 67 */
-  SLOVAK,       /* 68 */
-  CHINESE_T,    /* 69 */      // This is added to solve the problem of
-                              // distinguishing Traditional and Simplified
-                              // Chinese when the encoding is UTF8.
-  FAROESE,      /* 70 */
-  SUNDANESE,    /* 71 */
-  UZBEK,        /* 72 */
-  AMHARIC,      /* 73 */
-  AZERBAIJANI,  /* 74 */
-  GEORGIAN,     /* 75 */
-  TIGRINYA,     /* 76 */
-  PERSIAN,      /* 77 */
-  BOSNIAN,      /* 78 */
-  SINHALESE,    /* 79 */
-  NORWEGIAN_N,  /* 80 */
-  PORTUGUESE_P, /* 81 */
-  PORTUGUESE_B, /* 82 */
-  XHOSA,        /* 83 */
-  ZULU,         /* 84 */
-  GUARANI,      /* 85 */
-  SESOTHO,      /* 86 */
-  TURKMEN,      /* 87 */
-  KYRGYZ,       /* 88 */
-  BRETON,       /* 89 */
-  TWI,          /* 90 */
-  YIDDISH,      /* 91 */
-  ORIYA,        /* 92 */
-  SERBO_CROATIAN,       /* 93 */
-  SOMALI,       /* 94 */
-  UIGHUR,       /* 95 */
-  KURDISH,      /* 96 */
-  MONGOLIAN,    /* 97 */
-  ARMENIAN,     /* 98 */
-  LAOTHIAN,     /* 99 */
-  SINDHI,       /* 100! */
-  RHAETO_ROMANCE,  /* 101 */
-  CHINESE_JAPANESE_KOREAN,  /* 103 */  // Not really a language
-  PSEUDOTRANSLATION,  /* 104 */  // Not really a language
-  NUM_LANGUAGES,              // Always keep this at the end. It is not a
-                              // valid Language enum, it is only used to
-                              // indicate the total number of Languages.
-};
-
-
-// Language codes for those languages we support, used to map to IDs from
-// the Language enumeration.  We could have used the Rfc1766ToLcid from the
-// Win32 system's mlang.dll to map these to LCIDs, but a) we don't want to
-// have to load mlang.dll and b) we are using our own language IDs.
-const TCHAR* const kLangCodeChinesePrc = _T("zh_cn");
-const TCHAR* const kLangCodeChineseTaiwan = _T("zh_tw");
-const TCHAR* const kLangCodeCjk = _T("cjk");
-const TCHAR* const kLangCodeDutch = _T("nl");
-const TCHAR* const kLangCodeEnglish = _T("en");
-const TCHAR* const kLangCodeFrench = _T("fr");
-const TCHAR* const kLangCodeGerman = _T("de");
-const TCHAR* const kLangCodeItalian = _T("it");
-const TCHAR* const kLangCodeJapanese = _T("ja");
-const TCHAR* const kLangCodeKorean = _T("ko");
-const TCHAR* const kLangCodePseudo = _T("x");
-const TCHAR* const kLangCodeSpanish = _T("es");
-
-
-// Maps language codes to languages.  Terminated by a { NULL, UNKNOWN_LANGUAGE }
-// item.
-struct CodeToLanguage {
-  const TCHAR* code;
-  Language language;
-};
-
-SELECTANY CodeToLanguage codes_to_languages[] = {
-  { kLangCodeChinesePrc, CHINESE },
-  { kLangCodeChineseTaiwan, CHINESE_T },
-  { kLangCodeCjk, CHINESE_JAPANESE_KOREAN },
-  { kLangCodeDutch, DUTCH },
-  { kLangCodeEnglish, ENGLISH },
-  { kLangCodeFrench, FRENCH },
-  { kLangCodeGerman, GERMAN },
-  { kLangCodeItalian, ITALIAN },
-  { kLangCodeJapanese, JAPANESE },
-  { kLangCodeKorean, KOREAN },
-  { kLangCodePseudo, PSEUDOTRANSLATION },
-  { kLangCodeSpanish, SPANISH },
-  { NULL, UNKNOWN_LANGUAGE }
-};
-
-
-
-// Macro to wrap the notion of "unknown language".
-#define IS_LANGUAGE_UNKNOWN(l)  \
-  ((l) == TG_UNKNOWN_LANGUAGE || (l) == UNKNOWN_LANGUAGE)
-
-// NOTE: Only add new encodings to the end of this list (but before
-// NUM_ENCODINGS).
-// NOTE: If you add an encoding here, you must also modify basistech_encoding()
-// and google2/com/google/i18n/Encoding.java
-enum Encoding {
-  ISO_8859_1 = 0,       // 0: Teragram ASCII
-  ISO_8859_2,           // 1: Teragram Latin2
-  ISO_8859_3,           // 2: in BasisTech but not in Teragram
-  ISO_8859_4,           // 3: Teragram Latin4
-  ISO_8859_5,           // 4: Teragram ISO-8859-5
-  ISO_8859_6,           // 5: Teragram Arabic
-  ISO_8859_7,           // 6: Teragram Greek
-  ISO_8859_8,           // 7: Teragram Hebrew
-  ISO_8859_9,           // 8: in BasisTech but not in Teragram
-  ISO_8859_10,          // 9: in BasisTech but not in Teragram
-  JAPANESE_EUC_JP,      // 10: Teragram EUC_JP
-  JAPANESE_SHIFT_JIS,   // 11: Teragram SJS
-  JAPANESE_JIS,         // 12: Teragram JIS
-  CHINESE_BIG5,         // 13: Teragram BIG5
-  CHINESE_GB,           // 14: Teragram GB
-  CHINESE_EUC_CN,       // 15: Teragram EUC-CN
-  KOREAN_EUC_KR,        // 16: Teragram KSC
-  UNICODE_ENCODING,     // 17: Teragram Unicode, changed to UNICODE_ENCODING
-                        //     from UNICODE, which is predefined by WINDOW
-  CHINESE_EUC_DEC,      // 18: Teragram EUC
-  CHINESE_CNS,          // 19: Teragram CNS
-  CHINESE_BIG5_CP950,   // 20: Teragram BIG5_CP950
-  JAPANESE_CP932,       // 21: Teragram CP932
-  UTF8,                 // 22
-  UNKNOWN_ENCODING,     // 23
-  ASCII_7BIT,           // 24: ISO_8859_1 with all characters <= 127.
-                        //     Should be present only in the crawler
-                        //     and in the repository,
-                        //     *never* as a result of Document::encoding().
-  RUSSIAN_KOI8_R,       // 25: Teragram KOI8R
-  RUSSIAN_CP1251,       // 26: Teragram CP1251
-
-  //----------------------------------------------------------
-  // These are _not_ output from teragram. Instead, they are as
-  // detected in the headers of usenet articles.
-  MSFT_CP1252,          // 27: CP1252 aka MSFT euro ascii
-  RUSSIAN_KOI8_RU,      // 28: CP21866 aka KOI8_RU, used for Ukrainian
-  MSFT_CP1250,          // 29: CP1250 aka MSFT eastern european
-  ISO_8859_15,          // 30: aka ISO_8859_0 aka ISO_8859_1 euroized
-  //----------------------------------------------------------
-
-  //----------------------------------------------------------
-  // These are in BasisTech but not in Teragram. They are
-  // needed for new interface languages. Now detected by
-  // research langid
-  MSFT_CP1254,          // 31: used for Turkish
-  MSFT_CP1257,          // 32: used in Baltic countries
-  //----------------------------------------------------------
-
-  //----------------------------------------------------------
-  //----------------------------------------------------------
-  // New encodings detected by Teragram
-  ISO_8859_11,          // 33: aka TIS-620, used for Thai
-  MSFT_CP874,           // 34: used for Thai
-  MSFT_CP1256,          // 35: used for Arabic
-
-  //----------------------------------------------------------
-  // Detected as ISO_8859_8 by Teragram, but can be found in META tags
-  MSFT_CP1255,          // 36: Logical Hebrew Microsoft
-  ISO_8859_8_I,         // 37: Iso Hebrew Logical
-  HEBREW_VISUAL,        // 38: Iso Hebrew Visual
-  //----------------------------------------------------------
-
-  //----------------------------------------------------------
-  // Detected by research langid
-  CZECH_CP852,          // 39
-  CZECH_CSN_369103,     // 40: aka ISO_IR_139 aka KOI8_CS
-  MSFT_CP1253,          // 41: used for Greek
-  RUSSIAN_CP866,        // 42
-  //----------------------------------------------------------
-  HZ_ENCODING,
-  ISO2022_CN,
-  ISO2022_KR,
-
-  NUM_ENCODINGS              // Always keep this at the end. It is not a
-                             // valid Encoding enum, it is only used to
-                             // indicate the total number of Encodings.
-};
-
-const int kNumLanguages = NUM_LANGUAGES;
-const int kNumEncodings = NUM_ENCODINGS;
-
-#endif  // OMAHA_COMMON_LANG_ENC_H_
diff --git a/common/lang_unittest.cc b/common/lang_unittest.cc
new file mode 100644
index 0000000..10c1582
--- /dev/null
+++ b/common/lang_unittest.cc
@@ -0,0 +1,250 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+#include <map>
+#include <vector>
+#include "omaha/base/app_util.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/string.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/lang.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+const int kNumberOfLanguages = 56;
+
+}  // namespace
+
+class LanguageManagerTest : public testing::Test {
+ protected:
+  CString GetLang(LANGID langid) {
+    return lang::GetLanguageForLangID(langid);
+  }
+};
+
+TEST_F(LanguageManagerTest, GetLanguageForLangID_NoLangID) {
+  EXPECT_STREQ(_T("en"), lang::GetLanguageForLangID(0xFFFF));
+}
+
+TEST_F(LanguageManagerTest, IsLanguageSupported) {
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("en")));
+
+  EXPECT_FALSE(lang::IsLanguageSupported(_T("")));
+  EXPECT_FALSE(lang::IsLanguageSupported(_T("non-existing lang")));
+  EXPECT_FALSE(lang::IsLanguageSupported(_T("en-US")));
+}
+
+TEST_F(LanguageManagerTest, GetLanguageForLangID_SupportedIds) {
+  EXPECT_STREQ(_T("am"), GetLang(MAKELANGID(LANG_AMHARIC, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("ar"), GetLang(MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("bg"), GetLang(MAKELANGID(LANG_BULGARIAN, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("ca"), GetLang(MAKELANGID(LANG_CATALAN, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("cs"), GetLang(MAKELANGID(LANG_CZECH, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("da"), GetLang(MAKELANGID(LANG_DANISH, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("de"), GetLang(MAKELANGID(LANG_GERMAN, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("el"), GetLang(MAKELANGID(LANG_GREEK, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("en"), GetLang(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("en-GB"), GetLang(MAKELANGID(LANG_ENGLISH,
+                                               SUBLANG_ENGLISH_UK)));
+  EXPECT_STREQ(_T("es"), GetLang(MAKELANGID(LANG_SPANISH,
+                                            SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("es"), GetLang(MAKELANGID(LANG_SPANISH,
+                                            SUBLANG_SPANISH)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
+                                                SUBLANG_SPANISH_MEXICAN)));
+  EXPECT_STREQ(_T("es"), GetLang(MAKELANGID(LANG_SPANISH,
+                                            SUBLANG_SPANISH_MODERN)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
+                                                SUBLANG_SPANISH_GUATEMALA)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
+                                                SUBLANG_SPANISH_COSTA_RICA)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
+                                                SUBLANG_SPANISH_PANAMA)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(
+      LANG_SPANISH,
+      SUBLANG_SPANISH_DOMINICAN_REPUBLIC)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
+                                                SUBLANG_SPANISH_VENEZUELA)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
+                                                SUBLANG_SPANISH_COLOMBIA)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
+                                                SUBLANG_SPANISH_PERU)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
+                                                SUBLANG_SPANISH_ARGENTINA)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
+                                                SUBLANG_SPANISH_ECUADOR)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
+                                                SUBLANG_SPANISH_CHILE)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
+                                                SUBLANG_SPANISH_URUGUAY)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
+                                                SUBLANG_SPANISH_PARAGUAY)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
+                                                SUBLANG_SPANISH_BOLIVIA)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
+                                                SUBLANG_SPANISH_EL_SALVADOR)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
+                                                SUBLANG_SPANISH_HONDURAS)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
+                                                SUBLANG_SPANISH_NICARAGUA)));
+  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
+                                                SUBLANG_SPANISH_PUERTO_RICO)));
+  EXPECT_STREQ(_T("et"), GetLang(MAKELANGID(LANG_ESTONIAN,
+                                            SUBLANG_ESTONIAN_ESTONIA)));
+  EXPECT_STREQ(_T("fi"), GetLang(MAKELANGID(LANG_FINNISH, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("fil"), GetLang(MAKELANGID(LANG_FILIPINO, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("fr"), GetLang(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("hi"), GetLang(MAKELANGID(LANG_HINDI, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("hr"), GetLang(MAKELANGID(LANG_CROATIAN, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("hr"), GetLang(MAKELANGID(LANG_SERBIAN,
+                                            SUBLANG_SERBIAN_CROATIA)));
+  EXPECT_STREQ(_T("hu"), GetLang(MAKELANGID(LANG_HUNGARIAN, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("id"), GetLang(MAKELANGID(LANG_INDONESIAN, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("it"), GetLang(MAKELANGID(LANG_ITALIAN, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("iw"), GetLang(MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("ja"), GetLang(MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("ko"), GetLang(MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("lt"), GetLang(MAKELANGID(LANG_LITHUANIAN, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("lv"), GetLang(MAKELANGID(LANG_LATVIAN, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("nl"), GetLang(MAKELANGID(LANG_DUTCH, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("no"), GetLang(MAKELANGID(LANG_NORWEGIAN, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("pl"), GetLang(MAKELANGID(LANG_POLISH, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("pt-BR"), GetLang(MAKELANGID(LANG_PORTUGUESE,
+                                               SUBLANG_PORTUGUESE_BRAZILIAN)));
+  EXPECT_STREQ(_T("pt-PT"), GetLang(MAKELANGID(LANG_PORTUGUESE,
+                                               SUBLANG_PORTUGUESE)));
+  EXPECT_STREQ(_T("ro"), GetLang(MAKELANGID(LANG_ROMANIAN, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("ru"), GetLang(MAKELANGID(LANG_RUSSIAN, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("sk"), GetLang(MAKELANGID(LANG_SLOVAK, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("sl"), GetLang(MAKELANGID(LANG_SLOVENIAN, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("sr"), GetLang(
+      MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_CYRILLIC)));
+  EXPECT_STREQ(_T("sr"), GetLang(
+      MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_LATIN)));
+  EXPECT_STREQ(_T("sr"), GetLang(MAKELANGID(LANG_SERBIAN,
+                                            SUBLANG_SERBIAN_CYRILLIC)));
+  EXPECT_STREQ(_T("sr"), GetLang(MAKELANGID(LANG_SERBIAN,
+                                            SUBLANG_SERBIAN_LATIN)));
+  EXPECT_STREQ(_T("sv"), GetLang(MAKELANGID(LANG_SWEDISH, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("sw"), GetLang(MAKELANGID(LANG_SWAHILI, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("th"), GetLang(MAKELANGID(LANG_THAI, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("tr"), GetLang(MAKELANGID(LANG_TURKISH, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("uk"), GetLang(MAKELANGID(LANG_UKRAINIAN, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("vi"), GetLang(MAKELANGID(LANG_VIETNAMESE, SUBLANG_DEFAULT)));
+  EXPECT_STREQ(_T("zh-HK"), GetLang(MAKELANGID(LANG_CHINESE,
+                                               SUBLANG_CHINESE_HONGKONG)));
+  EXPECT_STREQ(_T("zh-TW"), GetLang(MAKELANGID(LANG_CHINESE,
+                                               SUBLANG_CHINESE_MACAU)));
+  EXPECT_STREQ(_T("zh-CN"), GetLang(MAKELANGID(LANG_CHINESE,
+                                               SUBLANG_CHINESE_SIMPLIFIED)));
+  EXPECT_STREQ(_T("zh-CN"), GetLang(MAKELANGID(LANG_CHINESE,
+                                               SUBLANG_CHINESE_SINGAPORE)));
+  EXPECT_STREQ(_T("zh-TW"), GetLang(MAKELANGID(LANG_CHINESE,
+                                               SUBLANG_CHINESE_TRADITIONAL)));
+}
+
+// Unsupported languages and sublanguages fall back to "en".
+TEST_F(LanguageManagerTest, GetLanguageForLangID_UnsupportedSubLang) {
+  // LANG_NEUTRAL is unsupported.
+  EXPECT_STREQ(_T("en"), GetLang(MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)));
+  // LANG_AFRIKAANS is unsupported.
+  EXPECT_STREQ(_T("en"), GetLang(MAKELANGID(LANG_AFRIKAANS, SUBLANG_NEUTRAL)));
+  // SUBLANG_NEUTRAL is unsupported.
+  EXPECT_STREQ(_T("en"), GetLang(MAKELANGID(LANG_SPANISH, SUBLANG_NEUTRAL)));
+  // SUBLANG_SYS_DEFAULT is unsupported. It happens to be 2, which is not
+  // supported for Hungarian but is for English, Spanish, and others/
+  EXPECT_STREQ(_T("en"),
+               GetLang(MAKELANGID(LANG_HUNGARIAN, SUBLANG_SYS_DEFAULT)));
+  EXPECT_STREQ(_T("es-419"),
+               GetLang(MAKELANGID(LANG_SPANISH, SUBLANG_SYS_DEFAULT)));
+  // 0x3f is an invalid sublang. There is a "es" file.
+  EXPECT_STREQ(_T("en"), GetLang(MAKELANGID(LANG_SPANISH, 0x3f)));
+  // 0x3f is an invalid sublang. There is not a "zh" file.
+  EXPECT_STREQ(_T("en"), GetLang(MAKELANGID(LANG_CHINESE, 0x3f)));
+}
+
+TEST_F(LanguageManagerTest, TestCountLanguagesInTranslationTable) {
+  std::vector<CString> languages;
+  lang::GetSupportedLanguages(&languages);
+  EXPECT_EQ(kNumberOfLanguages, languages.size());
+}
+
+TEST_F(LanguageManagerTest, TestAppropriateLanguagesInTranslationTable) {
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("am")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("ar")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("bg")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("bn")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("ca")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("cs")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("da")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("de")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("el")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("en-GB")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("en")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("es-419")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("es")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("et")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("fa")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("fi")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("fil")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("fr")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("gu")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("hi")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("hr")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("hu")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("id")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("is")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("it")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("iw")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("ja")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("kn")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("ko")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("lt")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("lv")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("ml")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("mr")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("ms")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("nl")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("no")));
+  EXPECT_FALSE(lang::IsLanguageSupported(_T("or")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("pl")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("pt-BR")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("pt-PT")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("ro")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("ru")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("sk")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("sl")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("sr")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("sv")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("sw")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("ta")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("te")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("th")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("tr")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("uk")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("ur")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("vi")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("zh-CN")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("zh-HK")));
+  EXPECT_TRUE(lang::IsLanguageSupported(_T("zh-TW")));
+}
+
+}  // namespace omaha
diff --git a/common/localization.cc b/common/localization.cc
deleted file mode 100644
index 4a14058..0000000
--- a/common/localization.cc
+++ /dev/null
@@ -1,336 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// localization.cpp
-//
-// Localization functions for date-time, strings, locales, and numbers
-
-#include "omaha/common/localization.h"
-
-#include <windows.h>  // SetThreadLocale
-#include <mlang.h>
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/string.h"
-#include "omaha/common/time.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-// The maximum length for a date
-#define kDateLengthMax 200
-
-// The maximum length for chracter seperators
-#define kDecimalSeparatorMaxLen 5
-
-// Allow the unittest to override.
-static LCID lcid_override = MAKELCID(-1, -1);
-
-void SetLcidOverride(const LCID & lcid_new) {
-  lcid_override = lcid_new;
-}
-
-// We should call this, it allows for our override
-LCID GetLiveLcid() {
-  if (lcid_override != MAKELCID(-1, -1))
-    return lcid_override;
-
-  return GetUserDefaultLCID();
-}
-
-
-CString ShowDateInternal(const time64 & t, const LCID & lcid,
-                         // Either type needs to be 0 or format needs to be
-                         // NULL; both cannot be set simultaneously:
-                         const DWORD type, const TCHAR * format ) {
-  SYSTEMTIME time = Time64ToLocalTime(t);
-  TCHAR buf[kDateLengthMax] = {_T('\0')};
-  int num = ::GetDateFormat(lcid, type, &time, format, buf, kDateLengthMax);
-  ASSERT(num > 0, (_T("[localization::ShowDateInternal] - GetDateFormat ")
-                   _T("failed")));
-
-  return CString(buf);
-}
-
-CString ShowDateForLocale(const time64 & t, const LCID & lcid) {
-  return ShowDateInternal(t, lcid, DATE_SHORTDATE, NULL);
-}
-
-CString ShowFormattedDateForLocale(const time64 & t, const LCID & lcid,
-                                   const TCHAR * format) {
-  return ShowDateInternal(t, lcid, 0, format);
-}
-
-
-CString ShowTimeInternal(const time64 & t, const LCID & lcid,
-                         // Either type needs to be 0 or format needs to be
-                         // NULL; both cannot be set simultaneously:
-                         const DWORD type, const TCHAR * format) {
-  ASSERT(IsValidTime(t), (_T("[localization::ShowTimeInternal - Invalid ")
-                          _T("time %llu"), t));
-
-  SYSTEMTIME time = Time64ToLocalTime(t);
-  TCHAR buf[kDateLengthMax] = {_T('\0')};
-  int num = ::GetTimeFormat(lcid, type, &time, format, buf, kDateLengthMax);
-  ASSERT(num > 0, (_T("[localization::ShowTimeInternal - GetTimeFormat ")
-                   _T("failed")));
-
-  return CString(buf);
-}
-
-CString ShowTimeForLocale(const time64 & t, const LCID & lcid) {
-  return ShowTimeInternal(t, lcid, TIME_NOSECONDS, NULL);
-}
-
-CString ShowFormattedTimeForLocale(const time64 & t, const LCID & lcid,
-                                   const TCHAR * format) {
-  return ShowTimeInternal(t, lcid, 0, format);
-}
-
-// Show the long date and time [ie - Tuesday, March 20, 2004 5:15pm]
-CString ShowDateTimeForLocale(const time64 & t, const LCID & lcid) {
-  return ShowDateForLocale(t, lcid) + _T(" ") + ShowTimeForLocale(t, lcid);
-}
-
-// Get the long data and time in a (US English) format for logging
-CString ShowDateTimeForLogging(const time64 & t) {
-  if (t == 0) {
-     return CString();
-  }
-  const LCID lcid = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
-  return ShowDateTimeForLocale(t, lcid);
-}
-
-// Convert a number in string format to formatted string
-static CString Show(const CString & in, const int decimal_places) {
-  NUMBERFMT nf = {0};
-  TCHAR decimal_seperator[8] = {_T('\0')};
-  TCHAR thousands_seperator[8] = {_T('\0')};
-  GetNumberFormatForLCID(GetLiveLcid(), &nf,
-                         decimal_seperator, arraysize(decimal_seperator),
-                         thousands_seperator, arraysize(thousands_seperator));
-  nf.NumDigits = decimal_places;
-
-  TCHAR buf[kDateLengthMax] = {_T('\0')};
-  int num = GetNumberFormat(GetLiveLcid(),  // locale (current user locale)
-    0,                                      // options
-    in,                                     // input string (see MSDN for chars)
-    &nf,                                    // formatting information
-    buf,                                    // formatted string buffer
-    kDateLengthMax);                        // size of buffer
-
-  ASSERT(num > 0, (_T("GetNumberFormat failed: %s?"), in.GetString()));
-
-  return CString(buf);
-}
-
-// If we have a formatted number containing a decimal, and we want it to be
-// an int
-CString TrimDecimal(const CString & in) {
-  // Get the decimal seperator -- cache it as this is very slow
-  static LCID last_user_default_lcid = MAKELCID(-1, -1);
-  static TCHAR buf[kDecimalSeparatorMaxLen] = {_T('\0')};
-
-  LCID current_lcid = GetLiveLcid();
-  if (last_user_default_lcid != current_lcid) {
-    int num = GetLocaleInfo(GetLiveLcid(),
-                            LOCALE_SDECIMAL,
-                            buf,
-                            kDecimalSeparatorMaxLen);
-    ASSERT(num > 0, (L"GetLocaleInfo(.., LOCALE_SDECIMAL, ..) failed?"));
-    last_user_default_lcid = current_lcid;
-  }
-
-  CString sep(buf);
-
-  // Trim it if necessary
-  int pos = String_FindString(in, sep);
-  if (pos != -1)
-    return in.Left(pos);
-
-  return in;
-}
-
-// Number Functions
-// Changes the number into a user viewable format for the current locale
-
-// TODO(omaha): Rename these functions into ShowNumberForLocale.
-CString Show(const int i) {
-  return TrimDecimal(Show(itostr(i), 0));
-}
-
-CString Show(const uint32 u) {
-  return TrimDecimal(Show(itostr(u), 0));
-}
-
-CString Show(const double & d, const int decimal_places) {
-  return Show(String_DoubleToString(d, decimal_places), decimal_places);
-}
-
-HRESULT SetLocaleToRfc1766(const TCHAR * rfc1766_locale) {
-  ASSERT1(rfc1766_locale != NULL);
-
-  // Convert the RFC 1766 locale (eg, "fr-CA" for Canadian French to a
-  // Windows LCID (eg, 0x0c0c for Canadian French)
-  CComPtr<IMultiLanguage2> pIM;
-  RET_IF_FAILED(pIM.CoCreateInstance(__uuidof(CMultiLanguage)));
-
-  LCID lcid = 0;
-  CComBSTR rfc1766_locale_bstr(rfc1766_locale);
-  RET_IF_FAILED(pIM->GetLcidFromRfc1766(&lcid, (BSTR)rfc1766_locale_bstr));
-
-  return SetLocaleToLCID(lcid);
-}
-
-HRESULT SetLocaleToLCID(const LCID & lcid) {
-  // Initialize the locales
-  //   (in an attempt to cut down on our memory footprint, don't call
-  //    the libc version of setlocale)
-  if (!::SetThreadLocale(lcid)) {
-    UTIL_LOG(LEVEL_ERROR, (_T("Unable to SetThreadLocale to lcid 0x%x"),
-                           lcid));
-    return E_FAIL;
-  }
-
-  return S_OK;
-}
-
-HRESULT GetLocaleAsLCID(LCID * lcid) {
-  ASSERT1(lcid != NULL);
-
-  *lcid = GetThreadLocale();
-  return S_OK;
-}
-
-HRESULT GetLocaleAsRfc1766(CString * rfc1766_locale) {
-  ASSERT1(rfc1766_locale != NULL);
-
-  LCID lcid = 0;
-  HRESULT hr = GetLocaleAsLCID(&lcid);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  CComPtr<IMultiLanguage2> pIM;
-  RET_IF_FAILED(pIM.CoCreateInstance(__uuidof(CMultiLanguage)));
-
-  CComBSTR bstr;
-  RET_IF_FAILED(pIM->GetRfc1766FromLcid(lcid, &bstr));
-
-  *rfc1766_locale = bstr;
-  return hr;
-}
-
-HRESULT GetNumberFormatForLCID(const LCID & lcid, NUMBERFMT * fmt,
-                               TCHAR * fmt_decimal_buf,
-                               size_t decimal_buf_len,  // including null char
-                               TCHAR * fmt_thousand_buf,
-                               size_t thousand_buf_len) {  // including null
-  ASSERT1(fmt);
-
-  TCHAR buf[64] = {_T('\0')};
-  size_t buf_len = arraysize(buf);
-
-  HRESULT hr = S_OK;
-  int retval = GetLocaleInfo(lcid, LOCALE_IDIGITS, buf, buf_len);
-
-  if (!retval) {
-    CORE_LOG(LEVEL_WARNING, (_T("[localization::GetNumberFormatForLCID - ")
-                             _T("Failed to load LOCALE_IDIGITS]")));
-    hr = E_FAIL;
-  } else {
-    fmt->NumDigits = String_StringToInt(buf);
-  }
-
-  retval = GetLocaleInfo(lcid, LOCALE_ILZERO, buf, buf_len);
-  if (!retval) {
-    CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ")
-                             _T("Failed to load LOCALE_ILZERO]")));
-    hr = E_FAIL;
-  } else {
-    fmt->LeadingZero = String_StringToInt(buf);
-  }
-
-  retval = GetLocaleInfo(lcid, LOCALE_INEGNUMBER, buf, buf_len);
-  if (!retval) {
-    CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ")
-                             _T("Failed to load LOCALE_INEGNUMBER]")));
-    hr = E_FAIL;
-  } else {
-    fmt->NegativeOrder = String_StringToInt(buf);
-  }
-
-  retval = GetLocaleInfo(lcid, LOCALE_SGROUPING, buf, buf_len);
-  if (!retval) {
-    CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ")
-                             _T("Failed to load LOCALE_SGROUPING]")));
-    hr = E_FAIL;
-  } else {
-    // A string terminated in ';0' is equivalent to the substring without
-    // the ';0', so just truncate the ';0' from the string
-    int semicolon_idx = String_ReverseFindChar(buf, _T(';'));
-    if (retval > semicolon_idx && buf[semicolon_idx + 1] == _T('0')) {
-      buf[semicolon_idx] = _T('\0');
-    }
-
-    if (String_FindChar(buf, _T(';')) != -1) {
-      // NUMBERFMT only allows values 0-9 or 32 for number grouping.  If
-      // this locale has variable-length grouping rules (as indicated by
-      // the presence of ';[1-9]'), pass in the only variable-length
-      // grouping rule NUMBERFMT understands: 32.  Note that '3;0' is
-      // considered a fixed-length grouping rule and handled above.
-      // This is a HACK.
-      fmt->Grouping = 32;
-    } else {
-      fmt->Grouping = String_StringToInt(buf);
-    }
-  }
-
-  // GetLocaleInfo doesn't write more than 4 chars for this field (per MSDN)
-  retval = GetLocaleInfo(lcid, LOCALE_SDECIMAL, fmt_decimal_buf,
-                         decimal_buf_len);
-  if (!retval) {
-    CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ")
-                             _T("Failed to load LOCALE_SDECIMAL]")));
-    hr = E_FAIL;
-  } else {
-    fmt->lpDecimalSep = fmt_decimal_buf;
-  }
-
-  // GetLocaleInfo doesn't write more than 4 chars for this field (per MSDN)
-  retval = GetLocaleInfo(lcid, LOCALE_STHOUSAND, fmt_thousand_buf,
-                         thousand_buf_len);
-  if (!retval) {
-    CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ")
-                             _T("Failed to load LOCALE_STHOUSAND]")));
-    hr = E_FAIL;
-  } else {
-    fmt->lpThousandSep = fmt_thousand_buf;
-  }
-
-  retval = GetLocaleInfo(lcid, LOCALE_INEGNUMBER, buf, buf_len);
-  if (!retval) {
-    CORE_LOG(LEVEL_WARNING, (_T("[App::Impl::InitializeLocaleSettings - ")
-                             _T("Failed to load LOCALE_INEGNUMBER]")));
-    hr = E_FAIL;
-  } else {
-    fmt->NegativeOrder = String_StringToInt(buf);
-  }
-
-  return hr;
-}
-
-}  // namespace omaha
-
diff --git a/common/localization.h b/common/localization.h
deleted file mode 100644
index c710d72..0000000
--- a/common/localization.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-//
-// localization.h
-//
-// Localization functions for date-time, strings, locales, and numbers
-
-#ifndef OMAHA_COMMON_LOCALIZATION_H__
-#define OMAHA_COMMON_LOCALIZATION_H__
-
-#include <atlstr.h>
-#include "base/basictypes.h"
-#include "omaha/common/commontypes.h"
-
-namespace omaha {
-
-// Allows us to override LCIDs for unittests
-void SetLcidOverride(const LCID & lcid_new);
-
-
-//
-// Date-time Functions
-//
-
-// Show the time in the specified locale's default format.
-// If you want to use the user's default format, use LOCALE_USER_DEFAULT
-// for the locale [eg - "5:15:34 pm" is the US default]
-CString ShowDateForLocale(const time64 & t, const LCID & lcid);
-
-// Show the time in the specified format for the specified locale.
-// If you want to use the user's default format, use LOCALE_USER_DEFAULT
-// for the locale [eg - "5:15:34 pm" is the US default]
-CString ShowFormattedDateForLocale(const time64 & t, const LCID & lcid,
-                                   const TCHAR * format);
-
-
-// Show the time in the specified locale's default format.
-// If you want to use the user's default format, use LOCALE_USER_DEFAULT
-// for the locale [eg - "5:15:34 pm" is the US default]
-CString ShowTimeForLocale(const time64 & t, const LCID & lcid);
-
-// Show the time in the specified format for the specified locale.
-// If you want to use the user's default format, use LOCALE_USER_DEFAULT
-// for the locale [eg - "5:15:34 pm" is the US default]
-CString ShowFormattedTimeForLocale(const time64 & t, const LCID & lcid,
-                                   const TCHAR * format);
-
-// Show the long date and time [ie - Tuesday, March 20, 2004 5:15pm]
-CString ShowDateTimeForLocale(const time64 & t, const LCID & lcid);
-
-// Get the long data and time in a (US English) format for logging
-CString ShowDateTimeForLogging(const time64 & t);
-
-//
-// Number Functions
-//
-
-// Changes the number into a user viewable format for the current locale
-CString Show(const int i);
-CString Show(const uint32 u);
-CString Show(const double & d, const int decimal_places);
-
-
-//
-// Locale Name / LCID / RFC 1766 conversions
-//
-HRESULT SetLocaleToRfc1766(const TCHAR * rfc1766_locale);
-HRESULT SetLocaleToLCID(const LCID & lcid);
-
-HRESULT GetLocaleAsLCID(LCID * lcid);
-HRESULT GetLocaleAsRfc1766(CString * rfc1766_locale);
-
-HRESULT GetNumberFormatForLCID(const LCID & lcid, NUMBERFMT * fmt,
-                               TCHAR * fmt_decimal_buf,
-                               size_t decimal_buf_len,
-                               TCHAR * fmt_thousand_buf,
-                               size_t thousand_buf_len);
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_LOCALIZATION_H__
diff --git a/common/localization_unittest.cc b/common/localization_unittest.cc
deleted file mode 100644
index 2fcc7f4..0000000
--- a/common/localization_unittest.cc
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// localization_unittest.cpp
-//
-// Unit test functions for Localization
-
-#include "omaha/common/localization.h"
-#include "omaha/common/string.h"
-#include "omaha/common/time.h"
-#include "omaha/testing/unit_test.h"
-
-using testing::Message;
-
-namespace omaha {
-
-// Test out the time display functions
-void LocalizationTimeTest() {
-  CString time_str;
-
-  // Lets process this a bit to give ourselves a known time.
-  SYSTEMTIME temp_time;
-  temp_time.wYear = 2004;
-  temp_time.wMonth = 4;
-  temp_time.wDayOfWeek = 1;
-  temp_time.wDay = 19;
-  temp_time.wHour = 19;
-  temp_time.wMinute = 18;
-  temp_time.wSecond = 17;
-  temp_time.wMilliseconds = 16;
-
-  time64 override_time = SystemTimeToTime64(&temp_time);
-
-  // Useful when debugging to confirm that this worked
-  SYSTEMTIME confirm = Time64ToSystemTime(override_time);
-
-  // the need to check two different times below is because:
-
-  // FileTimeToLocalFileTime uses the current settings for the time
-  // zone and daylight saving time. Therefore, if it is daylight
-  // saving time, this function will take daylight saving time into
-  // account, even if the time you are converting is in standard
-  // time.
-  // TODO(omaha): we may want to fix this.
-
-  // Show just the time [ie -12:19pm]
-  time_str = ShowTimeForLocale(override_time, 1033 /* US english */);
-  ASSERT_TRUE(time_str == _T("12:18 PM") || time_str == _T("11:18 AM"))
-      << _T("Returned time string was ") << time_str.GetString();
-
-  // Show just the time [ie - 12:19:18pm]
-  time_str = ShowFormattedTimeForLocale(override_time, 1033,
-                                        _T("hh:mm:ss tt"));
-  ASSERT_TRUE(time_str == _T("12:18:17 PM") || time_str == _T("11:18:17 AM"))
-      << _T("Returned time string was ") << time_str.GetString();
-
-  // Try it out with a some different values to test out single digit
-  // minutes and such
-  temp_time.wHour = 15;
-  temp_time.wMinute = 4;
-  temp_time.wSecond = 3;
-  temp_time.wMilliseconds = 2;
-  override_time = SystemTimeToTime64(&temp_time);
-
-  time_str = ShowTimeForLocale(override_time, 1033);
-  ASSERT_TRUE(time_str == _T("8:04 AM") || time_str == _T("7:04 AM"))
-      << _T("Returned time string was ") << time_str.GetString();
-
-  time_str = ShowFormattedTimeForLocale(override_time, 1033,
-                                        _T("hh:mm:ss tt"));
-  ASSERT_TRUE(time_str == _T("08:04:03 AM") || time_str == _T("07:04:03 AM"))
-      << _T("Returned time string was ") << time_str.GetString();
-
-
-  //
-  // Check the date functionality
-  //
-
-  temp_time.wYear = 2004;
-  temp_time.wMonth = 4;
-  temp_time.wDayOfWeek = 1;
-  temp_time.wDay = 19;
-
-  // Show the short date
-  time_str = ShowDateForLocale(override_time, 1033);
-//  CHKM(time_str == _T("Monday, April 19, 2004"),
-  ASSERT_STREQ(time_str, _T("4/19/2004"));
-
-  // Show the customized date
-  time_str = ShowFormattedDateForLocale(override_time, 1033,
-                                        _T("MMM d, yyyy"));
-  ASSERT_STREQ(time_str, _T("Apr 19, 2004"));
-
-  // Try it out with a some different values to test out single dates and such
-  temp_time.wDay = 1;
-  override_time = SystemTimeToTime64(&temp_time);
-
-  time_str = ShowFormattedDateForLocale(override_time, 1033,
-                                        _T("ddd, MMM dd"));
-  ASSERT_STREQ(time_str, _T("Thu, Apr 01"));
-
-  time_str = ShowFormattedDateForLocale(override_time, 1033, _T("MM/dd/yyyy"));
-  ASSERT_STREQ(time_str, _T("04/01/2004"));
-}
-
-// Test out the numbers and display functions
-void LocalizationNumberTest() {
-  // Make sure we are using the normal american version
-  SetLcidOverride(1033);  // the codepage for american english
-
-  // Try some basics
-  ASSERT_STREQ(Show(1), _T("1"));
-  ASSERT_STREQ(Show(2), _T("2"));
-
-  // Try some extremes
-  ASSERT_STREQ(Show(0), _T("0"));
-  ASSERT_STREQ(Show(kInt32Max), _T("2,147,483,647"));
-  ASSERT_STREQ(Show(-kInt32Max), _T("-2,147,483,647"));
-  ASSERT_STREQ(Show(kUint32Max), _T("4,294,967,295"));
-
-  // Try some doubles
-  ASSERT_STREQ(Show(0.3, 0), _T("0"));
-  ASSERT_STREQ(Show(0.3, 1), _T("0.3"));
-  ASSERT_STREQ(Show(0.3, 2), _T("0.30"));
-  ASSERT_STREQ(Show(0.3, 5), _T("0.30000"));
-
-  // Try some with interesting rounding
-  ASSERT_STREQ(Show(0.159, 0), _T("0"));
-  ASSERT_STREQ(Show(0.159, 1), _T("0.1"));
-  ASSERT_STREQ(Show(0.159, 2), _T("0.15"));
-  ASSERT_STREQ(Show(0.159, 5), _T("0.15900"));
-
-  // Try a nice whole number
-  ASSERT_STREQ(Show(12.0, 0), _T("12"));
-  ASSERT_STREQ(Show(12.0, 1), _T("12.0"));
-  ASSERT_STREQ(Show(12.0, 2), _T("12.00"));
-  ASSERT_STREQ(Show(12.0, 5), _T("12.00000"));
-}
-
-TEST(LocalizationTest, Localization) {
-  LocalizationTimeTest();
-  LocalizationNumberTest();
-}
-
-}  // namespace omaha
-
diff --git a/common/lock_ptr.h b/common/lock_ptr.h
deleted file mode 100644
index da29f1d..0000000
--- a/common/lock_ptr.h
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// lock_ptr.h
-//
-// A smart pointer to manage synchronized access to a shared resource.
-//
-// LockPtr provides a simple and concise syntax for accessing a
-// shared resource. The LockPtr is a smart pointer and provides
-// pointer operators -> and *. LockPtr does not have copy semantics and it is
-// not intended to be stored in containers. Instead, instances of LockPtr are
-// usually unamed or short-lived named variables.
-//
-// LockPtr uses an external lock, it acquires the lock in the constructor, and
-// it guarantees the lock is released in the destructor.
-//
-// Since different types of locks have different method names, such as
-// Enter/Exit or Lock/Unlock, etc, LockPtr uses an external customizable policy
-// to bind to different operations. The external policy is a set of template
-// functions that can be specialized for different types of locks, if needed.
-// Think of this policy as an adapter between the lock type and the LockPtr.
-//
-// Usage: let's assume that we have the type below:
-//
-// class X {
-//  public:
-//    X() : i_(0) {}
-//    void f() {}
-//
-//  private:
-//    int i_;
-//
-//    friend int LockPtrTest(int, int);
-// };
-//
-// We have an instance of this type and an external lock instance to serialize
-// the access to the X instance.
-//
-// Using LockPtr, the code is:
-//
-//    X x;
-//    LLock local_lock;
-//
-//    LockPtr<X>(x, local_lock)->f();
-//
-// For more example, please see the unit test of the module.
-
-
-
-#ifndef OMAHA_COMMON_LOCK_PTR_H_
-#define OMAHA_COMMON_LOCK_PTR_H_
-
-#include "omaha/common/debug.h"
-
-namespace omaha {
-
-template <typename T>
-class LockPtr {
- public:
-  template <typename U>
-  LockPtr(T& obj, U& lock)
-      : pobj_(&obj),
-        plock_(&lock),
-        punlock_method_(&LockPtr::Unlock<U>) {
-    AcquireLock(lock);
-  }
-
-  ~LockPtr() {
-    ASSERT1(punlock_method_);
-    (this->*punlock_method_)();
-  }
-
-  // Pointer behavior
-  T& operator*() {
-    ASSERT1(pobj_);
-    return *pobj_;
-  }
-
-  T* operator->() {
-    return pobj_;
-  }
-
- private:
-  // template method to restore the type of the lock and to call the
-  // release policy for the lock
-  template <class U>
-  void Unlock() {
-    ASSERT1(plock_);
-    U& lock = *(static_cast<U*>(plock_));
-    ReleaseLock(lock);
-  }
-
-  T* pobj_;       // managed shared object
-  void* plock_;   // type-less lock to control access to pobj_
-
-  void (LockPtr::*punlock_method_)();   // the address of the method to Unlock
-
-  DISALLOW_EVIL_CONSTRUCTORS(LockPtr);
-};
-
-// template functions to define the policy of acquiring and releasing
-// the locks.
-template <class Lock> inline void AcquireLock(Lock& lock) { lock.Lock(); }
-template <class Lock> inline void ReleaseLock(Lock& lock) { lock.Unlock(); }
-
-// specialization of policy for diferent types of locks.
-#include "omaha/common/synchronized.h"
-template <> void inline AcquireLock(CriticalSection& cs) { cs.Enter(); }
-template <> void inline ReleaseLock(CriticalSection& cs) { cs.Exit(); }
-
-// Add more policy specializations below, if needed.
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_LOCK_PTR_H_
-
diff --git a/common/lock_ptr_unittest.cc b/common/lock_ptr_unittest.cc
deleted file mode 100644
index 4a363f7..0000000
--- a/common/lock_ptr_unittest.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// lock_ptr_unittest.cpp
-
-#include "omaha/common/lock_ptr.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-// Test type.
-class X {
- public:
-  X() : i_(0) {}
-  void f() { i_ = 10; }
-
- private:
-  int i_;
-
-  friend class LockTest;
-  FRIEND_TEST(LockTest, X);
-};
-
-// A dummy lock just to test that locking and unlocking methods get called.
-class DummyLock {
- public:
-  DummyLock() : cnt_(0) {}
-
-  void FakeLock() { ++cnt_; }
-  void FakeUnlock() { --cnt_; }
-
- private:
-  int cnt_;
-
-  friend class LockTest;
-  FRIEND_TEST(LockTest, DummyLock);
-};
-
-// Specialization of the policy for the DummyLock.
-template<> void AcquireLock(DummyLock& lock) { lock.FakeLock(); }
-template<> void ReleaseLock(DummyLock& lock) { lock.FakeUnlock(); }
-
-// Empty test fixture.
-class LockTest : public testing::Test {};
-
-TEST_F(LockTest, X) {
-  // Create a few synchronization objects.
-  CriticalSection cs_lock;
-  LLock local_lock;
-  GLock global_lock;
-  ASSERT_TRUE(global_lock.Initialize(_T("test")));
-
-  // The instance to lock.
-  X x;
-
-  // Lock the instance and call a method.
-  LockPtr<X>(x, local_lock)->f();
-  ASSERT_EQ((*LockPtr<X>(x, cs_lock)).i_, 10);
-
-  // Lock the instance and access a data member.
-  LockPtr<X>(x, cs_lock)->i_ = 0;
-  ASSERT_EQ((*LockPtr<X>(x, cs_lock)).i_, 0);
-
-  // Lock the instance and call a method.
-  LockPtr<X>(x, global_lock)->f();
-  ASSERT_EQ((*LockPtr<X>(x, cs_lock)).i_, 10);
-}
-
-TEST_F(LockTest, DummyLock) {
-  DummyLock dummy_lock;
-  ASSERT_EQ(dummy_lock.cnt_, 0);
-
-  // The instance to lock.
-  X x;
-
-  {
-    LockPtr<X> p(x, dummy_lock);
-    ASSERT_EQ(dummy_lock.cnt_, 1);
-  }
-
-  ASSERT_EQ(dummy_lock.cnt_, 0);
-}
-
-}  // namespace omaha
-
diff --git a/common/logging.cc b/common/logging.cc
deleted file mode 100644
index 3cdfcd0..0000000
--- a/common/logging.cc
+++ /dev/null
@@ -1,1349 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Tracing and logging system.
-//
-// The log output goes to "All Users/Application Data/Google/Update/Log/".
-// The log configuration file is under
-// "%Program Files%/C:\Program Files\Google\Common\Update".
-// By default, the logging is on in debug modes and off in opt mode although in
-// opt mode only the OPT_LOG statements write to the log.
-//
-// The log is open to all the users to write to. There is a known vulnerability
-// where a DOS can be created by holding on to the logging mutex.
-//
-// In this module use of ASSERT & REPORT is banned.  This is to prevent any
-// possible recursion issues between logging (logging.h) and
-// asserting/reporting (debug.h).  Both are basement-level systems that need to
-// work when almost nothing else works and interdependencies are best avoided.
-// One unavoidable interdependency is that debugASSERT will send messages to
-// the logger via Logger::OutputMessage - which then broadcasts it to each
-// LogWriter's OutputMessage.  So these methods should be as simple as
-// possible.  (Also, unlike asserting/reporting - this module will not avoid
-// use of the heap, however the code executed from Logger::OutputMessage
-// doesn't use the heap (for all the LogWriters in this file).)
-//
-// TODO(omaha): implement the minidump handling in terms of breakpad.
-//              Log initialization if full of lazy init. Consider doing
-//              eager init of log and its registered log writers when the
-//              log is created and initialized.
-//              Reimplement in terms of smart handles and locks.
-//              Reimplement without dependency on any other compilation unit
-//               that can call assert, verify, or the log itself.
-//              Redo the history logging feature
-
-#include "omaha/common/logging.h"
-
-#include <excpt.h>  // Microsoft specific: structured exceptions.
-#include <shlobj.h>
-#include <shlwapi.h>
-#include <string.h>
-#include <atlpath.h>
-#include <atlsecurity.h>
-#include "base/basictypes.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/const_debug.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/file.h"
-#include "omaha/common/path.h"
-#include "omaha/common/string.h"
-#include "omaha/common/time.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-// enforce ban on ASSERT/REPORT
-#undef ASSERT
-#undef REPORT
-
-#ifdef LOGGING
-
-#define kNumLockRetries (20)
-#define kLockRetryDelayMs (50)
-
-// Circular buffer to log history.
-static wchar_t history_buffer[kMaxHistoryBufferSize];
-
-// Index into the history buffer to begin writing at.
-static int history_buffer_next_idx = 0;
-
-// Indicates whether the history buffer has ever reached its full capacity.
-// Once this boolean is true, if never becomes false.
-static bool history_buffer_full = false;
-
-// If you want to log anything else, add it to this structure.  This structure
-// basically says that each logged message is composed of two parts, and they
-// are output one after the other.  Intended to be used for a message "prefix"
-// and the message itself (the prefix can contain the component name, the time,
-// and any other logging system boilerplate, while the message is supplied by
-// the component).  (This basically saves having to copy a variable length -
-// possibly very large - message just to tack it onto the end of the message
-// prefix.)
-struct OutputInfo {
-  LogCategory category;
-  LogLevel level;
-  const wchar_t* msg1;
-  const wchar_t* msg2;
-
-  OutputInfo(LogCategory cat, LogLevel log_level,
-             const wchar_t* m1, const wchar_t* m2)
-      : category(cat),
-        level(log_level),
-        msg1(m1),
-        msg2(m2) {}
-};
-
-//
-// Table of category names to categories.
-//
-#define LC_ENTRY(lc_value)  (L#lc_value), (lc_value)
-struct {
-  wchar_t* category_name;
-  LogCategory category;
-} static LogCategoryNames[] = {
-  LC_ENTRY(LC_UTIL),
-  LC_ENTRY(LC_SETUP),
-  LC_ENTRY(LC_SHELL),
-  LC_ENTRY(LC_CORE),
-  LC_ENTRY(LC_JS),
-  LC_ENTRY(LC_PLUGIN),
-  LC_ENTRY(LC_SERVICE),
-  LC_ENTRY(LC_OPT),
-  LC_ENTRY(LC_NET),
-};
-
-static CString StripBackslashes(const CString& name) {
-  int n = String_FindChar(name, L'\\');
-  if (n == -1) {
-    return name;
-  } else {
-    CString result;
-    for (int i = 0; i < name.GetLength(); ++i) {
-      if (name[i] != L'\\') {
-        result += name[i];
-      }
-    }
-    return result;
-  }
-}
-
-static CString GetProcName() {
-  CString proc_name(app_util::GetAppNameWithoutExtension());
-  CString module_name(app_util::GetCurrentModuleNameWithoutExtension());
-  CString result(proc_name);
-  if (module_name.CompareNoCase(proc_name) != 0) {
-    result += L":";
-    result += module_name;
-  }
-  return result;
-}
-
-// Formats a line prefix with the current time min:sec:milisec if wanted,
-// otherwise just the process:module.
-static void FormatLinePrefix(bool show_time,
-                             const wchar_t* proc_name,
-                             CString& result) {
-  if (show_time) {
-    SYSTEMTIME system_time = {0};
-    GetLocalTime(&system_time);
-    result.Format(L"[%02d/%02d/%02d %02d:%02d:%02d.%03d]",
-                  system_time.wMonth, system_time.wDay, system_time.wYear % 100,
-                  system_time.wHour, system_time.wMinute, system_time.wSecond,
-                  system_time.wMilliseconds);
-  }
-  result.AppendFormat(L"[%s][%u:%u]",
-                      proc_name,
-                      ::GetCurrentProcessId(),
-                      ::GetCurrentThreadId());
-}
-
-static bool g_logging_valid = false;
-static Logging g_logging;
-
-// Singleton factory for the Logging object.
-Logging* GetLogging() {
-  return g_logging_valid ? &g_logging : NULL;
-}
-
-// Force the logging system to be initialized during elaboration of static
-// constructors, while the program is still single-threaded.  This global
-// static object will cause the static logger inside of GetLogging() to be
-// constructed.  However, it might not be the first call to GetLogging() -
-// another static object in another object file might get constructed first,
-// and in its constructor call a logging API.  Just as long as it is done when
-// the system is single threaded.  (Reason: because the Logger object has a
-// LLock object which has a Win32 critical section which needs to be
-// initialized - only once!)
-
-
-Logging::Logging()
-    : logging_initialized_(false),
-      logging_enabled_(true),
-      force_show_time_(false),
-      show_time_(true),
-      log_to_file_(true),
-      log_to_debug_out_(true),
-      append_to_file_(true),
-      logging_shutdown_(false),
-      num_writers_(0),
-      file_log_writer_(NULL),
-      debug_out_writer_(NULL),
-      is_initializing_(false),
-      log_file_name_(kDefaultLogFileName),
-      config_file_path_(GetConfigurationFilePath()) {
-  g_last_category_check_time = 0;
-  for (int i = 0; i < max_writers; ++i) {
-    writers_[i] = NULL;
-  }
-  proc_name_ = GetProcName();
-  g_logging_valid = true;
-
-  // Read initial settings from the config file.
-  ReadLoggingSettings();
-}
-
-// TODO(omaha): why aren't we using a mutexscope and what if an the code
-// throws? Will the lock be unlocked?
-Logging::~Logging() {
-  // Acquire the lock outside the try/except block so we'll always release it
-  lock_.Lock();
-
-  __try {
-    // prevent further access to the system
-    // necessary because the static destructors happen
-    // in a non-deterministic order
-    logging_shutdown_ = true;
-
-    // Delete all registered LogWriters
-    for (int i = 0; i < num_writers_; ++i) {
-      if (writers_[i]) {
-        delete writers_[i];
-      }
-    }
-
-    logging_initialized_ = false;
-  } __except(SehNoMinidump(GetExceptionCode(),
-                           GetExceptionInformation(),
-                           __FILE__,
-                           __LINE__,
-                           true)) {
-    OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n");
-    logging_initialized_  = false;
-  }
-
-  g_logging_valid = false;
-  lock_.Unlock();
-}
-
-void Logging::UpdateCatAndLevel(const wchar_t* cat_name, LogCategory cat) {
-  if (cat_name == NULL) {
-    return;
-  }
-  if (cat >= LC_MAX_CAT) {
-    return;
-  }
-  int log_level = kDefaultLogLevel;
-  CString config_file = GetCurrentConfigurationFilePath();
-  if (!config_file.IsEmpty()) {
-    log_level = GetPrivateProfileInt(kConfigSectionLoggingLevel,
-                                     cat_name,
-                                     kDefaultLogLevel,
-                                     config_file);
-  }
-  category_list_[cat].enabled = (log_level != 0);
-  category_list_[cat].log_level = static_cast<LogLevel>(log_level);
-}
-
-void Logging::ReadLoggingSettings() {
-  CString config_file = GetCurrentConfigurationFilePath();
-  if (!config_file.IsEmpty()) {
-    logging_enabled_ = ::GetPrivateProfileInt(
-        kConfigSectionLoggingSettings,
-        kConfigAttrEnableLogging,
-        kDefaultLoggingEnabled,
-        config_file) == 0 ? false : true;
-
-    show_time_ = ::GetPrivateProfileInt(
-        kConfigSectionLoggingSettings,
-        kConfigAttrShowTime,
-        kDefaultShowTime,
-        config_file) == 0 ? false : true;
-
-    log_to_file_ = ::GetPrivateProfileInt(
-        kConfigSectionLoggingSettings,
-        kConfigAttrLogToFile,
-        kDefaultLogToFile,
-        config_file) == 0 ? false : true;
-
-    log_to_debug_out_ = ::GetPrivateProfileInt(
-        kConfigSectionLoggingSettings,
-        kConfigAttrLogToOutputDebug,
-        kDefaultLogToOutputDebug,
-        config_file) == 0 ? false : true;
-
-    append_to_file_ = ::GetPrivateProfileInt(
-        kConfigSectionLoggingSettings,
-        kConfigAttrAppendToFile,
-        kDefaultAppendToFile,
-        config_file) == 0 ? false : true;
-
-    ::GetPrivateProfileString(kConfigSectionLoggingSettings,
-                              kConfigAttrLogFilePath,
-                              kDefaultLogFileName,
-                              CStrBuf(log_file_name_, MAX_PATH),
-                              MAX_PATH,
-                              config_file);
-  } else {
-    logging_enabled_ = kDefaultLoggingEnabled;
-    show_time_ = kDefaultShowTime;
-    log_to_file_ = kDefaultLogToFile;
-    log_to_debug_out_ = kDefaultLogToOutputDebug;
-    append_to_file_ = kDefaultAppendToFile;
-    log_file_name_ = kDefaultLogFileName;
-  }
-
-  if (force_show_time_) {
-    show_time_ = true;
-  }
-
-  // The "default" category is always enabled.
-  category_list_[LC_LOGGING].enabled = true;
-  category_list_[LC_LOGGING].log_level = LEVEL_ALL;
-
-  // Read each category from the ini file.
-  for (size_t i = 0; i < arraysize(LogCategoryNames); ++i) {
-    UpdateCatAndLevel(LogCategoryNames[i].category_name,
-                      LogCategoryNames[i].category);
-  }
-
-  g_last_category_check_time = GetCurrent100NSTime();
-}
-
-CString Logging::GetDefaultLogDirectory() const {
-  CString path;
-  CStrBuf buf(path, MAX_PATH);
-  HRESULT hr = ::SHGetFolderPath(NULL,
-                                 CSIDL_COMMON_APPDATA,
-                                 NULL,
-                                 SHGFP_TYPE_CURRENT,
-                                 buf);
-  if (FAILED(hr)) {
-    return L"";
-  }
-  if (!::PathAppend(buf, OMAHA_REL_LOG_DIR)) {
-    return L"";
-  }
-  return path;
-}
-
-CString Logging::GetLogFilePath() const {
-  if (log_file_name_.IsEmpty()) {
-    return CString();
-  }
-
-  if (!ATLPath::IsRelative(log_file_name_)) {
-    return log_file_name_;
-  }
-
-  CString path = GetDefaultLogDirectory();
-  if (path.IsEmpty()) {
-    return CString();
-  }
-
-  if (!::PathAppend(CStrBuf(path, MAX_PATH), log_file_name_)) {
-    return CString();
-  }
-
-  return path;
-}
-
-// Configures/unconfigures the log writers for the current settings.
-bool Logging::ConfigureLogging() {
-  // Create the logging file.
-  if (log_to_file_ && file_log_writer_ == NULL) {
-    CString path = GetLogFilePath();
-    if (path.IsEmpty()) {
-      return false;
-    }
-
-    // Extract the final target directory which will not be what
-    // GetDefaultLogDirectory() returns if log_file_name_ is an absolute path.
-    CString log_file_dir = GetDirectoryFromPath(path);
-    if (!File::Exists(log_file_dir)) {
-      if (FAILED(CreateDir(log_file_dir, NULL))) {
-        return false;
-      }
-    }
-    file_log_writer_ = FileLogWriter::Create(path, append_to_file_);
-    if (file_log_writer_ == NULL) {
-      OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - "
-                                L"Cannot create log writer to %s",
-                                proc_name_, path));
-    }
-  }
-  if (log_to_file_ && file_log_writer_ != NULL) {
-    InternalRegisterWriter(file_log_writer_);
-  }
-  if (log_to_debug_out_ && debug_out_writer_ == NULL) {
-    debug_out_writer_ = OutputDebugStringLogWriter::Create();
-    if (debug_out_writer_ == NULL) {
-      OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - "
-                                L"Cannot create OutputDebugString log writer",
-                                proc_name_));
-    }
-  }
-  if (log_to_debug_out_ && debug_out_writer_ != NULL) {
-    InternalRegisterWriter(debug_out_writer_);
-  }
-  return true;
-}
-
-void Logging::UnconfigureLogging() {
-  if (file_log_writer_ != NULL) {
-    InternalUnregisterWriter(file_log_writer_);
-  }
-  if (debug_out_writer_ != NULL) {
-    InternalUnregisterWriter(debug_out_writer_);
-  }
-}
-
-bool Logging::InternalInitialize() {
-  __try {
-    if (logging_shutdown_ == true) {
-      OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - "
-                                L"Calling the logging system after "
-                                L"it has been shut down \n",
-                                GetProcName()));
-      return false;
-    }
-
-    if (logging_initialized_ == true) {
-      return true;
-    }
-    // If something called by this method is attempting to do logging,
-    // just ignore it. The cost/benefit ratio is too high to do otherwise.
-    if (is_initializing_) {
-      return false;
-    }
-    is_initializing_ = true;
-
-    // Read the initial settings from the config file.
-    ReadLoggingSettings();
-
-    // Initialize logging system if enabled at start.
-    if (logging_enabled_) {
-      logging_initialized_ = ConfigureLogging();
-    }
-  } __except(SehNoMinidump(GetExceptionCode(),
-                           GetExceptionInformation(),
-                           __FILE__,
-                           __LINE__,
-                           true)) {
-    OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n");
-    logging_initialized_  = false;
-    return false;
-  }
-
-  is_initializing_ = false;
-  return true;
-}
-
-bool Logging::InitializeLogging() {
-  // Double-checked locking idiom is broken, especially on multicore machines.
-  // TODO(omaha): understand how this works and fix it.
-  if (logging_shutdown_ == true) {
-    OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: ERROR - Calling the logging "
-                              L"system after it has been shut down \n",
-                              GetProcName()));
-    return false;
-  }
-
-  if (logging_initialized_ == true) {
-    return true;
-  }
-
-  // Acquire the lock outside the try/except block so we'll always release it.
-  __mutexScope(lock_);
-  return InternalInitialize();
-}
-
-// Enables/disables the logging mechanism. Allows turning logging on/off
-// in mid-run.
-// TODO(omaha):  same comment as for the destructor.
-void Logging::EnableLogging() {
-  if (!InitializeLogging()) {
-    return;
-  }
-
-  // Acquire the lock outside the try/except block so we'll always release it.
-  lock_.Lock();
-
-  __try {
-    if (!logging_enabled_) {
-      ConfigureLogging();
-      logging_enabled_ = true;
-    }
-  } __except(SehNoMinidump(GetExceptionCode(),
-                           GetExceptionInformation(),
-                           __FILE__,
-                           __LINE__,
-                           true)) {
-    OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n");
-    logging_enabled_  = false;
-  }
-
-  lock_.Unlock();
-}
-
-void Logging::DisableLogging() {
-  if (!InitializeLogging()) {
-    return;
-  }
-
-  // Acquire the lock outside the try/except block so we'll always release it.
-  lock_.Lock();
-
-  __try {
-    if (logging_enabled_) {
-      logging_enabled_ = false;
-      UnconfigureLogging();
-    }
-  } __except(SehNoMinidump(GetExceptionCode(),
-             GetExceptionInformation(),
-             __FILE__,
-             __LINE__,
-             true)) {
-    OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n");
-    logging_enabled_  = false;
-  }
-
-  lock_.Unlock();
-}
-
-// Checks if logging is enabled - and updates logging settings from the
-// configuration file every kLogSettingsCheckInterval seconds.
-bool Logging::IsLoggingEnabled() {
-  if (!InitializeLogging()) {
-    return false;
-  }
-
-  // Dynamic update - including reading a new value of logging_enabled_.
-  bool prev_logging_enabled = logging_enabled_;
-  if (GetCurrent100NSTime() >
-      g_last_category_check_time + kLogSettingsCheckInterval) {
-    ReadLoggingSettings();
-  }
-
-  // If enabled state has changed either enable or disable logging.
-  if (prev_logging_enabled != logging_enabled_) {
-    if (logging_enabled_) {
-      EnableLogging();
-    } else {
-      DisableLogging();
-    }
-  }
-
-  return logging_enabled_;
-}
-
-bool Logging::IsLoggingAlreadyEnabled() const {
-  return logging_enabled_;
-}
-
-void Logging::ForceShowTimestamp(bool force_show_time) {
-  force_show_time_ = show_time_ = force_show_time;
-}
-
-// Get category level
-LogLevel Logging::GetCatLevel(LogCategory category) const {
-  if (!IsLoggingAlreadyEnabled()) {
-    return kDefaultLogLevel;
-  }
-
-  if (category >= LC_MAX_CAT) {
-    return kDefaultLogLevel;
-  }
-
-  return category_list_[category].log_level;
-}
-
-// Check if logging is enabled for a given category and level
-DWORD Logging::IsCatLevelEnabled(LogCategory category, LogLevel level) {
-  if (!IsLoggingEnabled()) {
-    return 0;
-  }
-
-  if (category >= LC_MAX_CAT) {
-    return 0;
-  }
-
-  // If the config value is to log: then log to all writers.
-  if (category_list_[category].enabled &&
-      level <= category_list_[category].log_level) {
-    return static_cast<DWORD>(all_writers_mask);
-  }
-
-  // Check each of the registered loggers to see if they want to override the
-  // negative config value.
-  DWORD mask = 0;
-  for (int i = num_writers_ - 1; i >= 0; --i) {
-    mask <<= 1;
-    if (writers_[i] && writers_[i]->IsCatLevelEnabled(category, level)) {
-      mask |= 1;
-    }
-  }
-
-  return mask;
-}
-
-// TODO(omaha): For now this is hard coded and there is no way to override
-// writing other log categories into the history. Add a store_in_history
-// boolean into the CategoryInfo struct that allows reading from the config
-// file. This will enable other log categories to get buffered in the history.
-bool Logging::IsCategoryEnabledForBuffering(LogCategory cat) {
-  return cat == LC_REPORT;
-}
-
-void Logging::LogMessage(LogCategory cat, LogLevel level,
-                         const wchar_t* fmt, ...) {
-  va_list args;
-  va_start(args, fmt);
-  LogMessageVA(cat, level, fmt, args);
-  va_end(args);
-}
-
-void Logging::LogMessageVA(LogCategory cat, LogLevel level,
-                           const wchar_t* fmt, va_list args) {
-  LogMessageMaskedVA(static_cast<DWORD>(all_writers_mask),
-                     cat,
-                     level,
-                     fmt,
-                     args);
-}
-
-void Logging::InternalLogMessageMaskedVA(DWORD writer_mask,
-                                         LogCategory cat,
-                                         LogLevel level,
-                                         CString* log_buffer,
-                                         CString* prefix,
-                                         const wchar_t* fmt,
-                                         va_list args) {
-  __try {
-    // Initial buffer size in characters.
-    // It will adjust dynamically if the message is bigger.
-    DWORD buffer_size = 512;
-
-    // Count of chars / bytes written.
-    int num_chars = 0;
-    bool result = false;
-
-    // Write the message in the buffer.
-    // Dynamically adjust the size to hold the entire message.
-
-    while ((num_chars = _vsnwprintf_s(
-        log_buffer->GetBufferSetLength(buffer_size),
-        buffer_size,
-        _TRUNCATE,
-        fmt,
-        args)) == -1) {
-      // Truncate if the message is too big.
-      if (buffer_size >= kMaxLogMessageSize) {
-        num_chars = buffer_size;
-        break;
-      }
-
-      // Get a buffer that is big enough.
-      buffer_size *= 2;
-    }
-
-    log_buffer->ReleaseBuffer(num_chars);
-
-    FormatLinePrefix(show_time_, proc_name_, *prefix);
-
-    // Log the message.
-    OutputInfo info(cat, level, *prefix, *log_buffer);
-    OutputMessage(writer_mask, &info);
-  } __except(SehSendMinidump(GetExceptionCode(),
-                             GetExceptionInformation(),
-                             kMinsTo100ns)) {
-    OutputDebugStringA("Unexpected exception in: " __FUNCTION__ "\r\n");
-    OutputDebugString(fmt);
-    OutputDebugString(L"\n\r");
-  }
-}
-
-void Logging::LogMessageMaskedVA(DWORD writer_mask,
-                                 LogCategory cat,
-                                 LogLevel level,
-                                 const wchar_t* fmt,
-                                 va_list args) {
-  if (!fmt) {
-    return;
-  }
-
-  if (writer_mask == 0 && level > kMaxLevelToStoreInLogHistory) {
-    return;
-  }
-
-#pragma warning(push)
-// construction of local static object is not thread-safe
-#pragma warning(disable : 4640)
-  // TODO(omaha): make these class members.
-  static CString log_buffer;    // The buffer for formatted log messages.
-  static CString prefix;
-#pragma warning(pop)
-
-  int i = 0;
-  while (++i <= kNumLockRetries) {
-    if (lock_.Lock(0)) {
-      InternalLogMessageMaskedVA(writer_mask, cat, level, &log_buffer,
-                                 &prefix, fmt, args);
-      lock_.Unlock();
-      break;
-    }
-
-    Sleep(kLockRetryDelayMs);
-  }
-
-  if (i > kNumLockRetries) {
-    OutputDebugStringA("LOG_SYSTEM: Couldn't acquire lock - ");
-    OutputDebugString(fmt);
-    OutputDebugString(L"\n\r");
-  }
-}
-
-void Logging::OutputMessage(DWORD writer_mask, LogCategory cat, LogLevel level,
-                            const wchar_t* msg1, const wchar_t* msg2) {
-  OutputInfo info(cat, level, msg1, msg2);
-  OutputMessage(writer_mask, &info);
-}
-
-// Store log message in in-memory history buffer.
-void Logging::StoreInHistory(const OutputInfo* output_info) {
-  AppendToHistory(output_info->msg1);
-  AppendToHistory(output_info->msg2);
-  AppendToHistory(L"\r\n");
-}
-
-// Append string to in-memory history buffer.
-// history_buffer_next_idx points to the next index to write at,
-// thus it should always be in (0 - kHistoryBufferEndIdx).
-void Logging::AppendToHistory(const wchar_t* msg) {
-  int msg_len = wcslen(msg);
-  if (msg_len == 0) {
-    return;
-  }
-
-  if (msg_len >= kMaxHistoryBufferSize) {
-    // Write the first kMaxHistoryBufferSize chars.
-    memcpy(history_buffer, msg, kMaxHistoryBufferSize * sizeof(TCHAR));
-    history_buffer_next_idx = 0;
-    history_buffer_full = true;
-    return;
-  }
-
-  // Determine if the message fits into the portion of the buffer after
-  // history_buffer_next_idx.
-  if (msg_len + history_buffer_next_idx < kMaxHistoryBufferSize) {
-    memcpy(history_buffer + history_buffer_next_idx, msg,
-           msg_len * sizeof(TCHAR));
-    history_buffer_next_idx += msg_len;
-    return;
-  }
-
-  // Have to split the input message into the part that fits in
-  // history_buffer_next_idx to kMaxHistoryBufferSize and the remaining message.
-  int msg_first_part_len = kMaxHistoryBufferSize - history_buffer_next_idx;
-  int msg_second_part_len = msg_len - msg_first_part_len;
-  memcpy(history_buffer + history_buffer_next_idx,
-         msg,
-         msg_first_part_len * sizeof(TCHAR));
-
-  history_buffer_full = true;
-  history_buffer_next_idx = msg_second_part_len;
-  if (msg_second_part_len) {
-    memcpy(history_buffer,
-           msg + msg_first_part_len,
-           msg_second_part_len * sizeof(TCHAR));
-  }
-}
-
-// Retrieve in-memory history buffer.
-CString Logging::GetHistory() {
-  CString history;
-
-  if (history_buffer_full) {
-    history.Append(history_buffer + history_buffer_next_idx,
-                   kMaxHistoryBufferSize - history_buffer_next_idx);
-  }
-  history.Append(history_buffer, history_buffer_next_idx);
-
-  // Reset the history buffer to the original state.
-  history_buffer_next_idx = 0;
-  history_buffer_full = false;
-  memset(history_buffer, 0, kMaxHistoryBufferSize * sizeof(TCHAR));
-
-  return history;
-}
-
-void Logging::OutputMessage(DWORD writer_mask,
-                            const OutputInfo* output_info) {
-  if (output_info->level <= kMaxLevelToStoreInLogHistory &&
-      IsCategoryEnabledForBuffering(output_info->category)) {
-    StoreInHistory(output_info);
-  }
-
-  for (int i = 0; i < num_writers_; ++i) {
-    if (writer_mask & 1) {
-      __try {
-        if (logging_enabled_ || writers_[i]->WantsToLogRegardless()) {
-          writers_[i]->OutputMessage(output_info);
-        }
-      }
-      __except(SehNoMinidump(GetExceptionCode(),
-                             GetExceptionInformation(),
-                             __FILE__,
-                             __LINE__,
-                             true)) {
-        // Just eat errors that happen from within the LogWriters.  This is
-        // important so that if such an error happens when OutputMessage is
-        // called from debugASSERT we don't go recursively into more
-        // error handling ...
-      }
-    }
-    writer_mask >>= 1;
-  }
-}
-
-bool Logging::InternalRegisterWriter(LogWriter* log_writer) {
-  if (num_writers_ >= max_writers) {
-    return false;
-  }
-  writers_[num_writers_++] = log_writer;
-  return true;
-}
-
-bool Logging::RegisterWriter(LogWriter* log_writer) {
-  if (!InternalRegisterWriter(log_writer)) {
-    return false;
-  }
-  if (log_writer->WantsToLogRegardless()) {
-    EnableLogging();
-  }
-  return true;
-}
-
-bool Logging::InternalUnregisterWriter(LogWriter* log_writer) {
-  bool result = false;
-  for (int i = 0; i < num_writers_; ++i) {
-    if (writers_[i] == log_writer) {
-      // Replace this entry with last entry in array, then truncate.
-      writers_[i] = writers_[--num_writers_];
-      result = true;
-      break;
-    }
-  }
-  return result;
-}
-
-bool Logging::UnregisterWriter(LogWriter* log_writer) {
-  if (!InternalUnregisterWriter(log_writer)) {
-    return false;
-  }
-  if (num_writers_ == 0) {
-    DisableLogging();
-  }
-  return true;
-}
-
-// The primary configuration file under %PROGRAMFILES%\Google\Update is
-// removed on uninstall. This is midly inconvenient during development
-// therefore a fallback location for the configuration file is desired.
-CString Logging::GetCurrentConfigurationFilePath() const {
-  if (!config_file_path_.IsEmpty() &&
-      File::Exists(config_file_path_)) {
-    return config_file_path_;
-  } else {
-    return L"";
-  }
-}
-
-CString Logging::GetConfigurationFilePath() const {
-  CString file_path;
-  CString system_drive = GetEnvironmentVariableAsString(_T("SystemDrive"));
-  if (!system_drive.IsEmpty()) {
-    file_path = system_drive;
-    file_path += L"\\";
-  }
-  return file_path + kFilePrefix L".ini";
-}
-
-LogWriter::LogWriter() {
-}
-
-LogWriter::~LogWriter() {
-}
-
-void LogWriter::Cleanup() {}
-
-bool LogWriter::WantsToLogRegardless() const { return false; }
-
-bool LogWriter::IsCatLevelEnabled(LogCategory, LogLevel) const {
-  return false;
-}
-
-void LogWriter::OutputMessage(const OutputInfo*) { }
-
-bool LogWriter::Register() {
-  Logging* logger = GetLogging();
-  if (logger) {
-    return logger->RegisterWriter(this);
-  } else {
-    return false;
-  }
-}
-
-bool LogWriter::Unregister() {
-  Logging* logger = GetLogging();
-  if (logger) {
-    return logger->RegisterWriter(this);
-  } else {
-    return false;
-  }
-}
-
-// FileLogWriter
-
-FileLogWriter* FileLogWriter::Create(const wchar_t* file_name, bool append) {
-  return new FileLogWriter(file_name, append);
-}
-
-FileLogWriter::FileLogWriter(const wchar_t* file_name, bool append)
-    : initialized_(false),
-      valid_(false),
-      file_name_(file_name),
-      log_file_mutex_(NULL),
-      log_file_(NULL),
-      append_(append),
-      max_file_size_(kDefaultMaxLogFileSize),
-      log_file_wide_(kDefaultLogFileWide) {
-  Logging* logger = GetLogging();
-  if (logger) {
-    CString config_file_path = logger->GetCurrentConfigurationFilePath();
-    if (!config_file_path.IsEmpty()) {
-        max_file_size_ = ::GetPrivateProfileInt(
-            kConfigSectionLoggingSettings,
-            kConfigAttrMaxLogFileSize,
-            kDefaultMaxLogFileSize,
-            config_file_path);
-        log_file_wide_ = ::GetPrivateProfileInt(
-            kConfigSectionLoggingSettings,
-            kConfigAttrLogFileWide,
-            kDefaultLogFileWide,
-            config_file_path) == 0 ? false : true;
-    } else {
-      max_file_size_ = kDefaultMaxLogFileSize;
-      log_file_wide_ = kDefaultLogFileWide;
-    }
-    proc_name_ = logger->proc_name();
-  }
-}
-
-FileLogWriter::~FileLogWriter() {
-  // TODO(omaha): Figure out a way to pass the proc_name - and possibly
-  // the show_time var - into here.
-  Logging* logger = GetLogging();
-  if (logger && logger->IsLoggingAlreadyEnabled()) {
-    // OutputInfo info(LEVEL_WARNING, NULL, kEndOfLogMessage);
-    // OutputMessage(&info);
-  }
-  Cleanup();
-}
-
-void FileLogWriter::Initialize() {
-  if (initialized_) {
-    return;
-  }
-
-  initialized_ = true;
-
-  bool already_created = CreateLoggingMutex();
-  if (!log_file_mutex_) {
-    return;
-  }
-
-  if (already_created) {
-    append_ = true;
-  }
-
-  if (!GetMutex()) {
-    ::OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: "
-                                L"Could not acquire logging mutex %s\n",
-                                proc_name_,
-                                log_file_mutex_name_));
-    return;
-  }
-
-  CreateLoggingFile();
-  if (!log_file_) {
-    ::OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: "
-                                L"Could not create logging file %s\n",
-                                proc_name_,
-                                file_name_));
-    valid_ = false;
-  }
-
-  valid_ = true;
-  ReleaseMutex();
-}
-
-void FileLogWriter::Cleanup() {
-  if (log_file_) {
-    ::CloseHandle(log_file_);
-  }
-  if (log_file_mutex_) {
-    ::ReleaseMutex(log_file_mutex_);
-    ::CloseHandle(log_file_mutex_);
-  }
-}
-
-bool FileLogWriter::CreateLoggingMutex() {
-  log_file_mutex_name_ = StripBackslashes(kLoggingMutexName L"_" + file_name_);
-  // TODO(omaha): I don't see where this class is used, but I guess the
-  // caller is always in the same context. We should use the default security
-  // here.  If the caller can be in different contexts (System Service, Usermode
-  // applications, etc), then we should revisit this code to give access only
-  // to those who really need it.  What if a malicious piece a code decide
-  // to get the mutex and lock it? If it happens, everytime you try to log
-  // something, the thread would hang for 500ms and then fail to log the
-  // message.
-  CSecurityAttributes sa;
-  GetEveryoneDaclSecurityAttributes(&sa, GENERIC_ALL);
-  log_file_mutex_ = CreateMutexWithSyncAccess(log_file_mutex_name_, &sa);
-  if (log_file_mutex_) {
-    return ERROR_ALREADY_EXISTS == ::GetLastError();
-  }
-  return false;
-}
-
-bool FileLogWriter::CreateLoggingFile() {
-  uint32 file_size(0);
-  File::GetFileSizeUnopen(file_name_, &file_size);
-  if (file_size > max_file_size_) {
-    ArchiveLoggingFile();
-  }
-  log_file_ = ::CreateFile(file_name_,
-                           GENERIC_WRITE,
-                           FILE_SHARE_WRITE | FILE_SHARE_READ,
-                           NULL,
-                           append_ ? OPEN_ALWAYS : CREATE_ALWAYS,
-                           FILE_ATTRIBUTE_NORMAL,
-                           NULL);
-  if (log_file_ == INVALID_HANDLE_VALUE) {
-    // The code in this file is written with the assumption that log_file_ is
-    // NULL on creation errors. The easy fix is to set it to NULL here. The
-    // long term fix should be implementing it in terms of a smart handle.
-    log_file_ = NULL;
-    return false;
-  }
-
-  // Allow users to read, write, and delete the log file.
-  ACCESS_MASK mask = GENERIC_READ | GENERIC_WRITE | DELETE;
-  CDacl dacl;
-  if (dacl.AddAllowedAce(ATL::Sids::Users(), mask)) {
-    AtlSetDacl(file_name_, SE_FILE_OBJECT, dacl);
-  }
-
-  // Insert a BOM in the newly created file.
-  if (GetLastError() != ERROR_ALREADY_EXISTS && log_file_wide_) {
-    DWORD num = 0;
-    ::WriteFile(log_file_, &kUnicodeBom, sizeof(kUnicodeBom), &num, NULL);
-  }
-  return true;
-}
-
-bool FileLogWriter::TruncateLoggingFile() {
-  DWORD share_mode = FILE_SHARE_WRITE;
-  HANDLE log_file = ::CreateFile(file_name_,
-                                 GENERIC_WRITE,
-                                 FILE_SHARE_WRITE | FILE_SHARE_READ,
-                                 NULL,
-                                 TRUNCATE_EXISTING,
-                                 FILE_ATTRIBUTE_NORMAL,
-                                 NULL);
-  if (log_file_ == INVALID_HANDLE_VALUE) {
-    return false;
-  }
-
-  // Insert a BOM in the newly created file.
-  if (log_file_wide_) {
-    DWORD num = 0;
-    ::WriteFile(log_file, &kUnicodeBom, sizeof(kUnicodeBom), &num, NULL);
-  }
-  ::CloseHandle(log_file);
-  return true;
-}
-
-bool FileLogWriter::ArchiveLoggingFile() {
-  ::OutputDebugString(L"LOG_SYSTEM: trying to move log file to backup\n");
-  CString backup_file_name = file_name_ + L".bak";
-  HRESULT hr = File::Move(file_name_, backup_file_name, true);
-  if (FAILED(hr)) {
-    ::OutputDebugString(L"LOG_SYSTEM: failed to move log file to backup\n");
-
-    // Trying to move the log file when loggers have it open returns
-    // ERROR_SHARING_VIOLATION. Each call to MoveFileAfterReboot inserts the
-    // file into PendingFileRenames list. Moving files at reboot requires the
-    // user to be either the LocalSystem account or in the Administrators
-    // group.
-    if (!IsArchivePending()) {
-      File::MoveAfterReboot(file_name_, backup_file_name);
-    }
-    return false;
-  }
-  return true;
-}
-
-bool FileLogWriter::IsArchivePending() {
-  // We look at the PendingFileRenameOperations to see if our log file is
-  // pending a rename. The list is a REG_MULTI_SZ, which is a sequence of
-  // null-terminated strings, terminated by an empty string "\0".
-  // The strings have the structure:
-  // \??\file1\0!\??\file2\0\file3\0\0...\0\0. where file1 is to be renamed to
-  // file2 and file3 is to be deleted.
-  // It is valid for the PFR list to include an empty string in the middle
-  // of the sequence.
-  const wchar_t sub_key_name[] = L"SYSTEM\\CurrentControlSet\\Control\\"
-                                 L"Session Manager";
-  HKEY key = NULL;
-  int res = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
-                            sub_key_name,
-                            0,
-                            KEY_READ,
-                            &key);
-  if (res != ERROR_SUCCESS) {
-    return false;
-  }
-  DWORD bytes = 0;
-  DWORD type = REG_MULTI_SZ;
-  res = ::RegQueryValueEx(key,
-                          L"PendingFileRenameOperations",
-                          0,
-                          &type,
-                          NULL,
-                          &bytes);
-  if (!(res == ERROR_SUCCESS && type == REG_MULTI_SZ)) {
-    return false;
-  }
-  scoped_array<byte> buf(new byte[bytes]);
-  memset(buf.get(), 0, bytes);
-  res = ::RegQueryValueEx(key,
-                          L"PendingFileRenameOperations",
-                          0,
-                          NULL,
-                          buf.get(),
-                          &bytes);
-  if (res != ERROR_SUCCESS) {
-    return false;
-  }
-  const wchar_t* multi_str = reinterpret_cast<const wchar_t*>(buf.get());
-  size_t count = bytes / sizeof(*multi_str);
-  const size_t kMaxRegistryValueLen = 1024 * 1024;  // 1MB
-  if (!(count >= 2 &&
-        count < kMaxRegistryValueLen &&
-        multi_str[count - 2] == L'\0' &&
-        multi_str[count - 1] == L'\0')) {
-    return false;
-  }
-  // The file names in the PFR list are prefixed by \??\.
-  CString file_name = L"\\??\\" + file_name_;
-  return FindFirstInMultiString(multi_str, count, file_name) != -1;
-}
-
-int FileLogWriter::FindFirstInMultiString(const wchar_t* multi_str,
-                                          size_t count,
-                                          const wchar_t* str) {
-  const wchar_t* p = multi_str;
-  size_t i = 0;
-  while (i < count) {
-    p =  multi_str + i;
-    if (lstrcmp(p, str) == 0) {
-      return i;
-    } else {
-      size_t len = lstrlen(p);
-      i += len + 1;
-    }
-  }
-  return -1;
-}
-
-void FileLogWriter::OutputMessage(const OutputInfo* output_info) {
-  if (!initialized_) {
-    Initialize();
-  }
-
-  if (!valid_) {
-    return;
-  }
-
-  // Acquire the mutex.
-  if (!GetMutex()) {
-    return;
-  }
-
-  // Move to end of file.
-  DWORD pos = ::SetFilePointer(log_file_, 0, NULL, FILE_END);
-  int64 stop_gap_file_size = kStopGapLogFileSizeFactor *
-                             static_cast<int64>(max_file_size_);
-  if (pos >= stop_gap_file_size) {
-    if (!TruncateLoggingFile()) {
-      // Logging stops until the log can be archived over since we do not
-      // want to overfill the disk.
-      return;
-    }
-  }
-  pos = ::SetFilePointer(log_file_, 0, NULL, FILE_END);
-
-  // Write the date, followed by a CRLF
-  DWORD written_size = 0;
-  if (output_info->msg1) {
-    if (log_file_wide_) {
-      ::WriteFile(log_file_, output_info->msg1,
-                  lstrlen(output_info->msg1) * sizeof(wchar_t), &written_size,
-                  NULL);
-    } else {
-      CStringA msg(WideToAnsiDirect(output_info->msg1));
-      ::WriteFile(log_file_, msg.GetString(), msg.GetLength(), &written_size,
-                  NULL);
-    }
-  }
-
-  if (output_info->msg2) {
-    if (log_file_wide_) {
-      ::WriteFile(log_file_, output_info->msg2,
-                  lstrlen(output_info->msg2) * sizeof(wchar_t), &written_size,
-                  NULL);
-    } else {
-      CStringA msg(WideToAnsiDirect(output_info->msg2));
-      ::WriteFile(log_file_, msg.GetString(), msg.GetLength(), &written_size,
-                  NULL);
-    }
-  }
-
-  if (log_file_wide_) {
-    ::WriteFile(log_file_, L"\r\n", 2 * sizeof(wchar_t), &written_size, NULL);
-  } else {
-    ::WriteFile(log_file_, "\r\n", 2, &written_size, NULL);
-  }
-
-  ReleaseMutex();
-}
-
-bool FileLogWriter::GetMutex() {
-  if (!log_file_mutex_) {
-    return false;
-  }
-
-  DWORD res = ::WaitForSingleObject(log_file_mutex_, kMaxMutexWaitTimeMs);
-  if (res != WAIT_OBJECT_0 && res != WAIT_ABANDONED) {
-    ::OutputDebugString(SPRINTF(L"LOG_SYSTEM: [%s]: "
-                                L"Could not acquire logging mutex %s\n",
-                                proc_name_, log_file_mutex_name_));
-    valid_ = false;
-    return false;
-  }
-
-  return true;
-}
-
-void FileLogWriter::ReleaseMutex() {
-  if (log_file_mutex_) {
-    ::ReleaseMutex(log_file_mutex_);
-  }
-}
-
-// OutputDebugStringLogWriter.
-OutputDebugStringLogWriter* OutputDebugStringLogWriter::Create() {
-  return new OutputDebugStringLogWriter();
-}
-
-OutputDebugStringLogWriter::OutputDebugStringLogWriter() {}
-
-OutputDebugStringLogWriter::~OutputDebugStringLogWriter() {
-  Logging* logger = GetLogging();
-  if (logger && logger->IsLoggingAlreadyEnabled()) {
-    // OutputInfo info(LEVEL_WARNING, NULL, kEndOfLogMessage);
-    // OutputMessage(&info);
-  }
-  Cleanup();
-}
-
-void OutputDebugStringLogWriter::OutputMessage(const OutputInfo* output_info) {
-  // Combine everything into one string so that messages coming from
-  // multiple threads don't get interleaved.
-  ::OutputDebugString(SPRINTF(L"%s%s\n", output_info->msg1, output_info->msg2));
-}
-
-// OverrideConfigLogWriter.
-OverrideConfigLogWriter* OverrideConfigLogWriter::Create(LogCategory category,
-    LogLevel level, LogWriter* log_writer, bool force_logging_enabled) {
-  return new OverrideConfigLogWriter(category,
-                                     level,
-                                     log_writer,
-                                     force_logging_enabled);
-}
-
-OverrideConfigLogWriter::OverrideConfigLogWriter(LogCategory category,
-                                                 LogLevel level,
-                                                 LogWriter* log_writer,
-                                                 bool force_logging_enabled)
-    : category_(category),
-      level_(level),
-      log_writer_(log_writer),
-      force_logging_enabled_(force_logging_enabled) {}
-
-void OverrideConfigLogWriter::Cleanup() {
-  if (log_writer_) {
-    delete log_writer_;
-  }
-}
-
-bool OverrideConfigLogWriter::WantsToLogRegardless() const {
-  return force_logging_enabled_;
-}
-
-bool OverrideConfigLogWriter::IsCatLevelEnabled(LogCategory category,
-                                                LogLevel level) const {
-  if (category != category_) {
-    return false;
-  }
-  if (level > level_) {
-    return false;
-  }
-  return true;
-}
-
-void OverrideConfigLogWriter::OutputMessage(const OutputInfo* output_info) {
-  if (log_writer_) {
-    log_writer_->OutputMessage(output_info);
-  }
-  return;
-}
-
-}  // namespace omaha
-
-#endif  // LOGGING
-
diff --git a/common/logging.h b/common/logging.h
deleted file mode 100644
index 800f36f..0000000
--- a/common/logging.h
+++ /dev/null
@@ -1,499 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// logging.h
-//
-// Tracing and logging system.
-// Allows filtering of the log messages based on logging categories and levels.
-
-#ifndef OMAHA_COMMON_LOGGING_H__
-#define OMAHA_COMMON_LOGGING_H__
-
-#include "omaha/common/commontypes.h"
-#include "omaha/common/synchronized.h"
-
-#ifdef LOGGING
-
-// Logging levels.
-enum LogLevel {
-  LEVEL_FATALERROR = -3,      // crashing fatal error
-  LEVEL_ERROR      = -2,      // errors - recoverable but shouldn't happen
-  LE               = -2,
-  LEVEL_WARNING    = -1,      // warnings
-  LW               = -1,
-  L1               =  1,      // for aprox. 10 logs per run
-  L2,                         // for aprox. 100 logs per run
-  L3,                         // for aprox. 1,000 logs per run
-  L4,                         // for aprox. 10,000 logs per run
-  L5,                         // for aprox. 100,000 logs per run
-  L6,                         // for > 1,000,000 logs per run
-
-  // add above
-  LEVEL_ALL                   // all errors
-};
-
-#endif
-
-namespace omaha {
-
-#define kDefaultLogFileName             kFilePrefix L".log"
-#define kDefaultLogFileWide             1
-#define kDefaultLogToFile               1
-#define kDefaultShowTime                1
-#define kDefaultAppendToFile            1
-
-#ifdef _DEBUG
-#define kDefaultMaxLogFileSize          0xFFFFFFFF  // 4GB
-#define kDefaultLogToOutputDebug        1
-#define kDefaultLogLevel                L3
-#define kDefaultLoggingEnabled          1
-#else
-#define kDefaultMaxLogFileSize          10000000    // 10MB
-#define kDefaultLogToOutputDebug        0
-#define kDefaultLogLevel                L1
-#define kDefaultLoggingEnabled          0
-#endif
-
-// Truncates the log file when the size of the log file is this many
-// times over the MaxLogFileSize to prevent disk overfill.
-#define kStopGapLogFileSizeFactor       10
-
-// config file sections
-#define kConfigSectionLoggingLevel      L"LoggingLevel"
-#define kConfigSectionLoggingSettings   L"LoggingSettings"
-
-// config file attributes
-#define kConfigAttrEnableLogging        L"EnableLogging"
-#define kConfigAttrShowTime             L"ShowTime"
-#define kConfigAttrLogToFile            L"LogToFile"
-#define kConfigAttrLogFilePath          L"LogFilePath"
-#define kConfigAttrLogFileWide          L"LogFileWide"
-#define kConfigAttrLogToOutputDebug     L"LogToOutputDebug"
-#define kConfigAttrAppendToFile         L"AppendToFile"
-#define kConfigAttrMaxLogFileSize       L"MaxLogFileSize"
-
-#define kLoggingMutexName               kLockPrefix L"logging_mutex"
-#define kMaxMutexWaitTimeMs             500
-
-// Does not allow messages bigger than 1 MB.
-#define kMaxLogMessageSize              (1024 * 1024)
-
-#define kLogSettingsCheckInterval       (5 * kSecsTo100ns)
-
-#define kStartOfLogMessage \
-    L"********************* NEW LOG *********************"
-#define kEndOfLogMessage   \
-    L"********************* END LOG *********************"
-
-// TODO(omaha): Allow these defaults to be overriden in the config file.
-#define kMaxLevelToStoreInLogHistory L2
-#define kMaxHistoryBufferSize 1024
-
-#ifdef LOGGING
-
-#define LC_LOG(cat, level, msg) \
-  do {                                                     \
-    omaha::Logging* logger = omaha::GetLogging();          \
-    if (logger) {                                          \
-      omaha::LoggingHelper(logger, cat, level,             \
-        logger->IsCatLevelEnabled(cat, level)) msg;        \
-    }                                                      \
-  } while (0)
-
-#define LC_LOG_OPT(cat, level, msg)   LC_LOG(cat, level, msg)
-
-#else
-#define LC_LOG(cat, level, msg)   ((void)0)
-#endif
-
-#ifdef _DEBUG
-#define LC_LOG_DEBUG(cat, level, msg) LC_LOG(cat, level, msg)
-#else
-#define LC_LOG_DEBUG(cat, level, msg) ((void)0)
-#endif
-
-// Shortcuts for different logging categories - no need to specify the category.
-#define CORE_LOG(x, y)         LC_LOG_DEBUG(omaha::LC_CORE, x, y)
-#define NET_LOG(x, y)          LC_LOG_DEBUG(omaha::LC_NET, x, y)
-#define PLUGIN_LOG(x, y)       LC_LOG_DEBUG(omaha::LC_PLUGIN, x, y)
-#define SERVICE_LOG(x, y)      LC_LOG_DEBUG(omaha::LC_SERVICE, x, y)
-#define SETUP_LOG(x, y)        LC_LOG_DEBUG(omaha::LC_SETUP, x, y)
-#define SHELL_LOG(x, y)        LC_LOG_DEBUG(omaha::LC_SHELL, x, y)
-#define UTIL_LOG(x, y)         LC_LOG_DEBUG(omaha::LC_UTIL, x, y)
-
-#define OPT_LOG(x, y)          LC_LOG_OPT(omaha::LC_OPT, x, y)
-#define REPORT_LOG(x, y)       LC_LOG_OPT(omaha::LC_REPORT, x, y)
-
-#ifdef LOGGING
-
-// Logging components.
-// Maximum 32 categories unless mask is increased to 64 bits.
-enum LogCategory {
-  LC_LOGGING = 0,
-
-  // ADD BELOW - AND REMEMBER:
-  //   - add a line to the LogCategoryNames table in logging.cpp!!!
-  //   - add to C:\GoogleUpdate.ini
-
-  LC_UTIL,
-  LC_SETUP,
-  LC_SHELL,
-  LC_CORE,
-  LC_JS,
-  LC_PLUGIN,
-  LC_SERVICE,
-  LC_OPT,
-  LC_NET,
-  LC_REPORT,
-
-  // ADD ABOVE
-
-  LC_MAX_CAT
-};
-
-#define kCatEnabledField      L"Enabled"
-#define kCatLevelField        L"Level"
-
-struct CategoryInfo {
-  bool enabled;
-  LogLevel log_level;
-};
-
-struct OutputInfo;
-
-// The LogWriter - can decide whether to process message or not, then
-// will process it.  Actually, the message is processed if either a) the
-// individual LogWriter wants to process it or b) it is marked as processable
-// by settings in config ini.
-//
-// Included LogWriters:
-//   OutputDebugStringLogWriter - Logs to OutputDebugString() API
-//   FileLogWriter - Logs to a file
-//   OverrideConfigLogWriter - Overrides the level settings of a
-//     particular category, uses another writer to actually do the writing.
-//     Used, e.g., in installer to force SETUP_LOG messages to go to a file
-//     tr_setup_log.info even if the there is no trconfig.ini file.
-//
-// Not included LogWriters:
-//   StdLogWriter - Logs to stdout or stderr
-//   SubmitToGoogleLogWriter - When done logging submits the log file to
-//     Google's status-receiving server
-class LogWriter {
- protected:
-  LogWriter();
-  virtual void Cleanup();
- public:
-  virtual ~LogWriter();
-
-  // Returns true if this Logging object wants to log even if the global
-  // "enable logging" flag is off.  Useful for always creating a log, e.g., an
-  // install log, even without a GoogleUpdate.ini.
-  virtual bool WantsToLogRegardless() const;
-
-  // Returns true if this Logging object wants to handle the message,
-  // regardless of other settings.
-  virtual bool IsCatLevelEnabled(LogCategory category, LogLevel level) const;
-
-  virtual void OutputMessage(const OutputInfo* output_info);
-
-  // Registers and unregisters this LogWriter with the Logging system.  When
-  // registered, the Logging class assumes ownership.
-  bool Register();
-  bool Unregister();
-
- private:
-  DISALLOW_EVIL_CONSTRUCTORS(LogWriter);
-};
-
-// A LogWriter that writes to a named file.
-class FileLogWriter : public LogWriter {
- protected:
-  FileLogWriter(const wchar_t* file_name, bool append);
-  ~FileLogWriter();
-  virtual void Cleanup();
-
- public:
-  static FileLogWriter* Create(const wchar_t* file_name, bool append);
-  virtual void OutputMessage(const OutputInfo* output_info);
-
- private:
-  void Initialize();
-  bool CreateLoggingMutex();
-  bool CreateLoggingFile();
-  bool ArchiveLoggingFile();
-  bool TruncateLoggingFile();
-  bool GetMutex();
-  void ReleaseMutex();
-
-  // Returns true if archiving of the log file is pending a computer restart.
-  bool IsArchivePending();
-
-  // Returns the first position of str inside of a MULTI_SZ of count characters
-  // including the terminating zeros.
-  static int FindFirstInMultiString(const wchar_t* multi_str,
-                                    size_t count,
-                                    const wchar_t* str);
-
-  uint32 max_file_size_;
-  bool initialized_;
-  bool valid_;
-  bool append_;
-  bool log_file_wide_;
-  CString log_file_mutex_name_;
-  HANDLE log_file_mutex_;
-  CString file_name_;
-  HANDLE log_file_;
-  CString proc_name_;
-
-  friend class FileLogWriterTest;
-
-  DISALLOW_EVIL_CONSTRUCTORS(FileLogWriter);
-};
-
-// A LogWriter that uses OutputDebugString() to write messages.
-class OutputDebugStringLogWriter : public LogWriter {
- protected:
-  OutputDebugStringLogWriter();
-  ~OutputDebugStringLogWriter();
- public:
-  static OutputDebugStringLogWriter* Create();
-  virtual void OutputMessage(const OutputInfo* info);
- private:
-  DISALLOW_EVIL_CONSTRUCTORS(OutputDebugStringLogWriter);
-};
-
-// A LogWriter that overrides the settings in trconfig.ini and sends messages
-// to another LogWriter.  Takes ownership of the other LogWriter.
-class OverrideConfigLogWriter : public LogWriter {
- protected:
-  OverrideConfigLogWriter(LogCategory category, LogLevel level,
-                          LogWriter* log_writer, bool force_logging_enabled);
-  virtual void Cleanup();
- public:
-  static OverrideConfigLogWriter* Create(LogCategory category, LogLevel level,
-    LogWriter* log_writer, bool force_logging_enabled);
-  virtual bool WantsToLogRegardless() const;
-  virtual bool IsCatLevelEnabled(LogCategory category, LogLevel level) const;
-  virtual void OutputMessage(const OutputInfo* output_info);
- private:
-  LogCategory category_;
-  LogLevel level_;
-  LogWriter* log_writer_;
-  bool force_logging_enabled_;
-  DISALLOW_EVIL_CONSTRUCTORS(OverrideConfigLogWriter);
-};
-
-// The Logging class - Singleton class
-// Fine-grain logging based on categories and levels.
-// Can log to a file, stdout or debugger.
-class Logging {
- public:
-  // constructor
-  Logging();
-
-  // destructor
-  ~Logging();
-
-  // Enables/disables the logging mechanism.  Allows turning logging on/off
-  // in mid-run.  Returns true for success (not for 'logging enabled').
-  void EnableLogging();
-  void DisableLogging();
-
-  // Checks if logging is enabled - and updates logging settings from the
-  // configuration file every kLogSettingsCheckInterval seconds
-  bool IsLoggingEnabled();
-
-  // Checks if logging is already enabled. It does not try to enable it.
-  bool IsLoggingAlreadyEnabled() const;
-
-  // Overrides the config file settings for showing the time stamps.
-  void ForceShowTimestamp(bool force_show_time);
-
-  // Checks if logging is enabled for a given category and level.
-  DWORD IsCatLevelEnabled(LogCategory category, LogLevel level);
-  LogLevel GetCatLevel(LogCategory category) const;
-
-  // Logs a message.
-  void LogMessage(LogCategory cat, LogLevel level, const wchar_t* fmt, ...);
-  void LogMessageVA(LogCategory cat, LogLevel level, const wchar_t* fmt,
-                    va_list args);
-
-  // Retrieves the default location of the log directory.
-  CString GetDefaultLogDirectory() const;
-
-  // Computes and returns the complete path of the log file.
-  CString GetLogFilePath() const;
-
-  // Retrieves in-memory history buffer.
-  CString GetHistory();
-
-  // Returns the file path of the current GoogleUpdate.ini.
-  CString GetCurrentConfigurationFilePath() const;
-
-  const CString& proc_name() const { return proc_name_; }
-
-  bool IsCategoryEnabledForBuffering(LogCategory cat);
- private:
-  bool InternalInitialize();
-  void InternalLogMessageMaskedVA(DWORD writer_mask,
-                                  LogCategory cat,
-                                  LogLevel level,
-                                  CString* log_buffer,
-                                  CString* prefix,
-                                  const wchar_t* fmt,
-                                  va_list args);
-
-  friend class LoggingHelper;
-  void LogMessageMaskedVA(DWORD writer_mask, LogCategory cat, LogLevel level,
-                          const wchar_t* fmt, va_list args);
-
-  // Stores log message in in-memory history buffer.
-  void StoreInHistory(const OutputInfo* output_info);
-
-  // Appends string to in-memory history buffer.
-  void AppendToHistory(const wchar_t* msg);
-
-  // Initializes the logging engine. Harmless to call multiple times.
-  bool InitializeLogging();
-
-  // Configures/unconfigures the log writers for the current settings.  That
-  // is, given the current settings from GoogleUpdate.ini, either initializes
-  // and registers the file-out and debug-out logwriters, or unregisters them.
-  bool ConfigureLogging();
-  void UnconfigureLogging();
-
-  void UpdateCatAndLevel(const wchar_t* cat_name, LogCategory cat);
-  void ReadLoggingSettings();
-
-  // Returns the primary file path of the GoogleUpdate.ini.
-  CString GetConfigurationFilePath() const;
-
-  // Returns the alternate file path of the GoogleUpdate.ini.
-  CString GetAltConfigurationFilePath() const;
-
- public:
-
-  // Passes the messages along to other OutputMessage()
-  void OutputMessage(DWORD writer_mask, LogCategory cat, LogLevel level,
-                     const wchar_t* msg1, const wchar_t* msg2);
-
-  // Broadcasts the message to each LogWriter.
-  // It should be private but the function we want to be able to use this,
-  // debugASSERT is extern "C" and thus can't be declared a friend of
-  // Logging.
-  void OutputMessage(DWORD writer_mask, const OutputInfo* output_info);
-
- private:
-
-  CategoryInfo category_list_[LC_MAX_CAT];
-
-  // Checks if logging is initialized.
-  bool logging_initialized_;
-
-  // Is logging in the process of initializing?
-  bool is_initializing_;
-
-  // The logging process name including the calling module.
-  CString proc_name_;
-
-  // Serializes changing logging init/uninit/enable/disable status.
-  LLock lock_;
-
-  // Bunch of settings from the config .ini file.
-  bool logging_enabled_;     // Checks if logging is enabled.
-  bool force_show_time_;
-  bool show_time_;
-  bool log_to_file_;
-  CString log_file_name_;
-  bool log_to_debug_out_;
-  bool append_to_file_;
-
-  // Signals the logging system is shutting down.
-  bool logging_shutdown_;
-
-  // Checkpoint time for dynamic category updates.
-  time64 g_last_category_check_time;
-
-  // The file path of the optional ini file which defines the logging
-  // configuration.
-  CString config_file_path_;
-
- private:
-  bool InternalRegisterWriter(LogWriter* log_writer);
-  bool InternalUnregisterWriter(LogWriter* log_writer);
-
- public:
-  bool RegisterWriter(LogWriter* log_writer);
-  bool UnregisterWriter(LogWriter* log_writer);
-  enum { all_writers_mask = -1 };
-
- private:
-  enum { max_writers = 15 };
-  int num_writers_;
-  LogWriter* writers_[max_writers];
-
-  LogWriter* file_log_writer_;
-  LogWriter* debug_out_writer_;
-
-  friend class HistoryTest;
-
-  DISALLOW_EVIL_CONSTRUCTORS(Logging);
-};
-
-// In order to make the logging macro LC_LOG work out we need to pass a
-// parameter (the mask of loggers to write to) (*) to the actual logging
-// method. However, the last parameter to the macro LC_LOG has its own
-// parenthesis - it encloses multiple expressions (a format string and
-// arguments). So this function object is used as an intermediary in order to
-// hold the writer mask.
-//
-// (*) The mask needs to be transferred separately because we want to keep the
-// LC_LOG structure of asking if the message is going to be logged before
-// evaluating the arguments, and we can't store it in the singleton Logging
-// object - wouldn't be thread-safe.
-
-class LoggingHelper {
- public:
-  LoggingHelper(Logging* logger, LogCategory cat,
-                LogLevel level, DWORD writer_mask)
-      : logger_(logger),
-        category_(cat),
-        level_(level),
-        writer_mask_(writer_mask) {}
-
-  void operator()(const wchar_t* fmt, ...) {
-    va_list args;
-    va_start(args, fmt);
-    logger_->LogMessageMaskedVA(writer_mask_, category_, level_, fmt, args);
-    va_end(args);
-  }
-
- private:
-  Logging* logger_;
-  DWORD writer_mask_;
-  LogLevel level_;
-  LogCategory category_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(LoggingHelper);
-};
-
-// Getter for the Logging singleton class.
-Logging* GetLogging();
-
-#endif  // LOGGING
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_LOGGING_H__
diff --git a/common/logging/build.scons b/common/logging/build.scons
deleted file mode 100644
index a74fd8b..0000000
--- a/common/logging/build.scons
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/python2.4
-#
-# Copyright 2009 Google Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# ========================================================================
-
-
-Import('env')
-
-#
-# Build the googleclient port of the logging module.
-#
-logging_env = env.Clone()
-
-target_name = 'logging.lib'
-
-logging_inputs = [
-    'logging.cc',
-    ]
-if env.Bit('use_precompiled_headers'):
-  logging_inputs += logging_env.EnablePrecompile(target_name)
-
-logging_env.ComponentLibrary(
-    lib_name=target_name,
-    source=logging_inputs,
-)
-
diff --git a/common/logging/logging.cc b/common/logging/logging.cc
deleted file mode 100644
index 2101baa..0000000
--- a/common/logging/logging.cc
+++ /dev/null
@@ -1,348 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// This is code that defines the backend for the new LogMessage definition
-// and the functions that the client application can call to control logging.
-
-#include <ctime>
-#include <iomanip>
-#include <cstring>
-#include <windows.h>
-#include <tchar.h>
-#include <algorithm>
-#include "omaha/common/logging/logging.h"
-
-namespace logging {
-
-const char* const log_severity_names[LOG_NUM_SEVERITIES] = {
-  "INFO", "WARNING", "ERROR", "FATAL" };
-
-int min_log_level = 0;
-LogLockingState lock_log_file = LOCK_LOG_FILE;
-LoggingDestination logging_destination = LOG_ONLY_TO_FILE;
-
-// which log file to use? This is initialized by InitLogging or
-// will be lazily initialized to the default value when it is
-// first needed.
-TCHAR log_file_name[MAX_PATH] = { 0 };
-
-// this file is lazily opened and the handle may be NULL
-HANDLE log_file = NULL;
-
-// what should be prepended to each message?
-bool log_process_id = false;
-bool log_thread_id = false;
-bool log_timestamp = true;
-bool log_tickcount = false;
-
-// An assert handler override specified by the client to be called instead of
-// the debug message dialog.
-LogAssertHandlerFunction log_assert_handler = NULL;
-
-// The critical section is used if log file locking is false. It helps us
-// avoid problems with multiple threads writing to the log file at the same
-// time.
-bool initialized_critical_section = false;
-CRITICAL_SECTION log_critical_section;
-
-// When we don't use a critical section, we are using a global mutex. We
-// need to do this because LockFileEx is not thread safe
-HANDLE log_mutex = NULL;
-
-void InitLogMutex() {
-  if (!log_mutex) {
-    // \ is not a legal character in mutex names so we replace \ with /
-    std::wstring safe_name(log_file_name);
-    std::replace(safe_name.begin(), safe_name.end(), '\\', '/');
-    std::wstring t(L"Global\\");
-    t.append(safe_name);
-    log_mutex = ::CreateMutex(NULL, FALSE, t.c_str());
-  }
-}
-
-void InitLogging(const TCHAR* new_log_file, LoggingDestination logging_dest,
-                 LogLockingState lock_log, OldFileDeletionState delete_old) {
-  if (log_file) {
-    // calling InitLogging twice or after some log call has already opened the
-    // default log file will re-initialize to the new options
-    CloseHandle(log_file);
-    log_file = NULL;
-  }
-
-  lock_log_file = lock_log;
-  logging_destination = logging_dest;
-
-  // ignore file options if logging is only to system
-  if (logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG)
-    return;
-
-  _tcsncpy(log_file_name, new_log_file, MAX_PATH);
-  log_file_name[MAX_PATH - 1] = _T('\0');
-  if (delete_old == DELETE_OLD_LOG_FILE)
-    DeleteFile(log_file_name);
-
-  if (lock_log_file == LOCK_LOG_FILE) {
-    InitLogMutex();
-  } else if (!initialized_critical_section) {
-    // initialize the critical section
-    InitializeCriticalSection(&log_critical_section);
-    initialized_critical_section = true;
-  }
-}
-
-void SetMinLogLevel(int level) {
-  min_log_level = level;
-}
-
-void SetLogItems(bool enable_process_id, bool enable_thread_id,
-                 bool enable_timestamp, bool enable_tickcount) {
-  log_process_id = enable_process_id;
-  log_thread_id = enable_thread_id;
-  log_timestamp = enable_timestamp;
-  log_tickcount = enable_tickcount;
-}
-
-void SetLogAssertHandler(LogAssertHandlerFunction handler) {
-  log_assert_handler = handler;
-}
-
-// Called by logging functions to ensure that debug_file is initialized
-// and can be used for writing. Returns false if the file could not be
-// initialized. debug_file will be NULL in this case.
-bool VerifyLogFileHandle() {
-  if (log_file)
-    return true;
-
-  if (!log_file_name[0]) {
-    // nobody has called InitLogging to specify a debug log file, so here we
-    // initialize the log file name to the default
-    GetModuleFileName(NULL, log_file_name, MAX_PATH);
-    TCHAR* last_backslash = _tcsrchr(log_file_name, '\\');
-    if (last_backslash)
-      last_backslash[1] = 0; // name now ends with the backslash
-    _tcscat(log_file_name, _T("debug.log"));
-  }
-
-  log_file = CreateFile(log_file_name, GENERIC_WRITE,
-                        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
-                        OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
-  if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) {
-    log_file = NULL;
-    return false;
-  }
-  SetFilePointer(log_file, 0, 0, FILE_END);
-  return true;
-}
-
-// Displays a message box to the user with the error message in it. For
-// Windows programs, it's possible that the message loop is messed up on
-// a fatal error, and creating a MessageBox will cause that message loop
-// to be run. Instead, we try to spawn another process that displays its
-// command line. We look for "Debug Message.exe" in the same directory as
-// the application. If it exists, we use it, otherwise, we use a regular
-// message box.
-void DisplayDebugMessage(const std::string& str) {
-  if (str.empty())
-    return;
-
-  // look for the debug dialog program next to our application
-  wchar_t prog_name[MAX_PATH];
-  GetModuleFileNameW(NULL, prog_name, MAX_PATH);
-  wchar_t* backslash = wcsrchr(prog_name, '\\');
-  if (backslash)
-    backslash[1] = 0;
-  wcsncat(prog_name, L"DebugMessage.exe", MAX_PATH);
-  prog_name[MAX_PATH - 1] = L'\0';
-
-  // stupid CreateProcess requires a non-const command line and may modify it.
-  // We also want to use the wide string
-  int charcount = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
-  if (!charcount)
-    return;
-  scoped_array<wchar_t> cmdline(new wchar_t[charcount]);
-  if (!MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, cmdline.get(),
-      charcount))
-    return;
-
-  STARTUPINFO startup_info;
-  memset(&startup_info, 0, sizeof(startup_info));
-  startup_info.cb = sizeof(startup_info);
-
-  PROCESS_INFORMATION process_info;
-  if (CreateProcessW(prog_name, cmdline.get(), NULL, NULL, false, 0, NULL,
-                     NULL, &startup_info, &process_info)) {
-    WaitForSingleObject(process_info.hProcess, INFINITE);
-    CloseHandle(process_info.hThread);
-    CloseHandle(process_info.hProcess);
-  } else {
-    // debug process broken, let's just do a message box
-    MessageBoxW(NULL, cmdline.get(), L"Fatal error", MB_OK | MB_ICONHAND);
-  }
-}
-
-LogMessage::LogMessage(const char* file, int line, LogSeverity severity, int)
-    : severity_(severity) {
-  Init(file, line);
-}
-
-LogMessage::LogMessage(const char* file, int line, const CheckOpString& result)
-    : severity_(LOG_FATAL) {
-  Init(file, line);
-  stream_ << "Check failed: " << (*result.str_);
-}
-
-LogMessage::LogMessage(const char* file, int line)
-     : severity_(LOG_INFO) {
-  Init(file, line);
-}
-
-LogMessage::LogMessage(const char* file, int line, LogSeverity severity)
-    : severity_(severity) {
-  Init(file, line);
-}
-
-// writes the common header info to the stream
-void LogMessage::Init(const char* file, int line) {
-  // log only the filename
-  const char* last_slash = strrchr(file, '\\');
-  if (last_slash)
-    file = last_slash + 1;
-
-  // TODO(omaha): It might be nice if the columns were fixed width.
-
-  stream_ <<  '[';
-  if (log_process_id)
-    stream_ << GetCurrentProcessId() << ':';
-  if (log_thread_id)
-    stream_ << GetCurrentThreadId() << ':';
-  if (log_timestamp) {
-    time_t t = time(NULL);
-#if _MSC_VER >= 1400
-    struct tm local_time = {0};
-    localtime_s(&local_time, &t);
-    struct tm* tm_time = &local_time;
-#else
-    struct tm* tm_time = localtime(&t);
-#endif
-    stream_ << std::setfill('0')
-            << std::setw(2) << 1 + tm_time->tm_mon
-            << std::setw(2) << tm_time->tm_mday
-            << '/'
-            << std::setw(2) << tm_time->tm_hour
-            << std::setw(2) << tm_time->tm_min
-            << std::setw(2) << tm_time->tm_sec
-            << ':';
-  }
-  if (log_tickcount)
-    stream_ << GetTickCount() << ':';
-  stream_ << log_severity_names[severity_] << ":" << file << "(" << line
-      << ")] ";
-}
-
-LogMessage::~LogMessage() {
-  // TODO(omaha) modify the macros so that nothing is executed when the log
-  // level is too high or there is
-  if (severity_ < min_log_level)
-    return;
-
-  std::string str_newline(stream_.str(), stream_.pcount());
-  str_newline.append("\r\n");
-  if (logging_destination != LOG_ONLY_TO_FILE)
-    OutputDebugStringA(str_newline.c_str());
-
-  // write to log file
-  if (logging_destination != LOG_ONLY_TO_SYSTEM_DEBUG_LOG &&
-      VerifyLogFileHandle()) {
-    // we can have multiple threads and/or processes, so try to prevent them
-    // from clobbering each other's writes
-    if (lock_log_file == LOCK_LOG_FILE) {
-      // Ensure that the mutex is initialized in case the client app did not
-      // call InitLogging. This is not thread safe. See below
-      InitLogMutex();
-
-      DWORD r = ::WaitForSingleObject(log_mutex, INFINITE);
-      DCHECK(r != WAIT_ABANDONED);
-    } else {
-      // use the critical section
-      if (!initialized_critical_section) {
-        // The client app did not call InitLogging, and so the critical section
-        // has not been created. We do this on demand, but if two threads try to
-        // do this at the same time, there will be a race condition to create
-        // the critical section. This is why InitLogging should be called from
-        // the main thread at the beginning of execution.
-        InitializeCriticalSection(&log_critical_section);
-        initialized_critical_section = true;
-      }
-      EnterCriticalSection(&log_critical_section);
-    }
-
-    SetFilePointer(log_file, 0, 0, SEEK_END);
-    DWORD num_written;
-    WriteFile(log_file, (void*)str_newline.c_str(), (DWORD)str_newline.length(),
-              &num_written, NULL);
-
-    if (lock_log_file == LOCK_LOG_FILE) {
-      ReleaseMutex(log_mutex);
-    } else {
-      LeaveCriticalSection(&log_critical_section);
-    }
-  }
-
-  if (severity_ == LOG_FATAL) {
-    // display a message or break into the debugger on a fatal error
-    if (::IsDebuggerPresent()) {
-      DebugBreak();
-    } else {
-      if (log_assert_handler) {
-        log_assert_handler(std::string(stream_.str(), stream_.pcount()));
-      } else {
-        // don't use the string with the newline, get a fresh version to send to
-        // the debug message process
-        DisplayDebugMessage(std::string(stream_.str(), stream_.pcount()));
-        TerminateProcess(GetCurrentProcess(), 1);
-      }
-    }
-  }
-
-  // Calling stream_.str() freezes the stream buffer.  A frozen buffer will
-  // not be freed during strstreambuf destruction.
-  stream_.freeze(false);
-}
-
-void CloseLogFile() {
-  if (!log_file)
-    return;
-
-  CloseHandle(log_file);
-  log_file = NULL;
-}
-
-} // namespace logging
-
-std::ostream& operator<<(std::ostream& out, const wchar_t* wstr) {
-  if (!wstr || !wstr[0])
-    return out;
-
-  // compute the length of the buffer we'll need
-  int charcount = WideCharToMultiByte(CP_UTF8, 0, wstr, -1,
-                                      NULL, 0, NULL, NULL);
-  if (charcount == 0)
-    return out;
-
-  // convert
-  scoped_array<char> buf(new char[charcount]);
-  WideCharToMultiByte(CP_UTF8, 0, wstr, -1, buf.get(), charcount, NULL, NULL);
-  return out << buf.get();
-}
diff --git a/common/logging_unittest.cc b/common/logging_unittest.cc
deleted file mode 100644
index 4693a82..0000000
--- a/common/logging_unittest.cc
+++ /dev/null
@@ -1,268 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "base/basictypes.h"
-#include "omaha/common/logging.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(LoggingTest, Logging) {
-#ifdef _DEBUG
-  OPT_LOG(L1, (_T("[OPT_LOG from debug build.]")));
-#else
-  OPT_LOG(L1, (_T("[OPT_LOG from optimized build.]")));
-#endif
-}
-
-class FileLogWriterTest : public testing::Test {
- public:
-
-  int FindFirstInMultiString(const TCHAR* multi_str,
-                             size_t count,
-                             const TCHAR* str) {
-    return FileLogWriter::FindFirstInMultiString(multi_str, count, str);
-  }
-};
-
-class HistoryTest : public testing::Test {
- protected:
-  HistoryTest() {
-    logging_ = GetLogging();
-  }
-
-  void AppendToHistory(const wchar_t* msg) {
-    logging_->AppendToHistory(msg);
-  }
-
-  CString GetHistory() {
-    return logging_->GetHistory();
-  }
-
- private:
-  Logging* logging_;
-};
-
-#define EOS _T("")
-TEST_F(FileLogWriterTest, FindInMultiString) {
-  // One string of one char.
-  const TCHAR s1[] = _T("a\0") EOS;
-  EXPECT_EQ(FindFirstInMultiString(s1, arraysize(s1), _T("a")), 0);
-
-  // One string.
-  const TCHAR s2[] = _T("abc\0") EOS;
-  EXPECT_EQ(FindFirstInMultiString(s2, arraysize(s2), _T("abc")), 0);
-
-  // Two strings of one char.
-  const TCHAR s3[] = _T("a\0b\0") EOS;
-  EXPECT_EQ(FindFirstInMultiString(s3, arraysize(s3), _T("b")), 2);
-
-  // Two strings.
-  const TCHAR s4[] = _T("ab\0cde\0") EOS;
-  EXPECT_EQ(FindFirstInMultiString(s4, arraysize(s4), _T("cde")), 3);
-
-  // Three strings one char.
-  const TCHAR s5[] = _T("a\0b\0c\0") EOS;
-  EXPECT_EQ(FindFirstInMultiString(s5, arraysize(s5), _T("c")), 4);
-
-  // Many strings.
-  const TCHAR s6[] = _T("a\0bcd\0efgh\0") EOS;
-  EXPECT_EQ(FindFirstInMultiString(s6, arraysize(s6), _T("efgh")), 6);
-
-  // Many strings including empty string.
-  const TCHAR s7[] = _T("a\0\0bc\0\0de\0fg") EOS;
-  EXPECT_EQ(FindFirstInMultiString(s7, arraysize(s7), _T("fg")), 10);
-
-  // Many strings, empty string at the end, negative test.
-  const TCHAR s8[] = _T("a\0bcd\0efgh\0\0") EOS;
-  EXPECT_EQ(FindFirstInMultiString(s8, arraysize(s8), _T("foo")), -1);
-
-  // Another negative test.
-  const TCHAR s9[] = _T("a\0bcd\0\0\0efgh\0") EOS;
-  EXPECT_EQ(FindFirstInMultiString(s9, arraysize(s9), _T("foo")), -1);
-
-  // Empty string is always found.
-  const TCHAR s10[] = _T("\0") EOS;
-  EXPECT_EQ(FindFirstInMultiString(s10, arraysize(s10), _T("\0")), 0);
-
-  const TCHAR s11[] = _T("\0") EOS;
-  EXPECT_EQ(FindFirstInMultiString(s11, arraysize(s11), _T("a")), -1);
-}
-
-TEST_F(HistoryTest, GetHistory) {
-  EXPECT_TRUE(GetHistory().IsEmpty());
-
-  const TCHAR msg1[] = _T("Hello");
-  AppendToHistory(msg1);
-  EXPECT_STREQ(msg1, GetHistory());
-  EXPECT_TRUE(GetHistory().IsEmpty());
-}
-
-TEST_F(HistoryTest, AppendToHistoryTest) {
-  // Test one character.
-  const TCHAR msg1[] = _T("A");
-  AppendToHistory(msg1);
-  EXPECT_STREQ(msg1, GetHistory());
-
-  // Test small string.
-  const TCHAR msg2[] = _T("ABCD");
-  AppendToHistory(msg2);
-  EXPECT_STREQ(msg2, GetHistory());
-
-  // Test one string that fills the buffer.
-  TCHAR msg3[kMaxHistoryBufferSize + 1] = {0};
-  for (int i = 0; i <= kMaxHistoryBufferSize; ++i) {
-    msg3[i] = _T('A');
-  }
-  msg3[kMaxHistoryBufferSize] = _T('\0');
-  AppendToHistory(msg3);
-  EXPECT_STREQ(msg3, GetHistory());
-
-  // Test set of strings that exactly fill buffer.
-  const int test_buffer_size = 64;
-  TCHAR msg4[test_buffer_size + 1] = {0};
-  for (int i = 0; i <= test_buffer_size; ++i) {
-    msg4[i] = _T('A');
-  }
-  msg4[test_buffer_size] = _T('\0');
-
-  int num_times_to_append = kMaxHistoryBufferSize / test_buffer_size;
-  EXPECT_EQ(kMaxHistoryBufferSize, num_times_to_append * test_buffer_size);
-  for (int i = 0; i < num_times_to_append; ++i) {
-    AppendToHistory(msg4);
-  }
-  EXPECT_STREQ(msg3, GetHistory());
-}
-
-TEST_F(HistoryTest, AppendToHistoryTest_WrapAround) {
-  // Test string that wraps around the buffer.
-  // First fill kMaxHistoryBufferSize - 1 with one string, then use
-  // another string of length 2("XX"), and another string to length of length 3.
-  // "XFFFGGGGG....GGGX" should be the result. The returned string should
-  // be in correct FIFO order i.e. "GGGGG......XXFFF".
-  const TCHAR msg6[] = _T("XX");
-  const TCHAR msg7[] = _T("FFF");
-  const int test_buffer_size = kMaxHistoryBufferSize - 1;
-  TCHAR msg5[test_buffer_size + 1] = {0};
-  TCHAR expected_buffer[kMaxHistoryBufferSize + 1] = {0};
-  for (int i = 0; i <= test_buffer_size; ++i) {
-    msg5[i] = _T('G');
-  }
-  msg5[test_buffer_size] = _T('\0');
-
-  // Call test method.
-  AppendToHistory(msg5);
-  AppendToHistory(msg6);
-  AppendToHistory(msg7);
-
-  // Create the expected string.
-  for (int i = 0; i <= kMaxHistoryBufferSize; ++i) {
-    expected_buffer[i] = _T('G');
-  }
-  int msg6len = wcslen(msg6);
-  int msg7len = wcslen(msg7);
-  memcpy(expected_buffer + kMaxHistoryBufferSize - msg6len - msg7len,
-         msg6,
-         msg6len * sizeof(TCHAR));
-  memcpy(expected_buffer + kMaxHistoryBufferSize - msg7len,
-         msg7,
-         msg7len * sizeof(TCHAR));
-  expected_buffer[kMaxHistoryBufferSize] = _T('\0');
-  EXPECT_STREQ(expected_buffer, GetHistory());
-}
-
-TEST_F(HistoryTest, AppendToHistoryTest_AnotherWrapAroundTest) {
-  // Test string that wraps around the buffer.
-  // First fill the kMaxHistoryBufferSize - 1 with one string, then fill it with
-  // another string of same length.
-  const int test_buffer_size = kMaxHistoryBufferSize - 1;
-  TCHAR msg2[test_buffer_size + 1] = {0};
-  TCHAR msg1[test_buffer_size + 1] = {0};
-  TCHAR expected_buffer[kMaxHistoryBufferSize + 1] = {0};
-  for (int i = 0; i <= test_buffer_size; ++i) {
-    msg1[i] = _T('G');
-    msg2[i] = _T('J');
-  }
-  msg2[test_buffer_size] = _T('\0');
-  msg1[test_buffer_size] = _T('\0');
-
-  // Call test method.
-  AppendToHistory(msg1);
-  AppendToHistory(msg2);
-
-  // Create the expected string.
-  for (int i = 0; i <= kMaxHistoryBufferSize; ++i) {
-    expected_buffer[i] = _T('J');
-  }
-  expected_buffer[0] = _T('G');
-  expected_buffer[kMaxHistoryBufferSize] = _T('\0');
-  EXPECT_STREQ(expected_buffer, GetHistory());
-}
-
-TEST_F(HistoryTest, AppendToHistoryTest_LotsOfLogs) {
-  // Run over a number of Append calls, with strings length
-  // (kMaxHistoryBufferSize / 2) + 1, causing wrap on every run.
-  TCHAR expected_buffer[kMaxHistoryBufferSize + 1] = {0};
-  const int test_buffer_size = (kMaxHistoryBufferSize / 2) + 1;
-  TCHAR msg1[test_buffer_size + 1] = {0};
-  for (int test_char = 'A'; test_char <= 'Z'; ++test_char) {
-    for (int i = 0; i <= test_buffer_size; ++i) {
-      msg1[i] = static_cast<TCHAR>(test_char);
-    }
-    msg1[test_buffer_size] = _T('\0');
-
-    // Call test method.
-    AppendToHistory(msg1);
-  }
-
-  // Create the expected string.
-  int i = 0;
-  for (; i < test_buffer_size - 2; ++i) {
-    expected_buffer[i] = _T('Y');
-  }
-  for (; i <= kMaxHistoryBufferSize; ++i) {
-    expected_buffer[i] = _T('Z');
-  }
-  expected_buffer[kMaxHistoryBufferSize] = _T('\0');
-  EXPECT_STREQ(expected_buffer, GetHistory());
-}
-
-TEST_F(HistoryTest, AppendToHistoryTest_LargeBuffer) {
-  // Test with a message that is larger than the buffer.
-  const int test_buffer_size = kMaxHistoryBufferSize + 10;
-  TCHAR msg4[test_buffer_size + 1] = {0};
-  TCHAR expected_buffer[kMaxHistoryBufferSize + 1] = {0};
-  for (int i = 0; i < test_buffer_size; ++i) {
-    msg4[i] = _T('A');
-  }
-  msg4[test_buffer_size] = _T('\0');
-
-  for (int i = 0; i <= kMaxHistoryBufferSize; ++i) {
-    expected_buffer[i] = _T('A');
-  }
-  expected_buffer[kMaxHistoryBufferSize] = _T('\0');
-
-  AppendToHistory(msg4);
-  EXPECT_STREQ(expected_buffer, GetHistory());
-}
-
-TEST_F(HistoryTest, AppendToHistoryTest_EmptyBuffer) {
-  CString test_string;
-  AppendToHistory(test_string);
-  EXPECT_TRUE(GetHistory().IsEmpty());
-}
-
-}  // namespace omaha
-
diff --git a/common/md5.cc b/common/md5.cc
deleted file mode 100644
index 1477361..0000000
--- a/common/md5.cc
+++ /dev/null
@@ -1,453 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-/*
- * md5_opt.c V1.0 - optimized md5c.c from RFC1321 reference implementation
- *
- * Copyright (c) 1995 University of Southern California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation, advertising
- * materials, and other materials related to such distribution and use
- * acknowledge that the software was developed by the University of
- * Southern California, Information Sciences Institute.  The name of the
- * University may not be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- J. Touch / touch@isi.edu
- 5/1/95
-
-*/
-/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
- */
-
-/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
-rights reserved.
-
-License to copy and use this software is granted provided that it
-is identified as the "RSA Data Security, Inc. MD5 Message-Digest
-Algorithm" in all material mentioning or referencing this software
-or this function.
-
-License is also granted to make and use derivative works provided
-that such works are identified as "derived from the RSA Data
-Security, Inc. MD5 Message-Digest Algorithm" in all material
-mentioning or referencing the derived work.
-
-RSA Data Security, Inc. makes no representations concerning either
-the merchantability of this software or the suitability of this
-software for any particular purpose. It is provided "as is"
-without express or implied warranty of any kind.
-
-These notices must be retained in any copies of any part of this
-documentation and/or software.
-*/
-
-#include "md5.h"
-#include "common/debug.h"
-
-namespace omaha {
-
-#if (defined(i386) || defined (__i386__) || defined (_M_IX86) || defined(__alpha))  // little-endian
-#undef REORDER
-#else
-#define REORDER 1
-#endif
-
-// Constants for MD5Transform routine
-#define kS11 7
-#define kS12 12
-#define kS13 17
-#define kS14 22
-#define kS21 5
-#define kS22 9
-#define kS23 14
-#define kS24 20
-#define kS31 4
-#define kS32 11
-#define kS33 16
-#define kS34 23
-#define kS41 6
-#define kS42 10
-#define kS43 15
-#define kS44 21
-
-static void MD5Transform (uint32 [4], unsigned char [64]);
-static void Encode (unsigned char *, uint32 *, unsigned int);
-#ifdef REORDER
-static void Decode (uint32 *, unsigned char *, unsigned int);
-#endif
-
-// SELECTANY static unsigned char PADDING[64] = {
-static unsigned char PADDING[64] = {
-    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-};
-
-// F, G, H and I are basic MD5 functions.
-#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
-#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-#define I(x, y, z) ((y) ^ ((x) | (~z)))
-
-// ROTATE_LEFT rotates x left n bits.
-#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
-
-// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
-// Rotation is separate from addition to prevent recomputation.
-#define FF(a, b, c, d, x, s, ac) { \
-    (a) += F ((b), (c), (d)) + (x) + (uint32)(ac); \
-    (a) = ROTATE_LEFT ((a), (s)); \
-    (a) += (b); \
-    }
-#define GG(a, b, c, d, x, s, ac) { \
-    (a) += G ((b), (c), (d)) + (x) + (uint32)(ac); \
-    (a) = ROTATE_LEFT ((a), (s)); \
-    (a) += (b); \
-    }
-#define HH(a, b, c, d, x, s, ac) { \
-    (a) += H ((b), (c), (d)) + (x) + (uint32)(ac); \
-    (a) = ROTATE_LEFT ((a), (s)); \
-    (a) += (b); \
-    }
-#define II(a, b, c, d, x, s, ac) { \
-    (a) += I ((b), (c), (d)) + (x) + (uint32)(ac); \
-    (a) = ROTATE_LEFT ((a), (s)); \
-    (a) += (b); \
-    }
-
-// MD5 initialization. Begins an MD5 operation, writing a new context.
-void MD5Init (MD5_CTX *context) {
-    ASSERT(context, (L""));
-
-    context->count[0] = context->count[1] = 0;
-    // Load magic initialization constants.
-    context->state[0] = 0x67452301;
-    context->state[1] = 0xefcdab89;
-    context->state[2] = 0x98badcfe;
-    context->state[3] = 0x10325476;
-}
-
-// MD5 block update operation. Continues an MD5 message-digest
-// operation, processing another message block, and updating the context.
-void MD5Update (MD5_CTX *context, unsigned char *input, unsigned int inputLen) {
-    ASSERT(input, (L""));
-    ASSERT(context, (L""));
-
-    unsigned int i, index, partLen;
-
-    // Compute number of bytes mod 64
-    index = (unsigned int)((context->count[0] >> 3) & 0x3F);
-
-    // Update number of bits
-    if ((context->count[0] += ((uint32)inputLen << 3)) < ((uint32)inputLen << 3))
-        context->count[1]++;
-
-    context->count[1] += ((uint32)inputLen >> 29);
-    partLen = 64 - index;
-
-    // Transform as many times as possible
-    if (inputLen >= partLen) {
-       memcpy ((POINTER)&context->buffer[index], (POINTER)input, partLen);
-       MD5Transform (context->state, context->buffer);
-
-       for (i = partLen; i + 63 < inputLen; i += 64) MD5Transform (context->state, &input[i]);
-       index = 0;
-       }
-    else
-       i = 0;
-
-    // Buffer remaining input
-    memcpy ((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i);
-}
-
-// MD5 finalization. Ends an MD5 message-digest operation, writing the
-// the message digest and zeroizing the context.
-void MD5Final (unsigned char digest[16], MD5_CTX *context) {
-    ASSERT(context, (L""));
-
-    unsigned char bits[8];
-    unsigned int index, padLen;
-
-    // Save number of bits
-    Encode (bits, context->count, 8);
-
-    // Pad out to 56 mod 64.
-    index = (unsigned int)((context->count[0] >> 3) & 0x3f);
-    padLen = (index < 56) ? (56 - index) : (120 - index);
-    MD5Update (context, PADDING, padLen);
-
-    // Append length (before padding)
-    MD5Update (context, bits, 8);
-    // Store state in digest
-    Encode (digest, context->state, 16);
-
-    // Zeroize sensitive information.
-    memset ((POINTER)context, 0, sizeof (*context));
-}
-
-// MD5 basic transformation. Transforms state based on block.
-void MD5Transform (uint32 state[4], unsigned char block[64]) {
-    // USC/ISI J. Touch - encourage pushing state variables into registers
-    register uint32 a = state[0], b = state[1], c = state[2], d = state[3];
-
-    /* USC/ISI J. Touch
-    decode and using copied data vs. direct use of input buffer
-    depends on whether reordering is required, which is a combination
-    of the byte order of the architecture and the command-line option
-    override
-    */
-
-#ifdef REORDER
-    uint32 x[16];
-
-    Decode (x, block, 64);
-
-    /* Round 1 */
-    FF (a, b, c, d, x[ 0], kS11, 0xd76aa478); /* 1 */
-    FF (d, a, b, c, x[ 1], kS12, 0xe8c7b756); /* 2 */
-    FF (c, d, a, b, x[ 2], kS13, 0x242070db); /* 3 */
-    FF (b, c, d, a, x[ 3], kS14, 0xc1bdceee); /* 4 */
-    FF (a, b, c, d, x[ 4], kS11, 0xf57c0faf); /* 5 */
-    FF (d, a, b, c, x[ 5], kS12, 0x4787c62a); /* 6 */
-    FF (c, d, a, b, x[ 6], kS13, 0xa8304613); /* 7 */
-    FF (b, c, d, a, x[ 7], kS14, 0xfd469501); /* 8 */
-    FF (a, b, c, d, x[ 8], kS11, 0x698098d8); /* 9 */
-    FF (d, a, b, c, x[ 9], kS12, 0x8b44f7af); /* 10 */
-    FF (c, d, a, b, x[10], kS13, 0xffff5bb1); /* 11 */
-    FF (b, c, d, a, x[11], kS14, 0x895cd7be); /* 12 */
-    FF (a, b, c, d, x[12], kS11, 0x6b901122); /* 13 */
-    FF (d, a, b, c, x[13], kS12, 0xfd987193); /* 14 */
-    FF (c, d, a, b, x[14], kS13, 0xa679438e); /* 15 */
-    FF (b, c, d, a, x[15], kS14, 0x49b40821); /* 16 */
-
-    /* Round 2 */
-    GG (a, b, c, d, x[ 1], kS21, 0xf61e2562); /* 17 */
-    GG (d, a, b, c, x[ 6], kS22, 0xc040b340); /* 18 */
-    GG (c, d, a, b, x[11], kS23, 0x265e5a51); /* 19 */
-    GG (b, c, d, a, x[ 0], kS24, 0xe9b6c7aa); /* 20 */
-    GG (a, b, c, d, x[ 5], kS21, 0xd62f105d); /* 21 */
-    GG (d, a, b, c, x[10], kS22,  0x2441453); /* 22 */
-    GG (c, d, a, b, x[15], kS23, 0xd8a1e681); /* 23 */
-    GG (b, c, d, a, x[ 4], kS24, 0xe7d3fbc8); /* 24 */
-    GG (a, b, c, d, x[ 9], kS21, 0x21e1cde6); /* 25 */
-    GG (d, a, b, c, x[14], kS22, 0xc33707d6); /* 26 */
-    GG (c, d, a, b, x[ 3], kS23, 0xf4d50d87); /* 27 */
-    GG (b, c, d, a, x[ 8], kS24, 0x455a14ed); /* 28 */
-    GG (a, b, c, d, x[13], kS21, 0xa9e3e905); /* 29 */
-    GG (d, a, b, c, x[ 2], kS22, 0xfcefa3f8); /* 30 */
-    GG (c, d, a, b, x[ 7], kS23, 0x676f02d9); /* 31 */
-    GG (b, c, d, a, x[12], kS24, 0x8d2a4c8a); /* 32 */
-
-    /* Round 3 */
-    HH (a, b, c, d, x[ 5], kS31, 0xfffa3942); /* 33 */
-    HH (d, a, b, c, x[ 8], kS32, 0x8771f681); /* 34 */
-    HH (c, d, a, b, x[11], kS33, 0x6d9d6122); /* 35 */
-    HH (b, c, d, a, x[14], kS34, 0xfde5380c); /* 36 */
-    HH (a, b, c, d, x[ 1], kS31, 0xa4beea44); /* 37 */
-    HH (d, a, b, c, x[ 4], kS32, 0x4bdecfa9); /* 38 */
-    HH (c, d, a, b, x[ 7], kS33, 0xf6bb4b60); /* 39 */
-    HH (b, c, d, a, x[10], kS34, 0xbebfbc70); /* 40 */
-    HH (a, b, c, d, x[13], kS31, 0x289b7ec6); /* 41 */
-    HH (d, a, b, c, x[ 0], kS32, 0xeaa127fa); /* 42 */
-    HH (c, d, a, b, x[ 3], kS33, 0xd4ef3085); /* 43 */
-    HH (b, c, d, a, x[ 6], kS34,  0x4881d05); /* 44 */
-    HH (a, b, c, d, x[ 9], kS31, 0xd9d4d039); /* 45 */
-    HH (d, a, b, c, x[12], kS32, 0xe6db99e5); /* 46 */
-    HH (c, d, a, b, x[15], kS33, 0x1fa27cf8); /* 47 */
-    HH (b, c, d, a, x[ 2], kS34, 0xc4ac5665); /* 48 */
-
-    /* Round 4 */
-    II (a, b, c, d, x[ 0], kS41, 0xf4292244); /* 49 */
-    II (d, a, b, c, x[ 7], kS42, 0x432aff97); /* 50 */
-    II (c, d, a, b, x[14], kS43, 0xab9423a7); /* 51 */
-    II (b, c, d, a, x[ 5], kS44, 0xfc93a039); /* 52 */
-    II (a, b, c, d, x[12], kS41, 0x655b59c3); /* 53 */
-    II (d, a, b, c, x[ 3], kS42, 0x8f0ccc92); /* 54 */
-    II (c, d, a, b, x[10], kS43, 0xffeff47d); /* 55 */
-    II (b, c, d, a, x[ 1], kS44, 0x85845dd1); /* 56 */
-    II (a, b, c, d, x[ 8], kS41, 0x6fa87e4f); /* 57 */
-    II (d, a, b, c, x[15], kS42, 0xfe2ce6e0); /* 58 */
-    II (c, d, a, b, x[ 6], kS43, 0xa3014314); /* 59 */
-    II (b, c, d, a, x[13], kS44, 0x4e0811a1); /* 60 */
-    II (a, b, c, d, x[ 4], kS41, 0xf7537e82); /* 61 */
-    II (d, a, b, c, x[11], kS42, 0xbd3af235); /* 62 */
-    II (c, d, a, b, x[ 2], kS43, 0x2ad7d2bb); /* 63 */
-    II (b, c, d, a, x[ 9], kS44, 0xeb86d391); /* 64 */
-
-#else
-    // USC/ISI J. Touch
-    // omit reordering, and use the input block as source data
-    /* Round 1 */
-    FF (a, b, c, d, ((uint32 *)block)[ 0], kS11, 0xd76aa478); /* 1 */
-    FF (d, a, b, c, ((uint32 *)block)[ 1], kS12, 0xe8c7b756); /* 2 */
-    FF (c, d, a, b, ((uint32 *)block)[ 2], kS13, 0x242070db); /* 3 */
-    FF (b, c, d, a, ((uint32 *)block)[ 3], kS14, 0xc1bdceee); /* 4 */
-    FF (a, b, c, d, ((uint32 *)block)[ 4], kS11, 0xf57c0faf); /* 5 */
-    FF (d, a, b, c, ((uint32 *)block)[ 5], kS12, 0x4787c62a); /* 6 */
-    FF (c, d, a, b, ((uint32 *)block)[ 6], kS13, 0xa8304613); /* 7 */
-    FF (b, c, d, a, ((uint32 *)block)[ 7], kS14, 0xfd469501); /* 8 */
-    FF (a, b, c, d, ((uint32 *)block)[ 8], kS11, 0x698098d8); /* 9 */
-    FF (d, a, b, c, ((uint32 *)block)[ 9], kS12, 0x8b44f7af); /* 10 */
-    FF (c, d, a, b, ((uint32 *)block)[10], kS13, 0xffff5bb1); /* 11 */
-    FF (b, c, d, a, ((uint32 *)block)[11], kS14, 0x895cd7be); /* 12 */
-    FF (a, b, c, d, ((uint32 *)block)[12], kS11, 0x6b901122); /* 13 */
-    FF (d, a, b, c, ((uint32 *)block)[13], kS12, 0xfd987193); /* 14 */
-    FF (c, d, a, b, ((uint32 *)block)[14], kS13, 0xa679438e); /* 15 */
-    FF (b, c, d, a, ((uint32 *)block)[15], kS14, 0x49b40821); /* 16 */
-
-    /* Round 2 */
-    GG (a, b, c, d, ((uint32 *)block)[ 1], kS21, 0xf61e2562); /* 17 */
-    GG (d, a, b, c, ((uint32 *)block)[ 6], kS22, 0xc040b340); /* 18 */
-    GG (c, d, a, b, ((uint32 *)block)[11], kS23, 0x265e5a51); /* 19 */
-    GG (b, c, d, a, ((uint32 *)block)[ 0], kS24, 0xe9b6c7aa); /* 20 */
-    GG (a, b, c, d, ((uint32 *)block)[ 5], kS21, 0xd62f105d); /* 21 */
-    GG (d, a, b, c, ((uint32 *)block)[10], kS22,  0x2441453); /* 22 */
-    GG (c, d, a, b, ((uint32 *)block)[15], kS23, 0xd8a1e681); /* 23 */
-    GG (b, c, d, a, ((uint32 *)block)[ 4], kS24, 0xe7d3fbc8); /* 24 */
-    GG (a, b, c, d, ((uint32 *)block)[ 9], kS21, 0x21e1cde6); /* 25 */
-    GG (d, a, b, c, ((uint32 *)block)[14], kS22, 0xc33707d6); /* 26 */
-    GG (c, d, a, b, ((uint32 *)block)[ 3], kS23, 0xf4d50d87); /* 27 */
-    GG (b, c, d, a, ((uint32 *)block)[ 8], kS24, 0x455a14ed); /* 28 */
-    GG (a, b, c, d, ((uint32 *)block)[13], kS21, 0xa9e3e905); /* 29 */
-    GG (d, a, b, c, ((uint32 *)block)[ 2], kS22, 0xfcefa3f8); /* 30 */
-    GG (c, d, a, b, ((uint32 *)block)[ 7], kS23, 0x676f02d9); /* 31 */
-    GG (b, c, d, a, ((uint32 *)block)[12], kS24, 0x8d2a4c8a); /* 32 */
-
-    /* Round 3 */
-    HH (a, b, c, d, ((uint32 *)block)[ 5], kS31, 0xfffa3942); /* 33 */
-    HH (d, a, b, c, ((uint32 *)block)[ 8], kS32, 0x8771f681); /* 34 */
-    HH (c, d, a, b, ((uint32 *)block)[11], kS33, 0x6d9d6122); /* 35 */
-    HH (b, c, d, a, ((uint32 *)block)[14], kS34, 0xfde5380c); /* 36 */
-    HH (a, b, c, d, ((uint32 *)block)[ 1], kS31, 0xa4beea44); /* 37 */
-    HH (d, a, b, c, ((uint32 *)block)[ 4], kS32, 0x4bdecfa9); /* 38 */
-    HH (c, d, a, b, ((uint32 *)block)[ 7], kS33, 0xf6bb4b60); /* 39 */
-    HH (b, c, d, a, ((uint32 *)block)[10], kS34, 0xbebfbc70); /* 40 */
-    HH (a, b, c, d, ((uint32 *)block)[13], kS31, 0x289b7ec6); /* 41 */
-    HH (d, a, b, c, ((uint32 *)block)[ 0], kS32, 0xeaa127fa); /* 42 */
-    HH (c, d, a, b, ((uint32 *)block)[ 3], kS33, 0xd4ef3085); /* 43 */
-    HH (b, c, d, a, ((uint32 *)block)[ 6], kS34,  0x4881d05); /* 44 */
-    HH (a, b, c, d, ((uint32 *)block)[ 9], kS31, 0xd9d4d039); /* 45 */
-    HH (d, a, b, c, ((uint32 *)block)[12], kS32, 0xe6db99e5); /* 46 */
-    HH (c, d, a, b, ((uint32 *)block)[15], kS33, 0x1fa27cf8); /* 47 */
-    HH (b, c, d, a, ((uint32 *)block)[ 2], kS34, 0xc4ac5665); /* 48 */
-
-    /* Round 4 */
-    II (a, b, c, d, ((uint32 *)block)[ 0], kS41, 0xf4292244); /* 49 */
-    II (d, a, b, c, ((uint32 *)block)[ 7], kS42, 0x432aff97); /* 50 */
-    II (c, d, a, b, ((uint32 *)block)[14], kS43, 0xab9423a7); /* 51 */
-    II (b, c, d, a, ((uint32 *)block)[ 5], kS44, 0xfc93a039); /* 52 */
-    II (a, b, c, d, ((uint32 *)block)[12], kS41, 0x655b59c3); /* 53 */
-    II (d, a, b, c, ((uint32 *)block)[ 3], kS42, 0x8f0ccc92); /* 54 */
-    II (c, d, a, b, ((uint32 *)block)[10], kS43, 0xffeff47d); /* 55 */
-    II (b, c, d, a, ((uint32 *)block)[ 1], kS44, 0x85845dd1); /* 56 */
-    II (a, b, c, d, ((uint32 *)block)[ 8], kS41, 0x6fa87e4f); /* 57 */
-    II (d, a, b, c, ((uint32 *)block)[15], kS42, 0xfe2ce6e0); /* 58 */
-    II (c, d, a, b, ((uint32 *)block)[ 6], kS43, 0xa3014314); /* 59 */
-    II (b, c, d, a, ((uint32 *)block)[13], kS44, 0x4e0811a1); /* 60 */
-    II (a, b, c, d, ((uint32 *)block)[ 4], kS41, 0xf7537e82); /* 61 */
-    II (d, a, b, c, ((uint32 *)block)[11], kS42, 0xbd3af235); /* 62 */
-    II (c, d, a, b, ((uint32 *)block)[ 2], kS43, 0x2ad7d2bb); /* 63 */
-    II (b, c, d, a, ((uint32 *)block)[ 9], kS44, 0xeb86d391); /* 64 */
-#endif  // REORDER
-
-    state[0] += a;
-    state[1] += b;
-    state[2] += c;
-    state[3] += d;
-
-#ifdef REORDER
-    memset ((POINTER)x, 0, sizeof (x));  // zero sensitive information
-#endif
-}
-
-// Encodes input (uint32) into output (unsigned char). Assumes len is a multiple of 4.
-void Encode (unsigned char *output, uint32 *input, unsigned int len) {
-    ASSERT(input, (L""));
-    ASSERT(output, (L""));
-
-    unsigned int i, j;
-
-    for (i = 0, j = 0; j < len; i++, j += 4) {
-        output[j] = (unsigned char)(input[i] & 0xff);
-        output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
-        output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
-        output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
-        }
-}
-
-#ifdef REORDER
-
-// Decodes input (unsigned char) into output (uint32). Assumes len is a multiple of 4.
-void Decode (uint32 *output, unsigned char *input, unsigned int len) {
-    ASSERT(input, (L""));
-    ASSERT(output, (L""));
-
-    register uint32 out,other;
-
-    // for (i = 0, j = 0; j < len; i++, j += 4)
-    // output[i] = ((uint32)input[j]) | (((uint32)input[j+1]) << 8) |
-    // (((uint32)input[j+2]) << 16) | (((uint32)input[j+3]) << 24);
-
-    // USC/ISI J. Touch
-    // these are optimized swap routines, in C code they cost more in "computation" operations, but less in
-    // "loads" than the above code, and run substantially faster as a result
-#if (!defined(hpux))
-#define swapbyte(src,dst) { \
-     out = ROTATE_LEFT((src),16); \
-     other = out >> 8; \
-     other &= 0x00ff00ff; \
-     out &= 0x00ff00ff; \
-     out <<= 8; \
-     (dst) = out | other; \
-     }
-#else
-#define swapbyte(src,dst) { \
-     (dst) = (ROTATE_LEFT((src),8) & 0x00ff00ff) | ROTATE_LEFT((src) & 0x00ff00ff,24); \
-     }
-#endif
-
-    // USC/ISI J. Touch
-    // unroll the loop above, because the code runs faster with constants for indices than even with variable indices
-    // as conventional (automatic) unrolling would perform (!)
-    swapbyte(((uint32 *)input)[0],output[0]);
-    swapbyte(((uint32 *)input)[1],output[1]);
-    swapbyte(((uint32 *)input)[2],output[2]);
-    swapbyte(((uint32 *)input)[3],output[3]);
-    swapbyte(((uint32 *)input)[4],output[4]);
-    swapbyte(((uint32 *)input)[5],output[5]);
-    swapbyte(((uint32 *)input)[6],output[6]);
-    swapbyte(((uint32 *)input)[7],output[7]);
-    swapbyte(((uint32 *)input)[8],output[8]);
-    swapbyte(((uint32 *)input)[9],output[9]);
-    swapbyte(((uint32 *)input)[10],output[10]);
-    swapbyte(((uint32 *)input)[11],output[11]);
-    swapbyte(((uint32 *)input)[12],output[12]);
-    swapbyte(((uint32 *)input)[13],output[13]);
-    swapbyte(((uint32 *)input)[14],output[14]);
-    swapbyte(((uint32 *)input)[15],output[15]);
-}
-
-#endif // #ifdef REORDER
-
-}  // namespace omaha
-
diff --git a/common/md5_unittest.cc b/common/md5_unittest.cc
deleted file mode 100644
index ac4ff42..0000000
--- a/common/md5_unittest.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-// MD5 unittest
-
-#include <atlstr.h>
-#include "omaha/common/debug.h"
-#include "omaha/common/md5.h"
-#include "omaha/common/string.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-void CheckMD5 (char *string, TCHAR *correct_digest) {
-    ASSERT_TRUE(correct_digest);
-    ASSERT_TRUE(string);
-
-    MD5_CTX context;
-    MD5Init (&context);
-    MD5Update (&context, (unsigned char *)string, strlen (string));
-    unsigned char digest[16];
-    MD5Final (digest, &context);
-
-    const DWORD digest_len = 32+1;
-    TCHAR digest_string[digest_len];
-    digest_string[31] = '\0';
-    digest_string[0] = '\0';
-
-    for (int i = 0; i < 16; i++) {
-        SafeStrCat (digest_string, SPRINTF (_T("%02x"), digest[i]), digest_len);
-    }
-
-    ASSERT_STREQ(digest_string, correct_digest);
-}
-
-TEST(Md5Test, MD5) {
-    CheckMD5 ("", _T("d41d8cd98f00b204e9800998ecf8427e"));
-    CheckMD5 ("a", _T("0cc175b9c0f1b6a831c399e269772661"));
-    CheckMD5 ("abc", _T("900150983cd24fb0d6963f7d28e17f72"));
-    CheckMD5 ("message digest", _T("f96b697d7cb7938d525a2f31aaf161d0"));
-    CheckMD5 ("abcdefghijklmnopqrstuvwxyz", _T("c3fcd3d76192e4007dfb496cca67e13b"));
-    CheckMD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", _T("d174ab98d277d9f5a5611c2c9f419d9f"));
-    CheckMD5 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890", _T("57edf4a22be3c955ac49da2e2107b67a"));
-}
-
-}  // namespace omaha
-
diff --git a/common/module_utils.cc b/common/module_utils.cc
deleted file mode 100644
index 2a34a4c..0000000
--- a/common/module_utils.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/module_utils.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-const int kLongPath = (_MAX_PATH * 2);
-const int kReallyLongPath = (kLongPath * 2);
-
-HMODULE ModuleFromStatic(void* pointer_to_static_in_module) {
-  ASSERT(pointer_to_static_in_module, (L""));
-
-  MEMORY_BASIC_INFORMATION info = { 0 };
-  VirtualQuery(reinterpret_cast<void*>(pointer_to_static_in_module),
-    &info, sizeof(info));
-  // Module handles are just the allocation base address of the module.
-  return reinterpret_cast<HMODULE>(info.AllocationBase);
-}
-
-bool GetModuleDirectory(HMODULE module, TCHAR* directory) {
-  ASSERT(directory, (L"Invalid arguments"));
-  if (!directory) {
-    return false;
-  }
-
-  // PathRemoveFileSpec only supports buffers up to MAX_PATH so we must
-  // limit ourselves to this.  It will "always" work anyway, given that
-  // our installation path is not absurdly deep.
-  if (0 == GetModuleFileName(module, directory, MAX_PATH)) {
-    ASSERT(false, (L"Path longer than MAX_PATH"));
-    return false;
-  }
-
-  if (!String_PathRemoveFileSpec(directory)) {
-    ASSERT(false, (L"PathRemoveFileSpec failed"));
-    // Ensure we don't return with an incorrect path in the buffer that was
-    // passed in.
-    ZeroMemory(directory, MAX_PATH * sizeof(TCHAR));
-    return false;
-  }
-
-  return true;
-}
-
-HRESULT GetModuleFileName(HMODULE module, CString* path) {
-  ASSERT(path, (_T("must be valid")));
-
-  // _MAX_PATH should cover at least 99% of the paths
-  int buf_size = _MAX_PATH;
-  int chars_copied = 0;
-  while ((chars_copied = ::GetModuleFileName(module,
-                                             CStrBuf(*path, buf_size + 1),
-                                             buf_size)) == buf_size) {
-    // We'll stop before things get ridiculous
-    if (buf_size >= kReallyLongPath) {
-      UTIL_LOG(LEVEL_ERROR,
-               (_T("[GetModuleFileName - unusually long path '%s']"), path));
-      chars_copied = 0;
-      ::SetLastError(ERROR_NOT_ENOUGH_MEMORY);
-      break;
-    }
-
-    buf_size *= 2;
-  }
-
-  if (!chars_copied) {
-    path->Empty();
-    return GetCurError();
-  }
-
-  return S_OK;
-}
-
-}  // namespace omaha
-
diff --git a/common/module_utils.h b/common/module_utils.h
deleted file mode 100644
index 9c4c61e..0000000
--- a/common/module_utils.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_COMMON_MODULE_UTILS_H_
-#define OMAHA_COMMON_MODULE_UTILS_H_
-
-#include "omaha/common/string.h"
-
-namespace omaha {
-
-// Utilities for working with modules in processes.
-
-// Returns the module handle of a module, given a pointer to a static
-// member of the module (e.g. a static function or static variable).
-HMODULE ModuleFromStatic(void* pointer_to_static_in_module);
-
-// Copies the path of the directory that contains the file for 'module' into
-// 'directory'.
-//
-// @param module Must be a valid, non-NULL module handle.
-// @param directory MUST have room for MAX_PATH characters or more.  The path
-// copied into this buffer will not have a trailing backslash.
-//
-// @return false iff there is an error.
-bool GetModuleDirectory(HMODULE module, TCHAR* directory);
-
-/**
-* Returns a path to a module.  Uses the
-* Win32 GetModuleFileName function, so you
-* can pass NULL for module.
-*
-* @param module Handle to the module or NULL for the current module.
-* @param path Holds the path to the module on successful return.
-* @returns S_OK if successful, otherwise an error value.
-*/
-HRESULT GetModuleFileName(HMODULE module, OUT CString* path);
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_MODULE_UTILS_H_
diff --git a/common/module_utils_unittest.cc b/common/module_utils_unittest.cc
deleted file mode 100644
index fa409b3..0000000
--- a/common/module_utils_unittest.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Unit test for module utility functions.
-
-#include "omaha/common/constants.h"
-#include "omaha/common/module_utils.h"
-#include "omaha/common/path.h"
-#include "omaha/common/utils.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(ModuleUtilsTest, ModuleUtils) {
-  // ModuleFromStatic
-  HMODULE module = ModuleFromStatic(reinterpret_cast<void*>(ModuleFromStatic));
-  ASSERT_TRUE(module);
-
-  // GetModuleDirectory
-  TCHAR directory1[MAX_PATH] = {0};
-  TCHAR directory2[MAX_PATH] = {0};
-  ASSERT_TRUE(GetModuleDirectory(module, directory1));
-  ASSERT_TRUE(GetModuleDirectory(NULL, directory2));
-  EXPECT_STREQ(directory1, directory2);
-
-  // GetModuleFileName
-  CString path1;
-  CString path2;
-  ASSERT_SUCCEEDED(GetModuleFileName(module, &path1));
-  ASSERT_SUCCEEDED(GetModuleFileName(NULL, &path2));
-  EXPECT_STREQ(path1, path2);
-
-  // Verify values, as much as we can.
-  CString file(GetFileFromPath(path1));
-  EXPECT_STREQ(file, kUnittestName);
-
-  CString dir(GetDirectoryFromPath(path1));
-  EXPECT_STREQ(dir, directory1);
-}
-
-}  // namespace omaha
diff --git a/common/object_factory.h b/common/object_factory.h
deleted file mode 100644
index 18c04e4..0000000
--- a/common/object_factory.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_COMMON_OBJECT_FACTORY_H__
-#define OMAHA_COMMON_OBJECT_FACTORY_H__
-
-#include <map>
-
-namespace omaha {
-
-// Factory creates instances of objects based on a unique type id.
-//
-// AbstractProduct - base class of the product hierarchy.
-// TypeId - type id for each type in the hierarchy.
-// ProductCreator - callable entity to create objects.
-
-template <class AbstractProduct,
-          typename TypeId,
-          typename ProductCreator = AbstractProduct* (*)()>
-class Factory {
- public:
-  Factory() {}
-
-  // Registers a creator for the type id. Returns true if the creator has
-  // been registered succesfully.
-  bool Register(const TypeId& id, ProductCreator creator) {
-    return id_to_creators_.insert(Map::value_type(id, creator)).second;
-  }
-
-  // Unregisters a type id.
-  bool Unregister(const TypeId& id) {
-    return id_to_creators_.erase(id) == 1;
-  }
-
-  // Creates an instance of the abstract product.
-  AbstractProduct* CreateObject(const TypeId& id) {
-    typename Map::const_iterator it = id_to_creators_.find(id);
-    if (it != id_to_creators_.end()) {
-      return (it->second)();
-    } else {
-      return NULL;
-    }
-  }
-
- private:
-  typedef std::map<TypeId, ProductCreator> Map;
-  Map id_to_creators_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(Factory);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_OBJECT_FACTORY_H__
-
diff --git a/common/object_factory_unittest.cc b/common/object_factory_unittest.cc
deleted file mode 100644
index 2e7bf11..0000000
--- a/common/object_factory_unittest.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/object_factory.h"
-#include "omaha/testing/unit_test.h"
-
-// TODO(omaha): write unit tests
diff --git a/common/oem_install_utils.cc b/common/oem_install_utils.cc
new file mode 100644
index 0000000..1358dfe
--- /dev/null
+++ b/common/oem_install_utils.cc
@@ -0,0 +1,120 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+//
+// *** Documentation for OEM Installs ***
+//
+// A /oem install requires:
+//  * Per-machine install
+//  * Running as admin
+//  * Running in Windows audit mode (ConfigManager::IsWindowsInstalling())
+//  * Offline installer (determined in LaunchHandoffProcess())
+//
+// If the first three conditions are met, SetOemInstallState() writes the
+// OemInstallTime registry value, which is used by IsOemInstalling() along with
+// other logic to determine whether Omaha is running in an OEM install
+// environment. Other objects use IsOemInstalling() - not
+// ConfigureManager::IsWindowsInstalling() - to determine whether to run in a
+// disabled mode for OEM factory installations. For example, the core exits
+// immediately without checking for updates or Code Red events, no instances
+// ping, and persistent IDs are not saved.
+
+#include "omaha/common/oem_install_utils.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/time.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
+
+namespace omaha {
+
+namespace oem_install_utils {
+
+HRESULT SetOemInstallState(bool is_machine) {
+  bool is_windows_installing = ConfigManager::Instance()->IsWindowsInstalling();
+  if (!is_machine || !vista_util::IsUserAdmin() || !is_windows_installing) {
+    return GOOPDATE_E_OEM_NOT_MACHINE_AND_PRIVILEGED_AND_AUDIT_MODE;
+  }
+
+  const DWORD now = Time64ToInt32(GetCurrent100NSTime());
+
+  OPT_LOG(L1, (_T("[Beginning OEM install][%u]"), now));
+
+  goopdate_utils::DeleteUserId(is_machine);
+
+  return RegKey::SetValue(
+      ConfigManager::Instance()->machine_registry_update(),
+      kRegValueOemInstallTimeSec,
+      now);
+}
+
+HRESULT ResetOemInstallState(bool is_machine) {
+  if (!is_machine) {
+    return E_INVALIDARG;
+  }
+
+  OPT_LOG(L1, (_T("[Reset OEM install state][%u]"),
+      Time64ToInt32(GetCurrent100NSTime())));
+
+  HRESULT hr = RegKey::DeleteValue(
+      ConfigManager::Instance()->machine_registry_update(),
+      kRegValueOemInstallTimeSec);
+
+  return hr;
+}
+
+// Always returns false if !is_machine. This prevents ever blocking per-user
+// instances.
+// Returns true if OEM install time is present and it has been less than
+// kMinOemModeSec since the OEM install.
+// Non-OEM installs can never be blocked from updating because OEM install time
+// will not be present.
+bool IsOemInstalling(bool is_machine) {
+  if (!is_machine) {
+    return false;
+  }
+
+  DWORD oem_install_time_seconds = 0;
+  if (FAILED(RegKey::GetValue(MACHINE_REG_UPDATE,
+                              kRegValueOemInstallTimeSec,
+                              &oem_install_time_seconds))) {
+    CORE_LOG(L3, (_T("[IsOemInstalling][OemInstallTime not found]")));
+    return false;
+  }
+
+  const uint32 now_seconds = Time64ToInt32(GetCurrent100NSTime());
+  if (now_seconds < oem_install_time_seconds) {
+    CORE_LOG(LW, (_T("[possible time warp detected][now %u][last checked %u]"),
+                  now_seconds, oem_install_time_seconds));
+  }
+  const int time_difference_seconds =
+      abs(static_cast<int>(now_seconds - oem_install_time_seconds));
+
+  ASSERT1(0 <= time_difference_seconds);
+  const bool result = time_difference_seconds < kMinOemModeSec ? true : false;
+
+  CORE_LOG(L3, (_T("[now %u][OEM install time %u][time difference %u][%d]"),
+                now_seconds, oem_install_time_seconds, time_difference_seconds,
+                result));
+  return result;
+}
+
+}  // namespace oem_install_utils
+
+}  // namespace omaha
diff --git a/common/oem_install_utils.h b/common/oem_install_utils.h
new file mode 100644
index 0000000..2ff0953
--- /dev/null
+++ b/common/oem_install_utils.h
@@ -0,0 +1,38 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_OEM_INSTALL_UTILS_H_
+#define OMAHA_COMMON_OEM_INSTALL_UTILS_H_
+
+#include <windows.h>
+
+namespace omaha {
+
+namespace oem_install_utils {
+
+// Writes OEM install beginning timestamp in the registry.
+HRESULT SetOemInstallState(bool is_machine);
+
+// Removes OEM install beginning timestamp from the registry.
+HRESULT ResetOemInstallState(bool is_machine);
+
+// Returns true if running in the context of an OEM install.
+bool IsOemInstalling(bool is_machine);
+
+}  // namespace oem_install_utils
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_OEM_INSTALL_UTILS_H_
diff --git a/common/oem_install_utils_test.cc b/common/oem_install_utils_test.cc
new file mode 100644
index 0000000..5dfde4f
--- /dev/null
+++ b/common/oem_install_utils_test.cc
@@ -0,0 +1,458 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/string.h"
+#include "omaha/base/time.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/oem_install_utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+const TCHAR* const kVistaSetupStateKey =
+    _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State");
+const TCHAR* const kXpSystemSetupKey = _T("HKLM\\System\\Setup");
+
+}  // namespace
+
+class OemInstallTest : public testing::Test {
+ protected:
+  OemInstallTest() : cm_(ConfigManager::Instance()) {
+  }
+
+  virtual void SetUp() {
+    RegKey::DeleteKey(kRegistryHiveOverrideRoot, true);
+    OverrideRegistryHives(kRegistryHiveOverrideRoot);
+  }
+
+  virtual void TearDown() {
+    RestoreRegistryHives();
+    EXPECT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
+  }
+
+  ConfigManager* cm_;
+};
+
+class AuditModeTest : public OemInstallTest {
+ protected:
+  virtual void SetUp() {
+    OemInstallTest::SetUp();
+
+    if (vista_util::IsVistaOrLater()) {
+      EXPECT_SUCCEEDED(RegKey::SetValue(kVistaSetupStateKey,
+                                        _T("ImageState"),
+                                        _T("IMAGE_STATE_UNDEPLOYABLE")));
+    } else {
+      EXPECT_SUCCEEDED(RegKey::SetValue(kXpSystemSetupKey,
+                                        _T("AuditInProgress"),
+                                        static_cast<DWORD>(1)));
+    }
+
+    EXPECT_TRUE(ConfigManager::Instance()->IsWindowsInstalling());
+  }
+};
+
+TEST_F(OemInstallTest, SetOemInstallState_User) {
+  EXPECT_EQ(GOOPDATE_E_OEM_NOT_MACHINE_AND_PRIVILEGED_AND_AUDIT_MODE,
+            oem_install_utils::SetOemInstallState(false));
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, kRegValueOemInstallTimeSec));
+  EXPECT_FALSE(
+      RegKey::HasValue(MACHINE_REG_UPDATE, kRegValueOemInstallTimeSec));
+}
+
+TEST_F(AuditModeTest, SetOemInstallState_User) {
+  EXPECT_EQ(GOOPDATE_E_OEM_NOT_MACHINE_AND_PRIVILEGED_AND_AUDIT_MODE,
+            oem_install_utils::SetOemInstallState(false));
+  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, kRegValueOemInstallTimeSec));
+  EXPECT_FALSE(
+      RegKey::HasValue(MACHINE_REG_UPDATE, kRegValueOemInstallTimeSec));
+}
+
+TEST_F(OemInstallTest, SetOemInstallState_Machine) {
+  if (!vista_util::IsUserAdmin()) {
+    std::wcout << _T("\tTest did not cover expected path because not an admin.")
+               << std::endl;
+  }
+  EXPECT_EQ(GOOPDATE_E_OEM_NOT_MACHINE_AND_PRIVILEGED_AND_AUDIT_MODE,
+            oem_install_utils::SetOemInstallState(true));
+  EXPECT_FALSE(
+      RegKey::HasValue(MACHINE_REG_UPDATE, kRegValueOemInstallTimeSec));
+}
+
+TEST_F(AuditModeTest, SetOemInstallState_Machine) {
+  if (!vista_util::IsUserAdmin()) {
+    std::wcout << _T("\tTest did not run because the user is not an admin.")
+               << std::endl;
+    return;
+  }
+  EXPECT_SUCCEEDED(oem_install_utils::SetOemInstallState(true));
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  EXPECT_TRUE(RegKey::HasValue(MACHINE_REG_UPDATE, kRegValueOemInstallTimeSec));
+  const uint32 install_time = GetDwordValue(MACHINE_REG_UPDATE,
+                                            kRegValueOemInstallTimeSec);
+  EXPECT_GE(now, install_time);
+  EXPECT_GE(static_cast<uint32>(200), now - install_time);
+
+  EXPECT_TRUE(oem_install_utils::IsOemInstalling(true));
+  EXPECT_SUCCEEDED(oem_install_utils::ResetOemInstallState(true));
+  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE,
+                                kRegValueOemInstallTimeSec));
+  EXPECT_FALSE(oem_install_utils::IsOemInstalling(true));
+}
+
+TEST_F(AuditModeTest, SetOemInstallState_Machine_NeedsElevation) {
+  if (vista_util::IsUserAdmin()) {
+    std::wcout << _T("\tTest did not run because the user IS an admin.")
+               << std::endl;
+    return;
+  }
+
+  EXPECT_EQ(GOOPDATE_E_OEM_NOT_MACHINE_AND_PRIVILEGED_AND_AUDIT_MODE,
+            oem_install_utils::SetOemInstallState(true));
+  EXPECT_FALSE(
+      RegKey::HasValue(MACHINE_REG_UPDATE, kRegValueOemInstallTimeSec));
+}
+
+//
+// IsOemInstalling tests.
+//
+
+TEST_F(OemInstallTest, IsOemInstalling_Machine_Normal) {
+  EXPECT_FALSE(oem_install_utils::IsOemInstalling(true));
+}
+
+TEST_F(OemInstallTest, IsOemInstalling_User_Normal) {
+  EXPECT_FALSE(oem_install_utils::IsOemInstalling(false));
+}
+
+TEST_F(OemInstallTest,
+       IsOemInstalling_Machine_OemInstallTimeNow_NotAuditMode) {
+  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    now_seconds));
+
+  EXPECT_TRUE(oem_install_utils::IsOemInstalling(true));
+}
+
+TEST_F(OemInstallTest, IsOemInstalling_Machine_OemInstallTimeNow_AuditMode) {
+  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    now_seconds));
+
+  if (vista_util::IsVistaOrLater()) {
+    EXPECT_SUCCEEDED(RegKey::SetValue(
+        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+        _T("ImageState"),
+        _T("IMAGE_STATE_UNDEPLOYABLE")));
+  } else {
+    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
+                                      _T("AuditInProgress"),
+                                      static_cast<DWORD>(1)));
+  }
+  EXPECT_TRUE(cm_->IsWindowsInstalling());
+
+  EXPECT_TRUE(oem_install_utils::IsOemInstalling(true));
+}
+
+TEST_F(OemInstallTest,
+       IsOemInstalling_Machine_OemInstallTime71HoursAgo_NotAuditMode) {
+  const DWORD kDesiredDifferenceSeconds = 71 * 60 * 60;  // 71 hours.
+  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
+  EXPECT_GT(now_seconds, kDesiredDifferenceSeconds);
+  const DWORD install_time_seconds = now_seconds - kDesiredDifferenceSeconds;
+  EXPECT_LT(install_time_seconds, now_seconds);
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    install_time_seconds));
+
+  EXPECT_TRUE(oem_install_utils::IsOemInstalling(true));
+}
+
+TEST_F(OemInstallTest,
+       IsOemInstalling_Machine_OemInstallTime71HoursAgo_AuditMode) {
+  const DWORD kDesiredDifferenceSeconds = 71 * 60 * 60;  // 71 hours.
+  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
+  EXPECT_GT(now_seconds, kDesiredDifferenceSeconds);
+  const DWORD install_time_seconds = now_seconds - kDesiredDifferenceSeconds;
+  EXPECT_LT(install_time_seconds, now_seconds);
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    install_time_seconds));
+
+  if (vista_util::IsVistaOrLater()) {
+    EXPECT_SUCCEEDED(RegKey::SetValue(
+        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+        _T("ImageState"),
+        _T("IMAGE_STATE_UNDEPLOYABLE")));
+  } else {
+    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
+                                      _T("AuditInProgress"),
+                                      static_cast<DWORD>(1)));
+  }
+  EXPECT_TRUE(cm_->IsWindowsInstalling());
+
+  EXPECT_TRUE(oem_install_utils::IsOemInstalling(true));
+}
+
+TEST_F(OemInstallTest,
+       IsOemInstalling_Machine_OemInstallTime73HoursAgo_NotAuditMode) {
+  const DWORD kDesiredDifferenceSeconds = 73 * 60 * 60;  // 73 hours.
+  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
+  EXPECT_GT(now_seconds, kDesiredDifferenceSeconds);
+  const DWORD install_time_seconds = now_seconds - kDesiredDifferenceSeconds;
+  EXPECT_LT(install_time_seconds, now_seconds);
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    install_time_seconds));
+
+  EXPECT_FALSE(oem_install_utils::IsOemInstalling(true));
+}
+
+TEST_F(OemInstallTest,
+       IsOemInstalling_Machine_OemInstallTime73HoursAgo_AuditMode) {
+  const DWORD kDesiredDifferenceSeconds = 73 * 60 * 60;  // 73 hours.
+  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
+  EXPECT_GT(now_seconds, kDesiredDifferenceSeconds);
+  const DWORD install_time_seconds = now_seconds - kDesiredDifferenceSeconds;
+  EXPECT_LT(install_time_seconds, now_seconds);
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    install_time_seconds));
+
+  if (vista_util::IsVistaOrLater()) {
+    EXPECT_SUCCEEDED(RegKey::SetValue(
+        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+        _T("ImageState"),
+        _T("IMAGE_STATE_UNDEPLOYABLE")));
+  } else {
+    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
+                                      _T("AuditInProgress"),
+                                      static_cast<DWORD>(1)));
+  }
+  EXPECT_TRUE(cm_->IsWindowsInstalling());
+
+  EXPECT_FALSE(oem_install_utils::IsOemInstalling(true));
+}
+
+TEST_F(OemInstallTest,
+       IsOemInstalling_Machine_OemInstallTime71HoursInFuture_NotAuditMode) {
+  const DWORD kDesiredDifferenceSeconds = 71 * 60 * 60;  // 71 hours.
+  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
+  const DWORD install_time_seconds = now_seconds + kDesiredDifferenceSeconds;
+  EXPECT_GT(install_time_seconds, now_seconds);
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    install_time_seconds));
+
+  EXPECT_TRUE(oem_install_utils::IsOemInstalling(true));
+}
+
+TEST_F(OemInstallTest,
+       IsOemInstalling_Machine_OemInstallTime71HoursInFuture_AuditMode) {
+  const DWORD kDesiredDifferenceSeconds = 71 * 60 * 60;  // 71 hours.
+  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
+  const DWORD install_time_seconds = now_seconds + kDesiredDifferenceSeconds;
+  EXPECT_GT(install_time_seconds, now_seconds);
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    install_time_seconds));
+
+  if (vista_util::IsVistaOrLater()) {
+    EXPECT_SUCCEEDED(RegKey::SetValue(
+        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+        _T("ImageState"),
+        _T("IMAGE_STATE_UNDEPLOYABLE")));
+  } else {
+    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
+                                      _T("AuditInProgress"),
+                                      static_cast<DWORD>(1)));
+  }
+  EXPECT_TRUE(cm_->IsWindowsInstalling());
+
+  EXPECT_TRUE(oem_install_utils::IsOemInstalling(true));
+}
+
+TEST_F(OemInstallTest,
+       IsOemInstalling_Machine_OemInstallTime73HoursInFuture_NotAuditMode) {
+  const DWORD kDesiredDifferenceSeconds = 73 * 60 * 60;  // 73 hours.
+  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
+  const DWORD install_time_seconds = now_seconds + kDesiredDifferenceSeconds;
+  EXPECT_GT(install_time_seconds, now_seconds);
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    install_time_seconds));
+
+  EXPECT_FALSE(oem_install_utils::IsOemInstalling(true));
+}
+
+TEST_F(OemInstallTest,
+       IsOemInstalling_Machine_OemInstallTime73HoursInFuture_AuditMode) {
+  const DWORD kDesiredDifferenceSeconds = 73 * 60 * 60;  // 73 hours.
+  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
+  const DWORD install_time_seconds = now_seconds + kDesiredDifferenceSeconds;
+  EXPECT_GT(install_time_seconds, now_seconds);
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    install_time_seconds));
+
+  if (vista_util::IsVistaOrLater()) {
+    EXPECT_SUCCEEDED(RegKey::SetValue(
+        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+        _T("ImageState"),
+        _T("IMAGE_STATE_UNDEPLOYABLE")));
+  } else {
+    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
+                                      _T("AuditInProgress"),
+                                      static_cast<DWORD>(1)));
+  }
+  EXPECT_TRUE(cm_->IsWindowsInstalling());
+
+  EXPECT_FALSE(oem_install_utils::IsOemInstalling(true));
+}
+
+TEST_F(OemInstallTest,
+       IsOemInstalling_Machine_OemInstallTimeZero_NotAuditMode) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    static_cast<DWORD>(0)));
+
+  EXPECT_FALSE(oem_install_utils::IsOemInstalling(true));
+}
+
+TEST_F(OemInstallTest,
+       IsOemInstalling_Machine_OemInstallTimeZero_AuditMode) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    static_cast<DWORD>(0)));
+
+  if (vista_util::IsVistaOrLater()) {
+    EXPECT_SUCCEEDED(RegKey::SetValue(
+        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+        _T("ImageState"),
+        _T("IMAGE_STATE_UNDEPLOYABLE")));
+  } else {
+    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
+                                      _T("AuditInProgress"),
+                                      static_cast<DWORD>(1)));
+  }
+  EXPECT_TRUE(cm_->IsWindowsInstalling());
+
+  EXPECT_FALSE(oem_install_utils::IsOemInstalling(true));
+}
+
+TEST_F(OemInstallTest,
+       IsOemInstalling_Machine_OemInstallTimeWrongType_NotAuditMode) {
+  const uint32 now_seconds = Time64ToInt32(GetCurrent100NSTime());
+  const CString now_string = itostr(now_seconds);
+  EXPECT_FALSE(now_string.IsEmpty());
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    now_string));
+
+  EXPECT_FALSE(oem_install_utils::IsOemInstalling(true));
+}
+
+TEST_F(OemInstallTest,
+       IsOemInstalling_Machine_OemInstallTimeWrongType_AuditMode) {
+  const uint32 now_seconds = Time64ToInt32(GetCurrent100NSTime());
+  const CString now_string = itostr(now_seconds);
+  EXPECT_FALSE(now_string.IsEmpty());
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    now_string));
+
+  if (vista_util::IsVistaOrLater()) {
+    EXPECT_SUCCEEDED(RegKey::SetValue(
+        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+        _T("ImageState"),
+        _T("IMAGE_STATE_UNDEPLOYABLE")));
+  } else {
+    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
+                                      _T("AuditInProgress"),
+                                      static_cast<DWORD>(1)));
+  }
+  EXPECT_TRUE(cm_->IsWindowsInstalling());
+
+  EXPECT_FALSE(oem_install_utils::IsOemInstalling(true));
+}
+
+TEST_F(OemInstallTest, IsOemInstalling_Machine_NoOemInstallTime_AuditMode) {
+  if (vista_util::IsVistaOrLater()) {
+    EXPECT_SUCCEEDED(RegKey::SetValue(
+        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+        _T("ImageState"),
+        _T("IMAGE_STATE_UNDEPLOYABLE")));
+  } else {
+    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
+                                      _T("AuditInProgress"),
+                                      static_cast<DWORD>(1)));
+  }
+  EXPECT_TRUE(cm_->IsWindowsInstalling());
+
+  EXPECT_FALSE(oem_install_utils::IsOemInstalling(true));
+}
+
+TEST_F(OemInstallTest,
+       IsOemInstalling_User_OemInstallTimeNow_NotAuditMode) {
+  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    now_seconds));
+
+  EXPECT_FALSE(oem_install_utils::IsOemInstalling(false));
+}
+
+TEST_F(OemInstallTest, IsOemInstalling_User_OemInstallTimeNow_AuditMode) {
+  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
+  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    now_seconds));
+
+  if (vista_util::IsVistaOrLater()) {
+    EXPECT_SUCCEEDED(RegKey::SetValue(
+        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+        _T("ImageState"),
+        _T("IMAGE_STATE_UNDEPLOYABLE")));
+  } else {
+    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
+                                      _T("AuditInProgress"),
+                                      static_cast<DWORD>(1)));
+  }
+  EXPECT_TRUE(cm_->IsWindowsInstalling());
+
+  EXPECT_FALSE(oem_install_utils::IsOemInstalling(false));
+}
+
+}  // namespace omaha
diff --git a/common/omaha_customization_unittest.cc b/common/omaha_customization_unittest.cc
new file mode 100644
index 0000000..c28c737
--- /dev/null
+++ b/common/omaha_customization_unittest.cc
@@ -0,0 +1,408 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Tests the constants that vary depending on the customization of Omaha.
+// The test checks for the Google Update variations, but can be modified for
+// your purposes.
+
+#include <tchar.h>
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/const_config.h"
+#include "omaha/base/const_debug.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/process.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/const_group_policy.h"
+#include "omaha/common/omaha_customization_proxy_clsid.h"
+#include "omaha/testing/omaha_customization_test.h"
+
+// TODO(omaha): Make use of EXPECT_GU_STREQ, etc.
+
+namespace omaha {
+
+TEST(OmahaCustomizationTest, Constants_BuildFiles) {
+  // Primary main.scons values.
+
+  // TODO(omaha): Most of these tests are of extremely questionable
+  // value, as they're just checking that certain #defines exist and
+  // haven't changed.  But, unanticipated changes to most of these
+  // would cause build breaks anyways!  Consider deleting them.
+#ifdef GOOGLE_UPDATE_BUILD
+  EXPECT_STREQ("Google Inc.", FULL_COMPANY_NAME_ANSI);
+  EXPECT_STREQ("Google", SHORT_COMPANY_NAME_ANSI);
+  EXPECT_STREQ("Update", PRODUCT_NAME_ANSI);
+
+  EXPECT_STREQ("google", COMPANY_DOMAIN_BASE_ANSI);
+  EXPECT_STREQ("google.com", COMPANY_DOMAIN_ANSI);
+
+  EXPECT_STREQ("Google Update", OMAHA_APP_NAME_ANSI);
+#endif  // GOOGLE_UPDATE_BUILD
+
+  EXPECT_STREQ("goopdate", MAIN_DLL_BASE_NAME_ANSI);
+
+  const GUID kActualProxyClsidIsMachineGuid = PROXY_CLSID_IS_MACHINE;
+  EXPECT_TRUE(::IsEqualGUID(kProxyClsidIsMachineGuid,
+                            kActualProxyClsidIsMachineGuid));
+
+  const GUID kActualProxyClsidIsUserGuid = PROXY_CLSID_IS_USER;
+  EXPECT_TRUE(::IsEqualGUID(kProxyClsidIsUserGuid,
+                            kActualProxyClsidIsUserGuid));
+
+  // VERSION file values. Only the relatively stable ones are tested.
+  // The versions may or may not match in non-Google Update builds.
+#ifdef GOOGLE_UPDATE_BUILD
+  EXPECT_STREQ("9", ONECLICK_PLUGIN_VERSION_ANSI);
+  // TODO(omaha): Change the name to ANSI.
+  EXPECT_STREQ("3", UPDATE_PLUGIN_VERSION_ANSI);
+#else
+  std::wcout << _T("Did not test version values.") << std::endl;
+#endif
+
+  // Primary omaha_version_utils values.
+  EXPECT_STREQ(_T("npGoogleOneClick"), ONECLICK_PLUGIN_NAME);
+  EXPECT_STREQ(_T("npGoogleUpdate"), UPDATE_PLUGIN_NAME);
+  EXPECT_STREQ(_T("GoopdateBho"), BHO_NAME);
+
+  // Filenames from omaha_version_utils.
+  EXPECT_STREQ(
+      _T("npGoogleOneClick") _T(ONECLICK_PLUGIN_VERSION_ANSI) _T(".dll"),
+      ONECLICK_PLUGIN_FILENAME);
+  EXPECT_STREQ(_T("npGoogleUpdate") _T(UPDATE_PLUGIN_VERSION_ANSI) _T(".dll"),
+               UPDATE_PLUGIN_FILENAME);
+  EXPECT_STREQ(_T("GoopdateBho.dll"), BHO_FILENAME);
+}
+
+TEST(OmahaCustomizationTest, Constants_Names) {
+  // Company and product names.
+
+  // TODO(omaha): Most of these tests are of extremely questionable
+  // value, as they're just checking that certain #defines exist and
+  // haven't changed.  But, unanticipated changes to most of these
+  // would cause build breaks anyways!  Consider deleting them.
+#ifdef GOOGLE_UPDATE_BUILD
+  EXPECT_STREQ(_T("Google Inc."), kFullCompanyName);
+  EXPECT_STREQ(_T("Google"), SHORT_COMPANY_NAME);
+  EXPECT_STREQ(_T("Google"), kShortCompanyName);
+  EXPECT_STREQ(_T("Update"), PRODUCT_NAME);
+
+  EXPECT_STREQ(_T("google.com"), COMPANY_DOMAIN);
+
+  // Full app name.
+  EXPECT_STREQ(_T("Google Update"), kAppName);
+
+  // Identifiers.
+  EXPECT_STREQ(_T("Google"), COMPANY_NAME_IDENTIFIER);
+  EXPECT_STREQ(_T("Update"), PRODUCT_NAME_IDENTIFIER);
+  EXPECT_STREQ(_T("GoogleUpdate"), APP_NAME_IDENTIFIER);
+
+  // Other values based on the app name.
+  EXPECT_STREQ(_T("_Google_Update_"), kLockPrefix);
+#endif  // GOOGLE_UPDATE_BUILD
+
+  // Filename bases
+  EXPECT_STREQ(_T("GoogleUpdate"), MAIN_EXE_BASE_NAME);
+  EXPECT_STREQ(_T("goopdate"), MAIN_DLL_BASE_NAME);
+}
+
+TEST(OmahaCustomizationTest, Constants_Filenames) {
+  EXPECT_STREQ(_T("GoogleUpdate.exe"), kOmahaShellFileName);
+  EXPECT_STREQ(_T("GoogleCrashHandler.exe"), kCrashHandlerFileName);
+  EXPECT_STREQ(_T("goopdate.dll"), kOmahaDllName);
+  EXPECT_STREQ(_T("goopdateres_%s.dll"), kOmahaResourceDllNameFormat);
+  EXPECT_STREQ(_T("GoogleUpdateBroker.exe"), kOmahaBrokerFileName);
+  EXPECT_STREQ(_T("GoogleUpdateOnDemand.exe"), kOmahaOnDemandFileName);
+  EXPECT_STREQ(_T("psmachine.dll"), kPSFileNameMachine);
+  EXPECT_STREQ(_T("psuser.dll"), kPSFileNameUser);
+}
+
+TEST(OmahaCustomizationTest, Constants_Certificate) {
+  EXPECT_STREQ(_T("Google Inc"), kCertificateSubjectName);
+}
+
+TEST(OmahaCustomizationTest, Constants_OmahaAppId_String) {
+  EXPECT_STREQ(_T("{430FD4D0-B729-4F61-AA34-91526481799D}"), GOOPDATE_APP_ID);
+  EXPECT_STREQ(_T("{430FD4D0-B729-4F61-AA34-91526481799D}"),
+               kGoogleUpdateAppId);
+}
+
+TEST(OmahaCustomizationTest, Constants_OmahaAppId_GUID) {
+  const GUID kExpectedGoogleUpdateGuid =
+      {0x430FD4D0, 0xB729, 0x4F61,
+       {0xAA, 0x34, 0x91, 0x52, 0x64, 0x81, 0x79, 0x9D}};
+  EXPECT_TRUE(::IsEqualGUID(kExpectedGoogleUpdateGuid, kGoopdateGuid));
+  EXPECT_STREQ(_T("{430FD4D0-B729-4F61-AA34-91526481799D}"),
+               GuidToString(kGoopdateGuid));
+}
+
+TEST(OmahaCustomizationTest, Constants_OmahaAppId_GUIDAndStringMatch) {
+  EXPECT_STREQ(kGoogleUpdateAppId, GuidToString(kGoopdateGuid));
+}
+
+TEST(OmahaCustomizationTest, Constants_Directories) {
+  EXPECT_STREQ(_T("Offline"), OFFLINE_DIR_NAME);
+  EXPECT_GU_STREQ(_T("Google"), OMAHA_REL_COMPANY_DIR);
+  EXPECT_GU_STREQ(_T("Google\\CrashReports"), OMAHA_REL_CRASH_DIR);
+  EXPECT_GU_STREQ(_T("Google\\Update"), OMAHA_REL_GOOPDATE_INSTALL_DIR);
+  EXPECT_GU_STREQ(_T("Google\\Update\\Log"), OMAHA_REL_LOG_DIR);
+  EXPECT_GU_STREQ(_T("Google\\Update\\Offline"),
+                  OMAHA_REL_OFFLINE_STORAGE_DIR);
+  EXPECT_GU_STREQ(_T("Google\\Update\\Download"),
+                  OMAHA_REL_DOWNLOAD_STORAGE_DIR);
+  EXPECT_GU_STREQ(_T("Google\\Update\\Install"),
+                  OMAHA_REL_INSTALL_WORKING_DIR);
+}
+
+TEST(OmahaCustomizationTest, Constants_RegistryKeys_NotCustomized) {
+  EXPECT_STREQ(_T("HKLM"), MACHINE_KEY_NAME);
+  EXPECT_STREQ(_T("HKLM\\"), MACHINE_KEY);
+  EXPECT_STREQ(_T("HKCU"), USER_KEY_NAME);
+  EXPECT_STREQ(_T("HKCU\\"), USER_KEY);
+  EXPECT_STREQ(_T("HKU\\"), USERS_KEY);
+}
+
+TEST(OmahaCustomizationTest, Constants_RegistryKeys) {
+  EXPECT_GU_STREQ(_T("Software\\Google\\"), COMPANY_MAIN_KEY);
+  EXPECT_GU_STREQ(_T("Software\\Google\\Update\\"), GOOPDATE_MAIN_KEY);
+  EXPECT_GU_STREQ(_T("Software\\Google\\Update\\Clients\\"), GOOPDATE_REG_RELATIVE_CLIENTS);  // NOLINT
+  EXPECT_GU_STREQ(_T("Software\\Google\\Update\\ClientState\\"), GOOPDATE_REG_RELATIVE_CLIENT_STATE);  // NOLINT
+  EXPECT_GU_STREQ(_T("Software\\Google\\Update\\ClientStateMedium\\"), GOOPDATE_REG_RELATIVE_CLIENT_STATE_MEDIUM);  // NOLINT
+  EXPECT_GU_STREQ(_T("Software\\Policies\\Google\\"), COMPANY_POLICIES_MAIN_KEY);
+  EXPECT_GU_STREQ(_T("Software\\Policies\\Google\\Update\\"), GOOPDATE_POLICIES_RELATIVE);  // NOLINT
+
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\"), USER_REG_GOOGLE);
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\"), USER_REG_UPDATE);
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\Clients\\"), USER_REG_CLIENTS);  // NOLINT
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\Clients\\{430FD4D0-B729-4F61-AA34-91526481799D}"), USER_REG_CLIENTS_GOOPDATE);  // NOLINT
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\ClientState\\"), USER_REG_CLIENT_STATE);  // NOLINT
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\ClientState\\{430FD4D0-B729-4F61-AA34-91526481799D}"), USER_REG_CLIENT_STATE_GOOPDATE);  // NOLINT
+
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\"), MACHINE_REG_GOOGLE);
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\"), MACHINE_REG_UPDATE);
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\Clients\\"), MACHINE_REG_CLIENTS);  // NOLINT
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\Clients\\{430FD4D0-B729-4F61-AA34-91526481799D}"), MACHINE_REG_CLIENTS_GOOPDATE);  // NOLINT
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientState\\"), MACHINE_REG_CLIENT_STATE);  // NOLINT
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientState\\{430FD4D0-B729-4F61-AA34-91526481799D}"), MACHINE_REG_CLIENT_STATE_GOOPDATE);  // NOLINT
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientStateMedium\\"), MACHINE_REG_CLIENT_STATE_MEDIUM);  // NOLINT
+
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\UpdateDev\\"), MACHINE_REG_UPDATE_DEV);  // NOLINT
+}
+
+TEST(OmahaCustomizationTest, Constants_RegistryKeys_GroupPolicy) {
+  EXPECT_GU_STREQ(_T("Software\\Policies\\Google\\Update\\"), GOOPDATE_POLICIES_RELATIVE);  // NOLINT
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Policies\\Google\\Update\\"), kRegKeyGoopdateGroupPolicy);  // NOLINT
+}
+
+TEST(OmahaCustomizationTest, Constants_RegistryValues) {
+  EXPECT_GU_STREQ(_T("Google Update"), kRunValueName);
+}
+
+TEST(OmahaCustomizationTest, Constants_MsiMsp) {
+  EXPECT_STREQ(_T("GoogleUpdateHelper.msi"), kHelperInstallerName);
+  EXPECT_STREQ(_T("{A92DAB39-4E2C-4304-9AB6-BC44E68B55E2}"),
+               kHelperInstallerProductGuid);
+  EXPECT_STREQ(_T("GoogleUpdateHelperPatch.msp"), kHelperPatchName);
+  EXPECT_STREQ(_T("{E0D0D2C9-5836-4023-AB1D-54EC3B90AD03}"), kHelperPatchGuid);
+}
+
+TEST(OmahaCustomizationTest, Constants_CompatibleShellVersions) {
+  EXPECT_EQ(2, arraysize(kCompatibleOlderShellVersions));
+  EXPECT_EQ(0x0001000200830007, kCompatibleOlderShellVersions[0]);
+  EXPECT_EQ(0x0001000200B70009, kCompatibleOlderShellVersions[1]);
+}
+
+TEST(OmahaCustomizationTest, Constants_BrandCode) {
+  EXPECT_STREQ(_T("GGLS"), kDefaultGoogleUpdateBrandCode);
+}
+
+TEST(OmahaCustomizationTest, Constants_Addresses) {
+  EXPECT_STREQ(_T("www.google.com"), kGoogleHttpServer);
+  EXPECT_STREQ(_T("tools.google.com"), kGoopdateServer);
+  EXPECT_STREQ(_T("https://tools.google.com/service/update2"), kUrlUpdateCheck);
+  EXPECT_STREQ(_T("http://tools.google.com/service/update2"), kUrlPing);
+  EXPECT_STREQ(_T("http://clients2.google.com/cr/report"), kUrlCrashReport);
+  EXPECT_STREQ(_T("http://www.google.com/support/installer/?"), kUrlMoreInfo);
+  EXPECT_STREQ(_T("http://cr-tools.clients.google.com/service/check2"),
+               kUrlCodeRedCheck);
+  EXPECT_STREQ(_T("http://clients5.google.com/tbproxy/usagestats"),
+               kUrlUsageStatsReport);
+}
+
+TEST(OmahaCustomizationTest, Constants_Config) {
+  EXPECT_GU_STREQ(_T("Software\\Google\\Update\\Shared"), kCiRegKeyShared);
+}
+
+TEST(OmahaCustomizationTest, Constants_Debug) {
+  EXPECT_GU_STREQ(_T("GoogleUpdate-debug"), kCiDebugDirectory);
+}
+
+TEST(OmahaCustomizationTest, Constants_Logging) {
+  EXPECT_STREQ(_T("GoogleUpdate.ini"), kLogConfigFileName);
+  EXPECT_STREQ(_T("GoogleUpdate.log"), kDefaultLogFileName);
+}
+
+// These should not change during customization.
+TEST(OmahaCustomizationTest, Constants_ObjectNames_Prefixes) {
+  EXPECT_GU_STREQ(_T("Global\\G"), kGlobalPrefix);
+}
+
+TEST(OmahaCustomizationTest, Constants_ObjectNames_Pipes) {
+  EXPECT_GU_STREQ(_T("\\\\.\\pipe\\GoogleCrashServices"), kCrashPipeNamePrefix);
+}
+
+TEST(OmahaCustomizationTest, Constants_ObjectNames_MutexesAndEvents) {
+  EXPECT_STREQ(_T("{A9A86B93-B54E-4570-BE89-42418507707B}"), kSetupMutex);
+  EXPECT_STREQ(_T("{A0C1F415-D2CE-4ddc-9B48-14E56FD55162}"), kShutdownEvent);
+  EXPECT_STREQ(_T("{B5665124-2B19-40e2-A7BC-B44321E72C4B}"),
+               kCoreSingleInstance);
+  EXPECT_STREQ(_T("{C4F406E5-F024-4e3f-89A7-D5AB7663C3CD}"),
+               kCrashHandlerSingleInstance);
+  EXPECT_STREQ(_T("{D0BB2EF1-C183-4cdb-B218-040922092869}"),
+               kUpdateAppsSingleInstance);
+  EXPECT_STREQ(_T("%s-{F707E94F-D66B-4525-AD84-B1DA87D6A971}"),
+               kInstallAppSingleInstance);
+  EXPECT_STREQ(_T("{0A175FBE-AEEC-4fea-855A-2AA549A88846}"),
+               kInstallManagerSerializer);
+  EXPECT_STREQ(_T("{C68009EA-1163-4498-8E93-D5C4E317D8CE}"),
+               kMetricsSerializer);
+  EXPECT_STREQ(_T("{0E900C7B-04B0-47f9-81B0-F8D94F2DF01B}"),
+               kNetworkConfigLock);
+  EXPECT_STREQ(_T("{66CC0160-ABB3-4066-AE47-1CA6AD5065C8}"),
+               kRegistryAccessMutex);
+}
+
+TEST(OmahaCustomizationTest, Constants_ObjectNames_SharedMemory) {
+  EXPECT_GU_STREQ(_T("Global\\GoogleUpdate3"),
+                  kGoogleUpdate3SharedMemoryName);
+  EXPECT_GU_STREQ(_T("Global\\GoogleUpdateCore"),
+                  kGoogleUpdateCoreSharedMemoryName);
+}
+
+TEST(OmahaCustomizationTest, Constants_Services) {
+  EXPECT_GU_STREQ(_T("gupdate_service_name"), kRegValueServiceName);
+  EXPECT_GU_STREQ(_T("gupdatem_service_name"), kRegValueMediumServiceName);
+  EXPECT_GU_STREQ(_T("gupdate_task_name_c"), kRegValueTaskNameC);
+  EXPECT_GU_STREQ(_T("gupdate_task_name_ua"), kRegValueTaskNameUA);
+
+  EXPECT_GU_STREQ(_T("gupdate"), kServicePrefix);
+  EXPECT_GU_STREQ(_T("gupdatem"), kMediumServicePrefix);
+
+  EXPECT_STREQ(_T("GoogleUpdate.exe"), kServiceFileName);
+}
+
+TEST(OmahaCustomizationTest, Constants_ScheduledTasks) {
+  EXPECT_GU_STREQ(_T("GoogleUpdateTaskUser"), kScheduledTaskNameUserPrefix);
+  EXPECT_GU_STREQ(_T("GoogleUpdateTaskMachine"), kScheduledTaskNameMachinePrefix);
+}
+
+TEST(OmahaCustomizationTest, Constants_Plugins) {
+  EXPECT_GU_STREQ(_T("Google.OneClickCtrl.") _T(ONECLICK_PLUGIN_VERSION_ANSI),
+                  kOneClickProgId);
+  EXPECT_STREQ(
+      "application/x-vnd.google.oneclickctrl." ONECLICK_PLUGIN_VERSION_ANSI,
+      kOneClickPluginMimeTypeAnsi);
+}
+
+TEST(OmahaCustomizationTest, Constants_HostCheck) {
+  EXPECT_EQ(4, arraysize(kSiteLockPatternStrings));
+  EXPECT_STREQ(_T("^(gears)|(mail)|(tools)|(www)|(desktop)|(pack)\\.google\\.com$"), kSiteLockPatternStrings[0]);  // NOLINT
+  EXPECT_STREQ(_T("^www\\.google\\.(ad)|(bg)|(ca)|(cn)|(cz)|(de)|(es)|(fi)|(fr)|(gr)|(hr)|(hu)|(it)|(ki)|(kr)|(lt)|(lv)|(nl)|(no)|(pl)|(pt)|(ro)|(ru)|(sk)|(sg)|(sl)|(sr)|(vn)$"), kSiteLockPatternStrings[1]);  // NOLINT
+  EXPECT_STREQ(_T("^www\\.google\\.co\\.(hu)|(id)|(il)|(it)|(jp)|(kr)|(th)|(uk)$"), kSiteLockPatternStrings[2]);  // NOLINT
+  EXPECT_STREQ(_T("^www\\.google\\.com\\.(ar)|(au)|(br)|(cn)|(et)|(gr)|(hr)|(ki)|(lv)|(om)|(pl)|(pt)|(ru)|(sg)|(sv)|(tr)|(vn)$"), kSiteLockPatternStrings[3]);  // NOLINT
+}
+
+//
+// ConfigManager keys.
+//
+
+TEST(OmahaCustomizationTest, ConfigManager_RegistryKeys) {
+  const ConfigManager& cm = *ConfigManager::Instance();
+
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\Clients\\"), cm.user_registry_clients());  // NOLINT
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\Clients\\"), cm.machine_registry_clients());  // NOLINT
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\Clients\\"), cm.registry_clients(false));  // NOLINT
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\Clients\\"), cm.registry_clients(true));  // NOLINT
+
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\Clients\\{430FD4D0-B729-4F61-AA34-91526481799D}"), cm.user_registry_clients_goopdate());  // NOLINT
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\Clients\\{430FD4D0-B729-4F61-AA34-91526481799D}"), cm.machine_registry_clients_goopdate());  // NOLINT
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\Clients\\{430FD4D0-B729-4F61-AA34-91526481799D}"), cm.registry_clients_goopdate(false));  // NOLINT
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\Clients\\{430FD4D0-B729-4F61-AA34-91526481799D}"), cm.registry_clients_goopdate(true));  // NOLINT
+
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\ClientState\\"), cm.user_registry_client_state());  // NOLINT
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientState\\"), cm.machine_registry_client_state());  // NOLINT
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\ClientState\\"), cm.registry_client_state(false));  // NOLINT
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientState\\"), cm.registry_client_state(true));  // NOLINT
+
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\ClientState\\{430FD4D0-B729-4F61-AA34-91526481799D}"), cm.user_registry_client_state_goopdate());  // NOLINT
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientState\\{430FD4D0-B729-4F61-AA34-91526481799D}"), cm.machine_registry_client_state_goopdate());  // NOLINT
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\ClientState\\{430FD4D0-B729-4F61-AA34-91526481799D}"), cm.registry_client_state_goopdate(false));  // NOLINT
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientState\\{430FD4D0-B729-4F61-AA34-91526481799D}"), cm.registry_client_state_goopdate(true));  // NOLINT
+
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientStateMedium\\"), cm.machine_registry_client_state_medium());  // NOLINT
+
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\"), cm.user_registry_update());  // NOLINT
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\"), cm.machine_registry_update());  // NOLINT
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\Update\\"), cm.registry_update(false));  // NOLINT
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\Update\\"), cm.registry_update(true));  // NOLINT
+
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\"), cm.user_registry_google());
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\"), cm.machine_registry_google());
+  EXPECT_GU_STREQ(_T("HKCU\\Software\\Google\\"), cm.registry_google(false));
+  EXPECT_GU_STREQ(_T("HKLM\\Software\\Google\\"), cm.registry_google(true));
+}
+
+TEST(OmahaCustomizationTest, IsInternalUser) {
+  if (IsBuildSystem()) {
+  // The build system is not configured the same.
+  // This may or may not be true in non-Google Update builds.
+#ifdef GOOGLE_UPDATE_BUILD
+    EXPECT_FALSE(ConfigManager::Instance()->IsInternalUser());
+#else
+  std::wcout << _T("Did not test IsInternalUser.") << std::endl;
+#endif
+  } else {
+    EXPECT_TRUE(ConfigManager::Instance()->IsInternalUser());
+  }
+}
+
+//
+// Test helpers.
+//
+
+TEST(OmahaCustomizationTest, GetGoogleUserPath) {
+  EXPECT_STREQ(GetLocalAppDataPath() + SHORT_COMPANY_NAME + _T("\\"),
+               GetGoogleUserPath());
+}
+
+TEST(OmahaCustomizationTest, GetGoogleUpdateUserPath) {
+  EXPECT_STREQ(GetLocalAppDataPath() + SHORT_COMPANY_NAME + _T("\\")
+                                     + PRODUCT_NAME + _T("\\"),
+               GetGoogleUpdateUserPath());
+}
+
+// Assumes Program Files is in the normal location.
+TEST(OmahaCustomizationTest, GetGoogleUpdateMachinePath) {
+  CString expected_machine_path;
+  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES | CSIDL_FLAG_DONT_VERIFY,
+                                 &expected_machine_path));
+  expected_machine_path.Append(_T("\\") SHORT_COMPANY_NAME
+                               _T("\\") PRODUCT_NAME);
+  EXPECT_STREQ(expected_machine_path, GetGoogleUpdateMachinePath());
+}
+
+}  // namespace omaha
diff --git a/common/omaha_version.cc b/common/omaha_version.cc
deleted file mode 100644
index 05b8611..0000000
--- a/common/omaha_version.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/file_ver.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/utils.h"
-
-namespace {
-
-// The version string is not visible outside this module and it is only
-// accessible through the omaha::GetVersionString accessor.
-CString version_string;
-
-ULONGLONG omaha_version = 0;
-
-}  // namespace
-
-namespace omaha {
-
-// In both GetVersion* methods, we assert only that InitializeVersion* was
-// already called and not that they weren't called twice, which is OK.
-// There is no detection that the version_ variables aren't accessed before
-// being initialized because this would require accessor methods to enforce and
-// lead to bloat.
-
-const TCHAR* GetVersionString() {
-  ASSERT1(!version_string.IsEmpty());
-  return version_string;
-}
-
-ULONGLONG GetVersion() {
-  ASSERT1(!version_string.IsEmpty());
-  return omaha_version;
-}
-
-void InitializeVersionFromModule(HINSTANCE instance) {
-  ULONGLONG module_version = app_util::GetVersionFromModule(instance);
-
-  InitializeVersion(module_version);
-}
-
-void InitializeVersion(ULONGLONG version) {
-  omaha_version = version;
-
-  version_string = StringFromVersion(omaha_version);
-}
-
-}  // namespace omaha
diff --git a/common/omaha_version_unittest.cc b/common/omaha_version_unittest.cc
deleted file mode 100644
index ec6e4ad..0000000
--- a/common/omaha_version_unittest.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/utils.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(OmahaVersionTest, InitializeVersion) {
-  CString version_string = GetVersionString();
-  ULONGLONG version = GetVersion();
-
-  EXPECT_STREQ(OMAHA_BUILD_VERSION_STRING, version_string);
-  EXPECT_EQ(OMAHA_BUILD_VERSION, version);
-
-  InitializeVersion(MAKEDLLVERULL(0, 0, 0, 0));
-  EXPECT_STREQ(_T("0.0.0.0"), GetVersionString());
-  EXPECT_EQ(0, GetVersion());
-
-  InitializeVersion(MAKEDLLVERULL(1, 2, 3, 4));
-  EXPECT_STREQ(_T("1.2.3.4"), GetVersionString());
-  EXPECT_EQ(0x0001000200030004, GetVersion());
-
-  InitializeVersion(MAKEDLLVERULL(0x7fff, 0x7fff, 0x7fff, 0x7fff));
-  EXPECT_STREQ(_T("32767.32767.32767.32767"), GetVersionString());
-  EXPECT_EQ(0x7fff7fff7fff7fff, GetVersion());
-
-  InitializeVersion(MAKEDLLVERULL(0xffff, 0xffff, 0xffff, 0xffff));
-  EXPECT_STREQ(_T("65535.65535.65535.65535"), GetVersionString());
-  EXPECT_EQ(0xffffffffffffffff, GetVersion());
-
-  // Sets back the initial version.
-  InitializeVersion(VersionFromString(version_string));
-  EXPECT_STREQ(version_string, GetVersionString());
-  EXPECT_EQ(version, GetVersion());
-}
-
-}  // namespace omaha
diff --git a/common/path.cc b/common/path.cc
deleted file mode 100644
index 92cf7a6..0000000
--- a/common/path.cc
+++ /dev/null
@@ -1,448 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Path utility functions.
-
-#include "omaha/common/path.h"
-
-#include <atlbase.h>
-#include <atlstr.h>
-#include <atlpath.h>
-#include <map>
-#include <vector>
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/shell.h"
-#include "omaha/common/string.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-const TCHAR* const kRegSvr32Cmd1 = _T("regsvr32 ");
-const TCHAR* const kRegSvr32Cmd2 = _T("regsvr32.exe ");
-const TCHAR* const kRunDll32Cmd1 = _T("rundll32 ");
-const TCHAR* const kRunDll32Cmd2 = _T("rundll32.exe ");
-const TCHAR* const kMsiExecCmd1  = _T("msiexec ");
-const TCHAR* const kMsiExecCmd2  = _T("msiexec.exe ");
-const TCHAR* const kDotExe       = _T(".exe");
-
-
-namespace detail {
-
-typedef bool (*Filter)(const WIN32_FIND_DATA&);
-
-bool IsFile(const WIN32_FIND_DATA& find_data) {
-  return (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
-}
-
-bool IsDirectory(const WIN32_FIND_DATA& find_data) {
-  return (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
-}
-
-bool AllFiles(const WIN32_FIND_DATA&) {
-  return true;
-}
-
-HRESULT FindFilesEx(const CString& dir,
-                    const CString& pattern,
-                    std::vector<CString>* files,
-                    Filter func) {
-  ASSERT1(files);
-
-  files->clear();
-  if (!File::Exists(dir)) {
-    return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
-  }
-
-  CString files_to_find = ConcatenatePath(dir, pattern);
-  WIN32_FIND_DATA find_data = {0};
-  scoped_hfind hfind(::FindFirstFile(files_to_find, &find_data));
-  if (!hfind) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(L5, (_T("[File::GetWildcards - FindFirstFile failed][0x%x]"), hr));
-    return hr;
-  }
-
-  do {
-    if (func(find_data)) {
-      files->push_back(find_data.cFileName);
-    }
-  } while (::FindNextFile(get(hfind), &find_data));
-
-  return S_OK;
-}
-
-}  // namespace detail.
-
-// Get the starting path from the command string
-CString GetStartingPathFromString(const CString& s) {
-  CString path;
-  CString str(s);
-  TrimCString(str);
-
-  int len = str.GetLength();
-  if (len > 0) {
-    if (str[0] == _T('"')) {
-      // For something like:  "c:\Program Files\...\" ...
-      int idx = String_FindChar(str.GetString() + 1, _T('"'));
-      if (idx != -1)
-        path.SetString(str.GetString() + 1, idx);
-    } else {
-      // For something like:  c:\PRGRA~1\... ...
-      int idx = String_FindChar(str, _T(' '));
-      path.SetString(str, idx == -1 ? len : idx);
-    }
-  }
-
-  return path;
-}
-
-// Get the trailing path from the command string
-CString GetTrailingPathFromString(const CString& s) {
-  CString path;
-  CString str(s);
-  TrimCString(str);
-
-  int len = str.GetLength();
-  if (len > 0) {
-    if (str[len - 1] == _T('"')) {
-      // For something like:  regsvr32 /u /s "c:\Program Files\..."
-      str.Truncate(len - 1);
-      int idx = String_ReverseFindChar(str, _T('"'));
-      if (idx != -1)
-        path.SetString(str.GetString() + idx + 1, len - idx - 1);
-    } else {
-      // For something like:  regsvr32 /u /s c:\PRGRA~1\...
-      int idx = String_ReverseFindChar(str, _T(' '));
-      if (idx != -1)
-        path.SetString(str.GetString() + idx + 1, len - idx - 1);
-    }
-  }
-
-  return path;
-}
-
-// Get the file from the command string
-HRESULT GetFileFromCommandString(const TCHAR* s, CString* file) {
-  ASSERT1(file);
-
-  if (!s || !*s) {
-    return E_INVALIDARG;
-  }
-
-  CString str(s);
-  TrimCString(str);
-
-  // Handle the string starting with quotation mark
-  // For example: "C:\Program Files\WinZip\WINZIP32.EXE" /uninstall
-  if (str[0] == _T('"')) {
-    int idx_quote = str.Find(_T('"'), 1);
-    if (idx_quote != -1) {
-      file->SetString(str.GetString() + 1, idx_quote - 1);
-      return S_OK;
-    } else {
-      return E_FAIL;
-    }
-  }
-
-  // Handle the string starting with "regsvr32"
-  // For example: regsvr32 /u /s "c:\program files\google\googletoolbar3.dll"
-  if (String_StartsWith(str, kRegSvr32Cmd1, true) ||
-      String_StartsWith(str, kRegSvr32Cmd2, true)) {
-    file->SetString(GetTrailingPathFromString(str));
-    return S_OK;
-  }
-
-  // Handle the string starting with "rundll32"
-  // For example: "rundll32.exe setupapi.dll,InstallHinfSection DefaultUninstall 132 C:\WINDOWS\INF\PCHealth.inf"  // NOLINT
-  if (String_StartsWith(str, kRunDll32Cmd1, true) ||
-      String_StartsWith(str, kRunDll32Cmd2, true)) {
-    int idx_space = str.Find(_T(' '));
-    ASSERT1(idx_space != -1);
-    int idx_comma = str.Find(_T(','), idx_space + 1);
-    if (idx_comma != -1) {
-      file->SetString(str.GetString() + idx_space + 1,
-                      idx_comma - idx_space - 1);
-      TrimCString(*file);
-      return S_OK;
-    } else {
-      return E_FAIL;
-    }
-  }
-
-  // Handle the string starting with "msiexec"
-  // For example: MsiExec.exe /I{25A13826-8E4A-4FBF-AD2B-776447FE9646}
-  if (String_StartsWith(str, kMsiExecCmd1, true) ||
-      String_StartsWith(str, kMsiExecCmd2, true)) {
-    return E_FAIL;
-  }
-
-  // Otherwise, try to find the file till reaching ".exe"
-  // For example: "C:\Program Files\Google\Google Desktop Search\GoogleDesktopSetup.exe -uninstall"  // NOLINT
-  for (int i = 0; i < str.GetLength(); ++i) {
-    if (String_StartsWith(str.GetString() + i, kDotExe, true)) {
-      file->SetString(str, i + _tcslen(kDotExe));
-      return S_OK;
-    }
-  }
-
-  // As last resort, return the part from the beginning to first space found.
-  int idx = str.Find(_T(' '));
-  if (idx == -1) {
-    file->SetString(str);
-  } else {
-    file->SetString(str, idx);
-  }
-
-  return S_OK;
-}
-
-// Expands the string with embedded special folder variables.
-// TODO(omaha): This function seems to have a very specific purpose, which
-// is not used in our code base. Consider removing it.
-HRESULT ExpandStringWithSpecialFolders(CString* str) {
-  ASSERT(str, (L""));
-
-#pragma warning(push)
-// construction of local static object is not thread-safe
-#pragma warning(disable : 4640)
-  static std::map<CString, CString> g_special_folders_mapping;
-#pragma warning(pop)
-
-  if (g_special_folders_mapping.size() == 0) {
-    RET_IF_FAILED(
-        Shell::GetSpecialFolderKeywordsMapping(&g_special_folders_mapping));
-  }
-
-  CString expanded_str;
-  RET_IF_FAILED(
-      ExpandEnvLikeStrings(*str, g_special_folders_mapping, &expanded_str));
-
-  str->SetString(expanded_str);
-
-  return S_OK;
-}
-
-// Internal helper method for normalizing a path
-HRESULT NormalizePathInternal(const TCHAR* path, CString* normalized_path) {
-  // We use '|' to separate fields
-  CString field;
-  int bar_idx = String_FindChar(path, _T('|'));
-  if (bar_idx == -1)
-    field = path;
-  else
-    field.SetString(path, bar_idx);
-
-  if (IsRegistryPath(field)) {
-    CString key_name, value_name;
-    RET_IF_FAILED(RegSplitKeyvalueName(field, &key_name, &value_name));
-
-    CString reg_value;
-    RET_IF_FAILED(RegKey::GetValue(key_name, value_name, &reg_value));
-    normalized_path->Append(reg_value);
-  } else {
-    RET_IF_FAILED(ExpandStringWithSpecialFolders(&field));
-    normalized_path->Append(field);
-  }
-
-  if (bar_idx != -1)
-    return NormalizePathInternal(path + bar_idx + 1, normalized_path);
-  else
-    return S_OK;
-}
-
-// Normalize a path
-HRESULT NormalizePath(const TCHAR* path, CString* normalized_path) {
-  ASSERT1(normalized_path);
-
-  normalized_path->Empty();
-
-  if (path) {
-    HRESULT hr = NormalizePathInternal(path, normalized_path);
-    if (FAILED(hr)) {
-      normalized_path->Empty();
-      UTIL_LOG(LE, (_T("[NormalizePath - unable to normalize path][%s][0x%x]"),
-                    path, hr));
-    }
-    return hr;
-  } else {
-    return S_OK;
-  }
-}
-
-CString ConcatenatePath(const CString& path1, const CString& path2) {
-  CString ret(path1);
-
-  // Append the file path using the PathAppend.
-  VERIFY1(::PathAppend(CStrBuf(ret, MAX_PATH), path2));
-
-  return ret;
-}
-
-// Get the filename from the path
-// "C:\TEST\sample.txt" returns "sample.txt"
-CString GetFileFromPath(const CString& path) {
-  CPath path1(path);
-  path1.StripPath();
-  return static_cast<CString>(path1);
-}
-
-// Get the directory from the path
-// "C:\TEST\sample.txt" returns "C:\TEST"
-// Get the directory out of the file path
-CString GetDirectoryFromPath(const CString& path) {
-  CPath path1(path);
-  path1.RemoveFileSpec();
-  return static_cast<CString>(path1);
-}
-
-// Remove the extension from the path.
-// "C:\TEST\sample.txt" returns "C:\TEST\sample"
-CString GetPathRemoveExtension(const CString& path) {
-  CPath path1(path);
-  path1.RemoveExtension();
-  return static_cast<CString>(path1);
-}
-
-// Basically, an absolute path starts with X:\ or \\ (a UNC name)
-bool IsAbsolutePath(const TCHAR* path) {
-  ASSERT1(path);
-
-  int len = ::_tcslen(path);
-  if (len < 3)
-    return false;
-  if (*path == _T('"'))
-    path++;
-  if (String_StartsWith(path+1, _T(":\\"), false))
-    return true;
-  if (String_StartsWith(path, _T("\\\\"), false))
-    return true;
-  return false;
-}
-
-void EnclosePath(CString* path) {
-  ASSERT1(path);
-
-  if (path->IsEmpty()) {
-    return;
-  }
-
-  bool starts_with_quote = (_T('"') == path->GetAt(0));
-  bool ends_with_quote = (_T('"') == path->GetAt(path->GetLength() - 1));
-  ASSERT(starts_with_quote == ends_with_quote, (_T("%s"), path->GetString()));
-  bool is_enclosed = starts_with_quote && ends_with_quote;
-  if (is_enclosed) {
-    return;
-  }
-
-  path->Insert(0, _T('"'));
-  path->AppendChar(_T('"'));
-}
-
-CString EnclosePathIfExe(const CString& module_path) {
-  if (!String_EndsWith(module_path, _T(".exe"), true)) {
-    return module_path;
-  }
-
-  CString enclosed_path(module_path);
-  EnclosePath(&enclosed_path);
-  return enclosed_path;
-}
-
-// remove any double quotation masks from an enclosed path
-void UnenclosePath(CString* path) {
-  ASSERT1(path);
-
-  if (path->GetLength() > 1 && path->GetAt(0) == _T('"')) {
-    bool right_quote_exists = (path->GetAt(path->GetLength() - 1) == _T('"'));
-    ASSERT(right_quote_exists,
-           (_T("[UnenclosePath - double quote mismatches]")));
-    if (right_quote_exists) {
-      // Remove the double quotation masks
-      path->Delete(0);
-      path->Truncate(path->GetLength() - 1);
-    }
-  }
-}
-
-HRESULT ShortPathToLongPath(const CString& short_path, CString* long_path) {
-  ASSERT1(long_path);
-
-  TCHAR long_name[MAX_PATH] = {0};
-  if (!::GetLongPathName(short_path, long_name, MAX_PATH)) {
-    return HRESULTFromLastError();
-  }
-
-  *long_path = long_name;
-  return S_OK;
-}
-
-HRESULT FindFilesEx(const CString& dir,
-                    const CString& pattern,
-                    std::vector<CString>* files) {
-  return detail::FindFilesEx(dir, pattern, files, &detail::IsFile);
-}
-
-HRESULT FindFiles(const CString& dir,
-                  const CString& pattern,
-                  std::vector<CString>* files) {
-  return detail::FindFilesEx(dir, pattern, files, &detail::AllFiles);
-}
-
-HRESULT FindSubDirectories(const CString& dir,
-                           const CString& pattern,
-                           std::vector<CString>* files) {
-  return detail::FindFilesEx(dir, pattern, files, &detail::IsDirectory);
-}
-
-HRESULT FindFileRecursive(const CString& dir,
-                          const CString& pattern,
-                          std::vector<CString>* files) {
-  ASSERT1(files);
-
-  std::vector<CString> temp_files;
-  HRESULT hr = FindFilesEx(dir, pattern, &temp_files);
-  if (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) && FAILED(hr)) {
-    return hr;
-  }
-
-  for (size_t i = 0; i < temp_files.size(); ++i) {
-    files->push_back(ConcatenatePath(dir, temp_files[i]));
-  }
-
-  std::vector<CString> sub_dirs;
-  hr = FindSubDirectories(dir, _T("*"), &sub_dirs);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  for (size_t i = 0; i < sub_dirs.size(); ++i) {
-    const CString& sub_dir = sub_dirs[i];
-    if (sub_dir == _T(".") || sub_dir == _T("..")) {
-      continue;
-    }
-
-    CString path = ConcatenatePath(dir, sub_dir);
-    hr = FindFileRecursive(path, pattern, files);
-    if (FAILED(hr)) {
-      return hr;
-    }
-  }
-
-  return S_OK;
-}
-
-}  // namespace omaha
-
diff --git a/common/path.h b/common/path.h
deleted file mode 100644
index f3f288b..0000000
--- a/common/path.h
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Path utility functions.
-
-#ifndef OMAHA_COMMON_PATH_H__
-#define OMAHA_COMMON_PATH_H__
-
-#include <atlstr.h>
-#include <vector>
-
-namespace omaha {
-
-// Get the starting path from the command string
-CString GetStartingPathFromString(const CString& s);
-
-// Get the trailing path from the command string
-CString GetTrailingPathFromString(const CString& s);
-
-// Get the file from the command string
-HRESULT GetFileFromCommandString(const TCHAR* s, CString* file);
-
-// Expands the string with embedded special folder variables
-HRESULT ExpandStringWithSpecialFolders(CString* str);
-
-// Normalize a path
-HRESULT NormalizePath(const TCHAR* path, CString* normalized_path);
-
-// Concatenate two paths together
-CString ConcatenatePath(const CString& path1, const CString& path2);
-
-// Get the file out of the file path
-CString GetFileFromPath(const CString& path);
-
-// Get the directory from the path
-CString GetDirectoryFromPath(const CString& path);
-
-// Remove the extension from the path.
-CString GetPathRemoveExtension(const CString& path);
-
-// Returns true iff path is an absolute path (starts with a drive name)
-bool IsAbsolutePath(const TCHAR* path);
-
-// Makes sure the path is enclosed with double quotation marks.
-void EnclosePath(CString* path);
-
-// Used to enclose paths that are typically used with LocalServer32 entries.
-// Unenclosed LocalServer32 entries with spaces are not recommended because
-// the LocalServer32 entry is a command line, not just an EXE path.
-// DLL paths should not be enclosed, because InProcServer32 entries are just
-// a DLL path and not a command line.
-CString EnclosePathIfExe(const CString& module_path);
-
-// remove any double quotation masks from an enclosed path
-void UnenclosePath(CString* path);
-
-// Converts the short path name to long name.
-HRESULT ShortPathToLongPath(const CString& short_path, CString* long_path);
-
-// Returns a list of files that match the criteria.
-HRESULT FindFiles(const CString& dir,
-                  const CString& pattern,
-                  std::vector<CString>* files);
-
-HRESULT FindFilesEx(const CString& dir,
-                    const CString& pattern,
-                    std::vector<CString>* files);
-
-HRESULT FindFileRecursive(const CString& dir,
-                          const CString& pattern,
-                          std::vector<CString>* files);
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_PATH_H__
diff --git a/common/path_unittest.cc b/common/path_unittest.cc
deleted file mode 100644
index 4769baa..0000000
--- a/common/path_unittest.cc
+++ /dev/null
@@ -1,413 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Path-utility unit tests.
-
-#include <vector>
-#include "omaha/common/file.h"
-#include "omaha/common/path.h"
-#include "omaha/common/utils.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(PathTest, IsAbsolutePath) {
-  ASSERT_TRUE(IsAbsolutePath(L"C:\\Foo.bar"));
-  ASSERT_TRUE(IsAbsolutePath(L"\\\\user-laptop1\\path"));
-  ASSERT_FALSE(IsAbsolutePath(L"windows\\system32"));
-}
-
-// EnclosePath is overzealous and quotes a path even though no spaces exists.
-TEST(PathTest, EnclosePath) {
-  CString path;
-  EnclosePath(&path);
-  EXPECT_STREQ(_T(""), path);
-
-  path = _T("");
-  EnclosePath(&path);
-  EXPECT_STREQ(_T(""), path);
-
-  path = _T("a");
-  EnclosePath(&path);
-  EXPECT_STREQ(_T("\"a\""), path);
-
-  path = _T("a b");
-  EnclosePath(&path);
-  EXPECT_STREQ(_T("\"a b\""), path);
-
-  path = " a b ";
-  EnclosePath(&path);
-  EXPECT_STREQ(_T("\" a b \""), path);
-
-  path = _T("\"a b\"");
-  EnclosePath(&path);
-  EXPECT_STREQ(_T("\"a b\""), path);
-
-  path = _T("c:\\Windows\\notepad.exe");
-  EnclosePath(&path);
-  EXPECT_STREQ(_T("\"c:\\Windows\\notepad.exe\""), path);
-
-  path = _T("c:\\Program Files\\Google\\Common\\Google Update");
-  EnclosePath(&path);
-  EXPECT_STREQ(_T("\"c:\\Program Files\\Google\\Common\\Google Update\""),
-               path);
-}
-
-// EnclosePath encloses a string that has a quote only at the beginning or end.
-TEST(PathTest, EnclosePath_OnlyOneQuote) {
-  ExpectAsserts expect_asserts;
-  CString path = _T("\"a b");
-  EnclosePath(&path);
-  EXPECT_STREQ(_T("\"\"a b\""), path);
-
-  path = _T("a b\"");
-  EnclosePath(&path);
-  EXPECT_STREQ(_T("\"a b\"\""), path);
-}
-
-// EnclosePath does not look at the middle of the string.
-TEST(PathTest, EnclosePath_QuoteInMiddle) {
-  CString path = _T("a\" b");
-  EnclosePath(&path);
-  EXPECT_STREQ(_T("\"a\" b\""), path);
-}
-
-TEST(PathTest, EnclosePath_SingleQuotes) {
-  CString path = _T("'foo'");
-  EnclosePath(&path);
-  EXPECT_STREQ(_T("\"'foo'\""), path);
-}
-
-TEST(PathTest, EnclosePathIfExe) {
-  CString original_path;
-  CString new_path;
-  new_path = EnclosePathIfExe(original_path);
-  EXPECT_STREQ(original_path, new_path);
-
-  original_path = _T("");
-  new_path = EnclosePathIfExe(original_path);
-  EXPECT_STREQ(original_path, new_path);
-
-  original_path = _T("a");
-  new_path = EnclosePathIfExe(original_path);
-  EXPECT_STREQ(original_path, new_path);
-
-  original_path = _T("a b");
-  new_path = EnclosePathIfExe(original_path);
-  EXPECT_STREQ(original_path, new_path);
-
-  original_path = " a b ";
-  new_path = EnclosePathIfExe(original_path);
-  EXPECT_STREQ(original_path, new_path);
-
-  original_path = _T("\"a b\"");
-  new_path = EnclosePathIfExe(original_path);
-  EXPECT_STREQ(original_path, new_path);
-
-  original_path = _T("c:\\Windows\\notepad.exe");
-  new_path = EnclosePathIfExe(original_path);
-  EXPECT_STREQ(_T("\"c:\\Windows\\notepad.exe\""), new_path);
-
-  original_path = _T("c:\\Program Files\\Google\\Update");
-  new_path = EnclosePathIfExe(original_path);
-  EXPECT_STREQ(original_path, new_path);
-
-  original_path = _T("c:\\Progra Files\\Google\\Update\\1.1.1.1\\goopdate.dll");
-  new_path = EnclosePathIfExe(original_path);
-  EXPECT_STREQ(original_path, new_path);
-
-  original_path = _T("c:\\Prog F\\Googl\\Update\\GoogleUpdate.exe");
-  new_path = EnclosePathIfExe(original_path);
-  EXPECT_STREQ(_T("\"c:\\Prog F\\Googl\\Update\\GoogleUpdate.exe\""), new_path);
-}
-
-TEST(PathTest, ConcatenatePath) {
-  CString expected_path("C:\\first\\part\\second\\part");
-
-  CString start("C:\\first\\part");
-  CString start_slash("C:\\first\\part\\");
-  CString end("second\\part");
-  CString end_slash("\\second\\part");
-
-  EXPECT_STREQ(expected_path, ConcatenatePath(start, end));
-  EXPECT_STREQ(expected_path, ConcatenatePath(start_slash, end));
-  EXPECT_STREQ(expected_path, ConcatenatePath(start, end_slash));
-  EXPECT_STREQ(expected_path, ConcatenatePath(start_slash, end_slash));
-}
-
-TEST(PathTest, ConcatenatePath_PathTooLong) {
-  CString two_hundred_char_root_path(_T("C:\\reallllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllly\\loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"));  // NOLINT
-  CString two_hundred_char_path(_T("realllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllly\\loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"));  // NOLINT
-  EXPECT_EQ(200, two_hundred_char_root_path.GetLength());
-  CString fifty_eight_char_name(
-      _T("filenaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaame"));
-  EXPECT_EQ(58, fifty_eight_char_name.GetLength());
-  CString fifty_nine_char_name(
-      _T("filenaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaame"));
-  EXPECT_EQ(59, fifty_nine_char_name.GetLength());
-  CString sixty_char_name(
-      _T("filenaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaame"));
-  EXPECT_EQ(60, sixty_char_name.GetLength());
-
-  // Adding the '\' makes it 259 chars.
-  CString result = ConcatenatePath(two_hundred_char_root_path,
-                                   fifty_eight_char_name);
-  EXPECT_STREQ(_T("C:\\reallllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllly\\loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong\\filenaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaame"),  // NOLINT
-               result);
-
-  // Adding the '\' makes it 260 chars.
-  ExpectAsserts expect_asserts;
-  result = ConcatenatePath(two_hundred_char_root_path,
-                                   fifty_nine_char_name);
-  EXPECT_TRUE(result.IsEmpty());
-
-  // Adding the '\' makes it 261 chars.
-  result = ConcatenatePath(two_hundred_char_root_path, sixty_char_name);
-  EXPECT_TRUE(result.IsEmpty());
-
-  // Test for buffer overflow on long strings.
-}
-
-TEST(PathTest, ConcatenatePath_EmptyString) {
-  EXPECT_STREQ(_T("bar.exe"), ConcatenatePath(_T(""), _T("bar.exe")));
-  EXPECT_STREQ(_T("foo"), ConcatenatePath(_T("foo"), _T("")));
-  // This is not what I would expect, but it is what the API does.
-  EXPECT_STREQ(_T("\\"), ConcatenatePath(_T(""), _T("")));
-}
-
-// TODO(omaha): The expected and actual values are reversed throughout.
-
-TEST(PathTest, ShortPathToLongPath) {
-  CString expected_path("C:\\Program Files");
-  CString short_path("C:\\Progra~1");
-
-  CString long_path;
-  ASSERT_SUCCEEDED(ShortPathToLongPath(short_path, &long_path));
-  ASSERT_STREQ(expected_path, long_path);
-}
-
-TEST(PathTest, FindFilesTest) {
-  GUID guid = GUID_NULL;
-  ASSERT_SUCCEEDED(::CoCreateGuid(&guid));
-
-  TCHAR path[MAX_PATH] = {0};
-  ASSERT_NE(::GetTempPath(MAX_PATH, path), 0);
-
-  CString dir = ConcatenatePath(path, GuidToString(guid));
-  EXPECT_FALSE(dir.IsEmpty());
-  EXPECT_FALSE(File::Exists(dir));
-
-  // Test with non-existent dir.
-  std::vector<CString> files;
-  EXPECT_FAILED(FindFiles(dir, _T("*.txt"), &files));
-  EXPECT_EQ(files.size(), 0);
-
-  // Test with empty dir.
-  EXPECT_NE(::CreateDirectory(dir, NULL), 0);
-  files.clear();
-  EXPECT_EQ(FindFiles(dir, _T("asdf.txt"), &files), 0x80070002);
-  EXPECT_EQ(files.size(), 0);
-
-  CString filename1 = _T("one.txt");
-  CString filepath1 = ConcatenatePath(dir, filename1);
-  CString filename2 = _T("two_en.txt");
-  CString filepath2 = ConcatenatePath(dir, filename2);
-
-  // Test with dir containing one file.
-  HANDLE handle1 = ::CreateFile(filepath1, GENERIC_READ, FILE_SHARE_READ,
-                                NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
-                                NULL);
-  EXPECT_TRUE(handle1 != NULL);
-  files.clear();
-  EXPECT_SUCCEEDED(FindFiles(dir, _T("*.txt"), &files));
-  EXPECT_EQ(files.size(), 1);
-  EXPECT_STREQ(files[0], filename1);
-
-  files.clear();
-  EXPECT_SUCCEEDED(FindFiles(dir, _T("o*.txt"), &files));
-  EXPECT_EQ(files.size(), 1);
-  EXPECT_STREQ(files[0], filename1);
-
-  files.clear();
-  EXPECT_SUCCEEDED(FindFiles(dir, filepath1, &files));
-  EXPECT_EQ(files.size(), 1);
-  EXPECT_STREQ(files[0], filename1);
-
-  files.clear();
-  EXPECT_EQ(FindFiles(dir, _T("t*.txt"), &files), 0x80070002);
-  EXPECT_EQ(files.size(), 0);
-
-  // Test with dir containing two files.
-  HANDLE handle2 = ::CreateFile(filepath2, GENERIC_READ, FILE_SHARE_READ,
-                                NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
-                                NULL);
-  EXPECT_TRUE(handle2 != NULL);
-  files.clear();
-  EXPECT_SUCCEEDED(FindFiles(dir, _T("*.txt"), &files));
-  EXPECT_EQ(files.size(), 2);
-  EXPECT_STREQ(files[0], filename1);
-  EXPECT_STREQ(files[1], filename2);
-
-  files.clear();
-  EXPECT_SUCCEEDED(FindFiles(dir, _T("o*.txt"), &files));
-  EXPECT_EQ(files.size(), 1);
-  EXPECT_STREQ(files[0], filename1);
-
-  files.clear();
-  EXPECT_SUCCEEDED(FindFiles(dir, _T("t*.txt"), &files));
-  EXPECT_EQ(files.size(), 1);
-  EXPECT_STREQ(files[0], filename2);
-
-  files.clear();
-  EXPECT_EQ(FindFiles(dir, _T("asdf.txt"), &files), 0x80070002);
-  EXPECT_EQ(files.size(), 0);
-
-  EXPECT_NE(::CloseHandle(handle1), 0);
-  EXPECT_SUCCEEDED(File::Remove(filepath1));
-  EXPECT_NE(::CloseHandle(handle2), 0);
-  EXPECT_SUCCEEDED(File::Remove(filepath2));
-  EXPECT_NE(::RemoveDirectory(dir), 0);
-}
-
-namespace detail {
-
-struct Directory {
-  Directory() {}
-  explicit Directory(const CString& name) : dir_name(name) {}
-
-  CString dir_name;
-  std::vector<Directory> sub_dirs;
-  std::vector<CString> files;
-};
-
-void ConvertDirectoryStructureToFiles(const Directory& directory,
-                                      const CString& dir_path,
-                                      std::vector<CString>* files) {
-  ASSERT_TRUE(files != NULL);
-  ASSERT_HRESULT_SUCCEEDED(CreateDir(dir_path, NULL));
-
-  for (size_t i = 0; i < directory.files.size(); ++i) {
-    const CString& file = ConcatenatePath(dir_path, directory.files[i]);
-    scoped_handle handle(::CreateFile(file, GENERIC_READ, FILE_SHARE_READ,
-                                      NULL, CREATE_ALWAYS,
-                                      FILE_ATTRIBUTE_NORMAL,
-                                      NULL));
-    EXPECT_TRUE(get(handle) != NULL);
-    files->push_back(file);
-  }
-
-  for (size_t i = 0; i < directory.sub_dirs.size(); ++i) {
-    ConvertDirectoryStructureToFiles(
-        directory.sub_dirs[i],
-        ConcatenatePath(dir_path, directory.sub_dirs[i].dir_name),
-        files);
-  }
-}
-
-void CreateTestDirectoryStructure(Directory* root_dir) {
-  EXPECT_TRUE(root_dir != NULL);
-  GUID guid = GUID_NULL;
-  ASSERT_HRESULT_SUCCEEDED(::CoCreateGuid(&guid));
-
-  TCHAR path[MAX_PATH] = {0};
-  ASSERT_NE(0, ::GetTempPath(MAX_PATH, path));
-
-  CString dir = ConcatenatePath(path, GuidToString(guid));
-  EXPECT_FALSE(dir.IsEmpty());
-  EXPECT_FALSE(File::Exists(dir));
-
-  root_dir->dir_name = dir;
-  root_dir->files.push_back(_T("test1.txt"));
-  root_dir->files.push_back(_T("test2.txt"));
-
-  Directory sub_dir1_level1(_T("sub_dir1_level1"));
-  sub_dir1_level1.files.push_back(_T("sub_dir1_level1_test1.txt"));
-  Directory sub_dir1_level2(_T("sub_dir1_level2"));
-  sub_dir1_level2.files.push_back(_T("sub_dir1_level2_test1.txt"));
-  sub_dir1_level1.sub_dirs.push_back(sub_dir1_level2);
-  root_dir->sub_dirs.push_back(sub_dir1_level1);
-
-  Directory sub_dir2_level1(_T("sub_dir2_level1"));
-  sub_dir2_level1.files.push_back(_T("sub_dir2_level1_test1.txt"));
-  root_dir->sub_dirs.push_back(sub_dir2_level1);
-}
-}  // detail.
-
-TEST(PathTest, FindFileRecursiveTest) {
-  detail::Directory dir;
-  detail::CreateTestDirectoryStructure(&dir);
-
-  std::vector<CString> expected_files;
-  detail::ConvertDirectoryStructureToFiles(dir, dir.dir_name, &expected_files);
-
-  // Call the test method.
-  std::vector<CString> files;
-  ASSERT_HRESULT_SUCCEEDED(FindFileRecursive(dir.dir_name,
-                                             _T("*test*.txt"),
-                                             &files));
-
-  // Validate the results.
-  ASSERT_EQ(expected_files.size(), files.size());
-  for (size_t i = 0; i < expected_files.size(); ++i) {
-    EXPECT_STREQ(expected_files[i], files[i]);
-  }
-
-  // Cleanup.
-  ASSERT_HRESULT_SUCCEEDED(DeleteDirectory(dir.dir_name));
-}
-
-TEST(PathTest, FindFileRecursiveTest_Empty) {
-  GUID guid = GUID_NULL;
-  ASSERT_HRESULT_SUCCEEDED(::CoCreateGuid(&guid));
-
-  TCHAR path[MAX_PATH] = {0};
-  ASSERT_NE(0, ::GetTempPath(MAX_PATH, path));
-
-  CString dir = ConcatenatePath(path, GuidToString(guid));
-  EXPECT_FALSE(dir.IsEmpty());
-  EXPECT_FALSE(File::Exists(dir));
-
-  ASSERT_HRESULT_SUCCEEDED(CreateDir(dir, NULL));
-
-  // Call the test method.
-  std::vector<CString> files;
-  ASSERT_HRESULT_SUCCEEDED(FindFileRecursive(dir, _T("*test*.txt"),
-                                             &files));
-
-  // Validate results.
-  ASSERT_EQ(0, files.size());
-
-  // Cleanup.
-  ASSERT_HRESULT_SUCCEEDED(DeleteDirectory(dir));
-}
-
-TEST(PathTest, FindFileRecursiveTest_DirNotCreated) {
-  GUID guid = GUID_NULL;
-  ASSERT_HRESULT_SUCCEEDED(::CoCreateGuid(&guid));
-
-  TCHAR path[MAX_PATH] = {0};
-  ASSERT_NE(0, ::GetTempPath(MAX_PATH, path));
-
-  CString dir = ConcatenatePath(path, GuidToString(guid));
-  EXPECT_FALSE(dir.IsEmpty());
-  EXPECT_FALSE(File::Exists(dir));
-
-  // Call the test method.
-  std::vector<CString> files;
-  ASSERT_HRESULT_FAILED(FindFileRecursive(dir, _T("*test*.txt"), &files));
-}
-
-}  // namespace omaha
-
diff --git a/common/pe_utils.cc b/common/pe_utils.cc
deleted file mode 100644
index 1e3b1e5..0000000
--- a/common/pe_utils.cc
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Utility functions related to PE files (executables)
-
-#include "omaha/common/pe_utils.h"
-
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/scoped_any.h"
-
-namespace omaha {
-
-// Not really necessary as long as running on x86 architecture throughout, but
-// what the hell
-#if defined(BIG_ENDIAN)
-uint32 GetUint32LE(void const * const p) {
-  uchar const * const pu = reinterpret_cast<uchar const * const>(p);
-  uint32 i = pu[0] | pu[1]<<8 | pu[2]<<16 | pu[3]<<24;
-  return i;
-}
-
-void PutUint32LE(uint32 i, void * const p) {
-  uchar * const pu = reinterpret_cast<uchar * const>(p);
-  pu[0] = i & 0xff;
-  pu[1] = (i >>  8) & 0xff;
-  pu[2] = (i >> 16) & 0xff;
-  pu[3] = (i >> 24) & 0xff;
-}
-#else  // LITTLE_ENDIAN
-inline uint32 GetUint32LE(void const * const p) {
-  uint32 const * const pu = reinterpret_cast<uint32 const * const>(p);
-  return *pu;
-}
-
-inline void PutUint32LE(uint32 i, void * const p) {
-  uint32 * const pu = reinterpret_cast<uint32 * const>(p);
-  *pu = i;
-}
-#endif
-
-// Magic PE constants
-const uint32 kPEHeaderOffset         = 60;
-const uint32 kPEHeaderChecksumOffset = 88;
-const uint32 kPEHeaderSizeMin        = 160;
-const char magic_EXE_header[] = "MZ\0\0";
-const char magic_PE_header[]  = "PE\0\0";
-
-HRESULT SetPEChecksum(const TCHAR *filename, uint32 checksum) {
-  // Write the checksum field of the Windows NT-specific "optional" header.
-  // Use Windows API calls rather than C library calls so that it will be
-  // really a small routine when used in the stub executable.
-
-  ASSERT(filename, (L""));
-
-  scoped_hfile file(::CreateFile(filename,
-                                 GENERIC_READ | GENERIC_WRITE,
-                                 0,
-                                 NULL,
-                                 OPEN_EXISTING,
-                                 FILE_ATTRIBUTE_NORMAL,
-                                 NULL));
-  if (!file)
-    return HRESULTFromLastError();
-
-  size_t size = ::GetFileSize(get(file), NULL);
-  if (size == INVALID_FILE_SIZE)
-    return HRESULTFromLastError();
-
-  scoped_file_mapping mapping(::CreateFileMapping(get(file),
-                                                  NULL,
-                                                  PAGE_READWRITE,
-                                                  0,
-                                                  0,
-                                                  NULL));
-  if (!mapping)
-    return HRESULTFromLastError();
-
-  scoped_file_view file_data(::MapViewOfFile(get(mapping),
-                                             FILE_MAP_WRITE,
-                                             0,
-                                             0,
-                                             size));
-  if (!file_data)
-    return HRESULTFromLastError();
-
-  uchar * image = reinterpret_cast<uchar *>(get(file_data));
-
-  return SetPEChecksumToBuffer(image, size, checksum);
-}
-
-HRESULT GetPEChecksum(const TCHAR *filename, uint32 * checksum) {
-  // Read the checksum field out of the Windows NT-specific "optional" header.
-  // Use Windows API calls rather than C library calls so that it will be
-  // really a small routine when used in the stub executable.
-
-  ASSERT(filename, (L""));
-  ASSERT(checksum, (L""));
-
-  scoped_hfile file(::CreateFile(filename,
-                                 GENERIC_READ,
-                                 FILE_SHARE_READ,
-                                 NULL,
-                                 OPEN_EXISTING,
-                                 FILE_ATTRIBUTE_READONLY,
-                                 NULL));
-  if (!file)
-    return HRESULTFromLastError();
-
-  size_t size = ::GetFileSize(get(file), NULL);
-  if (size == INVALID_FILE_SIZE)
-    return HRESULTFromLastError();
-
-  scoped_file_mapping mapping(::CreateFileMapping(get(file),
-                              NULL,
-                              PAGE_READONLY,
-                              0,
-                              0,
-                              NULL));
-  if (!mapping)
-    return HRESULTFromLastError();
-
-  scoped_file_view file_data(::MapViewOfFile(get(mapping),
-                             FILE_MAP_READ,
-                             0,
-                             0,
-                             size));
-  if (!file_data)
-    return HRESULTFromLastError();
-
-  uchar * image = reinterpret_cast<uchar *>(get(file_data));
-
-  return GetPEChecksumFromBuffer(image, size, checksum);
-}
-
-HRESULT SetPEChecksumToBuffer(uchar *buffer, size_t size, uint32 checksum) {
-  // Sanity checks
-  if (size < 64) {
-    ASSERT(false, (L"File too short to be valid executable"));
-    return E_FAIL;
-  }
-
-  uint32 x = GetUint32LE(magic_EXE_header);
-  if (::memcmp(buffer, &x, 2)) {
-    ASSERT(false, (L"Missing executable's magic number"));
-    return E_FAIL;
-  }
-
-  uint32 peheader = GetUint32LE(buffer + kPEHeaderOffset);
-  if (size < peheader + kPEHeaderSizeMin) {
-    ASSERT(false, (L"Too small given PE header size"));
-    return E_FAIL;
-  }
-
-  x = GetUint32LE(magic_PE_header);
-  if (::memcmp(buffer + peheader, &x, 4)) {
-    ASSERT(false, (L"Missing PE header magic number"));
-    return E_FAIL;
-  }
-
-  // Finally, write the checksum
-  PutUint32LE(checksum, &x);
-  ::memcpy(buffer + peheader + kPEHeaderChecksumOffset, &x, 4);
-
-  return S_OK;
-}
-
-HRESULT GetPEChecksumFromBuffer(const unsigned char *buffer,
-                                size_t size,
-                                uint32 *checksum) {
-  // Sanity checks
-  if (size < 64) {
-    ASSERT(false, (L"File too short to be valid executable"));
-    return E_FAIL;
-  }
-
-  uint32 x = GetUint32LE(magic_EXE_header);
-  if (::memcmp(buffer, &x, 2)) {
-    ASSERT(false, (L"Missing executable's magic number"));
-    return E_FAIL;
-  }
-
-  uint32 peheader = GetUint32LE(buffer + kPEHeaderOffset);
-  if (size < peheader + kPEHeaderSizeMin) {
-    ASSERT(false, (L"Too small given PE header size"));
-    return E_FAIL;
-  }
-
-  x = GetUint32LE(magic_PE_header);
-  if (::memcmp(buffer + peheader, &x, 4)) {
-    ASSERT(false, (L"Missing PE header magic number"));
-    return E_FAIL;
-  }
-
-  // Finally, read the checksum
-
-  *checksum = GetUint32LE(buffer + peheader + kPEHeaderChecksumOffset);
-
-  return S_OK;
-}
-
-}  // namespace omaha
-
diff --git a/common/pe_utils_unittest.cc b/common/pe_utils_unittest.cc
deleted file mode 100644
index 4a14dca..0000000
--- a/common/pe_utils_unittest.cc
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Unittests for pe_utils
-
-
-#include "omaha/common/debug.h"
-#include "omaha/common/file.h"
-#include "omaha/common/pe_utils.h"
-#include "omaha/common/utils.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(PEUtilsTest, PEUtils) {
-  // Get some known directories
-  CString windows_dir;
-  CString temp_dir;
-  DWORD dw = ::GetEnvironmentVariable(L"SystemRoot",
-                                      CStrBuf(windows_dir, MAX_PATH), MAX_PATH);
-  ASSERT_TRUE(dw);
-  dw = ::GetEnvironmentVariable(L"TEMP", CStrBuf(temp_dir, MAX_PATH), MAX_PATH);
-  ASSERT_TRUE(dw);
-
-  // Get a known executable to play with
-  CString notepad(windows_dir + L"\\NOTEPAD.EXE");
-  ASSERT_TRUE(File::Exists(notepad));
-
-  CString temp_exe(temp_dir + L"\\pe_utils_test.exe");
-  if (File::Exists(temp_exe)) {
-    File::Remove(temp_exe);
-  }
-  ASSERT_FALSE(File::Exists(temp_exe));
-  ASSERT_SUCCEEDED(File::Copy(notepad, temp_exe, true));
-
-  // Stomp on its checksum and check the result
-  const unsigned int kChk1 = 0xFEE1BAD;
-  const unsigned int kChk2 = 0x600DF00D;
-
-  unsigned int checksum = 0;
-
-  // Test Get/SetPEChecksum
-  ASSERT_SUCCEEDED(SetPEChecksum(temp_exe, kChk1));
-  ASSERT_SUCCEEDED(GetPEChecksum(temp_exe, &checksum));
-  ASSERT_EQ(kChk1, checksum);
-
-  ASSERT_SUCCEEDED(SetPEChecksum(temp_exe, kChk2));
-  ASSERT_SUCCEEDED(GetPEChecksum(temp_exe, &checksum));
-  ASSERT_EQ(kChk2, checksum);
-
-  // Test GetPEChecksumFromBuffer/SetPEChecksumToBuffer
-  std::vector<byte> buffer;
-  ASSERT_SUCCEEDED(ReadEntireFile(temp_exe, 0, &buffer));
-
-  int buffer_data_len = buffer.size();
-  uchar *buffer_data = reinterpret_cast<uchar*>(&buffer.front());
-
-  ASSERT_SUCCEEDED(SetPEChecksumToBuffer(buffer_data, buffer_data_len, kChk1));
-  ASSERT_SUCCEEDED(GetPEChecksumFromBuffer(buffer_data,
-                                           buffer_data_len,
-                                           &checksum));
-  ASSERT_EQ(kChk1, checksum);
-
-  ASSERT_SUCCEEDED(SetPEChecksumToBuffer(buffer_data, buffer_data_len, kChk2));
-  ASSERT_SUCCEEDED(GetPEChecksumFromBuffer(buffer_data,
-                                           buffer_data_len,
-                                           &checksum));
-  ASSERT_EQ(kChk2, checksum);
-
-  // Clean up
-  ASSERT_SUCCEEDED(File::Remove(temp_exe));
-}
-
-}  // namespace omaha
-
diff --git a/common/ping.cc b/common/ping.cc
new file mode 100644
index 0000000..181df73
--- /dev/null
+++ b/common/ping.cc
@@ -0,0 +1,440 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/ping.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/scoped_impersonation.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vista_utils.h"
+#include "omaha/common/app_registry_utils.h"
+#include "omaha/common/command_line.h"
+#include "omaha/common/command_line_builder.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/update_request.h"
+#include "omaha/common/update_response.h"
+#include "omaha/goopdate/app.h"
+#include "omaha/goopdate/app_bundle.h"
+#include "omaha/goopdate/update_request_utils.h"
+#include "omaha/goopdate/update_response_utils.h"
+
+namespace omaha {
+
+const TCHAR* const Ping::kRegKeyPing = _T("Pings");
+const time64 Ping::kPingExpiry100ns  = 10 * kDaysTo100ns;  // 10 days.
+
+Ping::Ping(bool is_machine,
+           const CString& session_id,
+           const CString& install_source)
+    : is_machine_(is_machine),
+      ping_request_(xml::UpdateRequest::Create(is_machine,
+                                               session_id,
+                                               install_source,
+                                               CString())) {
+}
+
+Ping::~Ping() {
+}
+
+void Ping::BuildRequest(const App* app, bool is_update_check) {
+  update_request_utils::BuildRequest(app, is_update_check, ping_request_.get());
+}
+
+void Ping::LoadAppDataFromExtraArgs(const CommandLineExtraArgs& extra_args) {
+  const CString installation_id = GuidToString(extra_args.installation_id);
+  for (size_t i = 0; i != extra_args.apps.size(); ++i) {
+    AppData app_data;
+    app_data.app_id = GuidToString(extra_args.apps[i].app_guid);
+    app_data.language = extra_args.language;
+    app_data.brand_code = extra_args.brand_code;
+    app_data.client_id = extra_args.client_id;
+    app_data.installation_id = installation_id;
+    app_data.experiment_labels = extra_args.apps[i].experiment_labels;
+    apps_data_.push_back(app_data);
+  }
+
+  omaha_data_.app_id = kGoogleUpdateAppId;
+  omaha_data_.language = extra_args.language;
+  omaha_data_.brand_code = extra_args.brand_code;
+  omaha_data_.client_id = extra_args.client_id;
+  omaha_data_.installation_id = installation_id;
+  omaha_data_.experiment_labels = extra_args.experiment_labels;
+}
+
+void Ping::LoadOmahaDataFromRegistry() {
+  omaha_data_.app_id = kGoogleUpdateAppId;
+  app_registry_utils::GetClientStateData(
+      is_machine_,
+      kGoogleUpdateAppId,
+      NULL,
+      NULL,         // ap is not used yet.
+      &omaha_data_.language,
+      &omaha_data_.brand_code,
+      &omaha_data_.client_id,
+      &omaha_data_.installation_id,
+      &omaha_data_.experiment_labels);
+}
+
+void Ping::LoadAppDataFromRegistry(const std::vector<CString>& app_ids) {
+  for (size_t i = 0; i != app_ids.size(); ++i) {
+    AppData app_data;
+    app_data.app_id = app_ids[i];
+    app_registry_utils::GetClientStateData(
+        is_machine_,
+        app_data.app_id,
+        &app_data.pv,
+        NULL,         // ap is not used yet.
+        &app_data.language,
+        &app_data.brand_code,
+        &app_data.client_id,
+        &app_data.installation_id,
+        &app_data.experiment_labels);
+    apps_data_.push_back(app_data);
+  }
+
+  LoadOmahaDataFromRegistry();
+}
+
+HRESULT Ping::Send(bool is_fire_and_forget) {
+  CORE_LOG(L3, (_T("[Ping::Send]")));
+
+  ASSERT1(ConfigManager::Instance()->CanUseNetwork(is_machine_));
+
+  if (ping_request_->IsEmpty()) {
+    CORE_LOG(L3, (_T("[Ping::Send did not send empty ping]")));
+    return S_FALSE;
+  }
+
+  CString request_string;
+  HRESULT hr = BuildRequestString(&request_string);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[BuildRequestString failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  const DWORD wait_timeout_ms = is_fire_and_forget ? 0 : INFINITE;
+  hr = SendUsingGoogleUpdate(request_string, wait_timeout_ms);
+  if (SUCCEEDED(hr)) {
+    return hr;
+  }
+
+  CORE_LOG(LE, (_T("[Ping::SendUsingGoogleUpdate failed][0x%x]"), hr));
+
+  hr = SendInProcess(request_string);
+  if (SUCCEEDED(hr)) {
+    return hr;
+  }
+
+  CORE_LOG(LE, (_T("[Ping::SendInProcess failed][0x%x]"), hr));
+
+  return PersistPing(is_machine_, request_string);
+}
+
+void Ping::BuildOmahaPing(const CString& version,
+                          const CString& next_version,
+                          const PingEventPtr& ping_event) {
+  xml::request::App app(BuildOmahaApp(version, next_version));
+  app.ping_events.push_back(ping_event);
+  ping_request_->AddApp(app);
+}
+
+void Ping::BuildOmahaPing(const CString& version,
+                          const CString& next_version,
+                          const PingEventPtr& ping_event1,
+                          const PingEventPtr& ping_event2) {
+  xml::request::App app(BuildOmahaApp(version, next_version));
+  app.ping_events.push_back(ping_event1);
+  app.ping_events.push_back(ping_event2);
+  ping_request_->AddApp(app);
+}
+
+xml::request::App Ping::BuildOmahaApp(const CString& version,
+                                      const CString& next_version) const {
+  xml::request::App app;
+
+  app.app_id         = omaha_data_.app_id;
+  app.lang           = omaha_data_.language;
+  app.brand_code     = omaha_data_.brand_code;
+  app.client_id      = omaha_data_.client_id;
+  app.experiments    = omaha_data_.experiment_labels;
+  app.iid            = omaha_data_.installation_id;
+
+  app.version        = version;
+  app.next_version   = next_version;
+
+  return app;
+}
+
+void Ping::BuildAppsPing(const PingEventPtr& ping_event) {
+  for (size_t i = 0; i != apps_data_.size(); ++i) {
+    xml::request::App app;
+
+    app.version        = apps_data_[i].pv;
+    app.app_id         = apps_data_[i].app_id;
+    app.lang           = apps_data_[i].language;
+    app.brand_code     = apps_data_[i].brand_code;
+    app.client_id      = apps_data_[i].client_id;
+    app.experiments    = apps_data_[i].experiment_labels;
+    app.iid            = apps_data_[i].installation_id;
+
+    app.ping_events.push_back(ping_event);
+    ping_request_->AddApp(app);
+  }
+}
+
+HRESULT Ping::SendUsingGoogleUpdate(const CString& request_string,
+                                    DWORD wait_timeout_ms) const {
+  CStringA request_string_utf8(WideToUtf8(request_string));
+  CStringA ping_string_utf8;
+  WebSafeBase64Escape(request_string_utf8, &ping_string_utf8);
+
+  CommandLineBuilder builder(COMMANDLINE_MODE_PING);
+  builder.set_ping_string(Utf8ToWideChar(ping_string_utf8,
+                                         ping_string_utf8.GetLength()));
+  CString args = builder.GetCommandLineArgs();
+
+  scoped_process ping_process;
+  HRESULT hr = goopdate_utils::StartGoogleUpdateWithArgs(is_machine_,
+                                                         args,
+                                                         address(ping_process));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[failed to start ping process][0x%08x]"), hr));
+    return hr;
+  }
+
+  if (wait_timeout_ms) {
+    DWORD result = ::WaitForSingleObject(get(ping_process), wait_timeout_ms);
+    DWORD exit_code(0);
+    if (result == WAIT_OBJECT_0 &&
+        ::GetExitCodeProcess(get(ping_process), &exit_code)) {
+      ASSERT1(exit_code == 0 || FAILED(exit_code));
+      return (exit_code == 0) ? S_OK : exit_code;
+    } else {
+      if (result == WAIT_TIMEOUT) {
+        CORE_LOG(LW, (_T("[ping process did not finish in time][pid=%u]"),
+            ::GetProcessId(get(ping_process))));
+        VERIFY1(::TerminateProcess(get(ping_process), UINT_MAX));
+      }
+      return E_FAIL;
+    }
+  }
+
+  return S_OK;
+}
+
+HRESULT Ping::SendInProcess(const CString& request_string) const {
+  HRESULT hr = SendString(is_machine_, HeadersVector(), request_string);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[SendString failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT Ping::BuildRequestString(CString* request_string) const {
+  ASSERT1(request_string);
+  return ping_request_->Serialize(request_string);
+}
+
+CString Ping::GetPingRegPath(bool is_machine) {
+  CString ping_reg_path = is_machine ? MACHINE_REG_UPDATE : USER_REG_UPDATE;
+  return AppendRegKeyPath(ping_reg_path, kRegKeyPing);
+}
+
+HRESULT Ping::LoadPersistedPings(bool is_machine, PingsVector* pings) {
+  ASSERT1(pings);
+
+  RegKey ping_reg_key;
+  HRESULT hr = ping_reg_key.Open(GetPingRegPath(is_machine), KEY_READ);
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[Unable to open Ping regkey][0x%x]"), hr));
+    return hr;
+  }
+
+  int num_pings = ping_reg_key.GetValueCount();
+  for (int i = 0; i < num_pings; ++i) {
+    CString persisted_time_string;
+    hr = ping_reg_key.GetValueNameAt(i, &persisted_time_string, NULL);
+    if (FAILED(hr)) {
+      CORE_LOG(LW, (_T("[GetValueNameAt failed][%d]"), i));
+      continue;
+    }
+
+    time64 persisted_time = _tcstoui64(persisted_time_string, NULL, 10);
+    if (persisted_time == 0 || persisted_time == _UI64_MAX) {
+      CORE_LOG(LW, (_T("[Incorrect time value][%s]"), persisted_time_string));
+      continue;
+    }
+
+    CString ping_string;
+    hr = ping_reg_key.GetValue(persisted_time_string, &ping_string);
+    if (FAILED(hr)) {
+      CORE_LOG(LW, (_T("[GetValue failed][%s]"), persisted_time_string));
+      continue;
+    }
+
+    pings->push_back(std::make_pair(persisted_time, ping_string));
+  }
+
+  return S_OK;
+}
+
+bool Ping::IsPingExpired(time64 persisted_time) {
+  const time64 now = GetCurrent100NSTime();
+
+  if (now < persisted_time) {
+    CORE_LOG(LW, (_T("[Incorrect clock time][%I64u][%I64u]"),
+                  now, persisted_time));
+    return true;
+  }
+
+  const time64 time_difference = now - persisted_time;
+  CORE_LOG(L3, (_T("[%I64u][%I64u][%I64u]"),
+                now, persisted_time, time_difference));
+
+  const bool result = time_difference >= kPingExpiry100ns;
+  CORE_LOG(L3, (_T("[IsPingExpired][%d]"), result));
+  return result;
+}
+
+HRESULT Ping::DeletePersistedPing(bool is_machine, time64 persisted_time) {
+  CString persisted_time_string;
+  persisted_time_string.Format(_T("%I64u"), persisted_time);
+  CORE_LOG(L3, (_T("[Ping::DeletePersistedPing][%s]"), persisted_time_string));
+
+  CString ping_reg_path(GetPingRegPath(is_machine));
+  HRESULT hr = RegKey::DeleteValue(ping_reg_path, persisted_time_string);
+
+  if (RegKey::IsKeyEmpty(ping_reg_path)) {
+    VERIFY1(SUCCEEDED(RegKey::DeleteKey(ping_reg_path)));
+  }
+
+  return hr;
+}
+
+HRESULT Ping::PersistPing(bool is_machine, const CString& ping_string) {
+  CString time_now_str;
+  time_now_str.Format(_T("%I64u"), GetCurrent100NSTime());
+  CORE_LOG(L3, (_T("[Ping::PersistPing][%s][%s]"), time_now_str, ping_string));
+
+  return RegKey::SetValue(GetPingRegPath(is_machine),
+                          time_now_str,
+                          ping_string);
+}
+
+HRESULT Ping::SendPersistedPings(bool is_machine) {
+  PingsVector pings;
+  HRESULT hr = LoadPersistedPings(is_machine, &pings);
+  if (FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))) {
+    return hr;
+  }
+
+  for (size_t i = 0; i != pings.size(); ++i) {
+    time64 persisted_time = pings[i].first;
+    int32 request_age = Time64ToInt32(GetCurrent100NSTime()) -
+                        Time64ToInt32(persisted_time);
+    const CString& ping_string(pings[i].second);
+
+    CORE_LOG(L3, (_T("[Resending ping][%I64u][%d][%s]"),
+                  persisted_time, request_age, ping_string));
+
+    CString request_age_string;
+    request_age_string.Format(_T("%d"), request_age);
+    HeadersVector headers;
+    headers.push_back(std::make_pair(kHeaderXRequestAge, request_age_string));
+
+    hr = SendString(is_machine, headers, ping_string);
+
+    if (SUCCEEDED(hr) || IsPingExpired(persisted_time)) {
+      CORE_LOG(L3, (_T("[Deleting ping][0x%x]"), hr));
+      VERIFY1(SUCCEEDED(DeletePersistedPing(is_machine, persisted_time)));
+    }
+  }
+
+  return S_OK;
+}
+
+// TODO(omaha): Ping support for authenticated proxies.
+HRESULT Ping::SendString(bool is_machine,
+                         const HeadersVector& headers,
+                         const CString& request_string) {
+  ASSERT1(ConfigManager::Instance()->CanUseNetwork(is_machine));
+
+  CORE_LOG(L3, (_T("[ping request string][%s]"), request_string));
+
+  CString url;
+  ConfigManager::Instance()->GetPingUrl(&url);
+
+  // Impersonate the user if the caller is machine, running as local system,
+  // and a user is logged on to the system.
+  scoped_handle impersonation_token(
+      goopdate_utils::GetImpersonationTokenForMachineProcess(is_machine));
+  scoped_impersonation impersonate_user(get(impersonation_token));
+
+  WebServicesClient web_service_client(is_machine);
+  HRESULT hr(web_service_client.Initialize(url, headers, false));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[WebServicesClient::Initialize failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  scoped_ptr<xml::UpdateResponse> response(xml::UpdateResponse::Create());
+  hr = web_service_client.SendString(&request_string, response.get());
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[WebServicesClient::SendString failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  // If a ping is sent but the response is corrupted in some way (or we can't
+  // persist the labels for some reason), returning a failure code would result
+  // in the ping being persisted and re-sent later.  For this reason, we always
+  // return a success code if we sent the ping, even if following actions fail.
+  VERIFY1(SUCCEEDED(update_response_utils::ApplyExperimentLabelDeltas(
+      is_machine,
+      response.get())));
+
+  return S_OK;
+}
+
+HRESULT Ping::HandlePing(bool is_machine, const CString& ping_string) {
+  CORE_LOG(L3, (_T("[Ping::HandlePing][%s]"), ping_string));
+
+  CStringA ping_string_utf8(WideToUtf8(ping_string));
+
+  CStringA request_string_utf8;
+  int out_buffer_length = ping_string_utf8.GetLength();
+  char* out_buffer = request_string_utf8.GetBufferSetLength(out_buffer_length);
+
+  out_buffer_length = WebSafeBase64Unescape(ping_string_utf8,
+                                            ping_string_utf8.GetLength(),
+                                            out_buffer,
+                                            out_buffer_length);
+  ASSERT1(out_buffer_length <= ping_string_utf8.GetLength());
+  request_string_utf8.ReleaseBufferSetLength(out_buffer_length);
+
+  CString request_string(Utf8ToWideChar(request_string_utf8,
+                                        request_string_utf8.GetLength()));
+
+  return Ping::SendString(is_machine, HeadersVector(), request_string);
+}
+
+}  // namespace omaha
+
diff --git a/common/ping.h b/common/ping.h
new file mode 100644
index 0000000..7cff0e6
--- /dev/null
+++ b/common/ping.h
@@ -0,0 +1,209 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Functions related to the sending the setup pings. The functionality provided
+// by this class is used in by install, handoff, and update features.
+// This is an overview of how the setup pings work.
+// In the case of install, the execution flow includes the following steps:
+// elevation if needed, setting up Omaha, and installing the applications
+// specified in the tag.
+// The code guarantees that an EVENT_INSTALL_COMPLETE(2) ping for Omaha is sent
+// in all cases, except trivial errors that may happen before the execution flow
+// reaches the Install function.
+// A EVENT_INSTALL_COMPLETE(2) ping for the apps is also sent in all cases.
+//
+// Where the code fails affects how the pings are generated and sent, as
+// following:
+// * if the elevation was required but the elevated process failed to run,
+//   then both pings are sent from the medium integrity /install
+//   process.
+// * if the Omaha setup code ran but it errored out or the handoff failed to
+//   launch, then both pings are sent from the /install process or the
+//   elevated /install process if elevation was successful. The pings will be
+//   split in two different http transactions in the case setup completed
+//   successfully but it failed to handoff.
+// * if the /handoff process launched but an error occured in the handoff
+//   process itself, then the Omaha "2" ping is sent from the /install process
+//   and the apps "2" ping is sent from the /handoff process.
+//   The apps ping is only sent if the handoff code did not proceed far enough
+//   to create a bundle of  applications. Beyond that point, the bundle takes
+//   over the responsibility of sending "2" pings for each app in the bundle.
+//
+// There is an IPC mechanism between /install and /handoff processes based
+// on detected input idle to avoid overlapping error handling and
+// displaying redundant error messages in different processes. Usually ping
+// handling, error handling, and displaying error UI is done in the same layer.
+// When an error happens in the chain of /install, elevated install,
+// and /handoff  processes, then UI is displayed by one of these processes only
+// if the child process did not display UI. Since UI is displayed in the
+// /handoff process in both the success and error cases, this information can't
+// be useful to handle the pings, therefore pings only rely on a weaker
+// guarantee, which is whether the child process has launched or not.
+
+// TODO(omaha): unify the install and bundle pings mechanisms. There is
+// no facility to cancel the install pings in the current implementation.
+
+// TODO(omaha): use a pimpl to avoid the dependency on UpdateRequest.
+
+#ifndef OMAHA_COMMON_PING_H_
+#define OMAHA_COMMON_PING_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include <utility>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/common/ping_event.h"
+#include "omaha/common/update_request.h"
+#include "omaha/common/web_services_client.h"
+#include "third_party/gtest/include/gtest/gtest.h"
+
+namespace omaha {
+
+struct CommandLineExtraArgs;
+class App;
+
+class Ping {
+ public:
+  Ping(bool is_machine,
+       const CString& session_id,
+       const CString& install_source);
+  ~Ping();
+
+  // TODO(omaha): Consider moving everything except the functionality that
+  // actually sends the pings out of the Ping class into builder classes. A
+  // dependency on the model App is not desirable here.
+  void BuildRequest(const App* app, bool is_update_check);
+
+  // Loads app data from a location other than the Omaha state machine.
+  void LoadAppDataFromExtraArgs(const CommandLineExtraArgs& extra_args);
+  void LoadAppDataFromRegistry(const std::vector<CString>& apps);
+  void LoadOmahaDataFromRegistry();
+
+  // Builds pings for Omaha or apps loaded previously.
+  void BuildOmahaPing(const CString& version,
+                      const CString& next_version,
+                      const PingEventPtr& ping_event);
+
+  void BuildOmahaPing(const CString& version,
+                      const CString& next_version,
+                      const PingEventPtr& ping_event1,
+                      const PingEventPtr& ping_event2);
+
+  void BuildAppsPing(const PingEventPtr& ping_event);
+
+  // Serializes a ping request as a string.
+  HRESULT BuildRequestString(CString* request_string) const;
+
+  // Sends the ping events. The pings could be sent out-of-process,
+  // using the installed Omaha or in-process, if the out-of-process delivery
+  // fails.
+  //
+  // Sending pings is attempted out-of-process first, with a timeout
+  // of 60 seconds, after which the in-process delivery kicks in. The pinging
+  // process pinging is terminated before the in-process pinging is attempted
+  // in order to avoid duplicate pings and prevent run away processes.
+  //
+  // The 'is_fire_and_forget' argument only applies to the out-of-process
+  // delivery mechanism. This allows the execution flow to return to the caller
+  // as soon as possible and it is useful for sending success pings.
+  // The in-process pinging is always blocking.
+  //
+  // If the caller is local system and a user is logged on, the function
+  // impersonatates that user.
+  //
+  // The function returns S_OK if the ping was successfully sent using either
+  // mechanism.
+  HRESULT Send(bool is_fire_and_forget);
+
+  // Sends all persisted pings. Deletes successful or expired pings.
+  static HRESULT SendPersistedPings(bool is_machine);
+
+  // Sends a ping string to the server, in-process. The ping_string must be web
+  // safe base64 encoded and it will be decoded before the ping is sent.
+  static HRESULT HandlePing(bool is_machine, const CString& ping_string);
+
+ private:
+  FRIEND_TEST(PingTest, BuildOmahaPing);
+  FRIEND_TEST(PingTest, BuildOmahaPingWithSessionOverride);
+  FRIEND_TEST(PingTest, BuildAppsPing);
+  FRIEND_TEST(PingTest, BuildAppsPingFromRegistry);
+  FRIEND_TEST(PingTest, SendString);
+  FRIEND_TEST(PingTest, SendInProcess);
+  FRIEND_TEST(PingTest, IsPingExpired_PastTime);
+  FRIEND_TEST(PingTest, IsPingExpired_CurrentTime);
+  FRIEND_TEST(PingTest, IsPingExpired_FutureTime);
+  FRIEND_TEST(PingTest, LoadPersistedPings_NoPersistedPings);
+  FRIEND_TEST(PingTest, LoadPersistedPings);
+  FRIEND_TEST(PingTest, PersistPing);
+  FRIEND_TEST(PingTest, DeletePersistedPing);
+  FRIEND_TEST(PingTest, PersistPing_Load_Delete);
+  FRIEND_TEST(PingTest, SendPersistedPings);
+  FRIEND_TEST(PingTest, DISABLED_SendUsingGoogleUpdate);
+
+  typedef std::vector<std::pair<time64, CString> > PingsVector;
+  static const TCHAR* const kRegKeyPing;
+  static const time64 kPingExpiry100ns;
+
+  // Sends pings using the installed GoogleUpdate, which runs in the
+  // ping mode. the function waits for the pings to be sent if wait_timeout_ms
+  // is not zero. Returns S_OK if the pings have been successfully sent.
+  HRESULT SendUsingGoogleUpdate(const CString& request_string,
+                                DWORD wait_timeout_ms) const;
+
+  // Sends ping events in process. Returns S_OK if the pings have been
+  // sent to the server and the server response is 200 OK;
+  HRESULT SendInProcess(const CString& request_string) const;
+
+  xml::request::App BuildOmahaApp(const CString& version,
+                                  const CString& next_version) const;
+
+  // Persistent Ping utility functions.
+  static CString GetPingRegPath(bool is_machine);
+  static HRESULT LoadPersistedPings(bool is_machine, PingsVector* pings);
+  static bool IsPingExpired(time64 persisted_time);
+  static HRESULT DeletePersistedPing(bool is_machine, time64 persisted_time);
+  static HRESULT PersistPing(bool is_machine, const CString& ping_string);
+
+  // Sends a string to the server.
+  static HRESULT SendString(bool is_machine,
+                            const HeadersVector& headers,
+                            const CString& request_string);
+
+  bool is_machine_;
+
+  // Information about apps.
+  struct AppData {
+    CString app_id;
+    CString language;
+    CString brand_code;
+    CString client_id;
+    CString installation_id;
+    CString pv;
+    CString experiment_labels;
+  };
+  std::vector<AppData> apps_data_;
+  AppData omaha_data_;
+
+  scoped_ptr<xml::UpdateRequest> ping_request_;
+
+  DISALLOW_COPY_AND_ASSIGN(Ping);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_PING_H_
+
diff --git a/common/ping_event.cc b/common/ping_event.cc
new file mode 100644
index 0000000..83a2b8a
--- /dev/null
+++ b/common/ping_event.cc
@@ -0,0 +1,66 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/ping_event.h"
+#include "omaha/base/string.h"
+#include "omaha/base/xml_utils.h"
+#include "omaha/common/xml_const.h"
+
+namespace omaha {
+
+HRESULT PingEvent::ToXml(IXMLDOMNode* parent_node) const {
+  HRESULT hr = AddXMLAttributeNode(parent_node,
+                                   xml::kXmlNamespace,
+                                   xml::attribute::kEventType,
+                                   itostr(event_type_));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = AddXMLAttributeNode(parent_node,
+                           xml::kXmlNamespace,
+                           xml::attribute::kEventResult,
+                           itostr(event_result_));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = AddXMLAttributeNode(parent_node,
+                           xml::kXmlNamespace,
+                           xml::attribute::kErrorCode,
+                           itostr(error_code_));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return AddXMLAttributeNode(parent_node,
+                             xml::kXmlNamespace,
+                             xml::attribute::kExtraCode1,
+                             itostr(extra_code1_));
+}
+
+CString PingEvent::ToString() const {
+  CString ping_str;
+  ping_str.Format(_T("%s=%s, %s=%s, %s=%s, %s=%s"),
+      xml::attribute::kEventType, itostr(event_type_),
+      xml::attribute::kEventResult, itostr(event_result_),
+      xml::attribute::kErrorCode, itostr(error_code_),
+      xml::attribute::kExtraCode1, itostr(extra_code1_));
+
+  return ping_str;
+}
+
+}  // namespace omaha
+
diff --git a/common/ping_event.h b/common/ping_event.h
new file mode 100644
index 0000000..7551418
--- /dev/null
+++ b/common/ping_event.h
@@ -0,0 +1,135 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_PING_EVENT_H_
+#define OMAHA_COMMON_PING_EVENT_H_
+
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "omaha/base/debug.h"
+#include "third_party/bar/shared_ptr.h"
+
+namespace omaha {
+
+class PingEvent {
+ public:
+  // The extra code represents the file order as defined by the setup.
+  static const int kSetupFilesExtraCodeMask = 0x00000100;
+
+  // The extra code represents a state of the app state machine.
+  static const int kAppStateExtraCodeMask   = 0x10000000;
+
+  // When updating this enum, also update the protocol file on the server.
+  // These values get reported to the server, so do not change existing ones.
+  //
+  // Checkpoints:
+  //  "EVENT_INSTALL_*" events report the progress of initial installs.
+  //  "EVENT_UPDATE_*" events report the progress of silent updates.
+  //  These checkpoints represent the "START" or "FINISH" of a phase.
+  // Actions:
+  //  "EVENT_*_BEGIN" events report the start of a specific action (i.e. job).
+  //  "EVENT_*_COMPLETE" events represent the end of such actions and report
+  // successful completion or the error that occurred during the action.
+  enum Types {
+    EVENT_UNKNOWN = 0,
+    EVENT_INSTALL_DOWNLOAD_FINISH = 1,
+    EVENT_INSTALL_COMPLETE = 2,
+    EVENT_UPDATE_COMPLETE = 3,
+    EVENT_UNINSTALL = 4,
+    EVENT_INSTALL_DOWNLOAD_START = 5,
+    EVENT_INSTALL_INSTALLER_START = 6,
+    // Never used = 7
+    // No longer used - EVENT_INSTALLED_GOOPDATE_STARTED = 8,
+    EVENT_INSTALL_APPLICATION_BEGIN = 9,
+
+    // Install Setup events.
+    // No longer used - EVENT_SETUP_INSTALL_BEGIN = 10,
+    // No longer used - EVENT_SETUP_INSTALL_COMPLETE = 11,
+
+    // Update Events.
+    // The Update Event = 3 above is used for update completion.
+    EVENT_UPDATE_APPLICATION_BEGIN = 12,
+    EVENT_UPDATE_DOWNLOAD_START = 13,
+    EVENT_UPDATE_DOWNLOAD_FINISH = 14,
+    EVENT_UPDATE_INSTALLER_START = 15,
+
+    // Self-update Setup events.
+    // No longer used - EVENT_SETUP_UPDATE_BEGIN = 16,
+    // No longer used - EVENT_SETUP_UPDATE_COMPLETE = 17,
+
+    // Ping when installed via /registerproduct.
+    EVENT_REGISTER_PRODUCT_COMPLETE = 20,
+
+    // Ping when an end user first boots a new system with an OEM-installed app.
+    EVENT_INSTALL_OEM_FIRST_CHECK = 30,
+
+    // App Command Events
+    EVENT_APP_COMMAND_BEGIN = 40,
+    EVENT_APP_COMMAND_COMPLETE = 41,
+
+    // Failure report events - not part of the normal flow.
+    // No longer used - EVENT_SETUP_INSTALL_FAILURE = 100,
+    // No longer used - EVENT_GOOPDATE_DLL_FAILURE = 101,
+    // No longer used - EVENT_SETUP_COM_SERVER_FAILURE = 102,
+    // No longer used - EVENT_SETUP_UPDATE_FAILURE = 103,
+  };
+
+  // When updating this enum, also update the identical one in
+  // omaha_extensions.proto.
+  // These values get reported to the server, so do not change existing ones.
+  enum Results {
+    EVENT_RESULT_ERROR = 0,
+    EVENT_RESULT_SUCCESS = 1,
+    EVENT_RESULT_SUCCESS_REBOOT = 2,
+    //  EVENT_RESULT_SUCCESS_RESTART_BROWSER = 3,
+    EVENT_RESULT_CANCELLED = 4,
+    EVENT_RESULT_INSTALLER_ERROR_MSI = 5,
+    EVENT_RESULT_INSTALLER_ERROR_OTHER = 6,
+    //  EVENT_RESULT_NOUPDATE = 7,
+    EVENT_RESULT_INSTALLER_ERROR_SYSTEM = 8,
+    EVENT_RESULT_UPDATE_DEFERRED = 9,
+    EVENT_RESULT_HANDOFF_ERROR = 10,
+  };
+
+  PingEvent(Types type,
+            Results result,
+            int error_code,
+            int extra_code1)
+      : event_type_(type),
+        event_result_(result),
+        error_code_(error_code),
+        extra_code1_(extra_code1) {
+    ASSERT1(EVENT_UNKNOWN != event_type_);
+  }
+  virtual ~PingEvent() {}
+
+  virtual HRESULT ToXml(IXMLDOMNode* parent_node) const;
+  virtual CString ToString() const;
+
+ private:
+  const Types event_type_;
+  const Results event_result_;
+  const int error_code_;
+  const int extra_code1_;
+};
+
+typedef shared_ptr<const PingEvent> PingEventPtr;
+typedef std::vector<PingEventPtr> PingEventVector;
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_PING_EVENT_H_
+
diff --git a/common/ping_test.cc b/common/ping_test.cc
new file mode 100644
index 0000000..f073f7e
--- /dev/null
+++ b/common/ping_test.cc
@@ -0,0 +1,366 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <string.h>
+#include "omaha/base/string.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/command_line.h"
+#include "omaha/common/ping.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+class PingTest : public testing::Test {
+};
+
+TEST_F(PingTest, BuildOmahaPing) {
+  PingEventPtr ping_event1(
+      new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE,
+                    PingEvent::EVENT_RESULT_SUCCESS,
+                    10,
+                    20));
+
+  PingEventPtr ping_event2(
+      new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE,
+                    PingEvent::EVENT_RESULT_SUCCESS,
+                    30,
+                    40));
+
+  CommandLineExtraArgs command_line_extra_args;
+  StringToGuidSafe(_T("{DE06587E-E5AB-4364-A46B-F3AC733007B3}"),
+                   &command_line_extra_args.installation_id);
+  command_line_extra_args.brand_code = _T("GGLS");
+  command_line_extra_args.client_id  = _T("a client id");
+  command_line_extra_args.language   = _T("en");
+
+  // Machine ping.
+  Ping install_ping(true, _T("session"), _T("oneclick"));
+  install_ping.LoadAppDataFromExtraArgs(command_line_extra_args);
+  install_ping.BuildOmahaPing(_T("1.0.0.0"),
+                              _T("2.0.0.0"),
+                              ping_event1,
+                              ping_event2);
+
+  CString expected_ping_request_substring;
+  expected_ping_request_substring.Format(_T("<app appid=\"{430FD4D0-B729-4F61-AA34-91526481799D}\" version=\"1.0.0.0\" nextversion=\"2.0.0.0\" lang=\"en\" brand=\"GGLS\" client=\"a client id\" iid=\"{DE06587E-E5AB-4364-A46B-F3AC733007B3}\"><event eventtype=\"2\" eventresult=\"1\" errorcode=\"10\" extracode1=\"20\"/><event eventtype=\"2\" eventresult=\"1\" errorcode=\"30\" extracode1=\"40\"/></app>"));  // NOLINT
+
+  CString actual_ping_request;
+  install_ping.BuildRequestString(&actual_ping_request);
+
+  // The ping_request_string contains some data that depends on the machine
+  // environment, such as operating system version. Look for a partial match in
+  // the string corresponding to the <app> element.
+  EXPECT_NE(-1, actual_ping_request.Find(expected_ping_request_substring));
+}
+
+TEST_F(PingTest, BuildAppsPing) {
+  const TCHAR* const kOmahaUserClientStatePath =
+      _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+      _T("\\ClientState\\") GOOPDATE_APP_ID;
+
+  const CString expected_pv           = _T("1.3.23.0");
+  const CString expected_lang         = _T("en");
+  const CString expected_brand_code   = _T("GGLS");
+  const CString expected_client_id    = _T("someclientid");
+  const CString expected_iid          =
+      _T("{7C0B6E56-B24B-436b-A960-A6EA201E886F}");
+  const CString expected_experiment_label =
+      _T("some_experiment=a|Fri, 14 Aug 2015 16:13:03 GMT");
+
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                            kRegValueProductVersion,
+                                            expected_pv));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                            kRegValueLanguage,
+                                            expected_lang));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                            kRegValueBrandCode,
+                                            expected_brand_code));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                            kRegValueClientId,
+                                            expected_client_id));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                            kRegValueInstallationId,
+                                            expected_iid));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                            kRegValueExperimentLabels,
+                                            expected_experiment_label));
+
+  PingEventPtr ping_event(
+      new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE,
+                    PingEvent::EVENT_RESULT_SUCCESS,
+                    34,
+                    6));
+
+  Ping apps_ping(false, _T("unittest"), _T("InstallSource_Foo"));
+  std::vector<CString> apps;
+  apps.push_back(GOOPDATE_APP_ID);
+  apps_ping.LoadAppDataFromRegistry(apps);
+  apps_ping.BuildAppsPing(ping_event);
+
+  CString expected_ping_request_substring;
+  expected_ping_request_substring.Format(_T("<app appid=\"{430FD4D0-B729-4F61-AA34-91526481799D}\" version=\"1.3.23.0\" nextversion=\"\" lang=\"en\" brand=\"GGLS\" client=\"someclientid\" experiments=\"some_experiment=a|Fri, 14 Aug 2015 16:13:03 GMT\" iid=\"{7C0B6E56-B24B-436b-A960-A6EA201E886F}\"><event eventtype=\"2\" eventresult=\"1\" errorcode=\"34\" extracode1=\"6\"/></app>"));  // NOLINT
+
+  CString actual_ping_request;
+  apps_ping.BuildRequestString(&actual_ping_request);
+
+  EXPECT_NE(-1, actual_ping_request.Find(expected_ping_request_substring));
+}
+
+TEST_F(PingTest, SendString) {
+  CString request_string = _T("<?xml version=\"1.0\" encoding=\"UTF-8\"?><request protocol=\"3.0\" version=\"1.3.23.0\" ismachine=\"1\" sessionid=\"unittest\" installsource=\"oneclick\" testsource=\"dev\" requestid=\"{EC821C33-E4EE-4E75-BC85-7E9DFC3652F5}\" periodoverridesec=\"7407360\"><os platform=\"win\" version=\"6.0\" sp=\"Service Pack 1\"/><app appid=\"{430FD4D0-B729-4F61-AA34-91526481799D}\" version=\"1.0.0.0\" nextversion=\"2.0.0.0\" lang=\"en\" brand=\"GGLS\" client=\"a client id\" iid=\"{DE06587E-E5AB-4364-A46B-F3AC733007B3}\"><event eventtype=\"10\" eventresult=\"1\" errorcode=\"0\" extracode1=\"0\"/></app></request>");   // NOLINT
+  EXPECT_HRESULT_SUCCEEDED(Ping::SendString(false,
+                                            HeadersVector(),
+                                            request_string));
+
+  // 400 Bad Request returned by the server.
+  EXPECT_EQ(0x80042190, Ping::SendString(false, HeadersVector(), _T("")));
+}
+
+TEST_F(PingTest, HandlePing) {
+  CString request_string = _T("<?xml version=\"1.0\" encoding=\"UTF-8\"?><request protocol=\"3.0\" version=\"1.3.23.0\" ismachine=\"1\" sessionid=\"unittest\" installsource=\"oneclick\" testsource=\"dev\" requestid=\"{EC821C33-E4EE-4E75-BC85-7E9DFC3652F5}\" periodoverridesec=\"7407360\"><os platform=\"win\" version=\"6.0\" sp=\"Service Pack 1\"/><app appid=\"{430FD4D0-B729-4F61-AA34-91526481799D}\" version=\"1.0.0.0\" nextversion=\"2.0.0.0\" lang=\"en\" brand=\"GGLS\" client=\"a client id\" iid=\"{DE06587E-E5AB-4364-A46B-F3AC733007B3}\"><event eventtype=\"10\" eventresult=\"1\" errorcode=\"0\" extracode1=\"0\"/></app></request>");   // NOLINT
+
+  CStringA request_string_utf8(WideToUtf8(request_string));
+  CStringA ping_string_utf8;
+  WebSafeBase64Escape(request_string_utf8, &ping_string_utf8);
+
+  EXPECT_HRESULT_SUCCEEDED(
+      Ping::HandlePing(false, Utf8ToWideChar(ping_string_utf8,
+                                             ping_string_utf8.GetLength())));
+
+  // 400 Bad Request returned by the server.
+  EXPECT_EQ(0x80042190, Ping::HandlePing(false, _T("")));
+}
+
+TEST_F(PingTest, SendInProcess) {
+  PingEventPtr ping_event(
+      new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE,
+                    PingEvent::EVENT_RESULT_SUCCESS,
+                    0,
+                    0));
+
+  CommandLineExtraArgs command_line_extra_args;
+  StringToGuidSafe(_T("{DE06587E-E5AB-4364-A46B-F3AC733007B3}"),
+                   &command_line_extra_args.installation_id);
+  command_line_extra_args.brand_code = _T("GGLS");
+  command_line_extra_args.client_id  = _T("a client id");
+  command_line_extra_args.language   = _T("en");
+
+  // User ping.
+  Ping install_ping(false, _T("unittest"), _T("oneclick"));
+  install_ping.LoadAppDataFromExtraArgs(command_line_extra_args);
+  install_ping.BuildOmahaPing(_T("1.0.0.0"), _T("2.0.0.0"), ping_event);
+
+  CString request_string;
+  EXPECT_HRESULT_SUCCEEDED(install_ping.BuildRequestString(&request_string));
+  EXPECT_HRESULT_SUCCEEDED(install_ping.SendInProcess(request_string));
+}
+
+TEST_F(PingTest, IsPingExpired_PastTime) {
+  const time64 time = GetCurrent100NSTime() - (Ping::kPingExpiry100ns + 1);
+  EXPECT_TRUE(Ping::IsPingExpired(time));
+}
+
+TEST_F(PingTest, IsPingExpired_CurrentTime) {
+  const time64 time = GetCurrent100NSTime();
+  EXPECT_FALSE(Ping::IsPingExpired(time));
+}
+
+TEST_F(PingTest, IsPingExpired_FutureTime) {
+  const time64 time = GetCurrent100NSTime() + 10;
+  EXPECT_TRUE(Ping::IsPingExpired(time));
+}
+
+TEST_F(PingTest, LoadPersistedPings_NoPersistedPings) {
+  Ping::PingsVector pings;
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            Ping::LoadPersistedPings(false, &pings));
+  EXPECT_EQ(0, pings.size());
+}
+
+TEST_F(PingTest, LoadPersistedPings) {
+  CString ping_reg_path(Ping::GetPingRegPath(false));
+
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(ping_reg_path,
+                                            _T("1"),
+                                            _T("Test Ping String 1")));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(ping_reg_path,
+                                            _T("2"),
+                                            _T("Test Ping String 2")));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(ping_reg_path,
+                                            _T("3"),
+                                            _T("Test Ping String 3")));
+
+  Ping::PingsVector pings;
+  EXPECT_HRESULT_SUCCEEDED(Ping::LoadPersistedPings(false, &pings));
+  EXPECT_EQ(3, pings.size());
+
+  EXPECT_EQ(1, pings[0].first);
+  EXPECT_EQ(2, pings[1].first);
+  EXPECT_EQ(3, pings[2].first);
+  EXPECT_STREQ(_T("Test Ping String 1"), pings[0].second);
+  EXPECT_STREQ(_T("Test Ping String 2"), pings[1].second);
+  EXPECT_STREQ(_T("Test Ping String 3"), pings[2].second);
+
+  EXPECT_HRESULT_SUCCEEDED(RegKey::DeleteKey(ping_reg_path));
+}
+
+TEST_F(PingTest, PersistPing) {
+  EXPECT_HRESULT_SUCCEEDED(Ping::PersistPing(false, _T("Test Ping String 1")));
+  ::Sleep(15);
+  EXPECT_HRESULT_SUCCEEDED(Ping::PersistPing(false, _T("Test Ping String 2")));
+  ::Sleep(15);
+  EXPECT_HRESULT_SUCCEEDED(Ping::PersistPing(false, _T("Test Ping String 3")));
+
+  Ping::PingsVector pings;
+  EXPECT_HRESULT_SUCCEEDED(Ping::LoadPersistedPings(false, &pings));
+  EXPECT_EQ(3, pings.size());
+
+  EXPECT_FALSE(Ping::IsPingExpired(pings[0].first));
+  EXPECT_FALSE(Ping::IsPingExpired(pings[1].first));
+  EXPECT_FALSE(Ping::IsPingExpired(pings[2].first));
+  EXPECT_STREQ(_T("Test Ping String 1"), pings[0].second);
+  EXPECT_STREQ(_T("Test Ping String 2"), pings[1].second);
+  EXPECT_STREQ(_T("Test Ping String 3"), pings[2].second);
+
+  EXPECT_HRESULT_SUCCEEDED(RegKey::DeleteKey(Ping::GetPingRegPath(false)));
+}
+
+TEST_F(PingTest, DeletePersistedPing) {
+  CString ping_reg_path(Ping::GetPingRegPath(false));
+
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(ping_reg_path,
+                                            _T("1"),
+                                            _T("Test Ping String 1")));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(ping_reg_path,
+                                            _T("2"),
+                                            _T("Test Ping String 2")));
+
+  EXPECT_HRESULT_SUCCEEDED(Ping::DeletePersistedPing(false, 1));
+  EXPECT_HRESULT_SUCCEEDED(Ping::DeletePersistedPing(false, 2));
+
+  EXPECT_FALSE(RegKey::HasKey(ping_reg_path));
+}
+
+TEST_F(PingTest, SendPersistedPings) {
+  PingEventPtr ping_event(
+      new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE,
+                    PingEvent::EVENT_RESULT_SUCCESS,
+                    0,
+                    0));
+
+  CommandLineExtraArgs command_line_extra_args;
+  StringToGuidSafe(_T("{DE06587E-E5AB-4364-A46B-F3AC733007B3}"),
+                   &command_line_extra_args.installation_id);
+  command_line_extra_args.brand_code = _T("GGLS");
+  command_line_extra_args.client_id  = _T("a client id");
+  command_line_extra_args.language   = _T("en");
+
+  // User ping.
+  Ping install_ping(false, _T("unittest"), _T("oneclick"));
+  install_ping.LoadAppDataFromExtraArgs(command_line_extra_args);
+  install_ping.BuildOmahaPing(_T("1.0.0.0"), _T("2.0.0.0"), ping_event);
+
+  CString request_string;
+  EXPECT_HRESULT_SUCCEEDED(install_ping.BuildRequestString(&request_string));
+  EXPECT_HRESULT_SUCCEEDED(Ping::PersistPing(false, request_string));
+
+  EXPECT_HRESULT_SUCCEEDED(Ping::SendPersistedPings(false));
+
+  EXPECT_FALSE(RegKey::HasKey(Ping::GetPingRegPath(false)));
+}
+
+// The tests below rely on the out-of-process mechanism to send install pings.
+// Enable the test to debug the sending code.
+TEST_F(PingTest, DISABLED_SendUsingGoogleUpdate) {
+  PingEventPtr ping_event(
+      new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE,
+                    PingEvent::EVENT_RESULT_SUCCESS,
+                    0,
+                    0));
+
+  CommandLineExtraArgs command_line_extra_args;
+  StringToGuidSafe(_T("{DE06587E-E5AB-4364-A46B-F3AC733007B3}"),
+                   &command_line_extra_args.installation_id);
+  command_line_extra_args.brand_code = _T("GGLS");
+  command_line_extra_args.client_id  = _T("a client id");
+  command_line_extra_args.language   = _T("en");
+
+  // User ping and wait for completion.
+  Ping install_ping(false, _T("unittest"), _T("oneclick"));
+  install_ping.LoadAppDataFromExtraArgs(command_line_extra_args);
+  install_ping.BuildOmahaPing(_T("1.0.0.0"), _T("2.0.0.0"), ping_event);
+
+  const int kWaitForPingProcessToCompleteMs = 60000;
+  CString request_string;
+  EXPECT_HRESULT_SUCCEEDED(install_ping.BuildRequestString(&request_string));
+  EXPECT_HRESULT_SUCCEEDED(install_ping.SendUsingGoogleUpdate(
+      request_string, kWaitForPingProcessToCompleteMs));
+}
+
+TEST_F(PingTest, Send_Empty) {
+  CommandLineExtraArgs command_line_extra_args;
+  Ping install_ping(false, _T("unittest"), _T("oneclick"));
+  EXPECT_EQ(S_FALSE, install_ping.Send(false));
+}
+
+TEST_F(PingTest, DISABLED_Send) {
+  PingEventPtr ping_event(
+      new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE,
+                    PingEvent::EVENT_RESULT_SUCCESS,
+                    0,
+                    0));
+
+  CommandLineExtraArgs command_line_extra_args;
+  StringToGuidSafe(_T("{DE06587E-E5AB-4364-A46B-F3AC733007B3}"),
+                   &command_line_extra_args.installation_id);
+  command_line_extra_args.brand_code = _T("GGLS");
+  command_line_extra_args.client_id  = _T("a client id");
+  command_line_extra_args.language   = _T("en");
+
+  // User ping and wait for completion.
+  Ping install_ping(false, _T("unittest"), _T("oneclick"));
+  install_ping.LoadAppDataFromExtraArgs(command_line_extra_args);
+  install_ping.BuildOmahaPing(_T("1.0.0.0"), _T("2.0.0.0"), ping_event);
+
+  EXPECT_HRESULT_SUCCEEDED(install_ping.Send(false));
+}
+
+TEST_F(PingTest, DISABLED_SendFireAndForget) {
+  PingEventPtr ping_event(
+      new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE,
+                    PingEvent::EVENT_RESULT_SUCCESS,
+                    0,
+                    0));
+
+  CommandLineExtraArgs command_line_extra_args;
+  StringToGuidSafe(_T("{DE06587E-E5AB-4364-A46B-F3AC733007B3}"),
+                   &command_line_extra_args.installation_id);
+  command_line_extra_args.brand_code = _T("GGLS");
+  command_line_extra_args.client_id  = _T("a client id");
+  command_line_extra_args.language   = _T("en");
+
+  // User ping and do not wait for completion.
+  Ping install_ping(false, _T("unittest"), _T("oneclick"));
+  install_ping.LoadAppDataFromExtraArgs(command_line_extra_args);
+  install_ping.BuildOmahaPing(_T("1.0.0.0"), _T("2.0.0.0"), ping_event);
+
+  EXPECT_HRESULT_SUCCEEDED(install_ping.Send(true));
+}
+
+}  // namespace omaha
+
diff --git a/common/popup_menu.cc b/common/popup_menu.cc
deleted file mode 100644
index 7b9c00a..0000000
--- a/common/popup_menu.cc
+++ /dev/null
@@ -1,464 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/popup_menu.h"
-
-#include <windows.h>
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/string.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-// Hold owner draw data
-struct OwnerDrawData {
-  HFONT font;
-  CString text;
-  HICON icon;
-
-  OwnerDrawData(HFONT f, const TCHAR* t, HICON i) {
-    font = f;
-    text = t;
-    icon = i;
-  }
-};
-
-
-// Default constructor
-PopupMenu::PopupMenu()
-    : wnd_(NULL) {
-  reset(menu_, ::CreatePopupMenu());
-  ASSERT1(menu_);
-}
-
-// Constructor
-PopupMenu::PopupMenu(HINSTANCE inst, const TCHAR* name)
-    : wnd_(NULL) {
-  LoadFromResource(inst, name);
-  ASSERT1(menu_);
-}
-
-// Destructor
-PopupMenu::~PopupMenu() {
-}
-
-// Load from resource
-bool PopupMenu::LoadFromResource(HINSTANCE inst, const TCHAR* name) {
-  reset(menu_, GetSubMenu(reinterpret_cast<HMENU>(::LoadMenu(inst, name)), 0));
-  return get(menu_) != NULL;
-}
-
-// Append menu item
-bool PopupMenu::AppendMenuItem(int menu_item_id, const TCHAR* text) {
-  return AppendMenuItem(menu_item_id, text, NULL);
-}
-
-// Append menu item
-bool PopupMenu::AppendMenuItem(int menu_item_id,
-                               const TCHAR* text,
-                               const MenuItemDrawStyle* style) {
-  int count = ::GetMenuItemCount(get(menu_));
-  if (count == -1)
-    return false;
-
-  return InsertMenuItem(menu_item_id, count, true, text, style);
-}
-
-// Append separator
-bool PopupMenu::AppendSeparator() {
-  return AppendMenuItem(-1, NULL, NULL);
-}
-
-// Insert menu item
-bool PopupMenu::InsertMenuItem(int menu_item_id,
-                               int before_item,
-                               bool by_pos,
-                               const TCHAR* text) {
-  return InsertMenuItem(menu_item_id, before_item, by_pos, text, NULL);
-}
-
-// Helper function that populates the MENUITEMINFO structure and sets
-// accelerator keys for OWNERDRAW menu items
-MENUITEMINFO PopupMenu::PrepareMenuItemInfo(int menu_item_id, const TCHAR* text,
-                                            const MenuItemDrawStyle* style) {
-  // Fill in the MENUITEMINFO structure
-  MENUITEMINFO menuitem_info;
-  SetZero(menuitem_info);
-  menuitem_info.cbSize = sizeof(MENUITEMINFO);
-  menuitem_info.wID = menu_item_id;
-  if (text == NULL) {
-    menuitem_info.fMask = MIIM_FTYPE | MIIM_ID;
-    menuitem_info.fType = MFT_SEPARATOR;
-  } else {
-    if (!style) {
-      menuitem_info.fMask = MIIM_STRING | MIIM_ID;
-      menuitem_info.fType = MFT_STRING;
-      menuitem_info.dwTypeData = const_cast<TCHAR*>(text);
-    } else {
-      // Handle bold font style
-      HFONT font = NULL;
-      if (style->is_bold) {
-        font = GetBoldFont();
-      }
-
-      // Remove '&' if it is there
-      CString text_str(text);
-      int pos = String_FindChar(text_str, _T('&'));
-      if (pos != -1) {
-        if (pos + 1 < text_str.GetLength()) {
-          accelerator_keys_.Add(Char_ToLower(text_str[pos + 1]), menu_item_id);
-        }
-        ReplaceCString(text_str, _T("&"), _T(""));
-      }
-
-      // Set owner-draw related properties
-      OwnerDrawData* data = new OwnerDrawData(font, text_str, style->icon);
-      menuitem_info.fMask = MIIM_FTYPE | MIIM_DATA | MIIM_ID;
-      menuitem_info.fType = MFT_OWNERDRAW;
-      menuitem_info.dwItemData = reinterpret_cast<ULONG_PTR>(data);
-    }
-  }
-
-  return menuitem_info;
-}
-
-// Insert menu item
-bool PopupMenu::InsertMenuItem(int menu_item_id,
-                               int before_item,
-                               bool by_pos,
-                               const TCHAR* text,
-                               const MenuItemDrawStyle* style) {
-  MENUITEMINFO menuitem_info = PrepareMenuItemInfo(menu_item_id, text, style);
-  if (!::InsertMenuItem(get(menu_), before_item, by_pos, &menuitem_info))
-    return false;
-
-  return Redraw();
-}
-
-// Insert separator
-bool PopupMenu::InsertSeparator(int before_item, bool by_pos) {
-  return InsertMenuItem(-1, before_item, by_pos, NULL, NULL);
-}
-
-// Modify a given menu item
-bool PopupMenu::ModifyMenuItem(int menu_item, bool by_pos, const TCHAR* text,
-                               const MenuItemDrawStyle* style) {
-  // Get OWNERDRAW data for later deletion
-  MENUITEMINFO menuitem_info;
-  SetZero(menuitem_info);
-  menuitem_info.cbSize = sizeof(MENUITEMINFO);
-  menuitem_info.fMask = MIIM_FTYPE | MIIM_DATA;
-  if (!::GetMenuItemInfo(get(menu_), menu_item, by_pos, &menuitem_info)) {
-    return false;
-  }
-
-  OwnerDrawData* old_owner_data = NULL;
-  if ((menuitem_info.fType | MFT_OWNERDRAW) && menuitem_info.dwItemData) {
-    old_owner_data =
-        reinterpret_cast<OwnerDrawData *>(menuitem_info.dwItemData);
-  }
-
-  // Remove old accelerator mapping
-  int menu_item_id = by_pos ? ::GetMenuItemID(get(menu_), menu_item) :
-                              menu_item;
-  int key_pos = accelerator_keys_.FindVal(menu_item_id);
-  if (key_pos != -1) {
-    accelerator_keys_.RemoveAt(key_pos);
-  }
-
-  // Set new menu item info
-  menuitem_info = PrepareMenuItemInfo(menu_item_id, text, style);
-  if (!::SetMenuItemInfo(get(menu_), menu_item, by_pos, &menuitem_info)) {
-    return false;
-  }
-
-  // Delete old owner draw data
-  if (old_owner_data) {
-    delete old_owner_data;
-  }
-
-  // Redraw
-  return Redraw();
-}
-
-// Remove a menu item
-bool PopupMenu::RemoveMenuItem(int menu_item, bool by_pos) {
-  // Get OWNERDRAW data for later deletion
-  MENUITEMINFO menuitem_info;
-  SetZero(menuitem_info);
-  menuitem_info.cbSize = sizeof(MENUITEMINFO);
-  menuitem_info.fMask = MIIM_FTYPE | MIIM_DATA;
-  if (!::GetMenuItemInfo(get(menu_), menu_item, by_pos, &menuitem_info)) {
-    return false;
-  }
-
-  OwnerDrawData* old_owner_data = NULL;
-  if ((menuitem_info.fType | MFT_OWNERDRAW) && menuitem_info.dwItemData) {
-    old_owner_data =
-        reinterpret_cast<OwnerDrawData *>(menuitem_info.dwItemData);
-  }
-
-  // Remove the menu item
-  if (!::RemoveMenu(get(menu_), menu_item, by_pos ? MF_BYPOSITION :
-                                                    MF_BYCOMMAND)) {
-    return false;
-  }
-
-  // Remove old accelerator mapping
-  int menu_item_id = by_pos ? ::GetMenuItemID(get(menu_), menu_item) :
-                              menu_item;
-  int key_pos = accelerator_keys_.FindVal(menu_item_id);
-  if (key_pos != -1) {
-    accelerator_keys_.RemoveAt(key_pos);
-  }
-
-  // Delete old owner draw data
-  if (old_owner_data) {
-    delete old_owner_data;
-  }
-
-  // Redraw
-  return Redraw();
-}
-
-// Enable menu item
-bool PopupMenu::EnableMenuItem(int menu_item, bool by_pos, bool enabled) {
-  if (::EnableMenuItem(get(menu_), menu_item,
-                        (by_pos ? MF_BYPOSITION : MF_BYCOMMAND) |
-                        (enabled ? MF_ENABLED : MF_GRAYED)) == -1)
-    return false;
-
-  return Redraw();
-}
-
-// Get menu state
-bool PopupMenu::GetMenuState(int menu_item, bool by_pos, int* menu_state) {
-  int state = ::GetMenuState(get(menu_),
-                             menu_item, by_pos ? MF_BYPOSITION : MF_BYCOMMAND);
-  if (menu_state)
-    *menu_state = state;
-  return state != -1;
-}
-
-// Exists a menu item
-bool PopupMenu::ExistsMenuItem(int menu_item_id) {
-  return GetMenuState(menu_item_id, false, NULL);
-}
-
-// Redraw menu
-bool PopupMenu::Redraw() {
-  if (!wnd_)
-    return true;
-
-  return ::DrawMenuBar(wnd_) == TRUE;
-}
-
-// Track menu
-bool PopupMenu::Track() {
-  ASSERT1(wnd_);
-
-  // If we don't set it to be foreground, it will not stop tracking even
-  // if we click outside of menu.
-  ::SetForegroundWindow(wnd_);
-
-  POINT point = {0, 0};
-  VERIFY(::GetCursorPos(&point), (_T("")));
-
-  uint32 kFlags = TPM_LEFTALIGN  |
-                  TPM_RETURNCMD  |
-                  TPM_NONOTIFY   |
-                  TPM_LEFTBUTTON |
-                  TPM_VERTICAL;
-  int command = ::TrackPopupMenuEx(get(menu_),
-                                   kFlags,
-                                   point.x, point.y, wnd_, NULL);
-
-  if (command != 0)
-    ::SendMessage(wnd_, WM_COMMAND, command, 0);
-
-  return true;
-}
-
-// Handle WM_MEASUREITEM message
-bool PopupMenu::OnMeasureItem(MEASUREITEMSTRUCT* mi) {
-  ASSERT1(wnd_);
-
-  // Get owner draw data
-  ASSERT1(mi->itemData);
-  OwnerDrawData* data = reinterpret_cast<OwnerDrawData*>(mi->itemData);
-
-  // Get the DC
-  scoped_hdc dc;
-  reset(dc, ::GetDC(wnd_));
-
-  // Select the font
-  HFONT old_font = reinterpret_cast<HFONT>(::SelectObject(get(dc), data->font));
-  if (!old_font)
-    return false;
-
-  // compute the size of the text
-  SIZE size = {0, 0};
-  bool success = ::GetTextExtentPoint32(get(dc),
-                                        data->text.GetString(),
-                                        data->text.GetLength(),
-                                        &size) != 0;
-  if (success) {
-    mi->itemWidth = size.cx;
-    mi->itemHeight = size.cy;
-  }
-
-  // deselect the title font
-  ::SelectObject(get(dc), old_font);
-
-  return success;
-}
-
-// Handle WM_MDRAWITEM message
-bool PopupMenu::OnDrawItem(DRAWITEMSTRUCT* di) {
-  ASSERT1(di);
-
-  // Get owner draw data
-  ASSERT1(di->itemData);
-  OwnerDrawData* data = reinterpret_cast<OwnerDrawData*>(di->itemData);
-
-  // Select the font
-  HFONT prev_font = NULL;
-  if (data->font) {
-    prev_font = reinterpret_cast<HFONT>(::SelectObject(di->hDC, data->font));
-    if (!prev_font) {
-      return false;
-    }
-  }
-
-  // Draw the text per the menuitem state
-  int fg_color_idx =
-      (di->itemState & ODS_DISABLED) ?
-      COLOR_GRAYTEXT :
-      ((di->itemState & ODS_SELECTED) ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT);
-
-  int bg_color_idx =
-      (di->itemState & ODS_SELECTED) ? COLOR_HIGHLIGHT : COLOR_MENU;
-
-  bool success = DrawText(data->text, di, fg_color_idx, bg_color_idx);
-
-  // Restore the original font
-  if (prev_font) {
-    ::SelectObject(di->hDC, prev_font);
-  }
-
-  // Compute the width and height
-  int height = di->rcItem.bottom - di->rcItem.top + 1;
-  int width = static_cast<int>(::GetSystemMetrics(SM_CXMENUCHECK) *
-    (static_cast<double>(height) / ::GetSystemMetrics(SM_CYMENUCHECK)));
-
-  // Draw the icon
-  // TODO(omaha): Draw a grayed icon when the menuitem is disabled
-  if (success && data->icon) {
-    success = ::DrawIconEx(di->hDC,
-                           di->rcItem.left,
-                           di->rcItem.top,
-                           data->icon,
-                           width,
-                           height,
-                           0,
-                           NULL,
-                           DI_NORMAL) != 0;
-  }
-
-  return success;
-}
-
-// Draw the text
-bool PopupMenu::DrawText(const CString& text,
-                         DRAWITEMSTRUCT* di,
-                         int fg_color_idx,
-                         int bg_color_idx) {
-  // Set the appropriate foreground and background colors
-  COLORREF prev_fg_color = 0, prev_bg_color = 0;
-  prev_fg_color = ::SetTextColor(di->hDC, ::GetSysColor(fg_color_idx));
-  if (prev_fg_color == CLR_INVALID) {
-    return false;
-  }
-  prev_bg_color = ::SetBkColor(di->hDC, ::GetSysColor(bg_color_idx));
-  if (prev_bg_color == CLR_INVALID) {
-    return false;
-  }
-
-  // Draw the text
-  bool success = ::ExtTextOut(
-      di->hDC,
-      di->rcItem.left + ::GetSystemMetrics(SM_CXMENUCHECK) + 4,
-      di->rcItem.top,
-      ETO_OPAQUE,
-      &di->rcItem,
-      text.GetString(),
-      text.GetLength(),
-      NULL) == TRUE;
-
-  // Restore the original colors
-  ::SetTextColor(di->hDC, prev_fg_color);
-  ::SetBkColor(di->hDC, prev_bg_color);
-
-  return success;
-}
-
-// Handle WM_MENUCHAR message
-int PopupMenu::OnMenuChar(TCHAR key) {
-  int pos = accelerator_keys_.FindKey(Char_ToLower(key));
-  if (pos != -1)
-    return GetMenuPosFromID(accelerator_keys_.GetValueAt(pos));
-  else
-    return -1;
-}
-
-HFONT PopupMenu::GetBoldFont() {
-  if (!bold_font_) {
-    NONCLIENTMETRICS ncm;
-    SetZero(ncm);
-    ncm.cbSize = sizeof(NONCLIENTMETRICS);
-    if (::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0)) {
-      ncm.lfMenuFont.lfWeight = FW_BOLD;
-      reset(bold_font_, ::CreateFontIndirect(&ncm.lfMenuFont));
-    } else {
-      UTIL_LOG(LEVEL_ERROR, (_T("[PopupMenu::GetBoldFont]")
-                             _T("[failed to get system menu font][0x%x]"),
-                             HRESULTFromLastError()));
-    }
-  }
-
-  ASSERT1(bold_font_);
-
-  return get(bold_font_);
-}
-
-// Get menu pos from ID
-int PopupMenu::GetMenuPosFromID(int id) {
-  ASSERT1(id >= 0);
-  int count = ::GetMenuItemCount(get(menu_));
-  if (count > 0) {
-    for (int pos = 0; pos < count; ++pos) {
-      if (::GetMenuItemID(get(menu_), pos) == static_cast<UINT>(id)) {
-        return pos;
-      }
-    }
-  }
-
-  return -1;
-}
-
-}  // namespace omaha
-
diff --git a/common/popup_menu.h b/common/popup_menu.h
deleted file mode 100644
index 74c40c3..0000000
--- a/common/popup_menu.h
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef  OMAHA_COMMON_POPUP_MENU_H_
-#define  OMAHA_COMMON_POPUP_MENU_H_
-
-#include "omaha/common/debug.h"
-#include "omaha/common/scoped_any.h"
-
-namespace omaha {
-
-struct MenuItemDrawStyle {
-  bool is_bold;
-  HICON icon;
-
-  MenuItemDrawStyle() : is_bold(false), icon(NULL) {}
-};
-
-
-class PopupMenu {
- public:
-  PopupMenu();
-  PopupMenu(HINSTANCE inst, const TCHAR* name);
-
-  ~PopupMenu();
-
-  // Load from resource
-  bool LoadFromResource(HINSTANCE inst, const TCHAR* name);
-
-  // Append menu item
-  bool AppendMenuItem(int menu_item_id, const TCHAR* text);
-  bool AppendMenuItem(int menu_item_id,
-                      const TCHAR* text,
-                      const MenuItemDrawStyle* style);
-
-  // Append separator
-  bool AppendSeparator();
-
-  // Helper function that populates the MENUITEMINFO structure and sets
-  // accelerator keys for OWNERDRAW menu items
-  MENUITEMINFO PrepareMenuItemInfo(int menu_item_id,
-                                   const TCHAR* text,
-                                   const MenuItemDrawStyle* style);
-
-  // Insert menu item
-  bool InsertMenuItem(int menu_item_id,
-                      int before_item,
-                      bool by_pos,
-                      const TCHAR* text);
-
-  bool InsertMenuItem(int menu_item_id,
-                      int before_item,
-                      bool by_pos,
-                      const TCHAR* text,
-                      const MenuItemDrawStyle* style);
-
-  // Insert separator
-  bool InsertSeparator(int before_item, bool by_pos);
-
-  // Modify a given menu item
-  bool ModifyMenuItem(int menu_item,
-                      bool by_pos,
-                      const TCHAR* text,
-                      const MenuItemDrawStyle* style);
-
-  // Remove menu item
-  bool RemoveMenuItem(int menu_item, bool by_pos);
-
-  // Enable menu item
-  bool EnableMenuItem(int menu_item, bool by_pos, bool enabled);
-
-  // Get menu state
-  bool GetMenuState(int menu_item, bool by_pos, int* menu_state);
-
-  // Exists a menu item
-  bool ExistsMenuItem(int menu_item_id);
-
-  // Get menu pos from ID
-  int GetMenuPosFromID(int id);
-
-  // Attach to the window
-  void AttachToWindow(HWND wnd) {
-    ASSERT1(wnd);
-    wnd_ = wnd;
-  }
-
-  // Redraw menu
-  bool Redraw();
-
-  // Track menu
-  bool Track();
-
-  // Handle WM_MEASUREITEM message
-  bool OnMeasureItem(MEASUREITEMSTRUCT* mi);
-
-  // Handle WM_MDRAWITEM message
-  bool OnDrawItem(DRAWITEMSTRUCT* di);
-
-  // Handle WM_MENUCHAR message
-  int OnMenuChar(TCHAR key);
-
- private:
-  // Get bold font
-  HFONT GetBoldFont();
-
-  // Draw the text
-  bool DrawText(const CString& text,
-                DRAWITEMSTRUCT* di,
-                int fg_color_idx,
-                int bg_color_idx);
-
-  HWND wnd_;           // HWND associated with this menu
-  scoped_hmenu menu_;  // HMENU associated with this menu
-
-  // Bold font used in owner draw menu-item
-  scoped_hfont bold_font_;
-
-  // Accelerator key used in owner draw menu-item
-  CSimpleMap<TCHAR, int> accelerator_keys_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(PopupMenu);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_POPUP_MENU_H_
-
diff --git a/common/proc_utils.cc b/common/proc_utils.cc
deleted file mode 100644
index f362c33..0000000
--- a/common/proc_utils.cc
+++ /dev/null
@@ -1,463 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// proc_utils.cpp
-//
-// Useful functions that relate to process/thread manipulation/information
-// (Originally moved from utils.cpp)
-
-#include "omaha/common/proc_utils.h"
-
-#include <psapi.h>
-#include "base/scoped_ptr.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/const_config.h"
-#include "omaha/common/const_timeouts.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/process.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/string.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/window_utils.h"
-
-namespace omaha {
-
-ProcessTerminator::ProcessTerminator(const CString& process_name)
-    : recursion_level_(0),
-      process_name_(process_name),
-      flash_window_(false),
-      session_id_(INVALID_SESSION_ID) {
-  MakeLowerCString(process_name_);
-}
-
-ProcessTerminator::ProcessTerminator(const CString& process_name,
-                                     const CString& user_sid)
-    : recursion_level_(0),
-      user_sid_(user_sid),
-      process_name_(process_name),
-      flash_window_(false),
-      session_id_(INVALID_SESSION_ID) {
-  MakeLowerCString(process_name_);
-}
-
-ProcessTerminator::ProcessTerminator(const CString& process_name,
-                                     const CString& user_sid,
-                                     int session_id)
-    : recursion_level_(0),
-      user_sid_(user_sid),
-      session_id_(session_id),
-      process_name_(process_name),
-      flash_window_(false) {
-  MakeLowerCString(process_name_);
-}
-
-ProcessTerminator::~ProcessTerminator() {
-  CloseAllHandles();
-}
-
-// Will close all currently opened handles.
-void ProcessTerminator::CloseAllHandles() {
-  UTIL_LOG(L3, (_T("[CloseAllHandles]")));
-  // Do clean up if we have opened handles.
-  for (size_t i = 0; i < process_handles_.size(); i++) {
-    VERIFY1(::CloseHandle(process_handles_[i]));
-  }
-
-  process_handles_.clear();
-}
-
-// Wait for a while till all process instances will die.
-bool ProcessTerminator::WaitForProcessInstancesToDie(
-    uint32 timeout_msec) const {
-  UTIL_LOG(L3, (_T("[WaitForProcessInstancesToDie]")));
-  size_t size = process_handles_.size();
-  scoped_array<HANDLE> handles(new HANDLE[size]);
-
-  for (size_t i = 0; i < size; i++) {
-    handles[i] = process_handles_[i];
-  }
-
-  DWORD wait_result = ::WaitForMultipleObjectsEx(size,
-                                                 handles.get(),
-                                                 true,
-                                                 timeout_msec,
-                                                 false);
-#pragma warning(disable : 4296)
-// C4296: '>=' : expression is always true
-  if ((wait_result >= WAIT_OBJECT_0) &&
-      (wait_result < WAIT_OBJECT_0 + size)) {
-    return true;
-  }
-#pragma warning(default : 4296)
-
-  UTIL_LOG(L3, (_T("WaitForProcessToDie timed out for '%s'. Waited for %d ms."),
-                process_name_, timeout_msec));
-  return false;
-}
-
-// Finds all process ids for the process of a given name.
-bool ProcessTerminator::FindProcessInstances() {
-  UTIL_LOG(L3, (_T("[FindProcessInstances]")));
-
-  DWORD exclude_mask = EXCLUDE_CURRENT_PROCESS;
-  if (!user_sid_.IsEmpty()) {
-    exclude_mask |= INCLUDE_ONLY_PROCESS_OWNED_BY_USER;
-  }
-
-  std::vector<CString> command_lines;
-  HRESULT hr = S_OK;
-  if (session_id_ != INVALID_SESSION_ID) {
-    hr = Process::FindProcessesInSession(session_id_,
-                                         exclude_mask,
-                                         process_name_,
-                                         true,
-                                         user_sid_,
-                                         command_lines,
-                                         &process_ids_);
-  } else {
-    hr = Process::FindProcesses(exclude_mask,
-                                process_name_,
-                                true,
-                                user_sid_,
-                                command_lines,
-                                &process_ids_);
-  }
-
-  return SUCCEEDED(hr) && !process_ids_.empty();
-}
-
-// Tries to kill all instances of the process that was specified in the
-// constructor.
-// 'method_mask' determines which technique to attempt.
-// 'was_found' is optional and can be NULL.
-// Returns S_OK if all instances were killed, S_FALSE if process wasn't running,
-// and E_FAIL if one or more instances weren't killed.
-// Always sets 'was_found' correctly, regardless of return value.
-HRESULT ProcessTerminator::KillTheProcess(uint32 timeout_msec,
-                                          bool* was_found,
-                                          uint32 method_mask,
-                                          bool flash_window) {
-  UTIL_LOG(L3, (_T("[KillTheProcess]")));
-  if (!FindProcessInstances()) {
-    if (was_found != NULL) {
-      *was_found = false;
-    }
-    return S_FALSE;  // process is not running, so don't return a FAILED hr
-  }
-
-  // If got here, found at least one process to kill
-  if (was_found != NULL) {
-    *was_found = true;
-  }
-
-  flash_window_ = flash_window;
-  // Try the nicest, cleanest method of closing a process: window messages
-  if (method_mask & KILL_METHOD_1_WINDOW_MESSAGE) {
-    if (PrepareToKill(KILL_METHOD_1_WINDOW_MESSAGE)) {
-      KillProcessViaWndMessages(timeout_msec);
-    }
-
-    // Are any instances of the process still running?
-    if (!FindProcessInstances()) {
-      return S_OK;  // killed them all
-    }
-  }
-
-  // Also nice method
-  if (method_mask & KILL_METHOD_2_THREAD_MESSAGE) {
-    if (PrepareToKill(KILL_METHOD_2_THREAD_MESSAGE)) {
-      KillProcessViaThreadMessages(timeout_msec);
-    }
-    // Are any instances of the process still running?
-    if (!FindProcessInstances()) {
-      return S_OK;  // killed them all
-    }
-  }
-
-  // the crude one.
-  if (method_mask & KILL_METHOD_4_TERMINATE_PROCESS) {
-    if (PrepareToKill(KILL_METHOD_4_TERMINATE_PROCESS)) {
-      KillProcessViaTerminate(timeout_msec);
-    }
-    // Are any instances of the process still running?
-    if (!FindProcessInstances()) {
-      return S_OK;  // killed them all
-    }
-
-    UTIL_LOG(LEVEL_ERROR, (_T("[ProcessTerminator::KillTheProcess]")
-                           _T("[totally unable to kill process '%s']"),
-                           process_name_));
-  }
-
-  return E_FAIL;
-}
-
-HRESULT ProcessTerminator::WaitForAllToDie(uint32 timeout_msec) {
-  UTIL_LOG(L3, (_T("[WaitForAllToDie]")));
-  if (!FindProcessInstances()) {
-    return S_OK;
-  }
-
-  if (PrepareToKill(KILL_METHOD_1_WINDOW_MESSAGE)) {
-    return WaitForProcessInstancesToDie(timeout_msec) ? S_OK :
-              HRESULT_FROM_WIN32(WAIT_TIMEOUT);
-  }
-
-  return E_FAIL;
-}
-
-// Given process_ids array will try to
-// open handle to each instance.
-// Leaves process handles open (in member process_handles_)
-// Will use access rights for opening appropriate for the purpose_of_opening.
-// This function recursively calls itself if by the time it tries to open
-// handles to process instances some of the processes died or naturally exited.
-bool ProcessTerminator::PrepareToKill(uint32 method_mask) {
-  UTIL_LOG(L3, (_T("[PrepareToKill]")));
-  uint32 desired_access = 0;
-
-  if (method_mask & KILL_METHOD_4_TERMINATE_PROCESS) {
-    desired_access = SYNCHRONIZE       |
-                     PROCESS_TERMINATE |
-                     PROCESS_QUERY_INFORMATION;
-  } else {
-    desired_access = SYNCHRONIZE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
-  }
-
-  // do clean up in case some handles are opened.
-  CloseAllHandles();
-
-  if (process_ids_.empty()) {
-    // no instances are running.
-    return false;
-  }
-
-  for (size_t i = 0; i < process_ids_.size(); i++) {
-    HANDLE handle = ::OpenProcess(desired_access, false, process_ids_[i]);
-    if (handle) {
-      process_handles_.push_back(handle);
-    } else {
-      if (::GetLastError() == ERROR_ACCESS_DENIED) {
-        // If we are here that means that we do not have enough priveleges to
-        // open the process for a given kill method. No reason to attempt other
-        // instances. Just clean up and return false.
-        UTIL_LOG(L3, (_T("PrepareToKill failed for '%s'. Kill method %d."),
-                      process_name_, method_mask));
-        CloseAllHandles();
-        return false;
-      }
-    }
-  }
-  // We already handled the case when we don't have enough privileges to open
-  // the process. So if we have less handles than process ids -> some of the
-  // processes have died since we made a snapshot untill the time we tried to
-  // open handles. We need to do another snapshot and try to open handles one
-  // more time. We need number of handles and number of ids to be equal.
-  // We can do it with recursion. The idea is: make the next snapshot and open
-  // handles. Hopefully the number will be equal. Stop recursion at the third
-  // level.
-
-  if (process_handles_.size() != process_ids_.size()) {
-    recursion_level_++;
-
-    // we have a disbalance here. This is pretty bad.
-    // Some of the processes died already so let's try
-    // to balance them.
-    if (!FindProcessInstances()) {
-      // they are all dead.
-      recursion_level_ = 0;
-      return false;
-    }
-
-    // try to balance three times no more.
-    if (recursion_level_ >= 3) {
-      recursion_level_ = 0;
-      UTIL_LOG(L3, (_T("Recursion level too deep in PrepareToKill for '%s'."),
-                    process_name_));
-      return false;
-    }
-
-    // recursively call the function
-    return PrepareToKill(method_mask);
-  }
-  recursion_level_ = 0;
-  return true;
-}
-
-// ProcessTerminator::FindProcessWindows
-// Just calls enumeration function
-bool ProcessTerminator::FindProcessWindows() {
-  window_handles_.clear();
-  return ::EnumWindows(EnumAllWindowsProc, reinterpret_cast<LPARAM>(this)) &&
-         !window_handles_.empty();
-}
-
-// ProcessTerminator::EnumAllWindowsProc
-// During enumeration this function will try to find a match between
-// process id we already found and process id obtained from each window.
-// if there is a match, we record the window in an array
-BOOL ProcessTerminator::EnumAllWindowsProc(HWND hwnd, LPARAM lparam) {
-  ProcessTerminator* this_pointer =
-        reinterpret_cast<ProcessTerminator*>(lparam);
-  ASSERT1(this_pointer);
-
-  uint32 process_id = 0;
-  uint32 thread_id =
-    ::GetWindowThreadProcessId(hwnd, reinterpret_cast<DWORD*>(&process_id));
-
-  typedef std::vector<uint32>::const_iterator ProcessIdIterator;
-  for (ProcessIdIterator it = this_pointer->process_ids_.begin();
-       it != this_pointer->process_ids_.end();
-       ++it) {
-    if (*it == process_id) {
-      // The main idea is: Find all top level windows (NO PARENT!!!)
-      // AND this windows must have system menu and be visible. So we make sure
-      // that we send WM_CLOSE ONLY to the windows that user might close
-      // interactively. This way we are safe. The last thing to check is if it
-      // is tr hidden window.
-      if (WindowUtils::IsMainWindow(hwnd) && WindowUtils::HasSystemMenu(hwnd)) {
-        this_pointer->window_handles_.push_back(hwnd);
-      }
-    }
-  }
-  return TRUE;
-}
-
-// ProcessTerminator::KillProcessViaWndMessages()
-// try to post a windows message
-bool  ProcessTerminator::KillProcessViaWndMessages(uint32 timeout_msec) {
-  UTIL_LOG(L3, (_T("[KillProcessViaWndMessages]")));
-  if (!FindProcessWindows()) {
-    UTIL_LOG(L1, (_T("[KillProcessViaWndMessages]")
-                  _T("[failed to find any windows for '%s']"), process_name_));
-    return false;
-  }
-
-  bool post_messages_succeeded = false;
-
-  for (size_t i = 0; i < window_handles_.size(); i++) {
-    // Previous method used WM_CLOSE, WM_SYSCOMMAND+SC_CLOSE is slightly better.
-    // It closes our apps, and also works correctly on AOL!
-    if (::PostMessage(window_handles_[i], WM_SYSCOMMAND, SC_CLOSE, 0)) {
-      if (flash_window_) {
-        UTIL_LOG(L3, (_T("[PostMessageSucceeded flashing window]")));
-        ::FlashWindow(window_handles_[i], true);
-      }
-      post_messages_succeeded = true;
-    }
-  }
-
-  if (!post_messages_succeeded) {
-    UTIL_LOG(L3, (_T("[KillProcessViaWndMessages]")
-                  _T("[failed to PostMessage to windows of '%s']"),
-                  process_name_));
-  }
-  // If we succeeded in posting message at least one time we have to wait.
-  // We don't know the relationship between windows in the process.
-  return post_messages_succeeded && WaitForProcessInstancesToDie(timeout_msec);
-}
-
-// Try to post a thread message.
-bool ProcessTerminator::KillProcessViaThreadMessages(uint32 timeout_msec) {
-  UTIL_LOG(L3, (_T("[KillProcessViaThreadMessages]")));
-  std::vector<uint32> thread_ids;
-
-  if (!FindProcessThreads(&thread_ids)) {
-    UTIL_LOG(L3, (_T("[KillProcessViaThreadMessages]")
-                  _T("[failed to find any threads for '%s']"), process_name_));
-    return false;
-  }
-
-  bool post_messages_succeeded = false;
-  for (size_t i = 0; i < thread_ids.size(); i++) {
-    if (::PostThreadMessage(thread_ids[i], WM_CLOSE, 0, 0)) {
-      post_messages_succeeded = true;
-    }
-  }
-
-  if (!post_messages_succeeded) {
-    UTIL_LOG(L3, (_T("[KillProcessViaWndMessages]")
-                  _T("[failed to PostMessage to threads of '%s'."),
-                  process_name_));
-  }
-  // If we succeded in posting message to at least one thread we have to wait.
-  // We don't know the relationship between threads in the process.
-  return post_messages_succeeded && WaitForProcessInstancesToDie(timeout_msec);
-}
-
-// find all the threads running in a given process.
-bool ProcessTerminator::FindProcessThreads(std::vector<uint32>* thread_ids) {
-  HANDLE process_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
-  if (process_snapshot == INVALID_HANDLE_VALUE) {
-    return false;
-  }
-
-  THREADENTRY32 thread_info = {0};  // zero it out just in case.
-  thread_info.dwSize = sizeof(THREADENTRY32);
-
-  if (::Thread32First(process_snapshot, &thread_info))  {
-    do {
-      for (std::vector<uint32>::const_iterator it = process_ids_.begin();
-           it != process_ids_.end(); ++it) {
-        if (*it == thread_info.th32OwnerProcessID) {
-          // we have found it.
-          thread_ids->push_back(thread_info.th32ThreadID);
-        }
-      }
-      // system changes this value, do not forget to reset to
-      // max possible.
-      thread_info.dwSize = sizeof(THREADENTRY32);
-    } while (::Thread32Next(process_snapshot, &thread_info));
-  }
-
-  return !thread_ids->empty();
-}
-
-// Last and crude method to kill the process. Should be used only
-// if all other methods have failed.
-bool ProcessTerminator::KillProcessViaTerminate(uint32 timeout_msec) {
-  UTIL_LOG(L3, (_T("[KillProcessViaTerminate]")));
-  bool at_least_one_terminated = false;
-
-  for (size_t i = 0; i < process_handles_.size(); i++) {
-    if (!::TerminateProcess(process_handles_[i], 0)) {
-      UTIL_LOG(L3, (_T("[KillProcessViaTerminate]")
-                    _T("[failed for instance of '%s'][System error %d]"),
-                    process_name_, ::GetLastError()));
-    } else {
-       at_least_one_terminated = true;
-    }
-  }
-  return at_least_one_terminated ? WaitForProcessInstancesToDie(timeout_msec) :
-                                   false;
-}
-
-HRESULT SetProcessSilentShutdown() {
-  DWORD shut_down_level(0), shut_down_flags(0);
-  if (!::GetProcessShutdownParameters(&shut_down_level, &shut_down_flags)) {
-    return HRESULTFromLastError();
-  }
-  shut_down_flags |= SHUTDOWN_NORETRY;
-  if (!::SetProcessShutdownParameters(shut_down_level, shut_down_flags)) {
-    return HRESULTFromLastError();
-  }
-  return S_OK;
-}
-
-}  // namespace omaha
-
diff --git a/common/proc_utils_unittest.cc b/common/proc_utils_unittest.cc
deleted file mode 100644
index 3b5c618..0000000
--- a/common/proc_utils_unittest.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/proc_utils.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(ProcUtilsTest, SetSilentShutdown) {
-  ASSERT_HRESULT_SUCCEEDED(SetProcessSilentShutdown());
-}
-
-}  // namespace omaha
-
diff --git a/common/process.cc b/common/process.cc
deleted file mode 100644
index 2680be6..0000000
--- a/common/process.cc
+++ /dev/null
@@ -1,1631 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Defines class Process to incapsulate win32
-// functions for creation and some manipulations of
-// processes.
-
-#include "omaha/common/process.h"
-
-#include <ntsecapi.h>
-#include <psapi.h>
-#include <stierr.h>
-#include <tlhelp32.h>
-#include <vector>
-
-#ifndef NT_SUCCESS
-#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
-#endif
-
-#include "omaha/common/debug.h"
-#include "omaha/common/disk.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/string.h"
-#include "omaha/common/system.h"
-#include "omaha/common/system_info.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/window_utils.h"
-
-namespace omaha {
-
-const int kNumRetriesToFindProcess = 4;
-const int kFindProcessRetryIntervalMs = 500;
-const int kMaxCmdLineLengthBytes = 4096;
-
-// Constructor
-Process::Process(const TCHAR* name, const TCHAR* window_class_name)
-    : process_id_(0),
-      exit_code_(0),
-      number_of_restarts_(static_cast<uint32>(-1)),
-      name_(name),
-      shutdown_event_(NULL) {
-  ASSERT1(name);
-
-  command_line_ = name;
-  window_class_name_ = window_class_name;
-}
-
-// Constructor
-Process::Process(uint32 process_id)
-    : process_id_(process_id),
-      exit_code_(0),
-      number_of_restarts_(static_cast<uint32>(-1)),
-      name_(itostr(static_cast<uint32>(process_id))),
-      shutdown_event_(NULL) {
-  reset(process_, ::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
-                                false,
-                                process_id));
-  if (!valid(process_)) {
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[Process::Process - failed to open process][%u][0x%x]"),
-              process_id, HRESULTFromLastError()));
-  }
-}
-
-// Destructor
-Process::~Process() {
-}
-
-// Start with command params
-bool Process::Start(const TCHAR* command_line_parameters) {
-  return StartWithHwnd(command_line_parameters, NULL);
-}
-
-// Start with command params and specific hwnd
-bool Process::StartWithHwnd(const TCHAR* command_line_parameters, HWND hwnd) {
-  // command_line_parameters may be NULL
-  // hwnd may be NULL
-
-  // Can't start the same process twice in the same
-  // containing object.
-  if (Running()) {
-    return false;
-  }
-
-  // Add command line params if any.
-  if (command_line_parameters && *command_line_parameters) {
-    command_line_parameters_ = command_line_parameters;
-  }
-
-  // just reuse the existing function, don't repeat the code.
-  number_of_restarts_ = static_cast<uint32>(-1);
-  time_of_start_      = GetTickCount();
-  return Restart(hwnd);
-}
-
-// Restart with the old command params
-bool Process::Restart(HWND hwnd) {
-  // Can't start the same process twice in the same
-  // containing object.
-  if (Running()) {
-    return false;
-  }
-
-  // start the process.
-  HRESULT hr = System::ShellExecuteProcess(command_line_,
-                                           command_line_parameters_,
-                                           hwnd,
-                                           address(process_));
-
-  if (SUCCEEDED(hr)) {
-    process_id_ = GetProcessIdFromHandle(get(process_));
-    ASSERT1(process_id_);
-    number_of_restarts_++;
-  } else {
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("System::ShellExecuteProcess '%s' failed with 0x%08x"),
-             command_line_, hr));
-  }
-
-  return SUCCEEDED(hr);
-}
-
-// Check if the process is running.
-bool Process::Running() const {
-  if (!get(process_)) {
-    return false;
-  }
-
-  return (::WaitForSingleObject(get(process_), 0) == WAIT_TIMEOUT);
-}
-
-// Create a job and assign the process to it
-HANDLE Process::AssignToJob() {
-  // Make sure that the process handle is valid
-  if (!get(process_)) {
-    return false;
-  }
-
-  // Create a job
-  scoped_job job(::CreateJobObject(NULL, NULL));
-  if (!valid(job)) {
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[Process::AssignToJob - CreateJobObject failed][0x%x]"),
-              HRESULTFromLastError()));
-    return false;
-  }
-
-  // Assign the process to the job
-  if (!::AssignProcessToJobObject(get(job), get(process_))) {
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[Process::AssignToJob-AssignProcessToJobObject fail][0x%x]"),
-              HRESULTFromLastError()));
-    return false;
-  }
-
-  return release(job);
-}
-
-// Wait till the process finishes
-bool Process::WaitUntilDead(uint32 timeout_msec) {
-  ASSERT1(timeout_msec);
-
-  if (!get(process_)) {
-    return false;
-  }
-
-  uint32 ret = 0;
-  if (shutdown_event_) {
-    HANDLE wait_handles[2] = {0};
-    wait_handles[0] = get(process_);
-    wait_handles[1] = shutdown_event_;
-    ret = ::WaitForMultipleObjectsEx(2,
-                                     wait_handles,
-                                     false,
-                                     timeout_msec,
-                                     true);
-  } else {
-    ret = ::WaitForSingleObjectEx(get(process_), timeout_msec, true);
-  }
-  if (ret == WAIT_OBJECT_0) {
-    UTIL_LOG(L2, (_T("[Process::WaitUntilDead - succeeded to wait process]")
-                  _T("[%s]"), GetName()));
-    return true;
-  } else if (ret == WAIT_IO_COMPLETION) {
-    UTIL_LOG(LEVEL_ERROR, (_T("[Process::WaitUntilDead-recv APC][%s][%u][%u]"),
-                           GetName(), process_id_));
-    return false;
-  } else {
-    UTIL_LOG(LEVEL_ERROR, (_T("[Process::WaitUntilDead - fail to wait process,")
-                           _T("possibly timeout][%s][%u][%u]"),
-                           GetName(), process_id_, ret));
-    return false;
-  }
-}
-
-// Wait some time till the process and all its descendent processes finish
-//
-// Background:
-//   Some process might spawn another process and get itself terminated
-// without waiting the descendant process to finish.
-//
-// Args:
-//   job:                Job to which the process is assigned
-//                       AssignToJob() will be called when NULL value is passed
-//   timeout_msec:       Timeout value in msec
-//   path_to_exclude:    Path of descendant process to excluded from waiting
-//                       (this should be in long format)
-//   exit_code:          To hold the exit code being returned
-bool Process::WaitUntilAllDead(HANDLE job,
-                               uint32 timeout_msec,
-                               const TCHAR* path_to_exclude,
-                               uint32* exit_code) {
-  ASSERT1(timeout_msec);
-
-  UTIL_LOG(L2, (_T("[Process::WaitUntilAllDead][%u][%s]"),
-                timeout_msec, path_to_exclude));
-
-  if (exit_code) {
-  *exit_code = 0;
-  }
-
-  scoped_job job_guard;
-  if (!job) {
-    reset(job_guard, AssignToJob());
-    if (!valid(job_guard)) {
-      return false;
-    }
-    job = get(job_guard);
-  }
-
-  return InternalWaitUntilAllDead(job,
-                                  timeout_msec,
-                                  path_to_exclude,
-                                  exit_code);
-}
-
-// Helper function to wait till the process and all its descendent processes
-// finish.
-bool Process::InternalWaitUntilAllDead(HANDLE job,
-                                       uint32 timeout_msec,
-                                       const TCHAR* path_to_exclude,
-                                       uint32* exit_code) {
-  ASSERT1(job);
-  ASSERT1(timeout_msec);
-
-  // Wait until current process finishes
-  if (!WaitUntilDead(timeout_msec)) {
-    return false;
-  }
-
-  // Find descendant process
-  uint32 desc_process_id = GetDescendantProcess(
-                               job,
-                               false,  // child_only
-                               exit_code != NULL,  // sole_descendent
-                               NULL,  // search_name
-                               path_to_exclude);
-
-  if (desc_process_id) {
-    // Open descendent process
-    Process desc_process(desc_process_id);
-
-    // If descendant process dies too soon, do not need to wait for it
-    if (desc_process.Running()) {
-      // Release the parent process handle
-      // This to handle the scenario that Firefox uninstall code will wait till
-      // parent process handle becomes NULL
-      reset(process_);
-
-      UTIL_LOG(L2, (_T("[Process::InternalWaitUntilAllDead]")
-                    _T("[waiting descendant process][%u]"), desc_process_id));
-
-      // Propagate the shutdown event to descendent process
-      if (shutdown_event_) {
-        desc_process.SetShutdownEvent(shutdown_event_);
-      }
-
-      // Wait till descendant process finishes
-      bool wait_ret = desc_process.InternalWaitUntilAllDead(job,
-                                                            timeout_msec,
-                                                            path_to_exclude,
-                                                            exit_code);
-
-      return wait_ret;
-    }
-  }
-
-  // Use the exit code from parent process
-  if (exit_code) {
-  VERIFY1(GetExitCode(exit_code));
-  }
-
-  // Release the parent process handle
-  reset(process_);
-
-  return true;
-}
-
-// Wait until process is dead or a windows message arrives (for use in a message
-// loop while waiting)
-HRESULT Process::WaitUntilDeadOrInterrupt(uint32 msec) {
-  if (!get(process_)) {
-    return E_FAIL;
-  }
-
-  HANDLE events[1] = { get(process_) };
-  uint32 dw = ::MsgWaitForMultipleObjects(1, events, FALSE, msec, QS_ALLEVENTS);
-  switch (dw) {
-    case WAIT_OBJECT_0:
-      return CI_S_PROCESSWAIT_DEAD;
-    case WAIT_OBJECT_0 + 1:
-      return CI_S_PROCESSWAIT_MESSAGE;
-    case WAIT_TIMEOUT:
-      return CI_S_PROCESSWAIT_TIMEOUT;
-    case WAIT_FAILED:
-    default:
-      return E_FAIL;
-  }
-}
-
-#if !SHIPPING
-CString Process::GetDebugInfo() const {
-  return debug_info_;
-}
-#endif
-
-// Return the process ID
-uint32 Process::GetId() const {
-  return process_id_;
-}
-
-// Return the process name
-const TCHAR *Process::GetName() const {
-  return name_;
-}
-
-// Return win32 handle to the process.
-HANDLE Process::GetHandle() const {
-  return get(process_);
-}
-
-// Get process exit code.
-bool Process::GetExitCode(uint32* exit_code) const {
-  ASSERT1(exit_code);
-
-  if (!get(process_)) {
-    return false;
-  }
-
-  if (!::GetExitCodeProcess(get(process_),
-                            reinterpret_cast<DWORD*>(&exit_code_))) {
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[Process::GetExitCode - failed to get exit code][%u][0x%x]"),
-              process_id_, HRESULTFromLastError()));
-    return false;
-  }
-  if (exit_code_ == STILL_ACTIVE) {
-    return false;
-  }
-
-  *exit_code = exit_code_;
-  return true;
-}
-
-// default implementation allows termination
-bool Process::IsTerminationAllowed() const {
-  return true;
-}
-
-// Terminate the process. If wait_for_terminate_msec == 0 return value doesn't
-// mean that the process actualy terminated. It becomes assync. operation.
-// Check the status with Running accessor function in this case.
-bool Process::Terminate(uint32 wait_for_terminate_msec) {
-  if (!Running()) {
-    return true;
-  }
-
-  if (!IsTerminationAllowed()) {
-    return false;
-  }
-
-  if (!::TerminateProcess(get(process_), 1)) {
-    return false;
-  }
-
-  return wait_for_terminate_msec ? WaitUntilDead(wait_for_terminate_msec) :
-                                   true;
-}
-
-// Default returns INFINITE means never restart.
-// Return any number of msec if overwriting
-uint32 Process::GetRestartInterval() const {
-  return INFINITE;
-}
-
-// How many times the process can be restarted
-// in case it crashes. When overriding return any
-// number or INFINITE to restart forever.
-uint32 Process::GetMaxNumberOfRestarts() const {
-  return 0;
-}
-
-// what is the time window for number of crashes returned by
-// GetMaxNumberOfRestarts(). If crashed more that this number of restarts
-// in a specified time window - do not restart it anymore.
-// Default implementation returns INFINITE which means that this is not time
-// based at all, if the process crashed more than the value returned by
-// GetMaxNumberOfRestarts it will not be restarted no matter how long it took.
-uint32 Process::GetTimeWindowForCrashes() const {
-  return INFINITE;
-}
-
-uint32 Process::GetMaxMemory() const {
-  return 0;
-}
-
-// Have we exceeded the number of maximum restarting?
-bool Process::AllowedToRestart() const {
-  uint32 max_number_of_restarts = GetMaxNumberOfRestarts();
-
-  if ((max_number_of_restarts == INFINITE) ||
-     (number_of_restarts_ < max_number_of_restarts)) {
-    return true;
-  }
-
-  // process crashed too many times. Let's look at the rate of crashes.
-  // Maybe we can "forgive" the process if it took some time for it to crash.
-  if ((::GetTickCount() - time_of_start_) < GetTimeWindowForCrashes()) {
-    return false;  // not forgiven
-  }
-
-  // Everything is forgiven. Give the process
-  // new start in life.
-  time_of_start_ = ::GetTickCount();
-  number_of_restarts_ = static_cast<uint32>(-1);
-
-  return true;
-}
-
-// Set shutdown event using in signaling the process watch
-void Process::SetShutdownEvent(HANDLE shutdown_event) {
-  ASSERT1(shutdown_event);
-
-  shutdown_event_ = shutdown_event;
-}
-
-// Set priority class to the process.
-bool Process::SetPriority(uint32 priority_class) const {
-  if (!get(process_)) {
-    return false;
-  }
-
-  VERIFY1(::SetPriorityClass(get(process_), priority_class));
-  return true;
-}
-
-// Try to get a descendant process. Return process id if found.
-uint32 Process::GetDescendantProcess(HANDLE job,
-                                     bool child_only,
-                                     bool sole_descedent,
-                                     const TCHAR* search_name,
-                                     const TCHAR* path_to_exclude) {
-  ASSERT1(job);
-
-  // Find all descendent processes
-  std::vector<ProcessInfo> descendant_processes;
-  if (FAILED(GetAllDescendantProcesses(job,
-                                       child_only,
-                                       search_name,
-                                       path_to_exclude,
-                                       &descendant_processes))) {
-    return 0;
-  }
-
-  // If more than one decendent processes is found, filter out those that are
-  // not direct children. This is because it might be the case that in a very
-  // short period of time, process A spawns B and B spawns C, and we capture
-  // both B and C.
-  std::vector<ProcessInfo> child_processes;
-  typedef std::vector<ProcessInfo>::const_iterator ProcessInfoConstIterator;
-  if (descendant_processes.size() > 1) {
-    for (ProcessInfoConstIterator it(descendant_processes.begin());
-         it != descendant_processes.end(); ++it) {
-      if (it->parent_id == process_id_) {
-        child_processes.push_back(*it);
-      }
-    }
-    if (!child_processes.empty()) {
-      descendant_processes = child_processes;
-    }
-  }
-
-  // Save the debugging information if needed
-#if !SHIPPING
-  if (sole_descedent && descendant_processes.size() > 1) {
-    debug_info_ = _T("More than one descendent process is found for process ");
-    debug_info_ += itostr(process_id_);
-    debug_info_ += _T("\n");
-    for (ProcessInfoConstIterator it(descendant_processes.begin());
-         it != descendant_processes.end(); ++it) {
-      debug_info_.AppendFormat(_T("%u %u %s\n"),
-                               it->process_id,
-                               it->parent_id,
-                               it->exe_file);
-    }
-  }
-#else
-  sole_descedent;   // unreferenced formal parameter
-#endif
-
-  return descendant_processes.empty() ? 0 : descendant_processes[0].process_id;
-}
-
-BOOL Process::IsProcessInJob(HANDLE process_handle,
-                             HANDLE job_handle,
-                             PBOOL result)  {
-  typedef BOOL (WINAPI *Fun)(HANDLE process_handle,
-                             HANDLE job_handle,
-                             PBOOL result);
-
-  HINSTANCE kernel_instance = ::GetModuleHandle(_T("kernel32.dll"));
-  ASSERT1(kernel_instance);
-  Fun pfn = reinterpret_cast<Fun>(::GetProcAddress(kernel_instance,
-                                                   "IsProcessInJob"));
-  ASSERT(pfn, (_T("IsProcessInJob export not found in kernel32.dll")));
-  return pfn ? (*pfn)(process_handle, job_handle, result) : FALSE;
-}
-
-// Try to get all matching descendant processes
-HRESULT Process::GetAllDescendantProcesses(
-                     HANDLE job,
-                     bool child_only,
-                     const TCHAR* search_name,
-                     const TCHAR* path_to_exclude,
-                     std::vector<ProcessInfo>* descendant_processes) {
-  ASSERT1(job);
-  ASSERT1(descendant_processes);
-
-  // Take a snapshot
-  // Note that we do not have a seperate scoped_* type defined to wrap the
-  // handle returned by CreateToolhelp32Snapshot. So scoped_hfile with similar
-  // behavior is used.
-  scoped_hfile process_snap(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
-  if (!process_snap) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[Process::GetAllDescendantProcesses - fail to get snapshot]")
-              _T("[0x%x]"), hr));
-    return hr;
-  }
-
-  // Eumerate all processes in the snapshot
-  PROCESSENTRY32 pe32;
-  SetZero(pe32);
-  pe32.dwSize = sizeof(PROCESSENTRY32);
-  if (!::Process32First(get(process_snap), &pe32)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[Process::GetAllDescendantProcesses - failed to")
-                           _T("get first process][0x%x]"), hr));
-    return hr;
-  }
-
-  do {
-    // Skip process 0 and current process
-    if (pe32.th32ProcessID == 0 || pe32.th32ProcessID == process_id_) {
-      continue;
-    }
-
-    // If searching for child only, perform the check
-    if (child_only && pe32.th32ParentProcessID != process_id_) {
-      continue;
-    }
-
-    // Open the process
-    scoped_process process(::OpenProcess(PROCESS_QUERY_INFORMATION |
-                                         SYNCHRONIZE,
-                                         false,
-                                         pe32.th32ProcessID));
-    if (!valid(process)) {
-      continue;
-    }
-
-    // Determines whether the process is running in the specified job
-    BOOL result = FALSE;
-    if (!IsProcessInJob(get(process), job, &result) || !result) {
-      continue;
-    }
-
-    // Check whether the process is still running
-    if (::WaitForSingleObject(get(process), 0) != WAIT_TIMEOUT) {
-      continue;
-    }
-
-    // Compare the name if needed
-    if (search_name && *search_name) {
-      if (_tcsicmp(pe32.szExeFile, search_name) != 0) {
-        continue;
-      }
-    }
-
-    // If we need to exclude certain path, check it now
-    if (path_to_exclude && *path_to_exclude) {
-      if (IsProcessRunningWithPath(pe32.th32ProcessID, path_to_exclude)) {
-        continue;
-      }
-    }
-
-    // Add to the list
-    ProcessInfo proc_info;
-    proc_info.process_id = pe32.th32ProcessID;
-    proc_info.parent_id = pe32.th32ParentProcessID;
-#if !SHIPPING
-    proc_info.exe_file = pe32.szExeFile;
-#endif
-    descendant_processes->push_back(proc_info);
-  } while (::Process32Next(get(process_snap), &pe32));
-
-  return S_OK;
-}
-
-HRESULT Process::FindProcesses(uint32 exclude_mask,
-                               const TCHAR* search_name,
-                               bool search_main_executable_only,
-                               std::vector<uint32>* process_ids_found) {
-  ASSERT1(process_ids_found);
-  // Remove the only include processes owned by user mask from the exclude
-  // mask. This is needed as this is the behavior expected by the method,
-  // before the addition of the user_sid.
-  exclude_mask &= (~INCLUDE_ONLY_PROCESS_OWNED_BY_USER);
-  std::vector<CString> command_lines;
-  return FindProcesses(exclude_mask, search_name, search_main_executable_only,
-                       _T(""), command_lines, process_ids_found);
-}
-
-bool Process::IsStringPresentInList(const CString& process_command_line,
-                                    const std::vector<CString>& list) {
-  std::vector<CString>::const_iterator iter = list.begin();
-  for (; iter != list.end(); ++iter) {
-    CString value_to_find = *iter;
-
-    // If we are able to open the process command line, then we should
-    // ensure that it does not contain the value that we are looking for.
-    if (process_command_line.Find(value_to_find) != -1) {
-      // Found a match.
-      return true;
-    }
-  }
-
-  return false;
-}
-
-// TODO(omaha): Change the implementation of this method to take in a
-// predicate that determines whether a process should be included in the
-// result set.
-HRESULT Process::FindProcesses(uint32 exclude_mask,
-                               const TCHAR* search_name,
-                               bool search_main_executable_only,
-                               const CString& user_sid,
-                               const std::vector<CString>& command_lines,
-                               std::vector<uint32>* process_ids_found) {
-  ASSERT1(search_name && *search_name);
-  ASSERT1(process_ids_found);
-  ASSERT1(!((exclude_mask & EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING) &&
-            (exclude_mask & INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING)));
-
-  const TCHAR* const kLocalSystemSid = _T("S-1-5-18");
-
-  // Clear the output queue
-  process_ids_found->clear();
-
-  // Get the list of process identifiers.
-  uint32 process_ids[kMaxProcesses];
-  SetZero(process_ids);
-  uint32 bytes_returned = 0;
-  if (!::EnumProcesses(reinterpret_cast<DWORD*>(process_ids),
-                       sizeof(process_ids),
-                       reinterpret_cast<DWORD*>(&bytes_returned))) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[Process::FindProcesses-fail to EnumProcesses]")
-                           _T("[0x%x]"), hr));
-    return hr;
-  }
-
-  // Enumerate all processes
-  int num_processes = bytes_returned / sizeof(process_ids[0]);
-  // We have found an elevated number of crashes in 1.2.584.15114 on what
-  // we believe are Italian systems. The first step to solving this Italian job
-  // is to assert on the condition while we are further testing this.
-  ASSERT1(num_processes <= kMaxProcesses);
-
-  // In Vista, SeDebugPrivilege is required to open the process not owned by
-  // current user. Also required for XP admins to open Local System processes
-  // with PROCESS_QUERY_INFORMATION access rights.
-  System::AdjustPrivilege(SE_DEBUG_NAME, true);
-
-  // Get ID of current process
-  uint32 curr_process_id = ::GetCurrentProcessId();
-
-  // Get SID of current user
-  CString curr_user_sid;
-  HRESULT hr = omaha::user_info::GetCurrentUser(NULL, NULL, &curr_user_sid);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  UTIL_LOG(L4, (_T("[Process::FindProcesses][processes=%d]"), num_processes));
-  for (int i = 0; i < num_processes; ++i) {
-    // Skip the system idle process.
-    if (process_ids[i] == 0) {
-      continue;
-    }
-
-    // Get the owner sid.
-    // Note that we may fail to get the owner which is not current user.
-    // So if the owner_sid is empty, the process is sure not to be owned by the
-    // current user.
-    CString owner_sid;
-    Process::GetProcessOwner(process_ids[i], &owner_sid);
-
-    if ((exclude_mask & INCLUDE_ONLY_PROCESS_OWNED_BY_USER) &&
-      owner_sid != user_sid) {
-      UTIL_LOG(L4,
-          (_T("[Excluding process as not owned by user][%d]"), process_ids[i]));
-      continue;
-    }
-
-    // Skip it if it is owned by the one specified in exclude_mask
-    if ((exclude_mask & EXCLUDE_CURRENT_PROCESS) &&
-        process_ids[i] == curr_process_id) {
-      UTIL_LOG(L4, (_T("[Excluding current process %d"), process_ids[i]));
-      continue;
-    }
-    if ((exclude_mask & EXCLUDE_PROCESS_OWNED_BY_CURRENT_USER) &&
-        owner_sid == curr_user_sid) {
-      UTIL_LOG(L4,
-          (_T("[Excluding process as owned by current user][%d]"),
-           process_ids[i]));
-      continue;
-    }
-    if ((exclude_mask & EXCLUDE_PROCESS_OWNED_BY_SYSTEM) &&
-        owner_sid == kLocalSystemSid) {
-      UTIL_LOG(L4,
-          (_T("[Excluding process as owned by system][%d]"), process_ids[i]));
-      continue;
-    }
-    if (exclude_mask & EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING ||
-        exclude_mask & INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING) {
-      CString process_command_line;
-      HRESULT hr = GetCommandLine(process_ids[i], &process_command_line);
-      if (FAILED(hr)) {
-        UTIL_LOG(L4,
-          (_T("[Excluding process could not get command line][%d]"),
-           process_ids[i]));
-        continue;
-      }
-
-      // If we are able to open the process command line, then we should
-      // ensure that it does not contain the value that we are looking for if
-      // we are excluding the command line or that it contains the command line
-      // that we are looking for in case the include switch is specified.
-      bool present = IsStringPresentInList(process_command_line, command_lines);
-      if ((present &&
-            (exclude_mask & EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING)) ||
-          (!present &&
-            (exclude_mask & INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING))) {
-        UTIL_LOG(L4, (_T("[Process command line matches criteria][%d]'[%s]'"),
-                 process_ids[i], process_command_line));
-        continue;
-      }
-    }
-
-    // If search_name is provided, make sure it matches
-    if (Process::IsProcessUsingExeOrDll(process_ids[i],
-                                        search_name,
-                                        search_main_executable_only)) {
-      UTIL_LOG(L4,
-          (_T("[Including process][%d][%s]"), process_ids[i], search_name));
-      process_ids_found->push_back(process_ids[i]);
-    }
-  }
-
-  return S_OK;
-}
-
-HRESULT Process::FindProcessesInSession(
-    DWORD session_id,
-    uint32 exclude_mask,
-    const TCHAR* search_name,
-    bool search_main_executable_only,
-    const CString& user_sid,
-    const std::vector<CString>& cmd_lines,
-    std::vector<uint32>* process_ids_found) {
-  HRESULT hr = FindProcesses(exclude_mask,
-                             search_name,
-                             search_main_executable_only,
-                             user_sid,
-                             cmd_lines,
-                             process_ids_found);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // Filter to processes running under session_id.
-  std::vector<uint32>::iterator iter = process_ids_found->begin();
-  while (iter != process_ids_found->end()) {
-    uint32 process_pid = *iter;
-    DWORD process_session = 0;
-    hr = S_OK;
-    if (!::ProcessIdToSessionId(process_pid, &process_session)) {
-      hr = HRESULTFromLastError();
-      UTIL_LOG(LE,  (_T("[::ProcessIdToSessionId failed][0x%x]"), hr));
-    }
-
-    if (FAILED(hr) || process_session != session_id) {
-      // Remove from list and continue.
-      iter = process_ids_found->erase(iter);
-      continue;
-    }
-
-    ++iter;
-  }
-
-  return S_OK;
-}
-
-bool Process::IsModuleMatchingExeOrDll(const TCHAR* module_name,
-                                       const TCHAR* search_name,
-                                       bool is_fully_qualified_name) {
-  UTIL_LOG(L4, (_T("[Process::IsModuleMatchingExeOrDll]")
-                _T("[module=%s][search=%s]"), module_name, search_name));
-  CString module_file_name;
-  if (is_fully_qualified_name) {
-    if (FAILED(GetLongPathName(module_name, &module_file_name))) {
-      return false;
-    }
-  } else {
-    module_file_name = ::PathFindFileName(module_name);
-    ASSERT1(!module_file_name.IsEmpty());
-    if (module_file_name.IsEmpty()) {
-      return false;
-    }
-  }
-
-  return (module_file_name.CompareNoCase(search_name) == 0);
-}
-
-DWORD Process::GetProcessImageFileName(HANDLE proc_handle,
-                                       LPTSTR image_file,
-                                       DWORD file_size)  {
-  typedef DWORD (WINAPI *Fun)(HANDLE proc_handle,
-                              LPWSTR image_file,
-                              DWORD file_size);
-
-  HINSTANCE psapi_instance = ::GetModuleHandle(_T("Psapi.dll"));
-  ASSERT1(psapi_instance);
-  Fun pfn = reinterpret_cast<Fun>(::GetProcAddress(psapi_instance,
-                                                   "GetProcessImageFileNameW"));
-  if (!pfn) {
-    UTIL_LOG(L1, (_T("::GetProcessImageFileNameW() not found in Psapi.dll")));
-    return 0;
-  }
-  return (*pfn)(proc_handle, image_file, file_size);
-}
-
-bool Process::IsProcImageMatch(HANDLE proc_handle,
-                               const TCHAR* search_name,
-                               bool is_fully_qualified_name)  {
-  TCHAR image_name[MAX_PATH] = _T("");
-  if (!GetProcessImageFileName(proc_handle,
-                               image_name,
-                               arraysize(image_name))) {
-    UTIL_LOG(L4, (_T("[GetProcessImageFileName fail[0x%x]"),
-                  HRESULTFromLastError()));
-    return false;
-  }
-
-  UTIL_LOG(L4, (_T("[GetProcessImageFileName][%s]"), image_name));
-  CString dos_name;
-  HRESULT hr(DevicePathToDosPath(image_name, &dos_name));
-  if (FAILED(hr)) {
-    UTIL_LOG(L4, (_T("[DevicePathToDosPath fail[0x%x]"), hr));
-    return false;
-  }
-
-  return IsModuleMatchingExeOrDll(dos_name,
-                                  search_name,
-                                  is_fully_qualified_name);
-}
-
-// Is the process using the specified exe/dll?
-bool Process::IsProcessUsingExeOrDll(uint32 process_id,
-                                     const TCHAR* search_name,
-                                     bool search_main_executable_only) {
-  UTIL_LOG(L4, (_T("[Process::IsProcessUsingExeOrDll]")
-                _T("[pid=%d][search_name=%s]"), process_id, search_name));
-  ASSERT1(search_name);
-
-  // Open the process
-  scoped_process process_handle(::OpenProcess(
-                                    PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
-                                    FALSE,
-                                    process_id));
-  if (!process_handle) {
-    UTIL_LOG(L4, (_T("[::OpenProcess failed][0x%x]"), HRESULTFromLastError()));
-    return false;
-  }
-
-  // Does the name represent a fully qualified name?
-  // We only do a simple check here
-  bool is_fully_qualified_name = String_FindChar(search_name, _T('\\')) != -1;
-  CString long_search_name;
-  if (is_fully_qualified_name) {
-    HRESULT hr(GetLongPathName(search_name, &long_search_name));
-    if (FAILED(hr)) {
-      UTIL_LOG(L4, (_T("[GetLongPathName fail][hr=x%x]"), hr));
-      return false;
-    }
-    search_name = long_search_name;
-  }
-
-  // Take a snapshot of all modules in the specified process
-  int num_modules_to_fetch = search_main_executable_only ? 1 :
-                                                           kMaxProcessModules;
-  HMODULE module_handles[kMaxProcessModules];
-  SetZero(module_handles);
-  uint32 bytes_needed = 0;
-  if (!::EnumProcessModules(get(process_handle),
-                            module_handles,
-                            num_modules_to_fetch * sizeof(HMODULE),
-                            reinterpret_cast<DWORD*>(&bytes_needed))) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[EnumProcessModules failed][0x%x]"), hr));
-
-    if (IsWow64(::GetCurrentProcessId())) {
-      // ::EnumProcessModules from a WoW64 process fails for x64 processes.
-      // We try ::GetProcessImageFileName as a workaround here.
-      return search_main_executable_only ?
-                 IsProcImageMatch(get(process_handle),
-                                  search_name,
-                                  is_fully_qualified_name) :
-                 false;
-    } else {
-      return false;
-    }
-  }
-
-  int num_modules = bytes_needed / sizeof(HMODULE);
-  if (num_modules > num_modules_to_fetch) {
-    num_modules = num_modules_to_fetch;
-  }
-
-  for (int i = 0; i < num_modules; ++i) {
-    TCHAR module_name[MAX_PATH];
-    SetZero(module_name);
-    if (!::GetModuleFileNameEx(get(process_handle),
-                               module_handles[i],
-                               module_name,
-                               arraysize(module_name))) {
-      UTIL_LOG(LEVEL_ERROR, (_T("[GetModuleFileNameEx fail[x%x]"),
-                             HRESULTFromLastError()));
-      continue;
-    }
-
-    if (IsModuleMatchingExeOrDll(module_name,
-                                 search_name,
-                                 is_fully_qualified_name)) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-// Helper function to get long path name
-HRESULT Process::GetLongPathName(const TCHAR* short_name, CString* long_name) {
-  ASSERT1(short_name);
-  ASSERT1(long_name);
-
-  TCHAR temp_name[MAX_PATH];
-  SetZero(temp_name);
-
-  HRESULT hr = S_OK;
-  if (!::GetLongPathName(short_name, temp_name, arraysize(temp_name))) {
-    hr = HRESULTFromLastError();
-  } else {
-    long_name->SetString(temp_name);
-  }
-
-  return hr;
-}
-
-// Type definitions needed for GetCommandLine() and GetProcessIdFromHandle()
-// From MSDN document on NtQueryInformationProcess() and other sources
-typedef struct _PROCESS_BASIC_INFORMATION {
-  PVOID Reserved1;
-  BYTE *PebBaseAddress;
-  PVOID Reserved2[2];
-  ULONG_PTR UniqueProcessId;
-  PVOID Reserved3;
-} PROCESS_BASIC_INFORMATION;
-
-typedef enum _PROCESSINFOCLASS {
-  ProcessBasicInformation = 0,
-  ProcessWow64Information = 26
-} PROCESSINFOCLASS;
-
-typedef WINBASEAPI DWORD WINAPI
-GetProcessIdFn(
-    HANDLE Process
-);
-
-typedef LONG WINAPI
-NtQueryInformationProcess(
-  IN HANDLE ProcessHandle,
-  IN PROCESSINFOCLASS ProcessInformationClass,
-  OUT PVOID ProcessInformation,
-  IN ULONG ProcessInformationLength,
-  OUT PULONG ReturnLength OPTIONAL
-);
-
-typedef struct _RTL_DRIVE_LETTER_CURDIR {
-  USHORT Flags;
-  USHORT Length;
-  ULONG TimeStamp;
-  UNICODE_STRING DosPath;
-} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR;
-
-typedef struct _RTL_USER_PROCESS_PARAMETERS {
-  ULONG MaximumLength;
-  ULONG Length;
-  ULONG Flags;
-  ULONG DebugFlags;
-  PVOID ConsoleHandle;
-  ULONG ConsoleFlags;
-  HANDLE StdInputHandle;
-  HANDLE StdOutputHandle;
-  HANDLE StdErrorHandle;
-  UNICODE_STRING CurrentDirectoryPath;
-  HANDLE CurrentDirectoryHandle;
-  UNICODE_STRING DllPath;
-  UNICODE_STRING ImagePathName;
-  UNICODE_STRING CommandLine;
-  PVOID Environment;
-  ULONG StartingPositionLeft;
-  ULONG StartingPositionTop;
-  ULONG Width;
-  ULONG Height;
-  ULONG CharWidth;
-  ULONG CharHeight;
-  ULONG ConsoleTextAttributes;
-  ULONG WindowFlags;
-  ULONG ShowWindowFlags;
-  UNICODE_STRING WindowTitle;
-  UNICODE_STRING DesktopName;
-  UNICODE_STRING ShellInfo;
-  UNICODE_STRING RuntimeData;
-  RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20];
-} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
-
-// Get the function pointer to GetProcessId in KERNEL32.DLL
-static HRESULT EnsureGPIFunction(GetProcessIdFn** gpi_func_ptr) {
-  static GetProcessIdFn* gpi_func = NULL;
-  if (!gpi_func) {
-    HMODULE kernel32_module = ::GetModuleHandle(_T("kernel32.dll"));
-    if (!kernel32_module) {
-      return HRESULTFromLastError();
-    }
-    gpi_func = reinterpret_cast<GetProcessIdFn*>(
-                  ::GetProcAddress(kernel32_module, "GetProcessId"));
-    if (!gpi_func) {
-      return HRESULTFromLastError();
-    }
-  }
-
-  *gpi_func_ptr = gpi_func;
-  return S_OK;
-}
-
-// Get the function pointer to NtQueryInformationProcess in NTDLL.DLL
-static HRESULT EnsureQIPFunction(NtQueryInformationProcess** qip_func_ptr) {
-  static NtQueryInformationProcess* qip_func = NULL;
-  if (!qip_func) {
-    HMODULE ntdll_module = ::GetModuleHandle(_T("ntdll.dll"));
-    if (!ntdll_module) {
-      return HRESULTFromLastError();
-    }
-    qip_func = reinterpret_cast<NtQueryInformationProcess*>(
-                  ::GetProcAddress(ntdll_module, "NtQueryInformationProcess"));
-    if (!qip_func) {
-      return HRESULTFromLastError();
-    }
-  }
-
-  *qip_func_ptr = qip_func;
-  return S_OK;
-}
-
-// Obtain the process ID from a hProcess HANDLE
-ULONG Process::GetProcessIdFromHandle(HANDLE hProcess) {
-  if (SystemInfo::IsRunningOnXPSP1OrLater()) {
-    // Thunk to the documented ::GetProcessId() API
-    GetProcessIdFn* gpi_func = NULL;
-    HRESULT hr = EnsureGPIFunction(&gpi_func);
-    if (FAILED(hr)) {
-      ASSERT(FALSE,
-             (_T("Process::GetProcessIdFromHandle - EnsureGPIFunction")
-              _T(" failed[0x%x]"), hr));
-      return 0;
-    }
-    ASSERT1(gpi_func);
-    return gpi_func(hProcess);
-  }
-
-  // For lower versions of Windows, we use undocumented
-  // function NtQueryInformationProcess to get at the PID
-  NtQueryInformationProcess* qip_func = NULL;
-  HRESULT hr = EnsureQIPFunction(&qip_func);
-  if (FAILED(hr)) {
-    ASSERT(FALSE,
-           (_T("Process::GetProcessIdFromHandle - EnsureQIPFunction")
-            _T(" failed[0x%x]"), hr));
-    return 0;
-  }
-  ASSERT1(qip_func);
-
-  PROCESS_BASIC_INFORMATION info;
-  SetZero(info);
-  if (!NT_SUCCESS(qip_func(hProcess,
-                           ProcessBasicInformation,
-                           &info,
-                           sizeof(info),
-                           NULL))) {
-    ASSERT(FALSE, (_T("Process::GetProcessIdFromHandle - ")
-                   _T("NtQueryInformationProcess failed!")));
-    return 0;
-  }
-
-  return info.UniqueProcessId;
-}
-
-// Get the command line of a process
-HRESULT Process::GetCommandLine(uint32 process_id, CString* cmd_line) {
-  ASSERT1(process_id);
-  ASSERT1(cmd_line);
-
-  // Open the process
-  scoped_process process_handle(::OpenProcess(
-                                    PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
-                                    false,
-                                    process_id));
-  if (!process_handle) {
-    return HRESULTFromLastError();
-  }
-
-  // Obtain Process Environment Block
-  // Note that NtQueryInformationProcess is not available in Windows 95/98/ME
-  NtQueryInformationProcess* qip_func = NULL;
-  HRESULT hr = EnsureQIPFunction(&qip_func);
-
-  if (FAILED(hr)) {
-    return hr;
-  }
-  ASSERT1(qip_func);
-
-  PROCESS_BASIC_INFORMATION info;
-  SetZero(info);
-  if (!NT_SUCCESS(qip_func(get(process_handle),
-                           ProcessBasicInformation,
-                           &info,
-                           sizeof(info),
-                           NULL))) {
-    return E_FAIL;
-  }
-  BYTE* peb = info.PebBaseAddress;
-
-  // Read address of parameters (see some PEB reference)
-  // TODO(omaha): use offsetof(PEB, ProcessParameters) to replace 0x10
-  // http://msdn.microsoft.com/en-us/library/aa813706.aspx
-  SIZE_T bytes_read = 0;
-  uint32 dw = 0;
-  if (!::ReadProcessMemory(get(process_handle),
-                           peb + 0x10,
-                           &dw,
-                           sizeof(dw),
-                           &bytes_read)) {
-    return HRESULTFromLastError();
-  }
-
-  // Read all the parameters
-  RTL_USER_PROCESS_PARAMETERS params;
-  SetZero(params);
-  if (!::ReadProcessMemory(get(process_handle),
-                           reinterpret_cast<PVOID>(dw),
-                           &params,
-                           sizeof(params),
-                           &bytes_read)) {
-    return HRESULTFromLastError();
-  }
-
-  // Read the command line parameter
-  const int max_cmd_line_len = std::min(
-      static_cast<int>(params.CommandLine.MaximumLength),
-      kMaxCmdLineLengthBytes);
-  if (!::ReadProcessMemory(get(process_handle),
-                           params.CommandLine.Buffer,
-                           cmd_line->GetBufferSetLength(max_cmd_line_len),
-                           max_cmd_line_len,
-                           &bytes_read)) {
-    return HRESULTFromLastError();
-  }
-
-  cmd_line->ReleaseBuffer();
-
-  return S_OK;
-}
-
-// Check if the process is running with a specified path
-bool Process::IsProcessRunningWithPath(uint32 process_id, const TCHAR* path) {
-  ASSERT1(process_id);
-  ASSERT1(path && *path);
-
-  const int kProcessWaitModuleFullyUpMs = 100;
-  const int kProcessWaitModuleRetries = 10;
-
-  // Open the process
-  scoped_process process(::OpenProcess(
-                             PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
-                             false,
-                             process_id));
-  if (!process) {
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[Process::IsProcessRunningWithPath - OpenProcess failed]")
-              _T("[%u][0x%x]"),
-             process_id, HRESULTFromLastError()));
-    return false;
-  }
-
-  for (int i = 0; i < kProcessWaitModuleRetries; ++i) {
-    // Get the command line path of the main module
-    // Note that we are using psapi functions which is not supported in Windows
-    // 95/98/ME
-    //
-    // Sometimes it might be the case that the process is created but the main
-    // module is not fully loaded. If so, wait a while and then try again
-    TCHAR process_path[MAX_PATH];
-    if (::GetModuleFileNameEx(get(process),
-                              NULL,
-                              process_path,
-                              arraysize(process_path))) {
-      // Do the check
-      if (String_StartsWith(process_path, path, true)) {
-        return true;
-      }
-
-      // Try again with short form
-      TCHAR short_path[MAX_PATH];
-      if (::GetShortPathName(path, short_path, arraysize(short_path)) &&
-          String_StartsWith(process_path, short_path, true)) {
-        return true;
-      }
-
-      return false;
-    }
-
-    UTIL_LOG(LEVEL_ERROR,
-              (_T("[Process::IsProcessRunningWithPath - GetModuleFileNameEx ")
-               _T("failed][%u][0x%x]"),
-              process_id, HRESULTFromLastError()));
-
-    ::Sleep(kProcessWaitModuleFullyUpMs);
-  }
-
-  UTIL_LOG(LEVEL_ERROR,
-           (_T("[Process::IsProcessRunningWithPath - failed to get process ")
-            _T("path][%u][0x%x]"),
-            process_id, HRESULTFromLastError()));
-
-  return false;
-}
-
-// Get the process owner
-// Note that we may fail to get the owner which is not current user.
-// TODO(omaha): merge with UserInfo::GetCurrentUser
-HRESULT Process::GetProcessOwner(uint32 pid, CString* owner_sid) {
-  ASSERT1(pid);
-  ASSERT1(owner_sid);
-
-  scoped_process process(::OpenProcess(PROCESS_QUERY_INFORMATION, false, pid));
-  if (!valid(process)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[Process::GetProcessOwner - OpenProcess failed][%u][0x%x]"),
-              pid, hr));
-    return hr;
-  }
-
-  scoped_handle process_token;
-  if (!::OpenProcessToken(get(process),
-                          READ_CONTROL | TOKEN_QUERY,
-                          address(process_token))) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(L4,
-             (_T("[Process::GetProcessOwner - OpenProcessToken failed][0x%x]"),
-              hr));
-    return hr;
-  }
-
-  DWORD size_needed = 0;
-  BOOL b = ::GetTokenInformation(get(process_token),
-                                 TokenUser,
-                                 NULL,
-                                 0,
-                                 &size_needed);
-  ASSERT1(!b && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER);
-
-  scoped_array<byte> token_user(new byte[size_needed]);
-  DWORD size_returned = 0;
-  if (!::GetTokenInformation(get(process_token),
-                             TokenUser,
-                             token_user.get(),
-                             size_needed,
-                             &size_returned)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[Process::GetProcessOwner - GetTokenInformation fail][0x%x]"),
-              hr));
-    return hr;
-  }
-
-  PSID process_sid = (reinterpret_cast<TOKEN_USER*>(
-                          token_user.get()))->User.Sid;
-  ASSERT1(process_sid);
-  if (!process_sid) {
-    return E_FAIL;
-  }
-
-  TCHAR* process_sid_str = NULL;
-  if (!::ConvertSidToStringSid(process_sid, &process_sid_str)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[IsOwnedByUser - ConvertSidToStringSid failed][0x%x]"),
-              hr));
-    return hr;
-  }
-  scoped_hlocal scoped_guard_sid_str(process_sid_str);
-
-  *owner_sid = process_sid_str;
-
-  return S_OK;
-}
-
-// Creates an impersonation token for the user running process_id.
-// The caller is responsible for closing the returned handle.
-HRESULT Process::GetImpersonationToken(DWORD process_id, HANDLE* user_token) {
-  // Get a handle to the process.
-  scoped_process process(::OpenProcess(
-                             PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION,
-                             TRUE,
-                             process_id));
-  if (!valid(process)) {
-    HRESULT hr(HRESULTFromLastError());
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[GetImpersonationToken - ::OpenProcess failed][0x%x]"),
-              hr));
-    return hr;
-  }
-
-  HRESULT result = S_OK;
-  scoped_handle process_token;
-  if (!::OpenProcessToken(get(process), TOKEN_DUPLICATE | TOKEN_QUERY,
-                          address(process_token))) {
-    result = HRESULTFromLastError();
-  } else {
-    if (!::DuplicateTokenEx(get(process_token),
-                            TOKEN_IMPERSONATE | TOKEN_QUERY |
-                            TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE,
-                            NULL,
-                            SecurityImpersonation,
-                            TokenPrimary,
-                            user_token)) {
-      result = HRESULTFromLastError();
-    }
-  }
-
-  ASSERT(SUCCEEDED(result), (_T("[GetImpersonationToken Failed][hr=0x%x]"),
-                             result));
-  return result;
-}
-
-HRESULT Process::GetUsersOfProcesses(const TCHAR* task_name,
-                                     int maximum_users,
-                                     scoped_handle user_tokens[],
-                                     int* number_of_users) {
-  ASSERT1(task_name && *task_name);
-  ASSERT1(maximum_users);
-  ASSERT1(user_tokens);
-  ASSERT1(number_of_users);
-
-  scoped_hfile th32cs_snapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,
-                                                          0));
-  if (!valid(th32cs_snapshot)) {
-    HRESULT hr(HRESULTFromLastError());
-    UTIL_LOG(LEVEL_ERROR, (_T("[::CreateToolhelp32Snapshot fail][0x%x]"), hr));
-    return hr;
-  }
-
-  HRESULT result = S_OK;
-  *number_of_users = 0;
-  // Walk the list of processes.
-  PROCESSENTRY32 process = {0};
-  process.dwSize = sizeof(PROCESSENTRY32);
-  for (BOOL found = ::Process32First(get(th32cs_snapshot), &process); found;
-       found = ::Process32Next(get(th32cs_snapshot), &process)) {
-    // Check if it is one of the processes we are looking for.
-    if (_tcsicmp(task_name, process.szExeFile) == 0) {
-      // We match.  Get the user's token.
-      scoped_handle user_token;
-      if (FAILED(GetImpersonationToken(process.th32ProcessID,
-                                       address(user_token))))
-        continue;
-
-      // Search through the existing list to see if it's a duplicate.
-      // It's O(n^2) but we should have very few logged on users.
-      int i = 0;
-      for (; i < *number_of_users; i++) {
-        if (get(user_tokens[i]) == get(user_token)) {
-          // It's a duplicate.
-          break;
-        }
-      }
-      if (i >= *number_of_users) {
-        // It's a new one.  Add it if there's room.
-        ASSERT1(i < maximum_users);
-        if (i < maximum_users) {
-          // Release the user_token, we don't want it to be closed
-          // by the user_token destructor
-          reset(user_tokens[(*number_of_users)++], release(user_token));
-        }
-      }
-     }
-  }
-  return result;
-}
-
-HRESULT Process::GetImagePath(const CString& process_name,
-                              const CString& user_sid,
-                              CString* path) {
-  ASSERT1(path);
-
-  // Search for running processes with process_name.
-  uint32 mask = INCLUDE_ONLY_PROCESS_OWNED_BY_USER;
-  std::vector<CString> command_line;
-  std::vector<uint32> process_ids;
-  HRESULT hr = FindProcesses(mask,
-                             process_name,
-                             true,
-                             user_sid,
-                             command_line,
-                             &process_ids);
-  if (FAILED(hr)) {
-    UTIL_LOG(LEVEL_WARNING, (_T("[FindProcesses failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  if (process_ids.empty()) {
-    return E_FAIL;
-  }
-
-  uint32 process_id = process_ids[0];
-  UTIL_LOG(L4, (_T("[GetImagePath][pid=%d]"), process_id));
-  scoped_process process_handle(::OpenProcess(PROCESS_QUERY_INFORMATION |
-                                              PROCESS_VM_READ,
-                                              FALSE,
-                                              process_id));
-  if (!process_handle) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(L4, (_T("[OpenProcess failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  HMODULE module_handle = NULL;
-  DWORD bytes_needed = 0;
-  if (!::EnumProcessModules(get(process_handle),
-                            &module_handle,
-                            sizeof(HMODULE),
-                            &bytes_needed)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_WARNING, (_T("[EnumProcessModules failed][0x%08x]"), hr));
-    // ::EnumProcessModules from a WoW64 process fails for x64 processes. We try
-    // ::GetProcessImageFileName as a workaround here.
-    TCHAR image_name[MAX_PATH] = {0};
-    if (!GetProcessImageFileName(get(process_handle),
-                                 image_name,
-                                 arraysize(image_name))) {
-      HRESULT hr = HRESULTFromLastError();
-      UTIL_LOG(LE, (_T("[GetProcessImageFileName failed][0x%08x]"), hr));
-      return hr;
-    } else {
-      *path = image_name;
-      return S_OK;
-    }
-  }
-
-  TCHAR module_name[MAX_PATH] = {0};
-  if (!::GetModuleFileNameEx(get(process_handle),
-                             module_handle,
-                             module_name,
-                             arraysize(module_name))) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[GetModuleFileNameEx failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  *path = module_name;
-  return S_OK;
-}
-
-bool Process::IsWow64(uint32 pid) {
-  typedef BOOL (WINAPI *IsWow64Process)(HANDLE, BOOL*);
-  scoped_process handle(::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
-                                      false,
-                                      pid));
-  if (!handle) {
-    return false;
-  }
-
-  HINSTANCE kernel_instance = ::GetModuleHandle(_T("kernel32.dll"));
-  if (kernel_instance == NULL) {
-    ASSERT1(false);
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LW, (_T("[::GetModuleHandle  kernel32.dll failed][0x%08x]"), hr));
-    return false;
-  }
-
-  IsWow64Process pfn = reinterpret_cast<IsWow64Process>(::GetProcAddress(
-      kernel_instance,
-      "IsWow64Process"));
-  if (!pfn) {
-    UTIL_LOG(LW, (_T("[::IsWow64Process() not found in kernel32.dll]")));
-    return false;
-  }
-
-  BOOL wow64 = FALSE;
-  if (!(*pfn)(get(handle), &wow64)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LW, (_T("[::IsWow64Process() failed][0x%08x]"), hr));
-    return false;
-  }
-
-  return (wow64 != 0);
-}
-
-HRESULT Process::MakeProcessWindowForeground(const CString& executable) {
-  UTIL_LOG(L3, (_T("[MakeProcessWindowForeground]")));
-
-  CString sid;
-  HRESULT hr = omaha::user_info::GetCurrentUser(NULL, NULL, &sid);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // This code does not handle two cases:
-  // 1. If a new process instance is starting up but there are other process
-  //    instances running, then we will not wait for the new process instance.
-  //    One way to fix this is to pass the number of expected processes to this
-  //    method.
-  // 2. If we find multiple processes, and we are able to find the windows only
-  //    for some of the processes (maybe because the rest are still starting up)
-  //    then we will only set the windows of the one that we found to the
-  //    foreground and ignore the rest.
-  bool found = false;
-  for (int retries = 0; retries < kNumRetriesToFindProcess && !found;
-       ++retries) {
-    std::vector<CString> command_lines;
-    std::vector<uint32> processes;
-    DWORD flags = EXCLUDE_CURRENT_PROCESS | INCLUDE_ONLY_PROCESS_OWNED_BY_USER;
-    hr = Process::FindProcesses(flags,
-                                executable,
-                                true,
-                                sid,
-                                command_lines,
-                                &processes);
-    if (FAILED(hr)) {
-      UTIL_LOG(LW, (_T("[FindProcesses failed][0x%08x]"), hr));
-      return hr;
-    }
-
-    UTIL_LOG(L3, (_T("[Found %d processes]"), processes.size()));
-    for (size_t i = 0; i < processes.size(); ++i) {
-      CSimpleArray<HWND> windows;
-      if (!WindowUtils::FindProcessWindows(processes[i], 0, &windows)) {
-        UTIL_LOG(L3, (_T("[FindProcessWindows failed][0x%08x]"), hr));
-        continue;
-      }
-
-      for (int j = 0; j < windows.GetSize(); ++j) {
-        if (WindowUtils::IsMainWindow(windows[j])) {
-          UTIL_LOG(L4, (_T("[Found main window of process %d]"), processes[i]));
-          WindowUtils::MakeWindowForeground(windows[j]);
-          ::FlashWindow(windows[j], true);
-          found = true;
-          break;
-        }
-      }
-    }
-
-    if (!found) {
-      ::Sleep(kFindProcessRetryIntervalMs);
-    }
-  }
-
-  return S_OK;
-}
-
-}  // namespace omaha
-
diff --git a/common/process.h b/common/process.h
deleted file mode 100644
index 4cabdf8..0000000
--- a/common/process.h
+++ /dev/null
@@ -1,306 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Declares class Process to incapsulate win32
-// functions for creation and some manipulations of
-// processes.
-
-#ifndef OMAHA_COMMON_PROCESS_H__
-#define OMAHA_COMMON_PROCESS_H__
-
-#include <windows.h>
-#include <psapi.h>
-#include <atlstr.h>
-#include <vector>
-#include "base/basictypes.h"
-#include "omaha/common/commontypes.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/scoped_any.h"
-
-namespace omaha {
-
-const int kMaxProcesses = 1024;
-const int kMaxProcessModules = 1024;
-
-// Exclude mask for finding processes.
-enum FindProcessesExcludeMask {
-  EXCLUDE_NONE = 0,
-  EXCLUDE_CURRENT_PROCESS = 0x1,
-  EXCLUDE_PROCESS_OWNED_BY_CURRENT_USER = 0x2,
-  EXCLUDE_PROCESS_OWNED_BY_SYSTEM = 0x4,
-  INCLUDE_ONLY_PROCESS_OWNED_BY_USER = 0x08,
-  EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING = 0x10,
-  INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING = 0x20
-};
-
-// Process info used in finding descendent processes.
-struct ProcessInfo {
-  uint32 process_id;
-  uint32 parent_id;
-#if !SHIPPING
-  CString exe_file;
-#endif
-};
-
-// Process class
-class Process {
- public:
-  // Constructor
-  // Init the process object with the executable name
-  // and if known the window class name of the process.
-  // If window_class_name is known it will be easy
-  // to stop the process just by sending messages to
-  // a window.
-  Process(const TCHAR* name, const TCHAR* window_class_name);
-
-  // Constructor.
-  // Init the process object with the process id.
-  explicit Process(uint32 process_id);
-
-  // Destructor
-  virtual ~Process();
-
-  // Start the process with some command line params if any.
-  virtual bool Start(const TCHAR* command_line_parameters);
-
-  // Start with command params and specific hwnd.
-  virtual bool StartWithHwnd(const TCHAR* command_line_parameters, HWND hwnd);
-
-  // Restart the process with the old command line params.
-  bool Restart(HWND hwnd);
-
-  // Set shutdown event using in signaling the process watch.
-  void SetShutdownEvent(HANDLE shutdown_event);
-
-  // Sets the specified priority class to the process.
-  bool SetPriority(uint32 priority_class) const;
-
-  // Check if the process is running.
-  bool Running() const;
-
-  // Create a job and assign the process to it.
-  HANDLE AssignToJob();
-
-  // Wait some time till the process finishes.
-  bool WaitUntilDead(uint32 timeout_msec);
-
-  // Wait some time till the process and all its descendent processes finish.
-  bool WaitUntilAllDead(HANDLE job,
-                        uint32 timeout_msec,
-                        const TCHAR* path_to_exclude,
-                        uint32* exit_code);
-
-  // Wait until process is dead or a windows message arrives. For use in a
-  // message loop while waiting.
-  HRESULT WaitUntilDeadOrInterrupt(uint32 msec);
-  // Return values include CI_S_PROCESSWAIT_DEAD, CI_S_PROCESSWAIT_TIMEOUT,
-  // CI_S_PROCESSWAIT_MESSAGE.
-
-#if !SHIPPING
-  CString GetDebugInfo() const;
-#endif
-
-  // Return the process ID.
-  uint32 GetId() const;
-
-  // Return a readable representation of the process's name.
-  const TCHAR *GetName() const;
-
-  // Get win32 handle to process.
-  HANDLE GetHandle() const;
-
-  // Get process exit code.
-  bool GetExitCode(uint32* exit_code) const;
-
-  // can we kill the process via terminating
-  // some processes are not safe to terminate.
-  virtual bool IsTerminationAllowed() const;
-
-  // Second, more rude method to stop the process. window_class_name was
-  // not given or CloseWithMessage didn't succeed.
-  bool Terminate(uint32 wait_for_terminate_msec);
-
-  // Try to get a descendant process. Return process id if found.
-  uint32 GetDescendantProcess(HANDLE job,
-                              bool child_only,
-                              bool sole_descedent,
-                              const TCHAR* search_name,
-                              const TCHAR* path_to_exclude);
-
-  // Dynamically links and calls ::IsProcessInJob() in kernel32.dll.
-  static BOOL IsProcessInJob(HANDLE process_handle,
-                             HANDLE job_handle,
-                             PBOOL result);
-
-  // Try to get all matching descendant processes.
-  HRESULT GetAllDescendantProcesses(
-      HANDLE job,
-      bool child_only,
-      const TCHAR* search_name,
-      const TCHAR* path_to_exclude,
-      std::vector<ProcessInfo>* descendant_proc_ids);
-
-  // Finds the processes based on passed criteria.
-  static HRESULT FindProcesses(uint32 exclude_mask,
-                               const TCHAR* search_name,
-                               bool search_main_executable_only,
-                               std::vector<uint32>* process_ids_found);
-
-  // Find processes which loads the specified exe/dll. Uses the user_sid only
-  // if the INCLUDE_ONLY_PROCESS_OWNED_BY_USER flag has been set.
-  // The command_line is only used when
-  // EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING is set.
-  static HRESULT FindProcesses(uint32 exclude_mask,
-                               const TCHAR* search_name,
-                               bool search_main_executable_only,
-                               const CString& user_sid,
-                               const std::vector<CString>& command_line,
-                               std::vector<uint32>* process_ids_found);
-
-  // Find processes with the specified criteria running in specific session.
-  static HRESULT FindProcessesInSession(DWORD session_id,
-                                        uint32 exclude_mask,
-                                        const TCHAR* search_name,
-                                        bool search_main_executable_only,
-                                        const CString& user_sid,
-                                        const std::vector<CString>& cmd_lines,
-                                        std::vector<uint32>* process_ids_found);
-
-  // Is the process using the specified exe/dll.
-  static bool IsProcessUsingExeOrDll(uint32 process_id,
-                                     const TCHAR* search_name,
-                                     bool search_main_executable_only);
-
-  // Obtain the process ID from a hProcess HANDLE.
-  static ULONG GetProcessIdFromHandle(HANDLE hProcess);
-
-  // Get the command line of a process.
-  static HRESULT GetCommandLine(uint32 process_id, CString* cmd_line);
-
-  // Get the process owner.
-  static HRESULT GetProcessOwner(uint32 pid, CString* owner_sid);
-
-  // Creates an impersonation token for the user running process_id.
-  // The caller is responsible for closing the returned handle.
-  static HRESULT GetImpersonationToken(DWORD process_id, HANDLE* user_token);
-
-  // Returns user token handles for the users currently running the named task.
-  // maximum_users specifies the maximun number of handles to be retured.
-  // The actual number filled is returned.
-  static HRESULT GetUsersOfProcesses(const TCHAR* task_name,
-                                     int maximum_users,
-                                     scoped_handle users[],
-                                     int* number_of_users);
-
-  // Gets the on disk path from where the process image is loaded.
-  static HRESULT GetImagePath(const CString& process_name,
-                              const CString& user_sid,
-                              CString* path);
-
-  // Returns if the process is running under WOW64.
-  static bool IsWow64(uint32 pid);
-
- public:
-  // How many times the process can be restarted in case it crashes.
-  virtual uint32 GetMaxNumberOfRestarts() const;
-
-  // Maximum amount of memory process is allowed to use before it's killed.
-  // Default of 0 means unlimited.
-  virtual uint32 GetMaxMemory() const;
-
-  // Have we exceeded the number of maximum restarting.
-  bool AllowedToRestart() const;
-
-  // In case of crash, how soon to restart.
-  virtual uint32 GetRestartInterval() const;
-
-  // The idea is the following. Each process has maximum number of restarts.
-  // As soon as the process reaches that number of restarts in should no longer
-  // be restarted unless the time window in which the process was crashing is
-  // more than the value returned by this function. For example:
-  // Process X returns 3 from the function GetMaxNumberOfRestarts.
-  // The same process returns 30*1000*60 (30 minutes) from
-  // GetTimeWindowForCrashes if  process X crashed more than 3 times in 30
-  // minutes it will not be restarted. if it took more than 30 minutes for
-  // process X to crash more than 3 times - internal counters for number of
-  // crashes will be reset and the process will be happily restarted.
-  // Each derived process can override this function to return its own time
-  // window for crashes.
-  // Default implementation returns INFINITE which means that this is not time
-  // based at all, if the process crashed more than the value returned by
-  // GetMaxNumberOfRestarts it will not be restarted no matter how long it took.
-  virtual uint32 GetTimeWindowForCrashes() const;
-
-  // Sets the main window of all process instances to the foreground.
-  static HRESULT MakeProcessWindowForeground(const CString& executable);
-
- private:
-  mutable uint32 number_of_restarts_;
-  CString command_line_;
-  CString command_line_parameters_;
-  CString window_class_name_;
-  CString name_;
-  scoped_process process_;
-  uint32 process_id_;
-  mutable uint32 time_of_start_;
-  mutable uint32 exit_code_;
-  HANDLE shutdown_event_;
-
-  // Helper function to wait till the process and all its descendent processes
-  // finish.
-  bool InternalWaitUntilAllDead(HANDLE job,
-                                uint32 timeout_msec,
-                                const TCHAR* path_to_exclude,
-                                uint32* exit_code);
-
-  // Check if the process is running with a specified path.
-  static bool IsProcessRunningWithPath(uint32 process_id, const TCHAR* path);
-
-  // Checks if the command line of the process has been specified as one to
-  // ignore.
-  static bool IsStringPresentInList(const CString& process_command_line,
-                                    const std::vector<CString>& list);
-
-
-  // Helper function to get long path name.
-  static HRESULT GetLongPathName(const TCHAR* short_name, CString* long_name);
-
-  // Helper for Process::IsProcessUsingExeOrDll(). Use GetProcessImageFileName
-  // to get the filename, and match against search_name.
-  static bool IsProcImageMatch(HANDLE proc_handle,
-                               const TCHAR* search_name,
-                               bool is_fully_qualified_name);
-
-  // Dynamically links and calls ::GetProcessImageFileName() in psapi.dll.
-  static DWORD GetProcessImageFileName(HANDLE proc_handle,
-                                       LPTSTR image_file,
-                                       DWORD file_size);
-
-  // Helper for Process::IsProcessUsingExeOrDll().
-  // Is there a match between the module and the specified exe/dll?
-  static bool IsModuleMatchingExeOrDll(const TCHAR* module_name,
-                                       const TCHAR* search_name,
-                                       bool is_fully_qualified_name);
-
-#if !SHIPPING
-  CString debug_info_;
-#endif
-  DISALLOW_EVIL_CONSTRUCTORS(Process);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_PROCESS_H__
-
diff --git a/common/process_unittest.cc b/common/process_unittest.cc
deleted file mode 100644
index ae2a176..0000000
--- a/common/process_unittest.cc
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// Process unit tests.
-
-#include <vector>
-
-#include "omaha/common/app_util.h"
-#include "omaha/common/path.h"
-#include "omaha/common/process.h"
-#include "omaha/common/user_info.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-const int kWaitUntilDeadMs = 10000;
-
-// Process class that terminates the associated process when deleted.
-class ScopedProcess : public Process {
- public:
-  explicit ScopedProcess(const TCHAR* name) : Process(name, NULL) {}
-  virtual ~ScopedProcess() {
-    Terminate(0);
-    EXPECT_TRUE(WaitUntilDead(kWaitUntilDeadMs));
-  }
-};
-
-TEST(ProcessTest, StartOneProcess) {
-  const TCHAR kExecutableName[] = _T("cmd.exe");
-  const TCHAR kExecutableArguments[] = _T("/c exit 702");
-  const int kExpectedExitCode = 702;
-
-  CString path = ConcatenatePath(app_util::GetSystemDir(), kExecutableName);
-  ScopedProcess process(path);
-
-  ASSERT_TRUE(process.Start(kExecutableArguments));
-  EXPECT_TRUE(process.WaitUntilDead(kWaitUntilDeadMs));
-
-  // Check the exit code to get some assurance that the process actually ran.
-  uint32 exit_code = 0;
-  EXPECT_TRUE(process.GetExitCode(&exit_code));
-  EXPECT_EQ(kExpectedExitCode, exit_code);
-}
-
-// Dummy process to spin off and then find.  The numeric argument will make
-// netstat run until it's killed by the ScopedProcess destructor.
-const TCHAR kTestExecutable[] = _T("netstat.exe");
-const TCHAR kTestArguments[] = _T("10");
-const TCHAR kTestExcludeArguments[] = _T("-o 20");
-const TCHAR kTestExcludeString[] = _T("20");
-const TCHAR kTestIncludeArguments[] = _T("-o 30");
-const TCHAR kTestIncludeString[] = _T("30");
-const int kWaitForProcessStartMs = 500;
-const int kMaxWaitIterations = 10;
-
-TEST(ProcessTest, FindOneProcess) {
-  CString path = ConcatenatePath(app_util::GetSystemDir(), kTestExecutable);
-  ScopedProcess process(path);
-  ASSERT_TRUE(process.Start(kTestArguments));
-  for (int i = 0; i < kMaxWaitIterations; ++i) {
-    ::Sleep(kWaitForProcessStartMs);
-    if (process.Running())
-      break;
-  }
-  EXPECT_TRUE(process.Running());
-
-  // Try to find the test process.
-  uint32 exclude_mask = INCLUDE_ONLY_PROCESS_OWNED_BY_USER;
-  CString user_sid;
-  std::vector<CString> command_lines;
-  std::vector<uint32> process_ids;
-
-  ASSERT_SUCCEEDED(omaha::user_info::GetCurrentUser(NULL, NULL, &user_sid));
-
-  // This test intermittently fails to find the process when run on Pulse.
-  // This code attempts to ensure that the process is further along in the
-  // initialization process by waiting until Process::GetCommandLine succeeds.
-  // This test case does not result in FindProcesses using GetCommandLine, but
-  // waiting until this point may be enough to address the intermitent failures.
-  HRESULT hr = E_FAIL;
-  CString process_cmd;
-  for (int tries = 0; tries < 100 && FAILED(hr); ++tries) {
-    ::Sleep(50);
-    hr = Process::GetCommandLine(process.GetId(), &process_cmd);
-  }
-  EXPECT_SUCCEEDED(hr);
-
-  ASSERT_SUCCEEDED(Process::FindProcesses(exclude_mask,
-                                          kTestExecutable,
-                                          true,
-                                          user_sid,
-                                          command_lines,
-                                          &process_ids));
-  ASSERT_EQ(1, process_ids.size());  // Exit before accessing invalid element.
-  EXPECT_EQ(process.GetId(), process_ids[0]);
-}
-
-TEST(ProcessTest, ExcludeProcess) {
-  // Make sure the test process is not already running.
-  uint32 exclude_mask = INCLUDE_ONLY_PROCESS_OWNED_BY_USER;
-  CString user_sid;
-  std::vector<CString> command_lines;
-  std::vector<uint32> process_ids;
-
-  ASSERT_SUCCEEDED(omaha::user_info::GetCurrentUser(NULL, NULL, &user_sid));
-  ASSERT_SUCCEEDED(Process::FindProcesses(exclude_mask,
-                                          kTestExecutable,
-                                          true,
-                                          user_sid,
-                                          command_lines,
-                                          &process_ids));
-  ASSERT_EQ(0, process_ids.size());
-
-  // Ok, test process not running. Let's continue running the test.
-  CString path = ConcatenatePath(app_util::GetSystemDir(), kTestExecutable);
-  ScopedProcess process(path);
-  ScopedProcess exclude_process(path);
-
-  ASSERT_TRUE(process.Start(kTestArguments));
-  ASSERT_TRUE(exclude_process.Start(kTestExcludeArguments));
-  for (int i = 0; i < kMaxWaitIterations; ++i) {
-    ::Sleep(kWaitForProcessStartMs);
-    if (process.Running() && exclude_process.Running())
-      break;
-  }
-  EXPECT_TRUE(process.Running());
-  EXPECT_TRUE(exclude_process.Running());
-
-  // Try to find just the first process, excluding the other.
-  exclude_mask = INCLUDE_ONLY_PROCESS_OWNED_BY_USER |
-                 EXCLUDE_CURRENT_PROCESS |
-                 EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING;
-
-  command_lines.push_back(kTestExcludeString);
-  ASSERT_SUCCEEDED(Process::FindProcesses(exclude_mask,
-                                          kTestExecutable,
-                                          true,
-                                          user_sid,
-                                          command_lines,
-                                          &process_ids));
-  ASSERT_EQ(1, process_ids.size());
-  EXPECT_EQ(process.GetId(), process_ids[0]);
-}
-
-TEST(ProcessTest, IncludeProcess) {
-  CString path = ConcatenatePath(app_util::GetSystemDir(), kTestExecutable);
-  ScopedProcess process(path);
-  ScopedProcess include_process(path);
-
-  ASSERT_TRUE(process.Start(kTestArguments));
-  ASSERT_TRUE(include_process.Start(kTestIncludeArguments));
-  for (int i = 0; i < kMaxWaitIterations; ++i) {
-    ::Sleep(kWaitForProcessStartMs);
-    if (process.Running() && include_process.Running())
-      break;
-  }
-  EXPECT_TRUE(process.Running());
-  EXPECT_TRUE(include_process.Running());
-
-  // Try to find just the first process, excluding the other.
-  uint32 exclude_mask = INCLUDE_ONLY_PROCESS_OWNED_BY_USER |
-                        EXCLUDE_CURRENT_PROCESS |
-                        INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING;
-  CString user_sid;
-  std::vector<CString> command_lines;
-  std::vector<uint32> process_ids;
-
-  command_lines.push_back(kTestIncludeString);
-  ASSERT_SUCCEEDED(omaha::user_info::GetCurrentUser(NULL, NULL, &user_sid));
-  ASSERT_SUCCEEDED(Process::FindProcesses(exclude_mask,
-                                          kTestExecutable,
-                                          true,
-                                          user_sid,
-                                          command_lines,
-                                          &process_ids));
-  ASSERT_EQ(1, process_ids.size());
-  EXPECT_EQ(include_process.GetId(), process_ids[0]);
-}
-
-TEST(ProcessTest, GetImagePath) {
-  // Get this module's path.
-  HMODULE handle = ::GetModuleHandle(NULL);
-  ASSERT_TRUE(handle != NULL);
-
-  TCHAR file_name[MAX_PATH] = {0};
-  ASSERT_NE(::GetModuleFileName(handle, file_name, MAX_PATH), 0);
-  ASSERT_NE(0, wcslen(file_name));
-
-  CString exe = GetFileFromPath(file_name);
-  ASSERT_FALSE(exe.IsEmpty());
-
-  CString user_sid;
-  ASSERT_SUCCEEDED(omaha::user_info::GetCurrentUser(NULL, NULL, &user_sid));
-
-  // Test the method.
-  CString path;
-  ASSERT_SUCCEEDED(Process::GetImagePath(exe, user_sid, &path));
-
-  // Compare the result.
-  ASSERT_STREQ(file_name, path);
-}
-
-}  // namespace omaha
-
diff --git a/common/processor_type.cc b/common/processor_type.cc
deleted file mode 100644
index e07f92b..0000000
--- a/common/processor_type.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Processor brand detection implementation
-//
-#include "omaha/common/processor_type.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-// Returns "GenuineIntel", "AuthenticAMD", etc...
-CString GetProcessorType() {
-  // Reference for checking for chip type
-  // http://en.wikipedia.org/wiki/CPUID
-
-  // There are 12 characters in the chip id (e.g. "GenuineIntel")
-  union {
-    char str[16];
-    uint32 n[4];
-  } regs;
-  SetZero(regs.str);
-  __asm {
-    xor eax, eax;  // Set EAX = 0 to get CPU type
-    cpuid;         // Now ebx, edx, ecx will contain cpu brand
-    mov regs.n[0], ebx;
-    mov regs.n[4], edx;
-    mov regs.n[8], ecx;
-  }
-  return CString(regs.str);
-}
-
-// Returns true if we're running on an Intel
-bool IsIntelProcessor() {
-  CString chip = GetProcessorType();
-  return (chip == kIntelVendorId);
-}
-
-}  // namespace omaha
diff --git a/common/progress_sampler.h b/common/progress_sampler.h
new file mode 100644
index 0000000..18d8b41
--- /dev/null
+++ b/common/progress_sampler.h
@@ -0,0 +1,129 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_PROGRESS_SAMPLER_H_
+#define OMAHA_COMMON_PROGRESS_SAMPLER_H_
+
+#include <windows.h>
+#include <algorithm>
+#include <queue>
+#include "omaha/base/debug.h"
+#include "omaha/base/time.h"
+
+namespace omaha {
+
+// This class keeps track of the input data (samples) and helps to calculate
+// the average progress based on that.
+//
+// Example usage:
+//   // Create a sampler that keep samples within last 500ms and calculates
+//   // average progress only if minimum time range 100ms is reached.
+//   ProgressSampler<int> progress_sampler(500, 100);
+//   ASSERT1(!progress_sampler.HasEnoughSamples());
+//
+//   progress_sampler.AddSample(0, 100);
+//   ASSERT1(!progress_sampler.HasEnoughSamples());
+//
+//   progress_sampler.AddSample(20, 200);
+//   // Minimum time range 100ms has not been reached yet.
+//   ASSERT1(!progress_sampler.HasEnoughSamples());
+//
+//   progress_sampler.AddSample(200, 300);
+//   ASSERT1(progress_sampler.HasEnoughSamples());
+//   // Samples in queue: [(0, 100), (20, 200), (200, 300)].
+//   // Average speed: (300-100) / (200-0) = 1.
+//   ASSERT1(1 == progress_sampler.GetAverageProgressPerMs());
+//
+//   progress_sampler.AddSample(520, 450);
+//   // The first sample value was added at timeline 0 is now out of
+//   // range (500ms) and thus discarded.
+//   // Samples in queue now: [(20, 200), (200, 300), [520, 450)].
+//   // Average speed: (520-20) / (450-200) = 2.
+//   ASSERT1(2 == progress_sampler.GetAverageProgressPerMs());
+template<typename T> class ProgressSampler {
+ public:
+  ProgressSampler(int sample_time_range_ms, int minimum_range_required_ms)
+      : sample_time_range_ms_(sample_time_range_ms),
+        minimum_range_required_ms_(minimum_range_required_ms) {
+      ASSERT1(minimum_range_required_ms > 0);
+  }
+
+  void AddSampleWithCurrentTimeStamp(T sample_value) {
+    AddSample(GetCurrent100NSTime() / kMillisecsTo100ns, sample_value);
+  }
+
+  void AddSample(uint64 timestamp_in_ms, T sample_value) {
+    if (!samples_.empty() &&
+        (sample_value < samples_.back().value ||          // Value regression.
+         timestamp_in_ms < samples_.back().timestamp)) {  // Clock regression.
+      Reset();
+      return;
+    }
+
+    samples_.push(Sample(timestamp_in_ms, sample_value));
+
+    // Discard old data that is out of range.
+    while (samples_.back().timestamp - samples_.front().timestamp >
+           sample_time_range_ms_ && samples_.size() > 2) {
+      samples_.pop();
+    }
+  }
+
+  bool HasEnoughSamples() const {
+    if (samples_.size() < 2) {
+      return false;
+    }
+
+    ASSERT1(samples_.back().timestamp >= samples_.front().timestamp);
+    return (samples_.back().timestamp - samples_.front().timestamp >
+            minimum_range_required_ms_);
+  }
+
+  T GetAverageProgressPerMs() const {
+    if (!HasEnoughSamples()) {
+      return kUnknownProgressPerMs;
+    }
+
+    uint64 time_diff = samples_.back().timestamp - samples_.front().timestamp;
+    ASSERT1(time_diff > 0);
+    return (samples_.back().value - samples_.front().value) /
+            static_cast<T>(time_diff);
+  }
+
+  void Reset() {
+    std::queue<Sample> empty_queue;
+    std::swap(samples_, empty_queue);
+  }
+
+  static const T kUnknownProgressPerMs = static_cast<T>(-1);
+
+ private:
+  const uint64 sample_time_range_ms_;
+  const uint64 minimum_range_required_ms_;
+
+  struct Sample {
+    Sample(uint64 local_timestamp, T local_value)
+        : timestamp(local_timestamp), value(local_value) {
+    }
+
+    uint64  timestamp;
+    T value;
+  };
+  std::queue<Sample> samples_;
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_PROGRESS_SAMPLER_H_
diff --git a/common/protocol_definition.h b/common/protocol_definition.h
new file mode 100644
index 0000000..34f8295
--- /dev/null
+++ b/common/protocol_definition.h
@@ -0,0 +1,241 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Definitions for the request and response data structures that are part of
+// the xml protocol. Each attribute or element that occurs in the xml protocol
+// has a definition here. The request and response xml artifacts are defined
+// inside their corresponding namespaces.
+
+#ifndef OMAHA_COMMON_PROTOCOL_DEFINITION_H_
+#define OMAHA_COMMON_PROTOCOL_DEFINITION_H_
+
+#include <vector>
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/install_manifest.h"
+#include "omaha/common/ping_event.h"
+
+namespace omaha {
+
+namespace xml {
+
+namespace request {
+
+// Defines the structure of the Omaha protocol update request.
+// The structure of the request is:
+//
+// Request | --- OS
+//         | -<- App | --- UpdateCheck
+//                   | --- Data
+//                   | --- Ping
+//                   | -<- PingEvent
+//
+// The xml parser traverses this data structure in order to serialize it. The
+// names of the members of structures closely match the names of the elements
+// and attributes in the xml document.
+//
+// TODO(omaha): briefly document the members.
+
+struct OS {
+  CString platform;
+  CString version;
+  CString service_pack;
+  CString arch;
+};
+
+struct UpdateCheck {
+  UpdateCheck() : is_valid(false), is_update_disabled(false) {}
+
+  // TODO(omaha): this member is not serialized. Use pointers to indicate
+  // optional elements instead of is_valid.
+  bool is_valid;
+
+  bool is_update_disabled;
+
+  CString tt_token;
+};
+
+// For now, only a single "install" data is supported.
+struct Data {
+  CString install_data_index;
+};
+
+// didrun element. The element is named "ping" for legacy reasons.
+struct Ping {
+  Ping() : active(ACTIVE_UNKNOWN),
+           days_since_last_active_ping(0),
+           days_since_last_roll_call(0) {}
+
+  ActiveStates active;
+  int days_since_last_active_ping;
+  int days_since_last_roll_call;
+};
+
+struct App {
+    App() : install_time_diff_sec(0) {}
+
+    CString app_id;
+
+    CString version;
+
+    CString next_version;
+
+    CString ap;
+
+    CString lang;
+
+    CString iid;
+
+    CString brand_code;
+
+    CString client_id;
+
+    CString experiments;
+
+    int install_time_diff_sec;
+
+    // Optional update check.
+    UpdateCheck update_check;
+
+    // Optional data.
+    Data data;
+
+    // Optional 'did run' ping.
+    Ping ping;
+
+    // Progress/result pings.
+    PingEventVector ping_events;
+  };
+
+struct Request {
+  Request() : is_machine(false), check_period_sec(-1) {}
+
+  bool is_machine;
+
+  CString uid;
+
+  CString protocol_version;
+
+  CString omaha_version;
+
+  CString install_source;
+
+  CString origin_url;
+
+  // Identifies the source of the request as a test/production prober system.
+  CString test_source;
+
+  // Unique identifier for this request, used to associate the same request
+  // received multiple times on the server.
+  CString request_id;
+
+  // Unique identifier for this session, used to correlate multiple requests
+  // associated with a single operation by the Omaha client.
+  CString session_id;
+
+  // Time between update checks in seconds.
+  // TODO(omaha): see if we can enforce by convention that -1 means it is
+  // using the default value.
+  int check_period_sec;
+
+  OS os;
+
+  std::vector<App> apps;
+};
+
+}  // namespace request
+
+namespace response {
+
+// Defines an Omaha protocol update response. The structure of the response is:
+//
+// Response | --- DayStart
+//          | -<- App | --- UpdateCheck | --- Urls
+//                                      | --- InstallManifest | --- Packages
+//                                                            | --- Actions
+//                    | --- Data
+//                    | --- Ping
+//                    | -<- Event
+//
+// The xml parser traverses the xml dom and populates the data members of
+// the response. The names of the members of structures closely match the names
+// of the elements and attributes in the xml document.
+//
+// TODO(omaha): briefly document the members.
+
+struct UpdateCheck {
+  CString status;
+
+  CString tt_token;
+
+  CString error_url;         // URL describing error.
+
+  std::vector<CString> urls;
+
+  InstallManifest install_manifest;
+};
+
+// For now, only a single "install" data is supported.
+struct Data {
+  CString status;
+
+  CString install_data_index;
+  CString install_data;
+};
+
+struct Ping {
+  CString status;
+};
+
+struct Event {
+  CString status;
+};
+
+struct App {
+  CString status;
+
+  // TODO(omaha): rename to app_id.
+  CString appid;
+
+  CString experiments;
+
+  UpdateCheck update_check;
+
+  std::vector<Data> data;
+
+  Ping ping;
+
+  std::vector<Event> events;
+};
+
+struct DayStart {
+  DayStart() : elapsed_seconds(0) {}
+
+  int elapsed_seconds;
+};
+
+struct Response {
+  CString protocol;
+  DayStart day_start;
+  std::vector<App> apps;
+};
+
+}  // namespace response
+
+}  // namespace xml
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_PROTOCOL_DEFINITION_H_
+
diff --git a/common/protocol_definition_test.cc b/common/protocol_definition_test.cc
new file mode 100644
index 0000000..bb69599
--- /dev/null
+++ b/common/protocol_definition_test.cc
@@ -0,0 +1,35 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+#include "omaha/common/protocol_definition.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+TEST(XmlRequestPingTest, ConstructorDefaultValue) {
+  xml::request::Ping ping;
+
+  const bool was_active = ping.active == ACTIVE_RUN;
+  const bool need_active = ping.active != ACTIVE_UNKNOWN;
+  const bool has_sent_a_today = ping.days_since_last_active_ping == 0;
+  const bool need_a = was_active && !has_sent_a_today;
+  const bool need_r = ping.days_since_last_roll_call != 0;
+
+  EXPECT_FALSE(need_active);
+  EXPECT_FALSE(need_a);
+  EXPECT_FALSE(need_r);
+}
+
+}  // namespace omaha
diff --git a/common/queue_timer.cc b/common/queue_timer.cc
deleted file mode 100644
index 5bae3d6..0000000
--- a/common/queue_timer.cc
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// The implementation is straightforward except the destruction of the
-// QueueTimer which needs some clarification.
-// If there is no callback running, then the destructor gets the critical
-// section and then it blocks on the DeleteTimerQueueTimer call, waiting for
-// the kernel to clean up the timer handle. The callback never fires in this
-// case.
-// If a callback is running, then there are two possibilities:
-// 1. The callback gets the critical section. The callback runs as usual and
-// then the destructor gets the critical section. This is also easy.
-// 2. The destructor gets the critical section. In this case, the callback
-// tries the critical section then it returns right away.
-//
-// Alarm timers are started and restarted every time they fire. The usage
-// patterns for alarms is usually Start, Callback, Start, Callback, etc...
-// The cleanup of an alarm timer handle usually happens in the callback, unless
-// the destructor of the QueueTimer is called, in which case the logic
-// above applies.
-//
-// Periodic timers are only started once: Start, Callback, Callback, etc...
-// In this case, the destructor does all the necessary cleanup.
-// Periodic timers must fire at intervals that are reasonable long so that
-// the callbacks do not queue up.
-
-#include "omaha/common/queue_timer.h"
-
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-
-namespace omaha {
-
-QueueTimer::QueueTimer(HANDLE timer_queue, Callback callback, void* ctx)
-    : callback_tid_(0),
-      ctx_(ctx),
-      due_time_(0),
-      period_(0),
-      flags_(0),
-      timer_handle_(NULL),
-      timer_queue_(timer_queue),
-      callback_(callback) {
-  UTIL_LOG(L3, (_T("[QueueTimer::QueueTimer][0x%08x]"), this));
-  ASSERT1(timer_queue);
-  ASSERT1(callback);
-  ::InitializeCriticalSection(&dtor_cs_);
-  ::InitializeCriticalSection(&cs_);
-}
-
-// The destructor blocks on waiting for the timer kernel object to be deleted.
-// We can't call the destructor of QueueTimer while we are handling a callback.
-// This will result is a deadlock.
-QueueTimer::~QueueTimer() {
-  UTIL_LOG(L3, (_T("[QueueTimer::~QueueTimer][0x%08x]"), this));
-
-  ::EnterCriticalSection(&dtor_cs_);
-  if (timer_handle_) {
-    ASSERT1(callback_tid_ != ::GetCurrentThreadId());
-
-    // This is a blocking call waiting for all callbacks to clear up.
-    bool res = !!::DeleteTimerQueueTimer(timer_queue_,
-                                         timer_handle_,
-                                         INVALID_HANDLE_VALUE);
-    ASSERT1(res);
-    timer_handle_ = NULL;
-  }
-  callback_ = NULL;
-  timer_queue_ = NULL;
-  flags_ = 0;
-  period_ = 0;
-  due_time_ = 0;
-  ctx_ = 0;
-  callback_tid_ = 0;
-  ::LeaveCriticalSection(&dtor_cs_);
-
-  ::DeleteCriticalSection(&cs_);
-  ::DeleteCriticalSection(&dtor_cs_);
-}
-
-// Thread safe.
-HRESULT QueueTimer::Start(int due_time, int period, uint32 flags) {
-  // Since Start creates the timer there could be a race condition where
-  // the timer could fire while we are still executing Start. We protect
-  // the start with a critical section so the Start completes before the
-  // timer can be entered by the callback.
-
-  ::EnterCriticalSection(&cs_);
-  HRESULT hr = DoStart(due_time, period, flags);
-  ::LeaveCriticalSection(&cs_);
-  return hr;
-}
-
-// Thread-safe.
-void QueueTimer::TimerCallback(void* param, BOOLEAN timer_or_wait) {
-  ASSERT1(param);
-  VERIFY1(timer_or_wait);
-
-  QueueTimer* timer = static_cast<QueueTimer*>(param);
-
-  if (!::TryEnterCriticalSection(&timer->dtor_cs_)) {
-    return;
-  }
-
-  ::EnterCriticalSection(&timer->cs_);
-  timer->DoCallback();
-  ::LeaveCriticalSection(&timer->cs_);
-
-  ::LeaveCriticalSection(&timer->dtor_cs_);
-}
-
-
-HRESULT QueueTimer::DoStart(int due_time, int period, uint32 flags) {
-  due_time_ = due_time;
-  period_ = period;
-  flags_ = flags;
-
-  // Application Verifier says period must be 0 for WT_EXECUTEONLYONCE timers.
-  if ((flags & WT_EXECUTEONLYONCE) && period != 0) {
-    return E_INVALIDARG;
-  }
-
-  // Periodic timers can't be started more than one time.
-  if (timer_handle_) {
-    return E_UNEXPECTED;
-  }
-
-  bool res = !!::CreateTimerQueueTimer(&timer_handle_,
-                                       timer_queue_,
-                                       &QueueTimer::TimerCallback,
-                                       this,
-                                       due_time,
-                                       period,
-                                       flags_);
-  if (!res) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LE, (_T("[QueueTimer::Start failed][0x%08x][0x%08x]"), hr, this));
-    return hr;
-  }
-
-  ASSERT1(timer_handle_);
-  UTIL_LOG(L3, (_T("[QueueTimer::Start timer created][0x%08x]"), this));
-  return S_OK;
-}
-
-void QueueTimer::DoCallback() {
-  UTIL_LOG(L2, (_T("[QueueTimer::OnCallback][0x%08x]"), this));
-
-  ASSERT1(timer_queue_);
-  ASSERT1(timer_handle_);
-  ASSERT1(callback_);
-
-  if (!period_) {
-    // Non-periodic aka alarm timers fire only once. We delete the timer
-    // handle so that the timer object can be restarted later on.
-    // The call below is non-blocking. The deletion of the kernel object can
-    // succeed right away, for example if the timer runs in the timer thread
-    // itself. Otherwise, if the last error is ERROR_IO_PENDING the kernel
-    // cleans up the object once the callback returns.
-    bool res = !!::DeleteTimerQueueTimer(timer_queue_, timer_handle_, NULL);
-    ASSERT1(res || (!res && ::GetLastError() == ERROR_IO_PENDING));
-    timer_handle_ = NULL;
-  }
-
-  callback_tid_ = ::GetCurrentThreadId();
-  callback_(this);
-  callback_tid_ = 0;
-}
-
-}  // namespace omaha
-
diff --git a/common/queue_timer_unittest.cc b/common/queue_timer_unittest.cc
deleted file mode 100644
index 54a2ccc..0000000
--- a/common/queue_timer_unittest.cc
+++ /dev/null
@@ -1,175 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include <iostream>
-#include "base/scoped_ptr.h"
-#include "omaha/common/queue_timer.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/timer.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-class QueueTimerTest : public testing::Test {
- protected:
-  QueueTimerTest()
-      : timer_queue_(NULL),
-        cnt_(0),
-        max_cnt_(0) {}
-
-  virtual void SetUp() {
-    cnt_ = 0;
-    timer_queue_ = ::CreateTimerQueue();
-    ASSERT_TRUE(timer_queue_);
-    reset(ev_, ::CreateEvent(NULL, true, false, NULL));
-  }
-
-  virtual void TearDown() {
-    // First destroy the timer, otherwise the timer could fire and
-    // access invalid test case state.
-    queue_timer_.reset();
-    reset(ev_);
-    ASSERT_TRUE(::DeleteTimerQueueEx(timer_queue_, INVALID_HANDLE_VALUE));
-    cnt_ = 0;
-    max_cnt_ = 0;
-  }
-
-  // Handles the alarm mode of the timer queue, where the timer fires once.
-  static void AlarmCallback(QueueTimer* queue_timer);
-
-  // Handles the periodic timer.
-  static void TimerCallback(QueueTimer* queue_timer);
-
-  HANDLE timer_queue_;
-  scoped_ptr<QueueTimer> queue_timer_;
-  scoped_event ev_;
-  volatile int cnt_;
-  volatile int max_cnt_;
-};
-
-void QueueTimerTest::AlarmCallback(QueueTimer* queue_timer) {
-  ASSERT_TRUE(queue_timer);
-  void* ctx = queue_timer->ctx();
-  QueueTimerTest* test = static_cast<QueueTimerTest*>(ctx);
-  test->cnt_ = 1;
-  ASSERT_TRUE(::SetEvent(get(test->ev_)));
-}
-
-void QueueTimerTest::TimerCallback(QueueTimer* queue_timer) {
-  ASSERT_TRUE(queue_timer);
-  void* ctx = queue_timer->ctx();
-  QueueTimerTest* test = static_cast<QueueTimerTest*>(ctx);
-
-  // Wait max_cnt_ ticks before signaling.
-  ++test->cnt_;
-  if (test->cnt_ == test->max_cnt_) {
-    ::SetEvent(get(test->ev_));
-  }
-}
-
-TEST_F(QueueTimerTest, QuickAlarm) {
-  queue_timer_.reset(new QueueTimer(timer_queue_,
-                                    &QueueTimerTest::AlarmCallback,
-                                    this));
-  const int kWaitTimeMaxMs = 1000;
-
-  // Set the timer to fire once right away.
-  LowResTimer timer(true);
-  ASSERT_HRESULT_SUCCEEDED(
-      queue_timer_->Start(0, 0, WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE));
-  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(ev_), kWaitTimeMaxMs));
-  EXPECT_EQ(1, cnt_);
-
-  // Expect the alarm to fire quickly.
-  EXPECT_GE(50u, timer.GetMilliseconds());
-}
-
-// The test takes about 50 seconds to run.
-TEST_F(QueueTimerTest, Alarm) {
-  if (!IsBuildSystem()) {
-    std::wcout << _T("\tThe long timing test below only runs on build system")
-               << std::endl;
-    return;
-  }
-
-  queue_timer_.reset(new QueueTimer(timer_queue_,
-                                    &QueueTimerTest::AlarmCallback,
-                                    this));
-  const int kWaitTimeMaxMs = 60 * 1000;     // 60 seconds.
-
-  // Set the timer to fire once after 5 sec, 10 sec, 15 sec, 20 sec and wait.
-  LowResTimer timer(false);
-  for (int i = 1; i <= 4; ++i) {
-    const int time_interval_ms = 5 * 1000 * i;
-    SCOPED_TRACE(testing::Message() << "time_interval_ms=" << time_interval_ms);
-
-    timer.Start();
-    ASSERT_HRESULT_SUCCEEDED(
-        queue_timer_->Start(time_interval_ms, 0, WT_EXECUTEONLYONCE));
-    EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(ev_), kWaitTimeMaxMs));
-
-    int actual_time_ms = timer.GetMilliseconds();
-    timer.Reset();
-
-    // Expect the alarm to fire anytime between a narrow interval.
-    EXPECT_EQ(1, cnt_);
-    EXPECT_LE(time_interval_ms - 50, actual_time_ms);
-    EXPECT_GE(time_interval_ms + 150, actual_time_ms);
-
-    cnt_ = 0;
-    ::ResetEvent(get(ev_));
-  }
-
-  // Set the timer to fire once after 2000 ms but do not wait for it to fire.
-  ASSERT_HRESULT_SUCCEEDED(
-      queue_timer_->Start(2000, 0, WT_EXECUTEONLYONCE));
-  EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(get(ev_), 100));
-  EXPECT_EQ(0, cnt_);
-}
-
-// The test takes about 35 seconds to run.
-TEST_F(QueueTimerTest, Timer) {
-  if (!IsBuildSystem()) {
-    std::wcout << _T("\tThe long timing test below only runs on build system")
-               << std::endl;
-    return;
-  }
-
-  queue_timer_.reset(new QueueTimer(timer_queue_,
-                                    &QueueTimerTest::TimerCallback,
-                                    this));
-  const int kWaitTimeMaxMs = 60 * 1000;       // 60 seconds.
-
-  max_cnt_ = 4;
-
-  // Set the timer to fire at 10 seconds intervals with an initial delay of
-  // 5 seconds.
-  LowResTimer timer(true);
-  ASSERT_HRESULT_SUCCEEDED(queue_timer_->Start(5000, 10000, WT_EXECUTEDEFAULT));
-  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(ev_), kWaitTimeMaxMs));
-
-  int actual_time_ms = timer.GetMilliseconds();
-
-  EXPECT_EQ(4, cnt_);
-  EXPECT_LE(35 * 1000 - 50, actual_time_ms);
-  EXPECT_GE(35 * 1000 + 350, actual_time_ms);
-
-  // Tests it can't start periodic timers more than one time.
-  ASSERT_EQ(E_UNEXPECTED, queue_timer_->Start(25, 50, WT_EXECUTEDEFAULT));
-}
-
-}  // namespace omaha
-
diff --git a/common/reactor.cc b/common/reactor.cc
deleted file mode 100644
index c9e10a3..0000000
--- a/common/reactor.cc
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include "omaha/common/reactor.h"
-
-#include <vector>
-#include "base/scoped_ptr.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/event_handler.h"
-
-namespace omaha {
-
-Reactor::Reactor() {
-  CORE_LOG(L4, (_T("[Reactor::Reactor]")));
-  ::InitializeCriticalSection(&cs_);
-}
-
-Reactor::~Reactor() {
-  CORE_LOG(L4, (_T("[Reactor::~Reactor]")));
-
-  // Each handle must be unregistered before destroying the reactor.
-  ASSERT1(handlers_.empty());
-  ::DeleteCriticalSection(&cs_);
-}
-
-// The reactor loop is just an efficient wait, as the demultiplexing of the
-// events is actually done by the OS thread pool.
-// TODO(omaha): replace the alertable wait with waiting on an event and provide
-// a method for the reactor to stop handling events.
-HRESULT Reactor::HandleEvents() {
-  CORE_LOG(L1, (_T("[Reactor::HandleEvents]")));
-  VERIFY1(::SleepEx(INFINITE, true) == WAIT_IO_COMPLETION);
-  CORE_LOG(L1, (_T("[Reactor::HandleEvents exit]")));
-  return S_OK;
-}
-
-void __stdcall Reactor::Callback(void* param, BOOLEAN timer_or_wait) {
-  ASSERT1(param);
-
-  // Since we wait an INFINITE the wait handle is always signaled.
-  VERIFY1(!timer_or_wait);
-  RegistrationState* state = static_cast<RegistrationState*>(param);
-  ASSERT1(state->reactor);
-  state->reactor->DoCallback(state);
-}
-
-// Method does not check to see if the same handle is registered twice.
-HRESULT Reactor::RegisterHandle(HANDLE handle,
-                                EventHandler* event_handler,
-                                uint32 flags) {
-  ASSERT1(handle);
-  ASSERT1(event_handler);
-
-  if (!handle || !event_handler) {
-    return E_INVALIDARG;
-  }
-
-  scoped_ptr<RegistrationState> state(new RegistrationState);
-  state->event_handler = event_handler;
-  state->handle = handle;
-  state->reactor = this;
-  state->flags = flags | WT_EXECUTEONLYONCE;
-
-  // The reactor only calls the handler once.
-  ASSERT1(WT_EXECUTEDEFAULT == 0);
-
-  // As soon as the handle is registered, the thread pool can queue up a
-  // callback and reenter the reactor on a different thread.
-  // Acquire the critical section before registering the handle.
-  ::EnterCriticalSection(&cs_);
-#if DEBUG
-  // The same handle should not be registered multiple times.
-  std::vector<RegistrationState*>::iterator it = handlers_.begin();
-  for (; it != handlers_.end(); ++it) {
-    ASSERT((*it)->handle != handle, (_T("[already registered %d]"), handle));
-  }
-#endif
-  bool res = !!::RegisterWaitForSingleObject(&state->wait_handle,
-                                             state->handle,
-                                             &Reactor::Callback,
-                                             state.get(),
-                                             INFINITE,
-                                             state->flags);
-  HRESULT hr = res ? S_OK : HRESULTFromLastError();
-  if (SUCCEEDED(hr)) {
-    handlers_.push_back(state.release());
-  }
-  ::LeaveCriticalSection(&cs_);
-
-  return hr;
-}
-
-HRESULT Reactor::RegisterHandle(HANDLE handle) {
-  ::EnterCriticalSection(&cs_);
-  HRESULT hr = DoRegisterHandle(handle);
-  ::LeaveCriticalSection(&cs_);
-  return hr;
-}
-
-HRESULT Reactor::DoRegisterHandle(HANDLE handle) {
-  ASSERT1(handle);
-  std::vector<RegistrationState*>::iterator it = handlers_.begin();
-  for (; it != handlers_.end(); ++it) {
-    if ((*it)->handle == handle) {
-      break;
-    }
-  }
-  if (it == handlers_.end()) {
-    // The handle is not registered with the reactor anymore. Registering the
-    // the handle again is not possible.
-    return E_FAIL;
-  }
-
-  // Unregister and register the handle again. Unregistering is an non blocking
-  // call.
-  RegistrationState* state = *it;
-  bool res = !!::UnregisterWaitEx(state->wait_handle, NULL);
-  if (!res && ::GetLastError() != ERROR_IO_PENDING) {
-    return HRESULTFromLastError();
-  }
-  if (!::RegisterWaitForSingleObject(&state->wait_handle,
-                                     state->handle,
-                                     &Reactor::Callback,
-                                     state,
-                                     INFINITE,
-                                     state->flags)) {
-    return HRESULTFromLastError();
-  }
-  return S_OK;
-}
-
-HRESULT Reactor::UnregisterHandle(HANDLE handle) {
-  ASSERT1(handle);
-  if (!handle) {
-    return E_INVALIDARG;
-  }
-
-  // Attempts to take the ownership of the registration state for the handle.
-  // If taking the ownership does not succeed, it means the handle has already
-  // been unregistered.
-  scoped_ptr<RegistrationState> state(ReleaseHandlerState(handle));
-  if (!state.get()) {
-    return E_UNEXPECTED;
-  }
-
-  // Unregisters the wait handle from the thread pool. The call blocks waiting
-  // for any pending callbacks to finish. No lock is being held while waiting
-  // here. If there is no callback pending, the call will succeed right away.
-  // Otherwise, if a callback has already started, the call waits for the
-  // callback to complete.
-  bool res = !!::UnregisterWaitEx(state->wait_handle, INVALID_HANDLE_VALUE);
-
-  // Clear the registration state, as a defensive programming measure and
-  // for debugging purposes.
-  state->reactor          = NULL;
-  state->handle           = NULL;
-  state->wait_handle      = NULL;
-  state->event_handler    = NULL;
-
-  return res ? S_OK : HRESULTFromLastError();
-}
-
-void Reactor::DoCallback(RegistrationState* state) {
-  ASSERT1(state);
-  ASSERT1(state->event_handler);
-  ASSERT1(state->handle);
-  state->event_handler->HandleEvent(state->handle);
-}
-
-// Looks up the registration state for a handle and releases the ownership
-// of it to the caller. As the clean up of the state can happen from multiple
-// places, the transfer of ownership ensures the clean up happens once and
-// only once.
-Reactor::RegistrationState* Reactor::ReleaseHandlerState(HANDLE handle) {
-  RegistrationState* registration_state = NULL;
-  ::EnterCriticalSection(&cs_);
-  std::vector<RegistrationState*>::iterator it = handlers_.begin();
-  for (; it != handlers_.end(); ++it) {
-    if ((*it)->handle == handle) {
-      registration_state = *it;
-      handlers_.erase(it);
-      break;
-    }
-  }
-  ::LeaveCriticalSection(&cs_);
-  return registration_state;
-}
-
-}  // namespace omaha
-
diff --git a/common/reactor_unittest.cc b/common/reactor_unittest.cc
deleted file mode 100644
index bacb397..0000000
--- a/common/reactor_unittest.cc
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include <stdlib.h>
-#include "base/scoped_ptr.h"
-#include "omaha/common/event_handler.h"
-#include "omaha/common/reactor.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-// TODO(omaha): rename EventHandler to EventHandlerInterface.
-
-// Creates and registers two waitable timers with the reactor. They go off
-// randomly until the reactor stops handling events.
-class ReactorTest
-    : public testing::Test,
-      public EventHandler {
- protected:
-  ReactorTest() : cnt_(0) {}
-
-  virtual void SetUp() {
-    // Timer handles are with auto reset for simplicity.
-    reset(timer1_, ::CreateWaitableTimer(NULL, false, NULL));
-    reset(timer2_, ::CreateWaitableTimer(NULL, false, NULL));
-
-    reset(event_done_, ::CreateEvent(NULL, true, false, NULL));
-
-    ASSERT_TRUE(timer1_);
-    ASSERT_TRUE(timer2_);
-    ASSERT_TRUE(event_done_);
-
-    // We only need the thread handle to queue an empty APC to it.
-    reset(main_thread_, ::OpenThread(THREAD_ALL_ACCESS,
-                                     false,
-                                     ::GetCurrentThreadId()));
-    ASSERT_TRUE(main_thread_);
-  }
-
-  virtual void TearDown() {
-  }
-
-  // EventHandler.
-  virtual void HandleEvent(HANDLE h);
-
-  // Empty APC to stop the reactor.
-  static void _stdcall Stop(ULONG_PTR) {}
-
-  // Returns an integer value in the [0, 10) range.
-  static int GetSmallInt() {
-    unsigned int val = 0;
-    rand_s(&val);
-    return val % 10;
-  }
-
-  Reactor reactor_;
-
-  scoped_timer timer1_;
-  scoped_timer timer2_;
-  scoped_event event_done_;
-
-  scoped_handle main_thread_;
-  LONG cnt_;
-  static const LONG ReactorTest::kMaxCount = 10;
-};
-
-const LONG ReactorTest::kMaxCount;
-
-void ReactorTest::HandleEvent(HANDLE h) {
-  EXPECT_TRUE(h);
-  if (h == get(event_done_)) {
-    ASSERT_TRUE(::QueueUserAPC(&ReactorTest::Stop,
-                               get(main_thread_),
-                               0));
-  } else if (h == get(timer1_) || h == get(timer2_)) {
-    // Check the handles auto reset correctly.
-    EXPECT_EQ(::WaitForSingleObject(h, 0), WAIT_TIMEOUT);
-    if (::InterlockedIncrement(&cnt_) > kMaxCount) {
-      ASSERT_TRUE(::SetEvent(get(event_done_)));
-    } else {
-      ASSERT_HRESULT_SUCCEEDED(reactor_.RegisterHandle(h));
-
-      unsigned int val = 0;
-      ASSERT_EQ(rand_s(&val), 0);
-      val %= 10;
-
-      // Set the timer to fire; negative values indicate relative time.
-      LARGE_INTEGER due_time_100ns = {0};
-      due_time_100ns.QuadPart = -(static_cast<int>(val) * 10 * 1000);
-      ASSERT_TRUE(::SetWaitableTimer(h, &due_time_100ns, 0, NULL, NULL, false));
-    }
-  }
-}
-
-// Registers the handles, primes the timers, and handles events.
-TEST_F(ReactorTest, HandleEvents) {
-  ASSERT_HRESULT_SUCCEEDED(reactor_.RegisterHandle(get(event_done_), this, 0));
-  ASSERT_HRESULT_SUCCEEDED(reactor_.RegisterHandle(get(timer1_), this, 0));
-  ASSERT_HRESULT_SUCCEEDED(reactor_.RegisterHandle(get(timer2_), this, 0));
-
-  LARGE_INTEGER due_time_100ns = {0};
-  ASSERT_TRUE(SetWaitableTimer(get(timer1_),
-                               &due_time_100ns,
-                               0,
-                               NULL,
-                               NULL,
-                               false));
-  ASSERT_TRUE(SetWaitableTimer(get(timer2_),
-                               &due_time_100ns,
-                               0,
-                               NULL,
-                               NULL,
-                               false));
-
-  ASSERT_HRESULT_SUCCEEDED(reactor_.HandleEvents());
-
-  ASSERT_HRESULT_SUCCEEDED(reactor_.UnregisterHandle(get(timer2_)));
-  ASSERT_HRESULT_SUCCEEDED(reactor_.UnregisterHandle(get(timer1_)));
-  ASSERT_HRESULT_SUCCEEDED(reactor_.UnregisterHandle(get(event_done_)));
-}
-
-}  // namespace omaha
diff --git a/common/reg_key.cc b/common/reg_key.cc
deleted file mode 100644
index 9a86d7a..0000000
--- a/common/reg_key.cc
+++ /dev/null
@@ -1,1270 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Registry configuration wrapers class implementation
-
-#include <raserror.h>
-#include "omaha/common/reg_key.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/common/static_assert.h"
-#include "omaha/common/string.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/common/system.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-HRESULT RegKey::Close() {
-  HRESULT hr = S_OK;
-  if (h_key_ != NULL) {
-    LONG res = RegCloseKey(h_key_);
-    hr = HRESULT_FROM_WIN32(res);
-    h_key_ = NULL;
-  }
-  return hr;
-}
-
-HRESULT RegKey::Create(HKEY hKeyParent,
-                       const TCHAR * key_name,
-                       TCHAR * lpszClass,
-                       DWORD options,
-                       REGSAM sam_desired,
-                       LPSECURITY_ATTRIBUTES lpSecAttr,
-                       LPDWORD lpdwDisposition) {
-  // lpszClass may be NULL
-  ASSERT1(key_name);
-  ASSERT1(hKeyParent != NULL);
-  DWORD dw;
-  HKEY hKey = NULL;
-  LONG res = ::RegCreateKeyEx(hKeyParent,
-                              key_name,
-                              0,
-                              lpszClass,
-                              options,
-                              sam_desired,
-                              lpSecAttr,
-                              &hKey,
-                              &dw);
-  HRESULT hr = HRESULT_FROM_WIN32(res);
-
-  if (lpdwDisposition != NULL)
-    *lpdwDisposition = dw;
-  // we have to close the currently opened key
-  // before replacing it with the new one
-  if (hr == S_OK) {
-    hr = Close();
-    ASSERT1(hr == S_OK);
-    h_key_ = hKey;
-  }
-  return hr;
-}
-
-HRESULT RegKey::Create(const TCHAR * full_key_name,
-                       TCHAR * lpszClass, DWORD options,
-                       REGSAM sam_desired,
-                       LPSECURITY_ATTRIBUTES lpSecAttr,
-                       LPDWORD lpdwDisposition) {
-  // lpszClass may be NULL
-  ASSERT1(full_key_name);
-  CString key_name(full_key_name);
-
-  HKEY parent_key = RegKey::GetRootKeyInfo(&key_name);
-  if (!parent_key) {
-    ASSERT(false, (_T("unable to get root key location %s"), full_key_name));
-    return HRESULT_FROM_WIN32(ERROR_KEY_NOT_FOUND);
-  }
-
-  return Create(parent_key, key_name, lpszClass,
-    options, sam_desired, lpSecAttr, lpdwDisposition);
-}
-
-HRESULT RegKey::CreateKeys(const TCHAR* keys_to_create[],
-                           DWORD number_of_keys,
-                           TCHAR* lpszClass,
-                           DWORD options,
-                           LPSECURITY_ATTRIBUTES lpSecAttr) {
-  ASSERT1(keys_to_create);
-  ASSERT1(number_of_keys);
-
-  for (DWORD i = 0; i < number_of_keys; i++) {
-    HRESULT hr = CreateKey(keys_to_create[i], lpszClass, options, lpSecAttr);
-    if (FAILED(hr)) {
-      return hr;
-    }
-  }
-
-  return S_OK;
-}
-
-HRESULT RegKey::CreateKey(const TCHAR* full_key_name,
-                          TCHAR* lpszClass,
-                          DWORD options,
-                          LPSECURITY_ATTRIBUTES lpSecAttr) {
-  ASSERT1(full_key_name);
-
-  RegKey key;
-  HRESULT hr = key.Create(full_key_name,
-                          lpszClass,
-                          options,
-                          KEY_ALL_ACCESS,
-                          lpSecAttr,
-                          NULL);
-  if (FAILED(hr)) {
-    UTIL_LOG(L3, (_T("[couldn't create %s reg key]"), full_key_name));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT RegKey::Open(HKEY hKeyParent,
-                     const TCHAR * key_name,
-                     REGSAM sam_desired) {
-  ASSERT1(key_name);
-  ASSERT1(hKeyParent != NULL);
-  HKEY hKey = NULL;
-  LONG res = ::RegOpenKeyEx(hKeyParent, key_name, 0, sam_desired, &hKey);
-  HRESULT hr = HRESULT_FROM_WIN32(res);
-
-  // we have to close the currently opened key
-  // before replacing it with the new one
-  if (hr == S_OK) {
-    // close the currently opened key if any
-    hr = Close();
-    ASSERT1(hr == S_OK);
-    h_key_ = hKey;
-  }
-  return hr;
-}
-
-HRESULT RegKey::Open(const TCHAR * full_key_name, REGSAM sam_desired) {
-  ASSERT1(full_key_name);
-  CString key_name(full_key_name);
-
-  HKEY parent_key = RegKey::GetRootKeyInfo(&key_name);
-  if (!parent_key) {
-    ASSERT(false, (_T("unable to get root key for %s"), full_key_name));
-    return HRESULT_FROM_WIN32(ERROR_KEY_NOT_FOUND);
-  }
-
-  return Open(parent_key, key_name, sam_desired);
-}
-
-// save the key and all of its subkeys and values to a file
-HRESULT RegKey::Save(const TCHAR* full_key_name, const TCHAR* file_name) {
-  ASSERT1(full_key_name);
-  ASSERT1(file_name);
-
-  CString key_name(full_key_name);
-  HKEY h_key = GetRootKeyInfo(&key_name);
-  if (!h_key) {
-    return E_FAIL;
-  }
-
-  RegKey key;
-  HRESULT hr = key.Open(h_key, key_name, KEY_READ);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  System::AdjustPrivilege(SE_BACKUP_NAME, true);
-  LONG res = ::RegSaveKey(key.h_key_, file_name, NULL);
-  System::AdjustPrivilege(SE_BACKUP_NAME, false);
-
-  return HRESULT_FROM_WIN32(res);
-}
-
-// restore the key and all of its subkeys and values which are saved into a file
-HRESULT RegKey::Restore(const TCHAR* full_key_name, const TCHAR* file_name) {
-  ASSERT1(full_key_name);
-  ASSERT1(file_name);
-
-  CString key_name(full_key_name);
-  HKEY h_key = GetRootKeyInfo(&key_name);
-  if (!h_key) {
-    return E_FAIL;
-  }
-
-  RegKey key;
-  HRESULT hr = key.Open(h_key, key_name, KEY_WRITE);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  System::AdjustPrivilege(SE_RESTORE_NAME, true);
-  LONG res = ::RegRestoreKey(key.h_key_, file_name, REG_FORCE_RESTORE);
-  System::AdjustPrivilege(SE_RESTORE_NAME, false);
-
-  return HRESULT_FROM_WIN32(res);
-}
-
-// check if the current key has the specified subkey
-bool RegKey::HasSubkey(const TCHAR * key_name) const {
-  ASSERT1(key_name);
-  ASSERT1(h_key_);
-
-  RegKey key;
-  HRESULT hr = key.Open(h_key_, key_name, KEY_READ);
-  key.Close();
-  return S_OK == hr;
-}
-
-// static flush key
-HRESULT RegKey::FlushKey(const TCHAR * full_key_name) {
-  ASSERT1(full_key_name);
-
-  HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
-  // get the root HKEY
-  CString key_name(full_key_name);
-  HKEY h_key = GetRootKeyInfo(&key_name);
-
-  if (h_key != NULL) {
-    LONG res = RegFlushKey(h_key);
-    hr = HRESULT_FROM_WIN32(res);
-  }
-  return hr;
-}
-
-// static SET helper
-HRESULT RegKey::SetValueStaticHelper(const TCHAR * full_key_name,
-                                     const TCHAR * value_name,
-                                     DWORD type,
-                                     LPVOID value,
-                                     DWORD byte_count) {
-  // value_name may be NULL
-  ASSERT1(full_key_name);
-
-  HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
-  // get the root HKEY
-  CString key_name(full_key_name);
-  HKEY h_key = GetRootKeyInfo(&key_name);
-
-  if (h_key != NULL) {
-    RegKey key;
-    hr = key.Create(h_key, key_name.GetString());
-    if (hr == S_OK) {
-      switch (type) {
-        case REG_DWORD:
-          hr = key.SetValue(value_name, *reinterpret_cast<DWORD *>(value));
-          if (SUCCEEDED(hr)) {
-            UTIL_LOG(L6, (_T("[Wrote int32 key: %s\\%s = %d]"),
-                          full_key_name,
-                          value_name,
-                          *reinterpret_cast<DWORD*>(value)));
-          }
-          break;
-        case REG_QWORD:
-          hr = key.SetValue(value_name, *reinterpret_cast<DWORD64 *>(value));
-          if (SUCCEEDED(hr)) {
-            UTIL_LOG(L6, (_T("[Wrote int64 key: %s\\%s = %s]"),
-                          full_key_name,
-                          value_name,
-                          String_Int64ToString(
-                              *reinterpret_cast<DWORD64*>(value), 10)));
-          }
-          break;
-        case REG_SZ:
-          hr = key.SetValue(value_name, reinterpret_cast<const TCHAR *>(value));
-          if (SUCCEEDED(hr)) {
-            UTIL_LOG(L6, (_T("[Wrote string key: %s\\%s = %s]"),
-                          full_key_name,
-                          value_name,
-                          reinterpret_cast<const TCHAR *>(value)));
-          }
-          break;
-        case REG_BINARY:
-          hr = key.SetValue(value_name,
-                            reinterpret_cast<const byte *>(value),
-                            byte_count);
-          if (SUCCEEDED(hr)) {
-            UTIL_LOG(L6, (_T("[Wrote binary key: %s\\%s, len = %d]"),
-                          full_key_name, value_name, byte_count));
-          }
-          break;
-        case REG_MULTI_SZ:
-          hr = key.SetValue(value_name,
-                            reinterpret_cast<const byte *>(value),
-                            byte_count,
-                            type);
-          if (SUCCEEDED(hr)) {
-            UTIL_LOG(L6, (_T("[Wrote multi-sz key: %s\\%s, len = %d]"),
-                          full_key_name, value_name, byte_count));
-          }
-          break;
-        case REG_EXPAND_SZ:
-          hr = key.SetStringValue(value_name,
-                                  reinterpret_cast<const TCHAR *>(value),
-                                  type);
-          if (SUCCEEDED(hr)) {
-            UTIL_LOG(L6, (_T("[Wrote expandable string key: %s\\%s = %s]"),
-                          full_key_name, value_name, (const TCHAR *)value));
-          }
-          break;
-        default:
-          ASSERT(false, (_T("Unsupported Registry Type")));
-          hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
-          break;
-      }
-      // close the key after writing
-      HRESULT temp_res = key.Close();
-      if (hr == S_OK) {
-        hr = temp_res;
-      } else {
-        ASSERT(false, (_T("Failed to write reg key: %s\\%s (hr=0x%x)"),
-                       full_key_name, value_name, hr));
-      }
-    } else {
-      UTIL_LOG(L3, (_T("[Failed to create reg key: %s]"), full_key_name));
-    }
-  }
-  return hr;
-}
-
-// static GET helper
-// byte_count may be NULL.
-// value_name may be NULL.
-HRESULT RegKey::GetValueStaticHelper(const TCHAR * full_key_name,
-                                     const TCHAR * value_name,
-                                     DWORD type,
-                                     LPVOID value,
-                                     DWORD * byte_count) {
-  ASSERT1(full_key_name);
-
-  HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
-  // get the root HKEY
-  CString key_name(full_key_name);
-  HKEY h_key = GetRootKeyInfo(&key_name);
-
-  if (h_key != NULL) {
-    RegKey key;
-    hr = key.Open(h_key, key_name.GetString(), KEY_READ);
-    if (hr == S_OK) {
-      switch (type) {
-        case REG_DWORD:
-          hr = key.GetValue(value_name, reinterpret_cast<DWORD *>(value));
-          if (SUCCEEDED(hr)) {
-            UTIL_LOG(L6, (_T("[Read int32 key: %s\\%s = %d]"),
-                          full_key_name,
-                          value_name,
-                          *reinterpret_cast<DWORD*>(value)));
-          }
-          break;
-        case REG_QWORD:
-          hr = key.GetValue(value_name, reinterpret_cast<DWORD64 *>(value));
-          if (SUCCEEDED(hr)) {
-            UTIL_LOG(L6, (_T("[Read int64 key: %s\\%s = %s]"),
-                          full_key_name,
-                          value_name,
-                          String_Int64ToString(
-                              *(reinterpret_cast<DWORD64*>(value)), 10)));
-          }
-          break;
-        case REG_SZ:
-          hr = key.GetValue(value_name, reinterpret_cast<TCHAR * *>(value));
-          if (SUCCEEDED(hr)) {
-            UTIL_LOG(L6, (_T("[Read string key: %s\\%s = %s]"),
-                          full_key_name,
-                          value_name,
-                          *reinterpret_cast<TCHAR * *>(value)));
-          }
-          break;
-        case REG_MULTI_SZ:
-          hr = key.GetValue(value_name,
-                            reinterpret_cast<std::vector<CString> *>(value));
-          if (SUCCEEDED(hr)) {
-            UTIL_LOG(L6, (_T("[Read multi string key: %s\\%s = %d]"),
-                full_key_name,
-                value_name,
-                reinterpret_cast<std::vector<CString>*>(value)->size()));
-          }
-          break;
-        case REG_BINARY:
-          hr = key.GetValue(value_name,
-                            reinterpret_cast<byte * *>(value),
-                            byte_count);
-          if (SUCCEEDED(hr)) {
-            UTIL_LOG(L6, (_T("[Read binary key: %s\\%s, len = %d]"),
-                          full_key_name, value_name, byte_count));
-          }
-          break;
-        default:
-          ASSERT(false, (_T("Unsupported Registry Type")));
-          hr = HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
-          break;
-      }
-      // close the key after writing
-      HRESULT temp_res = key.Close();
-      if (hr == S_OK) {
-        hr = temp_res;
-      } else {
-        UTIL_LOG(L5, (_T("[Failed to read key: %s\\%s]"),
-                      full_key_name, value_name));
-      }
-    } else {
-      UTIL_LOG(L5, (_T("[reg key does not exist: %s]"), key_name));
-    }
-  }
-  return hr;
-}
-
-// GET helper
-// value_name may be NULL.
-HRESULT RegKey::GetValueHelper(const TCHAR * value_name,
-                               DWORD * type,
-                               byte * * value,
-                               DWORD * byte_count) const {
-  ASSERT1(byte_count);
-  ASSERT1(value);
-  ASSERT1(type);
-  ASSERT1(h_key_);
-
-  // init return buffer
-  *value = NULL;
-
-  // get the size of the return data buffer
-  LONG res = ::SHQueryValueEx(h_key_, value_name, NULL, type, NULL, byte_count);
-  HRESULT hr = HRESULT_FROM_WIN32(res);
-
-  if (hr == S_OK) {
-    // if the value length is 0, nothing to do
-    if (*byte_count != 0) {
-      // allocate the buffer
-      *value = new byte[*byte_count];
-      ASSERT1(*value);
-
-      // make the call again to get the data
-      res = ::SHQueryValueEx(h_key_,
-                             value_name,
-                             NULL,
-                             type,
-                             *value,
-                             byte_count);
-      hr = HRESULT_FROM_WIN32(res);
-      ASSERT1(S_OK == hr);
-    }
-  }
-  return hr;
-}
-
-// Int32 Get
-// value_name may be NULL.
-HRESULT RegKey::GetValue(const TCHAR * value_name, DWORD * value) const {
-  ASSERT1(value);
-  ASSERT1(h_key_);
-
-  DWORD type = 0;
-  DWORD byte_count = sizeof(DWORD);
-  LONG res = ::SHQueryValueEx(h_key_,
-                              value_name,
-                              NULL,
-                              &type,
-                              reinterpret_cast<byte*>(value),
-                              &byte_count);
-  HRESULT hr = HRESULT_FROM_WIN32(res);
-  ASSERT1((hr != S_OK) || (type == REG_DWORD));
-  ASSERT1((hr != S_OK) || (byte_count == sizeof(DWORD)));
-  return hr;
-}
-
-// Int64 Get
-// value_name may be NULL.
-HRESULT RegKey::GetValue(const TCHAR * value_name, DWORD64 * value) const {
-  ASSERT1(value);
-  ASSERT1(h_key_);
-
-  DWORD type = 0;
-  DWORD byte_count = sizeof(DWORD64);
-  LONG res = ::SHQueryValueEx(h_key_,
-                              value_name,
-                              NULL,
-                              &type,
-                              reinterpret_cast<byte *>(value),
-                              &byte_count);
-  HRESULT hr = HRESULT_FROM_WIN32(res);
-  ASSERT1((hr != S_OK) || (type == REG_QWORD));
-  ASSERT1((hr != S_OK) || (byte_count == sizeof(DWORD64)));
-  return hr;
-}
-
-// String Get
-// value_name may be NULL.
-HRESULT RegKey::GetValue(const TCHAR * value_name, TCHAR * * value) const {
-  ASSERT1(value);
-  ASSERT1(h_key_);
-
-  DWORD byte_count = 0;
-  DWORD type = 0;
-
-  // first get the size of the string buffer
-  LONG res = ::SHQueryValueEx(h_key_,
-                              value_name,
-                              NULL,
-                              &type,
-                              NULL,
-                              &byte_count);
-  HRESULT hr = HRESULT_FROM_WIN32(res);
-
-  if (hr == S_OK) {
-    // allocate room for the string and a terminating \0
-    *value = new TCHAR[(byte_count / sizeof(TCHAR)) + 1];
-
-    if ((*value) != NULL) {
-      if (byte_count != 0) {
-        // make the call again
-        res = ::SHQueryValueEx(h_key_, value_name, NULL, &type,
-                              reinterpret_cast<byte*>(*value), &byte_count);
-        hr = HRESULT_FROM_WIN32(res);
-      } else {
-        (*value)[0] = _T('\0');
-      }
-
-      ASSERT1((hr != S_OK) || (type == REG_SZ) ||
-              (type == REG_MULTI_SZ) || (type == REG_EXPAND_SZ));
-    } else {
-      hr = E_OUTOFMEMORY;
-    }
-  }
-
-  return hr;
-}
-
-// CString Get
-// value_name may be NULL.
-HRESULT RegKey::GetValue(const TCHAR* value_name, OUT CString* value) const {
-  ASSERT1(value);
-  ASSERT1(h_key_);
-
-  DWORD byte_count = 0;
-  DWORD type = 0;
-
-  // first get the size of the string buffer
-  LONG res = ::SHQueryValueEx(h_key_,
-                              value_name,
-                              NULL,
-                              &type,
-                              NULL,
-                              &byte_count);
-  HRESULT hr = HRESULT_FROM_WIN32(res);
-
-  if (hr == S_OK) {
-    if (byte_count != 0) {
-      // Allocate some memory and make the call again
-      TCHAR* buffer = value->GetBuffer(byte_count / sizeof(TCHAR) + 1);
-      if (buffer == NULL) {
-        hr = E_OUTOFMEMORY;
-      } else {
-        res = ::SHQueryValueEx(h_key_, value_name, NULL, &type,
-                              reinterpret_cast<byte*>(buffer), &byte_count);
-        hr = HRESULT_FROM_WIN32(res);
-      }
-      value->ReleaseBuffer();
-    } else {
-      value->Empty();
-    }
-
-    ASSERT1((hr != S_OK) || (type == REG_SZ) ||
-            (type == REG_MULTI_SZ) || (type == REG_EXPAND_SZ));
-  }
-
-  return hr;
-}
-
-// convert REG_MULTI_SZ bytes to string array
-HRESULT RegKey::MultiSZBytesToStringArray(const byte * buffer,
-                                          DWORD byte_count,
-                                          std::vector<CString> * value) {
-  ASSERT1(buffer);
-  ASSERT1(value);
-
-  const TCHAR* data = reinterpret_cast<const TCHAR*>(buffer);
-  DWORD data_len = byte_count / sizeof(TCHAR);
-  value->clear();
-  if (data_len > 1) {
-    // must be terminated by two null characters
-    if (data[data_len - 1] != 0 || data[data_len - 2] != 0) {
-      return E_INVALIDARG;
-    }
-
-    // put null-terminated strings into arrays
-    while (*data) {
-      CString str(data);
-      value->push_back(str);
-      data += str.GetLength() + 1;
-    }
-  }
-  return S_OK;
-}
-
-// get a vector<CString> value from REG_MULTI_SZ type
-HRESULT RegKey::GetValue(const TCHAR * value_name,
-                         std::vector<CString> * value) const {
-  ASSERT1(value);
-  // value_name may be NULL
-
-  DWORD byte_count = 0;
-  DWORD type = 0;
-  byte* buffer = 0;
-
-  // first get the size of the buffer
-  HRESULT hr = GetValueHelper(value_name, &type, &buffer, &byte_count);
-  ASSERT1((hr != S_OK) || (type == REG_MULTI_SZ));
-
-  if (SUCCEEDED(hr)) {
-    hr = MultiSZBytesToStringArray(buffer, byte_count, value);
-  }
-
-  return hr;
-}
-
-// Binary data Get
-HRESULT RegKey::GetValue(const TCHAR * value_name,
-                         byte * * value,
-                         DWORD * byte_count) const {
-  ASSERT1(byte_count);
-  ASSERT1(value);
-  // value_name may be NULL
-
-  DWORD type = 0;
-  HRESULT hr = GetValueHelper(value_name, &type, value, byte_count);
-  ASSERT1((hr != S_OK) || (type == REG_MULTI_SZ) || (type == REG_BINARY));
-  return hr;
-}
-
-// Raw data get
-HRESULT RegKey::GetValue(const TCHAR * value_name,
-                         byte * * value,
-                         DWORD * byte_count,
-                         DWORD *type) const {
-  ASSERT1(type);
-  ASSERT1(byte_count);
-  ASSERT1(value);
-
-  return GetValueHelper(value_name, type, value, byte_count);
-}
-
-// Int32 set
-// value_name may be NULL
-HRESULT RegKey::SetValue(const TCHAR * value_name, DWORD value) const {
-  ASSERT1(h_key_);
-  LONG res = RegSetValueEx(h_key_,
-                           value_name,
-                           NULL,
-                           REG_DWORD,
-                           reinterpret_cast<byte *>(&value),
-                           sizeof(DWORD));
-  return HRESULT_FROM_WIN32(res);
-}
-
-// Int64 set
-// value_name may be NULL
-HRESULT RegKey::SetValue(const TCHAR * value_name, DWORD64 value) const {
-  ASSERT1(h_key_);
-  LONG res = RegSetValueEx(h_key_,
-                           value_name,
-                           NULL,
-                           REG_QWORD,
-                           reinterpret_cast<byte *>(&value),
-                           sizeof(DWORD64));
-  return HRESULT_FROM_WIN32(res);
-}
-
-// String set
-HRESULT RegKey::SetValue(const TCHAR * value_name, const TCHAR * value) const {
-  return SetStringValue(value_name, value, REG_SZ);
-}
-
-// String set helper
-// value_name may be NULL.
-HRESULT RegKey::SetStringValue(const TCHAR * value_name,
-                               const TCHAR * value,
-                               DWORD type) const {
-  ASSERT1(value);
-  ASSERT1(h_key_);
-  ASSERT1(type == REG_SZ || type == REG_EXPAND_SZ);
-  LONG res = RegSetValueEx(h_key_,
-                           value_name,
-                           NULL,
-                           type,
-                           reinterpret_cast<const byte *>(value),
-                           (lstrlen(value) + 1) * sizeof(TCHAR));
-  return HRESULT_FROM_WIN32(res);
-}
-
-// Binary data set
-// value may be NULL.
-// value_name may be NULL.
-HRESULT RegKey::SetValue(const TCHAR * value_name,
-                         const byte * value,
-                         DWORD byte_count) const {
-  ASSERT1(h_key_);
-
-  // special case - if 'value' is NULL make sure byte_count is zero
-  if (value == NULL) {
-    byte_count = 0;
-  }
-
-  LONG res = RegSetValueEx(h_key_,
-                           value_name,
-                           NULL,
-                           REG_BINARY,
-                           value,
-                           byte_count);
-  return HRESULT_FROM_WIN32(res);
-}
-
-// Raw data set
-// value_name may be NULL.
-HRESULT RegKey::SetValue(const TCHAR * value_name,
-                         const byte * value,
-                         DWORD byte_count,
-                         DWORD type) const {
-  ASSERT1(value);
-  ASSERT1(h_key_);
-  LONG res = RegSetValueEx(h_key_, value_name, NULL, type, value, byte_count);
-  return HRESULT_FROM_WIN32(res);
-}
-
-HRESULT RegKey::RenameValue(const TCHAR* old_value_name,
-                            const TCHAR* new_value_name) const {
-  ASSERT1(h_key_);
-  ASSERT1(new_value_name);
-  ASSERT1(old_value_name);
-
-  scoped_ptr<byte> value;
-  DWORD byte_count = 0;
-  DWORD type = 0;
-
-  HRESULT hr = GetValue(old_value_name, address(value), &byte_count, &type);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = SetValue(new_value_name, value.get(), byte_count, type);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  VERIFY1(SUCCEEDED(DeleteValue(old_value_name)));
-  return S_OK;
-}
-
-bool RegKey::HasKey(const TCHAR * full_key_name) {
-  return HasKeyHelper(full_key_name, KEY_READ);
-}
-
-bool RegKey::HasNativeKey(const TCHAR * full_key_name) {
-  return HasKeyHelper(full_key_name, KEY_READ | KEY_WOW64_64KEY);
-}
-
-bool RegKey::HasKeyHelper(const TCHAR * full_key_name, DWORD sam_flags) {
-  ASSERT1(full_key_name);
-  ASSERT1(sam_flags & KEY_READ);
-
-  // get the root HKEY
-  CString key_name(full_key_name);
-  HKEY h_key = GetRootKeyInfo(&key_name);
-
-  if (h_key != NULL) {
-    RegKey key;
-    HRESULT hr = key.Open(h_key, key_name.GetString(), sam_flags);
-    key.Close();
-    return S_OK == hr;
-  }
-  return false;
-}
-
-HRESULT RegKey::CopyValue(const TCHAR * full_from_key_name,
-                          const TCHAR * from_value_name,
-                          const TCHAR * full_to_key_name,
-                          const TCHAR * to_value_name) {
-  ASSERT1(full_from_key_name);
-  ASSERT1(full_to_key_name);
-
-  RegKey from_reg_key;
-  HRESULT hr = from_reg_key.Open(full_from_key_name, KEY_READ);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  scoped_ptr<byte> val;
-  DWORD byte_count = 0;
-  DWORD type = 0;
-  hr = from_reg_key.GetValue(from_value_name, address(val), &byte_count, &type);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  RegKey to_reg_key;
-  hr = to_reg_key.Open(full_to_key_name, KEY_WRITE);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return to_reg_key.SetValue(to_value_name, val.get(), byte_count, type);
-}
-
-// static version of HasValue
-bool RegKey::HasValue(const TCHAR * full_key_name, const TCHAR * value_name) {
-  ASSERT1(full_key_name);
-
-  bool has_value = false;
-  // get the root HKEY
-  CString key_name(full_key_name);
-  HKEY h_key = GetRootKeyInfo(&key_name);
-
-  if (h_key != NULL) {
-    RegKey key;
-    if (key.Open(h_key, key_name.GetString(), KEY_READ) == S_OK) {
-      has_value = key.HasValue(value_name);
-      key.Close();
-    }
-  }
-  return has_value;
-}
-
-HRESULT RegKey::GetValueType(const TCHAR* full_key_name,
-                             const TCHAR* value_name,
-                             DWORD* value_type) {
-  ASSERT1(full_key_name);
-  // value_name may be NULL
-  ASSERT1(value_type);
-
-  *value_type = REG_NONE;
-
-  CString key_name(full_key_name);
-  HKEY root_key = GetRootKeyInfo(&key_name);
-
-  HKEY key = NULL;
-  LONG res = ::RegOpenKeyEx(root_key, key_name, 0, KEY_READ, &key);
-  if (res != ERROR_SUCCESS) {
-    return HRESULT_FROM_WIN32(res);
-  }
-
-  scoped_hkey hkey(key);
-
-  res = ::SHQueryValueEx(get(hkey), value_name, NULL, value_type, NULL, NULL);
-  if (res != ERROR_SUCCESS) {
-    return HRESULT_FROM_WIN32(res);
-  }
-
-  return S_OK;
-}
-
-HRESULT RegKey::DeleteKey(const TCHAR* full_key_name) {
-  ASSERT1(full_key_name);
-
-  return DeleteKey(full_key_name, true);
-}
-
-HRESULT RegKey::DeleteKey(const TCHAR* full_key_name, bool recursively) {
-  ASSERT1(full_key_name);
-
-  // need to open the parent key first
-  // get the root HKEY
-  CString key_name(full_key_name);
-  HKEY h_key = GetRootKeyInfo(&key_name);
-
-  // get the parent key
-  CString parent_key(GetParentKeyInfo(&key_name));
-
-  RegKey key;
-  HRESULT hr = key.Open(h_key, parent_key);
-
-  if (hr == S_OK) {
-    hr = recursively ? key.RecurseDeleteSubKey(key_name) :
-                       key.DeleteSubKey(key_name);
-  } else if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
-             hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) {
-    hr = S_FALSE;
-  }
-
-  key.Close();
-  return hr;
-}
-
-HRESULT RegKey::DeleteValue(const TCHAR * full_key_name,
-                            const TCHAR * value_name) {
-  ASSERT1(value_name);
-  ASSERT1(full_key_name);
-
-  HRESULT hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
-  // get the root HKEY
-  CString key_name(full_key_name);
-  HKEY h_key = GetRootKeyInfo(&key_name);
-
-  if (h_key != NULL) {
-    RegKey key;
-    hr = key.Open(h_key, key_name.GetString());
-    if (hr == S_OK) {
-      hr = key.DeleteValue(value_name);
-      key.Close();
-    }
-  }
-  return hr;
-}
-
-HRESULT RegKey::RecurseDeleteSubKey(const TCHAR * key_name) {
-  ASSERT1(key_name);
-  ASSERT1(h_key_);
-
-  RegKey key;
-  HRESULT hr = key.Open(h_key_, key_name);
-  if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
-      hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) {
-    hr = S_FALSE;
-  }
-  if (hr != S_OK) {
-    return hr;
-  }
-
-  // enumerate all subkeys of this key
-  // and recursivelly delete them
-  FILETIME time;
-  TCHAR key_name_buf[kMaxKeyNameChars];
-  DWORD key_name_buf_size = kMaxKeyNameChars;
-  while (RegEnumKeyEx(key.h_key_,
-                      0,
-                      key_name_buf,
-                      &key_name_buf_size,
-                      NULL,
-                      NULL,
-                      NULL,
-                      &time) == ERROR_SUCCESS) {
-    hr = key.RecurseDeleteSubKey(key_name_buf);
-    // return if error deleting key
-    if (hr != S_OK)
-      return hr;
-    // restore the buffer size
-    key_name_buf_size = kMaxKeyNameChars;
-  }
-  // close the top key
-  key.Close();
-
-  // the key has no more children keys
-  // delete the key and all of its values
-  return DeleteSubKey(key_name);
-}
-
-HKEY RegKey::GetRootKeyInfo(CString * full_key_name) {
-  ASSERT1(full_key_name);
-
-  HKEY h_key = NULL;
-  // get the root HKEY
-  int index = String_FindChar(*(full_key_name), '\\');
-  CString root_key;
-
-  if (index == -1) {
-    root_key = *full_key_name;
-    *full_key_name = _T("");
-  } else {
-    root_key= full_key_name->Left(index);
-    *full_key_name =
-        full_key_name->Right(full_key_name->GetLength() - index - 1);
-  }
-
-  if (!root_key.CompareNoCase(_T("HKLM")) ||
-      !root_key.CompareNoCase(_T("HKEY_LOCAL_MACHINE")))
-    h_key = HKEY_LOCAL_MACHINE;
-  else if (!root_key.CompareNoCase(_T("HKCU")) ||
-           !root_key.CompareNoCase(_T("HKEY_CURRENT_USER")))
-    h_key = HKEY_CURRENT_USER;
-  else if (!root_key.CompareNoCase(_T("HKU")) ||
-           !root_key.CompareNoCase(_T("HKEY_USERS")))
-    h_key = HKEY_USERS;
-  else if (!root_key.CompareNoCase(_T("HKCR")) ||
-           !root_key.CompareNoCase(_T("HKEY_CLASSES_ROOT")))
-    h_key = HKEY_CLASSES_ROOT;
-
-  return h_key;
-}
-
-
-// Returns true if this key name is 'safe' for deletion (doesn't specify a
-// key root)
-bool RegKey::SafeKeyNameForDeletion(const wchar_t *key_name) {
-  ASSERT1(key_name);
-  CString key(key_name);
-
-  HKEY root_key = GetRootKeyInfo(&key);
-
-  if ( !root_key ) {
-    key = key_name;
-  }
-  if ( key.IsEmpty() ) {
-    return false;
-  }
-  bool found_subkey = false, backslash_found = false;
-  for (int i = 0 ; i < key.GetLength() ; ++i) {
-    if ( key[i] == L'\\' ) {
-      backslash_found = true;
-    } else if ( backslash_found ) {
-      found_subkey = true;
-      break;
-    }
-  }
-  return ( root_key == HKEY_USERS ) ? found_subkey : true;
-}
-
-CString RegKey::GetParentKeyInfo(CString * key_name) {
-  ASSERT1(key_name);
-
-  // get the parent key
-  int index = key_name->ReverseFind('\\');
-  CString parent_key;
-  if (index == -1) {
-    parent_key = _T("");
-  } else {
-    parent_key = key_name->Left(index);
-    *key_name = key_name->Right(key_name->GetLength() - index - 1);
-  }
-
-  return parent_key;
-}
-
-// get the number of values for this key
-uint32 RegKey::GetValueCount() {
-  ASSERT1(h_key_);
-  // number of values for key
-  DWORD  num_values = 0;
-
-  LONG res = ::RegQueryInfoKey(h_key_,       // key handle
-                               NULL,         // buffer for class name
-                               NULL,         // size of class string
-                               NULL,         // reserved
-                               NULL,         // number of subkeys
-                               NULL,         // longest subkey size
-                               NULL,         // longest class string
-                               &num_values,  // number of values for this key
-                               NULL,         // longest value name
-                               NULL,         // longest value data
-                               NULL,         // security descriptor
-                               NULL);        // last write time
-
-  ASSERT1(res == ERROR_SUCCESS);
-  return num_values;
-}
-
-// Enumerators for the value_names for this key
-
-// Called to get the value name for the given value name index
-// Use GetValueCount() to get the total value_name count for this key
-// Returns failure if no key at the specified index
-// type may be NULL.
-HRESULT RegKey::GetValueNameAt(int index, CString *value_name, DWORD *type) {
-  ASSERT1(value_name);
-  ASSERT1(h_key_);
-
-  LONG res = ERROR_SUCCESS;
-  TCHAR value_name_buf[kMaxValueNameChars];
-  DWORD value_name_buf_size = kMaxValueNameChars;
-  res = ::RegEnumValue(h_key_,
-                       index,
-                       value_name_buf,
-                       &value_name_buf_size,
-                       NULL,
-                       type,
-                       NULL,
-                       NULL);
-
-  if (res == ERROR_SUCCESS) {
-    value_name->SetString(value_name_buf);
-  }
-
-  return HRESULT_FROM_WIN32(res);
-}
-
-uint32 RegKey::GetSubkeyCount() {
-  ASSERT1(h_key_);
-
-  DWORD num_subkeys = 0;   // number of values for key
-
-  LONG res = ::RegQueryInfoKey(h_key_,        // key handle
-                               NULL,          // buffer for class name
-                               NULL,          // size of class string
-                               NULL,          // reserved
-                               &num_subkeys,  // number of subkeys
-                               NULL,          // longest subkey size
-                               NULL,          // longest class string
-                               NULL,          // number of values for this key
-                               NULL,          // longest value name
-                               NULL,          // longest value data
-                               NULL,          // security descriptor
-                               NULL);         // last write time
-
-  ASSERT1(res == ERROR_SUCCESS);
-  return num_subkeys;
-}
-
-HRESULT RegKey::GetSubkeyNameAt(int index, CString * key_name) {
-  ASSERT1(key_name);
-  ASSERT1(h_key_);
-
-  LONG res = ERROR_SUCCESS;
-  TCHAR key_name_buf[kMaxKeyNameChars];
-  DWORD key_name_buf_size = kMaxKeyNameChars;
-
-  res = ::RegEnumKeyEx(h_key_,
-                       index,
-                       key_name_buf,
-                       &key_name_buf_size,
-                       NULL,
-                       NULL,
-                       NULL,
-                       NULL);
-
-  if (res == ERROR_SUCCESS) {
-    key_name->SetString(key_name_buf);
-  }
-
-  return HRESULT_FROM_WIN32(res);
-}
-
-// Is the key empty: having no sub-keys and values
-bool RegKey::IsKeyEmpty(const TCHAR* full_key_name) {
-  ASSERT1(full_key_name);
-
-  bool is_empty = true;
-
-  // Get the root HKEY
-  CString key_name(full_key_name);
-  HKEY h_key = GetRootKeyInfo(&key_name);
-
-  // Open the key to check
-  if (h_key != NULL) {
-    RegKey key;
-    HRESULT hr = key.Open(h_key, key_name.GetString(), KEY_READ);
-    if (SUCCEEDED(hr)) {
-      is_empty = key.GetSubkeyCount() == 0 && key.GetValueCount() == 0;
-      key.Close();
-    }
-  }
-
-  return is_empty;
-}
-
-// close this reg key and the event
-HRESULT RegKeyWithChangeEvent::Close() {
-  reset(change_event_);
-  return RegKey::Close();
-}
-
-// Called to create/reset the event that gets signaled
-// any time the registry key changes
-// Note:
-//   * reg key should have been opened using KEY_NOTIFY for the sam_desired
-//
-// See the documentation for RegNotifyChangeKeyValue
-// for values for notify_filter.
-HRESULT RegKeyWithChangeEvent::SetupEvent(bool watch_subtree,
-                                          DWORD notify_filter) {
-  // If the event exists, then it should be in the signaled state
-  // indicating a registry change took place.  If not, then
-  // the caller is setting up the event a second time and this
-  // will create a memory leak.
-  ASSERT(!valid(change_event_) || HasChangeOccurred(),
-         (_T("Event is getting set-up for a second ")
-          _T("time without being signaled.")));
-
-  if (!valid(change_event_)) {
-    reset(change_event_, ::CreateEvent(NULL, TRUE, FALSE, NULL));
-    if (!valid(change_event_)) {
-      ASSERT(false, (_T("create event failed")));
-      return HRESULT_FROM_WIN32(::GetLastError());
-    }
-  } else {
-    if (!::ResetEvent(get(change_event_))) {
-      ASSERT(false, (_T("reset event failed")));
-      return HRESULT_FROM_WIN32(::GetLastError());
-    }
-  }
-
-  LONG res = ::RegNotifyChangeKeyValue(Key(), watch_subtree, notify_filter,
-      get(change_event_), TRUE);
-
-  if (res != ERROR_SUCCESS) {
-    // You may get this failure if you didn't pass in KEY_NOTIFY
-    // as part of the sam_desired flags during Open or Create
-    ASSERT(false, (_T("setting up change notification for a reg key failed")));
-
-    // Leave the event around so that it never changes once it has been set-up
-    // but in this case it will not get signaled again.
-  }
-
-  return HRESULT_FROM_WIN32(res);
-}
-
-// Indicates if any changes (that are being monitored have occured)
-bool RegKeyWithChangeEvent::HasChangeOccurred() const {
-  return IsHandleSignaled(get(change_event_));
-}
-
-
-RegKeyWatcher::RegKeyWatcher(const TCHAR* reg_key, bool watch_subtree,
-                             DWORD notify_filter, bool allow_creation)
-    : reg_key_string_(reg_key),
-      watch_subtree_(watch_subtree),
-      notify_filter_(notify_filter),
-      allow_creation_(allow_creation) {
-  UTIL_LOG(L3, (_T("[RegKeyWatcher::RegKeyWatcher][%s]"), reg_key));
-}
-
-HRESULT RegKeyWatcher::EnsureEventSetup() {
-  UTIL_LOG(L3, (_T("[RegKeyWatcher::EnsureEventSetup]")));
-  if (!reg_key_with_change_event_.get()) {
-    scoped_ptr<RegKeyWithChangeEvent> local_reg_key(new RegKeyWithChangeEvent);
-    if (!local_reg_key.get()) {
-      ASSERT(false, (_T("unable to allocate local_reg_key")));
-      return E_FAIL;
-    }
-
-    if (allow_creation_ && !RegKey::HasKey(reg_key_string_)) {
-      RegKey key;
-      VERIFY1(SUCCEEDED(key.Create(reg_key_string_)));
-    }
-
-    HRESULT hr = local_reg_key->Open(reg_key_string_, KEY_NOTIFY);
-    if (FAILED(hr)) {
-      ASSERT(false, (_T("couldn't open %s reg key for notifications. ")
-                     _T("Make sure you have pre-created the key!"),
-                     reg_key_string_));
-      return hr;
-    }
-    reg_key_with_change_event_.reset(local_reg_key.release());
-    reg_key_string_.Empty();
-  }
-
-  // if the event is set-up and no changes have occurred,
-  // then there is no need to re-setup the event.
-  if (reg_key_with_change_event_->change_event() && !HasChangeOccurred()) {
-    return S_OK;
-  }
-
-  return reg_key_with_change_event_->SetupEvent(watch_subtree_,
-                                                notify_filter_);
-}
-
-// Get the event that is signaled on registry changes.
-HANDLE RegKeyWatcher::change_event() const {
-  if (!reg_key_with_change_event_.get()) {
-    ASSERT(false, (_T("call RegKeyWatcher::EnsureEventSetup first")));
-    return NULL;
-  }
-  return reg_key_with_change_event_->change_event();
-}
-
-}  // namespace omaha
-
diff --git a/common/reg_key.h b/common/reg_key.h
deleted file mode 100644
index fbf2dcf..0000000
--- a/common/reg_key.h
+++ /dev/null
@@ -1,784 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// reg_key.h
-//
-// Registry configuration wrappers class
-//
-// Offers static functions for convenient
-// fast access for individual values
-//
-// Also provides a wrapper class for efficient
-// batch operations on values of a given registry key.
-
-#ifndef OMAHA_COMMON_REG_KEY_H_
-#define OMAHA_COMMON_REG_KEY_H_
-
-#include <windows.h>
-#include <vector>
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/static_assert.h"
-#include "omaha/common/store_watcher.h"
-
-namespace omaha {
-
-// maximum sizes registry key and value names
-#define kMaxKeyNameChars   (255 + 1)
-#define kMaxValueNameChars (16383 + 1)
-
-class RegKey {
- public:
-  RegKey();
-  virtual ~RegKey();
-
-  // create a reg key
-  HRESULT Create(HKEY hKeyParent, const TCHAR * key_name,
-        TCHAR * reg_class = REG_NONE, DWORD options = REG_OPTION_NON_VOLATILE,
-        REGSAM sam_desired = KEY_ALL_ACCESS,
-        LPSECURITY_ATTRIBUTES lp_sec_attr = NULL,
-        LPDWORD lp_disposition = NULL);
-
-  // create a reg key, given the full key name, including the HKEY root
-  // (say for example, "HKLM\\Software")
-  HRESULT Create(const TCHAR * full_key_name,
-        TCHAR * reg_class = REG_NONE, DWORD options = REG_OPTION_NON_VOLATILE,
-        REGSAM sam_desired = KEY_ALL_ACCESS,
-        LPSECURITY_ATTRIBUTES lp_sec_attr = NULL,
-        LPDWORD lp_disposition = NULL);
-
-  // static helper function that create a set of reg keys,
-  // given an array of full key names including the HKEY root
-  // (say for example, "HKLM\\Software")
-  static HRESULT CreateKeys(const TCHAR* keys_to_create[],
-                            DWORD number_of_keys,
-                            TCHAR* reg_class = REG_NONE,
-                            DWORD options = REG_OPTION_NON_VOLATILE,
-                            LPSECURITY_ATTRIBUTES lp_sec_attr = NULL);
-
-  // Static method to create a single key.
-  static HRESULT CreateKey(const TCHAR * full_key_name,
-                           TCHAR * reg_class = REG_NONE,
-                           DWORD options = REG_OPTION_NON_VOLATILE,
-                           LPSECURITY_ATTRIBUTES lp_sec_attr = NULL);
-
-  // open an existing reg key
-  HRESULT Open(HKEY hKeyParent,
-               const TCHAR * key_name,
-               REGSAM sam_desired = KEY_ALL_ACCESS);
-
-  // open an existing reg key, given the full key name, including the HKEY root
-  // (say for example, "HKLM\\Software")
-  HRESULT Open(const TCHAR * full_key_name,
-               REGSAM sam_desired = KEY_ALL_ACCESS);
-
-  // close this reg key
-  virtual HRESULT Close();
-
-  // check if the key has a specified value
-  bool HasValue(const TCHAR * value_name);
-
-  // get the number of values for this key
-  uint32 GetValueCount();
-
-  // Called to get the value name for the given value name index
-  // Use GetValueCount() to get the total value_name count for this key
-  // Returns failure if no key at the specified index
-  // If you modify the key while enumerating, the indexes will be out of order.
-  // Since the index order is not guaranteed, you need to reset your counting
-  // loop.
-  // type refers to REG_DWORD, REG_QWORD, etc..
-  // 'type' can be NULL if not interested in the value type
-  HRESULT GetValueNameAt(int index, CString * value_name, DWORD *type);
-
-  // check if the current key has the specified subkey
-  bool HasSubkey(const TCHAR * key_name) const;
-
-  // get the number of subkeys for this key
-  uint32 GetSubkeyCount();
-
-  // Called to get the key name for the given key index
-  // Use GetSubkeyCount() to get the total count for this key
-  // Returns failure if no key at the specified index
-  // If you modify the key while enumerating, the indexes will be out of order.
-  // Since the index order is not guaranteed, you need to reset your counting
-  // loop.
-  HRESULT GetSubkeyNameAt(int index, CString * key_name);
-
-  // SETTERS
-
-  // set an int32 value - use when reading multiple values from a key
-  HRESULT SetValue(const TCHAR * value_name, DWORD value) const;
-
-  // set an int64 value
-  HRESULT SetValue(const TCHAR * value_name, DWORD64 value) const;
-
-  // set a string value
-  HRESULT SetValue(const TCHAR * value_name, const TCHAR * value) const;
-
-  // set binary data
-  HRESULT SetValue(const TCHAR * value_name,
-                   const byte * value,
-                   DWORD byte_count) const;
-
-  // set raw data, including type
-  HRESULT SetValue(const TCHAR * value_name,
-                   const byte * value,
-                   DWORD byte_count,
-                   DWORD type) const;
-
-  // GETTERS
-
-  // get an int32 value
-  HRESULT GetValue(const TCHAR * value_name, DWORD * value) const;
-
-  // get an int64 value
-  //
-  // Note: if you are using time64 you should
-  // likely use GetLimitedTimeValue (util.h) instead of this method.
-  HRESULT GetValue(const TCHAR * value_name, DWORD64 * value) const;
-
-  // get a string value - the caller must free the return buffer
-  HRESULT GetValue(const TCHAR * value_name, TCHAR * * value) const;
-
-  // get a CString value
-  HRESULT GetValue(const TCHAR* value_name, OUT CString* value) const;
-
-  // get a vector<CString> value from REG_MULTI_SZ type
-  HRESULT GetValue(const TCHAR * value_name,
-                   std::vector<CString> * value) const;
-
-  // get binary data - the caller must free the return buffer
-  HRESULT GetValue(const TCHAR * value_name,
-                   byte * * value,
-                   DWORD * byte_count) const;
-
-  // get raw data, including type - the caller must free the return buffer
-  HRESULT GetValue(const TCHAR * value_name,
-                   byte * * value,
-                   DWORD * byte_count,
-                   DWORD *type) const;
-
-  // RENAMERS
-
-  // Rename a named value.
-  HRESULT RenameValue(const TCHAR * old_value_name,
-                      const TCHAR * new_value_name) const;
-
-  // STATIC VERSIONS
-
-  // flush
-  static HRESULT FlushKey(const TCHAR * full_key_name);
-
-  // Check if a key exists.
-  static bool HasKey(const TCHAR * full_key_name);
-
-  // Check if a key exists in the native (i.e. non-redirected) registry.
-  static bool HasNativeKey(const TCHAR * full_key_name);
-
-  // check if the key has a specified value
-  static bool HasValue(const TCHAR * full_key_name, const TCHAR * value_name);
-
-  // SETTERS
-
-  // STATIC int32 set
-  static HRESULT SetValue(const TCHAR * full_key_name,
-                          const TCHAR * value_name,
-                          DWORD value);
-
-  // STATIC int64 set
-  static HRESULT SetValue(const TCHAR * full_key_name,
-                          const TCHAR * value_name,
-                          DWORD64 value);
-
-  // STATIC float set
-  static HRESULT SetValue(const TCHAR * full_key_name,
-                          const TCHAR * value_name,
-                          float value);
-
-  // STATIC double set
-  static HRESULT SetValue(const TCHAR * full_key_name,
-                          const TCHAR * value_name,
-                          double value);
-
-  // STATIC string set
-  static HRESULT SetValue(const TCHAR * full_key_name,
-                          const TCHAR * value_name,
-                          const TCHAR * value);
-
-  // STATIC binary data set
-  static HRESULT SetValue(const TCHAR * full_key_name,
-                          const TCHAR * value_name,
-                          const byte * value,
-                          DWORD byte_count);
-
-  // STATIC array of strings set
-  static HRESULT SetValueMultiSZ(const TCHAR * full_key_name,
-                                 const TCHAR * value_name,
-                                 const byte * value,
-                                 DWORD byte_count);
-
-  // STATIC expandable string set
-  static HRESULT SetValueExpandSZ(const TCHAR * full_key_name,
-                                  const TCHAR * value_name,
-                                  const TCHAR * value);
-
-  // GETTERS
-
-  // STATIC int32 get
-  static HRESULT GetValue(const TCHAR * full_key_name,
-                          const TCHAR * value_name,
-                          DWORD * value);
-
-  // STATIC int64 get
-  //
-  // Note: if you are using time64 you should
-  // likely use GetLimitedTimeValue (util.h) instead of this method.
-  static HRESULT GetValue(const TCHAR * full_key_name,
-                          const TCHAR * value_name,
-                          DWORD64 * value);
-
-  // STATIC float get
-  static HRESULT GetValue(const TCHAR * full_key_name,
-                          const TCHAR * value_name,
-                          float * value);
-
-  // STATIC double get
-  static HRESULT GetValue(const TCHAR * full_key_name,
-                          const TCHAR * value_name,
-                          double * value);
-
-  // STATIC string get (STR and CString versions) - the caller must free
-  // the return buffer
-  static HRESULT GetValue(const TCHAR * full_key_name,
-                          const TCHAR * value_name,
-                          TCHAR * * value);
-
-  static HRESULT GetValue(const TCHAR * full_key_name,
-                          const TCHAR * value_name,
-                          CString * value);
-
-  // STATIC REG_MULTI_SZ get
-  static HRESULT GetValue(const TCHAR * full_key_name,
-                          const TCHAR * value_name,
-                          std::vector<CString> * value);
-
-  // STATIC get binary data - the caller must free the return buffer
-  static HRESULT GetValue(const TCHAR * full_key_name,
-                          const TCHAR * value_name,
-                          byte * * value,
-                          DWORD * byte_count);
-
-  // Try reg keys successively if there is a failure in getting a value.
-  //
-  // Typically used when there is a user value and a default value if the
-  // user has none.
-  template<typename T>
-  static HRESULT GetValue(const TCHAR * full_key_names[],
-                          int key_names_length,
-                          const TCHAR * value_name,
-                          T* value);
-
-  // RENAMERS
-
-  // Rename a named value.
-  static HRESULT RenameValue(const TCHAR * full_key_name,
-                             const TCHAR * old_value_name,
-                             const TCHAR * new_value_name);
-
-  // COPIERS
-
-  // The full_to_key must exist for CopyValue to succeed.
-  static HRESULT CopyValue(const TCHAR * full_from_key_name,
-                           const TCHAR * from_value_name,
-                           const TCHAR * full_to_key_name,
-                           const TCHAR * to_value_name);
-
-  static HRESULT CopyValue(const TCHAR * full_from_key_name,
-                           const TCHAR * full_to_key_name,
-                           const TCHAR * value_name);
-
-  // Get type of a registry value
-  static HRESULT GetValueType(const TCHAR* full_key_name,
-                              const TCHAR* value_name,
-                              DWORD* value_type);
-
-  // delete a subkey of the current key (with no subkeys)
-  HRESULT DeleteSubKey(const TCHAR * key_name);
-
-  // recursively delete a sub key of the current key (and all its subkeys)
-  HRESULT RecurseDeleteSubKey(const TCHAR * key_name);
-
-  // STATIC version of delete key - handles nested keys also
-  // delete a key and all its sub-keys recursively
-  // Returns S_FALSE if key didn't exist, S_OK if deletion was successful,
-  // and failure otherwise.
-  static HRESULT DeleteKey(const TCHAR* full_key_name);
-
-  // STATIC version of delete key
-  // delete a key recursively or non-recursively
-  // Returns S_FALSE if key didn't exist, S_OK if deletion was successful,
-  // and failure otherwise.
-  static HRESULT DeleteKey(const TCHAR* full_key_name, bool recursive);
-
-  // delete the specified value
-  HRESULT DeleteValue(const TCHAR * value_name) const;
-
-  // STATIC version of delete value
-  // Returns S_FALSE if key didn't exist, S_OK if deletion was successful,
-  // and failure otherwise.
-  static HRESULT DeleteValue(const TCHAR * full_key_name,
-                             const TCHAR * value_name);
-
-  // Peek inside (use a RegKey as a smart wrapper around a registry handle)
-  HKEY Key() { return h_key_; }
-
-  // Used to help test the private functionality
-  friend class RegKeyTestClass;
-
-  // helper function to get the HKEY and the root key from a string
-  // representation modifies the argument in place and returns the key name
-  // e.g. HKLM\\Software\\Google\... returns HKLM, "Software\\Google\..."
-  // Necessary for the static versions that use the full name of the reg key
-  static HKEY GetRootKeyInfo(CString * full_key_name);
-
-  // Returns true if this key name is 'safe' for deletion (doesn't specify
-  // a key root)
-  static bool SafeKeyNameForDeletion(const wchar_t *key_name);
-
-  // save the key and all of its subkeys and values to a file
-  static HRESULT Save(const TCHAR* full_key_name, const TCHAR* file_name);
-
-  // restore the key and all of its subkeys and values which are saved into
-  // a file
-  static HRESULT Restore(const TCHAR* full_key_name, const TCHAR* file_name);
-
-  // Is the key empty: having no sub-keys and values
-  static bool IsKeyEmpty(const TCHAR* full_key_name);
-
- private:
-
-  // Helper function to check if a key exists, using the sam flags specified.
-  // Note: KEY_READ must be included in sam_flags.
-  static bool HasKeyHelper(const TCHAR * full_key_name, DWORD sam_flags);
-
-  // helper function to get the parent key name and the subkey from a string
-  // representation modifies the argument in place and returns the key name
-  // e.g. Software\\Google\\Foo_Bar returns "Software\\Google", "Foo_Bar"
-  // Necessary for the static versions that use the full name of the reg key
-  static CString GetParentKeyInfo(CString * key_name);
-
-  // helper function to get any value from the registry
-  // used when the size of the data is unknown
-  HRESULT GetValueHelper(const TCHAR * value_name,
-                         DWORD * type,
-                         byte * * value,
-                         DWORD * byte_count) const;
-
-  // common SET Helper for the static case
-  static HRESULT SetValueStaticHelper(const TCHAR * full_key_name,
-                                      const TCHAR * value_name,
-                                      DWORD type,
-                                      LPVOID value,
-                                      DWORD byte_count = 0);
-
-  // common GET Helper for the static case
-  static HRESULT GetValueStaticHelper(const TCHAR * full_key_name,
-                                      const TCHAR * value_name,
-                                      DWORD type,
-                                      LPVOID value,
-                                      DWORD * byte_count = NULL);
-
-  // convert REG_MULTI_SZ bytes to string array
-  static HRESULT MultiSZBytesToStringArray(const byte * buffer,
-                                           DWORD byte_count,
-                                           std::vector<CString> * value);
-
-  // set a string or expandable string value
-  HRESULT SetStringValue(const TCHAR * value_name,
-                         const TCHAR * value,
-                         DWORD type) const;
-
-  // the HKEY for the current key
-  HKEY h_key_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(RegKey);
-};
-
-// Provides all the functionality of RegKey plus
-// an event to watch for changes to the registry key.
-class RegKeyWithChangeEvent : public RegKey {
- public:
-  RegKeyWithChangeEvent() {}
-  // close this reg key and the event
-  virtual HRESULT Close();
-
-  // Called to create/reset the event that gets signaled
-  // any time the registry key changes.  Access the created
-  // event using change_event().
-  //
-  // See the documentation for RegNotifyChangeKeyValue
-  // for values for notify_filter.
-  HRESULT SetupEvent(bool watch_subtree, DWORD notify_filter);
-
-  // Indicates if any changes (that are being monitored) have occured
-  bool HasChangeOccurred() const;
-
-  // Get the event that is signaled on registry changes.
-  // Note:
-  //   * This event will remain constant until Close() is called.
-  //   * One should call SetupEvent to set-up the event.
-  //   * The event is only signaled on the next change and remains signaled.
-  //     Do not call ::ResetEvent().  Call SetupEvent() to reset
-  //     the event and wait for more changes.
-  HANDLE change_event() const {
-    return get(change_event_);
-  }
-
- private:
-  scoped_handle change_event_;
-  DISALLOW_EVIL_CONSTRUCTORS(RegKeyWithChangeEvent);
-};
-
-// Does the common things necessary for watching
-// registry key changes.  If there are file change or other watchers,
-// there could be a common interface for the three methods to decouple
-// the code that is doing the watching from the code that owns the store.
-class RegKeyWatcher : public StoreWatcher {
- public:
-  // reg_key: the full string for the reg key
-  // watch_subtree: watch all subkey changes  or
-  //                only immediate child values
-  // notify_filter: See the documentation for RegNotifyChangeKeyValue
-  // allow_creation: Should the key be created if it doesn't exist?
-  RegKeyWatcher(const TCHAR* reg_key, bool watch_subtree,
-                DWORD notify_filter, bool allow_creation);
-  virtual ~RegKeyWatcher() {}
-
-  // Called to create/reset the event that gets signaled
-  // any time the registry key changes.  Access the created
-  // event using change_event().
-  virtual HRESULT EnsureEventSetup();
-
-  // Get the event that is signaled on registry changes.
-  virtual HANDLE change_event() const;
-
- private:
-  // Used to do the SetupEvent method
-  scoped_ptr<RegKeyWithChangeEvent> reg_key_with_change_event_;
-
-  CString reg_key_string_;
-  bool watch_subtree_;
-  bool allow_creation_;
-  DWORD notify_filter_;
-  DISALLOW_EVIL_CONSTRUCTORS(RegKeyWatcher);
-};
-
-
-inline RegKey::RegKey() { h_key_ = NULL; }
-
-inline RegKey::~RegKey() { Close(); }
-
-inline bool RegKey::HasValue(const TCHAR* value_name) {
-  return (ERROR_SUCCESS == ::RegQueryValueEx(h_key_,
-                                             value_name,
-                                             NULL, NULL,
-                                             NULL,
-                                             NULL));
-}
-
-// SETTERS static versions
-inline HRESULT RegKey::SetValue(const TCHAR* full_key_name,
-                                const TCHAR* value_name,
-                                DWORD value) {
-  ASSERT1(full_key_name);
-
-  return SetValueStaticHelper(full_key_name, value_name, REG_DWORD, &value);
-}
-
-inline HRESULT RegKey::SetValue(const TCHAR* full_key_name,
-                                const TCHAR* value_name,
-                                DWORD64 value) {
-  ASSERT1(full_key_name);
-
-  return SetValueStaticHelper(full_key_name, value_name, REG_QWORD, &value);
-}
-
-inline HRESULT RegKey::SetValue(const TCHAR* full_key_name,
-                                const TCHAR* value_name,
-                                float value) {
-  ASSERT1(full_key_name);
-
-  return SetValueStaticHelper(full_key_name,
-                              value_name,
-                              REG_BINARY,
-                              &value,
-                              sizeof(value));
-}
-
-inline HRESULT RegKey::SetValue(const TCHAR* full_key_name,
-                                const TCHAR* value_name,
-                                double value) {
-  ASSERT1(full_key_name);
-
-  return SetValueStaticHelper(full_key_name,
-                              value_name,
-                              REG_BINARY,
-                              &value,
-                              sizeof(value));
-}
-
-inline HRESULT RegKey::SetValue(const TCHAR* full_key_name,
-                                const TCHAR* value_name,
-                                const TCHAR* value) {
-  ASSERT1(full_key_name);
-  ASSERT1(value);
-
-  return SetValueStaticHelper(full_key_name,
-                              value_name,
-                              REG_SZ,
-                              const_cast<TCHAR*>(value));
-}
-
-inline HRESULT RegKey::SetValue(const TCHAR* full_key_name,
-                                const TCHAR* value_name,
-                                const byte* value,
-                                DWORD byte_count) {
-  ASSERT1(full_key_name);
-
-  return SetValueStaticHelper(full_key_name, value_name, REG_BINARY,
-                              const_cast<byte*>(value), byte_count);
-}
-
-inline HRESULT RegKey::SetValueMultiSZ(const TCHAR* full_key_name,
-                                       const TCHAR* value_name,
-                                       const byte* value,
-                                       DWORD byte_count) {
-  ASSERT1(full_key_name);
-
-  return SetValueStaticHelper(full_key_name, value_name, REG_MULTI_SZ,
-                              const_cast<byte*>(value), byte_count);
-}
-
-inline HRESULT RegKey::SetValueExpandSZ(const TCHAR* full_key_name,
-                                        const TCHAR* value_name,
-                                        const TCHAR* value) {
-  ASSERT1(full_key_name);
-  ASSERT1(value);
-
-  return SetValueStaticHelper(full_key_name,
-                              value_name,
-                              REG_EXPAND_SZ,
-                              const_cast<TCHAR*>(value));
-}
-
-// GETTERS static versions
-inline HRESULT RegKey::GetValue(const TCHAR* full_key_name,
-                                const TCHAR* value_name,
-                                DWORD* value) {
-  ASSERT1(full_key_name);
-  ASSERT1(value);
-
-  return GetValueStaticHelper(full_key_name, value_name, REG_DWORD, value);
-}
-
-inline HRESULT RegKey::GetValue(const TCHAR* full_key_name,
-                                const TCHAR* value_name,
-                                DWORD64* value) {
-  ASSERT1(full_key_name);
-  ASSERT1(value);
-
-  return GetValueStaticHelper(full_key_name, value_name, REG_QWORD, value);
-}
-
-inline HRESULT RegKey::GetValue(const TCHAR* full_key_name,
-                                const TCHAR* value_name,
-                                float* value) {
-  ASSERT1(value);
-  ASSERT1(value_name);
-  ASSERT1(full_key_name);
-
-  DWORD byte_count = 0;
-  byte* buffer = NULL;
-  HRESULT hr = GetValueStaticHelper(full_key_name,
-                                    value_name,
-                                    REG_BINARY,
-                                    &buffer,
-                                    &byte_count);
-  scoped_array<byte> free_buffer(buffer);
-
-  if (SUCCEEDED(hr)) {
-    if (byte_count == sizeof(*value)) {
-      ::CopyMemory(value, buffer, sizeof(*value));
-    } else {
-      UTIL_LOG(LEVEL_ERROR, (_T("[RegKey::GetValue]")
-                             _T("[size mismatches for float value][%s\\%s]"),
-                             full_key_name, value_name));
-      return HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
-    }
-  }
-
-  return hr;
-}
-
-inline HRESULT RegKey::GetValue(const TCHAR* full_key_name,
-                                const TCHAR* value_name,
-                                double* value) {
-  ASSERT1(value);
-  ASSERT1(value_name);
-  ASSERT1(full_key_name);
-
-  DWORD byte_count = 0;
-  byte* buffer = NULL;
-  HRESULT hr = GetValueStaticHelper(full_key_name,
-                                    value_name,
-                                    REG_BINARY,
-                                    &buffer,
-                                    &byte_count);
-  scoped_array<byte> free_buffer(buffer);
-
-  if (SUCCEEDED(hr)) {
-    if (byte_count == sizeof(*value)) {
-      ::CopyMemory(value, buffer, sizeof(*value));
-    } else {
-      UTIL_LOG(LEVEL_ERROR, (_T("[RegKey::GetValue]")
-                             _T("[size mismatches for double value][%s\\%s]"),
-                             full_key_name, value_name));
-      return HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
-    }
-  }
-
-  return hr;
-}
-
-inline HRESULT RegKey::GetValue(const TCHAR* full_key_name,
-                                const TCHAR* value_name,
-                                TCHAR** value) {
-  ASSERT1(full_key_name);
-  ASSERT1(value);
-
-  return GetValueStaticHelper(full_key_name, value_name, REG_SZ, value);
-}
-
-inline HRESULT RegKey::GetValue(const TCHAR* full_key_name,
-                                const TCHAR* value_name,
-                                CString* value) {
-  ASSERT1(full_key_name);
-  ASSERT1(value);
-
-  TCHAR* buffer = NULL;
-  HRESULT hr = RegKey::GetValue(full_key_name, value_name, &buffer);
-  value->SetString(buffer);
-  delete [] buffer;
-  return hr;
-}
-
-inline HRESULT RegKey::GetValue(const TCHAR* full_key_name,
-                                const TCHAR* value_name,
-                                std::vector<CString>* value) {
-  ASSERT1(full_key_name);
-  ASSERT1(value);
-
-  return GetValueStaticHelper(full_key_name, value_name, REG_MULTI_SZ, value);
-}
-
-inline HRESULT RegKey::GetValue(const TCHAR* full_key_name,
-                                const TCHAR* value_name,
-                                byte** value,
-                                DWORD* byte_count) {
-  ASSERT1(full_key_name);
-  ASSERT1(value);
-  ASSERT1(byte_count);
-
-  return GetValueStaticHelper(full_key_name,
-                              value_name,
-                              REG_BINARY,
-                              value,
-                              byte_count);
-}
-
-template<typename T>
-HRESULT RegKey::GetValue(const TCHAR* full_key_names[],
-                         int key_names_length,
-                         const TCHAR* value_name,
-                         T* value) {
-  HRESULT hr = S_OK;
-  for (int i = 0; i < key_names_length; ++i) {
-    hr = GetValue(full_key_names[i], value_name, value);
-    if (SUCCEEDED(hr)) {
-      return hr;
-    }
-  }
-  return hr;
-}
-
-// Rename a named value.
-inline HRESULT RegKey::RenameValue(const TCHAR * full_key_name,
-                                   const TCHAR * old_value_name,
-                                   const TCHAR * new_value_name) {
-  ASSERT1(full_key_name);
-
-  RegKey reg_key;
-  HRESULT hr = reg_key.Open(full_key_name);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return reg_key.RenameValue(old_value_name, new_value_name);
-}
-
-inline HRESULT RegKey::CopyValue(const TCHAR * full_from_key_name,
-                                 const TCHAR * full_to_key_name,
-                                 const TCHAR * value_name) {
-  return CopyValue(full_from_key_name,
-                   value_name,
-                   full_to_key_name,
-                   value_name);
-}
-
-// DELETE
-inline HRESULT RegKey::DeleteSubKey(const TCHAR* key_name) {
-  ASSERT1(key_name);
-  ASSERT1(h_key_);
-
-  LONG res = ::RegDeleteKey(h_key_, key_name);
-  HRESULT hr = HRESULT_FROM_WIN32(res);
-  if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
-      hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) {
-    hr = S_FALSE;
-  }
-  return hr;
-}
-
-inline HRESULT RegKey::DeleteValue(const TCHAR* value_name) const {
-  ASSERT1(value_name);
-  ASSERT1(h_key_);
-
-  LONG res = ::RegDeleteValue(h_key_, value_name);
-  HRESULT hr = HRESULT_FROM_WIN32(res);
-  if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
-      hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) {
-    hr = S_FALSE;
-  }
-  return hr;
-}
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_REG_KEY_H_
-
diff --git a/common/reg_key_unittest.cc b/common/reg_key_unittest.cc
deleted file mode 100644
index 2ebb477..0000000
--- a/common/reg_key_unittest.cc
+++ /dev/null
@@ -1,898 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/reg_key.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/utils.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-#define kStTestRkeyRelativeBase   _T("Software\\Google\\Update\\UnitTest")
-#define kStTestRkeyBase   _T("HKCU\\") kStTestRkeyRelativeBase
-#define kStRkey1Name      _T("TEST")
-#define kStRkey1          kStTestRkeyBase _T("\\") kStRkey1Name
-#define kRkey1            kStTestRkeyRelativeBase _T("\\") kStRkey1Name
-#define kStRkey2          kStTestRkeyBase _T("\\TEST2")
-#define kStRkey3          kStTestRkeyBase _T("\\TEST3")
-#define kRkey1SubkeyName  _T("subkey_test")
-#define kRkey1Subkey      kRkey1 _T("\\") kRkey1SubkeyName
-#define kStRkey1Subkey    kStRkey1 _T("\\") kRkey1SubkeyName
-
-// NON - STATIC
-
-#define kValNameInt          _T("Int32 Value")
-#define kRenameValNameInt    _T("Renamed Int32 Value")
-#define kIntVal              (DWORD)20
-#define kIntVal2             (DWORD)30
-
-#define kValNameInt64        _T("Int64 Value")
-#define kIntVal64            (DWORD64)40
-#define kIntVal642           (DWORD64)50
-
-#define kValNameStr          _T("Str Value")
-#define kStrVal              _T("Some string data 1")
-#define kStrVal2             _T("Some string data 2")
-
-#define kValNameBinary       _T("Binary Value")
-#define kBinaryVal           "Some binary data abcdefghi 1"
-#define kBinaryVal2          "Some binary data abcdefghi 2"
-
-// STATIC
-
-#define kStValNameInt        _T("Static Int32 Value")
-#define kStIntVal            (DWORD)60
-
-#define kStValNameInt64      _T("Static Int64 Value")
-#define kStIntVal64          (DWORD64)80
-
-#define kStValNameFloat      _T("Static Float Value")
-#define kStFloatVal          (static_cast<float>(12.3456789))
-
-#define kStValNameDouble     _T("Static Double Value")
-#define kStDoubleVal         (static_cast<double>(98.7654321))
-
-#define kStValNameStr        _T("Static Str Value")
-#define kRenameStValNameStr  _T("Renamed Static Str Value")
-#define kStStrVal            _T("Some static string data 2")
-
-#define kStValNameBinary     _T("Static Binary Value")
-#define kStBinaryVal         "Some static binary data abcdefghi 2"
-
-// Test the private member functions of RegKey
-class RegKeyTestClass : public testing::Test {
- protected:
-  static const HKEY GetHKey(const RegKey& reg) {
-    return reg.h_key_;
-  }
-
-  static CString GetParentKeyInfo(CString* key_name) {
-    return RegKey::GetParentKeyInfo(key_name);
-  }
-};
-
-class RegKeyCleanupTestKeyTest : public testing::Test {
- protected:
-  virtual void SetUp() {
-    EXPECT_SUCCEEDED(RegKey::DeleteKey(kStTestRkeyBase));
-  }
-
-  virtual void TearDown() {
-    EXPECT_SUCCEEDED(RegKey::DeleteKey(kStTestRkeyBase));
-  }
-
-  RegKey key_;
-};
-
-// Make sure the RegKey is nice and clean when we first initialize it
-TEST_F(RegKeyTestClass, Init) {
-  // Make a new RegKey object so we can test its pristine state
-  RegKey reg;
-
-  ASSERT_TRUE(GetHKey(reg) == NULL);
-}
-
-// Make sure the helper functions work
-TEST_F(RegKeyTestClass, Helper) {
-  // Dud items cause NULL
-  CString temp_key;
-
-  // RegKey::GetRootKeyInfo turns a string into the HKEY and subtree value
-
-  // Try out some dud values
-  temp_key = _T("");
-  ASSERT_TRUE(RegKey::GetRootKeyInfo(&temp_key) == NULL);
-  ASSERT_STREQ(temp_key, _T(""));
-
-  temp_key = _T("a");
-  ASSERT_TRUE(RegKey::GetRootKeyInfo(&temp_key) == NULL);
-  ASSERT_STREQ(temp_key, _T(""));
-
-  // The basics
-  temp_key = _T("HKLM\\a");
-  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_LOCAL_MACHINE);
-  ASSERT_STREQ(temp_key, _T("a"));
-
-  temp_key = _T("HKEY_LOCAL_MACHINE\\a");
-  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_LOCAL_MACHINE);
-  ASSERT_STREQ(temp_key, _T("a"));
-
-  temp_key = _T("HKCU\\a");
-  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CURRENT_USER);
-  ASSERT_STREQ(temp_key, _T("a"));
-
-  temp_key = _T("HKEY_CURRENT_USER\\a");
-  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CURRENT_USER);
-  ASSERT_STREQ(temp_key, _T("a"));
-
-  temp_key = _T("HKU\\a");
-  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_USERS);
-  ASSERT_STREQ(temp_key, _T("a"));
-
-  temp_key = _T("HKEY_USERS\\a");
-  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_USERS);
-  ASSERT_STREQ(temp_key, _T("a"));
-
-  temp_key = _T("HKCR\\a");
-  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CLASSES_ROOT);
-  ASSERT_STREQ(temp_key, _T("a"));
-
-  temp_key = _T("HKEY_CLASSES_ROOT\\a");
-  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CLASSES_ROOT);
-  ASSERT_STREQ(temp_key, _T("a"));
-
-  // Make sure it is case insensitive
-  temp_key = _T("hkcr\\a");
-  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CLASSES_ROOT);
-  ASSERT_STREQ(temp_key, _T("a"));
-
-  temp_key = _T("hkey_CLASSES_ROOT\\a");
-  ASSERT_EQ(RegKey::GetRootKeyInfo(&temp_key), HKEY_CLASSES_ROOT);
-  ASSERT_STREQ(temp_key, _T("a"));
-
-  // Test out temp_GetParentKeyInfo
-
-  // dud cases
-  temp_key = _T("");
-  ASSERT_STREQ(GetParentKeyInfo(&temp_key), _T(""));
-  ASSERT_STREQ(temp_key, _T(""));
-
-  temp_key = _T("a");
-  ASSERT_STREQ(GetParentKeyInfo(&temp_key), _T(""));
-  ASSERT_STREQ(temp_key, _T("a"));
-
-  temp_key = _T("a\\b");
-  ASSERT_STREQ(GetParentKeyInfo(&temp_key), _T("a"));
-  ASSERT_STREQ(temp_key, _T("b"));
-
-  temp_key = _T("\\b");
-  ASSERT_STREQ(GetParentKeyInfo(&temp_key), _T(""));
-  ASSERT_STREQ(temp_key, _T("b"));
-
-
-  // Some regular cases
-  temp_key = _T("HKEY_CLASSES_ROOT\\moon");
-  ASSERT_STREQ(GetParentKeyInfo(&temp_key), _T("HKEY_CLASSES_ROOT"));
-  ASSERT_STREQ(temp_key, _T("moon"));
-
-  temp_key = _T("HKEY_CLASSES_ROOT\\moon\\doggy");
-  ASSERT_STREQ(GetParentKeyInfo(&temp_key),
-               _T("HKEY_CLASSES_ROOT\\moon"));
-  ASSERT_STREQ(temp_key, _T("doggy"));
-}
-
-
-TEST(RegKeyTest, RegKey) {
-  //
-  // PRIVATE MEMBER WHITE BOX TESTS
-  //
-  RegKeyWithChangeEvent r_key;
-  bool bool_res = false;
-  HRESULT hr = E_FAIL;
-  DWORD int_val = 0;
-  DWORD64 int64_val = 0;
-  time64 t = 0;
-  float float_val = 0;
-  double double_val = 0;
-  TCHAR * str_val = NULL;
-  byte * binary_val = NULL;
-  DWORD byte_count = 0;
-
-  // Just in case...
-  // make sure the no test key residue is left from previous aborted runs
-  hr = RegKey::DeleteKey(kStTestRkeyBase);
-
-  // first test the non-static version
-
-  // create a reg key
-  hr = r_key.Create(HKEY_CURRENT_USER, kRkey1);
-  ASSERT_SUCCEEDED(hr);
-
-  // do the create twice - it should return the already created one
-  hr = r_key.Create(HKEY_CURRENT_USER, kRkey1);
-  ASSERT_SUCCEEDED(hr);
-
-  // now do an open - should work just fine
-  hr = r_key.Open(HKEY_CURRENT_USER, kRkey1);
-  ASSERT_SUCCEEDED(hr);
-
-  // get an in-existent value
-  hr = r_key.GetValue(kValNameInt, &int_val);
-  ASSERT_EQ(hr, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
-
-  // set-up an event to watch for changes
-  hr = r_key.SetupEvent(TRUE,
-                        REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET);
-  ASSERT_SUCCEEDED(hr);
-  HANDLE change_event = r_key.change_event();
-  ASSERT_EQ(::WaitForSingleObject(change_event, 0), WAIT_TIMEOUT);
-
-  // set and get some values and verify that the handle gets signaled
-
-  // set an INT 32
-  hr = r_key.SetValue(kValNameInt, kIntVal);
-  ASSERT_SUCCEEDED(hr);
-  // verify that we got the change and that the event got reset appropriately
-  // and set-up the notification again (use the actual event this time)
-  ASSERT_EQ(::WaitForSingleObject(change_event, 0), WAIT_OBJECT_0);
-  hr = r_key.SetupEvent(TRUE,
-                        REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_FALSE(r_key.HasChangeOccurred());
-
-  // check that the value exists
-  bool_res = r_key.HasValue(kValNameInt);
-  ASSERT_TRUE(bool_res);
-  // No change expected on a read
-  ASSERT_FALSE(r_key.HasChangeOccurred());
-
-  // read it back
-  hr = r_key.GetValue(kValNameInt, &int_val);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(int_val, kIntVal);
-  // No change expected on a read
-  ASSERT_FALSE(r_key.HasChangeOccurred());
-
-  // set it again!
-  hr = r_key.SetValue(kValNameInt, kIntVal2);
-  ASSERT_SUCCEEDED(hr);
-  // verify that we got the change and that the event got reset appropriately
-  // and set-up the notification again
-  ASSERT_TRUE(r_key.HasChangeOccurred());
-  hr = r_key.SetupEvent(TRUE,
-                        REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_FALSE(r_key.HasChangeOccurred());
-
-  // read it again
-  hr = r_key.GetValue(kValNameInt, &int_val);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(int_val, kIntVal2);
-  // No change expected on a read
-  ASSERT_FALSE(r_key.HasChangeOccurred());
-
-  // delete the value
-  hr = r_key.DeleteValue(kValNameInt);
-  ASSERT_SUCCEEDED(hr);
-  // verify that we got the change
-  ASSERT_TRUE(r_key.HasChangeOccurred());
-
-  // check that the value is gone
-  bool_res = r_key.HasValue(kValNameInt);
-  ASSERT_FALSE(bool_res);
-
-  // set an INT 64
-  hr = r_key.SetValue(kValNameInt64, kIntVal64);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value exists
-  bool_res = r_key.HasValue(kValNameInt64);
-  ASSERT_TRUE(bool_res);
-
-  // read it back
-  hr = r_key.GetValue(kValNameInt64, &int64_val);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(int64_val, kIntVal64);
-
-  // delete the value
-  hr = r_key.DeleteValue(kValNameInt64);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value is gone
-  bool_res = r_key.HasValue(kValNameInt64);
-  ASSERT_FALSE(bool_res);
-
-  // set a string
-  hr = r_key.SetValue(kValNameStr, kStrVal);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value exists
-  bool_res = r_key.HasValue(kValNameStr);
-  ASSERT_TRUE(bool_res);
-
-  // read it back
-  hr = r_key.GetValue(kValNameStr, &str_val);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_STREQ(str_val, kStrVal);
-  delete [] str_val;
-
-  // set it again
-  hr = r_key.SetValue(kValNameStr, kStrVal2);
-  ASSERT_SUCCEEDED(hr);
-
-  // read it again
-  hr = r_key.GetValue(kValNameStr, &str_val);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_STREQ(str_val, kStrVal2);
-  delete [] str_val;
-
-  // delete the value
-  hr = r_key.DeleteValue(kValNameStr);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value is gone
-  bool_res = r_key.HasValue(kValNameInt);
-  ASSERT_FALSE(bool_res);
-
-  // set a binary value
-  hr = r_key.SetValue(kValNameBinary, (const byte *)kBinaryVal,
-                      sizeof(kBinaryVal)-1);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value exists
-  bool_res = r_key.HasValue(kValNameBinary);
-  ASSERT_TRUE(bool_res);
-
-  // read it back
-  hr = r_key.GetValue(kValNameBinary, &binary_val, &byte_count);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(0, memcmp(binary_val, kBinaryVal, sizeof(kBinaryVal)-1));
-  delete [] binary_val;
-
-  // set it again
-  hr = r_key.SetValue(kValNameBinary, (const byte *)kBinaryVal2,
-                      sizeof(kBinaryVal)-1);
-  ASSERT_SUCCEEDED(hr);
-
-  // read it again
-  hr = r_key.GetValue(kValNameBinary, &binary_val, &byte_count);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(0, memcmp(binary_val, kBinaryVal2, sizeof(kBinaryVal2)-1));
-  delete [] binary_val;
-
-  // delete the value
-  hr = r_key.DeleteValue(kValNameBinary);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value is gone
-  bool_res = r_key.HasValue(kValNameBinary);
-  ASSERT_FALSE(bool_res);
-
-  // set some values and check the total count
-
-  // set an INT 32
-  hr = r_key.SetValue(kValNameInt, kIntVal);
-  ASSERT_SUCCEEDED(hr);
-
-  // set an INT 64
-  hr = r_key.SetValue(kValNameInt64, kIntVal64);
-  ASSERT_SUCCEEDED(hr);
-
-  // set a string
-  hr = r_key.SetValue(kValNameStr, kStrVal);
-  ASSERT_SUCCEEDED(hr);
-
-  // set a binary value
-  hr = r_key.SetValue(kValNameBinary, (const byte *)kBinaryVal,
-                      sizeof(kBinaryVal)-1);
-  ASSERT_SUCCEEDED(hr);
-
-  // get the value count
-  uint32 value_count = r_key.GetValueCount();
-  ASSERT_EQ(value_count, 4);
-
-  // check the value names
-  CString value_name;
-  DWORD type;
-
-  hr = r_key.GetValueNameAt(0, &value_name, &type);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(value_name, kValNameInt);
-  ASSERT_EQ(type, REG_DWORD);
-
-  hr = r_key.GetValueNameAt(1, &value_name, &type);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(value_name, kValNameInt64);
-  ASSERT_EQ(type, REG_QWORD);
-
-  hr = r_key.GetValueNameAt(2, &value_name, &type);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(value_name, kValNameStr);
-  ASSERT_EQ(type, REG_SZ);
-
-  hr = r_key.GetValueNameAt(3, &value_name, &type);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(value_name, kValNameBinary);
-  ASSERT_EQ(type, REG_BINARY);
-
-  // check that there are no more values
-  hr = r_key.GetValueNameAt(4, &value_name, &type);
-  ASSERT_FAILED(hr);
-
-  uint32 subkey_count = r_key.GetSubkeyCount();
-  ASSERT_EQ(subkey_count, 0);
-
-  RegKey temp_key;
-
-  // now create a subkey and make sure we can get the name
-  hr = temp_key.Create(HKEY_CURRENT_USER, kRkey1Subkey);
-  ASSERT_SUCCEEDED(hr);
-
-  // check the subkey exists
-  bool_res = r_key.HasSubkey(kRkey1SubkeyName);
-  ASSERT_TRUE(bool_res);
-
-  // check the name
-  subkey_count = r_key.GetSubkeyCount();
-  ASSERT_EQ(subkey_count, 1);
-
-  CString subkey_name;
-  hr = r_key.GetSubkeyNameAt(0, &subkey_name);
-  ASSERT_EQ(subkey_name, kRkey1SubkeyName);
-
-  // verify that the event handle remained the same throughout everything
-  ASSERT_EQ(change_event, r_key.change_event());
-
-  // close this key
-  r_key.Close();
-
-  // whack the whole key
-  hr = RegKey::DeleteKey(kStTestRkeyBase);
-  ASSERT_SUCCEEDED(hr);
-
-  // STATIC
-  // now set a different value using the static versions
-
-  // get an in-existent value from an un-existent key
-  hr = RegKey::GetValue(kStRkey1, kStValNameInt, &int_val);
-  ASSERT_EQ(hr, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
-
-  // set int32
-  hr = RegKey::SetValue(kStRkey1, kStValNameInt, kStIntVal);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value exists
-  bool_res = RegKey::HasValue(kStRkey1, kStValNameInt);
-  ASSERT_TRUE(bool_res);
-
-  // get an in-existent value from an existent key
-  hr = RegKey::GetValue(kStRkey1, _T("bogus"), &int_val);
-  ASSERT_EQ(hr, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
-
-  // read it back
-  hr = RegKey::GetValue(kStRkey1, kStValNameInt, &int_val);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(int_val, kStIntVal);
-
-  // delete the value
-  hr = RegKey::DeleteValue(kStRkey1, kStValNameInt);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value is gone
-  bool_res = RegKey::HasValue(kStRkey1, kStValNameInt);
-  ASSERT_FALSE(bool_res);
-
-
-  // set int64
-  hr = RegKey::SetValue(kStRkey1, kStValNameInt64, kStIntVal64);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value exists
-  bool_res = RegKey::HasValue(kStRkey1, kStValNameInt64);
-  ASSERT_TRUE(bool_res);
-
-  // read it back
-  hr = RegKey::GetValue(kStRkey1, kStValNameInt64, &int64_val);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(int64_val, kStIntVal64);
-
-  // read it back to test time64
-  bool limited_value;
-  hr = GetLimitedTimeValue(kStRkey1, kStValNameInt64,  kStIntVal64 + 10, &t,
-                           &limited_value);
-  ASSERT_SUCCEEDED(hr);
-  EXPECT_FALSE(limited_value);
-  ASSERT_EQ(t, kStIntVal64);
-  hr = GetLimitedTimeValue(kStRkey1, kStValNameInt64,  kStIntVal64 - 10, &t,
-                           &limited_value);
-  EXPECT_TRUE(limited_value);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(t, kStIntVal64 - 10);
-  // Verify that the GetValue permanently made the value lower
-  hr = GetLimitedTimeValue(kStRkey1, kStValNameInt64,  kStIntVal64, &t,
-                           &limited_value);
-  EXPECT_FALSE(limited_value);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(t, kStIntVal64 - 10);
-
-  // delete the value
-  hr = RegKey::DeleteValue(kStRkey1, kStValNameInt64);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value is gone
-  bool_res = RegKey::HasValue(kStRkey1, kStValNameInt64);
-  ASSERT_FALSE(bool_res);
-
-  // set float
-  hr = RegKey::SetValue(kStRkey1, kStValNameFloat, kStFloatVal);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value exists
-  bool_res = RegKey::HasValue(kStRkey1, kStValNameFloat);
-  ASSERT_TRUE(bool_res);
-
-  // read it back
-  hr = RegKey::GetValue(kStRkey1, kStValNameFloat, &float_val);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(float_val, kStFloatVal);
-
-  // delete the value
-  hr = RegKey::DeleteValue(kStRkey1, kStValNameFloat);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value is gone
-  bool_res = RegKey::HasValue(kStRkey1, kStValNameFloat);
-  ASSERT_FALSE(bool_res);
-  hr = RegKey::GetValue(kStRkey1, kStValNameFloat, &float_val);
-  ASSERT_FAILED(hr);
-
-
-  // set double
-  hr = RegKey::SetValue(kStRkey1, kStValNameDouble, kStDoubleVal);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value exists
-  bool_res = RegKey::HasValue(kStRkey1, kStValNameDouble);
-  ASSERT_TRUE(bool_res);
-
-  // read it back
-  hr = RegKey::GetValue(kStRkey1, kStValNameDouble, &double_val);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(double_val, kStDoubleVal);
-
-  // delete the value
-  hr = RegKey::DeleteValue(kStRkey1, kStValNameDouble);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value is gone
-  bool_res = RegKey::HasValue(kStRkey1, kStValNameDouble);
-  ASSERT_FALSE(bool_res);
-  hr = RegKey::GetValue(kStRkey1, kStValNameDouble, &double_val);
-  ASSERT_FAILED(hr);
-
-  // set string
-  hr = RegKey::SetValue(kStRkey1, kStValNameStr, kStStrVal);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value exists
-  bool_res = RegKey::HasValue(kStRkey1, kStValNameStr);
-  ASSERT_TRUE(bool_res);
-
-  // read it back
-  hr = RegKey::GetValue(kStRkey1, kStValNameStr, &str_val);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_STREQ(str_val, kStStrVal);
-  delete [] str_val;
-
-  // get an in-existent value from an existent key
-  hr = RegKey::GetValue(kStRkey1, _T("bogus"), &str_val);
-  ASSERT_EQ(hr, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
-
-  // delete the value
-  hr = RegKey::DeleteValue(kStRkey1, kStValNameStr);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value is gone
-  bool_res = RegKey::HasValue(kStRkey1, kStValNameStr);
-  ASSERT_FALSE(bool_res);
-
-  // set binary
-  hr = RegKey::SetValue(kStRkey1, kStValNameBinary, (const byte *)kStBinaryVal,
-                        sizeof(kStBinaryVal)-1);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value exists
-  bool_res = RegKey::HasValue(kStRkey1, kStValNameBinary);
-  ASSERT_TRUE(bool_res);
-
-  // read it back
-  hr = RegKey::GetValue(kStRkey1, kStValNameBinary, &binary_val, &byte_count);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(0, memcmp(binary_val, kStBinaryVal, sizeof(kStBinaryVal)-1));
-  delete [] binary_val;
-
-  // delete the value
-  hr = RegKey::DeleteValue(kStRkey1, kStValNameBinary);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value is gone
-  bool_res = RegKey::HasValue(kStRkey1, kStValNameBinary);
-  ASSERT_FALSE(bool_res);
-
-  // special case - set a binary value with length 0
-  hr = RegKey::SetValue(kStRkey1, kStValNameBinary,
-                        (const byte *)kStBinaryVal, 0);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value exists
-  bool_res = RegKey::HasValue(kStRkey1, kStValNameBinary);
-  ASSERT_TRUE(bool_res);
-
-  // read it back
-  hr = RegKey::GetValue(kStRkey1, kStValNameBinary, &binary_val, &byte_count);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(byte_count, 0);
-  ASSERT_TRUE(binary_val == NULL);
-  delete [] binary_val;
-
-  // delete the value
-  hr = RegKey::DeleteValue(kStRkey1, kStValNameBinary);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value is gone
-  bool_res = RegKey::HasValue(kStRkey1, kStValNameBinary);
-  ASSERT_FALSE(bool_res);
-
-  // special case - set a NULL binary value
-  hr = RegKey::SetValue(kStRkey1, kStValNameBinary, NULL, 100);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value exists
-  bool_res = RegKey::HasValue(kStRkey1, kStValNameBinary);
-  ASSERT_TRUE(bool_res);
-
-  // read it back
-  hr = RegKey::GetValue(kStRkey1, kStValNameBinary, &binary_val, &byte_count);
-  ASSERT_SUCCEEDED(hr);
-  ASSERT_EQ(byte_count, 0);
-  ASSERT_TRUE(binary_val == NULL);
-  delete [] binary_val;
-
-  // delete the value
-  hr = RegKey::DeleteValue(kStRkey1, kStValNameBinary);
-  ASSERT_SUCCEEDED(hr);
-
-  // check that the value is gone
-  bool_res = RegKey::HasValue(kStRkey1, kStValNameBinary);
-  ASSERT_FALSE(bool_res);
-
-  // whack the whole key
-
-  hr = RegKey::DeleteKey(kStTestRkeyBase);
-  ASSERT_SUCCEEDED(hr);
-}
-
-// RegKey::GetValue changes the output CString when errors occur.
-TEST_F(RegKeyTestClass, ChangesStringOnErrors) {
-  CString string_val = _T("foo");
-  EXPECT_FAILED(RegKey::GetValue(_T("HCKU"), _T("no_such_value"), &string_val));
-  ASSERT_TRUE(string_val.IsEmpty());
-}
-
-TEST_F(RegKeyCleanupTestKeyTest, CreateKeys) {
-  // 3 keys specified but the count is two.
-  const TCHAR* keys[] = {kStRkey1, kStRkey2, kStRkey3};
-  ASSERT_SUCCEEDED(RegKey::CreateKeys(keys, 2));
-
-  EXPECT_TRUE(RegKey::HasKey(kStRkey1));
-  EXPECT_TRUE(RegKey::HasKey(kStRkey2));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey3));
-}
-
-TEST_F(RegKeyCleanupTestKeyTest, CreateKey) {
-  ASSERT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
-
-  EXPECT_TRUE(RegKey::HasKey(kStRkey1));
-}
-
-TEST_F(RegKeyCleanupTestKeyTest, RenameValue) {
-  RegKey reg_key;
-  ASSERT_SUCCEEDED(reg_key.Create(HKEY_CURRENT_USER, kRkey1));
-  ASSERT_SUCCEEDED(reg_key.SetValue(kValNameInt, kIntVal));
-  ASSERT_TRUE(reg_key.HasValue(kValNameInt));
-
-  ASSERT_SUCCEEDED(reg_key.RenameValue(kValNameInt, kRenameValNameInt));
-  ASSERT_FALSE(reg_key.HasValue(kValNameInt));
-
-  DWORD int_val = 0;
-  EXPECT_SUCCEEDED(reg_key.GetValue(kRenameValNameInt, &int_val));
-  EXPECT_EQ(kIntVal, int_val);
-
-  EXPECT_SUCCEEDED(reg_key.Close());
-}
-
-TEST_F(RegKeyCleanupTestKeyTest, RenameValueStatic) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(kStRkey1, kStValNameStr, kStStrVal));
-  ASSERT_TRUE(RegKey::HasValue(kStRkey1, kStValNameStr));
-
-  RegKey::RenameValue(kStRkey1, kStValNameStr, kRenameStValNameStr);
-  ASSERT_FALSE(RegKey::HasValue(kStRkey1, kStValNameStr));
-
-  CString str_val;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kStRkey1, kRenameStValNameStr, &str_val));
-  EXPECT_STREQ(kStStrVal, str_val);
-}
-
-TEST_F(RegKeyCleanupTestKeyTest, CopyValue) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kStRkey1, kStValNameStr, kStStrVal));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kStRkey1, NULL, kStStrVal));
-  EXPECT_TRUE(RegKey::HasValue(kStRkey1, kStValNameStr));
-
-  // Test that CopyValue fails when the to_key does not exist.
-  EXPECT_FALSE(RegKey::HasKey(kStRkey2));
-  EXPECT_FAILED(RegKey::CopyValue(kStRkey1, kStRkey2, kStValNameStr));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey2));
-
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey2));
-  // Test CopyValue(full_from_key_name, full_to_key_name, value_name).
-  EXPECT_FALSE(RegKey::HasValue(kStRkey2, kStValNameStr));
-  RegKey::CopyValue(kStRkey1, kStRkey2, kStValNameStr);
-  CString str_val;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kStRkey2, kStValNameStr, &str_val));
-  EXPECT_STREQ(kStStrVal, str_val);
-
-  // Test CopyValue to a (Default) value.
-  EXPECT_FALSE(RegKey::HasValue(kStRkey2, NULL));
-  RegKey::CopyValue(kStRkey1, kStRkey2, NULL);
-  str_val.Empty();
-  EXPECT_SUCCEEDED(RegKey::GetValue(kStRkey2, NULL, &str_val));
-  EXPECT_STREQ(kStStrVal, str_val);
-
-  // Test CopyValue(full_from_key_name, from_value_name, full_to_key_name,
-  //                to_value_name).
-  EXPECT_FALSE(RegKey::HasValue(kStRkey2, kRenameStValNameStr));
-  RegKey::CopyValue(kStRkey1, kStValNameStr, kStRkey2, kRenameStValNameStr);
-  str_val.Empty();
-  EXPECT_SUCCEEDED(RegKey::GetValue(kStRkey2, kRenameStValNameStr, &str_val));
-  EXPECT_STREQ(kStStrVal, str_val);
-}
-
-// Delete a key that does not have children.
-
-TEST_F(RegKeyCleanupTestKeyTest, DeleteKey_NoChildren_Recursively) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
-
-  EXPECT_EQ(S_OK, RegKey::DeleteKey(kStRkey1, true));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
-}
-
-TEST_F(RegKeyCleanupTestKeyTest, DeleteKey_NoChildren_NotRecursively) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
-
-  EXPECT_EQ(S_OK, RegKey::DeleteKey(kStRkey1, false));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
-}
-
-TEST_F(RegKeyCleanupTestKeyTest, RecurseDeleteSubKey_NoChildren) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
-  EXPECT_SUCCEEDED(key_.Open(kStTestRkeyBase));
-
-  EXPECT_EQ(S_OK, key_.RecurseDeleteSubKey(kStRkey1Name));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
-}
-
-TEST_F(RegKeyCleanupTestKeyTest, DeleteSubKey_NoChildren) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
-  EXPECT_SUCCEEDED(key_.Open(kStTestRkeyBase));
-
-  EXPECT_EQ(S_OK, key_.DeleteSubKey(kStRkey1Name));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
-}
-
-// Delete a key that has a child.
-
-TEST_F(RegKeyCleanupTestKeyTest, DeleteKey_WithChild_Recursively) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1Subkey));
-
-  EXPECT_EQ(S_OK, RegKey::DeleteKey(kStRkey1, true));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
-}
-
-// Deleting a key with children present results in ERROR_ACCESS_DENIED.
-TEST_F(RegKeyCleanupTestKeyTest, DeleteKey_WithChild_NotRecursively) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1Subkey));
-
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED),
-            RegKey::DeleteKey(kStRkey1, false));
-  EXPECT_TRUE(RegKey::HasKey(kStRkey1));
-  EXPECT_TRUE(RegKey::HasKey(kStRkey1Subkey));
-}
-
-TEST_F(RegKeyCleanupTestKeyTest, RecurseDeleteSubKey_WithChild) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1Subkey));
-  EXPECT_SUCCEEDED(key_.Open(kStTestRkeyBase));
-
-  EXPECT_EQ(S_OK, key_.RecurseDeleteSubKey(kStRkey1Name));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
-}
-
-// Deleting a key with children present results in ERROR_ACCESS_DENIED.
-TEST_F(RegKeyCleanupTestKeyTest, DeleteSubKey_WithChild) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1Subkey));
-  EXPECT_SUCCEEDED(key_.Open(kStTestRkeyBase));
-
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED),
-            key_.DeleteSubKey(kStRkey1Name));
-  EXPECT_TRUE(RegKey::HasKey(kStRkey1));
-  EXPECT_TRUE(RegKey::HasKey(kStRkey1Subkey));
-}
-
-// Delete a key that does not exist.
-
-TEST_F(RegKeyCleanupTestKeyTest, DeleteKey_KeyDoesNotExist_Recursively) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
-
-  EXPECT_EQ(S_FALSE, RegKey::DeleteKey(kStRkey1Subkey, true));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
-}
-
-TEST_F(RegKeyCleanupTestKeyTest, DeleteKey_KeyDoesNotExist_NotRecursively) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
-
-  EXPECT_EQ(S_FALSE, RegKey::DeleteKey(kStRkey1Subkey, false));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
-}
-
-TEST_F(RegKeyCleanupTestKeyTest, RecurseDeleteSubKey_KeyDoesNotExist) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
-  EXPECT_SUCCEEDED(key_.Open(kStRkey1));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
-
-  EXPECT_EQ(S_FALSE, key_.RecurseDeleteSubKey(kRkey1SubkeyName));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
-}
-
-TEST_F(RegKeyCleanupTestKeyTest, DeleteSubKey_KeyDoesNotExist) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kStRkey1));
-  EXPECT_SUCCEEDED(key_.Open(kStRkey1));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
-
-  EXPECT_EQ(S_FALSE, key_.DeleteSubKey(kRkey1SubkeyName));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
-}
-
-// Delete a key whose parent does not exist.
-// There is no equivalent test for RecurseDeleteSubKey and DeleteSubKey.
-
-TEST_F(RegKeyCleanupTestKeyTest, DeleteKey_ParentKeyDoesNotExist_Recursively) {
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
-
-  EXPECT_EQ(S_FALSE, RegKey::DeleteKey(kStRkey1Subkey, true));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
-}
-
-TEST_F(RegKeyCleanupTestKeyTest,
-       DeleteKey_ParentKeyDoesNotExist_NotRecursively) {
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1));
-
-  EXPECT_EQ(S_FALSE, RegKey::DeleteKey(kStRkey1Subkey, false));
-  EXPECT_FALSE(RegKey::HasKey(kStRkey1Subkey));
-}
-
-}  // namespace omaha
diff --git a/common/regexp.cc b/common/regexp.cc
deleted file mode 100644
index a79889e..0000000
--- a/common/regexp.cc
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "regexp.h"
-#include "common/debug.h"
-
-namespace omaha {
-
-#define kMaxArgs 16
-
-bool RE::PartialMatch(const TCHAR* text, const RE& re,  // 3..16 args
-                      CString * a0,
-                      CString * a1,
-                      CString * a2,
-                      CString * a3,
-                      CString * a4,
-                      CString * a5,
-                      CString * a6,
-                      CString * a7,
-                      CString * a8,
-                      CString * a9,
-                      CString * a10,
-                      CString * a11,
-                      CString * a12,
-                      CString * a13,
-                      CString * a14,
-                      CString * a15)
-{
-  ASSERT(text, (L""));
-  // a0 may be NULL
-  // a1 may be NULL
-  // a2 may be NULL
-  // a3 may be NULL
-  // a4 may be NULL
-  // a5 may be NULL
-  // a6 may be NULL
-  // a7 may be NULL
-  // a8 may be NULL
-  // a9 may be NULL
-  // a10 may be NULL
-  // a11 may be NULL
-  // a12 may be NULL
-  // a13 may be NULL
-  // a14 may be NULL
-  // a15 may be NULL
-
-  CString * args[kMaxArgs];
-  int n = 0;
-  if (a0 == NULL) goto done; args[n++] = a0;
-  if (a1 == NULL) goto done; args[n++] = a1;
-  if (a2 == NULL) goto done; args[n++] = a2;
-  if (a3 == NULL) goto done; args[n++] = a3;
-  if (a4 == NULL) goto done; args[n++] = a4;
-  if (a5 == NULL) goto done; args[n++] = a5;
-  if (a6 == NULL) goto done; args[n++] = a6;
-  if (a7 == NULL) goto done; args[n++] = a7;
-  if (a8 == NULL) goto done; args[n++] = a8;
-  if (a9 == NULL) goto done; args[n++] = a9;
-  if (a10 == NULL) goto done; args[n++] = a10;
-  if (a11 == NULL) goto done; args[n++] = a11;
-  if (a12 == NULL) goto done; args[n++] = a12;
-  if (a13 == NULL) goto done; args[n++] = a13;
-  if (a14 == NULL) goto done; args[n++] = a14;
-  if (a15 == NULL) goto done; args[n++] = a15;
-
-done:
-  return re.DoMatchImpl(text,args,n,NULL);
-}
-
-// Like PartialMatch(), except the "input" is advanced past the matched
-// text.  Note: "input" is modified iff this routine returns true.
-// For example, "FindAndConsume(s, "(\\w+)", &word)" finds the next
-// word in "s" and stores it in "word".
-bool RE::FindAndConsume(const TCHAR **input, const RE& re,
-                        CString * a0,
-                        CString * a1,
-                        CString * a2,
-                        CString * a3,
-                        CString * a4,
-                        CString * a5,
-                        CString * a6,
-                        CString * a7,
-                        CString * a8,
-                        CString * a9,
-                        CString * a10,
-                        CString * a11,
-                        CString * a12,
-                        CString * a13,
-                        CString * a14,
-                        CString * a15)
-{
-  ASSERT(input, (L""));
-  // a0 may be NULL
-  // a1 may be NULL
-  // a2 may be NULL
-  // a3 may be NULL
-  // a4 may be NULL
-  // a5 may be NULL
-  // a6 may be NULL
-  // a7 may be NULL
-  // a8 may be NULL
-  // a9 may be NULL
-  // a10 may be NULL
-  // a11 may be NULL
-  // a12 may be NULL
-  // a13 may be NULL
-  // a14 may be NULL
-  // a15 may be NULL
-
-  CString * args[kMaxArgs];
-  int n = 0;
-  if (a0 == NULL) goto done; args[n++] = a0;
-  if (a1 == NULL) goto done; args[n++] = a1;
-  if (a2 == NULL) goto done; args[n++] = a2;
-  if (a3 == NULL) goto done; args[n++] = a3;
-  if (a4 == NULL) goto done; args[n++] = a4;
-  if (a5 == NULL) goto done; args[n++] = a5;
-  if (a6 == NULL) goto done; args[n++] = a6;
-  if (a7 == NULL) goto done; args[n++] = a7;
-  if (a8 == NULL) goto done; args[n++] = a8;
-  if (a9 == NULL) goto done; args[n++] = a9;
-  if (a10 == NULL) goto done; args[n++] = a10;
-  if (a11 == NULL) goto done; args[n++] = a11;
-  if (a12 == NULL) goto done; args[n++] = a12;
-  if (a13 == NULL) goto done; args[n++] = a13;
-  if (a14 == NULL) goto done; args[n++] = a14;
-  if (a15 == NULL) goto done; args[n++] = a15;
-
-done:
-  return re.DoMatchImpl(*input,args,n,input);
-}
-
-}  // namespace omaha
-
diff --git a/common/registry_hive.cc b/common/registry_hive.cc
deleted file mode 100644
index 69458ed..0000000
--- a/common/registry_hive.cc
+++ /dev/null
@@ -1,268 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/registry_hive.h"
-#include "omaha/common/accounts.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/string.h"
-#include "omaha/common/system.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-RegistryHive::RegistryHive()
-    : hive_holding_key_(NULL) {
-}
-
-RegistryHive::~RegistryHive() {
-  if ( !hive_name_.IsEmpty() ) {
-    UnloadHive();
-  }
-}
-
-// Loads hive for requested SID. SID should be in format "S-X-X....."
-// name is a name under which the hive will be added to the HKEY_USERS,
-// you could use persons name here. This parameter should not have '\\'
-// characters!
-// It is not recommended to load more than one hive at once - LoadHive,
-// manipulate hive, UnloadHive, and then work on the next one.
-//
-// Hive could be already loaded: you logged out from other user but system
-// had open key in your current user. In that case I open hive_holding_key_ to
-// prevent system from unloading hive and do not load/unload hive - the system
-// will do it.
-HRESULT RegistryHive::LoadHive(TCHAR const * sid, TCHAR const *name) {
-  ASSERT1(sid);
-  ASSERT1(name);
-  ASSERT1(hive_name_.IsEmpty());
-  ASSERT1(String_FindChar(name, _T('\\')) == -1);
-
-  CString profile_key;
-  CString hive_path;
-
-  // Need set SE_RESTORE_NAME/SE_BACKUP_NAME priveleges to current process
-  // otherwise loading of the hive will fail
-  RET_IF_FAILED(System::AdjustPrivilege(SE_RESTORE_NAME, true));
-  RET_IF_FAILED(System::AdjustPrivilege(SE_BACKUP_NAME, true));
-
-  profile_key.Format(kProfileKeyFormat, sid);
-
-  if ( FAILED(RegKey::GetValue(profile_key, kProfilePathValue, &hive_path)) ) {
-    return E_FAIL;
-  }
-
-
-  wchar_t temporary_buffer[MAX_PATH];
-  DWORD ret = ExpandEnvironmentStrings(hive_path, temporary_buffer, MAX_PATH);
-
-  if ( !ret || ret >= MAX_PATH ) {
-    return E_FAIL;
-  }
-  hive_path = temporary_buffer;
-
-  hive_path.Append(_T("\\"));
-  hive_path.Append(kHiveName);
-
-  hive_name_ = name;
-
-  LONG res = RegLoadKey(HKEY_USERS, hive_name_, hive_path);
-
-  if ( ERROR_SHARING_VIOLATION == res ) {
-    // It is quite possible that the hive is still held by system.
-    hive_name_ = sid;
-
-    // if it is the case, this call will succeeed, and the system will not
-    // unload the hive while there are outstanding keys opened.
-    res = RegOpenKeyEx(HKEY_USERS,
-                       hive_name_,
-                       0,
-                       KEY_ALL_ACCESS,
-                       &hive_holding_key_);
-  }
-
-  return (res == ERROR_SUCCESS) ? S_OK : E_FAIL;
-}
-
-// Loads hive for requested SID, but only if the SID is another user
-// (since we don't need to do anything if the sid is ours)
-HRESULT RegistryHive::LoadHive(TCHAR const * user_sid) {
-  ASSERT1(user_sid != NULL);
-  bool other_user = false;
-
-  // Determine if the SID passed in is really another user
-  CString current_user_sid;
-  HRESULT hr = omaha::user_info::GetCurrentUser(NULL, NULL, &current_user_sid);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[RegistryHive::LoadHive - failed to get current user")));
-    return hr;
-  }
-
-  // user_sid is the current user - no need to load the hive
-  if (lstrcmpi(current_user_sid, user_sid) == 0)
-    return S_FALSE;
-
-  // Get info on the sid we're being asked to load for
-  CString name;
-  CString domain;
-  SID_NAME_USE user_type;
-  hr = accounts::GetUserInfo(user_sid, &name, &domain, &user_type);
-  if ( FAILED(hr) || user_type != SidTypeUser ) {
-    // Either Sid no longer exists or Sid is not a user Sid
-    // (There is other possibility: Sid could be for roaming profile on domain
-    // which is currently down, but we do not support roaming profiles)
-    return FAILED(hr) ? hr : E_FAIL;
-  }
-
-  hr = LoadHive(user_sid, name);  // Use user name as a temporary key name.
-  if ( FAILED(hr) ) {
-    // Hive no longer present.
-    return E_FAIL;
-  }
-
-  return S_OK;
-}
-
-
-// Unloads and saves loaded hive
-HRESULT RegistryHive::UnloadHive() {
-  if (hive_name_.IsEmpty())
-    return S_OK;
-
-  LONG res;
-  if ( hive_holding_key_ ) {
-    res = RegCloseKey(hive_holding_key_);
-    hive_holding_key_ = NULL;
-    // no need to unload hive. System will do it.
-  } else {
-    res = RegUnLoadKey(HKEY_USERS, hive_name_);
-  }
-  hive_name_.Empty();
-  return (res == ERROR_SUCCESS) ? S_OK : E_FAIL;
-}
-
-// Does it recursively. The name should be relative to HKEY_CURRENT_USER.
-HRESULT RegistryHive::DeleteUserKey(TCHAR const * key_name) {
-  ASSERT(key_name && *key_name, (L""));
-  if ( !key_name || !*key_name ) {
-    return E_FAIL;
-  }
-  CString key(key_name);
-  ExpandKeyName(&key);
-
-  if ( !RegKey::SafeKeyNameForDeletion(key) ) {
-    return E_FAIL;
-  }
-
-  return RegKey::DeleteKey(key);
-}
-
-void RegistryHive::ExpandKeyName(CString * str) {
-  ASSERT1(str);
-
-  // If we haven't loaded another user's hive, use HKCU instead of
-  // HKEY_USERS
-  CString key_name;
-  if (hive_name_.IsEmpty()) {
-    key_name = _T("HKCU\\");
-  } else {
-    key_name = _T("HKEY_USERS\\");
-    key_name.Append(hive_name_ + _T("\\"));
-  }
-
-  key_name.Append(*str);
-  *str = key_name;
-}
-
-// Load a user registry
-int LoadUserRegistry(const TCHAR* user_sid,
-                     ProcessUserRegistryFunc* handler,
-                     LONG_PTR param) {
-  ASSERT1(user_sid && *user_sid);
-  ASSERT1(handler);
-
-  // Get current user SID
-  CString curr_user_sid;
-  HRESULT hr = omaha::user_info::GetCurrentUser(NULL, NULL, &curr_user_sid);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[LoadUserRegistry - can't get current user][0x%x]"), hr));
-    return 0;
-  }
-
-  // Is current user?
-  bool other_user = curr_user_sid.CompareNoCase(user_sid) != 0;
-
-  // Get the hive for this user
-  RegistryHive user_hive;
-  if (other_user) {
-    // Get the info about this user
-    SID_NAME_USE user_type = SidTypeInvalid;
-    CString name, domain;
-    hr = accounts::GetUserInfo(user_sid, &name, &domain, &user_type);
-    if (FAILED(hr) || user_type != SidTypeUser) {
-      // Either SID no longer exists or SID is not a user Sid
-      // (There is other possibility: SID could be for roaming profile on domain
-      // which is currently down, but we do not support roaming profiles)
-      UTIL_LOG(LEVEL_WARNING,
-               (_T("[LoadUserRegistry - SID %s invalid or unsupported][0x%x]"),
-                user_sid, hr));
-      return 0;
-    }
-
-    // Load the hive
-    hr = user_hive.LoadHive(user_sid, domain + _T("_") + name);
-    if (FAILED(hr)) {
-      // Hive no longer present.
-      UTIL_LOG(LW, (_T("[LoadUserRegistry]")
-                    _T("[hive not present for %s][0x%x]"), user_sid, hr));
-      return 0;
-    }
-  }
-
-  // Get the registry key path
-  CString user_reg_path;
-  user_hive.ExpandKeyName(&user_reg_path);
-
-  // Call the handler
-  int res = (*handler)(user_sid, user_reg_path, param);
-
-  // Unload the hive
-  if (other_user) {
-    hr = user_hive.UnloadHive();
-    if (FAILED(hr)) {
-      UTIL_LOG(LE, (_T("[LoadUserRegistry]")
-                    _T("[failed to save hive for %s][0x%x]"), user_sid, hr));
-    }
-  }
-
-  return res;
-}
-
-// Enumerate all user registries
-void EnumerateAllUserRegistries(ProcessUserRegistryFunc* handler,
-                                LONG_PTR param) {
-  ASSERT1(handler);
-
-  CSimpleArray<CString> sid_array;
-  accounts::GetAllUserSids(&sid_array);
-  for (int i = 0 ; i < sid_array.GetSize() ; ++i) {
-    if (LoadUserRegistry(sid_array[i], handler, param)) {
-      return;
-    }
-  }
-}
-
-}  // namespace omaha
-
diff --git a/common/registry_monitor_manager.cc b/common/registry_monitor_manager.cc
deleted file mode 100644
index 43eb5f7..0000000
--- a/common/registry_monitor_manager.cc
+++ /dev/null
@@ -1,586 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// RegistryMonitor creates a KeyWatcher for every unique registry key that
-// contains a value registered by MonitorValue. Each KeyWatcher is responsible
-// for monitoring one or more values in a single registry key but not its
-// subkeys. RegistryMonitor manages a thread which waits on event objects.
-// The events are signaled when the corresponding monitored key changes.
-
-#include "omaha/common/registry_monitor_manager.h"
-#include <atlbase.h>
-#include <utility>
-#include <vector>
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/common/thread.h"
-
-namespace omaha {
-
-namespace detail {
-
-// converts a registry change type value to a string for logging purposes.
-CString RegistryChangeTypeToString(RegistryChangeType registry_change_type) {
-  switch (registry_change_type) {
-  case REGISTRY_CHANGE_TYPE_CREATE:
-    return _T("create");
-  case REGISTRY_CHANGE_TYPE_UPDATE:
-    return _T("update");
-  case REGISTRY_CHANGE_TYPE_DELETE:
-    return _T("delete");
-  default:
-    ASSERT1(false);
-    return _T("unknown");
-  }
-};
-
-// Holds a pair of root key and sub key, as monitoring must be unique for
-// each pair.
-class KeyId {
- public:
-  KeyId(HKEY parent_key, const CString& key_name)
-      : parent_key_(parent_key), key_name_(key_name) {}
-
-  HKEY parent_key() const { return parent_key_; }
-  CString key_name() const { return key_name_; }
-
-  static bool IsEqual(const KeyId& id1, const KeyId& id2) {
-    return id1.parent_key_ == id2.parent_key_ &&
-           id1.key_name_   == id2.key_name_;
-  }
- private:
-  HKEY    parent_key_;
-  CString key_name_;
-};
-
-class KeyWatcher;
-
-// ValueWatcher represents a single monitored registry value.
-// It is used by KeyWatcher to determine which registry value has changed when
-// it detects a change in its key.
-class ValueWatcher {
- public:
-  ValueWatcher(KeyWatcher* key_watcher,
-               const CString& value_name,
-               int value_type,
-               RegistryValueChangeCallback callback,
-               void* user_data);
-  ~ValueWatcher();
-
-  // Returns true if the initial value has changed.
-  bool HasChanged();
-
-  // Calls the callback function to do the notification of the change.
-  void DoCallback();
-
- private:
-  CString GetCurrentValueString();
-  DWORD   GetCurrentValueDword();
-
-  CString last_known_value_string_;
-  DWORD last_known_value_dword_;
-  bool value_is_valid_;
-  RegistryChangeType change_type_;
-  CString value_name_;
-  int value_type_;
-  KeyWatcher* key_watcher_;
-  RegistryValueChangeCallback callback_;
-  void* callback_param_;
-};
-
-
-// KeyWatcher is responsible for monitoring changes to a single key in the
-// Windows registry. RegistryMonitor keeps a container of KeyWatcher objects,
-// one object for each key that contains a value to be monitored.
-class KeyWatcher {
- public:
-  explicit KeyWatcher(const KeyId& key_id);
-
-  ~KeyWatcher();
-
-  // Adds a new registry value to monitor.
-  HRESULT AddValue(const CString& value_name,
-                   int value_type,
-                   RegistryValueChangeCallback callback,
-                   void* user_data);
-
-  // Registers the key watcher with the OS and gets ready to receive events.
-  HRESULT StartWatching();
-
-  // Returns true if the underlying registry handle corresponds to a valid key.
-  bool IsKeyValid();
-
-  HANDLE notification_event() const { return get(notification_event_); }
-
-  RegKey& key() { return key_; }
-
-  CString key_name() const { return key_id_.key_name(); }
-
-  void set_callback(RegistryKeyChangeCallback callback, void* callback_param) {
-    callback_       = callback;
-    callback_param_ = callback_param;
-  }
-
-  // Callback called when the notification event is signaled by the OS
-  // as a result of a change in the monitored key.
-  void HandleEvent(HANDLE handle);
-
- private:
-  // Ensures the key to monitor is always open.
-  HRESULT EnsureOpen();
-
-  std::vector<ValueWatcher*> values_;
-  RegKey key_;
-  const KeyId key_id_;
-  scoped_event notification_event_;
-
-  RegistryKeyChangeCallback callback_;
-  void* callback_param_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(KeyWatcher);
-};
-
-class RegistryMonitorImpl : public Runnable {
- public:
-  RegistryMonitorImpl();
-  ~RegistryMonitorImpl();
-
-  HRESULT MonitorKey(HKEY root_key,
-                     const CString& sub_key,
-                     RegistryKeyChangeCallback callback,
-                     void* user_data);
-
-  HRESULT MonitorValue(HKEY root_key,
-                       const CString& sub_key,
-                       const CString& value_name,
-                       int value_type,
-                       RegistryValueChangeCallback callback,
-                       void* user_data);
-
-  HRESULT Initialize();
-
-  HRESULT StartMonitoring();
-
- private:
-
-  // Runnable.
-  virtual void Run();
-
-  typedef std::pair<KeyId, KeyWatcher*> Watcher;
-  std::vector<Watcher> watchers_;
-
-  Thread thread_;
-  scoped_ptr<Gate> start_monitoring_gate_;
-  scoped_event stop_monitoring_;
-  DISALLOW_EVIL_CONSTRUCTORS(RegistryMonitorImpl);
-};
-
-
-ValueWatcher::ValueWatcher(KeyWatcher* key_watcher,
-                           const CString &value_name,
-                           int value_type,
-                           RegistryValueChangeCallback callback,
-                           void* user_data)
-    : key_watcher_(key_watcher),
-      value_is_valid_(false),
-      change_type_(REGISTRY_CHANGE_TYPE_CREATE),
-      callback_(callback),
-      callback_param_(user_data),
-      value_name_(value_name),
-      value_type_(value_type),
-      last_known_value_dword_(0) {
-  ASSERT1(key_watcher);
-  ASSERT1(callback);
-  if (value_type_ == REG_SZ) {
-    last_known_value_string_ = GetCurrentValueString();
-  } else if (value_type_ == REG_DWORD) {
-    last_known_value_dword_ = GetCurrentValueDword();
-  } else {
-    ASSERT(false, (_T("value type not supported")));
-  }
-}
-
-ValueWatcher::~ValueWatcher() {
-}
-
-bool ValueWatcher::HasChanged() {
-  UTIL_LOG(L3, (_T("[ValueWatcher::HasChanged]")
-                _T("[key name '%s'][value '%s'][valid %d]"),
-                key_watcher_->key_name(), value_name_, value_is_valid_));
-
-  const bool value_was_valid = value_is_valid_;
-
-  bool has_changed = false;
-  if (value_type_ == REG_SZ) {
-    CString new_value = GetCurrentValueString();
-    has_changed = last_known_value_string_ != new_value;
-
-    UTIL_LOG(L3, (_T("[ValueWatcher::HasChanged][old value %s][new value %s]"),
-                  last_known_value_string_, new_value));
-
-    last_known_value_string_ = new_value;
-  } else if (value_type_ == REG_DWORD) {
-    DWORD new_value = GetCurrentValueDword();
-    has_changed = last_known_value_dword_ != new_value;
-
-    UTIL_LOG(L3, (_T("[ValueWatcher::HasChanged][old value %d][new value %d]"),
-                  last_known_value_dword_, new_value));
-
-    last_known_value_dword_ = new_value;
-  } else {
-    ASSERT(false, (_T("value type not supported")));
-  }
-
-  // Detect the type of the change based on previous and current value state.
-  if (value_was_valid && value_is_valid_) {
-    change_type_ = REGISTRY_CHANGE_TYPE_UPDATE;
-  } else if (value_was_valid && !value_is_valid_) {
-    change_type_ = REGISTRY_CHANGE_TYPE_DELETE;
-  } else if (!value_was_valid && value_is_valid_) {
-    change_type_ = REGISTRY_CHANGE_TYPE_CREATE;
-  } else {
-    ASSERT1(!value_was_valid && !value_is_valid_);
-  }
-
-  if (has_changed) {
-    UTIL_LOG(L3, (_T("[ValueWatcher::HasChanged]")
-                  _T("[key name '%s'][value '%s' has changed][%s]"),
-                  key_watcher_->key_name(), value_name_,
-                  RegistryChangeTypeToString(change_type_)));
-  } else {
-    UTIL_LOG(L3, (_T("[ValueWatcher::HasChanged]")
-                  _T("[key name '%s'][value '%s' is the same]"),
-                  key_watcher_->key_name(), value_name_));
-  }
-
-  return has_changed;
-}
-
-CString ValueWatcher::GetCurrentValueString() {
-  CString value_data;
-  RegKey& key = key_watcher_->key();
-  ASSERT1(key.Key());
-  value_is_valid_ = SUCCEEDED(key.GetValue(value_name_, &value_data));
-  return value_is_valid_ ? value_data : CString();
-}
-
-DWORD ValueWatcher::GetCurrentValueDword() {
-  DWORD value_data = 0;
-  RegKey& key = key_watcher_->key();
-  ASSERT1(key.Key());
-  value_is_valid_ = SUCCEEDED(key.GetValue(value_name_, &value_data));
-  return value_is_valid_ ? value_data : static_cast<DWORD>(-1);
-}
-
-void ValueWatcher::DoCallback() {
-  ASSERT1(callback_ != NULL);
-
-  const void* value = NULL;
-  if (value_type_ == REG_SZ) {
-    value = static_cast<const TCHAR*>(last_known_value_string_);
-  } else if (value_type_ == REG_DWORD) {
-    value = reinterpret_cast<void*>(last_known_value_dword_);
-  }
-
-  callback_(key_watcher_->key_name(), value_name_, change_type_,
-            value, callback_param_);
-
-  // If value was not valid, for example, the key was deleted or renamed, and
-  // it is valid after callback, update last known with the current value.
-  if (!value_is_valid_) {
-    if (value_type_ == REG_SZ) {
-      CString new_value = GetCurrentValueString();
-      if (value_is_valid_) {
-        last_known_value_string_ = new_value;
-      }
-    } else if (value_type_ == REG_DWORD) {
-      DWORD new_value = GetCurrentValueDword();
-      if (value_is_valid_) {
-        last_known_value_dword_ = new_value;
-      }
-    }
-  }
-}
-
-KeyWatcher::KeyWatcher(const KeyId& key_id)
-    : key_id_(key_id),
-      notification_event_(::CreateEvent(NULL, false, false, NULL)),
-      callback_(NULL),
-      callback_param_(NULL) {
-}
-
-KeyWatcher::~KeyWatcher() {
-  for (size_t i = 0; i != values_.size(); ++i) {
-    delete values_[i];
-  }
-}
-
-HRESULT KeyWatcher::StartWatching() {
-  // By this time the key could be deleted or renamed. Check if the handle
-  // is still valid and reopen the key if needed.
-  HRESULT hr = EnsureOpen();
-  if (FAILED(hr)) {
-    return hr;
-  }
-  ASSERT1(key_.Key());
-  const DWORD kNotifyFilter = REG_NOTIFY_CHANGE_NAME          |
-                              REG_NOTIFY_CHANGE_ATTRIBUTES    |
-                              REG_NOTIFY_CHANGE_LAST_SET      |
-                              REG_NOTIFY_CHANGE_SECURITY;
-  LONG result = ::RegNotifyChangeKeyValue(key_.Key(), false, kNotifyFilter,
-                                          get(notification_event_), true);
-  UTIL_LOG(L3, (_T("[KeyWatcher::StartWatching][key '%s' %s]"),
-                key_id_.key_name(),
-                result == ERROR_SUCCESS ? _T("ok") : _T("failed")));
-  return HRESULT_FROM_WIN32(result);
-}
-
-HRESULT KeyWatcher::AddValue(const CString& value_name,
-                             int value_type,
-                             RegistryValueChangeCallback callback,
-                             void* user_data) {
-  ASSERT1(callback);
-  HRESULT hr = EnsureOpen();
-  if (FAILED(hr)) {
-    return hr;
-  }
-  values_.push_back(
-      new ValueWatcher(this, value_name, value_type, callback, user_data));
-  return S_OK;
-}
-
-void KeyWatcher::HandleEvent(HANDLE handle) {
-  UTIL_LOG(L3, (_T("[KeyWatcher::HandleEvent][key '%s']"), key_id_.key_name()));
-
-  ASSERT1(handle);
-  ASSERT1(handle == get(notification_event_));
-  UNREFERENCED_PARAMETER(handle);
-
-  // Although not documented, it seems the OS pulses the event so the event
-  // is never signaled at this point.
-  ASSERT1(::WaitForSingleObject(handle, 0) == WAIT_TIMEOUT);
-
-  // Notify the key has changed.
-  if (callback_) {
-    callback_(key_name(), callback_param_);
-  }
-
-  // Notify the values have changed.
-  for (size_t i = 0; i != values_.size(); ++i) {
-    ValueWatcher* value = values_[i];
-    if (value != NULL) {
-      if (value->HasChanged()) {
-        value->DoCallback();
-      }
-    }
-  }
-
-  VERIFY1(SUCCEEDED(StartWatching()));
-}
-
-HRESULT KeyWatcher::EnsureOpen() {
-  // Close the key if it is not valid for whatever reasons, such as it was
-  // deleted and recreated back.
-  if (!IsKeyValid()) {
-    UTIL_LOG(L3, (_T("[key '%s' is not valid]"), key_id_.key_name()));
-    VERIFY1(SUCCEEDED(key_.Close()));
-  }
-
-  // Open the key if not already open or create the key if needed.
-  HRESULT hr = S_OK;
-  if (!key_.Key()) {
-    hr = key_.Create(key_id_.parent_key(), key_id_.key_name());
-    if (SUCCEEDED(hr)) {
-      UTIL_LOG(L3, (_T("[key '%s' has been created]"), key_id_.key_name()));
-    }
-  }
-  return hr;
-}
-
-bool KeyWatcher::IsKeyValid() {
-  if (!key_.Key()) {
-    return false;
-  }
-  LONG ret = RegQueryInfoKey(key_.Key(),
-                             NULL, NULL, NULL, NULL, NULL,
-                             NULL, NULL, NULL, NULL, NULL, NULL);
-  return ret == ERROR_SUCCESS;
-}
-
-RegistryMonitorImpl::RegistryMonitorImpl() {
-}
-
-RegistryMonitorImpl::~RegistryMonitorImpl() {
-  if (stop_monitoring_) {
-    VERIFY1(::SetEvent(get(stop_monitoring_)));
-  }
-  VERIFY1(thread_.WaitTillExit(INFINITE));
-
-  for (size_t i = 0; i != watchers_.size(); ++i) {
-    ASSERT1(watchers_[i].second);
-    delete watchers_[i].second;
-  }
-}
-
-HRESULT RegistryMonitorImpl::Initialize() {
-  reset(stop_monitoring_, ::CreateEvent(NULL, true, false, NULL));
-  if (!stop_monitoring_) {
-    return HRESULTFromLastError();
-  }
-  return S_OK;
-}
-
-HRESULT RegistryMonitorImpl::MonitorKey(HKEY root_key,
-                                        const CString& sub_key,
-                                        RegistryKeyChangeCallback callback,
-                                        void* user_data) {
-  ASSERT1(callback);
-  ASSERT1(!thread_.Running());
-
-  KeyId key_id(root_key, sub_key);
-  for (size_t i = 0; i != watchers_.size(); ++i) {
-    if (KeyId::IsEqual(watchers_[i].first, key_id)) {
-      watchers_[i].second->set_callback(callback, user_data);
-      return S_OK;
-    }
-  }
-  scoped_ptr<KeyWatcher> key_watcher(new KeyWatcher(key_id));
-  key_watcher->set_callback(callback, user_data);
-  Watcher watcher(key_id, key_watcher.release());
-  watchers_.push_back(watcher);
-  return S_OK;
-}
-
-HRESULT RegistryMonitorImpl::MonitorValue(
-    HKEY root_key, const CString& sub_key, const CString& value_name,
-    int value_type, RegistryValueChangeCallback callback, void* user_data) {
-  ASSERT1(callback);
-  ASSERT1(!thread_.Running());
-
-  // Reuse an existing key watcher if there is a value already registered
-  // for monitoring under the respective registry key.
-  KeyId key_id(root_key, sub_key);
-  for (size_t i = 0; i != watchers_.size(); ++i) {
-    if (KeyId::IsEqual(watchers_[i].first, key_id)) {
-      return watchers_[i].second->AddValue(value_name, value_type,
-                                           callback, user_data);
-    }
-  }
-  scoped_ptr<KeyWatcher> key_watcher(new KeyWatcher(key_id));
-  HRESULT hr = key_watcher->AddValue(value_name, value_type,
-                                     callback, user_data);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[RegistryMonitorImpl::RegisterValue failed]")
-                  _T("[key %s][value %s][0x%x]"), sub_key, value_name, hr));
-    return hr;
-  }
-  Watcher watcher(key_id, key_watcher.release());
-  watchers_.push_back(watcher);
-  return S_OK;
-}
-
-HRESULT RegistryMonitorImpl::StartMonitoring() {
-  // Starts the thread and waits on the gate for the thread to open after
-  // it has registered all watchers for notifications and it is ready to
-  // handle notification events. The gate is only needed to synchronize the
-  // caller and the monitoring threads.
-  start_monitoring_gate_.reset(new Gate);
-  if (!thread_.Start(this)) {
-    return E_FAIL;
-  }
-  bool wait_result = start_monitoring_gate_->Wait(INFINITE);
-  start_monitoring_gate_.reset();
-  ASSERT1(wait_result);
-  return wait_result ? S_OK : HRESULTFromLastError();
-}
-
-void RegistryMonitorImpl::Run() {
-  UTIL_LOG(L3, (_T("[started monitoring registry]")));
-
-  const size_t kNumNotificationHandles = watchers_.size();
-  const size_t kNumHandles = kNumNotificationHandles + 1;
-  const size_t kStopMonitoringHandleIndex = kNumNotificationHandles;
-
-  scoped_array<HANDLE> handles(new HANDLE[kNumHandles]);
-  for (size_t i = 0; i != watchers_.size(); ++i) {
-    handles[i] = watchers_[i].second->notification_event();
-    VERIFY1(SUCCEEDED(watchers_[i].second->StartWatching()));
-  }
-  handles[kStopMonitoringHandleIndex] = get(stop_monitoring_);
-
-  // Open the gate and allow the RegistryMonitor::StartMonitoring call to
-  // to return to the caller.
-  ASSERT1(start_monitoring_gate_.get());
-  VERIFY1(start_monitoring_gate_->Open());
-
-  for (;;) {
-    DWORD result = ::WaitForMultipleObjects(kNumHandles,
-                                            handles.get(),
-                                            false,
-                                            INFINITE);
-    COMPILE_ASSERT(0 == WAIT_OBJECT_0, invalid_wait_object_0);
-    ASSERT1(result < kNumHandles);
-    if (result < kNumHandles) {
-      if (result == kStopMonitoringHandleIndex) {
-        break;
-      } else {
-        size_t i = result - WAIT_OBJECT_0;
-        watchers_[i].second->HandleEvent(handles[i]);
-      }
-    }
-  }
-  UTIL_LOG(L3, (_T("[stopped monitoring registry]")));
-}
-
-}  // namespace detail
-
-RegistryMonitor::RegistryMonitor()
-    : impl_(new detail::RegistryMonitorImpl) {
-}
-
-RegistryMonitor::~RegistryMonitor() {
-}
-
-HRESULT RegistryMonitor::MonitorKey(HKEY root_key,
-                                    const CString& sub_key,
-                                    RegistryKeyChangeCallback callback,
-                                    void* user_data) {
-  return impl_->MonitorKey(root_key, sub_key, callback, user_data);
-}
-
-HRESULT RegistryMonitor::MonitorValue(HKEY root_key,
-                                      const CString& sub_key,
-                                      const CString& value_name,
-                                      int value_type,
-                                      RegistryValueChangeCallback callback,
-                                      void* user_data) {
-  return impl_->MonitorValue(root_key, sub_key, value_name, value_type,
-                             callback, user_data);
-}
-
-HRESULT RegistryMonitor::Initialize() {
-  return impl_->Initialize();
-}
-
-HRESULT RegistryMonitor::StartMonitoring() {
-  return impl_->StartMonitoring();
-}
-
-}  // namespace omaha
-
diff --git a/common/registry_monitor_manager_unittest.cc b/common/registry_monitor_manager_unittest.cc
deleted file mode 100644
index ef20634..0000000
--- a/common/registry_monitor_manager_unittest.cc
+++ /dev/null
@@ -1,235 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <windows.h>
-#include <limits.h>
-#include "base/basictypes.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/reactor.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/registry_monitor_manager.h"
-#include "omaha/common/thread.h"
-#include "omaha/common/utils.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-namespace {
-
-const TCHAR kKeyNameFull[] = _T("HKCU\\key");
-const TCHAR kKeyName[]     = _T("key");
-const TCHAR kValueName[]   = _T("value");
-
-}  // namespace
-
-class RegistryMonitorTest : public testing::Test {
- protected:
-  RegistryMonitorTest() {}
-
-  virtual void SetUp() {
-    // Override HKCU.
-    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
-    OverrideSpecifiedRegistryHives(kRegistryHiveOverrideRoot, false, true);
-    reset(registry_changed_event_, ::CreateEvent(NULL, true, false, NULL));
-  }
-
-  virtual void TearDown() {
-    reset(registry_changed_event_);
-    RestoreRegistryHives();
-    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
-  }
-
-  static void RegistryDeleteCallback(const TCHAR* key_name,
-                                     const TCHAR* value_name,
-                                     RegistryChangeType change_type,
-                                     const void* new_value_data,
-                                     void* user_data) {
-    EXPECT_STREQ(kKeyName, key_name);
-    EXPECT_STREQ(kValueName, value_name);
-    EXPECT_EQ(REGISTRY_CHANGE_TYPE_DELETE, change_type);
-    EXPECT_TRUE(new_value_data);
-    EXPECT_TRUE(user_data);
-    RegistryMonitorTest* object = static_cast<RegistryMonitorTest*>(user_data);
-    DWORD actual_value = reinterpret_cast<DWORD>(new_value_data);
-    EXPECT_EQ(ULONG_MAX, actual_value);
-    EXPECT_TRUE(::SetEvent(get(object->registry_changed_event_)));
-  }
-
-  static void RegistryChangeCallback(const TCHAR* key_name,
-                                     const TCHAR* value_name,
-                                     RegistryChangeType change_type,
-                                     const void* new_value_data,
-                                     void* user_data) {
-    EXPECT_STREQ(kKeyName, key_name);
-    EXPECT_STREQ(kValueName, value_name);
-    EXPECT_EQ(REGISTRY_CHANGE_TYPE_UPDATE, change_type);
-    EXPECT_TRUE(new_value_data);
-    EXPECT_TRUE(user_data);
-    RegistryMonitorTest* object = static_cast<RegistryMonitorTest*>(user_data);
-    const TCHAR* actual_value = static_cast<const TCHAR*>(new_value_data);
-    EXPECT_STREQ(_T("foo"), actual_value);
-    EXPECT_TRUE(::SetEvent(get(object->registry_changed_event_)));
-  }
-
-  static void RegistryChangesCallback(const TCHAR* key_name,
-                                     const TCHAR* value_name,
-                                     RegistryChangeType change_type,
-                                     const void* new_value_data,
-                                     void* user_data) {
-    EXPECT_STREQ(kKeyName, key_name);
-    EXPECT_STREQ(kValueName, value_name);
-    EXPECT_EQ(REGISTRY_CHANGE_TYPE_UPDATE, change_type);
-    EXPECT_TRUE(new_value_data);
-    EXPECT_TRUE(user_data);
-    RegistryMonitorTest* object = static_cast<RegistryMonitorTest*>(user_data);
-    EXPECT_TRUE(::SetEvent(get(object->registry_changed_event_)));
-  }
-
-  static void RegistryCreateCallback(const TCHAR* key_name,
-                                     const TCHAR* value_name,
-                                     RegistryChangeType change_type,
-                                     const void* new_value_data,
-                                     void* user_data) {
-    EXPECT_STREQ(kKeyName, key_name);
-    EXPECT_STREQ(kValueName, value_name);
-    EXPECT_EQ(REGISTRY_CHANGE_TYPE_CREATE, change_type);
-    EXPECT_TRUE(new_value_data);
-    EXPECT_TRUE(user_data);
-    RegistryMonitorTest* object = static_cast<RegistryMonitorTest*>(user_data);
-    DWORD actual_value = reinterpret_cast<DWORD>(new_value_data);
-    EXPECT_EQ(1, actual_value);
-    EXPECT_TRUE(::SetEvent(get(object->registry_changed_event_)));
-  }
-
-  static void RegistryKeyCallback(const TCHAR* key_name, void* user_data) {
-    EXPECT_STREQ(kKeyName, key_name);
-    RegistryMonitorTest* object = static_cast<RegistryMonitorTest*>(user_data);
-    EXPECT_TRUE(::SetEvent(get(object->registry_changed_event_)));
-  }
-
-  scoped_event registry_changed_event_;
-
-  static DWORD const kWaitForChangeMs = 5000;
-};
-
-TEST_F(RegistryMonitorTest, DeleteValue) {
-  DWORD value = 0;
-  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(kKeyNameFull, kValueName, value));
-  RegistryMonitor registry_monitor;
-  ASSERT_HRESULT_SUCCEEDED(registry_monitor.Initialize());
-  ASSERT_HRESULT_SUCCEEDED(registry_monitor.MonitorValue(
-      HKEY_CURRENT_USER, kKeyName, kValueName, REG_DWORD,
-      RegistryDeleteCallback, this));
-  ASSERT_HRESULT_SUCCEEDED(registry_monitor.StartMonitoring());
-
-  EXPECT_HRESULT_SUCCEEDED(RegKey::DeleteValue(kKeyNameFull, kValueName));
-  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(registry_changed_event_),
-                                                 kWaitForChangeMs));
-}
-
-TEST_F(RegistryMonitorTest, ChangeValue) {
-  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(kKeyNameFull, kValueName, _T("")));
-  RegistryMonitor registry_monitor;
-  ASSERT_HRESULT_SUCCEEDED(registry_monitor.Initialize());
-  ASSERT_HRESULT_SUCCEEDED(registry_monitor.MonitorValue(
-    HKEY_CURRENT_USER, kKeyName, kValueName, REG_SZ,
-    RegistryChangeCallback, this));
-  ASSERT_HRESULT_SUCCEEDED(registry_monitor.StartMonitoring());
-
-  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(kKeyNameFull,
-                                            kValueName,
-                                            _T("foo")));
-  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(registry_changed_event_),
-                                                 kWaitForChangeMs));
-}
-
-// Tests changing the same value two times. This is useful to detect if
-// the key is registered back for notification after a succesful callback.
-TEST_F(RegistryMonitorTest, ChangeValues) {
-  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(kKeyNameFull,
-                                            kValueName,
-                                            _T("")));
-
-  RegistryMonitor registry_monitor;
-  ASSERT_HRESULT_SUCCEEDED(registry_monitor.Initialize());
-  ASSERT_HRESULT_SUCCEEDED(registry_monitor.MonitorValue(
-      HKEY_CURRENT_USER, kKeyName, kValueName, REG_SZ,
-      RegistryChangesCallback, this));
-  ASSERT_HRESULT_SUCCEEDED(registry_monitor.StartMonitoring());
-
-  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(kKeyNameFull,
-                                            kValueName,
-                                            _T("foo")));
-  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(registry_changed_event_),
-                                                 kWaitForChangeMs));
-  EXPECT_TRUE(::ResetEvent(get(registry_changed_event_)));
-
-  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(kKeyNameFull,
-                                            kValueName,
-                                            _T("bar")));
-  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(registry_changed_event_),
-                                                 kWaitForChangeMs));
-}
-
-TEST_F(RegistryMonitorTest, CreateValue) {
-  RegistryMonitor registry_monitor;
-  ASSERT_HRESULT_SUCCEEDED(registry_monitor.Initialize());
-  ASSERT_HRESULT_SUCCEEDED(RegKey::CreateKey(kKeyNameFull));
-  ASSERT_HRESULT_SUCCEEDED(registry_monitor.MonitorValue(
-      HKEY_CURRENT_USER, kKeyName, kValueName, REG_DWORD,
-      RegistryCreateCallback, this));
-  ASSERT_HRESULT_SUCCEEDED(registry_monitor.StartMonitoring());
-
-  DWORD value = 1;
-  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(kKeyNameFull, kValueName, value));
-  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(registry_changed_event_),
-                                                 kWaitForChangeMs));
-}
-
-// Monitoring values under the same key pair is allowed.
-TEST_F(RegistryMonitorTest, MonitorSame) {
-  RegistryMonitor registry_monitor;
-  ASSERT_HRESULT_SUCCEEDED(registry_monitor.Initialize());
-  ASSERT_HRESULT_SUCCEEDED(RegKey::CreateKey(kKeyNameFull));
-  ASSERT_HRESULT_SUCCEEDED(registry_monitor.MonitorValue(
-      HKEY_CURRENT_USER, kKeyName, kValueName, REG_DWORD,
-      RegistryCreateCallback, this));
-  ASSERT_HRESULT_SUCCEEDED(registry_monitor.MonitorValue(
-      HKEY_CURRENT_USER, kKeyName, kValueName, REG_DWORD,
-      RegistryCreateCallback, this));
-}
-
-TEST_F(RegistryMonitorTest, MonitorKey) {
-  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey(kKeyNameFull));
-
-  RegistryMonitor registry_monitor;
-  EXPECT_HRESULT_SUCCEEDED(registry_monitor.Initialize());
-  EXPECT_HRESULT_SUCCEEDED(registry_monitor.MonitorKey(
-      HKEY_CURRENT_USER, kKeyName, RegistryKeyCallback, this));
-
-  EXPECT_HRESULT_SUCCEEDED(registry_monitor.StartMonitoring());
-
-  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey(_T("HKCU\\key\\subkey")));
-  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(registry_changed_event_),
-                                                 kWaitForChangeMs));
-
-  EXPECT_TRUE(::ResetEvent(get(registry_changed_event_)));
-  EXPECT_HRESULT_SUCCEEDED(RegKey::DeleteKey(_T("HKCU\\key\\subkey")));
-  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(registry_changed_event_),
-                                                 kWaitForChangeMs));
-}
-
-}  // namespace omaha
-
diff --git a/common/registry_store.cc b/common/registry_store.cc
deleted file mode 100644
index dca1d63..0000000
--- a/common/registry_store.cc
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/registry_store.h"
-#include <vector>
-#include "omaha/common/debug.h"
-#include "omaha/common/reg_key.h"
-
-namespace omaha {
-
-bool RegistryStore::Open(const TCHAR* key_path) {
-  key_path_ = key_path;
-  return true;
-}
-
-bool RegistryStore::Close() {
-  key_path_.Empty();
-  return true;
-}
-
-bool RegistryStore::Clear() {
-  if (RegKey::HasKey(key_path_)) {
-    return SUCCEEDED(RegKey::DeleteKey(key_path_, false));
-  } else {
-    return true;
-  }
-}
-
-bool RegistryStore::Read(const TCHAR* name, std::vector<byte>* data) const {
-  ASSERT1(name);
-  ASSERT1(data);
-
-  byte* sdata = NULL;
-  DWORD sdata_size = 0;
-  HRESULT hr = RegKey::GetValue(key_path_, name, &sdata, &sdata_size);
-  if (FAILED(hr) || !sdata || !sdata_size)
-    return false;
-
-  data->resize(sdata_size);
-  memcpy(&data->front(), sdata, sdata_size);
-
-  delete[] sdata;
-
-  return true;
-}
-
-bool RegistryStore::Write(const TCHAR* name, byte* data, int data_size) {
-  ASSERT1(name);
-  ASSERT1(data);
-  ASSERT1(data_size);
-
-  return SUCCEEDED(RegKey::SetValue(key_path_, name, data, data_size));
-}
-
-bool RegistryStore::Exists(const TCHAR* name) {
-  ASSERT1(name);
-
-  return RegKey::HasValue(key_path_, name);
-}
-
-bool RegistryStore::Remove(const TCHAR* name) {
-  ASSERT1(name);
-
-  return SUCCEEDED(RegKey::DeleteValue(key_path_, name));
-}
-
-bool RegistryStore::GetValueCount(uint32* value_count) {
-  ASSERT1(value_count);
-
-  CString key_name(key_path_);
-  HKEY h_key = RegKey::GetRootKeyInfo(&key_name);
-
-  RegKey reg_key;
-  if (FAILED(reg_key.Open(h_key, key_name.GetString(), KEY_READ)))
-    return false;
-
-  *value_count = reg_key.GetValueCount();
-
-  reg_key.Close();
-
-  return true;
-}
-
-bool RegistryStore::GetValueNameAt(int index, CString* value_name) {
-  ASSERT1(index >= 0);
-  ASSERT1(value_name);
-
-  CString key_name(key_path_);
-  HKEY h_key = RegKey::GetRootKeyInfo(&key_name);
-
-  RegKey reg_key;
-  if (FAILED(reg_key.Open(h_key, key_name.GetString(), KEY_READ)))
-    return false;
-
-  HRESULT hr = reg_key.GetValueNameAt(index, value_name, NULL);
-
-  reg_key.Close();
-
-  return SUCCEEDED(hr);
-}
-
-}  // namespace omaha
-
diff --git a/common/registry_store_unittest.cc b/common/registry_store_unittest.cc
deleted file mode 100644
index c75de3a..0000000
--- a/common/registry_store_unittest.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// Unit test for RegistryStore.
-
-#include "omaha/common/registry_store.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-static const TCHAR kRSTestKey[] =
-    _T("HKCU\\Software\\Google\\Common_Installer__TEST_STORE");
-static const TCHAR kRSTestName[] = _T("TestValueName");
-static const byte kRSTestValue[] = {0x01, 0x02, 0x03, 0x04, 0x05};
-static const int kRSTestValueSize = arraysize(kRSTestValue);
-
-TEST(RegistryStoreTest, RegistryStore) {
-  RegistryStore registry_store;
-  uint32 value_count = 42; // We want to make sure it's overwritten with 0.
-  CString value_name;
-  std::vector<byte> data;
-
-  // Set up and get in a known state.
-  EXPECT_TRUE(registry_store.Open(kRSTestKey));
-  EXPECT_TRUE(registry_store.Clear());
-
-  // Add and test a single value
-  EXPECT_FALSE(registry_store.Exists(kRSTestName));
-  EXPECT_FALSE(registry_store.Read(kRSTestName, &data));
-  data.clear();
-
-  EXPECT_TRUE(registry_store.Write(kRSTestName,
-                                   const_cast<byte*>(kRSTestValue),
-                                   kRSTestValueSize));
-
-  EXPECT_TRUE(registry_store.Exists(kRSTestName));
-  EXPECT_TRUE(registry_store.Read(kRSTestName, &data));
-  EXPECT_EQ(data.size(), kRSTestValueSize);
-  for (int i = 0; i < kRSTestValueSize; i++)
-    EXPECT_EQ(data[i], kRSTestValue[i]);
-
-  // Remove and re-add value
-  EXPECT_TRUE(registry_store.Remove(kRSTestName));
-  EXPECT_FALSE(registry_store.Exists(kRSTestName));
-  EXPECT_TRUE(registry_store.GetValueCount(&value_count));
-  EXPECT_EQ(value_count, 0);
-  EXPECT_TRUE(registry_store.Write(kRSTestName,
-                                   const_cast<byte*>(kRSTestValue),
-                                   kRSTestValueSize));
-  EXPECT_TRUE(registry_store.GetValueCount(&value_count));
-  EXPECT_EQ(value_count, 1);
-  EXPECT_TRUE(registry_store.GetValueNameAt(0, &value_name));
-  EXPECT_TRUE(value_name == kRSTestName);
-
-  // Clean up and finish.
-  EXPECT_TRUE(registry_store.Clear());
-  EXPECT_FALSE(registry_store.Exists(kRSTestName));
-  EXPECT_FALSE(registry_store.GetValueCount(&value_count));
-  EXPECT_TRUE(registry_store.Close());
-}
-
-}  // namespace omaha
diff --git a/common/run_as_invoker.rc b/common/run_as_invoker.rc
deleted file mode 100644
index 4ab0dfb..0000000
--- a/common/run_as_invoker.rc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "afxres.h"
-
-LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
-#pragma code_page(1252)
-
-1 RT_MANIFEST "run_as_invoker.manifest"
diff --git a/common/run_as_invoker.res b/common/run_as_invoker.res
deleted file mode 100644
index 2d02c93..0000000
--- a/common/run_as_invoker.res
+++ /dev/null
Binary files differ
diff --git a/common/scheduled_task_utils.cc b/common/scheduled_task_utils.cc
new file mode 100644
index 0000000..f922f13
--- /dev/null
+++ b/common/scheduled_task_utils.cc
@@ -0,0 +1,1076 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "omaha/common/scheduled_task_utils.h"
+#include "omaha/common/scheduled_task_utils_internal.h"
+#include <corerror.h>
+#include <lmcons.h>
+#include <lmsname.h>
+#include <mstask.h>
+#include <atlsecurity.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_ptr_cotask.h"
+#include "omaha/base/service_utils.h"
+#include "omaha/base/string.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/time.h"
+#include "omaha/base/timer.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/client/resource.h"
+#include "omaha/common/command_line_builder.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
+
+namespace omaha {
+
+namespace scheduled_task_utils {
+
+namespace internal {
+
+CString GetCurrentTaskNameCore(bool is_machine) {
+  UTIL_LOG(L3, (_T("[GetCurrentTaskNameCore][%d]"), is_machine));
+
+  CString default_name(GetDefaultGoopdateTaskName(is_machine,
+                                                  COMMANDLINE_MODE_CORE));
+  return goopdate_utils::GetCurrentVersionedName(is_machine,
+                                                 kRegValueTaskNameC,
+                                                 default_name);
+}
+
+HRESULT CreateAndSetVersionedTaskNameCoreInRegistry(
+    bool is_machine) {
+  UTIL_LOG(L3, (_T("[CreateAndSetVersionedTaskNameCoreInRegistry][%d]"),
+                is_machine));
+
+  CString default_name(GetDefaultGoopdateTaskName(is_machine,
+                                                  COMMANDLINE_MODE_CORE));
+  return goopdate_utils::CreateAndSetVersionedNameInRegistry(
+             is_machine,
+             default_name,
+             kRegValueTaskNameC);
+}
+
+CString GetCurrentTaskNameUA(bool is_machine) {
+  UTIL_LOG(L3, (_T("[GetCurrentTaskNameUA][%d]"), is_machine));
+
+  CString default_name(GetDefaultGoopdateTaskName(is_machine,
+                                                  COMMANDLINE_MODE_UA));
+  return goopdate_utils::GetCurrentVersionedName(is_machine,
+                                                 kRegValueTaskNameUA,
+                                                 default_name);
+}
+
+HRESULT CreateAndSetVersionedTaskNameUAInRegistry(bool machine) {
+  UTIL_LOG(L3, (_T("[CreateAndSetVersionedTaskNameUAInRegistry][%d]"),
+                machine));
+
+  CString default_name(GetDefaultGoopdateTaskName(machine,
+                                                  COMMANDLINE_MODE_UA));
+  return goopdate_utils::CreateAndSetVersionedNameInRegistry(
+             machine,
+             default_name,
+             kRegValueTaskNameUA);
+}
+
+bool IsInstalledScheduledTask(const TCHAR* task_name) {
+  ASSERT1(task_name && *task_name);
+
+  CComPtr<ITaskScheduler> scheduler;
+  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
+                                          NULL,
+                                          CLSCTX_INPROC_SERVER);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr));
+    return false;
+  }
+
+  CComPtr<ITask> task;
+  hr = scheduler->Activate(task_name,
+                           __uuidof(ITask),
+                           reinterpret_cast<IUnknown**>(&task));
+
+  UTIL_LOG(L3, (_T("[IsInstalledScheduledTask returned][0x%x]"), hr));
+  return COR_E_FILENOTFOUND == hr ? false : true;
+}
+
+DWORD GetScheduledTaskPriority(const TCHAR* task_name) {
+  ASSERT1(task_name && *task_name);
+
+  CComPtr<ITaskScheduler> scheduler;
+  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
+                                          NULL,
+                                          CLSCTX_INPROC_SERVER);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr));
+    return 0;
+  }
+
+  CComPtr<ITask> task;
+  hr = scheduler->Activate(task_name,
+                           __uuidof(ITask),
+                           reinterpret_cast<IUnknown**>(&task));
+
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[GetScheduledTaskPriority][Activate failed][0x%x]"), hr));
+    return 0;
+  }
+
+  DWORD priority = 0;
+  hr = task->GetPriority(&priority);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITask.GetMostRecentRunTime failed][0x%x]"), hr));
+    return 0;
+  }
+
+  ASSERT1(priority);
+  return priority;
+}
+
+bool HasScheduledTaskEverRun(const TCHAR* task_name) {
+  ASSERT1(task_name && *task_name);
+
+  CComPtr<ITaskScheduler> scheduler;
+  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
+                                          NULL,
+                                          CLSCTX_INPROC_SERVER);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr));
+    return false;
+  }
+
+  CComPtr<ITask> task;
+  hr = scheduler->Activate(task_name,
+                           __uuidof(ITask),
+                           reinterpret_cast<IUnknown**>(&task));
+
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[HasScheduledTaskEverRun][Activate failed][0x%x]"), hr));
+    return false;
+  }
+
+  SYSTEMTIME recent_run_time = {0};
+  hr = task->GetMostRecentRunTime(&recent_run_time);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITask.GetMostRecentRunTime failed][0x%x]"), hr));
+    return false;
+  }
+
+  // hr == SCHED_S_TASK_HAS_NOT_RUN if the task has never run.
+  return hr == S_OK;
+}
+
+HRESULT GetScheduledTaskStatus(const TCHAR* task_name) {
+  ASSERT1(task_name && *task_name);
+
+  CComPtr<ITaskScheduler> scheduler;
+  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
+                                          NULL,
+                                          CLSCTX_INPROC_SERVER);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<ITask> task;
+  hr = scheduler->Activate(task_name,
+                           __uuidof(ITask),
+                           reinterpret_cast<IUnknown**>(&task));
+
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[GetScheduledTaskStatus: Activate failed][0x%x]"), hr));
+    return hr;
+  }
+
+  HRESULT task_status(S_OK);
+  hr = task->GetStatus(&task_status);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITask.GetStatus failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return task_status;
+}
+
+HRESULT GetScheduledTaskExitCode(const TCHAR* task_name) {
+  ASSERT1(task_name && *task_name);
+
+  CComPtr<ITaskScheduler> scheduler;
+  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
+                                          NULL,
+                                          CLSCTX_INPROC_SERVER);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<ITask> task;
+  hr = scheduler->Activate(task_name,
+                           __uuidof(ITask),
+                           reinterpret_cast<IUnknown**>(&task));
+
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITask.Activate failed][0x%x]"), hr));
+    return hr;
+  }
+
+  DWORD exit_code(0);
+  hr = task->GetExitCode(&exit_code);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITask.GetExitCode failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return hr == SCHED_S_TASK_HAS_NOT_RUN ? hr : exit_code;
+}
+
+HRESULT StartScheduledTask(const TCHAR* task_name) {
+  ASSERT1(task_name && *task_name);
+
+  if (v2::IsTaskScheduler2APIAvailable()) {
+    return v2::StartScheduledTask(task_name);
+  }
+
+  if (GetScheduledTaskStatus(task_name) == SCHED_S_TASK_RUNNING) {
+    return S_OK;
+  }
+
+  CComPtr<ITaskScheduler> scheduler;
+  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
+                                          NULL,
+                                          CLSCTX_INPROC_SERVER);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<ITask> task;
+  hr = scheduler->Activate(task_name,
+                           __uuidof(ITask),
+                           reinterpret_cast<IUnknown**>(&task));
+
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskScheduler.Activate failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = task->Run();
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITask.Run failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return hr;
+}
+
+HRESULT StopScheduledTask(const TCHAR* task_name) {
+  ASSERT1(task_name && *task_name);
+
+  if (v2::IsTaskScheduler2APIAvailable()) {
+    return v2::StopScheduledTask(task_name);
+  }
+
+  if (GetScheduledTaskStatus(task_name) != SCHED_S_TASK_RUNNING) {
+    return S_OK;
+  }
+
+  CComPtr<ITaskScheduler> scheduler;
+  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
+                                          NULL,
+                                          CLSCTX_INPROC_SERVER);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<ITask> task;
+  hr = scheduler->Activate(task_name,
+                           __uuidof(ITask),
+                           reinterpret_cast<IUnknown**>(&task));
+
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskScheduler.Activate failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = task->Terminate();
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITask.Run failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return hr;
+}
+
+HRESULT CreateLogonTrigger(ITask* task) {
+  ASSERT1(task);
+
+  CComPtr<ITaskTrigger> trigger;
+  WORD index = 0;
+
+  // Create a trigger to run on every user logon.
+  HRESULT hr = task->CreateTrigger(&index, &trigger);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITask.CreateTrigger failed][0x%x]"), hr));
+    return hr;
+  }
+
+  TASK_TRIGGER trigger_config = {0};
+  trigger_config.cbTriggerSize = sizeof(trigger_config);
+  // These are required parameters. A past start date is good.
+  trigger_config.wBeginDay = 1;
+  trigger_config.wBeginMonth = 1;
+  trigger_config.wBeginYear = 1999;
+
+  // Run on every user logon.
+  trigger_config.TriggerType = TASK_EVENT_TRIGGER_AT_LOGON;
+
+  hr = trigger->SetTrigger(&trigger_config);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskTrigger.SetTrigger failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT CreatePeriodicTrigger(ITask* task, bool create_hourly_trigger) {
+  ASSERT1(task);
+
+  CComPtr<ITaskTrigger> trigger;
+  WORD index = 0;
+
+  // Create a trigger to run every day.
+  HRESULT hr = task->CreateTrigger(&index, &trigger);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITask.CreateTrigger failed][0x%x]"), hr));
+    return hr;
+  }
+
+  // Start time set to 5 minutes from the current time.
+  time64 start_time = GetCurrent100NSTime() + (5 * kMinsTo100ns);
+  SYSTEMTIME sys_time = Time64ToSystemTime(start_time);
+  SYSTEMTIME locale_time = {0};
+  hr = SystemTimeToTzSpecificLocalTime(NULL, &sys_time, &locale_time);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[SystemTimeToTzSpecificLocalTime failed][0x%x]"), hr));
+    return hr;
+  }
+
+  TASK_TRIGGER trigger_config = {0};
+  trigger_config.cbTriggerSize = sizeof(trigger_config);
+  trigger_config.wBeginYear = locale_time.wYear;
+  trigger_config.wBeginMonth = locale_time.wMonth;
+  trigger_config.wBeginDay = locale_time.wDay;
+  trigger_config.wStartHour = locale_time.wHour;
+  trigger_config.wStartMinute = locale_time.wMinute;
+
+  trigger_config.TriggerType = TASK_TIME_TRIGGER_DAILY;
+  trigger_config.Type.Daily.DaysInterval = 1;  // every 1 day
+
+  if (create_hourly_trigger) {
+    // The task will be run daily at 24 hour intervals. And the task will be
+    // repeated every au_timer_interval_minutes within a single 24 hour
+    // interval.
+    const DWORD kTaskTrigger24HoursDuration = 24 * 60;
+    int au_timer_interval_minutes =
+        ConfigManager::Instance()->GetAutoUpdateTimerIntervalMs() / (60 * 1000);
+    ASSERT1(au_timer_interval_minutes > 0 &&
+            au_timer_interval_minutes < kTaskTrigger24HoursDuration);
+
+    trigger_config.MinutesDuration = kTaskTrigger24HoursDuration;
+    trigger_config.MinutesInterval = au_timer_interval_minutes;
+  }
+
+  hr = trigger->SetTrigger(&trigger_config);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskTrigger.SetTrigger failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT CreateScheduledTask(ITask* task,
+                            const TCHAR* task_path,
+                            const TCHAR* task_parameters,
+                            const TCHAR* task_comment,
+                            bool is_machine,
+                            bool create_logon_trigger,
+                            bool create_daily_trigger,
+                            bool create_hourly_trigger) {
+  ASSERT1(task);
+  ASSERT1(task_path && *task_path);
+  ASSERT1(task_parameters);
+  ASSERT1(task_comment && *task_comment);
+  ASSERT1(create_logon_trigger || create_daily_trigger);
+  ASSERT1(!create_logon_trigger || (create_logon_trigger && is_machine));
+  ASSERT1(!create_hourly_trigger ||
+          (create_hourly_trigger && create_daily_trigger));
+
+  UTIL_LOG(L3, (_T("[CreateScheduledTask][%s][%s][%d]"),
+                task_path, task_parameters, is_machine));
+
+  HRESULT hr = task->SetApplicationName(task_path);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITask.SetApplicationName failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = task->SetParameters(task_parameters);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITask.SetParameters failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = task->SetComment(task_comment);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITask.SetComment failed][0x%x]"), hr));
+    return hr;
+  }
+
+  if (is_machine) {
+    // Run using SYSTEM credentials, by passing in an empty username string.
+    hr = task->SetAccountInformation(_T(""), NULL);
+  } else {
+    // Run as current user.
+    // For the user task, we set TASK_FLAG_RUN_ONLY_IF_LOGGED_ON, so that we do
+    // not need the user password for task creation.
+    hr = task->SetFlags(TASK_FLAG_RUN_ONLY_IF_LOGGED_ON);
+    if (FAILED(hr)) {
+      UTIL_LOG(LE, (_T("[ITask.SetFlags failed][0x%x]"), hr));
+      return hr;
+    }
+
+    CString user_name;
+    DWORD buffer_size = UNLEN + 1;
+    if (!::GetUserName(CStrBuf(user_name, buffer_size), &buffer_size)) {
+      hr = HRESULTFromLastError();
+      UTIL_LOG(LE, (_T("[::GetUserName failed][0x%x]"), hr));
+      return hr;
+    }
+    hr = task->SetAccountInformation(user_name, NULL);
+  }
+
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITask.SetAccountInformation failed][0x%x]"), hr));
+    return hr;
+  }
+
+  // The default is to run for a finite number of days. We want to run
+  // indefinitely.
+  // Due to a bug introduced in Vista, and propogated to Windows 7, setting the
+  // MaxRunTime to INFINITE results in the task only running for 72 hours. For
+  // these operating systems, setting the RunTime to "INFINITE - 1" gets the
+  // desired behavior of allowing an "infinite" run of the task.
+  DWORD max_time = INFINITE - (SystemInfo::IsRunningOnVistaOrLater() ? 1 : 0);
+  hr = task->SetMaxRunTime(max_time);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITask.SetMaxRunTime failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<ITaskTrigger> trigger;
+  WORD index = 0;
+
+  if (create_logon_trigger && is_machine) {
+    // Create a trigger to run on every user logon. Non-admin users are not able
+    // to create logon triggers, so we create only for machine.
+    hr = CreateLogonTrigger(task);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (create_daily_trigger) {
+    hr = CreatePeriodicTrigger(task, create_hourly_trigger);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  // Save task.
+  CComQIPtr<IPersistFile> persist(task);
+  if (!persist) {
+    hr = E_NOINTERFACE;
+    UTIL_LOG(LE, (_T("[ITask.QueryInterface IPersistFile failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = persist->Save(NULL, TRUE);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[IPersistFile.Save failed][0x%x]"), hr));
+    return hr;
+  }
+
+  if (is_machine) {
+    return S_OK;
+  }
+
+  // Adjust privileges to explicitly allow the current user to be able to
+  // manipulate this task. User applications, and consequently, Omaha, can be
+  // installed in an elevated mode. This can happen, for instance, if the user
+  // installs on XP, then upgrades to Vista. Or chooses "Run as Administrator"
+  // when running the meta-installer on Vista. Subsequently, Omaha running at
+  // medium integrity needs to be able to manipulate the installed task.
+  scoped_ptr_cotask<OLECHAR> job_file;
+  hr = persist->GetCurFile(address(job_file));
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[IPersistFile.GetCurFile failed][0x%x]"), hr));
+    return hr;
+  }
+
+  persist.Release();
+
+  CAccessToken token;
+  CSid current_sid;
+  if (!token.GetEffectiveToken(TOKEN_QUERY) || !token.GetUser(&current_sid)) {
+    hr = HRESULTFromLastError();
+    UTIL_LOG(LE, (_T("[Failed to get current user sid][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = AddAllowedAce(job_file.get(),
+                     SE_FILE_OBJECT,
+                     current_sid,
+                     FILE_ALL_ACCESS,
+                     0);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[Could not adjust DACL][%s][0x%x]"), job_file.get(), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT UpgradeScheduledTask(const TCHAR* task_name,
+                             const TCHAR* task_path,
+                             const TCHAR* task_parameters,
+                             const TCHAR* task_comment,
+                             bool is_machine,
+                             bool create_logon_trigger,
+                             bool create_daily_trigger,
+                             bool create_hourly_trigger) {
+  ASSERT1(task_name && *task_name);
+  ASSERT1(IsInstalledScheduledTask(task_name));
+
+  UTIL_LOG(L3, (_T("[UpgradeScheduledTask][%s][%s][%s][%d]"),
+                task_name, task_path, task_parameters, is_machine));
+
+  // TODO(Omaha): Perhaps pass the ITaskScheduler around where possible.
+  CComPtr<ITaskScheduler> scheduler;
+  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
+                                          NULL,
+                                          CLSCTX_INPROC_SERVER);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<ITask> task;
+  hr = scheduler->Activate(task_name,
+                           __uuidof(ITask),
+                           reinterpret_cast<IUnknown**>(&task));
+
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[UpgradeScheduledTask][Activate failed][0x%x]"), hr));
+    return hr;
+  }
+
+  // Delete existing triggers. CreateScheduledTask() will recreate them anew.
+  WORD trigger_count(0);
+  hr = task->GetTriggerCount(&trigger_count);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskScheduler.GetTriggerCount failed][0x%x]"), hr));
+    return hr;
+  }
+
+  for (int i = 0; i < trigger_count; ++i) {
+    hr = task->DeleteTrigger(0);
+    if (FAILED(hr)) {
+      UTIL_LOG(LE, (_T("[ITaskScheduler.DeleteTrigger failed][0x%x]"), hr));
+      return hr;
+    }
+  }
+
+  return CreateScheduledTask(task,
+                             task_path,
+                             task_parameters,
+                             task_comment,
+                             is_machine,
+                             create_logon_trigger,
+                             create_daily_trigger,
+                             create_hourly_trigger);
+}
+
+// TODO(Omaha): Change the apis to avoid specifying hourly and daily triggers.
+HRESULT InstallScheduledTask(const TCHAR* task_name,
+                             const TCHAR* task_path,
+                             const TCHAR* task_parameters,
+                             const TCHAR* task_comment,
+                             bool is_machine,
+                             bool create_logon_trigger,
+                             bool create_daily_trigger,
+                             bool create_hourly_trigger) {
+  if (IsInstalledScheduledTask(task_name)) {
+    return UpgradeScheduledTask(task_name,
+                                task_path,
+                                task_parameters,
+                                task_comment,
+                                is_machine,
+                                create_logon_trigger,
+                                create_daily_trigger,
+                                create_hourly_trigger);
+  }
+
+  CComPtr<ITaskScheduler> scheduler;
+  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
+                                          NULL,
+                                          CLSCTX_INPROC_SERVER);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<ITask> task;
+  hr = scheduler->NewWorkItem(task_name,
+                              CLSID_CTask,
+                              __uuidof(ITask),
+                              reinterpret_cast<IUnknown**>(&task));
+
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskScheduler.NewWorkItem failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return CreateScheduledTask(task,
+                             task_path,
+                             task_parameters,
+                             task_comment,
+                             is_machine,
+                             create_logon_trigger,
+                             create_daily_trigger,
+                             create_hourly_trigger);
+}
+
+HRESULT UninstallScheduledTask(const TCHAR* task_name) {
+  ASSERT1(task_name && *task_name);
+
+  CComPtr<ITaskScheduler> scheduler;
+  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
+                                          NULL,
+                                          CLSCTX_INPROC_SERVER);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr));
+    return hr;
+  }
+
+  // Stop the task before deleting it. Ignore return value.
+  VERIFY1(SUCCEEDED(StopScheduledTask(task_name)));
+
+  // delete the task.
+  hr = scheduler->Delete(task_name);
+  if (FAILED(hr) && COR_E_FILENOTFOUND != hr) {
+    UTIL_LOG(LE, (_T("[GetScheduledTaskStatus][Delete failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT UninstallScheduledTasks(const TCHAR* task_prefix) {
+  ASSERT1(task_prefix && *task_prefix);
+
+  CComPtr<ITaskScheduler> scheduler;
+  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
+                                          NULL,
+                                          CLSCTX_INPROC_SERVER);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskScheduler.CoCreateInstance failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<IEnumWorkItems> enum_items;
+  hr = scheduler->Enum(&enum_items);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskScheduler.Enum failed][0x%x]"), hr));
+    return hr;
+  }
+
+  TCHAR** task_names = NULL;
+  DWORD task_count = 0;
+  while (enum_items->Next(1, &task_names, &task_count) == S_OK) {
+    ASSERT1(task_count == 1);
+    scoped_co_task_ptr task_names_guard(task_names);
+    scoped_co_task_ptr task_name_guard(task_names[0]);
+
+    if (String_StartsWith(task_names[0], task_prefix, true)) {
+      UninstallScheduledTask(task_names[0]);
+    }
+  }
+
+  return S_OK;
+}
+
+// Returns the task name Omaha used to install in Omaha 1.2.x.
+CString GetOmaha1LegacyTaskName(bool is_machine) {
+  const TCHAR* const kLegacyOmaha1TaskNameMachine = _T("GoogleUpdateTask");
+  const TCHAR* const kLegacyOmaha1TaskNameUser = _T("GoogleUpdateTaskUser");
+  return is_machine ? kLegacyOmaha1TaskNameMachine : kLegacyOmaha1TaskNameUser;
+}
+
+// Returns the task name Omaha used to install in Omaha 2 before the
+// "GoogleUpdate.exe does not run all the time" refactoring.
+CString GetOmaha2LegacyTaskName(bool is_machine) {
+  const TCHAR* kLegacyOmaha2TaskNameUserPrefix = _T("GoogleUpdateTaskUser");
+  const TCHAR* kLegacyOmaha2TaskNameMachine = _T("GoogleUpdateTaskMachine");
+  if (is_machine) {
+    return kLegacyOmaha2TaskNameMachine;
+  }
+
+  CString task_name_user = kLegacyOmaha2TaskNameUserPrefix;
+  CString user_sid;
+  VERIFY1(SUCCEEDED(user_info::GetProcessUser(NULL, NULL, &user_sid)));
+  task_name_user += user_sid;
+  return task_name_user;
+}
+
+HRESULT WaitForTaskStatus(const TCHAR* task_name, HRESULT status, int time_ms) {
+  int kSleepBetweenRetriesMs = 100;
+  Timer timer(true);
+  while (timer.GetMilliseconds() < time_ms) {
+    if (GetScheduledTaskStatus(task_name) == status) {
+      return status;
+    }
+    ::Sleep(kSleepBetweenRetriesMs);
+  }
+  return GetScheduledTaskStatus(task_name);
+}
+
+namespace v2 {
+
+bool IsTaskScheduler2APIAvailable() {
+  CComPtr<ITaskService> task_service;
+  return SUCCEEDED(task_service.CoCreateInstance(CLSID_TaskScheduler,
+                                                 NULL,
+                                                 CLSCTX_INPROC_SERVER));
+}
+
+HRESULT GetRegisteredTask(const TCHAR* task_name, IRegisteredTask** task) {
+  ASSERT1(IsTaskScheduler2APIAvailable());
+  ASSERT1(task_name && *task_name);
+  ASSERT1(task);
+
+  CComPtr<ITaskService> task_service;
+  HRESULT hr = task_service.CoCreateInstance(CLSID_TaskScheduler,
+                                             NULL,
+                                             CLSCTX_INPROC_SERVER);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskService.CoCreateInstance failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = task_service->Connect(CComVariant(), CComVariant(),
+                             CComVariant(), CComVariant());
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[ITaskService::Connect failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<ITaskFolder> task_folder;
+  hr = task_service->GetFolder(CComBSTR(_T("\\")) , &task_folder);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[Cannot get Root Folder pointer][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<IRegisteredTask> registered_task;
+  hr = task_folder->GetTask(CComBSTR(task_name), &registered_task);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[Cannot get the registered task][0x%x]"), hr));
+    return hr;
+  }
+
+  *task = registered_task.Detach();
+  return S_OK;
+}
+
+bool IsScheduledTaskRunning(const TCHAR* task_name) {
+  ASSERT1(IsTaskScheduler2APIAvailable());
+  ASSERT1(task_name && *task_name);
+
+  CComPtr<IRegisteredTask> registered_task;
+  HRESULT hr = GetRegisteredTask(task_name, &registered_task);
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  CComPtr<IRunningTaskCollection> running_task_collection;
+  hr = registered_task->GetInstances(0, &running_task_collection);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[IRegisteredTask.GetInstances failed][0x%x]"), hr));
+    return false;
+  }
+
+  long count = 0;
+  hr = running_task_collection->get_Count(&count);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[IRunningTaskCollection.get_Count failed][0x%x]"), hr));
+    return false;
+  }
+
+  return count > 0;
+}
+
+HRESULT StartScheduledTask(const TCHAR* task_name) {
+  ASSERT1(IsTaskScheduler2APIAvailable());
+  ASSERT1(task_name && *task_name);
+
+  if (IsScheduledTaskRunning(task_name)) {
+    return S_OK;
+  }
+
+  CComPtr<IRegisteredTask> registered_task;
+  HRESULT hr = GetRegisteredTask(task_name, &registered_task);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = registered_task->Run(CComVariant(), NULL);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[IRegisteredTask.Run failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return hr;
+}
+
+HRESULT StopScheduledTask(const TCHAR* task_name) {
+  ASSERT1(IsTaskScheduler2APIAvailable());
+  ASSERT1(task_name && *task_name);
+
+  if (!IsScheduledTaskRunning(task_name)) {
+    return S_OK;
+  }
+
+  CComPtr<IRegisteredTask> registered_task;
+  HRESULT hr = GetRegisteredTask(task_name, &registered_task);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CComPtr<IRunningTaskCollection> running_task_collection;
+  hr = registered_task->GetInstances(0, &running_task_collection);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[IRegisteredTask.GetInstances failed][0x%x]"), hr));
+    return hr;
+  }
+
+  long count = 0;
+  hr = running_task_collection->get_Count(&count);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[IRunningTaskCollection.get_Count failed][0x%x]"), hr));
+    return hr;
+  }
+
+  if (count <= 0) {
+    return S_OK;
+  }
+
+  for (long i = 0; i < count; ++i) {
+    CComPtr<IRunningTask> running_task;
+    hr = running_task_collection->get_Item(CComVariant(i+1), &running_task);
+    if (FAILED(hr)) {
+      UTIL_LOG(LE, (_T("[IRunningTaskCollection.get_Item][%d][0x%x]"), i, hr));
+      return hr;
+    }
+
+    hr = running_task->Stop();
+    if (FAILED(hr)) {
+      UTIL_LOG(LE, (_T("[IRunningTask.Stop failed][%d][0x%x]"), i, hr));
+      return hr;
+    }
+  }
+
+  return S_OK;
+}
+
+}  // namespace v2
+
+}  // namespace internal
+
+CString GetDefaultGoopdateTaskName(bool is_machine, CommandLineMode mode) {
+  ASSERT1(mode == COMMANDLINE_MODE_CORE || mode == COMMANDLINE_MODE_UA);
+
+  CString task_name;
+  if (is_machine) {
+    task_name = kScheduledTaskNameMachinePrefix;
+  } else {
+    task_name = kScheduledTaskNameUserPrefix;
+    CString user_sid;
+    VERIFY1(SUCCEEDED(user_info::GetProcessUser(NULL, NULL, &user_sid)));
+    task_name += user_sid;
+  }
+
+  task_name += (mode == COMMANDLINE_MODE_CORE) ? kScheduledTaskNameCoreSuffix :
+                                                 kScheduledTaskNameUASuffix;
+  return task_name;
+}
+
+HRESULT InstallGoopdateTaskForMode(const TCHAR* task_path,
+                                   bool is_machine,
+                                   CommandLineMode mode) {
+  ASSERT1(mode == COMMANDLINE_MODE_CORE || mode == COMMANDLINE_MODE_UA);
+
+  CommandLineBuilder builder(mode);
+  if (mode == COMMANDLINE_MODE_UA) {
+    builder.set_install_source(kCmdLineInstallSource_Scheduler);
+  }
+  const CString task_parameters = builder.GetCommandLineArgs();
+
+  CString company_name;
+  VERIFY1(company_name.LoadString(IDS_FRIENDLY_COMPANY_NAME));
+  CString task_description;
+  task_description.FormatMessage(IDS_SCHEDULED_TASK_DESCRIPTION, company_name);
+
+  CString task_name(mode == COMMANDLINE_MODE_CORE ?
+                    internal::GetCurrentTaskNameCore(is_machine) :
+                    internal::GetCurrentTaskNameUA(is_machine));
+  if (internal::IsInstalledScheduledTask(task_name)) {
+    HRESULT hr = internal::InstallScheduledTask(task_name,
+                                                task_path,
+                                                task_parameters,
+                                                task_description,
+                                                is_machine,
+                                                mode == COMMANDLINE_MODE_CORE &&
+                                                is_machine,
+                                                true,
+                                                mode == COMMANDLINE_MODE_UA);
+
+    if (SUCCEEDED(hr)) {
+      return hr;
+    }
+
+    // Try to uninstall the task that we failed to upgrade. Then create a new
+    // task name, and fall through to install that.
+    internal::UninstallScheduledTask(task_name);
+    if (mode == COMMANDLINE_MODE_CORE) {
+      VERIFY1(SUCCEEDED(
+      internal::CreateAndSetVersionedTaskNameCoreInRegistry(is_machine)));
+      task_name = internal::GetCurrentTaskNameCore(is_machine);
+    } else {
+      VERIFY1(SUCCEEDED(
+      internal::CreateAndSetVersionedTaskNameUAInRegistry(is_machine)));
+      task_name = internal::GetCurrentTaskNameUA(is_machine);
+    }
+    ASSERT1(!internal::IsInstalledScheduledTask(task_name));
+  }
+
+  return internal::InstallScheduledTask(task_name,
+                                        task_path,
+                                        task_parameters,
+                                        task_description,
+                                        is_machine,
+                                        mode == COMMANDLINE_MODE_CORE &&
+                                        is_machine,
+                                        true,
+                                        mode == COMMANDLINE_MODE_UA);
+}
+
+HRESULT InstallGoopdateTasks(const TCHAR* task_path, bool is_machine) {
+  HRESULT hr = InstallGoopdateTaskForMode(task_path,
+                                          is_machine,
+                                          COMMANDLINE_MODE_CORE);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return InstallGoopdateTaskForMode(task_path, is_machine, COMMANDLINE_MODE_UA);
+}
+
+HRESULT UninstallGoopdateTasks(bool is_machine) {
+  VERIFY1(SUCCEEDED(internal::UninstallScheduledTask(
+      internal::GetCurrentTaskNameCore(is_machine))));
+  VERIFY1(SUCCEEDED(internal::UninstallScheduledTask(
+      internal::GetCurrentTaskNameUA(is_machine))));
+
+  // Try to uninstall any tasks that we failed to update during a previous
+  // overinstall. It is possible that we fail to uninstall these again here.
+  VERIFY1(SUCCEEDED(internal::UninstallScheduledTasks(
+      scheduled_task_utils::GetDefaultGoopdateTaskName(is_machine,
+                                                 COMMANDLINE_MODE_CORE))));
+  VERIFY1(SUCCEEDED(internal::UninstallScheduledTasks(
+      scheduled_task_utils::GetDefaultGoopdateTaskName(is_machine,
+                                                 COMMANDLINE_MODE_UA))));
+  return S_OK;
+}
+
+HRESULT UninstallLegacyGoopdateTasks(bool is_machine) {
+  const CString& legacy_omaha1_task =
+      internal::GetOmaha1LegacyTaskName(is_machine);
+  VERIFY1(SUCCEEDED(internal::UninstallScheduledTask(legacy_omaha1_task)));
+
+  const CString& legacy_omaha2_task =
+      internal::GetOmaha2LegacyTaskName(is_machine);
+  VERIFY1(SUCCEEDED(internal::UninstallScheduledTask(legacy_omaha2_task)));
+
+  return S_OK;
+}
+
+HRESULT StartGoopdateTaskCore(bool is_machine) {
+  return internal::StartScheduledTask(
+             internal::GetCurrentTaskNameCore(is_machine));
+}
+
+bool IsInstalledGoopdateTaskUA(bool is_machine) {
+  return internal::IsInstalledScheduledTask(
+                             internal::GetCurrentTaskNameUA(is_machine));
+}
+
+bool IsDisabledGoopdateTaskUA(bool is_machine) {
+  const CString& task_name(internal::GetCurrentTaskNameUA(is_machine));
+  return internal::GetScheduledTaskStatus(task_name) == SCHED_S_TASK_DISABLED;
+}
+
+HRESULT GetExitCodeGoopdateTaskUA(bool is_machine) {
+  const CString& task_name(internal::GetCurrentTaskNameUA(is_machine));
+  return internal::GetScheduledTaskExitCode(task_name);
+}
+
+bool IsUATaskHealthy(bool is_machine) {
+  if (!ServiceUtils::IsServiceRunning(SERVICE_SCHEDULE)) {
+    UTIL_LOG(LW, (_T("[Task Scheduler Service is not running]")));
+    return false;
+  }
+
+  if (!IsInstalledGoopdateTaskUA(is_machine)) {
+    UTIL_LOG(LW, (_T("[UA Task not installed]")));
+    return false;
+  }
+
+  if (IsDisabledGoopdateTaskUA(is_machine)) {
+    UTIL_LOG(LW, (_T("[UA Task disabled]")));
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace scheduled_task_utils
+
+}  // namespace omaha
diff --git a/common/scheduled_task_utils.h b/common/scheduled_task_utils.h
new file mode 100644
index 0000000..f669618
--- /dev/null
+++ b/common/scheduled_task_utils.h
@@ -0,0 +1,47 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_SCHEDULED_TASK_UTILS_H_
+#define OMAHA_COMMON_SCHEDULED_TASK_UTILS_H_
+
+#include <windows.h>
+#include <atlstr.h>
+// TODO(omaha3): Try to avoid depending on goopdate command line in these files.
+// If Omaha 3 ends up needing multiple tasks, consider using a bool or
+// task-specific enum instead.
+#include "omaha/common/command_line.h"
+
+namespace omaha {
+
+namespace scheduled_task_utils {
+
+// This method will return the default scheduled task name. This default value
+// is also used as the prefix for generating unique task names.
+CString GetDefaultGoopdateTaskName(bool is_machine, CommandLineMode mode);
+
+HRESULT InstallGoopdateTasks(const TCHAR* task_path, bool is_machine);
+HRESULT UninstallGoopdateTasks(bool is_machine);
+HRESULT UninstallLegacyGoopdateTasks(bool is_machine);
+HRESULT StartGoopdateTaskCore(bool is_machine);
+bool IsInstalledGoopdateTaskUA(bool is_machine);
+bool IsDisabledGoopdateTaskUA(bool is_machine);
+HRESULT GetExitCodeGoopdateTaskUA(bool is_machine);
+bool IsUATaskHealthy(bool is_machine);
+
+}  // namespace scheduled_task_utils
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_SCHEDULED_TASK_UTILS_H_
diff --git a/common/scheduled_task_utils_internal.h b/common/scheduled_task_utils_internal.h
new file mode 100644
index 0000000..c7a5c2d
--- /dev/null
+++ b/common/scheduled_task_utils_internal.h
@@ -0,0 +1,111 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_SCHEDULED_TASK_UTILS_INTERNAL_H_
+#define OMAHA_COMMON_SCHEDULED_TASK_UTILS_INTERNAL_H_
+
+#include <windows.h>
+#include <taskschd.h>
+
+namespace omaha {
+
+namespace scheduled_task_utils {
+
+namespace internal {
+
+// Gets the current name, say "GoogleUpdateTaskMachineCore", of the
+// GoogleUpdateCore scheduled task, either from the registry, or a default
+// value if there is no registration.
+CString GetCurrentTaskNameCore(bool is_machine);
+
+// Creates a unique name, say "GoogleUpdateTaskMachineCore1c9b3d6baf90df3", of
+// the GoogleUpdateCore scheduled task, and stores it in the registry.
+// Subsequent invocations of GetCurrentTaskNameCore() will return this new
+// value.
+HRESULT CreateAndSetVersionedTaskNameCoreInRegistry(bool machine);
+
+// Gets the current name, say "GoogleUpdateTaskMachineUA", of the
+// GoogleUpdateUA scheduled task, either from the registry, or a default value
+// if there is no registration.
+CString GetCurrentTaskNameUA(bool is_machine);
+
+// Creates a unique name, say "GoogleUpdateTaskMachineUA1c9b3d6baf90df3", of
+// the GoogleUpdateUA scheduled task, and stores it in the registry.
+// Subsequent invocations of GetCurrentTaskNameUA() will return this new
+// value.
+HRESULT CreateAndSetVersionedTaskNameUAInRegistry(bool machine);
+
+// Installs a scheduled task. The task will run as either as SYSTEM or the
+// current user. The task will be triggered at each user logon, and/or
+// fixed intervals.
+HRESULT InstallScheduledTask(const TCHAR* task_name,
+                             const TCHAR* task_path,
+                             const TCHAR* task_parameters,
+                             const TCHAR* task_comment,
+                             bool is_machine,
+                             bool create_logon_trigger,
+                             bool create_daily_trigger,
+                             bool create_hourly_trigger);
+
+// Deletes a scheduled task.
+HRESULT UninstallScheduledTask(const TCHAR* task_name);
+
+// Deletes all scheduled tasks with the given prefix.
+HRESULT UninstallScheduledTasks(const TCHAR* task_prefix);
+
+// Runs a scheduled task immediately.
+HRESULT StartScheduledTask(const TCHAR* task_name);
+
+// Returns true if the scheduled task exists.
+bool IsInstalledScheduledTask(const TCHAR* task_name);
+
+// Returns the priority at which the scheduled task process will run. Returns 0
+// on failure.
+DWORD GetScheduledTaskPriority(const TCHAR* task_name);
+
+// Returns true if the scheduled task ever ran.
+bool HasScheduledTaskEverRun(const TCHAR* task_name);
+
+// Returns a status code on success. List of status codes at
+// http://msdn2.microsoft.com/en-us/library/aa381263.aspx
+HRESULT GetScheduledTaskStatus(const TCHAR* task_name);
+
+// Stops the task if it is already running.
+HRESULT StopScheduledTask(const TCHAR* task_name);
+
+// Waits for the task to change its status to the specified status value and
+// returns the status of the task. If the status did not change within the
+// time, it returns the status of the task at the end of the wait.
+HRESULT WaitForTaskStatus(const TCHAR* task_name, HRESULT status, int time_ms);
+
+namespace v2 {
+
+// Task Scheduler 2.0 API helpers.
+bool IsTaskScheduler2APIAvailable();
+HRESULT GetRegisteredTask(const TCHAR* task_name, IRegisteredTask** task);
+bool IsScheduledTaskRunning(const TCHAR* task_name);
+HRESULT StartScheduledTask(const TCHAR* task_name);
+HRESULT StopScheduledTask(const TCHAR* task_name);
+
+}  // namespace v2
+
+}  // namespace internal
+
+}  // namespace scheduled_task_utils
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_SCHEDULED_TASK_UTILS_INTERNAL_H_
+
diff --git a/common/scheduled_task_utils_unittest.cc b/common/scheduled_task_utils_unittest.cc
new file mode 100644
index 0000000..8c921ed
--- /dev/null
+++ b/common/scheduled_task_utils_unittest.cc
@@ -0,0 +1,304 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <windows.h>
+#include <atlstr.h>
+#include <mstask.h>
+#include "omaha/base/app_util.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/scoped_ptr_cotask.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/scheduled_task_utils.h"
+#include "omaha/common/scheduled_task_utils_internal.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+const int kMaxWaitForProcessMs                  = 120000;
+
+const TCHAR kLongRunningProcessesRelativePath[] =
+    _T("unittest_support\\does_not_shutdown\\GoogleUpdate.exe");
+
+CString GetLongRunningProcessPath() {
+  const CString module_dir(app_util::GetCurrentModuleDirectory());
+  return ConcatenatePath(module_dir, kLongRunningProcessesRelativePath);
+}
+
+}  // namespace
+
+namespace scheduled_task_utils {
+
+using internal::GetCurrentTaskNameCore;
+using internal::GetCurrentTaskNameUA;
+using internal::GetScheduledTaskStatus;
+using internal::HasScheduledTaskEverRun;
+using internal::StartScheduledTask;
+using internal::StopScheduledTask;
+using internal::WaitForTaskStatus;
+
+namespace v2 = internal::v2;
+
+using vista_util::IsUserAdmin;
+
+namespace internal {
+
+void StopScheduledTaskAndVerifyReadyState(const CString& task_name) {
+  // For some reason, StopScheduleTask may not successfully stop the task
+  // even it returns S_OK. So try to stop multiple times.
+  for (int i = 0; i < 3; ++i) {
+    EXPECT_SUCCEEDED(StopScheduledTask(task_name));
+
+    if (SCHED_S_TASK_READY == WaitForTaskStatus(task_name,
+                                                SCHED_S_TASK_READY,
+                                                kMsPerSec)) {
+      break;
+    }
+  }
+  EXPECT_EQ(SCHED_S_TASK_READY, GetScheduledTaskStatus(task_name));
+}
+
+TEST(ScheduledTaskUtilsTest, ScheduledTasks) {
+  const TCHAR kSchedTestTaskName[]            = _T("TestScheduledTask");
+  const TCHAR kScheduledTaskExecutable[]      = _T("netstat.exe");
+  const TCHAR kScheduledTaskParameters[]      = _T("20");
+  const TCHAR kSchedTestTaskComment[]         = _T("Google Test Task");
+
+  const CString task_path = ConcatenatePath(app_util::GetSystemDir(),
+                                            kScheduledTaskExecutable);
+  // Install/uninstall.
+  EXPECT_SUCCEEDED(InstallScheduledTask(kSchedTestTaskName,
+                                        task_path,
+                                        _T(""),
+                                        kSchedTestTaskComment,
+                                        IsUserAdmin(),
+                                        IsUserAdmin(),
+                                        true,
+                                        true));
+  EXPECT_SUCCEEDED(UninstallScheduledTask(kSchedTestTaskName));
+
+  // Calling InstallScheduledTask twice should succeed.
+  for (int i = 0; i < 2; ++i) {
+    EXPECT_SUCCEEDED(InstallScheduledTask(kSchedTestTaskName,
+                                          task_path,
+                                          _T(""),
+                                          kSchedTestTaskComment,
+                                          IsUserAdmin(),
+                                          IsUserAdmin(),
+                                          true,
+                                          true));
+  }
+
+  // "Upgrade" to a new version, which now has parameters.
+  EXPECT_SUCCEEDED(InstallScheduledTask(kSchedTestTaskName,
+                                        task_path,
+                                        kScheduledTaskParameters,
+                                        kSchedTestTaskComment,
+                                        IsUserAdmin(),
+                                        IsUserAdmin(),
+                                        true,
+                                        true));
+
+  EXPECT_FALSE(HasScheduledTaskEverRun(kSchedTestTaskName));
+
+  // Start and stop.
+  EXPECT_EQ(SCHED_S_TASK_HAS_NOT_RUN,
+            GetScheduledTaskStatus(kSchedTestTaskName));
+  EXPECT_SUCCEEDED(StartScheduledTask(kSchedTestTaskName));
+  EXPECT_EQ(SCHED_S_TASK_RUNNING,
+            WaitForTaskStatus(kSchedTestTaskName,
+                              SCHED_S_TASK_RUNNING,
+                              kMaxWaitForProcessMs));
+
+  EXPECT_TRUE(HasScheduledTaskEverRun(kSchedTestTaskName));
+
+  StopScheduledTaskAndVerifyReadyState(kSchedTestTaskName);
+
+  // Finally, uninstall.
+  EXPECT_SUCCEEDED(UninstallScheduledTask(kSchedTestTaskName));
+}
+
+TEST(ScheduledTaskUtilsTest, ScheduledTasksV2) {
+  if (!v2::IsTaskScheduler2APIAvailable()) {
+    std::wcout << _T("\tTest did not run because this OS does not support the ")
+                  _T("Task Scheduler 2.0 API.") << std::endl;
+    return;
+  }
+
+  const TCHAR kSchedTestTaskName[]            = _T("TestScheduledTaskV2");
+  const TCHAR kScheduledTaskExecutable[]      = _T("netstat.exe");
+  const TCHAR kScheduledTaskParameters[]      = _T("20");
+  const TCHAR kSchedTestTaskComment[]         = _T("Google Test Task V2");
+
+  const CString task_path = ConcatenatePath(app_util::GetSystemDir(),
+                                            kScheduledTaskExecutable);
+  EXPECT_SUCCEEDED(InstallScheduledTask(kSchedTestTaskName,
+                                        task_path,
+                                        _T(""),
+                                        kSchedTestTaskComment,
+                                        IsUserAdmin(),
+                                        IsUserAdmin(),
+                                        true,
+                                        true));
+
+  // Start and stop.
+  EXPECT_FALSE(v2::IsScheduledTaskRunning(kSchedTestTaskName));
+  EXPECT_SUCCEEDED(v2::StartScheduledTask(kSchedTestTaskName));
+  EXPECT_TRUE(v2::IsScheduledTaskRunning(kSchedTestTaskName));
+
+  EXPECT_SUCCEEDED(v2::StopScheduledTask(kSchedTestTaskName));
+  EXPECT_FALSE(v2::IsScheduledTaskRunning(kSchedTestTaskName));
+
+  // Finally, uninstall.
+  EXPECT_SUCCEEDED(UninstallScheduledTask(kSchedTestTaskName));
+}
+
+}  // namespace internal
+
+
+TEST(ScheduledTaskUtilsTest, GoopdateTasks) {
+  const CString task_name = GetCurrentTaskNameCore(IsUserAdmin());
+  const CString task_path = GetLongRunningProcessPath();
+
+  // Install/uninstall.
+  EXPECT_SUCCEEDED(InstallGoopdateTasks(task_path, IsUserAdmin()));
+  EXPECT_SUCCEEDED(UninstallGoopdateTasks(IsUserAdmin()));
+
+  EXPECT_SUCCEEDED(InstallGoopdateTasks(task_path, IsUserAdmin()));
+  EXPECT_FALSE(HasScheduledTaskEverRun(task_name));
+
+  // Start and stop.
+  EXPECT_EQ(SCHED_S_TASK_HAS_NOT_RUN, GetScheduledTaskStatus(task_name));
+  EXPECT_SUCCEEDED(StartGoopdateTaskCore(IsUserAdmin()));
+
+  EXPECT_EQ(SCHED_S_TASK_RUNNING,
+            WaitForTaskStatus(task_name,
+                              SCHED_S_TASK_RUNNING,
+                              kMaxWaitForProcessMs));
+
+  EXPECT_TRUE(HasScheduledTaskEverRun(task_name));
+
+  internal::StopScheduledTaskAndVerifyReadyState(task_name);
+
+  // Finally, uninstall.
+  EXPECT_SUCCEEDED(UninstallGoopdateTasks(IsUserAdmin()));
+}
+
+TEST(ScheduledTaskUtilsTest, GoopdateTaskInUseOverinstall) {
+  const CString task_path = GetLongRunningProcessPath();
+  EXPECT_SUCCEEDED(InstallGoopdateTasks(task_path, IsUserAdmin()));
+
+  CString original_task_name(GetCurrentTaskNameCore(IsUserAdmin()));
+
+  // Open the file underlying the current task in exclusive mode, so that
+  // InstallGoopdateTasks() is forced to create a new task.
+  CComPtr<ITaskScheduler> scheduler;
+  EXPECT_SUCCEEDED(scheduler.CoCreateInstance(CLSID_CTaskScheduler,
+                                              NULL,
+                                              CLSCTX_INPROC_SERVER));
+  CComPtr<ITask> task;
+  EXPECT_SUCCEEDED(scheduler->Activate(original_task_name,
+                                       __uuidof(ITask),
+                                       reinterpret_cast<IUnknown**>(&task)));
+  CComQIPtr<IPersistFile> persist(task);
+  EXPECT_TRUE(persist);
+  scoped_ptr_cotask<OLECHAR> job_file;
+  EXPECT_SUCCEEDED(persist->GetCurFile(address(job_file)));
+  persist.Release();
+
+  File file;
+  EXPECT_SUCCEEDED(file.OpenShareMode(job_file.get(), false, false, 0));
+
+  EXPECT_SUCCEEDED(InstallGoopdateTasks(task_path, IsUserAdmin()));
+  CString new_task_name(GetCurrentTaskNameCore(IsUserAdmin()));
+  EXPECT_STRNE(original_task_name, new_task_name);
+
+  // Cleanup.
+  file.Close();
+  EXPECT_SUCCEEDED(UninstallGoopdateTasks(IsUserAdmin()));
+}
+
+TEST(ScheduledTaskUtilsTest, GetExitCodeGoopdateTaskUA) {
+  const CString task_name = GetCurrentTaskNameUA(IsUserAdmin());
+  const CString task_path = ConcatenatePath(
+                                app_util::GetCurrentModuleDirectory(),
+                                _T("unittest_support\\SaveArguments.exe"));
+
+  EXPECT_SUCCEEDED(InstallGoopdateTasks(task_path, IsUserAdmin()));
+  EXPECT_EQ(SCHED_S_TASK_HAS_NOT_RUN,
+            GetExitCodeGoopdateTaskUA(IsUserAdmin()));
+  EXPECT_FALSE(HasScheduledTaskEverRun(task_name));
+
+  // Start the task and wait for it to run and become ready again. The task
+  // runs a program that returns right away. Sometimes the task does not run
+  // for unknown reason. Attempting to run the task multiple times does not
+  // work. This remains a flaky test.
+  EXPECT_SUCCEEDED(StartScheduledTask(task_name));
+  EXPECT_EQ(SCHED_S_TASK_READY,
+            WaitForTaskStatus(task_name,
+                              SCHED_S_TASK_READY,
+                              kMaxWaitForProcessMs));
+  EXPECT_TRUE(HasScheduledTaskEverRun(task_name));
+  EXPECT_EQ(S_OK, GetExitCodeGoopdateTaskUA(IsUserAdmin()));
+
+  EXPECT_SUCCEEDED(File::Remove(
+      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                      _T("unittest_support\\saved_arguments.txt"))));
+  EXPECT_SUCCEEDED(UninstallGoopdateTasks(IsUserAdmin()));
+}
+
+TEST(ScheduledTaskUtilsTest, GetDefaultGoopdateTaskName_Core_Machine) {
+  CString expected_task_name(kScheduledTaskNameMachinePrefix);
+  expected_task_name += kScheduledTaskNameCoreSuffix;
+  EXPECT_STREQ(expected_task_name,
+               GetDefaultGoopdateTaskName(true, COMMANDLINE_MODE_CORE));
+}
+
+TEST(ScheduledTaskUtilsTest, GetDefaultGoopdateTaskName_Core_User) {
+  CString expected_task_name_user = kScheduledTaskNameUserPrefix;
+  CString user_sid;
+  EXPECT_SUCCEEDED(user_info::GetProcessUser(NULL, NULL, &user_sid));
+  expected_task_name_user += user_sid;
+  expected_task_name_user += kScheduledTaskNameCoreSuffix;
+  EXPECT_STREQ(expected_task_name_user,
+               GetDefaultGoopdateTaskName(false, COMMANDLINE_MODE_CORE));
+}
+
+TEST(ScheduledTaskUtilsTest, GetDefaultGoopdateTaskName_UA_Machine) {
+  CString expected_task_name(kScheduledTaskNameMachinePrefix);
+  expected_task_name += kScheduledTaskNameUASuffix;
+  EXPECT_STREQ(expected_task_name,
+               GetDefaultGoopdateTaskName(true, COMMANDLINE_MODE_UA));
+}
+
+TEST(ScheduledTaskUtilsTest, GetDefaultGoopdateTaskName_UA_User) {
+  CString expected_task_name_user = kScheduledTaskNameUserPrefix;
+  CString user_sid;
+  EXPECT_SUCCEEDED(user_info::GetProcessUser(NULL, NULL, &user_sid));
+  expected_task_name_user += user_sid;
+  expected_task_name_user += kScheduledTaskNameUASuffix;
+  EXPECT_STREQ(expected_task_name_user,
+               GetDefaultGoopdateTaskName(false, COMMANDLINE_MODE_UA));
+}
+
+}  // namespace scheduled_task_utils
+
+}  // namespace omaha
+
diff --git a/common/scope_guard.h b/common/scope_guard.h
deleted file mode 100644
index 326dc0c..0000000
--- a/common/scope_guard.h
+++ /dev/null
@@ -1,337 +0,0 @@
-//
-// Author: Andrei Alexandrescu - andrei@metalanguage.com
-//
-// The code below is based on the following article published in
-// C/C++ Users Journal by Andrei Alexandrescu:
-//
-// http://www.cuj.com/documents/s=8000/cujcexp1812alexandr/alexandr.htm
-//
-// ScopeGuard is useful when you need to perform automatic cleanup of resources.
-// This idiom is important when you want to assemble an operation out of several
-// atomic operations, each of which could fail.
-//
-// Usage
-// ------
-// Scope guard for objects:
-// void f(T& t) {
-//   std::vector<T> v;
-//   v.push_back(t);
-//   ScopeGuard guard = MakeObjGuard(v, &std::vector<T>v::pop_back);
-//   if (!Commit()) {
-//     return;
-//   }
-//   guard.Dismiss();     // removes the t from the vector in the case Commit fails
-// }
-//
-// Scope guard for functions:
-// void open();
-// void close(int i);
-// void g(int i) {
-//   open();
-//   ScopeGuard guard = MakeGuard(close, 0);
-//   if (!read()) {
-//       return;
-//   }
-//   if (!write()) {
-//       return;
-//   }
-// }
-//
-// Using the macros:
-// void g(int i) {
-//   open();
-//   ON_SCOPE_EXIT(close, 0);
-//   if (!read()) {
-//       return;
-//   }
-//   if (!write()) {
-//       return;
-//   }
-// }
-
-// TODO(omaha): provide support to run with or without exceptions enabled.
-// For now it assumes that the code is not throwing exceptions.
-#ifndef SCOPEGUARD_H_
-#define SCOPEGUARD_H_
-
-namespace omaha {
-
-template <class T>
-class RefHolder
-{
-  T& ref_;
-public:
-  RefHolder(T& ref) : ref_(ref) {}
-  operator T& () const
-  {
-    return ref_;
-  }
-private:
-    // Disable assignment - not implemented
-    RefHolder& operator=(const RefHolder&);
-};
-
-template <class T>
-inline RefHolder<T> ByRef(T& t)
-{
-  return RefHolder<T>(t);
-}
-
-class ScopeGuardImplBase
-{
-  ScopeGuardImplBase& operator =(const ScopeGuardImplBase&);
-protected:
-  ~ScopeGuardImplBase()
-  {
-  }
-  ScopeGuardImplBase(const ScopeGuardImplBase& other) throw()
-    : dismissed_(other.dismissed_)
-  {
-    other.Dismiss();
-  }
-  template <typename J>
-  static void SafeExecute(J& j) throw()
-  {
-    if (!j.dismissed_)
-    {
-      // TODO(omaha): assume this does not throw
-      j.Execute();
-    }
-  }
-
-  mutable bool dismissed_;
-public:
-  ScopeGuardImplBase() throw() : dismissed_(false)
-  {
-  }
-  void Dismiss() const throw()
-  {
-    dismissed_ = true;
-  }
-};
-
-typedef const ScopeGuardImplBase& ScopeGuard;
-
-template <typename F>
-class ScopeGuardImpl0 : public ScopeGuardImplBase
-{
-public:
-  static ScopeGuardImpl0<F> MakeGuard(F fun)
-  {
-    return ScopeGuardImpl0<F>(fun);
-  }
-  ~ScopeGuardImpl0() throw()
-  {
-    SafeExecute(*this);
-  }
-  void Execute()
-  {
-    fun_();
-  }
-protected:
-  ScopeGuardImpl0(F fun) : fun_(fun)
-  {
-  }
-  F fun_;
-};
-
-template <typename F>
-inline ScopeGuardImpl0<F> MakeGuard(F fun)
-{
-  return ScopeGuardImpl0<F>::MakeGuard(fun);
-}
-
-template <typename F, typename P1>
-class ScopeGuardImpl1 : public ScopeGuardImplBase
-{
-public:
-  static ScopeGuardImpl1<F, P1> MakeGuard(F fun, P1 p1)
-  {
-    return ScopeGuardImpl1<F, P1>(fun, p1);
-  }
-  ~ScopeGuardImpl1() throw()
-  {
-    SafeExecute(*this);
-  }
-  void Execute()
-  {
-    fun_(p1_);
-  }
-protected:
-  ScopeGuardImpl1(F fun, P1 p1) : fun_(fun), p1_(p1)
-  {
-  }
-  F fun_;
-  const P1 p1_;
-};
-
-template <typename F, typename P1>
-inline ScopeGuardImpl1<F, P1> MakeGuard(F fun, P1 p1)
-{
-  return ScopeGuardImpl1<F, P1>::MakeGuard(fun, p1);
-}
-
-template <typename F, typename P1, typename P2>
-class ScopeGuardImpl2: public ScopeGuardImplBase
-{
-public:
-  static ScopeGuardImpl2<F, P1, P2> MakeGuard(F fun, P1 p1, P2 p2)
-  {
-    return ScopeGuardImpl2<F, P1, P2>(fun, p1, p2);
-  }
-  ~ScopeGuardImpl2() throw()
-  {
-    SafeExecute(*this);
-  }
-  void Execute()
-  {
-    fun_(p1_, p2_);
-  }
-protected:
-  ScopeGuardImpl2(F fun, P1 p1, P2 p2) : fun_(fun), p1_(p1), p2_(p2)
-  {
-  }
-  F fun_;
-  const P1 p1_;
-  const P2 p2_;
-};
-
-template <typename F, typename P1, typename P2>
-inline ScopeGuardImpl2<F, P1, P2> MakeGuard(F fun, P1 p1, P2 p2)
-{
-  return ScopeGuardImpl2<F, P1, P2>::MakeGuard(fun, p1, p2);
-}
-
-template <typename F, typename P1, typename P2, typename P3>
-class ScopeGuardImpl3 : public ScopeGuardImplBase
-{
-public:
-  static ScopeGuardImpl3<F, P1, P2, P3> MakeGuard(F fun, P1 p1, P2 p2, P3 p3)
-  {
-    return ScopeGuardImpl3<F, P1, P2, P3>(fun, p1, p2, p3);
-  }
-  ~ScopeGuardImpl3() throw()
-  {
-    SafeExecute(*this);
-  }
-  void Execute()
-  {
-    fun_(p1_, p2_, p3_);
-  }
-protected:
-  ScopeGuardImpl3(F fun, P1 p1, P2 p2, P3 p3) : fun_(fun), p1_(p1), p2_(p2), p3_(p3)
-  {
-  }
-  F fun_;
-  const P1 p1_;
-  const P2 p2_;
-  const P3 p3_;
-};
-
-template <typename F, typename P1, typename P2, typename P3>
-inline ScopeGuardImpl3<F, P1, P2, P3> MakeGuard(F fun, P1 p1, P2 p2, P3 p3)
-{
-  return ScopeGuardImpl3<F, P1, P2, P3>::MakeGuard(fun, p1, p2, p3);
-}
-
-
-template <class Obj, typename MemFun>
-class ObjScopeGuardImpl0 : public ScopeGuardImplBase
-{
-public:
-  static ObjScopeGuardImpl0<Obj, MemFun> MakeObjGuard(Obj& obj, MemFun memFun)
-  {
-    return ObjScopeGuardImpl0<Obj, MemFun>(obj, memFun);
-  }
-  ~ObjScopeGuardImpl0() throw()
-  {
-    SafeExecute(*this);
-  }
-  void Execute()
-  {
-    (obj_.*memFun_)();
-  }
-protected:
-  ObjScopeGuardImpl0(Obj& obj, MemFun memFun)
-    : obj_(obj), memFun_(memFun) {}
-  Obj& obj_;
-  MemFun memFun_;
-};
-
-template <class Obj, typename MemFun>
-inline ObjScopeGuardImpl0<Obj, MemFun> MakeObjGuard(Obj& obj, MemFun memFun)
-{
-  return ObjScopeGuardImpl0<Obj, MemFun>::MakeObjGuard(obj, memFun);
-}
-
-template <class Obj, typename MemFun, typename P1>
-class ObjScopeGuardImpl1 : public ScopeGuardImplBase
-{
-public:
-  static ObjScopeGuardImpl1<Obj, MemFun, P1> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1)
-  {
-    return ObjScopeGuardImpl1<Obj, MemFun, P1>(obj, memFun, p1);
-  }
-  ~ObjScopeGuardImpl1() throw()
-  {
-    SafeExecute(*this);
-  }
-  void Execute()
-  {
-    (obj_.*memFun_)(p1_);
-  }
-protected:
-  ObjScopeGuardImpl1(Obj& obj, MemFun memFun, P1 p1)
-    : obj_(obj), memFun_(memFun), p1_(p1) {}
-  Obj& obj_;
-  MemFun memFun_;
-  const P1 p1_;
-};
-
-template <class Obj, typename MemFun, typename P1>
-inline ObjScopeGuardImpl1<Obj, MemFun, P1> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1)
-{
-  return ObjScopeGuardImpl1<Obj, MemFun, P1>::MakeObjGuard(obj, memFun, p1);
-}
-
-template <class Obj, typename MemFun, typename P1, typename P2>
-class ObjScopeGuardImpl2 : public ScopeGuardImplBase
-{
-public:
-  static ObjScopeGuardImpl2<Obj, MemFun, P1, P2> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1, P2 p2)
-  {
-    return ObjScopeGuardImpl2<Obj, MemFun, P1, P2>(obj, memFun, p1, p2);
-  }
-  ~ObjScopeGuardImpl2() throw()
-  {
-    SafeExecute(*this);
-  }
-  void Execute()
-  {
-    (obj_.*memFun_)(p1_, p2_);
-  }
-protected:
-  ObjScopeGuardImpl2(Obj& obj, MemFun memFun, P1 p1, P2 p2)
-    : obj_(obj), memFun_(memFun), p1_(p1), p2_(p2) {}
-  Obj& obj_;
-  MemFun memFun_;
-  const P1 p1_;
-  const P2 p2_;
-};
-
-template <class Obj, typename MemFun, typename P1, typename P2>
-inline ObjScopeGuardImpl2<Obj, MemFun, P1, P2> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1, P2 p2)
-{
-  return ObjScopeGuardImpl2<Obj, MemFun, P1, P2>::MakeObjGuard(obj, memFun, p1, p2);
-}
-
-#define CONCATENATE_DIRECT(s1, s2) s1##s2
-#define CONCATENATE(s1, s2) CONCATENATE_DIRECT(s1, s2)
-#define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__)
-
-#define ON_SCOPE_EXIT ScopeGuard ANONYMOUS_VARIABLE(scopeGuard) = MakeGuard
-#define ON_SCOPE_EXIT_OBJ ScopeGuard ANONYMOUS_VARIABLE(scopeGuard) = MakeObjGuard
-
-}  // namespace omaha
-
-#endif //SCOPEGUARD_H_
diff --git a/common/scoped_impersonation.h b/common/scoped_impersonation.h
deleted file mode 100644
index e7efec6..0000000
--- a/common/scoped_impersonation.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// TODO(omaha): deprecate and use ATL::CAccessToken instead.
-
-#ifndef OMAHA_COMMON_SCOPED_IMPERSONATION_H__
-#define OMAHA_COMMON_SCOPED_IMPERSONATION_H__
-
-#include <windows.h>
-#include "omaha/common/debug.h"
-#include "omaha/common/scoped_any.h"
-
-namespace omaha {
-
-inline DWORD smart_impersonate_helper(HANDLE token) {
-  if (!token) {
-    return ERROR_INVALID_HANDLE;
-  }
-  return ::ImpersonateLoggedOnUser(token) ? ERROR_SUCCESS : ::GetLastError();
-}
-
-inline void smart_unimpersonate_helper(DWORD result) {
-  if (result == ERROR_SUCCESS) {
-    VERIFY1(::RevertToSelf());
-  }
-}
-
-typedef close_fun<void (*)(DWORD), smart_unimpersonate_helper>
-    close_impersonation;
-
-typedef value_const<DWORD, static_cast<DWORD>(-1)>
-    impersonation_not_init;
-
-typedef scoped_any<DWORD, close_impersonation, impersonation_not_init>
-    scoped_impersonation_close;
-
-// Manages the calls to ImpersonateLoggedOnUser and RevertToSelf.
-struct scoped_impersonation {
-  explicit scoped_impersonation(HANDLE token)
-      : result_(smart_impersonate_helper(token)) {
-  }
-
-  HRESULT result() const { return get(result_); }
-
- private:
-  const scoped_impersonation_close result_;
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_SCOPED_IMPERSONATION_H__
diff --git a/common/scoped_impersonation_unittest.cc b/common/scoped_impersonation_unittest.cc
deleted file mode 100644
index cc2c7a2..0000000
--- a/common/scoped_impersonation_unittest.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <windows.h>
-#include "base/basictypes.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/scoped_impersonation.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/common/vista_utils.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(ScopedImpersonationTest, ImpersonateLoggedOnUser) {
-  scoped_handle token;
-  vista::GetLoggedOnUserToken(address(token));
-  if (token) {
-    scoped_impersonation impersonate_user(get(token));
-    EXPECT_EQ(impersonate_user.result(), ERROR_SUCCESS);
-  }
-}
-
-TEST(ScopedImpersonationTest, ImpersonateLoggedOnUserInvalidHandle) {
-  scoped_impersonation impersonate_user(NULL);
-  EXPECT_EQ(impersonate_user.result(), ERROR_INVALID_HANDLE);
-}
-
-}  // namespace omaha
-
diff --git a/common/scoped_ptr_address.h b/common/scoped_ptr_address.h
deleted file mode 100644
index aa0be1c..0000000
--- a/common/scoped_ptr_address.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// Helper functions to enable a useful pattern of using scoped pointers in
-// which the ownership of the memory is transferred out to an empty
-// scoped_ptr or scoped_array.
-//
-// The usage pattern is as follows:
-//    void foo(Bar** b);
-//
-//    scoped_ptr<B> p;
-//    foo(address(p));
-//
-// To receive the ownwership of the resource the scoped pointer must be
-// empty, otherwise it will leak.
-//
-// As an implementation detail, the scoped pointers in "base/scoped_ptr.h" do
-// not offer support for this idiom. The code below may break if the
-// implementation of the scoped_ptr changes. The code works with the vast
-// majority of the scoped_ptr implementations though.
-//
-// TODO(omaha): add unit tests.
-
-#ifndef OMAHA_COMMON_SCOPED_PTR_ADDRESS__
-#define OMAHA_COMMON_SCOPED_PTR_ADDRESS__
-
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "omaha/common/debug.h"
-
-template <typename T>
-inline T** address(const scoped_ptr<T>& t) {
-  COMPILE_ASSERT(sizeof(T*) == sizeof(scoped_ptr<T>), types_do_not_match);
-  ASSERT1(!t.get());
-  return reinterpret_cast<T**>(&const_cast<scoped_ptr<T>&>(t));
-}
-
-template <typename T>
-inline T** address(const scoped_array<T>& t) {
-  COMPILE_ASSERT(sizeof(T*) == sizeof(scoped_ptr<T>), types_do_not_match);
-  ASSERT1(!t.get());
-  return reinterpret_cast<T**>(&const_cast<scoped_array<T>&>(t));
-}
-
-#endif // OMAHA_COMMON_SCOPED_PTR_ADDRESS__
-
diff --git a/common/scoped_ptr_cotask.h b/common/scoped_ptr_cotask.h
deleted file mode 100644
index ad0914b..0000000
--- a/common/scoped_ptr_cotask.h
+++ /dev/null
@@ -1,260 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Defines utility classes and functions which facilitate working with the
-// memory allocation functions CoTaskMemAlloc and CoTaskMemFree.
-
-#ifndef OMAHA_COMMON_SCOPED_PTR_COTASK_H__
-#define OMAHA_COMMON_SCOPED_PTR_COTASK_H__
-
-#include "omaha/common/debug.h"
-
-// scoped_ptr_cotask is identical to scoped_ptr, except that CoTaskMemFree is
-// called instead of delete.  For documentation of the interface, see
-// scoped_ptr.h.
-
-template <typename T>
-class scoped_ptr_cotask;
-
-template <typename T>
-scoped_ptr_cotask<T> make_scoped_ptr_cotask(T* p);
-
-template <typename T>
-class scoped_ptr_cotask {
- private:
-  T* ptr_;
-
-  scoped_ptr_cotask(scoped_ptr_cotask const &);
-  scoped_ptr_cotask & operator=(scoped_ptr_cotask const &);
-
-  friend scoped_ptr_cotask<T> make_scoped_ptr_cotask<T>(T* p);
-
- public:
-  typedef T element_type;
-
-  explicit scoped_ptr_cotask(T* p = 0): ptr_(p) {}
-
-  ~scoped_ptr_cotask() {
-    typedef char type_must_be_complete[sizeof(T)];
-    ::CoTaskMemFree(ptr_);
-  }
-
-  void reset(T* p = 0) {
-    typedef char type_must_be_complete[sizeof(T)];
-
-    if (ptr_ != p) {
-      ::CoTaskMemFree(ptr_);
-      ptr_ = p;
-    }
-  }
-
-  T& operator*() const {
-    assert(ptr_ != 0);
-    return *ptr_;
-  }
-
-  T* operator->() const  {
-    assert(ptr_ != 0);
-    return ptr_;
-  }
-
-  bool operator==(T* p) const {
-    return ptr_ == p;
-  }
-
-  bool operator!=(T* p) const {
-    return ptr_ != p;
-  }
-
-  T* get() const  {
-    return ptr_;
-  }
-
-  void swap(scoped_ptr_cotask & b) {
-    T* tmp = b.ptr_;
-    b.ptr_ = ptr_;
-    ptr_ = tmp;
-  }
-
-  T* release() {
-    T* tmp = ptr_;
-    ptr_ = 0;
-    return tmp;
-  }
-
- private:
-  template <typename U> bool operator==(scoped_ptr_cotask<U> const& p) const;
-  template <typename U> bool operator!=(scoped_ptr_cotask<U> const& p) const;
-};
-
-template <typename T>
-scoped_ptr_cotask<T> make_scoped_ptr_cotask(T* p) {
-  return scoped_ptr_cotask<T>(p);
-}
-
-template <typename T> inline
-void swap(scoped_ptr_cotask<T>& a, scoped_ptr_cotask<T>& b) {
-  a.swap(b);
-}
-
-template <typename T> inline
-bool operator==(T* p, const scoped_ptr_cotask<T>& b) {
-  return p == b.get();
-}
-
-template <typename T> inline
-bool operator!=(T* p, const scoped_ptr_cotask<T>& b) {
-  return p != b.get();
-}
-
-template <typename T>
-inline T** address(const scoped_ptr_cotask<T>& t) {
-  COMPILE_ASSERT(sizeof(T*) == sizeof(t), types_do_not_match);
-  ASSERT1(!t.get());
-  return reinterpret_cast<T**>(&const_cast<scoped_ptr_cotask<T>&>(t));
-}
-
-// scoped_array_cotask manages an array of pointers to objects allocated by
-// CoTaskMemAlloc.  The array is also allocated via CoTaskMemAlloc.  The
-// interface is similar to scoped_array, except that an array length must be
-// explicitly provided.  When the object is destructed, the array and each
-// element of the array are explicitly freed.
-
-template <typename T>
-class scoped_array_cotask;
-
-template <typename T>
-class scoped_array_cotask<T*> {
- private:
-  size_t count_;
-  T** ptr_;
-
-  scoped_array_cotask(scoped_array_cotask const &);
-  scoped_array_cotask & operator=(scoped_array_cotask const &);
-
- public:
-  typedef T* element_type;
-
-  explicit scoped_array_cotask(size_t c, T** p = 0)
-      : count_(c), ptr_(p) {
-    if (!ptr_) {
-      const size_t array_size = sizeof(T*) * count_;
-      ptr_ = static_cast<T**>(::CoTaskMemAlloc(array_size));
-      memset(ptr_, 0, array_size);
-    }
-  }
-
-  ~scoped_array_cotask() {
-    typedef char type_must_be_complete[sizeof(T*)];
-    if (ptr_) {
-      for (size_t i = 0; i < count_; ++i) {
-        ::CoTaskMemFree(ptr_[i]);
-      }
-      ::CoTaskMemFree(ptr_);
-    }
-  }
-
-  size_t size() const { return count_; }
-
-  void reset(size_t c, T** p = 0) {
-    typedef char type_must_be_complete[sizeof(T*)];
-
-    if (ptr_ != p) {
-      if (ptr_) {
-        for (size_t i = 0; i < count_; ++i) {
-          ::CoTaskMemFree(ptr_[i]);
-        }
-        ::CoTaskMemFree(ptr_);
-      }
-      ptr_ = p;
-    }
-    count_ = c;
-    if (!ptr_) {
-      const size_t array_size = sizeof(T*) * count_;
-      ptr_ = static_cast<T**>(::CoTaskMemAlloc(array_size));
-      memset(ptr_, 0, array_size);
-    }
-  }
-
-  T*& operator[](std::ptrdiff_t i) const {
-    assert(ptr_ != 0);
-    assert(i >= 0);
-    return ptr_[i];
-  }
-
-  bool operator==(T** p) const {
-    return ptr_ == p;
-  }
-
-  bool operator!=(T** p) const {
-    return ptr_ != p;
-  }
-
-  T** get() const {
-    return ptr_;
-  }
-
-  void swap(scoped_array_cotask & b) {
-    T** tmp = b.ptr_;
-    b.ptr_ = ptr_;
-    ptr_ = tmp;
-  }
-
-  T** release() {
-    T** tmp = ptr_;
-    ptr_ = 0;
-    return tmp;
-  }
-
- private:
-  template <typename U> bool operator==(scoped_array_cotask<U> const& p) const;
-  template <typename U> bool operator!=(scoped_array_cotask<U> const& p) const;
-};
-
-template <typename T> inline
-void swap(scoped_array_cotask<T>& a, scoped_array_cotask<T>& b) {
-  a.swap(b);
-}
-
-template <typename T> inline
-bool operator==(T* p, const scoped_array_cotask<T>& b) {
-  return p == b.get();
-}
-
-template <typename T> inline
-bool operator!=(T* p, const scoped_array_cotask<T>& b) {
-  return p != b.get();
-}
-
-// address() is not relevant to scoped_array_cotask, due to the count parameter.
-//
-// template <typename T>
-// inline T** address(const scoped_array_cotask<T>& t) {
-//   COMPILE_ASSERT(sizeof(T*) == sizeof(t), types_do_not_match);
-//   ASSERT1(!t.get());
-//   return reinterpret_cast<T**>(&const_cast<scoped_array_cotask<T>&>(t));
-// }
-
-// StrDupCoTask allocates a copy of a string using CoTaskMemAlloc.
-
-template <class T>
-inline T* StrDupCoTask(const T* str, size_t length) {
-  T* mem = static_cast<T*>(::CoTaskMemAlloc(sizeof(T) * (length + 1)));
-  memcpy(mem, str, sizeof(T) * length);
-  mem[length] = 0;
-  return mem;
-}
-
-#endif  // OMAHA_COMMON_SCOPED_PTR_COTASK_H__
diff --git a/common/scoped_ptr_cotask_unittest.cc b/common/scoped_ptr_cotask_unittest.cc
deleted file mode 100644
index 7414eb3..0000000
--- a/common/scoped_ptr_cotask_unittest.cc
+++ /dev/null
@@ -1,308 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <vector>
-#include "omaha/common/debug.h"
-#include "omaha/common/scoped_ptr_cotask.h"
-#include "omaha/testing/unit_test.h"
-
-// TestMallocSpy monitors CoTaskMemAlloc/Free, and records statistics about
-// them.
-
-class TestMallocSpy : public IMallocSpy {
- public:
-  struct Alloc {
-    size_t size;
-    void* ptr;
-    bool freed;
-  };
-
-  TestMallocSpy() : ref_(1) {}
-
-  virtual ~TestMallocSpy() {}
-
-  size_t NumAllocs() const { return allocs_.size(); }
-
-  const Alloc* GetAlloc(size_t i) const {
-    if (i >= allocs_.size())
-      return NULL;
-    return &allocs_[i];
-  }
-
-  size_t NumFrees() const { return frees_.size(); }
-
-  const Alloc* GetFree(size_t i) const {
-    if (i >= frees_.size())
-      return NULL;
-    ASSERT1(frees_[i] < allocs_.size());
-    return &allocs_[frees_[i]];
-  }
-
-  // IUnknown methods
-  virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) {
-    if (NULL == ppv) {
-      return E_POINTER;
-    }
-    if (::IsEqualIID(__uuidof(IUnknown), riid) ||
-        ::IsEqualIID(__uuidof(IMallocSpy), riid)) {
-      AddRef();
-      *ppv = static_cast<IUnknown*>(this);
-      return S_OK;
-    }
-    *ppv = NULL;
-    return E_NOINTERFACE;
-  }
-  virtual ULONG STDMETHODCALLTYPE AddRef() {
-    return ++ref_;
-  }
-  virtual ULONG STDMETHODCALLTYPE Release() {
-    ULONG r = --ref_;
-    if (0 == r) {
-      delete this;
-    }
-    return r;
-  }
-
-  // IMallocSpy methods
-  virtual SIZE_T STDMETHODCALLTYPE PreAlloc(SIZE_T request_size) {
-    Alloc a = { request_size, NULL, false };
-    allocs_.push_back(a);
-    return request_size;
-  }
-  virtual void* STDMETHODCALLTYPE PostAlloc(void* actual) {
-    ASSERT1(!allocs_.empty());
-    ASSERT1(NULL == allocs_.back().ptr);
-    allocs_.back().ptr = actual;
-    return actual;
-  }
-  virtual void* STDMETHODCALLTYPE PreFree(void* request, BOOL spyed) {
-    if (spyed) {
-      bool found = false;
-      for (size_t i = 0; i < allocs_.size(); ++i) {
-        if ((allocs_[i].ptr == request) && !allocs_[i].freed) {
-          allocs_[i].freed = true;
-          frees_.push_back(i);
-          found = true;
-          break;
-        }
-      }
-      ASSERT1(found);
-    }
-    return request;
-  }
-  virtual void STDMETHODCALLTYPE PostFree(BOOL) {}
-  virtual SIZE_T STDMETHODCALLTYPE PreRealloc(void*,
-                                              SIZE_T request_size,
-                                              void**,
-                                              BOOL) {
-    return request_size;
-  }
-  virtual void* STDMETHODCALLTYPE PostRealloc(void* actual, BOOL) {
-    return actual;
-  }
-  virtual void* STDMETHODCALLTYPE PreGetSize(void* request, BOOL) {
-    return request;
-  }
-  virtual SIZE_T STDMETHODCALLTYPE PostGetSize(SIZE_T actual_size, BOOL) {
-    return actual_size;
-  }
-  virtual void* STDMETHODCALLTYPE PreDidAlloc(void* request, BOOL) {
-    return request;
-  }
-  virtual int STDMETHODCALLTYPE PostDidAlloc(void*, BOOL, int fActual) {
-    return fActual;
-  }
-  virtual void STDMETHODCALLTYPE PreHeapMinimize() {}
-  virtual void STDMETHODCALLTYPE PostHeapMinimize() {}
-
- private:
-  ULONG ref_;
-  std::vector<Alloc> allocs_;
-  std::vector<size_t> frees_;
-};
-
-// MallocTest runs tests with a TestMallocSpy installed.
-
-class MallocTest : public testing::Test {
- public:
-  virtual void SetUp() {
-    spy_.Attach(new TestMallocSpy);
-    ASSERT_SUCCEEDED(::CoRegisterMallocSpy(spy_.p));
-    EXPECT_EQ(0, spy()->NumAllocs());
-  }
-  virtual void TearDown() {
-    EXPECT_EQ(spy()->NumAllocs(), spy()->NumFrees());
-    ASSERT_SUCCEEDED(::CoRevokeMallocSpy());
-  }
-  TestMallocSpy* spy() { return spy_.p; }
-
- private:
-  CComPtr<TestMallocSpy> spy_;
-};
-
-TEST_F(MallocTest, StrDupCoTask) {
-  const char kNarrowString[] = "Hello";
-  const size_t kNarrowLen = strlen(kNarrowString);
-  const wchar_t kWideString[] = L"World";
-  const size_t kWideLen = wcslen(kWideString);
-
-  // Test StrDupCoTask with narrow strings.
-  char* narrow_copy = StrDupCoTask(kNarrowString, kNarrowLen);
-
-  ASSERT_EQ(1, spy()->NumAllocs());
-  EXPECT_EQ(0, spy()->NumFrees());
-  EXPECT_EQ((kNarrowLen + 1) * sizeof(char), spy()->GetAlloc(0)->size);
-  EXPECT_EQ(narrow_copy, spy()->GetAlloc(0)->ptr);
-  EXPECT_FALSE(spy()->GetAlloc(0)->freed);
-
-  ::CoTaskMemFree(narrow_copy);
-
-  ASSERT_EQ(1, spy()->NumFrees());
-  EXPECT_EQ(spy()->GetAlloc(0), spy()->GetFree(0));
-  EXPECT_EQ(narrow_copy, spy()->GetFree(0)->ptr);
-  EXPECT_TRUE(spy()->GetFree(0)->freed);
-
-  // Test StrDupCoTask with wide strings.
-  wchar_t* wide_copy = StrDupCoTask(kWideString, kWideLen);
-
-  ASSERT_EQ(2, spy()->NumAllocs());
-  EXPECT_EQ(1, spy()->NumFrees());
-  EXPECT_EQ((kWideLen + 1) * sizeof(wchar_t), spy()->GetAlloc(1)->size);
-  EXPECT_EQ(wide_copy, spy()->GetAlloc(1)->ptr);
-  EXPECT_FALSE(spy()->GetAlloc(1)->freed);
-
-  ::CoTaskMemFree(wide_copy);
-
-  ASSERT_EQ(2, spy()->NumFrees());
-  EXPECT_EQ(spy()->GetAlloc(1), spy()->GetFree(1));
-  EXPECT_EQ(wide_copy, spy()->GetFree(1)->ptr);
-  EXPECT_TRUE(spy()->GetFree(1)->freed);
-}
-
-TEST_F(MallocTest, scoped_ptr_cotask) {
-  scoped_ptr_cotask<wchar_t>* string_ptr;
-
-  // Creating an empty ptr does no additional allocations.
-  string_ptr = new scoped_ptr_cotask<wchar_t>;
-  ASSERT_EQ(0, spy()->NumAllocs());
-  EXPECT_EQ(0, spy()->NumFrees());
-
-  // Assigning a string does not additional allocations.
-  string_ptr->reset(StrDupCoTask(L"hi", 2));
-  ASSERT_EQ(1, spy()->NumAllocs());
-  EXPECT_EQ(0, spy()->NumFrees());
-  EXPECT_EQ(3 * sizeof(wchar_t), spy()->GetAlloc(0)->size);
-  EXPECT_FALSE(spy()->GetAlloc(0)->freed);
-
-  EXPECT_EQ(0, memcmp(string_ptr->get(), L"hi", 3 * sizeof(wchar_t)));
-
-  // Replacing the string frees the old memory.
-  string_ptr->reset(StrDupCoTask(L"there", 5));
-  ASSERT_EQ(2, spy()->NumAllocs());
-  EXPECT_EQ(1, spy()->NumFrees());
-  EXPECT_EQ(6 * sizeof(wchar_t), spy()->GetAlloc(1)->size);
-  EXPECT_TRUE(spy()->GetAlloc(0)->freed);
-  EXPECT_FALSE(spy()->GetAlloc(1)->freed);
-
-  // Deleting the string frees the memory.
-  delete string_ptr;
-  ASSERT_EQ(2, spy()->NumAllocs());
-  EXPECT_EQ(2, spy()->NumFrees());
-  EXPECT_TRUE(spy()->GetAlloc(1)->freed);
-}
-
-TEST_F(MallocTest, scoped_array_cotask) {
-  const size_t kSize = 5;
-  scoped_array_cotask<wchar_t*>* string_array;
-
-  // Allocate an array of 5 empty elements.
-  string_array = new scoped_array_cotask<wchar_t*>(kSize);
-  ASSERT_EQ(kSize, string_array->size());
-  ASSERT_EQ(1, spy()->NumAllocs());
-  EXPECT_EQ(0, spy()->NumFrees());
-  EXPECT_EQ(kSize * sizeof(wchar_t*), spy()->GetAlloc(0)->size);
-
-  // Populate array elements.
-  for (size_t i = 0; i < kSize; ++i) {
-    EXPECT_TRUE(NULL == (*string_array)[i]);
-    (*string_array)[i] = StrDupCoTask(L"hi", 2);
-  }
-  EXPECT_EQ(1 + kSize, spy()->NumAllocs());
-  EXPECT_EQ(0, spy()->NumFrees());
-
-  // Get is idempotent.
-  wchar_t** ptr = string_array->get();
-  EXPECT_EQ(ptr, string_array->get());
-  EXPECT_EQ(ptr, spy()->GetAlloc(0)->ptr);
-  EXPECT_EQ(0, spy()->NumFrees());
-
-  // Release is not idempotent, but does not free memory.
-  ptr = string_array->release();
-  EXPECT_TRUE(NULL == string_array->release());
-  EXPECT_EQ(ptr, spy()->GetAlloc(0)->ptr);
-  EXPECT_EQ(0, spy()->NumFrees());
-
-  // Deleting a released array does not free memory.
-  delete string_array;
-  EXPECT_EQ(0, spy()->NumFrees());
-
-  // Constructing an array from existing memory, does not cause allocations.
-  string_array = new scoped_array_cotask<wchar_t*>(kSize, ptr);
-  EXPECT_EQ(1 + kSize, spy()->NumAllocs());
-  EXPECT_EQ(0, spy()->NumFrees());
-
-  // Deleting an array frees all elements and the array.
-  delete string_array;
-  ASSERT_EQ(1 + kSize, spy()->NumAllocs());
-  EXPECT_EQ(1 + kSize, spy()->NumFrees());
-  for (size_t i = 0; i < spy()->NumAllocs(); ++i) {
-    EXPECT_TRUE(spy()->GetAlloc(i)->freed);
-  }
-}
-
-TEST_F(MallocTest, scoped_array_cotask_reset) {
-  // This test exposes a former bug, where reset did not reallocate a new
-  // array after being released.
-
-  // Allocate an empty array.
-  const size_t kSize = 5;
-  scoped_array_cotask<int*>* array = new scoped_array_cotask<int*>(kSize);
-  ASSERT_EQ(1, spy()->NumAllocs());
-
-  // Release the array, to verify it was allocated.
-  int** first_raw_array = array->release();
-  EXPECT_TRUE(NULL != first_raw_array);
-  EXPECT_FALSE(spy()->GetAlloc(0)->freed);
-
-  // Allocate another empty array.
-  array->reset(kSize);
-  ASSERT_EQ(2, spy()->NumAllocs());
-
-  // Release the second array, to verify it was allocated.
-  int** second_raw_array = array->release();
-  EXPECT_TRUE(NULL != second_raw_array);
-  EXPECT_FALSE(spy()->GetAlloc(1)->freed);
-
-  // Use the scoped_array_cotask object to dispose of the allocated arrays.
-  array->reset(kSize, first_raw_array);
-  array->reset(kSize, second_raw_array);
-  delete array;
-
-  // Check the final conditions.
-  ASSERT_EQ(2, spy()->NumAllocs());
-  for (size_t i = 0; i < spy()->NumAllocs(); ++i) {
-    EXPECT_TRUE(spy()->GetAlloc(i)->freed);
-  }
-}
diff --git a/common/security/build.scons b/common/security/build.scons
deleted file mode 100644
index eb77083..0000000
--- a/common/security/build.scons
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/python2.4
-#
-# Copyright 2009 Google Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# ========================================================================
-
-Import('env')
-
-#
-# Build Security library
-#
-security_env = env.Clone()
-security_env.Append(
-    CPPPATH = [
-        '$MAIN_DIR/third_party/c99/include',
-        ],
-    CCFLAGS = [
-        '/wd4242',  # conversion from 'type1' to 'type2', possible loss of data
-        '/wd4244',  # conversion from 'type1' to 'type2', possible loss of data
-        '/wd4255',  # no function prototype given: converting '()' to '(void)'
-        '/wd4510',  # default constructor could not be generated
-        '/wd4610',  # object 'class' can never be instantiated
-        ],
-)
-
-
-security_inputs = [
-    'aes.c',
-    'b64.c',
-    'challenger.cc',
-    'hmac.c',
-    'md5.c',
-    'rc4.c',
-    'rsa.cc',
-    'sha.c',
-    ]
-
-security_env.ComponentLibrary('security.lib', security_inputs)
diff --git a/common/serializable_object.cc b/common/serializable_object.cc
deleted file mode 100644
index d3106da..0000000
--- a/common/serializable_object.cc
+++ /dev/null
@@ -1,375 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Provides the base class framework for those objects to be serialized
-//
-// HACK:
-//
-// During the serialization/deserialization of vector<T> members, we
-// coerce the type from vector<T> to vector<byte> since we are unable to
-// get the real type vector<T> at later time. This is feasible because
-// vector<T> in the vector library we are linking now keeps track of
-// only front() and end() pointers and use them to calculate size().
-// We need to check whether this approach is still OK if we upgrade the
-// standard libraries.
-
-#include "omaha/common/serializable_object.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-
-namespace omaha {
-
-// Serialize
-bool SerializableObject::Serialize(std::vector<byte>* data) const {
-  ASSERT(data, (_T("")));
-
-  // Estimate how much memory we need
-  int size = data->size();
-  for (size_t i = 0; i < members_.size(); ++i)
-    size += (members_[i].size > 0) ? members_[i].size : sizeof(int);
-
-  // Reserve the estimated size fo vector memory
-  data->reserve(size);
-
-  // Copy over the data
-  for (size_t i = 0; i < members_.size(); ++i) {
-    switch (members_[i].type) {
-      case SERIALIZABLE_VALUE_TYPE: {
-        int pos = data->size();
-        data->resize(data->size() + members_[i].size);
-        memcpy(&(*data)[pos], members_[i].ptr, members_[i].size);
-        break;
-      }
-
-      case SERIALIZABLE_CSTRING: {
-        CString* s = reinterpret_cast<CString*>(members_[i].ptr);
-        SerializeValueList(data,
-                           reinterpret_cast<const byte*>(s->GetString()),
-                           sizeof(TCHAR),
-                           s->GetLength());
-        break;
-      }
-
-      case SERIALIZABLE_NESTED_OBJECT: {
-        SerializableObject* nested_obj =
-            reinterpret_cast<SerializableObject*>(members_[i].ptr);
-        if (!nested_obj->Serialize(data))
-          return false;
-        break;
-      }
-
-      case SERIALIZABLE_VECTOR | SERIALIZABLE_VALUE_TYPE: {
-        // Hack: coerce vector<T> to vector<byte>
-        std::vector<byte>* v =
-            reinterpret_cast<std::vector<byte>*>(members_[i].ptr);
-        if (v->size() != 0) {
-          SerializeValueList(data,
-                             &v->front(),
-                             members_[i].size,
-                             v->size() / members_[i].size);
-        } else {
-          SerializeValueList(data,
-                             NULL,
-                             members_[i].size,
-                             v->size() / members_[i].size);
-        }
-        break;
-      }
-
-      case SERIALIZABLE_VECTOR | SERIALIZABLE_CSTRING: {
-        std::vector<CString>* v =
-            reinterpret_cast<std::vector<CString>*>(members_[i].ptr);
-        SerializeSizeAndCount(data, 1, v->size());
-        if (!v->empty()) {
-          for (std::vector<CString>::const_iterator it = v->begin();
-               it != v->end();
-               ++it) {
-            SerializeValueList(data,
-                               reinterpret_cast<const byte*>(it->GetString()),
-                               sizeof(TCHAR),
-                               it->GetLength());
-          }
-        }
-        break;
-      }
-
-      case SERIALIZABLE_VECTOR | SERIALIZABLE_NESTED_OBJECT: {
-        if (!SerializeVectorNestedObject(data, members_[i].ptr))
-          return false;
-        break;
-      }
-
-      default:
-        ASSERT(false, (_T("")));
-        return false;
-    }
-  }
-
-  return true;
-}
-
-// Serialize the size and count values
-void SerializableObject::SerializeSizeAndCount(std::vector<byte>* data,
-                                               int size,
-                                               int count) const {
-  ASSERT(data, (_T("")));
-  ASSERT(size >= 0, (_T("")));
-
-  // Get current size
-  int pos = data->size();
-
-  // Adjust the size of the data buffer
-  data->resize(data->size() + 2 * sizeof(int));
-
-  // Get pointer to the position of data buffer we start to write
-  byte* ptr = &((*data)[pos]);
-
-  // Push size
-  memcpy(ptr, &size, sizeof(int));
-  ptr += sizeof(int);
-
-  // Push count
-  memcpy(ptr, &count, sizeof(int));
-  ptr += sizeof(int);
-}
-
-// Serialize a list of value-typed elements
-//
-// Args:
-//   ser_data:  pointer to the vector for the serialized data
-//   raw_data:  pointer to the raw data to be serialized
-//   size:      the size of the element in the list
-//   count:     the number of the elements in the list
-void SerializableObject::SerializeValueList(std::vector<byte>* ser_data,
-                                            const byte* raw_data,
-                                            int size,
-                                            int count) const {
-  ASSERT(ser_data, (_T("")));
-  ASSERT(size > 0, (_T("")));
-
-  // Serialize the size and count values
-  SerializeSizeAndCount(ser_data, size, count);
-
-  // Push data
-  if (count > 0) {
-    // Get current size
-    int pos = ser_data->size();
-
-    // Adjust the size of the data buffer
-    ser_data->resize(ser_data->size() + count * size);
-
-    // Get pointer to the position of data buffer we start to write
-    byte* ptr = &((*ser_data)[pos]);
-
-    // Copy data
-    memcpy(ptr, raw_data, count * size);
-  }
-}
-
-// Deserialize
-bool SerializableObject::Deserialize(byte* data, int size, uint32 version) {
-  ASSERT(data, (_T("")));
-  ASSERT(size > 0, (_T("")));
-
-  byte* tail = data + size;
-  byte** data_ptr = &data;
-  if (!DeserializeHelper(data_ptr, size, version))
-    return false;
-
-  if (*data_ptr != tail) {
-    UTIL_LOG(LE, (_T("[SerializableObject::Deserialize]")
-                  _T("[failed to deserialize all data]")));
-    return false;
-  }
-
-  return true;
-}
-
-// Deserialize helper
-bool SerializableObject::DeserializeHelper(byte** data,
-                                           int size,
-                                           uint32 version) {
-  ASSERT(data, (_T("")));
-  ASSERT(size > 0, (_T("")));
-
-  byte* tail = *data + size;
-
-  for (size_t i = 0; i < members_.size(); ++i) {
-    // Ignore those members which are persisted in newer versions
-    if (version != kLatestSerializableVersion &&
-        members_[i].version  > version) {
-      continue;
-    }
-
-    switch (members_[i].type) {
-      case SERIALIZABLE_VALUE_TYPE:
-        if (*data + members_[i].size > tail) {
-          UTIL_LOG(L6, (_T("[SerializableObject::DeserializeHelper]")
-                        _T("[overflow when deserializing value type]")));
-          return false;
-        }
-        memcpy(members_[i].ptr, *data, members_[i].size);
-        *data += members_[i].size;
-        break;
-
-      case SERIALIZABLE_CSTRING: {
-        std::vector<byte> deser_data;
-        if (!DeserializeValueList(&deser_data,
-                                  members_[i].size,
-                                  data,
-                                  tail - *data))
-          return false;
-        CString* s = reinterpret_cast<CString*>(members_[i].ptr);
-        if (deser_data.size() != 0) {
-          s->SetString(reinterpret_cast<const TCHAR*>(&deser_data.front()),
-                       deser_data.size() / members_[i].size);
-        } else {
-          s->SetString(_T(""));
-        }
-        break;
-      }
-
-      case SERIALIZABLE_NESTED_OBJECT: {
-        SerializableObject* nested_obj =
-            reinterpret_cast<SerializableObject*>(members_[i].ptr);
-        if (!nested_obj->DeserializeHelper(data, size, version))
-          return false;
-        break;
-      }
-
-      case SERIALIZABLE_VECTOR | SERIALIZABLE_VALUE_TYPE: {
-        // Hack: coerce vector<T> to vector<byte>
-        std::vector<byte>* v =
-            reinterpret_cast<std::vector<byte>*>(members_[i].ptr);
-        if (!DeserializeValueList(v, members_[i].size, data, tail - *data))
-          return false;
-        break;
-      }
-
-      case SERIALIZABLE_VECTOR | SERIALIZABLE_CSTRING: {
-        std::vector<CString>* v =
-              reinterpret_cast<std::vector<CString>*>(members_[i].ptr);
-        int count = 0;
-        if (!DeserializeSizeAndCount(&count, 1, data, tail - *data))
-          return false;
-        for (int j = 0; j < count; ++j) {
-          std::vector<byte> deser_data;
-          if (!DeserializeValueList(&deser_data,
-                                    members_[i].size,
-                                    data,
-                                    tail - *data))
-            return false;
-
-          CString s;
-          if (deser_data.size() != 0) {
-            s = CString(reinterpret_cast<const TCHAR*>(&deser_data.front()),
-                        deser_data.size() / members_[i].size);
-          }
-          v->push_back(s);
-        }
-        break;
-      }
-
-      case SERIALIZABLE_VECTOR | SERIALIZABLE_NESTED_OBJECT: {
-        if (!DeserializeVectorNestedObject(data,
-                                           tail - *data,
-                                           members_[i].ptr,
-                                           version))
-          return false;
-        break;
-      }
-
-      default:
-        ASSERT(false, (_T("")));
-        break;
-    }
-  }
-
-  return true;
-}
-
-// Serialize the size and count values
-bool SerializableObject::DeserializeSizeAndCount(int* count,
-                                                 int size,
-                                                 byte** ser_data,
-                                                 int ser_size) const {
-  ASSERT(ser_data, (_T("")));
-  ASSERT(count, (_T("")));
-
-  byte* ser_tail = *ser_data + ser_size;
-
-  // Check to make sure that the serialization data should at least contain
-  // 'size' and 'count'
-  if (*ser_data + 2 * sizeof(int) > ser_tail) {
-    UTIL_LOG(L6, (_T("[SerializableObject::DeserializeSizeAndCount]")
-                  _T("[overflow when deserializing size and count]")));
-    return false;
-  }
-
-  // Get size
-  // If the passing size is 0, skip the size check
-  int size2 = *(reinterpret_cast<const int*>(*ser_data));
-  *ser_data += sizeof(int);
-  if (size && size != size2)
-    return false;
-
-  // Get count
-  *count = *(reinterpret_cast<const int*>(*ser_data));
-  *ser_data += sizeof(int);
-
-  return true;
-}
-
-// Deserialize a list of value-typed elements
-//
-// Args:
-//   ser_data:  pointer to the vector for the serialized data
-//   size:      the size of the element in the list
-//   raw_data:  pointer to the raw data to be serialized
-//   ser_size:  size of the serization data
-bool SerializableObject::DeserializeValueList(std::vector<byte>* raw_data,
-                                              int size,
-                                              byte** ser_data,
-                                              int ser_size) {
-  ASSERT(raw_data, (_T("")));
-  ASSERT(ser_data, (_T("")));
-
-  byte* ser_tail = *ser_data + ser_size;
-
-  // Deserialize the size and count values
-  int count = 0;
-  bool ret = DeserializeSizeAndCount(&count, size, ser_data, ser_size);
-  if (!ret)
-    return false;
-
-  // Check to make sure that the serialization data is in the right size
-  if (*ser_data + count * size > ser_tail) {
-    UTIL_LOG(L6, (_T("[SerializableObject::DeserializeValueList]")
-                  _T("[overflow when deserializing value list]")));
-    return false;
-  }
-
-  // Get data
-  raw_data->resize(size * count);
-  if (count > 0) {
-    memcpy(&raw_data->front(), *ser_data, count * size);
-    *ser_data += count * size;
-  }
-
-  return true;
-}
-
-}  // namespace omaha
-
diff --git a/common/serializable_object.h b/common/serializable_object.h
deleted file mode 100644
index 7f7877f..0000000
--- a/common/serializable_object.h
+++ /dev/null
@@ -1,373 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Declares class SerializableObject
-//
-// Provides the base class framework for those objects to be serialized
-//
-// Currently we support the serialization for the following member type
-//
-//   1) Value-type object
-//   2) CString
-//   3) Nested serializable object
-//   4) Vector of the value-type objects
-//   5) Vector of CString
-//   6) Vector of serializable objects
-//
-// Usage:
-//
-//   1) Declare the object class, which you want to serialize, to be derived
-//      from SerializableObject
-//   2) In its constructor, call AddSerializableMember(...) to add those fields
-//      to be included in the serialization
-//   3) If you need to serialize a vector of serializable objects,
-//      a) The inner object class has to implement copy constructor and
-//         operator =
-//      b) The outer object class has to override SerializeVectorNestedObject()
-//         and DeserializeVectorNestedObject() (see sample)
-//
-// Versioning support:
-//
-//   To add new fields in the new version, call
-//        AddSerializableMember(version, &member);
-//   If "version" is not given, it is 0 by default.
-//
-//   To deserialize the data for latest version, call
-//        Deserialize(data, size, kLatestSerializableVersion);
-//   To deserialize the data for a particular version, call
-//        Deserialize(data, size, version);
-//
-// Sample:
-//
-//   class FooObject : public SerializableObject {
-//     ...
-//   };
-//
-//   class BarObject : public SerializableObject {
-//    public:
-//     BarObject() {
-//      AddSerializableMember(&value1_);
-//      AddSerializableMember(&value2_);
-//      AddSerializableMember(&value3_);
-//      AddSerializableMember(&value4_);
-//      AddSerializableMember(&value5_);
-//      AddSerializableMember(&value6_);
-//      AddSerializableMember(1, &value7_);     // New version
-//     }
-//
-//    protected:
-//     virtual bool SerializeVectorNestedObject(std::vector<byte>* data,
-//                                              const byte* ptr) const {
-//       ASSERT(data, (_T("")));
-//       ASSERT(ptr, (_T("")));
-//       if (ptr == reinterpret_cast<const byte*>(&value6_))
-//         return SerializeVectorNestedObjectHelper(data, &value6_);
-//       return false;
-//     }
-//
-//     virtual bool DeserializeVectorNestedObject(byte** data,
-//                                                int size,
-//                                                byte* ptr,
-//                                                uint32 version) {
-//       ASSERT(data, (_T("")));
-//       ASSERT(*data, (_T("")));
-//       ASSERT(ptr, (_T("")));
-//       if (ptr == reinterpret_cast<byte*>(&value6_))
-//         return DeserializeVectorNestedObjectHelper(data,
-//                                                    size,
-//                                                    &value6_,
-//                                                    version);
-//       return false;
-//     }
-//
-//    private:
-//     int value1_;
-//     CString value2_;
-//     FooObject value3_;
-//     std::vector<byte> value4_;
-//     std::vector<CString> value5_;
-//     std::vector<BarObject> value6_;
-//     int value7_;
-//   };
-//
-// Binary format:
-//
-//   1) Value type: data
-//      e.g.  100           =>   64 00 00 00
-//   2) CString:    size count data
-//      e.g.  "ABC"         =>   02 00 00 00 03 00 00 00 41 00 42 00 43 00
-//   3) Vector:     size count data
-//      e.g.  vector<int> = {1, 2}
-//                          =>   04 00 00 00 02 00 00 00 01 00 00 00 02 00 00 00
-//
-// TODO(omaha):
-//   1) Define struct TypeTrait for all type-related info
-//   2) Initialize TypeTrait on per-type basis instead of per-object basis
-
-#ifndef OMAHA_COMMON_SERIALIZABLE_OBJECT_H_
-#define OMAHA_COMMON_SERIALIZABLE_OBJECT_H_
-
-#include <vector>
-#include "base/basictypes.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/type_utils.h"
-
-namespace omaha {
-
-// Constants
-const uint32 kLatestSerializableVersion = 0xFFFFFFFF;
-
-// Serializable object
-class SerializableObject {
- private:
-  // Define SerializableMemberType, for internal use
-  typedef uint32 SerializableMemberType;
-
-  #define SERIALIZABLE_VALUE_TYPE     1
-  #define SERIALIZABLE_CSTRING        2
-  #define SERIALIZABLE_NESTED_OBJECT  3
-  #define SERIALIZABLE_VECTOR         0x8000
-
-  // Serializable member info
-  struct SerializableMemberInfo {
-    byte* ptr;                      // Pointers to the serializable member
-    SerializableMemberType type;    // Type of the serializable member
-    int size;                       // Size of the serializable member
-    uint32 version;                 // Version when the member is added
-
-    SerializableMemberInfo()
-        : ptr(NULL), type(SERIALIZABLE_VALUE_TYPE), size(0) {}
-  };
-
- public:
-  // Constructor
-  SerializableObject() {}
-
-  // Destructor
-  virtual ~SerializableObject() {}
-
-  // Serialize
-  bool Serialize(std::vector<byte>* data) const;
-
-  // Deserialize the data for the latest version
-  bool Deserialize(byte* data, int size) {
-    return Deserialize(data, size, kLatestSerializableVersion);
-  }
-
-  // Deserialize the data for a particular version
-  bool Deserialize(byte* data, int size, uint32 version);
-
- protected:
-  // Clear the serializable member list
-  void ClearSerializableMemberList() {
-    members_.clear();
-  }
-
-  // Add value-typed member to the serializable member list
-  template<typename T>
-  void AddSerializableMember(T* ptr) {
-    return AddSerializableMember(0, ptr);
-  }
-
-  // Add value-typed member to the serializable member list
-  template<typename T>
-  void AddSerializableMember(uint32 version, T* ptr) {
-    if (SUPERSUBCLASS(SerializableObject, T)) {
-      #pragma warning(push)
-      // reinterpret_cast used between related classes
-      #pragma warning(disable : 4946)
-      return AddSerializableMember(version,
-                                   reinterpret_cast<SerializableObject*>(ptr),
-                                   sizeof(T));
-      #pragma warning(pop)
-    }
-    SerializableMemberInfo member;
-    member.ptr = reinterpret_cast<byte*>(ptr);
-    member.type = SERIALIZABLE_VALUE_TYPE;
-    member.size = sizeof(T);
-    member.version = version;
-    members_.push_back(member);
-  }
-
-  // Add CString-typed member to the serializable member list
-  void AddSerializableMember(CString* ptr) {
-    AddSerializableMember(0, ptr);
-  }
-
-  // Add CString-typed member to the serializable member list
-  void AddSerializableMember(uint32 version, CString* ptr) {
-    SerializableMemberInfo member;
-    member.ptr = reinterpret_cast<byte*>(ptr);
-    member.type = SERIALIZABLE_CSTRING;
-    member.size = sizeof(TCHAR);
-    member.version = version;
-    members_.push_back(member);
-  }
-
-  // Add nested serializable member to the serializable member list
-  void AddSerializableMember(SerializableObject* ptr, int size) {
-    AddSerializableMember(0, ptr, size);
-  }
-
-  // Add nested serializable member to the serializable member list
-  void AddSerializableMember(uint32 version,
-                             SerializableObject* ptr,
-                             int size) {
-    SerializableMemberInfo member;
-    member.ptr = reinterpret_cast<byte*>(ptr);
-    member.type = SERIALIZABLE_NESTED_OBJECT;
-    member.size = size;
-    member.version = version;
-    members_.push_back(member);
-  }
-
-  // Add vector-typed member to the serializable member list
-  template<typename T>
-  void AddSerializableMember(std::vector<T>* ptr) {
-    AddSerializableMember(0, ptr);
-  }
-
-  // Add vector-typed member to the serializable member list
-  template<typename T>
-  void AddSerializableMember(uint32 version, std::vector<T>* ptr) {
-    SerializableMemberInfo member;
-    member.ptr = reinterpret_cast<byte*>(ptr);
-    member.version = version;
-
-    if (SUPERSUBCLASS(CString, T)) {
-      member.type =
-          static_cast<SerializableMemberType>(SERIALIZABLE_VECTOR |
-                                              SERIALIZABLE_CSTRING);
-      member.size = sizeof(TCHAR);
-    } else if (SUPERSUBCLASS(SerializableObject, T)) {
-      member.type =
-          static_cast<SerializableMemberType>(SERIALIZABLE_VECTOR |
-                                              SERIALIZABLE_NESTED_OBJECT);
-      member.size = sizeof(T);
-    } else {
-      member.type =
-          static_cast<SerializableMemberType>(SERIALIZABLE_VECTOR |
-                                              SERIALIZABLE_VALUE_TYPE);
-      member.size = sizeof(T);
-    }
-
-    members_.push_back(member);
-  }
-
-  // If there is a vector of SerializableObject to be serialized, the derived
-  // class need to provide the implementation
-  virtual bool SerializeVectorNestedObject(std::vector<byte>*,
-                                           const byte*) const {
-    ASSERT(false, (_T("Provide the implementation in the derived class.")));
-    return false;
-  }
-
-  // Helper method to serialize a vector of SerializableObject
-  template<typename T>
-  bool SerializeVectorNestedObjectHelper(std::vector<byte>* data,
-                                         const std::vector<T>* list) const {
-    ASSERT(data, (_T("")));
-    ASSERT(list, (_T("")));
-    ASSERT(SUPERSUBCLASS(SerializableObject, T), (_T("")));
-
-    // Size of SerializableObject is unknown
-    SerializeSizeAndCount(data, 0, list->size());
-    for (size_t i = 0; i < list->size(); ++i) {
-      // To work around compiler complaint while using dynamic_cast
-      SerializableObject* so =
-          const_cast<SerializableObject*>(
-              static_cast<const SerializableObject*>(&(*list)[i]));
-      bool res = so->Serialize(data);
-      if (!res)
-        return false;
-    }
-    return true;
-  }
-
-  // If there is a vector of SerializableObject to be serialized, the derived
-  // class need to provide the implementation
-  virtual bool DeserializeVectorNestedObject(byte**, int, byte*, uint32) {
-    ASSERT(false, (_T("provide the implementation in the derived class.")));
-    return false;
-  }
-
-  // Helper method to deserialize a vector of SerializableObject
-  template<typename T>
-  bool DeserializeVectorNestedObjectHelper(byte** data,
-                                           int size,
-                                           std::vector<T>* list,
-                                           uint32 version) {
-    ASSERT(data, (_T("")));
-    ASSERT(*data, (_T("")));
-    ASSERT(size, (_T("")));
-    ASSERT(list, (_T("")));
-    ASSERT(SUPERSUBCLASS(SerializableObject, T), (_T("")));
-
-    byte* tail = *data + size;
-
-    // Size of SerializableObject is unknown
-    int count = 0;
-    bool res = DeserializeSizeAndCount(&count, 0, data, size);
-    if (!res)
-      return false;
-
-    for (int i = 0; i < count; ++i) {
-      T obj;
-      bool res = obj.DeserializeHelper(data, tail - *data, version);
-      if (!res)
-        return false;
-      list->push_back(obj);
-    }
-    return true;
-  }
-
- private:
-  // Serialize the size and count values
-  void SerializeSizeAndCount(std::vector<byte>* data,
-                             int size,
-                             int count) const;
-
-  // Serialize a list of value-typed elements
-  void SerializeValueList(std::vector<byte>* ser_data,
-                          const byte* raw_data,
-                          int size,
-                          int count) const;
-
-  // Deserialize helper
-  bool DeserializeHelper(byte** data, int size, uint32 version);
-
-  // Deserialize the size and count values
-  bool DeserializeSizeAndCount(int* count,
-                               int size,
-                               byte** ser_data,
-                               int ser_size) const;
-
-  // Deserialize a list of value-typed elements
-  bool DeserializeValueList(std::vector<byte>* raw_data,
-                            int size,
-                            byte** ser_data,
-                            int ser_size);
-
-  // List of serializable members
-  std::vector<SerializableMemberInfo> members_;
-
-  // We need to initialize TypeTrait on per-type basis instead of per-object
-  // basis and remove the following use of macro.
-  DISALLOW_EVIL_CONSTRUCTORS(SerializableObject);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_SERIALIZABLE_OBJECT_H_
diff --git a/common/serializable_object_unittest.cc b/common/serializable_object_unittest.cc
deleted file mode 100644
index 68f3581..0000000
--- a/common/serializable_object_unittest.cc
+++ /dev/null
@@ -1,248 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/serializable_object.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-// Template test function to round-trip an object through its serialized form.
-// Requires the object to implement SetTestValues() and VerifyTestValues().
-template <class T>
-void TestSerializeRoundTrip() {
-  T in;
-  T out;
-  std::vector<byte> data;
-
-  in.SetTestValues();
-  in.VerifyTestValues();  // sanity check to catch broken tests
-  EXPECT_TRUE(in.Serialize(&data));
-  EXPECT_TRUE(out.Deserialize(&data[0], data.size()));
-  out.VerifyTestValues();
-}
-
-
-// Ordinary values.
-class SimpleValues : public SerializableObject {
- public:
-  SimpleValues() {
-    AddSerializableMember(&value1_);
-    AddSerializableMember(&value2_);
-    AddSerializableMember(&value3_);
-    AddSerializableMember(&value4_);
-  }
-
-  virtual void SetTestValues() {
-    value1_ = 452;
-    value2_ = 'Z';
-    value3_ = false;
-    value4_ = 9276554;
-  }
-
-  virtual void VerifyTestValues() {
-    EXPECT_EQ(452, value1_);
-    EXPECT_EQ('Z', value2_);
-    EXPECT_EQ(false, value3_);
-    EXPECT_EQ(9276554, value4_);
-  }
-
- private:
-  int value1_;
-  char value2_;
-  bool value3_;
-  int value4_;
-};
-
-TEST(SerializableObjectTest, SimpleValues) {
-  TestSerializeRoundTrip<SimpleValues>();
-}
-
-
-// Strings.
-const TCHAR kString1[] = _T("an example\tvalue\n");
-const TCHAR kString2[] = _T("");
-const TCHAR kString3[] = _T("and the mome raths outgrabe");
-
-class StringValues : public SerializableObject {
- public:
-  StringValues() {
-    AddSerializableMember(&string1_);
-    AddSerializableMember(&string2_);
-    AddSerializableMember(&string3_);
-  }
-
-  virtual void SetTestValues() {
-    string1_ = kString1;
-    string2_ = kString2;
-    string3_ = kString3;
-  }
-
-  virtual void VerifyTestValues() {
-    EXPECT_STREQ(kString1, string1_);
-    EXPECT_STREQ(kString2, string2_);
-    EXPECT_STREQ(kString3, string3_);
-  }
-
- private:
-  CString string1_;
-  CString string2_;
-  CString string3_;
-};
-
-TEST(SerializableObjectTest, StringValues) {
-  TestSerializeRoundTrip<StringValues>();
-}
-
-
-// Nested objects.
-class NestedObjects : public SerializableObject {
- public:
-  NestedObjects() {
-    AddSerializableMember(&simple_values_);
-    AddSerializableMember(&string_values_);
-  }
-
-  virtual void SetTestValues() {
-    simple_values_.SetTestValues();
-    string_values_.SetTestValues();
-  }
-
-  virtual void VerifyTestValues() {
-    simple_values_.VerifyTestValues();
-    string_values_.VerifyTestValues();
-  }
-
- private:
-  SimpleValues simple_values_;
-  StringValues string_values_;
-};
-
-TEST(SerializableObjectTest, NestedObjects) {
-  TestSerializeRoundTrip<NestedObjects>();
-}
-
-
-// Vector of values.
-class ValueVector : public SerializableObject {
- public:
-  ValueVector() {
-    AddSerializableMember(&vector_);
-    AddSerializableMember(&empty_vector_);
-  }
-
-  virtual void SetTestValues() {
-    vector_.push_back(5);
-    vector_.push_back(8);
-    vector_.push_back(13);
-  }
-
-  virtual void VerifyTestValues() {
-    EXPECT_EQ(0, empty_vector_.size());
-    EXPECT_EQ(3, vector_.size());
-    EXPECT_EQ(5, vector_[0]);
-    EXPECT_EQ(8, vector_[1]);
-    EXPECT_EQ(13, vector_[2]);
-  }
-
- private:
-  std::vector<int> vector_;
-  std::vector<int> empty_vector_;
-};
-
-TEST(SerializableObjectTest, ValueVector) {
-  TestSerializeRoundTrip<ValueVector>();
-}
-
-
-// Vector of objects.
-class InnerObject : public SerializableObject {
- public:
-  InnerObject() : value_(-1) {
-    AddSerializableMember(&value_);
-  }
-
-  explicit InnerObject(int i) : value_(i) {
-    AddSerializableMember(&value_);
-  }
-
-  InnerObject(const InnerObject& other) {
-    AddSerializableMember(&value_);
-    value_ = other.value_;
-  }
-
-  InnerObject& operator=(const InnerObject& other) {
-    value_ = other.value_;
-    return *this;
-  }
-
-  int value() {
-    return value_;
-  }
-
- private:
-  int value_;
-};
-
-class ObjectVector : public SerializableObject {
- public:
-  ObjectVector() {
-    AddSerializableMember(&vector_);
-  }
-
-  virtual void SetTestValues() {
-    vector_.push_back(InnerObject(21));
-    vector_.push_back(InnerObject(34));
-    vector_.push_back(InnerObject(55));
-  }
-
-  virtual void VerifyTestValues() {
-    EXPECT_EQ(3, vector_.size());
-    EXPECT_EQ(21, vector_[0].value());
-    EXPECT_EQ(34, vector_[1].value());
-    EXPECT_EQ(55, vector_[2].value());
-  }
-
-  virtual bool SerializeVectorNestedObject(std::vector<byte>* data,
-                                           const byte* ptr) const {
-    EXPECT_TRUE(data);
-    EXPECT_TRUE(ptr);
-    if (ptr == reinterpret_cast<const byte*>(&vector_))
-      return SerializeVectorNestedObjectHelper(data, &vector_);
-    return false;
-  }
-
-  virtual bool DeserializeVectorNestedObject(byte** data,
-                                             int size,
-                                             byte* ptr,
-                                             uint32 version) {
-    EXPECT_TRUE(data);
-    EXPECT_TRUE(ptr);
-    if (ptr == reinterpret_cast<byte*>(&vector_)) {
-      return DeserializeVectorNestedObjectHelper(data, size,
-                                                 &vector_, version);
-    }
-    return false;
-  }
-
- private:
-  std::vector<InnerObject> vector_;
-};
-
-TEST(SerializableObjectTest, ObjectVector) {
-  TestSerializeRoundTrip<ObjectVector>();
-}
-
-}  // namespace omaha
-
diff --git a/common/service_utils.cc b/common/service_utils.cc
deleted file mode 100644
index 7bebfac..0000000
--- a/common/service_utils.cc
+++ /dev/null
@@ -1,360 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// Service-related utilities.
-//
-
-#include "omaha/common/service_utils.h"
-
-#include <windows.h>
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/smart_handle.h"
-#include "omaha/common/string.h"
-#include "omaha/common/timer.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-HRESULT ScmDatabase::EnumerateServices(
-    ScmDatabase::EnumerateServicesCallback callback,
-    void* callback_context) {
-  ASSERT1(callback);
-  if (!callback)
-    return E_POINTER;
-
-  const wchar_t* kServicesRegKeyFromRoot =
-    L"SYSTEM\\CurrentControlSet\\Services";
-
-  HRESULT hr = E_FAIL;
-
-  RegKey services_key;
-  if (FAILED(hr = services_key.Open(HKEY_LOCAL_MACHINE,
-                                    kServicesRegKeyFromRoot,
-                                    KEY_ENUMERATE_SUB_KEYS))) {
-    ASSERT1(false);
-    REPORT(false, R_ERROR, (L"Couldn't open services subkey, hr=0x%x", hr),
-           9834572);
-    return hr;
-  }
-
-  CString service_name;
-  int key_index = 0;
-  while (SUCCEEDED(hr = services_key.GetSubkeyNameAt(key_index++,
-                                                     &service_name))) {
-    hr = callback(callback_context, service_name);
-    if (FAILED(hr) || hr == S_FALSE) {
-      // Callback asked to terminate enumeration.
-      return hr;
-    }
-  }
-
-  if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)) {
-    ASSERT1(false);
-    REPORT(false, R_ERROR, (L"Failed enumerating service subkeys: 0x%x", hr),
-           1499372);
-    return hr;
-  }
-
-  return S_OK;
-}
-
-bool ScmDatabase::IsServiceStateEqual(SC_HANDLE service, DWORD state) {
-  ASSERT1(service);
-
-  DWORD bytes_needed_ignored = 0;
-  byte buffer[8 * 1024] = { 0 };
-  QUERY_SERVICE_CONFIG* service_config =
-    reinterpret_cast<QUERY_SERVICE_CONFIG*>(buffer);
-  if (!::QueryServiceConfig(service, service_config, sizeof(buffer),
-                            &bytes_needed_ignored)) {
-    ASSERT(false, (L"Failed to query service config, perhaps handle is missing "
-                   L"SERVICE_QUERY_CONFIG rights?"));
-    return false;
-  }
-
-  return (service_config[0].dwStartType == state);
-}
-
-bool ScmDatabase::IsServiceMarkedDeleted(SC_HANDLE service) {
-  ASSERT1(service);
-
-  // Services that have been marked deleted are always in the
-  // SERVICE_DISABLED state.  The converse is not true, and unfortunately
-  // there is no way to check if a service has been marked deleted except by
-  // attempting to change one of its configuration parameters, at which
-  // point you get a specific error indicating it has been marked deleted.
-  //
-  // The following call to ChangeServiceConfig does not actually change any
-  // of the service's configuration, but should hopefully return the
-  // specific error if the service has been marked deleted.
-  if (!::ChangeServiceConfig(service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
-                             SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL,
-                             NULL, NULL, NULL) &&
-      ::GetLastError() == ERROR_SERVICE_MARKED_FOR_DELETE) {
-    ASSERT1(IsServiceStateEqual(service, SERVICE_DISABLED));
-    return true;
-  } else {
-    return false;
-  }
-}
-
-HRESULT ServiceInstall::UninstallByPrefix(void* context,
-                                          const wchar_t* service_name) {
-  ASSERT1(context != NULL);
-  if (!context)
-    return E_POINTER;
-
-  UninstallByPrefixParams* params =
-    reinterpret_cast<UninstallByPrefixParams*>(context);
-
-  if (String_StartsWith(service_name, params->prefix, true) &&
-      lstrcmpiW(service_name, params->unless_matches) != 0) {
-    // The service must be stopped before attempting to remove it from the
-    // database. Otherwise, the SCM database remains dirty and all service
-    // functions return ERROR_SERVICE_MARKED_FOR_DELETE until the system is
-    // restarted.
-    StopService(service_name);
-
-    scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
-    if (!scm) {
-      HRESULT hr = HRESULTFromLastError();
-      ASSERT1(false);
-      REPORT(false, R_ERROR, (L"Failed to open SCM: 0x%x", hr), 77223399);
-      return hr;
-    }
-    scoped_service service(::OpenService(get(scm),
-                                         service_name,
-                                         SERVICE_CHANGE_CONFIG | DELETE));
-    if (service) {
-      // The service may not get deleted immediately; if there are handles to
-      // it open, it won't get deleted until the last one is closed.  If the
-      // service is running, it won't get deleted immediately but rather will be
-      // marked for deletion (which happens on next reboot).  Having to wait for
-      // a while and even until reboot doesn't matter much to us as our new
-      // service is installed under a new name and we are just cleaning up old
-      // ones.
-      if (!::DeleteService(get(service))) {
-        // We do not assert but just report so that we know if this happens
-        // abnormally often.
-        if (::GetLastError() == ERROR_SERVICE_MARKED_FOR_DELETE) {
-          REPORT(false, R_INFO,
-                 (L"Failed to immediately delete service %s", service_name),
-                 5440098);
-        } else {
-          ASSERT(false, (L"Failed to delete service %s, error %d",
-                         service_name, ::GetLastError()));
-        }
-        // DO NOT return an error here; we want to keep going through all the
-        // services.
-      } else {
-        SERVICE_LOG(L1,
-                    (L"Deleted old service %s", service_name));
-      }
-    } else {
-      // Per documentation of the EnumerateServicesCallback interface we can
-      // expect not to be able to open the service with one of the following two
-      // error codes, because of discrepancies between the registry and the SCM
-      // database in memory.
-      DWORD last_error = ::GetLastError();
-      ASSERT(last_error == ERROR_SERVICE_DOES_NOT_EXIST ||
-             last_error == ERROR_INVALID_NAME,
-             (L"Failed to open service %s, last error %d", service_name,
-              last_error));
-      REPORT(last_error == ERROR_SERVICE_DOES_NOT_EXIST ||
-             last_error == ERROR_INVALID_NAME, R_ERROR,
-             (L"Failed to open service %s, last error %d", service_name,
-              last_error), 5576234);
-    }
-  }
-
-  return S_OK;
-}
-
-CString ServiceInstall::GenerateServiceName(const TCHAR* service_prefix) {
-  FILETIME ft = {0};
-  ::GetSystemTimeAsFileTime(&ft);
-  CString versioned_service_name;
-  versioned_service_name.Format(_T("%s%x%x"),
-                                service_prefix,
-                                ft.dwHighDateTime,
-                                ft.dwLowDateTime);
-
-  ASSERT1(!versioned_service_name.IsEmpty());
-  return versioned_service_name;
-}
-
-HRESULT ServiceInstall::UninstallServices(const TCHAR* service_prefix,
-                                          const TCHAR* exclude_service) {
-  SERVICE_LOG(L2, (L"ServiceInstall::UninstallServices"));
-
-  UninstallByPrefixParams params = {
-    service_prefix,
-    exclude_service,
-  };
-
-  return ScmDatabase::EnumerateServices(UninstallByPrefix, &params);
-}
-
-bool ServiceInstall::CanInstallWithoutReboot() {
-  scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
-  if (!scm) {
-    ASSERT1(false);
-    REPORT(false, R_ERROR, (L"Failed to open SCM: %d", ::GetLastError()),
-           77224449);
-    return false;  // request reboot just in case
-  }
-
-  scoped_service service(::OpenService(get(scm),
-                                       _T("gupdate"),
-                                       SERVICE_QUERY_CONFIG |
-                                       SERVICE_CHANGE_CONFIG));
-  if (!service) {
-    DWORD last_error = ::GetLastError();
-    if (last_error == ERROR_ACCESS_DENIED ||
-        last_error == ERROR_INVALID_HANDLE) {
-      // unable to verify the service is fully deleted, so request reboot
-      ASSERT(false, (L"Expected access and correct handle"));
-      return false;
-    } else {
-      // service does not exist
-      return true;
-    }
-  }
-
-  return !ScmDatabase::IsServiceMarkedDeleted(get(service));
-}
-
-HRESULT ServiceInstall::StopService(const CString& service_name) {
-  SERVICE_LOG(L1, (_T("[ServiceInstall::StopService]")));
-
-  scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
-  if (!scm) {
-    return HRESULTFromLastError();
-  }
-  scoped_service service(::OpenService(get(scm),
-                                       service_name,
-                                       SERVICE_QUERY_STATUS | SERVICE_STOP));
-  if (!service) {
-    return HRESULTFromLastError();
-  }
-
-  SERVICE_STATUS status = {0};
-  if (::QueryServiceStatus(get(service), &status)) {
-    if (status.dwCurrentState != SERVICE_STOPPED &&
-        status.dwCurrentState != SERVICE_STOP_PENDING) {
-      // Stop the service.
-      SetZero(status);
-      if (!::ControlService(get(service), SERVICE_CONTROL_STOP, &status)) {
-        return HRESULTFromLastError();
-      }
-    }
-  }
-
-  if (status.dwCurrentState != SERVICE_STOPPED) {
-    SERVICE_LOG(L1, (_T("[Service is stopping...]")));
-
-    const int kWaitForServiceToStopMs = 8000;
-    LowResTimer t(true);
-
-    while (status.dwCurrentState != SERVICE_STOPPED &&
-           t.GetMilliseconds() < kWaitForServiceToStopMs) {
-      const int kSleepTimeMs = 50;
-      ::Sleep(kSleepTimeMs);
-      SetZero(status);
-      VERIFY1(::QueryServiceStatus(get(service), &status));
-      SERVICE_LOG(L1, (_T("[Waiting for service to stop %d]"),
-          static_cast<int>(t.GetMilliseconds())));
-    }
-
-    if (status.dwCurrentState != SERVICE_STOPPED) {
-      SERVICE_LOG(LEVEL_WARNING, (_T("[Service did not stop! Not good...]")));
-      return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
-    }
-  }
-
-  ASSERT1(status.dwCurrentState == SERVICE_STOPPED);
-  SERVICE_LOG(L1, (_T("[ServiceInstall::StopService - service stopped]")));
-  return S_OK;
-}
-
-bool ServiceInstall::IsServiceInstalled(const TCHAR* service_name) {
-  ASSERT1(service_name);
-  scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT));
-  if (!scm) {
-    return false;
-  }
-  scoped_service service(::OpenService(get(scm),
-                                       service_name,
-                                       SERVICE_QUERY_CONFIG));
-  return valid(service);
-}
-
-// TODO(Omaha): Move all functions under a common ServiceUtils namespace.
-bool ServiceUtils::IsServiceRunning(const TCHAR* service_name) {
-  ASSERT1(service_name);
-
-  scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT));
-  if (!scm) {
-    SERVICE_LOG(LE, (_T("[OpenSCManager fail][0x%x]"), HRESULTFromLastError()));
-    return false;
-  }
-
-  scoped_service service(::OpenService(get(scm),
-                                       service_name,
-                                       SERVICE_QUERY_STATUS));
-  if (!service) {
-    SERVICE_LOG(LE, (_T("[OpenService failed][%s][0x%x]"),
-                     service_name, HRESULTFromLastError()));
-    return false;
-  }
-
-  SERVICE_STATUS status = {0};
-  if (!::QueryServiceStatus(get(service), &status)) {
-    SERVICE_LOG(LE, (_T("[QueryServiceStatus failed][%s][0x%x]"),
-                     service_name, HRESULTFromLastError()));
-    return false;
-  }
-
-  return status.dwCurrentState == SERVICE_RUNNING ||
-         status.dwCurrentState == SERVICE_START_PENDING;
-}
-
-bool ServiceUtils::IsServiceDisabled(const TCHAR* service_name) {
-  ASSERT1(service_name);
-
-  scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT));
-  if (!scm) {
-    SERVICE_LOG(LE, (_T("[OpenSCManager fail][0x%x]"), HRESULTFromLastError()));
-    return false;
-  }
-
-  scoped_service service(::OpenService(get(scm),
-                                       service_name,
-                                       SERVICE_QUERY_CONFIG));
-  if (!service) {
-    SERVICE_LOG(LE, (_T("[OpenService failed][%s][0x%x]"),
-                     service_name, HRESULTFromLastError()));
-    return false;
-  }
-
-  return ScmDatabase::IsServiceStateEqual(get(service), SERVICE_DISABLED);
-}
-
-}  // namespace omaha
-
diff --git a/common/service_utils.h b/common/service_utils.h
deleted file mode 100644
index 31b654c..0000000
--- a/common/service_utils.h
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Service-related utilities.
-
-#ifndef OMAHA_COMMON_SERVICE_UTILS_H__
-#define OMAHA_COMMON_SERVICE_UTILS_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include "base/basictypes.h"
-
-namespace omaha {
-
-// Utility functions for working with the SCM database.
-class ScmDatabase {
- public:
-  // Callback function type for EnumerateServices. This gets called for each
-  // service in the registry.
-  //
-  // @param callback_context Passed unchanged from the caller of
-  // EnumerateServices to the callback function.
-  // @param service_name The name of the service (not the display name but
-  // rather the canonical name, to be used with e.g. ::OpenService()).  Note
-  // that because this function is based on enumerating the registry, it's
-  // possible that services that were recently deleted will show up in the
-  // enumeration; therefore, it should not be considered an error if you try
-  // to ::OpenService() on this name and it fails with a last error of
-  // ERROR_SERVICE_DOES_NOT_EXIST or possibly ERROR_INVALID_NAME (if
-  // somebody messed up the registry by hand).
-  //
-  // @return S_OK to continue enumeration, S_FALSE or a COM error code to
-  // stop enumeration.  The return value will be propagated to the caller
-  // of Enumerate.
-  //
-  // @note The initial version of this function used EnumServicesStatusEx
-  // but it turns out the function is a fair bit flaky, not returning all
-  // recently created (e.g. created but never started) services.
-  typedef HRESULT(*EnumerateServicesCallback)(void* callback_context,
-                                              const wchar_t* service_name);
-
-  // Calls 'callback' for each of the services in the registry.
-  //
-  // @param callback Callback function to call
-  // @param callback_context Passed unchanged to your callback function
-  //
-  // @return S_OK or a COM error code.
-  static HRESULT EnumerateServices(EnumerateServicesCallback callback,
-                                   void* callback_context);
-
-  // Returns true iff the service passed in is in the indicated state.
-  //
-  // @param service An open handle to a service.  The handle must have at
-  // least SERVICE_QUERY_CONFIG rights.
-  // @param state One of the SERVICE_XXX constants indicating the state of a
-  // service (e.g. SERVICE_DISABLED).
-  //
-  // @return True iff 'service' is in state 'state'.
-  static bool IsServiceStateEqual(SC_HANDLE service, DWORD state);
-
-  // Returns true iff the service passed in has been marked deleted.
-  //
-  // @param service An open handle to a service.  The handle must have at
-  // least SERVICE_QUERY_CONFIG and SERVICE_CHANGE_CONFIG rights.
-  //
-  // @return True iff 'service' has been marked deleted.
-  static bool IsServiceMarkedDeleted(SC_HANDLE service);
-
- private:
-  DISALLOW_EVIL_CONSTRUCTORS(ScmDatabase);
-};
-
-// Utility functions for the service's installation, overinstall etc.
-class ServiceInstall {
- public:
-
-  // Generates a versioned service name based on the current system time.
-  static CString GenerateServiceName(const TCHAR* service_prefix);
-
-  // Uninstalls all versions of the service other than the one that matches
-  // the service name passed in. Pass in NULL to uninstall everything.
-  static HRESULT UninstallServices(const TCHAR* service_prefix,
-                                   const TCHAR* exclude_service);
-
-  static bool IsServiceInstalled(const TCHAR* service_name);
-
-  // @return True if the current service can be installed without rebooting,
-  // false if a reboot is required before it can be installed.  The cases
-  // where the current service can be installed without rebooting are:
-  // a) when no service exists with the current name
-  // b) when there is an existing service with the current name but it is
-  //    not marked for deletion
-  static bool CanInstallWithoutReboot();
-
-  // Given a service name, stops it if it is already running.
-  static HRESULT StopService(const CString& service_name);
-
- protected:
-  // Context passed to the UninstallIfNotCurrent function; this is made a
-  // parameter so we can unit test the function without mucking with the
-  // "actual" services.
-  struct UninstallByPrefixParams {
-    CString prefix;  // prefix of services we want to uninstall
-    CString unless_matches;  // name of current service, to not touch
-  };
-
-  // Uninstalls a given service if it matches a given prefix but does not match
-  // a given full service name.
-  //
-  // This is an ScmDatabase::EnumerateServicesCallback function.
-  //
-  // @param context Pointer to an UninstallByPrefix structure.
-  static HRESULT UninstallByPrefix(void* context, const wchar_t* service_name);
-
- private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(ServiceInstall);
-};
-
-// Service utility functions for querying current state, and eventually more.
-class ServiceUtils {
- public:
-  static bool IsServiceRunning(const TCHAR* service_name);
-  static bool IsServiceDisabled(const TCHAR* service_name);
-
- private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(ServiceUtils);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_SERVICE_UTILS_H__
-
diff --git a/common/service_utils_unittest.cc b/common/service_utils_unittest.cc
deleted file mode 100644
index 3ece94f..0000000
--- a/common/service_utils_unittest.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/service_utils.h"
-#include <lmsname.h>
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-// Set to true when the test callback sees the service indicated in the
-// context parameter.
-bool found_service = false;
-
-HRESULT TestEnumCallback(void* context, const wchar_t* service_name) {
-  EXPECT_TRUE(context);
-
-  const wchar_t* find_service_name = reinterpret_cast<const wchar_t*>(context);
-  if (lstrcmpW(service_name, find_service_name) == 0) {
-    found_service = true;
-  }
-
-  return S_OK;
-}
-
-TEST(ServiceUtilsTest, ScmDatabaseEnumerateServices) {
-  found_service = false;
-  EXPECT_TRUE(SUCCEEDED(ScmDatabase::EnumerateServices(TestEnumCallback,
-                            reinterpret_cast<void*>(_T("Eventlog")))));
-  EXPECT_TRUE(found_service);
-}
-
-TEST(ServiceUtilsTest, IsServiceInstalled) {
-  EXPECT_TRUE(ServiceInstall::IsServiceInstalled(SERVICE_SCHEDULE));
-  EXPECT_FALSE(ServiceInstall::IsServiceInstalled(_T("FooBar")));
-}
-
-TEST(ServiceUtilsTest, IsServiceRunning) {
-  EXPECT_TRUE(ServiceUtils::IsServiceRunning(SERVICE_SCHEDULE));
-  EXPECT_FALSE(ServiceUtils::IsServiceRunning(_T("FooBar")));
-}
-
-TEST(ServiceUtilsTest, IsServiceDisabled) {
-  EXPECT_FALSE(ServiceUtils::IsServiceDisabled(SERVICE_SCHEDULE));
-}
-
-}  // namespace omaha
-
diff --git a/common/sha.cc b/common/sha.cc
deleted file mode 100644
index e6d91e9..0000000
--- a/common/sha.cc
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright 2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// REMINDER: Identifiers are in accordance with the FIPS180-1 spec
-
-#include "sha.h"
-#include "common/debug.h"
-
-namespace omaha {
-
-typedef SecureHashAlgorithm::uint uint;
-
-//const int SecureHashAlgorithm::kDigestSize;
-
-static inline uint f(uint t, uint B, uint C, uint D) {
-  if (t < 20)
-    return (B & C) | ((~B) & D);
-  else if (t < 40)
-    return B ^ C ^ D;
-  else if (t < 60)
-    return (B & C) | (B & D) | (C & D);
-  else
-    return B ^ C ^ D;
-}
-
-static inline uint S(uint n, uint X) {
-  return (X << n) | (X >> (32-n));
-}
-
-static inline uint K(uint t) {
-  if (t < 20)
-    return 0x5a827999;
-  else if (t < 40)
-    return 0x6ed9eba1;
-  else if (t < 60)
-    return 0x8f1bbcdc;
-  else
-    return 0xca62c1d6;
-}
-
-static inline void swapends(uint& t) {
-  t = ((t & 0xff000000) >> 24) |
-      ((t & 0xff0000) >> 8) |
-      ((t & 0xff00) << 8) |
-      ((t & 0xff) << 24);
-}
-
-//---------------------------------------------------------------------------
-
-void SecureHashAlgorithm::Init() {
-  cursor = 0;
-  l = 0;
-  H[0] = 0x67452301;
-  H[1] = 0xefcdab89;
-  H[2] = 0x98badcfe;
-  H[3] = 0x10325476;
-  H[4] = 0xc3d2e1f0;
-}
-
-void SecureHashAlgorithm::Finished() {
-  Pad();
-  Process();
-
-  for(int t = 0; t < 5; ++t)
-    swapends(H[t]);
-}
-
-void SecureHashAlgorithm::AddBytes(const void * data, int nbytes) {
-  ASSERT(data, (L""));
-
-  const byte * d = reinterpret_cast<const byte *>(data);
-  while (nbytes--) {
-    M[cursor++] = *d++;
-    if (cursor >= 64) this->Process();
-    l += 8;
-  }
-}
-
-void SecureHashAlgorithm::Pad() {
-  M[cursor++] = 0x80;
-  if (cursor > 64-8) {
-    // pad out to next block
-    while (cursor < 64)
-      M[cursor++] = 0;
-    this->Process();
-  }
-  while (cursor < 64-4)
-    M[cursor++] = 0;
-  M[64-4] = static_cast<byte>((l & 0xff000000) >> 24);
-  M[64-3] = static_cast<byte>((l & 0xff0000) >> 16);
-  M[64-2] = static_cast<byte>((l & 0xff00) >> 8);
-  M[64-1] = static_cast<byte>((l & 0xff));
-}
-
-void SecureHashAlgorithm::Process() {
-  uint t;
-
-  // a.
-  // CopyMemory(W, M, sizeof(M));
-  for (t = 0; t < 16; ++t)
-    swapends(W[t]);
-
-  // b.
-  for (t = 16; t < 80; ++t)
-    W[t] = S(1, W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
-
-  // c.
-  A = H[0];
-  B = H[1];
-  C = H[2];
-  D = H[3];
-  E = H[4];
-
-  // d.
-  for (t = 0; t < 80; ++t) {
-    uint TEMP = S(5,A) + f(t,B,C,D) + E + W[t] + K(t);
-    E = D;
-    D = C;
-    C = S(30,B);
-    B = A;
-    A = TEMP;
-  }
-
-  // e.
-  H[0] += A;
-  H[1] += B;
-  H[2] += C;
-  H[3] += D;
-  H[4] += E;
-
-  cursor = 0;
-}
-
-}  // namespace omaha
-
diff --git a/common/sha.h b/common/sha.h
deleted file mode 100644
index c5212ec..0000000
--- a/common/sha.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_COMMON_SHA_H__
-#define OMAHA_COMMON_SHA_H__
-
-#include <wtypes.h>
-#include "base/basictypes.h"
-
-namespace omaha {
-
-// Implementation of SHA-1. Only handles data in byte-sized blocks,
-// which simplifies the code a fair bit.
-//
-// NOTE: All identifier names follow the notation in FIPS PUB 180-1 for
-// easy debugging. http://www.google.com/search?q=lucky:+FIPS+PUB+180-1
-//
-// Usage example:
-//
-// SecureHashAlgorithm sha;
-// while(there is data to hash)
-//   sha.AddBytes(moredata, size of data);
-// sha.Finished();
-// CopyMemory(somewhere, sha.Digest(), 20);
-//
-// To reuse the instance of sha, call sha.Init();
-//
-// This class is NOT threadsafe!
-
-
-class SecureHashAlgorithm {
-
- public:
-  SecureHashAlgorithm() { Init(); }
-
-  typedef unsigned int uint;         // to keep with notation in the spec
-
-  static const int kDigestSize = 20;   // 20 bytes of output
-
-  void Init();                                   // resets internal registers
-  void AddBytes(const void * data, int nbytes);  // processes bytes
-  void Finished();                               // computes final hash
-
-  // extracts the digest (20 bytes)
-  const unsigned char * Digest() const {
-    return reinterpret_cast<const unsigned char *>(H);
-  }
-
- private:
-  void Pad();           // if length of input is less than blocksize
-  void Process();       // does some rounds
-
-  uint A,B,C,D,E;       // internal registers; see spec
-
-  uint H[5];            // 20 bytes of message digest
-
-  union {
-    uint W[80];         // see spec
-    byte M[64];
-  };
-
-  uint cursor;          // placekeeping variables; see spec
-  uint l;
-
-  DISALLOW_EVIL_CONSTRUCTORS(SecureHashAlgorithm);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_SHA_H__
diff --git a/common/sha_unittest.cc b/common/sha_unittest.cc
deleted file mode 100644
index bed3ee2..0000000
--- a/common/sha_unittest.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// sha_unittest.cpp
-//
-// Unit test functions for SHA
-
-#include <cstring>
-
-#include "omaha/common/sha.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-// From the Original Eric Fredricksen implementation
-TEST(ShaTest, Digest) {
-
-  const char * const abc_digest =
-      "\xA9\x99\x3E\x36"
-      "\x47\x06\x81\x6A"
-      "\xBA\x3E\x25\x71"
-      "\x78\x50\xC2\x6C"
-      "\x9C\xD0\xD8\x9D";
-
-  { // FIPS 180-1 Appendix A example
-    SecureHashAlgorithm sha1;
-    sha1.AddBytes("abc", 3);
-    sha1.Finished();
-    ASSERT_TRUE(!memcmp(sha1.Digest(), abc_digest, 20));
-
-    // do it again to make sure Init works
-    sha1.Init();
-    sha1.AddBytes("abc", 3);
-    sha1.Finished();
-    ASSERT_TRUE(!memcmp(sha1.Digest(), abc_digest, 20));
-  }
-
-  const char * const multiblock_digest =
-      "\x84\x98\x3E\x44"
-      "\x1C\x3B\xD2\x6E"
-      "\xBA\xAE\x4A\xA1"
-      "\xF9\x51\x29\xE5"
-      "\xE5\x46\x70\xF1";
-
-  { // FIPS 180-1 Appendix A example
-    SecureHashAlgorithm sha1;
-    char * to_hash = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
-    sha1.AddBytes(to_hash, strlen(to_hash));
-    sha1.Finished();
-    ASSERT_TRUE(!memcmp(sha1.Digest(), multiblock_digest, 20));
-  }
-}
-
-}  // namespace omaha
-
diff --git a/common/shared_any.h b/common/shared_any.h
deleted file mode 100644
index 3250231..0000000
--- a/common/shared_any.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// See the comments in omaha/common/scoped_any.h for details.
-
-#ifndef OMAHA_COMMON_SHARED_ANY__
-#define OMAHA_COMMON_SHARED_ANY__
-
-#pragma warning(push)
-// C4640: construction of local static object is not thread-safe
-#pragma warning(disable : 4640)
-#include "omaha/third_party/smartany/shared_any.h"
-#pragma warning(pop)
-
-#endif  // OMAHA_COMMON_SHARED_ANY__
-
diff --git a/common/shared_memory_ptr.h b/common/shared_memory_ptr.h
deleted file mode 100644
index 7761641..0000000
--- a/common/shared_memory_ptr.h
+++ /dev/null
@@ -1,392 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Defines template class used to share data
-// between processes.
-//
-
-#ifndef OMAHA_COMMON_SHARED_MEMORY_PTR_H__
-#define OMAHA_COMMON_SHARED_MEMORY_PTR_H__
-
-#include "common/debug.h"
-#include "common/singleton.h"
-#include "common/system_info.h"
-#include "common/vista_utils.h"
-
-namespace omaha {
-
-// SharedMemoryPtr class is designed to allow seamless data sharing process boundaries.
-// All the data passed as a template parameter will be shared between processes.
-// Very important to remember that for now - all shared data should be on stack.
-// For example if the class A had stl vector as a member, the members of the vector
-// would be allocated not from shared memory and therefore will not be shared.
-// That could be solved with allocators, but for now we don't need that.
-//
-// Here is a typical example of usage:
-//   Class A {
-//    int i_;
-//    double d_;
-//    .......
-//    ........
-//
-//    public:
-//    set_double(double d){d_=d;}
-//    double get_double(){return d_};
-//
-// };
-//
-// ... Prosess one...
-//    SharedMemoryPtr<A> spA("ABC");
-//    if (!spA)
-//      return false;
-//
-//    spA->set_double(3.14);
-//
-//  ... Process two ...
-//
-//
-//    SharedMemoryPtr<A> spA1("ABC");
-//    if (!spA1)
-//      return false;
-//
-//    process two will see the value set by process one.
-//    it will be 3.14
-//    double d = spA1->get_double();
-//
-
-// You should implement a class member of SystemSharedData if the data you want
-// to share is several hundred bytes. Always try this approach first before you implement
-// new class that is derived from SharedMemoryPtr. The main difference is that SharedMemoryPtr
-// will allocate a least page of shared memory. If your class is just a member of SystemSharedData
-// memory mapped file will be shared between members. It is just more efficient.
-// Look in system_shared_data.h , shared_data_member.h, and system_shared_data_members.h
-// for more details.
-
-// Forward declaration.
-template <typename LockType, typename T> class SharedMemoryPtr;
-
-// During several code reviews it has been noticed that the same error gets repeated over and over.
-// People create SharedMemoryPtr<SomeData>. And than the access to member functions of SomeData
-// is not synchronized by __mutexBlock or __mutexScope. So we need to somehow find a way to make
-// automatic syncronization whenever people access shared data methods or  members.
-// Since by design the only way we can acess shared data is through operator -> of SharedMemoryPtr
-// we need to somehow invoke synchronization at the time of access.
-// We can implement this mainly because of the mechanics of operator-> dictated by C++ standard.
-// When you apply operator-> to a type that's not a built-in pointer, the compiler does an interesting thing.
-// After looking up and applying the user-defined operator-> to that type, it applies operator-> again to the result.
-// The compiler keeps doing this recursively until it reaches a pointer to a built-in type, and only then proceeds with member access.
-// It follows that a SharedMemoryPtr<T> operator-> does not have to return a pointer.
-// It can return an object that in turn implements operator->, without changing the use syntax.
-// So we can implement: pre- and postfunction calls. (See Stroustrup 2000)
-// If you return an object of some type X
-// by value from operator->, the sequence of execution is as follows:
-// 1. Constructor of type X
-// 2. X::operator-> called;  returns a pointer to an object of type T of SharedMemoryPtr
-// 3. Member access
-// 4. Destructor of X
-// In a nutshell, we have a  way of implementing locked function calls.
-
-template <typename LockType, typename T>
-class SharedDataLockingProxy {
-public:
-  // Lock on construction.
-  SharedDataLockingProxy(SharedMemoryPtr<LockType, T> * mem_ptr, T* shared_data)
-    : mem_ptr_(mem_ptr), shared_data_(shared_data) {
-      mem_ptr_->Lock();
-    }
-   // Unlock on destruction.
-  ~SharedDataLockingProxy() {
-    mem_ptr_->Unlock();
-  }
-  // operator
-  T* operator->() const  {
-    ASSERT(shared_data_ != NULL, (L"NULL object pointer being dereferenced"));
-    return shared_data_;
-  }
-private:
-  SharedDataLockingProxy& operator=(const SharedDataLockingProxy&);
-  SharedMemoryPtr<LockType, T>* mem_ptr_;
-  T* shared_data_;
-  // To allow this implicit locking - copy constructor must be
-  // enabled. hence, no DISALLOW_EVIL_CONSTRUCTORS
-};
-
-template <typename LockType, typename T> class SharedMemoryPtr
-    : public LockType {
-  // Handle to disk file if we're backing this shared memory by a file
-  HANDLE file_;
-  // Local handle to file mapping.
-  HANDLE file_mapping_;
-  // pointer to a view.
-  T*     data_;
-  // If the first time creation can do some initialization.
-  bool   first_instance_;
-public:
-  // The heart of the whole idea. Points to shared memrory
-  // instead of the beginning of the class.
-  SharedDataLockingProxy<LockType, T> operator->() {
-    return SharedDataLockingProxy<LockType, T>(this, data_);
-  }
-  // To check after creation.
-  // For example:
-  // SharedMemoryPtr<GLock, SomeClass> sm;
-  // if (sm)
-  // { do whatever you want}
-  // else
-  // {error reporting}
-  operator bool() const {return ((file_mapping_ != NULL) && (data_ != NULL));}
-
-  // Initialize memory mapped file and sync mechanics.
-  // by calling InitializeSharedAccess
-  SharedMemoryPtr(const CString& name,
-                  LPSECURITY_ATTRIBUTES sa,
-                  LPSECURITY_ATTRIBUTES sa_mutex,
-                  bool read_only)
-      : file_(INVALID_HANDLE_VALUE),
-        file_mapping_(NULL),
-        data_(NULL) {
-    HRESULT hr = InitializeSharedAccess(name, false, sa, sa_mutex, read_only);
-    if (FAILED(hr)) {
-      UTIL_LOG(LE, (_T("InitializeSharedAccess failed [%s][%s][0x%x]"),
-                    name, read_only ? _T("R") : _T("RW"), hr));
-    }
-  }
-
-  // Use this constructor if you want to back the shared memory by a file.
-  // NOTE: if using a persistent shared memory, every object with this same
-  // name should be persistent. Otherwise, the objects marked as
-  // non-persistent will lead to InitializeSharedData called again if
-  // they are instantiated before the ones marked as persistent.
-  SharedMemoryPtr(bool persist,
-                  LPSECURITY_ATTRIBUTES sa,
-                  LPSECURITY_ATTRIBUTES sa_mutex,
-                  bool read_only)
-      : file_(INVALID_HANDLE_VALUE),
-        file_mapping_(NULL),
-        data_(NULL) {
-    // Each shared data must implement GetFileName() to use this c-tor. The
-    // implementation should be:
-    // const CString GetFileName() const {return L"C:\\directory\file";}
-    // This is purposedly different from GetSharedName, so that the user is
-    // well aware that a file name is expected, not a mutex name.
-    HRESULT hr = InitializeSharedAccess(data_->GetFileName(),
-                                        persist,
-                                        sa,
-                                        sa_mutex,
-                                        read_only);
-    if (FAILED(hr)) {
-      UTIL_LOG(LE, (_T("InitializeSharedAccess failed [%s][%s][0x%x]"),
-                    data_->GetFileName(), read_only ? _T("R") : _T("RW"), hr));
-    }
-  }
-
-  // Initialize memory mapped file and sync mechanics.
-  // by calling InitializeSharedAccess
-  SharedMemoryPtr() :
-      file_(INVALID_HANDLE_VALUE), file_mapping_(NULL), data_(NULL) {
-    // This should never happen but let's assert
-    // in case it does.
-    // Each shared data must implement GetSharedData() to use this c-tor.
-    // The implementation should be:
-    // const TCHAR * GetSharedName() const
-    //     {return L"Some_unique_string_with_no_spaces";}
-    HRESULT hr = InitializeSharedAccess(data_->GetSharedName(),
-                                        false,
-                                        NULL,
-                                        NULL,
-                                        false);
-    if (FAILED(hr)) {
-      UTIL_LOG(LE, (_T("InitializeSharedAccess failed [%s][%s][0x%x]"),
-                    data_->GetSharedName(), _T("RW"), hr));
-    }
-  }
-
-  // Clean up.
-  ~SharedMemoryPtr() {
-    Cleanup();
-  }
-
-  void Cleanup() {
-    __mutexScope(this);
-    if (data_)
-      UnmapViewOfFile(data_);
-    if (file_mapping_)
-      VERIFY(CloseHandle(file_mapping_), (L""));
-    if (file_ != INVALID_HANDLE_VALUE)
-      VERIFY(CloseHandle(file_), (L""));
-  }
-
-  // Initialize memory mapped file and sync object.
-  bool InitializeSharedAccess(const CString& name,
-                              bool persist,
-                              LPSECURITY_ATTRIBUTES sa,
-                              LPSECURITY_ATTRIBUTES sa_mutex,
-                              bool read_only) {
-    return InitializeSharedAccessInternal(name,
-                                          persist,
-                                          sa,
-                                          sa_mutex,
-                                          read_only,
-                                          sizeof(T),
-                                          &T::InitializeSharedData);
-  }
-
- private:
-  // Initialize memory mapped file and sync object.
-  //
-  // This internal method allows template method folding by only using things
-  // that are consistent in all templates.  Things that vary are passed in.
-  bool InitializeSharedAccessInternal(const CString& name, bool persist,
-                                      LPSECURITY_ATTRIBUTES sa,
-                                      LPSECURITY_ATTRIBUTES sa_mutex,
-                                      bool read_only,
-                                      size_t data_size,
-                                      void (T::*initialize_shared_data)
-                                          (const CString&)) {
-    // If this memory mapped object is backed by a file, then "name" is a fully
-    // qualified name with backslashes. Since we can't use backslashes in a
-    // mutex's name, let's make another name where we convert them to
-    // underscores.
-    CString mem_name(name);
-    if (persist) {
-      mem_name.Replace(_T('\\'), _T('_'));
-    }
-
-    // Initialize the mutex
-    CString mutex_name(mem_name + _T("MUTEX"));
-    LPSECURITY_ATTRIBUTES mutex_attr = sa_mutex ? sa_mutex : sa;
-    if (!InitializeWithSecAttr(mutex_name, mutex_attr)) {
-      ASSERT(false, (L"Failed to initialize mutex. Err=%i", ::GetLastError()));
-      return false;
-    }
-
-    // everything is synchronized till the end of the function or return.
-    __mutexScope(this);
-
-    first_instance_ = false;
-
-    if (persist) {
-      // Back this shared memory by a file
-      file_ = CreateFile(name,
-                         GENERIC_READ | (read_only ? 0 : GENERIC_WRITE),
-                         FILE_SHARE_READ | (read_only ? 0 : FILE_SHARE_WRITE),
-                         sa,
-                         OPEN_ALWAYS,
-                         NULL,
-                         NULL);
-      if (file_ == INVALID_HANDLE_VALUE)
-        return false;
-
-      if (!read_only && GetLastError() != ERROR_ALREADY_EXISTS)
-        first_instance_ = true;
-    } else {
-      ASSERT(file_ == INVALID_HANDLE_VALUE, (L""));
-      file_ = INVALID_HANDLE_VALUE;
-    }
-
-    if (read_only) {
-      file_mapping_ = OpenFileMapping(FILE_MAP_READ, false, mem_name);
-      if (!file_mapping_) {
-        UTIL_LOG(LW, (L"[OpenFileMapping failed][error %i]", ::GetLastError()));
-      }
-    } else {
-      file_mapping_ = CreateFileMapping(file_, sa,
-                                        PAGE_READWRITE, 0, data_size, mem_name);
-      ASSERT(file_mapping_, (L"CreateFileMapping. Err=%i", ::GetLastError()));
-    }
-
-    if (!file_mapping_) {
-      return false;
-    } else if (!read_only &&
-               file_ == INVALID_HANDLE_VALUE &&
-               GetLastError() != ERROR_ALREADY_EXISTS) {
-      first_instance_ = true;
-    }
-
-    data_ = reinterpret_cast<T*>(MapViewOfFile(file_mapping_,
-                                               FILE_MAP_READ |
-                                               (read_only ? 0 : FILE_MAP_WRITE),
-                                               0,
-                                               0,
-                                               data_size));
-
-    if (!data_) {
-      ASSERT(false, (L"MapViewOfFile. Err=%i", ::GetLastError()));
-      VERIFY(CloseHandle(file_mapping_), (L""));
-      file_mapping_ = NULL;
-
-      if (file_ != INVALID_HANDLE_VALUE) {
-        VERIFY(CloseHandle(file_), (L""));
-        file_ = INVALID_HANDLE_VALUE;
-      }
-
-      return false;
-    }
-
-    if (!first_instance_) {
-      return true;
-    }
-
-    // If this is the first instance of shared object
-    // call initialization function. This is nice but
-    // at the same time we can not share built in data types.
-    // SharedMemoryPtr<double> - will not compile. But this is OK
-    // We don't want all the overhead to just share couple of bytes.
-    // Signature is void InitializeSharedData()
-    (data_->*initialize_shared_data)(name);
-
-    return true;
-  }
-
-  DISALLOW_EVIL_CONSTRUCTORS(SharedMemoryPtr);
-};
-
-// Sometimes we want Singletons that are shared between processes.
-// SharedMemoryPtr can do that. But if used in C-written module there will be
-// a need to make SharedMemoryPtr a global object. Making a Singleton from SharedMemoryPtr
-// is possible in this situation, but syntactically this is very difficult to read.
-// The following template solves the problem. It hides difficult to read details inside.
-// Usage is the same as SharedMemoryPtr (ONLY through -> operator). Completely thread-safe.
-// Can be used in two ways:
-// Class A {
-//  public:
-//  void foo(){}
-//
-//};
-// SharedMemorySingleton<A> a, b;
-// a->foo();
-// b->foo();  //refers to the same data in any process.
-//
-//  or
-//
-// class A : public SharedMemorySingleton<A> {
-//  public:
-//  void foo(){}
-//};
-//  A a, b;
-//  a->foo();
-//  b->foo(); //refers to the same data in any process.
-
-template <typename LockType, typename T> class SharedMemorySingleton  {
-public:
-  SharedDataLockingProxy<LockType, T> operator->() {
-    return
-      Singleton<SharedMemoryPtr<LockType, T> >::Instance()->operator->();
-  }
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_SHARED_MEMORY_PTR_H__
diff --git a/common/shell.cc b/common/shell.cc
deleted file mode 100644
index 32b76b4..0000000
--- a/common/shell.cc
+++ /dev/null
@@ -1,398 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Shell functions
-
-#include "omaha/common/shell.h"
-
-#include <shlobj.h>
-#include <shellapi.h>
-#include "omaha/common/app_util.h"
-#include "omaha/common/browser_utils.h"
-#include "omaha/common/commands.h"
-#include "omaha/common/const_utils.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/path.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/string.h"
-#include "omaha/common/system.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-// create and store a shortcut
-// uses shell IShellLink and IPersistFile interfaces
-HRESULT Shell::CreateLink(const TCHAR *source,
-                          const TCHAR *destination,
-                          const TCHAR *working_dir,
-                          const TCHAR *arguments,
-                          const TCHAR *description,
-                          WORD hotkey_virtual_key_code,
-                          WORD hotkey_modifiers,
-                          const TCHAR *icon) {
-  ASSERT1(source);
-  ASSERT1(destination);
-  ASSERT1(working_dir);
-  ASSERT1(arguments);
-  ASSERT1(description);
-
-  scoped_co_init co_init(COINIT_APARTMENTTHREADED);
-  HRESULT hr = co_init.hresult();
-  if (FAILED(hr) && hr != RPC_E_CHANGED_MODE) {
-    UTIL_LOG(LEVEL_ERROR, (_T("[Shell::CreateLink - failed to co_init]"), hr));
-    return hr;
-  }
-
-  UTIL_LOG(L1, (_T("[Create shell link]")
-                _T("[source %s dest %s dir %s arg %s desc %s hotkey %x:%x]"),
-                source, destination, working_dir, arguments, description,
-                hotkey_modifiers, hotkey_virtual_key_code));
-
-  // Get a pointer to the IShellLink interface
-  CComPtr<IShellLink> shell_link;
-
-  RET_IF_FAILED(shell_link.CoCreateInstance(CLSID_ShellLink));
-  ASSERT(shell_link, (L""));
-
-  // Set the path to the shortcut target and add the description
-  VERIFY1(SUCCEEDED(shell_link->SetPath(source)));
-  VERIFY1(SUCCEEDED(shell_link->SetArguments(arguments)));
-  VERIFY1(SUCCEEDED(shell_link->SetDescription(description)));
-  VERIFY1(SUCCEEDED(shell_link->SetWorkingDirectory(working_dir)));
-
-  // If we are given an icon, then set it
-  // For now, we always use the first icon if this happens to have multiple ones
-  if (icon) {
-    VERIFY1(SUCCEEDED(shell_link->SetIconLocation(icon, 0)));
-  }
-
-// C4201: nonstandard extension used : nameless struct/union
-#pragma warning(disable : 4201)
-  union {
-    WORD flags;
-    struct {                  // little-endian machine:
-      WORD virtual_key:8;     // low order byte
-      WORD modifiers:8;       // high order byte
-    };
-  } hot_key;
-#pragma warning(default : 4201)
-
-  hot_key.virtual_key = hotkey_virtual_key_code;
-  hot_key.modifiers = hotkey_modifiers;
-
-  if (hot_key.flags) {
-    shell_link->SetHotkey(hot_key.flags);
-  }
-
-  // Query IShellLink for the IPersistFile interface for saving the shortcut in
-  // persistent storage
-  CComQIPtr<IPersistFile> persist_file(shell_link);
-  if (!persist_file)
-    return E_FAIL;
-
-  // Save the link by calling IPersistFile::Save
-  RET_IF_FAILED(persist_file->Save(destination, TRUE));
-
-  return S_OK;
-}
-
-HRESULT Shell::RemoveLink(const TCHAR *link) {
-  ASSERT(link, (L""));
-  ASSERT(*link, (L""));
-
-  return File::Remove(link);
-}
-
-// Open a URL in a new browser window
-HRESULT Shell::OpenLinkInNewWindow(const TCHAR* url, UseBrowser use_browser) {
-  ASSERT1(url);
-
-  HRESULT hr = S_OK;
-  CString browser_path;
-
-  // Try to open with default browser
-  if (use_browser == USE_DEFAULT_BROWSER) {
-    // Load full browser path from regkey
-    hr = GetDefaultBrowserPath(&browser_path);
-
-    // If there is a default browser and it is not AOL, load the url in that
-    // browser
-    if (SUCCEEDED(hr) && !String_Contains(browser_path, _T("aol"))) {
-      if (!browser_path.IsEmpty()) {
-        // Have we figured out how to append the URL onto the browser path?
-        bool acceptable_url = false;
-
-        if (ReplaceCString(browser_path, _T("\"%1\""), url)) {
-          // the "browser.exe "%1"" case
-          acceptable_url = true;
-        } else if (ReplaceCString(browser_path, _T("%1"), url)) {
-          // the "browser.exe %1 "case
-          acceptable_url = true;
-        } else if (ReplaceCString(browser_path, _T("-nohome"), url)) {
-          // the "browser.exe -nohome" case
-          acceptable_url = true;
-        } else {
-          // the browser.exe case.
-          // simply append the quoted url.
-          EnclosePath(&browser_path);
-          browser_path.AppendChar(_T(' '));
-          CString quoted_url(url);
-          EnclosePath(&quoted_url);
-          browser_path.Append(quoted_url);
-          acceptable_url = true;
-        }
-
-        if (acceptable_url) {
-          hr = System::ShellExecuteCommandLine(browser_path, NULL, NULL);
-          if (SUCCEEDED(hr)) {
-            return S_OK;
-          } else {
-            UTIL_LOG(LE, (_T("[Shell::OpenLinkInNewWindow]")
-                          _T("[failed to start default browser to open url]")
-                          _T("[%s][0x%x]"), url, hr));
-          }
-        }
-      }
-    }
-  }
-
-  // Try to open with IE if can't open with default browser or required
-  if (use_browser == USE_DEFAULT_BROWSER ||
-      use_browser == USE_INTERNET_EXPLORER) {
-    hr = RegKey::GetValue(kRegKeyIeClass, kRegValueIeClass, &browser_path);
-    if (SUCCEEDED(hr)) {
-      hr = System::ShellExecuteProcess(browser_path, url, NULL, NULL);
-      if (SUCCEEDED(hr)) {
-        return S_OK;
-      } else {
-        UTIL_LOG(LE, (_T("[Shell::OpenLinkInNewWindow]")
-                      _T("[failed to start IE to open url][%s][0x%x]"),
-                      url, hr));
-      }
-    }
-  }
-
-  // Try to open with Firefox if can't open with default browser or required
-  if (use_browser == USE_DEFAULT_BROWSER || use_browser == USE_FIREFOX) {
-    hr = RegKey::GetValue(kRegKeyFirefox, kRegValueFirefox, &browser_path);
-    if (SUCCEEDED(hr) && !browser_path.IsEmpty()) {
-      ReplaceCString(browser_path, _T("%1"), url);
-      hr = System::ShellExecuteCommandLine(browser_path, NULL, NULL);
-      if (SUCCEEDED(hr)) {
-        return S_OK;
-      } else {
-        UTIL_LOG(LE, (_T("[Shell::OpenLinkInNewWindow]")
-                      _T("[failed to start Firefox to open url][%s][0x%x]"),
-                      url, hr));
-      }
-    }
-  }
-
-  // ShellExecute the url directly as a last resort
-  hr = Shell::Execute(url);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[Shell::OpenLinkInNewWindow]")
-                  _T("[failed to run ShellExecute to open url][%s][0x%x]"),
-                  url, hr));
-  }
-
-  return hr;
-}
-
-HRESULT Shell::Execute(const TCHAR* file) {
-  ASSERT1(file);
-
-  // Prepare everything required for ::ShellExecuteEx().
-  SHELLEXECUTEINFO sei;
-  SetZero(sei);
-  sei.cbSize = sizeof(sei);
-  sei.fMask = SEE_MASK_FLAG_NO_UI     |  // Do not display an error message box.
-              SEE_MASK_NOZONECHECKS   |  // Do not perform a zone check.
-              SEE_MASK_NOASYNC;          // Wait to complete before returning.
-  // Pass NULL for hwnd. This will have ShellExecuteExEnsureParent()
-  // create a dummy parent window for us.
-  // sei.hwnd = NULL;
-  sei.lpVerb = _T("open");
-  sei.lpFile = file;
-  // No parameters to pass
-  // sei.lpParameters = NULL;
-  // Use parent's starting directory
-  // sei.lpDirectory = NULL;
-  sei.nShow = SW_SHOWNORMAL;
-
-  // Use ShellExecuteExEnsureParent to ensure that we always have a parent HWND.
-  // We need to use the HWND Property to be acknowledged as a Foreground
-  // Application on Vista. Otherwise, the elevation prompt will appear minimized
-  // on the taskbar.
-  if (!ShellExecuteExEnsureParent(&sei)) {
-    HRESULT hr(HRESULTFromLastError());
-    ASSERT(false,
-        (_T("Shell::Execute - ShellExecuteEx failed][%s][0x%x]"), file, hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT Shell::BasicGetSpecialFolder(DWORD csidl, CString* folder_path) {
-  ASSERT1(folder_path);
-
-  // Get a ITEMIDLIST* (called a PIDL in the MSDN documentation) to the
-  // special folder
-  scoped_any<ITEMIDLIST*, close_co_task_free> folder_location;
-  RET_IF_FAILED(::SHGetFolderLocation(NULL,
-                                      csidl,
-                                      NULL,
-                                      0,
-                                      address(folder_location)));
-  ASSERT(get(folder_location), (_T("")));
-
-  // Get an interface to the Desktop folder
-  CComPtr<IShellFolder> desktop_folder;
-  RET_IF_FAILED(::SHGetDesktopFolder(&desktop_folder));
-  ASSERT1(desktop_folder);
-
-  // Ask the desktop for the display name of the special folder
-  STRRET str_return;
-  SetZero(str_return);
-  str_return.uType = STRRET_WSTR;
-  RET_IF_FAILED(desktop_folder->GetDisplayNameOf(get(folder_location),
-                                                 SHGDN_FORPARSING,
-                                                 &str_return));
-
-  // Get the display name of the special folder and return it
-  scoped_any<wchar_t*, close_co_task_free> folder_name;
-  RET_IF_FAILED(::StrRetToStr(&str_return,
-                              get(folder_location),
-                              address(folder_name)));
-  *folder_path = get(folder_name);
-
-  return S_OK;
-}
-
-HRESULT Shell::GetSpecialFolder(DWORD csidl,
-                                bool create_if_missing,
-                                CString* folder_path) {
-  ASSERT(folder_path, (L""));
-
-  HRESULT hr = Shell::BasicGetSpecialFolder(csidl, folder_path);
-
-  // If the folder does not exist, ::SHGetFolderLocation may return error
-  // code ERROR_FILE_NOT_FOUND.
-  if (create_if_missing) {
-    if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
-        (SUCCEEDED(hr) && !File::Exists(*folder_path))) {
-      hr = Shell::BasicGetSpecialFolder(csidl | CSIDL_FLAG_CREATE, folder_path);
-    }
-  }
-  ASSERT(FAILED(hr) || File::Exists(*folder_path), (_T("")));
-
-  return hr;
-}
-
-#pragma warning(disable : 4510 4610)
-// C4510: default constructor could not be generated
-// C4610: struct can never be instantiated - user defined constructor required
-struct {
-  const TCHAR* name;
-  const DWORD csidl;
-} const folder_mappings[] = {
-  L"APPDATA",            CSIDL_APPDATA,
-  L"DESKTOP",            CSIDL_DESKTOPDIRECTORY,
-  L"LOCALAPPDATA",       CSIDL_LOCAL_APPDATA,
-  L"MYMUSIC",            CSIDL_MYMUSIC,
-  L"MYPICTURES",         CSIDL_MYPICTURES,
-  L"PROGRAMFILES",       CSIDL_PROGRAM_FILES,
-  L"PROGRAMFILESCOMMON", CSIDL_PROGRAM_FILES_COMMON,
-  L"PROGRAMS",           CSIDL_PROGRAMS,
-  L"STARTMENU",          CSIDL_STARTMENU,
-  L"STARTUP",            CSIDL_STARTUP,
-  L"SYSTEM",             CSIDL_SYSTEM,
-  L"WINDOWS",            CSIDL_WINDOWS,
-};
-#pragma warning(default : 4510 4610)
-
-HRESULT Shell::GetSpecialFolderKeywordsMapping(
-    std::map<CString, CString>* special_folders_map) {
-  ASSERT1(special_folders_map);
-
-  special_folders_map->clear();
-
-  for (size_t i = 0; i < arraysize(folder_mappings); ++i) {
-    CString name(folder_mappings[i].name);
-    DWORD csidl(folder_mappings[i].csidl);
-    CString folder;
-    HRESULT hr = GetSpecialFolder(csidl, false, &folder);
-    if (FAILED(hr)) {
-      UTIL_LOG(LE, (_T("[Shell::GetSpecialFolderKeywordsMapping]")
-                    _T("[failed to retrieve %s]"), name));
-      continue;
-    }
-    special_folders_map->insert(std::make_pair(name, folder));
-  }
-
-  // Get the current module directory
-  CString module_dir = app_util::GetModuleDirectory(::GetModuleHandle(NULL));
-  ASSERT1(module_dir.GetLength() > 0);
-  special_folders_map->insert(std::make_pair(_T("CURRENTMODULEDIR"),
-                                             module_dir));
-
-  return S_OK;
-}
-
-HRESULT Shell::DeleteDirectory(const TCHAR* dir) {
-  ASSERT1(dir && *dir);
-
-  if (!SafeDirectoryNameForDeletion(dir)) {
-    return E_INVALIDARG;
-  }
-
-  uint32 dir_len = lstrlen(dir);
-  if (dir_len >= MAX_PATH) {
-    return E_INVALIDARG;
-  }
-
-  // the 'from' must be double-terminated with 0. Reserve space for one more
-  // zero at the end
-  TCHAR from[MAX_PATH + 1] = {0};
-  lstrcpyn(from, dir, MAX_PATH);
-  from[1 + dir_len] = 0;    // the second zero terminator.
-
-  SHFILEOPSTRUCT file_op = {0};
-
-  file_op.hwnd   = 0;
-  file_op.wFunc  = FO_DELETE;
-  file_op.pFrom  = from;
-  file_op.pTo    = 0;
-  file_op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
-
-  // ::SHFileOperation returns non-zero on errors
-  return ::SHFileOperation(&file_op) ? HRESULTFromLastError() : S_OK;
-}
-
-HRESULT Shell::GetApplicationExecutablePath(const CString& exe,
-                                            CString* path) {
-  ASSERT1(path);
-
-  CString reg_key_name = AppendRegKeyPath(kRegKeyApplicationPath, exe);
-  return RegKey::GetValue(reg_key_name, kRegKeyPathValue, path);
-}
-
-}  // namespace omaha
-
diff --git a/common/shell.h b/common/shell.h
deleted file mode 100644
index c79382d..0000000
--- a/common/shell.h
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Shell functions
-//
-// create shortcut links
-// remove shortcut links
-
-#ifndef OMAHA_COMMON_SHELL_H_
-#define OMAHA_COMMON_SHELL_H_
-
-#include <windows.h>
-#include <shlobj.h>   // for the CSIDL definitions
-#include <atlstr.h>
-#include <map>
-
-#include "base/basictypes.h"
-
-namespace omaha {
-
-enum UseBrowser {
-  USE_DEFAULT_BROWSER = 0,
-  USE_INTERNET_EXPLORER = 1,
-  USE_FIREFOX = 2
-};
-
-class Shell {
- public:
-
-  // create and store a shortcut link
-  // note that icon can be NULL if no icon is required
-  // note that we always pick the 0th icon for now, this can be changed later
-  // [relevant with EXE and compound icons]
-  static HRESULT CreateLink(const TCHAR *source,
-                            const TCHAR *destination,
-                            const TCHAR *working_dir,
-                            const TCHAR *arguments,
-                            const TCHAR *description,
-                            WORD hotkey_virtual_key_code,
-                            WORD hotkey_modifiers,
-                            const TCHAR *icon);
-  // For information on hotkey modifiers see MSDN IShellLink::GetHotKey method.
-
-  // Delete a shortcut link
-  static HRESULT RemoveLink(const TCHAR *link);
-
-  // Open a URL in a new browser window
-  static HRESULT OpenLinkInNewWindow(const TCHAR* url, UseBrowser use_browser);
-
-  // Execute a file
-  static HRESULT Shell::Execute(const TCHAR* file);
-
-  // Get the location of a special folder.  The special folders are identified
-  // by a unique integer - see the platform SDK files shfolder.h and
-  // shlobj.h.  (These names will be the localized versions for the
-  // system that is running.)
-  static HRESULT GetSpecialFolder(DWORD csidl,
-                                  bool create_if_missing,
-                                  CString* folder_path);
-
-  // Get a mapping from special folder "env var" names to special folder
-  // pathnames.
-  // Provides mappings for: APPDATA, DESKTOP, LOCALAPPDATA, MYMUSIC, MYPICTURES,
-  // PROGRAMFILES, PROGRAMFILESCOMMON, PROGRAMS, STARTMENU, STARTUP, SYSTEM,
-  // WINDOWS.
-  static HRESULT GetSpecialFolderKeywordsMapping(
-      std::map<CString, CString>* special_folders_map);
-
-  // Recursively delete a directory including its files.
-  static HRESULT DeleteDirectory(const TCHAR* dir);
-
-  // Reads the application executable path from
-  // HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths.
-  static HRESULT GetApplicationExecutablePath(const CString& exe,
-                                              CString* path);
-
- private:
-
-  static HRESULT BasicGetSpecialFolder(DWORD csidl, CString* folder_path);
-
-  DISALLOW_EVIL_CONSTRUCTORS(Shell);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_SHELL_H_
diff --git a/common/shell_unittest.cc b/common/shell_unittest.cc
deleted file mode 100644
index ddfe140..0000000
--- a/common/shell_unittest.cc
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Shell functions
-
-#include <shlobj.h>
-#include <map>
-#include "base/basictypes.h"
-#include "omaha/common/dynamic_link_kernel32.h"
-#include "omaha/common/file.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/shell.h"
-#include "omaha/common/system_info.h"
-#include "omaha/common/utils.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(ShellTest, ShellLink) {
-  CString desktop;
-  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_DESKTOP, &desktop));
-  CString link(desktop + _T("\\Shell Unittest.lnk"));
-  CString install_dir;
-  ASSERT_SUCCEEDED(Shell::GetSpecialFolder(CSIDL_PROGRAM_FILES,
-                                           true,
-                                           &install_dir));
-  install_dir += _T("\\Shell Unittest");
-  CString exe = install_dir + _T("\\foo.bar.exe");
-  ASSERT_FALSE(File::Exists(link));
-  ASSERT_SUCCEEDED(Shell::CreateLink(exe,
-                                     link,
-                                     install_dir,
-                                     _T(""),
-                                     _T("Google Update Unit Test"),
-                                     'W',
-                                     HOTKEYF_ALT | HOTKEYF_CONTROL,
-                                     NULL));
-  ASSERT_TRUE(File::Exists(link));
-  ASSERT_SUCCEEDED(Shell::RemoveLink(link));
-  ASSERT_FALSE(File::Exists(link));
-}
-
-struct Folders {
-    DWORD csidl;
-    CString name;
-};
-
-TEST(ShellTest, GetSpecialFolder) {
-  Folders folders[] = {
-    { CSIDL_COMMON_APPDATA,
-      CString("C:\\Documents and Settings\\All Users\\Application Data") },
-    { CSIDL_FONTS,
-      CString("C:\\WINDOWS\\Fonts") },
-    { CSIDL_PROGRAM_FILES,
-      CString("C:\\Program Files") },
-  };
-
-  if (SystemInfo::IsRunningOnVistaOrLater()) {
-    folders[0].name = _T("C:\\ProgramData");
-  }
-
-  // Override the program files location, which changes for 32-bit processes
-  // running on 64-bit systems.
-  BOOL isWow64 = FALSE;
-  EXPECT_SUCCEEDED(Kernel32::IsWow64Process(GetCurrentProcess(), &isWow64));
-  if (isWow64) {
-    folders[2].name += _T(" (x86)");
-  }
-
-  for (size_t i = 0; i != arraysize(folders); ++i) {
-    CString folder_name;
-    EXPECT_SUCCEEDED(Shell::GetSpecialFolder(folders[i].csidl,
-                                             false,
-                                             &folder_name));
-    // This should work, but CmpHelperSTRCASEEQ is not overloaded for wchars.
-    // EXPECT_STRCASEEQ(folder_name, folders[i].name);
-    EXPECT_EQ(folder_name.CompareNoCase(folders[i].name), 0);
-  }
-}
-
-TEST(ShellTest, GetSpecialFolderKeywordsMapping) {
-  typedef std::map<CString, CString> mapping;
-  mapping folder_map;
-  ASSERT_SUCCEEDED(Shell::GetSpecialFolderKeywordsMapping(&folder_map));
-}
-
-}  // namespace omaha
-
diff --git a/common/shutdown_handler.cc b/common/shutdown_handler.cc
deleted file mode 100644
index a03fb79..0000000
--- a/common/shutdown_handler.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/shutdown_handler.h"
-
-#include <atlsecurity.h>
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/reactor.h"
-#include "omaha/common/shutdown_callback.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-ShutdownHandler::ShutdownHandler()
-  : shutdown_callback_(NULL) {
-}
-
-ShutdownHandler::~ShutdownHandler() {
-  if (get(shutdown_event_)) {
-    VERIFY1(SUCCEEDED(reactor_->UnregisterHandle(get(shutdown_event_))));
-  }
-}
-
-HRESULT ShutdownHandler::Initialize(Reactor* reactor,
-                                    ShutdownCallback* shutdown,
-                                    bool is_machine) {
-  ASSERT1(reactor);
-  ASSERT1(shutdown);
-  shutdown_callback_ = shutdown;
-  reactor_ = reactor;
-  is_machine_ = is_machine;
-
-  NamedObjectAttributes attr;
-  GetNamedObjectAttributes(kShutdownEvent, is_machine_, &attr);
-  // Manual reset=true and signaled=false
-  reset(shutdown_event_, ::CreateEvent(&attr.sa, true, false, attr.name));
-  if (!shutdown_event_) {
-    return HRESULTFromLastError();
-  }
-
-  HRESULT hr = reactor_->RegisterHandle(get(shutdown_event_), this, 0);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return S_OK;
-}
-
-void ShutdownHandler::HandleEvent(HANDLE handle) {
-  if (handle == get(shutdown_event_)) {
-    CORE_LOG(L1, (_T("[shutdown event is signaled]")));
-  } else {
-    ASSERT1(false);
-  }
-  ASSERT1(shutdown_callback_);
-  shutdown_callback_->Shutdown();
-}
-
-}  // namespace omaha
-
diff --git a/common/shutdown_handler.h b/common/shutdown_handler.h
deleted file mode 100644
index d7cfa94..0000000
--- a/common/shutdown_handler.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// ShutdownHandler monitors a shutdown event.
-
-#ifndef OMAHA_COMMON_SHUTDOWN_HANDLER_H__
-#define OMAHA_COMMON_SHUTDOWN_HANDLER_H__
-
-#include <windows.h>
-#include "base/basictypes.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/event_handler.h"
-
-namespace omaha {
-
-class Reactor;
-class ShutdownCallback;
-
-class ShutdownHandler : public EventHandler {
- public:
-  ShutdownHandler();
-  ~ShutdownHandler();
-
-  HRESULT Initialize(Reactor* reactor,
-                     ShutdownCallback* shutdown,
-                     bool is_machine);
-  virtual void HandleEvent(HANDLE handle);
-
- private:
-  Reactor* reactor_;
-  scoped_event shutdown_event_;
-  ShutdownCallback* shutdown_callback_;
-  bool is_machine_;
-  DISALLOW_EVIL_CONSTRUCTORS(ShutdownHandler);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_SHUTDOWN_HANDLER_H__
-
diff --git a/common/signatures.cc b/common/signatures.cc
deleted file mode 100644
index 65fd668..0000000
--- a/common/signatures.cc
+++ /dev/null
@@ -1,1028 +0,0 @@
-// Copyright 2003-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// signatures.cpp
-//
-// Classes and functions related to crypto-hashes of buffers and digital
-// signatures of buffers.
-
-#include "omaha/common/signatures.h"
-#include <wincrypt.h>
-#include <memory.h>
-
-#pragma warning(disable : 4245)
-// C4245 : conversion from 'type1' to 'type2', signed/unsigned mismatch
-#include <atlenc.h>
-#pragma warning(default : 4245)
-#include <vector>
-#include "base/scoped_ptr.h"
-#include "omaha/common/const_utils.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/sha.h"
-#include "omaha/common/string.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-const ALG_ID kHashAlgorithm = CALG_SHA1;
-const DWORD kEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
-const DWORD kProviderType = PROV_RSA_FULL;
-const DWORD kCertificateNameType = CERT_NAME_SIMPLE_DISPLAY_TYPE;
-const DWORD kKeyPairType = AT_SIGNATURE;
-
-// Maximum file size allowed for performing authentication.
-const int kMaxFileSizeForAuthentication = 512 * 1000 * 1000;  // 512MB
-
-namespace CryptDetails {
-
-  // Useful scoped pointers for working with CryptoAPI objects
-
-  void crypt_release_context(HCRYPTPROV provider) {
-    UTIL_LOG(L3, (L"Releasing HCRYPTPROV 0x%08lx", provider));
-    BOOL b = ::CryptReleaseContext(provider, 0 /*flags*/);
-    ASSERT(b, (L""));
-  }
-
-  void crypt_close_store(HCERTSTORE store) {
-    UTIL_LOG(L3, (L"Releasing HCERTSTORE 0x%08lx", store));
-    BOOL b = ::CertCloseStore(store, 0 /*flags*/);
-    ASSERT(b, (L""));
-    ASSERT(::GetLastError() != CRYPT_E_PENDING_CLOSE, (L""));
-  }
-
-  void crypt_free_certificate(PCCERT_CONTEXT certificate) {
-    UTIL_LOG(L3, (L"Releasing PCCERT_CONTEXT 0x%08lx", certificate));
-    BOOL b = ::CertFreeCertificateContext(certificate);
-    ASSERT(b, (L""));
-  }
-
-  void crypt_destroy_key(HCRYPTKEY key) {
-    UTIL_LOG(L3, (L"Releasing HCRYPTKEY 0x%08lx", key));
-    BOOL b = ::CryptDestroyKey(key);
-    ASSERT(b, (L""));
-  }
-
-  void crypt_destroy_hash(HCRYPTHASH hash) {
-    UTIL_LOG(L3, (L"Releasing HCRYPTHASH 0x%08lx", hash));
-    BOOL b = ::CryptDestroyHash(hash);
-    ASSERT(b, (L""));
-  }
-
-  typedef close_fun<void (*)(HCRYPTHASH),
-                    crypt_destroy_hash> smart_destroy_hash;
-  typedef scoped_any<HCRYPTHASH, smart_destroy_hash, null_t> scoped_crypt_hash;
-}
-
-// Base64 encode/decode functions are part of ATL Server
-HRESULT Base64::Encode(const std::vector<byte>& buffer_in,
-                       std::vector<byte>* encoded,
-                       bool break_into_lines) {
-  ASSERT(encoded, (L""));
-
-  if (buffer_in.empty()) {
-    encoded->resize(0);
-    return S_OK;
-  }
-
-  int32 encoded_len =
-    Base64EncodeGetRequiredLength(
-        buffer_in.size(),
-        break_into_lines ? ATL_BASE64_FLAG_NONE : ATL_BASE64_FLAG_NOCRLF);
-  ASSERT(encoded_len > 0, (L""));
-
-  encoded->resize(encoded_len);
-  int32 str_out_len = encoded_len;
-
-  BOOL result = Base64Encode(
-      &buffer_in.front(),
-      buffer_in.size(),
-      reinterpret_cast<char*>(&encoded->front()),
-      &str_out_len,
-      break_into_lines ? ATL_BASE64_FLAG_NONE : ATL_BASE64_FLAG_NOCRLF);
-  if (!result)
-    return E_FAIL;
-  ASSERT(str_out_len <= encoded_len, (L""));
-  if (str_out_len < encoded_len)
-    encoded->resize(str_out_len);
-
-  return S_OK;
-}
-
-HRESULT Base64::Encode(const std::vector<byte>& buffer_in,
-                       CStringA* encoded,
-                       bool break_into_lines) {
-  ASSERT(encoded, (L""));
-
-  if (buffer_in.empty()) {
-    return S_OK;
-  }
-
-  std::vector<byte> buffer_out;
-  RET_IF_FAILED(Encode(buffer_in, &buffer_out, break_into_lines));
-  encoded->Append(reinterpret_cast<const char*>(&buffer_out.front()),
-                                                buffer_out.size());
-
-  return S_OK;
-}
-
-HRESULT Base64::Encode(const std::vector<byte>& buffer_in,
-                       CString* encoded,
-                       bool break_into_lines) {
-  ASSERT(encoded, (L""));
-
-  CStringA string_out;
-  RET_IF_FAILED(Encode(buffer_in, &string_out, break_into_lines));
-  *encoded = string_out;
-
-  return S_OK;
-}
-
-HRESULT Base64::Decode(const std::vector<byte>& encoded,
-                       std::vector<byte>* buffer_out) {
-  ASSERT(buffer_out, (L""));
-
-  size_t encoded_len = encoded.size();
-  int32 required_len = Base64DecodeGetRequiredLength(encoded_len);
-
-  buffer_out->resize(required_len);
-
-  if (required_len == 0) {
-    return S_OK;
-  }
-
-  int32 bytes_written = required_len;
-  BOOL result = Base64Decode(reinterpret_cast<const char*>(&encoded.front()),
-                             encoded_len,
-                             &buffer_out->front(),
-                             &bytes_written);
-  if (!result)
-    return E_FAIL;
-  ASSERT(bytes_written <= required_len, (L""));
-  if (bytes_written < required_len) {
-    buffer_out->resize(bytes_written);
-  }
-
-  return S_OK;
-}
-
-HRESULT Base64::Decode(const CStringA& encoded, std::vector<byte>* buffer_out) {
-  ASSERT(buffer_out, (L""));
-
-  size_t encoded_len = encoded.GetLength();
-  std::vector<byte> buffer_in(encoded_len);
-  if (encoded_len != 0) {
-    ::memcpy(&buffer_in.front(), encoded.GetString(), encoded_len);
-  }
-
-  return Decode(buffer_in, buffer_out);
-}
-
-// Base64 in a CString -> binary
-HRESULT Base64::Decode(const CString& encoded, std::vector<byte>* buffer_out) {
-  ASSERT(buffer_out, (L""));
-
-  CW2A encoded_a(encoded.GetString());
-
-  size_t encoded_len = ::strlen(encoded_a);
-  std::vector<byte> buffer_in(encoded_len);
-  if (encoded_len != 0) {
-    ::memcpy(&buffer_in.front(), encoded_a, encoded_len);
-  }
-
-  return Decode(buffer_in, buffer_out);
-}
-
-// Using google SHA-1 algorithms rather than CryptoAPI SHA-1
-// algorithms; saves the trouble of dealing with CSPs and the like.
-
-CryptoHash::CryptoHash() {
-}
-
-CryptoHash::~CryptoHash() {
-}
-
-HRESULT CryptoHash::Compute(const TCHAR* filepath,
-                            uint64 max_len,
-                            std::vector<byte>* hash_out) {
-  ASSERT1(filepath);
-  ASSERT1(hash_out);
-
-  std::vector<CString> filepaths;
-  filepaths.push_back(filepath);
-  return Compute(filepaths, max_len, hash_out);
-}
-
-HRESULT CryptoHash::Compute(const std::vector<CString>& filepaths,
-                            uint64 max_len,
-                            std::vector<byte>* hash_out) {
-  ASSERT1(filepaths.size() > 0);
-  ASSERT1(hash_out);
-
-  return ComputeOrValidate(filepaths, max_len, NULL, hash_out);
-}
-
-HRESULT CryptoHash::Compute(const std::vector<byte>& buffer_in,
-                            std::vector<byte>* hash_out) {
-  ASSERT1(buffer_in.size() > 0);
-  ASSERT1(hash_out);
-
-  return ComputeOrValidate(buffer_in, NULL, hash_out);
-}
-
-HRESULT CryptoHash::Validate(const TCHAR* filepath,
-                             uint64 max_len,
-                             const std::vector<byte>& hash_in) {
-  ASSERT1(filepath);
-  ASSERT1(hash_in.size() == kHashSize);
-
-  std::vector<CString> filepaths;
-  filepaths.push_back(filepath);
-  return Validate(filepaths, max_len, hash_in);
-}
-
-HRESULT CryptoHash::Validate(const std::vector<CString>& filepaths,
-                             uint64 max_len,
-                             const std::vector<byte>& hash_in) {
-  ASSERT1(hash_in.size() == kHashSize);
-
-  return ComputeOrValidate(filepaths, max_len, &hash_in, NULL);
-}
-
-
-HRESULT CryptoHash::Validate(const std::vector<byte>& buffer_in,
-                             const std::vector<byte>& hash_in) {
-  ASSERT1(buffer_in.size() > 0);
-  ASSERT1(hash_in.size() == kHashSize);
-
-  return ComputeOrValidate(buffer_in, &hash_in, NULL);
-}
-
-HRESULT CryptoHash::ComputeOrValidate(const std::vector<CString>& filepaths,
-                                      uint64 max_len,
-                                      const std::vector<byte>* hash_in,
-                                      std::vector<byte>* hash_out) {
-  ASSERT1(filepaths.size() > 0);
-  ASSERT1(hash_in && !hash_out || !hash_in && hash_out);
-
-  byte buf[1024] = {0};
-  SecureHashAlgorithm sha;
-  uint64 curr_len = 0;
-  for (size_t i = 0; i < filepaths.size(); ++i) {
-    scoped_hfile file_handle(::CreateFile(filepaths[i],
-                                          FILE_READ_DATA,
-                                          FILE_SHARE_READ,
-                                          NULL,
-                                          OPEN_EXISTING,
-                                          FILE_ATTRIBUTE_NORMAL,
-                                          NULL));
-    if (!file_handle) {
-      return HRESULTFromLastError();
-    }
-
-    if (max_len) {
-      LARGE_INTEGER file_size = {0};
-      if (!::GetFileSizeEx(get(file_handle), &file_size)) {
-        return HRESULTFromLastError();
-      }
-      curr_len += ((static_cast<uint64>(file_size.HighPart)) << 32) +
-                  static_cast<uint64>(file_size.LowPart);
-      if (curr_len > max_len) {
-        UTIL_LOG(LE, (_T("[CryptoHash::ComputeOrValidate]")
-                      _T(" exceed max len][curr_len=%lu][max_len=%lu]"),
-                      curr_len, max_len));
-        return E_FAIL;
-      }
-    }
-
-    DWORD bytes_read = 0;
-    do {
-      if (!::ReadFile(get(file_handle),
-                      buf,
-                      arraysize(buf),
-                      &bytes_read,
-                      NULL)) {
-        return HRESULTFromLastError();
-      }
-
-      if (bytes_read > 0) {
-        sha.AddBytes(buf, bytes_read);
-      }
-    } while (bytes_read == arraysize(buf));
-  }
-  sha.Finished();
-
-  if (hash_in) {
-    int res = ::memcmp(&hash_in->front(), sha.Digest(), kHashSize);
-    if (res == 0) {
-      return S_OK;
-    }
-
-    std::vector<byte> calculated_hash(kHashSize);
-    memcpy(&calculated_hash.front(), sha.Digest(), kHashSize);
-    CStringA base64_encoded_hash;
-    Base64::Encode(calculated_hash, &base64_encoded_hash, false);
-    CString hash = AnsiToWideString(base64_encoded_hash,
-                                    base64_encoded_hash.GetLength());
-    REPORT_LOG(L1, (_T("[actual hash=%s]"), hash));
-    return SIGS_E_INVALID_SIGNATURE;
-  } else {
-    hash_out->resize(kHashSize);
-    ::memcpy(&hash_out->front(), sha.Digest(), kHashSize);
-    return S_OK;
-  }
-}
-
-HRESULT CryptoHash::ComputeOrValidate(const std::vector<byte>& buffer_in,
-                                      const std::vector<byte>* hash_in,
-                                      std::vector<byte>* hash_out) {
-  ASSERT1(hash_in && !hash_out || !hash_in && hash_out);
-
-  SecureHashAlgorithm sha;
-  if (!buffer_in.empty()) {
-    sha.AddBytes(&buffer_in.front(), buffer_in.size());
-  }
-  sha.Finished();
-
-  if (hash_in) {
-    int res = ::memcmp(&hash_in->front(), sha.Digest(), kHashSize);
-    return (res == 0) ? S_OK : SIGS_E_INVALID_SIGNATURE;
-  } else {
-    hash_out->resize(kHashSize);
-    ::memcpy(&hash_out->front(), sha.Digest(), kHashSize);
-    return S_OK;
-  }
-}
-
-// To sign data you need a CSP with the proper private key installed.
-// To get a signing certificate you start with a PFX file.  This file
-// encodes a "certificate store" which can hold more than one
-// certificate.  (In general it can hold a certificate chain, but we
-// only use the signing certificate.)  There are special APIs to verify
-// the format of a PFX file and read it into a new certificate store.  A
-// password must be specified to read the PFX file as it is encrypted.
-// The password was set when the PFX file was exported or otherwise
-// created.  Then you search for the proper certificate in the store
-// (using the subject_name which tells who the certificate was issued
-// to).  Finally, to get a CSP with the certificate's private key
-// available there is a special API, CryptAcquireCertificatePrivateKey,
-// that takes a CSP and a certificate and makes the private key of the
-// certificate the private key of the CSP.
-
-CryptoSigningCertificate::CryptoSigningCertificate() : key_spec_(0) {
-}
-
-CryptoSigningCertificate::~CryptoSigningCertificate() {
-}
-
-HRESULT CryptoSigningCertificate::ImportCertificate(
-    const TCHAR * filepath,
-    const TCHAR * password,
-    const TCHAR * subject_name) {
-  ASSERT(filepath, (L""));
-  ASSERT(password, (L""));
-
-  std::vector<byte> buffer;
-  HRESULT hr = ReadEntireFile(filepath, kMaxCertificateSize, &buffer);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (L"[CryptoSigningCertificate::ImportCertificate]"
-                  L"['%s' not read, hr 0x%08lx]", filepath, hr));
-    return hr;
-  }
-  return ImportCertificate(buffer, password, subject_name);
-}
-
-HRESULT CryptoSigningCertificate::ImportCertificate(
-    const std::vector<byte>& certificate_in,
-    const TCHAR * password,
-    const TCHAR * subject_name) {
-  ASSERT(password, (L""));
-  ASSERT1(!certificate_in.empty());
-
-  UTIL_LOG(L2, (L"[CryptoSigningCertificate::ImportCertificate]"
-                L"[%d bytes, subject_name '%s']",
-                certificate_in.size(), subject_name ? subject_name : L""));
-
-  // CryptoAPI treats the certificate as a "blob"
-  CRYPT_DATA_BLOB blob;
-  blob.cbData = certificate_in.size();
-  blob.pbData = const_cast<BYTE*>(&certificate_in.front());
-
-  // Ensure that it is PFX formatted
-  BOOL b = ::PFXIsPFXBlob(&blob);
-  if (!b) {
-    ASSERT(0, (L"Invalid PFX certificate, err 0x%08lx", ::GetLastError()));
-    return SIGS_E_INVALID_PFX_CERTIFICATE;
-  }
-
-  // Make sure the password checks out
-  b = ::PFXVerifyPassword(&blob, password, 0 /* flags */);
-  if (!b) {
-    UTIL_LOG(LE, (L"[CryptoSigningCertificate::ImportCertificate]"
-                  L"[invalid password, err 0x%08lx]", ::GetLastError()));
-    return SIGS_E_INVALID_PASSWORD;
-  }
-
-  // Do the import from the certificate to a new certificate store
-  // TODO(omaha): Check that this is in fact a new certificate store, not an
-  // existing one.  If it is an existing one we'll need to delete the
-  // certificate later.
-  // The last parameter to ::PFXImportCertStore() is 0, indicating that we want
-  // the CSP to be "silent"; i.e., not prompt.
-  reset(store_, ::PFXImportCertStore(&blob, password, 0));
-  if (!store_) {
-    DWORD err = ::GetLastError();
-    ASSERT(0, (L"Failed to import PFX certificate into a certificate store, "
-               L"err 0x%08lx", err));
-    return HRESULT_FROM_WIN32(err);
-  }
-  UTIL_LOG(L3, (L"[CryptoSigningCertificate::ImportCertificate]"
-                L"[new store 0x%08lx]", get(store_)));
-
-  // Now that we have a store, look for the correct certificate.  (There may
-  // have been more than one in the PFX file, e.g., a certificate chain.)
-  PCCERT_CONTEXT certificate_context = NULL;
-  while ((certificate_context =
-          ::CertEnumCertificatesInStore(get(store_),
-                                        certificate_context)) != NULL) {
-    // Have a certificate, does it look like the right one?  Check the name
-    DWORD name_len = ::CertGetNameString(certificate_context,
-                                         kCertificateNameType,
-                                         0 /*flags*/,
-                                         NULL,
-                                         NULL,
-                                         0);
-    if (name_len <= 1) {
-      // Name attribute not found - should never happen
-      ASSERT(0, (L"CryptoSigningCertificate::ImportCertificate failed to get "
-                 L"certificate name length, err 0x%08lx", ::GetLastError()));
-      continue;
-    }
-    // name_len includes the terminating null
-
-    std::vector<TCHAR> name;
-    name.resize(name_len);
-    ASSERT1(!name.empty());
-    DWORD name_len2 = ::CertGetNameString(certificate_context,
-                                          kCertificateNameType,
-                                          0,
-                                          NULL,
-                                          &name.front(),
-                                          name_len);
-    ASSERT(name_len2 == name_len, (L""));
-
-    UTIL_LOG(L3, (L"[CryptoSigningCertificate::ImportCertificate]"
-                  L"[found '%s' in store]", &name.front()));
-
-    // Check the name if the user so desires.  (If subject_name == NULL then
-    // the first certificate found is used.)
-    if (subject_name && (0 != String_StrNCmp(&name.front(),
-                                             subject_name,
-                                             ::lstrlen(subject_name),
-                                             false))) {
-      // name mismatch
-      UTIL_LOG(L3, (L"[CryptoSigningCertificate::ImportCertificate]"
-                    L"[not the right certificate, we're looking for '%s']",
-                    subject_name));
-      continue;
-    }
-
-    // This is the right certificate
-    subject_name_ = &name.front();
-    reset(certificate_, certificate_context);
-    UTIL_LOG(L3, (L"[CryptoSigningCertificate::ImportCertificate]"
-                  L"[new certificate 0x%08lx]", get(certificate_)));
-    break;
-  }
-
-  return S_OK;
-}
-
-HRESULT CryptoSigningCertificate::GetCSPContext(HCRYPTPROV* csp_context) {
-  ASSERT(csp_context, (L""));
-  ASSERT(get(certificate_), (L""));
-
-  // CSP may have already been used - reset it
-  reset(csp_);
-
-  // Create a CSP context using the private key of the certificate we imported
-  // earlier.
-  HCRYPTPROV csp = NULL;
-  BOOL must_free_csp = FALSE;
-  BOOL b = ::CryptAcquireCertificatePrivateKey(get(certificate_),
-                                               0 /*flags*/,
-                                               0 /*reserved*/,
-                                               &csp,
-                                               &key_spec_,
-                                               &must_free_csp);
-  if (!b) {
-    DWORD err = ::GetLastError();
-    ASSERT(0, (L"CryptoSigningCertificate::GetCSPContext "
-               L"CryptAcquireCertificatePrivateKey failed, err 0x%08lx", err));
-    return HRESULT_FROM_WIN32(err);
-  }
-
-  // (Funky API returns a boolean which tells you whether it is your
-  // responsibility to delete the CSP context or not.)
-  if (must_free_csp) {
-    reset(csp_, csp);
-  }
-  if (get(csp_)) {
-    UTIL_LOG(L3, (L"[CryptoSigningCertificate::GetCSPContext new CSP 0x%08lx]",
-                  get(csp_)));
-  }
-
-  ASSERT(key_spec_ == AT_SIGNATURE || key_spec_ == AT_KEYEXCHANGE, (L""));
-  if (key_spec_ != kKeyPairType) {
-    UTIL_LOG(LE, (L"[CryptoSigningCertificate::GetCSPContext]"
-                  L"[requires a AT_SIGNATURE type key]"));
-    return SIGS_E_INVALID_KEY_TYPE;
-  }
-
-#ifdef _DEBUG
-  // Which CSP did we get?
-  char csp_name[256] = {0};
-  DWORD csp_name_len = arraysize(csp_name);
-  b = ::CryptGetProvParam(csp,
-                          PP_NAME,
-                          reinterpret_cast<BYTE*>(&csp_name[0]),
-                          &csp_name_len,
-                          0 /*flags*/);
-  if (!b) {
-    DWORD err = ::GetLastError();
-    UTIL_LOG(LE, (L"[CryptoSigningCertificate::GetCSPContext]"
-                  L"[error getting CSP name, err 0x%08lx]", err));
-  }
-  DWORD csp_prov_type;
-  DWORD csp_prov_type_len = sizeof(csp_prov_type);
-  b = ::CryptGetProvParam(csp,
-                          PP_PROVTYPE,
-                          reinterpret_cast<BYTE*>(&csp_prov_type),
-                          &csp_prov_type_len,
-                          0 /*flags*/);
-  if (!b) {
-    DWORD err = ::GetLastError();
-    UTIL_LOG(LE, (L"[CryptoSigningCertificate::GetCSPContext]"
-                  L"[error getting CSP provtype, err 0x%08lx]", err));
-  }
-  char csp_container[256] = {0};
-  DWORD csp_container_len = arraysize(csp_container);
-  b = ::CryptGetProvParam(csp,
-                          PP_CONTAINER,
-                          reinterpret_cast<BYTE*>(&csp_container[0]),
-                          &csp_container_len,
-                          0 /*flags*/);
-  if (!b) {
-    DWORD err = ::GetLastError();
-    UTIL_LOG(LE, (L"[CryptoSigningCertificate::GetCSPContext]"
-                  L"[error getting CSP current container name, err 0x%08lx]",
-                  err));
-  }
-  UTIL_LOG(L2, (L"[CryptoSigningCertificate::GetCSPContext]"
-                L"[have CSP '%S' (provtype %d) key container '%S']",
-                csp_name, csp_prov_type, csp_container));
-  // End of which CSP did we get
-#endif
-
-  *csp_context = csp;
-
-  UTIL_LOG(L2, (L"[CryptoSigningCertificate::GetCSPContext]"
-                L"[getting CSP with private key from certificate]"
-                L"[HCRYPTPROV 0x%08lx]", csp));
-
-  return S_OK;
-}
-
-// To sign some data using CryptoAPI you first hash it into a hash
-// object, then sign it using the CSP.  The CSP needs to have the
-// private key, of type AT_SIGNATURE, in it already, as it isn't a
-// parameter of the CryptSignHash API.  The CryptoSigningCertificate
-// can provide such a CSP.
-
-CryptoComputeSignature::CryptoComputeSignature(
-    CryptoSigningCertificate* certificate)
-    : certificate_(certificate) {
-}
-
-CryptoComputeSignature::~CryptoComputeSignature() {
-}
-
-HRESULT CryptoComputeSignature::Sign(TCHAR const * const filepath,
-                                     uint32 max_len,
-                                     std::vector<byte>* signature_out) {
-  ASSERT(filepath, (L""));
-  std::vector<byte> buffer;
-  HRESULT hr = ReadEntireFile(filepath, max_len, &buffer);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]"
-                  L"['%s not read, hr 0x%08lx]", filepath, hr));
-    return hr;
-  }
-  return Sign(buffer, signature_out);
-}
-
-HRESULT CryptoComputeSignature::Sign(const std::vector<byte>& buffer_in,
-                                     std::vector<byte>* signature_out) {
-  ASSERT(signature_out, (L""));
-  ASSERT1(!buffer_in.empty());
-
-  UTIL_LOG(L2, (L"[CryptoComputeSignature::Sign]"
-                L"[buffer of %d bytes]", buffer_in.size()));
-
-  // Get the proper CSP with the private key (certificate retains ownership)
-  HCRYPTPROV csp = NULL;
-  HRESULT hr = certificate_->GetCSPContext(&csp);
-  ASSERT(SUCCEEDED(hr) && csp, (L""));
-
-  // Hash the data
-  CryptDetails::scoped_crypt_hash hash;
-  BOOL b = ::CryptCreateHash(csp, kHashAlgorithm, 0, 0, address(hash));
-  if (!b) {
-    // hash is now invalid, but might not be NULL, so stomp on it
-    DWORD err = ::GetLastError();
-    ASSERT(!hash, (L""));
-    UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]"
-                  L"[could not create hash, err 0x%08lx]", err));
-    return HRESULT_FROM_WIN32(err);
-  }
-  UTIL_LOG(L3, (L"CryptoComputeSignature::Sign new hash 0x%08lx", get(hash)));
-
-  b = ::CryptHashData(get(hash), &buffer_in.front(), buffer_in.size(), 0);
-  if (!b) {
-    DWORD err = ::GetLastError();
-    UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]"
-                  L"[could not hash data, err 0x%08lx]", err));
-    return HRESULT_FROM_WIN32(err);
-  }
-
-  // Sign the hash (first get length, then allocate buffer and do real signing)
-  DWORD signature_len = 0;
-  b = ::CryptSignHash(get(hash),
-                      kKeyPairType,
-                      NULL,
-                      0 /*flags*/,
-                      NULL,
-                      &signature_len);
-  if (!b && ::GetLastError() != ERROR_MORE_DATA) {
-    DWORD err = ::GetLastError();
-    UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]"
-                  L"[could not compute size of signature, err 0x%08lx]", err));
-    return HRESULT_FROM_WIN32(err);
-  }
-  signature_out->resize(signature_len);
-  b = ::CryptSignHash(get(hash),
-                      kKeyPairType,
-                      NULL,
-                      0,
-                      &signature_out->front(),
-                      &signature_len);
-  if (!b) {
-    DWORD err = ::GetLastError();
-    UTIL_LOG(LE, (L"[CryptoComputeSignature::Sign]"
-                  L"[could not compute signature, err 0x%08lx]", err));
-    return HRESULT_FROM_WIN32(err);
-  }
-  ASSERT(signature_len == signature_out->size(), (L""));
-
-  UTIL_LOG(L3, (L"[CryptoComputeSignature::Sign]"
-                L"[have %d byte signature]", signature_out->size()));
-
-  return S_OK;
-}
-
-// To verify signed data you need a CSP, and you also need the public
-// key extracted from a certificate.  The CSP can be any RSA CSP on the
-// machine, the default one is fine.  To get the public key you start
-// by importing a certificate in standard "DER encoded" format.  That
-// returns a giant data structure, one field of which is the public key
-// in a format that CryptoAPI understands.  You import this public key
-// into the CSP with the CryptImportPublicKey() API, and then create a
-// key object from it suitable for use with the verification API.
-
-CryptoSignatureVerificationCertificate::CryptoSignatureVerificationCertificate() {   // NOLINT
-}
-
-CryptoSignatureVerificationCertificate::~CryptoSignatureVerificationCertificate() {  // NOLINT
-}
-
-HRESULT CryptoSignatureVerificationCertificate::ImportCertificate(
-    const TCHAR * filepath,
-    const TCHAR * subject_name) {
-  ASSERT(filepath, (L""));
-  std::vector<byte> buffer;
-  HRESULT hr = ReadEntireFile(filepath, kMaxCertificateSize, &buffer);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]"
-                  L"['%s' not read, hr 0x%08lx]", filepath, hr));
-    return hr;
-  }
-  return ImportCertificate(buffer, subject_name);
-}
-
-HRESULT CryptoSignatureVerificationCertificate::ImportCertificate(
-    const std::vector<byte>& certificate_in,
-    const TCHAR * subject_name) {
-  // Import the certificate
-  ASSERT1(!certificate_in.empty());
-  reset(certificate_, ::CertCreateCertificateContext(kEncodingType,
-                                                     &certificate_in.front(),
-                                                     certificate_in.size()));
-  if (!certificate_) {
-    DWORD err = ::GetLastError();
-    UTIL_LOG(LE, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]"
-                  L"[could not import certificate, err 0x%08lx]", err));
-    return SIGS_E_INVALID_DER_CERTIFICATE;
-  }
-  UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]"
-                L"[new certificate 0x%08lx]", get(certificate_)));
-
-  // Get certificate's subject name
-  DWORD name_len = ::CertGetNameString(get(certificate_),
-                                       kCertificateNameType,
-                                       0 /*flags*/,
-                                       NULL,
-                                       NULL,
-                                       0);
-  if (name_len <= 1) {
-    // Name attribute not found - should never happen
-    ASSERT(0, (L"CryptoSignatureVerificationCertificate failed to get "
-               L"certificate name length, err 0x%08lx", ::GetLastError()));
-    return E_FAIL;
-  }
-  // name_len includes the terminating NULL
-
-  std::vector <TCHAR> name;
-  name.resize(name_len);
-  ASSERT1(!name.empty());
-  DWORD name_len2 = ::CertGetNameString(get(certificate_),
-                                        kCertificateNameType,
-                                        0,
-                                        NULL,
-                                        &name.front(),
-                                        name_len);
-  ASSERT(name_len2 == name_len, (L""));
-
-  UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]"
-                L"['%s' is subject of certificate]", &name.front()));
-
-  subject_name_ = &name.front();
-
-  // Check the name if the user so desires.
-  if (subject_name && (0 != String_StrNCmp(&name.front(),
-                                           subject_name,
-                                           ::lstrlen(subject_name), false))) {
-      // name mismatch
-    UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::ImportCertificate]"
-                  L"[not the right certificate, we're looking for '%s']",
-                  subject_name));
-    return E_FAIL;
-  }
-
-  return S_OK;
-}
-
-HRESULT CryptoSignatureVerificationCertificate::GetCSPContextAndKey(
-    HCRYPTPROV* csp_context,
-    HCRYPTKEY* public_key) {
-  ASSERT(csp_context, (L""));
-  ASSERT(public_key, (L""));
-  ASSERT(get(certificate_), (L""));
-
-  // Get the public key out of the certificate
-  PCERT_INFO cert_info = get(certificate_)->pCertInfo;
-  ASSERT(cert_info, (L""));
-  PCERT_PUBLIC_KEY_INFO public_key_info = &cert_info->SubjectPublicKeyInfo;
-  ASSERT(public_key_info, (L""));
-
-  // Reset the CSP and key in case it has been used already
-  reset(key_);
-  reset(csp_);
-
-  // Get the default CSP.  With CRYPT_VERIFYCONTEXT don't need to worry
-  // about creating/destroying a key container.
-  // TODO(omaha):  Why wasn't PROV_RSA_SIG available?  Maybe looking for the
-  // default isn't a good idea?
-  BOOL b = ::CryptAcquireContext(address(csp_),
-                                 NULL,
-                                 NULL,
-                                 kProviderType,
-                                 CRYPT_VERIFYCONTEXT|CRYPT_SILENT);
-  if (!b) {
-    DWORD err = ::GetLastError();
-    UTIL_LOG(LE, (L"[GetCSPContextAndKey]"
-                  L"[failed to acquire CSP, err 0x%08lx]", err));
-    return HRESULT_FROM_WIN32(err);
-  }
-  UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::GetCSPContextAndKey]"
-                L"[new CSP 0x%08lx]", get(csp_)));
-
-  // Convert the public key in encoded form into a CryptoAPI HCRYPTKEY
-  b = ::CryptImportPublicKeyInfo(get(csp_),
-                                 kEncodingType,
-                                 public_key_info,
-                                 address(key_));
-  if (!b) {
-    DWORD err = ::GetLastError();
-    UTIL_LOG(LE, (L"[GetCSPContextAndKey]"
-                  L"[failed to import public key, err 0x%08lx]", err));
-    return HRESULT_FROM_WIN32(err);
-  }
-  UTIL_LOG(L3, (L"[CryptoSignatureVerificationCertificate::GetCSPContextAndKey]"
-                L"[new key 0x%08lx]", get(key_)));
-
-  *csp_context = get(csp_);
-  *public_key = get(key_);
-
-  return S_OK;
-}
-
-// To verify the signature of some data using CryptoAPI you first hash
-// it into a hash object, then verify it using the CSP and a public key.
-// In this case the CryptVerifySignature takes the key (of type
-// AT_SIGNATURE) as a separate parameter. The
-// CryptoSignatureVerificationCertificate can provide the proper CSP and
-// the public key from the certificate.
-
-CryptoVerifySignature::CryptoVerifySignature(
-    CryptoSignatureVerificationCertificate& certificate)
-    : certificate_(&certificate) {
-}
-
-CryptoVerifySignature::~CryptoVerifySignature() {
-}
-
-HRESULT CryptoVerifySignature::Validate(const TCHAR* filepath,
-                                        uint32 max_len,
-                                        const std::vector<byte>& signature_in) {
-  ASSERT(filepath, (L""));
-  std::vector<byte> buffer;
-  HRESULT hr = ReadEntireFile(filepath, max_len, &buffer);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (L"[CryptoVerifySignature::Validate]"
-                  L"['%s' not read, hr 0x%08lx]", filepath, hr));
-    return hr;
-  }
-  return Validate(buffer, signature_in);
-}
-
-HRESULT CryptoVerifySignature::Validate(const std::vector<byte>& buffer_in,
-                                        const std::vector<byte>& signature_in) {
-  ASSERT(certificate_, (L""));
-  ASSERT1(!buffer_in.empty());
-  ASSERT1(!signature_in.empty());
-
-  UTIL_LOG(L2, (L"[CryptoVerifySignature::Validate]"
-                L"[buffer of %d bytes, signature of %d bytes]",
-                buffer_in.size(), signature_in.size()));
-
-  // Get the CSP context and the public key from the certificate
-  HCRYPTPROV csp = NULL;
-  HCRYPTKEY key = NULL;
-  HRESULT hr = certificate_->GetCSPContextAndKey(&csp, &key);
-  ASSERT(SUCCEEDED(hr) && csp && key, (L""));
-
-  // Hash the data
-  CryptDetails::scoped_crypt_hash hash;
-  BOOL b = ::CryptCreateHash(csp, kHashAlgorithm, 0, 0, address(hash));
-  if (!b) {
-    // hash is now invalid, but might not be NULL, so stomp on it
-    DWORD err = ::GetLastError();
-    ASSERT(!hash, (L""));
-    UTIL_LOG(LE, (L"[CrypoVerifySignature::Validate]"
-                  L"[could not create hash], err 0x%08lx", err));
-    return HRESULT_FROM_WIN32(err);
-  }
-  UTIL_LOG(L3, (L"CryptoVerifySignature::Validate new hash 0x%08lx", hash));
-
-  b = ::CryptHashData(get(hash),
-                      &buffer_in.front(),
-                      buffer_in.size(),
-                      0 /*flags*/);
-  if (!b) {
-    DWORD err = ::GetLastError();
-    UTIL_LOG(LE, (L"[CryptoVerifySignature::Validate]"
-                  L"[could not hash data, err 0x%08lx]", err));
-    return HRESULT_FROM_WIN32(err);
-  }
-
-  // Verify the hash
-  b = ::CryptVerifySignature(get(hash),
-                             &signature_in.front(),
-                             signature_in.size(),
-                             key,
-                             NULL,
-                             0 /*flags*/);
-  if (!b) {
-    DWORD err = ::GetLastError();
-#ifdef LOGGING
-    CString encoded_signature;
-    Base64::Encode(signature_in, &encoded_signature, false);
-
-    UTIL_LOG(LE, (_T("CryptoVerifySignature::Validate could not ")
-                  _T("verify signature, err 0x%08lx with sig \"%s\""),
-                  err, encoded_signature));
-#endif
-    if (err == NTE_BAD_SIGNATURE)
-      return SIGS_E_INVALID_SIGNATURE;
-    else
-      return HRESULT_FROM_WIN32(err);
-  }
-
-  return S_OK;
-}
-
-HRESULT SignData(const TCHAR* certificate_path,
-                 const TCHAR* certificate_password,
-                 const TCHAR* certificate_subject_name,
-                 const std::vector<byte>& data,
-                 CString* signature_base64) {
-  ASSERT(certificate_path, (L""));
-  ASSERT(certificate_password, (L""));
-  // certificate_subject_name can be NULL
-  ASSERT(signature_base64, (L""));
-
-  CryptoSigningCertificate certificate;
-  RET_IF_FAILED(certificate.ImportCertificate(certificate_path,
-                                              certificate_password,
-                                              certificate_subject_name));
-
-  CryptoComputeSignature signer(&certificate);
-  std::vector<byte> signature;
-  RET_IF_FAILED(signer.Sign(data, &signature));
-  RET_IF_FAILED(Base64::Encode(signature, signature_base64, false));
-
-  return S_OK;
-}
-
-HRESULT VerifyData(const TCHAR* certificate_path,
-                   const TCHAR* certificate_subject_name,
-                   const std::vector<byte>& data,
-                   const TCHAR* signature_base64) {
-  ASSERT(certificate_path, (L""));
-  // certificate_subject_name can be NULL
-  ASSERT(signature_base64, (L""));
-
-  std::vector<byte> signature;
-  RET_IF_FAILED(Base64::Decode(CString(signature_base64), &signature));
-
-  CryptoSignatureVerificationCertificate certificate;
-  RET_IF_FAILED(certificate.ImportCertificate(certificate_path,
-                                              certificate_subject_name));
-
-  CryptoVerifySignature verifier(certificate);
-  RET_IF_FAILED(verifier.Validate(data, signature));
-
-  return S_OK;
-}
-
-HRESULT VerifyData(const std::vector<byte>& certificate_buffer,
-                   const TCHAR* certificate_subject_name,
-                   const std::vector<byte>& data,
-                   const TCHAR* signature_base64) {
-  // certificate_subject_name can be NULL
-  ASSERT(signature_base64, (L""));
-
-  std::vector<byte> signature;
-  RET_IF_FAILED(Base64::Decode(CString(signature_base64), &signature));
-
-  CryptoSignatureVerificationCertificate certificate;
-  RET_IF_FAILED(certificate.ImportCertificate(certificate_buffer,
-                                              certificate_subject_name));
-
-  CryptoVerifySignature verifier(certificate);
-  RET_IF_FAILED(verifier.Validate(data, signature));
-
-  return S_OK;
-}
-
-// Authenticate files
-HRESULT AuthenticateFiles(const std::vector<CString>& files,
-                          const CString& hash) {
-  ASSERT1(files.size() > 0);
-  ASSERT1(!hash.IsEmpty());
-
-  // Test the bytes against its hash
-  std::vector<byte> hash_vector;
-  RET_IF_FAILED(Base64::Decode(hash, &hash_vector));
-  ASSERT1(hash_vector.size() == CryptoHash::kHashSize);
-
-  CryptoHash crypto;
-  return crypto.Validate(files, kMaxFileSizeForAuthentication, hash_vector);
-}
-
-}  // namespace omaha
-
diff --git a/common/signatures.h b/common/signatures.h
deleted file mode 100644
index 0a23fe7..0000000
--- a/common/signatures.h
+++ /dev/null
@@ -1,297 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// signatures.h
-//
-// Classes and functions related to crypto-hashes of buffers and digital
-// signatures of buffers.
-
-#ifndef OMAHA_COMMON_SIGNATURES_H__
-#define OMAHA_COMMON_SIGNATURES_H__
-
-#include <windows.h>
-#include <wincrypt.h>
-#include <atlstr.h>
-#include <vector>
-#include "base/basictypes.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/sha.h"
-
-namespace omaha {
-
-// Forward decls of classes defined here
-class CryptoHash;
-class CryptoComputeSignature;
-class CryptoVerifySignature;
-class CryptoSigningCertificate;
-class CryptoSignatureVerificationCertificate;
-
-// Useful scoped pointers for working with CryptoAPI objects
-namespace CryptDetails {
-
-  void crypt_close_store(HCERTSTORE);
-  void crypt_release_context(HCRYPTPROV);
-  void crypt_free_certificate(PCCERT_CONTEXT);
-  void crypt_destroy_key(HCRYPTKEY);
-
-  typedef close_fun<void (*)(HCERTSTORE),crypt_close_store>          smart_close_store;       // NOLINT
-  typedef close_fun<void (*)(HCRYPTPROV),crypt_release_context>      smart_release_context;   // NOLINT
-  typedef close_fun<void (*)(PCCERT_CONTEXT),crypt_free_certificate> smart_free_certificate;  // NOLINT
-  typedef close_fun<void (*)(HCRYPTKEY),crypt_destroy_key>           smart_destroy_key;       // NOLINT
-
-  typedef scoped_any<HCERTSTORE,smart_close_store,null_t>            scoped_crypt_store;      // NOLINT
-  typedef scoped_any<HCRYPTPROV,smart_release_context,null_t>        scoped_crypt_context;    // NOLINT
-  typedef scoped_any<PCCERT_CONTEXT,smart_free_certificate,null_t>   scoped_crypt_cert;       // NOLINT
-  typedef scoped_any<HCRYPTKEY,smart_destroy_key,null_t>             scoped_crypt_key;        // NOLINT
-}
-
-// A namespace for encoding binary into base64 (portable ASCII) representation
-// and for decoding it again.
-namespace Base64 {
-  // Binary -> base64 in a buffer
-  HRESULT Encode(const std::vector<byte>& buffer_in,
-                 std::vector<byte>* encoded,
-                 bool break_into_lines = true);
-
-  // Binary -> base64 in a string
-  HRESULT Encode(const std::vector<byte>& buffer_in,
-                 CStringA* encoded,
-                 bool break_into_lines = true);
-
-  // Binary -> base64 in a wide string
-  HRESULT Encode(const std::vector<byte>& buffer_in,
-                 CString* encoded,
-                 bool break_into_lines = true);
-
-  // Base64 in a buffer -> binary
-  HRESULT Decode(const std::vector<byte>& encoded,
-                 std::vector<byte>* buffer_out);
-
-  // Base64 in a CStringA -> binary
-  HRESULT Decode(const CStringA& encoded, std::vector<byte>* buffer_out);
-
-  // Base64 in a CString -> binary
-  HRESULT Decode(const CString& encoded, std::vector<byte>* buffer_out);
-}
-
-
-// Compute and validate SHA-1 hashes of data
-class CryptoHash {
-  public:
-
-    CryptoHash();
-    ~CryptoHash();
-
-    static const int kHashSize = SecureHashAlgorithm::kDigestSize;
-
-    // Hash a file
-    HRESULT Compute(const TCHAR * filepath,
-                    uint64 max_len,
-                    std::vector<byte>* hash_out);
-
-    // Hash a list of files
-    HRESULT Compute(const std::vector<CString>& filepaths,
-                    uint64 max_len,
-                    std::vector<byte>* hash_out);
-
-    // Hash a buffer
-    HRESULT Compute(const std::vector<byte>& buffer_in,
-                    std::vector<byte>* hash_out);
-
-    // Verify hash of a file
-    HRESULT Validate(const TCHAR * filepath,
-                     uint64 max_len,
-                     const std::vector<byte>& hash_in);
-
-    // Verify hash of a list of files
-    HRESULT Validate(const std::vector<CString>& filepaths,
-                     uint64 max_len,
-                     const std::vector<byte>& hash_in);
-
-    // Verify hash of a buffer
-    HRESULT Validate(const std::vector<byte>& buffer_in,
-                     const std::vector<byte>& hash_in);
-
-  private:
-    // Compute or verify hash of a file
-    HRESULT ComputeOrValidate(const std::vector<CString>& filepaths,
-                              uint64 max_len,
-                              const std::vector<byte>* hash_in,
-                              std::vector<byte>* hash_out);
-
-    // Compute or verify hash of a buffer
-    HRESULT ComputeOrValidate(const std::vector<byte>& buffer_in,
-                              const std::vector<byte>* hash_in,
-                              std::vector<byte>* hash_out);
-
-    DISALLOW_EVIL_CONSTRUCTORS(CryptoHash);
-};
-
-
-// Import and use a certificate for signing data (has a private key)
-class CryptoSigningCertificate {
-  public:
-    CryptoSigningCertificate();
-    ~CryptoSigningCertificate();
-
-    // Import certificate - with both public key and private key.
-    // Must be in PFX format.  Password must unlock the PFX file.
-    // subject_name is the certificate's subject name (who it was
-    // issued to) - if not NULL then it is checked for an exact
-    // match against the certificate.
-
-    // User can get the certificate in PFX format by following the procedure at
-    // http://support.globalsign.net/en/objectsign/transform.cfm
-
-    HRESULT ImportCertificate(const TCHAR * filepath,
-                              const TCHAR * password,
-                              const TCHAR * subject_name);
-
-    HRESULT ImportCertificate(const std::vector<byte>& certificate_in,
-                              const TCHAR * password,
-                              const TCHAR * subject_name);
-
-    CString subject_name() { return subject_name_; }
-
-  private:
-    static const int kMaxCertificateSize = 100000;
-
-    CryptDetails::scoped_crypt_store   store_;
-    CryptDetails::scoped_crypt_cert    certificate_;
-    CryptDetails::scoped_crypt_context csp_;
-    CString subject_name_;
-    DWORD key_spec_;
-
-    friend class CryptoComputeSignature;
-    // Get the CSP with the private key
-    // (CryptoSigningCertificate retains ownership of csp_context.)
-    HRESULT GetCSPContext(HCRYPTPROV* csp_context);
-
-    DISALLOW_EVIL_CONSTRUCTORS(CryptoSigningCertificate);
-};
-
-
-// Compute digital signatures
-class CryptoComputeSignature {
-  public:
-    explicit CryptoComputeSignature(CryptoSigningCertificate* certificate);
-    ~CryptoComputeSignature();
-
-    // Sign a file, returning a separate signature
-    HRESULT Sign(const TCHAR * filepath,
-                 uint32 max_len,
-                 std::vector<byte>* signature_out);
-
-    // Sign a chunk of memory, returning a separate signature
-    HRESULT Sign(const std::vector<byte>& buffer_in,
-                 std::vector<byte>* signature_out);
-
-  private:
-    // Does not take ownership of the certificate
-    CryptoSigningCertificate* const certificate_;
-
-    DISALLOW_EVIL_CONSTRUCTORS(CryptoComputeSignature);
-};
-
-
-// Import and use a certificate for verifying signatures (has public key only)
-class CryptoSignatureVerificationCertificate {
-  public:
-    CryptoSignatureVerificationCertificate();
-    ~CryptoSignatureVerificationCertificate();
-
-    // Import certificate - with only public key.  Must be in DER (.cer) format.
-    // subject_name is the certificate's subject name (who it was
-    // issued to) - if not NULL then it is checked for an exact
-    // match against the certificate.
-
-    // User can get certificate in DER format (.cer) by exporting from
-    // certmgr.exe, using openssl, etc.)
-
-    HRESULT ImportCertificate(const TCHAR * filepath,
-                              const TCHAR * subject_name);
-    HRESULT ImportCertificate(const std::vector<byte>& certificate_in,
-                              const TCHAR * subject_name);
-
-    CString subject_name() { return subject_name_; }
-
-  private:
-    static const int kMaxCertificateSize = 100000;
-
-    CryptDetails::scoped_crypt_cert    certificate_;
-    CryptDetails::scoped_crypt_context csp_;
-    CryptDetails::scoped_crypt_key     key_;
-    CString subject_name_;
-
-    friend class CryptoVerifySignature;
-    // Get the CSP and the public key
-    // (CryptoSignatureVerificationCertificate retains ownership of csp_context
-    // and public_key.)
-    HRESULT GetCSPContextAndKey(HCRYPTPROV* csp_context, HCRYPTKEY* public_key);
-
-    DISALLOW_EVIL_CONSTRUCTORS(CryptoSignatureVerificationCertificate);
-};
-
-
-// Verify digital signatures
-class CryptoVerifySignature {
-  public:
-    explicit CryptoVerifySignature(
-        CryptoSignatureVerificationCertificate& certificate);
-    ~CryptoVerifySignature();
-
-    // Validate signature of a file, signature given separately
-    HRESULT Validate(const TCHAR * filepath,
-                     uint32 max_len,
-                     const std::vector<byte>& signature_in);
-
-    // Validate signature of a buffer of data, signature given separately
-    HRESULT Validate(const std::vector<byte>& buffer_in,
-                     const std::vector<byte>& signature_in);
-
-  private:
-    // Does not take ownership of the certificate
-    CryptoSignatureVerificationCertificate* const certificate_;
-
-    DISALLOW_EVIL_CONSTRUCTORS(CryptoVerifySignature);
-};
-
-
-// All-in-one routine to sign a chunk of data and return the signature
-// (encoded in base64)
-HRESULT SignData(const TCHAR* certificate_path,
-                 const TCHAR* certificate_password,
-                 const TCHAR* certificate_subject_name,
-                 const std::vector<byte>& data,
-                 CString* signature_base64);
-
-// All-in-one routine to verify the signature of a chunk of data
-HRESULT VerifyData(const TCHAR* certificate_path,
-                   const TCHAR* certificate_subject_name,
-                   const std::vector<byte>& data,
-                   const TCHAR* signature_base64);
-
-HRESULT VerifyData(const std::vector<byte>& certificate_buffer,
-                   const TCHAR* certificate_subject_name,
-                   const std::vector<byte>& data,
-                   const TCHAR* signature_base64);
-
-// Authenticate files
-HRESULT AuthenticateFiles(const std::vector<CString>& files,
-                          const CString& hash);
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_SIGNATURES_H__
diff --git a/common/signatures_unittest.cc b/common/signatures_unittest.cc
deleted file mode 100644
index 78778f9..0000000
--- a/common/signatures_unittest.cc
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// signatures_unittest.cpp
-//
-// Unittests for classes and functions related to crypto-hashes of buffers and
-// digital signatures of buffers.
-// TODO(omaha): There are a number of places inside the signatures code, where
-// empty vector iterators were being dereferenced. Ensure that all these are
-// being tested.
-
-#include <cstring>
-#include <vector>
-
-#include "omaha/common/signatures.h"
-#include "omaha/common/module_utils.h"
-#include "omaha/common/utils.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-namespace {
-
-struct {
-  char* binary;
-  char* base64;
-} test_data[] = {
-  "",                                "",
-  "what",                            "d2hhdA==",
-  "what will print out",             "d2hhdCB3aWxsIHByaW50IG91dA==",
-  "foobar",                          "Zm9vYmFy",
-  "a man, a plan, a canal: panama!", "YSBtYW4sIGEgcGxhbiwgYSBjYW5hbDogcGFuYW1hIQ==",    // NOLINT
-};
-
-// This test data from http://en.wikipedia.org/wiki/SHA-1:
-struct {
-  char* binary;
-  byte  hash[20];
-} test_hash[] = {
-  "The quick brown fox jumps over the lazy dog",
-    0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28, 0xfc, 0xed, 0x84,
-    0x9e, 0xe1, 0xbb, 0x76, 0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12,
-  "The quick brown fox jumps over the lazy cog",
-    0xde, 0x9f, 0x2c, 0x7f, 0xd2, 0x5e, 0x1b, 0x3a, 0xfa, 0xd3,
-    0xe8, 0x5a, 0x0b, 0xd1, 0x7d, 0x9b, 0x10, 0x0d, 0xb4, 0xb3,
-};
-
-}  // namespace
-
-TEST(SignaturesTest, Base64) {
-  for (size_t i = 0; i != arraysize(test_data); i++) {
-    std::vector<byte> buffer(strlen(test_data[i].binary));
-    if (strlen(test_data[i].binary) != 0) {
-      memcpy(&buffer.front(), test_data[i].binary, strlen(test_data[i].binary));
-    }
-    CStringA test_e;
-    ASSERT_SUCCEEDED(Base64::Encode(buffer, &test_e));
-    ASSERT_STREQ(test_e, test_data[i].base64);
-    std::vector<byte> test_d;
-    uint32 test_d_written = 0;
-    ASSERT_SUCCEEDED(Base64::Decode(test_e, &test_d));
-    ASSERT_EQ(test_d.size(), strlen(test_data[i].binary));
-    if (strlen(test_data[i].binary) != 0) {
-      ASSERT_EQ(0, memcmp(&test_d.front(),
-                          test_data[i].binary,
-                          strlen(test_data[i].binary)));
-    }
-  }
-}
-
-TEST(SignaturesTest, CryptoHash) {
-  CryptoHash chash;
-  for (size_t i = 0; i != arraysize(test_hash); i++) {
-    std::vector<byte> buffer(strlen(test_hash[i].binary));
-    memcpy(&buffer.front(), test_hash[i].binary, strlen(test_hash[i].binary));
-    std::vector<byte> hash;
-    ASSERT_SUCCEEDED(chash.Compute(buffer, &hash));
-    ASSERT_EQ(hash.size(), CryptoHash::kHashSize);
-    ASSERT_EQ(0, memcmp(&hash.front(),
-                        test_hash[i].hash,
-                        CryptoHash::kHashSize));
-    ASSERT_SUCCEEDED(chash.Validate(buffer, hash));
-  }
-}
-
-TEST(SignaturesTest, CreationVerification) {
-  TCHAR module_directory[MAX_PATH] = {0};
-  ASSERT_TRUE(GetModuleDirectory(NULL, module_directory));
-  CString directory;
-  directory.Format(_T("%s\\unittest_support"), module_directory);
-
-  CString encoded_cert_with_private_key_path;
-  encoded_cert_with_private_key_path.AppendFormat(
-      _T("%s\\certificate-with-private-key.pfx"), directory);
-  CString encoded_cert_without_private_key_path;
-  encoded_cert_without_private_key_path.AppendFormat(
-      _T("%s\\certificate-without-private-key.cer"), directory);
-  CString raw_test_data_path;
-  raw_test_data_path.AppendFormat(_T("%s\\declaration.txt"), directory);
-
-  // Get cert with private key and cert without private key.
-  std::vector<byte> encoded_cert_with_private_key;
-  std::vector<byte> encoded_cert_without_private_key;
-  ASSERT_SUCCEEDED(ReadEntireFile(encoded_cert_with_private_key_path,
-                                  0,
-                                  &encoded_cert_with_private_key));
-  ASSERT_SUCCEEDED(ReadEntireFile(encoded_cert_without_private_key_path,
-                                  0,
-                                  &encoded_cert_without_private_key));
-  CString cert_password = _T("f00bar");
-  CString cert_subject_name = _T("Unofficial Google Test");
-
-  // Get testdata.
-  std::vector<byte> raw_testdata;
-  ASSERT_SUCCEEDED(ReadEntireFile(raw_test_data_path, 0, &raw_testdata));
-
-  // Create a signing certificate.
-  CryptoSigningCertificate signing_certificate;
-  ASSERT_SUCCEEDED(signing_certificate.ImportCertificate(
-      encoded_cert_with_private_key, cert_password, cert_subject_name));
-
-  // Create a signature object and sign the test data.
-  std::vector<byte> signature;
-  CryptoComputeSignature signer(&signing_certificate);
-  ASSERT_SUCCEEDED(signer.Sign(raw_testdata, &signature));
-
-  // Create a validating certificate.
-  CryptoSignatureVerificationCertificate verification_certificate;
-  ASSERT_SUCCEEDED(verification_certificate.ImportCertificate(
-      encoded_cert_without_private_key, cert_subject_name));
-
-  // Create a signature object and verify the test data's signature.
-  CryptoVerifySignature verifier(verification_certificate);
-  ASSERT_SUCCEEDED(verifier.Validate(raw_testdata, signature));
-
-  // Mess up the signature and show it doesn't verify.
-  size_t mid = signature.size() / 2;
-  byte mid_byte = signature[mid];
-  signature[mid] = ~mid_byte;
-  ASSERT_FAILED(verifier.Validate(raw_testdata, signature));
-
-  // Restore the signature, mess up the test data, and show it doesn't verify.
-  signature[mid] = mid_byte;
-  mid = raw_testdata.size() / 2;
-  mid_byte = raw_testdata[mid];
-  raw_testdata[mid] = ~mid_byte;
-  ASSERT_FAILED(verifier.Validate(raw_testdata, signature));
-}
-
-}  // namespace omaha
-
diff --git a/common/signaturevalidator.cc b/common/signaturevalidator.cc
deleted file mode 100644
index d2d58d1..0000000
--- a/common/signaturevalidator.cc
+++ /dev/null
@@ -1,518 +0,0 @@
-// Copyright 2002-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include "omaha/common/signaturevalidator.h"
-
-#include <atltime.h>
-#include <softpub.h>
-#include <wincrypt.h>
-#include <wintrust.h>
-#pragma warning(push)
-// C4100: unreferenced formal parameter
-// C4310: cast truncates constant value
-// C4548: expression before comma has no effect
-#pragma warning(disable : 4100 4310 4548)
-#include "base/basictypes.h"
-#pragma warning(pop)
-#include "omaha/common/error.h"
-
-namespace omaha {
-
-namespace {
-
-const LPCTSTR kEmptyStr = _T("");
-const DWORD kCertificateEncoding = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
-
-// Gets a handle to the certificate store and optionally the cryptographic
-// message from the specified file.
-// The caller is responsible for closing the store and message.
-// message can be NULL if the handle is not needed.
-HRESULT GetCertStoreFromFile(const wchar_t* signed_file,
-                             HCERTSTORE* cert_store,
-                             HCRYPTMSG* message) {
-  if (!signed_file || !cert_store) {
-    return E_INVALIDARG;
-  }
-
-  // Get message handle and store handle from the signed file.
-  if (!::CryptQueryObject(CERT_QUERY_OBJECT_FILE,
-                          signed_file,
-                          CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
-                          CERT_QUERY_FORMAT_FLAG_BINARY,
-                          0,              // reserved, must be 0
-                          NULL,           // pdwMsgAndCertEncodingType
-                          NULL,           // pdwContentType
-                          NULL,           // pdwFormatType
-                          cert_store,
-                          message,
-                          NULL)) {        // ppvContext
-    return HRESULT_FROM_WIN32(::GetLastError());
-  }
-
-  return S_OK;
-}
-
-// Gets the signer info from the crypt message.
-// The caller is responsible for freeing the signer info using LocalFree.
-HRESULT GetSignerInfo(HCRYPTMSG message, PCMSG_SIGNER_INFO* signer_info) {
-  if (!signer_info) {
-    return E_INVALIDARG;
-  }
-  *signer_info = NULL;
-
-  DWORD info_size = 0;
-  if (!::CryptMsgGetParam(message,
-                          CMSG_SIGNER_INFO_PARAM,
-                          0,
-                          NULL,
-                          &info_size)) {
-    return HRESULT_FROM_WIN32(::GetLastError());
-  }
-
-  *signer_info = static_cast<PCMSG_SIGNER_INFO>(::LocalAlloc(LPTR, info_size));
-  if (!*signer_info) {
-    return HRESULT_FROM_WIN32(::GetLastError());
-  }
-
-  if (!::CryptMsgGetParam(message,
-                          CMSG_SIGNER_INFO_PARAM,
-                          0,
-                          *signer_info,
-                          &info_size)) {
-    return HRESULT_FROM_WIN32(::GetLastError());
-  }
-
-  return S_OK;
-}
-
-// Gets the signer info for the time stamp signature in the specified signature.
-HRESULT GetTimeStampSignerInfo(PCMSG_SIGNER_INFO signer_info,
-                               PCMSG_SIGNER_INFO* countersigner_info) {
-  if (!signer_info || !countersigner_info) {
-    return E_INVALIDARG;
-  }
-  *countersigner_info = NULL;
-
-  PCRYPT_ATTRIBUTE attr = NULL;
-
-  // The countersigner info is contained in the unauthenticated attributes and
-  // indicated by the szOID_RSA_counterSign OID.
-  for (size_t i = 0; i < signer_info->UnauthAttrs.cAttr; ++i) {
-    if (lstrcmpA(szOID_RSA_counterSign,
-                 signer_info->UnauthAttrs.rgAttr[i].pszObjId) == 0) {
-      attr = &signer_info->UnauthAttrs.rgAttr[i];
-      break;
-    }
-  }
-
-  if (!attr) {
-    return E_FAIL;
-  }
-
-  // Decode and get CMSG_SIGNER_INFO structure for the timestamp certificate.
-  DWORD data_size = 0;
-  if (!::CryptDecodeObject(kCertificateEncoding,
-                           PKCS7_SIGNER_INFO,
-                           attr->rgValue[0].pbData,
-                           attr->rgValue[0].cbData,
-                           0,
-                           NULL,
-                           &data_size)) {
-    return HRESULT_FROM_WIN32(::GetLastError());
-  }
-
-  *countersigner_info =
-      static_cast<PCMSG_SIGNER_INFO>(::LocalAlloc(LPTR, data_size));
-  if (!*countersigner_info) {
-    return HRESULT_FROM_WIN32(::GetLastError());
-  }
-
-  if (!::CryptDecodeObject(kCertificateEncoding,
-                           PKCS7_SIGNER_INFO,
-                           attr->rgValue[0].pbData,
-                           attr->rgValue[0].cbData,
-                           0,
-                           *countersigner_info,
-                           &data_size)) {
-    return HRESULT_FROM_WIN32(::GetLastError());
-  }
-
-  return S_OK;
-}
-
-// Gets the time of the date stamp for the specified signature.
-// The time is in UTC.
-HRESULT GetDateOfTimeStamp(PCMSG_SIGNER_INFO signer_info,
-                           SYSTEMTIME* system_time) {
-  if (!signer_info || !system_time) {
-    return E_INVALIDARG;
-  }
-
-  PCRYPT_ATTRIBUTE attr = NULL;
-
-  // The signing time is contained in the authenticated attributes and
-  // indicated by the szOID_RSA_signingTime OID.
-  for (size_t i = 0; i < signer_info->AuthAttrs.cAttr; ++i) {
-    if (lstrcmpA(szOID_RSA_signingTime,
-                 signer_info->AuthAttrs.rgAttr[i].pszObjId) == 0) {
-      attr = &signer_info->AuthAttrs.rgAttr[i];
-      break;
-    }
-  }
-
-  if (!attr) {
-    return E_FAIL;
-  }
-
-  FILETIME file_time = {0};
-
-  // Decode and get FILETIME structure.
-  DWORD data_size = sizeof(file_time);
-  if (!::CryptDecodeObject(kCertificateEncoding,
-                           szOID_RSA_signingTime,
-                           attr->rgValue[0].pbData,
-                           attr->rgValue[0].cbData,
-                           0,
-                           &file_time,
-                           &data_size)) {
-    return HRESULT_FROM_WIN32(::GetLastError());
-  }
-
-  if (!::FileTimeToSystemTime(&file_time, system_time)) {
-    return HRESULT_FROM_WIN32(::GetLastError());
-  }
-
-  return S_OK;
-}
-
-}  // namespace
-
-CertInfo::CertInfo(const CERT_CONTEXT* given_cert_context)
-    : cert_context_(NULL) {
-  if (given_cert_context) {
-    // CertDuplicateCertificateContext just increases reference count of a given
-    // CERT_CONTEXT.
-    cert_context_ = CertDuplicateCertificateContext(given_cert_context);
-    not_valid_before_ = cert_context_->pCertInfo->NotBefore;
-    not_valid_after_ = cert_context_->pCertInfo->NotAfter;
-    // Extract signed party details.
-    ExtractIssuerInfo(cert_context_,
-                      &issuing_company_name_,
-                      &issuing_dept_name_,
-                      &trust_authority_name_);
-  }
-}
-
-CertInfo::~CertInfo() {
-  // Decrement reference count, if needed.
-  if (cert_context_)
-    CertFreeCertificateContext(cert_context_);
-}
-
-
-bool CertInfo::IsValidNow() const {
-  // we cannot directly get current time in FILETIME format.
-  // so first get it in SYSTEMTIME format and convert it into FILETIME.
-  SYSTEMTIME now;
-  GetSystemTime(&now);
-  FILETIME filetime_now;
-  SystemTimeToFileTime(&now, &filetime_now);
-  // CompareFileTime() is a windows function
-  return ((CompareFileTime(&filetime_now, &not_valid_before_) > 0)
-          && (CompareFileTime(&filetime_now, &not_valid_after_) < 0));
-}
-
-
-CString CertInfo::FileTimeToString(const FILETIME* ft) {
-  if (ft == NULL)
-    return _T("");
-  SYSTEMTIME st;
-  if (!FileTimeToSystemTime(ft, &st))
-    return _T("");
-
-  // Build a string showing the date and time.
-  CString time_str;
-  time_str.Format(_T("%02d/%02d/%d  %02d:%02d"), st.wDay, st.wMonth, st.wYear,
-    st.wHour, st.wMinute);
-  return time_str;
-}
-
-
-bool CertInfo::ExtractField(const CERT_CONTEXT* cert_context,
-                            const char* field_name,
-                            CString* field_value) {
-  if ((!cert_context) || (!field_name) || (!field_value)) {
-    return false;
-  }
-
-  field_value->Empty();
-
-  DWORD num_chars = ::CertGetNameString(cert_context,
-                                        CERT_NAME_ATTR_TYPE,
-                                        0,
-                                        const_cast<char*>(field_name),
-                                        NULL,
-                                        0);
-  if (num_chars > 1) {
-    num_chars = ::CertGetNameString(cert_context,
-                                  CERT_NAME_ATTR_TYPE,
-                                  0,
-                                  const_cast<char*>(field_name),
-                                  CStrBuf(*field_value, num_chars),
-                                  num_chars);
-  }
-
-  return num_chars > 1 ? true : false;
-}
-
-
-bool CertInfo::ExtractIssuerInfo(const CERT_CONTEXT* cert_context,
-                                 CString* orgn_name,
-                                 CString* orgn_dept_name,
-                                 CString* trust_authority) {
-  // trust-authority is optional, so no check.
-  if ((!orgn_name) || (!orgn_dept_name)) {
-    return false;
-  }
-
-  ExtractField(cert_context, szOID_COMMON_NAME, orgn_name);
-  ExtractField(cert_context, szOID_ORGANIZATIONAL_UNIT_NAME, orgn_dept_name);
-  if (trust_authority != NULL) {
-    ExtractField(cert_context, szOID_ORGANIZATION_NAME, trust_authority);
-  }
-
-  return true;
-}
-
-
-void CertList::FindFirstCert(CertInfo** result_cert_info,
-                             const CString &company_name_to_match,
-                             const CString &orgn_unit_to_match,
-                             const CString &trust_authority_to_match,
-                             bool allow_test_variant,
-                             bool check_cert_is_valid_now) {
-  if (!result_cert_info)
-    return;
-  (*result_cert_info) = NULL;
-
-  for (CertInfoList::const_iterator cert_iter = cert_list_.begin();
-       cert_iter != cert_list_.end();
-       ++cert_iter) {
-    // If any of the criteria does not match, continue on to next certificate
-    if (!company_name_to_match.IsEmpty()) {
-      const TCHAR* certificate_company_name =
-          (*cert_iter)->issuing_company_name_;
-      bool names_match = company_name_to_match == certificate_company_name;
-      if (!names_match && allow_test_variant) {
-        CString test_variant = company_name_to_match;
-        test_variant += _T(" (TEST)");
-        names_match = test_variant == certificate_company_name;
-      }
-      if (!names_match)
-        continue;
-    }
-    if (!orgn_unit_to_match.IsEmpty() &&
-        orgn_unit_to_match != (*cert_iter)->issuing_dept_name_)
-      continue;
-    if (!trust_authority_to_match.IsEmpty() &&
-        trust_authority_to_match != (*cert_iter)->trust_authority_name_)
-      continue;
-    // All the criteria matched. But, add only if it is a valid certificate.
-    if (!check_cert_is_valid_now || (*cert_iter)->IsValidNow()) {
-      (*result_cert_info) = (*cert_iter);
-      return;
-    }
-  }
-}
-
-
-void ExtractAllCertificatesFromSignature(const wchar_t* signed_file,
-                                         CertList* cert_list) {
-  if ((!signed_file) || (!cert_list))
-    return;
-
-  DWORD encoding_type = 0, content_type = 0, format_type = 0;
-  // If successful, cert_store will be populated by
-  // a store containing all the certificates related to the file signature.
-  HCERTSTORE cert_store = NULL;
-  BOOL succeeded = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
-                    signed_file,
-                    CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
-                    CERT_QUERY_FORMAT_FLAG_ALL,
-                    0,               // has to be zero as documentation says
-                    &encoding_type,  // DWORD *pdwMsgAndCertEncodingType,
-                    &content_type,   // DWORD *pdwContentType,
-                    &format_type,    // DWORD *pdwFormatType,
-                    &cert_store,     // HCERTSTORE *phCertStore,
-                    NULL,            // HCRYPTMSG *phMsg,
-                    NULL);           // const void** pvContext
-
-  if (succeeded && (cert_store != NULL)) {
-    PCCERT_CONTEXT   cert_context_ptr = NULL;
-    while ((cert_context_ptr =
-            CertEnumCertificatesInStore(cert_store, cert_context_ptr))
-           != NULL) {
-      CertInfo* cert_info = new CertInfo(cert_context_ptr);
-      cert_list->AddCertificate(cert_info);
-    }
-  }
-  if (cert_store) {
-    CertCloseStore(cert_store, 0);
-  }
-  return;
-}
-
-// Only check the CN. The OU can change.
-// TODO(omaha): A better way to implement the valid now check would be to add
-// a parameter to VerifySignature that adds WTD_LIFETIME_SIGNING_FLAG.
-bool VerifySigneeIsGoogleInternal(const wchar_t* signed_file,
-                                  bool check_cert_is_valid_now) {
-  const TCHAR* google_name = _T("Google Inc");
-
-  CertList cert_list;
-  ExtractAllCertificatesFromSignature(signed_file, &cert_list);
-  if (cert_list.size() > 0) {
-    CertInfo* required_cert = NULL;
-    // now, see if one of the certificates in the signature belongs to Google.
-    cert_list.FindFirstCert(&required_cert, google_name, CString(),
-                            CString(), true, check_cert_is_valid_now);
-    if (required_cert != NULL) {
-      return true;
-    }
-  }
-  return false;
-}
-
-// Does not verify that the certificate is currently valid.
-// VerifySignature verifies that the certificate was valid at signing time as
-// part of the normal signature verification.
-bool VerifySigneeIsGoogle(const wchar_t* signed_file) {
-  return VerifySigneeIsGoogleInternal(signed_file, false);
-}
-
-HRESULT VerifySignature(const wchar_t* signed_file, bool allow_network_check) {
-  // Don't pop up any windows
-  HWND const kWindowMode = reinterpret_cast<HWND>(INVALID_HANDLE_VALUE);
-
-  // Verify file & certificates
-  GUID verification_type = WINTRUST_ACTION_GENERIC_VERIFY_V2;
-
-  // Info for the file we're going to verify
-  WINTRUST_FILE_INFO file_info = {0};
-  file_info.cbStruct = sizeof(file_info);
-  file_info.pcwszFilePath = signed_file;
-
-  // Info for request to WinVerifyTrust
-  WINTRUST_DATA trust_data;
-  ZeroMemory(&trust_data, sizeof(trust_data));
-  trust_data.cbStruct = sizeof(trust_data);
-  trust_data.dwUIChoice = WTD_UI_NONE;               // no graphics
-  // No additional revocation checking -- note that this flag does not
-  // cancel the flag we set in dwProvFlags; it specifies that no -additional-
-  // checks are to be performed beyond the provider-specified ones.
-  trust_data.fdwRevocationChecks = WTD_REVOKE_NONE;
-  trust_data.dwProvFlags = WTD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
-
-  if (!allow_network_check)
-    trust_data.dwProvFlags |= WTD_CACHE_ONLY_URL_RETRIEVAL;
-
-  trust_data.dwUnionChoice = WTD_CHOICE_FILE;        // check a file
-  trust_data.pFile = &file_info;                     // check this file
-
-  // If the trust provider verifies that the subject is trusted for the
-  // specified action, the return value is zero. No other value besides zero
-  // should be considered a successful return.
-  LONG result = ::WinVerifyTrust(kWindowMode, &verification_type, &trust_data);
-  if (result != 0) {
-    return FAILED(result) ? result : HRESULT_FROM_WIN32(result);
-  }
-  return S_OK;
-}
-
-// This method must not return until the end to avoid leaking memory.
-// More info on Authenticode Signatures Time Stamping can be found at
-// http://msdn2.microsoft.com/en-us/library/bb931395.aspx.
-HRESULT GetSigningTime(const wchar_t* signed_file, SYSTEMTIME* signing_time) {
-  if (!signed_file || !signing_time) {
-    return E_INVALIDARG;
-  }
-
-  HCERTSTORE cert_store = NULL;
-  HCRYPTMSG message = NULL;
-  PCMSG_SIGNER_INFO signer_info = NULL;
-  PCMSG_SIGNER_INFO countersigner_info = NULL;
-
-  HRESULT hr = GetCertStoreFromFile(signed_file, &cert_store, &message);
-
-  if (SUCCEEDED(hr)) {
-    hr = GetSignerInfo(message, &signer_info);
-  }
-
-  if (SUCCEEDED(hr)) {
-    hr = GetTimeStampSignerInfo(signer_info, &countersigner_info);
-  }
-
-  if (SUCCEEDED(hr)) {
-    hr = GetDateOfTimeStamp(countersigner_info, signing_time);
-  }
-
-  if (cert_store) {
-    ::CertCloseStore(cert_store, 0);
-  }
-  if (message) {
-    ::CryptMsgClose(message);
-  }
-  ::LocalFree(signer_info);
-  ::LocalFree(countersigner_info);
-
-  return hr;
-}
-
-HRESULT VerifyFileSignedWithinDays(const wchar_t* signed_file, int days) {
-  if (!signed_file || days <= 0) {
-    return E_INVALIDARG;
-  }
-
-  SYSTEMTIME signing_time = {0};
-  HRESULT hr = GetSigningTime(signed_file, &signing_time);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // Use the Win32 API instead of CTime::GetCurrentTime() because the latter
-  // is broken in VS 2003 and 2005 and doesn't account for the timezone.
-  SYSTEMTIME current_system_time = {0};
-  ::GetSystemTime(&current_system_time);
-
-  CTime signed_time(signing_time);
-  CTime current_time(current_system_time);
-
-  if (current_time <= signed_time) {
-    return TRUST_E_TIME_STAMP;
-  }
-
-  CTimeSpan time_since_signed = current_time - signed_time;
-  CTimeSpan max_duration(days, 0, 0, 0);
-
-  if (max_duration < time_since_signed) {
-    return TRUST_E_TIME_STAMP;
-  }
-
-  return S_OK;
-}
-
-}  // namespace omaha
-
diff --git a/common/signaturevalidator_unittest.cc b/common/signaturevalidator_unittest.cc
deleted file mode 100644
index a025ada..0000000
--- a/common/signaturevalidator_unittest.cc
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Unit tests for the Google file signature validation.
-
-#include <windows.h>
-#include <atlstr.h>
-#include "omaha/common/app_util.h"
-#include "omaha/common/file.h"
-#include "omaha/common/signaturevalidator.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-class SignatureValidatorTest : public testing::Test {
-  virtual void SetUp() {
-  }
-
-  virtual void TearDown() {
-  }
-};
-
-
-TEST_F(SignatureValidatorTest, VerifySigneeIsGoogle_OfficiallySigned) {
-  const TCHAR kRelativePath[] = _T("unittest_support\\SaveArguments.exe");
-
-  CString executable_full_path(app_util::GetCurrentModuleDirectory());
-  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
-                           kRelativePath));
-  ASSERT_TRUE(File::Exists(executable_full_path));
-  EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path));
-}
-
-// Tests a certificate subject containing multiple CNs such as:
-//    "CN = Google Inc (TEST), CN = Some Other CN, ...
-// The code exactly matches on the first CN only.
-TEST_F(SignatureValidatorTest, VerifySigneeIsGoogle_TestSigned_MultipleCN) {
-  const TCHAR kRelativePath[] =
-      _T("unittest_support\\SaveArguments_multiple_cn.exe");
-
-  CString executable_full_path(app_util::GetCurrentModuleDirectory());
-  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
-                           kRelativePath));
-  ASSERT_TRUE(File::Exists(executable_full_path));
-  EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path));
-}
-
-TEST_F(SignatureValidatorTest,
-       VerifySigneeIsGoogle_OfficiallySigned_DifferentOU) {
-  const TCHAR kRelativePath[] =
-      _T("unittest_support\\SaveArguments_different_ou.exe");
-
-  CString executable_full_path(app_util::GetCurrentModuleDirectory());
-  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
-                           kRelativePath));
-  ASSERT_TRUE(File::Exists(executable_full_path));
-  EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path));
-}
-
-TEST_F(SignatureValidatorTest, VerifySigneeIsGoogle_OmahaTestSigned) {
-  const TCHAR kRelativePath[] =
-      _T("unittest_support\\SaveArguments_OmahaTestSigned.exe");
-
-  CString executable_full_path(app_util::GetCurrentModuleDirectory());
-  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
-                           kRelativePath));
-  ASSERT_TRUE(File::Exists(executable_full_path));
-  EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path));
-}
-
-// The certificate was valid when it was used to sign the executable, but it has
-// since expired.
-TEST_F(SignatureValidatorTest, VerifySigneeIsGoogle_SignedWithNowExpiredCert) {
-  const TCHAR kRelativePath[] =
-      _T("unittest_support\\GoogleUpdate_now_expired_cert.exe");
-
-  CString executable_full_path(app_util::GetCurrentModuleDirectory());
-  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
-                           kRelativePath));
-  ASSERT_TRUE(File::Exists(executable_full_path));
-  EXPECT_TRUE(VerifySigneeIsGoogle(executable_full_path));
-}
-
-TEST_F(SignatureValidatorTest, VerifySigneeIsGoogle_TestSigned_NoCN) {
-  const TCHAR kRelativePath[] =
-      _T("unittest_support\\SaveArguments_no_cn.exe");
-
-  CString executable_full_path(app_util::GetCurrentModuleDirectory());
-  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
-                           kRelativePath));
-  ASSERT_TRUE(File::Exists(executable_full_path));
-  EXPECT_FALSE(VerifySigneeIsGoogle(executable_full_path));
-}
-
-TEST_F(SignatureValidatorTest, VerifySigneeIsGoogle_TestSigned_WrongCN) {
-  const TCHAR kRelativePath[] =
-      _T("unittest_support\\SaveArguments_wrong_cn.exe");
-
-  CString executable_full_path(app_util::GetCurrentModuleDirectory());
-  ASSERT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
-                           kRelativePath));
-  ASSERT_TRUE(File::Exists(executable_full_path));
-  EXPECT_FALSE(VerifySigneeIsGoogle(executable_full_path));
-}
-
-}  // namespace omaha
diff --git a/common/single_instance.cc b/common/single_instance.cc
deleted file mode 100644
index fea4c06..0000000
--- a/common/single_instance.cc
+++ /dev/null
@@ -1,196 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Synchronization functions
-
-#include "omaha/common/single_instance.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-// Check to see whether an instance is already running across all sessions.
-// If not, enter single instance protection. The user must call Shutdown()
-// on that SingleInstance once single instance protection is no longer needed.
-bool SingleInstance::StartupSingleInstance(const TCHAR* id) {
-  ASSERT1(id);
-
-  bool already_running = false, already_running_in_different_session = false;
-  HRESULT hr = Startup(id,
-                       &already_running,
-                       &already_running_in_different_session);
-  ASSERT(SUCCEEDED(hr), (_T("")));
-
-  return already_running || already_running_in_different_session;
-}
-
-// Check to see whether an instance is already running in this session. If not,
-// enter session-only single instance protection. The user must call Shutdown()
-// on that SingleInstance once single instance protection is no longer needed.
-bool SingleInstance::StartupSingleSessionInstance(const TCHAR* id) {
-  ASSERT1(id);
-
-  bool already_running = false;
-  HRESULT hr = Startup(id, &already_running, NULL);
-  ASSERT(SUCCEEDED(hr), (_T("")));
-
-  return already_running;
-}
-
-// Startup a single instance protection. The user must call Shutdown() on
-// that SingleInstance once the single instance protection is no longer needed.
-//
-// Returns whether or not the process is already running
-// already_running means "already running in same session".
-// already_running_in_different_session means "already running on machine"
-HRESULT SingleInstance::Startup(const TCHAR* id,
-                                bool* already_running,
-                                bool* already_running_in_different_session) {
-  ASSERT1(id);
-  ASSERT1(already_running);
-
-  CString mutex_id;
-
-  // Use two mutexes: one to check for being the only instance in this
-  // session, and one for being the only instance in any terminal session.
-  // Only create (and check) the global mutex for one-per-machine check if
-  // the result is asked for.
-  // We don't actually obtain ownership of the mutex
-  // For information on the "Local" and "Global" namespace prefixes, see MSDN
-  // article "Kernel Object Namespaces".
-
-  // Create a user level mutex
-  CreateSyncId(id, SYNC_USER, &mutex_id);
-  RET_IF_FAILED(CreateInstanceMutex(mutex_id,
-                                    &user_mutex_handle_,
-                                    already_running));
-
-  // Create a global mutex
-  if (already_running_in_different_session) {
-    CreateSyncId(id, SYNC_GLOBAL, &mutex_id);
-    RET_IF_FAILED(CreateInstanceMutex(mutex_id,
-                                      &global_mutex_handle_,
-                                      already_running_in_different_session));
-  }
-
-  return S_OK;
-}
-
-// Create a mutex
-HRESULT SingleInstance::CreateInstanceMutex(const TCHAR* mutex_id,
-                                            HANDLE* mutex_handle,
-                                            bool* already_running) {
-  ASSERT1(mutex_id && *mutex_id);
-  ASSERT1(mutex_handle);
-  ASSERT1(already_running);
-
-  *already_running = false;
-
-  *mutex_handle = ::CreateMutex(NULL, false, mutex_id);
-  DWORD last_error = ::GetLastError();
-
-  // We check for both values because we sometimes see access
-  // denied.  We expect this to mean that the mutex was created by a
-  // different set of user credentials, which shouldn't happen under
-  // normal circumstances in our applications, but in fact we did
-  // see it happen.
-  if (last_error == ERROR_ALREADY_EXISTS || last_error == ERROR_ACCESS_DENIED) {
-    *already_running = true;
-    return S_OK;
-  }
-
-  if (*mutex_handle == NULL) {
-    HRESULT hr = HRESULT_FROM_WIN32(last_error);
-    ASSERT(false, (_T("[SingleInstance::CreateInstanceMutex]")
-                   _T("[failed to create mutex][%s][0x%x]"), mutex_id, hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-// Shutdown a single instance protection
-HRESULT SingleInstance::Shutdown() {
-  if (user_mutex_handle_) {
-    VERIFY(::CloseHandle(user_mutex_handle_), (_T("")));
-    user_mutex_handle_ = NULL;
-  }
-
-  if (global_mutex_handle_) {
-    VERIFY(::CloseHandle(global_mutex_handle_), (_T("")));
-    global_mutex_handle_ = NULL;
-  }
-
-  return S_OK;
-}
-
-// Check to see whether an instance is already running
-HRESULT SingleInstance::CheckAlreadyRunning(
-    const TCHAR* id,
-    bool* already_running,
-    bool* already_running_in_different_session) {
-  ASSERT1(id);
-  ASSERT1(already_running);
-
-  CString mutex_id;
-
-  // Open a user level mutex
-  CreateSyncId(id, SYNC_USER, &mutex_id);
-  RET_IF_FAILED(OpenInstanceMutex(mutex_id, already_running));
-
-  // Open a global mutex
-  if (already_running_in_different_session) {
-    CreateSyncId(id, SYNC_GLOBAL, &mutex_id);
-    RET_IF_FAILED(OpenInstanceMutex(mutex_id,
-                                    already_running_in_different_session));
-  }
-
-  return S_OK;
-}
-
-// Open a mutex
-HRESULT SingleInstance::OpenInstanceMutex(const TCHAR* mutex_id,
-                                          bool* already_running) {
-  ASSERT1(mutex_id && *mutex_id);
-  ASSERT1(already_running);
-
-  *already_running = false;
-
-  scoped_handle mutex_handle(::OpenMutex(NULL, false, mutex_id));
-  DWORD last_error = ::GetLastError();
-
-  if (get(mutex_handle) || last_error == ERROR_ACCESS_DENIED) {
-    UTIL_LOG(L3, (_T("[SingleInstance::OpenInstanceMutex]")
-                  _T("[already running][0x%x]"), last_error));
-    *already_running = true;
-    return S_OK;
-  }
-
-  if (last_error != ERROR_FILE_NOT_FOUND) {
-    HRESULT hr = HRESULT_FROM_WIN32(last_error);
-    ASSERT(false, (_T("[SingleInstance::OpenInstanceMutex]")
-                   _T("[failed to open mutex][%s][0x%x]"), mutex_id, hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-}  // namespace omaha
-
diff --git a/common/single_instance.h b/common/single_instance.h
deleted file mode 100644
index df0d2d0..0000000
--- a/common/single_instance.h
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-// Single Instance of a process running (plus some synchronization functions
-// that should be moved elsewhere)
-//
-// synchronization functions
-
-#ifndef OMAHA_COMMON_SINGLE_INSTANCE_H_
-#define OMAHA_COMMON_SINGLE_INSTANCE_H_
-
-#include <windows.h>
-
-#include "base/basictypes.h"
-#include "omaha/common/debug.h"
-
-namespace omaha {
-
-// Used to ensure only a single instance of a process per machine
-// (e.g., even in terminal server sessions)
-class SingleInstance {
- public:
-  // Constructor
-  SingleInstance() : user_mutex_handle_(NULL), global_mutex_handle_(NULL) {}
-
-  // Destructor
-  ~SingleInstance() { Shutdown(); }
-
-  // Check to see whether an instance is already running across all sessions.
-  // If not, enter single instance protection. The user must call Shutdown()
-  // on that SingleInstance once single instance protection is no longer needed.
-  bool StartupSingleInstance(const TCHAR* id);
-
-  // Check to see whether an instance is already running for this user. If not,
-  // enter user-only single instance protection. The user must call Shutdown()
-  // on that SingleInstance once single instance protection is no longer needed.
-  bool StartupSingleSessionInstance(const TCHAR* id);
-
-  // Startup a single instance protection. The user must call Shutdown() on
-  // that SingleInstance once the single instance protection is no longer needed.
-  HRESULT Startup(const TCHAR* id,
-                  bool* already_running,
-                  bool* already_running_in_different_session);
-
-  // Shutdown a single instance protection
-  HRESULT Shutdown();
-
-  // Check to see whether an instance is already running
-  static HRESULT CheckAlreadyRunning(
-      const TCHAR* id,
-      bool* already_running,
-      bool* already_running_in_different_session);
-
- private:
-  // Create a mutex
-  static HRESULT CreateInstanceMutex(const TCHAR* mutex_id,
-                                     HANDLE* mutex_handle,
-                                     bool* already_running);
-
-  // Open a mutex
-  static HRESULT OpenInstanceMutex(const TCHAR* mutex_id,
-                                   bool* already_running);
-
-  HANDLE user_mutex_handle_;
-  HANDLE global_mutex_handle_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(SingleInstance);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_SINGLE_INSTANCE_H_
diff --git a/common/singleton.h b/common/singleton.h
deleted file mode 100644
index 3e99f89..0000000
--- a/common/singleton.h
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Defines two classes
-// 1. class SingletonBase.
-// 2. template class Singleton.
-// Creation of singletons is a common
-// activity. Use Singleton class to not
-// repeat code every time.
-
-#ifndef OMAHA_COMMON_SINGLETON_H_
-#define OMAHA_COMMON_SINGLETON_H_
-
-#include "omaha/common/debug.h"
-#include "omaha/common/synchronized.h"
-
-
-// Very important design pattern.
-// Singleton class can be used in two ways.
-// 1. Pass the class you want to make into Singleton as a
-//    template parameter.
-//
-//    class SomeClass {
-//     protected:
-//    SomeClass(){}  // note - it is protected
-//    ~SomeClass(){} // note - it is protected
-//    public:
-//    void Foo() {}
-//  };
-//
-//   Singleton<SomeClass> s;
-//   s::Instance()->Foo();
-//
-//     OR
-// 2. You class can be derived from Singleton in a following way:
-//    class SomeClass : public Singleton<SomeClass> {
-//     protected:
-//    SomeClass(){}
-//    ~SomeClass(){}
-//    public:
-//    void Foo() {}
-//  };
-//
-//   SomeClass::Instance()->Foo();
-//
-//  There is no requirement on the class you want to make into
-//  Singleton except is has to have constructor that takes nothing.
-//  As long as the class has void constructor it can become a singleton.
-//  However if you want you class to be trully singleton you have to make
-//  it constructors and destructors protected. Than you can only access your
-//  class through the singlenot interface Instance().
-//  If simple void constructor is not enough for you class, provide some kind of
-//  initialization function, which could be called after the instance is
-// created.
-
-#define kSingletonMutexName               kLockPrefix L"Singleton_Creation_Lock"
-
-#ifdef _DEBUG
-  #define InstanceReturnTypeDeclaration SingletonProxy<T>
-  #define InstanceReturnTypeStatement   SingletonProxy<T>
-#else
-  #define InstanceReturnTypeDeclaration T*
-  #define InstanceReturnTypeStatement
-#endif
-
-template <typename T> class Singleton  {
-  // Caching pointers to Singletons is very dangerous and goes against
-  // Singleton philosophy. So we will return proxy from instance in Debug mode.
-  // In release mode we will not go this route for efficiency.
-  template <typename T> class SingletonProxy {
-    T* data_;
-  public:
-    explicit SingletonProxy(T* data) : data_(data) {}
-    T* operator->() const  {
-      return data_;
-    }
-    SingletonProxy& operator=(const SingletonProxy&);
-  };
-
- public:
-  Singleton() {}
-
-  // Use double-check pattern for efficiency.
-  // TODO(omaha): the pattern is broken on multicore.
-  static InstanceReturnTypeDeclaration Instance() {
-    if(instance_ == NULL) {
-      // We use GLock here since LLock will not give us synchronization and
-      // SimpleLock will create deadlock if one singleton is created in the
-      // constructor of the other singleton.
-      GLock creation_lock;
-      TCHAR mutex_name[MAX_PATH] = {0};
-      wsprintf(mutex_name, L"%s%d",
-               kSingletonMutexName, ::GetCurrentProcessId());
-
-      VERIFY1(creation_lock.Initialize(mutex_name));
-      __mutexScope(creation_lock);
-      if(instance_ == NULL)
-         instance_ = GetInstance();
-    }
-    return InstanceReturnTypeStatement(instance_);
-  }
-
- private:
-  static T* GetInstance() {
-    static MyT my_t;
-    return &my_t;
-  }
-
-  // shared between the same type T.
-  static T * instance_;
-
-  // Needed to access the protected constructor
-  // of a client.
-  class MyT : public T {
-  };
-};
-
-// This instance_ is shared between template of the same type.
-template <typename T>  T* Singleton<T>::instance_ = NULL;
-
-#endif  // OMAHA_COMMON_SINGLETON_H_
diff --git a/common/sta.cc b/common/sta.cc
deleted file mode 100644
index cb3c59f..0000000
--- a/common/sta.cc
+++ /dev/null
@@ -1,353 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/sta.h"
-
-#include <atlbase.h>
-#include <atlwin.h>
-#include "base/scoped_ptr.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/sta_call.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-namespace {
-
-class CallDispatcher;
-
-// ApartmentState maintains the state of the apartment. It keeps a reference
-// to a call dispatcher. The dispatcher is basically a window object that
-// handles user messages corresponding to each call.
-//
-// ApartmentState is a singleton. It's lifetime is controlled by the
-// 'InitializeApartment' and 'UnintializeApartment'.
-//
-// The current implementation is limited to the main STA. Creating multiple
-// apartments in a process is not possible.
-//
-// TODO(omaha): implement a simple reference counting of the threads to make
-// sure the all the calling threads have returned when the apt is destroyed.
-
-class ApartmentState {
- public:
-  ApartmentState(DWORD thread_id, CallDispatcher* call_disp)
-      : thread_id_(thread_id), call_dispatcher_(call_disp) {}
-
-  ~ApartmentState() {
-    ASSERT1(ref_cnt_ == 0);
-  }
-
-  // Initialize the state of the singleton.
-  static HRESULT Initialize(DWORD reserved);
-
-  // Uninitialize the state of the singleton.
-  static HRESULT Uninitialize();
-
-  // Accessors.
-  static ApartmentState* apartment_state() {
-    return apartment_state_;
-  }
-
-  CallDispatcher& call_dispatcher() const {
-    ASSERT(call_dispatcher_.get(),
-      (_T("'InitializeApartment' has not been called.")));
-    return *call_dispatcher_;
-  }
-
-  DWORD thread_id() const {
-    ASSERT(thread_id_,
-      (_T("'InitializeApartment' has not been called.")));
-    return thread_id_;
-  }
-
- private:
-  static int ref_cnt_;    // the reference count of init/uninit.
-
-  const DWORD thread_id_;   // thread that called the InitializeApartment
-  scoped_ptr<CallDispatcher> call_dispatcher_;
-
-  static ApartmentState* apartment_state_;    // the instance of the state
-  DISALLOW_EVIL_CONSTRUCTORS(ApartmentState);
-};
-
-
-// CallDispatcher uses functors to cross call from the caller's thread to
-// this apartment thread (main thread).
-class CallDispatcher
-    : public CWindowImpl<CallDispatcher,
-                         CWindow,
-                         CWinTraits<WS_OVERLAPPED, WS_EX_TOOLWINDOW> > {
- public:
-  explicit CallDispatcher(DWORD options);
-  ~CallDispatcher();
-
-  // Two-phase initialization
-  HRESULT Init();
-
-  // The initiator of the cross call.
-  HRESULT DoCrossApartmentCall(BaseFunctor* caller, void* presult);
-
- private:
-  static const UINT WM_METHOD_CALL = WM_USER + 0x100;
-  static const UINT WM_METHOD_CALL_COMPLETE = WM_USER + 0x101;
-
-  BEGIN_MSG_MAP(CallDispatcher)
-    MESSAGE_HANDLER(WM_METHOD_CALL, OnMethodCall)
-  END_MSG_MAP()
-
- private:
-  LRESULT OnMethodCall(UINT uMsg, WPARAM wParam,
-    LPARAM lParam, BOOL& bHandled);
-
-  DWORD options_;
-
-  // Currently only one option is supported for testing purposes.
-  // It testing mode, this option disables the cross-call mechanism and
-  // directly calls the functor in the same thread as the invoker.
-  static const DWORD kTestingMode = DWORD(-1);
-};
-
-// initialize the static data memebers
-ApartmentState* ApartmentState::apartment_state_ = 0;
-int ApartmentState::ref_cnt_ = 0;
-
-HRESULT ApartmentState::Initialize(DWORD reserved) {
-  CORE_LOG(L3, (_T("[ApartmentState::Initialize]")));
-  ASSERT(ref_cnt_ >= 0, (_T("Apartment Reference Counting")));
-  if (ref_cnt_ < 0) return E_UNEXPECTED;
-
-  DWORD thread_id = ::GetCurrentThreadId();
-  ASSERT1(thread_id);
-
-  if (ref_cnt_ > 0) {
-    ASSERT(apartment_state(), (_T("Apartment State is 0.")));
-    bool same_thread = thread_id == apartment_state()->thread_id();
-    // if initialized multiple times verify the thread identity just in case
-    ASSERT(same_thread, (_T("Wrong Thread.")));
-    if (!same_thread) return E_UNEXPECTED;
-    ++ref_cnt_;
-    return S_OK;
-  }
-
-  ASSERT1(ref_cnt_ == 0);
-
-  // do the initialization of the apartment
-  scoped_ptr<CallDispatcher> call_disp(new CallDispatcher(reserved));
-  RET_IF_FAILED(call_disp->Init());
-  scoped_ptr<ApartmentState> ap_state(
-    new ApartmentState(thread_id, call_disp.get()));
-
-  call_disp.release();
-  ApartmentState::apartment_state_ = ap_state.release();
-
-  ++ref_cnt_;
-  return S_OK;
-}
-
-HRESULT ApartmentState::Uninitialize() {
-  ASSERT(ref_cnt_ > 0, (_T("Apartment Reference Counting")));
-  if (ref_cnt_ <= 0) return E_UNEXPECTED;
-
-  DWORD thread_id = ::GetCurrentThreadId();
-  ASSERT1(thread_id);
-
-  ASSERT(apartment_state(), (_T("Apartment State is 0.")));
-  bool same_thread = thread_id == apartment_state()->thread_id();
-  // verify the thread identity just in case
-  ASSERT(same_thread, (_T("Wrong Thread.")));
-  if (!same_thread) return E_UNEXPECTED;
-
-  if (--ref_cnt_ == 0) {
-    delete ApartmentState::apartment_state();
-    ApartmentState::apartment_state_ = 0;
-  }
-
-  return S_OK;
-}
-
-CallDispatcher::CallDispatcher(DWORD options) : options_(options) {
-  // TODO(omaha): Log
-}
-
-CallDispatcher::~CallDispatcher() {
-  // TODO(omaha): Log
-  if (m_hWnd) {
-    DestroyWindow();
-  }
-}
-
-HRESULT CallDispatcher::Init() {
-  // Create a message-only window for the dispatcher. It is not visible,
-  // has no z-order, cannot be enumerated, and does not receive broadcast
-  // messages. The window simply dispatches messages.
-  const TCHAR kWndName[] = _T("{FFE21900-612E-44a9-8424-3FC71B382E61}");
-  HWND hwnd = Create(HWND_MESSAGE, NULL, kWndName);
-  return hwnd ? S_OK : HRESULT_FROM_WIN32(::GetLastError());
-}
-
-//
-LRESULT CallDispatcher::OnMethodCall(UINT, WPARAM wParam,
-                                     LPARAM result, BOOL&) {
-  CORE_LOG(L6, (_T("[CallDispatcher::OnMethodCall]")));
-
-  ASSERT1(wParam);
-  BaseFunctor& call = *reinterpret_cast<BaseFunctor*>(wParam);
-
-  // presult is non-zero if the method or function has a return type.
-  // presult is zero for void methods and functions.
-
-  void* presult = reinterpret_cast<void*>(result);
-
-  ASSERT(
-    ApartmentState::apartment_state()->thread_id() == ::GetCurrentThreadId(),
-    (_T("Wrong Thread")));
-
-  // the function object virtual call;
-  call(presult);
-
-  bool is_async = call.is_async();
-  // For async calls, do not post a message, because the caller will not be
-  // waiting for the call to complete.
-  if (!is_async &&
-      !::PostThreadMessage(call.thread_id(), WM_METHOD_CALL_COMPLETE, 0, 0)) {
-    DWORD error = ::GetLastError();
-    CORE_LOG(LEVEL_ERROR,
-        (_T("[CallDispatcher::OnMethodCall - PostThreadMessage][%d]"), error));
-    ASSERT(false, (_T("Failed to PostThreadMessage.")));
-
-    // TODO(omaha): raise here.
-  }
-
-  // DO NOT ACCESS THE CALL OBJECT FROM DOWN ON. IN THE CASE OF A SYNCHRONOUS
-  // CALL THE CALL OBJECT MAY HAVE ALREADY DESTROYED.
-
-  if (is_async) {
-    // Auto cleanup of the call object in the case of a async call.
-    delete &call;
-  }
-
-  CORE_LOG(L6, (_T("CallDispatcher::OnMethodCall returns.")));
-  return true;
-}
-
-//
-HRESULT CallDispatcher::DoCrossApartmentCall(BaseFunctor* call,
-                                             void* presult) {
-  CORE_LOG(L6, (_T("[CallDispatcher::DoCrossApartmentCall]")));
-
-  ASSERT(IsWindow(), (_T("The dispatcher must have a window.")));
-  bool is_async = call->is_async();
-  if (options_ == kTestingMode) {
-    (*call)(presult);
-    if (is_async) {
-      // We need to delete the functor as if we were the callee.
-      delete call;
-    }
-    return S_OK;
-  }
-
-  if (!is_async) {
-    // Usually it is a mistake to call a synchronous method from the main STA
-    // to the main STA.
-
-    DWORD thread_id = ApartmentState::apartment_state()->thread_id();
-    ASSERT(thread_id != ::GetCurrentThreadId(), (_T("Wrong Thread")));
-
-    ASSERT(GetWindowThreadID() != ::GetCurrentThreadId(),
-           (_T("DoCrossApartmentCall calling its own thread.")));
-  }
-
-  if (!PostMessage(WM_METHOD_CALL,
-                   reinterpret_cast<WPARAM>(call),
-                   reinterpret_cast<LPARAM>(presult))) {
-    DWORD err = ::GetLastError();
-    CORE_LOG(LEVEL_ERROR,
-        (_T("[CallDispatcher::DoCrossApartmentCall - PostMessage][%d]"), err));
-    ASSERT(false, (_T("Failed to PostMessage.")));
-
-    return HRESULT_FROM_WIN32(err);
-  }
-
-  // Once the call has been made, do not access the state of the functor as
-  // the other end might have already executed the call and delete the functor.
-  // This is true for asyncronous calls but it would not hurt for synchronous
-  // calls as well.
-  call = NULL;
-
-  if (is_async) {
-    // Do not wait for the call to complete. The call will complete at
-    // some time in the future and the call object is going to be cleaned up.
-    return S_OK;
-  }
-
-  // Pump all messages, waiting for WM_METHOD_CALL_COMPLETE or WM_QUIT.
-  MSG msg;
-  SetZero(msg);
-  int ret = 0;
-  while ((ret = ::GetMessage(&msg, 0, 0, 0)) != 0) {
-    if (ret == -1) {
-      DWORD error = ::GetLastError();
-      CORE_LOG(LEVEL_ERROR,
-               (_T("[CallDispatcher::DoCrossApartmentCall - GetMessage][%d]"),
-               error));
-      // TODO(omaha): raise here.
-    }
-
-    if (msg.message == WM_METHOD_CALL_COMPLETE) {
-      break;
-    }
-
-    ::DispatchMessage(&msg);
-  }
-
-  // Repost the WM_QUIT message to properly exit all message loops.
-  if (msg.message == WM_QUIT) {
-    ASSERT1(ret == 0);
-    ::PostQuitMessage(msg.wParam);
-  }
-
-  CORE_LOG(L6, (_T("CallDispatcher::DoCrossApartmentCall returns")));
-  return S_OK;
-}
-
-}  // namespace
-
-void BaseFunctor::DoInvoke(void* presult) {
-  ASSERT(ApartmentState::apartment_state(),
-    (_T("Did you forgot to call 'InitializeApartment'?")));
-
-  CallDispatcher& call_dispatcher =
-    ApartmentState::apartment_state()->call_dispatcher();
-
-  HRESULT hr = call_dispatcher.DoCrossApartmentCall(this, presult);
-
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("Failed to call across apartments.")));
-    // TODO(omaha): log, report, raise.
-  }
-}
-
-HRESULT InitializeApartment(DWORD reserved) {
-  return ApartmentState::Initialize(reserved);
-}
-
-HRESULT UninitializeApartment() {
-  return ApartmentState::Uninitialize();
-}
-
-}  // namespace omaha
-
diff --git a/common/sta.h b/common/sta.h
deleted file mode 100644
index 76204d0..0000000
--- a/common/sta.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// STA initializes a custom non-COM Single Threaded Apartment to facilitate
-// calling of functions and object methods in the thread of the STA. This is
-// useful when creating a simple threading model based on a main thread and
-// several worker threads, especially for client components that have a UI or
-// use the COM STA models.
-//
-// The current implementation only supports initializing the main STA, which is
-// the STA created by the main thread of the process. This is usually the UI
-// thread. Having multiple STA in a process is not possible yet.
-//
-// This custom STA does not interfere with the COM STAs.
-//
-// In order for the STA to work properly, the STA thread must keep processing
-// messages and not block, just like in the COM STA case.
-
-#ifndef OMAHA_COMMON_STA_H__
-#define OMAHA_COMMON_STA_H__
-
-#include <windows.h>
-#include "omaha/common/debug.h"
-#include "omaha/common/scoped_any.h"
-
-namespace omaha {
-
-// Initializes the STA apartment. The 'reserved' parameter must be 0.
-// InitializeApartment and UninitializeApartment are reference-counted.
-HRESULT InitializeApartment(DWORD reserved);
-
-// Uninitializes the STA apartment.
-HRESULT UninitializeApartment();
-
-// A scoped_sta smart pointer is provided to manage the calls to
-// InitializeApartment and UninitializeApartment.
-inline HRESULT smart_sta_init_helper(DWORD reserved) {
-  return InitializeApartment(reserved);
-}
-
-inline void smart_uninit_helper(HRESULT result) {
-  if (result == S_OK) {
-    VERIFY1(SUCCEEDED(UninitializeApartment()));
-  }
-}
-
-typedef close_fun<void (*)(HRESULT), smart_uninit_helper> close_sta;
-
-typedef value_const<HRESULT, E_UNEXPECTED> sta_not_init;
-
-typedef scoped_any<HRESULT, close_sta, sta_not_init> scoped_sta_close;
-
-struct scoped_sta {
-  explicit scoped_sta(DWORD reserved)
-      : result_(smart_sta_init_helper(reserved)) {}
-
-  HRESULT result() const { return get(result_); }
-
- private:
-  const scoped_sta_close result_;
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_STA_H__
-
diff --git a/common/sta_call.h b/common/sta_call.h
deleted file mode 100644
index 682b66f..0000000
--- a/common/sta_call.h
+++ /dev/null
@@ -1,1117 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// sta_call.h generics for cross apartment calling.
-//
-// The code is using compile-time and run-time polymorphism to create
-// type-safe call wrappers that can be used to cross call from a
-// worker thread to an STA thread. The current implementation only
-// supports calling the main STA.
-//
-// Functions as well as object methods can be called.
-//
-// Examples:
-// class X {
-//   public:
-//     int Add(int i, int j) { return i + j; }
-// };
-//
-//
-// namespace {
-//   int Add(long i, long j) { return i + j; }
-// }
-//
-// X x;
-// int sum = CallMethod(&x, X::Add, 10, 20);
-// int j = CallFunction(Add, -10, 10);
-//
-// The central piece of machinery is a hierarchy of functors. A functor is
-// instantiated by a function template (CallFunction or CallMethod) and its
-// 'Invoke' method gets called.
-// Calling 'Invoke' will send the functor using 'SendMessage'
-// to a window. The window message handler picks up the functor and
-// calls the functor virtual operator().
-// This virtual call is what actually calls the specified function or the
-// method, the only difference being that the call is now made in a thread
-// different than the thread that called 'Invoke'. There is a partial
-// specialization of the templates for void, so that void type is supported
-// as a return type.
-//
-//
-// !!! Limitations !!!
-//
-// There are a few important design and implementation limitations. They are
-// mostly related to template parameters ambiguities (T or T&) especially
-// for overloaded names or const types. The limitations are significant although
-// the code is useful enough as it is in most of the cases.
-// However, when using the code it is frustrating to discover that it does not
-// compile for obvious and useful cases, a constant reminder that a better
-// solution is to be seeked.
-//
-//
-// The implementation does not support calling all 'stdcall' calling convention.
-//
-// The design does not support calling functions or methods that use pass by
-// reference arguments: f(std::string&) .
-//
-// The design does not support well calling functions or methods that take
-// pointer to const types parameters : f(const std::string*) .
-//
-// The implementation does not support calling methods of const objects.
-//
-// To reduce the number of templates that get instantiated, the types of the
-// arguments of the call must match exactly the types of parameters of the
-// function or method . In some cases static_casts mey be required
-// at the point of the call. Example: CallMethod(f, static_cast<long>(10));
-
-#ifndef OMAHA_COMMON_STA_CALL_H__
-#define OMAHA_COMMON_STA_CALL_H__
-
-#include "base/scoped_ptr.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-
-namespace omaha {
-
-// C4347: 'function template' is called instead of 'function'
-#pragma warning(disable : 4347)
-
-// The Base Functor is the base of the functor hierarchy.
-class BaseFunctor {
- public:
-  explicit BaseFunctor(bool is_async) :
-      thread_id_(::GetCurrentThreadId()),
-      is_async_(is_async) {
-    CORE_LOG(L6, (_T("[BaseFunctor::BaseFunctor]")));
-  }
-
-  // Functors are polymorphic objects.
-  virtual ~BaseFunctor() {
-    CORE_LOG(L6, (_T("[BaseFunctor::~BaseFunctor]")));
-  }
-
-  // Abstract virtual function call operator. This is always called
-  // in the callee thread by the dispatcher of the apartment.
-  virtual void operator()(void* presult) = 0;
-
-  // The thread id of the calling thread.
-  DWORD thread_id() const { return thread_id_; }
-
-  bool is_async() const { return is_async_; }
-
- protected:
-
-  // Invoke is called by each of the derived functors. This is how
-  // the  cross thread invocation is made and the result of the invocation
-  // is retrieved. Invoke is always called in the caller thread.
-  template <typename R>
-  R Invoke() {
-    R r = R();      // ensure r is initialized even for primitive types.
-    if (!is_async_) {
-      DoInvoke(&r);
-    } else {
-      // We handle the async calls as if the call returns void.
-      DoInvoke(0);
-    }
-    return r;
-  }
-
-  // non-template method to be called by the derived functors
-  // specialized for void.
-  void Invoke() {
-    // When the argument of the invocation is 0, we are not
-    // interested in the result.
-    DoInvoke(0);
-  }
-
- private:
-  void DoInvoke(void* presult);   // Does the actual invocation.
-  DWORD thread_id_;               // The thread id of the calling thread.
-  bool is_async_;                 // True for async calls.
-
-  DISALLOW_EVIL_CONSTRUCTORS(BaseFunctor);
-};
-
-//
-// 0-ary method functor.
-//
-template <class T, typename R>
-class MethodFunctor0 : public BaseFunctor {
- public:
-  MethodFunctor0(bool is_async, T* pt, R (T::*pm)()) :
-      BaseFunctor(is_async), pobj_(pt), pm_(pm) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT(pobj_, (_T("Null object.")));
-    if (presult) {
-      *static_cast<R*>(presult) = (pobj_->*pm_)();
-    } else {
-      (pobj_->*pm_)();
-    }
-  }
-
-  R Invoke() {
-    // Don't forget to call the base implementation.
-    return BaseFunctor::Invoke<R>();
-  }
-
- private:
-  T* pobj_;
-  R (T::*pm_)();
-};
-
-//
-// 0-ary partial specialization for void return types.
-//
-template <class T>
-class MethodFunctor0<T, void> : public BaseFunctor {
- public:
-  MethodFunctor0(bool is_async, T* pt, void (T::*pm)()) :
-      BaseFunctor(is_async), pobj_(pt), pm_(pm) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT(pobj_, (_T("Null object.")));
-    ASSERT1(!presult);
-    presult;  //  unreferenced formal parameter
-
-    // the actual call. There is no return value when the return type is void.
-    (pobj_->*pm_)();
-  }
-
-  // Bring in the name from the Base
-  using BaseFunctor::Invoke;
-
- private:
-  T* pobj_;
-  void (T::*pm_)();
-};
-
-//
-// 0-ary functor and specialization for void.
-//
-template <typename R>
-class Functor0 : public BaseFunctor {
- public:
-  Functor0(bool is_async, R (*pf)()) :
-      BaseFunctor(is_async), pf_(pf) {}
-
-  virtual void operator()(void* presult) {
-    if (presult) {
-      *static_cast<R*>(presult) = (*pf_)();
-    } else {
-      (*pf_)();
-    }
-  }
-
-  R Invoke() {
-    return BaseFunctor::Invoke<R>();
-  }
-
- private:
-  R (*pf_)();
-};
-
-template <>
-class Functor0<void> : public BaseFunctor {
- public:
-  Functor0(bool is_async, void (*pf)()) :
-      BaseFunctor(is_async), pf_(pf) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT1(!presult);
-    presult;  // unreferenced formal parameter
-
-    (*pf_)();
-  }
-
-  using BaseFunctor::Invoke;
-
- private:
-  void (*pf_)();
-};
-
-
-//
-// 1-ary
-//
-template <class T, typename R, typename P>
-class MethodFunctor1 : public BaseFunctor {
- public:
-  MethodFunctor1(bool is_async, T* pt, R (T::*pm)(P), P p) :
-      BaseFunctor(is_async), pobj_(pt), pm_(pm), p_(p) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT(pobj_, (_T("Null object.")));
-    if (presult) {
-      *static_cast<R*>(presult) = (pobj_->*pm_)(p_);
-    } else {
-      (pobj_->*pm_)(p_);
-    }
-  }
-
-  R Invoke() {
-    return BaseFunctor::Invoke<R>();
-  }
-
- private:
-  T* pobj_;
-  R (T::*pm_)(P);
-  P p_;
-};
-
-template <class T, typename P>
-class MethodFunctor1<T, void, P> : public BaseFunctor {
- public:
-  MethodFunctor1(bool is_async, T* pt, void (T::*pm)(P), P p) :
-      BaseFunctor(is_async), pobj_(pt), pm_(pm), p_(p) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT(pobj_, (_T("Null object.")));
-    ASSERT1(!presult);
-    presult;  //  unreferenced formal parameter
-
-    (pobj_->*pm_)(p_);
-  }
-
-  using BaseFunctor::Invoke;
-
- private:
-  T* pobj_;
-  void (T::*pm_)(P);
-  P p_;
-};
-
-template <typename R, typename P1>
-class Functor1 : public BaseFunctor {
- public:
-  Functor1(bool is_async, R (*pf)(P1), P1 p1) :
-      BaseFunctor(is_async), pf_(pf), p1_(p1) {}
-
-  virtual void operator()(void* presult) {
-    if (presult) {
-      *static_cast<R*>(presult) = (*pf_)(p1_);
-    } else {
-      (*pf_)(p1_);
-    }
-  }
-
-  R Invoke() {
-    return BaseFunctor::Invoke<R>();
-  }
-
- private:
-  R (*pf_)(P1);
-  P1 p1_;
-};
-
-template <typename P1>
-class Functor1<void, P1> : public BaseFunctor {
- public:
-  Functor1(bool is_async, void (*pf)(P1), P1 p1) :
-      BaseFunctor(is_async), pf_(pf), p1_(p1) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT1(!presult);
-    presult;  //  unreferenced formal parameter
-
-    (*pf_)(p1_);
-  }
-
-  using BaseFunctor::Invoke;
-
- private:
-  void (*pf_)(P1);
-  P1 p1_;
-};
-
-
-//
-// 2-ary
-//
-template <class T, typename R, typename P1, typename P2>
-class MethodFunctor2 : public BaseFunctor {
- public:
-  MethodFunctor2(bool is_async, T* pt, R (T::*pm)(P1, P2), P1 p1, P2 p2) :
-      BaseFunctor(is_async), pobj_(pt), pm_(pm), p1_(p1), p2_(p2) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT(pobj_, (_T("Null object.")));
-    if (presult) {
-      *static_cast<R*>(presult) = (pobj_->*pm_)(p1_, p2_);
-    } else {
-      (pobj_->*pm_)(p1_, p2_);
-    }
-  }
-
-  R Invoke() {
-    return BaseFunctor::Invoke<R>();
-  }
-
- private:
-  T* pobj_;
-  R (T::*pm_)(P1, P2);
-  P1 p1_;
-  P2 p2_;
-};
-
-template <class T, typename P1, typename P2>
-class MethodFunctor2<T, void, P1, P2> : public BaseFunctor {
- public:
-  MethodFunctor2(bool is_async, T* pt, void (T::*pm)(P1, P2), P1 p1, P2 p2) :
-      BaseFunctor(is_async), pobj_(pt), pm_(pm), p1_(p1), p2_(p2) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT(pobj_, (_T("Null object.")));
-    ASSERT1(!presult);
-    presult;  //  unreferenced formal parameter
-
-    (pobj_->*pm_)(p1_, p2_);
-  }
-
-  using BaseFunctor::Invoke;
-
- private:
-  T* pobj_;
-  void (T::*pm_)(P1, P2);
-  P1 p1_;
-  P2 p2_;
-};
-
-template <typename R, typename P1, typename P2>
-class Functor2 : public BaseFunctor {
- public:
-  Functor2(bool is_async, R (*pf)(P1, P2), P1 p1, P2 p2) :
-      BaseFunctor(is_async), pf_(pf), p1_(p1), p2_(p2) {}
-
-  virtual void operator()(void* presult) {
-    if (presult) {
-      *static_cast<R*>(presult) = pf_(p1_, p2_);
-    } else {
-      pf_(p1_, p2_);
-    }
-  }
-
-  R Invoke() {
-    return BaseFunctor::Invoke<R>();
-  }
-
- private:
-  R (*pf_)(P1, P2);
-  P1 p1_;
-  P2 p2_;
-};
-
-template <typename P1, typename P2>
-class Functor2<void, P1, P2> : public BaseFunctor {
- public:
-  Functor2(bool is_async, void (*pf)(P1, P2), P1 p1, P2 p2) :
-      BaseFunctor(is_async), pf_(pf), p1_(p1), p2_(p2) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT1(!presult);
-    presult;  //  unreferenced formal parameter
-
-    (*pf_)(p1_, p2_);
-  }
-
-  using BaseFunctor::Invoke;
-
- private:
-  void (*pf_)(P1, P2);
-  P1 p1_;
-  P2 p2_;
-};
-
-//
-// 3-ary
-//
-template <class T, typename R, typename P1, typename P2, typename P3>
-class MethodFunctor3 : public BaseFunctor {
- public:
-  MethodFunctor3(bool is_async,
-                 T* pt,
-                 R (T::*pm)(P1, P2, P3),
-                 P1 p1,
-                 P2 p2,
-                 P3 p3) :
-      BaseFunctor(is_async), pobj_(pt), pm_(pm), p1_(p1), p2_(p2), p3_(p3) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT(pobj_, (_T("Null object.")));
-    if (presult) {
-      *static_cast<R*>(presult) = (pobj_->*pm_)(p1_, p2_, p3_);
-    } else {
-      (pobj_->*pm_)(p1_, p2_, p3_);
-    }
-  }
-
-  R Invoke() {
-    return BaseFunctor::Invoke<R>();
-  }
-
- private:
-  T* pobj_;
-  R (T::*pm_)(P1, P2, P3);
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-};
-
-template <class T, typename P1, typename P2, typename P3>
-class MethodFunctor3<T, void, P1, P2, P3> : public BaseFunctor {
- public:
-  MethodFunctor3(bool is_async,
-                 T* pt,
-                 void (T::*pm)(P1, P2, P3),
-                 P1 p1,
-                 P2 p2,
-                 P3 p3) :
-      BaseFunctor(is_async), pobj_(pt), pm_(pm), p1_(p1), p2_(p2), p3_(p3) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT(pobj_, (_T("Null object.")));
-    ASSERT1(!presult);
-    presult;  //  unreferenced formal parameter
-
-    (pobj_->*pm_)(p1_, p2_, p3_);
-  }
-
-  using BaseFunctor::Invoke;
-
- private:
-  T* pobj_;
-  void (T::*pm_)(P1, P2, P3);
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-};
-
-
-template <typename R, typename P1, typename P2, typename P3>
-class Functor3 : public BaseFunctor {
- public:
-  Functor3(bool is_async, R (*pf)(P1, P2, P3), P1 p1, P2 p2, P3 p3) :
-      BaseFunctor(is_async), pf_(pf), p1_(p1), p2_(p2), p3_(p3) {}
-  virtual void operator()(void* presult) {
-    if (presult) {
-      *static_cast<R*>(presult) = (*pf_)(p1_, p2_, p3_);
-    } else {
-      (*pf_)(p1_, p2_, p3_);
-    }
-  }
-
-  R Invoke() {
-    return BaseFunctor::Invoke<R>();
-  }
-
- private:
-  R (*pf_)(P1, P2, P3);
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-};
-
-template <typename P1, typename P2, typename P3>
-class Functor3<void, P1, P2, P3> : public BaseFunctor {
- public:
-  Functor3(bool is_async, void (*pf)(P1, P2, P3), P1 p1, P2 p2, P3 p3) :
-      BaseFunctor(is_async), pf_(pf), p1_(p1), p2_(p2), p3_(p3) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT1(!presult);
-    presult;  //  unreferenced formal parameter
-
-    (*pf_)(p1_, p2_, p3_);
-  }
-
-  using BaseFunctor::Invoke;
-
- private:
-  void (*pf_)(P1, P2, P3);
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-};
-
-//
-// 4-ary
-//
-template <class T,
-          typename R,
-          typename P1,
-          typename P2,
-          typename P3,
-          typename P4>
-class MethodFunctor4 : public BaseFunctor {
- public:
-  MethodFunctor4(bool is_async,
-                 T* pt,
-                 R (T::*pm)(P1, P2, P3, P4),
-                 P1 p1,
-                 P2 p2,
-                 P3 p3,
-                 P4 p4) :
-      BaseFunctor(is_async),
-      pobj_(pt),
-      pm_(pm),
-      p1_(p1),
-      p2_(p2),
-      p3_(p3),
-      p4_(p4) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT(pobj_, (_T("Null object.")));
-    if (presult) {
-      *static_cast<R*>(presult) = (pobj_->*pm_)(p1_, p2_, p3_, p4_);
-    } else {
-      (pobj_->*pm_)(p1_, p2_, p3_, p4_);
-    }
-  }
-
-  R Invoke() {
-    return BaseFunctor::Invoke<R>();
-  }
-
- private:
-  T* pobj_;
-  R (T::*pm_)(P1, P2, P3, P4);
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-  P4 p4_;
-};
-
-template <class T, typename P1, typename P2, typename P3, typename P4>
-class MethodFunctor4<T, void, P1, P2, P3, P4> : public BaseFunctor {
- public:
-  MethodFunctor4(bool is_async,
-                 T* pt,
-                 void (T::*pm)(P1, P2, P3, P4),
-                 P1 p1,
-                 P2 p2,
-                 P3 p3,
-                 P4 p4) :
-      BaseFunctor(is_async),
-      pobj_(pt),
-      pm_(pm),
-      p1_(p1),
-      p2_(p2),
-      p3_(p3),
-      p4_(p4) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT(pobj_, (_T("Null object.")));
-    ASSERT1(!presult);
-    presult;  //  unreferenced formal parameter
-
-    (pobj_->*pm_)(p1_, p2_, p3_, p4_);
-  }
-
-  using BaseFunctor::Invoke;
-
- private:
-  T* pobj_;
-  void (T::*pm_)(P1, P2, P3, P4);
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-  P4 p4_;
-};
-
-
-template <typename R, typename P1, typename P2, typename P3, typename P4>
-class Functor4 : public BaseFunctor {
- public:
-  Functor4(bool is_async, R (*pf)(P1, P2, P3, P4), P1 p1, P2 p2, P3 p3, P4 p4) :
-      BaseFunctor(is_async), pf_(pf), p1_(p1), p2_(p2), p3_(p3), p4_(p4) {}
-
-  virtual void operator()(void* presult) {
-    if (presult) {
-      *static_cast<R*>(presult) = (*pf_)(p1_, p2_, p3_, p4_);
-    } else {
-      (*pf_)(p1_, p2_, p3_, p4_);
-    }
-  }
-
-  R Invoke() {
-    return BaseFunctor::Invoke<R>();
-  }
-
- private:
-  R (*pf_)(P1, P2, P3, P4);
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-  P4 p4_;
-};
-
-template <typename P1, typename P2, typename P3, typename P4>
-class Functor4<void, P1, P2, P3, P4> : public BaseFunctor {
- public:
-  Functor4(bool is_async,
-           void (*pf)(P1, P2, P3, P4),
-           P1 p1,
-           P2 p2,
-           P3 p3,
-           P4 p4) :
-      BaseFunctor(is_async), pf_(pf), p1_(p1), p2_(p2), p3_(p3), p4_(p4) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT1(!presult);
-    presult;  //  unreferenced formal parameter
-
-    (*pf_)(p1_, p2_, p3_, p4_);
-  }
-
-  using BaseFunctor::Invoke;
-
- private:
-  void (*pf_)(P1, P2, P3, P4);
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-  P4 p4_;
-};
-
-//
-// 5-ary
-//
-template <class T,
-          typename R,
-          typename P1,
-          typename P2,
-          typename P3,
-          typename P4,
-          typename P5>
-class MethodFunctor5 : public BaseFunctor {
- public:
-  MethodFunctor5(bool is_async,
-                 T* pt,
-                 R (T::*pm)(P1, P2, P3, P4, P5),
-                 P1 p1,
-                 P2 p2,
-                 P3 p3,
-                 P4 p4,
-                 P5 p5) :
-      BaseFunctor(is_async),
-      pobj_(pt),
-      pm_(pm),
-      p1_(p1),
-      p2_(p2),
-      p3_(p3),
-      p4_(p4),
-      p5_(p5) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT(pobj_, (_T("Null object.")));
-    if (presult) {
-      *static_cast<R*>(presult) = (pobj_->*pm_)(p1_, p2_, p3_, p4_, p5_);
-    } else {
-      (pobj_->*pm_)(p1_, p2_, p3_, p4_, p5_);
-    }
-  }
-
-  R Invoke() {
-    return BaseFunctor::Invoke<R>();
-  }
-
- private:
-  T* pobj_;
-  R (T::*pm_)(P1, P2, P3, P4, P5);
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-  P4 p4_;
-  P5 p5_;
-};
-
-template <class T,
-          typename P1,
-          typename P2,
-          typename P3,
-          typename P4,
-          typename P5>
-class MethodFunctor5<T, void, P1, P2, P3, P4, P5> : public BaseFunctor {
- public:
-  MethodFunctor5(bool is_async,
-                 T* pt,
-                 void (T::*pm)(P1, P2, P3, P4, P5),
-                 P1 p1,
-                 P2 p2,
-                 P3 p3,
-                 P4 p4,
-                 P5 p5) :
-      BaseFunctor(is_async),
-      pobj_(pt),
-      pm_(pm),
-      p1_(p1),
-      p2_(p2),
-      p3_(p3),
-      p4_(p4),
-      p5_(p5) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT(pobj_, (_T("Null object.")));
-    ASSERT1(!presult);
-    presult;  //  unreferenced formal parameter
-
-    (pobj_->*pm_)(p1_, p2_, p3_, p4_, p5_);
-  }
-
-  using BaseFunctor::Invoke;
-
- private:
-  T* pobj_;
-  void (T::*pm_)(P1, P2, P3, P4, P5);
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-  P4 p4_;
-  P5 p5_;
-};
-
-template <typename R,
-          typename P1,
-          typename P2,
-          typename P3,
-          typename P4,
-          typename P5>
-class Functor5 : public BaseFunctor {
- public:
-  Functor5(bool is_async,
-           R (*pf)(P1, P2, P3, P4, P5),
-           P1 p1,
-           P2 p2,
-           P3 p3,
-           P4 p4,
-           P5 p5) :
-      BaseFunctor(is_async),
-      pf_(pf),
-      p1_(p1),
-      p2_(p2),
-      p3_(p3),
-      p4_(p4),
-      p5_(p5) {}
-  virtual void operator()(void* presult) {
-    if (presult) {
-      *static_cast<R*>(presult) = (*pf_)(p1_, p2_, p3_, p4_, p5_);
-    } else {
-      (*pf_)(p1_, p2_, p3_, p4_, p5_);
-    }
-  }
-
-  R Invoke() {
-    return BaseFunctor::Invoke<R>();
-  }
-
- private:
-  R (*pf_)(P1, P2, P3, P4, P5);
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-  P4 p4_;
-  P5 p5_;
-};
-
-template <typename P1, typename P2, typename P3, typename P4, typename P5>
-class Functor5<void, P1, P2, P3, P4, P5> : public BaseFunctor {
- public:
-  Functor5(bool is_async,
-           void (*pf)(P1, P2, P3, P4, P5),
-           P1 p1,
-           P2 p2,
-           P3 p3,
-           P4 p4,
-           P5 p5) :
-      BaseFunctor(is_async),
-      pf_(pf),
-      p1_(p1),
-      p2_(p2),
-      p3_(p3),
-      p4_(p4),
-      p5_(p5) {}
-
-  virtual void operator()(void* presult) {
-    ASSERT1(!presult);
-    presult;  //  unreferenced formal parameter
-
-    (*pf_)(p1_, p2_, p3_, p4_, p5_);
-  }
-
-  using BaseFunctor::Invoke;
-
- private:
-  void (*pf_)(P1, P2, P3, P4, P5);
-  P1 p1_;
-  P2 p2_;
-  P3 p3_;
-  P4 p4_;
-  P5 p5_;
-};
-
-
-// This is what the clients of the STA code instantiate and call.
-//
-// Synchronous Callers.
-//
-template <class T, typename R>
-R CallMethod(T* object, R (T::*pm)()) {
-  return MethodFunctor0<T, R>(false, object, pm).Invoke();
-}
-
-template <typename R>
-R CallFunction(R (*pf)()) {
-  return Functor0<R>(false, pf).Invoke();
-}
-
-template <class T, typename R, typename P>
-R CallMethod(T* object, R (T::*pm)(P), P p) {
-  return MethodFunctor1<T, R, P>(false, object, pm, p).Invoke();
-}
-
-template <typename R, typename P>
-R CallFunction(R (*pf)(P), P p) {
-  return Functor1<R, P>(false, pf, p).Invoke();
-}
-
-template <class T, typename R, typename P1, typename P2>
-R CallMethod(T* object, R (T::*pm)(P1, P2), P1 p1, P2 p2) {
-  return MethodFunctor2<T, R, P1, P2>(false, object, pm, p1, p2).Invoke();
-}
-
-template <typename R, typename P1, typename P2>
-R CallFunction(R (*pf)(P1, P2), P1 p1, P2 p2) {
-  return Functor2<R, P1, P2>(false, pf, p1, p2).Invoke();
-}
-
-template <class T, typename R, typename P1, typename P2, typename P3>
-R CallMethod(T* object, R (T::*pm)(P1, P2, P3), P1 p1, P2 p2, P3 p3) {
-  return MethodFunctor3<T, R, P1, P2, P3>(false,
-                                          object, pm, p1, p2, p3).Invoke();
-}
-
-template <typename R, typename P1, typename P2, typename P3>
-R CallFunction(R (*pf)(P1, P2, P3), P1 p1, P2 p2, P3 p3) {
-  return Functor3<R, P1, P2, P3>(false, pf, p1, p2, p3).Invoke();
-}
-
-template <class T,
-          typename R,
-          typename P1,
-          typename P2,
-          typename P3,
-          typename P4>
-R CallMethod(T* object,
-             R (T::*pm)(P1, P2, P3, P4),
-             P1 p1,
-             P2 p2,
-             P3 p3,
-             P4 p4) {
-  return MethodFunctor4<T, R, P1, P2, P3, P4>(false,
-                                              object,
-                                              pm,
-                                              p1,
-                                              p2,
-                                              p3,
-                                              p4).Invoke();
-}
-
-template <typename R, typename P1, typename P2, typename P3, typename P4>
-R CallFunction(R (*pf)(P1, P2, P3, P4), P1 p1, P2 p2, P3 p3, P4 p4) {
-  return Functor4<R, P1, P2, P3, P4>(false, pf, p1, p2, p3, p4).Invoke();
-}
-
-template <class T,
-          typename R,
-          typename P1,
-          typename P2,
-          typename P3,
-          typename P4,
-          typename P5>
-R CallMethod(T* object,
-             R (T::*pm)(P1, P2, P3, P4, P5),
-             P1 p1,
-             P2 p2,
-             P3 p3,
-             P4 p4,
-             P5 p5) {
-  return MethodFunctor5<T, R, P1, P2, P3, P4, P5>(false,
-                                                  object,
-                                                  pm,
-                                                  p1,
-                                                  p2,
-                                                  p3,
-                                                  p4,
-                                                  p5).Invoke();
-}
-
-template <typename R,
-          typename P1,
-          typename P2,
-          typename P3,
-          typename P4,
-          typename P5>
-R CallFunction(R (*pf)(P1, P2, P3, P4, P5), P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) {
-  return Functor5<R, P1, P2, P3, P4, P5>(false,
-                                         pf,
-                                         p1,
-                                         p2,
-                                         p3,
-                                         p4,
-                                         p5).Invoke();
-}
-
-//
-// Asynchronous Callers.
-//
-template <class T, typename R>
-void CallMethodAsync(T* object, R (T::*pm)()) {
-  scoped_ptr<MethodFunctor0<T, R> > fun(
-      new MethodFunctor0<T, R>(true, object, pm));
-  fun->Invoke();
-  fun.release();
-}
-
-template <typename R>
-void CallFunctionAsync(R (*pf)()) {
-  scoped_ptr<Functor0<R> > fun(new Functor0<R>(true, pf));
-  fun->Invoke();
-  fun.release();
-}
-
-template <class T, typename R, typename P>
-void CallMethodAsync(T* object, R (T::*pm)(P), P p) {
-  scoped_ptr<MethodFunctor1<T, R, P> > fun(
-      new MethodFunctor1<T, R, P>(true, object, pm, p));
-  fun->Invoke();
-  fun.release();
-}
-
-template <typename R, typename P>
-void CallFunctionAsync(R (*pf)(P), P p) {
-  scoped_ptr<Functor1<R, P> > fun(new Functor1<R, P>(true, pf, p));
-  fun->Invoke();
-  fun.release();
-}
-
-template <class T, typename R, typename P1, typename P2>
-void CallMethodAsync(T* object, R (T::*pm)(P1, P2), P1 p1, P2 p2) {
-  scoped_ptr<MethodFunctor2<T, R, P1, P2> > fun(
-      new MethodFunctor2<T, R, P1, P2>(true, object, pm, p1, p2));
-  fun->Invoke();
-  fun.release();
-}
-
-template <typename R, typename P1, typename P2>
-void CallFunctionAsync(R (*pf)(P1, P2), P1 p1, P2 p2) {
-  scoped_ptr<Functor2<R, P1, P2> > fun(
-      new Functor2<R, P1, P2>(true, pf, p1, p2));
-  fun->Invoke();
-  fun.release();
-}
-
-template <class T, typename R, typename P1, typename P2, typename P3>
-void CallMethodAsync(T* object, R (T::*pm)(P1, P2, P3), P1 p1, P2 p2, P3 p3) {
-  scoped_ptr<MethodFunctor3<T, R, P1, P2, P3> > fun(
-      new MethodFunctor3<T, R, P1, P2, P3>(true, object, pm, p1, p2, p3));
-  fun->Invoke();
-  fun.release();
-}
-
-template <typename R, typename P1, typename P2, typename P3>
-void CallFunctionAsync(R (*pf)(P1, P2, P3), P1 p1, P2 p2, P3 p3) {
-  scoped_ptr<Functor3<R, P1, P2, P3> > fun(
-      new Functor3<R, P1, P2, P3>(true, pf, p1, p2, p3));
-  fun->Invoke();
-  fun.release();
-}
-
-template <class T,
-          typename R,
-          typename P1,
-          typename P2,
-          typename P3,
-          typename P4>
-void CallMethodAsync(T* obj,
-                     R (T::*pm)(P1, P2, P3, P4),
-                     P1 p1,
-                     P2 p2,
-                     P3 p3,
-                     P4 p4) {
-  scoped_ptr<MethodFunctor4<T, R, P1, P2, P3, P4> > fun(
-      new MethodFunctor4<T, R, P1, P2, P3, P4>(true, obj, pm, p1, p2, p3, p4));
-  fun->Invoke();
-  fun.release();
-}
-
-template <typename R, typename P1, typename P2, typename P3, typename P4>
-void CallFunctionAsync(R (*pf)(P1, P2, P3, P4), P1 p1, P2 p2, P3 p3, P4 p4) {
-  scoped_ptr<Functor4<R, P1, P2, P3, P4> > fun(
-      new Functor4<R, P1, P2, P3, P4>(true, pf, p1, p2, p3, p4));
-  fun->Invoke();
-  fun.release();
-}
-
-template <class T,
-          typename R,
-          typename P1,
-          typename P2,
-          typename P3,
-          typename P4,
-          typename P5>
-void CallMethodAsync(T* object,
-                     R (T::*pm)(P1, P2, P3, P4, P5),
-                     P1 p1,
-                     P2 p2,
-                     P3 p3,
-                     P4 p4,
-                     P5 p5) {
-  scoped_ptr<MethodFunctor5<T, R, P1, P2, P3, P4, P5> > fun(
-      new MethodFunctor5<T, R, P1, P2, P3, P4, P5>(true,
-                                                   object,
-                                                   pm,
-                                                   p1,
-                                                   p2,
-                                                   p3,
-                                                   p4,
-                                                   p5));
-  fun->Invoke();
-  fun.release();
-}
-
-template <typename R,
-          typename P1,
-          typename P2,
-          typename P3,
-          typename P4,
-          typename P5>
-void CallFunctionAsync(R (*pf)(P1, P2, P3, P4, P5),
-                       P1 p1,
-                       P2 p2,
-                       P3 p3,
-                       P4 p4,
-                       P5 p5) {
-  scoped_ptr<Functor5<R, P1, P2, P3, P4, P5> > fun(
-      new Functor5<R, P1, P2, P3, P4, P5>(true, pf, p1, p2, p3, p4, p5));
-  fun->Invoke();
-  fun.release();
-}
-
-#pragma warning(default : 4347)
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_STA_CALL_H__
-
diff --git a/common/sta_unittest.cc b/common/sta_unittest.cc
deleted file mode 100644
index 1e52b68..0000000
--- a/common/sta_unittest.cc
+++ /dev/null
@@ -1,288 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include <atlstr.h>
-#include "omaha/common/sta.h"
-#include "omaha/common/sta_call.h"
-#include "omaha/common/thread.h"
-#include "omaha/common/utils.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-namespace {
-
-class X {
- public:
-  void f() {}
-  void f(int) {}
-  void f(unsigned int, X*) {}
-  void f(bool, char, long*) {}
-
-  static void g() {}
-  static void g(int) {}
-  static void g(unsigned int, X*) {}
-  static void g(bool, char, long*) {}
-};
-
-class Y {
- public:
-  HRESULT f() { return S_OK; }
-  HRESULT f(int) { return S_OK; }
-  HRESULT f(unsigned int, X*) { return S_OK; }
-  HRESULT f(bool, char, long*) { return S_OK; }
-
-  static HRESULT g() { return S_OK; }
-  static HRESULT g(int) { return S_OK; }
-  static HRESULT g(unsigned int, X*) { return S_OK; }
-  static HRESULT g(bool, char, long*) { return S_OK; }
-};
-
-class Z {
- public:
-  void f(char, signed char, unsigned char) {}
-
-  void f(Y*) {}
-  // void f(const Y*) {} // not supported !!!
-
-  void m() {}
-  void m() const {}
-};
-
-class Test {
- public:
-  int Add(int i, int j) { return i + j; }
-  void Add(int i, int j, int* sum) { *sum = i + j; }
-};
-
-int Add(long i, long j) { return i + j; }
-void Add(long i, long j, long* sum) { *sum = i + j; }
-
-void Print(const char*) {}
-void Print1(CString*) {}
-
-}  // namespace
-
-class CompileTest : public Runnable {
- protected:
-  virtual void Run();
-};
-
-void CompileTest::Run() {
-  X x;
-  Y y;
-
-  CallFunction(X::g);
-  CallFunction(X::g, 10);
-  CallFunction(X::g, static_cast<unsigned int>(10), &x);
-  CallFunction(X::g, true, 'a', static_cast<long*>(0));
-
-  CallFunction(Y::g);
-  CallFunction(Y::g, 10);
-  CallFunction(Y::g, static_cast<unsigned int>(10), &x);
-  CallFunction(Y::g, true, 'a', static_cast<long*>(0));
-
-  CallMethod(&x, &X::f);
-  CallMethod(&x, &X::f, 10);
-  CallMethod(&x, &X::f, static_cast<unsigned int>(10), &x);
-  CallMethod(&x, &X::f, true, 'a', static_cast<long*>(0));
-
-  CallMethod(&y, &Y::f);
-  CallMethod(&y, &Y::f, 20);
-  CallMethod(&y, &Y::f, static_cast<unsigned int>(10), &x);
-  CallMethod(&y, &Y::f, true, 'a', static_cast<long*>(0));
-
-  Z z;
-  CallMethod(&z,
-             &Z::f,
-             'a',
-             static_cast<signed char>('a'),
-             static_cast<unsigned char>('a'));
-
-
-  CallMethod(&z, &Z::f, &y);
-
-  // Does not compile: template parameter 'P' is ambiguous
-  // const Y cy;
-  // CallMethod(&z, &Z::f, &cy);
-
-  CallMethod(&z, &Z::m);
-
-  // Does not compile: template parameter 'T' is ambiguous
-  // const Z cz;
-  // CallMethod(&cz, &Z::m);
-
-  // Does not compile: cannot convert from 'const Z *' to 'Z *const '
-  // const Z cz;
-  // CallMethod<const Z, void>(&cz, &Z::m);
-
-  CString msg(_T("test"));
-  CallFunction(Print, "test");
-  CallFunction(Print1, &msg);
-}
-
-class RuntimeTest : public Runnable {
- protected:
-  virtual void Run();
-};
-
-void RuntimeTest::Run() {
-  Test test;
-  ASSERT_EQ(CallMethod(&test, &Test::Add, 10, 20), 30);
-
-  int sum(0);
-  CallMethod(&test, &Test::Add, -10, 20, &sum);
-  ASSERT_EQ(sum, 10);
-
-  {
-  ASSERT_EQ(CallFunction(Add, long(10), long(20)), 30);
-
-  long sum = 0;
-  CallFunction(Add, long(10), long(-20), &sum);
-  ASSERT_EQ(sum, -10);
-  }
-}
-
-
-class AsyncTest : public Runnable {
- protected:
-  virtual void Run();
-};
-
-void AsyncTest::Run() {
-  static X x;
-  static Y y;
-
-  CallFunctionAsync(X::g);
-  CallFunctionAsync(X::g, 10);
-  CallFunctionAsync(X::g, static_cast<unsigned int>(10), &x);
-  CallFunctionAsync(X::g, true, 'a', static_cast<long*>(0));
-
-  CallFunctionAsync(Y::g);
-  CallFunctionAsync(Y::g, 10);
-  CallFunctionAsync(Y::g, static_cast<unsigned int>(10), &x);
-  CallFunctionAsync(Y::g, true, 'a', static_cast<long*>(0));
-
-  CallMethodAsync(&x, &X::f);
-  CallMethodAsync(&x, &X::f, 10);
-  CallMethodAsync(&x, &X::f, static_cast<unsigned int>(10), &x);
-  CallMethodAsync(&x, &X::f, true, 'a', static_cast<long*>(0));
-
-  CallMethodAsync(&y, &Y::f);
-  CallMethodAsync(&y, &Y::f, 20);
-  CallMethodAsync(&y, &Y::f, static_cast<unsigned int>(10), &x);
-  CallMethodAsync(&y, &Y::f, true, 'a', static_cast<long*>(0));
-
-  static Z z;
-  CallMethodAsync(&z,
-                  &Z::f,
-                  'a',
-                  static_cast<signed char>('a'),
-                  static_cast<unsigned char>('a'));
-
-
-  CallMethodAsync(&z, &Z::f, &y);
-
-  // Does not compile: template parameter 'P' is ambiguous
-  // const Y cy;
-  // CallMethod(&z, &Z::f, &cy);
-
-  CallMethodAsync(&z, &Z::m);
-
-  // Does not compile: template parameter 'T' is ambiguous
-  // const Z cz;
-  // CallMethod(&cz, &Z::m);
-
-  // Does not compile: cannot convert from 'const Z *' to 'Z *const '
-  // const Z cz;
-  // CallMethod<const Z, void>(&cz, &Z::m);
-
-  CString msg(_T("test"));
-  CallFunctionAsync(Print, "test");
-  CallFunctionAsync(Print1, &msg);
-
-  WaitWithMessageLoopTimed(1000);
-}
-
-
-TEST(STATest, CompileTest) {
-  ASSERT_SUCCEEDED(InitializeApartment(0));
-
-  Thread t;
-  CompileTest compile_test;
-  t.Start(&compile_test);
-  EXPECT_TRUE(WaitWithMessageLoop(t.GetThreadHandle()));
-
-  ASSERT_SUCCEEDED(UninitializeApartment());
-}
-
-TEST(STATest, RuntimeTest) {
-  ASSERT_SUCCEEDED(InitializeApartment(0));
-
-  Thread t;
-  RuntimeTest runtime_test;
-  t.Start(&runtime_test);
-  EXPECT_TRUE(WaitWithMessageLoop(t.GetThreadHandle()));
-
-  ASSERT_SUCCEEDED(UninitializeApartment());
-}
-
-
-TEST(STATest, AsyncTest) {
-  ASSERT_SUCCEEDED(InitializeApartment(0));
-
-  Thread t;
-  AsyncTest async_test;
-  t.Start(&async_test);
-  EXPECT_TRUE(WaitWithMessageLoop(t.GetThreadHandle()));
-
-  ASSERT_SUCCEEDED(UninitializeApartment());
-}
-
-TEST(STATest, ApartmentRefCounting) {
-  // Check the reference counting is working.
-  ASSERT_SUCCEEDED(InitializeApartment(0));
-  ASSERT_SUCCEEDED(InitializeApartment(0));
-  ASSERT_SUCCEEDED(UninitializeApartment());
-  ASSERT_SUCCEEDED(UninitializeApartment());
-
-  // The call below will raise an assert in the the STA code.
-  ExpectAsserts expect_asserts;
-  ASSERT_EQ(E_UNEXPECTED, UninitializeApartment());
-}
-
-TEST(STATest, ScopedSTA) {
-  {
-    scoped_sta sta(0);
-    ASSERT_SUCCEEDED(sta.result());
-  }
-  {
-    scoped_sta sta(0);
-    ASSERT_SUCCEEDED(sta.result());
-  }
-  {
-    scoped_sta sta1(0);
-    scoped_sta sta2(0);
-    ASSERT_SUCCEEDED(sta1.result());
-    ASSERT_SUCCEEDED(sta2.result());
-  }
-
-  ExpectAsserts expect_asserts;
-  ASSERT_EQ(E_UNEXPECTED, UninitializeApartment());
-}
-
-}  // namespace omaha
-
diff --git a/common/stats_uploader.cc b/common/stats_uploader.cc
new file mode 100644
index 0000000..9e41f11
--- /dev/null
+++ b/common/stats_uploader.cc
@@ -0,0 +1,283 @@
+// Copyright 2008-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include "omaha/common/stats_uploader.h"
+#include <atlbase.h>
+#include <atlconv.h>
+#include <atlstr.h>
+#include <ctime>
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/scoped_impersonation.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vista_utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/net/network_config.h"
+#include "omaha/net/network_request.h"
+#include "omaha/net/simple_request.h"
+#include "omaha/statsreport/aggregator-win32.h"
+#include "omaha/statsreport/const-win32.h"
+#include "omaha/statsreport/formatter.h"
+#include "omaha/statsreport/metrics.h"
+#include "omaha/statsreport/persistent_iterator-win32.h"
+
+using stats_report::g_global_metrics;
+
+using stats_report::kCountsKeyName;
+using stats_report::kTimingsKeyName;
+using stats_report::kIntegersKeyName;
+using stats_report::kBooleansKeyName;
+using stats_report::kStatsKeyFormatString;
+using stats_report::kLastTransmissionTimeValueName;
+
+using stats_report::Formatter;
+using stats_report::MetricsAggregatorWin32;
+using stats_report::PersistentMetricsIteratorWin32;
+
+namespace omaha {
+
+namespace {
+
+HRESULT ResetPersistentMetrics(RegKey* key) {
+  ASSERT1(key);
+  HRESULT result = S_OK;
+  DWORD now_sec = static_cast<DWORD>(time(NULL));
+  HRESULT hr = key->SetValue(kLastTransmissionTimeValueName, now_sec);
+  if (FAILED(hr)) {
+    result = hr;
+  }
+  hr = key->DeleteSubKey(kCountsKeyName);
+  if (FAILED(hr)) {
+    result = hr;
+  }
+  hr = key->DeleteSubKey(kTimingsKeyName);
+  if (FAILED(hr)) {
+    result = hr;
+  }
+  hr = key->DeleteSubKey(kIntegersKeyName);
+  if (FAILED(hr)) {
+    result = hr;
+  }
+  hr = key->DeleteSubKey(kBooleansKeyName);
+  if (FAILED(hr)) {
+    result = hr;
+  }
+  return result;
+}
+
+// Returns S_OK without uploading in OEM mode.
+HRESULT UploadMetrics(bool is_machine,
+                      const TCHAR* extra_url_data,
+                      const TCHAR* content) {
+  ASSERT1(content);
+
+  CString uid = goopdate_utils::GetUserIdLazyInit(is_machine);
+
+  // Impersonate the user if the caller is machine, running as local system,
+  // and a user is logged on to the system.
+  scoped_handle impersonation_token(
+      goopdate_utils::GetImpersonationTokenForMachineProcess(is_machine));
+  scoped_impersonation impersonate_user(get(impersonation_token));
+
+  // Do not access the network during an OEM install.
+  if (!ConfigManager::Instance()->CanUseNetwork(is_machine)) {
+    CORE_LOG(L1, (_T("[Stats not uploaded because network use prohibited]")));
+    return GOOPDATE_E_CANNOT_USE_NETWORK;
+  }
+
+  const TCHAR* version = GetVersionString();
+  CString test_source(ConfigManager::Instance()->GetTestSource());
+
+  CString url;
+  HRESULT hr = ConfigManager::Instance()->GetUsageStatsReportUrl(&url);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[GetUsageStatsReportUrl failed][0x%08x]"), hr));
+    return hr;
+  }
+  SafeCStringAppendFormat(&url, _T("?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s"),
+      kMetricsServerParamSourceId,  kMetricsProductName,
+      kMetricsServerParamVersion,   version,
+      kMetricsServerParamIsMachine, is_machine ? _T("1") : _T("0"),
+      kMetricsServerTestSource,     test_source,
+      kMetricsServerUserId,         uid,
+      extra_url_data);
+
+  CORE_LOG(L3, (_T("[upload usage stats][%s]"), content));
+
+  NetworkConfig* network_config = NULL;
+  NetworkConfigManager& network_manager = NetworkConfigManager::Instance();
+  hr = network_manager.GetUserNetworkConfig(&network_config);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[GetUserNetworkConfig failed][0x%08x]"), hr));
+    return hr;
+  }
+  NetworkRequest network_request(network_config->session());
+
+  network_request.set_num_retries(1);
+  network_request.AddHttpRequest(new SimpleRequest);
+
+  // PostRequest falls back to https.
+  std::vector<uint8> response_buffer;
+  return PostRequest(&network_request, true, url, content, &response_buffer);
+}
+
+HRESULT ReportMetrics(bool is_machine,
+                      const TCHAR* extra_url_data,
+                      DWORD interval) {
+  PersistentMetricsIteratorWin32 it(kMetricsProductName, is_machine), end;
+  Formatter formatter(CT2A(kMetricsProductName), interval);
+
+  for (; it != end; ++it) {
+    formatter.AddMetric(*it);
+  }
+
+  return UploadMetrics(is_machine, extra_url_data, CA2T(formatter.output()));
+}
+
+HRESULT DoResetMetrics(bool is_machine) {
+  CString key_name;
+  key_name.Format(kStatsKeyFormatString, kMetricsProductName);
+  HKEY parent_key = is_machine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+  RegKey key;
+  HRESULT hr = key.Create(parent_key, key_name);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Unable to create metrics key][0x%08x]"), hr));
+    return hr;
+  }
+  return ResetPersistentMetrics(&key);
+}
+
+HRESULT DoAggregateMetrics(bool is_machine) {
+  MetricsAggregatorWin32 aggregator(g_global_metrics,
+                                    kMetricsProductName,
+                                    is_machine);
+  if (!aggregator.AggregateMetrics()) {
+    CORE_LOG(LW, (_T("[Metrics aggregation failed for unknown reasons]")));
+    return GOOPDATE_E_METRICS_AGGREGATE_FAILED;
+  }
+  return S_OK;
+}
+
+HRESULT DoAggregateAndReportMetrics(bool is_machine, bool force_report) {
+  CString key_name;
+  key_name.Format(kStatsKeyFormatString, kMetricsProductName);
+  HKEY parent_key = is_machine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+  RegKey key;
+  HRESULT hr = key.Create(parent_key, key_name);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Unable to create metrics key][0x%08x]"), hr));
+    return hr;
+  }
+
+  DWORD now_sec = static_cast<DWORD>(time(NULL));
+
+  DWORD last_transmission_sec(0);
+  hr = key.GetValue(kLastTransmissionTimeValueName, &last_transmission_sec);
+
+  // Reset and start over if last transmission time is missing or hinky.
+  if (FAILED(hr) || last_transmission_sec > now_sec) {
+    CORE_LOG(LW, (_T("[hinky or missing last transmission time][%u][now: %u]"),
+                  last_transmission_sec, now_sec));
+    ResetPersistentMetrics(&key);
+    return S_OK;
+  }
+
+  hr = DoAggregateMetrics(is_machine);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[DoAggregateMetrics failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  DWORD time_since_last_transmission = now_sec - last_transmission_sec;
+  if (!force_report &&
+      time_since_last_transmission < kMetricsUploadIntervalSec) {
+    CORE_LOG(L1, (_T("[Stats upload not needed][last: %u][now: %u]"),
+                  last_transmission_sec, now_sec));
+    return S_OK;
+  }
+
+  // Report the metrics, reset the metrics, and update 'LastTransmission'.
+  hr = ReportMetrics(is_machine, NULL, time_since_last_transmission);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Stats upload failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  VERIFY1(SUCCEEDED(ResetPersistentMetrics(&key)));
+  CORE_LOG(L3, (_T("[Stats upload successful]")));
+  return S_OK;
+}
+
+bool InitializeLock(GLock* lock, bool is_machine) {
+  ASSERT1(lock);
+  NamedObjectAttributes attributes;
+  GetNamedObjectAttributes(kMetricsSerializer, is_machine, &attributes);
+  return lock->InitializeWithSecAttr(attributes.name, &attributes.sa);
+}
+
+}  // namespace
+
+
+HRESULT ResetMetrics(bool is_machine) {
+  CORE_LOG(L2, (_T("[ResetMetrics]")));
+  GLock lock;
+  if (!InitializeLock(&lock, is_machine)) {
+    return GOOPDATE_E_METRICS_LOCK_INIT_FAILED;
+  }
+  __mutexScope(lock);
+  return DoResetMetrics(is_machine);
+}
+
+HRESULT AggregateMetrics(bool is_machine) {
+  CORE_LOG(L2, (_T("[AggregateMetrics]")));
+
+  if (!ConfigManager::Instance()->CanCollectStats(is_machine)) {
+    return S_OK;
+  }
+
+  GLock lock;
+  if (!InitializeLock(&lock, is_machine)) {
+    return GOOPDATE_E_METRICS_LOCK_INIT_FAILED;
+  }
+  __mutexScope(lock);
+  return DoAggregateMetrics(is_machine);
+}
+
+HRESULT AggregateAndReportMetrics(bool is_machine, bool force_report) {
+  CORE_LOG(L2, (_T("[AggregateAndReportMetrics]")));
+
+  if (!ConfigManager::Instance()->CanCollectStats(is_machine)) {
+    return S_OK;
+  }
+
+  GLock lock;
+  if (!InitializeLock(&lock, is_machine)) {
+    return GOOPDATE_E_METRICS_LOCK_INIT_FAILED;
+  }
+  __mutexScope(lock);
+  return DoAggregateAndReportMetrics(is_machine, force_report);
+}
+
+}  // namespace omaha
+
diff --git a/common/stats_uploader.h b/common/stats_uploader.h
new file mode 100644
index 0000000..49dca19
--- /dev/null
+++ b/common/stats_uploader.h
@@ -0,0 +1,52 @@
+// Copyright 2008-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Constants for the statsreport library.
+
+#ifndef OMAHA_COMMON_STATS_UPLOADER_H__
+#define OMAHA_COMMON_STATS_UPLOADER_H__
+
+#include <windows.h>
+#include <tchar.h>
+
+namespace omaha {
+
+// The product name is chosen so that the stats are persisted under
+// the Google Update registry key for the machine or user, respectively.
+const TCHAR* const kMetricsProductName           = _T("Update");
+
+const TCHAR* const kMetricsServerParamSourceId   = _T("sourceid");
+const TCHAR* const kMetricsServerParamVersion    = _T("v");
+const TCHAR* const kMetricsServerParamIsMachine  = _T("ismachine");
+const TCHAR* const kMetricsServerTestSource      = _T("testsource");
+const TCHAR* const kMetricsServerUserId          = _T("ui");
+
+// Metrics are uploaded every 25 hours.
+const int kMetricsUploadIntervalSec              = 25 * 60 * 60;
+
+// Deletes existing metrics and initializes 'LastTransmission' to current time.
+HRESULT ResetMetrics(bool is_machine);
+
+// Aggregates metrics by saving them in registry.
+HRESULT AggregateMetrics(bool is_machine);
+
+// Aggregates and reports the metrics if needed, as defined by the metrics
+// upload interval. The interval is ignored when 'force_report' is true.
+HRESULT AggregateAndReportMetrics(bool is_machine, bool force_report);
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_STATS_UPLOADER_H__
+
diff --git a/common/stats_uploader_unittest.cc b/common/stats_uploader_unittest.cc
new file mode 100644
index 0000000..a28248b
--- /dev/null
+++ b/common/stats_uploader_unittest.cc
@@ -0,0 +1,262 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// All tests are user only.
+
+#include <windows.h>
+#include <limits.h>
+#include <ctime>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/error.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/common/stats_uploader.h"
+#include "omaha/statsreport/metrics.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+DEFINE_METRIC_bool(test_bool);
+
+}  // namespace
+
+class StatsUploaderTest : public testing::Test {
+ protected:
+  // These tests assume that metric collection is enabled. Since HKLM is not
+  // overridden, save the existing value if present before overriding it.
+  static void SetUpTestCase() {
+    is_updatedev_usagestats_present_ =
+        SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                                   kRegValueForceUsageStats,
+                                   &existing_updatedev_usagestats_value_));
+
+    EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                      kRegValueForceUsageStats,
+                                      static_cast<DWORD>(1)));
+  }
+
+  static void TearDownTestCase() {
+    if (is_updatedev_usagestats_present_) {
+      EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                        kRegValueForceUsageStats,
+                                        existing_updatedev_usagestats_value_));
+    } else {
+      EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV,
+                                           kRegValueForceUsageStats));
+    }
+  }
+
+  virtual void SetUp() {
+    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
+
+    // Overriding HKLM prevents the Windows DNS resolver from working.
+    // Only override HKCU and run the tests as user.
+    OverrideSpecifiedRegistryHives(kRegistryHiveOverrideRoot, false, true);
+    stats_report::g_global_metrics.Initialize();
+  }
+
+  virtual void TearDown() {
+    stats_report::g_global_metrics.Uninitialize();
+    RestoreRegistryHives();
+    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
+  }
+
+  HRESULT GetMetricValue(const TCHAR* value_name, bool* value) {
+    CString key_name = key_name_ + CString(_T("Booleans"));
+
+    scoped_array<byte> buffer;
+    DWORD byte_count(0);
+    HRESULT hr = RegKey::GetValue(key_name,
+                                  value_name,
+                                  address(buffer),
+                                  &byte_count);
+    if (FAILED(hr)) {
+      return hr;
+    }
+    if (byte_count != sizeof(uint32)) {                         // NOLINT
+      return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+    }
+    *value = *reinterpret_cast<uint32*>(buffer.get()) != 0;
+    return S_OK;
+  }
+
+  HRESULT GetLastTrasmission(DWORD* last_transmission) {
+    const TCHAR value_name[] = _T("LastTransmission");
+    return RegKey::GetValue(key_name_, value_name, last_transmission);
+  }
+
+  HRESULT SetLastTransmission(DWORD last_transmission) {
+    const TCHAR value_name[] = _T("LastTransmission");
+    return RegKey::SetValue(key_name_, value_name, last_transmission);
+  }
+
+  bool AreMetricsEmpty() {
+    RegKey reg_key;
+    HRESULT hr = reg_key.Open(key_name_, KEY_READ);
+    if (FAILED(hr)) {
+      return true;
+    }
+    return reg_key.GetSubkeyCount() == 0;
+  }
+
+  static const TCHAR key_name_[];
+  static const TCHAR metric_name_[];
+
+ private:
+  static bool is_updatedev_usagestats_present_;
+  static DWORD existing_updatedev_usagestats_value_;
+};
+
+const TCHAR StatsUploaderTest::key_name_[] =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME
+    _T("\\") PRODUCT_NAME _T("\\UsageStats\\Daily\\");
+const TCHAR StatsUploaderTest::metric_name_[] =  _T("test_bool");
+
+bool StatsUploaderTest::is_updatedev_usagestats_present_ = false;
+DWORD StatsUploaderTest::existing_updatedev_usagestats_value_ = 0;
+
+TEST_F(StatsUploaderTest, AggregateMetrics) {
+  bool value = false;
+  EXPECT_HRESULT_FAILED(GetMetricValue(metric_name_, &value));
+
+  metric_test_bool = true;
+  EXPECT_HRESULT_SUCCEEDED(AggregateMetrics(false));    // User.
+
+  EXPECT_HRESULT_SUCCEEDED(GetMetricValue(metric_name_, &value));
+  EXPECT_EQ(true, value);
+
+  metric_test_bool = false;
+  EXPECT_HRESULT_SUCCEEDED(AggregateMetrics(false));    // User.
+
+  EXPECT_HRESULT_SUCCEEDED(GetMetricValue(metric_name_, &value));
+  EXPECT_EQ(false, value);
+}
+
+TEST_F(StatsUploaderTest, AggregateAndReportMetrics) {
+  metric_test_bool = true;
+
+  // Metrics are not in the registry until they are aggregated.
+  bool value = false;
+  EXPECT_HRESULT_FAILED(GetMetricValue(metric_name_, &value));
+
+  // AggregateAndReportMetrics resets metrics and updates 'LastTransmission' to
+  // the current time since there was no 'LastTransmission'.
+  EXPECT_HRESULT_SUCCEEDED(AggregateAndReportMetrics(false, false));
+  EXPECT_TRUE(AreMetricsEmpty());
+  DWORD last_transmission(0);
+  EXPECT_HRESULT_SUCCEEDED(GetLastTrasmission(&last_transmission));
+  EXPECT_NE(0, last_transmission);
+
+  // AggregateAndReportMetrics aggregates but it does not report since
+  // 'LastTransmission is current.
+  EXPECT_HRESULT_SUCCEEDED(AggregateAndReportMetrics(false, false));
+  EXPECT_FALSE(AreMetricsEmpty());
+  EXPECT_HRESULT_SUCCEEDED(GetMetricValue(metric_name_, &value));
+  EXPECT_EQ(true, value);
+  DWORD previous_last_transmission = last_transmission;
+  EXPECT_HRESULT_SUCCEEDED(GetLastTrasmission(&last_transmission));
+  EXPECT_EQ(previous_last_transmission, last_transmission);
+
+  // Roll back 'Last Trasmission' by 26 hours. AggregateAndReportMetrics
+  // aggregates, reports metrics, and updates 'LastTransmission'.
+  metric_test_bool = true;
+  last_transmission -= 26 * 60 * 60;
+  EXPECT_HRESULT_SUCCEEDED(SetLastTransmission(last_transmission));
+  EXPECT_HRESULT_SUCCEEDED(AggregateAndReportMetrics(false, false));
+  EXPECT_TRUE(AreMetricsEmpty());
+  previous_last_transmission = last_transmission;
+  EXPECT_HRESULT_SUCCEEDED(GetLastTrasmission(&last_transmission));
+  EXPECT_NE(previous_last_transmission, last_transmission);
+
+  // Roll forward the 'LastTransmission' by 60 seconds.
+  // AggregateAndReportMetrics resets metrics and updates 'LastTransmission' to
+  // the current time since there 'LastTransmission' was in the future.
+  metric_test_bool = true;
+  last_transmission = static_cast<DWORD>(time(NULL)) + 60;
+  EXPECT_HRESULT_SUCCEEDED(SetLastTransmission(last_transmission));
+  EXPECT_HRESULT_SUCCEEDED(AggregateAndReportMetrics(false, false));
+  EXPECT_TRUE(AreMetricsEmpty());
+  EXPECT_HRESULT_SUCCEEDED(GetLastTrasmission(&last_transmission));
+  EXPECT_NE(0, last_transmission);
+
+  // Force reporting the metrics.
+  metric_test_bool = true;
+  EXPECT_HRESULT_SUCCEEDED(AggregateAndReportMetrics(false, true));
+  EXPECT_TRUE(AreMetricsEmpty());
+}
+
+TEST_F(StatsUploaderTest, ResetPersistentMetricsTest) {
+  const TCHAR* keys[] = {
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\UsageStats\\Daily\\Timings"),  // NOLINT
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\UsageStats\\Daily\\Counts"),   // NOLINT
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\UsageStats\\Daily\\Integers"), // NOLINT
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\UsageStats\\Daily\\Booleans"), // NOLINT
+  };
+  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKeys(keys, arraysize(keys)));
+  EXPECT_HRESULT_SUCCEEDED(ResetMetrics(false));    // User.
+
+  for (size_t i = 0; i != arraysize(keys); ++i) {
+    EXPECT_FALSE(RegKey::HasKey(keys[i]));
+  }
+  EXPECT_TRUE(AreMetricsEmpty());
+
+  DWORD last_transmission(ULONG_MAX);
+  EXPECT_HRESULT_SUCCEEDED(GetLastTrasmission(&last_transmission));
+  EXPECT_NE(0, last_transmission);
+}
+
+// AggregateAndReportMetrics aggregates, but is unable to report metrics and
+// does not update 'LastTransmission'.
+TEST_F(StatsUploaderTest,
+       AggregateAndReportMetrics_GoogleUpdateEulaNotAccepted_DoNotForce) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+
+  metric_test_bool = true;
+  DWORD last_transmission = 12345678;
+  EXPECT_HRESULT_SUCCEEDED(SetLastTransmission(last_transmission));
+  EXPECT_EQ(GOOPDATE_E_CANNOT_USE_NETWORK,
+            AggregateAndReportMetrics(false, false));
+  EXPECT_FALSE(AreMetricsEmpty());
+  DWORD previous_last_transmission = last_transmission;
+  EXPECT_HRESULT_SUCCEEDED(GetLastTrasmission(&last_transmission));
+  EXPECT_EQ(12345678, last_transmission);
+}
+
+// AggregateAndReportMetrics aggregates, but is unable to report metrics and
+// does not update 'LastTransmission'.
+TEST_F(StatsUploaderTest,
+       AggregateAndReportMetrics_GoogleUpdateEulaNotAccepted_Force) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+
+  metric_test_bool = true;
+  DWORD last_transmission = 12345678;
+  EXPECT_HRESULT_SUCCEEDED(SetLastTransmission(last_transmission));
+  EXPECT_EQ(GOOPDATE_E_CANNOT_USE_NETWORK,
+            AggregateAndReportMetrics(false, true));
+  EXPECT_FALSE(AreMetricsEmpty());
+  DWORD previous_last_transmission = last_transmission;
+  EXPECT_HRESULT_SUCCEEDED(GetLastTrasmission(&last_transmission));
+  EXPECT_EQ(12345678, last_transmission);
+}
+
+}  // namespace omaha
diff --git a/common/store_watcher.h b/common/store_watcher.h
deleted file mode 100644
index d630540..0000000
--- a/common/store_watcher.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// A simple interface for monitoring changes
-// happening to a store.
-//
-#ifndef OMAHA_COMMON_STORE_WATCHER_H_
-#define OMAHA_COMMON_STORE_WATCHER_H_
-
-#include "omaha/common/synchronized.h"
-
-namespace omaha {
-
-// Allows for monitoring changes happening to a store
-// (independant of what the underlying store is).
-class StoreWatcher {
- public:
-  StoreWatcher() {}
-  virtual ~StoreWatcher() {}
-
-  // Called to create/reset the event that gets signaled
-  // any time the store changes.  Access the created
-  // event using change_event().
-  virtual HRESULT EnsureEventSetup() = 0;
-
-  // Indicates if any changes have occured
-  bool HasChangeOccurred() const {
-    return IsHandleSignaled(change_event());
-  }
-
-  // Get the event that is signaled on store changes.
-  // Note:
-  //   * This event will remain constant until the class is destroyed.
-  //   * One should call EnsureEventSetup to set-up the event.
-  //   * The event is only signaled on the next change and remains signaled.
-  //     Do not call ::ResetEvent(). Call EnsureEventSetup() to reset
-  //     the event and wait for more changes.
-  virtual HANDLE change_event() const = 0;
-
- private:
-  DISALLOW_EVIL_CONSTRUCTORS(StoreWatcher);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_STORE_WATCHER_H_
diff --git a/common/string.cc b/common/string.cc
deleted file mode 100644
index 9e9b63d..0000000
--- a/common/string.cc
+++ /dev/null
@@ -1,3345 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/string.h"
-
-#include <wininet.h>        // For INTERNET_MAX_URL_LENGTH.
-#include <algorithm>
-#include <cstdlib>
-#include "base/scoped_ptr.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/localization.h"
-#include "omaha/common/logging.h"
-
-namespace omaha {
-
-namespace {
-// Testing shows that only the following ASCII characters are
-// considered spaces by GetStringTypeA: 9-13, 32, 160.
-// Rather than call GetStringTypeA with no locale, as we used to,
-// we look up the values directly in a precomputed array.
-
-SELECTANY byte spaces[256] = {
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 1,  // 0-9
-  1, 1, 1, 1, 0, 0, 0, 0, 0, 0,  // 10-19
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 20-29
-  0, 0, 1, 0, 0, 0, 0, 0, 0, 0,  // 30-39
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 40-49
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 50-59
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 60-69
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 70-79
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 80-89
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 90-99
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 100-109
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 110-119
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 120-129
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 130-139
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 140-149
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 150-159
-  1, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 160-169
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 170-179
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 180-189
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 190-199
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 200-209
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 210-219
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 220-229
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 230-239
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 240-249
-  0, 0, 0, 0, 0, 1,              // 250-255
-};
-}  // namespace
-
-const TCHAR* const kFalse = _T("false");
-const TCHAR* const kTrue  = _T("true");
-
-bool IsSpaceW(WCHAR c) {
-  // GetStringTypeW considers these characters to be spaces:
-  // 9-13, 32, 133, 160, 5760, 8192-8203, 8232, 8233, 12288
-  if (c < 256)
-    return (c == 133 || IsSpaceA((char) (c & 0xff)));
-
-  return (c >= 8192 && c <= 8203) || c == 8232 ||
-    c == 8233 || c == 12288;
-}
-
-bool IsSpaceA(char c) {
-  return spaces[static_cast<unsigned char>(c)] == 1;
-}
-
-int TrimCString(CString &s) {
-  int len = Trim(s.GetBuffer());
-  s.ReleaseBufferSetLength(len);
-  return len;
-}
-
-void MakeLowerCString(CString & s) {
-  int len = s.GetLength();
-  String_FastToLower(s.GetBuffer());
-  s.ReleaseBufferSetLength(len);
-}
-
-int Trim(TCHAR *s) {
-  ASSERT(s, (L""));
-
-  // First find end of leading spaces
-  TCHAR *start = s;
-  while (*start) {
-    if (!IsSpace(*start))
-      break;
-    ++start;
-  }
-
-  // Now search for the end, remembering the start of the last spaces
-  TCHAR *end = start;
-  TCHAR *last_space = end;
-  while (*end) {
-    if (!IsSpace(*end))
-      last_space = end + 1;
-    ++end;
-  }
-
-  // Copy the part we want
-  int len = last_space - start;
-  // lint -e{802}  Conceivably passing a NULL pointer
-  memmove(s, start, len * sizeof(TCHAR));
-
-  // 0 terminate
-  s[len] = 0;
-
-  return len;
-}
-
-void TrimString(CString& s, const TCHAR* delimiters) {
-  s = s.Trim(delimiters);
-}
-
-// Strip the first token from the front of argument s.  A token is a
-// series of consecutive non-blank characters - unless the first
-// character is a double-quote ("), in that case the token is the full
-// quoted string
-CString StripFirstQuotedToken(const CString& s) {
-  const int npos = -1;
-
-  // Make a writeable copy
-  CString str(s);
-
-  // Trim any surrounding blanks (and tabs, for the heck of it)
-  TrimString(str, L" \t");
-
-  // Too short to have a second token
-  if (str.GetLength() <= 1)
-    return L"";
-
-  // What kind of token are we stripping?
-  if (str[0] == L'\"') {
-    // Remove leading quoting string
-    int i = str.Find(L"\"", 1);
-    if (i != npos)
-      i++;
-    return str.Mid(i);
-  } else {
-    // Remove leading token
-    int i = str.FindOneOf(L" \t");
-    if (i != npos)
-      i++;
-    return str.Mid(i);
-  }
-}
-
-// A block of text to separate lines, and back
-void TextToLines(const CString& text, const TCHAR* delimiter, std::vector<CString>* lines) {
-  ASSERT(delimiter, (L""));
-  ASSERT(lines, (L""));
-
-  size_t delimiter_len = ::lstrlen(delimiter);
-  int b = 0;
-  int e = 0;
-
-  for (b = 0; e != -1 && b < text.GetLength(); b = e + delimiter_len) {
-    e = text.Find(delimiter, b);
-    if (e != -1) {
-      ASSERT1(e - b > 0);
-      lines->push_back(text.Mid(b, e - b));
-    } else {
-      lines->push_back(text.Mid(b));
-    }
-  }
-}
-
-void LinesToText(const std::vector<CString>& lines, const TCHAR* delimiter, CString* text) {
-  ASSERT(delimiter, (L""));
-  ASSERT(text, (L""));
-
-  size_t delimiter_len = ::lstrlen(delimiter);
-  size_t len = 0;
-  for (size_t i = 0; i < lines.size(); ++i) {
-    len += lines[i].GetLength() + delimiter_len;
-  }
-  text->Empty();
-  text->Preallocate(len);
-  for (std::vector<CString>::size_type i = 0; i < lines.size(); ++i) {
-    text->Append(lines[i]);
-    if (delimiter_len) {
-      text->Append(delimiter);
-    }
-  }
-}
-
-int CleanupWhitespaceCString(CString &s) {
-  int len = CleanupWhitespace(s.GetBuffer());
-  s.ReleaseBufferSetLength(len);
-  return len;
-}
-
-int CleanupWhitespace(TCHAR *str) {
-  ASSERT(str, (L""));
-
-  TCHAR *src      = str;
-  TCHAR *dest     = str;
-  int    spaces   = 0;
-  bool   at_start = true;
-  while (true) {
-    // At end of string?
-    TCHAR c = *src;
-    if (0 == c)
-      break;
-
-    // Look for whitespace; copy it over if not whitespace
-    if (IsSpace(c)) {
-      ++spaces;
-    }
-    else {
-      *dest++ = c;
-      at_start = false;
-      spaces = 0;
-    }
-
-    // Write only first consecutive space (but skip space at start)
-    if (1 == spaces && !at_start)
-      *dest++ = ' ';
-
-    ++src;
-  }
-
-  // Remove trailing space, if any
-  if (dest > str && *(dest - 1) == L' ')
-    --dest;
-
-  // 0-terminate
-  *dest = 0;
-
-  return dest - str;
-}
-
-// Take 1 single hexadecimal "digit" (as a character) and return its decimal value
-// Returns -1 if given invalid hex digit
-int HexDigitToDec(const TCHAR digit) {
-  if (digit >= L'A' && digit <= L'F')
-    return 10 + (digit - L'A');
-  else if (digit >= L'a' && digit <= L'f')
-    return 10 + (digit - L'a');
-  else if (digit >= L'0' && digit <= L'9')
-    return (digit - L'0');
-  else
-    return -1;
-}
-
-// Convert the 2 hex chars at positions <pos> and <pos>+1 in <s> to a char (<char_out>)
-// Note: scanf was giving me troubles, so here's the manual version
-// Extracted char gets written to <char_out>, which must be allocated by
-// the caller; return true on success or false if parameters are incorrect
-// or string does not have 2 hex digits at the specified position
-// NOTE: <char_out> is NOT a string, just a pointer to a char for the result
-bool ExtractChar(const CString & s, int pos, unsigned char * char_out) {
-  // char_out may be NULL
-
-  if (s.GetLength() < pos + 1) {
-    return false;
-  }
-
-  if (pos < 0 || NULL == char_out) {
-    ASSERT(0, (_T("invalid params: pos<0 or char_out is NULL")));
-    return false;
-  }
-
-  TCHAR c1 = s.GetAt(pos);
-  TCHAR c2 = s.GetAt(pos+1);
-
-  int p1 = HexDigitToDec(c1);
-  int p2 = HexDigitToDec(c2);
-
-  if (p1 == -1 || p2 == -1) {
-    return false;
-  }
-
-  *char_out = (unsigned char)(p1 * 16 + p2);
-  return true;
-}
-
-WCHAR *ToWide (const char *s, int len) {
-    ASSERT (s, (L""));
-    WCHAR *w = new WCHAR [len+1]; if (!w) { return NULL; }
-    // int rc = MultiByteToWideChar (CP_ACP, 0, s.GetString(), (int)s.GetLength()+1, w, s.GetLength()+1);
-    // TODO(omaha): why would it ever be the case that rc > len?
-    int rc = MultiByteToWideChar (CP_ACP, 0, s, len, w, len);
-    if (rc > len) { delete [] w; return NULL; }
-    // ASSERT (rc <= len, (L""));
-    w[rc]=L'\0';
-    return w;
-}
-
-const byte *BufferContains (const byte *buf, uint32 buf_len, const byte *data, uint32 data_len) {
-  ASSERT(data, (L""));
-  ASSERT(buf, (L""));
-
-  for (uint32 i = 0; i < buf_len; i++) {
-    uint32 j = i;
-    uint32 k = 0;
-    uint32 len = 0;
-    while (j < buf_len && k < data_len && buf[j++] == data[k++]) { len++; }
-    if (len == data_len) { return buf + i; }
-  }
-  return 0;
-}
-
-// Converting the Ansi Multibyte String into unicode string. The multibyte
-// string is encoded using the specified codepage.
-// The code is pretty much like the U2W function, except the codepage can be
-// any valid windows CP.
-BOOL AnsiToWideString(const char *from, int length, UINT codepage, CString *to) {
-  ASSERT(from, (L""));
-  ASSERT(to, (L""));
-  ASSERT1(length >= -1);
-  // Figure out how long the string is
-  int req_chars = MultiByteToWideChar(codepage, 0, from, length, NULL, 0);
-
-  if (req_chars <= 0) {
-    UTIL_LOG(LEVEL_WARNING, (_T("MultiByteToWideChar Failed ")));
-    *to = AnsiToWideString(from, length);
-    return FALSE;
-  }
-
-  TCHAR *buffer = to->GetBufferSetLength(req_chars);
-  int conv_chars = MultiByteToWideChar(codepage, 0, from, length, buffer, req_chars);
-  if (conv_chars == 0) {
-    UTIL_LOG(LEVEL_WARNING, (_T("MultiByteToWideChar Failed ")));
-    to->ReleaseBuffer(0);
-    *to = AnsiToWideString(from, length);
-    return FALSE;
-  }
-
-  // Something truly horrible happened.
-  ASSERT (req_chars == conv_chars, (L"MBToWide returned unexpected value: GetLastError()=%d",GetLastError()));
-  // If length was inferred, conv_chars includes the null terminator.
-  // Adjust the length here to remove null termination,
-  // because we use the length-qualified CString constructor,
-  // which automatically adds null termination given an unterminated array.
-  if (-1 == length) { --conv_chars; }
-  to->ReleaseBuffer(conv_chars);
-  return TRUE;
-}
-
-// CStringW(const char* from) did not cast all character properly
-// so we write our own.
-CString AnsiToWideString(const char *from, int length) {
-  ASSERT(from, (L""));
-  ASSERT1(length >= -1);
-  if (length < 0)
-    length = strlen(from);
-  CString to;
-  TCHAR *buffer = to.GetBufferSetLength(length);
-  for (int i = 0; i < length; ++i)
-      buffer[i] = static_cast<UINT8>(from[i]);
-  to.ReleaseBuffer(length);
-  return to;
-}
-
-
-// Transform a unicode string into UTF8, as represented in an ASCII string
-CStringA WideToUtf8(const CString& w) {
-  // Add a cutoff. If it's all ascii, convert it directly
-  const TCHAR* input = static_cast<const TCHAR*>(w.GetString());
-  int input_len = w.GetLength(), i;
-  for (i = 0; i < input_len; ++i) {
-    if (input[i] > 127) {
-      break;
-    }
-  }
-
-  // If we made it to the end without breaking, then it's all ANSI, so do a quick convert
-  if (i == input_len) {
-    return WideToAnsiDirect(w);
-  }
-
-  // Figure out how long the string is
-  int req_bytes = ::WideCharToMultiByte(CP_UTF8, 0, w, -1, NULL, 0, NULL, NULL);
-
-  scoped_array<char> utf8_buffer(new char[req_bytes]);
-
-  int conv_bytes = ::WideCharToMultiByte(CP_UTF8, 0, w, -1, utf8_buffer.get(), req_bytes, NULL, NULL);
-  ASSERT1(req_bytes == conv_bytes);
-
-  // conv_bytes includes the null terminator, when we read this in, don't read the terminator
-  CStringA out(utf8_buffer.get(), conv_bytes - 1);
-
-  return out;
-}
-
-CString Utf8ToWideChar(const char* utf8, uint32 num_bytes) {
-  ASSERT1(utf8);
-  if (num_bytes == 0) {
-    // It's OK to use lstrlenA to count the number of characters in utf8 because
-    // UTF-8 encoding has the nice property of not having any embedded NULLs.
-    num_bytes = lstrlenA(utf8);
-  }
-
-  uint32 number_of_wide_chars = ::MultiByteToWideChar(CP_UTF8, 0, utf8, num_bytes, NULL, 0);
-  number_of_wide_chars += 1;  // make room for NULL terminator
-
-  CString ret_string;
-  TCHAR* buffer = ret_string.GetBuffer(number_of_wide_chars);
-  DWORD number_of_characters_copied = ::MultiByteToWideChar(CP_UTF8, 0, utf8, num_bytes, buffer, number_of_wide_chars);
-  ASSERT1(number_of_characters_copied == number_of_wide_chars - 1);
-  buffer[number_of_wide_chars - 1] = _T('\0');  // ensure there is a NULL terminator
-  ret_string.ReleaseBuffer();
-
-  // Strip the byte order marker if there is one in the document.
-  if (ret_string[0] == kUnicodeBom) {
-    ret_string = ret_string.Right(ret_string.GetLength() - 1);
-  }
-
-  if (number_of_characters_copied > 0) {
-    return ret_string;
-  }
-
-  // Failure case
-  return _T("");
-}
-
-CString Utf8BufferToWideChar(const std::vector<uint8>& buffer) {
-  CString result;
-  if (!buffer.empty()) {
-    result = Utf8ToWideChar(
-        reinterpret_cast<const char*>(&buffer.front()), buffer.size());
-  }
-  return result;
-}
-
-CString AbbreviateString (const CString & title, int32 max_len) {
-    ASSERT (max_len, (L""));
-    CString s(title);
-    TrimCString(s);  // remove whitespace at start/end
-    if (s.GetLength() > max_len) {
-        s = s.Left (max_len - 2);
-        CString orig(s);
-        // remove partial words
-        while (s.GetLength() > 1 && !IsSpace(s[s.GetLength()-1])) { s = s.Left (s.GetLength() - 1); }
-        // but not if it would make the string very short
-        if (s.GetLength() < max_len / 2) { s = orig; }
-        s += _T("..");
-        }
-
-    return s;
-}
-
-CString GetAbsoluteUri(const CString& uri) {
-  int i = String_FindString(uri, _T("://"));
-  if (i==-1) return uri;
-
-  // add trailing / if none exists
-  int j = String_FindChar(uri, L'/',i+3);
-  if (j==-1) return (uri+NOTRANSL(_T("/")));
-
-  // remove duplicate trailing slashes
-  int len = uri.GetLength();
-  if (len > 1 && uri.GetAt(len-1) == '/' && uri.GetAt(len-2) == '/') {
-    CString new_uri(uri);
-    int new_len = new_uri.GetLength();
-    while (new_len > 1 && new_uri.GetAt(new_len-1) == '/' && new_uri.GetAt(new_len-2) == '/') {
-      new_len--;
-      new_uri = new_uri.Left(new_len);
-    }
-    return new_uri;
-  }
-  else return uri;
-}
-
-// requires that input have a PROTOCOL (http://) for proper behavior
-// items with the "file" protocol are returned as is (what is the hostname in that case? C: ? doesn't make sense)
-// TODO(omaha): loosen requirement
-// includes http://, e.g. http://www.google.com/
-CString GetUriHostName(const CString& uri, bool strip_leading) {
-  if (String_StartsWith(uri,NOTRANSL(_T("file:")),true)) return uri;
-
-  // correct any "errors"
-  CString s(GetAbsoluteUri(uri));
-
-  // Strip the leading "www."
-  if (strip_leading)
-  {
-    int index_www = String_FindString(s, kStrLeadingWww);
-    if (index_www != -1)
-      ReplaceCString (s, kStrLeadingWww, _T(""));
-  }
-
-  int i = String_FindString(s, _T("://"));
-  if(i==-1) return uri;
-  int j = String_FindChar(s, L'/',i+3);
-  if(j==-1) return uri;
-  return s.Left(j+1);
-}
-
-// requires that input have a PROTOCOL (http://) for proper behavior
-// TODO(omaha): loosen requirement
-// removes the http:// and the extra slash '/' at the end.
-// http://www.google.com/ -> www.google.com (or google.com if strip_leading = true)
-CString GetUriHostNameHostOnly(const CString& uri, bool strip_leading) {
-  CString s(GetUriHostName(uri,strip_leading));
-
-  // remove protocol
-  int i = String_FindString (s, _T("://"));
-  if(i==-1) return s;
-  CString ss(s.Right (s.GetLength() - i-3));
-
-  // remove the last '/'
-  int j = ss.ReverseFind('/');
-  if (j == -1) return ss;
-  return ss.Left(j);
-}
-
-CString AbbreviateUri(const CString& uri, int32 max_len) {
-  ASSERT1(max_len);
-  ASSERT1(!uri.IsEmpty());
-
-  CString s(uri);
-  VERIFY1(String_FindString (s, _T("://")));
-
-  TrimCString(s);
-  // SKIP_LOC_BEGIN
-  RemoveFromStart (s, _T("ftp://"), false);
-  RemoveFromStart (s, _T("http://"), false);
-  RemoveFromStart (s, _T("https://"), false);
-  RemoveFromStart (s, _T("www."), false);
-  RemoveFromStart (s, _T("ftp."), false);
-  RemoveFromStart (s, _T("www-"), false);
-  RemoveFromStart (s, _T("ftp-"), false);
-  RemoveFromEnd (s, _T(".htm"));
-  RemoveFromEnd (s, _T(".html"));
-  RemoveFromEnd (s, _T(".asp"));
-  // SKIP_LOC_END
-  if (s.GetLength() > max_len) {
-    // try to keep the portion after the last /
-    int32 last_slash = s.ReverseFind ((TCHAR)'/');
-    CString after_last_slash;
-    if (last_slash == -1) { after_last_slash = _T(""); }
-    else { after_last_slash = s.Right (uri.GetLength() - last_slash - 1); }
-    if (after_last_slash.GetLength() > max_len / 2) {
-        after_last_slash = after_last_slash.Right (max_len / 2);
-    }
-    s = s.Left (max_len - after_last_slash.GetLength() - 2);
-    s += "..";
-    s += after_last_slash;
-  }
-  return s;
-}
-
-// normalized version of a URI intended to map duplicates to the same string
-// the normalized URI is not a valid URI
-CString NormalizeUri (const CString & uri) {
-  CString s(uri);
-  TrimCString(s);
-  MakeLowerCString(s);
-  // SKIP_LOC_BEGIN
-  ReplaceCString (s, _T(":80"), _T(""));
-
-  RemoveFromEnd (s, _T("/index.html"));
-  RemoveFromEnd (s, _T("/welcome.html"));  // old netscape standard
-  RemoveFromEnd (s, _T("/"));
-
-  RemoveFromStart (s, _T("ftp://"), false);
-  RemoveFromStart (s, _T("http://"), false);
-  RemoveFromStart (s, _T("https://"), false);
-  RemoveFromStart (s, _T("www."), false);
-  RemoveFromStart (s, _T("ftp."), false);
-  RemoveFromStart (s, _T("www-"), false);
-  RemoveFromStart (s, _T("ftp-"), false);
-
-  ReplaceCString (s, _T("/./"), _T("/"));
-  // SKIP_LOC_END
-
-  // TODO(omaha):
-  // fixup URLs like a/b/../../c
-  // while ($s =~ m!\/\.\.\!!) {
-  //    $s =~ s!/[^/]*/\.\./!/!;
-  //    }
-
-  // TODO(omaha):
-  // unescape characters
-  // Note from RFC1630:  "Sequences which start with a percent sign
-  // but are not followed by two hexadecimal characters are reserved
-  // for future extension"
-  // $str =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg if defined $str;
-
-  return s;
-}
-
-CString RemoveInternetProtocolHeader (const CString& url) {
-  int find_colon_slash_slash = String_FindString(url, NOTRANSL(L"://"));
-  if( find_colon_slash_slash != -1 ) {
-    // remove PROTOCOL://
-    return url.Right(url.GetLength() - find_colon_slash_slash - 3);
-  } else if (String_StartsWith(url, NOTRANSL(L"mailto:"), true)) {
-    // remove "mailto:"
-    return url.Right(url.GetLength() - 7);
-  } else {
-    // return as is
-    return url;
-  }
-}
-
-void RemoveFromStart (CString & s, const TCHAR* remove, bool ignore_case) {
-  ASSERT(remove, (L""));
-
-  // Remove the characters if it is the prefix
-  if (String_StartsWith(s, remove, ignore_case))
-    s.Delete(0, lstrlen(remove));
-}
-
-bool String_EndsWith(const TCHAR *str, const TCHAR *end_str, bool ignore_case) {
-  ASSERT(end_str, (L""));
-  ASSERT(str, (L""));
-
-  int str_len = lstrlen(str);
-  int end_len = lstrlen(end_str);
-
-  // Definitely false if the suffix is longer than the string
-  if (end_len > str_len)
-    return false;
-
-  const TCHAR *str_ptr = str + str_len;
-  const TCHAR *end_ptr = end_str + end_len;
-
-  while (end_ptr >= end_str) {
-    // Check for matching characters
-    TCHAR c1 = *str_ptr;
-    TCHAR c2 = *end_ptr;
-
-    if (ignore_case) {
-      c1 = Char_ToLower(c1);
-      c2 = Char_ToLower(c2);
-    }
-
-    if (c1 != c2)
-      return false;
-
-    --str_ptr;
-    --end_ptr;
-  }
-
-  // if we haven't failed out, it must be ok!
-  return true;
-}
-
-CString String_MakeEndWith(const TCHAR* str, const TCHAR* end_str, bool ignore_case) {
-  if (String_EndsWith(str, end_str, ignore_case)) {
-    return str;
-  } else {
-    CString r(str);
-    r += end_str;
-    return r;
-  }
-}
-
-void RemoveFromEnd (CString & s, const TCHAR* remove) {
-  ASSERT(remove, (L""));
-
-  // If the suffix is shorter than the string, don't bother
-  int remove_len = lstrlen(remove);
-  if (s.GetLength() < remove_len) return;
-
-  // If the suffix is equal
-  int suffix_begin = s.GetLength() - remove_len;
-  if (0 == lstrcmp(s.GetString() + suffix_begin, remove))
-    s.Delete(suffix_begin, remove_len);
-}
-
-CString ElideIfNeeded (const CString & input_string, int max_len, int min_len) {
-  ASSERT (min_len <= max_len, (L""));
-  ASSERT (max_len >= TSTR_SIZE(kEllipsis)+1, (L""));
-  ASSERT (min_len >= TSTR_SIZE(kEllipsis)+1, (L""));
-
-  CString s = input_string;
-
-  s.TrimRight();
-  if (s.GetLength() > max_len) {
-    int truncate_at = max_len - TSTR_SIZE(kEllipsis);
-    // find first space going backwards from character one after the truncation point
-    while (truncate_at >= min_len && !IsSpace(s.GetAt(truncate_at)))
-      truncate_at--;
-
-    // skip the space(s)
-    while (truncate_at >= min_len && IsSpace(s.GetAt(truncate_at)))
-      truncate_at--;
-
-    truncate_at++;
-
-    if (truncate_at <= min_len || truncate_at > (max_len - static_cast<int>(TSTR_SIZE(kEllipsis)))) {
-      // we weren't able to break at a word boundary, may as well use more of the string
-      truncate_at = max_len - TSTR_SIZE(kEllipsis);
-
-      // skip space(s)
-      while (truncate_at > 0 && IsSpace(s.GetAt(truncate_at-1)))
-        truncate_at--;
-    }
-
-    s = s.Left(truncate_at);
-    s += kEllipsis;
-  }
-
-  UTIL_LOG(L6, (L"elide (%d %d) %s -> %s", min_len, max_len, input_string, s));
-  return s;
-}
-
-// these functions untested
-// UTF8 parameter supported on XP/2000 only
-HRESULT AnsiToUTF8 (char * src, int src_len, char * dest, int *dest_len) {
-  ASSERT (dest_len, (L""));
-  ASSERT (dest, (L""));
-  ASSERT (src, (L""));
-
-  // First use MultiByteToWideChar(CP_UTF8, ...) to convert to Unicode
-  // then use WideCharToMultiByte to convert from Unicode to UTF8
-  WCHAR *unicode = new WCHAR [(src_len + 1) * sizeof (TCHAR)]; ASSERT (unicode, (L""));
-  int chars_written = MultiByteToWideChar (CP_ACP, 0, src, src_len, unicode, src_len);
-  ASSERT (chars_written == src_len, (L""));
-  char *unmappable = " ";
-  BOOL unmappable_characters = false;
-  *dest_len = WideCharToMultiByte (CP_UTF8, 0, unicode, chars_written, dest, *dest_len, unmappable, &unmappable_characters);
-  delete [] unicode;
-  return S_OK;
-}
-
-// Convert Wide to ANSI directly. Use only when it is all ANSI
-CStringA WideToAnsiDirect(const CString & in) {
-  int in_len = in.GetLength();
-  const TCHAR * in_buf = static_cast<const TCHAR*>(in.GetString());
-
-  CStringA out;
-  unsigned char * out_buf = (unsigned char *)out.GetBufferSetLength(in_len);
-
-  for(int i = 0; i < in_len; ++i)
-    out_buf[i] = static_cast<unsigned char>(in_buf[i]);
-
-  out.ReleaseBuffer(in_len);
-  return out;
-}
-
-HRESULT UCS2ToUTF8 (LPCWSTR src, int src_len, char * dest, int *dest_len) {
-  ASSERT(dest_len, (L""));
-  ASSERT(dest, (L""));
-
-  *dest_len = WideCharToMultiByte (CP_UTF8, 0, src, src_len, dest, *dest_len, NULL,NULL);
-  return S_OK;
-}
-
-HRESULT UTF8ToUCS2 (const char * src, int src_len, LPWSTR dest, int *dest_len) {
-  ASSERT (dest_len, (L""));
-  ASSERT (src, (L""));
-
-  *dest_len = MultiByteToWideChar (CP_UTF8, 0, src, src_len, dest, *dest_len);
-  ASSERT (*dest_len == src_len, (L""));
-  return S_OK;
-}
-
-HRESULT UTF8ToAnsi (char * src, int, char * dest, int *dest_len) {
-  ASSERT(dest_len, (L""));
-  ASSERT(dest, (L""));
-  ASSERT(src, (L""));
-
-  src; dest; dest_len;  // unreferenced formal parameter
-
-  // First use MultiByteToWideChar(CP_UTF8, ...) to convert to Unicode
-  // then use WideCharToMultiByte to convert from Unicode to ANSI
-  return E_FAIL;
-}
-
-// clean up a string so it can be included within a JavaScript string
-// mainly involves escaping characters
-CString SanitizeString(const CString & in, DWORD mode) {
-  CString out(in);
-
-  if (mode & kSanHtml) {
-    // SKIP_LOC_BEGIN
-    ReplaceCString(out, _T("&"), _T("&amp;"));
-    ReplaceCString(out, _T("<"), _T("&lt;"));
-    ReplaceCString(out, _T(">"), _T("&gt;"));
-    // SKIP_LOC_END
-  }
-
-  if ((mode & kSanXml) == kSanXml) {
-    // SKIP_LOC_BEGIN
-    ReplaceCString(out, _T("'"), _T("&apos;"));
-    ReplaceCString(out, _T("\""), _T("&quot;"));
-    // SKIP_LOC_END
-  }
-
-  // Note that this SAN_JAVASCRIPT and kSanXml should not be used together.
-  ASSERT ((mode & (kSanJs | kSanXml)) != (kSanJs | kSanXml), (L""));
-
-  if ((mode & kSanJs) == kSanJs) {
-    // SKIP_LOC_BEGIN
-    ReplaceCString(out, _T("\\"), _T("\\\\"));
-    ReplaceCString(out, _T("\'"), _T("\\\'"));
-    ReplaceCString(out, _T("\""), _T("\\\""));
-    ReplaceCString(out, _T("\n"), _T(" "));
-    ReplaceCString(out, _T("\t"), _T(" "));
-    // SKIP_LOC_END
-  }
-
-  if ((mode & kSanHtmlInput) == kSanHtmlInput) {
-    // SKIP_LOC_BEGIN
-    ReplaceCString(out, _T("\""), _T("&quot;"));
-    ReplaceCString(out, _T("'"), _T("&#39;"));
-    // SKIP_LOC_END
-  }
-
-  return out;
-}
-
-// Bolds the periods used for abbreviation.  Call this after HighlightTerms.
-CString BoldAbbreviationPeriods(const CString & in) {
-  CString out(in);
-  CString abbrev;
-  for (int i = 0; i < kAbbreviationPeriodLength; ++i)
-    abbrev += _T(".");
-  ReplaceCString(out, abbrev, NOTRANSL(_T("<b>")) + abbrev + NOTRANSL(_T("</b>")));
-  return out;
-}
-
-// Unescape a escaped sequence leading by a percentage symbol '%',
-// and converted the unescaped sequence (in UTF8) into unicode.
-// Inputs:  src is the input string.
-//          pos is the starting position.
-// Returns: true if a EOS(null) char was encounted.
-//          out contains the unescaped and converted unicode string.
-//          consumed_length is how many bytes in the src string have been
-//          unescaped.
-// We can avoid the expensive UTF8 conversion step if there are no higher
-// ansi characters So if there aren't any, just convert it ANSI-to-WIDE
-// directly, which is cheaper.
-inline bool UnescapeSequence(const CString &src, int pos,
-                             CStringW *out, int *consumed_length) {
-  ASSERT1(out);
-  ASSERT1(consumed_length);
-
-  int length = src.GetLength();
-  // (input_len - pos) / 3 is enough for un-escaping the (%xx)+ sequences.
-  int max_dst_length = (length - pos) / 3;
-  scoped_array<char> unescaped(new char[max_dst_length]);
-  char *buf = unescaped.get();
-  if (buf == NULL) {  // no enough space ???
-    *consumed_length = 0;
-    return false;
-  }
-  char *dst = buf;
-  bool is_utf8 = false;
-  // It is possible that there is a null character '\0' in the sequence.
-  // Because the CStringT does't support '\0' in it, we stop
-  // parsing the input string when it is encounted.
-  bool eos_encounted = false;
-  uint8 ch;
-  int s = pos;
-  while (s + 2 < length && src[s] == '%' && !eos_encounted &&
-         ExtractChar(src, s + 1, &ch)) {
-    if (ch != 0)
-      *dst++ = ch;
-    else
-      eos_encounted = true;
-    if (ch >= 128)
-      is_utf8 = true;
-    s += 3;
-  }
-
-  ASSERT1(dst <= buf + max_dst_length);  // just to make sure
-
-  *consumed_length = s - pos;
-  if (is_utf8)
-    AnsiToWideString(buf, dst - buf, CP_UTF8, out);
-  else
-    *out = AnsiToWideString(buf, dst - buf);
-  return eos_encounted;
-}
-
-// There is an encoding called "URL-encoding". This function takes a URL-encoded string
-// and converts it back to the original representation
-// example: "?q=moon+doggy_%25%5E%26&" = "moon doggy_%^&"
-CString Unencode(const CString &input) {
-  const int input_len = input.GetLength();
-  const TCHAR *src = input.GetString();
-  // input_len is enough for containing the unencoded string.
-  CString out;
-  TCHAR *head = out.GetBuffer(input_len);
-  TCHAR *dst = head;
-  int s = 0;
-  bool eos_encounted = false;
-  bool is_utf8 = false;
-  CStringW fragment;
-  int consumed_length = 0;
-  while (s < input_len && !eos_encounted) {
-    switch (src[s]) {
-      case '+' :
-        *dst++ = ' ';
-        ASSERT1(dst <= head + input_len);
-        ++s;
-        break;
-      case '%' :
-        eos_encounted =
-          UnescapeSequence(input, s, &fragment, &consumed_length);
-        if (consumed_length > 0) {
-          s += consumed_length;
-          ASSERT1(dst + fragment.GetLength() <= head + input_len);
-          for (int i = 0; i < fragment.GetLength(); ++i)
-            *dst++ = fragment[i];
-        } else {
-          *dst++ = src[s++];
-          ASSERT1(dst <= head + input_len);
-        }
-        break;
-      default:
-        *dst++ = src[s];
-        ASSERT1(dst <= head + input_len);
-        ++s;
-    }
-  }
-  int out_len = dst - head;
-  out.ReleaseBuffer(out_len);
-  return out;
-}
-
-CString GetTextInbetween(const CString &input, const CString &start, const CString &end) {
-  int start_index = String_FindString(input, start);
-  if (start_index == -1)
-    return L"";
-
-  start_index += start.GetLength();
-  int end_index = String_FindString(input, end, start_index);
-  if (end_index == -1)
-    return L"";
-
-  return input.Mid(start_index, end_index - start_index);
-}
-
-// Given a string, get the parameter and url-unencode it
-CString GetParam(const CString & input, const CString & key) {
-  CString my_key(_T("?"));
-  my_key.Append(key);
-  my_key += L'=';
-
-  return Unencode(GetTextInbetween(input, my_key, NOTRANSL(L"?")));
-}
-
-// Get an xml-like field from a string
-CString GetField (const CString & input, const CString & field) {
-  CString start_field(NOTRANSL(_T("<")));
-  start_field += field;
-  start_field += L'>';
-
-  int32 start = String_FindString(input, start_field);
-  if (start == -1) { return _T(""); }
-  start += 2 + lstrlen (field);
-
-  CString end_field(NOTRANSL(_T("</")));
-  end_field += field;
-  end_field += L'>';
-
-  int32 end = String_FindString(input, end_field);
-  if (end == -1) { return _T(""); }
-
-  return input.Mid (start, end - start);
-}
-
-// ------------------------------------------------------------
-// Finds a whole word match in the query.
-// If the word has non-spaces either before or after, it will not qualify as
-//   a match.  i.e. "pie!" is not a match because of the exclamation point.
-// TODO(omaha): Add parameter that will consider punctuation acceptable.
-//
-// Optionally will look for a colon at the end.
-// If not found, return -1.
-int FindWholeWordMatch (const CString &query,
-  const CString &word_to_match,
-  const bool end_with_colon,
-  const int index_begin) {
-  if (word_to_match.IsEmpty()) {
-    return -1;
-  }
-
-  int index_word_begin = index_begin;
-
-  // Keep going until we find a whole word match, or the string ends.
-  do {
-    index_word_begin = String_FindString (query, word_to_match, index_word_begin);
-
-    if (-1 == index_word_begin) {
-      return index_word_begin;
-    }
-
-    // If it's not a whole word match, keep going.
-    if (index_word_begin > 0 &&
-      !IsSpaceW (query[index_word_begin - 1])) {
-      goto LoopEnd;
-    }
-
-    if (end_with_colon) {
-      int index_colon = String_FindChar (query, L':', index_word_begin);
-
-      // If there is no colon in the string, return now.
-      if (-1 == index_colon) {
-        return -1;
-      }
-
-      // If there is text between the end of the word and the colon, keep going.
-      if (index_colon - index_word_begin != word_to_match.GetLength()) {
-        goto LoopEnd;
-      }
-    } else {
-      // If there are more chars left after this word/phrase, and
-      // they are not spaces, return.
-      if (query.GetLength() > index_word_begin + word_to_match.GetLength() &&
-        !IsSpaceW (query.GetAt (index_word_begin + word_to_match.GetLength()))) {
-        goto LoopEnd;
-      }
-    }
-
-    // It fits all the requirements, so return the index to the beginning of the word.
-    return index_word_begin;
-
-LoopEnd:
-    ++index_word_begin;
-
-  } while (-1 != index_word_begin);
-
-  return index_word_begin;
-}
-
-// --------------------------------------------------------
-// Do whole-word replacement in "str".
-void ReplaceWholeWord (const CString &string_to_replace,
-  const CString &replacement,
-  const bool trim_whitespace,
-  CString *str) {
-  ASSERT (str, (L"ReplaceWholeWord"));
-
-  if (string_to_replace.IsEmpty() || str->IsEmpty()) {
-    return;
-  }
-
-  int index_str = 0;
-  do {
-    index_str = FindWholeWordMatch (*str, string_to_replace, false, index_str);
-
-    if (-1 != index_str) {
-      // Get the strings before and after, and trim whitespace.
-      CString str_before_word(str->Left (index_str));
-      if (trim_whitespace) {
-        str_before_word.TrimRight();
-      }
-
-      CString str_after_word(str->Mid (index_str + string_to_replace.GetLength()));
-      if (trim_whitespace) {
-        str_after_word.TrimLeft();
-      }
-
-      *str = str_before_word + replacement + str_after_word;
-      index_str += replacement.GetLength() + 1;
-    }
-  } while (index_str != -1);
-}
-
-// --------------------------------------------------------
-// Reverse (big-endian<->little-endian) the shorts that make up
-// Unicode characters in a byte array of Unicode chars
-HRESULT ReverseUnicodeByteOrder(byte* unicode_string, int size_in_bytes) {
-  ASSERT (unicode_string, (L""));
-
-  // If odd # of bytes, just leave the last one alone
-  for (int i = 0; i < size_in_bytes - 1; i += 2) {
-    byte b = unicode_string[i];
-    unicode_string[i] = unicode_string[i+1];
-    unicode_string[i+1] = b;
-  }
-
-  return S_OK;
-}
-
-// case insensitive strstr
-// adapted from http://c.snippets.org/snip_lister.php?fname=stristr.c
-const char *stristr(const char *string, const char *pattern)
-{
-  ASSERT (pattern, (L""));
-  ASSERT (string, (L""));
-  ASSERT (string && pattern, (L""));
-  char *pattern_ptr, *string_ptr;
-  const char *start;
-
-  for (start = string; *start != 0; start++)
-  {
-    // find start of pattern in string
-    for ( ; ((*start!=0) && (String_ToUpperA(*start) != String_ToUpperA(*pattern))); start++)
-     ;
-    if (0 == *start)
-     return NULL;
-
-    pattern_ptr = (char *)pattern;
-    string_ptr = (char *)start;
-
-    while (String_ToUpperA(*string_ptr) == String_ToUpperA(*pattern_ptr))
-    {
-      string_ptr++;
-      pattern_ptr++;
-
-      // if end of pattern then pattern was found
-      if (0 == *pattern_ptr)
-        return (start);
-    }
-  }
-
-  return NULL;
-}
-
-// case insensitive Unicode strstr
-// adapted from http://c.snippets.org/snip_lister.php?fname=stristr.c
-const WCHAR *stristrW(const WCHAR *string, const WCHAR *pattern)
-{
-  ASSERT (pattern, (L""));
-  ASSERT (string, (L""));
-  ASSERT (string && pattern, (L""));
-  const WCHAR *start;
-
-  for (start = string; *start != 0; start++)
-  {
-    // find start of pattern in string
-    for ( ; ((*start!=0) && (String_ToUpper(*start) != String_ToUpper(*pattern))); start++)
-     ;
-    if (0 == *start)
-     return NULL;
-
-    const WCHAR *pattern_ptr = pattern;
-    const WCHAR *string_ptr = start;
-
-    while (String_ToUpper(*string_ptr) == String_ToUpper(*pattern_ptr))
-    {
-      string_ptr++;
-      pattern_ptr++;
-
-      // if end of pattern then pattern was found
-      if (0 == *pattern_ptr)
-        return (start);
-    }
-  }
-
-  return NULL;
-}
-
-// case sensitive Unicode strstr
-// adapted from http://c.snippets.org/snip_lister.php?fname=stristr.c
-const WCHAR *strstrW(const WCHAR *string, const WCHAR *pattern)
-{
-  ASSERT (pattern, (L""));
-  ASSERT (string, (L""));
-  ASSERT (string && pattern, (L""));
-  const WCHAR *start;
-
-  for (start = string; *start != 0; start++)
-  {
-    // find start of pattern in string
-    for ( ; ((*start!=0) && (*start != *pattern)); start++)
-     ;
-    if (0 == *start)
-     return NULL;
-
-    const WCHAR *pattern_ptr = pattern;
-    const WCHAR *string_ptr = start;
-
-    while (*string_ptr == *pattern_ptr)
-    {
-      string_ptr++;
-      pattern_ptr++;
-
-      // if end of pattern then pattern was found
-      if (0 == *pattern_ptr)
-        return (start);
-    }
-  }
-
-  return NULL;
-}
-
-// -------------------------------------------------------------------------
-// Helper function
-float GetLenWithWordWrap (const float len_so_far,
-  const float len_to_add,
-  const uint32 len_line) {
-  // lint -save -e414  Possible division by 0
-  ASSERT (len_line != 0, (L""));
-
-  float len_total = len_so_far + len_to_add;
-
-  // Figure out if we need to word wrap by seeing if adding the second
-  // string will cause us to span more lines than before.
-  uint32 num_lines_before = static_cast<uint32> (len_so_far / len_line);
-  uint32 num_lines_after = static_cast<uint32> (len_total / len_line);
-
-  // If it just barely fit onto the line, do not wrap to the next line.
-  if (num_lines_after > 0 && (len_total / len_line - num_lines_after == 0)) {
-    --num_lines_after;
-  }
-
-  if (num_lines_after > num_lines_before)  {
-    // Need to word wrap.
-    // lint -e{790}  Suspicious truncation
-    return num_lines_after * len_line + len_to_add;
-  }
-  else
-    return len_total;
-
-  // lint -restore
-}
-
-int CalculateBase64EscapedLen(int input_len, bool do_padding) {
-  // these formulae were copied from comments that used to go with the base64
-  // encoding functions
-  int intermediate_result = 8 * input_len + 5;
-  ASSERT(intermediate_result > 0,(L""));     // make sure we didn't overflow
-  int len = intermediate_result / 6;
-  if (do_padding) len = ((len + 3) / 4) * 4;
-  return len;
-}
-
-// Base64Escape does padding, so this calculation includes padding.
-int CalculateBase64EscapedLen(int input_len) {
-  return CalculateBase64EscapedLen(input_len, true);
-}
-
-// Base64Escape
-//   Largely based on b2a_base64 in google/docid_encryption.c
-//
-//
-int Base64EscapeInternal(const char *src, int szsrc,
-                         char *dest, int szdest, const char *base64,
-                         bool do_padding)
-{
-  ASSERT(base64, (L""));
-  ASSERT(dest, (L""));
-  ASSERT(src, (L""));
-
-  static const char kPad64 = '=';
-
-  if (szsrc <= 0) return 0;
-
-  char *cur_dest = dest;
-  const unsigned char *cur_src = reinterpret_cast<const unsigned char*>(src);
-
-  // Three bytes of data encodes to four characters of cyphertext.
-  // So we can pump through three-byte chunks atomically.
-  while (szsrc > 2) { /* keep going until we have less than 24 bits */
-    if( (szdest -= 4) < 0 ) return 0;
-    cur_dest[0] = base64[cur_src[0] >> 2];
-    cur_dest[1] = base64[((cur_src[0] & 0x03) << 4) + (cur_src[1] >> 4)];
-    cur_dest[2] = base64[((cur_src[1] & 0x0f) << 2) + (cur_src[2] >> 6)];
-    cur_dest[3] = base64[cur_src[2] & 0x3f];
-
-    cur_dest += 4;
-    cur_src += 3;
-    szsrc -= 3;
-  }
-
-  /* now deal with the tail (<=2 bytes) */
-  switch (szsrc) {
-case 0:
-  // Nothing left; nothing more to do.
-  break;
-case 1:
-  // One byte left: this encodes to two characters, and (optionally)
-  // two pad characters to round out the four-character cypherblock.
-  if( (szdest -= 2) < 0 ) return 0;
-  cur_dest[0] = base64[cur_src[0] >> 2];
-  cur_dest[1] = base64[(cur_src[0] & 0x03) << 4];
-  cur_dest += 2;
-  if (do_padding) {
-    if( (szdest -= 2) < 0 ) return 0;
-    cur_dest[0] = kPad64;
-    cur_dest[1] = kPad64;
-    cur_dest += 2;
-  }
-  break;
-case 2:
-  // Two bytes left: this encodes to three characters, and (optionally)
-  // one pad character to round out the four-character cypherblock.
-  if( (szdest -= 3) < 0 ) return 0;
-  cur_dest[0] = base64[cur_src[0] >> 2];
-  cur_dest[1] = base64[((cur_src[0] & 0x03) << 4) + (cur_src[1] >> 4)];
-  cur_dest[2] = base64[(cur_src[1] & 0x0f) << 2];
-  cur_dest += 3;
-  if (do_padding) {
-    if( (szdest -= 1) < 0 ) return 0;
-    cur_dest[0] = kPad64;
-    cur_dest += 1;
-  }
-  break;
-default:
-  // Should not be reached: blocks of 3 bytes are handled
-  // in the while loop before this switch statement.
-  ASSERT(false, (L"Logic problem? szsrc = %S",szsrc));
-  break;
-  }
-  return (cur_dest - dest);
-}
-
-#define kBase64Chars  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
-
-#define kWebSafeBase64Chars "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
-
-int Base64Escape(const char *src, int szsrc, char *dest, int szdest) {
-  ASSERT(dest, (L""));
-  ASSERT(src, (L""));
-
-  return Base64EscapeInternal(src, szsrc, dest, szdest, kBase64Chars, true);
-}
-int WebSafeBase64Escape(const char *src, int szsrc, char *dest,
-  int szdest, bool do_padding) {
-    ASSERT(dest, (L""));
-    ASSERT(src, (L""));
-
-    return Base64EscapeInternal(src, szsrc, dest, szdest,
-      kWebSafeBase64Chars, do_padding);
-  }
-
-void Base64Escape(const char *src, int szsrc,
-                  CStringA* dest, bool do_padding)
-{
-  ASSERT(src, (L""));
-  ASSERT(dest,(L""));
-  const int max_escaped_size = CalculateBase64EscapedLen(szsrc, do_padding);
-  dest->Empty();
-  const int escaped_len = Base64EscapeInternal(src, szsrc,
-      dest->GetBufferSetLength(max_escaped_size + 1), max_escaped_size + 1,
-    kBase64Chars,
-    do_padding);
-  ASSERT(max_escaped_size <= escaped_len,(L""));
-  dest->ReleaseBuffer(escaped_len);
-}
-
-void WebSafeBase64Escape(const char *src, int szsrc,
-                         CStringA *dest, bool do_padding)
-{
-  ASSERT(src, (L""));
-  ASSERT(dest,(L""));
-  const int max_escaped_size =
-    CalculateBase64EscapedLen(szsrc, do_padding);
-  dest->Empty();
-  const int escaped_len = Base64EscapeInternal(src, szsrc,
-    dest->GetBufferSetLength(max_escaped_size + 1), max_escaped_size + 1,
-    kWebSafeBase64Chars,
-    do_padding);
-  ASSERT(max_escaped_size <= escaped_len,(L""));
-  dest->ReleaseBuffer(escaped_len);
-}
-
-void WebSafeBase64Escape(const CStringA& src, CStringA* dest) {
-  ASSERT(dest,(L""));
-  int encoded_len = CalculateBase64EscapedLen(src.GetLength());
-  scoped_array<char> buf(new char[encoded_len]);
-  int len = WebSafeBase64Escape(src,src.GetLength(), buf.get(), encoded_len, false);
-  dest->SetString(buf.get(), len);
-}
-
-// ----------------------------------------------------------------------
-// int Base64Unescape() - base64 decoder
-//
-// Check out
-// http://www.cis.ohio-state.edu/htbin/rfc/rfc2045.html for formal
-// description, but what we care about is that...
-//   Take the encoded stuff in groups of 4 characters and turn each
-//   character into a code 0 to 63 thus:
-//           A-Z map to 0 to 25
-//           a-z map to 26 to 51
-//           0-9 map to 52 to 61
-//           +(- for WebSafe) maps to 62
-//           /(_ for WebSafe) maps to 63
-//   There will be four numbers, all less than 64 which can be represented
-//   by a 6 digit binary number (aaaaaa, bbbbbb, cccccc, dddddd respectively).
-//   Arrange the 6 digit binary numbers into three bytes as such:
-//   aaaaaabb bbbbcccc ccdddddd
-//   Equals signs (one or two) are used at the end of the encoded block to
-//   indicate that the text was not an integer multiple of three bytes long.
-// ----------------------------------------------------------------------
-int Base64UnescapeInternal(const char *src, int len_src,
-                           char *dest, int len_dest, const char* unbase64) {
-  ASSERT (unbase64, (L""));
-  ASSERT (src, (L""));
-
-  static const char kPad64 = '=';
-
-  int decode;
-  int destidx = 0;
-  int state = 0;
-  // Used an unsigned char, since ch is used as an array index (into unbase64).
-  unsigned char ch = 0;
-  while (len_src-- && (ch = *src++) != '\0')  {
-    if (IsSpaceA(ch))  // Skip whitespace
-      continue;
-
-    if (ch == kPad64)
-      break;
-
-    decode = unbase64[ch];
-    if (decode == 99)  // A non-base64 character
-      return (-1);
-
-    // Four cyphertext characters decode to three bytes.
-    // Therefore we can be in one of four states.
-    switch (state) {
-      case 0:
-        // We're at the beginning of a four-character cyphertext block.
-        // This sets the high six bits of the first byte of the
-        // plaintext block.
-        if (dest) {
-          if (destidx >= len_dest)
-            return (-1);
-          // lint -e{734} Loss of precision
-          dest[destidx] = static_cast<char>(decode << 2);
-        }
-        state = 1;
-        break;
-      case 1:
-        // We're one character into a four-character cyphertext block.
-        // This sets the low two bits of the first plaintext byte,
-        // and the high four bits of the second plaintext byte.
-        // However, if this is the end of data, and those four
-        // bits are zero, it could be that those four bits are
-        // leftovers from the encoding of data that had a length
-        // of one mod three.
-        if (dest) {
-          if (destidx >= len_dest)
-            return (-1);
-          // lint -e{734} Loss of precision
-          dest[destidx]   |=  decode >> 4;
-          if (destidx + 1 >= len_dest) {
-            if (0 != (decode & 0x0f))
-              return (-1);
-            else
-              ;
-          } else {
-            // lint -e{734} Loss of precision
-            dest[destidx+1] = static_cast<char>((decode & 0x0f) << 4);
-          }
-        }
-        destidx++;
-        state = 2;
-        break;
-      case 2:
-        // We're two characters into a four-character cyphertext block.
-        // This sets the low four bits of the second plaintext
-        // byte, and the high two bits of the third plaintext byte.
-        // However, if this is the end of data, and those two
-        // bits are zero, it could be that those two bits are
-        // leftovers from the encoding of data that had a length
-        // of two mod three.
-        if (dest) {
-          if (destidx >= len_dest)
-            return (-1);
-          // lint -e{734} Loss of precision
-          dest[destidx]   |=  decode >> 2;
-          if (destidx +1 >= len_dest) {
-            if (0 != (decode & 0x03))
-              return (-1);
-            else
-              ;
-          } else {
-            // lint -e{734} Loss of precision
-            dest[destidx+1] = static_cast<char>((decode & 0x03) << 6);
-          }
-        }
-        destidx++;
-        state = 3;
-        break;
-      case 3:
-        // We're at the last character of a four-character cyphertext block.
-        // This sets the low six bits of the third plaintext byte.
-        if (dest) {
-          if (destidx >= len_dest)
-            return (-1);
-          // lint -e{734} Loss of precision
-          dest[destidx] |= decode;
-        }
-        destidx++;
-        state = 0;
-        break;
-
-    default:
-      ASSERT (false, (L""));
-      break;
-    }
-  }
-
-  // We are done decoding Base-64 chars.  Let's see if we ended
-  //      on a byte boundary, and/or with erroneous trailing characters.
-  if (ch == kPad64) {               // We got a pad char
-    if ((state == 0) || (state == 1))
-      return (-1);  // Invalid '=' in first or second position
-    if (len_src == 0) {
-      if (state == 2)  // We run out of input but we still need another '='
-        return (-1);
-      // Otherwise, we are in state 3 and only need this '='
-    } else {
-      if (state == 2) {  // need another '='
-        while ((ch = *src++) != '\0' && (len_src-- > 0)) {
-          if (!IsSpaceA(ch))
-            break;
-        }
-        if (ch != kPad64)
-          return (-1);
-      }
-      // state = 1 or 2, check if all remain padding is space
-      while ((ch = *src++) != '\0' && (len_src-- > 0)) {
-        if (!IsSpaceA(ch))
-          return(-1);
-      }
-    }
-  } else {
-    // We ended by seeing the end of the string.  Make sure we
-    //      have no partial bytes lying around.  Note that we
-    //      do not require trailing '=', so states 2 and 3 are okay too.
-    if (state == 1)
-      return (-1);
-  }
-
-  return (destidx);
-}
-
-int Base64Unescape(const char *src, int len_src, char *dest, int len_dest) {
-  ASSERT(dest, (L""));
-  ASSERT(src, (L""));
-
-  static const char UnBase64[] = {
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      62/*+*/, 99,      99,      99,      63/*/ */,
-     52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
-     60/*8*/, 61/*9*/, 99,      99,      99,      99,      99,      99,
-     99,       0/*A*/,  1/*B*/,  2/*C*/,  3/*D*/,  4/*E*/,  5/*F*/,  6/*G*/,
-      7/*H*/,  8/*I*/,  9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
-     15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
-     23/*X*/, 24/*Y*/, 25/*Z*/, 99,      99,      99,      99,      99,
-     99,      26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
-     33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
-     41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
-     49/*x*/, 50/*y*/, 51/*z*/, 99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99,
-     99,      99,      99,      99,      99,      99,      99,      99
-  };
-
-  // The above array was generated by the following code
-  // #include <sys/time.h>
-  // #include <stdlib.h>
-  // #include <string.h>
-  // main()
-  // {
-  //   static const char Base64[] =
-  //     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-  //   char *pos;
-  //   int idx, i, j;
-  //   printf("    ");
-  //   for (i = 0; i < 255; i += 8) {
-  //     for (j = i; j < i + 8; j++) {
-  //       pos = strchr(Base64, j);
-  //       if ((pos == NULL) || (j == 0))
-  //         idx = 99;
-  //       else
-  //         idx = pos - Base64;
-  //       if (idx == 99)
-  //         printf(" %2d,     ", idx);
-  //       else
-  //         printf(" %2d/*%c*/,", idx, j);
-  //     }
-  //     printf("\n    ");
-  //   }
-  // }
-
-  return Base64UnescapeInternal(src, len_src, dest, len_dest, UnBase64);
-}
-
-int WebSafeBase64Unescape(const char *src, int szsrc, char *dest, int szdest) {
-  ASSERT(dest, (L""));
-  ASSERT(src, (L""));
-
-  static const char UnBase64[] = {
-    99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      62/*-*/, 99,      99,
-      52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
-      60/*8*/, 61/*9*/, 99,      99,      99,      99,      99,      99,
-      99,       0/*A*/,  1/*B*/,  2/*C*/,  3/*D*/,  4/*E*/,  5/*F*/,  6/*G*/,
-      7/*H*/,  8/*I*/,  9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
-      15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
-      23/*X*/, 24/*Y*/, 25/*Z*/, 99,      99,      99,      99,      63/*_*/,
-      99,      26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
-      33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
-      41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
-      49/*x*/, 50/*y*/, 51/*z*/, 99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99,
-      99,      99,      99,      99,      99,      99,      99,      99
-  };
-  // The above array was generated by the following code
-  // #include <sys/time.h>
-  // #include <stdlib.h>
-  // #include <string.h>
-  // main()
-  // {
-  //   static const char Base64[] =
-  //     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
-  //   char *pos;
-  //   int idx, i, j;
-  //   printf("    ");
-  //   for (i = 0; i < 255; i += 8) {
-  //     for (j = i; j < i + 8; j++) {
-  //       pos = strchr(Base64, j);
-  //       if ((pos == NULL) || (j == 0))
-  //         idx = 99;
-  //       else
-  //         idx = pos - Base64;
-  //       if (idx == 99)
-  //         printf(" %2d,     ", idx);
-  //       else
-  //         printf(" %2d/*%c*/,", idx, j);
-  //     }
-  //     printf("\n    ");
-  //   }
-  // }
-
-  return Base64UnescapeInternal(src, szsrc, dest, szdest, UnBase64);
-}
-
-bool IsHexDigit (WCHAR c) {
-  return (((c >= L'a') && (c <= L'f'))
-     || ((c >= L'A') && (c <= L'F'))
-     || ((c >= L'0') && (c <= L'9')));
-}
-
-int HexDigitToInt (WCHAR c) {
-  return ((c >= L'a') ? ((c - L'a') + 10) :
-          (c >= L'A') ? ((c - L'A') + 10) :
-          (c - L'0'));
-}
-
-// ----------------------------------------------------------------------
-// int QuotedPrintableUnescape()
-//
-// Check out http://www.cis.ohio-state.edu/htbin/rfc/rfc2045.html for
-// more details, only briefly implemented. But from the web...
-// Quoted-printable is an encoding method defined in the MIME
-// standard. It is used primarily to encode 8-bit text (such as text
-// that includes foreign characters) into 7-bit US ASCII, creating a
-// document that is mostly readable by humans, even in its encoded
-// form. All MIME compliant applications can decode quoted-printable
-// text, though they may not necessarily be able to properly display the
-// document as it was originally intended. As quoted-printable encoding
-// is implemented most commonly, printable ASCII characters (values 33
-// through 126, excluding 61), tabs and spaces that do not appear at the
-// end of lines, and end-of-line characters are not encoded. Other
-// characters are represented by an equal sign (=) immediately followed
-// by that character's hexadecimal value. Lines that are longer than 76
-// characters are shortened by line breaks, with the equal sign marking
-// where the breaks occurred.
-//
-// Update: we really want QuotedPrintableUnescape to conform to rfc2047,
-// which expands the q encoding. In particular, it specifices that _'s are
-// to be treated as spaces.
-// ----------------------------------------------------------------------
-int QuotedPrintableUnescape(const WCHAR *source, int slen,
-                            WCHAR *dest, int len_dest) {
-  ASSERT(dest, (L""));
-  ASSERT(source, (L""));
-
-  WCHAR* d = dest;
-  const WCHAR* p = source;
-
-  while (*p != '\0' && p < source+slen && d < dest+len_dest) {
-    switch (*p) {
-      case '=':
-        if (p == source+slen-1) {
-          // End of line, no need to print the =..
-          return (d-dest);
-        }
-        // if its valid, convert to hex and insert
-        if (p < source+slen-2 && IsHexDigit(p[1]) && IsHexDigit(p[2])) {
-          // lint -e{734} Loss of precision
-          *d++ = static_cast<WCHAR>(
-                    HexDigitToInt(p[1]) * 16 + HexDigitToInt(p[2]));
-          p += 3;
-        } else {
-          p++;
-        }
-        break;
-      case '_':   // According to rfc2047, _'s are to be treated as spaces
-        *d++ = ' '; p++;
-        break;
-      default:
-        *d++ = *p++;
-        break;
-    }
-  }
-  return (d-dest);
-}
-
-// TODO(omaha): currently set not to use IsCharUpper because that is relatively slow
-// this is used in the QUIB; consider if we need to use IsCharUpper or a replacement
-bool String_IsUpper(TCHAR c) {
-  return (c >= 'A' && c <= 'Z');
-  // return (IsCharUpper (c));
-}
-
-// Replacement for the CRT toupper(c)
-int String_ToUpper(int c) {
-  // If it's < 128, then convert is ourself, which is far cheaper than the system conversion
-  if (c < 128)
-    return String_ToUpperA(static_cast<char>(c));
-
-  TCHAR * p_c = reinterpret_cast<TCHAR *>(c);
-  int conv_c = reinterpret_cast<int>(::CharUpper(p_c));
-  return conv_c;
-}
-
-// Replacement for the CRT toupper(c)
-char String_ToUpperA(char c) {
-  if (c >= 'a' && c <= 'z') return (c - ('a' - 'A'));
-  return c;
-}
-
-void String_ToLower(TCHAR* str) {
-  ASSERT1(str);
-  ::CharLower(str);
-}
-
-void String_ToUpper(TCHAR* str) {
-  ASSERT1(str);
-  ::CharUpper(str);
-}
-
-// String comparison based on length
-// Replacement for the CRT strncmp(i)
-int String_StrNCmp(const TCHAR * str1, const TCHAR * str2, uint32 len, bool ignore_case) {
-  ASSERT(str2, (L""));
-  ASSERT(str1, (L""));
-
-  TCHAR c1, c2;
-
-  if (len == 0)
-    return 0;
-
-  // compare each char
-  // TODO(omaha): If we use a lot of case sensitive compares consider having 2 loops.
-  do {
-    c1 = *str1++;
-    c2 = *str2++;
-    if (ignore_case) {
-      c1 = (TCHAR)String_ToLowerChar((int)(c1));  // lint !e507  Suspicious truncation
-      c2 = (TCHAR)String_ToLowerChar((int)(c2));  // lint !e507
-    }
-  } while ( (--len) && c1 && (c1 == c2) );
-
-  return (int)(c1 - c2);
-}
-
-// TODO(omaha): Why do we introduce this behaviorial difference?
-// Replacement for strncpy() - except ALWAYS ends string with null
-TCHAR* String_StrNCpy(TCHAR* destination, const TCHAR* source, uint32 len) {
-  ASSERT (source, (L""));
-  ASSERT (destination, (L""));
-
-  TCHAR* result = destination;
-
-  ASSERT (0 != len, (L""));     // Too short a destination for even the null character
-
-  while (*source && len) {
-    *destination++ = *source++;
-    len--;
-  }
-
-  // If we ran out of space, back up one
-  if (0 == len) {
-    destination--;
-  }
-
-  // Null-terminate the string
-  *destination = _T('\0');
-
-  return result;
-}
-
-// check if a string starts with another string
-bool String_StartsWith(const TCHAR *str, const TCHAR *start_str,
-                            bool ignore_case) {
-  ASSERT(start_str, (L""));
-  ASSERT(str, (L""));
-
-  while (0 != *str) {
-    // Check for matching characters
-    TCHAR c1 = *str;
-    TCHAR c2 = *start_str;
-
-    // Reached the end of start_str?
-    if (0 == c2)
-      return true;
-
-    if (ignore_case) {
-      c1 = (TCHAR)String_ToLowerChar((int)(c1));  // lint !e507  Suspicious truncation
-      c2 = (TCHAR)String_ToLowerChar((int)(c2));  // lint !e507  Suspicious truncation
-    }
-
-    if (c1 != c2)
-      return false;
-
-    ++str;
-    ++start_str;
-  }
-
-  // If str is shorter than start_str, no match.  If equal size, match.
-  return 0 == *start_str;
-}
-
-// check if a string starts with another string
-bool String_StartsWithA(const char *str, const char *start_str, bool ignore_case) {
-  ASSERT(start_str, (L""));
-  ASSERT(str, (L""));
-
-  while (0 != *str) {
-    // Check for matching characters
-    char c1 = *str;
-    char c2 = *start_str;
-
-    // Reached the end of start_str?
-    if (0 == c2)
-      return true;
-
-    if (ignore_case) {
-      c1 = String_ToLowerCharAnsi(c1);
-      c2 = String_ToLowerCharAnsi(c2);
-    }
-
-    if (c1 != c2)
-      return false;
-
-    ++str;
-    ++start_str;
-  }
-
-  // If str is shorter than start_str, no match.  If equal size, match.
-  return 0 == *start_str;
-}
-
-// the wrapper version below actually increased code size as of 5/31/04
-// perhaps because the int64 version is larger and in some EXE/DLLs we only need the int32 version
-
-// converts a string to an int
-// Does not check for overflow
-// is the direct int32 version significantly faster for our usage?
-// int32 String_StringToInt(const TCHAR * str) {
-//    ASSERT(str, (L""));
-//    return static_cast<int32>(String_StringToInt64 (str));
-// }
-
-// converts a string to an int
-// Does not check for overflow
-int32 String_StringToInt(const TCHAR * str) {
-  ASSERT(str, (L""));
-
-  int c;              // current char
-  int32 total;         // current total
-  int sign;           // if '-', then negative, otherwise positive
-
-  // remove spaces
-  while ( *str == _T(' '))
-      ++str;
-
-  c = (int)*str++;
-  sign = c;           // save sign indication
-  if (c == _T('-') || c == _T('+'))
-      c = (int)*str++;    // skip sign
-
-  total = 0;
-
-  while ((c = String_CharToDigit(static_cast<TCHAR>(c))) != -1 ) {
-      total = 10 * total + c;     // accumulate digit
-      c = *str++;    // get next char
-  }
-
-  if (sign == '-')
-    return -total;
-  else
-    return total;   // return result, negated if necessary
-}
-
-// converts a string to an int64
-// Does not check for overflow
-int64 String_StringToInt64(const TCHAR * str) {
-  ASSERT(str, (L""));
-
-  int c;  // current char
-  int64 total;  // current total
-  int sign;
-
-  while (*str == ' ') ++str;  // skip space
-
-  c = (int)*str++;
-  sign = c;           /* save sign indication */
-  if (c == '-' || c == '+')
-    c = (int)*str++;
-
-  total = 0;
-
-  while ((c = String_CharToDigit(static_cast<TCHAR>(c))) != -1) {
-    total = 10 * total + c;     /* accumulate digit */
-    c = *str++;    /* get next char */
-  }
-
-  if (sign == '-')
-    return -total;
-  else
-    return total;
-}
-
-// A faster version of the ::CharLower command. We first check if all characters are in low ANSI
-// If so, we can convert it ourselves [which is about 10x faster]
-// Otherwise, ask the system to do it for us.
-TCHAR * String_FastToLower(TCHAR * str) {
-  ASSERT(str, (L""));
-
-  TCHAR * p = str;
-  while (*p) {
-    // If we can't process it ourselves, then do it with the API
-    if (*p > 127)
-      return ::CharLower(str);
-    ++p;
-  }
-
-  // If we're still here, do it ourselves
-  p = str;
-  while (*p) {
-    // Lower case it
-    if (*p >= L'A' && *p <= 'Z')
-      *p |= 0x20;
-    ++p;
-  }
-
-  return str;
-}
-
-// Convert a size_t to a CString
-CString sizet_to_str(const size_t & i) {
-  CString out;
-  out.Format(NOTRANSL(_T("%u")),i);
-  return out;
-}
-
-// Convert an int to a CString
-CString itostr(const int i) {
-  return String_Int64ToString(i, 10);
-}
-
-// Convert a uint to a CString
-CString itostr(const uint32 i) {
-  return String_Int64ToString(i, 10);
-}
-
-// converts an int to a string
-// Does not check for overflow
-CString String_Int64ToString(int64 value, int radix) {
-  ASSERT(radix > 0, (L""));
-
-  // Space big enough for it in binary, plus the sign
-  TCHAR temp[66];
-
-  bool negative = false;
-  if (value < 0) {
-    negative = true;
-    value = -value;
-  }
-
-  int pos = 0;
-
-  // Add digits in reverse order
-  do {
-    TCHAR digit = (TCHAR) (value % radix);
-    if (digit > 9)
-      temp[pos] = L'a' + digit - 10;
-    else
-      temp[pos] = L'0' + digit;
-
-    pos++;
-    value /= radix;
-  } while (value > 0);
-
-  if (negative)
-    temp[pos++] = L'-';
-
-  // Reverse it before making a CString out of it
-  int start = 0, end = pos - 1;
-  while (start < end) {
-    TCHAR t = temp[start];
-    temp[start] = temp[end];
-    temp[end] = t;
-
-    end--;
-    start++;
-  }
-
-  return CString(temp, pos);
-}
-
-// converts an uint64 to a string
-// Does not check for overflow
-CString String_Uint64ToString(uint64 value, int radix) {
-  ASSERT1(radix > 0);
-
-  CString ret;
-
-  const uint32 kMaxUint64Digits = 65;
-
-  // Space big enough for it in binary
-  TCHAR* temp = ret.GetBufferSetLength(kMaxUint64Digits);
-
-  int pos = 0;
-
-  // Add digits in reverse order
-  do {
-    TCHAR digit = static_cast<TCHAR>(value % radix);
-    if (digit > 9) {
-      temp[pos] = _T('a') + digit - 10;
-    } else {
-      temp[pos] = _T('0') + digit;
-    }
-
-    pos++;
-    value /= radix;
-  } while (value > 0 && pos < kMaxUint64Digits);
-
-  ret.ReleaseBuffer(pos);
-
-  // Reverse it before making a CString out of it
-  ret.MakeReverse();
-
-  return ret;
-}
-
-// converts an double to a string specifies the number of digits after
-// the decimal point
-CString String_DoubleToString(double value, int point_digits) {
-  int64 int_val = (int64) value;
-
-  // Deal with integer part
-  CString result(String_Int64ToString(int_val, 10));
-
-  if (point_digits > 0) {
-    result.AppendChar(L'.');
-
-    // get the fp digits
-    double rem_val = value - int_val;
-    if (rem_val < 0)
-      rem_val = -rem_val;
-
-    // multiply w/ the requested number of significant digits
-    // construct the string in place
-    for(int i=0; i<point_digits; i++) {
-      // TODO(omaha): I have seen 1.2 turn into 1.1999999999999, and generate that string.
-      // We should round better. For now, I'll add a quick fix to favor high
-      rem_val += 1e-12;
-      rem_val *= 10;
-      // Get the ones digit
-      int64 int_rem_dig = std::min(10LL, static_cast<int64>(rem_val));
-      result += static_cast<TCHAR>(int_rem_dig + L'0');
-      rem_val = rem_val - int_rem_dig;
-    }
-  }
-
-  return result;
-}
-
-double String_StringToDouble (const TCHAR *s) {
-  ASSERT(s, (L""));
-
-  double value, power;
-  int i = 0, sign;
-
-  while (IsSpaceW(s[i])) i++;
-
-  // get sign
-  sign = (s[i] == '-') ? -1 : 1;
-  if (s[i] == '+' || s[i] == '-') i++;
-
-  for (value = 0.0; s[i] >= '0' && s[i] <= '9'; i++)
-    value = 10.0 * value + (s[i] - '0');
-
-  if (s[i] == '.') i++;
-
-  for (power = 1.0; s[i] >= '0' && s[i] <= '9'; i++) {
-    value = 10.0 * value + (s[i] - '0');
-    power *= 10.0;
-  }
-
-  return sign * value / power;
-}
-
-// Converts a character to a digit
-// if the character is not a digit return -1 (same as CRT)
-int32 String_CharToDigit(const TCHAR c) {
-  return ((c) >= '0' && (c) <= '9' ? (c) - '0' : -1);
-}
-
-bool String_IsDigit (const TCHAR c) {
-  return ((c) >= '0' && (c) <= '9');
-}
-
-TCHAR String_DigitToChar(unsigned int n) {
-  ASSERT1(n < 10);
-  return static_cast<TCHAR>(_T('0') + n % 10);
-}
-
-// Returns true if an identifier character: letter, digit, or "_"
-bool String_IsIdentifierChar(const TCHAR c) {
-  return ((c >= _T('A') && c <= _T('Z')) ||
-          (c >= _T('a') && c <= _T('z')) ||
-          (c >= _T('0') && c <= _T('9')) ||
-          c == _T('_'));
-}
-
-// Returns true if the string has letters in it.
-// This is used by the keyword extractor to downweight numbers,
-// IDs (sequences of numbers like social security numbers), etc.
-bool String_HasAlphabetLetters (const TCHAR * str) {
-  ASSERT (str, (L""));
-
-  while (*str != '\0') {
-    // if (iswalpha (*str)) {
-    // Note that IsCharAlpha is slower but we want to avoid the CRT
-    if (IsCharAlpha (*str)) {
-      return true;
-    }
-    ++str;
-  }
-
-  return false;
-}
-
-CString String_LargeIntToApproximateString(uint64 value, bool base_ten, int* power) {
-  uint32 to_one_decimal;
-
-  uint32 gig         = base_ten ? 1000000000 : (1<<30);
-  uint32 gig_div_10  = base_ten ?  100000000 : (1<<30)/10;
-  uint32 meg         = base_ten ?    1000000 : (1<<20);
-  uint32 meg_div_10  = base_ten ?     100000 : (1<<20)/10;
-  uint32 kilo        = base_ten ?       1000 : (1<<10);
-  uint32 kilo_div_10 = base_ten ?        100 : (1<<10)/10;
-
-  if (value >= gig) {
-    if (power) *power = 3;
-    to_one_decimal = static_cast<uint32>(value / gig_div_10);
-  } else if (value >= meg) {
-    if (power) *power = 2;
-    to_one_decimal = static_cast<uint32>(value / meg_div_10);
-  } else if (value >= kilo) {
-    if (power) *power = 1;
-    to_one_decimal = static_cast<uint32>(value / kilo_div_10);
-  } else {
-    if (power) *power = 0;
-    return String_Int64ToString(static_cast<uint32>(value), 10 /*radix*/);
-  }
-
-  uint32 whole_part = to_one_decimal / 10;
-
-  if (whole_part < 10)
-    return Show(0.1 * static_cast<double>(to_one_decimal), 1);
-
-  return String_Int64ToString(whole_part, 10 /*radix*/);
-}
-
-int String_FindString(const TCHAR *s1, const TCHAR *s2) {
-  ASSERT(s2, (L""));
-  ASSERT(s1, (L""));
-
-  // Naive implementation, but still oodles better than ATL's implementation
-  // (which deals with variable character widths---we don't).
-
-  const TCHAR *found = _tcsstr(s1, s2);
-  if (NULL == found)
-    return -1;
-
-  return found - s1;
-}
-
-int String_FindString(const TCHAR *s1, const TCHAR *s2, int start_pos) {
-  ASSERT(s2, (L""));
-  ASSERT(s1, (L""));
-
-  // Naive implementation, but still oodles better than ATL's implementation
-  // (which deals with variable character widths---we don't).
-
-  int skip = start_pos;
-
-  const TCHAR *s = s1;
-  while (skip && *s) {
-    ++s;
-    --skip;
-  }
-  if (!(*s))
-    return -1;
-
-  const TCHAR *found = _tcsstr(s, s2);
-  if (NULL == found)
-    return -1;
-
-  return found - s1;
-}
-
-int String_FindChar(const TCHAR *str, const TCHAR c) {
-  ASSERT (str, (L""));
-  const TCHAR *s = str;
-  while (*s) {
-    if (*s == c)
-      return s - str;
-    ++s;
-  }
-
-  return -1;
-}
-
-// taken from wcsrchr, modified to behave in the CString way
-int String_ReverseFindChar(const TCHAR * str,TCHAR c) {
-  ASSERT (str, (L""));
-  TCHAR *start = (TCHAR *)str;
-
-  while (*str++)                       /* find end of string */
-    ;
-  /* search towards front */
-  while (--str != start && *str != (TCHAR)c)
-    ;
-
-  if (*str == (TCHAR)c)             /* found ? */
-    return( str - start );
-
-  return -1;
-}
-
-int String_FindChar(const TCHAR *str, const TCHAR c, int start_pos) {
-  ASSERT (str, (L""));
-  int n = 0;
-  const TCHAR *s = str;
-  while (*s) {
-    if (n++ >= start_pos && *s == c)
-      return s - str;
-    ++s;
-  }
-
-  return -1;
-}
-
-bool String_Contains(const TCHAR *s1, const TCHAR *s2) {
-  ASSERT(s2, (L""));
-  ASSERT(s1, (L""));
-
-  return -1 != String_FindString(s1, s2);
-}
-
-void String_ReplaceChar(TCHAR *str, TCHAR old_char, TCHAR new_char) {
-  ASSERT (str, (L""));
-  while (*str) {
-    if (*str == old_char)
-      *str = new_char;
-
-    ++str;
-  }
-}
-
-void String_ReplaceChar(CString & str, TCHAR old_char, TCHAR new_char) {
-  String_ReplaceChar (str.GetBuffer(), old_char, new_char);
-  str.ReleaseBuffer();
-}
-
-int ReplaceCString (CString & src, const TCHAR *from, const TCHAR *to) {
-  ASSERT(to, (L""));
-  ASSERT(from, (L""));
-
-  return ReplaceCString(src, from, lstrlen(from), to, lstrlen(to), kRepMax);
-}
-
-// A special version of the replace function which takes advantage of CString properties
-// to make it much faster when the string grows
-// 1) It will resize the string in place if possible. Even if it has to 'grow' the string
-// 2) It will cutoff after a maximum number of matches
-// 3) It expects sizing data to be passed to it
-int ReplaceCString (CString & src, const TCHAR *from, unsigned int from_len,
-                                   const TCHAR *to, unsigned int to_len,
-                                   unsigned int max_matches) {
-  ASSERT (from, (L""));
-  ASSERT (to, (L""));
-  ASSERT (from[0] != '\0', (L""));
-  int i = 0, j = 0;
-  unsigned int matches = 0;
-
-  // Keep track of the matches, it's easier than recalculating them
-  unsigned int match_pos_stack[kExpectedMaxReplaceMatches];
-
-  // We might need to dynamically allocate space for the matches
-  bool dynamic_allocate = false;
-  unsigned int * match_pos = (unsigned int*)match_pos_stack;
-  unsigned int max_match_size = kExpectedMaxReplaceMatches;
-
-  // Is the string getting bigger?
-  bool longer = to_len > from_len;
-
-  // don't compute the lengths unless we know we need to
-  int src_len = src.GetLength();
-  int cur_len = src_len;
-
-  // Trick: We temporarily add 1 extra character to the string. The first char from the from
-  // string. This way we can avoid searching for NULL, since we are guaranteed to find it
-  TCHAR * buffer = src.GetBufferSetLength(src_len+1);
-  const TCHAR from_0 = from[0];
-  buffer[src_len] = from[0];
-
-  while (i < cur_len) {
-    // If we have too many matches, then re-allocate to a dynamic buffer that is
-    // twice as big as the one we are currently using
-    if (longer && (matches == max_match_size)) {
-      // Double the buffer size, and copy it over
-      unsigned int * temp = new unsigned int[max_match_size * 2];
-      memcpy(temp, match_pos, matches * sizeof(unsigned int));
-      if (dynamic_allocate)
-        delete [] match_pos;  // lint !e424  Inappropriate deallocation
-      match_pos = temp;
-
-      max_match_size *= 2;
-      dynamic_allocate = true;
-    }
-
-    // If we have the maximum number of matches already, then stop
-    if (matches >= max_matches) {
-      break;
-    }
-
-    // For each potential match
-    // Note: oddly enough, this is the most expensive line in the function under normal usage. So I am optimizing the heck out of it
-    TCHAR * buf_ptr = buffer + i;
-    while (*buf_ptr != from_0) { ++buf_ptr; }
-    i = buf_ptr - buffer;
-
-    // We're done!
-    if (i >= cur_len)
-      break;
-
-    // buffer is not NULL terminated, we replaced the NULL above
-    while (i < cur_len && buffer[i] && buffer[i] == from[j]) {
-      ++i; ++j;
-      if (from[j] == '\0') {  // found match
-
-        if (!longer) {  // modify in place
-
-          memcpy ((byte *)(buffer+i) - (sizeof (TCHAR) * from_len), (byte *)to, sizeof (TCHAR) * to_len);
-          // if there are often a lot of replacements, it would be faster to create a new string instead
-          // of using memmove
-
-          // TODO(omaha): - memmove will cause n^2 behavior in strings with multiple matches since it will be moved many times...
-          if (to_len < from_len) { memmove ((byte *)(buffer+i) - (sizeof (TCHAR) * (from_len - to_len)),
-                                          (byte *)(buffer+i), (src_len - i + 1) * sizeof (TCHAR)); }
-
-          i -= (from_len - to_len);
-          cur_len -= (from_len - to_len);
-        }
-        else
-          match_pos[matches] = i - from_len;
-
-        ++matches;
-
-        break;
-      }
-    }
-
-    j = 0;
-  }
-
-  if (to_len <= from_len)
-    src_len -= matches * (from_len - to_len);
-
-  // if the new string is longer we do another pass now that we know how long the new string needs to be
-  if (matches && to_len > from_len) {
-    src.ReleaseBuffer(src_len);
-
-    int new_len = src_len + matches * (to_len - from_len);
-    buffer = src.GetBufferSetLength(new_len);
-
-    // It's easier to assemble it backwards...
-    int temp_end = new_len;
-    for(i = matches-1; i >= 0; --i) {
-      // Figure out where the trailing portion isthe trailing portion
-      int len = src_len - match_pos[i] - from_len;
-      int start  = match_pos[i] + from_len;
-      int dest   = temp_end - len;
-      memmove(buffer+dest, buffer+start, (len) * sizeof(TCHAR));
-
-      // copy the new item
-      memcpy(buffer + dest - to_len, to, to_len * sizeof(TCHAR));
-
-      // Update the pointers
-      temp_end = dest - to_len;
-      src_len = match_pos[i];
-
-    }
-    src_len = new_len;
-  }
-
-  src.ReleaseBuffer(src_len);
-  if (dynamic_allocate)
-    delete [] match_pos;  // lint !e673  Possibly inappropriate deallocation
-
-  return matches;
-}
-
-/*
-   The following 2 functions will do replacement on TCHAR* directly. They is currently unused.
-   Feel free to put it back if you need to.
-*/
-int ReplaceString (TCHAR *src, const TCHAR *from, const TCHAR *to, TCHAR **out, int *out_len) {
-  ASSERT(out_len, (L""));
-  ASSERT(out, (L""));
-  ASSERT(to, (L""));
-  ASSERT(from, (L""));
-  ASSERT(src, (L""));
-
-  bool created_new_string;
-  int matches = ReplaceStringMaybeInPlace (src, from, to, out, out_len, &created_new_string);
-  if (!created_new_string) {
-      *out = new TCHAR [(*out_len)+1];
-      if (!(*out)) { *out = src; return 0; }
-      _tcscpy_s(*out, *out_len + 1, src);
-  }
-
-  return matches;
-}
-
-int ReplaceStringMaybeInPlace (TCHAR *src, const TCHAR *from, const TCHAR *to, TCHAR **out, int *out_len, bool *created_new_string) {
-  ASSERT (created_new_string, (L""));
-  ASSERT (out_len, (L""));
-  ASSERT (src, (L""));
-  ASSERT (from, (L""));
-  ASSERT (to, (L""));
-  ASSERT (out, (L""));
-  ASSERT (from[0] != '\0', (L""));
-  int i = 0, j = 0;
-  int matches = 0;
-
-  // don't compute the lengths unless we know we need to
-  int from_len = -1, to_len = -1, src_len = -1;
-
-  *created_new_string = false;
-  *out = src;
-
-  while (src[i]) {
-    while (src[i] && src[i] != from[0]) { i++; }
-    while (src[i] && src[i] == from[j]) {
-      i++; j++;
-      if (from[j] == '\0') {  // found match
-        if (from_len == -1) {  // compute lengths if not known
-          from_len = lstrlen (from);
-          to_len = lstrlen (to);
-          src_len = lstrlen (src);
-        }
-
-        matches++;
-
-        if (to_len <= from_len) {  // modify in place
-          memcpy ((byte *)(src+i) - (sizeof (TCHAR) * from_len), (byte *)to, sizeof (TCHAR) * to_len);
-          // if there are often a lot of replacements, it would be faster to create a new string instead
-          // of using memmove
-          if (to_len < from_len) { memmove ((byte *)(src+i) - (sizeof (TCHAR) * (from_len - to_len)),
-                                            (byte *)(src+i), (src_len - i + 1) * sizeof (TCHAR)); }
-          i -= (from_len - to_len);
-        }
-
-        break;
-      }
-    }
-
-    j = 0;
-  }
-
-  *out_len = i;
-
-  // if the new string is longer we do another pass now that we know how long the new string needs to be
-  if (matches && to_len > from_len) {
-      ASSERT (src_len == i, (L""));
-      int new_len = src_len + matches * (to_len - from_len);
-      *out = new TCHAR [new_len+1];
-      if (!(*out)) { *out = src; *out_len = lstrlen (src); return 0; }
-      *created_new_string = true;
-      i = 0; j = 0; int k = 0;
-
-      while (src[i]) {
-          while (src[i] && src[i] != from[0]) {
-              (*out)[k++] = src[i++];
-          }
-          while (src[i] && src[i] == from[j]) {
-              (*out)[k++] = src[i++];
-              j++;
-
-              if (from[j] == '\0') {  // found match
-                  k -= from_len;
-                  ASSERT (k >= 0, (L""));
-                  memcpy ((byte *)((*out)+k), (byte *)to, sizeof (TCHAR) * to_len);
-                  k += to_len;
-                  break;
-              }
-          }
-
-          j = 0;
-      }
-
-      (*out)[k] = '\0';
-      ASSERT (k == new_len, (L""));
-      *out_len = new_len;
-  }
-
-  return matches;
-}
-
-/****************************************************************************
-* wcstol, wcstoul(nptr,endptr,ibase) - Convert ascii string to long un/signed int.
-*
-* modified from:
-*
-* wcstol.c - Contains C runtimes wcstol and wcstoul
-*
-*       Copyright (c) Microsoft Corporation. All rights reserved.
-*
-*   Purpose:
-*       Convert an ascii string to a long 32-bit value.  The base
-*       used for the caculations is supplied by the caller.  The base
-*       must be in the range 0, 2-36.  If a base of 0 is supplied, the
-*       ascii string must be examined to determine the base of the
-*       number:
-*           (a) First char = '0', second char = 'x' or 'X',
-*               use base 16.
-*           (b) First char = '0', use base 8
-*           (c) First char in range '1' - '9', use base 10.
-*
-*       If the 'endptr' value is non-NULL, then wcstol/wcstoul places
-*       a pointer to the terminating character in this value.
-*       See ANSI standard for details
-*
-*Entry:
-*       nptr == NEAR/FAR pointer to the start of string.
-*       endptr == NEAR/FAR pointer to the end of the string.
-*       ibase == integer base to use for the calculations.
-*
-*       string format: [whitespace] [sign] [0] [x] [digits/letters]
-*
-*Exit:
-*       Good return:
-*           result
-*
-*       Overflow return:
-*           wcstol -- LONG_MAX or LONG_MIN
-*           wcstoul -- ULONG_MAX
-*           wcstol/wcstoul -- errno == ERANGE
-*
-*       No digits or bad base return:
-*           0
-*           endptr = nptr*
-*
-*Exceptions:
-*       None.
-*
-*******************************************************************************/
-
-// flag values */
-#define kFlUnsigned   (1)       // wcstoul called */
-#define kFlNeg        (2)       // negative sign found */
-#define kFlOverflow   (4)       // overflow occured */
-#define kFlReaddigit  (8)       // we've read at least one correct digit */
-
-static unsigned long __cdecl wcstoxl (const wchar_t *nptr, wchar_t **endptr, int ibase, int flags) {
-  ASSERT(nptr, (L""));
-
-  const wchar_t *p;
-  wchar_t c;
-  unsigned long number;
-  unsigned digval;
-  unsigned long maxval;
-  // #ifdef _MT
-  // pthreadlocinfo ptloci = _getptd()->ptlocinfo;
-
-  // if ( ptloci != __ptlocinfo )
-  //    ptloci = __updatetlocinfo();
-  // #endif  // _MT */
-
-  p = nptr;           // p is our scanning pointer */
-  number = 0;         // start with zero */
-
-  c = *p++;           // read char */
-
-  // #ifdef _MT
-  //        while ( __iswspace_mt(ptloci, c) )
-  // #else  // _MT */
-  while (c == ' ')
-  //        while ( iswspace(c) )
-  // #endif  // _MT */
-      c = *p++;       // skip whitespace */
-
-  if (c == '-') {
-      flags |= kFlNeg;    // remember minus sign */
-      c = *p++;
-  }
-  else if (c == '+')
-      c = *p++;       // skip sign */
-
-  if (ibase < 0 || ibase == 1 || ibase > 36) {
-      // bad base! */
-      if (endptr)
-          // store beginning of string in endptr */
-          *endptr = const_cast<wchar_t *>(nptr);
-      return 0L;      // return 0 */
-  }
-  else if (ibase == 0) {
-      // determine base free-lance, based on first two chars of
-      // string */
-      if (String_CharToDigit(c) != 0)
-          ibase = 10;
-      else if (*p == L'x' || *p == L'X')
-          ibase = 16;
-      else
-          ibase = 8;
-  }
-
-  if (ibase == 16) {
-      // we might have 0x in front of number; remove if there */
-      if (String_CharToDigit(c) == 0 && (*p == L'x' || *p == L'X')) {
-          ++p;
-          c = *p++;   // advance past prefix */
-      }
-  }
-
-  // if our number exceeds this, we will overflow on multiply */
-  maxval = ULONG_MAX / ibase;
-
-  for (;;) {  // exit in middle of loop */
-
-      // convert c to value */
-      if ( (digval = String_CharToDigit(c)) != (unsigned) -1 )
-          ;
-      else if (c >= 'A' && c <= 'F') { digval = c - 'A' + 10; }
-      else if (c >= 'a' && c <= 'f') { digval = c - 'a' + 10; }
-      // else if ( __ascii_iswalpha(c))
-      //     digval = __ascii_towupper(c) - L'A' + 10;
-      else
-          break;
-
-      if (digval >= (unsigned)ibase)
-          break;      // exit loop if bad digit found */
-
-      // record the fact we have read one digit */
-      flags |= kFlReaddigit;
-
-      // we now need to compute number = number * base + digval,
-      // but we need to know if overflow occured.  This requires
-      // a tricky pre-check. */
-
-      if (number < maxval || (number == maxval &&
-      (unsigned long)digval <= ULONG_MAX % ibase)) {
-          // we won't overflow, go ahead and multiply */
-          number = number * ibase + digval;
-      }
-      else {
-          // we would have overflowed -- set the overflow flag */
-          flags |= kFlOverflow;
-      }
-
-      c = *p++;       // read next digit */
-  }
-
-  --p;                // point to place that stopped scan */
-
-  if (!(flags & kFlReaddigit)) {
-      // no number there; return 0 and point to beginning of string */
-      if (endptr)
-          // store beginning of string in endptr later on */
-          p = nptr;
-      number = 0L;        // return 0 */
-  }
-  // lint -save -e648 -e650  Overflow in -LONG_MIN
-#pragma warning(push)
-// C4287 : unsigned/negative constant mismatch.
-// The offending expression is number > -LONG_MIN. -LONG_MIN overflows and
-// technically -LONG_MIN == LONG_MIN == 0x80000000. It should actually
-// result in a compiler warning, such as C4307: integral constant overflow.
-// Anyway, in the expression (number > -LONG_MIN) the right operand is converted
-// to unsigned long, so the expression is actually evaluated as
-// number > 0x80000000UL. The code is probably correct but subtle, to say the
-// least.
-#pragma warning(disable : 4287)
-  else if ( (flags & kFlOverflow) ||
-        ( !(flags & kFlUnsigned) &&
-          ( ( (flags & kFlNeg) && (number > -LONG_MIN) ) ||
-            ( !(flags & kFlNeg) && (number > LONG_MAX) ) ) ) )
-  {
-      // overflow or signed overflow occurred */
-      // errno = ERANGE;
-      if ( flags & kFlUnsigned )
-          number = ULONG_MAX;
-      else if ( flags & kFlNeg )
-        // lint -e{648, 650}  Overflow in -LONG_MIN
-        number = (unsigned long)(-LONG_MIN);
-      else
-        number = LONG_MAX;
-  }
-#pragma warning(pop)
-  // lint -restore
-
-  if (endptr != NULL)
-      // store pointer to char that stopped the scan */
-      *endptr = const_cast<wchar_t *>(p);
-
-  if (flags & kFlNeg)
-      // negate result if there was a neg sign */
-      number = (unsigned long)(-(long)number);
-
-  return number;          // done. */
-}
-
-long __cdecl Wcstol (const wchar_t *nptr, wchar_t **endptr, int ibase) {
-  ASSERT(endptr, (L""));
-  ASSERT(nptr, (L""));
-
-  return (long) wcstoxl(nptr, endptr, ibase, 0);
-}
-
-unsigned long __cdecl Wcstoul (const wchar_t *nptr, wchar_t **endptr, int ibase) {
-  // endptr may be NULL
-  ASSERT(nptr, (L""));
-
-  return wcstoxl(nptr, endptr, ibase, kFlUnsigned);
-}
-
-// Functions on arrays of strings
-
-// Returns true iff s is in the array strings (case-insensitive compare)
-bool String_MemberOf(const TCHAR* const* strings, const TCHAR* s) {
-  ASSERT(s, (L""));
-  // strings may be NULL
-
-  const int s_length = lstrlen(s);
-  if (strings == NULL)
-    return false;
-  for (; *strings != NULL; strings++) {
-    if (0 == String_StrNCmp(*strings, s, s_length, true)) {
-      return true;      // Found equal string
-    }
-  }
-  return false;
-}
-
-// Returns index of s in the array of strings (or -1 for missing) (case-insensitive compare)
-int String_IndexOf(const TCHAR* const* strings, const TCHAR* s) {
-  ASSERT(s, (L""));
-  // strings may be NULL
-
-  const int s_length = lstrlen(s);
-  if (strings == NULL)
-    return -1;
-  for (int i = 0; *strings != NULL; i++, strings++) {
-    if (0 == String_StrNCmp(*strings, s, s_length, true)) {
-      return i;      // Found equal string
-    }
-  }
-  return -1;
-}
-
-// The internal format is a int64.
-time64 StringToTime(const CString & time) {
-  return static_cast<time64>(String_StringToInt64(time));
-}
-
-// See above comment from StringToTime.
-// Just show it as a INT64 for now
-// NOTE: this will truncating it to INT64, which may lop off some times in the future
-CString TimeToString(const time64 & time) {
-  return String_Int64ToString(static_cast<int64>(time), 10);
-}
-
-const TCHAR *FindStringASpaceStringB (const TCHAR *s, const TCHAR *a, const TCHAR *b) {
-  ASSERT(s, (L""));
-  ASSERT(a, (L""));
-  ASSERT(b, (L""));
-
-  const TCHAR *search_from = s;
-  const TCHAR *pos;
-  while (*search_from && (pos = stristrW (search_from, a)) != NULL) {
-      const TCHAR *start = pos;
-      pos += lstrlen(a);
-      search_from = pos;
-      while (*pos == ' ' || *pos == '\t') pos++;
-      if (!String_StrNCmp (pos, b, lstrlen(b), true)) return start;
-  }
-
-  return 0;
-}
-
-bool IsAlphaA (const char c) {
-  return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
-}
-
-bool IsDigitA (const char c) {
-  return (c >= '0' && c <= '9');
-}
-
-void SafeStrCat (TCHAR *dest, const TCHAR *src, int dest_buffer_len) {
-  _tcscat_s(dest, dest_buffer_len, src);
-}
-
-// extracts next float in a string
-// skips any non-digit characters
-// return position after end of float
-const TCHAR *ExtractNextDouble (const TCHAR *s, double *f) {
-  ASSERT (f, (L""));
-  ASSERT (s, (L""));
-
-  CString num;
-  while (*s && !String_IsDigit (*s)) s++;
-  while (*s && (*s == '.' || String_IsDigit (*s))) { num += *s; s++; }
-  ASSERT (num.GetLength(), (L""));
-  *f = String_StringToDouble (num);
-  return s;
-}
-
-TCHAR *String_PathFindExtension(const TCHAR *path) {
-  ASSERT(path, (L""));
-
-  // Documentation says PathFindExtension string must be of max length
-  // MAX_PATH but a trusted tester hit the ASSERT and we don't really
-  // need it here, so commented out. We can't address where it is
-  // called because it's called from ATL code.
-  // ASSERT(lstrlen(path)<=MAX_PATH, (L""));
-
-  // point to terminating NULL
-  const TCHAR *ret = path + lstrlen(path);
-  const TCHAR *pos = ret;
-
-  while (--pos >= path) {
-    if (*pos == '.')
-      return const_cast<TCHAR *>(pos);
-  }
-
-  return const_cast<TCHAR *>(ret);
-}
-
-char String_ToLowerCharAnsi(char c) {
-  if (c >= 'A' && c <= 'Z') return (c + ('a' - 'A'));
-  return c;
-}
-
-int String_ToLowerChar(int c) {
-  // If it's < 128, then convert is ourself, which is far cheaper than the system conversion
-  if (c < 128)
-    return String_ToLowerCharAnsi(static_cast<char>(c));
-
-  return Char_ToLower(static_cast<TCHAR>(c));
-}
-
-
-bool String_PathRemoveFileSpec(TCHAR *path) {
-  ASSERT (path, (L""));
-
-  int len, pos;
-  len = pos = lstrlen (path);
-
-  // You might think that the SHLWAPI API does not change "c:\windows" -> "c:\"
-  // when c:\windows is a directory, but it does.
-
-  // If we don't want to match this weird API we can use the following to check
-  // for directories:
-
-  // Check if we are already a directory.
-  WIN32_FILE_ATTRIBUTE_DATA attrs;
-  // Failure (if file does not exist) is OK.
-  BOOL success = GetFileAttributesEx(path, GetFileExInfoStandard, &attrs);
-  UTIL_LOG(L4, (_T("[String_PathRemoveFileSpec][path %s][success %d][dir %d]"),
-                path,
-                success,
-                attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
-  if (success && (attrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
-    // Remove trailing backslash, if any.
-    if (path[pos-1] == '\\')
-      path[pos-1] = '\0';
-    return 1;
-  }
-
-  // Find last backslash.
-  while (pos && path[pos] != '\\') pos--;
-  if (!pos && path[pos] != '\\') return 0;
-
-  ASSERT (pos < len, (L""));
-
-  // The documentation says it removes backslash but it doesn't for c:\.
-  if (!pos || path[pos-1] == ':' || (pos == 1 && path[0] == '\\'))
-    // Keep the backslash in this case.
-    path[pos+1] = '\0';
-  else
-    path[pos] = '\0';
-
-  return 1;
-}
-
-void String_EndWithChar(TCHAR *str, TCHAR c) {
-  ASSERT (str, (L""));
-  int len = lstrlen(str);
-  if (len == 0 || str[len - 1] != c) {
-    str[len] = c;
-    str[len + 1] = 0;
-  }
-}
-
-bool StartsWithBOM(const TCHAR* string) {
-  ASSERT(string, (L""));
-  wchar_t c = string[0];
-  if (c == 0xFFFE || c == 0xFEFF)
-    return true;
-  else
-    return false;
-}
-
-const TCHAR* StringAfterBOM(const TCHAR* string) {
-  ASSERT(string, (L""));
-  return &string[StartsWithBOM(string) ? 1 : 0];
-}
-
-bool String_StringToDecimalIntChecked(const TCHAR* str, int* value) {
-  ASSERT1(str);
-  ASSERT1(value);
-
-  if (_set_errno(0)) {
-    return false;
-  }
-
-  TCHAR* end_ptr = NULL;
-  *value = _tcstol(str, &end_ptr, 10);
-  ASSERT1(end_ptr);
-
-  if (errno) {
-    ASSERT1(ERANGE == errno);
-    // Overflow or underflow.
-    return false;
-  } else if (*value == 0) {
-    // The value returned could be an error code. tcsltol returns
-    // zero when it cannot convert the string. However we need to
-    // distinguish a real zero. Thus check to see if end_ptr is not the start
-    // of the string (str is not an empty string) and is pointing to a '\0'.
-    // If not, we have an error.
-    if ((str == end_ptr) || (*end_ptr != '\0')) {
-      return false;
-    }
-  } else if (*end_ptr != '\0') {
-    // The end_ptr is pointing at a character that is
-    // not the end of the string. Only part of the string could be converted.
-    return false;
-  }
-
-  return true;
-}
-
-bool CLSIDToCString(const GUID& guid, CString* str) {
-  ASSERT(str, (L""));
-
-  LPOLESTR string_guid = NULL;
-  if (::StringFromCLSID(guid, &string_guid) != S_OK) {
-    return false;
-  }
-  *str = string_guid;
-  ::CoTaskMemFree(string_guid);
-
-  return true;
-}
-
-HRESULT String_StringToBool(const TCHAR* str, bool* value) {
-  ASSERT1(str);
-  ASSERT1(value);
-
-  // This method now performs a case-insentitive
-  // culture aware compare. We should however be ok as we are only comparing
-  // latin characters.
-  if (_tcsicmp(kFalse, str) == 0) {
-    *value = false;
-  } else if (_tcsicmp(kTrue, str) == 0) {
-    *value = true;
-  } else {
-    // we found another string. should error out.
-    return E_FAIL;
-  }
-  return S_OK;
-}
-
-HRESULT String_BoolToString(bool value, CString* string) {
-  ASSERT1(string);
-  *string = value ? kTrue : kFalse;
-  return S_OK;
-}
-
-// Escape and unescape strings (shlwapi-based implementation).
-// The intended usage for these APIs is escaping strings to make up
-// URLs, for example building query strings.
-//
-// Pass false to the flag segment_only to escape the url. This will not
-// cause the conversion of the # (%23), ? (%3F), and / (%2F) characters.
-
-// Characters that must be encoded include any characters that have no
-// corresponding graphic character in the US-ASCII coded character
-// set (hexadecimal 80-FF, which are not used in the US-ASCII coded character
-// set, and hexadecimal 00-1F and 7F, which are control characters),
-// blank spaces, "%" (which is used to encode other characters),
-// and unsafe characters (<, >, ", #, {, }, |, \, ^, ~, [, ], and ').
-//
-// The input and output strings can't be longer than INTERNET_MAX_URL_LENGTH
-
-HRESULT StringEscape(const CString& str_in,
-                     bool segment_only,
-                     CString* str_out) {
-  ASSERT1(str_out);
-  ASSERT1(str_in.GetLength() < INTERNET_MAX_URL_LENGTH);
-
-  DWORD buf_len = INTERNET_MAX_URL_LENGTH + 1;
-  HRESULT hr = ::UrlEscape(str_in, str_out->GetBufferSetLength(buf_len), &buf_len,
-    segment_only ? URL_ESCAPE_PERCENT | URL_ESCAPE_SEGMENT_ONLY : URL_ESCAPE_PERCENT);
-  if (SUCCEEDED(hr)) {
-    str_out->ReleaseBuffer();
-    ASSERT1(buf_len <= INTERNET_MAX_URL_LENGTH);
-  }
-  return hr;
-}
-
-HRESULT StringUnescape(const CString& str_in, CString* str_out) {
-  ASSERT1(str_out);
-  ASSERT1(str_in.GetLength() < INTERNET_MAX_URL_LENGTH);
-
-  DWORD buf_len = INTERNET_MAX_URL_LENGTH + 1;
-  HRESULT hr = ::UrlUnescape(const_cast<TCHAR*>(str_in.GetString()),
-    str_out->GetBufferSetLength(buf_len), &buf_len, 0);
-  if (SUCCEEDED(hr)) {
-    str_out->ReleaseBuffer(buf_len + 1);
-    ASSERT1(buf_len <= INTERNET_MAX_URL_LENGTH);
-  }
-  return hr;
-}
-
-bool String_StringToTristate(const TCHAR* str, Tristate* value) {
-  ASSERT1(str);
-  ASSERT1(value);
-
-  int numerical_value = 0;
-  if (!String_StringToDecimalIntChecked(str, &numerical_value)) {
-    return false;
-  }
-
-  switch (numerical_value) {
-    case 0:
-      *value = TRISTATE_FALSE;
-      break;
-    case 1:
-      *value = TRISTATE_TRUE;
-      break;
-    case 2:
-      *value = TRISTATE_NONE;
-      break;
-    default:
-      return false;
-  }
-
-  return true;
-}
-
-// Extracts the name and value from a string that contains a name/value pair.
-bool ParseNameValuePair(const CString& token,
-                        TCHAR separator,
-                        CString* name,
-                        CString* value) {
-  ASSERT1(name);
-  ASSERT1(value);
-
-  int separator_index = token.Find(separator);
-  if ((separator_index == -1) ||  // Not a name-value pair.
-      (separator_index == 0) ||  // No name was supplied.
-      (separator_index == (token.GetLength() - 1))) {  // No value was supplied.
-    return false;
-  }
-
-  *name = token.Left(separator_index);
-  *value = token.Right(token.GetLength() - separator_index - 1);
-
-  ASSERT1(token.GetLength() == name->GetLength() + value->GetLength() + 1);
-
-  // It's not possible for the name to contain the separator.
-  ASSERT1(-1 == name->Find(separator));
-  if (-1 != value->Find(separator)) {
-    // The value contains the separator.
-    return false;
-  }
-
-  return true;
-}
-
-bool SplitCommandLineInPlace(TCHAR *command_line,
-                             TCHAR **first_argument_parameter,
-                             TCHAR **remaining_arguments_parameter) {
-  if (!command_line ||
-      !first_argument_parameter ||
-      !remaining_arguments_parameter) {
-    return false;
-  }
-
-  TCHAR end_char;
-  TCHAR *&first_argument = *first_argument_parameter;
-  TCHAR *&remaining_arguments = *remaining_arguments_parameter;
-  if (_T('\"') == *command_line) {
-    end_char = _T('\"');
-    first_argument = remaining_arguments = command_line + 1;
-  } else {
-    end_char = _T(' ');
-    first_argument = remaining_arguments = command_line;
-  }
-  // Search for the end of the first argument
-  while (end_char != *remaining_arguments && '\0' != *remaining_arguments) {
-    ++remaining_arguments;
-  }
-  if (end_char == *remaining_arguments) {
-    *remaining_arguments = '\0';
-    do {
-      // Skip the spaces between the first argument and the remaining arguments.
-      ++remaining_arguments;
-    } while (_T(' ') == *remaining_arguments);
-  }
-  return true;
-}
-
-bool ContainsOnlyAsciiChars(const CString& str) {
-  for (int i = 0; i < str.GetLength(); ++i) {
-    if (str[i] > 0x7F) {
-      return false;
-    }
-  }
-  return true;
-}
-CString BytesToHex(const uint8* bytes, size_t num_bytes) {
-  CString result;
-  if (bytes) {
-    result.Preallocate(num_bytes * sizeof(TCHAR));
-    static const TCHAR* const kHexChars = _T("0123456789abcdef");
-    for (size_t i = 0; i != num_bytes; ++i) {
-      result.AppendChar(kHexChars[(bytes[i] >> 4)]);
-      result.AppendChar(kHexChars[(bytes[i] & 0xf)]);
-    }
-  }
-  return result;
-}
-
-CString BytesToHex(const std::vector<uint8>& bytes) {
-  CString result;
-  if (!bytes.empty()) {
-    result.SetString(BytesToHex(&bytes.front(), bytes.size()));
-  }
-  return result;
-}
-
-void JoinStrings(const std::vector<CString>& components,
-                 const TCHAR* delim,
-                 CString* result) {
-  ASSERT1(result);
-  result->Empty();
-
-  // Compute length so we can reserve memory.
-  size_t length = 0;
-  size_t delim_length = delim ? _tcslen(delim) : 0;
-  for (size_t i = 0; i != components.size(); ++i) {
-    if (i != 0) {
-      length += delim_length;
-    }
-    length += components[i].GetLength();
-  }
-
-  result->Preallocate(length);
-
-  for (size_t i = 0; i != components.size(); ++i) {
-    if (i != 0 && delim) {
-      result->Append(delim, delim_length);
-    }
-    result->Append(components[i]);
-  }
-}
-
-void JoinStringsInArray(const TCHAR* components[],
-                        int num_components,
-                        const TCHAR* delim,
-                        CString* result) {
-  ASSERT1(result);
-  result->Empty();
-
-  for (int i = 0; i != num_components; ++i) {
-    if (i != 0 && delim) {
-      result->Append(delim);
-    }
-    if (components[i]) {
-      result->Append(components[i]);
-    }
-  }
-}
-
-CString FormatResourceMessage(uint32 resource_id, ...) {
-  CString format;
-  const bool is_loaded = !!format.LoadString(resource_id);
-
-  if (!is_loaded) {
-    return CString();
-  }
-
-  va_list arg_list;
-  va_start(arg_list, resource_id);
-
-  CString formatted;
-  formatted.FormatMessageV(format, &arg_list);
-
-  va_end(arg_list);
-
-  return formatted;
-}
-
-CString FormatErrorCode(DWORD error_code) {
-  CString error_code_string;
-  if (FAILED(error_code)) {
-    error_code_string.Format(_T("0x%08x"), error_code);
-  } else {
-    error_code_string.Format(_T("%u"), error_code);
-  }
-  return error_code_string;
-}
-
-HRESULT WideStringToUtf8UrlEncodedString(const CString& str, CString* out) {
-  ASSERT1(out);
-
-  out->Empty();
-  if (str.IsEmpty()) {
-    return S_OK;
-  }
-
-  // Utf8 encode the Utf16 string first. Next urlencode it.
-  CStringA utf8str = WideToUtf8(str);
-  ASSERT1(!utf8str.IsEmpty());
-  DWORD buf_len = INTERNET_MAX_URL_LENGTH;
-  CStringA escaped_utf8_name;
-  HRESULT hr = ::UrlEscapeA(utf8str,
-                            CStrBufA(escaped_utf8_name, buf_len),
-                            &buf_len,
-                            0);
-  ASSERT1(buf_len <= INTERNET_MAX_URL_LENGTH);
-  ASSERT1(escaped_utf8_name.GetLength() == static_cast<int>(buf_len));
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[UrlEscapeA failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  *out = CString(escaped_utf8_name);
-  return S_OK;
-}
-
-HRESULT Utf8UrlEncodedStringToWideString(const CString& str, CString* out) {
-  ASSERT1(out);
-
-  out->Empty();
-  if (str.IsEmpty()) {
-    return S_OK;
-  }
-
-  // The value is a utf8 encoded url escaped string that is stored as a
-  // unicode string. Because of this, it should contain only ascii chars.
-  if (!ContainsOnlyAsciiChars(str)) {
-    UTIL_LOG(LE, (_T("[String contains non ascii chars]")));
-    return E_INVALIDARG;
-  }
-
-  CStringA escaped_utf8_val = WideToAnsiDirect(str);
-  DWORD buf_len = INTERNET_MAX_URL_LENGTH;
-  CStringA unescaped_val;
-  HRESULT hr = ::UrlUnescapeA(const_cast<char*>(escaped_utf8_val.GetString()),
-                              CStrBufA(unescaped_val, buf_len),
-                              &buf_len,
-                              0);
-  ASSERT1(unescaped_val.GetLength() == static_cast<int>(buf_len));
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[UrlUnescapeA failed][0x%08x]"), hr));
-    return hr;
-  }
-  ASSERT1(buf_len == static_cast<DWORD>(unescaped_val.GetLength()));
-  ASSERT1(buf_len <= INTERNET_MAX_URL_LENGTH);
-  CString app_name = Utf8ToWideChar(unescaped_val,
-                                    unescaped_val.GetLength());
-  if (app_name.IsEmpty()) {
-    return E_INVALIDARG;
-  }
-
-  *out = app_name;
-  return S_OK;
-}
-
-}  // namespace omaha
-
diff --git a/common/string.h b/common/string.h
deleted file mode 100644
index 258aa1b..0000000
--- a/common/string.h
+++ /dev/null
@@ -1,537 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_COMMON_STRING_H__
-#define OMAHA_COMMON_STRING_H__
-
-#include <windows.h>
-#include <vector>
-#include "base/basictypes.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-
-namespace omaha {
-
-#define STR_SIZE(str) (arraysize(str)-1)  // number of characters in char array (only for single-byte string literals!!!)
-#define TSTR_SIZE(tstr) (arraysize(tstr)-1)  // like STR_SIZE but works on _T("string literal") ONLY!!!
-
-#define kEllipsis L".."
-
-// The number of replacements matches we expect, before we start allocating extra memory
-// to process it. This is an optimizing constant
-#define kExpectedMaxReplaceMatches 100
-
-// TODO(omaha): above each of these function names, we should
-// define what we expect the implementation to do. that way,
-// implementers will know what is desired. an example would probably
-// make things easiest.
-CString AbbreviateString (const CString & title, int32 max_len);
-CString AbbreviateUri (const CString & uri, int32 max_len);
-CString NormalizeUri (const CString & uri);
-
-// removes "http://", "ftp://", "mailto:" or "file://" (note that the "file" protocol is
-// like: "file:///~/calendar", this method removes only the first two slashes
-CString RemoveInternetProtocolHeader (const CString& url);
-
-void RemoveFromStart (CString & s, const TCHAR* remove, bool ignore_case);
-void RemoveFromEnd (CString & s, const TCHAR* remove);
-
-// Limit string to max length, truncating and adding ellipsis if needed
-// Attempts to not leave a partial word at the end, unless min_len is reached
-CString ElideIfNeeded (const CString & input_string, int max_len, int min_len);
-
-// The ability to clean up a string for relevant target audiences. Add flags accordingly
-
-// Sanitizes for insertion in an HTML document, uses the basic literals [<>&]
-#define kSanHtml 0x1
-
-// XML is the HTML replacements, and a few more
-#define kSanXml (kSanHtml | 0x2)
-
-// Javascript has a seperate set of encodings [which is a superset of HTML replacements]
-#define kSanJs (kSanHtml | 0x4)
-
-// For input fields on HTML documents
-#define kSanHtmlInput 0x8
-
-// TODO(omaha): be consistent on use of int/uint32/int32 for lengths
-
-// The input length of the string does not include the null terminator.
-// Caller deletes the returned buffer.
-WCHAR *ToWide (const char *s, int len);
-
-// returns pointer to data if found otherwise NULL
-const byte *BufferContains (const byte *buf, uint32 buf_len, const byte *data, uint32 data_len);
-
-// Given a string, 'protect' the characters that are invalid for a given mode
-// For instance, kSanHtml will replace < with the HTML literal equivalent
-// If kSanHtml is used, and bold_periods is true, then periods used for url abbreviation are bolded.
-// NOTE: If you call AbbreviateLinkForDisplay before this function, then there might be periods
-// used for abbreviation.  BoldAbbreviationPeriods should be called after HighlightTerms.
-CString SanitizeString(const CString & in, DWORD mode);
-
-// Bolds the periods used for abbreviation.  Call this after HighlightTerms.
-CString BoldAbbreviationPeriods(const CString & in);
-
-// Unencode a URL encoded string
-CString Unencode(const CString & input);
-
-CString GetTextInbetween(const CString &input, const CString &start, const CString &end);
-
-// Given a ? seperated string, extract a particular segment, and URL-Unencode it
-CString GetParam(const CString & input, const CString & key);
-
-// Given an XML style string, extract the contents of a <INPUT>...</INPUT> pair
-CString GetField (const CString & input, const CString & field);
-
-// Finds a whole word match in the query, followed by a ":".
-// If not found, return -1.
-//
-// Note: this is case sensitive.
-int FindWholeWordMatch (const CString &query,
-  const CString &word_to_match,
-  const bool end_with_colon,
-  const int index_begin);
-
-// Do whole-word replacement in "str".
-// This does not do partial matches (unlike CString::Replace),
-//   e.g.  CString::Replace will replace "ie" within "pie" and
-// this function will not.
-//
-// Note: this is case sensitive.
-void ReplaceWholeWord (const CString &string_to_replace,
-  const CString &replacement,
-  const bool trim_whitespace,
-  CString *str);
-
-// Convert Wide to ANSI directly. Use only when it is all ANSI
-CStringA WideToAnsiDirect(const CString & in);
-
-// Transform a unicode string into UTF8, used primarily by the webserver
-CStringA WideToUtf8(const CString& w);
-
-// Converts the UTF-8 encoded buffer to an in-memory Unicode (wide character)
-// string.
-// @param utf8 A non-NULL pointer to a UTF-8 encoded buffer that has at
-// least num_bytes valid characters.  If num_bytes is 0, the buffer must be
-// NULL-terminated.
-// @param num_bytes Number of bytes to process from utf8, or 0 if utf8 is
-// NULL-terminated and should be processed in its entirety.
-// @return The Unicode string represented by utf8 (or that part of it
-// specified by num_bytes).  If the UTF-8 representation of the string started
-// with a byte-order marker (BOM), it will be ignored and not included in the
-// returned string.  On failure, the function returns the empty string.
-CString Utf8ToWideChar(const char* utf8, uint32 num_bytes);
-CString Utf8BufferToWideChar(const std::vector<uint8>& buffer);
-
-// Dealing with Unicode BOM
-bool StartsWithBOM(const TCHAR* string);
-const TCHAR* StringAfterBOM(const TCHAR* string);
-
-// Convert an ANSI string into Widechar string, according to the specified
-// codepage. The input length can be -1, if the string is null terminated, and
-// the actual length will be used internally.
-BOOL AnsiToWideString(const char *from, int length, UINT codepage, CString *to);
-
-// Convert char to Wchar directly
-CString AnsiToWideString(const char *from, int length);
-
-// these functions untested
-// they should not be used unless tested
-// HRESULT AnsiToUTF8 (char * src, int src_len, char * dest, int *dest_len);
-// HRESULT UTF8ToAnsi (char * src, int src_len, char * dest, int *dest_len);
-// HRESULT UCS2ToUTF8 (LPCWSTR src, int src_len, char * dest, int *dest_len);
-// HRESULT UTF8ToUCS2 (char * src, int src_len, LPWSTR dest, int *dest_len);
-
-// "Absolute" is perhaps not the right term, this normalizes the Uri
-// given http://www.google.com changes to correct http://www.google.com/
-// given http://www.google.com// changes to correct http://www.google.com/
-// given http://www.google.com/home.html returns the same
-CString GetAbsoluteUri(const CString& uri);
-
-// Reverse (big-endian<->little-endian) the shorts that make up
-// Unicode characters in a byte array of Unicode chars
-HRESULT ReverseUnicodeByteOrder(byte* unicode_string, int size_in_bytes);
-
-// given http://google.com/bobby this returns http://google.com/
-// If strip_leading is specified, it will turn
-// http://www.google.com into http://google.com
-#define kStrLeadingWww _T("www.")
-// TODO(omaha): no default parameters
-CString GetUriHostName(const CString& uri, bool strip_leading = false);
-CString GetUriHostNameHostOnly(const CString& uri, bool strip_leading_www);
-
-const char *stristr(const char *string, const char *pattern);
-const WCHAR *stristrW(const WCHAR *string, const WCHAR *pattern);
-const WCHAR *strstrW(const WCHAR *string, const WCHAR *pattern);
-
-// Add len_to_add to len_so_far, assuming that if it exceeds the
-// length of the line, it will word wrap onto the next line.  Returns
-// the total length of all the lines summed together.
-float GetLenWithWordWrap (const float len_so_far,
-  const float len_to_add,
-  const uint32 len_line);
-
-// ----------------------------------------------------------------------
-// QuotedPrintableUnescape()
-//    Copies "src" to "dest", rewriting quoted printable escape sequences
-//    =XX to their ASCII equivalents. src is not null terminated, instead
-//    specify len. I recommend that slen<len_dest, but we honour len_dest
-//    anyway.
-//    RETURNS the length of dest.
-// ----------------------------------------------------------------------
-int QuotedPrintableUnescape(const WCHAR *src, int slen, WCHAR *dest, int len_dest);
-
-// Return the length to use for the output buffer given to the base64 escape
-// routines. Make sure to use the same value for do_padding in both.
-// This function may return incorrect results if given input_len values that
-// are extremely high, which should happen rarely.
-int CalculateBase64EscapedLen(int input_len, bool do_padding);
-// Use this version when calling Base64Escape without a do_padding arg.
-int CalculateBase64EscapedLen(int input_len);
-
-// ----------------------------------------------------------------------
-// Base64Escape()
-// WebSafeBase64Escape()
-//    Encode "src" to "dest" using base64 encoding.
-//    src is not null terminated, instead specify len.
-//    'dest' should have at least CalculateBase64EscapedLen() length.
-//    RETURNS the length of dest.
-//    The WebSafe variation use '-' instead of '+' and '_' instead of '/'
-//    so that we can place the out in the URL or cookies without having
-//    to escape them.  It also has an extra parameter "do_padding",
-//    which when set to false will prevent padding with "=".
-// ----------------------------------------------------------------------
-int Base64Escape(const char *src, int slen, char *dest, int szdest);
-int WebSafeBase64Escape(const char *src, int slen, char *dest,
-                        int szdest, bool do_padding);
-void WebSafeBase64Escape(const CStringA& src, CStringA* dest);
-
-void Base64Escape(const char *src, int szsrc,
-                  CStringA* dest, bool do_padding);
-void WebSafeBase64Escape(const char *src, int szsrc,
-                         CStringA* dest, bool do_padding);
-
-// ----------------------------------------------------------------------
-// Base64Unescape()
-//    Copies "src" to "dest", where src is in base64 and is written to its
-//    ASCII equivalents. src is not null terminated, instead specify len.
-//    I recommend that slen<len_dest, but we honour len_dest anyway.
-//    RETURNS the length of dest.
-//    The WebSafe variation use '-' instead of '+' and '_' instead of '/'.
-// ----------------------------------------------------------------------
-int Base64Unescape(const char *src, int slen, char *dest, int len_dest);
-int WebSafeBase64Unescape(const char *src, int slen, char *dest, int szdest);
-
-#ifdef UNICODE
-#define IsSpace IsSpaceW
-#else
-#define IsSpace IsSpaceA
-#endif
-
-bool IsSpaceW(WCHAR c);
-bool IsSpaceA(char c);
-
-// Remove all leading and trailing whitespace from s.
-// Returns the new length of the string (not including 0-terminator)
-int TrimCString(CString &s);
-int Trim(TCHAR *s);
-
-// Trims all characters in the delimiter string from both ends of the
-// string s
-void TrimString(CString& s, const TCHAR* delimiters);
-
-// Strip the first token from the front of argument s.  A token is a
-// series of consecutive non-blank characters - unless the first
-// character is a double-quote ("), in that case the token is the full
-// quoted string
-CString StripFirstQuotedToken(const CString& s);
-
-// A block of text to separate lines, and back
-void TextToLines(const CString& text, const TCHAR* delimiter, std::vector<CString>* lines);
-// (LinesToText puts a delimiter at the end of the last line too)
-void LinesToText(const std::vector<CString>& lines, const TCHAR* delimiter, CString* text);
-
-// Make a CString lower case
-void MakeLowerCString(CString & s);
-
-// Clean up the string: replace all whitespace with spaces, and
-// replace consecutive spaces with one.
-// Returns the new length of the string (not including 0-terminator)
-int CleanupWhitespaceCString(CString &s);
-int CleanupWhitespace(TCHAR *s);
-
-int HexDigitToInt (WCHAR c);
-bool IsHexDigit (WCHAR c);
-
-// Converts to lower, but does so much faster if the string is ANSI
-TCHAR * String_FastToLower(TCHAR * str);
-
-// Replacement for the CRT toupper(c)
-int String_ToUpper(int c);
-
-// Replacement for the CRT toupper(c)
-char String_ToUpperA(char c);
-
-// Converts str to lowercase in place.
-void String_ToLower(TCHAR* str);
-
-// Converts str to uppercase in place.
-void String_ToUpper(TCHAR* str);
-
-bool String_IsUpper(TCHAR c);
-
-// String comparison based on length
-// Replacement for the CRT strncmp(i)
-int String_StrNCmp(const TCHAR * str1, const TCHAR * str2, uint32 len, bool ignore_case);
-
-// Replacement for strncpy() - except ALWAYS ends string with null
-TCHAR* String_StrNCpy(TCHAR* destination, const TCHAR* source, uint32 len);
-
-// check if str starts with start_str
-bool String_StartsWith(const TCHAR *str, const TCHAR *start_str, bool ignore_case);
-
-// check if str starts with start_str, for char *
-bool String_StartsWithA(const char *str, const char *start_str, bool ignore_case);
-
-// check if str ends with end_str
-bool String_EndsWith(const TCHAR *str, const TCHAR *end_str, bool ignore_case);
-
-// If the input string str doesn't already end with the string end_str,
-// make it end with the string end_str.
-CString String_MakeEndWith(const TCHAR *str, const TCHAR* end_str, bool ignore_case);
-
-// converts an int to a string
-CString String_Int64ToString(int64 value, int radix);
-
-// converts an uint64 to a string
-CString String_Uint64ToString(uint64 value, int radix);
-
-// Convert numeric types to CString
-CString sizet_to_str(const size_t & i);
-CString itostr(const int i);
-CString itostr(const uint32 i);
-
-// converts a large number to an approximate value, like "1.2G" or "900M"
-// base_ten = true if based on powers of 10 (like disk space) otherwise based
-// on powers of two.  power = 0 for *10^0, 1 for *10^3 or 2^10, 2 for *10^6
-// or 2^20, and 3 for *10^9 or 2^30, in other words: no units, K, M, or G.
-CString String_LargeIntToApproximateString(uint64 value, bool base_ten, int* power);
-
-// converts a string to an  int
-// Does not check for overflow
-int32 String_StringToInt(const TCHAR * str);
-
-int64 String_StringToInt64(const TCHAR * str);
-
-// converts an double to a string
-// specifies the number of digits after the decimal point
-// TODO(omaha): Make this work for negative values
-CString String_DoubleToString(double value, int point_digits);
-
-// convert string to double
-double String_StringToDouble (const TCHAR *s);
-
-// Converts a character to a digit
-// if the character is not a digit return -1
-int32 String_CharToDigit(const TCHAR c);
-
-// returns true if ASCII digit
-bool String_IsDigit(const TCHAR c);
-
-// Converts the digit to a character.
-TCHAR String_DigitToChar(unsigned int n);
-
-// Returns true if an identifier character: letter, digit, or "_"
-bool String_IsIdentifierChar(const TCHAR c);
-
-// Returns true if the string has letters in it.
-// This is used by the keyword extractor to downweight numbers,
-// IDs (sequences of numbers like social security numbers), etc.
-bool String_HasAlphabetLetters (const TCHAR *str);
-
-// Return the index of the first occurrence of s2 in s1, or -1 if none.
-int String_FindString(const TCHAR *s1, const TCHAR *s2);
-int String_FindString(const TCHAR *s1, const TCHAR *s2, int start_pos);
-
-// Return the index of the first occurrence of c in s1, or -1 if none.
-int String_FindChar(const TCHAR *str, const TCHAR c);
-// start from index start_pos
-int String_FindChar(const TCHAR *str, const TCHAR c, int start_pos);
-
-// Return the index of the first occurrence of c in string, or -1 if none.
-int String_ReverseFindChar(const TCHAR * str, TCHAR c);
-
-bool String_Contains(const TCHAR *s1, const TCHAR *s2);
-
-// Replace old_char with new_char in str.
-void String_ReplaceChar(TCHAR *str, TCHAR old_char, TCHAR new_char);
-void String_ReplaceChar(CString & str, TCHAR old_char, TCHAR new_char);
-
-// Append the given character to the string if it doesn't already end with it.
-// There must be room in the string to append the character if necessary.
-void String_EndWithChar(TCHAR *str, TCHAR c);
-
-// A special version of the replace function which takes advantage of CString properties
-// to make it much faster when the string grows
-
-// NOTE: it CANNOT match more than kMaxReplaceMatches instances within the string
-// do not use this function if that is a possibility
-
-// The maximum number of replacements to perform. Essentially infinite
-#define kRepMax kUint32Max
-int ReplaceCString (CString & src, const TCHAR *from, unsigned int from_len,
-                                   const TCHAR *to, unsigned int to_len,
-                                   unsigned int max_matches);
-
-// replace from with to in src
-// on memory allocation error, returns the original string
-int ReplaceString (TCHAR *src, const TCHAR *from, const TCHAR *to, TCHAR **out, int *out_len);
-
-// replace from with to in src
-// will replace in place if length(to) <= length(from) and return *out == src
-// WILL CREATE NEW OUTPUT BUFFER OTHERWISE and set created_new_string to true
-// on memory allocation error, returns the original string
-int ReplaceStringMaybeInPlace (TCHAR *src, const TCHAR *from, const TCHAR *to, TCHAR **out, int *out_len, bool *created_new_string);
-
-// you really want to use the straight TCHAR version above. you know it
-// on memory allocation error, returns the original string
-int ReplaceCString (CString & src, const TCHAR *from, const TCHAR *to);
-
-long __cdecl Wcstol (const wchar_t *nptr, wchar_t **endptr, int ibase);
-unsigned long __cdecl Wcstoul (const wchar_t *nptr, wchar_t **endptr, int ibase);
-
-// Functions on arrays of strings
-
-// Returns true iff s is in the array strings (case-insensitive compare)
-bool String_MemberOf(const TCHAR* const* strings, const TCHAR* s);
-// Returns index of s in the array of strings (or -1 for missing) (case-insensitive compare)
-int String_IndexOf(const TCHAR* const* strings, const TCHAR* s);
-
-// Serializes a time64 to a string, and then loads it out again, this string it not for human consumption
-time64 StringToTime(const CString & time);
-CString TimeToString(const time64 & time);
-
-// looks for string A followed by any number of spaces/tabs followed by string b
-// returns starting position of a if found, NULL if not
-// case insensitive
-const TCHAR *FindStringASpaceStringB (const TCHAR *s, const TCHAR *a, const TCHAR *b);
-
-bool IsAlphaA (const char c);
-bool IsDigitA (const char c);
-
-// TODO(omaha): deprecate since we have secure CRT now.
-// dest_buffer_len includes the NULL
-// always NULL terminates
-// dest must be a valid string with length < dest_buffer_len
-void SafeStrCat (TCHAR *dest, const TCHAR *src, int dest_buffer_len);
-
-const TCHAR *ExtractNextDouble (const TCHAR *s, double *f);
-
-TCHAR *String_PathFindExtension(const TCHAR *path);
-
-inline TCHAR Char_ToLower(TCHAR c) {
-// C4302: truncation from 'type 1' to 'type 2'
-#pragma  warning(disable : 4302)
-  return reinterpret_cast<TCHAR>(::CharLower(reinterpret_cast<TCHAR*>(c)));
-#pragma warning(default : 4302)
-}
-
-// @returns the lowercase character (type is int to be consistent with the CRT)
-int String_ToLowerChar(int c);
-
-// Replacement for the CRT tolower(c)
-char String_ToLowerCharAnsi(char c);
-
-bool String_PathRemoveFileSpec(TCHAR *path);
-
-// Escapes and unescapes strings (shlwapi-based implementation).
-// The indended usage for these APIs is escaping strings to make up
-// URLs, for example building query strings.
-//
-// Pass false to the flag segment_only to escape the url. This will not
-// cause the conversion of the # (%23), ? (%3F), and / (%2F) characters.
-HRESULT StringEscape(const CString& str_in,
-                     bool segment_only,
-                     CString* str_out);
-
-HRESULT StringUnescape(const CString& str_in, CString* str_out);
-
-// Converts a string to an int, performs all the necessary
-// checks to ensure that the string is correct.
-// Tests for overflow and non-int strings.
-bool String_StringToDecimalIntChecked(const TCHAR* str, int* value);
-
-// Converts CLSID to a string.
-bool CLSIDToCString(const GUID& guid, CString* str);
-
-// Converts a string to a bool.
-HRESULT String_StringToBool(const TCHAR* str, bool* value);
-
-// Convert boolean to its string representation.
-HRESULT String_BoolToString(bool value, CString* string);
-
-// Converts a string to a Tristate enum.
-bool String_StringToTristate(const TCHAR* str, Tristate* value);
-
-// Extracts the name and value from a string that contains a name/value pair.
-bool ParseNameValuePair(const CString& token, TCHAR separator,
-                        CString* name, CString* value);
-
-// Splits a command line buffer into two parts in place:
-// first argument (which could be path to executable) and remaining arguments.
-// Note that the same pointer can be used for both command_line and
-// either of the remaining parameters.
-bool SplitCommandLineInPlace(TCHAR *command_line,
-                             TCHAR **first_argument,
-                             TCHAR **remaining_arguments);
-
-// Returns true if the unicode string only contains ascii values.
-bool ContainsOnlyAsciiChars(const CString& str);
-// Converts a buffer of bytes to a hex string.
-CString BytesToHex(const uint8* bytes, size_t num_bytes);
-
-// Converts a vector of bytes to a hex string.
-CString BytesToHex(const std::vector<uint8>& bytes);
-
-void JoinStrings(const std::vector<CString>& components,
-                 const TCHAR* delim,
-                 CString* result);
-
-void JoinStringsInArray(const TCHAR* components[],
-                        int num_components,
-                        const TCHAR* delim,
-                        CString* result);
-
-// Formats the specified message ID.
-// It is similar to CStringT::FormatMessage() but it returns an empty string
-// instead of throwing when the message ID cannot be loaded.
-CString FormatResourceMessage(uint32 resource_id, ...);
-
-// Formats an error code as an 8-digit HRESULT-style hex number or an unsigned
-// integer depending on whether it matches the HRESULT failure format.
-CString FormatErrorCode(DWORD error_code);
-
-// Converts the unicode string into a utf8 encoded, urlencoded string.
-// The resulting ascii string is returned in a wide CString.
-HRESULT WideStringToUtf8UrlEncodedString(const CString& str, CString* out);
-
-// Converts a string that is in the utf8 representation and is urlencoded
-// into a unicode string.
-HRESULT Utf8UrlEncodedStringToWideString(const CString& str, CString* out);
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_STRING_H__
diff --git a/common/string_unittest.cc b/common/string_unittest.cc
deleted file mode 100644
index 01d838b..0000000
--- a/common/string_unittest.cc
+++ /dev/null
@@ -1,1712 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "base/basictypes.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/localization.h"
-#include "omaha/common/string.h"
-#include "omaha/common/time.h"
-#include "omaha/common/timer.h"
-#include "omaha/common/tr_rand.h"
-#include "omaha/goopdate/resources/goopdateres/goopdate.grh"
-#include "omaha/testing/resource.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(StringTest, IntToString) {
-  ASSERT_STREQ(String_Int64ToString(0, 10), L"0");
-  ASSERT_STREQ(String_Int64ToString(1, 10), L"1");
-  ASSERT_STREQ(String_Int64ToString(-1, 10), L"-1");
-  ASSERT_STREQ(String_Int64ToString(123456789, 10), L"123456789");
-  ASSERT_STREQ(String_Int64ToString(-123456789, 10), L"-123456789");
-  ASSERT_STREQ(String_Int64ToString(1234567890987654321, 10),
-               L"1234567890987654321");
-  ASSERT_STREQ(String_Int64ToString(-1234567890987654321, 10),
-               L"-1234567890987654321");
-  ASSERT_STREQ(String_Int64ToString(0xabcdef, 16), L"abcdef");
-  ASSERT_STREQ(String_Int64ToString(0x101fff, 16), L"101fff");
-  ASSERT_STREQ(String_Int64ToString(0x999999, 16), L"999999");
-  ASSERT_STREQ(String_Int64ToString(0x0, 16), L"0");
-
-  ASSERT_STREQ(String_Int64ToString(01234, 8), L"1234");
-  ASSERT_STREQ(String_Int64ToString(0, 8), L"0");
-  ASSERT_STREQ(String_Int64ToString(0777, 8), L"777");
-  ASSERT_STREQ(String_Int64ToString(0123456, 8), L"123456");
-
-  ASSERT_STREQ(String_Int64ToString(0, 2), L"0");
-  ASSERT_STREQ(String_Int64ToString(0xf, 2), L"1111");
-  ASSERT_STREQ(String_Int64ToString(0x5ad1, 2), L"101101011010001");
-  ASSERT_STREQ(String_Int64ToString(-1, 2), L"-1");
-}
-
-TEST(StringTest, UintToString) {
-  ASSERT_STREQ(String_Uint64ToString(0, 10), L"0");
-  ASSERT_STREQ(String_Uint64ToString(1, 10), L"1");
-  ASSERT_STREQ(String_Uint64ToString(123456789, 10), L"123456789");
-  ASSERT_STREQ(String_Uint64ToString(1234567890987654321, 10),
-               L"1234567890987654321");
-  ASSERT_STREQ(String_Uint64ToString(18446744073709551615, 10),
-               L"18446744073709551615");
-
-  ASSERT_STREQ(String_Uint64ToString(0xabcdef, 16), L"abcdef");
-  ASSERT_STREQ(String_Uint64ToString(0x101fff, 16), L"101fff");
-  ASSERT_STREQ(String_Uint64ToString(0x999999, 16), L"999999");
-  ASSERT_STREQ(String_Uint64ToString(0x0, 16), L"0");
-  ASSERT_STREQ(String_Uint64ToString(0xffffffffffffffff, 16), L"ffffffffffffffff");
-
-  ASSERT_STREQ(String_Uint64ToString(01234, 8), L"1234");
-  ASSERT_STREQ(String_Uint64ToString(0, 8), L"0");
-  ASSERT_STREQ(String_Uint64ToString(0777, 8), L"777");
-  ASSERT_STREQ(String_Uint64ToString(0123456, 8), L"123456");
-
-  ASSERT_STREQ(String_Uint64ToString(0, 2), L"0");
-  ASSERT_STREQ(String_Uint64ToString(0xf, 2), L"1111");
-  ASSERT_STREQ(String_Uint64ToString(0x5ad1, 2), L"101101011010001");
-}
-
-TEST(StringTest, DoubleToString) {
-  ASSERT_STREQ(String_DoubleToString(1.234, 1), L"1.2");
-  ASSERT_STREQ(String_DoubleToString(0.0, 0), L"0");
-  ASSERT_STREQ(String_DoubleToString(0.0, 2), L"0.00");
-  ASSERT_STREQ(String_DoubleToString(199.234, 2), L"199.23");
-  ASSERT_STREQ(String_DoubleToString(-199.234, 2), L"-199.23");
-  ASSERT_STREQ(String_DoubleToString(199.23490776, 5), L"199.23490");
-  ASSERT_STREQ(String_DoubleToString(-1.0001, 1), L"-1.0");
-  ASSERT_STREQ(String_DoubleToString(123456789.987654321, 3), L"123456789.987");
-}
-
-TEST(StringTest, StrNCpy) {
-  TCHAR * str1 = L"test str 1234";
-  TCHAR * str2 = L"test str 12";
-  TCHAR * str3 = L"Test StR 1234";
-
-  // check case sensitive
-  ASSERT_TRUE(0 == String_StrNCmp(str1, str2, 10, false));
-  ASSERT_TRUE(0 == String_StrNCmp(str1, str2, 11, false));
-
-  // check case in-sensitive
-  ASSERT_TRUE(0 == String_StrNCmp(str2, str3, 10, true));
-  ASSERT_TRUE(0 == String_StrNCmp(str2, str3, 11, true));
-}
-
-TEST(StringTest, StartsWith) {
-  ASSERT_TRUE(String_StartsWith(L"", L"", false));
-  ASSERT_TRUE(String_StartsWith(L"Joe", L"", false));
-  ASSERT_TRUE(String_StartsWith(L"Joe", L"J", false));
-  ASSERT_TRUE(String_StartsWith(L"Joe\\", L"J", false));
-  ASSERT_TRUE(String_StartsWith(L"Joe", L"Joe", false));
-  ASSERT_TRUE(String_StartsWith(L"The quick brown fox", L"The quic", false));
-  ASSERT_FALSE(String_StartsWith(L"", L"J", false));
-  ASSERT_FALSE(String_StartsWith(L"Joe", L"Joe2", false));
-  ASSERT_FALSE(String_StartsWith(L"The quick brown fox", L"The quiC", false));
-
-  ASSERT_TRUE(String_StartsWith(L"", L"", true));
-  ASSERT_TRUE(String_StartsWith(L"Joe", L"j", true));
-  ASSERT_TRUE(String_StartsWith(L"The quick brown fox", L"The quiC", true));
-}
-
-TEST(StringTest, StartsWithA) {
-  ASSERT_TRUE(String_StartsWithA("", "", false));
-  ASSERT_TRUE(String_StartsWithA("Joe", "", false));
-  ASSERT_TRUE(String_StartsWithA("Joe", "J", false));
-  ASSERT_TRUE(String_StartsWithA("Joe\\", "J", false));
-  ASSERT_TRUE(String_StartsWithA("Joe", "Joe", false));
-  ASSERT_TRUE(String_StartsWithA("The quick brown fox", "The quic", false));
-  ASSERT_FALSE(String_StartsWithA("", "J", false));
-  ASSERT_FALSE(String_StartsWithA("Joe", "Joe2", false));
-  ASSERT_FALSE(String_StartsWithA("The quick brown fox", "The quiC", false));
-
-  ASSERT_TRUE(String_StartsWithA("", "", true));
-  ASSERT_TRUE(String_StartsWithA("Joe", "j", true));
-  ASSERT_TRUE(String_StartsWithA("The quick brown fox", "The quiC", true));
-}
-
-TEST(StringTest, EndsWith) {
-  // Case sensitive
-
-  // Empty suffix
-  ASSERT_TRUE(String_EndsWith(L"", L"", false));
-  ASSERT_TRUE(String_EndsWith(L"Joe", L"", false));
-
-  // Partial suffix
-  ASSERT_TRUE(String_EndsWith(L"Joe", L"e", false));
-  ASSERT_TRUE(String_EndsWith(L"Joe\\", L"\\", false));
-  ASSERT_TRUE(String_EndsWith(L"The quick brown fox", L"n fox", false));
-
-  // Suffix == String
-  ASSERT_TRUE(String_EndsWith(L"Joe", L"Joe", false));
-  ASSERT_TRUE(String_EndsWith(L"The quick brown fox",
-                              L"The quick brown fox",
-                              false));
-
-  // Fail cases
-  ASSERT_FALSE(String_EndsWith(L"", L"J", false));
-  ASSERT_FALSE(String_EndsWith(L"Joe", L"Joe2", false));
-  ASSERT_FALSE(String_EndsWith(L"Joe", L"2Joe", false));
-  ASSERT_FALSE(String_EndsWith(L"The quick brown fox", L"n foX", false));
-
-  // Check case insensitive
-
-  // Empty suffix
-  ASSERT_TRUE(String_EndsWith(L"", L"", true));
-  ASSERT_TRUE(String_EndsWith(L"Joe", L"", true));
-
-  // Partial suffix
-  ASSERT_TRUE(String_EndsWith(L"Joe", L"E", true));
-  ASSERT_TRUE(String_EndsWith(L"The quick brown fox", L"n FOX", true));
-
-  // Suffix == String
-  ASSERT_TRUE(String_EndsWith(L"Joe", L"JOE", true));
-  ASSERT_TRUE(String_EndsWith(L"The quick brown fox",
-                              L"The quick brown FOX",
-                              true));
-
-  // Fail cases
-  ASSERT_FALSE(String_EndsWith(L"The quick brown fox", L"s", true));
-  ASSERT_FALSE(String_EndsWith(L"The quick brown fox", L"Xs", true));
-  ASSERT_FALSE(String_EndsWith(L"The quick brown fox", L"the brown foX", true));
-}
-
-TEST(StringTest, Unencode) {
-  // Normal, correct usage.
-  // char 0x25 is '%'
-  ASSERT_STREQ(Unencode(L"?q=moon+doggy_%25%5E%26"), L"?q=moon doggy_%^&");
-  ASSERT_STREQ(Unencode(L"%54%68%69%73+%69%73%09%61%20%74%65%73%74%0A"),
-               L"This is\ta test\n");
-  ASSERT_STREQ(Unencode(L"This+is%09a+test%0a"), L"This is\ta test\n");
-
-  // NULL char.
-  ASSERT_STREQ(Unencode(L"Terminated%00before+this"), L"Terminated");
-  ASSERT_STREQ(Unencode(L"invalid+%a%25"), L"invalid %a%");
-  ASSERT_STREQ(Unencode(L"invalid+%25%41%37"), L"invalid %A7");
-  ASSERT_STREQ(Unencode(L"not a symbol %RA"), L"not a symbol %RA");
-  ASSERT_STREQ(Unencode(L"%ag"), L"%ag");
-  ASSERT_STREQ(Unencode(L"dontdecode%dont"), L"dontdecode%dont");
-  ASSERT_STREQ(Unencode(L""), L"");
-  ASSERT_STREQ(Unencode(L"%1"), L"%1");
-  ASSERT_STREQ(Unencode(L"\x100"), L"\x100");
-  ASSERT_STREQ(Unencode(L"this is%20a%20wide%20char%20\x345"),
-               L"this is a wide char \x345");
-  ASSERT_STREQ(Unencode(L"a utf8 string %E7%BC%9c %E4%B8%8a = 2"),
-               L"a utf8 string \x7f1c \x4e0a = 2");
-}
-
-#if 0
-static const struct {
-  const char *ansi;
-  const TCHAR *wide;
-  UINT cp;
-} kAnsi2WideTests[] = {
-  { "\xc8\xae\xc1\xbe", L"\x72ac\x8f86", CP_GB2312},
-  { "\xa5\x69\xb1\x4e\xc2\xb2\xc5\xe9",
-    L"\x53ef\x5c07\x7c21\x9ad4", CP_BIG5},
-  { "\xE7\xBC\x96\xE4\xB8\x8B", L"\x7f16\x4e0b", CP_UTF8},
-  { "ascii", L"ascii", CP_GB2312},
-  { "\x3C\x20\xE7\xBC\x96", L"\x003c\x0020\x00E7\x00BC\x0096", 0 },
-};
-
-bool TestAnsiToWideString() {
-  for (size_t i = 0; i < arraysize(kAnsi2WideTests); ++i) {
-    CStringW out;
-    if (kAnsi2WideTests[i].cp == 0) {
-      out = AnsiToWideString(kAnsi2WideTests[i].ansi,
-                             strlen(kAnsi2WideTests[i].ansi));
-    } else {
-      AnsiToWideString(kAnsi2WideTests[i].ansi,
-                       strlen(kAnsi2WideTests[i].ansi),
-                       kAnsi2WideTests[i].cp, &out);
-    }
-    CHK(out == kAnsi2WideTests[i].wide);
-  }
-  return true;
-}
-#endif
-
-TEST(StringTest, Show) {
-  ASSERT_STREQ(Show(0), _T("0"));
-  ASSERT_STREQ(Show(1), _T("1"));
-  ASSERT_STREQ(Show(-1), _T("-1"));
-}
-
-
-// Test international strings.
-TEST(StringTest, International) {
-  CString tabs_by_lang[] = {
-      _T("Web    Prente    Groepe    Gids    "),                    // Afrikaans
-      _T("Web    Fotografitë    Grupet    Drejtoriumi    "),        // Albanian
-      // Amharic is missing, that doesn't show in normal windows fonts
-      _T("ويب    صور    مجموعات    الدليل     "),                    // Arabic
-      _T("Web   Şəkillər   Qruplar   Qovluq   "),                   // Azerbaijani
-      _T("Web   Irudiak   Taldeak   Direktorioa   "),               // Basque
-      _T("Ўэб    Малюнкі    Групы    Каталёг    "),                 // Belarusian
-      _T("Antorjal    Chitraboli    Gosthi    Bishoy-Talika    "),  // Bengali
-      _T("MakarJal    Chhaya    Jerow    Nirdeshika    "),          // Bihari
-      _T("Veb   Imeges   Gruoops   Durectury "),                    // Bork
-      _T("Internet    Slike    Grupe    Katalog    "),              // Bosnian
-      _T("Gwiad    Skeudennoù    Strolladoù    Roll    "),          // Breton
-      _T("Мрежата    Изображения    Групи    Директория    "),      // Bulgarian
-      _T("Web    Imatges    Grups    Directori    "),               // Catalan
-      _T("所有网站    图像    网上论坛    网页目录    "),                            // Chinese Simplified
-      _T("所有網頁    圖片    網上論壇    網頁目錄     "),                            // Chinese Traditional
-      _T("Web    Slike    Grupe    Imenik    "),                    // Croatian
-      _T("Web    Obrázky    Skupiny    Adresář    "),               // Czech
-      _T("Nettet    Billeder    Grupper    Katalog    "),           // Danish
-      _T("Het Internet    Afbeeldingen    Discussiegroepen    Gids    "),  // Dutch
-      _T("Web    Images    Gwoups    Diwectowy    "),               // Elmer
-      _T("Web    Images    Groups    News    Froogle    more »"),   // English
-      _T("TTT    Bildoj    Grupoj    Katalogo     "),               // Esperanto
-      _T("Veeb    Pildid    Grupid    Kataloog    "),               // Estonian
-      _T("Netið    Myndir    Bólkar    Øki    "),                   // Faroese
-      _T("Web    Mga Larawan    Mga Grupo    Direktoryo    "),      // Filipino
-      _T("Web    Kuvat    Keskusteluryhmät    Hakemisto    "),      // Finnish
-      _T("Web    Images    Groupes    Annuaire    Actualités    "),  // French
-      _T("Web   Printsjes   Diskusjegroepen   Directory   "),       // Frisian
-      _T("Web    Imaxes    Grupos    Directorio    "),              // Galician
-      _T("ინტერნეტი   სურათები   ჯგუფები   კატალოგი   "),                      // Georgian
-      _T("Web    Bilder    Groups    Verzeichnis    News    "),     // German
-      _T("Ιστός    Eικόνες    Ομάδες    Κατάλογος    "),            // Greek
-      _T("Ñanduti   Ta'anga   Atypy   Sãmbyhypy "),                 // Guarani
-      _T("jalu    Chhabi    Sangathan    Shabdakosh    "),          // Gujarati
-      _T("n0rM4L s33rCh    1|\\/|4935    6r00pZ    d1r3c70rY    "),  // Hacker
-      _T("אתרים ברשת    תמונות    קבוצות דיון    מדריך האתרים     "),  // Hebrew
-      _T("वेब    छवियाँ    समूह    निर्देशिका    "),                             // Hindi
-      _T("Web    Képek    Csoportok    Címtár    "),                // Hungarian
-      _T("Vefur    Myndir    Hópar    Flokkar    "),                // Icelandic
-      _T("Web    Gambar    Grup    Direktori    "),                 // Indonesian
-      _T("Web    Imagines    Gruppos    Catalogo  "),               // Interlingua
-      _T("An Gréasán    Íomhánna    Grúpaí    Eolaire    "),        // Irish
-      _T("Web    Immagini    Gruppi    Directory    News Novità!    "),  // Italian
-      _T("ウェブ    イメージ    グループ    ディレクトリ    "),                        // Japanese
-      _T("Web    Gambar - gambar    Paguyuban    Bagian    "),      // Javanese
-      _T("antharajAla    chitragaLu    gumpugaLu    Huduku vibhaagagaLu    "),  // Kannada
-      _T("Daqmey pat    naghmey beQ    ghommey    mem    "),        // Klingon
-      _T("웹 문서    이미지    뉴스그룹    디렉토리    "),                            // Klingon
-      _T("Желе   Суроттор   Группалар   Тизме   "),                 // Kyrgyz
-      _T("Tela   Imagines   Circuli   Index "),                     // Latin
-      _T("Internets    Attēli    Vēstkopas    Katalogs"),           // Latvian
-      _T("Internetas    Vaizdai    Grupės    Katalogas    "),       // Lithuanian
-      _T("Мрежа    Слики    Групи    Директориум    "),             // Macedonian
-      _T("Jaringan    Imej    Kumpulan    Direktori    "),          // Malay
-      _T("വെബ്    ചിത്രങ്ങള്    സംഘങ്ങള്    ഡയറക്ടറി    "),                     // Malayalam
-      _T("Web    Stampi    Gruppi    Direttorju    "),              // Maltese
-      _T("वेबशोध    चित्रशोध    ग्रूप्स    डिरेक्टरी    "),                          // Marathi
-      _T("वेब    तस्वीर    समूह    डाइरेक्टरी    "),                            // Nepali
-      _T("Nett    Bilder    Grupper    Katalog    "),               // Norwegian
-      _T("Veven    Bilete    Grupper    Katalog    "),              // Norwegian (Nynorsk)
-      _T("Ret    Imatges    Grops    Directori    "),               // Occitan
-      _T("web   chitra   goSThi   prasanga tAlikA "),               // Oriya
-      _T("وب    تصويرها    گروهها    فهرست     "),                  // Persian
-      _T("ebway    imagesyay    oupsgray    Irectoryday    "),      // P. Latin
-      _T("WWW    Grafika    Grupy dyskusyjne    Katalog   "),       // Polish
-      _T("Web    Imagens    Grupos    Diretório    "),              // Potruguese (Brazil)
-      _T("Web    Imagens    Grupos    Directório  "),               // Potruguese (Portugal)
-      _T("Web/Zaal    Tasveraan    Gutt    Directory    "),         // Punjabi
-      _T("Web    Imagini    Grupuri    Director    "),              // Romanian
-      _T("Веб    Картинки    Группы    Каталог  "),                 // Russian
-      _T("Lìon    Dealbhan    Cuantail    Eòlaire    "),            // Scots Gaelic
-      _T("Интернет    Слике    Групе    Каталог    "),              // Serbian
-      _T("Internet    Slike    Grupe    Spisak    "),               // Serbo-Croatian
-      _T("Web   Ponahalo   Dihlopha   Tshupetso "),                 // Sesotho
-      _T("WEB    Roopa    Kandayam    Namawaliya    "),             // Sinhalese
-      _T("Web    Obrázky    Skupiny    Katalóg    "),               // Slovak
-      _T("Internet    Slike    Skupine    Imenik    "),             // Slovenian
-      _T("La Web    Imágenes    Grupos    Directorio    News ¡Nuevo!    "),  // Spanish
-      _T("Web   Gambar   Grup   Direktori "),                       // Sudanese
-      _T("Mtandao    Picha    Vikundi    Orodha    "),              // Swahili
-      _T("Nätet    Bilder    Grupper    Kategori    "),             // Swedish
-      _T("வலை    படங்கள்    குழுக்கள்    விபரக்கோவை    "),          // Tamil
-      _T("వెబ్    చిత్రాలు    సమూహములు    darshini    "),                        // Telugu
-      _T("เว็บ    รูปภาพ    กลุ่มข่าว    สารบบเว็บ    "),                            // Thai
-      // Tigrinya is missing, that doesn't show in normal windows fonts
-      _T("Web    Grafikler    Gruplar    Dizin    "),               // Turkish
-      _T("Web   Suratlar   Toparlar   Düzine "),                    // Turkmen
-      _T("tintan   Nfonyin   Akuokuo   Krataa nhwemu "),            // Twi
-      _T("Веб    Зображення    Групи    Каталог    "),              // Ukrainian
-      _T("ويب    تصاوير    گروہ    فہرست         ")                           // Urdu
-      _T("To'r    Tasvirlar    Gruppalar    Papka    "),            // Uzbek
-      _T("Internet    Hình Ảnh    Nhóm    Thư Mục    "),            // Vietnamese
-      _T("Y We    Lluniau    Grwpiau    Cyfeiriadur    "),          // Welsh
-      _T("Web   Imifanekiso   Amaqela   Isilawuli "),               // Xhosa
-      _T("װעב    בילדער    גרופּעס    פּאַפּקע     "),                  // Yiddish
-      _T("I-web   Izithombe   Amaqembu   Uhlu lwamafayela   "),     // Zulu
-  };
-
-  int i = 0;
-  for(i = 0; i < arraysize(tabs_by_lang); ++i) {
-    // Get the cannonical lower version with ::CharLower
-    CString true_lower(tabs_by_lang[i]);
-    ::CharLower(true_lower.GetBuffer());
-    true_lower.ReleaseBuffer();
-
-    // Get the lower version with String_ToLower,
-    CString low_temp(tabs_by_lang[i]);
-    String_ToLower(low_temp.GetBuffer());
-    low_temp.ReleaseBuffer();
-
-    // make sure they match
-    ASSERT_STREQ(low_temp, true_lower);
-
-    // Now make sure they match letter by letter
-    for(int j = 0; j < tabs_by_lang[i].GetLength(); ++j) {
-      TCHAR cur_char = tabs_by_lang[i].GetAt(j);
-
-      TCHAR low1 = static_cast<TCHAR>(String_ToLowerChar(cur_char));
-
-      ASSERT_EQ(low1, true_lower.GetAt(j));
-      ASSERT_EQ(Char_ToLower(cur_char), true_lower.GetAt(j));
-
-      // Check the Ansi version if applicable
-      if (cur_char < 128)
-        ASSERT_EQ(String_ToLowerChar(static_cast<char>(cur_char)),
-                  true_lower.GetAt(j));
-    }
-
-    // Test out the CString conversion
-    CString temp(tabs_by_lang[i]);
-    MakeLowerCString(temp);
-    ASSERT_STREQ(temp, true_lower);
-
-    // Test out the fast version
-    temp = tabs_by_lang[i];
-    String_FastToLower(temp.GetBuffer());
-    temp.ReleaseBuffer();
-
-    ASSERT_STREQ(temp, true_lower);
-
-    // Make sure that the normal CString::Trim works the same as our fast one
-    CString trim_normal(tabs_by_lang[i]);
-    trim_normal.Trim();
-
-    CString trim_fast(tabs_by_lang[i]);
-    TrimCString(trim_fast);
-
-    ASSERT_STREQ(trim_normal, trim_fast);
-  }
-}
-
-void TestReplaceString (TCHAR *src, TCHAR *from, TCHAR *to, TCHAR *expected) {
-  ASSERT_TRUE(expected);
-  ASSERT_TRUE(to);
-  ASSERT_TRUE(from);
-  ASSERT_TRUE(src);
-
-  size_t new_src_size = _tcslen(src) + 1;
-  TCHAR* new_src = new TCHAR[new_src_size];
-
-  _tcscpy_s(new_src, new_src_size, src);
-
-  Timer tchar (false);
-  Timer tchar2 (false);
-  Timer cstring (false);
-  Timer orig_cstring (false);
-
-  // int iterations = 10000;
-  int iterations = 10;
-
-  int out_len;
-  TCHAR *out;
-
-  for (int i = 0; i < iterations; i++) {
-      _tcscpy_s(new_src, new_src_size, src);
-      bool created_new_string = false;
-
-      tchar.Start();
-      ReplaceString (new_src, from, to, &out, &out_len);
-      tchar.Stop();
-
-      ASSERT_STREQ(out, expected);
-      delete [] out;
-  }
-
-  for (int i = 0; i < iterations; i++) {
-      _tcscpy_s(new_src, new_src_size, src);
-      bool created_new_string = false;
-
-      tchar2.Start();
-      ReplaceStringMaybeInPlace (new_src, from, to, &out,
-                                 &out_len, &created_new_string);
-      tchar2.Stop();
-
-      ASSERT_STREQ(out, expected);
-      if (out != new_src) { delete [] out; }
-  }
-
-  for (int i = 0; i < iterations; i++) {
-      CString src_string(src);
-
-      orig_cstring.Start();
-      src_string.Replace (from, to);
-      orig_cstring.Stop();
-
-      ASSERT_STREQ(src_string, CString(expected));
-  }
-
-  for (int i = 0; i < iterations; i++) {
-      CString src_string(src);
-
-      cstring.Start();
-      ReplaceCString (src_string, from, to);
-      cstring.Stop();
-
-      ASSERT_STREQ(src_string, CString(expected));
-  }
-
-  delete [] new_src;
-}
-
-TEST(StringTest, ReplaceCString) {
-  CString t;
-  t = _T("a a a b ");
-  ReplaceCString(t, _T("a"), 1, _T("d"), 1, 5);
-  ASSERT_STREQ(_T("d d d b "), t);
-
-  t = _T("a a a b ");
-  ReplaceCString(t, _T("b"), 1, _T("d"), 1, 5);
-  ASSERT_STREQ(_T("a a a d "), t);
-
-  t = _T("a a a b ");
-  ReplaceCString(t, _T("a"), 1, _T("d"), 1, 1);
-  ASSERT_STREQ(_T("d a a b "), t);
-
-  t = _T("a a a b ");
-  ReplaceCString(t, _T("a"), 1, _T("dd"), 2, 5);
-  ASSERT_STREQ(_T("dd dd dd b "), t);
-
-  ReplaceCString(t, _T("dd"), 2, _T("dddd"), 4, 5);
-  ASSERT_STREQ(_T("dddd dddd dddd b "), t);
-
-  ReplaceCString(t, _T("dd"), 2, _T("dddd"), 4, 5);
-  ASSERT_STREQ(_T("dddddddd dddddddd dddddd b "), t);
-
-  ReplaceCString(t, _T("dddddddd"), 8, _T("dddd"), 4, 2);
-  ASSERT_STREQ(_T("dddd dddd dddddd b "), t);
-
-  ReplaceCString(t, _T("d"), 1, _T("a"), 1, 2);
-  ASSERT_STREQ(_T("aadd dddd dddddd b "), t);
-
-  ReplaceCString(t, _T("d d"), 3, _T("c"), 1, 2);
-  ASSERT_STREQ(_T("aadcddcddddd b "), t);
-
-  ReplaceCString(t, _T("c"), 1, _T("1234567890"), 10, 2);
-  ASSERT_STREQ(_T("aad1234567890dd1234567890ddddd b "), t);
-
-  ReplaceCString(t, _T("1"), 1, _T("1234567890"), 10, 2);
-  ASSERT_STREQ(_T("aad1234567890234567890dd1234567890234567890ddddd b "), t);
-
-  ReplaceCString(t, _T("1234567890"), 10, _T(""), 0, 2);
-  ASSERT_STREQ(_T("aad234567890dd234567890ddddd b "), t);
-
-  t = _T("a aa aa b ");
-  ReplaceCString(t, _T("aa"), 2, _T("b"), 1, 5);
-  ASSERT_STREQ(_T("a b b b "), t);
-
-  t = _T("moo a aa aa b ");
-  ReplaceCString(t, _T("aa"), 2, _T("b"), 1, 5);
-  ASSERT_STREQ(_T("moo a b b b "), t);
-
-  // Time to test some big strings
-  int test_sizes[] = {200, 500, 900, 10000};
-
-  int i;
-  for(i = 0; i < arraysize(test_sizes); ++i) {
-    CString in, out;
-    for(int j = 0; j < test_sizes[i]; ++j) {
-      in += L'a';
-      out += _T("bb");
-    }
-    CString bak_in(in);
-
-    // Make it a bit bigger
-    int times = ReplaceCString(in, _T("a"), 1, _T("bb"), 2, kRepMax);
-    ASSERT_EQ(times, test_sizes[i]);
-    ASSERT_EQ(out, in);
-
-    // Make it bigger still
-    times = ReplaceCString(in, _T("bb"), 2, _T("ccc"), 3, kRepMax);
-    ASSERT_EQ(times, test_sizes[i]);
-
-    // Same size swap
-    times = ReplaceCString(in, _T("c"), 1, _T("d"), 1, kRepMax);
-    ASSERT_EQ(times, test_sizes[i] * 3);
-
-    // Make it smaller again
-    times = ReplaceCString(in, _T("ddd"), 3, _T("a"), 1, kRepMax);
-    ASSERT_EQ(times, test_sizes[i]);
-    ASSERT_EQ(bak_in, in);
-  }
-}
-
-TEST(StringTest, GetField) {
-  CString s(_T("<a>a</a><b>123</b><c>aa\ndd</c>"));
-
-  CString a(GetField (s, L"a"));
-  ASSERT_STREQ(a, L"a");
-
-  CString b(GetField (s, L"b"));
-  ASSERT_STREQ(b, L"123");
-
-  CString c(GetField (s, L"c"));
-  ASSERT_STREQ(c, L"aa\ndd");
-}
-
-TEST(StringTest, String_HasAlphabetLetters) {
-  ASSERT_TRUE(String_HasAlphabetLetters (L"abc"));
-  ASSERT_TRUE(String_HasAlphabetLetters (L"X"));
-  ASSERT_TRUE(String_HasAlphabetLetters (L" pie "));
-  ASSERT_FALSE(String_HasAlphabetLetters (L"1"));
-  ASSERT_FALSE(String_HasAlphabetLetters (L"0"));
-  ASSERT_FALSE(String_HasAlphabetLetters (L"010"));
-  ASSERT_FALSE(String_HasAlphabetLetters (L"314-159"));
-  ASSERT_TRUE(String_HasAlphabetLetters (L"pie0"));
-}
-
-TEST(StringTest, String_LargeIntToApproximateString) {
-  int power;
-  ASSERT_TRUE(String_LargeIntToApproximateString(10LL, true, &power) == _T("10") && power == 0);
-  ASSERT_TRUE(String_LargeIntToApproximateString(99LL, true, &power) == _T("99") && power == 0);
-  ASSERT_TRUE(String_LargeIntToApproximateString(990LL, true, &power) == _T("990") && power == 0);
-  ASSERT_TRUE(String_LargeIntToApproximateString(999LL, true, &power) == _T("999") && power == 0);
-
-  ASSERT_TRUE(String_LargeIntToApproximateString(1000LL, true, &power) == _T("1.0") && power == 1);
-  ASSERT_TRUE(String_LargeIntToApproximateString(1200LL, true, &power) == _T("1.2") && power == 1);
-  ASSERT_TRUE(String_LargeIntToApproximateString(7500LL, true, &power) == _T("7.5") && power == 1);
-  ASSERT_TRUE(String_LargeIntToApproximateString(9900LL, true, &power) == _T("9.9") && power == 1);
-  ASSERT_TRUE(String_LargeIntToApproximateString(10000LL, true, &power) == _T("10") && power == 1);
-  ASSERT_TRUE(String_LargeIntToApproximateString(11000LL, true, &power) == _T("11") && power == 1);
-  ASSERT_TRUE(String_LargeIntToApproximateString(987654LL, true, &power) == _T("987") && power == 1);
-
-  ASSERT_TRUE(String_LargeIntToApproximateString(1000000LL, true, &power) == _T("1.0") && power == 2);
-  ASSERT_TRUE(String_LargeIntToApproximateString(1300000LL, true, &power) == _T("1.3") && power == 2);
-  ASSERT_TRUE(String_LargeIntToApproximateString(987654321LL, true, &power) == _T("987") && power == 2);
-
-  ASSERT_TRUE(String_LargeIntToApproximateString(1000000000LL, true, &power) == _T("1.0") && power == 3);
-  ASSERT_TRUE(String_LargeIntToApproximateString(1999999999LL, true, &power) == _T("1.9") && power == 3);
-  ASSERT_TRUE(String_LargeIntToApproximateString(20000000000LL, true, &power) == _T("20") && power == 3);
-  ASSERT_TRUE(String_LargeIntToApproximateString(1000000000000LL, true, &power) == _T("1000") && power == 3);
-  ASSERT_TRUE(String_LargeIntToApproximateString(12345678901234LL, true, &power) == _T("12345") && power == 3);
-
-  ASSERT_TRUE(String_LargeIntToApproximateString(1023LL, false, &power) == _T("1023") && power == 0);
-
-  ASSERT_TRUE(String_LargeIntToApproximateString(1024LL, false, &power) == _T("1.0") && power == 1);
-  ASSERT_TRUE(String_LargeIntToApproximateString(1134LL, false, &power) == _T("1.1") && power == 1);
-  ASSERT_TRUE(String_LargeIntToApproximateString(10240LL, false, &power) == _T("10") && power == 1);
-
-  ASSERT_TRUE(String_LargeIntToApproximateString(5242880LL, false, &power) == _T("5.0") && power == 2);
-
-  ASSERT_TRUE(String_LargeIntToApproximateString(1073741824LL, false, &power) == _T("1.0") && power == 3);
-  ASSERT_TRUE(String_LargeIntToApproximateString(17179869184LL, false, &power) == _T("16") && power == 3);
-}
-
-TEST(StringTest, FindWholeWordMatch) {
-  // words with spaces before / after
-  ASSERT_EQ(0, FindWholeWordMatch (L"pi", L"pi", false, 0));
-  ASSERT_EQ(1, FindWholeWordMatch (L" pi", L"pi", false, 0));
-  ASSERT_EQ(1, FindWholeWordMatch (L" pi ", L"pi", false, 0));
-  ASSERT_EQ(0, FindWholeWordMatch (L"pi ", L"pi", false, 0));
-
-  // partial matches
-  ASSERT_EQ(-1, FindWholeWordMatch (L"pie ", L"pi", false, 0));
-  ASSERT_EQ(-1, FindWholeWordMatch (L" pie ", L"pi", false, 0));
-  ASSERT_EQ(-1, FindWholeWordMatch (L"pie", L"pi", false, 0));
-  ASSERT_EQ(-1, FindWholeWordMatch (L" pie", L"pi", false, 0));
-
-  // partial match with non-alphanumeric chars
-  ASSERT_EQ(-1, FindWholeWordMatch (L" pumpkin_pie ", L"pie", false, 0));
-  ASSERT_EQ(-1, FindWholeWordMatch (L" pie_crust ", L"pie", false, 0));
-  ASSERT_EQ(-1, FindWholeWordMatch (L"tartar", L"tar", false, 0));
-  ASSERT_EQ(-1, FindWholeWordMatch (L"pie!", L"pie", false, 0));
-}
-
-TEST(StringTest, ReplaceWholeWord) {
-  CString str (L"pie");
-  ReplaceWholeWord (L"ie", L"..", false, &str);
-  ASSERT_STREQ(str, L"pie");
-
-  ReplaceWholeWord (L"pie", L"..", false, &str);
-  ASSERT_STREQ(str, L"..");
-
-  str = L"banana pie";
-  ReplaceWholeWord (L"pie", L"..", false, &str);
-  ASSERT_STREQ(str, L"banana ..");
-
-  str = L"banana pie";
-  ReplaceWholeWord (L"banana", L"..", false, &str);
-  ASSERT_STREQ(str, L".. pie");
-
-  str = L"banana pie";
-  ReplaceWholeWord (L"banana pie", L" .. ", false, &str);
-  ASSERT_STREQ(str, L" .. ");
-
-  str = L"banana pie";
-  ReplaceWholeWord (L"pi", L" .. ", false, &str);
-  ASSERT_STREQ(str, L"banana pie");
-
-  str = L"ishniferatsu";
-  ReplaceWholeWord (L"era", L" .. ", false, &str);
-  ASSERT_STREQ(str, L"ishniferatsu");
-
-  str = L"i i i hi ii i";
-  ReplaceWholeWord (L"i", L"you", false, &str);
-  ASSERT_STREQ(str, L"you you you hi ii you");
-
-  str = L"a nice cream cheese pie";
-  ReplaceWholeWord (L"cream cheese", L"..", false, &str);
-  ASSERT_STREQ(str, L"a nice .. pie");
-
-  // ---
-  // Test replacement with whitespace trimming
-
-  // Replace in the middle of the string.
-  str = L"a nice cream cheese pie";
-  ReplaceWholeWord (L"cream cheese", L"..", true, &str);
-  ASSERT_STREQ(str, L"a nice..pie");
-
-  // Replace in the beginning of the string.
-  str = L"a nice cream cheese pie";
-  ReplaceWholeWord (L"a nice", L"..", true, &str);
-  ASSERT_STREQ(str, L"..cream cheese pie");
-
-  // Replace in the end of the string.
-  str = L"a nice cream cheese pie";
-  ReplaceWholeWord (L"pie", L"..", true, &str);
-  ASSERT_STREQ(str, L"a nice cream cheese..");
-}
-
-
-TEST(StringTest, TestReplaceString) {
-  // timing for replace string, for the specific tests below shows:
-  //
-  // the TCHAR version is always faster than CRT CString::Replace
-  //
-  // the CString version is faster than CRT CString::Replace:
-  // - always if the replacement is shorter
-  // - if the source string is longer than ~60 characters if the replacement is
-  //   longer
-  //
-  // based on our current usage of CString::Replace, I expect the new CString
-  // version is faster on average than CRT CString::Replace
-  //
-  // non-CRT CString::Replace is much slower, so all of these should be much
-  // faster than that
-
-  TestReplaceString(L"that's what i changed -it was propagating the error code but i ..", L" .. ", L"<b> .. </b>", L"that's what i changed -it was propagating the error code but i ..");
-  TestReplaceString(L"news.com.url", L".url", L"", L"news.com");
-  TestReplaceString(L"news.com..url", L".url", L"", L"news.com.");
-  TestReplaceString(L"news.com.u.url", L".url", L"", L"news.com.u");
-  TestReplaceString(L"abanana pie banana", L"banana", L"c", L"ac pie c");
-  TestReplaceString(L"bananabananabanana", L"banana", L"c", L"ccc");
-  TestReplaceString(L"abanana pie banana", L"banana", L"cabanapie", L"acabanapie pie cabanapie");
-  TestReplaceString(L"bananabananabanana", L"banana", L"cabanapie", L"cabanapiecabanapiecabanapie");
-  TestReplaceString(L"banana pie banana pie", L"banana", L"c", L"c pie c pie");
-  TestReplaceString(L"banana pie banana pie", L"pie", L"z", L"banana z banana z");
-  TestReplaceString(L"banana pie banana pie", L"banana", L"bananacabana", L"bananacabana pie bananacabana pie");
-  TestReplaceString(L"banana pie banana pie", L"pie", L"pietie", L"banana pietie banana pietie");
-  TestReplaceString(L"banana pie banana pie", L"tie", L"pietie", L"banana pie banana pie");
-  TestReplaceString(L"banana pie banana pie banana pie banana pie banana pie", L"banana", L"bananacab", L"bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie");
-  TestReplaceString(L"banana pie banana pie banana pie banana pie banana pie banana pie banana pie", L"banana", L"bananacab", L"bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie");
-  TestReplaceString(L"banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie", L"banana", L"bananacab", L"bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie");
-  TestReplaceString(L"banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie", L"banana", L"bananacab", L"bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie bananacab pie");
-  TestReplaceString(L"banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie", L"banana", L"cab", L"cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie cab pie");
-  TestReplaceString(L"news", L"news", L"", L"");
-  TestReplaceString(L"&nbsp;", L"&nbsp;", L"", L"");
-  TestReplaceString(L"&nbsp;&nbsp;&nbsp;", L"&nbsp;", L"", L"");
-  TestReplaceString(L"&nbsp; &nbsp;&nbsp;", L"&nbsp;", L"", L" ");
-}
-
-
-TEST(StringTest, GetAbsoluteUri) {
-  ASSERT_STREQ(GetAbsoluteUri(L"http://www.google.com"),
-               L"http://www.google.com/");
-  ASSERT_STREQ(GetAbsoluteUri(L"http://www.google.com/"),
-               L"http://www.google.com/");
-  ASSERT_STREQ(GetAbsoluteUri(L"http://www.google.com//"),
-               L"http://www.google.com/");
-  ASSERT_STREQ(GetAbsoluteUri(L"http://www.google.com/test"),
-               L"http://www.google.com/test");
-}
-
-void TestTrim(const TCHAR *str, const TCHAR *result) {
-  ASSERT_TRUE(result);
-  ASSERT_TRUE(str);
-
-  size_t ptr_size = _tcslen(str) + 1;
-  TCHAR* ptr = new TCHAR[ptr_size];
-  _tcscpy_s(ptr, ptr_size, str);
-
-  int len = Trim(ptr);
-  ASSERT_STREQ(ptr, result);
-  ASSERT_EQ(len, lstrlen(result));
-
-  delete [] ptr;
-}
-
-TEST(StringTest, Trim) {
-  TestTrim(L"", L"");
-  TestTrim(L" ", L"");
-  TestTrim(L"\t", L"");
-  TestTrim(L"\n", L"");
-  TestTrim(L"\n\t    \t \n", L"");
-  TestTrim(L"    joe", L"joe");
-  TestTrim(L"joe      ", L"joe");
-  TestTrim(L"    joe      ", L"joe");
-  TestTrim(L"joe smith    ", L"joe smith");
-  TestTrim(L"     joe smith    ", L"joe smith");
-  TestTrim(L"     joe   smith    ", L"joe   smith");
-  TestTrim(L"     The quick brown fox,\tblah", L"The quick brown fox,\tblah");
-  TestTrim(L" \tblah\n    joe smith    ", L"blah\n    joe smith");
-}
-
-// IsSpaceA1 is much faster without the cache clearing (which is what happends
-// in release mode)
-// IsSpaceA1 is roughly the same speed as IsSpaceA2 with cache clearing (in
-// debug mode)
-// IsSpaceA3 is always much slower
-
-static const byte spacesA[256] = {
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 1,  // 0-9
-  1, 1, 1, 1, 0, 0, 0, 0, 0, 0,  // 10-19
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 20-29
-  0, 0, 1, 0, 0, 0, 0, 0, 0, 0,  // 30-39
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 40-49
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 50-59
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 60-69
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 70-79
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 80-89
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 90-99
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 100-109
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 110-119
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 120-129
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 130-139
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 140-149
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 150-159
-  1, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 160-169
-};
-
-bool IsSpaceA1(char c) {
-  return spacesA[c] == 1;
-}
-
-bool IsSpaceA2(char c) {
-  // return (c==32);
-  // if (c>32) { return 0; }
-  // most characters >32, check first for that case
-  if (c>32 && c!=160) { return 0; }
-  if (c==32) { return 1; }
-  if (c>=9&&c<=13) return 1; else return 0;
-}
-
-bool IsSpaceA3(char c) {
-  WORD result;
-  if (GetStringTypeA(0, CT_CTYPE1, &c, 1, &result)) {
-    return (0 != (result & C1_SPACE));
-  }
-  return false;
-}
-
-void TestIsSpace (char *s) {
-    ASSERT_TRUE(s);
-
-    Timer t1 (false);
-    Timer t2 (false);
-    Timer t3 (false);
-
-    // int iterations = 10000;
-    int iterations = 100;
-    int len = strlen (s);
-
-    // used to try to clear the processor cache
-    int dlen = 100000;
-    char *dummy = new char [dlen];
-    for (int i = 0; i < dlen; i++) {
-      dummy[i] = static_cast<char>(tr_rand() % 256);
-    }
-
-    int num_spaces = 0;
-    int n = iterations * len;
-    for (int i = 0; i < iterations; i++) {
-        t1.Start();
-        for (int j = 0; j < len; j++) {
-            num_spaces += IsSpaceA1 (s[j]);
-        }
-        t1.Stop();
-        // this cache clearing code gets optimized out in release mode
-        int d2 = 0;
-        for (int i = 0; i < dlen; i++) { d2 += dummy[i]; }
-    }
-
-    num_spaces = 0;
-    for (int i = 0; i < iterations; i++) {
-        t2.Start();
-        for (int j = 0; j < len; j++) {
-            num_spaces += IsSpaceA2 (s[j]);
-        }
-        t2.Stop();
-        int d2 = 0;
-        for (int i = 0; i < dlen; i++) { d2 += dummy[i]; }
-    }
-
-    num_spaces = 0;
-    for (int i = 0; i < iterations; i++) {
-        t3.Start();
-        for (int j = 0; j < len; j++) {
-            num_spaces += IsSpaceA3 (s[j]);
-        }
-        t3.Stop();
-        int d2 = 0;
-        for (int i = 0; i < dlen; i++) { d2 += dummy[i]; }
-    }
-}
-
-TEST(StringTest, IsSpace) {
-  TestIsSpace("banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie banana pie");
-  TestIsSpace("sdlfhdkgheorutsgj sdlj aoi oaj gldjg opre gdsfjng oate yhdnv ;zsj fpoe v;kjae hgpaieh dajlgn aegh avn WEIf h9243y 9814cu 902t7 9[-32 [O8W759 RC90817 V9pDAHc n( ny(7LKFJAOISF *&^*^%$$%#*&^(*_*)_^& 67% 796%&$*^$ 8)6 (^ 08&^ )*^ 9-7=90z& +(^ )^* %9%4386 $& (& &+ 7- &(_* ");
-}
-
-void TestCleanupWhitespace(const TCHAR *str, const TCHAR *result) {
-  ASSERT_TRUE(result);
-  ASSERT_TRUE(str);
-
-  size_t ptr_size = _tcslen(str) + 1;
-  TCHAR* ptr = new TCHAR[ptr_size];
-  _tcscpy_s(ptr, ptr_size, str);
-
-  int len = CleanupWhitespace(ptr);
-  ASSERT_STREQ(ptr, result);
-  ASSERT_EQ(len, lstrlen(result));
-
-  delete [] ptr;
-}
-
-TEST(StringTest, CleanupWhitespace) {
-  TestCleanupWhitespace(L"", L"");
-  TestCleanupWhitespace(L"a    ", L"a");
-  TestCleanupWhitespace(L"    a", L"a");
-  TestCleanupWhitespace(L" a   ", L"a");
-  TestCleanupWhitespace(L"\t\n\r a   ", L"a");
-  TestCleanupWhitespace(L"  \n   a  \t   \r ", L"a");
-  TestCleanupWhitespace(L"a      b", L"a b");
-  TestCleanupWhitespace(L"   a \t\n\r     b", L"a b");
-  TestCleanupWhitespace(L"   vool                 voop", L"vool voop");
-  TestCleanupWhitespace(L"thisisaverylongstringwithsometext",
-                        L"thisisaverylongstringwithsometext");
-  TestCleanupWhitespace(L"thisisavery   longstringwithsometext",
-                        L"thisisavery longstringwithsometext");
-}
-
-void TestWcstoul (TCHAR *string, int radix, unsigned long expected) {
-    ASSERT_TRUE(string);
-
-    wchar_t *ptr;
-    int v = Wcstoul (string, &ptr, radix);
-    ASSERT_EQ(v, expected);
-
-#ifdef DEBUG
-    int v2 = wcstoul (string, &ptr, radix);
-    ASSERT_EQ(v, v2);
-#endif
-}
-
-TEST(StringTest, Wcstoul) {
-  TestWcstoul(L"625", 16, 1573);
-  TestWcstoul(L" 625", 16, 1573);
-  TestWcstoul(L"a3", 16, 163);
-  TestWcstoul(L"A3", 16, 163);
-  TestWcstoul(L"  A3", 16, 163);
-  TestWcstoul(L" 12445", 10, 12445);
-  TestWcstoul(L"12445778", 10, 12445778);
-}
-
-TEST(StringTest, IsDigit) {
-  ASSERT_TRUE(String_IsDigit('0'));
-  ASSERT_TRUE(String_IsDigit('1'));
-  ASSERT_TRUE(String_IsDigit('2'));
-  ASSERT_TRUE(String_IsDigit('3'));
-  ASSERT_TRUE(String_IsDigit('4'));
-  ASSERT_TRUE(String_IsDigit('5'));
-  ASSERT_TRUE(String_IsDigit('6'));
-  ASSERT_TRUE(String_IsDigit('7'));
-  ASSERT_TRUE(String_IsDigit('8'));
-  ASSERT_TRUE(String_IsDigit('9'));
-  ASSERT_FALSE(String_IsDigit('a'));
-  ASSERT_FALSE(String_IsDigit('b'));
-  ASSERT_FALSE(String_IsDigit('z'));
-  ASSERT_FALSE(String_IsDigit('A'));
-  ASSERT_FALSE(String_IsDigit(' '));
-  ASSERT_FALSE(String_IsDigit('#'));
-}
-
-TEST(StringTest, IsUpper) {
-  ASSERT_FALSE(String_IsUpper('0'));
-  ASSERT_FALSE(String_IsUpper(' '));
-  ASSERT_FALSE(String_IsUpper('#'));
-  ASSERT_FALSE(String_IsUpper('a'));
-  ASSERT_FALSE(String_IsUpper('z'));
-  ASSERT_TRUE(String_IsUpper('A'));
-  ASSERT_TRUE(String_IsUpper('B'));
-  ASSERT_TRUE(String_IsUpper('C'));
-  ASSERT_TRUE(String_IsUpper('D'));
-  ASSERT_TRUE(String_IsUpper('H'));
-  ASSERT_TRUE(String_IsUpper('Y'));
-  ASSERT_TRUE(String_IsUpper('Z'));
-}
-
-TEST(StringTest, StringToDouble) {
-  ASSERT_DOUBLE_EQ(String_StringToDouble(L"625"), 625);
-  ASSERT_DOUBLE_EQ(String_StringToDouble(L"-625"), -625);
-  ASSERT_DOUBLE_EQ(String_StringToDouble(L"-6.25"), -6.25);
-  ASSERT_DOUBLE_EQ(String_StringToDouble(L"6.25"), 6.25);
-  ASSERT_DOUBLE_EQ(String_StringToDouble(L"0.00"), 0);
-  ASSERT_DOUBLE_EQ(String_StringToDouble(L" 55.1"), 55.1);
-  ASSERT_DOUBLE_EQ(String_StringToDouble(L" 55.001"), 55.001);
-  ASSERT_DOUBLE_EQ(String_StringToDouble(L"  1.001"), 1.001);
-}
-
-TEST(StringTest, StringToInt) {
-  ASSERT_EQ(String_StringToInt(L"625"), 625);
-  ASSERT_EQ(String_StringToInt(L"6"), 6);
-  ASSERT_EQ(String_StringToInt(L"0"), 0);
-  ASSERT_EQ(String_StringToInt(L" 122"), 122);
-  ASSERT_EQ(String_StringToInt(L"a"), 0);
-  ASSERT_EQ(String_StringToInt(L" a"), 0);
-}
-
-TEST(StringTest, StringToInt64) {
-  ASSERT_EQ(String_StringToInt64(L"119600064000000000"),
-            119600064000000000uI64);
-  ASSERT_EQ(String_StringToInt64(L" 119600064000000000"),
-            119600064000000000uI64);
-  ASSERT_EQ(String_StringToInt64(L"625"), 625);
-  ASSERT_EQ(String_StringToInt64(L"6"), 6);
-  ASSERT_EQ(String_StringToInt64(L"0"), 0);
-  ASSERT_EQ(String_StringToInt64(L" 122"), 122);
-  ASSERT_EQ(String_StringToInt64(L"a"), 0);
-  ASSERT_EQ(String_StringToInt64(L" a"), 0);
-}
-
-void TestEndWithChar(const TCHAR *s, char c, const TCHAR *expected) {
-  ASSERT_TRUE(expected);
-  ASSERT_TRUE(s);
-
-  TCHAR buf[5000];
-  _tcscpy(buf, s);
-  String_EndWithChar(buf, c);
-  ASSERT_STREQ(buf, expected);
-}
-
-TEST(StringTest, EndWithChar) {
-  TestEndWithChar(L"", L'a', L"a");
-  TestEndWithChar(L"", L'\\', L"\\");
-  TestEndWithChar(L"a", L'a', L"a");
-  TestEndWithChar(L"a", L'b', L"ab");
-  TestEndWithChar(L"abcdefghij", L'a', L"abcdefghija");
-  TestEndWithChar(L"abcdefghij", L'\\', L"abcdefghij\\");
-}
-
-TEST(StringTest, HexDigitToInt) {
-  ASSERT_EQ(HexDigitToInt(L'0'), 0);
-  ASSERT_EQ(HexDigitToInt(L'1'), 1);
-  ASSERT_EQ(HexDigitToInt(L'2'), 2);
-  ASSERT_EQ(HexDigitToInt(L'3'), 3);
-  ASSERT_EQ(HexDigitToInt(L'4'), 4);
-  ASSERT_EQ(HexDigitToInt(L'5'), 5);
-  ASSERT_EQ(HexDigitToInt(L'6'), 6);
-  ASSERT_EQ(HexDigitToInt(L'7'), 7);
-  ASSERT_EQ(HexDigitToInt(L'8'), 8);
-  ASSERT_EQ(HexDigitToInt(L'9'), 9);
-  ASSERT_EQ(HexDigitToInt(L'A'), 10);
-  ASSERT_EQ(HexDigitToInt(L'a'), 10);
-  ASSERT_EQ(HexDigitToInt(L'B'), 11);
-  ASSERT_EQ(HexDigitToInt(L'b'), 11);
-  ASSERT_EQ(HexDigitToInt(L'C'), 12);
-  ASSERT_EQ(HexDigitToInt(L'c'), 12);
-  ASSERT_EQ(HexDigitToInt(L'D'), 13);
-  ASSERT_EQ(HexDigitToInt(L'd'), 13);
-  ASSERT_EQ(HexDigitToInt(L'E'), 14);
-  ASSERT_EQ(HexDigitToInt(L'e'), 14);
-  ASSERT_EQ(HexDigitToInt(L'F'), 15);
-  ASSERT_EQ(HexDigitToInt(L'f'), 15);
-}
-
-TEST(StringTest, IsHexDigit) {
-  ASSERT_TRUE(IsHexDigit(L'0'));
-  ASSERT_TRUE(IsHexDigit(L'1'));
-  ASSERT_TRUE(IsHexDigit(L'2'));
-  ASSERT_TRUE(IsHexDigit(L'3'));
-  ASSERT_TRUE(IsHexDigit(L'4'));
-  ASSERT_TRUE(IsHexDigit(L'5'));
-  ASSERT_TRUE(IsHexDigit(L'6'));
-  ASSERT_TRUE(IsHexDigit(L'7'));
-  ASSERT_TRUE(IsHexDigit(L'8'));
-  ASSERT_TRUE(IsHexDigit(L'9'));
-  ASSERT_TRUE(IsHexDigit(L'a'));
-  ASSERT_TRUE(IsHexDigit(L'A'));
-  ASSERT_TRUE(IsHexDigit(L'b'));
-  ASSERT_TRUE(IsHexDigit(L'B'));
-  ASSERT_TRUE(IsHexDigit(L'c'));
-  ASSERT_TRUE(IsHexDigit(L'C'));
-  ASSERT_TRUE(IsHexDigit(L'd'));
-  ASSERT_TRUE(IsHexDigit(L'D'));
-  ASSERT_TRUE(IsHexDigit(L'e'));
-  ASSERT_TRUE(IsHexDigit(L'E'));
-  ASSERT_TRUE(IsHexDigit(L'f'));
-  ASSERT_TRUE(IsHexDigit(L'F'));
-
-  for(TCHAR digit = static_cast<TCHAR>(127); digit < 10000; ++digit) {
-    ASSERT_FALSE(IsHexDigit(digit));
-  }
-}
-
-TEST(StringTest, Remove) {
-  CString temp_remove;
-
-  // Remove everything
-  temp_remove = _T("ftp://");
-  RemoveFromStart (temp_remove, _T("ftp://"), false);
-  ASSERT_STREQ(temp_remove, _T(""));
-
-  // Remove all but 1 letter
-  temp_remove = _T("ftp://a");
-  RemoveFromStart (temp_remove, _T("ftp://"), false);
-  ASSERT_STREQ(temp_remove, _T("a"));
-
-  // Remove the first instance
-  temp_remove = _T("ftp://ftp://");
-  RemoveFromStart (temp_remove, _T("ftp://"), false);
-  ASSERT_STREQ(temp_remove, _T("ftp://"));
-
-  // Remove normal
-  temp_remove = _T("ftp://taz the tiger");
-  RemoveFromStart (temp_remove, _T("ftp://"), false);
-  ASSERT_STREQ(temp_remove, _T("taz the tiger"));
-
-  // Wrong prefix
-  temp_remove = _T("ftp:/taz the tiger");
-  RemoveFromStart (temp_remove, _T("ftp://"), false);
-  ASSERT_STREQ(temp_remove, _T("ftp:/taz the tiger"));
-
-  // Not long enough
-  temp_remove = _T("ftp:/");
-  RemoveFromStart (temp_remove, _T("ftp://"), false);
-  ASSERT_STREQ(temp_remove, _T("ftp:/"));
-
-  // Remove nothing
-  temp_remove = _T("ftp:/");
-  RemoveFromStart (temp_remove, _T(""), false);
-  ASSERT_STREQ(temp_remove, _T("ftp:/"));
-
-  // Remove 1 character
-  temp_remove = _T("ftp:/");
-  RemoveFromStart (temp_remove, _T("f"), false);
-  ASSERT_STREQ(temp_remove, _T("tp:/"));
-
-  // Wrong case
-  temp_remove = _T("ftp:/");
-  RemoveFromStart (temp_remove, _T("F"), false);
-  ASSERT_STREQ(temp_remove, _T("ftp:/"));
-
-  // Remove everything
-  temp_remove = _T(".edu");
-  RemoveFromEnd (temp_remove, _T(".edu"));
-  ASSERT_STREQ(temp_remove, _T(""));
-
-  // Remove all but 1 letter
-  temp_remove = _T("a.edu");
-  RemoveFromEnd(temp_remove, _T(".edu"));
-  ASSERT_STREQ(temp_remove, _T("a"));
-
-  // Remove the first instance
-  temp_remove = _T(".edu.edu");
-  RemoveFromEnd(temp_remove, _T(".edu"));
-  ASSERT_STREQ(temp_remove, _T(".edu"));
-
-  // Remove normal
-  temp_remove = _T("ftp://taz the tiger.edu");
-  RemoveFromEnd(temp_remove, _T(".edu"));
-  ASSERT_STREQ(temp_remove, _T("ftp://taz the tiger"));
-
-  // Wrong suffix
-  temp_remove = _T("ftp:/taz the tiger.edu");
-  RemoveFromEnd(temp_remove, _T("/edu"));
-  ASSERT_STREQ(temp_remove, _T("ftp:/taz the tiger.edu"));
-
-  // Not long enough
-  temp_remove = _T("edu");
-  RemoveFromEnd(temp_remove, _T(".edu"));
-  ASSERT_STREQ(temp_remove, _T("edu"));
-
-  // Remove nothing
-  temp_remove = _T(".edu");
-  RemoveFromEnd(temp_remove, _T(""));
-  ASSERT_STREQ(temp_remove, _T(".edu"));
-
-  // Remove 1 character
-  temp_remove = _T(".edu");
-  RemoveFromEnd(temp_remove, _T("u"));
-  ASSERT_STREQ(temp_remove, _T(".ed"));
-
-  // Wrong case
-  temp_remove = _T(".edu");
-  RemoveFromEnd(temp_remove, _T("U"));
-  ASSERT_STREQ(temp_remove, _T(".edu"));
-}
-
-TEST(StringTest, WideToAnsiDirect) {
-  CString temp_convert;
-  ASSERT_STREQ("", WideToAnsiDirect(_T("")));
-  ASSERT_STREQ("a", WideToAnsiDirect(_T("a")));
-  ASSERT_STREQ("moon doggy", WideToAnsiDirect(_T("moon doggy")));
-
-  // Generate a string of all characters 0-255.
-  const int kNumChars = 256;
-  TCHAR nasty_chars[kNumChars];
-  for (int i = 0; i < kNumChars; ++i) {
-    nasty_chars[i] = static_cast<TCHAR>(i);
-  }
-  CString temp(nasty_chars, kNumChars);
-
-  // Convert it and make sure it matches.
-  CStringA out = WideToAnsiDirect(temp);
-  ASSERT_EQ(out.GetLength(), kNumChars);
-  for (int i = 0; i < kNumChars; ++i) {
-    ASSERT_EQ(static_cast<unsigned char>(nasty_chars[i]),
-              static_cast<unsigned char>(out.GetAt(i)));
-  }
-}
-
-TEST(StringTest, FindStringASpaceStringB) {
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-type: text/html", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-TYPE: text/html", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-TYPE: text/HTML", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-TYPE: text/HTML", L"content-type:", L"text/HTML"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-TYPE: text/HTML", L"content-TYPE:", L"text/HTML"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:text/html", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:  text/html", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:   text/html", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-type: sdfjsldkgjsdg content-type:    text/html", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-type: content-type: sdfjsldkgjsdg content-type:    text/html", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:content-type: sdfjsldkgjsdg content-type:    text/html", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:content-type:    text/html", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"test/html content-type:content-type:    text/html", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:    text/html", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:\ttext/html", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:\t text/html", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"Content-Type:\t text/html", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"aasd content-type: text/html", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"aa content-TYPE: text/html", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"text.html  content-TYPE: text/HTML", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"text/html content-TYPE: text/HTML", L"content-type:", L"text/HTML"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"AAAA content-TYPE: text/HTML", L"content-TYPE:", L"text/HTML"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:text/html AAAAA", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:  text/html", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:   text/htmlaaa", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:    text/html  asdsdg content-type", L"content-type:", L"text/html"));
-  ASSERT_TRUE(FindStringASpaceStringB(L"content-type:\ttext/htmlconttent-type:te", L"content-type:", L"text/html"));
-
-  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:  a  text/html", L"content-type:", L"text/html"));
-  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:  content-type:  a  text/html", L"content-type:", L"text/html"));
-  ASSERT_FALSE(FindStringASpaceStringB(L"content-type: b text/html  content-type:  a  text/html", L"content-type:", L"text/html"));
-  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:-text/html", L"content-type:", L"text/html"));
-  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:\ntext/html", L"content-type:", L"text/html"));
-  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:  a  TEXT/html", L"content-type:", L"text/html"));
-  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:  a  html/text", L"content-type:", L"text/html"));
-  ASSERT_FALSE(FindStringASpaceStringB(L"a dss content-type:  a  text/html", L"content-type:", L"text/html"));
-  ASSERT_FALSE(FindStringASpaceStringB(L"text/html content-type:-text/html", L"content-type:", L"text/html"));
-  ASSERT_FALSE(FindStringASpaceStringB(L"text/html sdfsd fcontent-type:\ntext/html", L"content-type:", L"text/html"));
-  ASSERT_FALSE(FindStringASpaceStringB(L"AAAA content-type:  a  TEXT/html", L"content-type:", L"text/html"));
-  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:  a  html/text AAA", L"content-type:", L"text/html"));
-  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:", L"content-type:", L"text/html"));
-  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:content-type:", L"content-type:", L"text/html"));
-  ASSERT_FALSE(FindStringASpaceStringB(L"content-type:content-type: content-type:", L"content-type:", L"text/html"));
-}
-
-TEST(StringTest, ElideIfNeeded) {
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 3, 3), L"1..");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 4, 3), L"12..");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 5, 3), L"123..");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 6, 3), L"1234..");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 7, 3), L"1234..");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 8, 3), L"1234..");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 9, 3), L"1234..");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 10, 3), L"1234..");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 11, 3), L"1234 6789..");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 12, 3), L"1234 6789..");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 13, 3), L"1234 6789..");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 14, 3), L"1234 6789 1234");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 15, 3), L"1234 6789 1234");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 16, 3), L"1234 6789 1234");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 17, 3), L"1234 6789 1234");
-
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 7, 6), L"1234..");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 8, 6), L"1234 6..");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 9, 6), L"1234 67..");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 10, 6), L"1234 678..");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 11, 6), L"1234 6789..");
-  ASSERT_STREQ(ElideIfNeeded(L"1234 6789 1234", 12, 6), L"1234 6789..");
-}
-
-TEST(StringTest, SafeStrCat) {
-  const int kDestLen = 7;
-  TCHAR dest[kDestLen];
-  lstrcpyn(dest, L"short", kDestLen);
-  ASSERT_LT(lstrlen(dest), kDestLen);
-
-  dest[kDestLen-1] = 'a';
-  lstrcpyn(dest, L"medium123", kDestLen);
-  ASSERT_EQ(dest[kDestLen - 1], '\0');
-  ASSERT_LT(lstrlen(dest), kDestLen);
-
-  lstrcpyn(dest, L"longerlonger", kDestLen);
-  ASSERT_EQ(dest[kDestLen - 1], '\0');
-  ASSERT_LT(lstrlen(dest), kDestLen);
-
-  lstrcpyn(dest, L"12", kDestLen);
-  SafeStrCat(dest, L"3456", kDestLen);
-  ASSERT_EQ(dest[kDestLen - 1], '\0');
-  ASSERT_LT(lstrlen(dest), kDestLen);
-}
-
-void TestPathFindExtension(const TCHAR *s) {
-  ASSERT_STREQ(String_PathFindExtension(s), PathFindExtension(s));
-}
-
-TEST(StringTest, TestPathFindExtension) {
-  TestPathFindExtension(L"c:\\test.tmp");
-  TestPathFindExtension(L"c:\\test.temp");
-  TestPathFindExtension(L"c:\\t\\e\\st.temp");
-  TestPathFindExtension(L"c:\\a.temp");
-  TestPathFindExtension(L"\\aaa\\a.temp");
-  TestPathFindExtension(L"\\a\\a.temp");
-  TestPathFindExtension(L"\\a\\a.temp");
-  TestPathFindExtension(L"\\a\\a.t....emp");
-  TestPathFindExtension(L"\\a.a.a...a\\a.t....emp");
-  TestPathFindExtension(L"\\a\\a\\bbbb\\ddddddddddddddd.temp");
-  TestPathFindExtension(L"\\a\\a\\bbbb\\ddddddddddddddd.te___124567mp");
-  TestPathFindExtension(L"\\a\\a\\bbbb\\ddddddd.dddddddd.te___124567mp");
-}
-
-TEST(StringTest, TextToLinesAndBack) {
-  const TCHAR sample_input[]  = L"Now is the time\r\nfor all good men\r\nto come to the aid of their country";
-  const TCHAR* sample_lines[] = { L"Now is the time", L"for all good men", L"to come to the aid of their country" };
-  const TCHAR sample_output1[] = L"Now is the time\nfor all good men\nto come to the aid of their country\n";
-  const TCHAR sample_output2[] = L"Now is the timefor all good mento come to the aid of their country";
-
-  CString text_in(sample_input);
-  std::vector<CString> lines;
-  CString text_out;
-
-  TextToLines(text_in, L"\r\n", &lines);
-  ASSERT_EQ(lines.size(), 3);
-  for (size_t i = 0; i < arraysize(sample_lines); ++i) {
-    ASSERT_TRUE(0 == lines[i].Compare(sample_lines[i]));
-  }
-  LinesToText(lines, L"\n", &text_out);
-  ASSERT_TRUE(0 == text_out.Compare(sample_output1));
-  LinesToText(lines, L"", &text_out);
-  ASSERT_TRUE(0 == text_out.Compare(sample_output2));
-}
-
-CString TrimStdString(const TCHAR* str) {
-  CString s(str);
-  TrimString(s, L" \t");
-  return s;
-}
-
-TEST(StringTest, TrimString) {
-  ASSERT_STREQ(L"abc", TrimStdString(L"abc"));
-  ASSERT_STREQ(L"abc", TrimStdString(L" abc "));
-  ASSERT_STREQ(L"a c", TrimStdString(L" a c  "));
-  ASSERT_STREQ(L"abc", TrimStdString(L" \tabc\t "));
-  ASSERT_STREQ(L"", TrimStdString(L""));
-  ASSERT_STREQ(L"", TrimStdString(L"   "));
-}
-
-TEST(StringTest, StripFirstQuotedToken) {
-  ASSERT_STREQ(StripFirstQuotedToken(L""), L"");
-  ASSERT_STREQ(StripFirstQuotedToken(L"a" ), L"");
-  ASSERT_STREQ(StripFirstQuotedToken(L"  a b  "), L"b");
-  ASSERT_STREQ(StripFirstQuotedToken(L"\"abc\" def"), L" def");
-  ASSERT_STREQ(StripFirstQuotedToken(L"  \"abc def\" ghi  "), L" ghi");
-  ASSERT_STREQ(StripFirstQuotedToken(L"\"abc\"   \"def\" "), L"   \"def\"");
-}
-
-TEST(StringTest, EscapeUnescape) {
-  CString original_str(_T("test <>\"#{}|\\^[]?%&/"));
-  CString escaped_str;
-  ASSERT_SUCCEEDED(StringEscape(original_str, true, &escaped_str));
-  ASSERT_STREQ(escaped_str,
-               _T("test%20%3C%3E%22%23%7B%7D%7C%5C%5E%5B%5D%3F%25%26%2F"));
-  CString unescaped_str;
-  ASSERT_SUCCEEDED(StringUnescape(escaped_str, &unescaped_str));
-  ASSERT_STREQ(original_str, unescaped_str);
-
-  original_str = _T("foo.test path?app=1");
-  ASSERT_SUCCEEDED(StringEscape(original_str, false, &escaped_str));
-  ASSERT_STREQ(escaped_str,
-               _T("foo.test%20path?app=1"));
-  ASSERT_SUCCEEDED(StringUnescape(escaped_str, &unescaped_str));
-  ASSERT_STREQ(original_str, unescaped_str);
-}
-
-TEST(StringTest, String_StringToDecimalIntChecked) {
-  int value = 0;
-
-  // This code before the first valid case verifies that errno is properly
-  // cleared and there are no dependencies on prior code.
-  EXPECT_EQ(0, _set_errno(ERANGE));
-
-  // Valid Cases
-  EXPECT_TRUE(String_StringToDecimalIntChecked(_T("935"), &value));
-  EXPECT_EQ(value, 935);
-  EXPECT_TRUE(String_StringToDecimalIntChecked(_T("-935"), &value));
-  EXPECT_EQ(value, -935);
-  EXPECT_TRUE(String_StringToDecimalIntChecked(_T("0"), &value));
-  EXPECT_EQ(value, 0);
-  EXPECT_TRUE(String_StringToDecimalIntChecked(_T("2147483647"), &value));
-  EXPECT_EQ(value, LONG_MAX);
-  EXPECT_TRUE(String_StringToDecimalIntChecked(_T("-2147483648"), &value));
-  EXPECT_EQ(value, LONG_MIN);
-  EXPECT_TRUE(String_StringToDecimalIntChecked(_T(" 0"), &value));
-  EXPECT_EQ(value, 0);
-
-  // Failing Cases
-  EXPECT_FALSE(String_StringToDecimalIntChecked(_T(""), &value));
-  EXPECT_FALSE(String_StringToDecimalIntChecked(_T("2147483648"), &value));
-  EXPECT_EQ(value, LONG_MAX);
-  EXPECT_FALSE(String_StringToDecimalIntChecked(_T("-2147483649"), &value));
-  EXPECT_EQ(value, LONG_MIN);
-  EXPECT_FALSE(String_StringToDecimalIntChecked(_T("0x935"), &value));
-  EXPECT_FALSE(String_StringToDecimalIntChecked(_T("nine"), &value));
-  EXPECT_FALSE(String_StringToDecimalIntChecked(_T("9nine"), &value));
-  EXPECT_FALSE(String_StringToDecimalIntChecked(_T("nine9"), &value));
-
-  // A valid case after an overflow verifies that this method clears errno.
-  EXPECT_FALSE(String_StringToDecimalIntChecked(_T("2147483648"), &value));
-  EXPECT_TRUE(String_StringToDecimalIntChecked(_T("935"), &value));
-}
-TEST(StringTest, String_StringToTristate) {
-  Tristate value = TRISTATE_NONE;
-
-  // Valid Cases
-  EXPECT_TRUE(String_StringToTristate(_T("0"), &value));
-  EXPECT_EQ(value, TRISTATE_FALSE);
-  EXPECT_TRUE(String_StringToTristate(_T("1"), &value));
-  EXPECT_EQ(value, TRISTATE_TRUE);
-  EXPECT_TRUE(String_StringToTristate(_T("2"), &value));
-  EXPECT_EQ(value, TRISTATE_NONE);
-
-  // Invalid Cases
-  EXPECT_FALSE(String_StringToTristate(_T("-1"), &value));
-  EXPECT_FALSE(String_StringToTristate(_T("3"), &value));
-  EXPECT_FALSE(String_StringToTristate(_T(""), &value));
-}
-
-TEST(StringTest, ParseNameValuePair) {
-  CString name;
-  CString value;
-
-  // Valid Cases
-  EXPECT_TRUE(ParseNameValuePair(_T("xx=yyzz"), _T('='), &name, &value));
-  EXPECT_EQ(name, _T("xx"));
-  EXPECT_EQ(value, _T("yyzz"));
-  EXPECT_TRUE(ParseNameValuePair(_T("x=3?\\/\r\n "), _T('='), &name, &value));
-  EXPECT_EQ(name, _T("x"));
-  EXPECT_EQ(value, _T("3?\\/\r\n "));
-  EXPECT_TRUE(ParseNameValuePair(_T("3?google"), _T('?'), &name, &value));
-  EXPECT_EQ(name, _T("3"));
-  EXPECT_EQ(value, _T("google"));
-
-  // Invalid Cases
-  EXPECT_FALSE(ParseNameValuePair(_T(""), _T('='), &name, &value));
-  EXPECT_FALSE(ParseNameValuePair(_T(" "), _T('='), &name, &value));
-  EXPECT_FALSE(ParseNameValuePair(_T("="), _T('='), &name, &value));
-  EXPECT_FALSE(ParseNameValuePair(_T("x="), _T('='), &name, &value));
-  EXPECT_FALSE(ParseNameValuePair(_T("=y"), _T('='), &name, &value));
-  EXPECT_FALSE(ParseNameValuePair(_T("="), _T('='), &name, &value));
-  EXPECT_FALSE(ParseNameValuePair(_T("xxyyzz"), _T('='), &name, &value));
-  EXPECT_FALSE(ParseNameValuePair(_T("xx yyzz"), _T('='), &name, &value));
-  EXPECT_FALSE(ParseNameValuePair(_T("xx==yyzz"), _T('='), &name, &value));
-  EXPECT_FALSE(ParseNameValuePair(_T("xx=yy=zz"), _T('='), &name, &value));
-}
-
-TEST(StringTest, SplitCommandLineInPlace) {
-  const TCHAR * const test_long_paths[] = {
-    _T("c:\\Program Files\\Google\\App\\App.exe"),
-    _T("c:\\Program Files\\Google\\App\\Long Name App.exe"),
-  };
-  const TCHAR * const test_short_paths[] = {
-    _T("notepad.exe"),
-  };
-  const TCHAR * const test_arguments[] = {
-    _T("/a=\"some text\""),
-    _T("/a /b /c"),
-    _T(""),
-  };
-  TCHAR command_line[1024] = {};
-  TCHAR* path = NULL;
-  TCHAR* arguments = NULL;
-  for (int ii = 0; ii < ARRAYSIZE(test_arguments) ; ++ii) {
-    for (int jj = 0; jj < ARRAYSIZE(test_long_paths) ; ++jj) {
-      _snwprintf_s(command_line, ARRAYSIZE(command_line), _TRUNCATE,
-          _T("\"%s\" %s"), test_long_paths[jj], test_arguments[ii]);
-      EXPECT_EQ(true, SplitCommandLineInPlace(command_line, &path, &arguments));
-      EXPECT_STREQ(test_long_paths[jj], path);
-      EXPECT_STREQ(test_arguments[ii], arguments);
-    }
-    for (int kk = 0; kk < ARRAYSIZE(test_short_paths) ; ++kk) {
-      _snwprintf_s(command_line, ARRAYSIZE(command_line), _TRUNCATE,
-          _T("%s %s"), test_short_paths[kk], test_arguments[ii]);
-      EXPECT_EQ(true, SplitCommandLineInPlace(command_line, &path, &arguments));
-      EXPECT_STREQ(test_short_paths[kk], path);
-      EXPECT_STREQ(test_arguments[ii], arguments);
-    }
-  }
-}
-
-TEST(StringTest, ContainsOnlyAsciiChars) {
-  CString test(_T("hello worlr"));
-  ASSERT_TRUE(ContainsOnlyAsciiChars(test));
-
-  TCHAR non_ascii[] = {0x4345, 0x1234, 0x2000};
-  ASSERT_FALSE(ContainsOnlyAsciiChars(non_ascii));
-}
-TEST(StringTest, BytesToHex) {
-  EXPECT_STREQ(BytesToHex(NULL, 0), _T(""));
-  uint8 i = 0;
-  EXPECT_STREQ(BytesToHex(&i, sizeof(i)), _T("00"));
-  i = 0x7f;
-  EXPECT_STREQ(BytesToHex(&i, sizeof(i)), _T("7f"));
-  i = 0xff;
-  EXPECT_STREQ(BytesToHex(&i, sizeof(i)), _T("ff"));
-
-  // Assumes little-endian representation of integers.
-  const uint32 array[] = {0x67452301, 0xefcdab89};
-  EXPECT_STREQ(BytesToHex(reinterpret_cast<const uint8*>(array), sizeof(array)),
-               _T("0123456789abcdef"));
-
-  const uint8* first = reinterpret_cast<const uint8*>(array);
-  const uint8* last  = first + sizeof(array);
-  EXPECT_STREQ(BytesToHex(std::vector<uint8>(first, last)),
-               _T("0123456789abcdef"));
-}
-
-TEST(StringTest, JoinStrings) {
-  std::vector<CString> components;
-  const TCHAR* delim = _T("-");
-  CString result;
-
-  JoinStrings(components, delim, &result);
-  EXPECT_TRUE(result.IsEmpty());
-  JoinStrings(components, NULL, &result);
-  EXPECT_TRUE(result.IsEmpty());
-
-  components.push_back(CString(_T("foo")));
-  JoinStrings(components, delim, &result);
-  EXPECT_STREQ(result, (_T("foo")));
-  JoinStrings(components, NULL, &result);
-  EXPECT_STREQ(result, (_T("foo")));
-
-  components.push_back(CString(_T("bar")));
-  JoinStrings(components, delim, &result);
-  EXPECT_STREQ(result, (_T("foo-bar")));
-  JoinStrings(components, NULL, &result);
-  EXPECT_STREQ(result, (_T("foobar")));
-
-  components.push_back(CString(_T("baz")));
-  JoinStrings(components, delim, &result);
-  EXPECT_STREQ(result, (_T("foo-bar-baz")));
-  JoinStrings(components, NULL, &result);
-  EXPECT_STREQ(result, (_T("foobarbaz")));
-
-
-  JoinStringsInArray(NULL, 0, delim, &result);
-  EXPECT_TRUE(result.IsEmpty());
-  JoinStringsInArray(NULL, 0, NULL, &result);
-  EXPECT_TRUE(result.IsEmpty());
-
-  const TCHAR* array1[] = {_T("foo")};
-  JoinStringsInArray(array1, arraysize(array1), delim, &result);
-  EXPECT_STREQ(result, (_T("foo")));
-  JoinStringsInArray(array1, arraysize(array1), NULL, &result);
-  EXPECT_STREQ(result, (_T("foo")));
-
-  const TCHAR* array2[] = {_T("foo"), _T("bar")};
-  JoinStringsInArray(array2, arraysize(array2), delim, &result);
-  EXPECT_STREQ(result, (_T("foo-bar")));
-  JoinStringsInArray(array2, arraysize(array2), NULL, &result);
-  EXPECT_STREQ(result, (_T("foobar")));
-
-  const TCHAR* array3[] = {_T("foo"), _T("bar"), _T("baz")};
-  JoinStringsInArray(array3, arraysize(array3), delim, &result);
-  EXPECT_STREQ(result, (_T("foo-bar-baz")));
-  JoinStringsInArray(array3, arraysize(array3), NULL, &result);
-  EXPECT_STREQ(result, (_T("foobarbaz")));
-
-  const TCHAR* array_null_1[] = {NULL};
-  JoinStringsInArray(array_null_1, arraysize(array_null_1), delim, &result);
-  EXPECT_STREQ(result, (_T("")));
-
-  const TCHAR* array_null_2[] = {NULL, NULL};
-  JoinStringsInArray(array_null_2, arraysize(array_null_2), delim, &result);
-  EXPECT_STREQ(result, (_T("-")));
-}
-
-TEST(StringTest, String_ToUpper) {
-  // String_ToUpper is a wrapper over ::CharUpper.
-  TCHAR s[] = _T("foo");
-  String_ToUpper(s);
-  EXPECT_STREQ(s, _T("FOO"));
-}
-
-TEST(StringTest, FormatResourceMessage_Valid) {
-  EXPECT_STREQ(
-      _T("Thanks for installing Gears."),
-      FormatResourceMessage(IDS_APPLICATION_INSTALLED_SUCCESSFULLY,
-                            _T("Gears")));
-
-  EXPECT_STREQ(
-      _T("The installer encountered error 12345: Action failed."),
-      FormatResourceMessage(IDS_INSTALLER_FAILED_WITH_MESSAGE,
-                            _T("12345"),
-                            _T("Action failed.")));
-}
-
-TEST(StringTest, FormatResourceMessage_IdNotFound) {
-  EXPECT_STREQ(_T(""), FormatResourceMessage(100000, "foo", 9));
-}
-
-TEST(StringTest, FormatErrorCode) {
-  EXPECT_STREQ(_T("0xffffffff"), FormatErrorCode(static_cast<DWORD>(-1)));
-  EXPECT_STREQ(_T("0"), FormatErrorCode(0));
-  EXPECT_STREQ(_T("567"), FormatErrorCode(567));
-  EXPECT_STREQ(_T("2147483647"), FormatErrorCode(0x7fffffff));
-  EXPECT_STREQ(_T("0x80000000"), FormatErrorCode(0x80000000));
-  EXPECT_STREQ(_T("0x80000001"), FormatErrorCode(0x80000001));
-  EXPECT_STREQ(_T("0x8fffffff"), FormatErrorCode(0x8fffffff));
-}
-
-TEST(StringTest, Utf8BufferToWideChar) {
-  // Unicode Greek capital letters.
-  const TCHAR expected_string[] = {913, 914, 915, 916, 917, 918, 919, 920,
-                                   921, 922, 923, 924, 925, 926, 927, 928,
-                                   929, 931, 932, 933, 934, 935, 936, 937, 0};
-  // Greek capital letters UTF-8 encoded.
-  const char buffer[] = "ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ";
-  CString actual_string = Utf8BufferToWideChar(
-      std::vector<uint8>(buffer, buffer + arraysize(buffer)));
-  EXPECT_STREQ(expected_string, actual_string);
-
-  EXPECT_STREQ(_T(""), Utf8BufferToWideChar(std::vector<uint8>()));
-}
-
-TEST(StringTest, WideStringToUtf8UrlEncodedStringRoundTrip) {
-  CString unicode_string;
-  ASSERT_TRUE(unicode_string.LoadString(IDS_ESCAPE_TEST));
-
-  // Convert from unicode to a wide representation of utf8,url encoded string.
-  CString utf8encoded_str;
-  ASSERT_HRESULT_SUCCEEDED(WideStringToUtf8UrlEncodedString(unicode_string,
-                                                            &utf8encoded_str));
-  ASSERT_FALSE(utf8encoded_str.IsEmpty());
-
-  // Reconvert from the utf8, url encoded string to the wide version.
-  CString out;
-  ASSERT_HRESULT_SUCCEEDED(Utf8UrlEncodedStringToWideString(utf8encoded_str,
-                                                            &out));
-  ASSERT_FALSE(out.IsEmpty());
-  ASSERT_STREQ(unicode_string, out);
-}
-
-TEST(StringTest, WideStringToUtf8UrlEncodedStringEmptyString) {
-  CString unicode_string;
-
-  // Convert from unicode to a wide representation of utf8,url encoded string.
-  CString utf8encoded_str;
-  ASSERT_HRESULT_SUCCEEDED(WideStringToUtf8UrlEncodedString(unicode_string,
-                                                            &utf8encoded_str));
-  ASSERT_TRUE(utf8encoded_str.IsEmpty());
-}
-
-TEST(StringTest, WideStringToUtf8UrlEncodedStringSpaces) {
-  CString unicode_string(_T("   "));
-  CStringA expected("%20%20%20");
-  CString exp(expected);
-
-  // Convert from unicode to a wide representation of utf8,url encoded string.
-  CString utf8encoded_str;
-  ASSERT_HRESULT_SUCCEEDED(WideStringToUtf8UrlEncodedString(unicode_string,
-                                                            &utf8encoded_str));
-  ASSERT_STREQ(exp, utf8encoded_str);
-}
-
-TEST(StringTest, WideStringToUtf8UrlEncodedStringTestString) {
-  CString unicode_string(_T("Test Str/ing&values=&*^%$#"));
-  CStringA ansi_exp("Test%20Str/ing%26values=%26*%5E%$#");
-  CString exp(ansi_exp);
-
-  // Convert from unicode to a wide representation of utf8,url encoded string.
-  CString utf8encoded_str;
-  ASSERT_HRESULT_SUCCEEDED(WideStringToUtf8UrlEncodedString(unicode_string,
-                                                            &utf8encoded_str));
-  ASSERT_STREQ(exp, utf8encoded_str);
-}
-
-TEST(StringTest, WideStringToUtf8UrlEncodedStringSimpleTestString) {
-  CString unicode_string(_T("TestStr"));
-  CStringA ansi_exp("TestStr");
-  CString exp(ansi_exp);
-
-  // Convert from unicode to a wide representation of utf8,url encoded string.
-  CString utf8encoded_str;
-  ASSERT_HRESULT_SUCCEEDED(WideStringToUtf8UrlEncodedString(unicode_string,
-                                                            &utf8encoded_str));
-  ASSERT_STREQ(exp, utf8encoded_str);
-}
-
-TEST(StringTest, Utf8UrlEncodedStringToWideStringEmpty) {
-  // Convert from wide representation of utf8,url encoded string to unicode.
-  CString unicode_string;
-  CString utf8encoded_str;
-  ASSERT_HRESULT_SUCCEEDED(Utf8UrlEncodedStringToWideString(utf8encoded_str,
-                                                            &unicode_string));
-  ASSERT_TRUE(unicode_string.IsEmpty());
-}
-
-TEST(StringTest, Utf8UrlEncodedStringToWideStringSpaces) {
-  CString exp(_T("   "));
-  CStringA utf8("%20%20%20");
-  CString utf8encoded_str(utf8);
-
-  // Convert from wide representation of utf8,url encoded string to unicode.
-  CString unicode_string;
-  ASSERT_HRESULT_SUCCEEDED(Utf8UrlEncodedStringToWideString(utf8encoded_str,
-                                                            &unicode_string));
-  ASSERT_STREQ(exp, unicode_string);
-}
-
-TEST(StringTest, Utf8UrlEncodedStringToWideStringSimpleString) {
-  CString exp(_T("TestStr"));
-  CStringA utf8("TestStr");
-  CString utf8encoded_str(utf8);
-
-  // Convert from wide representation of utf8,url encoded string to unicode.
-  CString unicode_string;
-  ASSERT_HRESULT_SUCCEEDED(Utf8UrlEncodedStringToWideString(utf8encoded_str,
-                                                            &unicode_string));
-  ASSERT_STREQ(exp, unicode_string);
-}
-
-}  // namespace omaha
-
diff --git a/common/synchronized.cc b/common/synchronized.cc
deleted file mode 100644
index 926231e..0000000
--- a/common/synchronized.cc
+++ /dev/null
@@ -1,504 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Defines methods of classes used to encapsulate
-// the synchronization primitives.
-
-#include "omaha/common/synchronized.h"
-
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/system.h"
-#include "omaha/common/system_info.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-typedef HANDLE WINAPI CreateMutexExFunction(
-  LPSECURITY_ATTRIBUTES attributes,
-  const WCHAR* name,
-  DWORD flags,
-  DWORD desired_access
-);
-
-#define CREATE_EVENT_MANUAL_RESET 0x01
-typedef HANDLE WINAPI CreateEventExFunction(
-  LPSECURITY_ATTRIBUTES attributes,
-  const WCHAR* name,
-  DWORD flags,
-  DWORD desired_access
-);
-
-CreateMutexExFunction* create_mutex_ex_function = NULL;
-CreateEventExFunction* create_event_ex_function = NULL;
-
-void EnsureCreateEx() {
-  if ((create_mutex_ex_function && create_event_ex_function) ||
-      !SystemInfo::IsRunningOnVistaOrLater()) {
-    return;
-  }
-
-  HMODULE kernel32_module = ::GetModuleHandle(_T("kernel32.dll"));
-  if (!kernel32_module) {
-    ASSERT(kernel32_module,
-           (_T("[GetModuleHandle error 0x%x]"), ::GetLastError()));
-    return;
-  }
-  GPA(kernel32_module, "CreateMutexExW", &create_mutex_ex_function);
-  ASSERT(create_mutex_ex_function,
-         (_T("[GPA error 0x%x]"), ::GetLastError()));
-
-  GPA(kernel32_module, "CreateEventExW", &create_event_ex_function);
-  ASSERT(create_event_ex_function,
-         (_T("[GPA error 0x%x]"), ::GetLastError()));
-}
-
-HANDLE CreateMutexWithSyncAccess(const TCHAR* name,
-                                 LPSECURITY_ATTRIBUTES lock_attributes) {
-  EnsureCreateEx();
-  if (create_mutex_ex_function) {
-    return create_mutex_ex_function(lock_attributes, name, 0,
-                                    SYNCHRONIZE |
-                                    MUTEX_MODIFY_STATE);  // for ReleaseMutex
-  }
-  return ::CreateMutex(lock_attributes, false, name);
-}
-
-HANDLE CreateEventWithSyncAccess(const TCHAR* name,
-                                 LPSECURITY_ATTRIBUTES event_attributes) {
-  EnsureCreateEx();
-  if (create_event_ex_function) {
-    return create_event_ex_function(event_attributes, name,
-                                    CREATE_EVENT_MANUAL_RESET,
-                                    SYNCHRONIZE |
-                                    EVENT_MODIFY_STATE);  // for Set/Reset, etc.
-  }
-  return ::CreateEvent(event_attributes, true, false, name);
-}
-
-// c-tor will take mutex.
-AutoSync::AutoSync(const Lockable *pLock)
-    : lock_(pLock),
-      first_time_(true) {
-  ASSERT(lock_, (L""));
-  VERIFY(lock_->Lock(), (L"Failed to lock in constructor"));
-}
-
-// c-tor will take mutex.
-AutoSync::AutoSync(const Lockable &rLock)
-    : lock_(&rLock),
-      first_time_(true) {
-  ASSERT(lock_, (L""));
-  VERIFY(lock_->Lock(), (L"Failed to lock in constructor"));
-}
-
-// d-tor will release mutex.
-AutoSync::~AutoSync() {
-  ASSERT(lock_, (L""));
-  VERIFY(lock_->Unlock(), (L"Failed to unlock in denstructor"));
-}
-
-// Allows to write the for loop of __mutexBlock macro
-bool AutoSync::FirstTime() {
-  if (first_time_) {
-     first_time_ = false;
-     return true;
-  }
-  return false;
-}
-
-// Constructor.
-GLock::GLock() : mutex_(NULL) {
-}
-
-bool GLock::InitializeWithSecAttr(const TCHAR* name,
-                                  LPSECURITY_ATTRIBUTES lock_attributes) {
-  ASSERT(!mutex_, (L""));
-
-#if defined(DEBUG) || defined(ASSERT_IN_RELEASE)
-  name_ = name;
-#endif
-
-  mutex_ = CreateMutexWithSyncAccess(name, lock_attributes);
-  return mutex_ != NULL;
-}
-
-// Create mutex return the status of creation. Sets to default DACL.
-bool GLock::Initialize(const TCHAR* name) {
-  return InitializeWithSecAttr(name, NULL);
-}
-
-// Clean up.
-GLock::~GLock() {
-  if (mutex_) {
-    VERIFY(::CloseHandle(mutex_), (_T("")));
-  }
-};
-
-// Wait until signaled.
-bool GLock::Lock() const {
-  return Lock(INFINITE);
-}
-
-bool GLock::Lock(DWORD dwMilliseconds) const {
-  ASSERT1(mutex_);
-
-  DWORD ret = ::WaitForSingleObject(mutex_, dwMilliseconds);
-  if (ret == WAIT_OBJECT_0) {
-    return true;
-  } else if (ret == WAIT_ABANDONED) {
-    UTIL_LOG(LE, (_T("[GLock::Lock - mutex was abandoned %s]"), name_));
-    return true;
-  }
-  return false;
-}
-
-// Release.
-bool GLock::Unlock() const {
-  ASSERT1(mutex_);
-
-  bool ret = (false != ::ReleaseMutex(mutex_));
-  ASSERT(ret, (_T("ReleaseMutex failed.  Err=%i"), ::GetLastError()));
-
-  return ret;
-}
-
-LLock::LLock() {
-  InitializeCriticalSection(&critical_section_);
-}
-
-LLock::~LLock() {
-  DeleteCriticalSection(&critical_section_);
-}
-
-bool LLock::Lock() const {
-  EnterCriticalSection(&critical_section_);
-  return true;
-}
-
-// not very precise funcion, but OK for our goals.
-bool LLock::Lock(DWORD wait_ms) const {
-  if (::TryEnterCriticalSection(&critical_section_))
-    return true;
-  DWORD ticks_at_the_begin_of_wait = GetTickCount();
-  do {
-    ::Sleep(0);
-    if (::TryEnterCriticalSection(&critical_section_)) {
-      return true;
-    }
-  } while (::GetTickCount() - ticks_at_the_begin_of_wait <  wait_ms);
-  return false;
-}
-
-bool LLock::Unlock() const {
-  LeaveCriticalSection(&critical_section_);
-  return true;
-}
-
-// Use this c-tor for interprocess gates.
-Gate::Gate(const TCHAR * event_name) : gate_(NULL) {
-  VERIFY(Initialize(event_name), (_T("")));
-}
-
-// Use this c-tor for in-process gates.
-Gate::Gate() : gate_(NULL) {
-  VERIFY(Initialize(NULL), (_T("")));
-}
-
-// clean up.
-Gate::~Gate() {
-  VERIFY(CloseHandle(gate_), (_T("")));
-}
-
-bool Gate::Initialize(const TCHAR * event_name) {
-  // event_name may be NULL
-  ASSERT1(gate_ == NULL);
-
-  // Create the event. The gate is initially closed.
-  // if this is in process gate we don't name event, otherwise we do.
-  // Created with default permissions.
-  gate_ = CreateEventWithSyncAccess(event_name, NULL);
-  return (NULL != gate_);
-}
-
-// Open the gate. Anyone can go through.
-bool Gate::Open() {
-  return FALSE != SetEvent(gate_);
-}
-
-// Shut the gate closed.
-bool Gate::Close() {
-  return FALSE != ResetEvent(gate_);
-}
-
-bool Gate::Wait(DWORD msec) {
-  return WAIT_OBJECT_0 == WaitForSingleObject(gate_, msec);
-}
-
-// Returns S_OK, and sets selected_gate to zero based index of the gate that
-// was opened
-// Returns E_FAIL if timeout occured or gate was abandoned.
-HRESULT Gate::WaitAny(Gate const * const *gates,
-                      int num_gates,
-                      DWORD msec,
-                      int *selected_gate) {
-  ASSERT1(selected_gate);
-  ASSERT1(gates);
-
-  return WaitMultipleHelper(gates, num_gates, msec, selected_gate, false);
-}
-
-// Returns S_OK if all gates were opened
-// Returns E_FAIL if timeout occured or gate was abandoned.
-HRESULT Gate::WaitAll(Gate const * const *gates, int num_gates, DWORD msec) {
-  ASSERT1(gates);
-
-  return WaitMultipleHelper(gates, num_gates, msec, NULL, true);
-}
-
-HRESULT Gate::WaitMultipleHelper(Gate const * const *gates,
-                                 int num_gates,
-                                 DWORD msec,
-                                 int *selected_gate,
-                                 bool wait_all) {
-  ASSERT1(gates);
-  ASSERT(num_gates > 0, (_T("There must be at least 1 gate")));
-
-  if ( num_gates <= 0 ) {
-    return E_FAIL;
-  }
-  HANDLE *gate_array = new HANDLE[ num_gates ];
-  ASSERT1(gate_array);
-  for ( int i = 0 ; i < num_gates ; ++i ) {
-    gate_array[ i ] = gates[ i ]->gate_;
-  }
-  DWORD res = WaitForMultipleObjects(num_gates,
-                                     gate_array,
-                                     wait_all ? TRUE : FALSE,
-                                     msec);
-  delete[] gate_array;
-
-#pragma warning(disable : 4296)
-// C4296: '>=' : expression is always true
-  if (WAIT_OBJECT_0 <= res && res < (WAIT_OBJECT_0 + num_gates)) {
-    if (selected_gate) {
-      *selected_gate = res - WAIT_OBJECT_0;
-    }
-    return S_OK;
-  }
-#pragma warning(default : 4296)
-  return E_FAIL;
-}
-
-bool WaitAllowRepaint(const Gate& gate, DWORD msec) {
-  DWORD wait = 0;
-  HANDLE gate_handle = gate;
-  while ((wait = ::MsgWaitForMultipleObjects(1,
-                                             &gate_handle,
-                                             FALSE,
-                                             msec,
-                                             QS_PAINT)) == WAIT_OBJECT_0 + 1) {
-    MSG msg;
-    if (::PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) ||
-        ::PeekMessage(&msg, NULL, WM_NCPAINT, WM_NCPAINT, PM_REMOVE)) {
-      ::TranslateMessage(&msg);
-      ::DispatchMessage(&msg);
-    }
-  }
-  return (wait == WAIT_OBJECT_0);
-}
-
-// SimpleLock
-// TODO(omaha): Replace InterlockedCompareExchange with
-// InterlockedCompareExchangeAcquire
-// and InterlockedDecrement with InterlockedDecrementRelease for Windows 2003
-
-bool SimpleLock::Lock() const {
-  while (1 == ::InterlockedCompareExchange(&lock_, 1, 0))
-    ::SleepEx(0, TRUE);
-  return true;
-}
-
-bool SimpleLock::Unlock() const {
-  ::InterlockedDecrement(&lock_);
-  return true;
-}
-
-// same with a delay in the loop to prevent CPU usage with significant
-// contention
-
-bool SimpleLockWithDelay::Lock() const {
-  while (1 == ::InterlockedCompareExchange(&lock_, 1, 0))
-    ::SleepEx(25, FALSE);
-  return true;
-}
-
-bool SimpleLockWithDelay::Unlock() const {
-  ::InterlockedDecrement(&lock_);
-  return true;
-}
-
-
-CriticalSection::CriticalSection()
-: number_entries_(0) {
-  InitializeCriticalSection(&critical_section_);
-}
-
-// allow only one thread to hold a lock
-CriticalSection::~CriticalSection() {
-  // we should not have to do anything in the destructor
-  ASSERT(!number_entries_, (_T("critical section destroyed while active")));
-  while (number_entries_) {
-    LeaveCriticalSection(&critical_section_);
-    number_entries_--;
-  }
-
-  DeleteCriticalSection(&critical_section_);
-}
-
-// enter the critical section
-// entries may be nested
-void CriticalSection::Enter() {
-  EnterCriticalSection(&critical_section_);
-  number_entries_++;
-}
-
-// exit the critical section
-// number of exits must match number of entries
-void CriticalSection::Exit() {
-  LeaveCriticalSection(&critical_section_);
-  number_entries_--;
-}
-
-// Take a CriticalSection and lock it
-SingleLock::SingleLock(CriticalSection * cs) {
-  ASSERT(cs, (L""));
-  critical_section_ = cs;
-  critical_section_->Enter();
-}
-
-// If we haven't freed it yet, do so now since we fell out of scope
-SingleLock::~SingleLock() {
-  if (critical_section_) {
-    critical_section_->Exit();
-    critical_section_ = NULL;
-  }
-}
-
-// Explicitly unlock
-HRESULT SingleLock::Unlock() {
-  // If they did not
-  if (critical_section_ == NULL)
-    return S_FALSE;
-
-  critical_section_->Exit();
-  critical_section_ = NULL;
-  return S_OK;
-}
-
-// Encapsulation for kernel Event. Initializes and destroys with it's lifetime
-void EventObj::Init(const TCHAR * event_name) {
-  ASSERT(event_name, (L""));
-
-  h_ = ::CreateEvent(NULL, false, false, event_name);
-  ASSERT1(h_);
-}
-
-EventObj::~EventObj() {
-  if (h_) {
-    VERIFY(CloseHandle(h_), (L""));
-    h_ = NULL;
-  }
-}
-
-BOOL EventObj::SetEvent() {
-  ASSERT(h_, (L""));
-  return ::SetEvent(h_);
-}
-
-// Is the given handle signaled?
-//
-// Typically used for events.
-bool IsHandleSignaled(HANDLE h) {
-  ASSERT(h != NULL &&
-         h != INVALID_HANDLE_VALUE, (_T("")));
-
-  DWORD result = ::WaitForSingleObject(h, 0);
-  if (result == WAIT_OBJECT_0) {
-    return true;
-  }
-
-  ASSERT(result == WAIT_TIMEOUT,
-         (_T("unexpected result value: %u (hr=0x%x)"),
-          result, HRESULTFromLastError()));
-  return false;
-}
-
-
-// Create an id for the events/mutexes that can be used at the given scope.
-// TODO(omaha): Error handling.
-void CreateSyncId(const TCHAR* id, SyncScope scope, CString* sync_id) {
-  ASSERT1(id);
-  ASSERT1(sync_id);
-
-  CString postfix;
-  switch (scope) {
-    default:
-      ASSERT1(false);
-      break;
-
-    case SYNC_LOCAL:
-      sync_id->SetString(_T("Local\\"));
-      // no postfix for local ids
-      break;
-
-    case SYNC_USER:
-    case SYNC_GLOBAL:
-      sync_id->SetString(_T("Global\\"));
-
-      if (scope == SYNC_GLOBAL) {
-        // (MSDN insists that you can create objects with the same name with the
-        // prefixes "Global\" and "Local\" in a system "running Terminal
-        // Services". And it also assures that XP when running Fast User
-        // Switching uses Terminal Services. But when you try to create two
-        // objects with the same name but in the different namespaces on an
-        // XP Pro workstation NOT running Fast User Switching you can't - you
-        // get ERROR_ALREADY_EXISTS. And the reason is that in the Object table,
-        // Global and Local are both symlinks to the same object directory.
-        // Yet every technique that you can use to interrogate the system on
-        // whether or not the system is "running Terminal Services" says that
-        // the system is, in fact, running Terminal Services.
-        // Which is exactly what you'd expect, yet you can't create the
-        // two objects with the same name in different workspaces.  So we change
-        // the name slightly.)
-        postfix.SetString(_T("_global"));
-      } else {
-        ASSERT1(scope == SYNC_USER);
-        // make the postfix the sid
-        VERIFY1(SUCCEEDED(omaha::user_info::GetCurrentUser(NULL,
-                                                           NULL,
-                                                           &postfix)));
-      }
-      break;
-  }
-
-  sync_id->Append(id);
-  sync_id->Append(postfix);
-}
-
-}  // namespace omaha
-
diff --git a/common/synchronized.h b/common/synchronized.h
deleted file mode 100644
index b280845..0000000
--- a/common/synchronized.h
+++ /dev/null
@@ -1,355 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Declares some classes and macros to encapsulate
-// the synchronization primitives.
-
-// TODO(omaha): remove dependency on atlstr
-
-#ifndef OMAHA_COMMON_SYNCHRONIZED_H__
-#define OMAHA_COMMON_SYNCHRONIZED_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include "base/basictypes.h"
-
-namespace omaha {
-
-// This macros are used to create a unique name
-// We need to go through two steps of expansion.
-// Macro kMakeName1 will expand to string + number
-// and macro kMakeName1 will put them together to create
-// the unique name.
-#define MAKE_NAME2(x, y) x##y
-#define MAKE_NAME1(x, y) MAKE_NAME2(x, y)
-#define MAKE_NAME(x) MAKE_NAME1(x, __COUNTER__)
-
-// Declare the interface in implement mutual
-// exclusion. For in process mutual exclusion
-// simple critical sections can be used. For
-// interprocess mutual exclusion some named
-// kernel mode object will have to be used.
-struct Lockable {
-  virtual ~Lockable() {}
-  virtual bool Lock() const = 0;
-  virtual bool Unlock() const = 0;
-};
-
-// Scope based mutual exclusion. Locks
-// the object on construction and unlocks
-// during destruction. Very convinient to use
-// with the macros __mutexScope and __mutexBlock
-class AutoSync {
-  bool first_time_;
- public:
-  explicit AutoSync(const Lockable *pLock);
-  explicit AutoSync(const Lockable &rLock);
-  ~AutoSync();
-  // this function is only needed to use with
-  // the macro __mutexBlock
-  bool FirstTime();
- private:
-  const Lockable * lock_;
-  DISALLOW_EVIL_CONSTRUCTORS(AutoSync);
-};
-
-// the usaage:
-// class A : public Lockable {
-//
-//
-//
-//  void foo(){
-//   __mutexScope(this);
-// ......
-// .......
-// everything is synchronized till the end of the
-// function or the time it returns (from any place)
-// } // end foo.
-//
-// void bar() {
-// ......
-// ...... do something here.
-// ......
-//   __mutexBlock(this){
-//    .... do some other stuff
-//    ....
-//    ....
-//    } everything is synchronized till here
-//
-// }; // end class A
-
-//
-#define __mutexScope(lock) AutoSync MAKE_NAME(hiddenLock)(lock)
-#define __mutexBlock(lock) \
-    for (AutoSync hiddenLock(lock); hiddenLock.FirstTime(); )
-
-// GLock stands for global lock.
-// Implementaion of Lockable to allow mutual exclusion
-// between different processes.
-// For in-process mutual exclusion use LLock - local lock
-class GLock : public Lockable {
- public:
-  GLock();
-  virtual ~GLock();
-
-  // Create mutex return the status of creation.
-  // Takes a SECURITY_ATTRIBUTES structure.
-  bool InitializeWithSecAttr(const TCHAR* name,
-                             LPSECURITY_ATTRIBUTES lock_attributes);
-
-  // Create mutex return the status of creation. Sets to default DACL.
-  bool Initialize(const TCHAR* name);
-
-  virtual bool Lock() const;
-  virtual bool Lock(DWORD dwMilliseconds) const;
-  virtual bool Unlock() const;
-
- private:
-#if defined(DEBUG) || defined(ASSERT_IN_RELEASE)
-  CString name_;
-#endif
-  mutable HANDLE mutex_;
-  DISALLOW_EVIL_CONSTRUCTORS(GLock);
-};
-
-// FakeGLock looks like a GLock, but none of its methods do anything.
-// Only used with SharedMemoryPtr, in cases where locking is not required or
-// desired.
-class FakeGLock : public Lockable {
- public:
-  FakeGLock() {}
-  virtual ~FakeGLock() {}
-  bool InitializeWithSecAttr(const TCHAR*, LPSECURITY_ATTRIBUTES) {
-    return true;
-  }
-  bool Initialize(const TCHAR*) { return true; }
-  virtual bool Lock() const { return true; }
-  virtual bool Lock(DWORD) const { return true; }
-  virtual bool Unlock() const { return true; }
-
- private:
-  DISALLOW_EVIL_CONSTRUCTORS(FakeGLock);
-};
-
-// LLock stands for local lock.
-// means works only inside the process.
-// use GLock - global lock for inter-process
-// guarded access to data.
-class LLock : public Lockable {
- public:
-  LLock();
-  virtual ~LLock();
-  virtual bool Lock() const;
-  virtual bool Lock(DWORD wait_ms) const;
-  virtual bool Unlock() const;
- private:
-  mutable CRITICAL_SECTION        critical_section_;
-  DISALLOW_EVIL_CONSTRUCTORS(LLock);
-};
-
-// A gate is a synchronization object used to either stop all
-// threads from proceeding through a point or to allow them all to proceed.
-class Gate {
- public:
-  // In process gate.
-  Gate();
-
-  // Interprocess gate.
-  explicit Gate(const TCHAR * event_name);
-
-  ~Gate();
-
-  // Open the gate.
-  bool Open();
-
-  // Close the gate.
-  bool Close();
-
-  // Wait to enter the gate.
-  bool Wait(DWORD msec);
-
-  // Conversion from the object to a HANDLE.
-  operator HANDLE() const {return gate_;}
-
-  // Returns S_OK, and sets selected_gate to zero based index of the gate that
-  // was opened.
-  // Returns E_FAIL if timeout occured or gate was abandoned.
-  static HRESULT WaitAny(Gate const * const *gates,
-                         int num_gates,
-                         DWORD msec,
-                         int *selected_gate);
-
-  // Returns S_OK if all gates were opened
-  // Returns E_FAIL if timeout occured or gate was abandoned.
-  static HRESULT WaitAll(Gate const * const *gates, int num_gates, DWORD msec);
-
- private:
-  bool Initialize(const TCHAR * event_name);
-  static HRESULT WaitMultipleHelper(Gate const * const *gates,
-                                    int num_gates,
-                                    DWORD msec,
-                                    int *selected_gate,
-                                    bool wait_all);
-  HANDLE gate_;
-  DISALLOW_EVIL_CONSTRUCTORS(Gate);
-};
-
-bool WaitAllowRepaint(const Gate& gate, DWORD msec);
-
-class AutoGateKeeper {
- public:
-  explicit AutoGateKeeper(Gate *gate) : gate_(gate) {
-    gate_->Open();
-  }
-  ~AutoGateKeeper() {
-    gate_->Close();
-  }
- private:
-  Gate *gate_;
-  DISALLOW_EVIL_CONSTRUCTORS(AutoGateKeeper);
-};
-
-// A very simple rather fast lock - if uncontested.  USE ONLY AS A GLOBAL OBJECT
-// (i.e., DECLARED AT FILE SCOPE or as a STATIC CLASS MEMBER) - this is not
-// enforced.  Uses interlocked instructions on an int to get a fast user-mode
-// lock.  (Locks the bus and does a couple of memory references so it isn't
-// free.)  Spin-waits to get the lock.  Has the advantage that it needs no
-// initialization - thus has no order-of-evaluation problems with respect to
-// other global objects.  Does not work (causes deadlock) if locked twice by
-// the same thread. (Has no constructor so is initialized to 0 by C++.
-// This is why it must be a global or static class member: it doesn't initialize
-// itself to 0.  This is also why it doesn't inherit from Lockable, which would
-// make it need to initialize a virtual table.)
-struct SimpleLock {
-  bool Lock() const;
-  bool Unlock() const;
- private:
-  mutable volatile long lock_;
-};
-
-struct SimpleLockWithDelay {
-  bool Lock() const;
-  bool Unlock() const;
- private:
-  mutable volatile long lock_;
-};
-
-class AutoSimpleLock {
- public:
-  explicit AutoSimpleLock(const SimpleLock& lock)
-      : lock_(lock) { lock_.Lock(); }
-  ~AutoSimpleLock() { lock_.Unlock(); }
- private:
-  const SimpleLock& lock_;
-  DISALLOW_EVIL_CONSTRUCTORS(AutoSimpleLock);
-};
-
-class AutoSimpleLockWithDelay {
- public:
-  explicit AutoSimpleLockWithDelay(const SimpleLockWithDelay& lock)
-      : lock_(lock) { lock_.Lock(); }
-  ~AutoSimpleLockWithDelay() { lock_.Unlock(); }
- private:
-  const SimpleLockWithDelay& lock_;
-  DISALLOW_EVIL_CONSTRUCTORS(AutoSimpleLockWithDelay);
-};
-
-
-// allow only one thread to hold a lock
-class CriticalSection {
- public:
-  CriticalSection();
-  ~CriticalSection();
-
-  void Enter();
-  void Exit();
-
- private:
-  CRITICAL_SECTION critical_section_;
-  uint32 number_entries_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(CriticalSection);
-};
-
-// A class that manages a CriticalSection with its lifetime, you pass
-// it one in the constructor and then it will either be freed in the
-// destructor or implicitly [but only once]
-class SingleLock {
- public:
-  // TODO(omaha): Not sure if immediately locking is a good idea;
-  // the API is asymmetrical (there's an Unlock but no Lock).
-
-  // Lock a critical section immediately
-  explicit SingleLock(CriticalSection * cs);
-
-  // If we have not explicitly unlocked it, this destructor will
-  ~SingleLock();
-
-  // Release the lock explicitly [should be called only once, after that
-  // does nothing. If we do not do so, the destructor will]
-  HRESULT Unlock();
-
- private:
-  CriticalSection * critical_section_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(SingleLock);
-};
-
-// Encapsulation for kernel Event. Initializes and destroys with it's lifetime
-class EventObj {
- public:
-  explicit EventObj(const TCHAR * event_name) {
-    Init(event_name);
-  }
-  ~EventObj();
-  void Init(const TCHAR * event_name);
-  BOOL SetEvent();
-  HANDLE GetHandle() { return h_; }
-
- private:
-  HANDLE h_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(EventObj);
-};
-
-// Is the given handle signaled?
-//
-// Typically used for events.
-bool IsHandleSignaled(HANDLE h);
-
-
-enum SyncScope {
-  // local to a session
-  SYNC_LOCAL,
-
-  // global scope but the name is decorated to make it unique for the user
-  SYNC_USER,
-
-  // a globally scoped name
-  SYNC_GLOBAL,
-};
-
-// Create an id for the events/mutexes that can be used at the given scope
-void CreateSyncId(const TCHAR* id, SyncScope scope, CString* sync_id);
-
-// If any place needs to create a mutex that multiple
-// processes need to access, use this.
-HANDLE CreateMutexWithSyncAccess(const TCHAR* name,
-                                 LPSECURITY_ATTRIBUTES lock_attributes);
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_SYNCHRONIZED_H__
-
diff --git a/common/system.cc b/common/system.cc
deleted file mode 100644
index 41095f8..0000000
--- a/common/system.cc
+++ /dev/null
@@ -1,1033 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/system.h"
-
-#include <objidl.h>
-#include <psapi.h>
-#include <winioctl.h>
-#include <wtsapi32.h>
-#include "omaha/common/commands.h"
-#include "omaha/common/const_config.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/disk.h"
-#include "omaha/common/dynamic_link_kernel32.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/module_utils.h"
-#include "omaha/common/path.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/string.h"
-#include "omaha/common/system_info.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-// Constant
-const TCHAR kNeedRebootHiddenFileSuffix[] = _T(".needreboot");
-
-HRESULT System::WaitForDiskActivity(const uint32 max_delay_milliseconds,
-                                    const uint32 sleep_time_ms,
-                                    uint32 *time_waited) {
-  ASSERT(time_waited, (L""));
-  uint32 sleep_time = sleep_time_ms;
-  if (sleep_time < 20) { sleep_time = 20; }
-  else if (sleep_time > 1000) { sleep_time = 1000; }
-  HRESULT r;
-  *time_waited = 0;
-  uint64 writes = 0;
-  uint64 new_writes = 0;
-  // get current counters
-  if (FAILED(r=GetDiskActivityCounters(NULL, &writes, NULL, NULL))) {
-    return r;
-  }
-
-  // wait until a write - reads may be cached
-  while (1) {
-    if (FAILED(r=GetDiskActivityCounters(NULL, &new_writes, NULL, NULL))) {
-      return r;
-    }
-    if (new_writes > writes) { return S_OK; }
-    if (*time_waited > max_delay_milliseconds) { return E_FAIL; }
-    SleepEx(sleep_time, TRUE);
-    *time_waited += sleep_time;
-  }
-}
-
-HRESULT System::GetDiskActivityCounters(uint64* reads,
-                                        uint64* writes,
-                                        uint64* bytes_read,
-                                        uint64* bytes_written) {
-  if (reads) {
-    *reads = 0;
-  }
-
-  if (writes) {
-    *writes = 0;
-  }
-
-  if (bytes_read) {
-    *bytes_read = 0;
-  }
-
-  if (bytes_written) {
-    *bytes_written = 0;
-  }
-
-  // Don't want to risk displaying UI errors here
-  DisableThreadErrorUI disable_error_dialog_box;
-
-  // for all drives
-  for (int drive = 0; ; drive++) {
-    struct _DISK_PERFORMANCE perf_data;
-    const int max_device_len = 50;
-
-    // check whether we can access this device
-    CString device_name;
-    device_name.Format(_T("\\\\.\\PhysicalDrive%d"), drive);
-    scoped_handle device(::CreateFile(device_name, 0,
-                                      FILE_SHARE_READ | FILE_SHARE_WRITE,
-                                      NULL, OPEN_EXISTING, 0, NULL));
-
-    if (get(device) == INVALID_HANDLE_VALUE) {
-      if (!drive) {
-        UTIL_LOG(LEVEL_ERROR, (_T("[Failed to access drive %i][0x%x]"),
-                               drive,
-                               HRESULTFromLastError()));
-      }
-      break;
-    }
-
-    // disk performance counters must be on (diskperf -y on older machines;
-    // defaults to on on newer windows)
-    DWORD size = 0;
-    if (::DeviceIoControl(get(device),
-                          IOCTL_DISK_PERFORMANCE,
-                          NULL,
-                          0,
-                          &perf_data,
-                          sizeof(_DISK_PERFORMANCE),
-                          &size,
-                          NULL)) {
-      if (reads) {
-        *reads += perf_data.ReadCount;
-      }
-
-      if (writes) {
-        *writes += perf_data.WriteCount;
-      }
-
-      if (bytes_read) {
-        *bytes_read += perf_data.BytesRead.QuadPart;
-      }
-
-      if (bytes_written) {
-        *bytes_written += perf_data.BytesWritten.QuadPart;
-      }
-    } else {
-      HRESULT hr = HRESULTFromLastError();
-      UTIL_LOG(LEVEL_ERROR,
-               (_T("[System::GetDiskActivityCounters - failed to ")
-                _T("DeviceIoControl][0x%x]"), hr));
-      return hr;
-    }
-  }
-
-  return S_OK;
-}
-
-HRESULT System::GetDiskStatistics(const TCHAR* path,
-                                  uint64 *free_bytes_current_user,
-                                  uint64 *total_bytes_current_user,
-                                  uint64 *free_bytes_all_users) {
-  ASSERT1(path);
-  ASSERT1(free_bytes_current_user);
-  ASSERT1(total_bytes_current_user);
-  ASSERT1(free_bytes_all_users);
-  ASSERT1(sizeof(LARGE_INTEGER) == sizeof(uint64));  // NOLINT
-
-  DisableThreadErrorUI disable_error_dialog_box;
-
-  if (!::GetDiskFreeSpaceEx(
-           path,
-           reinterpret_cast<PULARGE_INTEGER>(free_bytes_current_user),
-           reinterpret_cast<PULARGE_INTEGER>(total_bytes_current_user),
-           reinterpret_cast<PULARGE_INTEGER>(free_bytes_all_users))) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[Failed to GetDiskFreeSpaceEx][%s][0x%x]"), path, hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT System::GetProcessMemoryStatistics(uint64 *current_working_set,
-                                           uint64 *peak_working_set,
-                                           uint64 *min_working_set_size,
-                                           uint64 *max_working_set_size) {
-  HANDLE process_handle = GetCurrentProcess();
-  HRESULT hr = S_OK;
-
-  DWORD min_size(0), max_size(0);
-  if (GetProcessWorkingSetSize(process_handle, &min_size, &max_size)) {
-    UTIL_LOG(L2, (_T("working set %lu %lu"), min_size, max_size));
-    if (min_working_set_size) {
-      *min_working_set_size = min_size;
-    }
-    if (max_working_set_size) {
-      *max_working_set_size = max_size;
-    }
-  } else {
-    if (min_working_set_size) {
-      *min_working_set_size = 0;
-    }
-    if (max_working_set_size) {
-      *max_working_set_size = 0;
-    }
-    hr = E_FAIL;
-  }
-
-  if (current_working_set) { *current_working_set = 0; }
-  if (peak_working_set) { *peak_working_set = 0; }
-
-  // including this call (w/psapi.lib) adds 24k to the process memory
-  // according to task manager in one test, memory usage according to task
-  // manager increased by 4k after calling this
-  PROCESS_MEMORY_COUNTERS counters = { sizeof(counters), 0 };
-  if (GetProcessMemoryInfo(process_handle,
-                           &counters,
-                           sizeof(PROCESS_MEMORY_COUNTERS))) {
-    if (current_working_set) {
-      *current_working_set = counters.WorkingSetSize;
-    }
-    if (peak_working_set) {
-      *peak_working_set = counters.PeakWorkingSetSize;
-    }
-    UTIL_LOG(L2, (_T("current/peak working set %s %s"),
-                  String_Int64ToString(*current_working_set, 10),
-                  String_Int64ToString(*peak_working_set, 10)));
-  } else {
-    if (current_working_set) {
-      *current_working_set = 0;
-    }
-    if (peak_working_set) {
-      *peak_working_set = 0;
-    }
-    hr = E_FAIL;
-  }
-
-  return hr;
-}
-
-HRESULT System::MaxPhysicalMemoryAvailable(uint64* max_bytes) {
-  ASSERT1(max_bytes);
-
-  *max_bytes = 0;
-
-  uint32 memory_load_percentage = 0;
-  uint64 free_physical_memory = 0;
-
-  RET_IF_FAILED(System::GetGlobalMemoryStatistics(&memory_load_percentage,
-    &free_physical_memory, NULL, NULL, NULL, NULL, NULL));
-
-  UTIL_LOG(L4, (_T("mem load %u max physical memory available %s"),
-                memory_load_percentage,
-                String_Int64ToString(free_physical_memory, 10)));
-
-  *max_bytes = free_physical_memory;
-
-  return S_OK;
-}
-
-HRESULT System::GetGlobalMemoryStatistics(uint32 *memory_load_percentage,
-                                          uint64 *free_physical_memory,
-                                          uint64 *total_physical_memory,
-                                          uint64 *free_paged_memory,
-                                          uint64 *total_paged_memory,
-                                          uint64 *process_free_virtual_memory,
-                                          uint64 *process_total_virtual_mem) {
-  MEMORYSTATUSEX status;
-  status.dwLength = sizeof(status);
-  if (!GlobalMemoryStatusEx(&status)) {
-    UTIL_LOG(LEVEL_ERROR, (_T("memory status error %u"), GetLastError()));
-    return E_FAIL;
-  }
-  if (memory_load_percentage) { *memory_load_percentage = status.dwMemoryLoad; }
-  if (free_physical_memory) { *free_physical_memory = status.ullAvailPhys; }
-  if (total_physical_memory) { *total_physical_memory = status.ullTotalPhys; }
-  if (free_paged_memory) { *free_paged_memory = status.ullAvailPageFile; }
-  if (total_paged_memory) { *total_paged_memory = status.ullTotalPageFile; }
-  if (process_free_virtual_memory) {
-    *process_free_virtual_memory = status.ullAvailVirtual;
-  }
-  if (process_total_virtual_mem) {
-    *process_total_virtual_mem = status.ullTotalVirtual;
-  }
-  // GetPerformanceInfo;
-  return S_OK;
-}
-
-void System::FreeProcessWorkingSet() {
-  // -1,-1 is a special signal to the OS to temporarily trim the working set
-  // size to 0.  See MSDN for further information.
-  ::SetProcessWorkingSetSize(::GetCurrentProcess(), (SIZE_T)-1, (SIZE_T)-1);
-}
-
-HRESULT System::SetThreadPriority(enum Priority priority) {
-  int pri;
-
-  switch (priority) {
-    case LOW: pri = THREAD_PRIORITY_BELOW_NORMAL; break;
-    case HIGH: pri = THREAD_PRIORITY_HIGHEST; break;
-    case NORMAL: pri = THREAD_PRIORITY_NORMAL; break;
-    case IDLE: pri = THREAD_PRIORITY_IDLE; break;
-    default: return E_FAIL;
-  }
-
-  if (::SetThreadPriority(GetCurrentThread(), pri)) {
-    return S_OK;
-  } else {
-    return E_FAIL;
-  }
-}
-
-HRESULT System::SetProcessPriority(enum Priority priority) {
-  DWORD pri = 0;
-  switch (priority) {
-    case LOW: pri = BELOW_NORMAL_PRIORITY_CLASS; break;
-    case HIGH: pri = ABOVE_NORMAL_PRIORITY_CLASS; break;
-    case NORMAL: pri = NORMAL_PRIORITY_CLASS; break;
-    case IDLE: return E_INVALIDARG;
-    default: return E_INVALIDARG;
-  }
-
-  DWORD pid = ::GetCurrentProcessId();
-
-  scoped_handle handle(::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid));
-  if (!valid(handle)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LE, (_T("[::OpenProcess failed][%u][0x%x]"), pid, hr));
-    return hr;
-  }
-
-  if (!::SetPriorityClass(get(handle), pri)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LE, (_T("[::SetPriorityClass failed][%u][0x%x]"), pid, hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-// start another process painlessly via ::CreateProcess. Use the
-// ShellExecuteProcessXXX variants instead of these methods where possible,
-// since ::ShellExecuteEx has better behavior on Windows Vista.
-// When using this method, avoid using process_name - see
-// http://blogs.msdn.com/oldnewthing/archive/2006/05/15/597984.aspx.
-HRESULT System::StartProcess(const TCHAR* process_name,
-                             TCHAR* command_line,
-                             PROCESS_INFORMATION* pi) {
-  ASSERT1(pi);
-  ASSERT1(command_line || process_name);
-  ASSERT(!process_name, (_T("Avoid using process_name. See method comment.")));
-
-  STARTUPINFO si = {sizeof(si), 0};
-
-  // Feedback cursor is off while the process is starting.
-  si.dwFlags = STARTF_FORCEOFFFEEDBACK;
-
-  UTIL_LOG(L3, (_T("[System::StartProcess][process %s][cmd %s]"),
-                process_name, command_line));
-
-  BOOL success = ::CreateProcess(
-      process_name,     // Module name
-      command_line,     // Command line
-      NULL,             // Process handle not inheritable
-      NULL,             // Thread handle not inheritable
-      FALSE,            // Set handle inheritance to FALSE
-      0,                // No creation flags
-      NULL,             // Use parent's environment block
-      NULL,             // Use parent's starting directory
-      &si,              // Pointer to STARTUPINFO structure
-      pi);              // Pointer to PROCESS_INFORMATION structure
-
-  if (!success) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[System::StartProcess][::CreateProcess failed][0x%x]"), hr));
-    return hr;
-  }
-
-  OPT_LOG(L1, (_T("[Started process][%u]"), pi->dwProcessId));
-
-  return S_OK;
-}
-
-// start another process painlessly via ::CreateProcess. Use the
-// ShellExecuteProcessXXX variants instead of these methods where possible,
-// since ::ShellExecuteEx has better behavior on Windows Vista.
-HRESULT System::StartProcessWithArgsAndInfo(const TCHAR *process_name,
-                                            const TCHAR *cmd_line_arguments,
-                                            PROCESS_INFORMATION *pi) {
-  ASSERT1(process_name && cmd_line_arguments && pi);
-
-  CString command_line(process_name);
-  EnclosePath(&command_line);
-  command_line.AppendChar(_T(' '));
-  command_line.Append(cmd_line_arguments);
-  return System::StartProcess(NULL, command_line.GetBuffer(), pi);
-}
-
-// start another process painlessly via ::CreateProcess. Use the
-// ShellExecuteProcessXXX variants instead of these methods where possible,
-// since ::ShellExecuteEx has better behavior on Windows Vista.
-HRESULT System::StartProcessWithArgs(const TCHAR *process_name,
-                                     const TCHAR *cmd_line_arguments) {
-  ASSERT1(process_name && cmd_line_arguments);
-  PROCESS_INFORMATION pi = {0};
-  HRESULT hr = System::StartProcessWithArgsAndInfo(process_name,
-                                                   cmd_line_arguments,
-                                                   &pi);
-  if (SUCCEEDED(hr)) {
-    ::CloseHandle(pi.hProcess);
-    ::CloseHandle(pi.hThread);
-  }
-  return hr;
-}
-
-// start another process painlessly via ::ShellExecuteEx. Use this method
-// instead of the StartProcessXXX methods that use ::CreateProcess where
-// possible, since ::ShellExecuteEx has better behavior on Windows Vista.
-//
-// ShellExecuteExEnsureParent displays the PID of the started process if it is
-// returned. It is only returned if the mask includes SEE_MASK_NOCLOSEPROCESS.
-// Therefore, we always set this flag and pass a handle. If the caller did not
-// request the handle, we close it.
-HRESULT System::ShellExecuteProcess(const TCHAR* file_name_to_execute,
-                                    const TCHAR* command_line_parameters,
-                                    HWND hwnd,
-                                    HANDLE* process_handle) {
-  ASSERT1(file_name_to_execute);
-
-  UTIL_LOG(L3, (_T("[System::ShellExecuteProcess]")
-                _T("[file_name_to_execute '%s' command_line_parameters '%s']"),
-                file_name_to_execute, command_line_parameters));
-
-  SHELLEXECUTEINFO sei = {0};
-  sei.cbSize = sizeof(sei);
-  // SEE_MASK_NOZONECHECKS is set below to work around a problem in systems that
-  // had Internet Explorer 7 Beta installed. See http://b/804674.
-  // This only works for Windows XP SP1 and later.
-  sei.fMask = SEE_MASK_NOCLOSEPROCESS |  // Set hProcess to process handle.
-              SEE_MASK_FLAG_NO_UI     |  // Do not display an error message box.
-              SEE_MASK_NOZONECHECKS   |  // Do not perform a zone check.
-              SEE_MASK_NOASYNC;          // Wait to complete before returning.
-  sei.lpVerb = _T("open");
-  sei.lpFile = file_name_to_execute;
-  sei.lpParameters = command_line_parameters;
-  sei.nShow = SW_SHOWNORMAL;
-  sei.hwnd = hwnd;
-
-  // Use ShellExecuteExEnsureParent to ensure that we always have a parent
-  // window. We need to use the HWND property to be acknowledged as a foreground
-  // application on Windows Vista. Otherwise, the elevation prompt will appear
-  // minimized on the taskbar.
-  if (!ShellExecuteExEnsureParent(&sei)) {
-    HRESULT hr(HRESULTFromLastError());
-    OPT_LOG(LEVEL_ERROR, (_T("[Failed to ::ShellExecuteEx][%s][%s][0x%08x]"),
-                          file_name_to_execute, command_line_parameters, hr));
-    return hr;
-  }
-
-  if (process_handle) {
-    *process_handle = sei.hProcess;
-  } else {
-    ::CloseHandle(sei.hProcess);
-  }
-
-  return S_OK;
-}
-
-// start another process painlessly via ::ShellExecuteEx. Use this method
-// instead of the StartProcessXXX methods that use ::CreateProcess where
-// possible, since ::ShellExecuteEx has better behavior on Windows Vista.
-HRESULT System::ShellExecuteCommandLine(const TCHAR* command_line_to_execute,
-                                        HWND hwnd,
-                                        HANDLE* process_handle) {
-  ASSERT1(command_line_to_execute);
-
-  CString exe;
-  CString args;
-
-  HRESULT hr = CommandParsingSimple::SplitExeAndArgs(command_line_to_execute,
-                                                     &exe,
-                                                     &args);
-
-  if (SUCCEEDED(hr)) {
-    hr = System::ShellExecuteProcess(exe, args, hwnd, process_handle);
-    if (FAILED(hr)) {
-      UTIL_LOG(LEVEL_ERROR, (_T("[System::ShellExecuteProcess failed]")
-                             _T("[%s][%s][0x%08x]"), exe, args, hr));
-    }
-  }
-
-  return hr;
-}
-
-// returns the number of ms the system has had no user input
-int System::GetUserIdleTime() {
-  LASTINPUTINFO last_input_info;
-  last_input_info.cbSize = sizeof(LASTINPUTINFO);
-  // get time in windows ticks since system start of last activity
-  BOOL b = GetLastInputInfo(&last_input_info);
-  if (b == TRUE) {
-    return (GetTickCount()-last_input_info.dwTime);  // compute idle time
-  }
-  return 0;
-}
-
-bool System::IsUserIdle() {
-  // Only notify when the user has been idle less than this time
-  static int user_idle_threshold_ms = kUserIdleThresholdMs;
-
-  bool is_user_idle = (GetUserIdleTime() > user_idle_threshold_ms);
-  UTIL_LOG(L2, (_T("System::IsUserIdle() %s; user_idle_threshold_ms = %d"),
-                is_user_idle ? _T("TRUE") : _T("FALSE"),
-                user_idle_threshold_ms));
-  return is_user_idle;
-}
-
-bool System::IsUserBusy() {
-  // The user is busy typing or interacting with another application
-  // if the user is below the minimum threshold:
-  static int user_idle_min_threshold_ms = kUserIdleMinThresholdMs;
-  // The user is probably not paying attention
-  // if the user is above the maximum threshold:
-  static int user_idle_max_threshold_ms = kUserIdleMaxThresholdMs;
-
-  int user_idle_time = GetUserIdleTime();
-  bool is_user_busy = user_idle_time < user_idle_min_threshold_ms ||
-    user_idle_time > user_idle_max_threshold_ms;
-  UTIL_LOG(L2, (_T("[System::IsUserBusy() %s][user_idle_time = %d]")
-                _T("[user_idle_min_threshold_ms = %d]")
-                _T("[user_idle_max_threshold_ms = %d]"),
-                is_user_busy? _T("TRUE") : _T("FALSE"),
-                user_idle_time,
-                user_idle_min_threshold_ms,
-                user_idle_max_threshold_ms));
-  return is_user_busy;
-}
-
-bool System::IsScreensaverRunning() {
-  // NT 4.0 and below require testing OpenDesktop("screen-saver")
-  // We require W2K or better so we have an easier way
-  DWORD result = 0;
-  ::SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &result, 0);
-  bool is_screensaver_running = (result != FALSE);
-  UTIL_LOG(L2, (_T("System::IsScreensaverRunning() %s"),
-                is_screensaver_running? _T("TRUE") : _T("FALSE")));
-  return is_screensaver_running;
-}
-
-bool System::IsWorkstationLocked() {
-  bool is_workstation_locked = true;
-  HDESK inputdesk = ::OpenInputDesktop(0, 0, GENERIC_READ);
-  if (NULL != inputdesk)  {
-    TCHAR name[256];
-    DWORD needed = arraysize(name);
-    BOOL ok = ::GetUserObjectInformation(inputdesk,
-                                         UOI_NAME,
-                                         name,
-                                         sizeof(name),
-                                         &needed);
-    ::CloseDesktop(inputdesk);
-    if (ok) {
-      is_workstation_locked = (0 != lstrcmpi(name, NOTRANSL(_T("default"))));
-    }
-  }
-
-  UTIL_LOG(L2, (_T("System::IsWorkstationLocked() %s"),
-                is_workstation_locked? _T("TRUE") : _T("FALSE")));
-  return is_workstation_locked;
-}
-
-bool System::IsUserAway() {
-  return IsScreensaverRunning() || IsWorkstationLocked();
-}
-
-uint32 System::GetProcessHandleCount() {
-  typedef LONG (CALLBACK *Fun)(HANDLE, int32, PVOID, ULONG, PULONG);
-
-  // This new version of getting the number of open handles works on win2k.
-  HMODULE h = GetModuleHandle(_T("ntdll.dll"));
-  Fun NtQueryInformationProcess =
-      reinterpret_cast<Fun>(::GetProcAddress(h, "NtQueryInformationProcess"));
-
-  if (!NtQueryInformationProcess) {
-    UTIL_LOG(LEVEL_ERROR, (_T("[NtQueryInformationProcess failed][0x%x]"),
-                           HRESULTFromLastError()));
-    return 0;
-  }
-
-  DWORD count = 0;
-  VERIFY(NtQueryInformationProcess(GetCurrentProcess(),
-                                   kProcessHandleCount,
-                                   &count,
-                                   sizeof(count),
-                                   NULL) >= 0, (L""));
-
-  return count;
-}
-
-uint32 System::GetProcessHandleCountOld() {
-  typedef BOOL (CALLBACK * Fun)(HANDLE, PDWORD);
-
-  // GetProcessHandleCount not available on win2k
-  HMODULE handle = GetModuleHandle(_T("kernel32"));
-  Fun f = reinterpret_cast<Fun>(GetProcAddress(handle,
-                                               "GetProcessHandleCount"));
-
-  if (!f) return 0;
-
-  DWORD count = 0;
-  VERIFY((*f)(GetCurrentProcess(), &count), (L""));
-  return count;
-
-  //  DWORD GetGuiResources (HANDLE hProcess, DWORD uiFlags);
-  //  Parameters, hProcess
-  //  [in] Handle to the process. The handle must have the
-  //  PROCESS_QUERY_INFORMATION access right. For more information, see Process
-  //  Security and Access Rights.
-  //  uiFlags
-  //  [in] GUI object type. This parameter can be one of the following values.
-  //  Value          Meaning
-  //  GR_GDIOBJECTS  Return the count of GDI objects.
-  //  GR_USEROBJECTS Return the count of USER objects.
-}
-
-void System::GetGuiObjectCount(uint32 *gdi, uint32 *user) {
-  if (gdi) {
-    *gdi = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
-  }
-  if (user) {
-    *user = GetGuiResources(GetCurrentProcess(), GR_USEROBJECTS);
-  }
-}
-
-HRESULT System::GetRebootCheckDummyFileName(const TCHAR* base_file,
-                                            CString* dummy_file) {
-  ASSERT1(dummy_file);
-
-  if (base_file && *base_file) {
-    ASSERT1(File::Exists(base_file));
-    dummy_file->SetString(base_file);
-  } else {
-    RET_IF_FAILED(GetModuleFileName(NULL, dummy_file));
-  }
-  dummy_file->Append(_T(".needreboot"));
-  return S_OK;
-}
-
-// Is the system being rebooted?
-bool System::IsRebooted(const TCHAR* base_file) {
-  CString dummy_file;
-  if (SUCCEEDED(GetRebootCheckDummyFileName(base_file, &dummy_file))) {
-    if (File::Exists(dummy_file)) {
-      // If the file exists but it is not found in the
-      // PendingFileRenameOperations, (probably becaused that this key is messed
-      // up and thus the system restart fails to delete the file), re-add it
-      if (!File::AreMovesPendingReboot(dummy_file, true)) {
-        File::MoveAfterReboot(dummy_file, NULL);
-      }
-      return false;
-    } else {
-      return true;
-    }
-  }
-  return false;
-}
-
-// Mark the system as reboot required
-HRESULT System::MarkAsRebootRequired(const TCHAR* base_file) {
-  // Create a dummy file if needed
-  CString dummy_file;
-  RET_IF_FAILED(GetRebootCheckDummyFileName(base_file, &dummy_file));
-  if (File::Exists(dummy_file)) {
-    return S_OK;
-  }
-
-  File file;
-  RET_IF_FAILED(file.Open(dummy_file, true, false));
-  RET_IF_FAILED(file.Close());
-
-  // Hide it
-  DWORD file_attr = ::GetFileAttributes(dummy_file);
-  if (file_attr == INVALID_FILE_ATTRIBUTES ||
-      !::SetFileAttributes(dummy_file, file_attr | FILE_ATTRIBUTE_HIDDEN)) {
-    return HRESULTFromLastError();
-  }
-
-  // Mark it as being deleted after reboot
-  return File::MoveAfterReboot(dummy_file, NULL);
-}
-
-// Unmark the system as reboot required
-HRESULT System::UnmarkAsRebootRequired(const TCHAR* base_file) {
-  CString dummy_file;
-  RET_IF_FAILED(GetRebootCheckDummyFileName(base_file, &dummy_file));
-
-  return File::RemoveFromMovesPendingReboot(dummy_file, false);
-}
-
-// Restart the computer
-HRESULT System::RestartComputer() {
-  RET_IF_FAILED(AdjustPrivilege(SE_SHUTDOWN_NAME, true));
-
-  if (!::ExitWindowsEx(EWX_REBOOT, SHTDN_REASON_MAJOR_APPLICATION |
-                       SHTDN_REASON_MINOR_INSTALLATION |
-                       SHTDN_REASON_FLAG_PLANNED)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[System::RestartComputer - failed to")
-                           _T(" ExitWindowsEx][0x%x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-// The implementation works on all Windows versions. On NT and XP the screen
-// saver is actually stored in registry at
-// HKEY_CURRENT_USER\Control Panel\Desktop\SCRNSAVE.EXE but the
-// GetPrivateProfileString call is automatically mapped to the registry
-HRESULT System::GetCurrentScreenSaver(CString* fileName) {
-  if (!fileName) return E_POINTER;
-
-  DWORD nChars = ::GetPrivateProfileString(_T("boot"),
-                                           _T("SCRNSAVE.EXE"),
-                                           _T(""),
-                                           fileName->GetBuffer(MAX_PATH),
-                                           MAX_PATH,
-                                           _T("system.ini"));
-  fileName->ReleaseBufferSetLength(nChars);
-
-  return S_OK;
-}
-
-// Create an instance of a COM Local Server class using either plain vanilla
-// CoCreateInstance, or using the Elevation moniker depending on the operating
-// system
-HRESULT System::CoCreateInstanceAsAdmin(HWND hwnd,
-                                        REFCLSID rclsid,
-                                        REFIID riid,
-                                        void** ppv) {
-  if (SystemInfo::IsRunningOnVistaOrLater()) {
-    // Use the Elevation Moniker to create the Install Manager in Windows Vista.
-    // If the UI is running in medium integrity, this will result in a
-    // elevation prompt
-
-    scoped_window hwnd_parent;
-
-    if (!hwnd) {
-      reset(hwnd_parent, CreateForegroundParentWindowForUAC());
-
-      if (!hwnd_parent) {
-        return HRESULTFromLastError();
-      }
-      // Use the newly created dummy window as the hwnd
-      hwnd = get(hwnd_parent);
-    }
-
-    CString moniker_name(_T("Elevation:Administrator!new:"));
-    moniker_name += GuidToString(rclsid);
-    BIND_OPTS3 bo;
-    SetZero(bo);
-    bo.cbStruct = sizeof(bo);
-    bo.hwnd = hwnd;
-    bo.dwClassContext = CLSCTX_LOCAL_SERVER;
-
-    return ::CoGetObject(moniker_name, &bo, riid, ppv);
-  } else {
-    // Use plain-vanilla ::CoCreateInstance()
-    return ::CoCreateInstance(rclsid, NULL, CLSCTX_LOCAL_SERVER, riid, ppv);
-  }
-}
-
-HRESULT System::IsPrivilegeEnabled(const TCHAR* privilege, bool* present) {
-  ASSERT1(privilege);
-  ASSERT1(present);
-
-  *present = false;
-
-  scoped_handle token;
-  if (!::OpenProcessToken(::GetCurrentProcess(),
-                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
-                          address(token))) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[System::IsPrivilegeEnabled - failed to ")
-                           _T("OpenProcessToken][0x%x]"), hr));
-    return hr;
-  }
-
-  LUID luid = {0};
-  if (!::LookupPrivilegeValue(NULL, privilege, &luid)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[System::IsPrivilegeEnabled - failed to")
-                           _T("LookupPrivilegeValue][0x%x]"), hr));
-    return hr;
-  }
-
-  PRIVILEGE_SET required_privilege = {0};
-  required_privilege.PrivilegeCount = 1;
-  required_privilege.Control = PRIVILEGE_SET_ALL_NECESSARY;
-  required_privilege.Privilege[0].Luid = luid;
-
-  BOOL result = FALSE;
-  if (!::PrivilegeCheck(get(token), &required_privilege, &result)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[System::IsPrivilegeEnabled - failed to")
-                           _T("PrivilegeCheck][0x%x]"), hr));
-    return hr;
-  }
-
-  if (required_privilege.Privilege[0].Attributes &
-      SE_PRIVILEGE_USED_FOR_ACCESS) {
-    *present = true;
-  }
-
-  return S_OK;
-}
-
-// Attempts to adjust current process privileges.
-// Only process running with administrator privileges will succeed.
-HRESULT System::AdjustPrivilege(const TCHAR* privilege, bool enable) {
-  ASSERT1(privilege);
-
-  scoped_handle token;
-  if (!::OpenProcessToken(::GetCurrentProcess(),
-                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
-                          address(token))) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[System::AdjustPrivilege - failed to ")
-                           _T("OpenProcessToken][0x%x]"), hr));
-    return hr;
-  }
-
-  LUID luid = {0};
-  if (!::LookupPrivilegeValue(NULL, privilege, &luid)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[System::AdjustPrivilege - failed to")
-                           _T("LookupPrivilegeValue][0x%x]"), hr));
-    return hr;
-  }
-
-  TOKEN_PRIVILEGES privs;
-  privs.PrivilegeCount = 1;
-  privs.Privileges[0].Luid = luid;
-  privs.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
-
-  if (!::AdjustTokenPrivileges(get(token), FALSE, &privs, 0, NULL, 0)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[System::AdjustPrivilege - failed to ")
-                           _T("AdjustTokenPrivileges][0x%x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-DWORD System::WTSGetActiveConsoleSessionId()  {
-  typedef DWORD (* Fun)();
-
-  HINSTANCE hInst = ::GetModuleHandle(_T("kernel32.dll"));
-  ASSERT1(hInst);
-  Fun pfn = reinterpret_cast<Fun>(::GetProcAddress(
-                                      hInst,
-                                      "WTSGetActiveConsoleSessionId"));
-  return !pfn ? kInvalidSessionId : (*pfn)();
-}
-
-// Get the session the current process is running under
-DWORD System::GetCurrentSessionId() {
-  DWORD session_id = kInvalidSessionId;
-  DWORD* session_id_ptr = NULL;
-  DWORD bytes_returned = 0;
-
-  if (::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,
-                                   WTS_CURRENT_SESSION,
-                                   WTSSessionId,
-                                   reinterpret_cast<LPTSTR*>(&session_id_ptr),
-                                   &bytes_returned)) {
-    ASSERT1(bytes_returned == sizeof(*session_id_ptr));
-    session_id = *session_id_ptr;
-    ::WTSFreeMemory(session_id_ptr);
-    UTIL_LOG(L6, (_T("[System::GetCurrentSessionId]")
-                  _T("[session_id from ::WTSQuerySessionInformation][%d]"),
-                  session_id));
-    return session_id;
-  }
-
-  // ::WTSQuerySessionInformation can fail if we are not running
-  // in a Terminal Services scenario, in which case, we use
-  // ::ProcessIdToSessionId()
-  if (::ProcessIdToSessionId(::GetCurrentProcessId(), &session_id)) {
-    UTIL_LOG(L6,  (_T("[System::GetCurrentSessionId]")
-                   _T("[session_id from ::ProcessIdToSessionId][%d]"),
-                   session_id));
-    return session_id;
-  }
-
-  UTIL_LOG(LEVEL_ERROR,
-           (_T("[System::GetCurrentSessionId - both")
-            _T("::WTSQuerySessionInformation and ")
-            _T("::ProcessIdToSessionId failed][0x%x]"),
-            ::GetLastError()));
-
-  return kInvalidSessionId;
-}
-
-// Get the best guess as to the currently active session, or kInvalidSessionId
-// if there is no active session.
-DWORD System::GetActiveSessionId() {
-  // WTSGetActiveConsoleSessionId retrieves the Terminal Services session
-  // currently attached to the physical console.
-  DWORD active_session_id = WTSGetActiveConsoleSessionId();
-
-  if (IsSessionActive(active_session_id)) {
-    UTIL_LOG(L6, (_T("[System::GetActiveSessionId]")
-                  _T("[Active session id from ::WTSGetActiveConsoleSessionId]")
-                  _T("[%d]"), active_session_id));
-
-    return active_session_id;
-  }
-
-  // WTSGetActiveConsoleSessionId works for FUS, but it does not work for TS
-  // servers where the current active session is always the console. We then use
-  // a different method as below. We get all the sessions that are present on
-  // the system, to see if we can find an active session.
-  active_session_id = kInvalidSessionId;
-  WTS_SESSION_INFO* session_info = NULL;
-  DWORD num_sessions = 0;
-  if (::WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1,
-                              &session_info, &num_sessions)) {
-    // Pick the first active session we can find
-    for (DWORD i = 0 ; i < num_sessions; ++i) {
-      if (session_info[i].State == WTSActive) {
-        // There is a user logged on to the WinStation associated with the
-        // session.
-        active_session_id = session_info[i].SessionId;
-        break;
-      }
-    }
-
-    ::WTSFreeMemory(session_info);
-    UTIL_LOG(L6, (_T("[System::GetActiveSessionId]")
-                  _T("[Active session id from ::WTSEnumerateSessions][0x%x]"),
-                  active_session_id));
-
-    return active_session_id;
-  }
-
-  UTIL_LOG(LEVEL_ERROR,
-           (_T("[System::GetActiveSessionId - ")
-           _T("Both ::WTSGetActiveConsoleSessionId and ::WTSEnumerateSessions ")
-           _T("failed][0x%x]"),
-           ::GetLastError()));
-
-  return kInvalidSessionId;
-}
-
-// Is there a user logged on and active in the specified session?
-bool System::IsSessionActive(DWORD session_id) {
-  if (kInvalidSessionId == session_id) {
-    return false;
-  }
-
-  WTS_CONNECTSTATE_CLASS wts_connect_state = WTSDisconnected;
-  WTS_CONNECTSTATE_CLASS* ptr_wts_connect_state = NULL;
-  DWORD bytes_returned = 0;
-  if (::WTSQuerySessionInformation(
-          WTS_CURRENT_SERVER_HANDLE,
-          session_id,
-          WTSConnectState,
-          reinterpret_cast<LPTSTR*>(&ptr_wts_connect_state),
-          &bytes_returned)) {
-    ASSERT1(bytes_returned == sizeof(*ptr_wts_connect_state));
-    wts_connect_state = *ptr_wts_connect_state;
-    ::WTSFreeMemory(ptr_wts_connect_state);
-
-    UTIL_LOG(L6, (_T("[System::IsSessionActive]")
-                  _T("[wts_connect_state %d]"), wts_connect_state));
-    return WTSActive == wts_connect_state;
-  }
-
-  UTIL_LOG(LE, (_T("[WTSQuerySessionInformation failed][0x%x]"),
-                ::GetLastError()));
-  return false;
-}
-
-// Is the current process running under WinSta0
-bool System::IsCurrentProcessInteractive() {
-  // Use a non-scoped handle, since a handle retrieved via
-  // ::GetProcessWindowStation() should not be closed.
-  HWINSTA handle_window_station(::GetProcessWindowStation());
-  DWORD len = 0;
-  CString str_window_station;
-
-  if (!handle_window_station || handle_window_station == INVALID_HANDLE_VALUE) {
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[System::IsCurrentProcessInteractive - ")
-              _T("::GetProcessWindowStation() failed (%d)]"),
-              ::GetLastError()));
-    return false;
-  }
-
-  if (!::GetUserObjectInformation(handle_window_station,
-                                  UOI_NAME,
-                                  CStrBuf(str_window_station, MAX_PATH),
-                                  MAX_PATH,
-                                  &len)) {
-    UTIL_LOG(LEVEL_ERROR,
-             (_T("[System::IsCurrentProcessInteractive - ")
-              _T("::GetUserObjectInfoformation(hWinSta) failed (%d)]"),
-              ::GetLastError()));
-    return false;
-  }
-
-  UTIL_LOG(L6, (_T("[System::IsCurrentProcessInteractive]")
-                _T("[WindowStation name][%s]"),
-                str_window_station));
-  return (str_window_station == _T("WinSta0"));
-}
-
-// is the current process running under WinSta0 for the currently active session
-bool System::IsCurrentProcessActiveAndInteractive() {
-  return IsSessionActive(GetCurrentSessionId()) &&
-         IsCurrentProcessInteractive();
-}
-
-bool System::IsRunningOnBatteries() {
-  SYSTEM_POWER_STATUS system_power_status = {0};
-  if (::GetSystemPowerStatus(&system_power_status)) {
-    bool has_battery = !(system_power_status.BatteryFlag & 128);
-    bool ac_status_offline = system_power_status.ACLineStatus == 0;
-    return ac_status_offline && has_battery;
-  }
-  return false;
-}
-
-}  // namespace omaha
-
diff --git a/common/system.h b/common/system.h
deleted file mode 100644
index 017b042..0000000
--- a/common/system.h
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// system functions for checking disk space / memory usage / etc.
-
-#ifndef OMAHA_COMMON_SYSTEM_H__
-#define OMAHA_COMMON_SYSTEM_H__
-
-#include <atlstr.h>
-#include <base/basictypes.h>
-
-namespace omaha {
-
-#define kMaxRegistryBackupWaitMs 3000
-#define kMaxRegistryRestoreWaitMs 30000
-
-// amount of time the user must have no input before we declare them idle
-// used by outlook addin, outlook_cap, and filecap.
-#define kUserIdleThresholdMs 30000
-
-// The user is busy typing or interacting with another application
-// if the user is below the minimum threshold.
-#define kUserIdleMinThresholdMs 30000
-
-// The user is probably not paying attention
-// if the user is above the maximum threshold.
-#define kUserIdleMaxThresholdMs 600000
-
-const DWORD kInvalidSessionId = 0xFFFFFFFF;
-
-class System {
-  public:
-
-    // disk activity.
-
-    // waits up to specified time for disk activity to occur; sleeps in
-    // increments of sleep_time.
-    static HRESULT WaitForDiskActivity(uint32 max_delay_milliseconds,
-                                       uint32 sleep_time_ms,
-                                       uint32 *time_waited);
-    // disk activity counters; may require admin on some machines? should return
-    // E_FAIL if so.
-    static HRESULT GetDiskActivityCounters(uint64 *reads,
-                                           uint64 *writes,
-                                           uint64 *bytes_read,
-                                           uint64 *bytes_written);
-
-    // disk statistics.
-
-    // disk total and free space.
-    // Path is either the root of a drive or an existing folder on a drive; the
-    // statistics are for that drive.
-    static HRESULT GetDiskStatistics(const TCHAR* path,
-                                     uint64 *free_bytes_current_user,
-                                     uint64 *total_bytes_current_user,
-                                     uint64 *free_bytes_all_users);
-
-    enum Priority {
-        LOW,
-        HIGH,
-        NORMAL,
-        IDLE
-    };
-
-    // functions to alter process/thread priority.
-    static HRESULT SetThreadPriority(enum Priority priority);
-    static HRESULT SetProcessPriority(enum Priority priority);
-
-    // The three functions below start background processes via ::CreateProcess.
-    // Use the ShellExecuteProcessXXX functions when starting foreground
-    // processes.
-    static HRESULT StartProcessWithArgs(const TCHAR *process_name,
-                                        const TCHAR *cmd_line_arguments);
-    static HRESULT StartProcessWithArgsAndInfo(const TCHAR *process_name,
-                                               const TCHAR *cmd_line_arguments,
-                                               PROCESS_INFORMATION *pi);
-    static HRESULT StartProcess(const TCHAR *process_name,
-                                TCHAR *command_line,
-                                PROCESS_INFORMATION *pi);
-
-
-    // start another process painlessly via ::ShellExecuteEx. Use this method
-    // instead of the StartProcessXXX methods that use ::CreateProcess where
-    // possible, since ::ShellExecuteEx has better behavior on Vista.
-    static HRESULT ShellExecuteProcess(const TCHAR* file_name_to_execute,
-                                       const TCHAR* command_line_parameters,
-                                       HWND hwnd,
-                                       HANDLE* process_handle);
-
-    // start another process painlessly via ::ShellExecuteEx. Use this method
-    // instead of the StartProcessXXX methods that use ::CreateProcess where
-    // possible, since ::ShellExecuteEx has better behavior on Vista.
-    static HRESULT ShellExecuteCommandLine(const TCHAR* command_line_to_execute,
-                                           HWND hwnd,
-                                           HANDLE* process_handle);
-
-    // memory statistics.
-
-    // max amount of memory that can be allocated without paging.
-    static HRESULT MaxPhysicalMemoryAvailable(uint64 *max_bytes);
-
-    // global memory stats
-    static HRESULT GetGlobalMemoryStatistics(
-                       uint32 *memory_load_percentage,
-                       uint64 *free_physical_memory,
-                       uint64 *total_physical_memory,
-                       uint64 *free_paged_memory,
-                       uint64 *total_paged_memory,
-                       uint64 *process_free_virtual_memory,
-                       uint64 *process_total_virtual_memory);
-
-    // process memory stats
-    static HRESULT GetProcessMemoryStatistics(uint64 *current_working_set,
-                                              uint64 *peak_working_set,
-                                              uint64 *min_working_set_size,
-                                              uint64 *max_working_set_size);
-
-    // TODO(omaha): determine if using this where we do with machines
-    // with slow disks causes noticeable slowdown
-
-    // reduce process working set - beware of possible negative performance
-    // implications - this function frees (to the page cache) all used pages,
-    // minimizing the working set - but could lead to additional page faults
-    // when the process continues. If the process continues soon enough the
-    // pages will still be in the page cache so they'll be relatively cheap
-    // soft page faults. This function is best used to reduce memory footprint
-    // when a component is about to go idle for "awhile".
-    static void FreeProcessWorkingSet();
-
-    // returns the number of ms the system has had no user input.
-    static int GetUserIdleTime();
-
-    // from ntddk.h, used as a parameter to get the process handle count.
-    static const int kProcessHandleCount = 20;
-    static uint32 GetProcessHandleCount();
-    static uint32 GetProcessHandleCountOld();
-
-    static void GetGuiObjectCount(uint32 *gdi, uint32 *user);
-
-    static bool IsUserIdle();
-    static bool IsUserBusy();
-    static bool IsScreensaverRunning();
-    static bool IsWorkstationLocked();
-    static bool IsUserAway();
-
-    // Is the system requiring reboot.
-    static bool IsRebooted(const TCHAR* base_file);
-
-    // Mark the system as reboot required.
-    static HRESULT MarkAsRebootRequired(const TCHAR* base_file);
-
-    // Unmark the system as reboot required.
-    static HRESULT UnmarkAsRebootRequired(const TCHAR* base_file);
-
-    // Restart the computer.
-    static HRESULT RestartComputer();
-
-    // Get the full path name of the screen saver program currently selected.
-    // If no screen saver is selected then "fileName" is empty.
-    static HRESULT GetCurrentScreenSaver(CString* fileName);
-
-    // Creates an instance of a COM Local Server class using either plain
-    // vanilla CoCreateInstance, or using the Elevation moniker depending on the
-    // operating system.
-    static HRESULT CoCreateInstanceAsAdmin(HWND hwnd,
-                                           REFCLSID rclsid,
-                                           REFIID riid,
-                                           void** ppv);
-
-    // Attempts to adjust current process privileges.
-    // Only process running with administrator privileges will succeed.
-    static HRESULT AdjustPrivilege(const TCHAR* privilege, bool enable);
-
-    // Checks if the given privilege is enabled for the current process.
-    static HRESULT IsPrivilegeEnabled(const TCHAR* privilege, bool* present);
-
-    // Dynamically links and calls ::WTSGetActiveConsoleSessionId(). Returns
-    // kInvalidSessionId if it cannot find the export in kernel32.dll.
-    static DWORD WTSGetActiveConsoleSessionId();
-
-    // Get the session the current process is running under.
-    static DWORD GetCurrentSessionId();
-
-    // Get the best guess as to the currently active session,
-    // or kInvalidSessionId if there is no active session.
-    static DWORD GetActiveSessionId();
-
-    // Is there a user logged on and active in the specified session?
-    static bool IsSessionActive(DWORD session_id);
-
-    // Is the current process running under WinSta0.
-    static bool IsCurrentProcessInteractive();
-
-    // is the current process running under WinSta0 for the currently active
-    // session.
-    static bool IsCurrentProcessActiveAndInteractive();
-
-    // Returns true if a system battery is detected and the AC line
-    // status is 'offline', otherwise it returns false.
-    static bool IsRunningOnBatteries();
-
-  private:
-    static HRESULT GetRebootCheckDummyFileName(const TCHAR* base_file,
-                                               CString* dummy_file);
-    DISALLOW_EVIL_CONSTRUCTORS(System);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_SYSTEM_H__
-
diff --git a/common/system_info.cc b/common/system_info.cc
deleted file mode 100644
index 830c94a..0000000
--- a/common/system_info.cc
+++ /dev/null
@@ -1,462 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/system_info.h"
-#include "base/basictypes.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/string.h"
-
-namespace omaha {
-
-bool SystemInfo::OSWinXPSP2OrLater() {
-  OSVersionType os_type(OS_WINDOWS_UNKNOWN);
-  DWORD sp(0);
-
-  HRESULT hr = CategorizeOS(&os_type, &sp);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("[CategorizeOS failed][0x%x]"), hr));
-    return false;
-  }
-
-  return ((os_type == SystemInfo::OS_WINDOWS_XP && sp >= 2) ||
-          os_type > SystemInfo::OS_WINDOWS_XP);
-}
-
-bool SystemInfo::IsRunningOnW2K() {
-  OSVERSIONINFO os_info = {0};
-  os_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
-
-  if (!::GetVersionEx(&os_info)) {
-    ASSERT(false, (L"GetVersionEx"));
-    return false;
-  }
-
-  return os_info.dwMajorVersion == 5 && os_info.dwMinorVersion == 0;
-}
-
-bool SystemInfo::IsRunningOnXPOrLater() {
-  OSVersionType os_type(OS_WINDOWS_UNKNOWN);
-
-  HRESULT hr = CategorizeOS(&os_type, NULL);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("[Failed to get os type][0x%x]"), hr));
-    return false;
-  }
-
-  return os_type >= SystemInfo::OS_WINDOWS_XP;
-}
-
-bool SystemInfo::IsRunningOnXPSP1OrLater() {
-  OSVersionType os_type(OS_WINDOWS_UNKNOWN);
-  DWORD sp(0);
-
-  HRESULT hr = CategorizeOS(&os_type, &sp);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("[Failed to get os type][0x%x]"), hr));
-    return false;
-  }
-
-  return ((os_type == SystemInfo::OS_WINDOWS_XP && sp >= 1) ||
-          os_type > SystemInfo::OS_WINDOWS_XP);
-}
-
-
-bool SystemInfo::IsRunningOnVistaOrLater() {
-  OSVersionType os_type(OS_WINDOWS_UNKNOWN);
-  DWORD sp(0);
-
-  HRESULT hr = CategorizeOS(&os_type, &sp);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("[Failed to get os type][0x%x]"), hr));
-    return false;
-  }
-
-  return (os_type >= OS_WINDOWS_VISTA);
-}
-
-bool SystemInfo::IsRunningOnVistaRTM() {
-  OSVersionType os_type(OS_WINDOWS_UNKNOWN);
-  DWORD sp(0);
-
-  HRESULT hr = CategorizeOS(&os_type, &sp);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("[Failed to get os type][0x%x]"), hr));
-    return false;
-  }
-
-  return (os_type == SystemInfo::OS_WINDOWS_VISTA && sp == 0);
-}
-
-HRESULT SystemInfo::CategorizeOS(OSVersionType* os_ver, DWORD* sp) {
-  static OSVersionType os_ver_cached(OS_WINDOWS_UNKNOWN);
-  // Hopefully, Windows doesn't release a SP that's kUint32Max.
-  static DWORD sp_cached(kUint32Max);
-
-  ASSERT(os_ver, (L""));
-
-  if (sp) {
-    *sp = 0;
-  }
-
-  if (os_ver_cached == OS_WINDOWS_UNKNOWN || sp_cached == kUint32Max) {
-    // Use GetVersionEx to get OS and Service Pack information.
-    OSVERSIONINFOEX osviex;
-    ::ZeroMemory(&osviex, sizeof(OSVERSIONINFOEX));
-    osviex.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
-    BOOL r = ::GetVersionEx(reinterpret_cast<OSVERSIONINFO *>(&osviex));
-
-    // If ::GetVersionEx fails when given an OSVERSIONINFOEX then we're running
-    // on NT4.0SP5 or earlier.
-    if (!r) {
-      os_ver_cached = OS_WINDOWS_9X_OR_NT;
-    } else {
-      switch (osviex.dwPlatformId) {
-        case VER_PLATFORM_WIN32_NT:
-          // Windows 7 beta 1 reports the same major version as Vista does.
-          if (osviex.dwMajorVersion == 6 && osviex.dwMinorVersion == 1) {
-            os_ver_cached = OS_WINDOWS_7;
-          } else if (osviex.dwMajorVersion == 6 && osviex.dwMinorVersion == 0) {
-            os_ver_cached = OS_WINDOWS_VISTA;
-          } else if (osviex.dwMajorVersion == 5 && osviex.dwMinorVersion == 2) {
-            os_ver_cached = OS_WINDOWS_SERVER_2003;
-          } else if (osviex.dwMajorVersion == 5 && osviex.dwMinorVersion == 1) {
-            os_ver_cached = OS_WINDOWS_XP;
-          } else if (osviex.dwMajorVersion == 5 && osviex.dwMinorVersion == 0) {
-            os_ver_cached = OS_WINDOWS_2000;
-          } else if (osviex.dwMajorVersion <= 4) {
-            os_ver_cached = OS_WINDOWS_9X_OR_NT;
-            break;
-          } else {
-            os_ver_cached = OS_WINDOWS_UNKNOWN;
-            break;
-          }
-          sp_cached = osviex.wServicePackMajor;
-          break;
-
-        case VER_PLATFORM_WIN32_WINDOWS:
-        case VER_PLATFORM_WIN32s:
-        default:
-          os_ver_cached = OS_WINDOWS_9X_OR_NT;
-          break;
-      }
-    }
-
-    UTIL_LOG(L1, (L"[CategorizeOS][version %s][service pack %d]",
-                  OSVersionTypeAsString(os_ver_cached),
-                  sp_cached));
-  }
-
-  ASSERT1(os_ver_cached != OS_WINDOWS_UNKNOWN && sp_cached != kUint32Max);
-
-  *os_ver = os_ver_cached;
-  if (sp) {
-    *sp = sp_cached;
-  }
-
-  return S_OK;
-}
-
-const wchar_t* SystemInfo::OSVersionTypeAsString(OSVersionType t) {
-  switch (t) {
-    case OS_WINDOWS_9X_OR_NT:    return L"OS_WINDOWS_9X_OR_NT";
-    case OS_WINDOWS_2000:        return L"OS_WINDOWS_2000";
-    case OS_WINDOWS_XP:          return L"OS_WINDOWS_XP";
-    case OS_WINDOWS_SERVER_2003: return L"OS_WINDOWS_SERVER_2003";
-    case OS_WINDOWS_UNKNOWN:     return L"OS_WINDOWS_UNKNOWN";
-    case OS_WINDOWS_VISTA:       return L"OS_WINDOWS_VISTA";
-    case OS_WINDOWS_7:           return L"OS_WINDOWS_7";
-    default:                     return L"<unknown>";
-  }
-}
-
-// The following code which names the operating system comes from MSDN article
-// "Getting the System Version"
-#define kNullChar (_T('\0'))
-bool SystemInfo::GetSystemVersion(int* major_version,
-                                  int* minor_version,
-                                  int* service_pack_major,
-                                  int* service_pack_minor,
-                                  TCHAR*  name_buf,
-                                  size_t name_buf_len) {
-  ASSERT1(major_version);
-  ASSERT1(minor_version);
-  ASSERT1(service_pack_major);
-  ASSERT1(service_pack_minor);
-  ASSERT1(name_buf);
-  ASSERT1(0 < name_buf_len);
-
-  // Clear the name to start with.
-  name_buf[0] = kNullChar;
-
-  DWORD buf_len = MAX_PATH;
-  TCHAR buffer[MAX_PATH];
-  TCHAR format_buffer[64];
-
-  buffer[0] = kNullChar;
-
-  OSVERSIONINFOEX osvi;
-  BOOL ver_info_exists;
-
-  // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
-  // If that fails, try using the OSVERSIONINFO structure.
-  ::ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
-  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
-
-  ver_info_exists = ::GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&osvi));
-  if (!ver_info_exists) {
-    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
-    if (!::GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&osvi))) {
-      return false;
-    }
-  }
-
-  *major_version      = osvi.dwMajorVersion;
-  *minor_version      = osvi.dwMinorVersion;
-  *service_pack_major = osvi.wServicePackMajor;
-  *service_pack_minor = osvi.wServicePackMinor;
-
-  switch (osvi.dwPlatformId) {
-    // Test for the Windows NT product family.
-    case VER_PLATFORM_WIN32_NT:
-
-      // Test for the specific product family.
-      if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
-        SafeStrCat(buffer,
-                   _T("Microsoft Windows Server 2003 family, "),
-                   buf_len);
-      }
-
-      if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) {
-        SafeStrCat(buffer, _T("Microsoft Windows XP "), buf_len);
-      }
-
-      if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
-        SafeStrCat(buffer, _T("Microsoft Windows 2000 "), buf_len);
-      }
-
-      if (osvi.dwMajorVersion <= 4) {
-        SafeStrCat(buffer, _T("Microsoft Windows NT "), buf_len);
-      }
-
-      // Test for specific product on Windows NT 4.0 SP6 and later.
-      if (ver_info_exists) {
-        // Test for the workstation type.
-        if (osvi.wProductType == VER_NT_WORKSTATION) {
-          if (osvi.dwMajorVersion == 4) {
-            SafeStrCat(buffer, _T("Workstation 4.0 "), buf_len);
-          } else if (osvi.wSuiteMask & VER_SUITE_PERSONAL) {
-            SafeStrCat(buffer, _T("Home Edition "), buf_len);
-          } else {
-            SafeStrCat(buffer, _T("Professional "), buf_len);
-          }
-        } else if (osvi.wProductType == VER_NT_SERVER ||
-                   osvi.wProductType == VER_NT_DOMAIN_CONTROLLER) {
-          // server type.
-          if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
-            if (osvi.wSuiteMask & VER_SUITE_DATACENTER) {
-              SafeStrCat(buffer, _T("Datacenter Edition "), buf_len);
-            } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
-              SafeStrCat(buffer, _T("Enterprise Edition "), buf_len);
-            } else if (osvi.wSuiteMask == VER_SUITE_BLADE) {
-              SafeStrCat(buffer, _T("Web Edition "), buf_len);
-            } else {
-              SafeStrCat(buffer, _T("Standard Edition "), buf_len);
-            }
-          } else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) {
-            if (osvi.wSuiteMask & VER_SUITE_DATACENTER) {
-              SafeStrCat(buffer, _T("Datacenter Server "), buf_len);
-            } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
-              SafeStrCat(buffer, _T("Advanced Server "), buf_len);
-            } else {
-              SafeStrCat(buffer, _T("Server "), buf_len);
-            }
-          } else {
-            // Windows NT 4.0.
-            if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) {
-              SafeStrCat(buffer,
-                         _T("Server 4.0, Enterprise Edition "),
-                         buf_len);
-            } else {
-              SafeStrCat(buffer, _T("Server 4.0 "), buf_len);
-            }
-          }
-        }
-      } else {
-        // Test for specific product on Windows NT 4.0 SP5 and earlier.
-        HKEY hKey;
-        TCHAR product_type[64] = {0};
-        DWORD dwBufLen = arraysize(product_type);
-        LONG lRet;
-
-        // TODO(omaha): should we use the RegKey API for consistency.
-        lRet = ::RegOpenKeyEx(
-                   HKEY_LOCAL_MACHINE,
-                   _T("SYSTEM\\CurrentControlSet\\Control\\ProductOptions"),
-                   0,
-                   KEY_QUERY_VALUE,
-                   &hKey);
-        if (lRet != ERROR_SUCCESS) {
-          return false;
-        }
-
-        lRet = ::RegQueryValueEx(hKey,
-                                 _T("ProductType"),
-                                 NULL,
-                                 NULL,
-                                 reinterpret_cast<byte *>(product_type),
-                                 &dwBufLen);
-        if ((lRet != ERROR_SUCCESS) || (dwBufLen > arraysize(product_type))) {
-          return false;
-        }
-
-        ::RegCloseKey(hKey);
-
-        if (::lstrcmpi(_T("WINNT"), product_type) == 0) {
-          SafeStrCat(buffer, _T("Workstation "), buf_len);
-        }
-        if (::lstrcmpi(_T("LANMANNT"), product_type) == 0) {
-          SafeStrCat(buffer, _T("Server "), buf_len);
-        }
-        if (::lstrcmpi(_T("SERVERNT"), product_type) == 0) {
-          SafeStrCat(buffer, _T("Advanced Server "), buf_len);
-        }
-
-        ::wsprintf(format_buffer,
-                   _T("%d.%d "),
-                   osvi.dwMajorVersion,
-                   osvi.dwMinorVersion);
-        SafeStrCat(buffer, format_buffer, buf_len);
-      }
-
-      // Display service pack (if any) and build number.
-      if (osvi.dwMajorVersion == 4 &&
-          ::lstrcmpi(osvi.szCSDVersion, _T("Service Pack 6")) == 0) {
-        HKEY hKey;
-        LONG lRet;
-
-        // Test for SP6 versus SP6a.
-        lRet = ::RegOpenKeyEx(
-                   HKEY_LOCAL_MACHINE,
-                   _T("SOFTWARE\\Microsoft\\Windows NT\\")
-                       _T("CurrentVersion\\Hotfix\\Q246009"),
-                   0,
-                   KEY_QUERY_VALUE,
-                   &hKey);
-        if (lRet == ERROR_SUCCESS) {
-          ::wsprintf(format_buffer,
-                     _T("Service Pack 6a (Build %d)"),
-                     osvi.dwBuildNumber & 0xFFFF);
-          SafeStrCat(buffer, format_buffer, buf_len);
-        } else {
-          // Windows NT 4.0 prior to SP6a.
-          ::wsprintf(format_buffer, _T("%s (Build %d)"),
-                      osvi.szCSDVersion,
-                      osvi.dwBuildNumber & 0xFFFF);
-          SafeStrCat(buffer, format_buffer, buf_len);
-        }
-        ::RegCloseKey(hKey);
-      } else {
-        // Windows NT 3.51 and earlier or Windows 2000 and later.
-        ::wsprintf(format_buffer,
-                   _T("%s (Build %d)"),
-                   osvi.szCSDVersion,
-                   osvi.dwBuildNumber & 0xFFFF);
-        SafeStrCat(buffer, format_buffer, buf_len);
-      }
-
-      break;
-
-      // Test for the Windows 95 product family.
-    case VER_PLATFORM_WIN32_WINDOWS:
-
-      if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) {
-        SafeStrCat(buffer, _T("Microsoft Windows 95 "), buf_len);
-        if (osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B') {
-          SafeStrCat(buffer, _T("OSR2 "), buf_len);
-        }
-      }
-
-      if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) {
-        SafeStrCat(buffer, _T("Microsoft Windows 98 "), buf_len);
-        if (osvi.szCSDVersion[1] == 'A') {
-          SafeStrCat(buffer, _T("SE "), buf_len);
-        }
-      }
-
-      if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) {
-        SafeStrCat(buffer,
-                   _T("Microsoft Windows Millennium Edition"),
-                   buf_len);
-      }
-      break;
-
-    case VER_PLATFORM_WIN32s:
-
-      SafeStrCat(buffer, _T("Microsoft Win32s"), buf_len);
-      break;
-
-  default:
-    SafeStrCat(buffer, _T("Unknown operating system"), buf_len);
-    break;
-  }
-  // SKIP_LOC_END
-
-  // Remove trailing space, if any.
-  DWORD buffer_len = ::lstrlen(buffer);
-  if (buffer[buffer_len-1] == kNullChar) {
-    buffer[buffer_len-1] = kNullChar;
-  }
-
-  // Copy to destination argument.
-  String_StrNCpy(name_buf, buffer, name_buf_len);
-
-  return true;
-}
-
-
-bool SystemInfo::IsRunningOn64Bit() {
-  static DWORD is64_cached(kUint32Max);
-
-  if (is64_cached == kUint32Max) {
-    typedef void (WINAPI * GetSystemInfoFunc)(LPSYSTEM_INFO);
-
-    HMODULE handle = ::GetModuleHandle(_T("kernel32"));
-    ASSERT1(handle);
-    GetSystemInfoFunc get_native_system_info =
-        reinterpret_cast<GetSystemInfoFunc>(::GetProcAddress(
-                                                handle,
-                                                "GetNativeSystemInfo"));
-
-    if (get_native_system_info != NULL) {
-      SYSTEM_INFO sys_info = {0};
-
-      get_native_system_info(&sys_info);
-
-      is64_cached =
-          sys_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
-    } else {
-      // If we couldn't get the _native_ system info, then we must be on OS
-      // earlier than XP, so can't be 64-bit anyway.
-      is64_cached = 0;
-    }
-  }
-
-  return is64_cached != 0;
-}
-
-
-}  // namespace omaha
-
diff --git a/common/system_info.h b/common/system_info.h
deleted file mode 100644
index 2acba56..0000000
--- a/common/system_info.h
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// TODO(omaha): this code should be updated according to code published by
-// Microsoft at http://msdn.microsoft.com/en-us/library/ms724429(VS.85).aspx.
-// We need a more rigorous clasification of versions.
-
-#ifndef OMAHA_COMMON_SYSTEM_INFO_H__
-#define OMAHA_COMMON_SYSTEM_INFO_H__
-
-#include <windows.h>
-#include <tchar.h>
-
-namespace omaha {
-
-// TODO(omaha): refactor to use a namespace.
-class SystemInfo {
- public:
-  // Find out if the OS is at least Windows 2000
-  // Service pack 4. If OS version is less than that
-  // will return false, all other cases true.
-  static bool OSWin2KSP4OrLater() {
-    // Use GetVersionEx to get OS and Service Pack information.
-    OSVERSIONINFOEX osviex;
-    ::ZeroMemory(&osviex, sizeof(osviex));
-    osviex.dwOSVersionInfoSize = sizeof(osviex);
-    BOOL success = ::GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&osviex));
-    // If this failed we're on Win9X or a pre NT4SP6 OS.
-    if (!success) {
-      return false;
-    }
-
-    if (osviex.dwMajorVersion < 5) {
-      return false;
-    }
-    if (osviex.dwMajorVersion > 5) {
-      return true;    // way beyond Windows XP.
-    }
-    if (osviex.dwMinorVersion >= 1) {
-      return true;    // Windows XP or better.
-    }
-    if (osviex.wServicePackMajor >= 4) {
-      return true;    // Windows 2000 SP4.
-    }
-
-    return false;     // Windows 2000, < SP4.
-  }
-
-  // Returns true if the OS is at least XP SP2.
-  static bool OSWinXPSP2OrLater();
-
-  // CategorizeOS returns a categorization of what operating system is running,
-  // and the service pack level.
-  // NOTE: Please keep this in the order of increasing OS versions
-  enum OSVersionType {
-    OS_WINDOWS_UNKNOWN = 1,
-    OS_WINDOWS_9X_OR_NT,
-    OS_WINDOWS_2000,
-    OS_WINDOWS_XP,
-    OS_WINDOWS_SERVER_2003,
-    OS_WINDOWS_VISTA,
-    OS_WINDOWS_7
-  };
-  static HRESULT CategorizeOS(OSVersionType* os_version, DWORD* service_pack);
-  static const wchar_t* OSVersionTypeAsString(OSVersionType t);
-
-  // Returns true if the current operating system is Windows 2000.
-  static bool IsRunningOnW2K();
-
-  // Are we running on Windows XP or later.
-  static bool IsRunningOnXPOrLater();
-
-  // Are we running on Windows XP SP1 or later.
-  static bool IsRunningOnXPSP1OrLater();
-
-  // Are we running on Windows Vista or later.
-  static bool IsRunningOnVistaOrLater();
-
-  static bool IsRunningOnVistaRTM();
-
-  // Returns the version and the name of the operating system.
-  static bool GetSystemVersion(int* major_version,
-                               int* minor_version,
-                               int* service_pack_major,
-                               int* service_pack_minor,
-                               TCHAR* name_buf,
-                               size_t name_buf_len);
-
-  // Returns whether this is a 64-bit system.
-  static bool IsRunningOn64Bit();
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_SYSTEM_INFO_H__
-
diff --git a/common/system_info_unittest.cc b/common/system_info_unittest.cc
deleted file mode 100644
index cadfe18..0000000
--- a/common/system_info_unittest.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <atlstr.h>
-#include "omaha/common/system_info.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(SystemInfoTest, SystemInfo) {
-  SystemInfo::OSVersionType os_type = SystemInfo::OS_WINDOWS_UNKNOWN;
-  DWORD service_pack = 0;
-  ASSERT_SUCCEEDED(SystemInfo::CategorizeOS(&os_type, &service_pack));
-}
-
-TEST(SystemInfoTest, GetSystemVersion) {
-  int major_version(0);
-  int minor_version(0);
-  int service_pack_major(0);
-  int service_pack_minor(0);
-
-  CString name;
-  ASSERT_TRUE(SystemInfo::GetSystemVersion(&major_version,
-                                           &minor_version,
-                                           &service_pack_major,
-                                           &service_pack_minor,
-                                           CStrBuf(name, MAX_PATH),
-                                           MAX_PATH));
-  EXPECT_NE(0, major_version);
-  EXPECT_EQ(0, service_pack_minor);
-
-  EXPECT_FALSE(name.IsEmpty());
-}
-
-}  // namespace omaha
-
diff --git a/common/system_unittest.cc b/common/system_unittest.cc
deleted file mode 100644
index 010f49c..0000000
--- a/common/system_unittest.cc
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-// System unittest
-//
-// TODO(omaha): there are some untested functions: memory stats, thread
-// priorities, getdirsize (that's mine), backup/restore of registry trees.. not
-// sure how high priority it is to test these things but should probably be
-// added
-
-#include "omaha/common/system.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(SystemTest, System) {
-    uint32 time_waited = 0;
-    ASSERT_SUCCEEDED(System::WaitForDiskActivity(10000, 25, &time_waited));
-
-    uint64 free_bytes_current_user = 0;
-    uint64 total_bytes_current_user = 0;
-    uint64 free_bytes_all_users = 0;
-    ASSERT_SUCCEEDED(System::GetDiskStatistics(_T("C:\\"),
-                                               &free_bytes_current_user,
-                                               &total_bytes_current_user,
-                                               &free_bytes_all_users));
-
-    ASSERT_EQ(System::GetProcessHandleCount(),
-              System::GetProcessHandleCountOld());
-}
-
-// Assume the workstations and PULSE are not running on batteries. The test
-// fails on laptops running on batteries.
-TEST(SystemTest, IsRunningOnBatteries) {
-  ASSERT_FALSE(System::IsRunningOnBatteries());
-}
-
-TEST(SystemTest, GetProcessMemoryStatistics) {
-  uint64 current_working_set(0);
-  uint64 peak_working_set(0);
-  uint64 min_working_set_size(0);
-  uint64 max_working_set_size(0);
-  ASSERT_HRESULT_SUCCEEDED(
-    System::GetProcessMemoryStatistics(&current_working_set,
-                                       &peak_working_set,
-                                       &min_working_set_size,
-                                       &max_working_set_size));
-  EXPECT_LT(0, current_working_set);
-  EXPECT_LT(0, peak_working_set);
-  EXPECT_LT(0, min_working_set_size);
-  EXPECT_LT(0, max_working_set_size);
-}
-
-TEST(SystemTest, GetProcessHandleCount) {
-  DWORD handle_count(0);
-  ASSERT_TRUE(::GetProcessHandleCount(::GetCurrentProcess(), &handle_count));
-  EXPECT_LE(0u, handle_count);
-  EXPECT_EQ(handle_count, System::GetProcessHandleCount());
-}
-
-}  // namespace omaha
-
diff --git a/common/thread.cc b/common/thread.cc
deleted file mode 100644
index f7e743b..0000000
--- a/common/thread.cc
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/thread.h"
-
-#include "omaha/common/debug.h"
-#include "omaha/common/exception_barrier.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/time.h"
-
-namespace omaha {
-
-// The system keeps an event associated with the thread message queue for a
-// while even after the thread is dead. It can appear as a handle leak in the
-// unit test but in fact it is not.
-
-Thread::Thread() : thread_id_(0), thread_(NULL) {
-}
-
-Thread::~Thread() {
-  if (thread_) {
-    VERIFY1(CloseHandle(thread_));
-  }
-  thread_ = NULL;
-}
-
-// This is the thread proc function as required by win32.
-DWORD __stdcall Thread::Prepare(void* this_pointer) {
-  ExceptionBarrier eb;
-
-  ASSERT1(this_pointer);
-  Thread   * this_thread =  reinterpret_cast<Thread*>(this_pointer);
-  Runnable * this_runner =  this_thread->runner_;
-
-  // Create a message queue. Our thread should have one.
-  MSG message = {0};
-  PeekMessage(&message, NULL, WM_USER, WM_USER, PM_NOREMOVE);
-
-  // Start method is waiting on this gate to be open in order
-  // to proceed. By opening gate we say: OK thread is running.
-  this_thread->start_gate_.Open();
-
-  // Now call the interface method. We are done.
-  UTIL_LOG(L4, (L"Thread::Prepare calling thread's Run()"));
-  this_runner->Run();
-
-  return 0;
-}
-
-// Starts the thread. It does not return until the thread is started.
-bool Thread::Start(Runnable* runner) {
-  ASSERT1(runner);
-
-  // Allow the thread object to be reused by cleaning its state up.
-  if (thread_) {
-    VERIFY1(CloseHandle(thread_));
-  }
-  start_gate_.Close();
-
-  runner_ = runner;
-  thread_ = CreateThread(NULL,              // default security attributes
-                         0,                 // use default stack size
-                         &Thread::Prepare,  // thread function
-                         this,              // argument to thread function
-                         0,                 // use default creation flags
-                         &thread_id_);      // returns the thread identifier
-  if (!thread_) {
-    return false;
-  }
-  // Wait until the newly created thread opens the gate for us.
-  return start_gate_.Wait(INFINITE);
-}
-
-DWORD Thread::GetThreadId() const {
-  return thread_id_;
-}
-
-HANDLE Thread::GetThreadHandle() const {
-  return thread_;
-}
-
-bool Thread::Suspend() {
-  return (static_cast<DWORD>(-1) != SuspendThread(thread_));
-}
-
-bool Thread::Resume() {
-  return (static_cast<DWORD>(-1) != ResumeThread(thread_));
-}
-
-bool Thread::Terminate(int exit_code) {
-  return TRUE == TerminateThread(thread_, exit_code);
-}
-
-bool Thread::SetPriority(int priority) {
-  return TRUE == SetThreadPriority(thread_, priority);
-}
-
-bool Thread::GetPriority(int* priority) const {
-  if (!priority) {
-    return false;
-  }
-  *priority = GetThreadPriority(thread_);
-  return THREAD_PRIORITY_ERROR_RETURN != *priority;
-}
-
-// Waits for handle to become signaled.
-bool Thread::WaitTillExit(DWORD msec) const {
-  if (!Running()) {
-    return true;
-  }
-  return WAIT_OBJECT_0 == WaitForSingleObject(thread_, msec);
-}
-
-// Checks if the thread is running.
-bool Thread::Running() const {
-  if (NULL == thread_) {
-    return false;
-  }
-  return WAIT_TIMEOUT == WaitForSingleObject(thread_, 0);
-}
-
-// Executes an APC request.
-void __stdcall Thread::APCProc(ULONG_PTR param) {
-  ApcInfo* pInfo = reinterpret_cast<ApcInfo*>(param);
-  if (pInfo) {
-    if (pInfo->receiver_) {
-      pInfo->receiver_->OnApc(pInfo->param_);
-    }
-    // Deallocates what was allocated in QueueApc.
-    delete pInfo;
-  }
-}
-
-// ApcReceiver wants to execute its OnApc function in the
-// context of this thread.
-bool Thread::QueueApc(ApcReceiver* receiver, ULONG_PTR param) {
-  ASSERT1(receiver);
-  if (!Running()) {
-    // No reason to queue anything to not running thread.
-    return true;
-  }
-
-  // This allocation will be freed in Thread::APCProc
-  ApcInfo* pInfo = new ApcInfo();
-  pInfo->receiver_ = receiver;
-  pInfo->param_    = param;
-  return 0 != QueueUserAPC(&Thread::APCProc,
-                           thread_,
-                           reinterpret_cast<ULONG_PTR>(pInfo));
-}
-
-bool Thread::PostMessage(UINT msg, WPARAM wparam, LPARAM lparam) {
-  return TRUE == PostThreadMessage(thread_id_, msg, wparam, lparam);
-}
-
-}  // namespace omaha
-
diff --git a/common/thread.h b/common/thread.h
deleted file mode 100644
index d2b149c..0000000
--- a/common/thread.h
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Defines interface Runnable and class Thread.
-//
-// Thread encapsulates win32 primitives of creating and
-// manipulating win32 threads.
-
-#ifndef OMAHA_COMMON_THREAD_H__
-#define OMAHA_COMMON_THREAD_H__
-
-#include "omaha/common/synchronized.h"
-
-namespace omaha {
-
-// Any class which requires part of its execution in a
-// separate thread should be derived from Runnable interface.
-// It can have member variable of type Thread. When the
-// thread needs to be launched one does something like that.
-//  A::func() {
-//    thread_.start(this);
-//  }
-
-class Runnable {
-  friend class Thread;
- protected:
-  Runnable() {}
-  virtual ~Runnable() {}
-  virtual void Run() = 0;
- private:
-  DISALLOW_EVIL_CONSTRUCTORS(Runnable);
-};
-
-// Any class devived from this one will be able to call
-// Thread function QueueApc and have the function OnApc get
-// executed in context of this thread. Thread must be in alertable
-// state to be able to execute the apc function.
-class ApcReceiver {
-  friend class Thread;
- protected:
-  ApcReceiver() {}
-  virtual ~ApcReceiver() {}
-  virtual void OnApc(ULONG_PTR param) = 0;
- private:
-  DISALLOW_EVIL_CONSTRUCTORS(ApcReceiver);
-};
-
-// This class encapsulates win32 thread management functions.
-class Thread {
- public:
-  Thread();
-  ~Thread();
-
-  bool Start(Runnable* runner);
-  bool Suspend();
-  bool Resume();
-  bool Terminate(int exit_code);
-  bool SetPriority(int priority);
-  bool GetPriority(int* priority) const;
-  DWORD GetThreadId() const;
-  HANDLE GetThreadHandle() const;
-
-  // Checks if the thread is running.
-  bool Running() const;
-
-  // Waits until thread exits.
-  bool WaitTillExit(DWORD msec) const;
-
-  // Queues an APC to the ApcReceiver.
-  bool QueueApc(ApcReceiver* receiver, ULONG_PTR param);
-
-  // Posts message to a thread.
-  bool PostMessage(UINT msg, WPARAM wparam, LPARAM lparam);
- private:
-  static DWORD __stdcall Prepare(void* thisPointer);      // Thread proc.
-  static void __stdcall APCProc(ULONG_PTR dwParam);
-
-  Runnable* runner_;     // Interface to work with.
-  HANDLE    thread_;
-  DWORD     thread_id_;
-  Gate start_gate_;     // Synchronizes the thread start.
-
-  struct ApcInfo {
-    ApcReceiver* receiver_;
-    ULONG_PTR    param_;
-  };
-
-  DISALLOW_EVIL_CONSTRUCTORS(Thread);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_THREAD_H__
diff --git a/common/thread_pool.cc b/common/thread_pool.cc
deleted file mode 100644
index c0ae30b..0000000
--- a/common/thread_pool.cc
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/thread_pool.h"
-
-#include "base/scoped_ptr.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/exception_barrier.h"
-#include "omaha/common/logging.h"
-
-namespace omaha {
-
-namespace {
-
-// Context keeps track the information necessary to execute a work item
-// inside a thread pool thread.
-class Context {
- public:
-  Context(ThreadPool* pool, UserWorkItem* work_item)
-      : pool_(pool),
-        work_item_(work_item) {
-    ASSERT1(pool);
-    ASSERT1(work_item);
-  }
-
-  ThreadPool*   pool() const { return pool_; }
-  UserWorkItem* work_item() const { return work_item_; }
-
- private:
-  ThreadPool*   pool_;
-  UserWorkItem* work_item_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(Context);
-};
-
-// Returns true if delta time since 'baseline' is greater or equal than
-// 'milisecs'. Note: GetTickCount wraps around every ~48 days.
-bool TimeHasElapsed(DWORD baseline, DWORD milisecs) {
-  DWORD current = ::GetTickCount();
-  DWORD wrap_bias = 0;
-  if (current < baseline) {
-    wrap_bias = static_cast<DWORD>(0xFFFFFFFF);
-  }
-  return (current - baseline + wrap_bias) >= milisecs ? true : false;
-}
-
-}   // namespace
-
-
-DWORD WINAPI ThreadPool::ThreadProc(void* param) {
-  ExceptionBarrier eb;
-  UTIL_LOG(L4, (_T("[ThreadPool::ThreadProc]")));
-  ASSERT1(param);
-  Context* context = static_cast<Context*>(param);
-  context->pool()->ProcessWorkItem(context->work_item());
-  delete context;
-  return 0;
-}
-
-ThreadPool::ThreadPool()
-    : work_item_count_(0),
-      shutdown_delay_(0) {
-  UTIL_LOG(L2, (_T("[ThreadPool::ThreadPool]")));
-}
-
-ThreadPool::~ThreadPool() {
-  UTIL_LOG(L2, (_T("[ThreadPool::~ThreadPool]")));
-  DWORD baseline_tick_count = ::GetTickCount();
-  if (::SetEvent(get(shutdown_event_))) {
-    while (work_item_count_ != 0) {
-      ::Sleep(1);
-      if (TimeHasElapsed(baseline_tick_count, shutdown_delay_)) {
-        UTIL_LOG(LE, (_T("[ThreadPool::~ThreadPool][timeout elapsed]")));
-        break;
-      }
-    }
-  }
-}
-
-HRESULT ThreadPool::Initialize(int shutdown_delay) {
-  shutdown_delay_ = shutdown_delay;
-  reset(shutdown_event_, ::CreateEvent(NULL, true, false, NULL));
-  return shutdown_event_ ? S_OK : HRESULTFromLastError();
-}
-
-void ThreadPool::ProcessWorkItem(UserWorkItem* work_item) {
-  ASSERT1(work_item);
-  work_item->Process();
-  delete work_item;
-  ::InterlockedDecrement(&work_item_count_);
-}
-
-HRESULT ThreadPool::QueueUserWorkItem(UserWorkItem* work_item, uint32 flags) {
-  UTIL_LOG(L4, (_T("[ThreadPool::QueueUserWorkItem]")));
-  ASSERT1(work_item);
-
-  scoped_ptr<Context> context(new Context(this, work_item));
-  work_item->set_shutdown_event(get(shutdown_event_));
-  ::InterlockedIncrement(&work_item_count_);
-  if (!::QueueUserWorkItem(&ThreadPool::ThreadProc, context.get(), flags)) {
-    ::InterlockedDecrement(&work_item_count_);
-    return HRESULTFromLastError();
-  }
-
-  // The thread pool has the ownership of the work item thereon.
-  context.release();
-  return S_OK;
-}
-
-}   // namespace omaha
-
diff --git a/common/thread_pool.h b/common/thread_pool.h
deleted file mode 100644
index abbdf28..0000000
--- a/common/thread_pool.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_COMMON_THREAD_POOL_H__
-#define OMAHA_COMMON_THREAD_POOL_H__
-
-#include <windows.h>
-#include "base/basictypes.h"
-#include "omaha/common/scoped_any.h"
-
-namespace omaha {
-
-class UserWorkItem {
- public:
-  UserWorkItem() : shutdown_event_(NULL) {}
-  virtual ~UserWorkItem() {}
-
-  // Template method interface
-  void Process() { DoProcess(); }
-
-  HANDLE shutdown_event() const { return shutdown_event_; }
-  void set_shutdown_event(HANDLE shutdown_event) {
-    shutdown_event_ = shutdown_event;
-  }
-
- private:
-  // Executes the work item.
-  virtual void DoProcess() = 0;
-
-  // It is the job of implementers to watch for the signaling of this event
-  // and shutdown correctly. This event is set when the thread pool is closing.
-  // Do not close this event as is owned by the thread pool.
-  HANDLE shutdown_event_;
-  DISALLOW_EVIL_CONSTRUCTORS(UserWorkItem);
-};
-
-class ThreadPool {
- public:
-  ThreadPool();
-
-  // The destructor might block for 'shutdown_delay'.
-  ~ThreadPool();
-
-  HRESULT Initialize(int shutdown_delay);
-
-  // Returns true if any work items are still in progress.
-  bool HasWorkItems() const { return (0 != work_item_count_); }
-
-  // Adds a work item to the queue. If the add fails the ownership of the
-  // work items remains with the caller.
-  HRESULT QueueUserWorkItem(UserWorkItem* work_item, uint32 flags);
-
- private:
-  // Calls UserWorkItem::Process() in the context of the worker thread.
-  void ProcessWorkItem(UserWorkItem* work_item);
-
-  // This is the thread callback required by the underlying windows API.
-  static DWORD WINAPI ThreadProc(void* context);
-
-  // Approximate number of work items in the pool.
-  volatile LONG work_item_count_;
-
-  // This event signals when the thread pool destructor is in progress.
-  scoped_event shutdown_event_;
-
-  // How many milliseconds to wait for the work items to finish when
-  // the thread pool is shutting down. The shutdown delay resolution is ~10ms.
-  int shutdown_delay_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(ThreadPool);
-};
-
-}   // namespace omaha
-
-#endif  // OMAHA_COMMON_THREAD_POOL_H__
-
diff --git a/common/thread_pool_unittest.cc b/common/thread_pool_unittest.cc
deleted file mode 100644
index 70deeca..0000000
--- a/common/thread_pool_unittest.cc
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "omaha/common/thread_pool.h"
-#include "omaha/common/timer.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-namespace {
-
-volatile LONG g_completed_count = 0;
-
-// Increments the global count by 1.
-class MyJob1 : public UserWorkItem {
- public:
-  MyJob1() {}
-
- private:
-  virtual void DoProcess() { ::InterlockedExchangeAdd(&g_completed_count, 1); }
-
-  DISALLOW_EVIL_CONSTRUCTORS(MyJob1);
-};
-
-// Increments the global count by 2.
-class MyJob2 : public UserWorkItem {
- public:
-  MyJob2() {}
-
- private:
-  virtual void DoProcess() { ::InterlockedExchangeAdd(&g_completed_count, 2); }
-
-  DISALLOW_EVIL_CONSTRUCTORS(MyJob2);
-};
-
-// Increments the global count by 3.
-class MyJob3 : public UserWorkItem {
- public:
-  MyJob3() {}
-
- private:
-  virtual void DoProcess() { ::InterlockedExchangeAdd(&g_completed_count, 3); }
-
-  DISALLOW_EVIL_CONSTRUCTORS(MyJob3);
-};
-
-HRESULT QueueMyJob1(ThreadPool* thread_pool) {
-  scoped_ptr<MyJob1> job(new MyJob1);
-  HRESULT hr = thread_pool->QueueUserWorkItem(job.get(), WT_EXECUTEDEFAULT);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  job.release();
-  return S_OK;
-}
-
-HRESULT QueueMyJob2(ThreadPool* thread_pool) {
-  scoped_ptr<MyJob2> job(new MyJob2);
-  HRESULT hr = thread_pool->QueueUserWorkItem(job.get(), WT_EXECUTEDEFAULT);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  job.release();
-  return S_OK;
-}
-
-HRESULT QueueMyJob3(ThreadPool* thread_pool) {
-  scoped_ptr<MyJob3> job(new MyJob3);
-  HRESULT hr = thread_pool->QueueUserWorkItem(job.get(), WT_EXECUTEDEFAULT);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  job.release();
-  return S_OK;
-}
-
-}   // namespace
-
-// Creates several jobs to increment a global counter by different values and
-// then it checks the value is correct.
-TEST(ThreadPoolTest, ThreadPool) {
-  const int kShutdownDelayMs = 0;
-  const int kNumJobsEachType = 100;
-
-  ThreadPool thread_pool;
-  ASSERT_HRESULT_SUCCEEDED(thread_pool.Initialize(kShutdownDelayMs));
-
-  for (int i = 0; i != kNumJobsEachType; ++i) {
-    EXPECT_HRESULT_SUCCEEDED(QueueMyJob1(&thread_pool));
-    EXPECT_HRESULT_SUCCEEDED(QueueMyJob2(&thread_pool));
-    EXPECT_HRESULT_SUCCEEDED(QueueMyJob3(&thread_pool));
-  }
-
-  const int kMaxWaitForJobsMs = 2000;
-  LowResTimer t(true);
-  while (thread_pool.HasWorkItems() &&
-         t.GetMilliseconds() < kMaxWaitForJobsMs) {
-    ::Sleep(100);
-  }
-  EXPECT_EQ(g_completed_count, 6 * kNumJobsEachType);
-}
-
-}   // namespace omaha
-
diff --git a/common/time.cc b/common/time.cc
deleted file mode 100644
index ec17dbc..0000000
--- a/common/time.cc
+++ /dev/null
@@ -1,500 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Time functions
-
-#include "omaha/common/time.h"
-
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/string.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-// Date Constants
-
-#define kNumOfDays 7
-#define kNumOfMonth 12
-
-static const TCHAR kRFC822_DateDelimiters[]    = _T(" ,:");
-static const TCHAR kRFC822_TimeDelimiter[]     = _T(":");
-SELECTANY const TCHAR* kRFC822_Day[kNumOfDays] = {
-  _T("Mon"),
-  _T("Tue"),
-  _T("Wed"),
-  _T("Thu"),
-  _T("Fri"),
-  _T("Sat"),
-  _T("Sun") };
-
-SELECTANY const TCHAR* kRFC822_Month[kNumOfMonth] = {
-  _T("Jan"),
-  _T("Feb"),
-  _T("Mar"),
-  _T("Apr"),
-  _T("May"),
-  _T("Jun"),
-  _T("Jul"),
-  _T("Aug"),
-  _T("Sep"),
-  _T("Oct"),
-  _T("Nov"),
-  _T("Dec") };
-
-struct TimeZoneInfo {
-  const TCHAR* zone_name;
-  int hour_dif;
-};
-
-SELECTANY TimeZoneInfo kRFC822_TimeZone[] = {
-  { _T("UT"),  0 },
-  { _T("GMT"), 0 },
-  { _T("EST"), -5 },
-  { _T("EDT"), -4 },
-  { _T("CST"), -6 },
-  { _T("CDT"), -5 },
-  { _T("MST"), -7 },
-  { _T("MDT"), -6 },
-  { _T("PST"), -8 },
-  { _T("PDT"), -7 },
-  { _T("A"),   -1 },  // Military time zones
-  { _T("B"),   -2 },
-  { _T("C"),   -3 },
-  { _T("D"),   -4 },
-  { _T("E"),   -5 },
-  { _T("F"),   -6 },
-  { _T("G"),   -7 },
-  { _T("H"),   -8 },
-  { _T("I"),   -9 },
-  { _T("K"),   -10 },
-  { _T("L"),   -11 },
-  { _T("M"),   -12 },
-  { _T("N"),    1 },
-  { _T("O"),    2 },
-  { _T("P"),    3 },
-  { _T("Q"),    4 },
-  { _T("R"),    5 },
-  { _T("S"),    6 },
-  { _T("T"),    7 },
-  { _T("U"),    8 },
-  { _T("V"),    9 },
-  { _T("W"),    10 },
-  { _T("X"),    11 },
-  { _T("Y"),    12 },
-  { _T("Z"),    0 },
-};
-
-SELECTANY const TCHAR *days[] =
-  { L"Sun", L"Mon", L"Tue", L"Wed", L"Thu", L"Fri", L"Sat" };
-
-SELECTANY const TCHAR *months[] = {
-  L"Jan",
-  L"Feb",
-  L"Mar",
-  L"Apr",
-  L"May",
-  L"Jun",
-  L"Jul",
-  L"Aug",
-  L"Sep",
-  L"Oct",
-  L"Nov",
-  L"Dec"
-};
-
-// NOTE: so long as the output is used internally only, no localization is
-// needed here.
-CString ConvertTimeToGMTString(const FILETIME *ft) {
-  ASSERT(ft, (L""));
-
-  CString s;
-  SYSTEMTIME st;
-  if (!FileTimeToSystemTime(ft, &st)) {
-    return L"";
-  }
-
-  // same as FormatGmt(_T("%a, %d %b %Y %H:%M:%S GMT"));
-  s.Format(NOTRANSL(L"%s, %02d %s %d %02d:%02d:%02d GMT"), days[st.wDayOfWeek],
-    st.wDay, months[st.wMonth-1], st.wYear, st.wHour, st.wMinute, st.wSecond);
-  return s;
-}
-
-time64 ConvertTime16ToTime64(uint16 time16) {
-  return time16 * kTimeGranularity + kStart100NsTime;
-}
-
-uint16 ConvertTime64ToTime16(time64 time) {
-  ASSERT1(time >= kStart100NsTime);
-
-  time64 t64 = (time - kStart100NsTime) / kTimeGranularity;
-  ASSERT1(t64 <= kTime16Max);
-
-  return static_cast<uint16>(t64);
-}
-
-time64 TimeTToTime64(const time_t& old_value) {
-  FILETIME file_time;
-  TimeTToFileTime(old_value, &file_time);
-  return FileTimeToTime64(file_time);
-}
-
-#ifdef _DEBUG
-void ComputeStartTime() {
-    SYSTEMTIME start_system_time = kStartSystemTime;
-    time64 start_100ns_time = SystemTimeToTime64(&start_system_time);
-    UTIL_LOG(L1, (_T("posting list starting time = %s\n"),
-                  String_Int64ToString(start_100ns_time, 10)));
-}
-#endif
-
-// Time management
-
-// Allow the unittest to override.
-static time64 time_override = 0;
-
-// #ifdef UNITTEST
-void SetTimeOverride(const time64 & time_new) {
-  time_override = time_new;
-}
-
-// #endif
-
-time64 GetCurrent100NSTime() {
-  if (time_override != 0)
-    return time_override;
-
-  // In order gte the 100ns time we shouldn't use SystemTime
-  // as it's granularity is 1 ms. Below is the correct implementation.
-  // On the other hand the system clock granularity is 15 ms, so we
-  // are not gaining much by having the timestamp in nano-sec
-  // If we decide to go with ms, divide "time64 time" by 10000
-  // SYSTEMTIME sys_time;
-  // GetLocalTime(&sys_time);
-  // return SystemTimeToTime64(&sys_time);
-
-  // get the current time in 100-nanoseconds intervals
-  FILETIME file_time;
-  ::GetSystemTimeAsFileTime(&file_time);
-
-  time64 time = FileTimeToTime64(file_time);
-  return time;
-}
-
-time64 SystemTimeToTime64(const SYSTEMTIME* sys_time) {
-  ASSERT1(sys_time);
-
-  FILETIME file_time;
-  SetZero(file_time);
-
-  if (!::SystemTimeToFileTime(sys_time, &file_time)) {
-    UTIL_LOG(LE,
-             (_T("[SystemTimeToTime64 - failed to SystemTimeToFileTime][0x%x]"),
-              HRESULTFromLastError()));
-    return 0;
-  }
-
-  return FileTimeToTime64(file_time);
-}
-
-// returns a value compatible with EXE/DLL timestamps
-// and the C time() function
-// NOTE: behavior is independent of wMilliseconds value
-int32 SystemTimeToInt32(const SYSTEMTIME *sys_time) {
-  ASSERT(sys_time, (L""));
-
-  time64 t64 = SystemTimeToTime64(sys_time);
-  int32 t32 = 0;
-
-  if (t64 != 0) {
-    t32 = Time64ToInt32(t64);
-  }
-  return t32;
-}
-
-int32 Time64ToInt32(const time64 & time) {
-  // convert to 32-bit format
-  // time() (32-bit) measures seconds since 1970/01/01 00:00:00 (UTC)
-  // FILETIME (64-bit) measures 100-ns intervals since 1601/01/01 00:00:00 (UTC)
-
-  // seconds between 1601 and 1970
-  time64 t32 = (time / kSecsTo100ns) -
-               ((time64(60*60*24) * time64(365*369 + 89)));
-  ASSERT(t32 == (t32 & 0x7FFFFFFF), (L""));  // make sure it fits
-
-  // cast at the end (avoids overflow/underflow when computing 32-bit value)
-  return static_cast<int32>(t32);
-}
-
-time64 Int32ToTime64(const int32 & time) {
-  // convert to 64-bit format
-  // time() (32-bit) measures seconds since 1970/01/01 00:00:00 (UTC)
-  // FILETIME (64-bit) measures 100-ns intervals since 1601/01/01 00:00:00 (UTC)
-
-  // seconds between 1601 and 1970
-  time64 t64 = (static_cast<time64>(time) +
-               (time64(60*60*24) * time64(365*369 + 89))) * kSecsTo100ns;
-  return t64;
-}
-
-// TODO(omaha): The next 2 functions can fail if FileTimeToLocalFileTime or
-// FileTimeToSystemTime fails.
-// Consider having it return a HRESULT. Right now if FileTimeToSystemTime fails,
-// it returns an undefined value.
-
-// Convert a uint to a genuine systemtime
-SYSTEMTIME Time64ToSystemTime(const time64& time) {
-  FILETIME file_time;
-  SetZero(file_time);
-  Time64ToFileTime(time, &file_time);
-
-  SYSTEMTIME sys_time;
-  SetZero(sys_time);
-  if (!FileTimeToSystemTime(&file_time, &sys_time)) {
-    UTIL_LOG(LE, (_T("[Time64ToSystemTime]")
-                  _T("[failed to FileTimeToSystemTime][0x%x]"),
-                  HRESULTFromLastError()));
-  }
-
-  return sys_time;
-}
-
-
-// Convert a uint to a genuine localtime
-// Should ONLY be used for display, since internally we use only UTC
-SYSTEMTIME Time64ToLocalTime(const time64& time) {
-  FILETIME file_time;
-  SetZero(file_time);
-  Time64ToFileTime(time, &file_time);
-
-  FILETIME local_file_time;
-  SetZero(local_file_time);
-  if (!FileTimeToLocalFileTime(&file_time, &local_file_time)) {
-    UTIL_LOG(LE, (_T("[Time64ToLocalTime]")
-                  _T("[failed to FileTimeToLocalFileTime][0x%x]"),
-                  HRESULTFromLastError()));
-  }
-
-  SYSTEMTIME local_time;
-  SetZero(local_time);
-  if (!FileTimeToSystemTime(&local_file_time, &local_time)) {
-    UTIL_LOG(LE, (_T("[Time64ToLocalTime]")
-                  _T("[failed to FileTimeToSystemTime][0x%x]"),
-                  HRESULTFromLastError()));
-  }
-
-  return local_time;
-}
-
-time64 FileTimeToTime64(const FILETIME & file_time) {
-  return static_cast<time64>(
-      file_time.dwHighDateTime) << 32 | file_time.dwLowDateTime;
-}
-
-void Time64ToFileTime(const time64 & time, FILETIME *ft) {
-  ASSERT(ft, (L""));
-
-  ft->dwHighDateTime = static_cast<DWORD>(time >> 32);
-  ft->dwLowDateTime = static_cast<DWORD>(time & 0xffffffff);
-}
-
-// Convert from FILETIME to time_t
-time_t FileTimeToTimeT(const FILETIME& file_time) {
-  return static_cast<time_t>(
-      (FileTimeToTime64(file_time) - kTimeTConvValue) / kSecsTo100ns);
-}
-
-// Convert from time_t to FILETIME
-void TimeTToFileTime(const time_t& time, FILETIME* file_time) {
-  ASSERT1(file_time);
-
-  LONGLONG ll = Int32x32To64(time, kSecsTo100ns) + kTimeTConvValue;
-  file_time->dwLowDateTime = static_cast<DWORD>(ll);
-  file_time->dwHighDateTime = static_cast<DWORD>(ll >> 32);
-}
-
-// Parses RFC 822 Date/Time format
-//    5.  DATE AND TIME SPECIFICATION
-//     5.1.  SYNTAX
-//
-//     date-time   =  [ day "," ] date time        ; dd mm yy
-//                                                 ;  hh:mm:ss zzz
-//     day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
-//                 /  "Fri"  / "Sat" /  "Sun"
-//
-//     date        =  1*2DIGIT month 2DIGIT        ; day month year
-//                                                 ;  e.g. 20 Jun 82
-//
-//     month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
-//                 /  "May"  /  "Jun" /  "Jul"  /  "Aug"
-//                 /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"
-//
-//     time        =  hour zone                    ; ANSI and Military
-//
-//     hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT]
-//                                                 ; 00:00:00 - 23:59:59
-//
-//     zone        =  "UT"  / "GMT"                ; Universal Time
-//                                                 ; North American : UT
-//                 /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4
-//                 /  "CST" / "CDT"                ;  Central:  - 6/ - 5
-//                 /  "MST" / "MDT"                ;  Mountain: - 7/ - 6
-//                 /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7
-//                 /  1ALPHA                       ; Military: Z = UT;
-//                                                 ;  A:-1; (J not used)
-//                                                 ;  M:-12; N:+1; Y:+12
-//                 / ( ("+" / "-") 4DIGIT )        ; Local differential
-//                                                 ;  hours+min. (HHMM)
-// return local time if ret_local_time == true,
-// return time is GMT / UTC time otherwise
-bool RFC822DateToSystemTime(const TCHAR* str_RFC822_date,
-                            SYSTEMTIME* psys_time,
-                            bool ret_local_time) {
-  ASSERT(str_RFC822_date != NULL, (L""));
-  ASSERT(psys_time != NULL, (L""));
-
-  CString str_date = str_RFC822_date;
-  CString str_token;
-  int cur_pos = 0;
-
-  str_token= str_date.Tokenize(kRFC822_DateDelimiters, cur_pos);
-  if (str_token == "")
-    return false;
-
-  int i = 0;
-  for (i = 0; i < kNumOfDays; i++) {
-    if (str_token == kRFC822_Day[i]) {
-      str_token = str_date.Tokenize(kRFC822_DateDelimiters, cur_pos);
-      if (str_token == "")
-        return false;
-      break;
-    }
-  }
-
-  int day = String_StringToInt(str_token);
-  if (day < 0 || day > 31)
-    return false;
-
-  str_token = str_date.Tokenize(kRFC822_DateDelimiters, cur_pos);
-  if (str_token == "")
-    return false;
-
-  int month = -1;
-  for (i = 0; i < kNumOfMonth; i++) {
-    if (str_token == kRFC822_Month[i]) {
-      month = i+1;  // month is 1 based number
-      break;
-    }
-  }
-  if (month == -1)  // month not found
-    return false;
-
-  str_token = str_date.Tokenize(kRFC822_DateDelimiters, cur_pos);
-  if (str_token == "")
-    return false;
-
-  int year = String_StringToInt(str_token);
-  if (year < 100)  // two digit year format, convert to 1950 - 2050 range
-    if (year < 50)
-      year += 2000;
-    else
-      year += 1900;
-
-  str_token = str_date.Tokenize(kRFC822_DateDelimiters, cur_pos);
-  if (str_token == "")
-    return false;
-
-  int hour = String_StringToInt(str_token);
-  if (hour < 0 || hour > 23)
-    return false;
-
-  str_token = str_date.Tokenize(kRFC822_DateDelimiters, cur_pos);
-  if (str_token == "")
-    return false;
-
-  int minute = String_StringToInt(str_token);
-  if (minute < 0 || minute > 59)
-    return false;
-
-  str_token = str_date.Tokenize(kRFC822_DateDelimiters, cur_pos);
-  if (str_token == "")
-    return false;
-
-  int second = 0;
-  // distingushed between XX:XX and XX:XX:XX time formats
-  if (str_token.GetLength() == 2 &&
-      String_IsDigit(str_token[0]) &&
-      String_IsDigit(str_token[1])) {
-    second = String_StringToInt(str_token);
-    if (second < 0 || second > 59)
-      return false;
-
-    str_token = str_date.Tokenize(kRFC822_DateDelimiters, cur_pos);
-    if (str_token == "")
-      return false;
-  }
-
-  int bias = 0;
-  if (str_token[0] == '+' ||
-      str_token[0] == '-' ||
-      String_IsDigit(str_token[0])) {  // numeric format
-    int zone = String_StringToInt(str_token);
-
-    // zone is in HHMM format, need to convert to the number of minutes
-    bias = (zone / 100) * 60 + (zone % 100);
-  } else {  // text format
-    for (i = 0; i < sizeof(kRFC822_TimeZone) / sizeof(TimeZoneInfo); i++)
-      if (str_token == kRFC822_TimeZone[i].zone_name) {
-        bias = kRFC822_TimeZone[i].hour_dif * 60;
-        break;
-      }
-  }
-
-  SYSTEMTIME mail_time;
-  memset(&mail_time, 0, sizeof(mail_time));
-
-  mail_time.wYear   = static_cast<WORD>(year);
-  mail_time.wMonth  = static_cast<WORD>(month);
-  mail_time.wDay    = static_cast<WORD>(day);
-  mail_time.wHour   = static_cast<WORD>(hour);
-  mail_time.wMinute = static_cast<WORD>(minute);
-  mail_time.wSecond = static_cast<WORD>(second);
-
-  // TzSpecificLocalTimeToSystemTime() is incompatible with Win 2000,
-  // convert time manually here
-  time64 time_64 = SystemTimeToTime64(&mail_time);
-  time_64 = time_64 - (bias*kMinsTo100ns);
-
-  *psys_time = Time64ToSystemTime(time_64);
-
-  if (ret_local_time) {
-    TIME_ZONE_INFORMATION local_time_zone_info;
-    SYSTEMTIME universal_time = *psys_time;
-
-    if (GetTimeZoneInformation(&local_time_zone_info) == TIME_ZONE_ID_INVALID) {
-      return false;
-    }
-    if (!SystemTimeToTzSpecificLocalTime(&local_time_zone_info,
-                                    &universal_time,
-                                    psys_time)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-}  // namespace omaha
-
diff --git a/common/time.h b/common/time.h
deleted file mode 100644
index 7b704da..0000000
--- a/common/time.h
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Time functions
-
-#ifndef OMAHA_COMMON_TIME_H__
-#define OMAHA_COMMON_TIME_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include "omaha/common/commontypes.h"
-
-namespace omaha {
-
-#define kMicrosecsTo100ns (10ULL)
-#define kMillisecsTo100ns (10000ULL)
-#define kSecsTo100ns (1000 * kMillisecsTo100ns)
-#define kMinsTo100ns (60 * kSecsTo100ns)
-#define kHoursTo100ns (60 * kMinsTo100ns)
-#define kDaysTo100ns (24 * kHoursTo100ns)
-
-// Jan 1 1980 was tuesday (day 2)
-#define kStartSystemTime {1980, 1, 2, 1, 0, 0, 0, 0}
-
-// this is Jan 1 1980 in time64
-#define kStart100NsTime (119600064000000000uI64)
-#define kTimeGranularity (kDaysTo100ns)
-
-// 2^15-1 because we use signed delta times
-#define kTime16Max ((1 << 15) - 1)
-
-// Constant value used in conversion between FILETIME and time_t
-// It is the time difference between January 1, 1601 and January 1, 1970
-#define kTimeTConvValue (116444736000000000)
-
-time64 ConvertTime16ToTime64(uint16 time16);
-uint16 ConvertTime64ToTime16(time64 time);
-
-#ifdef _DEBUG
-void ComputeStartTime();
-#endif
-
-uint64 GetCurrent100NSTime();
-
-// Note - these return 0 if we can't convert the time
-time64 SystemTimeToTime64(const SYSTEMTIME *sys_time);
-
-// Conversions to/from values compatible with
-// EXE/DLL timestamps and the C time() function
-// NOTE: behavior is independent of wMilliseconds value
-int32  SystemTimeToInt32(const SYSTEMTIME *sys_time);
-int32  Time64ToInt32(const time64 & time);
-time64 Int32ToTime64(const int32 & time);
-time64 TimeTToTime64(const time_t& old_value);
-
-// Returns the system time in GMT
-SYSTEMTIME Time64ToSystemTime(const time64 & time);
-
-// Returns the system time in the computer's time zone
-SYSTEMTIME Time64ToLocalTime(const time64 & time);
-
-// Returns the UTC (system) time given the local time
-SYSTEMTIME LocalTimeToSystemTime(const SYSTEMTIME *local_time);
-
-// This returns a standard formatted string that represents
-// the UTC time corresponding to 'ft'.  This is suitable for use
-// in e.g. HTTP headers.
-//
-// @note IMPORTANT!  This does not return a localized string - it's
-// always in English.  The string returned is intended for use in
-// machine-readable contexts, i.e. HTTP headers and thus should not
-// be localized.
-CString ConvertTimeToGMTString(const FILETIME *ft);
-
-// Convert to and from FileTime
-time64 FileTimeToTime64(const FILETIME & file_time);
-void Time64ToFileTime(const time64 & time, FILETIME *ft);
-
-void SetTimeOverride(const time64 & time_new);
-
-// Convert from FILETIME to time_t
-time_t FileTimeToTimeT(const FILETIME& file_time);
-
-// Convert from time_t to FILETIME
-void TimeTToFileTime(const time_t& time, FILETIME* file_time);
-
-// Parses RFC 822 Date/Time format
-//    5.  DATE AND TIME SPECIFICATION
-//     5.1.  SYNTAX
-//
-//     date-time   =  [ day "," ] date time        ; dd mm yy
-//                                                 ;  hh:mm:ss zzz
-//     day         =  "Mon"  / "Tue" /  "Wed"  / "Thu"
-//                 /  "Fri"  / "Sat" /  "Sun"
-//
-//     date        =  1*2DIGIT month 2DIGIT        ; day month year
-//                                                 ;  e.g. 20 Jun 82
-//
-//     month       =  "Jan"  /  "Feb" /  "Mar"  /  "Apr"
-//                 /  "May"  /  "Jun" /  "Jul"  /  "Aug"
-//                 /  "Sep"  /  "Oct" /  "Nov"  /  "Dec"
-//
-//     time        =  hour zone                    ; ANSI and Military
-//
-//     hour        =  2DIGIT ":" 2DIGIT [":" 2DIGIT]
-//                                                 ; 00:00:00 - 23:59:59
-//
-//     zone        =  "UT"  / "GMT"                ; Universal Time
-//                                                 ; North American : UT
-//                 /  "EST" / "EDT"                ;  Eastern:  - 5/ - 4
-//                 /  "CST" / "CDT"                ;  Central:  - 6/ - 5
-//                 /  "MST" / "MDT"                ;  Mountain: - 7/ - 6
-//                 /  "PST" / "PDT"                ;  Pacific:  - 8/ - 7
-//                 /  1ALPHA                       ; Military: Z = UT;
-//                                                 ;  A:-1; (J not used)
-//                                                 ;  M:-12; N:+1; Y:+12
-//                 / ( ("+" / "-") 4DIGIT )        ; Local differential
-//                                                 ;  hours+min. (HHMM)
-// return local time if ret_local_time == true,
-// return time is GMT / UTC time otherwise
-bool RFC822DateToSystemTime(const TCHAR* str_RFC822_date,
-                            SYSTEMTIME* psys_time,
-                            bool ret_local_time);
-
-// TODO(omaha): overlap in functionality with FileTimeToTime64. Consider
-// removing this one.
-inline int64 FileTimeToInt64(const FILETIME& filetime) {
-  LARGE_INTEGER large_int = {filetime.dwLowDateTime, filetime.dwHighDateTime};
-  return large_int.QuadPart;
-}
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_TIME_H__
diff --git a/common/time_unittest.cc b/common/time_unittest.cc
deleted file mode 100644
index 04318b6..0000000
--- a/common/time_unittest.cc
+++ /dev/null
@@ -1,189 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Time unittest
-
-#include <atltime.h>
-
-#include "omaha/common/time.h"
-#include "omaha/common/utils.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-// Test generation of int32 time values (which uses Time64ToInt32 internally).
-TEST(TimeTest, SystemTimeToInt32NearEpoch) {
-  // Try a simple value near the start of int32 time.
-  SYSTEMTIME system_time = {1970,  // year
-                            1,     // month (1 == January)
-                            0,     // day of week (0 == Sunday)
-                            2,     // day of month
-                            0,     // hour
-                            0,     // minute
-                            0,     // second
-                            0};    // msec
-  system_time.wMilliseconds = 0;
-  int32 time1 = SystemTimeToInt32(&system_time);
-  system_time.wMilliseconds = 999;
-  int32 time2 = SystemTimeToInt32(&system_time);
-
-  // Make sure result is independent of milliseconds value.
-  ASSERT_EQ(time1, time2);
-
-  // 00:00:00 on 1970/01/02 should return the number of seconds in 1 day.
-  ASSERT_EQ(time1, (60*60*24));
-}
-
-// Test an empirical value taken from running dumpbin.exe on a DLL.
-// (IMPORTANT: ran this *after* setting machine's time zone to GMT,
-//  without daylight savings).
-// 40AEE7AA time date stamp Sat May 22 05:39:54 2004
-TEST(TimeTest, SystemTimeToInt32) {
-  SYSTEMTIME system_time = {2004, 5, 6, 22, 5, 39, 54, 0};
-  int32 time = SystemTimeToInt32(&system_time);
-  ASSERT_EQ(time, 0x40AEE7AA);
-}
-
-// Test conversion between int32 and time64 values.
-// By testing SystemTimeToInt32 above, we've already checked Time64ToInt32
-// against empirical values, so it's okay to simply test back-and-forth
-// conversion here.
-TEST(TimeTest, Conversion) {
-  // Simple checks when starting with int32 values, because time64 has more
-  // precision.
-  ASSERT_EQ(Time64ToInt32(Int32ToTime64(0x12345678)), 0x12345678);
-  ASSERT_EQ(Time64ToInt32(Int32ToTime64(INT_MAX)), INT_MAX);
-  ASSERT_EQ(Time64ToInt32(Int32ToTime64(0)), 0);
-
-  // Extra conversions when going opposite direction because int32 has less
-  // precision.
-  ASSERT_EQ(Int32ToTime64(Time64ToInt32(Int32ToTime64(0x12345678))),
-            Int32ToTime64(0x12345678));
-  ASSERT_EQ(Int32ToTime64(Time64ToInt32(Int32ToTime64(INT_MAX))),
-            Int32ToTime64(INT_MAX));
-  ASSERT_EQ(Int32ToTime64(Time64ToInt32(Int32ToTime64(0))),
-            Int32ToTime64(0));
-}
-
-void TimeToStringTest(FILETIME *ft, bool daylight_savings_time) {
-  CTime t(*ft, daylight_savings_time);
-  CString date1(t.FormatGmt(_T("%a, %d %b %Y %H:%M:%S GMT")));
-  CString date2(ConvertTimeToGMTString(ft));
-
-  ASSERT_STREQ(date1, date2);
-}
-
-TEST(TimeTest, TimeToStringTest) {
-  bool daylight_savings_time = false;
-  TIME_ZONE_INFORMATION tz;
-  if (GetTimeZoneInformation(&tz) == TIME_ZONE_ID_DAYLIGHT) {
-    daylight_savings_time = true;
-  }
-
-  FILETIME file_time;
-  ::GetSystemTimeAsFileTime(&file_time);
-  TimeToStringTest(&file_time, daylight_savings_time);
-
-  uint64 t = FileTimeToTime64(file_time);
-
-  // months
-  for (int i = 0; i < 13; i++) {
-    t += (24 * kHoursTo100ns) * 28;
-    Time64ToFileTime(t, &file_time);
-    TimeToStringTest(&file_time, daylight_savings_time);
-  }
-
-  // days
-  for (int i = 0; i < 30; i++) {
-    t += (24 * kHoursTo100ns);
-    Time64ToFileTime(t, &file_time);
-    TimeToStringTest(&file_time, daylight_savings_time);
-  }
-
-  // hours
-  for (int i = 0; i < 24; i++) {
-    t += (24 * kHoursTo100ns);
-    Time64ToFileTime(t, &file_time);
-    TimeToStringTest(&file_time, daylight_savings_time);
-  }
-}
-
-TEST(TimeTest, RFC822TimeParsing) {
-  SYSTEMTIME time = {0};
-  ASSERT_TRUE(RFC822DateToSystemTime(_T("Mon, 16 May 2005 15:44:18 -0700"),
-                                     &time,
-                                     false));
-  ASSERT_EQ(time.wYear , 2005);
-  ASSERT_EQ(time.wMonth , 5);
-  ASSERT_EQ(time.wDay , 16);
-  ASSERT_EQ(time.wHour , 22);
-  ASSERT_EQ(time.wMinute , 44);
-  ASSERT_EQ(time.wSecond , 18);
-
-  ASSERT_TRUE(RFC822DateToSystemTime(_T("Mon, 16 May 2005 15:44:18 -0700"),
-                                     &time,
-                                     true));
-  ASSERT_EQ(time.wYear , 2005);
-  ASSERT_EQ(time.wMonth , 5);
-  ASSERT_EQ(time.wDay , 16);
-  ASSERT_TRUE(time.wHour == 15 || time.wHour == 14);  // daylight saving time
-  ASSERT_EQ(time.wMinute , 44);
-  ASSERT_EQ(time.wSecond , 18);
-
-  ASSERT_TRUE(RFC822DateToSystemTime(_T("Tue, 17 May 2005 02:56:18 +0400"),
-                                     &time,
-                                     false));
-  ASSERT_EQ(time.wYear , 2005);
-  ASSERT_EQ(time.wMonth , 5);
-  ASSERT_EQ(time.wDay , 16);
-  ASSERT_EQ(time.wHour , 22);
-  ASSERT_EQ(time.wMinute , 56);
-  ASSERT_EQ(time.wSecond , 18);
-
-  ASSERT_TRUE(RFC822DateToSystemTime(_T("Tue, 17 May 2005 02:56:18 +0400"),
-                                     &time,
-                                     true));
-  ASSERT_EQ(time.wYear , 2005);
-  ASSERT_EQ(time.wMonth , 5);
-  ASSERT_EQ(time.wDay , 16);
-  ASSERT_TRUE(time.wHour == 15 || time.wHour == 14);  // daylight saving time
-  ASSERT_EQ(time.wMinute , 56);
-  ASSERT_EQ(time.wSecond , 18);
-}
-
-TEST(TimeTest, FileTimeToInt64) {
-  {
-  FILETIME file_time = {0};
-  EXPECT_EQ(0, FileTimeToInt64(file_time));
-  }
-
-  {
-  FILETIME file_time = {LONG_MAX, 0};
-  EXPECT_EQ(LONG_MAX, FileTimeToInt64(file_time));
-  }
-
-  {
-  FILETIME file_time = {ULONG_MAX, 0};
-  EXPECT_EQ(ULONG_MAX, FileTimeToInt64(file_time));
-  }
-
-  {
-  FILETIME file_time = {ULONG_MAX, ULONG_MAX};
-  EXPECT_EQ(kuint64max, FileTimeToInt64(file_time));
-  }
-}
-
-}  // namespace omaha
-
diff --git a/common/timer.cc b/common/timer.cc
deleted file mode 100644
index 9f7690d..0000000
--- a/common/timer.cc
+++ /dev/null
@@ -1,214 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Timing
-
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/string.h"
-#include "omaha/common/timer.h"
-
-namespace omaha {
-
-// LowResTimer class is implemented on top of ::GetTickCount.
-// ::GetTickCount wraps around every 49.7 days. The code can't handle
-// this condition so there is a small probability that something can go
-// wrong.
-LowResTimer::LowResTimer(bool running)
-    : running_(false), iterations_(0), elapsed_(0), start_(0) {
-  if (running) {
-    Start();
-  }
-}
-
-LowResTimer::~LowResTimer() {
-}
-
-void LowResTimer::Reset() {
-  elapsed_ = 0;
-  running_ = 0;
-  iterations_ = 0;
-}
-
-void LowResTimer::Start() {
-  ASSERT1(!running_);
-
-  start_ = ::GetTickCount();
-  running_ = 1;
-}
-
-uint32 LowResTimer::Stop() {
-  ASSERT1(running_);
-
-  uint32 stop = ::GetTickCount();
-  ASSERT1(stop >= start_);
-  uint32 diff = stop - start_;
-  elapsed_ += diff;
-  iterations_++;
-  running_ = 0;
-  return diff;
-}
-
-uint32 LowResTimer::GetMilliseconds() const {
-  uint32 running_time = 0;
-  if (running_) {
-    uint32 now = ::GetTickCount();
-    ASSERT1(now >= start_);
-    running_time = now - start_;
-  }
-  return elapsed_ + running_time;
-}
-
-// statics
-// get the frequency only once
-SELECTANY time64 Timer::count_freq_ = 0;
-
-Timer::Timer(bool running)
-    : running_(0), iterations_(0), elapsed_(0), start_(0), split_(0) {
-  // initialize only once
-  if (count_freq_ == 0) {
-    count_freq_ = GetRdtscFrequency();
-    if (count_freq_ <= 1) {
-       UTIL_LOG(LEVEL_ERROR,
-          (_T("[Timer::Timer - high-res counter not supported]")));
-       count_freq_ = 1;
-    }
-  }
-  if (running) {
-    Start();
-  }
-}
-
-Timer::~Timer() {
-}
-
-void Timer::Reset() {
-  elapsed_ = 0;
-  running_ = 0;
-  iterations_ = 0;
-}
-
-void Timer::Start() {
-  ASSERT1(!running_);
-
-  start_ = GetRdtscCounter();
-  split_ = start_;
-  running_ = 1;
-}
-
-void Timer::Split(double* split_time_ms, double* total_time_ms) {
-  ASSERT1(running_);
-
-  time64 now = GetRdtscCounter();
-  if (split_time_ms) {
-    *split_time_ms = PerfCountToNanoSeconds(now - split_)/ 1000000;
-  }
-  if (total_time_ms) {
-    *total_time_ms =
-        PerfCountToNanoSeconds(elapsed_ + (now - start_)) / 1000000;
-  }
-  split_ = now;
-}
-
-time64 Timer::Stop() {
-  ASSERT1(running_);
-
-  time64 stop = GetRdtscCounter();
-  time64 diff = stop - start_;
-  elapsed_ += diff;
-  iterations_++;
-  running_ = 0;
-  return diff;
-}
-
-double Timer::GetNanoseconds() const {
-  time64 running_time = 0;
-  if (running_) {
-    time64 now = GetRdtscCounter();
-    running_time = now - start_;
-  }
-  return PerfCountToNanoSeconds(elapsed_ + running_time);
-}
-
-#ifdef _DEBUG
-CString Timer::DebugString() const {
-  CString s;
-  double seconds = GetSeconds();
-  if (iterations_) {
-    s.Format(_T("%s sec %d iterations %s sec/iteration"),
-             String_DoubleToString(seconds, 3), iterations_,
-             String_DoubleToString(seconds/iterations_, 3));
-  } else {
-    s.Format(_T("%s sec"), String_DoubleToString(seconds, 3));
-  }
-  return s;
-}
-#endif
-
-// Computes the frequency (ticks/sec) for the CPU tick-count timer (RDTSC)
-// Don't call this function frequently, because computing the frequency is slow
-// (relatively).
-//
-// TODO(omaha): check return values, and return 0 on failure.
-// But hard to imagine a machine where our program will install/run but this
-// will fail.
-time64 Timer::GetRdtscFrequency() {
-  //
-  // Get elapsed RDTSC and elapsed QPC over same time period
-  //
-
-  // compute length of time period to measure
-  time64 freq_qpc = 0;  // ticks per second
-  QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&freq_qpc));
-
-  // fraction of second to run timers for; tradeoff b/w speed and accuracy;
-  // 1/1000 (1 msec) seems like good tradeoff
-  time64 interval_qpc = freq_qpc / 1000;
-
-  // get timer values over same time period
-  time64 begin_qpc = 0;
-  time64 end_qpc = 0;
-
-  QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&begin_qpc));
-
-  time64 begin_rdtsc = Timer::GetRdtscCounter();
-
-  // spin and protect against infinite loop, if QPC does something wacky
-  int count = 0;
-  const int count_max = 50000;
-  do {
-    QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&end_qpc));
-    ++count;
-  } while ((end_qpc - begin_qpc) < interval_qpc  &&  count < count_max);
-
-  QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&end_qpc));
-  time64 end_rdtsc = Timer::GetRdtscCounter();
-
-  ASSERT1(count < count_max);
-
-  //
-  // Compute RDTSC frequency from QPC frequency
-  //
-
-  time64 diff_qpc = end_qpc - begin_qpc;
-  time64 diff_rdtsc = end_rdtsc - begin_rdtsc;
-
-  time64 freq_rdtsc = freq_qpc * diff_rdtsc / diff_qpc;
-
-  return freq_rdtsc;
-}
-
-}  // namespace omaha
-
diff --git a/common/timer.h b/common/timer.h
deleted file mode 100644
index 0ec6bee..0000000
--- a/common/timer.h
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Timing
-
-#ifndef OMAHA_COMMON_TIMER_H__
-#define OMAHA_COMMON_TIMER_H__
-
-#include "omaha/common/debug.h"
-
-namespace omaha {
-
-// Low resolution timer which can be used to time loops. The resolution depends
-// on the platform and can be expected to be about 10 ms on Windows 2000 and up.
-class LowResTimer {
- public:
-  explicit LowResTimer(bool running);
-  ~LowResTimer();
-
-  // LowResTimer keeps track of elapsed time, which can consist of multiple
-  // Start()-Stop() intervals.
-  void Start();
-
-  // Returns time between Start and Stop call and increments the time elapsed.
-  uint32 Stop();
-  void Reset();
-
-  // Return time in seconds, milliseconds.
-  double GetSeconds() const;
-  uint32 GetMilliseconds() const;
-
-  // Gets the number of iteration this timer was started/stopped.
-  uint32 GetIterations() const { return iterations_; }
-  bool IsRunning() const { return running_; }
-
- private:
-
-  bool running_;
-  uint32 start_;
-  uint32 elapsed_;
-  uint32 iterations_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(LowResTimer);
-};
-
-inline double LowResTimer::GetSeconds() const {
-  return static_cast<double>(GetMilliseconds()) / 1000;
-}
-
-// WARNING - Timer is implemented on top of RDTSC and
-// QueryPerformanceCounter which have undefined behavior when running
-// on multi-core, speedstep, bugs in HAL, certain chipsets, etc.
-// Do not use the Timer in production code where the execution flow
-// depends on timing. Timer is primarily intended to be used for
-// code profiling and performance measurements.
-class Timer {
- public:
-  explicit Timer(bool running);
-  ~Timer();
-
-  // Timer keeps track of elapsed time, which can consist of multiple
-  // Start()-Stop() intervals.
-  void Start();
-
-  // returns time for last split (elapsed time since Start() or time since
-  // last Split(), whichever came last) as well as total elapsed time.
-  void Split(double* split_time_ms, double* total_time_ms);
-
-  // returns time elapsed (in hi-res perf-counts) between Start and Stop call
-  // and increments the time elapsed_
-  time64 Stop();
-  void Reset();
-
-  // return time in seconds, milliseconds, etc.
-  double GetSeconds() const;
-  double GetMilliseconds() const;
-  double GetMicroseconds() const;
-  double GetNanoseconds() const;
-  time64 Get100Nanoseconds() const;
-
-  // TODO(omaha): Probably should have been a static method, or even
-  // standalone func convert the high-perf counter to nano-seconds
-  double PerfCountToNanoSeconds(time64 perf_count) const;
-
-  // get the number of iteration this timer was started/stopped
-  uint32 GetIterations() const { return iterations_; }
-  bool IsRunning() const { return running_; }
-
-  // useful funcs beyond just the Timer class
-  static time64 GetRdtscCounter();  // return perf-counter value
-  static time64 GetRdtscFrequency();  // return perf-counter frequency
-
-    // outputs total time, number of iterations, and the average time
-#ifdef _DEBUG
-  CString DebugString() const;
-#endif
-
- private:
-
-  bool running_;
-  time64 start_;
-  time64 split_;
-  time64 elapsed_;
-  uint32 iterations_;
-  static time64 count_freq_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(Timer);
-};
-
-// lint -e{533}  Function should return a value
-// lint -e{31}   Redefinition of symbol
-__forceinline time64 Timer::GetRdtscCounter() { __asm rdtsc }
-
-inline double Timer::PerfCountToNanoSeconds(time64 perf_count) const {
-  return (static_cast<double>(perf_count) / static_cast<double>(count_freq_)) *
-         static_cast<double>(1000000000.0);
-}
-
-inline double Timer::GetSeconds() const {
-  return GetNanoseconds() / 1000000000;
-}
-
-inline double Timer::GetMilliseconds() const {
-  return GetNanoseconds() / 1000000;
-}
-
-inline double Timer::GetMicroseconds() const {
-  return GetNanoseconds() / 1000;
-}
-
-inline time64 Timer::Get100Nanoseconds() const {
-  return (time64) GetNanoseconds() / 100;
-}
-
-// Helper class which starts the timer in its constructor and stops it
-// in its destructor.  This prevents accidentally leaving the timer running
-// if a function has an early exit.
-//
-// Usage:
-//
-// class A {
-//   Timer timer_;
-//
-//   void foo(){
-//     TimerScope (timer_);
-//   ......
-//   }  // end foo
-//
-// Everything is timed till the end of the function or when it returns
-// from any place.
-
-class TimerScope {
- public:
-  explicit TimerScope(Timer *timer) : timer_(timer) {
-    if (timer_) {
-      timer_->Start();
-    }
-  }
-
-  ~TimerScope() {
-    if (timer_ && timer_->IsRunning()) {
-      timer_->Stop();
-    }
-  }
-
- private:
-  Timer *timer_;
-  DISALLOW_EVIL_CONSTRUCTORS(TimerScope);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_TIMER_H__
-
diff --git a/common/timer_unittest.cc b/common/timer_unittest.cc
deleted file mode 100644
index 01905ed..0000000
--- a/common/timer_unittest.cc
+++ /dev/null
@@ -1,232 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Timer unittest
-
-#include <cmath>
-#include "omaha/common/time.h"
-#include "omaha/common/timer.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-// The accuracy of the unit test measurements is expected to be within 50 ms.
-// The error varies depending on how the unit test process gets scheduled.
-// The timer test is prone to failing when run by Pulse. Consider diabling the
-// test completely.
-const int kErrorMs = 50;
-
-// The tests that use the Timer class are flaky (see warning in timer.h),
-// and Timer isn't used in production Omaha code, so we leave out everything
-// but the LowResTimer test.
-#if 0
-
-class TimerTest : public testing::Test {
- protected:
-  // Set up a test so that we can measure the same time interval using the
-  // low and high resolution timers. If the difference between them is too
-  // big then we consider that the high resolution time is busted and we
-  // stop running the unit tests.
-  // The high resolution timer may have undefined behavior, see the header
-  // file for more comments.
-  static void SetUpTestCase() {
-    const int kSleepMs = 100;
-    const int kDiffMs = 1;
-    LowResTimer t(false);
-    Timer u(false);
-    t.Start();
-    u.Start();
-    ::Sleep(kSleepMs);
-    busted_ = abs(t.GetMilliseconds()- u.GetMilliseconds()) >= kErrorMs;
-  }
-
-  void PrintError() {
-    // This is going to print "Test Foo is busted but passed."
-    printf("is busted but ");
-  }
-
-  static bool busted_;
-};
-
-bool TimerTest::busted_ = false;
-
-#endif // #if 0
-
-TEST(TimerTest, LowResTimer) {
-  if (omaha::IsBuildSystem()) {
-    return;
-  }
-
-  LowResTimer t(false);
-
-  const int kSleep1 = 100;
-  t.Start();
-  ::Sleep(kSleep1);
-  uint32 elapsedMs = t.Stop();
-
-  // For the first run of the timer the elapsed value must be equal to
-  // the timer interval.
-  EXPECT_EQ(elapsedMs, t.GetMilliseconds());
-
-  // About 100 ms now.
-  EXPECT_NEAR(kSleep1, elapsedMs, kErrorMs);
-
-  // Test the accessors of different time units.
-  EXPECT_DOUBLE_EQ(t.GetSeconds() * 1000, t.GetMilliseconds());
-
-  const int kSleep2 = 10;
-  t.Start();
-  ::Sleep(kSleep2);
-  elapsedMs = t.Stop();
-  EXPECT_NEAR(kSleep2, elapsedMs, kErrorMs);
-
-  // About 110 ms now.
-  EXPECT_NEAR(kSleep1 + kSleep2, t.GetMilliseconds(), 2 * kErrorMs);
-
-  const int kSleep3 = 50;
-  t.Start();
-  ::Sleep(kSleep3);
-  elapsedMs = t.Stop();
-  EXPECT_NEAR(kSleep3, elapsedMs, kErrorMs);
-
-  // About 160 ms now.
-  EXPECT_NEAR(kSleep1 + kSleep2 + kSleep3, t.GetMilliseconds(), 3 * kErrorMs);
-
-  t.Reset();
-  EXPECT_EQ(0, t.GetMilliseconds());
-}
-
-// Tests disabled, see comment at top of file.
-#if 0
-// Test that values from RTDSC change quickly.
-TEST_F(TimerTest, RTDSC) {
-  uint32 last = 0;
-  for (int i = 0; i < 10; ++i) {
-    uint64 counter = Timer::GetRdtscCounter();
-    uint32 a = *(reinterpret_cast<uint32 *>(&counter));
-    ASSERT_NE(a, last);
-    last = a;
-  }
-}
-
-// Compare everything as ms units for uniformity.
-
-TEST_F(TimerTest, Timer) {
-  if (busted_) {
-    PrintError();
-    return;
-  }
-
-  Timer t(false);
-
-  const int kSleep1 = 100;
-  t.Start();
-  ::Sleep(kSleep1);
-  time64 elapsed = t.Stop();
-
-  // For the first run of the timer the elapsed value must be equal to
-  // the timer interval.
-  EXPECT_DOUBLE_EQ(t.PerfCountToNanoSeconds(elapsed) / 1000000,
-                   t.GetNanoseconds() / 1000000);
-
-  // About 100 ms now.
-  EXPECT_NEAR(kSleep1, t.PerfCountToNanoSeconds(elapsed) / 1000000, kErrorMs);
-
-  // Test the accessors of different time units.
-  EXPECT_DOUBLE_EQ(t.GetSeconds() * 1000, t.GetMilliseconds());
-  EXPECT_DOUBLE_EQ(t.GetMilliseconds() * 1000, t.GetMicroseconds());
-  EXPECT_DOUBLE_EQ(t.GetMicroseconds() * 1000, t.GetNanoseconds());
-
-  EXPECT_NEAR(t.Get100Nanoseconds() * 100.0, t.GetNanoseconds(), 100);
-
-  const int kSleep2 = 10;
-  t.Start();
-  ::Sleep(kSleep2);
-  elapsed = t.Stop();
-  EXPECT_NEAR(kSleep2, t.PerfCountToNanoSeconds(elapsed) / 1000000, kErrorMs);
-
-  // About 110 ms now.
-  EXPECT_NEAR(kSleep1 + kSleep2, t.GetMilliseconds(), 2 * kErrorMs);
-
-  const int kSleep3 = 50;
-  t.Start();
-  ::Sleep(kSleep3);
-  elapsed = t.Stop();
-  EXPECT_NEAR(kSleep3, t.PerfCountToNanoSeconds(elapsed) / 1000000, kErrorMs);
-
-  // About 160 ms now.
-  EXPECT_NEAR(kSleep1 + kSleep2 + kSleep3, t.GetMilliseconds(), 3 * kErrorMs);
-
-  t.Reset();
-  EXPECT_DOUBLE_EQ(0, t.GetMilliseconds());
-}
-
-TEST_F(TimerTest, TimerSplit) {
-  if (busted_) {
-    PrintError();
-    return;
-  }
-
-  const int kSleep1 = 50;
-  const int kSleep2 = 125;
-  const int kSleep3 = 25;
-
-  double split1(0), split2(0), split3(0);
-  double elapsed1(0), elapsed2(0);
-
-  Timer t(false);
-  t.Start();
-  ::Sleep(kSleep1);
-  t.Split(&split1, &elapsed1);
-  EXPECT_NEAR(split1, kSleep1, kErrorMs);
-  EXPECT_NEAR(elapsed1, kSleep1, kErrorMs);
-  EXPECT_DOUBLE_EQ(split1, elapsed1);
-
-  ::Sleep(kSleep2);
-  t.Split(&split2, &elapsed2);
-  EXPECT_NEAR(split2, kSleep2, kErrorMs);
-  EXPECT_DOUBLE_EQ(split1 + split2, elapsed2);
-
-  ::Sleep(kSleep3);
-  t.Split(&split3, NULL);
-  t.Stop();
-  EXPECT_NEAR(split3, kSleep3, kErrorMs);
-  EXPECT_NEAR(split1 + split2 + split3, t.GetMilliseconds(), kErrorMs);
-}
-
-// Time QueryPerformanceCounter
-TEST_F(TimerTest, QueryPerformanceCounter) {
-  if (busted_) {
-    PrintError();
-    return;
-  }
-
-  Timer t(false);
-  t.Start();
-
-  const int kIterations = 100;
-  LARGE_INTEGER count = {0};
-  for (int i = 0; i < kIterations; i++) {
-    ASSERT_TRUE(::QueryPerformanceCounter(&count));
-  }
-
-  t.Stop();
-
-  // Expect the call to take anywhere up to 1000 nano seconds.
-  EXPECT_NEAR(t.GetNanoseconds() / kIterations, 500, 500);
-}
-#endif // #if 0
-
-}  // namespace omaha
diff --git a/common/tr_rand.cc b/common/tr_rand.cc
deleted file mode 100644
index 2a532ad..0000000
--- a/common/tr_rand.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-//
-// Simple pseudo-random number generator.
-// Not intended for anything but random-to-humans numbers.
-// Very fast, and has a period of 32768 for all seed values (including zero).
-// Returns values in the range 0..0xFFFF (inclusive).
-//
-
-#include "common/tr_rand.h"
-
-namespace omaha {
-
-static int rand_val = 0;
-
-void tr_srand(unsigned int seed) {
-  rand_val = seed & 0xFFFF;
-}
-
-int tr_rand() {
-  rand_val = ((rand_val * 75) + 1) & 0xFFFF;
-  return rand_val;
-}
-
-}  // namespace omaha
-
diff --git a/common/tr_rand_unittest.cc b/common/tr_rand_unittest.cc
deleted file mode 100644
index 682d254..0000000
--- a/common/tr_rand_unittest.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-
-#include <cstring>
-#include "omaha/common/tr_rand.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(TRRandTest, TRRand) {
-  int min_period = +99999;
-  int max_period = -99999;
-
-  int min_period_at = -99999;
-  int max_period_at = -99999;
-
-  byte hits[65536] = {0};
-  memset(hits, 0, sizeof(hits));
-
-  // Compute minimum and maximum period by considering all possible seed values.
-  for (int seed = 0; seed < 65536; ++seed) {
-    // See if value is part of some known sequence we've traversed.
-    // If multiple values map to same next-val, this check could cause us to
-    // report a min_period that's too short. But a long min_period still
-    // indicates success.
-    if (hits[seed]) { continue; }
-
-    // Compute length of period starting at this seed.
-    tr_srand(seed);
-    int i = seed;
-    int period = 0;
-    do {
-      ++hits[i];
-      ++period;
-      i = tr_rand();
-      ASSERT_GE(i, 0);
-    } while (hits[i] == 0);
-
-    // Update stats.
-    if (period < min_period) {
-      min_period = period;
-      min_period_at = seed;
-    }
-    if (period > max_period) {
-      max_period = period;
-      max_period_at = seed;
-    }
-  }
-  ASSERT_GE(min_period, (0xFFFF / 2));
-}
-
-}  // namespace omaha
-
diff --git a/common/unittest_utils.cc b/common/unittest_utils.cc
deleted file mode 100644
index a9927ab..0000000
--- a/common/unittest_utils.cc
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// unittest_utils.cpp
-
-#include <windows.h>
-#include "omaha/common/unittest_utils.h"
-#include "omaha/common/string.h"
-#include "omaha/common/tr_rand.h"
-
-namespace omaha {
-
-// ---------------------------------------------------
-// Unittest utils
-//
-
-int32 UnittestUtils::GetRandomInt32() {
-  int32 x = 0;
-
-  // rand() returns a value between 0x0000 and 0x7fff
-  // that's only 16 bits, so we shift by 16
-  // and since it's 0x7fff, we randomly flip the last bit
-
-  int32 a = tr_rand();
-  if (tr_rand() % 2 == 0) {
-    a = a | 0x8000;
-  }
-
-  int32 b = tr_rand();
-  if (tr_rand() % 2 == 0) {
-    b = b | 0x8000;
-  }
-
-  x = (x | a) << 16;
-  x = x | b;
-
-  return x;
-}
-
-int64 UnittestUtils::GetRandomInt64() {
-  int64 x = 0;
-
-  // rand() returns a value between 0x0000 and 0x7fff
-  // that's only 16 bits, so we shift by 16
-  // and since it's 0x7fff, we randomly flip the last bit
-
-  int32 a = tr_rand();
-  if (tr_rand() % 2 == 0) {
-    a = a | 0x8000;
-  }
-
-  int32 b = tr_rand();
-  if (tr_rand() % 2 == 0) {
-    b = b | 0x8000;
-  }
-
-  int32 c = tr_rand();
-  if (tr_rand() % 2 == 0) {
-    c = c | 0x8000;
-  }
-
-  int32 d = tr_rand();
-  if (tr_rand() % 2 == 0) {
-    d = d | 0x8000;
-  }
-
-  x = (x | a) << 16;
-  x = (x | b) << 16;
-  x = (x | c) << 16;
-  x = x | d;
-
-  return x;
-}
-
-char UnittestUtils::RandomPrintableCharacter() {
-  // +1 is to make it inclusive
-  return kMinASCIIPrintable +
-         (tr_rand() % (kMaxASCIIPrintable - kMinASCIIPrintable + 1));
-}
-
-CString UnittestUtils::CreateRandomString(uint32 length) {
-  CString cstr;
-  if (length > 0) {
-    char* rand_chars = new char[length];
-    if (rand_chars != NULL) {
-      for (uint32 i = 0; i < length; ++i) {
-        rand_chars[i] = RandomPrintableCharacter();
-      }
-
-      WCHAR* rand_wchars = ToWide(rand_chars, length);
-      cstr = rand_wchars;
-
-      delete[] rand_wchars;
-      delete[] rand_chars;
-      return cstr;
-    }
-  }
-  return cstr;
-}
-
-bool UnittestUtils::CreateRandomByteArray(uint32 length, byte out_bytes[]) {
-  if (length > 0) {
-      for (uint32 i = 0; i < length; ++i) {
-        out_bytes[i] = static_cast<byte>(tr_rand() % 256);
-      }
-      return true;
-  }
-  return false;
-}
-
-}  // namespace omaha
-
diff --git a/common/unittest_utils.h b/common/unittest_utils.h
deleted file mode 100644
index a7fc15c..0000000
--- a/common/unittest_utils.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2003-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// unittest_utils.h
-
-#ifndef OMAHA_COMMON_UNITTEST_UTILS_H__
-#define OMAHA_COMMON_UNITTEST_UTILS_H__
-
-#include <atlstr.h>
-#include "base/basictypes.h"
-
-namespace omaha {
-
-#define kMinASCIIPrintable  32
-#define kMaxASCIIPrintable  126
-
-// Overriden class that does work in unittests.
-class UnittestUtils {
- public:
-  // returns a random signed 32-bit integer
-  // not particularly fast, done by using rand() and shifting
-  static int32 GetRandomInt32();
-
-  // returns a random signed 64-bit integer
-  // not particularly fast, done by using rand() and shifting
-  static int64 GetRandomInt64();
-
-  // returns a random printable ASCII character
-  // characters range from kMinASCIIPrintable (32 = spacebar) to
-  // kMaxASCIIPrintable (126 = ~)
-  // see: http://www.asciitable.com
-  static char RandomPrintableCharacter();
-
-  // creates a string of random characters using RandomPrintableCharacter()
-  // characters are then converted to wide strings
-  // returns L"" if length = 0
-  static CString CreateRandomString(uint32 length);
-
-  // assumes out_bytes[] has length length
-  // fills out_bytes[] with random bytes
-  // returns NULL if length = 0
-  static bool CreateRandomByteArray(uint32 length, byte out_bytes[]);
-
- private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(UnittestUtils);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_UNITTEST_UTILS_H__
diff --git a/common/update3_utils.cc b/common/update3_utils.cc
new file mode 100644
index 0000000..61ff1b1
--- /dev/null
+++ b/common/update3_utils.cc
@@ -0,0 +1,269 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/update3_utils.h"
+#include <atlsafe.h>
+#include "goopdate/omaha3_idl.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/system.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/goopdate/google_update3.h"
+
+namespace omaha {
+
+namespace update3_utils {
+
+namespace {
+
+template <typename Update3COMClassT>
+HRESULT CreateGoogleUpdate3LocalClass(IGoogleUpdate3** server) {
+  CORE_LOG(L3, (_T("[CreateGoogleUpdate3LocalClass]")));
+  ASSERT1(server);
+
+  typedef CComObject<Update3COMClassT> Update3;
+  scoped_ptr<Update3> update3;
+  HRESULT hr = Update3::CreateInstance(address(update3));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Update3 creation failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = update3->QueryInterface(server);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Update3 QueryInterface failed][0x%x]"), hr));
+    return hr;
+  }
+
+  update3.release();
+
+  return S_OK;
+}
+
+}  // namespace
+
+HRESULT SetProxyBlanketAllowImpersonate(IUnknown* server) {
+  ASSERT1(server);
+
+  HRESULT hr = ::CoSetProxyBlanket(server, RPC_C_AUTHN_DEFAULT,
+      RPC_C_AUTHZ_DEFAULT, COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_DEFAULT,
+      RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_DEFAULT);
+
+  // E_NOINTERFACE indicates an in-proc intra-apartment call.
+  if (FAILED(hr) && hr != E_NOINTERFACE) {
+    CORE_LOG(LE, (_T("[::CoSetProxyBlanket failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT CreateGoogleUpdate3Class(bool is_machine, IGoogleUpdate3** server) {
+  CORE_LOG(L3, (_T("[CreateGoogleUpdate3Class][%d]"), is_machine));
+  ASSERT1(server);
+
+  CComPtr<IGoogleUpdate3> com_server;
+  HRESULT hr = is_machine ? CreateGoogleUpdate3MachineClass(&com_server) :
+                            CreateGoogleUpdate3UserClass(&com_server);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = SetProxyBlanketAllowImpersonate(com_server);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  *server = com_server.Detach();
+  return S_OK;
+}
+
+// Tries to CoCreate the service CLSID first. If that fails, tries to create the
+// server in-proc. Finally, sets a security blanket on the interface to allow
+// the server to impersonate the client.
+HRESULT CreateGoogleUpdate3MachineClass(IGoogleUpdate3** machine_server) {
+  ASSERT1(machine_server);
+  ASSERT1(vista_util::IsUserAdmin());
+
+  CComPtr<IGoogleUpdate3> server;
+  HRESULT hr = server.CoCreateInstance(__uuidof(GoogleUpdate3ServiceClass));
+
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CoCreate GoogleUpdate3ServiceClass failed][0x%x]"), hr));
+
+    hr = CreateGoogleUpdate3LocalClass<Update3COMClassService>(&server);
+    if (hr == GOOPDATE_E_INSTANCES_RUNNING) {
+      CORE_LOG(L3, (_T("[Retry CoCreate GoogleUpdate3ServiceClass]")));
+      hr = server.CoCreateInstance(__uuidof(GoogleUpdate3ServiceClass));
+    }
+
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[Create GoogleUpdate3MachineClass failed][0x%x]"), hr));
+      return hr;
+    }
+  }
+
+  ASSERT1(server);
+  *machine_server = server.Detach();
+  return S_OK;
+}
+
+// Tries to CoCreate the LocalServer CLSID first. If that fails, tries to create
+// the server in-proc.
+HRESULT CreateGoogleUpdate3UserClass(IGoogleUpdate3** user_server) {
+  ASSERT1(user_server);
+
+  CComPtr<IGoogleUpdate3> server;
+  HRESULT hr = server.CoCreateInstance(__uuidof(GoogleUpdate3UserClass));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CoCreate GoogleUpdate3UserClass failed][0x%x]"), hr));
+
+    // The primary reason for the LocalServer activation failing on Vista/Win7
+    // is that COM does not look at HKCU registration when the code is running
+    // elevated. We fall back to an in-proc mode. The in-proc mode is limited to
+    // one install at a time, so we use it only as a backup mechanism.
+    OPT_LOG(LE, (_T("[IsElevatedWithUACMaybeOn][%d]"),
+                 vista_util::IsElevatedWithUACMaybeOn()));
+    hr = CreateGoogleUpdate3LocalClass<Update3COMClassUser>(&server);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  ASSERT1(server);
+  *user_server = server.Detach();
+  return S_OK;
+}
+
+HRESULT CreateAppBundle(IGoogleUpdate3* server, IAppBundle** app_bundle) {
+  ASSERT1(server);
+  ASSERT1(app_bundle);
+
+  CComPtr<IDispatch> idispatch;
+  HRESULT hr = server->createAppBundle(&idispatch);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return idispatch.QueryInterface(app_bundle);
+}
+
+HRESULT CreateApp(BSTR app_id, IAppBundle* app_bundle, IApp** app) {
+  ASSERT1(app_id);
+  ASSERT1(app_bundle);
+  ASSERT1(app);
+
+  CComPtr<IDispatch> idispatch;
+  HRESULT hr = app_bundle->createApp(app_id, &idispatch);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return idispatch.QueryInterface(app);
+}
+
+HRESULT CreateInstalledApp(BSTR app_id, IAppBundle* app_bundle, IApp** app) {
+  ASSERT1(app_id);
+  ASSERT1(app_bundle);
+  ASSERT1(app);
+
+  CComPtr<IDispatch> idispatch;
+  HRESULT hr = app_bundle->createInstalledApp(app_id, &idispatch);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return idispatch.QueryInterface(app);
+}
+
+HRESULT CreateAllInstalledApps(IAppBundle* app_bundle) {
+  ASSERT1(app_bundle);
+
+  return app_bundle->createAllInstalledApps();
+}
+
+HRESULT GetApp(IAppBundle* app_bundle, long index, IApp** app) {  // NOLINT
+  ASSERT1(app_bundle);
+  ASSERT1(index >= 0);
+  ASSERT1(app);
+
+  CComPtr<IDispatch> app_idispatch;
+  HRESULT hr = app_bundle->get_Item(index, &app_idispatch);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  ASSERT1(app_idispatch);
+
+  return app_idispatch.QueryInterface(app);
+}
+
+HRESULT GetCurrentAppVersion(IApp* app, IAppVersion** app_version) {
+  ASSERT1(app);
+  ASSERT1(app_version);
+
+  CComPtr<IDispatch> idispatch;
+  HRESULT hr = app->get_currentVersion(&idispatch);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return idispatch.QueryInterface(app_version);
+}
+
+HRESULT GetNextAppVersion(IApp* app, IAppVersion** app_version) {
+  ASSERT1(app);
+  ASSERT1(app_version);
+
+  CComPtr<IDispatch> idispatch;
+  HRESULT hr = app->get_nextVersion(&idispatch);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return idispatch.QueryInterface(app_version);
+}
+
+HRESULT GetAppCurrentState(IApp* app,
+                           CurrentState* current_state,
+                           ICurrentState** icurrent_state) {
+  ASSERT1(app);
+  ASSERT1(current_state);
+  ASSERT1(icurrent_state);
+
+  CComPtr<IDispatch> idispatch;
+  HRESULT hr = app->get_currentState(&idispatch);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = idispatch.QueryInterface(icurrent_state);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  LONG state = 0;
+  hr = (*icurrent_state)->get_stateValue(&state);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  *current_state = static_cast<CurrentState>(state);
+  return S_OK;
+}
+
+}  // namespace update3_utils
+
+}  // namespace omaha
diff --git a/common/update3_utils.h b/common/update3_utils.h
new file mode 100644
index 0000000..20ef560
--- /dev/null
+++ b/common/update3_utils.h
@@ -0,0 +1,57 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Utilities for IGoogleUpdate3 and related interfaces.
+
+#ifndef OMAHA_COMMON_UPDATE3_UTILS_H_
+#define OMAHA_COMMON_UPDATE3_UTILS_H_
+
+#include <windows.h>
+#include <atlsafe.h>
+#include "goopdate/omaha3_idl.h"
+#include "omaha/base/debug.h"
+
+namespace omaha {
+
+namespace update3_utils {
+
+// Helper methods.
+HRESULT SetProxyBlanketAllowImpersonate(IUnknown* server);
+
+// Create methods.
+HRESULT CreateGoogleUpdate3Class(bool is_machine, IGoogleUpdate3** server);
+HRESULT CreateGoogleUpdate3MachineClass(IGoogleUpdate3** machine_server);
+HRESULT CreateGoogleUpdate3UserClass(IGoogleUpdate3** user_server);
+HRESULT CreateAppBundle(IGoogleUpdate3* server, IAppBundle** app_bundle);
+HRESULT CreateApp(BSTR app_id, IAppBundle* app_bundle, IApp** app);
+HRESULT CreateInstalledApp(BSTR app_id, IAppBundle* app_bundle, IApp** app);
+HRESULT CreateAllInstalledApps(IAppBundle* app_bundle);
+
+// Get methods.
+HRESULT GetApp(IAppBundle* app_bundle, long index, IApp** app);  // NOLINT
+HRESULT GetCurrentAppVersion(IApp* app, IAppVersion** app_version);
+HRESULT GetNextAppVersion(IApp* app, IAppVersion** app_version);
+
+// TODO(omaha): consider removing current_state parameter since it is
+// returned in the icurrent_state object as well.
+HRESULT GetAppCurrentState(IApp* app,
+                           CurrentState* current_state,
+                           ICurrentState** icurrent_state);
+
+}  // namespace update3_utils
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_UPDATE3_UTILS_H_
diff --git a/common/update_request.cc b/common/update_request.cc
new file mode 100644
index 0000000..22839ab
--- /dev/null
+++ b/common/update_request.cc
@@ -0,0 +1,102 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/update_request.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/xml_parser.h"
+
+namespace omaha {
+
+namespace xml {
+
+UpdateRequest::UpdateRequest() {
+}
+
+UpdateRequest::~UpdateRequest() {
+}
+
+// TODO(omaha): handle errors.
+UpdateRequest* UpdateRequest::Create(bool is_machine,
+                                     const CString& session_id,
+                                     const CString& install_source,
+                                     const CString& origin_url) {
+  scoped_ptr<UpdateRequest> update_request(new UpdateRequest);
+
+  request::Request& request = update_request->request_;
+
+  request.is_machine = is_machine;
+  request.protocol_version = _T("3.0");
+
+  request.uid = goopdate_utils::GetUserIdLazyInit(is_machine);
+
+  request.omaha_version = GetVersionString();
+  request.install_source = install_source;
+  request.origin_url = origin_url;
+  request.test_source = ConfigManager::Instance()->GetTestSource();
+
+  GUID req_id = GUID_NULL;
+  VERIFY1(SUCCEEDED(::CoCreateGuid(&req_id)));
+  request.request_id = GuidToString(req_id);
+
+  request.session_id = session_id;
+
+  request.os.platform = kPlatformWin;
+  VERIFY1(SUCCEEDED(goopdate_utils::GetOSInfo(&request.os.version,
+                                              &request.os.service_pack)));
+  request.os.arch = xml::ConvertProcessorArchitectureToString(
+      SystemInfo::GetProcessorArchitecture());
+
+  bool is_period_overridden = false;
+  const int check_period_sec =
+      ConfigManager::Instance()->GetLastCheckPeriodSec(&is_period_overridden);
+  if (is_period_overridden) {
+    request.check_period_sec = check_period_sec;
+  }
+
+  return update_request.release();
+}
+
+void UpdateRequest::AddApp(const request::App& app) {
+  request_.apps.push_back(app);
+}
+
+bool UpdateRequest::has_tt_token() const {
+  for (size_t i = 0; i != request_.apps.size(); ++i) {
+    const request::App& app(request_.apps[i]);
+    if (!app.update_check.tt_token.IsEmpty()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+HRESULT UpdateRequest::Serialize(CString* buffer) const {
+  ASSERT1(buffer);
+  return XmlParser::SerializeRequest(*this, buffer);
+}
+
+bool UpdateRequest::IsEmpty() const {
+  return request_.apps.empty();
+}
+
+}  // namespace xml
+
+}  // namespace omaha
diff --git a/common/update_request.h b/common/update_request.h
new file mode 100644
index 0000000..4ab1535
--- /dev/null
+++ b/common/update_request.h
@@ -0,0 +1,69 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// UpdateRequest allows the caller to build an update request object and
+// serialize it as a string.
+
+#ifndef OMAHA_COMMON_UPDATE_REQUEST_H_
+#define OMAHA_COMMON_UPDATE_REQUEST_H_
+
+#include <windows.h>
+#include "base/basictypes.h"
+#include "omaha/common/protocol_definition.h"
+
+namespace omaha {
+
+namespace xml {
+
+class UpdateRequest {
+ public:
+  ~UpdateRequest();
+
+  // Creates an instance of the class. Caller takes ownership.
+  static UpdateRequest* Create(bool is_machine,
+                               const CString& session_id,
+                               const CString& install_source,
+                               const CString& origin_url);
+
+  // Adds an 'app' element to the request.
+  void AddApp(const request::App& app);
+
+  // Returns true if the requests does not contain applications.
+  bool IsEmpty() const;
+
+  // Serializes the request into a buffer.
+  HRESULT Serialize(CString* buffer) const;
+
+  // Returns true if one of the applications in the request carries a
+  // trusted tester token.
+  bool has_tt_token() const;
+
+  const request::Request& request() const { return request_; }
+
+ private:
+  friend class XmlParserTest;
+
+  UpdateRequest();
+
+  request::Request request_;
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateRequest);
+};
+
+}  // namespace xml
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_UPDATE_REQUEST_H_
diff --git a/common/update_request_unittest.cc b/common/update_request_unittest.cc
new file mode 100644
index 0000000..1420b43
--- /dev/null
+++ b/common/update_request_unittest.cc
@@ -0,0 +1,49 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// TODO(omaha): write tests.
+// TODO(omaha): nice to mock the machine/user ids.
+#include "base/scoped_ptr.h"
+#include "omaha/common/update_request.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace xml {
+
+class UpdateRequestTest : public testing::Test {
+  virtual void SetUp() {}
+  virtual void TearDown() {}
+};
+
+TEST_F(UpdateRequestTest, Create_Machine) {
+  scoped_ptr<UpdateRequest> update_request(
+      UpdateRequest::Create(true, _T("unittest"), _T("unittest"), CString()));
+  ASSERT_TRUE(update_request.get());
+  EXPECT_TRUE(update_request->IsEmpty());
+}
+
+TEST_F(UpdateRequestTest, Create_User) {
+  scoped_ptr<UpdateRequest> update_request(
+      UpdateRequest::Create(false, _T("unittest"), _T("unittest"), CString()));
+
+  ASSERT_TRUE(update_request.get());
+  EXPECT_TRUE(update_request->IsEmpty());
+}
+
+}  // namespace xml
+
+}  // namespace omaha
+
diff --git a/common/update_response.cc b/common/update_response.cc
new file mode 100644
index 0000000..4f40ad3
--- /dev/null
+++ b/common/update_response.cc
@@ -0,0 +1,64 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/update_response.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/xml_parser.h"
+
+namespace omaha {
+
+namespace xml {
+
+UpdateResponse::UpdateResponse() {
+}
+
+UpdateResponse::~UpdateResponse() {
+}
+
+UpdateResponse* UpdateResponse::Create() {
+  return new UpdateResponse;
+}
+
+HRESULT UpdateResponse::Deserialize(const std::vector<uint8>& buffer) {
+  return XmlParser::DeserializeResponse(buffer, this);
+}
+
+HRESULT UpdateResponse::DeserializeFromFile(const CString& filename) {
+  std::vector<uint8> buffer;
+  HRESULT hr = ReadEntireFile(filename, 0, &buffer);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  ASSERT1(!buffer.empty());
+  return Deserialize(buffer);
+}
+
+int UpdateResponse::GetElapsedSecondsSinceDayStart() const {
+  return response_.day_start.elapsed_seconds;
+}
+
+// Sets update_response's response_ member to response. Used by unit tests to
+// set the response without needing to craft corresponding XML. UpdateResponse
+// friends this function, allowing it to access the private member.
+void SetResponseForUnitTest(UpdateResponse* update_response,
+                            const response::Response& response) {
+  ASSERT1(update_response);
+  update_response->response_ = response;
+}
+
+}  // namespace xml
+
+}  // namespace omaha
diff --git a/common/update_response.h b/common/update_response.h
new file mode 100644
index 0000000..1880dc1
--- /dev/null
+++ b/common/update_response.h
@@ -0,0 +1,71 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// UpdateRequest allows the caller to deserialize an update request into
+// a corresponding object object.
+
+#ifndef OMAHA_COMMON_UPDATE_RESPONSE_H_
+#define OMAHA_COMMON_UPDATE_RESPONSE_H_
+
+#include <windows.h>
+#include <utility>
+#include <vector>
+#include "base/basictypes.h"
+#include "omaha/common/protocol_definition.h"
+
+namespace omaha {
+
+namespace xml {
+
+class UpdateResponse {
+ public:
+  ~UpdateResponse();
+
+  // Creates an instance of the class. Caller takes ownership.
+  static UpdateResponse* Create();
+
+  // Initializes an update response from a xml document in a buffer.
+  HRESULT Deserialize(const std::vector<uint8>& buffer);
+
+  // Initializes an update response from a xml document in a file.
+  HRESULT DeserializeFromFile(const CString& filename);
+
+  int GetElapsedSecondsSinceDayStart() const;
+
+  const response::Response& response() const { return response_; }
+
+ private:
+  friend class XmlParser;
+  friend class XmlParserTest;
+
+  // Sets response_ for unit testing.
+  friend void SetResponseForUnitTest(UpdateResponse* update_response,
+                                     const response::Response& response);
+
+  UpdateResponse();
+
+  response::Response response_;
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateResponse);
+};
+
+typedef std::pair<HRESULT, CString> UpdateResponseResult;
+
+}  // namespace xml
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_UPDATE_RESPONSE_H_
+
diff --git a/common/user_info.cc b/common/user_info.cc
deleted file mode 100644
index a0079e7..0000000
--- a/common/user_info.cc
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2004-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include "omaha/common/user_info.h"
-
-#include <windows.h>
-#include <security.h>
-#include <secext.h>
-#include <sddl.h>
-#include <lmcons.h>
-#include <atlsecurity.h>
-#include "base/scoped_ptr.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/scoped_any.h"
-
-namespace omaha {
-
-namespace user_info {
-
-HRESULT GetCurrentUser(CString* name, CString* domain, CString* sid) {
-  CSid current_sid;
-
-  HRESULT hr = GetCurrentUserSid(&current_sid);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  if (sid != NULL) {
-    *sid = current_sid.Sid();
-  }
-  if (name != NULL) {
-    *name = current_sid.AccountName();
-  }
-  if (domain != NULL) {
-    *domain = current_sid.Domain();
-  }
-  return S_OK;
-}
-
-HRESULT GetCurrentUserSid(CSid* sid) {
-  ASSERT1(sid);
-
-  CAccessToken token;
-  if (!token.GetProcessToken(TOKEN_QUERY) || !token.GetUser(sid)) {
-    HRESULT hr = HRESULTFromLastError();
-    ASSERT(false, (_T("[Failed to get current user SID][0x%x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT IsLocalSystemUser(bool* is_local_system, CString* user_sid) {
-  ASSERT1(is_local_system);
-
-  CString sid;
-  HRESULT hr = GetCurrentUser(NULL, NULL, &sid);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  *is_local_system = sid.CompareNoCase(kLocalSystemSid) == 0;
-  if (user_sid) {
-    user_sid->SetString(sid);
-  }
-  return S_OK;
-}
-
-HRESULT GetCurrentThreadUser(CString* sid) {
-  ASSERT1(sid);
-  CAccessToken access_token;
-  CSid user_sid;
-  if (access_token.GetThreadToken(TOKEN_READ) &&
-      access_token.GetUser(&user_sid)) {
-    sid->SetString(user_sid.Sid());
-    return S_OK;
-  } else {
-    return HRESULTFromLastError();
-  }
-}
-
-}  // namespace user_info
-
-}  // namespace omaha
-
diff --git a/common/user_info.h b/common/user_info.h
deleted file mode 100644
index d357e52..0000000
--- a/common/user_info.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2004-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Class UserInfo: Information related to the current user or other users on
-// this machine.
-//
-// TODO(omaha): seems we can merge this module with user_rights.
-
-#ifndef OMAHA_COMMON_USER_INFO_H__
-#define OMAHA_COMMON_USER_INFO_H__
-
-#include <windows.h>
-#include <atlsecurity.h>
-#include <atlstr.h>
-
-namespace omaha {
-
-namespace user_info {
-
-// Gets the user name, domain, and the SID associated with the access token
-// of the current process.
-HRESULT GetCurrentUser(CString* name, CString* domain, CString* sid);
-
-// Gets SID associated with the access token of the current process.
-HRESULT GetCurrentUserSid(CSid* sid);
-
-// Gets the user sid associated with the access token of the current thread if
-// the thread is impersonating. If the thread is not impersonating, the API
-// fails with ERROR_NO_TOKEN.
-HRESULT GetCurrentThreadUser(CString* sid);
-
-// TODO(omaha): deprecate weird API.
-// Looks at the current user SID and checks if it's the same as the
-// LocalSystem user.
-HRESULT IsLocalSystemUser(bool* is_local_system,
-                          CString* user_sid);     // optional.
-
-}  // namespace user_info
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_USER_INFO_H__
diff --git a/common/user_info_unittest.cc b/common/user_info_unittest.cc
deleted file mode 100644
index fa4cdd5..0000000
--- a/common/user_info_unittest.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/user_info.h"
-#include "omaha/testing/unit_test.h"
-
-// We can't make any assumption about the context the unit test runs, however
-// we expect the calls to succeed.
-namespace omaha {
-
-namespace {
-
-const TCHAR kNtNonUniqueIdPrefix[] = _T("S-1-5-21-");
-const int kNtNonUniqueIdPrefixLength = arraysize(kNtNonUniqueIdPrefix) - 1;
-
-}  // namespace
-
-TEST(UserInfoTest, GetCurrentUser) {
-  CString name, domain, sid;
-  EXPECT_HRESULT_SUCCEEDED(user_info::GetCurrentUser(&name, &domain, &sid));
-
-  EXPECT_FALSE(name.IsEmpty());
-  EXPECT_FALSE(domain.IsEmpty());
-  EXPECT_EQ(0, sid.Find(_T("S-1-5-21-")));
-}
-
-TEST(UserInfoTest, GetCurrentUser_SidOnly) {
-  CString name, domain, sid1;
-  EXPECT_HRESULT_SUCCEEDED(user_info::GetCurrentUser(&name, &domain, &sid1));
-
-  EXPECT_STREQ(kNtNonUniqueIdPrefix, sid1.Left(kNtNonUniqueIdPrefixLength));
-
-  CString sid2;
-  EXPECT_HRESULT_SUCCEEDED(user_info::GetCurrentUser(NULL, NULL, &sid2));
-  EXPECT_STREQ(sid1, sid2);
-}
-
-
-TEST(UserInfoTest, GetCurrentUserSid) {
-  CSid sid;
-  EXPECT_HRESULT_SUCCEEDED(user_info::GetCurrentUserSid(&sid));
-
-  const CString name = sid.AccountName();
-  EXPECT_FALSE(name.IsEmpty());
-  const CString sid_string = sid.Sid();
-  EXPECT_STREQ(kNtNonUniqueIdPrefix,
-               sid_string.Left(kNtNonUniqueIdPrefixLength));
-
-  EXPECT_EQ(1, sid.GetPSID()->Revision);
-
-  const SID_IDENTIFIER_AUTHORITY kNtAuthority = SECURITY_NT_AUTHORITY;
-  const SID_IDENTIFIER_AUTHORITY* authority =
-      sid.GetPSID_IDENTIFIER_AUTHORITY();
-  for (int i = 0; i < arraysize(authority->Value); ++i) {
-    EXPECT_EQ(kNtAuthority.Value[i], authority->Value[i]);
-  }
-
-  EXPECT_EQ(5, sid.GetSubAuthorityCount());
-
-  EXPECT_EQ(SECURITY_NT_NON_UNIQUE, sid.GetSubAuthority(0));
-  EXPECT_LT(static_cast<DWORD>(DOMAIN_USER_RID_MAX), sid.GetSubAuthority(4));
-}
-
-// Expect the unit tests do not run impersonated.
-TEST(UserInfoTest, GetCurrentThreadUser) {
-  CString thread_sid;
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_NO_TOKEN),
-            user_info::GetCurrentThreadUser(&thread_sid));
-}
-
-TEST(UserInfoTest, IsLocalSystemUser) {
-  bool is_system = false;
-  CString sid;
-  EXPECT_HRESULT_SUCCEEDED(user_info::IsLocalSystemUser(&is_system, &sid));
-}
-
-}  // namespace omaha
diff --git a/common/user_rights.cc b/common/user_rights.cc
deleted file mode 100644
index cf10673..0000000
--- a/common/user_rights.cc
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/user_rights.h"
-#include <lm.h>
-#include <wtsapi32.h>
-#include "base/scoped_ptr.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/system_info.h"
-#include "omaha/common/vistautil.h"
-
-namespace omaha {
-
-bool UserRights::TokenIsAdmin(HANDLE token) {
-  return BelongsToGroup(token, DOMAIN_ALIAS_RID_ADMINS);
-}
-
-bool UserRights::UserIsAdmin() {
-  return BelongsToGroup(NULL, DOMAIN_ALIAS_RID_ADMINS);
-}
-
-bool UserRights::UserIsUser() {
-  return BelongsToGroup(NULL, DOMAIN_ALIAS_RID_USERS);
-}
-
-bool UserRights::UserIsPowerUser() {
-  return BelongsToGroup(NULL, DOMAIN_ALIAS_RID_POWER_USERS);
-}
-
-bool UserRights::UserIsGuest() {
-  return BelongsToGroup(NULL, DOMAIN_ALIAS_RID_GUESTS);
-}
-
-bool UserRights::BelongsToGroup(HANDLE token, int group_id) {
-  SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY;
-  PSID group = NULL;
-
-  BOOL check = ::AllocateAndInitializeSid(&nt_authority,
-                                          2,
-                                          SECURITY_BUILTIN_DOMAIN_RID,
-                                          group_id,
-                                          0,
-                                          0,
-                                          0,
-                                          0,
-                                          0,
-                                          0,
-                                          &group);
-  if (check) {
-    if (!::CheckTokenMembership(token, group, &check)) {
-      check = false;
-    }
-    ::FreeSid(group);
-  }
-  return !!check;
-}
-
-bool UserRights::UserIsRestricted() {
-  scoped_handle token;
-  if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, address(token))) {
-    UTIL_LOG(LE, (_T("[UserRights::UserIsRestricted - OpenProcessToken failed]")
-                  _T("[0x%08x]"), HRESULTFromLastError()));
-    return true;
-  }
-
-  return !!::IsTokenRestricted(get(token));
-}
-
-bool UserRights::UserIsLowOrUntrustedIntegrity() {
-  if (SystemInfo::IsRunningOnVistaOrLater()) {
-    MANDATORY_LEVEL integrity_level = MandatoryLevelUntrusted;
-    if (FAILED(vista_util::GetProcessIntegrityLevel(0, &integrity_level)) ||
-        integrity_level == MandatoryLevelUntrusted ||
-        integrity_level == MandatoryLevelLow) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-HRESULT UserRights::UserIsLoggedOnInteractively(bool* is_logged_on) {
-  ASSERT1(is_logged_on);
-
-  *is_logged_on = false;
-
-  HRESULT hr = S_OK;
-
-  TCHAR* domain_name = NULL;
-  DWORD domain_name_len = 0;
-  if (!::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,
-                                    WTS_CURRENT_SESSION,
-                                    WTSDomainName,
-                                    &domain_name,
-                                    &domain_name_len)) {
-    hr = HRESULTFromLastError();
-    UTIL_LOG(LE, (_T("[WTSQuerySessionInformation failed][0x%08x]"), hr));
-    return hr;
-  }
-  ON_SCOPE_EXIT(::WTSFreeMemory, domain_name);
-
-  TCHAR* user_name = NULL;
-  DWORD user_name_len = 0;
-  if (!::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,
-                                    WTS_CURRENT_SESSION,
-                                    WTSUserName,
-                                    &user_name,
-                                    &user_name_len)) {
-    hr = HRESULTFromLastError();
-    UTIL_LOG(LE, (_T("[WTSQuerySessionInformation failed][0x%08x]"), hr));
-    return hr;
-  }
-  ON_SCOPE_EXIT(::WTSFreeMemory, user_name);
-
-  UTIL_LOG(L2, (_T("[ts domain=%s][ts user=%s]"), domain_name, user_name));
-
-  // Occasionally, the domain name and user name could not be retrieved when
-  // the program is started just at logon time.
-  if (!(domain_name && *domain_name && user_name && *user_name)) {
-    return E_FAIL;
-  }
-
-  // Get the user associated with the current process.
-  WKSTA_USER_INFO_1* user_info = NULL;
-  NET_API_STATUS status = ::NetWkstaUserGetInfo(
-                              NULL,
-                              1,
-                              reinterpret_cast<uint8**>(&user_info));
-  if (status != NERR_Success || user_info == NULL) {
-    UTIL_LOG(LE, (_T("[NetWkstaUserGetInfo failed][%u]"), status));
-    return HRESULT_FROM_WIN32(status);
-  }
-  ON_SCOPE_EXIT(::NetApiBufferFree, user_info);
-
-  UTIL_LOG(L2, (_T("[wks domain=%s][wks user=%s]"),
-                user_info->wkui1_logon_domain, user_info->wkui1_username));
-
-  *is_logged_on = _tcsicmp(user_info->wkui1_logon_domain, domain_name) == 0 &&
-                  _tcsicmp(user_info->wkui1_username, user_name) == 0;
-  return S_OK;
-}
-
-HRESULT UserRights::GetCallerToken(HANDLE* token) {
-  ASSERT1(token);
-
-  scoped_handle smart_token;
-  HRESULT hr = ::CoImpersonateClient();
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = ::OpenThreadToken(::GetCurrentThread(),
-                         TOKEN_QUERY,
-                         true,
-                         address(smart_token)) ? S_OK : HRESULTFromLastError();
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = ::CoRevertToSelf();
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  *token = release(smart_token);
-  return S_OK;
-}
-
-bool UserRights::ImpersonateAndVerifyCallerIsAdmin() {
-  scoped_handle impersonated_token;
-  if (FAILED(GetCallerToken(address(impersonated_token)))) {
-    return false;
-  }
-  return TokenIsAdmin(get(impersonated_token));
-}
-
-}  // namespace omaha
-
diff --git a/common/user_rights.h b/common/user_rights.h
deleted file mode 100644
index 24aa011..0000000
--- a/common/user_rights.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// This class finds out different user rights on the system.
-// For example it can find out if the user is an administrator.
-
-#ifndef OMAHA_COMMON_USER_RIGHTS_H__
-#define OMAHA_COMMON_USER_RIGHTS_H__
-
-#include <windows.h>
-#include "base/basictypes.h"
-
-namespace omaha {
-
-class UserRights {
- public:
-
-  // Returns true if token is a member of the local Administrators group.
-  static bool TokenIsAdmin(HANDLE token);
-
-  // Returns true if the user belongs to the local Administrators group.
-  static bool UserIsAdmin();
-
-  // Returns true if the user belongs to the Users group.
-  static bool UserIsUser();
-
-  // Returns true if the user belongs to the Power User group.
-  static bool UserIsPowerUser();
-
-  // Returns true if the user is a Guest.
-  static bool UserIsGuest();
-
-  // Returns true if the owner of the current process has a restricted token.
-  static bool UserIsRestricted();
-
-  // Returns true if the owner of the current process runs under low or
-  // untrusted integrity on Vista.
-  static bool UserIsLowOrUntrustedIntegrity();
-
-  // Returns true if the owner of the current process has an interactive
-  // session: console, terminal services, or fast user switching.
-  static HRESULT UserIsLoggedOnInteractively(bool* is_logged_on);
-
-  static HRESULT GetCallerToken(HANDLE* token);
-
-  static bool ImpersonateAndVerifyCallerIsAdmin();
-
-  // Returns true if the owner of the current process is the primary logon token
-  // for the current interactive session: console, terminal services, or fast
-  // user switching.
-  static bool BelongsToGroup(HANDLE token, int group_id);
-
- private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(UserRights);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_USER_RIGHTS_H__
-
diff --git a/common/user_rights_unittest.cc b/common/user_rights_unittest.cc
deleted file mode 100644
index f62374a..0000000
--- a/common/user_rights_unittest.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/testing/unit_test.h"
-#include "omaha/common/user_rights.h"
-
-namespace omaha {
-
-TEST(UserRightsTest, UserIsLoggedOnInteractively) {
-  bool is_logged_on(false);
-  EXPECT_HRESULT_SUCCEEDED(
-    UserRights::UserIsLoggedOnInteractively(&is_logged_on));
-  EXPECT_TRUE(is_logged_on);
-}
-
-}  // namespace omaha
-
diff --git a/common/utils.cc b/common/utils.cc
deleted file mode 100644
index 66c2c6c..0000000
--- a/common/utils.cc
+++ /dev/null
@@ -1,1926 +0,0 @@
-// Copyright 2003-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/utils.h"
-
-#include <ras.h>
-#include <regstr.h>
-#include <urlmon.h>
-#include <wincrypt.h>
-#include <ATLComTime.h>
-#include <atlpath.h>
-#include <map>
-#include <vector>
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/const_config.h"
-#include "omaha/common/const_timeouts.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/process.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/shell.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/string.h"
-#include "omaha/common/system.h"
-#include "omaha/common/system_info.h"
-#include "omaha/common/time.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/user_rights.h"
-#include "omaha/common/vistautil.h"
-
-namespace omaha {
-
-namespace {
-
-// Private object namespaces for Vista processes.
-const TCHAR* const kGoopdateBoundaryDescriptor = _T("GoogleUpdate_BD");
-const TCHAR* const kGoopdatePrivateNamespace = _T("GoogleUpdate");
-const TCHAR* const kGoopdatePrivateNamespacePrefix = _T("GoogleUpdate\\");
-
-// Helper for IsPrivateNamespaceAvailable().
-// For simplicity, the handles opened here are leaked. We need these until
-// process exit, at which point they will be cleaned up automatically by the OS.
-bool EnsurePrivateNamespaceAvailable() {
-  HANDLE boundary_descriptor =
-      CreateBoundaryDescriptorWWrap(kGoopdateBoundaryDescriptor, 0);
-  if (NULL == boundary_descriptor) {
-    DWORD last_error(::GetLastError());
-    UTIL_LOG(LE, (_T("CreateBoundaryDescriptor failed[%d]"), last_error));
-    return false;
-  }
-
-  char sid[SECURITY_MAX_SID_SIZE] = {0};
-  DWORD size = sizeof(sid);
-  // Mark the boundary descriptor with the Admins Group SID. Consequently, all
-  // admins, including SYSTEM, will create objects in the same private
-  // namespace.
-  if (!::CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, sid, &size)) {
-    DWORD last_error(::GetLastError());
-    UTIL_LOG(LE, (_T("::CreateWellKnownSid failed[%d]"), last_error));
-    return false;
-  }
-  if (!AddSIDToBoundaryDescriptorWrap(&boundary_descriptor, sid)) {
-    DWORD last_error(::GetLastError());
-    UTIL_LOG(LE, (_T("AddSIDToBoundaryDescriptor fail[%d]"), last_error));
-    return false;
-  }
-
-  NamedObjectAttributes attr;
-  GetAdminDaclSecurityAttributes(&attr.sa, GENERIC_ALL);
-  // The private namespace created here will be used to create objects of the
-  // form "GoogleUpdate\xyz". As the article "Object Namespaces" on MSDN
-  // explains, these kernel objects are safe from squatting attacks from lower
-  // integrity processes.
-  HANDLE namespace_handle =
-      CreatePrivateNamespaceWWrap(&attr.sa,
-                                  boundary_descriptor,
-                                  kGoopdatePrivateNamespace);
-  if (namespace_handle) {
-    return true;
-  }
-  ASSERT(ERROR_ALREADY_EXISTS == ::GetLastError(),
-         (_T("CreatePrivateNamespaceW failed. %d"), ::GetLastError()));
-
-  // Another process has already created the namespace. Attempt to open.
-  namespace_handle = OpenPrivateNamespaceWWrap(boundary_descriptor,
-                                               kGoopdatePrivateNamespace);
-  if (namespace_handle || ::GetLastError() == ERROR_DUP_NAME) {
-    // ERROR_DUP_NAME indicates that we have called CreatePrivateNamespaceWWrap
-    // or OpenPrivateNamespaceWWrap before in the same process. Either way, we
-    // can now create objects prefixed with our private namespace.
-    return true;
-  }
-
-  ASSERT(namespace_handle, (_T("[Could not open private namespace][%d]"),
-                            ::GetLastError()));
-  return false;
-}
-
-}  // namespace
-
-// Returns 0 if an error occurs.
-ULONGLONG VersionFromString(const CString& s) {
-  int pos(0);
-  unsigned int quad[4] = {0, 0, 0, 0};
-
-  for (int i = 0; i < 4; ++i) {
-    CString q = s.Tokenize(_T("."), pos);
-    if (pos == -1) {
-      return 0;
-    }
-
-    int quad_value(0);
-    if (!String_StringToDecimalIntChecked(q, &quad_value)) {
-      return 0;
-    }
-
-    quad[i] = static_cast<unsigned int>(quad_value);
-
-    if (kuint16max < quad[i]) {
-      return 0;
-    }
-  }
-
-  if (s.GetLength() + 1 != pos) {
-    return 0;
-  }
-
-  return MAKEDLLVERULL(quad[0], quad[1], quad[2], quad[3]);
-}
-
-CString StringFromVersion(ULONGLONG version) {
-  const WORD version_major = HIWORD(version >> 32);
-  const WORD version_minor = LOWORD(version >> 32);
-  const WORD version_build = HIWORD(version);
-  const WORD version_patch = LOWORD(version);
-
-  CString version_string;
-  version_string.Format((_T("%u.%u.%u.%u")),
-                        version_major,
-                        version_minor,
-                        version_build,
-                        version_patch);
-  return version_string;
-}
-
-CString GetCurrentDir() {
-  TCHAR cur_dir[MAX_PATH] = {0};
-  if (!::GetCurrentDirectory(MAX_PATH, cur_dir)) {
-    return CString(_T('.'));
-  }
-  return CString(cur_dir);
-}
-
-HRESULT GetNewFileNameInDirectory(const CString& dir, CString* file_name) {
-  ASSERT1(file_name);
-
-  GUID guid = {0};
-  HRESULT hr = ::CoCreateGuid(&guid);
-  if (FAILED(hr)) {
-    CORE_LOG(LEVEL_WARNING, (_T("[CoCreateGuid failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  CString guid_file_name = GuidToString(guid);
-  CPath file_path(dir);
-  file_path.Append(guid_file_name);
-
-  *file_name = static_cast<const TCHAR*>(file_path);
-  return S_OK;
-}
-
-// determines if a time is in the distant past, present, or future
-TimeCategory GetTimeCategory(const time64 system_time) {
-  time64 now = GetCurrent100NSTime();
-
-  // Times more than a few days in the future are wrong [I will allow a little
-  // leeway, since it could be set in another future time zone, or a program
-  // that likes UNC]]
-  if (system_time > (now + kDaysTo100ns * 5)) {
-    return FUTURE;
-  }
-
-  // times more than 40 years ago are wrong
-  if (system_time < (now - kDaysTo100ns * 365 * 40)) {
-    return PAST;
-  }
-
-  return PRESENT;
-}
-
-// Determine if a given time is probably valid
-bool IsValidTime(const time64 t) {
-  return (GetTimeCategory(t) == PRESENT);
-}
-
-LARGE_INTEGER MSto100NSRelative(DWORD ms) {
-  const __int64 convert_ms_to_100ns_units = 1000 /*ms/us*/ * 10 /*us/100ns*/;
-  __int64 timeout_100ns = static_cast<__int64>(ms) * convert_ms_to_100ns_units;
-  LARGE_INTEGER timeout = {0};
-  timeout.QuadPart = -timeout_100ns;
-  return timeout;
-}
-
-// Use of this method is unsafe. Be careful!
-// Local System and admins get GENERIC_ALL access. Authenticated non-admins get
-// non_admin_access_mask access.
-void GetEveryoneDaclSecurityAttributes(CSecurityAttributes* sec_attr,
-                                       ACCESS_MASK non_admin_access_mask) {
-  ASSERT1(sec_attr);
-
-  // Grant access to all users.
-  CDacl dacl;
-  dacl.AddAllowedAce(Sids::System(), GENERIC_ALL);
-  dacl.AddAllowedAce(Sids::Admins(), GENERIC_ALL);
-  dacl.AddAllowedAce(Sids::Interactive(), non_admin_access_mask);
-
-  CSecurityDesc security_descriptor;
-  security_descriptor.SetDacl(dacl);
-  security_descriptor.MakeAbsolute();
-
-  sec_attr->Set(security_descriptor);
-}
-
-void GetAdminDaclSecurityAttributes(CSecurityAttributes* sec_attr,
-                                    ACCESS_MASK accessmask) {
-  ASSERT1(sec_attr);
-  CDacl dacl;
-  dacl.AddAllowedAce(Sids::System(), accessmask);
-  dacl.AddAllowedAce(Sids::Admins(), accessmask);
-
-  CSecurityDesc security_descriptor;
-  security_descriptor.SetOwner(Sids::Admins());
-  security_descriptor.SetGroup(Sids::Admins());
-  security_descriptor.SetDacl(dacl);
-  security_descriptor.MakeAbsolute();
-
-  sec_attr->Set(security_descriptor);
-}
-
-// This function is not thread-safe.
-bool IsPrivateNamespaceAvailable(bool is_machine) {
-  static bool is_initialized = false;
-  static bool is_available = false;
-
-  if (!is_machine) {
-    // TODO(Omaha): From a security viewpoint, private namespaces do not add
-    // much value for the User Omaha. But from a uniformity perspective, makes
-    // sense to use for both.
-    return false;
-  }
-
-  if (is_initialized) {
-    return is_available;
-  }
-
-  if (!SystemInfo::IsRunningOnVistaOrLater()) {
-    is_available = false;
-    is_initialized = true;
-    return false;
-  }
-
-  is_available = EnsurePrivateNamespaceAvailable();
-  is_initialized = true;
-  return is_available;
-}
-
-
-void GetNamedObjectAttributes(const TCHAR* base_name,
-                              bool is_machine,
-                              NamedObjectAttributes* attr) {
-  ASSERT1(base_name);
-  ASSERT1(attr);
-
-  // TODO(Omaha): Enable this code after we have a better understanding of
-  // Private Object Namespaces.
-#if 0
-  if (IsPrivateNamespaceAvailable(is_machine)) {
-    attr->name = kGoopdatePrivateNamespacePrefix;
-  } else {
-    ASSERT1(!SystemInfo::IsRunningOnVistaOrLater());
-#endif
-
-  attr->name = omaha::kGlobalPrefix;
-
-  if (!is_machine) {
-    CString user_sid;
-    VERIFY1(SUCCEEDED(omaha::user_info::GetCurrentUser(NULL, NULL, &user_sid)));
-    attr->name += user_sid;
-  } else {
-    // Grant access to administrators and system.
-    GetAdminDaclSecurityAttributes(&attr->sa, GENERIC_ALL);
-  }
-
-  attr->name += base_name;
-  UTIL_LOG(L1, (_T("[GetNamedObjectAttributes][named_object=%s]"), attr->name));
-}
-
-// For now, required_ace_flags is only supported for SE_REGISTRY_KEY objects.
-// INHERITED_ACE may be added to the read ACE flags, so it is excluded from
-// the comparison with required_ace_flags.
-HRESULT AddAllowedAce(const TCHAR* object_name,
-                      SE_OBJECT_TYPE object_type,
-                      const CSid& sid,
-                      ACCESS_MASK required_permissions,
-                      uint8 required_ace_flags) {
-  ASSERT1(SE_REGISTRY_KEY == object_type || !required_ace_flags);
-  ASSERT1(0 == (required_ace_flags & INHERITED_ACE));
-
-  CDacl dacl;
-  if (!AtlGetDacl(object_name, object_type, &dacl)) {
-    return HRESULTFromLastError();
-  }
-
-  int ace_count = dacl.GetAceCount();
-  for (int i = 0; i < ace_count; ++i) {
-    CSid sid_entry;
-    ACCESS_MASK existing_permissions = 0;
-    BYTE existing_ace_flags = 0;
-    dacl.GetAclEntry(i,
-                     &sid_entry,
-                     &existing_permissions,
-                     NULL,
-                     &existing_ace_flags);
-    if (sid_entry == sid &&
-        required_permissions == (existing_permissions & required_permissions) &&
-        required_ace_flags == (existing_ace_flags & ~INHERITED_ACE)) {
-      return S_OK;
-    }
-  }
-
-  if (!dacl.AddAllowedAce(sid, required_permissions, required_ace_flags) ||
-      !AtlSetDacl(object_name, object_type, dacl)) {
-    return HRESULTFromLastError();
-  }
-
-  return S_OK;
-}
-
-HRESULT CreateDir(const TCHAR* in_dir,
-                  LPSECURITY_ATTRIBUTES security_attr) {
-  ASSERT1(in_dir);
-  CString path;
-  if (!PathCanonicalize(CStrBuf(path, MAX_PATH), in_dir)) {
-    return E_FAIL;
-  }
-  // Standardize path on backslash so Find works.
-  path.Replace(_T('/'), _T('\\'));
-  int next_slash = path.Find(_T('\\'));
-  while (true) {
-    int len = 0;
-    if (next_slash == -1) {
-      len = path.GetLength();
-    } else {
-      len = next_slash;
-    }
-    CString dir(path.Left(len));
-    // The check for File::Exists should not be needed. However in certain
-    // cases, i.e. when the program is run from a n/w drive or from the
-    // root drive location, the first CreateDirectory fails with an
-    // E_ACCESSDENIED instead of a ALREADY_EXISTS. Hence we protect the call
-    // with the exists.
-    if (!File::Exists(dir)) {
-      if (!::CreateDirectory(dir, security_attr)) {
-        DWORD error = ::GetLastError();
-        if (ERROR_FILE_EXISTS != error && ERROR_ALREADY_EXISTS != error) {
-          return HRESULT_FROM_WIN32(error);
-        }
-      }
-    }
-    if (next_slash == -1) {
-      break;
-    }
-    next_slash = path.Find(_T('\\'), next_slash + 1);
-  }
-
-  return S_OK;
-}
-
-HRESULT GetFolderPath(int csidl, CString* path) {
-  if (!path) {
-    return E_INVALIDARG;
-  }
-
-  TCHAR buffer[MAX_PATH] = {0};
-  HRESULT hr = ::SHGetFolderPath(NULL, csidl, NULL, SHGFP_TYPE_CURRENT, buffer);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  *path = buffer;
-  return S_OK;
-}
-
-// Delete directory files. If failed, try to schedule deletion at next reboot
-HRESULT DeleteDirectoryFiles(const TCHAR* dir_name) {
-  ASSERT1(dir_name);
-  return DeleteWildcardFiles(dir_name, _T("*"));
-}
-
-// Delete a set of wildcards within dir_name.
-// If unable to delete immediately, try to schedule deletion at next reboot
-HRESULT DeleteWildcardFiles(const TCHAR* dir_name, const TCHAR* wildcard_name) {
-  ASSERT1(dir_name);
-  ASSERT1(wildcard_name);
-
-  HRESULT hr = S_OK;
-
-  WIN32_FIND_DATA find_data;
-  SetZero(find_data);
-
-  CString find_file(dir_name);
-  find_file += _T('\\');
-  find_file += wildcard_name;
-
-  scoped_hfind hfind(::FindFirstFile(find_file, &find_data));
-  if (!hfind) {
-    if (::GetLastError() == ERROR_NO_MORE_FILES) {
-      return S_OK;
-    } else {
-      hr = HRESULTFromLastError();
-      UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]")
-                             _T("[failed to get first file][0x%x]"), hr));
-      return hr;
-    }
-  }
-
-  do {
-    if (!(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
-      CString specific_file_name(dir_name);
-      specific_file_name += _T('\\');
-      specific_file_name += find_data.cFileName;
-      if (!::DeleteFile(specific_file_name)) {
-        if (!SUCCEEDED(hr = File::DeleteAfterReboot(specific_file_name))) {
-          UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]")
-                                 _T("[failed to delete after reboot]")
-                                 _T("[%s][0x%x]"), specific_file_name, hr));
-        }
-      }
-    }
-  } while (::FindNextFile(get(hfind), &find_data));
-
-  if (::GetLastError() != ERROR_NO_MORE_FILES) {
-    hr = HRESULTFromLastError();
-    UTIL_LOG(LEVEL_ERROR, (_T("[DeleteWildcardFiles]")
-                           _T("[failed to get next file][0x%x]"), hr));
-  }
-
-  return hr;
-}
-
-// Delete directory and files within. If failed, try to schedule deletion at
-// next reboot
-// TODO(Omaha) - the code to delete the directory is complicated,
-// especially the way the result code is built from hr and hr1. I wonder if we
-// could simplify this by reimplementing it on top of SHFileOperation and
-// also save a few tens of bytes in the process.
-HRESULT DeleteDirectory(const TCHAR* dir_name) {
-  ASSERT1(dir_name);
-
-  if (!SafeDirectoryNameForDeletion(dir_name)) {
-    return E_FAIL;
-  }
-
-  // Make sure the directory exists (it is ok if it doesn't)
-  DWORD dir_attributes = ::GetFileAttributes(dir_name);
-  if (dir_attributes == INVALID_FILE_ATTRIBUTES) {
-    if (::GetLastError() == ERROR_FILE_NOT_FOUND)
-      return S_OK;  // Ok if directory is missing
-    else
-      return HRESULTFromLastError();
-  }
-  // Confirm it is a directory
-  if (!(dir_attributes & FILE_ATTRIBUTE_DIRECTORY)) {
-    return E_FAIL;
-  }
-
-  // Try to delete all files at best effort
-  // Return the first HRESULT error encountered
-
-  // First delete all the normal files
-  HRESULT hr = DeleteDirectoryFiles(dir_name);
-
-  // Recursively delete any subdirectories
-
-  WIN32_FIND_DATA find_data = {0};
-
-  CString find_file(dir_name);
-  find_file += _T("\\*");
-
-  // Note that the follows are enclosed in a block because we need to close the
-  // find handle before deleting the directorty itself
-  {
-    scoped_hfind hfind(::FindFirstFile(find_file, &find_data));
-    if (!hfind) {
-      if (::GetLastError() == ERROR_NO_MORE_FILES) {
-        return hr;
-      } else {
-        HRESULT hr1 = HRESULTFromLastError();
-        UTIL_LOG(LEVEL_ERROR, (_T("[DeleteDirectory]")
-                               _T("[failed to get first file][0x%x]"), hr1));
-        return SUCCEEDED(hr) ? hr1 : hr;
-      }
-    }
-
-    do {
-      if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
-        if (String_StrNCmp(find_data.cFileName, _T("."), 2, false) == 0 ||
-            String_StrNCmp(find_data.cFileName, _T(".."), 3, false) == 0) {
-          continue;
-        }
-
-        CString sub_dir(dir_name);
-        sub_dir += _T("\\");
-        sub_dir += find_data.cFileName;
-        HRESULT hr1 = DeleteDirectory(sub_dir);
-        if (SUCCEEDED(hr) && FAILED(hr1)) {
-          hr = hr1;
-        }
-      }
-    }
-    while (::FindNextFile(get(hfind), &find_data));
-  }
-
-  // Delete the empty directory itself
-  if (!::RemoveDirectory(dir_name)) {
-    HRESULT hr1 = E_FAIL;
-    if (FAILED(hr1 = File::DeleteAfterReboot(dir_name))) {
-      UTIL_LOG(LEVEL_ERROR, (_T("[DeleteDirectory]")
-                             _T("[failed to delete after reboot]")
-                             _T("[%s][0x%x]"), dir_name, hr1));
-    }
-
-    if (SUCCEEDED(hr) && FAILED(hr1)) {
-      hr = hr1;
-    }
-  }
-
-  return hr;
-}
-
-// Returns true if this directory name is 'safe' for deletion (doesn't contain
-// "..", doesn't specify a drive root)
-bool SafeDirectoryNameForDeletion(const TCHAR* dir_name) {
-  ASSERT1(dir_name);
-
-  // empty name isn't allowed
-  if (!(dir_name && *dir_name)) {
-    return false;
-  }
-
-  // require a character other than \/:. after the last :
-  // disallow anything with ".."
-  bool ok = false;
-  for (const TCHAR* s = dir_name; *s; ++s) {
-    if (*s != _T('\\') && *s != _T('/') && *s != _T(':') && *s != _T('.')) {
-      ok = true;
-    }
-    if (*s == _T('.') && s > dir_name && *(s-1) == _T('.')) {
-      return false;
-    }
-    if (*s == _T(':')) {
-      ok = false;
-    }
-  }
-  return ok;
-}
-
-// Utility function that deletes either a file or directory,
-// before or after reboot
-HRESULT DeleteBeforeOrAfterReboot(const TCHAR* targetname) {
-  if (!File::Exists(targetname)) {
-    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
-  }
-
-  HRESULT hr = E_FAIL;
-  if (File::IsDirectory(targetname)) {
-    // DeleteDirectory will schedule deletion at next reboot if it cannot delete
-    // immediately.
-    hr = DeleteDirectory(targetname);
-  } else  {
-    hr = File::Remove(targetname);
-    // If failed, schedule deletion at next reboot
-    if (FAILED(hr)) {
-      UTIL_LOG(L1, (_T("[DeleteBeforeOrAfterReboot]")
-                    _T("[trying to delete %s after reboot]"), targetname));
-      hr = File::DeleteAfterReboot(targetname);
-    }
-  }
-
-  if (FAILED(hr)) {
-    UTIL_LOG(L1, (_T("[DeleteBeforeOrAfterReboot]")
-                  _T("[failed to delete %s]"), targetname));
-  }
-
-  return hr;
-}
-
-
-// Internal implementation of the safe version of getting size of all files in
-// a directory. It is able to abort the counting if one of the maximum criteria
-// is reached.
-HRESULT InternalSafeGetDirectorySize(const TCHAR* dir_name,
-                                     uint64* size,
-                                     HANDLE shutdown_event,
-                                     uint64 max_size,
-                                     int curr_file_count,
-                                     int max_file_count,
-                                     int curr_depth,
-                                     int max_depth,
-                                     DWORD end_time_ms) {
-  ASSERT1(dir_name && *dir_name);
-  ASSERT1(size);
-
-  CString dir_find_name = String_MakeEndWith(dir_name, _T("\\"), false);
-  dir_find_name += _T("*");
-  WIN32_FIND_DATA find_data = {0};
-  scoped_hfind hfind(::FindFirstFile(dir_find_name, &find_data));
-  if (!hfind) {
-    return ::GetLastError() == ERROR_NO_MORE_FILES ? S_OK :
-                                                     HRESULTFromLastError();
-  }
-
-  do {
-    // Bail out if shutting down
-    if (shutdown_event && IsHandleSignaled(shutdown_event)) {
-      return E_ABORT;
-    }
-
-    // Bail out if reaching maximum running time
-    if (end_time_ms && ::GetTickCount() >= end_time_ms) {
-      UTIL_LOG(L6, (_T("[InternalSafeGetDirectorySize]")
-                    _T("[reaching max running time][%s][%u]"),
-                    dir_name, end_time_ms));
-      return E_ABORT;
-    }
-
-    // Skip reparse point since it might be a hard link which could cause an
-    // infinite recursive directory loop.
-    if (find_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
-      continue;
-    }
-
-    if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
-      // Skip . and ..
-      if (String_StrNCmp(find_data.cFileName, _T("."), 2, false) == 0 ||
-          String_StrNCmp(find_data.cFileName, _T(".."), 3, false) == 0) {
-        continue;
-      }
-
-      // Bail out if reaching maximum depth
-      if (max_depth && curr_depth + 1 >= max_depth) {
-        UTIL_LOG(L6, (_T("[InternalSafeGetDirectorySize]")
-                      _T("[reaching max depth][%s][%u]"), dir_name, max_depth));
-        return E_ABORT;
-      }
-
-      // Walk over sub-directory
-      CString sub_dir_name = String_MakeEndWith(dir_name, _T("\\"), false);
-      sub_dir_name += find_data.cFileName;
-      RET_IF_FAILED(InternalSafeGetDirectorySize(sub_dir_name,
-                                                 size,
-                                                 shutdown_event,
-                                                 max_size,
-                                                 curr_file_count,
-                                                 max_file_count,
-                                                 curr_depth + 1,
-                                                 max_depth,
-                                                 end_time_ms));
-    } else {
-      // Bail out if reaching maximum number of files
-      ++curr_file_count;
-      if (max_file_count && curr_file_count >= max_file_count) {
-        UTIL_LOG(L6, (_T("[InternalSafeGetDirectorySize]")
-                      _T("[reaching max file count][%s][%u]"),
-                      dir_name, max_file_count));
-        return E_ABORT;
-      }
-
-      // Count the file size
-      uint64 file_size =
-          ((static_cast<uint64>((find_data.nFileSizeHigh)) << 32)) +
-          static_cast<uint64>(find_data.nFileSizeLow);
-      *size += file_size;
-
-      // Bail out if reaching maximum size
-      if (max_size && *size >= max_size) {
-        UTIL_LOG(L6, (_T("[InternalSafeGetDirectorySize]")
-                      _T("[reaching max size][%s][%u]"), dir_name, max_size));
-        return E_ABORT;
-      }
-    }
-  } while (::FindNextFile(get(hfind), &find_data));
-
-  return ::GetLastError() == ERROR_NO_MORE_FILES ? S_OK :
-                                                   HRESULTFromLastError();
-}
-
-// The safe version of getting size of all files in a directory
-// It is able to abort the counting if one of the maximum criteria is reached
-HRESULT SafeGetDirectorySize(const TCHAR* dir_name,
-                             uint64* size,
-                             HANDLE shutdown_event,
-                             uint64 max_size,
-                             int max_file_count,
-                             int max_depth,
-                             int max_running_time_ms) {
-  ASSERT1(dir_name && *dir_name);
-  ASSERT1(size);
-
-  *size = 0;
-
-  DWORD end_time = 0;
-  if (max_running_time_ms > 0) {
-    end_time = ::GetTickCount() + max_running_time_ms;
-  }
-  return InternalSafeGetDirectorySize(dir_name,
-                                      size,
-                                      shutdown_event,
-                                      max_size,
-                                      0,
-                                      max_file_count,
-                                      0,
-                                      max_depth,
-                                      end_time);
-}
-
-// Get size of all files in a directory
-HRESULT GetDirectorySize(const TCHAR* dir_name, uint64* size) {
-  ASSERT1(dir_name && *dir_name);
-  ASSERT1(size);
-  return SafeGetDirectorySize(dir_name, size, NULL, 0, 0, 0, 0);
-}
-
-// Handles the logic to determine the handle that was signaled
-// as a result of calling *WaitForMultipleObjects.
-HRESULT GetSignaledObjectPosition(uint32 cnt, DWORD res, uint32* pos) {
-  ASSERT1(pos);
-
-  if (res == WAIT_FAILED) {
-    return S_FALSE;
-  }
-
-#pragma warning(disable : 4296)
-  // C4296: '>=' : expression is always true
-  if ((res >= WAIT_OBJECT_0) && (res < WAIT_OBJECT_0 + cnt)) {
-    *pos = res - WAIT_OBJECT_0;
-    return S_OK;
-  }
-#pragma warning(default : 4296)
-
-  if ((res >= WAIT_ABANDONED_0) && (res < WAIT_ABANDONED_0 + cnt)) {
-    *pos = res - WAIT_ABANDONED_0;
-    return S_OK;
-  }
-  return E_INVALIDARG;
-}
-
-// Supports all of the other WaitWithMessage* functions.
-//
-// Returns:
-//    S_OK when the message loop should continue.
-//    S_FALSE when it receives something that indicates the
-//      loop should quit.
-//    E_* only when GetMessage failed
-//
-// This function is not exposed outside of this file.  Only
-// friendly wrappers of it are.
-HRESULT WaitWithMessageLoopAnyInternal(
-    const HANDLE* phandles,
-    uint32 cnt,
-    uint32* pos,
-    MessageHandlerInternalInterface* message_handler) {
-  ASSERT1(pos && message_handler);
-  // cnt and phandles are either both zero or both not zero.
-  ASSERT1(!cnt == !phandles);
-
-  // Loop until an error happens or the wait is satisfied by a signaled
-  // object or an abandoned mutex.
-  for (;;) {
-    MSG msg = {0};
-
-    // Process the messages in the input queue.
-    while (::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) {
-      BOOL ret = false;
-      if ((ret = ::GetMessage(&msg, NULL, 0, 0)) != 0) {
-        if (ret == -1) {
-          HRESULT hr = HRESULTFromLastError();
-          UTIL_LOG(LEVEL_ERROR,
-              (_T("[WaitWithMessageLoopAny - GetMessage failed][0x%08x]"), hr));
-          return hr;
-        }
-        message_handler->Process(&msg, &phandles, &cnt);
-      } else {
-        // We need to re-post the quit message we retrieved so that it could
-        // propagate to the outer layer. Otherwise, the program will seem to
-        // "get stuck" in its shutdown code.
-        ::PostQuitMessage(msg.wParam);
-        return S_FALSE;
-      }
-
-      // WaitForMultipleObjects fails if cnt == 0.
-      if (cnt) {
-        // Briefly check the state of the handle array to see if something
-        // has signaled as we processed a message.
-        ASSERT1(phandles);
-        DWORD res = ::WaitForMultipleObjects(cnt, phandles, false, 0);
-        ASSERT1(res != WAIT_FAILED);
-        HRESULT hr = GetSignaledObjectPosition(cnt, res, pos);
-        if (SUCCEEDED(hr)) {
-          return hr;
-        }
-      }
-    }
-
-    // The wait with message. It is satisfied by either the objects getting
-    // signaled or when messages enter the message queue.
-    // TODO(omaha): implementing timeout is a little bit tricky since we
-    // want the timeout on the handles only and the native API does not
-    // have this semantic.
-    //
-    // TODO(omaha): use a waitable timer to implement the timeout.
-    //
-    // When cnt is zero then the execution flow waits here until messages
-    // arrive in the input queue. Unlike WaitForMultipleObjects,
-    // MsgWaitForMultipleObjects does not error out when cnt == 0.
-    const DWORD timeout = INFINITE;
-    DWORD res(::MsgWaitForMultipleObjects(cnt, phandles, false, timeout,
-                                          QS_ALLINPUT));
-    ASSERT((res != WAIT_FAILED),
-           (_T("[MsgWaitForMultipleObjects returned WAIT_FAILED][%u]"),
-            ::GetLastError()));
-
-    ASSERT1(res != WAIT_TIMEOUT);
-
-    HRESULT hr = GetSignaledObjectPosition(cnt, res, pos);
-    if (SUCCEEDED(hr)) {
-      return hr;
-    }
-  }
-}
-
-// The simplest implementation of a message processor
-void BasicMessageHandler::Process(MSG* msg) {
-  ASSERT1(msg);
-  ::TranslateMessage(msg);
-  ::DispatchMessage(msg);
-}
-
-class BasicMessageHandlerInternal : public BasicMessageHandler,
-                                    public MessageHandlerInternalInterface {
- public:
-  BasicMessageHandlerInternal() {}
-  virtual void Process(MSG* msg, const HANDLE**, uint32*) {
-    BasicMessageHandler::Process(msg);
-  }
- private:
-  DISALLOW_EVIL_CONSTRUCTORS(BasicMessageHandlerInternal);
-};
-
-
-bool WaitWithMessageLoopAny(const std::vector<HANDLE>& handles, uint32* pos) {
-  BasicMessageHandlerInternal msg_handler;
-  return WaitWithMessageLoopAnyInternal(&handles.front(), handles.size(), pos,
-                                        &msg_handler) != S_FALSE;
-}
-
-bool WaitWithMessageLoopAll(const std::vector<HANDLE>& handles) {
-  // make a copy of the vector, as objects must be removed from the
-  // wait array as they get signaled.
-  std::vector<HANDLE> h(handles);
-
-  // The function is mainly implemented in terms of WaitWithMessageLoopAny
-
-  // loop until all objects are signaled.
-  while (!h.empty()) {
-    uint32 pos(static_cast<uint32>(-1));
-    if (!WaitWithMessageLoopAny(h, &pos)) return false;
-    ASSERT1(pos < h.size());
-    h.erase(h.begin() + pos);   // remove the signaled object and loop
-  }
-
-  return true;
-}
-
-bool WaitWithMessageLoop(HANDLE h) {
-  BasicMessageHandlerInternal msg_handler;
-  uint32 pos(static_cast<uint32>(-1));
-  bool res =
-    WaitWithMessageLoopAnyInternal(&h, 1, &pos, &msg_handler) != S_FALSE;
-  if (res) {
-    // It's the first and the only handle that it is signaled.
-    ASSERT1(pos == 0);
-  }
-  return res;
-}
-
-// Wait with message loop for a certain period of time
-bool WaitWithMessageLoopTimed(DWORD ms) {
-  scoped_timer timer(::CreateWaitableTimer(NULL,
-                                           true,   // manual reset
-                                           NULL));
-  ASSERT1(get(timer));
-  LARGE_INTEGER timeout = MSto100NSRelative(ms);
-  BOOL timer_ok = ::SetWaitableTimer(get(timer),
-                                     &timeout,
-                                     0,
-                                     NULL,
-                                     NULL,
-                                     false);
-  ASSERT1(timer_ok);
-  return WaitWithMessageLoop(get(timer));
-}
-
-MessageLoopWithWait::MessageLoopWithWait() : message_handler_(NULL) {
-}
-
-void MessageLoopWithWait::set_message_handler(
-    MessageHandlerInterface* message_handler) {
-  message_handler_ = message_handler;
-}
-
-// The message loop and handle callback routine.
-HRESULT MessageLoopWithWait::Process() {
-  while (true) {
-    ASSERT1(callback_handles_.size() == callbacks_.size());
-
-    // The implementation allows for an empty array of handles. Taking the
-    // address of elements in an empty container is not allowed so we must
-    // deal with this case here.
-    size_t pos(0);
-    HRESULT hr = WaitWithMessageLoopAnyInternal(
-        callback_handles_.empty() ? NULL : &callback_handles_.front(),
-        callback_handles_.size(),
-        &pos,
-        this);
-
-    // In addition to E_*, S_FALSE should cause a return to happen here.
-    if (hr != S_OK) {
-      return hr;
-    }
-
-    ASSERT1(pos < callback_handles_.size());
-    ASSERT1(callback_handles_.size() == callbacks_.size());
-
-    HANDLE signaled_handle = callback_handles_[pos];
-    WaitCallbackInterface* callback_interface = callbacks_[pos];
-    RemoveHandleAt(pos);
-
-    if (!callback_interface->HandleSignaled(signaled_handle)) {
-      return S_OK;
-    }
-  }
-}
-
-// Handles one messgae and adjust the handles and cnt as appropriate after
-// handling the message.
-void MessageLoopWithWait::Process(MSG* msg, const HANDLE** handles,
-                                  uint32* cnt) {
-  ASSERT1(msg && handles && cnt);
-
-  if (message_handler_) {
-    message_handler_->Process(msg);
-  }
-
-  // Set the handles and count again because they may have changed
-  // while processing the message.
-  *handles = callback_handles_.empty() ? NULL : &callback_handles_.front();
-  *cnt = callback_handles_.size();
-}
-// Starts waiting on the given handle
-bool MessageLoopWithWait::RegisterWaitForSingleObject(
-    HANDLE handle, WaitCallbackInterface* callback) {
-  ASSERT1(callback_handles_.size() == callbacks_.size());
-  ASSERT1(callback != NULL);
-
-  if (callback_handles_.size() >= MAXIMUM_WAIT_OBJECTS - 1) {
-    return false;
-  }
-
-  // In case the user is registering a handle, that they previous added
-  // remove the previous one before adding it back into the array.
-  UnregisterWait(handle);
-  callback_handles_.push_back(handle);
-  callbacks_.push_back(callback);
-
-  ASSERT1(callback_handles_.size() == callbacks_.size());
-  return true;
-}
-
-// Finds the given handle and stops waiting on it
-bool MessageLoopWithWait::UnregisterWait(HANDLE handle) {
-  ASSERT1(callback_handles_.size() == callbacks_.size());
-
-  for (uint32 index = 0; index < callback_handles_.size() ; index++) {
-    if (callback_handles_[index] == handle) {
-      RemoveHandleAt(index);
-      return true;
-    }
-  }
-  return false;
-}
-
-// Removes the wait handle at the given position
-void MessageLoopWithWait::RemoveHandleAt(uint32 pos) {
-  ASSERT1(callback_handles_.size() == callbacks_.size());
-  ASSERT1(pos < callback_handles_.size());
-
-  callback_handles_.erase(callback_handles_.begin() + pos);
-  callbacks_.erase(callbacks_.begin() + pos);
-
-  ASSERT1(callback_handles_.size() == callbacks_.size());
-}
-
-HRESULT CallEntryPoint0(const TCHAR* dll_path,
-                        const char* function_name,
-                        HRESULT* result) {
-  ASSERT1(dll_path);
-  ASSERT1(::lstrlen(dll_path) > 0);
-  ASSERT1(function_name);
-  ASSERT1(::strlen(function_name) > 0);
-  ASSERT1(result);
-
-  scoped_library dll(::LoadLibrary(dll_path));
-  if (!dll) {
-    return HRESULTFromLastError();
-  }
-
-  HRESULT (*proc)() = reinterpret_cast<HRESULT (*)()>(
-      ::GetProcAddress(get(dll), function_name));
-  if (!proc) {
-    return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
-  }
-
-  *result = (proc)();
-  return S_OK;
-}
-
-// Register a DLL
-HRESULT RegisterDll(const TCHAR* dll_path) {
-  HRESULT hr = S_OK;
-  HRESULT hr_call = CallEntryPoint0(dll_path, "DllRegisterServer", &hr);
-  if (SUCCEEDED(hr_call)) {
-    return hr;
-  }
-  return hr_call;
-}
-
-// Unregister a DLL
-HRESULT UnregisterDll(const TCHAR* dll_path) {
-  HRESULT hr = S_OK;
-  HRESULT hr_call = CallEntryPoint0(dll_path, "DllUnregisterServer", &hr);
-  if (SUCCEEDED(hr_call)) {
-    return hr;
-  }
-  return hr_call;
-}
-
-// Register/unregister an EXE
-HRESULT RegisterOrUnregisterExe(const TCHAR* exe_path, const TCHAR* cmd_line) {
-  ASSERT1(exe_path);
-  ASSERT1(cmd_line);
-
-  // cmd_line parameter really contains the arguments to be passed
-  // on the process creation command line.
-  PROCESS_INFORMATION pi = {0};
-  HRESULT hr = System::StartProcessWithArgsAndInfo(exe_path, cmd_line, &pi);
-  if (FAILED(hr)) {
-    UTIL_LOG(LEVEL_WARNING, (_T("[RegisterOrUnregisterExe]")
-                             _T("[failed to start process]")
-                             _T("[%s][%s][0x%08x]"), exe_path, cmd_line, hr));
-    return hr;
-  }
-  // Take ownership of the handles for clean up.
-  scoped_thread thread(pi.hThread);
-  scoped_process process(pi.hProcess);
-
-  // ATL COM servers return an HRESULT on exit. There is a case in which they
-  // return -1 which seems like a bug in ATL. It appears there is no
-  // documented convention on what a local server would return for errors.
-  // There is a possibility that a server would return Windows errors.
-
-  // Wait on the process to exit and return the exit code of the process.
-  DWORD result(::WaitForSingleObject(get(process), kRegisterExeTimeoutMs));
-  DWORD exit_code(0);
-  if (result == WAIT_OBJECT_0 &&
-      ::GetExitCodeProcess(get(process), &exit_code)) {
-    return static_cast<HRESULT>(exit_code);
-  } else {
-    return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
-  }
-}
-
-// Register a COM Local Server
-HRESULT RegisterServer(const TCHAR* exe_path) {
-  return RegisterOrUnregisterExe(exe_path, _T("/RegServer"));
-}
-
-// Unregister a COM Local Server
-HRESULT UnregisterServer(const TCHAR* exe_path) {
-  return RegisterOrUnregisterExe(exe_path, _T("/UnregServer"));
-}
-
-// Register a Service
-HRESULT RegisterService(const TCHAR* exe_path) {
-  return RegisterOrUnregisterExe(exe_path, _T("/Service"));
-}
-
-// Unregister a Service
-HRESULT UnregisterService(const TCHAR* exe_path) {
-  // Unregistering a service is via UnregServer
-  return RegisterOrUnregisterExe(exe_path, _T("/UnregServer"));
-}
-
-// Adapted from gds installer/install/work_list.cpp: InstallServiceExecutable
-HRESULT RunService(const TCHAR* service_name) {
-  scoped_service manager(::OpenSCManager(NULL,  // local machine
-                                         NULL,  // ServicesActive database
-                                         STANDARD_RIGHTS_READ));
-  ASSERT1(get(manager));
-  if (!get(manager)) {
-    return HRESULTFromLastError();
-  }
-
-  scoped_service service(::OpenService(get(manager), service_name,
-                                       SERVICE_START));
-  ASSERT1(get(service));
-  if (!get(service)) {
-    return HRESULTFromLastError();
-  }
-
-  UTIL_LOG(L2, (_T("start service")));
-  if (!::StartService(get(service), 0, NULL)) {
-    return HRESULTFromLastError();
-  }
-  return S_OK;
-}
-
-
-HRESULT ReadEntireFile(const TCHAR* filepath,
-                       uint32 max_len,
-                       std::vector<byte>* buffer_out) {
-  return ReadEntireFileShareMode(filepath, max_len, 0, buffer_out);
-}
-
-HRESULT ReadEntireFileShareMode(const TCHAR* filepath,
-                                uint32 max_len,
-                                DWORD share_mode,
-                                std::vector<byte>* buffer_out) {
-  ASSERT1(filepath);
-  ASSERT1(buffer_out);
-
-  File file;
-  HRESULT hr = file.OpenShareMode(filepath, false, false, share_mode);
-  if (FAILED(hr)) {
-    // File missing.
-    return hr;
-  }
-
-  ON_SCOPE_EXIT_OBJ(file, &File::Close);
-
-  uint32 file_len = 0;
-  hr = file.GetLength(&file_len);
-  if (FAILED(hr)) {
-    // Should never happen
-    return hr;
-  }
-
-  if (max_len != 0 && file_len > max_len) {
-    // Too large to consider
-    return MEM_E_INVALID_SIZE;
-  }
-
-  if (file_len == 0) {
-    buffer_out->clear();
-    return S_OK;
-  }
-
-  int old_size = buffer_out->size();
-  buffer_out->resize(old_size + file_len);
-
-  uint32 bytes_read = 0;
-  hr = file.ReadFromStartOfFile(file_len,
-                                &(*buffer_out)[old_size],
-                                &bytes_read);
-  if (FAILED(hr)) {
-    // I/O error of some kind
-    return hr;
-  }
-
-  if (bytes_read != file_len) {
-    // Unexpected length. This could happen when reading a file someone else
-    // is writing to such as log files.
-    ASSERT1(false);
-    return E_UNEXPECTED;
-  }
-
-  // All's well that ends well
-  return S_OK;
-}
-
-HRESULT WriteEntireFile(const TCHAR * filepath,
-                        const std::vector<byte>& buffer_in) {
-  ASSERT1(filepath);
-
-  // File::WriteAt doesn't implement clear-on-open-for-write semantics,
-  // so just delete the file if it exists instead of writing into it.
-
-  if (File::Exists(filepath)) {
-    HRESULT hr = File::Remove(filepath);
-    if (FAILED(hr)) {
-      return hr;
-    }
-  }
-
-  File file;
-  HRESULT hr = file.Open(filepath, true, false);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  ON_SCOPE_EXIT_OBJ(file, &File::Close);
-
-  uint32 bytes_written = 0;
-  hr = file.WriteAt(0, &buffer_in.front(), buffer_in.size(), 0, &bytes_written);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  if (bytes_written != buffer_in.size()) {
-    // This shouldn't happen, caller needs to investigate what's up.
-    ASSERT1(false);
-    return E_UNEXPECTED;
-  }
-
-  return S_OK;
-}
-
-// Conversions between a byte stream and a std::string
-HRESULT BufferToString(const std::vector<byte>& buffer_in, CStringA* str_out) {
-  ASSERT1(str_out);
-  str_out->Append(reinterpret_cast<const char*>(&buffer_in.front()),
-                  buffer_in.size());
-  return S_OK;
-}
-
-HRESULT StringToBuffer(const CStringA& str_in, std::vector<byte>* buffer_out) {
-  ASSERT1(buffer_out);
-  buffer_out->assign(str_in.GetString(),
-                     str_in.GetString() + str_in.GetLength());
-  return S_OK;
-}
-
-HRESULT BufferToString(const std::vector<byte>& buffer_in, CString* str_out) {
-  ASSERT1(str_out);
-
-  size_t len2 = buffer_in.size();
-  ASSERT1(len2 % 2 == 0);
-  size_t len = len2 / 2;
-
-  str_out->Append(reinterpret_cast<const TCHAR*>(&buffer_in.front()), len);
-
-  return S_OK;
-}
-
-HRESULT StringToBuffer(const CString& str_in, std::vector<byte>* buffer_out) {
-  ASSERT1(buffer_out);
-
-  size_t len = str_in.GetLength();
-  size_t len2 = len * 2;
-
-  buffer_out->resize(len2);
-  ::memcpy(&buffer_out->front(), str_in.GetString(), len2);
-
-  return S_OK;
-}
-
-HRESULT RegSplitKeyvalueName(const CString& keyvalue_name,
-                             CString* key_name,
-                             CString* value_name) {
-  ASSERT1(key_name);
-  ASSERT1(value_name);
-
-  const TCHAR kDefault[] = _T("\\(default)");
-
-  if (String_EndsWith(keyvalue_name, _T("\\"), false)) {
-    key_name->SetString(keyvalue_name, keyvalue_name.GetLength() - 1);
-    value_name->Empty();
-  } else if (String_EndsWith(keyvalue_name, kDefault, true)) {
-    key_name->SetString(keyvalue_name,
-                        keyvalue_name.GetLength() - TSTR_SIZE(kDefault));
-    value_name->Empty();
-  } else {
-    int last_slash = String_ReverseFindChar(keyvalue_name, _T('\\'));
-    if (last_slash == -1) {
-      // No slash found - bizzare and wrong
-      return E_FAIL;
-    }
-    key_name->SetString(keyvalue_name, last_slash);
-    value_name->SetString(keyvalue_name.GetString() + last_slash + 1,
-                          keyvalue_name.GetLength() - last_slash - 1);
-  }
-
-  return S_OK;
-}
-
-HRESULT ExpandEnvLikeStrings(const TCHAR* src,
-                             const std::map<CString, CString>& keywords,
-                             CString* dest) {
-  ASSERT1(src);
-  ASSERT1(dest);
-
-  const TCHAR kMarker = _T('%');
-
-  dest->Empty();
-
-  // Loop while finding the marker in the string
-  HRESULT hr = S_OK;
-  int pos = 0;
-  int marker_pos1 = -1;
-  while ((marker_pos1 = String_FindChar(src, kMarker, pos)) != -1) {
-    // Try to find the right marker
-    int marker_pos2 = -1;
-    const TCHAR* s = src + marker_pos1 + 1;
-    for (; *s; ++s) {
-      if (*s == kMarker) {
-        marker_pos2 = s - src;
-        break;
-      }
-      if (!String_IsIdentifierChar(*s)) {
-        break;
-      }
-    }
-    if (marker_pos2 == -1) {
-      // Unmatched marker found, skip
-      dest->Append(src + pos, marker_pos1 - pos + 1);
-      pos = marker_pos1 + 1;
-      continue;
-    }
-
-    // Get the name - without the % markers on each end
-    CString name(src + marker_pos1 + 1, marker_pos2 - marker_pos1 - 1);
-
-    bool found = false;
-    for (std::map<CString, CString>::const_iterator it(keywords.begin());
-         it != keywords.end();
-         ++it) {
-      if (_tcsicmp(it->first, name) == 0) {
-        dest->Append(src + pos, marker_pos1 - pos);
-        dest->Append(it->second);
-        found = true;
-        break;
-      }
-    }
-    if (!found) {
-      // No mapping found
-      UTIL_LOG(LEVEL_ERROR, (_T("[ExpandEnvLikeStrings]")
-                             _T("[no mapping found for '%s' in '%s']"),
-                             name, src));
-      dest->Append(src + pos, marker_pos2 - pos + 1);
-      hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
-    }
-
-    pos = marker_pos2 + 1;
-  }
-
-  int len = _tcslen(src);
-  if (pos < len) {
-    dest->Append(src + pos, len - pos);
-  }
-
-  return hr;
-}
-
-bool IsRegistryPath(const TCHAR* path) {
-  return String_StartsWith(path, _T("HKLM\\"), false) ||
-         String_StartsWith(path, _T("HKCU\\"), false) ||
-         String_StartsWith(path, _T("HKCR\\"), false) ||
-         String_StartsWith(path, _T("HKEY_LOCAL_MACHINE\\"), false) ||
-         String_StartsWith(path, _T("HKEY_CURRENT_USER\\"), false) ||
-         String_StartsWith(path, _T("HKEY_CLASSES_ROOT\\"), false);
-}
-
-bool IsUrl(const TCHAR* path) {
-  // Currently we only check for "http://" and "https://"
-  return String_StartsWith(path, kHttpProto, true) ||
-         String_StartsWith(path, kHttpsProto, true);
-}
-
-
-CString GuidToString(const GUID& guid) {
-  TCHAR guid_str[40] = {0};
-  VERIFY1(::StringFromGUID2(guid, guid_str, arraysize(guid_str)));
-  String_ToUpper(guid_str);
-  return guid_str;
-}
-
-// Helper function to convert string to GUID
-GUID StringToGuid(const CString& str) {
-  GUID guid(GUID_NULL);
-  if (!str.IsEmpty()) {
-    TCHAR* s = const_cast<TCHAR*>(str.GetString());
-    VERIFY(SUCCEEDED(::CLSIDFromString(s, &guid)), (_T("guid %s"), s));
-  }
-  return guid;
-}
-
-// Helper function to convert a variant containing a list of strings
-void VariantToStringList(VARIANT var, std::vector<CString>* list) {
-  ASSERT1(list);
-
-  list->clear();
-
-  ASSERT1(V_VT(&var) == VT_DISPATCH);
-  CComPtr<IDispatch> obj = V_DISPATCH(&var);
-  ASSERT1(obj);
-
-  CComVariant var_length;
-  VERIFY1(SUCCEEDED(obj.GetPropertyByName(_T("length"), &var_length)));
-  ASSERT1(V_VT(&var_length) == VT_I4);
-  int length = V_I4(&var_length);
-
-  for (int i = 0; i < length; ++i) {
-    CComVariant value;
-    VERIFY1(SUCCEEDED(obj.GetPropertyByName(itostr(i), &value)));
-    if (V_VT(&value) == VT_BSTR) {
-      list->push_back(V_BSTR(&value));
-    } else {
-      ASSERT1(false);
-    }
-  }
-}
-
-
-HRESULT GetCurrentProcessHandle(HANDLE* handle) {
-  ASSERT1(handle);
-  scoped_process real_handle;
-  HANDLE pseudo_handle = ::GetCurrentProcess();
-  bool res = ::DuplicateHandle(
-    pseudo_handle,         // this process pseudo-handle
-    pseudo_handle,         // handle to duplicate
-    pseudo_handle,         // the process receiving the handle
-    address(real_handle),  // this process real handle
-    0,                     // ignored
-    false,                 // don't inherit this handle
-    DUPLICATE_SAME_ACCESS) != 0;
-
-  *handle = NULL;
-  if (!res) {
-    return HRESULTFromLastError();
-  }
-  *handle = release(real_handle);
-  return S_OK;
-}
-
-
-// get a time64 value
-// NOTE: If the value is greater than the
-// max value, then SetValue will be called using the max_value.
-HRESULT GetLimitedTimeValue(const TCHAR* full_key_name, const TCHAR* value_name,
-                            time64 max_time, time64* value,
-                            bool* limited_value) {
-  ASSERT1(full_key_name);
-  ASSERT1(value);
-  STATIC_ASSERT(sizeof(time64) == sizeof(DWORD64));
-
-  if (limited_value) {
-    *limited_value = false;
-  }
-  HRESULT hr = RegKey::GetValue(full_key_name, value_name, value);
-  if (SUCCEEDED(hr) && *value > max_time) {
-    *value = max_time;
-
-    // Use a different hr for the setting of the value b/c
-    // the returned hr should reflect the success/failure of reading the key
-    HRESULT set_value_hr = RegKey::SetValue(full_key_name, value_name, *value);
-    ASSERT(SUCCEEDED(set_value_hr), (_T("[GetLimitedTimeValue - failed ")
-                                     _T("when setting a value][0x%x]"),
-                                     set_value_hr));
-    if (SUCCEEDED(set_value_hr) && limited_value) {
-      *limited_value = true;
-    }
-  }
-  return hr;
-}
-
-// get a time64 value trying reg keys successively if there is a
-// failure in getting a value.
-HRESULT GetLimitedTimeValues(const TCHAR* full_key_names[],
-                             int key_names_length,
-                             const TCHAR* value_name,
-                             time64 max_time,
-                             time64* value,
-                             bool* limited_value) {
-  ASSERT1(full_key_names);
-  ASSERT1(value);
-  ASSERT1(key_names_length > 0);
-
-  HRESULT hr = E_FAIL;
-  for (int i = 0; i < key_names_length; ++i) {
-    hr = GetLimitedTimeValue(full_key_names[i], value_name, max_time, value,
-                             limited_value);
-    if (SUCCEEDED(hr)) {
-      return hr;
-    }
-  }
-  return hr;
-}
-
-// Wininet.dll (and especially the version that comes with IE7, with 01/12/07
-// timestamp) incorrectly initializes Rasman.dll. As a result, there is a race
-// condition that causes double-free on a memory from process heap.
-// This causes memory corruption in the heap that may later produce a variety
-// of ill effects, most frequently a crash with a callstack that contains
-// wininet and rasman, or ntdll!RtlAllocHeap. The root cause is that
-// Rasapi32!LoadRasmanDllAndInit is not thread safe and can start very involved
-// process of initialization on 2 threads at the same time. It's a bug.
-// Solution: in the begining of the program, trigger synchronous load of
-// rasman dll. The easy way is to call a public ras api that does synchronous
-// initialization, which is what we do here.
-void EnsureRasmanLoaded() {
-  RASENTRYNAME ras_entry_name = {0};
-  DWORD size_bytes = sizeof(ras_entry_name);
-  DWORD number_of_entries = 0;
-  ras_entry_name.dwSize = size_bytes;
-  // we don't really need results of this method,
-  // it simply triggers RASAPI32!LoadRasmanDllAndInit() internally.
-  ::RasEnumEntries(NULL,
-                   NULL,
-                   &ras_entry_name,
-                   &size_bytes,
-                   &number_of_entries);
-}
-
-// Appends two reg keys. Handles the situation where there are traling
-// back slashes in one and leading back slashes in two.
-CString AppendRegKeyPath(const CString& one, const CString& two) {
-  CString leftpart(one);
-  int length = leftpart.GetLength();
-  int i = 0;
-  for (i = length - 1; i >= 0; --i) {
-    if (leftpart[i] != _T('\\')) {
-      break;
-    }
-  }
-  leftpart = leftpart.Left(i+1);
-
-  CString rightpart(two);
-  int lengthr = rightpart.GetLength();
-  for (i = 0; i < lengthr; ++i) {
-    if (rightpart[i] != _T('\\')) {
-      break;
-    }
-  }
-  rightpart = rightpart.Right(lengthr - i);
-
-  CString result;
-  result.Format(_T("%s\\%s"), leftpart, rightpart);
-  return result;
-}
-
-CString AppendRegKeyPath(const CString& one, const CString& two,
-                         const CString& three) {
-  CString result = AppendRegKeyPath(one, two);
-  result = AppendRegKeyPath(result, three);
-  return result;
-}
-
-
-HRESULT GetUserKeysFromHkeyUsers(std::vector<CString>* key_names) {
-  ASSERT1(key_names);
-  CORE_LOG(L3, (_T("[GetUserKeysFromHkeyUsers]")));
-
-  TCHAR user_key_name[MAX_PATH] = {0};
-  int i = 0;
-  while (::RegEnumKey(HKEY_USERS, i++, user_key_name, MAX_PATH) !=
-                      ERROR_NO_MORE_ITEMS) {
-        byte sid_buffer[SECURITY_MAX_SID_SIZE] = {0};
-    PSID sid = reinterpret_cast<PSID>(sid_buffer);
-    if (::ConvertStringSidToSid(user_key_name, &sid) != 0) {
-      // We could convert the string SID into a real SID. If not
-      // we just ignore.
-      DWORD size = MAX_PATH;
-      DWORD size_domain = MAX_PATH;
-      SID_NAME_USE sid_type = SidTypeComputer;
-      TCHAR user_name[MAX_PATH] = {0};
-      TCHAR domain_name[MAX_PATH] = {0};
-
-      if (::LookupAccountSid(NULL, sid, user_name, &size,
-                             domain_name, &size_domain, &sid_type) == 0) {
-        HRESULT hr = HRESULTFromLastError();
-        CORE_LOG(LEVEL_WARNING,
-            (_T("[GetUserKeysFromHkeyUsers LookupAccountSid] ")
-             _T(" failed [0x%08x]"),
-             hr));
-        continue;
-      }
-
-      if (sid_type == SidTypeUser) {
-        // Change the RunAs keys for the user goopdates to point to the
-        // machine install.
-        CString user_reg_key_name = AppendRegKeyPath(USERS_KEY, user_key_name);
-        key_names->push_back(user_reg_key_name);
-      }
-    }
-  }
-
-  return S_OK;
-}
-
-HRESULT IsSystemProcess(bool* is_system_process) {
-  CAccessToken current_process_token;
-  if (!current_process_token.GetProcessToken(TOKEN_QUERY,
-                                             ::GetCurrentProcess())) {
-    HRESULT hr = HRESULTFromLastError();
-    ASSERT(false, (_T("[CAccessToken::GetProcessToken 0x%x]"), hr));
-    return hr;
-  }
-  CSid logon_sid;
-  if (!current_process_token.GetUser(&logon_sid)) {
-    HRESULT hr = HRESULTFromLastError();
-    ASSERT(false, (_T("[CAccessToken::GetUser 0x%x]"), hr));
-    return hr;
-  }
-  *is_system_process = logon_sid == Sids::System();
-  return S_OK;
-}
-
-HRESULT IsUserLoggedOn(bool* is_logged_on) {
-  ASSERT1(is_logged_on);
-  bool is_local_system(false);
-  HRESULT hr = IsSystemProcess(&is_local_system);
-  if (SUCCEEDED(hr) && is_local_system) {
-    *is_logged_on = true;
-    return S_OK;
-  }
-  return UserRights::UserIsLoggedOnInteractively(is_logged_on);
-}
-
-bool IsClickOnceDisabled() {
-  CComPtr<IInternetZoneManager> zone_mgr;
-  HRESULT hr =  zone_mgr.CoCreateInstance(CLSID_InternetZoneManager);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[CreateInstance InternetZoneManager failed][0x%x]"), hr));
-    return true;
-  }
-
-  DWORD policy = URLPOLICY_DISALLOW;
-  size_t policy_size = sizeof(policy);
-  hr = zone_mgr->GetZoneActionPolicy(URLZONE_INTERNET,
-                                     URLACTION_MANAGED_UNSIGNED,
-                                     reinterpret_cast<BYTE*>(&policy),
-                                     policy_size,
-                                     URLZONEREG_DEFAULT);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[GetZoneActionPolicy failed][0x%x]"), hr));
-    return true;
-  }
-
-  return policy == URLPOLICY_DISALLOW;
-}
-
-// This function only uses kernel32, and it is safe to call from DllMain.
-HRESULT PinModuleIntoProcess(const CString& module_name) {
-  ASSERT1(!module_name.IsEmpty());
-  static HMODULE module_handle = NULL;
-  typedef BOOL (WINAPI *Fun)(DWORD flags,
-                             LPCWSTR module_name,
-                             HMODULE* module_handle);
-
-  HINSTANCE kernel_instance = ::GetModuleHandle(_T("kernel32.dll"));
-  ASSERT1(kernel_instance);
-  Fun pfn = NULL;
-  if (GPA(kernel_instance, "GetModuleHandleExW", &pfn)) {
-    if ((*pfn)(GET_MODULE_HANDLE_EX_FLAG_PIN, module_name, &module_handle)) {
-      return S_OK;
-    }
-    ASSERT(false, (_T("GetModuleHandleExW() failed [%d]"), ::GetLastError()));
-  }
-
-  module_handle = ::LoadLibrary(module_name);
-  ASSERT(NULL != module_handle, (_T("LoadLibrary [%d]"), ::GetLastError()));
-  if (NULL == module_handle) {
-    return HRESULTFromLastError();
-  }
-
-  return S_OK;
-}
-
-bool ShellExecuteExEnsureParent(LPSHELLEXECUTEINFO shell_exec_info) {
-  UTIL_LOG(L3, (_T("[ShellExecuteExEnsureParent]")));
-
-  ASSERT1(shell_exec_info);
-  bool shell_exec_succeeded(false);
-  DWORD last_error(ERROR_SUCCESS);
-
-  {
-    // hwnd_parent window is destroyed at the end of the scope when the
-    // destructor of scoped_window calls ::DestroyWindow.
-    scoped_window hwnd_parent;
-
-    if (!shell_exec_info->hwnd && vista_util::IsVistaOrLater()) {
-      reset(hwnd_parent, CreateForegroundParentWindowForUAC());
-
-      if (!hwnd_parent) {
-        last_error = ::GetLastError();
-        UTIL_LOG(LE, (_T("[CreateDummyOverlappedWindow failed]")));
-        // Restore last error in case the logging reset it.
-        ::SetLastError(last_error);
-        return false;
-      }
-
-      shell_exec_info->hwnd = get(hwnd_parent);
-
-      // If elevation is required on Vista, call ::SetForegroundWindow(). This
-      // will make sure that the elevation prompt, as well as the elevated
-      // process window comes up in the foreground. It will also ensure that in
-      // the case where the elevation prompt is cancelled, the error dialog
-      // shown from this process comes up in the foreground.
-      if (shell_exec_info->lpVerb &&
-          _tcsicmp(shell_exec_info->lpVerb, _T("runas")) == 0) {
-        if (!::SetForegroundWindow(get(hwnd_parent))) {
-          UTIL_LOG(LW, (_T("[SetForegroundWindow fail %d]"), ::GetLastError()));
-        }
-      }
-    }
-
-    shell_exec_succeeded = !!::ShellExecuteEx(shell_exec_info);
-
-    if (shell_exec_succeeded) {
-      if (shell_exec_info->hProcess) {
-        DWORD pid = Process::GetProcessIdFromHandle(shell_exec_info->hProcess);
-        OPT_LOG(L1, (_T("[Started process][%u]"), pid));
-        if (!::AllowSetForegroundWindow(pid)) {
-          UTIL_LOG(LW, (_T("[AllowSetForegroundWindow %d]"), ::GetLastError()));
-        }
-      } else {
-        OPT_LOG(L1, (_T("[Started process][PID unknown]")));
-      }
-    } else {
-      last_error = ::GetLastError();
-      UTIL_LOG(LE, (_T("[ShellExecuteEx fail][%s][%s][0x%08x]"),
-                    shell_exec_info->lpFile, shell_exec_info->lpParameters,
-                    last_error));
-    }
-  }
-
-  // The implicit ::DestroyWindow call from the scoped_window could have reset
-  // the last error, so restore it.
-  ::SetLastError(last_error);
-
-  return shell_exec_succeeded;
-}
-
-// Loads and unloads advapi32.dll for every call. If performance is an issue
-// consider keeping the dll always loaded and holding the pointer to the
-// RtlGenRandom in a static variable.
-// Use the function with care. While the function is documented, it may be
-// altered or made unavailable in future versions of the operating system.
-bool GenRandom(void* buffer, size_t buffer_length) {
-  ASSERT1(buffer);
-  scoped_library lib(::LoadLibrary(_T("ADVAPI32.DLL")));
-  if (lib) {
-    typedef BOOLEAN (APIENTRY *RtlGenRandomType)(void*, ULONG);
-    RtlGenRandomType rtl_gen_random = reinterpret_cast<RtlGenRandomType>(
-        ::GetProcAddress(get(lib), "SystemFunction036"));
-    return rtl_gen_random && rtl_gen_random(buffer, buffer_length);
-  }
-
-  // Use CAPI to generate randomness for systems which do not support
-  // RtlGenRandomType, for instance Windows 2000.
-  const uint32 kCspFlags = CRYPT_VERIFYCONTEXT | CRYPT_SILENT;
-  HCRYPTPROV csp = NULL;
-  if (::CryptAcquireContext(&csp, NULL, NULL, PROV_RSA_FULL, kCspFlags)) {
-    if (::CryptGenRandom(csp, buffer_length, static_cast<BYTE*>(buffer))) {
-      return true;
-    }
-  }
-  VERIFY1(::CryptReleaseContext(csp, 0));
-  return false;
-}
-
-// Assumes the path in command is properly enclosed if necessary.
-HRESULT ConfigureRunAtStartup(const CString& root_key_name,
-                              const CString& run_value_name,
-                              const CString& command,
-                              bool install) {
-  UTIL_LOG(L3, (_T("ConfigureRunAtStartup")));
-
-  const CString key_path = AppendRegKeyPath(root_key_name, REGSTR_PATH_RUN);
-  HRESULT hr(S_OK);
-
-  if (install) {
-    hr = RegKey::SetValue(key_path, run_value_name, command);
-  } else {
-    hr = RegKey::DeleteValue(key_path, run_value_name);
-  }
-
-  return hr;
-}
-
-HRESULT GetExePathFromCommandLine(const TCHAR* command_line,
-                                  CString* exe_path) {
-  ASSERT1(exe_path);
-  CString command_line_str(command_line);
-  command_line_str.Trim(_T(' '));
-  if (command_line_str.IsEmpty()) {
-    // ::CommandLineToArgvW parses the current process command line for blank
-    // strings. We do not want this behavior.
-    return E_INVALIDARG;
-  }
-
-  int argc = 0;
-  wchar_t** argv = ::CommandLineToArgvW(command_line_str, &argc);
-  if (argc == 0 || !argv) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LE, (_T("[::CommandLineToArgvW failed][0x%x]"), hr));
-    return hr;
-  }
-
-  *exe_path = argv[0];
-  ::LocalFree(argv);
-  exe_path->Trim(_T(' '));
-  ASSERT1(!exe_path->IsEmpty());
-  return S_OK;
-}
-
-// Tries to open the _MSIExecute mutex and tests its state. MSI sets the
-// mutex when processing sequence tables. This indicates MSI is busy.
-// The function returns S_OK if the mutex is not owned by MSI or the mutex has
-// not been created.
-HRESULT WaitForMSIExecute(int timeout_ms) {
-  const TCHAR* mutex_name = _T("Global\\_MSIExecute");
-  scoped_mutex mutex(::OpenMutex(SYNCHRONIZE, false, mutex_name));
-  if (!mutex) {
-    DWORD error = ::GetLastError();
-    return (error == ERROR_FILE_NOT_FOUND) ? S_OK : HRESULT_FROM_WIN32(error);
-  }
-  UTIL_LOG(L3, (_T("[Wait for _MSIExecute]")));
-  switch (::WaitForSingleObject(get(mutex), timeout_ms)) {
-    case WAIT_OBJECT_0:
-    case WAIT_ABANDONED:
-      VERIFY1(::ReleaseMutex(get(mutex)));
-      return S_OK;
-    case WAIT_TIMEOUT:
-      return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
-    case WAIT_FAILED:
-      return HRESULTFromLastError();
-    default:
-      return E_FAIL;
-  }
-}
-
-CString GetEnvironmentVariableAsString(const TCHAR* name) {
-  CString value;
-  size_t value_length = ::GetEnvironmentVariable(name, NULL, 0);
-  if (value_length) {
-    VERIFY1(::GetEnvironmentVariable(name,
-                                     CStrBuf(value, value_length),
-                                     value_length));
-  }
-  return value;
-}
-
-// States are documented at
-// http://technet.microsoft.com/en-us/library/cc721913.aspx.
-bool IsWindowsInstalling() {
-  static const TCHAR kVistaSetupStateKey[] =
-      _T("Software\\Microsoft\\Windows\\CurrentVersion\\Setup\\State");
-  static const TCHAR kImageStateValueName[] = _T("ImageState");
-  static const TCHAR kImageStateUnuseableValue[] =
-      _T("IMAGE_STATE_UNDEPLOYABLE");
-  static const TCHAR kImageStateGeneralAuditValue[] =
-      _T("IMAGE_STATE_GENERALIZE_RESEAL_TO_AUDIT");
-  static const TCHAR kImageStateSpecialAuditValue[] =
-      _T("IMAGE_STATE_SPECIALIZE_RESEAL_TO_AUDIT");
-
-  static const TCHAR kXPSetupStateKey[] = _T("System\\Setup");
-  static const TCHAR kAuditFlagValueName[] = _T("AuditInProgress");
-
-  if (vista_util::IsVistaOrLater()) {
-    RegKey vista_setup_key;
-    HRESULT hr =
-        vista_setup_key.Open(HKEY_LOCAL_MACHINE, kVistaSetupStateKey, KEY_READ);
-    if (SUCCEEDED(hr)) {
-      CString state;
-      hr = vista_setup_key.GetValue(kImageStateValueName, &state);
-      if (SUCCEEDED(hr) &&
-          !state.IsEmpty() &&
-          (0 == state.CompareNoCase(kImageStateUnuseableValue) ||
-           0 == state.CompareNoCase(kImageStateGeneralAuditValue) ||
-           0 == state.CompareNoCase(kImageStateSpecialAuditValue)))
-        return true;  // Vista is still installing.
-    }
-  } else {
-    RegKey xp_setup_key;
-    HRESULT hr =
-        xp_setup_key.Open(HKEY_LOCAL_MACHINE, kXPSetupStateKey, KEY_READ);
-    if (SUCCEEDED(hr)) {
-      DWORD audit_flag(0);
-      hr = xp_setup_key.GetValue(kAuditFlagValueName, &audit_flag);
-      if (SUCCEEDED(hr) && 0 != audit_flag)
-        return true;  // XP is still installing.
-    }
-  }
-  return false;
-}
-
-}  // namespace omaha
diff --git a/common/utils.h b/common/utils.h
deleted file mode 100644
index f5ef29d..0000000
--- a/common/utils.h
+++ /dev/null
@@ -1,812 +0,0 @@
-// Copyright 2003-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_COMMON_UTILS_H__
-#define OMAHA_COMMON_UTILS_H__
-
-#include <windows.h>
-#include <accctrl.h>
-#include <aclapi.h>
-#include <sddl.h>
-#include <shellapi.h>
-#include <shlobj.h>
-#include <atlstr.h>
-#include <atlsecurity.h>
-#include <atlwin.h>
-#include <memory.h>
-#include <map>
-#include <vector>
-#include "base/basictypes.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/static_assert.h"
-#include "omaha/common/user_info.h"
-
-namespace omaha {
-
-// Determines whether to run ClickOnce components. This constant is not defined
-// in the SDK headers.
-#ifndef URLACTION_MANAGED_UNSIGNED
-#define URLACTION_MANAGED_UNSIGNED (0x00002004)
-#endif
-
-ULONGLONG VersionFromString(const CString& s);
-
-CString StringFromVersion(ULONGLONG version);
-
-// Gets current directory
-CString GetCurrentDir();
-
-// Creates a unique file name using a new guid. Does not check for
-// presence of this file in the directory.
-HRESULT GetNewFileNameInDirectory(const CString& dir, CString* file_name);
-
-// Use of this method is unsafe. Be careful!
-// Gets security attributes for a "everyone" (authenticated users) DACL.
-void GetEveryoneDaclSecurityAttributes(CSecurityAttributes* sec_attr,
-                                       ACCESS_MASK non_admin_access_mask);
-
-// Get security attributes containing a DACL that grant the ACCESS_MASK access
-// to admins and system.
-void GetAdminDaclSecurityAttributes(CSecurityAttributes* sec_attr,
-                                    ACCESS_MASK accessmask);
-
-// Merges an Allowed ACE into a named object. If the ACE already exists in the
-// DACL with the same permissions (or a superset) and the same ACE flags, the
-// merge is skipped.
-HRESULT AddAllowedAce(const TCHAR* object_name,
-                      SE_OBJECT_TYPE object_type,
-                      const CSid& sid,
-                      ACCESS_MASK required_permissions,
-                      uint8 required_ace_flags);
-
-struct NamedObjectAttributes {
-  CString name;
-  CSecurityAttributes sa;
-};
-
-// For machine and local system, the prefix would be "Global\G{obj_name}".
-// For user, the prefix would be "Global\G{user_sid}{obj_name}".
-// For machine objects, returns a security attributes that gives permissions to
-// both Admins and SYSTEM. This allows for cases where SYSTEM creates the named
-// object first. The default DACL for SYSTEM will not allow Admins access.
-void GetNamedObjectAttributes(const TCHAR* base_name,
-                              bool is_machine,
-                              NamedObjectAttributes* attr);
-
-// Returns true if the current process is running as SYSTEM.
-HRESULT IsSystemProcess(bool* is_system_process);
-
-// Returns true if the user of the current process is Local System or it has an
-// interactive session: console, terminal services, or fast user switching.
-HRESULT IsUserLoggedOn(bool* is_logged_on);
-
-// Returns true if URLACTION_MANAGED_UNSIGNED is disabled for the Internet zone
-// for the current user.
-bool IsClickOnceDisabled();
-
-// Wrapper around ::GetProcAddress().
-template <typename T>
-bool GPA(HMODULE module, const char* function_name, T* function_pointer) {
-  ASSERT1(module);
-  ASSERT1(function_name);
-  ASSERT1(function_pointer);
-
-  *function_pointer = reinterpret_cast<T>(::GetProcAddress(module,
-                                                           function_name));
-  if (NULL == *function_pointer) {
-    UTIL_LOG(LW, (_T("GetProcAddress failed [%s]"), CA2T(function_name)));
-  }
-  return NULL != *function_pointer;
-}
-
-#define GPA_WRAP(module,                                                    \
-                 function,                                                  \
-                 proto,                                                     \
-                 call,                                                      \
-                 calling_convention,                                        \
-                 result_type,                                               \
-                 result_error)                                              \
-typedef result_type (calling_convention *function##_pointer) proto;         \
-inline result_type function##Wrap proto {                                   \
-  HINSTANCE module_instance = ::GetModuleHandle(_T(#module));               \
-  ASSERT1(module_instance);                                                 \
-  if (!module_instance) {                                                   \
-    return result_error;                                                    \
-  }                                                                         \
-  function##_pointer fn = NULL;                                             \
-  return GPA(module_instance, #function, &fn) ? (*fn) call : result_error;  \
-}
-
-GPA_WRAP(kernel32.dll,
-         AttachConsole,
-         (DWORD process_id),
-         (process_id),
-         WINAPI,
-         BOOL,
-         0);
-
-// Private Object Namespaces for Vista and above. More information here:
-// http://msdn2.microsoft.com/en-us/library/ms684295(VS.85).aspx
-GPA_WRAP(kernel32.dll,
-         CreateBoundaryDescriptorW,
-         (LPCWSTR boundary_name, ULONG flags),
-         (boundary_name, flags),
-         WINAPI,
-         HANDLE,
-         NULL);
-GPA_WRAP(kernel32.dll,
-         AddSIDToBoundaryDescriptor,
-         (HANDLE* boundary_descriptor, PSID required_sid),
-         (boundary_descriptor, required_sid),
-         WINAPI,
-         BOOL,
-         FALSE);
-GPA_WRAP(kernel32.dll,
-         CreatePrivateNamespaceW,
-         (LPSECURITY_ATTRIBUTES private_namespace_attributes, LPVOID boundary_descriptor, LPCWSTR alias_prefix),  // NOLINT
-         (private_namespace_attributes, boundary_descriptor, alias_prefix),
-         WINAPI,
-         HANDLE,
-         NULL);
-GPA_WRAP(kernel32.dll,
-         OpenPrivateNamespaceW,
-         (LPVOID boundary_descriptor, LPCWSTR alias_prefix),
-         (boundary_descriptor, alias_prefix),
-         WINAPI,
-         HANDLE,
-         NULL);
-
-bool IsPrivateNamespaceAvailable();
-
-
-HRESULT PinModuleIntoProcess(const CString& module_name);
-
-// Creates a directory with default security.
-//   S_OK:    Created directory
-//   S_FALSE: Directory already existed
-//   E_FAIL:  Couldn't create
-HRESULT CreateDir(const TCHAR* dirname, LPSECURITY_ATTRIBUTES security_attr);
-
-// Gets the path for the specified special folder.
-HRESULT GetFolderPath(int csidl, CString* path);
-
-// Returns true if this directory name is 'safe' for deletion:
-//  - it doesn't contain ".."
-//  - it doesn't specify a drive root
-bool SafeDirectoryNameForDeletion(const TCHAR* dir_name);
-
-// Deletes a directory.
-HRESULT DeleteDirectory(const TCHAR* ir_name);
-
-// Deletes all files in a directory.
-HRESULT DeleteDirectoryFiles(const TCHAR* dir_name);
-
-// Deletes all files in a directory matching a wildcard.
-HRESULT DeleteWildcardFiles(const TCHAR* dir_name, const TCHAR* wildcard_name);
-
-// Deletes either a file or directory before or after reboot.
-HRESULT DeleteBeforeOrAfterReboot(const TCHAR* targetname);
-
-// Gets the size of all files in a directory.
-// Aborts counting if one of the maximum criteria is reached.
-HRESULT SafeGetDirectorySize(const TCHAR* dir_name,
-                             uint64* size,
-                             HANDLE shutdown_event,
-                             uint64 max_size,
-                             int max_depth,
-                             int max_file_count,
-                             int max_running_time_ms);
-
-// Gets size of all files in a directory.
-HRESULT GetDirectorySize(const TCHAR* dir_name, uint64* size);
-
-enum TimeCategory {
-  PAST = 0,  // older than 40 years from now
-  FUTURE,    // in the future by > 1 day
-  PRESENT,   // neither ANCIENT nor FUTURE
-};
-
-TimeCategory GetTimeCategory(time64 t);
-
-// Returns true if a given time is likely to be valid.
-bool IsValidTime(time64 t);
-
-// Gets a time64 value.
-// If the value is greater than the
-// max value, then SetValue will be called using the max_value.
-//
-//  Args:
-//   full_key_name:   the reg keyto read to get the time value.
-//   value_name:      the name for the reg key value to be read.
-//                    (may be NULL to get the default value)
-//   max_time:        the maximum value for the reg key.
-//   value:           the time value read.  May not be set on failure.
-//   limited_value:   true iff will be set if the value was
-//                    changed (and resaved). (NULL is allowable.)
-HRESULT GetLimitedTimeValue(const TCHAR* full_key_name,
-                            const TCHAR* value_name,
-                            time64 max_time,
-                            time64* value,
-                            bool* limited_value);
-
-// Gets a time64 value trying reg keys successively if there is a
-// failure in getting a value.
-//
-// Typically used when there is a user value and a default value if the
-// user has none.
-//
-//  Args:
-//   full_key_names:  a list of reg keys to try successively (starting
-//                    with index 0) to read to get the time value.  The
-//                    attempts stops as soon as there is a successful read or
-//                    when all keys have been tried.
-//   key_names_length: number of keys in full_key_names.
-//   value_name:      the name for the reg key value to be read.
-//                    (may be NULL to get the default value)
-//   max_time:        the maximum value for the reg key.
-//   value:           the time value read.  May not be set on failure.
-//   limited_value:   true iff will be set if the value was
-//                    changed (and resaved). (NULL is allowable.)
-HRESULT GetLimitedTimeValues(const TCHAR* full_key_names[],
-                             int key_names_length,
-                             const TCHAR* value_name,
-                             time64 max_time,
-                             time64* value,
-                             bool* limited_value);
-
-// Convert milliseconds to a relative time in units of 100ns, suitable for use
-// with waitable timers
-LARGE_INTEGER MSto100NSRelative(DWORD ms);
-
-// TODO(omaha): remove from public interface.
-inline void WINAPI NullAPCFunc(ULONG_PTR) {}
-
-// Forces rasman.dll load to avoid a crash in wininet.
-void EnsureRasmanLoaded();
-
-// Returns if the HRESULT argument is a COM error
-// TODO(omaha): use an ANONYMOUS_VARIABLE to avoid the situation in which the
-// macro gets called like RET_IF_FAILED(hr);
-// For now, use a quick fix hr -> __hr. Leading underscore names are not to be
-// used in application code.
-#define RET_IF_FAILED(x)    \
-    do {                    \
-      HRESULT __hr(x);      \
-      if (FAILED(__hr)) {   \
-        return __hr;        \
-      }                     \
-    } while (false)
-
-// return error if the first argument evaluates to false
-#define RET_IF_FALSE(x, err)  \
-  do {                        \
-    if (!(x)) {               \
-      return err;             \
-    }                         \
-  } while (false)
-
-// return false if the HRESULT argument is a COM error
-#define RET_FALSE_IF_FAILED(x)  \
-  do {                          \
-    if (FAILED(x)) {            \
-      return false;             \
-    }                           \
-  } while (false)
-
-// return true if the HRESULT argument is a COM error
-#define RET_TRUE_IF_FAILED(x)   \
-  do {                          \
-    if (FAILED(x)) {            \
-      return true;              \
-    }                           \
-  } while (false)
-
-// return if the HRESULT argument evaluates to FAILED - but also assert
-// if failed
-#define RET_IF_FAILED_ASSERT(x, msg) \
-    do {                             \
-      HRESULT hr(x);                 \
-      if (FAILED(hr)) {              \
-        ASSERT(false, msg);          \
-        return hr;                   \
-      }                              \
-    } while (false)
-
-
-// return if the HRESULT argument evaluates to FAILED - but also log an error
-// message if failed
-#define RET_IF_FAILED_LOG(x, cat, msg) \
-    do {                               \
-      HRESULT hr(x);                   \
-      if (FAILED(hr)) {                \
-        LC_LOG(cat, LEVEL_ERROR, msg); \
-        return hr;                     \
-      }                                \
-    } while (false)
-
-// return if the HRESULT argument evaluates to FAILED - but also REPORT an error
-// message if failed
-#define RET_IF_FAILED_REPORT(x, msg, n) \
-    do {                                \
-      HRESULT hr(x);                    \
-      if (FAILED(hr)) {                 \
-        REPORT(false, R_ERROR, msg, n); \
-        return hr;                      \
-      }                                 \
-    } while (false)
-
-// Initializes a POD to zero.
-// Using this function requires discipline. Don't use for types that have a
-// v-table or virtual bases.
-template <typename T>
-inline void SetZero(T& p) {   // NOLINT
-  // Guard against the easy mistake of
-  //    foo(int *p) { SetZero(p); } instead of
-  //                  SetZero(*p);
-  // which it should be.
-  STATIC_ASSERT(sizeof(p) != sizeof(void*));    // NOLINT
-
-  // A POD (plain old data) object has one of these data types:
-  // a fundamental type, union, struct, array,
-  // or class--with no constructor. PODs don't have virtual functions or
-  // virtual bases.
-
-  // Test to see if the type has constructors.
-  union CtorTest {
-      T t;
-      int i;
-  };
-
-  // TODO(omaha): There might be a way to test if the type has virtuals
-  // For now, if we zero a type with virtuals by mistake, it is going to crash
-  // predictable at run-time when the virtuals are called.
-
-  memset(&p, 0, sizeof(T));
-}
-
-inline void SecureSetZero(CString* p) {
-  ASSERT1(p);
-  if (!p->IsEmpty()) {
-    ::SecureZeroMemory(p->GetBufferSetLength(p->GetLength()),
-                       p->GetLength() * sizeof(TCHAR));
-    p->ReleaseBuffer();
-  }
-}
-
-inline void SecureSetZero(CComVariant* p) {
-  ASSERT1(p);
-  ASSERT1(V_VT(p) == VT_BSTR);
-  uint32 byte_len = ::SysStringByteLen(V_BSTR(p));
-  if (byte_len > 0) {
-    ::SecureZeroMemory(V_BSTR(p), byte_len);
-  }
-}
-
-// CreateForegroundParentWindowForUAC creates a WS_POPUP | WS_VISIBLE with zero
-// size, of the STATIC WNDCLASS. It uses the default running EXE module
-// handle for creation.
-//
-// A visible centered foreground window is needed as the parent in Windows 7 and
-// above, to allow the UAC prompt to come up in the foreground, centered.
-// Otherwise, the elevation prompt will be minimized on the taskbar. A zero size
-// window works. A plain vanilla WS_POPUP allows the window to be free of
-// adornments. WS_EX_TOOLWINDOW prevents the task bar from showing the
-// zero-sized window.
-//
-// Returns NULL on failure. Call ::GetLastError() to get extended error
-// information on failure.
-inline HWND CreateForegroundParentWindowForUAC() {
-  CWindow foreground_parent;
-  if (foreground_parent.Create(_T("STATIC"), NULL, NULL, NULL,
-                               WS_POPUP | WS_VISIBLE, WS_EX_TOOLWINDOW)) {
-    foreground_parent.CenterWindow(NULL);
-    ::SetForegroundWindow(foreground_parent);
-  }
-  return foreground_parent.Detach();
-}
-
-// TODO(omaha): move the definition and ShellExecuteExEnsureParent function
-// below into shell.h
-
-#if (NTDDI_VERSION < NTDDI_WINXPSP1)
-// This value is not defined in the header, but has no effect on older OSes.
-#define SEE_MASK_NOZONECHECKS      0x00800000
-#endif
-
-// ShellExecuteExEnsureParent is a wrapper around ::ShellExecuteEx.
-// It ensures that we always have a parent window. In elevation scenarios, we
-// need to use the HWND property to be acknowledged as a foreground application
-// on Windows Vista. Otherwise, the elevation prompt will appear minimized on
-// the taskbar The UAC elevation mechanism uses the HWND as part of determining
-// whether the elevation is a foreground elevation.
-//
-// A better place for this might be in the Process class. However, to
-// reduce dependencies for the stub, placing this in utils.
-//
-// Return values:
-//   ShellExecuteExEnsureParent returns TRUE on success, and FALSE on failure.
-//   Call ::GetLastError() to get the extended error information on failure.
-//
-// Args:
-//   shell_exec_info structure pointer, filled in, with an optional HWND.
-//   The structure is not validated. If the HWND is NULL, it creates one.
-bool ShellExecuteExEnsureParent(LPSHELLEXECUTEINFO shell_exec_info);
-
-//
-// Wait with a message loop.
-// Returns true if the wait completed successfully and false in case of an
-// error.
-//
-
-// Waits with a message loop until any of the synchronization objects is
-// signaled. Return the index of the object that satisfied the wait.
-bool WaitWithMessageLoopAny(const std::vector<HANDLE>& handles, uint32* pos);
-
-// Waits with message loop until all the synchronization objects are signaled.
-bool WaitWithMessageLoopAll(const std::vector<HANDLE>& handles);
-
-// Waits with message loop until the synchronization object is signaled.
-bool WaitWithMessageLoop(HANDLE h);
-
-// Waits with message loop for a certain period of time
-bool WaitWithMessageLoopTimed(DWORD ms);
-
-//
-// TODO(omaha): message handler classes should go in other module.
-//
-// Handles windows messages
-class MessageHandlerInterface {
- public:
-  virtual ~MessageHandlerInterface() {}
-  // Does the translate/dispatch for one window message
-  // msg is never NULL.
-  virtual void Process(MSG* msg) = 0;
-};
-
-// The simplest working implementation of a message handler.
-// It does TranslateMessage/DispatchMessage.
-class BasicMessageHandler : public MessageHandlerInterface {
- public:
-  BasicMessageHandler() {}
-  virtual void Process(MSG* msg);
- private:
-  DISALLOW_EVIL_CONSTRUCTORS(BasicMessageHandler);
-};
-
-// An internal detail (used to handle messages
-// and adjust the handles/cnt as needed).
-class MessageHandlerInternalInterface {
- public:
-  virtual ~MessageHandlerInternalInterface() {}
-  // Does the translate/dispatch for one window message
-  // msg is never NULL and may change the handles and cnt (which are
-  // never NULL).
-  virtual void Process(MSG* msg, const HANDLE** handles, uint32* cnt) = 0;
-};
-
-// The callback for MessageLoopInterface::RegisterWaitForSingleObject
-class WaitCallbackInterface {
- public:
-  virtual ~WaitCallbackInterface() {}
-  // Return false from this method to terminate the message loop.
-  virtual bool HandleSignaled(HANDLE handle) = 0;
-};
-
-// This is similar to a threadpool interface
-// but this is done on the main message loop instead.
-//
-// Important: These method calls are *not* threadsafe and
-// should only be done on the message loop thread.
-class MessageLoopInterface {
- public:
-  virtual ~MessageLoopInterface() {}
-  // sets-up a callback for when the handle becomes signaled
-  //   * the callback happens on the main thread.
-  //   * the handle/callback is removed when
-  //     the callback is made.
-  //   * If the handle becomes invalid while it is being waited on,
-  //     the message loop will exit.
-  //   * If the handle has already been registered,
-  //     then adding it again will essentially replace the
-  //     previous callback with the new one.
-  virtual bool RegisterWaitForSingleObject(HANDLE handle,
-                                           WaitCallbackInterface* callback) = 0;
-
-  // stops watching for the handle to be signaled
-  virtual bool UnregisterWait(HANDLE handle) = 0;
-};
-
-// An implementation of the MessageLoopInterface
-class MessageLoopWithWait : public MessageLoopInterface,
-                            private MessageHandlerInternalInterface {
- public:
-  MessageLoopWithWait();
-
-  // This method needs to be called before the "Process()"
-  // method or else "Process()" will crash.
-  //
-  // Args:
-  //    message_handler: handles any messages that occur
-  //       *Note* It is the callers responsibility to keep
-  //       message_handler around while this class exists
-  //       and the *caller* should free message_handler
-  //       after that.
-  void set_message_handler(MessageHandlerInterface* message_handler);
-
-  // sets-up a callback for when the handle becomes signaled
-  virtual bool RegisterWaitForSingleObject(HANDLE handle,
-                                           WaitCallbackInterface* callback);
-
-  // stops watching for the handle to be signaled
-  virtual bool UnregisterWait(HANDLE handle);
-
-  // The message loop and handle callback routine
-  HRESULT Process();
-
- private:
-  // Handles one messgae and adjus tthe handles and cnt as appropriate after
-  // handling the message
-  virtual void Process(MSG* msg, const HANDLE** handles, uint32* cnt);
-
-  void RemoveHandleAt(uint32 pos);
-
-  MessageHandlerInterface* message_handler_;
-
-  // Handles that are checked for being signaled.
-  std::vector<HANDLE> callback_handles_;
-
-  // What to call when a handle is signaled.
-  std::vector<WaitCallbackInterface*> callbacks_;
-  DISALLOW_EVIL_CONSTRUCTORS(MessageLoopWithWait);
-};
-
-// Calls an entry point that may be exposed from a DLL
-//   It is an error if the DLL is missing or can't be loaded
-//   If the entry point is missing the error returned is
-//   ERROR_INVALID_FUNCTION (as an HRESULT).
-//   Otherwise, if the function is called successfully then
-//   CallEntryPoint0 return S_OK and the result parameter is set to the
-//   return value from the function call.
-HRESULT CallEntryPoint0(const TCHAR* dll_path,
-                        const char* function_name,
-                        HRESULT* result);
-
-// (Un)Registers a COM DLL with the system.  Returns S_FALSE if entry
-// point missing (so that it you can call (Un)RegisterDll on any DLL
-// without worrying whether the DLL is actually a COM server or not).
-HRESULT RegisterDll(const TCHAR* dll_path);
-HRESULT UnregisterDll(const TCHAR* dll_path);
-
-// (Un)Registers a COM Local Server with the system.
-HRESULT RegisterServer(const TCHAR* exe_path);
-HRESULT UnregisterServer(const TCHAR* exe_path);
-HRESULT RegisterOrUnregisterExe(const TCHAR* exe_path, const TCHAR* cmd_line);
-
-// (Un)Registers a COM Service with the system.
-HRESULT RegisterService(const TCHAR* exe_path);
-HRESULT UnregisterService(const TCHAR* exe_path);
-
-// Starts a service.
-HRESULT RunService(const TCHAR* service_name);
-
-// Read an entire file into a memory buffer. Use this function when you need
-// exclusive access to the file.
-// Returns MEM_E_INVALID_SIZE if the file size is larger than max_len (unless
-// max_len == 0, in which case it is ignored)
-HRESULT ReadEntireFile(const TCHAR* filepath,
-                       uint32 max_len,
-                       std::vector<byte>* buffer_out);
-
-// Allows specifying a sharing mode such as FILE_SHARE_READ. Otherwise,
-// this is identical to ReadEntireFile.
-HRESULT ReadEntireFileShareMode(const TCHAR* filepath,
-                                uint32 max_len,
-                                DWORD share_mode,
-                                std::vector<byte>* buffer_out);
-
-// Writes an entire file from a memory buffer
-HRESULT WriteEntireFile(const TCHAR * filepath,
-                        const std::vector<byte>& buffer_in);
-
-// Conversions between a byte stream and a std::string
-HRESULT BufferToString(const std::vector<byte>& buffer_in, CStringA* str_out);
-HRESULT BufferToString(const std::vector<byte>& buffer_in, CString* str_out);
-HRESULT StringToBuffer(const CStringA& str_in, std::vector<byte>* buffer_out);
-HRESULT StringToBuffer(const CString& str_in, std::vector<byte>* buffer_out);
-
-// Splits a "full regkey name" into a key name part and a value name part.
-// Handles "(default)" as a value name.  Treats a trailing "/" as "(default)".
-HRESULT RegSplitKeyvalueName(const CString& keyvalue_name,
-                             CString* key_name,
-                             CString* value_name);
-
-// Expands string with embedded special variables which are enclosed
-// in '%' pair. For example, "%PROGRAMFILES%\Google" expands to
-// "C:\Program Files\Google".
-// If any of the embedded variable can not be expanded, we will leave it intact
-// and return HRESULT_FROM_WIN32(ERROR_NOT_FOUND)
-HRESULT ExpandEnvLikeStrings(const TCHAR* src,
-                             const std::map<CString, CString>& keywords,
-                             CString* dest);
-
-// Returns true if the path represents a registry path.
-bool IsRegistryPath(const TCHAR* path);
-
-// Returns true if the path is a URL.
-bool IsUrl(const TCHAR* path);
-
-// Converts GUID to string.
-CString GuidToString(const GUID& guid);
-
-// Converts string to GUID.
-GUID StringToGuid(const CString& str);
-
-// Converts a variant containing a list of strings.
-void VariantToStringList(VARIANT var, std::vector<CString>* list);
-
-// Appends two registry key paths, takes care of extra separators in the
-// beginning or end of the key.
-CString AppendRegKeyPath(const CString& one, const CString& two);
-CString AppendRegKeyPath(const CString& one,
-                         const CString& two,
-                         const CString& three);
-
-// Returns the list of user keys that are present within the HKEY_USERS key
-// the method only returns the keys of the users and takes care of,
-// removing the well known sids. The returned values are the complete values
-// from the root of the registry
-HRESULT GetUserKeysFromHkeyUsers(std::vector<CString>* key_names);
-
-// Use when a function should be able to
-// be replaced with another implementation. Usually,
-// this is done for testing code only.
-//
-// Typical usage:
-//
-//  typedef bool BoolPreferenceFunctionType();
-//  CallInterceptor<BoolPreferenceFunctionType> should_send_stats_interceptor;
-//  BoolPreferenceFunctionType* ReplaceShouldSendStatsFunction(
-//      BoolPreferenceFunctionType* replacement) {
-//    return should_send_stats_interceptor.ReplaceFunction(replacement);
-//  }
-template <typename R>
-class CallInterceptor {
- public:
-  CallInterceptor() {
-    interceptor_ = NULL;
-  }
-
-  R* ReplaceFunction(R* replacement) {
-    R* old = interceptor_;
-    interceptor_ = replacement;
-    return old;
-  }
-
-  R* interceptor() {
-    return interceptor_;
-  }
-
- private:
-  R* interceptor_;
-  DISALLOW_EVIL_CONSTRUCTORS(CallInterceptor);
-};
-
-// Gets a handle of the current process. The handle is a real handle
-// and the caller must close it
-HRESULT GetCurrentProcessHandle(HANDLE* handle);
-
-// Helper class for an ATL module that registers a custom AccessPermission
-// to allow local calls from interactive users and the system account.
-// Derive from this class as well as CAtlModuleT (or a derivative).
-// Override RegisterAppId() and UnregisterAppId(), and delegate to the
-// corresponding functions in this class.
-template <class T>
-class LocalCallAccessPermissionHelper {
- public:
-  HRESULT RegisterAppId() throw() {
-    // Local call permissions allowed for Interactive Users and Local System
-    static LPCTSTR ALLOW_LOCAL_CALL_SDDL =
-        _T("O:BAG:BAD:(A;;0x3;;;IU)(A;;0x3;;;SY)");
-
-    UTIL_LOG(L1, (_T("[LocalCallAccessPermissionHelper::RegisterAppId]")));
-
-    // First call the base ATL module implementation, so the AppId is registered
-    RET_IF_FAILED(T::UpdateRegistryAppId(TRUE));
-
-    // Next, write the AccessPermission value
-    RegKey key_app_id;
-    RET_IF_FAILED(key_app_id.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_WRITE));
-
-    RegKey key;
-    RET_IF_FAILED(key.Create(key_app_id.Key(), T::GetAppIdT()));
-    CSecurityDesc sd;
-    RET_IF_FALSE(sd.FromString(ALLOW_LOCAL_CALL_SDDL), HRESULTFromLastError());
-    RET_IF_FAILED(key.SetValue(
-        _T("AccessPermission"),
-        reinterpret_cast<const byte*>(sd.GetPSECURITY_DESCRIPTOR()),
-        sd.GetLength()));
-
-    UTIL_LOG(L1, (_T("[LocalCallAccessPermissionHelper::RegisterAppId]")
-                  _T("[succeeded]")));
-    return S_OK;
-  }
-
-  HRESULT UnregisterAppId() throw() {
-    // First remove the AccesPermission entry.
-    RegKey key_app_id;
-    RET_IF_FAILED(key_app_id.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_WRITE));
-
-    RegKey key;
-    RET_IF_FAILED(key.Open(key_app_id.Key(), T::GetAppIdT(), KEY_WRITE));
-    VERIFY1(SUCCEEDED(key.DeleteValue(_T("AccessPermission"))));
-
-    // Now, call the base ATL module implementation to unregister the AppId
-    RET_IF_FAILED(T::UpdateRegistryAppId(FALSE));
-
-    UTIL_LOG(L1, (_T("[LocalCallAccessPermissionHelper::UnregisterAppId'")
-                  _T("[succeeded]")));
-    return S_OK;
-  }
-};
-
-// Returns true if the argument is a guid.
-inline bool IsGuid(const TCHAR* s) {
-  if (!s) return false;
-  GUID guid = {0};
-  return SUCCEEDED(::CLSIDFromString(const_cast<TCHAR*>(s), &guid));
-}
-
-inline bool IsLocalSystemSid(const TCHAR* sid) {
-  ASSERT1(sid);
-  return _tcsicmp(sid, kLocalSystemSid) == 0;
-}
-
-// Fills a buffer with cryptographically random bytes.
-bool GenRandom(void* buffer, size_t buffer_length);
-
-// Deletes an object. The functor is useful in for_each algorithms.
-struct DeleteFun {
-  template <class T> void operator()(T ptr) { delete ptr; }
-};
-
-// Sets or clears the specified value in the Run key to the specified command.
-HRESULT ConfigureRunAtStartup(const CString& root_key_name,
-                              const CString& run_value_name,
-                              const CString& command,
-                              bool install);
-
-// Cracks a command line and returns the program name, which is the first
-// whitespace separated token.
-HRESULT GetExePathFromCommandLine(const TCHAR* command_line,
-                                  CString* exe_path);
-
-// Waits for MSI to complete, if MSI is busy installing or uninstalling apps.
-HRESULT WaitForMSIExecute(int timeout_ms);
-
-// Returns the value of the specified environment variable.
-CString GetEnvironmentVariableAsString(const TCHAR* name);
-
-// Returns true if the OS is installing (e.g., Audit Mode at an OEM factory).
-// NOTE: This is unreliable on Windows Vista and later. Some computers remain in
-// one of the incomplete states even after OOBE. See http://b/1690617.
-bool IsWindowsInstalling();
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_UTILS_H__
diff --git a/common/utils_unittest.cc b/common/utils_unittest.cc
deleted file mode 100644
index 60ab0a8..0000000
--- a/common/utils_unittest.cc
+++ /dev/null
@@ -1,591 +0,0 @@
-// Copyright 2003-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <ATLComTime.h>
-#include <atltypes.h>
-#include <atlwin.h>
-#include <map>
-#include <vector>
-#include "omaha/common/app_util.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/dynamic_link_kernel32.h"
-#include "omaha/common/file.h"
-#include "omaha/common/module_utils.h"
-#include "omaha/common/path.h"
-#include "omaha/common/shell.h"
-#include "omaha/common/string.h"
-#include "omaha/common/time.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/system_info.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-// Make sure that the time functions work.
-TEST(UtilsTest, Time) {
-  // TODO(omaha): - add a test from string to time and back again.
-  // Further test the time converters.
-  time64 now = GetCurrent100NSTime();
-  ASSERT_TRUE(StringToTime(TimeToString(now)) == now);
-
-  // Test GetTimeCategory.
-  ASSERT_EQ(PAST,
-            GetTimeCategory(static_cast<time64>(0)));
-  ASSERT_EQ(PRESENT,
-            GetTimeCategory(static_cast<time64>(now)));
-  ASSERT_EQ(PRESENT,
-            GetTimeCategory(static_cast<time64>(now - kDaysTo100ns)));
-  ASSERT_EQ(PRESENT,
-            GetTimeCategory(static_cast<time64>(now - 365 * kDaysTo100ns)));
-  // A little bit in the future is also considered present.
-  ASSERT_EQ(PRESENT,
-            GetTimeCategory(static_cast<time64>(now + kDaysTo100ns)));
-  ASSERT_EQ(PRESENT,
-            GetTimeCategory(static_cast<time64>(
-                now - 30 * 365 * kDaysTo100ns)));
-  ASSERT_EQ(PAST,
-            GetTimeCategory(static_cast<time64>(
-                now - 50 * 365 * kDaysTo100ns)));
-  ASSERT_EQ(FUTURE,
-            GetTimeCategory(static_cast<time64>(now + kDaysTo100ns * 6)));
-  ASSERT_EQ(FUTURE,
-            GetTimeCategory(static_cast<time64>(now + 365 * kDaysTo100ns)));
-
-  // Test IsValidTime.
-  ASSERT_FALSE(IsValidTime(static_cast<time64>(0)));
-  ASSERT_TRUE(IsValidTime(static_cast<time64>(now)));
-  ASSERT_TRUE(IsValidTime(static_cast<time64>(now - 365 * kDaysTo100ns)));
-  ASSERT_TRUE(IsValidTime(static_cast<time64>(now - 10 * 365 * kDaysTo100ns)));
-  ASSERT_TRUE(IsValidTime(static_cast<time64>(now + kDaysTo100ns)));
-  ASSERT_FALSE(IsValidTime(static_cast<time64>(now - 50 * 365 * kDaysTo100ns)));
-  ASSERT_FALSE(IsValidTime(static_cast<time64>(now + 50 * 365 * kDaysTo100ns)));
-  ASSERT_FALSE(IsValidTime(static_cast<time64>(now + kDaysTo100ns * 6)));
-}
-
-TEST(UtilsTest, GetFolderPath_Success) {
-  CString path;
-  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &path));
-  BOOL isWow64 = FALSE;
-  EXPECT_SUCCEEDED(Kernel32::IsWow64Process(GetCurrentProcess(), &isWow64));
-  CString expected_path = isWow64 ?
-      _T("C:\\Program Files (x86)") : _T("C:\\Program Files");
-  EXPECT_STREQ(expected_path, path);
-}
-
-TEST(UtilsTest, GetFolderPath_Errors) {
-  CString path;
-  EXPECT_EQ(E_INVALIDARG, GetFolderPath(0x7fff, &path));
-  EXPECT_TRUE(path.IsEmpty());
-  EXPECT_EQ(E_INVALIDARG, GetFolderPath(CSIDL_PROGRAM_FILES, NULL));
-}
-
-TEST(UtilsTest, CallEntryPoint0) {
-  HRESULT hr(E_FAIL);
-  ASSERT_FAILED(CallEntryPoint0(L"random-nonsense.dll", "foobar", &hr));
-}
-
-TEST(UtilsTest, ReadEntireFile) {
-  TCHAR directory[MAX_PATH] = {0};
-  ASSERT_TRUE(GetModuleDirectory(NULL, directory));
-  CString file_name;
-  file_name.Format(_T("%s\\unittest_support\\declaration.txt"), directory);
-
-  std::vector<byte> buffer;
-  ASSERT_FAILED(ReadEntireFile(L"C:\\F00Bar\\ImaginaryFile", 0, &buffer));
-
-  ASSERT_SUCCEEDED(ReadEntireFile(file_name, 0, &buffer));
-  ASSERT_EQ(9405, buffer.size());
-  buffer.resize(0);
-  ASSERT_FAILED(ReadEntireFile(L"C:\\WINDOWS\\Greenstone.bmp", 1000, &buffer));
-}
-
-// TODO(omaha): Need a test for WriteEntireFile
-// TEST(UtilsTest, WriteEntireFile) {
-// }
-
-TEST(UtilsTest, RegSplitKeyvalueName) {
-  CString key_name, value_name;
-  ASSERT_SUCCEEDED(RegSplitKeyvalueName(CString(L"HKLM\\Foo\\"),
-                                        &key_name,
-                                        &value_name));
-  ASSERT_STREQ(key_name, L"HKLM\\Foo");
-  ASSERT_TRUE(value_name.IsEmpty());
-
-  ASSERT_SUCCEEDED(RegSplitKeyvalueName(CString(L"HKLM\\Foo\\(default)"),
-                                        &key_name,
-                                        &value_name));
-  ASSERT_STREQ(key_name, L"HKLM\\Foo");
-  ASSERT_TRUE(value_name.IsEmpty());
-
-  ASSERT_SUCCEEDED(RegSplitKeyvalueName(CString(L"HKLM\\Foo\\Bar"),
-                                        &key_name,
-                                        &value_name));
-  ASSERT_STREQ(key_name, L"HKLM\\Foo");
-  ASSERT_STREQ(value_name, L"Bar");
-}
-
-TEST(UtilsTest, ExpandEnvLikeStrings) {
-  std::map<CString, CString> mapping;
-  ASSERT_SUCCEEDED(Shell::GetSpecialFolderKeywordsMapping(&mapping));
-
-  CString out;
-  ASSERT_SUCCEEDED(ExpandEnvLikeStrings(
-      L"Foo%WINDOWS%Bar%SYSTEM%Zebra%WINDOWS%%SYSTEM%", mapping, &out));
-
-  // This should work, but CmpHelperSTRCASEEQ is not overloaded for wchars.
-  // ASSERT_STRCASEEQ(out, L"FooC:\\WINDOWSBarC:\\WINDOWS\\system32Zebra"
-  //                       L"C:\\WINDOWSC:\\WINDOWS\\system32");
-  ASSERT_EQ(out.CompareNoCase(L"FooC:\\WINDOWSBarC:\\WINDOWS\\system32Zebra"
-                              L"C:\\WINDOWSC:\\WINDOWS\\system32"),
-            0);
-  ASSERT_FAILED(ExpandEnvLikeStrings(L"Foo%WINDOWS%%BAR%Zebra", mapping, &out));
-}
-
-TEST(UtilsTest, GetCurrentProcessHandle) {
-  scoped_process proc;
-  ASSERT_SUCCEEDED(GetCurrentProcessHandle(address(proc)));
-  ASSERT_TRUE(valid(proc));
-}
-
-TEST(UtilsTest, IsGuid) {
-  ASSERT_FALSE(IsGuid(NULL));
-  ASSERT_FALSE(IsGuid(_T("")));
-  ASSERT_FALSE(IsGuid(_T("{}")));
-  ASSERT_FALSE(IsGuid(_T("a")));
-  ASSERT_FALSE(IsGuid(_T("CA3045BFA6B14fb8A0EFA615CEFE452C")));
-
-  // Missing {}
-  ASSERT_FALSE(IsGuid(_T("CA3045BF-A6B1-4fb8-A0EF-A615CEFE452C")));
-
-  // Invalid char X
-  ASSERT_FALSE(IsGuid(_T("{XA3045BF-A6B1-4fb8-A0EF-A615CEFE452C}")));
-
-  // Invalid binary char 0x200
-  ASSERT_FALSE(IsGuid(_T("{\0x200a3045bf-a6b1-4fb8-a0ef-a615cefe452c}")));
-
-  // Missing -
-  ASSERT_FALSE(IsGuid(_T("{CA3045BFA6B14fb8A0EFA615CEFE452C}")));
-
-  // Double quotes
-  ASSERT_FALSE(IsGuid(_T("\"{ca3045bf-a6b1-4fb8-a0ef-a615cefe452c}\"")));
-
-  ASSERT_TRUE(IsGuid(_T("{00000000-0000-0000-0000-000000000000}")));
-  ASSERT_TRUE(IsGuid(_T("{CA3045BF-A6B1-4fb8-A0EF-A615CEFE452C}")));
-  ASSERT_TRUE(IsGuid(_T("{ca3045bf-a6b1-4fb8-a0ef-a615cefe452c}")));
-}
-
-TEST(UtilsTest, VersionFromString_ValidVersion) {
-  EXPECT_EQ(MAKEDLLVERULL(42, 1, 21, 12345),
-            VersionFromString(_T("42.1.21.12345")));
-}
-
-TEST(UtilsTest, VersionFromString_VersionZero) {
-  EXPECT_EQ(0, VersionFromString(_T("0.0.0.0")));
-}
-
-TEST(UtilsTest, VersionFromString_VersionUpperLimits) {
-  EXPECT_EQ(MAKEDLLVERULL(0xffff, 0xffff, 0xffff, 0xffff),
-            VersionFromString(_T("65535.65535.65535.65535")));
-  EXPECT_EQ(0, VersionFromString(_T("65536.65536.65536.65536")));
-  EXPECT_EQ(0, VersionFromString(_T("1.2.65536.65536")));
-}
-
-TEST(UtilsTest, VersionFromString_IntegerOverflow) {
-  EXPECT_EQ(0, VersionFromString(_T("1.2.3.4294967296")));
-}
-
-TEST(UtilsTest, VersionFromString_NegativeVersion) {
-  EXPECT_EQ(0, VersionFromString(_T("1.2.3.-22")));
-}
-
-TEST(UtilsTest, VersionFromString_TooFewElements) {
-  EXPECT_EQ(0, VersionFromString(_T("1.1.1")));
-}
-
-TEST(UtilsTest, VersionFromString_ExtraPeriod) {
-  EXPECT_EQ(0, VersionFromString(_T("1.1.2.3.")));
-}
-
-TEST(UtilsTest, VersionFromString_TooManyElements) {
-  EXPECT_EQ(0, VersionFromString(_T("1.1.2.3.4")));
-}
-
-TEST(UtilsTest, VersionFromString_Char) {
-  EXPECT_EQ(0, VersionFromString(_T("1.B.3.4")));
-  EXPECT_EQ(0, VersionFromString(_T("1.2.3.B")));
-  EXPECT_EQ(0, VersionFromString(_T("1.2.3.9B")));
-}
-
-TEST(UtilsTest, StringFromVersion_ValidVersion) {
-  EXPECT_STREQ(_T("42.1.21.12345"),
-               StringFromVersion(MAKEDLLVERULL(42, 1, 21, 12345)));
-}
-
-TEST(UtilsTest, StringFromVersion_VersionZero) {
-  EXPECT_STREQ(_T("0.0.0.0"), StringFromVersion(0));
-}
-
-TEST(UtilsTest, StringFromVersion_VersionUpperLimits) {
-  EXPECT_STREQ(
-      _T("65535.65535.65535.65535"),
-      StringFromVersion(MAKEDLLVERULL(0xffff, 0xffff, 0xffff, 0xffff)));
-}
-
-TEST(UtilsTest, IsLocalSystemSid) {
-  EXPECT_TRUE(IsLocalSystemSid(kLocalSystemSid));
-  EXPECT_TRUE(IsLocalSystemSid(_T("S-1-5-18")));
-  EXPECT_TRUE(IsLocalSystemSid(_T("s-1-5-18")));
-
-  EXPECT_FALSE(IsLocalSystemSid(_T("")));
-  EXPECT_FALSE(IsLocalSystemSid(_T("S-1-5-17")));
-}
-
-// There is a very small probability the test could fail.
-TEST(UtilsTest, GenRandom) {
-  int random_int = 0;
-  EXPECT_TRUE(GenRandom(&random_int, sizeof(random_int)));
-  EXPECT_NE(random_int, 0);
-
-  int another_random_int = 0;
-  EXPECT_TRUE(GenRandom(&another_random_int, sizeof(another_random_int)));
-  EXPECT_NE(another_random_int, 0);
-
-  EXPECT_NE(random_int, another_random_int);
-}
-
-// Counts instances of the class.
-class Counter {
- public:
-  Counter() {
-    ++instance_count_;
-  }
-  ~Counter() {
-    --instance_count_;
-  }
-  static int instance_count() { return instance_count_; }
- private:
-  static int instance_count_;
-  DISALLOW_EVIL_CONSTRUCTORS(Counter);
-};
-
-int Counter::instance_count_ = 0;
-
-// Checks if the functor is actually calling the destructor of the type.
-TEST(UtilsTest, DeleteFun) {
-  EXPECT_EQ(Counter::instance_count(), 0);
-  Counter* counter = new Counter;
-  EXPECT_EQ(Counter::instance_count(), 1);
-  DeleteFun().operator()(counter);
-  EXPECT_EQ(Counter::instance_count(), 0);
-
-  // Checks if the template can be instantiated for some common built in types.
-  int* pointer_int = NULL;
-  DeleteFun().operator()(pointer_int);
-
-  const char* pointer_char = NULL;
-  DeleteFun().operator()(pointer_char);
-}
-
-TEST(UtilsTest, IsUserLoggedOn) {
-  bool is_logged_on(false);
-  ASSERT_HRESULT_SUCCEEDED(IsUserLoggedOn(&is_logged_on));
-  ASSERT_TRUE(is_logged_on);
-}
-
-TEST(UtilsTest, IsClickOnceDisabled) {
-  EXPECT_FALSE(IsClickOnceDisabled());
-}
-
-TEST(UtilsTest, ConfigureRunAtStartup) {
-  const TCHAR kRunKeyPath[] =
-      _T("HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run");
-
-  RegKey::DeleteKey(kRegistryHiveOverrideRoot, true);
-  OverrideRegistryHives(kRegistryHiveOverrideRoot);
-
-  EXPECT_FALSE(RegKey::HasKey(kRunKeyPath));
-
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            ConfigureRunAtStartup(USER_KEY_NAME, _T("FooApp"),
-                                  _T("\"foo.exe\""), false));
-  EXPECT_FALSE(RegKey::HasKey(kRunKeyPath));
-
-  EXPECT_SUCCEEDED(ConfigureRunAtStartup(USER_KEY_NAME, _T("FooApp"),
-                                         _T("\"C:\\foo.exe\" /x"), true));
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kRunKeyPath, _T("FooApp"), &value));
-  EXPECT_STREQ(_T("\"C:\\foo.exe\" /x"), value);
-
-  EXPECT_SUCCEEDED(ConfigureRunAtStartup(USER_KEY_NAME, _T("FooApp"),
-                                         _T("\"foo.exe\""), false));
-  EXPECT_FALSE(RegKey::HasValue(kRunKeyPath, _T("FooApp")));
-  EXPECT_TRUE(RegKey::HasKey(kRunKeyPath));
-
-  RestoreRegistryHives();
-  EXPECT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
-}
-
-TEST(UtilsTest, ValidPath) {
-  CString cmd_line =
-      _T("\"C:\\Program Files\\Internet Explorer\\iexplore.exe\" -nohome");
-  CString exe_path;
-  EXPECT_SUCCEEDED(GetExePathFromCommandLine(cmd_line, &exe_path));
-  EXPECT_STREQ(_T("C:\\Program Files\\Internet Explorer\\iexplore.exe"),
-               exe_path);
-}
-
-TEST(UtilsTest, InvalidPath) {
-  CString cmd_line = _T("");
-  CString exe_path;
-  EXPECT_FAILED(GetExePathFromCommandLine(cmd_line, &exe_path));
-  EXPECT_TRUE(exe_path.IsEmpty());
-}
-
-TEST(UtilsTest, PinModuleIntoProcess) {
-  const TCHAR module_name[] = _T("icmp.dll");
-  const void* kNullModule = NULL;
-
-  // The module should not be loaded at this time.
-  EXPECT_EQ(kNullModule, ::GetModuleHandle(module_name));
-
-  // Loads and unloads the module.
-  {
-    scoped_library module(::LoadLibrary(module_name));
-    EXPECT_TRUE(module);
-    EXPECT_NE(kNullModule, ::GetModuleHandle(module_name));
-  }
-  EXPECT_EQ(kNullModule, ::GetModuleHandle(module_name));
-
-  // Loads, pins, and unloads the module.
-  {
-    scoped_library module(::LoadLibrary(module_name));
-    EXPECT_TRUE(module);
-    EXPECT_NE(kNullModule, ::GetModuleHandle(module_name));
-    PinModuleIntoProcess(module_name);
-  }
-  EXPECT_NE(kNullModule, ::GetModuleHandle(module_name));
-}
-
-// Assumes Windows is installed on the C: drive.
-TEST(UtilsTest, GetEnvironmentVariableAsString) {
-  EXPECT_STREQ(_T("C:"), GetEnvironmentVariableAsString(_T("SystemDrive")));
-  EXPECT_STREQ(_T("Windows_NT"), GetEnvironmentVariableAsString(_T("OS")));
-  EXPECT_STREQ(_T(""), GetEnvironmentVariableAsString(_T("FOO")));
-}
-
-TEST(UtilsTest, IsWindowsInstalling_Normal) {
-  EXPECT_FALSE(IsWindowsInstalling());
-}
-
-TEST(UtilsTest, IsWindowsInstalling_Installing_Vista_InvalidValues) {
-  if (!vista_util::IsVistaOrLater()) {
-    return;
-  }
-
-  RegKey::DeleteKey(kRegistryHiveOverrideRoot, true);
-  OverrideRegistryHives(kRegistryHiveOverrideRoot);
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      _T("")));
-  EXPECT_FALSE(IsWindowsInstalling());
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      _T("foo")));
-  EXPECT_FALSE(IsWindowsInstalling());
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      static_cast<DWORD>(1)));
-  ExpectAsserts expect_asserts;  // RegKey asserts because value type is wrong.
-  EXPECT_FALSE(IsWindowsInstalling());
-
-  RestoreRegistryHives();
-  EXPECT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
-}
-
-TEST(UtilsTest, IsWindowsInstalling_Installing_Vista_ValidStates) {
-  if (!vista_util::IsVistaOrLater()) {
-    return;
-  }
-
-  RegKey::DeleteKey(kRegistryHiveOverrideRoot, true);
-  OverrideRegistryHives(kRegistryHiveOverrideRoot);
-
-  // These states return false in the original implementation.
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      _T("IMAGE_STATE_COMPLETE")));
-  EXPECT_FALSE(IsWindowsInstalling());
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      _T("IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE")));
-  EXPECT_FALSE(IsWindowsInstalling());
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      _T("IMAGE_STATE_SPECIALIZE_RESEAL_TO_OOBE")));
-  EXPECT_FALSE(IsWindowsInstalling());
-
-  // These states are specified in the original implementation.
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      _T("IMAGE_STATE_UNDEPLOYABLE")));
-  EXPECT_TRUE(IsWindowsInstalling());
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      _T("IMAGE_STATE_GENERALIZE_RESEAL_TO_AUDIT")));
-  EXPECT_TRUE(IsWindowsInstalling());
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      _T("IMAGE_STATE_SPECIALIZE_RESEAL_TO_AUDIT")));
-  EXPECT_TRUE(IsWindowsInstalling());
-
-  RestoreRegistryHives();
-  EXPECT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
-}
-
-TEST(UtilsTest, AddAllowedAce) {
-  CString test_file_path = ConcatenatePath(
-      app_util::GetCurrentModuleDirectory(), _T("TestAddAllowedAce.exe"));
-  EXPECT_SUCCEEDED(File::Remove(test_file_path));
-
-  EXPECT_SUCCEEDED(File::Copy(
-      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                      _T("GoogleUpdate.exe")),
-      test_file_path,
-      false));
-
-  CDacl dacl;
-  EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl));
-  const int original_ace_count = dacl.GetAceCount();
-
-  EXPECT_SUCCEEDED(AddAllowedAce(test_file_path,
-                                 SE_FILE_OBJECT,
-                                 Sids::Dialup(),
-                                 FILE_GENERIC_READ,
-                                 0));
-
-  dacl.SetEmpty();
-  EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl));
-  EXPECT_EQ(original_ace_count + 1, dacl.GetAceCount());
-
-  // Add the same access. No ACE is added.
-  EXPECT_SUCCEEDED(AddAllowedAce(test_file_path,
-                                 SE_FILE_OBJECT,
-                                 Sids::Dialup(),
-                                 FILE_GENERIC_READ,
-                                 0));
-  dacl.SetEmpty();
-  EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl));
-  EXPECT_EQ(original_ace_count + 1, dacl.GetAceCount());
-
-  // Add a subset of the existing access. No ACE is added.
-  EXPECT_EQ(FILE_READ_ATTRIBUTES, FILE_GENERIC_READ & FILE_READ_ATTRIBUTES);
-  EXPECT_SUCCEEDED(AddAllowedAce(test_file_path,
-                                 SE_FILE_OBJECT,
-                                 Sids::Dialup(),
-                                 FILE_READ_ATTRIBUTES,
-                                 0));
-  dacl.SetEmpty();
-  EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl));
-  EXPECT_EQ(original_ace_count + 1, dacl.GetAceCount());
-
-  // Add more access. An ACE is added.
-  EXPECT_SUCCEEDED(AddAllowedAce(test_file_path,
-                                 SE_FILE_OBJECT,
-                                 Sids::Dialup(),
-                                 FILE_ALL_ACCESS,
-                                 0));
-  dacl.SetEmpty();
-  EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl));
-  EXPECT_EQ(original_ace_count + 2, dacl.GetAceCount());
-
-  // TODO(omaha): An assert occurs because the ACE flags are being used on a
-  // file object. Add a new test to use a registry key.
-  ExpectAsserts expect_asserts;
-
-  // Different ACE flags. An ACE is added.
-  const BYTE kTestAce = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
-  const BYTE kTestAceSubset = CONTAINER_INHERIT_ACE;
-  EXPECT_SUCCEEDED(AddAllowedAce(test_file_path,
-                                 SE_FILE_OBJECT,
-                                 Sids::Dialup(),
-                                 FILE_ALL_ACCESS,
-                                 kTestAce));
-  dacl.SetEmpty();
-  EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl));
-  EXPECT_EQ(original_ace_count + 3, dacl.GetAceCount());
-
-  // Subset of existing ACE flags. An ACE is added because flags must be exact.
-  EXPECT_SUCCEEDED(AddAllowedAce(test_file_path,
-                                 SE_FILE_OBJECT,
-                                 Sids::Dialup(),
-                                 FILE_ALL_ACCESS,
-                                 kTestAceSubset));
-  dacl.SetEmpty();
-  EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl));
-  EXPECT_EQ(original_ace_count + 4, dacl.GetAceCount());
-
-  // Same flags. An ACE should not be added because all values match.
-  // TODO(omaha): This does not work, possibly because the object is a file.
-  // Try the test using a registry key.
-  EXPECT_SUCCEEDED(AddAllowedAce(test_file_path,
-                                 SE_FILE_OBJECT,
-                                 Sids::Dialup(),
-                                 FILE_ALL_ACCESS,
-                                 kTestAceSubset));
-  dacl.SetEmpty();
-  EXPECT_TRUE(AtlGetDacl(test_file_path, SE_FILE_OBJECT, &dacl));
-  EXPECT_EQ(original_ace_count + 5, dacl.GetAceCount());
-
-  EXPECT_SUCCEEDED(File::Remove(test_file_path));
-}
-
-TEST(UtilsTest, CreateForegroundParentWindowForUAC) {
-  CWindow foreground_parent;
-  foreground_parent.Attach(CreateForegroundParentWindowForUAC());
-  EXPECT_TRUE(foreground_parent.IsWindow());
-  EXPECT_TRUE(foreground_parent.IsWindowVisible());
-
-  CRect foreground_rect;
-  EXPECT_TRUE(foreground_parent.GetWindowRect(&foreground_rect));
-  EXPECT_EQ(0, foreground_rect.Width());
-  EXPECT_EQ(0, foreground_rect.Height());
-
-  EXPECT_TRUE((WS_POPUP | WS_VISIBLE) & foreground_parent.GetStyle());
-  EXPECT_TRUE(WS_EX_TOOLWINDOW & foreground_parent.GetExStyle());
-
-  EXPECT_TRUE(foreground_parent.DestroyWindow());
-}
-
-}  // namespace omaha
-
diff --git a/common/vista_utils.cc b/common/vista_utils.cc
deleted file mode 100644
index f052983..0000000
--- a/common/vista_utils.cc
+++ /dev/null
@@ -1,482 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/vista_utils.h"
-
-#include <vector>
-#include "base/scoped_ptr.h"
-#include "omaha/common/const_utils.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/proc_utils.h"
-#include "omaha/common/process.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/smart_handle.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/common/system.h"
-#include "omaha/common/system_info.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/user_rights.h"
-#include "omaha/common/utils.h"
-
-#define LOW_INTEGRITY_SDDL_SACL_A     NOTRANSL("S:(ML;;NW;;;LW)")
-#define LOW_INTEGRITY_SID_W           NOTRANSL(L"S-1-16-4096")
-
-namespace omaha {
-
-namespace vista {
-
-namespace {
-
-// TODO(Omaha): Unit test for this method.
-HRESULT RunAsUser(const CString& command_line,
-                  HANDLE user_token,
-                  bool run_as_current_user) {
-  if (INVALID_HANDLE_VALUE == user_token) {
-    return E_INVALIDARG;
-  }
-
-  CString cmd(command_line);
-
-  STARTUPINFO startup_info = { sizeof(startup_info) };
-  PROCESS_INFORMATION process_info = {0};
-
-  DWORD creation_flags(0);
-  void* environment_block(NULL);
-  ON_SCOPE_EXIT(::DestroyEnvironmentBlock, environment_block);
-  if (::CreateEnvironmentBlock(&environment_block, user_token, FALSE)) {
-    creation_flags |= CREATE_UNICODE_ENVIRONMENT;
-  } else {
-    ASSERT(false, (_T("::CreateEnvironmentBlock failed %d"), ::GetLastError()));
-    environment_block = NULL;
-  }
-
-  // ::CreateProcessAsUser() does not work unless the caller is SYSTEM. Does not
-  // matter if the user token is for the current user.
-  BOOL success = run_as_current_user ?
-      ::CreateProcess(0, CStrBuf(cmd, MAX_PATH), 0, 0, false, creation_flags,
-                      environment_block, 0, &startup_info, &process_info) :
-      ::CreateProcessAsUser(user_token, 0, CStrBuf(cmd, MAX_PATH), 0, 0, false,
-                            creation_flags, environment_block, 0, &startup_info,
-                            &process_info);
-
-  if (!success) {
-    HRESULT hr(HRESULTFromLastError());
-    UTIL_LOG(LE, (_T("[RunAsUser failed][cmd=%s][hresult=0x%x]"), cmd, hr));
-    return hr;
-  }
-
-  VERIFY1(::CloseHandle(process_info.hThread));
-  VERIFY1(::CloseHandle(process_info.hProcess));
-
-  return S_OK;
-}
-
-}  // namespace
-
-bool IsProcessProtected() {
-  if (!SystemInfo::IsRunningOnVistaOrLater()) {
-    return false;
-  }
-
-  AutoHandle token;
-  VERIFY1(::OpenProcessToken(GetCurrentProcess(),
-                             TOKEN_QUERY | TOKEN_QUERY_SOURCE,
-                             &token.receive()));
-
-  // Get the Integrity level.
-  DWORD length_needed;
-  BOOL b = ::GetTokenInformation(token,
-                                 TokenIntegrityLevel,
-                                 NULL,
-                                 0,
-                                 &length_needed);
-  ASSERT1(b == FALSE);
-  if (b) {
-    return false;
-  }
-
-  // The first call to GetTokenInformation is just to get the buffer size
-  if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
-    return false;
-  }
-
-  scoped_ptr<TOKEN_MANDATORY_LABEL> integration_level;
-
-  integration_level.reset(reinterpret_cast<TOKEN_MANDATORY_LABEL*>(
-      new char[length_needed]));
-  if (integration_level.get() == NULL) {
-    return false;
-  }
-
-  if (!::GetTokenInformation(token,
-                             TokenIntegrityLevel,
-                             integration_level.get(),
-                             length_needed,
-                             &length_needed)) {
-    return false;
-  }
-
-  wchar_t* sid_str = NULL;
-  VERIFY1(::ConvertSidToStringSid(integration_level->Label.Sid, &sid_str));
-  bool ret = ::lstrcmpW(sid_str, LOW_INTEGRITY_SID_W) == 0;
-  ::LocalFree(sid_str);
-
-  return ret;
-}
-
-HRESULT AllowProtectedProcessAccessToSharedObject(const TCHAR* name) {
-  if (!SystemInfo::IsRunningOnVistaOrLater()) {
-    return S_FALSE;
-  }
-
-  ASSERT1(name != NULL);
-
-  PSECURITY_DESCRIPTOR psd = NULL;
-  VERIFY1(::ConvertStringSecurityDescriptorToSecurityDescriptorA(
-              LOW_INTEGRITY_SDDL_SACL_A,
-              SDDL_REVISION_1,
-              &psd,
-              NULL));
-
-  BOOL sacl_present = FALSE;
-  BOOL sacl_defaulted = FALSE;
-  PACL sacl = NULL;
-  VERIFY1(::GetSecurityDescriptorSacl(psd,
-                                      &sacl_present,
-                                      &sacl,
-                                      &sacl_defaulted));
-
-  DWORD ret = ::SetNamedSecurityInfoW(const_cast<TCHAR*>(name),
-                                      SE_KERNEL_OBJECT,
-                                      LABEL_SECURITY_INFORMATION,
-                                      NULL,
-                                      NULL,
-                                      NULL,
-                                      sacl);
-
-  ::LocalFree(psd);
-
-  return HRESULT_FROM_WIN32(ret);
-}
-
-HRESULT RunAsCurrentUser(const CString& command_line) {
-  scoped_handle token;
-  if (!::OpenProcessToken(::GetCurrentProcess(),
-                          TOKEN_QUERY | TOKEN_DUPLICATE,
-                          address(token))) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LE, (_T("[RunAsCurrentUser: OpenProcessToken failed][0x%x]"), hr));
-    return hr;
-  }
-
-  return RunAsUser(command_line, get(token), true);
-}
-
-static HRESULT StartInternetExplorerAsUser(HANDLE user_token,
-                                           const CString& options) {
-  // Internet Explorer path
-  CString ie_file_path;
-  HRESULT result = RegKey::GetValue(kRegKeyIeClass,
-                                    kRegValueIeClass,
-                                    &ie_file_path);
-  ASSERT1(SUCCEEDED(result));
-
-  if (SUCCEEDED(result)) {
-    CString command_line(ie_file_path);
-    command_line += _T(' ');
-    command_line += options;
-    UTIL_LOG(L5, (_T("[StartInternetExplorerAsUser]")
-                  _T("[Running IExplore with command line][%s]"),
-                  command_line));
-    result = RunAsUser(command_line, user_token, false);
-  }
-  return result;
-}
-
-//
-// Constants used by RestartIEUser()
-//
-// The IEUser executable name
-const TCHAR* kIEUser = _T("IEUSER.EXE");
-
-// The maximum number of simultaneous
-// logged on users in FUS that we support
-const int kMaximumUsers = 16;
-
-
-// Restart IEUser processs. This is to allow for
-// IEUser.exe to refresh it's ElevationPolicy cache. Due to a bug
-// within IE7, IEUser.exe does not refresh it's cache unless it
-// is restarted in the manner below. If the cache is not refreshed
-// IEUser does not respect any new ElevationPolicies that a fresh
-// setup program installs for an ActiveX control or BHO. This code
-// is adapted from Toolbar.
-HRESULT RestartIEUser() {
-  // Use the service to restart IEUser.
-  // This allows us to restart IEUser for:
-  //   (a) Multiple users for the first-install case
-  //       (we currently only restart IEUser for the current interactive user)
-  //   (b) Even if we are started in an elevated mode
-
-  if (!SystemInfo::IsRunningOnVistaOrLater()) {
-    UTIL_LOG(L5, (_T("[RestartIEUser - not running on Vista - Exiting]")));
-    return S_OK;
-  }
-
-  // The restart should be attempted from the system account
-  bool is_system_process = false;
-  if (FAILED(IsSystemProcess(&is_system_process)) || !is_system_process) {
-    ASSERT1(false);
-    return E_ACCESSDENIED;
-  }
-
-  // Get the list of users currently running IEUser.exe processes.
-  scoped_handle ieuser_users[kMaximumUsers];
-  int number_of_users = 0;
-  Process::GetUsersOfProcesses(kIEUser, kMaximumUsers, ieuser_users,
-                               &number_of_users);
-
-  UTIL_LOG(L5, (_T("[RestartIEUser]")
-                _T("[number_of_users running IEUser %d]"), number_of_users));
-
-  if (!number_of_users) {
-    UTIL_LOG(L5, (_T("[RestartIEUser][No IEUser processes running]")));
-    return S_OK;
-  }
-
-  // Kill current IEUser processes.
-  ProcessTerminator pt(kIEUser);
-  const int kKillWaitTimeoutMs = 5000;
-  bool found = false;
-  const int kill_method = (ProcessTerminator::KILL_METHOD_4_TERMINATE_PROCESS);
-
-  RET_IF_FAILED(pt.KillTheProcess(kKillWaitTimeoutMs,
-                                  &found,
-                                  kill_method,
-                                  false));
-
-  // Restart them.
-  HRESULT result = S_OK;
-  for (int i = 0; i < number_of_users; i++) {
-    // To start a new ieuser.exe, simply start iexplore.exe as a normal user
-    // The -embedding prevents IE from opening a window
-    HRESULT restart_result = StartInternetExplorerAsUser(get(ieuser_users[i]),
-                                                         _T("-embedding"));
-    if (FAILED(restart_result)) {
-      UTIL_LOG(LEVEL_ERROR, (_T("[StartInternetExplorerAsUser failed][0x%x]"),
-                             restart_result));
-      result = restart_result;
-    }
-  }
-
-  return result;
-}
-
-HRESULT GetExplorerPidForCurrentUserOrSession(uint32* pid) {
-  ASSERT1(pid);
-  std::vector<uint32> pids;
-  HRESULT hr = GetProcessPidsForActiveUserOrSession(kExplorer, &pids);
-  if (FAILED(hr)) {
-    CORE_LOG(LW, (_T("[Did not find explorer.exe processes][0x%x]"), hr));
-    return hr;
-  }
-
-  CORE_LOG(L1, (_T("[Found %u instance(s) of explorer.exe]"), pids.size()));
-
-  *pid = pids[0];   // Return only the first instance of explorer.exe.
-  return S_OK;
-}
-
-HRESULT GetExplorerTokenForLoggedInUser(HANDLE* token) {
-  UTIL_LOG(L3, (_T("[GetExplorerTokenForLoggedInUser]")));
-  ASSERT1(token);
-
-  // TODO(omaha): One can set the windows shell to be other than
-  // explorer.exe, handle this case. One way to handle this is to
-  // read the regkey
-  // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon
-  // The only problem with this is it can be overriden with the user reg keys.
-  // Need to figure out a method to do this.
-  // Also consider using the interactive user before picking the first explorer
-  // process i.e. the active user.
-  std::vector<uint32> processes;
-  DWORD flags = EXCLUDE_CURRENT_PROCESS;
-  std::vector<CString> command_lines;
-  CString explorer_file_name(kExplorer);
-  CString user_sid;
-
-  HRESULT hr = Process::FindProcesses(flags,
-                                      explorer_file_name,
-                                      true,
-                                      user_sid,
-                                      command_lines,
-                                      &processes);
-  if (FAILED(hr)) {
-    CORE_LOG(LEVEL_ERROR, (_T("[FindProcesses failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  std::vector<uint32>::const_iterator iter = processes.begin();
-  for (; iter != processes.end(); ++iter) {
-    uint32 explorer_pid = *iter;
-    scoped_handle exp(::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
-                                    false,
-                                    explorer_pid));
-    if (exp) {
-      if (::OpenProcessToken(get(exp),
-                             TOKEN_DUPLICATE | TOKEN_QUERY | TOKEN_IMPERSONATE,
-                             token)) {
-        // TODO(omaha): Consider using the GetWindowsAccountDomainSid
-        // method here. This method returns the domain SID associated
-        // with the passed in SID. This allows us to detect if the user is a
-        // domain user. We should prefer domain users over normal users,
-        // as in corporate environments, these users will be more likely to
-        // allow to be tunneled through a proxy.
-        return S_OK;
-      } else {
-        hr = HRESULTFromLastError();
-        CORE_LOG(LEVEL_WARNING, (_T("[OpenProcessToken failed][0x%08x]"), hr));
-      }
-    } else {
-      hr = HRESULTFromLastError();
-      CORE_LOG(LEVEL_WARNING, (_T("[OpenProcess failed][0x%08x]"), hr));
-    }
-  }
-
-  return hr;
-}
-
-HRESULT GetPidsInSession(const TCHAR* exe_name,
-                         const TCHAR* user_sid,
-                         DWORD session_id,
-                         std::vector<uint32>* pids) {
-  ASSERT1(pids);
-  ASSERT1(exe_name);
-  ASSERT1(*exe_name);
-  UTIL_LOG(L3, (_T("[GetPidsInSession][%s][sid=%s][session=%d]"),
-                exe_name, user_sid, session_id));
-
-  pids->clear();
-
-  DWORD flags = EXCLUDE_CURRENT_PROCESS;
-  if (user_sid != NULL) {
-    flags |= INCLUDE_ONLY_PROCESS_OWNED_BY_USER;
-  }
-  std::vector<CString> command_lines;
-  HRESULT hr = Process::FindProcessesInSession(session_id,
-                                               flags,
-                                               exe_name,
-                                               true,
-                                               user_sid,
-                                               command_lines,
-                                               pids);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  return pids->empty() ? HRESULT_FROM_WIN32(ERROR_NOT_FOUND) : S_OK;
-}
-
-HRESULT GetProcessPidsForActiveUserOrSession(const TCHAR* exe_name,
-                                             std::vector<uint32>* pids) {
-  bool is_system = false;
-  HRESULT hr = IsSystemProcess(&is_system);
-  if (FAILED(hr)) {
-    NET_LOG(LE, (_T("[IsSystemProcess failed][0x%x]"), hr));
-    return hr;
-  }
-
-  if (is_system) {
-    return vista::GetPidsInSession(exe_name,
-                                   NULL,
-                                   System::GetActiveSessionId(),
-                                   pids);
-  }
-
-  CString user_sid;
-  // If this call fails, we are still ok.
-  omaha::user_info::GetCurrentUser(NULL, NULL, &user_sid);
-  DWORD current_session = System::GetCurrentSessionId();
-  if (FAILED(vista::GetPidsInSession(exe_name,
-                                     user_sid,
-                                     current_session,
-                                     pids))) {
-    // In the case of RunAs, the processes may be under a different identity
-    // than the current sid. So if we are unable to find a process under the
-    // current user's sid, we search for processes running in the current
-    // session regardless of the sid they are running under.
-    return vista::GetPidsInSession(exe_name,
-                                   NULL,
-                                   current_session,
-                                   pids);
-  }
-
-  return S_OK;
-}
-
-
-
-HRESULT StartProcessWithTokenOfProcess(uint32 pid,
-                                       const CString& command_line) {
-  UTIL_LOG(L5, (_T("[StartProcessWithTokenOfProcess]")
-                _T("[pid %u][command_line '%s']"), pid, command_line));
-
-  // Get the token from process.
-  scoped_handle user_token;
-  HRESULT hr = Process::GetImpersonationToken(pid, address(user_token));
-  if (FAILED(hr)) {
-    CORE_LOG(LEVEL_ERROR, (_T("[GetImpersonationToken failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  // Start process using the token.
-  UTIL_LOG(L5, (_T("[StartProcessWithTokenOfProcess][Running process %s]"),
-                command_line));
-  hr = RunAsUser(command_line, get(user_token), false);
-
-  if (FAILED(hr)) {
-    UTIL_LOG(LEVEL_ERROR,
-      (_T("[Vista::StartProcessWithTokenOfProcess - RunAsUser failed][0x%x]"),
-      hr));
-  }
-
-  return hr;
-}
-
-HRESULT GetLoggedOnUserToken(HANDLE* token) {
-  ASSERT1(token);
-  *token = NULL;
-
-  uint32 pid = 0;
-  HRESULT hr = GetExplorerPidForCurrentUserOrSession(&pid);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  hr = Process::GetImpersonationToken(pid, token);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  ASSERT1(*token);
-  return S_OK;
-}
-
-}  // namespace vista
-
-}  // namespace omaha
-
diff --git a/common/vista_utils.h b/common/vista_utils.h
deleted file mode 100644
index 8592d32..0000000
--- a/common/vista_utils.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_COMMON_VISTA_UTILS_H__
-#define OMAHA_COMMON_VISTA_UTILS_H__
-
-#include <windows.h>
-#include <aclapi.h>
-#include <sddl.h>
-#include <userenv.h>
-#include <atlstr.h>
-#include <vector>
-#include "base/basictypes.h"
-
-namespace omaha {
-
-// Constants.
-const TCHAR* const kExplorer = _T("EXPLORER.EXE");
-const TCHAR* const kIExplore = _T("IEXPLORE.EXE");
-
-namespace vista {
-
-// Returns true if the current process is running in 'protected mode'.
-bool IsProcessProtected();
-
-// Allows processes that run under protected mode access to a shared kernel
-// object such as mapped memory.
-//
-// Returns S_OK if successful, S_FALSE if not running on vista, or an
-// error value.
-HRESULT AllowProtectedProcessAccessToSharedObject(const TCHAR* name);
-
-// Restarts IEUser process if we can. This is to allow for
-// IEUser.exe to refresh it's ElevationPolicy cache. Due to a bug
-// within IE7, IEUser.exe does not refresh it's cache unless it
-// is restarted in the manner below. If the cache is not refreshed
-// IEUser does not respect any new ElevationPolicies that a fresh
-// setup program installs for an ActiveX control or BHO. This code
-// is adapted from Toolbar.
-HRESULT RestartIEUser();
-
-// TODO(Omaha): Move these to a different utils file, since these are not
-// Vista-specific.
-// TODO(Omaha): rename for consistency with
-// GetProcessPidsForActiveUserOrSession.
-//
-// Gets current user's explorer.exe pid. If that fails, gets the pid of any
-// explorer.exe running in the current session.
-HRESULT GetExplorerPidForCurrentUserOrSession(uint32* pid);
-
-// Returns the TOKEN of the explorer process of any user that is logged in.
-HRESULT GetExplorerTokenForLoggedInUser(HANDLE* token);
-
-// Retrieves a primary token for one of the logged on users. The logged on
-// user is either the current user or a user logged on in the same session as
-// the current user. The caller must close the token handle.
-HRESULT GetLoggedOnUserToken(HANDLE* token);
-
-// Get PIDs for the processes running with the specified executable, user_sid,
-// and session_id. user_sid can be blank, in which case, the search will
-// encompass all processes with the given name in session_id. The session
-// always has to be a valid session, hence the name GetPidsInSession().
-HRESULT GetPidsInSession(const TCHAR* exe_name,
-                         const TCHAR* user_sid,
-                         DWORD session_id,
-                         std::vector<uint32>* pids);
-
-// Get the handle of exe_name running under the active user or active session.
-// If the call is made from the SYSTEM account, returns PIDs for exe_name
-// in the currently active user session. If the call is made from a user account
-// returns PIDs for that user, or if that cannot be found, in the current
-// session.
-HRESULT GetProcessPidsForActiveUserOrSession(const TCHAR* exe_name,
-                                             std::vector<uint32>* pids);
-
-// Starts process with the token obtained from the specified process.
-HRESULT StartProcessWithTokenOfProcess(uint32 pid,
-                                       const CString& command_line);
-
-// Runs the command on behalf of the current user. Creates a fresh environment
-// block based on the user's token.
-HRESULT RunAsCurrentUser(const CString& command_line);
-
-}  // namespace vista
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_VISTA_UTILS_H__
-
diff --git a/common/vista_utils_unittest.cc b/common/vista_utils_unittest.cc
deleted file mode 100644
index 06230cb..0000000
--- a/common/vista_utils_unittest.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/app_util.h"
-#include "omaha/common/path.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vista_utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-namespace vista {
-
-// Exercises RunAsUser() with explorer token. For Vista, the call to
-// StartProcessWithTokenOfProcess() will succeed only if the caller is SYSTEM.
-TEST(VistaUtilsTest, StartProcessWithExplorerTokenTest) {
-  CString path = ConcatenatePath(app_util::GetSystemDir(), _T("cmd.exe"));
-  EnclosePath(&path);
-  path += _T(" /c exit 702");
-  uint32 pid(0);
-  EXPECT_SUCCEEDED(GetExplorerPidForCurrentUserOrSession(&pid));
-
-  HRESULT hr = StartProcessWithTokenOfProcess(pid, path);
-  if (!vista_util::IsVistaOrLater()) {
-    EXPECT_SUCCEEDED(hr);
-    return;
-  }
-
-  bool is_system = false;
-  EXPECT_SUCCEEDED(IsSystemProcess(&is_system));
-  if (is_system) {
-    EXPECT_SUCCEEDED(hr);
-    return;
-  }
-
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_PRIVILEGE_NOT_HELD), hr);
-}
-
-// Exercises RunAsUser() with current user token.
-TEST(VistaUtilsTest, RunAsCurrentUserTest) {
-  CString path = ConcatenatePath(app_util::GetSystemDir(), _T("cmd.exe"));
-  EnclosePath(&path);
-  path += _T(" /c exit 702");
-  EXPECT_SUCCEEDED(vista::RunAsCurrentUser(path));
-}
-
-}  // namespace vista
-
-}  // namespace omaha
-
diff --git a/common/vistautil.cc b/common/vistautil.cc
deleted file mode 100644
index 25b248b..0000000
--- a/common/vistautil.cc
+++ /dev/null
@@ -1,577 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include "omaha/common/vistautil.h"
-
-#include <accctrl.h>
-#include <Aclapi.h>
-#include <Sddl.h>
-#include <ShellAPI.h>
-#include <shlobj.h>
-#include "base/scoped_ptr.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/process.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vista_utils.h"
-#include "omaha/third_party/smartany/scoped_any.h"
-
-namespace omaha {
-
-namespace vista_util {
-
-static SID_IDENTIFIER_AUTHORITY mandatory_label_auth =
-    SECURITY_MANDATORY_LABEL_AUTHORITY;
-
-
-static HRESULT GetSidIntegrityLevel(PSID sid, MANDATORY_LEVEL* level) {
-  if (!IsValidSid(sid))
-    return E_FAIL;
-
-  SID_IDENTIFIER_AUTHORITY* authority = GetSidIdentifierAuthority(sid);
-  if (!authority)
-    return E_FAIL;
-
-  if (memcmp(authority, &mandatory_label_auth,
-      sizeof(SID_IDENTIFIER_AUTHORITY)))
-    return E_FAIL;
-
-  PUCHAR count = GetSidSubAuthorityCount(sid);
-  if (!count || *count != 1)
-    return E_FAIL;
-
-  DWORD* rid = GetSidSubAuthority(sid, 0);
-  if (!rid)
-    return E_FAIL;
-
-  if ((*rid & 0xFFF) != 0 || *rid > SECURITY_MANDATORY_PROTECTED_PROCESS_RID)
-    return E_FAIL;
-
-  *level = static_cast<MANDATORY_LEVEL>(*rid >> 12);
-  return S_OK;
-}
-
-// Will return S_FALSE and MandatoryLevelMedium if the acl is NULL
-static HRESULT GetAclIntegrityLevel(PACL acl, MANDATORY_LEVEL* level,
-    bool* and_children) {
-  *level = MandatoryLevelMedium;
-  if (and_children)
-    *and_children = false;
-  if (!acl) {
-    // This is the default label value if the acl was empty
-    return S_FALSE;
-  }
-
-  SYSTEM_MANDATORY_LABEL_ACE* mandatory_label_ace;
-  if (!GetAce(acl, 0, reinterpret_cast<void**>(&mandatory_label_ace)))
-    return S_FALSE;
-
-  if (mandatory_label_ace->Header.AceType != SYSTEM_MANDATORY_LABEL_ACE_TYPE)
-    return S_FALSE;
-
-  if (!(mandatory_label_ace->Mask & SYSTEM_MANDATORY_LABEL_NO_WRITE_UP)) {
-    // I have found that if this flag is not set, a low integrity label doesn't
-    // prevent writes from being virtualized.  MS provides zero documentation.
-    // I just did an MSDN search, a Google search, and a search of the Beta
-    // Vista SDKs, and no docs. TODO(omaha): Check docs again periodically.
-    // For now, act as if no label was set, and default to medium.
-    return S_FALSE;
-  }
-
-  if (and_children) {
-    *and_children = ((mandatory_label_ace->Header.AceFlags &
-        (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE))
-        == (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE));
-  }
-
-  return GetSidIntegrityLevel(reinterpret_cast<SID*>(&mandatory_label_ace->
-      SidStart), level);
-}
-
-// If successful, the caller needs to free the ACL using LocalFree()
-// on failure, returns NULL
-static ACL* CreateMandatoryLabelAcl(MANDATORY_LEVEL level, bool and_children) {
-  int ace_size = sizeof(SYSTEM_MANDATORY_LABEL_ACE)
-      - sizeof(DWORD) + GetSidLengthRequired(1);
-  int acl_size = sizeof(ACL) + ace_size;
-
-  ACL* acl = reinterpret_cast<ACL*>(LocalAlloc(LPTR, acl_size));
-  if (!acl)
-    return NULL;
-
-  bool failed = true;
-  if (InitializeAcl(acl, acl_size, ACL_REVISION)) {
-    if (level > 0) {
-      SYSTEM_MANDATORY_LABEL_ACE* ace = reinterpret_cast<
-          SYSTEM_MANDATORY_LABEL_ACE*>(LocalAlloc(LPTR, ace_size));
-      if (ace) {
-        ace->Header.AceType = SYSTEM_MANDATORY_LABEL_ACE_TYPE;
-        ace->Header.AceFlags = and_children ?
-            (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE) : 0;
-        ace->Header.AceSize = static_cast<WORD>(ace_size);
-        ace->Mask = SYSTEM_MANDATORY_LABEL_NO_WRITE_UP;
-
-        SID* sid = reinterpret_cast<SID*>(&ace->SidStart);
-
-        if (InitializeSid(sid, &mandatory_label_auth, 1)) {
-          *GetSidSubAuthority(sid, 0) = static_cast<DWORD>(level) << 12;
-          failed = !AddAce(acl, ACL_REVISION, 0, ace, ace_size);
-        }
-        LocalFree(ace);
-      }
-    }
-  }
-  if (failed) {
-    LocalFree(acl);
-    acl = NULL;
-  }
-  return acl;
-}
-
-
-TCHAR* AllocFullRegPath(HKEY root, const TCHAR* subkey) {
-  if (!subkey)
-    return NULL;
-
-  const TCHAR* root_string;
-
-  if (root == HKEY_CURRENT_USER)
-    root_string = _T("CURRENT_USER\\");
-  else if (root == HKEY_LOCAL_MACHINE)
-    root_string = _T("MACHINE\\");
-  else if (root == HKEY_CLASSES_ROOT)
-    root_string = _T("CLASSES_ROOT\\");
-  else if (root == HKEY_USERS)
-    root_string = _T("USERS\\");
-  else
-    return NULL;
-
-  size_t root_size = _tcslen(root_string);
-  size_t size = root_size + _tcslen(subkey) + 1;
-  TCHAR* result = reinterpret_cast<TCHAR*>(LocalAlloc(LPTR,
-      size * sizeof(TCHAR)));
-  if (!result)
-    return NULL;
-
-  memcpy(result, root_string, size * sizeof(TCHAR));
-  memcpy(result + root_size, subkey, (1 + size - root_size) * sizeof(TCHAR));
-  return result;
-}
-
-
-bool IsUserNonElevatedAdmin() {
-  // If pre-Vista return false;
-  if (!IsVistaOrLater()) {
-    return false;
-  }
-
-  bool non_elevated_admin = false;
-  scoped_handle token;
-  if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_READ, address(token))) {
-    TOKEN_ELEVATION_TYPE elevation_type = TokenElevationTypeDefault;
-    DWORD infoLen = 0;
-    if (::GetTokenInformation(get(token),
-                              TokenElevationType,
-                              reinterpret_cast<void*>(&elevation_type),
-                              sizeof(elevation_type),
-                              &infoLen)) {
-      if (elevation_type == TokenElevationTypeLimited) {
-        non_elevated_admin = true;
-      }
-    }
-  }
-
-  return non_elevated_admin;
-}
-
-bool IsUserAdmin() {
-  // Determine if the user is part of the adminstators group. This will return
-  // true in case of XP and 2K if the user belongs to admin group. In case of
-  // Vista, it only returns true if the admin is running elevated.
-  SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY;
-  PSID administrators_group = NULL;
-  BOOL result = ::AllocateAndInitializeSid(&nt_authority,
-                                           2,
-                                           SECURITY_BUILTIN_DOMAIN_RID,
-                                           DOMAIN_ALIAS_RID_ADMINS,
-                                           0, 0, 0, 0, 0, 0,
-                                           &administrators_group);
-  if (result) {
-    if (!::CheckTokenMembership(NULL, administrators_group, &result)) {
-      result = false;
-    }
-    ::FreeSid(administrators_group);
-  }
-  return !!result;
-}
-
-bool IsVistaOrLater() {
-  static bool known = false;
-  static bool is_vista = false;
-  if (!known) {
-    OSVERSIONINFOEX osvi = { 0 };
-    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
-    osvi.dwMajorVersion = 6;
-    DWORDLONG conditional = 0;
-    VER_SET_CONDITION(conditional, VER_MAJORVERSION, VER_GREATER_EQUAL);
-    is_vista = !!VerifyVersionInfo(&osvi, VER_MAJORVERSION, conditional);
-    // If the Win32 API failed for some other reason, callers may incorrectly
-    // perform non-Vista operations. Assert we don't see any other failures.
-    ASSERT1(is_vista || ERROR_OLD_WIN_VERSION == ::GetLastError());
-    known = true;
-  }
-  return is_vista;
-}
-
-HRESULT IsUserRunningSplitToken(bool* is_split_token) {
-  ASSERT1(is_split_token);
-
-  if (!IsVistaOrLater()) {
-    *is_split_token = false;
-    return S_OK;
-  }
-
-  scoped_handle process_token;
-  if (!::OpenProcessToken(::GetCurrentProcess(),
-                          TOKEN_QUERY,
-                          address(process_token))) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(L1, (_T("[OpenProcessToken failed][0x%x]"), hr));
-    return hr;
-  }
-
-  TOKEN_ELEVATION_TYPE elevation_type = TokenElevationTypeDefault;
-  DWORD size_returned = 0;
-  if (!::GetTokenInformation(get(process_token),
-                             TokenElevationType,
-                             &elevation_type,
-                             sizeof(elevation_type),
-                             &size_returned)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(L1, (_T("[GetTokenInformation failed][0x%x]"), hr));
-    return hr;
-  }
-
-  *is_split_token = elevation_type == TokenElevationTypeFull ||
-                    elevation_type == TokenElevationTypeLimited;
-  ASSERT1(*is_split_token || elevation_type == TokenElevationTypeDefault);
-
-  return S_OK;
-}
-
-bool IsUACDisabled() {
-  if (!IsVistaOrLater()) {
-    return false;
-  }
-
-  // Split token indicates that UAC is on.
-  bool is_split_token = true;
-  if SUCCEEDED(IsUserRunningSplitToken(&is_split_token)) {
-    return !is_split_token;
-  } else {
-    // Return a safe value on failure.
-    return false;
-  }
-}
-
-HRESULT RunElevated(const TCHAR* file_path,
-                    const TCHAR* parameters,
-                    int show_window,
-                    DWORD* exit_code) {
-  UTIL_LOG(L1, (_T("[Running elevated][%s][%s]"), file_path, parameters));
-
-  SHELLEXECUTEINFO shell_execute_info;
-  shell_execute_info.cbSize = sizeof(SHELLEXECUTEINFO);
-  shell_execute_info.fMask = SEE_MASK_FLAG_NO_UI     |
-                             SEE_MASK_NOZONECHECKS   |
-                             SEE_MASK_NOASYNC;
-  if (exit_code != NULL) {
-    shell_execute_info.fMask |= SEE_MASK_NOCLOSEPROCESS;
-  }
-  shell_execute_info.hProcess = NULL;
-  shell_execute_info.hwnd = NULL;
-  shell_execute_info.lpVerb = L"runas";
-  shell_execute_info.lpFile = file_path;
-  shell_execute_info.lpParameters = parameters;
-  shell_execute_info.lpDirectory = NULL;
-  shell_execute_info.nShow = show_window;
-  shell_execute_info.hInstApp = NULL;
-
-  if (!ShellExecuteExEnsureParent(&shell_execute_info)) {
-    return AtlHresultFromLastError();
-  }
-
-  scoped_process process(shell_execute_info.hProcess);
-
-  // Wait for the end of the spawned process, if needed
-  if (exit_code) {
-    WaitForSingleObject(get(process), INFINITE);
-    VERIFY1(GetExitCodeProcess(get(process), exit_code));
-    UTIL_LOG(L1, (_T("[Elevated process exited][PID: %u][exit code: %u]"),
-                  Process::GetProcessIdFromHandle(get(process)), *exit_code));
-  } else {
-    UTIL_LOG(L1, (_T("[Elevated process exited][PID: %u]"),
-                  Process::GetProcessIdFromHandle(get(process))));
-  }
-
-  return S_OK;
-}
-
-
-HRESULT GetProcessIntegrityLevel(DWORD process_id, MANDATORY_LEVEL* level) {
-  if (!IsVistaOrLater())
-    return E_NOTIMPL;
-
-  if (process_id == 0)
-    process_id = ::GetCurrentProcessId();
-
-  HRESULT result = E_FAIL;
-  HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process_id);
-  if (process != NULL) {
-    HANDLE current_token;
-    if (OpenProcessToken(process,
-                         TOKEN_QUERY | TOKEN_QUERY_SOURCE,
-                         &current_token)) {
-      DWORD label_size = 0;
-      TOKEN_MANDATORY_LABEL* label;
-      GetTokenInformation(current_token, TokenIntegrityLevel,
-          NULL, 0, &label_size);
-      if (label_size && (label = reinterpret_cast<TOKEN_MANDATORY_LABEL*>
-          (LocalAlloc(LPTR, label_size))) != NULL) {
-        if (GetTokenInformation(current_token, TokenIntegrityLevel,
-            label, label_size, &label_size)) {
-          result = GetSidIntegrityLevel(label->Label.Sid, level);
-        }
-        LocalFree(label);
-      }
-      CloseHandle(current_token);
-    }
-    CloseHandle(process);
-  }
-  return result;
-}
-
-
-HRESULT GetFileOrFolderIntegrityLevel(const TCHAR* file,
-    MANDATORY_LEVEL* level, bool* and_children) {
-  if (!IsVistaOrLater())
-    return E_NOTIMPL;
-
-  PSECURITY_DESCRIPTOR descriptor;
-  PACL acl = NULL;
-
-  DWORD result = GetNamedSecurityInfo(const_cast<TCHAR*>(file), SE_FILE_OBJECT,
-      LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, &acl, &descriptor);
-  if (result != ERROR_SUCCESS)
-    return HRESULT_FROM_WIN32(result);
-
-  HRESULT hr = GetAclIntegrityLevel(acl, level, and_children);
-  LocalFree(descriptor);
-  return hr;
-}
-
-
-HRESULT SetFileOrFolderIntegrityLevel(const TCHAR* file,
-    MANDATORY_LEVEL level, bool and_children) {
-  if (!IsVistaOrLater())
-    return E_NOTIMPL;
-
-  ACL* acl = CreateMandatoryLabelAcl(level, and_children);
-  if (!acl)
-    return E_FAIL;
-
-  DWORD result = SetNamedSecurityInfo(const_cast<TCHAR*>(file), SE_FILE_OBJECT,
-      LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, acl);
-  LocalFree(acl);
-  return HRESULT_FROM_WIN32(result);
-}
-
-
-HRESULT GetRegKeyIntegrityLevel(HKEY root, const TCHAR* subkey,
-    MANDATORY_LEVEL* level, bool* and_children) {
-  if (!IsVistaOrLater())
-    return E_NOTIMPL;
-
-  TCHAR* reg_path = AllocFullRegPath(root, subkey);
-  if (!reg_path)
-    return E_FAIL;
-
-  PSECURITY_DESCRIPTOR descriptor;
-  PACL acl = NULL;
-
-  DWORD result = GetNamedSecurityInfo(reg_path, SE_REGISTRY_KEY,
-      LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, &acl, &descriptor);
-  if (result != ERROR_SUCCESS) {
-    LocalFree(reg_path);
-    return HRESULT_FROM_WIN32(result);
-  }
-
-  HRESULT hr = GetAclIntegrityLevel(acl, level, and_children);
-  LocalFree(descriptor);
-  LocalFree(reg_path);
-  return hr;
-}
-
-
-HRESULT SetRegKeyIntegrityLevel(HKEY root, const TCHAR* subkey,
-    MANDATORY_LEVEL level, bool and_children) {
-  if (!IsVistaOrLater())
-    return E_NOTIMPL;
-
-  TCHAR* reg_path = AllocFullRegPath(root, subkey);
-  if (!reg_path)
-    return E_FAIL;
-
-  ACL* acl = CreateMandatoryLabelAcl(level, and_children);
-  if (!acl) {
-    LocalFree(reg_path);
-    return E_FAIL;
-  }
-
-  DWORD result = SetNamedSecurityInfo(reg_path, SE_REGISTRY_KEY,
-      LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, acl);
-  LocalFree(acl);
-  LocalFree(reg_path);
-  return HRESULT_FROM_WIN32(result);
-}
-
-
-CSecurityDesc* BuildSecurityDescriptor(const TCHAR* sddl_sacl,
-                                       ACCESS_MASK mask) {
-  if (!IsVistaOrLater()) {
-    return NULL;
-  }
-
-  scoped_ptr<CSecurityDesc> security_descriptor(new CSecurityDesc);
-  security_descriptor->FromString(sddl_sacl);
-
-  // Fill out the rest of the security descriptor from the process token.
-  CAccessToken token;
-  if (!token.GetProcessToken(TOKEN_QUERY)) {
-    return NULL;
-  }
-
-  // The owner.
-  CSid sid_owner;
-  if (!token.GetOwner(&sid_owner)) {
-    return NULL;
-  }
-  security_descriptor->SetOwner(sid_owner);
-
-  // The group.
-  CSid sid_group;
-  if (!token.GetPrimaryGroup(&sid_group)) {
-    return NULL;
-  }
-  security_descriptor->SetGroup(sid_group);
-
-  // The discretionary access control list.
-  CDacl dacl;
-  if (!token.GetDefaultDacl(&dacl)) {
-    return NULL;
-  }
-
-  // Add an access control entry mask for the current user.
-  // This is what grants this user access from lower integrity levels.
-  CSid sid_user;
-  if (!token.GetUser(&sid_user)) {
-    return NULL;
-  }
-
-  if (!dacl.AddAllowedAce(sid_user, mask)) {
-    return NULL;
-  }
-
-  // Lastly, save the dacl to this descriptor.
-  security_descriptor->SetDacl(dacl);
-  return security_descriptor.release();
-};
-
-CSecurityDesc* CreateLowIntegritySecurityDesc(ACCESS_MASK mask) {
-  return BuildSecurityDescriptor(LOW_INTEGRITY_SDDL_SACL, mask);
-}
-
-CSecurityDesc* CreateMediumIntegritySecurityDesc(ACCESS_MASK mask) {
-  return BuildSecurityDescriptor(MEDIUM_INTEGRITY_SDDL_SACL, mask);
-}
-
-HRESULT AddLowIntegritySaclToExistingDesc(CSecurityDesc* sd) {
-  ASSERT1(sd);
-  ASSERT1(sd->GetPSECURITY_DESCRIPTOR());
-
-  if (!IsVistaOrLater()) {
-    return S_FALSE;
-  }
-
-  CSecurityDesc sd_low;
-  if (!sd_low.FromString(LOW_INTEGRITY_SDDL_SACL)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LE, (_T("[Failed to parse LOW_INTEGRITY_SDDL_SACL][0x%x]"), hr));
-    return hr;
-  }
-
-  // Atl::CSacl does not support SYSTEM_MANDATORY_LABEL_ACE_TYPE.
-  BOOL sacl_present = FALSE;
-  BOOL sacl_defaulted = FALSE;
-  PACL sacl = NULL;
-  if (!::GetSecurityDescriptorSacl(
-             const_cast<SECURITY_DESCRIPTOR*>(sd_low.GetPSECURITY_DESCRIPTOR()),
-             &sacl_present,
-             &sacl,
-             &sacl_defaulted) ||
-      !sacl) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LE, (_T("[Failed to get the low integrity SACL][0x%x]"), hr));
-    return hr;
-  }
-
-  ACL_SIZE_INFORMATION acl_size = {0};
-  if (!::GetAclInformation(sacl,
-                           &acl_size,
-                           sizeof(acl_size),
-                           AclSizeInformation)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LE, (_T("[Failed to get AclSizeInformation][0x%x]"), hr));
-    return hr;
-  }
-
-  // The CSecurityDesc destructor expects the memory to have been malloced.
-  PACL new_sacl = static_cast<PACL>(malloc(acl_size.AclBytesInUse));
-  ::CopyMemory(new_sacl, sacl, acl_size.AclBytesInUse);
-
-  CSacl sacl_empty;
-  sd->SetSacl(sacl_empty);
-
-  if (!::SetSecurityDescriptorSacl(
-             const_cast<SECURITY_DESCRIPTOR*>(sd->GetPSECURITY_DESCRIPTOR()),
-             sacl_present,
-             new_sacl,
-             sacl_defaulted))  {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LE, (_T("[Failed to set the low integrity SACL][0x%x]"), hr));
-    free(new_sacl);
-    return hr;
-  }
-
-  return S_OK;
-}
-
-}  // namespace vista_util
-
-}  // namespace omaha
-
diff --git a/common/vistautil.h b/common/vistautil.h
deleted file mode 100644
index 11e690c..0000000
--- a/common/vistautil.h
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_COMMON_VISTAUTIL_H__
-#define OMAHA_COMMON_VISTAUTIL_H__
-
-#include <windows.h>
-#include <tchar.h>
-#include <accctrl.h>
-#include <Aclapi.h>
-#include <Sddl.h>
-#include <WinNT.h>
-#include <atlsecurity.h>
-
-namespace omaha {
-
-// SACLs are normally used for auditing, but Vista also uses them to
-// determine integrity levels.
-// For more info, http://www.google.com/search?q=SDDL+for+Mandatory+Labels
-// S = SACL
-// ML = Mandatory label (aka integrity level)
-// NW = No write up (integrity levels less than low cannot gain access)
-// LW = Low Integrity Level (What IE normally runs in)
-
-// The LABEL_SECURITY_INFORMATION SDDL SACL for medium integrity.
-// L"S:(ML;;NW;;;ME)"
-#define MEDIUM_INTEGRITY_SDDL_SACL  SDDL_SACL             \
-                                    SDDL_DELIMINATOR      \
-                                    SDDL_ACE_BEGIN        \
-                                    SDDL_MANDATORY_LABEL  \
-                                    SDDL_SEPERATOR        \
-                                    SDDL_SEPERATOR        \
-                                    SDDL_NO_WRITE_UP      \
-                                    SDDL_SEPERATOR        \
-                                    SDDL_SEPERATOR        \
-                                    SDDL_SEPERATOR        \
-                                    SDDL_ML_MEDIUM        \
-                                    SDDL_ACE_END
-
-// The LABEL_SECURITY_INFORMATION SDDL SACL for low integrity.
-// L"S:(ML;;NW;;;LW)"
-#define LOW_INTEGRITY_SDDL_SACL     SDDL_SACL             \
-                                    SDDL_DELIMINATOR      \
-                                    SDDL_ACE_BEGIN        \
-                                    SDDL_MANDATORY_LABEL  \
-                                    SDDL_SEPERATOR        \
-                                    SDDL_SEPERATOR        \
-                                    SDDL_NO_WRITE_UP      \
-                                    SDDL_SEPERATOR        \
-                                    SDDL_SEPERATOR        \
-                                    SDDL_SEPERATOR        \
-                                    SDDL_ML_LOW           \
-                                    SDDL_ACE_END
-
-namespace vista_util {
-
-// This is fast, since it caches the answer after first run.
-bool IsVistaOrLater();
-
-// Is the user running on Vista or later with a split-token.
-HRESULT IsUserRunningSplitToken(bool* is_split_token);
-
-// Returns true if the user has the reg key for disabling UAC policy at startup
-// set.
-bool IsUACDisabled();
-
-// Returns true if the process is running under credentials of an user
-// belonging to the admin group in case of pre-Vista and in case Vista
-// returns true if the user is running as an elevated admin.
-bool IsUserAdmin();
-
-// Returns true if the user is running as a non-elevated admin in case of
-// Vista. In case of XP always returns false.
-bool IsUserNonElevatedAdmin();
-
-// Determine the mandatory level of a process
-//   processID, the process to query, or (0) to use the current process
-//   On Vista, level should alwys be filled in with either
-//     MandatoryLevelLow (IE)
-//     MandatoryLevelMedium(user), or
-//     MandatoryLevelHigh( Elevated Admin)
-//   On error, level remains unchanged
-HRESULT GetProcessIntegrityLevel(DWORD processID, MANDATORY_LEVEL* level);
-
-// Elevated processes need to be careful how they launch child processes
-// to avoid having them inherit too many credentials or not being able to
-// elevate their own IE processes normally.  Microsoft's advice from
-// http://msdn.microsoft.com/library/en-us/ietechcol/dnwebgen/protectedmode.asp
-// will launch a low integrity IE, but that IE cannot elevate properly since
-// it was running from the wrong token. The best method I can gather is to find
-// an existing process on the machine running at normal user rights, and launch
-// this process impersonating that token rather than trying to adjust token
-// privileges of the elevated token.  TODO(omaha): Implement and test this.
-HRESULT CreateProcessAsNormalUserFromElevatedAdmin(const TCHAR* commandline,
-    STARTUPINFO* startup_info, PROCESS_INFORMATION* process_info);
-
-// Starts a new elevated process. file_path specifies the program to be run.
-// If exit_code is not null, the function waits until the spawned process has
-// completed. The exit code of the process is returned therein.
-// If exit_code is null, the function will return after spawning the program
-// and will not wait for completion.
-// show_window is one of the SW_* constants to specify howw the windows is
-// opened.
-HRESULT RunElevated(const TCHAR* file_path, const TCHAR* parameters,
-    int show_window, DWORD* exit_code);
-
-// If there is no specific integrity level defined, return S_FALSE (1) and set
-// level to MandatoryLevelMedium (the Vista default)
-HRESULT GetFileOrFolderIntegrityLevel(const TCHAR* file,
-    MANDATORY_LEVEL* level, bool* and_children);
-
-// A level of MandatoryLevelUntrusted (0) will remove the integrity level for
-// this file and all children
-HRESULT SetFileOrFolderIntegrityLevel(const TCHAR* file,
-    MANDATORY_LEVEL level, bool and_children);
-
-// If there is no specific integrity level defined, return S_FALSE (1) and set
-// level to MandatoryLevelMedium (the Vista default)
-// root must be one of the 4 pre-defined roots: HKLM, HKCU, HKCR, HCU
-HRESULT GetRegKeyIntegrityLevel(HKEY root, const TCHAR* subkey,
-    MANDATORY_LEVEL* level, bool* and_children);
-
-// A level of MandatoryLevelUntrusted (0) will remove the integrity label
-// root must be one of the 4 pre-defined roots: HKLM, HKCU, HKCR, HCU
-HRESULT SetRegKeyIntegrityLevel(HKEY root, const TCHAR* subkey,
-    MANDATORY_LEVEL level, bool and_children);
-
-// Creates a security descriptor that can be used to make an object accessible
-// from the specified integrity level. When not running on Windows Vista or
-// in case of errors, the function returns NULL, which results in using
-// the default security descriptor.
-// The caller must take ownership of the returned security descriptor.
-// Mask will be added as an allowed ACE of the DACL.
-// For example, use MUTEX_ALL_ACCESS for shared mutexes.
-CSecurityDesc* CreateLowIntegritySecurityDesc(ACCESS_MASK mask);
-CSecurityDesc* CreateMediumIntegritySecurityDesc(ACCESS_MASK mask);
-
-// For Vista or later, add the low integrity SACL to an existing CSecurityDesc.
-HRESULT AddLowIntegritySaclToExistingDesc(CSecurityDesc* sd);
-
-}  // namespace vista_util
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_VISTAUTIL_H__
-
diff --git a/common/vistautil_unittest.cc b/common/vistautil_unittest.cc
deleted file mode 100644
index 17934d8..0000000
--- a/common/vistautil_unittest.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <shlobj.h>
-#include "omaha/common/vistautil.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-namespace vista_util {
-
-TEST(VistaUtilTest, IsUserAdmin) {
-  bool is_admin = !!::IsUserAnAdmin();
-  EXPECT_EQ(is_admin, vista_util::IsUserAdmin());
-}
-
-// Tests the code returns false if not Vista or later.
-TEST(VistaUtilTest, IsUACDisabled) {
-  bool is_uac_disabled = IsUACDisabled();
-  bool is_vista_or_later = vista_util::IsVistaOrLater();
-  if (!is_vista_or_later) {
-    EXPECT_FALSE(is_uac_disabled);
-  }
-}
-
-}  // namespace vista_util
-
-}  // namespace omaha
-
diff --git a/common/web_services_client.cc b/common/web_services_client.cc
new file mode 100644
index 0000000..5926a3c
--- /dev/null
+++ b/common/web_services_client.cc
@@ -0,0 +1,201 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/web_services_client.h"
+#include <atlstr.h>
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/update_request.h"
+#include "omaha/common/update_response.h"
+#include "omaha/net/cup_request.h"
+#include "omaha/net/network_config.h"
+#include "omaha/net/network_request.h"
+#include "omaha/net/simple_request.h"
+
+namespace omaha {
+
+WebServicesClient::WebServicesClient(bool is_machine)
+    : lock_(NULL),
+      is_machine_(is_machine) {
+}
+
+WebServicesClient::~WebServicesClient() {
+  CORE_LOG(L3, (_T("[WebServicesClient::~WebServicesClient]")));
+
+  delete &lock();
+  omaha::interlocked_exchange_pointer(&lock_, static_cast<Lockable*>(NULL));
+}
+
+HRESULT  WebServicesClient::Initialize(const CString& url,
+                                       const HeadersVector& headers,
+                                       bool use_cup) {
+  CORE_LOG(L3, (_T("[WebServicesClient::Initialize][%s][%d]"), url, use_cup));
+
+  omaha::interlocked_exchange_pointer(&lock_,
+                                      static_cast<Lockable*>(new LLock));
+  __mutexScope(lock());
+
+  url_ = url;
+
+  NetworkConfig* network_config = NULL;
+  NetworkConfigManager& network_manager = NetworkConfigManager::Instance();
+  HRESULT hr = network_manager.GetUserNetworkConfig(&network_config);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  const NetworkConfig::Session& session(network_config->session());
+
+  network_request_.reset(new NetworkRequest(session));
+
+  for (size_t i = 0; i < headers.size(); ++i) {
+    network_request_->AddHeader(headers[i].first, headers[i].second);
+  }
+
+  if (use_cup) {
+    network_request_->AddHttpRequest(new CupRequest(new SimpleRequest));
+  }
+  network_request_->AddHttpRequest(new SimpleRequest);
+  network_request_->set_num_retries(1);
+
+  return S_OK;
+}
+
+const Lockable& WebServicesClient::lock() const {
+  return *omaha::interlocked_exchange_pointer(&lock_, lock_);
+}
+
+CString WebServicesClient::url() const {
+  __mutexScope(lock());
+  return url_;
+}
+
+NetworkRequest* WebServicesClient::network_request() {
+  __mutexScope(lock());
+  return network_request_.get();
+}
+
+HRESULT WebServicesClient::Send(const xml::UpdateRequest* update_request,
+                                xml::UpdateResponse* update_response) {
+  CORE_LOG(L3, (_T("[WebServicesClient::Send]")));
+  ASSERT1(update_request);
+  ASSERT1(update_response);
+
+  if (!ConfigManager::Instance()->CanUseNetwork(is_machine_)) {
+    CORE_LOG(LE, (_T("[WebServicesClient::Send][network use prohibited]")));
+    return GOOPDATE_E_CANNOT_USE_NETWORK;
+  }
+
+  CString request_string;
+  HRESULT hr = update_request->Serialize(&request_string);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Serialize failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  ASSERT1(!request_string.IsEmpty());
+
+  // For security reasons, if there's tt_token in the request, we must
+  // set_preserve_protocol in network request to prevent it from replacing
+  // https with http scheme.
+  const bool need_preserve_https = update_request->has_tt_token();
+
+  return SendStringPreserveProtocol(need_preserve_https, &request_string,
+                                    update_response);
+}
+
+HRESULT WebServicesClient::SendString(const CString* request_string,
+                                      xml::UpdateResponse* update_response) {
+  CORE_LOG(L3, (_T("[WebServicesClient::SendString]")));
+  ASSERT1(request_string);
+  ASSERT1(update_response);
+
+  return SendStringPreserveProtocol(false, request_string, update_response);
+}
+
+HRESULT WebServicesClient::SendStringPreserveProtocol(
+    bool need_preserve_https,
+    const CString* request_string,
+    xml::UpdateResponse* update_response) {
+  CORE_LOG(L3, (_T("[WebServicesClient::SendStringPreserveProtocol]")));
+  ASSERT1(request_string);
+  ASSERT1(update_response);
+
+  CORE_LOG(L3, (_T("[sending web services request][%s]"), *request_string));
+
+  std::vector<uint8> response_buffer;
+
+  CString request_url = url();
+  ASSERT1(!need_preserve_https ||
+      String_StartsWith(request_url, kHttpsProtoScheme, true));
+  network_request_->set_preserve_protocol(need_preserve_https);
+  HRESULT hr = PostRequest(network_request_.get(), true,
+                           request_url, *request_string, &response_buffer);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[PostString failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  // The web services server is expected to reply with 200 OK if the
+  // transaction has been successful.
+  ASSERT1(is_http_success());
+  if (!is_http_success()) {
+    CORE_LOG(LE, (_T("[PostString returned success on a failed transaction]")));
+    return E_FAIL;
+  }
+
+  CString response_string = Utf8BufferToWideChar(response_buffer);
+  CORE_LOG(L3, (_T("[received web services response][%s]"), response_string));
+
+  hr = update_response->Deserialize(response_buffer);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[UpdateResponse::Deserialize failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+void WebServicesClient::Cancel() {
+  CORE_LOG(L3, (_T("[WebServicesClient::Cancel]")));
+  NetworkRequest* network_request(network_request());
+  if (network_request) {
+    network_request->Cancel();
+  }
+}
+
+void WebServicesClient::set_proxy_auth_config(const ProxyAuthConfig& config) {
+  ASSERT1(network_request());
+  network_request()->set_proxy_auth_config(config);
+}
+
+bool WebServicesClient::is_http_success() const {
+  return network_request_->http_status_code() == HTTP_STATUS_OK;
+}
+
+int WebServicesClient::http_status_code() const {
+  return network_request_->http_status_code();
+}
+
+CString WebServicesClient::http_trace() const {
+  return network_request_->trace();
+}
+
+}  // namespace omaha
diff --git a/common/web_services_client.h b/common/web_services_client.h
new file mode 100644
index 0000000..6ee00d9
--- /dev/null
+++ b/common/web_services_client.h
@@ -0,0 +1,115 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_WEB_SERVICES_CLIENT_H_
+#define OMAHA_COMMON_WEB_SERVICES_CLIENT_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include <utility>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/net/proxy_auth.h"
+
+namespace omaha {
+
+namespace xml {
+
+class UpdateRequest;
+class UpdateResponse;
+
+}  // namespace xml
+
+struct Lockable;
+class NetworkRequest;
+
+typedef std::vector<std::pair<CString, CString> > HeadersVector;
+
+class WebServicesClientInterface {
+ public:
+  virtual ~WebServicesClientInterface() {}
+
+  virtual HRESULT Send(const xml::UpdateRequest* update_request,
+                       xml::UpdateResponse* update_response) = 0;
+
+  virtual HRESULT SendString(const CString* request_buffer,
+                             xml::UpdateResponse* response_buffer) = 0;
+
+  virtual void Cancel() = 0;
+
+  virtual void set_proxy_auth_config(const ProxyAuthConfig& config) = 0;
+
+  // Returns true if the http transaction completed with 200 OK.
+  virtual bool is_http_success() const = 0;
+
+  // Returns the last http request status code.
+  virtual int http_status_code() const = 0;
+
+  // Returns http request trace (for logging).
+  virtual CString http_trace() const = 0;
+};
+
+// Defines a class to send and receive protocol requests.
+class WebServicesClient : public WebServicesClientInterface {
+ public:
+  explicit WebServicesClient(bool is_machine);
+  virtual ~WebServicesClient();
+
+  HRESULT  Initialize(const CString& url,
+                      const HeadersVector& headers,
+                      bool use_cup);
+
+  virtual HRESULT Send(const xml::UpdateRequest* update_request,
+                       xml::UpdateResponse* update_response);
+
+  virtual HRESULT SendString(const CString* request_buffer,
+                             xml::UpdateResponse* update_response);
+
+  virtual void Cancel();
+
+  virtual void set_proxy_auth_config(const ProxyAuthConfig& proxy_auth_config);
+
+  virtual bool is_http_success() const;
+
+  virtual int http_status_code() const;
+
+  virtual CString http_trace() const;
+
+ private:
+  HRESULT SendStringPreserveProtocol(bool need_preserve_https,
+                                     const CString* request_buffer,
+                                     xml::UpdateResponse* update_response);
+
+  const Lockable& lock() const;
+
+  CString url() const;
+
+  NetworkRequest* network_request();
+
+  mutable Lockable* volatile lock_;   // Owned by this instance.
+
+  const bool is_machine_;
+  CString url_;
+
+  scoped_ptr<NetworkRequest> network_request_;
+
+  friend class WebServicesClientTest;
+  DISALLOW_EVIL_CONSTRUCTORS(WebServicesClient);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_WEB_SERVICES_CLIENT_H_
diff --git a/common/web_services_client_unittest.cc b/common/web_services_client_unittest.cc
new file mode 100644
index 0000000..5c11e30
--- /dev/null
+++ b/common/web_services_client_unittest.cc
@@ -0,0 +1,245 @@
+// Copyright 2008-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "base/scoped_ptr.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/string.h"
+#include "omaha/base/vista_utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/update_request.h"
+#include "omaha/common/update_response.h"
+#include "omaha/common/web_services_client.h"
+#include "omaha/net/network_request.h"
+#include "omaha/testing/unit_test.h"
+
+using ::testing::_;
+
+namespace omaha {
+
+// TODO(omaha): test the machine case.
+
+class WebServicesClientTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    EXPECT_HRESULT_SUCCEEDED(
+        ConfigManager::Instance()->GetUpdateCheckUrl(&update_check_url_));
+
+    web_service_client_.reset(new WebServicesClient(false));
+
+    update_request_.reset(xml::UpdateRequest::Create(false,
+                                                     _T("unittest_sessionid"),
+                                                     _T("unittest_instsource"),
+                                                     CString()));
+    update_response_.reset(xml::UpdateResponse::Create());
+  }
+
+  virtual void TearDown() {
+    web_service_client_.reset();
+  }
+
+  NetworkRequest* network_request() const {
+    return web_service_client_->network_request();
+  }
+
+  CString update_check_url_;
+
+  scoped_ptr<WebServicesClient> web_service_client_;
+  scoped_ptr<xml::UpdateRequest> update_request_;
+  scoped_ptr<xml::UpdateResponse> update_response_;
+};
+
+TEST_F(WebServicesClientTest, Send) {
+  EXPECT_HRESULT_SUCCEEDED(web_service_client_->Initialize(update_check_url_,
+                                                           HeadersVector(),
+                                                           false));
+
+  // Test sending a user update check request.
+  EXPECT_HRESULT_SUCCEEDED(web_service_client_->Send(update_request_.get(),
+                                                     update_response_.get()));
+  EXPECT_TRUE(web_service_client_->is_http_success());
+
+  xml::response::Response response(update_response_->response());
+  EXPECT_STREQ(_T("3.0"), response.protocol);
+
+  NetworkRequest* network_request(network_request());
+
+  CString cookie;
+  EXPECT_HRESULT_FAILED(network_request->QueryHeadersString(
+      WINHTTP_QUERY_FLAG_REQUEST_HEADERS | WINHTTP_QUERY_COOKIE,
+      WINHTTP_HEADER_NAME_BY_INDEX,
+      &cookie));
+  EXPECT_TRUE(cookie.IsEmpty());
+
+  CString etag;
+  EXPECT_HRESULT_FAILED(network_request->QueryHeadersString(
+      WINHTTP_QUERY_ETAG, WINHTTP_HEADER_NAME_BY_INDEX, &etag));
+  EXPECT_TRUE(etag.IsEmpty());
+}
+
+TEST_F(WebServicesClientTest, SendUsingCup) {
+  EXPECT_HRESULT_SUCCEEDED(web_service_client_->Initialize(update_check_url_,
+                                                           HeadersVector(),
+                                                           true));
+
+  // Test sending a user update check request.
+  EXPECT_HRESULT_SUCCEEDED(web_service_client_->Send(update_request_.get(),
+                                                     update_response_.get()));
+  EXPECT_TRUE(web_service_client_->is_http_success());
+
+  xml::response::Response response(update_response_->response());
+  EXPECT_STREQ(_T("3.0"), response.protocol);
+
+  NetworkRequest* network_request(network_request());
+
+  CString no_request_age_header;
+  network_request->QueryHeadersString(
+      WINHTTP_QUERY_CUSTOM | WINHTTP_QUERY_FLAG_REQUEST_HEADERS,
+      _T("X-RequestAge"),
+      &no_request_age_header);
+
+  EXPECT_STREQ(_T(""), no_request_age_header);
+
+  // A CUP transaction has either a request or a response CUP cookie and
+  // the ETag response header.
+  CString request_cookie;
+  network_request->QueryHeadersString(
+      WINHTTP_QUERY_COOKIE | WINHTTP_QUERY_FLAG_REQUEST_HEADERS,
+      WINHTTP_HEADER_NAME_BY_INDEX,
+      &request_cookie);
+  const bool has_cup_request_cookie = request_cookie.Find(_T("c=")) != -1;
+
+  CString response_cookie;
+  network_request->QueryHeadersString(WINHTTP_QUERY_SET_COOKIE,
+                                      WINHTTP_HEADER_NAME_BY_INDEX,
+                                      &response_cookie);
+  const bool has_cup_response_cookie = response_cookie.Find(_T("c=")) != -1;
+
+  EXPECT_TRUE(has_cup_request_cookie || has_cup_response_cookie);
+
+  CString etag;
+  EXPECT_HRESULT_SUCCEEDED(network_request->QueryHeadersString(
+      WINHTTP_QUERY_ETAG, WINHTTP_HEADER_NAME_BY_INDEX, &etag));
+  EXPECT_FALSE(etag.IsEmpty());
+}
+
+TEST_F(WebServicesClientTest, SendForcingHttps) {
+  // Skips the test if the update check URL is not https.
+  if (!String_StartsWith(update_check_url_, kHttpsProtoScheme, true)) {
+    return;
+  }
+
+  EXPECT_HRESULT_SUCCEEDED(web_service_client_->Initialize(update_check_url_,
+                                                           HeadersVector(),
+                                                           true));
+
+  EXPECT_TRUE(update_request_->IsEmpty());
+
+  // Adds an application with non-empty tt_token to the update request.
+  // This should prevent the network stack from replacing https with
+  // CUP protocol.
+  xml::request::App app;
+  app.app_id = _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}");
+  app.iid    = _T("{00000000-0000-0000-0000-000000000000}");
+  app.update_check.is_valid = true;
+  app.update_check.tt_token = _T("Test TT token");
+  update_request_->AddApp(app);
+
+  EXPECT_FALSE(update_request_->IsEmpty());
+  EXPECT_TRUE(update_request_->has_tt_token());
+
+  EXPECT_HRESULT_SUCCEEDED(web_service_client_->Send(update_request_.get(),
+                                                    update_response_.get()));
+  EXPECT_TRUE(web_service_client_->is_http_success());
+
+  // Do a couple of sanity checks on the parsing of the response.
+  xml::response::Response response(update_response_->response());
+  EXPECT_STREQ(_T("3.0"), response.protocol);
+  ASSERT_EQ(1, response.apps.size());
+  EXPECT_STREQ(_T("error-unknownApplication"), response.apps[0].status);
+}
+
+TEST_F(WebServicesClientTest, SendWithCustomHeader) {
+  HeadersVector headers;
+  headers.push_back(std::make_pair(_T("X-RequestAge"), _T("200")));
+
+  EXPECT_HRESULT_SUCCEEDED(web_service_client_->Initialize(update_check_url_,
+                                                           headers,
+                                                           true));
+
+  EXPECT_HRESULT_SUCCEEDED(web_service_client_->Send(update_request_.get(),
+                                                     update_response_.get()));
+  EXPECT_TRUE(web_service_client_->is_http_success());
+
+  xml::response::Response response(update_response_->response());
+  EXPECT_STREQ(_T("3.0"), response.protocol);
+
+  NetworkRequest* network_request(network_request());
+
+  CString request_age_header;
+  network_request->QueryHeadersString(
+      WINHTTP_QUERY_CUSTOM | WINHTTP_QUERY_FLAG_REQUEST_HEADERS,
+      _T("X-RequestAge"),
+      &request_age_header);
+
+  EXPECT_STREQ(_T("200"), request_age_header);
+}
+
+TEST_F(WebServicesClientTest, SendString) {
+  EXPECT_HRESULT_SUCCEEDED(web_service_client_->Initialize(update_check_url_,
+                                                           HeadersVector(),
+                                                           false));
+
+  // Test sending a user update check request.
+  CString request_string =
+    _T("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
+    _T("<request protocol=\"3.0\" testsource=\"dev\"></request>");
+  scoped_ptr<xml::UpdateResponse> response(xml::UpdateResponse::Create());
+  EXPECT_HRESULT_SUCCEEDED(web_service_client_->SendString(&request_string,
+                                                           response.get()));
+  EXPECT_TRUE(web_service_client_->is_http_success());
+}
+
+TEST_F(WebServicesClientTest, SendStringWithCustomHeader) {
+  HeadersVector headers;
+  headers.push_back(std::make_pair(_T("X-FooBar"), _T("424")));
+
+  EXPECT_HRESULT_SUCCEEDED(web_service_client_->Initialize(update_check_url_,
+                                                           headers,
+                                                           false));
+
+  // Test sending a user update check request.
+  CString request_string =
+    _T("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
+    _T("<request protocol=\"3.0\" testsource=\"dev\"></request>");
+  scoped_ptr<xml::UpdateResponse> response(xml::UpdateResponse::Create());
+  EXPECT_HRESULT_SUCCEEDED(web_service_client_->SendString(&request_string,
+                                                           response.get()));
+  EXPECT_TRUE(web_service_client_->is_http_success());
+
+  NetworkRequest* network_request(network_request());
+
+  CString foobar_header;
+  network_request->QueryHeadersString(
+      WINHTTP_QUERY_CUSTOM | WINHTTP_QUERY_FLAG_REQUEST_HEADERS,
+      _T("X-FooBar"),
+      &foobar_header);
+
+  EXPECT_STREQ(_T("424"), foobar_header);
+}
+
+}  // namespace omaha
+
diff --git a/common/webplugin_utils.cc b/common/webplugin_utils.cc
new file mode 100644
index 0000000..168199b
--- /dev/null
+++ b/common/webplugin_utils.cc
@@ -0,0 +1,224 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/webplugin_utils.h"
+
+#include "omaha/base/app_util.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/string.h"
+#include "omaha/base/system.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/xml_utils.h"
+#include "omaha/common/command_line_builder.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/lang.h"
+#include "omaha/net/browser_request.h"
+#include "omaha/net/cup_request.h"
+#include "omaha/net/network_request.h"
+#include "omaha/net/simple_request.h"
+
+namespace omaha {
+
+namespace webplugin_utils {
+
+HRESULT BuildOneClickRequestString(const CommandLineArgs& args,
+                                   CString* request_str) {
+  if (NULL == request_str) {
+    return E_INVALIDARG;
+  }
+
+  // If we're not /webplugin or the urldomain is empty, something's wrong.
+  if (args.mode != COMMANDLINE_MODE_WEBPLUGIN ||
+      args.webplugin_urldomain.IsEmpty()) {
+    return E_UNEXPECTED;
+  }
+
+  const TCHAR* request_string_template = _T("?du=%s&args=%s");
+  CString request;
+
+  CString urldomain_escaped;
+  CString pluginargs_escaped;
+
+  StringEscape(args.webplugin_urldomain, false, &urldomain_escaped);
+  StringEscape(args.webplugin_args, false, &pluginargs_escaped);
+
+  SafeCStringFormat(&request, request_string_template,
+                    urldomain_escaped,
+                    pluginargs_escaped);
+
+  *request_str = request;
+  return S_OK;
+}
+
+HRESULT IsLanguageSupported(const CString& webplugin_args) {
+  CString cmd_line;
+  SafeCStringFormat(&cmd_line, _T("gu.exe %s"), webplugin_args);
+  CommandLineArgs parsed_args;
+  HRESULT hr = ParseCommandLine(cmd_line, &parsed_args);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[ParseCommandLine failed][0x%08x]"), hr));
+    return hr;
+  }
+
+
+  if (!lang::IsLanguageSupported(parsed_args.extra.language)) {
+    CORE_LOG(LE, (_T("Language not supported][%s]"),
+                  parsed_args.extra.language));
+    return GOOPDATE_E_ONECLICK_LANGUAGE_NOT_SUPPORTED;
+  }
+
+  return S_OK;
+}
+
+HRESULT BuildOneClickWorkerArgs(const CommandLineArgs& args,
+                                CString* oneclick_args) {
+  ASSERT1(oneclick_args);
+
+  // Since this is being called via WebPlugin only, we can rebuild the
+  // command line arguments from the valid params we can send on.
+  // For example, the web plugin will not send crash_cmd or debug_cmd
+  // or reg_server or unreg_server so we don't have to worry about those here.
+  CString cmd_line_args;
+  CommandLineArgs webplugin_cmdline_args;
+
+  // ParseCommandLine assumes the first argument is the program being run.
+  // Don't want to enforce that constraint on our callers, so we prepend with a
+  // fake exe name.
+  CString args_to_parse;
+  SafeCStringFormat(&args_to_parse, _T("%s %s"),
+                    kOmahaShellFileName,
+                    args.webplugin_args);
+
+  // Parse the arguments we received as the second parameter to /webplugin.
+  HRESULT hr = ParseCommandLine(args_to_parse, &webplugin_cmdline_args);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Silent and other non-standard installs could be malicious. Prevent them.
+  if (webplugin_cmdline_args.mode != COMMANDLINE_MODE_INSTALL) {
+    return E_INVALIDARG;
+  }
+  if (webplugin_cmdline_args.is_silent_set ||
+      webplugin_cmdline_args.is_eula_required_set) {
+    return E_INVALIDARG;
+  }
+
+  CommandLineBuilder builder(COMMANDLINE_MODE_INSTALL);
+  builder.set_extra_args(webplugin_cmdline_args.extra_args_str);
+
+  // We expect this value from the plugin.
+  ASSERT1(!args.install_source.IsEmpty());
+  if (args.install_source.IsEmpty()) {
+    return E_INVALIDARG;
+  }
+
+  builder.set_install_source(args.install_source);
+
+  *oneclick_args = builder.GetCommandLineArgs();
+
+  return S_OK;
+}
+
+// It is important that current_goopdate_path be the version path and not the
+// Update\ path.
+HRESULT CopyGoopdateToTempDir(const CPath& current_goopdate_path,
+                              CPath* goopdate_temp_path) {
+  ASSERT1(goopdate_temp_path);
+
+  // Create a unique directory in the user's temp directory.
+  TCHAR pathbuf[MAX_PATH] = {0};
+  DWORD ret = ::GetTempPath(arraysize(pathbuf), pathbuf);
+  if (0 == ret) {
+    return HRESULTFromLastError();
+  }
+  if (ret >= arraysize(pathbuf)) {
+    return E_FAIL;
+  }
+
+  GUID guid = GUID_NULL;
+  HRESULT hr = ::CoCreateGuid(&guid);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CString guid_str = GuidToString(guid);
+  CPath temp_path = pathbuf;
+  temp_path.Append(guid_str);
+  temp_path.Canonicalize();
+
+  hr = CreateDir(temp_path, NULL);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = File::CopyTree(current_goopdate_path, temp_path, true);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CORE_LOG(L2, (_T("[CopyGoopdateToTempDir][temp_path = %s]"), temp_path));
+  *goopdate_temp_path = temp_path;
+  return S_OK;
+}
+
+HRESULT DoOneClickInstall(const CommandLineArgs& args) {
+  CString cmd_line_args;
+  HRESULT hr = BuildOneClickWorkerArgs(args, &cmd_line_args);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[BuildOneClickWorkerArgs failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  CORE_LOG(L2, (_T("[DoOneClickInstall][cmd_line_args: %s]"), cmd_line_args));
+
+  // Check if we're running from the machine dir.
+  // If we're not, we must be running from user directory since OneClick only
+  // works against installed versions of Omaha.
+  CPath current_goopdate_path(app_util::GetCurrentModuleDirectory());
+  CPath goopdate_temp_path;
+  hr = CopyGoopdateToTempDir(current_goopdate_path, &goopdate_temp_path);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CopyGoopdateToTempDir failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  CPath goopdate_temp_exe_path = goopdate_temp_path;
+  goopdate_temp_exe_path.Append(kOmahaShellFileName);
+
+  // Launch goopdate again with the updated command line arguments.
+  hr = System::ShellExecuteProcess(goopdate_temp_exe_path,
+                                   cmd_line_args,
+                                   NULL,
+                                   NULL);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[ShellExecuteProcess failed][%s][0x%08x]"),
+                  goopdate_temp_exe_path, hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+}  // namespace webplugin_utils
+
+}  // namespace omaha
+
diff --git a/common/webplugin_utils.h b/common/webplugin_utils.h
new file mode 100644
index 0000000..adbb2ea
--- /dev/null
+++ b/common/webplugin_utils.h
@@ -0,0 +1,53 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_WEBPLUGIN_UTILS_H__
+#define OMAHA_COMMON_WEBPLUGIN_UTILS_H__
+
+#include <windows.h>
+#include <atlpath.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+#include "omaha/common/command_line.h"
+#include "omaha/common/config_manager.h"
+
+namespace omaha {
+
+namespace webplugin_utils {
+
+// Parses the arguments, extracts the language parameter, and verifies that we
+// support the requested language.
+HRESULT IsLanguageSupported(const CString& webplugin_args);
+
+// Copies required Goopdate files to a temp location before installing.
+HRESULT CopyGoopdateToTempDir(const CPath& current_goopdate_path,
+                              CPath* goopdate_temp_path);
+
+// Launches google_update.exe based on parameters sent with /webplugin.
+HRESULT DoOneClickInstall(const CommandLineArgs& args);
+
+// Creates request string for the webplugin URL check webservice call.
+HRESULT BuildOneClickRequestString(const CommandLineArgs& args,
+                                   CString* request_str);
+
+// Builds up the command line arguments to re-launch google_update.exe
+// when called with /pi.
+HRESULT BuildOneClickWorkerArgs(const CommandLineArgs& args, CString* args_out);
+
+}  // namespace webplugin_utils
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_WEBPLUGIN_UTILS_H__
diff --git a/common/webplugin_utils_unittest.cc b/common/webplugin_utils_unittest.cc
new file mode 100644
index 0000000..c1c4e89
--- /dev/null
+++ b/common/webplugin_utils_unittest.cc
@@ -0,0 +1,156 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/app_util.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/webplugin_utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace webplugin_utils {
+
+#define YOUTUBEUPLOADEREN_TAG \
+    _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&") \
+    _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
+
+
+TEST(WebPluginUtilsTest, BuildOneClickRequestString_NullOutParam) {
+  CommandLineArgs args;
+  EXPECT_EQ(E_INVALIDARG, BuildOneClickRequestString(args, NULL));
+}
+
+TEST(WebPluginUtilsTest, BuildOneClickRequestString_WrongArgs) {
+  CommandLineArgs args;
+  CString request;
+  EXPECT_EQ(E_UNEXPECTED, BuildOneClickRequestString(args, &request));
+}
+
+TEST(WebPluginUtilsTest, BuildOneClickRequestString_NoUrlDomain) {
+  CommandLineArgs args;
+  CString request;
+
+  args.mode = COMMANDLINE_MODE_WEBPLUGIN;
+  EXPECT_EQ(E_UNEXPECTED, BuildOneClickRequestString(args, &request));
+}
+
+TEST(WebPluginUtilsTest, BuildOneClickRequestString_Valid) {
+  CommandLineArgs args;
+  CString request;
+
+  args.mode = COMMANDLINE_MODE_WEBPLUGIN;
+  args.webplugin_urldomain = _T("http://www.google.com/");
+  args.webplugin_args = _T("/install \"appguid=")
+      _T("{8A69D345-D564-463c-AFF1-A69D9E530F96}")
+      _T("&appname=Google Chrome&needsadmin=true&lang=en\"");
+  EXPECT_EQ(S_OK, BuildOneClickRequestString(args, &request));
+
+  EXPECT_STREQ(_T("?du=http://www.google.com/&args=/install%20")
+               _T("%22appguid=%7B8A69D345-D564-463c-AFF1-A69D9E530F96")
+               _T("%7D%26appname=Google%20Chrome%26needsadmin=true%26")
+               _T("lang=en%22"),
+               request);
+}
+
+TEST(WebPluginUtilsTest, BuildOneClickWorkerArgs_Valid) {
+  CommandLineArgs args;
+  CString oneclick_args;
+
+  args.install_source = _T("oneclick");
+  args.webplugin_args = _T("/install \"appguid=")
+      _T("{8A69D345-D564-463c-AFF1-A69D9E530F96}")
+      _T("&appname=Google Chrome&needsadmin=true&lang=en\"");
+  EXPECT_EQ(S_OK, BuildOneClickWorkerArgs(args, &oneclick_args));
+
+  EXPECT_STREQ(_T("/install ")
+               _T("\"appguid={8A69D345-D564-463c-AFF1-A69D9E530F96}")
+               _T("&appname=Google Chrome&needsadmin=true&lang=en\" ")
+               _T("/installsource oneclick"),
+               oneclick_args);
+}
+
+// This tests valid command line args that are not valid to be sent through
+// to google_update.exe (e.g. /install).
+TEST(WebPluginUtilsTest, BuildOneClickWorkerArgs_Invalid) {
+  CommandLineArgs args;
+  CString oneclick_args;
+
+  args.install_source = _T("oneclick");
+
+  args.webplugin_args = _T("/handoff ") YOUTUBEUPLOADEREN_TAG;
+  EXPECT_EQ(E_INVALIDARG, BuildOneClickWorkerArgs(args, &oneclick_args));
+
+  args.webplugin_args = _T("/regserver");
+  EXPECT_EQ(E_INVALIDARG, BuildOneClickWorkerArgs(args, &oneclick_args));
+
+  args.webplugin_args = _T("/unregserver");
+  EXPECT_EQ(E_INVALIDARG, BuildOneClickWorkerArgs(args, &oneclick_args));
+
+  args.webplugin_args = _T("/install ") YOUTUBEUPLOADEREN_TAG _T(" /silent");
+  EXPECT_EQ(E_INVALIDARG, BuildOneClickWorkerArgs(args, &oneclick_args));
+}
+
+TEST(WebPluginUtilsTest, CopyGoopdateToTempDir) {
+  CPath current_goopdate_path(app_util::GetCurrentModuleDirectory());
+  current_goopdate_path.Append(_T("unittest_support\\omaha_1.3.x\\"));
+  CPath goopdate_temp_path;
+  ASSERT_SUCCEEDED(CopyGoopdateToTempDir(current_goopdate_path,
+                                         &goopdate_temp_path));
+
+  std::vector<CString> files;
+  EXPECT_HRESULT_SUCCEEDED(FindFilesEx(goopdate_temp_path, _T("*.*"), &files));
+
+  EXPECT_EQ(3, files.size());
+
+  std::map<CString, int> files_map;
+  for (size_t file_index = 0; file_index < files.size(); ++file_index) {
+    files_map[files[file_index]] = 1;
+  }
+
+  EXPECT_TRUE(files_map.find(_T("GoogleUpdate.exe")) != files_map.end());
+  EXPECT_TRUE(files_map.find(_T("goopdate.dll")) != files_map.end());
+  EXPECT_TRUE(files_map.find(_T("goopdateres_en.dll")) != files_map.end());
+
+  EXPECT_HRESULT_SUCCEEDED(DeleteDirectory(goopdate_temp_path));
+}
+
+TEST(WebPluginUtilsTest, IsLanguageSupported_InvalidArgs) {
+  CString args = _T("/en");
+  EXPECT_FAILED(IsLanguageSupported(args));
+}
+
+TEST(WebPluginUtilsTest, IsLanguageSupported_LangOK) {
+  CString args = _T("/install \"appguid=")
+                 _T("{8A69D345-D564-463c-AFF1-A69D9E530F96}")
+                 _T("&appname=Google Chrome&needsadmin=true&lang=en\"");
+  EXPECT_SUCCEEDED(IsLanguageSupported(args));
+}
+
+TEST(WebPluginUtilsTest, IsLanguageSupported_LangNotFound) {
+  CString args = _T("/install \"appguid=")
+                 _T("{8A69D345-D564-463c-AFF1-A69D9E530F96}")
+                 _T("&appname=Google Chrome&needsadmin=true&lang=zz\"");
+
+  EXPECT_EQ(GOOPDATE_E_ONECLICK_LANGUAGE_NOT_SUPPORTED,
+            IsLanguageSupported(args));
+}
+
+}  // namespace webplugin_utils
+
+}  // namespace omaha
+
diff --git a/common/window_utils.cc b/common/window_utils.cc
deleted file mode 100644
index 1a97ac2..0000000
--- a/common/window_utils.cc
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/window_utils.h"
-
-#include "base/basictypes.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/string.h"
-
-namespace omaha {
-
-namespace {
-
-struct FindProcessWindowsRecord {
-  uint32 process_id;
-  uint32 window_flags;
-  CSimpleArray<HWND>* windows;
-};
-
-BOOL CALLBACK FindProcessWindowsEnumProc(HWND hwnd, LPARAM lparam) {
-  FindProcessWindowsRecord* enum_record =
-      reinterpret_cast<FindProcessWindowsRecord*>(lparam);
-  ASSERT1(enum_record);
-
-  DWORD process_id = 0;
-  ::GetWindowThreadProcessId(hwnd, &process_id);
-
-  // Only count this window if it is in the right process
-  // and it satisfies all specified window requirements.
-  if (enum_record->process_id != process_id) {
-    return true;
-  }
-  if ((enum_record->window_flags & kWindowMustBeTopLevel) &&
-      ::GetParent(hwnd)) {
-    return true;
-  }
-
-  if ((enum_record->window_flags & kWindowMustHaveSysMenu) &&
-      !(GetWindowLong(hwnd, GWL_STYLE) & WS_SYSMENU)) {
-    return true;
-  }
-
-  if ((enum_record->window_flags & kWindowMustBeVisible) &&
-      !::IsWindowVisible(hwnd)) {
-    return true;
-  }
-
-  enum_record->windows->Add(hwnd);
-  return true;
-}
-
-}  // namespace
-
-bool WindowUtils::FindProcessWindows(uint32 process_id,
-                                     uint32 window_flags,
-                                     CSimpleArray<HWND>* windows) {
-  ASSERT1(windows);
-  windows->RemoveAll();
-  FindProcessWindowsRecord enum_record = {0};
-  enum_record.process_id = process_id;
-  enum_record.window_flags = window_flags;
-  enum_record.windows = windows;
-  ::EnumWindows(FindProcessWindowsEnumProc,
-                reinterpret_cast<LPARAM>(&enum_record));
-  int num_windows = enum_record.windows->GetSize();
-  return num_windows > 0;
-}
-
-void WindowUtils::MakeWindowForeground(HWND wnd) {
-  if (!IsWindowVisible(wnd)) {
-    // If the window is hidden and we call SetWindowPos with SWP_SHOWWINDOW
-    // then the window will be visible.
-    // If the caller wants it visible they should do it themselves first.
-    return;
-  }
-  if (!SetWindowPos(wnd,
-                    HWND_TOP,
-                    0,
-                    0,
-                    0,
-                    0,
-                    SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW)) {
-    UTIL_LOG(LE, (_T("[WindowUtils::MakeWindowForeground]")
-                  _T("[SetWindowPos failed][0x%08x]"), HRESULTFromLastError()));
-  }
-}
-
-bool WindowUtils::IsMainWindow(HWND wnd) {
-  return NULL == ::GetParent(wnd) && IsWindowVisible(wnd);
-}
-
-bool WindowUtils::HasSystemMenu(HWND wnd) {
-  return (GetWindowLong(wnd, GWL_STYLE) & WS_SYSMENU) != 0;
-}
-
-}  // namespace omaha
-
diff --git a/common/window_utils.h b/common/window_utils.h
deleted file mode 100644
index 4f95ca8..0000000
--- a/common/window_utils.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2004-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#ifndef OMAHA_COMMON_WINDOW_UTILS_H__
-#define OMAHA_COMMON_WINDOW_UTILS_H__
-
-#include <atlcoll.h>
-#include "base/basictypes.h"
-
-namespace omaha {
-
-// Flags for window requirements.
-const uint32 kWindowMustBeTopLevel  =  0x00000001;
-const uint32 kWindowMustHaveSysMenu =  0x00000002;
-const uint32 kWindowMustBeVisible   =  0x00000004;
-
-class WindowUtils {
- public:
-  // Finds all the primary windows owned by the given process. For the
-  // purposes of this function, primary windows are top-level, have a system
-  // menu, and are visible.
-  static bool FindProcessWindows(uint32 process_id,
-                                 uint32 window_flags,
-                                 CSimpleArray<HWND>* windows);
-
-  // Forces the window to the foreground.
-  static void MakeWindowForeground(HWND wnd);
-
-  // Returns true if the window is the "main window" of a process:
-  // if it's Visible, and Top Level
-  static bool IsMainWindow(HWND wnd);
-
-  // Returns true if the window has a System Menu
-  static bool HasSystemMenu(HWND wnd);
-
- private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(WindowUtils);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_WINDOW_UTILS_H__
diff --git a/common/wmi_query.cc b/common/wmi_query.cc
deleted file mode 100644
index 64c5f62..0000000
--- a/common/wmi_query.cc
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include "omaha/common/wmi_query.h"
-
-#include <atlcomcli.h>
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-WmiQuery::WmiQuery() : at_end_(true) {
-}
-
-WmiQuery::~WmiQuery() {
-}
-
-HRESULT WmiQuery::Connect(const TCHAR* resource) {
-  UTIL_LOG(L6, (_T("[WmiQuery::Connect][resource=%s]"), resource));
-  ASSERT1(resource && *resource);
-
-  CComBSTR object_path;
-  HRESULT hr = object_path.Append(resource);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = wbem_.CoCreateInstance(__uuidof(WbemLocator));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // Connect to WMI through the IWbemLocator::ConnectServer method. This
-  // call can block up to 2 minutes on XP or indefinitely on Windows 2000 if
-  // the server is broken.
-  hr = wbem_->ConnectServer(object_path,                      // object path
-                            NULL,                             // username
-                            NULL,                             // password
-                            NULL,                             // locale
-                            WBEM_FLAG_CONNECT_USE_MAX_WAIT,   // security flags
-                            NULL,                             // authority
-                            0,                                // context
-                            &service_);                       // namespace
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // Set security levels on the proxy.
-  hr = ::CoSetProxyBlanket(service_,
-                           RPC_C_AUTHN_WINNT,
-                           RPC_C_AUTHZ_NONE,
-                           NULL,
-                           RPC_C_AUTHN_LEVEL_CALL,
-                           RPC_C_IMP_LEVEL_IMPERSONATE,
-                           NULL,
-                           EOAC_NONE);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT WmiQuery::Query(const TCHAR* query) {
-  UTIL_LOG(L6, (_T("[WmiQuery::Query][query=%s]"), query));
-  ASSERT1(query && *query);
-
-  CComBSTR query_language, query_string;
-  HRESULT hr = query_language.Append(_T("WQL"));
-  if (FAILED(hr)) {
-    return hr;
-  }
-  hr = query_string.Append(query);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  uint32 flags = WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY;
-  hr = service_->ExecQuery(query_language,
-                           query_string,
-                           flags,
-                           NULL,
-                           &enumerator_);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  at_end_ = false;
-  return Next();
-}
-
-HRESULT WmiQuery::Next() {
-  UTIL_LOG(L6, (_T("[WmiQuery::Next]")));
-
-  ASSERT1(!at_end_);
-
-  ULONG ret = 0;
-  HRESULT hr = enumerator_->Next(WBEM_INFINITE, 1, &obj_, &ret);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  at_end_ = ret == 0;
-  return S_OK;
-}
-
-bool WmiQuery::AtEnd() {
-  return at_end_;
-}
-
-HRESULT WmiQuery::GetValue(const TCHAR* name, CComVariant* value) {
-  ASSERT1(name && *name);
-  ASSERT1(value);
-  ASSERT1(!at_end_ && obj_);
-
-  value->Clear();
-
-  CComBSTR name_string;
-  HRESULT hr = name_string.Append(name);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  hr = obj_->Get(name_string, 0, value, 0, 0);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT WmiQuery::GetValue(const TCHAR* name, CString* value) {
-  ASSERT1(name && *name);
-  ASSERT1(value);
-
-  CComVariant var;
-  HRESULT hr = GetValue(name, &var);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  ASSERT1(V_VT(&var) == VT_BSTR);
-  value->SetString(var.bstrVal);
-  return S_OK;
-}
-
-HRESULT WmiQuery::GetValue(const TCHAR* name, bool* value) {
-  ASSERT1(name && *name);
-  ASSERT1(value);
-
-  CComVariant var;
-  HRESULT hr = GetValue(name, &var);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  ASSERT1(V_VT(&var) == VT_BOOL);
-  *value = var.boolVal != 0;
-  return S_OK;
-}
-
-HRESULT WmiQuery::GetValue(const TCHAR* name, int* value) {
-  ASSERT1(name && *name);
-  ASSERT1(value);
-
-  CComVariant var;
-  HRESULT hr = GetValue(name, &var);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  ASSERT1(V_VT(&var) == VT_I4);
-  *value = var.lVal;
-  return S_OK;
-}
-
-HRESULT WmiQuery::GetValue(const TCHAR* name, uint32* value) {
-  ASSERT1(name && *name);
-  ASSERT1(value);
-
-  CComVariant var;
-  HRESULT hr = GetValue(name, &var);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  ASSERT1(V_VT(&var) == VT_UI4);
-  *value = var.ulVal;
-  return S_OK;
-}
-
-}  // namespace omaha
-
diff --git a/common/wmi_query_unittest.cc b/common/wmi_query_unittest.cc
deleted file mode 100644
index 83aaa4a..0000000
--- a/common/wmi_query_unittest.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/wmi_query.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(WmiQueryTest, WmiQuery) {
-  WmiQuery wq;
-  ASSERT_HRESULT_SUCCEEDED(wq.Connect(_T("root\\CIMV2")));
-  ASSERT_HRESULT_SUCCEEDED(wq.Query(_T("select * from Win32_OperatingSystem")));
-
-  CString manufacturer;
-  EXPECT_HRESULT_SUCCEEDED(wq.GetValue(_T("Manufacturer"), &manufacturer));
-  EXPECT_STREQ(_T("Microsoft Corporation"), manufacturer);
-
-  // Expect a retail build of the OS.
-  bool is_debug(true);
-  EXPECT_HRESULT_SUCCEEDED(wq.GetValue(_T("Debug"), &is_debug));
-  EXPECT_FALSE(is_debug);
-
-  int max_number_of_processes(0);
-  EXPECT_HRESULT_SUCCEEDED(wq.GetValue(_T("MaxNumberOfProcesses"),
-                           &max_number_of_processes));
-  EXPECT_EQ(-1, max_number_of_processes);
-}
-
-}  // namespace omaha
-
diff --git a/common/xml_const.cc b/common/xml_const.cc
new file mode 100644
index 0000000..0122a2b
--- /dev/null
+++ b/common/xml_const.cc
@@ -0,0 +1,137 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/xml_const.h"
+
+namespace omaha {
+
+namespace xml {
+
+const TCHAR* const kXmlDirective =
+    _T("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+
+const TCHAR* const kXmlNamespace = NULL;
+
+namespace element {
+
+const TCHAR* const kAction = _T("action");
+const TCHAR* const kActions = _T("actions");
+const TCHAR* const kApp = _T("app");
+const TCHAR* const kData = _T("data");
+const TCHAR* const kDayStart = _T("daystart");
+
+// ping element. The element is named "event" for legacy reasons.
+const TCHAR* const kEvent = _T("event");
+const TCHAR* const kManifest = _T("manifest");
+const TCHAR* const kOs = _T("os");
+const TCHAR* const kPackage = _T("package");
+const TCHAR* const kPackages = _T("packages");
+
+// didrun element. The element is named "ping" for legacy reasons.
+const TCHAR* const kPing = _T("ping");
+const TCHAR* const kRequest = _T("request");
+const TCHAR* const kResponse = _T("response");
+const TCHAR* const kUpdateCheck = _T("updatecheck");
+const TCHAR* const kUrl = _T("url");
+const TCHAR* const kUrls = _T("urls");
+
+}  // namespace element
+
+namespace attribute {
+
+const TCHAR* const kActive = _T("active");
+const TCHAR* const kAdditionalParameters = _T("ap");
+const TCHAR* const kAppBytesDownloaded = _T("downloaded");
+const TCHAR* const kAppBytesTotal = _T("total");
+const TCHAR* const kAppGuid = _T("appguid");
+const TCHAR* const kApplicationName = _T("appname");
+const TCHAR* const kAppId = _T("appid");
+const TCHAR* const kArch = _T("arch");
+const TCHAR* const kArguments = _T("arguments");
+const TCHAR* const kBrandCode = _T("brand");
+const TCHAR* const kBrowserType = _T("browser");
+const TCHAR* const kClientId = _T("client");
+const TCHAR* const kCodebase = _T("codebase");
+const TCHAR* const kCountry = _T("country");
+const TCHAR* const kDaysSinceLastActivePing = _T("a");
+const TCHAR* const kDaysSinceLastRollCall = _T("r");
+const TCHAR* const kDownloadTime = _T("download_time_ms");
+const TCHAR* const kElapsedSeconds = _T("elapsed_seconds");
+const TCHAR* const kErrorCode = _T("errorcode");
+const TCHAR* const kEvent = _T("event");
+const TCHAR* const kEventResult = _T("eventresult");
+const TCHAR* const kEventType = _T("eventtype");
+const TCHAR* const kErrorUrl = _T("errorurl");
+const TCHAR* const kExperiments = _T("experiments");
+const TCHAR* const kExtraCode1 = _T("extracode1");
+const TCHAR* const kHash = _T("hash");
+const TCHAR* const kIndex = _T("index");
+const TCHAR* const kInstalledAgeDays = _T("installage");
+const TCHAR* const kIsMachine = _T("ismachine");
+const TCHAR* const kInstallationId = _T("iid");
+const TCHAR* const kInstallSource = _T("installsource");
+const TCHAR* const kOriginURL = _T("originurl");
+const TCHAR* const kLang = _T("lang");
+const TCHAR* const kName = _T("name");
+const TCHAR* const kNextVersion = _T("nextversion");
+const TCHAR* const kParameter = _T("parameter");
+const TCHAR* const kPeriodOverrideSec = _T("periodoverridesec");
+const TCHAR* const kPlatform = _T("platform");
+const TCHAR* const kProtocol = _T("protocol");
+const TCHAR* const kRequestId = _T("requestid");
+const TCHAR* const kRequired = _T("required");
+const TCHAR* const kRun = _T("run");
+const TCHAR* const kServicePack = _T("sp");
+const TCHAR* const kSessionId = _T("sessionid");
+const TCHAR* const kSignature = _T("signature");
+const TCHAR* const kSize = _T("size");
+const TCHAR* const kStatus = _T("status");
+const TCHAR* const kSuccessAction = _T("onsuccess");
+const TCHAR* const kSuccessUrl = _T("successurl");
+const TCHAR* const kTestSource = _T("testsource");
+const TCHAR* const kTerminateAllBrowsers = _T("terminateallbrowsers");
+const TCHAR* const kTTToken = _T("tttoken");
+const TCHAR* const kUpdateDisabled = _T("updatedisabled");
+const TCHAR* const kUserId = _T("userid");
+const TCHAR* const kVersion = _T("version");
+const TCHAR* const kXmlns = _T("xmlns");
+
+}  // namespace attribute
+
+namespace value {
+
+const TCHAR* const kArchAmd64 = _T("x64");
+const TCHAR* const kArchIntel = _T("x86");
+const TCHAR* const kArchUnknown = _T("unknown");
+const TCHAR* const kFalse = _T("false");
+const TCHAR* const kInstall = _T("install");
+const TCHAR* const kInstallData = _T("install");
+const TCHAR* const kPostinstall = _T("postinstall");
+const TCHAR* const kPreinstall = _T("preinstall");
+const TCHAR* const kRequestType = _T("UpdateRequest");
+const TCHAR* const kStatusError = _T("error");
+const TCHAR* const kSuccessActionDefault = _T("default");
+const TCHAR* const kSuccessActionExitSilently = _T("exitsilently");
+const TCHAR* const kSuccessActionExitSilentlyOnLaunchCmd =
+    _T("exitsilentlyonlaunchcmd");
+const TCHAR* const kTrue = _T("true");
+const TCHAR* const kUpdate = _T("update");
+const TCHAR* const kVersion3 = _T("3.0");
+
+}  // namespace value
+
+}  // namespace xml
+
+}  // namespace omaha
diff --git a/common/xml_const.h b/common/xml_const.h
new file mode 100644
index 0000000..e18ae9e
--- /dev/null
+++ b/common/xml_const.h
@@ -0,0 +1,143 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_COMMON_XML_CONST_H_
+#define OMAHA_COMMON_XML_CONST_H_
+
+#include <windows.h>
+#include <atlbase.h>
+#include <atlstr.h>
+
+namespace omaha {
+
+namespace xml {
+
+const int kGuidLen = 38;
+
+extern const TCHAR* const kXmlDirective;
+extern const TCHAR* const kXmlNamespace;
+
+namespace element {
+
+extern const TCHAR* const kAction;
+extern const TCHAR* const kActions;
+extern const TCHAR* const kApp;
+extern const TCHAR* const kData;
+extern const TCHAR* const kDayStart;
+
+// ping element. The element is named "event" for legacy reasons.
+extern const TCHAR* const kEvent;
+extern const TCHAR* const kManifest;
+extern const TCHAR* const kOs;
+extern const TCHAR* const kPackage;
+extern const TCHAR* const kPackages;
+
+// didrun element. The element is named "ping" for legacy reasons.
+extern const TCHAR* const kPing;
+extern const TCHAR* const kRequest;
+extern const TCHAR* const kResponse;
+extern const TCHAR* const kUpdateCheck;
+extern const TCHAR* const kUrl;
+extern const TCHAR* const kUrls;
+
+}  // namespace element
+
+namespace attribute {
+
+extern const TCHAR* const kActive;
+extern const TCHAR* const kAdditionalParameters;
+extern const TCHAR* const kAppBytesDownloaded;
+extern const TCHAR* const kAppBytesTotal;
+extern const TCHAR* const kAppGuid;
+extern const TCHAR* const kApplicationName;
+extern const TCHAR* const kAppId;
+extern const TCHAR* const kArch;
+extern const TCHAR* const kArguments;
+extern const TCHAR* const kBrandCode;
+extern const TCHAR* const kBrowserType;
+extern const TCHAR* const kClientId;
+extern const TCHAR* const kCodebase;
+extern const TCHAR* const kCountry;
+extern const TCHAR* const kDaysSinceLastActivePing;
+extern const TCHAR* const kDaysSinceLastRollCall;
+extern const TCHAR* const kDownloadTime;
+extern const TCHAR* const kElapsedSeconds;
+extern const TCHAR* const kErrorCode;
+extern const TCHAR* const kEvent;
+extern const TCHAR* const kEventResult;
+extern const TCHAR* const kEventType;
+extern const TCHAR* const kErrorUrl;
+extern const TCHAR* const kExperiments;
+extern const TCHAR* const kExtraCode1;
+extern const TCHAR* const kHash;
+extern const TCHAR* const kIndex;
+extern const TCHAR* const kInstalledAgeDays;
+extern const TCHAR* const kIsMachine;
+extern const TCHAR* const kInstallationId;
+extern const TCHAR* const kInstallSource;
+extern const TCHAR* const kLang;
+extern const TCHAR* const kName;
+extern const TCHAR* const kNextVersion;
+extern const TCHAR* const kOriginURL;
+extern const TCHAR* const kParameter;
+extern const TCHAR* const kPeriodOverrideSec;
+extern const TCHAR* const kPlatform;
+extern const TCHAR* const kProtocol;
+extern const TCHAR* const kRequestId;
+extern const TCHAR* const kRequired;
+extern const TCHAR* const kRun;
+extern const TCHAR* const kServicePack;
+extern const TCHAR* const kSessionId;
+extern const TCHAR* const kSignature;
+extern const TCHAR* const kSize;
+extern const TCHAR* const kStatus;
+extern const TCHAR* const kSuccessAction;
+extern const TCHAR* const kSuccessUrl;
+extern const TCHAR* const kTestSource;
+extern const TCHAR* const kTerminateAllBrowsers;
+extern const TCHAR* const kTTToken;
+extern const TCHAR* const kUpdateDisabled;
+extern const TCHAR* const kUserId;
+extern const TCHAR* const kVersion;
+extern const TCHAR* const kXmlns;
+
+}  // namespace attribute
+
+namespace value {
+
+extern const TCHAR* const kArchAmd64;
+extern const TCHAR* const kArchIntel;
+extern const TCHAR* const kArchUnknown;
+extern const TCHAR* const kFalse;
+extern const TCHAR* const kInstall;
+extern const TCHAR* const kInstallData;
+extern const TCHAR* const kPostinstall;
+extern const TCHAR* const kPreinstall;
+extern const TCHAR* const kRequestType;
+extern const TCHAR* const kStatusError;
+extern const TCHAR* const kSuccessActionDefault;
+extern const TCHAR* const kSuccessActionExitSilently;
+extern const TCHAR* const kSuccessActionExitSilentlyOnLaunchCmd;
+extern const TCHAR* const kTrue;
+extern const TCHAR* const kUpdate;
+extern const TCHAR* const kVersion3;
+
+}  // namespace value
+
+}  // namespace xml
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_XML_CONST_H_
diff --git a/common/xml_parser.cc b/common/xml_parser.cc
new file mode 100644
index 0000000..22c9eb1
--- /dev/null
+++ b/common/xml_parser.cc
@@ -0,0 +1,1457 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/xml_parser.h"
+#include <stdlib.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/error.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/xml_utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/update_request.h"
+#include "omaha/common/update_response.h"
+#include "omaha/common/xml_const.h"
+
+namespace omaha {
+
+namespace xml {
+
+namespace {
+
+// Helper structure similar with an std::pair but without a constructor.
+// Instance of it can be stored in arrays.
+template <typename Type1, typename Type2>
+struct Tuple {
+  Type1 first;
+  Type2 second;
+};
+
+// Converts a string to the SuccessfulInstallAction enum.
+HRESULT ConvertStringToSuccessfulInstallAction(
+    const CString& str,
+    SuccessfulInstallAction* successful_install_action) {
+  ASSERT1(successful_install_action);
+
+  const Tuple<const TCHAR*, SuccessfulInstallAction> tuples[] = {
+    { xml::value::kSuccessActionDefault,
+      SUCCESS_ACTION_DEFAULT },
+
+    { xml::value::kSuccessActionExitSilently,
+      SUCCESS_ACTION_EXIT_SILENTLY },
+
+    { xml::value::kSuccessActionExitSilentlyOnLaunchCmd,
+      SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD },
+  };
+
+  if (str.IsEmpty()) {
+    *successful_install_action = SUCCESS_ACTION_DEFAULT;
+    return S_OK;
+  }
+
+  for (size_t i = 0; i != arraysize(tuples); ++i) {
+    if (str.CompareNoCase(tuples[i].first) == 0) {
+      *successful_install_action = tuples[i].second;
+      return S_OK;
+    }
+  }
+
+  // Using the default action allows Omaha to be forward-compatible with
+  // new SuccessActions, meaning older versions will not fail if a config
+  // uses a new action.
+  ASSERT(false, (_T("[Unrecognized success action][%s]"), str));
+  *successful_install_action = SUCCESS_ACTION_DEFAULT;
+  return S_OK;
+}
+
+HRESULT ConvertStringToInstallEvent(
+    const CString& str,
+    InstallAction::InstallEvent* install_event) {
+  ASSERT1(install_event);
+
+  const Tuple<const TCHAR*, InstallAction::InstallEvent> tuples[] = {
+    {xml::value::kPreinstall,  InstallAction::kPreInstall},
+    {xml::value::kInstall,     InstallAction::kInstall},
+    {xml::value::kUpdate,      InstallAction::kUpdate},
+    {xml::value::kPostinstall, InstallAction::kPostInstall},
+  };
+
+  for (size_t i = 0; i != arraysize(tuples); ++i) {
+    if (str.CompareNoCase(tuples[i].first) == 0) {
+      *install_event = tuples[i].second;
+      return S_OK;
+    }
+  }
+
+  return E_INVALIDARG;
+}
+
+// Returns S_OK if each child element of the node is any of the elements
+// provided as an argument. This is useful to detect if the element contains
+// only known children.
+// TODO(omaha): implement.
+HRESULT AreChildrenAnyOf(IXMLDOMNode* node,
+                         const std::vector<const TCHAR*>& element_names) {
+  UNREFERENCED_PARAMETER(node);
+  UNREFERENCED_PARAMETER(element_names);
+
+  return S_OK;
+}
+
+// TODO(omaha): implement.
+HRESULT HasNoChildren(IXMLDOMNode* node) {
+  UNREFERENCED_PARAMETER(node);
+  return S_OK;
+}
+
+// Verify that the protocol version is understood. We accept
+// all version numbers where major version is the same as kExpectedVersion
+// which are greater than or equal to kExpectedVersion. In other words,
+// we handle future minor version number increases which should be
+// compatible.
+HRESULT VerifyProtocolCompatibility(const CString& actual_version,
+                                    const CString& expected_version) {
+  if (_tcscmp(actual_version, expected_version) != 0) {
+    const double version = String_StringToDouble(actual_version);
+    const double expected = String_StringToDouble(expected_version);
+    if (expected > version) {
+      return GOOPDATEXML_E_XMLVERSION;
+    }
+    const int version_major = static_cast<int>(version);
+    const int expected_major = static_cast<int>(expected);
+    if (version_major != expected_major) {
+      return GOOPDATEXML_E_XMLVERSION;
+    }
+  }
+  return S_OK;
+}
+
+}  // namespace
+
+CString ConvertProcessorArchitectureToString(DWORD arch) {
+  switch (arch) {
+    case PROCESSOR_ARCHITECTURE_INTEL:
+      return xml::value::kArchIntel;
+
+    case PROCESSOR_ARCHITECTURE_AMD64:
+      return xml::value::kArchAmd64;
+
+    default:
+      ASSERT1(false);
+      return xml::value::kArchUnknown;
+  };
+}
+
+// The ElementHandler classes should also be in an anonymous namespace but
+// the base class cannot be because it is used in the header file.
+
+// Defines the base class of a hierarchy that deals with validating and
+// parsing of a single node element in the dom. The implementation uses
+// the template method design pattern.
+class ElementHandler {
+ public:
+  ElementHandler() {}
+  virtual ~ElementHandler() {}
+
+  HRESULT Handle(IXMLDOMNode* node, response::Response* response) {
+    ASSERT1(node);
+    ASSERT1(response);
+
+    HRESULT hr = Validate(node);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = Parse(node, response);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    return S_OK;
+  }
+
+ private:
+  // Validates a node and returns S_OK in case of success.
+  virtual HRESULT Validate(IXMLDOMNode* node) {
+    UNREFERENCED_PARAMETER(node);
+    return S_OK;
+  }
+
+  // Parses the node and stores its values in the response.
+  virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) {
+    UNREFERENCED_PARAMETER(node);
+    UNREFERENCED_PARAMETER(response);
+    return S_OK;
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(ElementHandler);
+};
+
+
+// Parses 'response'.
+class ResponseElementHandler : public ElementHandler {
+ public:
+  static ElementHandler* Create() { return new ResponseElementHandler; }
+
+ private:
+  virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) {
+    HRESULT hr = ReadStringAttribute(node,
+                                     xml::attribute::kProtocol,
+                                     &response->protocol);
+    if (FAILED(hr)) {
+      return hr;
+    }
+    hr = VerifyProtocolCompatibility(response->protocol, xml::value::kVersion3);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    return S_OK;
+  }
+};
+
+// Parses 'app'.
+class AppElementHandler : public ElementHandler {
+ public:
+  static ElementHandler* Create() { return new AppElementHandler; }
+
+ private:
+  virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) {
+    response::App app;
+
+    HRESULT hr = ReadStringAttribute(node, xml::attribute::kAppId, &app.appid);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = ReadStringAttribute(node, xml::attribute::kStatus, &app.status);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    // At present, the server may omit the following optional attributes if the
+    // contents would be empty strings.  Guard these reads appropriately.
+    // TODO(omaha3): If we adapt the server to send an empty string for these
+    // attributes, we can remove these checks.
+    if (HasAttribute(node, xml::attribute::kExperiments)) {
+      hr = ReadStringAttribute(node,
+                               xml::attribute::kExperiments,
+                               &app.experiments);
+      if (FAILED(hr)) {
+        return hr;
+      }
+    }
+
+    response->apps.push_back(app);
+    return S_OK;
+  }
+};
+
+
+// Parses 'updatecheck'.
+class UpdateCheckElementHandler : public ElementHandler {
+ public:
+  static ElementHandler* Create() { return new UpdateCheckElementHandler; }
+
+ private:
+  virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) {
+    response::UpdateCheck& update_check = response->apps.back().update_check;
+
+    ReadStringAttribute(node,
+                        xml::attribute::kTTToken,
+                        &update_check.tt_token);
+
+    ReadStringAttribute(node,
+                        xml::attribute::kErrorUrl,
+                        &update_check.error_url);
+
+    return ReadStringAttribute(node,
+                               xml::attribute::kStatus,
+                               &update_check.status);
+  }
+};
+
+
+// Parses 'urls'.
+class UrlsElementHandler : public ElementHandler {
+ public:
+  static ElementHandler* Create() { return new UrlsElementHandler; }
+};
+
+
+// Parses 'url'.
+class UrlElementHandler : public ElementHandler {
+ public:
+  static ElementHandler* Create() { return new UrlElementHandler; }
+
+ private:
+  virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) {
+    CString url;
+    HRESULT hr = ReadStringAttribute(node, xml::attribute::kCodebase, &url);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    response::UpdateCheck& update_check = response->apps.back().update_check;
+    update_check.urls.push_back(url);
+
+    return S_OK;
+  }
+};
+
+
+// Parses 'manifest'.
+class ManifestElementHandler : public ElementHandler {
+ public:
+  static ElementHandler* Create() { return new ManifestElementHandler; }
+
+ private:
+  virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) {
+    InstallManifest& install_manifest =
+        response->apps.back().update_check.install_manifest;
+    // TODO(omaha3): Uncomment when version becomes a required value for
+    // version 2 configs.
+    /*return*/ ReadStringAttribute(node,
+                               xml::attribute::kVersion,
+                               &install_manifest.version);
+    return S_OK;
+  }
+};
+
+
+// Parses 'packages'.
+class PackagesElementHandler : public ElementHandler {
+ public:
+  static ElementHandler* Create() { return new PackagesElementHandler; }
+};
+
+
+
+// Parses 'package'.
+class PackageElementHandler : public ElementHandler {
+ public:
+  static ElementHandler* Create() { return new PackageElementHandler; }
+
+ private:
+  virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) {
+    InstallPackage install_package;
+
+    HRESULT hr = ReadStringAttribute(node,
+                                     xml::attribute::kName,
+                                     &install_package.name);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    install_package.is_required = true;
+    hr = ReadBooleanAttribute(node,
+                              xml::attribute::kRequired,
+                              &install_package.is_required);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = ReadIntAttribute(node, xml::attribute::kSize, &install_package.size);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = ReadStringAttribute(node,
+                             xml::attribute::kHash,
+                             &install_package.hash);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    InstallManifest& install_manifest =
+        response->apps.back().update_check.install_manifest;
+    install_manifest.packages.push_back(install_package);
+
+    return S_OK;
+  }
+};
+
+// Parses 'actions'.
+class ActionsElementHandler : public ElementHandler {
+ public:
+  static ElementHandler* Create() { return new ActionsElementHandler; }
+};
+
+
+// Parses 'action'.
+class ActionElementHandler : public ElementHandler {
+ public:
+  static ElementHandler* Create() { return new ActionElementHandler; }
+
+ private:
+  virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) {
+    InstallAction install_action;
+
+    CString event;
+    HRESULT hr = ReadStringAttribute(node, xml::attribute::kEvent, &event);
+    if (FAILED(hr)) {
+      return hr;
+    }
+    hr = ConvertStringToInstallEvent(event, &install_action.install_event);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    ReadStringAttribute(node,
+                        xml::attribute::kRun,
+                        &install_action.program_to_run);
+    ReadStringAttribute(node,
+                        xml::attribute::kArguments,
+                        &install_action.program_arguments);
+
+    ReadStringAttribute(node,
+                        xml::attribute::kSuccessUrl,
+                        &install_action.success_url);
+
+    ReadBooleanAttribute(node,
+                         xml::attribute::kTerminateAllBrowsers,
+                         &install_action.terminate_all_browsers);
+
+    CString success_action;
+    ReadStringAttribute(node,
+                        xml::attribute::kSuccessAction,
+                        &success_action);
+    ConvertStringToSuccessfulInstallAction(success_action,
+                                           &install_action.success_action);
+
+    InstallManifest& install_manifest =
+        response->apps.back().update_check.install_manifest;
+    install_manifest.install_actions.push_back(install_action);
+
+    return S_OK;
+  }
+};
+
+
+// Parses 'data'.
+class DataElementHandler : public ElementHandler {
+ public:
+  static ElementHandler* Create() { return new DataElementHandler; }
+
+ private:
+  virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) {
+    response->apps.back().data.push_back(response::Data());
+    response::Data& data = response->apps.back().data.back();
+
+    HRESULT hr = ReadStringAttribute(node,
+                                     xml::attribute::kStatus,
+                                     &data.status);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    CString data_name;
+    hr = ReadStringAttribute(node, xml::attribute::kName, &data_name);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    if (xml::value::kInstallData != data_name) {
+      return E_UNEXPECTED;
+    }
+
+    hr = ReadStringAttribute(node,
+                             xml::attribute::kIndex,
+                             &data.install_data_index);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    if (data.status != kResponseStatusOkValue) {
+      // There is no data, so do not try to read it.
+      return S_OK;
+    }
+
+    return ReadStringValue(node, &data.install_data);
+  }
+};
+
+
+// Parses 'ping'.
+class PingElementHandler : public ElementHandler {
+ public:
+  static ElementHandler* Create() { return new PingElementHandler; }
+
+ private:
+  virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) {
+    response::Ping& ping = response->apps.back().ping;
+    ReadStringAttribute(node, xml::attribute::kStatus, &ping.status);
+    ASSERT1(ping.status == kResponseStatusOkValue);
+    return S_OK;
+  }
+};
+
+
+// Parses 'event'.
+class EventElementHandler : public ElementHandler {
+ public:
+  static ElementHandler* Create() { return new EventElementHandler; }
+
+ private:
+  virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) {
+    response::Event event;
+    ReadStringAttribute(node, xml::attribute::kStatus, &event.status);
+    ASSERT1(event.status == kResponseStatusOkValue);
+    response::App& app = response->apps.back();
+    app.events.push_back(event);
+    return S_OK;
+  }
+};
+
+// Parses 'daystart'.
+class DayStartElementHandler : public ElementHandler {
+ public:
+  static ElementHandler* Create() { return new DayStartElementHandler; }
+
+ private:
+  virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) {
+    ReadIntAttribute(node,
+                     xml::attribute::kElapsedSeconds,
+                     &response->day_start.elapsed_seconds);
+    return S_OK;
+  }
+};
+
+namespace v2 {
+
+namespace element {
+
+const TCHAR* const kGUpdate = _T("gupdate");
+
+}  // namespace element
+
+namespace attributev2 {
+
+// Some v2 offline manifests were incorrectly authored as 'Version' with a
+// capital 'V'.
+const TCHAR* const kVersionProperCased = _T("Version");
+
+}  // namespace attribute
+
+namespace value {
+
+const TCHAR* const kVersion2 = _T("2.0");
+
+}  // namespace value
+
+// Parses Omaha v2 'gupdate'.
+class GUpdateElementHandler : public ElementHandler {
+ public:
+  static ElementHandler* Create() { return new GUpdateElementHandler; }
+
+ private:
+  virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) {
+    HRESULT hr = ReadStringAttribute(node,
+                                     xml::attribute::kProtocol,
+                                     &response->protocol);
+    if (FAILED(hr)) {
+      return hr;
+    }
+    return VerifyProtocolCompatibility(response->protocol,
+                                       value::kVersion2);
+  }
+};
+
+// Parses Omaha v2 'updatecheck'.
+class UpdateCheckElementHandler : public ElementHandler {
+ public:
+  static ElementHandler* Create() { return new UpdateCheckElementHandler; }
+
+ private:
+  virtual HRESULT Parse(IXMLDOMNode* node, response::Response* response) {
+    response::UpdateCheck& update_check = response->apps.back().update_check;
+
+    HRESULT hr = ReadStringAttribute(node,
+                                     xml::attribute::kStatus,
+                                     &update_check.status);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    if (update_check.status.CompareNoCase(kResponseStatusOkValue)) {
+      return S_OK;
+    }
+
+    InstallManifest& install_manifest = update_check.install_manifest;
+    if (FAILED(ReadStringAttribute(node,
+                                   xml::attribute::kVersion,
+                                   &install_manifest.version))) {
+      ReadStringAttribute(node,
+                          v2::attributev2::kVersionProperCased,
+                          &install_manifest.version);
+    }
+
+    CString url;
+    hr = ReadStringAttribute(node, xml::attribute::kCodebase, &url);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    int start_file_name_idx = url.ReverseFind(_T('/'));
+    if (start_file_name_idx <= 0) {
+      return GOOPDATEDOWNLOAD_E_INVALID_PATH;
+    }
+    CString base_url = url.Left(start_file_name_idx + 1);
+    update_check.urls.push_back(base_url);
+
+    CString package_name = url.Right(url.GetLength() - start_file_name_idx - 1);
+    if (package_name.IsEmpty()) {
+      return GOOPDATEDOWNLOAD_E_FILE_NAME_EMPTY;
+    }
+
+    InstallPackage install_package;
+    install_package.name = package_name;
+    install_package.is_required = true;
+    hr = ReadIntAttribute(node, xml::attribute::kSize, &install_package.size);
+    if (FAILED(hr)) {
+      return hr;
+    }
+    hr = ReadStringAttribute(node,
+                             xml::attribute::kHash,
+                             &install_package.hash);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    install_manifest.packages.push_back(install_package);
+
+    InstallAction install_action;
+    install_action.install_event = InstallAction::kInstall;
+    install_action.program_to_run = package_name;
+    ReadStringAttribute(node,
+                        xml::attribute::kArguments,
+                        &install_action.program_arguments);
+
+    install_manifest.install_actions.push_back(install_action);
+
+    InstallAction post_install_action;
+    if (SUCCEEDED(ParsePostInstallActions(node, &post_install_action))) {
+      install_manifest.install_actions.push_back(post_install_action);
+    }
+
+    return S_OK;
+  }
+
+  HRESULT ParsePostInstallActions(IXMLDOMNode* node,
+                                  InstallAction* post_install_action) {
+    InstallAction install_action;
+    CString success_action;
+    if (FAILED(ReadStringAttribute(node,
+                                   xml::attribute::kSuccessAction,
+                                   &success_action)) &&
+        FAILED(ReadStringAttribute(node,
+                                   xml::attribute::kSuccessUrl,
+                                   &install_action.success_url)) &&
+        FAILED(ReadBooleanAttribute(node,
+                                    xml::attribute::kTerminateAllBrowsers,
+                                    &install_action.terminate_all_browsers))) {
+      return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
+    }
+
+    install_action.install_event = InstallAction::kPostInstall;
+    ReadStringAttribute(node, xml::attribute::kSuccessAction, &success_action);
+    ConvertStringToSuccessfulInstallAction(success_action,
+                                           &install_action.success_action);
+    ReadStringAttribute(node,
+                        xml::attribute::kSuccessUrl,
+                        &install_action.success_url);
+    ReadBooleanAttribute(node,
+                         xml::attribute::kTerminateAllBrowsers,
+                         &install_action.terminate_all_browsers);
+    *post_install_action = install_action;
+    return S_OK;
+  }
+};
+
+}  // namespace v2
+
+XmlParser::XmlParser() {}
+
+void XmlParser::InitializeElementHandlers() {
+  const Tuple<const TCHAR*, ElementHandler* (*)()> tuples[] = {
+    {xml::element::kAction, &ActionElementHandler::Create},
+    {xml::element::kActions, &ActionsElementHandler::Create},
+    {xml::element::kApp, &AppElementHandler::Create},
+    {xml::element::kData, &DataElementHandler::Create},
+    {xml::element::kDayStart, &DayStartElementHandler::Create},
+    {xml::element::kEvent, &EventElementHandler::Create},
+    {xml::element::kManifest, &ManifestElementHandler::Create},
+    {xml::element::kPackage, &PackageElementHandler::Create},
+    {xml::element::kPackages, &PackagesElementHandler::Create},
+    {xml::element::kPing, &PingElementHandler::Create},
+    {xml::element::kResponse, &ResponseElementHandler::Create},
+    {xml::element::kUpdateCheck, &UpdateCheckElementHandler::Create},
+    {xml::element::kUrl, &UrlElementHandler::Create},
+    {xml::element::kUrls, &UrlsElementHandler::Create},
+  };
+
+  for (size_t i = 0; i != arraysize(tuples); ++i) {
+    VERIFY1(element_handler_factory_.Register(tuples[i].first,
+                                              tuples[i].second));
+  }
+}
+
+// The AppElementHandler and the DataElementHandler are shared, because the
+// format is identical between Omaha v2 and Omaha v3. We should make copies of
+// the shared classes if these elements diverge between v2 and v3. The worst
+// case scenario is that we break v2 compatibility if we do not do a
+// copy-on-write, which might be acceptable.
+void XmlParser::InitializeLegacyElementHandlers() {
+  const Tuple<const TCHAR*, ElementHandler* (*)()> tuples[] = {
+    {xml::element::kApp, &AppElementHandler::Create},
+    {xml::element::kData, &DataElementHandler::Create},
+    {v2::element::kGUpdate, &v2::GUpdateElementHandler::Create},
+    {xml::element::kUpdateCheck, &v2::UpdateCheckElementHandler::Create},
+  };
+
+  for (size_t i = 0; i != arraysize(tuples); ++i) {
+    VERIFY1(element_handler_factory_.Register(tuples[i].first,
+                                              tuples[i].second));
+  }
+}
+
+HRESULT XmlParser::SerializeRequest(const UpdateRequest& update_request,
+                                    CString* buffer) {
+  ASSERT1(buffer);
+
+  XmlParser xml_parser;
+
+  HRESULT hr = xml_parser.BuildDom(update_request.request());
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = xml_parser.GetXml(buffer);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT XmlParser::BuildDom(const request::Request& request) {
+  CORE_LOG(L3, (_T("[XmlParser::BuildDom]")));
+  HRESULT hr = CoCreateSafeDOMDocument(&document_);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  request_ = &request;
+
+  hr = BuildRequestElement();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+// Extract the string out of the DOM, and add the initial processing
+// instruction. The xml property of the document converts to the document from
+// its original encoding to Unicode. As a result, the xml directive and the
+// desired encoding must be explicitely set here.
+HRESULT XmlParser::GetXml(CString* buffer) {
+  CORE_LOG(L3, (_T("[XmlParser::GetXml]")));
+
+  ASSERT1(buffer);
+
+  CComBSTR xml_body;
+  HRESULT hr = document_->get_xml(&xml_body);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  *buffer = kXmlDirective;
+  *buffer += xml_body;
+
+  // The xml string contains a CR LF pair at the end.
+  buffer->TrimRight(_T("\r\n"));
+
+  return S_OK;
+}
+
+
+HRESULT XmlParser::BuildRequestElement() {
+  CORE_LOG(L3, (_T("[XmlParser::BuildRequestElement]")));
+
+  CComPtr<IXMLDOMNode> element;
+  HRESULT hr = CreateElementNode(xml::element::kRequest, _T(""), &element);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  ASSERT1(request_);
+
+  // Add attributes to the top element:
+  // * protocol - protocol version
+  // * version - Omaha version
+  // * ismachine - is machine Omaha
+  // * installsource - install source
+  // * originurl - origin url, primarily set by Update3Web plugins
+  // * testsource - test source
+  // * requestid - unique request ID
+  // * periodoverridesec - override value for update check frequency
+
+  hr = AddXMLAttributeNode(element,
+                           kXmlNamespace,
+                           xml::attribute::kProtocol,
+                           request_->protocol_version);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = AddXMLAttributeNode(element,
+                           kXmlNamespace,
+                           xml::attribute::kVersion,
+                           request_->omaha_version);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = AddXMLAttributeNode(element,
+                           kXmlNamespace,
+                           xml::attribute::kIsMachine,
+                           request_->is_machine ? _T("1") : _T("0"));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = AddXMLAttributeNode(element,
+                           kXmlNamespace,
+                           xml::attribute::kSessionId,
+                           request_->session_id);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (!request_->uid.IsEmpty()) {
+    hr = AddXMLAttributeNode(element,
+                             kXmlNamespace,
+                             xml::attribute::kUserId,
+                             request_->uid);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (!request_->install_source.IsEmpty()) {
+    hr = AddXMLAttributeNode(element,
+                             kXmlNamespace,
+                             xml::attribute::kInstallSource,
+                             request_->install_source);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (!request_->origin_url.IsEmpty()) {
+    hr = AddXMLAttributeNode(element,
+                             kXmlNamespace,
+                             xml::attribute::kOriginURL,
+                             request_->origin_url);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (!request_->test_source.IsEmpty()) {
+    hr = AddXMLAttributeNode(element,
+                             kXmlNamespace,
+                             xml::attribute::kTestSource,
+                             request_->test_source);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (!request_->request_id.IsEmpty()) {
+    hr = AddXMLAttributeNode(element,
+                             kXmlNamespace,
+                             xml::attribute::kRequestId,
+                             request_->request_id);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (request_->check_period_sec != -1) {
+    hr = AddXMLAttributeNode(element,
+                             kXmlNamespace,
+                             xml::attribute::kPeriodOverrideSec,
+                             itostr(request_->check_period_sec));
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  hr = BuildOsElement(element);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Add the app element sequence to the request.
+  hr = BuildAppElement(element);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Add the request node to the document root.
+  CComPtr<IXMLDOMElement> element_node;
+  hr = element->QueryInterface(&element_node);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = document_->putref_documentElement(element_node);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT XmlParser::BuildOsElement(IXMLDOMNode* parent_node) {
+  CORE_LOG(L3, (_T("[XmlParser::BuildOsElement]")));
+
+  ASSERT1(parent_node);
+  ASSERT1(request_);
+
+  CComPtr<IXMLDOMNode> element;
+  HRESULT hr = CreateElementNode(xml::element::kOs, _T(""), &element);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = AddXMLAttributeNode(element,
+                           kXmlNamespace,
+                           xml::attribute::kPlatform,
+                           request_->os.platform);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = AddXMLAttributeNode(element,
+                           kXmlNamespace,
+                           xml::attribute::kVersion,
+                           request_->os.version);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = AddXMLAttributeNode(element,
+                           kXmlNamespace,
+                           xml::attribute::kServicePack,
+                           request_->os.service_pack);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = AddXMLAttributeNode(element,
+                           kXmlNamespace,
+                           xml::attribute::kArch,
+                           request_->os.arch);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = parent_node->appendChild(element, NULL);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+// Create and add request's to the requests node.
+HRESULT XmlParser::BuildAppElement(IXMLDOMNode* parent_node) {
+  CORE_LOG(L3, (_T("[XmlParser::BuildAppElement]")));
+
+  ASSERT1(parent_node);
+  ASSERT1(request_);
+
+  for (size_t i = 0; i < request_->apps.size(); ++i) {
+    const request::App& app = request_->apps[i];
+
+    CComPtr<IXMLDOMNode> element;
+    HRESULT hr = CreateElementNode(xml::element::kApp, _T(""), &element);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    ASSERT1(IsGuid(app.app_id));
+    hr = AddXMLAttributeNode(element,
+                             kXmlNamespace,
+                             xml::attribute::kAppId,
+                             app.app_id);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = AddXMLAttributeNode(element,
+                             kXmlNamespace,
+                             xml::attribute::kVersion,
+                             app.version);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = AddXMLAttributeNode(element,
+                             kXmlNamespace,
+                             xml::attribute::kNextVersion,
+                             app.next_version);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    if (!app.ap.IsEmpty()) {
+      hr = AddXMLAttributeNode(element,
+                               kXmlNamespace,
+                               xml::attribute::kAdditionalParameters,
+                               app.ap);
+      if (FAILED(hr)) {
+        return hr;
+      }
+    }
+
+    hr = AddXMLAttributeNode(element,
+                             kXmlNamespace,
+                             xml::attribute::kLang,
+                             app.lang);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = AddXMLAttributeNode(element,
+                             kXmlNamespace,
+                             xml::attribute::kBrandCode,
+                             app.brand_code);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = AddXMLAttributeNode(element,
+                             kXmlNamespace,
+                             xml::attribute::kClientId,
+                             app.client_id);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    // TODO(omaha3): Determine whether or not the server is able to accept an
+    // empty string here.  If so, remove this IsEmpty() check, and always emit.
+    if (!app.experiments.IsEmpty()) {
+      hr = AddXMLAttributeNode(element,
+                               kXmlNamespace,
+                               xml::attribute::kExperiments,
+                               app.experiments);
+      if (FAILED(hr)) {
+        return hr;
+      }
+    }
+
+    // 0 seconds indicates unknown install time. A new install uses -1 days.
+    if (app.install_time_diff_sec) {
+      const int installed_full_days =
+          static_cast<int>(app.install_time_diff_sec) / kSecondsPerDay;
+      ASSERT1(installed_full_days >= 0 || installed_full_days == -1);
+      hr = AddXMLAttributeNode(element,
+                               kXmlNamespace,
+                               xml::attribute::kInstalledAgeDays,
+                               itostr(installed_full_days));
+      if (FAILED(hr)) {
+        return hr;
+      }
+    }
+
+    if (!app.iid.IsEmpty() && app.iid != GuidToString(GUID_NULL)) {
+      hr = AddXMLAttributeNode(element,
+                               kXmlNamespace,
+                               xml::attribute::kInstallationId,
+                               app.iid);
+      if (FAILED(hr)) {
+        return hr;
+      }
+    }
+
+    hr = BuildUpdateCheckElement(app, element);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = BuildPingRequestElement(app, element);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = BuildDataElement(app, element);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = BuildDidRunElement(app, element);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = parent_node->appendChild(element, NULL);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  return S_OK;
+}
+
+HRESULT XmlParser::BuildUpdateCheckElement(const request::App& app,
+                                           IXMLDOMNode* parent_node) {
+  CORE_LOG(L3, (_T("[XmlParser::BuildUpdateCheckElement]")));
+  ASSERT1(parent_node);
+
+  // Create a DOM element only if the update check member is valid.
+  if (!app.update_check.is_valid) {
+    return S_OK;
+  }
+
+  CComPtr<IXMLDOMNode> element;
+  HRESULT hr = CreateElementNode(xml::element::kUpdateCheck, _T(""), &element);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (app.update_check.is_update_disabled) {
+    hr = AddXMLAttributeNode(element,
+                             kXmlNamespace,
+                             xml::attribute::kUpdateDisabled,
+                             xml::value::kTrue);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (!app.update_check.tt_token.IsEmpty()) {
+    hr = AddXMLAttributeNode(element,
+                             kXmlNamespace,
+                             xml::attribute::kTTToken,
+                             app.update_check.tt_token);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  hr = parent_node->appendChild(element, NULL);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+// Ping elements are called "event" elements for legacy reasons.
+HRESULT XmlParser::BuildPingRequestElement(const request::App& app,
+                                           IXMLDOMNode* parent_node) {
+  CORE_LOG(L3, (_T("[XmlParser::BuildPingRequestElement]")));
+  ASSERT1(parent_node);
+
+  // Create a DOM element only if there is are ping_events.
+  if (app.ping_events.empty()) {
+    return S_OK;
+  }
+
+  PingEventVector::const_iterator it;
+  for (it = app.ping_events.begin(); it != app.ping_events.end(); ++it) {
+    const PingEventPtr ping_event = *it;
+    CComPtr<IXMLDOMNode> element;
+    HRESULT hr = CreateElementNode(xml::element::kEvent, _T(""), &element);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = ping_event->ToXml(element);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = parent_node->appendChild(element, NULL);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  return S_OK;
+}
+
+HRESULT XmlParser::BuildDataElement(const request::App& app,
+                                    IXMLDOMNode* parent_node) {
+  CORE_LOG(L3, (_T("[XmlParser::BuildDataElement]")));
+  ASSERT1(parent_node);
+
+  // Create a DOM element only if there is a data object.
+  if (app.data.install_data_index.IsEmpty()) {
+    return S_OK;
+  }
+
+  ASSERT1(app.update_check.is_valid);
+
+  CComPtr<IXMLDOMNode> element;
+  HRESULT hr = CreateElementNode(xml::element::kData, _T(""), &element);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = AddXMLAttributeNode(element,
+                           kXmlNamespace,
+                           xml::attribute::kName,
+                           xml::value::kInstallData);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = AddXMLAttributeNode(element,
+                           kXmlNamespace,
+                           xml::attribute::kIndex,
+                           app.data.install_data_index);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = parent_node->appendChild(element, NULL);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT XmlParser::BuildDidRunElement(const request::App& app,
+                                      IXMLDOMNode* parent_node) {
+  CORE_LOG(L3, (_T("[XmlParser::BuildDidRunElement]")));
+  ASSERT1(parent_node);
+
+  bool was_active = app.ping.active == ACTIVE_RUN;
+  bool need_active = app.ping.active != ACTIVE_UNKNOWN;
+  bool has_sent_a_today = app.ping.days_since_last_active_ping == 0;
+  bool need_a = was_active && !has_sent_a_today;
+  bool need_r = app.ping.days_since_last_roll_call != 0;
+
+  // Create a DOM element only if the didrun object has actual state.
+  if (!need_active && !need_a && !need_r) {
+    return S_OK;
+  }
+
+  ASSERT1(app.update_check.is_valid);
+
+  CComPtr<IXMLDOMNode> element;
+  HRESULT hr = CreateElementNode(xml::element::kPing, _T(""), &element);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // TODO(omaha): Remove "active" attribute after transition.
+  if (need_active) {
+    const TCHAR* active_str(was_active ? _T("1") : _T("0"));
+    hr = AddXMLAttributeNode(element,
+                             kXmlNamespace,
+                             xml::attribute::kActive,
+                             active_str);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (need_a) {
+    hr = AddXMLAttributeNode(element,
+                             kXmlNamespace,
+                             xml::attribute::kDaysSinceLastActivePing,
+                             itostr(app.ping.days_since_last_active_ping));
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  if (need_r) {
+    hr = AddXMLAttributeNode(element,
+                             kXmlNamespace,
+                             xml::attribute::kDaysSinceLastRollCall,
+                             itostr(app.ping.days_since_last_roll_call));
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  hr = parent_node->appendChild(element, NULL);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT XmlParser::CreateElementNode(const TCHAR* name,
+                                     const TCHAR* value,
+                                     IXMLDOMNode** element) {
+  ASSERT1(name);
+  ASSERT1(value);
+  ASSERT1(element);
+
+  // When there is a namespace, the element names get o: prepended to avoid a
+  // size explosion where the namespace uri gets automatically added to every
+  // element by msxml.
+  CString namespace_qualified_name;
+  namespace_qualified_name.Format(kXmlNamespace ? _T("o:%s") : _T("%s"),
+                                  name);
+  ASSERT1(document_);
+  HRESULT hr = CreateXMLNode(document_,
+                             NODE_ELEMENT,
+                             namespace_qualified_name,
+                             kXmlNamespace,
+                             value,
+                             element);
+  return hr;
+}
+
+
+HRESULT XmlParser::DeserializeResponse(const std::vector<uint8>& buffer,
+                                       UpdateResponse* update_response) {
+  ASSERT1(update_response);
+
+  XmlParser xml_parser;
+  HRESULT hr = LoadXMLFromRawData(buffer, false, &xml_parser.document_);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  xml_parser.response_ = &update_response->response_;
+
+  hr = xml_parser.Parse();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT XmlParser::Parse() {
+  CORE_LOG(L3, (_T("[XmlParser::Parse]")));
+  ASSERT1(response_);
+
+  CComPtr<IXMLDOMElement> root_node;
+  HRESULT hr = document_->get_documentElement(&root_node);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  if (!root_node) {
+    return GOOPDATEXML_E_PARSE_ERROR;
+  }
+
+  CComBSTR root_name;
+  hr = root_node->get_baseName(&root_name);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (root_name == xml::element::kResponse) {
+    InitializeElementHandlers();
+    return TraverseDOM(root_node);
+  }
+
+  if (root_name == v2::element::kGUpdate) {
+    InitializeLegacyElementHandlers();
+    return TraverseDOM(root_node);
+  }
+
+  return GOOPDATEXML_E_RESPONSENODE;
+}
+
+HRESULT XmlParser::TraverseDOM(IXMLDOMNode* node) {
+  CORE_LOG(L5, (_T("[XmlParser::TraverseDOM]")));
+  ASSERT1(node);
+
+  HRESULT hr = VisitElement(node);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CComPtr<IXMLDOMNodeList> children_list;
+  hr = node->get_childNodes(&children_list);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  long num_children = 0;   // NOLINT
+  hr = children_list->get_length(&num_children);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  for (int i = 0; i < num_children; ++i) {
+    CComPtr<IXMLDOMNode> child_node;
+    hr = children_list->get_item(i, &child_node);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    DOMNodeType type = NODE_INVALID;
+    hr = child_node->get_nodeType(&type);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    ASSERT1(type == NODE_TEXT || type == NODE_ELEMENT || type == NODE_COMMENT);
+
+    if (type == NODE_ELEMENT) {
+      hr = TraverseDOM(child_node);
+      if (FAILED(hr)) {
+        return hr;
+      }
+    }
+  }
+
+  return S_OK;
+}
+
+HRESULT XmlParser::VisitElement(IXMLDOMNode* node) {
+  XMLFQName node_name;
+  HRESULT hr = GetXMLFQName(node, &node_name);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[GetXMLFQName failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CORE_LOG(L4, (_T("[element name][%s:%s]"), node_name.uri, node_name.base));
+
+  // Ignore elements not understood.
+  ElementHandler* element_handler =
+      element_handler_factory_.CreateObject(node_name.base);
+  if (element_handler) {
+    return element_handler->Handle(node, response_);
+  } else {
+    CORE_LOG(LW, (_T("[VisitElement: don't know how to handle %s:%s]"),
+                  node_name.uri, node_name.base));
+  }
+  return S_OK;
+}
+
+}  // namespace xml
+
+}  // namespace omaha
+
diff --git a/common/xml_parser.h b/common/xml_parser.h
new file mode 100644
index 0000000..551b6cb
--- /dev/null
+++ b/common/xml_parser.h
@@ -0,0 +1,130 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Defines the Goopdate XML parser. The clients of this class are
+// UpdateRequest and UpdateResponse.
+
+#ifndef OMAHA_COMMON_XML_PARSER_H_
+#define OMAHA_COMMON_XML_PARSER_H_
+
+#include <windows.h>
+#include <objbase.h>
+#include <msxml2.h>
+#include <atlbase.h>
+#include <atlstr.h>
+#include <map>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/object_factory.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/update_request.h"
+#include "omaha/common/update_response.h"
+
+namespace omaha {
+
+namespace xml {
+
+class ElementHandler;
+
+CString ConvertProcessorArchitectureToString(DWORD processor_architecture);
+
+// Public static methods instantiate a temporary instance of this class, which
+// then parses the specified document. This avoids reusing instances of the
+// parser and dealing with stale and dirty data.
+class XmlParser {
+ public:
+  // Parses the update response buffer and fills in the UpdateResponse. In case
+  // of errors, the UpdateResponse object may contain partial information up
+  // to the point of the parsing error.
+  // TODO(omaha): since the xml docs are strings we could use a CString as
+  // an input parameter, no reason why this should be a buffer.
+  static HRESULT DeserializeResponse(const std::vector<uint8>& buffer,
+                                     UpdateResponse* update_response);
+
+  // Generates the update request from the request node.
+  static HRESULT SerializeRequest(const UpdateRequest& update_request,
+                                  CString* buffer);
+
+ private:
+  typedef Factory<ElementHandler, CString> ElementHandlerFactory;
+
+  XmlParser();
+  void InitializeElementHandlers();
+  void InitializeLegacyElementHandlers();
+
+  // Builds an XML object model corresponding to an xml request.
+  HRESULT BuildDom(const request::Request& request);
+
+  // Builds the 'request' element.
+  HRESULT BuildRequestElement();
+
+  // Creates the 'os' element.
+  HRESULT BuildOsElement(IXMLDOMNode* parent_node);
+
+  // Creates the 'app' element. This is usually a sequence of elements.
+  HRESULT BuildAppElement(IXMLDOMNode* parent_node);
+
+  // Creates the 'updatecheck' element for an application.
+  HRESULT BuildUpdateCheckElement(const request::App& app,
+                                  IXMLDOMNode* parent_node);
+
+  // Creates Ping aka 'event' elements for an application.
+  HRESULT BuildPingRequestElement(const request::App& app,
+                                  IXMLDOMNode* parent_node);
+
+  // Creates the 'data' element for an application.
+  HRESULT BuildDataElement(const request::App& app,
+                           IXMLDOMNode* parent_node);
+
+  // Creates the 'didrun' aka 'active' aka 'ping' element for an application.
+  HRESULT BuildDidRunElement(const request::App& app,
+                             IXMLDOMNode* parent_node);
+
+  // Serializes the DOM into a string.
+  HRESULT GetXml(CString* buffer);
+
+  // Creates an element in the Update2 xml namespace.
+  HRESULT CreateElementNode(const TCHAR* name,
+                            const TCHAR* value,
+                            IXMLDOMNode** element);
+
+  // Starts parsing of the xml document.
+  HRESULT Parse();
+
+  // Does a DFS traversal of the dom.
+  HRESULT TraverseDOM(IXMLDOMNode* node);
+
+  // Handles a single node during traversal.
+  HRESULT VisitElement(IXMLDOMNode* node);
+
+  // The current xml document.
+  CComPtr<IXMLDOMDocument> document_;
+
+  // The xml request being serialized. Not owned by this class.
+  const request::Request* request_;
+
+  // The xml response being deserialized. Not owned by this class.
+  response::Response* response_;
+
+  ElementHandlerFactory element_handler_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(XmlParser);
+};
+
+}  // namespace xml
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_XML_PARSER_H_
diff --git a/common/xml_parser_unittest.cc b/common/xml_parser_unittest.cc
new file mode 100644
index 0000000..908cae8
--- /dev/null
+++ b/common/xml_parser_unittest.cc
@@ -0,0 +1,306 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <windows.h>
+#include "base/utils.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/error.h"
+#include "omaha/common/xml_parser.h"
+#include "omaha/goopdate/update_response_utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace {
+
+const int kSeedManifestFileCount = 1;
+const int kSeedManifestResponseCount = 7;
+
+const int kExpectedRequestLength = 2048;
+
+}  // namespace
+
+namespace omaha {
+
+namespace xml {
+
+// TODO(omaha): there were many tests related to
+// updatedev_check_period_override and policy_check_period_override, which
+// the current parser is unaware of. These parameters must be handled outside
+// the parser itself. Not sure how we are handling them now.
+class XmlParserTest : public testing::Test {
+ protected:
+  XmlParserTest() {
+  }
+
+  ~XmlParserTest() {
+  }
+
+  virtual void SetUp() {
+  }
+
+  virtual void TearDown() {
+  }
+
+  // Allows test fixtures access to implementation details of UpdateRequest.
+  request::Request& get_xml_request(UpdateRequest* update_request) {
+    return update_request->request_;
+  }
+};
+
+// Creates a machine update request and serializes it.
+TEST_F(XmlParserTest, GenerateRequestWithoutUserId_MachineUpdateRequest) {
+  // The origin URL contains an invalid XML character, the double-quote. The
+  // expectation is that this character should be escaped to "&quot;".
+  scoped_ptr<UpdateRequest> update_request(
+      UpdateRequest::Create(true,
+                            _T("unittest_session"),
+                            _T("unittest_install"),
+                            _T("http://go/foo/\"")));
+
+  request::Request& xml_request = get_xml_request(update_request.get());
+
+  xml_request.omaha_version = _T("1.2.3.4");
+  xml_request.test_source = _T("dev");
+  xml_request.request_id = _T("{387E2718-B39C-4458-98CC-24B5293C8383}");
+  xml_request.os.platform = _T("win");
+  xml_request.os.version = _T("6.0");
+  xml_request.os.service_pack = _T("Service Pack 1");
+  xml_request.os.arch = _T("x86");
+  xml_request.check_period_sec = 100000;
+  xml_request.uid.Empty();
+
+  request::App app1;
+  app1.app_id = _T("{8A69D345-D564-463C-AFF1-A69D9E530F96}");
+  app1.lang = _T("en");
+  app1.iid = GuidToString(GUID_NULL);  // Prevents assert.
+  app1.ap = _T("ap_with_update_check");
+  app1.update_check.is_valid = true;
+  app1.data.install_data_index = _T("verboselogging");
+  app1.ping.active = ACTIVE_NOTRUN;
+  app1.ping.days_since_last_active_ping = -1;
+  app1.ping.days_since_last_roll_call = 5;
+  xml_request.apps.push_back(app1);
+
+  request::App app2;
+  app2.app_id = _T("{AD3D0CC0-AD1E-4b1f-B98E-BAA41DCE396C}");
+  app2.lang = _T("en");
+  app2.iid = GuidToString(GUID_NULL);  // Prevents assert.
+  app2.version = _T("1.0");
+  app2.next_version = _T("2.0");
+  app2.ap = _T("ap_with_no_update_check");
+  app2.experiments = _T("url_exp_2=a|Fri, 14 Aug 2015 16:13:03 GMT");
+  xml_request.apps.push_back(app2);
+
+  CString expected_buffer = _T("<?xml version=\"1.0\" encoding=\"UTF-8\"?><request protocol=\"3.0\" version=\"1.2.3.4\" ismachine=\"1\" sessionid=\"unittest_session\" installsource=\"unittest_install\" originurl=\"http://go/foo/&quot;\" testsource=\"dev\" requestid=\"{387E2718-B39C-4458-98CC-24B5293C8383}\" periodoverridesec=\"100000\"><os platform=\"win\" version=\"6.0\" sp=\"Service Pack 1\" arch=\"x86\"/><app appid=\"{8A69D345-D564-463C-AFF1-A69D9E530F96}\" version=\"\" nextversion=\"\" ap=\"ap_with_update_check\" lang=\"en\" brand=\"\" client=\"\"><updatecheck/><data name=\"install\" index=\"verboselogging\"/><ping active=\"0\" r=\"5\"/></app><app appid=\"{AD3D0CC0-AD1E-4b1f-B98E-BAA41DCE396C}\" version=\"1.0\" nextversion=\"2.0\" ap=\"ap_with_no_update_check\" lang=\"en\" brand=\"\" client=\"\" experiments=\"url_exp_2=a|Fri, 14 Aug 2015 16:13:03 GMT\"/></request>");  // NOLINT
+
+  CString actual_buffer;
+  EXPECT_HRESULT_SUCCEEDED(XmlParser::SerializeRequest(*update_request,
+                                                       &actual_buffer));
+  EXPECT_STREQ(expected_buffer, actual_buffer);
+}
+
+// Creates a machine update request and serializes it.
+TEST_F(XmlParserTest, GenerateRequestWithUserId_MachineUpdateRequest) {
+  // The origin URL contains an invalid XML character, the double-quote. The
+  // expectation is that this character should be escaped to "&quot;".
+  scoped_ptr<UpdateRequest> update_request(
+      UpdateRequest::Create(true,
+                            _T("unittest_session"),
+                            _T("unittest_install"),
+                            _T("http://go/bar/\"")));
+
+  request::Request& xml_request = get_xml_request(update_request.get());
+
+  xml_request.uid = _T("{c5bcb37e-47eb-4331-a544-2f31101951ab}");
+
+  xml_request.omaha_version = _T("4.3.2.1");
+  xml_request.test_source = _T("dev");
+  xml_request.request_id = _T("{387E2718-B39C-4458-98CC-24B5293C8384}");
+  xml_request.os.platform = _T("win");
+  xml_request.os.version = _T("7.0");
+  xml_request.os.service_pack = _T("Service Pack 2");
+  xml_request.os.arch = _T("x64");
+  xml_request.check_period_sec = 200000;
+
+  request::App app1;
+  app1.app_id = _T("{8A69D345-D564-463C-AFF1-A69D9E530F97}");
+  app1.lang = _T("en");
+  app1.iid = GuidToString(GUID_NULL);  // Prevents assert.
+  app1.ap = _T("ap_with_update_check");
+  app1.update_check.is_valid = true;
+  app1.data.install_data_index = _T("verboselogging");
+  app1.ping.active = ACTIVE_NOTRUN;
+  app1.ping.days_since_last_active_ping = -1;
+  app1.ping.days_since_last_roll_call = 5;
+  xml_request.apps.push_back(app1);
+
+  request::App app2;
+  app2.app_id = _T("{AD3D0CC0-AD1E-4b1f-B98E-BAA41DCE396D}");
+  app2.lang = _T("en");
+  app2.iid = GuidToString(GUID_NULL);  // Prevents assert.
+  app2.version = _T("1.0");
+  app2.next_version = _T("2.0");
+  app2.ap = _T("ap_with_no_update_check");
+  app2.experiments = _T("url_exp_2=a|Fri, 14 Aug 2015 16:13:03 GMT");
+  xml_request.apps.push_back(app2);
+
+  CString expected_buffer = _T("<?xml version=\"1.0\" encoding=\"UTF-8\"?><request protocol=\"3.0\" version=\"4.3.2.1\" ismachine=\"1\" sessionid=\"unittest_session\" userid=\"{c5bcb37e-47eb-4331-a544-2f31101951ab}\" installsource=\"unittest_install\" originurl=\"http://go/bar/&quot;\" testsource=\"dev\" requestid=\"{387E2718-B39C-4458-98CC-24B5293C8384}\" periodoverridesec=\"200000\"><os platform=\"win\" version=\"7.0\" sp=\"Service Pack 2\" arch=\"x64\"/><app appid=\"{8A69D345-D564-463C-AFF1-A69D9E530F97}\" version=\"\" nextversion=\"\" ap=\"ap_with_update_check\" lang=\"en\" brand=\"\" client=\"\"><updatecheck/><data name=\"install\" index=\"verboselogging\"/><ping active=\"0\" r=\"5\"/></app><app appid=\"{AD3D0CC0-AD1E-4b1f-B98E-BAA41DCE396D}\" version=\"1.0\" nextversion=\"2.0\" ap=\"ap_with_no_update_check\" lang=\"en\" brand=\"\" client=\"\" experiments=\"url_exp_2=a|Fri, 14 Aug 2015 16:13:03 GMT\"/></request>");  // NOLINT
+
+  CString actual_buffer;
+  EXPECT_HRESULT_SUCCEEDED(XmlParser::SerializeRequest(*update_request,
+                                                       &actual_buffer));
+  EXPECT_STREQ(expected_buffer, actual_buffer);
+}
+
+// TODO(omaha3): Add a UserUpdateRequest test with more values (brand, etc.).
+
+// Parses a response for one application.
+TEST_F(XmlParserTest, Parse) {
+  // Array of two request strings that are almost same except the second one
+  // contains some unsupported elements that we expect to be ignored.
+  CStringA buffer_strings[] = {
+            "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\"><daystart elapsed_seconds=\"8400\" /><app appid=\"{8A69D345-D564-463C-AFF1-A69D9E530F96}\" status=\"ok\" experiments=\"url_exp_2=a|Fri, 14 Aug 2015 16:13:03 GMT\"><updatecheck status=\"ok\"><urls><url codebase=\"http://cache.pack.google.com/edgedl/chrome/install/172.37/\"/></urls><manifest version=\"2.0.172.37\"><packages><package hash=\"NT/6ilbSjWgbVqHZ0rT1vTg1coE=\" name=\"chrome_installer.exe\" required=\"true\" size=\"9614320\"/></packages><actions><action arguments=\"--do-not-launch-chrome\" event=\"install\" needsadmin=\"false\" run=\"chrome_installer.exe\"/><action event=\"postinstall\" onsuccess=\"exitsilentlyonlaunchcmd\"/></actions></manifest></updatecheck><data index=\"verboselogging\" name=\"install\" status=\"ok\">{\n \"distribution\": {\n   \"verbose_logging\": true\n }\n}\n</data><ping status=\"ok\"/></app></response>",  // NOLINT
+            "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\" ExtraUnsupportedAttribute=\"123\"><daystart elapsed_seconds=\"8400\" /><UnsupportedElement1 UnsupportedAttribute1=\"some value\" /><app appid=\"{8A69D345-D564-463C-AFF1-A69D9E530F96}\" status=\"ok\" experiments=\"url_exp_2=a|Fri, 14 Aug 2015 16:13:03 GMT\"><updatecheck status=\"ok\"><urls><url codebase=\"http://cache.pack.google.com/edgedl/chrome/install/172.37/\"/></urls><manifest version=\"2.0.172.37\"><packages><package hash=\"NT/6ilbSjWgbVqHZ0rT1vTg1coE=\" name=\"chrome_installer.exe\" required=\"true\" size=\"9614320\"/></packages><actions><action arguments=\"--do-not-launch-chrome\" event=\"install\" needsadmin=\"false\" run=\"chrome_installer.exe\"/><action event=\"postinstall\" onsuccess=\"exitsilentlyonlaunchcmd\"/></actions></manifest></updatecheck><data index=\"verboselogging\" name=\"install\" status=\"ok\">{\n \"distribution\": {\n   \"verbose_logging\": true\n }\n}\n</data><ping status=\"ok\"/></app><UnsupportedElement2 UnsupportedAttribute2=\"Unsupported value\" >Some strings inside an unsupported element, should be ignored.<ping status=\"ok\"/></UnsupportedElement2></response>",  // NOLINT
+  };
+
+  for (int i = 0; i < arraysize(buffer_strings); i++) {
+    std::vector<uint8> buffer(buffer_strings[i].GetLength());
+    memcpy(&buffer.front(), buffer_strings[i], buffer.size());
+
+    scoped_ptr<UpdateResponse> update_response(UpdateResponse::Create());
+    EXPECT_HRESULT_SUCCEEDED(XmlParser::DeserializeResponse(
+        buffer,
+        update_response.get()));
+
+    const response::Response& xml_response(update_response->response());
+
+    EXPECT_STREQ(_T("3.0"), xml_response.protocol);
+    EXPECT_EQ(1, xml_response.apps.size());
+
+    const response::App& app(xml_response.apps[0]);
+    EXPECT_STREQ(_T("{8A69D345-D564-463C-AFF1-A69D9E530F96}"), app.appid);
+    EXPECT_STREQ(_T("ok"), app.status);
+    EXPECT_STREQ(_T("url_exp_2=a|Fri, 14 Aug 2015 16:13:03 GMT"),
+        app.experiments);
+
+    const response::UpdateCheck& update_check(app.update_check);
+    EXPECT_STREQ(_T("ok"), update_check.status);
+    EXPECT_EQ(1, update_check.urls.size());
+    EXPECT_STREQ(
+        _T("http://cache.pack.google.com/edgedl/chrome/install/172.37/"),
+        update_check.urls[0]);
+
+    const InstallManifest& install_manifest(update_check.install_manifest);
+    EXPECT_STREQ(_T("2.0.172.37"), install_manifest.version);
+    EXPECT_EQ(1, install_manifest.packages.size());
+
+    const InstallPackage& install_package(install_manifest.packages[0]);
+    EXPECT_STREQ(_T("chrome_installer.exe"), install_package.name);
+    EXPECT_TRUE(install_package.is_required);
+    EXPECT_EQ(9614320, install_package.size);
+    EXPECT_STREQ(_T("NT/6ilbSjWgbVqHZ0rT1vTg1coE="), install_package.hash);
+
+    EXPECT_EQ(2, install_manifest.install_actions.size());
+
+    const InstallAction* install_action(&install_manifest.install_actions[0]);
+    EXPECT_EQ(InstallAction::kInstall, install_action->install_event);
+    EXPECT_EQ(NEEDS_ADMIN_NO, install_action->needs_admin);
+    EXPECT_STREQ(_T("chrome_installer.exe"), install_action->program_to_run);
+    EXPECT_STREQ(_T("--do-not-launch-chrome"),
+                 install_action->program_arguments);
+    EXPECT_FALSE(install_action->terminate_all_browsers);
+    EXPECT_EQ(SUCCESS_ACTION_DEFAULT, install_action->success_action);
+
+    install_action = &install_manifest.install_actions[1];
+    EXPECT_EQ(InstallAction::kPostInstall, install_action->install_event);
+    EXPECT_EQ(NEEDS_ADMIN_NO, install_action->needs_admin);
+    EXPECT_FALSE(install_action->terminate_all_browsers);
+    EXPECT_EQ(SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD,
+              install_action->success_action);
+
+    EXPECT_EQ(0, app.events.size());
+
+    CString value;
+    EXPECT_SUCCEEDED(update_response_utils::GetInstallData(app.data,
+                                                           _T("verboselogging"),
+                                                           &value));
+    EXPECT_STREQ(
+        _T("{\n \"distribution\": {\n   \"verbose_logging\": true\n }\n}\n"),
+        value);
+  }
+}
+
+// Parses a response for one application.
+TEST_F(XmlParserTest, Parse_InvalidDataStatusError) {
+  CStringA buffer_string = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><response protocol=\"3.0\"><app appid=\"{8A69D345-D564-463C-AFF1-A69D9E530F96}\" status=\"ok\"><updatecheck status=\"ok\"><urls><url codebase=\"http://cache.pack.google.com/edgedl/chrome/install/172.37/\"/></urls><manifest version=\"2.0.172.37\"><packages><package hash=\"NT/6ilbSjWgbVqHZ0rT1vTg1coE=\" name=\"chrome_installer.exe\" required=\"false\" size=\"9614320\"/></packages><actions><action arguments=\"--do-not-launch-chrome\" event=\"install\" needsadmin=\"false\" run=\"chrome_installer.exe\"/><action event=\"postinstall\" onsuccess=\"exitsilentlyonlaunchcmd\"/></actions></manifest></updatecheck><data index=\"verboselog\" name=\"install\" status=\"error-nodata\"/><ping status=\"ok\"/></app></response>";  // NOLINT
+  std::vector<uint8> buffer(buffer_string.GetLength());
+  memcpy(&buffer.front(), buffer_string, buffer.size());
+
+  scoped_ptr<UpdateResponse> update_response(UpdateResponse::Create());
+  EXPECT_HRESULT_SUCCEEDED(XmlParser::DeserializeResponse(
+      buffer,
+      update_response.get()));
+  const response::Response& xml_response(update_response->response());
+
+  EXPECT_STREQ(_T("3.0"), xml_response.protocol);
+  EXPECT_EQ(1, xml_response.apps.size());
+
+  const response::App& app(xml_response.apps[0]);
+  EXPECT_STREQ(_T("{8A69D345-D564-463C-AFF1-A69D9E530F96}"), app.appid);
+  EXPECT_STREQ(_T("ok"), app.status);
+
+  const response::UpdateCheck& update_check(app.update_check);
+  EXPECT_STREQ(_T("ok"), update_check.status);
+  EXPECT_EQ(1, update_check.urls.size());
+  EXPECT_STREQ(_T("http://cache.pack.google.com/edgedl/chrome/install/172.37/"),
+               update_check.urls[0]);
+
+  const InstallManifest& install_manifest(update_check.install_manifest);
+  EXPECT_STREQ(_T("2.0.172.37"), install_manifest.version);
+  EXPECT_EQ(1, install_manifest.packages.size());
+
+  const InstallPackage& install_package(install_manifest.packages[0]);
+  EXPECT_STREQ(_T("chrome_installer.exe"), install_package.name);
+  EXPECT_FALSE(install_package.is_required);
+  EXPECT_EQ(9614320, install_package.size);
+  EXPECT_STREQ(_T("NT/6ilbSjWgbVqHZ0rT1vTg1coE="), install_package.hash);
+
+  EXPECT_EQ(2, install_manifest.install_actions.size());
+
+  const InstallAction* install_action(&install_manifest.install_actions[0]);
+  EXPECT_EQ(InstallAction::kInstall, install_action->install_event);
+  EXPECT_EQ(NEEDS_ADMIN_NO, install_action->needs_admin);
+  EXPECT_STREQ(_T("chrome_installer.exe"), install_action->program_to_run);
+  EXPECT_STREQ(_T("--do-not-launch-chrome"), install_action->program_arguments);
+  EXPECT_FALSE(install_action->terminate_all_browsers);
+  EXPECT_EQ(SUCCESS_ACTION_DEFAULT, install_action->success_action);
+
+  install_action = &install_manifest.install_actions[1];
+  EXPECT_EQ(InstallAction::kPostInstall, install_action->install_event);
+  EXPECT_EQ(NEEDS_ADMIN_NO, install_action->needs_admin);
+  EXPECT_FALSE(install_action->terminate_all_browsers);
+  EXPECT_EQ(SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD,
+            install_action->success_action);
+
+  EXPECT_EQ(0, app.events.size());
+
+  CString value;
+  EXPECT_EQ(GOOPDATE_E_INVALID_INSTALL_DATA_INDEX,
+            update_response_utils::GetInstallData(app.data, _T("verboselog"),
+                                                  &value));
+}
+
+}  // namespace xml
+
+}  // namespace omaha
diff --git a/common/xml_utils.cc b/common/xml_utils.cc
deleted file mode 100644
index 1f2ee5a..0000000
--- a/common/xml_utils.cc
+++ /dev/null
@@ -1,648 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// xml_utils.cpp
-//
-// Utilities for working with XML files via MSXML.
-
-#include "omaha/common/xml_utils.h"
-
-#include <msxml2.h>
-#include <atlsafe.h>
-#include <vector>
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/string.h"
-#include "omaha/common/utils.h"
-
-namespace omaha {
-
-XMLFQName::XMLFQName() {}
-
-XMLFQName::XMLFQName(const TCHAR* u, const TCHAR* b)
-    : uri(u && ::_tcslen(u) ? u : 0),
-      base(b && ::_tcslen(b) ? b : 0) {}
-
-XMLFQName::~XMLFQName() {}
-
-HRESULT CoCreateSafeDOMDocument(IXMLDOMDocument** my_xmldoc) {
-  ASSERT1(my_xmldoc && !*my_xmldoc);
-  if (!my_xmldoc) {
-    UTIL_LOG(LE, (L"[CoCreateSafeDOMDocument E_INVALIDARG]"));
-    return E_INVALIDARG;
-  }
-  *my_xmldoc = NULL;
-  CComPtr<IXMLDOMDocument> xml_doc;
-  HRESULT hr = xml_doc.CoCreateInstance(__uuidof(DOMDocument2));
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[xml_doc.CoCreateInstance failed][0x%x]"), hr));
-    return hr;
-  }
-  ASSERT1(xml_doc);
-  hr = xml_doc->put_resolveExternals(VARIANT_FALSE);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[put_resolveExternals failed][0x%x]"), hr));
-    return hr;
-  }
-  *my_xmldoc = xml_doc.Detach();
-  return S_OK;
-}
-
-HRESULT LoadXMLFromFile(const TCHAR* xmlfile,
-                        bool preserve_whitespace,
-                        IXMLDOMDocument** xmldoc) {
-  ASSERT1(xmlfile);
-  ASSERT1(xmldoc);
-  ASSERT1(!*xmldoc);
-
-  *xmldoc = NULL;
-  CComPtr<IXMLDOMDocument> my_xmldoc;
-  HRESULT hr = CoCreateSafeDOMDocument(&my_xmldoc);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[CoCreateSafeDOMDocument failed][0x%x]"), hr));
-    return hr;
-  }
-  hr = my_xmldoc->put_preserveWhiteSpace(VARIANT_BOOL(preserve_whitespace));
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[put_preserveWhiteSpace failed][0x%x]"), hr));
-    return hr;
-  }
-  CComBSTR my_xmlfile(xmlfile);
-  VARIANT_BOOL is_successful(VARIANT_FALSE);
-  hr = my_xmldoc->load(CComVariant(my_xmlfile), &is_successful);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[my_xmldoc->load failed][0x%x]"), hr));
-    return hr;
-  }
-  if (!is_successful) {
-    CComPtr<IXMLDOMParseError> error;
-    CString error_message;
-    hr = GetXMLParseError(my_xmldoc, &error);
-    if (FAILED(hr)) {
-      UTIL_LOG(LE, (_T("[GetXMLParseError failed][0x%x]"), hr));
-      return hr;
-    }
-    ASSERT1(error);
-    HRESULT error_code = 0;
-    hr = InterpretXMLParseError(error, &error_code, &error_message);
-    if (FAILED(hr)) {
-      UTIL_LOG(LE, (_T("[InterpretXMLParseError failed][0x%x]"), hr));
-      return hr;
-    }
-    UTIL_LOG(LE, (L"[LoadXMLFromFile '%s'][parse error: %s]",
-                  xmlfile, error_message));
-    ASSERT1(FAILED(error_code));
-    return FAILED(error_code) ? error_code : CI_E_XML_LOAD_ERROR;
-  }
-  *xmldoc = my_xmldoc.Detach();
-  return S_OK;
-}
-
-HRESULT LoadXMLFromMemory(const TCHAR* xmlstring,
-                          bool preserve_whitespace,
-                          IXMLDOMDocument** xmldoc) {
-  ASSERT1(xmlstring);
-  ASSERT1(xmldoc);
-  ASSERT1(!*xmldoc);
-
-  *xmldoc = NULL;
-  CComPtr<IXMLDOMDocument> my_xmldoc;
-  RET_IF_FAILED(CoCreateSafeDOMDocument(&my_xmldoc));
-  RET_IF_FAILED(my_xmldoc->put_preserveWhiteSpace(
-                               VARIANT_BOOL(preserve_whitespace)));
-  CComBSTR xmlmemory(xmlstring);
-  VARIANT_BOOL is_successful(VARIANT_FALSE);
-  RET_IF_FAILED(my_xmldoc->loadXML(xmlmemory, &is_successful));
-  if (!is_successful) {
-    CComPtr<IXMLDOMParseError> error;
-    CString error_message;
-    RET_IF_FAILED(GetXMLParseError(my_xmldoc, &error));
-    ASSERT1(error);
-    HRESULT error_code = 0;
-    RET_IF_FAILED(InterpretXMLParseError(error, &error_code, &error_message));
-    UTIL_LOG(LE, (L"[LoadXMLFromMemory][parse error: %s]", error_message));
-    ASSERT1(FAILED(error_code));
-    return FAILED(error_code) ? error_code : CI_E_XML_LOAD_ERROR;
-  }
-  *xmldoc = my_xmldoc.Detach();
-  return S_OK;
-}
-
-HRESULT LoadXMLFromRawData(const std::vector<byte>& xmldata,
-                           bool preserve_whitespace,
-                           IXMLDOMDocument** xmldoc) {
-  ASSERT1(xmldoc);
-  ASSERT1(!*xmldoc);
-
-  *xmldoc = NULL;
-  if (!xmldata.size()) {
-    return E_INVALIDARG;
-  }
-
-  CComPtr<IXMLDOMDocument> my_xmldoc;
-  RET_IF_FAILED(CoCreateSafeDOMDocument(&my_xmldoc));
-  RET_IF_FAILED(my_xmldoc->put_preserveWhiteSpace(
-                              VARIANT_BOOL(preserve_whitespace)));
-
-  CComSafeArray<byte> xmlsa;
-  xmlsa.Add(xmldata.size(), &xmldata.front());
-  CComVariant xmlvar(xmlsa);
-
-  VARIANT_BOOL is_successful(VARIANT_FALSE);
-  RET_IF_FAILED(my_xmldoc->load(xmlvar, &is_successful));
-  if (!is_successful) {
-    CComPtr<IXMLDOMParseError> error;
-    CString error_message;
-    RET_IF_FAILED(GetXMLParseError(my_xmldoc, &error));
-    ASSERT1(error);
-    HRESULT error_code = 0;
-    RET_IF_FAILED(InterpretXMLParseError(error, &error_code, &error_message));
-    UTIL_LOG(LE, (_T("[LoadXMLFromRawData][parse error: %s]"), error_message));
-    ASSERT1(FAILED(error_code));
-    return FAILED(error_code) ? error_code : CI_E_XML_LOAD_ERROR;
-  }
-  *xmldoc = my_xmldoc.Detach();
-  return S_OK;
-}
-
-HRESULT SaveXMLToFile(IXMLDOMDocument* xmldoc, const TCHAR* xmlfile) {
-  ASSERT1(xmldoc);
-  ASSERT1(xmlfile);
-
-  CComBSTR my_xmlfile(xmlfile);
-  RET_IF_FAILED(xmldoc->save(CComVariant(my_xmlfile)));
-  return S_OK;
-}
-
-HRESULT SaveXMLToMemory(IXMLDOMDocument* xmldoc, CString* xmlstring) {
-  ASSERT1(xmldoc);
-  ASSERT1(xmlstring);
-
-  CComBSTR xmlmemory;
-  RET_IF_FAILED(xmldoc->get_xml(&xmlmemory));
-  *xmlstring = xmlmemory;
-
-  return S_OK;
-}
-
-HRESULT SaveXMLToRawData(IXMLDOMDocument* xmldoc, std::vector<byte>* buffer) {
-  ASSERT1(xmldoc);
-  ASSERT1(buffer);
-
-  CComPtr<IStream> stream;
-  HRESULT hr = ::CreateStreamOnHGlobal(NULL, TRUE, &stream);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  ASSERT1(stream);
-
-  hr = xmldoc->save(CComVariant(stream));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // To get the exact size of the stream, we have to use the seek function.
-  LARGE_INTEGER li = {0, 0};
-  ULARGE_INTEGER uli = {0, 0};
-  hr = stream->Seek(li, STREAM_SEEK_END, &uli);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  buffer->resize(static_cast<size_t>(uli.QuadPart));
-
-  HGLOBAL hglobal = NULL;
-  hr = ::GetHGlobalFromStream(stream, &hglobal);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  memcpy_s(&buffer->front(),
-           buffer->size(),
-           ::GlobalLock(hglobal),
-           buffer->size());
-  ::GlobalUnlock(hglobal);
-
-  return S_OK;
-}
-
-HRESULT CanonicalizeXML(const TCHAR* xmlstring, CString* canonical_xmlstring) {
-  ASSERT1(xmlstring);
-  ASSERT1(canonical_xmlstring);
-
-  // Round-trip through MSXML, having it strip whitespace.
-
-  CComPtr<IXMLDOMDocument> xmldoc;
-  RET_IF_FAILED(CoCreateSafeDOMDocument(&xmldoc));
-  RET_IF_FAILED(xmldoc->put_preserveWhiteSpace(VARIANT_FALSE));
-  {
-    CComBSTR xmlmemory(StringAfterBOM(xmlstring));
-    VARIANT_BOOL is_successful(VARIANT_FALSE);
-    RET_IF_FAILED(xmldoc->loadXML(xmlmemory, &is_successful));
-    if (!is_successful) {
-      CComPtr<IXMLDOMParseError> error;
-      CString error_message;
-      RET_IF_FAILED(GetXMLParseError(xmldoc, &error));
-      ASSERT1(error);
-      HRESULT error_code = 0;
-      RET_IF_FAILED(InterpretXMLParseError(error, &error_code, &error_message));
-      UTIL_LOG(LE, (L"[CanonicalizeXML][parse error: %s]", error_message));
-      ASSERT1(FAILED(error_code));
-      return FAILED(error_code) ? error_code : CI_E_XML_LOAD_ERROR;
-    }
-  }
-  std::vector<CString> lines;
-  {
-    CComBSTR xmlmemory2;
-    RET_IF_FAILED(xmldoc->get_xml(&xmlmemory2));
-    TextToLines(CString(xmlmemory2), L"\r\n", &lines);
-  }
-  {
-    for (size_t i = 0; i < lines.size(); ++i) {
-      TrimString(lines[i], L" \t");
-    }
-    LinesToText(lines, L"", canonical_xmlstring);
-  }
-
-  return S_OK;
-}
-
-bool operator==(const XMLFQName& u, const XMLFQName& v) {
-  if (u.uri && v.uri) {
-    // Both uris are non-null -> compare all the components.
-    return !_tcscmp(u.uri, v.uri) && !_tcscmp(u.base, v.base);
-  } else if (!u.uri && !v.uri) {
-    // Both uris are null -> only compare the base names.
-    return !_tcscmp(u.base ? u.base : __T(""), v.base ? v.base : __T(""));
-  } else {
-    // Either uri is null -> the names are in different namespaces.
-    return false;
-  }
-}
-
-bool operator!=(const XMLFQName& u, const XMLFQName& v) {
-  return !(u == v);
-}
-
-bool operator<(const XMLFQName& u, const XMLFQName &v) {
-  if (u.uri && v.uri) {
-    return (_tcscmp(u.uri, v.uri) < 0) ||
-            ((_tcscmp(u.uri, v.uri) == 0) && (_tcscmp(u.base, v.base) < 0));
-  } else if (!u.uri && !v.uri) {
-    return _tcscmp(u.base, v.base) < 0;
-  } else {
-    return false;
-  }
-}
-
-bool operator>(const XMLFQName& u, const XMLFQName& v) {
-  return v < u;
-}
-
-bool operator<=(const XMLFQName& u, const XMLFQName& v) {
-  return !(v < u);
-}
-
-bool operator>=(const XMLFQName& u, const XMLFQName& v) {
-  return !(u < v);
-}
-
-bool EqualXMLName(const XMLFQName& u, const XMLFQName& v) {
-  return u == v;
-}
-
-// msxml returns a null uri for nodes that don't belong to a namespace.
-bool EqualXMLName(IXMLDOMNode* pnode, const XMLFQName& u) {
-  CComBSTR name;
-  CComBSTR uri;
-  if (FAILED(pnode->get_baseName(&name)) ||
-      FAILED(pnode->get_namespaceURI(&uri))) {
-    return false;
-  }
-  return EqualXMLName(XMLFQName(uri, name), u);
-}
-
-inline bool EqualXMLName(const XMLFQName& u, IXMLDOMNode* pnode) {
-  return EqualXMLName(pnode, u);
-}
-
-HRESULT GetXMLFQName(IXMLDOMNode* node, XMLFQName* name) {
-  ASSERT1(node);
-  ASSERT1(name);
-
-  CComBSTR basename, uri;
-  RET_IF_FAILED(node->get_baseName(&basename));
-  RET_IF_FAILED(node->get_namespaceURI(&uri));
-  *name = XMLFQName(uri, basename);
-  return S_OK;
-}
-
-CString XMLFQNameToString(const XMLFQName& fqname) {
-  CString name;
-  if (fqname.uri) {
-    name += fqname.uri;
-    name += L":";
-  }
-  if (fqname.base) {
-    name += fqname.base;
-  }
-  return name;
-}
-
-CString NodeToString(IXMLDOMNode* pnode) {
-  ASSERT1(pnode);
-
-  XMLFQName node_name;
-  if (SUCCEEDED(GetXMLFQName(pnode, &node_name))) {
-    return XMLFQNameToString(node_name);
-  }
-  return L"";
-}
-
-HRESULT CreateXMLNode(IXMLDOMDocument* xmldoc,
-                      int node_type,
-                      const TCHAR* node_name,
-                      const TCHAR* namespace_uri,
-                      const TCHAR* text,
-                      IXMLDOMNode** node_out) {
-  ASSERT1(xmldoc);
-  ASSERT1(node_name);
-  // namespace_uri can be NULL
-  // text can be NULL
-  ASSERT1(node_out);
-  ASSERT1(!*node_out);
-
-  *node_out = NULL;
-  CComPtr<IXMLDOMNode> new_node;
-  CComBSTR node_name_string, namespace_uri_string;
-  RET_IF_FAILED(node_name_string.Append(node_name));
-  RET_IF_FAILED(namespace_uri_string.Append(namespace_uri));
-  RET_IF_FAILED(xmldoc->createNode(CComVariant(node_type),
-                                   node_name_string,
-                                   namespace_uri_string,
-                                   &new_node));
-  ASSERT1(new_node);
-
-  // If any text was supplied, put it in the node
-  if (text && text[0]) {
-    RET_IF_FAILED(new_node->put_text(CComBSTR(text)));
-  }
-
-  *node_out = new_node.Detach();
-  return S_OK;
-}
-
-HRESULT AppendXMLNode(IXMLDOMNode* xmlnode, IXMLDOMNode* new_child) {
-  ASSERT1(xmlnode);
-  ASSERT1(new_child);
-
-  CComPtr<IXMLDOMNode> useless;
-  RET_IF_FAILED(xmlnode->appendChild(new_child, &useless));
-  return S_OK;
-}
-
-HRESULT AppendXMLNode(IXMLDOMNode* xmlnode, const TCHAR* text) {
-  ASSERT1(xmlnode);
-  // text can be NULL
-
-  if (text && text[0]) {
-    CComPtr<IXMLDOMDocument> xml_doc;
-    CComPtr<IXMLDOMText> text_node;
-    RET_IF_FAILED(xmlnode->get_ownerDocument(&xml_doc));
-    ASSERT1(xml_doc);
-    RET_IF_FAILED(xml_doc->createTextNode(CComBSTR(text), &text_node));
-    RET_IF_FAILED(AppendXMLNode(xmlnode, text_node));
-  }
-  return S_OK;
-}
-
-HRESULT AddXMLAttributeNode(IXMLDOMNode* xmlnode, IXMLDOMAttribute* new_child) {
-  ASSERT1(xmlnode);
-  ASSERT1(new_child);
-
-  CComPtr<IXMLDOMNamedNodeMap> attributes;
-  CComPtr<IXMLDOMNode> useless;
-  RET_IF_FAILED(xmlnode->get_attributes(&attributes));
-  RET_IF_FAILED(attributes->setNamedItem(new_child, &useless));
-  return S_OK;
-}
-
-HRESULT AddXMLAttributeNode(IXMLDOMElement* xmlelement,
-                            const TCHAR* attribute_name,
-                            const TCHAR* attribute_value) {
-  ASSERT1(xmlelement);
-  ASSERT1(attribute_name);
-  // attribute_value can be NULL
-
-  RET_IF_FAILED(xmlelement->setAttribute(CComBSTR(attribute_name),
-                                         CComVariant(attribute_value)));
-  return S_OK;
-}
-
-HRESULT AddXMLAttributeNode(IXMLDOMNode* xmlnode,
-                            const TCHAR* attribute_namespace,
-                            const TCHAR* attribute_name,
-                            const TCHAR* attribute_value) {
-  ASSERT1(xmlnode);
-  ASSERT1(attribute_name);
-  // attribute_namespace can be NULL
-  // attribute_value can be NULL
-
-  CComPtr<IXMLDOMDocument> xmldoc;
-  RET_IF_FAILED(xmlnode->get_ownerDocument(&xmldoc));
-  ASSERT1(xmldoc);
-
-  CComPtr<IXMLDOMNode> attribute_node;
-  RET_IF_FAILED(CreateXMLNode(xmldoc,
-                              NODE_ATTRIBUTE,
-                              attribute_name,
-                              attribute_namespace,
-                              attribute_value,
-                              &attribute_node));
-  CComQIPtr<IXMLDOMAttribute> attribute(attribute_node);
-  ASSERT1(attribute);
-  RET_IF_FAILED(AddXMLAttributeNode(xmlnode, attribute));
-  return S_OK;
-}
-
-HRESULT RemoveXMLChildrenByName(IXMLDOMNode* xmlnode, const XMLFQName& name) {
-  ASSERT1(xmlnode);
-
-  CComPtr<IXMLDOMNodeList> node_list;
-  RET_IF_FAILED(xmlnode->get_childNodes(&node_list));
-  ASSERT1(node_list);
-
-  bool found = false;
-  do {
-    found = false;
-    long count = 0;   // NOLINT
-    RET_IF_FAILED(node_list->get_length(&count));
-    RET_IF_FAILED(node_list->reset());
-
-    for (int i = 0; i < count; ++i) {
-      CComPtr<IXMLDOMNode> child_node, useless;
-      RET_IF_FAILED(node_list->get_item(i, &child_node));
-      ASSERT1(child_node);
-      if (EqualXMLName(child_node, name)) {
-        RET_IF_FAILED(xmlnode->removeChild(child_node, &useless));
-        // Start loop over: the list is "alive" and changes when you remove a
-        // node from it. Yes this seems to be n^2 but in fact we expect at
-        // most one each of <Hash> and/or <Size> nodes.
-        found = true;
-        break;
-      }
-    }
-  } while (found);
-
-  return S_OK;
-}
-
-HRESULT GetXMLChildByName(IXMLDOMElement* xmlnode,
-                          const TCHAR* child_name,
-                          IXMLDOMNode** xmlchild) {
-  ASSERT1(xmlnode);
-  ASSERT1(child_name);
-  ASSERT1(xmlchild);
-  ASSERT1(!*xmlchild);
-
-  *xmlchild = NULL;
-  CComPtr<IXMLDOMNodeList> node_list;
-  long node_list_length = 0;    // NOLINT
-  RET_IF_FAILED(xmlnode->getElementsByTagName(CComBSTR(child_name),
-                                              &node_list));
-  ASSERT1(node_list);
-  RET_IF_FAILED(node_list->get_length(&node_list_length));
-  if (node_list_length <= 0) {
-    return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
-  }
-  // Should only be one child node with name we're looking for.
-  if (node_list_length > 1) {
-    return CI_E_INVALID_MANIFEST;
-  }
-  RET_IF_FAILED(node_list->reset());
-  RET_IF_FAILED(node_list->get_item(0, xmlchild));
-  ASSERT1(*xmlchild);
-  return S_OK;
-}
-
-HRESULT InsertXMLBeforeItem(IXMLDOMNode* xmlnode,
-                            IXMLDOMNode* new_child,
-                            size_t item_number) {
-  ASSERT1(xmlnode);
-  ASSERT1(new_child);
-
-  CComPtr<IXMLDOMNodeList> child_list;
-  CComPtr<IXMLDOMNode> refchild, useless;
-
-  RET_IF_FAILED(xmlnode->get_childNodes(&child_list));
-  ASSERT1(child_list);
-  RET_IF_FAILED(child_list->get_item(item_number, &refchild));
-  ASSERT1(refchild);
-  RET_IF_FAILED(xmlnode->insertBefore(new_child,
-                                      CComVariant(refchild),
-                                      &useless));
-  return S_OK;
-}
-
-HRESULT GetXMLParseError(IXMLDOMDocument* xmldoc,
-                         IXMLDOMParseError** parse_error) {
-  ASSERT1(xmldoc);
-  ASSERT1(parse_error);
-  ASSERT1(!*parse_error);
-
-  *parse_error = NULL;
-  CComPtr<IXMLDOMParseError> error;
-  RET_IF_FAILED(xmldoc->get_parseError(&error));
-  HRESULT error_code = 0;
-  HRESULT hr = error->get_errorCode(&error_code);
-  if (hr == S_OK) {
-    *parse_error = error.Detach();
-    return S_OK;
-  } else if (hr == S_FALSE) {
-    // No parse error
-    return S_FALSE;
-  } else {
-    return hr;
-  }
-}
-
-HRESULT InterpretXMLParseError(IXMLDOMParseError* parse_error,
-                               HRESULT* error_code,
-                               CString* message) {
-  ASSERT1(parse_error);
-  ASSERT1(error_code);
-  ASSERT1(message);
-
-  long line = 0;      // NOLINT
-  long char_pos = 0;  // NOLINT
-  CComBSTR src_text, reason;
-  RET_IF_FAILED(parse_error->get_errorCode(error_code));
-  RET_IF_FAILED(parse_error->get_line(&line));
-  RET_IF_FAILED(parse_error->get_linepos(&char_pos));
-  RET_IF_FAILED(parse_error->get_srcText(&src_text));
-  RET_IF_FAILED(parse_error->get_reason(&reason));
-
-  // Wild guess.
-  size_t size_estimate = src_text.Length() + reason.Length() + 100;
-
-  // TODO(omaha): think about replacing this call to _snwprintf with a
-  // safestring function.
-  std::vector<TCHAR> s(size_estimate);
-  _snwprintf_s(&s.front(), size_estimate, _TRUNCATE,
-               L"%d(%d) : error 0x%08lx: %s\n  %s",
-               line, char_pos, *error_code,
-               reason ? reason : L"",
-               src_text ? src_text : L"<no source text>");
-  // _snwprintf doesn't terminate the string with a null if
-  // the formatted string fills the entire buffer.
-  s[s.size()- 1] = L'\0';
-  *message = &s.front();
-  return S_OK;
-}
-
-HRESULT GetNumChildren(IXMLDOMNode* node, int* num_children) {
-  ASSERT1(node);
-  ASSERT1(num_children);
-
-  *num_children = 0;
-  CComPtr<IXMLDOMNodeList> children;
-  RET_IF_FAILED(node->get_childNodes(&children));
-  ASSERT1(children);
-
-  long len = 0;   // NOLINT
-  RET_IF_FAILED(children->get_length(&len));
-  *num_children = len;
-  return S_OK;
-}
-
-int GetNumAttributes(IXMLDOMNode* node) {
-  ASSERT1(node);
-
-  CComPtr<IXMLDOMNamedNodeMap> attr_map;
-  if (FAILED(node->get_attributes(&attr_map))) {
-    return 0;
-  }
-  ASSERT1(attr_map);
-  long len = 0;   // NOLINT
-  if (FAILED(attr_map->get_length(&len))) {
-    return 0;
-  }
-  return len;
-}
-
-}  // namespace omaha
-
diff --git a/common/xml_utils.h b/common/xml_utils.h
deleted file mode 100644
index 8ce60cc..0000000
--- a/common/xml_utils.h
+++ /dev/null
@@ -1,224 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// xml_utils.h
-//
-// Utilities for working with XML files via MSXML.
-
-#ifndef OMAHA_COMMON_XML_UTILS_H__
-#define OMAHA_COMMON_XML_UTILS_H__
-
-#include <windows.h>
-#include <objbase.h>
-#include <msxml.h>
-#include <atlstr.h>
-#include <utility>
-#include <vector>
-
-namespace omaha {
-
-// Creates a DOMDocument that disallows external definitions to be included and
-// resolved as part of the XML document stream at parse time.
-HRESULT CoCreateSafeDOMDocument(IXMLDOMDocument** my_xmldoc);
-
-// xmlfile can be any specified encoding.
-HRESULT LoadXMLFromFile(const TCHAR* xmlfile,
-                        bool preserve_whitespace,
-                        IXMLDOMDocument** xmldoc);
-
-// xmlstring must be UTF-16 or UCS-2.
-HRESULT LoadXMLFromMemory(const TCHAR* xmlstring,
-                          bool preserve_whitespace,
-                          IXMLDOMDocument** xmldoc);
-
-// xmldata can be any raw data supported by xml parser
-HRESULT LoadXMLFromRawData(const std::vector<byte>& xmldata,
-                           bool preserve_whitespace,
-                           IXMLDOMDocument** xmldoc);
-
-// xmlfile is in encoding specified in the XML document.
-HRESULT SaveXMLToFile(IXMLDOMDocument* xmldoc, const TCHAR * xmlfile);
-
-// xmlstring is in UCS-2
-HRESULT SaveXMLToMemory(IXMLDOMDocument* xmldoc, CString* xmlstring);
-
-// buffer is in the encoding specified in the XML document.
-HRESULT SaveXMLToRawData(IXMLDOMDocument* xmldoc, std::vector<byte>* buffer);
-
-// Canonicalizes the XML string so you can compute a signature on it.
-// This is not the official canonicalization but a cheaper scheme which
-// depends on the whitespace stripping capability of MSXML.
-//
-// xmlstring is in UTF-16 or UCS-2
-HRESULT CanonicalizeXML(const TCHAR* xmlstring, CString* canonical_xmlstring);
-
-
-// Dealing with element/attribute names: the combination of a base name
-// and a namespace URI is a fully-qualified XML name, or: XMLFQName.
-
-// We can't just typedef a std::pair because we need proper comparison operators
-// in case we want to stick a XMLFQName into a standard collection.
-struct XMLFQName {
-  XMLFQName();
-  XMLFQName(const TCHAR* u, const TCHAR* b);
-  ~XMLFQName();
-
-  CString uri;
-  CString base;
-};
-
-bool operator==(const XMLFQName& u, const XMLFQName& v);
-bool operator!=(const XMLFQName& u, const XMLFQName& v);
-bool operator< (const XMLFQName& u, const XMLFQName& v);
-bool operator> (const XMLFQName& u, const XMLFQName& v);
-bool operator<=(const XMLFQName& u, const XMLFQName& v);
-bool operator>=(const XMLFQName& u, const XMLFQName& v);
-
-bool EqualXMLName(const XMLFQName& u, const XMLFQName& v);
-bool EqualXMLName(IXMLDOMNode* pnode, const XMLFQName& u);
-bool EqualXMLName(const XMLFQName& u, IXMLDOMNode* pnode);
-
-// Returns the FQ name from the node.
-HRESULT GetXMLFQName(IXMLDOMNode* node, XMLFQName* name);
-
-// Returns a string version of an XMLFQName suitable for debugging use.
-CString XMLFQNameToString(const XMLFQName& fqname);
-
-// Returns a string version of a node's name suitable for debugging use.
-CString NodeToString(IXMLDOMNode* pnode);
-
-//
-// Routines for dealing with fragments of DOM trees.
-//
-// Creates an XMLDOMNode of the given type with a given name and optional text.
-HRESULT CreateXMLNode(IXMLDOMDocument* xmldoc,
-                      int node_type,
-                      const TCHAR* node_name,
-                      const TCHAR* namespace_uri,
-                      const TCHAR* text,
-                      IXMLDOMNode** node_out);
-
-// Adds newchild as a child node of xmlnode after all existing children.
-HRESULT AppendXMLNode(IXMLDOMNode* xmlnode, IXMLDOMNode* new_child);
-
-// Adds text as a child node of xmlnode after all existing children.
-HRESULT AppendXMLNode(IXMLDOMNode* xmlnode, const TCHAR* text);
-
-// Adds newchild as an attribute node of xmlnode replacing existing
-// attribute with same name.
-HRESULT AddXMLAttributeNode(IXMLDOMNode* xmlnode, IXMLDOMAttribute* new_child);
-
-// Adds name/value pair as an attribute node of xmlnode replacing
-// existing attribute with same name.
-HRESULT AddXMLAttributeNode(IXMLDOMElement* xmlelement,
-                            const TCHAR* attribute_name,
-                            const TCHAR* attribute_value);
-
-// Adds name/value pair as an attribute node of xmlnode replacing
-// existing attribute with same name.
-// Can add attributes to nodes other than IXMLDOMElement.
-// Can add attributes with non-null namespaces.
-HRESULT AddXMLAttributeNode(IXMLDOMNode* xmlnode,
-                            const TCHAR* attribute_namespace,
-                            const TCHAR* attribute_name,
-                            const TCHAR* attribute_value);
-
-// Removes all children of the given node that have the specified name.
-HRESULT RemoveXMLChildrenByName(IXMLDOMNode* xmlnode, const XMLFQName& name);
-
-// Gets a child of a given node by name
-HRESULT GetXMLChildByName(IXMLDOMElement* xmlnode,
-                          const TCHAR* child_name,
-                          IXMLDOMNode** xmlchild);
-
-// Adds newchild as a child node of xmlnode, before the exiting
-// child item_number.
-HRESULT InsertXMLBeforeItem(IXMLDOMNode* xmlnode,
-                            IXMLDOMNode* new_child,
-                            size_t item_number);
-
-// Gets parse error information after a failed load.
-HRESULT GetXMLParseError(IXMLDOMDocument* xmldoc,
-                         IXMLDOMParseError** parse_error);
-
-// Interprets parse error.
-HRESULT InterpretXMLParseError(IXMLDOMParseError* parse_error,
-                               HRESULT* error_code,
-                               CString* message);
-
-// Gets the number of children of this node.
-HRESULT GetNumChildren(IXMLDOMNode* pnode, int* num_children);
-
-// Gets the number of attributes of this node.
-int GetNumAttributes(IXMLDOMNode* pnode);
-
-// Maps over a list of XML DOM nodes of some kind, executing a function
-// against each attribute in the list. Passes a cookie along to each
-// function call useful for accumulating results.
-// Template class List is usually a IXMLDOMNodeList or a IXMLDOMNamedNodeMap.
-template <class List, class Cookie>
-HRESULT ForEachNodeInList(List list,
-                          HRESULT (*fun)(CComPtr<IXMLDOMNode>, Cookie),
-                          Cookie cookie) {
-  ASSERT1(list);  // List assumed to be a pointer type or smart pointer type
-  ASSERT1(fun);
-
-  long len = 0;   // NOLINT
-  RET_IF_FAILED(list->get_length(&len));
-  for (long i = 0; i != len; ++i) {   // NOLINT
-    CComPtr<IXMLDOMNode> pnode;
-    RET_IF_FAILED(list->get_item(i, &pnode));
-    ASSERT1(pnode);
-    RET_IF_FAILED(fun(pnode, cookie));
-  }
-  return S_OK;
-}
-
-// Maps over the attributes of a node, executing a function against each
-// attribute. Passes a cookie along to each function call.
-template <typename Cookie>
-HRESULT ForEachAttribute(CComPtr<IXMLDOMNode> pnode,
-                         HRESULT (*fun)(CComPtr<IXMLDOMNode>, Cookie),
-                         Cookie cookie) {
-  ASSERT1(pnode);
-  ASSERT1(fun);
-
-  CComPtr<IXMLDOMNamedNodeMap> attr_list;
-  RET_IF_FAILED(pnode->get_attributes(&attr_list));
-  ASSERT1(attr_list);
-  RET_IF_FAILED(ForEachNodeInList(attr_list, fun, cookie));
-  return S_OK;
-}
-
-// Maps over the children nodes of a node, executing a function against
-// each child node. Passes a cookie along to each function call.
-template <typename Cookie>
-HRESULT ForEachChildNode(CComPtr<IXMLDOMNode> pnode,
-                         HRESULT (*fun)(CComPtr<IXMLDOMNode>, Cookie),
-                         Cookie cookie) {
-  ASSERT1(pnode);
-  ASSERT1(fun);
-
-  CComPtr<IXMLDOMNodeList> child_list;
-  RET_IF_FAILED(pnode->get_childNodes(&child_list));
-  ASSERT1(child_list);
-  RET_IF_FAILED(ForEachNodeInList(child_list, fun, cookie));
-  return S_OK;
-}
-
-}  // namespace omaha
-
-#endif  // OMAHA_COMMON_XML_UTILS_H__
-
diff --git a/common/xml_utils_unittest.cc b/common/xml_utils_unittest.cc
deleted file mode 100644
index b0ff18c..0000000
--- a/common/xml_utils_unittest.cc
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <windows.h>
-#include <atlstr.h>
-#include <vector>
-#include "omaha/common/file.h"
-#include "omaha/common/module_utils.h"
-#include "omaha/common/string.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/xml_utils.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-const TCHAR kTestXMLFile[] = _T("manifest.xml");
-const TCHAR kTempXMLFile[] = _T("foobar.xml");
-
-const XMLFQName fqLowNoURI(NULL, _T("Bar"));
-const XMLFQName fqLowNoURI2(NULL, _T("Bar"));
-const XMLFQName fqHighNoURI(NULL, _T("Foo"));
-const XMLFQName fqLowURI(_T("Zebra"), _T("Bar"));
-const XMLFQName fqLowURI2(_T("Zebra"), _T("Bar"));
-const XMLFQName fqHighURI(_T("Zebra"), _T("Foo"));
-const XMLFQName fqDifferentURI(_T("Xray"), _T("Bar"));
-
-TEST(XmlUtilsTest, XMLFQName) {
-  ASSERT_TRUE(fqLowNoURI == fqLowNoURI2);
-  ASSERT_TRUE(fqHighNoURI == fqHighNoURI);
-  ASSERT_TRUE(fqLowNoURI != fqHighNoURI);
-  ASSERT_TRUE(fqLowNoURI != fqLowURI);
-  ASSERT_TRUE(fqLowNoURI < fqHighNoURI);
-  ASSERT_TRUE(fqLowNoURI <= fqHighNoURI);
-  ASSERT_TRUE(fqHighNoURI > fqLowNoURI);
-  ASSERT_TRUE(fqHighNoURI >= fqLowNoURI);
-  ASSERT_TRUE(fqLowURI == fqLowURI2);
-  ASSERT_TRUE(fqHighURI == fqHighURI);
-  ASSERT_TRUE(fqLowURI != fqHighURI);
-  ASSERT_TRUE(fqLowURI < fqHighURI);
-  ASSERT_TRUE(fqLowURI <= fqHighURI);
-  ASSERT_TRUE(fqHighURI > fqLowURI);
-  ASSERT_TRUE(fqHighURI >= fqLowURI);
-  ASSERT_TRUE(fqLowURI != fqDifferentURI);
-}
-
-TEST(XmlUtilsTest, LoadSave) {
-  scoped_co_init co_init;
-
-  // Get some directory and file names to start with.
-  TCHAR directory[MAX_PATH] = {0};
-  ASSERT_TRUE(GetModuleDirectory(NULL, directory));
-  CString test_file;
-  test_file.Format(_T("%s\\unittest_support\\%s"), directory, kTestXMLFile);
-
-  TCHAR temp_path[MAX_PATH] = {0};
-  ASSERT_TRUE(::GetTempPath(MAX_PATH, temp_path));
-  CString temp_file;
-  temp_file.AppendFormat(_T("%s%s"), temp_path, kTempXMLFile);
-
-  // Test loading and storing to a file.
-  CComPtr<IXMLDOMDocument> xmldoc;
-  ASSERT_SUCCEEDED(LoadXMLFromFile(test_file, true, &xmldoc));
-  ASSERT_TRUE(xmldoc);
-  ASSERT_SUCCEEDED(SaveXMLToFile(xmldoc, temp_file));
-
-  // Test loading and storing raw UTF8 data to memory.
-  std::vector<byte> buffer_utf8;
-  ASSERT_SUCCEEDED(ReadEntireFile(temp_file, 0, &buffer_utf8));
-
-  CComPtr<IXMLDOMDocument> xmldoc2;
-  ASSERT_SUCCEEDED(LoadXMLFromRawData(buffer_utf8, true, &xmldoc2));
-  ASSERT_TRUE(xmldoc2);
-  std::vector<byte> xml_utf8;
-  ASSERT_SUCCEEDED(SaveXMLToRawData(xmldoc2, &xml_utf8));
-
-  CStringA input_utf8(reinterpret_cast<char*>(&buffer_utf8.front()),
-                      buffer_utf8.size());
-  CStringA output_utf8(reinterpret_cast<char*>(&xml_utf8.front()),
-                       xml_utf8.size());
-  ASSERT_STREQ(input_utf8, output_utf8);
-
-  // Test loading and storing Unicode to memory.
-  // The input must be Unicode for the calls below, but our test file is UTF-8.
-  // So read it and convert it to Unicode.
-  int len(::MultiByteToWideChar(CP_UTF8,
-                                0, /*flags*/
-                                reinterpret_cast<const char*>(&buffer_utf8[0]),
-                                buffer_utf8.size(),
-                                NULL,
-                                0));
-  std::vector<wchar_t> buffer_unicode(len+1);
-  int len2(::MultiByteToWideChar(CP_UTF8,
-                                 0, /*flags*/
-                                 reinterpret_cast<const char*>(&buffer_utf8[0]),
-                                 buffer_utf8.size(),
-                                 &buffer_unicode[0],
-                                 len));
-  ASSERT_EQ(len, len2);
-  buffer_unicode[len] = 0;  // null terminate the unicode string.
-
-  // Now round-trip the load from memory and save to memory.
-  xmldoc2 = NULL;
-  ASSERT_SUCCEEDED(LoadXMLFromMemory(&buffer_unicode.front(), true, &xmldoc2));
-  ASSERT_TRUE(xmldoc2);
-  CString xmlmemory;
-  ASSERT_SUCCEEDED(SaveXMLToMemory(xmldoc2, &xmlmemory));
-
-  // Now compare that the result of the round-trip is the same as the input.
-  CString input(&buffer_unicode.front());
-  CString output(xmlmemory);
-  // Except must first remove the " encoding="UTF-8"" attribute from the
-  // input string.
-  ReplaceCString(input, L" encoding=\"UTF-8\"", L"");
-  ASSERT_STREQ(input, output);
-
-  // Clean up.
-  ASSERT_SUCCEEDED(File::Remove(temp_file));
-}
-
-}  // namespace omaha
-
diff --git a/core/build.scons b/core/build.scons
index d22364f..406d0f2 100644
--- a/core/build.scons
+++ b/core/build.scons
@@ -15,20 +15,17 @@
 
 Import('env')
 
+local_env = env.Clone()
+
 inputs = [
     'core.cc',
     'core_metrics.cc',
     'crash_handler.cc',
     'google_update_core.cc',
-    'legacy_manifest_handler.cc',
     'scheduler.cc',
     'system_monitor.cc',
     ]
 
-# Create a local clone, so the parent environment is
-# unaffected by changes made here.
-local_env = env.Clone()
-
 local_env['CPPPATH'] += [
     '$MAIN_DIR/third_party/breakpad/src/',
 
@@ -38,4 +35,4 @@
     '$OBJ_ROOT',
     ]
 
-local_env.ComponentLibrary('core', inputs)
+local_env.ComponentStaticLibrary('core', inputs)
diff --git a/core/core.cc b/core/core.cc
index e865517..d65dc64 100644
--- a/core/core.cc
+++ b/core/core.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -26,33 +26,37 @@
 #include <map>
 #include <string>
 #include <vector>
-#include "omaha/common/app_util.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/module_utils.h"
+#include "omaha/base/path.h"
+#include "omaha/base/program_instance.h"
+#include "omaha/base/reactor.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/service_utils.h"
+#include "omaha/base/shutdown_handler.h"
+#include "omaha/base/system.h"
+#include "omaha/base/time.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/app_registry_utils.h"
 #include "omaha/common/const_cmd_line.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/module_utils.h"
-#include "omaha/common/path.h"
-#include "omaha/common/reactor.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/service_utils.h"
-#include "omaha/common/shutdown_handler.h"
-#include "omaha/common/system.h"
-#include "omaha/common/time.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
+#include "omaha/common/command_line_builder.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/oem_install_utils.h"
+#include "omaha/common/scheduled_task_utils.h"
+#include "omaha/common/stats_uploader.h"
 #include "omaha/core/core_metrics.h"
-#include "omaha/core/legacy_manifest_handler.h"
 #include "omaha/core/scheduler.h"
 #include "omaha/core/system_monitor.h"
-#include "omaha/goopdate/command_line_builder.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/crash.h"
-#include "omaha/goopdate/program_instance.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/stats_uploader.h"
+#include "omaha/goopdate/resource_manager.h"
+#include "omaha/goopdate/worker.h"
+#include "omaha/net/network_config.h"
+#include "omaha/setup/setup_service.h"
 
 namespace omaha {
 
@@ -88,26 +92,27 @@
     return false;
   }
 
-  if (!goopdate_utils::IsInstalledGoopdateTaskUA(is_system_)) {
+  if (!scheduled_task_utils::IsInstalledGoopdateTaskUA(is_system_)) {
     ++metric_core_run_scheduled_task_missing;
     CORE_LOG(LE, (_T("[UA Task not installed]")));
     return false;
   }
 
-  if (goopdate_utils::IsDisabledGoopdateTaskUA(is_system_)) {
+  if (scheduled_task_utils::IsDisabledGoopdateTaskUA(is_system_)) {
     ++metric_core_run_scheduled_task_disabled;
     CORE_LOG(LE, (_T("[UA Task disabled]")));
     return false;
   }
 
   HRESULT ua_task_last_exit_code =
-      goopdate_utils::GetExitCodeGoopdateTaskUA(is_system_);
+      scheduled_task_utils::GetExitCodeGoopdateTaskUA(is_system_);
 
   if (ua_task_last_exit_code == SCHED_S_TASK_HAS_NOT_RUN &&
       !ConfigManager::Is24HoursSinceInstall(is_system_)) {
     // Not 24 hours yet since install or update. Let us give the UA task the
     // benefit of the doubt, and assume all is well for right now.
-    CORE_LOG(L3, (_T("[Not yet 24 hours since install/update]")));
+    CORE_LOG(L3, (_T("[Core::AreScheduledTasksHealthy]")
+                  _T("[Not yet 24 hours since install/update]")));
     ua_task_last_exit_code = S_OK;
   }
 
@@ -121,37 +126,17 @@
   return true;
 }
 
-bool Core::IsServiceHealthy() const {
-  if (!is_system_) {
-    return true;
-  }
-
-  if (!goopdate_utils::IsServiceInstalled()) {
-    ++metric_core_run_service_missing;
-    CORE_LOG(LE, (_T("[GoogleUpdate Service is not installed]")));
-    return false;
-  }
-
-  if (ServiceUtils::IsServiceDisabled(
-      ConfigManager::GetCurrentServiceName())) {
-    ++metric_core_run_service_disabled;
-    CORE_LOG(LE, (_T("[GoogleUpdate Service is disabled]")));
-    return false;
-  }
-
-  return true;
-}
-
 bool Core::IsCheckingForUpdates() const {
   if (!ConfigManager::Is24HoursSinceInstall(is_system_)) {
-    CORE_LOG(L3, (_T("[Not yet 24 hours since install/update]")));
+    CORE_LOG(L3, (_T("[Core::IsCheckingForUpdates]")
+                  _T("[Not yet 24 hours since install/update]")));
     return true;
   }
 
-  ConfigManager* cm = ConfigManager::Instance();
+  const ConfigManager& cm = *ConfigManager::Instance();
   const int k14DaysSec = 14 * 24 * 60 * 60;
 
-  if (cm->GetTimeSinceLastCheckedSec(is_system_) >= k14DaysSec) {
+  if (cm.GetTimeSinceLastCheckedSec(is_system_) >= k14DaysSec) {
     ++metric_core_run_not_checking_for_updates;
     CORE_LOG(LE, (_T("[LastChecked older than 14 days]")));
     return false;
@@ -170,19 +155,17 @@
 //
 // Under these conditions, Omaha uses the built-in scheduler hosted by the core
 // and it keeps the core running.
-//
-// In addition, for the machine GoogleUpdate, the Core will run all the time if
-// the service is not installed, or is disabled. In this case, Omaha uses the
-// elevator interface hosted by the core, and this keeps the core running.
 bool Core::ShouldRunForever() const {
+  CORE_LOG(L3, (_T("[Core::ShouldRunForever]")));
+
   // The methods are being called individually to enable metrics capture.
   bool are_scheduled_tasks_healthy(AreScheduledTasksHealthy());
-  bool is_service_healthy(IsServiceHealthy());
   bool is_checking_for_updates(IsCheckingForUpdates());
 
-  return !are_scheduled_tasks_healthy ||
-         !is_service_healthy ||
-         !is_checking_for_updates;
+  bool result = !are_scheduled_tasks_healthy ||
+                !is_checking_for_updates;
+  CORE_LOG(L1, (_T("[Core::ShouldRunForever][%u]"), result));
+  return result;
 }
 
 
@@ -191,7 +174,11 @@
   is_system_ = is_system;
   is_crash_handler_enabled_ = is_crash_handler_enabled;
 
-  if (ConfigManager::Instance()->IsOemInstalling(is_system_)) {
+  CORE_LOG(L1, (_T("[is_system_: %d][is_crash_handler_enabled_: %d]"),
+                is_system_, is_crash_handler_enabled_));
+
+  const ConfigManager& cm = *ConfigManager::Instance();
+  if (oem_install_utils::IsOemInstalling(is_system_)) {
     // Exit immediately while an OEM is installing Windows. This prevents cores
     // or update workers from being started by the Scheduled Task or other means
     // before the system is sealed.
@@ -203,26 +190,20 @@
   // Do a code red check as soon as possible.
   StartCodeRed();
 
-  CORE_LOG(L2, (_T("[IsGoogler %d]"), ConfigManager::Instance()->IsGoogler()));
+  CORE_LOG(L2, (_T("[IsInternalUser: %d]"), cm.IsInternalUser()));
 
   NamedObjectAttributes single_core_attr;
-  GetNamedObjectAttributes(kCoreSingleInstance, is_system, &single_core_attr);
+  GetNamedObjectAttributes(kCoreSingleInstance, is_system_, &single_core_attr);
   ProgramInstance instance(single_core_attr.name);
   bool is_already_running = !instance.EnsureSingleInstance();
   if (is_already_running) {
-    OPT_LOG(L1, (_T("[another core instance is already running]")));
+    OPT_LOG(L1, (_T("[Another core instance is already running]")));
     return S_OK;
   }
 
   // TODO(omaha): the user Omaha core should run at medium integrity level and
   // it should deelevate itself if it does not, see bug 1549842.
 
-  // Clean up the initial install directory and ignore the errors.
-  HRESULT hr = CleanUpInitialManifestDirectory();
-  if (FAILED(hr)) {
-    CORE_LOG(LW, (_T("[CleanUpInitialManifestDirectory failed][0x%08x]"), hr));
-  }
-
   // Start the crash handler if necessary.
   if (is_crash_handler_enabled_) {
     HRESULT hr = StartCrashHandler();
@@ -235,7 +216,7 @@
     return S_OK;
   }
 
-  // TODO(Omaha): Delay starting update worker when run at startup.
+  // TODO(omaha): Delay starting update worker when run at startup.
   StartUpdateWorkerInternal();
 
   // Force the main thread to create a message queue so any future WM_QUIT
@@ -246,18 +227,11 @@
 
   reactor_.reset(new Reactor);
   shutdown_handler_.reset(new ShutdownHandler);
-  hr = shutdown_handler_->Initialize(reactor_.get(), this, is_system_);
+  HRESULT hr = shutdown_handler_->Initialize(reactor_.get(), this, is_system_);
   if (FAILED(hr)) {
     return hr;
   }
 
-  if (!is_system_) {
-    // We watch the legacy manifest install directory only if we are the
-    // user core. The omaha1 -> omaha2 machine hand off occurs using the
-    // /UI cmd line switch.
-    VERIFY1(SUCCEEDED(InitializeManifestDirectoryWatcher()));
-  }
-
   scheduler_.reset(new Scheduler(*this));
   hr = scheduler_->Initialize();
   if (FAILED(hr)) {
@@ -268,20 +242,8 @@
   VERIFY1(SUCCEEDED(system_monitor_->Initialize(true)));
   system_monitor_->set_observer(this);
 
-  if (is_system_) {
-     VERIFY(SUCCEEDED(RegisterCoreProxy()),
-            (_T("The core may have been started when the registered version ")
-             _T("of Google Update does not exist or one is not registered.")));
-  }
-
   // Start processing messages and events from the system.
-  hr = DoRun();
-
-  if (is_system) {
-    UnregisterCoreProxy();
-  }
-
-  return hr;
+  return DoRun();
 }
 
 // Signals the core to shutdown. The shutdown method is called by a thread
@@ -293,7 +255,13 @@
 }
 
 HRESULT Core::ShutdownInternal() const {
-  OPT_LOG(L1, (_T("[Google Update is shutting down...]")));
+  LONG atl_module_count(const_cast<Core*>(this)->GetLockCount());
+  if (atl_module_count > 0) {
+    CORE_LOG(L1, (_T("[Core COM server in use][%d]"), atl_module_count));
+    return S_OK;
+  }
+
+  OPT_LOG(L1, (_T("[Google Update core is shutting down...]")));
   ASSERT1(::GetCurrentThreadId() != main_thread_id_);
   if (::PostThreadMessage(main_thread_id_, WM_QUIT, 0, 0)) {
     return S_OK;
@@ -350,19 +318,11 @@
 }
 
 HRESULT Core::StartUpdateWorkerInternal() const {
-  // The uninstall check is tentative. There are stronger checks, protected
-  // by locks, which are done by the worker process.
-  size_t num_clients(0);
-  const bool is_uninstall =
-      FAILED(goopdate_utils::GetNumClients(is_system_, &num_clients)) ||
-      num_clients <= 1;
-
-  CORE_LOG(L2, (_T("[Core::StartUpdateWorker][%u]"), num_clients));
+  CORE_LOG(L2, (_T("[Core::StartUpdateWorkerInternal]")));
 
   CString exe_path = goopdate_utils::BuildGoogleUpdateExePath(is_system_);
   CommandLineBuilder builder(COMMANDLINE_MODE_UA);
-  builder.set_install_source(kCmdLineInstallSourceCore);
-  builder.set_is_uninstall_set(is_uninstall);
+  builder.set_install_source(kCmdLineInstallSource_Core);
   CString cmd_line = builder.GetCommandLineArgs();
   HRESULT hr = System::StartProcessWithArgs(exe_path, cmd_line);
   if (SUCCEEDED(hr)) {
@@ -398,69 +358,16 @@
 HRESULT Core::StartCrashHandler() const {
   CORE_LOG(L2, (_T("[Core::StartCrashHandler]")));
 
-  CString exe_path = goopdate_utils::BuildGoogleUpdateServicesPath(is_system_);
-  CommandLineBuilder builder(COMMANDLINE_MODE_CRASH_HANDLER);
-  CString cmd_line = builder.GetCommandLineArgs();
-  HRESULT hr = System::StartProcessWithArgs(exe_path, cmd_line);
+  HRESULT hr = goopdate_utils::StartCrashHandler(is_system_);
   if (SUCCEEDED(hr)) {
     ++metric_core_start_crash_handler_succeeded;
   } else {
-    CORE_LOG(LE, (_T("[can't start Crash Handler][0x%08x]"), hr));
+    CORE_LOG(LE, (_T("[Cannot start Crash Handler][0x%08x]"), hr));
   }
   ++metric_core_start_crash_handler_total;
   return hr;
 }
 
-HRESULT Core::InitializeManifestDirectoryWatcher() {
-  // We watch the legacy manifest install directory only if we are the
-  // user core. The omaha1 -> omaha2 machine hand off occurs using the
-  // /UI cmd line switch.
-  legacy_manifest_handler_.reset(new LegacyManifestHandler());
-  return legacy_manifest_handler_->Initialize(this);
-}
-
-HRESULT Core::StartInstallWorker() {
-  // Get all the manifests that are present in the handoff directory.
-  CString manifest_dir =
-      ConfigManager::Instance()->GetUserInitialManifestStorageDir();
-  std::vector<CString> manifests;
-  HRESULT hr = File::GetWildcards(manifest_dir, _T("*.gup"), &manifests);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  std::vector<CString>::iterator it;
-  for (it = manifests.begin(); it != manifests.end(); ++it) {
-    // Launch the worker using /UIUser manifest_file.
-    CString filename = *it;
-    EnclosePath(&filename);
-
-    CommandLineBuilder builder(COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF);
-    builder.set_legacy_manifest_path(filename);
-    CString cmd_line = builder.GetCommandLineArgs();
-    HRESULT hr_ret = goopdate_utils::StartGoogleUpdateWithArgs(is_system_,
-                                                               cmd_line,
-                                                               NULL);
-    if (FAILED(hr_ret)) {
-      OPT_LOG(LE, (_T("[StartGoogleUpdateWithArgs failed][0x%08x]"), hr_ret));
-      hr = hr_ret;
-    }
-  }
-
-  return hr;
-}
-
-HRESULT Core::CleanUpInitialManifestDirectory() {
-  CORE_LOG(L2, (_T("[CleanUpInitialManifestDirectory]")));
-
-  const CString dir =
-      ConfigManager::Instance()->GetUserInitialManifestStorageDir();
-  if (dir.IsEmpty()) {
-    return GOOPDATE_E_CORE_INTERNAL_ERROR;
-  }
-  return DeleteDirectoryFiles(dir);
-}
-
 void Core::AggregateMetrics() const {
   CORE_LOG(L2, (_T("[aggregate core metrics]")));
   CollectMetrics();
@@ -514,35 +421,4 @@
   metric_core_disk_space_available = free_bytes_current_user;
 }
 
-HRESULT Core::RegisterCoreProxy() {
-  CComObjectNoLock<GoogleUpdateCore>* google_update_core =
-      new CComObjectNoLock<GoogleUpdateCore>;
-
-  CDacl dacl;
-  dacl.AddAllowedAce(Sids::System(), GENERIC_ALL);
-  dacl.AddAllowedAce(Sids::Users(), GENERIC_READ);
-  CSecurityDesc sd;
-  sd.SetDacl(dacl);
-  sd.MakeAbsolute();
-
-  SharedMemoryAttributes attr(kGoogleUpdateCoreSharedMemoryName, sd);
-  google_update_core_proxy_.reset(new GoogleUpdateCoreProxy(false, &attr));
-
-#if DEBUG
-  // The core interface should be registered only once.
-  CComPtr<IGoogleUpdateCore> core_interface;
-  HRESULT hr = google_update_core_proxy_->GetObject(&core_interface);
-  ASSERT1(FAILED(hr) || !core_interface);
-#endif
-
-  return google_update_core_proxy_->RegisterObject(google_update_core);
-}
-
-void Core::UnregisterCoreProxy() {
-  if (google_update_core_proxy_.get()) {
-    google_update_core_proxy_->RevokeObject();
-  }
-}
-
 }  // namespace omaha
-
diff --git a/core/core.h b/core/core.h
index 6cff074..2ec0dc7 100644
--- a/core/core.h
+++ b/core/core.h
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -27,21 +27,24 @@
 #include <string>
 #include "base/basictypes.h"
 #include "base/scoped_ptr.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/shutdown_callback.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/shutdown_callback.h"
 #include "omaha/core/google_update_core.h"
 #include "omaha/core/system_monitor.h"
+#include "omaha/goopdate/google_update3.h"
 
 namespace omaha {
 
 class Reactor;
 class Scheduler;
 class ShutdownHandler;
-class LegacyManifestHandler;
 
+// To support hosting ATL COM objects, Core derives from CAtlExeModuleT. Other
+// than the ATL module count, no functionality of CAtlExeModuleT is used.
 class Core
     : public ShutdownCallback,
-      public SystemMonitorObserver {
+      public SystemMonitorObserver,
+      public CAtlExeModuleT<Core> {
  public:
   Core();
   virtual ~Core();
@@ -56,9 +59,6 @@
   // Starts a code red process.
   HRESULT StartCodeRed() const;
 
-  // Starts an install worker process.
-  HRESULT StartInstallWorker();
-
   // Starts the crash handler.
   HRESULT StartCrashHandler() const;
 
@@ -68,6 +68,12 @@
   Reactor* reactor() const { return reactor_.get(); }
   bool is_system() const { return is_system_; }
 
+  virtual LONG Unlock() throw() {
+    // We are long-running independent of the ATL module count, therefore
+    // transition to zero does not by itself unload the process.
+    return CAtlModuleT<Core>::Unlock();
+  }
+
  private:
 
   HRESULT DoMain(bool is_system, bool is_crash_handler_enabled);
@@ -91,14 +97,6 @@
 
   HRESULT DoRun();
   HRESULT DoHandleEvents();
-  HRESULT CleanUpInitialManifestDirectory();
-  HRESULT InitializeManifestDirectoryWatcher();
-
-  // Makes available the COM interface implemented by the core.
-  HRESULT RegisterCoreProxy();
-
-  // Revokes the COM interface.
-  void UnregisterCoreProxy();
 
   // Collects ambient core metrics.
   void CollectMetrics()const;
@@ -112,10 +110,8 @@
 
   scoped_ptr<Reactor>               reactor_;
   scoped_ptr<ShutdownHandler>       shutdown_handler_;
-  scoped_ptr<LegacyManifestHandler> legacy_manifest_handler_;
   scoped_ptr<Scheduler>             scheduler_;
   scoped_ptr<SystemMonitor>         system_monitor_;
-  scoped_ptr<GoogleUpdateCoreProxy> google_update_core_proxy_;
 
   friend class CoreUtilsTest;
 
@@ -125,4 +121,3 @@
 }  // namespace omaha
 
 #endif  // OMAHA_CORE_CORE_H_
-
diff --git a/core/core_unittest.cc b/core/core_unittest.cc
index 291c506..e107ea6 100644
--- a/core/core_unittest.cc
+++ b/core/core_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2008-2009 Google Inc.
+// Copyright 2008-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -14,22 +14,22 @@
 // ========================================================================
 
 
-#include "omaha/common/app_util.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/error.h"
-#include "omaha/common/path.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/thread.h"
-#include "omaha/common/time.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/error.h"
+#include "omaha/base/path.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/thread.h"
+#include "omaha/base/time.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/scheduled_task_utils.h"
 #include "omaha/core/core.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
 #include "omaha/setup/setup_service.h"
 #include "omaha/testing/unit_test.h"
-#include "omaha/worker/application_manager.h"
 
 namespace omaha {
 
@@ -46,7 +46,7 @@
  private:
   virtual void Run() {
     Core core;
-    core.Main(is_machine_, true);         // Run the crash handler.
+    core.Main(is_machine_, false);         // Do not run the crash handler.
   }
 
   bool is_machine_;
@@ -60,6 +60,12 @@
   CoreTest() : is_machine_(false) {}
 
   virtual void SetUp() {
+    // The Core has it's own ATL module. ATL does not like having multiple ATL
+    // modules. This TestCase saves and restore the original ATL module to get
+    // around ATL's limitation. This is a hack.
+    original_atl_module_ = _pAtlModule;
+    _pAtlModule = NULL;
+
     ASSERT_HRESULT_SUCCEEDED(IsSystemProcess(&is_machine_));
 
     ConfigManager::Instance()->SetLastCheckedTime(is_machine_, 10);
@@ -71,6 +77,7 @@
   }
 
   virtual void TearDown() {
+     _pAtlModule = original_atl_module_;
   }
 
   HRESULT SignalShutdownEvent() {
@@ -86,6 +93,8 @@
  protected:
   bool is_machine_;
   scoped_event shutdown_event_;
+
+  CAtlModule* original_atl_module_;
 };
 
 // Tests the core shutdown mechanism.
@@ -107,6 +116,8 @@
   EXPECT_HRESULT_SUCCEEDED(SignalShutdownEvent());
   EXPECT_TRUE(thread.WaitTillExit(2000));
   if (thread.Running()) {
+    // If you see a crash here, it was likely caused by Application Verifier.
+    // TODO(omaha): Is there a better way to exit? Should we wait longer?
     thread.Terminate(-1);
   }
   EXPECT_HRESULT_SUCCEEDED(ResetShutdownEvent());
@@ -117,45 +128,51 @@
   CoreUtilsTest() : is_machine_(vista_util::IsUserAdmin()) {}
 
   virtual void SetUp() {
-    core_.is_system_ = is_machine_;
+    // The Core has it's own ATL module. ATL does not like having multiple ATL
+    // modules. This TestCase saves and restore the original ATL module to get
+    // around ATL's limitation. This is a hack.
+    original_atl_module_ = _pAtlModule;
+    _pAtlModule = NULL;
+
+    // The Core must be created after the ATL module work around.
+    core_.reset(new Core);
+    core_->is_system_ = is_machine_;
   }
 
   virtual void TearDown() {
+    _pAtlModule = original_atl_module_;
   }
 
   bool AreScheduledTasksHealthy() {
-    return core_.AreScheduledTasksHealthy();
-  }
-
-  bool IsServiceHealthy() {
-    return core_.IsServiceHealthy();
+    return core_->AreScheduledTasksHealthy();
   }
 
   bool IsCheckingForUpdates() {
-    return core_.IsCheckingForUpdates();
+    return core_->IsCheckingForUpdates();
   }
 
-  static HRESULT DoInstallService(const TCHAR* service_cmd_line,
-                                  const TCHAR* desc) {
-    return SetupService::DoInstallService(service_cmd_line, desc);
+  static HRESULT DoInstallService(const TCHAR* service_cmd_line) {
+    return SetupUpdate3Service::DoInstallService(service_cmd_line);
   }
 
-  static HRESULT DeleteServices() {
-    return SetupService::DeleteServices();
+  static HRESULT DeleteService() {
+    return SetupUpdate3Service::DeleteService();
   }
 
-  Core core_;
+  scoped_ptr<Core> core_;
   bool is_machine_;
+
+  CAtlModule* original_atl_module_;
 };
 
 TEST_F(CoreUtilsTest, AreScheduledTasksHealthy) {
-  EXPECT_SUCCEEDED(goopdate_utils::UninstallGoopdateTasks(is_machine_));
+  EXPECT_SUCCEEDED(scheduled_task_utils::UninstallGoopdateTasks(is_machine_));
   EXPECT_FALSE(AreScheduledTasksHealthy());
 
   CString task_path = ConcatenatePath(app_util::GetCurrentModuleDirectory(),
                                       _T("LongRunningSilent.exe"));
-  EXPECT_SUCCEEDED(goopdate_utils::InstallGoopdateTasks(task_path,
-                                                        is_machine_));
+  EXPECT_SUCCEEDED(scheduled_task_utils::InstallGoopdateTasks(task_path,
+                                                              is_machine_));
   const uint32 now = Time64ToInt32(GetCurrent100NSTime());
   const int k12HourPeriodSec = 12 * 60 * 60;
   const DWORD first_install_12 = now - k12HourPeriodSec;
@@ -165,26 +182,7 @@
       first_install_12));
   EXPECT_TRUE(AreScheduledTasksHealthy());
 
-  EXPECT_SUCCEEDED(goopdate_utils::UninstallGoopdateTasks(is_machine_));
-}
-
-TEST_F(CoreUtilsTest, IsServiceHealthy) {
-  if (!is_machine_) {
-    EXPECT_TRUE(IsServiceHealthy());
-    return;
-  }
-
-  EXPECT_SUCCEEDED(DeleteServices());
-  EXPECT_FALSE(IsServiceHealthy());
-
-  // Using a signed file because some anti-virus programs find this behavior
-  // suspicious with unsigned files.
-  CString service_path = ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                         _T("GoogleUpdate.exe"));
-  EXPECT_SUCCEEDED(DoInstallService(service_path, _T(" ")));
-  EXPECT_TRUE(IsServiceHealthy());
-
-  EXPECT_SUCCEEDED(DeleteServices());
+  EXPECT_SUCCEEDED(scheduled_task_utils::UninstallGoopdateTasks(is_machine_));
 }
 
 TEST_F(CoreUtilsTest, IsCheckingForUpdates) {
@@ -207,8 +205,7 @@
       first_install_48_hours_back));
   EXPECT_FALSE(IsCheckingForUpdates());
 
-  AppManager app_manager(is_machine_);
-  EXPECT_SUCCEEDED(app_manager.UpdateLastChecked());
+  EXPECT_SUCCEEDED(goopdate_utils::UpdateLastChecked(is_machine_));
   EXPECT_TRUE(IsCheckingForUpdates());
 
   const int k15DaysPeriodSec = 15 * 24 * 60 * 60;
@@ -219,4 +216,3 @@
 }
 
 }  // namespace omaha
-
diff --git a/core/crash_handler.cc b/core/crash_handler.cc
index cf87b2c..222964c 100644
--- a/core/crash_handler.cc
+++ b/core/crash_handler.cc
@@ -18,17 +18,17 @@
 // sessions. If the user has turned off crash reporting, this process will not
 // run.
 
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/program_instance.h"
+#include "omaha/base/reactor.h"
+#include "omaha/base/shutdown_handler.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/config_manager.h"
 #include "omaha/core/crash_handler.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/reactor.h"
-#include "omaha/common/shutdown_handler.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/config_manager.h"
 #include "omaha/goopdate/crash.h"
-#include "omaha/goopdate/program_instance.h"
 
 namespace omaha {
 
diff --git a/core/crash_handler.h b/core/crash_handler.h
index cff0563..8871db8 100644
--- a/core/crash_handler.h
+++ b/core/crash_handler.h
@@ -19,8 +19,8 @@
 #include <windows.h>
 #include "base/basictypes.h"
 #include "base/scoped_ptr.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/shutdown_callback.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/shutdown_callback.h"
 #include "third_party/breakpad/src/client/windows/crash_generation/client_info.h"
 #include "third_party/breakpad/src/client/windows/crash_generation/crash_generation_server.h"
 
diff --git a/core/google_update_core.cc b/core/google_update_core.cc
index 58ad6f2..94f6ef7 100644
--- a/core/google_update_core.cc
+++ b/core/google_update_core.cc
@@ -14,32 +14,34 @@
 // ========================================================================
 
 #include "omaha/core/google_update_core.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/exception_barrier.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/system.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/config_manager.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/exception_barrier.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/system.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/goopdate/app_command.h"
 
 namespace omaha {
 
-GoogleUpdateCore::GoogleUpdateCore() {
-  CORE_LOG(L3, (_T("[GoogleUpdateCore::GoogleUpdateCore]")));
+GoogleUpdateCoreBase::GoogleUpdateCoreBase() : StdMarshalInfo(true) {
+  CORE_LOG(L3, (_T("[GoogleUpdateCoreBase::GoogleUpdateCoreBase]")));
 }
 
-GoogleUpdateCore::~GoogleUpdateCore() {
-  CORE_LOG(L3, (_T("[GoogleUpdateCore::~GoogleUpdateCore]")));
+GoogleUpdateCoreBase::~GoogleUpdateCoreBase() {
+  CORE_LOG(L3, (_T("[GoogleUpdateCoreBase::~GoogleUpdateCoreBase]")));
 }
 
-STDMETHODIMP GoogleUpdateCore::LaunchCmdElevated(const WCHAR* app_guid,
-                                                 const WCHAR* cmd_id,
-                                                 DWORD caller_proc_id,
-                                                 ULONG_PTR* proc_handle) {
-  CORE_LOG(L3, (_T("[GoogleUpdateCore::LaunchCmdElevated]")
+STDMETHODIMP GoogleUpdateCoreBase::LaunchCmdElevated(const WCHAR* app_guid,
+                                                     const WCHAR* cmd_id,
+                                                     DWORD caller_proc_id,
+                                                     ULONG_PTR* proc_handle) {
+  CORE_LOG(L3, (_T("[GoogleUpdateCoreBase::LaunchCmdElevated]")
                 _T("[app %s][cmd %s][pid %d]"),
                 app_guid, cmd_id, caller_proc_id));
 
@@ -61,80 +63,46 @@
     return hr;
   }
 
-  CString cmd(GetCommandToLaunch(app_guid, cmd_id));
-  CORE_LOG(L3, (_T("[GoogleUpdateCore::LaunchCmdElevated][cmd %s]"), cmd));
-  if (cmd.IsEmpty()) {
-    return GOOPDATE_E_CORE_MISSING_CMD;
-  }
-  return LaunchCmd(&cmd, get(caller_proc_handle), proc_handle);
-}
+  // Allocate a session ID for the ping that this call will generate.  (I'd
+  // really like to be able to pipe an external session ID through this API,
+  // but this is old and I don't feel comfortable changing the signature on it.)
+  CString session_id;
+  GetGuid(&session_id);
 
-HRESULT GoogleUpdateCore::OpenCallerProcessHandle(DWORD proc_id,
-                                                  HANDLE* proc_handle) {
-  ASSERT1(proc_handle);
-  *proc_handle = NULL;
-
-  HRESULT hr = ::CoImpersonateClient();
+  scoped_ptr<AppCommand> app_command;
+  // true == machine level
+  hr = AppCommand::Load(app_guid,
+                        true,
+                        cmd_id,
+                        session_id,
+                        address(app_command));
   if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[failed to load command configuration][0x%x]"), hr));
     return hr;
   }
-  ON_SCOPE_EXIT(::CoRevertToSelf);
-
-  *proc_handle = ::OpenProcess(PROCESS_DUP_HANDLE, false, proc_id);
-  return *proc_handle ? S_OK : HRESULTFromLastError();
-}
-
-CString GoogleUpdateCore::GetCommandToLaunch(const TCHAR* app_guid,
-                                             const TCHAR* cmd_id) {
-  CString cmd_line;
-  if (!app_guid || !cmd_id) {
-    return cmd_line;
-  }
-
-  ConfigManager* config_manager = ConfigManager::Instance();
-  CString clients_key_name = config_manager->machine_registry_clients();
-  CString app_key_name = AppendRegKeyPath(clients_key_name, app_guid);
-
-  RegKey::GetValue(app_key_name, cmd_id, &cmd_line);
-  return cmd_line;
-}
-
-HRESULT GoogleUpdateCore::LaunchCmd(CString* cmd,
-                                    HANDLE caller_proc_handle,
-                                    ULONG_PTR* proc_handle) {
-  if (!cmd || !caller_proc_handle || !proc_handle) {
-    return E_INVALIDARG;
-  }
-
-  *proc_handle = NULL;
-  HRESULT hr = S_OK;
 
   // This is a pseudo handle that must not be closed.
   HANDLE this_process_handle = ::GetCurrentProcess();
 
-  PROCESS_INFORMATION pi = {0};
-  hr = System::StartProcess(NULL, cmd->GetBuffer(), &pi);
+  scoped_process command_process;
+  scoped_process duplicate_proc_handle;
+
+  hr = app_command->Execute(address(command_process));
   if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[failed to launch cmd][%s][0x%08x]"), *cmd, hr));
+    CORE_LOG(LE, (_T("[failed to launch app command][0x%x]"), hr));
     return hr;
   }
 
-  // DuplicateHandle call will close the source handle regardless of any error
-  // status returned.
-  ASSERT1(pi.hProcess);
-  VERIFY1(::CloseHandle(pi.hThread));
-
-  scoped_process duplicate_proc_handle;
-
   DWORD desired_access = PROCESS_QUERY_INFORMATION | SYNCHRONIZE;
   bool res = ::DuplicateHandle(
       this_process_handle,             // Current process.
-      pi.hProcess,                     // Process handle to duplicate.
-      caller_proc_handle,              // Process receiving the handle.
+      get(command_process),            // Process handle to duplicate.
+      get(caller_proc_handle),         // Process receiving the handle.
       address(duplicate_proc_handle),  // Duplicated handle.
       desired_access,                  // Access requested for the new handle.
       false,                           // Don't inherit the new handle.
-      DUPLICATE_CLOSE_SOURCE) != 0;    // Closes the source handle.
+      0) != 0;                         // Flags.
+
   if (!res) {
     hr = HRESULTFromLastError();
     CORE_LOG(LE, (_T("[failed to duplicate the handle][0x%08x]"), hr));
@@ -148,6 +116,20 @@
   return S_OK;
 }
 
+HRESULT GoogleUpdateCoreBase::OpenCallerProcessHandle(DWORD proc_id,
+                                                      HANDLE* proc_handle) {
+  ASSERT1(proc_handle);
+  *proc_handle = NULL;
+
+  HRESULT hr = ::CoImpersonateClient();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  ON_SCOPE_EXIT(::CoRevertToSelf);
+
+  *proc_handle = ::OpenProcess(PROCESS_DUP_HANDLE, false, proc_id);
+  return *proc_handle ? S_OK : HRESULTFromLastError();
+}
 
 }  // namespace omaha
 
diff --git a/core/google_update_core.h b/core/google_update_core.h
index d6f19ed..d05b3a4 100644
--- a/core/google_update_core.h
+++ b/core/google_update_core.h
@@ -1,4 +1,4 @@
-// Copyright 2008-2009 Google Inc.
+// Copyright 2008-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -20,45 +20,34 @@
 #include <atlbase.h>
 #include <atlcom.h>
 #include <atlstr.h>
-#include "omaha/common/atlregmapex.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/goopdate/google_update_proxy.h"
-#include "omaha/goopdate/resource.h"
+#include "base/atlregmapex.h"
+#include "base/preprocessor_fun.h"
+#include "base/synchronized.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/goopdate/com_proxy.h"
+#include "omaha/goopdate/elevation_moniker_resource.h"
+#include "omaha/goopdate/non_localized_resource.h"
 
 // Generated by MIDL in the "BUILD_MODE.OBJ_ROOT + SETTINGS.SUBDIR".
-#include "goopdate/google_update_idl.h"
+#include "goopdate/omaha3_idl.h"
 
 namespace omaha {
 
-// TODO(omaha): might need to synchronize on the GoogleUpdateCoreProxy.
-typedef SharedMemoryProxy<IGoogleUpdateCore, FakeGLock> GoogleUpdateCoreProxy;
+#pragma warning(push)
+// Construction of local static object is not thread-safe
+#pragma warning(disable:4640)
 
-class ATL_NO_VTABLE GoogleUpdateCore
+class ATL_NO_VTABLE GoogleUpdateCoreBase
     : public CComObjectRootEx<CComMultiThreadModel>,
-      public CComCoClass<GoogleUpdateCore>,
-      public IGoogleUpdateCore {
+      public IGoogleUpdateCore,
+      public StdMarshalInfo {
  public:
-  GoogleUpdateCore();
-  virtual ~GoogleUpdateCore();
+  GoogleUpdateCoreBase();
+  virtual ~GoogleUpdateCoreBase();
 
-  DECLARE_NOT_AGGREGATABLE(GoogleUpdateCore)
-  DECLARE_PROTECT_FINAL_CONSTRUCT()
-  DECLARE_REGISTRY_RESOURCEID_EX(IDR_GOOGLE_UPDATE_CORE_CLASS)
-
-  // C4640: construction of local static object is not thread-safe
-  #pragma warning(disable : 4640)
-  BEGIN_REGISTRY_MAP()
-    REGMAP_ENTRY(_T("PROGID"),      _T("GoogleUpdate.CoreClass"))
-    REGMAP_ENTRY(_T("VERSION"),     _T("1"))
-    REGMAP_ENTRY(_T("NAME"),        _T("GoogleUpdateCoreClass"))
-    REGMAP_ENTRY(_T("DESCRIPTION"), _T("Google Update Core Class"))
-    REGMAP_UUID(_T("CLSID"),        __uuidof(GoogleUpdateCoreClass))
-  END_REGISTRY_MAP()
-
-  // C4505: unreferenced IUnknown local functions have been removed
-  #pragma warning(disable : 4505)
-  BEGIN_COM_MAP(GoogleUpdateCore)
+  BEGIN_COM_MAP(GoogleUpdateCoreBase)
     COM_INTERFACE_ENTRY(IGoogleUpdateCore)
+    COM_INTERFACE_ENTRY(IStdMarshalInfo)
   END_COM_MAP()
 
   // Launches a command line elevated.
@@ -71,17 +60,48 @@
   // the process handle.
   static HRESULT OpenCallerProcessHandle(DWORD proc_id, HANDLE* proc_handle);
 
-  static CString GetCommandToLaunch(const TCHAR* app_guid, const TCHAR* cmd_id);
-
-  static HRESULT LaunchCmd(CString* cmd,
-                           HANDLE caller_proc_handle,
-                           ULONG_PTR* proc_handle);
-
   friend class GoogleUpdateCoreTest;
+
+  DISALLOW_EVIL_CONSTRUCTORS(GoogleUpdateCoreBase);
+};
+
+template <bool is_service>
+class ATL_NO_VTABLE GoogleUpdateCore
+    : public GoogleUpdateCoreBase,
+      public CComCoClass<GoogleUpdateCore<is_service> > {
+ public:
+  GoogleUpdateCore() {}
+  virtual ~GoogleUpdateCore() {}
+
+  DECLARE_NOT_AGGREGATABLE(GoogleUpdateCore)
+  DECLARE_REGISTRY_RESOURCEID_EX(is_service ? IDR_LOCAL_SERVICE_RGS :
+                                              IDR_LOCAL_SERVER_ELEVATION_RGS)
+
+  BEGIN_REGISTRY_MAP()
+    REGMAP_ENTRY(_T("PROGID"), is_service ? kProgIDGoogleUpdateCoreService :
+                                            kProgIDGoogleUpdateCoreMachine)
+    REGMAP_ENTRY(_T("VERSION"), _T("1"))
+    REGMAP_ENTRY(_T("NAME"), _T("GoogleUpdateCoreClass"))
+    REGMAP_ENTRY(_T("DESCRIPTION"), _T("Google Update Core Class"))
+    REGMAP_UUID(_T("CLSID"), is_service ?
+                             __uuidof(GoogleUpdateCoreClass) :
+                             __uuidof(GoogleUpdateCoreMachineClass))
+    REGMAP_ENTRY(L"ICONRESID", PP_STRINGIZE(IDI_ELEVATION_MONIKER_ICON))
+    REGMAP_ENTRY(L"STRINGRESID",
+                 PP_STRINGIZE(IDS_ELEVATION_MONIKER_DISPLAYNAME))
+    REGMAP_MODULE2(L"MODULE", kOmahaOnDemandFileName)
+  END_REGISTRY_MAP()
+
+ private:
+
   DISALLOW_EVIL_CONSTRUCTORS(GoogleUpdateCore);
 };
 
+typedef GoogleUpdateCore<false> GoogleUpdateCoreMachine;
+typedef GoogleUpdateCore<true> GoogleUpdateCoreService;
+
+#pragma warning(pop)
+
 }  // namespace omaha
 
 #endif  // OMAHA_CORE_GOOGLE_UPDATE_CORE_H_
-
diff --git a/core/google_update_core_unittest.cc b/core/google_update_core_unittest.cc
index a93a86c..ef4515d 100644
--- a/core/google_update_core_unittest.cc
+++ b/core/google_update_core_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2008-2009 Google Inc.
+// Copyright 2008-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -13,232 +13,80 @@
 // limitations under the License.
 // ========================================================================
 
-#include "omaha/common/app_util.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/error.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/system.h"
+#include "omaha/base/timer.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/command_line_builder.h"
 #include "omaha/common/const_cmd_line.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/error.h"
-#include "omaha/common/path.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/common/system.h"
-#include "omaha/common/vistautil.h"
-
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
 #include "omaha/core/google_update_core.h"
-#include "omaha/goopdate/command_line_builder.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/google_update_proxy.h"
 #include "omaha/setup/setup_service.h"
 #include "omaha/testing/unit_test.h"
 
 namespace omaha {
 
+namespace {
+
+const TCHAR update_key[]      = MACHINE_REG_UPDATE;
+const TCHAR appid_key[]       = MACHINE_REG_CLIENTS_GOOPDATE;
+const TCHAR appid_state_key[] = MACHINE_REG_CLIENT_STATE_GOOPDATE;
+
+}  // namespace
+
 class GoogleUpdateCoreTest : public testing::Test {
  protected:
   GoogleUpdateCoreTest() {
-    GUID proxy_clsid = PROXY_CLSID_IS;
-    proxy_guid_ = CW2A(GuidToString(proxy_clsid));
-
-    regedit_uninstall_string_.Format(
-        "Windows Registry Editor Version 5.00\r\n"
-        "\r\n"
-        "[-HKEY_LOCAL_MACHINE\\Software\\Google\\Update\\Clients\\{430FD4D0-B729-4F61-AA34-91526481799D}]\r\n"  // NOLINT
-        "\r\n"
-        "[-HKEY_LOCAL_MACHINE\\Software\\Google\\Update\\ClientState\\{430FD4D0-B729-4F61-AA34-91526481799D}]\r\n"  // NOLINT
-        "\r\n"
-        "[-HKEY_LOCAL_MACHINE\\Software\\Google\\Update\\network\\{430FD4D0-B729-4F61-AA34-91526481799D}]\r\n"  // NOLINT
-        "\r\n"
-        "[-HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\Interface\\{909489C2-85A6-4322-AA56-D25278649D67}]\r\n"  // NOLINT
-        "\r\n"
-        "[-HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\CLSID\\%s]\r\n"
-        "\r\n"
-
-        // Registration for the user must be cleared too since COM looks up HKCR
-        // and that resolves to either HKCU or HKLM classes.
-        "[-HKEY_CURRENT_USER\\SOFTWARE\\Classes\\Interface\\{909489C2-85A6-4322-AA56-D25278649D67}]\r\n"  // NOLINT
-        "\r\n"
-        "[-HKEY_CURRENT_USER\\SOFTWARE\\Classes\\CLSID\\%s]\r\n"
-        "\r\n",
-        proxy_guid_, proxy_guid_);
   }
 
-  virtual void SetUp() {
-    if (!vista_util::IsUserAdmin()) {
-      return;
+  static void SetUpTestCase() {
+    if (vista_util::IsUserAdmin()) {
+      System::AdjustPrivilege(SE_DEBUG_NAME, true);
+      TerminateAllGoogleUpdateProcesses();
     }
-    System::AdjustPrivilege(SE_DEBUG_NAME, true);
-    TerminateAllGoogleUpdateProcesses();
-    SetupRegistry();
+
+    const CString shell_path = goopdate_utils::BuildGoogleUpdateExePath(true);
+    EXPECT_SUCCEEDED(RegKey::SetValue(update_key,
+                                      kRegValueInstalledPath,
+                                      shell_path));
+
+    EXPECT_SUCCEEDED(RegKey::SetValue(update_key,
+                                      kRegValueInstalledVersion,
+                                      GetVersionString()));
+
+    EXPECT_SUCCEEDED(RegKey::SetValue(appid_key,
+                                      kRegValueProductVersion,
+                                      GetVersionString()));
+
+    EXPECT_SUCCEEDED(RegKey::SetValue(appid_key, _T("fc"), _T("fc /?")));
+
+    EXPECT_SUCCEEDED(RegKey::SetValue(appid_state_key,
+                                      kRegValueProductVersion,
+                                      GetVersionString()));
+
+    CopyGoopdateFiles(GetGoogleUpdateMachinePath(), GetVersionString());
   }
 
-  virtual void TearDown() {
-    if (!vista_util::IsUserAdmin()) {
-      return;
+  static void TearDownTestCase() {
+    if (vista_util::IsUserAdmin()) {
+      TerminateAllGoogleUpdateProcesses();
     }
-    TerminateAllGoogleUpdateProcesses();
-    TeardownRegistry();
+
+    EXPECT_SUCCEEDED(RegKey::DeleteValue(appid_key, _T("fc")));
   }
 
-  CString GetCommandToLaunch(const TCHAR* app_guid, const TCHAR* cmd_id) {
-    return GoogleUpdateCore::GetCommandToLaunch(app_guid, cmd_id);
-  }
-
-  HRESULT LaunchCmd(CString* cmd,
-                    HANDLE caller_proc_handle,
-                    ULONG_PTR* proc_handle) {
-    return GoogleUpdateCore::LaunchCmd(cmd, caller_proc_handle, proc_handle);
-  }
-
-  // Starts the core and waits for it to register its shared memory section.
-  HANDLE StartCore();
-
-  // Creates the COM registration required by the test.
-  void SetupRegistry();
-
-  // Cleans up the COM registration.
-  void TeardownRegistry();
-
   void DoLaunchCmdElevatedTests(IUnknown* core_object);
-
-  // The guid of the proxy code. This is a build constant.
-  CStringA proxy_guid_;
-
-  CStringA regedit_uninstall_string_;
 };
 
-void WriteRegeditDataToRegistry(const CStringA& regedit_data) {
-  EXPECT_TRUE(regedit_data.GetLength());
-
-  CString temp_file;
-  EXPECT_TRUE(::GetTempFileName(app_util::GetModuleDirectory(NULL),
-                                _T("reg"),
-                                0,
-                                CStrBuf(temp_file, MAX_PATH)));
-
-  CString reg_file_path = temp_file + _T(".reg");
-  scoped_hfile file_handle(::CreateFile(reg_file_path,
-                                        GENERIC_WRITE,
-                                        FILE_SHARE_READ,
-                                        NULL,
-                                        CREATE_ALWAYS,
-                                        FILE_ATTRIBUTE_NORMAL,
-                                        NULL));
-  EXPECT_TRUE(file_handle);
-  DWORD bytes_written = 0;
-  EXPECT_TRUE(::WriteFile(get(file_handle),
-                          regedit_data,
-                          regedit_data.GetLength(),
-                          &bytes_written,
-                          NULL));
-  EXPECT_TRUE(bytes_written);
-  reset(file_handle);
-
-  CString regedit_path(_T("regedit.exe"));
-  CString cmd_line;
-  cmd_line.Format(_T("/s \"%s\""), reg_file_path);
-
-  if (vista_util::IsUserAdmin()) {
-    EXPECT_SUCCEEDED(RegisterOrUnregisterExe(regedit_path, cmd_line));
-  } else {
-    // Elevate RegEdit.exe for medium integrity users on Vista and above.
-    DWORD exit_code(0);
-    EXPECT_SUCCEEDED(vista_util::RunElevated(regedit_path,
-                                             cmd_line,
-                                             SW_SHOWNORMAL,
-                                             &exit_code));
-    EXPECT_EQ(0, exit_code);
-  }
-
-  EXPECT_TRUE(::DeleteFile(temp_file));
-  EXPECT_TRUE(::DeleteFile(reg_file_path));
-}
-
-void GoogleUpdateCoreTest::SetupRegistry() {
-  CStringA regedit_data(regedit_uninstall_string_);
-
-  CString unittest_dir = app_util::GetModuleDirectory(NULL);
-  CStringA goopdate_dll(CW2A(ConcatenatePath(unittest_dir, kGoopdateDllName)));
-  goopdate_dll.Replace("\\", "\\\\");
-
-  regedit_data.AppendFormat(
-      "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\Interface\\{909489C2-85A6-4322-AA56-D25278649D67}]\r\n"  // NOLINT
-      "@=\"IGoogleUpdateCore\"\r\n"
-      "\r\n"
-      "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\Interface\\{909489C2-85A6-4322-AA56-D25278649D67}\\NumMethods]\r\n"  // NOLINT
-      "@=\"%d\"\r\n"
-      "\r\n"
-      "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\Interface\\{909489C2-85A6-4322-AA56-D25278649D67}\\ProxyStubClsid32]\r\n"  // NOLINT
-      "@=\"%s\"\r\n"
-      "\r\n"
-      "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\CLSID\\%s]\r\n"
-      "@=\"PSFactoryBuffer\"\r\n"
-      "\r\n"
-      "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\CLSID\\%s\\InProcServer32]\r\n"
-      "@=\"%s\"\r\n"
-      "\"ThreadingModel\"=\"Both\"\r\n"
-      "\r\n",
-      4, proxy_guid_, proxy_guid_, proxy_guid_, goopdate_dll);
-
-      // Create one mock client so that core does not try to start an
-      // uninstall worker when the unit test is manipulating the clients key.
-      // And create entries to satisfy Setup::CheckInstallStateConsistency().
-  regedit_data.Append(
-      "[HKEY_LOCAL_MACHINE\\Software\\Google\\Update\\Clients\\{430FD4D0-B729-4F61-AA34-91526481799D}]\r\n"  // NOLINT
-      "\"fc\"=\"fc /?\"\r\n"
-      "\r\n"
-      "[HKEY_LOCAL_MACHINE\\Software\\Google\\Update\\network\\{430FD4D0-B729-4F61-AA34-91526481799D}]\r\n"  // NOLINT
-      "\r\n"
-      "[HKEY_LOCAL_MACHINE\\Software\\Google\\Update]\r\n"
-      "\"path\"=\"blah\"\r\n"
-      "\r\n"
-      "[HKEY_LOCAL_MACHINE\\Software\\Google\\Update]\r\n"
-      "\"version\"=\"0.0.0.1\"\r\n"
-      "\r\n"
-      "[HKEY_LOCAL_MACHINE\\Software\\Google\\Update\\ClientState\\{430FD4D0-B729-4F61-AA34-91526481799D}]\r\n"  // NOLINT
-      "\"pv\"=\"0.0.0.1\"\r\n"
-      "\r\n");
-
-  WriteRegeditDataToRegistry(regedit_data);
-}
-
-void GoogleUpdateCoreTest::TeardownRegistry() {
-  WriteRegeditDataToRegistry(regedit_uninstall_string_);
-}
-
-HANDLE GoogleUpdateCoreTest::StartCore() {
-  CString unittest_dir = app_util::GetModuleDirectory(NULL);
-  CString google_update = ConcatenatePath(unittest_dir, kGoopdateFileName);
-  EnclosePath(&google_update);
-  HANDLE handle = NULL;
-  LaunchProcessAsSystem(google_update + _T(" /c"), &handle);
-  EXPECT_TRUE(handle != NULL);
-  if (!handle) {
-    return NULL;
-  }
-
-  // Give the core some time to start and register its shared memory section.
-  // Sometimes psexec is slow to start the machine core, so give it some
-  // time to run.
-  size_t count(0), kMaxCount(10);
-  while (count++ < kMaxCount) {
-    const TCHAR* shmem_name = kGoogleUpdateCoreSharedMemoryName;
-    scoped_file_mapping shmem_handle(::OpenFileMapping(FILE_MAP_READ,
-                                                       false,
-                                                       shmem_name));
-    if (shmem_handle) {
-      // Sleep for a short duration to allow the Core process to register the
-      // COM object in the shared memory.
-      ::Sleep(100);
-      break;
-    }
-
-    ::Sleep(1000);
-  }
-
-  return handle;
-}
-
 void GoogleUpdateCoreTest::DoLaunchCmdElevatedTests(IUnknown* core_object) {
   CComQIPtr<IGoogleUpdateCore> google_update_core = core_object;
   EXPECT_TRUE(google_update_core != NULL);
@@ -296,120 +144,58 @@
   EXPECT_TRUE(::CloseHandle(handle));
 }
 
-TEST_F(GoogleUpdateCoreTest, LaunchCmdElevated_CoreNotRunning) {
+TEST_F(GoogleUpdateCoreTest, LaunchCmdElevated_LocalServerRegistered) {
+  RegisterOrUnregisterGoopdateLocalServer(true);
+
+  CComPtr<IUnknown> local_server_com;
+  EXPECT_SUCCEEDED(System::CoCreateInstanceAsAdmin(
+      NULL, __uuidof(GoogleUpdateCoreMachineClass),
+      IID_PPV_ARGS(&local_server_com)));
+
+  DoLaunchCmdElevatedTests(local_server_com);
+
+  local_server_com.Release();
+
+  RegisterOrUnregisterGoopdateLocalServer(false);
+}
+
+TEST_F(GoogleUpdateCoreTest,
+       LaunchCmdElevated_ServiceAndLocalServerRegistered) {
+  RegisterOrUnregisterGoopdateService(true);
+  RegisterOrUnregisterGoopdateLocalServer(true);
+
+  CComPtr<IUnknown> service_com;
+  EXPECT_SUCCEEDED(
+      service_com.CoCreateInstance(__uuidof(GoogleUpdateCoreClass)));
+
+  DoLaunchCmdElevatedTests(service_com);
+
+  CComPtr<IUnknown> local_server_com;
+  EXPECT_SUCCEEDED(System::CoCreateInstanceAsAdmin(
+      NULL, __uuidof(GoogleUpdateCoreMachineClass),
+      IID_PPV_ARGS(&local_server_com)));
+
+  DoLaunchCmdElevatedTests(local_server_com);
+
+  service_com.Release();
+  local_server_com.Release();
+
+  RegisterOrUnregisterGoopdateLocalServer(false);
+  RegisterOrUnregisterGoopdateService(false);
+}
+
+TEST_F(GoogleUpdateCoreTest, LaunchCmdElevated_ServiceRunning) {
   if (!vista_util::IsUserAdmin()) {
     SUCCEED() << "\tTest did not run because the user is not an admin.";
     return;
   }
 
-  SharedMemoryAttributes attr(kGoogleUpdateCoreSharedMemoryName,
-                              CSecurityDesc());
-  GoogleUpdateCoreProxy google_update_core_proxy(true, &attr);
-  CComPtr<IGoogleUpdateCore> google_update_core;
-  EXPECT_FAILED(google_update_core_proxy.GetObject(&google_update_core));
-}
+  RegisterOrUnregisterGoopdateService(true);
 
-TEST_F(GoogleUpdateCoreTest, LaunchCmdElevated) {
-  if (!vista_util::IsUserAdmin()) {
-    SUCCEED() << "\tTest did not run because the user is not an admin.";
-    return;
-  }
+  // RegisterOrUnregisterGoopdateLocalServer is needed for handler registration.
+  RegisterOrUnregisterGoopdateLocalServer(true);
 
-  // Start the machine instance of the core.
-  StartCore();
-
-  // Get the proxy for the core interface.
-  SharedMemoryAttributes attr(kGoogleUpdateCoreSharedMemoryName,
-                              CSecurityDesc());
-  GoogleUpdateCoreProxy google_update_core_proxy(true, &attr);
-  CComPtr<IGoogleUpdateCore> google_update_core;
-
-  EXPECT_SUCCEEDED(google_update_core_proxy.GetObject(&google_update_core));
-  EXPECT_TRUE(google_update_core != NULL);
-
-  DoLaunchCmdElevatedTests(google_update_core);
-}
-
-TEST_F(GoogleUpdateCoreTest, GetCommandToLaunch) {
-  if (!vista_util::IsUserAdmin()) {
-    SUCCEED() << "\tTest did not run because the user is not an admin.";
-    return;
-  }
-  EXPECT_STREQ(_T(""), GetCommandToLaunch(NULL, _T("foo")));
-  EXPECT_STREQ(_T(""), GetCommandToLaunch(_T("bar"), NULL));
-
-  CString cmd = GetCommandToLaunch(_T("{430FD4D0-B729-4F61-AA34-91526481799D}"),
-                                   _T("cmd"));
-  EXPECT_STREQ(_T(""), cmd);
-
-  const TCHAR* key_name = _T("HKLM\\Software\\Google\\Update\\Clients\\")
-                          _T("{430FD4D0-B729-4F61-AA34-91526481799D}");
-  EXPECT_SUCCEEDED(RegKey::SetValue(key_name, _T("cmd"), _T("foobar")));
-
-  cmd = GetCommandToLaunch(_T("{430FD4D0-B729-4F61-AA34-91526481799D}"),
-                           _T("cmd"));
-  EXPECT_STREQ(_T("foobar"), cmd);
-}
-
-TEST_F(GoogleUpdateCoreTest, LaunchCmd) {
-  if (!vista_util::IsUserAdmin()) {
-    SUCCEED() << "\tTest did not run because the user is not an admin.";
-    return;
-  }
-  ULONG_PTR proc_handle = 0;
-  scoped_process caller_proc_handle(::OpenProcess(PROCESS_DUP_HANDLE,
-                                                  false,
-                                                  ::GetCurrentProcessId()));
-  EXPECT_TRUE(caller_proc_handle);
-
-  CString cmd = _T("cmd /c \"dir > nul\"");
-  EXPECT_SUCCEEDED(LaunchCmd(&cmd, get(caller_proc_handle), &proc_handle));
-
-  EXPECT_NE(0, proc_handle);
-
-  HANDLE handle = reinterpret_cast<HANDLE>(proc_handle);
-  EXPECT_NE(WAIT_FAILED, ::WaitForSingleObject(handle, 10000));
-  EXPECT_TRUE(::CloseHandle(handle));
-}
-
-class GoogleUpdateCoreServiceTest : public GoogleUpdateCoreTest {
- protected:
-  virtual void SetUp() {
-    SetupRegistry();
-    RegisterOrUnregisterService(true);
-  }
-
-  virtual void TearDown() {
-    RegisterOrUnregisterService(false);
-    TeardownRegistry();
-  }
-
-  void RegisterOrUnregisterService(bool reg);
-};
-
-void GoogleUpdateCoreServiceTest::RegisterOrUnregisterService(bool reg) {
-  CString unittest_dir = app_util::GetModuleDirectory(NULL);
-  CString service_path = ConcatenatePath(unittest_dir, kServiceFileName);
-  EnclosePath(&service_path);
-
-  CommandLineBuilder builder(reg ? COMMANDLINE_MODE_SERVICE_REGISTER :
-                                   COMMANDLINE_MODE_SERVICE_UNREGISTER);
-  CString cmd_line = builder.GetCommandLineArgs();
-  if (vista_util::IsUserAdmin()) {
-    EXPECT_SUCCEEDED(RegisterOrUnregisterExe(service_path, cmd_line));
-    return;
-  }
-
-  // Elevate for medium integrity users on Vista and above.
-  DWORD exit_code(S_OK);
-  EXPECT_SUCCEEDED(vista_util::RunElevated(service_path,
-                                           cmd_line,
-                                           SW_SHOWNORMAL,
-                                           &exit_code));
-  EXPECT_SUCCEEDED(exit_code);
-}
-
-TEST_F(GoogleUpdateCoreServiceTest, LaunchCmdElevated_ServiceNotRunning) {
+  EXPECT_SUCCEEDED(SetupUpdateMediumService::StartService());
   CComPtr<IUnknown> service_com;
   EXPECT_SUCCEEDED(
       service_com.CoCreateInstance(__uuidof(GoogleUpdateCoreClass)));
@@ -417,21 +203,9 @@
   DoLaunchCmdElevatedTests(service_com);
 
   service_com.Release();
-}
 
-TEST_F(GoogleUpdateCoreServiceTest, LaunchCmdElevated_ServiceRunning) {
-  if (!vista_util::IsUserAdmin()) {
-    SUCCEED() << "\tTest did not run because the user is not an admin.";
-    return;
-  }
-  EXPECT_SUCCEEDED(SetupService::StartService());
-  CComPtr<IUnknown> service_com;
-  EXPECT_SUCCEEDED(
-      service_com.CoCreateInstance(__uuidof(GoogleUpdateCoreClass)));
-
-  DoLaunchCmdElevatedTests(service_com);
-
-  service_com.Release();
+  RegisterOrUnregisterGoopdateLocalServer(false);
+  RegisterOrUnregisterGoopdateService(false);
 }
 
 }  // namespace omaha
diff --git a/core/legacy_manifest_handler.cc b/core/legacy_manifest_handler.cc
deleted file mode 100644
index fd8371f..0000000
--- a/core/legacy_manifest_handler.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/core/legacy_manifest_handler.h"
-
-#include <atlsecurity.h>
-#include <atlstr.h>
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/reactor.h"
-#include "omaha/core/core.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-
-namespace omaha {
-
-LegacyManifestHandler::LegacyManifestHandler()
-    : core_(NULL),
-      reactor_(NULL) {
-  CORE_LOG(L3, (_T("[LegacyManifestHandler::LegacyManifestHandler]")));
-}
-
-LegacyManifestHandler::~LegacyManifestHandler() {
-  CORE_LOG(L3, (_T("[LegacyManifestHandler::~LegacyManifestHandler]")));
-  ASSERT1(reactor_);
-  ASSERT1(dir_watcher_.get());
-  VERIFY1(SUCCEEDED(reactor_->UnregisterHandle(dir_watcher_->change_event())));
-}
-
-HRESULT LegacyManifestHandler::Initialize(Core* core) {
-  CORE_LOG(L3, (_T("[LegacyManifestHandler::Initialize]")));
-  ASSERT1(core);
-  ASSERT1(!core->is_system());
-  core_ = core;
-
-  reactor_ = core_->reactor();
-  ASSERT1(reactor_);
-
-  CString dir = ConfigManager::Instance()->GetUserInitialManifestStorageDir();
-  dir_watcher_.reset(new FileWatcher(dir,
-                                     false,
-                                     FILE_NOTIFY_CHANGE_FILE_NAME));
-  ASSERT1(dir_watcher_.get());
-  HRESULT hr = dir_watcher_->EnsureEventSetup();
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = reactor_->RegisterHandle(dir_watcher_->change_event(), this, 0);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return S_OK;
-}
-
-void LegacyManifestHandler::HandleEvent(HANDLE handle) {
-  UNREFERENCED_PARAMETER(handle);
-  ASSERT1(handle == dir_watcher_->change_event());
-  CORE_LOG(L1, (_T("[Got an omaha1 manifest handoff event.]")));
-
-  // Set up the watcher again.
-  ASSERT1(dir_watcher_.get());
-  HRESULT hr = dir_watcher_->EnsureEventSetup();
-  if (FAILED(hr)) {
-    // Ignore the error and handle the current event.
-    // It is possible to not be able to register the file watcher in case
-    // omaha is being uninstalled.
-    CORE_LOG(L1, (_T("Could not recreate the file watcher")));
-  }
-  VERIFY1(SUCCEEDED(reactor_->RegisterHandle(dir_watcher_->change_event())));
-
-  ASSERT1(core_);
-  core_->StartInstallWorker();
-}
-
-}  // namespace omaha
-
diff --git a/core/legacy_manifest_handler.h b/core/legacy_manifest_handler.h
deleted file mode 100644
index a387dd3..0000000
--- a/core/legacy_manifest_handler.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// LegacyManifestHandler monitors the user manifest directory for manifest
-// files that are copied there by omaha1.
-
-#ifndef OMAHA_CORE_LEGACY_MANIFEST_HANDLER_H__
-#define OMAHA_CORE_LEGACY_MANIFEST_HANDLER_H__
-
-#include <windows.h>
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "omaha/common/event_handler.h"
-#include "omaha/common/file.h"
-
-namespace omaha {
-
-class Reactor;
-class Core;
-
-class LegacyManifestHandler : public EventHandler {
- public:
-  LegacyManifestHandler();
-  ~LegacyManifestHandler();
-
-  HRESULT Initialize(Core* core);
-
-  virtual void HandleEvent(HANDLE handle);
-
- private:
-  scoped_ptr<FileWatcher> dir_watcher_;
-  Reactor* reactor_;
-  Core* core_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(LegacyManifestHandler);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_CORE_LEGACY_MANIFEST_HANDLER_H__
-
diff --git a/core/scheduler.cc b/core/scheduler.cc
index 8394072..d2363e7 100644
--- a/core/scheduler.cc
+++ b/core/scheduler.cc
@@ -15,14 +15,14 @@
 
 #include "omaha/core/scheduler.h"
 #include <algorithm>
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/highres_timer-win32.h"
-#include "omaha/common/queue_timer.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/highres_timer-win32.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/queue_timer.h"
+#include "omaha/common/config_manager.h"
 #include "omaha/core/core.h"
 #include "omaha/core/core_metrics.h"
-#include "omaha/goopdate/config_manager.h"
 
 namespace omaha {
 
diff --git a/core/system_monitor.cc b/core/system_monitor.cc
index 1520268..7629db8 100644
--- a/core/system_monitor.cc
+++ b/core/system_monitor.cc
@@ -14,13 +14,13 @@
 // ========================================================================
 
 #include "omaha/core/system_monitor.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/common/app_registry_utils.h"
+#include "omaha/common/const_goopdate.h"
 
 namespace omaha {
 
@@ -135,7 +135,7 @@
 
   const bool is_machine = system_monitor->is_machine_;
   size_t num_clients(0);
-  if (SUCCEEDED(goopdate_utils::GetNumClients(is_machine, &num_clients)) &&
+  if (SUCCEEDED(app_registry_utils::GetNumClients(is_machine, &num_clients)) &&
       num_clients <= 1) {
     system_monitor->observer_->NoRegisteredClients();
   }
diff --git a/core/system_monitor.h b/core/system_monitor.h
index d27a762..68bd16d 100644
--- a/core/system_monitor.h
+++ b/core/system_monitor.h
@@ -22,7 +22,7 @@
 #include <atlwin.h>
 #include "base/basictypes.h"
 #include "base/scoped_ptr.h"
-#include "omaha/common/registry_monitor_manager.h"
+#include "omaha/base/registry_monitor_manager.h"
 
 namespace omaha {
 
diff --git a/core/system_monitor_unittest.cc b/core/system_monitor_unittest.cc
index c68727a..8bff9b7 100644
--- a/core/system_monitor_unittest.cc
+++ b/core/system_monitor_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2008-2009 Google Inc.
+// Copyright 2008-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -13,12 +13,12 @@
 // limitations under the License.
 // ========================================================================
 
-#include "omaha/common/constants.h"
-#include "omaha/common/path.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/synchronized.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/common/const_goopdate.h"
 #include "omaha/core/system_monitor.h"
-#include "omaha/goopdate/const_goopdate.h"
 #include "omaha/testing/unit_test.h"
 
 namespace omaha {
diff --git a/enterprise/build.scons b/enterprise/build.scons
index 73c77d7..f994429 100644
--- a/enterprise/build.scons
+++ b/enterprise/build.scons
@@ -15,33 +15,15 @@
 # limitations under the License.
 # ========================================================================
 
-import os
-from enterprise import public_apps
-from enterprise import generate_group_policy_template
+# To build an ADM file:
+#  1) Add a file containing the apps definition (e.g. APPS in public_apps.py).
+#  2) Import that file below.
+#  3) Add a call to the following at the end of this file:
+#       build_group_policy_template.BuildGroupPolicyTemplate(
+#           env, '$STAGING_DIR/Omaha.adm', public_apps.APPS, 'public_apps.py')
+
+from enterprise import build_group_policy_template
 
 Import('env')
 
-# Build the Group Policy template (.adm) file.
-
-_adm_contents = generate_group_policy_template.GenerateGroupPolicyTemplate(
-    public_apps.EXTERNAL_APPS)
-
-def _WriteAdmFile(target, source, env):
-  f = open(env.File(target[0]).abspath, 'w')
-  f.write(env['write_data'])
-  f.close()
-  return 0
-
-adm_output = env.Command(
-    target='$STAGING_DIR/GoogleUpdate.adm',
-    source=[],
-    action=_WriteAdmFile,
-    write_data=_adm_contents
-)
-
-# Force the ADM file to rebuild whenever the script or public_apps data change.
-env.Depends(adm_output,
-            ['generate_group_policy_template.py', 'public_apps.py'])
-
-if not env.Bit('official_installers'):
-  env.BuildSConscript('installer')
+env.BuildSConscript('installer')
diff --git a/enterprise/build_group_policy_template.py b/enterprise/build_group_policy_template.py
new file mode 100644
index 0000000..b920d01
--- /dev/null
+++ b/enterprise/build_group_policy_template.py
@@ -0,0 +1,57 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========================================================================
+
+"""A Hammer-specific wrapper for generate_group_policy_template."""
+
+from omaha.enterprise import generate_group_policy_template
+
+
+def BuildGroupPolicyTemplate(env, target, apps, apps_file_path=None):
+  """Builds a Group Policy ADM template file, handling dependencies.
+
+  Causes WriteGroupPolicyTemplate() to be called at build time instead of as
+  part of the processing stage.
+
+  Args:
+    env: The environment.
+    target: ADM output file.
+    apps: A list of tuples containing information about each app. See
+        generate_group_policy_template for details.
+    apps_file_path: Optional path to the file that defines apps. Used to enforce
+        dependencies.
+  """
+
+  def _WriteAdmFile(target, source, env):
+    """Called during the build phase to generate and write the ADM file."""
+    source = source  # Avoid PyLint warning.
+    generate_group_policy_template.WriteGroupPolicyTemplate(
+        env.File(target[0]).abspath,
+        env['public_apps'])
+    return 0
+
+  adm_output = env.Command(
+      target=target,
+      source=[],
+      action=_WriteAdmFile,
+      public_apps=apps
+  )
+
+  # Force ADM file to rebuild whenever the script or apps data change.
+  dependencies = ['$MAIN_DIR/enterprise/generate_group_policy_template.py']
+  if apps_file_path:
+    dependencies.append(apps_file_path)
+  env.Depends(adm_output, dependencies)
diff --git a/enterprise/const_group_policy.h b/enterprise/const_group_policy.h
deleted file mode 100644
index d639673..0000000
--- a/enterprise/const_group_policy.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_ENTERPRISE_CONST_GROUP_POLICY_H__
-#define OMAHA_ENTERPRISE_CONST_GROUP_POLICY_H__
-
-#include <tchar.h>
-#include "omaha/common/constants.h"
-
-namespace omaha {
-
-// Key containing Google Update Group Policy settings. All policies are in HKLM.
-const TCHAR* const kRegKeyGoopdateGroupPolicy =
-    MACHINE_KEY GOOPDATE_POLICIES_RELATIVE;
-
-// Preferences Categroy.
-const TCHAR* const kRegValueAutoUpdateCheckPeriodOverrideMinutes =
-    _T("AutoUpdateCheckPeriodMinutes");
-
-// Applications Categroy.
-// The prefix strings have the app's GUID appended to them.
-const TCHAR* const kRegValueInstallAppsDefault  = _T("InstallDefault");
-const TCHAR* const kRegValueInstallAppPrefix    = _T("Install");
-const TCHAR* const kRegValueUpdateAppsDefault   = _T("UpdateDefault");
-const TCHAR* const kRegValueUpdateAppPrefix     = _T("Update");
-
-const bool kInstallPolicyDefault    = true;
-const bool kUpdatePolicyDefault     = true;
-
-const int kPolicyDisabled           = 0;
-const int kPolicyEnabled            = 1;
-const int kPolicyManualUpdatesOnly  = 2;
-
-}  // namespace omaha
-
-#endif  // OMAHA_ENTERPRISE_CONST_GROUP_POLICY_H__
-
diff --git a/enterprise/generate_group_policy_template.py b/enterprise/generate_group_policy_template.py
index 9cca309..a690609 100644
--- a/enterprise/generate_group_policy_template.py
+++ b/enterprise/generate_group_policy_template.py
@@ -17,19 +17,19 @@
 
 """Generates a Group Policy template file for Google Update policies.
 
-This script only works on Windows because gpedit.msc requires Windows-style
-line endings (\r\n).
+The resulting strings and files use CRLF as required by gpedit.msc.
 
 To unit test this module, just run the file from the command line.
 """
 
+import codecs
 import filecmp
 import os
 import sys
 
 
 HORIZONTAL_RULE = ';%s\n' % ('-' * 78)
-MAIN_POLICY_KEY = "Software\Policies\Google\Update"
+MAIN_POLICY_KEY = 'Software\Policies\Google\Update'
 
 # pylint: disable-msg=C6004
 HEADER = """\
@@ -263,8 +263,13 @@
 
 
 def GenerateGroupPolicyTemplate(apps):
+  # pylint: disable-msg=C6114
   """Generates a Group Policy template (ADM format)for the specified apps.
 
+  Replaces LF in strings above with CRLF as required by gpedit.msc.
+  When writing the resulting contents to a file, use binary mode to ensure the
+  CRLFs are preserved.
+
   Args:
     apps: A list of tuples containing information about each app.
         Each element of the list is a tuple of:
@@ -272,11 +277,11 @@
           * app ID
           * optional string to append to the auto-update explanation
             - Should start with a space or double new line (\n\n).
-    target_path: Output path of the .ADM template file.
 
   Returns:
     String containing the contents of the .ADM file.
   """
+  # pylint: enable-msg=C6114
 
   def _CreateLegalIdentifier(input_string):
     """Converts input_string to a legal identifier for ADM files.
@@ -319,7 +324,11 @@
                         .replace('%', '')
                         .replace('^', '')
                         .replace('*', '')
-                        .replace('+', ''))
+                        .replace('+', '')
+                        .replace(u'\u00a9', '')   # Copyright (C).
+                        .replace(u'\u00ae', '')   # Registered Trademark (R).
+                        .replace(u'\u2122', ''))  # Trademark (TM).
+
     # pylint: enable-msg=C6004
 
   def _WriteTemplateForApp(template, app):
@@ -373,18 +382,39 @@
       _WriteTemplateForAllApps(STRINGS_APP_POLICY_EXPLANATIONS_TEMPLATE, apps),
       ]
 
-  return ''.join(target_contents)
+  # Join the sections of content then replace LF with CRLF.
+  return ''.join(target_contents).replace('\n', '\r\n')
 
 
+def WriteGroupPolicyTemplate(target_path, apps):
+  """Writes a Group Policy template (ADM format)for the specified apps.
+
+  The file is UTF-16 and contains CRLF on all platforms.
+
+  Args:
+    target_path: Output path of the .ADM template file.
+    apps: A list of tuples containing information about each app.
+        Each element of the list is a tuple of:
+          * app name
+          * app ID
+          * optional string to append to the auto-update explanation
+            - Should start with a space or double new line (\n\n).
+  """  # pylint: disable-msg=C6114
+
+  contents = GenerateGroupPolicyTemplate(apps)
+  f = codecs.open(target_path, 'wb', 'utf-16')
+  f.write(contents)
+  f.close()
+
 # Run a unit test when the module is run directly.
 if __name__ == '__main__':
   TEST_APPS = [
-      ('Google Chrome',
-       '{8A69D345-D564-463C-AFF1-A69D9E530F96}',
-       ' Check http://www.google.com/chrome/.'),
-      ('Google Earth',
-       '{74AF07D8-FB8F-4D51-8AC7-927721D56EBB}',
-       ' Check http://earth.google.com/.'),
+      ('Google Test Foo',
+       '{D6B08267-B440-4c85-9F79-E195E80D9937}',
+       ' Check http://www.google.com/test_foo/.'),
+      (u'Google User Test Foo\u00a9\u00ae\u2122',
+       '{104844D6-7DDA-460b-89F0-FBF8AFDD0A67}',
+       ' Check http://www.google.com/user_test_foo/.'),
       ]
   TEST_GOLD_FILENAME = 'test_gold.adm'
   TEST_OUTPUT_FILENAME = 'test_out.adm'
@@ -393,11 +423,7 @@
   gold_path = os.path.join(module_dir, TEST_GOLD_FILENAME)
   output_path = os.path.join(module_dir, TEST_OUTPUT_FILENAME)
 
-  test_target_contents = GenerateGroupPolicyTemplate(TEST_APPS)
-
-  target = open(output_path, 'wt')
-  target.write(test_target_contents)
-  target.close()
+  WriteGroupPolicyTemplate(output_path, TEST_APPS)
 
   if filecmp.cmp(gold_path, output_path, shallow=False):
     print 'PASS: Contents equal.'
diff --git a/enterprise/installer/build.scons b/enterprise/installer/build.scons
index 554794a..08eefb8 100644
--- a/enterprise/installer/build.scons
+++ b/enterprise/installer/build.scons
@@ -34,12 +34,22 @@
 # Build the installer using build rules specific to Google Update builds.
 BUILDING_WITH_GOOGLE_UPDATE = True
 
+# We need to build the custom_actions dll.
+env.BuildSConscript('custom_actions')
+
+if BUILDING_WITH_GOOGLE_UPDATE and not env.Bit('no-tests'):
+  env.BuildSConscript('test')
+
 # APPTEAMS:
 # Note about versions: Each released build needs to have a different Build
 # number in the version "Major.Minor.Build.Qfe". The Qfe is ignored by MSI and
 # only increasing it may cause both the old and new versions of the this MSI
 # to exist side-by-side (http://msdn.microsoft.com/en-us/library/aa370859.aspx).
 
+# Custom action DLL name.
+# Set this to the path to the DLL containing the error display custom action.
+CUSTOM_ACTION_DLL = ('$STAGING_DIR/show_error_action.dll')
+
 #
 # Applications with EXE installers
 #
@@ -61,11 +71,13 @@
      '&brand=mkfi',
      '$MAIN_DIR/testing/unittest_support/SaveArguments.exe',
      '/install 0.1.2.0', '/donotregister', '/silentuninstall 0.1.2.0',
+     CUSTOM_ACTION_DLL,
      'SaveArguments_Enterprise_Installer_0.1.2.0'),
     ('Save Arguments', '0.1.3.0', '{7DD1EF7B-D075-47c0-BD51-F624ED87CCF0}',
      '&brand=mkfi',
      '$MAIN_DIR/testing/unittest_support/SaveArguments.exe',
      '/install 0.1.3.0', '/donotregister', '/silentuninstall 0.1.3.0',
+     CUSTOM_ACTION_DLL,
      'SaveArguments_Enterprise_Installer_0.1.3.0'),
     ]
 
@@ -89,6 +101,7 @@
          product_installer_install_command,
          product_installer_disable_update_registration_arg,
          product_uninstaller_additional_args,
+         show_error_action_dll,
          msi_base_name) = product
 
         product_custom_params = '&brand=' + combo[0] + '&ap=' + combo[1]
@@ -103,6 +116,7 @@
             product_uninstaller_additional_args,
             prefix + msi_base_name + '_' + combo[0],
             '$MAIN_DIR/enterprise/installer',
+            show_error_action_dll,
             '$STAGING_DIR/%sGoogleUpdateSetup.exe' % prefix,
             output_dir = '$TARGET_ROOT/Test_Installers'
         )
@@ -118,7 +132,7 @@
   for omaha_version_info in env['omaha_versions_info']:
     prefix = omaha_version_info.filename_prefix
 
-    msi_base_name = 'TestFoo_Standalone_Enterprise_Installer_1.0.101.0'
+    msi_base_name = 'TestFoo_Installer_1.0.101.0'
     BRAND = 'ENTC'
     AP = 'enterprise-C'
     product_custom_params = '&brand=' + BRAND + '&ap=' + AP
@@ -139,6 +153,7 @@
         product_installer_data,
         ('$TARGET_ROOT/Test_Installers/' +
             '%sUNOFFICIAL_TestFooStandaloneInstaller.exe' % (prefix)),
+        CUSTOM_ACTION_DLL,
         prefix + msi_base_name + '_' + BRAND,
         '$MAIN_DIR/enterprise/installer',
         output_dir = '$TARGET_ROOT/Test_Installers'
diff --git a/enterprise/installer/build_enterprise_installer.py b/enterprise/installer/build_enterprise_installer.py
index 65ca788..d09d47d 100644
--- a/enterprise/installer/build_enterprise_installer.py
+++ b/enterprise/installer/build_enterprise_installer.py
@@ -44,8 +44,8 @@
                               google_update_wxs_template_path):
   """Build an update fragment into a WiX object.
 
-    Takes a supplied wix fragment, and turns it into a .wixobj object for later
-    inclusion into an MSI.
+  Takes a supplied wix fragment, and turns it into a .wixobj object for later
+  inclusion into an MSI.
 
   Args:
     env: environment to build with
@@ -109,16 +109,17 @@
                     msi_base_name,
                     google_update_wixobj_output,
                     enterprise_installer_dir,
+                    show_error_action_dll_path,
                     metainstaller_path,
                     output_dir):
   """Build an MSI installer for use in enterprise situations.
 
-    Builds an MSI for the executable installer at product_installer_path using
-    the supplied details. Requires an existing Google Update installer fragment
-    as well as a path to a custom action DLL containing the logic to launch the
-    product's uninstaller.
+  Builds an MSI for the executable installer at product_installer_path using
+  the supplied details. Requires an existing Google Update installer fragment
+  as well as a path to a custom action DLL containing the logic to launch the
+  product's uninstaller.
 
-    This is intended to enable enterprise installation scenarios.
+  This is intended to enable enterprise installation scenarios.
 
   Args:
     env: environment to build with
@@ -139,6 +140,10 @@
         installer.
     enterprise_installer_dir: path to dir which contains
         enterprise_installer.wxs.xml
+    show_error_action_dll_path: path to the error display custom action dll that
+        exports a ShowInstallerResultUIString method. This CA method will read
+        the LastInstallerResultUIString from the product's ClientState key in
+        the registry and display the string via MsiProcessMessage.
     metainstaller_path: path to the Omaha metainstaller. Should be same file
         used for google_update_wixobj_output. Used only to force rebuilds.
     output_dir: path to the directory that will contain the resulting MSI
@@ -173,6 +178,9 @@
       action='@copy /y $SOURCE $TARGET',
   )
 
+  # Disable warning LGHT1076 and internal check ICE61 on light.exe.  Details:
+  # http://blogs.msdn.com/astebner/archive/2007/02/13/building-an-msi-using-wix-v3-0-that-includes-the-vc-8-0-runtime-merge-modules.aspx
+  # http://windows-installer-xml-wix-toolset.687559.n2.nabble.com/ICE61-Upgrade-VersionMax-format-is-wrong-td4396813.html # pylint: disable-msg=C6310
   wix_env = env.Clone()
   wix_env.Append(
       WIXCANDLEFLAGS=[
@@ -185,19 +193,18 @@
               product_installer_install_command),
           '-dProductInstallerDisableUpdateRegistrationArg=' + (
               product_installer_disable_update_registration_arg),
+          '-dShowErrorCADll=' + env.File(show_error_action_dll_path).abspath,
           '-dProductUninstallerAdditionalArgs=' + (
               product_uninstaller_additional_args),
           '-dMsiProductId=' + msi_product_id,
           '-dMsiUpgradeCode=' + msi_upgradecode_guid,
           ],
+      WIXLIGHTFLAGS=[
+          '-sw1076',
+          '-sice:ICE61',
+          ],
   )
 
-  # Disable warning LGHT1076 which complains about a string-length ICE error
-  # that can safely be ignored as per
-  # http://blogs.msdn.com/astebner/archive/2007/02/13/building-an-msi-
-  # using-wix-v3-0-that-includes-the-vc-8-0-runtime-merge-modules.aspx
-  wix_env['WIXLIGHTFLAGS'].append('-sw1076')
-
   wix_output = wix_env.WiX(
       target='unsigned_' + msi_name,
       source=[copy_target, google_update_wixobj_output],
@@ -208,7 +215,9 @@
   # file is rebuilt because the hash of the .wixobj does not change.
   # Also force a dependency on the CA DLL. Otherwise, it might not be built
   # before the MSI.
-  wix_env.Depends(wix_output, [product_installer_path, metainstaller_path])
+  wix_env.Depends(wix_output, [product_installer_path,
+                               metainstaller_path,
+                               show_error_action_dll_path])
 
   sign_output = wix_env.SignedBinary(
       target=msi_name,
@@ -221,10 +230,10 @@
 def GenerateNameBasedGUID(namespace, name):
   """Generate a GUID based on the names supplied.
 
-    Follows a methodology recommended in Section 4.3 of RFC 4122 to generate
-    a "name-based UUID," which basically means that you want to control the
-    inputs to the GUID so that you can generate the same valid GUID each time
-    given the same inputs.
+  Follows a methodology recommended in Section 4.3 of RFC 4122 to generate
+  a "name-based UUID," which basically means that you want to control the
+  inputs to the GUID so that you can generate the same valid GUID each time
+  given the same inputs.
 
   Args:
     namespace: First part of identifier used to generate GUID
@@ -266,6 +275,49 @@
           ord(md5_hash[13]), ord(md5_hash[14]), ord(md5_hash[15])))
 
 
+def ConvertToMSIVersionNumberIfNeeded(product_version):
+  """Change product_version to fit in an MSI version number if needed.
+
+  Some products use a 4-field version numbering scheme whereas MSI looks only
+  at the first three fields when considering version numbers. Furthermore, MSI
+  version fields have documented width restrictions of 8bits.8bits.16bits as
+  per http://msdn.microsoft.com/en-us/library/aa370859(VS.85).aspx
+
+  As such, the following scheme is used:
+
+  Product a.b.c.d -> a.(c>>8).(((c & 0xFF) << 8) + d)
+
+  So eg. 6.1.420.8 would become 6.1.41992.
+
+  This assumes:
+  1) we don't care about the product minor number, e.g. we will never reset
+     the 'c' number after an increase in 'b'.
+  2) 'd' will always be <= 255
+  3) 'c' is <= 65535
+
+  As a final note, if product_version is not of the format a.b.c.d then
+  this function returns the original product_version value.
+  """
+
+  try:
+    version_field_strings = product_version.split('.')
+    (major, minor, build, patch) = [int(x) for x in version_field_strings]
+  except:
+    # Couldn't parse the version number as a 4-term period-separated number,
+    # just return the original string.
+    return product_version
+
+  # Input version number was out of range. Return the original string.
+  if patch > 255 or build > 65535:
+    return product_string
+
+  msi_major = major
+  msi_minor = build >> 8
+  msi_build = ((build & 0xff) << 8) + patch
+
+  return str(msi_major) + '.' + str(msi_minor) + '.' + str(msi_build)
+
+
 def BuildEnterpriseInstaller(env,
                              product_name,
                              product_version,
@@ -277,12 +329,13 @@
                              product_uninstaller_additional_args,
                              msi_base_name,
                              enterprise_installer_dir,
+                             show_error_action_dll_path,
                              metainstaller_path,
                              output_dir='$STAGING_DIR'):
   """Build an installer for use in enterprise situations.
 
-    Builds an MSI using the supplied details and binaries. This MSI is
-    intended to enable enterprise installation scenarios.
+  Builds an MSI using the supplied details and binaries. This MSI is
+  intended to enable enterprise installation scenarios.
 
   Args:
     env: environment to build with
@@ -302,6 +355,10 @@
     msi_base_name: root of name for the MSI
     enterprise_installer_dir: path to dir which contains
         enterprise_installer.wxs.xml
+    show_error_action_dll_path: path to the error display custom action dll that
+        exports a ShowInstallerResultUIString method. This CA method will read
+        the LastInstallerResultUIString from the product's ClientState key in
+        the registry and display the string via MsiProcessMessage.
     metainstaller_path: path to the Omaha metainstaller to include
     output_dir: path to the directory that will contain the resulting MSI
 
@@ -311,6 +368,7 @@
   Raises:
     Nothing.
   """
+  product_version = ConvertToMSIVersionNumberIfNeeded(product_version)
 
   google_update_wixobj_output = BuildGoogleUpdateFragment(
       env,
@@ -334,6 +392,7 @@
       msi_base_name,
       google_update_wixobj_output,
       enterprise_installer_dir,
+      show_error_action_dll_path,
       metainstaller_path,
       output_dir)
 
@@ -347,17 +406,18 @@
     product_uninstaller_additional_args,
     product_installer_data,
     standalone_installer_path,
+    show_error_action_dll_path,
     msi_base_name,
     enterprise_installer_dir,
     output_dir='$STAGING_DIR'):
   """Build an installer for use in enterprise situations.
 
-    Builds an MSI around the supplied standalone installer. This MSI is
-    intended to enable enterprise installation scenarios while being as close
-    to a normal install as possible. It does not suffer from the separation of
-    Omaha and application install like the other methods do.
+  Builds an MSI around the supplied standalone installer. This MSI is
+  intended to enable enterprise installation scenarios while being as close
+  to a normal install as possible. It does not suffer from the separation of
+  Omaha and application install like the other methods do.
 
-    This method only works for installers that do not use an MSI.
+  This method only works for installers that do not use an MSI.
 
   Args:
     env: environment to build with
@@ -376,6 +436,10 @@
         passed to the product installer when it is wrapped in a standalone
         installer.
     standalone_installer_path: path to product's standalone installer
+    show_error_action_dll_path: path to the error display custom action dll that
+        exports a ShowInstallerResultUIString method. This CA method will read
+        the LastInstallerResultUIString from the product's ClientState key in
+        the registry and display the string via MsiProcessMessage.
     msi_base_name: root of name for the MSI
     enterprise_installer_dir: path to dir which contains
         enterprise_standalone_installer.wxs.xml
@@ -389,6 +453,7 @@
   """
   product_name_legal_identifier = product_name.replace(' ', '')
   msi_name = msi_base_name + '.msi'
+  product_version = ConvertToMSIVersionNumberIfNeeded(product_version)
 
   omaha_installer_namespace = binascii.a2b_hex(_GOOGLE_UPDATE_NAMESPACE_GUID)
 
@@ -424,6 +489,7 @@
       '-dProductCustomParams="%s"' % product_custom_params,
       '-dStandaloneInstallerPath=' + (
           env.File(standalone_installer_path).abspath),
+      '-dShowErrorCADll=' + env.File(show_error_action_dll_path).abspath,
       '-dProductUninstallerAdditionalArgs=' + (
           product_uninstaller_additional_args),
       '-dMsiProductId=' + msi_product_id,
@@ -433,15 +499,15 @@
   if product_installer_data:
     wix_candle_flags.append('-dProductInstallerData=' + product_installer_data)
 
-  wix_env.Append(
-      WIXCANDLEFLAGS=wix_candle_flags
-  )
+  wix_light_flags = [
+      '-sw1076',
+      '-sice:ICE61',
+  ]
 
-  # Disable warning LGHT1076 which complains about a string-length ICE error
-  # that can safely be ignored as per
-  # http://blogs.msdn.com/astebner/archive/2007/02/13/building-an-msi-
-  # using-wix-v3-0-that-includes-the-vc-8-0-runtime-merge-modules.aspx
-  wix_env['WIXLIGHTFLAGS'].append('-sw1076')
+  wix_env.Append(
+      WIXCANDLEFLAGS=wix_candle_flags,
+      WIXLIGHTFLAGS=wix_light_flags
+  )
 
   wix_output = wix_env.WiX(
       target = output_directory_name + '/' + 'unsigned_' + msi_name,
@@ -453,7 +519,8 @@
   # file is rebuilt because the hash of the .wixobj does not change.
   # Also force a dependency on the CA DLL. Otherwise, it might not be built
   # before the MSI.
-  wix_env.Depends(wix_output, [standalone_installer_path])
+  wix_env.Depends(wix_output, [standalone_installer_path,
+                               show_error_action_dll_path])
 
   sign_output = wix_env.SignedBinary(
       target=output_directory_name + '/' + msi_name,
diff --git a/enterprise/installer/custom_actions/build.scons b/enterprise/installer/custom_actions/build.scons
index 5608df5..7c30817 100644
--- a/enterprise/installer/custom_actions/build.scons
+++ b/enterprise/installer/custom_actions/build.scons
@@ -1,6 +1,6 @@
 #!/usr/bin/python2.4
 #
-# Copyright 2009 Google Inc.
+# Copyright 2009-2011 Google Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@
 Import('env')
 
 #
-# Build uninstall_action.dll
+# Build show_error_action.dll
 #
 dll_env = env.Clone(
     # Build a dll, not a lib.
@@ -27,25 +27,18 @@
 
 dll_env.Append(
     LIBS = [
-        ('atls.lib', 'atlsd.lib')[dll_env.Bit('debug')],
         ('libcmt.lib', 'libcmtd.lib')[dll_env.Bit('debug')],
         ('libcpmt.lib', 'libcpmtd.lib')[dll_env.Bit('debug')],
         'msi.lib',
-        'shlwapi.lib',
-        ],
-
-    CPPDEFINES = [
-        '_USRDLL',
-        '_WIN32_IE=0x0602',  # Need this for the Shell reg functions.
         ],
 )
 
-signed_target_name = 'uninstall_action'
+signed_target_name = 'show_error_action'
 target_name = signed_target_name + '_unsigned'
 
 dll_inputs = [
-    'uninstall_action.cc',
-    'uninstall_action.def',
+    'show_error_action.cc',
+    'show_error_action.def',
     ]
 
 unsigned_dll = dll_env.ComponentLibrary(
diff --git a/enterprise/installer/custom_actions/show_error_action.cc b/enterprise/installer/custom_actions/show_error_action.cc
new file mode 100644
index 0000000..a44f052
--- /dev/null
+++ b/enterprise/installer/custom_actions/show_error_action.cc
@@ -0,0 +1,285 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Author: grt
+//
+// A Windows Installer custom action that displays and logs an app installer's
+// InstallResultUIString via MsiProcessMessage.  The app's guid must be provided
+// by the MSI wrapper by way of the CustomActionData property.
+
+#include <windows.h>
+#include <msi.h>
+#include <msiquery.h>
+#include <objbase.h>
+#include <stdlib.h>
+
+#include <algorithm>
+#include <limits>
+#include <string>
+
+#define SOFTWARE_GOOGLE_UPDATE L"Software\\Google\\Update"
+#define SOFTWARE_GOOGLE_UPDATE_CLIENTSTATE \
+    SOFTWARE_GOOGLE_UPDATE L"\\ClientState"
+
+namespace {
+
+const int kGuidStringLength = 38;  // 128/4 + 4 dashes + 2 braces.
+const DWORD kInstallerResultFailedCustomError = 1;
+const wchar_t kPropertyCustomActionData[] = L"CustomActionData";
+const wchar_t kRegKeyClientState[] = SOFTWARE_GOOGLE_UPDATE_CLIENTSTATE;
+const wchar_t kRegKeyGoogleUpdate[] = SOFTWARE_GOOGLE_UPDATE;
+const wchar_t kRegValueLastInstallerResult[] = L"LastInstallerResult";
+const wchar_t kRegValueLastInstallerResultUIString[] =
+    L"LastInstallerResultUIString";
+
+// Gets the value of the property named |property_name|, putting it in
+// |property_value|. The anticipated length of the value can be provided in
+// |expected_length| to reduce overhead. Returns true if a (possibly empty)
+// value is read, or false on error.
+bool GetProperty(MSIHANDLE install,
+                 const wchar_t* property_name,
+                 int expected_length,
+                 std::wstring* property_value) {
+  DWORD value_len = static_cast<DWORD>(std::max(0, expected_length));
+  UINT result = ERROR_SUCCESS;
+  do {
+    // Make space to hold the string terminator.
+    property_value->resize(++value_len);
+    result = MsiGetProperty(install, property_name, &(*property_value)[0],
+                            &value_len);
+  } while (result == ERROR_MORE_DATA &&
+           value_len <= std::numeric_limits<DWORD>::max() - 1);
+
+  if (result == ERROR_SUCCESS)
+    property_value->resize(value_len);
+  else
+    property_value->clear();
+
+  return result == ERROR_SUCCESS;
+}
+
+// The type of a function that returns true if |c| is a valid char in a GUID.
+typedef bool (*IsGuidCharFn)(wchar_t c);
+
+// A function template that returns true if |c| equals some constant |C|.
+template<wchar_t C>
+bool IsChar(wchar_t c) {
+  return c == C;
+}
+
+// Returns true if |c| is a valid hex character.
+bool IsHexDigit(wchar_t c) {
+  return ((c >= L'0' && c <= '9') ||
+          (c >= L'a' && c <= 'f') ||
+          (c >= L'A' && c <= 'F'));
+}
+
+// Returns true if |guid| is a well-formed GUID.
+bool IsGuid(const std::wstring& guid) {
+  static const IsGuidCharFn kGuidCharValidators[] = {
+    IsChar<L'{'>,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsChar<L'-'>,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsChar<L'-'>,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsChar<L'-'>,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsChar<L'-'>,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsHexDigit,
+    IsChar<L'}'>,
+  };
+
+  if (guid.size() != _countof(kGuidCharValidators))
+    return false;
+
+  for (size_t i = 0, end = guid.size(); i < end; ++i) {
+    if (!kGuidCharValidators[i](guid[i]))
+      return false;
+  }
+
+  return true;
+}
+
+// Gets the app guid for the product being installed. Returns false if a value
+// that doesn't look like a GUID is read.
+bool GetProductGuid(MSIHANDLE install, std::wstring* guid) {
+  return GetProperty(install, kPropertyCustomActionData, kGuidStringLength,
+                     guid) &&
+         IsGuid(*guid);
+}
+
+// Populates |key_name| with the full name of |app_guid|'s ClientState registry
+// key.
+void GetAppClientStateKey(const std::wstring& app_guid,
+                          std::wstring* key_name) {
+  const size_t base_len = _countof(kRegKeyClientState) - 1;
+  key_name->reserve(base_len + 1 + app_guid.size());
+  key_name->assign(kRegKeyClientState, base_len);
+  key_name->append(1, L'\\');
+  key_name->append(app_guid);
+}
+
+// Reads the string value named |value_name| in registry key |key| into
+// |result_string|.  Returns ERROR_NOT_SUPPORTED if the value exists but is not
+// of type REG_SZ.
+LONG ReadRegistryStringValue(HKEY key, const wchar_t* value_name,
+                             std::wstring* result_string) {
+  LONG result;
+  DWORD type;
+  DWORD byte_length;
+
+  // Use all of the provided buffer.
+  result_string->resize(result_string->capacity());
+
+  // Figure out how much we can hold there, being careful about overflow.
+  byte_length = static_cast<DWORD>(std::min(
+      result_string->size(),
+      static_cast<size_t>(
+          std::numeric_limits<DWORD>::max() / sizeof(wchar_t))));
+  byte_length *= sizeof(wchar_t);
+
+  do {
+    // Read into the provided buffer.
+    BYTE* buffer = reinterpret_cast<BYTE*>(
+        result_string->empty() ? NULL : &(*result_string)[0]);
+    result = RegQueryValueEx(key, value_name, NULL, &type, buffer,
+                             &byte_length);
+    if (result == ERROR_SUCCESS) {
+      const size_t chars_read = byte_length / sizeof(wchar_t);
+      if (type != REG_SZ) {
+        // The value wasn't a string.
+        result = ERROR_NOT_SUPPORTED;
+      } else if (byte_length == 0) {
+        // The string was empty.
+        result_string->clear();
+      } else if ((*result_string)[chars_read - 1] != L'\0') {
+        // The string was not terminated.  Let std::basic_string do so.
+        result_string->resize(chars_read);
+      } else {
+        // The string was terminated.  Trim off the terminator.
+        result_string->resize(chars_read - 1);
+      }
+    } else if (result == ERROR_MORE_DATA) {
+      // Increase the buffer and try again.
+      result_string->resize(byte_length / sizeof(wchar_t));
+    }
+  } while (result == ERROR_MORE_DATA);
+
+  return result;
+}
+
+// Reads the DWORD value named |value_name| in registry key |key| into |value|.
+// Returns ERROR_NOT_SUPPORTED if the value exists but is not of type REG_DWORD.
+LONG ReadRegistryDwordValue(HKEY key, const wchar_t* value_name, DWORD* value) {
+  LONG result;
+  DWORD type;
+  DWORD byte_length = sizeof(*value);
+
+  result = RegQueryValueEx(key, value_name, NULL, &type,
+                           reinterpret_cast<BYTE*>(value), &byte_length);
+  if (result == ERROR_SUCCESS && type != REG_DWORD) {
+    // The value wasn't a DWORD.
+    result = ERROR_NOT_SUPPORTED;
+  }
+
+  return result;
+}
+
+// Checks to see if the app installer failed with a custom error and provided a
+// UI string.  If so, returns true and populates |result_string| with the UI
+// string.  Otherwise, returns false.
+bool GetLastInstallerResultUIString(const std::wstring& app_guid,
+                                    std::wstring* result_string) {
+  std::wstring client_state_name;
+  HKEY key = NULL;
+
+  GetAppClientStateKey(app_guid, &client_state_name);
+
+  // First try looking in the app's ClientState key.  Failing that, fall back to
+  // Google Update's own SOFTWARE\Google\Update key, into which GoogleUpdate.exe
+  // copies the app's value (see AppManager::ClearInstallerResultApiValues).
+  LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, client_state_name.c_str(),
+                             NULL, KEY_QUERY_VALUE, &key);
+  if (result != ERROR_SUCCESS) {
+    result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, kRegKeyGoogleUpdate, NULL,
+                          KEY_QUERY_VALUE, &key);
+  }
+
+  if (result == ERROR_SUCCESS) {
+    // Is LastInstallerResult == INSTALLER_RESULT_FAILED_CUSTOM_ERROR?
+    DWORD last_installer_error = 0;
+    result = ReadRegistryDwordValue(key, kRegValueLastInstallerResult,
+                                    &last_installer_error);
+    if (result == ERROR_SUCCESS &&
+        last_installer_error == kInstallerResultFailedCustomError) {
+      result = ReadRegistryStringValue(
+          key, kRegValueLastInstallerResultUIString, result_string);
+    }
+
+    RegCloseKey(key);
+  }
+
+  return result == ERROR_SUCCESS;
+}
+
+}  // namespace
+
+// A DLL custom action entrypoint that performs the work described at the top
+// of this file.
+extern "C" UINT __stdcall ShowInstallerResultUIString(MSIHANDLE install) {
+  std::wstring app_guid;
+  std::wstring result_string;
+
+  if (GetProductGuid(install, &app_guid) &&
+      GetLastInstallerResultUIString(app_guid, &result_string) &&
+      !result_string.empty()) {
+    PMSIHANDLE record = MsiCreateRecord(0);
+    if (record != 0UL) {
+      UINT result = MsiRecordSetString(record, 0, result_string.c_str());
+      if (result == ERROR_SUCCESS)
+        MsiProcessMessage(install, INSTALLMESSAGE_ERROR, record);
+    }
+  }
+
+  return ERROR_SUCCESS;
+}
diff --git a/enterprise/installer/custom_actions/show_error_action.def b/enterprise/installer/custom_actions/show_error_action.def
new file mode 100644
index 0000000..a5bdced
--- /dev/null
+++ b/enterprise/installer/custom_actions/show_error_action.def
@@ -0,0 +1,2 @@
+EXPORTS
+ShowInstallerResultUIString PRIVATE
diff --git a/enterprise/installer/custom_actions/uninstall_action.cc b/enterprise/installer/custom_actions/uninstall_action.cc
deleted file mode 100644
index 610e017..0000000
--- a/enterprise/installer/custom_actions/uninstall_action.cc
+++ /dev/null
@@ -1,273 +0,0 @@
-// Copyright 2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// This file implements a Custom Action DLL for standalone MSI installers that
-// uninstall Omaha-managed products by exposing a UninstallOmahaProduct
-// method.
-//
-// The custom action makes the following assumptions:
-// 1) The software to be uninstalled has a corresponding Omaha key under
-//    kOmahaClientsKey\{AppGuid}
-//
-// 2) {AppGuid} and any extra flags needed to cause the product's uninstall
-//    to execute silently are passed in via the CustomActionData property (if
-//    using Wix, set a Property of type 'immediate' with the same name as your
-//    'deferred' custom action - this causes the value to get passed in as
-//    the CustomActionData property here.)
-//    The expected format for the CustomActionData is:
-//      {AppGuid}|<uninstall flags>
-//    For example:
-//      {8BA986DA-5100-405E-AA35-86F34A02ACBF}|--force-uninstall
-//
-// 3) The app's "Clients" key contains a 'name' string value that is used as
-//    the key to the list of Windows uninstall shortcuts that reside in
-//    kUninstallKey.
-//
-// 4) The program to be uninstalled has made its registrations under HKLM.
-//
-// TODO(robertshield): Make 4) an argument, don't assume HKLM.
-// TODO(robertshield): Make this work for non-Omaha-managed products as well.
-
-#include <Windows.h>
-#include <Msi.h>
-#include <MsiQuery.h>
-#include <Shlwapi.h>
-
-#include <string>
-#include <vector>
-
-const wchar_t kOmahaClientsKey[] = L"Software\\Google\\Update\\Clients\\";
-const wchar_t kOmahaProductName[] = L"name";
-const wchar_t kUninstallKey[] =
-    L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\";
-const wchar_t kUninstallCmdName[] = L"UninstallString";
-
-// This is how long (in ms) we wait for the uninstall command to complete.
-const DWORD kUninstallCmdTimeoutMs = 120 * 1000;
-
-static void MsiLogInfo(MSIHANDLE install, const std::wstring& msg) {
-  // Note that PMSIHANDLES clean up after themselves.
-  PMSIHANDLE record = ::MsiCreateRecord(2);
-  std::wstring log_msg(L"UNINSTALL LOG: ");
-  log_msg += msg;
-  ::MsiRecordSetString(record, 0, log_msg.c_str());
-  ::MsiProcessMessage(install, INSTALLMESSAGE(INSTALLMESSAGE_INFO), record);
-}
-
-// Retrieve the named property from the database and stuff it in
-// return_value. Returns true on success, false otherwise.
-//
-// Note that in practice, if this is run as a deferred custom action, you can
-// only query for CustomActionData and a few others as per
-// http://msdn.microsoft.com/en-us/library/aa370543(VS.85).aspx.
-static bool GetMsiProperty(MSIHANDLE msi_handle,
-                           const std::wstring& name,
-                           std::wstring* return_value) {
-  if (!return_value) {
-    return false;
-  }
-
-  DWORD size = 1024;
-  return_value->resize(size + 1);
-  UINT result = ::MsiGetProperty(msi_handle, name.c_str(), &(*return_value)[0],
-                                 &size);
-  if (result == ERROR_MORE_DATA) {
-    return_value->resize(size + 1);
-    result = ::MsiGetProperty(msi_handle, name.c_str(), &(*return_value)[0],
-                              &size);
-  }
-  // Resize the string down to the actual number of characters copied.
-  return_value->resize(size + 1);
-
-  if (result != ERROR_SUCCESS) {
-    std::wstring msg(L"Failed to retrieve property: ");
-    msg += name;
-    MsiLogInfo(msi_handle, msg);
-  }
-
-  return (result == ERROR_SUCCESS);
-}
-
-// Reads in the registry value called value_name residing at key_path under
-// root_key and stuffs it in value. Returns true on success, false
-// otherwise.
-static bool ReadRegStringValue(MSIHANDLE msi_handle,
-                               HKEY root_key,
-                               const std::wstring& key_path,
-                               const std::wstring& value_name,
-                               std::wstring* value) {
-  if (!value) {
-    return false;
-  }
-
-  LSTATUS result;
-  DWORD size = 0;
-  result = ::SHRegGetValue(root_key, key_path.c_str(), value_name.c_str(),
-                           SRRF_RT_REG_SZ, NULL, NULL, &size);
-
-  std::vector<BYTE> buffer;
-  // Note that since we passed NULL in as the buffer, SHRegGetValue will
-  // have returned ERROR_SUCCESS if the key exists.
-  if (result == ERROR_SUCCESS && size > 0) {
-    buffer.resize(size);
-    result = ::SHRegGetValue(root_key, key_path.c_str(), value_name.c_str(),
-                             SRRF_RT_REG_SZ, NULL, &buffer[0], &size);
-  }
-
-  if (result != ERROR_SUCCESS || buffer.empty()) {
-    std::wstring error_message(L"Failed to read reg value: ");
-    error_message += key_path;
-    error_message += L"\\";
-    error_message += value_name;
-    MsiLogInfo(msi_handle, error_message);
-  } else {
-    size_t string_length = size / sizeof(wchar_t);
-    // Note that we must subtract one from the length to account for the extra
-    // null terminator added by SHRegValue().
-    value->assign(reinterpret_cast<wchar_t*>(&buffer[0]), string_length - 1);
-  }
-
-  return (result == ERROR_SUCCESS);
-}
-
-// Runs the command given in cmd_line and waits kUninstallCmdTimeoutMs for it
-// to complete and places the exit code in exit_code. Tries to kill the
-// process if it hasn't completed before the timeout. Returns true on successful
-// completion of the command, false otherwise.
-static bool LaunchAppAndReturnExitCode(MSIHANDLE msi_handle,
-                                       const std::wstring& cmd_line,
-                                       DWORD* exit_code) {
-  if (!exit_code) {
-    return false;
-  }
-
-  bool is_successful = false;
-  STARTUPINFO startup_info = {0};
-  startup_info.cb = sizeof(startup_info);
-  PROCESS_INFORMATION process_info = {0};
-  if (::CreateProcess(NULL,
-                      const_cast<wchar_t*>(cmd_line.c_str()), NULL, NULL,
-                      FALSE, 0, NULL, NULL,
-                      &startup_info, &process_info)) {
-    DWORD wait_result = ::WaitForSingleObject(process_info.hProcess,
-                                              kUninstallCmdTimeoutMs);
-    if (wait_result == WAIT_TIMEOUT) {
-      // Looks like our uninstall process is hung, try to kill it.
-      ::TerminateProcess(process_info.hProcess, 0);
-    } else if (wait_result == WAIT_OBJECT_0) {
-      if (::GetExitCodeProcess(process_info.hProcess, exit_code)) {
-        if (*exit_code != STILL_ACTIVE)
-          is_successful = true;
-      }
-    } else {
-      std::wstring error_message(L"Error waiting for process exit: ");
-      error_message += cmd_line;
-      MsiLogInfo(msi_handle, error_message);
-    }
-
-    // Don't leak the handles.
-    ::CloseHandle(process_info.hThread);
-    ::CloseHandle(process_info.hProcess);
-  } else {
-    std::wstring error_message(L"Failed to CreateProcess ");
-    error_message += cmd_line;
-    MsiLogInfo(msi_handle, error_message);
-  }
-
-  return is_successful;
-}
-
-static bool ParseCustomActionData(const std::wstring& custom_action_data,
-                                  std::wstring* app_id,
-                                  std::wstring* additional_uninstall_args) {
-  if (!app_id || !additional_uninstall_args) {
-    return false;
-  }
-
-  bool result = false;
-  size_t separator_pos = custom_action_data.find(L'|');
-  if (separator_pos != std::wstring::npos) {
-    *app_id = custom_action_data.substr(0, separator_pos);
-    *additional_uninstall_args = custom_action_data.substr(separator_pos + 1);
-    result = true;
-  }
-
-  return result;
-}
-
-extern "C" UINT __stdcall UninstallOmahaProduct(MSIHANDLE msi_handle) {
-  DWORD result = ERROR_INSTALL_FAILURE;
-
-  // Get the app id we're interested in as well as the product uninstallation
-  // parameters.
-  bool valid_ca_data = false;
-  std::wstring custom_action_data;
-  std::wstring app_id;
-  std::wstring additional_uninstall_args;
-  if (GetMsiProperty(msi_handle, L"CustomActionData", &custom_action_data)) {
-    valid_ca_data = ParseCustomActionData(custom_action_data, &app_id,
-                                          &additional_uninstall_args);
-  }
-
-  std::wstring uninstall_cmd;
-  if (valid_ca_data) {
-    // Use the app id to look up the product name...
-    std::wstring product_name;
-    std::wstring omaha_client_key(kOmahaClientsKey);
-    omaha_client_key += app_id;
-
-    std::wstring product_msg(L"Looking for product name in: ");
-    product_msg += omaha_client_key;
-    MsiLogInfo(msi_handle, product_msg);
-
-    if (ReadRegStringValue(msi_handle, HKEY_LOCAL_MACHINE,
-                           omaha_client_key.c_str(), kOmahaProductName,
-                           &product_name)) {
-      std::wstring uninstall_key_path(kUninstallKey);
-      uninstall_key_path += product_name;
-
-      std::wstring key_msg(L"Looking for uninstall key: ");
-      key_msg += uninstall_key_path;
-      MsiLogInfo(msi_handle, key_msg);
-
-      // ... and then use product name to look up the uninstall command line.
-      if (ReadRegStringValue(msi_handle, HKEY_LOCAL_MACHINE,
-                             uninstall_key_path.c_str(), kUninstallCmdName,
-                             &uninstall_cmd)) {
-        result = ERROR_SUCCESS;
-      }
-    }
-  }
-
-  if (result == ERROR_SUCCESS) {
-    // Append the necessary flags to keep the uninstall silent.
-    uninstall_cmd += L" ";
-    uninstall_cmd += additional_uninstall_args;
-
-    std::wstring uninstall_msg(L"Found uninstall command, executing: ");
-    uninstall_msg += uninstall_cmd;
-    MsiLogInfo(msi_handle, uninstall_msg);
-
-    // We've found the uninstall command. Now run it and wait for the response.
-    DWORD exit_code;
-    if (LaunchAppAndReturnExitCode(msi_handle, uninstall_cmd, &exit_code)) {
-      // TODO(robertshield): See about doing something based on the return code.
-    } else {
-      result = ERROR_INSTALL_FAILURE;
-    }
-  }
-
-  return result;
-}
diff --git a/enterprise/installer/custom_actions/uninstall_action.def b/enterprise/installer/custom_actions/uninstall_action.def
deleted file mode 100644
index d5ec42a..0000000
--- a/enterprise/installer/custom_actions/uninstall_action.def
+++ /dev/null
@@ -1,4 +0,0 @@
-LIBRARY    "uninstall_action"
-
-EXPORTS
-  UninstallOmahaProduct         PRIVATE
diff --git a/enterprise/installer/enterprise_installer.wxs.xml b/enterprise/installer/enterprise_installer.wxs.xml
index 050d4d7..92cacfc 100644
--- a/enterprise/installer/enterprise_installer.wxs.xml
+++ b/enterprise/installer/enterprise_installer.wxs.xml
@@ -84,6 +84,9 @@
     <Binary Id='$(var.ProductNameLegalIdentifier)Installer'
             SourceFile='$(var.ProductInstallerPath)' />
 
+    <Binary Id='ShowInstallerResultUIStringDll'
+            SourceFile='$(var.ShowErrorCADll)' />
+
     <Feature Id='Complete' Level='1'>
       <ComponentRef Id='ComponentGoogleUpdate' />
     </Feature>
@@ -102,6 +105,21 @@
              $(var.ProductInstallerDisableUpdateRegistrationArg)'
       Execute='immediate'
       Return='check' />
+    <!-- Send the ProductGuid to the ShowInstallerResultUIString custom action.
+         The value is accessed through the "CustomActionData" property from
+         within the action itself. -->
+    <CustomAction Id='SetAppGuidProperty'
+      Property='ShowInstallerResultUIString'
+      Value='$(var.ProductGuid)' />
+
+    <!-- A custom action to be executed on rollback to log and display the
+         LastInstallerResultUIString. -->
+    <CustomAction Id='ShowInstallerResultUIString'
+      BinaryKey='ShowInstallerResultUIStringDll'
+      DllEntry='ShowInstallerResultUIString'
+      Execute='rollback'
+      Impersonate='no' />
+
     <CustomAction Id='Install$(var.ProductNameLegalIdentifier)'
       BinaryKey='$(var.ProductNameLegalIdentifier)Installer'
       Impersonate='no'
@@ -160,10 +178,18 @@
               After='SetInstallerInstallCommandProperty'>
         (($ComponentGoogleUpdate>2) OR REINSTALL) AND DISABLE_UPDATES
       </Custom>
-      <Custom Action='Install$(var.ProductNameLegalIdentifier)'
+      <Custom Action='SetAppGuidProperty'
               After='AppendDisableUpdateRegistrationArgToInstallerInstallCommandProperty'>
         (($ComponentGoogleUpdate>2) OR REINSTALL)
       </Custom>
+      <Custom Action='ShowInstallerResultUIString'
+              After='SetAppGuidProperty'>
+        (($ComponentGoogleUpdate>2) OR REINSTALL)
+      </Custom>
+      <Custom Action='Install$(var.ProductNameLegalIdentifier)'
+              After='ShowInstallerResultUIString'>
+        (($ComponentGoogleUpdate>2) OR REINSTALL)
+      </Custom>
 
       <Custom Action='CallUninstallerArgs.SetProperty'
               Before='CallUninstaller.SetProperty'>
diff --git a/enterprise/installer/enterprise_standalone_installer.wxs.xml b/enterprise/installer/enterprise_standalone_installer.wxs.xml
index fca65b7..2ca7d94 100644
--- a/enterprise/installer/enterprise_standalone_installer.wxs.xml
+++ b/enterprise/installer/enterprise_standalone_installer.wxs.xml
@@ -85,6 +85,14 @@
 
             <Component Id='ProductClientState'
                        Guid='6B528A57-0CD8-4b26-85F8-1CA05523B8F1'>
+              <!-- Clear the last UI string before running the installer so we know
+                   that any value present upon rollback is fresh. -->
+              <RemoveRegistryValue Key='$(var.UpdateKeyPath)\ClientState\$(var.ProductGuid)'
+                                   Name='LastInstallerResultUIString'
+                                   Root='HKLM' />
+              <RemoveRegistryValue Key='$(var.UpdateKeyPath)'
+                                   Name='LastInstallerResultUIString'
+                                   Root='HKLM' />
               <RegistryValue Id='NonEmptyComponent' Action='write'
                              Root='HKLM'
                              Key='$(var.UpdateKeyPath)\ClientState\$(var.ProductGuid)'
@@ -105,6 +113,9 @@
     <Binary Id='$(var.ProductNameLegalIdentifier)Installer'
             SourceFile='$(var.StandaloneInstallerPath)' />
 
+    <Binary Id='ShowInstallerResultUIStringDll'
+            SourceFile='$(var.ShowErrorCADll)' />
+
     <CustomAction Id='NewerVersionError' Error='4000'/>
 
     <CustomAction Id='SetProductTagProperty'
@@ -137,6 +148,21 @@
                       Return='check' />
     <?endif?>
 
+    <!-- Send the ProductGuid to the ShowInstallerResultUIString custom action.
+         The value is accessed through the "CustomActionData" property from
+         within the action itself. -->
+    <CustomAction Id='SetAppGuidProperty'
+      Property='ShowInstallerResultUIString'
+      Value='$(var.ProductGuid)' />
+
+    <!-- A custom action to be executed on rollback to log and display the
+         LastInstallerResultUIString. -->
+    <CustomAction Id='ShowInstallerResultUIString'
+      BinaryKey='ShowInstallerResultUIStringDll'
+      DllEntry='ShowInstallerResultUIString'
+      Execute='rollback'
+      Impersonate='no' />
+
     <CustomAction Id='DoInstall'
       BinaryKey='$(var.ProductNameLegalIdentifier)Installer'
       Impersonate='no'
@@ -180,7 +206,16 @@
               Before='BuildInstallCommand'>
         (($ProductClientState>2) OR REINSTALL) AND (BRAND &lt;&gt; "")
       </Custom>
-      <Custom Action='BuildInstallCommand' Before='DoInstall'>
+      <Custom Action='BuildInstallCommand'
+              Before='SetAppGuidProperty'>
+        (($ProductClientState>2) OR REINSTALL)
+      </Custom>
+      <Custom Action='SetAppGuidProperty'
+              Before='ShowInstallerResultUIString'>
+        (($ProductClientState>2) OR REINSTALL)
+      </Custom>
+      <Custom Action='ShowInstallerResultUIString'
+              Before='DoInstall'>
         (($ProductClientState>2) OR REINSTALL)
       </Custom>
 
diff --git a/enterprise/installer/google_update_installer_fragment.wxs.xml b/enterprise/installer/google_update_installer_fragment.wxs.xml
index 5b0e6e7..72f019f 100644
--- a/enterprise/installer/google_update_installer_fragment.wxs.xml
+++ b/enterprise/installer/google_update_installer_fragment.wxs.xml
@@ -27,6 +27,14 @@
 
           <Component Id='ComponentGoogleUpdate'
                      Guid='6B528A57-0CD8-4b26-85F8-1CA05523B8F1'>
+            <!-- Clear the last UI string before running the installer so we know
+                 that any value present upon rollback is fresh. -->
+            <RemoveRegistryValue Key='$(var.UpdateKeyPath)\ClientState\$(var.ProductGuid)'
+                                 Name='LastInstallerResultUIString'
+                                 Root='HKLM' />
+            <RemoveRegistryValue Key='$(var.UpdateKeyPath)'
+                                 Name='LastInstallerResultUIString'
+                                 Root='HKLM' />
             <?ifdef UsingGoogleUpdate_1_2_171_OrLater?>
             <RegistryValue Id='NonEmptyComponent' Action='write'
                            Root='HKLM'
diff --git a/enterprise/installer/test/build.scons b/enterprise/installer/test/build.scons
new file mode 100644
index 0000000..c618828
--- /dev/null
+++ b/enterprise/installer/test/build.scons
@@ -0,0 +1,68 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2011 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========================================================================
+
+""" Build enterprise installer test.
+"""
+
+Import('env')
+
+from standalone import standalone_installer
+
+# Build test_setup.exe
+test_setup_env = env.Clone()
+
+test_setup_env.Append(
+    LIBS = [
+        ('msvcrt', 'libcmtd')[test_setup_env.Bit('debug')],
+        ('msvcprt', 'libcpmtd')[test_setup_env.Bit('debug')],
+        ],
+)
+
+test_setup_env.FilterOut(
+    CPPDEFINES=['_WINDOWS'],
+    LINKFLAGS=['/SUBSYSTEM:WINDOWS'],
+)
+test_setup_env.Append(
+    CPPDEFINES=['_CONSOLE'],
+    LINKFLAGS=['/SUBSYSTEM:CONSOLE'],
+)
+
+target_name = 'test_setup'
+
+exe_inputs = [
+    'test_setup.cc',
+    ]
+
+test_setup_env.ComponentProgram(
+    prog_name = target_name,
+    source = exe_inputs,
+)
+
+# Build standalone and enterprise installers for test_setup using each version.
+for omaha_version_info in env['omaha_versions_info']:
+  prefix = omaha_version_info.filename_prefix
+
+  source_binary = '$OBJ_ROOT/mi_exe_stub/%smi_exe_stub.exe' % (prefix)
+
+  standalone_installer.BuildOfflineInstallersVersion(
+      env,
+      omaha_version_info,
+      '$STAGING_DIR',
+      source_binary,
+      '$MAIN_DIR/enterprise/installer/test/standalone_installers.txt',
+      '$MAIN_DIR/enterprise/installer/test',
+      prefix)
diff --git a/enterprise/installer/test/standalone_installers.txt b/enterprise/installer/test/standalone_installers.txt
new file mode 100644
index 0000000..1820fcf
--- /dev/null
+++ b/enterprise/installer/test/standalone_installers.txt
@@ -0,0 +1 @@
+('Failing Enterprise Test', 'TestStandaloneFailure', [('1.0.0.0', '$STAGING_DIR/test_setup.exe', '{665BDD8E-F40C-4384-A9C6-CA3CD5665C83}')], 'TestStandaloneFailureEnterprise', '', '/uninstall', True, '', '' )
diff --git a/enterprise/installer/test/test_setup.cc b/enterprise/installer/test/test_setup.cc
new file mode 100644
index 0000000..bc193d9
--- /dev/null
+++ b/enterprise/installer/test/test_setup.cc
@@ -0,0 +1,58 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Author: grt
+//
+// A dummy app installer that does nothing more than write error information to
+// the registry as per the Google Update Installer Result API and return 1.
+
+#include <windows.h>
+
+#define GOOGLE_UPDATE_KEY L"SOFTWARE\\Google\\Update"
+#define TEST_SETUP_APP_GUID L"{665BDD8E-F40C-4384-A9C6-CA3CD5665C83}"
+
+namespace {
+
+const DWORD kInstallerResultFailedCustomError = 1;
+const wchar_t kErrorString[] = L"This is a detailed error message.";
+const wchar_t kRegClientStateKey[] =
+    GOOGLE_UPDATE_KEY L"\\ClientState\\" TEST_SETUP_APP_GUID;
+const wchar_t kRegInstallerResultValue[] = L"InstallerResult";
+const wchar_t kRegInstallerResultUIStringValue[] = L"InstallerResultUIString";
+
+void WriteFailureValues() {
+  HKEY client_state_key;
+  LONG result = RegCreateKeyEx(HKEY_LOCAL_MACHINE, kRegClientStateKey, 0, NULL,
+                               REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL,
+                               &client_state_key, NULL);
+  if (result == ERROR_SUCCESS) {
+    RegSetValueEx(
+        client_state_key, kRegInstallerResultValue, 0, REG_DWORD,
+        reinterpret_cast<const BYTE*>(&kInstallerResultFailedCustomError),
+        sizeof(kInstallerResultFailedCustomError));
+    RegSetValueEx(
+        client_state_key, kRegInstallerResultUIStringValue, 0, REG_SZ,
+        reinterpret_cast<const BYTE*>(&kErrorString),
+        sizeof(kErrorString));
+    RegCloseKey(client_state_key);
+  }
+}
+
+}  // namespace
+
+int wmain(int argc, wchar_t* argv[]) {
+  WriteFailureValues();
+  return 1;
+}
diff --git "a/enterprise/installer/test/\173665BDD8E-F40C-4384-A9C6-CA3CD5665C83\175.gup" "b/enterprise/installer/test/\173665BDD8E-F40C-4384-A9C6-CA3CD5665C83\175.gup"
new file mode 100644
index 0000000..1316f7e
--- /dev/null
+++ "b/enterprise/installer/test/\173665BDD8E-F40C-4384-A9C6-CA3CD5665C83\175.gup"
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<response protocol="3.0">
+  <app appid="{665BDD8E-F40C-4384-A9C6-CA3CD5665C83}" status="ok">
+    <updatecheck status="ok">
+      <urls>
+        <url codebase="http://dl.google.com/edgedl/chrome/install/${INSTALLER_VERSION}/"/>
+      </urls>
+      <manifest version="${INSTALLER_VERSION}">
+        <packages>
+          <package name="test_setup.exe" hash="${INSTALLER_HASH}" size="${INSTALLER_SIZE}" required="true"/>
+        </packages>
+        <actions>
+          <action event="install" run="test_setup.exe" arguments="" needsadmin="false"/>
+          <action event="postinstall" onsuccess="exitsilentlyonlaunchcmd"/>
+        </actions>
+      </manifest>
+    </updatecheck>
+  </app>
+</response>
diff --git a/enterprise/public_apps.py b/enterprise/public_apps.py
deleted file mode 100644
index 9965660..0000000
--- a/enterprise/public_apps.py
+++ /dev/null
@@ -1,138 +0,0 @@
-#!/usr/bin/python2.4
-#
-# Copyright 2009 Google Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# ========================================================================
-
-"""Contains the list of supported apps that have been publicly announced.
-
-This file is used in the creation of GoogleUpdate.adm.
-"""
-
-# Specifies the list of supported apps that have been publicly announced.
-# The list is used to create app-specific entries in the Group Policy template.
-# Each element of the list is a tuple of:
-# (app name, app ID, optional string to append to the auto-update explanation).
-# The auto-update explanation should start with a space or double new line
-# (\n\n)
-# PLEASE KEEP THE LIST ALPHABETIZED BY APP NAME.
-EXTERNAL_APPS = [
-    ('Gears',
-     '{283EAF47-8817-4C2B-A801-AD1FADFB7BAA}',
-     ' Check http://gears.google.com/.'),
-    ('Google Advertising Cookie Opt-out Plugin',
-     '{ADDE8406-A0F3-4AC2-8878-ADC0BD37BD86}',
-     ' Check http://www.google.com/ads/preferences/plugin/.'),
-    ('Google Apps',
-     '{C4D65027-B96A-49A5-B13C-4E7FAFD2FB7B}',
-     ''),
-    ('Google Apps Sync for Microsoft Outlook',
-     '{BEBCAD10-F1BC-4F92-B4A7-9E2545C809ED}',
-     ' Check http://tools.google.com/dlpage/gappssync/.'),
-    ('Google Chrome',
-     '{8A69D345-D564-463C-AFF1-A69D9E530F96}',
-     ' Check http://www.google.com/chrome/.'),
-    ('Google Chrome Frame',
-     '{8BA986DA-5100-405E-AA35-86F34A02ACBF}',
-     ' Check http://www.google.com/chromeframe/.'),
-    ('Google Earth',
-     '{74AF07D8-FB8F-4D51-8AC7-927721D56EBB}',
-     ' Check http://earth.google.com/.'),
-    ('Google Earth (per-user install)',
-     '{0A52903D-0FBF-439A-93E4-CB609A2F63DB}',
-     ' Check http://earth.google.com/.'),
-    ('Google Earth Plugin',
-     '{2BF2CA35-CCAF-4E58-BAB7-4163BFA03B88}',
-     ' Check http://code.google.com/apis/earth/.'),
-    ('Google Earth Pro',
-     '{65E60E95-0DE9-43FF-9F3F-4F7D2DFF04B5}',
-     ' Check http://earth.google.com/enterprise/earth_pro.html.'),
-    ('Google Email Uploader',
-     '{84F41014-78F2-4EBF-AF9B-8D7D12FCC37B}',
-     ' Check http://mail.google.com/mail/help/email_uploader.html.'),
-    ('Google Talk Labs Edition',
-     '{7C9D2019-25AD-4F9B-B4C4-F0F537A9626E}',
-     ' Check http://www.google.com/talk/labsedition/.'),
-    ('Google Talk Plugin (Voice and Video Chat)',
-     '{D0AB2EBC-931B-4013-9FEB-C9C4C2225C8C}',
-     ' Check http://mail.google.com/videochat.'),
-    ('O3D',
-     '{70308795-045C-42DA-8F4E-D452381A7459}',
-     ' Check http://code.google.com/apis/o3d/.'),
-    ('O3D Extras',
-     '{34B2805D-C72C-4F81-AED5-5A22D1E092F1}',
-     ''),
-    # IMEs.
-    # All are transliteration IMEs unless otherwise specified.
-    ('Google Amharic Input',
-     '{12C37803-FC8D-48C1-A759-7C88C36BCAA4}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Arabic Input',
-     '{49B24240-CC72-48D7-9A01-6285118C9CA9}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Bengali Input',
-     '{446C4D62-5D85-4E6A-845E-FB19AC8C84F8}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Farsi Input',
-     '{E0642E36-9D8E-441E-A527-683F77A50FDF}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Greek Input',
-     '{45186F45-0E1D-49F3-A534-A52B81F60897}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Gujarati Input',
-     '{0693199F-9DF6-4020-B760-CA993177C362}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Hindi Input',
-     '{06A2F917-C899-44EE-8F47-5B9128D96B0A}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Japanese Input',  # Not a transliteration IME.
-     '{DDCCD2A9-025E-4142-BCEB-F467B88CF830}',
-     ' Check http://www.google.com/intl/ja/ime/.'),
-    ('Google Kannada Input',
-     '{689F4361-5837-4A9C-8BF8-078D04406EC3}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Malayalam Input',
-     '{DA2110CA-14F7-4560-A76E-D47345024C49}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Marathi Input',
-     '{79D2E710-121A-4892-9541-66740728CEBB}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Nepali Input',
-     '{0657DE2E-EC18-4C72-8D58-7D864EA210DE}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Punjabi Input',
-     '{A8DE44D0-9B9D-4EAF-BD5B-6411CE79A39E}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Russian Input',
-     '{BBE57E48-4B5B-4346-BD7C-FF75A3AADD55}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Sanskrit Input',
-     '{0D85AB45-243B-4C69-9F5E-7D309CF4CE33}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Serbian Input',
-     '{3A76050C-F9E7-44AF-B463-408CEBC0A896}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Tamil Input',
-     '{7498340C-3670-47E3-82AE-1BF2B1D3FCD6}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Telugu Input',
-     '{9FF9FAC2-A7E1-4A34-AB91-77AD18CED53F}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Tigrinya Input',
-     '{ADC6C65A-FF16-40D4-B7F1-19E403583515}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ('Google Urdu Input',
-     '{311963FF-A0E6-4D8E-BFC7-1C90B261180C}',
-     ' Check http://www.google.com/ime/transliteration/.'),
-    ]
diff --git a/enterprise/test_gold.adm b/enterprise/test_gold.adm
index 5afee08..2f3fc22 100644
--- a/enterprise/test_gold.adm
+++ b/enterprise/test_gold.adm
Binary files differ
diff --git a/google_update/build.scons b/google_update/build.scons
index 7ed4259..77a6ee0 100644
--- a/google_update/build.scons
+++ b/google_update/build.scons
@@ -14,7 +14,7 @@
 # limitations under the License.
 # ========================================================================
 
-# We always build the shell as GoogleUpdate_built.exe.
+# We always build the shell as GoogleUpdate_signed.exe.
 # For official builds, we copy the saved constant shell to the output directory.
 # For unofficial builds, we copy the built shell.
 # Otherwise, the goopdate.dll certificate check fails.
@@ -87,7 +87,7 @@
 
 exe_inputs = [
     'winmain.cc',
-     '../common/signaturevalidator.cc',
+     '../base/signaturevalidator.cc',
      exe_env.RES('resource.rc'),
      ]
 
@@ -115,7 +115,7 @@
   exe_env['CCFLAGS'] += ['/GS-']
 
 unsigned_exe_output = exe_env.ComponentProgram(
-    prog_name='GoogleUpdate_unsigned.exe',
+    prog_name='GoogleUpdate_unsigned',
     source=exe_inputs,
 )
 
@@ -140,5 +140,5 @@
 env.Replicate('$STAGING_DIR', source_shell, REPLICATE_REPLACE=[('_signed', '')])
 env.Replicate(target='$STAGING_DIR',
               source=sign_output[0],
-              REPLICATE_REPLACE=[('GoogleUpdate_signed', 'GoogleCrashHandler')]
+              REPLICATE_REPLACE=[('GoogleUpdate_signed', env['omaha_versions_info'][0].crash_handler_filename)]
 )
diff --git a/google_update/generated_resources_am.rc b/google_update/generated_resources_am.rc
index ef8168e..412496c 100644
--- a/google_update/generated_resources_am.rc
+++ b/google_update/generated_resources_am.rc
Binary files differ
diff --git a/google_update/generated_resources_ar.rc b/google_update/generated_resources_ar.rc
index 54fee44..6b78c4c 100644
--- a/google_update/generated_resources_ar.rc
+++ b/google_update/generated_resources_ar.rc
Binary files differ
diff --git a/google_update/generated_resources_bg.rc b/google_update/generated_resources_bg.rc
index 7952225..e4f5988 100644
--- a/google_update/generated_resources_bg.rc
+++ b/google_update/generated_resources_bg.rc
Binary files differ
diff --git a/google_update/generated_resources_bn.rc b/google_update/generated_resources_bn.rc
index 8d318fc..aacd0a7 100644
--- a/google_update/generated_resources_bn.rc
+++ b/google_update/generated_resources_bn.rc
Binary files differ
diff --git a/google_update/generated_resources_ca.rc b/google_update/generated_resources_ca.rc
index 5d9ab59..b0349bc 100644
--- a/google_update/generated_resources_ca.rc
+++ b/google_update/generated_resources_ca.rc
Binary files differ
diff --git a/google_update/generated_resources_cs.rc b/google_update/generated_resources_cs.rc
index 1542f1f..d3cb1b2 100644
--- a/google_update/generated_resources_cs.rc
+++ b/google_update/generated_resources_cs.rc
Binary files differ
diff --git a/google_update/generated_resources_da.rc b/google_update/generated_resources_da.rc
index 597639b..55bbcfa 100644
--- a/google_update/generated_resources_da.rc
+++ b/google_update/generated_resources_da.rc
Binary files differ
diff --git a/google_update/generated_resources_de.rc b/google_update/generated_resources_de.rc
index c8b69d2..2815dfb 100644
--- a/google_update/generated_resources_de.rc
+++ b/google_update/generated_resources_de.rc
Binary files differ
diff --git a/google_update/generated_resources_el.rc b/google_update/generated_resources_el.rc
index 65dba7f..f6e86bf 100644
--- a/google_update/generated_resources_el.rc
+++ b/google_update/generated_resources_el.rc
Binary files differ
diff --git a/google_update/generated_resources_en-GB.rc b/google_update/generated_resources_en-GB.rc
index 2b21a6c..6f1f74c 100644
--- a/google_update/generated_resources_en-GB.rc
+++ b/google_update/generated_resources_en-GB.rc
Binary files differ
diff --git a/google_update/generated_resources_en.rc b/google_update/generated_resources_en.rc
index caad7e7..0afd082 100644
--- a/google_update/generated_resources_en.rc
+++ b/google_update/generated_resources_en.rc
Binary files differ
diff --git a/google_update/generated_resources_es-419.rc b/google_update/generated_resources_es-419.rc
index b7b7f08..6198aa0 100644
--- a/google_update/generated_resources_es-419.rc
+++ b/google_update/generated_resources_es-419.rc
Binary files differ
diff --git a/google_update/generated_resources_es.rc b/google_update/generated_resources_es.rc
index acb0763..d17e4a4 100644
--- a/google_update/generated_resources_es.rc
+++ b/google_update/generated_resources_es.rc
Binary files differ
diff --git a/google_update/generated_resources_et.rc b/google_update/generated_resources_et.rc
index a4f4528..f0609f3 100644
--- a/google_update/generated_resources_et.rc
+++ b/google_update/generated_resources_et.rc
Binary files differ
diff --git a/google_update/generated_resources_fa.rc b/google_update/generated_resources_fa.rc
index 24e414a..c55304b 100644
--- a/google_update/generated_resources_fa.rc
+++ b/google_update/generated_resources_fa.rc
Binary files differ
diff --git a/google_update/generated_resources_fi.rc b/google_update/generated_resources_fi.rc
index 9568222..430c736 100644
--- a/google_update/generated_resources_fi.rc
+++ b/google_update/generated_resources_fi.rc
Binary files differ
diff --git a/google_update/generated_resources_fil.rc b/google_update/generated_resources_fil.rc
index ffb9803..0acfcc0 100644
--- a/google_update/generated_resources_fil.rc
+++ b/google_update/generated_resources_fil.rc
Binary files differ
diff --git a/google_update/generated_resources_fr.rc b/google_update/generated_resources_fr.rc
index bb82ec3..4caaa81 100644
--- a/google_update/generated_resources_fr.rc
+++ b/google_update/generated_resources_fr.rc
Binary files differ
diff --git a/google_update/generated_resources_gu.rc b/google_update/generated_resources_gu.rc
index 40e5a0e..d992a2d 100644
--- a/google_update/generated_resources_gu.rc
+++ b/google_update/generated_resources_gu.rc
Binary files differ
diff --git a/google_update/generated_resources_hi.rc b/google_update/generated_resources_hi.rc
index 2bc8ea9..35fc8b8 100644
--- a/google_update/generated_resources_hi.rc
+++ b/google_update/generated_resources_hi.rc
Binary files differ
diff --git a/google_update/generated_resources_hr.rc b/google_update/generated_resources_hr.rc
index 9e2fa8a..034b30f 100644
--- a/google_update/generated_resources_hr.rc
+++ b/google_update/generated_resources_hr.rc
Binary files differ
diff --git a/google_update/generated_resources_hu.rc b/google_update/generated_resources_hu.rc
index 3a90e0d..1a0bcf1 100644
--- a/google_update/generated_resources_hu.rc
+++ b/google_update/generated_resources_hu.rc
Binary files differ
diff --git a/google_update/generated_resources_id.rc b/google_update/generated_resources_id.rc
index f5f455b..70c62a8 100644
--- a/google_update/generated_resources_id.rc
+++ b/google_update/generated_resources_id.rc
Binary files differ
diff --git a/google_update/generated_resources_is.rc b/google_update/generated_resources_is.rc
index ec8e504..76a3420 100644
--- a/google_update/generated_resources_is.rc
+++ b/google_update/generated_resources_is.rc
Binary files differ
diff --git a/google_update/generated_resources_it.rc b/google_update/generated_resources_it.rc
index dca20dc..bbf06e4 100644
--- a/google_update/generated_resources_it.rc
+++ b/google_update/generated_resources_it.rc
Binary files differ
diff --git a/google_update/generated_resources_iw.rc b/google_update/generated_resources_iw.rc
index d3ed1a2..b0da73d 100644
--- a/google_update/generated_resources_iw.rc
+++ b/google_update/generated_resources_iw.rc
Binary files differ
diff --git a/google_update/generated_resources_ja.rc b/google_update/generated_resources_ja.rc
index a57bb19..02c64ba 100644
--- a/google_update/generated_resources_ja.rc
+++ b/google_update/generated_resources_ja.rc
Binary files differ
diff --git a/google_update/generated_resources_kn.rc b/google_update/generated_resources_kn.rc
index 02537ac..67de798 100644
--- a/google_update/generated_resources_kn.rc
+++ b/google_update/generated_resources_kn.rc
Binary files differ
diff --git a/google_update/generated_resources_ko.rc b/google_update/generated_resources_ko.rc
index b674691..6269452 100644
--- a/google_update/generated_resources_ko.rc
+++ b/google_update/generated_resources_ko.rc
Binary files differ
diff --git a/google_update/generated_resources_lt.rc b/google_update/generated_resources_lt.rc
index ff05b43..2612bae 100644
--- a/google_update/generated_resources_lt.rc
+++ b/google_update/generated_resources_lt.rc
Binary files differ
diff --git a/google_update/generated_resources_lv.rc b/google_update/generated_resources_lv.rc
index 18f5d6e..943f5ad 100644
--- a/google_update/generated_resources_lv.rc
+++ b/google_update/generated_resources_lv.rc
Binary files differ
diff --git a/google_update/generated_resources_ml.rc b/google_update/generated_resources_ml.rc
index a5e3ccc..b702ce6 100644
--- a/google_update/generated_resources_ml.rc
+++ b/google_update/generated_resources_ml.rc
Binary files differ
diff --git a/google_update/generated_resources_mr.rc b/google_update/generated_resources_mr.rc
index 36edc9b..b8bf1a5 100644
--- a/google_update/generated_resources_mr.rc
+++ b/google_update/generated_resources_mr.rc
Binary files differ
diff --git a/google_update/generated_resources_ms.rc b/google_update/generated_resources_ms.rc
index a11225f..541c703 100644
--- a/google_update/generated_resources_ms.rc
+++ b/google_update/generated_resources_ms.rc
Binary files differ
diff --git a/google_update/generated_resources_nl.rc b/google_update/generated_resources_nl.rc
index ab86977..2bb60b7 100644
--- a/google_update/generated_resources_nl.rc
+++ b/google_update/generated_resources_nl.rc
Binary files differ
diff --git a/google_update/generated_resources_no.rc b/google_update/generated_resources_no.rc
index 9f5c769..06f98b4 100644
--- a/google_update/generated_resources_no.rc
+++ b/google_update/generated_resources_no.rc
Binary files differ
diff --git a/google_update/generated_resources_or.rc b/google_update/generated_resources_or.rc
index b0c9107..88242e9 100644
--- a/google_update/generated_resources_or.rc
+++ b/google_update/generated_resources_or.rc
Binary files differ
diff --git a/google_update/generated_resources_pl.rc b/google_update/generated_resources_pl.rc
index 17388ac..0193115 100644
--- a/google_update/generated_resources_pl.rc
+++ b/google_update/generated_resources_pl.rc
Binary files differ
diff --git a/google_update/generated_resources_pt-BR.rc b/google_update/generated_resources_pt-BR.rc
index f0567e8..b9c1aef 100644
--- a/google_update/generated_resources_pt-BR.rc
+++ b/google_update/generated_resources_pt-BR.rc
Binary files differ
diff --git a/google_update/generated_resources_pt-PT.rc b/google_update/generated_resources_pt-PT.rc
index 657aa08..df6b223 100644
--- a/google_update/generated_resources_pt-PT.rc
+++ b/google_update/generated_resources_pt-PT.rc
Binary files differ
diff --git a/google_update/generated_resources_ro.rc b/google_update/generated_resources_ro.rc
index 2b35bd4..e8ccc74 100644
--- a/google_update/generated_resources_ro.rc
+++ b/google_update/generated_resources_ro.rc
Binary files differ
diff --git a/google_update/generated_resources_ru.rc b/google_update/generated_resources_ru.rc
index 3603639..d2bd899 100644
--- a/google_update/generated_resources_ru.rc
+++ b/google_update/generated_resources_ru.rc
Binary files differ
diff --git a/google_update/generated_resources_sk.rc b/google_update/generated_resources_sk.rc
index 19a2ddb..4f02548 100644
--- a/google_update/generated_resources_sk.rc
+++ b/google_update/generated_resources_sk.rc
Binary files differ
diff --git a/google_update/generated_resources_sl.rc b/google_update/generated_resources_sl.rc
index 4c35a72..be44431 100644
--- a/google_update/generated_resources_sl.rc
+++ b/google_update/generated_resources_sl.rc
Binary files differ
diff --git a/google_update/generated_resources_sr.rc b/google_update/generated_resources_sr.rc
index f2b6d5f..890cd3c 100644
--- a/google_update/generated_resources_sr.rc
+++ b/google_update/generated_resources_sr.rc
Binary files differ
diff --git a/google_update/generated_resources_sv.rc b/google_update/generated_resources_sv.rc
index 282b4bc..fb1beef 100644
--- a/google_update/generated_resources_sv.rc
+++ b/google_update/generated_resources_sv.rc
Binary files differ
diff --git a/google_update/generated_resources_sw.rc b/google_update/generated_resources_sw.rc
index f88d8af..ebe51b1 100644
--- a/google_update/generated_resources_sw.rc
+++ b/google_update/generated_resources_sw.rc
Binary files differ
diff --git a/google_update/generated_resources_ta.rc b/google_update/generated_resources_ta.rc
index 46b0d31..f8ad2b2 100644
--- a/google_update/generated_resources_ta.rc
+++ b/google_update/generated_resources_ta.rc
Binary files differ
diff --git a/google_update/generated_resources_te.rc b/google_update/generated_resources_te.rc
index 571939f..7fcf7ed 100644
--- a/google_update/generated_resources_te.rc
+++ b/google_update/generated_resources_te.rc
Binary files differ
diff --git a/google_update/generated_resources_th.rc b/google_update/generated_resources_th.rc
index 40fee4d..4f75099 100644
--- a/google_update/generated_resources_th.rc
+++ b/google_update/generated_resources_th.rc
Binary files differ
diff --git a/google_update/generated_resources_tr.rc b/google_update/generated_resources_tr.rc
index ab7e33d..da5234b 100644
--- a/google_update/generated_resources_tr.rc
+++ b/google_update/generated_resources_tr.rc
Binary files differ
diff --git a/google_update/generated_resources_uk.rc b/google_update/generated_resources_uk.rc
index 9a796fa..9f93de5 100644
--- a/google_update/generated_resources_uk.rc
+++ b/google_update/generated_resources_uk.rc
Binary files differ
diff --git a/google_update/generated_resources_ur.rc b/google_update/generated_resources_ur.rc
index 2ebce6b..d02115e 100644
--- a/google_update/generated_resources_ur.rc
+++ b/google_update/generated_resources_ur.rc
Binary files differ
diff --git a/google_update/generated_resources_userdefault.rc b/google_update/generated_resources_userdefault.rc
index 71ac396..b17bcab 100644
--- a/google_update/generated_resources_userdefault.rc
+++ b/google_update/generated_resources_userdefault.rc
Binary files differ
diff --git a/google_update/generated_resources_vi.rc b/google_update/generated_resources_vi.rc
index 8e746bf..2136404 100644
--- a/google_update/generated_resources_vi.rc
+++ b/google_update/generated_resources_vi.rc
Binary files differ
diff --git a/google_update/generated_resources_zh-CN.rc b/google_update/generated_resources_zh-CN.rc
index b9c56d4..f28e0c9 100644
--- a/google_update/generated_resources_zh-CN.rc
+++ b/google_update/generated_resources_zh-CN.rc
Binary files differ
diff --git a/google_update/generated_resources_zh-HK.rc b/google_update/generated_resources_zh-HK.rc
index cbbcf56..0a80e81 100644
--- a/google_update/generated_resources_zh-HK.rc
+++ b/google_update/generated_resources_zh-HK.rc
Binary files differ
diff --git a/google_update/generated_resources_zh-TW.rc b/google_update/generated_resources_zh-TW.rc
index dc5e2e6..aec1f7c 100644
--- a/google_update/generated_resources_zh-TW.rc
+++ b/google_update/generated_resources_zh-TW.rc
Binary files differ
diff --git a/google_update/google_update.grh b/google_update/google_update.grh
index 1bc4e4d..563bb97 100644
--- a/google_update/google_update.grh
+++ b/google_update/google_update.grh
@@ -1,4 +1,4 @@
-// Copyright 2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -13,11 +13,11 @@
 // limitations under the License.
 // ========================================================================
 // This file is automatically generated by GRIT.  Do not edit.
-// Built on Tue Oct 13 11:25:08 2009
+// Built on Thu Nov 18 11:10:52 2010
 
-#ifndef RESOURCE_804928684763__
-#define RESOURCE_804928684763__
+#ifndef RESOURCE_636496767058__
+#define RESOURCE_636496767058__
 
 
 
-#endif // RESOURCE_804928684763__
+#endif // RESOURCE_636496767058__
diff --git a/goopdate/goopdate.ico b/google_update/google_update.ico
similarity index 100%
rename from goopdate/goopdate.ico
rename to google_update/google_update.ico
Binary files differ
diff --git a/google_update/google_update_unittest.cc b/google_update/google_update_unittest.cc
index 46a5e72..b8aea62 100644
--- a/google_update/google_update_unittest.cc
+++ b/google_update/google_update_unittest.cc
@@ -14,18 +14,18 @@
 // ========================================================================
 
 #include <atlpath.h>
-#include "omaha/common/app_util.h"
-#include "omaha/common/file.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/const_goopdate.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/file.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/utils.h"
 #include "omaha/testing/unit_test.h"
 
 namespace omaha {
 
 TEST(GoogleUpdateTest, ShellVersion) {
   CPath actual_shell_path(app_util::GetCurrentModuleDirectory());
-  ASSERT_TRUE(actual_shell_path.Append(kGoopdateFileName));
+  ASSERT_TRUE(actual_shell_path.Append(kOmahaShellFileName));
 
   ASSERT_TRUE(File::Exists(actual_shell_path));
   const ULONGLONG actual_shell_version =
diff --git a/google_update/resource.rc b/google_update/resource.rc
index 5a35419..a008c4d 100644
--- a/google_update/resource.rc
+++ b/google_update/resource.rc
@@ -13,8 +13,8 @@
 // limitations under the License.
 // ========================================================================
 
+#include <afxres.h>
 #include "resource.h"
-#include "afxres.h"
 
 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
 #pragma code_page(1252)
@@ -23,5 +23,5 @@
 
 // Icon with lowest ID value placed first to ensure application icon
 // remains consistent on all systems.
-IDI_APP                 ICON                    "omaha/goopdate/goopdate.ico"
+IDI_APP   ICON    "omaha/google_update/google_update.ico"
 
diff --git a/google_update/version.rc b/google_update/version.rc
deleted file mode 100644
index 5756175..0000000
--- a/google_update/version.rc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <afxres.h>
-
-LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
-
-VS_VERSION_INFO VERSIONINFO
- FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_PATCH
- PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_PATCH
- FILEFLAGSMASK 0x17L
-#ifdef _DEBUG
- FILEFLAGS 0x1L
-#else
- FILEFLAGS 0x0L
-#endif
- FILEOS 0x4L
- FILETYPE 0x0L
- FILESUBTYPE 0x0L
-BEGIN
-    BLOCK "StringFileInfo"
-    BEGIN
-        BLOCK "[GRITVERLANGCHARSETHEX]"
-        BEGIN
-            VALUE "CompanyName", "Google Inc."
-            VALUE "FileDescription", "Google Installer"
-            VALUE "FileVersion", VERSION_NUMBER_STRING
-            VALUE "InternalName", "Google Update"
-            VALUE "LegalCopyright", "Copyright 2007-2010 Google Inc."
-            VALUE "OriginalFilename", "GoogleUpdate.exe"
-            VALUE "ProductName", "Google Update"
-            VALUE "ProductVersion", VERSION_NUMBER_STRING
-#ifdef _DEBUG
-            VALUE "Debug", ""
-#endif
-#if !OFFICIAL_BUILD
-            VALUE "Privatebuild", ""
-#endif
-        END
-    END
-    BLOCK "VarFileInfo"
-    BEGIN
-        VALUE "Translation",  [GRITVERLANGID], [GRITVERCHARSETID]
-    END
-END
diff --git a/google_update/winmain.cc b/google_update/winmain.cc
index 15e30a1..a307a0a 100644
--- a/google_update/winmain.cc
+++ b/google_update/winmain.cc
@@ -42,10 +42,12 @@
 #include <atlpath.h>
 #include <atlstr.h>
 
-#include "omaha/common/constants.h"
-#include "omaha/common/error.h"
-#include "omaha/common/signaturevalidator.h"
-#include "omaha/goopdate/const_goopdate.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/error.h"
+#include "omaha/base/signaturevalidator.h"
+#include "omaha/common/const_goopdate.h"
+
+// TODO(omaha3): move to common.
 #include "omaha/goopdate/main.h"
 
 namespace omaha {
@@ -90,58 +92,6 @@
   return is_vista;
 }
 
-// Adapted from vistautil.cc.
-HRESULT IsUserRunningSplitToken(bool* is_split_token) {
-  if (!IsVistaOrLater()) {
-    *is_split_token = false;
-    return S_OK;
-  }
-
-  HANDLE process_token = NULL;
-  if (!::OpenProcessToken(::GetCurrentProcess(),
-                          TOKEN_QUERY,
-                          &process_token)) {
-    HRESULT hr = HRESULTFromLastError();
-    ::CloseHandle(process_token);
-    return hr;
-  }
-
-  TOKEN_ELEVATION_TYPE elevation_type = TokenElevationTypeDefault;
-  DWORD size_returned = 0;
-  if (!::GetTokenInformation(process_token,
-                             TokenElevationType,
-                             &elevation_type,
-                             sizeof(elevation_type),
-                             &size_returned)) {
-    HRESULT hr = HRESULTFromLastError();
-    ::CloseHandle(process_token);
-    return hr;
-  }
-
-  ::CloseHandle(process_token);
-
-  *is_split_token = elevation_type == TokenElevationTypeFull ||
-                    elevation_type == TokenElevationTypeLimited;
-
-  return S_OK;
-}
-
-// Adapted from vistautil.cc.
-bool IsUACDisabled() {
-  if (!IsVistaOrLater()) {
-    return false;
-  }
-
-  // Split token indicates that UAC is on.
-  bool is_split_token = true;
-  if SUCCEEDED(IsUserRunningSplitToken(&is_split_token)) {
-    return !is_split_token;
-  } else {
-    // Return a safe value on failure.
-    return false;
-  }
-}
-
 // Checking the full path vs. just being somewhere in Program Files is important
 // because other programs may have lowered the ACLs of some subdirectories.
 bool IsRunningFromProgramFilesDirectory() {
@@ -217,10 +167,6 @@
     return S_OK;
   }
 
-  if (IsUACDisabled()) {
-    return S_OK;
-  }
-
   // Verify the Authenticode signature but use only the local cache for
   // revocation checks.
   HRESULT hr = VerifySignature(file_path, false);
@@ -284,7 +230,7 @@
 
   // Try the side-by-side DLL first.
   _tcscpy_s(path, arraysize(path), base_path);
-  if (!::PathAppend(path, omaha::kGoopdateDllName)) {
+  if (!::PathAppend(path, omaha::kOmahaDllName)) {
     return HRESULTFromLastError();
   }
   if (FileExists(path)) {
@@ -302,7 +248,7 @@
   if (!::PathAppend(path, version)) {
     return HRESULTFromLastError();
   }
-  if (!::PathAppend(path, omaha::kGoopdateDllName)) {
+  if (!::PathAppend(path, omaha::kOmahaDllName)) {
     return HRESULTFromLastError();
   }
   if (!FileExists(path)) {
diff --git a/goopdate/app.cc b/goopdate/app.cc
new file mode 100644
index 0000000..871f4d2
--- /dev/null
+++ b/goopdate/app.cc
@@ -0,0 +1,1177 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app.h"
+#include "omaha/base/error.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/time.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/experiment_labels.h"
+#include "omaha/goopdate/app_manager.h"
+#include "omaha/goopdate/app_state.h"
+#include "omaha/goopdate/app_state_init.h"
+#include "omaha/goopdate/current_state.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/server_resource.h"
+#include "omaha/goopdate/string_formatter.h"
+
+namespace omaha {
+
+App::App(const GUID& app_guid, bool is_update, AppBundle* app_bundle)
+    : ModelObject(app_bundle->model()),
+      app_bundle_(app_bundle),
+      is_update_(is_update),
+      has_update_available_(false),
+      app_guid_(app_guid),
+      iid_(GUID_NULL),
+      install_time_diff_sec_(0),
+      is_eula_accepted_(TRISTATE_NONE),  // Cannot ping until set true.
+      browser_type_(BROWSER_UNKNOWN),
+      days_since_last_active_ping_(0),
+      days_since_last_roll_call_(0),
+      usage_stats_enable_(TRISTATE_NONE),
+      did_run_(ACTIVE_UNKNOWN),
+      is_canceled_(false),
+      completion_result_(PingEvent::EVENT_RESULT_SUCCESS),
+      installer_result_code_(0),
+      installer_result_extra_code1_(0),
+      post_install_action_(POST_INSTALL_ACTION_DEFAULT),
+      previous_total_download_bytes_(0),
+      download_start_time_ms_(0),
+      download_complete_time_ms_(0),
+      num_bytes_downloaded_(0) {
+  ASSERT1(!::IsEqualGUID(GUID_NULL, app_guid_));
+
+  current_version_.reset(new AppVersion(this));
+  next_version_.reset(new AppVersion(this));
+
+  // TODO(omaha):  set the working_version_ correctly to indicate which
+  // version of the app is modified: current version for components and
+  // next version for install/updates. The code do not support components yet
+  // and the working version in set to next always.
+  working_version_ = next_version_.get();
+  app_state_.reset(new fsm::AppStateInit);
+}
+
+// Destruction of App objects happens within the scope of their parent,
+// which controls the locking.
+App::~App() {
+  ASSERT1(model()->IsLockedByCaller());
+  working_version_ = NULL;
+  app_bundle_ = NULL;
+}
+
+STDMETHODIMP App::get_appId(BSTR* app_id) {
+  __mutexScope(model()->lock());
+  ASSERT1(app_id);
+  *app_id = GuidToString(app_guid_).AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP App::get_language(BSTR* language) {
+  __mutexScope(model()->lock());
+  ASSERT1(language);
+  *language = language_.AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP App::put_language(BSTR language) {
+  __mutexScope(model()->lock());
+  language_ = language;
+  return S_OK;
+}
+
+STDMETHODIMP App::get_ap(BSTR* ap) {
+  __mutexScope(model()->lock());
+  ASSERT1(ap);
+  *ap = ap_.AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP App::put_ap(BSTR ap) {
+  __mutexScope(model()->lock());
+  ap_ = ap;
+  return S_OK;
+}
+
+STDMETHODIMP App::get_pv(BSTR* pv) {
+  __mutexScope(model()->lock());
+  ASSERT1(pv);
+  *pv = pv_.AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP App::put_pv(BSTR pv) {
+  __mutexScope(model()->lock());
+  pv_ = pv;
+  return S_OK;
+}
+
+STDMETHODIMP App::get_ttToken(BSTR* tt_token) {
+  __mutexScope(model()->lock());
+  ASSERT1(tt_token);
+  *tt_token = tt_token_.AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP App::put_ttToken(BSTR tt_token) {
+  __mutexScope(model()->lock());
+  tt_token_ = tt_token;
+  return S_OK;
+}
+
+STDMETHODIMP App::get_iid(BSTR* iid) {
+  __mutexScope(model()->lock());
+  ASSERT1(iid);
+  *iid = GuidToString(iid_).AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP App::put_iid(BSTR iid) {
+  __mutexScope(model()->lock());
+  return StringToGuidSafe(iid, &iid_);
+}
+
+STDMETHODIMP App::get_brandCode(BSTR* brand_code) {
+  __mutexScope(model()->lock());
+  ASSERT1(brand_code);
+  *brand_code = brand_code_.AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP App::put_brandCode(BSTR brand_code) {
+  __mutexScope(model()->lock());
+  brand_code_ = brand_code;
+  return S_OK;
+}
+
+STDMETHODIMP App::get_clientId(BSTR* client_id) {
+  __mutexScope(model()->lock());
+  ASSERT1(client_id);
+  *client_id = client_id_.AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP App::put_clientId(BSTR client_id) {
+  __mutexScope(model()->lock());
+  client_id_ = client_id;
+  return S_OK;
+}
+
+STDMETHODIMP App::get_labels(BSTR* labels) {
+  __mutexScope(model()->lock());
+  ASSERT1(labels);
+  *labels = GetExperimentLabels().AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP App::put_labels(BSTR labels) {
+  __mutexScope(model()->lock());
+  ExperimentLabels decoded_labels;
+  if (!decoded_labels.Deserialize(labels)) {
+    return E_INVALIDARG;
+  }
+  return decoded_labels.WriteToRegistry(app_bundle_->is_machine(),
+                                        app_guid_string());
+}
+
+STDMETHODIMP App::get_referralId(BSTR* referral_id) {
+  __mutexScope(model()->lock());
+  ASSERT1(referral_id);
+  *referral_id = referral_id_.AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP App::put_referralId(BSTR referral_id) {
+  __mutexScope(model()->lock());
+  referral_id_ = referral_id;
+  return S_OK;
+}
+
+STDMETHODIMP App::get_installTimeDiffSec(UINT* install_time_diff_sec) {
+  __mutexScope(model()->lock());
+  ASSERT1(install_time_diff_sec);
+  *install_time_diff_sec = install_time_diff_sec_;
+  return S_OK;
+}
+
+STDMETHODIMP App::get_isEulaAccepted(VARIANT_BOOL* is_eula_accepted) {
+  __mutexScope(model()->lock());
+  ASSERT1(is_eula_accepted);
+  *is_eula_accepted = App::is_eula_accepted() ? VARIANT_TRUE : VARIANT_FALSE;
+  return S_OK;
+}
+
+STDMETHODIMP App::put_isEulaAccepted(VARIANT_BOOL is_eula_accepted) {
+  __mutexScope(model()->lock());
+  is_eula_accepted_ = is_eula_accepted ? TRISTATE_TRUE : TRISTATE_FALSE;
+  return S_OK;
+}
+
+STDMETHODIMP App::get_displayName(BSTR* display_name) {
+  __mutexScope(model()->lock());
+  ASSERT1(display_name);
+  *display_name = display_name_.AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP App::put_displayName(BSTR display_name) {
+  __mutexScope(model()->lock());
+  display_name_ = display_name;
+  return S_OK;
+}
+
+STDMETHODIMP App::get_browserType(UINT* browser_type) {
+  __mutexScope(model()->lock());
+  ASSERT1(browser_type);
+  *browser_type = browser_type_;
+  return S_OK;
+}
+
+STDMETHODIMP App::put_browserType(UINT browser_type) {
+  __mutexScope(model()->lock());
+  if (browser_type >= BROWSER_MAX) {
+    return E_INVALIDARG;
+  }
+  browser_type_ = static_cast<BrowserType>(browser_type);
+  return S_OK;
+}
+
+STDMETHODIMP App::get_clientInstallData(BSTR* data) {
+  __mutexScope(model()->lock());
+  ASSERT1(data);
+  *data = client_install_data_.AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP App::put_clientInstallData(BSTR data) {
+  __mutexScope(model()->lock());
+  client_install_data_ = data;
+  return S_OK;
+}
+
+STDMETHODIMP App::get_serverInstallDataIndex(BSTR* index) {
+  __mutexScope(model()->lock());
+  ASSERT1(index);
+  *index = server_install_data_index_.AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP App::put_serverInstallDataIndex(BSTR index) {
+  __mutexScope(model()->lock());
+  server_install_data_index_ = index;
+  return S_OK;
+}
+
+STDMETHODIMP App::get_usageStatsEnable(UINT* usage_stats_enable) {
+  __mutexScope(model()->lock());
+  ASSERT1(usage_stats_enable);
+  *usage_stats_enable = usage_stats_enable_;
+  return S_OK;
+}
+
+STDMETHODIMP App::put_usageStatsEnable(UINT usage_stats_enable) {
+  __mutexScope(model()->lock());
+  if (usage_stats_enable > TRISTATE_NONE) {
+    return E_INVALIDARG;
+  }
+  usage_stats_enable_ = static_cast<Tristate>(usage_stats_enable);
+  return S_OK;
+}
+
+// TODO(omaha3): Replace decisions based on state() with calls to AppState.
+// In this case, there should be a GetCurrentState() method on AppState.
+STDMETHODIMP App::get_currentState(IDispatch** current_state) {
+  __mutexScope(model()->lock());
+
+  CORE_LOG(L3, (_T("[App::get_currentState][0x%p]"), this));
+  ASSERT1(current_state);
+
+  ULONGLONG bytes_downloaded = 0;
+  ULONGLONG total_bytes_to_download = 0;
+  ULONGLONG next_download_retry_time = 0;
+  LONG download_time_remaining_ms = kCurrentStateProgressUnknown;
+  LONG install_progress_percentage = kCurrentStateProgressUnknown;
+  LONG install_time_remaining_ms = kCurrentStateProgressUnknown;
+
+  HRESULT hr = S_OK;
+  switch (state()) {
+    case STATE_INIT:
+      break;
+    case STATE_WAITING_TO_CHECK_FOR_UPDATE:
+      break;
+    case STATE_CHECKING_FOR_UPDATE:
+      break;
+    case STATE_UPDATE_AVAILABLE:
+      break;
+    case STATE_NO_UPDATE:
+      ASSERT1(error_code() == S_OK ||
+              error_code() == GOOPDATE_E_UPDATE_DEFERRED);
+      ASSERT1(!completion_message_.IsEmpty());
+      ASSERT1(completion_result_ == PingEvent::EVENT_RESULT_SUCCESS ||
+              completion_result_ == PingEvent::EVENT_RESULT_UPDATE_DEFERRED);
+      ASSERT1(installer_result_code_ == 0);
+      break;
+    case STATE_WAITING_TO_DOWNLOAD:
+    case STATE_RETRYING_DOWNLOAD:
+    case STATE_DOWNLOADING:
+    case STATE_DOWNLOAD_COMPLETE:
+    case STATE_EXTRACTING:
+    case STATE_APPLYING_DIFFERENTIAL_PATCH:
+    case STATE_READY_TO_INSTALL:
+      hr = GetDownloadProgress(&bytes_downloaded,
+                               &total_bytes_to_download,
+                               &download_time_remaining_ms,
+                               &next_download_retry_time);
+      break;
+    case STATE_WAITING_TO_INSTALL:
+      break;
+    case STATE_INSTALLING:
+      // TODO(omaha3): Obtain install_progress_percentage and
+      // install_time_remaining_ms.
+      break;
+    case STATE_INSTALL_COMPLETE:
+      install_progress_percentage = 100;
+      install_time_remaining_ms = 0;
+
+      ASSERT1(error_code() == S_OK);
+      ASSERT1(!completion_message_.IsEmpty());
+      ASSERT1(completion_result_ == PingEvent::EVENT_RESULT_SUCCESS ||
+              completion_result_ == PingEvent::EVENT_RESULT_SUCCESS_REBOOT);
+      break;
+    case STATE_PAUSED:
+      break;
+    case STATE_ERROR:
+      ASSERT1(error_code() != S_OK);
+      ASSERT1(!completion_message_.IsEmpty());
+      ASSERT1(
+          completion_result_ == PingEvent::EVENT_RESULT_ERROR ||
+          completion_result_ == PingEvent::EVENT_RESULT_CANCELLED ||
+          completion_result_ == PingEvent::EVENT_RESULT_INSTALLER_ERROR_MSI ||
+          completion_result_ == PingEvent::EVENT_RESULT_INSTALLER_ERROR_OTHER ||
+          completion_result_ == PingEvent::EVENT_RESULT_INSTALLER_ERROR_SYSTEM);
+      break;
+    default:
+      ASSERT1(false);
+      hr = E_FAIL;
+      break;
+  }
+
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CComObject<CurrentAppState>* state_object = NULL;
+  hr = CurrentAppState::Create(state(),
+                               next_version()->version(),
+                               bytes_downloaded,
+                               total_bytes_to_download,
+                               download_time_remaining_ms,
+                               next_download_retry_time,
+                               install_progress_percentage,
+                               install_time_remaining_ms,
+                               is_canceled_,
+                               error_context_.error_code,
+                               error_context_.extra_code1,
+                               completion_message_,
+                               installer_result_code_,
+                               installer_result_extra_code1_,
+                               post_install_launch_command_line_,
+                               post_install_url_,
+                               post_install_action_,
+                               &state_object);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return state_object->QueryInterface(current_state);
+}
+
+// TODO(omaha3): If some packages are already cached, there may be awkward jumps
+// in progress if we don't filter those out of bytes_total from the beginning.
+// TODO(omaha3): For now we use the package's expected_size to calculate
+// bytes_total. This may or may not be what we want. See the TODO for
+// Package::OnProgress().
+// TODO(omaha3): Maybe optimize, especially in states other than
+// STATE_DOWNLOADING.
+HRESULT App::GetDownloadProgress(uint64* bytes_downloaded,
+                                 uint64* bytes_total,
+                                 LONG* time_remaining_ms,
+                                 uint64* next_retry_time) {
+  ASSERT1(model()->IsLockedByCaller());
+
+  ASSERT1(bytes_downloaded);
+  ASSERT1(bytes_total);
+  ASSERT1(time_remaining_ms);
+  ASSERT1(next_retry_time);
+
+  *bytes_downloaded = 0;
+  *bytes_total = 0;
+  *next_retry_time = 0;
+
+  *time_remaining_ms = kCurrentStateProgressUnknown;
+
+  for (size_t i = 0; i < working_version_->GetNumberOfPackages(); ++i) {
+    const Package* package = working_version_->GetPackage(i);
+
+    const uint64 package_bytes = package->bytes_downloaded();
+    ASSERT1((*bytes_downloaded + package_bytes > *bytes_downloaded) ||
+            package_bytes == 0);
+    *bytes_downloaded += package_bytes;
+
+    const uint64 package_size = package->expected_size();
+    ASSERT1(0 < package_size);
+    ASSERT1(*bytes_total + package_size > *bytes_total);
+    *bytes_total += package_size;
+
+    LONG package_remaining_time_ms =
+        package->GetEstimatedRemainingDownloadTimeMs();
+    if (*time_remaining_ms < package_remaining_time_ms) {
+      *time_remaining_ms = package_remaining_time_ms;
+    }
+
+    uint64 package_next_retry_time = package->next_download_retry_time();
+    if (package_bytes < package_size && package_next_retry_time != 0 &&
+        (*next_retry_time == 0 || *next_retry_time > package_next_retry_time)) {
+      *next_retry_time = package_next_retry_time;
+    }
+  }
+
+  ASSERT1(*bytes_downloaded <= *bytes_total);
+
+  ASSERT1(previous_total_download_bytes_ == *bytes_total ||
+          previous_total_download_bytes_ == 0);
+  previous_total_download_bytes_ = *bytes_total;
+
+  return S_OK;
+}
+
+AppBundle* App::app_bundle() {
+  __mutexScope(model()->lock());
+  return app_bundle_;
+}
+
+const AppBundle* App::app_bundle() const {
+  __mutexScope(model()->lock());
+  return app_bundle_;
+}
+
+AppVersion* App::current_version() {
+  __mutexScope(model()->lock());
+  return current_version_.get();
+}
+
+const AppVersion* App::current_version() const {
+  __mutexScope(model()->lock());
+  return current_version_.get();
+}
+
+AppVersion* App::next_version() {
+  __mutexScope(model()->lock());
+  return next_version_.get();
+}
+
+const AppVersion* App::next_version() const {
+  __mutexScope(model()->lock());
+  return next_version_.get();
+}
+
+CString App::app_guid_string() const {
+  return GuidToString(app_guid());
+}
+
+GUID App::app_guid() const {
+  __mutexScope(model()->lock());
+  return app_guid_;
+}
+
+void App::set_app_guid(const GUID& app_guid) {
+  __mutexScope(model()->lock());
+  app_guid_ = app_guid;
+}
+
+CString App::language() const {
+  __mutexScope(model()->lock());
+  return language_;
+}
+
+bool App::is_eula_accepted() const {
+  __mutexScope(model()->lock());
+  return is_eula_accepted_ == TRISTATE_TRUE;
+}
+
+CString App::display_name() const {
+  __mutexScope(model()->lock());
+  return display_name_;
+}
+
+CurrentState App::state() const {
+  __mutexScope(model()->lock());
+  return app_state_->state();
+}
+
+bool App::is_update() const {
+  __mutexScope(model()->lock());
+  ASSERT1(current_version_->version().IsEmpty() != is_update_);
+  return is_update_;
+}
+
+bool App::has_update_available() const {
+  __mutexScope(model()->lock());
+  return has_update_available_;
+}
+
+void App::set_has_update_available(bool has_update_available) {
+  __mutexScope(model()->lock());
+  has_update_available_ = has_update_available;
+}
+
+GUID App::iid() const {
+  __mutexScope(model()->lock());
+  return iid_;
+}
+
+CString App::client_id() const {
+  __mutexScope(model()->lock());
+  return client_id_;
+}
+
+CString App::GetExperimentLabels() const {
+  __mutexScope(model()->lock());
+  ExperimentLabels stored_labels;
+  VERIFY1(SUCCEEDED(stored_labels.ReadFromRegistry(app_bundle_->is_machine(),
+                                                   app_guid_string())));
+  return stored_labels.Serialize();
+}
+
+CString App::referral_id() const {
+  __mutexScope(model()->lock());
+  return referral_id_;
+}
+
+BrowserType App::browser_type() const {
+  __mutexScope(model()->lock());
+  return browser_type_;
+}
+
+Tristate App::usage_stats_enable() const {
+  __mutexScope(model()->lock());
+  return usage_stats_enable_;
+}
+
+CString App::client_install_data() const {
+  __mutexScope(model()->lock());
+  return client_install_data_;
+}
+
+CString App::server_install_data() const {
+  __mutexScope(model()->lock());
+  return server_install_data_;
+}
+
+void App::set_server_install_data(const CString& server_install_data) {
+  __mutexScope(model()->lock());
+  server_install_data_ = server_install_data;
+}
+
+CString App::brand_code() const {
+  __mutexScope(model()->lock());
+  return brand_code_;
+}
+
+// TODO(omaha): for better accuracy, compute the value when used.
+uint32 App::install_time_diff_sec() const {
+  __mutexScope(model()->lock());
+  return install_time_diff_sec_;
+}
+
+ActiveStates App::did_run() const {
+  __mutexScope(model()->lock());
+  return did_run_;
+}
+
+int App::days_since_last_active_ping() const {
+  __mutexScope(model()->lock());
+  return days_since_last_active_ping_;
+}
+
+void App::set_days_since_last_active_ping(int days) {
+  __mutexScope(model()->lock());
+  days_since_last_active_ping_ = days;
+}
+
+int App::days_since_last_roll_call() const {
+  __mutexScope(model()->lock());
+  return days_since_last_roll_call_;
+}
+
+void App::set_days_since_last_roll_call(int days) {
+  __mutexScope(model()->lock());
+  days_since_last_roll_call_ = days;
+}
+
+CString App::ap() const {
+  __mutexScope(model()->lock());
+  return ap_;
+}
+
+CString App::tt_token() const {
+  __mutexScope(model()->lock());
+  return tt_token_;
+}
+
+CString App::server_install_data_index() const {
+  __mutexScope(model()->lock());
+  return server_install_data_index_;
+}
+
+HRESULT App::error_code() const {
+  __mutexScope(model()->lock());
+  return error_context_.error_code;
+}
+
+ErrorContext App::error_context() const {
+  __mutexScope(model()->lock());
+  return error_context_;
+}
+
+int App::installer_result_code() const {
+  __mutexScope(model()->lock());
+  return installer_result_code_;
+}
+
+int App::installer_result_extra_code1() const {
+  __mutexScope(model()->lock());
+  return installer_result_extra_code1_;
+}
+
+const PingEventVector& App::ping_events() const {
+  __mutexScope(model()->lock());
+  return ping_events_;
+}
+
+AppVersion* App::working_version() {
+  __mutexScope(model()->lock());
+  return working_version_;
+}
+
+const AppVersion* App::working_version() const {
+  __mutexScope(model()->lock());
+  return working_version_;
+}
+
+CString App::FetchAndResetLogText() {
+  __mutexScope(model()->lock());
+  CString event_log_text(event_log_text_);
+  event_log_text_.Empty();
+
+  return event_log_text;
+}
+
+void App::LogTextAppendFormat(const TCHAR* format, ...) {
+  ASSERT1(format);
+
+  CString log_string;
+
+  va_list arguments;
+  va_start(arguments, format);
+  SafeCStringFormatV(&log_string, format, arguments);
+  va_end(arguments);
+
+  __mutexScope(model()->lock());
+  SafeCStringAppendFormat(&event_log_text_, _T("App=%s, Ver=%s, %s\n"),
+                          app_guid_string().GetString(),
+                          current_version()->version().GetString(),
+                          log_string.GetString());
+}
+
+void App::AddPingEvent(const PingEventPtr& ping_event) {
+  __mutexScope(model()->lock());
+  ping_events_.push_back(ping_event);
+  CORE_LOG(L3, (_T("[ping event added][%s]"), ping_event->ToString()));
+}
+
+HRESULT App::CheckGroupPolicy() const {
+  __mutexScope(model()->lock());
+
+  bool is_auto_update = false;
+
+  if (is_update_) {
+    if (!ConfigManager::Instance()->CanUpdateApp(
+             app_guid_,
+             !app_bundle_->is_auto_update())) {
+      return GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY;
+    }
+  } else {
+    ASSERT1(!is_auto_update);
+    if (!ConfigManager::Instance()->CanInstallApp(app_guid_)) {
+      return GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY;
+    }
+  }
+
+  return S_OK;
+}
+
+void App::SetDownloadStartTime() {
+  __mutexScope(model()->lock());
+  ASSERT1(download_complete_time_ms_ == 0);
+  ASSERT1(num_bytes_downloaded_ == 0);
+
+  download_start_time_ms_ = GetCurrentMsTime();
+}
+
+void App::SetDownloadCompleteTime() {
+  __mutexScope(model()->lock());
+  download_complete_time_ms_ = GetCurrentMsTime();
+}
+
+void App::UpdateNumBytesDownloaded(uint64 num_bytes) {
+  __mutexScope(model()->lock());
+
+  CORE_LOG(L3, (_T("[RecordDownloadedBytes][new bytes downloaded: %llu]"),
+                num_bytes));
+  num_bytes_downloaded_ += num_bytes;
+}
+
+int App::GetDownloadTimeMs() const {
+  __mutexScope(model()->lock());
+
+  if (download_complete_time_ms_ < download_start_time_ms_) {
+    return 0;
+  }
+
+  return static_cast<int>(download_complete_time_ms_ - download_start_time_ms_);
+}
+
+uint64 App::num_bytes_downloaded() const {
+  __mutexScope(model()->lock());
+  return num_bytes_downloaded_;
+}
+
+uint64 App::GetPackagesTotalSize() const {
+  __mutexScope(model()->lock());
+
+  uint64 total_size = 0;
+  const size_t num_packages = working_version_->GetNumberOfPackages();
+  for (size_t i = 0; i < num_packages; ++i) {
+    total_size += working_version_->GetPackage(i)->expected_size();
+  }
+
+  return total_size;
+}
+
+//
+// State transition methods.
+// These should not do anything except acquire the lock if appropriate and call
+// the corresponding AppState method.
+//
+
+// This is the first transition. EULA acceptance must have been set by now.
+// Fail so that client developers realize quickly that something is wrong.
+// Otherwise, they might ship a client that installs apps that never update.
+void App::QueueUpdateCheck() {
+  __mutexScope(model()->lock());
+
+  ASSERT1(is_eula_accepted_ != TRISTATE_NONE);
+  if (is_eula_accepted_ == TRISTATE_NONE) {
+    CString message;
+    StringFormatter formatter(app_bundle_->display_language());
+    VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)));
+    Error(ErrorContext(GOOPDATE_E_CALL_UNEXPECTED), message);
+  }
+
+  app_state_->QueueUpdateCheck(this);
+}
+
+void App::PreUpdateCheck(xml::UpdateRequest* update_request) {
+  ASSERT1(update_request);
+  __mutexScope(model()->lock());
+  app_state_->PreUpdateCheck(this, update_request);
+}
+
+void App::PostUpdateCheck(HRESULT result,
+                          xml::UpdateResponse* update_response) {
+  ASSERT1(update_response);
+  __mutexScope(model()->lock());
+  app_state_->PostUpdateCheck(this, result, update_response);
+}
+
+void App::QueueDownload() {
+  __mutexScope(model()->lock());
+  app_state_->QueueDownload(this);
+}
+
+void App::QueueDownloadOrInstall() {
+  __mutexScope(model()->lock());
+  app_state_->QueueDownloadOrInstall(this);
+}
+
+// Does not take the lock because this is a blocking call.
+void App::Download(DownloadManagerInterface* download_manager) {
+  app_state_->Download(this, download_manager);
+}
+
+void App::Downloading() {
+  __mutexScope(model()->lock());
+  app_state_->Downloading(this);
+}
+
+void App::DownloadComplete() {
+  __mutexScope(model()->lock());
+  app_state_->DownloadComplete(this);
+}
+
+void App::MarkReadyToInstall() {
+  __mutexScope(model()->lock());
+  app_state_->MarkReadyToInstall(this);
+}
+
+void App::QueueInstall() {
+  __mutexScope(model()->lock());
+  app_state_->QueueInstall(this);
+}
+
+// Does not take the lock because this is a blocking call.
+void App::Install(InstallManagerInterface* install_manager) {
+  app_state_->Install(this, install_manager);
+}
+
+void App::Installing() {
+  __mutexScope(model()->lock());
+  app_state_->Installing(this);
+}
+
+void App::ReportInstallerComplete(const InstallerResultInfo& result_info) {
+  __mutexScope(model()->lock());
+  app_state_->ReportInstallerComplete(this,
+                                      result_info);
+}
+
+void App::Pause() {
+  __mutexScope(model()->lock());
+  return app_state_->Pause(this);
+}
+
+void App::Cancel() {
+  __mutexScope(model()->lock());
+  return app_state_->Cancel(this);
+}
+
+void App::Error(const ErrorContext& error_context, const CString& message) {
+  __mutexScope(model()->lock());
+  app_state_->Error(this, error_context, message);
+}
+
+void App::ChangeState(fsm::AppState* app_state) {
+  ASSERT1(app_state);
+  ASSERT1(model()->IsLockedByCaller());
+  CurrentState existing_state = app_state_->state();
+  app_state_.reset(app_state);
+  PingEventPtr ping_event(
+      app_state->CreatePingEvent(this, existing_state));
+  if (ping_event.get()) {
+    AddPingEvent(ping_event);
+  }
+}
+
+void App::SetError(const ErrorContext& error_context, const CString& message) {
+  ASSERT1(FAILED(error_context.error_code));
+  ASSERT1(!message.IsEmpty());
+  ASSERT1(model()->IsLockedByCaller());
+
+  error_context_      = error_context;
+  completion_message_ = message;
+
+  is_canceled_ = (error_context_.error_code == GOOPDATE_E_CANCELLED);
+  completion_result_  = is_canceled_ ? PingEvent::EVENT_RESULT_CANCELLED :
+                                       PingEvent::EVENT_RESULT_ERROR;
+}
+
+void App::SetNoUpdate(const ErrorContext& error_context,
+                      const CString& message) {
+  ASSERT1(!message.IsEmpty());
+  ASSERT1(model()->IsLockedByCaller());
+
+  error_context_      = error_context;
+  completion_message_ = message;
+
+  const bool is_deferred_update =
+      (error_context_.error_code == GOOPDATE_E_UPDATE_DEFERRED);
+  completion_result_  = is_deferred_update ?
+                        PingEvent::EVENT_RESULT_UPDATE_DEFERRED :
+                        PingEvent::EVENT_RESULT_SUCCESS;
+}
+
+void App::SetInstallerResult(const InstallerResultInfo& result_info) {
+  ASSERT1(result_info.type != INSTALLER_RESULT_UNKNOWN);
+  ASSERT1(!result_info.text.IsEmpty());
+  ASSERT1(model()->IsLockedByCaller());
+
+  completion_message_               = result_info.text;
+  installer_result_code_            = result_info.code;
+  installer_result_extra_code1_     = result_info.extra_code1;
+  post_install_launch_command_line_ =
+      result_info.post_install_launch_command_line;
+  post_install_url_                 = result_info.post_install_url;
+  post_install_action_              = result_info.post_install_action;
+
+  switch (result_info.type) {
+    case INSTALLER_RESULT_SUCCESS: {
+      error_context_.error_code = S_OK;
+
+      // TODO(omaha3): Determine whether a reboot is required. See TODO in
+      // InstallerWrapper.
+      const bool is_reboot_required = false;
+      completion_result_ = is_reboot_required ?
+                           PingEvent::EVENT_RESULT_SUCCESS_REBOOT :
+                           PingEvent::EVENT_RESULT_SUCCESS;
+
+      // We do not know whether Goopdate has succeeded because its installer has
+      // not completed.
+      if (!::IsEqualGUID(kGoopdateGuid, app_guid_)) {
+        AppManager::Instance()->PersistSuccessfulInstall(*this);
+      }
+      break;
+    }
+    case INSTALLER_RESULT_ERROR_MSI:
+      completion_result_ = PingEvent::EVENT_RESULT_INSTALLER_ERROR_MSI;
+      error_context_.error_code = GOOPDATEINSTALL_E_INSTALLER_FAILED;
+      break;
+    case INSTALLER_RESULT_ERROR_SYSTEM:
+      completion_result_ = PingEvent::EVENT_RESULT_INSTALLER_ERROR_SYSTEM;
+      error_context_.error_code = GOOPDATEINSTALL_E_INSTALLER_FAILED;
+      break;
+    case INSTALLER_RESULT_ERROR_OTHER:
+      completion_result_ = PingEvent::EVENT_RESULT_INSTALLER_ERROR_OTHER;
+      error_context_.error_code = GOOPDATEINSTALL_E_INSTALLER_FAILED;
+      break;
+    case INSTALLER_RESULT_UNKNOWN:
+    default:
+      ASSERT1(false);
+      completion_result_ = PingEvent::EVENT_RESULT_ERROR;
+      error_context_.error_code = E_FAIL;
+  }
+}
+
+CString App::GetInstallData() const {
+  __mutexScope(model()->lock());
+
+  ASSERT1(state() >= STATE_UPDATE_AVAILABLE &&
+          state() <= STATE_INSTALL_COMPLETE);
+
+  if (!client_install_data_.IsEmpty()) {
+    return client_install_data_;
+  }
+
+  return server_install_data_;
+}
+
+// IApp.
+STDMETHODIMP AppWrapper::get_currentVersion(IDispatch** current_version) {
+  __mutexScope(model()->lock());
+  return AppVersionWrapper::Create(controlling_ptr(),
+                                   wrapped_obj()->current_version(),
+                                   current_version);
+}
+
+STDMETHODIMP AppWrapper::get_nextVersion(IDispatch** next_version) {
+  __mutexScope(model()->lock());
+  return AppVersionWrapper::Create(controlling_ptr(),
+                                   wrapped_obj()->next_version(),
+                                   next_version);
+}
+
+// IApp.
+STDMETHODIMP AppWrapper::get_appId(BSTR* app_id) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_appId(app_id);
+}
+
+STDMETHODIMP AppWrapper::get_pv(BSTR* pv) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_pv(pv);
+}
+
+STDMETHODIMP AppWrapper::put_pv(BSTR pv) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_pv(pv);
+}
+
+STDMETHODIMP AppWrapper::get_language(BSTR* language) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_language(language);
+}
+
+STDMETHODIMP AppWrapper::put_language(BSTR language) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_language(language);
+}
+
+STDMETHODIMP AppWrapper::get_ap(BSTR* ap) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_ap(ap);
+}
+
+STDMETHODIMP AppWrapper::put_ap(BSTR ap) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_ap(ap);
+}
+
+STDMETHODIMP AppWrapper::get_ttToken(BSTR* tt_token) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_ttToken(tt_token);
+}
+
+STDMETHODIMP AppWrapper::put_ttToken(BSTR tt_token) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_ttToken(tt_token);
+}
+
+STDMETHODIMP AppWrapper::get_iid(BSTR* iid) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_iid(iid);
+}
+
+STDMETHODIMP AppWrapper::put_iid(BSTR iid) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_iid(iid);
+}
+
+STDMETHODIMP AppWrapper::get_brandCode(BSTR* brand_code) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_brandCode(brand_code);
+}
+
+STDMETHODIMP AppWrapper::put_brandCode(BSTR brand_code) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_brandCode(brand_code);
+}
+
+STDMETHODIMP AppWrapper::get_clientId(BSTR* client_id) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_clientId(client_id);
+}
+
+STDMETHODIMP AppWrapper::put_clientId(BSTR client_id) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_clientId(client_id);
+}
+
+STDMETHODIMP AppWrapper::get_labels(BSTR* labels) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_labels(labels);
+}
+
+STDMETHODIMP AppWrapper::put_labels(BSTR labels) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_labels(labels);
+}
+
+STDMETHODIMP AppWrapper::get_referralId(BSTR* referral_id) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_referralId(referral_id);
+}
+
+STDMETHODIMP AppWrapper::put_referralId(BSTR referral_id) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_referralId(referral_id);
+}
+
+STDMETHODIMP AppWrapper::get_installTimeDiffSec(UINT* install_time_diff_sec) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_installTimeDiffSec(install_time_diff_sec);
+}
+
+STDMETHODIMP AppWrapper::get_isEulaAccepted(VARIANT_BOOL* is_eula_accepted) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_isEulaAccepted(is_eula_accepted);
+}
+
+STDMETHODIMP AppWrapper::put_isEulaAccepted(VARIANT_BOOL is_eula_accepted) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_isEulaAccepted(is_eula_accepted);
+}
+
+STDMETHODIMP AppWrapper::get_displayName(BSTR* display_name) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_displayName(display_name);
+}
+
+STDMETHODIMP AppWrapper::put_displayName(BSTR display_name) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_displayName(display_name);
+}
+
+STDMETHODIMP AppWrapper::get_browserType(UINT* browser_type) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_browserType(browser_type);
+}
+
+STDMETHODIMP AppWrapper::put_browserType(UINT browser_type) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_browserType(browser_type);
+}
+
+STDMETHODIMP AppWrapper::get_clientInstallData(BSTR* data) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_clientInstallData(data);
+}
+
+STDMETHODIMP AppWrapper::put_clientInstallData(BSTR data) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_clientInstallData(data);
+}
+
+STDMETHODIMP AppWrapper::get_serverInstallDataIndex(BSTR* index) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_serverInstallDataIndex(index);
+}
+
+STDMETHODIMP AppWrapper::put_serverInstallDataIndex(BSTR index) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_serverInstallDataIndex(index);
+}
+
+STDMETHODIMP AppWrapper::get_usageStatsEnable(UINT* usage_stats_enable) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_usageStatsEnable(usage_stats_enable);
+}
+
+STDMETHODIMP AppWrapper::put_usageStatsEnable(UINT usage_stats_enable) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_usageStatsEnable(usage_stats_enable);
+}
+
+STDMETHODIMP AppWrapper::get_currentState(IDispatch** current_state_disp) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_currentState(current_state_disp);
+}
+
+
+// Sets app's app_state to state. Used by unit tests to set up the state to the
+// correct precondition for the test case. App friends this function, allowing
+// it to call the private member function.
+void SetAppStateForUnitTest(App* app, fsm::AppState* state) {
+  ASSERT1(app);
+  ASSERT1(state);
+  __mutexScope(app->model()->lock());
+  app->ChangeState(state);
+}
+
+}  // namespace omaha
diff --git a/goopdate/app.h b/goopdate/app.h
new file mode 100644
index 0000000..9e2dcd9
--- /dev/null
+++ b/goopdate/app.h
@@ -0,0 +1,481 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Defines the App COM object exposed by the model. App tracks two versions:
+//  - the version currently installed
+//  - the future version to be updated to, if such a version exists
+
+#ifndef OMAHA_GOOPDATE_APP_H_
+#define OMAHA_GOOPDATE_APP_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/base/browser_utils.h"
+#include "omaha/base/constants.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/ping_event.h"
+#include "omaha/goopdate/com_wrapper_creator.h"
+#include "omaha/goopdate/installer_result_info.h"
+#include "omaha/goopdate/model_object.h"
+
+namespace omaha {
+
+// Stores the error codes associated with a particular error.
+struct ErrorContext {
+  ErrorContext() : error_code(S_OK), extra_code1(0) {}
+  explicit ErrorContext(HRESULT hr, int code1 = 0)
+      : error_code(hr),
+        extra_code1(code1) {}
+
+  HRESULT error_code;
+  int     extra_code1;
+
+  // Add more extra codes here as needed.
+};
+
+class DownloadManagerInterface;
+class InstallManagerInterface;
+
+namespace fsm {
+
+class AppState;
+
+}  // namespace fsm
+
+namespace xml {
+
+class UpdateRequest;
+class UpdateResponse;
+
+}  // namespace xml
+
+class AppBundle;
+class AppVersion;
+class CurrentAppState;
+
+class App : public ModelObject {
+ public:
+  App(const GUID& app_guid, bool is_update, AppBundle* app_bundle);
+  virtual ~App();
+
+  AppVersion* current_version();
+  const AppVersion* current_version() const;
+
+  AppVersion* next_version();
+  const AppVersion* next_version() const;
+
+  AppBundle* app_bundle();
+  const AppBundle* app_bundle() const;
+
+  CString app_guid_string() const;
+
+  // TODO(omaha): refactor so that the app guid setter is only used by tests.
+  GUID app_guid() const;
+  void set_app_guid(const GUID& app_guid);
+
+  CString language() const;
+
+  bool is_eula_accepted() const;
+
+  CString display_name() const;
+
+  CurrentState state() const;
+
+  bool is_install() const { return !is_update(); }
+  bool is_update() const;
+
+  bool has_update_available() const;
+  void set_has_update_available(bool has_update_available);
+
+  GUID iid() const;
+
+  CString client_id() const;
+
+  CString GetExperimentLabels() const;
+
+  CString referral_id() const;
+
+  BrowserType browser_type() const;
+
+  Tristate usage_stats_enable() const;
+
+  CString client_install_data() const;
+
+  CString server_install_data() const;
+  void set_server_install_data(const CString& server_install_data);
+
+  CString brand_code() const;
+
+  uint32 install_time_diff_sec() const;
+
+  ActiveStates did_run() const;
+
+  int days_since_last_active_ping() const;
+  void set_days_since_last_active_ping(int days);
+
+  int days_since_last_roll_call() const;
+  void set_days_since_last_roll_call(int days);
+
+  CString ap() const;
+
+  CString tt_token() const;
+
+  CString server_install_data_index() const;
+
+  HRESULT error_code() const;
+
+  ErrorContext error_context() const;
+
+  int installer_result_code() const;
+
+  int installer_result_extra_code1() const;
+
+  const PingEventVector& ping_events() const;
+
+  AppVersion* working_version();
+  const AppVersion* working_version() const;
+
+  // IApp.
+  STDMETHOD(get_appId)(BSTR* app_id);
+
+  STDMETHOD(get_pv)(BSTR* pv);
+  STDMETHOD(put_pv)(BSTR pv);
+
+  STDMETHOD(get_language)(BSTR* language);
+  STDMETHOD(put_language)(BSTR language);
+
+  STDMETHOD(get_ap)(BSTR* ap);
+  STDMETHOD(put_ap)(BSTR ap);
+
+  STDMETHOD(get_ttToken)(BSTR* tt_token);
+  STDMETHOD(put_ttToken)(BSTR tt_token);
+
+  STDMETHOD(get_iid)(BSTR* iid);
+  STDMETHOD(put_iid)(BSTR iid);
+
+  STDMETHOD(get_brandCode)(BSTR* brand_code);
+  STDMETHOD(put_brandCode)(BSTR brand_code);
+
+  STDMETHOD(get_clientId)(BSTR* client_id);
+  STDMETHOD(put_clientId)(BSTR client_id);
+
+  STDMETHOD(get_labels)(BSTR* labels);
+  STDMETHOD(put_labels)(BSTR labels);
+
+  STDMETHOD(get_referralId)(BSTR* referral_id);
+  STDMETHOD(put_referralId)(BSTR referral_id);
+
+  STDMETHOD(get_installTimeDiffSec)(UINT* install_time_diff_sec);
+
+  STDMETHOD(get_isEulaAccepted)(VARIANT_BOOL* is_eula_accepted);
+  STDMETHOD(put_isEulaAccepted)(VARIANT_BOOL is_eula_accepted);
+
+  STDMETHOD(get_displayName)(BSTR* display_name);
+  STDMETHOD(put_displayName)(BSTR display_name);
+
+  STDMETHOD(get_browserType)(UINT* browser_type);
+  STDMETHOD(put_browserType)(UINT browser_type);
+
+  STDMETHOD(get_clientInstallData)(BSTR* data);
+  STDMETHOD(put_clientInstallData)(BSTR data);
+
+  STDMETHOD(get_serverInstallDataIndex)(BSTR* index);
+  STDMETHOD(put_serverInstallDataIndex)(BSTR index);
+
+  STDMETHOD(get_usageStatsEnable)(UINT* usage_stats_enable);
+  STDMETHOD(put_usageStatsEnable)(UINT usage_stats_enable);
+
+  STDMETHOD(get_currentState)(IDispatch** current_state);
+
+  // Sets the error context and the completion message.
+  void SetNoUpdate(const ErrorContext& error_context, const CString& message);
+  void SetError(const ErrorContext& error_context, const CString& message);
+
+  // Records the details of the installer success or failure.
+  void SetInstallerResult(const InstallerResultInfo& result_info);
+
+  // Gets the appropriate installer data regardless of the source. Only valid
+  // in STATE_UPDATE_AVAILABLE and later.
+  CString GetInstallData() const;
+
+
+  //
+  // App state machine transition conditions. These functions make
+  // the application change states.
+  //
+
+  // Sets the app to Waiting To Check.
+  void QueueUpdateCheck();
+
+  // Adds the app to the update request.
+  void PreUpdateCheck(xml::UpdateRequest* update_request);
+
+  // Processes the update response for the app.
+  void PostUpdateCheck(HRESULT result, xml::UpdateResponse* update_response);
+
+  // Sets the app to Waiting To Download.
+  void QueueDownload();
+
+  // Sets the app to Waiting To Download or Waiting To Install depending on the
+  // current state.
+  void QueueDownloadOrInstall();
+
+  // Initiates download of the app if necessary.
+  void Download(DownloadManagerInterface* download_manager);
+
+  // Reports that the download is in progress. May be called multiple times.
+  void Downloading();
+
+  // Reports that all packages have been downloaded.
+  void DownloadComplete();
+
+  // Sets the app to Ready To install.
+  void MarkReadyToInstall();
+
+  // Sets the app to Waiting To Install.
+  void QueueInstall();
+
+  // Initiates installation of the app.
+  void Install(InstallManagerInterface* install_manager);
+
+  // Reports that the install is in progress. May be called multiple times.
+  void Installing();
+
+  // Reports that the app installer has completed. Can be success or failure.
+  void ReportInstallerComplete(const InstallerResultInfo& result_info);
+
+  // TODO(omaha3): What does this pause?
+  void Pause();
+
+  // Cancels the app install.
+  void Cancel();
+
+  // Stops installation in the Error state.
+  void Error(const ErrorContext& error_context, const CString& message);
+
+  // For logging support.
+  CString FetchAndResetLogText();
+  void LogTextAppendFormat(const TCHAR* format, ...);
+
+  // Adds an event to the app's ping, which is sent when the bundle is
+  // destroyed. In most cases, pings should be added in
+  // AppState::CreatePingEvent().
+  void AddPingEvent(const PingEventPtr& ping_event);
+
+  // Returns an error if update/install, as determined by is_update_, is
+  // disabled by Group Policy.
+  HRESULT CheckGroupPolicy() const;
+
+  // Sets current time as download start time.
+  void SetDownloadStartTime();
+
+  // Sets current time as download complete time.
+  void SetDownloadCompleteTime();
+
+  // Updates num bytes downloaded by adding newly downloaded bytes.
+  void UpdateNumBytesDownloaded(uint64 num_bytes);
+
+  // Returns how long it takes for the download manager to download this app.
+  int GetDownloadTimeMs() const;
+
+  // Returns how many bytes are actually downloaded.
+  uint64 num_bytes_downloaded() const;
+
+  // Returns the size sum of all packages for this app.
+  uint64 GetPackagesTotalSize() const;
+
+ private:
+  // TODO(omaha): accessing directly the data members bypasses locking. Review
+  // the places where members are accessed by friends and check the caller locks
+  // before going directly for the private members.
+  friend class AppManager;
+  friend class AppManagerTestBase;
+
+  // TODO(omaha3): Maybe use a mock in these tests instead.
+  friend class InstallManagerInstallAppTest;
+
+  friend class fsm::AppState;
+
+  // Sets the app state for unit testing.
+  friend void SetAppStateForUnitTest(App* app, fsm::AppState* state);
+
+  HRESULT GetDownloadProgress(uint64* bytes_downloaded,
+                              uint64* bytes_total,
+                              LONG* time_remaining_ms,
+                              uint64* next_retry_time);
+
+  void ChangeState(fsm::AppState* app_state);
+
+  scoped_ptr<fsm::AppState> app_state_;
+
+  scoped_ptr<AppVersion> current_version_;
+  scoped_ptr<AppVersion> next_version_;
+
+  // Alias to the version of the app that is being modified.
+  AppVersion* working_version_;
+
+  PingEventVector ping_events_;
+
+  CString event_log_text_;
+
+  // Weak reference to the containing bundle.
+  AppBundle* app_bundle_;
+
+  // True if the app is in the update scenario.
+  const bool is_update_;
+
+  // True if the server responded that an update is available for the app.
+  // This can happen in both install and update cases.
+  bool has_update_available_;
+
+  GUID app_guid_;
+  CString pv_;
+
+  // These values are stored in Clients. language can be in ClientState too.
+  CString language_;
+  CString display_name_;
+
+  // These values are stored in ClientState.
+  CString ap_;
+  CString tt_token_;
+  GUID iid_;
+  CString brand_code_;
+  CString client_id_;
+  // TODO(omaha3): Rename member and registry value to match the COM property.
+  CString referral_id_;
+  uint32 install_time_diff_sec_;
+  Tristate is_eula_accepted_;
+  BrowserType browser_type_;
+  int days_since_last_active_ping_;
+  int days_since_last_roll_call_;
+
+  // This value is stored in ClientState but not currently populated from there.
+  Tristate usage_stats_enable_;
+
+  // This value is stored by the clients in one of several registry locations.
+  ActiveStates did_run_;
+
+  // This value is not currently persisted in the registry.
+  CString server_install_data_index_;
+
+  // Contains the installer data string specified by the client (usually
+  // contained in /appdata).
+  CString client_install_data_;
+
+  // Contains the installer data string returned by the server for
+  // server_install_data_index_.
+  CString server_install_data_;
+
+  bool is_canceled_;
+  ErrorContext error_context_;
+  CString completion_message_;
+  PingEvent::Results completion_result_;
+  int installer_result_code_;
+  int installer_result_extra_code1_;
+  CString post_install_launch_command_line_;
+  CString post_install_url_;
+  PostInstallAction post_install_action_;
+
+  uint64 previous_total_download_bytes_;
+
+  // Values for download metrics.
+  uint64 download_start_time_ms_;
+  uint64 download_complete_time_ms_;
+  uint64 num_bytes_downloaded_;
+
+  DISALLOW_COPY_AND_ASSIGN(App);
+};
+
+class ATL_NO_VTABLE AppWrapper
+    : public ComWrapper<AppWrapper, App>,
+      public IDispatchImpl<IApp,
+                          &__uuidof(IApp),
+                          &CAtlModule::m_libid,
+                          kMajorTypeLibVersion,
+                          kMinorTypeLibVersion> {
+ public:
+  // IApp.
+  STDMETHOD(get_currentVersion)(IDispatch** current);
+  STDMETHOD(get_nextVersion)(IDispatch** next);
+  STDMETHOD(get_currentState)(IDispatch** current_state_disp);
+
+  STDMETHOD(get_appId)(BSTR* app_id);
+
+  STDMETHOD(get_pv)(BSTR* pv);
+  STDMETHOD(put_pv)(BSTR pv);
+
+  STDMETHOD(get_language)(BSTR* language);
+  STDMETHOD(put_language)(BSTR language);
+
+  STDMETHOD(get_ap)(BSTR* ap);
+  STDMETHOD(put_ap)(BSTR ap);
+
+  STDMETHOD(get_ttToken)(BSTR* tt_token);
+  STDMETHOD(put_ttToken)(BSTR tt_token);
+
+  STDMETHOD(get_iid)(BSTR* iid);
+  STDMETHOD(put_iid)(BSTR iid);
+
+  STDMETHOD(get_brandCode)(BSTR* brand_code);
+  STDMETHOD(put_brandCode)(BSTR brand_code);
+
+  STDMETHOD(get_clientId)(BSTR* client_id);
+  STDMETHOD(put_clientId)(BSTR client_id);
+
+  STDMETHOD(get_labels)(BSTR* labels);
+  STDMETHOD(put_labels)(BSTR labels);
+
+  STDMETHOD(get_referralId)(BSTR* referral_id);
+  STDMETHOD(put_referralId)(BSTR referral_id);
+
+  STDMETHOD(get_installTimeDiffSec)(UINT* install_time_diff_sec);
+
+  STDMETHOD(get_isEulaAccepted)(VARIANT_BOOL* is_eula_accepted);
+  STDMETHOD(put_isEulaAccepted)(VARIANT_BOOL is_eula_accepted);
+
+  STDMETHOD(get_displayName)(BSTR* display_name);
+  STDMETHOD(put_displayName)(BSTR display_name);
+
+  STDMETHOD(get_browserType)(UINT* browser_type);
+  STDMETHOD(put_browserType)(UINT browser_type);
+
+  STDMETHOD(get_clientInstallData)(BSTR* data);
+  STDMETHOD(put_clientInstallData)(BSTR data);
+
+  STDMETHOD(get_serverInstallDataIndex)(BSTR* index);
+  STDMETHOD(put_serverInstallDataIndex)(BSTR index);
+
+  STDMETHOD(get_usageStatsEnable)(UINT* usage_stats_enable);
+  STDMETHOD(put_usageStatsEnable)(UINT usage_stats_enable);
+
+ protected:
+  AppWrapper() {}
+  virtual ~AppWrapper() {}
+
+  BEGIN_COM_MAP(AppWrapper)
+    COM_INTERFACE_ENTRY(IApp)
+    COM_INTERFACE_ENTRY(IDispatch)
+  END_COM_MAP()
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppWrapper);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_H_
diff --git a/goopdate/app_bundle.cc b/goopdate/app_bundle.cc
new file mode 100644
index 0000000..bcdc6fe
--- /dev/null
+++ b/goopdate/app_bundle.cc
@@ -0,0 +1,896 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include"omaha/goopdate/app_bundle.h"
+#include <atlsafe.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_impersonation.h"
+#include "omaha/base/user_rights.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/lang.h"
+#include "omaha/common/ping.h"
+#include "omaha/common/update_request.h"
+#include "omaha/common/update_response.h"
+#include "omaha/common/web_services_client.h"
+#include "omaha/goopdate/app_bundle_state_init.h"
+#include "omaha/goopdate/app_manager.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/update_request_utils.h"
+
+namespace omaha {
+
+namespace {
+
+const TCHAR* const kDefaultInstallSource = _T("unknown");
+
+}  // namespace
+
+AppBundle::AppBundle(bool is_machine, Model* model)
+    : ModelObject(model),
+      install_source_(kDefaultInstallSource),
+      is_machine_(is_machine),
+      is_auto_update_(false),
+      priority_(INSTALL_PRIORITY_HIGH),
+      parent_hwnd_(NULL),
+      user_work_item_(NULL),
+      display_language_(lang::GetDefaultLanguage(is_machine)) {
+  CORE_LOG(L3, (_T("[AppBundle::AppBundle][0x%p]"), this));
+  app_bundle_state_.reset(new fsm::AppBundleStateInit);
+}
+
+AppBundle::~AppBundle() {
+  CORE_LOG(L3, (_T("[AppBundle::~AppBundle][0x%p]"), this));
+
+  // Destruction of this object is not serialized. The lifetime of AppBundle
+  // objects is controlled by the client and multiple objects can destruct at
+  // the same time.
+  ASSERT1(!model()->IsLockedByCaller());
+
+  HRESULT hr = SendPingEvents();
+  CORE_LOG(L3, (_T("[SendPingEvents returned 0x%x]"), hr));
+
+  __mutexScope(model()->lock());
+  for (size_t i = 0; i < apps_.size(); ++i) {
+    delete apps_[i];
+  }
+
+  // If the thread running this AppBundle does not exit before the
+  // NetworkConfigManager::DeleteInstance() happens in GoopdateImpl::Main, the
+  // update_check_client_ destructor will crash. Resetting here explicitly.
+  update_check_client_.reset();
+
+  // Garbage-collect everything that has expired, including this object.
+  // The model holds weak references to AppBundle objects. Those weak
+  // references expire before the destructor for the object runs. Therefore, it
+  // is not possible to associate this object with any of the weak references
+  // in the model. Those weak references must be garbage collected.
+  model()->CleanupExpiredAppBundles();
+}
+
+ControllingPtr AppBundle::controlling_ptr() {
+  __mutexScope(model()->lock());
+  return shared_from_this();
+}
+
+bool AppBundle::is_pending_non_blocking_call() const {
+  __mutexScope(model()->lock());
+  return user_work_item_ != NULL;
+}
+
+void AppBundle::set_user_work_item(UserWorkItem* user_work_item) {
+  ASSERT(user_work_item, (_T("Use CompleteAsyncCall() instead.")));
+  __mutexScope(model()->lock());
+
+  user_work_item_ = user_work_item;
+}
+
+HANDLE AppBundle::impersonation_token() const {
+  __mutexScope(model()->lock());
+  return alt_impersonation_token_.GetHandle() ?
+         alt_impersonation_token_.GetHandle() :
+         impersonation_token_.GetHandle();
+}
+
+HANDLE AppBundle::primary_token() const {
+  __mutexScope(model()->lock());
+  return alt_primary_token_.GetHandle() ? alt_primary_token_.GetHandle() :
+                                          primary_token_.GetHandle();
+}
+
+HRESULT AppBundle::CaptureCallerImpersonationToken() {
+  __mutexScope(model()->lock());
+
+  if (!is_machine_) {
+    return S_OK;
+  }
+
+  if (impersonation_token_.GetHandle()) {
+    ::CloseHandle(impersonation_token_.Detach());
+  }
+
+  HRESULT hr = UserRights::GetCallerToken(&impersonation_token_);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CaptureCallerImpersonationToken failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT AppBundle::CaptureCallerPrimaryToken() {
+  __mutexScope(model()->lock());
+
+  if (!is_machine_) {
+    return S_OK;
+  }
+
+  ASSERT1(impersonation_token_.GetHandle());
+  if (!UserRights::TokenIsAdmin(impersonation_token_.GetHandle())) {
+    ASSERT1(false);
+    return E_UNEXPECTED;
+  }
+
+  if (primary_token_.GetHandle()) {
+    ::CloseHandle(primary_token_.Detach());
+  }
+
+  if (!impersonation_token_.CreatePrimaryToken(&primary_token_)) {
+    HRESULT hr = HRESULTFromLastError();
+    CORE_LOG(LE, (_T("[CreatePrimaryToken failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+size_t AppBundle::GetNumberOfApps() const {
+  __mutexScope(model()->lock());
+  return apps_.size();
+}
+
+App* AppBundle::GetApp(size_t index) {
+  __mutexScope(model()->lock());
+
+  if (index >= GetNumberOfApps()) {
+    ASSERT1(false);
+    return NULL;
+  }
+
+  App* app = apps_[index];
+  ASSERT1(app);
+  return app;
+}
+
+CString AppBundle::FetchAndResetLogText() {
+  __mutexScope(model()->lock());
+
+  CString event_log_text;
+  for (size_t i = 0; i < apps_.size(); ++i) {
+    event_log_text += apps_[i]->FetchAndResetLogText();
+  }
+
+  return event_log_text;
+}
+
+HRESULT AppBundle::SendPingEvents() {
+  CORE_LOG(L3, (_T("[AppBundle::SendPingEvents]")));
+
+  scoped_impersonation impersonate_user(impersonation_token());
+
+  if (!ConfigManager::Instance()->CanUseNetwork(is_machine_)) {
+    CORE_LOG(L1, (_T("[Ping not sent because network use prohibited]")));
+    return S_OK;
+  }
+
+  Ping ping(is_machine_, session_id_, install_source_);
+
+  __mutexBlock(model()->lock()) {
+    for (size_t i = 0; i != apps_.size(); ++i) {
+      if (apps_[i]->is_eula_accepted()) {
+        ping.BuildRequest(apps_[i], false);
+      }
+    }
+
+    for (size_t i = 0; i != uninstalled_apps_.size(); ++i) {
+      if (uninstalled_apps_[i]->is_eula_accepted()) {
+        ping.BuildRequest(uninstalled_apps_[i], false);
+      }
+    }
+  }
+
+  CORE_LOG(L3, (_T("[AppBundle::SendPingEvents][sending ping events]")
+                _T("[%d uninstalled apps]"), uninstalled_apps_.size()));
+
+  // TODO(Omaha): Add sample to metric_ping_succeeded_ms or
+  // metric_ping_failed_ms based on the result of Send().
+  return ping.Send(false);
+}
+
+// IAppBundle.
+STDMETHODIMP AppBundle::get_displayName(BSTR* display_name) {
+  ASSERT1(display_name);
+  __mutexScope(model()->lock());
+  *display_name = display_name_.AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP AppBundle::put_displayName(BSTR display_name) {
+  __mutexScope(model()->lock());
+  display_name_ = display_name;
+  return S_OK;
+}
+
+STDMETHODIMP AppBundle::get_installSource(BSTR* install_source) {
+  ASSERT1(install_source);
+  __mutexScope(model()->lock());
+  *install_source = install_source_.AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP AppBundle::put_installSource(BSTR install_source) {
+  __mutexScope(model()->lock());
+  install_source_ = install_source;
+  return S_OK;
+}
+
+STDMETHODIMP AppBundle::get_originURL(BSTR* origin_url) {
+  ASSERT1(origin_url);
+  __mutexScope(model()->lock());
+  *origin_url = origin_url_.AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP AppBundle::put_originURL(BSTR origin_url) {
+  __mutexScope(model()->lock());
+  origin_url_ = origin_url;
+  return S_OK;
+}
+
+STDMETHODIMP AppBundle::get_offlineDirectory(BSTR* offline_dir) {
+  ASSERT1(offline_dir);
+  __mutexScope(model()->lock());
+  *offline_dir = offline_dir_.AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP AppBundle::put_offlineDirectory(BSTR offline_dir) {
+  CORE_LOG(L3, (_T("[AppBundle::put_offlineDirectory][%s]"), offline_dir));
+  __mutexScope(model()->lock());
+  offline_dir_ = offline_dir;
+  return S_OK;
+}
+
+STDMETHODIMP AppBundle::get_sessionId(BSTR* session_id) {
+  ASSERT1(session_id);
+  __mutexScope(model()->lock());
+  *session_id = session_id_.AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP AppBundle::put_sessionId(BSTR session_id) {
+  CORE_LOG(L3, (_T("[AppBundle::put_sessionId][%s]"), session_id));
+  __mutexScope(model()->lock());
+  return app_bundle_state_->put_sessionId(this, session_id);
+}
+
+STDMETHODIMP AppBundle::get_priority(long* priority) {  // NOLINT
+  ASSERT1(priority);
+  __mutexScope(model()->lock());
+  *priority = priority_;
+  return S_OK;
+}
+
+STDMETHODIMP AppBundle::put_priority(long priority) {  // NOLINT
+  if ((priority < INSTALL_PRIORITY_LOW) || (priority > INSTALL_PRIORITY_HIGH)) {
+    return E_INVALIDARG;
+  }
+  __mutexScope(model()->lock());
+  priority_ = priority;
+  return S_OK;
+}
+
+STDMETHODIMP AppBundle::put_altTokens(ULONG_PTR impersonation_token,
+                                      ULONG_PTR primary_token,
+                                      DWORD caller_proc_id) {
+  ASSERT1(impersonation_token);
+  ASSERT1(primary_token);
+  ASSERT1(caller_proc_id);
+  __mutexScope(model()->lock());
+
+  return app_bundle_state_->put_altTokens(this,
+                                          impersonation_token,
+                                          primary_token,
+                                          caller_proc_id);
+}
+
+STDMETHODIMP AppBundle::put_parentHWND(ULONG_PTR hwnd) {
+  CORE_LOG(L3, (_T("[AppBundle::put_parentHWND][0x%x]"), hwnd));
+
+  __mutexScope(model()->lock());
+  parent_hwnd_ = reinterpret_cast<HWND>(hwnd);
+  update_check_client_->set_proxy_auth_config(GetProxyAuthConfig());
+  return S_OK;
+}
+
+CString AppBundle::display_language() const {
+  __mutexScope(model()->lock());
+  return display_language_;
+}
+
+STDMETHODIMP AppBundle::get_displayLanguage(BSTR* language) {
+  ASSERT1(language);
+  __mutexScope(model()->lock());
+  *language = display_language_.AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP AppBundle::put_displayLanguage(BSTR language) {
+  __mutexScope(model()->lock());
+  if (::SysStringLen(language) == 0) {
+    return E_INVALIDARG;
+  }
+
+  if (!lang::IsLanguageSupported(language)) {
+    return E_INVALIDARG;
+  }
+
+  display_language_ = language;
+  return S_OK;
+}
+
+bool AppBundle::is_machine() const {
+  __mutexScope(model()->lock());
+  return is_machine_;
+}
+
+bool AppBundle::is_auto_update() const {
+  __mutexScope(model()->lock());
+  return is_auto_update_;
+}
+
+void AppBundle::set_is_auto_update(bool is_auto_update) {
+  __mutexScope(model()->lock());
+  is_auto_update_ = is_auto_update;
+}
+
+bool AppBundle::is_offline_install() const {
+  __mutexScope(model()->lock());
+  return !offline_dir_.IsEmpty();
+}
+
+const CString& AppBundle::offline_dir() const {
+  __mutexScope(model()->lock());
+  return offline_dir_;
+}
+
+const CString& AppBundle::session_id() const {
+  __mutexScope(model()->lock());
+  return session_id_;
+}
+
+int AppBundle::priority() const {
+  __mutexScope(model()->lock());
+  return priority_;
+}
+
+ProxyAuthConfig AppBundle::GetProxyAuthConfig() const {
+  __mutexScope(model()->lock());
+  return ProxyAuthConfig(parent_hwnd_, display_name_);
+}
+
+STDMETHODIMP AppBundle::initialize() {
+  __mutexScope(model()->lock());
+
+  // Ensure that clients that run as Local System were designed with alt tokens
+  // in mind. The alt tokens might not always be a different user, but at least
+  // the client considered the need to set the alt tokens.
+  // TODO(omaha): The /ua process should not need to call put_altTokens()
+  // when there is no logged in user. This may be causing issues on Windows 7.
+  bool alt_tokens_set(alt_impersonation_token_.GetHandle() &&
+                      alt_primary_token_.GetHandle());
+  ASSERT1(!is_machine_ ||
+          alt_tokens_set ||
+          !UserRights::VerifyCallerIsSystem());
+
+  return app_bundle_state_->Initialize(this);
+}
+
+// App is created with is_update=false because the caller is not using
+// information about any installed app. It is either a new or over-install.
+STDMETHODIMP AppBundle::createApp(BSTR app_id, App** app) {
+  CORE_LOG(L1, (_T("[AppBundle::createApp][%s][0x%p]"), app_id, this));
+  ASSERT1(app_id);
+  ASSERT1(app);
+
+  __mutexScope(model()->lock());
+
+  scoped_impersonation impersonate_user(impersonation_token());
+  HRESULT hr = impersonate_user.result();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return app_bundle_state_->CreateApp(this, app_id, app);
+}
+
+STDMETHODIMP AppBundle::createInstalledApp(BSTR app_id, App** app) {
+  CORE_LOG(L1, (_T("[AppBundle::createInstalledApp][%s][0x%p]"), app_id, this));
+  ASSERT1(app);
+
+  __mutexScope(model()->lock());
+
+
+  scoped_impersonation impersonate_user(impersonation_token());
+  HRESULT hr = impersonate_user.result();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return app_bundle_state_->CreateInstalledApp(this, app_id, app);
+}
+
+STDMETHODIMP AppBundle::createAllInstalledApps() {
+  CORE_LOG(L1, (_T("[AppBundle::createAllInstalledApps][0x%p]"), this));
+
+  __mutexScope(model()->lock());
+
+  scoped_impersonation impersonate_user(impersonation_token());
+  HRESULT hr = impersonate_user.result();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return app_bundle_state_->CreateAllInstalledApps(this);
+}
+
+STDMETHODIMP AppBundle::get_Count(long* count) {  // NOLINT
+  ASSERT1(count);
+
+  __mutexScope(model()->lock());
+
+  *count = apps_.size();
+
+  return S_OK;
+}
+
+STDMETHODIMP AppBundle::get_Item(long index, App** app) {  // NOLINT
+  ASSERT1(app);
+
+  __mutexScope(model()->lock());
+
+  if (index < 0 || static_cast<size_t>(index) >= apps_.size()) {
+    return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
+  }
+
+  *app = apps_[index];
+  return S_OK;
+}
+
+WebServicesClientInterface* AppBundle::update_check_client() {
+  __mutexScope(model()->lock());
+  ASSERT1(update_check_client_.get());
+  return update_check_client_.get();
+}
+
+STDMETHODIMP AppBundle::checkForUpdate() {
+  CORE_LOG(L1, (_T("[AppBundle::checkForUpdate][0x%p]"), this));
+
+  __mutexScope(model()->lock());
+
+  scoped_impersonation impersonate_user(impersonation_token());
+  HRESULT hr = impersonate_user.result();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return app_bundle_state_->CheckForUpdate(this);
+}
+
+STDMETHODIMP AppBundle::download() {
+  CORE_LOG(L1, (_T("[AppBundle::download][0x%p]"), this));
+
+  __mutexScope(model()->lock());
+
+  scoped_impersonation impersonate_user(impersonation_token());
+  HRESULT hr = impersonate_user.result();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return app_bundle_state_->Download(this);
+}
+
+// Captures the primary token since it is the only function that needs it, and
+// to prevent any scenarios where one user sets up a bundle and another installs
+// the app(s) with the other user's credentials.
+STDMETHODIMP AppBundle::install() {
+  CORE_LOG(L1, (_T("[AppBundle::install][0x%p]"), this));
+
+  __mutexScope(model()->lock());
+
+  HRESULT hr = CaptureCallerPrimaryToken();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  scoped_impersonation impersonate_user(impersonation_token());
+  hr = impersonate_user.result();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return app_bundle_state_->Install(this);
+}
+
+STDMETHODIMP AppBundle::updateAllApps() {
+  CORE_LOG(L1, (_T("[AppBundle::updateAllApps][0x%p]"), this));
+
+  __mutexScope(model()->lock());
+
+  scoped_impersonation impersonate_user(impersonation_token());
+  HRESULT hr = impersonate_user.result();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return app_bundle_state_->UpdateAllApps(this);
+}
+
+STDMETHODIMP AppBundle::stop() {
+  CORE_LOG(L1, (_T("[AppBundle::stop][0x%p]"), this));
+
+  __mutexScope(model()->lock());
+
+  return app_bundle_state_->Stop(this);
+}
+
+STDMETHODIMP AppBundle::pause() {
+  CORE_LOG(L1, (_T("[AppBundle::pause][0x%p]"), this));
+
+  __mutexScope(model()->lock());
+
+  return app_bundle_state_->Pause(this);
+}
+
+STDMETHODIMP AppBundle::resume() {
+  CORE_LOG(L1, (_T("[AppBundle::resume][0x%p]"), this));
+
+  __mutexScope(model()->lock());
+
+  return app_bundle_state_->Resume(this);
+}
+
+STDMETHODIMP AppBundle::isBusy(VARIANT_BOOL* is_busy) {
+  CORE_LOG(L3, (_T("[AppBundle::isBusy][0x%p]"), this));
+  ASSERT1(is_busy);
+
+  __mutexScope(model()->lock());
+
+  *is_busy = IsBusy() ? VARIANT_TRUE : VARIANT_FALSE;
+  return S_OK;
+}
+
+STDMETHODIMP AppBundle::downloadPackage(BSTR app_id, BSTR package_name) {
+  CORE_LOG(L1, (_T("[AppBundle::downloadPackage][%s][%s]"),
+      app_id, package_name));
+
+  __mutexScope(model()->lock());
+
+  scoped_impersonation impersonate_user(impersonation_token());
+  HRESULT hr = impersonate_user.result();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return app_bundle_state_->DownloadPackage(this, app_id, package_name);
+}
+
+// TODO(omaha3): May need to provide aggregate status. See TODO in IDL file.
+STDMETHODIMP AppBundle::get_currentState(VARIANT* current_state) {
+  CORE_LOG(L3, (_T("[AppBundle::get_currentState][0x%p]"), this));
+  ASSERT1(current_state);
+  UNREFERENCED_PARAMETER(current_state);
+  ASSERT(false, (_T("Not implemented. Should not call at this time.")));
+  return E_NOTIMPL;
+}
+
+// This function is only called internal to the COM server and affects a
+// separate vector of Apps, so it can be called in any state.
+// It assumes all calls have a unique app_id.
+HRESULT AppBundle::CreateUninstalledApp(const CString& app_id, App** app) {
+  CORE_LOG(L1, (_T("[AppBundle::CreateUninstalledApp][%s][0x%p]"),
+                app_id, this));
+  ASSERT1(app);
+
+  __mutexScope(model()->lock());
+
+  GUID app_guid = {0};
+  HRESULT hr = StringToGuidSafe(app_id, &app_guid);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[invalid app id][%s]"), app_id));
+    return hr;
+  }
+
+  scoped_ptr<App> local_app(new App(app_guid, true, this));
+
+  hr = AppManager::Instance()->ReadUninstalledAppPersistentData(
+           local_app.get());
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[ReadUninstalledAppPersistentData failed][0x%x][%s]"),
+                  hr, app_id));
+    return hr;
+  }
+
+  uninstalled_apps_.push_back(local_app.get());
+
+  *app = local_app.release();
+  return S_OK;
+}
+
+void AppBundle::CompleteAsyncCall() {
+  __mutexScope(model()->lock());
+
+  ASSERT1(is_pending_non_blocking_call());
+
+  VERIFY1(SUCCEEDED(app_bundle_state_->CompleteAsyncCall(this)));
+
+  user_work_item_ = NULL;
+}
+
+bool AppBundle::IsBusy() const {
+  __mutexScope(model()->lock());
+  const bool is_busy = app_bundle_state_->IsBusy();
+  CORE_LOG(L3, (_T("[AppBundle::isBusy returned][0x%p][%u]"), this, is_busy));
+  return is_busy;
+}
+
+void AppBundle::ChangeState(fsm::AppBundleState* app_bundle_state) {
+  ASSERT1(app_bundle_state);
+  ASSERT1(model()->IsLockedByCaller());
+
+  app_bundle_state_.reset(app_bundle_state);
+}
+
+
+//
+// AppBundleWrapper implementation.
+//
+
+AppBundleWrapper::AppBundleWrapper() {
+  CORE_LOG(L3, (_T("[AppBundleWrapper::AppBundleWrapper][0x%p]"), this));
+}
+
+AppBundleWrapper::~AppBundleWrapper() {
+  CORE_LOG(L3, (_T("[AppBundleWrapper::~AppBundleWrapper][0x%p]"), this));
+}
+
+//
+// IAppBundle.
+//
+
+STDMETHODIMP AppBundleWrapper::get_displayName(BSTR* display_name) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_displayName(display_name);
+}
+
+STDMETHODIMP AppBundleWrapper::put_displayName(BSTR display_name) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_displayName(display_name);
+}
+
+STDMETHODIMP AppBundleWrapper::get_installSource(BSTR* install_source) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_installSource(install_source);
+}
+
+STDMETHODIMP AppBundleWrapper::put_installSource(BSTR install_source) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_installSource(install_source);
+}
+
+STDMETHODIMP AppBundleWrapper::get_originURL(BSTR* origin_url) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_originURL(origin_url);
+}
+
+STDMETHODIMP AppBundleWrapper::put_originURL(BSTR origin_url) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_originURL(origin_url);
+}
+
+STDMETHODIMP AppBundleWrapper::get_offlineDirectory(BSTR* offline_dir) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_offlineDirectory(offline_dir);
+}
+
+STDMETHODIMP AppBundleWrapper::put_offlineDirectory(BSTR offline_dir) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_offlineDirectory(offline_dir);
+}
+
+STDMETHODIMP AppBundleWrapper::get_sessionId(BSTR* session_id) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_sessionId(session_id);
+}
+
+STDMETHODIMP AppBundleWrapper::put_sessionId(BSTR session_id) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_sessionId(session_id);
+}
+
+STDMETHODIMP AppBundleWrapper::get_priority(long* priority) {  // NOLINT
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_priority(priority);
+}
+
+STDMETHODIMP AppBundleWrapper::put_priority(long priority) {  // NOLINT
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_priority(priority);
+}
+
+STDMETHODIMP AppBundleWrapper::put_altTokens(ULONG_PTR impersonation_token,
+                                             ULONG_PTR primary_token,
+                                             DWORD caller_proc_id) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_altTokens(impersonation_token,
+                                      primary_token,
+                                      caller_proc_id);
+}
+
+STDMETHODIMP AppBundleWrapper::put_parentHWND(ULONG_PTR hwnd) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_parentHWND(hwnd);
+}
+
+STDMETHODIMP AppBundleWrapper::get_displayLanguage(BSTR* language) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_displayLanguage(language);
+}
+STDMETHODIMP AppBundleWrapper::put_displayLanguage(BSTR language) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->put_displayLanguage(language);
+}
+
+STDMETHODIMP AppBundleWrapper::initialize() {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->initialize();
+}
+
+STDMETHODIMP AppBundleWrapper::createApp(BSTR app_id, IDispatch** app_disp) {
+  __mutexScope(model()->lock());
+
+  App* app = NULL;
+  HRESULT hr = wrapped_obj()->createApp(app_id, &app);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return AppWrapper::Create(controlling_ptr(), app, app_disp);
+}
+
+STDMETHODIMP AppBundleWrapper::createInstalledApp(BSTR appId,
+                                                  IDispatch** app_disp) {
+  __mutexScope(model()->lock());
+
+  App* app = NULL;
+  HRESULT hr = wrapped_obj()->createInstalledApp(appId, &app);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return AppWrapper::Create(controlling_ptr(), app, app_disp);
+}
+
+STDMETHODIMP AppBundleWrapper::createAllInstalledApps() {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->createAllInstalledApps();
+}
+
+STDMETHODIMP AppBundleWrapper::get_Count(long* count) {  // NOLINT
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_Count(count);
+}
+
+STDMETHODIMP AppBundleWrapper::get_Item(long index, IDispatch** app_disp) {  // NOLINT
+  __mutexScope(model()->lock());
+
+  App* app = NULL;
+  HRESULT hr = wrapped_obj()->get_Item(index, &app);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return AppWrapper::Create(controlling_ptr(), app, app_disp);
+}
+
+STDMETHODIMP AppBundleWrapper::checkForUpdate() {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->checkForUpdate();
+}
+
+STDMETHODIMP AppBundleWrapper::download() {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->download();
+}
+
+STDMETHODIMP AppBundleWrapper::install() {
+  if (wrapped_obj()->is_machine() && !UserRights::VerifyCallerIsAdmin()) {
+    ASSERT(false, (_T("AppBundle::install - Caller not an admin")));
+    return E_ACCESSDENIED;
+  }
+
+  __mutexScope(model()->lock());
+  return wrapped_obj()->install();
+}
+
+STDMETHODIMP AppBundleWrapper::updateAllApps() {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->updateAllApps();
+}
+
+STDMETHODIMP AppBundleWrapper::stop() {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->stop();
+}
+
+STDMETHODIMP AppBundleWrapper::pause() {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->pause();
+}
+
+STDMETHODIMP AppBundleWrapper::resume() {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->resume();
+}
+
+STDMETHODIMP AppBundleWrapper::isBusy(VARIANT_BOOL* is_busy) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->isBusy(is_busy);
+}
+
+STDMETHODIMP AppBundleWrapper::downloadPackage(BSTR app_id, BSTR package_name) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->downloadPackage(app_id, package_name);
+}
+
+STDMETHODIMP AppBundleWrapper::get_currentState(VARIANT* current_state) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_currentState(current_state);
+}
+
+
+// Sets app bundle's app_state to state. Used by unit tests to set up the state
+// to the correct precondition for the test case. AppBundle friends this
+// function, allowing it to call the private member function.
+void SetAppBundleStateForUnitTest(AppBundle* app_bundle,
+                                  fsm::AppBundleState* state) {
+  ASSERT1(app_bundle);
+  ASSERT1(state);
+  __mutexScope(app_bundle->model()->lock());
+  app_bundle->ChangeState(state);
+}
+
+}  // namespace omaha
diff --git a/goopdate/app_bundle.h b/goopdate/app_bundle.h
new file mode 100644
index 0000000..82fbd72
--- /dev/null
+++ b/goopdate/app_bundle.h
@@ -0,0 +1,298 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Defines the AppBundle COM object exposed by the model.
+
+#ifndef OMAHA_GOOPDATE_APP_BUNDLE_H_
+#define OMAHA_GOOPDATE_APP_BUNDLE_H_
+
+#include <windows.h>
+#include <atlbase.h>
+#include <atlcom.h>
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/goopdate/com_wrapper_creator.h"
+#include "omaha/goopdate/model_object.h"
+#include "omaha/net/proxy_auth.h"
+#include "third_party/bar/shared_ptr.h"
+
+namespace omaha {
+
+// TODO(omaha): needs to figure out a smaller public interface that
+// Worker expose for the model. AppBundle needs to delegate calls to Worker,
+// such as Pause, Resume, Update, Install, etc...
+
+class App;
+class Model;
+class WebServicesClientInterface;
+class UserWorkItem;
+
+namespace fsm {
+
+class AppBundleState;
+class AppBundleStateInit;
+
+}  // namespace fsm
+
+// AppBundle instances are reference-counted using shared pointers. Lifetime
+// of an AppBundle instance is controlled by both external and internal
+// outstanding references. External reference are the COM wrappers that depend
+// on the AppBundle, including its children in the object model.
+// Internal references to the AppBundle are maintained by several objects that
+// depend on the bundle objects.
+class AppBundle
+    : public ModelObject,
+      public enable_shared_from_this<AppBundle> {
+ public:
+  AppBundle(bool is_machine, Model* model);
+  virtual ~AppBundle();
+
+  // IAppBundle.
+  STDMETHOD(get_displayName)(BSTR* display_name);
+  STDMETHOD(put_displayName)(BSTR display_name);
+  STDMETHOD(get_displayLanguage)(BSTR* language);
+  STDMETHOD(put_displayLanguage)(BSTR language);
+  STDMETHOD(get_installSource)(BSTR* install_source);
+  STDMETHOD(put_installSource)(BSTR install_source);
+  STDMETHOD(get_originURL)(BSTR* origin_url);
+  STDMETHOD(put_originURL)(BSTR origin_url);
+  STDMETHOD(get_offlineDirectory)(BSTR* offline_dir);
+  STDMETHOD(put_offlineDirectory)(BSTR offline_dir);
+  STDMETHOD(get_sessionId)(BSTR* session_id);
+  STDMETHOD(put_sessionId)(BSTR session_id);
+  STDMETHOD(get_priority)(long* priority);  // NOLINT
+  STDMETHOD(put_priority)(long priority);  // NOLINT
+  STDMETHOD(get_Count)(long* count);  // NOLINT
+  STDMETHOD(get_Item)(long index, App** app);  // NOLINT
+  STDMETHOD(put_altTokens)(ULONG_PTR impersonation_token,
+                           ULONG_PTR primary_token,
+                           DWORD caller_proc_id);
+  STDMETHOD(put_parentHWND)(ULONG_PTR hwnd);
+  STDMETHOD(initialize)();
+  STDMETHOD(createApp)(BSTR app_id, App** app);
+  STDMETHOD(createInstalledApp)(BSTR app_id, App** app);
+  STDMETHOD(createAllInstalledApps)();
+  STDMETHOD(checkForUpdate)();
+  STDMETHOD(download)();
+  STDMETHOD(install)();
+  STDMETHOD(updateAllApps)();
+  STDMETHOD(stop)();
+  STDMETHOD(pause)();
+  STDMETHOD(resume)();
+  STDMETHOD(isBusy)(VARIANT_BOOL* is_busy);
+  STDMETHOD(downloadPackage)(BSTR app_id, BSTR package_name);
+  STDMETHOD(get_currentState)(VARIANT* current_state);
+
+  // Creates an App for each uninstalled app and adds it to
+  HRESULT CreateUninstalledApp(const CString& app_id, App** app);
+
+  // Marks an asynchronous operation complete.
+  void CompleteAsyncCall();
+
+  bool IsBusy() const;
+
+  // Returns a shared pointer to this instance of the class under the
+  // assumption that the instance is already managed by a shared pointer.
+  // This shared pointer controls the lifetime of the app bundle object and
+  // all its children.
+  ControllingPtr controlling_ptr();
+
+  void set_user_work_item(UserWorkItem* user_work_item);
+
+  const CString& install_source() const { return install_source_; }
+
+  const CString& origin_url() const { return origin_url_; }
+
+  // Gets the impersonation token of the current COM caller.
+  HANDLE impersonation_token() const;
+
+  // Gets the primary token of the current COM caller.
+  HANDLE primary_token() const;
+
+  size_t GetNumberOfApps() const;
+
+  App* GetApp(size_t index);
+
+  WebServicesClientInterface* update_check_client();
+
+  bool is_machine() const;
+
+  bool is_auto_update() const;
+  void set_is_auto_update(bool is_auto_update);
+
+  bool is_offline_install() const;
+
+  const CString& offline_dir() const;
+
+  const CString& session_id() const;
+
+  CString display_language() const;
+
+  int priority() const;
+
+  ProxyAuthConfig GetProxyAuthConfig() const;
+
+  // Gathers accumulated event logs from all child apps and clears the
+  // log buffer in each app.
+  CString FetchAndResetLogText();
+
+ private:
+  // Sets the state for unit testing.
+  friend void SetAppBundleStateForUnitTest(AppBundle* app_bundle,
+                                           fsm::AppBundleState* state);
+
+  // TODO(omaha): missing unit test.
+  // Sends the ping if the applications in the bundle have accumulated
+  // any ping events.
+  HRESULT SendPingEvents();
+
+  // These methods capture the current COM caller tokens.
+  HRESULT CaptureCallerImpersonationToken();
+  HRESULT CaptureCallerPrimaryToken();
+
+  void ChangeState(fsm::AppBundleState* app_bundle_state);
+
+  bool is_pending_non_blocking_call() const;
+
+  CString display_name_;
+  CString install_source_;
+  CString origin_url_;
+
+  bool is_machine_;
+
+  // True if the bundle is an update bundle.
+  bool is_auto_update_;
+
+  int priority_;
+
+  HWND parent_hwnd_;
+
+  CString offline_dir_;
+
+  // Contains the session ID - a unique marker that we include with each
+  // server communication (update checks, pings, etc.) in a single Omaha task.
+  // Clients are expected to set this on a bundle before calling initialize();
+  // if they don't, we will randomly generate one.
+  CString session_id_;
+
+  // The current non-blocking command object if any of them is executing.
+  // The class only checks whether the pointer is NULL to determine if a
+  // non-blocking call is pending. We use a pointer because it can be useful
+  // for debugging.
+  UserWorkItem* user_work_item_;
+
+  scoped_ptr<WebServicesClientInterface> update_check_client_;
+
+  // The apps in the bundle. Do not add to it directly; use AddApp() instead.
+  std::vector<App*> apps_;
+
+  // Uninstalled apps. Not accessible and only used to store Apps for
+  // uninstalled app IDs so that app uninstall pings can be sent along with
+  // other pings.
+  std::vector<App*> uninstalled_apps_;
+
+  scoped_ptr<fsm::AppBundleState> app_bundle_state_;
+
+  // Impersonation and primary tokens set by the client. Typically only
+  // set by the gupdatem service. The gupdatem service exposes a narrow
+  // interface to medium integrity clients. When a medium integrity client calls
+  // into the gupdatem service, the gupdatem service captures the token of the
+  // caller, and then calls put_altTokens() on the gupdate service, so that the
+  // gupdate service can use it for future download() and install() requests.
+  CAccessToken alt_impersonation_token_;
+  CAccessToken alt_primary_token_;
+
+  // The current COM caller's impersonation token.
+  CAccessToken impersonation_token_;
+
+  // The current COM caller's primary token. Lazy initialized at the install()
+  // entry point.
+  CAccessToken primary_token_;
+
+  // COM caller's display language.
+  CString display_language_;
+
+  friend class fsm::AppBundleState;
+  friend class fsm::AppBundleStateInit;
+
+  friend class AppBundleTest;
+  friend class WorkerTest;
+
+  DISALLOW_COPY_AND_ASSIGN(AppBundle);
+};
+
+class ATL_NO_VTABLE AppBundleWrapper
+    : public ComWrapper<AppBundleWrapper, AppBundle>,
+      public IDispatchImpl<IAppBundle,
+                           &__uuidof(IAppBundle),
+                           &CAtlModule::m_libid,
+                           kMajorTypeLibVersion,
+                           kMinorTypeLibVersion> {
+ public:
+  AppBundleWrapper();
+  virtual ~AppBundleWrapper();
+
+  // IAppBundle.
+  STDMETHOD(get_displayName)(BSTR* display_name);
+  STDMETHOD(put_displayName)(BSTR display_name);
+  STDMETHOD(get_displayLanguage)(BSTR* language);
+  STDMETHOD(put_displayLanguage)(BSTR language);
+  STDMETHOD(get_installSource)(BSTR* install_source);
+  STDMETHOD(put_installSource)(BSTR install_source);
+  STDMETHOD(get_originURL)(BSTR* origin_url);
+  STDMETHOD(put_originURL)(BSTR origin_url);
+  STDMETHOD(get_offlineDirectory)(BSTR* offline_dir);
+  STDMETHOD(put_offlineDirectory)(BSTR offline_dir);
+  STDMETHOD(get_sessionId)(BSTR* session_id);
+  STDMETHOD(put_sessionId)(BSTR session_id);
+  STDMETHOD(get_priority)(long* priority);  // NOLINT
+  STDMETHOD(put_priority)(long priority);  // NOLINT
+  STDMETHOD(get_Count)(long* count);  // NOLINT
+  STDMETHOD(get_Item)(long index, IDispatch** app_disp);  // NOLINT
+  STDMETHOD(put_altTokens)(ULONG_PTR impersonation_token,
+                           ULONG_PTR primary_token,
+                           DWORD caller_proc_id);
+  STDMETHOD(put_parentHWND)(ULONG_PTR hwnd);
+  STDMETHOD(initialize)();
+  STDMETHOD(createApp)(BSTR app_id, IDispatch** app_disp);
+  STDMETHOD(createInstalledApp)(BSTR app_id, IDispatch** app_disp);
+  STDMETHOD(createAllInstalledApps)();
+  STDMETHOD(checkForUpdate)();
+  STDMETHOD(download)();
+  STDMETHOD(install)();
+  STDMETHOD(updateAllApps)();
+  STDMETHOD(stop)();
+  STDMETHOD(pause)();
+  STDMETHOD(resume)();
+  STDMETHOD(isBusy)(VARIANT_BOOL* is_busy);
+  STDMETHOD(downloadPackage)(BSTR app_id, BSTR package_name);
+  STDMETHOD(get_currentState)(VARIANT* current_state);
+
+ private:
+  BEGIN_COM_MAP(AppBundleWrapper)
+    COM_INTERFACE_ENTRY(IAppBundle)
+    COM_INTERFACE_ENTRY(IDispatch)
+  END_COM_MAP()
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_BUNDLE_H_
diff --git a/goopdate/app_bundle_state.cc b/goopdate/app_bundle_state.cc
new file mode 100644
index 0000000..5fa69e1
--- /dev/null
+++ b/goopdate/app_bundle_state.cc
@@ -0,0 +1,197 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_bundle_state.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/common/web_services_client.h"
+#include "omaha/goopdate/app_bundle_state_busy.h"
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+namespace fsm {
+
+HRESULT AppBundleState::put_altTokens(AppBundle* app_bundle,
+                                      ULONG_PTR impersonation_token,
+                                      ULONG_PTR primary_token,
+                                      DWORD caller_proc_id) {
+  UNREFERENCED_PARAMETER(impersonation_token);
+  UNREFERENCED_PARAMETER(primary_token);
+  UNREFERENCED_PARAMETER(caller_proc_id);
+  return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+}
+
+HRESULT AppBundleState::put_sessionId(AppBundle* app_bundle, BSTR session_id) {
+  UNREFERENCED_PARAMETER(session_id);
+  return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+}
+
+HRESULT AppBundleState::Initialize(AppBundle* app_bundle) {
+  return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+}
+
+HRESULT AppBundleState::CreateApp(AppBundle* app_bundle,
+                                  const CString& app_id,
+                                  App** app) {
+  UNREFERENCED_PARAMETER(app_id);
+  UNREFERENCED_PARAMETER(app);
+  return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+}
+
+HRESULT AppBundleState::CreateInstalledApp(AppBundle* app_bundle,
+                                           const CString& app_id,
+                                           App** app) {
+  UNREFERENCED_PARAMETER(app_id);
+  UNREFERENCED_PARAMETER(app);
+  return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+}
+
+HRESULT AppBundleState::CreateAllInstalledApps(AppBundle* app_bundle) {
+  return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+}
+
+HRESULT AppBundleState::CheckForUpdate(AppBundle* app_bundle) {
+  return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+}
+
+HRESULT AppBundleState::Download(AppBundle* app_bundle) {
+  return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+}
+
+HRESULT AppBundleState::Install(AppBundle* app_bundle) {
+  return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+}
+
+HRESULT AppBundleState::UpdateAllApps(AppBundle* app_bundle) {
+  return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+}
+
+HRESULT AppBundleState::Stop(AppBundle* app_bundle) {
+  return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+}
+
+HRESULT AppBundleState::Pause(AppBundle* app_bundle) {
+  return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+}
+
+HRESULT AppBundleState::Resume(AppBundle* app_bundle) {
+  return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+}
+
+HRESULT AppBundleState::DownloadPackage(AppBundle* app_bundle,
+                                        const CString& app_id,
+                                        const CString& package_name) {
+  UNREFERENCED_PARAMETER(app_id);
+  UNREFERENCED_PARAMETER(package_name);
+  return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+}
+
+HRESULT AppBundleState::CompleteAsyncCall(AppBundle* app_bundle) {
+  return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+}
+
+bool AppBundleState::IsBusy() const {
+  return false;
+}
+
+void AppBundleState::AddAppToBundle(AppBundle* app_bundle, App* app) {
+  ASSERT1(app_bundle);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+  app_bundle->apps_.push_back(app);
+}
+
+bool AppBundleState::IsPendingNonBlockingCall(AppBundle* app_bundle) {
+  ASSERT1(app_bundle);
+  return app_bundle->is_pending_non_blocking_call();
+}
+
+HRESULT AppBundleState::DoDownloadPackage(AppBundle* app_bundle,
+                                          const CString& app_id,
+                                          const CString& package_name) {
+  CORE_LOG(L3, (_T("[AppBundleState::DoDownloadPackage][0x%p]"), app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+  ASSERT1(!IsPendingNonBlockingCall(app_bundle));
+
+  GUID app_guid = {0};
+  HRESULT hr = StringToGuidSafe(app_id, &app_guid);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  App* app = NULL;
+  for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
+    App* candidate_app = app_bundle->GetApp(i);
+    if (::IsEqualGUID(candidate_app->app_guid(), app_guid)) {
+      app = candidate_app;
+      break;
+    }
+  }
+
+  if (!app) {
+    return E_INVALIDARG;
+  }
+
+  // Only packages of installed applications can be downloaded.
+  Package* package = NULL;
+  AppVersion* version = app->current_version();
+  for (size_t i = 0; i != version->GetNumberOfPackages(); ++i) {
+    if (version->GetPackage(i)->filename() == package_name) {
+      package = version->GetPackage(i);
+      break;
+    }
+  }
+
+  if (!package) {
+    return E_INVALIDARG;
+  }
+
+  hr = app_bundle->model()->DownloadPackage(package);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[DownloadPackage failed][0x%x][0x%p]"), hr, app_bundle));
+    return hr;
+  }
+
+  ChangeState(app_bundle, new AppBundleStateBusy);
+  return S_OK;
+}
+
+void AppBundleState::ChangeState(AppBundle* app_bundle, AppBundleState* state) {
+  ASSERT1(app_bundle);
+  ASSERT1(state);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+  CORE_LOG(L3, (_T("[AppBundleState::ChangeState][0x%p][from: %u][to: %u]"),
+                app_bundle, state_, state->state_));
+
+  app_bundle->ChangeState(state);
+}
+
+HRESULT AppBundleState::HandleInvalidStateTransition(
+    AppBundle* app_bundle,
+    const TCHAR* function_name) {
+  UNREFERENCED_PARAMETER(app_bundle);
+  UNREFERENCED_PARAMETER(function_name);
+  ASSERT1(app_bundle);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+  CORE_LOG(LE, (_T("[Invalid state transition][%s called while in %u]"),
+                function_name, state_));
+  return GOOPDATE_E_CALL_UNEXPECTED;
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_bundle_state.h b/goopdate/app_bundle_state.h
new file mode 100644
index 0000000..38ecb25
--- /dev/null
+++ b/goopdate/app_bundle_state.h
@@ -0,0 +1,113 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_BUNDLE_STATE_H_
+#define OMAHA_GOOPDATE_APP_BUNDLE_STATE_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+class App;
+class AppBundle;
+class AppBundleTest;
+class WebServicesClientInterface;
+
+namespace fsm {
+
+// Defines the interface for encapsulating behavior associated with a particular
+// state of the AppBundle object.
+// All state transition calls should go through AppBundle, meaning it is the
+// only class that should use this interface.
+class AppBundleState {
+ public:
+  virtual ~AppBundleState() {}
+
+  virtual HRESULT put_altTokens(AppBundle* app_bundle,
+                                ULONG_PTR impersonation_token,
+                                ULONG_PTR primary_token,
+                                DWORD caller_proc_id);
+  virtual HRESULT put_sessionId(AppBundle* app_bundle, BSTR session_id);
+  virtual HRESULT Initialize(AppBundle* app_bundle);
+  virtual HRESULT CreateApp(AppBundle* app_bundle,
+                            const CString& app_id,
+                            App** app);
+  virtual HRESULT CreateInstalledApp(AppBundle* app_bundle,
+                                     const CString& app_id,
+                                     App** app);
+  virtual HRESULT CreateAllInstalledApps(AppBundle* app_bundle);
+
+  virtual HRESULT CheckForUpdate(AppBundle* app_bundle);
+  virtual HRESULT Download(AppBundle* app_bundle);
+  virtual HRESULT Install(AppBundle* app_bundle);
+
+  virtual HRESULT UpdateAllApps(AppBundle* app_bundle);
+
+  virtual HRESULT Stop(AppBundle* app_bundle);
+  virtual HRESULT Pause(AppBundle* app_bundle);
+  virtual HRESULT Resume(AppBundle* app_bundle);
+
+  virtual HRESULT DownloadPackage(AppBundle* app_bundle,
+                                  const CString& app_id,
+                                  const CString& package_name);
+
+  virtual HRESULT CompleteAsyncCall(AppBundle* app_bundle);
+
+  virtual bool IsBusy() const;
+
+ protected:
+  enum BundleState {
+    STATE_INIT,
+    STATE_INITIALIZED,
+    STATE_BUSY,
+    STATE_READY,
+    STATE_PAUSED,
+    STATE_STOPPED,
+  };
+
+
+  explicit AppBundleState(BundleState state) : state_(state) {}
+
+  // These functions provide pass-through access to private AppBundle members.
+  // TODO(omaha): remove paranoid asserts and implement inline.
+  void AddAppToBundle(AppBundle* app_bundle, App* app);
+  bool IsPendingNonBlockingCall(AppBundle* app_bundle);
+
+  HRESULT DoDownloadPackage(AppBundle* app_bundle,
+                            const CString& app_id,
+                            const CString& package_name);
+
+  // After calling this method, the old state (this object) is deleted and the
+  // code may not reference the members of the old state anymore.
+  void ChangeState(AppBundle* app_bundle, AppBundleState* state);
+
+  HRESULT HandleInvalidStateTransition(AppBundle* app_bundle,
+                                       const TCHAR* function_name);
+
+ private:
+  friend class AppBundleTest;
+
+  BundleState state_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppBundleState);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_BUNDLE_STATE_H_
diff --git a/goopdate/app_bundle_state_busy.cc b/goopdate/app_bundle_state_busy.cc
new file mode 100644
index 0000000..51be42e
--- /dev/null
+++ b/goopdate/app_bundle_state_busy.cc
@@ -0,0 +1,99 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_bundle_state_busy.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/goopdate/app_bundle_state_paused.h"
+#include "omaha/goopdate/app_bundle_state_ready.h"
+#include "omaha/goopdate/app_bundle_state_stopped.h"
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+namespace fsm {
+
+HRESULT AppBundleStateBusy::Pause(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[AppBundleStateBusy::Pause][0x%p]"), app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+  ASSERT1(IsPendingNonBlockingCall(app_bundle));
+
+  HRESULT hr = app_bundle->model()->Pause(app_bundle);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Pause failed][0x%08x][0x%p]"), hr, app_bundle));
+    return hr;
+  }
+
+  ChangeState(app_bundle, new AppBundleStatePaused);
+  return S_OK;
+}
+
+HRESULT AppBundleStateBusy::Stop(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[AppBundleStateBusy::Stop][0x%p]"), app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+  ASSERT1(IsPendingNonBlockingCall(app_bundle));
+
+  HRESULT hr = app_bundle->model()->Stop(app_bundle);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Stop failed][0x%08x][0x%p]"), hr, app_bundle));
+    return hr;
+  }
+
+  // Handling Stop is non-blocking. The worker completes the pending bundle
+  // calls while the bundle remains in the stopped state.
+  ChangeState(app_bundle, new AppBundleStateStopped);
+  return S_OK;
+}
+
+HRESULT AppBundleStateBusy::CompleteAsyncCall(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[AppBundleStateBusy::CompleteAsyncCall][0x%p]"),
+                app_bundle));
+  ASSERT1(IsPendingNonBlockingCall(app_bundle));
+  ChangeState(app_bundle, new AppBundleStateReady);
+  return S_OK;
+}
+
+HRESULT AppBundleStateBusy::Download(AppBundle* app_bundle) {
+  UNREFERENCED_PARAMETER(app_bundle);
+  ASSERT1(IsPendingNonBlockingCall(app_bundle));
+  return GOOPDATE_E_NON_BLOCKING_CALL_PENDING;
+}
+
+HRESULT AppBundleStateBusy::Install(AppBundle* app_bundle) {
+  UNREFERENCED_PARAMETER(app_bundle);
+  ASSERT1(IsPendingNonBlockingCall(app_bundle));
+  return GOOPDATE_E_NON_BLOCKING_CALL_PENDING;
+}
+
+HRESULT AppBundleStateBusy::DownloadPackage(AppBundle* app_bundle,
+                                            const CString& app_id,
+                                            const CString& package_name) {
+  UNREFERENCED_PARAMETER(app_bundle);
+  UNREFERENCED_PARAMETER(app_id);
+  UNREFERENCED_PARAMETER(package_name);
+  ASSERT1(IsPendingNonBlockingCall(app_bundle));
+  return GOOPDATE_E_NON_BLOCKING_CALL_PENDING;
+}
+
+bool AppBundleStateBusy::IsBusy() const {
+  return true;
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_bundle_state_busy.h b/goopdate/app_bundle_state_busy.h
new file mode 100644
index 0000000..de74262
--- /dev/null
+++ b/goopdate/app_bundle_state_busy.h
@@ -0,0 +1,53 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_BUNDLE_STATE_BUSY_H_
+#define OMAHA_GOOPDATE_APP_BUNDLE_STATE_BUSY_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_bundle_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppBundleStateBusy : public AppBundleState {
+ public:
+  AppBundleStateBusy() : AppBundleState(STATE_BUSY) {}
+  virtual ~AppBundleStateBusy() {}
+
+  virtual HRESULT Stop(AppBundle* app_bundle);
+  virtual HRESULT Pause(AppBundle* app_bundle);
+
+  virtual HRESULT CompleteAsyncCall(AppBundle* app_bundle);
+
+  virtual bool IsBusy() const;
+
+  // These all return GOOPDATE_E_NON_BLOCKING_CALL_PENDING.
+  virtual HRESULT Download(AppBundle* app_bundle);
+  virtual HRESULT Install(AppBundle* app_bundle);
+  virtual HRESULT DownloadPackage(AppBundle* app_bundle,
+                                  const CString& app_id,
+                                  const CString& package_name);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppBundleStateBusy);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_BUNDLE_STATE_BUSY_H_
diff --git a/goopdate/app_bundle_state_init.cc b/goopdate/app_bundle_state_init.cc
new file mode 100644
index 0000000..7314cda
--- /dev/null
+++ b/goopdate/app_bundle_state_init.cc
@@ -0,0 +1,133 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_bundle_state_init.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/user_rights.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/web_services_client.h"
+#include "omaha/goopdate/app_bundle_state_initialized.h"
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+namespace fsm {
+
+HRESULT AppBundleStateInit::put_altTokens(AppBundle* app_bundle,
+                                          ULONG_PTR impersonation_token,
+                                          ULONG_PTR primary_token,
+                                          DWORD caller_proc_id) {
+  CORE_LOG(L3, (_T("[AppBundleStateInit::put_altTokens][0x%p]"), app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(impersonation_token);
+  ASSERT1(primary_token);
+  ASSERT1(caller_proc_id);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+
+  scoped_handle caller_proc_handle(::OpenProcess(PROCESS_DUP_HANDLE,
+                                                 false,
+                                                 caller_proc_id));
+  if (!get(caller_proc_handle)) {
+    return HRESULTFromLastError();
+  }
+
+  if (app_bundle->alt_impersonation_token_.GetHandle() ||
+      app_bundle->alt_primary_token_.GetHandle()) {
+    return E_UNEXPECTED;
+  }
+
+  HRESULT hr = DuplicateTokenIntoCurrentProcess(
+      get(caller_proc_handle),
+      reinterpret_cast<HANDLE>(impersonation_token),
+      &app_bundle->alt_impersonation_token_);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return DuplicateTokenIntoCurrentProcess(
+             get(caller_proc_handle),
+             reinterpret_cast<HANDLE>(primary_token),
+             &app_bundle->alt_primary_token_);
+}
+
+HRESULT AppBundleStateInit::put_sessionId(AppBundle* app_bundle,
+                                          BSTR session_id) {
+  CORE_LOG(L3, (_T("[AppBundleStateInit::put_sessionId][0x%p]"), app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+
+  if (!session_id) {
+    return E_POINTER;
+  }
+
+  ASSERT1(IsGuid(session_id));
+
+  app_bundle->session_id_ = session_id;
+  return S_OK;
+}
+
+// Captures the caller's imperonation token and uses it to initialize the ping
+// and WebServicesClient objects.
+// It is possible that one user could call this function and another admin user
+// could call subsequent methods with this impersonation token. Since it is only
+// used for network access, this is okay. The primary token, which is used for
+// installation, is only captured by install().
+// The WebServicesClient objects are created here instead of in the constructor
+// because the Initialize() function is not part of WebServicesClientInterface.
+// TODO(omaha): Enforce ordering - initialize must be called before any other
+// non-property functions. This code must move to AppBundleStateInit.
+HRESULT AppBundleStateInit::Initialize(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[AppBundleStateInit::Initialize][0x%p]"), app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+
+  // Clients should have set these properties before calling this function.
+  ASSERT1(!app_bundle->display_name_.IsEmpty());
+  ASSERT1(!app_bundle->display_language().IsEmpty());
+
+  // If the client hasn't set a session ID before calling this function,
+  // generate a random one for them.
+  if (app_bundle->session_id_.IsEmpty()) {
+    GetGuid(&app_bundle->session_id_);
+  }
+
+  HRESULT hr = app_bundle->CaptureCallerImpersonationToken();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  ASSERT1(!app_bundle->update_check_client_.get());
+  CString update_check_url;
+  VERIFY1(SUCCEEDED(
+      ConfigManager::Instance()->GetUpdateCheckUrl(&update_check_url)));
+  scoped_ptr<WebServicesClient> web_service_client;
+  web_service_client.reset(new WebServicesClient(app_bundle->is_machine()));
+  hr = web_service_client->Initialize(update_check_url, HeadersVector(), true);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Update check client init failed][0x%08x]"), hr));
+    return hr;
+  }
+  app_bundle->update_check_client_.reset(web_service_client.release());
+
+  ChangeState(app_bundle, new AppBundleStateInitialized);
+  return S_OK;
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_bundle_state_init.h b/goopdate/app_bundle_state_init.h
new file mode 100644
index 0000000..58566dc
--- /dev/null
+++ b/goopdate/app_bundle_state_init.h
@@ -0,0 +1,45 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_BUNDLE_STATE_INIT_H_
+#define OMAHA_GOOPDATE_APP_BUNDLE_STATE_INIT_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_bundle_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppBundleStateInit : public AppBundleState {
+ public:
+  AppBundleStateInit() : AppBundleState(STATE_INIT) {}
+
+  virtual HRESULT put_altTokens(AppBundle* app_bundle,
+                                ULONG_PTR impersonation_token,
+                                ULONG_PTR primary_token,
+                                DWORD caller_proc_id);
+  virtual HRESULT put_sessionId(AppBundle* app_bundle, BSTR session_id);
+  virtual HRESULT Initialize(AppBundle* app_bundle);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppBundleStateInit);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_BUNDLE_STATE_INIT_H_
diff --git a/goopdate/app_bundle_state_initialized.cc b/goopdate/app_bundle_state_initialized.cc
new file mode 100644
index 0000000..fc82eb8
--- /dev/null
+++ b/goopdate/app_bundle_state_initialized.cc
@@ -0,0 +1,306 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_bundle_state_initialized.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/common/app_registry_utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/web_services_client.h"
+#include "omaha/goopdate/app_bundle_state_busy.h"
+#include "omaha/goopdate/app_bundle_state_paused.h"
+#include "omaha/goopdate/app_bundle_state_stopped.h"
+#include "omaha/goopdate/app_manager.h"
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+namespace fsm {
+
+HRESULT AppBundleStateInitialized::Pause(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[AppBundleStateInitialized::Pause][0x%p]"), app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+
+  ChangeState(app_bundle, new AppBundleStatePaused);
+  return S_OK;
+}
+
+HRESULT AppBundleStateInitialized::Stop(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[AppBundleStateInitialized::Stop][0x%p]"), app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+
+  ChangeState(app_bundle, new AppBundleStateStopped);
+  return S_OK;
+}
+
+// Remains in this state.
+HRESULT AppBundleStateInitialized::CreateApp(AppBundle* app_bundle,
+                                             const CString& app_id,
+                                             App** app) {
+  CORE_LOG(L3, (_T("[AppBundleStateInitialized::CreateApp][0x%p]"),
+                app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(app);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+
+  // TODO(omaha): consider enabling this runtime test. Currently, there are
+  // a few unit tests that break this assumption mostly during the setup of
+  // the unit test itself.
+#if 0
+  if (app_id.CompareNoCase(kGoogleUpdateAppId) == 0) {
+    CORE_LOG(LE, (_T("[Omaha itself can't be created as a new app]")));
+    return E_INVALIDARG;
+  }
+#endif
+
+  if (has_installed_app_) {
+    CORE_LOG(LE, (_T("[CreateApp][Installed app already in bundle]")));
+    return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+  }
+
+  GUID app_guid = {0};
+  HRESULT hr = StringToGuidSafe(app_id, &app_guid);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[invalid app id][%s]"), app_id));
+    return hr;
+  }
+
+  scoped_ptr<App> local_app(new App(app_guid, false, app_bundle));
+  hr = AddApp(app_bundle, local_app.get());
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // When overinstalling, we want the install age for the existing install, so
+  // explicitly get it here. This is the only value read from the registry for
+  // installs.
+  AppManager::Instance()->ReadAppInstallTimeDiff(local_app.get());
+
+  *app = local_app.release();
+  has_new_app_ = true;
+  return S_OK;
+}
+
+// Remains in this state.
+HRESULT AppBundleStateInitialized::CreateInstalledApp(AppBundle* app_bundle,
+                                                      const CString& app_id,
+                                                      App** app) {
+  CORE_LOG(L3, (_T("[AppBundleStateInitialized::CreateInstalledApp][0x%p]"),
+                app_bundle));
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+
+  if (has_new_app_) {
+    CORE_LOG(LE, (_T("[CreateInstalledApp][New app already in bundle]")));
+    return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+  }
+
+  // Make sure that the application registration is up to date.
+  HRESULT hr = AppManager::Instance()->RunRegistrationUpdateHook(app_id);
+  if (FAILED(hr)) {
+    CORE_LOG(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr ? L3 : LW,
+             (_T("[RunRegistrationUpdateHook failed][%s][0x%x]"),
+             app_id, hr));
+  }
+
+  hr = AddInstalledApp(app_bundle, app_id, app);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+// Remains in this state.
+// This function must explicitly check to ensure duplicate apps are not added
+// because AddInstalledApp errors are ignored. The check for an empty bundle
+// also covers the has_new_app_ case.
+HRESULT AppBundleStateInitialized::CreateAllInstalledApps(
+    AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[AppBundleStateInitialized::CreateAllInstalledApps][0x%p]"),
+                app_bundle));
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+
+  if (app_bundle->GetNumberOfApps() > 0) {
+    CORE_LOG(LE, (_T("[CreateAllInstalledApps][Bundle already has apps]")));
+    return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+  }
+  ASSERT1(!has_new_app_);
+
+  // Make sure the list of installed applications is up to date. This is
+  // primarily important for Google Pack, which supports updating third-party
+  // applications that are not aware of Omaha registration, and hence will not
+  // update the registration during an install or uninstall outside of Pack.
+  AppManager& app_manager = *AppManager::Instance();
+  HRESULT hr = app_manager.RunAllRegistrationUpdateHooks();
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[RunAllRegistrationUpdateHooks failed][0x%x]"), hr));
+  }
+
+  AppIdVector registered_app_ids;
+  hr = app_manager.GetRegisteredApps(&registered_app_ids);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[GetRegisteredApps failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  for (size_t i = 0; i != registered_app_ids.size(); ++i) {
+    const CString& app_id = registered_app_ids[i];
+
+    ASSERT(RegKey::HasKey(app_registry_utils::GetAppClientStateKey(
+                              app_bundle->is_machine(), app_id)),
+           (_T("[Clients key without matching ClientState][%s]"), app_id));
+
+    App* app = NULL;
+    HRESULT hr = AddInstalledApp(app_bundle, app_id, &app);
+    if (FAILED(hr)) {
+      CORE_LOG(LW, (_T("[AddInstalledApp failed processing app][%s]"), app_id));
+    }
+  }
+
+  return S_OK;
+}
+
+// It is important that the lock is held for the entirety of this and similar
+// methods with asynchronous callbacks because CompleteAsyncCall() must not be
+// called before the state has been changed to busy.
+HRESULT AppBundleStateInitialized::CheckForUpdate(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[AppBundleStateInitialized::CheckForUpdate][0x%p]"),
+                app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+  ASSERT1(!IsPendingNonBlockingCall(app_bundle));
+
+  if (app_bundle->GetNumberOfApps() == 0) {
+    CORE_LOG(LE, (_T("[CheckForUpdate][No apps in bundle]")));
+    return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+  }
+
+  ASSERT1(has_new_app_ != has_installed_app_);
+
+  HRESULT hr = app_bundle->model()->CheckForUpdate(app_bundle);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CheckForUpdates failed][0x%x][0x%p]"), hr, app_bundle));
+    return hr;
+  }
+
+  ChangeState(app_bundle, new AppBundleStateBusy);
+  return S_OK;
+}
+
+HRESULT AppBundleStateInitialized::UpdateAllApps(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[AppBundleStateInitialized::UpdateAllApps][0x%p]"),
+                app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+  ASSERT1(!IsPendingNonBlockingCall(app_bundle));
+
+  if (app_bundle->GetNumberOfApps() != 0) {
+    CORE_LOG(LE, (_T("[UpdateAllApps][Apps already in bundle]")));
+    return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+  }
+
+  app_bundle->set_is_auto_update(true);
+
+  HRESULT hr = app_bundle->createAllInstalledApps();
+  if (FAILED(hr)) {
+    return hr;
+  }
+  ASSERT1(app_bundle->GetNumberOfApps() > 0);
+
+  hr = app_bundle->model()->UpdateAllApps(app_bundle);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[UpdateAllApps failed][0x%08x][0x%p]"), hr, app_bundle));
+    return hr;
+  }
+
+  ChangeState(app_bundle, new AppBundleStateBusy);
+  return S_OK;
+}
+
+HRESULT AppBundleStateInitialized::DownloadPackage(
+    AppBundle* app_bundle,
+    const CString& app_id,
+    const CString& package_name) {
+  CORE_LOG(L3, (_T("[AppBundleStateInitialized::DownloadPackage][0x%p]"),
+                app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+
+  if (app_bundle->GetNumberOfApps() == 0 || has_new_app_) {
+    CORE_LOG(LE, (_T("[DownloadPackage][No existing apps in bundle]")));
+    return HandleInvalidStateTransition(app_bundle, _T(__FUNCTION__));
+  }
+
+  return DoDownloadPackage(app_bundle, app_id, package_name);
+}
+
+// App is created with is_update=true because using an installed app's
+// information, including a non-zero version, is an update.
+HRESULT AppBundleStateInitialized::AddInstalledApp(AppBundle* app_bundle,
+                                                   const CString& app_id,
+                                                   App** app) {
+  ASSERT1(app_bundle);
+  ASSERT1(app);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+
+  GUID app_guid = {0};
+  HRESULT hr = StringToGuidSafe(app_id, &app_guid);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[invalid app id][%s]"), app_id));
+    return hr;
+  }
+
+  scoped_ptr<App> local_app(new App(app_guid, true, app_bundle));
+
+  hr = AppManager::Instance()->ReadAppPersistentData(local_app.get());
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[ReadAppPersistentData failed][0x%x][%s]"), hr, app_id));
+    return hr;
+  }
+
+  hr = AddApp(app_bundle, local_app.get());
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  has_installed_app_ = true;
+  *app = local_app.release();
+  return S_OK;
+}
+
+// Fails if the app already exists in the bundle.
+HRESULT AppBundleStateInitialized::AddApp(AppBundle* app_bundle, App* app) {
+  ASSERT1(app_bundle);
+  ASSERT1(app);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+
+  for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
+    App* existing_app = app_bundle->GetApp(i);
+    if (::IsEqualGUID(existing_app->app_guid(), app->app_guid())) {
+      CORE_LOG(LE, (_T("[App already in bundle][%s]"), app->app_guid_string()));
+      return GOOPDATE_E_CALL_UNEXPECTED;
+    }
+  }
+
+  AddAppToBundle(app_bundle, app);
+  return S_OK;
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_bundle_state_initialized.h b/goopdate/app_bundle_state_initialized.h
new file mode 100644
index 0000000..01265e8
--- /dev/null
+++ b/goopdate/app_bundle_state_initialized.h
@@ -0,0 +1,70 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_BUNDLE_STATE_INITIALIZED_H_
+#define OMAHA_GOOPDATE_APP_BUNDLE_STATE_INITIALIZED_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_bundle_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppBundleStateInitialized : public AppBundleState {
+ public:
+  AppBundleStateInitialized()
+      : AppBundleState(STATE_INITIALIZED),
+        has_new_app_(false),
+        has_installed_app_(false) {}
+  virtual ~AppBundleStateInitialized() {}
+
+  virtual HRESULT Stop(AppBundle* app_bundle);
+  virtual HRESULT Pause(AppBundle* app_bundle);
+  virtual HRESULT CreateApp(AppBundle* app_bundle,
+                            const CString& app_id,
+                            App** app);
+  virtual HRESULT CreateInstalledApp(AppBundle* app_bundle,
+                                     const CString& app_id,
+                                     App** app);
+  virtual HRESULT CreateAllInstalledApps(AppBundle* app_bundle);
+
+  virtual HRESULT CheckForUpdate(AppBundle* app_bundle);
+
+  virtual HRESULT UpdateAllApps(AppBundle* app_bundle);
+
+  virtual HRESULT DownloadPackage(AppBundle* app_bundle,
+                                  const CString& app_id,
+                                  const CString& package_name);
+
+ private:
+  HRESULT AddInstalledApp(AppBundle* app_bundle,
+                          const CString& appId,
+                          App** app);
+
+  // Adds an app to app_bundle's apps_. Takes ownership of app when successful.
+  HRESULT AddApp(AppBundle* app_bundle, App* app);
+
+  bool has_new_app_;
+  bool has_installed_app_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppBundleStateInitialized);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_BUNDLE_STATE_INITIALIZED_H_
diff --git a/goopdate/app_bundle_state_paused.cc b/goopdate/app_bundle_state_paused.cc
new file mode 100644
index 0000000..2c62395
--- /dev/null
+++ b/goopdate/app_bundle_state_paused.cc
@@ -0,0 +1,66 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_bundle_state_paused.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/goopdate/app_bundle_state_busy.h"
+#include "omaha/goopdate/app_bundle_state_ready.h"
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+namespace fsm {
+
+// Remains in this state since the bundle is already paused.
+HRESULT AppBundleStatePaused::Pause(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[AppBundleStatePaused::Pause][0x%p]"), app_bundle));
+  UNREFERENCED_PARAMETER(app_bundle);
+  return S_OK;
+}
+
+HRESULT AppBundleStatePaused::Resume(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[AppBundleStatePaused::Resume][0x%p]"), app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+
+  HRESULT hr = app_bundle->model()->Resume(app_bundle);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Resume failed][0x%08x][0x%p]"), hr, app_bundle));
+    return hr;
+  }
+
+  if (is_async_call_complete_) {
+    ChangeState(app_bundle, new AppBundleStateReady);
+  } else {
+    ChangeState(app_bundle, new AppBundleStateBusy);
+  }
+  return S_OK;
+}
+
+HRESULT AppBundleStatePaused::CompleteAsyncCall(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[AppBundleStatePaused::CompleteAsyncCall][0x%p]"),
+                app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+  ASSERT1(IsPendingNonBlockingCall(app_bundle));
+  UNREFERENCED_PARAMETER(app_bundle);
+  is_async_call_complete_ = true;
+  return S_OK;
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_bundle_state_paused.h b/goopdate/app_bundle_state_paused.h
new file mode 100644
index 0000000..1763eb2
--- /dev/null
+++ b/goopdate/app_bundle_state_paused.h
@@ -0,0 +1,53 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_BUNDLE_STATE_PAUSED_H_
+#define OMAHA_GOOPDATE_APP_BUNDLE_STATE_PAUSED_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_bundle_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppBundleStatePaused : public AppBundleState {
+ public:
+  AppBundleStatePaused()
+      : AppBundleState(STATE_PAUSED),
+        is_async_call_complete_(false) {}
+  virtual ~AppBundleStatePaused() {}
+
+  // TODO(omaha): What should Stop() do?
+  virtual HRESULT Pause(AppBundle* app_bundle);
+  virtual HRESULT Resume(AppBundle* app_bundle);
+
+  virtual HRESULT CompleteAsyncCall(AppBundle* app_bundle);
+
+ private:
+  // It is possible that an asynchronous operation will complete while in the
+  // paused state because pausing itself is asynchronous. Therefore,
+  // CompleteAsyncCall() may be called in this state. Remember whether
+  // it was so that we can transition to the correct state on Resume().
+  bool is_async_call_complete_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppBundleStatePaused);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_BUNDLE_STATE_PAUSED_H_
diff --git a/goopdate/app_bundle_state_ready.cc b/goopdate/app_bundle_state_ready.cc
new file mode 100644
index 0000000..afe324e
--- /dev/null
+++ b/goopdate/app_bundle_state_ready.cc
@@ -0,0 +1,87 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_bundle_state_ready.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/goopdate/app_bundle_state_busy.h"
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+namespace fsm {
+
+HRESULT AppBundleStateReady::Download(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[AppBundleStateReady::Download][0x%p]"), app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+  ASSERT1(!IsPendingNonBlockingCall(app_bundle));
+
+  HRESULT hr = app_bundle->model()->Download(app_bundle);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Download failed][0x%08x][0x%p]"), hr, app_bundle));
+    return hr;
+  }
+
+  ChangeState(app_bundle, new AppBundleStateBusy);
+  return S_OK;
+}
+
+HRESULT AppBundleStateReady::Install(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[AppBundleStateReady::Install][0x%p]"), app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(app_bundle->model()->IsLockedByCaller());
+  ASSERT1(!IsPendingNonBlockingCall(app_bundle));
+
+  HRESULT hr = app_bundle->model()->DownloadAndInstall(app_bundle);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Install failed][0x%08x][0x%p]"), hr, app_bundle));
+    return hr;
+  }
+
+  ChangeState(app_bundle, new AppBundleStateBusy);
+  return S_OK;
+}
+
+// Remains in this state since there is nothing to pause.
+HRESULT AppBundleStateReady::Pause(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[AppBundleStateReady::Pause][0x%p]"), app_bundle));
+  UNREFERENCED_PARAMETER(app_bundle);
+  return S_OK;
+}
+
+// Remains in this state since the bundle is not paused. This might occur if
+// Pause() was called while in this state.
+HRESULT AppBundleStateReady::Resume(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[AppBundleStateReady::Resume][0x%p]"), app_bundle));
+  UNREFERENCED_PARAMETER(app_bundle);
+  return S_OK;
+}
+
+HRESULT AppBundleStateReady::DownloadPackage(AppBundle* app_bundle,
+                                             const CString& app_id,
+                                             const CString& package_name) {
+  CORE_LOG(L3, (_T("[AppBundleStateReady::DownloadPackage][0x%p]"),
+                app_bundle));
+
+  // TODO(omaha): There may need to be some check here that the app is
+  // downloaded or installed.
+
+  return DoDownloadPackage(app_bundle, app_id, package_name);
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_bundle_state_ready.h b/goopdate/app_bundle_state_ready.h
new file mode 100644
index 0000000..f4f38be
--- /dev/null
+++ b/goopdate/app_bundle_state_ready.h
@@ -0,0 +1,50 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_BUNDLE_STATE_READY_H_
+#define OMAHA_GOOPDATE_APP_BUNDLE_STATE_READY_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_bundle_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppBundleStateReady : public AppBundleState {
+ public:
+  AppBundleStateReady() : AppBundleState(STATE_READY) {}
+  virtual ~AppBundleStateReady() {}
+
+  virtual HRESULT Download(AppBundle* app_bundle);
+  virtual HRESULT Install(AppBundle* app_bundle);
+
+  // TODO(omaha): What should Stop() do?
+  virtual HRESULT Pause(AppBundle* app_bundle);
+  virtual HRESULT Resume(AppBundle* app_bundle);
+
+  virtual HRESULT DownloadPackage(AppBundle* app_bundle,
+                                  const CString& app_id,
+                                  const CString& package_name);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppBundleStateReady);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_BUNDLE_STATE_READY_H_
diff --git a/goopdate/app_bundle_state_stopped.cc b/goopdate/app_bundle_state_stopped.cc
new file mode 100644
index 0000000..d82911c
--- /dev/null
+++ b/goopdate/app_bundle_state_stopped.cc
@@ -0,0 +1,39 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_bundle_state_stopped.h"
+#include "omaha/base/logging.h"
+
+namespace omaha {
+
+namespace fsm {
+
+HRESULT AppBundleStateStopped::Stop(AppBundle* app_bundle) {
+  UNREFERENCED_PARAMETER(app_bundle);
+  CORE_LOG(L3, (_T("[AppBundleStateStopped::Stop][0x%p]"), app_bundle));
+  return S_OK;
+}
+
+HRESULT AppBundleStateStopped::CompleteAsyncCall(AppBundle* app_bundle) {
+  UNREFERENCED_PARAMETER(app_bundle);
+  CORE_LOG(L3, (_T("[AppBundleStateStopped::CompleteAsyncCall][0x%p]"),
+      app_bundle));
+  return S_OK;
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
+
diff --git a/goopdate/app_bundle_state_stopped.h b/goopdate/app_bundle_state_stopped.h
new file mode 100644
index 0000000..bebbdc5
--- /dev/null
+++ b/goopdate/app_bundle_state_stopped.h
@@ -0,0 +1,46 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_BUNDLE_STATE_STOPPED_H_
+#define OMAHA_GOOPDATE_APP_BUNDLE_STATE_STOPPED_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_bundle_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppBundleStateStopped : public AppBundleState {
+ public:
+  AppBundleStateStopped()
+      : AppBundleState(STATE_STOPPED) {}
+  virtual ~AppBundleStateStopped() {}
+
+ private:
+
+  virtual HRESULT Stop(AppBundle* app_bundle);
+
+  virtual HRESULT CompleteAsyncCall(AppBundle* app_bundle);
+
+  DISALLOW_COPY_AND_ASSIGN(AppBundleStateStopped);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_BUNDLE_STATE_STOPPED_H_
+
diff --git a/goopdate/app_bundle_unittest.cc b/goopdate/app_bundle_unittest.cc
new file mode 100644
index 0000000..13220f2
--- /dev/null
+++ b/goopdate/app_bundle_unittest.cc
@@ -0,0 +1,2357 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <atlsecurity.h>
+#include "omaha/base/app_util.h"
+#include "omaha/base/error.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/thread_pool.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/lang.h"
+#include "omaha/goopdate/app_bundle_state_busy.h"
+#include "omaha/goopdate/app_bundle_state_initialized.h"
+#include "omaha/goopdate/app_bundle_state_paused.h"
+#include "omaha/goopdate/app_bundle_state_ready.h"
+#include "omaha/goopdate/app_bundle_state_stopped.h"
+#include "omaha/goopdate/app_manager.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/resource_manager.h"
+#include "omaha/goopdate/worker.h"
+#include "omaha/goopdate/worker_mock.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+const int kKnownError = 0x87658765;
+
+const TCHAR* const kDefaultAppName = _T("Google Application");
+
+const uint32 kInitialInstallTimeDiff = static_cast<uint32>(-1 * kSecondsPerDay);
+
+// TODO(omaha): there is a problem with this unit test. The model is built
+// bottom up. This makes it impossible to set the references to parents. Will
+// have to fix the code, eventually using Builder DP to create a bunch of
+// models containing bundles, apps, and such.
+
+
+// Helper functions.
+// TODO(omaha): helper functions need to go into their own compilation unit to
+// avoid dependencies between unit test modules.
+
+void ValidateFreshInstallDefaultValues(const App& app) {
+  EXPECT_FALSE(::IsEqualGUID(GUID_NULL, app.app_guid()));
+  EXPECT_TRUE(app.language().IsEmpty());
+  EXPECT_TRUE(app.ap().IsEmpty());
+  EXPECT_TRUE(app.tt_token().IsEmpty());
+  EXPECT_TRUE(::IsEqualGUID(GUID_NULL, app.iid()));
+  EXPECT_TRUE(app.brand_code().IsEmpty());
+  EXPECT_TRUE(app.client_id().IsEmpty());
+  EXPECT_TRUE(app.GetExperimentLabels().IsEmpty());
+  EXPECT_TRUE(app.referral_id().IsEmpty());
+  EXPECT_EQ(kInitialInstallTimeDiff, app.install_time_diff_sec());
+  EXPECT_FALSE(app.is_eula_accepted());
+  EXPECT_TRUE(app.display_name().IsEmpty());
+  EXPECT_EQ(BROWSER_UNKNOWN, app.browser_type());
+  EXPECT_TRUE(app.server_install_data_index().IsEmpty());
+  EXPECT_EQ(TRISTATE_NONE, app.usage_stats_enable());
+  EXPECT_TRUE(app.client_install_data().IsEmpty());
+  EXPECT_TRUE(app.server_install_data().IsEmpty());
+  EXPECT_EQ(ACTIVE_UNKNOWN, app.did_run());
+  EXPECT_EQ(0, app.days_since_last_active_ping());
+  EXPECT_EQ(0, app.days_since_last_roll_call());
+
+  EXPECT_TRUE(app.current_version()->version().IsEmpty());
+  EXPECT_TRUE(app.next_version()->version().IsEmpty());
+    // TODO(omaha3): Add all the new values (state_, etc.).
+}
+
+void PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(
+    bool is_machine,
+    App* expected_app0,
+    App* expected_app1,
+    App* expected_app2,
+    App* opposite_hive_app1,
+    App* opposite_hive_app2);
+
+void SetDisplayName(const CString& name, App* app);
+
+void ValidateExpectedValues(const App& expected, const App& actual) {
+  EXPECT_STREQ(GuidToString(expected.app_guid()),
+               GuidToString(actual.app_guid()));
+  EXPECT_STREQ(expected.language(), actual.language());
+  EXPECT_STREQ(expected.ap(), actual.ap());
+  EXPECT_STREQ(expected.tt_token(), actual.tt_token());
+  EXPECT_STREQ(GuidToString(expected.iid()), GuidToString(actual.iid()));
+  EXPECT_STREQ(expected.brand_code(), actual.brand_code());
+  EXPECT_STREQ(expected.client_id(), actual.client_id());
+  EXPECT_STREQ(expected.GetExperimentLabels(), actual.GetExperimentLabels());
+  EXPECT_STREQ(expected.referral_id(), actual.referral_id());
+  EXPECT_EQ(expected.install_time_diff_sec(), actual.install_time_diff_sec());
+  EXPECT_EQ(expected.is_eula_accepted(), actual.is_eula_accepted());
+  EXPECT_STREQ(expected.display_name(), actual.display_name());
+  EXPECT_EQ(expected.browser_type(), actual.browser_type());
+  EXPECT_STREQ(expected.server_install_data_index(),
+               actual.server_install_data_index());
+  EXPECT_EQ(expected.usage_stats_enable(), actual.usage_stats_enable());
+  EXPECT_STREQ(expected.client_install_data(), actual.client_install_data());
+  EXPECT_STREQ(expected.server_install_data(), actual.server_install_data());
+  EXPECT_EQ(expected.did_run(), actual.did_run());
+
+  EXPECT_STREQ(expected.current_version()->version(),
+               actual.current_version()->version());
+  EXPECT_STREQ(expected.next_version()->version(),
+               actual.next_version()->version());
+
+  // TODO(omaha3): Add all the new values (state(), etc.)?
+}
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace {
+
+// TODO(omaha): At least some of these and where they are used have to be kept
+// in sync with app_manager_unittest.cc because the constants are hard-coded in
+// functions used by these tests. Break this coupling.
+const TCHAR* const kGuid1 = _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}");
+const TCHAR* const kGuid2 = _T("{A979ACBD-1F55-4b12-A35F-4DBCA5A7CCB8}");
+const TCHAR* const kGuid3 = _T("{661045C5-4429-4140-BC48-8CEA241D1DEF}");
+const TCHAR* const kGuid4 = _T("{AAFA1CF9-E94F-42e6-A899-4CD27F37D5A7}");
+const TCHAR* const kGuid5 = _T("{3B1A3CCA-0525-4418-93E6-A0DB3398EC9B}");
+const TCHAR* const kGuid6 = _T("{F3F2CFD4-5F98-4bf0-ABB0-BEEEA46C62B4}");
+const TCHAR* const kGuid7 = _T("{6FD2272F-8583-4bbd-895A-E65F8003FC7B}");
+
+
+class DummyUserWorkItem : public UserWorkItem {
+ private:
+  virtual void DoProcess() {}
+};
+
+ACTION_P(SetWorkItem, work_item) {
+  UNREFERENCED_ACTION_PARAMETERS;
+  arg0->set_user_work_item(work_item);
+  return 0;
+}
+
+}  // namespace
+
+class AppBundleNoBundleTest : public testing::Test {
+ protected:
+  explicit AppBundleNoBundleTest(bool is_machine)
+      : is_machine_(is_machine) {}
+
+  virtual void SetUp() {
+    EXPECT_SUCCEEDED(AppManager::CreateInstance(is_machine_));
+
+    // By default, no Worker methods should be called, so use StrictMock.
+    // Override this behavior for specific methods in the individual test cases.
+    worker_.reset(new testing::StrictMock<MockWorker>);
+    model_.reset(new Model(worker_.get()));
+
+    EXPECT_CALL(*worker_, Lock()).WillRepeatedly(Return(2));
+    EXPECT_CALL(*worker_, Unlock()).WillRepeatedly(Return(1));
+  }
+
+  virtual void TearDown() {
+    AppManager::DeleteInstance();
+  }
+
+  const bool is_machine_;
+  scoped_ptr<MockWorker> worker_;
+  scoped_ptr<Model> model_;
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(AppBundleNoBundleTest);
+};
+
+class AppBundleTest : public AppBundleNoBundleTest {
+ protected:
+  explicit AppBundleTest(bool is_machine) : AppBundleNoBundleTest(is_machine) {}
+
+  static void SetUpTestCase() {
+    EXPECT_EQ(STATE_INIT,         fsm::AppBundleState::STATE_INIT);
+    EXPECT_EQ(STATE_INITIALIZED,  fsm::AppBundleState::STATE_INITIALIZED);
+    EXPECT_EQ(STATE_BUSY,         fsm::AppBundleState::STATE_BUSY);
+    EXPECT_EQ(STATE_READY,        fsm::AppBundleState::STATE_READY);
+    EXPECT_EQ(STATE_PAUSED,       fsm::AppBundleState::STATE_PAUSED);
+    EXPECT_EQ(STATE_STOPPED,      fsm::AppBundleState::STATE_STOPPED);
+  }
+
+  virtual void SetUp() {
+    AppBundleNoBundleTest::SetUp();
+    app_bundle_ = model_->CreateAppBundle(is_machine_);
+    ASSERT_TRUE(app_bundle_.get());
+  }
+
+  virtual void TearDown() {
+    app_bundle_.reset();
+    AppBundleNoBundleTest::TearDown();
+  }
+
+  void TestPropertyReflexiveness() {
+    EXPECT_SUCCEEDED(app_bundle_->put_displayName(CComBSTR(_T("My Apps"))));
+    CComBSTR name(_T(""));
+    EXPECT_SUCCEEDED(app_bundle_->get_displayName(&name));
+    EXPECT_STREQ(_T("My Apps"), name);
+
+    EXPECT_SUCCEEDED(app_bundle_->put_displayLanguage(CComBSTR(_T("en"))));
+    CComBSTR lang(_T(""));
+    EXPECT_SUCCEEDED(app_bundle_->get_displayLanguage(&lang));
+    EXPECT_STREQ(_T("en"), lang);
+
+    EXPECT_SUCCEEDED(app_bundle_->put_installSource(CComBSTR(_T("unittest"))));
+    CComBSTR source(_T(""));
+    EXPECT_SUCCEEDED(app_bundle_->get_installSource(&source));
+    EXPECT_STREQ(_T("unittest"), source);
+
+    EXPECT_SUCCEEDED(app_bundle_->put_priority(INSTALL_PRIORITY_LOW));
+    long priority = INSTALL_PRIORITY_HIGH;  // NOLINT
+    EXPECT_SUCCEEDED(app_bundle_->get_priority(&priority));
+    EXPECT_EQ(INSTALL_PRIORITY_LOW, priority);
+  }
+
+  // A copy of the protected enum in AppBundleState, allowing the individual
+  // tests to use these values.
+  enum BundleState {
+    STATE_INIT,
+    STATE_INITIALIZED,
+    STATE_BUSY,
+    STATE_READY,
+    STATE_PAUSED,
+    STATE_STOPPED,
+  };
+
+  BundleState GetBundleState() {
+    return static_cast<BundleState>(app_bundle_->app_bundle_state_->state_);
+  }
+
+  shared_ptr<AppBundle> app_bundle_;
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(AppBundleTest);
+};
+
+class AppBundleUninitializedTest : public AppBundleTest {
+ protected:
+  explicit AppBundleUninitializedTest(bool is_machine)
+      : AppBundleTest(is_machine) {}
+
+  void TestPropertyDefaults() {
+    CComBSTR name;
+    EXPECT_SUCCEEDED(app_bundle_->get_displayName(&name));
+    EXPECT_STREQ(_T(""), name);
+
+    CComBSTR lang;
+    EXPECT_SUCCEEDED(app_bundle_->get_displayLanguage(&lang));
+    EXPECT_STREQ(lang::GetDefaultLanguage(is_machine_), lang);
+
+    CComBSTR install_source;
+    EXPECT_SUCCEEDED(app_bundle_->get_installSource(&install_source));
+    EXPECT_STREQ(_T("unknown"), install_source);
+
+    CComBSTR offline_dir;
+    EXPECT_SUCCEEDED(app_bundle_->get_offlineDirectory(&offline_dir));
+    EXPECT_STREQ(_T(""), offline_dir);
+
+    long priority;  // NOLINT
+    EXPECT_SUCCEEDED(app_bundle_->get_priority(&priority));
+    EXPECT_EQ(INSTALL_PRIORITY_HIGH, priority);
+  }
+};
+
+class AppBundleUninitializedMachineTest : public AppBundleUninitializedTest {
+ protected:
+  AppBundleUninitializedMachineTest() : AppBundleUninitializedTest(true) {}
+};
+
+class AppBundleUninitializedUserTest : public AppBundleUninitializedTest {
+ protected:
+  AppBundleUninitializedUserTest() : AppBundleUninitializedTest(false) {}
+};
+
+TEST_F(AppBundleUninitializedMachineTest, Properties) {
+  TestPropertyDefaults();
+  TestPropertyReflexiveness();
+}
+
+TEST_F(AppBundleUninitializedUserTest, Properties) {
+  TestPropertyDefaults();
+  TestPropertyReflexiveness();
+}
+
+class AppBundleInitializedTest : public AppBundleTest {
+ protected:
+  explicit AppBundleInitializedTest(bool is_machine)
+      : AppBundleTest(is_machine) {}
+
+  virtual void SetUp() {
+    AppBundleTest::SetUp();
+    // TODO(omaha): UserRights::GetCallerToken() fails with ERROR_NO_TOKEN
+    // when initialize() is called for a machine bundle during these tests.
+    // It might make sense to move theimpersonation stuff to the COM wrapper,
+    // but I am not sure how this would work with the AppBundleStates.
+    // If we can do something about this, call initialize() in all cases.
+    if (is_machine_) {
+      SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                   new fsm::AppBundleStateInitialized);
+    } else {
+      EXPECT_SUCCEEDED(app_bundle_->put_displayName(CComBSTR(_T("My Bundle"))));
+      EXPECT_SUCCEEDED(app_bundle_->put_displayLanguage(CComBSTR(_T("en"))));
+      EXPECT_SUCCEEDED(app_bundle_->initialize());
+    }
+  }
+};
+
+class AppBundleNoBundleMachineTest : public AppBundleNoBundleTest {
+ protected:
+  AppBundleNoBundleMachineTest() : AppBundleNoBundleTest(true) {}
+};
+
+class AppBundleNoBundleUserTest : public AppBundleNoBundleTest {
+ protected:
+  AppBundleNoBundleUserTest() : AppBundleNoBundleTest(false) {}
+};
+
+class AppBundleInitializedMachineTest : public AppBundleInitializedTest {
+ protected:
+  AppBundleInitializedMachineTest() : AppBundleInitializedTest(true) {}
+};
+
+class AppBundleInitializedUserTest : public AppBundleInitializedTest {
+ protected:
+  AppBundleInitializedUserTest() : AppBundleInitializedTest(false) {}
+};
+
+
+// The creation of an app bundle inserts it in the model and increments
+// the server module count.
+TEST_F(AppBundleNoBundleUserTest, ConstructorAndDestructor) {
+  EXPECT_CALL(*worker_, Lock()).WillOnce(Return(2));
+  EXPECT_CALL(*worker_, Unlock()).WillOnce(Return(1));
+
+  shared_ptr<AppBundle> app_bundle(model_->CreateAppBundle(is_machine_));
+  EXPECT_TRUE(app_bundle.get());
+  EXPECT_EQ(1, model_->GetNumberOfAppBundles());
+  EXPECT_EQ(app_bundle.get(), model_->GetAppBundle(0).get());
+  app_bundle.reset();
+  EXPECT_EQ(0, model_->GetNumberOfAppBundles());
+}
+
+TEST_F(AppBundleNoBundleMachineTest, ConstructorAndDestructor) {
+  EXPECT_CALL(*worker_, Lock()).WillOnce(Return(2));
+  EXPECT_CALL(*worker_, Unlock()).WillOnce(Return(1));
+
+  shared_ptr<AppBundle> app_bundle(model_->CreateAppBundle(is_machine_));
+  EXPECT_TRUE(app_bundle.get());
+  EXPECT_EQ(1, model_->GetNumberOfAppBundles());
+  EXPECT_EQ(app_bundle.get(), model_->GetAppBundle(0).get());
+  app_bundle.reset();
+  EXPECT_EQ(0, model_->GetNumberOfAppBundles());
+}
+
+class AppBundlePopulatedRegistryTest : public AppBundleInitializedTest {
+ protected:
+  explicit AppBundlePopulatedRegistryTest(bool is_machine)
+      : AppBundleInitializedTest(is_machine),
+        hive_override_key_name_(kRegistryHiveOverrideRoot) {}
+
+  virtual void SetUp() {
+    AppBundleInitializedTest::SetUp();
+
+    RegKey::DeleteKey(hive_override_key_name_);
+    OverrideRegistryHives(hive_override_key_name_);
+
+    EXPECT_SUCCEEDED(ResourceManager::Create(
+      is_machine_, app_util::GetCurrentModuleDirectory(), _T("en")));
+
+    dummy_app_bundle_for_expected_apps_ = model_->CreateAppBundle(is_machine_);
+    ASSERT_TRUE(dummy_app_bundle_for_expected_apps_.get());
+    // TODO(omaha): Address with the TODO in AppBundleInitializedTest::SetUp.
+    if (is_machine_) {
+      SetAppBundleStateForUnitTest(dummy_app_bundle_for_expected_apps_.get(),
+                                   new fsm::AppBundleStateInitialized);
+    } else {
+      EXPECT_SUCCEEDED(dummy_app_bundle_for_expected_apps_->put_displayName(
+                           CComBSTR(_T("My Bundle"))));
+      EXPECT_SUCCEEDED(dummy_app_bundle_for_expected_apps_->put_displayLanguage(
+                           CComBSTR(_T("en"))));
+      EXPECT_SUCCEEDED(dummy_app_bundle_for_expected_apps_->initialize());
+    }
+  }
+
+  virtual void TearDown() {
+    dummy_app_bundle_for_expected_apps_.reset();
+
+    ResourceManager::Delete();
+
+    RestoreRegistryHives();
+    RegKey::DeleteKey(hive_override_key_name_);
+
+    AppBundleInitializedTest::TearDown();
+  }
+
+  // App will be cleaned up when bundle is destroyed.
+  // This is a hack for creating registry data. Would be nice to have a
+  // different mechanism.
+  App* CreateExpectedApp(const TCHAR* app_id) {
+    App* app = NULL;
+    EXPECT_SUCCEEDED(
+        dummy_app_bundle_for_expected_apps_->createApp(CComBSTR(app_id), &app));
+    ASSERT1(app);
+    return app;
+  }
+
+  void PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(
+      App** expected_app0,
+      App** expected_app1,
+      App** expected_app2) {
+    *expected_app0 = CreateExpectedApp(kGuid1);
+    *expected_app1 = CreateExpectedApp(kGuid2);
+    *expected_app2 = CreateExpectedApp(kGuid3);
+    App* opposite_hive_app1 = CreateExpectedApp(kGuid6);
+    App* opposite_hive_app2 = CreateExpectedApp(kGuid7);
+    omaha::PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(
+        is_machine_,
+        *expected_app0,
+        *expected_app1,
+        *expected_app2,
+        opposite_hive_app1,
+        opposite_hive_app2);
+
+    // Reload install age since registry has been changed.
+    AppManager::Instance()->ReadAppInstallTimeDiff(*expected_app0);
+    AppManager::Instance()->ReadAppInstallTimeDiff(*expected_app1);
+    AppManager::Instance()->ReadAppInstallTimeDiff(*expected_app2);
+    AppManager::Instance()->ReadAppInstallTimeDiff(opposite_hive_app1);
+    AppManager::Instance()->ReadAppInstallTimeDiff(opposite_hive_app2);
+  }
+
+  CString hive_override_key_name_;
+  shared_ptr<AppBundle> dummy_app_bundle_for_expected_apps_;
+  LLock lock_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(AppBundlePopulatedRegistryTest);
+};
+
+class AppBundlePopulatedRegistryMachineTest
+    : public AppBundlePopulatedRegistryTest {
+ protected:
+  AppBundlePopulatedRegistryMachineTest()
+      : AppBundlePopulatedRegistryTest(true) {}
+};
+
+class AppBundlePopulatedRegistryUserTest
+    : public AppBundlePopulatedRegistryTest {
+ protected:
+  AppBundlePopulatedRegistryUserTest()
+      : AppBundlePopulatedRegistryTest(false) {}
+};
+
+TEST_F(AppBundleInitializedUserTest, CountAndItem_NoApps) {
+  EXPECT_EQ(0, app_bundle_->GetNumberOfApps());
+  long num_apps = 0;  // NOLINT
+  EXPECT_SUCCEEDED(app_bundle_->get_Count(&num_apps));
+  EXPECT_EQ(0, num_apps);
+
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(app_bundle_->GetApp(0));
+  }
+  App* app0_obtained = NULL;
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INVALID_INDEX),
+            app_bundle_->get_Item(0, &app0_obtained));
+  EXPECT_FALSE(app0_obtained);
+
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(app_bundle_->GetApp(1));
+  }
+  App* app1_obtained = NULL;
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INVALID_INDEX),
+            app_bundle_->get_Item(1, &app1_obtained));
+  EXPECT_FALSE(app1_obtained);
+}
+
+TEST_F(AppBundleInitializedUserTest, CountAndItem_OneApp) {
+  App* app0_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app0_created));
+
+  EXPECT_EQ(1, app_bundle_->GetNumberOfApps());
+  long num_apps = 0;  // NOLINT
+  EXPECT_SUCCEEDED(app_bundle_->get_Count(&num_apps));
+  EXPECT_EQ(1, num_apps);
+
+  EXPECT_TRUE(app_bundle_->GetApp(0));
+  App* app0_obtained = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->get_Item(0, &app0_obtained));
+  EXPECT_TRUE(app0_obtained);
+
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(app_bundle_->GetApp(1));
+  }
+  App* app1_obtained = NULL;
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INVALID_INDEX),
+            app_bundle_->get_Item(1, &app1_obtained));
+  EXPECT_FALSE(app1_obtained);
+}
+
+TEST_F(AppBundleInitializedUserTest, CountAndItem_TwoApp) {
+  App* app0_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app0_created));
+  App* app1_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid2), &app1_created));
+
+  EXPECT_EQ(2, app_bundle_->GetNumberOfApps());
+  long num_apps = 0;  // NOLINT
+  EXPECT_SUCCEEDED(app_bundle_->get_Count(&num_apps));
+  EXPECT_EQ(2, num_apps);
+
+  EXPECT_TRUE(app_bundle_->GetApp(0));
+  App* app0_obtained = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->get_Item(0, &app0_obtained));
+  EXPECT_TRUE(app0_obtained);
+  EXPECT_EQ(app0_created, app0_obtained);
+
+  EXPECT_TRUE(app_bundle_->GetApp(1));
+  App* app1_obtained = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->get_Item(1, &app1_obtained));
+  EXPECT_TRUE(app1_obtained);
+
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(app_bundle_->GetApp(2));
+  }
+  App* app2_obtained = NULL;
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INVALID_INDEX),
+            app_bundle_->get_Item(2, &app2_obtained));
+  EXPECT_FALSE(app2_obtained);
+}
+
+TEST_F(AppBundleInitializedUserTest, createApp) {
+  App* app0_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app0_created));
+  EXPECT_TRUE(app0_created);
+  EXPECT_EQ(1, app_bundle_->GetNumberOfApps());
+
+  App* app0 = app_bundle_->GetApp(0);
+  EXPECT_EQ(app0_created, app0);
+
+  EXPECT_STREQ(CString(kGuid1).MakeUpper(), app0->app_guid_string());
+  ValidateFreshInstallDefaultValues(*app0);
+}
+
+TEST_F(AppBundleInitializedUserTest, createApp_TwoApps) {
+  App* app0_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app0_created));
+  EXPECT_TRUE(app0_created);
+  EXPECT_EQ(1, app_bundle_->GetNumberOfApps());
+
+  App* app0 = app_bundle_->GetApp(0);
+  EXPECT_EQ(app0_created, app0);
+
+  EXPECT_STREQ(CString(kGuid1).MakeUpper(), app0->app_guid_string());
+  ValidateFreshInstallDefaultValues(*app0);
+
+  // Add a second app to the bundle.
+
+  App* app1_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid2), &app1_created));
+  EXPECT_TRUE(app1_created);
+  EXPECT_EQ(2, app_bundle_->GetNumberOfApps());
+
+  App* app1 = app_bundle_->GetApp(1);
+  EXPECT_EQ(app1_created, app1);
+
+  EXPECT_STREQ(CString(kGuid2).MakeUpper(), app1->app_guid_string());
+  ValidateFreshInstallDefaultValues(*app1);
+}
+
+TEST_F(AppBundleInitializedUserTest, createApp_SameAppTwice) {
+  App* app0_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app0_created));
+  EXPECT_TRUE(app0_created);
+  EXPECT_EQ(1, app_bundle_->GetNumberOfApps());
+
+  App* app0 = app_bundle_->GetApp(0);
+  EXPECT_EQ(app0_created, app0);
+
+  EXPECT_STREQ(CString(kGuid1).MakeUpper(), app0->app_guid_string());
+  ValidateFreshInstallDefaultValues(*app0);
+
+  // Attempt to add the same app to the bundle again.
+
+  App* app1_created = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createApp(CComBSTR(kGuid1), &app1_created));
+  EXPECT_FALSE(app1_created);
+  EXPECT_EQ(1, app_bundle_->GetNumberOfApps());
+}
+
+TEST_F(AppBundleInitializedUserTest, createApp_AfterUpdateCheck) {
+  DummyUserWorkItem dummy_work_item;
+  EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+      .WillOnce(SetWorkItem(&dummy_work_item));
+
+  App* app0_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app0_created));
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+
+  App* app1_created = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createApp(CComBSTR(kGuid2), &app1_created));
+
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+}
+
+TEST_F(AppBundleInitializedMachineTest, createApp) {
+  App* app0_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app0_created));
+  EXPECT_TRUE(app0_created);
+  EXPECT_EQ(1, app_bundle_->GetNumberOfApps());
+
+  App* app0 = app_bundle_->GetApp(0);
+  EXPECT_EQ(app0_created, app0);
+
+  EXPECT_STREQ(CString(kGuid1).MakeUpper(), app0->app_guid_string());
+  ValidateFreshInstallDefaultValues(*app0);
+}
+
+TEST_F(AppBundleInitializedUserTest, checkForUpdate_NoApps) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->checkForUpdate());
+}
+
+// Does not verify the update check occurs.
+TEST_F(AppBundleInitializedUserTest, checkForUpdate_OneApp) {
+  DummyUserWorkItem dummy_work_item;
+  EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+      .WillOnce(SetWorkItem(&dummy_work_item));
+
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app));
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+}
+
+// Does not verify the update check occurs.
+TEST_F(AppBundleInitializedUserTest, checkForUpdate_TwoApps) {
+  DummyUserWorkItem dummy_work_item;
+  EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+      .WillOnce(SetWorkItem(&dummy_work_item));
+
+  App* app0 = NULL;
+  App* app1 = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app0));
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid2), &app1));
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+}
+
+TEST_F(AppBundleInitializedUserTest, checkForUpdate_Twice) {
+  DummyUserWorkItem dummy_work_item;
+  EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+      .WillOnce(SetWorkItem(&dummy_work_item));
+
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app));
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->checkForUpdate());
+}
+
+TEST_F(AppBundleInitializedUserTest, checkForUpdate_WhileBundleIsBusy) {
+  DummyUserWorkItem dummy_work_item;
+  EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+      .WillOnce(SetWorkItem(&dummy_work_item));
+
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app));
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->checkForUpdate());
+
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+}
+
+// Does not verify the update check occurs.
+TEST_F(AppBundleInitializedMachineTest, checkForUpdate_OneApp) {
+  DummyUserWorkItem dummy_work_item;
+  EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+      .WillOnce(SetWorkItem(&dummy_work_item));
+
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app));
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+}
+
+TEST_F(AppBundleInitializedUserTest, download_WithoutUpdateCheck) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->download());
+}
+
+// The AppBundle does not prevent this, but the apps may enter the error state.
+TEST_F(AppBundleInitializedUserTest, download_AfterInstall) {
+  DummyUserWorkItem dummy_work_item;
+  {
+    ::testing::InSequence dummy;
+    EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+        .WillOnce(SetWorkItem(&dummy_work_item));
+    EXPECT_CALL(*worker_, DownloadAndInstallAsync(_))
+        .WillOnce(SetWorkItem(&dummy_work_item));
+    EXPECT_CALL(*worker_, DownloadAsync(_))
+        .WillOnce(SetWorkItem(&dummy_work_item));
+  }
+
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app));
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+  EXPECT_SUCCEEDED(app_bundle_->install());
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+
+  EXPECT_SUCCEEDED(app_bundle_->download());
+
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+}
+
+// The AppBundle does not prevent this, but the apps may enter the error state.
+TEST_F(AppBundleInitializedUserTest, download_Twice) {
+  DummyUserWorkItem dummy_work_item;
+  {
+    ::testing::InSequence dummy;
+    EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+        .WillOnce(SetWorkItem(&dummy_work_item));
+    EXPECT_CALL(*worker_, DownloadAsync(_))
+        .Times(2)
+        .WillRepeatedly(SetWorkItem(&dummy_work_item));
+  }
+
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app));
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+  EXPECT_SUCCEEDED(app_bundle_->download());
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+
+  EXPECT_SUCCEEDED(app_bundle_->download());
+
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+}
+
+// Simulates the update check still in progress when download() is called.
+TEST_F(AppBundleInitializedUserTest, download_WhileBundleIsBusy) {
+  DummyUserWorkItem dummy_work_item;
+  EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+      .WillOnce(SetWorkItem(&dummy_work_item));
+
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app));
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+
+  EXPECT_EQ(GOOPDATE_E_NON_BLOCKING_CALL_PENDING, app_bundle_->download());
+
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+}
+
+// The AppBundle does not prevent this, but the apps may enter the error state.
+TEST_F(AppBundleInitializedMachineTest, download_Twice) {
+  DummyUserWorkItem dummy_work_item;
+  {
+    ::testing::InSequence dummy;
+    EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+        .WillOnce(SetWorkItem(&dummy_work_item));
+    EXPECT_CALL(*worker_, DownloadAsync(_))
+        .Times(2)
+        .WillRepeatedly(SetWorkItem(&dummy_work_item));
+  }
+
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app));
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+  EXPECT_SUCCEEDED(app_bundle_->download());
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+
+  EXPECT_SUCCEEDED(app_bundle_->download());
+
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+}
+
+TEST_F(AppBundleInitializedUserTest, install_WithoutUpdateCheck) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->install());
+}
+
+TEST_F(AppBundleInitializedUserTest, install_WithoutDownload) {
+  DummyUserWorkItem dummy_work_item;
+  {
+    ::testing::InSequence dummy;
+    EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+        .WillOnce(SetWorkItem(&dummy_work_item));
+    EXPECT_CALL(*worker_, DownloadAndInstallAsync(_))
+        .WillOnce(SetWorkItem(&dummy_work_item));
+  }
+
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app));
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+
+  EXPECT_SUCCEEDED(app_bundle_->install());
+
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+}
+
+TEST_F(AppBundleInitializedUserTest, install_AfterDownload) {
+  DummyUserWorkItem dummy_work_item;
+  {
+    ::testing::InSequence dummy;
+    EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+        .WillOnce(SetWorkItem(&dummy_work_item));
+    EXPECT_CALL(*worker_, DownloadAsync(_))
+        .WillOnce(SetWorkItem(&dummy_work_item));
+    EXPECT_CALL(*worker_, DownloadAndInstallAsync(_))
+        .WillOnce(SetWorkItem(&dummy_work_item));
+  }
+
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app));
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+  EXPECT_SUCCEEDED(app_bundle_->download());
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+
+  EXPECT_SUCCEEDED(app_bundle_->install());
+
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+}
+
+// The AppBundle does not prevent this, but the apps may enter the error state.
+TEST_F(AppBundleInitializedUserTest, install_Twice) {
+  DummyUserWorkItem dummy_work_item;
+  {
+    ::testing::InSequence dummy;
+    EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+        .WillOnce(SetWorkItem(&dummy_work_item));
+    EXPECT_CALL(*worker_, DownloadAndInstallAsync(_))
+        .Times(2)
+        .WillRepeatedly(SetWorkItem(&dummy_work_item));
+  }
+
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app));
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+  EXPECT_SUCCEEDED(app_bundle_->install());
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+
+  EXPECT_SUCCEEDED(app_bundle_->install());
+
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+}
+
+// TODO(omaha): These tests are disabled because CaptureCallerPrimaryToken()
+// fails. We could move this to AppBundleWrapper, but we would need to expose
+// some functions to AppBundleWrapper.
+TEST_F(AppBundleInitializedMachineTest, DISABLED_install_WithoutDownload) {
+  DummyUserWorkItem dummy_work_item;
+  {
+    ::testing::InSequence dummy;
+    EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+        .WillOnce(SetWorkItem(&dummy_work_item));
+    EXPECT_CALL(*worker_, DownloadAndInstallAsync(_))
+        .WillOnce(SetWorkItem(&dummy_work_item));
+  }
+
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app));
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+
+  EXPECT_SUCCEEDED(app_bundle_->install());
+
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+}
+
+TEST_F(AppBundleInitializedMachineTest, DISABLED_install_AfterDownload) {
+  DummyUserWorkItem dummy_work_item;
+  {
+    ::testing::InSequence dummy;
+    EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+        .WillOnce(SetWorkItem(&dummy_work_item));
+    EXPECT_CALL(*worker_, DownloadAsync(_))
+        .WillOnce(SetWorkItem(&dummy_work_item));
+    EXPECT_CALL(*worker_, DownloadAndInstallAsync(_))
+        .WillOnce(SetWorkItem(&dummy_work_item));
+  }
+
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app));
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+  EXPECT_SUCCEEDED(app_bundle_->download());
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+
+  EXPECT_SUCCEEDED(app_bundle_->install());
+
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+}
+
+// Simulates the update check still in progress when install is called.
+TEST_F(AppBundleInitializedUserTest, install_WhileBundleIsBusy) {
+  DummyUserWorkItem dummy_work_item;
+  EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+      .WillOnce(SetWorkItem(&dummy_work_item));
+
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app));
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+  EXPECT_EQ(GOOPDATE_E_NON_BLOCKING_CALL_PENDING, app_bundle_->install());
+
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+}
+
+// Also tests adding another app via the same method.
+TEST_F(AppBundlePopulatedRegistryUserTest, createInstalledApp_Present_TwoApps) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(&expected_app0,
+                                                              &expected_app1,
+                                                              &expected_app2);
+
+  App* app0_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createInstalledApp(CComBSTR(kGuid1),
+                                                   &app0_created));
+  EXPECT_TRUE(app0_created);
+  EXPECT_EQ(1, app_bundle_->GetNumberOfApps());
+
+  App* app0 = app_bundle_->GetApp(0);
+  EXPECT_EQ(app0_created, app0);
+  EXPECT_STREQ(CString(kGuid1).MakeUpper(), app0->app_guid_string());
+  EXPECT_SUCCEEDED(expected_app0->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app0, *app0);
+
+  // Add a second app to the bundle.
+
+  App* app1_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createInstalledApp(CComBSTR(kGuid2),
+                                                   &app1_created));
+  EXPECT_TRUE(app1_created);
+  EXPECT_EQ(2, app_bundle_->GetNumberOfApps());
+
+  long num_apps = 0;  // NOLINT
+  EXPECT_SUCCEEDED(app_bundle_->get_Count(&num_apps));
+  EXPECT_EQ(2, num_apps);
+
+  App* app1 = app_bundle_->GetApp(1);
+  EXPECT_EQ(app1_created, app1);
+  EXPECT_STREQ(CString(kGuid2).MakeUpper(), app1->app_guid_string());
+  SetDisplayName(kDefaultAppName, expected_app1);
+  EXPECT_SUCCEEDED(expected_app1->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app1, *app1);
+
+  // Verify COM methods return the same values as the C++ methods used above.
+  EXPECT_SUCCEEDED(app_bundle_->get_Count(&num_apps));
+  EXPECT_EQ(2, num_apps);
+  App* app0_obtained = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->get_Item(0, &app0_obtained));
+  EXPECT_EQ(app0, app0_obtained);
+  App* app1_obtained = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->get_Item(1, &app1_obtained));
+  EXPECT_EQ(app1, app1_obtained);
+}
+
+TEST_F(AppBundlePopulatedRegistryMachineTest, createInstalledApp_Present) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(&expected_app0,
+                                                              &expected_app1,
+                                                              &expected_app2);
+
+  App* app0_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createInstalledApp(CComBSTR(kGuid1),
+                                                   &app0_created));
+  EXPECT_TRUE(app0_created);
+  EXPECT_EQ(1, app_bundle_->GetNumberOfApps());
+
+  App* app0 = app_bundle_->GetApp(0);
+  EXPECT_EQ(app0_created, app0);
+  EXPECT_STREQ(CString(kGuid1).MakeUpper(), app0->app_guid_string());
+  EXPECT_SUCCEEDED(expected_app0->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app0, *app0);
+
+  long num_apps = 0;  // NOLINT
+  EXPECT_SUCCEEDED(app_bundle_->get_Count(&num_apps));
+  EXPECT_EQ(1, num_apps);
+}
+
+// TODO(omaha3): Test that the same app can be added to different bundles for
+// each of the create methods.
+TEST_F(AppBundlePopulatedRegistryUserTest, createInstalledApp_SameAppTwice) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(&expected_app0,
+                                                              &expected_app1,
+                                                              &expected_app2);
+
+  App* app0_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createInstalledApp(CComBSTR(kGuid1),
+                                                   &app0_created));
+  EXPECT_TRUE(app0_created);
+  EXPECT_EQ(1, app_bundle_->GetNumberOfApps());
+
+  App* app0 = app_bundle_->GetApp(0);
+  EXPECT_EQ(app0_created, app0);
+  EXPECT_STREQ(CString(kGuid1).MakeUpper(), app0->app_guid_string());
+  EXPECT_SUCCEEDED(expected_app0->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app0, *app0);
+
+  // Attempt to add the same app to the bundle again.
+
+  App* app1_created = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createInstalledApp(CComBSTR(kGuid1), &app1_created));
+  EXPECT_FALSE(app1_created);
+  EXPECT_EQ(1, app_bundle_->GetNumberOfApps());
+}
+
+TEST_F(AppBundlePopulatedRegistryUserTest, createInstalledApp_NotPresent) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(&expected_app0,
+                                                              &expected_app1,
+                                                              &expected_app2);
+
+  App* app0_created = NULL;
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            app_bundle_->createInstalledApp(
+                CComBSTR(_T("{2D5F8E16-B56B-496a-BA8B-3A0B5EC17F4F}")),
+                &app0_created));
+  EXPECT_FALSE(app0_created);
+  EXPECT_EQ(0, app_bundle_->GetNumberOfApps());
+}
+
+TEST_F(AppBundlePopulatedRegistryUserTest,
+       createInstalledApp_NoAppsRegistered) {
+  App* app0_created = NULL;
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            app_bundle_->createInstalledApp(CComBSTR(kGuid1), &app0_created));
+  EXPECT_FALSE(app0_created);
+  EXPECT_EQ(0, app_bundle_->GetNumberOfApps());
+}
+
+TEST_F(AppBundlePopulatedRegistryUserTest,
+       createInstalledApp_AfterUpdateCheck) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(&expected_app0,
+                                                              &expected_app1,
+                                                              &expected_app2);
+
+  DummyUserWorkItem dummy_work_item;
+  EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+      .WillOnce(SetWorkItem(&dummy_work_item));
+
+  App* app0_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createInstalledApp(CComBSTR(kGuid1),
+                                                   &app0_created));
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+
+  App* app1_created = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createInstalledApp(CComBSTR(kGuid2), &app1_created));
+}
+
+TEST_F(AppBundlePopulatedRegistryUserTest, createAllInstalledApps) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(&expected_app0,
+                                                              &expected_app1,
+                                                              &expected_app2);
+  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey(
+      AppendRegKeyPath(USER_REG_CLIENT_STATE, kGuid4)));  // Avoid assert.
+
+  // See comment in machine createAllInstalledApps test.
+  const CString incomplete_clients_key =
+      AppendRegKeyPath(USER_REG_CLIENTS, kGuid4);
+  EXPECT_TRUE(RegKey::HasKey(incomplete_clients_key));
+  EXPECT_FALSE(RegKey::HasValue(incomplete_clients_key,
+                                kRegValueProductVersion));
+
+  EXPECT_SUCCEEDED(app_bundle_->createAllInstalledApps());
+  EXPECT_EQ(2, app_bundle_->GetNumberOfApps());
+
+  App* app0 = app_bundle_->GetApp(0);
+  EXPECT_STREQ(CString(kGuid1).MakeUpper(), app0->app_guid_string());
+  EXPECT_SUCCEEDED(expected_app0->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app0, *app0);
+
+  App* app1 = app_bundle_->GetApp(1);
+  EXPECT_STREQ(CString(kGuid2).MakeUpper(), app1->app_guid_string());
+  SetDisplayName(kDefaultAppName, expected_app1);
+  EXPECT_SUCCEEDED(expected_app1->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app1, *app1);
+
+  // Verify COM methods return the same values as the C++ methods used above.
+  long num_registered_apps = 0;  // NOLINT
+  EXPECT_SUCCEEDED(app_bundle_->get_Count(&num_registered_apps));
+  EXPECT_EQ(2, num_registered_apps);
+  App* app0_obtained = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->get_Item(0, &app0_obtained));
+  EXPECT_EQ(app0, app0_obtained);
+  App* app1_obtained = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->get_Item(1, &app1_obtained));
+  EXPECT_EQ(app1, app1_obtained);
+}
+
+TEST_F(AppBundlePopulatedRegistryMachineTest, createAllInstalledApps) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(&expected_app0,
+                                                              &expected_app1,
+                                                              &expected_app2);
+  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey(
+      AppendRegKeyPath(MACHINE_REG_CLIENT_STATE, kGuid4)));  // Avoid assert.
+
+  // An important part of this test is that processing continues and
+  // even though there is a key for an app that is not properly
+  // registered (e.g. it does not have a pv value or has an invalid pv value).
+  // Since this is so important, explicitly check registry was set up correctly.
+  // Invalid pv value type is checked in a separate test since it causes an
+  // assert.
+  // An app without a pv is not added to the bundle.
+  const CString incomplete_clients_key =
+      AppendRegKeyPath(MACHINE_REG_CLIENTS, kGuid4);
+  EXPECT_TRUE(RegKey::HasKey(incomplete_clients_key));
+  EXPECT_FALSE(RegKey::HasValue(incomplete_clients_key,
+                                kRegValueProductVersion));
+
+  EXPECT_SUCCEEDED(app_bundle_->createAllInstalledApps());
+  EXPECT_EQ(2, app_bundle_->GetNumberOfApps());
+
+  App* app0 = app_bundle_->GetApp(0);
+  EXPECT_STREQ(CString(kGuid1).MakeUpper(), app0->app_guid_string());
+  EXPECT_SUCCEEDED(expected_app0->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app0, *app0);
+
+  App* app1 = app_bundle_->GetApp(1);
+  EXPECT_STREQ(CString(kGuid2).MakeUpper(), app1->app_guid_string());
+  SetDisplayName(kDefaultAppName, expected_app1);
+  EXPECT_SUCCEEDED(expected_app1->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app1, *app1);
+
+  // Verify COM methods return the same values as the C++ methods used above.
+  long num_registered_apps = 0;  // NOLINT
+  EXPECT_SUCCEEDED(app_bundle_->get_Count(&num_registered_apps));
+  EXPECT_EQ(2, num_registered_apps);
+  App* app0_obtained = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->get_Item(0, &app0_obtained));
+  EXPECT_EQ(app0, app0_obtained);
+  App* app1_obtained = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->get_Item(1, &app1_obtained));
+  EXPECT_EQ(app1, app1_obtained);
+}
+
+TEST_F(AppBundlePopulatedRegistryUserTest,
+       createAllInstalledApps_InvalidPvValueType) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(&expected_app0,
+                                                              &expected_app1,
+                                                              &expected_app2);
+
+  // Incorrect data type for pv. See comment in machine createAllInstalledApps
+  // test.
+  // It is important that this value is alphabetically before the other AppIds.
+  // This allows us to test that processing continues despite the bad pv.
+  const TCHAR* const kInvalidPvTypeAppId =
+      _T("{0150B619-867C-4985-B193-ED309A23EE36}");
+  const CString invalid_pv_type_clients_key =
+      AppendRegKeyPath(USER_REG_CLIENTS, kInvalidPvTypeAppId);
+  const DWORD kInvalidPvDword = 0x3039;
+  const TCHAR* const kInvalidPvDwordAsString = _T("\x3039");
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(invalid_pv_type_clients_key,
+                                            kRegValueProductVersion,
+                                            kInvalidPvDword));
+  EXPECT_TRUE(RegKey::HasValue(invalid_pv_type_clients_key,
+                               kRegValueProductVersion));
+  DWORD value_type = REG_SZ;
+  EXPECT_SUCCEEDED(RegKey::GetValueType(invalid_pv_type_clients_key,
+                                        kRegValueProductVersion,
+                                        &value_type));
+  EXPECT_NE(REG_SZ, value_type);
+  App* invalid_pv_app = CreateExpectedApp(kInvalidPvTypeAppId);
+  invalid_pv_app->current_version()->set_version(kInvalidPvDwordAsString);
+  invalid_pv_app->set_days_since_last_active_ping(-1);
+  invalid_pv_app->set_days_since_last_roll_call(-1);
+  SetDisplayName(kDefaultAppName, invalid_pv_app);
+
+  {
+    // An assert occurs when reading the wrong value type.
+    ExpectAsserts expect_asserts;
+    EXPECT_SUCCEEDED(app_bundle_->createAllInstalledApps());
+  }
+
+  EXPECT_EQ(3, app_bundle_->GetNumberOfApps());
+
+  // The invalid pv app is added to the bundle with an unreadable pv.
+  App* app0 = app_bundle_->GetApp(0);
+  EXPECT_STREQ(kInvalidPvTypeAppId, app0->app_guid_string());
+  EXPECT_STREQ(kInvalidPvDwordAsString, app0->current_version()->version());
+  EXPECT_SUCCEEDED(invalid_pv_app->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*invalid_pv_app, *app0);
+
+  App* app1 = app_bundle_->GetApp(1);
+  EXPECT_STREQ(CString(kGuid1).MakeUpper(), app1->app_guid_string());
+  EXPECT_SUCCEEDED(expected_app0->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app0, *app1);
+
+  App* app2 = app_bundle_->GetApp(2);
+  EXPECT_STREQ(CString(kGuid2).MakeUpper(), app2->app_guid_string());
+  SetDisplayName(kDefaultAppName, expected_app1);
+  EXPECT_SUCCEEDED(expected_app1->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app1, *app2);
+}
+
+TEST_F(AppBundlePopulatedRegistryUserTest,
+       createAllInstalledApps_NoAppsRegistered) {
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            app_bundle_->createAllInstalledApps());
+  EXPECT_EQ(0, app_bundle_->GetNumberOfApps());
+
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(app_bundle_->GetApp(0));
+  }
+}
+
+TEST_F(AppBundlePopulatedRegistryUserTest,
+       createAllInstalledApps_OneAppRegistered) {
+  CString clients_key_name = AppendRegKeyPath(USER_REG_CLIENTS, kGuid1);
+  EXPECT_SUCCEEDED(RegKey::SetValue(clients_key_name,
+                                    kRegValueProductVersion,
+                                    _T("1.0.0.0")));
+  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey(
+      AppendRegKeyPath(USER_REG_CLIENT_STATE, kGuid1)));  // Avoid assert.
+
+  EXPECT_SUCCEEDED(app_bundle_->createAllInstalledApps());
+  EXPECT_EQ(1, app_bundle_->GetNumberOfApps());
+
+  App* app0 = app_bundle_->GetApp(0);
+  EXPECT_STREQ(CString(kGuid1).MakeUpper(), app0->app_guid_string());
+}
+
+TEST_F(AppBundlePopulatedRegistryUserTest,
+       createAllInstalledApps_AfterUpdateCheck) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(&expected_app0,
+                                                              &expected_app1,
+                                                              &expected_app2);
+  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey(
+      AppendRegKeyPath(USER_REG_CLIENT_STATE, kGuid4)));  // Avoid assert.
+
+  DummyUserWorkItem dummy_work_item;
+  EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+      .WillOnce(SetWorkItem(&dummy_work_item));
+
+  EXPECT_SUCCEEDED(app_bundle_->createAllInstalledApps());
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+  app_bundle_->CompleteAsyncCall();  // Simulate thread completion.
+
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->createAllInstalledApps());
+}
+
+TEST_F(AppBundlePopulatedRegistryUserTest, createApp_After_createInstalledApp) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(&expected_app0,
+                                                              &expected_app1,
+                                                              &expected_app2);
+
+  App* app0_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createInstalledApp(CComBSTR(kGuid1),
+                                                   &app0_created));
+
+  App* app1_created = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createApp(CComBSTR(kGuid2), &app1_created));
+}
+
+TEST_F(AppBundlePopulatedRegistryUserTest, createApp_createAllInstalledApps) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(&expected_app0,
+                                                              &expected_app1,
+                                                              &expected_app2);
+  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey(
+      AppendRegKeyPath(USER_REG_CLIENT_STATE, kGuid4)));  // Avoid assert.
+
+  EXPECT_SUCCEEDED(app_bundle_->createAllInstalledApps());
+
+  App* app0_created = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createApp(CComBSTR(kGuid1), &app0_created));
+}
+
+TEST_F(AppBundleInitializedUserTest, createInstalledApp_After_createApp) {
+  App* app0_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app0_created));
+
+  App* app1_created = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createInstalledApp(CComBSTR(kGuid2), &app1_created));
+}
+
+TEST_F(AppBundlePopulatedRegistryUserTest,
+       createInstalledApp_After_createAllInstalledApps) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(&expected_app0,
+                                                              &expected_app1,
+                                                              &expected_app2);
+  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey(
+      AppendRegKeyPath(USER_REG_CLIENT_STATE, kGuid4)));  // Avoid assert.
+
+  EXPECT_SUCCEEDED(app_bundle_->createAllInstalledApps());
+
+  App* app0_created = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createInstalledApp(CComBSTR(kGuid1), &app0_created));
+}
+
+TEST_F(AppBundleInitializedUserTest, createAllInstalledApps_After_createApp) {
+  App* app0_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app0_created));
+
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->createAllInstalledApps());
+}
+
+TEST_F(AppBundlePopulatedRegistryUserTest,
+       createAllInstalledApps_After_createInstalledApp) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(&expected_app0,
+                                                              &expected_app1,
+                                                              &expected_app2);
+
+  App* app0_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createInstalledApp(CComBSTR(kGuid1),
+                                                   &app0_created));
+
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->createAllInstalledApps());
+}
+
+TEST_F(AppBundlePopulatedRegistryUserTest,
+       createAllInstalledApps_Twice) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(&expected_app0,
+                                                              &expected_app1,
+                                                              &expected_app2);
+
+  App* app0_created = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createInstalledApp(CComBSTR(kGuid1),
+                                                   &app0_created));
+
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->createAllInstalledApps());
+}
+
+// TODO(omaha): Enable if we end up needing such a function.
+#if 0
+TEST_F(AppBundlePopulatedRegistryMachineTest, createUninstalledApps) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(&expected_app0,
+                                                              &expected_app1,
+                                                              &expected_app2);
+
+  EXPECT_SUCCEEDED(app_bundle_->createUninstalledApps());
+  EXPECT_EQ(1, app_bundle_->GetNumberOfApps());
+
+  App* app0 = app_bundle_->GetApp(0);
+  EXPECT_STREQ(kGuid3, app0->app_guid_string());
+  ValidateExpectedValues(*expected_app2, *app0);
+}
+
+TEST_F(AppBundlePopulatedRegistryUserTest, createUninstalledApps) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(&expected_app0,
+                                                              &expected_app1,
+                                                              &expected_app2);
+
+  EXPECT_SUCCEEDED(app_bundle_->createUninstalledApps());
+  EXPECT_EQ(1, app_bundle_->GetNumberOfApps());
+
+  App* app0 = app_bundle_->GetApp(0);
+  EXPECT_STREQ(kGuid3, app0->app_guid_string());
+  ValidateExpectedValues(*expected_app2, *app0);
+}
+#endif
+
+//
+// State tests.
+//
+
+class AppBundleStateUserTest : public AppBundleTest {
+ protected:
+  AppBundleStateUserTest()
+      : AppBundleTest(false),
+        hive_override_key_name_(kRegistryHiveOverrideRoot) {}
+
+  virtual void SetUp() {
+    AppBundleTest::SetUp();
+
+    RegKey::DeleteKey(hive_override_key_name_);
+    OverrideRegistryHives(hive_override_key_name_);
+  }
+
+  virtual void TearDown() {
+    RestoreRegistryHives();
+    RegKey::DeleteKey(hive_override_key_name_);
+
+    AppBundleTest::TearDown();
+  }
+
+  // Writing name avoids needing to create the ResourceManager.
+  void CreateClientsKeyForApp1() {
+    CString clients_key = AppendRegKeyPath(USER_REG_CLIENTS, kGuid1);
+    EXPECT_SUCCEEDED(RegKey::SetValue(clients_key,
+                                      kRegValueProductVersion,
+                                      _T("1.2.3.4")));
+    EXPECT_SUCCEEDED(RegKey::SetValue(clients_key,
+                                      kRegValueAppName,
+                                      _T("Test App")));
+  }
+
+  CString hive_override_key_name_;
+};
+
+
+class AppBundleStateInitUserTest : public AppBundleStateUserTest {
+ protected:
+  AppBundleStateInitUserTest() : AppBundleStateUserTest() {}
+
+  virtual void SetUp() {
+    AppBundleStateUserTest::SetUp();
+
+    // Nothing to do since the budle starts in this state.
+  }
+};
+
+class AppBundleStateInitializedUserTest : public AppBundleStateUserTest {
+ protected:
+  AppBundleStateInitializedUserTest() : AppBundleStateUserTest() {}
+
+  virtual void SetUp() {
+    AppBundleStateUserTest::SetUp();
+    EXPECT_SUCCEEDED(app_bundle_->put_displayName(CComBSTR(_T("My Bundle"))));
+    EXPECT_SUCCEEDED(app_bundle_->put_displayLanguage(CComBSTR(_T("en"))));
+    EXPECT_SUCCEEDED(app_bundle_->initialize());
+  }
+};
+
+class AppBundleStateBusyUserTest : public AppBundleStateUserTest {
+ protected:
+  AppBundleStateBusyUserTest() : AppBundleStateUserTest() {}
+
+  virtual void SetUp() {
+    AppBundleStateUserTest::SetUp();
+
+    DummyUserWorkItem dummy_work_item;
+    app_bundle_->set_user_work_item(&dummy_work_item);
+
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStateBusy);
+  }
+};
+
+class AppBundleStateReadyUserTest : public AppBundleStateUserTest {
+ protected:
+  AppBundleStateReadyUserTest() : AppBundleStateUserTest() {}
+
+  virtual void SetUp() {
+    AppBundleStateUserTest::SetUp();
+    EXPECT_SUCCEEDED(app_bundle_->put_displayName(CComBSTR(_T("My Bundle"))));
+    EXPECT_SUCCEEDED(app_bundle_->put_displayLanguage(CComBSTR(_T("en"))));
+    EXPECT_SUCCEEDED(app_bundle_->initialize());
+
+    // In all cases, the bundle should have at least one app.
+    // The downloadPackage test needs an installed app.
+    CreateClientsKeyForApp1();
+    App* app = NULL;
+    EXPECT_SUCCEEDED(app_bundle_->createInstalledApp(CComBSTR(kGuid1), &app));
+
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStateReady);
+  }
+};
+
+class AppBundleStatePausedUserTest : public AppBundleStateUserTest {
+ protected:
+  AppBundleStatePausedUserTest() : AppBundleStateUserTest() {}
+
+  virtual void SetUp() {
+    AppBundleStateUserTest::SetUp();
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStatePaused);
+  }
+};
+
+class AppBundleStateStoppedUserTest : public AppBundleStateUserTest {
+ protected:
+  AppBundleStateStoppedUserTest() : AppBundleStateUserTest() {}
+
+  virtual void SetUp() {
+    AppBundleStateUserTest::SetUp();
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStateStopped);
+  }
+};
+
+// Init.
+
+TEST_F(AppBundleStateInitUserTest, Properties) {
+  TestPropertyReflexiveness();
+  EXPECT_EQ(STATE_INIT, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitUserTest, CountAndItem) {
+  long num_apps = 0;  // NOLINT
+  EXPECT_SUCCEEDED(app_bundle_->get_Count(&num_apps));
+  EXPECT_EQ(0, num_apps);
+
+  App* app0_obtained = NULL;
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INVALID_INDEX),
+            app_bundle_->get_Item(0, &app0_obtained));
+  EXPECT_FALSE(app0_obtained);
+
+  EXPECT_EQ(STATE_INIT, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitUserTest, put_altTokens) {
+  CAccessToken access_token;
+  access_token.GetProcessToken(TOKEN_DUPLICATE);
+  ULONG_PTR process_token =
+      reinterpret_cast<ULONG_PTR>(access_token.GetHandle());
+  EXPECT_SUCCEEDED(app_bundle_->put_altTokens(process_token,
+                                              process_token,
+                                              ::GetCurrentProcessId()));
+  EXPECT_EQ(STATE_INIT, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitUserTest, initialize) {
+  EXPECT_SUCCEEDED(app_bundle_->put_displayName(CComBSTR(_T("My Bundle"))));
+  EXPECT_SUCCEEDED(app_bundle_->put_displayLanguage(CComBSTR(_T("en"))));
+  EXPECT_SUCCEEDED(app_bundle_->initialize());
+  EXPECT_EQ(STATE_INITIALIZED, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitUserTest, createApp) {
+  App* app = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createApp(CComBSTR(kGuid1), &app));
+  EXPECT_EQ(STATE_INIT, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitUserTest, createInstalledApp) {
+  CreateClientsKeyForApp1();
+
+  App* app = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createInstalledApp(CComBSTR(kGuid1), &app));
+  EXPECT_EQ(STATE_INIT, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitUserTest, createAllInstalledApps) {
+  CreateClientsKeyForApp1();
+
+  App* app = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createAllInstalledApps());
+  EXPECT_EQ(STATE_INIT, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitUserTest, checkForUpdate) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->checkForUpdate());
+  EXPECT_EQ(STATE_INIT, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitUserTest, download) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->download());
+  EXPECT_EQ(STATE_INIT, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitUserTest, install) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->install());
+  EXPECT_EQ(STATE_INIT, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitUserTest, updateAllApps) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->updateAllApps());
+  EXPECT_EQ(STATE_INIT, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitUserTest, stop) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->stop());
+  EXPECT_EQ(STATE_INIT, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitUserTest, pause) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->pause());
+  EXPECT_EQ(STATE_INIT, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitUserTest, resume) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->resume());
+  EXPECT_EQ(STATE_INIT, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitUserTest, isBusy) {
+  VARIANT_BOOL is_busy = VARIANT_TRUE;
+  EXPECT_SUCCEEDED(app_bundle_->isBusy(&is_busy));
+  EXPECT_EQ(VARIANT_FALSE, is_busy);
+  EXPECT_EQ(STATE_INIT, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitUserTest, downloadPackage) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->downloadPackage(CComBSTR(kGuid1),
+                                         CComBSTR(_T("package"))));
+  EXPECT_EQ(STATE_INIT, GetBundleState());
+}
+
+// TODO(omaha): Check the state or remove this function.
+TEST_F(AppBundleStateInitUserTest, get_currentState) {
+  VARIANT current_state;
+  ExpectAsserts expect_asserts;  // Not yet implemented.
+  EXPECT_EQ(E_NOTIMPL, app_bundle_->get_currentState(&current_state));
+  EXPECT_EQ(STATE_INIT, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitUserTest, CompleteAsyncCall) {
+  {
+    ExpectAsserts expect_asserts;
+    app_bundle_->CompleteAsyncCall();
+  }
+
+  EXPECT_EQ(STATE_INIT, GetBundleState());
+}
+
+// Initialized.
+
+TEST_F(AppBundleStateInitializedUserTest, Properties) {
+  TestPropertyReflexiveness();
+  EXPECT_EQ(STATE_INITIALIZED, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitializedUserTest, CountAndItem) {
+  long num_apps = 0;  // NOLINT
+  EXPECT_SUCCEEDED(app_bundle_->get_Count(&num_apps));
+  EXPECT_EQ(0, num_apps);
+
+  App* app0_obtained = NULL;
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INVALID_INDEX),
+            app_bundle_->get_Item(0, &app0_obtained));
+  EXPECT_FALSE(app0_obtained);
+
+  EXPECT_EQ(STATE_INITIALIZED, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitializedUserTest, put_altTokens) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->put_altTokens(1, 2, 3));
+  EXPECT_EQ(STATE_INITIALIZED, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitializedUserTest, initialize) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->initialize());
+  EXPECT_EQ(STATE_INITIALIZED, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitializedUserTest, createApp) {
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app));
+  EXPECT_EQ(STATE_INITIALIZED, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitializedUserTest, createInstalledApp) {
+  CreateClientsKeyForApp1();
+
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createInstalledApp(CComBSTR(kGuid1), &app));
+  EXPECT_EQ(STATE_INITIALIZED, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitializedUserTest, createAllInstalledApps) {
+  CreateClientsKeyForApp1();
+  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey(
+      AppendRegKeyPath(USER_REG_CLIENT_STATE, kGuid1)));  // Avoid assert.
+
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createAllInstalledApps());
+  EXPECT_EQ(STATE_INITIALIZED, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitializedUserTest, checkForUpdate) {
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app));
+  DummyUserWorkItem dummy_work_item;
+  EXPECT_CALL(*worker_, CheckForUpdateAsync(_))
+      .WillOnce(SetWorkItem(&dummy_work_item));
+
+  EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+
+  EXPECT_FALSE(app_bundle_->is_auto_update());
+}
+
+TEST_F(AppBundleStateInitializedUserTest, download) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->download());
+  EXPECT_EQ(STATE_INITIALIZED, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitializedUserTest, install) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->install());
+  EXPECT_EQ(STATE_INITIALIZED, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitializedUserTest, updateAllApps) {
+  CreateClientsKeyForApp1();
+  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey(
+      AppendRegKeyPath(USER_REG_CLIENT_STATE, kGuid1)));  // Avoid assert.
+
+  DummyUserWorkItem dummy_work_item;
+  EXPECT_CALL(*worker_, UpdateAllAppsAsync(_))
+      .WillOnce(SetWorkItem(&dummy_work_item));
+
+  EXPECT_SUCCEEDED(app_bundle_->updateAllApps());
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+
+  EXPECT_TRUE(app_bundle_->is_auto_update());
+}
+
+TEST_F(AppBundleStateInitializedUserTest, stop) {
+  EXPECT_SUCCEEDED(app_bundle_->stop());
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitializedUserTest, pause) {
+  EXPECT_SUCCEEDED(app_bundle_->pause());
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitializedUserTest, resume) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->resume());
+  EXPECT_EQ(STATE_INITIALIZED, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitializedUserTest, isBusy) {
+  VARIANT_BOOL is_busy = VARIANT_TRUE;
+  EXPECT_SUCCEEDED(app_bundle_->isBusy(&is_busy));
+  EXPECT_EQ(VARIANT_FALSE, is_busy);
+  EXPECT_EQ(STATE_INITIALIZED, GetBundleState());
+}
+
+// downloadPackage() returns E_INVALIDARG because the app has no packages.
+// TODO(omaha): Add the package so downloadPackage can succeed.
+TEST_F(AppBundleStateInitializedUserTest, downloadPackage) {
+  CreateClientsKeyForApp1();
+  App* app = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createInstalledApp(CComBSTR(kGuid1), &app));
+
+  EXPECT_EQ(
+      E_INVALIDARG,
+      app_bundle_->downloadPackage(CComBSTR(kGuid1), CComBSTR(_T("package"))));
+  EXPECT_EQ(STATE_INITIALIZED, GetBundleState());
+
+  EXPECT_FALSE(app_bundle_->is_auto_update());
+}
+
+TEST_F(AppBundleStateInitializedUserTest, get_currentState) {
+  VARIANT current_state;
+  ExpectAsserts expect_asserts;  // Not yet implemented.
+  EXPECT_EQ(E_NOTIMPL, app_bundle_->get_currentState(&current_state));
+  EXPECT_EQ(STATE_INITIALIZED, GetBundleState());
+}
+
+TEST_F(AppBundleStateInitializedUserTest, CompleteAsyncCall) {
+  {
+    ExpectAsserts expect_asserts;
+    app_bundle_->CompleteAsyncCall();
+  }
+
+  EXPECT_EQ(STATE_INITIALIZED, GetBundleState());
+}
+
+// Busy.
+
+TEST_F(AppBundleStateBusyUserTest, Properties) {
+  TestPropertyReflexiveness();
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, CountAndItem) {
+  long num_apps = 0;  // NOLINT
+  EXPECT_SUCCEEDED(app_bundle_->get_Count(&num_apps));
+  EXPECT_EQ(0, num_apps);
+
+  App* app0_obtained = NULL;
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INVALID_INDEX),
+            app_bundle_->get_Item(0, &app0_obtained));
+  EXPECT_FALSE(app0_obtained);
+
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, put_altTokens) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->put_altTokens(1, 2, 3));
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, initialize) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->initialize());
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, createApp) {
+  App* app = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createApp(CComBSTR(kGuid1), &app));
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, createInstalledApp) {
+  CreateClientsKeyForApp1();
+
+  App* app = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createInstalledApp(CComBSTR(kGuid1), &app));
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, createAllInstalledApps) {
+  CreateClientsKeyForApp1();
+
+  App* app = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createAllInstalledApps());
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, checkForUpdate) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->checkForUpdate());
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, download) {
+  EXPECT_EQ(GOOPDATE_E_NON_BLOCKING_CALL_PENDING, app_bundle_->download());
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, install) {
+  EXPECT_EQ(GOOPDATE_E_NON_BLOCKING_CALL_PENDING, app_bundle_->install());
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, updateAllApps) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->updateAllApps());
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, stop_Succeeds) {
+  EXPECT_CALL(*worker_, Stop(app_bundle_.get()))
+      .WillOnce(Return(S_OK));
+
+  EXPECT_SUCCEEDED(app_bundle_->stop());
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, stop_Fails) {
+  EXPECT_CALL(*worker_, Stop(app_bundle_.get()))
+      .WillOnce(Return(kKnownError));
+
+  EXPECT_EQ(kKnownError, app_bundle_->stop());
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, pause_Succeeds) {
+  EXPECT_CALL(*worker_, Pause(app_bundle_.get()))
+      .WillOnce(Return(S_OK));
+
+  EXPECT_EQ(S_OK, app_bundle_->pause());
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, pause_Fails) {
+  EXPECT_CALL(*worker_, Pause(_))
+      .WillOnce(Return(kKnownError));
+
+  EXPECT_EQ(kKnownError, app_bundle_->pause());
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, resume) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->resume());
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, isBusy) {
+  VARIANT_BOOL is_busy = VARIANT_TRUE;
+  EXPECT_SUCCEEDED(app_bundle_->isBusy(&is_busy));
+  EXPECT_EQ(VARIANT_TRUE, is_busy);
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, downloadPackage) {
+  EXPECT_EQ(GOOPDATE_E_NON_BLOCKING_CALL_PENDING,
+            app_bundle_->downloadPackage(CComBSTR(kGuid1),
+                                         CComBSTR(_T("package"))));
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, get_currentState) {
+  VARIANT current_state;
+  ExpectAsserts expect_asserts;  // Not yet implemented.
+  EXPECT_EQ(E_NOTIMPL, app_bundle_->get_currentState(&current_state));
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStateBusyUserTest, CompleteAsyncCall) {
+  app_bundle_->CompleteAsyncCall();
+
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+// Ready.
+
+TEST_F(AppBundleStateReadyUserTest, Properties) {
+  TestPropertyReflexiveness();
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+TEST_F(AppBundleStateReadyUserTest, CountAndItem) {
+  long num_apps = 0;  // NOLINT
+  EXPECT_SUCCEEDED(app_bundle_->get_Count(&num_apps));
+  EXPECT_EQ(1, num_apps);
+
+  App* app0_obtained = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->get_Item(0, &app0_obtained));
+  EXPECT_TRUE(app0_obtained);
+
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+TEST_F(AppBundleStateReadyUserTest, put_altTokens) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->put_altTokens(1, 2, 3));
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+TEST_F(AppBundleStateReadyUserTest, initialize) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->initialize());
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+TEST_F(AppBundleStateReadyUserTest, createApp) {
+  App* app = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createApp(CComBSTR(kGuid1), &app));
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+TEST_F(AppBundleStateReadyUserTest, createInstalledApp) {
+  CreateClientsKeyForApp1();
+
+  App* app = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createInstalledApp(CComBSTR(kGuid1), &app));
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+TEST_F(AppBundleStateReadyUserTest, createAllInstalledApps) {
+  CreateClientsKeyForApp1();
+
+  App* app = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createAllInstalledApps());
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+TEST_F(AppBundleStateReadyUserTest, checkForUpdate) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->checkForUpdate());
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+TEST_F(AppBundleStateReadyUserTest, download) {
+  DummyUserWorkItem dummy_work_item;
+  EXPECT_CALL(*worker_, DownloadAsync(_))
+      .WillOnce(SetWorkItem(&dummy_work_item));
+
+  EXPECT_SUCCEEDED(app_bundle_->download());
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+
+  EXPECT_FALSE(app_bundle_->is_auto_update());
+}
+
+TEST_F(AppBundleStateReadyUserTest, install) {
+  DummyUserWorkItem dummy_work_item;
+  EXPECT_CALL(*worker_, DownloadAndInstallAsync(_))
+      .WillOnce(SetWorkItem(&dummy_work_item));
+
+  EXPECT_SUCCEEDED(app_bundle_->install());
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+
+  EXPECT_FALSE(app_bundle_->is_auto_update());
+}
+
+TEST_F(AppBundleStateReadyUserTest, updateAllApps) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->updateAllApps());
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+TEST_F(AppBundleStateReadyUserTest, stop) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->stop());
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+TEST_F(AppBundleStateReadyUserTest, pause) {
+  EXPECT_SUCCEEDED(app_bundle_->pause());
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+TEST_F(AppBundleStateReadyUserTest, resume) {
+  EXPECT_SUCCEEDED(app_bundle_->resume());
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+TEST_F(AppBundleStateReadyUserTest, isBusy) {
+  VARIANT_BOOL is_busy = VARIANT_TRUE;
+  EXPECT_SUCCEEDED(app_bundle_->isBusy(&is_busy));
+  EXPECT_EQ(VARIANT_FALSE, is_busy);
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+// downloadPackage() returns E_INVALIDARG because the app has no packages.
+// TODO(omaha): Add the package so downloadPackage can succeed.
+TEST_F(AppBundleStateReadyUserTest, downloadPackage) {
+  EXPECT_EQ(
+      E_INVALIDARG,
+      app_bundle_->downloadPackage(CComBSTR(kGuid1), CComBSTR(_T("package"))));
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+TEST_F(AppBundleStateReadyUserTest, get_currentState) {
+  VARIANT current_state;
+  ExpectAsserts expect_asserts;  // Not yet implemented.
+  EXPECT_EQ(E_NOTIMPL, app_bundle_->get_currentState(&current_state));
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+TEST_F(AppBundleStateReadyUserTest, CompleteAsyncCall) {
+  {
+    ExpectAsserts expect_asserts;
+    app_bundle_->CompleteAsyncCall();
+  }
+
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+// Paused.
+
+TEST_F(AppBundleStatePausedUserTest, Properties) {
+  TestPropertyReflexiveness();
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, CountAndItem) {
+  long num_apps = 0;  // NOLINT
+  EXPECT_SUCCEEDED(app_bundle_->get_Count(&num_apps));
+  EXPECT_EQ(0, num_apps);
+
+  App* app0_obtained = NULL;
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INVALID_INDEX),
+            app_bundle_->get_Item(0, &app0_obtained));
+  EXPECT_FALSE(app0_obtained);
+
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, put_altTokens) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->put_altTokens(1, 2, 3));
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, initialize) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->initialize());
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, createApp) {
+  App* app = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createApp(CComBSTR(kGuid1), &app));
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, createInstalledApp) {
+  CreateClientsKeyForApp1();
+
+  App* app = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createInstalledApp(CComBSTR(kGuid1), &app));
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, createAllInstalledApps) {
+  CreateClientsKeyForApp1();
+
+  App* app = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createAllInstalledApps());
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, checkForUpdate) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->checkForUpdate());
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, download) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->download());
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, install) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->install());
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, updateAllApps) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->updateAllApps());
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+// This may change when implemented.
+TEST_F(AppBundleStatePausedUserTest, stop) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->stop());
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, pause) {
+  EXPECT_SUCCEEDED(app_bundle_->pause());
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, resume_Succeeds_AsyncCallNotCompleted) {
+  EXPECT_CALL(*worker_, Resume(app_bundle_.get()))
+      .WillOnce(Return(S_OK));
+
+  EXPECT_SUCCEEDED(app_bundle_->resume());
+  EXPECT_EQ(STATE_BUSY, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, resume_Succeeds_AsyncCallCompleted) {
+  DummyUserWorkItem dummy_work_item;
+  app_bundle_->set_user_work_item(&dummy_work_item);
+  app_bundle_->CompleteAsyncCall();
+
+  EXPECT_CALL(*worker_, Resume(app_bundle_.get()))
+      .WillOnce(Return(S_OK));
+
+  EXPECT_SUCCEEDED(app_bundle_->resume());
+  EXPECT_EQ(STATE_READY, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, resume_Fails_AsyncCallNotCompleted) {
+  EXPECT_CALL(*worker_, Resume(app_bundle_.get()))
+      .WillOnce(Return(kKnownError));
+
+  EXPECT_EQ(kKnownError, app_bundle_->resume());
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, resume_Fails_AsyncCallCompleted) {
+  DummyUserWorkItem dummy_work_item;
+  app_bundle_->set_user_work_item(&dummy_work_item);
+  app_bundle_->CompleteAsyncCall();
+
+  EXPECT_CALL(*worker_, Resume(app_bundle_.get()))
+      .WillOnce(Return(kKnownError));
+
+  EXPECT_EQ(kKnownError, app_bundle_->resume());
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, isBusy) {
+  VARIANT_BOOL is_busy = VARIANT_TRUE;
+  EXPECT_SUCCEEDED(app_bundle_->isBusy(&is_busy));
+  EXPECT_EQ(VARIANT_FALSE, is_busy);
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, downloadPackage) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->downloadPackage(CComBSTR(kGuid1),
+                                         CComBSTR(_T("package"))));
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+TEST_F(AppBundleStatePausedUserTest, get_currentState) {
+  VARIANT current_state;
+  ExpectAsserts expect_asserts;  // Not yet implemented.
+  EXPECT_EQ(E_NOTIMPL, app_bundle_->get_currentState(&current_state));
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+// Remains Paused until resumed.
+TEST_F(AppBundleStatePausedUserTest, CompleteAsyncCall) {
+  DummyUserWorkItem dummy_work_item;
+  app_bundle_->set_user_work_item(&dummy_work_item);
+  SetAppBundleStateForUnitTest(app_bundle_.get(),
+                               new fsm::AppBundleStatePaused);
+
+  app_bundle_->CompleteAsyncCall();
+
+  EXPECT_EQ(STATE_PAUSED, GetBundleState());
+}
+
+// Stopped.
+
+TEST_F(AppBundleStateStoppedUserTest, Properties) {
+  TestPropertyReflexiveness();
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateStoppedUserTest, CountAndItem) {
+  long num_apps = 0;  // NOLINT
+  EXPECT_SUCCEEDED(app_bundle_->get_Count(&num_apps));
+  EXPECT_EQ(0, num_apps);
+
+  App* app0_obtained = NULL;
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INVALID_INDEX),
+            app_bundle_->get_Item(0, &app0_obtained));
+  EXPECT_FALSE(app0_obtained);
+
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateStoppedUserTest, put_altTokens) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->put_altTokens(1, 2, 3));
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateStoppedUserTest, initialize) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->initialize());
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateStoppedUserTest, createApp) {
+  App* app = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createApp(CComBSTR(kGuid1), &app));
+
+  EXPECT_EQ(S_OK, app_bundle_->stop());
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateStoppedUserTest, createInstalledApp) {
+  CreateClientsKeyForApp1();
+
+  App* app = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createInstalledApp(CComBSTR(kGuid1), &app));
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateStoppedUserTest, createAllInstalledApps) {
+  CreateClientsKeyForApp1();
+
+  App* app = NULL;
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->createAllInstalledApps());
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateStoppedUserTest, checkForUpdate) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->checkForUpdate());
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateStoppedUserTest, download) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->download());
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateStoppedUserTest, install) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->install());
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateStoppedUserTest, updateAllApps) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->updateAllApps());
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateStoppedUserTest, stop) {
+  EXPECT_EQ(S_OK, app_bundle_->stop());
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateStoppedUserTest, pause) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->pause());
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateStoppedUserTest, resume) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED, app_bundle_->resume());
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateStoppedUserTest, isBusy) {
+  VARIANT_BOOL is_busy = VARIANT_TRUE;
+  EXPECT_SUCCEEDED(app_bundle_->isBusy(&is_busy));
+  EXPECT_EQ(VARIANT_FALSE, is_busy);
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateStoppedUserTest, downloadPackage) {
+  EXPECT_EQ(GOOPDATE_E_CALL_UNEXPECTED,
+            app_bundle_->downloadPackage(CComBSTR(kGuid1),
+                                         CComBSTR(_T("package"))));
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateStoppedUserTest, get_currentState) {
+  VARIANT current_state;
+  ExpectAsserts expect_asserts;  // Not yet implemented.
+  EXPECT_EQ(E_NOTIMPL, app_bundle_->get_currentState(&current_state));
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+TEST_F(AppBundleStateStoppedUserTest, CompleteAsyncCall) {
+  DummyUserWorkItem dummy_work_item;
+  app_bundle_->set_user_work_item(&dummy_work_item);
+
+  app_bundle_->CompleteAsyncCall();
+
+  EXPECT_EQ(STATE_STOPPED, GetBundleState());
+}
+
+}  // namespace omaha
diff --git a/goopdate/app_command.cc b/goopdate/app_command.cc
new file mode 100644
index 0000000..9757e1e
--- /dev/null
+++ b/goopdate/app_command.cc
@@ -0,0 +1,454 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_command.h"
+
+#include "base/scoped_ptr.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/exception_barrier.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/system.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/ping.h"
+#include "omaha/common/ping_event.h"
+
+namespace omaha {
+
+namespace {
+
+// Sends a single ping event to the Omaha server, synchronously.
+void SendPing(const CString& app_guid,
+              bool is_machine,
+              const CString& session_id,
+              PingEvent::Types type,
+              PingEvent::Results result,
+              int error_code,
+              int extra_code) {
+  PingEventPtr ping_event(new PingEvent(type, result, error_code, extra_code));
+
+  Ping ping(is_machine, session_id, kCmdLineInstallSource_OneClick);
+  std::vector<CString> apps;
+  apps.push_back(app_guid);
+  ping.LoadAppDataFromRegistry(apps);
+  ping.BuildAppsPing(ping_event);
+  ping.Send(true);  // true == is_fire_and_forget
+}
+
+// Waits on a process to exit, sends a ping based on the outcome.
+// This is a COM object so that, during its lifetime, the process will not exit.
+// The instance is AddRef'd in the instantiating thread and Release'd by the
+// thread procedure when all work is completed.
+class ATL_NO_VTABLE CompletePingSender
+    : public CComObjectRootEx<CComMultiThreadModel>,
+      public IUnknown {
+ public:
+  // Starts a wait on a process, belonging to the specified app and having the
+  // given reporting ID. Will send a ping when the process exits.
+  static void Start(const CString& app_guid,
+                    bool is_machine,
+                    const CString& session_id,
+                    int reporting_id,
+                    HANDLE process);
+
+  BEGIN_COM_MAP(CompletePingSender)
+  END_COM_MAP()
+
+ protected:
+  CompletePingSender();
+  virtual ~CompletePingSender();
+
+ private:
+  static HRESULT Create(const CString& app_guid,
+                        bool is_machine,
+                        const CString& session_id,
+                        int reporting_id,
+                        HANDLE process,
+                        CompletePingSender** sender);
+
+  // Sends an EVENT_APP_COMMAND_COMPLETE ping with data from member
+  // variables and parameters.
+  void SendCompletePing(PingEvent::Results result, int error_code);
+
+  // Waits for the process to exit, returning S_OK and the exit_code or the
+  // underlying error code if the wait fails.
+  HRESULT WaitForProcessExit(DWORD* exit_code);
+
+  // Waits until the process exits or timeout occurs, then sends a ping with
+  // the result. parameter is the CompletePingSender instance.
+  static DWORD WINAPI WaitFunction(void* parameter);
+
+  CString app_guid_;
+  bool is_machine_;
+  CString session_id_;
+  int reporting_id_;
+  scoped_process process_;
+
+  DISALLOW_COPY_AND_ASSIGN(CompletePingSender);
+};  // class CompletePingSender
+
+CompletePingSender::CompletePingSender() {
+}
+
+HRESULT CompletePingSender::Create(const CString& app_guid,
+                                   bool is_machine,
+                                   const CString& session_id,
+                                   int reporting_id,
+                                   HANDLE process,
+                                   CompletePingSender** sender) {
+  ASSERT1(process && sender);
+
+  scoped_process process_handle(process);
+  process = NULL;
+
+  typedef CComObject<CompletePingSender> ComObjectCompletePingSender;
+
+  scoped_ptr<ComObjectCompletePingSender> new_object;
+  HRESULT hr = ComObjectCompletePingSender::CreateInstance(address(new_object));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  new_object->app_guid_ = app_guid;
+  new_object->is_machine_ = is_machine;
+  new_object->session_id_ = session_id;
+  new_object->reporting_id_ = reporting_id;
+  reset(new_object->process_, release(process_handle));
+
+  new_object->AddRef();
+  *sender = new_object.release();
+  return S_OK;
+}
+
+CompletePingSender::~CompletePingSender() {
+}
+
+void CompletePingSender::Start(const CString& app_guid,
+                               bool is_machine,
+                               const CString& session_id,
+                               int reporting_id,
+                               HANDLE process) {
+  ASSERT1(process);
+
+  scoped_process process_handle(process);
+  process = NULL;
+
+  CComPtr<CompletePingSender> sender;
+  HRESULT hr = CompletePingSender::Create(app_guid,
+                                          is_machine,
+                                          session_id,
+                                          reporting_id,
+                                          release(process_handle),
+                                          &sender);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[failed to create CompletePingSender]"),
+                  _T("[0x%08x]"), hr));
+    return;
+  }
+
+  void* context =
+      reinterpret_cast<void *>(static_cast<CompletePingSender*>(sender));
+
+  scoped_handle thread(::CreateThread(NULL, 0, WaitFunction, context, 0, NULL));
+
+  if (thread) {
+    // In case of success, the thread is responsible for calling Release.
+    sender.Detach();
+  } else {
+    hr = HRESULTFromLastError();
+    CORE_LOG(LE, (_T("[failed to start wait thread for app command ")
+                  _T("process exit]") _T("[0x%08x]"), hr));
+    sender->SendCompletePing(PingEvent::EVENT_RESULT_ERROR, hr);
+  }
+}
+
+void CompletePingSender::SendCompletePing(PingEvent::Results result,
+                                          int error_code) {
+  SendPing(app_guid_,
+           is_machine_,
+           session_id_,
+           PingEvent::EVENT_APP_COMMAND_COMPLETE,
+           result,
+           error_code,
+           reporting_id_);
+}
+
+HRESULT CompletePingSender::WaitForProcessExit(DWORD* exit_code) {
+  ASSERT1(exit_code);
+  if (!exit_code) {
+    return E_INVALIDARG;
+  }
+
+  DWORD wait_result = ::WaitForSingleObject(get(process_), INFINITE);
+
+  if (wait_result == WAIT_TIMEOUT) {
+    return GOOPDATEINSTALL_E_INSTALLER_TIMED_OUT;
+  } else if (wait_result == WAIT_FAILED) {
+    return HRESULTFromLastError();
+  }
+
+  ASSERT1(wait_result == WAIT_OBJECT_0);
+
+  if (wait_result != WAIT_OBJECT_0) {
+    return E_UNEXPECTED;
+  }
+
+  if (!::GetExitCodeProcess(get(process_), exit_code)) {
+    return HRESULTFromLastError();
+  }
+
+  return S_OK;
+}
+
+DWORD WINAPI CompletePingSender::WaitFunction(void* parameter) {
+  scoped_co_init init_com_apt(COINIT_MULTITHREADED);
+  HRESULT hr = init_com_apt.hresult();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[init_com_apt failed][0x%x]"), hr));
+    return 0;
+  }
+
+  CComPtr<CompletePingSender> instance(
+      reinterpret_cast<CompletePingSender*>(parameter));
+  DWORD exit_code = 0;
+  hr = instance->WaitForProcessExit(&exit_code);
+
+  PingEvent::Results result = PingEvent::EVENT_RESULT_SUCCESS;
+  int error_code = 0;
+
+  if (FAILED(hr)) {
+    result = PingEvent::EVENT_RESULT_ERROR;
+    error_code = hr;
+  } else {
+    switch (exit_code) {
+      case ERROR_SUCCESS_REBOOT_REQUIRED:
+        result = PingEvent::EVENT_RESULT_SUCCESS_REBOOT;
+        break;
+      case ERROR_SUCCESS:
+        result = PingEvent::EVENT_RESULT_SUCCESS;
+        break;
+      default:
+        result = PingEvent::EVENT_RESULT_INSTALLER_ERROR_OTHER;
+        error_code = exit_code;
+        break;
+    }
+  }
+
+  instance->SendCompletePing(result, error_code);
+
+  return 0;
+}
+
+// Attempts to read the command line from the given registry key and value.
+// Logs a message in case of failure.
+HRESULT ReadCommandLine(const CString& key_name,
+                        const CString& value_name,
+                        CString* command_line) {
+  HRESULT hr = RegKey::GetValue(key_name, value_name, command_line);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[failed to read command line]")
+                  _T("[key %s][value %s][0x%08x]"), key_name, value_name, hr));
+  }
+
+  return hr;
+}
+
+// Checks if the specified value exists in the registry under the specified key.
+// If so, attempts to read the value's DWORD contents into 'paramter'. Succeeds
+// iff the value is absent or a DWORD value is successfully read.
+HRESULT ReadCommandParameter(const CString& key_name,
+                             const CString& value_name,
+                             DWORD* parameter) {
+  if (!RegKey::HasValue(key_name, value_name)) {
+    return S_OK;
+  }
+
+  HRESULT hr = RegKey::GetValue(key_name, value_name, parameter);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[failed to read command parameter]")
+                  _T("[key %s][value %s][0x%08x]"), key_name, value_name, hr));
+  }
+
+  return hr;
+}
+
+}  // namespace
+
+AppCommand::AppCommand(const CString& app_guid,
+                       bool is_machine,
+                       const CString& cmd_id,
+                       const CString& cmd_line,
+                       bool sends_pings,
+                       const CString& session_id,
+                       bool is_web_accessible,
+                       DWORD reporting_id)
+  : app_guid_(app_guid),
+    is_machine_(is_machine),
+    cmd_id_(cmd_id),
+    session_id_(session_id),
+    cmd_line_(cmd_line),
+    sends_pings_(sends_pings),
+    reporting_id_(reporting_id),
+    is_web_accessible_(is_web_accessible) {
+}
+
+HRESULT AppCommand::Load(const CString& app_guid,
+                         bool is_machine,
+                         const CString& cmd_id,
+                         const CString& session_id,
+                         AppCommand** app_command) {
+  ASSERT1(app_command);
+
+  CString cmd_line;
+  DWORD sends_pings = 0;
+  DWORD is_web_accessible = 0;
+  DWORD reporting_id = 0;
+
+  ConfigManager* config_manager = ConfigManager::Instance();
+  CString clients_key_name = config_manager->registry_clients(is_machine);
+
+  CString app_key_name(AppendRegKeyPath(clients_key_name, app_guid));
+  CString command_key_name(
+      AppendRegKeyPath(app_key_name, kCommandsRegKeyName, cmd_id));
+
+  // Prefer the new layout, otherwise look for the legacy layout. See comments
+  // in app_command.h for description of each.
+  if (!RegKey::HasKey(command_key_name)) {
+    if (!RegKey::HasValue(app_key_name, cmd_id)) {
+      return GOOPDATE_E_CORE_MISSING_CMD;
+    }
+
+    // Legacy command layout.
+    HRESULT hr = ReadCommandLine(app_key_name, cmd_id, &cmd_line);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  } else {
+    // New command layout.
+    HRESULT hr = ReadCommandLine(command_key_name, kRegValueCommandLine,
+                                 &cmd_line);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = ReadCommandParameter(command_key_name, kRegValueSendsPings,
+                              &sends_pings);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = ReadCommandParameter(command_key_name, kRegValueWebAccessible,
+                              &is_web_accessible);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = ReadCommandParameter(command_key_name, kRegValueReportingId,
+                              &reporting_id);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  *app_command = new AppCommand(app_guid,
+                                is_machine,
+                                cmd_id,
+                                cmd_line,
+                                sends_pings != 0,
+                                session_id,
+                                is_web_accessible != 0,
+                                reporting_id);
+  return S_OK;
+}
+
+HRESULT AppCommand::Execute(HANDLE* process) const {
+  ASSERT1(process);
+  if (!process) {
+    return E_INVALIDARG;
+  }
+
+  *process = NULL;
+
+  CString cmd_line(cmd_line_);
+
+  PROCESS_INFORMATION pi = {0};
+  HRESULT hr = System::StartProcess(NULL, cmd_line.GetBuffer(), &pi);
+
+  if (sends_pings_) {
+    PingEvent::Results result = SUCCEEDED(hr) ?
+        PingEvent::EVENT_RESULT_SUCCESS : PingEvent::EVENT_RESULT_ERROR;
+
+    SendPing(app_guid_,
+             is_machine_,
+             session_id_,
+             PingEvent::EVENT_APP_COMMAND_BEGIN,
+             result,
+             hr,
+             reporting_id_);
+  }
+
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[failed to launch cmd][%s][0x%08x]"), cmd_line_, hr));
+    return hr;
+  }
+
+  ASSERT1(pi.hProcess);
+  VERIFY1(::CloseHandle(pi.hThread));
+
+  *process = pi.hProcess;
+
+  if (sends_pings_) {
+    StartBackgroundThread(pi.hProcess);
+  }
+
+  return S_OK;
+}
+
+// Starts a background thread with a duplicate of the process handle.
+// We need to duplicate the handle because the original handle will be returned
+// to the client.
+void AppCommand::StartBackgroundThread(HANDLE command_process) const {
+  HANDLE duplicate_process = NULL;
+
+  // This is a pseudo handle that need not be closed.
+  HANDLE this_process_handle = ::GetCurrentProcess();
+
+  if (::DuplicateHandle(this_process_handle, command_process,
+                        this_process_handle, &duplicate_process,
+                        NULL, false, DUPLICATE_SAME_ACCESS)) {
+    CompletePingSender::Start(app_guid_,
+                              is_machine_,
+                              session_id_,
+                              reporting_id_,
+                              duplicate_process);
+  } else {
+    CORE_LOG(LE, (_T("[failed call to DuplicateHandle][0x%08x]"),
+                  HRESULTFromLastError()));
+    SendPing(app_guid_,
+             is_machine_,
+             session_id_,
+             PingEvent::EVENT_APP_COMMAND_COMPLETE,
+             PingEvent::EVENT_RESULT_ERROR,
+             HRESULTFromLastError(),
+             reporting_id_);
+  }
+}
+
+}  // namespace omaha
diff --git a/goopdate/app_command.h b/goopdate/app_command.h
new file mode 100644
index 0000000..ba57b7e
--- /dev/null
+++ b/goopdate/app_command.h
@@ -0,0 +1,102 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Apps may define commands using Registry entries. There are two supported
+// formats:
+//
+// Legacy Format:
+// ROOT\\Software\\Google\\Update\\Clients\\{app-guid}
+//   <command-id> = REG_SZ (command line)
+//
+// New Format:
+// ROOT\\Software\\Google\\Update\\Clients\\{app-guid}\\Commands\\<command-id>
+//   CommandLine   = REG_SZ
+//   SendsPings    = DWORD
+//   WebAccessible = DWORD
+//   ReportingId   = DWORD
+//
+// Only the command line is required, all other values default to 0. It is not
+// possible to set other values using the Legacy format.
+
+#ifndef OMAHA_GOOPDATE_APP_COMMAND_H__
+#define OMAHA_GOOPDATE_APP_COMMAND_H__
+
+#include <windows.h>
+#include <string>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+// Loads, provides metadata for, and executes named commands for installed
+// apps.
+class AppCommand {
+ public:
+  static HRESULT Load(const CString& app_guid,
+                      bool is_machine,
+                      const CString& cmd_id,
+                      const CString& session_id,
+                      AppCommand** app_command);
+
+  // Executes the command at the current integrity level. If successful,
+  // the caller is responsible for closing the process HANDLE. This method does
+  // not enforce the 'web accessible' constraint (this is the caller's
+  // responsibility).
+  HRESULT Execute(HANDLE* process) const;
+
+  // Returns true if this command is allowed to be invoked through the
+  // OneClick control.
+  bool is_web_accessible() const { return is_web_accessible_; }
+
+ private:
+  AppCommand(const CString& app_guid,
+             bool is_machine,
+             const CString& cmd_id,
+             const CString& cmd_line,
+             bool sends_pings,
+             const CString& session_id,
+             bool is_web_accessible,
+             DWORD reporting_id);
+
+  // Starts a thread which waits for the process to exit, sends a ping, and then
+  // terminates. Waits up to 15 minutes before aborting the wait. A ping is also
+  // sent if the wait times out or if a failure occurs while starting the
+  // thread, initializing the wait, or retrieving the process exit code.
+  //
+  // Duplicates the process HANDLE, leaving the caller with ownership of the
+  // passed handle.
+  //
+  // The thread holds a COM object until the work is completed in order to
+  // avoid early termination if all other clients of the process release their
+  // references.
+  void StartBackgroundThread(HANDLE process) const;
+
+  // Identifying information.
+  const CString app_guid_;
+  const bool is_machine_;
+  const CString cmd_id_;
+  const CString session_id_;
+
+  // Configuration from the registry.
+  const CString cmd_line_;
+  const bool sends_pings_;
+  const bool is_web_accessible_;
+  const int reporting_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppCommand);
+};  // class AppCommand
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_COMMAND_H__
diff --git a/goopdate/app_command_unittest.cc b/goopdate/app_command_unittest.cc
new file mode 100644
index 0000000..dc7ee8f
--- /dev/null
+++ b/goopdate/app_command_unittest.cc
@@ -0,0 +1,221 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <io.h>
+#include <stdio.h>
+#include <atlstr.h>
+#include <atlsimpstr.h>
+#include <windows.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/file.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/goopdate/app_command.h"
+#include "omaha/goopdate/app_unittest_base.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+const TCHAR* const kAppGuid1 = _T("{3B1A3CCA-0525-4418-93E6-A0DB3398EC9B}");
+
+const TCHAR* const kCmdLineExit0 = _T("cmd.exe /c \"exit 0\"");
+
+const TCHAR* const kCmdId1 = _T("command one");
+const TCHAR* const kCmdId2 = _T("command two");
+
+const TCHAR* const kSessionId = _T("unittest_session_id");
+
+const bool kTrue = true;
+const bool kFalse = false;
+
+const DWORD kOne = 1;
+const DWORD kTwo = 2;
+
+}  // namespace
+
+CString GetEchoCommandLine(CString string, CString output_file) {
+  CString command_line;
+  _sntprintf_s(CStrBuf(command_line, MAX_PATH),
+               MAX_PATH,
+               _TRUNCATE,
+               _T("cmd.exe /c \"echo %s > \"%s\"\""),
+               static_cast<const TCHAR*>(string),
+               static_cast<const TCHAR*>(output_file));
+  return command_line;
+}
+
+class AppCommandTest : public AppTestBaseWithRegistryOverride {
+ protected:
+  // false == is_machine
+  AppCommandTest() : AppTestBaseWithRegistryOverride(false, true) {}
+
+  static void CreateAppClientKey(const CString& guid, bool is_machine) {
+    CString client_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->registry_clients(is_machine), guid);
+
+    RegKey client_key;
+
+    ASSERT_SUCCEEDED(client_key.Create(client_key_name));
+    ASSERT_SUCCEEDED(client_key.SetValue(kRegValueProductVersion,
+                                         _T("1.1.1.3")));
+    ASSERT_SUCCEEDED(client_key.SetValue(kRegValueAppName,
+                                         _T("Dispay Name of ") + guid));
+  }
+
+  static void CreateLegacyCommand(const CString& guid,
+                                  bool is_machine,
+                                  const CString& cmd_id,
+                                  const CString& cmd_line) {
+    CString client_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->registry_clients(is_machine), guid);
+
+    RegKey client_key;
+
+    ASSERT_SUCCEEDED(client_key.Create(client_key_name));
+    ASSERT_SUCCEEDED(client_key.SetValue(cmd_id, cmd_line));
+  }
+
+  static void CreateCommand(const CString& guid,
+                            bool is_machine,
+                            const CString& cmd_id,
+                            const CString& cmd_line,
+                            const bool* sends_pings,
+                            const bool* is_web_accessible,
+                            const DWORD* reporting_id) {
+    CString client_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->registry_clients(is_machine), guid);
+
+    CString command_key_name = AppendRegKeyPath(client_key_name,
+                                                kCommandsRegKeyName,
+                                                cmd_id);
+
+    RegKey command_key;
+
+    ASSERT_SUCCEEDED(command_key.Create(command_key_name));
+    ASSERT_SUCCEEDED(command_key.SetValue(kRegValueCommandLine, cmd_line));
+    if (sends_pings != NULL) {
+      ASSERT_SUCCEEDED(command_key.SetValue(
+          kRegValueSendsPings, static_cast<DWORD>(*sends_pings ? 1 : 0)));
+    }
+    if (is_web_accessible != NULL) {
+      ASSERT_SUCCEEDED(command_key.SetValue(
+          kRegValueWebAccessible,
+          static_cast<DWORD>(*is_web_accessible ? 1 : 0)));
+    }
+    if (reporting_id != NULL) {
+      ASSERT_SUCCEEDED(command_key.SetValue(kRegValueReportingId,
+                                            *reporting_id));
+    }
+  }
+};  // class AppCommandTest
+
+TEST_F(AppCommandTest, NoApp) {
+  scoped_ptr<AppCommand> app_command;
+  ASSERT_FAILED(AppCommand::Load(
+      kAppGuid1, false, kCmdId1, kSessionId, address(app_command)));
+}
+
+TEST_F(AppCommandTest, NoCmd) {
+  scoped_ptr<AppCommand> app_command;
+  CreateAppClientKey(kAppGuid1, false);
+  CreateCommand(
+      kAppGuid1, false, kCmdId1, kCmdLineExit0, &kTrue, &kFalse, &kOne);
+
+  ASSERT_FAILED(AppCommand::Load(
+      kAppGuid1, false, kCmdId2, kSessionId, address(app_command)));
+}
+
+TEST_F(AppCommandTest, WrongLevel) {
+  scoped_ptr<AppCommand> app_command;
+  CreateAppClientKey(kAppGuid1, true);
+  CreateCommand(
+      kAppGuid1, true, kCmdId1, kCmdLineExit0, &kTrue, &kFalse, &kOne);
+
+  ASSERT_FAILED(AppCommand::Load(
+      kAppGuid1, false, kCmdId1, kSessionId, address(app_command)));
+}
+
+TEST_F(AppCommandTest, LoadCommand) {
+  scoped_ptr<AppCommand> app_command;
+  CreateAppClientKey(kAppGuid1, true);
+  CreateCommand(
+      kAppGuid1, true, kCmdId1, kCmdLineExit0, &kTrue, &kFalse, &kOne);
+
+  ASSERT_SUCCEEDED(AppCommand::Load(
+      kAppGuid1, true, kCmdId1, kSessionId, address(app_command)));
+  ASSERT_FALSE(app_command->is_web_accessible());
+}
+
+TEST_F(AppCommandTest, LoadCommandDefaultValues) {
+  scoped_ptr<AppCommand> app_command;
+  CreateAppClientKey(kAppGuid1, true);
+  CreateCommand(
+      kAppGuid1, true, kCmdId1, kCmdLineExit0, NULL, NULL, NULL);
+
+  ASSERT_SUCCEEDED(AppCommand::Load(
+      kAppGuid1, true, kCmdId1, kSessionId, address(app_command)));
+  ASSERT_FALSE(app_command->is_web_accessible());
+}
+
+TEST_F(AppCommandTest, LoadWebAccessibleCommand) {
+  scoped_ptr<AppCommand> app_command;
+  CreateAppClientKey(kAppGuid1, true);
+  CreateCommand(
+      kAppGuid1, true, kCmdId1, kCmdLineExit0, NULL, &kTrue, NULL);
+
+  ASSERT_SUCCEEDED(AppCommand::Load(
+      kAppGuid1, true, kCmdId1, kSessionId, address(app_command)));
+  ASSERT_TRUE(app_command->is_web_accessible());
+}
+
+TEST_F(AppCommandTest, Execute) {
+  CString temp_file;
+  ASSERT_TRUE(::GetTempFileName(app_util::GetTempDir(),
+                                _T("omaha"),
+                                0,
+                                CStrBuf(temp_file, MAX_PATH)));
+
+  // GetTempFileName created an empty file. Delete it.
+  ASSERT_EQ(0, _tunlink(temp_file));
+
+  // Hopefully we will cause the file to be created. Cause its deletion at exit.
+  ON_SCOPE_EXIT(_tunlink, temp_file);
+
+  CString command_line = GetEchoCommandLine(_T("hello world!"), temp_file);
+
+  scoped_ptr<AppCommand> app_command;
+  CreateAppClientKey(kAppGuid1, true);
+  CreateCommand(
+      kAppGuid1, true, kCmdId1, command_line, &kTrue, &kTrue, NULL);
+
+  ASSERT_SUCCEEDED(AppCommand::Load(
+      kAppGuid1, true, kCmdId1, kSessionId, address(app_command)));
+
+  scoped_process process;
+  ASSERT_SUCCEEDED(app_command->Execute(address(process)));
+  ASSERT_EQ(WAIT_OBJECT_0, WaitForSingleObject(get(process), 16 * kMsPerSec));
+
+  ASSERT_TRUE(File::Exists(temp_file));
+}
+
+}  // namespace omaha
diff --git a/goopdate/app_manager.cc b/goopdate/app_manager.cc
new file mode 100644
index 0000000..a517354
--- /dev/null
+++ b/goopdate/app_manager.cc
@@ -0,0 +1,1336 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_manager.h"
+#include <algorithm>
+#include <cstdlib>
+#include <functional>
+#include <map>
+#include "base/scoped_ptr.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/error.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/time.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/app_registry_utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/oem_install_utils.h"
+#include "omaha/goopdate/application_usage_data.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/server_resource.h"
+#include "omaha/goopdate/string_formatter.h"
+
+namespace omaha {
+
+namespace {
+
+const uint32 kInitialInstallTimeDiff = static_cast<uint32>(-1 * kSecondsPerDay);
+
+// Returns the number of days haven been passed since the given time.
+// The parameter time is in the same format as C time() returns.
+int GetNumberOfDaysSince(int time) {
+  ASSERT1(time >= 0);
+  const int now = Time64ToInt32(GetCurrent100NSTime());
+  ASSERT1(now >= time);
+
+  if (now < time) {
+    // In case the client computer clock is adjusted in between.
+    return 0;
+  }
+  return (now - time) / kSecondsPerDay;
+}
+
+// Determines if an application is registered with Omaha.
+class IsAppRegisteredFunc
+    : public std::unary_function<const CString&, HRESULT> {
+ public:
+  explicit IsAppRegisteredFunc(const CString& guid)
+      : is_registered_(false),
+        guid_(guid) {}
+
+  bool is_registered() const { return is_registered_; }
+
+  HRESULT operator() (const CString& guid) {
+    if (guid.CompareNoCase(guid_) == 0) {
+      is_registered_ = true;
+    }
+    return S_OK;
+  }
+ private:
+  CString guid_;
+  bool is_registered_;
+};
+
+// Enumerates all sub keys of the key and calls the functor for each of them,
+// ignoring errors to ensure all keys are processed.
+template <typename T>
+HRESULT EnumerateSubKeys(const TCHAR* key_name, T* functor) {
+  RegKey client_key;
+  HRESULT hr = client_key.Open(key_name, KEY_READ);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  int num_sub_keys = client_key.GetSubkeyCount();
+  for (int i = 0; i < num_sub_keys; ++i) {
+    CString sub_key_name;
+    hr = client_key.GetSubkeyNameAt(i, &sub_key_name);
+    if (SUCCEEDED(hr)) {
+      (*functor)(sub_key_name);
+    }
+  }
+
+  return S_OK;
+}
+
+}  // namespace
+
+typedef bool (*AppPredictFunc)(const AppManager& app_manager,
+                               const CString& app_id);
+
+bool IsUninstalledAppPredicate(const AppManager& app_manager,
+                               const CString& app_id) {
+  return app_manager.IsAppUninstalled(app_id);
+}
+
+bool IsAppOemInstalledAndEulaAcceptedPredicate(const AppManager& app_manager,
+                                               const CString& app_id) {
+  return app_manager.IsAppOemInstalledAndEulaAccepted(app_id);
+}
+
+bool IsRegisteredAppPredicate(const AppManager& app_manager,
+                              const CString& app_id) {
+  return app_manager.IsAppRegistered(app_id);
+}
+
+// Accumulates app IDs for apps that satisfies the predicate.
+class CollectProductsFunc
+    : public std::unary_function<const CString&, HRESULT> {
+ public:
+  CollectProductsFunc(const AppPredictFunc predicate,
+                      const AppManager& app_manager,
+                      AppIdVector* app_ids)
+      : predicate_(predicate),
+        app_manager_(app_manager),
+        app_ids_(app_ids) {
+    ASSERT1(app_ids);
+  }
+
+  // Ignores errors and accumulates as many applications as possible.
+  HRESULT operator() (const CString& app_id) const {
+    if ((*predicate_)(app_manager_, app_id)) {
+      app_ids_->push_back(app_id);
+    }
+
+    return S_OK;
+  }
+
+ private:
+  const AppPredictFunc predicate_;
+  const AppManager& app_manager_;
+  AppIdVector* const app_ids_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(CollectProductsFunc);
+};
+
+// Runs application registration hooks registered under Omaha AppIds.
+// Reads the Hook Clsid entry under Clients\{AppID}. CoCreates the CLSID. Calls
+// IRegistrationUpdateHook::UpdateRegistry().
+class RunRegistrationUpdateHooksFunc
+    : public std::unary_function<const CString&, HRESULT> {
+ public:
+  explicit RunRegistrationUpdateHooksFunc(const AppManager& app_manager)
+      : app_manager_(app_manager) {
+  }
+
+  HRESULT operator() (const CString& app_id) {
+    GUID app_guid = GUID_NULL;
+    HRESULT hr = StringToGuidSafe(app_id, &app_guid);
+    if (FAILED(hr)) {
+      return hr;
+    }
+    RegKey client_key;
+    hr = app_manager_.OpenClientKey(app_guid, &client_key);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    CString hook_clsid_str;
+    hr = client_key.GetValue(kRegValueUpdateHookClsid, &hook_clsid_str);
+    if (FAILED(hr)) {
+      return hr;
+    }
+    GUID hook_clsid = GUID_NULL;
+    hr = StringToGuidSafe(hook_clsid_str, &hook_clsid);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    CORE_LOG(L3, (_T("[Update Hook Clsid][%s][%s]"), app_id, hook_clsid_str));
+
+    CComPtr<IRegistrationUpdateHook> registration_hook;
+    hr = registration_hook.CoCreateInstance(hook_clsid);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[IRegistrationUpdateHook CoCreate failed][0x%x]"), hr));
+      return hr;
+    }
+
+    hr = registration_hook->UpdateRegistry(CComBSTR(app_id),
+                                           app_manager_.is_machine_);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[registration_hook UpdateRegistry failed][0x%x]"), hr));
+      return hr;
+    }
+
+    return S_OK;
+  }
+
+ private:
+  const AppManager& app_manager_;
+};
+
+AppManager* AppManager::instance_ = NULL;
+
+// We do not worry about contention on creation because only the Worker should
+// create AppManager during its initialization.
+HRESULT AppManager::CreateInstance(bool is_machine) {
+  ASSERT1(!instance_);
+  if (instance_) {
+    return S_OK;
+  }
+
+  AppManager* instance(new AppManager(is_machine));
+  if (!instance->InitializeRegistryLock()) {
+    HRESULT hr(HRESULTFromLastError());
+    delete instance;
+    return hr;
+  }
+
+  instance_ = instance;
+  return S_OK;
+}
+
+void AppManager::DeleteInstance() {
+  delete instance_;
+  instance_ = NULL;
+}
+
+AppManager* AppManager::Instance() {
+  ASSERT1(instance_);
+  return instance_;
+}
+
+HRESULT AppManager::ReadAppVersionNoLock(bool is_machine, const GUID& app_guid,
+                                         CString* version) {
+  ASSERT1(version);
+  CORE_LOG(L2, (_T("[ReadAppVersionNoLock][%s]"), GuidToString(app_guid)));
+
+  AppManager app_manager(is_machine);
+  RegKey client_key;
+  HRESULT hr = app_manager.OpenClientKey(app_guid, &client_key);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = client_key.GetValue(kRegValueProductVersion, version);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CORE_LOG(L3, (_T("[kRegValueProductVersion][%s]"), *version));
+  return S_OK;
+}
+
+AppManager::AppManager(bool is_machine)
+    : is_machine_(is_machine) {
+  CORE_LOG(L3, (_T("[AppManager::AppManager][is_machine=%d]"), is_machine));
+}
+
+// App installers should use similar code to create a lock to acquire while
+// modifying Omaha registry.
+bool AppManager::InitializeRegistryLock() {
+  NamedObjectAttributes lock_attr;
+  GetNamedObjectAttributes(kRegistryAccessMutex, is_machine_, &lock_attr);
+  return registry_access_lock_.InitializeWithSecAttr(lock_attr.name,
+                                                     &lock_attr.sa);
+}
+
+// Vulnerable to a race condition with installers. To prevent this, acquire
+// GetRegistryStableStateLock().
+bool AppManager::IsAppRegistered(const GUID& app_guid) const {
+  return IsAppRegistered(GuidToString(app_guid));
+}
+
+// Vulnerable to a race condition with installers. To prevent this, acquire
+// GetRegistryStableStateLock().
+bool AppManager::IsAppRegistered(const CString& app_id) const {
+  IsAppRegisteredFunc func(app_id);
+  HRESULT hr = EnumerateSubKeys(
+      ConfigManager::Instance()->registry_clients(is_machine_),
+      &func);
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  return func.is_registered();
+}
+
+bool AppManager::IsAppUninstalled(const CString& app_id) const {
+  GUID app_guid = {0};
+  if (FAILED(StringToGuidSafe(app_id, &app_guid))) {
+    ASSERT1(false);
+    return false;
+  }
+  return IsAppUninstalled(app_guid);
+}
+
+// An app is considered uninstalled if:
+//  * The app's Clients key does not exist AND
+//  * The app's ClientState key exists and contains the pv value.
+// We check for the pv key value in the ClientState to prevent Omaha from
+// detecting the key created in the following scenarios as an uninstalled app.
+//  * Per-machine apps may write dr to per-user Omaha's key. Per-user Omaha
+//    must not detect this as an uninstalled app.
+//  * Omaha may create the app's ClientState key and write values from the
+//    metainstaller tag before running the installer, which creates the
+//    Clients key.
+bool AppManager::IsAppUninstalled(const GUID& app_guid) const {
+  if (IsAppRegistered(app_guid)) {
+    return false;
+  }
+
+  return RegKey::HasValue(GetClientStateKeyName(app_guid),
+                          kRegValueProductVersion);
+}
+
+bool AppManager::IsAppOemInstalledAndEulaAccepted(const CString& app_id) const {
+  GUID app_guid = GUID_NULL;
+  if (FAILED(StringToGuidSafe(app_id, &app_guid))) {
+    ASSERT1(false);
+    return false;
+  }
+
+  if (IsAppUninstalled(app_guid)) {
+    return false;
+  }
+
+  if (!app_registry_utils::IsAppEulaAccepted(is_machine_, app_id, false)) {
+    CORE_LOG(L3, (_T("[EULA not accepted for app %s, its OEM ping not sent.]"),
+                  app_id.GetString()));
+    return false;
+  }
+
+  return RegKey::HasValue(GetClientStateKeyName(app_guid), kRegValueOemInstall);
+}
+
+// Vulnerable to a race condition with installers. To prevent this, hold
+// GetRegistryStableStateLock() while calling this function and related
+// functions, such as ReadAppPersistentData().
+HRESULT AppManager::GetRegisteredApps(AppIdVector* app_ids) const {
+  ASSERT1(app_ids);
+
+  CollectProductsFunc func(IsRegisteredAppPredicate, *this, app_ids);
+
+  return EnumerateSubKeys(
+      ConfigManager::Instance()->registry_clients(is_machine_),
+      &func);
+}
+
+// Vulnerable to a race condition with installers. To prevent this, acquire
+// GetRegistryStableStateLock().
+HRESULT AppManager::GetUninstalledApps(AppIdVector* app_ids) const {
+  ASSERT1(app_ids);
+
+  CollectProductsFunc func(IsUninstalledAppPredicate, *this, app_ids);
+
+  return EnumerateSubKeys(
+      ConfigManager::Instance()->registry_client_state(is_machine_),
+      &func);
+}
+
+HRESULT AppManager::GetOemInstalledAndEulaAcceptedApps(
+    AppIdVector* app_ids) const {
+  ASSERT1(app_ids);
+
+  CollectProductsFunc func(IsAppOemInstalledAndEulaAcceptedPredicate,
+                           *this,
+                           app_ids);
+
+  return EnumerateSubKeys(
+      ConfigManager::Instance()->registry_client_state(is_machine_),
+      &func);
+}
+
+HRESULT AppManager::RunRegistrationUpdateHook(const CString& app_id) const {
+  return RunRegistrationUpdateHooksFunc(*this)(app_id);
+}
+
+// Vulnerable to a race condition with installers. We think this is acceptable.
+// If there is a future requirement for greater consistency, acquire
+// GetRegistryStableStateLock().
+HRESULT AppManager::RunAllRegistrationUpdateHooks() const {
+  RunRegistrationUpdateHooksFunc func(*this);
+  const TCHAR* key(ConfigManager::Instance()->registry_clients(is_machine_));
+  return EnumerateSubKeys(key, &func);
+}
+
+CString AppManager::GetClientKeyName(const GUID& app_guid) const {
+  return app_registry_utils::GetAppClientsKey(is_machine_,
+                                              GuidToString(app_guid));
+}
+
+CString AppManager::GetClientStateKeyName(const GUID& app_guid) const {
+  return app_registry_utils::GetAppClientStateKey(is_machine_,
+                                                  GuidToString(app_guid));
+}
+
+CString AppManager::GetClientStateMediumKeyName(const GUID& app_guid) const {
+  ASSERT1(is_machine_);
+  return app_registry_utils::GetAppClientStateMediumKey(is_machine_,
+                                                        GuidToString(app_guid));
+}
+
+// Assumes the registry access lock is held.
+HRESULT AppManager::OpenClientKey(const GUID& app_guid,
+                                  RegKey* client_key) const {
+  ASSERT1(client_key);
+  return client_key->Open(GetClientKeyName(app_guid), KEY_READ);
+}
+
+// Assumes the registry access lock is held.
+HRESULT AppManager::OpenClientStateKey(const GUID& app_guid,
+                                       REGSAM sam_desired,
+                                       RegKey* client_state_key) const {
+  ASSERT1(client_state_key);
+  CString key_name = GetClientStateKeyName(app_guid);
+  return client_state_key->Open(key_name, sam_desired);
+}
+
+// Also creates the ClientStateMedium key for machine apps, ensuring it exists
+// whenever ClientState exists.  Does not create ClientStateMedium for Omaha.
+// This function is called for self-updates, so it must explicitly avoid this.
+// Assumes the registry access lock is held.
+HRESULT AppManager::CreateClientStateKey(const GUID& app_guid,
+                                         RegKey* client_state_key) {
+  ASSERT1(client_state_key);
+  // TODO(omaha3): Add GetOwner() to GLock & add this to Open() functions too.
+  // ASSERT1(::GetCurrentThreadId() == registry_access_lock_.GetOwner());
+
+  const CString key_name = GetClientStateKeyName(app_guid);
+  HRESULT hr = client_state_key->Create(key_name);
+  if (FAILED(hr)) {
+    CORE_LOG(L3, (_T("[RegKey::Create failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  if (!is_machine_) {
+    return S_OK;
+  }
+
+  if (::IsEqualGUID(kGoopdateGuid, app_guid)) {
+    return S_OK;
+  }
+
+  const CString medium_key_name = GetClientStateMediumKeyName(app_guid);
+  hr = RegKey::CreateKey(medium_key_name);
+  if (FAILED(hr)) {
+    CORE_LOG(L3, (_T("[RegKey::Create ClientStateMedium failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+// Reads the following values from the registry:
+//  Clients key
+//    pv
+//    lang
+//    name
+//  ClientState key
+//    lang (if not present in Clients)
+//    ap
+//    tttoken
+//    iid
+//    brand
+//    client
+//    experiment
+//    (referral is intentionally not read)
+//    InstallTime (converted to diff)
+//    oeminstall
+//  ClientState and ClientStateMedium key
+//    eulaaccepted
+//  ClientState key in HKCU/HKLM/Low integrity
+//    did run
+//
+// app_guid_ is set to the app_guid argument.
+// Note: pv is not read from ClientState into app_data. It's
+// presence is checked for an uninstall
+// TODO(omaha3): We will need to get ClientState's pv when reporting uninstalls.
+// Note: If the application is uninstalled, the Clients key may not exist.
+HRESULT AppManager::ReadAppPersistentData(App* app) {
+  ASSERT1(app);
+
+  const GUID& app_guid = app->app_guid();
+  const CString& app_guid_string = app->app_guid_string();
+
+  CORE_LOG(L2, (_T("[AppManager::ReadAppPersistentData][%s]"),
+                app_guid_string));
+
+  ASSERT1(app->model()->IsLockedByCaller());
+
+  __mutexScope(registry_access_lock_);
+
+  const bool is_eula_accepted =
+      app_registry_utils::IsAppEulaAccepted(is_machine_,
+                                            app_guid_string,
+                                            false);
+  app->is_eula_accepted_ = is_eula_accepted ? TRISTATE_TRUE : TRISTATE_FALSE;
+
+  bool client_key_exists = false;
+  RegKey client_key;
+  HRESULT hr = OpenClientKey(app_guid, &client_key);
+  if (SUCCEEDED(hr)) {
+    client_key_exists = true;
+
+    CString version;
+    hr = client_key.GetValue(kRegValueProductVersion, &version);
+    CORE_LOG(L3, (_T("[AppManager::ReadAppPersistentData]")
+                  _T("[%s][version=%s]"), app_guid_string, version));
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    app->current_version()->set_version(version);
+
+    // Language and name might not be written by installer, so ignore failures.
+    client_key.GetValue(kRegValueLanguage, &app->language_);
+    client_key.GetValue(kRegValueAppName, &app->display_name_);
+  }
+
+  // Ensure there is a valid display name.
+  if (app->display_name_.IsEmpty()) {
+    StringFormatter formatter(app->app_bundle()->display_language());
+
+    CString company_name;
+    VERIFY1(SUCCEEDED(formatter.LoadString(IDS_FRIENDLY_COMPANY_NAME,
+                                           &company_name)));
+
+    VERIFY1(SUCCEEDED(formatter.FormatMessage(&app->display_name_,
+                                              IDS_DEFAULT_APP_DISPLAY_NAME,
+                                              company_name)));
+  }
+
+  // If ClientState registry key doesn't exist, the function could return.
+  // Before opening the key, set days_since_last* to -1, which is the
+  // default value if reg key doesn't exist. If later we find that the values
+  // are readable, new values will overwrite current ones.
+  app->set_days_since_last_active_ping(-1);
+  app->set_days_since_last_roll_call(-1);
+
+  // The following do not rely on client_state_key, so check them before
+  // possibly returning if OpenClientStateKey fails.
+
+  // Reads the did run value.
+  ApplicationUsageData app_usage(is_machine_, vista_util::IsVistaOrLater());
+  app_usage.ReadDidRun(app_guid_string);
+
+  // Sets did_run regardless of the return value of ReadDidRun above. If read
+  // fails, active_state() should return ACTIVE_UNKNOWN which is intented.
+  app->did_run_ = app_usage.active_state();
+
+  // TODO(omaha3): Consider moving GetInstallTimeDiffSec() up here. Be careful
+  // that the results when ClientState does not exist are desirable. See the
+  // comments near that function and above set_days_since_last_active_ping call.
+
+  RegKey client_state_key;
+  hr = OpenClientStateKey(app_guid, KEY_READ, &client_state_key);
+  if (FAILED(hr)) {
+    // It is possible that the client state key has not yet been populated.
+    // In this case just return the information that we have gathered thus far.
+    // However if both keys do not exist, then we are doing something wrong.
+    CORE_LOG(LW, (_T("[AppManager::ReadAppPersistentData - No ClientState]")));
+    if (client_key_exists) {
+      return S_OK;
+    } else {
+      return hr;
+    }
+  }
+
+  // Read language from ClientState key if it was not found in the Clients key.
+  if (app->language().IsEmpty()) {
+    client_state_key.GetValue(kRegValueLanguage, &app->language_);
+  }
+
+  client_state_key.GetValue(kRegValueAdditionalParams, &app->ap_);
+  client_state_key.GetValue(kRegValueTTToken, &app->tt_token_);
+
+  CString iid;
+  client_state_key.GetValue(kRegValueInstallationId, &iid);
+  GUID iid_guid;
+  if (SUCCEEDED(StringToGuidSafe(iid, &iid_guid))) {
+    app->iid_ = iid_guid;
+  }
+
+  client_state_key.GetValue(kRegValueBrandCode, &app->brand_code_);
+  ASSERT1(app->brand_code_.GetLength() <= kBrandIdLength);
+  client_state_key.GetValue(kRegValueClientId, &app->client_id_);
+
+  // We do not need the referral_id.
+
+  DWORD last_active_ping_sec(0);
+  if (SUCCEEDED(client_state_key.GetValue(kRegValueActivePingDayStartSec,
+                                          &last_active_ping_sec))) {
+    int days_since_last_active_ping =
+        GetNumberOfDaysSince(static_cast<int32>(last_active_ping_sec));
+    app->set_days_since_last_active_ping(days_since_last_active_ping);
+  }
+
+  DWORD last_roll_call_sec(0);
+  if (SUCCEEDED(client_state_key.GetValue(kRegValueRollCallDayStartSec,
+                                          &last_roll_call_sec))) {
+    int days_since_last_roll_call =
+        GetNumberOfDaysSince(static_cast<int32>(last_roll_call_sec));
+    app->set_days_since_last_roll_call(days_since_last_roll_call);
+  }
+
+  app->install_time_diff_sec_ = GetInstallTimeDiffSec(app_guid);
+  // Generally GetInstallTimeDiffSec() shouldn't return kInitialInstallTimeDiff
+  // here. The only exception is in the unexpected case when ClientState exists
+  // without a pv.
+  ASSERT1((app->install_time_diff_sec_ != kInitialInstallTimeDiff) ||
+          !RegKey::HasValue(GetClientStateKeyName(app_guid),
+                            kRegValueProductVersion));
+
+  return S_OK;
+}
+
+void AppManager::ReadAppInstallTimeDiff(App* app) {
+  ASSERT1(app);
+  app->install_time_diff_sec_ = GetInstallTimeDiffSec(app->app_guid());
+}
+
+// Calls ReadAppPersistentData() to populate app and adds the following values
+// specific to uninstalled apps:
+//  ClientState key
+//    pv:  set as current_version()->version
+//
+// Since this is an uninstalled app, values from the Clients key should not be
+// populated.
+HRESULT AppManager::ReadUninstalledAppPersistentData(App* app) {
+  ASSERT1(app);
+  ASSERT1(!IsAppRegistered(app->app_guid_string()));
+
+  HRESULT hr = ReadAppPersistentData(app);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  ASSERT1(app->current_version()->version().IsEmpty());
+
+  RegKey client_state_key;
+  hr = OpenClientStateKey(app->app_guid(), KEY_READ, &client_state_key);
+  ASSERT(SUCCEEDED(hr), (_T("Uninstalled apps have a ClientState key.")));
+
+  CString version;
+  hr = client_state_key.GetValue(kRegValueProductVersion, &version);
+  CORE_LOG(L3, (_T("[AppManager::ReadAppPersistentData]")
+                _T("[%s][uninstalled version=%s]"),
+                app->app_guid_string(), version));
+  ASSERT(SUCCEEDED(hr), (_T("Uninstalled apps have a pv.")));
+  app->current_version()->set_version(version);
+
+  return S_OK;
+}
+
+// Sets the following values in the app's ClientState, to make them available to
+// the installer:
+//    lang
+//    ap
+//    brand (in SetAppBranding)
+//    client (in SetAppBranding)
+//    experiment
+//    referral (in SetAppBranding)
+//    InstallTime (in SetAppBranding; converted from diff)
+//    oeminstall (if appropriate)
+//    eulaaccepted (set/deleted)
+//    browser
+//    usagestats
+// Sets eulaaccepted=0 if the app is not already registered and the app's EULA
+// has not been accepted. Deletes eulaaccepted if the EULA has been accepted.
+// Only call for initial or over-installs. Do not call for updates to avoid
+// mistakenly replacing data, such as the application's language, and causing
+// unexpected changes to the app during a silent update.
+HRESULT AppManager::WritePreInstallData(const App& app) {
+  CORE_LOG(L2, (_T("[AppManager::WritePreInstallData][%s]"),
+                app.app_guid_string()));
+
+  ASSERT1(app.app_bundle()->is_machine() == is_machine_);
+
+  ASSERT1(IsRegistryStableStateLockedByCaller());
+  __mutexScope(registry_access_lock_);
+
+  RegKey client_state_key;
+  HRESULT hr = CreateClientStateKey(app.app_guid(), &client_state_key);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (app.is_eula_accepted()) {
+    hr = app_registry_utils::ClearAppEulaNotAccepted(is_machine_,
+                                                     app.app_guid_string());
+  } else {
+    if (!IsAppRegistered(app.app_guid())) {
+      hr = app_registry_utils::SetAppEulaNotAccepted(is_machine_,
+                                                     app.app_guid_string());
+    }
+  }
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (!app.language().IsEmpty()) {
+    VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueLanguage,
+                                                app.language())));
+  }
+
+  if (app.ap().IsEmpty()) {
+    VERIFY1(SUCCEEDED(client_state_key.DeleteValue(kRegValueAdditionalParams)));
+  } else {
+    VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueAdditionalParams,
+                                                app.ap())));
+  }
+
+  CString state_key_path = GetClientStateKeyName(app.app_guid());
+  VERIFY1(SUCCEEDED(app_registry_utils::SetAppBranding(state_key_path,
+                                                       app.brand_code(),
+                                                       app.client_id(),
+                                                       app.referral_id())));
+
+  if (app.GetExperimentLabels().IsEmpty()) {
+    VERIFY1(SUCCEEDED(client_state_key.DeleteValue(kRegValueExperimentLabels)));
+  } else {
+    VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueExperimentLabels,
+                                                app.GetExperimentLabels())));
+  }
+
+  if (oem_install_utils::IsOemInstalling(is_machine_)) {
+    ASSERT1(is_machine_);
+    VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueOemInstall, _T("1"))));
+  }
+
+  if (BROWSER_UNKNOWN == app.browser_type()) {
+    VERIFY1(SUCCEEDED(client_state_key.DeleteValue(kRegValueBrowser)));
+  } else {
+    DWORD browser_type = app.browser_type();
+    VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueBrowser,
+                                                browser_type)));
+  }
+
+  if (TRISTATE_NONE != app.usage_stats_enable()) {
+    VERIFY1(SUCCEEDED(app_registry_utils::SetUsageStatsEnable(
+                          is_machine_,
+                          app.app_guid_string(),
+                          app.usage_stats_enable())));
+  }
+
+  return S_OK;
+}
+
+// All values are optional.
+void AppManager::ReadInstallerResultApiValues(
+    const GUID& app_guid,
+    InstallerResult* installer_result,
+    DWORD* installer_error,
+    DWORD* installer_extra_code1,
+    CString* installer_result_uistring,
+    CString* installer_success_launch_cmd) {
+  ASSERT1(installer_result);
+  ASSERT1(installer_error);
+  ASSERT1(installer_extra_code1);
+  ASSERT1(installer_result_uistring);
+  ASSERT1(installer_success_launch_cmd);
+
+  __mutexScope(registry_access_lock_);
+
+  RegKey client_state_key;
+  HRESULT hr = OpenClientStateKey(app_guid, KEY_READ, &client_state_key);
+  if (FAILED(hr)) {
+    return;
+  }
+
+  if (SUCCEEDED(client_state_key.GetValue(
+                    kRegValueInstallerResult,
+                    reinterpret_cast<DWORD*>(installer_result)))) {
+    CORE_LOG(L1, (_T("[InstallerResult in registry][%u]"), *installer_result));
+  }
+  if (*installer_result >= INSTALLER_RESULT_MAX) {
+    CORE_LOG(LW, (_T("[Unsupported InstallerResult value]")));
+    *installer_result = INSTALLER_RESULT_DEFAULT;
+  }
+
+  if (SUCCEEDED(client_state_key.GetValue(kRegValueInstallerError,
+                                          installer_error))) {
+    CORE_LOG(L1, (_T("[InstallerError in registry][%u]"), *installer_error));
+  }
+
+  if (SUCCEEDED(client_state_key.GetValue(kRegValueInstallerExtraCode1,
+                                          installer_extra_code1))) {
+    CORE_LOG(L1, (_T("[InstallerExtraCode1 in registry][%u]"),
+        *installer_extra_code1));
+  }
+
+  if (SUCCEEDED(client_state_key.GetValue(kRegValueInstallerResultUIString,
+                                          installer_result_uistring))) {
+    CORE_LOG(L1, (_T("[InstallerResultUIString in registry][%s]"),
+        *installer_result_uistring));
+  }
+
+  if (SUCCEEDED(client_state_key.GetValue(
+                    kRegValueInstallerSuccessLaunchCmdLine,
+                    installer_success_launch_cmd))) {
+    CORE_LOG(L1, (_T("[InstallerSuccessLaunchCmdLine in registry][%s]"),
+        *installer_success_launch_cmd));
+  }
+
+  ClearInstallerResultApiValues(app_guid);
+}
+
+void AppManager::ClearInstallerResultApiValues(const GUID& app_guid) {
+  const CString client_state_key_name = GetClientStateKeyName(app_guid);
+  const CString update_key_name =
+      ConfigManager::Instance()->registry_update(is_machine_);
+
+  ASSERT1(IsRegistryStableStateLockedByCaller());
+  __mutexScope(registry_access_lock_);
+
+  // Delete the old LastXXX values.  These may not exist, so don't care if they
+  // fail.
+  RegKey::DeleteValue(client_state_key_name,
+                      kRegValueLastInstallerResult);
+  RegKey::DeleteValue(client_state_key_name,
+                      kRegValueLastInstallerResultUIString);
+  RegKey::DeleteValue(client_state_key_name,
+                      kRegValueLastInstallerError);
+  RegKey::DeleteValue(client_state_key_name,
+                      kRegValueLastInstallerExtraCode1);
+  RegKey::DeleteValue(client_state_key_name,
+                      kRegValueLastInstallerSuccessLaunchCmdLine);
+
+  // Also delete any values from Google\Update.
+  // TODO(Omaha): This is a temporary fix for bug 1539293. See TODO below.
+  RegKey::DeleteValue(update_key_name,
+                      kRegValueLastInstallerResult);
+  RegKey::DeleteValue(update_key_name,
+                      kRegValueLastInstallerResultUIString);
+  RegKey::DeleteValue(update_key_name,
+                      kRegValueLastInstallerError);
+  RegKey::DeleteValue(update_key_name,
+                      kRegValueLastInstallerExtraCode1);
+  RegKey::DeleteValue(update_key_name,
+                      kRegValueLastInstallerSuccessLaunchCmdLine);
+
+  // Rename current InstallerResultXXX values to LastXXX.
+  RegKey::RenameValue(client_state_key_name,
+                      kRegValueInstallerResult,
+                      kRegValueLastInstallerResult);
+  RegKey::RenameValue(client_state_key_name,
+                      kRegValueInstallerError,
+                      kRegValueLastInstallerError);
+  RegKey::RenameValue(client_state_key_name,
+                      kRegValueInstallerExtraCode1,
+                      kRegValueLastInstallerExtraCode1);
+  RegKey::RenameValue(client_state_key_name,
+                      kRegValueInstallerResultUIString,
+                      kRegValueLastInstallerResultUIString);
+  RegKey::RenameValue(client_state_key_name,
+                      kRegValueInstallerSuccessLaunchCmdLine,
+                      kRegValueLastInstallerSuccessLaunchCmdLine);
+
+  // Copy over to the Google\Update key.
+  // TODO(Omaha3): This is a temporary fix for bug 1539293. Once Pack V2 is
+  // deprecated (Pack stops taking offline installers for new versions of
+  // Omaha apps), remove this. (It might be useful to leave the CopyValue calls
+  // in DEBUG builds only.)
+  RegKey::CopyValue(client_state_key_name,
+                    update_key_name,
+                    kRegValueLastInstallerResult);
+  RegKey::CopyValue(client_state_key_name,
+                    update_key_name,
+                    kRegValueLastInstallerError);
+  RegKey::CopyValue(client_state_key_name,
+                    update_key_name,
+                    kRegValueLastInstallerExtraCode1);
+  RegKey::CopyValue(client_state_key_name,
+                    update_key_name,
+                    kRegValueLastInstallerResultUIString);
+  RegKey::CopyValue(client_state_key_name,
+                    update_key_name,
+                    kRegValueLastInstallerSuccessLaunchCmdLine);
+}
+
+// Reads the following values from Clients:
+//    pv
+//    lang (if present)
+// name is not read. TODO(omaha3): May change if we persist name in registry.
+HRESULT AppManager::ReadInstallerRegistrationValues(App* app) {
+  ASSERT1(app);
+
+  const CString& app_guid_string = app->app_guid_string();
+
+  CORE_LOG(L2, (_T("[AppManager::ReadInstallerRegistrationValues][%s]"),
+                app_guid_string));
+
+  ASSERT1(app->model()->IsLockedByCaller());
+
+  __mutexScope(registry_access_lock_);
+
+  RegKey client_key;
+  if (FAILED(OpenClientKey(app->app_guid(), &client_key))) {
+    OPT_LOG(LE, (_T("[Installer did not create key][%s]"), app_guid_string));
+    return GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY;
+  }
+
+  CString version;
+  if (FAILED(client_key.GetValue(kRegValueProductVersion, &version))) {
+    OPT_LOG(LE, (_T("[Installer did not register][%s]"), app_guid_string));
+    return GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY;
+  }
+
+  if (version.IsEmpty()) {
+    OPT_LOG(LE, (_T("[Installer did not write version][%s]"), app_guid_string));
+    return GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY;
+  }
+
+  app->next_version()->set_version(version);
+
+  CString language;
+  if (SUCCEEDED(client_key.GetValue(kRegValueLanguage, &language))) {
+    app->language_ = language;
+  }
+
+  return S_OK;
+}
+
+// Writes tttoken and updates relevant stats.
+void AppManager::PersistSuccessfulUpdateCheckResponse(
+    const App& app,
+    bool is_update_available) {
+  CORE_LOG(L2, (_T("[AppManager::PersistSuccessfulUpdateCheckResponse]")
+                _T("[%s][%d]"), app.app_guid_string(), is_update_available));
+  __mutexScope(registry_access_lock_);
+
+  VERIFY1(SUCCEEDED(SetTTToken(app)));
+
+  const CString client_state_key = GetClientStateKeyName(app.app_guid());
+
+  if (is_update_available) {
+    if (app.error_code() == GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY) {
+      // The error indicates is_update and updates are disabled by policy.
+      ASSERT1(app.is_update());
+      app_registry_utils::ClearUpdateAvailableStats(client_state_key);
+    } else if (app.is_update()) {
+      // Only record an update available event for updates.
+      // We have other mechanisms, including IID, to track install success.
+      UpdateUpdateAvailableStats(app.app_guid());
+    }
+  } else {
+    app_registry_utils::ClearUpdateAvailableStats(client_state_key);
+    app_registry_utils::PersistSuccessfulUpdateCheck(client_state_key);
+  }
+}
+
+// Writes the following values to the ClientState key:
+//    pv (should be value written by installer in Clients key)
+//    lang (should be value written by installer in Clients key)
+//    iid (set/deleted)
+//
+// Does not write the following values because they were set by
+// WritePreInstallData() and would not have changed during installation unless
+// modified directly by the app installer.
+//    ap
+//    brand
+//    client
+//    experiment
+//    referral
+//    InstallTime (converted from diff)
+//    oeminstall
+//    eulaaccepted
+//    browser
+//    usagestats
+// TODO(omaha3): Maybe we should delete referral at this point. Ask Chrome.
+//
+// Other values, such as tttoken were set after the update check.
+//
+// The caller is responsible for modifying the values in app_data as
+// appropriate, including:
+//   * Updating values in app_data to reflect installer's values (pv and lang)
+//   * Clearing iid if appropriate.
+//   * Clearing the did run value. TODO(omaha3): Depends on TODO below.
+void AppManager::PersistSuccessfulInstall(const App& app) {
+  CORE_LOG(L2, (_T("[AppManager::PersistSuccessfulInstall][%s]"),
+                app.app_guid_string()));
+
+  ASSERT1(IsRegistryStableStateLockedByCaller());
+  __mutexScope(registry_access_lock_);
+
+  ASSERT1(!::IsEqualGUID(kGoopdateGuid, app.app_guid()));
+
+  RegKey client_state_key;
+  VERIFY1(SUCCEEDED(CreateClientStateKey(app.app_guid(), &client_state_key)));
+
+  VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueProductVersion,
+                                              app.next_version()->version())));
+
+  if (!app.language().IsEmpty()) {
+    VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueLanguage,
+                                                app.language())));
+  }
+
+  if (::IsEqualGUID(app.iid(), GUID_NULL)) {
+    VERIFY1(SUCCEEDED(client_state_key.DeleteValue(kRegValueInstallationId)));
+  } else {
+    VERIFY1(SUCCEEDED(client_state_key.SetValue(
+                          kRegValueInstallationId,
+                          GuidToString(app.iid()))));
+  }
+
+  const CString client_state_key_path = GetClientStateKeyName(app.app_guid());
+  app_registry_utils::PersistSuccessfulInstall(client_state_key_path,
+                                               app.is_update(),
+                                               false);  // TODO(omaha3): offline
+}
+
+HRESULT AppManager::SynchronizeClientState(const GUID& app_guid) {
+  __mutexScope(registry_access_lock_);
+
+  RegKey client_key;
+  HRESULT hr = OpenClientKey(app_guid, &client_key);
+  if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) {
+    return S_OK;
+  }
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  RegKey client_state_key;
+  hr = CreateClientStateKey(app_guid, &client_state_key);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CString version;
+  client_key.GetValue(kRegValueProductVersion, &version);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  hr = client_state_key.SetValue(kRegValueProductVersion, version);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CString language;
+  client_key.GetValue(kRegValueLanguage, &language);
+  if (!language.IsEmpty()) {
+    return client_state_key.SetValue(kRegValueLanguage, language);
+  }
+
+  return S_OK;
+}
+
+// TODO(omaha3): tttoken is not currently read from the server response.
+// TODO(omaha3): When implementing offline, we must make sure that the tttoken
+// is not deleted by the offline response processing.
+// TODO(omaha3): Having the parser write the server's token to the same member
+// that is used for the value from the tag exposes this value to the COM setter.
+// It would be nice to avoid that, possibly by only allowing that setter to work
+// in certain states.
+HRESULT AppManager::SetTTToken(const App& app) {
+  CORE_LOG(L3, (_T("[AppManager::SetTTToken][token=%s]"), app.tt_token()));
+
+  __mutexScope(registry_access_lock_);
+
+  RegKey client_state_key;
+  HRESULT hr = CreateClientStateKey(app.app_guid(), &client_state_key);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (app.tt_token().IsEmpty()) {
+    return client_state_key.DeleteValue(kRegValueTTToken);
+  } else {
+    return client_state_key.SetValue(kRegValueTTToken, app.tt_token());
+  }
+}
+
+void AppManager::ClearOemInstalled(const AppIdVector& app_ids) {
+  __mutexScope(registry_access_lock_);
+
+  AppIdVector::const_iterator it;
+  for (it = app_ids.begin(); it != app_ids.end(); ++it) {
+    ASSERT1(IsAppOemInstalledAndEulaAccepted(*it));
+    RegKey state_key;
+
+    GUID app_guid = GUID_NULL;
+    HRESULT hr = StringToGuidSafe(*it, &app_guid);
+    if (FAILED(hr)) {
+      continue;
+    }
+
+    hr = OpenClientStateKey(app_guid, KEY_ALL_ACCESS, &state_key);
+    if (FAILED(hr)) {
+      continue;
+    }
+
+    VERIFY1(SUCCEEDED(state_key.DeleteValue(kRegValueOemInstall)));
+  }
+}
+
+void AppManager::UpdateUpdateAvailableStats(const GUID& app_guid) {
+  __mutexScope(registry_access_lock_);
+
+  RegKey state_key;
+  HRESULT hr = CreateClientStateKey(app_guid, &state_key);
+  if (FAILED(hr)) {
+    ASSERT1(false);
+    return;
+  }
+
+  DWORD update_available_count(0);
+  hr = state_key.GetValue(kRegValueUpdateAvailableCount,
+                          &update_available_count);
+  if (FAILED(hr)) {
+    update_available_count = 0;
+  }
+  ++update_available_count;
+  VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueUpdateAvailableCount,
+                                       update_available_count)));
+
+  DWORD64 update_available_since_time(0);
+  hr = state_key.GetValue(kRegValueUpdateAvailableSince,
+                          &update_available_since_time);
+  if (FAILED(hr)) {
+    // There is no existing value, so this must be the first update notice.
+    VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueUpdateAvailableSince,
+                                         GetCurrent100NSTime())));
+
+    // TODO(omaha): It would be nice to report the version that we were first
+    // told to update to. This is available in UpdateResponse but we do not
+    // currently send it down in update responses. If we start using it, add
+    // kRegValueFirstUpdateResponseVersion.
+  }
+}
+
+// Returns 0 for any values that are not found.
+void AppManager::ReadUpdateAvailableStats(
+    const GUID& app_guid,
+    DWORD* update_responses,
+    DWORD64* time_since_first_response_ms) {
+  ASSERT1(update_responses);
+  ASSERT1(time_since_first_response_ms);
+  *update_responses = 0;
+  *time_since_first_response_ms = 0;
+
+  __mutexScope(registry_access_lock_);
+
+  RegKey state_key;
+  HRESULT hr = OpenClientStateKey(app_guid, KEY_READ, &state_key);
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[App ClientState key does not exist][%s]"),
+                  GuidToString(app_guid)));
+    return;
+  }
+
+  DWORD update_responses_in_reg(0);
+  hr = state_key.GetValue(kRegValueUpdateAvailableCount,
+                          &update_responses_in_reg);
+  if (SUCCEEDED(hr)) {
+    *update_responses = update_responses_in_reg;
+  }
+
+  DWORD64 update_available_since_time(0);
+  hr = state_key.GetValue(kRegValueUpdateAvailableSince,
+                          &update_available_since_time);
+  if (SUCCEEDED(hr)) {
+    const DWORD64 current_time = GetCurrent100NSTime();
+    ASSERT1(update_available_since_time <= current_time);
+    const DWORD64 time_since_first_response_in_100ns =
+        current_time - update_available_since_time;
+    *time_since_first_response_ms =
+        time_since_first_response_in_100ns / kMillisecsTo100ns;
+  }
+}
+
+uint32 AppManager::GetInstallTimeDiffSec(const GUID& app_guid) const {
+  if (!IsAppRegistered(app_guid) && !IsAppUninstalled(app_guid)) {
+    return kInitialInstallTimeDiff;
+  }
+
+  RegKey client_state_key;
+  HRESULT hr = OpenClientStateKey(app_guid, KEY_READ, &client_state_key);
+  if (FAILED(hr)) {
+    return 0;
+  }
+
+  DWORD install_time(0);
+  DWORD install_time_diff_sec(0);
+  if (SUCCEEDED(client_state_key.GetValue(kRegValueInstallTimeSec,
+                                          &install_time))) {
+    const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+    if (0 != install_time && now >= install_time) {
+      install_time_diff_sec = now - install_time;
+      // TODO(omaha3): Restore this assert. In Omaha 2, this function gets
+      // called as part of installation verification and Job::UpdateJob(), so
+      // the value can be 0. This will not be the case in Omaha 3.
+      // ASSERT1(install_time_diff_sec != 0);
+    }
+  }
+
+  return install_time_diff_sec;
+}
+
+// Clear the Installation ID if at least one of the conditions is true:
+// 1) DidRun==yes. First run is the last time we want to use the Installation
+//    ID. So delete Installation ID if it is present.
+// 2) kMaxLifeOfInstallationIDSec has passed since the app was installed. This
+//    is to ensure that Installation ID is cleared even if DidRun is never set.
+// 3) The app is Omaha. Always delete Installation ID if it is present
+//    because DidRun does not apply.
+HRESULT AppManager::ClearInstallationId(const App& app) {
+  ASSERT1(app.model()->IsLockedByCaller());
+  __mutexScope(registry_access_lock_);
+
+  if (::IsEqualGUID(app.iid(), GUID_NULL)) {
+    return S_OK;
+  }
+
+  if ((ACTIVE_RUN == app.did_run()) ||
+      (kMaxLifeOfInstallationIDSec <= app.install_time_diff_sec()) ||
+      (::IsEqualGUID(kGoopdateGuid, app.app_guid()))) {
+    CORE_LOG(L1, (_T("[Deleting iid for app][%s]"), app.app_guid_string()));
+
+    RegKey client_state_key;
+    HRESULT hr = CreateClientStateKey(app.app_guid(), &client_state_key);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    return client_state_key.DeleteValue(kRegValueInstallationId);
+  }
+
+  return S_OK;
+}
+
+void AppManager::SetLastPingDayStartTime(const App& app,
+                                         int elapsed_seconds_since_day_start) {
+  ASSERT1(elapsed_seconds_since_day_start >= 0);
+  ASSERT1(elapsed_seconds_since_day_start < kMaxTimeSinceMidnightSec);
+  ASSERT1(app.model()->IsLockedByCaller());
+
+  __mutexScope(registry_access_lock_);
+
+  int now = Time64ToInt32(GetCurrent100NSTime());
+
+  RegKey client_state_key;
+  if (FAILED(CreateClientStateKey(app.app_guid(), &client_state_key))) {
+    return;
+  }
+
+  bool did_send_active_ping = (app.did_run() == ACTIVE_RUN &&
+                               app.days_since_last_active_ping() != 0);
+  if (did_send_active_ping) {
+    VERIFY1(SUCCEEDED(client_state_key.SetValue(
+        kRegValueActivePingDayStartSec,
+        static_cast<DWORD>(now - elapsed_seconds_since_day_start))));
+  }
+
+  bool did_send_roll_call = (app.days_since_last_roll_call() != 0);
+  if (did_send_roll_call) {
+    VERIFY1(SUCCEEDED(client_state_key.SetValue(
+        kRegValueRollCallDayStartSec,
+        static_cast<DWORD>(now - elapsed_seconds_since_day_start))));
+  }
+}
+
+// Writes the day start time when last active ping/roll call happened to
+// registry if the corresponding ping has been sent.
+// Removes installation id, if did run = true or if goopdate.
+// Clears did run.
+HRESULT AppManager::PersistUpdateCheckSuccessfullySent(
+    const App& app,
+    int elapsed_seconds_since_day_start) {
+  ASSERT1(app.model()->IsLockedByCaller());
+
+  ApplicationUsageData app_usage(app.app_bundle()->is_machine(),
+                                 vista_util::IsVistaOrLater());
+  VERIFY1(SUCCEEDED(app_usage.ResetDidRun(app.app_guid_string())));
+
+  SetLastPingDayStartTime(app, elapsed_seconds_since_day_start);
+
+  // Handle the installation id.
+  VERIFY1(SUCCEEDED(ClearInstallationId(app)));
+
+  return S_OK;
+}
+
+HRESULT AppManager::RemoveClientState(const GUID& app_guid) {
+  CORE_LOG(L2, (_T("[AppManager::RemoveClientState][%s]"),
+                GuidToString(app_guid)));
+  ASSERT1(IsRegistryStableStateLockedByCaller());
+  __mutexScope(registry_access_lock_);
+
+  ASSERT1(!IsAppRegistered(app_guid));
+
+  return app_registry_utils::RemoveClientState(is_machine_,
+                                               GuidToString(app_guid));
+}
+
+// TODO(omaha3): May not need these
+#if 0
+// Writes 0.0.0.1 to pv. This value avoids any special cases, such as initial
+// install rules, for 0.0.0.0, while being unlikely to be higher than the
+// product's actual current version.
+HRESULT AppManager::RegisterProduct(const GUID& product_guid,
+                                    const CString& product_name) {
+  const TCHAR* const kRegisterProductVersion = _T("0.0.0.1");
+
+  __mutexScope(GetRegistryStableStateLock());
+  RegKey client_key;
+  HRESULT hr = client_key.Create(GetClientKeyName(GUID_NULL, product_guid));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = client_key.SetValue(kRegValueProductVersion, kRegisterProductVersion);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // AppName is not a required parameter since it's only used for being able to
+  // easily tell what application is there when reading the registry.
+  VERIFY1(SUCCEEDED(client_key.SetValue(kRegValueAppName, product_name)));
+
+  return S_OK;
+}
+
+HRESULT AppManager::UnregisterProduct(const GUID& product_guid) {
+  __mutexScope(GetRegistryStableStateLock());
+  return RegKey::DeleteKey(GetClientKeyName(GUID_NULL, product_guid), true);
+}
+#endif
+
+}  // namespace omaha
diff --git a/goopdate/app_manager.h b/goopdate/app_manager.h
new file mode 100644
index 0000000..12e862b
--- /dev/null
+++ b/goopdate/app_manager.h
@@ -0,0 +1,254 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_MANAGER_H_
+#define OMAHA_GOOPDATE_APP_MANAGER_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "omaha/base/synchronized.h"
+
+namespace omaha {
+
+class App;
+class RegKey;
+
+typedef std::vector<CString> AppIdVector;
+
+// Manages the persistence of application state in the registry.
+// All functions that operate on model objects assume the call is protected by
+// the model lock.
+// All public functions hold a registry access lock for the duration of registry
+// accesses in that function. Unless otherwise noted, read operations may return
+// inconsistent/unstable state in some cases. Examples include:
+// * The app installer is running and modifying the registry (not all installers
+//   acquire this lock before modifying the registry).
+// * Omaha is in the process of installing an app, and the read occurred between
+//   registry operations (i.e. after WritePreInstallData() but before
+//   WriteAppPersistentData().
+// If your operation absolutely needs consistent/stable state, use the functions
+// that ensure this.
+// All write functions assume that the lock returned by
+// GetRegistryStableStateLock() is held. Reads do not require this lock to be
+// held.
+class AppManager {
+ public:
+  // These values are a public API. Do not remove or move existing values.
+  enum InstallerResult {
+    INSTALLER_RESULT_SUCCESS = 0,
+    INSTALLER_RESULT_FAILED_CUSTOM_ERROR = 1,
+    INSTALLER_RESULT_FAILED_MSI_ERROR = 2,
+    INSTALLER_RESULT_FAILED_SYSTEM_ERROR = 3,
+    INSTALLER_RESULT_EXIT_CODE = 4,
+    INSTALLER_RESULT_DEFAULT = INSTALLER_RESULT_EXIT_CODE,
+    INSTALLER_RESULT_MAX,
+  };
+
+  static HRESULT CreateInstance(bool is_machine);
+  static void DeleteInstance();
+
+  static AppManager* Instance();
+
+  // Reads the "pv" value from Google\Update\Clients\{app_guid}, and is used by
+  // the Update3WebControl. This method does not take any locks, and is not
+  // recommended for use in any other scenario.
+  static HRESULT ReadAppVersionNoLock(bool is_machine, const GUID& app_guid,
+                                      CString* version);
+
+  bool IsAppRegistered(const GUID& app_guid) const;
+  bool IsAppUninstalled(const GUID& app_guid) const;
+
+  // Adds all registered products to bundle.
+  HRESULT GetRegisteredApps(AppIdVector* app_ids) const;
+
+  // Adds all uninstalled products to bundle.
+  HRESULT GetUninstalledApps(AppIdVector* app_ids) const;
+
+  // Adds all OEM installed apps that user has accepted EULA either explicitly
+  // or implicitly.
+  HRESULT GetOemInstalledAndEulaAcceptedApps(AppIdVector* app_ids) const;
+
+  // TODO(omaha3): Consider moving these two RegistrationUpdateHook functions
+  // out of this class. Instead, AppManager should expose a function to obtain
+  // the hook for each app.
+
+  // CoCreates and runs the HookClsid for app_id.
+  HRESULT RunRegistrationUpdateHook(const CString& app_id) const;
+
+  // CoCreates and runs HookClsids for registered products.
+  HRESULT RunAllRegistrationUpdateHooks() const;
+
+  // Populates the app object with the persisted state stored in the registry.
+  HRESULT ReadAppPersistentData(App* app);
+
+  // Populates the app object with the install time diff based on the install
+  // time stored in the registry.
+  // If the app is registered or has pv value, app's install time diff will be
+  // calculated based on InstallTime value from the registry, or 0 if the value
+  // is not there. For other cases, the install time diff will be -1 day.
+  void ReadAppInstallTimeDiff(App* app);
+
+  // Populates the app object with the persisted state for an uninstalled app
+  // stored in the registry.
+  HRESULT ReadUninstalledAppPersistentData(App* app);
+
+  // Sets dynamic install parameters that the installer or app may use.
+  // Call this method before calling the installer.
+  HRESULT WritePreInstallData(const App& app);
+
+  // Reads Installer Result API values the installer may have written to the
+  // registry. Clears all values after reading.
+  void ReadInstallerResultApiValues(const GUID& app_guid,
+                                    InstallerResult* installer_result,
+                                    DWORD* installer_error,
+                                    DWORD* installer_extra_code1,
+                                    CString* installer_result_uistring,
+                                    CString* installer_success_launch_cmd);
+
+  // Clears the Installer Result API values from the registry.
+  void ClearInstallerResultApiValues(const GUID& app_guid);
+
+  // Reads the values the app wrote to the Clients key and stores them in the
+  // app object. Replaces existing values.
+  HRESULT ReadInstallerRegistrationValues(App* app);
+
+  // Updates relevant values of the app object in the registry after a
+  // successful update check, which is either a "noupdate" response or an update
+  // available even if it will not be applied.
+  void PersistSuccessfulUpdateCheckResponse(const App& app,
+                                            bool is_update_available);
+
+  // Persists relevant values of the app object in the registry after a
+  // successful install.
+  void PersistSuccessfulInstall(const App& app);
+
+  // Copies product version and language from client key to client state key.
+  // Returns S_OK when the client key does not exist.
+  HRESULT SynchronizeClientState(const GUID& app_guid);
+
+  // Updates application state after an update check request has been
+  // successfully sent to the server.
+  HRESULT PersistUpdateCheckSuccessfullySent(
+      const App& app,
+      int elapsed_seconds_since_day_start);
+
+  // TODO(omaha3): Most of these methods should be eliminated or moved (i.e. to
+  // App) since we only want to write the registry in one or two functions.
+  // Can't make them all private in the meantime because unit tests use them.
+
+  // Clears the OEM-installed flag for the apps.
+  void ClearOemInstalled(const AppIdVector& app_ids);
+
+  // Obtains usage stats information from the stored information about update
+  // available events for the app.
+  void ReadUpdateAvailableStats(const GUID& app_guid,
+                                DWORD* update_responses,
+                                DWORD64* time_since_first_response_ms);
+
+  // Removes the ClientState and ClientStateMedium keys for the application.
+  HRESULT RemoveClientState(const GUID& app_guid);
+
+  // Returns a reference to the lock that ensures the registry is in a stable
+  // state (i.e. no app is being installed). Acquire this lock before calling
+  // read functions if you require a consistent/stable snapshot of the system
+  // (for example, to determine whether Omaha should install). Because this
+  // lock is held throughout app install, the Lock() call could block for
+  // seconds or more.
+  Lockable& GetRegistryStableStateLock() { return registry_stable_state_lock_; }
+
+  // Gets the time since InstallTime was written. Returns 0 if InstallTime
+  // could not be read. This could occur if the app is not already installed or
+  // there is no valid install time in the registry, which can occur for apps
+  // installed before installtime was implemented.
+  uint32 GetInstallTimeDiffSec(const GUID& app_guid) const;
+
+#if 0
+  HRESULT RegisterProduct(const GUID& product_guid,
+                          const CString& product_name);
+  HRESULT UnregisterProduct(const GUID& product_guid);
+#endif
+
+  bool IsAppRegistered(const CString& app_id) const;
+  bool IsAppUninstalled(const CString& app_id) const;
+  bool IsAppOemInstalledAndEulaAccepted(const CString& app_id) const;
+
+ private:
+  explicit AppManager(bool is_machine);
+  ~AppManager() {}
+
+  bool InitializeRegistryLock();
+
+  CString GetClientKeyName(const GUID& app_guid) const;
+  CString GetClientStateKeyName(const GUID& app_guid) const;
+  CString GetClientStateMediumKeyName(const GUID& app_guid) const;
+
+  // Opens the app's Client key for read access.
+  HRESULT OpenClientKey(const GUID& app_guid, RegKey* client_key) const;
+  // Opens the app's ClientState key with the specified access.
+  HRESULT OpenClientStateKey(const GUID& app_guid,
+                             REGSAM sam_desired,
+                             RegKey* client_state_key) const;
+  // Creates the app's ClientState key.
+  HRESULT CreateClientStateKey(const GUID& app_guid, RegKey* client_state_key);
+
+  // Write the TT Token with what the server returned.
+  HRESULT SetTTToken(const App& app);
+
+  // Stores information about the update available event for the app.
+  // Call each time an update is available.
+  void UpdateUpdateAvailableStats(const GUID& app_guid);
+
+  HRESULT ClearInstallationId(const App& app);
+
+  // Writes the day start time when last active ping/roll call happened to
+  // registry.
+  void SetLastPingDayStartTime(const App& app,
+                               int elapsed_seconds_since_day_start);
+
+  bool IsRegistryStableStateLockedByCaller() const {
+    return ::GetCurrentThreadId() == registry_stable_state_lock_.GetOwner();
+  }
+
+  const bool is_machine_;
+
+  // Locks.
+  // If it is going to be acquired, registry_stable_state_lock_ should always be
+  // acquired before registry_access_lock_.
+  // registry_access_lock_ is only ever acquired by this class and app
+  // installers.
+
+  // Ensures that each function's access is on a stable snapshot of the
+  // registry, excluding values modified by the installer.
+  GLock registry_access_lock_;
+
+  // Ensures the registry is in a stable state (i.e. all apps are fully
+  // installed and no installer is running that might be modifying the
+  // registry.) Uninstalls are still an issue unless the app uninstaller informs
+  // Omaha that it is uninstalling the app.
+  LLock registry_stable_state_lock_;
+
+  static AppManager* instance_;
+
+  friend class RunRegistrationUpdateHooksFunc;
+  friend class AppManagerTestBase;
+
+  DISALLOW_COPY_AND_ASSIGN(AppManager);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_MANAGER_H_
diff --git a/goopdate/app_manager_unittest.cc b/goopdate/app_manager_unittest.cc
new file mode 100644
index 0000000..f5b4727
--- /dev/null
+++ b/goopdate/app_manager_unittest.cc
@@ -0,0 +1,2632 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <vector>
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/error.h"
+#include "omaha/base/highres_timer-win32.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/thread.h"
+#include "omaha/base/time.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/base/wmi_query.h"
+#include "omaha/common/app_registry_utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/goopdate/app_manager.h"
+#include "omaha/goopdate/app_unittest_base.h"
+#include "omaha/setup/setup_google_update.h"
+#include "omaha/testing/unit_test.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace omaha {
+
+// TODO(omaha): there is a problem with this unit test. The model is built
+// bottom up. This makes it impossible to set the references to parents. Will
+// have to fix the code, eventually using Builder DP to create a bunch of
+// models containing bundles, apps, and such.
+
+namespace {
+
+const TCHAR* const kGuid1 = _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}");
+const TCHAR* const kGuid2 = _T("{A979ACBD-1F55-4b12-A35F-4DBCA5A7CCB8}");
+const TCHAR* const kGuid3 = _T("{661045C5-4429-4140-BC48-8CEA241D1DEF}");
+const TCHAR* const kGuid4 = _T("{AAFA1CF9-E94F-42e6-A899-4CD27F37D5A7}");
+const TCHAR* const kGuid5 = _T("{3B1A3CCA-0525-4418-93E6-A0DB3398EC9B}");
+const TCHAR* const kGuid6 = _T("{F3F2CFD4-5F98-4bf0-ABB0-BEEEA46C62B4}");
+const TCHAR* const kGuid7 = _T("{6FD2272F-8583-4bbd-895A-E65F8003FC7B}");
+const TCHAR* const kIid1  = _T("{F723495F-8ACF-4746-8240-643741C797B5}");
+
+const TCHAR* const kNonExistentClsid =
+    _T("{BC00156D-3B01-4ba3-9F5E-2C46E8B6E824}");
+
+const TCHAR* const kGuid1ClientsKeyPathUser =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME
+    _T("\\") PRODUCT_NAME _T("\\Clients\\")
+    _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}");
+const TCHAR* const kGuid1ClientsKeyPathMachine =
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME
+    _T("\\") PRODUCT_NAME _T("\\Clients\\")
+    _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}");
+const TCHAR* const kGuid1ClientStateKeyPathUser =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME
+    _T("\\") PRODUCT_NAME _T("\\ClientState\\")
+    _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}");
+const TCHAR* const kGuid1ClientStateKeyPathMachine =
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME
+    _T("\\") PRODUCT_NAME _T("\\ClientState\\")
+    _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}");
+
+const TCHAR* const kDefaultAppName = SHORT_COMPANY_NAME _T(" Application");
+
+const uint32 kInitialInstallTimeDiff = static_cast<uint32>(-1 * kSecondsPerDay);
+
+// Initializes a GLock in the same way AppManager does. Used for lock conflict
+// tests.
+void InitializeAppManagerRegistryLock(bool is_machine, GLock* lock) {
+  ASSERT1(lock);
+  NamedObjectAttributes lock_attr;
+  GetNamedObjectAttributes(kRegistryAccessMutex, is_machine, &lock_attr);
+  EXPECT_SUCCEEDED(lock->InitializeWithSecAttr(lock_attr.name, &lock_attr.sa));
+}
+
+}  // namespace
+
+// Helper functions defined in other test files.
+void ValidateExpectedValues(const App& expected, const App& actual);
+void VerifyHklmKeyHasMediumIntegrity(const CString& key_full_name);
+void VerifyHklmKeyHasDefaultIntegrity(const CString& key_full_name);
+
+class AppManagerTestBase : public AppTestBaseWithRegistryOverride {
+ public:
+  static void SetDisplayName(const CString& name, App* app) {
+    ASSERT1(app);
+    app->display_name_ = name;
+  }
+
+ protected:
+  // Creates the application registration entries based on the passed in data.
+  // If passed an application that is uninstalled, the function only creates
+  // the registration entries in the client state and no information is written
+  // in the clients.
+  static void CreateAppRegistryState(const App& app,
+                                     bool is_machine,
+                                     const CString& previous_version,
+                                     bool can_write_clients_key) {
+    CString clients_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->registry_clients(is_machine),
+        app.app_guid_string());
+    CString client_state_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->registry_client_state(is_machine),
+        app.app_guid_string());
+    const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+    RegKey client_key;
+    if (can_write_clients_key) {
+      ASSERT_SUCCEEDED(client_key.Create(clients_key_name));
+
+      CString current_version(app.current_version()->version());
+      if (!current_version.IsEmpty()) {
+        ASSERT_SUCCEEDED(client_key.SetValue(kRegValueProductVersion,
+                                             current_version));
+      }
+
+      if (!app.display_name_.IsEmpty()) {
+        ASSERT_SUCCEEDED(client_key.SetValue(kRegValueAppName,
+                                             app.display_name_));
+      }
+    }
+
+    RegKey client_state_key;
+    ASSERT_SUCCEEDED(client_state_key.Create(client_state_key_name));
+
+    if (!previous_version.IsEmpty()) {
+      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueProductVersion,
+                                                 previous_version));
+    }
+
+    if (!app.language_.IsEmpty()) {
+      // TODO(omaha3): This is some interesting logic wrt Clients/ClientState.
+      // Does it still make sense for Omaha 3?
+      if (can_write_clients_key) {
+        ASSERT_SUCCEEDED(client_key.SetValue(kRegValueLanguage,
+                                             app.language_));
+      } else {
+        ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueLanguage,
+                                                   app.language_));
+      }
+    }
+
+    if (app.did_run_ != ACTIVE_UNKNOWN) {
+      CString dr = (app.did_run_ == ACTIVE_NOTRUN) ? _T("0") : _T("1");
+      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueDidRun,
+                                                 dr));
+    }
+
+    if (!app.ap_.IsEmpty()) {
+      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueAdditionalParams,
+                                                 app.ap_));
+    }
+
+    if (!app.tt_token_.IsEmpty()) {
+      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueTTToken,
+                                                 app.tt_token_));
+    }
+
+    if (!::IsEqualGUID(app.iid_, GUID_NULL)) {
+      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueInstallationId,
+                                                 GuidToString(app.iid_)));
+    }
+
+    if (!app.brand_code_.IsEmpty()) {
+      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueBrandCode,
+                                                 app.brand_code_));
+    }
+
+    if (!app.client_id_.IsEmpty()) {
+      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueClientId,
+                                                 app.client_id_));
+    }
+
+    if (!app.referral_id_.IsEmpty()) {
+      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueReferralId,
+                                                 app.referral_id_));
+    }
+
+    if (!app.referral_id_.IsEmpty()) {
+      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueReferralId,
+                                                 app.referral_id_));
+    }
+
+    if (app.install_time_diff_sec_) {
+      const DWORD install_time = now - app.install_time_diff_sec_;
+      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueInstallTimeSec,
+                                                 install_time));
+    }
+
+    if (app.is_eula_accepted_ == TRISTATE_FALSE) {
+      ASSERT_SUCCEEDED(client_state_key.SetValue(_T("eulaaccepted"),
+                                                 static_cast<DWORD>(0)));
+    }
+
+    int days = app.days_since_last_active_ping();
+
+    if (days != -1) {
+      EXPECT_GE(days, 0);
+      ASSERT1(now > static_cast<uint32>(days * kSecondsPerDay));
+      uint32 last_active_time = now - days * kSecondsPerDay;
+
+      ASSERT_SUCCEEDED(client_state_key.SetValue(
+          kRegValueActivePingDayStartSec,
+          static_cast<DWORD>(last_active_time)));
+    }
+
+    days = app.days_since_last_roll_call();
+    if (days != -1) {
+      EXPECT_GE(days, 0);
+      EXPECT_GE(now, static_cast<uint32>(days * kSecondsPerDay));
+
+      uint32 last_roll_call_time = now - days * kSecondsPerDay;
+
+      ASSERT_SUCCEEDED(client_state_key.SetValue(
+          kRegValueRollCallDayStartSec,
+          static_cast<DWORD>(last_roll_call_time)));
+    }
+  }
+
+  // App will be cleaned up when bundle is destroyed.
+  // This is a hack for creating registry data. Would be nice to have a
+  // different mechanism.
+  App* CreateAppForRegistryPopulation(const TCHAR* app_id) {
+    App* app = NULL;
+    EXPECT_SUCCEEDED(
+        dummy_app_bundle_for_app_creation_->createApp(CComBSTR(app_id), &app));
+    ASSERT1(app);
+
+    // install_time_diff_sec_ is -1 day for new app. After that, the app
+    // becomes registered and the install age will be 0. So set the time to 0
+    // to make the expected value and actual equal.
+    app->install_time_diff_sec_ = 0;
+    return app;
+  }
+
+  static void PopulateExpectedApp1ClientsOnly(App* expected_app) {
+    ASSERT_TRUE(expected_app);
+    expected_app->current_version()->set_version(_T("1.1.1.3"));
+    expected_app->language_ = _T("abc");
+    expected_app->display_name_ = _T("My App");
+
+    // This is the result when Client State does not exist.
+    expected_app->install_time_diff_sec_ =
+        static_cast<uint32>(-1 * kSecondsPerDay);
+  }
+
+  static void PopulateExpectedApp1(App* expected_app) {
+    ASSERT_TRUE(expected_app);
+    expected_app->current_version()->set_version(_T("1.1.1.3"));
+    expected_app->language_ = _T("abc");
+    expected_app->display_name_ = _T("My App");
+    expected_app->ap_ = _T("Test ap");
+    expected_app->tt_token_ = _T("Test TT Token");
+    expected_app->iid_ =
+        StringToGuid(_T("{F723495F-8ACF-4746-8240-643741C797B5}"));
+    expected_app->brand_code_ = _T("GOOG");
+    expected_app->client_id_ = _T("someclient");
+    // Do not set referral_id or install_time_diff_sec because these are not
+    // expected in most cases.
+    expected_app->did_run_ = ACTIVE_RUN;
+    expected_app->set_days_since_last_active_ping(3);
+    expected_app->set_days_since_last_roll_call(1);
+  }
+
+  static void PopulateExpectedApp2(App* expected_app) {
+    ASSERT_TRUE(expected_app);
+    expected_app->current_version()->set_version(_T("1.2.1.3"));
+    expected_app->language_ = _T("de");
+    expected_app->ap_ = _T("beta");
+    expected_app->tt_token_ = _T("beta TT Token");
+    expected_app->iid_ =
+        StringToGuid(_T("{431EC961-CFD8-49ea-AB7B-2B99BCA274AD}"));
+    expected_app->brand_code_ = _T("GooG");
+    expected_app->client_id_ = _T("anotherclient");
+    expected_app->did_run_ = ACTIVE_NOTRUN;
+    expected_app->set_days_since_last_active_ping(100);
+    expected_app->set_days_since_last_roll_call(1);
+  }
+
+  static void PopulateExpectedUninstalledApp(const CString& uninstalled_version,
+                                             App* expected_app) {
+    ASSERT_TRUE(expected_app);
+    PopulateExpectedApp2(expected_app);
+
+    expected_app->current_version()->set_version(uninstalled_version);
+  }
+
+  static void SetAppInstallTimeDiffSec(App* app,
+                                       uint32 install_time_diff_sec) {
+    ASSERT_TRUE(app);
+    app->install_time_diff_sec_ = install_time_diff_sec;
+  }
+
+  explicit AppManagerTestBase(bool is_machine)
+      : AppTestBaseWithRegistryOverride(is_machine, true),
+        app_manager_(NULL),
+        app_(NULL),
+        guid1_(StringToGuid(kGuid1)) {}
+
+  virtual void SetUp() {
+    AppTestBaseWithRegistryOverride::SetUp();
+
+    app_manager_ = AppManager::Instance();
+    ASSERT_TRUE(app_manager_);
+
+    // Initialize the second bundle.
+    dummy_app_bundle_for_app_creation_ = model_->CreateAppBundle(is_machine_);
+    ASSERT_TRUE(dummy_app_bundle_for_app_creation_.get());
+
+    EXPECT_SUCCEEDED(dummy_app_bundle_for_app_creation_->put_displayName(
+                         CComBSTR(_T("My Bundle"))));
+    EXPECT_SUCCEEDED(dummy_app_bundle_for_app_creation_->put_displayLanguage(
+                         CComBSTR(_T("en"))));
+    EXPECT_SUCCEEDED(dummy_app_bundle_for_app_creation_->put_installSource(
+                         CComBSTR(_T("unittest"))));
+    // TODO(omaha3): Address with the TODO in AppBundleInitializedTest::SetUp().
+    if (is_machine_) {
+      SetAppBundleStateForUnitTest(dummy_app_bundle_for_app_creation_.get(),
+                                   new fsm::AppBundleStateInitialized);
+    } else {
+      EXPECT_SUCCEEDED(dummy_app_bundle_for_app_creation_->initialize());
+    }
+
+    EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app_));
+    ASSERT_TRUE(app_);
+  }
+
+  virtual void TearDown() {
+    app_manager_ = NULL;
+
+    AppTestBaseWithRegistryOverride::TearDown();
+  }
+
+  static void UpdateUpdateAvailableStats(const GUID& app_guid,
+                                         AppManager* app_manager) {
+    ASSERT1(app_manager);
+    app_manager->UpdateUpdateAvailableStats(app_guid);
+  }
+
+  CString GetClientKeyName(const GUID& app_guid) const {
+    return app_manager_->GetClientKeyName(app_guid);
+  }
+
+  static void SetAppGuid(const CString& guid, App* app) {
+    ASSERT1(app);
+    app->app_guid_ = StringToGuid(guid);
+  }
+
+  bool IsClientStateKeyPresent(const App& app) {
+    CString client_state_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->registry_client_state(is_machine_),
+        GuidToString(app.app_guid_));
+
+    return RegKey::HasKey(client_state_key_name);
+  }
+
+  // TODO(omaha3): In this test or elsewhere, test the new Omaha 3 behaviors:
+  // IID is deleted if GUID_NULL and dr is cleared.
+  // TODO(omaha3): Add checks for values set/not set by
+  // app_registry_utils::PersistSuccessfulInstall().
+  void PersistSuccessfulInstallTest() {
+    // Create the data the installer would have written. These values should
+    // not be read below because the caller is responsible for updating these
+    // values.
+    CString clients_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->registry_clients(is_machine_),
+        kGuid1);
+    EXPECT_SUCCEEDED(RegKey::SetValue(clients_key_name,
+                                     kRegValueProductVersion,
+                                     _T("9.8.7.6")));
+    EXPECT_SUCCEEDED(
+        RegKey::SetValue(clients_key_name, kRegValueLanguage, _T("fr")));
+
+    // Populate the App structure. For the most part, these values are not
+    // used. Exceptions are:
+    // * pv and language are written.
+    // * iid is written to the registry.
+    app_->display_name_ = _T("foo");
+    app_->next_version()->set_version(_T("4.5.6.7"));
+    app_->language_ = _T("de");
+    app_->ap_ = _T("test ap");
+    app_->tt_token_ = _T("test TT Token");
+    app_->iid_ =
+        StringToGuid(_T("{64333341-CA93-490d-9FB7-7FC5728721F4}"));
+    app_->brand_code_ = _T("g00g");
+    app_->client_id_ = _T("myclient");
+    app_->referral_id_ = _T("somereferrer");
+    app_->set_days_since_last_active_ping(-1);
+    app_->set_days_since_last_roll_call(-1);
+    EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE));
+
+    __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+
+    app_manager_->PersistSuccessfulInstall(*app_);
+
+    const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+    // Validate the results.
+    CString client_state_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->registry_client_state(is_machine_),
+        kGuid1);
+    RegKey client_state_key;
+    EXPECT_SUCCEEDED(client_state_key.Create(client_state_key_name));
+
+    ValidateClientStateMedium(is_machine_, kGuid1);
+
+    // Check version is based on app_ and not read from Clients key.
+    CString client_state_version;
+    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueProductVersion,
+                                               &client_state_version));
+    EXPECT_STREQ(_T("4.5.6.7"), app_->next_version()->version());
+    EXPECT_STREQ(_T("4.5.6.7"), client_state_version);
+
+    // Check language is based on app_ and not read from Clients key.
+    CString language;
+    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueLanguage, &language));
+    EXPECT_STREQ(_T("de"), app_->language_);
+    EXPECT_STREQ(_T("de"), language);
+
+    // Check iid is set correctly in ClientState.
+    CString iid;
+    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueInstallationId, &iid));
+    EXPECT_STREQ(_T("{64333341-CA93-490D-9FB7-7FC5728721F4}"),
+                 GuidToString(app_->iid_));
+    EXPECT_STREQ(_T("{64333341-CA93-490D-9FB7-7FC5728721F4}"), iid);
+
+    DWORD last_successful_check(0);
+    EXPECT_SUCCEEDED(
+        client_state_key.GetValue(kRegValueLastSuccessfulCheckSec,
+                                  &last_successful_check));
+    EXPECT_GE(now, last_successful_check);
+    EXPECT_GE(static_cast<uint32>(200), now - last_successful_check);
+
+    // Check other values were not written.
+    EXPECT_EQ(4, client_state_key.GetValueCount());
+    EXPECT_FALSE(client_state_key.HasValue(kRegValueAdditionalParams));
+    EXPECT_FALSE(client_state_key.HasValue(kRegValueBrandCode));
+    EXPECT_FALSE(client_state_key.HasValue(kRegValueBrowser));
+    EXPECT_FALSE(client_state_key.HasValue(kRegValueClientId));
+    EXPECT_FALSE(client_state_key.HasValue(kRegValueDidRun));
+    EXPECT_FALSE(client_state_key.HasValue(kRegValueOemInstall));
+    EXPECT_FALSE(client_state_key.HasValue(kRegValueReferralId));
+    EXPECT_FALSE(client_state_key.HasValue(kRegValueEulaAccepted));
+    EXPECT_FALSE(client_state_key.HasValue(kRegValueUsageStats));
+    EXPECT_FALSE(client_state_key.HasValue(kRegValueInstallTimeSec));
+    EXPECT_FALSE(client_state_key.HasValue(kRegValueLastUpdateTimeSec));
+    EXPECT_FALSE(client_state_key.HasValue(kRegValueTTToken));
+    EXPECT_FALSE(client_state_key.HasValue(kRegValueActivePingDayStartSec));
+    EXPECT_FALSE(client_state_key.HasValue(kRegValueRollCallDayStartSec));
+
+    EXPECT_TRUE(app_registry_utils::IsAppEulaAccepted(is_machine_,
+                                                      kGuid1,
+                                                      false));
+  }
+
+  void PersistUpdateCheckSuccessfullySent_AllUpdated() {
+    const CString client_state_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->registry_client_state(is_machine_),
+        kGuid1);
+
+    // Create the test data.
+    App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+    expected_app->current_version()->set_version(_T("1.0.0.0"));
+    expected_app->iid_ = StringToGuid(kIid1);
+    expected_app->did_run_ = ACTIVE_RUN;
+    // Set non-zero values for activities so that the registry values can
+    // be updated.
+    expected_app->set_days_since_last_active_ping(4);
+    expected_app->set_days_since_last_roll_call(2);
+    CreateAppRegistryState(*expected_app, is_machine_, _T(""), true);
+
+    __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+
+    __mutexBlock(app_->model()->lock()) {
+      EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+
+      // We only want to make sure the timestamps in the registry are updated
+      // and don't care about time_since_midnight_sec here, so just pass a 0 as
+      // the first parameter.
+      EXPECT_SUCCEEDED(
+          app_manager_->PersistUpdateCheckSuccessfullySent(*app_, 0));
+    }
+
+    // Validate the results.
+    RegKey client_state_key;
+    EXPECT_SUCCEEDED(client_state_key.Open(client_state_key_name));
+
+    // Check installation id removed.
+    EXPECT_FALSE(client_state_key.HasValue(kRegValueInstallationId));
+
+    // Check ping timestamps are updated.
+    const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+    DWORD last_active_ping_day_start_sec = 0;
+    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueActivePingDayStartSec,
+        &last_active_ping_day_start_sec));
+    EXPECT_GE(now, last_active_ping_day_start_sec);
+    EXPECT_LE(now, last_active_ping_day_start_sec + kMaxTimeSinceMidnightSec);
+
+    DWORD last_roll_call_day_start_sec = 0;
+    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueRollCallDayStartSec,
+        &last_roll_call_day_start_sec));
+    EXPECT_GE(now, last_roll_call_day_start_sec);
+    EXPECT_LE(now, last_roll_call_day_start_sec + kMaxTimeSinceMidnightSec);
+
+    // Check did_run is cleared.
+    CString did_run;
+    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueDidRun, &did_run));
+    EXPECT_STREQ(_T("0"), did_run);
+
+    // Check that members in app_ are not changed.
+    EXPECT_TRUE(expected_app->iid() == app_->iid());
+    EXPECT_EQ(expected_app->days_since_last_active_ping(),
+              app_->days_since_last_active_ping());
+    EXPECT_EQ(expected_app->days_since_last_roll_call(),
+              app_->days_since_last_roll_call());
+    EXPECT_EQ(expected_app->did_run(), app_->did_run());
+  }
+
+  void PersistUpdateCheckSuccessfullySent_NotRun() {
+    const CString client_state_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->registry_client_state(is_machine_),
+        kGuid1);
+    const int kDaysSinceLastActivePing = 2;
+
+    // Create the test data.
+    App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+    expected_app->current_version()->set_version(_T("1.0.0.0"));
+    expected_app->iid_ = StringToGuid(kIid1);
+    expected_app->did_run_ = ACTIVE_NOTRUN;
+    expected_app->set_days_since_last_active_ping(kDaysSinceLastActivePing);
+    expected_app->set_days_since_last_roll_call(0);
+    CreateAppRegistryState(*expected_app, is_machine_, _T(""), true);
+
+    // Choose a time that is close to current time but with some skew so that
+    // if the registry is rewritten, we won't write the same value again and
+    // the change would be detected.
+    const uint32 base_time = Time64ToInt32(GetCurrent100NSTime()) - 2;
+
+    RegKey client_state_key;
+    EXPECT_SUCCEEDED(client_state_key.Open(client_state_key_name));
+    uint32 last_active_time =
+        base_time - kDaysSinceLastActivePing * kSecondsPerDay;
+    ASSERT_SUCCEEDED(client_state_key.SetValue(
+        kRegValueActivePingDayStartSec,
+        static_cast<DWORD>(last_active_time)));
+
+    ASSERT_SUCCEEDED(client_state_key.SetValue(
+        kRegValueRollCallDayStartSec,
+        static_cast<DWORD>(base_time)));
+
+    __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+
+    __mutexBlock(app_->model()->lock()) {
+      EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+
+      // We only want to make sure the timestamps in the registry are updated
+      // and don't care about time_since_midnight_sec here, so just pass a 0 as
+      // the first parameter.
+      EXPECT_SUCCEEDED(
+          app_manager_->PersistUpdateCheckSuccessfullySent(*app_, 0));
+    }
+
+    // Validate the results.
+
+    // did_run is false so installation id should still exist.
+    CString iid;
+    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueInstallationId, &iid));
+    EXPECT_STREQ(kIid1, iid);
+
+    // did_run is false so active ping timestamp should not be updated.
+    DWORD last_active_ping_day_start_sec = 0;
+    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueActivePingDayStartSec,
+        &last_active_ping_day_start_sec));
+    EXPECT_EQ(last_active_time, last_active_ping_day_start_sec);
+
+    // Previous days_since_last_roll_call is 0 so that timestamp should
+    // not change.
+    DWORD last_roll_call_day_start_sec = 0;
+    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueRollCallDayStartSec,
+        &last_roll_call_day_start_sec));
+    EXPECT_EQ(base_time, last_roll_call_day_start_sec);
+
+    // did_run is still not set.
+    CString did_run;
+    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueDidRun, &did_run));
+    EXPECT_STREQ(_T("0"), did_run);
+
+    // Checks that members in app_ are not changed.
+    EXPECT_TRUE(expected_app->iid() == app_->iid());
+    EXPECT_EQ(expected_app->days_since_last_active_ping(),
+              app_->days_since_last_active_ping());
+    EXPECT_EQ(expected_app->days_since_last_roll_call(),
+              app_->days_since_last_roll_call());
+    EXPECT_EQ(expected_app->did_run(), app_->did_run());
+  }
+
+  void PersistUpdateCheckSuccessfullySent_NoPreviousPing() {
+    const CString client_state_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->registry_client_state(is_machine_),
+        kGuid1);
+    const int kDaysSinceLastActivePing = 2;
+
+    // Create the test data.
+    App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+    expected_app->current_version()->set_version(_T("1.0.0.0"));
+    expected_app->iid_ = StringToGuid(kIid1);
+    expected_app->did_run_ = ACTIVE_UNKNOWN;
+    expected_app->set_days_since_last_active_ping(-1);
+    expected_app->set_days_since_last_roll_call(-1);
+    CreateAppRegistryState(*expected_app, is_machine_, _T(""), true);
+
+    __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+
+    __mutexBlock(app_->model()->lock()) {
+      EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+
+      // We only want to make sure the timestamps in the registry are updated
+      // and don't care about time_since_midnight_sec here, so just pass a 0 as
+      // the first parameter.
+      EXPECT_SUCCEEDED(
+          app_manager_->PersistUpdateCheckSuccessfullySent(*app_, 0));
+    }
+
+    // Validate the results.
+    RegKey client_state_key;
+    EXPECT_SUCCEEDED(client_state_key.Open(client_state_key_name));
+
+    // did_run is unknown so installation id should still exist.
+    CString iid;
+    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueInstallationId, &iid));
+    EXPECT_STREQ(kIid1, iid);
+
+    const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+    // did_run is unknown so active ping timestamp should not be updated.
+    EXPECT_FALSE(client_state_key.HasValue(kRegValueActivePingDayStartSec));
+
+    DWORD last_roll_call_day_start_sec = 0;
+    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueRollCallDayStartSec,
+        &last_roll_call_day_start_sec));
+    EXPECT_GE(now, last_roll_call_day_start_sec);
+    EXPECT_LE(now, last_roll_call_day_start_sec + kMaxTimeSinceMidnightSec);
+
+    // did_run is unknown.
+    CString did_run;
+    EXPECT_FALSE(client_state_key.HasValue(kRegValueDidRun));
+
+    // Checks that members in app_ are not changed.
+    EXPECT_TRUE(expected_app->iid() == app_->iid());
+    EXPECT_EQ(expected_app->days_since_last_active_ping(),
+              app_->days_since_last_active_ping());
+    EXPECT_EQ(expected_app->days_since_last_roll_call(),
+              app_->days_since_last_roll_call());
+    EXPECT_EQ(expected_app->did_run(), app_->did_run());
+  }
+
+  void SynchronizeClientStateTest(const CString& app_id) {
+    const CString client_state_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->registry_client_state(is_machine_),
+        app_id);
+
+    App* expected_app = CreateAppForRegistryPopulation(app_id);
+    PopulateExpectedApp1(expected_app);
+    expected_app->referral_id_ = _T("referrer");
+    EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_FALSE));
+    expected_app->install_time_diff_sec_ = 141516;
+    CreateAppRegistryState(*expected_app, is_machine_, _T(""), true);
+
+    EXPECT_TRUE(RegKey::HasValue(client_state_key_name, _T("referral")));
+
+    __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+
+    App* app = NULL;
+    EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(app_id), &app));
+    ASSERT_TRUE(app);
+
+    __mutexBlock(app_->model()->lock()) {
+      EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app));
+    }
+
+    EXPECT_TRUE(app->referral_id_.IsEmpty());
+
+    __mutexBlock(app_->model()->lock()) {
+      EXPECT_SUCCEEDED(app_manager_->SynchronizeClientState(app->app_guid()));
+    }
+
+    EXPECT_TRUE(app->referral_id_.IsEmpty());
+
+    const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+    EXPECT_TRUE(app->referral_id_.IsEmpty());
+
+    // Validate the results.
+    RegKey client_state_key;
+    EXPECT_SUCCEEDED(client_state_key.Open(client_state_key_name));
+
+    // Check version and language have been copied to client state.
+    EXPECT_STREQ(expected_app->current_version()->version(),
+                 GetSzValue(client_state_key_name, kRegValueProductVersion));
+    EXPECT_STREQ(expected_app->language_,
+                 GetSzValue(client_state_key_name, kRegValueLanguage));
+
+    // Check that ap, brand_code, and client_id, etc. are not changed.
+    EXPECT_STREQ(expected_app->ap_,
+                 GetSzValue(client_state_key_name, kRegValueAdditionalParams));
+    EXPECT_STREQ(expected_app->tt_token_,
+                 GetSzValue(client_state_key_name, kRegValueTTToken));
+    EXPECT_STREQ(expected_app->brand_code_,
+                 GetSzValue(client_state_key_name, kRegValueBrandCode));
+    EXPECT_STREQ(expected_app->client_id_,
+                 GetSzValue(client_state_key_name, kRegValueClientId));
+
+    // install_time_diff_sec should be roughly the same as now - installed.
+    const DWORD install_time =
+        GetDwordValue(client_state_key_name, kRegValueInstallTimeSec);
+    const DWORD calculated_install_diff = now - install_time;
+    EXPECT_GE(calculated_install_diff, expected_app->install_time_diff_sec_);
+    EXPECT_GE(static_cast<uint32>(500),
+              calculated_install_diff - expected_app->install_time_diff_sec_);
+
+    EXPECT_EQ(0, GetDwordValue(client_state_key_name, kRegValueEulaAccepted));
+    EXPECT_FALSE(expected_app->is_eula_accepted_);
+  }
+
+  void WritePreInstallDataTest(App* app, bool test_clearing_values) {
+    ASSERT1(is_machine_ == app->app_bundle()->is_machine());
+    const CString clients_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->registry_clients(is_machine_), kGuid1);
+    const CString client_state_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->registry_client_state(is_machine_),
+        kGuid1);
+
+    const bool expect_has_client_key = RegKey::HasKey(clients_key_name);
+
+    // Populate the test data.
+    app->brand_code_ = _T("GGLG");
+    app->client_id_ = _T("someclient");
+    app->referral_id_ = _T("referrer");
+    app->install_time_diff_sec_ = 657812;   // Not used.
+    app->usage_stats_enable_ = TRISTATE_TRUE;
+    app->browser_type_ = BROWSER_FIREFOX;
+    app->ap_ = _T("test_ap");
+    app->language_ = _T("en");
+
+    if (test_clearing_values) {
+      // Set values in registry and clear them in the app.
+      EXPECT_SUCCEEDED(RegKey::SetValue(
+                           kGuid1ClientsKeyPathUser,
+                           kRegValueBrowser,
+                           static_cast<DWORD>(app->browser_type())));
+      EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
+                                        kRegValueAdditionalParams,
+                                        app->ap()));
+
+      app->browser_type_ = BROWSER_UNKNOWN;
+      app->ap_.Empty();
+    }
+
+    __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+
+    EXPECT_SUCCEEDED(app_manager_->WritePreInstallData(*app));
+    const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+    // Validate the results.
+
+    // WritePreInstallData should never write to client_key, so it shouldn't
+    // exist if it did not before the function call.
+    EXPECT_EQ(expect_has_client_key, RegKey::HasKey(clients_key_name));
+
+    // ClientStateKey should exist.
+    RegKey client_state_key;
+    EXPECT_SUCCEEDED(client_state_key.Open(client_state_key_name));
+
+    ValidateClientStateMedium(is_machine_, kGuid1);
+
+    CString brand_code;
+    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueBrandCode,
+                                               &brand_code));
+    EXPECT_STREQ(_T("GGLG"), brand_code);
+
+    CString client_id;
+    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueClientId, &client_id));
+    EXPECT_STREQ(_T("someclient"), client_id);
+
+    CString referral_id;
+    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueReferralId,
+                                               &referral_id));
+    EXPECT_STREQ(_T("referrer"), referral_id);
+
+    DWORD install_time(0);
+    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueInstallTimeSec,
+                                               &install_time));
+    EXPECT_GE(now, install_time);
+    EXPECT_GE(static_cast<uint32>(200), now - install_time);
+
+    DWORD usage_stats_enable = 0;
+    EXPECT_SUCCEEDED(client_state_key.GetValue(_T("usagestats"),
+                                               &usage_stats_enable));
+    EXPECT_EQ(TRISTATE_TRUE, usage_stats_enable);
+
+    if (test_clearing_values) {
+      EXPECT_FALSE(client_state_key.HasValue(kRegValueBrowser));
+    } else {
+      DWORD browser_type = 0;
+      EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueBrowser,
+                                                 &browser_type));
+      EXPECT_EQ(BROWSER_FIREFOX, browser_type);
+    }
+
+    if (test_clearing_values) {
+      EXPECT_FALSE(client_state_key.HasValue(kRegValueAdditionalParams));
+    } else {
+      CString ap;
+      EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueAdditionalParams,
+                                                 &ap));
+      EXPECT_STREQ(_T("test_ap"), ap);
+    }
+
+    CString lang;
+    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueLanguage, &lang));
+    EXPECT_STREQ(_T("en"), lang);
+
+    // Version should not be written to clientstate by WritePreInstallData().
+    EXPECT_FALSE(RegKey::HasValue(client_state_key_name,
+                                  kRegValueProductVersion));
+  }
+
+  static void ValidateClientStateMedium(bool is_machine,
+                                        const CString& app_guid) {
+    const CString client_state_medium_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->machine_registry_client_state_medium(),
+        app_guid);
+    if (is_machine) {
+      RegKey client_state_medium_key;
+      ASSERT_SUCCEEDED(
+          client_state_medium_key.Open(client_state_medium_key_name));
+      EXPECT_EQ(0, client_state_medium_key.GetValueCount());
+    } else {
+      EXPECT_FALSE(RegKey::HasKey(client_state_medium_key_name));
+      // There is no such thing as a user ClientStateMedium key.
+      const CString user_client_state_medium_key_name = AppendRegKeyPath(
+          USER_KEY GOOPDATE_REG_RELATIVE_CLIENT_STATE_MEDIUM,
+          app_guid);
+      EXPECT_FALSE(RegKey::HasKey(user_client_state_medium_key_name));
+      return;
+    }
+  }
+
+  // Uses SetupGoogleUpdate to create the ClientStateMedium key with the
+  // appropriate permissions. Used to test that the permissions are inherited.
+  void CreateClientStateMediumKey() {
+    SetupGoogleUpdate setup_google_update(true);
+
+    // On Windows 7, AddAllowedAce() can fail if the registry is redirected. So
+    // we ignore errors from this call.
+    setup_google_update.CreateClientStateMedium();
+  }
+
+  AppManager* app_manager_;
+  App* app_;
+  // A second bundle is necessary because the same bundle cannot have the same
+  // app in it more than once and many of these tests create an app to populate
+  // the registry and another to read it.
+  shared_ptr<AppBundle> dummy_app_bundle_for_app_creation_;
+
+  const GUID guid1_;
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(AppManagerTestBase);
+};
+
+// For most tests, the EULA should be accepted.
+class AppManagerMachineTest : public AppManagerTestBase {
+ protected:
+  AppManagerMachineTest() : AppManagerTestBase(true) {}
+};
+
+class AppManagerUserTest : public AppManagerTestBase {
+ protected:
+  AppManagerUserTest() : AppManagerTestBase(false) {}
+};
+
+// These fixtures are also used for ReadUninstalledAppPersistentData tests.
+class AppManagerReadAppPersistentDataMachineTest
+    : public AppManagerMachineTest {
+};
+
+class AppManagerReadAppPersistentDataUserTest
+    : public AppManagerUserTest {
+};
+
+class AppManagerWithBundleTest : public AppManagerTestBase {
+ public:
+  explicit AppManagerWithBundleTest(bool is_machine)
+      : AppManagerTestBase(is_machine) {
+    // CoCreateInstance for registered hook CLSIDs returns ERROR_FILE_NOT_FOUND
+    // instead of REGDB_E_CLASSNOTREG without this WMI hack. This has to be done
+    // before the registry overriding that is done by the base class.
+    WmiQuery wmi_query;
+    EXPECT_SUCCEEDED(wmi_query.Connect(_T("root\\SecurityCenter")));
+  }
+
+  static void PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(
+    bool is_machine,
+    App* expected_app0,
+    App* expected_app1,
+    App* expected_app2,
+    App* opposite_hive_data1,
+    App* opposite_hive_data2);
+
+ protected:
+
+  // Wrappers for static functions; simplifies callers using this test fixture.
+
+  void PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(
+      bool is_machine,
+      App** expected_app0,
+      App** expected_app1,
+      App** expected_app2) {
+    *expected_app0 = CreateAppForRegistryPopulation(kGuid1);
+    *expected_app1 = CreateAppForRegistryPopulation(kGuid2);
+    *expected_app2 = CreateAppForRegistryPopulation(kGuid3);
+    App* opposite_hive_data1 = CreateAppForRegistryPopulation(kGuid6);
+    App* opposite_hive_data2 = CreateAppForRegistryPopulation(kGuid7);
+    PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(
+        is_machine,
+        *expected_app0,
+        *expected_app1,
+        *expected_app2,
+        opposite_hive_data1,
+        opposite_hive_data2);
+  }
+
+  void PopulateForRegistrationUpdateHookTests(
+      bool is_machine,
+      App** expected_app0,
+      App** expected_app1,
+      App** expected_app2,
+      const CString& clsid_app0) {
+    PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(
+        is_machine,
+        expected_app0,
+        expected_app1,
+        expected_app2);
+
+    CString clients_key_name = AppendRegKeyPath(
+        ConfigManager::Instance()->registry_clients(is_machine),
+        (*expected_app0)->app_guid_string());
+    EXPECT_SUCCEEDED(RegKey::SetValue(clients_key_name,
+                                      kRegValueUpdateHookClsid,
+                                      clsid_app0));
+  }
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(AppManagerWithBundleTest);
+};
+
+class AppManagerWithBundleMachineTest : public AppManagerWithBundleTest {
+ protected:
+  AppManagerWithBundleMachineTest() : AppManagerWithBundleTest(true) {}
+};
+
+class AppManagerWithBundleUserTest : public AppManagerWithBundleTest {
+ protected:
+  AppManagerWithBundleUserTest() : AppManagerWithBundleTest(false) {}
+};
+
+class HoldAppManagerLock : public Runnable {
+ public:
+  explicit HoldAppManagerLock(bool is_machine, const int period)
+      : period_(period) {
+    reset(lock_acquired_event_, ::CreateEvent(NULL, false, false, NULL));
+    reset(stop_event_, ::CreateEvent(NULL, false, false, NULL));
+
+    InitializeAppManagerRegistryLock(is_machine, &lock_);
+  }
+
+  virtual void Run() {
+    __mutexScope(lock_);
+
+    EXPECT_TRUE(::SetEvent(get(lock_acquired_event_)));
+
+    // TODO(omaha3): Could just use a sleep if we don't do more tests.
+    EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(get(stop_event_), period_));
+  }
+
+  void Stop() {
+    EXPECT_TRUE(::SetEvent(get(stop_event_)));
+  }
+
+  void WaitForLockToBeAcquired() {
+    EXPECT_EQ(WAIT_OBJECT_0,
+              ::WaitForSingleObject(get(lock_acquired_event_), 2000));
+  }
+
+ private:
+  const int period_;
+  GLock lock_;
+  scoped_event lock_acquired_event_;
+  scoped_event stop_event_;
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(HoldAppManagerLock);
+};
+
+// Provide access to the member functions for other tests without requiring them
+// to know about the test fixture class.
+
+void PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(
+    bool is_machine,
+    App* expected_app0,
+    App* expected_app1,
+    App* expected_app2,
+    App* opposite_hive_data1,
+    App* opposite_hive_data2) {
+  AppManagerWithBundleTest::
+      PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(
+      is_machine,
+      expected_app0,
+      expected_app1,
+      expected_app2,
+      opposite_hive_data1,
+      opposite_hive_data2);
+}
+
+void SetDisplayName(const CString& name, App* app) {
+  AppManagerTestBase::SetDisplayName(name, app);
+}
+
+// TODO(omaha3): Maybe use this to test the similar code in install_apps.cc.
+#if 0
+TEST_F(AppManagerTest, ConvertCommandLineToProductData_Succeeds) {
+  CommandLineAppArgs extra1;
+  extra1.app_guid = guid1_;
+  extra1.app_name = _T("foo");
+  extra1.needs_admin = false;
+  extra1.ap = _T("Test ap");
+  extra1.tt_token = _T("Test TT Token");
+  extra1.encoded_installer_data = _T("%20foobar");
+  extra1.install_data_index = _T("foobar");
+
+  CommandLineAppArgs extra2;
+  extra2.app_guid = StringToGuid(kGuid2);
+  extra2.app_name = _T("bar");
+  extra2.needs_admin = true;    // This gets ignored.
+  extra2.ap = _T("beta");
+  extra2.tt_token = _T("beta TT Token");
+
+  CommandLineAppArgs extra3;
+  extra3.app_guid = StringToGuid(kGuid3);
+  extra3.app_name = _T("bar");
+  extra3.needs_admin = true;    // This gets ignored.
+  extra3.ap = _T("beta");
+  extra3.tt_token = _T("beta TT Token");
+
+  CommandLineArgs args;
+  args.is_interactive_set = true;  // Not used.
+  args.is_machine_set = true;  // Not used.
+  args.is_crash_handler_disabled = true;  // Not used.
+  args.is_eula_required_set = true;
+  args.is_eula_required_set = true;  // Not used.
+  args.webplugin_urldomain = _T("http://nothing.google.com");  // Not used.
+  args.webplugin_args = _T("blah");  // Not used.
+  args.install_source = _T("one_click");
+  args.code_red_metainstaller_path = _T("foo.exe");  // Not used.
+  args.legacy_manifest_path = _T("bar.exe");  // Not used.
+  args.crash_filename = _T("foo.dmp");  // Not used.
+  args.extra.installation_id = StringToGuid(kIid1);
+  args.extra.brand_code = _T("GOOG");
+  args.extra.client_id = _T("someclient");
+  args.extra.experiment = _T("exp1");
+  args.extra.referral_id = _T("referrer1");
+  args.extra.browser_type = BROWSER_IE;
+  args.extra.language = _T("abc");
+  args.extra.usage_stats_enable = TRISTATE_TRUE;
+  args.extra.apps.push_back(extra1);
+  args.extra.apps.push_back(extra2);
+  args.extra.apps.push_back(extra3);
+
+  AppData* expected_data1 = CreateAppData();
+  PopulateExpectedAppData1(guid1_, false, &expected_data1);
+  expected_data1.set_version(_T(""));  // Clear value.
+  expected_data1.set_previous_version(_T(""));  // Clear value.
+  expected_data1.set_did_run(ACTIVE_UNKNOWN);  // Clear value.
+  expected_data1.set_display_name(_T("foo"));
+  expected_data1.set_browser_type(BROWSER_IE);
+  expected_data1.set_install_source(_T("one_click"));
+  expected_data1.set_encoded_installer_data(_T("%20foobar"));
+  expected_data1.set_install_data_index(_T("foobar"));
+  expected_data1.set_usage_stats_enable(TRISTATE_TRUE);
+  expected_data1.set_referral_id(_T("referrer1"));
+  expected_data1.set_install_time_diff_sec(
+      static_cast<uint32>(-1 * kSecondsPerDay));  // New install.
+  expected_data1.set_is_eula_accepted(false);
+  expected_data1.set_days_since_last_active_ping(0);
+  expected_data1.set_days_since_last_roll_call(0);
+
+  AppData* expected_data2 = CreateAppData();
+
+  // Make the first app appear to already be installed but without an
+  // InstallTime. This affects install_time_diff_sec.
+  expected_data2.set_version(_T("4.5.6.7"));
+  CreateAppRegistryState(expected_data2);
+
+  PopulateExpectedAppData2(StringToGuid(kGuid2), false, &expected_data2);
+
+  expected_data2.set_version(_T(""));  // Clear value.
+  expected_data2.set_previous_version(_T(""));  // Clear value.
+  expected_data2.set_did_run(ACTIVE_UNKNOWN);  // Clear value.
+  expected_data2.set_language(_T("abc"));
+  expected_data2.set_display_name(_T("bar"));
+  expected_data2.set_browser_type(BROWSER_IE);
+  expected_data2.set_install_source(_T("one_click"));
+  expected_data2.set_usage_stats_enable(TRISTATE_TRUE);
+  // Override unique expected data because the args apply to all apps.
+  expected_data2.set_iid(StringToGuid(kIid1));
+  expected_data2.set_brand_code(_T("GOOG"));
+  expected_data2.set_client_id(_T("someclient"));
+  expected_data2.set_experiment(_T("exp1"));
+  expected_data2.set_referral_id(_T("referrer1"));
+  expected_data2.set_install_time_diff_sec(0);  // InstallTime is unknown.
+  expected_data2.set_is_eula_accepted(false);
+  expected_data2.set_days_since_last_active_ping(0);
+  expected_data2.set_days_since_last_roll_call(0);
+
+  AppData expected_data3(StringToGuid(kGuid3), false);
+
+  // Make the first app appear to already be installed with a valid InstallTime.
+  // This affects install_time_diff_sec.
+  expected_data3.set_version(_T("4.5.6.7"));
+  expected_data3.set_install_time_diff_sec(123456);  // Known original time.
+  CreateAppRegistryState(expected_data3);
+
+  PopulateExpectedAppData2(&expected_data3);
+  expected_data3.set_version(_T(""));  // Clear value.
+  expected_data3.set_previous_version(_T(""));  // Clear value.
+  expected_data3.set_did_run(AppData::ACTIVE_UNKNOWN);  // Clear value.
+  expected_data3.set_language(_T("abc"));
+  expected_data3.set_display_name(_T("bar"));
+  expected_data3.set_browser_type(BROWSER_IE);
+  expected_data3.set_install_source(_T("one_click"));
+  expected_data3.set_usage_stats_enable(TRISTATE_TRUE);
+  // Override unique expected data because the args apply to all apps.
+  expected_data3.set_iid(StringToGuid(kIid1));
+  expected_data3.set_brand_code(_T("GOOG"));
+  expected_data3.set_client_id(_T("someclient"));
+  expected_data3.set_referral_id(_T("referrer1"));
+  expected_data3.set_is_eula_accepted(false);
+  expected_data3.set_days_since_last_active_ping(0);
+  expected_data3.set_days_since_last_roll_call(0);
+
+  ProductDataVector products;
+  AppManager app_manager(false);
+  app_manager.ConvertCommandLineToProductData(args, &products);
+
+  ASSERT_EQ(3, products.size());
+  EXPECT_EQ(0, products[0].num_components());
+  EXPECT_EQ(0, products[1].num_components());
+  EXPECT_EQ(0, products[2].num_components());
+  ValidateExpectedValues(expected_data1, products[0].app_data());
+  ValidateExpectedValues(expected_data2, products[1].app_data());
+
+  // install_time_diff_sec may be off by a second or so.
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+  EXPECT_GE(products[2].app_data().install_time_diff_sec(),
+            expected_data3.install_time_diff_sec());
+  EXPECT_GE(static_cast<uint32>(500),
+            products[2].app_data().install_time_diff_sec() -
+            expected_data3.install_time_diff_sec());
+  // Fix up expected_data3 or it might fail verification.
+  expected_data3.set_install_time_diff_sec(
+      products[2].app_data().install_time_diff_sec());
+
+  ValidateExpectedValues(expected_data3, products[2].app_data());
+}
+#endif
+
+TEST_F(AppManagerMachineTest, WritePreInstallData) {
+  SetAppGuid(kGuid1, app_);
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE));
+  WritePreInstallDataTest(app_, false);
+
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathMachine,
+                                _T("oeminstall")));
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathMachine,
+                                _T("eulaaccepted")));
+}
+
+TEST_F(AppManagerMachineTest, WritePreInstallData_IsOem) {
+  const DWORD now = Time64ToInt32(GetCurrent100NSTime());
+  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
+                                    _T("OemInstallTime"),
+                                    now));
+  if (vista_util::IsVistaOrLater()) {
+    ASSERT_SUCCEEDED(RegKey::SetValue(
+        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
+        _T("ImageState"),
+        _T("IMAGE_STATE_UNDEPLOYABLE")));
+  } else {
+    ASSERT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
+                                      _T("AuditInProgress"),
+                                      static_cast<DWORD>(1)));
+  }
+
+  SetAppGuid(kGuid1, app_);
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE));
+  WritePreInstallDataTest(app_, false);
+
+  CString oeminstall;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kGuid1ClientStateKeyPathMachine,
+                                    _T("oeminstall"),
+                                    &oeminstall));
+  EXPECT_STREQ(_T("1"), oeminstall);
+
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathMachine,
+                                _T("eulaaccepted")));
+}
+
+// Creates the ClientStateMedium key with the appropriate permissions then
+// verifies that the created app subkey inherits those.
+// The Update key must be created first to avoid applying ClientStateMedium's
+// permissions to all its parent keys.
+// This keys in this test need to inherit the HKLM privileges, so put the
+// override root in HKLM.
+TEST_F(AppManagerMachineTest,
+       WritePreInstallData_CheckClientStateMediumPermissions) {
+  const TCHAR kRegistryHiveOverrideRootInHklm[] =
+      _T("HKLM\\Software\\") SHORT_COMPANY_NAME
+      _T("\\") PRODUCT_NAME _T("\\UnitTest\\");
+  RestoreRegistryHives();
+  hive_override_key_name_ = kRegistryHiveOverrideRootInHklm;
+  RegKey::DeleteKey(hive_override_key_name_);
+  OverrideRegistryHives(hive_override_key_name_);
+
+  EXPECT_SUCCEEDED(RegKey::CreateKey(
+      ConfigManager::Instance()->machine_registry_update()));
+  CreateClientStateMediumKey();
+
+  SetAppGuid(kGuid1, app_);
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE));
+  WritePreInstallDataTest(app_, false);
+
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathMachine,
+                                _T("oeminstall")));
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathMachine,
+                                _T("eulaaccepted")));
+
+  const CString app_client_state_medium_key_name = AppendRegKeyPath(
+      _T("HKLM\\Software\\") SHORT_COMPANY_NAME
+      _T("\\") PRODUCT_NAME _T("\\ClientStateMedium\\"),
+      kGuid1);
+  VerifyHklmKeyHasMediumIntegrity(app_client_state_medium_key_name);
+  VerifyHklmKeyHasDefaultIntegrity(
+      _T("HKLM\\Software\\") SHORT_COMPANY_NAME
+      _T("\\") PRODUCT_NAME _T("\\ClientStateMedium\\"));
+}
+
+TEST_F(AppManagerMachineTest,
+       WritePreInstallData_ClearClientStateMediumUsageStats) {
+  const CString client_state_key_name =
+      AppendRegKeyPath(MACHINE_REG_CLIENT_STATE_MEDIUM, kGuid1);
+  EXPECT_SUCCEEDED(RegKey::SetValue(client_state_key_name,
+                                    _T("usagestats"),
+                                    static_cast<DWORD>(1)));
+
+  SetAppGuid(kGuid1, app_);
+  WritePreInstallDataTest(app_, false);
+
+  EXPECT_FALSE(RegKey::HasValue(client_state_key_name, _T("usagestats")));
+}
+
+// Tests the EULA accepted case too.
+TEST_F(AppManagerUserTest, WritePreInstallData) {
+  SetAppGuid(kGuid1, app_);
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE));
+  WritePreInstallDataTest(app_, false);
+
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("oeminstall")));
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("eulaaccepted")));
+}
+
+TEST_F(AppManagerUserTest,
+       WritePreInstallData_EulaNotAcceptedAppNotRegistered) {
+  SetAppGuid(kGuid1, app_);
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_FALSE));
+
+  WritePreInstallDataTest(app_, false);
+
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("oeminstall")));
+
+  DWORD eula_accepted = 99;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("eulaaccepted"),
+                                    &eula_accepted));
+  EXPECT_EQ(0, eula_accepted);
+}
+
+TEST_F(AppManagerUserTest,
+       WritePreInstallData_EulaNotAcceptedAppAlreadyInstalled) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  SetAppGuid(kGuid1, app_);
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_FALSE));
+
+  WritePreInstallDataTest(app_, false);
+
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("oeminstall")));
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("eulaaccepted")));
+}
+
+TEST_F(AppManagerUserTest,
+       WritePreInstallData_EulaAcceptedAppAlreadyInstalled) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  SetAppGuid(kGuid1, app_);
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE));
+
+  WritePreInstallDataTest(app_, false);
+
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("oeminstall")));
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("eulaaccepted")));
+}
+
+TEST_F(AppManagerUserTest,
+       WritePreInstallData_EulaAcceptedAppAlreadyInstalledAccepted) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+
+  SetAppGuid(kGuid1, app_);
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE));
+
+  WritePreInstallDataTest(app_, false);
+
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("oeminstall")));
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("eulaaccepted")));
+}
+
+TEST_F(AppManagerUserTest,
+       WritePreInstallData_EulaAcceptedAppAlreadyInstalledNotAccepted) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+
+  SetAppGuid(kGuid1, app_);
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE));
+
+  WritePreInstallDataTest(app_, false);
+
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("oeminstall")));
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("eulaaccepted")));
+}
+
+TEST_F(AppManagerReadAppPersistentDataMachineTest, NoApp) {
+  __mutexScope(app_->model()->lock());
+  EXPECT_FAILED(app_manager_->ReadAppPersistentData(app_));
+}
+
+// For new app, the install_time_diff_sec_ should be -1 day.
+TEST_F(AppManagerMachineTest, ReadAppInstallTimeDiff_NewApp) {
+  __mutexScope(app_->model()->lock());
+  app_manager_->ReadAppInstallTimeDiff(app_);
+  EXPECT_EQ(kInitialInstallTimeDiff, app_->install_time_diff_sec());
+}
+
+TEST_F(AppManagerUserTest, ReadAppInstallTimeDiff_NewApp) {
+  __mutexScope(app_->model()->lock());
+  app_manager_->ReadAppInstallTimeDiff(app_);
+  EXPECT_EQ(kInitialInstallTimeDiff, app_->install_time_diff_sec());
+}
+
+// For registered app, the install_time_diff_sec_ should be 0 if InstallTime
+// registry value does not exist.
+TEST_F(AppManagerMachineTest, ReadAppInstallTimeDiff_RegisteredApp) {
+  App* new_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedApp1(new_app);
+  CreateAppRegistryState(*new_app, is_machine_, _T("1.0.0.0"), true);
+
+  __mutexScope(app_->model()->lock());
+  app_manager_->ReadAppInstallTimeDiff(app_);
+  EXPECT_EQ(0, app_->install_time_diff_sec());
+}
+
+// For over-install app, the app is already registered. So
+// install_time_diff_sec_ will be read from InstallTime registry value if
+// exists. Otherwise it should be 0 as verified by the previous test case.
+TEST_F(AppManagerUserTest, ReadAppInstallTimeDiff_OverInstall) {
+  const uint32 kInstallTimeDiffSec = 100000;
+  App* over_install_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedApp1(over_install_app);
+  SetAppInstallTimeDiffSec(over_install_app, kInstallTimeDiffSec);
+  CreateAppRegistryState(*over_install_app, is_machine_, _T("1.1.1.1"), true);
+
+  __mutexScope(app_->model()->lock());
+  app_manager_->ReadAppInstallTimeDiff(app_);
+  EXPECT_GE(app_->install_time_diff_sec(), kInstallTimeDiffSec);
+  EXPECT_LE(app_->install_time_diff_sec(), kInstallTimeDiffSec + 1);
+}
+
+// For uninstalled app, the install_time_diff_sec_ will be read from registry
+// if InstallTime registry value exists.
+TEST_F(AppManagerMachineTest, ReadAppInstallTimeDiff_UninstalledApp) {
+  const uint32 kInstallTimeDiffSec = 123456;
+  App* uninstalled_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedUninstalledApp(_T("1.1.0.0"), uninstalled_app);
+  SetAppInstallTimeDiffSec(uninstalled_app, kInstallTimeDiffSec);
+  CreateAppRegistryState(*uninstalled_app, is_machine_, _T("1.1.0.0"), false);
+
+  __mutexScope(app_->model()->lock());
+  app_manager_->ReadAppInstallTimeDiff(app_);
+  EXPECT_GE(app_->install_time_diff_sec(), kInstallTimeDiffSec);
+  EXPECT_LE(app_->install_time_diff_sec(), kInstallTimeDiffSec + 1);
+}
+
+// A special case. If pv of the uninstalled app doesn't exist, then it is
+// considered not in uninstalled status. InstallTime should be ignored.
+TEST_F(AppManagerUserTest, ReadAppInstallTimeDiff_UninstalledAppWithoutPv) {
+  const uint32 kInstallTimeDiffSec = 112233;
+  App* uninstalled_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedUninstalledApp(_T("1.1.0.0"), uninstalled_app);
+  SetAppInstallTimeDiffSec(uninstalled_app, kInstallTimeDiffSec);
+  CreateAppRegistryState(*uninstalled_app, is_machine_, _T(""), false);
+
+  __mutexScope(app_->model()->lock());
+  app_manager_->ReadAppInstallTimeDiff(app_);
+  EXPECT_EQ(kInitialInstallTimeDiff, app_->install_time_diff_sec());
+}
+
+// Tests clearing of data that is already present when not present in app_.
+TEST_F(AppManagerUserTest,
+       WritePreInstallData_AppAlreadyInstalled_ClearExistingData) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+
+  SetAppGuid(kGuid1, app_);
+
+  WritePreInstallDataTest(app_, true);
+
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                kRegValueBrowser));
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                kRegValueAdditionalParams));
+}
+
+// Tests clearing of data that is already present when not present in app_.
+TEST_F(AppManagerUserTest,
+       WritePreInstallData_AppAlreadyInstalled_OverwriteExistingData) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
+                                    _T("pv"),
+                                    _T("1.2.3.4")));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
+                                    kRegValueBrowser,
+                                    static_cast<DWORD>(BROWSER_CHROME)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
+                                    kRegValueAdditionalParams,
+                                    _T("existingAP")));
+
+  SetAppGuid(kGuid1, app_);
+
+  WritePreInstallDataTest(app_, false);
+
+  EXPECT_EQ(BROWSER_FIREFOX, GetDwordValue(kGuid1ClientStateKeyPathUser,
+                                           kRegValueBrowser));
+  EXPECT_EQ(_T("test_ap"), GetSzValue(kGuid1ClientStateKeyPathUser,
+                                      kRegValueAdditionalParams));
+}
+
+// It is important that previous_version passed to CreateAppRegistryState() be
+// different than the current_version in PopulateExpectedApp1() to ensure the
+// version is populated from Clients and not ClientState.
+TEST_F(AppManagerReadAppPersistentDataUserTest, AppExists) {
+  App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedApp1(expected_app);
+  CreateAppRegistryState(*expected_app, is_machine_, _T("1.0.0.0"), true);
+
+  __mutexScope(app_->model()->lock());
+  EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+
+  EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app, *app_);
+}
+
+TEST_F(AppManagerReadAppPersistentDataMachineTest, AppExists) {
+  App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedApp1(expected_app);
+  CreateAppRegistryState(*expected_app, is_machine_, _T("1.0.0.0"), true);
+
+  __mutexScope(app_->model()->lock());
+  EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+
+  EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app, *app_);
+}
+
+TEST_F(AppManagerReadAppPersistentDataUserTest, AppExists_NoDisplayName) {
+  App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedApp1(expected_app);
+  SetDisplayName(_T(""), expected_app);
+  CreateAppRegistryState(*expected_app, is_machine_, _T("1.0.0.0"), true);
+
+  __mutexScope(app_->model()->lock());
+  EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+
+  SetDisplayName(kDefaultAppName, expected_app);
+
+  EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app, *app_);
+}
+
+TEST_F(AppManagerReadAppPersistentDataUserTest, AppExists_EmptyDisplayName) {
+  App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedApp1(expected_app);
+  CreateAppRegistryState(*expected_app, is_machine_, _T("1.0.0.0"), true);
+  EXPECT_SUCCEEDED(RegKey::SetValue(GetClientKeyName(guid1_),
+                                    kRegValueAppName,
+                                    _T("")));
+
+  __mutexScope(app_->model()->lock());
+  EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+
+  SetDisplayName(kDefaultAppName, expected_app);
+  EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app, *app_);
+}
+
+TEST_F(AppManagerReadAppPersistentDataUserTest, EulaNotAccepted) {
+  App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedApp1(expected_app);
+  CreateAppRegistryState(*expected_app, is_machine_, _T("1.0.0.0"), true);
+
+  // TODO(omaha): We should be able to eliminate the SetValue calls in
+  // AppManagerReadAppPersistentData* by moving put_isEulaAccepted() call above
+  // CreateAppRegistryState().
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_FALSE));
+
+  __mutexScope(app_->model()->lock());
+  EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+
+  ValidateExpectedValues(*expected_app, *app_);
+}
+
+TEST_F(AppManagerReadAppPersistentDataUserTest, EulaAccepted) {
+  App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedApp1(expected_app);
+  CreateAppRegistryState(*expected_app, is_machine_, _T("1.0.0.0"), true);
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_TRUE));
+
+  __mutexScope(app_->model()->lock());
+  EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+
+  ValidateExpectedValues(*expected_app, *app_);
+}
+
+TEST_F(AppManagerReadAppPersistentDataMachineTest, EulaNotAccepted) {
+  App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedApp1(expected_app);
+  CreateAppRegistryState(*expected_app, is_machine_, _T("1.0.0.0"), true);
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathMachine,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(0)));
+  EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_FALSE));
+
+  __mutexScope(app_->model()->lock());
+  EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+
+  ValidateExpectedValues(*expected_app, *app_);
+}
+
+TEST_F(AppManagerReadAppPersistentDataMachineTest, EulaAccepted) {
+  App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedApp1(expected_app);
+  CreateAppRegistryState(*expected_app, is_machine_, _T("1.0.0.0"), true);
+
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathMachine,
+                                    _T("eulaaccepted"),
+                                    static_cast<DWORD>(1)));
+  EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_TRUE));
+
+  __mutexScope(app_->model()->lock());
+  EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+
+  ValidateExpectedValues(*expected_app, *app_);
+}
+
+TEST_F(AppManagerReadAppPersistentDataUserTest, TwoApps) {
+  App* app2 = NULL;
+  EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid2), &app2));
+  ASSERT_TRUE(app2);
+
+  App* expected_app1 = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedApp1(expected_app1);
+  CreateAppRegistryState(*expected_app1, is_machine_, _T("1.0.0.0"), true);
+
+  App* expected_app2 = CreateAppForRegistryPopulation(kGuid2);
+  PopulateExpectedApp1(expected_app2);
+  CreateAppRegistryState(*expected_app2, is_machine_, _T("1.0.0.0"), true);
+
+  __mutexScope(app_->model()->lock());
+
+  EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+  EXPECT_SUCCEEDED(expected_app1->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app1, *app_);
+
+  EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app2));
+  EXPECT_SUCCEEDED(expected_app2->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app2, *app2);
+}
+
+TEST_F(AppManagerReadAppPersistentDataUserTest, NoClientState) {
+  App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedApp1ClientsOnly(expected_app);
+  CreateAppRegistryState(*expected_app, is_machine_, _T("1.0.0.0"), true);
+
+  // CreateAppRegistryState always creates the ClientState key, so delete it.
+  EXPECT_SUCCEEDED(RegKey::DeleteKey(kGuid1ClientStateKeyPathUser));
+
+  EXPECT_FALSE(IsClientStateKeyPresent(*app_));
+
+  __mutexScope(app_->model()->lock());
+  EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+
+  EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app, *app_);
+}
+
+// This is the No Clients key case.
+TEST_F(AppManagerReadAppPersistentDataUserTest,
+       ReadUninstalledAppPersistentData_UninstalledApp) {
+  App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedUninstalledApp(_T("1.1.0.0"), expected_app);
+  CreateAppRegistryState(*expected_app, is_machine_, _T("1.1.0.0"), false);
+
+  __mutexScope(app_->model()->lock());
+  EXPECT_SUCCEEDED(app_manager_->ReadUninstalledAppPersistentData(app_));
+
+  SetDisplayName(kDefaultAppName, expected_app);
+  EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app, *app_);
+
+  // Explicitly verify the absence of a Clients key and the version since these
+  // are important to this test.
+  EXPECT_FALSE(RegKey::HasKey(
+      AppendRegKeyPath(ConfigManager::Instance()->registry_clients(is_machine_),
+      kGuid1)));
+  EXPECT_STREQ(_T("1.1.0.0"), expected_app->current_version()->version());
+
+  // Verify that ReadAppPersistentData does not populate the version.
+  app_->current_version()->set_version(_T(""));
+  expected_app->current_version()->set_version(_T(""));
+  EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+  SetDisplayName(kDefaultAppName, expected_app);
+  ValidateExpectedValues(*expected_app, *app_);
+  EXPECT_TRUE(expected_app->current_version()->version().IsEmpty());
+}
+
+TEST_F(AppManagerReadAppPersistentDataMachineTest,
+       ReadUninstalledAppPersistentData_UninstalledApp) {
+  App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedUninstalledApp(_T("1.1.0.0"), expected_app);
+  EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_TRUE));
+  CreateAppRegistryState(*expected_app, is_machine_, _T("1.1.0.0"), false);
+
+  __mutexScope(app_->model()->lock());
+  EXPECT_SUCCEEDED(app_manager_->ReadUninstalledAppPersistentData(app_));
+
+  SetDisplayName(kDefaultAppName, expected_app);
+  ValidateExpectedValues(*expected_app, *app_);
+
+  // Explicitly verify the absence of a Clients key and the version since these
+  // are important to this test.
+  EXPECT_FALSE(RegKey::HasKey(
+      AppendRegKeyPath(ConfigManager::Instance()->registry_clients(is_machine_),
+      kGuid1)));
+  EXPECT_STREQ(_T("1.1.0.0"), expected_app->current_version()->version());
+
+  // Verify that ReadAppPersistentData does not populate the version.
+  app_->current_version()->set_version(_T(""));
+  expected_app->current_version()->set_version(_T(""));
+  EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+  SetDisplayName(kDefaultAppName, expected_app);
+  ValidateExpectedValues(*expected_app, *app_);
+  EXPECT_TRUE(expected_app->current_version()->version().IsEmpty());
+}
+
+TEST_F(AppManagerReadAppPersistentDataUserTest,
+       ReadUninstalledAppPersistentData_EulaNotAccepted) {
+  App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedUninstalledApp(_T("1.1.0.0"), expected_app);
+  EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_FALSE));
+  CreateAppRegistryState(*expected_app, is_machine_, _T("1.1.0.0"), false);
+
+  __mutexScope(app_->model()->lock());
+  EXPECT_SUCCEEDED(app_manager_->ReadUninstalledAppPersistentData(app_));
+
+  SetDisplayName(kDefaultAppName, expected_app);
+  ValidateExpectedValues(*expected_app, *app_);
+}
+
+// Tests the case where Omaha has created the Client State key before running
+// the installer. Uses PopulateExpectedUninstalledApp then clears pv before
+// writing the data to the registry. is_uninstalled_ is not set to false until
+// after CreateAppRegistryState to prevent Client key from being created.
+TEST_F(AppManagerReadAppPersistentDataUserTest,
+       ClientStateExistsWithoutPvOrClientsKey) {
+  App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedUninstalledApp(_T(""), expected_app);
+  CreateAppRegistryState(*expected_app, is_machine_, _T(""), false);
+
+  // Update expected_app->install_time_diff_sec_ value here since related
+  // registry values have been changed. app_ will be created based on the
+  // new registry status so to make the objects match, reloading here is
+  // necessary.
+  app_manager_->ReadAppInstallTimeDiff(expected_app);
+
+  __mutexScope(app_->model()->lock());
+  EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+
+  SetDisplayName(kDefaultAppName, expected_app);
+  EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app, *app_);
+}
+
+TEST_F(AppManagerReadAppPersistentDataMachineTest,
+       ClientStateExistsWithoutPvOrClientsKey) {
+  App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedUninstalledApp(_T(""), expected_app);
+  CreateAppRegistryState(*expected_app, is_machine_, _T(""), false);
+
+  // Update expected_app->install_time_diff_sec_ value here since related
+  // registry values have been changed. app_ will be created based on the
+  // new registry status so to make the objects match, reloading here is
+  // necessary.
+  app_manager_->ReadAppInstallTimeDiff(expected_app);
+
+  __mutexScope(app_->model()->lock());
+  EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+
+  SetDisplayName(kDefaultAppName, expected_app);
+  EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app, *app_);
+}
+
+// An empty pv value is the same as a populated one for uninstall checks.
+TEST_F(AppManagerReadAppPersistentDataUserTest,
+       ClientStateExistsWithEmptyPvNoClientsKey) {
+  App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedUninstalledApp(_T(""), expected_app);
+  CreateAppRegistryState(*expected_app, is_machine_, _T(""), false);
+
+  // Write the empty pv value.
+  CString client_state_key_name = AppendRegKeyPath(
+      ConfigManager::Instance()->registry_client_state(false),
+      kGuid1);
+  EXPECT_SUCCEEDED(RegKey::SetValue(client_state_key_name,
+                                     kRegValueProductVersion,
+                                     _T("")));
+
+  __mutexScope(app_->model()->lock());
+  EXPECT_SUCCEEDED(app_manager_->ReadAppPersistentData(app_));
+
+  SetDisplayName(kDefaultAppName, expected_app);
+  EXPECT_SUCCEEDED(expected_app->put_isEulaAccepted(VARIANT_TRUE));
+  ValidateExpectedValues(*expected_app, *app_);
+}
+
+TEST_F(AppManagerUserTest,
+       ReadInstallerRegistrationValues_FailsWhenClientsKeyAbsent) {
+  __mutexBlock(app_->model()->lock()) {
+    EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY,
+              app_manager_->ReadInstallerRegistrationValues(app_));
+  }
+
+  EXPECT_TRUE(app_->next_version()->version().IsEmpty());
+  EXPECT_TRUE(app_->language().IsEmpty());
+
+  EXPECT_FALSE(RegKey::HasKey(kGuid1ClientsKeyPathUser));
+  EXPECT_FALSE(RegKey::HasKey(kGuid1ClientStateKeyPathUser));
+}
+
+TEST_F(AppManagerUserTest,
+       ReadInstallerRegistrationValues_FailsWhenVersionValueAbsent) {
+  ASSERT_SUCCEEDED(RegKey::CreateKey(kGuid1ClientsKeyPathUser));
+
+  __mutexBlock(app_->model()->lock()) {
+    EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY,
+              app_manager_->ReadInstallerRegistrationValues(app_));
+  }
+
+  EXPECT_TRUE(app_->next_version()->version().IsEmpty());
+  EXPECT_TRUE(app_->language().IsEmpty());
+
+  EXPECT_TRUE(RegKey::HasKey(kGuid1ClientsKeyPathUser));
+  EXPECT_FALSE(RegKey::HasKey(kGuid1ClientStateKeyPathUser));
+}
+
+TEST_F(AppManagerUserTest,
+       ReadInstallerRegistrationValues_FailsWhenVersionValueEmpty) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
+                                    kRegValueProductVersion,
+                                    _T("")));
+
+  __mutexBlock(app_->model()->lock()) {
+    EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY,
+              app_manager_->ReadInstallerRegistrationValues(app_));
+  }
+
+  EXPECT_TRUE(app_->next_version()->version().IsEmpty());
+  EXPECT_TRUE(app_->language().IsEmpty());
+
+  EXPECT_TRUE(RegKey::HasKey(kGuid1ClientsKeyPathUser));
+  EXPECT_FALSE(RegKey::HasKey(kGuid1ClientStateKeyPathUser));
+}
+
+TEST_F(AppManagerUserTest,
+       ReadInstallerRegistrationValues_SucceedsWhenStateKeyAbsent) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
+                                    kRegValueProductVersion,
+                                    _T("0.9.68.4")));
+
+  __mutexBlock(app_->model()->lock()) {
+    EXPECT_SUCCEEDED(app_manager_->ReadInstallerRegistrationValues(app_));
+  }
+
+  EXPECT_STREQ(_T("0.9.68.4"), app_->next_version()->version());
+  EXPECT_TRUE(app_->language().IsEmpty());
+}
+
+TEST_F(AppManagerUserTest,
+       ReadInstallerRegistrationValues_SucceedsWithLanguage) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
+                                    kRegValueProductVersion,
+                                    _T("0.9.68.4")));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
+                                    kRegValueLanguage,
+                                    _T("zh-TW")));
+
+  __mutexBlock(app_->model()->lock()) {
+    EXPECT_SUCCEEDED(app_manager_->ReadInstallerRegistrationValues(app_));
+  }
+
+  EXPECT_STREQ(_T("0.9.68.4"), app_->next_version()->version());
+  EXPECT_STREQ(_T("zh-TW"), app_->language());
+}
+
+TEST_F(AppManagerMachineTest,
+       ReadInstallerRegistrationValues_SucceedsWithLanguage) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathMachine,
+                                    kRegValueProductVersion,
+                                    _T("0.9.68.4")));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathMachine,
+                                    kRegValueLanguage,
+                                    _T("zh-TW")));
+
+  __mutexBlock(app_->model()->lock()) {
+    EXPECT_SUCCEEDED(app_manager_->ReadInstallerRegistrationValues(app_));
+  }
+
+  EXPECT_STREQ(_T("0.9.68.4"), app_->next_version()->version());
+  EXPECT_STREQ(_T("zh-TW"), app_->language());
+}
+
+TEST_F(AppManagerUserTest, PersistSuccessfulInstall) {
+  PersistSuccessfulInstallTest();
+}
+
+TEST_F(AppManagerMachineTest, PersistSuccessfulInstall) {
+  PersistSuccessfulInstallTest();
+}
+
+TEST_F(AppManagerUserTest, PersistUpdateCheckSuccessfullySent_AllUpdated) {
+  PersistUpdateCheckSuccessfullySent_AllUpdated();
+}
+
+TEST_F(AppManagerMachineTest, PersistUpdateCheckSuccessfullySent_AllUpdated) {
+  PersistUpdateCheckSuccessfullySent_AllUpdated();
+}
+
+TEST_F(AppManagerUserTest, PersistUpdateCheckSuccessfullySent_NotRun) {
+  PersistUpdateCheckSuccessfullySent_NotRun();
+}
+
+TEST_F(AppManagerMachineTest, PersistUpdateCheckSuccessfullySent_NotRun) {
+  PersistUpdateCheckSuccessfullySent_NotRun();
+}
+
+TEST_F(AppManagerUserTest, PersistUpdateCheckSuccessfullySent_NoPreviousPing) {
+  PersistUpdateCheckSuccessfullySent_NoPreviousPing();
+}
+
+TEST_F(AppManagerMachineTest,
+       PersistUpdateCheckSuccessfullySent_NoPreviousPing) {
+  PersistUpdateCheckSuccessfullySent_NoPreviousPing();
+}
+
+TEST_F(AppManagerUserTest, SynchronizeClientStateTest) {
+  SynchronizeClientStateTest(kGuid2);  // App ID must be different than app_.
+
+  ValidateClientStateMedium(is_machine_, kGuid2);
+}
+
+TEST_F(AppManagerMachineTest, SynchronizeClientStateTest) {
+  SynchronizeClientStateTest(kGuid2);  // App ID must be different than app_.
+
+  ValidateClientStateMedium(is_machine_, kGuid2);
+}
+
+// Should not create ClientStateMedium key.
+TEST_F(AppManagerMachineTest, SynchronizeClientState_Omaha) {
+  SynchronizeClientStateTest(kGoogleUpdateAppId);
+
+  const CString client_state_medium_key_name = AppendRegKeyPath(
+    ConfigManager::Instance()->machine_registry_client_state_medium(),
+    kGoogleUpdateAppId);
+  EXPECT_FALSE(RegKey::HasKey(client_state_medium_key_name));
+}
+
+TEST_F(AppManagerUserTest, UpdateUpdateAvailableStats_NoExistingStats) {
+  const time64 before_time_in_100ns(GetCurrent100NSTime());
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  UpdateUpdateAvailableStats(guid1_, app_manager_);
+
+  const time64 after_time_in_100ns(GetCurrent100NSTime());
+
+  DWORD update_available_count(0);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableCount"),
+                                    &update_available_count));
+  EXPECT_EQ(1, update_available_count);
+
+  DWORD64 update_available_since_time(0);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableSince"),
+                                    &update_available_since_time));
+  EXPECT_LE(before_time_in_100ns, update_available_since_time);
+  EXPECT_GE(after_time_in_100ns, update_available_since_time);
+  const DWORD64 time_since_first_update_available =
+      after_time_in_100ns - update_available_since_time;
+  EXPECT_GT(10 * kSecsTo100ns, time_since_first_update_available);
+}
+
+TEST_F(AppManagerUserTest, UpdateUpdateAvailableStats_WithExistingStats) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableCount"),
+                                    static_cast<DWORD>(123456)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableSince"),
+                                    static_cast<DWORD64>(9876543210)));
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  UpdateUpdateAvailableStats(guid1_, app_manager_);
+
+  DWORD update_available_count(0);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableCount"),
+                                    &update_available_count));
+  EXPECT_EQ(123457, update_available_count);
+
+  DWORD64 update_available_since_time(0);
+  EXPECT_SUCCEEDED(RegKey::GetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableSince"),
+                                    &update_available_since_time));
+  EXPECT_EQ(9876543210, update_available_since_time);
+}
+
+// TODO(omaha3): Test PersistSuccessfulUpdateCheckResponse().
+// TODO(omaha): Move these tests to app_registry_utils_unittest.cc.
+#if 0
+TEST_F(AppManagerUserTest, ClearUpdateAvailableStats_KeyNotPresent) {
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  ClearUpdateAvailableStats(guid1_, app_manager_);
+}
+
+TEST_F(AppManagerUserTest, ClearUpdateAvailableStats_DataPresent) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableCount"),
+                                    static_cast<DWORD>(123456)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableSince"),
+                                    static_cast<DWORD64>(9876543210)));
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  ClearUpdateAvailableStats(guid1_, app_manager_);
+
+  EXPECT_TRUE(RegKey::HasKey(kGuid1ClientStateKeyPathUser));
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("UpdateAvailableCount")));
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("UpdateAvailableSince")));
+}
+#endif
+
+TEST_F(AppManagerUserTest, ReadUpdateAvailableStats_DataNotPresent) {
+  RegKey::CreateKey(kGuid1ClientStateKeyPathUser);
+
+  DWORD update_responses(1);
+  DWORD64 time_since_first_response_ms(1);
+  app_manager_->ReadUpdateAvailableStats(guid1_,
+                                         &update_responses,
+                                         &time_since_first_response_ms);
+
+  EXPECT_EQ(0, update_responses);
+  EXPECT_EQ(0, time_since_first_response_ms);
+}
+
+TEST_F(AppManagerUserTest, ReadUpdateAvailableStats_DataPresent) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableCount"),
+                                    static_cast<DWORD>(123456)));
+  const DWORD64 kUpdateAvailableSince =
+    GetCurrent100NSTime() - 2 * kMillisecsTo100ns;
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableSince"),
+                                    kUpdateAvailableSince));
+
+  DWORD update_responses(0);
+  DWORD64 time_since_first_response_ms(0);
+  app_manager_->ReadUpdateAvailableStats(guid1_,
+                                         &update_responses,
+                                         &time_since_first_response_ms);
+
+  EXPECT_EQ(123456, update_responses);
+  EXPECT_LE(2, time_since_first_response_ms);
+  EXPECT_GT(10 * kMsPerSec, time_since_first_response_ms);
+}
+
+// TODO(omaha): Move these tests to app_registry_utils_unittest.cc.
+#if 0
+TEST_F(AppManagerUserTest, PersistSuccessfulInstall_Install_Online) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableCount"),
+                                    static_cast<DWORD>(123456)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableSince"),
+                                    static_cast<DWORD64>(9876543210)));
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  app_manager_->PersistSuccessfulInstall(guid1_, false, false);
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  // Verify ClearUpdateAvailableStats() was called.
+  EXPECT_TRUE(RegKey::HasKey(kGuid1ClientStateKeyPathUser));
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("UpdateAvailableCount")));
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("UpdateAvailableSince")));
+
+  // Verify update check value is written but update value is not.
+  const uint32 last_check_sec = GetDwordValue(kGuid1ClientStateKeyPathUser,
+                                              kRegValueLastSuccessfulCheckSec);
+  EXPECT_GE(now, last_check_sec);
+  EXPECT_GE(static_cast<uint32>(200), now - last_check_sec);
+
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                kRegValueLastUpdateTimeSec));
+}
+
+TEST_F(AppManagerUserTest, PersistSuccessfulInstall_Install_Offline) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableCount"),
+                                    static_cast<DWORD>(123456)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableSince"),
+                                    static_cast<DWORD64>(9876543210)));
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  app_manager_->PersistSuccessfulInstall(guid1_, false, true);
+
+  // Verify ClearUpdateAvailableStats() was called.
+  EXPECT_TRUE(RegKey::HasKey(kGuid1ClientStateKeyPathUser));
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("UpdateAvailableCount")));
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("UpdateAvailableSince")));
+
+  // Verify update values are not written.
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                kRegValueLastSuccessfulCheckSec));
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                kRegValueLastUpdateTimeSec));
+}
+
+TEST_F(AppManagerUserTest, PersistSuccessfulInstall_Update_ExistingTimes) {
+  const DWORD kExistingUpdateValues = 0x70123456;
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableCount"),
+                                    static_cast<DWORD>(123456)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableSince"),
+                                    static_cast<DWORD64>(9876543210)));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    kRegValueLastSuccessfulCheckSec,
+                                    kExistingUpdateValues));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    kRegValueLastUpdateTimeSec,
+                                    kExistingUpdateValues));
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  app_manager_->PersistSuccessfulInstall(guid1_, true, false);
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  // Verify ClearUpdateAvailableStats() was called.
+  EXPECT_TRUE(RegKey::HasKey(kGuid1ClientStateKeyPathUser));
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("UpdateAvailableCount")));
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                _T("UpdateAvailableSince")));
+
+  // Verify update values updated.
+  const uint32 last_check_sec = GetDwordValue(kGuid1ClientStateKeyPathUser,
+                                              kRegValueLastSuccessfulCheckSec);
+  EXPECT_NE(kExistingUpdateValues, last_check_sec);
+  EXPECT_GE(now, last_check_sec);
+  EXPECT_GE(static_cast<uint32>(200), now - last_check_sec);
+
+  const uint32 last_update_sec =
+      GetDwordValue(kGuid1ClientStateKeyPathUser, kRegValueLastUpdateTimeSec);
+  EXPECT_NE(kExistingUpdateValues, last_update_sec);
+  EXPECT_GE(now, last_update_sec);
+  EXPECT_GE(static_cast<uint32>(200), now - last_update_sec);
+}
+
+TEST_F(AppManagerUserTest,
+       PersistSuccessfulInstall_Update_StateKeyDoesNotExist) {
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  app_manager_->PersistSuccessfulInstall(guid1_, true, false);
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  // Verify update values updated.
+  const uint32 last_check_sec = GetDwordValue(kGuid1ClientStateKeyPathUser,
+                                              kRegValueLastSuccessfulCheckSec);
+  EXPECT_GE(now, last_check_sec);
+  EXPECT_GE(static_cast<uint32>(200), now - last_check_sec);
+
+  const uint32 last_update_sec =
+      GetDwordValue(kGuid1ClientStateKeyPathUser, kRegValueLastUpdateTimeSec);
+  EXPECT_GE(now, last_update_sec);
+  EXPECT_GE(static_cast<uint32>(200), now - last_update_sec);
+}
+#endif
+
+// TODO(omaha): Move these tests to app_registry_utils_unittest.cc.
+#if 0
+TEST_F(AppManagerUserTest, PersistSuccessfulUpdateCheck_ExistingTime) {
+  const DWORD kExistingUpdateValue = 0x12345678;
+  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                    kRegValueLastSuccessfulCheckSec,
+                                    kExistingUpdateValue));
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  app_manager_->PersistSuccessfulUpdateCheck(guid1_);
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  const uint32 last_check_sec = GetDwordValue(kGuid1ClientStateKeyPathUser,
+                                              kRegValueLastSuccessfulCheckSec);
+  EXPECT_NE(kExistingUpdateValue, last_check_sec);
+  EXPECT_GE(now, last_check_sec);
+  EXPECT_GE(static_cast<uint32>(200), now - last_check_sec);
+
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                kRegValueLastUpdateTimeSec));
+}
+
+TEST_F(AppManagerUserTest, PersistSuccessfulUpdateCheck_StateKeyDoesNotExist) {
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  app_manager_->PersistSuccessfulUpdateCheck(guid1_);
+  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
+
+  const uint32 last_check_sec = GetDwordValue(kGuid1ClientStateKeyPathUser,
+                                              kRegValueLastSuccessfulCheckSec);
+  EXPECT_GE(now, last_check_sec);
+  EXPECT_GE(static_cast<uint32>(200), now - last_check_sec);
+
+  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
+                                kRegValueLastUpdateTimeSec));
+}
+#endif
+
+TEST_F(AppManagerMachineTest, RemoveClientState_Uninstalled) {
+  App* expected_app = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedUninstalledApp(_T("1.1.0.0"), expected_app);
+  CreateAppRegistryState(*expected_app, is_machine_, _T("1.1.0.0"), false);
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  EXPECT_SUCCEEDED(app_manager_->RemoveClientState(guid1_));
+  EXPECT_FALSE(IsClientStateKeyPresent(*expected_app));
+}
+
+// TODO(omaha): Move implementation up to class definition.
+// Creates 2 registered app and 1 unregistered app and populates the parameters.
+// Each is written to the registry.
+// Also creates partial Clients and ClientState keys and creates a registered
+// and an unregistered app in the opposite registry hive.
+void AppManagerWithBundleTest::
+    PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(
+    bool is_machine,
+    App* expected_app0,
+    App* expected_app1,
+    App* expected_app2,
+    App* opposite_hive_data1,
+    App* opposite_hive_data2) {
+
+  PopulateExpectedApp1(expected_app0);
+  CreateAppRegistryState(*expected_app0, is_machine, _T("1.0.0.0"), true);
+
+  PopulateExpectedApp2(expected_app1);
+  CreateAppRegistryState(*expected_app1, is_machine, _T("1.1.0.0"), true);
+
+  PopulateExpectedUninstalledApp(_T("2.3.0.0"), expected_app2);
+  CreateAppRegistryState(*expected_app2, is_machine, _T("2.3.0.0"), false);
+
+  //
+  // Add incomplete Clients and ClientState entries.
+  //
+
+  // No pv.
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(
+      AppendRegKeyPath(is_machine ? MACHINE_REG_CLIENTS : USER_REG_CLIENTS,
+                       kGuid4),
+      _T("name"),
+      _T("foo")));
+
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(
+      AppendRegKeyPath(is_machine ? MACHINE_REG_CLIENT_STATE :
+                                    USER_REG_CLIENT_STATE,
+                       kGuid5),
+      kRegValueDidRun,
+      _T("1")));
+
+  // Add registered and unregistered app to the opposite registry hive.
+  PopulateExpectedApp2(opposite_hive_data1);
+  CreateAppRegistryState(*opposite_hive_data1,
+                         !is_machine,
+                         _T("1.1.0.0"),
+                         true);
+
+  PopulateExpectedUninstalledApp(_T("1.1.0.0"), opposite_hive_data2);
+  CreateAppRegistryState(*opposite_hive_data2,
+                         !is_machine,
+                         _T("1.1.0.0"),
+                         false);
+}
+
+TEST_F(AppManagerWithBundleMachineTest, GetRegisteredApps) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(
+      true,
+      &expected_app0,
+      &expected_app1,
+      &expected_app2);
+
+  // An important part of this test is that GetRegisteredApps() continues and
+  // returns success even though there is a key for an app that is not properly
+  // registered (e.g. it does not have a pv value or has an invalid pv value).
+  // Since this is so important, explicitly check registry was set up correctly.
+  // Invalid pv value type is checked in a separate test since it causes an
+  // assert.
+  // Since GetRegisteredApps() does not rely on pv, the app is still returned
+  // in the vector.
+  // Note that kGuid4 is not upper-cased like the other GUIDs. This is because
+  // the other GUIDs are converted to a GUID and back to a string by
+  // PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests().
+  const CString incomplete_clients_key =
+      AppendRegKeyPath(MACHINE_REG_CLIENTS, kGuid4);
+  EXPECT_TRUE(RegKey::HasKey(incomplete_clients_key));
+  EXPECT_FALSE(RegKey::HasValue(incomplete_clients_key,
+                                kRegValueProductVersion));
+
+  AppIdVector registered_app_ids;
+  EXPECT_SUCCEEDED(app_manager_->GetRegisteredApps(&registered_app_ids));
+  EXPECT_EQ(3, registered_app_ids.size());
+
+  EXPECT_STREQ(CString(kGuid1).MakeUpper(), registered_app_ids[0]);
+  EXPECT_STREQ(CString(kGuid2).MakeUpper(), registered_app_ids[1]);
+  EXPECT_STREQ(kGuid4, registered_app_ids[2]);
+}
+
+TEST_F(AppManagerWithBundleUserTest, GetRegisteredApps) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(
+      false,
+      &expected_app0,
+      &expected_app1,
+      &expected_app2);
+
+  // See comment in machine GetRegisteredApps test.
+  const CString incomplete_clients_key =
+      AppendRegKeyPath(USER_REG_CLIENTS, kGuid4);
+  EXPECT_TRUE(RegKey::HasKey(incomplete_clients_key));
+  EXPECT_FALSE(RegKey::HasValue(incomplete_clients_key,
+                                kRegValueProductVersion));
+
+  AppIdVector registered_app_ids;
+  EXPECT_SUCCEEDED(app_manager_->GetRegisteredApps(&registered_app_ids));
+  EXPECT_EQ(3, registered_app_ids.size());
+
+  EXPECT_STREQ(CString(kGuid1).MakeUpper(), registered_app_ids[0]);
+  EXPECT_STREQ(CString(kGuid2).MakeUpper(), registered_app_ids[1]);
+  EXPECT_STREQ(kGuid4, registered_app_ids[2]);
+}
+
+TEST_F(AppManagerWithBundleUserTest, GetRegisteredApps_InvalidPvValueType) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(
+      false,
+      &expected_app0,
+      &expected_app1,
+      &expected_app2);
+
+  // It is important that this value is alphabetically before the other AppIds.
+  // This allows us to test that processing continues despite the bad pv.
+  const TCHAR* const kInvalidPvTypeAppId =
+      _T("{0150B619-867C-4985-B193-ED309A23EE36}");
+  const CString invalid_pv_type_clients_key =
+      AppendRegKeyPath(USER_REG_CLIENTS, kInvalidPvTypeAppId);
+
+  // Incorrect data type for pv. See comment in machine GetRegisteredApps test.
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(invalid_pv_type_clients_key,
+                                            kRegValueProductVersion,
+                                            static_cast<DWORD>(12345)));
+  EXPECT_TRUE(RegKey::HasValue(invalid_pv_type_clients_key,
+                               kRegValueProductVersion));
+  DWORD value_type = REG_SZ;
+  EXPECT_SUCCEEDED(RegKey::GetValueType(invalid_pv_type_clients_key,
+                                        kRegValueProductVersion,
+                                        &value_type));
+  EXPECT_NE(REG_SZ, value_type);
+
+  AppIdVector registered_app_ids;
+  EXPECT_SUCCEEDED(app_manager_->GetRegisteredApps(&registered_app_ids));
+
+  EXPECT_EQ(4, registered_app_ids.size());
+
+  EXPECT_STREQ(kInvalidPvTypeAppId, registered_app_ids[0]);
+  EXPECT_STREQ(CString(kGuid1).MakeUpper(), registered_app_ids[1]);
+  EXPECT_STREQ(CString(kGuid2).MakeUpper(), registered_app_ids[2]);
+  EXPECT_STREQ(kGuid4, registered_app_ids[3]);
+}
+
+TEST_F(AppManagerWithBundleMachineTest, GetUninstalledApps) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(
+      true,
+      &expected_app0,
+      &expected_app1,
+      &expected_app2);
+
+  AppIdVector registered_app_ids;
+  EXPECT_SUCCEEDED(app_manager_->GetUninstalledApps(&registered_app_ids));
+  EXPECT_EQ(1, registered_app_ids.size());
+
+  EXPECT_STREQ(CString(kGuid3).MakeUpper(), registered_app_ids[0]);
+}
+
+TEST_F(AppManagerWithBundleUserTest, GetUninstalledApps) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateDataAndRegistryForRegisteredAndUnInstalledAppsTests(
+      false,
+      &expected_app0,
+      &expected_app1,
+      &expected_app2);
+
+  AppIdVector registered_app_ids;
+  EXPECT_SUCCEEDED(app_manager_->GetUninstalledApps(&registered_app_ids));
+  EXPECT_EQ(1, registered_app_ids.size());
+
+  EXPECT_STREQ(CString(kGuid3).MakeUpper(), registered_app_ids[0]);
+}
+
+TEST_F(AppManagerWithBundleMachineTest, GetOemInstalledAndEulaAcceptedApps) {
+  // Create an OEM installed app.
+  App* expected_app1 = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedApp1(expected_app1);
+  CreateAppRegistryState(*expected_app1, is_machine_, _T("1.0.0.0"), true);
+
+  CString client_state_key_name = AppendRegKeyPath(
+      ConfigManager::Instance()->registry_client_state(is_machine_),
+      expected_app1->app_guid_string());
+  RegKey client_state_key;
+  ASSERT_SUCCEEDED(client_state_key.Create(client_state_key_name));
+  ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueOemInstall, _T("1")));
+
+  // Create a non-OEM installed app.
+  App* expected_app2 = CreateAppForRegistryPopulation(kGuid2);
+  PopulateExpectedApp2(expected_app2);
+  CreateAppRegistryState(*expected_app2, is_machine_, _T("2.0.0.0"), true);
+
+  AppIdVector oem_installed_app_ids;
+  EXPECT_SUCCEEDED(
+      app_manager_->GetOemInstalledAndEulaAcceptedApps(&oem_installed_app_ids));
+  EXPECT_EQ(1, oem_installed_app_ids.size());
+  EXPECT_STREQ(expected_app1->app_guid_string().MakeUpper(),
+               oem_installed_app_ids[0]);
+
+  app_manager_->ClearOemInstalled(oem_installed_app_ids);
+  oem_installed_app_ids.clear();
+  EXPECT_SUCCEEDED(
+      app_manager_->GetOemInstalledAndEulaAcceptedApps(&oem_installed_app_ids));
+  EXPECT_EQ(0, oem_installed_app_ids.size());
+}
+
+TEST_F(AppManagerWithBundleUserTest, GetOemInstalledAndEulaAcceptedApps) {
+  // Create an OEM installed app.
+  App* expected_app1 = CreateAppForRegistryPopulation(kGuid1);
+  PopulateExpectedApp1(expected_app1);
+  CreateAppRegistryState(*expected_app1, is_machine_, _T("1.0.0.0"), true);
+
+  CString client_state_key_name = AppendRegKeyPath(
+      ConfigManager::Instance()->registry_client_state(is_machine_),
+      expected_app1->app_guid_string());
+  RegKey client_state_key;
+  ASSERT_SUCCEEDED(client_state_key.Create(client_state_key_name));
+  ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueOemInstall, _T("1")));
+
+  // Create a non-OEM installed app.
+  App* expected_app2 = CreateAppForRegistryPopulation(kGuid2);
+  PopulateExpectedApp2(expected_app2);
+  CreateAppRegistryState(*expected_app2, is_machine_, _T("2.0.0.0"), true);
+
+  AppIdVector oem_installed_app_ids;
+  EXPECT_SUCCEEDED(
+      app_manager_->GetOemInstalledAndEulaAcceptedApps(&oem_installed_app_ids));
+  EXPECT_EQ(1, oem_installed_app_ids.size());
+  EXPECT_STREQ(expected_app1->app_guid_string().MakeUpper(),
+               oem_installed_app_ids[0]);
+
+  app_manager_->ClearOemInstalled(oem_installed_app_ids);
+  oem_installed_app_ids.clear();
+  EXPECT_SUCCEEDED(
+      app_manager_->GetOemInstalledAndEulaAcceptedApps(&oem_installed_app_ids));
+  EXPECT_EQ(0, oem_installed_app_ids.size());
+}
+
+// TODO(omaha): Perhaps CoCreate some real hooks and test further for the
+// *UpdateHook* tests.
+TEST_F(AppManagerWithBundleMachineTest, RunRegistrationUpdateHook) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateForRegistrationUpdateHookTests(
+      true,
+      &expected_app0,
+      &expected_app1,
+      &expected_app2,
+      kNonExistentClsid);
+
+  EXPECT_EQ(REGDB_E_CLASSNOTREG,
+            app_manager_->RunRegistrationUpdateHook(CString(kGuid1)));
+
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            app_manager_->RunRegistrationUpdateHook(CString(kGuid7)));
+}
+
+TEST_F(AppManagerWithBundleUserTest, RunRegistrationUpdateHook) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateForRegistrationUpdateHookTests(
+      false,
+      &expected_app0,
+      &expected_app1,
+      &expected_app2,
+      kNonExistentClsid);
+
+  EXPECT_EQ(REGDB_E_CLASSNOTREG,
+            app_manager_->RunRegistrationUpdateHook(CString(kGuid1)));
+
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            app_manager_->RunRegistrationUpdateHook(CString(kGuid7)));
+}
+
+// TODO(omaha): The RunAllRegistrationUpdateHooks tests do not actually
+// CoCreate any hook. Need to find a way to test this.
+TEST_F(AppManagerWithBundleMachineTest, RunAllRegistrationUpdateHooks) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateForRegistrationUpdateHookTests(
+      true,
+      &expected_app0,
+      &expected_app1,
+      &expected_app2,
+      GuidToString(GUID_NULL));
+
+  EXPECT_SUCCEEDED(app_manager_->RunAllRegistrationUpdateHooks());
+}
+
+TEST_F(AppManagerWithBundleUserTest, RunAllRegistrationUpdateHooks) {
+  App *expected_app0, *expected_app1, *expected_app2;
+  PopulateForRegistrationUpdateHookTests(
+      false,
+      &expected_app0,
+      &expected_app1,
+      &expected_app2,
+      GuidToString(GUID_NULL));
+
+  EXPECT_SUCCEEDED(app_manager_->RunAllRegistrationUpdateHooks());
+}
+
+TEST_F(AppManagerMachineTest, AppLockContention) {
+  const int kLockHeldTimeMs = 500;
+  HoldAppManagerLock hold_lock(is_machine_, kLockHeldTimeMs);
+
+  Thread thread;
+  thread.Start(&hold_lock);
+  hold_lock.WaitForLockToBeAcquired();
+
+  HighresTimer lock_metrics_timer;
+  __mutexScope(app_->model()->lock());
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            app_manager_->ReadAppPersistentData(app_));
+
+  // -20 because this sometimes failed with 48x vs. 500.
+  EXPECT_LE(kLockHeldTimeMs - 20, lock_metrics_timer.GetElapsedMs());
+
+  thread.WaitTillExit(1000);
+}
+
+TEST_F(AppManagerUserTest, AppLockContention) {
+  const int kLockHeldTimeMs = 500;
+  HoldAppManagerLock hold_lock(is_machine_, kLockHeldTimeMs);
+
+  Thread thread;
+  thread.Start(&hold_lock);
+  hold_lock.WaitForLockToBeAcquired();
+
+  HighresTimer lock_metrics_timer;
+  __mutexScope(app_->model()->lock());
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            app_manager_->ReadAppPersistentData(app_));
+
+  // -20 because this sometimes failed with 48x vs. 500.
+  EXPECT_LE(kLockHeldTimeMs - 20, lock_metrics_timer.GetElapsedMs());
+
+  thread.WaitTillExit(1000);
+}
+
+TEST_F(AppManagerMachineTest, AppLock_MachineAndUser) {
+  GLock app_manager_user_lock;
+  InitializeAppManagerRegistryLock(false, &app_manager_user_lock);
+
+  __mutexScope(app_manager_user_lock);
+
+  HighresTimer lock_metrics_timer;
+  __mutexScope(app_->model()->lock());
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            app_manager_->ReadAppPersistentData(app_));
+
+  EXPECT_GT(40, lock_metrics_timer.GetElapsedMs())
+      << _T("No delay is expected because there should not be a conflict.");
+}
+
+TEST_F(AppManagerUserTest, ReadAppVersionNoLock_NoApp) {
+  CString version;
+  EXPECT_FAILED(AppManager::ReadAppVersionNoLock(is_machine_,
+                                                 StringToGuid(kGuid1),
+                                                 &version));
+}
+
+TEST_F(AppManagerUserTest, ReadAppVersionNoLock_RegisteredApp) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
+                                    kRegValueProductVersion,
+                                    _T("0.9.75.4")));
+
+  CString version;
+  EXPECT_SUCCEEDED(AppManager::ReadAppVersionNoLock(is_machine_,
+                                                    StringToGuid(kGuid1),
+                                                    &version));
+  EXPECT_STREQ(_T("0.9.75.4"), version);
+  EXPECT_SUCCEEDED(RegKey::DeleteKey(kGuid1ClientsKeyPathUser));
+}
+
+TEST_F(AppManagerMachineTest, ReadAppVersionNoLock_NoApp) {
+  CString version;
+  EXPECT_FAILED(AppManager::ReadAppVersionNoLock(is_machine_,
+                                                 StringToGuid(kGuid1),
+                                                 &version));
+}
+
+TEST_F(AppManagerMachineTest, ReadAppVersionNoLock_RegisteredApp) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathMachine,
+                                    kRegValueProductVersion,
+                                    _T("0.9.80.4")));
+
+  CString version;
+  EXPECT_SUCCEEDED(AppManager::ReadAppVersionNoLock(is_machine_,
+                                                    StringToGuid(kGuid1),
+                                                    &version));
+  EXPECT_STREQ(_T("0.9.80.4"), version);
+  EXPECT_SUCCEEDED(RegKey::DeleteKey(kGuid1ClientsKeyPathMachine));
+}
+
+}  // namespace omaha
diff --git a/goopdate/app_state.cc b/goopdate/app_state.cc
new file mode 100644
index 0000000..bb75de0
--- /dev/null
+++ b/goopdate/app_state.cc
@@ -0,0 +1,167 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_state.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/goopdate/app_state_error.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/server_resource.h"
+#include "omaha/goopdate/string_formatter.h"
+
+namespace omaha {
+
+namespace fsm {
+
+const PingEvent* AppState::CreatePingEvent(App* app,
+                                           CurrentState previous_state) const {
+  UNREFERENCED_PARAMETER(app);
+  UNREFERENCED_PARAMETER(previous_state);
+  return NULL;
+}
+
+void AppState::QueueUpdateCheck(App* app) {
+  HandleInvalidStateTransition(app, _T(__FUNCTION__));
+}
+
+void AppState::PreUpdateCheck(App* app, xml::UpdateRequest* update_request) {
+  UNREFERENCED_PARAMETER(update_request);
+  HandleInvalidStateTransition(app, _T(__FUNCTION__));
+}
+
+void AppState::PostUpdateCheck(App* app,
+                               HRESULT result,
+                               xml::UpdateResponse* update_response) {
+  UNREFERENCED_PARAMETER(result);
+  UNREFERENCED_PARAMETER(update_response);
+  HandleInvalidStateTransition(app, _T(__FUNCTION__));
+}
+
+void AppState::QueueDownload(App* app) {
+  HandleInvalidStateTransition(app, _T(__FUNCTION__));
+}
+
+void AppState::QueueDownloadOrInstall(App* app) {
+  HandleInvalidStateTransition(app, _T(__FUNCTION__));
+}
+
+void AppState::Download(App* app, DownloadManagerInterface* download_manager) {
+  ASSERT1(download_manager);
+  UNREFERENCED_PARAMETER(download_manager);
+  // Must acquire the lock here because app does not acquire it before calling
+  // this method.
+  __mutexScope(app->model()->lock());
+  HandleInvalidStateTransition(app, _T(__FUNCTION__));
+}
+
+void AppState::Downloading(App* app) {
+  HandleInvalidStateTransition(app, _T(__FUNCTION__));
+}
+
+void AppState::DownloadComplete(App* app) {
+  HandleInvalidStateTransition(app, _T(__FUNCTION__));
+}
+
+void AppState::MarkReadyToInstall(App* app) {
+  HandleInvalidStateTransition(app, _T(__FUNCTION__));
+}
+
+void AppState::QueueInstall(App* app) {
+  HandleInvalidStateTransition(app, _T(__FUNCTION__));
+}
+
+void AppState::Install(App* app, InstallManagerInterface* install_manager) {
+  ASSERT1(install_manager);
+  UNREFERENCED_PARAMETER(install_manager);
+  // Must acquire the lock here because app does not acquire it before calling
+  // this method.
+  __mutexScope(app->model()->lock());
+  HandleInvalidStateTransition(app, _T(__FUNCTION__));
+}
+
+void AppState::Installing(App* app) {
+  HandleInvalidStateTransition(app, _T(__FUNCTION__));
+}
+
+void AppState::ReportInstallerComplete(App* app,
+                                       const InstallerResultInfo& result_info) {
+  UNREFERENCED_PARAMETER(result_info);
+  HandleInvalidStateTransition(app, _T(__FUNCTION__));
+}
+
+void AppState::Pause(App* app) {
+  HandleInvalidStateTransition(app, _T(__FUNCTION__));
+}
+
+// TODO(omaha3): If Cancel is not valid during certain states, override this in
+// those states and decide what should happen. Consider Worker::StopAsync().
+void AppState::Cancel(App* app) {
+  ASSERT1(app);
+  ASSERT1(app->model()->IsLockedByCaller());
+  CORE_LOG(L3, (_T("[AppState::Cancel][0x%p]"), app));
+
+  const HRESULT hr = GOOPDATE_E_CANCELLED;
+  CString message;
+
+  StringFormatter formatter(app->app_bundle()->display_language());
+  VERIFY1(SUCCEEDED(formatter.LoadString(IDS_CANCELED, &message)));
+  app->SetError(ErrorContext(hr), message);
+  ChangeState(app, new AppStateError);
+}
+
+void AppState::Error(App* app,
+                     const ErrorContext& error_context,
+                     const CString& message) {
+  ASSERT1(app);
+  ASSERT1(app->model()->IsLockedByCaller());
+  CORE_LOG(LE, (_T("[AppState::Error][0x%p][0x%08x][%s]"),
+      app, error_context.error_code, message));
+
+  app->SetError(error_context, message);
+  ChangeState(app, new AppStateError);
+}
+
+void AppState::ChangeState(App* app, AppState* app_state) {
+  ASSERT1(app);
+  ASSERT1(app_state);
+  ASSERT1(app->model()->IsLockedByCaller());
+  CORE_LOG(L3, (_T("[AppState::ChangeState][0x%p][%d]"),
+                app, app_state->state()));
+
+  app->ChangeState(app_state);
+}
+
+// Avoid infinite recursion by calling the base class's Error() method.
+void AppState::HandleInvalidStateTransition(App* app,
+                                            const TCHAR* function_name) {
+  UNREFERENCED_PARAMETER(function_name);
+  ASSERT(false, (_T("Invalid state transition: %s called while in state %u."),
+                 function_name, state_));
+  const HRESULT hr = GOOPDATE_E_INVALID_STATE_TRANSITION;
+  StringFormatter formatter(app->app_bundle()->display_language());
+  CString message;
+  VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)));
+  AppState::Error(app, ErrorContext(hr, state_), message);
+}
+
+PingEvent::Results AppState::GetCompletionResult(const App& app) {
+  ASSERT1(app.model()->IsLockedByCaller());
+  return app.completion_result_;
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_state.h b/goopdate/app_state.h
new file mode 100644
index 0000000..9882025
--- /dev/null
+++ b/goopdate/app_state.h
@@ -0,0 +1,121 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_STATE_H_
+#define OMAHA_GOOPDATE_APP_STATE_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/ping_event.h"
+#include "omaha/goopdate/installer_result_info.h"
+#include "goopdate/omaha3_idl.h"
+
+namespace omaha {
+
+class  App;
+struct ErrorContext;
+class  DownloadManagerInterface;
+class  InstallManagerInterface;
+
+namespace xml {
+
+class UpdateRequest;
+class UpdateResponse;
+
+}  // namespace xml
+
+namespace fsm {
+
+// Defines the interface for encapsulating behavior associated with a particular
+// state of the App object.
+// All state transition calls should go through App, meaning it is the only
+// class that should use this interface.
+class AppState {
+ public:
+  virtual ~AppState() {}
+
+  // Design note: avoid switch and conditional statements on CurrentState. They
+  // break polymorphism and they are a poor substitute for RTTI.
+  CurrentState state() const { return state_; }
+
+  // Creates a ping event for the current state. The caller owns the returned
+  // object.
+  virtual const PingEvent* CreatePingEvent(App* app,
+                                           CurrentState previous_state) const;
+
+  virtual void QueueUpdateCheck(App* app);
+
+  virtual void PreUpdateCheck(App* app, xml::UpdateRequest* update_request);
+
+  virtual void PostUpdateCheck(App* app,
+                               HRESULT result,
+                               xml::UpdateResponse* update_response);
+
+  virtual void QueueDownload(App* app);
+
+  // Queues the download for a download and install operation.
+  virtual void QueueDownloadOrInstall(App* app);
+
+  virtual void Download(App* app, DownloadManagerInterface* download_manager);
+
+  virtual void Downloading(App* app);
+
+  virtual void DownloadComplete(App* app);
+
+  virtual void MarkReadyToInstall(App* app);
+
+  virtual void QueueInstall(App* app);
+
+  virtual void Install(App* app, InstallManagerInterface* install_manager);
+
+  virtual void Installing(App* app);
+
+  virtual void ReportInstallerComplete(App* app,
+                                       const InstallerResultInfo& result_info);
+
+  virtual void Pause(App* app);
+
+  virtual void Cancel(App* app);
+
+  virtual void Error(App* app,
+                     const ErrorContext& error_context,
+                     const CString& message);
+
+ protected:
+  explicit AppState(CurrentState state) : state_(state) {}
+
+  // After calling this method, the old state (this object) is deleted and the
+  // code may not reference the members of the old state anymore.
+  void ChangeState(App* app, AppState* app_state);
+
+  void HandleInvalidStateTransition(App* app, const TCHAR* function_name);
+
+  static PingEvent::Results GetCompletionResult(const App& app);
+
+ private:
+  // TODO(omaha): rename to CurrentStateId or similar.
+  const CurrentState state_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppState);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_STATE_H_
diff --git a/goopdate/app_state_checking_for_update.cc b/goopdate/app_state_checking_for_update.cc
new file mode 100644
index 0000000..5c936b3
--- /dev/null
+++ b/goopdate/app_state_checking_for_update.cc
@@ -0,0 +1,247 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_state_checking_for_update.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/common/lang.h"
+#include "omaha/common/update_response.h"
+#include "omaha/goopdate/app_manager.h"
+#include "omaha/goopdate/app_state_no_update.h"
+#include "omaha/goopdate/app_state_update_available.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/server_resource.h"
+#include "omaha/goopdate/string_formatter.h"
+#include "omaha/goopdate/update_response_utils.h"
+#include "omaha/goopdate/worker_metrics.h"
+#include "omaha/goopdate/worker_utils.h"
+
+namespace omaha {
+
+namespace fsm {
+
+xml::UpdateResponseResult GetUpdateResponseResult(
+    const App* app,
+    const xml::UpdateResponse* update_response) {
+  ASSERT1(app);
+  ASSERT1(update_response);
+
+  const CString language = app->app_bundle()->display_language();
+
+  xml::UpdateResponseResult update_response_result =
+      update_response_utils::GetResult(update_response,
+                                       app->app_guid_string(),
+                                       language);
+
+  const bool is_omaha   = !!::IsEqualGUID(kGoopdateGuid, app->app_guid());
+  const bool has_update = update_response_result.first == S_OK &&
+                          app->is_update();
+
+  // Defer the update if the app is not Omaha, it has an update available, and
+  // an Omaha update is available at the same time.
+  if (!is_omaha  &&
+      has_update &&
+      update_response_utils::IsOmahaUpdateAvailable(update_response)) {
+    StringFormatter formatter(language);
+    CString text;
+    VERIFY1(SUCCEEDED(formatter.LoadString(IDS_NO_UPDATE_RESPONSE, &text)));
+    update_response_result = std::make_pair(GOOPDATE_E_UPDATE_DEFERRED, text);
+  }
+
+  return update_response_result;
+}
+
+AppStateCheckingForUpdate::AppStateCheckingForUpdate()
+    : AppState(STATE_CHECKING_FOR_UPDATE),
+      update_response_(NULL) {
+}
+
+// TODO(omaha3): Consider passing in an xml::response::App instead of a raw
+// xml::UpdateResponse to this method.
+void AppStateCheckingForUpdate::PostUpdateCheck(
+    App* app,
+    HRESULT update_check_result,
+    xml::UpdateResponse* update_response) {
+  CORE_LOG(L3, (_T("[AppStateCheckingForUpdate::PostUpdateCheck][0x%p]"), app));
+
+  ASSERT1(app);
+  ASSERT1(update_response);
+
+  ASSERT1(app->model()->IsLockedByCaller());
+
+  update_response_ = update_response;
+
+  const CString language = app->app_bundle()->display_language();
+
+  if (FAILED(update_check_result)) {
+    // TODO(omaha3): There is no guarantee that this is a actually network
+    // error. In Omaha 2, this was called much closer to the send. Making most
+    // errors, such as processing errors, app errors helps, but it could still
+    // be a parsing or other error.
+    CString error_message;
+    worker_utils::FormatMessageForNetworkError(update_check_result,
+                                               language,
+                                               &error_message);
+
+    Error(app, ErrorContext(update_check_result), error_message);
+    return;
+  }
+
+  PersistUpdateCheckSuccessfullySent(*app);
+
+  const xml::UpdateResponseResult update_response_result(
+      GetUpdateResponseResult(app, update_response));
+
+  const HRESULT& code    = update_response_result.first;
+  const CString& message = update_response_result.second;
+
+  if (SUCCEEDED(code)) {
+    HandleUpdateAvailable(app, code, message);
+  } else if (code == GOOPDATE_E_UPDATE_DEFERRED) {
+    HandleUpdateDeferred(app, code, message);
+  } else if (code == GOOPDATE_E_NO_UPDATE_RESPONSE) {
+    HandleNoUpdate(app, code, message);
+  } else {
+    HandleErrorResponse(app, code, message);
+  }
+}
+
+void AppStateCheckingForUpdate::HandleUpdateAvailable(App* app,
+                                                      HRESULT code,
+                                                      const CString& message) {
+  CORE_LOG(L3, (_T("[HandleUpdateAvailable][0x%p]"), app));
+
+  ASSERT1(app);
+  ASSERT1(SUCCEEDED(code));
+
+  UNREFERENCED_PARAMETER(code);
+  UNREFERENCED_PARAMETER(message);
+
+  app->set_has_update_available(true);
+
+  HRESULT hr = update_response_utils::BuildApp(update_response_, code, app);
+  if (FAILED(hr)) {
+    // Most of the errors that might actually be seen are likely to be due to
+    // response issues. Therefore, display a message about the server.
+    const CString language = app->app_bundle()->display_language();
+    StringFormatter formatter(language);
+    CString error_message;
+    VERIFY1(SUCCEEDED(formatter.LoadString(IDS_UNKNOWN_APPLICATION,
+                                           &error_message)));
+    Error(app, ErrorContext(hr), error_message);
+  }
+
+  const TCHAR* action = app->is_update() ? _T("update") : _T("install");
+  app->LogTextAppendFormat(_T("Status=%s"), action);
+
+  // Record the update available response regardless of how it is handled.
+  AppManager::Instance()->PersistSuccessfulUpdateCheckResponse(*app, true);
+
+  if (app->is_update()) {
+    if (::IsEqualGUID(kGoopdateGuid, app->app_guid())) {
+      ++metric_worker_self_updates_available;
+    } else {
+      ++metric_worker_app_updates_available;
+    }
+  }
+
+  ChangeState(app, new AppStateUpdateAvailable);
+}
+
+void AppStateCheckingForUpdate::HandleUpdateDeferred(App* app,
+                                                     HRESULT code,
+                                                     const CString& message) {
+  CORE_LOG(L3, (_T("[HandleUpdateDeferred][0x%p]"), app));
+
+  ASSERT1(app);
+  ASSERT1(code == GOOPDATE_E_UPDATE_DEFERRED);
+
+  ASSERT1(app->is_update());
+
+  app->SetNoUpdate(ErrorContext(code), message);
+  ChangeState(app, new AppStateNoUpdate);
+}
+
+void AppStateCheckingForUpdate::HandleNoUpdate(App* app,
+                                               HRESULT code,
+                                               const CString& message) {
+  CORE_LOG(L3, (_T("[HandleNoUpdate][0x%p]"), app));
+  ASSERT1(app);
+  ASSERT1(code == GOOPDATE_E_NO_UPDATE_RESPONSE);
+
+  app->LogTextAppendFormat(_T("Status=no-update"));
+
+  // For installs, no update is handled as an error.
+  if (!app->is_update()) {
+    Error(app, ErrorContext(code), message);
+    return;
+  }
+
+  VERIFY1(SUCCEEDED(update_response_utils::BuildApp(update_response_,
+                                                    code,
+                                                    app)));
+  AppManager::Instance()->PersistSuccessfulUpdateCheckResponse(*app, false);
+
+  app->SetNoUpdate(ErrorContext(S_OK), message);
+  ChangeState(app, new AppStateNoUpdate);
+}
+
+void AppStateCheckingForUpdate::HandleErrorResponse(App* app,
+                                                    HRESULT code,
+                                                    const CString& message) {
+  CORE_LOG(L3, (_T("[HandleErrorResponse][0x%p]"), app));
+
+  ASSERT1(app);
+  ASSERT1(FAILED(code));
+
+  CString log_status;
+  switch (code) {
+    case GOOPDATE_E_NO_SERVER_RESPONSE:
+      log_status = _T("no-response-received");
+      break;
+    case GOOPDATE_E_RESTRICTED_SERVER_RESPONSE:
+      log_status = _T("restricted");
+      break;
+    case GOOPDATE_E_UNKNOWN_APP_SERVER_RESPONSE:
+    case GOOPDATE_E_OS_NOT_SUPPORTED:
+    case GOOPDATE_E_INTERNAL_ERROR_SERVER_RESPONSE:
+    case GOOPDATE_E_SERVER_RESPONSE_NO_HASH:
+    case GOOPDATE_E_SERVER_RESPONSE_UNSUPPORTED_PROTOCOL:
+    case GOOPDATE_E_UNKNOWN_SERVER_RESPONSE:
+    default:
+      log_status = _T("error");
+      break;
+  }
+
+  app->LogTextAppendFormat(_T("Status=%s, Code=0x%08x"), log_status, code);
+
+  Error(app, ErrorContext(code), message);
+}
+
+void AppStateCheckingForUpdate::PersistUpdateCheckSuccessfullySent(
+    const App& app) {
+  AppManager& app_manager = *AppManager::Instance();
+  VERIFY1(SUCCEEDED(app_manager.PersistUpdateCheckSuccessfullySent(
+      app, update_response_->GetElapsedSecondsSinceDayStart())));
+
+  // Here we assume that some of the members in app object
+  // (days_since_last_active_ping_, days_since_last_roll_call_, iid_, did_run_)
+  // will not be used after the update check so there is no need to update them.
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_state_checking_for_update.h b/goopdate/app_state_checking_for_update.h
new file mode 100644
index 0000000..5563cd2
--- /dev/null
+++ b/goopdate/app_state_checking_for_update.h
@@ -0,0 +1,52 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_STATE_CHECKING_FOR_UPDATE_H_
+#define OMAHA_GOOPDATE_APP_STATE_CHECKING_FOR_UPDATE_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppStateCheckingForUpdate : public AppState {
+ public:
+  AppStateCheckingForUpdate();
+  virtual ~AppStateCheckingForUpdate() {}
+
+  virtual void PostUpdateCheck(App* app,
+                               HRESULT result,
+                               xml::UpdateResponse* update_response);
+
+ private:
+  void HandleUpdateAvailable(App* app, HRESULT code, const CString& message);
+  void HandleUpdateDeferred(App* app, HRESULT code, const CString& message);
+  void HandleNoUpdate(App* app, HRESULT code, const CString& message);
+  void HandleErrorResponse(App* app, HRESULT code, const CString& message);
+
+  void PersistUpdateCheckSuccessfullySent(const App& app);
+
+  xml::UpdateResponse* update_response_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppStateCheckingForUpdate);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_STATE_CHECKING_FOR_UPDATE_H_
diff --git a/goopdate/app_state_download_complete.cc b/goopdate/app_state_download_complete.cc
new file mode 100644
index 0000000..23b1286
--- /dev/null
+++ b/goopdate/app_state_download_complete.cc
@@ -0,0 +1,65 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_state_download_complete.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/goopdate/app_state_ready_to_install.h"
+#include "omaha/goopdate/download_complete_ping_event.h"
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+namespace fsm {
+
+AppStateDownloadComplete::AppStateDownloadComplete()
+    : AppState(STATE_DOWNLOAD_COMPLETE) {
+}
+
+const PingEvent* AppStateDownloadComplete::CreatePingEvent(
+    App* app,
+    CurrentState previous_state) const {
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(previous_state);
+
+  const PingEvent::Types event_type(app->is_update() ?
+      PingEvent::EVENT_UPDATE_DOWNLOAD_FINISH :
+      PingEvent::EVENT_INSTALL_DOWNLOAD_FINISH);
+
+  const HRESULT error_code = app->error_code();
+  ASSERT1(SUCCEEDED(error_code));
+
+  return new DownloadCompletePingEvent(event_type,
+                                       GetCompletionResult(*app),
+                                       error_code,
+                                       0,
+                                       app->GetDownloadTimeMs(),
+                                       app->num_bytes_downloaded(),
+                                       app->GetPackagesTotalSize());
+}
+
+// TODO(omaha3): When extraction and differential updates are supported, this
+// method will need to be copied/moved/changed to handle branches in the flow.
+void AppStateDownloadComplete::MarkReadyToInstall(App* app) {
+  CORE_LOG(L3,
+           (_T("[AppStateDownloadComplete::MarkReadyToInstall][0x%p]"), app));
+  ASSERT1(app);
+
+  ChangeState(app, new AppStateReadyToInstall);
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_state_download_complete.h b/goopdate/app_state_download_complete.h
new file mode 100644
index 0000000..9732522
--- /dev/null
+++ b/goopdate/app_state_download_complete.h
@@ -0,0 +1,45 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_STATE_DOWNLOAD_COMPLETE_H_
+#define OMAHA_GOOPDATE_APP_STATE_DOWNLOAD_COMPLETE_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppStateDownloadComplete : public AppState {
+ public:
+  AppStateDownloadComplete();
+  virtual ~AppStateDownloadComplete() {}
+
+  virtual const PingEvent* CreatePingEvent(App* app,
+                                           CurrentState previous_state) const;
+
+  virtual void MarkReadyToInstall(App* app);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppStateDownloadComplete);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_STATE_DOWNLOAD_COMPLETE_H_
+
diff --git a/goopdate/app_state_downloading.cc b/goopdate/app_state_downloading.cc
new file mode 100644
index 0000000..6dc4cc1
--- /dev/null
+++ b/goopdate/app_state_downloading.cc
@@ -0,0 +1,56 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_state_downloading.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/goopdate/app_state_download_complete.h"
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+namespace fsm {
+
+AppStateDownloading::AppStateDownloading()
+    : AppState(STATE_DOWNLOADING) {
+}
+
+const PingEvent* AppStateDownloading::CreatePingEvent(
+    App* app,
+    CurrentState previous_state) const {
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(previous_state);
+
+  const PingEvent::Types event_type(app->is_update() ?
+      PingEvent::EVENT_UPDATE_DOWNLOAD_START :
+      PingEvent::EVENT_INSTALL_DOWNLOAD_START);
+
+  const HRESULT error_code = app->error_code();
+  ASSERT1(SUCCEEDED(error_code));
+
+  return new PingEvent(event_type, GetCompletionResult(*app), error_code, 0);
+}
+
+void AppStateDownloading::DownloadComplete(App* app) {
+  CORE_LOG(L3, (_T("[AppStateDownloading::DownloadComplete][%p]"), app));
+  ASSERT1(app);
+
+  app->SetDownloadCompleteTime();
+  ChangeState(app, new AppStateDownloadComplete);
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_state_downloading.h b/goopdate/app_state_downloading.h
new file mode 100644
index 0000000..c310deb
--- /dev/null
+++ b/goopdate/app_state_downloading.h
@@ -0,0 +1,45 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_STATE_DOWNLOADING_H_
+#define OMAHA_GOOPDATE_APP_STATE_DOWNLOADING_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppStateDownloading : public AppState {
+ public:
+  AppStateDownloading();
+  virtual ~AppStateDownloading() {}
+
+  virtual const PingEvent* CreatePingEvent(App* app,
+                                           CurrentState previous_state) const;
+
+  virtual void DownloadComplete(App* app);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppStateDownloading);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_STATE_DOWNLOADING_H_
+
diff --git a/goopdate/app_state_error.cc b/goopdate/app_state_error.cc
new file mode 100644
index 0000000..6d17864
--- /dev/null
+++ b/goopdate/app_state_error.cc
@@ -0,0 +1,171 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_state_error.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+namespace fsm {
+
+AppStateError::AppStateError() : AppState(STATE_ERROR) {
+}
+
+const PingEvent* AppStateError::CreatePingEvent(
+    App* app,
+    CurrentState previous_state) const {
+  ASSERT1(app);
+
+  ASSERT1(FAILED(app->error_code()));
+
+  const PingEvent::Types event_type(app->is_update() ?
+      PingEvent::EVENT_UPDATE_COMPLETE :
+      PingEvent::EVENT_INSTALL_COMPLETE);
+
+  const PingEvent::Results result = GetCompletionResult(*app);
+
+  // Installer errors are reported in the ping in the case where the
+  // installer ran and failed. Otherwise, Omaha errors are reported.
+  //
+  // App extra codes are reported if set, otherwise the state of the state
+  // machine which caused the transition to the error state is encoded and
+  // reported.
+  const bool is_installer_error =
+      result == PingEvent::EVENT_RESULT_INSTALLER_ERROR_MSI    ||
+      result == PingEvent::EVENT_RESULT_INSTALLER_ERROR_OTHER  ||
+      result == PingEvent::EVENT_RESULT_INSTALLER_ERROR_SYSTEM;
+
+  HRESULT error_code = S_OK;
+  int extra_code1    = 0;
+
+  if (is_installer_error) {
+    error_code  = static_cast<HRESULT>(app->installer_result_code());
+    extra_code1 = app->installer_result_extra_code1();
+  } else {
+    const int app_extra_code1 = app->error_context().extra_code1;
+    error_code = app->error_code();
+    extra_code1 = app_extra_code1 ? app_extra_code1 :
+                          (PingEvent::kAppStateExtraCodeMask | previous_state);
+  }
+
+  // TODO(omaha): remove special case after the experiment is complete.
+  if (error_code == GOOPDATEDOWNLOAD_E_CACHING_FAILED ||
+      error_code == GOOPDATEINSTALL_E_INSTALLER_FAILED_START) {
+    extra_code1 = error_extra_code1();
+  }
+
+  // The error completion ping is sent whenever the application ended up in
+  // the error state:
+  // * in the install case always, since any install error is final.
+  // * in the update case only when an update has been available.
+  //
+  // In the update case, it is possible that the code errors out before it
+  // discovers that an update is available. Therefore, there is a window of
+  // uncertainty where the client did not get far enough to know if it was
+  // told by the server to update or not.
+  const bool can_ping = app->is_install() || app->has_update_available();
+  return can_ping ? new PingEvent(event_type, result, error_code, extra_code1) :
+                    NULL;
+}
+
+void AppStateError::DownloadComplete(App* app) {
+  CORE_LOG(L3, (_T("[AppStateError::DownloadComplete][0x%p]"), app));
+  UNREFERENCED_PARAMETER(app);
+}
+
+void AppStateError::MarkReadyToInstall(App* app) {
+  CORE_LOG(L3, (_T("[AppStateError::MarkReadyToInstall][0x%p]"), app));
+  UNREFERENCED_PARAMETER(app);
+}
+
+void AppStateError::PreUpdateCheck(App* app,
+                                   xml::UpdateRequest* update_request) {
+  CORE_LOG(L3, (_T("[AppStateError::PreUpdateCheck][%p]"), app));
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(app);
+  UNREFERENCED_PARAMETER(update_request);
+}
+
+void AppStateError::PostUpdateCheck(App* app,
+                               HRESULT result,
+                               xml::UpdateResponse* update_response) {
+  CORE_LOG(L3, (_T("[AppStateError::PostUpdateCheck][%p]"), app));
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(app);
+  UNREFERENCED_PARAMETER(result);
+  UNREFERENCED_PARAMETER(update_response);
+}
+
+void AppStateError::QueueDownload(App* app) {
+  CORE_LOG(L3, (_T("[AppStateError::QueueDownload][%p]"), app));
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(app);
+}
+
+void AppStateError::QueueDownloadOrInstall(App* app) {
+  CORE_LOG(L3, (_T("[AppStateError::QueueDownloadOrInstall][%p]"), app));
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(app);
+}
+
+void AppStateError::Download(
+    App* app,
+    DownloadManagerInterface* download_manager) {
+  CORE_LOG(L3, (_T("[AppStateError::Download][0x%p]"), app));
+  ASSERT1(app);
+  ASSERT1(download_manager);
+  UNREFERENCED_PARAMETER(app);
+  UNREFERENCED_PARAMETER(download_manager);
+}
+
+void AppStateError::QueueInstall(App* app) {
+  CORE_LOG(L3, (_T("[AppStateError::QueueInstall][%p]"), app));
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(app);
+}
+
+void AppStateError::Install(
+    App* app,
+    InstallManagerInterface* install_manager) {
+  CORE_LOG(L3, (_T("[AppStateError::Install][0x%p]"), app));
+  ASSERT1(app);
+  ASSERT1(install_manager);
+  UNREFERENCED_PARAMETER(app);
+  UNREFERENCED_PARAMETER(install_manager);
+}
+
+void AppStateError::Cancel(App* app) {
+  CORE_LOG(L3, (_T("[AppStateError::Cancel][0x%p]"), app));
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(app);
+}
+
+void AppStateError::Error(App* app,
+                          const ErrorContext& error_context,
+                          const CString& message) {
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(app);
+  UNREFERENCED_PARAMETER(error_context);
+  UNREFERENCED_PARAMETER(message);
+  CORE_LOG(L3, (_T("[app is already in the Error state]")
+      _T("[0x%p][app error=0x%x][this error=0x%x][%s]"),
+      app, app->error_code(), error_context.error_code, message));
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_state_error.h b/goopdate/app_state_error.h
new file mode 100644
index 0000000..f759f72
--- /dev/null
+++ b/goopdate/app_state_error.h
@@ -0,0 +1,79 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_STATE_ERROR_H_
+#define OMAHA_GOOPDATE_APP_STATE_ERROR_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+// The error state is idempotent. Further transitions from error state into
+// itself are allowed but have no effect. Therefore, the first error wins. One
+// scenario where this occurs is canceling the app. Canceling the app is not
+// blocking and it moves the app in the error state right away. The Error
+// method is called a second time, as the actual cancel code executes in the
+// thread pool.
+class AppStateError : public AppState {
+ public:
+  AppStateError();
+  virtual ~AppStateError() {}
+
+  virtual const PingEvent* CreatePingEvent(App* app,
+                                           CurrentState previous_state) const;
+
+  // These calls are legal in this state but do nothing. This can occur when
+  // this app has encountered an error but bundle is still being processed.
+  // For instance, when cancelling a bundle during a download, the applications
+  // transition right away in the error state. The cancel event is handled at
+  // some point in the future. Depending on a race condition, the downloads may
+  // have succeeded or failed due to the cancellation. The race condition is
+  // resolved when the transition call reaches this object and the call is
+  // ignored.
+  virtual void DownloadComplete(App* app);
+  virtual void MarkReadyToInstall(App* app);
+
+  // These calls are legal in this state but do nothing. This can occur when
+  // this app has encountered an error or has been canceled but bundle is still
+  // being processed.
+  virtual void PreUpdateCheck(App* app, xml::UpdateRequest* update_request);
+  virtual void PostUpdateCheck(App* app,
+                               HRESULT result,
+                               xml::UpdateResponse* update_response);
+  virtual void QueueDownload(App* app);
+  virtual void QueueDownloadOrInstall(App* app);
+  virtual void Download(App* app, DownloadManagerInterface* download_manager);
+  virtual void QueueInstall(App* app);
+  virtual void Install(App* app, InstallManagerInterface* install_manager);
+
+  // Canceling while in a terminal state has no effect.
+  virtual void Cancel(App* app);
+
+  virtual void Error(App* app,
+                     const ErrorContext& error_context,
+                     const CString& message);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppStateError);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_STATE_ERROR_H_
diff --git a/goopdate/app_state_init.cc b/goopdate/app_state_init.cc
new file mode 100644
index 0000000..b397a6b
--- /dev/null
+++ b/goopdate/app_state_init.cc
@@ -0,0 +1,41 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_state_init.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/goopdate/app_state_waiting_to_check_for_update.h"
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+namespace fsm {
+
+AppStateInit::AppStateInit() : AppState(STATE_INIT) {
+}
+
+void AppStateInit::QueueUpdateCheck(App* app) {
+  CORE_LOG(L3, (_T("[AppStateInit::QueueUpdateCheck][0x%p]"), app));
+  ASSERT1(app);
+
+  // Omaha should never be part of an app bundle in the install case. This is
+  // an important debug check to ensure that duplicate pings are not sent.
+  ASSERT1(!::IsEqualGUID(kGoopdateGuid, app->app_guid()) || app->is_update());
+  ChangeState(app, new AppStateWaitingToCheckForUpdate);
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_state_init.h b/goopdate/app_state_init.h
new file mode 100644
index 0000000..603abb3
--- /dev/null
+++ b/goopdate/app_state_init.h
@@ -0,0 +1,42 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_STATE_INIT_H_
+#define OMAHA_GOOPDATE_APP_STATE_INIT_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppStateInit : public AppState {
+ public:
+  AppStateInit();
+  virtual ~AppStateInit() {}
+
+  virtual void QueueUpdateCheck(App* app);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppStateInit);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_STATE_INIT_H_
+
diff --git a/goopdate/app_state_install_complete.cc b/goopdate/app_state_install_complete.cc
new file mode 100644
index 0000000..3a3d0ef
--- /dev/null
+++ b/goopdate/app_state_install_complete.cc
@@ -0,0 +1,93 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/common/config_manager.h"
+#include "omaha/goopdate/app_state_install_complete.h"
+#include "omaha/base/logging.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/worker.h"
+
+namespace omaha {
+
+namespace fsm {
+
+AppStateInstallComplete::AppStateInstallComplete(App* app)
+    : AppState(STATE_INSTALL_COMPLETE) {
+  ASSERT1(app);
+
+  // Start the crash handler if an installer has indicated that it requires OOP
+  // crash handling.
+  bool is_machine = app->app_bundle()->is_machine();
+  if (ConfigManager::Instance()->CanCollectStats(is_machine) &&
+      (!is_machine || user_info::IsRunningAsSystem())) {
+    VERIFY1(SUCCEEDED(goopdate_utils::StartCrashHandler(is_machine)));
+  }
+
+  VERIFY1(SUCCEEDED(app->model()->PurgeAppLowerVersions(
+      app->app_guid_string(), app->next_version()->version())));
+}
+
+// Omaha installs and updates are two-step processes. Omaha is handled as a
+// special case in both installs and updates.
+//
+// In the install case, Omaha itself is installed by the /install process, which
+// is responsible for pinging. Omaha is never part of the bundle in this case.
+//
+// In update case, the Omaha update is run as a /update process first.
+// The install manager does not wait for that process to complete and, if the
+// launch of it was successful, it transitions the Omaha app into the install
+// complete state. No ping should be sent in this case. Next, the update process
+// runs, finishes the update of Omaha, and then it sends the
+// EVENT_UPDATE_COMPLETE ping.
+const PingEvent* AppStateInstallComplete::CreatePingEvent(
+    App* app,
+    CurrentState previous_state) const {
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(previous_state);
+
+  const PingEvent::Types event_type(app->is_update() ?
+      PingEvent::EVENT_UPDATE_COMPLETE :
+      PingEvent::EVENT_INSTALL_COMPLETE);
+
+  const bool is_omaha = !!::IsEqualGUID(kGoopdateGuid, app->app_guid());
+
+  const bool can_ping = !is_omaha;
+
+  const HRESULT error_code(app->error_code());
+  ASSERT1(SUCCEEDED(error_code));
+
+  return can_ping ?
+      new PingEvent(event_type, GetCompletionResult(*app), error_code, 0) :
+      NULL;
+}
+
+// Canceling while in a terminal state has no effect.
+void AppStateInstallComplete::Cancel(App* app) {
+  CORE_LOG(L3, (_T("[AppStateInstallComplete::Cancel][0x%p]"), app));
+  UNREFERENCED_PARAMETER(app);
+}
+
+// Terminal states should not transition to error.
+void AppStateInstallComplete::Error(App* app,
+                                    const ErrorContext& error_context,
+                                    const CString& message) {
+  UNREFERENCED_PARAMETER(error_context);
+  UNREFERENCED_PARAMETER(message);
+  HandleInvalidStateTransition(app, _T(__FUNCTION__));
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_state_install_complete.h b/goopdate/app_state_install_complete.h
new file mode 100644
index 0000000..8aa16c4
--- /dev/null
+++ b/goopdate/app_state_install_complete.h
@@ -0,0 +1,46 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_STATE_INSTALL_COMPLETE_H_
+#define OMAHA_GOOPDATE_APP_STATE_INSTALL_COMPLETE_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppStateInstallComplete : public AppState {
+ public:
+  explicit AppStateInstallComplete(App* app);
+  virtual ~AppStateInstallComplete() {}
+
+  virtual const PingEvent* CreatePingEvent(App* app,
+                                           CurrentState previous_state) const;
+
+  virtual void Cancel(App* app);
+  virtual void Error(App* app,
+                     const ErrorContext& error_context,
+                     const CString& message);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppStateInstallComplete);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_STATE_INSTALL_COMPLETE_H_
diff --git a/goopdate/app_state_installing.cc b/goopdate/app_state_installing.cc
new file mode 100644
index 0000000..c9ac9f7
--- /dev/null
+++ b/goopdate/app_state_installing.cc
@@ -0,0 +1,83 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_state_installing.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/goopdate/app_state_error.h"
+#include "omaha/goopdate/app_state_install_complete.h"
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+namespace fsm {
+
+AppStateInstalling::AppStateInstalling()
+    : AppState(STATE_INSTALLING) {
+}
+
+const PingEvent* AppStateInstalling::CreatePingEvent(
+    App* app,
+    CurrentState previous_state) const {
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(previous_state);
+
+  const PingEvent::Types event_type(app->is_update() ?
+      PingEvent::EVENT_UPDATE_INSTALLER_START :
+      PingEvent::EVENT_INSTALL_INSTALLER_START);
+
+
+  const HRESULT error_code = app->error_code();
+  ASSERT1(SUCCEEDED(error_code));
+
+  return new PingEvent(event_type, GetCompletionResult(*app), error_code, 0);
+}
+
+// The state does not change if already in the Installing state.
+void AppStateInstalling::Installing(App* app) {
+  CORE_LOG(L3, (_T("[AppStateInstalling::Installing][0x%p]"), app));
+  ASSERT1(app);
+  ASSERT(false, (_T("This might be valid when we support install progress.")));
+
+  UNREFERENCED_PARAMETER(app);
+}
+
+void AppStateInstalling::ReportInstallerComplete(
+    App* app,
+    const InstallerResultInfo& result_info) {
+  CORE_LOG(L3, (_T("[AppStateInstalling::ReportInstallerComplete][%p]"), app));
+  ASSERT1(app);
+
+  app->SetInstallerResult(result_info);
+
+  ChangeState(app, result_info.type == INSTALLER_RESULT_SUCCESS ?
+      static_cast<AppState*>(new AppStateInstallComplete(app)) :
+      static_cast<AppState*>(new AppStateError));
+}
+
+// Cancel in installing state has no effect because currently we cannot cancel
+// installers.. Override the function to avoid moving app into error state.
+// The app will automatically enter the completion state when the installer
+// completes.
+void AppStateInstalling::Cancel(App* app) {
+  CORE_LOG(L3, (_T("[AppStateInstalling::Cancel][0x%p]"), app));
+  ASSERT1(app);
+
+  UNREFERENCED_PARAMETER(app);
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_state_installing.h b/goopdate/app_state_installing.h
new file mode 100644
index 0000000..8c33e0f
--- /dev/null
+++ b/goopdate/app_state_installing.h
@@ -0,0 +1,49 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_STATE_INSTALLING_H_
+#define OMAHA_GOOPDATE_APP_STATE_INSTALLING_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppStateInstalling : public AppState {
+ public:
+  AppStateInstalling();
+  virtual ~AppStateInstalling() {}
+
+  virtual const PingEvent* CreatePingEvent(App* app,
+                                           CurrentState previous_state) const;
+
+  virtual void Installing(App* app);
+
+  virtual void ReportInstallerComplete(App* app,
+                                       const InstallerResultInfo& result_info);
+
+  virtual void Cancel(App* app);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppStateInstalling);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_STATE_INSTALLING_H_
diff --git a/goopdate/app_state_no_update.cc b/goopdate/app_state_no_update.cc
new file mode 100644
index 0000000..2734b01
--- /dev/null
+++ b/goopdate/app_state_no_update.cc
@@ -0,0 +1,116 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_state_no_update.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+namespace fsm {
+
+AppStateNoUpdate::AppStateNoUpdate() : AppState(STATE_NO_UPDATE) {
+}
+
+const PingEvent* AppStateNoUpdate::CreatePingEvent(
+    App* app,
+    CurrentState previous_state) const {
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(previous_state);
+
+  // This state corresponds to the update case only. 'No updates' scenario in
+  // the installed case should be handled as errors by the state machine.
+  ASSERT1(app->is_update());
+
+  const PingEvent::Results completion_result = GetCompletionResult(*app);
+  ASSERT1(completion_result == PingEvent::EVENT_RESULT_SUCCESS ||
+          completion_result == PingEvent::EVENT_RESULT_UPDATE_DEFERRED);
+
+  // Creates a ping for deferred updates only.
+  if (completion_result == PingEvent::EVENT_RESULT_UPDATE_DEFERRED) {
+    // Omaha updates should never be deferred.
+    ASSERT1(!::IsEqualGUID(kGoopdateGuid, app->app_guid()));
+
+    const PingEvent::Types event_type(PingEvent::EVENT_UPDATE_COMPLETE);
+
+    const HRESULT error_code(app->error_code());
+    ASSERT1(error_code == GOOPDATE_E_UPDATE_DEFERRED);
+
+    return new PingEvent(event_type, completion_result, error_code, 0);
+  }
+
+  return NULL;
+}
+
+void AppStateNoUpdate::QueueDownload(App* app) {
+  CORE_LOG(L4, (_T("[AppStateNoUpdate::QueueDownload][%p]"), app));
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(app);
+}
+
+void AppStateNoUpdate::QueueDownloadOrInstall(App* app) {
+  CORE_LOG(L4, (_T("[AppStateNoUpdate::QueueDownloadOrInstall][%p]"), app));
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(app);
+}
+
+void AppStateNoUpdate::Download(
+    App* app,
+    DownloadManagerInterface* download_manager) {
+  CORE_LOG(L4, (_T("[AppStateNoUpdate::Download][0x%p]"), app));
+  ASSERT1(app);
+  ASSERT1(download_manager);
+  UNREFERENCED_PARAMETER(app);
+  UNREFERENCED_PARAMETER(download_manager);
+}
+
+void AppStateNoUpdate::QueueInstall(App* app) {
+  CORE_LOG(L4, (_T("[AppStateNoUpdate::QueueInstall][%p]"), app));
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(app);
+}
+
+void AppStateNoUpdate::Install(
+    App* app,
+    InstallManagerInterface* install_manager) {
+  CORE_LOG(L4, (_T("[AppStateNoUpdate::Install][0x%p]"), app));
+  ASSERT1(app);
+  ASSERT1(install_manager);
+  UNREFERENCED_PARAMETER(app);
+  UNREFERENCED_PARAMETER(install_manager);
+}
+
+// Canceling while in a terminal state has no effect.
+void AppStateNoUpdate::Cancel(App* app) {
+  CORE_LOG(L4, (_T("[AppStateNoUpdate::Cancel][0x%p]"), app));
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(app);
+}
+
+// Terminal states should not transition to error.
+void AppStateNoUpdate::Error(App* app,
+                             const ErrorContext& error_context,
+                             const CString& message) {
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(error_context);
+  UNREFERENCED_PARAMETER(message);
+  HandleInvalidStateTransition(app, _T(__FUNCTION__));
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_state_no_update.h b/goopdate/app_state_no_update.h
new file mode 100644
index 0000000..7a141ae
--- /dev/null
+++ b/goopdate/app_state_no_update.h
@@ -0,0 +1,55 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_STATE_NO_UPDATE_H_
+#define OMAHA_GOOPDATE_APP_STATE_NO_UPDATE_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppStateNoUpdate : public AppState {
+ public:
+  AppStateNoUpdate();
+  virtual ~AppStateNoUpdate() {}
+
+  virtual const PingEvent* CreatePingEvent(App* app,
+                                           CurrentState previous_state) const;
+
+  // These calls are legal in this state but do nothing. This can occur when
+  // no update is available for this app but updates are available for other
+  // apps in the bundle.
+  virtual void QueueDownload(App* app);
+  virtual void QueueDownloadOrInstall(App* app);
+  virtual void Download(App* app, DownloadManagerInterface* download_manager);
+  virtual void QueueInstall(App* app);
+  virtual void Install(App* app, InstallManagerInterface* install_manager);
+
+  virtual void Cancel(App* app);
+  virtual void Error(App* app,
+                     const ErrorContext& error_context,
+                     const CString& message);
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppStateNoUpdate);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_STATE_NO_UPDATE_H_
diff --git a/goopdate/app_state_ready_to_install.cc b/goopdate/app_state_ready_to_install.cc
new file mode 100644
index 0000000..f467176
--- /dev/null
+++ b/goopdate/app_state_ready_to_install.cc
@@ -0,0 +1,46 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_state_ready_to_install.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/goopdate/app_state_waiting_to_install.h"
+
+namespace omaha {
+
+namespace fsm {
+
+AppStateReadyToInstall::AppStateReadyToInstall()
+    : AppState(STATE_READY_TO_INSTALL) {
+}
+
+void AppStateReadyToInstall::QueueDownloadOrInstall(App* app) {
+  CORE_LOG(L3, (_T("[AppStateReadyToInstall::QueueDownloadOrInstall][0x%p]"),
+                app));
+  ASSERT1(app);
+
+  QueueInstall(app);
+}
+
+void AppStateReadyToInstall::QueueInstall(App* app) {
+  CORE_LOG(L3, (_T("[AppStateReadyToInstall::QueueInstall][%p]"), app));
+  ASSERT1(app);
+
+  ChangeState(app, new AppStateWaitingToInstall);
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_state_ready_to_install.h b/goopdate/app_state_ready_to_install.h
new file mode 100644
index 0000000..13337a3
--- /dev/null
+++ b/goopdate/app_state_ready_to_install.h
@@ -0,0 +1,44 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_STATE_READY_TO_INSTALL_H_
+#define OMAHA_GOOPDATE_APP_STATE_READY_TO_INSTALL_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppStateReadyToInstall : public AppState {
+ public:
+  AppStateReadyToInstall();
+  virtual ~AppStateReadyToInstall() {}
+
+  // Queues the install since the download is complete.
+  virtual void QueueDownloadOrInstall(App* app);
+
+  virtual void QueueInstall(App* app);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppStateReadyToInstall);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_STATE_READY_TO_INSTALL_H_
diff --git a/goopdate/app_state_update_available.cc b/goopdate/app_state_update_available.cc
new file mode 100644
index 0000000..cebdfba
--- /dev/null
+++ b/goopdate/app_state_update_available.cc
@@ -0,0 +1,73 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_state_update_available.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/goopdate/app_state_waiting_to_download.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/server_resource.h"
+#include "omaha/goopdate/string_formatter.h"
+
+namespace omaha {
+
+namespace fsm {
+
+AppStateUpdateAvailable::AppStateUpdateAvailable()
+    : AppState(STATE_UPDATE_AVAILABLE) {
+}
+
+void AppStateUpdateAvailable::QueueDownload(App* app) {
+  CORE_LOG(L3, (_T("[AppStateUpdateAvailable::QueueDownload][0x%p]"), app));
+  ASSERT1(app);
+
+  HRESULT policy_hr = app->CheckGroupPolicy();
+  if (FAILED(policy_hr)) {
+    HandleGroupPolicyError(app, policy_hr);
+    return;
+  }
+
+  ChangeState(app, new AppStateWaitingToDownload);
+}
+
+void AppStateUpdateAvailable::QueueDownloadOrInstall(App* app) {
+  CORE_LOG(L3, (_T("[AppStateUpdateAvailable::QueueDownloadOrInstall][0x%p]"),
+                app));
+  ASSERT1(app);
+
+  QueueDownload(app);
+}
+
+void AppStateUpdateAvailable::HandleGroupPolicyError(App* app, HRESULT code) {
+  ASSERT1(app);
+  ASSERT1(code == GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY ||
+          code == GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY);
+  CORE_LOG(L1, (_T("[Update available for disabled app][%s]"),
+                app->app_guid_string()));
+  app->LogTextAppendFormat(_T("Status=%s-disabled"),
+                           app->is_update() ? _T("update") : _T("install"));
+
+  StringFormatter formatter(app->app_bundle()->display_language());
+  CString error_message;
+  VERIFY1(SUCCEEDED(formatter.LoadString(
+                        IDS_APP_INSTALL_DISABLED_BY_GROUP_POLICY,
+                        &error_message)));
+  Error(app, ErrorContext(code), error_message);
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_state_update_available.h b/goopdate/app_state_update_available.h
new file mode 100644
index 0000000..547bb01
--- /dev/null
+++ b/goopdate/app_state_update_available.h
@@ -0,0 +1,48 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_STATE_UPDATE_AVAILABLE_H_
+#define OMAHA_GOOPDATE_APP_STATE_UPDATE_AVAILABLE_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppStateUpdateAvailable : public AppState {
+ public:
+  AppStateUpdateAvailable();
+  virtual ~AppStateUpdateAvailable() {}
+
+  virtual void QueueDownload(App* app);
+
+  // Queues the download. Install will need to be queued separately.
+  virtual void QueueDownloadOrInstall(App* app);
+
+ private:
+  // Moves the app to the Error state with an appropriate message.
+  void HandleGroupPolicyError(App* app, HRESULT code);
+
+  DISALLOW_COPY_AND_ASSIGN(AppStateUpdateAvailable);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_STATE_UPDATE_AVAILABLE_H_
+
diff --git a/goopdate/app_state_waiting_to_check_for_update.cc b/goopdate/app_state_waiting_to_check_for_update.cc
new file mode 100644
index 0000000..c9e18bd
--- /dev/null
+++ b/goopdate/app_state_waiting_to_check_for_update.cc
@@ -0,0 +1,91 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_state_waiting_to_check_for_update.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/highres_timer-win32.h"
+#include "omaha/base/logging.h"
+#include "omaha/common/update_request.h"
+#include "omaha/goopdate/app_manager.h"
+#include "omaha/goopdate/app_state_checking_for_update.h"
+#include "omaha/goopdate/update_request_utils.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/server_resource.h"
+#include "omaha/goopdate/string_formatter.h"
+#include "omaha/goopdate/worker.h"
+#include "omaha/goopdate/worker_metrics.h"
+
+namespace omaha {
+
+namespace fsm {
+
+AppStateWaitingToCheckForUpdate::AppStateWaitingToCheckForUpdate()
+    : AppState(STATE_WAITING_TO_CHECK_FOR_UPDATE) {
+}
+
+void AppStateWaitingToCheckForUpdate::PreUpdateCheck(
+    App* app,
+    xml::UpdateRequest* update_request) {
+  CORE_LOG(L3, (_T("[AppStateWaitingToCheckForUpdate::PreUpdateCheck]")));
+  ASSERT1(app);
+  ASSERT1(update_request);
+
+  ASSERT1(app->model()->IsLockedByCaller());
+
+  const CString& current_version(app->current_version()->version());
+  if (!current_version.IsEmpty()) {
+    app->model()->PurgeAppLowerVersions(app->app_guid_string(),
+                                        current_version);
+  }
+
+  AppManager* app_manager(AppManager::Instance());
+
+  VERIFY1(SUCCEEDED(app_manager->SynchronizeClientState(app->app_guid())));
+
+  // Handle the normal flow and return. Abnormal cases are below.
+  if (app->is_eula_accepted()) {
+    update_request_utils::BuildRequest(app, true, update_request);
+    ChangeState(app, new AppStateCheckingForUpdate);
+    return;
+  }
+
+  // The app's EULA has not been accepted, so do not add this app to the update
+  // check. This means bundle size does not always match the request size.
+
+  ASSERT1(app->app_guid() != kGoopdateGuid);
+
+  // TODO(omaha3): Is there a better way to do this such that we don't need to
+  // know about offline installs here?
+  if (app->app_bundle()->is_offline_install()) {
+    // Offline installs do not need requests, so skip building the request.
+    ChangeState(app, new AppStateCheckingForUpdate);
+    return;
+  }
+
+  ASSERT1(app->is_update());
+  metric_worker_apps_not_updated_eula++;
+
+  StringFormatter formatter(app->app_bundle()->display_language());
+  CString message;
+  VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)));
+  Error(app,
+        ErrorContext(GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED),
+        message);
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_state_waiting_to_check_for_update.h b/goopdate/app_state_waiting_to_check_for_update.h
new file mode 100644
index 0000000..9276363
--- /dev/null
+++ b/goopdate/app_state_waiting_to_check_for_update.h
@@ -0,0 +1,42 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_STATE_WAITING_TO_CHECK_FOR_UPDATE_H_
+#define OMAHA_GOOPDATE_APP_STATE_WAITING_TO_CHECK_FOR_UPDATE_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppStateWaitingToCheckForUpdate : public AppState {
+ public:
+  AppStateWaitingToCheckForUpdate();
+  virtual ~AppStateWaitingToCheckForUpdate() {}
+
+  virtual void PreUpdateCheck(App* app, xml::UpdateRequest* update_request);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppStateWaitingToCheckForUpdate);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_STATE_WAITING_TO_CHECK_FOR_UPDATE_H_
+
diff --git a/goopdate/app_state_waiting_to_download.cc b/goopdate/app_state_waiting_to_download.cc
new file mode 100644
index 0000000..284d207
--- /dev/null
+++ b/goopdate/app_state_waiting_to_download.cc
@@ -0,0 +1,84 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_state_waiting_to_download.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/goopdate/app_state_download_complete.h"
+#include "omaha/goopdate/app_state_downloading.h"
+#include "omaha/goopdate/download_manager.h"
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+namespace fsm {
+
+AppStateWaitingToDownload::AppStateWaitingToDownload()
+    : AppState(STATE_WAITING_TO_DOWNLOAD) {
+}
+
+const PingEvent* AppStateWaitingToDownload::CreatePingEvent(
+    App* app,
+    CurrentState previous_state) const {
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(previous_state);
+
+  // Omaha 3 reports this ping later than Omaha 2 because the COM server does
+  // not know the client's intent when doing the update check.
+  const PingEvent::Types event_type(app->is_update() ?
+      PingEvent::EVENT_UPDATE_APPLICATION_BEGIN :
+      PingEvent::EVENT_INSTALL_APPLICATION_BEGIN);
+
+  const HRESULT error_code = app->error_code();
+  ASSERT1(SUCCEEDED(error_code));
+
+  return new PingEvent(event_type, GetCompletionResult(*app), error_code, 0);
+}
+
+void AppStateWaitingToDownload::Download(
+    App* app,
+    DownloadManagerInterface* download_manager) {
+  CORE_LOG(L3, (_T("[AppStateWaitingToDownload::Download][0x%p]"), app));
+  ASSERT1(app);
+  ASSERT1(download_manager);
+
+  app->SetDownloadStartTime();
+
+  // This is a blocking call on the network.
+  HRESULT hr = download_manager->DownloadApp(app);
+
+  app->LogTextAppendFormat(_T("Download result=0x%08x"), hr);
+}
+
+void AppStateWaitingToDownload::Downloading(App* app) {
+  CORE_LOG(L3, (_T("[AppStateWaitingToDownload::Downloading][%p]"), app));
+  ASSERT1(app);
+
+  ChangeState(app, new AppStateDownloading);
+}
+
+void AppStateWaitingToDownload::DownloadComplete(App* app) {
+  CORE_LOG(L3, (_T("[AppStateWaitingToDownload::DownloadComplete][%p]"), app));
+  CORE_LOG(L3, (_T("[Did not download anything - likely because all packages ")
+                _T("were cached - or OnProgress callback was never called.]")));
+  ASSERT1(app);
+
+  ChangeState(app, new AppStateDownloadComplete);
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
+
diff --git a/goopdate/app_state_waiting_to_download.h b/goopdate/app_state_waiting_to_download.h
new file mode 100644
index 0000000..ccdc359
--- /dev/null
+++ b/goopdate/app_state_waiting_to_download.h
@@ -0,0 +1,47 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_STATE_WAITING_TO_DOWNLOAD_H_
+#define OMAHA_GOOPDATE_APP_STATE_WAITING_TO_DOWNLOAD_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppStateWaitingToDownload : public AppState {
+ public:
+  AppStateWaitingToDownload();
+  virtual ~AppStateWaitingToDownload() {}
+
+  virtual const PingEvent* CreatePingEvent(App* app,
+                                           CurrentState previous_state) const;
+
+  virtual void Download(App* app, DownloadManagerInterface* download_manager);
+  virtual void Downloading(App* app);
+  virtual void DownloadComplete(App* app);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppStateWaitingToDownload);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_STATE_WAITING_TO_DOWNLOAD_H_
+
diff --git a/goopdate/app_state_waiting_to_install.cc b/goopdate/app_state_waiting_to_install.cc
new file mode 100644
index 0000000..ddd4ace
--- /dev/null
+++ b/goopdate/app_state_waiting_to_install.cc
@@ -0,0 +1,90 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_state_waiting_to_install.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/utils.h"
+#include "omaha/goopdate/app_state_installing.h"
+#include "omaha/goopdate/install_manager.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/server_resource.h"
+#include "omaha/goopdate/string_formatter.h"
+
+namespace omaha {
+
+namespace fsm {
+
+AppStateWaitingToInstall::AppStateWaitingToInstall()
+    : AppState(STATE_WAITING_TO_INSTALL) {
+}
+
+void AppStateWaitingToInstall::Download(
+    App* app,
+    DownloadManagerInterface* download_manager) {
+  CORE_LOG(L3, (_T("[AppStateWaitingToInstall::Download][0x%p]"), app));
+  ASSERT1(app);
+  ASSERT1(download_manager);
+  UNREFERENCED_PARAMETER(app);
+  UNREFERENCED_PARAMETER(download_manager);
+}
+
+void AppStateWaitingToInstall::QueueInstall(App* app) {
+  CORE_LOG(L3, (_T("[AppStateWaitingToInstall::QueueInstall][%p]"), app));
+  ASSERT1(app);
+  UNREFERENCED_PARAMETER(app);
+}
+
+// Copies app packages and runs the installer. The packages are cleaned up
+// by the InstallManager constructor, when its instance will be created by
+// the next COM server that starts up.
+void AppStateWaitingToInstall::Install(
+    App* app,
+    InstallManagerInterface* install_manager) {
+  CORE_LOG(L3, (_T("[AppStateWaitingToInstall::Install][0x%p]"), app));
+  ASSERT1(app);
+  ASSERT1(install_manager);
+
+  CString guid;
+  HRESULT hr(GetGuid(&guid));
+  if (SUCCEEDED(hr)) {
+    const CString installer_dir(
+        ConcatenatePath(install_manager->install_working_dir(), guid));
+    hr = CopyAppVersionPackages(app->next_version(), installer_dir);
+    if (SUCCEEDED(hr)) {
+      install_manager->InstallApp(app, installer_dir);
+    }
+  }
+
+  if (FAILED(hr)) {
+    StringFormatter formatter(app->app_bundle()->display_language());
+    CString message;
+    VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)));
+    Error(app, ErrorContext(hr), message);
+  }
+}
+
+void AppStateWaitingToInstall::Installing(App* app) {
+  CORE_LOG(L3, (_T("[AppStateWaitingToInstall::Installing][%p]"), app));
+  ASSERT1(app);
+
+  ChangeState(app, new AppStateInstalling);
+}
+
+}  // namespace fsm
+
+}  // namespace omaha
diff --git a/goopdate/app_state_waiting_to_install.h b/goopdate/app_state_waiting_to_install.h
new file mode 100644
index 0000000..8864c9f
--- /dev/null
+++ b/goopdate/app_state_waiting_to_install.h
@@ -0,0 +1,47 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_APP_STATE_WAITING_TO_INSTALL_H_
+#define OMAHA_GOOPDATE_APP_STATE_WAITING_TO_INSTALL_H_
+
+#include "base/basictypes.h"
+#include "omaha/goopdate/app_state.h"
+
+namespace omaha {
+
+namespace fsm {
+
+class AppStateWaitingToInstall : public AppState {
+ public:
+  AppStateWaitingToInstall();
+  virtual ~AppStateWaitingToInstall() {}
+
+  // These calls are legal in this state but do nothing. This can occur when the
+  // app has already been downloaded and the client calls AppBundle::install().
+  virtual void Download(App* app, DownloadManagerInterface* download_manager);
+  virtual void QueueInstall(App* app);
+
+  virtual void Install(App* app, InstallManagerInterface* install_manager);
+  virtual void Installing(App* app);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppStateWaitingToInstall);
+};
+
+}  // namespace fsm
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_STATE_WAITING_TO_INSTALL_H_
diff --git a/goopdate/app_unittest.cc b/goopdate/app_unittest.cc
new file mode 100644
index 0000000..f239890
--- /dev/null
+++ b/goopdate/app_unittest.cc
@@ -0,0 +1,527 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include "omaha/base/error.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/const_group_policy.h"
+#include "omaha/common/update_request.h"
+#include "omaha/common/update_response.h"
+#include "omaha/goopdate/app_state_checking_for_update.h"
+#include "omaha/goopdate/app_state_update_available.h"
+#include "omaha/goopdate/app_state_waiting_to_check_for_update.h"
+#include "omaha/goopdate/app_unittest_base.h"
+#include "omaha/testing/unit_test.h"
+
+using ::testing::_;
+
+namespace omaha {
+
+namespace {
+
+#define APP_ID1 _T("{D9F05AEA-BEDA-4f91-B216-BE45DAE330CB}");
+const TCHAR* const kAppId1 = APP_ID1
+const TCHAR* const kInstallPolicyApp1 = _T("Install") APP_ID1;
+const TCHAR* const kUpdatePolicyApp1 = _T("Update") APP_ID1;
+const TCHAR* const kAppId1ClientsKeyPathUser =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\")
+                           PRODUCT_NAME _T("\\Clients\\") APP_ID1;
+const TCHAR* const kGuid1ClientStateKeyPathUser =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\")
+                           PRODUCT_NAME _T("\\ClientState\\") APP_ID1;
+
+#define APP_ID2 _T("{EF3CACD4-89EB-46b7-B9BF-B16B15F08584}");
+const TCHAR* const kInstallPolicyApp2 = _T("Install") APP_ID2;
+const TCHAR* const kUpdatePolicyApp2 = _T("Update") APP_ID2;
+
+void SetPolicy(const CString& policy, DWORD value) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
+                                    policy,
+                                    value));
+}
+
+}  // namespace
+
+class AppTest : public AppTestBaseWithRegistryOverride {
+ protected:
+  explicit AppTest(bool use_strict_mock)
+      : AppTestBaseWithRegistryOverride(false,  // Always as user for now.
+                                        use_strict_mock),
+        app_(NULL) {}
+
+  virtual void SetUp() {
+    AppTestBaseWithRegistryOverride::SetUp();
+
+    update_response_.reset(xml::UpdateResponse::Create());
+  }
+
+  void AddAppResponse(const CString& status) {
+    xml::response::App app;
+    app.status = kResponseStatusOkValue;
+    app.appid = kAppId1;
+    app.update_check.status = status;
+
+    xml::response::Response response;
+    response.apps.push_back(app);
+
+    SetResponseForUnitTest(update_response_.get(), response);
+  }
+
+  App* app_;
+  scoped_ptr<xml::UpdateResponse> update_response_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppTest);
+};
+
+class AppInstallTest : public AppTest {
+ protected:
+  AppInstallTest() : AppTest(true) {}
+
+  virtual void SetUp() {
+    AppTest::SetUp();
+    EXPECT_SUCCEEDED(
+        app_bundle_->createApp(CComBSTR(kAppId1), &app_));
+    ASSERT_TRUE(app_);
+  }
+};
+
+class AppManualUpdateTest : public AppTest {
+ protected:
+  AppManualUpdateTest() : AppTest(true) {}
+  explicit AppManualUpdateTest(bool use_strict_mock)
+      : AppTest(use_strict_mock) {}
+
+  // Calling checkForUpdate() should leave AppBundle::is_auto_update as false.
+  virtual void SetUp() {
+    AppTest::SetUp();
+    EXPECT_SUCCEEDED(RegKey::SetValue(kAppId1ClientsKeyPathUser,
+                                      kRegValueProductVersion,
+                                      _T("1.2.3.4")));
+    EXPECT_SUCCEEDED(RegKey::SetValue(kAppId1ClientsKeyPathUser,
+                                      kRegValueAppName,
+                                      _T("Unit Test App")));
+    EXPECT_SUCCEEDED(app_bundle_->createInstalledApp(
+                         CComBSTR(kAppId1), &app_));
+    ASSERT_TRUE(app_);
+
+    EXPECT_CALL(*mock_worker_, CheckForUpdateAsync(_)).Times(1);
+    EXPECT_SUCCEEDED(app_bundle_->checkForUpdate());
+    EXPECT_FALSE(app_bundle_->is_auto_update());
+  }
+};
+
+class AppAutoUpdateTest : public AppManualUpdateTest  {
+ protected:
+  AppAutoUpdateTest() : AppManualUpdateTest(false) {}
+
+  // Calling UpdateAllAppsAsync() sets AppBundle::is_auto_update.
+  virtual void SetUp() {
+    AppTest::SetUp();
+    EXPECT_SUCCEEDED(RegKey::SetValue(kAppId1ClientsKeyPathUser,
+                                      kRegValueProductVersion,
+                                      _T("1.2.3.4")));
+    EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
+                                      kRegValueProductVersion,
+                                      _T("1.2.3.4")));
+    EXPECT_SUCCEEDED(RegKey::SetValue(kAppId1ClientsKeyPathUser,
+                                      kRegValueAppName,
+                                      _T("Unit Test App")));
+
+    EXPECT_CALL(*mock_worker_, UpdateAllAppsAsync(_)).Times(1);
+    EXPECT_SUCCEEDED(app_bundle_->updateAllApps());
+    EXPECT_TRUE(app_bundle_->is_auto_update());
+
+    app_= app_bundle_->GetApp(0);
+    ASSERT_TRUE(app_);
+  }
+};
+
+//
+// CheckGroupPolicy Tests.
+//
+
+TEST_F(AppInstallTest, CheckGroupPolicy_NoPolicy) {
+  EXPECT_SUCCEEDED(app_->CheckGroupPolicy());
+}
+
+TEST_F(AppManualUpdateTest, CheckGroupPolicy_NoPolicy) {
+  EXPECT_SUCCEEDED(app_->CheckGroupPolicy());
+}
+
+TEST_F(AppAutoUpdateTest, CheckGroupPolicy_NoPolicy) {
+  EXPECT_SUCCEEDED(app_->CheckGroupPolicy());
+}
+
+TEST_F(AppInstallTest, CheckGroupPolicy_InstallDisabled) {
+  SetPolicy(kInstallPolicyApp1, kPolicyDisabled);
+  EXPECT_EQ(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY,
+            app_->CheckGroupPolicy());
+}
+
+TEST_F(AppManualUpdateTest, CheckGroupPolicy_InstallDisabled) {
+  SetPolicy(kInstallPolicyApp1, kPolicyDisabled);
+  EXPECT_SUCCEEDED(app_->CheckGroupPolicy());
+}
+
+TEST_F(AppAutoUpdateTest, CheckGroupPolicy_InstallDisabled) {
+  SetPolicy(kInstallPolicyApp1, kPolicyDisabled);
+  EXPECT_SUCCEEDED(app_->CheckGroupPolicy());
+}
+
+TEST_F(AppInstallTest, CheckGroupPolicy_AllUpdatesDisabled) {
+  SetPolicy(kUpdatePolicyApp1, kPolicyDisabled);
+  EXPECT_SUCCEEDED(app_->CheckGroupPolicy());
+}
+
+TEST_F(AppManualUpdateTest, CheckGroupPolicy_AllUpdatesDisabled) {
+  SetPolicy(kUpdatePolicyApp1, kPolicyDisabled);
+  EXPECT_EQ(GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY, app_->CheckGroupPolicy());
+}
+
+TEST_F(AppAutoUpdateTest, CheckGroupPolicy_AllUpdatesDisabled) {
+  SetPolicy(kUpdatePolicyApp1, kPolicyDisabled);
+  EXPECT_EQ(GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY, app_->CheckGroupPolicy());
+}
+
+TEST_F(AppInstallTest, CheckGroupPolicy_AutoUpdatesDisabled) {
+  SetPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly);
+  EXPECT_SUCCEEDED(app_->CheckGroupPolicy());
+}
+
+TEST_F(AppManualUpdateTest, CheckGroupPolicy_AutoUpdatesDisabled) {
+  SetPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly);
+  EXPECT_SUCCEEDED(app_->CheckGroupPolicy());
+}
+
+TEST_F(AppAutoUpdateTest, CheckGroupPolicy_AutoUpdatesDisabled) {
+  SetPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly);
+  EXPECT_EQ(GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY, app_->CheckGroupPolicy());
+}
+
+//
+// PostUpdateCheck Tests.
+//
+
+TEST_F(AppInstallTest, PostUpdateCheck_NoUpdate) {
+  SetAppStateForUnitTest(app_, new fsm::AppStateCheckingForUpdate);
+  AddAppResponse(kResponseStatusNoUpdate);
+
+  app_->PostUpdateCheck(S_OK, update_response_.get());
+  EXPECT_EQ(STATE_ERROR, app_->state());
+  EXPECT_EQ(GOOPDATE_E_NO_UPDATE_RESPONSE, app_->error_code());
+}
+
+TEST_F(AppInstallTest, PostUpdateCheck_UpdateAvailable) {
+  SetAppStateForUnitTest(app_, new fsm::AppStateCheckingForUpdate);
+  AddAppResponse(kResponseStatusOkValue);
+
+  app_->PostUpdateCheck(S_OK, update_response_.get());
+  EXPECT_EQ(STATE_UPDATE_AVAILABLE, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+}
+
+// Policy is not checked by this function.
+TEST_F(AppInstallTest, PostUpdateCheck_UpdateAvailable_InstallDisabled) {
+  SetPolicy(kInstallPolicyApp1, kPolicyDisabled);
+  SetAppStateForUnitTest(app_, new fsm::AppStateCheckingForUpdate);
+  AddAppResponse(kResponseStatusOkValue);
+
+  app_->PostUpdateCheck(S_OK, update_response_.get());
+  EXPECT_EQ(STATE_UPDATE_AVAILABLE, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+}
+
+TEST_F(AppManualUpdateTest, PostUpdateCheck_NoUpdate) {
+  SetAppStateForUnitTest(app_, new fsm::AppStateCheckingForUpdate);
+  AddAppResponse(kResponseStatusNoUpdate);
+
+  app_->PostUpdateCheck(S_OK, update_response_.get());
+  EXPECT_EQ(STATE_NO_UPDATE, app_->state());
+  EXPECT_EQ(0, app_->error_code());
+}
+
+TEST_F(AppManualUpdateTest, PostUpdateCheck_UpdateAvailable) {
+  SetAppStateForUnitTest(app_, new fsm::AppStateCheckingForUpdate);
+  AddAppResponse(kResponseStatusOkValue);
+
+  app_->PostUpdateCheck(S_OK, update_response_.get());
+  EXPECT_EQ(STATE_UPDATE_AVAILABLE, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+}
+
+// Policy is not checked by this function.
+TEST_F(AppManualUpdateTest,
+       PostUpdateCheck_UpdateAvailable_AllUpdatesDisabled) {
+  SetPolicy(kUpdatePolicyApp1, kPolicyDisabled);
+  SetAppStateForUnitTest(app_, new fsm::AppStateCheckingForUpdate);
+  AddAppResponse(kResponseStatusOkValue);
+
+  app_->PostUpdateCheck(S_OK, update_response_.get());
+  EXPECT_EQ(STATE_UPDATE_AVAILABLE, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+}
+
+TEST_F(AppAutoUpdateTest, PostUpdateCheck_NoUpdate) {
+  SetAppStateForUnitTest(app_, new fsm::AppStateCheckingForUpdate);
+  AddAppResponse(kResponseStatusNoUpdate);
+
+  app_->PostUpdateCheck(S_OK, update_response_.get());
+  EXPECT_EQ(STATE_NO_UPDATE, app_->state());
+  EXPECT_EQ(0, app_->error_code());
+}
+
+TEST_F(AppAutoUpdateTest, PostUpdateCheck_UpdateAvailable) {
+  SetAppStateForUnitTest(app_, new fsm::AppStateCheckingForUpdate);
+  AddAppResponse(kResponseStatusOkValue);
+
+  app_->PostUpdateCheck(S_OK, update_response_.get());
+  EXPECT_EQ(STATE_UPDATE_AVAILABLE, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+}
+
+// Policy is not checked by this function.
+TEST_F(AppAutoUpdateTest, PostUpdateCheck_UpdateAvailable_AllUpdatesDisabled) {
+  SetPolicy(kUpdatePolicyApp1, kPolicyDisabled);
+  SetAppStateForUnitTest(app_, new fsm::AppStateCheckingForUpdate);
+  AddAppResponse(kResponseStatusOkValue);
+
+  app_->PostUpdateCheck(S_OK, update_response_.get());
+  EXPECT_EQ(STATE_UPDATE_AVAILABLE, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+}
+
+//
+// QueueDownload Tests.
+//
+
+TEST_F(AppInstallTest, QueueDownload_NoPolicy) {
+  SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable);
+
+  app_->QueueDownload();
+  EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+}
+
+TEST_F(AppInstallTest, QueueDownload_InstallDisabled) {
+  SetPolicy(kInstallPolicyApp1, kPolicyDisabled);
+  SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable);
+
+  app_->QueueDownload();
+  EXPECT_EQ(STATE_ERROR, app_->state());
+  EXPECT_EQ(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY, app_->error_code());
+}
+
+TEST_F(AppInstallTest,
+       QueueDownload_InstallDisabledForDifferentApp) {
+  SetPolicy(kInstallPolicyApp2, kPolicyDisabled);
+  SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable);
+
+  app_->QueueDownload();
+  EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+}
+
+TEST_F(AppInstallTest, QueueDownload_AllUpdatesDisabled) {
+  SetPolicy(kUpdatePolicyApp1, kPolicyDisabled);
+  SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable);
+
+  app_->QueueDownload();
+  EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+}
+
+TEST_F(AppManualUpdateTest, QueueDownload_NoPolicy) {
+  SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable);
+
+  app_->QueueDownload();
+  EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+}
+
+TEST_F(AppManualUpdateTest, QueueDownload_AllUpdatesDisabled) {
+  SetPolicy(kUpdatePolicyApp1, kPolicyDisabled);
+  SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable);
+
+  app_->QueueDownload();
+  EXPECT_EQ(STATE_ERROR, app_->state());
+  EXPECT_EQ(GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY, app_->error_code());
+}
+
+TEST_F(AppManualUpdateTest,
+       QueueDownload_AllUpdatesDisabledForDifferentApp) {
+  SetPolicy(kUpdatePolicyApp2, kPolicyDisabled);
+  SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable);
+
+  app_->QueueDownload();
+  EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+}
+
+TEST_F(AppManualUpdateTest, QueueDownload_AutoUpdatesDisabled) {
+  SetPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly);
+  SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable);
+
+  app_->QueueDownload();
+  EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+}
+
+TEST_F(AppManualUpdateTest, QueueDownload_InstallDisabled) {
+  SetPolicy(kInstallPolicyApp1, kPolicyDisabled);
+  SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable);
+
+  app_->QueueDownload();
+  EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+}
+
+TEST_F(AppAutoUpdateTest, QueueDownload_AllUpdatesDisabled_NoPolicy) {
+  SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable);
+
+  app_->QueueDownload();
+  EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+}
+
+TEST_F(AppAutoUpdateTest, QueueDownload_AllUpdatesDisabled) {
+  SetPolicy(kUpdatePolicyApp1, kPolicyDisabled);
+  SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable);
+
+  app_->QueueDownload();
+  EXPECT_EQ(STATE_ERROR, app_->state());
+  EXPECT_EQ(GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY, app_->error_code());
+}
+
+TEST_F(AppAutoUpdateTest, QueueDownload_AllUpdatesDisabledForDifferentApp) {
+  SetPolicy(kUpdatePolicyApp2, kPolicyDisabled);
+  SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable);
+
+  app_->QueueDownload();
+  EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+}
+
+TEST_F(AppAutoUpdateTest, QueueDownload_AutoUpdatesDisabled) {
+  SetPolicy(kUpdatePolicyApp1, kPolicyManualUpdatesOnly);
+  SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable);
+
+  app_->QueueDownload();
+  EXPECT_EQ(STATE_ERROR, app_->state());
+  EXPECT_EQ(GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY, app_->error_code());
+}
+
+TEST_F(AppAutoUpdateTest, QueueDownload_InstallDisabled) {
+  SetPolicy(kInstallPolicyApp1, kPolicyDisabled);
+  SetAppStateForUnitTest(app_, new fsm::AppStateUpdateAvailable);
+
+  app_->QueueDownload();
+  EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+}
+
+//
+// PreUpdateCheck Tests.
+//
+
+TEST_F(AppInstallTest, PreUpdateCheck_EulaAccepted) {
+  SetAppStateForUnitTest(app_, new fsm::AppStateWaitingToCheckForUpdate);
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE));
+
+  scoped_ptr<xml::UpdateRequest> update_request;
+  update_request.reset(xml::UpdateRequest::Create(is_machine_,
+                                                  _T("unittest_sessionid"),
+                                                  _T("unittest_instsource"),
+                                                  CString()));
+  EXPECT_TRUE(update_request->IsEmpty());
+
+  app_->PreUpdateCheck(update_request.get());
+  EXPECT_EQ(STATE_CHECKING_FOR_UPDATE, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+  EXPECT_FALSE(update_request->IsEmpty());
+}
+
+TEST_F(AppAutoUpdateTest, PreUpdateCheck_EulaAccepted) {
+  SetAppStateForUnitTest(app_, new fsm::AppStateWaitingToCheckForUpdate);
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE));
+
+  scoped_ptr<xml::UpdateRequest> update_request;
+  update_request.reset(xml::UpdateRequest::Create(is_machine_,
+                                                  _T("unittest_sessionid"),
+                                                  _T("unittest_instsource"),
+                                                  CString()));
+  EXPECT_TRUE(update_request->IsEmpty());
+
+  app_->PreUpdateCheck(update_request.get());
+  EXPECT_EQ(STATE_CHECKING_FOR_UPDATE, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+  EXPECT_FALSE(update_request->IsEmpty());
+}
+
+TEST_F(AppInstallTest, PreUpdateCheck_EulaNotAccepted_Online) {
+  SetAppStateForUnitTest(app_, new fsm::AppStateWaitingToCheckForUpdate);
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_FALSE));
+
+  scoped_ptr<xml::UpdateRequest> update_request;
+  update_request.reset(xml::UpdateRequest::Create(is_machine_,
+                                                  _T("unittest_sessionid"),
+                                                  _T("unittest_instsource"),
+                                                  CString()));
+
+  ExpectAsserts expect_asserts;  // Asserts because not is_update.
+
+  app_->PreUpdateCheck(update_request.get());
+  EXPECT_EQ(STATE_ERROR, app_->state());
+  EXPECT_EQ(GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED,
+            app_->error_code());
+  EXPECT_TRUE(update_request->IsEmpty()) << _T("Should not add request.");
+}
+
+TEST_F(AppInstallTest, PreUpdateCheck_EulaNotAccepted_Offline) {
+  SetAppStateForUnitTest(app_, new fsm::AppStateWaitingToCheckForUpdate);
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_FALSE));
+  EXPECT_SUCCEEDED(app_bundle_->put_offlineDirectory(CComBSTR(_T("foo"))));
+
+  scoped_ptr<xml::UpdateRequest> update_request;
+  update_request.reset(xml::UpdateRequest::Create(is_machine_,
+                                                  _T("unittest_sessionid"),
+                                                  _T("unittest_instsource"),
+                                                  CString()));
+
+  app_->PreUpdateCheck(update_request.get());
+  EXPECT_EQ(STATE_CHECKING_FOR_UPDATE, app_->state());
+  EXPECT_EQ(S_OK, app_->error_code());
+  EXPECT_TRUE(update_request->IsEmpty()) << _T("Should not add request.");
+}
+
+TEST_F(AppAutoUpdateTest, PreUpdateCheck_EulaNotAccepted) {
+  SetAppStateForUnitTest(app_, new fsm::AppStateWaitingToCheckForUpdate);
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_FALSE));
+
+  scoped_ptr<xml::UpdateRequest> update_request;
+  update_request.reset(xml::UpdateRequest::Create(is_machine_,
+                                                  _T("unittest_sessionid"),
+                                                  _T("unittest_instsource"),
+                                                  CString()));
+
+  app_->PreUpdateCheck(update_request.get());
+  EXPECT_EQ(STATE_ERROR, app_->state());
+  EXPECT_EQ(GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED,
+            app_->error_code());
+  EXPECT_TRUE(update_request->IsEmpty()) << _T("Should not add request.");
+}
+
+}  // namespace omaha
diff --git a/goopdate/app_unittest_base.h b/goopdate/app_unittest_base.h
new file mode 100644
index 0000000..16ecc5a
--- /dev/null
+++ b/goopdate/app_unittest_base.h
@@ -0,0 +1,127 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Provides a base framework for unit tests that need an App object.
+
+#ifndef OMAHA_GOOPDATE_APP_UNITTEST_BASE_H_
+#define OMAHA_GOOPDATE_APP_UNITTEST_BASE_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/app_util.h"
+#include "omaha/goopdate/app_manager.h"
+#include "omaha/goopdate/app_bundle_state_initialized.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/resource_manager.h"
+#include "omaha/goopdate/worker_mock.h"
+#include "omaha/testing/unit_test.h"
+
+using ::testing::Return;
+
+namespace omaha {
+
+// Overrides the registry.
+class AppTestBase : public testing::Test {
+ protected:
+  AppTestBase(bool is_machine, bool use_strict_mock)
+      : is_machine_(is_machine),
+        use_strict_mock_(use_strict_mock) {
+  }
+
+  virtual void SetUp() {
+    EXPECT_SUCCEEDED(AppManager::CreateInstance(is_machine_));
+
+    // Needed for error strings.
+    EXPECT_SUCCEEDED(ResourceManager::Create(
+                         is_machine_,
+                         app_util::GetCurrentModuleDirectory(),
+                         _T("en")));
+
+    if (use_strict_mock_) {
+      mock_worker_.reset(new testing::StrictMock<MockWorker>);
+    } else {
+      mock_worker_.reset(new testing::NiceMock<MockWorker>);
+    }
+
+    EXPECT_CALL(*mock_worker_, Lock()).WillRepeatedly(Return(2));
+    EXPECT_CALL(*mock_worker_, Unlock()).WillRepeatedly(Return(1));
+
+    model_.reset(new Model(mock_worker_.get()));
+
+    app_bundle_ = model_->CreateAppBundle(is_machine_);
+    ASSERT_TRUE(app_bundle_.get());
+
+    EXPECT_SUCCEEDED(app_bundle_->put_displayName(CComBSTR(_T("Test Bundle"))));
+    EXPECT_SUCCEEDED(app_bundle_->put_displayLanguage(CComBSTR(_T("en"))));
+    EXPECT_SUCCEEDED(app_bundle_->put_installSource(CComBSTR(_T("unittest"))));
+    // TODO(omaha3): Address with the TODO in AppBundleInitializedTest::SetUp()
+    // then remove app_bundle_state_initialized.h above.
+    if (is_machine_) {
+      SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                   new fsm::AppBundleStateInitialized);
+    } else {
+      EXPECT_SUCCEEDED(app_bundle_->initialize());
+    }
+  }
+
+  virtual void TearDown() {
+    ResourceManager::Delete();
+    AppManager::DeleteInstance();
+  }
+
+  const bool is_machine_;
+  const bool use_strict_mock_;
+
+  CString hive_override_key_name_;
+
+  scoped_ptr<MockWorker> mock_worker_;
+  scoped_ptr<Model> model_;
+
+  shared_ptr<AppBundle> app_bundle_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AppTestBase);
+};
+
+class AppTestBaseWithRegistryOverride : public AppTestBase {
+ protected:
+  AppTestBaseWithRegistryOverride(bool is_machine, bool use_strict_mock)
+      : AppTestBase(is_machine, use_strict_mock),
+        hive_override_key_name_(kRegistryHiveOverrideRoot) {}
+
+  // Override the registry after initializing the AppBundle so that the latter
+  // has the correct network configuration in the event there are pings to send.
+  // TODO(omaha3): Ideally we would not send pings from tests: http://b/2911608.
+  virtual void SetUp() {
+    AppTestBase::SetUp();
+
+    RegKey::DeleteKey(hive_override_key_name_);
+    OverrideRegistryHives(hive_override_key_name_);
+  }
+
+  virtual void TearDown() {
+    RestoreRegistryHives();
+    RegKey::DeleteKey(hive_override_key_name_);
+
+    AppTestBase::TearDown();
+  }
+
+  CString hive_override_key_name_;
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_UNITTEST_BASE_H_
diff --git a/goopdate/app_version.cc b/goopdate/app_version.cc
new file mode 100644
index 0000000..a3bcde0
--- /dev/null
+++ b/goopdate/app_version.cc
@@ -0,0 +1,204 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/app_version.h"
+#include <atlsafe.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/utils.h"
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+AppVersion::AppVersion(App* app)
+    : ModelObject(app->model()),
+      app_(app) {
+}
+
+// Destruction of App objects happens within the scope of their parent,
+// which controls the locking.
+AppVersion::~AppVersion() {
+  ASSERT1(model()->IsLockedByCaller());
+
+  for (size_t i = 0; i < packages_.size(); ++i) {
+    delete packages_[i];
+  }
+}
+
+CString AppVersion::version() const {
+  __mutexScope(model()->lock());
+  return version_;
+}
+
+void AppVersion::set_version(const CString& version) {
+  __mutexScope(model()->lock());
+  version_ = version;
+}
+
+App* AppVersion::app() {
+  __mutexScope(model()->lock());
+  return app_;
+}
+
+const App* AppVersion::app() const {
+  __mutexScope(model()->lock());
+  return app_;
+}
+
+// TODO(omaha3): It's unfortunate that the manifest can be empty. We need to
+// make a copy anyway in UpdateResponse::BuildApp, so maybe this class
+// should just expose a manifest object created in the constructor. On the
+// other hand, current_version may not have a manifest, so this would be an
+// empty object. Because the manifest must be created, AppData must friend
+// InstallManager tests and other tests that need a manifest. This could
+// probably be solved through mocking too.
+const xml::InstallManifest* AppVersion::install_manifest() const {
+  __mutexScope(model()->lock());
+  return install_manifest_.get();
+}
+
+void AppVersion::set_install_manifest(xml::InstallManifest* install_manifest) {
+  __mutexScope(model()->lock());
+  ASSERT1(install_manifest);
+  install_manifest_.reset(install_manifest);
+}
+
+size_t AppVersion::GetNumberOfPackages() const {
+  __mutexScope(model()->lock());
+  return packages_.size();
+}
+
+HRESULT AppVersion::AddPackage(const CString& filename,
+                               uint32 size,
+                               const CString& hash) {
+  __mutexScope(model()->lock());
+  Package* package = new Package(this);
+  package->SetFileInfo(filename, size, hash);
+  packages_.push_back(package);
+  return S_OK;
+}
+
+Package* AppVersion::GetPackage(size_t index) {
+  __mutexScope(model()->lock());
+
+  if (index >= GetNumberOfPackages()) {
+    ASSERT1(false);
+    return NULL;
+  }
+
+  return packages_[index];
+}
+
+const Package* AppVersion::GetPackage(size_t index) const {
+  __mutexScope(model()->lock());
+
+  if (index >= GetNumberOfPackages()) {
+    ASSERT1(false);
+    return NULL;
+  }
+
+  return packages_[index];
+}
+
+const std::vector<CString>& AppVersion::download_base_urls() const {
+  __mutexScope(model()->lock());
+  ASSERT1(!download_base_urls_.empty());
+  return download_base_urls_;
+}
+
+HRESULT AppVersion::AddDownloadBaseUrl(const CString& base_url) {
+  __mutexScope(model()->lock());
+  ASSERT1(!base_url.IsEmpty());
+  download_base_urls_.push_back(base_url);
+  return S_OK;
+}
+
+// IAppVersion.
+STDMETHODIMP AppVersion::get_version(BSTR* version) {
+  __mutexScope(model()->lock());
+  ASSERT1(version);
+  *version = version_.AllocSysString();
+  return S_OK;
+}
+
+STDMETHODIMP AppVersion::get_packageCount(long* count) {  // NOLINT
+  __mutexScope(model()->lock());
+
+  *count = GetNumberOfPackages();
+  return S_OK;
+}
+
+STDMETHODIMP AppVersion::get_package(long index, Package** package) {  // NOLINT
+  __mutexScope(model()->lock());
+
+  if (index < 0 || static_cast<size_t>(index) >= GetNumberOfPackages()) {
+    return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
+  }
+
+  *package = GetPackage(index);
+  return S_OK;
+}
+
+STDMETHODIMP AppVersionWrapper::get_version(BSTR* version) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_version(version);
+}
+
+STDMETHODIMP AppVersionWrapper::get_packageCount(long* count) {  // NOLINT
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_packageCount(count);
+}
+
+STDMETHODIMP AppVersionWrapper::get_package(long index,  // NOLINT
+                                            IDispatch** package) {
+  __mutexScope(model()->lock());
+
+  Package* p = NULL;
+  HRESULT hr = wrapped_obj()->get_package(index, &p);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return PackageWrapper::Create(controlling_ptr(), p, package);
+}
+
+HRESULT CopyAppVersionPackages(const AppVersion* app_version,
+                               const CString& dir) {
+  ASSERT1(app_version);
+
+  HRESULT hr(CreateDir(dir, NULL));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  for (size_t i = 0; i != app_version->GetNumberOfPackages(); ++i) {
+    const Package* package = app_version->GetPackage(i);
+    ASSERT1(package);
+    if (package) {
+      hr = package->get(CComBSTR(dir));
+      if (FAILED(hr)) {
+        CORE_LOG(LE, (_T("[Package::get failed][%s][%s][0x%x]"),
+            package->filename(), dir, hr));
+        return hr;
+      }
+    }
+  }
+
+  return S_OK;
+}
+
+}  // namespace omaha
diff --git a/goopdate/app_version.h b/goopdate/app_version.h
new file mode 100644
index 0000000..0f1923c
--- /dev/null
+++ b/goopdate/app_version.h
@@ -0,0 +1,123 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Defines the AppVersion COM object exposed by the model.
+
+#ifndef OMAHA_GOOPDATE_APP_VERSION_H_
+#define OMAHA_GOOPDATE_APP_VERSION_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/base/constants.h"
+#include "omaha/common/install_manifest.h"
+#include "omaha/goopdate/com_wrapper_creator.h"
+#include "omaha/goopdate/model_object.h"
+
+namespace omaha {
+
+class App;
+class AppBundle;
+class Package;
+
+class AppVersion : public ModelObject {
+ public:
+  explicit AppVersion(App* app);
+  virtual ~AppVersion();
+
+  // IAppVersion.
+  STDMETHOD(get_version)(BSTR* version);
+  STDMETHOD(get_packageCount)(long* count);  // NOLINT
+  STDMETHOD(get_package)(long index, Package** package);  // NOLINT
+
+  App* app();
+  const App* app() const;
+
+  CString version() const;
+  void set_version(const CString& version);
+
+  const xml::InstallManifest* install_manifest() const;
+  void set_install_manifest(xml::InstallManifest* install_manifest);
+
+  // Returns the number of packages that make up this app version.
+  size_t GetNumberOfPackages() const;
+
+  // Gets the package at the specified index.
+  Package* GetPackage(size_t index);
+  const Package* GetPackage(size_t index) const;
+
+  // Adds a package to this app version.
+  HRESULT AddPackage(const CString& filename, uint32 size, const CString& hash);
+
+  // Returns the list of download servers to use in order of preference.
+  const std::vector<CString>& download_base_urls() const;
+
+  // Adds a download server to the list of fallbacks. Servers are attempted in
+  // the order they are added.
+  HRESULT AddDownloadBaseUrl(const CString& server_url);
+
+ private:
+
+  // product version "pv".
+  CString version_;
+
+  scoped_ptr<xml::InstallManifest> install_manifest_;
+
+  std::vector<Package*> packages_;
+
+  std::vector<CString> download_base_urls_;
+
+  App* app_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppVersion);
+};
+
+class ATL_NO_VTABLE AppVersionWrapper
+    : public ComWrapper<AppVersionWrapper, AppVersion>,
+      public IDispatchImpl<IAppVersion,
+                           &__uuidof(IAppVersion),
+                           &CAtlModule::m_libid,
+                           kMajorTypeLibVersion,
+                           kMinorTypeLibVersion> {
+ public:
+  // IAppVersion.
+  STDMETHOD(get_version)(BSTR* version);
+  STDMETHOD(get_packageCount)(long* count);  // NOLINT
+  STDMETHOD(get_package)(long index, IDispatch** package);  // NOLINT
+
+ protected:
+  AppVersionWrapper() {}
+  virtual ~AppVersionWrapper() {}
+
+  BEGIN_COM_MAP(AppVersionWrapper)
+    COM_INTERFACE_ENTRY(IAppVersion)
+    COM_INTERFACE_ENTRY(IDispatch)
+  END_COM_MAP()
+
+ private:
+  typedef ComWrapper<AppVersionWrapper, AppVersion> Creator;
+  friend class Creator;
+};
+
+// Copies all packages of an app version to the specified directory.
+HRESULT CopyAppVersionPackages(const AppVersion* app_version,
+                               const CString& dir);
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APP_VERSION_H_
diff --git a/goopdate/app_version_unittest.cc b/goopdate/app_version_unittest.cc
new file mode 100644
index 0000000..e6eff36
--- /dev/null
+++ b/goopdate/app_version_unittest.cc
@@ -0,0 +1,146 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/update3_utils.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+// TODO(omaha): there is a problem with this unit test. The model is built
+// bottom up. This makes it impossible to set the references to parents. Will
+// have to fix the code, eventually using Builder DP to create a bunch of
+// models containing bundles, apps, and such.
+
+#if 0
+
+const TCHAR* const kTestId = _T("{8260D23D-D23B-427F-AF1A-2CE36E6F073B}");
+
+class IDummyUnknownImpl : public IUnknown {
+ public:
+  virtual ULONG STDMETHODCALLTYPE AddRef() {
+    return 1;
+  }
+  virtual ULONG STDMETHODCALLTYPE Release() {
+    return 1;
+  }
+  virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, void**) {
+    return E_NOTIMPL;
+  }
+};
+
+class AppVersionTest : public testing::Test {
+ protected:
+  LLock lock_;
+};
+
+TEST_F(AppVersionTest, TestReadOnly) {
+  IDummyUnknownImpl dummy_unknown;
+  scoped_ptr<AppVersion> app_version;
+  EXPECT_SUCCEEDED(AppVersion::Create(&lock_,
+                                      &dummy_unknown,
+                                      true,
+                                      address(app_version)));
+
+  CComPtr<IAppDataReadOnly> version_data_ro;
+  EXPECT_SUCCEEDED(update3_utils::GetAppData(app_version.get(),
+                                             &version_data_ro));
+
+  CComPtr<IAppData> version_data_rw;
+  EXPECT_EQ(E_NOINTERFACE,
+            update3_utils::GetAppData(app_version.get(), &version_data_rw));
+
+  // Get the IDispatch interface, and verify that it is a read-only interface.
+  CComPtr<IDispatch> version_data_ro_dispatch;
+  EXPECT_SUCCEEDED(
+      version_data_ro.QueryInterface(&version_data_ro_dispatch));
+
+  CComVariant var;
+  EXPECT_SUCCEEDED(version_data_ro_dispatch.GetPropertyByName(_T("appGuid"),
+                                                              &var));
+  EXPECT_EQ(VT_BSTR, V_VT(&var));
+  EXPECT_STREQ(GuidToString(GUID_NULL), V_BSTR(&var));
+
+  var = kTestId;
+
+  // ITypeInfo::Invoke with a DISPATCH_PROPERTYPUT results in a
+  // DISP_E_BADPARAMCOUNT, and not in a DISP_E_MEMBERNOTFOUND, as I would have
+  // expected.
+  EXPECT_EQ(DISP_E_BADPARAMCOUNT,
+            version_data_ro_dispatch.PutPropertyByName(_T("appGuid"), &var));
+}
+
+TEST_F(AppVersionTest, TestReadWrite) {
+  IDummyUnknownImpl dummy_unknown;
+  scoped_ptr<AppVersion> app_version;
+  EXPECT_SUCCEEDED(AppVersion::Create(&lock_,
+                                      &dummy_unknown,
+                                      false,
+                                      address(app_version)));
+
+  CComPtr<IAppDataReadOnly> version_data_ro;
+  EXPECT_SUCCEEDED(update3_utils::GetAppData(app_version.get(),
+                                             &version_data_ro));
+
+  CComPtr<IAppData> version_data_rw;
+  EXPECT_SUCCEEDED(update3_utils::GetAppData(app_version.get(),
+                                             &version_data_rw));
+
+  CComPtr<IUnknown> version_data_ro_unknown;
+  EXPECT_SUCCEEDED(
+      version_data_ro.QueryInterface(&version_data_ro_unknown));
+
+  CComPtr<IUnknown> version_data_rw_unknown;
+  EXPECT_SUCCEEDED(
+      version_data_rw.QueryInterface(&version_data_rw_unknown));
+
+  // COM identity rule.
+  EXPECT_TRUE(version_data_ro_unknown == version_data_rw_unknown);
+
+  // Get the IDispatch interface, and verify that it is a read-write interface.
+  CComPtr<IDispatch> version_data_rw_dispatch;
+  EXPECT_SUCCEEDED(
+      version_data_rw.QueryInterface(&version_data_rw_dispatch));
+
+  CComVariant var;
+  EXPECT_SUCCEEDED(version_data_rw_dispatch.GetPropertyByName(_T("appGuid"),
+                                                              &var));
+  EXPECT_EQ(VT_BSTR, V_VT(&var));
+  EXPECT_STREQ(GuidToString(GUID_NULL), V_BSTR(&var));
+
+  var = kTestId;
+  EXPECT_SUCCEEDED(version_data_rw_dispatch.PutPropertyByName(_T("appGuid"),
+                                                              &var));
+
+  var.ClearToZero();
+  EXPECT_SUCCEEDED(version_data_rw_dispatch.GetPropertyByName(_T("appGuid"),
+                                                              &var));
+  EXPECT_EQ(VT_BSTR, V_VT(&var));
+  EXPECT_STREQ(kTestId, V_BSTR(&var));
+
+  // Verify that the read-only interface returns the same value for the appGuid.
+  CComBSTR app_id;
+  EXPECT_SUCCEEDED(version_data_ro->get_appId(&app_id));
+  EXPECT_STREQ(kTestId, app_id);
+}
+
+#endif
+
+}  // namespace omaha
diff --git a/goopdate/application_usage_data.cc b/goopdate/application_usage_data.cc
new file mode 100644
index 0000000..f1cf39b
--- /dev/null
+++ b/goopdate/application_usage_data.cc
@@ -0,0 +1,337 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/application_usage_data.h"
+#include "omaha/base/const_utils.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/string.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/app_registry_utils.h"
+#include "omaha/common/const_goopdate.h"
+
+namespace omaha {
+
+namespace {
+
+HRESULT RegistryReadStringOrDword(const RegKey& key,
+                                  const CString& value_name,
+                                  CString* result) {
+  ASSERT1(result);
+  DWORD type = REG_NONE;
+  DWORD value = 0;
+
+  HRESULT hr = key.GetValueType(value_name, &type);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  switch (type) {
+    case REG_DWORD:
+      hr = key.GetValue(value_name, &value);
+      if (FAILED(hr)) {
+        return hr;
+      }
+      *result = itostr(static_cast<const uint32>(value));
+      return S_OK;
+    case REG_SZ:
+      return key.GetValue(value_name, result);
+    default:
+      return HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
+  }
+}
+
+}  // namespace
+
+ApplicationUsageData::ApplicationUsageData(bool is_machine,
+                                           bool check_low_integrity)
+    : exists_(false),
+      did_run_(false),
+      is_machine_(is_machine),
+      is_pre_update_check_(true),
+      check_low_integrity_(check_low_integrity) {
+}
+
+ApplicationUsageData::~ApplicationUsageData() {
+}
+
+HRESULT ApplicationUsageData::ReadDidRun(const CString& app_guid) {
+  CORE_LOG(L4, (_T("[ApplicationUsageData::ReadDidRun][%s]"), app_guid));
+  is_pre_update_check_ = true;
+  HRESULT hr = ProcessDidRun(app_guid);
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[ProcessDidRun failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+ActiveStates ApplicationUsageData::active_state() const {
+  if (exists()) {
+    return did_run() ? ACTIVE_RUN : ACTIVE_NOTRUN;
+  } else {
+    return ACTIVE_UNKNOWN;
+  }
+}
+
+HRESULT ApplicationUsageData::ResetDidRun(const CString& app_guid) {
+  CORE_LOG(L4, (_T("[ApplicationUsageData::ResetDidRun][%s]"), app_guid));
+  is_pre_update_check_ = false;
+  return ProcessDidRun(app_guid);
+}
+
+HRESULT ApplicationUsageData::ProcessDidRun(const CString& app_guid) {
+  CORE_LOG(L4, (_T("[ApplicationUsageData::ProcessDidRun][%s]"), app_guid));
+  return is_machine_ ? ProcessMachineDidRun(app_guid) :
+                       ProcessUserDidRun(app_guid);
+}
+
+HRESULT ApplicationUsageData::ProcessMachineDidRun(const CString& app_guid) {
+  ASSERT1(is_machine_);
+
+  // Logic is as follows:
+  // for each user under HKU\<sid>
+  //   pre/post process HKU\<sid>
+  //   if vista
+  //     pre/post process HKU\<lowintegrity IE>\<sid>
+  // pre/post process HKLM
+  RegKey users_key;
+  HRESULT hr = users_key.Open(USERS_KEY, KEY_READ);
+  if (SUCCEEDED(hr)) {
+    uint32 num_users = users_key.GetSubkeyCount();
+    for (uint32 i = 0; i < num_users; ++i) {
+      CString sub_key_name;
+      hr = users_key.GetSubkeyNameAt(i, &sub_key_name);
+      if (FAILED(hr)) {
+        CORE_LOG(LEVEL_WARNING, (_T("[Key enum failed.][0x%08x][%d][%s]"),
+                                 hr, i, USERS_KEY));
+        continue;
+      }
+
+      CString temp_key = AppendRegKeyPath(USERS_KEY,
+                                          sub_key_name,
+                                          GOOPDATE_REG_RELATIVE_CLIENT_STATE);
+      CString user_state_key_name = AppendRegKeyPath(temp_key, app_guid);
+      hr = ProcessKey(user_state_key_name);
+      if (FAILED(hr)) {
+        CORE_LOG(L4, (_T("[ProcessKey failed][%s][0x%08x]"), app_guid, hr));
+      }
+
+      if (check_low_integrity_) {
+        // If we are running on vista we need to also look at the low
+        // integrity IE key where IE can write to. Note that we cannot
+        // use the IEGetWriteableHKCU function since this function assumes
+        // that we are running with the user's credentials.
+        CString temp_key = AppendRegKeyPath(USERS_KEY,
+                                            sub_key_name,
+                                            USER_REG_VISTA_LOW_INTEGRITY_HKCU);
+        CString li_hkcu_name = AppendRegKeyPath(
+                                   AppendRegKeyPath(
+                                       temp_key,
+                                       sub_key_name,
+                                       GOOPDATE_REG_RELATIVE_CLIENT_STATE),
+                                   app_guid);
+        hr = ProcessKey(li_hkcu_name);
+        if (FAILED(hr)) {
+          CORE_LOG(L4, (_T("[ProcessKey failed][%s][0x%08x]"), app_guid, hr));
+        }
+      }
+    }  // End of for
+
+    // Now Process the machine did run value also.
+    CString machine_state_key_name =
+        app_registry_utils::GetAppClientStateKey(true, app_guid);
+    hr = ProcessBackWardCompatKey(machine_state_key_name);
+    if (FAILED(hr)) {
+      CORE_LOG(L4, (_T("[ProcessBackWardCompatKey failed][0x%08x][%s]"),
+                    hr, machine_state_key_name));
+    }
+  } else {
+    CORE_LOG(LW, (_T("[Key open failed.][0x%08x][%s]"), hr, USERS_KEY));
+  }
+
+  return S_OK;
+}
+
+HRESULT ApplicationUsageData::ProcessUserDidRun(const CString& app_guid) {
+  ASSERT1(!is_machine_);
+
+  // Logic:
+  // Pre/Post process HKCU\
+  // if vista:
+  //    Pre/Post process HKCU\LowIntegrity
+  CString state_key_name = app_registry_utils::GetAppClientStateKey(false,
+                                                                    app_guid);
+  HRESULT hr = ProcessKey(state_key_name);
+  if (FAILED(hr)) {
+      CORE_LOG(L4, (_T("[ProcessKey failed][0x%08x][%s]"),
+                    hr, state_key_name));
+  }
+
+  if (check_low_integrity_) {
+    // If we are running on vista we need to also look at the low
+    // integrity IE key where IE can write to. To avoid loading
+    // ieframe.dll into our process, we just use the registry
+    // key location directly instead of using IEGetWriteableHKCU
+    CString sid;
+    hr = user_info::GetProcessUser(NULL, NULL, &sid);
+    if (FAILED(hr)) {
+      CORE_LOG(LEVEL_WARNING, (_T("[GetProcessUser failed][0x%08x][%s]"),
+                               hr, app_guid));
+      return hr;
+    }
+
+    CString temp_name = AppendRegKeyPath(USER_KEY_NAME,
+                                         USER_REG_VISTA_LOW_INTEGRITY_HKCU,
+                                         sid);
+    CString lowintegrity_hkcu_name = AppendRegKeyPath(
+                                         temp_name,
+                                         GOOPDATE_REG_RELATIVE_CLIENT_STATE,
+                                         app_guid);
+    hr = ProcessKey(lowintegrity_hkcu_name);
+    if (FAILED(hr)) {
+      CORE_LOG(LEVEL_WARNING, (_T("[Could not ProcessKey][0x%08x][%s]"),
+                               hr, app_guid));
+    }
+  }
+
+  return S_OK;
+}
+
+HRESULT ApplicationUsageData::ProcessKey(const CString& key_name) {
+  return is_pre_update_check_ ? ProcessPreUpdateCheck(key_name) :
+                                ProcessPostUpdateCheck(key_name);
+}
+
+HRESULT ApplicationUsageData::ProcessPreUpdateCheck(const CString& key_name) {
+  // Read in the regkey value if it exists, and or it with the previous value.
+  RegKey key;
+  HRESULT hr = key.Open(key_name, KEY_READ);
+  if (FAILED(hr)) {
+    CORE_LOG(L4, (_T("[failed to open key][%s][0x%08x]"), key_name, hr));
+    return hr;
+  }
+
+  // Now that we have the key, we should try and read the value of the
+  // did run key.
+  CString did_run_str(_T("0"));
+  hr = RegistryReadStringOrDword(key, kRegValueDidRun, &did_run_str);
+  if (FAILED(hr)) {
+    CORE_LOG(L3, (_T("[RegKey::GetValue failed][0x%08x][%s][%s]"),
+                  hr, key_name, kRegValueDidRun));
+    return hr;
+  }
+
+  if (did_run_str == _T("1")) {
+    did_run_ = true;
+  }
+  exists_ = true;
+
+  return hr;
+}
+
+HRESULT ApplicationUsageData::ProcessBackWardCompatKey(
+    const CString& key_name) {
+  // This method exists to support the installers that have not been
+  // updated to write to the HKCU key. Remove when we have all the installers
+  // correcly updated.
+  if (is_pre_update_check_) {
+    // Read in the regkey value if it exists, and or it with the previous value.
+    RegKey key;
+    HRESULT hr = key.Open(key_name, KEY_READ);
+    if (FAILED(hr)) {
+      CORE_LOG(L4, (_T("[failed to open key][%s][0x%08x]"), key_name, hr));
+      return hr;
+    }
+
+    // Now that we have the key, we should try and read the value of the
+    // did run key.
+    CString did_run_str;
+    hr = RegistryReadStringOrDword(key, kRegValueDidRun, &did_run_str);
+    if (FAILED(hr)) {
+      CORE_LOG(L3, (_T("[RegKey::GetValue failed][0x%08x][%s][%s]"),
+                    hr, key_name, kRegValueDidRun));
+      return hr;
+    }
+
+    if (did_run_str == _T("1")) {
+      did_run_ = true;
+    }
+    exists_ = true;
+
+    return hr;
+  } else {
+    RegKey key;
+    HRESULT hr = key.Open(key_name);
+    if (FAILED(hr)) {
+      CORE_LOG(L4, (_T("[failed to open key][%s][0x%08x]"), key_name, hr));
+      return hr;
+    }
+
+    // If the value exists, then it means that the installer has been updated,
+    // and we delete the machine value.
+    if (exists_) {
+      hr = RegKey::DeleteValue(key_name, kRegValueDidRun);
+      if (FAILED(hr)) {
+        CORE_LOG(LEVEL_WARNING, (_T("[RegKey::DeleteValue failed][0x%08x][%s]"),
+                                 hr, key_name));
+        return hr;
+      }
+    } else {
+      // Since the value does not exist else where, we reset the value in the
+      // HKLM key to zero.
+      exists_ = true;
+      CString did_run_str;
+      hr = RegistryReadStringOrDword(key, kRegValueDidRun, &did_run_str);
+      if (SUCCEEDED(hr)) {
+        hr = key.SetValue(kRegValueDidRun, _T("0"));
+        if (FAILED(hr)) {
+          CORE_LOG(LEVEL_WARNING, (_T("[RegKey::SetValue failed][0x%08x][%s]"),
+                                   hr, key_name));
+          return hr;
+        }
+      }
+    }
+  }
+
+  return S_OK;
+}
+
+HRESULT ApplicationUsageData::ProcessPostUpdateCheck(const CString& key_name) {
+  RegKey key;
+  HRESULT hr = key.Open(key_name);
+  if (FAILED(hr)) {
+    CORE_LOG(L4, (_T("[failed to open key][%s][0x%08x]"), key_name, hr));
+    return hr;
+  }
+
+  CString did_run_str;
+  hr = RegistryReadStringOrDword(key, kRegValueDidRun, &did_run_str);
+  if (SUCCEEDED(hr)) {
+    exists_ = true;
+    hr = key.SetValue(kRegValueDidRun, _T("0"));
+    if (FAILED(hr)) {
+      CORE_LOG(LEVEL_WARNING, (_T("[RegKey::SetValue failed][0x%08x][%s]"),
+                               hr, key_name));
+      return hr;
+    }
+  }
+  return S_OK;
+}
+
+}  // namespace omaha
diff --git a/goopdate/application_usage_data.h b/goopdate/application_usage_data.h
new file mode 100644
index 0000000..2bde86d
--- /dev/null
+++ b/goopdate/application_usage_data.h
@@ -0,0 +1,86 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// application_usage_data.h : Includes methods to deal with application
+// usage data. Currently it only deals with the did_run key.
+// The class provides methods to process the application data, before and
+// after the update check. In case of the did_run key we read the key
+// pre-update check and clear it post-update check.
+
+#ifndef OMAHA_GOOPDATE_APPLICATION_USAGE_DATA_H__
+#define OMAHA_GOOPDATE_APPLICATION_USAGE_DATA_H__
+
+#include <windows.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+enum ActiveStates;
+
+class ApplicationUsageData {
+ public:
+  ApplicationUsageData(bool is_machine, bool check_low_integrity);
+  ~ApplicationUsageData();
+
+  // Reads the did run values for the application indentified by the app_guid.
+  HRESULT ReadDidRun(const CString& app_guid);
+
+  // Clears and performs the post processing after an update ckeck for the
+  // did run key.
+  HRESULT ResetDidRun(const CString& app_guid);
+
+  bool exists() const { return exists_; }
+  bool did_run() const { return did_run_; }
+  ActiveStates active_state() const;
+
+ private:
+  // Processes the did run value for the machine goopdate.
+  HRESULT ProcessMachineDidRun(const CString& app_guid);
+
+  // Processes the did run value for the user goopdate.
+  HRESULT ProcessUserDidRun(const CString& app_guid);
+
+  // Calls the pre or the post update check methods based on the
+  // is_pre_update_check_ value.
+  HRESULT ProcessDidRun(const CString& app_guid);
+
+  // Pre or post process the key that is passed in.
+  HRESULT ProcessKey(const CString& key_name);
+
+  // Reads the did run value and populates did_run_ and exists_.
+  HRESULT ProcessPreUpdateCheck(const CString& key_name);
+
+  // Clears the did_run value.
+  HRESULT ProcessPostUpdateCheck(const CString& key_name);
+
+  // Reads and updates the did_run key for the machine. This is a backward
+  // compatibility requirement, since applications have not been updated to
+  // write to HKCU yet.
+  HRESULT ProcessBackWardCompatKey(const CString& key_name);
+
+  bool exists_;                // Whether the did_run value exists.
+  bool did_run_;               // The value of did_run.
+  bool is_machine_;            // Whether this is a machine instance.
+  bool is_pre_update_check_;   // Internal state of pre or post update.
+  bool check_low_integrity_;   // Whether to check the low integrity registry
+                               // location.
+
+  DISALLOW_EVIL_CONSTRUCTORS(ApplicationUsageData);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_APPLICATION_USAGE_DATA_H__
diff --git a/goopdate/application_usage_data_unittest.cc b/goopdate/application_usage_data_unittest.cc
new file mode 100644
index 0000000..82587c8
--- /dev/null
+++ b/goopdate/application_usage_data_unittest.cc
@@ -0,0 +1,747 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// ApplicationUsageData unit tests
+
+#include "omaha/base/reg_key.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/testing/unit_test.h"
+#include "omaha/goopdate/application_usage_data.h"
+
+namespace omaha {
+
+const TCHAR kAppDidRunValueName[] = _T("dr");
+const TCHAR kHKCUClientStateKeyName[] =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\{6ACB7D4D-E5BA-48b0-85FE-A4051500A1BD}");
+const TCHAR kMachineClientState[] =
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\{6ACB7D4D-E5BA-48b0-85FE-A4051500A1BD}");
+const TCHAR kLowIntegrityIEHKCU[] =
+    _T("HKCU\\Software\\Microsoft\\Internet Explorer\\")
+    _T("InternetRegistry\\REGISTRY\\USER\\");
+const TCHAR kAppGuid[] = _T("{6ACB7D4D-E5BA-48b0-85FE-A4051500A1BD}");
+const TCHAR kRelativeClientState[] =
+    _T("Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\{6ACB7D4D-E5BA-48b0-85FE-A4051500A1BD}");
+
+// TODO(omaha): Expected and actual are reversed throughout this file. Fix.
+
+class ApplicationUsageDataTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    CString sid;
+    ASSERT_SUCCEEDED(user_info::GetProcessUser(NULL, NULL, &sid));
+    low_integrity_key_name_ = AppendRegKeyPath(kLowIntegrityIEHKCU,
+                                               sid,
+                                               kRelativeClientState);
+    TearDown();
+  }
+
+  virtual void TearDown() {
+    RegKey::DeleteKey(kHKCUClientStateKeyName);
+    RegKey::DeleteKey(kMachineClientState);
+    RegKey::DeleteKey(low_integrity_key_name_);
+  }
+
+  void CreateMachineDidRunValue(bool value) {
+    if (!vista_util::IsUserAdmin()) {
+      return;
+    }
+    RegKey key;
+    ASSERT_SUCCEEDED(key.Create(kMachineClientState));
+    ASSERT_SUCCEEDED(key.SetValue(kAppDidRunValueName,
+                                  value == true ? _T("1") : _T("0")));
+  }
+
+  void CreateMachineDidRunDwordValue(bool value) {
+    if (!vista_util::IsUserAdmin()) {
+      return;
+    }
+    RegKey key;
+    DWORD new_value = (value == true ? 1 : 0);
+    ASSERT_SUCCEEDED(key.Create(kMachineClientState));
+    ASSERT_SUCCEEDED(key.SetValue(kAppDidRunValueName, new_value));
+  }
+
+  bool MachineDidRunValueExists() {
+    if (!vista_util::IsUserAdmin()) {
+      return true;
+    }
+    RegKey key;
+    if (FAILED(key.Open(kMachineClientState))) {
+      return false;
+    }
+
+    CString did_run_str(_T("0"));
+    if (FAILED(key.GetValue(kAppDidRunValueName, &did_run_str))) {
+      return false;
+    }
+
+    return true;
+  }
+
+  void DeleteMachineDidRunValue() {
+    if (!vista_util::IsUserAdmin()) {
+      return;
+    }
+    ASSERT_SUCCEEDED(RegKey::DeleteValue(kMachineClientState,
+                                         kAppDidRunValueName));
+  }
+
+  void CheckMachineDidRunValue(bool expected) {
+    if (!vista_util::IsUserAdmin()) {
+      return;
+    }
+    RegKey key;
+    ASSERT_SUCCEEDED(key.Open(kMachineClientState));
+
+    CString did_run_str(_T("0"));
+    ASSERT_SUCCEEDED(key.GetValue(kAppDidRunValueName, &did_run_str));
+    bool value = (did_run_str == _T("1")) ? true : false;
+
+    ASSERT_EQ(value, expected);
+  }
+
+  void CreateUserDidRunValue(bool value) {
+    RegKey key;
+    ASSERT_SUCCEEDED(key.Create(kHKCUClientStateKeyName));
+    ASSERT_SUCCEEDED(key.SetValue(kAppDidRunValueName,
+                                  (value == true) ? _T("1") : _T("0")));
+  }
+
+  void CreateUserDidRunDwordValue(bool value) {
+    RegKey key;
+    DWORD new_value = (value == true ? 1 : 0);
+    ASSERT_SUCCEEDED(key.Create(kHKCUClientStateKeyName));
+    ASSERT_SUCCEEDED(key.SetValue(kAppDidRunValueName, new_value));
+  }
+
+  void DeleteUserDidRunValue() {
+    ASSERT_SUCCEEDED(RegKey::DeleteValue(kHKCUClientStateKeyName,
+                                         kAppDidRunValueName));
+  }
+
+  void CheckUserDidRunValue(bool expected) {
+    RegKey key;
+    ASSERT_SUCCEEDED(key.Open(kHKCUClientStateKeyName));
+
+    CString did_run_str(_T("0"));
+    ASSERT_SUCCEEDED(key.GetValue(kAppDidRunValueName, &did_run_str));
+    bool value = (did_run_str == _T("1")) ? true : false;
+
+    ASSERT_EQ(value, expected);
+  }
+
+  bool UserDidRunValueExists() {
+    RegKey key;
+    if (FAILED(key.Open(kHKCUClientStateKeyName))) {
+      return false;
+    }
+
+    CString did_run_str(_T("0"));
+    if (FAILED(key.GetValue(kAppDidRunValueName, &did_run_str))) {
+      return false;
+    }
+
+    return true;
+  }
+
+  void CreateLowIntegrityUserDidRunValue(bool value) {
+    RegKey key;
+    ASSERT_SUCCEEDED(key.Create(low_integrity_key_name_));
+    ASSERT_SUCCEEDED(key.SetValue(kAppDidRunValueName,
+                                  (value == true) ? _T("1") : _T("0")));
+  }
+
+  void CreateLowIntegrityUserDidRunDwordValue(bool value) {
+    RegKey key;
+    DWORD new_value = (value == true ? 1 : 0);
+    ASSERT_SUCCEEDED(key.Create(low_integrity_key_name_));
+    ASSERT_SUCCEEDED(key.SetValue(kAppDidRunValueName, new_value));
+  }
+
+  void DeleteLowIntegrityUserDidRunValue() {
+    ASSERT_SUCCEEDED(RegKey::DeleteValue(low_integrity_key_name_,
+                                         kAppDidRunValueName));
+  }
+
+  void CheckLowIntegrityUserDidRunValue(bool expected) {
+    RegKey key;
+    ASSERT_SUCCEEDED(key.Open(low_integrity_key_name_));
+
+    CString did_run_str(_T("0"));
+    ASSERT_SUCCEEDED(key.GetValue(kAppDidRunValueName, &did_run_str));
+    bool value = (did_run_str == _T("1")) ? true : false;
+
+    ASSERT_EQ(value, expected);
+  }
+
+  bool LowIntegrityUserDidRunValueExists() {
+    RegKey key;
+    if (FAILED(key.Open(low_integrity_key_name_))) {
+      return false;
+    }
+
+    CString did_run_str(_T("0"));
+    if (FAILED(key.GetValue(kAppDidRunValueName, &did_run_str))) {
+      return false;
+    }
+
+    return true;
+  }
+
+  // This method takes in machine_did_run, user_did_run and
+  // low_user_did_run as int's. The idea is that the test tries to simulate
+  // all of these values as being not-present, and if present then true or
+  // false.
+  // -1 indicates non-presense, 1 indicates true, and 0 false. The caller
+  // then loops over all these values to capture testing all the permutations.
+  void TestUserAndMachineDidRun(int machine_did_run,
+                                int user_did_run,
+                                int low_user_did_run,
+                                bool expected_exists,
+                                bool expected_did_run,
+                                int is_vista) {
+    ApplicationUsageData data(true, is_vista ? true : false);
+
+    // Set up the registry for the test.
+    if (machine_did_run != -1) {
+      CreateMachineDidRunValue((machine_did_run == 1) ? true: false);
+    }
+
+    if (user_did_run != -1) {
+      CreateUserDidRunValue((user_did_run == 1) ? true: false);
+    }
+
+    if (low_user_did_run != -1) {
+      CreateLowIntegrityUserDidRunValue((low_user_did_run == 1) ? true: false);
+    }
+
+    // Perform the test.
+    ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
+    ASSERT_EQ(data.exists(), expected_exists);
+    ASSERT_EQ(data.did_run(), expected_did_run);
+
+    // Check the return values.
+    if (machine_did_run == -1) {
+      ASSERT_FALSE(MachineDidRunValueExists());
+    } else {
+      CheckMachineDidRunValue((machine_did_run == 1) ? true: false);
+    }
+
+    if (user_did_run == -1) {
+      ASSERT_FALSE(UserDidRunValueExists());
+    } else {
+      CheckUserDidRunValue((user_did_run == 1) ? true: false);
+    }
+
+    if (low_user_did_run == -1) {
+      ASSERT_FALSE(LowIntegrityUserDidRunValueExists());
+    } else {
+      CheckLowIntegrityUserDidRunValue((low_user_did_run == 1) ? true: false);
+    }
+  }
+
+  void TestUserAndMachineDidRunPostProcess(int machine_did_run,
+                                           int user_did_run,
+                                           int low_user_did_run,
+                                           bool expected_exists,
+                                           int is_vista) {
+    ApplicationUsageData data(true, is_vista ? true : false);
+
+    // Setup the registry for the test.
+    if (machine_did_run != -1) {
+      CreateMachineDidRunValue((machine_did_run == 1) ? true: false);
+    }
+
+    if (user_did_run != -1) {
+      CreateUserDidRunValue((user_did_run == 1) ? true: false);
+    }
+
+    if (low_user_did_run != -1) {
+      CreateLowIntegrityUserDidRunValue((low_user_did_run == 1) ? true: false);
+    }
+
+    // Run the test.
+    ASSERT_SUCCEEDED(data.ResetDidRun(kAppGuid));
+    if (user_did_run == -1) {
+      ASSERT_FALSE(UserDidRunValueExists());
+    } else {
+      CheckUserDidRunValue(false);
+    }
+
+    if (low_user_did_run == -1) {
+      ASSERT_FALSE(LowIntegrityUserDidRunValueExists());
+    } else {
+      if (is_vista) {
+        CheckLowIntegrityUserDidRunValue(false);
+      } else {
+        CheckLowIntegrityUserDidRunValue((low_user_did_run == 1) ? true: false);
+      }
+    }
+
+    if (machine_did_run == -1) {
+      ASSERT_FALSE(MachineDidRunValueExists());
+    } else {
+      if (user_did_run != -1 ||  (is_vista && low_user_did_run != -1)) {
+        // This means that the user keys exists for this application
+        // we should have delete the machine key.
+        ASSERT_EQ(MachineDidRunValueExists(), false);
+      } else {
+        CheckMachineDidRunValue(false);
+      }
+    }
+
+    ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
+    ASSERT_EQ(data.exists(), expected_exists);
+    ASSERT_EQ(data.did_run(), false);
+  }
+
+  void UserTestDidRunPreProcess(int user_did_run,
+                                int low_user_did_run,
+                                int is_vista,
+                                bool expected_exists,
+                                bool expected_did_run) {
+    ApplicationUsageData data(false, is_vista ? true : false);
+
+    // Set up the registry for the test.
+    CreateMachineDidRunValue(true);
+
+    if (user_did_run != -1) {
+      CreateUserDidRunValue((user_did_run == 1) ? true: false);
+    }
+
+    if (low_user_did_run != -1) {
+      CreateLowIntegrityUserDidRunValue((low_user_did_run == 1) ? true: false);
+    }
+
+    // Perform the test.
+    ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
+    ASSERT_EQ(data.exists(), expected_exists);
+    ASSERT_EQ(data.did_run(), expected_did_run);
+
+    // The machine value should not have changed from what we set it to.
+    CheckMachineDidRunValue(true);
+    if (user_did_run == -1) {
+      // If we did not create the user value it should not exist.
+      ASSERT_FALSE(UserDidRunValueExists());
+    }
+
+    if (low_user_did_run == -1) {
+      // If we did not create the low integrity user value it should not exist.
+      ASSERT_FALSE(LowIntegrityUserDidRunValueExists());
+    }
+  }
+
+  void UserTestDidRunPostProcess(int user_did_run,
+                                 int low_user_did_run,
+                                 int is_vista) {
+    // Create a user ApplicationUsageData class.
+    ApplicationUsageData data(false, is_vista ? true : false);
+
+    // This should not affect the test.
+    CreateMachineDidRunValue(true);
+
+    if (user_did_run != -1) {
+      CreateUserDidRunValue((user_did_run == 1) ? true: false);
+    }
+
+    if (low_user_did_run != -1) {
+      CreateLowIntegrityUserDidRunValue((low_user_did_run == 1) ? true: false);
+    }
+
+    ASSERT_SUCCEEDED(data.ResetDidRun(kAppGuid));
+
+    // The machine did run shold never get affected.
+    CheckMachineDidRunValue(true);
+    if (user_did_run == -1) {
+      ASSERT_FALSE(UserDidRunValueExists());
+    } else {
+      // In all cases if the HKCU did run is set, it should get cleared.
+      CheckUserDidRunValue(false);
+    }
+
+    if (low_user_did_run == -1) {
+      ASSERT_FALSE(LowIntegrityUserDidRunValueExists());
+    } else {
+      // In case of vista, the low integrity user value should get reset.
+      CheckLowIntegrityUserDidRunValue(is_vista ? false :
+                                       (low_user_did_run == 1) ? true : false);
+    }
+  }
+
+ private:
+  CString low_integrity_key_name_;
+};
+
+TEST_F(ApplicationUsageDataTest, ReadDidRunUser1) {
+  ApplicationUsageData data(true, false);
+
+  ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data.exists(), false);
+  ASSERT_EQ(data.did_run(), false);
+
+  // Test with false user value.
+  CreateUserDidRunValue(false);
+  ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data.exists(), true);
+  ASSERT_EQ(data.did_run(), false);
+}
+
+TEST_F(ApplicationUsageDataTest, ReadDwordDidRunUser1) {
+  ApplicationUsageData data(true, false);
+
+  ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data.exists(), false);
+  ASSERT_EQ(data.did_run(), false);
+
+  // Test with false user value.
+  CreateUserDidRunDwordValue(false);
+  ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data.exists(), true);
+  ASSERT_EQ(data.did_run(), false);
+}
+
+TEST_F(ApplicationUsageDataTest, ReadDidRunUser2) {
+  // Test with true user value.
+  ApplicationUsageData data1(true, false);
+  CreateUserDidRunValue(true);
+  ASSERT_SUCCEEDED(data1.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data1.exists(), true);
+  ASSERT_EQ(data1.did_run(), true);
+}
+
+TEST_F(ApplicationUsageDataTest, ReadDwordDidRunUser2) {
+  // Test with true user value.
+  ApplicationUsageData data1(true, false);
+  CreateUserDidRunDwordValue(true);
+  ASSERT_SUCCEEDED(data1.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data1.exists(), true);
+  ASSERT_EQ(data1.did_run(), true);
+}
+
+TEST_F(ApplicationUsageDataTest, ReadDidRunUser3) {
+  // low integrity user = false, vista
+  ApplicationUsageData data2(true, true);
+  CreateLowIntegrityUserDidRunValue(false);
+  ASSERT_SUCCEEDED(data2.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data2.exists(), true);
+  ASSERT_EQ(data2.did_run(), false);
+}
+
+TEST_F(ApplicationUsageDataTest, ReadDwordDidRunUser3) {
+  // low integrity user = false, vista
+  ApplicationUsageData data2(true, true);
+  CreateLowIntegrityUserDidRunDwordValue(false);
+  ASSERT_SUCCEEDED(data2.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data2.exists(), true);
+  ASSERT_EQ(data2.did_run(), false);
+}
+
+TEST_F(ApplicationUsageDataTest, ReadDidRunUser4) {
+  // low integrity user = true, vista
+  ApplicationUsageData data2(true, true);
+  CreateLowIntegrityUserDidRunValue(true);
+  ASSERT_SUCCEEDED(data2.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data2.exists(), true);
+  ASSERT_EQ(data2.did_run(), true);
+}
+
+TEST_F(ApplicationUsageDataTest, ReadDwordDidRunUser4) {
+  // low integrity user = true, vista
+  ApplicationUsageData data2(true, true);
+  CreateLowIntegrityUserDidRunDwordValue(true);
+  ASSERT_SUCCEEDED(data2.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data2.exists(), true);
+  ASSERT_EQ(data2.did_run(), true);
+}
+
+TEST_F(ApplicationUsageDataTest, ReadDidRunUser5) {
+  // low integrity user = true, not vista
+  ApplicationUsageData data2(true, false);
+  CreateLowIntegrityUserDidRunValue(true);
+  ASSERT_SUCCEEDED(data2.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data2.exists(), false);
+  ASSERT_EQ(data2.did_run(), false);
+}
+
+TEST_F(ApplicationUsageDataTest, ReadDwordDidRunUser5) {
+  // low integrity user = true, not vista
+  ApplicationUsageData data2(true, false);
+  CreateLowIntegrityUserDidRunDwordValue(true);
+  ASSERT_SUCCEEDED(data2.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data2.exists(), false);
+  ASSERT_EQ(data2.did_run(), false);
+}
+
+TEST_F(ApplicationUsageDataTest, ReadDidRunMachine1) {
+  if (!vista_util::IsUserAdmin()) {
+    return;
+  }
+
+  ApplicationUsageData data(true, true);
+
+  // create machine application key and test
+  CreateMachineDidRunValue(false);
+  ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data.exists(), true);
+  ASSERT_EQ(data.did_run(), false);
+}
+
+TEST_F(ApplicationUsageDataTest, ReadDwordDidRunMachine1) {
+  if (!vista_util::IsUserAdmin()) {
+    return;
+  }
+
+  ApplicationUsageData data(true, true);
+
+  // create machine application key and test
+  CreateMachineDidRunDwordValue(false);
+  ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data.exists(), true);
+  ASSERT_EQ(data.did_run(), false);
+}
+
+TEST_F(ApplicationUsageDataTest, ReadDidRunMachine2) {
+  if (!vista_util::IsUserAdmin()) {
+    return;
+  }
+
+  ApplicationUsageData data1(true, true);
+  CreateMachineDidRunValue(true);
+  ASSERT_SUCCEEDED(data1.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data1.exists(), true);
+  ASSERT_EQ(data1.did_run(), true);
+}
+
+TEST_F(ApplicationUsageDataTest, ReadDwordDidRunMachine2) {
+  if (!vista_util::IsUserAdmin()) {
+    return;
+  }
+
+  ApplicationUsageData data1(true, true);
+  CreateMachineDidRunDwordValue(true);
+  ASSERT_SUCCEEDED(data1.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data1.exists(), true);
+  ASSERT_EQ(data1.did_run(), true);
+}
+
+TEST_F(ApplicationUsageDataTest, ReadDidRunBoth1) {
+  if (!vista_util::IsUserAdmin()) {
+    return;
+  }
+
+  // We try all combinations of machine, user and low integrity user
+  // registry value for did run. -1 indicates the value does not exist
+  // 1 indicates true and 0 indicates false.
+  for (int vista = 0; vista < 2; ++vista) {
+    for (int machine = -1; machine < 2; ++machine) {
+      for (int user = -1; user < 2; ++user) {
+        for (int lowuser = -1; lowuser < 2; ++lowuser) {
+          bool expected_did_run = false;
+          bool expected_exists = false;
+
+          if (machine > -1 || user > -1 || (vista && lowuser > -1)) {
+            expected_exists = true;
+          }
+
+          if (machine > 0 || user > 0 || (vista && lowuser > 0)) {
+            expected_did_run = true;
+          }
+
+          TestUserAndMachineDidRun(machine, user, lowuser,
+                                   expected_exists,
+                                   expected_did_run,
+                                   vista);
+          TearDown();
+        }
+      }
+    }
+  }
+}
+
+TEST_F(ApplicationUsageDataTest, ResetDidRunUser1) {
+  ApplicationUsageData data(true, true);
+
+  // create user application key and test
+  CreateUserDidRunValue(false);
+  ASSERT_SUCCEEDED(data.ResetDidRun(kAppGuid));
+  CheckUserDidRunValue(false);
+
+  ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data.exists(), true);
+  ASSERT_EQ(data.did_run(), false);
+}
+
+TEST_F(ApplicationUsageDataTest, ResetDidRunUser2) {
+  ApplicationUsageData data1(true, true);
+  CreateUserDidRunValue(true);
+  ASSERT_SUCCEEDED(data1.ResetDidRun(kAppGuid));
+  CheckUserDidRunValue(false);
+
+  ASSERT_SUCCEEDED(data1.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data1.exists(), true);
+  ASSERT_EQ(data1.did_run(), false);
+}
+
+TEST_F(ApplicationUsageDataTest, ResetDidRunUser3) {
+  ApplicationUsageData data(true, true);
+
+  // create user application key and test
+  CreateUserDidRunDwordValue(false);
+  ASSERT_SUCCEEDED(data.ResetDidRun(kAppGuid));
+  CheckUserDidRunValue(false);
+
+  ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data.exists(), true);
+  ASSERT_EQ(data.did_run(), false);
+}
+
+TEST_F(ApplicationUsageDataTest, ResetDidRunUser4) {
+  ApplicationUsageData data1(true, true);
+  CreateUserDidRunDwordValue(true);
+  ASSERT_SUCCEEDED(data1.ResetDidRun(kAppGuid));
+  CheckUserDidRunValue(false);
+
+  ASSERT_SUCCEEDED(data1.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data1.exists(), true);
+  ASSERT_EQ(data1.did_run(), false);
+}
+
+TEST_F(ApplicationUsageDataTest, ResetDidRunMachine1) {
+  if (!vista_util::IsUserAdmin()) {
+    return;
+  }
+
+  ApplicationUsageData data(true, true);
+  CreateMachineDidRunValue(false);
+  ASSERT_SUCCEEDED(data.ResetDidRun(kAppGuid));
+  CheckMachineDidRunValue(false);
+
+  ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data.exists(), true);
+  ASSERT_EQ(data.did_run(), false);
+}
+
+TEST_F(ApplicationUsageDataTest, ResetDidRunMachine2) {
+  if (!vista_util::IsUserAdmin()) {
+    return;
+  }
+
+  ApplicationUsageData data1(true, true);
+  CreateMachineDidRunValue(true);
+  ASSERT_SUCCEEDED(data1.ResetDidRun(kAppGuid));
+  CheckMachineDidRunValue(false);
+
+  ASSERT_SUCCEEDED(data1.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data1.exists(), true);
+  ASSERT_EQ(data1.did_run(), false);
+}
+
+TEST_F(ApplicationUsageDataTest, ResetDidRunMachine3) {
+  if (!vista_util::IsUserAdmin()) {
+    return;
+  }
+
+  ApplicationUsageData data(true, true);
+  CreateMachineDidRunDwordValue(false);
+  ASSERT_SUCCEEDED(data.ResetDidRun(kAppGuid));
+  CheckMachineDidRunValue(false);
+
+  ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data.exists(), true);
+  ASSERT_EQ(data.did_run(), false);
+}
+
+TEST_F(ApplicationUsageDataTest, ResetDidRunMachine4) {
+  if (!vista_util::IsUserAdmin()) {
+    return;
+  }
+
+  ApplicationUsageData data1(true, true);
+  CreateMachineDidRunDwordValue(true);
+  ASSERT_SUCCEEDED(data1.ResetDidRun(kAppGuid));
+  CheckMachineDidRunValue(false);
+
+  ASSERT_SUCCEEDED(data1.ReadDidRun(kAppGuid));
+  ASSERT_EQ(data1.exists(), true);
+  ASSERT_EQ(data1.did_run(), false);
+}
+
+TEST_F(ApplicationUsageDataTest, ResetDidRunBoth) {
+  if (!vista_util::IsUserAdmin()) {
+    return;
+  }
+
+  // We try all combinations of machine, user and low integrity user
+  // registry value for did run. -1 indicates the value does not exist
+  // 1 indicates true and 0 indicates false.
+  for (int vista = 0; vista < 2; ++vista) {
+    for (int machine = -1; machine < 2; ++machine) {
+      for (int user = -1; user < 2; ++user) {
+        for (int lowuser = -1; lowuser < 2; ++lowuser) {
+          bool expected_exists = false;
+          if (machine > -1 || user > -1 || (vista && lowuser > -1)) {
+            expected_exists = true;
+          }
+
+          TestUserAndMachineDidRunPostProcess(machine, user, lowuser,
+                                              expected_exists,
+                                              vista);
+          TearDown();
+        }
+      }
+    }
+  }
+}
+
+TEST_F(ApplicationUsageDataTest, UserReadDidRunUser) {
+  for (int vista = 0; vista < 2; ++vista) {
+      for (int user = -1; user < 2; ++user) {
+        for (int lowuser = -1; lowuser < 2; ++lowuser) {
+          bool expected_exists = false;
+          bool expected_did_run = false;
+
+          if (user != -1 || (vista && lowuser != -1)) {
+            expected_exists = true;
+          }
+
+          if (user > 0 || (vista && lowuser > 0)) {
+            expected_did_run = true;
+          }
+
+          UserTestDidRunPreProcess(user, lowuser, vista, expected_exists,
+                                   expected_did_run);
+          TearDown();
+        }
+      }
+  }
+}
+
+TEST_F(ApplicationUsageDataTest, UserResetDidRunUser1) {
+  for (int vista = 0; vista < 2; ++vista) {
+    for (int user = -1; user < 2; ++user) {
+      for (int lowuser = -1; lowuser < 2; ++lowuser) {
+        UserTestDidRunPostProcess(user, lowuser, vista);
+        TearDown();
+      }
+    }
+  }
+}
+
+}  // namespace omaha
diff --git a/goopdate/broker_class_factory.h b/goopdate/broker_class_factory.h
new file mode 100644
index 0000000..4d1776a
--- /dev/null
+++ b/goopdate/broker_class_factory.h
@@ -0,0 +1,162 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_BROKER_CLASS_FACTORY_H_
+#define OMAHA_GOOPDATE_BROKER_CLASS_FACTORY_H_
+
+#include <atlcom.h>
+#include "base/basictypes.h"
+#include "omaha/base/atlregmapex.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/system.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/goopdate/elevation_moniker_resource.h"
+
+namespace omaha {
+
+// This class factory delegates CoCreation to the template "clsid" and returns
+// the resulting interface to the caller. If unable to CoCreate "clsid",
+// BrokerClassFactory attempts to CoCreateAsAdmin and return "clsid2" to the
+// caller.
+template <const CLSID& clsid, const CLSID& clsid2>
+class BrokerClassFactory : public CComClassFactory {
+ public:
+  BrokerClassFactory() {}
+
+  virtual ~BrokerClassFactory() {}
+
+  STDMETHOD(CreateInstance)(LPUNKNOWN outer_unk, REFIID riid, void** instance) {
+    CORE_LOG(L3, (_T("[BrokerClassFactory CreateInstance][%s]"),
+                  GuidToString(riid)));
+
+    // The LockServer combo is used to pulse the module count, which will
+    // shutdown the server after this CreateInstance() request completes,
+    // provided there are no other outstanding interface references being held
+    // by clients.
+    LockServer(TRUE);
+    ON_SCOPE_EXIT_OBJ(*this, &IClassFactory::LockServer, FALSE);
+
+    if (!instance) {
+      return E_POINTER;
+    }
+
+    *instance = NULL;
+
+    if (outer_unk) {
+      return CLASS_E_NOAGGREGATION;
+    }
+
+    HRESULT hr = ::CoCreateInstance(clsid,
+                                    outer_unk,
+                                    CLSCTX_ALL,
+                                    riid,
+                                    instance);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[Create failed][%s][0x%x]"), GuidToString(clsid), hr));
+
+      if (!vista_util::IsVistaOrLater() && !vista_util::IsUserAdmin()) {
+        return hr;
+      }
+
+      hr = System::CoCreateInstanceAsAdmin(NULL, clsid2, riid, instance);
+      if (FAILED(hr)) {
+        CORE_LOG(LE, (_T("[Create fail][%s][0x%x]"), GuidToString(clsid2), hr));
+        return hr;
+      }
+    }
+
+    return S_OK;
+  }
+};
+
+#pragma warning(push)
+// Construction of local static object is not thread-safe
+#pragma warning(disable:4640)
+
+// This class is used for COM registration and class factory registration and
+// instantiation of the delegate brokers. The class itself is not
+// instantiated. target_clsid is the CLSID that BrokerClassFactory
+// CoCreates and returns to the caller. If unable to CoCreate target_clsid,
+// BrokerClassFactory attempts to CoCreateAsAdmin target_clsid2. broker_clsid is
+// the CLSID that clients of the broker CoCreate.
+template <const CLSID& target_clsid, const CLSID& target_clsid2,
+          const CLSID& broker_clsid, const TCHAR* const broker_progid>
+class ATL_NO_VTABLE BrokerClassFactoryRegistrar
+    : public CComObjectRootEx<CComObjectThreadModel>,
+      public CComCoClass<BrokerClassFactoryRegistrar<target_clsid,
+                                                     target_clsid2,
+                                                     broker_clsid,
+                                                     broker_progid> > {
+ public:
+  BrokerClassFactoryRegistrar() {
+    ASSERT1(false);
+  }
+
+  typedef BrokerClassFactory<target_clsid, target_clsid2> BrokerClassFactoryT;
+
+  DECLARE_CLASSFACTORY_EX(BrokerClassFactoryT);
+  DECLARE_NOT_AGGREGATABLE(BrokerClassFactoryRegistrar);
+  DECLARE_REGISTRY_RESOURCEID_EX(IDR_LOCAL_SERVER_ELEVATION_RGS)
+
+  BEGIN_REGISTRY_MAP()
+    REGMAP_ENTRY(_T("PROGID"), broker_progid)
+    REGMAP_ENTRY(_T("VERSION"), _T("1.0"))
+    REGMAP_ENTRY(L"DESCRIPTION", L"Google Update Broker Class Factory")
+    REGMAP_ENTRY(L"CLSID", broker_clsid)
+    REGMAP_ENTRY(L"ICONRESID", PP_STRINGIZE(IDI_ELEVATION_MONIKER_ICON))
+    REGMAP_ENTRY(L"STRINGRESID",
+                 PP_STRINGIZE(IDS_ELEVATION_MONIKER_DISPLAYNAME))
+    REGMAP_MODULE2(L"MODULE", kOmahaBrokerFileName)
+  END_REGISTRY_MAP()
+
+  BEGIN_COM_MAP(BrokerClassFactoryRegistrar)
+  END_COM_MAP()
+
+ protected:
+  virtual ~BrokerClassFactoryRegistrar() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BrokerClassFactoryRegistrar);
+};
+
+#pragma warning(pop)
+
+extern TCHAR kOnDemandMachineBrokerProgId[];
+extern TCHAR kUpdate3WebMachineBrokerProgId[];
+
+// An OnDemand client CoCreates OnDemandMachineAppsClass, which
+// instantiates the class factory for the OnDemandMachineBroker typedef below.
+// The class factory in turn passes the CreateInstance through to
+// OnDemandMachineAppsServiceClass.
+typedef BrokerClassFactoryRegistrar<__uuidof(OnDemandMachineAppsServiceClass),
+                                    __uuidof(OnDemandMachineAppsFallbackClass),
+                                    __uuidof(OnDemandMachineAppsClass),
+                                    kOnDemandMachineBrokerProgId>
+                                    OnDemandMachineBroker;
+
+// The Pack web plugin client CoCreates GoogleUpdate3WebMachineClass, which
+// instantiates the class factory for the Update3WebBroker typedef below. The
+// class factory in turn passes the CreateInstance through to
+// GoogleUpdate3WebServiceClass.
+typedef BrokerClassFactoryRegistrar<
+    __uuidof(GoogleUpdate3WebServiceClass),
+    __uuidof(GoogleUpdate3WebMachineFallbackClass),
+    __uuidof(GoogleUpdate3WebMachineClass),
+    kUpdate3WebMachineBrokerProgId>
+    Update3WebMachineBroker;
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_BROKER_CLASS_FACTORY_H_
diff --git a/goopdate/broker_idl_datax.c b/goopdate/broker_idl_datax.c
new file mode 100644
index 0000000..a1af835
--- /dev/null
+++ b/goopdate/broker_idl_datax.c
@@ -0,0 +1,14 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
diff --git a/goopdate/browser_launcher.cc b/goopdate/browser_launcher.cc
deleted file mode 100644
index 480b5ba..0000000
--- a/goopdate/browser_launcher.cc
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include "omaha/goopdate/browser_launcher.h"
-
-#include "omaha/common/browser_utils.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/exception_barrier.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/system.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/core/google_update_core.h"
-#include "omaha/goopdate/google_update_proxy.h"
-
-namespace omaha {
-
-ProcessLauncher::ProcessLauncher() {}
-
-ProcessLauncher::~ProcessLauncher() {}
-
-STDMETHODIMP ProcessLauncher::LaunchCmdLine(const TCHAR* cmd_line) {
-  CORE_LOG(L1, (_T("[ProcessLauncher::LaunchCmdLine][%s]"), cmd_line));
-  // The exception barrier is needed, because any exceptions that are thrown
-  // in this method will get caught by the COM run time. We compile with
-  // exceptions off, and do not expect to throw any exceptions. This barrier
-  // will treat an exception in this method as a unhandled exception.
-  ExceptionBarrier barrier;
-  if (cmd_line == NULL) {
-    return E_INVALIDARG;
-  }
-  return System::ShellExecuteCommandLine(cmd_line, NULL, NULL);
-}
-
-STDMETHODIMP ProcessLauncher::LaunchBrowser(DWORD type, const TCHAR* url) {
-  CORE_LOG(L1, (_T("[ProcessLauncher::LaunchBrowser][%d][%s]"), type, url));
-  // The exception barrier is needed, because any exceptions that are thrown
-  // in this method will get caught by the COM run time. We compile with
-  // exceptions off, and do not expect to throw any exceptions. This barrier
-  // will treat an exception in this method as a unhandled exception.
-  ExceptionBarrier barrier;
-  if (type >= BROWSER_MAX || url == NULL) {
-    return E_INVALIDARG;
-  }
-  return ShellExecuteBrowser(static_cast<BrowserType>(type), url);
-}
-
-// This method delegates to the internal interface exposed by the system
-// service, and if the service cannot be installed, exposed by the core.
-// When starting, if the service is not installed, the core registers a proxy
-// for its interface in shared memory.
-//
-// Non elevated callers can request a command to be run elevated.
-// The command must be registered before by elevated code to prevent
-// launching untrusted commands. The security of the command is based on
-// having the correct registry ACLs for the machine Omaha registry.
-STDMETHODIMP ProcessLauncher::LaunchCmdElevated(const WCHAR* app_guid,
-                                                const WCHAR* cmd_id,
-                                                DWORD caller_proc_id,
-                                                ULONG_PTR* proc_handle) {
-  CORE_LOG(L3, (_T("[ProcessLauncher::LaunchCmdElevated]")
-                _T("[app %s][cmd %s][pid %d]"),
-                app_guid, cmd_id, caller_proc_id));
-
-  ExceptionBarrier barrier;
-
-  ASSERT1(app_guid);
-  ASSERT1(cmd_id);
-  ASSERT1(proc_handle);
-
-  CComPtr<IGoogleUpdateCore> google_update_core;
-  HRESULT hr =
-      google_update_core.CoCreateInstance(__uuidof(GoogleUpdateCoreClass));
-
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[CoCreate GoogleUpdateCoreClass failed][0x%x]"), hr));
-
-    SharedMemoryAttributes attr(kGoogleUpdateCoreSharedMemoryName,
-                                CSecurityDesc());
-    GoogleUpdateCoreProxy google_update_core_proxy(true, &attr);
-    hr = google_update_core_proxy.GetObject(&google_update_core);
-    if (FAILED(hr)) {
-      CORE_LOG(LE, (_T("[GetObject for IGoogleUpdateCore failed][0x%x]"), hr));
-      return hr;
-    }
-  }
-  if (!google_update_core) {
-    CORE_LOG(LE, (_T("[IGoogleUpdateCore is null]")));
-    return E_UNEXPECTED;
-  }
-  hr = ::CoSetProxyBlanket(google_update_core, RPC_C_AUTHN_DEFAULT,
-      RPC_C_AUTHZ_DEFAULT, COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_DEFAULT,
-      RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_DEFAULT);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return google_update_core->LaunchCmdElevated(app_guid,
-                                               cmd_id,
-                                               caller_proc_id,
-                                               proc_handle);
-}
-
-}  // namespace omaha
-
diff --git a/goopdate/browser_launcher.h b/goopdate/browser_launcher.h
deleted file mode 100644
index ae1bce6..0000000
--- a/goopdate/browser_launcher.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Contains ProcessLauncher class to launch a process using a COM interface. The
-// COM object is typically created by the caller as a medium integrity object
-// impersonating the identity of the active user. This is to allow for launching
-// a medium-integrity process from a high-integrity process.
-
-// TODO(omaha): consider renaming of the source files to match their purpose.
-
-#ifndef OMAHA_GOOPDATE_BROWSER_LAUNCHER_H__
-#define OMAHA_GOOPDATE_BROWSER_LAUNCHER_H__
-
-#include <windows.h>
-#include <atlbase.h>
-#include <atlcom.h>
-#include <atlstr.h>
-#include "omaha/common/atlregmapex.h"
-#include "omaha/common/const_cmd_line.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/resource.h"
-
-// Generated by MIDL in the "BUILD_MODE.OBJ_ROOT + SETTINGS.SUBDIR".
-#include "goopdate/google_update_idl.h"
-
-namespace omaha {
-
-const TCHAR* const kThreadingBoth = _T("Both");
-const TCHAR* const kTlbVersion    = _T("1.0");
-const TCHAR* const kProcessWorkerProgId =
-    _T("GoogleUpdateProcessLauncher");
-const TCHAR* const kProcessWorkerDescription =
-    _T("Google Update Process Launcher Class");
-
-class ATL_NO_VTABLE ProcessLauncher
-    : public CComObjectRootEx<CComMultiThreadModel>,
-      public CComCoClass<ProcessLauncher, &__uuidof(ProcessLauncherClass)>,
-      public IProcessLauncher {
- public:
-  ProcessLauncher();
-  virtual ~ProcessLauncher();
-
-  DECLARE_NOT_AGGREGATABLE(ProcessLauncher)
-  DECLARE_PROTECT_FINAL_CONSTRUCT()
-
-  DECLARE_REGISTRY_RESOURCEID_EX(IDR_GOOGLE_UPDATE_WORKER_CLASS)
-
-  #pragma warning(push)
-  // C4640: construction of local static object is not thread-safe
-  #pragma warning(disable : 4640)
-  BEGIN_REGISTRY_MAP()
-    REGMAP_ENTRY(_T("HKROOT"),       goopdate_utils::GetHKRoot())
-    REGMAP_ENTRY(_T("THREADING"),    kThreadingBoth)
-    REGMAP_EXE_MODULE(_T("MODULE"))
-    REGMAP_ENTRY(_T("VERSION"),      kTlbVersion)
-    REGMAP_ENTRY(_T("PROGID"),       kProcessWorkerProgId)
-    REGMAP_ENTRY(_T("DESCRIPTION"),  kProcessWorkerDescription)
-    REGMAP_UUID(_T("CLSID"),         __uuidof(ProcessLauncherClass))
-    REGMAP_UUID(_T("LIBID"),         LIBID_GoogleUpdateLib)
-  END_REGISTRY_MAP()
-  #pragma warning(pop)
-
-  // C4505: unreferenced IUnknown local functions have been removed
-  #pragma warning(disable : 4505)
-  BEGIN_COM_MAP(ProcessLauncher)
-    COM_INTERFACE_ENTRY(IProcessLauncher)
-  END_COM_MAP()
-
-  // Launches a command line at medium integrity.
-  STDMETHOD(LaunchCmdLine)(const TCHAR* cmd_line);
-
-  // Launches the appropriate browser.
-  STDMETHOD(LaunchBrowser)(DWORD type, const TCHAR* url);
-
-  // Launches a command line elevated.
-  STDMETHOD(LaunchCmdElevated)(const WCHAR* app_guid,
-                               const WCHAR* cmd_id,
-                               DWORD caller_proc_id,
-                               ULONG_PTR* proc_handle);
-
- private:
-  DISALLOW_EVIL_CONSTRUCTORS(ProcessLauncher);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_BROWSER_LAUNCHER_H__
-
diff --git a/goopdate/build.scons b/goopdate/build.scons
index 803e071..33f2d8f 100644
--- a/goopdate/build.scons
+++ b/goopdate/build.scons
@@ -1,6 +1,6 @@
 #!/usr/bin/python2.4
 #
-# Copyright 2009 Google Inc.
+# Copyright 2009-2010 Google Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -20,9 +20,56 @@
 
 Import('env')
 
+def BuildCOMForwarder(cmd_line_switch,
+                      signed_exe_name):
+  com_forwarder_env = env.Clone()
+
+  com_forwarder_env.FilterOut(CCFLAGS=['/GL', '/RTC1', '/GS'])
+  com_forwarder_env.FilterOut(LINKFLAGS=['/LTCG'])
+
+  com_forwarder_env.Append(
+      CCFLAGS=[
+          '/GS-',
+          '/Zl',
+      ],
+      CPPDEFINES = [
+          'CMD_LINE_SWITCH=_T(\\"%s\\")' % cmd_line_switch,
+      ],
+      LINKFLAGS=[
+          '/ENTRY:WinMainCRTStartup',
+      ],
+      LIBS=[
+          'libcmt.lib',
+          'shlwapi.lib',
+      ],
+  )
+  # The resource file used is the same as the one for GoogleUpdate.exe.
+  # The version resource used is the same as the one for goopdate.dll.
+  com_forwarder_inputs = [
+       com_forwarder_env.ComponentObject('%s.obj' % signed_exe_name,
+                                         'com_forwarder.cc'),
+       com_forwarder_env.RES('%s.res' % signed_exe_name,
+                             '../google_update/resource.rc'),
+       '$OBJ_ROOT/goopdate/goopdate_version.res',
+  ]
+
+  unsigned_broker = com_forwarder_env.ComponentProgram(
+      prog_name='%s_unsigned' % signed_exe_name,
+      source=com_forwarder_inputs,
+  )
+  signed_broker = com_forwarder_env.SignedBinary(
+      target='%s.exe' % signed_exe_name,
+      source=unsigned_broker,
+  )
+  env.Replicate('$STAGING_DIR', signed_broker)
+
+
+# Build the broker forwarder and legacy on-demand.
+BuildCOMForwarder('/broker', 'GoogleUpdateBroker')
+BuildCOMForwarder('/ondemand', 'GoogleUpdateOnDemand')
 
 #
-# Build Goopdate library
+# Build COM libraries and headers.
 #
 midl_env = env.Clone()
 midl_env.Tool('midl')
@@ -30,44 +77,195 @@
     '/Oicf',  # generate optimized stubless proxy/stub code
     ]
 
-# Compile the .idl file into .c & .h files
-midl_env.TypeLibrary('google_update_idl.idl')
+#
+# Generate omaha3_idl.idl. The output is an IDL file with a variant CLSID
+# for coclass GoogleComProxyMachineClass and GoogleComProxyUserClass.
+#
+generated_idl = env.Command(
+    target='omaha3_idl.idl',
+    source='$MAIN_DIR/goopdate/omaha3_idl.idl',
+    action=('python %s/tools/generate_omaha3_idl.py --idl_template_file '
+        '$SOURCE --idl_output_file $TARGET' % env['MAIN_DIR'])
+)
 
+# Compile the .idl file into .tlb, .c & .h files
+midl_input = 'omaha3_idl.idl'
+midl_outputs = midl_env.TypeLibrary(generated_idl)
+
+# Save the .idl and the produced .tlb and .h files so we can provide
+# them to clients.
+env.Replicate('$STAGING_DIR/idls', midl_input)
+for node in midl_outputs:
+  if not str(node).endswith('.c'):
+    env.Replicate('$STAGING_DIR/idls', node)
+
+midl_env.ComponentLibrary(
+    lib_name='omaha3_idl',
+    source='omaha3_idl_i.c',
+)
+
+handler_common_env = env.Clone()
+handler_common_env.Append(
+    CPPDEFINES = [
+        '_ATL_FREE_THREADED',
+    ],
+    CPPPATH = [
+        '$OBJ_ROOT',  # Needed for generated files.
+    ],
+    LIBS = [
+        '$LIB_DIR/base.lib',
+        '$LIB_DIR/goopdate_lib.lib',
+        ('atls.lib', 'atlsd.lib')[env.Bit('debug')],
+        ('libcmt.lib', 'libcmtd.lib')[env.Bit('debug')],
+        ('libcpmt.lib', 'libcpmtd.lib')[env.Bit('debug')],
+        'psapi.lib',
+        'netapi32.lib',
+        'rasapi32.lib',
+        'shlwapi.lib',
+        'userenv.lib',
+        'version.lib',
+        'wtsapi32.lib',
+    ],
+)
+
+def BuildGoogleUpdateHandlerDll(omaha_version_info, is_machine_handler, psname):
+  version_string = omaha_version_info.GetVersionString()
+  prefix = omaha_version_info.filename_prefix
+  handler_env = handler_common_env.Clone(COMPONENT_STATIC = False)
+
+  if prefix == 'TEST_':
+    handler_env['OBJPREFIX'] = handler_env.subst('test/$OBJPREFIX')
+  elif prefix:
+    raise Exception('ERROR: Unrecognized prefix "%s"' % prefix)
+
+  handler_env.Append(
+      CPPDEFINES = [
+          'IS_MACHINE_HANDLER=%d' % is_machine_handler,
+      ],
+      LIBS = [
+          '$LIB_DIR/common.lib',
+          'wininet.lib',
+          'rpcrt4.lib',
+      ],
+      RCFLAGS = [
+          '/DVERSION_MAJOR=%d' % omaha_version_info.version_major,
+          '/DVERSION_MINOR=%d' % omaha_version_info.version_minor,
+          '/DVERSION_BUILD=%d' % omaha_version_info.version_build,
+          '/DVERSION_PATCH=%d' % omaha_version_info.version_patch,
+          '/DVERSION_NUMBER_STRING=\\"%s\\"' % version_string,
+      ],
+  )
+
+  resource = handler_env.RES(target='%s%s_resource.res' % (prefix, psname),
+                             source='google_update_ps_resource.rc')
+
+  handler_env.Depends(
+      resource,
+      ['$MAIN_DIR/VERSION',
+       '$MAIN_DIR/base/generic_reg_file_dll_handler.rgs'])
+
+  target_name = '%s%s_unsigned' % (prefix, psname)
+
+  inputs = [
+      'google_update_ps.def',
+      resource,
+      prefix + 'goopdate_version.res',
+      ]
+  inputs += handler_env.Object('google_update_ps_%s.obj' % psname,
+                               '$OBJ_ROOT/goopdate/google_update_ps.cc')
+  inputs += handler_env.Object('omaha3_idl_datax_%s.obj' % psname,
+                               '$OBJ_ROOT/goopdate/omaha3_idl_datax.c')
+
+  unsigned_dll = handler_env.ComponentLibrary(
+      lib_name=target_name,
+      source=inputs,
+  )
+
+  signed_dll = handler_env.SignedBinary(
+      target='%s%s.dll' % (prefix, psname),
+      source=unsigned_dll,
+  )
+
+  env.Replicate('$STAGING_DIR', signed_dll)
+  env.Replicate('$STAGING_DIR', [f for f in unsigned_dll if f.suffix == '.pdb'])
+
+
+for omaha_version_info in env['omaha_versions_info']:
+  BuildGoogleUpdateHandlerDll(omaha_version_info, 1, 'psmachine')
+  BuildGoogleUpdateHandlerDll(omaha_version_info, 0, 'psuser')
 
 gd_env = env.Clone()
 
+# TODO(omaha3): Is it okay that other libs, such as common, do not define this.
+gd_env['CPPDEFINES'] += [
+    '_ATL_FREE_THREADED',
+    ]
+
 # Need to look in output dir to find .h files generated by midl compiler.
 gd_env['CPPPATH'] += [
-    '$OBJ_ROOT',
+    '$OBJ_ROOT',  # Needed for generated files.
     '$MAIN_DIR/third_party/breakpad/src/',
     ]
 
-target_name = 'goopdate_dll.lib'
+target_name = 'goopdate_lib'
 
 gd_inputs = [
-    #'browser_ping.cc',
-    'browser_launcher.cc',
-    'command_line.cc',
-    'command_line_builder.cc',
-    'command_line_parser.cc',
-    'command_line_validator.cc',
-    'config_manager.cc',
+    'app.cc',
+    'app_bundle.cc',
+    'app_bundle_state.cc',
+    'app_bundle_state_busy.cc',
+    'app_bundle_state_init.cc',
+    'app_bundle_state_initialized.cc',
+    'app_bundle_state_paused.cc',
+    'app_bundle_state_ready.cc',
+    'app_bundle_state_stopped.cc',
+    'app_command.cc',
+    'app_manager.cc',
+    'app_state.cc',
+    'app_state_error.cc',
+    'app_state_init.cc',
+    'app_state_checking_for_update.cc',
+    'app_state_download_complete.cc',
+    'app_state_downloading.cc',
+    'app_state_install_complete.cc',
+    'app_state_installing.cc',
+    'app_state_no_update.cc',
+    'app_state_ready_to_install.cc',
+    'app_state_update_available.cc',
+    'app_state_waiting_to_check_for_update.cc',
+    'app_state_waiting_to_download.cc',
+    'app_state_waiting_to_install.cc',
+    'app_version.cc',
+    'application_usage_data.cc',
+    'code_red_check.cc',
     'crash.cc',
-    'extra_args_parser.cc',
-    'event_logger.cc',
+    'cocreate_async.cc',
+    'cred_dialog.cc',
+    'current_state.cc',
+    'download_complete_ping_event.cc',
+    'download_manager.cc',
     'google_update.cc',
     'goopdate.cc',
-    'goopdate_command_line_validator.cc',
-    'goopdate_helper.cc',
     'goopdate_metrics.cc',
-    'goopdate_utils.cc',
-    'goopdate_xml_parser.cc',
-    'program_instance.cc',
-    'request.cc',
-    'stats_uploader.cc',
+    'install_manager.cc',
+    'installer_wrapper.cc',
+    'job_observer.cc',
+    'model.cc',
+    'model_object.cc',
+    'ondemand.cc',
+    'oneclick_process_launcher.cc',
+    'offline_utils.cc',
+    'string_formatter.cc',
+    'package.cc',
+    'package_cache.cc',
+    'process_launcher.cc',
     'resource_manager.cc',
-    'ui_displayed_event.cc',
-    'webplugin_utils.cc',
+    'update3web.cc',
+    'update_request_utils.cc',
+    'update_response_utils.cc',
+    'worker.cc',
+    'worker_utils.cc',
+    'worker_metrics.cc',
     ]
 if env.Bit('use_precompiled_headers'):
   gd_inputs += gd_env.EnablePrecompile(target_name)
@@ -77,15 +275,6 @@
 
 
 #
-# Build Goopdate proxy/stub library separately, because it is not compatible
-# with precompiled headers.
-#
-no_precomp_env = env.Clone()
-no_precomp_env['CPPPATH'] += ['$OBJ_ROOT']
-no_precomp_env.ComponentLibrary('google_update_ps', 'google_update_idl_datax.c')
-
-
-#
 # Build Goopdate DLL
 #
 for omaha_version_info in env['omaha_versions_info']:
@@ -105,43 +294,48 @@
   temp_env.Append(
       CPPPATH = [
           '$MAIN_DIR/third_party/breakpad/src/',
+          '$OBJ_ROOT',
           ],
 
       # Do not add static dependencies on system import libraries. Prefer delay
-      # loading when possible. When running as 'core', only what is necessary
-      # must be loaded in the memory space.
+      # loading when possible. Only what is necessary must be loaded in the
+      # memory space when long-running.
       LIBS = [
+          '$LIB_DIR/base.lib',
           '$LIB_DIR/breakpad.lib',
+          '$LIB_DIR/client.lib',
           '$LIB_DIR/common.lib',
           '$LIB_DIR/core.lib',
-          '$LIB_DIR/goopdate_dll.lib',
-          '$LIB_DIR/google_update_ps.lib',
           '$LIB_DIR/google_update_recovery.lib',
+          '$LIB_DIR/goopdate_lib.lib',
           '$LIB_DIR/logging.lib',
           '$LIB_DIR/net.lib',
-          '$LIB_DIR/repair_goopdate.lib',
+          '$LIB_DIR/omaha3_idl.lib',
           '$LIB_DIR/security.lib',
           '$LIB_DIR/service.lib',
           '$LIB_DIR/setup.lib',
           '$LIB_DIR/statsreport.lib',
-          '$LIB_DIR/worker.lib',
+          '$LIB_DIR/ui.lib',
           ('atls.lib', 'atlsd.lib')[temp_env.Bit('debug')],
           ('libcmt.lib', 'libcmtd.lib')[temp_env.Bit('debug')],
           ('libcpmt.lib', 'libcpmtd.lib')[temp_env.Bit('debug')],
+          # TODO(omaha3): This must be linked in because we have UI in the DLL.
+          'bits.lib',
           'comctl32.lib',
           'crypt32.lib',
           'delayimp.lib',
           'iphlpapi.lib',
-          'netapi32.lib',
           'msi.lib',
+          'msimg32.lib',
           'mstask.lib',
+          'netapi32.lib',
           'psapi.lib',
           'rasapi32.lib',
           'rpcns4.lib',
           'rpcrt4.lib',
           'shlwapi.lib',
+          'taskschd.lib',
           'version.lib',
-          'urlmon.lib',
           'userenv.lib',
           'wininet.lib',
           'wintrust.lib',
@@ -149,20 +343,13 @@
           'wtsapi32.lib',
           ],
       LINKFLAGS = [
-          '/DELAYLOAD:comctl32.dll',
-          '/DELAYLOAD:crypt32.dll',
-          '/DELAYLOAD:iphlpapi.dll',
-          '/DELAYLOAD:msi.dll',
           '/DELAYLOAD:oleaut32.dll',
           '/DELAYLOAD:psapi.dll',
           '/DELAYLOAD:rasapi32.dll',
           '/DELAYLOAD:shell32.dll',
           '/DELAYLOAD:shlwapi.dll',
-          '/DELAYLOAD:urlmon.dll',
           '/DELAYLOAD:userenv.dll',
           '/DELAYLOAD:version.dll',
-          '/DELAYLOAD:wininet.dll',
-          '/DELAYLOAD:wintrust.dll',
           '/DELAYLOAD:wtsapi32.dll',
 
           # Forces the dependency on ws2_32.lib.
@@ -182,10 +369,18 @@
               omaha_version_info.GetVersionString()),
 
           # goopdate.dll is resource neutral.
-          '/DLANGAUGE_STRING=\\"en\\"',
+          '/DLANGUAGE_STRING=\\"en\\"',
           ],
   )
 
+  resource_res = temp_env.RES(
+      target=prefix + 'goopdate.res',
+      source='goopdate.rc',
+  )
+
+  # Force a rebuild when the .tlb changes.
+  temp_env.Depends(resource_res, '$OBJ_ROOT/goopdate/omaha3_idl.tlb')
+
   version_res = temp_env.RES(
       target=prefix + 'goopdate_version.res',
       source='goopdate_version.rc'
@@ -194,7 +389,7 @@
   # Force a rebuild when the version changes.
   env.Depends(version_res, '$MAIN_DIR/VERSION')
 
-  target_name = prefix + 'goopdate_unsigned.dll'
+  target_name = prefix + 'goopdate_unsigned'
 
   # main.cc is included here because the linker gets confused if we try to
   # create a DLL without an entry point. There's probably a more accurate
@@ -202,7 +397,7 @@
   inputs = [
       'goopdate.def',
       'main.cc',
-      temp_env.RES(prefix + 'goopdate.res', 'goopdate.rc'),
+      resource_res,
       version_res,
       ]
   if env.Bit('use_precompiled_headers'):
@@ -229,5 +424,30 @@
   env.Replicate('$STAGING_DIR', [f for f in unsigned_dll if f.suffix == '.pdb'])
 
 
+customization_test_env = env.Clone()
+
+customization_test_env.Append(
+    LIBS = [
+        '$LIB_DIR/common.lib',
+    ],
+)
+
+customization_test_env['CPPPATH'] += [
+    '$OBJ_ROOT',  # Needed for generated files.
+    ]
+customization_test = customization_test_env.OmahaUnittest(
+    name='omaha_customization_goopdate_apis_unittest',
+    source=[
+        'omaha_customization_goopdate_apis_unittest.cc',
+        'omaha3_idl_i.obj',  # Needed for LIBID_*.
+    ],
+    all_in_one=False,
+    COMPONENT_TEST_SIZE='small',
+)
+
+# The test uses the DLL for its TypeLib.
+customization_test_env.Depends(customization_test, '$STAGING_DIR/goopdate.dll')
+
+
 # Build all the resource dlls.
 env.BuildSConscript('resources')
diff --git a/goopdate/cocreate_async.cc b/goopdate/cocreate_async.cc
new file mode 100644
index 0000000..31ba97e
--- /dev/null
+++ b/goopdate/cocreate_async.cc
@@ -0,0 +1,192 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/cocreate_async.h"
+#include "base/basictypes.h"
+#include "base/debug.h"
+#include "omaha/base/scope_guard.h"
+#include "base/scoped_ptr_address.h"
+#include "omaha/base/system.h"
+#include "omaha/base/thread_pool_callback.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/goopdate/goopdate.h"
+
+namespace omaha {
+
+CoCreateAsync::CoCreateAsync() : StdMarshalInfo(true) {
+}
+
+STDMETHODIMP CoCreateAsync::createOmahaMachineServerAsync(
+    BSTR origin_url,
+    BOOL create_elevated,
+    ICoCreateAsyncStatus** status) {
+  CORE_LOG(L3, (L"[CoCreateAsync::createOmahaMachineServerAsync][%s][%d]",
+                origin_url, create_elevated));
+  ASSERT1(status);
+  ASSERT1(origin_url && wcslen(origin_url));
+  *status = NULL;
+
+  if (create_elevated &&
+      !vista_util::IsVistaOrLater() && !vista_util::IsUserAdmin()) {
+    return E_ACCESSDENIED;
+  }
+
+  typedef CComObject<CoCreateAsyncStatus> ComObjectAsyncStatus;
+  scoped_ptr<ComObjectAsyncStatus> async_status;
+  HRESULT hr = ComObjectAsyncStatus::CreateInstance(address(async_status));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = async_status->CreateOmahaMachineServerAsync(origin_url, create_elevated);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = async_status->QueryInterface(status);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  async_status.release();
+  return S_OK;
+}
+
+CoCreateAsyncStatus::CoCreateAsyncStatus() : is_done_(false), hr_(E_PENDING) {
+}
+
+HRESULT CoCreateAsyncStatus::CreateOmahaMachineServerAsync(
+    BSTR origin_url,
+    BOOL create_elevated) {
+  // Create a thread pool work item for deferred execution of the CoCreate. The
+  // thread pool owns this call back object.
+  typedef ThreadPoolCallBack2<CoCreateAsyncStatus,
+                              const CString,
+                              BOOL> CallBack;
+  scoped_ptr<CallBack>
+      callback(new CallBack(this,
+                            &CoCreateAsyncStatus::CreateOmahaMachineServer,
+                            origin_url,
+                            create_elevated));
+  HRESULT hr = Goopdate::Instance().QueueUserWorkItem(callback.get(),
+                                                      WT_EXECUTELONGFUNCTION);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[QueueUserWorkItem failed][0x%x]"), hr));
+    return hr;
+  }
+
+  VERIFY1(thread_started_gate_.Wait(INFINITE));
+
+  callback.release();
+  return S_OK;
+}
+
+void CoCreateAsyncStatus::CreateOmahaMachineServer(const CString origin_url,
+                                                   BOOL create_elevated) {
+  CORE_LOG(L3, (_T("[CoCreateAsyncStatus::CreateOmahaMachineServer][%s][%d]"),
+                origin_url, create_elevated));
+  AddRef();
+  ON_SCOPE_EXIT_OBJ(*this, &CoCreateAsyncStatus::Release);
+
+  VERIFY1(thread_started_gate_.Open());
+
+  HRESULT hr = E_FAIL;
+  CComPtr<IDispatch> ptr;
+
+  // Since the values of hr and ptr are being modified after the scope guard,
+  // the variables are passed by reference instead of by values using ByRef.
+  ON_SCOPE_EXIT_OBJ(*this,
+                    &CoCreateAsyncStatus::SetCreateInstanceResults,
+                    ByRef(hr),
+                    ByRef(ptr));
+
+  scoped_co_init init_com_apt(COINIT_MULTITHREADED);
+  hr = init_com_apt.hresult();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[init_com_apt failed][0x%x]"), hr));
+    return;
+  }
+
+  CComPtr<IGoogleUpdate3WebSecurity> security;
+  REFCLSID clsid(__uuidof(GoogleUpdate3WebMachineClass));
+  hr = create_elevated ?
+      System::CoCreateInstanceAsAdmin(NULL, clsid, IID_PPV_ARGS(&security)) :
+      ::CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_PPV_ARGS(&security));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CoCreate failed][0x%x]"), hr));
+    return;
+  }
+
+  hr = security->setOriginURL(CComBSTR(origin_url));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[setOriginURL failed][0x%x]"), hr));
+    return;
+  }
+
+  hr = security.QueryInterface(&ptr);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[QueryInterface failed][0x%x]"), hr));
+    return;
+  }
+}
+
+void CoCreateAsyncStatus::SetCreateInstanceResults(
+    const HRESULT& hr,
+    const CComPtr<IDispatch>& ptr) {
+  CORE_LOG(L3, (_T("[SetCreateInstanceResults][0x%x][0x%p]"), hr, ptr));
+  Lock();
+  ON_SCOPE_EXIT_OBJ(*this, &CoCreateAsyncStatus::Unlock);
+
+  hr_ = hr;
+  ptr_ = ptr;
+  is_done_ = true;
+}
+
+// ICoCreateAsyncStatus.
+STDMETHODIMP CoCreateAsyncStatus::get_isDone(VARIANT_BOOL* is_done) {
+  Lock();
+  ON_SCOPE_EXIT_OBJ(*this, &CoCreateAsyncStatus::Unlock);
+
+  ASSERT1(is_done);
+
+  *is_done = is_done_ ? VARIANT_TRUE : VARIANT_FALSE;
+  CORE_LOG(L3, (_T("[get_isDone][%d]"), is_done_));
+  return S_OK;
+}
+
+STDMETHODIMP CoCreateAsyncStatus::get_completionHResult(LONG* hr) {
+  Lock();
+  ON_SCOPE_EXIT_OBJ(*this, &CoCreateAsyncStatus::Unlock);
+
+  ASSERT1(hr);
+
+  *hr = hr_;
+  CORE_LOG(L3, (_T("[get_completionHResult][0x%x]"), hr_));
+  return S_OK;
+}
+
+STDMETHODIMP CoCreateAsyncStatus::get_createdInstance(IDispatch** instance) {
+  Lock();
+  ON_SCOPE_EXIT_OBJ(*this, &CoCreateAsyncStatus::Unlock);
+
+  ASSERT1(instance);
+
+  ptr_.CopyTo(instance);
+  CORE_LOG(L3, (_T("[get_createdInstance][0x%p]"), *instance));
+  return S_OK;
+}
+
+}  // namespace omaha
+
diff --git a/goopdate/cocreate_async.h b/goopdate/cocreate_async.h
new file mode 100644
index 0000000..b59f19a
--- /dev/null
+++ b/goopdate/cocreate_async.h
@@ -0,0 +1,114 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_COCREATE_ASYNC_H_
+#define OMAHA_GOOPDATE_COCREATE_ASYNC_H_
+
+#include <atlbase.h>
+#include <oaidl.h>
+#include <oleauto.h>
+#include "goopdate/omaha3_idl.h"
+#include "omaha/base/atlregmapex.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/goopdate/com_proxy.h"
+#include "omaha/goopdate/non_localized_resource.h"
+
+namespace omaha {
+
+#pragma warning(push)
+// Construction of local static object is not thread-safe
+#pragma warning(disable:4640)
+
+class ATL_NO_VTABLE CoCreateAsync
+  : public CComObjectRootEx<CComObjectThreadModel>,
+    public CComCoClass<CoCreateAsync, &__uuidof(CoCreateAsyncClass)>,
+    public ICoCreateAsync,
+    public StdMarshalInfo {
+ public:
+  CoCreateAsync();
+
+  STDMETHOD(createOmahaMachineServerAsync)(BSTR origin_url,
+                                           BOOL create_elevated,
+                                           ICoCreateAsyncStatus** status);
+
+  DECLARE_NOT_AGGREGATABLE(CoCreateAsync);
+  DECLARE_REGISTRY_RESOURCEID_EX(IDR_LOCAL_SERVER_RGS)
+
+  BEGIN_REGISTRY_MAP()
+    REGMAP_ENTRY(_T("HKROOT"),       goopdate_utils::GetHKRoot())
+    REGMAP_MODULE2(_T("MODULE"),     kOmahaBrokerFileName)
+    REGMAP_ENTRY(_T("VERSION"),      _T("1.0"))
+    REGMAP_ENTRY(_T("PROGID"),       kProgIDCoCreateAsync)
+    REGMAP_ENTRY(_T("DESCRIPTION"),  _T("CoCreateAsync"))
+    REGMAP_UUID(_T("CLSID"),         GetObjectCLSID())
+  END_REGISTRY_MAP()
+
+  BEGIN_COM_MAP(CoCreateAsync)
+    COM_INTERFACE_ENTRY(ICoCreateAsync)
+    COM_INTERFACE_ENTRY(IStdMarshalInfo)
+  END_COM_MAP()
+
+ protected:
+  virtual ~CoCreateAsync() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CoCreateAsync);
+};
+
+class ATL_NO_VTABLE CoCreateAsyncStatus
+  : public CComObjectRootEx<CComObjectThreadModel>,
+    public IDispatchImpl<ICoCreateAsyncStatus,
+                         &__uuidof(ICoCreateAsyncStatus),
+                         &CAtlModule::m_libid,
+                         kMajorTypeLibVersion,
+                         kMinorTypeLibVersion> {
+ public:
+  CoCreateAsyncStatus();
+  HRESULT CreateOmahaMachineServerAsync(BSTR origin_url, BOOL create_elevated);
+
+  // ICoCreateAsyncStatus.
+  STDMETHOD(get_isDone)(VARIANT_BOOL* is_done);
+  STDMETHOD(get_completionHResult)(LONG* hr);
+  STDMETHOD(get_createdInstance)(IDispatch** instance);
+
+  BEGIN_COM_MAP(CoCreateAsyncStatus)
+    COM_INTERFACE_ENTRY(ICoCreateAsyncStatus)
+    COM_INTERFACE_ENTRY(IDispatch)
+  END_COM_MAP()
+
+ protected:
+  virtual ~CoCreateAsyncStatus() {}
+
+ private:
+  void CreateOmahaMachineServer(const CString origin_url, BOOL create_elevated);
+  void SetCreateInstanceResults(const HRESULT& hr,
+                                const CComPtr<IDispatch>& ptr);
+
+  Gate thread_started_gate_;
+
+  bool is_done_;
+  HRESULT hr_;
+  CComPtr<IDispatch> ptr_;
+
+  DISALLOW_COPY_AND_ASSIGN(CoCreateAsyncStatus);
+};
+
+#pragma warning(pop)
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_COCREATE_ASYNC_H_
diff --git a/goopdate/code_red_check.cc b/goopdate/code_red_check.cc
new file mode 100644
index 0000000..e591c6e
--- /dev/null
+++ b/goopdate/code_red_check.cc
@@ -0,0 +1,211 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/code_red_check.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/scoped_impersonation.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vista_utils.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/goopdate/goopdate_metrics.h"
+#include "omaha/net/network_request.h"
+#include "omaha/net/bits_request.h"
+#include "omaha/net/simple_request.h"
+#include "omaha/recovery/client/google_update_recovery.h"
+
+namespace omaha {
+
+namespace {
+
+bool IsThreadImpersonatingUser() {
+  CAccessToken access_token;
+  return access_token.GetThreadToken(TOKEN_READ);
+}
+
+HRESULT DownloadCodeRedFile(const TCHAR* url,
+                            const TCHAR* file_path,
+                            void*,
+                            int* http_status_code) {
+  ASSERT1(url);
+  ASSERT1(file_path);
+  ASSERT1(http_status_code);
+  *http_status_code = 0;
+  NetworkConfig* network_config = NULL;
+  NetworkConfigManager& network_manager = NetworkConfigManager::Instance();
+  HRESULT hr = network_manager.GetUserNetworkConfig(&network_config);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  NetworkRequest network_request(network_config->session());
+
+  network_request.AddHttpRequest(new SimpleRequest);
+
+  // BITS takes the job to BG_JOB_STATE_TRANSIENT_ERROR when the server returns
+  // 204. After the "no progress time out", the BITS job errors out. Since
+  // BITS follows the WinHTTP in the fallback chain, the code is expected to
+  // execute only if WinHTTP fails to get a response from the server.
+
+  // BITS transfers files only when the job owner is logged on.
+  bool is_logged_on(false);
+  if (IsThreadImpersonatingUser()) {
+    // Code red download thread only impersonates to logged on user. So when
+    // impersonation happens, it means the user is logged on.
+    is_logged_on = true;
+  } else {
+    // Assumes the caller is not logged on if the function failed.
+    hr = IsUserLoggedOn(&is_logged_on);
+    ASSERT1(SUCCEEDED(hr) || !is_logged_on);
+  }
+  if (is_logged_on) {
+    BitsRequest* bits_request(new BitsRequest);
+    bits_request->set_minimum_retry_delay(kSecPerMin);
+    bits_request->set_no_progress_timeout(5 * kSecPerMin);
+    network_request.AddHttpRequest(bits_request);
+  }
+
+  hr = network_request.DownloadFile(CString(url), CString(file_path));
+  if (FAILED(hr)) {
+    return E_FAIL;
+  }
+
+  *http_status_code = network_request.http_status_code();
+  switch (network_request.http_status_code()) {
+    case HTTP_STATUS_OK:
+      return S_OK;
+    case HTTP_STATUS_NO_CONTENT:
+      return E_FAIL;
+    default:
+      return E_FAIL;
+  }
+}
+
+HRESULT CreateUniqueTempFileForLoggedOnUser(CString* target_file) {
+  ASSERT1(target_file);
+  CAccessToken access_token;
+  TCHAR buffer[MAX_PATH] = {0};
+  VERIFY1(access_token.GetThreadToken(TOKEN_READ));
+  HRESULT hr = ::SHGetFolderPath(NULL,
+                                 CSIDL_LOCAL_APPDATA,
+                                 access_token.GetHandle(),
+                                 SHGFP_TYPE_CURRENT,
+                                 buffer);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return GetNewFileNameInDirectory(buffer, target_file);
+}
+
+HRESULT DownloadCodeRedFileAsLoggedOnUser(const TCHAR* url,
+                                      const TCHAR* file_path,
+                                      void* callback_argument,
+                                      int* http_status_code) {
+  ASSERT1(http_status_code);
+  *http_status_code = 0;
+  scoped_handle logged_on_user_token(
+      goopdate_utils::GetImpersonationTokenForMachineProcess(true));
+  if (!valid(logged_on_user_token)) {
+    return E_FAIL;
+  }
+
+  HRESULT hr = S_OK;
+  CString download_target_path;
+  {
+    scoped_impersonation impersonate_user(get(logged_on_user_token));
+    hr = HRESULT_FROM_WIN32(impersonate_user.result());
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = CreateUniqueTempFileForLoggedOnUser(&download_target_path);
+    if (FAILED(hr)) {
+      return hr;
+    }
+    if (download_target_path.IsEmpty()) {
+      return E_FAIL;
+    }
+
+    hr = DownloadCodeRedFile(url,
+                             download_target_path,
+                             callback_argument,
+                             http_status_code);
+    if (FAILED(hr)) {
+      ::DeleteFile(download_target_path);
+      return hr;
+    }
+  }
+
+  const DWORD kMoveFlag = MOVEFILE_COPY_ALLOWED |
+                          MOVEFILE_REPLACE_EXISTING |
+                          MOVEFILE_WRITE_THROUGH;
+  if (!::MoveFileEx(download_target_path,
+                    file_path,
+                    kMoveFlag)) {
+    hr = HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  ::DeleteFile(download_target_path);
+  return hr;
+}
+
+// Download Callback for Code Red.
+// Returns S_OK when the download of the Code Red file succeeds and E_FAIL
+// otherwise.
+HRESULT CodeRedDownloadCallback(const TCHAR* url,
+                                const TCHAR* file_path,
+                                void* callback_argument) {
+  ++metric_cr_callback_total;
+
+  int http_status_code = 0;
+  HRESULT hr = DownloadCodeRedFileAsLoggedOnUser(url,
+                                                 file_path,
+                                                 callback_argument,
+                                                 &http_status_code);
+  if (FAILED(hr) && (http_status_code != HTTP_STATUS_NO_CONTENT)) {
+    hr = DownloadCodeRedFile(url,
+                             file_path,
+                             callback_argument,
+                             &http_status_code);
+  }
+
+  switch (http_status_code) {
+    case HTTP_STATUS_OK:
+      ++metric_cr_callback_status_200;
+      break;
+    case HTTP_STATUS_NO_CONTENT:
+      ++metric_cr_callback_status_204;
+      break;
+    default:
+      ++metric_cr_callback_status_other;
+      break;
+  }
+
+  return hr;
+}
+
+}  // namespace
+
+HRESULT CheckForCodeRed(bool is_machine, const CString& omaha_version) {
+  HRESULT hr = FixGoogleUpdate(kGoogleUpdateAppId,
+                               omaha_version,
+                               _T(""),     // Omaha doesn't have a language.
+                               is_machine,
+                               &CodeRedDownloadCallback,
+                               NULL);
+  CORE_LOG(L2, (_T("[FixGoogleUpdate returned 0x%08x]"), hr));
+  return hr;
+}
+
+}  // namespace omaha
diff --git a/goopdate/code_red_check.h b/goopdate/code_red_check.h
new file mode 100644
index 0000000..82fccbd
--- /dev/null
+++ b/goopdate/code_red_check.h
@@ -0,0 +1,28 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_CODE_RED_CHECK_H_
+#define OMAHA_GOOPDATE_CODE_RED_CHECK_H_
+
+#include <windows.h>
+#include <atlstr.h>
+
+namespace omaha {
+
+HRESULT CheckForCodeRed(bool is_machine, const CString& omaha_version);
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_CODE_RED_CHECK_H_
diff --git a/goopdate/com_forwarder.cc b/goopdate/com_forwarder.cc
new file mode 100644
index 0000000..629fbfe
--- /dev/null
+++ b/goopdate/com_forwarder.cc
@@ -0,0 +1,91 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <shlwapi.h>
+#include <tchar.h>
+#include <strsafe.h>
+#include <windows.h>
+#include "base/basictypes.h"
+#include "omaha/base/constants.h"
+#include "omaha/common/const_cmd_line.h"
+
+// TODO(omaha): Use a registry override instead.
+#if !OFFICIAL_BUILD
+bool IsRunningFromStaging(const WCHAR* const command_line) {
+  return !wcscmp(command_line + (wcslen(command_line) - wcslen(L"staging")),
+                 L"staging");
+}
+#endif
+
+int WINAPI WinMain(HINSTANCE instance, HINSTANCE previous_instance,
+                   LPSTR cmd_line, int show) {
+  UNREFERENCED_PARAMETER(instance);
+  UNREFERENCED_PARAMETER(previous_instance);
+  UNREFERENCED_PARAMETER(cmd_line);
+  UNREFERENCED_PARAMETER(show);
+
+  WCHAR command_line[MAX_PATH * 2] = {};
+  if (0 == ::GetModuleFileName(NULL,
+                               command_line,
+                               arraysize(command_line))) {
+    return E_UNEXPECTED;
+  }
+
+  // TODO(omaha): Use the registry to get the path of the constant shell.
+  // Remove filename and move up one directory, because we want to use the
+  // constant shell GoogleUpdate.exe.
+  ::PathRemoveFileSpec(command_line);
+#if OFFICIAL_BUILD
+  ::PathRemoveFileSpec(command_line);
+#else
+  // This is to facilitate unit tests such as
+  // GoogleUpdateCoreTest.LaunchCmdElevated_LocalServerRegistered. If we are
+  // running from the staging directory, the shell is in the same directory.
+  if (!IsRunningFromStaging(command_line)) {
+    ::PathRemoveFileSpec(command_line);
+  }
+#endif
+
+  if (!::PathAppend(command_line, omaha::kOmahaShellFileName)) {
+    return E_UNEXPECTED;
+  }
+
+  if (FAILED(StringCchCat(command_line, arraysize(command_line), L" ")) ||
+      FAILED(StringCchCat(command_line, arraysize(command_line),
+                          CMD_LINE_SWITCH))) {
+    return E_UNEXPECTED;
+  }
+
+  STARTUPINFO si = { sizeof(si) };
+  // XXX: Normally, you should close the handles returned in
+  // PROCESS_INFORMATION. That step is skipped here since we are exiting
+  // immediately once the new process is created.
+  PROCESS_INFORMATION pi = {};
+  if (!::CreateProcess(
+          NULL,
+          command_line,
+          NULL,
+          NULL,
+          FALSE,
+          0,
+          NULL,
+          NULL,
+          &si,
+          &pi)) {
+    return E_UNEXPECTED;
+  }
+
+  return 0;
+}
diff --git a/goopdate/com_proxy.h b/goopdate/com_proxy.h
new file mode 100644
index 0000000..5d88bbf
--- /dev/null
+++ b/goopdate/com_proxy.h
@@ -0,0 +1,234 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_COM_PROXY_H_
+#define OMAHA_GOOPDATE_COM_PROXY_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include <atlctl.h>
+#include "base/basictypes.h"
+#include "omaha/base/atlregmapex.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/goopdate/google_update_ps_resource.h"
+#include "goopdate/omaha3_idl.h"
+
+namespace omaha {
+
+// All[*] coclasses in omaha3_idl.idl do the following:
+// * Derive from StdMarshalInfo.
+// * Construct StdMarshalInfo(is_machine).
+// * Add a COM_INTERFACE_ENTRY(IStdMarshalInfo)
+//
+// [*] The proxy classes GoogleComProxy[XXX]Class, being proxies, do not follow
+// the steps above. In addition, the CurrentStateClass has a custom marshaler
+// and does not follow the steps above.
+//
+const IID kIIDsToRegister[] = {
+  __uuidof(IGoogleUpdate3),
+  __uuidof(IAppBundle),
+  __uuidof(IApp),
+  __uuidof(IAppVersion),
+  __uuidof(IPackage),
+  __uuidof(ICurrentState),
+
+  __uuidof(IRegistrationUpdateHook),
+
+  __uuidof(IGoogleUpdate3Web),
+  __uuidof(IGoogleUpdate3WebSecurity),
+  __uuidof(IAppBundleWeb),
+  __uuidof(IAppWeb),
+  __uuidof(IAppVersionWeb),
+  __uuidof(ICoCreateAsync),
+  __uuidof(ICoCreateAsyncStatus),
+  __uuidof(ICredentialDialog),
+
+  // Omaha2 IIDs:
+  __uuidof(IBrowserHttpRequest2),
+  __uuidof(IProcessLauncher),
+  __uuidof(IProgressWndEvents),
+  __uuidof(IJobObserver),
+  __uuidof(IGoogleUpdate),
+  __uuidof(IGoogleUpdateCore),
+};
+
+struct ComProxyMode {
+  static bool is_machine() {
+    return goopdate_utils::IsRunningFromOfficialGoopdateDir(true);
+  }
+
+  static const GUID& class_id() {
+    return is_machine() ? __uuidof(GoogleComProxyMachineClass) :
+                          __uuidof(GoogleComProxyUserClass);
+  }
+
+  static const GUID ps_clsid() {
+    if (is_machine()) {
+      GUID proxy_clsid = PROXY_CLSID_IS_MACHINE;
+      return proxy_clsid;
+    } else {
+      GUID proxy_clsid = PROXY_CLSID_IS_USER;
+      return proxy_clsid;
+    }
+  }
+
+  static const TCHAR* const hk_root() {
+    return is_machine() ? _T("HKLM") : _T("HKCU");
+  }
+};
+
+#pragma warning(push)
+
+// C4640: construction of local static object is not thread-safe
+#pragma warning(disable : 4640)
+
+// C4505: unreferenced IUnknown local functions have been removed
+#pragma warning(disable : 4505)
+
+class ATL_NO_VTABLE ComProxy
+    : public CComObjectRootEx<CComMultiThreadModel>,
+      public CComCoClass<ComProxy>,
+      public IUnknown {
+ public:
+  ComProxy() {
+    CORE_LOG(L2, (_T("[ComProxy::ComProxy]")));
+  }
+
+  DECLARE_GET_CONTROLLING_UNKNOWN()
+  DECLARE_REGISTRY_RESOURCEID_EX(IDR_COM_PROXY_RGS);
+
+#pragma warning(push)
+// Construction of local static object is not thread-safe
+#pragma warning(disable:4640)
+  BEGIN_REGISTRY_MAP()
+    REGMAP_ENTRY(_T("HKROOT"), ComProxyMode::hk_root())
+    REGMAP_ENTRY(_T("CLSID"),  ComProxyMode::class_id())
+  END_REGISTRY_MAP()
+#pragma warning(pop)
+
+  BEGIN_COM_MAP(ComProxy)
+    COM_INTERFACE_ENTRY(IUnknown)
+    COM_INTERFACE_ENTRY_FUNC(__uuidof(IClientSecurity), 0, QueryInternal)
+    COM_INTERFACE_ENTRY_FUNC(__uuidof(IMultiQI), 0, QueryInternal)
+    COM_INTERFACE_ENTRY_AGGREGATE_BLIND(proxy_manager_.p)
+  END_COM_MAP()
+
+  static HRESULT WINAPI QueryInternal(void* ptr, REFIID iid,
+                                      void** retval, DWORD_PTR) {
+    ASSERT1(ptr);
+    ASSERT1(retval);
+    CORE_LOG(L2, (_T("[ComProxy::QueryInternal][%s]"), GuidToString(iid)));
+
+    ComProxy* this_ptr = reinterpret_cast<ComProxy*>(ptr);
+    return this_ptr->proxy_internal_unknown_->QueryInternalInterface(iid,
+                                                                     retval);
+  }
+
+  HRESULT FinalConstruct() {
+    CORE_LOG(L2, (_T("[ComProxy::FinalConstruct]")));
+
+    HRESULT hr = RegisterProxyStubs();
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[RegisterProxyStubs failed][0x%x]"), hr));
+      // If explicit registration failed, the registry-based proxy lookup
+      // mechanism may still work. Fall through.
+    }
+
+    hr = ::CoGetStdMarshalEx(GetControllingUnknown(),
+                             SMEXF_HANDLER,
+                             &proxy_manager_);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[::CoGetStdMarshalEx failed][0x%x]"), hr));
+      return hr;
+    }
+
+    return proxy_manager_.QueryInterface(&proxy_internal_unknown_);
+  }
+
+  void FinalRelease() {
+    CORE_LOG(L2, (_T("[ComProxy::FinalRelease]")));
+  }
+
+  static HRESULT RegisterProxyStubs() {
+    static LLock lock;
+    static bool is_registered = false;
+
+    __mutexScope(lock);
+
+    if (is_registered) {
+      return S_OK;
+    }
+
+    CORE_LOG(L2, (_T("[ComProxy::RegisterProxyStubs][Registering][%d]"),
+                  ComProxyMode::is_machine()));
+
+    const GUID ps_clsid = ComProxyMode::ps_clsid();
+    for (size_t i = 0; i < arraysize(kIIDsToRegister); ++i) {
+      HRESULT hr = ::CoRegisterPSClsid(kIIDsToRegister[i], ps_clsid);
+      if (FAILED(hr)) {
+        CORE_LOG(LE, (_T("[::CoRegisterPSClsid failed][%s][%s][0x%x]"),
+            GuidToString(kIIDsToRegister[i]), GuidToString(ps_clsid), hr));
+        return hr;
+      }
+    }
+
+    is_registered = true;
+    return S_OK;
+  }
+
+ protected:
+  virtual ~ComProxy() {
+    CORE_LOG(L2, (_T("[ComProxy::~ComProxy]")));
+  }
+
+  CComPtr<IUnknown> proxy_manager_;
+  CComPtr<IInternalUnknown> proxy_internal_unknown_;
+
+  DISALLOW_COPY_AND_ASSIGN(ComProxy);
+};
+
+#pragma warning(pop)
+
+class StdMarshalInfo : public IStdMarshalInfo {
+ public:
+  explicit StdMarshalInfo(bool is_machine) : is_machine_(is_machine) {
+    CORE_LOG(L6, (_T("[StdMarshalInfo::StdMarshalInfo][%d]"), is_machine));
+
+    VERIFY1(SUCCEEDED(ComProxy::RegisterProxyStubs()));
+  }
+
+  // IStdMarshalInfo.
+  STDMETHODIMP GetClassForHandler(DWORD context, void* ptr, CLSID* clsid) {
+    UNREFERENCED_PARAMETER(context);
+    UNREFERENCED_PARAMETER(ptr);
+
+    *clsid = is_machine_ ? __uuidof(GoogleComProxyMachineClass) :
+                           __uuidof(GoogleComProxyUserClass);
+    return S_OK;
+  }
+
+ private:
+  bool is_machine_;
+
+  DISALLOW_COPY_AND_ASSIGN(StdMarshalInfo);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_COM_PROXY_H_
+
diff --git a/goopdate/com_wrapper_creator.h b/goopdate/com_wrapper_creator.h
new file mode 100644
index 0000000..f367e13
--- /dev/null
+++ b/goopdate/com_wrapper_creator.h
@@ -0,0 +1,113 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// TODO(omaha): rename the file to match the name of the class.
+
+#ifndef OMAHA_GOOPDATE_COM_WRAPPER_CREATOR_H_
+#define OMAHA_GOOPDATE_COM_WRAPPER_CREATOR_H_
+
+#include <windows.h>
+#include <atlbase.h>
+#include <atlcom.h>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "base/scoped_ptr_address.h"
+#include "base/utils.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/goopdate/model_object.h"
+#include "third_party/bar/shared_ptr.h"
+
+namespace omaha {
+
+class AppBundle;
+typedef shared_ptr<AppBundle> ControllingPtr;
+
+// Generalizes the creation of COM wrappers for a given class T.
+// It requires:
+//   * The wrapper class TWrapper derives from ComWrapper
+//   * The wrapped class T provides access to model instance
+template <typename TWrapper, typename T>
+class ComWrapper : public CComObjectRootEx<CComObjectThreadModel> {
+ public:
+
+  static HRESULT Create(const ControllingPtr& controlling_ptr,
+                        T* t, IDispatch** t_wrapper) {
+    ASSERT1(t);
+    ASSERT1(t_wrapper);
+
+    ASSERT1(IsModelLockedByCaller(t->model()));
+
+    scoped_ptr<TComObject> t_com_object;
+    HRESULT hr = TComObject::CreateInstance(address(t_com_object));
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = t_com_object->QueryInterface(t_wrapper);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    t_com_object->model_ = t->model();
+    t_com_object->controlling_ptr_ = controlling_ptr;
+    t_com_object->wrapped_obj_ = t;
+
+    t_com_object.release();
+    return S_OK;
+  }
+
+ protected:
+  ComWrapper() : model_(NULL), wrapped_obj_(NULL) {}
+
+  ~ComWrapper() {}
+
+  void FinalRelease() {
+    controlling_ptr_.reset();
+    wrapped_obj_ = NULL;
+  }
+
+  const Model* model() const {
+    return omaha::interlocked_exchange_pointer(&model_, model_);
+  }
+
+  const ControllingPtr& controlling_ptr() const {
+    ASSERT1(IsModelLockedByCaller(wrapped_obj_->model()));
+    return controlling_ptr_;
+  }
+
+  T* wrapped_obj() {
+    return omaha::interlocked_exchange_pointer(&wrapped_obj_, wrapped_obj_);
+  }
+
+ private:
+
+  typedef CComObject<TWrapper> TComObject;
+
+  // The pointer is written and read from multiple threads and it is written
+  // to with the same value by the atomic pointer exchange, hence the volatile
+  // and mutable cv qualifiers respectively.
+  mutable Model* volatile model_;
+
+  ControllingPtr controlling_ptr_;
+
+  T* wrapped_obj_;
+
+  DISALLOW_COPY_AND_ASSIGN(ComWrapper);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_COM_WRAPPER_CREATOR_H_
+
diff --git a/goopdate/command_line.cc b/goopdate/command_line.cc
deleted file mode 100644
index 29650f5..0000000
--- a/goopdate/command_line.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/goopdate/command_line.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/goopdate/command_line_parser.h"
-#include "omaha/goopdate/goopdate_command_line_validator.h"
-
-namespace omaha {
-
-// Returns a pointer to the second token in the cmd_line parameter or an
-// empty string. See the implementation in vc7\crt\src\wincmdln.c
-//
-// TODO(omaha): consider moving this function into the tiny shell, as it
-// is logically part of our modified runtime environment.
-TCHAR* GetCmdLineTail(const TCHAR* cmd_line) {
-  ASSERT1(cmd_line);
-  bool in_quote = false;
-
-  // Skip past program name (first token in command line).
-  // Check for and handle quoted program name.
-
-  while ((*cmd_line > _T(' ')) ||
-         (*cmd_line && in_quote)) {
-    // Flip the in_quote if current character is '"'.
-    if (*cmd_line == _T('"')) {
-      in_quote = !in_quote;
-    }
-    ++cmd_line;
-  }
-
-  // Skip past any white space preceeding the second token.
-  while (*cmd_line && (*cmd_line <= _T(' '))) {
-    cmd_line++;
-  }
-
-  return const_cast<TCHAR*>(cmd_line);
-}
-
-// Assumption: The metainstaller has verified that space ' ' and
-// double quote '"' characters do not appear in the "extra" arguments.
-// This is important so that attackers can't create tags that provide a valid
-// extra argument string followed by other commands.
-HRESULT ParseCommandLine(const TCHAR* cmd_line, CommandLineArgs* args) {
-  ASSERT1(cmd_line);
-  ASSERT1(args);
-  CORE_LOG(L3, (_T("[ParseCommandLine][%s]"), cmd_line));
-
-  CommandLineParser parser;
-  HRESULT hr = parser.ParseFromString(cmd_line);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[ParseCommandLine][ParseFromString failed][0x%x]"), hr));
-    return hr;
-  }
-
-  GoopdateCommandLineValidator validator;
-  hr = validator.Setup();
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[ParseCommandLine][Validator Setup failed][0x%x]"), hr));
-    return hr;
-  }
-
-  hr = validator.Validate(&parser, args);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[ParseCommandLine][Validate failed][0x%x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-}  // namespace omaha
-
diff --git a/goopdate/command_line.h b/goopdate/command_line.h
deleted file mode 100644
index d410553..0000000
--- a/goopdate/command_line.h
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// TODO(omaha): consider making all what can be passed on the command line
-// "arguments". Our terminology to separate them in commands and options is not
-// consistent.
-
-#ifndef OMAHA_GOOPDATE_COMMAND_LINE_H__
-#define OMAHA_GOOPDATE_COMMAND_LINE_H__
-
-#include <tchar.h>
-#include <atlstr.h>
-#include <vector>
-#include "omaha/common/constants.h"
-#include "omaha/common/browser_utils.h"
-
-namespace omaha {
-
-// Replacement for the C runtime function to process the command line.
-// The first token of the command line in Windows is the process name.
-// What gets passed to WinMain by the C runtime must not include the first
-// token. Since our tiny shell does not use the C runtime we must handle
-// the command line by ourselves.
-TCHAR* GetCmdLineTail(const TCHAR* cmd_line);
-
-struct CommandLineAppArgs {
-  CommandLineAppArgs()
-      : app_guid(GUID_NULL),
-        needs_admin(false) {}
-
-  GUID app_guid;
-  CString app_name;
-  bool needs_admin;
-  CString ap;
-  CString tt_token;
-  CString encoded_installer_data;
-  CString install_data_index;
-};
-
-// Values may be sent in pings or stats. Do not remove or reuse existing values.
-typedef enum CommandLineMode {
-  COMMANDLINE_MODE_UNKNOWN = 0,
-  COMMANDLINE_MODE_NOARGS = 1,  // See GoopdateCommandLineValidator.
-  COMMANDLINE_MODE_CORE = 2,
-  COMMANDLINE_MODE_SERVICE = 3,
-  COMMANDLINE_MODE_REGSERVER = 4,
-  COMMANDLINE_MODE_UNREGSERVER = 5,
-  COMMANDLINE_MODE_NETDIAGS = 6,
-  COMMANDLINE_MODE_CRASH = 7,
-  COMMANDLINE_MODE_REPORTCRASH = 8,
-  COMMANDLINE_MODE_INSTALL = 9,
-  COMMANDLINE_MODE_UPDATE = 10,
-  COMMANDLINE_MODE_IG = 11,
-  COMMANDLINE_MODE_HANDOFF_INSTALL = 12,
-  COMMANDLINE_MODE_UG = 13,
-  COMMANDLINE_MODE_UA = 14,
-  COMMANDLINE_MODE_RECOVER = 15,
-  COMMANDLINE_MODE_WEBPLUGIN = 16,
-  COMMANDLINE_MODE_CODE_RED_CHECK = 17,
-  COMMANDLINE_MODE_COMSERVER = 18,
-  COMMANDLINE_MODE_LEGACYUI = 19,
-  COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF = 20,
-  COMMANDLINE_MODE_REGISTER_PRODUCT = 21,
-  COMMANDLINE_MODE_UNREGISTER_PRODUCT = 22,
-  COMMANDLINE_MODE_SERVICE_REGISTER = 23,
-  COMMANDLINE_MODE_SERVICE_UNREGISTER = 24,
-  COMMANDLINE_MODE_CRASH_HANDLER = 25,
-};
-
-struct CommandLineExtraArgs {
-  CommandLineExtraArgs()
-      : installation_id(GUID_NULL),
-        browser_type(BROWSER_UNKNOWN),
-        usage_stats_enable(TRISTATE_NONE) {}
-
-  GUID installation_id;
-  CString brand_code;
-  CString client_id;
-  CString referral_id;
-  CString language;
-  BrowserType browser_type;
-  Tristate usage_stats_enable;
-
-  std::vector<CommandLineAppArgs> apps;
-};
-
-struct CommandLineArgs {
-  CommandLineArgs()
-      : mode(COMMANDLINE_MODE_UNKNOWN),
-        is_interactive_set(false),
-        is_machine_set(false),
-        is_crash_handler_disabled(false),
-        is_install_elevated(false),
-        is_silent_set(false),
-        is_eula_required_set(false),
-        is_offline_set(false),
-        is_oem_set(false),
-        is_uninstall_set(false) {}
-
-  CommandLineMode mode;
-  bool is_interactive_set;
-  bool is_machine_set;
-  bool is_crash_handler_disabled;
-  bool is_install_elevated;
-  bool is_silent_set;
-  bool is_eula_required_set;
-  bool is_offline_set;
-  bool is_oem_set;
-  bool is_uninstall_set;
-  CString extra_args_str;
-  CString app_args_str;
-  CString install_source;
-  CString crash_filename;
-  CString custom_info_filename;
-  CString legacy_manifest_path;
-  CString webplugin_urldomain;
-  CString webplugin_args;
-  CString code_red_metainstaller_path;
-  CommandLineExtraArgs extra;
-};
-
-// Parses the goopdate command line.
-HRESULT ParseCommandLine(const TCHAR* cmd_line, CommandLineArgs* args);
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_COMMAND_LINE_H__
diff --git a/goopdate/command_line_builder.cc b/goopdate/command_line_builder.cc
deleted file mode 100644
index 9de2f7d..0000000
--- a/goopdate/command_line_builder.cc
+++ /dev/null
@@ -1,506 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/goopdate/command_line_builder.h"
-
-#include <shellapi.h>
-
-#include "omaha/common/const_cmd_line.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/path.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/const_goopdate.h"
-
-namespace omaha {
-
-CommandLineBuilder::CommandLineBuilder(CommandLineMode mode)
-    : mode_(mode),
-      is_interactive_set_(false),
-      is_machine_set_(false),
-      is_silent_set_(false),
-      is_eula_required_set_(false),
-      is_offline_set_(false),
-      is_uninstall_set_(false) {
-}
-
-CommandLineBuilder::~CommandLineBuilder() {
-}
-
-void CommandLineBuilder::set_is_interactive_set(bool is_interactive_set) {
-  ASSERT1(mode_ == COMMANDLINE_MODE_REPORTCRASH);
-  is_interactive_set_ = is_interactive_set;
-}
-
-void CommandLineBuilder::set_is_machine_set(bool is_machine_set) {
-  ASSERT1(mode_ == COMMANDLINE_MODE_UG ||
-          mode_ == COMMANDLINE_MODE_REPORTCRASH);
-  is_machine_set_ = is_machine_set;
-}
-
-void CommandLineBuilder::set_is_silent_set(bool is_silent_set) {
-  ASSERT1(mode_ == COMMANDLINE_MODE_INSTALL ||
-          mode_ == COMMANDLINE_MODE_IG ||
-          mode_ == COMMANDLINE_MODE_HANDOFF_INSTALL);
-  is_silent_set_ = is_silent_set;
-}
-
-void CommandLineBuilder::set_is_eula_required_set(bool is_eula_required_set) {
-  ASSERT1(mode_ == COMMANDLINE_MODE_INSTALL ||
-          mode_ == COMMANDLINE_MODE_IG ||
-          mode_ == COMMANDLINE_MODE_HANDOFF_INSTALL);
-  is_eula_required_set_ = is_eula_required_set;
-}
-
-void CommandLineBuilder::set_is_offline_set(bool is_offline_set) {
-  ASSERT1(mode_ == COMMANDLINE_MODE_IG ||
-          mode_ == COMMANDLINE_MODE_HANDOFF_INSTALL);
-  is_offline_set_ = is_offline_set;
-}
-
-void CommandLineBuilder::set_is_uninstall_set(bool is_uninstall_set) {
-  ASSERT1(mode_ == COMMANDLINE_MODE_UA);
-  is_uninstall_set_ = is_uninstall_set;
-}
-
-void CommandLineBuilder::set_extra_args(const CString& extra_args) {
-  ASSERT1(mode_ == COMMANDLINE_MODE_INSTALL ||
-          mode_ == COMMANDLINE_MODE_IG ||
-          mode_ == COMMANDLINE_MODE_HANDOFF_INSTALL ||
-          mode_ == COMMANDLINE_MODE_REGISTER_PRODUCT ||
-          mode_ == COMMANDLINE_MODE_UNREGISTER_PRODUCT);
-  extra_args_ = extra_args;
-}
-
-void CommandLineBuilder::set_app_args(const CString& app_args) {
-  ASSERT1(mode_ == COMMANDLINE_MODE_INSTALL ||
-          mode_ == COMMANDLINE_MODE_IG ||
-          mode_ == COMMANDLINE_MODE_HANDOFF_INSTALL);
-  app_args_ = app_args;
-}
-
-void CommandLineBuilder::set_install_source(const CString& install_source) {
-  ASSERT1(mode_ == COMMANDLINE_MODE_WEBPLUGIN ||
-          mode_ == COMMANDLINE_MODE_INSTALL ||
-          mode_ == COMMANDLINE_MODE_HANDOFF_INSTALL ||
-          mode_ == COMMANDLINE_MODE_IG ||
-          mode_ == COMMANDLINE_MODE_UA);
-  install_source_ = install_source;
-}
-
-void CommandLineBuilder::set_crash_filename(const CString& crash_filename) {
-  ASSERT1(mode_ == COMMANDLINE_MODE_REPORTCRASH);
-  crash_filename_ = crash_filename;
-}
-
-void CommandLineBuilder::set_custom_info_filename(
-    const CString& custom_info_filename) {
-  ASSERT1(mode_ == COMMANDLINE_MODE_REPORTCRASH);
-  custom_info_filename_ = custom_info_filename;
-}
-
-void CommandLineBuilder::set_legacy_manifest_path(
-    const CString& legacy_manifest_path) {
-  ASSERT1(mode_ == COMMANDLINE_MODE_LEGACYUI ||
-          mode_ == COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF);
-  legacy_manifest_path_ = legacy_manifest_path;
-}
-
-void CommandLineBuilder::set_webplugin_url_domain(
-    const CString& webplugin_url_domain) {
-  ASSERT1(mode_ == COMMANDLINE_MODE_WEBPLUGIN);
-  webplugin_url_domain_ = webplugin_url_domain;
-}
-
-void CommandLineBuilder::set_webplugin_args(const CString& webplugin_args) {
-  ASSERT1(mode_ == COMMANDLINE_MODE_WEBPLUGIN);
-  webplugin_args_ = webplugin_args;
-}
-
-void CommandLineBuilder::set_code_red_metainstaller_path(
-    const CString& code_red_metainstaller_path) {
-  ASSERT1(mode_ == COMMANDLINE_MODE_RECOVER);
-  code_red_metainstaller_path_ = code_red_metainstaller_path;
-}
-
-CString CommandLineBuilder::GetCommandLineArgs() const {
-  CString cmd_line_args;
-
-  switch (mode_) {
-    case COMMANDLINE_MODE_NOARGS:
-      cmd_line_args.Empty();
-      break;
-    case COMMANDLINE_MODE_CORE:
-      cmd_line_args = GetCore();
-      break;
-    case COMMANDLINE_MODE_CRASH_HANDLER:
-      cmd_line_args = GetCrashHandler();
-      break;
-    case COMMANDLINE_MODE_SERVICE:
-      cmd_line_args = GetService();
-      break;
-    case COMMANDLINE_MODE_SERVICE_REGISTER:
-      cmd_line_args = GetServiceRegister();
-      break;
-    case COMMANDLINE_MODE_SERVICE_UNREGISTER:
-      cmd_line_args = GetServiceUnregister();
-      break;
-    case COMMANDLINE_MODE_REGSERVER:
-      cmd_line_args = GetRegServer();
-      break;
-    case COMMANDLINE_MODE_UNREGSERVER:
-      cmd_line_args = GetUnregServer();
-      break;
-    case COMMANDLINE_MODE_NETDIAGS:
-      cmd_line_args = GetNetDiags();
-      break;
-    case COMMANDLINE_MODE_CRASH:
-      cmd_line_args = GetCrash();
-      break;
-    case COMMANDLINE_MODE_REPORTCRASH:
-      cmd_line_args = GetReportCrash();
-      break;
-    case COMMANDLINE_MODE_INSTALL:
-      cmd_line_args = GetInstall();
-      break;
-    case COMMANDLINE_MODE_UPDATE:
-      cmd_line_args = GetUpdate();
-      break;
-    case COMMANDLINE_MODE_IG:
-      cmd_line_args = GetIG();
-      break;
-    case COMMANDLINE_MODE_HANDOFF_INSTALL:
-      cmd_line_args = GetHandoffInstall();
-      break;
-    case COMMANDLINE_MODE_UG:
-      cmd_line_args = GetUG();
-      break;
-    case COMMANDLINE_MODE_UA:
-      cmd_line_args = GetUA();
-      break;
-    case COMMANDLINE_MODE_RECOVER:
-      cmd_line_args = GetRecover();
-      break;
-    case COMMANDLINE_MODE_WEBPLUGIN:
-      cmd_line_args = GetWebPlugin();
-      break;
-    case COMMANDLINE_MODE_CODE_RED_CHECK:
-      cmd_line_args = GetCodeRedCheck();
-      break;
-    case COMMANDLINE_MODE_COMSERVER:
-      cmd_line_args = GetComServer();
-      break;
-    case COMMANDLINE_MODE_LEGACYUI:
-      // No one in Omaha 2 should be using this mode.  It's only for
-      // compatibility with previous versions.
-      ASSERT1(false);
-      break;
-    case COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF:
-      cmd_line_args = GetLegacyHandoff();
-      break;
-    case COMMANDLINE_MODE_REGISTER_PRODUCT:
-      cmd_line_args = GetRegisterProduct();
-      break;
-    case COMMANDLINE_MODE_UNREGISTER_PRODUCT:
-      cmd_line_args = GetUnregisterProduct();
-      break;
-    case COMMANDLINE_MODE_UNKNOWN:
-    default:
-      ASSERT1(false);
-      break;
-  }
-
-#ifdef _DEBUG
-  CString full_command_line;
-  full_command_line.Format(_T("gu.exe %s"), cmd_line_args);
-  CommandLineArgs args;
-  ASSERT1(SUCCEEDED(ParseCommandLine(full_command_line, &args)));
-#endif
-
-  return cmd_line_args;
-}
-
-CString CommandLineBuilder::GetCommandLine(const CString& program_name) const {
-  // Do not pass the results of the the /update builder to GoogleUpdate.exe.
-  // The command line for /update is intended to be passed to a metainstaller.
-  // See GetUpdate() for more information.
-  ASSERT1(COMMANDLINE_MODE_UPDATE != mode_ ||
-          -1 == program_name.Find(kGoopdateFileName));
-
-  // Always enclose the program name in double quotes.
-  CString enclosed_program_name(program_name);
-  EnclosePath(&enclosed_program_name);
-  CString cmd_line;
-  cmd_line.Format(_T("%s %s"), enclosed_program_name, GetCommandLineArgs());
-  return cmd_line;
-}
-
-CString CommandLineBuilder::GetSingleSwitch(const CString& switch_name) const {
-  CString cmd_line;
-  cmd_line.Format(_T("/%s"), switch_name);
-  return cmd_line;
-}
-
-CString CommandLineBuilder::GetCore() const {
-  return GetSingleSwitch(kCmdLineCore);
-}
-
-CString CommandLineBuilder::GetCrashHandler() const {
-  return GetSingleSwitch(kCmdLineCrashHandler);
-}
-
-CString CommandLineBuilder::GetService() const {
-  return GetSingleSwitch(kCmdLineService);
-}
-
-CString CommandLineBuilder::GetServiceRegister() const {
-  return GetSingleSwitch(kCmdLineRegisterService);
-}
-
-CString CommandLineBuilder::GetServiceUnregister() const {
-  return GetSingleSwitch(kCmdLineUnregisterService);
-}
-
-CString CommandLineBuilder::GetRegServer() const {
-  return GetSingleSwitch(kCmdRegServer);
-}
-
-CString CommandLineBuilder::GetUnregServer() const {
-  return GetSingleSwitch(kCmdUnregServer);
-}
-
-CString CommandLineBuilder::GetNetDiags() const {
-  return GetSingleSwitch(kCmdLineNetDiags);
-}
-
-CString CommandLineBuilder::GetCrash() const {
-  CString cmd_line = GetSingleSwitch(kCmdLineCrash);
-  return cmd_line;
-}
-
-CString CommandLineBuilder::GetReportCrash() const {
-  ASSERT1(!crash_filename_.IsEmpty());
-  if (crash_filename_.IsEmpty()) {
-    return CString();
-  }
-  CString cmd_line = GetSingleSwitch(kCmdLineReport);
-  if (is_interactive_set_) {
-    cmd_line.AppendFormat(_T(" /%s"), kCmdLineInteractive);
-  }
-  CString enclosed_crash_filename_(crash_filename_);
-  EnclosePath(&enclosed_crash_filename_);
-  cmd_line.AppendFormat(_T(" %s"), enclosed_crash_filename_);
-  if (is_machine_set()) {
-    cmd_line.AppendFormat(_T(" /%s"), kCmdLineMachine);
-  }
-
-  if (!custom_info_filename_.IsEmpty()) {
-    CString enclosed_custom_info_filename_(custom_info_filename_);
-    EnclosePath(&enclosed_custom_info_filename_);
-    cmd_line.AppendFormat(_T(" /%s %s"),
-                          kCmdLineCustomInfoFileName,
-                          enclosed_custom_info_filename_);
-  }
-
-  return cmd_line;
-}
-
-CString CommandLineBuilder::GetExtraAndAppArgs(
-    const TCHAR* extra_switch_name) const {
-  ASSERT1(extra_switch_name && *extra_switch_name);
-  ASSERT1(!extra_args_.IsEmpty());
-  if (extra_args_.IsEmpty()) {
-    return CString();
-  }
-
-  CString cmd_line;
-  CString enclosed_extra_args_(extra_args_);
-  EnclosePath(&enclosed_extra_args_);
-  cmd_line.Format(_T("/%s %s"), extra_switch_name, enclosed_extra_args_);
-
-  if (!app_args_.IsEmpty()) {
-    CString enclosed_app_args = app_args_;
-    EnclosePath(&enclosed_app_args);
-    cmd_line.AppendFormat(_T(" /%s %s"), kCmdLineAppArgs, enclosed_app_args);
-  }
-
-  return cmd_line;
-}
-
-// Does not support /oem or /eularequired because we would never build that
-// internally.
-CString CommandLineBuilder::GetInstall() const {
-  CString cmd_line(GetExtraAndAppArgs(kCmdLineInstall));
-  if (cmd_line.IsEmpty()) {
-    return CString();
-  }
-
-  if (!install_source_.IsEmpty()) {
-    cmd_line.AppendFormat(_T(" /%s %s"),
-                          kCmdLineInstallSource,
-                          install_source_);
-  }
-  if (is_silent_set_) {
-    cmd_line.AppendFormat(_T(" /%s"), kCmdLineSilent);
-  }
-  return cmd_line;
-}
-
-CString CommandLineBuilder::GetUpdate() const {
-  CString cmd_line;
-  cmd_line.Format(_T("/%s"), kCmdLineUpdate);
-  return cmd_line;
-}
-
-CString CommandLineBuilder::GetIG() const {
-  CString cmd_line(GetExtraAndAppArgs(kCmdLineFinishGoogleUpdateInstall));
-  if (cmd_line.IsEmpty()) {
-    return CString();
-  }
-
-  if (!install_source_.IsEmpty()) {
-    cmd_line.AppendFormat(_T(" /%s %s"),
-                          kCmdLineInstallSource,
-                          install_source_);
-  }
-  if (is_silent_set_) {
-    cmd_line.AppendFormat(_T(" /%s"), kCmdLineSilent);
-  }
-  if (is_eula_required_set_) {
-    cmd_line.AppendFormat(_T(" /%s"), kCmdLineEulaRequired);
-  }
-  if (is_offline_set_) {
-    cmd_line.AppendFormat(_T(" /%s"), kCmdLineOfflineInstall);
-  }
-  return cmd_line;
-}
-
-CString CommandLineBuilder::GetHandoffInstall() const {
-  CString cmd_line(GetExtraAndAppArgs(kCmdLineAppHandoffInstall));
-  if (cmd_line.IsEmpty()) {
-    return CString();
-  }
-
-  if (!install_source_.IsEmpty()) {
-    cmd_line.AppendFormat(_T(" /%s %s"),
-                          kCmdLineInstallSource,
-                          install_source_);
-  }
-  if (is_silent_set_) {
-    cmd_line.AppendFormat(_T(" /%s"), kCmdLineSilent);
-  }
-  if (is_eula_required_set_) {
-    cmd_line.AppendFormat(_T(" /%s"), kCmdLineEulaRequired);
-  }
-  if (is_offline_set_) {
-    cmd_line.AppendFormat(_T(" /%s"), kCmdLineOfflineInstall);
-  }
-  return cmd_line;
-}
-
-CString CommandLineBuilder::GetUG() const {
-  CString cmd_line;
-  if (is_machine_set_) {
-    cmd_line.Format(_T("/%s /%s"),
-                    kCmdLineFinishGoogleUpdateUpdate,
-                    kCmdLineMachine);
-  } else {
-    cmd_line.Format(_T("/%s"), kCmdLineFinishGoogleUpdateUpdate);
-  }
-  return cmd_line;
-}
-
-CString CommandLineBuilder::GetUA() const {
-  ASSERT1(!install_source_.IsEmpty());
-  if (install_source_.IsEmpty()) {
-    return CString();
-  }
-
-  CString cmd_line(GetSingleSwitch(kCmdLineUpdateApps));
-  cmd_line.AppendFormat(_T(" /%s %s"), kCmdLineInstallSource, install_source_);
-
-  if (is_uninstall_set_) {
-    cmd_line.AppendFormat(_T(" /%s"), kCmdLineUninstall);
-  }
-
-  return cmd_line;
-}
-
-CString CommandLineBuilder::GetRecover() const {
-  ASSERT1(!code_red_metainstaller_path_.IsEmpty());
-  if (code_red_metainstaller_path_.IsEmpty()) {
-    return CString();
-  }
-  CString cmd_line;
-  cmd_line.Format(_T("/%s %s"), kCmdLineRecover, code_red_metainstaller_path_);
-  return cmd_line;
-}
-
-CString CommandLineBuilder::GetWebPlugin() const {
-  ASSERT1(!webplugin_url_domain_.IsEmpty());
-  ASSERT1(!webplugin_args_.IsEmpty());
-  ASSERT1(!install_source_.IsEmpty());
-  if (webplugin_url_domain_.IsEmpty() ||
-      webplugin_args_.IsEmpty() ||
-      install_source_.IsEmpty()) {
-    return CString();
-  }
-  CString cmd_line;
-  CString enclosed_webplugin_url_domain_(webplugin_url_domain_);
-  CString enclosed_webplugin_args_(webplugin_args_);
-  EnclosePath(&enclosed_webplugin_url_domain_);
-  EnclosePath(&enclosed_webplugin_args_);
-  // TODO(omaha): Do we want this to handle the urlencoding for us?
-  cmd_line.Format(_T("/%s %s %s /%s %s"),
-                  kCmdLineWebPlugin,
-                  enclosed_webplugin_url_domain_,
-                  enclosed_webplugin_args_,
-                  kCmdLineInstallSource,
-                  install_source_);
-  return cmd_line;
-}
-
-CString CommandLineBuilder::GetCodeRedCheck() const {
-  return GetSingleSwitch(kCmdLineCodeRedCheck);
-}
-
-CString CommandLineBuilder::GetComServer() const {
-  return kCmdLineComServerDash;
-}
-
-CString CommandLineBuilder::GetLegacyHandoff() const {
-  ASSERT1(!legacy_manifest_path_.IsEmpty());
-  if (legacy_manifest_path_.IsEmpty()) {
-    return CString();
-  }
-  CString cmd_line;
-  cmd_line.Format(_T("/%s %s"),
-                  kCmdLineLegacyUserManifest,
-                  legacy_manifest_path_);
-  return cmd_line;
-}
-
-CString CommandLineBuilder::GetRegisterProduct() const {
-  ASSERT1(app_args_.IsEmpty());
-  return GetExtraAndAppArgs(kCmdLineRegisterProduct);
-}
-
-CString CommandLineBuilder::GetUnregisterProduct() const {
-  ASSERT1(app_args_.IsEmpty());
-  return GetExtraAndAppArgs(kCmdLineUnregisterProduct);
-}
-
-}  // namespace omaha
diff --git a/goopdate/command_line_builder.h b/goopdate/command_line_builder.h
deleted file mode 100644
index 569da7d..0000000
--- a/goopdate/command_line_builder.h
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_GOOPDATE_COMMAND_LINE_BUILDER_H__
-#define OMAHA_GOOPDATE_COMMAND_LINE_BUILDER_H__
-
-#include <windows.h>
-#include <atlstr.h>
-
-#include "base/basictypes.h"
-#include "omaha/goopdate/command_line.h"
-
-namespace omaha {
-
-// This class builds a GoogleUpdate.exe command line and makes sure it's
-// valid against the GoopdateCommandLineValidator.
-class CommandLineBuilder {
- public:
-  explicit CommandLineBuilder(CommandLineMode mode);
-  ~CommandLineBuilder();
-
-  CommandLineMode mode() const { return mode_; }
-
-  bool is_interactive_set() const { return is_interactive_set_; }
-  void set_is_interactive_set(bool is_interactive_set);
-
-  bool is_machine_set() const { return is_machine_set_; }
-  void set_is_machine_set(bool is_machine_set);
-
-  bool is_silent_set() const { return is_silent_set_; }
-  void set_is_silent_set(bool is_silent_set);
-
-  bool is_eula_required_set() const { return is_eula_required_set_; }
-  void set_is_eula_required_set(bool is_eula_required_set);
-
-  bool is_offline_set() const { return is_offline_set_; }
-  void set_is_offline_set(bool is_offline_set);
-
-  bool is_uninstall_set() const { return is_uninstall_set_; }
-  void set_is_uninstall_set(bool is_uninstall_set);
-
-  CString extra_args() const { return extra_args_; }
-  void set_extra_args(const CString& extra_args);
-
-  CString app_args() const { return app_args_; }
-  void set_app_args(const CString& app_args);
-
-  CString install_source() const { return install_source_; }
-  void set_install_source(const CString& install_source);
-
-  CString crash_filename() const { return crash_filename_; }
-  void set_crash_filename(const CString& crash_filename);
-
-  CString custom_info_filename() const { return custom_info_filename_; }
-  void set_custom_info_filename(const CString& custom_info_filename);
-
-  CString legacy_manifest_path() const { return legacy_manifest_path_; }
-  void set_legacy_manifest_path(const CString& legacy_manifest_path);
-
-  CString webplugin_url_domain() const { return webplugin_url_domain_; }
-  void set_webplugin_url_domain(const CString& webplugin_url_domain);
-
-  CString webplugin_args() const { return webplugin_args_; }
-  void set_webplugin_args(const CString& webplugin_args);
-
-  CString code_red_metainstaller_path() const {
-    return code_red_metainstaller_path_;
-  }
-  void set_code_red_metainstaller_path(
-      const CString& code_red_metainstaller_path);
-
-  // Outputs the proper command line string for the properties that are set.
-  // If the properties aren't in a valid combination, function will assert.
-  CString GetCommandLineArgs()  const;
-
-  CString GetCommandLine(const CString& program_name) const;
-
- private:
-  CString GetSingleSwitch(const CString& switch_name) const;
-  CString GetExtraAndAppArgs(const TCHAR* extra_switch_name) const;
-
-  CString GetCore() const;
-  CString GetCrashHandler() const;
-  CString GetService() const;
-  CString GetServiceRegister() const;
-  CString GetServiceUnregister() const;
-  CString GetRegServer() const;
-  CString GetUnregServer() const;
-  CString GetNetDiags() const;
-  CString GetCrash() const;
-  CString GetReportCrash() const;
-  CString GetInstall() const;
-  CString GetUpdate() const;
-  CString GetIG() const;
-  CString GetHandoffInstall() const;
-  CString GetUG() const;
-  CString GetUA() const;
-  CString GetRecover() const;
-  CString GetWebPlugin() const;
-  CString GetCodeRedCheck() const;
-  CString GetComServer() const;
-  CString GetLegacyHandoff() const;
-  CString GetRegisterProduct() const;
-  CString GetUnregisterProduct() const;
-
-  const CommandLineMode mode_;
-  bool is_interactive_set_;
-  bool is_machine_set_;
-  bool is_silent_set_;
-  bool is_eula_required_set_;
-  bool is_offline_set_;
-  bool is_uninstall_set_;
-  CString extra_args_;
-  CString app_args_;
-  CString install_source_;
-  CString crash_filename_;
-  CString custom_info_filename_;
-  CString legacy_manifest_path_;
-  CString webplugin_url_domain_;
-  CString webplugin_args_;
-  CString code_red_metainstaller_path_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(CommandLineBuilder);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_COMMAND_LINE_BUILDER_H__
-
diff --git a/goopdate/command_line_builder_unittest.cc b/goopdate/command_line_builder_unittest.cc
deleted file mode 100644
index 4504c24..0000000
--- a/goopdate/command_line_builder_unittest.cc
+++ /dev/null
@@ -1,514 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/goopdate/command_line_builder.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-TEST(CommandLineBuilder, BuildUnknown) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_UNKNOWN);
-  ExpectAsserts expect_asserts;
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T(""), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildNoArgs) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_NOARGS);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T(""), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildCore) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_CORE);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/c"), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildService) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_SERVICE);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/svc"), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildRegServer) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_REGSERVER);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/regserver"), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildUnregServer) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_UNREGSERVER);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/unregserver"), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildNetDiags) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_NETDIAGS);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/netdiags"), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildCrashNoFilename) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_CRASH);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/crash"), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildReportCrash) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
-  ExpectAsserts expect_asserts;  // Missing filename.
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T(""), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildReportCrashWithFilename) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
-  builder.set_crash_filename(_T("foo.dmp"));
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/report \"foo.dmp\""), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildReportCrashWithFilenameMachine) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
-  builder.set_crash_filename(_T("foo.dmp"));
-  builder.set_is_machine_set(true);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/report \"foo.dmp\" /machine"), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildReportCrashWithEnclosedFilename) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
-  builder.set_crash_filename(_T("\"foo.dmp\""));
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/report \"foo.dmp\""), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildReportCrashWithCustomInfo) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
-  ExpectAsserts expect_asserts;  // Missing filename.
-  builder.set_custom_info_filename(_T("foo.txt"));
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T(""), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildReportCrashWithFileanameWithCustomInfo) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
-  builder.set_crash_filename(_T("foo.dmp"));
-  builder.set_custom_info_filename(_T("foo.txt"));
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/report \"foo.dmp\" /custom_info_filename \"foo.txt\""),
-               cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildReportCrashWithFileanameWithCustomInfoMachine) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
-  builder.set_crash_filename(_T("foo.dmp"));
-  builder.set_custom_info_filename(_T("foo.txt"));
-  builder.set_is_machine_set(true);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(
-      _T("/report \"foo.dmp\" /machine /custom_info_filename \"foo.txt\""),
-      cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildReportCrashWithEnclosedFileanameWithCustomInfo) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
-  builder.set_crash_filename(_T("\"foo.dmp\""));
-  builder.set_custom_info_filename(_T("\"foo.txt\""));
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/report \"foo.dmp\" /custom_info_filename \"foo.txt\""),
-               cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildReportCrashInteractiveWithFilename) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
-  builder.set_crash_filename(_T("foo.dmp"));
-  builder.set_is_interactive_set(true);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/report /i \"foo.dmp\""), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildReportCrashMachineInteractiveWithFilename) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
-  builder.set_crash_filename(_T("foo.dmp"));
-  builder.set_is_machine_set(true);
-  builder.set_is_interactive_set(true);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/report /i \"foo.dmp\" /machine"), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildInstall) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_INSTALL);
-  ExpectAsserts expect_asserts;  // Missing parameters.
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T(""), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildInstallWithExtraArgs) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_INSTALL);
-  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                         _T("appname=YouTubeUploader&needsadmin=False&")
-                         _T("lang=en"));
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/install \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-               _T("appname=YouTubeUploader&needsadmin=False&lang=en\""),
-               cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildInstallWithExtraArgsSilent) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_INSTALL);
-  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                         _T("appname=YouTubeUploader&needsadmin=False&")
-                         _T("lang=en"));
-  builder.set_is_silent_set(true);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/install \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-               _T("appname=YouTubeUploader&needsadmin=False&lang=en\" /silent"),
-               cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildUpdate) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_UPDATE);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/update"), cmd_line);
-}
-
-// The /update builder works when not used with GoogleUpdate.exe.
-TEST(CommandLineBuilder, BuildUpdateAndGetCommandLineWithNonGoogleUpdateExe) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_UPDATE);
-  CString cmd_line = builder.GetCommandLine(_T("C:\\GoogleUpdateSetup_en.exe"));
-  EXPECT_STREQ(_T("\"C:\\GoogleUpdateSetup_en.exe\" /update"), cmd_line);
-}
-
-// The /update builder should not be used with GoogleUpdate.exe directly.
-TEST(CommandLineBuilder, BuildUpdateAndGetCommandLineWithGoogleUpdateExe) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_UPDATE);
-  ExpectAsserts expect_asserts;
-  CString cmd_line = builder.GetCommandLine(_T("C:\\GoogleUpdate.exe"));
-  EXPECT_STREQ(_T("\"C:\\GoogleUpdate.exe\" /update"), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildLegacyManifestHandoff) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF);
-  ExpectAsserts expect_asserts;
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T(""), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildLegacyManifestHandoffWithManifest) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF);
-  builder.set_legacy_manifest_path(_T("foo.gup"));
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/uiuser foo.gup"), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildLegacyUi) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_LEGACYUI);
-  ExpectAsserts expect_asserts;  // Not a supported type to build.
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T(""), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildComServer) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_COMSERVER);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("-Embedding"), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildCodeRedCheck) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_CODE_RED_CHECK);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/cr"), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildWebPlugin) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_WEBPLUGIN);
-  ExpectAsserts expect_asserts;
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T(""), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildWebPluginWithUrlArgsAndInstallSource) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_WEBPLUGIN);
-  builder.set_webplugin_args(_T("piargs"));
-  builder.set_webplugin_url_domain(_T("http://www.google.com/"));
-  builder.set_install_source(_T("oneclick"));
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/pi \"http://www.google.com/\" \"piargs\"")
-               _T(" /installsource oneclick"),
-               cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildRecover) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_RECOVER);
-  ExpectAsserts expect_asserts;
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T(""), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildRecoverWithMIPath) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_RECOVER);
-  builder.set_code_red_metainstaller_path(_T("foo.exe"));
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/recover foo.exe"), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildUA) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_UA);
-  ExpectAsserts expect_asserts;
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T(""), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildUAWithInstallSource) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_UA);
-  builder.set_install_source(_T("blah"));
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/ua /installsource blah"), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildUAWithInstallSourceAndUninstall) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_UA);
-  builder.set_install_source(_T("blah"));
-  builder.set_is_uninstall_set(true);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/ua /installsource blah /uninstall"), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildUG) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_UG);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/ug"), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildUGWithMachine) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_UG);
-  builder.set_is_machine_set(true);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/ug /machine"), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildHandoffInstall) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL);
-  ExpectAsserts expect_asserts;  // Missing extra args.
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T(""), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildHandoffInstallWithExtraArgs) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL);
-  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                         _T("appname=YouTubeUploader&needsadmin=False&")
-                         _T("lang=en"));
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/handoff \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-               _T("appname=YouTubeUploader&needsadmin=False&lang=en\""),
-               cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildHandoffInstallWithExtraArgsOffline) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL);
-  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                         _T("appname=YouTubeUploader&needsadmin=False&")
-                         _T("lang=en"));
-  builder.set_install_source(_T("offline"));
-  builder.set_is_offline_set(true);
-
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/handoff \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-               _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
-               _T(" /installsource offline")
-               _T(" /offlineinstall"),
-               cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildHandoffInstallWithExtraArgsSilentOffline) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL);
-  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                         _T("appname=YouTubeUploader&needsadmin=False&")
-                         _T("lang=en"));
-  builder.set_install_source(_T("offline"));
-  builder.set_is_silent_set(true);
-  builder.set_is_offline_set(true);
-
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/handoff \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-               _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
-               _T(" /installsource offline")
-               _T(" /silent /offlineinstall"),
-               cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildHandoffWithAppArgsSilentOffline) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL);
-  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                         _T("appname=YouTubeUploader&needsadmin=False&")
-                         _T("lang=en"));
-  builder.set_app_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                       _T("installerdata=foobar%45"));
-  builder.set_install_source(_T("offline"));
-  builder.set_is_silent_set(true);
-  builder.set_is_offline_set(true);
-
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/handoff \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-               _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
-               _T(" /appargs \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-               _T("installerdata=foobar%45\"")
-               _T(" /installsource offline")
-               _T(" /silent /offlineinstall"),
-               cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildHandoffWithAppArgsSilentOfflineEulaRequired) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL);
-  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                         _T("appname=YouTubeUploader&needsadmin=False&")
-                         _T("lang=en"));
-  builder.set_app_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                       _T("installerdata=foobar%45"));
-  builder.set_install_source(_T("offline"));
-  builder.set_is_silent_set(true);
-  builder.set_is_eula_required_set(true);
-  builder.set_is_offline_set(true);
-
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/handoff \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-               _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
-               _T(" /appargs \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-               _T("installerdata=foobar%45\"")
-               _T(" /installsource offline")
-               _T(" /silent /eularequired /offlineinstall"),
-               cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildIG) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_IG);
-  ExpectAsserts expect_asserts;
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T(""), cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildIGWithExtraArgs) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_IG);
-  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                         _T("appname=YouTubeUploader&needsadmin=False&")
-                         _T("lang=en"));
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/ig \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-               _T("appname=YouTubeUploader&needsadmin=False&lang=en\""),
-               cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildIGWithExtraArgsOffline) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_IG);
-  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                         _T("appname=YouTubeUploader&needsadmin=False&")
-                         _T("lang=en"));
-  builder.set_install_source(_T("offline"));
-  builder.set_is_offline_set(true);
-
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/ig \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-               _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
-               _T(" /installsource offline")
-               _T(" /offlineinstall"),
-               cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildIGWithExtraArgsOfflineSilent) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_IG);
-  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                         _T("appname=YouTubeUploader&needsadmin=False&")
-                         _T("lang=en"));
-  builder.set_install_source(_T("offline"));
-  builder.set_is_silent_set(true);
-  builder.set_is_offline_set(true);
-
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/ig \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-               _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
-               _T(" /installsource offline")
-               _T(" /silent /offlineinstall"),
-               cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildIGWithAppArgsSilentOffline) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_IG);
-  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                         _T("appname=YouTubeUploader&needsadmin=False&")
-                         _T("lang=en"));
-  builder.set_app_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                       _T("installerdata=foobar%45"));
-  builder.set_install_source(_T("offline"));
-  builder.set_is_silent_set(true);
-  builder.set_is_offline_set(true);
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/ig \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-               _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
-               _T(" /appargs \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-               _T("installerdata=foobar%45\"")
-               _T(" /installsource offline")
-               _T(" /silent /offlineinstall"),
-               cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildIGWithAppArgsSilentOfflineEulaRequired) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_IG);
-  builder.set_extra_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                         _T("appname=YouTubeUploader&needsadmin=False&")
-                         _T("lang=en"));
-  builder.set_app_args(_T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                       _T("installerdata=foobar%45"));
-  builder.set_install_source(_T("offline"));
-  builder.set_is_silent_set(true);
-  builder.set_is_eula_required_set(true);
-  builder.set_is_offline_set(true);
-
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/ig \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-               _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
-               _T(" /appargs \"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-               _T("installerdata=foobar%45\"")
-               _T(" /installsource offline")
-               _T(" /silent /eularequired /offlineinstall"),
-               cmd_line);
-}
-TEST(CommandLineBuilder, BuildRegisterProduct) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_REGISTER_PRODUCT);
-  builder.set_extra_args(_T("appguid={7DD3DAE3-87F1-4CFE-8BF4-452C74421401}&")
-                         _T("appname=Google Toolbar&needsadmin=True&")
-                         _T("lang=en"));
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/registerproduct ")
-               _T("\"appguid={7DD3DAE3-87F1-4CFE-8BF4-452C74421401}&")
-               _T("appname=Google Toolbar&needsadmin=True&lang=en\""),
-               cmd_line);
-}
-
-TEST(CommandLineBuilder, BuildUnregisterProduc) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_UNREGISTER_PRODUCT);
-  builder.set_extra_args(_T("appguid={7DD3DAE3-87F1-4CFE-8BF4-452C74421401}&")
-                         _T("needsadmin=True&lang=en"));
-  CString cmd_line = builder.GetCommandLineArgs();
-  EXPECT_STREQ(_T("/unregisterproduct ")
-               _T("\"appguid={7DD3DAE3-87F1-4CFE-8BF4-452C74421401}&")
-               _T("needsadmin=True&lang=en\""),
-               cmd_line);
-}
-
-}  // namespace omaha
-
diff --git a/goopdate/command_line_parser-internal.h b/goopdate/command_line_parser-internal.h
deleted file mode 100644
index 5bdc9a6..0000000
--- a/goopdate/command_line_parser-internal.h
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_GOOPDATE_COMMAND_LINE_PARSER_INTERNAL_H_
-#define OMAHA_GOOPDATE_COMMAND_LINE_PARSER_INTERNAL_H_
-
-#include <windows.h>
-#include <map>
-#include <vector>
-
-namespace omaha {
-
-namespace internal {
-
-// Repository for switches and corresponding switch arguments for a command
-// line.
-class CommandLineParserArgs {
- public:
-  CommandLineParserArgs() {}
-  ~CommandLineParserArgs() {}
-
-  typedef std::vector<CString> StringVector;
-  typedef StringVector::const_iterator StringVectorIter;
-  typedef std::map<CString, StringVector > SwitchAndArgumentsMap;
-  typedef SwitchAndArgumentsMap::const_iterator SwitchAndArgumentsMapIter;
-
-  // Gets the number of switches in the parsed command line.  Will return 0 for
-  // count if a parse has not occurred.
-  int GetSwitchCount() const;
-
-  // Returns the switch at a particular index.
-  // This is meant for iteration only and is not guaranteed to be in the order
-  // of the switches in the parsed command line.
-  HRESULT GetSwitchNameAtIndex(int index, CString* switch_name) const;
-
-  // Returns true if a switch with the name switch_name is found.
-  bool HasSwitch(const CString& switch_name) const;
-
-  // Returns the number of arguments for switch_name.  Will fail if switch_name
-  // is not a valid switch.
-  HRESULT GetSwitchArgumentCount(const CString& switch_name, int* count) const;
-
-  // Returns the value of a switch argument at the specified offset.
-  // Fails if switch_name is not a valid switch.
-  HRESULT GetSwitchArgumentValue(const CString& switch_name,
-                                 int argument_index,
-                                 CString* argument_value) const;
-
-  void Reset();
-  HRESULT AddSwitch(const CString& switch_name);
-  HRESULT AddSwitchArgument(const CString& switch_name,
-                            const CString& argument_value);
-
- private:
-  SwitchAndArgumentsMap switch_arguments_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(CommandLineParserArgs);
-};
-
-}  // namespace internal
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_COMMAND_LINE_PARSER_INTERNAL_H_
-
diff --git a/goopdate/command_line_parser.cc b/goopdate/command_line_parser.cc
deleted file mode 100644
index 66e9d3f..0000000
--- a/goopdate/command_line_parser.cc
+++ /dev/null
@@ -1,377 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/goopdate/command_line_parser.h"
-
-#include <shellapi.h>
-
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-
-namespace omaha {
-
-namespace internal {
-
-void CommandLineParserArgs::Reset() {
-  switch_arguments_.clear();
-}
-
-// Assumes switch_name is already lower case.
-HRESULT CommandLineParserArgs::AddSwitch(const CString& switch_name) {
-  ASSERT1(CString(switch_name).MakeLower().Compare(switch_name) == 0);
-  if (switch_arguments_.find(switch_name) != switch_arguments_.end()) {
-    return E_INVALIDARG;
-  }
-
-  StringVector string_vector;
-  switch_arguments_[switch_name] = string_vector;
-  return S_OK;
-}
-
-// Assumes switch_name is already lower case.
-HRESULT CommandLineParserArgs::AddSwitchArgument(const CString& switch_name,
-                                                 const CString& value) {
-  ASSERT1(CString(switch_name).MakeLower().Compare(switch_name) == 0);
-  ASSERT1(!switch_name.IsEmpty());
-  if (switch_name.IsEmpty()) {
-    // We don't have a switch yet, so this is just a base argument.
-    // Example command line:  "foo.exe myarg /someswitch"
-    // Here, myarg would be a base argument.
-    // TODO(omaha): base_args_.push_back(switch_name_str);
-    return E_INVALIDARG;
-  }
-
-  SwitchAndArgumentsMap::iterator iter = switch_arguments_.find(switch_name);
-  if (iter == switch_arguments_.end()) {
-    return E_UNEXPECTED;
-  }
-  (*iter).second.push_back(value);
-
-  return S_OK;
-}
-
-int CommandLineParserArgs::GetSwitchCount() const {
-  return switch_arguments_.size();
-}
-
-bool CommandLineParserArgs::HasSwitch(const CString& switch_name) const {
-  CString switch_name_lower = switch_name;
-  switch_name_lower.MakeLower();
-  return switch_arguments_.find(switch_name_lower) != switch_arguments_.end();
-}
-
-// The value at a particular index may change if switch_names are added
-// since we're using a map underneath.  But this keeps us from having to write
-// an interator and expose it externally.
-HRESULT CommandLineParserArgs::GetSwitchNameAtIndex(int index,
-                                                    CString* name) const {
-  ASSERT1(name);
-
-  if (index >= static_cast<int>(switch_arguments_.size())) {
-    return E_INVALIDARG;
-  }
-
-  SwitchAndArgumentsMapIter iter = switch_arguments_.begin();
-  for (int i = 0; i < index; ++i) {
-    ++iter;
-  }
-
-  *name = (*iter).first;
-
-  return S_OK;
-}
-
-HRESULT CommandLineParserArgs::GetSwitchArgumentCount(
-            const CString& switch_name, int* count) const {
-  ASSERT1(count);
-
-  CString switch_name_lower = switch_name;
-  switch_name_lower.MakeLower();
-
-  SwitchAndArgumentsMapIter iter = switch_arguments_.find(switch_name_lower);
-  if (iter == switch_arguments_.end()) {
-    return E_INVALIDARG;
-  }
-
-  *count = (*iter).second.size();
-  return S_OK;
-}
-
-HRESULT CommandLineParserArgs::GetSwitchArgumentValue(
-    const CString& switch_name,
-    int argument_index,
-    CString* argument_value) const {
-  ASSERT1(argument_value);
-
-  CString switch_name_lower = switch_name;
-  switch_name_lower.MakeLower();
-
-  int count = 0;
-  HRESULT hr = GetSwitchArgumentCount(switch_name_lower, &count);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  if (argument_index >= count) {
-    return E_INVALIDARG;
-  }
-
-  SwitchAndArgumentsMapIter iter = switch_arguments_.find(switch_name_lower);
-  if (iter == switch_arguments_.end()) {
-    return E_INVALIDARG;
-  }
-
-  *argument_value = (*iter).second[argument_index];
-  return S_OK;
-}
-
-}  // namespace internal
-
-CommandLineParser::CommandLineParser() {
-}
-
-CommandLineParser::~CommandLineParser() {
-}
-
-HRESULT CommandLineParser::ParseFromString(const wchar_t* command_line) {
-  CString command_line_str(command_line);
-  command_line_str.Trim(_T(" "));
-
-  int argc = 0;
-  wchar_t** argv = ::CommandLineToArgvW(command_line_str, &argc);
-  if (!argv) {
-    return HRESULTFromLastError();
-  }
-
-  HRESULT hr = ParseFromArgv(argc, argv);
-  ::LocalFree(argv);
-  return hr;
-}
-
-// TODO(Omaha): Move the rule parser into a separate class.
-// TODO(Omaha): Fail the regular command parser if [/ switch is passed.
-// ParseFromArgv parses either a rule or a command line.
-//
-// Rules have required and optional parameters. An example of a rule is:
-//     "gu.exe /install <extraargs> [/oem [/appargs <appargs> [/silent"
-// This creates a rule for a command line that requires "/install" for the rule
-// to match. The other parameters are optional, indicated by prefixes of "[/".
-//
-// Command lines do not use "[/", and use "/" for all parameters.
-// A command line that looks like this:
-//     "gu.exe /install <extraargs> /oem /appargs <appargs>"
-// will match the rule above.
-HRESULT CommandLineParser::ParseFromArgv(int argc, wchar_t** argv) {
-  if (argc == 0 || !argv) {
-    return E_INVALIDARG;
-  }
-
-  CORE_LOG(L5, (_T("[CommandLineParser::ParseFromArgv][argc=%d]"), argc));
-
-  Reset();
-
-  if (argc == 1) {
-    // We only have the program name.  So, we're done parsing.
-    ASSERT1(!IsSwitch(argv[0]));
-    return S_OK;
-  }
-
-  CString current_switch_name;
-  bool is_optional_switch = false;
-
-  // Start parsing at the first argument after the program name (index 1).
-  for (int i = 1; i < argc; ++i) {
-    HRESULT hr = S_OK;
-    CString token = argv[i];
-    token.Trim(_T(" "));
-    CORE_LOG(L5, (_T("[Parsing arg][i=%d][argv[i]=%s]"), i, token));
-    if (IsSwitch(token)) {
-      hr = StripSwitchNameFromArgv(token, &current_switch_name);
-      if (FAILED(hr)) {
-        return hr;
-      }
-      hr = AddSwitch(current_switch_name);
-      if (FAILED(hr)) {
-        CORE_LOG(LE, (_T("[AddSwitch failed][%s][0x%x]"),
-                      current_switch_name, hr));
-        return hr;
-      }
-      is_optional_switch = false;
-    } else if (IsOptionalSwitch(token)) {
-      hr = StripOptionalSwitchNameFromArgv(token, &current_switch_name);
-      if (FAILED(hr)) {
-        return hr;
-      }
-      hr = AddOptionalSwitch(current_switch_name);
-      if (FAILED(hr)) {
-        CORE_LOG(LE, (_T("[AddOptionalSwitch failed][%s][0x%x]"),
-                      current_switch_name, hr));
-        return hr;
-      }
-      is_optional_switch = true;
-    } else {
-      hr = is_optional_switch ?
-          AddOptionalSwitchArgument(current_switch_name, token) :
-          AddSwitchArgument(current_switch_name, token);
-
-      if (FAILED(hr)) {
-        CORE_LOG(LE, (_T("[Adding switch argument failed][%d][%s][%s][0x%x]"),
-                      is_optional_switch, current_switch_name, token, hr));
-        return hr;
-      }
-    }
-  }
-
-  return S_OK;
-}
-
-bool CommandLineParser::IsSwitch(const CString& param) const {
-  // Switches must have a prefix (/) or (-), and at least one character.
-  if (param.GetLength() < 2) {
-    return false;
-  }
-
-  // All switches must start with / or -, and not contain any spaces.
-  // Since the argv parser strips out the enclosing quotes around an argument,
-  // we need to handle the following cases properly:
-  // * foo.exe /switch arg     -- /switch is a switch, arg is an arg
-  // * foo.exe /switch "/x y"  -- /switch is a switch, '/x y' is an arg and it
-  //   will get here _without_ the quotes.
-  // If param_str starts with / and contains no spaces, then it's a switch.
-  return ((param[0] == _T('/')) || (param[0] == _T('-'))) &&
-          (param.Find(_T(" ")) == -1) &&
-          (param.Find(_T("%20")) == -1);
-}
-
-bool CommandLineParser::IsOptionalSwitch(const CString& param) const {
-  // Optional switches must have a prefix ([/) or ([-), and at least one
-  // character.
-  return param[0] == _T('[') && IsSwitch(param.Right(param.GetLength() - 1));
-}
-
-HRESULT CommandLineParser::StripSwitchNameFromArgv(const CString& param,
-                                                   CString* switch_name) {
-  ASSERT1(switch_name);
-
-  if (!IsSwitch(param)) {
-    return E_INVALIDARG;
-  }
-
-  *switch_name = param.Right(param.GetLength() - 1);
-  switch_name->Trim(_T(" "));
-  switch_name->MakeLower();
-  return S_OK;
-}
-
-HRESULT CommandLineParser::StripOptionalSwitchNameFromArgv(const CString& param,
-                                                           CString* name) {
-  ASSERT1(name);
-
-  if (!IsOptionalSwitch(param)) {
-    return E_INVALIDARG;
-  }
-
-  return StripSwitchNameFromArgv(param.Right(param.GetLength() - 1), name);
-}
-
-void CommandLineParser::Reset() {
-  required_args_.Reset();
-  optional_args_.Reset();
-}
-
-HRESULT CommandLineParser::AddSwitch(const CString& switch_name) {
-  ASSERT1(switch_name == CString(switch_name).MakeLower());
-  return required_args_.AddSwitch(switch_name);
-}
-
-HRESULT CommandLineParser::AddSwitchArgument(const CString& switch_name,
-                                             const CString& argument_value) {
-  ASSERT1(switch_name == CString(switch_name).MakeLower());
-  return required_args_.AddSwitchArgument(switch_name, argument_value);
-}
-
-int CommandLineParser::GetSwitchCount() const {
-  return required_args_.GetSwitchCount();
-}
-
-bool CommandLineParser::HasSwitch(const CString& switch_name) const {
-  return required_args_.HasSwitch(switch_name);
-}
-
-// The value at a particular index may change if switch_names are added
-// since we're using a map underneath.  But this keeps us from having to write
-// an interator and expose it externally.
-HRESULT CommandLineParser::GetSwitchNameAtIndex(int index,
-                                                CString* switch_name) const {
-  return required_args_.GetSwitchNameAtIndex(index, switch_name);
-}
-
-HRESULT CommandLineParser::GetSwitchArgumentCount(const CString& switch_name,
-                                                  int* count) const {
-  return required_args_.GetSwitchArgumentCount(switch_name, count);
-}
-
-HRESULT CommandLineParser::GetSwitchArgumentValue(
-    const CString& switch_name,
-    int argument_index,
-    CString* argument_value) const {
-  return required_args_.GetSwitchArgumentValue(switch_name,
-                                               argument_index,
-                                               argument_value);
-}
-
-HRESULT CommandLineParser::AddOptionalSwitch(const CString& switch_name) {
-  ASSERT1(switch_name == CString(switch_name).MakeLower());
-  return optional_args_.AddSwitch(switch_name);
-}
-
-HRESULT CommandLineParser::AddOptionalSwitchArgument(const CString& switch_name,
-                                                     const CString& value) {
-  ASSERT1(switch_name == CString(switch_name).MakeLower());
-  return optional_args_.AddSwitchArgument(switch_name, value);
-}
-
-int CommandLineParser::GetOptionalSwitchCount() const {
-  return optional_args_.GetSwitchCount();
-}
-
-bool CommandLineParser::HasOptionalSwitch(const CString& switch_name) const {
-  return optional_args_.HasSwitch(switch_name);
-}
-
-// The value at a particular index may change if switch_names are added
-// since we're using a map underneath.  But this keeps us from having to write
-// an interator and expose it externally.
-HRESULT CommandLineParser::GetOptionalSwitchNameAtIndex(int index,
-                                                        CString* name) const {
-  return optional_args_.GetSwitchNameAtIndex(index, name);
-}
-
-HRESULT CommandLineParser::GetOptionalSwitchArgumentCount(const CString& name,
-                                                          int* count) const {
-  return optional_args_.GetSwitchArgumentCount(name, count);
-}
-
-HRESULT CommandLineParser::GetOptionalSwitchArgumentValue(const CString& name,
-                                                          int argument_index,
-                                                          CString* val) const {
-  return optional_args_.GetSwitchArgumentValue(name,
-                                               argument_index,
-                                               val);
-}
-
-}  // namespace omaha
-
diff --git a/goopdate/command_line_parser.h b/goopdate/command_line_parser.h
deleted file mode 100644
index 196d5af..0000000
--- a/goopdate/command_line_parser.h
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_GOOPDATE_COMMAND_LINE_PARSER_H__
-#define OMAHA_GOOPDATE_COMMAND_LINE_PARSER_H__
-
-#include <windows.h>
-#include <atlstr.h>
-
-#include <map>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "goopdate/command_line_parser-internal.h"
-
-namespace omaha {
-
-// This class will parse a command line either from a string or in argc/argv
-// format.  It then provides information about the parsed command line.
-// When passing the string, make sure it includes the program name as the first
-// argument.
-// A "switch" is an argument preceded by "/".  Each switch can take 0..n
-// arguments.
-// Example:  foo.exe /sw a "b b" /sw2 /sw3
-// * foo.exe is the program name
-// * sw, sw2, and sw3 are switches.
-// * a and 'b b' (without the quotes) are the arguments to the sw switch.
-// * sw has 2 arguments and sw2 and sw3 have no arguments.
-class CommandLineParser {
- public:
-  CommandLineParser();
-  ~CommandLineParser();
-
-  // Parses the command line from a string.  Must include the program name (e.g.
-  // foo.exe) as the first value in the command line.
-  HRESULT ParseFromString(const wchar_t* command_line);
-
-  // Parses the command line form argc/argv syntax.  Makes the assumption that
-  // argv[0] is the program name (e.g. foo.exe).
-  HRESULT ParseFromArgv(int argc, wchar_t** argv);
-
-  // TODO(Omaha): Name these methods "Required".
-  // Gets the number of required switches in the parsed command line.
-  int GetSwitchCount() const;
-
-  // Returns the required switch at a particular index.
-  HRESULT GetSwitchNameAtIndex(int index, CString* switch_name) const;
-
-  // Returns true if a required switch with the name switch_name is found.
-  bool HasSwitch(const CString& switch_name) const;
-
-  // Returns the number of required arguments for required switch switch_name.
-  HRESULT GetSwitchArgumentCount(const CString& switch_name,
-                                 int* count) const;
-
-  // Returns the value of a required switch argument at the specified offset.
-  HRESULT GetSwitchArgumentValue(const CString& switch_name,
-                                 int argument_index,
-                                 CString* argument_value) const;
-
-  // Functions that have the same functionality as the above functions,
-  // except they operate on the optional switches.
-  int GetOptionalSwitchCount() const;
-  bool HasOptionalSwitch(const CString& switch_name) const;
-  HRESULT GetOptionalSwitchNameAtIndex(int index, CString* switch_name) const;
-  HRESULT GetOptionalSwitchArgumentCount(const CString& switch_name,
-                                         int* count) const;
-  HRESULT GetOptionalSwitchArgumentValue(
-      const CString& switch_name,
-      int argument_index,
-      CString* argument_value) const;
-
- private:
-  bool IsSwitch(const CString& param) const;
-  HRESULT StripSwitchNameFromArgv(const CString& param, CString* switch_name);
-  bool IsOptionalSwitch(const CString& param) const;
-  HRESULT StripOptionalSwitchNameFromArgv(const CString& param, CString* name);
-
-  void Reset();
-
-  HRESULT AddSwitch(const CString& switch_name);
-  HRESULT AddSwitchArgument(const CString& switch_name,
-                            const CString& argument_value);
-  HRESULT AddOptionalSwitch(const CString& switch_name);
-  HRESULT AddOptionalSwitchArgument(const CString& switch_name,
-                                    const CString& argument_value);
-
-  internal::CommandLineParserArgs required_args_;
-  internal::CommandLineParserArgs optional_args_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(CommandLineParser);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_COMMAND_LINE_PARSER_H__
-
diff --git a/goopdate/command_line_parser_unittest.cc b/goopdate/command_line_parser_unittest.cc
deleted file mode 100644
index be730e9..0000000
--- a/goopdate/command_line_parser_unittest.cc
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/goopdate/command_line_parser.h"
-
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-// This will succeed since the CommandLineToArgvW function returns the
-// path to the current executable file if it's passed the empty string.
-TEST(CommandLineParserTest, ParseNullString) {
-  CommandLineParser parser;
-  EXPECT_SUCCEEDED(parser.ParseFromString(NULL));
-  EXPECT_EQ(0, parser.GetSwitchCount());
-}
-
-// This will succeed since the CommandLineToArgvW function returns the
-// path to the current executable file if it's passed the empty string.
-TEST(CommandLineParserTest, ParseEmptyString) {
-  CommandLineParser parser;
-  EXPECT_SUCCEEDED(parser.ParseFromString(_T("")));
-  EXPECT_EQ(0, parser.GetSwitchCount());
-}
-
-// This will succeed since the CommandLineToArgvW function returns the
-// path to the current executable file if it's passed the empty string.
-TEST(CommandLineParserTest, ParseSpacesOnlyString) {
-  CommandLineParser parser;
-  EXPECT_SUCCEEDED(parser.ParseFromString(_T("    ")));
-}
-
-TEST(CommandLineParserTest, ParseNullArgv) {
-  CommandLineParser parser;
-  EXPECT_FAILED(parser.ParseFromArgv(0, NULL));
-}
-
-TEST(CommandLineParserTest, CallFunctionsBeforeParse) {
-  CommandLineParser parser;
-  int arg_count = 0;
-  CString arg_value;
-  EXPECT_FALSE(parser.HasSwitch(_T("foo")));
-  EXPECT_EQ(0, parser.GetSwitchCount());
-  EXPECT_FAILED(parser.GetSwitchArgumentCount(_T("foo"), &arg_count));
-  EXPECT_FAILED(parser.GetSwitchArgumentValue(_T("foo"), 0, &arg_value));
-}
-
-TEST(CommandLineParserTest, ParseProgramNameOnly) {
-  CommandLineParser parser;
-  EXPECT_SUCCEEDED(parser.ParseFromString(_T("myprog.exe")));
-  EXPECT_EQ(0, parser.GetSwitchCount());
-}
-
-TEST(CommandLineParserTest, ValidateSwitchMixedCase) {
-  CommandLineParser parser;
-  EXPECT_SUCCEEDED(parser.ParseFromString(_T("myprog.exe /FooP")));
-  EXPECT_EQ(1, parser.GetSwitchCount());
-  EXPECT_TRUE(parser.HasSwitch(_T("foop")));
-  EXPECT_TRUE(parser.HasSwitch(_T("FooP")));
-  EXPECT_TRUE(parser.HasSwitch(_T("fOOp")));
-  EXPECT_TRUE(parser.HasSwitch(_T("FOOP")));
-  EXPECT_FALSE(parser.HasSwitch(_T("blAH")));
-}
-
-TEST(CommandLineParserTest, ParseOneSwitchNoArgs) {
-  CommandLineParser parser;
-  int arg_count = 0;
-  EXPECT_SUCCEEDED(parser.ParseFromString(_T("myprog.exe /foo")));
-  EXPECT_EQ(1, parser.GetSwitchCount());
-  EXPECT_TRUE(parser.HasSwitch(_T("foo")));
-  EXPECT_SUCCEEDED(parser.GetSwitchArgumentCount(_T("foo"), &arg_count));
-  EXPECT_EQ(0, arg_count);
-}
-
-TEST(CommandLineParserTest, ParseOneSwitchOneArg) {
-  CommandLineParser parser;
-  int arg_count = 0;
-  CString arg_value;
-  EXPECT_SUCCEEDED(parser.ParseFromString(_T("myprog.exe /foo bar")));
-  EXPECT_EQ(1, parser.GetSwitchCount());
-  EXPECT_TRUE(parser.HasSwitch(_T("foo")));
-  EXPECT_SUCCEEDED(parser.GetSwitchArgumentCount(_T("foo"), &arg_count));
-  EXPECT_EQ(1, arg_count);
-  EXPECT_SUCCEEDED(parser.GetSwitchArgumentValue(_T("foo"), 0, &arg_value));
-  EXPECT_STREQ(_T("bar"), arg_value);
-}
-
-TEST(CommandLineParserTest, ParseOneSwitchTwoArgs) {
-  CommandLineParser parser;
-  int arg_count = 0;
-  CString arg_value;
-  EXPECT_SUCCEEDED(parser.ParseFromString(_T("myprog.exe /foo bar baz")));
-  EXPECT_EQ(1, parser.GetSwitchCount());
-  EXPECT_TRUE(parser.HasSwitch(_T("foo")));
-  EXPECT_SUCCEEDED(parser.GetSwitchArgumentCount(_T("foo"), &arg_count));
-  EXPECT_EQ(2, arg_count);
-  EXPECT_SUCCEEDED(parser.GetSwitchArgumentValue(_T("foo"), 0, &arg_value));
-  EXPECT_STREQ(_T("bar"), arg_value);
-  EXPECT_SUCCEEDED(parser.GetSwitchArgumentValue(_T("foo"), 1, &arg_value));
-  EXPECT_STREQ(_T("baz"), arg_value);
-}
-
-TEST(CommandLineParserTest, ParseTwoSwitchesNoArgs) {
-  CommandLineParser parser;
-  int arg_count = 0;
-  CString arg_value;
-  EXPECT_SUCCEEDED(parser.ParseFromString(_T("myprog.exe /foo /bar")));
-  EXPECT_EQ(2, parser.GetSwitchCount());
-  EXPECT_TRUE(parser.HasSwitch(_T("foo")));
-  EXPECT_TRUE(parser.HasSwitch(_T("bar")));
-  EXPECT_SUCCEEDED(parser.GetSwitchArgumentCount(_T("foo"), &arg_count));
-  EXPECT_EQ(0, arg_count);
-  EXPECT_SUCCEEDED(parser.GetSwitchArgumentCount(_T("bar"), &arg_count));
-  EXPECT_EQ(0, arg_count);
-}
-
-TEST(CommandLineParserTest, ParseTwoSwitchesOneArgNoArg) {
-  CommandLineParser parser;
-  int arg_count = 0;
-  CString arg_value;
-  EXPECT_SUCCEEDED(parser.ParseFromString(_T("myprog.exe /foo blech /bar")));
-  EXPECT_EQ(2, parser.GetSwitchCount());
-  EXPECT_TRUE(parser.HasSwitch(_T("foo")));
-  EXPECT_TRUE(parser.HasSwitch(_T("bar")));
-  EXPECT_SUCCEEDED(parser.GetSwitchArgumentCount(_T("foo"), &arg_count));
-  EXPECT_EQ(1, arg_count);
-  EXPECT_SUCCEEDED(parser.GetSwitchArgumentValue(_T("foo"), 0, &arg_value));
-  EXPECT_STREQ(_T("blech"), arg_value);
-  EXPECT_SUCCEEDED(parser.GetSwitchArgumentCount(_T("bar"), &arg_count));
-  EXPECT_EQ(0, arg_count);
-}
-
-TEST(CommandLineParserTest, ParseArgInQuotesWithLeadingSlash) {
-  CommandLineParser parser;
-  int arg_count = 0;
-  CString arg_value;
-  EXPECT_SUCCEEDED(parser.ParseFromString(_T("f.exe /pi \"arg\" \"/sw x\"")));
-  EXPECT_EQ(1, parser.GetSwitchCount());
-  EXPECT_TRUE(parser.HasSwitch(_T("pi")));
-  EXPECT_SUCCEEDED(parser.GetSwitchArgumentCount(_T("pi"), &arg_count));
-  EXPECT_EQ(2, arg_count);
-  EXPECT_SUCCEEDED(parser.GetSwitchArgumentValue(_T("pi"), 0, &arg_value));
-  EXPECT_STREQ(_T("arg"), arg_value);
-  EXPECT_SUCCEEDED(parser.GetSwitchArgumentValue(_T("pi"), 1, &arg_value));
-  EXPECT_STREQ(_T("/sw x"), arg_value);
-}
-
-}  // namespace omaha
-
diff --git a/goopdate/command_line_unittest.cc b/goopdate/command_line_unittest.cc
deleted file mode 100644
index eb5ddf0..0000000
--- a/goopdate/command_line_unittest.cc
+++ /dev/null
@@ -1,1315 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/error.h"
-#include "omaha/common/string.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/command_line_builder.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/net/http_client.h"
-#include "omaha/testing/resource.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-// Used by extra_args_parser_unittest.cc.
-void VerifyCommandLineExtraArgs(const CommandLineExtraArgs& expected_val,
-                                const CommandLineExtraArgs& actual_val);
-
-namespace {
-
-#define YOUTUBEUPLOADEREN_TAG_WITHOUT_QUOTES \
-    _T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&") \
-    _T("appname=YouTubeUploader&needsadmin=False&lang=en")
-
-#define YOUTUBEUPLOADEREN_APP_ARGS_WITHOUT_QUOTES \
-    _T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&") \
-    _T("installerdata=YouTube%20Uploader%20Data")
-
-#define YOUTUBEUPLOADEREN_TAG \
-    _T("\"") YOUTUBEUPLOADEREN_TAG_WITHOUT_QUOTES _T("\"")
-
-#define YOUTUBEUPLOADEREN_APP_ARGS \
-    _T("\"") YOUTUBEUPLOADEREN_APP_ARGS_WITHOUT_QUOTES _T("\"")
-
-void VerifyCommandLineArgs(const CommandLineArgs& expected,
-                           const CommandLineArgs& actual) {
-  EXPECT_EQ(expected.mode, actual.mode);
-
-  EXPECT_EQ(expected.is_interactive_set, actual.is_interactive_set);
-  EXPECT_EQ(expected.is_machine_set, actual.is_machine_set);
-  EXPECT_EQ(expected.is_install_elevated, actual.is_install_elevated);
-  EXPECT_EQ(expected.is_silent_set, actual.is_silent_set);
-  EXPECT_EQ(expected.is_eula_required_set, actual.is_eula_required_set);
-  EXPECT_EQ(expected.is_offline_set, actual.is_offline_set);
-  EXPECT_EQ(expected.is_oem_set, actual.is_oem_set);
-  EXPECT_EQ(expected.is_uninstall_set, actual.is_uninstall_set);
-
-  EXPECT_STREQ(expected.extra_args_str, actual.extra_args_str);
-  EXPECT_STREQ(expected.app_args_str, actual.app_args_str);
-  EXPECT_STREQ(expected.install_source, actual.install_source);
-  EXPECT_STREQ(expected.crash_filename, actual.crash_filename);
-  EXPECT_STREQ(expected.custom_info_filename, actual.custom_info_filename);
-  EXPECT_STREQ(expected.legacy_manifest_path, actual.legacy_manifest_path);
-  EXPECT_STREQ(expected.webplugin_urldomain, actual.webplugin_urldomain);
-  EXPECT_STREQ(expected.webplugin_args, actual.webplugin_args);
-  EXPECT_STREQ(expected.code_red_metainstaller_path,
-               actual.code_red_metainstaller_path);
-
-  VerifyCommandLineExtraArgs(expected.extra, actual.extra);
-}
-
-void VerifyArgsWithSingleYouTubeUploaderEnApp(
-    const CommandLineArgs& expected_without_app,
-    const CommandLineArgs& actual,
-    bool expect_app_args,
-    bool expect_language) {
-  CommandLineArgs expected(expected_without_app);
-
-  const GUID expected_guid = {0xA4F7B07B, 0xB9BD, 0x4A33,
-                              {0xB1, 0x36, 0x96, 0xD2, 0xAD, 0xFB, 0x60, 0xCB}};
-  CommandLineAppArgs app_args;
-  app_args.app_guid = expected_guid;
-  app_args.app_name = _T("YouTubeUploader");
-  app_args.needs_admin = false;
-  if (expected.extra_args_str.IsEmpty()) {
-    expected.extra_args_str = YOUTUBEUPLOADEREN_TAG_WITHOUT_QUOTES;
-  }
-  if (expect_language) {
-    expected.extra.language = _T("en");
-  }
-  if (expect_app_args) {
-    expected.app_args_str =
-        YOUTUBEUPLOADEREN_APP_ARGS_WITHOUT_QUOTES;
-    app_args.encoded_installer_data = _T("YouTube%20Uploader%20Data");
-  }
-
-  expected.extra.apps.push_back(app_args);
-  VerifyCommandLineArgs(expected, actual);
-}
-
-}  // namespace
-
-void VerifyCommandLineExtraArgs(const CommandLineExtraArgs& expected_val,
-                                const CommandLineExtraArgs& actual_val) {
-  EXPECT_EQ(expected_val.apps.size(), actual_val.apps.size());
-
-  EXPECT_STREQ(GuidToString(expected_val.installation_id),
-               GuidToString(actual_val.installation_id));
-  EXPECT_STREQ(expected_val.brand_code, actual_val.brand_code);
-  EXPECT_STREQ(expected_val.client_id, actual_val.client_id);
-  EXPECT_STREQ(expected_val.referral_id, actual_val.referral_id);
-  EXPECT_EQ(expected_val.browser_type, actual_val.browser_type);
-  EXPECT_EQ(expected_val.usage_stats_enable, actual_val.usage_stats_enable);
-  EXPECT_STREQ(expected_val.language, actual_val.language);
-
-  for (size_t i = 0; i < actual_val.apps.size(); ++i) {
-    CommandLineAppArgs expected = expected_val.apps[i];
-    CommandLineAppArgs actual = actual_val.apps[i];
-
-    EXPECT_STREQ(GuidToString(expected.app_guid),
-                 GuidToString(actual.app_guid));
-    EXPECT_STREQ(expected.app_name, actual.app_name);
-    EXPECT_EQ(expected.needs_admin, actual.needs_admin);
-    EXPECT_STREQ(expected.ap, actual.ap);
-    EXPECT_STREQ(expected.tt_token, actual.tt_token);
-    EXPECT_STREQ(expected.encoded_installer_data,
-                 actual.encoded_installer_data);
-    EXPECT_STREQ(expected.install_data_index, actual.install_data_index);
-  }
-}
-
-class CommandLineTest : public testing::Test {
- protected:
-  CommandLineArgs args_;
-  CommandLineArgs expected_;
-};
-
-TEST(CommandLineSimpleTest, GetCmdLineTail1) {
-  EXPECT_STREQ(_T(""),  GetCmdLineTail(_T("")));
-}
-
-TEST(CommandLineSimpleTest, GetCmdLineTail2) {
-  EXPECT_STREQ(_T(""), GetCmdLineTail(_T("a")));
-}
-TEST(CommandLineSimpleTest, GetCmdLineTail3) {
-  EXPECT_STREQ(_T(""), GetCmdLineTail(_T("goopdate.exe")));
-}
-
-TEST(CommandLineSimpleTest, GetCmdLineTail4) {
-  // Double quotes.
-  EXPECT_STREQ(_T(""), GetCmdLineTail(_T("\"Google Update.exe\"")));
-}
-
-TEST(CommandLineSimpleTest, GetCmdLineTail5) {
-  // Argument.
-  EXPECT_STREQ(_T("foobar"), GetCmdLineTail(_T("goopdate.exe foobar")));
-}
-
-TEST(CommandLineSimpleTest, GetCmdLineTail6) {
-  // Double quotes and argument.
-  EXPECT_STREQ(_T("foobar"),
-               GetCmdLineTail(_T("\"Google Update.exe\" foobar")));
-}
-
-TEST(CommandLineSimpleTest, GetCmdLineTail7) {
-  // Double quotes and inner double quote and argument.
-  EXPECT_STREQ(_T("foobar"),
-               GetCmdLineTail(_T("\"Google\"\" Update.exe\" foobar")));
-}
-
-TEST(CommandLineSimpleTest, GetCmdLineTail8) {
-  // Double quotes and two arguments.
-  EXPECT_STREQ(_T("foo bar"),
-               GetCmdLineTail(_T("\"Google Update.exe\" foo bar")));
-}
-
-TEST(CommandLineSimpleTest, GetCmdLineTail9) {
-  // Double quotes and one argument with quotes.
-  EXPECT_STREQ(_T("\"foo bar\""),
-               GetCmdLineTail(_T("\"Google Update.exe\" \"foo bar\"")));
-}
-
-TEST(CommandLineSimpleTest, GetCmdLineTail10) {
-  // \t as white space.
-  EXPECT_STREQ(_T("foo bar"),
-               GetCmdLineTail(_T("\"Google Update.exe\"\tfoo bar")));
-}
-
-TEST(CommandLineSimpleTest, GetCmdLineTail11) {
-  // Trailing space.
-  EXPECT_STREQ(_T("foo bar "),
-               GetCmdLineTail(_T("\"Google Update.exe\" foo bar ")));
-}
-
-//
-// This block contains the positive test cases for each of the command lines.
-// If you add a new command line parameter permutation, add a test case here.
-//
-
-// TODO(omaha): Add some negative failure cases to the command lines (like
-// /install without "extraargs").
-
-// TODO(omaha): This is an Omaha1 back-compat issue.  Omaha2 should _never_
-// call googleupdate.exe with no arguments.  So when we stop supporting Omaha1
-// handoffs we should remove this support.
-  // Parse empty command line.
-TEST_F(CommandLineTest, ParseCommandLine_Empty) {
-  const TCHAR* kCmdLine = _T("");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_NOARGS;
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path>
-// Remember that by convention the OS is passing us the program executable
-// name as the first token in the command line and the parsing code skips that.
-TEST_F(CommandLineTest, ParseCommandLine_ProgramNameOnly) {
-  const TCHAR* kCmdLine = _T("goopdate.exe");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_NOARGS;
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /svc
-TEST_F(CommandLineTest, ParseCommandLine_Svc) {
-  const TCHAR* kCmdLine = _T("\"C:\\Program Files\\Google\\Common\\Update\\")
-                           _T("1.0.18.0\\goopdate.exe\" /svc");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_SERVICE;
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> -Embedding. The -Embedding text is injected via COM.
-TEST_F(CommandLineTest, ParseCommandLine_Server) {
-  const TCHAR* kCmdLine = _T("goopdate.exe -Embedding");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_COMSERVER;
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /install "extraargs"
-TEST_F(CommandLineTest, ParseCommandLine_Install) {
-  const TCHAR* kCmdLine =
-      _T("goopdate.exe /install ")
-      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-      _T("appname=YouTubeUploader&needsadmin=False&")
-      _T("appguid={C7A9A2F5-C4F9-42d3-8A8B-55086A205468}&")
-      _T("appname=TestApp&needsadmin=true&lang=en\"");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_INSTALL;
-
-  expected_.extra_args_str = _T("appguid=")
-                            _T("{A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                            _T("appname=YouTubeUploader&needsadmin=False&")
-                            _T("appguid=")
-                            _T("{C7A9A2F5-C4F9-42d3-8A8B-55086A205468}&")
-                            _T("appname=TestApp&needsadmin=true&lang=en");
-  CommandLineAppArgs app_args;
-  const GUID expected_guid = {0xA4F7B07B, 0xB9BD, 0x4A33,
-                              {0xB1, 0x36, 0x96, 0xD2, 0xAD, 0xFB, 0x60, 0xCB}};
-  app_args.app_guid = expected_guid;
-  app_args.app_name = _T("YouTubeUploader");
-  app_args.needs_admin = false;
-  expected_.extra.apps.push_back(app_args);
-
-  CommandLineAppArgs app_args1;
-  app_args1.app_guid =
-      StringToGuid(_T("{C7A9A2F5-C4F9-42d3-8A8B-55086A205468}"));
-  app_args1.app_name = _T("TestApp");
-  app_args1.needs_admin = true;
-  expected_.extra.apps.push_back(app_args1);
-  expected_.extra.language = _T("en");
-
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /install "extraargs" /oem
-TEST_F(CommandLineTest, ParseCommandLine_InstallWithOem) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /oem");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_INSTALL;
-  expected_.is_oem_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /install "extraargs" [/oem
-// This tests how we handle a switch with a bracket, which represents optional
-// parameters in a rule, when it appears in an actual command line.
-TEST_F(CommandLineTest, ParseCommandLine_InstallWithOemIgnored) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
-                          _T(" [/oem");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_INSTALL;
-  expected_.is_oem_set = false;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /install "extraargs" /appargs <appargs>
-TEST_F(CommandLineTest, ParseCommandLine_InstallWithAppArgs) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS;
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_INSTALL;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
-}
-
-// Parse: <path> /install "extraargs" /oem /appargs <appargs>
-TEST_F(CommandLineTest, ParseCommandLine_InstallWithOemAppArgs) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /oem")
-                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS;
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_INSTALL;
-  expected_.is_oem_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
-}
-
-// Parse: <path> /install "extraargs" /appargs <appargs> /silent
-TEST_F(CommandLineTest, ParseCommandLine_InstallWithAppArgsSilent) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
-                          _T(" /silent");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_INSTALL;
-  expected_.is_silent_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
-}
-
-// Parse: <path> /install "extraargs" /oem /appargs <appargs> /silent
-TEST_F(CommandLineTest, ParseCommandLine_InstallWithOemAppArgsSilent) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /oem")
-                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
-                          _T(" /silent");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_INSTALL;
-  expected_.is_oem_set = true;
-  expected_.is_silent_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
-}
-
-// Parse:
-//  <path> /install "extraargs" /oem /appargs <appargs> /silent /eularequired
-TEST_F(CommandLineTest,
-       ParseCommandLine_InstallWithOemAppArgsSilentEulaRequired) {
-  const TCHAR* kCmdLine =
-      _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
-      _T(" /oem")
-      _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
-      _T(" /silent")
-      _T(" /eularequired");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_INSTALL;
-  expected_.is_oem_set = true;
-  expected_.is_silent_set = true;
-  expected_.is_eula_required_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
-}
-
-// Parse: <path> /install "extraargs" /eularequired
-TEST_F(CommandLineTest, ParseCommandLine_InstallEulaRequired) {
-  const TCHAR* kCmdLine =
-      _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
-      _T(" /eularequired");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_INSTALL;
-  expected_.is_eula_required_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /install "extraargs" /oem /installsource oneclick
-TEST_F(CommandLineTest, ParseCommandLine_InstallWithOemAndSource) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /oem")
-                          _T(" /installsource oneclick");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_INSTALL;
-  expected_.is_oem_set = true;
-  expected_.install_source = _T("oneclick");
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /install "extraargs" /installsource oneclick
-TEST_F(CommandLineTest, ParseCommandLine_InstallWithSource) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /installsource oneclick");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_INSTALL;
-  expected_.install_source = _T("oneclick");
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /install "extraargs" /silent
-TEST_F(CommandLineTest, ParseCommandLine_InstallSilent) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /silent");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_INSTALL;
-  expected_.is_silent_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /install "extraargs" /silent /oem
-TEST_F(CommandLineTest, ParseCommandLine_InstallSilentWithOem) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /silent")
-                          _T(" /oem");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_INSTALL;
-  expected_.is_silent_set = true;
-  expected_.is_oem_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /install "extraargs" /installsource oneclick /silent
-TEST_F(CommandLineTest, ParseCommandLine_InstallSilentWithSource) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /installsource oneclick")
-                          _T(" /silent");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_INSTALL;
-  expected_.install_source = _T("oneclick");
-  expected_.is_silent_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /install "extraargs" /installelevated
-TEST_F(CommandLineTest, ParseCommandLine_InstallElevated) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /installelevated");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_INSTALL;
-  expected_.is_install_elevated = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /install "extraargs" /installelevated /installsource oneclick
-TEST_F(CommandLineTest, ParseCommandLine_InstallElevatedWithSource) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /install ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /installelevated /installsource oneclick");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_INSTALL;
-  expected_.is_install_elevated = true;
-  expected_.install_source = _T("oneclick");
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /ig "extraargs"
-TEST_F(CommandLineTest, ParseCommandLine_Ig) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ig ") YOUTUBEUPLOADEREN_TAG;
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_IG;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /ig "extraargs" /offlineinstall
-// While legal, this command line is not supported.
-TEST_F(CommandLineTest, ParseCommandLine_IgOfflineWithoutInstallSource) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ig ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /offlineinstall");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_IG;
-  expected_.is_offline_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /ig "extraargs" /installsource oneclick
-TEST_F(CommandLineTest, ParseCommandLine_IgWithSource) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ig ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /installsource oneclick");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_IG;
-  expected_.install_source = _T("oneclick");
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /ig "extraargs" /installsource offline /offlineinstall
-TEST_F(CommandLineTest, ParseCommandLine_IgWithSourceOffline) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ig ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /installsource offline /offlineinstall");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_IG;
-  expected_.install_source = _T("offline");
-  expected_.is_offline_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /ig "extraargs" /appargs <appargs>
-//               /installsource offline /offlineinstall
-TEST_F(CommandLineTest, ParseCommandLine_IgWithAppArgsSourceOffline) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ig ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
-                          _T(" /installsource offline /offlineinstall");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_IG;
-  expected_.install_source = _T("offline");
-  expected_.is_offline_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
-}
-
-// Parse: <path> /ig "extraargs" /silent
-TEST_F(CommandLineTest, ParseCommandLine_IgSilent) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ig ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /silent");
-
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_IG;
-  expected_.is_silent_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /ig "extraargs" /silent /offlineinstall
-TEST_F(CommandLineTest, ParseCommandLine_IgSilentOfflineWithoutInstallSource) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ig ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /silent")
-                          _T(" /offlineinstall");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_IG;
-  expected_.is_silent_set = true;
-  expected_.is_offline_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /ig "extraargs" /installsource oneclick /silent
-TEST_F(CommandLineTest, ParseCommandLine_IgSilentWithSource) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ig ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /installsource oneclick")
-                          _T(" /silent");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_IG;
-  expected_.install_source = _T("oneclick");
-  expected_.is_silent_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /ig "extraargs" /installsource oneclick /silent /offlineinstall
-TEST_F(CommandLineTest, ParseCommandLine_IgSilentWithSourceOffline) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ig ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /installsource oneclick")
-                          _T(" /silent")
-                          _T(" /offlineinstall");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_IG;
-  expected_.install_source = _T("oneclick");
-  expected_.is_silent_set = true;
-  expected_.is_offline_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /ig "extraargs" /appargs <appargs>
-TEST_F(CommandLineTest, ParseCommandLine_IgWithAppArgs) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ig ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS;
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_IG;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
-}
-
-// Parse: <path> /ig "extraargs" /appargs <appargs> /silent
-TEST_F(CommandLineTest, ParseCommandLine_IgSilentWithAppArgs) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ig ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
-                          _T(" /silent");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_IG;
-  expected_.is_silent_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
-}
-
-// Parse: <path> /ig "extraargs" /appargs <appargs> /offlineinstall
-TEST_F(CommandLineTest, ParseCommandLine_IgWithAppArgsOffline) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ig ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
-                          _T(" /offlineinstall");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_IG;
-  expected_.is_offline_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
-}
-
-// Parse: <path> /ig "extraargs" /appargs <appargs> /silent /offlineinstall
-TEST_F(CommandLineTest, ParseCommandLine_IgSilentWithAppArgsOffline) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ig ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
-                          _T(" /silent")
-                          _T(" /offlineinstall");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_IG;
-  expected_.is_silent_set = true;
-  expected_.is_offline_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
-}
-
-// Parse: <path> /ig "extraargs" /appargs <appargs>
-//               /installsource offline /silent /offlineinstall
-TEST_F(CommandLineTest, ParseCommandLine_IgSilentWithAppArgsSourceOffline) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ig ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
-                          _T(" /installsource offline")
-                          _T(" /silent")
-                          _T(" /offlineinstall");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_IG;
-  expected_.install_source = _T("offline");
-  expected_.is_silent_set = true;
-  expected_.is_offline_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
-}
-
-// Parse: <path> /ig "extraargs" /appargs <appargs>
-//               /installsource offline /silent /offlineinstall /eularequired
-TEST_F(CommandLineTest,
-       ParseCommandLine_IgSilentWithAppArgsSourceOfflineEulaRequired) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ig ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
-                          _T(" /installsource offline")
-                          _T(" /silent")
-                          _T(" /offlineinstall")
-                          _T(" /eularequired");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_IG;
-  expected_.install_source = _T("offline");
-  expected_.is_silent_set = true;
-  expected_.is_offline_set = true;
-  expected_.is_eula_required_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
-}
-
-// Parse: <path> /ig "extraargs" /eularequired
-TEST_F(CommandLineTest, ParseCommandLine_IgEulaRequired) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ig ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /eularequired");
-
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_IG;
-  expected_.is_eula_required_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /handoff "extraargs"
-TEST_F(CommandLineTest, ParseCommandLine_Handoff) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG;
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /handoff "extraargs" /offlineinstall
-TEST_F(CommandLineTest, ParseCommandLine_HandoffOfflineWithoutInstallSource) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /offlineinstall");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  expected_.is_offline_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /handoff "extraargs" /installsource "asd"
-TEST_F(CommandLineTest, ParseCommandLine_HandoffWithSource) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /installsource oneclick");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  expected_.install_source = _T("oneclick");
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /handoff "extraargs" /installsource offline /offlineinstall
-TEST_F(CommandLineTest, ParseCommandLine_HandoffWithSourceOffline) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /installsource offline")
-                          _T(" /offlineinstall");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  expected_.install_source = _T("offline");
-  expected_.is_offline_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /handoff "extraargs" /appargs <appargs>
-//               /installsource offline /offlineinstall
-TEST_F(CommandLineTest, ParseCommandLine_HandoffWithAppArgsSourceOffline) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
-                          _T(" /installsource offline /offlineinstall");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  expected_.install_source = _T("offline");
-  expected_.is_offline_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
-}
-
-// Parse: <path> /handoff "extraargs" /silent
-TEST_F(CommandLineTest, ParseCommandLine_HandoffSilent) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /silent");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  expected_.is_silent_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /handoff "extraargs" /silent /offlineinstall
-TEST_F(CommandLineTest,
-       ParseCommandLine_HandoffSilentOfflineWithoutInstallSource) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /silent")
-                          _T(" /offlineinstall");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  expected_.is_silent_set = true;
-  expected_.is_offline_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /handoff "extraargs" /installsource "asd" /silent
-TEST_F(CommandLineTest, ParseCommandLine_HandoffSilentWithSource) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /installsource oneclick")
-                          _T(" /silent");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  expected_.install_source = _T("oneclick");
-  expected_.is_silent_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse:
-//   <path> /handoff "extraargs" /installsource offline /silent /offlineinstall
-TEST_F(CommandLineTest, ParseCommandLine_HandoffSilentWithSourceOffline) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /installsource offline")
-                          _T(" /silent")
-                          _T(" /offlineinstall");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  expected_.install_source = _T("offline");
-  expected_.is_silent_set = true;
-  expected_.is_offline_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /handoff "extraargs" /appargs <appargs>
-TEST_F(CommandLineTest, ParseCommandLine_HandoffWithAppArgs) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS;
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
-}
-
-// Parse: <path> /handoff "extraargs" /appargs <appargs> /silent
-TEST_F(CommandLineTest, ParseCommandLine_HandoffSilentWithAppArgs) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
-                          _T(" /silent");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  expected_.is_silent_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
-}
-
-// Parse: <path> /handoff "extraargs" /appargs <appargs>
-//               /installsource offline /silent /offlineinstall
-TEST_F(CommandLineTest,
-       ParseCommandLine_HandoffSilentWithAppArgsSourceOffline) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
-                          _T(" /installsource offline")
-                          _T(" /silent")
-                          _T(" /offlineinstall");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  expected_.install_source = _T("offline");
-  expected_.is_silent_set = true;
-  expected_.is_offline_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
-}
-
-// Parse: <path> /handoff "extraargs" /appargs <appargs>
-//               /installsource offline /silent /offlineinstall /eularequired
-TEST_F(CommandLineTest,
-       ParseCommandLine_HandoffSilentWithAppArgsSourceOfflineEulaRequired) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /appargs ") YOUTUBEUPLOADEREN_APP_ARGS
-                          _T(" /installsource offline")
-                          _T(" /silent")
-                          _T(" /offlineinstall")
-                          _T(" /eularequired");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  expected_.install_source = _T("offline");
-  expected_.is_silent_set = true;
-  expected_.is_offline_set = true;
-  expected_.is_eula_required_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, true, true);
-}
-
-// Parse: <path> /handoff "extraargs" /eularequired
-TEST_F(CommandLineTest, ParseCommandLine_HandoffEulaRequired) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /handoff ") YOUTUBEUPLOADEREN_TAG
-                          _T(" /eularequired");
-
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  expected_.is_eula_required_set = true;
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /ug
-TEST_F(CommandLineTest, ParseCommandLine_Ug) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ug");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_UG;
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /ua
-TEST_F(CommandLineTest, ParseCommandLine_UaNoInstallSource) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ua");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_UA;
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /ua /installsource core
-TEST_F(CommandLineTest, ParseCommandLine_Ua) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ua /installsource core");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_UA;
-  expected_.install_source = _T("core");
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /ua /installsource core /uninstall
-TEST_F(CommandLineTest, ParseCommandLine_UaWithUninstall) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ua /installsource core /uninstall");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_UA;
-  expected_.install_source = _T("core");
-  expected_.is_uninstall_set = true;
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /update
-TEST_F(CommandLineTest, ParseCommandLine_Update) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /update");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_UPDATE;
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /netdiags
-TEST_F(CommandLineTest, ParseCommandLine_NetDiags) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /netdiags");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_NETDIAGS;
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /regserver
-TEST_F(CommandLineTest, ParseCommandLine_Regserver) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /regserver");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_REGSERVER;
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /unregserver
-TEST_F(CommandLineTest, ParseCommandLine_Unregserver) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /unregserver");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_UNREGSERVER;
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /registerproduct
-TEST_F(CommandLineTest, ParseCommandLine_RegisterProduct) {
-  const TCHAR* kCmdLine =
-      _T("goopdate.exe /registerproduct ")
-      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-      _T("appname=YouTubeUploader&needsadmin=False\"");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_REGISTER_PRODUCT;
-  expected_.extra_args_str = _T("appguid=")
-                            _T("{A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                            _T("appname=YouTubeUploader&needsadmin=False");
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, false);
-}
-
-// Parse: <path> /registerproduct /installsource enterprisemsi
-TEST_F(CommandLineTest, ParseCommandLine_RegisterProductWithInstallSource) {
-  const TCHAR* kCmdLine =
-      _T("goopdate.exe /registerproduct ")
-      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-      _T("appname=YouTubeUploader&needsadmin=False\"")
-      _T(" /installsource enterprisemsi");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-  expected_.mode = COMMANDLINE_MODE_REGISTER_PRODUCT;
-  expected_.install_source = _T("enterprisemsi");
-  expected_.extra_args_str = _T("appguid=")
-                            _T("{A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                            _T("appname=YouTubeUploader&needsadmin=False");
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, false);
-}
-
-// Parse: <path> /unregisterproduct
-TEST_F(CommandLineTest, ParseCommandLine_UnregisterProduct) {
-  const TCHAR* kCmdLine =
-      _T("goopdate.exe /unregisterproduct ")
-      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-      _T("appname=YouTubeUploader&needsadmin=False\"");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_UNREGISTER_PRODUCT;
-  expected_.extra_args_str = _T("appguid=")
-                            _T("{A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-                            _T("appname=YouTubeUploader&needsadmin=False");
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, false);
-}
-
-// Parse: <path> /c
-TEST_F(CommandLineTest, ParseCommandLine_Core) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /c");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_CORE;
-  expected_.is_crash_handler_disabled = false;
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /c /nocrashserver
-TEST_F(CommandLineTest, ParseCommandLine_CoreNoCrashHandler) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /c /nocrashserver");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_CORE;
-  expected_.is_crash_handler_disabled = true;
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /crash
-TEST_F(CommandLineTest, ParseCommandLine_Crash) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /crash");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_CRASH;
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /report crash_file
-TEST_F(CommandLineTest, ParseCommandLine_Report) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /report C:\\foo\\crash.dmp");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_REPORTCRASH;
-  expected_.crash_filename = _T("C:\\foo\\crash.dmp");
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /report crash_file /machine
-TEST_F(CommandLineTest, ParseCommandLine_ReportMachine) {
-  const TCHAR* kCmdLine =
-      _T("goopdate.exe /report C:\\foo\\crash.dmp /machine");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_REPORTCRASH;
-  expected_.crash_filename = _T("C:\\foo\\crash.dmp");
-  expected_.is_machine_set = true;
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /report crash_file
-TEST_F(CommandLineTest, ParseCommandLine_ReportWithCustomInfo) {
-  const TCHAR* kCmdLine =
-    _T("goopdate.exe /report C:\\foo.dmp /custom_info_filename C:\\foo.txt");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_REPORTCRASH;
-  expected_.crash_filename = _T("C:\\foo.dmp");
-  expected_.custom_info_filename = _T("C:\\foo.txt");
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /report /i crash_file
-TEST_F(CommandLineTest, ParseCommandLine_ReportInteractive) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /report /i C:\\foo\\crash.dmp");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_REPORTCRASH;
-  expected_.is_interactive_set = true;
-  expected_.crash_filename = _T("C:\\foo\\crash.dmp");
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-// Parse: <path> /report /i crash_file /machine
-TEST_F(CommandLineTest, ParseCommandLine_ReportMachineInteractive) {
-  const TCHAR*
-      kCmdLine = _T("goopdate.exe /report /i C:\\foo\\crash.dmp /machine");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_REPORTCRASH;
-  expected_.is_machine_set = true;
-  expected_.is_interactive_set = true;
-  expected_.crash_filename = _T("C:\\foo\\crash.dmp");
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-TEST_F(CommandLineTest, ParseCommandLine_CodeRedCheck) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /cr");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_CODE_RED_CHECK;
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-TEST_F(CommandLineTest, ParseCommandLine_WebPlugin) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /pi \"http://gears.google.com/\" ")
-                          _T("\"/install foo\" /installsource oneclick ");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_WEBPLUGIN;
-  expected_.webplugin_urldomain = _T("http://gears.google.com/");
-  expected_.webplugin_args = _T("/install foo");
-  expected_.install_source = _T("oneclick");
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-TEST_F(CommandLineTest, ParseCommandLine_WebPluginUrlEscaped) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /pi \"http://gears.google.com/\" ")
-                          _T("\"/install%20foo\" /installsource oneclick ");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_WEBPLUGIN;
-  expected_.webplugin_urldomain = _T("http://gears.google.com/");
-  expected_.webplugin_args = _T("/install foo");
-  expected_.install_source = _T("oneclick");
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-TEST_F(CommandLineTest, ParseCommandLine_WebPluginTestStringTrim) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /pi ")
-                          _T("\"  http://gears.google.com/   \"  ")
-                          _T("\"/install foo\" /installsource oneclick ");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_WEBPLUGIN;
-  expected_.webplugin_urldomain = _T("http://gears.google.com/");
-  expected_.webplugin_args = _T("/install foo");
-  expected_.install_source = _T("oneclick");
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-TEST_F(CommandLineTest, ParseCommandLine_UiNoLanguage) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ui \"manifestfilename.xml\"");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_LEGACYUI;
-  expected_.legacy_manifest_path = _T("manifestfilename.xml");
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-TEST_F(CommandLineTest, ParseCommandLine_UiWithLanguage) {
-  const TCHAR* kCmdLine =
-      _T("goopdate.exe /ui /lang fr \"manifestfilename.xml\"");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_LEGACYUI;
-  expected_.legacy_manifest_path = _T("manifestfilename.xml");
-  expected_.extra.language = _T("fr");
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-TEST_F(CommandLineTest, ParseCommandLine_UiUser) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /uiuser file.gup");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF;
-  expected_.legacy_manifest_path = _T("file.gup");
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-TEST_F(CommandLineTest, ParseCommandLine_Recover) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /recover repairfile.exe");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_RECOVER;
-  expected_.code_red_metainstaller_path = _T("repairfile.exe");
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-TEST_F(CommandLineTest, ParseCommandLine_RecoverMachine) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /recover /machine repfile.exe");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_RECOVER;
-  expected_.is_machine_set = true;
-  expected_.code_red_metainstaller_path = _T("repfile.exe");
-  VerifyCommandLineArgs(expected_, args_);
-}
-
-//
-// These are additional failure cases against the command line parsing.
-// Everything from here on down should fail ParseCommandLine().
-//
-
-
-// Parse: <path> manifest_file
-TEST_F(CommandLineTest, ParseCommandLine_GoopdateJustArg) {
-  const TCHAR* kCmdLine = _T("goopdate.exe \"foo bar\"");
-  ExpectAsserts expect_asserts;
-  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
-}
-
-// Parse: <path> /install manifest_file manifest_file
-// Fails since this is an invalid command line set.
-TEST_F(CommandLineTest, ParseCommandLine_Invalid) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /install \"foo bar\" foobar");
-  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
-}
-
-// Parse: <path> /recover
-TEST_F(CommandLineTest, Recover_WithoutFile) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /recover");
-  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
-}
-
-// Parse: <path> /machine
-TEST_F(CommandLineTest, MachineWithoutRecover) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /machine");
-  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
-}
-
-TEST_F(CommandLineTest, ExtraArgsHasDoubleQuoteInTheMiddle) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /install \"some_\"file\"");
-  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
-}
-
-TEST_F(CommandLineTest, CommandsNotSeparatedBySpaces) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /recover/machine");
-  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
-}
-
-TEST_F(CommandLineTest, CommandsDoNotHaveForwardSlashes) {
-  const TCHAR* kCmdLine = _T("goopdate.exe recover machine");
-  ExpectAsserts expect_asserts;
-  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
-}
-
-TEST_F(CommandLineTest, UnknownParameter) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /someunknowncommand");
-  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
-}
-
-TEST_F(CommandLineTest, UiWithLangNoLanguage) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /ui /lang \"manifestfilename.xml\"");
-  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
-}
-
-TEST_F(CommandLineTest, WebPluginInstallSourceInvalid_IncorrectValue) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /installsource invalid /pi ")
-                          _T("\"  http://gears.google.com/   \"  ");
-  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
-}
-
-TEST_F(CommandLineTest, WebPluginInstallSourceInvalid_Empty) {
-  const TCHAR* kCmdLine = _T("goopdate.exe /installsource /pi ")
-                          _T("\"  http://gears.google.com/   \"  ");
-  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
-}
-
-// Parse: <path> /handoff "extraargs" /lang "en"
-TEST_F(CommandLineTest, ParseCommandLine_HandoffLegacy) {
-  const TCHAR* kCmdLine =
-      _T("goopdate.exe /handoff ")
-      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-      _T("appname=YouTubeUploader&needsadmin=False\"")
-      _T(" /lang en");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  expected_.extra_args_str =
-      _T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-      _T("appname=YouTubeUploader&needsadmin=False");
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /handoff "extraargs" /installsource "asd" /lang "en"
-TEST_F(CommandLineTest, ParseCommandLine_HandoffWithSourceLegacy) {
-  const TCHAR* kCmdLine =
-      _T("goopdate.exe /handoff ")
-      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-      _T("appname=YouTubeUploader&needsadmin=False\"")
-      _T(" /installsource oneclick /lang en");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  expected_.install_source = _T("oneclick");
-  expected_.extra_args_str =
-      _T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-      _T("appname=YouTubeUploader&needsadmin=False");
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /handoff "extraargs" /installsource "oneclick" /lang "en"
-TEST_F(CommandLineTest, ParseCommandLine_HandoffWithSourceLegacyBoth) {
-  const TCHAR* kCmdLine =
-      _T("goopdate.exe /handoff ")
-      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-      _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
-      _T(" /installsource oneclick /lang en");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  expected_.install_source = _T("oneclick");
-  expected_.extra_args_str =
-      _T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-      _T("appname=YouTubeUploader&needsadmin=False&lang=en");
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-// Parse: <path> /install "extraargs" /lang en
-TEST_F(CommandLineTest, ParseCommandLine_InstallLegacy) {
-  const TCHAR* kCmdLine =
-      _T("goopdate.exe /install ")
-      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-      _T("appname=YouTubeUploader&needsadmin=False&")
-      _T("appguid={C7A9A2F5-C4F9-42d3-8A8B-55086A205468}&")
-      _T("appname=TestApp&needsadmin=true\" /lang en");
-  EXPECT_FAILED(ParseCommandLine(kCmdLine, &args_));
-}
-
-// Parse: <path> /install "extraargs" /installsource oneclick /lang en
-TEST_F(CommandLineTest, ParseCommandLine_InstallWithSourceLegacy) {
-  const TCHAR* kCmdLine =
-      _T("goopdate.exe /install ")
-      _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-      _T("appname=YouTubeUploader&needsadmin=False\"")
-      _T(" /installsource oneclick /lang en");
-  EXPECT_SUCCEEDED(ParseCommandLine(kCmdLine, &args_));
-
-  expected_.mode = COMMANDLINE_MODE_INSTALL;
-  expected_.install_source = _T("oneclick");
-  expected_.extra_args_str =
-      _T("appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&")
-      _T("appname=YouTubeUploader&needsadmin=False");
-  VerifyArgsWithSingleYouTubeUploaderEnApp(expected_, args_, false, true);
-}
-
-}  // namespace omaha
-
diff --git a/goopdate/command_line_validator.cc b/goopdate/command_line_validator.cc
deleted file mode 100644
index 4e11746..0000000
--- a/goopdate/command_line_validator.cc
+++ /dev/null
@@ -1,256 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/goopdate/command_line_validator.h"
-
-#include <atlbase.h>
-
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/command_line_parser.h"
-
-namespace omaha {
-
-CommandLineValidator::CommandLineValidator() : scenario_sequence_number_(0) {
-}
-
-CommandLineValidator::~CommandLineValidator() {
-  Clear();
-}
-
-HRESULT CommandLineValidator::CreateScenario(const CString& scenario_name) {
-  if (scenarios_.find(scenario_name) != scenarios_.end()) {
-    return E_INVALIDARG;
-  }
-
-  ScenarioParameters scenario_parameters;
-  scenarios_[scenario_name] = scenario_parameters;
-  return S_OK;
-}
-
-// TODO(Omaha): Instead of creating the scenario in the map then populating it,
-// which requires these methods to know about the map, verify the scenario
-// exists, etc. - why not build the scenario then add it to the map? That seems
-// more straightforward.
-HRESULT CommandLineValidator::AddScenarioParameter(
-    const CString& scenario_name,
-    const CString& switch_name,
-    int num_required_parameters) {
-  MapScenariosIter iter = scenarios_.find(scenario_name);
-  if (iter == scenarios_.end()) {
-    return E_INVALIDARG;
-  }
-
-  ScenarioParameter* scenario_parameter =
-      new ScenarioParameter(switch_name, num_required_parameters);
-  (*iter).second.required.push_back(scenario_parameter);
-  return S_OK;
-}
-
-HRESULT CommandLineValidator::AddOptionalScenarioParameter(
-    const CString& scenario_name,
-    const CString& switch_name,
-    int num_required_parameters) {
-  MapScenariosIter iter = scenarios_.find(scenario_name);
-  if (iter == scenarios_.end()) {
-    return E_INVALIDARG;
-  }
-
-  ScenarioParameter* scenario_parameter =
-      new ScenarioParameter(switch_name, num_required_parameters);
-  (*iter).second.optional.push_back(scenario_parameter);
-  return S_OK;
-}
-
-HRESULT CommandLineValidator::CreateScenarioFromCmdLine(
-    const CString& command_line,
-    CString* scenario_name) {
-  ASSERT1(scenario_name);
-
-  CommandLineParser parser;
-  HRESULT hr = parser.ParseFromString(command_line);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // Generate a unique scenario name.
-  CString scenario_name_str;
-  do {
-    ++scenario_sequence_number_;
-    scenario_name_str.Format(_T("scenario_%d"), scenario_sequence_number_);
-  } while (scenarios_.find(scenario_name_str) != scenarios_.end());
-
-  CreateScenario(scenario_name_str);
-
-  int switch_count = parser.GetSwitchCount();
-  for (int idx_switch = 0; idx_switch < switch_count; ++idx_switch) {
-    CString switch_name;
-    hr = parser.GetSwitchNameAtIndex(idx_switch, &switch_name);
-    if (FAILED(hr)) {
-      return hr;
-    }
-
-    int arg_count = 0;
-    hr = parser.GetSwitchArgumentCount(switch_name, &arg_count);
-    if (FAILED(hr)) {
-      return hr;
-    }
-
-    hr = AddScenarioParameter(scenario_name_str, switch_name, arg_count);
-    if (FAILED(hr)) {
-      return hr;
-    }
-  }
-
-  switch_count = parser.GetOptionalSwitchCount();
-  for (int idx_switch = 0; idx_switch < switch_count; ++idx_switch) {
-    CString switch_name;
-    hr = parser.GetOptionalSwitchNameAtIndex(idx_switch, &switch_name);
-    if (FAILED(hr)) {
-      return hr;
-    }
-
-    int arg_count = 0;
-    hr = parser.GetOptionalSwitchArgumentCount(switch_name, &arg_count);
-    if (FAILED(hr)) {
-      return hr;
-    }
-
-    hr = AddOptionalScenarioParameter(scenario_name_str,
-                                      switch_name,
-                                      arg_count);
-    if (FAILED(hr)) {
-      return hr;
-    }
-  }
-
-  *scenario_name = scenario_name_str;
-
-  return S_OK;
-}
-
-HRESULT CommandLineValidator::Validate(
-    const CommandLineParser& command_line_parser,
-    CString* scenario_name) const {
-  // Attempt to verify the data within the command_line_parser against each of
-  // the scenarios.
-  MapScenariosConstIter scenarios_iter;
-  for (scenarios_iter = scenarios_.begin();
-       scenarios_iter != scenarios_.end();
-       ++scenarios_iter) {
-    // Make sure we have a match for the number of switches in this scenario.
-    int parser_switch_count = command_line_parser.GetSwitchCount();
-    int scenario_required_switch_count =
-        (*scenarios_iter).second.required.size();
-    int scenario_optional_switch_count =
-        (*scenarios_iter).second.optional.size();
-
-    if (parser_switch_count < scenario_required_switch_count ||
-        parser_switch_count > scenario_required_switch_count +
-                              scenario_optional_switch_count) {
-      continue;
-    }
-
-    if (DoesScenarioMatch(command_line_parser, (*scenarios_iter).second)) {
-      *scenario_name = (*scenarios_iter).first;
-      return S_OK;
-    }
-  }
-
-  return GOOGLEUPDATE_COMMANDLINE_E_NO_SCENARIO_HANDLER_MATCHED;
-}
-
-bool CommandLineValidator::DoesScenarioMatch(
-    const CommandLineParser& command_line_parser,
-    const ScenarioParameters& scenario_parameters) const {
-  // Make sure that each switch matches with the right number of arguments.
-  ScenarioParameterVectorConstIter parameter_iter;
-  for (parameter_iter = scenario_parameters.required.begin();
-       parameter_iter != scenario_parameters.required.end();
-       ++parameter_iter) {
-    CString current_switch_name = (*parameter_iter)->switch_name_;
-    // This would probably allow duplicate switches (i.e. /c /c) in a command
-    // line.
-    if (!command_line_parser.HasSwitch(current_switch_name)) {
-      return false;
-    }
-
-    int arg_count = 0;
-    HRESULT hr = command_line_parser.GetSwitchArgumentCount(current_switch_name,
-                                                            &arg_count);
-    if (FAILED(hr)) {
-      return false;
-    }
-
-    int switch_arg_count = (*parameter_iter)->num_required_parameters_;
-    if (arg_count != switch_arg_count) {
-      return false;
-    }
-  }
-
-  int parser_optional_switch_count = command_line_parser.GetSwitchCount() -
-                                     scenario_parameters.required.size();
-  for (parameter_iter = scenario_parameters.optional.begin();
-       parser_optional_switch_count != 0 &&
-           parameter_iter != scenario_parameters.optional.end();
-       ++parameter_iter) {
-    CString current_switch_name = (*parameter_iter)->switch_name_;
-    // This would probably allow duplicate optional switches (i.e. /oem /oem) in
-    // a command line.
-    if (!command_line_parser.HasSwitch(current_switch_name)) {
-      continue;
-    }
-
-    int arg_count = 0;
-    HRESULT hr = command_line_parser.GetSwitchArgumentCount(current_switch_name,
-                                                            &arg_count);
-    if (FAILED(hr)) {
-      return false;
-    }
-
-    int switch_arg_count = (*parameter_iter)->num_required_parameters_;
-    if (arg_count != switch_arg_count) {
-      return false;
-    }
-    --parser_optional_switch_count;
-  }
-
-  return parser_optional_switch_count == 0;
-}
-
-void CommandLineValidator::Clear() {
-  MapScenariosIter scenarios_iter;
-  for (scenarios_iter = scenarios_.begin();
-       scenarios_iter != scenarios_.end();
-       ++scenarios_iter) {
-    ScenarioParameterVectorIter param_iter;
-    for (param_iter = (*scenarios_iter).second.required.begin();
-         param_iter != (*scenarios_iter).second.required.end();
-         ++param_iter) {
-      delete *param_iter;
-    }
-    for (param_iter = (*scenarios_iter).second.optional.begin();
-         param_iter != (*scenarios_iter).second.optional.end();
-         ++param_iter) {
-      delete *param_iter;
-    }
-  }
-  scenarios_.clear();
-}
-
-}  // namespace omaha
-
diff --git a/goopdate/command_line_validator.h b/goopdate/command_line_validator.h
deleted file mode 100644
index 027a865..0000000
--- a/goopdate/command_line_validator.h
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#ifndef OMAHA_GOOPDATE_COMMAND_LINE_VALIDATOR_H__
-#define OMAHA_GOOPDATE_COMMAND_LINE_VALIDATOR_H__
-
-#include <windows.h>
-#include <atlstr.h>
-
-#include <map>
-#include <vector>
-
-#include "base/basictypes.h"
-
-namespace omaha {
-
-class CommandLineParser;
-
-// This class allows creation of scenarios for command line combinations and
-// then provides a mechanism to validate a command line against those scenarios
-// to determine if there's a match.
-class CommandLineValidator {
- public:
-  class ScenarioParameter {
-   public:
-    ScenarioParameter(const TCHAR* switch_name, int num_required_parameters)
-      : switch_name_(switch_name),
-        num_required_parameters_(num_required_parameters) {
-    }
-    ~ScenarioParameter() {}
-
-    CString switch_name_;
-    int num_required_parameters_;
-
-   private:
-    DISALLOW_EVIL_CONSTRUCTORS(ScenarioParameter);
-  };
-
-  typedef std::vector<ScenarioParameter*> ScenarioParameterVector;
-  typedef ScenarioParameterVector::iterator ScenarioParameterVectorIter;
-  typedef ScenarioParameterVector::const_iterator
-      ScenarioParameterVectorConstIter;
-
-  struct ScenarioParameters {
-   public:
-    ScenarioParameterVector required;
-    ScenarioParameterVector optional;
-  };
-
-  typedef std::map<CString, ScenarioParameters> MapScenarios;
-  typedef MapScenarios::iterator MapScenariosIter;
-  typedef MapScenarios::const_iterator MapScenariosConstIter;
-
-  CommandLineValidator();
-  ~CommandLineValidator();
-
-  void Clear();
-
-  // Parses a command line rule and builds a scenario from it.  Returns a
-  // generated scenario name.
-  // Rules have required and optional parameters. An example of a rule is:
-  //     "gu.exe /install <extraargs> [/oem [/appargs <appargs> [/silent"
-  HRESULT CreateScenarioFromCmdLine(const CString& command_line,
-                                    CString* scenario_name);
-
-  // Validates a CommandLineParser against all scenarios.  If a match, returns
-  // S_OK and the scenario_name.  Fails if not a match.
-  // command_line_parser must already be compiled before calling.
-  HRESULT Validate(const CommandLineParser& command_line_parser,
-                   CString* scenario_name) const;
-
-  // Creates a scenario by name.
-  HRESULT CreateScenario(const CString& scenario_name);
-
-  // Adds a switch and its parameter count to an existing scenario.
-  HRESULT AddScenarioParameter(const CString& scenario_name,
-                               const CString& switch_name,
-                               int num_required_parameters);
-  HRESULT AddOptionalScenarioParameter(const CString& scenario_name,
-                                       const CString& switch_name,
-                                       int num_required_parameters);
-
- private:
-  bool DoesScenarioMatch(const CommandLineParser& command_line_parser,
-                         const ScenarioParameters& scenario_parameters) const;
-
-  int scenario_sequence_number_;
-  MapScenarios scenarios_;
-  DISALLOW_EVIL_CONSTRUCTORS(CommandLineValidator);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_COMMAND_LINE_VALIDATOR_H__
-
diff --git a/goopdate/command_line_validator_unittest.cc b/goopdate/command_line_validator_unittest.cc
deleted file mode 100644
index 60ab516..0000000
--- a/goopdate/command_line_validator_unittest.cc
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/goopdate/command_line_validator.h"
-
-#include "omaha/goopdate/command_line_parser.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-const TCHAR* kScenario1Name = _T("core");
-const TCHAR* kScenario2Name = _T("SomeScenario");
-const TCHAR* kScenario3Name = _T("OtherMechanism");
-
-const TCHAR* kScenario1CmdLine = _T("program.exe /lang foo");
-const TCHAR* kScenario2CmdLine = _T("program.exe /install x y /service");
-const TCHAR* kScenario3CmdLine = _T("prog.exe /install x y /service /lang en");
-
-const TCHAR* kLangSwitch = _T("lang");
-const int kLangSwitchArgCount = 1;
-const TCHAR* kInstallSwitch = _T("install");
-const int kInstallSwitchArgCount = 2;
-const TCHAR* kServiceSwitch = _T("service");
-const int kServiceSwitchArgCount = 0;
-
-class CommandLineValidatorTest : public testing::Test {
- public:
-
- protected:
-  CommandLineValidatorTest() {
-  }
-
-  virtual void SetUp() {
-    scenario_match_name_.Empty();
-
-    // This validator only has one scenario.
-    validator1_.Clear();
-    // "program.exe /lang foo"
-    validator1_.CreateScenario(kScenario1Name);
-    validator1_.AddScenarioParameter(kScenario1Name,
-                                     kLangSwitch,
-                                     kLangSwitchArgCount);
-
-    // This validator has three scenarios.
-    validator2_.Clear();
-    // "program.exe /lang foo"
-    validator2_.CreateScenario(kScenario1Name);
-    validator2_.AddScenarioParameter(kScenario1Name,
-                                     kLangSwitch,
-                                     kLangSwitchArgCount);
-
-    // "program.exe /install x y /service"
-    validator2_.CreateScenario(kScenario2Name);
-    validator2_.AddScenarioParameter(kScenario2Name,
-                                     kInstallSwitch,
-                                     kInstallSwitchArgCount);
-    validator2_.AddScenarioParameter(kScenario2Name,
-                                     kServiceSwitch,
-                                     kServiceSwitchArgCount);
-
-    // "program.exe /install x y /service /lang en"
-    validator2_.CreateScenario(kScenario3Name);
-    validator2_.AddScenarioParameter(kScenario3Name,
-                                     kInstallSwitch,
-                                     kInstallSwitchArgCount);
-    validator2_.AddScenarioParameter(kScenario3Name,
-                                     kServiceSwitch,
-                                     kServiceSwitchArgCount);
-    validator2_.AddScenarioParameter(kScenario3Name,
-                                     kLangSwitch,
-                                     kLangSwitchArgCount);
-  }
-
-  virtual void TearDown() {
-  }
-
-  CommandLineValidator validator1_;
-  CommandLineValidator validator2_;
-  CommandLineParser parser_;
-  CString scenario_match_name_;
-};
-
-TEST_F(CommandLineValidatorTest, BasicScenarioPass) {
-  EXPECT_SUCCEEDED(parser_.ParseFromString(kScenario1CmdLine));
-  EXPECT_SUCCEEDED(validator1_.Validate(parser_, &scenario_match_name_));
-  EXPECT_STREQ(kScenario1Name, scenario_match_name_);
-}
-
-TEST_F(CommandLineValidatorTest, BasicScenarioFail) {
-  EXPECT_SUCCEEDED(parser_.ParseFromString(_T("goopdate.exe /something bad")));
-  EXPECT_FAILED(validator1_.Validate(parser_, &scenario_match_name_));
-}
-
-TEST_F(CommandLineValidatorTest, Scenario1PassMulti) {
-  EXPECT_SUCCEEDED(parser_.ParseFromString(kScenario1CmdLine));
-  EXPECT_SUCCEEDED(validator2_.Validate(parser_, &scenario_match_name_));
-  EXPECT_STREQ(kScenario1Name, scenario_match_name_);
-}
-
-TEST_F(CommandLineValidatorTest, Scenario2PassMulti) {
-  EXPECT_SUCCEEDED(parser_.ParseFromString(kScenario2CmdLine));
-  EXPECT_SUCCEEDED(validator2_.Validate(parser_, &scenario_match_name_));
-  EXPECT_STREQ(kScenario2Name, scenario_match_name_);
-}
-
-TEST_F(CommandLineValidatorTest, Scenario3PassMulti) {
-  EXPECT_SUCCEEDED(parser_.ParseFromString(kScenario3CmdLine));
-  EXPECT_SUCCEEDED(validator2_.Validate(parser_, &scenario_match_name_));
-  EXPECT_STREQ(kScenario3Name, scenario_match_name_);
-}
-
-TEST_F(CommandLineValidatorTest, ScenarioFailMulti) {
-  EXPECT_SUCCEEDED(parser_.ParseFromString(_T("Goopdate.exe /fail me /here")));
-  EXPECT_FAILED(validator2_.Validate(parser_, &scenario_match_name_));
-}
-
-}  // namespace omaha
-
diff --git a/goopdate/config_manager.cc b/goopdate/config_manager.cc
deleted file mode 100644
index 826279c..0000000
--- a/goopdate/config_manager.cc
+++ /dev/null
@@ -1,851 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/goopdate/config_manager.h"
-#include <lm.h>
-#include <shlobj.h>
-#include <shlwapi.h>
-#include <wininet.h>
-#include <atlstr.h>
-#include <math.h>
-#include "omaha/common/app_util.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/service_utils.h"
-#include "omaha/common/string.h"
-#include "omaha/common/time.h"
-#include "omaha/common/utils.h"
-#include "omaha/enterprise/const_group_policy.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/resource.h"
-
-namespace omaha {
-
-namespace {
-
-HRESULT GetDir(int csidl,
-               const CString& path_tail,
-               bool create_dir,
-               CString* dir) {
-  ASSERT1(dir);
-
-  CString path;
-  HRESULT hr = GetFolderPath(csidl | CSIDL_FLAG_DONT_VERIFY, &path);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  if (!::PathAppend(CStrBuf(path, MAX_PATH), path_tail)) {
-    return GOOPDATE_E_PATH_APPEND_FAILED;
-  }
-  dir->SetString(path);
-
-  // Try to create the directory. Continue if the directory can't be created.
-  if (create_dir) {
-    hr = CreateDir(path, NULL);
-    if (FAILED(hr)) {
-      CORE_LOG(LE, (_T("[GetDir failed to create dir][%s][0x%08x]"), path, hr));
-    }
-  }
-  return S_OK;
-}
-
-// The app-specific value overrides the disable all value so read the former
-// first. If it doesn't exist, read the "disable all" value.
-bool GetEffectivePolicyForApp(const TCHAR* apps_default_value_name,
-                              const TCHAR* app_prefix_name,
-                              const GUID& app_guid,
-                              DWORD* effective_policy) {
-  ASSERT1(apps_default_value_name);
-  ASSERT1(app_prefix_name);
-  ASSERT1(effective_policy);
-
-  CString app_value_name(app_prefix_name);
-  app_value_name.Append(GuidToString(app_guid));
-
-  HRESULT hr = RegKey::GetValue(kRegKeyGoopdateGroupPolicy,
-                                app_value_name,
-                                effective_policy);
-  if (SUCCEEDED(hr)) {
-    return true;
-  } else {
-    CORE_LOG(L4, (_T("[Failed to read Group Policy value][%s]"),
-                  app_value_name));
-  }
-
-  hr = RegKey::GetValue(kRegKeyGoopdateGroupPolicy,
-                        apps_default_value_name,
-                        effective_policy);
-  if (SUCCEEDED(hr)) {
-    return true;
-  } else {
-    CORE_LOG(L4, (_T("[Failed to read Group Policy value][%s]"),
-                  apps_default_value_name));
-  }
-
-  return false;
-}
-
-// Gets the raw update check period override value in seconds from the registry.
-// The value must be processed for limits and overflow before using.
-// Checks UpdateDev and Group Policy.
-// Returns true if either override was successefully read.
-bool GetLastCheckPeriodSecFromRegistry(DWORD* period_sec) {
-  ASSERT1(period_sec);
-
-  DWORD update_dev_sec = 0;
-  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
-                                 kRegValueLastCheckPeriodSec,
-                                 &update_dev_sec))) {
-    CORE_LOG(L5, (_T("['LastCheckPeriodSec' override %d]"), update_dev_sec));
-    *period_sec = update_dev_sec;
-    return true;
-  }
-
-  DWORD group_policy_minutes = 0;
-  if (SUCCEEDED(RegKey::GetValue(kRegKeyGoopdateGroupPolicy,
-                                 kRegValueAutoUpdateCheckPeriodOverrideMinutes,
-                                 &group_policy_minutes))) {
-    CORE_LOG(L5, (_T("[Group Policy check period override %d]"),
-                  group_policy_minutes));
-
-
-    *period_sec = (group_policy_minutes > UINT_MAX / 60) ?
-                  UINT_MAX :
-                  group_policy_minutes * 60;
-
-    return true;
-  }
-
-  return false;
-}
-
-}  // namespace
-
-LLock ConfigManager::lock_;
-ConfigManager* ConfigManager::config_manager_ = NULL;
-
-ConfigManager* ConfigManager::Instance() {
-  __mutexScope(lock_);
-  if (!config_manager_) {
-    config_manager_ = new ConfigManager();
-  }
-  return config_manager_;
-}
-
-void ConfigManager::DeleteInstance() {
-  delete config_manager_;
-}
-
-CString ConfigManager::GetUserDownloadStorageDir() const {
-  CString path;
-  VERIFY1(SUCCEEDED(GetDir(CSIDL_LOCAL_APPDATA,
-                           CString(OMAHA_REL_DOWNLOAD_STORAGE_DIR),
-                           true,
-                           &path)));
-  return path;
-}
-
-CString ConfigManager::GetUserOfflineStorageDir() const {
-  CString path;
-  VERIFY1(SUCCEEDED(GetDir(CSIDL_LOCAL_APPDATA,
-                           CString(OMAHA_REL_OFFLINE_STORAGE_DIR),
-                           true,
-                           &path)));
-  return path;
-}
-
-CString ConfigManager::GetUserInitialManifestStorageDir() const {
-  CString path;
-  VERIFY1(SUCCEEDED(GetDir(CSIDL_LOCAL_APPDATA,
-                           CString(OMAHA_REL_INITIAL_MANIFEST_DIR),
-                           true,
-                           &path)));
-  return path;
-}
-
-CString ConfigManager::GetUserGoopdateInstallDir() const {
-  CString path;
-  VERIFY1(SUCCEEDED(GetDir(CSIDL_LOCAL_APPDATA,
-                           CString(OMAHA_REL_GOOPDATE_INSTALL_DIR),
-                           true,
-                           &path)));
-  return path;
-}
-
-bool ConfigManager::IsRunningFromUserGoopdateInstallDir() const {
-  CString path;
-  HRESULT hr = GetDir(CSIDL_LOCAL_APPDATA,
-                      CString(OMAHA_REL_GOOPDATE_INSTALL_DIR),
-                      false,
-                      &path);
-  if (FAILED(hr)) {
-    return false;
-  }
-
-  return (String_StrNCmp(path,
-                         app_util::GetCurrentModuleDirectory(),
-                         path.GetLength(),
-                         true) == 0);
-}
-
-CString ConfigManager::GetUserCrashReportsDir() const {
-  CString path;
-  VERIFY1(SUCCEEDED(GetDir(CSIDL_LOCAL_APPDATA,
-                           CString(OMAHA_REL_CRASH_DIR),
-                           true,
-                           &path)));
-  return path;
-}
-
-CString ConfigManager::GetMachineCrashReportsDir() const {
-  CString path;
-  VERIFY1(SUCCEEDED(GetDir(CSIDL_PROGRAM_FILES,
-                           CString(OMAHA_REL_CRASH_DIR),
-                           true,
-                           &path)));
-  return path;
-}
-
-CString ConfigManager::GetMachineDownloadStorageDir() const {
-  CString path;
-  VERIFY1(SUCCEEDED(GetDir(CSIDL_COMMON_APPDATA,
-                           CString(OMAHA_REL_DOWNLOAD_STORAGE_DIR),
-                           true,
-                           &path)));
-  return path;
-}
-
-CString ConfigManager::GetMachineSecureDownloadStorageDir() const {
-  CString path;
-  VERIFY1(SUCCEEDED(GetDir(CSIDL_PROGRAM_FILES,
-                           CString(OMAHA_REL_DOWNLOAD_STORAGE_DIR),
-                           true,
-                           &path)));
-  return path;
-}
-
-CString ConfigManager::GetMachineSecureOfflineStorageDir() const {
-  CString path;
-  VERIFY1(SUCCEEDED(GetDir(CSIDL_PROGRAM_FILES,
-                           CString(OMAHA_REL_OFFLINE_STORAGE_DIR),
-                           true,
-                           &path)));
-  return path;
-}
-
-CString ConfigManager::GetTempDownloadDir() const {
-  CString path;
-  VERIFY1(SUCCEEDED(GetDir(CSIDL_LOCAL_APPDATA,
-                           CString(OMAHA_REL_TEMP_DOWNLOAD_DIR),
-                           true,
-                           &path)));
-  return path;
-}
-
-CString ConfigManager::GetMachineGoopdateInstallDir() const {
-  CString path;
-  VERIFY1(SUCCEEDED(GetDir(CSIDL_PROGRAM_FILES,
-                           CString(OMAHA_REL_GOOPDATE_INSTALL_DIR),
-                           true,
-                           &path)));
-  return path;
-}
-
-bool ConfigManager::IsRunningFromMachineGoopdateInstallDir() const {
-  CString path;
-  HRESULT hr = GetDir(CSIDL_PROGRAM_FILES,
-                      CString(OMAHA_REL_GOOPDATE_INSTALL_DIR),
-                      false,
-                      &path);
-  if (FAILED(hr)) {
-    return false;
-  }
-
-  return (String_StrNCmp(path,
-                         app_util::GetCurrentModuleDirectory(),
-                         path.GetLength(),
-                         true) == 0);
-}
-
-// TODO(omaha): create a generic way to override configuration parameters.
-//
-// Overrides PingUrl in debug builds.
-HRESULT ConfigManager::GetPingUrl(CString* url) const {
-  ASSERT1(url);
-
-#ifdef DEBUG
-  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
-                                 kRegValueNamePingUrl,
-                                 url))) {
-    CORE_LOG(L5, (_T("['ping url' override %s]"), *url));
-    return S_OK;
-  }
-#endif
-
-  *url = kUrlPing;
-  return S_OK;
-}
-
-// Overrides Url (update check url) in debug builds.
-HRESULT ConfigManager::GetUpdateCheckUrl(CString* url) const {
-  ASSERT1(url);
-
-#ifdef DEBUG
-  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
-                                 kRegValueNameUrl,
-                                 url))) {
-    CORE_LOG(L5, (_T("['url' override %s]"), *url));
-    return S_OK;
-  }
-#endif
-
-  *url = kUrlUpdateCheck;
-  return S_OK;
-}
-
-// Returns the override from the registry locations if present. Otherwise,
-// returns the default value.
-// Default value is different value for Googlers, to make update checks more
-// aggresive.
-// Ensures returned value is between kMinLastCheckPeriodSec and INT_MAX except
-// when the override is 0, which indicates updates are disabled.
-int ConfigManager::GetLastCheckPeriodSec(bool* is_overridden) const {
-  ASSERT1(is_overridden);
-  DWORD registry_period_sec = 0;
-  *is_overridden = GetLastCheckPeriodSecFromRegistry(&registry_period_sec);
-  if (*is_overridden) {
-    if (0 == registry_period_sec) {
-      return 0;
-    }
-    const int period_sec = registry_period_sec > INT_MAX ?
-                           INT_MAX :
-                           static_cast<int>(registry_period_sec);
-
-    if (period_sec < kMinLastCheckPeriodSec) {
-      return kMinLastCheckPeriodSec;
-    }
-    return period_sec;
-  }
-
-  // Returns a lower value for Googlers.
-  if (IsGoogler()) {
-    return kLastCheckPeriodGooglerSec;
-  }
-
-  return kLastCheckPeriodSec;
-}
-
-// All time values are in seconds.
-int ConfigManager::GetTimeSinceLastCheckedSec(bool is_machine) const {
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-  const uint32 last_checked = GetLastCheckedTime(is_machine);
-  if (now < last_checked) {
-    CORE_LOG(LW, (_T("[possible time warp detected]")
-                  _T("[now %u][last checked %u]"), now, last_checked));
-  }
-  const int time_difference = abs(static_cast<int>(now - last_checked));
-  bool is_period_overridden = false;
-  CORE_LOG(L3, (_T("[now %u][last checked %u][update interval %u]")
-                _T("[time difference %u]"),
-                now, last_checked, GetLastCheckPeriodSec(&is_period_overridden),
-                time_difference));
-  return time_difference;
-}
-
-DWORD ConfigManager::GetLastCheckedTime(bool is_machine) const {
-  const TCHAR* reg_update_key = is_machine ? MACHINE_REG_UPDATE:
-                                             USER_REG_UPDATE;
-  DWORD last_checked_time = 0;
-  if (SUCCEEDED(RegKey::GetValue(reg_update_key,
-                                 kRegValueLastChecked,
-                                 &last_checked_time))) {
-    return last_checked_time;
-  }
-  return 0;
-}
-
-HRESULT ConfigManager::SetLastCheckedTime(bool is_machine, DWORD time) const {
-  const TCHAR* reg_update_key = is_machine ? MACHINE_REG_UPDATE:
-                                             USER_REG_UPDATE;
-  return RegKey::SetValue(reg_update_key, kRegValueLastChecked, time);
-}
-
-DWORD ConfigManager::GetInstallTime(bool is_machine) {
-  const CString client_state_key_name =
-      ConfigManager::Instance()->registry_client_state_goopdate(is_machine);
-  DWORD update_time(0);
-  if (SUCCEEDED(RegKey::GetValue(client_state_key_name,
-                                 kRegValueLastUpdateTimeSec,
-                                 &update_time))) {
-    return update_time;
-  }
-
-  DWORD install_time(0);
-  if (SUCCEEDED(RegKey::GetValue(client_state_key_name,
-                                 kRegValueInstallTimeSec,
-                                 &install_time))) {
-    return install_time;
-  }
-
-  return 0;
-}
-
-bool ConfigManager::Is24HoursSinceInstall(bool is_machine) {
-  const int kDaySec = 24 * 60 * 60;
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  const uint32 install_time = GetInstallTime(is_machine);
-  if (now < install_time) {
-    CORE_LOG(LW, (_T("[Incorrect clock time detected]")
-                  _T("[now %u][install_time %u]"), now, install_time));
-  }
-  const int time_difference = abs(static_cast<int>(now - install_time));
-  return time_difference >= kDaySec;
-}
-
-bool ConfigManager::CanCollectStats(bool is_machine) const {
-  if (RegKey::HasValue(MACHINE_REG_UPDATE_DEV, kRegValueForceUsageStats)) {
-    return true;
-  }
-
-  // TODO(omaha): This should actually be iterating over registered products
-  // rather than present ClientState keys. These are identical in most cases.
-  const TCHAR* state_key_name = registry_client_state(is_machine);
-
-  RegKey state_key;
-  HRESULT hr = state_key.Open(state_key_name, KEY_READ);
-  if (FAILED(hr)) {
-    return false;
-  }
-
-  int num_sub_keys = state_key.GetSubkeyCount();
-  for (int i = 0; i < num_sub_keys; ++i) {
-    CString sub_key_name;
-    if (FAILED(state_key.GetSubkeyNameAt(i, &sub_key_name))) {
-      continue;
-    }
-
-    if (goopdate_utils::AreAppUsageStatsEnabled(is_machine, sub_key_name)) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-// Overrides OverInstall in debug builds.
-bool ConfigManager::CanOverInstall() const {
-#ifdef DEBUG
-  DWORD value = 0;
-  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
-                                 kRegValueNameOverInstall,
-                                 &value))) {
-    CORE_LOG(L5, (_T("['OverInstall' override %d]"), value));
-    return value != 0;
-  }
-#endif
-  return !OFFICIAL_BUILD;
-}
-
-// Overrides AuCheckPeriodMs. Implements a lower bound value. Returns INT_MAX
-// if the registry value exceeds INT_MAX.
-int ConfigManager::GetAutoUpdateTimerIntervalMs() const {
-  DWORD interval(0);
-  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
-                                 kRegValueAuCheckPeriodMs,
-                                 &interval))) {
-    int ret_val = 0;
-    if (interval > INT_MAX) {
-      ret_val = INT_MAX;
-    } else if (interval < kMinAUCheckPeriodMs) {
-      ret_val = kMinAUCheckPeriodMs;
-    } else {
-      ret_val = interval;
-    }
-    ASSERT1(ret_val >= kMinAUCheckPeriodMs);
-    CORE_LOG(L5, (_T("['AuCheckPeriodMs' override %d]"), interval));
-    return ret_val;
-  }
-
-  // Returns a lower value for Googlers.
-  if (IsGoogler()) {
-    return kAUCheckPeriodGooglerMs;
-  }
-
-  return kAUCheckPeriodMs;
-}
-
-int ConfigManager::GetUpdateWorkerStartUpDelayMs() const {
-  int au_timer_interval_ms = GetAutoUpdateTimerIntervalMs();
-
-  // If the AuCheckPeriod is overriden then use that as the delay.
-  if (RegKey::HasValue(MACHINE_REG_UPDATE_DEV, kRegValueAuCheckPeriodMs)) {
-    return au_timer_interval_ms;
-  }
-
-  int random_delay = 0;
-  if (!GenRandom(&random_delay, sizeof(random_delay))) {
-    return au_timer_interval_ms;
-  }
-
-  // Scale the au_check_period number to be between
-  // kUpdateTimerStartupDelayMinMs and kUpdateTimerStartupDelayMaxMs.
-  int scale = kUpdateTimerStartupDelayMaxMs - kUpdateTimerStartupDelayMinMs;
-  ASSERT1(scale >= 0);
-
-  int random_addition = abs(random_delay) % scale;
-  ASSERT1(random_addition < scale);
-
-  au_timer_interval_ms = kUpdateTimerStartupDelayMinMs + random_addition;
-  ASSERT1(au_timer_interval_ms >= kUpdateTimerStartupDelayMinMs &&
-          au_timer_interval_ms <= kUpdateTimerStartupDelayMaxMs);
-
-  return au_timer_interval_ms;
-}
-
-// Overrides CodeRedCheckPeriodMs. Implements a lower bound value. Returns
-// INT_MAX if the registry value exceeds INT_MAX.
-int ConfigManager::GetCodeRedTimerIntervalMs() const {
-  DWORD interval(0);
-  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
-                                 kRegValueCrCheckPeriodMs,
-                                 &interval))) {
-    int ret_val = 0;
-    if (interval > INT_MAX) {
-      ret_val = INT_MAX;
-    } else if (interval < kMinCodeRedCheckPeriodMs) {
-      ret_val = kMinCodeRedCheckPeriodMs;
-    } else {
-      ret_val = interval;
-    }
-    ASSERT1(ret_val >= kMinCodeRedCheckPeriodMs);
-    CORE_LOG(L5, (_T("['CrCheckPeriodMs' override %d]"), interval));
-    return ret_val;
-  }
-  return kCodeRedCheckPeriodMs;
-}
-
-// Returns true if logging is enabled for the event type.
-// Logging of errors and warnings is enabled by default.
-bool ConfigManager::CanLogEvents(WORD event_type) const {
-  const TCHAR* reg_update_key = MACHINE_REG_UPDATE_DEV;
-  DWORD log_events_level = LOG_EVENT_LEVEL_NONE;
-  if (SUCCEEDED(RegKey::GetValue(reg_update_key,
-                                 kRegValueEventLogLevel,
-                                 &log_events_level))) {
-    switch (log_events_level) {
-      case LOG_EVENT_LEVEL_ALL:
-        return true;
-      case LOG_EVENT_LEVEL_WARN_AND_ERROR:
-        return event_type == EVENTLOG_ERROR_TYPE ||
-               event_type == EVENTLOG_WARNING_TYPE;
-      case LOG_EVENT_LEVEL_NONE:
-      default:
-        return false;
-    }
-  }
-
-  return event_type == EVENTLOG_ERROR_TYPE ||
-         event_type == EVENTLOG_WARNING_TYPE;
-}
-
-CString ConfigManager::GetTestSource() const {
-  CString test_source;
-  HRESULT hr = RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
-                                kRegValueTestSource,
-                                &test_source);
-  if (SUCCEEDED(hr)) {
-    if (test_source.IsEmpty()) {
-      test_source = kRegValueTestSourceAuto;
-    }
-    return test_source;
-  }
-
-  DWORD interval = 0;
-  hr = RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
-                        kRegValueAuCheckPeriodMs,
-                        &interval);
-  if (SUCCEEDED(hr)) {
-    return kRegValueTestSourceAuto;
-  }
-
-#if defined(DEBUG) || !OFFICIAL_BUILD
-  test_source = kRegValueTestSourceAuto;
-#endif
-
-  return test_source;
-}
-
-// Reads the current value under HKLM/HKCU\Google\Update\value_name. Returns
-// default_val if value_name does not exist.
-CString GetCurrentVersionedName(bool is_machine,
-                                const TCHAR* value_name,
-                                const TCHAR* default_val) {
-  CORE_LOG(L3, (_T("[ConfigManager::GetCurrentVersionedName]")));
-  ASSERT1(value_name && *value_name);
-  ASSERT1(default_val && *default_val);
-
-  const TCHAR* key_name = is_machine ? MACHINE_REG_UPDATE : USER_REG_UPDATE;
-  CString name;
-  HRESULT hr(RegKey::GetValue(key_name, value_name, &name));
-  if (FAILED(hr)) {
-    CORE_LOG(L4, (_T("[GetValue failed][%s][0x%x][Using default name][%s]"),
-                  value_name, hr, default_val));
-    name = default_val;
-  }
-
-  CORE_LOG(L3, (_T("[Versioned Name][%s]"), name));
-  return name;
-}
-
-// Creates a unique name of the form "{prefix}1c9b3d6baf90df3" and stores it in
-// the registry under HKLM/HKCU\Google\Update\value_name. Subsequent
-// invocations of GetCurrentTaskName() will return this new value.
-HRESULT CreateAndSetVersionedNameInRegistry(bool is_machine,
-                                            const TCHAR* prefix,
-                                            const TCHAR* value_name) {
-  ASSERT1(prefix && *prefix);
-  ASSERT1(value_name && *value_name);
-
-  CString name(ServiceInstall::GenerateServiceName(prefix));
-  CORE_LOG(L3, (_T("Versioned name[%s][%s][%s]"), prefix, value_name, name));
-
-  const TCHAR* key_name = is_machine ? MACHINE_REG_UPDATE : USER_REG_UPDATE;
-  return RegKey::SetValue(key_name, value_name, name);
-}
-
-CString ConfigManager::GetCurrentTaskNameCore(bool is_machine) {
-  CORE_LOG(L3, (_T("[GetCurrentTaskNameCore[%d]"), is_machine));
-
-  CString default_name(goopdate_utils::GetDefaultGoopdateTaskName(
-                                           is_machine,
-                                           COMMANDLINE_MODE_CORE));
-  return GetCurrentVersionedName(is_machine, kRegValueTaskNameC, default_name);
-}
-
-HRESULT ConfigManager::CreateAndSetVersionedTaskNameCoreInRegistry(
-    bool is_machine) {
-  CORE_LOG(L3, (_T("CreateAndSetVersionedTaskNameCoreInRegistry[%d]"),
-                is_machine));
-
-  CString default_name(goopdate_utils::GetDefaultGoopdateTaskName(
-                                           is_machine,
-                                           COMMANDLINE_MODE_CORE));
-  return CreateAndSetVersionedNameInRegistry(is_machine,
-                                             default_name,
-                                             kRegValueTaskNameC);
-}
-
-CString ConfigManager::GetCurrentTaskNameUA(bool is_machine) {
-  CORE_LOG(L3, (_T("[GetCurrentTaskNameUA[%d]"), is_machine));
-
-  CString default_name(goopdate_utils::GetDefaultGoopdateTaskName(
-                                           is_machine,
-                                           COMMANDLINE_MODE_UA));
-  return GetCurrentVersionedName(is_machine, kRegValueTaskNameUA, default_name);
-}
-
-HRESULT ConfigManager::CreateAndSetVersionedTaskNameUAInRegistry(bool machine) {
-  CORE_LOG(L3, (_T("CreateAndSetVersionedTaskNameUAInRegistry[%d]"), machine));
-
-  CString default_name(goopdate_utils::GetDefaultGoopdateTaskName(
-                                           machine,
-                                           COMMANDLINE_MODE_UA));
-  return CreateAndSetVersionedNameInRegistry(machine,
-                                             default_name,
-                                             kRegValueTaskNameUA);
-}
-
-CString ConfigManager::GetCurrentServiceName() {
-  CORE_LOG(L3, (_T("[ConfigManager::GetCurrentServiceName]")));
-  return GetCurrentVersionedName(true,
-                                 kRegValueServiceName,
-                                 kLegacyServiceName);
-}
-
-CString ConfigManager::GetCurrentServiceDisplayName() {
-  CORE_LOG(L3, (_T("[ConfigManager::GetCurrentServiceDisplayName]")));
-  CString display_name;
-  VERIFY1(display_name.LoadString(IDS_SERVICE_DISPLAY_NAME));
-  display_name.AppendFormat(_T(" (%s)"), GetCurrentServiceName());
-  return display_name;
-}
-
-HRESULT ConfigManager::CreateAndSetVersionedServiceNameInRegistry() {
-  CORE_LOG(L3, (_T("CreateAndSetVersionedServiceNameInRegistry")));
-  return CreateAndSetVersionedNameInRegistry(true,
-                                             kServicePrefix,
-                                             kRegValueServiceName);
-}
-
-HRESULT ConfigManager::GetNetConfig(CString* net_config) {
-  ASSERT1(net_config);
-  CString val;
-  HRESULT hr = RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
-                                kRegValueNetConfig,
-                                &val);
-  if (SUCCEEDED(hr)) {
-    *net_config = val;
-  }
-  return hr;
-}
-
-// Returns false if running in the context of an OEM install or waiting for a
-// EULA to be accepted.
-bool ConfigManager::CanUseNetwork(bool is_machine) const {
-  DWORD eula_accepted(0);
-  HRESULT hr = RegKey::GetValue(registry_update(is_machine),
-                                kRegValueOmahaEulaAccepted,
-                                &eula_accepted);
-  if (SUCCEEDED(hr) && 0 == eula_accepted) {
-    CORE_LOG(L3, (_T("[CanUseNetwork][eulaaccepted=0][false]")));
-    return false;
-  }
-
-  if (IsOemInstalling(is_machine)) {
-    CORE_LOG(L3, (_T("[CanUseNetwork][OEM installing][false]")));
-    return false;
-  }
-
-  return true;
-}
-
-// Always returns false if !is_machine. This prevents ever blocking per-user
-// instances.
-// Returns true if OEM install time is present and it has been less than
-// kMinOemModeSec since the OEM install.
-// Non-OEM installs can never be blocked from updating because OEM install time
-// will not be present.
-bool ConfigManager::IsOemInstalling(bool is_machine) const {
-  if (!is_machine) {
-    return false;
-  }
-
-  DWORD oem_install_time_seconds = 0;
-  if (FAILED(RegKey::GetValue(MACHINE_REG_UPDATE,
-                              kRegValueOemInstallTimeSec,
-                              &oem_install_time_seconds))) {
-    CORE_LOG(L3, (_T("[IsOemInstalling][OemInstallTime not found][false]")));
-    return false;
-  }
-
-  const uint32 now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  if (now_seconds < oem_install_time_seconds) {
-    CORE_LOG(LW, (_T("[possible time warp detected][now %u][last checked %u]"),
-                  now_seconds, oem_install_time_seconds));
-  }
-  const int time_difference_seconds =
-      abs(static_cast<int>(now_seconds - oem_install_time_seconds));
-
-  ASSERT1(0 <= time_difference_seconds);
-  const bool result = time_difference_seconds < kMinOemModeSec ? true : false;
-
-  CORE_LOG(L3, (_T("[now %u][OEM install time %u][time difference %u][%d]"),
-                now_seconds, oem_install_time_seconds, time_difference_seconds,
-                result));
-  return result;
-}
-
-// USE IsOemInstalling() INSTEAD in most cases.
-bool ConfigManager::IsWindowsInstalling() const {
-#if !OFFICIAL_BUILD
-  DWORD value = 0;
-  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
-                                 kRegValueNameWindowsInstalling,
-                                 &value))) {
-    CORE_LOG(L3, (_T("['WindowsInstalling' override %d]"), value));
-    return value != 0;
-  }
-#endif
-
-  return omaha::IsWindowsInstalling();
-}
-
-// Checks if the computer name ends with .google.com or the netbios domain is
-// google.
-bool ConfigManager::IsGoogler() const {
-  CORE_LOG(L4, (_T("[ConfigManager::IsGoogler]")));
-  TCHAR dns_name[INTERNET_MAX_HOST_NAME_LENGTH] = {0};
-  DWORD dns_name_size(arraysize(dns_name));
-  if (::GetComputerNameEx(ComputerNameDnsFullyQualified,
-                          dns_name, &dns_name_size)) {
-     CORE_LOG(L4, (_T("[dns name %s]"), dns_name));
-     if (String_EndsWith(dns_name, _T(".google.com"), true)) {
-       return true;
-     }
-  }
-
-  WKSTA_INFO_100* info = NULL;
-  int kInformationLevel = 100;
-  NET_API_STATUS status = ::NetWkstaGetInfo(NULL,
-                                            kInformationLevel,
-                                            reinterpret_cast<BYTE**>(&info));
-  ON_SCOPE_EXIT(::NetApiBufferFree, info);
-  if (status == NERR_Success) {
-    CORE_LOG(L4, (_T("[netbios name %s]"), info->wki100_langroup));
-    if (info->wki100_langroup &&
-        _tcsicmp(info->wki100_langroup, _T("google")) == 0) {
-      return true;
-    }
-  }
-  return false;
-}
-
-bool ConfigManager::CanInstallApp(const GUID& app_guid) const {
-  // Google Update should never be checking whether it can install itself.
-  ASSERT1(!::IsEqualGUID(kGoopdateGuid, app_guid));
-
-  DWORD effective_policy = 0;
-  if (!GetEffectivePolicyForApp(kRegValueInstallAppsDefault,
-                                kRegValueInstallAppPrefix,
-                                app_guid,
-                                &effective_policy)) {
-    return kInstallPolicyDefault;
-  }
-
-  return kPolicyDisabled != effective_policy;
-}
-
-// Self-updates cannot be disabled.
-bool ConfigManager::CanUpdateApp(const GUID& app_guid,
-                                 bool is_manual) const {
-  if (::IsEqualGUID(kGoopdateGuid, app_guid)) {
-    return true;
-  }
-
-  DWORD effective_policy = 0;
-  if (!GetEffectivePolicyForApp(kRegValueUpdateAppsDefault,
-                                kRegValueUpdateAppPrefix,
-                                app_guid,
-                                &effective_policy)) {
-    return kUpdatePolicyDefault;
-  }
-
-  if (kPolicyDisabled == effective_policy) {
-    return false;
-  }
-  if ((kPolicyManualUpdatesOnly == effective_policy) && !is_manual) {
-    return false;
-  }
-
-  return kUpdatePolicyDefault;
-}
-
-}  // namespace omaha
diff --git a/goopdate/config_manager.h b/goopdate/config_manager.h
deleted file mode 100644
index ee104f0..0000000
--- a/goopdate/config_manager.h
+++ /dev/null
@@ -1,264 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// The configuration manager that is used to provide the locations of the
-// directory and the registration entries that are to be used by goopdate.
-
-// TODO(omaha): consider removing some of the functions below and have a
-// parameter is_machine instead. This is consistent with the rest of the code
-// and it reduces the number of functions in the public interface.
-
-#ifndef OMAHA_GOOPDATE_CONFIG_MANAGER_H__
-#define OMAHA_GOOPDATE_CONFIG_MANAGER_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include "base/basictypes.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/synchronized.h"
-
-namespace omaha {
-
-class ConfigManager {
- public:
-  const TCHAR* user_registry_clients() const { return USER_REG_CLIENTS; }
-  const TCHAR* user_registry_clients_goopdate() const {
-    return USER_REG_CLIENTS_GOOPDATE;
-  }
-  const TCHAR* user_registry_client_state() const {
-    return USER_REG_CLIENT_STATE;
-  }
-  const TCHAR* user_registry_client_state_goopdate() const {
-    return USER_REG_CLIENT_STATE_GOOPDATE;
-  }
-  const TCHAR* user_registry_update() const { return USER_REG_UPDATE; }
-  const TCHAR* user_registry_google() const { return USER_REG_GOOGLE; }
-
-  const TCHAR* machine_registry_clients() const { return MACHINE_REG_CLIENTS; }
-  const TCHAR* machine_registry_clients_goopdate() const {
-    return MACHINE_REG_CLIENTS_GOOPDATE;
-  }
-  const TCHAR* machine_registry_client_state() const {
-    return MACHINE_REG_CLIENT_STATE;
-  }
-  const TCHAR* machine_registry_client_state_goopdate() const {
-    return MACHINE_REG_CLIENT_STATE_GOOPDATE;
-  }
-  const TCHAR* machine_registry_client_state_medium() const {
-    return MACHINE_REG_CLIENT_STATE_MEDIUM;
-  }
-  const TCHAR* machine_registry_update() const { return MACHINE_REG_UPDATE; }
-  const TCHAR* machine_registry_google() const { return MACHINE_REG_GOOGLE; }
-
-  const TCHAR* registry_clients(bool is_machine) const {
-    return is_machine ? machine_registry_clients() : user_registry_clients();
-  }
-  const TCHAR* registry_clients_goopdate(bool is_machine) const {
-    return is_machine ? machine_registry_clients_goopdate() :
-                        user_registry_clients_goopdate();
-  }
-  const TCHAR* registry_client_state(bool is_machine) const {
-    return is_machine ? machine_registry_client_state() :
-                        user_registry_client_state();
-  }
-  const TCHAR* registry_client_state_goopdate(bool is_machine) const {
-    return is_machine ? machine_registry_client_state_goopdate() :
-                        user_registry_client_state_goopdate();
-  }
-  const TCHAR* registry_update(bool is_machine) const {
-    return is_machine ? machine_registry_update() : user_registry_update();
-  }
-  const TCHAR* registry_google(bool is_machine) const {
-    return is_machine ? machine_registry_google() : user_registry_google();
-  }
-
-  // Gets the temporary download dir for the current thread token:
-  // %UserProfile%/AppData/Local/Temp/Downloads
-  CString GetTempDownloadDir() const;
-
-  // Creates download data dir:
-  // %UserProfile%/Application Data/Google/Update/Downloads
-  CString GetUserDownloadStorageDir() const;
-
-  // Creates offline data dir:
-  // %UserProfile%/Application Data/Google/Update/Offline
-  CString GetUserOfflineStorageDir() const;
-
-  // Creates initial manifest download dir:
-  // %UserProfile%/Application Data/Google/Update/Manifests/Initial
-  CString GetUserInitialManifestStorageDir() const;
-
-  // Creates goopdate install dir:
-  // %UserProfile%/Application Data/Google/Update
-  CString GetUserGoopdateInstallDir() const;
-
-  // Checks if the running program is executing from the User Goopdate dir.
-  bool IsRunningFromUserGoopdateInstallDir() const;
-
-  // Creates crash reports dir:
-  // %UserProfile%/Local Settings/Application Data/Google/CrashReports
-  CString GetUserCrashReportsDir() const;
-
-  // Creates crash reports dir: %ProgramFiles%/Google/CrashReports
-  CString GetMachineCrashReportsDir() const;
-
-  // TODO(omaha): this is legacy Omaha1. Remove later.
-  // Creates machine download data dir:
-  // %All Users%/Google/Update/Downloads
-  // This is the directory where all the machine downloads are initially
-  // downloaded to. This is needed as the download could have occured as
-  // a user who does not have permission to the machine download location.
-  CString GetMachineDownloadStorageDir() const;
-
-  // Creates machine download data dir:
-  // %ProgramFiles%/Google/Update/Downloads
-  // This is the directory where the installs for machine goopdate are copied
-  // to once the download has succeeded.
-  CString GetMachineSecureDownloadStorageDir() const;
-
-  // Creates machine offline data dir:
-  // %ProgramFiles%/Google/Update/Offline
-  CString GetMachineSecureOfflineStorageDir() const;
-
-  // Creates machine Gogole Update install dir:
-  // %ProgramFiles%/Google/Update
-  CString GetMachineGoopdateInstallDir() const;
-
-  // Checks if the running program is executing from the User Goopdate dir.
-  bool IsRunningFromMachineGoopdateInstallDir() const;
-
-  // Returns the service endpoint where the install/update/uninstall pings
-  // are being sent.
-  HRESULT GetPingUrl(CString* url) const;
-
-  // Returns the service endpoint where the manifest requests and update
-  // checks are being sent.
-  HRESULT GetUpdateCheckUrl(CString* url) const;
-
-  // Returns the time interval between update checks in seconds.
-  // 0 indicates updates are disabled.
-  int GetLastCheckPeriodSec(bool* is_overridden) const;
-
-  // Returns the number of seconds since the last successful update check.
-  int GetTimeSinceLastCheckedSec(bool is_machine) const;
-
-  // Gets and sets the last time a successful server update check was made.
-  DWORD GetLastCheckedTime(bool is_machine) const;
-  HRESULT SetLastCheckedTime(bool is_machine, DWORD time) const;
-
-  // Checks registry to see if user has enabled us to collect anonymous
-  // usage stats.
-  bool CanCollectStats(bool is_machine) const;
-
-  // Returns true if over-installing with the same version is allowed.
-  bool CanOverInstall() const;
-
-  // Returns the Autoupdate timer interval. This is the frequency of the
-  // auto update timer run by the core.
-  int GetAutoUpdateTimerIntervalMs() const;
-
-  // Returns the wait time in ms to start the first worker.
-  int GetUpdateWorkerStartUpDelayMs() const;
-
-  // Returns the Code Red timer interval. This is the frequency of the
-  // code red timer run by the core.
-  int GetCodeRedTimerIntervalMs() const;
-
-  // Returns true if event logging to the Windows Event Log is enabled.
-  bool CanLogEvents(WORD event_type) const;
-
-  // Retrieves TestSource which is to be set on dev, qa, and prober machines.
-  CString GetTestSource() const;
-
-  // Returns true if it is okay to do update checks and send pings.
-  bool CanUseNetwork(bool is_machine) const;
-
-  // Returns true if running in the context of an OEM install.
-  // !CanUseNetwork() may be more appropriate.
-  bool IsOemInstalling(bool is_machine) const;
-
-  // Returns true if running in Windows Audit mode (OEM install).
-  // USE IsOemInstalling() INSTEAD in most cases.
-  bool IsWindowsInstalling() const;
-
-  // Returns true if the user is considered a Googler.
-  bool IsGoogler() const;
-
-  // Returns true if installation of the specified app is allowed.
-  bool CanInstallApp(const GUID& app_guid) const;
-
-  // Returns true if updates are allowed for the specified app.
-  bool CanUpdateApp(const GUID& app_guid, bool is_manual) const;
-
-  // Gets the current name, say "GoogleUpdateTaskMachineCore", of the
-  // GoogleUpdateCore scheduled task, either from the registry, or a default
-  // value if there is no registration.
-  static CString GetCurrentTaskNameCore(bool is_machine);
-
-  // Creates a unique name, say "GoogleUpdateTaskMachineCore1c9b3d6baf90df3", of
-  // the GoogleUpdateCore scheduled task, and stores it in the registry.
-  // Subsequent invocations of GetCurrentTaskNameCore() will return this new
-  // value.
-  static HRESULT CreateAndSetVersionedTaskNameCoreInRegistry(bool machine);
-
-  // Gets the current name, say "GoogleUpdateTaskMachineUA", of the
-  // GoogleUpdateUA scheduled task, either from the registry, or a default value
-  // if there is no registration.
-  static CString GetCurrentTaskNameUA(bool is_machine);
-
-  // Creates a unique name, say "GoogleUpdateTaskMachineUA1c9b3d6baf90df3", of
-  // the GoogleUpdateUA scheduled task, and stores it in the registry.
-  // Subsequent invocations of GetCurrentTaskNameUA() will return this new
-  // value.
-  static HRESULT CreateAndSetVersionedTaskNameUAInRegistry(bool machine);
-
-  // Gets the current name, say "gupdate", of the goopdate system service,
-  // either from the registry, or a default value if there is no registration.
-  static CString GetCurrentServiceName();
-
-  // Gets the current name and description of goopdate service.
-  static CString GetCurrentServiceDisplayName();
-
-  // Creates a unique versioned string and sets the version of goopdate service
-  // in the registry. Subsequent invocations of GetCurrentServiceName() will
-  // return this new value.
-  static HRESULT CreateAndSetVersionedServiceNameInRegistry();
-
-  // Returns the network configuration override as a string.
-  static HRESULT GetNetConfig(CString* configuration_override);
-
-  // Gets the time when Goopdate was last updated or installed.
-  static DWORD GetInstallTime(bool is_machine);
-
-  // Returns true if it has been more than 24 hours since Goopdate was updated
-  // or installed.
-  static bool Is24HoursSinceInstall(bool is_machine);
-
-  static ConfigManager* Instance();
-  static void DeleteInstance();
-
- private:
-  ConfigManager() {}
-
-  static LLock lock_;
-  static ConfigManager* config_manager_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(ConfigManager);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_CONFIG_MANAGER_H__
-
diff --git a/goopdate/config_manager_unittest.cc b/goopdate/config_manager_unittest.cc
deleted file mode 100644
index 0f9684c..0000000
--- a/goopdate/config_manager_unittest.cc
+++ /dev/null
@@ -1,1896 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <limits.h>
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/file.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/string.h"
-#include "omaha/common/system_info.h"
-#include "omaha/common/time.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-namespace {
-
-#define APP_GUID1 _T("{6762F466-8863-424f-817C-5757931F346E}")
-const TCHAR* const kAppGuid1 = APP_GUID1;
-const TCHAR* const kAppMachineClientStatePath1 =
-    _T("HKLM\\Software\\Google\\Update\\ClientState\\") APP_GUID1;
-const TCHAR* const kAppUserClientStatePath1 =
-    _T("HKCU\\Software\\Google\\Update\\ClientState\\") APP_GUID1;
-const TCHAR* const kAppMachineClientStateMediumPath1 =
-    _T("HKLM\\Software\\Google\\Update\\ClientStateMedium\\") APP_GUID1;
-
-#define APP_GUID2 _T("{8A0FDD16-D4B7-4167-893F-1386F2A2F0FB}")
-const TCHAR* const kAppGuid2 = APP_GUID2;
-const TCHAR* const kAppMachineClientStatePath2 =
-    _T("HKLM\\Software\\Google\\Update\\ClientState\\") APP_GUID2;
-const TCHAR* const kAppUserClientStatePath2 =
-    _T("HKCU\\Software\\Google\\Update\\ClientState\\") APP_GUID2;
-
-const TCHAR* const kPolicyKey =
-    _T("HKLM\\Software\\Policies\\Google\\Update\\");
-const TCHAR* const kInstallPolicyApp1 = _T("Install") APP_GUID1;
-const TCHAR* const kInstallPolicyApp2 = _T("Install") APP_GUID2;
-const TCHAR* const kUpdatePolicyApp1 = _T("Update") APP_GUID1;
-const TCHAR* const kUpdatePolicyApp2 = _T("Update") APP_GUID2;
-
-// Helper to write policies to the registry. Eliminates ambiguity of which
-// overload of SetValue to use without the need for static_cast.
-HRESULT SetPolicy(const TCHAR* policy_name, DWORD value) {
-  return RegKey::SetValue(kPolicyKey, policy_name, value);
-}
-
-}  // namespace
-
-class ConfigManagerNoOverrideTest : public testing::Test {
- protected:
-  ConfigManagerNoOverrideTest()
-      : cm_(ConfigManager::Instance()) {
-  }
-
-  bool CanInstallApp(const TCHAR* guid) {
-    return cm_->CanInstallApp(StringToGuid(guid));
-  }
-
-  bool CanUpdateApp(const TCHAR* guid, bool is_manual) {
-    return cm_->CanUpdateApp(StringToGuid(guid), is_manual);
-  }
-
-  ConfigManager* cm_;
-};
-
-class ConfigManagerTest : public ConfigManagerNoOverrideTest {
- protected:
-  ConfigManagerTest()
-      : hive_override_key_name_(kRegistryHiveOverrideRoot) {
-  }
-
-  virtual void SetUp() {
-    RegKey::DeleteKey(hive_override_key_name_, true);
-    OverrideRegistryHives(hive_override_key_name_);
-  }
-
-  virtual void TearDown() {
-    RestoreRegistryHives();
-    EXPECT_SUCCEEDED(RegKey::DeleteKey(hive_override_key_name_, true));
-  }
-
-  void CanCollectStatsHelper(bool is_machine);
-  void CanCollectStatsIgnoresOppositeHiveHelper(bool is_machine);
-  HRESULT SetFirstInstallTime(bool is_machine, DWORD time);
-  HRESULT DeleteFirstInstallTime(bool is_machine);
-  HRESULT SetUpdateTime(bool is_machine, DWORD time);
-  HRESULT DeleteUpdateTime(bool is_machine);
-
-  CString hive_override_key_name_;
-};
-
-void ConfigManagerTest::CanCollectStatsHelper(bool is_machine) {
-  const TCHAR* app1_state_key_name = is_machine ? kAppMachineClientStatePath1 :
-                                                  kAppUserClientStatePath1;
-
-  EXPECT_FALSE(cm_->CanCollectStats(is_machine));
-
-  // Test the 'UsageStats' override.
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueForceUsageStats,
-                                    _T("")));
-  EXPECT_TRUE(cm_->CanCollectStats(is_machine));
-  EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV,
-                                       kRegValueForceUsageStats));
-
-  DWORD val = 1;
-  EXPECT_SUCCEEDED(RegKey::SetValue(app1_state_key_name,
-                                    _T("usagestats"),
-                                    val));
-  EXPECT_TRUE(cm_->CanCollectStats(is_machine));
-
-  val = 2;  // invalid value
-  EXPECT_SUCCEEDED(RegKey::SetValue(app1_state_key_name,
-                                    _T("usagestats"),
-                                    val));
-  EXPECT_FALSE(cm_->CanCollectStats(is_machine));
-
-  val = 0;
-  EXPECT_SUCCEEDED(RegKey::SetValue(app1_state_key_name,
-                                    _T("usagestats"),
-                                    val));
-  EXPECT_FALSE(cm_->CanCollectStats(is_machine));
-
-  // One 0 and one 1 results in true. The alphabetical order of the GUIDs is
-  // important assuming GetSubkeyNameAt returns subkeys in alphabetical order.
-  const TCHAR* app2_state_key_name = is_machine ? kAppMachineClientStatePath2 :
-                                                  kAppUserClientStatePath2;
-  val = 1;
-  EXPECT_SUCCEEDED(RegKey::SetValue(app2_state_key_name,
-                                    _T("usagestats"),
-                                    val));
-  EXPECT_TRUE(cm_->CanCollectStats(is_machine));
-}
-
-void ConfigManagerTest::CanCollectStatsIgnoresOppositeHiveHelper(
-    bool is_machine) {
-  const TCHAR* app1_state_key_name = is_machine ? kAppMachineClientStatePath1 :
-                                                  kAppUserClientStatePath1;
-
-  EXPECT_FALSE(cm_->CanCollectStats(is_machine));
-
-  DWORD val = 1;
-  EXPECT_SUCCEEDED(RegKey::SetValue(app1_state_key_name,
-                                    _T("usagestats"),
-                                    val));
-  EXPECT_TRUE(cm_->CanCollectStats(is_machine));
-  EXPECT_FALSE(cm_->CanCollectStats(!is_machine));
-}
-
-HRESULT ConfigManagerTest::SetFirstInstallTime(bool is_machine, DWORD time) {
-  return RegKey::SetValue(cm_->registry_client_state_goopdate(is_machine),
-                          kRegValueInstallTimeSec,
-                          time);
-}
-
-HRESULT ConfigManagerTest::DeleteFirstInstallTime(bool is_machine) {
-  if (!RegKey::HasValue(cm_->registry_client_state_goopdate(is_machine),
-                        kRegValueInstallTimeSec)) {
-    return S_OK;
-  }
-
-  return RegKey::DeleteValue(cm_->registry_client_state_goopdate(is_machine),
-                             kRegValueInstallTimeSec);
-}
-
-HRESULT ConfigManagerTest::SetUpdateTime(bool is_machine, DWORD time) {
-  return RegKey::SetValue(cm_->registry_client_state_goopdate(is_machine),
-                          kRegValueLastUpdateTimeSec,
-                          time);
-}
-
-HRESULT ConfigManagerTest::DeleteUpdateTime(bool is_machine) {
-  if (!RegKey::HasValue(cm_->registry_client_state_goopdate(is_machine),
-                        kRegValueLastUpdateTimeSec)) {
-    return S_OK;
-  }
-
-  return RegKey::DeleteValue(cm_->registry_client_state_goopdate(is_machine),
-                             kRegValueLastUpdateTimeSec);
-}
-
-TEST_F(ConfigManagerNoOverrideTest, RegistryKeys) {
-  EXPECT_STREQ(_T("HKCU\\Software\\Google\\Update\\Clients\\"),
-               cm_->user_registry_clients());
-  EXPECT_STREQ(_T("HKLM\\Software\\Google\\Update\\Clients\\"),
-               cm_->machine_registry_clients());
-  EXPECT_STREQ(_T("HKCU\\Software\\Google\\Update\\Clients\\"),
-               cm_->registry_clients(false));
-  EXPECT_STREQ(_T("HKLM\\Software\\Google\\Update\\Clients\\"),
-               cm_->registry_clients(true));
-
-  EXPECT_STREQ(_T("HKCU\\Software\\Google\\Update\\Clients\\")
-               _T("{430FD4D0-B729-4F61-AA34-91526481799D}"),
-               cm_->user_registry_clients_goopdate());
-  EXPECT_STREQ(_T("HKLM\\Software\\Google\\Update\\Clients\\")
-               _T("{430FD4D0-B729-4F61-AA34-91526481799D}"),
-               cm_->machine_registry_clients_goopdate());
-  EXPECT_STREQ(_T("HKCU\\Software\\Google\\Update\\Clients\\")
-               _T("{430FD4D0-B729-4F61-AA34-91526481799D}"),
-               cm_->registry_clients_goopdate(false));
-  EXPECT_STREQ(_T("HKLM\\Software\\Google\\Update\\Clients\\")
-               _T("{430FD4D0-B729-4F61-AA34-91526481799D}"),
-               cm_->registry_clients_goopdate(true));
-
-  EXPECT_STREQ(_T("HKCU\\Software\\Google\\Update\\ClientState\\"),
-               cm_->user_registry_client_state());
-  EXPECT_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientState\\"),
-               cm_->machine_registry_client_state());
-  EXPECT_STREQ(_T("HKCU\\Software\\Google\\Update\\ClientState\\"),
-               cm_->registry_client_state(false));
-  EXPECT_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientState\\"),
-               cm_->registry_client_state(true));
-
-  EXPECT_STREQ(_T("HKCU\\Software\\Google\\Update\\ClientState\\")
-               _T("{430FD4D0-B729-4F61-AA34-91526481799D}"),
-               cm_->user_registry_client_state_goopdate());
-  EXPECT_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientState\\")
-               _T("{430FD4D0-B729-4F61-AA34-91526481799D}"),
-               cm_->machine_registry_client_state_goopdate());
-  EXPECT_STREQ(_T("HKCU\\Software\\Google\\Update\\ClientState\\")
-               _T("{430FD4D0-B729-4F61-AA34-91526481799D}"),
-               cm_->registry_client_state_goopdate(false));
-  EXPECT_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientState\\")
-               _T("{430FD4D0-B729-4F61-AA34-91526481799D}"),
-               cm_->registry_client_state_goopdate(true));
-
-  EXPECT_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientStateMedium\\"),
-               cm_->machine_registry_client_state_medium());
-
-  EXPECT_STREQ(_T("HKCU\\Software\\Google\\Update\\"),
-               cm_->user_registry_update());
-  EXPECT_STREQ(_T("HKLM\\Software\\Google\\Update\\"),
-               cm_->machine_registry_update());
-  EXPECT_STREQ(_T("HKCU\\Software\\Google\\Update\\"),
-               cm_->registry_update(false));
-  EXPECT_STREQ(_T("HKLM\\Software\\Google\\Update\\"),
-               cm_->registry_update(true));
-
-  EXPECT_STREQ(_T("HKCU\\Software\\Google\\"),
-               cm_->user_registry_google());
-  EXPECT_STREQ(_T("HKLM\\Software\\Google\\"),
-               cm_->machine_registry_google());
-  EXPECT_STREQ(_T("HKCU\\Software\\Google\\"),
-               cm_->registry_google(false));
-  EXPECT_STREQ(_T("HKLM\\Software\\Google\\"),
-               cm_->registry_google(true));
-}
-
-TEST_F(ConfigManagerNoOverrideTest, GetUserCrashReportsDir) {
-  const CString expected_path = GetGoogleUserPath() + _T("CrashReports");
-  EXPECT_SUCCEEDED(DeleteDirectory(expected_path));
-  EXPECT_STREQ(expected_path, cm_->GetUserCrashReportsDir());
-  EXPECT_TRUE(File::Exists(expected_path));
-}
-
-// Should run before the subdirectory tests to ensure the directory is created.
-TEST_F(ConfigManagerNoOverrideTest, GetUserGoopdateInstallDir) {
-  const CString expected_path = GetGoogleUserPath() + _T("Update");
-  EXPECT_STREQ(expected_path, cm_->GetUserGoopdateInstallDir());
-  EXPECT_TRUE(File::Exists(expected_path));
-}
-
-TEST_F(ConfigManagerNoOverrideTest, GetDownloadStorage) {
-  const CString expected_path = GetGoogleUpdateUserPath() + _T("Download");
-  EXPECT_SUCCEEDED(DeleteDirectory(expected_path));
-  EXPECT_STREQ(expected_path, cm_->GetUserDownloadStorageDir());
-  EXPECT_TRUE(File::Exists(expected_path));
-}
-
-TEST_F(ConfigManagerNoOverrideTest, GetUserDownloadStorageDir) {
-  const CString expected_path = GetGoogleUpdateUserPath() + _T("Download");
-  EXPECT_SUCCEEDED(DeleteDirectory(expected_path));
-  EXPECT_STREQ(expected_path, cm_->GetUserDownloadStorageDir());
-  EXPECT_TRUE(File::Exists(expected_path));
-}
-
-TEST_F(ConfigManagerNoOverrideTest, GetUserOfflineStorageDir) {
-  const CString expected_path = GetGoogleUpdateUserPath() + _T("Offline");
-  EXPECT_SUCCEEDED(DeleteDirectory(expected_path));
-  EXPECT_STREQ(expected_path, cm_->GetUserOfflineStorageDir());
-  EXPECT_TRUE(File::Exists(expected_path));
-}
-
-TEST_F(ConfigManagerNoOverrideTest, IsRunningFromUserGoopdateInstallDir) {
-  EXPECT_FALSE(cm_->IsRunningFromUserGoopdateInstallDir());
-}
-
-TEST_F(ConfigManagerNoOverrideTest, GetMachineCrashReportsDir) {
-  CString program_files;
-  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &program_files));
-  const CString expected_path = program_files + _T("\\Google\\CrashReports");
-  EXPECT_SUCCEEDED(DeleteDirectory(expected_path));
-  EXPECT_STREQ(expected_path, cm_->GetMachineCrashReportsDir());
-  EXPECT_TRUE(File::Exists(expected_path) || !vista_util::IsUserAdmin());
-}
-
-// Should run before the subdirectory tests to ensure the directory is created.
-TEST_F(ConfigManagerNoOverrideTest, GetMachineGoopdateInstallDir) {
-  CString expected_path = GetGoogleUpdateMachinePath();
-  EXPECT_STREQ(expected_path, cm_->GetMachineGoopdateInstallDir());
-  EXPECT_TRUE(File::Exists(expected_path) || !vista_util::IsUserAdmin());
-}
-
-TEST_F(ConfigManagerNoOverrideTest, GetMachineSecureDownloadStorageDir) {
-  CString expected_path = GetGoogleUpdateMachinePath() + _T("\\Download");
-  EXPECT_SUCCEEDED(DeleteDirectory(expected_path));
-  EXPECT_STREQ(expected_path, cm_->GetMachineSecureDownloadStorageDir());
-  EXPECT_TRUE(File::Exists(expected_path) || !vista_util::IsUserAdmin());
-}
-
-TEST_F(ConfigManagerNoOverrideTest, GetMachineSecureOfflineStorageDir) {
-  CString expected_path = GetGoogleUpdateMachinePath() + _T("\\Offline");
-  EXPECT_SUCCEEDED(DeleteDirectory(expected_path));
-  EXPECT_STREQ(expected_path, cm_->GetMachineSecureOfflineStorageDir());
-  EXPECT_TRUE(File::Exists(expected_path) || !vista_util::IsUserAdmin());
-}
-
-TEST_F(ConfigManagerNoOverrideTest, IsRunningFromMachineGoopdateInstallDir) {
-  EXPECT_FALSE(cm_->IsRunningFromMachineGoopdateInstallDir());
-}
-
-// Tests the GetUpdateCheckUrl override.
-TEST_F(ConfigManagerTest, GetUpdateCheckUrl) {
-  CString url;
-  EXPECT_SUCCEEDED(cm_->GetUpdateCheckUrl(&url));
-  EXPECT_STREQ(url, kUrlUpdateCheck);
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueNameUrl,
-                                    _T("http://foo/")));
-  url.Empty();
-  EXPECT_TRUE(url.IsEmpty());
-  EXPECT_SUCCEEDED(cm_->GetUpdateCheckUrl(&url));
-#ifdef DEBUG
-  EXPECT_STREQ(url, _T("http://foo/"));
-#else
-  EXPECT_STREQ(url, kUrlUpdateCheck);
-#endif
-}
-
-// Tests the GetPingUrl override.
-TEST_F(ConfigManagerTest, GetPingUrl) {
-  CString url;
-  EXPECT_SUCCEEDED(cm_->GetPingUrl(&url));
-  EXPECT_STREQ(url, kUrlPing);
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueNamePingUrl,
-                                    _T("http://bar/")));
-  url.Empty();
-  EXPECT_TRUE(url.IsEmpty());
-  EXPECT_SUCCEEDED(cm_->GetPingUrl(&url));
-#ifdef DEBUG
-  EXPECT_STREQ(url, _T("http://bar/"));
-#else
-  EXPECT_STREQ(url, kUrlPing);
-#endif
-}
-
-// Tests LastCheckPeriodSec override.
-TEST_F(ConfigManagerTest, GetLastCheckPeriodSec_Default) {
-  bool is_overridden = true;
-  if (cm_->IsGoogler()) {
-    EXPECT_EQ(kLastCheckPeriodGooglerSec,
-              cm_->GetLastCheckPeriodSec(&is_overridden));
-  } else {
-    EXPECT_EQ(kLastCheckPeriodSec, cm_->GetLastCheckPeriodSec(&is_overridden));
-  }
-  EXPECT_FALSE(is_overridden);
-}
-
-TEST_F(ConfigManagerTest, GetLastCheckPeriodSec_UpdateDevOverride) {
-  // Zero is a special value meaning disabled.
-  DWORD val = 0;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueLastCheckPeriodSec,
-                                    val));
-  bool is_overridden = false;
-  EXPECT_EQ(0, cm_->GetLastCheckPeriodSec(&is_overridden));
-  EXPECT_TRUE(is_overridden);
-
-  val = kMinLastCheckPeriodSec - 1;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueLastCheckPeriodSec,
-                                    val));
-  is_overridden = false;
-  EXPECT_EQ(kMinLastCheckPeriodSec, cm_->GetLastCheckPeriodSec(&is_overridden));
-  EXPECT_TRUE(is_overridden);
-
-  val = INT_MAX + static_cast<uint32>(1);
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueLastCheckPeriodSec,
-                                    val));
-  is_overridden = false;
-  EXPECT_EQ(INT_MAX, cm_->GetLastCheckPeriodSec(&is_overridden));
-  EXPECT_TRUE(is_overridden);
-
-  val = 1000;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueLastCheckPeriodSec,
-                                    val));
-  is_overridden = false;
-  EXPECT_EQ(1000, cm_->GetLastCheckPeriodSec(&is_overridden));
-  EXPECT_TRUE(is_overridden);
-
-  EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV,
-                                       kRegValueLastCheckPeriodSec));
-  is_overridden = true;
-  if (cm_->IsGoogler()) {
-    EXPECT_EQ(kLastCheckPeriodGooglerSec,
-              cm_->GetLastCheckPeriodSec(&is_overridden));
-  } else {
-    EXPECT_EQ(kLastCheckPeriodSec, cm_->GetLastCheckPeriodSec(&is_overridden));
-  }
-  EXPECT_FALSE(is_overridden);
-}
-
-TEST_F(ConfigManagerTest, GetLastCheckPeriodSec_GroupPolicyOverride) {
-  const DWORD kOverrideMinutes = 16000;
-  const DWORD kExpectedSeconds = kOverrideMinutes * 60;
-  EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"),
-                             kOverrideMinutes));
-  bool is_overridden = false;
-  EXPECT_EQ(kExpectedSeconds, cm_->GetLastCheckPeriodSec(&is_overridden));
-  EXPECT_TRUE(is_overridden);
-}
-
-TEST_F(ConfigManagerTest, GetLastCheckPeriodSec_GroupPolicyOverride_TooLow) {
-  const DWORD kOverrideMinutes = 1;
-  EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"),
-                             kOverrideMinutes));
-  bool is_overridden = false;
-  EXPECT_EQ(kMinLastCheckPeriodSec, cm_->GetLastCheckPeriodSec(&is_overridden));
-  EXPECT_TRUE(is_overridden);
-}
-
-TEST_F(ConfigManagerTest, GetLastCheckPeriodSec_GroupPolicyOverride_Zero) {
-  const DWORD kOverrideMinutes = 0;
-  EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"),
-                             kOverrideMinutes));
-  bool is_overridden = false;
-  EXPECT_EQ(0, cm_->GetLastCheckPeriodSec(&is_overridden));
-  EXPECT_TRUE(is_overridden);
-}
-
-TEST_F(ConfigManagerTest,
-       GetLastCheckPeriodSec_GroupPolicyOverride_Overflow_SecondsConversion) {
-  const DWORD kOverrideMinutes = UINT_MAX;
-  EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"),
-                             kOverrideMinutes));
-  bool is_overridden = false;
-  EXPECT_EQ(INT_MAX, cm_->GetLastCheckPeriodSec(&is_overridden));
-  EXPECT_TRUE(is_overridden);
-
-  const DWORD kOverrideMinutes2 = INT_MAX + static_cast<uint32>(1);
-  EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"),
-                             kOverrideMinutes2));
-  is_overridden = false;
-  EXPECT_EQ(INT_MAX, cm_->GetLastCheckPeriodSec(&is_overridden));
-  EXPECT_TRUE(is_overridden);
-
-  const DWORD kOverrideMinutes3 = 0xf0000000;
-  EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"),
-                             kOverrideMinutes3));
-  is_overridden = false;
-  EXPECT_EQ(INT_MAX, cm_->GetLastCheckPeriodSec(&is_overridden));
-  EXPECT_TRUE(is_overridden);
-}
-
-// Overflow the integer but not the minutes to seconds conversion.
-TEST_F(ConfigManagerTest,
-       GetLastCheckPeriodSec_GroupPolicyOverride_Overflow_Int) {
-  const DWORD kOverrideMinutes = UINT_MAX / 60;
-  EXPECT_GT(UINT_MAX, kOverrideMinutes);
-
-  EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"),
-                             kOverrideMinutes));
-  bool is_overridden = false;
-  EXPECT_EQ(INT_MAX, cm_->GetLastCheckPeriodSec(&is_overridden));
-  EXPECT_TRUE(is_overridden);
-}
-
-// UpdateDev takes precedence over the Group Policy override.
-TEST_F(ConfigManagerTest,
-       GetLastCheckPeriodSec_GroupPolicyAndUpdateDevOverrides) {
-  const DWORD kGroupPolicyOverrideMinutes = 100;
-  EXPECT_SUCCEEDED(SetPolicy(_T("AutoUpdateCheckPeriodMinutes"),
-                             kGroupPolicyOverrideMinutes));
-  const DWORD kUpdateDevOverrideSeconds = 70;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueLastCheckPeriodSec,
-                                    kUpdateDevOverrideSeconds));
-
-  bool is_overridden = false;
-  EXPECT_EQ(kUpdateDevOverrideSeconds,
-            cm_->GetLastCheckPeriodSec(&is_overridden));
-  EXPECT_TRUE(is_overridden);
-}
-
-// Legacy location is not checked.
-TEST_F(ConfigManagerTest, CanCollectStats_LegacyLocationAndName) {
-  DWORD val = 1;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    kLegacyRegValueCollectUsageStats,
-                                    val));
-  EXPECT_FALSE(cm_->CanCollectStats(true));
-}
-
-TEST_F(ConfigManagerTest, CanCollectStats_LegacyLocationNewName) {
-  DWORD val = 1;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("usagestats"),
-                                    val));
-  EXPECT_FALSE(cm_->CanCollectStats(true));
-}
-
-TEST_F(ConfigManagerTest, CanCollectStats_MachineOnly) {
-  CanCollectStatsHelper(true);
-}
-
-TEST_F(ConfigManagerTest, CanCollectStats_UserOnly) {
-  CanCollectStatsHelper(false);
-}
-
-// This tests that the legacy conversion is honored.
-TEST_F(ConfigManagerTest, CanCollectStats_GoopdateGuidIsChecked) {
-  EXPECT_FALSE(cm_->CanCollectStats(true));
-
-  DWORD val = 1;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("usagestats"),
-                                    val));
-  EXPECT_TRUE(cm_->CanCollectStats(true));
-}
-
-TEST_F(ConfigManagerTest, CanCollectStats_MachineIgnoresUser) {
-  CanCollectStatsIgnoresOppositeHiveHelper(true);
-}
-
-TEST_F(ConfigManagerTest, CanCollectStats_UserIgnoresMachine) {
-  CanCollectStatsIgnoresOppositeHiveHelper(false);
-}
-// Unfortunately, the app's ClientStateMedium key is not checked if there is no
-// corresponding ClientState key.
-TEST_F(ConfigManagerTest,
-       CanCollectStats_Machine_ClientStateMediumOnly_AppClientStateKeyMissing) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath1,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_FALSE(cm_->CanCollectStats(true));
-}
-
-TEST_F(ConfigManagerTest,
-       CanCollectStats_Machine_ClientStateMediumOnly_AppClientStateKeyExists) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath1));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath1,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(cm_->CanCollectStats(true));
-}
-
-TEST_F(ConfigManagerTest,
-       CanCollectStats_Machine_ClientStateMediumInvalid) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath1));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath1,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(2)));
-  EXPECT_FALSE(cm_->CanCollectStats(true));
-}
-
-TEST_F(ConfigManagerTest, CanCollectStats_User_ClientStateMediumOnly) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStatePath1));
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKCU\\Software\\Google\\Update\\ClientStateMedium\\") APP_GUID1,
-      _T("usagestats"),
-      static_cast<DWORD>(1)));
-  EXPECT_FALSE(cm_->CanCollectStats(false));
-}
-
-TEST_F(ConfigManagerTest,
-       CanCollectStats_Machine_ClientStateZeroClientStateMediumOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath1,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_FALSE(cm_->CanCollectStats(true));
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath1,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(cm_->CanCollectStats(true));
-}
-
-TEST_F(ConfigManagerTest,
-       CanCollectStats_Machine_ClientStateOneClientStateMediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath1,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(cm_->CanCollectStats(true));
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath1,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_FALSE(cm_->CanCollectStats(true));
-}
-
-// Tests OverInstall override.
-TEST_F(ConfigManagerTest, CanOverInstall) {
-  EXPECT_EQ(cm_->CanOverInstall(), !OFFICIAL_BUILD);
-
-  DWORD val = 1;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueNameOverInstall,
-                                    val));
-#ifdef DEBUG
-  EXPECT_TRUE(cm_->CanOverInstall());
-#else
-  EXPECT_EQ(!OFFICIAL_BUILD, cm_->CanOverInstall());
-#endif
-
-  val = 0;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueNameOverInstall,
-                                    val));
-#ifdef DEBUG
-  EXPECT_FALSE(cm_->CanOverInstall());
-#else
-  EXPECT_EQ(!OFFICIAL_BUILD, cm_->CanOverInstall());
-#endif
-}
-
-// Tests AuCheckPeriodMs override.
-TEST_F(ConfigManagerTest, GetAutoUpdateTimerIntervalMs) {
-  EXPECT_EQ(cm_->IsGoogler() ? kAUCheckPeriodGooglerMs :
-                               kAUCheckPeriodMs,
-            cm_->GetAutoUpdateTimerIntervalMs());
-
-  DWORD val = 0;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueAuCheckPeriodMs,
-                                    val));
-  EXPECT_EQ(kMinAUCheckPeriodMs, cm_->GetAutoUpdateTimerIntervalMs());
-
-  val = kMinAUCheckPeriodMs - 1;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueAuCheckPeriodMs,
-                                    val));
-  EXPECT_EQ(kMinAUCheckPeriodMs, cm_->GetAutoUpdateTimerIntervalMs());
-
-  val = 30000;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueAuCheckPeriodMs,
-                                    val));
-  EXPECT_EQ(val, cm_->GetAutoUpdateTimerIntervalMs());
-
-  val = INT_MAX;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueAuCheckPeriodMs,
-                                    val));
-  EXPECT_EQ(val, cm_->GetAutoUpdateTimerIntervalMs());
-
-  // Tests overflow with large positive numbers.
-  val = INT_MAX;
-  ++val;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueAuCheckPeriodMs,
-                                    val));
-  EXPECT_EQ(INT_MAX, cm_->GetAutoUpdateTimerIntervalMs());
-
-  val = UINT_MAX;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueAuCheckPeriodMs,
-                                    val));
-  EXPECT_EQ(INT_MAX, cm_->GetAutoUpdateTimerIntervalMs());
-}
-
-// Tests CrCheckPeriodMs override.
-TEST_F(ConfigManagerTest, GetCodeRedTimerIntervalMs) {
-  EXPECT_EQ(kCodeRedCheckPeriodMs, cm_->GetCodeRedTimerIntervalMs());
-
-  DWORD val = 0;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueCrCheckPeriodMs,
-                                    val));
-  EXPECT_EQ(kMinCodeRedCheckPeriodMs, cm_->GetCodeRedTimerIntervalMs());
-
-  val = kMinCodeRedCheckPeriodMs - 1;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueCrCheckPeriodMs,
-                                    val));
-  EXPECT_EQ(kMinCodeRedCheckPeriodMs, cm_->GetCodeRedTimerIntervalMs());
-
-  val = 60000;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueCrCheckPeriodMs,
-                                    val));
-  EXPECT_EQ(val, cm_->GetCodeRedTimerIntervalMs());
-
-  val = INT_MAX;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueCrCheckPeriodMs,
-                                    val));
-  EXPECT_EQ(val, cm_->GetCodeRedTimerIntervalMs());
-
-  // Tests overflow with large positive numbers.
-  val = INT_MAX;
-  ++val;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueCrCheckPeriodMs,
-                                    val));
-  EXPECT_EQ(INT_MAX, cm_->GetCodeRedTimerIntervalMs());
-
-  val = UINT_MAX;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                      kRegValueCrCheckPeriodMs,
-                                      val));
-  EXPECT_EQ(INT_MAX, cm_->GetCodeRedTimerIntervalMs());
-}
-
-// Tests CanLogEvents override.
-TEST_F(ConfigManagerTest, CanLogEvents_WithOutOverride) {
-  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_SUCCESS));
-  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_ERROR_TYPE));
-  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_WARNING_TYPE));
-  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_INFORMATION_TYPE));
-  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_AUDIT_SUCCESS));
-  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_AUDIT_FAILURE));
-}
-
-TEST_F(ConfigManagerTest, CanLogEvents) {
-  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_INFORMATION_TYPE));
-
-  DWORD val = LOG_EVENT_LEVEL_ALL;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueEventLogLevel,
-                                    val));
-  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_SUCCESS));
-  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_ERROR_TYPE));
-  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_WARNING_TYPE));
-  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_INFORMATION_TYPE));
-  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_AUDIT_SUCCESS));
-  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_AUDIT_FAILURE));
-
-  val = LOG_EVENT_LEVEL_WARN_AND_ERROR;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueEventLogLevel,
-                                    val));
-  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_SUCCESS));
-  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_ERROR_TYPE));
-  EXPECT_TRUE(cm_->CanLogEvents(EVENTLOG_WARNING_TYPE));
-  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_INFORMATION_TYPE));
-  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_AUDIT_SUCCESS));
-  EXPECT_FALSE(cm_->CanLogEvents(EVENTLOG_AUDIT_FAILURE));
-}
-
-// Tests GetTestSource override.
-TEST_F(ConfigManagerTest, GetTestSource_Dev) {
-  CString expected_value;
-#if DEBUG || !OFFICIAL_BUILD
-  expected_value = kRegValueTestSourceAuto;
-#endif
-
-  CString test_source = cm_->GetTestSource();
-  EXPECT_STREQ(expected_value, test_source);
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueTestSource,
-                                   _T("dev")));
-  test_source = cm_->GetTestSource();
-  EXPECT_STREQ(_T("dev"), test_source);
-}
-
-TEST_F(ConfigManagerTest, GetTestSource_EmptyRegKey) {
-  CString expected_value;
-
-#if DEBUG || !OFFICIAL_BUILD
-  expected_value = kRegValueTestSourceAuto;
-#endif
-
-  CString test_source = cm_->GetTestSource();
-  EXPECT_STREQ(expected_value, test_source);
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueTestSource,
-                                   _T("")));
-  test_source = cm_->GetTestSource();
-  EXPECT_STREQ(kRegValueTestSourceAuto, test_source);
-}
-
-//
-// CanUseNetwork tests.
-//
-
-// Covers UpdateEulaAccepted case.
-TEST_F(ConfigManagerTest, CanUseNetwork_Machine_Normal) {
-  EXPECT_TRUE(cm_->CanUseNetwork(true));
-}
-
-// Covers UpdateEulaAccepted case.
-TEST_F(ConfigManagerTest, CanUseNetwork_User_Normal) {
-  EXPECT_TRUE(cm_->CanUseNetwork(false));
-}
-
-// These cover the not OEM install mode cases.
-TEST_F(ConfigManagerTest, CanUseNetwork_Machine_UpdateEulaNotAccepted) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_FALSE(cm_->CanUseNetwork(true));
-}
-
-TEST_F(ConfigManagerTest,
-       CanUseNetwork_Machine_UpdateEulaNotAccepted_AppEulaAccepted) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath1,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_FALSE(cm_->CanUseNetwork(true));
-}
-
-TEST_F(ConfigManagerTest, CanUseNetwork_Machine_AppEulaNotAccepted) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath1,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_TRUE(cm_->CanUseNetwork(true));
-}
-
-TEST_F(ConfigManagerTest, CanUseNetwork_Machine_AppEulaAccepted) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath1,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(cm_->CanUseNetwork(true));
-}
-
-TEST_F(ConfigManagerTest, CanUseNetwork_Machine_UserUpdateEulaNotAccepted) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_TRUE(cm_->CanUseNetwork(true));
-}
-
-TEST_F(ConfigManagerTest, CanUseNetwork_User_UpdateEulaNotAccepted) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_FALSE(cm_->CanUseNetwork(false));
-}
-
-TEST_F(ConfigManagerTest,
-       CanUseNetwork_User_UpdateEulaNotAccepted_AppEulaAccepted) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath1,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_FALSE(cm_->CanUseNetwork(false));
-}
-
-TEST_F(ConfigManagerTest, CanUseNetwork_User_AppEulaNotAccepted) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath1,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_TRUE(cm_->CanUseNetwork(false));
-}
-
-TEST_F(ConfigManagerTest, CanUseNetwork_User_AppEulaAccepted) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath1,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(cm_->CanUseNetwork(false));
-}
-
-TEST_F(ConfigManagerTest, CanUseNetwork_User_MachineUpdateEulaNotAccepted) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_TRUE(cm_->CanUseNetwork(false));
-}
-
-
-// Covers UpdateEulaAccepted case.
-TEST_F(ConfigManagerTest,
-       CanUseNetwork_Machine_OemInstallTimeNow_NotAuditMode) {
-  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    now_seconds));
-  EXPECT_TRUE(cm_->IsOemInstalling(true));
-
-  EXPECT_FALSE(cm_->CanUseNetwork(true));
-}
-
-TEST_F(ConfigManagerTest,
-       CanUseNetwork_Machine_OemInstallTimeNow_NotAuditMode_UpdateEulaNotAccepted) {  //NOLINT
-  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    now_seconds));
-  EXPECT_TRUE(cm_->IsOemInstalling(true));
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  EXPECT_FALSE(cm_->CanUseNetwork(true));
-}
-
-// These cover the EULA accepted cases.
-TEST_F(ConfigManagerTest,
-       CanUseNetwork_Machine_OemInstallTime73HoursAgo_NotAuditMode) {
-  const DWORD kDesiredDifferenceSeconds = 73 * 60 * 60;  // 73 hours.
-  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  EXPECT_GT(now_seconds, kDesiredDifferenceSeconds);
-  const DWORD install_time_seconds = now_seconds - kDesiredDifferenceSeconds;
-  EXPECT_LT(install_time_seconds, now_seconds);
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    install_time_seconds));
-  EXPECT_FALSE(cm_->IsOemInstalling(true));
-
-  EXPECT_TRUE(cm_->CanUseNetwork(true));
-}
-
-TEST_F(ConfigManagerTest, CanUseNetwork_User_OemInstallTimeNow_AuditMode) {
-  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    now_seconds));
-
-  if (vista_util::IsVistaOrLater()) {
-    EXPECT_SUCCEEDED(RegKey::SetValue(
-        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-        _T("ImageState"),
-        _T("IMAGE_STATE_UNDEPLOYABLE")));
-  } else {
-    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
-                                      _T("AuditInProgress"),
-                                      static_cast<DWORD>(1)));
-  }
-  EXPECT_FALSE(cm_->IsOemInstalling(false));
-
-  EXPECT_TRUE(cm_->CanUseNetwork(false));
-}
-
-TEST_F(ConfigManagerTest, CanUseNetwork_User_OemInstallTimeNow_AuditMode_UpdateEulaNotAccepted) {  //NOLINT
-  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    now_seconds));
-
-  if (vista_util::IsVistaOrLater()) {
-    EXPECT_SUCCEEDED(RegKey::SetValue(
-        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-        _T("ImageState"),
-        _T("IMAGE_STATE_UNDEPLOYABLE")));
-  } else {
-    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
-                                      _T("AuditInProgress"),
-                                      static_cast<DWORD>(1)));
-  }
-  EXPECT_FALSE(cm_->IsOemInstalling(false));
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  EXPECT_FALSE(cm_->CanUseNetwork(false));
-}
-
-//
-// IsOemInstalling tests.
-//
-
-TEST_F(ConfigManagerTest, IsOemInstalling_Machine_Normal) {
-  EXPECT_FALSE(cm_->IsOemInstalling(true));
-}
-
-TEST_F(ConfigManagerTest, IsOemInstalling_User_Normal) {
-  EXPECT_FALSE(cm_->IsOemInstalling(false));
-}
-
-TEST_F(ConfigManagerTest,
-       IsOemInstalling_Machine_OemInstallTimeNow_NotAuditMode) {
-  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    now_seconds));
-
-  EXPECT_TRUE(cm_->IsOemInstalling(true));
-}
-
-TEST_F(ConfigManagerTest, IsOemInstalling_Machine_OemInstallTimeNow_AuditMode) {
-  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    now_seconds));
-
-  if (vista_util::IsVistaOrLater()) {
-    EXPECT_SUCCEEDED(RegKey::SetValue(
-        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-        _T("ImageState"),
-        _T("IMAGE_STATE_UNDEPLOYABLE")));
-  } else {
-    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
-                                      _T("AuditInProgress"),
-                                      static_cast<DWORD>(1)));
-  }
-  EXPECT_TRUE(cm_->IsWindowsInstalling());
-
-  EXPECT_TRUE(cm_->IsOemInstalling(true));
-}
-
-TEST_F(ConfigManagerTest,
-       IsOemInstalling_Machine_OemInstallTime71HoursAgo_NotAuditMode) {
-  const DWORD kDesiredDifferenceSeconds = 71 * 60 * 60;  // 71 hours.
-  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  EXPECT_GT(now_seconds, kDesiredDifferenceSeconds);
-  const DWORD install_time_seconds = now_seconds - kDesiredDifferenceSeconds;
-  EXPECT_LT(install_time_seconds, now_seconds);
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    install_time_seconds));
-
-  EXPECT_TRUE(cm_->IsOemInstalling(true));
-}
-
-TEST_F(ConfigManagerTest,
-       IsOemInstalling_Machine_OemInstallTime71HoursAgo_AuditMode) {
-  const DWORD kDesiredDifferenceSeconds = 71 * 60 * 60;  // 71 hours.
-  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  EXPECT_GT(now_seconds, kDesiredDifferenceSeconds);
-  const DWORD install_time_seconds = now_seconds - kDesiredDifferenceSeconds;
-  EXPECT_LT(install_time_seconds, now_seconds);
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    install_time_seconds));
-
-  if (vista_util::IsVistaOrLater()) {
-    EXPECT_SUCCEEDED(RegKey::SetValue(
-        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-        _T("ImageState"),
-        _T("IMAGE_STATE_UNDEPLOYABLE")));
-  } else {
-    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
-                                      _T("AuditInProgress"),
-                                      static_cast<DWORD>(1)));
-  }
-  EXPECT_TRUE(cm_->IsWindowsInstalling());
-
-  EXPECT_TRUE(cm_->IsOemInstalling(true));
-}
-
-TEST_F(ConfigManagerTest,
-       IsOemInstalling_Machine_OemInstallTime73HoursAgo_NotAuditMode) {
-  const DWORD kDesiredDifferenceSeconds = 73 * 60 * 60;  // 73 hours.
-  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  EXPECT_GT(now_seconds, kDesiredDifferenceSeconds);
-  const DWORD install_time_seconds = now_seconds - kDesiredDifferenceSeconds;
-  EXPECT_LT(install_time_seconds, now_seconds);
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    install_time_seconds));
-
-  EXPECT_FALSE(cm_->IsOemInstalling(true));
-}
-
-TEST_F(ConfigManagerTest,
-       IsOemInstalling_Machine_OemInstallTime73HoursAgo_AuditMode) {
-  const DWORD kDesiredDifferenceSeconds = 73 * 60 * 60;  // 73 hours.
-  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  EXPECT_GT(now_seconds, kDesiredDifferenceSeconds);
-  const DWORD install_time_seconds = now_seconds - kDesiredDifferenceSeconds;
-  EXPECT_LT(install_time_seconds, now_seconds);
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    install_time_seconds));
-
-  if (vista_util::IsVistaOrLater()) {
-    EXPECT_SUCCEEDED(RegKey::SetValue(
-        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-        _T("ImageState"),
-        _T("IMAGE_STATE_UNDEPLOYABLE")));
-  } else {
-    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
-                                      _T("AuditInProgress"),
-                                      static_cast<DWORD>(1)));
-  }
-  EXPECT_TRUE(cm_->IsWindowsInstalling());
-
-  EXPECT_FALSE(cm_->IsOemInstalling(true));
-}
-
-TEST_F(ConfigManagerTest,
-       IsOemInstalling_Machine_OemInstallTime71HoursInFuture_NotAuditMode) {
-  const DWORD kDesiredDifferenceSeconds = 71 * 60 * 60;  // 71 hours.
-  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  const DWORD install_time_seconds = now_seconds + kDesiredDifferenceSeconds;
-  EXPECT_GT(install_time_seconds, now_seconds);
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    install_time_seconds));
-
-  EXPECT_TRUE(cm_->IsOemInstalling(true));
-}
-
-TEST_F(ConfigManagerTest,
-       IsOemInstalling_Machine_OemInstallTime71HoursInFuture_AuditMode) {
-  const DWORD kDesiredDifferenceSeconds = 71 * 60 * 60;  // 71 hours.
-  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  const DWORD install_time_seconds = now_seconds + kDesiredDifferenceSeconds;
-  EXPECT_GT(install_time_seconds, now_seconds);
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    install_time_seconds));
-
-  if (vista_util::IsVistaOrLater()) {
-    EXPECT_SUCCEEDED(RegKey::SetValue(
-        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-        _T("ImageState"),
-        _T("IMAGE_STATE_UNDEPLOYABLE")));
-  } else {
-    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
-                                      _T("AuditInProgress"),
-                                      static_cast<DWORD>(1)));
-  }
-  EXPECT_TRUE(cm_->IsWindowsInstalling());
-
-  EXPECT_TRUE(cm_->IsOemInstalling(true));
-}
-
-TEST_F(ConfigManagerTest,
-       IsOemInstalling_Machine_OemInstallTime73HoursInFuture_NotAuditMode) {
-  const DWORD kDesiredDifferenceSeconds = 73 * 60 * 60;  // 73 hours.
-  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  const DWORD install_time_seconds = now_seconds + kDesiredDifferenceSeconds;
-  EXPECT_GT(install_time_seconds, now_seconds);
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    install_time_seconds));
-
-  EXPECT_FALSE(cm_->IsOemInstalling(true));
-}
-
-TEST_F(ConfigManagerTest,
-       IsOemInstalling_Machine_OemInstallTime73HoursInFuture_AuditMode) {
-  const DWORD kDesiredDifferenceSeconds = 73 * 60 * 60;  // 73 hours.
-  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  const DWORD install_time_seconds = now_seconds + kDesiredDifferenceSeconds;
-  EXPECT_GT(install_time_seconds, now_seconds);
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    install_time_seconds));
-
-  if (vista_util::IsVistaOrLater()) {
-    EXPECT_SUCCEEDED(RegKey::SetValue(
-        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-        _T("ImageState"),
-        _T("IMAGE_STATE_UNDEPLOYABLE")));
-  } else {
-    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
-                                      _T("AuditInProgress"),
-                                      static_cast<DWORD>(1)));
-  }
-  EXPECT_TRUE(cm_->IsWindowsInstalling());
-
-  EXPECT_FALSE(cm_->IsOemInstalling(true));
-}
-
-TEST_F(ConfigManagerTest,
-       IsOemInstalling_Machine_OemInstallTimeZero_NotAuditMode) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    static_cast<DWORD>(0)));
-
-  EXPECT_FALSE(cm_->IsOemInstalling(true));
-}
-
-TEST_F(ConfigManagerTest,
-       IsOemInstalling_Machine_OemInstallTimeZero_AuditMode) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    static_cast<DWORD>(0)));
-
-  if (vista_util::IsVistaOrLater()) {
-    EXPECT_SUCCEEDED(RegKey::SetValue(
-        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-        _T("ImageState"),
-        _T("IMAGE_STATE_UNDEPLOYABLE")));
-  } else {
-    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
-                                      _T("AuditInProgress"),
-                                      static_cast<DWORD>(1)));
-  }
-  EXPECT_TRUE(cm_->IsWindowsInstalling());
-
-  EXPECT_FALSE(cm_->IsOemInstalling(true));
-}
-
-TEST_F(ConfigManagerTest,
-       IsOemInstalling_Machine_OemInstallTimeWrongType_NotAuditMode) {
-  const uint32 now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  const CString now_string = itostr(now_seconds);
-  EXPECT_FALSE(now_string.IsEmpty());
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    now_string));
-
-  EXPECT_FALSE(cm_->IsOemInstalling(true));
-}
-
-TEST_F(ConfigManagerTest,
-       IsOemInstalling_Machine_OemInstallTimeWrongType_AuditMode) {
-  const uint32 now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  const CString now_string = itostr(now_seconds);
-  EXPECT_FALSE(now_string.IsEmpty());
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    now_string));
-
-  if (vista_util::IsVistaOrLater()) {
-    EXPECT_SUCCEEDED(RegKey::SetValue(
-        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-        _T("ImageState"),
-        _T("IMAGE_STATE_UNDEPLOYABLE")));
-  } else {
-    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
-                                      _T("AuditInProgress"),
-                                      static_cast<DWORD>(1)));
-  }
-  EXPECT_TRUE(cm_->IsWindowsInstalling());
-
-  EXPECT_FALSE(cm_->IsOemInstalling(true));
-}
-
-TEST_F(ConfigManagerTest, IsOemInstalling_Machine_NoOemInstallTime_AuditMode) {
-  if (vista_util::IsVistaOrLater()) {
-    EXPECT_SUCCEEDED(RegKey::SetValue(
-        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-        _T("ImageState"),
-        _T("IMAGE_STATE_UNDEPLOYABLE")));
-  } else {
-    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
-                                      _T("AuditInProgress"),
-                                      static_cast<DWORD>(1)));
-  }
-  EXPECT_TRUE(cm_->IsWindowsInstalling());
-
-  EXPECT_FALSE(cm_->IsOemInstalling(true));
-}
-
-TEST_F(ConfigManagerTest,
-       IsOemInstalling_User_OemInstallTimeNow_NotAuditMode) {
-  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    now_seconds));
-
-  EXPECT_FALSE(cm_->IsOemInstalling(false));
-}
-
-TEST_F(ConfigManagerTest, IsOemInstalling_User_OemInstallTimeNow_AuditMode) {
-  const DWORD now_seconds = Time64ToInt32(GetCurrent100NSTime());
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    now_seconds));
-
-  if (vista_util::IsVistaOrLater()) {
-    EXPECT_SUCCEEDED(RegKey::SetValue(
-        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-        _T("ImageState"),
-        _T("IMAGE_STATE_UNDEPLOYABLE")));
-  } else {
-    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
-                                      _T("AuditInProgress"),
-                                      static_cast<DWORD>(1)));
-  }
-  EXPECT_TRUE(cm_->IsWindowsInstalling());
-
-  EXPECT_FALSE(cm_->IsOemInstalling(false));
-}
-
-// TODO(omaha): Figure out a way to test the result.
-TEST_F(ConfigManagerTest, IsGoogler) {
-  cm_->IsGoogler();
-}
-
-TEST_F(ConfigManagerTest, IsWindowsInstalling_Normal) {
-  EXPECT_FALSE(cm_->IsWindowsInstalling());
-}
-
-// While this test passes, the return value of IsWindowsInstalling() is not
-// fully tested because the account is not Administrator.
-TEST_F(ConfigManagerTest, IsWindowsInstalling_Installing_Vista_InvalidValues) {
-  if (!vista_util::IsVistaOrLater()) {
-    return;
-  }
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      _T("")));
-  EXPECT_FALSE(cm_->IsWindowsInstalling());
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      _T("foo")));
-  EXPECT_FALSE(cm_->IsWindowsInstalling());
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      static_cast<DWORD>(1)));
-  ExpectAsserts expect_asserts;  // RegKey asserts because value type is wrong.
-  EXPECT_FALSE(cm_->IsWindowsInstalling());
-}
-
-// TODO(omaha): This test fails because the account is not Administrator. Maybe
-// just delete them if this is the final implementation of Audit Mode detection.
-TEST_F(ConfigManagerTest,
-       DISABLED_IsWindowsInstalling_Installing_Vista_ValidStates) {
-  if (!vista_util::IsVistaOrLater()) {
-    return;
-  }
-
-  // These states return false in the original implementation.
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      _T("IMAGE_STATE_COMPLETE")));
-  EXPECT_FALSE(cm_->IsWindowsInstalling());
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      _T("IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE")));
-  EXPECT_FALSE(cm_->IsWindowsInstalling());
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      _T("IMAGE_STATE_SPECIALIZE_RESEAL_TO_OOBE")));
-  EXPECT_FALSE(cm_->IsWindowsInstalling());
-
-  // These states are specified in the original implementation.
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      _T("IMAGE_STATE_UNDEPLOYABLE")));
-  EXPECT_TRUE(cm_->IsWindowsInstalling());
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      _T("IMAGE_STATE_GENERALIZE_RESEAL_TO_AUDIT")));
-  EXPECT_TRUE(cm_->IsWindowsInstalling());
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(
-      _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-      _T("ImageState"),
-      _T("IMAGE_STATE_SPECIALIZE_RESEAL_TO_AUDIT")));
-  EXPECT_TRUE(cm_->IsWindowsInstalling());
-}
-
-TEST_F(ConfigManagerTest, CanInstallApp_NoGroupPolicy) {
-  EXPECT_TRUE(CanInstallApp(kAppGuid1));
-}
-
-TEST_F(ConfigManagerTest, CanInstallApp_DifferentAppDisabled) {
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp2, 0));
-  EXPECT_TRUE(CanInstallApp(kAppGuid1));
-}
-
-TEST_F(ConfigManagerTest, CanInstallApp_NoDefaultValue_AppDisabled) {
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 0));
-  EXPECT_FALSE(CanInstallApp(kAppGuid1));
-}
-
-TEST_F(ConfigManagerTest, CanInstallApp_NoDefaultValue_AppEnabled) {
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 1));
-  EXPECT_TRUE(CanInstallApp(kAppGuid1));
-}
-
-TEST_F(ConfigManagerTest, CanInstallApp_NoDefaultValue_AppInvalid) {
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 2));
-  EXPECT_TRUE(CanInstallApp(kAppGuid1));
-}
-
-TEST_F(ConfigManagerTest, CanInstallApp_DefaultDisabled_NoAppValue) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 0));
-  EXPECT_FALSE(CanInstallApp(kAppGuid1));
-}
-
-TEST_F(ConfigManagerTest, CanInstallApp_DefaultDisabled_AppDisabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 0));
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 0));
-  EXPECT_FALSE(CanInstallApp(kAppGuid1));
-}
-
-TEST_F(ConfigManagerTest, CanInstallApp_DefaultDisabled_AppEnabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 0));
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 1));
-  EXPECT_TRUE(CanInstallApp(kAppGuid1));
-}
-
-// Invalid value defaulting to true overrides the InstallDefault disable.
-TEST_F(ConfigManagerTest, CanInstallApp_DefaultDisabled_AppInvalid) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 0));
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 2));
-  EXPECT_TRUE(CanInstallApp(kAppGuid1));
-}
-
-TEST_F(ConfigManagerTest, CanInstallApp_DefaultEnabled_NoAppValue) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 1));
-  EXPECT_TRUE(CanInstallApp(kAppGuid1));
-}
-
-TEST_F(ConfigManagerTest, CanInstallApp_DefaultEnabled_AppDisabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 1));
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 0));
-  EXPECT_FALSE(CanInstallApp(kAppGuid1));
-}
-
-TEST_F(ConfigManagerTest, CanInstallApp_DefaultEnabled_AppEnabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 1));
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 1));
-  EXPECT_TRUE(CanInstallApp(kAppGuid1));
-}
-
-TEST_F(ConfigManagerTest, CanInstallApp_DefaultEnabled_AppInvalid) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("InstallDefault"), 1));
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp1, 2));
-  EXPECT_TRUE(CanInstallApp(kAppGuid1));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_NoGroupPolicy) {
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DifferentAppDisabled) {
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp2, 0));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DifferentAppManualOnly) {
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp2, 2));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_NoDefaultValue_AppDisabled) {
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 0));
-  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_NoDefaultValue_AppEnabled) {
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 1));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_NoDefaultValue_AppManualOnly) {
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 2));
-  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultDisabled_NoAppValue) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
-  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultDisabled_AppDisabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 0));
-  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultDisabled_AppEnabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 1));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultDisabled_AppManualOnly) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 2));
-  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
-}
-
-// Invalid value defaulting to true overrides the UpdateDefault disable.
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultDisabled_AppInvalid) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 3));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultEnabled_NoAppValue) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultEnabled_AppDisabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 0));
-  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultEnabled_AppEnabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 1));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultEnabled_AppManualOnly) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 2));
-  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultEnabled_AppInvalid) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 3));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultManualOnly_NoAppValue) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
-  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultManualOnly_AppDisabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 0));
-  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultManualOnly_AppEnabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 1));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultManualOnly_AppManualOnly) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 2));
-  EXPECT_FALSE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultManualOnly_AppInvalid) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 3));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_DefaultInvalid_NoAppValue) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 3));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_GoogleUpdate_DefaultDisabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
-  EXPECT_TRUE(CanUpdateApp(_T("{430FD4D0-B729-4F61-AA34-91526481799D}"),
-                           false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_GoogleUpdate_DefaultManualOnly) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
-  EXPECT_TRUE(CanUpdateApp(_T("{430FD4D0-B729-4F61-AA34-91526481799D}"),
-                           false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Auto_GoogleUpdate_AppDisabled) {
-  EXPECT_SUCCEEDED(
-      SetPolicy(_T("Update{430FD4D0-B729-4F61-AA34-91526481799D}"), 0));
-  EXPECT_TRUE(CanUpdateApp(_T("{430FD4D0-B729-4F61-AA34-91526481799D}"),
-                           false));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_NoGroupPolicy) {
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DifferentAppDisabled) {
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp2, 0));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DifferentAppManualOnly) {
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp2, 2));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_NoDefaultValue_AppDisabled) {
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 0));
-  EXPECT_FALSE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_NoDefaultValue_AppEnabled) {
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 1));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_NoDefaultValue_AppManualOnly) {
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 2));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultDisabled_NoAppValue) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
-  EXPECT_FALSE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultDisabled_AppDisabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 0));
-  EXPECT_FALSE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultDisabled_AppEnabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 1));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultDisabled_AppManualOnly) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 2));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
-}
-
-// Invalid value defaulting to true overrides the UpdateDefault disable.
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultDisabled_AppInvalid) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 3));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultEnabled_NoAppValue) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultEnabled_AppDisabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 0));
-  EXPECT_FALSE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultEnabled_AppEnabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 1));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultEnabled_AppManualOnly) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 2));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultEnabled_AppInvalid) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 1));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 3));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultManualOnly_NoAppValue) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultManualOnly_AppDisabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 0));
-  EXPECT_FALSE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultManualOnly_AppEnabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 1));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultManualOnly_AppManualOnly) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 2));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Maual_DefaultManualOnly_AppInvalid) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp1, 3));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_DefaultInvalid_NoAppValue) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 3));
-  EXPECT_TRUE(CanUpdateApp(kAppGuid1, true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_GoogleUpdate_DefaultDisabled) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 0));
-  EXPECT_TRUE(CanUpdateApp(_T("{430FD4D0-B729-4F61-AA34-91526481799D}"), true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_GoogleUpdate_DefaultManualOnly) {
-  EXPECT_SUCCEEDED(SetPolicy(_T("UpdateDefault"), 2));
-  EXPECT_TRUE(CanUpdateApp(_T("{430FD4D0-B729-4F61-AA34-91526481799D}"), true));
-}
-
-TEST_F(ConfigManagerTest, CanUpdateApp_Manual_GoogleUpdate_AppDisabled) {
-  EXPECT_SUCCEEDED(
-      SetPolicy(_T("Update{430FD4D0-B729-4F61-AA34-91526481799D}"), 0));
-  EXPECT_TRUE(CanUpdateApp(_T("{430FD4D0-B729-4F61-AA34-91526481799D}"), true));
-}
-
-TEST_F(ConfigManagerTest, LastCheckedTime) {
-  DWORD time = 500;
-  EXPECT_SUCCEEDED(cm_->SetLastCheckedTime(true, time));
-  EXPECT_EQ(time, cm_->GetLastCheckedTime(true));
-
-  time = 77003;
-  EXPECT_SUCCEEDED(cm_->SetLastCheckedTime(false, time));
-  EXPECT_EQ(time, cm_->GetLastCheckedTime(false));
-}
-
-// Tests GetDir indirectly.
-TEST_F(ConfigManagerTest, GetDir) {
-  RestoreRegistryHives();
-
-  CString user_install_dir = cm_->GetUserGoopdateInstallDir();
-  CString user_profile;
-  ASSERT_NE(0, ::GetEnvironmentVariable(_T("USERPROFILE"),
-                                        CStrBuf(user_profile, MAX_PATH),
-                                        MAX_PATH));
-  ASSERT_TRUE(String_StartsWith(user_install_dir, user_profile, true));
-}
-
-TEST_F(ConfigManagerTest, GetUpdateWorkerStartUpDelayMs_Repeated) {
-  if (!SystemInfo::IsRunningOnXPOrLater()) {
-    std::wcout << _T("\tTest did not run because GenRandom breaks on Windows ")
-               << _T("2000 if the registry keys are overridden.") << std::endl;
-    return;
-  }
-
-  // Test the UpdateDelay multiple times.
-  for (int i = 0; i < 10; ++i) {
-    int random = cm_->GetUpdateWorkerStartUpDelayMs();
-    EXPECT_GE(random, kUpdateTimerStartupDelayMinMs);
-    EXPECT_LE(random, kUpdateTimerStartupDelayMaxMs);
-  }
-}
-
-TEST_F(ConfigManagerTest, GetUpdateWorkerStartUpDelayMs) {
-  if (!SystemInfo::IsRunningOnXPOrLater()) {
-    std::wcout << _T("\tTest did not run because GenRandom breaks on Windows ")
-               << _T("2000 if the registry keys are overridden.") << std::endl;
-    return;
-  }
-
-  int random = cm_->GetUpdateWorkerStartUpDelayMs();
-  EXPECT_GE(random, kUpdateTimerStartupDelayMinMs);
-  EXPECT_LE(random, kUpdateTimerStartupDelayMaxMs);
-
-  int num_times_to_try_for_diff_number = 3;
-  // We run the method num_times_to_try_for_diff_number times to make
-  // sure that at least one of these returns a number that is different
-  // from the one that is returned above. This is needed, since the
-  // method returns a number between kUpdateTimerStartupDelayMinMs and
-  // kUpdateTimerStartupDelayMaxMs.
-  // If this fails a lot we should disable the if check below.
-  bool found_one_not_equal = false;
-  for (int i = 0; i < num_times_to_try_for_diff_number; ++i) {
-    int random_compare = cm_->GetUpdateWorkerStartUpDelayMs();
-
-    EXPECT_GE(random_compare, kUpdateTimerStartupDelayMinMs);
-    EXPECT_LE(random_compare, kUpdateTimerStartupDelayMaxMs);
-
-    if (random_compare != random) {
-      found_one_not_equal = true;
-      break;
-    }
-  }
-
-  EXPECT_TRUE(found_one_not_equal);
-}
-
-TEST_F(ConfigManagerTest, GetUpdateWorkerStartUpDelayMs_Override) {
-  // Test that the initial delay time to launch a worker can be overriden.
-  DWORD val = 3320;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueAuCheckPeriodMs,
-                                    val));
-
-  int random = cm_->GetUpdateWorkerStartUpDelayMs();
-  EXPECT_EQ(val, random);
-}
-
-TEST_F(ConfigManagerTest, GetTimeSinceLastCheckedSec_User) {
-  // First, there is no value present in the registry.
-  uint32 now_sec = Time64ToInt32(GetCurrent100NSTime());
-  int time_since_last_checked_sec = cm_->GetTimeSinceLastCheckedSec(false);
-  EXPECT_EQ(now_sec, time_since_last_checked_sec);
-
-  // Second, write the 'now' time.
-  EXPECT_HRESULT_SUCCEEDED(cm_->SetLastCheckedTime(false, now_sec));
-  time_since_last_checked_sec = cm_->GetTimeSinceLastCheckedSec(false);
-  EXPECT_EQ(0, time_since_last_checked_sec);
-}
-
-TEST_F(ConfigManagerTest, GetTimeSinceLastCheckedSec_Machine) {
-  uint32 now_sec = Time64ToInt32(GetCurrent100NSTime());
-  int time_since_last_checked_sec = cm_->GetTimeSinceLastCheckedSec(true);
-  EXPECT_EQ(now_sec, time_since_last_checked_sec);
-
-  EXPECT_HRESULT_SUCCEEDED(cm_->SetLastCheckedTime(true, now_sec));
-  time_since_last_checked_sec = cm_->GetTimeSinceLastCheckedSec(true);
-  EXPECT_EQ(0, time_since_last_checked_sec);
-}
-
-TEST_F(ConfigManagerTest, GetNetConfig) {
-  CString actual_value;
-  EXPECT_HRESULT_FAILED(cm_->GetNetConfig(&actual_value));
-
-  const CString expected_value = _T("proxy:8080");
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueNetConfig,
-                                    expected_value));
-
-  EXPECT_HRESULT_SUCCEEDED(cm_->GetNetConfig(&actual_value));
-  EXPECT_STREQ(expected_value, actual_value);
-}
-
-TEST_F(ConfigManagerTest, GetInstallTime) {
-  EXPECT_SUCCEEDED(DeleteUpdateTime(false));
-  EXPECT_SUCCEEDED(DeleteFirstInstallTime(false));
-  EXPECT_EQ(0, ConfigManager::GetInstallTime(false));
-
-  DWORD time = 500;
-  EXPECT_SUCCEEDED(SetFirstInstallTime(false, time));
-  EXPECT_EQ(time, ConfigManager::GetInstallTime(false));
-
-  time = 1000;
-  EXPECT_SUCCEEDED(SetUpdateTime(false, time));
-  EXPECT_EQ(time, ConfigManager::GetInstallTime(false));
-
-  EXPECT_SUCCEEDED(DeleteFirstInstallTime(false));
-  EXPECT_EQ(time, ConfigManager::GetInstallTime(false));
-}
-
-TEST_F(ConfigManagerTest, Is24HoursSinceInstall) {
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-  const int k12HourPeriodSec = 12 * 60 * 60;
-  const int k48HourPeriodSec = 48 * 60 * 60;
-
-  const uint32 first_install_12 = now - k12HourPeriodSec;
-  const uint32 first_install_48 = now - k48HourPeriodSec;
-
-  EXPECT_SUCCEEDED(SetFirstInstallTime(false, first_install_12));
-  EXPECT_FALSE(ConfigManager::Is24HoursSinceInstall(false));
-
-  EXPECT_SUCCEEDED(SetFirstInstallTime(false, first_install_48));
-  EXPECT_TRUE(ConfigManager::Is24HoursSinceInstall(false));
-
-  EXPECT_SUCCEEDED(SetUpdateTime(false, first_install_12));
-  EXPECT_FALSE(ConfigManager::Is24HoursSinceInstall(false));
-
-  EXPECT_SUCCEEDED(SetUpdateTime(false, first_install_48));
-  EXPECT_TRUE(ConfigManager::Is24HoursSinceInstall(false));
-}
-
-}  // namespace omaha
-
diff --git a/goopdate/const_goopdate.h b/goopdate/const_goopdate.h
deleted file mode 100644
index 0ce2125..0000000
--- a/goopdate/const_goopdate.h
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Goopdate constants - Reduces dependencies on goopdate.h for names that
-// are needed outside Goopdate.
-//
-// TODO(omaha): it appears that the string constants below are not
-// optimized out the program image even if not used. Figure out why they still
-// show up in the tiny shell in optimized builds.
-
-#ifndef OMAHA_GOOPDATE_CONST_GOOPDATE_H__
-#define OMAHA_GOOPDATE_CONST_GOOPDATE_H__
-
-#include <tchar.h>
-
-namespace omaha {
-
-// The enumeration of the events. Some of the events are used in IPC,
-// these are named events, the rest are not named. Of the named events there
-// are two types the global and the local events.
-// Global Events:
-// The events which are global are indicated with the Global suffix in the
-// enum. These events have the Global prefix attached to the event name and
-// are used for IPC across terminal server sessions, these events are used
-// as barriers, i.e. all waiting threads are release on these events.
-// Local Events:
-// The local counter parts of these events have the Local prefix attached
-// to their names, and dont have any suffix in the enum names. The local events
-// are used to release only one thread, this works as we know that there is
-// only one user goopdate process in a user session and one machine goopdate.
-// The local events also have the user sid added to the event name. This is to
-// work around a bug in win2K and on XP where in case of TS, the event names
-// will collide, inspite of the name changes.
-enum GoopdateEvents {
-  EVENT_INVALID = -1,
-  EVENT_KILL_MESSAGE_LOOP = 0,
-  EVENT_UPDATE_TIMER,
-  EVENT_NEW_MANIFEST,          // Used in IPC.
-  EVENT_QUIET_MODE,            // Used in IPC.
-  EVENT_LEGACY_QUIET_MODE,     // Only used to shut down pre-i18n goopdates.
-  EVENT_CODE_RED_TIMER,
-};
-
-// Using extern or intern linkage for these strings yields the same code size
-// for the executable DLL.
-
-
-// Registry values read from the Clients key for transmitting custom install
-// errors, messages, etc. On an update, the InstallerXXX values are renamed to
-// LastInstallerXXX values. The LastInstallerXXX values remain around until the
-// next update.
-const TCHAR* const kRegValueInstallerResult = _T("InstallerResult");
-const TCHAR* const kRegValueInstallerError  = _T("InstallerError");
-const TCHAR* const kRegValueInstallerResultUIString =
-    _T("InstallerResultUIString");
-const TCHAR* const kRegValueInstallerSuccessLaunchCmdLine =
-    _T("InstallerSuccessLaunchCmdLine");
-const TCHAR* const kRegValueLastInstallerResult =
-    _T("LastInstallerResult");
-const TCHAR* const kRegValueLastInstallerError =
-    _T("LastInstallerError");
-const TCHAR* const kRegValueLastInstallerResultUIString =
-    _T("LastInstallerResultUIString");
-const TCHAR* const kRegValueLastInstallerSuccessLaunchCmdLine =
-    _T("LastInstallerSuccessLaunchCmdLine");
-
-// Registry subkey in an app's Clients key that contains its components.
-const TCHAR* const kComponentsRegKeyName     = _T("Components");
-
-// Registry values read from the Clients key and stored in the ClientState key.
-const TCHAR* const kRegValueLanguage         = _T("lang");
-const TCHAR* const kRegValueAppName          = _T("name");
-const TCHAR* const kRegValueProductVersion   = _T("pv");
-
-// Registry values stored in the ClientState key.
-const TCHAR* const kRegValueAdditionalParams = _T("ap");
-const TCHAR* const kRegValueBrandCode        = _T("brand");
-const TCHAR* const kRegValueBrowser          = _T("browser");
-const TCHAR* const kRegValueClientId         = _T("client");
-const TCHAR* const kRegValueDidRun           = _T("dr");
-const TCHAR* const kRegValueInstallationId   = _T("iid");
-const TCHAR* const kRegValueOemInstall       = _T("oeminstall");
-const TCHAR* const kRegValueReferralId       = _T("referral");
-
-// This two registries hold client UTC timestamp of server's midnight of the day
-// that last active ping/roll call happened.
-const TCHAR* const kRegValueActivePingDayStartSec = _T("ActivePingDayStartSec");
-const TCHAR* const kRegValueRollCallDayStartSec   = _T("RollCallDayStartSec");
-
-// Registry values stored in the ClientState key related to Omaha's actions.
-// A "successful check" means "noupdate" received from the server or an update
-// was successfully applied.
-const TCHAR* const kRegValueInstallTimeSec          = _T("InstallTime");
-const TCHAR* const kRegValueLastSuccessfulCheckSec  = _T("LastCheckSuccess");
-const TCHAR* const kRegValueLastUpdateTimeSec       = _T("UpdateTime");
-
-// Registry values stored in the ClientState or ClientStateMedium keys.
-// Use accessor methods rather than reading them directly.
-const TCHAR* const kRegValueEulaAccepted     = _T("eulaaccepted");
-const TCHAR* const kRegValueUsageStats       = _T("usagestats");
-
-// Registry values stored in the ClientState key for Omaha's internal use.
-const TCHAR* const kRegValueTTToken               = _T("tttoken");
-const TCHAR* const kRegValueUpdateAvailableCount  = _T("UpdateAvailableCount");
-const TCHAR* const kRegValueUpdateAvailableSince  = _T("UpdateAvailableSince");
-
-// Registry values stored in the Update key.
-const TCHAR* const kRegValueOmahaEulaAccepted     = _T("eulaaccepted");
-const TCHAR* const kRegValueServiceName           = _T("gupdate_service_name");
-const TCHAR* const kRegValueTaskNameC             = _T("gupdate_task_name_c");
-const TCHAR* const kRegValueTaskNameUA            = _T("gupdate_task_name_ua");
-const TCHAR* const kRegValueLastChecked           = _T("LastChecked");
-const TCHAR* const kRegValueOemInstallTimeSec     = _T("OemInstallTime");
-const TCHAR* const kRegValueInstalledPath         = _T("path");
-const TCHAR* const kRegValueSelfUpdateExtraCode1  = _T("UpdateCode1");
-const TCHAR* const kRegValueSelfUpdateErrorCode   = _T("UpdateError");
-const TCHAR* const kRegValueSelfUpdateVersion     = _T("UpdateVersion");
-const TCHAR* const kRegValueInstalledVersion      = _T("version");
-
-// TODO(omaha): Remove with legacy Omaha 1 support.
-// Used to allow current builds to read opt-in values from previous builds.
-// This is deprecated in favor of kRegValueUsageStats.
-const TCHAR* const kLegacyRegValueCollectUsageStats = _T("CollectUsageStats");
-
-const TCHAR* const kLegacyServiceName           = _T("gupdate");
-const TCHAR* const kServicePrefix               = _T("gupdate");
-
-const TCHAR* const kScheduledTaskNameUserPrefix = _T("GoogleUpdateTaskUser");
-const TCHAR* const kScheduledTaskNameMachinePrefix =
-    _T("GoogleUpdateTaskMachine");
-const TCHAR* const kScheduledTaskNameCoreSuffix = _T("Core");
-const TCHAR* const kScheduledTaskNameUASuffix   = _T("UA");
-
-// TODO(omaha): make these two below the same symbol.
-const TCHAR* const kServiceFileName              = _T("GoogleUpdate.exe");
-const TCHAR* const kGoopdateFileName             = _T("GoogleUpdate.exe");
-const TCHAR* const kGoopdateCrashHandlerFileName = _T("GoogleCrashHandler.exe");
-const TCHAR* const kGoopdateDllName              = _T("goopdate.dll");
-const TCHAR* const kGoopdateResourceDllName      = _T("goopdateres_%s.dll");
-const TCHAR* const kGoopdateResDllFindPattern    = _T("goopdateres_*.dll");
-const char*  const kGoopdateDllEntryAnsi         = "DllEntry";
-
-
-// Event Id's used for reporting in the event log.
-// Crash Report events.
-const int kCrashReportEventId        = 1;
-const int kCrashUploadEventId        = 2;
-
-// Update Check events.
-const int kUpdateCheckEventId        = 11;
-const int kUpdateEventId             = 12;
-const int kUninstallEventId          = 13;
-const int kWorkerStartEventId        = 14;
-
-// Network Request events.
-const int kNetworkRequestEventId     = 20;
-
-// Maximum value the server can respond for elapsed_seconds attribute in
-// <daystart ...> element. The value is one day plus an hour ("fall back"
-// daylight savings).
-const int kMaxTimeSinceMidnightSec   = ((24 + 1) * 60 * 60);
-
-// Maximum time to keep the Installation ID. If the app was installed longer
-// than this time ago, the Installation ID will be deleted regardless of
-// whether the application has been run or not.
-const int kMaxLifeOfInstallationIDSec = (7 * 24 * 60 * 60);  // 7 days
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_CONST_GOOPDATE_H__
diff --git a/goopdate/crash.cc b/goopdate/crash.cc
index 84b8806..b799046 100644
--- a/goopdate/crash.cc
+++ b/goopdate/crash.cc
@@ -30,29 +30,31 @@
 #include <string>
 #include <vector>
 #include "base/basictypes.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/exception_barrier.h"
-#include "omaha/common/file.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/module_utils.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/path.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/time.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/command_line_builder.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/event_logger.h"
-#include "omaha/goopdate/goopdate_utils.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/exception_barrier.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/module_utils.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/time.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/command_line_builder.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/event_logger.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/stats_uploader.h"
 #include "omaha/goopdate/goopdate_metrics.h"
-#include "omaha/goopdate/stats_uploader.h"
 #include "third_party/breakpad/src/client/windows/common/ipc_protocol.h"
 #include "third_party/breakpad/src/client/windows/crash_generation/client_info.h"
 #include "third_party/breakpad/src/client/windows/crash_generation/crash_generation_server.h"
@@ -67,8 +69,6 @@
 
 namespace omaha {
 
-const TCHAR kPipeNamePrefix[] = _T("\\\\.\\pipe\\GoogleCrashServices");
-
 const ACCESS_MASK kPipeAccessMask = FILE_READ_ATTRIBUTES  |
                                     FILE_READ_DATA        |
                                     FILE_WRITE_ATTRIBUTES |
@@ -85,7 +85,8 @@
 CrashGenerationServer* Crash::crash_server_ = NULL;
 
 bool Crash::is_machine_ = false;
-const TCHAR* const Crash::kDefaultProductName = _T("Google Error Reporting");
+const TCHAR* const Crash::kDefaultProductName =
+    SHORT_COMPANY_NAME _T(" Error Reporting");
 
 HRESULT Crash::Initialize(bool is_machine) {
   is_machine_ = is_machine;
@@ -136,11 +137,17 @@
   if (exception_handler_) {
     delete exception_handler_;
   }
+
+  MINIDUMP_TYPE dump_type = ConfigManager::Instance()->IsInternalUser() ?
+                                MiniDumpWithFullMemory : MiniDumpNormal;
   exception_handler_ = new ExceptionHandler(crash_dir_.GetString(),
                                             NULL,
                                             &Crash::MinidumpCallback,
                                             NULL,
-                                            ExceptionHandler::HANDLER_ALL);
+                                            ExceptionHandler::HANDLER_ALL,
+                                            dump_type,
+                                            NULL,
+                                            NULL);
 
   // Breakpad does not get the exceptions that are not propagated to the
   // UnhandledExceptionFilter. This is the case where we crashed on a stack
@@ -199,6 +206,7 @@
   builder.set_custom_info_filename(custom_info_filename);
   builder.set_is_machine_set(is_machine);
   CString cmd_line = builder.GetCommandLine(module_filename_);
+
   hr = StartSenderWithCommandLine(&cmd_line);
   if (FAILED(hr)) {
     OPT_LOG(LE, (_T("[StartSenderWithCommandLine failed][0x%08x]"), hr));
@@ -227,38 +235,23 @@
 
     // TODO(omaha): format a command line without extra memory allocations.
     CString crash_filename;
-    crash_filename.Format(_T("%s\\%s.dmp"), dump_path, minidump_id);
+    SafeCStringFormat(&crash_filename, _T("%s\\%s.dmp"),
+                      dump_path, minidump_id);
     EnclosePath(&crash_filename);
+    StartReportCrash(is_interactive, crash_filename);
 
-    // CommandLineBuilder escapes the program name before returning the
-    // command line to the caller.
-    CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
-    builder.set_is_interactive_set(is_interactive);
-    builder.set_crash_filename(crash_filename);
-    builder.set_is_machine_set(is_machine_);
-    CString cmd_line = builder.GetCommandLine(module_filename_);
-
-    // Set an environment variable which the crash reporter process will
-    // inherit. We don't want to install a crash handler for the reporter
-    // process to avoid an infinite loop in the case the reporting process
-    // crashes also. When the reporting process begins execution, the presence
-    // of this environment variable is tested, and the crash handler will not be
-    // installed.
-    if (::SetEnvironmentVariable(kNoCrashHandlerEnvVariableName, (_T("1")))) {
-      STARTUPINFO si = {sizeof(si)};
-      PROCESS_INFORMATION pi = {0};
-      if (::CreateProcess(NULL,
-                          cmd_line.GetBuffer(),
-                          NULL,
-                          NULL,
-                          false,
-                          0,
-                          NULL,
-                          NULL,
-                          &si,
-                          &pi)) {
-        ::CloseHandle(pi.hProcess);
-        ::CloseHandle(pi.hThread);
+    // For in-proc crash generation, ExceptionHandler either creates a Normal
+    // MiniDump, or a Full MiniDump, based on the dump_type. However, in the
+    // case of OOP crash generation both the Normal and Full dumps are created
+    // by the crash handling server, with the default full dump filename having
+    // a suffix of "-full.dmp". If Omaha switches to using OOP crash generation,
+    // this file is uploaded as well.
+    if (ConfigManager::Instance()->IsInternalUser()) {
+      SafeCStringFormat(&crash_filename, _T("%s\\%s-full.dmp"),
+                        dump_path, minidump_id);
+      EnclosePath(&crash_filename);
+      if (File::Exists(crash_filename)) {
+        StartReportCrash(is_interactive, crash_filename);
       }
     }
   }
@@ -273,6 +266,41 @@
   return true;
 }
 
+void Crash::StartReportCrash(bool is_interactive,
+                             const CString& crash_filename) {
+  // CommandLineBuilder escapes the program name before returning the
+  // command line to the caller.
+  CommandLineBuilder builder(COMMANDLINE_MODE_REPORTCRASH);
+  builder.set_is_interactive_set(is_interactive);
+  builder.set_crash_filename(crash_filename);
+  builder.set_is_machine_set(is_machine_);
+  CString cmd_line = builder.GetCommandLine(module_filename_);
+
+  // Set an environment variable which the crash reporter process will
+  // inherit. We don't want to install a crash handler for the reporter
+  // process to avoid an infinite loop in the case the reporting process
+  // crashes also. When the reporting process begins execution, the presence
+  // of this environment variable is tested, and the crash handler will not be
+  // installed.
+  if (::SetEnvironmentVariable(kNoCrashHandlerEnvVariableName, (_T("1")))) {
+    STARTUPINFO si = {sizeof(si)};
+    PROCESS_INFORMATION pi = {0};
+    if (::CreateProcess(NULL,
+                        cmd_line.GetBuffer(),
+                        NULL,
+                        NULL,
+                        false,
+                        0,
+                        NULL,
+                        NULL,
+                        &si,
+                        &pi)) {
+      ::CloseHandle(pi.hProcess);
+      ::CloseHandle(pi.hThread);
+    }
+  }
+}
+
 HRESULT Crash::StartSenderWithCommandLine(CString* cmd_line) {
   TCHAR* env_vars = ::GetEnvironmentStrings();
   if (env_vars == NULL) {
@@ -346,11 +374,11 @@
 
   // Determine the path for custom info file.
   CString filepath = GetPathRemoveExtension(dump_file);
-  filepath.AppendFormat(_T(".txt"));
+  SafeCStringAppendFormat(&filepath, _T(".txt"));
 
   // Create a map of name/value pairs from custom client info.
   std::map<CString, CString> custom_info_map;
-  for (int i = 0; i < custom_client_info.count; ++i) {
+  for (size_t i = 0; i < custom_client_info.count; ++i) {
     custom_info_map[custom_client_info.entries[i].name] =
                     custom_client_info.entries[i].value;
   }
@@ -386,6 +414,8 @@
   HRESULT hr = S_OK;
   if (can_upload) {
     hr = UploadCrash(is_out_of_process, crash_filename, parameters, report_id);
+  } else {
+    CORE_LOG(L2, (_T("[crash uploads are not allowed]")));
   }
 
   return hr;
@@ -449,6 +479,8 @@
     }
   }
 
+  CORE_LOG(L2, (_T("[crash report code = %s]"), *report_id));
+
   // The event source for the out-of-process crashes is the product name.
   // Therefore, in the event of an out-of-process crash, the log entry
   // appears to be generated by the product that crashed.
@@ -458,11 +490,11 @@
   uint16 event_type(0);
   if (!report_id->IsEmpty()) {
     event_type = EVENTLOG_INFORMATION_TYPE;
-    event_text.Format(_T("Crash uploaded. Id=%s."), *report_id);
+    SafeCStringFormat(&event_text, _T("Crash uploaded. Id=%s."), *report_id);
   } else {
     ASSERT1(FAILED(hr));
     event_type = EVENTLOG_WARNING_TYPE;
-    event_text.Format(_T("Crash not uploaded. Error=0x%x."), hr);
+    SafeCStringFormat(&event_text, _T("Crash not uploaded. Error=0x%x."), hr);
   }
   VERIFY1(SUCCEEDED(Crash::Log(event_type,
                                kCrashUploadEventId,
@@ -480,7 +512,7 @@
     return E_INVALIDARG;
   }
   CString tmp;
-  tmp.Format(_T("%s-last.dmp"), product_name);
+  SafeCStringFormat(&tmp, _T("%s-last.dmp"), product_name);
   CString save_filename = ConcatenatePath(crash_dir_, tmp);
   if (save_filename.IsEmpty()) {
     return GOOPDATE_E_PATH_APPEND_FAILED;
@@ -552,29 +584,32 @@
 
   UNREFERENCED_PARAMETER(custom_info_filename);
 
+  ++metric_crashes_total;
+
   // Build the map of additional parameters to report along with the crash.
   CString ver(GetVersionString() + version_postfix_);
+  CString uid = goopdate_utils::GetUserIdLazyInit(is_machine_);
 
   ParameterMap parameters;
-  parameters[_T("prod")] = _T("Update2");
-  parameters[_T("ver")]  = ver;
-  parameters[_T("lang")] = lang;
+  parameters[_T("prod")]    = _T("Update2");
+  parameters[_T("ver")]     = ver;
+  parameters[_T("userid")]  = uid;
+  parameters[_T("lang")]    = lang;
 
   CString event_text;
-  event_text.Format(
-      _T("Google Update has encountered a fatal error.\r\n")
-      _T("ver=%s;lang=%s;is_machine=%d;upload=%d;minidump=%s"),
-      ver, lang, is_machine_, can_upload ? 1 : 0, crash_filename);
+  SafeCStringFormat(&event_text,
+      _T("%s has encountered a fatal error.\r\n")
+      _T("ver=%s;lang=%s;id=%s;is_machine=%d;upload=%d;minidump=%s"),
+      kAppName,
+      ver, lang, uid, is_machine_, can_upload ? 1 : 0, crash_filename);
   VERIFY1(SUCCEEDED(Crash::Log(EVENTLOG_ERROR_TYPE,
                                kCrashReportEventId,
                                kAppName,
                                event_text)));
 
-  ++metric_crashes_total;
-
   CString report_id;
   return DoSendCrashReport(can_upload,
-                           false,             // Google Update crash.
+                           false,             // Omaha crash.
                            crash_filename,
                            parameters,
                            &report_id);
@@ -609,6 +644,19 @@
     parameters[iter->first.GetString()] = iter->second.GetString();
   }
 
+  const CString product_name = GetProductName(parameters);
+  const std::wstring ver     = parameters[_T("ver")];
+
+  CString event_text;
+  SafeCStringFormat(&event_text,
+      _T("%s has encountered a fatal error.\r\n")
+      _T("ver=%s;is_machine=%d;minidump=%s"),
+      product_name, ver.c_str(), is_machine_, crash_filename);
+  VERIFY1(SUCCEEDED(Crash::Log(EVENTLOG_ERROR_TYPE,
+                               kCrashReportEventId,
+                               product_name,
+                               event_text)));
+
   CString report_id;
   return hr = DoSendCrashReport(can_upload,
                                 true,                  // Out of process crash.
@@ -983,13 +1031,13 @@
   // Append the current user's sid to the pipe name so that machine and
   // user instances of the crash server open different pipes.
   CString user_sid;
-  HRESULT hr = user_info::GetCurrentUser(NULL, NULL, &user_sid);
+  HRESULT hr = user_info::GetProcessUser(NULL, NULL, &user_sid);
   if (FAILED(hr)) {
-    OPT_LOG(LE, (_T("[Failed to get SID for current user][0x%08x]"), hr));
+    OPT_LOG(LE, (_T("[user_info::GetProcessUser failed][0x%08x]"), hr));
     return hr;
   }
-  CString pipe_name(kPipeNamePrefix);
-  pipe_name.AppendFormat(_T("\\%s"), user_sid);
+  CString pipe_name(kCrashPipeNamePrefix);
+  SafeCStringAppendFormat(&pipe_name, _T("\\%s"), user_sid);
 
   CSecurityAttributes pipe_sec_attrs;
 
diff --git a/goopdate/crash.h b/goopdate/crash.h
index c63a70b..013dfd1 100644
--- a/goopdate/crash.h
+++ b/goopdate/crash.h
@@ -22,7 +22,7 @@
 #include <atlstr.h>
 #include <map>
 #include "base/basictypes.h"
-#include "omaha/goopdate/const_goopdate.h"
+#include "omaha/common/const_goopdate.h"
 #include "third_party/gtest/include/gtest/gtest_prod.h"
 #include "third_party/breakpad/src/client/windows/crash_generation/client_info.h"
 #include "third_party/breakpad/src/client/windows/crash_generation/crash_generation_server.h"
@@ -78,8 +78,15 @@
                               const google_breakpad::ClientInfo& client_info,
                               const CString& crash_filename);
 
-  // Reports a crash, uploads it is out of process or can_upload_in_process is
-  // true, saves a copy of the crash, and deletes the crash file.
+  // Reports a crash by logging it to the Windows event log, saving a copy of
+  // the crash, and uploading it. After reporting the crash, the function
+  // deletes the crash file. Crashes that have a custom info file are
+  // considered product crashes and they are always handled out-of-process.
+  // These crashes are always uploaded.
+  // Crashes that do not specify a custom info file are considered Omaha
+  // internal crashes and they are handled in-process. In this case, the
+  // upload behavior is controlled by the value of can_upload_in_process
+  // parameter.
   static HRESULT Report(bool can_upload_in_process,
                         const CString& crash_filename,
                         const CString& custom_info_filename,
@@ -168,6 +175,10 @@
                                MDRawAssertionInfo* assertion,
                                bool succeeded);
 
+  // Start an instance of /report.
+  static void StartReportCrash(bool is_interactive,
+                               const CString& crash_filename);
+
   // Returns true if the crash has happened in an Omaha process which
   // has a top level window up.
   static bool IsInteractive();
diff --git a/goopdate/crash_unittest.cc b/goopdate/crash_unittest.cc
index 9d2797d..93f2f34 100644
--- a/goopdate/crash_unittest.cc
+++ b/goopdate/crash_unittest.cc
@@ -14,18 +14,22 @@
 // ========================================================================
 
 #include <string>
+#include "omaha/base/app_util.h"
+#include "omaha/base/atl_regexp.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/time.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/event_logger.h"
+#include "omaha/common/goopdate_utils.h"
 #include "omaha/goopdate/crash.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/file.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/time.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/goopdate_utils.h"
 #include "omaha/testing/unit_test.h"
 
 // TODO(omaha): Modify the tests to avoid writing files to the staging
@@ -121,11 +125,26 @@
     }
   }
 
-
   static HRESULT StartSenderWithCommandLine(CString* cmd_line) {
     return Crash::StartSenderWithCommandLine(cmd_line);
   }
 
+  // Returns the strings of the last Update2 event in the event log.
+  static CString GetLastCrashEventStrings() {
+    const size_t kBufferSize = 1024;
+    uint8 buffer[kBufferSize] = {0};
+    EVENTLOGRECORD* rec = reinterpret_cast<EVENTLOGRECORD*>(buffer);
+
+    rec->Length = kBufferSize;
+    EXPECT_SUCCEEDED(EventLogger::ReadLastEvent(_T("Update2"), rec));
+    EXPECT_EQ(kCrashUploadEventId, rec->EventID);
+
+    const TCHAR* strings = reinterpret_cast<const TCHAR*>(
+        (reinterpret_cast<uint8*>(buffer + rec->StringOffset)));
+
+    return CString(strings);
+  }
+
   CString module_dir_;
 
   static const int kMaxReportsPerDayFromUnittests = INT_MAX;
@@ -210,6 +229,14 @@
   ASSERT_SUCCEEDED(Crash::Report(true, crash_filename,
                                  custom_info_filename, _T("")));
 
+  // Check the 'crash uploaded' event log.
+  const CString strings = GetLastCrashEventStrings();
+
+  // Verify that the strings include the Id token.
+  AtlRE crash_id_regex(_T("Id={\\h+}."));
+  CString crash_id;
+  EXPECT_TRUE(AtlRE::PartialMatch(strings, crash_id_regex, &crash_id));
+
   // The crash artifacts should be deleted after the crash is reported.
   EXPECT_FALSE(File::Exists(crash_filename));
   EXPECT_FALSE(File::Exists(custom_info_filename));
@@ -218,9 +245,13 @@
 // Tests generation of a minidump and uploading it to the staging server.
 TEST_F(CrashTest, WriteMinidump) {
   ASSERT_TRUE(!Crash::crash_dir_.IsEmpty());
-  ASSERT_TRUE(ExceptionHandler::WriteMinidump(Crash::crash_dir_.GetString(),
-                                              &MinidumpCallback,
-                                              NULL));
+
+  MINIDUMP_TYPE dump_type = MiniDumpNormal;
+  ExceptionHandler handler(Crash::crash_dir_.GetString(), NULL,
+                           &MinidumpCallback, NULL,
+                           ExceptionHandler::HANDLER_NONE,
+                           dump_type, NULL, NULL);
+  ASSERT_TRUE(handler.WriteMinidump());
 }
 
 // Tests the retrieval of the exception information from an existing mini dump.
@@ -257,7 +288,8 @@
 
 TEST_F(CrashTest, GetProductName) {
   Crash::ParameterMap parameters;
-  EXPECT_STREQ(_T("Google Error Reporting"), Crash::GetProductName(parameters));
+  EXPECT_STREQ(SHORT_COMPANY_NAME _T(" Error Reporting"),
+               Crash::GetProductName(parameters));
 
   parameters[_T("prod")] = _T("Update2");
   EXPECT_STREQ(_T("Update2"), Crash::GetProductName(parameters));
@@ -291,9 +323,10 @@
 
   // Try opening the crash services pipe.
   CString user_sid;
-  EXPECT_HRESULT_SUCCEEDED(user_info::GetCurrentUser(NULL, NULL, &user_sid));
+  EXPECT_HRESULT_SUCCEEDED(user_info::GetProcessUser(NULL, NULL, &user_sid));
   CString pipe_name;
-  pipe_name.AppendFormat(_T("\\\\.\\pipe\\GoogleCrashServices\\%s"), user_sid);
+  pipe_name.AppendFormat(_T("\\\\.\\pipe\\%sCrashServices\\%s"),
+                         SHORT_COMPANY_NAME, user_sid);
   scoped_pipe pipe_handle(::CreateFile(pipe_name,
                                        GENERIC_READ | GENERIC_WRITE,
                                        FILE_SHARE_READ | FILE_SHARE_WRITE,
@@ -358,7 +391,7 @@
 
 TEST_F(CrashTest, StartSenderWithCommandLine) {
   // Negative test.
-  CString filename(_T(""));
+  CString filename(_T("DoesNotExist.exe"));
   EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
             StartSenderWithCommandLine(&filename));
 }
diff --git a/goopdate/cred_dialog.cc b/goopdate/cred_dialog.cc
new file mode 100644
index 0000000..e7d60f2
--- /dev/null
+++ b/goopdate/cred_dialog.cc
@@ -0,0 +1,155 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <wincred.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_impersonation.h"
+#include "omaha/base/system_info.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/client/resource.h"
+#include "omaha/goopdate/cred_dialog.h"
+
+namespace omaha {
+
+STDMETHODIMP CredentialDialogBase::QueryUserForCredentials(
+    ULONG_PTR owner_hwnd,
+    BSTR server,
+    BSTR caption,
+    BSTR* username,
+    BSTR* password) {
+  ASSERT1(server && wcslen(server));
+  ASSERT1(caption);
+  ASSERT1(username);
+  ASSERT1(password);
+
+  CORE_LOG(L3, (_T("[LaunchCredentialDialog][COM server launching dialog]")));
+
+  if (!::IsWindow(reinterpret_cast<HWND>(owner_hwnd))) {
+    return E_INVALIDARG;
+  }
+
+  if (!server || !wcslen(server) || !caption) {
+    return E_INVALIDARG;
+  }
+
+  if (!username || !password) {
+    return E_POINTER;
+  }
+
+  CString message;
+  message.LoadString(IDS_PROXY_PROMPT_MESSAGE);
+  message.FormatMessage(message, server);
+
+  CString capt_cstr(caption);
+  if (capt_cstr.IsEmpty()) {
+    capt_cstr.LoadString(IDS_PRODUCT_DISPLAY_NAME);
+  }
+
+  CString user;
+  CString pass;
+  DWORD result = DisplayDialog(reinterpret_cast<HWND>(owner_hwnd),
+                               server,
+                               message,
+                               capt_cstr,
+                               &user,
+                               &pass);
+
+  if (result == NO_ERROR) {
+    user.SetSysString(username);
+    ::SecureZeroMemory(user.GetBuffer(), user.GetAllocLength() * sizeof(TCHAR));
+    pass.SetSysString(password);
+    ::SecureZeroMemory(pass.GetBuffer(), pass.GetAllocLength() * sizeof(TCHAR));
+    return S_OK;
+  }
+
+  return HRESULT_FROM_WIN32(result);
+}
+
+DWORD CredentialDialogBase::DisplayDialog(
+    HWND hwnd,
+    LPCTSTR server,
+    LPCTSTR message,
+    LPCTSTR caption,
+    CString* username_out,
+    CString* password_out) {
+  scoped_library credui_lib(::LoadLibrary(L"credui.dll"));
+  ASSERT1(credui_lib);
+  if (!credui_lib) {
+    CORE_LOG(L3, (_T("[CredUIPromptForCredentialsW not available]")));
+    return ERROR_NOT_READY;
+  }
+
+  typedef BOOL (__stdcall *CredUIPromptForCredentialsW_type)(
+      PCREDUI_INFO pUiInfo,
+      PCTSTR pszTargetName,
+      PCtxtHandle Reserved,
+      DWORD dwAuthError,
+      PCTSTR pszUserName,
+      ULONG ulUserNameMaxChars,
+      PCTSTR pszPassword,
+      ULONG ulPasswordMaxChars,
+      PBOOL pfSave,
+      DWORD dwFlags);
+  CredUIPromptForCredentialsW_type CredUIPromptForCredentialsW_fn =
+      reinterpret_cast<CredUIPromptForCredentialsW_type>(
+          GetProcAddress(get(credui_lib), "CredUIPromptForCredentialsW"));
+  ASSERT1(CredUIPromptForCredentialsW_fn || SystemInfo::IsRunningOnW2K());
+  if (!CredUIPromptForCredentialsW_fn) {
+    CORE_LOG(L3, (_T("[CredUIPromptForCredentialsW not available]")));
+    return ERROR_NOT_READY;
+  }
+
+  wchar_t temp_username[CREDUI_MAX_USERNAME_LENGTH + 1] = {};
+  wchar_t temp_password[CREDUI_MAX_PASSWORD_LENGTH + 1] = {};
+  BOOL check;
+  CREDUI_INFO info = {0};
+  info.cbSize = sizeof(info);
+  info.hwndParent = hwnd;
+  info.pszMessageText = message;
+  info.pszCaptionText = caption;
+
+  DWORD result;
+  do {
+    temp_username[0] = L'\0';
+    temp_password[0] = L'\0';
+    result = CredUIPromptForCredentialsW_fn(
+        &info,
+        server,
+        NULL,
+        0,
+        temp_username,
+        CREDUI_MAX_USERNAME_LENGTH,
+        temp_password,
+        CREDUI_MAX_PASSWORD_LENGTH,
+        &check,
+        CREDUI_FLAGS_ALWAYS_SHOW_UI | CREDUI_FLAGS_GENERIC_CREDENTIALS |
+        CREDUI_FLAGS_DO_NOT_PERSIST);
+    CORE_LOG(L3, (_T("[CredUIPromptForCredentialsW returned %d]"), result));
+  } while (result == NO_ERROR && (!temp_username[0] || !temp_password[0]));
+
+  if (result == NO_ERROR) {
+    username_out->SetString(temp_username);
+    password_out->SetString(temp_password);
+  }
+
+  ::SecureZeroMemory(temp_username, sizeof(temp_username));
+  ::SecureZeroMemory(temp_password, sizeof(temp_password));
+
+  return result;
+}
+
+}  // namespace omaha
+
diff --git a/goopdate/cred_dialog.h b/goopdate/cred_dialog.h
new file mode 100644
index 0000000..531e868
--- /dev/null
+++ b/goopdate/cred_dialog.h
@@ -0,0 +1,190 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_CRED_DIALOG_H_
+#define OMAHA_GOOPDATE_CRED_DIALOG_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include "goopdate/omaha3_idl.h"
+#include "omaha/base/atlregmapex.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/goopdate/com_proxy.h"
+#include "omaha/goopdate/non_localized_resource.h"
+
+namespace omaha {
+
+#pragma warning(push)
+// Construction of local static object is not thread-safe
+#pragma warning(disable:4640)
+
+class ATL_NO_VTABLE CredentialDialogBase
+  : public CComObjectRootEx<CComObjectThreadModel>,
+    public ICredentialDialog,
+    public StdMarshalInfo {
+ public:
+  explicit CredentialDialogBase(bool is_machine)
+      : StdMarshalInfo(is_machine),
+        is_machine_(is_machine) {}
+
+  BEGIN_COM_MAP(CredentialDialogBase)
+    COM_INTERFACE_ENTRY(IStdMarshalInfo)
+    COM_INTERFACE_ENTRY(ICredentialDialog)
+  END_COM_MAP()
+
+  // ICredentialDialog methods.
+  STDMETHOD(QueryUserForCredentials)(ULONG_PTR owner_hwnd,
+                                     BSTR server,
+                                     BSTR caption,
+                                     BSTR* username,
+                                     BSTR* password);
+
+ protected:
+  virtual ~CredentialDialogBase() {}
+
+ private:
+  bool is_machine_;
+
+  static HRESULT DoQueryUserForCredentials(
+      HWND hwnd,
+      BSTR server,
+      BSTR caption,
+      BSTR* username,
+      BSTR* password);
+
+  static DWORD DisplayDialog(
+      HWND hwnd,
+      LPCTSTR server,
+      LPCTSTR message,
+      LPCTSTR caption,
+      CString* username_out,
+      CString* password_out);
+
+  DISALLOW_COPY_AND_ASSIGN(CredentialDialogBase);
+};
+
+template <typename T>
+class ATL_NO_VTABLE CredentialDialog
+    : public CredentialDialogBase,
+      public CComCoClass<CredentialDialog<T> > {
+ public:
+  CredentialDialog() : CredentialDialogBase(T::is_machine()) {}
+
+  DECLARE_NOT_AGGREGATABLE(CredentialDialog);
+  DECLARE_REGISTRY_RESOURCEID_EX(T::registry_res_id())
+
+  BEGIN_REGISTRY_MAP()
+    REGMAP_ENTRY(_T("HKROOT"), T::hk_root())
+    REGMAP_ENTRY(_T("VERSION"), _T("1.0"))
+    REGMAP_ENTRY(_T("PROGID"), T::prog_id())
+    REGMAP_ENTRY(_T("DESCRIPTION"), _T("GoogleUpdate CredentialDialog"))
+    REGMAP_ENTRY(_T("CLSID"), T::class_id())
+    REGMAP_MODULE2(_T("MODULE"), kOmahaOnDemandFileName)
+  END_REGISTRY_MAP()
+
+ protected:
+  virtual ~CredentialDialog() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CredentialDialog);
+};
+
+
+struct CredentialDialogModeUser {
+  static bool is_machine() { return false; }
+  static const TCHAR* const prog_id() { return kProgIDCredentialDialogUser; }
+  static GUID class_id() { return __uuidof(CredentialDialogUserClass); }
+  static UINT registry_res_id() { return IDR_LOCAL_SERVER_RGS; }
+  static const TCHAR* const hk_root() { return _T("HKCU"); }
+};
+
+struct CredentialDialogModeMachine {
+  static bool is_machine() { return true; }
+  static const TCHAR* const prog_id() { return kProgIDCredentialDialogMachine; }
+  static GUID class_id() { return __uuidof(CredentialDialogMachineClass); }
+  static UINT registry_res_id() { return IDR_LOCAL_SERVER_RGS; }
+  static const TCHAR* const hk_root() { return _T("HKLM"); }
+};
+
+typedef CredentialDialog<CredentialDialogModeUser> CredentialDialogUser;
+typedef CredentialDialog<CredentialDialogModeMachine> CredentialDialogMachine;
+
+// A static function that decides whether to display the dialog in-process
+// or launch an out-of-process COM server for showing it, and automatically
+// handles BSTR/CString conversion.
+inline HRESULT LaunchCredentialDialog(
+    bool is_machine,
+    HWND owner_hwnd,
+    const CString& server,
+    const CString& caption,
+    CString* username_out,
+    CString* password_out) {
+  ASSERT1(username_out);
+  ASSERT1(password_out);
+
+  CAccessToken access_token;
+  if (!access_token.GetThreadToken(TOKEN_READ)) {
+    // If this thread is currently impersonating a user, that's perfect, as the
+    // COM server will be started under that user.  If not, verify that the
+    // process isn't running as LocalSystem/LocalService - we cannot show UI
+    // in that scenario without impersonating.
+    bool is_system = true;
+    HRESULT hr = IsSystemProcess(&is_system);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[CredDialog][IsSystemProcess failed][0x%08x]"), hr));
+      return hr;
+    }
+    if (is_system) {
+      CORE_LOG(LE, (_T("[CredDialog][Process running as SYSTEM - aborting]")));
+      return E_ABORT;
+    }
+  }
+
+  CComPtr<ICredentialDialog> dialog;
+  REFCLSID clsid = is_machine ? __uuidof(CredentialDialogMachineClass) :
+                                __uuidof(CredentialDialogUserClass);
+  HRESULT hr = dialog.CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[LaunchCredentialDialog][CoCreate failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  CComBSTR server_bstr(server);
+  CComBSTR caption_bstr(caption);
+  CComBSTR username_bstr;
+  CComBSTR password_bstr;
+  hr = dialog->QueryUserForCredentials(reinterpret_cast<ULONG_PTR>(owner_hwnd),
+                                       server_bstr,
+                                       caption_bstr,
+                                       &username_bstr,
+                                       &password_bstr);
+
+  if (SUCCEEDED(hr)) {
+    username_out->SetString(username_bstr);
+    password_out->SetString(password_bstr);
+  }
+  ::SecureZeroMemory(username_bstr.m_str, username_bstr.ByteLength());
+  ::SecureZeroMemory(password_bstr.m_str, password_bstr.ByteLength());
+
+  return hr;
+}
+
+#pragma warning(pop)
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_CRED_DIALOG_H_
+
diff --git a/goopdate/cred_dialog_unittest.cc b/goopdate/cred_dialog_unittest.cc
new file mode 100644
index 0000000..d848bf3
--- /dev/null
+++ b/goopdate/cred_dialog_unittest.cc
@@ -0,0 +1,154 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/constants.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/scoped_impersonation.h"
+#include "omaha/goopdate/cred_dialog.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+class CredentialDialogTest : public testing::Test {
+ protected:
+  explicit CredentialDialogTest(bool is_machine)
+      : is_machine_(is_machine) {}
+
+  virtual void SetUp() {
+  }
+
+  virtual void TearDown() {
+  }
+
+  virtual bool ShouldRunTest() {
+    if (IsEnvironmentVariableSet(_T("OMAHA_TEST_UI"))) {
+      return true;
+    } else {
+      std::wcout << _T("\tThis test did not run because 'OMAHA_TEST_UI' is ")
+                    _T("not set in the environment.") << std::endl;
+      return false;
+    }
+  }
+
+  virtual void TestComObject(LPCTSTR server, LPCTSTR caption) {
+    REFCLSID clsid = is_machine_ ? __uuidof(CredentialDialogMachineClass)
+                                 : __uuidof(CredentialDialogUserClass);
+    CComPtr<ICredentialDialog> dialog;
+    EXPECT_SUCCEEDED(dialog.CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER));
+    if (dialog) {
+      CComBSTR server_bs(server);
+      CComBSTR caption_bs(caption);
+      CComBSTR user;
+      CComBSTR pass;
+      EXPECT_SUCCEEDED(dialog->QueryUserForCredentials(0,
+                                                       server_bs,
+                                                       caption_bs,
+                                                       &user,
+                                                       &pass));
+    }
+  }
+
+  virtual void TestAuto(LPCTSTR server, LPCTSTR caption) {
+    CString user;
+    CString pass;
+    EXPECT_SUCCEEDED(LaunchCredentialDialog(is_machine_,
+                                            NULL,
+                                            CString(server),
+                                            CString(caption),
+                                            &user,
+                                            &pass));
+  }
+
+  // TestAutoImpersonating() assumes that omaha_unittest.exe is being executed
+  // as LocalSystem; it will fail otherwise.
+  virtual void TestAutoImpersonating(LPCTSTR server, LPCTSTR caption) {
+    EXPECT_SUCCEEDED(InitializeClientSecurity());
+
+    // Manually load the COM proxy into the process before we start; it will
+    // assert on DLL load if we load it while impersonating.  (PSMachine should
+    // always be loaded already in a production environment; this is just an
+    // artifact of the unit test environment.)
+    CPath proxy_path(goopdate_utils::BuildInstallDirectory(
+                         is_machine_, omaha::GetVersionString()));
+    EXPECT_TRUE(proxy_path.Append(kPSFileNameMachine));
+    scoped_library psmachine_load(::LoadLibrary(proxy_path));
+    EXPECT_TRUE(!!psmachine_load);
+
+    scoped_handle logged_on_user_token(
+        goopdate_utils::GetImpersonationTokenForMachineProcess(is_machine_));
+    EXPECT_TRUE(valid(logged_on_user_token));
+    if (valid(logged_on_user_token)) {
+      scoped_impersonation impersonate_user(get(logged_on_user_token));
+      HRESULT hr = HRESULT_FROM_WIN32(impersonate_user.result());
+      EXPECT_SUCCEEDED(hr);
+      if (SUCCEEDED(hr)) {
+        TestAuto(server, caption);
+      }
+    }
+  }
+
+  const bool is_machine_;
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(CredentialDialogTest);
+};
+
+class CredentialDialogMachineTest : public CredentialDialogTest {
+ protected:
+  CredentialDialogMachineTest() : CredentialDialogTest(true) {}
+};
+
+class CredentialDialogUserTest : public CredentialDialogTest {
+ protected:
+  CredentialDialogUserTest() : CredentialDialogTest(false) {}
+};
+
+TEST_F(CredentialDialogUserTest, COM) {
+  if (ShouldRunTest()) {
+    TestComObject(_T("test-u-com-server"), _T("test-u-com-caption"));
+  }
+}
+
+TEST_F(CredentialDialogUserTest, Auto) {
+  if (ShouldRunTest()) {
+    TestAuto(_T("test-u-auto-server"), _T("test-u-auto-caption"));
+  }
+}
+
+TEST_F(CredentialDialogMachineTest, COM) {
+  if (ShouldRunTest()) {
+    TestComObject(_T("test-m-com-server"), _T("test-m-com-caption"));
+  }
+}
+
+TEST_F(CredentialDialogMachineTest, Auto) {
+  if (ShouldRunTest()) {
+    TestAuto(_T("test-m-auto-server"), _T("test-m-auto-caption"));
+  }
+}
+
+TEST_F(CredentialDialogMachineTest, Impersonating) {
+  bool is_system = false;
+  EXPECT_SUCCEEDED(IsSystemProcess(&is_system));
+  if (!is_system) {
+    std::wcout << _T("\tThis test is only meaningful when the unit test is ")
+                  _T("run as the SYSTEM account.") << std::endl;
+  } else {
+    TestAutoImpersonating(_T("test-m-imp-server"), _T("test-m-imp-caption"));
+  }
+}
+
+}  // end namespace omaha
+
diff --git a/goopdate/current_state.cc b/goopdate/current_state.cc
new file mode 100644
index 0000000..14ca0e3
--- /dev/null
+++ b/goopdate/current_state.cc
@@ -0,0 +1,241 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/current_state.h"
+#include <atlsafe.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+
+namespace omaha {
+
+HRESULT CurrentAppState::Create(
+    LONG state_value,
+    const CString& available_version,
+    ULONGLONG bytes_downloaded,
+    ULONGLONG total_bytes_to_download,
+    LONG download_time_remaining_ms,
+    ULONGLONG next_retry_time,
+    LONG install_progress_percentage,
+    LONG install_time_remaining_ms,
+    bool is_canceled,
+    LONG error_code,
+    LONG extra_code1,
+    const CString& completion_message,
+    LONG installer_result_code,
+    LONG installer_result_extra_code1,
+    const CString& post_install_launch_command_line,
+    const CString& post_install_url,
+    PostInstallAction post_install_action,
+    CComObject<CurrentAppState>** state) {
+  ASSERT1(state);
+  ASSERT1(state_value);
+
+  HRESULT hr = CComObject<CurrentAppState>::CreateInstance(state);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  (*state)->state_value_ = state_value;
+  (*state)->available_version_ = available_version.AllocSysString();
+  (*state)->bytes_downloaded_ = bytes_downloaded;
+  (*state)->total_bytes_to_download_ = total_bytes_to_download;
+  (*state)->download_time_remaining_ms_ = download_time_remaining_ms;
+  (*state)->next_retry_time_ = next_retry_time;
+  (*state)->install_progress_percentage_ = install_progress_percentage;
+  (*state)->install_time_remaining_ms_ = install_time_remaining_ms;
+  (*state)->is_canceled_ = is_canceled ? VARIANT_TRUE : VARIANT_FALSE;
+  (*state)->error_code_ = error_code;
+  (*state)->extra_code1_ = extra_code1;
+  (*state)->completion_message_ = completion_message.AllocSysString();
+  (*state)->installer_result_code_ = installer_result_code;
+  (*state)->installer_result_extra_code1_ = installer_result_extra_code1;
+  (*state)->post_install_launch_command_line_ =
+      post_install_launch_command_line.AllocSysString();
+  (*state)->post_install_url_ = post_install_url.AllocSysString();
+  (*state)->post_install_action_ = post_install_action;
+
+  return S_OK;
+}
+
+CurrentAppState::CurrentAppState()
+    : m_bRequiresSave(TRUE),
+      state_value_(0),
+      bytes_downloaded_(0),
+      total_bytes_to_download_(0),
+      download_time_remaining_ms_(0),
+      next_retry_time_(0),
+      install_progress_percentage_(0),
+      install_time_remaining_ms_(0),
+      is_canceled_(VARIANT_FALSE),
+      error_code_(0),
+      extra_code1_(0),
+      installer_result_code_(0),
+      installer_result_extra_code1_(0),
+      post_install_action_(0) {
+  CORE_LOG(L6, (_T("[CurrentAppState::CurrentAppState()")));
+}
+
+CurrentAppState::~CurrentAppState() {
+  CORE_LOG(L6, (_T("[CurrentAppState::~CurrentAppState()")));
+}
+
+// ICurrentState.
+// No locks are necessary because a copy of this object is returned to the
+// client.
+// TODO(omaha3): Perhaps we should set all the properties to valid values
+// regardless of the stateValue.
+// Or perhaps there are some good asserts we can and probably should do. Maybe
+// we need a helper method such as IsStateOrLater() that would handle the
+// non-contiguous issues, such as STATE_NO_UPDATE and STATE_PAUSED. Then, we
+// could ASSERT1(IsStateOrLater(STATE_UPDATE_AVAILABLE));
+
+STDMETHODIMP CurrentAppState::get_stateValue(LONG* state_value) {
+  ASSERT1(state_value);
+
+  *state_value = state_value_;
+  CORE_LOG(L6, (_T("[CurrentAppState::get_stateValue][%d]"), state_value_));
+  return S_OK;
+}
+
+STDMETHODIMP CurrentAppState::get_availableVersion(BSTR* available_version) {
+  ASSERT1(available_version);
+
+  *available_version = available_version_.Copy();
+  return S_OK;
+}
+
+STDMETHODIMP CurrentAppState::get_bytesDownloaded(ULONG* bytes_downloaded) {
+  ASSERT1(bytes_downloaded);
+
+  // Firefox does not support uint32...
+  if (bytes_downloaded_ > kint32max) {
+    return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
+  }
+  *bytes_downloaded = static_cast<ULONG>(bytes_downloaded_);
+  return S_OK;
+}
+
+STDMETHODIMP CurrentAppState::get_totalBytesToDownload(
+    ULONG* total_bytes_to_download) {
+  ASSERT1(total_bytes_to_download);
+
+  // Firefox does not support uint32...
+  if (total_bytes_to_download_ > kint32max) {
+    return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
+  }
+  *total_bytes_to_download = static_cast<ULONG>(total_bytes_to_download_);
+  return S_OK;
+}
+
+STDMETHODIMP CurrentAppState::get_downloadTimeRemainingMs(
+    LONG* download_time_remaining_ms) {
+  ASSERT1(download_time_remaining_ms);
+
+  *download_time_remaining_ms = download_time_remaining_ms_;
+  return S_OK;
+}
+
+STDMETHODIMP CurrentAppState::get_nextRetryTime(ULONGLONG* next_retry_time) {
+  ASSERT1(next_retry_time);
+
+  *next_retry_time = next_retry_time_;
+  return S_OK;
+}
+
+STDMETHODIMP CurrentAppState::get_installProgress(
+    LONG* install_progress_percentage) {
+
+  ASSERT1(install_progress_percentage);
+  *install_progress_percentage = install_progress_percentage_;
+  return S_OK;
+}
+
+STDMETHODIMP CurrentAppState::get_installTimeRemainingMs(
+    LONG* install_time_remaining_ms) {
+
+  ASSERT1(install_time_remaining_ms);
+  *install_time_remaining_ms = install_time_remaining_ms_;
+  return S_OK;
+}
+
+STDMETHODIMP CurrentAppState::get_isCanceled(VARIANT_BOOL* is_canceled) {
+  ASSERT1(is_canceled);
+
+  *is_canceled = is_canceled_;
+  return S_OK;
+}
+
+STDMETHODIMP CurrentAppState::get_errorCode(LONG* error_code) {
+  ASSERT1(error_code);
+
+  *error_code = error_code_;
+  return S_OK;
+}
+
+STDMETHODIMP CurrentAppState::get_extraCode1(LONG* extra_code1) {
+  ASSERT1(extra_code1);
+
+  *extra_code1 = extra_code1_;
+  return S_OK;
+}
+
+STDMETHODIMP CurrentAppState::get_completionMessage(
+    BSTR* completion_message) {
+  ASSERT1(completion_message);
+
+  *completion_message = completion_message_.Copy();
+  return S_OK;
+}
+
+STDMETHODIMP CurrentAppState::get_installerResultCode(
+    LONG* installer_result_code) {
+  ASSERT1(installer_result_code);
+
+  *installer_result_code = installer_result_code_;
+  return S_OK;
+}
+
+STDMETHODIMP CurrentAppState::get_installerResultExtraCode1(
+    LONG* installer_result_extra_code1) {
+  ASSERT1(installer_result_extra_code1);
+
+  *installer_result_extra_code1 = installer_result_extra_code1_;
+  return S_OK;
+}
+
+STDMETHODIMP CurrentAppState::get_postInstallLaunchCommandLine(
+    BSTR* post_install_launch_command_line) {
+  ASSERT1(post_install_launch_command_line);
+
+  *post_install_launch_command_line =
+      post_install_launch_command_line_.Copy();
+  return S_OK;
+}
+
+STDMETHODIMP CurrentAppState::get_postInstallUrl(BSTR* post_install_url) {
+  ASSERT1(post_install_url);
+
+  *post_install_url = post_install_url_.Copy();
+  return S_OK;
+}
+
+STDMETHODIMP CurrentAppState::get_postInstallAction(
+    LONG* post_install_action) {
+  ASSERT1(post_install_action);
+  *post_install_action = static_cast<LONG>(post_install_action_);
+  return S_OK;
+}
+
+}  // namespace omaha
diff --git a/goopdate/current_state.h b/goopdate/current_state.h
new file mode 100644
index 0000000..ac0e743
--- /dev/null
+++ b/goopdate/current_state.h
@@ -0,0 +1,165 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// The current state of the App.
+
+#ifndef OMAHA_GOOPDATE_CURRENT_STATE_H_
+#define OMAHA_GOOPDATE_CURRENT_STATE_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/base/atlregmapex.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/marshal_by_value.h"
+#include "omaha/goopdate/google_update_ps_resource.h"
+#include "omaha/common/goopdate_utils.h"
+
+namespace omaha {
+
+class ATL_NO_VTABLE CurrentAppState
+  : public CComObjectRootEx<CComObjectThreadModel>,
+    public CComCoClass<CurrentAppState>,
+    public IDispatchImpl<ICurrentState,
+                         &__uuidof(ICurrentState),
+                         &CAtlModule::m_libid,
+                         kMajorTypeLibVersion,
+                         kMinorTypeLibVersion>,
+    public IPersistStreamInitImpl<CurrentAppState>,
+    public MarshalByValue<CurrentAppState> {
+ public:
+  static HRESULT Create(LONG state_value,
+                        const CString& available_version,
+                        ULONGLONG bytes_downloaded,
+                        ULONGLONG total_bytes_to_download,
+                        LONG download_time_remaining_ms,
+                        ULONGLONG next_retry_time,
+                        LONG install_progress_percentage,
+                        LONG install_time_remaining_ms,
+                        bool is_canceled,
+                        LONG error_code,
+                        LONG extra_code1,
+                        const CString& completion_message,
+                        LONG installer_result_code,
+                        LONG installer_result_extra_code1,
+                        const CString& success_launch_cmd_line,
+                        const CString& post_install_url,
+                        PostInstallAction post_install_action,
+                        CComObject<CurrentAppState>** current_state);
+  CurrentAppState();
+
+  static bool is_machine() {
+    return goopdate_utils::IsRunningFromOfficialGoopdateDir(true);
+  }
+
+  static const CLSID& GetObjectCLSID() {
+    return is_machine() ? __uuidof(CurrentStateMachineClass) :
+                          __uuidof(CurrentStateUserClass);
+  }
+
+  DECLARE_REGISTRY_RESOURCEID_EX(IDR_INPROC_SERVER_RGS)
+
+  #pragma warning(push)
+  // C4640: construction of local static object is not thread-safe
+  #pragma warning(disable : 4640)
+  BEGIN_REGISTRY_MAP()
+    REGMAP_ENTRY(_T("HKROOT"), is_machine() ? _T("HKLM") : _T("HKCU"))
+    REGMAP_ENTRY(_T("CLSID"), GetObjectCLSID())
+  END_REGISTRY_MAP()
+
+  BEGIN_PROP_MAP(CurrentAppState)
+    PROP_DATA_ENTRY("StateValue", state_value_, VT_I4)
+    PROP_DATA_ENTRY("AvailableVersion", available_version_, VT_BSTR)
+    PROP_DATA_ENTRY("BytesDownloaded", bytes_downloaded_, VT_UI8)
+    PROP_DATA_ENTRY("TotalBytesToDownload", total_bytes_to_download_, VT_UI8)
+    PROP_DATA_ENTRY("DownloadTimeRemainingMs", download_time_remaining_ms_,
+                    VT_I4)
+    PROP_DATA_ENTRY("NextRetryTime", next_retry_time_, VT_UI8)
+    PROP_DATA_ENTRY("InstallProgressPercentage", install_progress_percentage_,
+                    VT_I4)
+    PROP_DATA_ENTRY("InstallTimeRemainingMs", install_time_remaining_ms_, VT_I4)
+    PROP_DATA_ENTRY("IsCanceled", is_canceled_, VT_BOOL)
+    PROP_DATA_ENTRY("ErrorCode", error_code_, VT_I4)
+    PROP_DATA_ENTRY("ExtraCode1", extra_code1_, VT_I4)
+    PROP_DATA_ENTRY("CompletionMessage", completion_message_, VT_BSTR)
+    PROP_DATA_ENTRY("InstallerResultCode", installer_result_code_, VT_I4)
+    PROP_DATA_ENTRY("InstallerResultExtraCode1", installer_result_extra_code1_,
+                    VT_I4)
+    PROP_DATA_ENTRY("PostInstallLaunchCommandLine",
+                    post_install_launch_command_line_, VT_BSTR)
+    PROP_DATA_ENTRY("PostInstallUrl", post_install_url_, VT_BSTR)
+    PROP_DATA_ENTRY("PostInstallAction", post_install_action_, VT_I4)
+  END_PROP_MAP()
+  #pragma warning(pop)
+
+  // ICurrentState.
+  STDMETHOD(get_stateValue)(LONG* state_value);
+  STDMETHOD(get_availableVersion)(BSTR* available_version);
+  STDMETHOD(get_bytesDownloaded)(ULONG* bytes_downloaded);
+  STDMETHOD(get_totalBytesToDownload)(ULONG* total_bytes_to_download);
+  STDMETHOD(get_downloadTimeRemainingMs)(LONG* download_time_remaining_ms);
+  STDMETHOD(get_nextRetryTime)(ULONGLONG* next_retry_time);
+  STDMETHOD(get_installProgress)(LONG* install_progress_percentage);
+  STDMETHOD(get_installTimeRemainingMs)(LONG* install_time_remaining_ms);
+  STDMETHOD(get_isCanceled)(VARIANT_BOOL* is_canceled);
+  STDMETHOD(get_errorCode)(LONG* error_code);
+  STDMETHOD(get_extraCode1)(LONG* extra_code1);
+  STDMETHOD(get_completionMessage)(BSTR* completion_message);
+  STDMETHOD(get_installerResultCode)(LONG* installer_result_code);
+  STDMETHOD(get_installerResultExtraCode1)(LONG* installer_result_extra_code1);
+  STDMETHOD(get_postInstallLaunchCommandLine)(
+      BSTR* post_install_launch_command_line);
+  STDMETHOD(get_postInstallUrl)(BSTR* post_install_url);
+  STDMETHOD(get_postInstallAction)(LONG* post_install_action);
+
+ protected:
+  virtual ~CurrentAppState();
+
+  BEGIN_COM_MAP(CurrentAppState)
+    COM_INTERFACE_ENTRY(ICurrentState)
+    COM_INTERFACE_ENTRY(IDispatch)
+    COM_INTERFACE_ENTRY(IMarshal)
+  END_COM_MAP()
+
+  BOOL m_bRequiresSave;
+
+ private:
+  LONG state_value_;
+  CComBSTR available_version_;
+  ULONGLONG bytes_downloaded_;
+  ULONGLONG total_bytes_to_download_;
+  LONG download_time_remaining_ms_;
+  ULONGLONG next_retry_time_;
+  LONG install_progress_percentage_;
+  LONG install_time_remaining_ms_;
+  VARIANT_BOOL is_canceled_;
+  LONG error_code_;
+  LONG extra_code1_;
+  CComBSTR completion_message_;
+  LONG installer_result_code_;
+  LONG installer_result_extra_code1_;
+  CComBSTR post_install_launch_command_line_;
+  CComBSTR post_install_url_;
+  LONG post_install_action_;
+
+  DISALLOW_COPY_AND_ASSIGN(CurrentAppState);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_CURRENT_STATE_H_
diff --git a/goopdate/download_complete_ping_event.cc b/goopdate/download_complete_ping_event.cc
new file mode 100644
index 0000000..f0a6b74
--- /dev/null
+++ b/goopdate/download_complete_ping_event.cc
@@ -0,0 +1,83 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/download_complete_ping_event.h"
+#include "omaha/base/string.h"
+#include "omaha/base/xml_utils.h"
+#include "omaha/common/xml_const.h"
+
+namespace omaha {
+
+DownloadCompletePingEvent::DownloadCompletePingEvent(
+    Types type,
+    Results result,
+    int error_code,
+    int extra_code1,
+    int download_time_ms,
+    uint64 num_bytes_downloaded,
+    uint64 app_size)
+    : PingEvent(type, result, error_code, extra_code1),
+      download_time_ms_(download_time_ms),
+      num_bytes_downloaded_(num_bytes_downloaded),
+      app_size_(app_size) {
+}
+
+HRESULT DownloadCompletePingEvent::ToXml(IXMLDOMNode* parent_node) const {
+  HRESULT hr = PingEvent::ToXml(parent_node);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // No need to report download metrics if nothing is downloaded.
+  if (num_bytes_downloaded_ == 0) {
+    return S_OK;
+  }
+
+  hr = AddXMLAttributeNode(parent_node,
+                           xml::kXmlNamespace,
+                           xml::attribute::kDownloadTime,
+                           itostr(download_time_ms_));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = AddXMLAttributeNode(parent_node,
+                           xml::kXmlNamespace,
+                           xml::attribute::kAppBytesDownloaded,
+                           String_Uint64ToString(num_bytes_downloaded_, 10));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return AddXMLAttributeNode(parent_node,
+                             xml::kXmlNamespace,
+                             xml::attribute::kAppBytesTotal,
+                             String_Uint64ToString(app_size_, 10));
+}
+
+CString DownloadCompletePingEvent::ToString() const {
+  CString ping_str;
+  ping_str.Format(_T("%s, %s=%s, %s=%s, %s=%s"),
+      PingEvent::ToString(),
+      xml::attribute::kDownloadTime, itostr(download_time_ms_),
+      xml::attribute::kAppBytesDownloaded,
+      String_Uint64ToString(num_bytes_downloaded_, 10),
+      xml::attribute::kAppBytesTotal, String_Uint64ToString(app_size_, 10));
+
+  return ping_str;
+}
+
+}  // namespace omaha
+
diff --git a/goopdate/download_complete_ping_event.h b/goopdate/download_complete_ping_event.h
new file mode 100644
index 0000000..bdaa965
--- /dev/null
+++ b/goopdate/download_complete_ping_event.h
@@ -0,0 +1,48 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_DOWNLOAD_COMPLETE_PING_EVENT_H_
+#define OMAHA_GOOPDATE_DOWNLOAD_COMPLETE_PING_EVENT_H_
+
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "omaha/common/ping_event.h"
+
+namespace omaha {
+
+class DownloadCompletePingEvent : public PingEvent {
+ public:
+  DownloadCompletePingEvent(Types type,
+                            Results result,
+                            int error_code,
+                            int extra_code1,
+                            int download_time_ms,
+                            uint64 num_bytes_downloaded,
+                            uint64 app_size);
+  virtual ~DownloadCompletePingEvent() {}
+
+  virtual HRESULT ToXml(IXMLDOMNode* parent_node) const;
+  virtual CString ToString() const;
+
+ private:
+  const int download_time_ms_;
+  const uint64 num_bytes_downloaded_;
+  const uint64 app_size_;
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_DOWNLOAD_COMPLETE_PING_EVENT_H_
diff --git a/goopdate/download_complete_ping_event_test.cc b/goopdate/download_complete_ping_event_test.cc
new file mode 100644
index 0000000..fcd005e
--- /dev/null
+++ b/goopdate/download_complete_ping_event_test.cc
@@ -0,0 +1,148 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/reg_key.h"
+#include "omaha/base/string.h"
+#include "omaha/common/ping.h"
+#include "omaha/goopdate/download_complete_ping_event.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+  const CString kPv = _T("1.3.23.0");
+  const CString kLang = _T("en");
+  const CString kBrandCode = _T("GOOG");
+  const CString kClientId = _T("testclientid");
+  const CString kIid = _T("{7C0B6E56-B24B-436b-A960-A6EA201E886D}");
+}  // namespace
+
+class DownloadCompletePingEventTest : public testing::Test {
+ protected:
+  void SetUpRegistry() {
+    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
+    OverrideRegistryHives(kRegistryHiveOverrideRoot);
+
+    const TCHAR* const kOmahaUserClientStatePath =
+        _T("HKCU\\Software\\") SHORT_COMPANY_NAME
+        _T("\\") PRODUCT_NAME
+        _T("\\ClientState\\") GOOPDATE_APP_ID;
+
+    EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                              kRegValueProductVersion,
+                                              kPv));
+    EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                              kRegValueLanguage,
+                                              kLang));
+    EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                              kRegValueBrandCode,
+                                              kBrandCode));
+    EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                              kRegValueClientId,
+                                              kClientId));
+    EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kOmahaUserClientStatePath,
+                                              kRegValueInstallationId,
+                                              kIid));
+  }
+
+  virtual void CleanUpRegistry() {
+    RestoreRegistryHives();
+    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
+  }
+};
+
+TEST_F(DownloadCompletePingEventTest, BuildDownloadCompletePing) {
+  const int error_code = 34;
+  const int extra_code1 = 3333;
+  const int download_time_ms = 15000;
+  const uint64 num_bytes_downloaded = 4000000;
+  const uint64 app_packages_total_size = 8000000;
+
+  SetUpRegistry();
+  PingEventPtr ping_event(
+      new DownloadCompletePingEvent(PingEvent::EVENT_INSTALL_COMPLETE,
+                                    PingEvent::EVENT_RESULT_SUCCESS,
+                                    error_code,
+                                    extra_code1,
+                                    download_time_ms,
+                                    num_bytes_downloaded,
+                                    app_packages_total_size));
+
+  Ping ping(false, _T("unittest"), _T("InstallSource_Foo"));
+  std::vector<CString> apps;
+  apps.push_back(GOOPDATE_APP_ID);
+  ping.LoadAppDataFromRegistry(apps);
+  ping.BuildAppsPing(ping_event);
+  CleanUpRegistry();
+
+  CString expected_ping_request_substring;
+  expected_ping_request_substring.Format(
+      _T("<app appid=\"%s\" version=\"%s\" nextversion=\"\" lang=\"%s\" ")
+      _T("brand=\"%s\" client=\"%s\" iid=\"%s\">")
+      _T("<event eventtype=\"%d\" eventresult=\"%d\" ")
+      _T("errorcode=\"%d\" extracode1=\"%d\" ")
+      _T("download_time_ms=\"%d\" downloaded=\"%I64u\" total=\"%I64u\"/>")
+      _T("</app>"),
+      GOOPDATE_APP_ID, kPv, kLang, kBrandCode, kClientId, kIid,
+      PingEvent::EVENT_INSTALL_COMPLETE, PingEvent::EVENT_RESULT_SUCCESS,
+      error_code, extra_code1, download_time_ms, num_bytes_downloaded,
+      app_packages_total_size);
+
+  CString actual_ping_request;
+  ping.BuildRequestString(&actual_ping_request);
+  EXPECT_NE(-1, actual_ping_request.Find(expected_ping_request_substring));
+}
+
+TEST_F(DownloadCompletePingEventTest, BuildDownloadCompletePing_NoDownload) {
+  const int error_code = 888;
+  const int extra_code1 = 0;
+  const int download_time_ms = 15;
+  const uint64 num_bytes_downloaded = 0;  // 0 indicates no actual download.
+  const uint64 app_packages_total_size = 4000000;
+
+  SetUpRegistry();
+  PingEventPtr ping_event(
+      new DownloadCompletePingEvent(PingEvent::EVENT_INSTALL_COMPLETE,
+                                    PingEvent::EVENT_RESULT_SUCCESS,
+                                    error_code,
+                                    extra_code1,
+                                    download_time_ms,
+                                    num_bytes_downloaded,
+                                    app_packages_total_size));
+
+  Ping ping(false, _T("unittest"), _T("InstallSource_Foo"));
+  std::vector<CString> apps;
+  apps.push_back(GOOPDATE_APP_ID);
+  ping.LoadAppDataFromRegistry(apps);
+  ping.BuildAppsPing(ping_event);
+  CleanUpRegistry();
+
+  CString expected_ping_request_substring;
+  expected_ping_request_substring.Format(
+      _T("<app appid=\"%s\" version=\"%s\" nextversion=\"\" lang=\"%s\" ")
+      _T("brand=\"%s\" client=\"%s\" iid=\"%s\">")
+      _T("<event eventtype=\"%d\" eventresult=\"%d\" ")
+      _T("errorcode=\"%d\" extracode1=\"%d\"/>")
+      _T("</app>"),
+      GOOPDATE_APP_ID, kPv, kLang, kBrandCode, kClientId, kIid,
+      PingEvent::EVENT_INSTALL_COMPLETE, PingEvent::EVENT_RESULT_SUCCESS,
+      error_code, extra_code1);
+
+  CString actual_ping_request;
+  ping.BuildRequestString(&actual_ping_request);
+  EXPECT_NE(-1, actual_ping_request.Find(expected_ping_request_substring));
+}
+
+}  // namespace omaha
diff --git a/goopdate/download_manager.cc b/goopdate/download_manager.cc
new file mode 100644
index 0000000..49674c3
--- /dev/null
+++ b/goopdate/download_manager.cc
@@ -0,0 +1,614 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// The download manager uses the network request to download the remote file.
+// Once the download is complete, the download manager stores the file in
+// the package cache, then it copies the file out to a location specified
+// by the caller.
+
+// TODO(omaha): the path where to copy the file is hardcoded. Change the
+// class interface to allow the path as a parameter.
+
+#include "omaha/goopdate/download_manager.h"
+#include <algorithm>
+#include <vector>
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/scoped_impersonation.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/string.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/user_rights.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/package_cache.h"
+#include "omaha/goopdate/server_resource.h"
+#include "omaha/goopdate/string_formatter.h"
+#include "omaha/goopdate/worker_metrics.h"
+#include "omaha/goopdate/worker_utils.h"
+#include "omaha/net/bits_request.h"
+#include "omaha/net/http_client.h"
+#include "omaha/net/network_request.h"
+#include "omaha/net/net_utils.h"
+#include "omaha/net/simple_request.h"
+
+namespace omaha {
+
+namespace {
+
+// Creates and initializes an instance of the NetworkRequest for the
+// DownloadManager to use. Defines the fallback chain: BITS, WinHttp.
+HRESULT CreateNetworkRequest(NetworkRequest** network_request_ptr) {
+  NetworkConfig* network_config = NULL;
+  NetworkConfigManager& network_manager = NetworkConfigManager::Instance();
+  HRESULT hr = network_manager.GetUserNetworkConfig(&network_config);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  const NetworkConfig::Session& session(network_config->session());
+  NetworkRequest* network_request(new NetworkRequest(session));
+
+  // TODO(omaha): provide a mechanism for different timeout values in
+  // silent and interactive downloads.
+
+  // BITS transfers files only when the job owner is logged on. If the process
+  // "Run As" another user, an empty BITS job gets created in suspended state
+  // but there is no way to manipulate the job, nor cancel it.
+  bool is_logged_on = false;
+  hr = UserRights::UserIsLoggedOnInteractively(&is_logged_on);
+  if (SUCCEEDED(hr) && is_logged_on) {
+    BitsRequest* bits_request(new BitsRequest);
+    bits_request->set_minimum_retry_delay(kSecPerMin);
+    bits_request->set_no_progress_timeout(5 * kSecPerMin);
+    network_request->AddHttpRequest(bits_request);
+  }
+
+  network_request->AddHttpRequest(new SimpleRequest);
+
+  network_request->set_num_retries(3);
+  *network_request_ptr = network_request;
+  return S_OK;
+}
+
+// TODO(omaha): Unit test this method.
+HRESULT ValidateSize(const CString& file_path, uint64 expected_size) {
+  CORE_LOG(L3, (_T("[ValidateSize][%s][%lld]"), file_path, expected_size));
+  ASSERT1(File::Exists(file_path));
+  ASSERT1(expected_size != 0);
+  ASSERT(expected_size <= UINT_MAX,
+         (_T("TODO(omaha): Add uint64 support to GetFileSizeUnopen().")));
+
+  uint32 file_size(0);
+  HRESULT hr = File::GetFileSizeUnopen(file_path, &file_size);
+  ASSERT1(SUCCEEDED(hr));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (0 == file_size) {
+    return GOOPDATEDOWNLOAD_E_FILE_SIZE_ZERO;
+  } else if (file_size < expected_size) {
+    return GOOPDATEDOWNLOAD_E_FILE_SIZE_SMALLER;
+  } else if (file_size > expected_size) {
+    return GOOPDATEDOWNLOAD_E_FILE_SIZE_LARGER;
+  }
+
+  ASSERT1(file_size == expected_size);
+  return S_OK;
+}
+
+}  // namespace
+
+DownloadManager::DownloadManager(bool is_machine)
+    : lock_(NULL), is_machine_(false) {
+  CORE_LOG(L3, (_T("[DownloadManager::DownloadManager]")));
+
+  omaha::interlocked_exchange_pointer(&lock_,
+                                      static_cast<Lockable*>(new LLock));
+  __mutexScope(lock());
+
+  is_machine_ = is_machine;
+
+  package_cache_root_ =
+      is_machine ?
+      ConfigManager::Instance()->GetMachineSecureDownloadStorageDir() :
+      ConfigManager::Instance()->GetUserDownloadStorageDir();
+
+  CORE_LOG(L3, (_T("[package_cache_root][%s]"), package_cache_root()));
+
+  package_cache_.reset(new PackageCache);
+}
+
+DownloadManager::~DownloadManager() {
+  CORE_LOG(L3, (_T("[DownloadManager::~DownloadManager]")));
+
+  ASSERT1(!IsBusy());
+
+  delete &lock();
+  omaha::interlocked_exchange_pointer(&lock_, static_cast<Lockable*>(NULL));
+}
+
+const Lockable& DownloadManager::lock() const {
+  return *omaha::interlocked_exchange_pointer(&lock_, lock_);
+}
+
+bool DownloadManager::is_machine() const {
+  __mutexScope(lock());
+  return is_machine_;
+}
+
+CString DownloadManager::package_cache_root() const {
+  __mutexScope(lock());
+  return package_cache_root_;
+}
+
+PackageCache* DownloadManager::package_cache() {
+  __mutexScope(lock());
+  return package_cache_.get();
+}
+
+const PackageCache* DownloadManager::package_cache() const {
+  __mutexScope(lock());
+  return package_cache_.get();
+}
+
+HRESULT DownloadManager::Initialize() {
+  HRESULT hr = package_cache()->Initialize(package_cache_root());
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[failed to initialize the package cache]0x%08x]"), hr));
+    return hr;
+  }
+
+  hr = package_cache()->PurgeOldPackagesIfNecessary();
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[PurgeOldPackagesIfNecessary failed][0x%08x]"), hr));
+  }
+
+  return S_OK;
+}
+
+CString DownloadManager::GetMessageForError(const ErrorContext& error_context,
+                                            const CString& language) {
+  CString message;
+  StringFormatter formatter(language);
+
+  switch (error_context.error_code) {
+    case SIGS_E_INVALID_SIGNATURE:
+    case GOOPDATEDOWNLOAD_E_FILE_SIZE_ZERO:
+    case GOOPDATEDOWNLOAD_E_FILE_SIZE_SMALLER:
+    case GOOPDATEDOWNLOAD_E_FILE_SIZE_LARGER:
+      VERIFY1(SUCCEEDED(formatter.LoadString(IDS_DOWNLOAD_HASH_MISMATCH,
+                                             &message)));
+      break;
+    case GOOPDATEDOWNLOAD_E_CACHING_FAILED:
+      VERIFY1(SUCCEEDED(formatter.FormatMessage(
+          &message, IDS_CACHING_ERROR, error_context.extra_code1, &message)));
+      break;
+    default:
+      if (!worker_utils::FormatMessageForNetworkError(error_context.error_code,
+                                                      language,
+                                                      &message)) {
+        VERIFY1(SUCCEEDED(formatter.LoadString(IDS_DOWNLOAD_ERROR, &message)));
+      }
+      break;
+  }
+
+  ASSERT1(!message.IsEmpty());
+  return message;
+}
+
+HRESULT DownloadManager::DownloadApp(App* app) {
+  CORE_LOG(L3, (_T("[DownloadManager::DownloadApp][0x%p]"), app));
+  ASSERT1(app);
+
+  // TODO(omaha3): Maybe rename these to include "app_". Maybe add package
+  // metrics too.
+  ++metric_worker_download_total;
+
+  // We assume the number of packages does not change after download is started.
+  // TODO(omaha3): Could be a problem if we allow installers to request more
+  // packages (http://b/1969071), but we will have lots of other problems then.
+  AppVersion* app_version = app->working_version();
+  const size_t num_packages = app_version->GetNumberOfPackages();
+
+  State* state = NULL;
+  HRESULT hr = CreateStateForApp(app, &state);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CreateStateForApp failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  app->Downloading();
+
+  CString message;
+  hr = S_OK;
+
+  for (size_t i = 0; i < num_packages; ++i) {
+    Package* package(app_version->GetPackage(i));
+    hr = DoDownloadPackage(package, state);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[DoDownloadPackage failed][%s][%s][0x%08x][%Iu]"),
+                    app->display_name(), package->filename(), hr, i));
+      message = GetMessageForError(ErrorContext(hr, error_extra_code1()),
+                                   app->app_bundle()->display_language());
+      break;
+    }
+  }
+
+  if (SUCCEEDED(hr)) {
+    app->DownloadComplete();
+
+    // TODO(omaha3): Extract and apply differential update if necessary.
+
+    app->MarkReadyToInstall();
+  } else {
+    app->Error(ErrorContext(hr, error_extra_code1()), message);
+  }
+
+  if (SUCCEEDED(hr)) {
+    ++metric_worker_download_succeeded;
+  }
+
+  VERIFY1(SUCCEEDED(DeleteStateForApp(app)));
+
+  return hr;
+}
+
+HRESULT DownloadManager::DownloadPackage(Package* package) {
+  CORE_LOG(L3, (_T("[DownloadManager::DownloadPackage][0x%p]"), package));
+  ASSERT1(package);
+
+  UNREFERENCED_PARAMETER(package);
+
+  // TODO(omaha): implement in terms of DoDownloadPackage.
+
+  return E_NOTIMPL;
+}
+
+HRESULT DownloadManager::GetPackage(const Package* package,
+                                    const CString& dir) const {
+  const CString app_id(package->app_version()->app()->app_guid_string());
+  const CString version(package->app_version()->version());
+  const CString package_name(package->filename());
+
+  const PackageCache::Key key(app_id, version, package_name);
+
+  CORE_LOG(L3, (_T("[DownloadManager::GetPackage][%s]"), key.ToString()));
+
+  const CString dest_file(ConcatenatePath(dir, package_name));
+  CORE_LOG(L3, (_T("[destination file is '%s']"), dest_file));
+
+  const CString hash(package->expected_hash());
+  HRESULT hr = package_cache()->Get(key, dest_file, hash);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[failed to get from cache][0x%08x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+bool DownloadManager::IsPackageAvailable(const Package* package) const {
+  const CString app_id(package->app_version()->app()->app_guid_string());
+  const CString version(package->app_version()->version());
+  const CString package_name(package->filename());
+
+  const PackageCache::Key key(app_id, version, package_name);
+
+  CORE_LOG(L3, (_T("[DownloadManager::IsPackageAvailable][%s]"),
+      key.ToString()));
+
+  const CString hash(package->expected_hash());
+  return package_cache()->IsCached(key, hash);
+}
+
+// Attempts a package download by trying the fallback urls. It does not
+// retry the download if the file validation fails.
+// Assumes the packages are not created or destroyed while method is running.
+HRESULT DownloadManager::DoDownloadPackage(Package* package, State* state) {
+  ASSERT1(package);
+  ASSERT1(state);
+
+  App* app = package->app_version()->app();
+  const CString app_id(app->app_guid_string());
+  const CString version(package->app_version()->version());
+  const CString package_name(package->filename());
+
+  const ConfigManager& cm = *ConfigManager::Instance();
+  // TODO(omaha): Since we don't currently have is_manual, check the least
+  // restrictive case of true. It would be nice if we had is_manual. We'll see.
+  ASSERT(SUCCEEDED(app->CheckGroupPolicy()),
+         (_T("Downloading package app for disallowed app.")));
+
+  if (app_id.IsEmpty() || package_name.IsEmpty()) {
+    return E_INVALIDARG;
+  }
+
+  PackageCache::Key key(app_id, version, package_name);
+
+  CORE_LOG(L3, (_T("[DownloadManager::DoDownloadPackage][%s]"),
+      key.ToString()));
+
+  const CString hash(package->expected_hash());
+
+  if (!package_cache()->IsCached(key, hash)) {
+    CORE_LOG(L3, (_T("[The package is not cached]")));
+
+    // TODO(omaha3): May need to consider the DownloadPackage case. Also, we may
+    // want a error code that does not include "UPDATE". If this is a valid
+    // case, need to add message for
+    // GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED to GetMessageForError().
+    // As of 9/7/2010, the offline case does not allow downloading if the
+    // package cannot be found, so offline scenarios should never get here.
+    if (!app->is_eula_accepted()) {
+      ASSERT(false, (_T("Can't download because app EULA is not accepted.")));
+      return GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED;
+    }
+
+    if (!ConfigManager::Instance()->CanUseNetwork(is_machine_)) {
+      CORE_LOG(LE, (_T("[DoDownloadPackage][network use prohibited]")));
+      return GOOPDATE_E_CANNOT_USE_NETWORK;
+    }
+
+    CString unique_filename_path;
+    HRESULT hr = BuildUniqueFileName(package_name, &unique_filename_path);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[BuildUniqueFileName failed][0x%08x]"), hr));
+      return hr;
+    }
+
+    NetworkRequest* network_request = state->network_request();
+
+    network_request->set_callback(package);
+
+    const std::vector<CString> download_base_urls(
+        package->app_version()->download_base_urls());
+
+    hr = E_FAIL;
+    for (size_t i = 0; i < download_base_urls.size() && FAILED(hr); ++i) {
+      // TODO(omaha3): Append nicely.
+      const CString url = download_base_urls[i] + package_name;
+
+      if (i > 0) {
+        CORE_LOG(L3, (_T("[retrying download with fallback base url][%s]"),
+                      url));
+      }
+      // TODO(omaha3): Increment a usage stat for the ith url being used.
+      // Supporting 3 or 4 should be enough.
+
+      CORE_LOG(L3, (_T("[starting file download][from '%s'][to '%s']"),
+                   url, unique_filename_path));
+
+      // Downloading a file is a blocking call. It assumes the model is not
+      // locked by the calling thread, otherwise other threads won't be able to
+      // to access the model until the file download is complete.
+      ASSERT1(!package->model()->IsLockedByCaller());
+
+      hr = network_request->DownloadFile(url, unique_filename_path);
+      if (FAILED(hr)) {
+        CORE_LOG(LW, (_T("[DownloadFile failed from url][0x%08x]['%s']['%s']"),
+                      hr, package_name, download_base_urls[i]));
+        worker_utils::AddHttpRequestDataToEventLog(
+            hr,
+            network_request->http_status_code(),
+            network_request->trace(),
+            is_machine_);
+        continue;
+      }
+
+      // A file has been successfully downloaded from current url. Validate
+      // and cache it.
+      hr = CallAsSelfAndImpersonate2(
+          this,
+          &DownloadManager::CachePackage,
+          static_cast<const Package*>(package),
+          static_cast<const CString*>(&unique_filename_path));
+      if (SUCCEEDED(hr)) {
+        break;
+      }
+
+      CORE_LOG(LE, (_T("[failed to cache package][0x%08x]"), hr));
+    }
+    VERIFY1(SUCCEEDED(network_request->Close()));
+    DeleteBeforeOrAfterReboot(unique_filename_path);
+
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[DownloadFile/caching failed from all urls][0x%08x]"),
+                    hr));
+      return hr;
+    }
+
+    // Assumes that downloaded bytes equal to the expected package size.
+    app->UpdateNumBytesDownloaded(package->expected_size());
+  } else {
+    CORE_LOG(L3, (_T("[package is cached]")));
+
+    // TODO(omaha3): We probably need to update the download stats that
+    // Package::OnProgress would set. It may be misleading to set
+    // bytes_downloaded to anything other than zero, but App uses this to
+    // calculate progress. I suppose we could add an is_complete field instead.
+    // There is a related issue with the callback not being called with the
+    // final size. See the TODO in the unit tests.
+  }
+
+  ASSERT1(package_cache()->IsCached(key, hash));
+  return S_OK;
+}
+
+void DownloadManager::Cancel(App* app) {
+  CORE_LOG(L3, (_T("[DownloadManager::Cancel][0x%p]"), app));
+  ASSERT1(app);
+
+  __mutexScope(lock());
+
+  for (size_t i = 0; i != download_state_.size(); ++i) {
+    if (app == download_state_[i]->app()) {
+      VERIFY1(SUCCEEDED(download_state_[i]->CancelNetworkRequest()));
+    }
+  }
+}
+
+void DownloadManager::CancelAll() {
+  CORE_LOG(L3, (_T("[DownloadManager::CancelAll]")));
+
+  __mutexScope(lock());
+
+  for (size_t i = 0; i != download_state_.size(); ++i) {
+    VERIFY1(SUCCEEDED(download_state_[i]->CancelNetworkRequest()));
+  }
+}
+
+bool DownloadManager::IsBusy() const {
+  __mutexScope(lock());
+  return !download_state_.empty();
+}
+
+HRESULT DownloadManager::PurgeAppLowerVersions(const CString& app_id,
+                                               const CString& version) {
+  return package_cache()->PurgeAppLowerVersions(app_id, version);
+}
+
+HRESULT DownloadManager::CachePackage(const Package* package,
+                                      const CString* filename_path) {
+  ASSERT1(package);
+  ASSERT1(filename_path);
+
+  const CString app_id(package->app_version()->app()->app_guid_string());
+  const CString version(package->app_version()->version());
+  const CString package_name(package->filename());
+  PackageCache::Key key(app_id, version, package_name);
+
+  const CString hash(package->expected_hash());
+
+  HRESULT hr = package_cache()->Put(key, *filename_path, hash);
+  if (hr != SIGS_E_INVALID_SIGNATURE) {
+    if (FAILED(hr)) {
+      set_error_extra_code1(static_cast<int>(hr));
+      return GOOPDATEDOWNLOAD_E_CACHING_FAILED;
+    }
+    return hr;
+  }
+
+  // Get a more specific error if possible.
+  // TODO(omaha): It would be nice to detect that we downloaded a proxy
+  // page and tell the user this. It would be even better if we could
+  // display it; that would require a lot more plumbing.
+  HRESULT size_hr = ValidateSize(*filename_path, package->expected_size());
+  if (FAILED(size_hr)) {
+    hr = size_hr;
+  }
+
+  return hr;
+}
+
+// The file is initially downloaded to a temporary unique name, to account
+// for the case where the same file is downloaded by multiple callers.
+HRESULT DownloadManager::BuildUniqueFileName(const CString& filename,
+                                             CString* unique_filename) {
+  ASSERT1(unique_filename);
+
+  GUID guid(GUID_NULL);
+  HRESULT hr = ::CoCreateGuid(&guid);
+  if (FAILED(hr)) {
+    CORE_LOG(L3, (_T("[CoCreateGuid failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  // Format of the unique file name is: <temp_download_dir>/<guid>-<filename>.
+  const CString temp_dir(ConfigManager::Instance()->GetTempDownloadDir());
+  CString temp_filename;
+  SafeCStringFormat(&temp_filename, _T("%s-%s"), GuidToString(guid), filename);
+  *unique_filename = ConcatenatePath(temp_dir, temp_filename);
+
+  return unique_filename->IsEmpty() ?
+         GOOPDATEDOWNLOAD_E_UNIQUE_FILE_PATH_EMPTY : S_OK;
+}
+
+HRESULT DownloadManager::CreateStateForApp(App* app, State** state) {
+  ASSERT1(app);
+  ASSERT1(state);
+
+  *state = NULL;
+
+  NetworkRequest* network_request = NULL;
+  HRESULT hr = CreateNetworkRequest(&network_request);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  ASSERT1(network_request);
+
+  const bool use_background_priority =
+                  (app->app_bundle()->priority() < INSTALL_PRIORITY_HIGH);
+  network_request->set_low_priority(use_background_priority);
+
+  network_request->set_proxy_auth_config(
+      app->app_bundle()->GetProxyAuthConfig());
+
+  scoped_ptr<State> state_ptr(new State(app, network_request));
+
+  __mutexBlock(lock()) {
+    download_state_.push_back(state_ptr.release());
+    *state = download_state_.back();
+  }
+
+  return S_OK;
+}
+
+HRESULT DownloadManager::DeleteStateForApp(App* app) {
+  ASSERT1(app);
+
+  __mutexScope(lock());
+
+  typedef std::vector<State*>::iterator Iter;
+  for (Iter it(download_state_.begin()); it != download_state_.end(); ++it) {
+    if (app == (*it)->app()) {
+      delete *it;
+      download_state_.erase(it);
+      return S_OK;
+    }
+  }
+
+  ASSERT1(false);
+
+  return E_UNEXPECTED;
+}
+
+DownloadManager::State::State(App* app, NetworkRequest* network_request)
+    : app_(app), network_request_(network_request) {
+  ASSERT1(app);
+  ASSERT1(network_request);
+}
+
+DownloadManager::State::~State() {
+}
+
+NetworkRequest* DownloadManager::State::network_request() const {
+  ASSERT1(ConfigManager::Instance()->CanUseNetwork(
+                                         app_->app_bundle()->is_machine()));
+
+  return network_request_.get();
+}
+
+HRESULT DownloadManager::State::CancelNetworkRequest() {
+  return network_request_->Cancel();
+}
+
+}  // namespace omaha
diff --git a/goopdate/download_manager.h b/goopdate/download_manager.h
new file mode 100644
index 0000000..aa84232
--- /dev/null
+++ b/goopdate/download_manager.h
@@ -0,0 +1,162 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_DOWNLOAD_MANAGER_H_
+#define OMAHA_GOOPDATE_DOWNLOAD_MANAGER_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+
+namespace omaha {
+
+class App;
+struct ErrorContext;
+class HttpClient;
+struct Lockable;        // TODO(omaha): make Lockable a class.
+class NetworkRequest;
+class Package;
+class PackageCache;
+
+// Public interface for the DownloadManager.
+class DownloadManagerInterface {
+ public:
+  virtual ~DownloadManagerInterface() {}
+  virtual HRESULT Initialize() = 0;
+  virtual HRESULT PurgeAppLowerVersions(const CString& app_id,
+                                        const CString& version) = 0;
+  virtual HRESULT CachePackage(const Package* package,
+                               const CString* filename_path) = 0;
+  virtual HRESULT DownloadApp(App* app) = 0;
+  virtual HRESULT DownloadPackage(Package* package) = 0;
+  virtual HRESULT GetPackage(const Package* package,
+                             const CString& dir) const = 0;
+  virtual bool IsPackageAvailable(const Package* package) const = 0;
+  virtual void Cancel(App* app) = 0;
+  virtual void CancelAll() = 0;
+  virtual bool IsBusy() const = 0;
+};
+
+class DownloadManager : public DownloadManagerInterface {
+ public:
+  explicit DownloadManager(bool is_machine);
+  virtual ~DownloadManager();
+
+  virtual HRESULT Initialize();
+
+  virtual HRESULT PurgeAppLowerVersions(const CString& app_id,
+                                        const CString& version);
+
+  virtual HRESULT CachePackage(const Package* package,
+                               const CString* filename_path);
+
+  // Downloads the specified app and stores its packages in the package cache.
+  //
+  // This is a blocking call. All errors are reported through the return value.
+  // Callers may use GetMessageForError() to convert this error value to an
+  // error message. Progress is reported via the NetworkRequestCallback
+  // method on the Package objects.
+  virtual HRESULT DownloadApp(App* app);
+
+  // Downloads the specified package and stores it in the package cache.
+  virtual HRESULT DownloadPackage(Package* package);
+
+  // Retrieves a package from the cache, if the package is locally available.
+  virtual HRESULT GetPackage(const Package* package, const CString& dir) const;
+
+  // Returns true if the specified package is in the package cache.
+  virtual bool IsPackageAvailable(const Package* package) const;
+
+  // Cancels the download of specified app and makes DownloadApp return to the
+  // caller at some point in the future. Cancel can be called multiple times
+  // until the DownloadApp returns.
+  virtual void Cancel(App* app);
+
+  // Cancels download of all apps currently downloading.
+  virtual void CancelAll();
+
+  // Returns true if applications are downloading.
+  virtual bool IsBusy() const;
+
+  // Returns a formatted message for the specified error in given language.
+  static CString GetMessageForError(const ErrorContext& error_context,
+                                    const CString& language);
+
+ private:
+  // Maintains per-app download state.
+  class State {
+   public:
+    State(App* app, NetworkRequest* network_request);
+    ~State();
+
+    App* app() const { return app_; }
+
+    NetworkRequest* network_request() const;
+
+    HRESULT CancelNetworkRequest();
+
+   private:
+    // Not owned by this object.
+    App* app_;
+
+    scoped_ptr<NetworkRequest> network_request_;
+
+    DISALLOW_EVIL_CONSTRUCTORS(State);
+  };
+
+  // Creates a download state corresponding to the app. The state object is
+  // owned by the download manager. A pointer to the state object is returned
+  // to the caller.
+  HRESULT CreateStateForApp(App* app, State** state);
+
+  HRESULT DeleteStateForApp(App* app);
+
+  HRESULT DoDownloadPackage(Package* package, State* state);
+
+  bool is_machine() const;
+
+  CString package_cache_root() const;
+
+  PackageCache* package_cache();
+  const PackageCache* package_cache() const;
+
+  const Lockable& lock() const;
+
+  // Returns the full path to a unique filename.
+  static HRESULT BuildUniqueFileName(const CString& filename,
+                                     CString* unique_filename);
+
+  // Locks shared instance state for concurrent downloads. This lock is
+  // owned by this class.
+  mutable Lockable* volatile lock_;
+
+  bool is_machine_;
+
+  // The root of the package_cache.
+  CString package_cache_root_;
+
+  std::vector<State*> download_state_;
+
+  scoped_ptr<PackageCache> package_cache_;
+
+  friend class DownloadManagerTest;
+  DISALLOW_EVIL_CONSTRUCTORS(DownloadManager);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_DOWNLOAD_MANAGER_H_
diff --git a/goopdate/download_manager_unittest.cc b/goopdate/download_manager_unittest.cc
new file mode 100644
index 0000000..29b3ef1
--- /dev/null
+++ b/goopdate/download_manager_unittest.cc
@@ -0,0 +1,1237 @@
+// Copyright 2008-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// TODO(omaha): why so many dependencies for this unit test?
+
+#include <windows.h>
+#include <atlstr.h>
+#include "omaha/base/app_util.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/signatures.h"
+#include "omaha/base/thread_pool.h"
+#include "omaha/base/timer.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/base/vista_utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/goopdate/app_state_checking_for_update.h"
+#include "omaha/goopdate/app_state_waiting_to_download.h"
+#include "omaha/goopdate/app_unittest_base.h"
+#include "omaha/goopdate/download_manager.h"
+#include "omaha/goopdate/update_response_utils.h"
+#include "omaha/testing/unit_test.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace omaha {
+
+namespace {
+
+const TCHAR kUpdateBinHash[] = _T("YF2z/br/S6E3KTca0MT7qziJN44=");
+const TCHAR kUpdateBin1Hash[] = _T("tbYInfmArVRUD62Ex292vN4LtGQ=");
+
+const TCHAR kAppGuid1[] = _T("{0B35E146-D9CB-4145-8A91-43FDCAEBCD1E}");
+const TCHAR kAppGuid2[] = _T("{C7F2B395-A01C-4806-AA07-9163F66AFC48}");
+
+
+class DownloadAppWorkItem : public UserWorkItem {
+ public:
+  DownloadAppWorkItem(DownloadManager* download_manager, App* app)
+      : download_manager_(download_manager), app_(app) {}
+
+ private:
+  virtual void DoProcess() {
+    download_manager_->DownloadApp(app_);
+  }
+
+  DownloadManager* download_manager_;
+  App* app_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(DownloadAppWorkItem);
+};
+
+}  // namespace
+
+class DownloadManagerTest : public AppTestBase {
+ public:
+  static HRESULT BuildUniqueFileName(const CString& filename,
+                                     CString* unique_filename) {
+    return DownloadManager::BuildUniqueFileName(filename,
+                                                unique_filename);
+  }
+
+ protected:
+  explicit DownloadManagerTest(bool is_machine)
+      : AppTestBase(is_machine, true) {}
+
+  virtual void SetUp() {
+    AppTestBase::SetUp();
+
+    CleanupFiles();
+
+    download_manager_.reset(new DownloadManager(is_machine_));
+    EXPECT_SUCCEEDED(download_manager_->Initialize());
+  }
+
+  virtual void TearDown() {
+    download_manager_.reset();
+    CleanupFiles();
+
+    AppTestBase::TearDown();
+  }
+
+  virtual void CleanupFiles() = 0;
+
+  static HRESULT LoadBundleFromXml(AppBundle* app_bundle,
+                                   const CStringA& buffer_string) {
+    __mutexScope(app_bundle->model()->lock());
+
+    std::vector<uint8> buffer(buffer_string.GetLength());
+    memcpy(&buffer.front(), buffer_string, buffer.size());
+
+    scoped_ptr<xml::UpdateResponse> update_response(
+        xml::UpdateResponse::Create());
+    HRESULT hr = update_response->Deserialize(buffer);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
+      hr = update_response_utils::BuildApp(update_response.get(),
+                                           S_OK,
+                                           app_bundle->GetApp(i));
+      if (FAILED(hr)) {
+        return hr;
+      }
+    }
+
+    return S_OK;
+  }
+
+  static void SetAppStateCheckingForUpdate(App* app) {
+    SetAppStateForUnitTest(app, new fsm::AppStateCheckingForUpdate);
+  }
+
+  static void SetAppStateWaitingToDownload(App* app) {
+    SetAppStateForUnitTest(app, new fsm::AppStateWaitingToDownload);
+  }
+
+  const CString cache_path_;
+  scoped_ptr<DownloadManager> download_manager_;
+};
+
+
+class DownloadManagerMachineTest : public DownloadManagerTest {
+ protected:
+  DownloadManagerMachineTest() : DownloadManagerTest(true) {}
+
+  virtual void CleanupFiles() {
+    ConfigManager* cm(ConfigManager::Instance());
+    DeleteDirectory(cm->GetMachineInstallWorkingDir());
+    DeleteDirectory(cm->GetMachineSecureDownloadStorageDir());
+  }
+};
+
+class DownloadManagerUserTest : public DownloadManagerTest {
+ protected:
+  DownloadManagerUserTest() : DownloadManagerTest(false) {}
+
+  virtual void CleanupFiles() {
+    ConfigManager* cm(ConfigManager::Instance());
+    DeleteDirectory(cm->GetUserInstallWorkingDir());
+    DeleteDirectory(cm->GetUserDownloadStorageDir());
+  }
+};
+
+TEST_F(DownloadManagerUserTest, DownloadApp_MultiplePackagesInOneApp) {
+  App* app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("App1"))));
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE));  // Allow download.
+
+  // One app, two packages.
+  CStringA buffer_string =
+
+  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+  "<response protocol=\"3.0\">"
+    "<app appid=\"{0B35E146-D9CB-4145-8A91-43FDCAEBCD1E}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"1.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"YF2z/br/S6E3KTca0MT7qziJN44=\" "
+              "name=\"UpdateData.bin\" "
+              "required=\"true\" "
+              "size=\"2048\"/>"
+            "<package "
+              "hash=\"tbYInfmArVRUD62Ex292vN4LtGQ=\" "
+              "name=\"UpdateData1.bin\" "
+              "required=\"true\" "
+              "size=\"2048\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+  "</response>";
+
+  EXPECT_HRESULT_SUCCEEDED(LoadBundleFromXml(app_bundle_.get(), buffer_string));
+  SetAppStateWaitingToDownload(app);
+
+  EXPECT_SUCCEEDED(download_manager_->DownloadApp(app));
+
+  // Tests the first package.
+  const Package* package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+  EXPECT_STREQ(_T("UpdateData.bin"), package->filename());
+  EXPECT_EQ(2048, package->expected_size());
+  EXPECT_STREQ(kUpdateBinHash, package->expected_hash());
+  EXPECT_EQ(2048, package->bytes_downloaded());
+  EXPECT_TRUE(download_manager_->IsPackageAvailable(package));
+
+  // Tests the second package.
+  package = app->next_version()->GetPackage(1);
+  ASSERT_TRUE(package);
+  EXPECT_STREQ(_T("UpdateData1.bin"), package->filename());
+  EXPECT_EQ(2048, package->expected_size());
+  EXPECT_STREQ(kUpdateBin1Hash, package->expected_hash());
+  EXPECT_EQ(2048, package->bytes_downloaded());
+  EXPECT_TRUE(download_manager_->IsPackageAvailable(package));
+}
+
+// Downloads multiple apps serially.
+TEST_F(DownloadManagerUserTest, DownloadApp_MultipleApps) {
+  App* app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("App1"))));
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE));  // Allow download.
+
+  app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid2), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("App2"))));
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE));  // Allow download.
+
+  // Two apps, one package each.
+  CStringA buffer_string =
+
+  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+  "<response protocol=\"3.0\">"
+    "<app appid=\"{0B35E146-D9CB-4145-8A91-43FDCAEBCD1E}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"1.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"YF2z/br/S6E3KTca0MT7qziJN44=\" "
+              "name=\"UpdateData.bin\" "
+              "required=\"true\" "
+              "size=\"2048\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+    "<app appid=\"{C7F2B395-A01C-4806-AA07-9163F66AFC48}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"2.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"tbYInfmArVRUD62Ex292vN4LtGQ=\" "
+              "name=\"UpdateData1.bin\" "
+              "required=\"true\" "
+              "size=\"2048\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+  "</response>";
+
+  EXPECT_HRESULT_SUCCEEDED(LoadBundleFromXml(app_bundle_.get(), buffer_string));
+
+  // Tests the first app.
+  app = app_bundle_->GetApp(0);
+  ASSERT_TRUE(app);
+  SetAppStateWaitingToDownload(app);
+  EXPECT_SUCCEEDED(download_manager_->DownloadApp(app));
+
+  const Package* package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+  EXPECT_STREQ(_T("UpdateData.bin"), package->filename());
+  EXPECT_EQ(2048, package->expected_size());
+  EXPECT_STREQ(kUpdateBinHash, package->expected_hash());
+  EXPECT_EQ(2048, package->bytes_downloaded());
+  EXPECT_TRUE(download_manager_->IsPackageAvailable(package));
+
+  // Tests the second app.
+  app = app_bundle_->GetApp(1);
+  ASSERT_TRUE(app);
+  SetAppStateWaitingToDownload(app);
+  EXPECT_SUCCEEDED(download_manager_->DownloadApp(app));
+
+  package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+  EXPECT_STREQ(_T("UpdateData1.bin"), package->filename());
+  EXPECT_EQ(2048, package->expected_size());
+  EXPECT_STREQ(kUpdateBin1Hash, package->expected_hash());
+  EXPECT_EQ(2048, package->bytes_downloaded());
+  EXPECT_TRUE(download_manager_->IsPackageAvailable(package));
+}
+
+// Downloads multiple apps concurrently. The test builds a bundle of two
+// apps, creates two thread pool work items to download the apps, waits
+// for the downloads to complete, and then checks the results of each download.
+// This is essentialy the same unit test as DownloadApp_MultipleApps done
+// concurrently instead of serially.
+TEST_F(DownloadManagerUserTest, DownloadApp_Concurrent) {
+  App* app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("App1"))));
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE));  // Allow download.
+
+  app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid2), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("App2"))));
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE));  // Allow download.
+
+  // Two apps, one package each.
+  CStringA buffer_string =
+
+  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+  "<response protocol=\"3.0\">"
+    "<app appid=\"{0B35E146-D9CB-4145-8A91-43FDCAEBCD1E}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"1.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"YF2z/br/S6E3KTca0MT7qziJN44=\" "
+              "name=\"UpdateData.bin\" "
+              "required=\"true\" "
+              "size=\"2048\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+    "<app appid=\"{C7F2B395-A01C-4806-AA07-9163F66AFC48}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"2.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"tbYInfmArVRUD62Ex292vN4LtGQ=\" "
+              "name=\"UpdateData1.bin\" "
+              "required=\"true\" "
+              "size=\"2048\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+  "</response>";
+
+  EXPECT_HRESULT_SUCCEEDED(LoadBundleFromXml(app_bundle_.get(), buffer_string));
+
+  // The thread pool waits up to 1 minute for the work items to complete when
+  // the thread pool object is destroyed.
+  const int kShutdownDelayMs = 60000;
+
+  ThreadPool thread_pool;
+  ASSERT_HRESULT_SUCCEEDED(thread_pool.Initialize(kShutdownDelayMs));
+
+  const int kNumApps = 2;
+
+  for (int i = 0; i != kNumApps; ++i) {
+    app = app_bundle_->GetApp(i);
+    SetAppStateWaitingToDownload(app);
+
+    scoped_ptr<DownloadAppWorkItem> work_item(
+        new DownloadAppWorkItem(download_manager_.get(), app));
+
+    // WT_EXECUTELONGFUNCTION causes the thread pool to use multiple threads.
+    ASSERT_HRESULT_SUCCEEDED(thread_pool.QueueUserWorkItem(
+                                 work_item.release(),
+                                 WT_EXECUTELONGFUNCTION));
+  }
+
+  // Poll the state of the download manager and wait up to 1 minute for the
+  // downloads to complete.
+  const int kTimeToWaitForDownloadsMs = 60000;
+  const int kTimeToSleepWhenPollingMs   = 10;
+
+  // Wait some time for the download manager to pick up the work items and
+  // become busy.
+  Timer timer(true);
+  while (timer.GetMilliseconds() < kTimeToWaitForDownloadsMs) {
+    if (download_manager_->IsBusy()) {
+      break;
+    }
+  }
+  timer.Reset();
+
+  // Wait for the download manager to exit its busy state.
+  timer.Start();
+  while (download_manager_->IsBusy() &&
+         timer.GetMilliseconds() < kTimeToWaitForDownloadsMs) {
+    ::Sleep(kTimeToSleepWhenPollingMs);
+  }
+
+  // Expect that downloads have completed in a reasonable time.
+  EXPECT_FALSE(download_manager_->IsBusy());
+
+  // Test the outcome of the two downloads.
+  app = app_bundle_->GetApp(0);
+  const Package* package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+  EXPECT_STREQ(_T("UpdateData.bin"), package->filename());
+  EXPECT_EQ(2048, package->expected_size());
+  EXPECT_STREQ(kUpdateBinHash, package->expected_hash());
+  EXPECT_EQ(2048, package->bytes_downloaded());
+  EXPECT_TRUE(download_manager_->IsPackageAvailable(package));
+
+  app = app_bundle_->GetApp(1);
+  package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+  EXPECT_STREQ(_T("UpdateData1.bin"), package->filename());
+  EXPECT_EQ(2048, package->expected_size());
+  EXPECT_STREQ(kUpdateBin1Hash, package->expected_hash());
+  EXPECT_EQ(2048, package->bytes_downloaded());
+  EXPECT_TRUE(download_manager_->IsPackageAvailable(package));
+
+  // Try to cancel the downloads if they could not complete in time and the
+  // download manager is still busy. The thread pool waits a while for the work
+  // items to complete after they have been canceled.
+  if (download_manager_->IsBusy()) {
+    for (int i = 0; i != kNumApps; ++i) {
+      download_manager_->Cancel(app_bundle_->GetApp(i));
+    }
+    return;
+  }
+}
+
+// Downloads multiple apps concurrently and cancels the downloads while they
+// are in progress. The test builds a bundle of two apps with one file each,
+// creates two thread pool work items to download the apps, waits for the
+// downloads to begin, and then cancels them.
+// TODO(omaha): Fix the intermittent failures.
+TEST_F(DownloadManagerUserTest, DISABLED_DownloadApp_Cancel) {
+  App* app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("App1"))));
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE));  // Allow download.
+
+  app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid2), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("App2"))));
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE));  // Allow download.
+
+  // Two apps, one package each.
+  CStringA buffer_string =
+
+  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+  "<response protocol=\"3.0\">"
+    "<app appid=\"{0B35E146-D9CB-4145-8A91-43FDCAEBCD1E}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/dl/edgedl/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"1.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"jCBqGodZn1Ms5oZ1U28LFUaQDXo=\" "
+              "name=\"UpdateData_10M.bin\" "
+              "required=\"true\" "
+              "size=\"10485760\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+    "<app appid=\"{C7F2B395-A01C-4806-AA07-9163F66AFC48}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/dl/edgedl/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"2.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"jCBqGodZn1Ms5oZ1U28LFUaQDXo=\" "
+              "name=\"UpdateData_10M.bin\" "
+              "required=\"true\" "
+              "size=\"10485760\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+  "</response>";
+
+  EXPECT_HRESULT_SUCCEEDED(LoadBundleFromXml(app_bundle_.get(), buffer_string));
+
+  // The thread pool waits up to 1 minute for the work items to complete when
+  // the thread pool object is destroyed.
+  const int kShutdownDelayMs = 60000;
+
+  ThreadPool thread_pool;
+  ASSERT_HRESULT_SUCCEEDED(thread_pool.Initialize(kShutdownDelayMs));
+
+  const int kNumApps = 2;
+
+  for (int i = 0; i != kNumApps; ++i) {
+    app = app_bundle_->GetApp(i);
+    SetAppStateWaitingToDownload(app);
+
+    scoped_ptr<DownloadAppWorkItem> work_item(
+        new DownloadAppWorkItem(download_manager_.get(), app));
+
+    ASSERT_HRESULT_SUCCEEDED(thread_pool.QueueUserWorkItem(
+                                 work_item.release(),
+                                 WT_EXECUTELONGFUNCTION));
+  }
+
+  for (int i = 0; i != kNumApps; ++i) {
+     app = app_bundle_->GetApp(i);
+     EXPECT_NE(STATE_ERROR, app->state());
+  }
+
+  // Poll the state of the download manager and wait up to 1 minute for the
+  // downloads to complete.
+  const int kTimeToWaitForDownloadsMs = 60000;
+  const int kTimeToSleepWhenPollingMs   = 10;
+
+  // Cancel the downloads as soon as all apps are downloading and
+  // wait until all apps have transitioned in the error state.
+  Timer timer(true);
+  bool is_done = false;
+  while (!is_done && timer.GetMilliseconds() < kTimeToWaitForDownloadsMs) {
+    int num_apps_downloading = 0;
+
+    for (int i = 0; i != kNumApps; ++i) {
+      app = app_bundle_->GetApp(i);
+      if (app->state() == STATE_DOWNLOADING) {
+        const Package* package = app->next_version()->GetPackage(0);
+        if (package->bytes_downloaded()) {
+          ++num_apps_downloading;
+        }
+      }
+    }
+
+    is_done = (num_apps_downloading == kNumApps);
+
+    ::Sleep(kTimeToSleepWhenPollingMs);
+  }
+
+  for (int i = 0; i != kNumApps; ++i) {
+    download_manager_->Cancel(app_bundle_->GetApp(i));
+  }
+
+  is_done = false;
+  while (!is_done && timer.GetMilliseconds() < kTimeToWaitForDownloadsMs) {
+    int num_apps_cancelled = 0;
+    for (int i = 0; i != kNumApps; ++i) {
+      app = app_bundle_->GetApp(i);
+      if (app->state() == STATE_ERROR) {
+        ++num_apps_cancelled;
+      }
+    }
+
+    is_done = (num_apps_cancelled == kNumApps);
+
+    ::Sleep(kTimeToSleepWhenPollingMs);
+  }
+
+  for (int i = 0; i != kNumApps; ++i) {
+    // Check the state of the app and the package after the cancel call.
+    app = app_bundle_->GetApp(i);
+    EXPECT_EQ(STATE_ERROR, app->state());
+
+    const Package* package = app->next_version()->GetPackage(0);
+    ASSERT_TRUE(package);
+    EXPECT_LT(0, package->bytes_downloaded());
+    VARIANT_BOOL is_available(false);
+    EXPECT_HRESULT_SUCCEEDED(package->get_isAvailable(&is_available));
+    EXPECT_FALSE(is_available);
+
+    // Check CurrentAppState members.
+    CComPtr<IDispatch> current_state_disp;
+    EXPECT_HRESULT_SUCCEEDED(app->get_currentState(&current_state_disp));
+    CComPtr<ICurrentState> current_state;
+    EXPECT_HRESULT_SUCCEEDED(
+        current_state_disp->QueryInterface(&current_state));
+
+    LONG state_value = 0;
+    EXPECT_HRESULT_SUCCEEDED(current_state->get_stateValue(&state_value));
+    EXPECT_EQ(STATE_ERROR, static_cast<CurrentState>(state_value));
+
+    LONG error_code = 0;
+    EXPECT_HRESULT_SUCCEEDED(current_state->get_errorCode(&error_code));
+    EXPECT_EQ(GOOPDATE_E_CANCELLED, error_code);
+
+    LONG extra_code1 = 0;
+    EXPECT_HRESULT_SUCCEEDED(current_state->get_errorCode(&extra_code1));
+    EXPECT_EQ(0, extra_code1);
+  }
+}
+
+// Common packages of different apps are not cached by the package cache and
+// will be redownloaded until the network cache is implemented.
+// TODO(omaha): fix unit test as soon as the network cache is implemented.
+TEST_F(DownloadManagerUserTest, DownloadApp_MultipleAppsCommonPackage) {
+  App* app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("App1"))));
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE));  // Allow download.
+
+  app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid2), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("App2"))));
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE));  // Allow download.
+
+  // Two apps, same package each.
+  CStringA buffer_string =
+
+  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+  "<response protocol=\"3.0\">"
+    "<app appid=\"{0B35E146-D9CB-4145-8A91-43FDCAEBCD1E}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"1.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"YF2z/br/S6E3KTca0MT7qziJN44=\" "
+              "name=\"UpdateData.bin\" "
+              "required=\"true\" "
+              "size=\"2048\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+    "<app appid=\"{C7F2B395-A01C-4806-AA07-9163F66AFC48}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"2.0\">"
+          "<packages>"
+          "<package "
+            "hash=\"YF2z/br/S6E3KTca0MT7qziJN44=\" "
+            "name=\"UpdateData.bin\" "
+            "required=\"true\" "
+            "size=\"2048\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+  "</response>";
+
+  EXPECT_HRESULT_SUCCEEDED(LoadBundleFromXml(app_bundle_.get(), buffer_string));
+
+  // Tests the first app.
+  app = app_bundle_->GetApp(0);
+  ASSERT_TRUE(app);
+  SetAppStateWaitingToDownload(app);
+  EXPECT_SUCCEEDED(download_manager_->DownloadApp(app));
+
+  const Package* package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+  EXPECT_STREQ(_T("UpdateData.bin"), package->filename());
+  EXPECT_EQ(2048, package->expected_size());
+  EXPECT_STREQ(kUpdateBinHash, package->expected_hash());
+  EXPECT_EQ(2048, package->bytes_downloaded());
+  EXPECT_TRUE(download_manager_->IsPackageAvailable(package));
+
+  // Tests the second app. The package is redownloaded.
+  app = app_bundle_->GetApp(1);
+  ASSERT_TRUE(app);
+  SetAppStateWaitingToDownload(app);
+  EXPECT_SUCCEEDED(download_manager_->DownloadApp(app));
+
+  package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+  EXPECT_STREQ(_T("UpdateData.bin"), package->filename());
+  EXPECT_EQ(2048, package->expected_size());
+  EXPECT_STREQ(kUpdateBinHash, package->expected_hash());
+  EXPECT_EQ(2048, package->bytes_downloaded());
+  EXPECT_TRUE(download_manager_->IsPackageAvailable(package));
+}
+
+// Creates two bundles with the same app. The package corresponding to the
+// app in the second bundle must come from the cache.
+TEST_F(DownloadManagerUserTest, DownloadApp_FileAlreadyInCache) {
+  App* app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("App1"))));
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE));  // Allow download.
+
+  CStringA buffer_string =
+
+  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+  "<response protocol=\"3.0\">"
+    "<app appid=\"{0B35E146-D9CB-4145-8A91-43FDCAEBCD1E}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"1.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"YF2z/br/S6E3KTca0MT7qziJN44=\" "
+              "name=\"UpdateData.bin\" "
+              "required=\"true\" "
+              "size=\"2048\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+  "</response>";
+
+  EXPECT_HRESULT_SUCCEEDED(LoadBundleFromXml(app_bundle_.get(), buffer_string));
+  SetAppStateWaitingToDownload(app);
+
+  EXPECT_SUCCEEDED(download_manager_->DownloadApp(app));
+
+  // Tests the package and the bytes downloaded.
+  const Package* package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+  EXPECT_STREQ(_T("UpdateData.bin"), package->filename());
+  EXPECT_EQ(2048, package->expected_size());
+  EXPECT_STREQ(kUpdateBinHash, package->expected_hash());
+  EXPECT_EQ(2048, package->bytes_downloaded());
+  EXPECT_TRUE(download_manager_->IsPackageAvailable(package));
+
+  // Create the second app bundle.
+  shared_ptr<AppBundle> app_bundle2(model_->CreateAppBundle(false));
+  EXPECT_SUCCEEDED(app_bundle2->put_displayName(CComBSTR(_T("My Bundle"))));
+  EXPECT_SUCCEEDED(app_bundle2->put_displayLanguage(CComBSTR(_T("en"))));
+  EXPECT_SUCCEEDED(app_bundle2->initialize());
+
+  app = NULL;
+  ASSERT_SUCCEEDED(app_bundle2->createApp(CComBSTR(kAppGuid1), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("App1"))));
+  // Since the package is cached, it does not matter if the EULA is accepted.
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_FALSE));
+
+  EXPECT_HRESULT_SUCCEEDED(LoadBundleFromXml(app_bundle2.get(), buffer_string));
+  SetAppStateWaitingToDownload(app);
+
+  EXPECT_SUCCEEDED(download_manager_->DownloadApp(app));
+
+  // Tests the package and the bytes downloaded.
+  package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+  EXPECT_STREQ(_T("UpdateData.bin"), package->filename());
+  EXPECT_EQ(2048, package->expected_size());
+  EXPECT_STREQ(kUpdateBinHash, package->expected_hash());
+
+  // No bytes are downloaded if the package has been cached already.
+  EXPECT_EQ(0, package->bytes_downloaded());
+  EXPECT_TRUE(download_manager_->IsPackageAvailable(package));
+}
+
+TEST_F(DownloadManagerUserTest, DownloadApp_404) {
+  App* app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("404 Test"))));
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE));  // Allow download.
+
+  CStringA buffer_string =
+
+  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+  "<response protocol=\"3.0\">"
+    "<app appid=\"{0B35E146-D9CB-4145-8A91-43FDCAEBCD1E}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"1.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"YF2z/br/S6E3KTca0MT7qziJN44=\" "
+              "name=\"NoSuchFile-OmahaTest.exe\" "
+              "required=\"true\" "
+              "size=\"2048\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+  "</response>";
+
+  EXPECT_HRESULT_SUCCEEDED(LoadBundleFromXml(app_bundle_.get(), buffer_string));
+  SetAppStateWaitingToDownload(app);
+
+  EXPECT_EQ(GOOPDATE_E_NETWORK_FIRST + 404,
+            download_manager_->DownloadApp(app));
+
+  const Package* package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+  EXPECT_STREQ(_T("NoSuchFile-OmahaTest.exe"), package->filename());
+  EXPECT_EQ(2048, package->expected_size());
+  EXPECT_STREQ(kUpdateBinHash, package->expected_hash());
+  EXPECT_EQ(0, package->bytes_downloaded());
+  EXPECT_FALSE(download_manager_->IsPackageAvailable(package));
+}
+
+TEST_F(DownloadManagerUserTest, DownloadApp_HashFailure) {
+  App* app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("Hash Fail"))));
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE));  // Allow download.
+
+  // Provides the wrong hash for the package.
+  CStringA buffer_string =
+
+  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+  "<response protocol=\"3.0\">"
+    "<app appid=\"{0B35E146-D9CB-4145-8A91-43FDCAEBCD1E}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"1.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"tbYInfmArVRUD62Ex292vN4LtGQ=\" "
+              "name=\"UpdateData.bin\" "
+              "required=\"true\" "
+              "size=\"2048\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+  "</response>";
+
+  EXPECT_HRESULT_SUCCEEDED(LoadBundleFromXml(app_bundle_.get(), buffer_string));
+  SetAppStateWaitingToDownload(app);
+
+  EXPECT_EQ(SIGS_E_INVALID_SIGNATURE,
+            download_manager_->DownloadApp(app));
+
+  const Package* package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+  EXPECT_STREQ(_T("UpdateData.bin"), package->filename());
+  EXPECT_EQ(2048, package->expected_size());
+  EXPECT_STREQ(kUpdateBin1Hash, package->expected_hash());
+  EXPECT_EQ(2048, package->bytes_downloaded());
+  EXPECT_FALSE(download_manager_->IsPackageAvailable(package));
+
+  // All bytes were downloaded even if the validation of the file has failed.
+  EXPECT_EQ(2048, package->bytes_downloaded());
+}
+
+TEST_F(DownloadManagerUserTest, DownloadApp_HashFailure_ActualSmaller) {
+  App* app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("Hash Fail"))));
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE));  // Allow download.
+
+  // Provides the wrong hash for the package.
+  CStringA buffer_string =
+
+  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+  "<response protocol=\"3.0\">"
+    "<app appid=\"{0B35E146-D9CB-4145-8A91-43FDCAEBCD1E}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"1.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"tbYInfmArVRUD62Ex292vN4LtGQ=\" "
+              "name=\"UpdateData.bin\" "
+              "required=\"true\" "
+              "size=\"2048000\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+  "</response>";
+
+  EXPECT_HRESULT_SUCCEEDED(LoadBundleFromXml(app_bundle_.get(), buffer_string));
+  SetAppStateWaitingToDownload(app);
+
+  EXPECT_EQ(GOOPDATEDOWNLOAD_E_FILE_SIZE_SMALLER,
+            download_manager_->DownloadApp(app));
+
+  const Package* package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+  EXPECT_STREQ(_T("UpdateData.bin"), package->filename());
+  EXPECT_EQ(2048000, package->expected_size());
+  EXPECT_STREQ(kUpdateBin1Hash, package->expected_hash());
+  EXPECT_EQ(2048, package->bytes_downloaded());
+  EXPECT_FALSE(download_manager_->IsPackageAvailable(package));
+
+  // Actual bytes were downloaded even if the validation of the file has failed.
+  EXPECT_EQ(2048, package->bytes_downloaded());
+}
+
+TEST_F(DownloadManagerUserTest, DownloadApp_HashFailure_ActualLarger) {
+  App* app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("Hash Fail"))));
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE));  // Allow download.
+
+  // Provides the wrong hash for the package.
+  CStringA buffer_string =
+
+  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+  "<response protocol=\"3.0\">"
+    "<app appid=\"{0B35E146-D9CB-4145-8A91-43FDCAEBCD1E}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"1.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"tbYInfmArVRUD62Ex292vN4LtGQ=\" "
+              "name=\"UpdateData.bin\" "
+              "required=\"true\" "
+              "size=\"20\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+  "</response>";
+
+  EXPECT_HRESULT_SUCCEEDED(LoadBundleFromXml(app_bundle_.get(), buffer_string));
+  SetAppStateWaitingToDownload(app);
+
+  EXPECT_EQ(GOOPDATEDOWNLOAD_E_FILE_SIZE_LARGER,
+            download_manager_->DownloadApp(app));
+
+  const Package* package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+  EXPECT_STREQ(_T("UpdateData.bin"), package->filename());
+  EXPECT_EQ(20, package->expected_size());
+  EXPECT_STREQ(kUpdateBin1Hash, package->expected_hash());
+  EXPECT_EQ(2048, package->bytes_downloaded());
+  EXPECT_FALSE(download_manager_->IsPackageAvailable(package));
+
+  // Actual bytes were downloaded even if the validation of the file has failed
+  // or expected a smaller file.
+  EXPECT_EQ(2048, package->bytes_downloaded());
+}
+
+TEST_F(DownloadManagerUserTest, DownloadApp_BaseUrlFallback) {
+  App* app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("Hash Fail"))));
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE));  // Allow download.
+
+  // Provides the wrong hash for the package.
+  CStringA buffer_string =
+
+  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+  "<response protocol=\"3.0\">"
+    "<app appid=\"{0B35E146-D9CB-4145-8A91-43FDCAEBCD1E}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/update2/TEST_NOT_EXIST/\"/>"
+          "<url codebase=\"http://dl.google.com/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"1.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"YF2z/br/S6E3KTca0MT7qziJN44=\" "
+              "name=\"UpdateData.bin\" "
+              "required=\"true\" "
+              "size=\"2048\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+  "</response>";
+
+  EXPECT_HRESULT_SUCCEEDED(LoadBundleFromXml(app_bundle_.get(), buffer_string));
+  SetAppStateWaitingToDownload(app);
+
+  EXPECT_SUCCEEDED(download_manager_->DownloadApp(app));
+  const Package* package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+  EXPECT_STREQ(_T("UpdateData.bin"), package->filename());
+  EXPECT_EQ(2048, package->expected_size());
+  EXPECT_STREQ(kUpdateBinHash, package->expected_hash());
+  EXPECT_EQ(2048, package->bytes_downloaded());
+  EXPECT_TRUE(download_manager_->IsPackageAvailable(package));
+}
+
+TEST_F(DownloadManagerUserTest, DownloadApp_FallbackToNextUrlIfCachingFails) {
+  App* app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app));
+  EXPECT_SUCCEEDED(
+      app->put_displayName(CComBSTR(_T("Hash Fails For First Url"))));
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE));  // Allow download.
+
+  // First URL points to a corrupted file.
+  CStringA buffer_string =
+
+  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+  "<response protocol=\"3.0\">"
+    "<app appid=\"{0B35E146-D9CB-4145-8A91-43FDCAEBCD1E}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/update2/test/fakedata/\"/>"
+          "<url codebase=\"http://dl.google.com/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"1.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"YF2z/br/S6E3KTca0MT7qziJN44=\" "
+              "name=\"UpdateData.bin\" "
+              "required=\"true\" "
+              "size=\"2048\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+  "</response>";
+
+  EXPECT_HRESULT_SUCCEEDED(LoadBundleFromXml(app_bundle_.get(), buffer_string));
+  SetAppStateWaitingToDownload(app);
+
+  EXPECT_SUCCEEDED(download_manager_->DownloadApp(app));
+  const Package* package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+  EXPECT_STREQ(_T("UpdateData.bin"), package->filename());
+  EXPECT_EQ(2048, package->expected_size());
+  EXPECT_STREQ(kUpdateBinHash, package->expected_hash());
+  EXPECT_EQ(2048, package->bytes_downloaded());
+  EXPECT_TRUE(download_manager_->IsPackageAvailable(package));
+}
+
+TEST_F(DownloadManagerUserTest, DownloadApp_EulaNotAccepted) {
+  App* app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("App1"))));
+
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_FALSE));
+
+  CStringA buffer_string =
+
+  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+  "<response protocol=\"3.0\">"
+    "<app appid=\"{0B35E146-D9CB-4145-8A91-43FDCAEBCD1E}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"1.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"YF2z/br/S6E3KTca0MT7qziJN44=\" "
+              "name=\"UpdateData.bin\" "
+              "required=\"true\" "
+              "size=\"2048\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+  "</response>";
+
+  EXPECT_HRESULT_SUCCEEDED(LoadBundleFromXml(app_bundle_.get(), buffer_string));
+  SetAppStateWaitingToDownload(app);
+
+  ExpectAsserts expect_asserts;  // Eula not accepted causes asserts.
+
+  EXPECT_EQ(GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED,
+            download_manager_->DownloadApp(app));
+
+  const Package* package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+  EXPECT_STREQ(_T("UpdateData.bin"), package->filename());
+  EXPECT_EQ(2048, package->expected_size());
+  EXPECT_STREQ(kUpdateBinHash, package->expected_hash());
+  EXPECT_EQ(0, package->bytes_downloaded());
+  EXPECT_FALSE(download_manager_->IsPackageAvailable(package));
+}
+
+TEST_F(DownloadManagerUserTest, GetPackage) {
+  App* app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("App1"))));
+  EXPECT_SUCCEEDED(app->put_isEulaAccepted(VARIANT_TRUE));  // Allow download.
+
+  // One app, one package.
+  CStringA buffer_string =
+
+  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+  "<response protocol=\"3.0\">"
+    "<app appid=\"{0B35E146-D9CB-4145-8A91-43FDCAEBCD1E}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"1.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"YF2z/br/S6E3KTca0MT7qziJN44=\" "
+              "name=\"UpdateData.bin\" "
+              "required=\"true\" "
+              "size=\"2048\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+  "</response>";
+
+  EXPECT_HRESULT_SUCCEEDED(LoadBundleFromXml(app_bundle_.get(), buffer_string));
+  SetAppStateWaitingToDownload(app);
+
+  const Package* package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+
+  EXPECT_FALSE(download_manager_->IsPackageAvailable(package));
+  EXPECT_SUCCEEDED(download_manager_->DownloadApp(app));
+  EXPECT_TRUE(download_manager_->IsPackageAvailable(package));
+
+  // Get a unique temp dir name. The directory is not created.
+  CString dir(GetUniqueTempDirectoryName());
+
+  // The call fails if the package destination directory does not exist.
+  EXPECT_FAILED(download_manager_->GetPackage(package, dir));
+
+  EXPECT_SUCCEEDED(CreateDir(dir, NULL));
+  EXPECT_SUCCEEDED(download_manager_->GetPackage(package, dir));
+
+  CString filename(ConcatenatePath(dir, package->filename()));
+  std::vector<CString> files;
+  files.push_back(filename);
+  EXPECT_SUCCEEDED(AuthenticateFiles(files, kUpdateBinHash));
+
+  // Getting the package the second time overwrites the destination file
+  // and succeeds.
+  EXPECT_SUCCEEDED(download_manager_->GetPackage(package, dir));
+
+  EXPECT_SUCCEEDED(DeleteDirectory(dir));
+}
+
+TEST_F(DownloadManagerUserTest, GetPackage_NotPresent) {
+  App* app = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppGuid1), &app));
+  EXPECT_SUCCEEDED(app->put_displayName(CComBSTR(_T("App1"))));
+
+  // One app, one package.
+  CStringA buffer_string =
+
+  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+  "<response protocol=\"3.0\">"
+    "<app appid=\"{0B35E146-D9CB-4145-8A91-43FDCAEBCD1E}\" status=\"ok\">"
+      "<updatecheck status=\"ok\">"
+        "<urls>"
+          "<url codebase=\"http://dl.google.com/update2/\"/>"
+        "</urls>"
+        "<manifest version=\"1.0\">"
+          "<packages>"
+            "<package "
+              "hash=\"YF2z/br/S6E3KTca0MT7qziJN44=\" "
+              "name=\"UpdateData.bin\" "
+              "required=\"true\" "
+              "size=\"2048\"/>"
+          "</packages>"
+        "</manifest>"
+      "</updatecheck>"
+    "</app>"
+  "</response>";
+
+  EXPECT_HRESULT_SUCCEEDED(LoadBundleFromXml(app_bundle_.get(), buffer_string));
+  SetAppStateWaitingToDownload(app);
+
+  const Package* package = app->next_version()->GetPackage(0);
+  ASSERT_TRUE(package);
+
+  EXPECT_FALSE(download_manager_->IsPackageAvailable(package));
+
+  // Get a unique temp dir name. The directory is not created.
+  CString dir(GetUniqueTempDirectoryName());
+
+  EXPECT_SUCCEEDED(CreateDir(dir, NULL));
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            download_manager_->GetPackage(package, dir));
+
+  EXPECT_SUCCEEDED(DeleteDirectory(dir));
+}
+
+TEST(DownloadManagerTest, BuildUniqueFileName) {
+  CString file1, file2;
+  EXPECT_SUCCEEDED(DownloadManagerTest::BuildUniqueFileName(_T("a"), &file1));
+  EXPECT_SUCCEEDED(DownloadManagerTest::BuildUniqueFileName(_T("a"), &file2));
+  EXPECT_STRNE(file1, file2);
+}
+
+TEST(DownloadManagerTest, GetMessageForError) {
+  const TCHAR* kEnglish = _T("en");
+  EXPECT_SUCCEEDED(ResourceManager::Create(
+      true, app_util::GetCurrentModuleDirectory(), kEnglish));
+
+  EXPECT_STREQ(
+      _T("The installer could not connect to the Internet. Ensure that your ")
+      _T("computer is connected to the Internet and your firewall allows ")
+      _T("GoogleUpdate.exe to connect then try again."),
+      DownloadManager::GetMessageForError(
+          ErrorContext(GOOPDATE_E_NO_NETWORK), kEnglish));
+
+  EXPECT_STREQ(
+      _T("The installer could not connect to the Internet because of ")
+      _T("an HTTP 401 Unauthorized response. This is likely a proxy ")
+      _T("configuration issue. Please configure the proxy server to allow ")
+      _T("network access and try again or contact your network administrator."),
+      DownloadManager::GetMessageForError(
+          ErrorContext(GOOPDATE_E_NETWORK_UNAUTHORIZED), kEnglish));
+
+  EXPECT_STREQ(
+      _T("The installer could not connect to the Internet because of ")
+      _T("an HTTP 403 Forbidden response. This is likely a proxy ")
+      _T("configuration issue. Please configure the proxy server to allow ")
+      _T("network access and try again or contact your network administrator."),
+      DownloadManager::GetMessageForError(
+          ErrorContext(GOOPDATE_E_NETWORK_FORBIDDEN), kEnglish));
+
+  EXPECT_STREQ(
+      _T("The installer could not connect to the Internet because a ")
+      _T("proxy server required user authentication. Please configure the ")
+      _T("proxy server to allow network access and try again or contact your ")
+      _T("network administrator."),
+      DownloadManager::GetMessageForError(
+          ErrorContext(GOOPDATE_E_NETWORK_PROXYAUTHREQUIRED),
+          kEnglish));
+
+  EXPECT_STREQ(
+      _T("The download failed."),
+      DownloadManager::GetMessageForError(ErrorContext(E_FAIL), kEnglish));
+
+  EXPECT_STREQ(
+      _T("Failed to cache the downloaded installer. Error: 0x80070005."),
+      DownloadManager::GetMessageForError(
+          ErrorContext(GOOPDATEDOWNLOAD_E_CACHING_FAILED, 0x80070005),
+          kEnglish));
+
+  ResourceManager::Delete();
+}
+
+}  // namespace omaha
diff --git a/goopdate/elevation_moniker_resource.h b/goopdate/elevation_moniker_resource.h
new file mode 100644
index 0000000..89136c4
--- /dev/null
+++ b/goopdate/elevation_moniker_resource.h
@@ -0,0 +1,23 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_ELEVATION_MONIKER_RESOURCE_H_
+#define OMAHA_GOOPDATE_ELEVATION_MONIKER_RESOURCE_H_
+
+#include "omaha/goopdate/resources/goopdate_dll/goopdate_dll.grh"
+
+#include "omaha/goopdate/non_localized_resource.h"
+
+#endif  // OMAHA_GOOPDATE_ELEVATION_MONIKER_RESOURCE_H_
diff --git a/goopdate/event_logger.cc b/goopdate/event_logger.cc
deleted file mode 100644
index ad03c35..0000000
--- a/goopdate/event_logger.cc
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include "omaha/goopdate/event_logger.h"
-
-#include <sddl.h>
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/user_info.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/resource_manager.h"
-
-namespace omaha {
-
-void LogEventHelper(WORD type, DWORD id, size_t count, const TCHAR** strings,
-                    const TCHAR* ctx) {
-  ASSERT1(count <= kint16max);
-  if (!ConfigManager::Instance()->CanLogEvents(type)) {
-    return;
-  }
-
-  // Include the circular logging buffer in the event log if the type is a
-  // warning or an error.
-  CStringA data(ctx);
-  CString context = GetLogging()->GetHistory();
-  if (!context.IsEmpty()) {
-    data.AppendFormat("\n[More context: %S]", context);
-  }
-
-  HRESULT hr = EventLogger::ReportEvent(EventLogger::kSourceName,
-                                        type,
-                                        EventLogger::kDefaultCategory,
-                                        id,
-                                        static_cast<WORD>(count),
-                                        strings,
-                                        data.GetLength(),
-                                        data.GetBuffer());
-  if (FAILED(hr)) {
-    CORE_LOG(LW, (_T("[Failed to log event][0x%08x]"), hr));
-  }
-}
-
-CString BuildEventSourceRegistryKeyName(const TCHAR* src_name) {
-  ASSERT1(src_name);
-  CString key_name;
-  key_name.Format(_T("HKLM\\SYSTEM\\CurrentControlSet\\Services\\EventLog\\")
-                  _T("Application\\%s"),
-                  src_name);
-  return key_name;
-}
-
-HRESULT EventLogger::AddEventSource(const TCHAR* src_name,
-                                    const TCHAR* msg_dll_path) {
-  ASSERT1(src_name);
-  if (!src_name) return E_INVALIDARG;
-  ASSERT1(msg_dll_path);
-  if (!msg_dll_path) return E_INVALIDARG;
-
-  // Create the event source as a subkey of the "Application" log.
-  RegKey reg_key;
-  HRESULT hr = reg_key.Create(BuildEventSourceRegistryKeyName(src_name));
-  if (FAILED(hr)) return hr;
-
-  // Set the name of the message file. RegKey class can't set REG_EXPAND_SZ
-  // values so we must use the low level OS call.
-  int result = ::RegSetValueEx(reg_key.Key(),
-                               _T("EventMessageFile"),
-                               0,
-                               REG_EXPAND_SZ,
-                               reinterpret_cast<const byte*>(msg_dll_path),
-                               (_tcslen(msg_dll_path) + 1) * sizeof(TCHAR));
-  if (result != ERROR_SUCCESS) return HRESULT_FROM_WIN32(result);
-
-  // Set the supported event types.
-  DWORD types = EVENTLOG_ERROR_TYPE |
-                EVENTLOG_WARNING_TYPE |
-                EVENTLOG_INFORMATION_TYPE;
-  hr = reg_key.SetValue(_T("TypesSupported"), types);
-  if (FAILED(hr)) return hr;
-
-  return S_OK;
-}
-
-HRESULT EventLogger::RemoveEventSource(const TCHAR* src_name) {
-  ASSERT1(src_name);
-  if (!src_name) return E_INVALIDARG;
-
-  // RegKey::DeleteKey  returns S_FALSE when attempting to delete
-  // a key that is not there.
-  HRESULT hr = RegKey::DeleteKey(BuildEventSourceRegistryKeyName(src_name),
-                                 false);
-  return SUCCEEDED(hr) ? S_OK : hr;
-}
-
-
-HRESULT EventLogger::ReportEvent(const TCHAR* src_name,
-                                 WORD type,
-                                 WORD category,
-                                 DWORD id,
-                                 WORD count,
-                                 const TCHAR** strings,
-                                 size_t buf_size,
-                                 void* buffer) {
-  ASSERT1(src_name);
-  ASSERT1(type == EVENTLOG_SUCCESS ||
-          type == EVENTLOG_ERROR_TYPE ||
-          type == EVENTLOG_WARNING_TYPE ||
-          type == EVENTLOG_INFORMATION_TYPE);
-
-  //  Opens the log on the local computer.
-  HANDLE hlog = ::RegisterEventSource(NULL, src_name);
-  if (!hlog) {
-    return HRESULTFromLastError();
-  }
-
-  // Best effort to get the sid for the current user. The event logging
-  // provides for logging the sid at no cost so that the user shows up
-  // in the event log.
-  CString sid_string;
-  VERIFY1(SUCCEEDED(user_info::GetCurrentUser(NULL, NULL, &sid_string)));
-  PSID psid = NULL;
-  if (!sid_string.IsEmpty()) {
-    VERIFY1(::ConvertStringSidToSid(sid_string, &psid));
-    ASSERT1(psid);
-  }
-
-  HRESULT hr = E_FAIL;
-  if (::ReportEvent(hlog,       // Event log handle.
-                    type,       // Event type.
-                    category,   // Event category.
-                    id,         // Event identifier.
-                    psid,       // User security identifier.
-                    count,      // Number of substitution strings.
-                    buf_size,   // Size of binary data.
-                    strings,    // Pointer to strings.
-                    buffer)) {  // Binary data.
-    hr = S_OK;
-  } else {
-    hr = HRESULTFromLastError();
-  }
-
-  ::LocalFree(psid);
-  VERIFY1(::DeregisterEventSource(hlog));
-  return hr;
-}
-
-// TODO(omaha): When we do i18n later on, decide if the string below needs
-// translation or not. On one hand this string makes it to the event viewer, on
-// the other hand the same string is used to register an event log for an
-// application in registry. We do not expect the mapping to change when the user
-// changes languages, however we may decide to do so.
-const TCHAR* const EventLogger::kSourceName = _T("Google Update");
-
-void GoogleUpdateLogEvent::WriteEvent() {
-  ASSERT1(!event_desc_.IsEmpty());
-  ASSERT1(type_ != 0);
-  ASSERT1(id_ != 0);
-
-  DWORD pid(::GetCurrentProcessId());
-  CString ver;
-  goopdate_utils::GetVerFromRegistry(is_machine_, kGoogleUpdateAppId, &ver);
-
-  CString lang = ResourceManager::GetDefaultUserLanguage();
-
-  const ConfigManager& cm = *ConfigManager::Instance();
-  CString msg;
-  msg.Format(_T("\n%s.\npid=%d, ver=%s, lang=%s, machine=%d, extern=%d"),
-             event_desc_, pid, ver, lang, is_machine_, !cm.IsGoogler());
-#if DEBUG
-  msg.Append(_T(", debug"));
-#endif
-#if !OFFICIAL_BUILD
-  msg.Append(_T(", private"));
-#endif
-
-  if (!event_text_.IsEmpty()) {
-    msg.AppendFormat(_T("\n%s"), event_text_);
-  }
-
-  LogEvent(static_cast<WORD>(type_), id_, msg);
-}
-
-
-}  // namespace omaha
-
diff --git a/goopdate/event_logger.h b/goopdate/event_logger.h
deleted file mode 100644
index a8fd394..0000000
--- a/goopdate/event_logger.h
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Event Logger provides a simple mechanism to log events to Windows
-// Event Log. A few overloads are defined to simplify logging by reducing
-// the number of parameters that must be provided. The overloads are
-// implemented in terms of the EventLogger class.
-//
-// The event logging works in both debug and optimized builds. This is not
-// a substitute for the debug log. Instead it is a way to provide some level
-// of transparency into what Google Update is doing at runtime and to help
-// diagnosing end user issues.
-//
-// Familiarity with Windows Event Log is helpful in understanding how
-// these wrappers are to be used. Windows Event Log uses localized strings
-// in a message file and it substitutes string insterts that correspond to
-// formatting characters in the message string. In addtion, the log is able
-// to record raw data, herein provided by a context string, which may be
-// useful to provide some context around the formatted message.
-
-// TODO(omaha): Provide some control for the verbosity level in the log.
-// TODO(omaha): Perhaps there is a better way to define the overloaded
-// wrappers below. I chose a compromise between the easy of use while not
-// mixing up different string parameters that have different meanings.
-
-#ifndef OMAHA_GOOPDATE_EVENT_LOGGER_H__
-#define OMAHA_GOOPDATE_EVENT_LOGGER_H__
-
-#include <atlstr.h>
-#include "base/basictypes.h"
-
-namespace omaha {
-
-void LogEventHelper(WORD type, DWORD id, size_t count, const TCHAR** strings,
-                    const TCHAR* ctx);
-
-// Logs an event to the Application log
-inline void LogEvent(WORD type, DWORD id) {
-  LogEventHelper(type, id, 0, NULL, NULL);
-}
-
-inline void LogEvent(WORD type, DWORD id, const TCHAR* s) {
-  const TCHAR* strings[] = {s};
-  LogEventHelper(type, id, arraysize(strings), strings, NULL);
-}
-
-inline void LogEvent(WORD type, DWORD id, const TCHAR* s1, const TCHAR* s2) {
-  const TCHAR* strings[] = {s1, s2};
-  LogEventHelper(type, id, arraysize(strings), strings, NULL);
-}
-
-inline void LogEvent(WORD type, DWORD id, const TCHAR* s1, const TCHAR* s2,
-                     const TCHAR* s3) {
-  const TCHAR* strings[] = {s1, s2, s3};
-  LogEventHelper(type, id, arraysize(strings), strings, NULL);
-}
-
-// Logs an event to the Application log with a context string.
-inline void LogEventContext(WORD type, DWORD id, const TCHAR* ctx) {
-  LogEventHelper(type, id, 0, NULL, ctx);
-}
-
-inline void LogEventContext(WORD type, DWORD id, const TCHAR* s,
-                            const TCHAR* ctx) {
-  const TCHAR* strings[] = {s};
-  LogEventHelper(type, id, arraysize(strings), strings, ctx);
-}
-
-inline void LogEventContext(WORD type, DWORD id, const TCHAR* s1,
-                            const TCHAR* s2, const TCHAR* ctx) {
-  const TCHAR* strings[] = {s1, s2};
-  LogEventHelper(type, id, arraysize(strings), strings, ctx);
-}
-
-inline void LogEventContext(WORD type, DWORD id, const TCHAR* s1,
-                            const TCHAR* s2, const TCHAR* s3,
-                            const TCHAR* ctx) {
-  const TCHAR* strings[] = {s1, s2, s3};
-  LogEventHelper(type, id, arraysize(strings), strings, ctx);
-}
-
-class EventLogger {
- public:
-  // Creates an event source for the "Application" log so that EventViewer can
-  // map event identifier codes to message strings.
-  static HRESULT AddEventSource(
-      const TCHAR* src_name,       // Event source name.
-      const TCHAR* msg_dll_path);  // Path for message DLL.
-
-  static HRESULT RemoveEventSource(
-      const TCHAR* src_name);      // Event source name.
-
-  // Writes an entry at the end of event log that contains the source name.
-  static HRESULT ReportEvent(
-      const TCHAR* src_name,       // Event source name.
-      WORD type,                   // Type of the event to be logged.
-      WORD category,               // Event category.
-      DWORD id,                    // Event identifier.
-      WORD count,                  // Count of insert strings.
-      const TCHAR** strings,       // Insert strings.
-      size_t buf_size,             // Size of binary data to append.
-      void* buffer);               // Buffer containing the binary data.
-
-  // Default name for the event source.
-  static const TCHAR* const kSourceName;
-
-  // Default event category.
-  static const WORD kDefaultCategory = 0;
-};
-
-class GoogleUpdateLogEvent {
- public:
-  GoogleUpdateLogEvent(int type, int id, bool is_machine)
-      : type_(type),
-        id_(id),
-        is_machine_(is_machine) {}
-  GoogleUpdateLogEvent() : type_(0), id_(0), is_machine_(false) {}
-  ~GoogleUpdateLogEvent() {}
-  void WriteEvent();
-  void set_event_desc(const CString& desc) { event_desc_ = desc; }
-  void set_event_text(const CString& text) { event_text_ = text; }
-
- private:
-  CString event_desc_;
-  CString event_text_;
-  int type_;
-  int id_;
-  bool is_machine_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(GoogleUpdateLogEvent);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_EVENT_LOGGER_H__
-
diff --git a/goopdate/event_logger_unittest.cc b/goopdate/event_logger_unittest.cc
deleted file mode 100644
index 4a080f4..0000000
--- a/goopdate/event_logger_unittest.cc
+++ /dev/null
@@ -1,207 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include <stdio.h>
-#include <stdarg.h>
-
-#include "base/basictypes.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/error.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/event_logger.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-class EventLoggerTest : public testing::Test {
- protected:
-  virtual void SetUp() {
-    EXPECT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
-    OverrideRegistryHives(kRegistryHiveOverrideRoot);
-
-    // Enable logging of events.
-    DWORD log_events = LOG_EVENT_LEVEL_ALL;
-    EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                      kRegValueEventLogLevel, log_events));
-  }
-
-  virtual void TearDown() {
-    RestoreRegistryHives();
-    EXPECT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
-  }
-
-  // Reads the topmost event log record.
-  HRESULT ReadLastEventLogRecord(const TCHAR* src_name,
-                                 EVENTLOGRECORD* rec) {
-    if (!(rec && src_name)) return E_INVALIDARG;
-    HANDLE hlog = ::OpenEventLog(NULL, src_name);
-    if (!hlog) {
-      return HRESULTFromLastError();
-    }
-    HRESULT hr = E_FAIL;
-    DWORD bytes_read(0), bytes_needed(0);
-    DWORD read_flags = EVENTLOG_BACKWARDS_READ | EVENTLOG_SEQUENTIAL_READ;
-    if (::ReadEventLog(hlog,              // Event log handle.
-                       read_flags,        // Reverse chronological order.
-                       0,                 // Not used.
-                       rec,               // Read buffer.
-                       rec->Length,       // Size of read buffer.
-                       &bytes_read,       // Number of bytes read.
-                       &bytes_needed)) {  // Number of bytes required.
-      hr = S_OK;
-    } else {
-      hr = HRESULTFromLastError();
-    }
-    ::CloseEventLog(hlog);
-    return hr;
-  }
-};
-
-TEST_F(EventLoggerTest, AddEventSource) {
-  if (!vista_util::IsUserAdmin()) {
-    std::wcout << _T("\tTest did not run because the user is not an admin.")
-               << std::endl;
-    return;
-  }
-
-  // Registers the "Google Update" event source for the "Application" log
-  EXPECT_SUCCEEDED(EventLogger::AddEventSource(EventLogger::kSourceName,
-                                               _T("path")));
-
-  const TCHAR key_name[] = _T("HKLM\\SYSTEM\\CurrentControlSet\\Services\\")
-                           _T("EventLog\\Application\\Google Update");
-  EXPECT_TRUE(RegKey::HasKey(key_name));
-
-  CString s;
-  EXPECT_SUCCEEDED(RegKey::GetValue(key_name, _T("EventMessageFile"), &s));
-  EXPECT_STREQ(s.GetString(), _T("path"));
-
-  DWORD types(0);
-  EXPECT_SUCCEEDED(RegKey::GetValue(key_name, _T("TypesSupported"), &types));
-  EXPECT_EQ(types,
-      EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE);
-
-  // Removes the "OmahaUnitTest" event source.
-  EXPECT_SUCCEEDED(EventLogger::RemoveEventSource(EventLogger::kSourceName));
-  EXPECT_FALSE(RegKey::HasKey(key_name));
-  EXPECT_TRUE(RegKey::HasKey(
-      _T("HKLM\\SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application")));
-}
-
-TEST_F(EventLoggerTest, ReportEvent) {
-  if (!vista_util::IsUserAdmin()) {
-    std::wcout << _T("\tTest did not run because the user is not an admin.")
-               << std::endl;
-    return;
-  }
-
-  EXPECT_SUCCEEDED(EventLogger::AddEventSource(EventLogger::kSourceName,
-                                               _T("path")));
-
-  const TCHAR* strings[] = {_T("foo"), _T("bar")};
-  byte buf[] = {0xaa, 0x55, 0};
-
-  const int kEventId = 100;
-  EXPECT_SUCCEEDED(EventLogger::ReportEvent(EventLogger::kSourceName,
-                                            EVENTLOG_WARNING_TYPE,
-                                            0,
-                                            kEventId,
-                                            arraysize(strings),
-                                            strings,
-                                            arraysize(buf),
-                                            buf));
-  // Read the record at the top to do a brief sanity check.
-  const size_t kBufferSize = 1024 * 64;
-  byte buffer[kBufferSize] = {0};
-  EVENTLOGRECORD* rec = reinterpret_cast<EVENTLOGRECORD*>(buffer);
-  rec->Length = kBufferSize;
-  EXPECT_SUCCEEDED(ReadLastEventLogRecord(EventLogger::kSourceName, rec));
-  EXPECT_EQ(rec->EventID, kEventId);
-  EXPECT_EQ(rec->EventType, EVENTLOG_WARNING_TYPE);
-  EXPECT_EQ(rec->EventCategory, 0);
-  EXPECT_EQ(rec->NumStrings, 2);
-  const TCHAR* src = reinterpret_cast<const TCHAR*>(
-      reinterpret_cast<byte*>(rec) + sizeof EVENTLOGRECORD);
-  EXPECT_STREQ(src, EventLogger::kSourceName);
-  const TCHAR* s2 = (LPTSTR) ((LPBYTE) rec + rec->StringOffset);
-  EXPECT_SUCCEEDED(EventLogger::RemoveEventSource(EventLogger::kSourceName));
-}
-
-TEST_F(EventLoggerTest, LogEvent_LoggingDisabled) {
-  // Disable logging.
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueEventLogLevel,
-                                    static_cast<DWORD>(0)));
-
-  const size_t kBufferSize = 1024 * 64;
-  byte buffer[kBufferSize] = {0};
-  EVENTLOGRECORD* rec = reinterpret_cast<EVENTLOGRECORD*>(buffer);
-  rec->Length = kBufferSize;
-  EXPECT_SUCCEEDED(ReadLastEventLogRecord(EventLogger::kSourceName, rec));
-  int record_number = rec->RecordNumber;
-
-  // Logging is disabled, expect no event is logged.
-  LogEvent(EVENTLOG_INFORMATION_TYPE, 10);
-  rec->Length = kBufferSize;
-  EXPECT_SUCCEEDED(ReadLastEventLogRecord(EventLogger::kSourceName, rec));
-  EXPECT_EQ(record_number, rec->RecordNumber);
-}
-
-TEST_F(EventLoggerTest, LogEvent) {
-  const size_t kBufferSize = 1024 * 64;
-  byte buffer[kBufferSize] = {0};
-  EVENTLOGRECORD* rec = reinterpret_cast<EVENTLOGRECORD*>(buffer);
-
-  LogEvent(EVENTLOG_INFORMATION_TYPE, 10);
-  rec->Length = kBufferSize;
-  EXPECT_SUCCEEDED(ReadLastEventLogRecord(EventLogger::kSourceName, rec));
-  EXPECT_EQ(10, rec->EventID);
-
-  LogEvent(EVENTLOG_INFORMATION_TYPE, 11, _T("s1"));
-  rec->Length = kBufferSize;
-  EXPECT_SUCCEEDED(ReadLastEventLogRecord(EventLogger::kSourceName, rec));
-  EXPECT_EQ(11, rec->EventID);
-
-  LogEvent(EVENTLOG_INFORMATION_TYPE, 12, _T("s1"), _T("s2"));
-  rec->Length = kBufferSize;
-  EXPECT_SUCCEEDED(ReadLastEventLogRecord(EventLogger::kSourceName, rec));
-  EXPECT_EQ(12, rec->EventID);
-}
-
-TEST_F(EventLoggerTest, LogEventContext) {
-  const size_t kBufferSize = 1024 * 64;
-  byte buffer[kBufferSize] = {0};
-  EVENTLOGRECORD* rec = reinterpret_cast<EVENTLOGRECORD*>(buffer);
-
-  LogEventContext(EVENTLOG_INFORMATION_TYPE, 20, _T("foo"));
-  rec->Length = kBufferSize;
-  EXPECT_SUCCEEDED(ReadLastEventLogRecord(EventLogger::kSourceName, rec));
-  EXPECT_EQ(20, rec->EventID);
-
-  LogEventContext(EVENTLOG_INFORMATION_TYPE, 21, _T("s1"), _T("bar"));
-  rec->Length = kBufferSize;
-  EXPECT_SUCCEEDED(ReadLastEventLogRecord(EventLogger::kSourceName, rec));
-  EXPECT_EQ(21, rec->EventID);
-
-  LogEventContext(EVENTLOG_INFORMATION_TYPE, 22,
-                  _T("s1"), _T("s2"), _T("foobar"));
-  rec->Length = kBufferSize;
-  EXPECT_SUCCEEDED(ReadLastEventLogRecord(EventLogger::kSourceName, rec));
-  EXPECT_EQ(22, rec->EventID);
-}
-
-}  // namespace omaha
diff --git a/goopdate/extra_args_parser.cc b/goopdate/extra_args_parser.cc
deleted file mode 100644
index 3987856..0000000
--- a/goopdate/extra_args_parser.cc
+++ /dev/null
@@ -1,239 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/goopdate/extra_args_parser.h"
-
-#include "omaha/common/const_cmd_line.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/string.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/goopdate_utils.h"
-
-namespace omaha {
-
-// TODO(omaha): There is no enforcement of required or optional values of
-// the extra arguments. Come up with a way to enforce this.
-HRESULT ExtraArgsParser::Parse(const TCHAR* extra_args,
-                               const TCHAR* app_args,
-                               CommandLineExtraArgs* args) {
-  // TODO(omaha): If we prefix extra_args with '/' and replace all '=' with ' '
-  // and all & with '/' then we should be able to use the CommandLineParser
-  // class to pull out the values here.  We'd need to define scenarios for all
-  // permutations of ExtraArgs, but this shouldn't be difficult to get the right
-  // ones.
-  HRESULT hr = Validate(extra_args);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  first_app_ = true;
-  int pos = 0;
-  CString input_str(extra_args);
-  CString token = input_str.Tokenize(kExtraArgsSeparators, pos);
-  while (!token.IsEmpty()) {
-    CORE_LOG(L2, (_T("[ExtraArgsParser::Parse][token=%s]"), token));
-    hr = HandleToken(token, args);
-    if (FAILED(hr)) {
-      return hr;
-    }
-
-    // Continue parsing
-    token = input_str.Tokenize(kExtraArgsSeparators, pos);
-  }
-
-  // Save the arguments for the last application.
-  args->apps.push_back(cur_extra_app_args_);
-  return ParseAppArgs(app_args, args);
-}
-
-HRESULT ExtraArgsParser::ParseAppArgs(const TCHAR* app_args,
-                                      CommandLineExtraArgs* args) {
-  if (!app_args || !*app_args) {
-    return S_OK;
-  }
-
-  HRESULT hr = Validate(app_args);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  int cur_app_args_index = -1;
-  int pos = 0;
-  CString input_str = app_args;
-  CString token = input_str.Tokenize(kExtraArgsSeparators, pos);
-  while (!token.IsEmpty()) {
-    CORE_LOG(L2, (_T("[ExtraArgsParser::ParseAppArgs][token=%s]"), token));
-    hr = HandleAppArgsToken(token, args, &cur_app_args_index);
-    if (FAILED(hr)) {
-      return hr;
-    }
-
-    token = input_str.Tokenize(kExtraArgsSeparators, pos);
-  }
-
-  return S_OK;
-}
-
-HRESULT ExtraArgsParser::Validate(const TCHAR* extra_args) {
-  CString extra_args_str(extra_args);
-  if (extra_args_str.IsEmpty()) {
-    return E_INVALIDARG;
-  }
-
-  if (-1 != extra_args_str.FindOneOf(kDisallowedCharsInExtraArgs)) {
-    // A '/' was found in the "extra" arguments or "extra" arguments were
-    // not specified before the next command.
-    return E_INVALIDARG;
-  }
-
-  return S_OK;
-}
-
-// Handles tokens from the extra arguments string.
-HRESULT ExtraArgsParser::HandleToken(const CString& token,
-                                     CommandLineExtraArgs* args) {
-  CString name;
-  CString value;
-  if (!ParseNameValuePair(token, kNameValueSeparatorChar, &name, &value)) {
-    return E_INVALIDARG;
-  }
-
-  // The first set of args apply to all apps. They may occur at any point, but
-  // only the last occurrence is recorded.
-  if (name.CompareNoCase(kExtraArgInstallationId) == 0) {
-    ASSERT1(!value.IsEmpty());
-    if (FAILED(::CLSIDFromString(const_cast<TCHAR*>(value.GetString()),
-                                 &args->installation_id))) {
-      return E_INVALIDARG;
-    }
-  } else if (name.CompareNoCase(kExtraArgBrandCode) == 0) {
-    if (value.GetLength() > kBrandIdLength) {
-      return E_INVALIDARG;
-    }
-    args->brand_code = value;
-  } else if (name.CompareNoCase(kExtraArgClientId) == 0) {
-    args->client_id = value;
-  } else if (name.CompareNoCase(kExtraArgReferralId) == 0) {
-    args->referral_id = value;
-  } else if (name.CompareNoCase(kExtraArgBrowserType) == 0) {
-    BrowserType type = BROWSER_UNKNOWN;
-    if (SUCCEEDED(goopdate_utils::ConvertStringToBrowserType(value, &type))) {
-      args->browser_type = type;
-    }
-  } else if (name.CompareNoCase(kExtraArgLanguage) == 0) {
-    if (value.GetLength() > kLangMaxLength) {
-      return E_INVALIDARG;
-    }
-    // Even if we don't support the language, we want to pass it to the
-    // installer. Omaha will pick its language later. See http://b/1336966.
-    args->language = value;
-  } else if (name.CompareNoCase(kExtraArgUsageStats) == 0) {
-    if (!String_StringToTristate(value, &args->usage_stats_enable)) {
-      return E_INVALIDARG;
-    }
-
-    // The following args are per app.
-  } else if (name.CompareNoCase(kExtraArgAdditionalParameters) == 0) {
-    cur_extra_app_args_.ap = value;
-  } else if (name.CompareNoCase(kExtraArgTTToken) == 0) {
-    cur_extra_app_args_.tt_token = value;
-  } else if (name.CompareNoCase(kExtraArgAppGuid) == 0) {
-    if (!first_app_) {
-      // Save the arguments for the application we have been processing.
-      args->apps.push_back(cur_extra_app_args_);
-    }
-    cur_extra_app_args_ = CommandLineAppArgs();
-
-    cur_extra_app_args_.app_guid = StringToGuid(value);
-    if (cur_extra_app_args_.app_guid == GUID_NULL) {
-      return E_INVALIDARG;
-    }
-    first_app_ = false;
-  } else if (name.CompareNoCase(kExtraArgAppName) == 0) {
-    if (value.GetLength() > kMaxAppNameLength) {
-      return E_INVALIDARG;
-    }
-    CString trimmed_val = value.Trim();
-    if (trimmed_val.IsEmpty()) {
-      return E_INVALIDARG;
-    }
-
-    // The value is a utf8 encoded url escaped string that is stored as a
-    // unicode string, convert it into a wide string.
-    CString app_name;
-    HRESULT hr = Utf8UrlEncodedStringToWideString(trimmed_val, &app_name);
-    if (FAILED(hr)) {
-      return hr;
-    }
-    cur_extra_app_args_.app_name = app_name;
-  } else if (name.CompareNoCase(kExtraArgNeedsAdmin) == 0) {
-    if (FAILED(String_StringToBool(value, &cur_extra_app_args_.needs_admin))) {
-      return E_INVALIDARG;
-    }
-  } else if (name.CompareNoCase(kExtraArgInstallDataIndex) == 0) {
-    cur_extra_app_args_.install_data_index = value;
-  } else {
-    // Unrecognized token
-    return E_INVALIDARG;
-  }
-
-  return S_OK;
-}
-
-// Handles tokens from the app arguments string.
-HRESULT ExtraArgsParser::HandleAppArgsToken(const CString& token,
-                                            CommandLineExtraArgs* args,
-                                            int* cur_app_args_index) {
-  ASSERT1(args);
-  ASSERT1(cur_app_args_index);
-  ASSERT1(*cur_app_args_index < static_cast<int>(args->apps.size()));
-
-  CString name;
-  CString value;
-  if (!ParseNameValuePair(token, kNameValueSeparatorChar, &name, &value)) {
-    return E_INVALIDARG;
-  }
-
-  if (name.CompareNoCase(kExtraArgAppGuid) == 0) {
-    *cur_app_args_index = -1;
-    for (size_t i = 0; i < args->apps.size(); ++i) {
-      if (!value.CompareNoCase(GuidToString(args->apps[i].app_guid))) {
-        *cur_app_args_index = i;
-        break;
-      }
-    }
-
-    if (-1 == *cur_app_args_index) {
-      return E_INVALIDARG;
-    }
-  } else if (name.CompareNoCase(kExtraArgInstallerData) == 0) {
-    if (-1 == *cur_app_args_index) {
-      return E_INVALIDARG;
-    }
-    args->apps[*cur_app_args_index].encoded_installer_data = value;
-  } else {
-    // Unrecognized token
-    return E_INVALIDARG;
-  }
-
-  return S_OK;
-}
-
-}  // namespace omaha
-
diff --git a/goopdate/extra_args_parser.h b/goopdate/extra_args_parser.h
deleted file mode 100644
index 106a20a..0000000
--- a/goopdate/extra_args_parser.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_GOOPDATE_EXTRA_ARGS_PARSER_H__
-#define OMAHA_GOOPDATE_EXTRA_ARGS_PARSER_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include "base/basictypes.h"
-#include "omaha/goopdate/command_line.h"
-
-namespace omaha {
-
-// This class handles tokenizing and parsing the ExtraArgs portion of the
-// command line.
-// The format of the extra arguments is as follows:
-// appguid={}&.....appguid={}....
-// appguid has to be the first value of the extra arguments.
-// Each appguid defines one product.
-class ExtraArgsParser {
- public:
-  ExtraArgsParser() : first_app_(false) {}
-
-  // Parses args->extra_args into other values of args.
-  HRESULT Parse(const TCHAR* extra_args,
-                const TCHAR* app_args,
-                CommandLineExtraArgs* args);
-
- private:
-  HRESULT ParseAppArgs(const TCHAR* app_args, CommandLineExtraArgs* args);
-
-  // Performs validation against extra_args and if it's valid, stores the
-  // extra_args value into args->extra_args.
-  HRESULT Validate(const TCHAR* extra_args);
-  HRESULT HandleToken(const CString& token, CommandLineExtraArgs* args);
-  HRESULT HandleAppArgsToken(const CString& token,
-                             CommandLineExtraArgs* args,
-                             int* cur_app_args_index);
-
-  CommandLineAppArgs cur_extra_app_args_;
-  bool first_app_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(ExtraArgsParser);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_EXTRA_ARGS_PARSER_H__
-
diff --git a/goopdate/extra_args_parser_unittest.cc b/goopdate/extra_args_parser_unittest.cc
deleted file mode 100644
index f273718..0000000
--- a/goopdate/extra_args_parser_unittest.cc
+++ /dev/null
@@ -1,1295 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/string.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/extra_args_parser.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/resource_manager.h"
-#include "omaha/net/http_client.h"
-#include "omaha/testing/resource.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-extern void VerifyCommandLineArgs(const CommandLineArgs& expected,
-                                  const CommandLineArgs& actual);
-
-extern void VerifyCommandLineExtraArgs(const CommandLineExtraArgs& expected,
-                                       const CommandLineExtraArgs& actual);
-
-void VerifyBrowserType(const CommandLineExtraArgs& args,
-                       const CString& app_guid,
-                       BrowserType type) {
-  CommandLineAppArgs app_args;
-  app_args.app_guid = StringToGuid(app_guid);
-
-  CommandLineExtraArgs expected;
-  expected.browser_type = type;
-  expected.apps.push_back(app_args);
-
-  VerifyCommandLineExtraArgs(expected, args);
-}
-
-void VerifyExtraArgsHaveSpecificValues(
-         const CommandLineExtraArgs& args,
-         const CString& app_guid,
-         Tristate expected_usage_stats_enable,
-         const GUID& expected_installation_id,
-         const CString& expected_brand_code,
-         const CString& expected_client_id,
-         const CString& expected_referral_id,
-         const CString& expected_ap,
-         const CString& expected_tt,
-         const CString& expected_encoded_installer_data,
-         const CString& expected_install_data_index) {
-  CommandLineAppArgs app_args;
-  app_args.app_guid = StringToGuid(app_guid);
-  app_args.ap = expected_ap;
-  app_args.tt_token = expected_tt;
-  app_args.encoded_installer_data = expected_encoded_installer_data;
-  app_args.install_data_index = expected_install_data_index;
-
-  CommandLineExtraArgs expected;
-  expected.apps.push_back(app_args);
-  expected.installation_id = expected_installation_id;
-  expected.brand_code = expected_brand_code;
-  expected.client_id = expected_client_id;
-  expected.referral_id = expected_referral_id;
-  expected.usage_stats_enable = expected_usage_stats_enable;
-
-  VerifyCommandLineExtraArgs(expected, args);
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsAppName1) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
-                       _T("appname1=Hello");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsAppName2) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
-                       _T("appname= ");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsAppName3) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-
-  CString app_guid = _T("{D0324988-DA8A-49e5-BCE5-925FCD04EAB7}");
-  CString extra_args = _T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
-                       _T("appname=Test");
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-
-  CommandLineAppArgs app_args;
-
-  app_args.app_name = _T("Test");
-  app_args.app_guid = StringToGuid(app_guid);
-  CommandLineExtraArgs expected;
-  expected.apps.push_back(app_args);
-  VerifyCommandLineExtraArgs(expected, args);
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsAppName4) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
-                       _T("appname=Test App");
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-
-  CommandLineAppArgs app_args;
-  app_args.app_guid =
-      StringToGuid(_T("{D0324988-DA8A-49e5-BCE5-925FCD04EAB7}"));
-  app_args.app_name = _T("Test App");
-
-  CommandLineExtraArgs expected;
-  expected.apps.push_back(app_args);
-
-  VerifyCommandLineExtraArgs(expected, args);
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsAppName5) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
-                       _T("appname= T Ap p ");
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-
-  CommandLineAppArgs app_args;
-  app_args.app_guid =
-      StringToGuid(_T("{D0324988-DA8A-49e5-BCE5-925FCD04EAB7}"));
-  app_args.app_name = _T("T Ap p");
-
-  CommandLineExtraArgs expected;
-  expected.apps.push_back(app_args);
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsAppName6) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
-                       _T("appname= T Ap p");
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-
-  CommandLineAppArgs app_args;
-  app_args.app_guid =
-      StringToGuid(_T("{D0324988-DA8A-49e5-BCE5-925FCD04EAB7}"));
-  app_args.app_name = _T("T Ap p");
-
-  CommandLineExtraArgs expected;
-  expected.apps.push_back(app_args);
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsAppName7) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  // The size of the application name is limited to 512 wide chars.
-  CString str(_T('a'), 513);
-  CString extra_args;
-  extra_args.Format(_T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
-                    _T("appname=%s"), str);
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsAppNameUnicode) {
-  // Read the non-ascii string from the resources, and convert
-  // it into a utf8 encoded, url escaped string.
-  CString non_ascii_name;
-  ASSERT_TRUE(non_ascii_name.LoadString(IDS_ESCAPE_TEST));
-
-  CString wide_tag;
-  ASSERT_HRESULT_SUCCEEDED(WideStringToUtf8UrlEncodedString(non_ascii_name,
-                                                            &wide_tag));
-
-  ExtraArgsParser parser;
-  CommandLineExtraArgs args;
-  CString extra_args;
-  extra_args.Format(_T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
-                    _T("appname=%s"), wide_tag);
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-
-  CommandLineAppArgs app_args;
-  app_args.app_name = non_ascii_name;
-  app_args.app_guid =
-      StringToGuid(_T("{D0324988-DA8A-49e5-BCE5-925FCD04EAB7}"));
-
-  CommandLineExtraArgs expected;
-  expected.apps.push_back(app_args);
-  VerifyCommandLineExtraArgs(expected, args);
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsAppNameUnicode2) {
-  // Read the non-ascii string from the resources, and convert
-  // it into a utf8 encoded, url escaped string.
-  CString non_ascii_name;
-  ASSERT_TRUE(non_ascii_name.LoadString(IDS_ESCAPE_TEST1));
-
-  CString escaped(_T("%E0%A4%B8%E0%A5%8D%E0%A4%A5%E0%A4%BE%E0%A4%AA%E0%A4%BF")
-                  _T("%E0%A4%A4%20%E0%A4%95%E0%A4%B0%20%E0%A4%B0%E0%A4%B9%E0")
-                  _T("%A4%BE%20%E0%A4%B9%E0%A5%88%E0%A5%A4"));
-
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-
-  CString extra_args;
-  extra_args.Format(_T("appguid={D0324988-DA8A-49e5-BCE5-925FCD04EAB7}&")
-                    _T("appname=%s"), escaped);
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-
-  CommandLineAppArgs app_args;
-  app_args.app_name = non_ascii_name;
-  app_args.app_guid =
-      StringToGuid(_T("{D0324988-DA8A-49e5-BCE5-925FCD04EAB7}"));
-
-  CommandLineExtraArgs expected;
-  expected.apps.push_back(app_args);
-
-  VerifyCommandLineExtraArgs(expected, args);
-}
-
-
-TEST(ExtraArgsParserTest, ExtraArgumentsAppGuid1) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid1=Hello");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsAppGuid2) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("")
-                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}");
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-
-  CommandLineAppArgs app_args;
-  const GUID guid = {0x8617EE50, 0xF91C, 0x4DC1,
-                          {0xB9, 0x37, 0x09, 0x69, 0xEE, 0xF5, 0x9B, 0x0B}};
-  app_args.app_guid = guid;
-
-  CommandLineExtraArgs expected;
-  expected.apps.push_back(app_args);
-
-  VerifyCommandLineExtraArgs(expected, args);
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsNeedsAdmin1) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("needsadmin=Hello");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsNeedsAdmin2) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("needsadmin= ");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsNeedsAdmin3) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("")
-                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("needsadmin=True");
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-
-  CommandLineAppArgs app_args;
-  app_args.needs_admin = true;
-  app_args.app_guid =
-      StringToGuid(_T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"));
-
-  CommandLineExtraArgs expected;
-  expected.apps.push_back(app_args);
-
-  VerifyCommandLineExtraArgs(expected, args);
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsNeedsAdmin4) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("")
-                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("needsadmin=true");
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-
-  CommandLineAppArgs app_args;
-  app_args.needs_admin = true;
-  app_args.app_guid =
-      StringToGuid(_T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"));
-
-  CommandLineExtraArgs expected;
-  expected.apps.push_back(app_args);
-
-  VerifyCommandLineExtraArgs(expected, args);
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsNeedsAdmin5) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("")
-                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("needsadmin=False");
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-
-  CommandLineAppArgs app_args;
-  app_args.needs_admin = false;
-  app_args.app_guid =
-      StringToGuid(_T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"));
-
-  CommandLineExtraArgs expected;
-  expected.apps.push_back(app_args);
-
-  VerifyCommandLineExtraArgs(expected, args);
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsNeedsAdmin6) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("")
-                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("needsadmin=false");
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-
-  CommandLineAppArgs app_args;
-  app_args.needs_admin = false;
-  app_args.app_guid =
-      StringToGuid(_T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"));
-
-  CommandLineExtraArgs expected;
-  expected.apps.push_back(app_args);
-
-  VerifyCommandLineExtraArgs(expected, args);
-}
-
-//
-// Test the handling of the contents of the extra arguments.
-//
-
-TEST(ExtraArgsParserTest, ExtraArgumentsAssignmentOnly) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("=");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsExtraAssignment1) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=1=");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsExtraAssignment2) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("=usagestats=1");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsExtraAssignment3) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=1&=");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsExtraAssignment4) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("=&usagestats=1");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsValueWithoutName) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("=hello");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-
-// Also tests ending extra arguments with '='.
-TEST(ExtraArgsParserTest, ExtraArgumentsNameWithoutValue) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsNameWithoutValueBeforeNextArgument) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("")
-                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=&client=hello");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest,
-     ExtraArgumentsNameWithoutArgumentSeparatorAfterIntValue) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("")
-                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=1client=hello");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest,
-     ExtraArgumentsNameWithoutArgumentSeparatorAfterStringValue) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("")
-                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=yesclient=hello");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsHaveDoubleAmpersand) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("")
-                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=1&&client=hello");
-
-  CString app_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                     _T("installerdata=foobar");
-
-  // Ideally, this would flag an error.
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, app_args, &args));
-  VerifyExtraArgsHaveSpecificValues(
-      args,
-      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-      TRISTATE_TRUE,
-      GUID_NULL,
-      _T(""),
-      _T("hello"),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T("foobar"),
-      _T(""));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsAmpersandOnly) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("&");
-
-  // Ideally, this would flag an error.
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyExtraArgsHaveSpecificValues(
-      args,
-      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-      TRISTATE_NONE,
-      GUID_NULL,
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsBeginInAmpersand) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("&appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=1");
-
-  // Ideally, this would flag an error.
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyExtraArgsHaveSpecificValues(
-      args,
-      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-      TRISTATE_TRUE,
-      GUID_NULL,
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsEndInAmpersand) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("&appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=1&");
-
-  // Ideally, this would flag an error.
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyExtraArgsHaveSpecificValues(
-      args,
-      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-      TRISTATE_TRUE,
-      GUID_NULL,
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsEmptyString) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceOnly1) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T(" ");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceOnly2) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("\t");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceOnly3) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("\r");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceOnly4) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("\n");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceOnly5) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("\r\n");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-
-//
-// Test the parsing of the extra command and its arguments into a string.
-//
-
-TEST(ExtraArgsParserTest, ExtraArgumentsOneValid) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("&appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=1");
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyExtraArgsHaveSpecificValues(
-      args,
-      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-      TRISTATE_TRUE,
-      GUID_NULL,
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsTwoValid) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("&appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=1&client=hello");
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyExtraArgsHaveSpecificValues(
-      args,
-      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-      TRISTATE_TRUE,
-      GUID_NULL,
-      _T(""),
-      _T("hello"),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsHaveSwitchInTheMiddle) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=1/other_value=9");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsHaveDoubleQuoteInTheMiddle) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=1\"/other_value=9");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest,
-       ExtraArgumentsHaveDoubleQuoteInTheMiddleAndNoForwardSlash) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=1\"other_value=9");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsHaveSpaceAndForwardSlashBeforeQuote) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=1 /other_value=9");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsHaveForwardSlashBeforeQuote) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=1/other_value=9");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsSpecifiedTwice) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=1\" \"client=10");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceBeforeArgs1) {
-  CommandLineExtraArgs args;
-  // TODO(omaha): This one passes now due to different whitespace
-  // handling.  Remove it?  Is this really a problem to have?
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T(" usagestats=1");
-  ExtraArgsParser parser;
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceBeforeArgs2) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("\tappguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=1");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceBeforeArgs3) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("\rappguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=1");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceBeforeArgs4) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("\nappguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}")
-                       _T("&usagestats=1");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsWhiteSpaceBeforeArgs5) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("\r\nusagestats=1");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsForwardSlash1) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("/");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsForwardSlash2) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("/ appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsBackwardSlash1) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("\\");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsBackwardSlash2) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("\\appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ExtraArgumentsBackwardSlash3) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("\\ appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-//
-// Test specific extra commands.
-//
-
-TEST(ExtraArgsParserTest, UsageStatsOutsideExtraCommand) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("/usagestats");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, UsageStatsOn) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=1");
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyExtraArgsHaveSpecificValues(
-      args,
-      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-      TRISTATE_TRUE,
-      GUID_NULL,
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""));
-}
-
-TEST(ExtraArgsParserTest, UsageStatsOff) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=0");
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyExtraArgsHaveSpecificValues(
-      args,
-      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-      TRISTATE_FALSE,
-      GUID_NULL,
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""));
-}
-
-// This commandline has no effect, but it's permitted.
-TEST(ExtraArgsParserTest, UsageStatsNone) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=2");
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyExtraArgsHaveSpecificValues(
-      args,
-      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-      TRISTATE_NONE,
-      GUID_NULL,
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""));
-}
-
-TEST(ExtraArgsParserTest, UsageStatsInvalidPositiveValue) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=3");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, UsageStatsInvalidNegativeValue) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=-1");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, UsageStatsValueIsString) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("usagestats=true");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, InstallationGuidValid) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("iid={98CEC468-9429-4984-AEDE-4F53C6A14869}");
-  const GUID expected_guid = {0x98CEC468, 0x9429, 0x4984,
-                              {0xAE, 0xDE, 0x4F, 0x53, 0xC6, 0xA1, 0x48, 0x69}};
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyExtraArgsHaveSpecificValues(
-      args,
-      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-      TRISTATE_NONE,
-      expected_guid,
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""));
-}
-
-TEST(ExtraArgsParserTest, InstallationGuidMissingBraces) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("iid=98CEC468-9429-4984-AEDE-4F53C6A14869");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, InstallationGuidMissingDashes) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("iid=98CEC46894294984AEDE4F53C6A14869");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, InstallationGuidMissingCharacter) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("iid=98CEC468-9429-4984-AEDE-4F53C6A1486");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, InstallationGuidIsString) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("iid=hello");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, BrandCodeValid) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("brand=GOOG");
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyExtraArgsHaveSpecificValues(
-      args,
-      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-      TRISTATE_NONE,
-      GUID_NULL,
-      _T("GOOG"),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""));
-}
-
-TEST(ExtraArgsParserTest, BrandCodeTooLong) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("brand=CHMI\xe3\x83\xbb");
-
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, ClientIdValid) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("client=some_partner");
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyExtraArgsHaveSpecificValues(
-      args,
-      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-      TRISTATE_NONE,
-      GUID_NULL,
-      _T(""),
-      _T("some_partner"),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""));
-}
-
-TEST(ExtraArgsParserTest, ReferralIdValid) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("referral=ABCD123");
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyExtraArgsHaveSpecificValues(
-      args,
-      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-      TRISTATE_NONE,
-      GUID_NULL,
-      _T(""),
-      _T(""),
-      _T("ABCD123"),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""));
-}
-
-TEST(ExtraArgsParserTest, ApValid) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("ap=developer");
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyExtraArgsHaveSpecificValues(
-      args,
-      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-      TRISTATE_NONE,
-      GUID_NULL,
-      _T(""),
-      _T(""),
-      _T(""),
-      _T("developer"),
-      _T(""),
-      _T(""),
-      _T(""));
-}
-
-TEST(ExtraArgsParserTest, TTValid) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("tttoken=7839g93");
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyExtraArgsHaveSpecificValues(
-      args,
-      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-      TRISTATE_NONE,
-      GUID_NULL,
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T("7839g93"),
-      _T(""),
-      _T(""));
-}
-
-TEST(ExtraArgsParserTest, AppArgsValid) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("tttoken=7839g93");
-
-  CString app_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                     _T("installerdata=%E0%A4foobar");
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, app_args, &args));
-  VerifyExtraArgsHaveSpecificValues(
-      args,
-      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-      TRISTATE_NONE,
-      GUID_NULL,
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T("7839g93"),
-      _T("%E0%A4foobar"),
-      _T(""));
-}
-
-TEST(ExtraArgsParserTest, AppArgsInvalidAppGuid) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("tttoken=7839g93");
-
-  CString app_args = _T("appguid={E135384F-85A2-4328-B07D-2CF70313D505}&")
-                     _T("installerdata=%E0%A4foobar");
-
-  EXPECT_EQ(E_INVALIDARG, parser.Parse(extra_args, app_args, &args));
-}
-
-TEST(ExtraArgsParserTest, AppArgsInvalidAttribute) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("tttoken=7839g93");
-
-  CString app_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                     _T("tttoken=foobar");
-
-  EXPECT_EQ(E_INVALIDARG, parser.Parse(extra_args, app_args, &args));
-}
-
-TEST(ExtraArgsParserTest, InstallerDataNotAllowedInExtraArgs) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("appname=TestApp2&")
-                       _T("needsadmin=true&")
-                       _T("installerdata=Hello%20World");
-
-  EXPECT_EQ(E_INVALIDARG, parser.Parse(extra_args, NULL, &args));
-}
-
-TEST(ExtraArgsParserTest, InstallDataIndexValid) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("installdataindex=foobar");
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyExtraArgsHaveSpecificValues(
-      args,
-      _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-      TRISTATE_NONE,
-      GUID_NULL,
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T(""),
-      _T("foobar"));
-}
-
-TEST(ExtraArgsParserTest, BrowserTypeValid_0) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("browser=0");
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyBrowserType(args,
-                    _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-                    BROWSER_UNKNOWN);
-}
-
-TEST(ExtraArgsParserTest, BrowserTypeValid_1) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("browser=1");
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyBrowserType(args,
-                    _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-                    BROWSER_DEFAULT);
-}
-
-TEST(ExtraArgsParserTest, BrowserTypeValid_2) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("browser=2");
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyBrowserType(args,
-                    _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-                    BROWSER_IE);
-}
-
-TEST(ExtraArgsParserTest, BrowserTypeValid_3) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("browser=3");
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyBrowserType(args,
-                    _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-                    BROWSER_FIREFOX);
-}
-
-TEST(ExtraArgsParserTest, BrowserTypeValid_4) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("browser=4");
-
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  VerifyBrowserType(args,
-                    _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-                    BROWSER_CHROME);
-}
-
-TEST(ExtraArgsParserTest, BrowserTypeInvalid) {
-  EXPECT_EQ(5, BROWSER_MAX) <<
-      _T("Browser type may have been added. Add new Valid_n test and change ")
-      _T("browser values in extra args strings below.");
-
-  CommandLineExtraArgs args1;
-  ExtraArgsParser parser1;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("browser=5");
-
-  EXPECT_SUCCEEDED(parser1.Parse(extra_args, NULL, &args1));
-  VerifyBrowserType(args1,
-                    _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-                    BROWSER_UNKNOWN);
-
-  CommandLineExtraArgs args2;
-  ExtraArgsParser parser2;
-  extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-               _T("browser=9");
-
-  EXPECT_SUCCEEDED(parser2.Parse(extra_args, NULL, &args2));
-  VerifyBrowserType(args2,
-                    _T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"),
-                    BROWSER_UNKNOWN);
-}
-
-TEST(ExtraArgsParserTest, ValidLang) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("lang=en");
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  EXPECT_STREQ(_T("en"), args.language);
-}
-
-// Language must be passed even if not supported. See http://b/1336966.
-TEST(ExtraArgsParserTest, UnsupportedLang) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("lang=foobar");
-  EXPECT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-  EXPECT_STREQ(_T("foobar"), args.language);
-}
-
-TEST(ExtraArgsParserTest, LangTooLong) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("lang=morethan10chars");
-  EXPECT_FAILED(parser.Parse(extra_args, NULL, &args));
-}
-
-//
-// Test multiple applications in the extra arguments
-//
-TEST(ExtraArgsParserTestMultipleEntries, TestNotStartingWithAppGuid) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appname=TestApp&")
-                       _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("appname=TestApp&")
-                       _T("appname=false&")
-                       _T("iid={98CEC468-9429-4984-AEDE-4F53C6A14869}&")
-                       _T("ap=test_ap&")
-                       _T("tttoken=foobar&")
-                       _T("usagestats=1&")
-                       _T("browser=2&");
-  EXPECT_HRESULT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-}
-
-// This also tests that the last occurrence of a global extra arg is the one
-// that is saved.
-TEST(ExtraArgsParserTestMultipleEntries, ThreeApplications) {
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  CString extra_args = _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                       _T("appname=TestApp&")
-                       _T("needsadmin=false&")
-                       _T("iid={98CEC468-9429-4984-AEDE-4F53C6A14869}&")
-                       _T("ap=test_ap&")
-                       _T("tttoken=foobar&")
-                       _T("usagestats=1&")
-                       _T("browser=2&")
-                       _T("brand=GOOG&")
-                       _T("client=_some_client&")
-                       _T("referral=A123456789&")
-                       _T("appguid={5E46DE36-737D-4271-91C1-C062F9FE21D9}&")
-                       _T("appname=TestApp2&")
-                       _T("needsadmin=true&")
-                       _T("iid={98CEC468-9429-4984-AEDE-4F53C6A14869}&")
-                       _T("ap=test_ap2&")
-                       _T("tttoken=foobar2&")
-                       _T("usagestats=0&")
-                       _T("browser=3&")
-                       _T("brand=g00g&")
-                       _T("client=_different_client&")
-                       _T("appguid={5F46DE36-737D-4271-91C1-C062F9FE21D9}&")
-                       _T("appname=TestApp3");
-
-  CString app_args = _T("appguid={5F46DE36-737D-4271-91C1-C062F9FE21D9}&")
-                     _T("installerdata=installerdata_app3&")
-                     _T("appguid={8617EE50-F91C-4DC1-B937-0969EEF59B0B}&")
-                     _T("installerdata=installerdata_app1");
-
-  EXPECT_HRESULT_SUCCEEDED(parser.Parse(extra_args, app_args, &args));
-
-  CommandLineAppArgs input1;
-  input1.app_guid = StringToGuid(_T("{8617EE50-F91C-4DC1-B937-0969EEF59B0B}"));
-  input1.app_name = _T("TestApp");
-  input1.needs_admin = false;
-  input1.ap = _T("test_ap");
-  input1.tt_token = _T("foobar");
-  input1.encoded_installer_data = _T("installerdata_app1");
-
-  CommandLineAppArgs input2;
-  input2.app_guid = StringToGuid(_T("{5E46DE36-737D-4271-91C1-C062F9FE21D9}"));
-  input2.app_name = _T("TestApp2");
-  input2.needs_admin = true;
-  input2.ap = _T("test_ap2");
-  input2.tt_token = _T("foobar2");
-
-  CommandLineAppArgs input3;
-  input3.app_guid = StringToGuid(_T("{5F46DE36-737D-4271-91C1-C062F9FE21D9}"));
-  input3.app_name = _T("TestApp3");
-  input3.encoded_installer_data = _T("installerdata_app3");
-
-  CommandLineExtraArgs expected;
-  expected.apps.push_back(input1);
-  expected.apps.push_back(input2);
-  expected.apps.push_back(input3);
-  expected.installation_id = StringToGuid(
-      _T("{98CEC468-9429-4984-AEDE-4F53C6A14869}"));
-  expected.brand_code = _T("g00g");
-  expected.client_id = _T("_different_client");
-  expected.referral_id = _T("A123456789");
-  expected.browser_type = BROWSER_FIREFOX;
-  expected.usage_stats_enable = TRISTATE_FALSE;
-
-  VerifyCommandLineExtraArgs(expected, args);
-}
-
-}  // namespace omaha
-
diff --git a/goopdate/google_update.cc b/goopdate/google_update.cc
index a33b11a..cdd6625 100644
--- a/goopdate/google_update.cc
+++ b/goopdate/google_update.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2009-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -13,155 +13,232 @@
 // limitations under the License.
 // ========================================================================
 //
-// Contains the ATL exe server used for OnDemand, as well as the ProcessLauncher
-// server used to launch a process.
-// The ProcessLauncher server is to be used only by the machine google update.
-// The idea is that the machine Google Update is elevated and launching
-// a process, say a browser, from that elevated process. This will cause the
-// browser to run elevated, which is a bad idea.
-// What is needed is the ability to run medium integrity processes from a
-// high integrity process. This can be done in two ways:
-// 1. Create a service and expose some form of IPC. A service is required
-//    because admins (even elevated) cannot call CreateProcessAsUser. Admins
-//    do not posess the required privilege. However the local system account
-//    has this privilege to call CreateProcessAsUser and hence can be used to
-//    start the browser with the token of a medium integrity process.
-// 2. Create a COM local server. Impersonate a medium integrity user in the
-//    client. Fortunately the impersonation works, then create instance the COM
-//    local server. If the COM security is set to use DYNAMIC_CLOAKING, then
-//    the local server will be created using the thread credentials, allowing
-//    the COM local server to be launched as medium integrity.
-// This class implements the second method listed above.
-// The server listens to the machine google update's shut down event.
+// Contains the ATL exe server registration.
 
 #include "omaha/goopdate/google_update.h"
-
-#include <windows.h>
-#include "omaha/common/debug.h"
-#include "omaha/common/event_handler.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/reactor.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/google_update_idl_datax.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/worker/worker.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/core/google_update_core.h"
+#include "omaha/goopdate/broker_class_factory.h"
+#include "omaha/goopdate/cocreate_async.h"
+#include "omaha/goopdate/cred_dialog.h"
+#include "omaha/goopdate/google_update3.h"
+#include "omaha/goopdate/omaha3_idl_datax.h"
+#include "omaha/goopdate/ondemand.h"
+#include "omaha/goopdate/oneclick_process_launcher.h"
+#include "omaha/goopdate/process_launcher.h"
+#include "omaha/goopdate/update3web.h"
+#include "omaha/goopdate/worker.h"
 
 namespace omaha {
 
-GoogleUpdate::GoogleUpdate() {}
+// Template arguments need to be non-const TCHAR arrays.
+TCHAR kOnDemandMachineBrokerProgId[] = kProgIDOnDemandMachine;
+TCHAR kUpdate3WebMachineBrokerProgId[] = kProgIDUpdate3WebMachine;
+TCHAR kHKRootUser[] = _T("HKCU");
+TCHAR kHKRootMachine[] = _T("HKLM");
+TCHAR kProgIDUpdate3COMClassUserLocal[] = kProgIDUpdate3COMClassUser;
 
-GoogleUpdate::~GoogleUpdate() {}
+BEGIN_OBJECT_MAP(object_map_update3_user_mode)
+  OBJECT_ENTRY(__uuidof(GoogleUpdate3UserClass), Update3COMClassUser)
+END_OBJECT_MAP()
+
+BEGIN_OBJECT_MAP(object_map_broker_machine_mode)
+  OBJECT_ENTRY(__uuidof(OnDemandMachineAppsClass), OnDemandMachineBroker)
+  OBJECT_ENTRY(__uuidof(GoogleUpdate3WebMachineClass), Update3WebMachineBroker)
+  OBJECT_ENTRY(__uuidof(CoCreateAsyncClass), CoCreateAsync)
+  OBJECT_ENTRY(__uuidof(OneClickMachineProcessLauncherClass),
+                        OneClickProcessLauncher)
+END_OBJECT_MAP()
+
+BEGIN_OBJECT_MAP(object_map_ondemand_user_mode)
+  OBJECT_ENTRY(__uuidof(GoogleUpdate3WebUserClass), Update3WebUser)
+  OBJECT_ENTRY(__uuidof(OnDemandUserAppsClass), OnDemandUser)
+  OBJECT_ENTRY(__uuidof(CredentialDialogUserClass), CredentialDialogUser)
+  OBJECT_ENTRY(__uuidof(OneClickUserProcessLauncherClass),
+                        OneClickProcessLauncher)
+END_OBJECT_MAP()
+
+BEGIN_OBJECT_MAP(object_map_ondemand_machine_mode)
+  OBJECT_ENTRY(__uuidof(ProcessLauncherClass), ProcessLauncher)
+  OBJECT_ENTRY(__uuidof(GoogleUpdateCoreMachineClass), GoogleUpdateCoreMachine)
+  OBJECT_ENTRY(__uuidof(OnDemandMachineAppsFallbackClass),
+               OnDemandMachineFallback)
+  OBJECT_ENTRY(__uuidof(GoogleUpdate3WebMachineFallbackClass),
+               Update3WebMachineFallback)
+  OBJECT_ENTRY(__uuidof(CredentialDialogMachineClass), CredentialDialogMachine)
+END_OBJECT_MAP()
+
+_ATL_OBJMAP_ENTRY* GoogleUpdate::GetObjectMap() {
+  if (mode_ == kUpdate3Mode && !is_machine_) {
+    return object_map_update3_user_mode;
+  }
+
+  if (mode_ == kBrokerMode && is_machine_) {
+    return object_map_broker_machine_mode;
+  }
+
+  if (mode_ == kOnDemandMode && !is_machine_) {
+    return object_map_ondemand_user_mode;
+  }
+
+  if (mode_ == kOnDemandMode && is_machine_) {
+    return object_map_ondemand_machine_mode;
+  }
+
+  return NULL;
+}
+
+GoogleUpdate::GoogleUpdate(bool is_machine, ComServerMode mode)
+    : is_machine_(is_machine), mode_(mode) {
+  // Disable the delay on shutdown mechanism in CAtlExeModuleT.
+  m_bDelayShutdown = false;
+}
+
+GoogleUpdate::~GoogleUpdate() {
+  // GoogleUpdate is typically created on the stack. We reset the _pAtlModule
+  // here, to allow for cases such as /RegServer, where multiple instances of
+  // GoogleUpdate are created and destroyed serially.
+  _pAtlModule = NULL;
+}
 
 HRESULT GoogleUpdate::Main() {
-  // Disable the delay on shutdown mechanism inside ATL.
-  m_bDelayShutdown = false;
+  HRESULT hr = E_FAIL;
+  if (!ParseCommandLine(::GetCommandLine(), &hr)) {
+    // This was either /RegServer or /UnregServer. Return early.
+    return hr;
+  }
+
+  hr = InitializeServerSecurity(is_machine_);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  DisableCOMExceptionHandling();
+
+  // TODO(omaha3): We do not call worker_->Run() from anywhere. This means that
+  // the ThreadPool and the ShutdownHandler within the Worker are not
+  // initialized. We need to eventually fix this.
+
+  CORE_LOG(L2, (_T("[Calling CAtlExeModuleT<GoogleUpdate>::WinMain]")));
   return CAtlExeModuleT<GoogleUpdate>::WinMain(0);
 }
 
-bool ShouldRegisterClsid(const CLSID& clsid) {
-  bool is_machine = goopdate_utils::IsRunningFromOfficialGoopdateDir(true);
-
-  // Machine-only CLSIDs.
-  if (IsEqualGUID(clsid, __uuidof(ProcessLauncherClass)) ||
-      IsEqualGUID(clsid, __uuidof(OnDemandMachineAppsClass))) {
-    return is_machine;
-  }
-
-  // User-only CLSIDs.
-  if (IsEqualGUID(clsid, __uuidof(OnDemandUserAppsClass))) {
-    return !is_machine;
-  }
-
-  // Unknown CLSIDs.
-  ASSERT(false, (_T("[Unknown CLSID][%s]"), GuidToString(clsid)));
-  return false;
-}
-
 HRESULT GoogleUpdate::RegisterClassObjects(DWORD, DWORD) throw() {
-  HRESULT hr = S_FALSE;
-  for (_ATL_OBJMAP_ENTRY** entry = _AtlComModule.m_ppAutoObjMapFirst;
-       entry < _AtlComModule.m_ppAutoObjMapLast && SUCCEEDED(hr);
+  CORE_LOG(L3, (_T("[RegisterClassObjects]")));
+
+  for (_ATL_OBJMAP_ENTRY* entry = GetObjectMap();
+       entry && entry->pclsid != NULL;
        entry++) {
-    if (*entry != NULL && ShouldRegisterClsid(*(*entry)->pclsid)) {
-      hr = (*entry)->RegisterClassObject(CLSCTX_LOCAL_SERVER,
-                                           REGCLS_SINGLEUSE | REGCLS_SUSPENDED);
+    HRESULT hr = entry->RegisterClassObject(CLSCTX_LOCAL_SERVER,
+                                            REGCLS_MULTIPLEUSE |
+                                            REGCLS_SUSPENDED);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[RegisterClassObject failed][%s][0x%x]"),
+                    GuidToString(*entry->pclsid), hr));
+      return hr;
     }
   }
 
-  return hr;
+  return S_OK;
 }
+
 HRESULT GoogleUpdate::RevokeClassObjects() throw() {
-    return AtlComModuleRevokeClassObjects(&_AtlComModule);
-}
+  CORE_LOG(L3, (_T("[RevokeClassObjects]")));
 
-HRESULT RegisterOrUnregisterExe(bool is_register) {
-  HRESULT hr = S_FALSE;
-  for (_ATL_OBJMAP_ENTRY** entry = _AtlComModule.m_ppAutoObjMapFirst;
-       entry < _AtlComModule.m_ppAutoObjMapLast && SUCCEEDED(hr);
+  for (_ATL_OBJMAP_ENTRY* entry = GetObjectMap();
+       entry && entry->pclsid != NULL;
        entry++) {
-    if (*entry != NULL) {
-      const CLSID& clsid = *(*entry)->pclsid;
-      if (!ShouldRegisterClsid(clsid)) {
-        continue;
-      }
-
-      hr = is_register ? _AtlComModule.RegisterServer(false, &clsid) :
-                         _AtlComModule.UnregisterServer(false, &clsid);
-      ASSERT(SUCCEEDED(hr), (_T("[RegisterOrUnregisterExe fail][%d][0x%x][%s]"),
-                             is_register, hr, GuidToString(clsid)));
+    HRESULT hr = entry->RevokeClassObject();
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[RevokeClassObject failed][%s][0x%x]"),
+                    GuidToString(*entry->pclsid), hr));
+      return hr;
     }
   }
 
-  return hr;
+  return S_OK;
 }
 
-HRESULT RegisterOrUnregisterProxy(bool is_register) {
-  HRESULT hr = is_register ? PrxDllRegisterServer() : PrxDllUnregisterServer();
-  if (FAILED(hr)) {
-    CORE_LOG(LW, (_T("[RegisterOrUnregisterProxy failed][%d][0x%x]"),
-                  is_register, hr));
+HRESULT GoogleUpdate::RegisterOrUnregisterExe(bool is_register) {
+  CORE_LOG(L3, (_T("[RegisterOrUnregisterExe][%d]"), is_register));
+
+  for (_ATL_OBJMAP_ENTRY* entry = GetObjectMap();
+       entry && entry->pclsid != NULL;
+       entry++) {
+    HRESULT hr = entry->pfnUpdateRegistry(is_register);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[pfnUpdateRegistry failed][%d][0x%x][%s]"),
+                    is_register, hr, GuidToString(*entry->pclsid)));
+      return hr;
+    }
   }
-  return hr;
+
+  return S_OK;
+}
+
+HRESULT GoogleUpdate::RegisterOrUnregisterExe(void* data,
+                                              bool is_register) {
+  ASSERT1(data);
+  return reinterpret_cast<GoogleUpdate*>(data)->RegisterOrUnregisterExe(
+      is_register);
+}
+
+HRESULT RegisterOrUnregisterProxies(void* data, bool is_register) {
+  ASSERT1(data);
+  bool is_machine = *reinterpret_cast<bool*>(data);
+  CORE_LOG(L3, (_T("[RegisterOrUnregisterProxies][%d][%d]"),
+                is_machine, is_register));
+
+  CPath ps_dll(app_util::GetCurrentModuleDirectory());
+  if (!ps_dll.Append(is_machine ? kPSFileNameMachine : kPSFileNameUser)) {
+    return HRESULTFromLastError();
+  }
+
+  ASSERT1(!is_register || ps_dll.FileExists());
+  HRESULT hr = is_register ? RegisterDll(ps_dll) : UnregisterDll(ps_dll);
+  CORE_LOG(L3, (_T("[  PS][%s][0x%x]"), ps_dll, hr));
+  if (FAILED(hr) && is_register) {
+    return hr;
+  }
+
+  return S_OK;
 }
 
 HRESULT GoogleUpdate::RegisterServer(BOOL, const CLSID*) throw() {
   HRESULT hr = goopdate_utils::RegisterOrUnregisterModule(
-      true, &RegisterOrUnregisterProxy);
+      is_machine_, true, &RegisterOrUnregisterProxies, &is_machine_);
   if (FAILED(hr)) {
     return hr;
   }
 
-  return goopdate_utils::RegisterOrUnregisterModule(true,
-                                                    &RegisterOrUnregisterExe);
+  return goopdate_utils::RegisterOrUnregisterModule(
+      is_machine_,
+      true,
+      &GoogleUpdate::RegisterOrUnregisterExe,
+      this);
 }
 
 HRESULT GoogleUpdate::UnregisterServer(BOOL, const CLSID*) throw() {
   HRESULT hr = goopdate_utils::RegisterOrUnregisterModule(
-      false, &RegisterOrUnregisterExe);
+      is_machine_, false, &GoogleUpdate::RegisterOrUnregisterExe, this);
   if (FAILED(hr)) {
     return hr;
   }
 
-  return goopdate_utils::RegisterOrUnregisterModule(false,
-                                                    &RegisterOrUnregisterProxy);
+  return goopdate_utils::RegisterOrUnregisterModule(
+      is_machine_, false, &RegisterOrUnregisterProxies, &is_machine_);
 }
 
 HRESULT GoogleUpdate::PreMessageLoop(int show_cmd) throw() {
-  bool is_machine = goopdate_utils::IsRunningFromOfficialGoopdateDir(true);
-  worker_.reset(new Worker(is_machine));
   return CAtlExeModuleT<GoogleUpdate>::PreMessageLoop(show_cmd);
 }
 
 HRESULT GoogleUpdate::PostMessageLoop() throw() {
-  HRESULT hr = CAtlExeModuleT<GoogleUpdate>::PostMessageLoop();
-  worker_.reset();
-  return hr;
+  return CAtlExeModuleT<GoogleUpdate>::PostMessageLoop();
 }
 
 }  // namespace omaha
-
diff --git a/goopdate/google_update.h b/goopdate/google_update.h
index a2e494c..b171942 100644
--- a/goopdate/google_update.h
+++ b/goopdate/google_update.h
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2009-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -16,30 +16,34 @@
 // Contains GoogleUpdate class which is the ATL exe module for the local
 // server that allows launching of the browser at medium integrity.
 
-#ifndef OMAHA_GOOPDATE_GOOGLE_UPDATE_H__
-#define OMAHA_GOOPDATE_GOOGLE_UPDATE_H__
+#ifndef OMAHA_GOOPDATE_GOOGLE_UPDATE_H_
+#define OMAHA_GOOPDATE_GOOGLE_UPDATE_H_
 
 #include <windows.h>
-#include <atlstr.h>
-#include "base/basictypes.h"
+#include <atlbase.h>
 #include "base/scoped_ptr.h"
-#include "omaha/common/atlregmapex.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/goopdate/resource.h"
-
-// Generated by MIDL in the "BUILD_MODE.OBJ_ROOT + SETTINGS.SUBDIR".
-#include "goopdate/google_update_idl.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/base/debug.h"
 
 namespace omaha {
 
-class Worker;
-
+// TODO(omaha): Perhaps a better approach might be to use Adapter classes to
+// modify behavior instead of the mode being explicitly specified here. Consider
+// when making future changes.
+// It might also be a good idea to rename this class and file since it is more
+// a boilerplate for COM servers than it is related to "Google Update" or the
+// main Omaha 3 COM server since it als handles various brokers.
 class GoogleUpdate : public CAtlExeModuleT<GoogleUpdate> {
  public:
-  // We do not register the AppID, because it is not needed, and because it
-  // would be one more thing to redirect for HKCU registration.
+  enum ComServerMode {
+    kUpdate3Mode,
+    kBrokerMode,
+    kOnDemandMode,
+  };
 
-  GoogleUpdate();
+  DECLARE_LIBID(LIBID_GoogleUpdate3Lib)
+
+  explicit GoogleUpdate(bool is_machine, ComServerMode mode);
   ~GoogleUpdate();
   HRESULT RegisterClassObjects(DWORD cls_ctx, DWORD flags) throw();
   HRESULT RevokeClassObjects() throw();
@@ -52,14 +56,48 @@
   HRESULT PostMessageLoop() throw();
   HRESULT Main();
 
-  Worker* worker() { return worker_.get(); }
+  // This is cloned from CAtlExeModuleT.Lock(). The one difference is the call
+  // to ::CoAddRefServerProcess(). See the description for Unlock() below for
+  // further information.
+  virtual LONG Lock() throw() {
+    ::CoAddRefServerProcess();
+    return CComGlobalsThreadModel::Increment(&m_nLockCnt);
+  }
+
+  // This is cloned from CAtlExeModuleT.Unlock(). The big difference is the call
+  // to ::CoReleaseServerProcess(), to ensure that the class factories are
+  // suspended once the lock count drops to zero. This fixes a a race condition
+  // where an activation request could come in in the middle of shutting down.
+  // This shutdown mechanism works with free threaded servers.
+  //
+  // There are race issues with the ATL  delayed shutdown mechanism, hence the
+  // associated code has been eliminated, and we have an assert to make sure
+  // m_bDelayShutdown is not set.
+  virtual LONG Unlock() throw() {
+    ASSERT1(!m_bDelayShutdown);
+
+    ::CoReleaseServerProcess();
+    LONG lRet = CComGlobalsThreadModel::Decrement(&m_nLockCnt);
+
+    if (lRet == 0) {
+      ::PostThreadMessage(m_dwMainThreadID, WM_QUIT, 0, 0);
+    }
+
+    return lRet;
+  }
+
  private:
-  scoped_ptr<Worker> worker_;
+  _ATL_OBJMAP_ENTRY* GetObjectMap();
+  HRESULT RegisterOrUnregisterExe(bool is_register);
+  static HRESULT RegisterOrUnregisterExe(void* data, bool is_register);
+
+  ComServerMode mode_;
+  bool is_machine_;
 
   DISALLOW_EVIL_CONSTRUCTORS(GoogleUpdate);
 };
 
 }  // namespace omaha
 
-#endif  // OMAHA_GOOPDATE_GOOGLE_UPDATE_H__
+#endif  // OMAHA_GOOPDATE_GOOGLE_UPDATE_H_
 
diff --git a/goopdate/google_update3.h b/goopdate/google_update3.h
new file mode 100644
index 0000000..20a43d1
--- /dev/null
+++ b/goopdate/google_update3.h
@@ -0,0 +1,244 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_GOOGLE_UPDATE3_H_
+#define OMAHA_GOOPDATE_GOOGLE_UPDATE3_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include <atlstr.h>
+#include <vector>
+#include "goopdate/omaha3_idl.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/atlregmapex.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/error.h"
+#include "omaha/base/exception_barrier.h"
+#include "omaha/base/preprocessor_fun.h"
+#include "omaha/base/user_rights.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/goopdate/com_proxy.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/non_localized_resource.h"
+#include "omaha/goopdate/worker.h"
+#include "third_party/bar/shared_ptr.h"
+
+namespace omaha {
+
+// The ATL Singleton Class Factory does not work very well if errors happen in
+// CreateInstance(), the server continues running. This is because the module
+// count is not incremented or decremented. This class fixes the issue so that
+// on error, the server shuts down as expected.
+template <class T>
+class SingletonClassFactory : public CComClassFactorySingleton<T> {
+ public:
+  SingletonClassFactory() {}
+  virtual ~SingletonClassFactory() {}
+
+  STDMETHOD(CreateInstance)(LPUNKNOWN unk, REFIID iid, void** obj) {
+    HRESULT hr = CComClassFactorySingleton<T>::CreateInstance(unk, iid, obj);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[SingletonClassFactory::CreateInstance failed][0x%x]")
+                    _T("[pulsing module count]"), hr));
+      LockServer(TRUE);
+      LockServer(FALSE);
+
+      return hr;
+    }
+
+    return hr;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(SingletonClassFactory);
+};
+
+template <bool machine, const TCHAR* const progid, const GUID& clsid,
+          UINT registry_resid, const TCHAR* const hkroot>
+struct Update3COMClassMode {
+  static bool is_machine() { return machine; }
+  static const TCHAR* const prog_id() { return progid; }
+  static GUID class_id() { return clsid; }
+  static UINT registry_res_id() { return registry_resid; }
+  static const TCHAR* const hk_root() { return hkroot; }
+};
+
+#pragma warning(push)
+// C4640: construction of local static object is not thread-safe
+#pragma warning(disable : 4640)
+// C4505: unreferenced IUnknown local functions have been removed
+#pragma warning(disable : 4505)
+
+template <typename T>
+class ATL_NO_VTABLE Update3COMClass
+    : public CComObjectRootEx<CComObjectThreadModel>,
+      public CComCoClass<Update3COMClass<T> >,
+      public IDispatchImpl<IGoogleUpdate3,
+                           &__uuidof(IGoogleUpdate3),
+                           &CAtlModule::m_libid,
+                           kMajorTypeLibVersion,
+                           kMinorTypeLibVersion>,
+      public StdMarshalInfo {
+ public:
+  typedef Update3COMClass<T> Update3COMClassT;
+  typedef SingletonClassFactory<Update3COMClassT> SingletonClassFactoryT;
+
+  Update3COMClass() : StdMarshalInfo(T::is_machine()), model_(NULL) {}
+  virtual ~Update3COMClass() {}
+
+  DECLARE_CLASSFACTORY_EX(SingletonClassFactoryT)
+  DECLARE_NOT_AGGREGATABLE(Update3COMClassT)
+  DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+  DECLARE_REGISTRY_RESOURCEID_EX(T::registry_res_id())
+
+  BEGIN_REGISTRY_MAP()
+    REGMAP_ENTRY(_T("HKROOT"), T::hk_root())
+    REGMAP_EXE_MODULE(_T("MODULE"))
+    REGMAP_ENTRY(_T("VERSION"), _T("1.0"))
+    REGMAP_ENTRY(_T("PROGID"), T::prog_id())
+    REGMAP_ENTRY(_T("DESCRIPTION"), _T("Update3COMClass"))
+    REGMAP_UUID(_T("CLSID"), T::class_id())
+  END_REGISTRY_MAP()
+
+  BEGIN_COM_MAP(Update3COMClassT)
+    COM_INTERFACE_ENTRY(IGoogleUpdate3)
+    COM_INTERFACE_ENTRY(IDispatch)
+    COM_INTERFACE_ENTRY(IStdMarshalInfo)
+  END_COM_MAP()
+
+  STDMETHODIMP get_Count(long* count) {  // NOLINT
+    ASSERT1(count);
+    ExceptionBarrier barrier;
+
+    __mutexScope(model()->lock());
+    *count = model()->GetNumberOfAppBundles();
+
+    return S_OK;
+  }
+
+  STDMETHODIMP get_Item(long index, IDispatch** app_bundle_wrapper) {  // NOLINT
+    ASSERT1(app_bundle_wrapper);
+    ExceptionBarrier barrier;
+
+    if (::IsUserAnAdmin() && !UserRights::VerifyCallerIsAdmin()) {
+      CORE_LOG(LE, (_T("[User is not an admin]")));
+      return E_ACCESSDENIED;
+    }
+
+    __mutexScope(model()->lock());
+
+    const size_t num_app_bundles(model()->GetNumberOfAppBundles());
+    if (index < 0 || static_cast<size_t>(index) >= num_app_bundles) {
+      return HRESULT_FROM_WIN32(ERROR_INVALID_INDEX);
+    }
+    shared_ptr<AppBundle> app_bundle(model()->GetAppBundle(index));
+    return AppBundleWrapper::Create(app_bundle->controlling_ptr(),
+                                    app_bundle.get(),
+                                    app_bundle_wrapper);
+  }
+
+  // Creates an AppBundle object and its corresponding COM wrapper.
+  STDMETHODIMP createAppBundle(IDispatch** app_bundle_wrapper) {
+    ASSERT1(app_bundle_wrapper);
+    ExceptionBarrier barrier;
+
+    __mutexScope(model()->lock());
+
+    shared_ptr<AppBundle> app_bundle(model()->CreateAppBundle(T::is_machine()));
+    return AppBundleWrapper::Create(app_bundle->controlling_ptr(),
+                                    app_bundle.get(),
+                                    app_bundle_wrapper);
+  }
+
+  HRESULT FinalConstruct() {
+    CORE_LOG(L2, (_T("[Update3COMClass::FinalConstruct]")));
+
+    HRESULT hr = InitializeWorker();
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[InitializeWorker failed][0x%x]"), hr));
+      return hr;
+    }
+
+    omaha::interlocked_exchange_pointer(&model_, Worker::Instance().model());
+    ASSERT1(model());
+
+    return S_OK;
+  }
+
+  void FinalRelease() {
+    CORE_LOG(L2, (_T("[Update3COMClass::FinalRelease]")));
+  }
+
+ private:
+  static HRESULT InitializeWorker() {
+    static LLock lock;
+    static bool is_initialized = false;
+
+    __mutexScope(lock);
+
+    if (is_initialized) {
+      return S_OK;
+    }
+
+    CORE_LOG(L2, (_T("[InitializeWorker][%d]"), T::is_machine()));
+
+    HRESULT hr = Worker::Instance().Initialize(T::is_machine());
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    is_initialized = true;
+    return S_OK;
+  }
+
+  Model* model() {
+    return omaha::interlocked_exchange_pointer(&model_, model_);
+  }
+
+  // C++ root of the object model. Not owned by this instance.
+  mutable Model* volatile model_;
+
+  DISALLOW_COPY_AND_ASSIGN(Update3COMClass);
+};
+
+#pragma warning(pop)
+
+extern TCHAR kHKRootUser[];
+extern TCHAR kHKRootMachine[];
+extern TCHAR kHKRootService[];
+extern TCHAR kProgIDUpdate3COMClassUserLocal[];
+extern TCHAR kProgIDUpdate3COMClassMachineLocal[];
+extern TCHAR kProgIDUpdate3COMClassServiceLocal[];
+
+typedef Update3COMClassMode<false,
+                            kProgIDUpdate3COMClassUserLocal,
+                            __uuidof(GoogleUpdate3UserClass),
+                            IDR_LOCAL_SERVER_RGS,
+                            kHKRootUser> Update3COMClassModeUser;
+
+typedef Update3COMClassMode<true,
+                            kProgIDUpdate3COMClassServiceLocal,
+                            __uuidof(GoogleUpdate3ServiceClass),
+                            IDR_LOCAL_SERVICE_RGS,
+                            kHKRootService> Update3COMClassModeService;
+
+typedef Update3COMClass<Update3COMClassModeUser> Update3COMClassUser;
+typedef Update3COMClass<Update3COMClassModeService> Update3COMClassService;
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_GOOGLE_UPDATE3_H_
diff --git a/goopdate/google_update_idl.idl b/goopdate/google_update_idl.idl
deleted file mode 100644
index 7140aa4..0000000
--- a/goopdate/google_update_idl.idl
+++ /dev/null
@@ -1,239 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// The proxy clsid #defined as PROXY_CLSID_IS in mk_common needs to be changed
-// anytime any interface below changes, or if a new interface is added.
-
-import "oaidl.idl";
-import "ocidl.idl";
-
-[
-  object,
-  uuid(5B25A8DC-1780-4178-A629-6BE8B8DEFAA2),
-  oleautomation,
-  nonextensible,
-  pointer_default(unique)
-]
-interface IBrowserHttpRequest2 : IUnknown {
-  // This method will send request/data from the browser process.
-  // @param url                     URL where request will be send.
-  // @param post_data               POST data, if any. Can be NULL.
-  // @param request_headers         HTTP request headers, if any. Can be NULL.
-  // @param response_headers_needed HTTP response headers that are needed.
-  //                                Should be one of the values listed here:
-  //                                    http://msdn.microsoft.com/aa385351.aspx
-  //                                The input is a SAFEARRAY of DWORD. Can be a
-  //                                VT_EMPTY.
-  // @param response_headers        HTTP response headers, returned as SAFEARRAY
-  //                                of BSTR. The values corresponding one-to-one
-  //                                with the response_headers_needed values. Can
-  //                                be NULL if response_headers_needed==VT_EMPTY
-  // @param response_code           HTTP response code.
-  // @param cache_filename          Cache file that contains the response data.
-  HRESULT Send([in] BSTR url,
-               [in] BSTR post_data,
-               [in] BSTR request_headers,
-               [in] VARIANT response_headers_needed,
-               [out] VARIANT* response_headers,
-               [out] DWORD* response_code,
-               [out] BSTR* cache_filename);
-};
-
-[
-  object,
-  oleautomation,
-  uuid(128C2DA6-2BC0-44c0-B3F6-4EC22E647964),
-  helpstring("Google Update IProcessLauncher Interface"),
-  pointer_default(unique)
-]
-interface IProcessLauncher : IUnknown {
-  // @param cmd_line The full command line to execute.
-  HRESULT LaunchCmdLine([in, string] const WCHAR* cmd_line);
-
-  // @param browser_type The browser to start.
-  // @param url The url to launch the browser with.
-  HRESULT LaunchBrowser([in] DWORD browser_type,
-                        [in, string] const WCHAR* url);
-
-  // @param app_id Unique id to identify the calling client application
-  // @param event_id Unique id for the command
-  // @param caller_proc_id The process id of the calling process
-  // @param proc_handle The process handle valid in the caller's context
-  HRESULT LaunchCmdElevated([in, string] const WCHAR* app_guid,
-                            [in, string] const WCHAR* cmd_id,
-                            [in] DWORD caller_proc_id,
-                            [out] ULONG_PTR* proc_handle);
-};
-
-typedef enum {
-  COMPLETION_CODE_SUCCESS = 1,
-  COMPLETION_CODE_SUCCESS_CLOSE_UI,
-  COMPLETION_CODE_ERROR,
-  COMPLETION_CODE_RESTART_ALL_BROWSERS,
-  COMPLETION_CODE_REBOOT,
-  COMPLETION_CODE_RESTART_BROWSER,
-  COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY,
-  COMPLETION_CODE_REBOOT_NOTICE_ONLY,
-  COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY,
-  COMPLETION_CODE_RUN_COMMAND,
-} CompletionCodes;
-
-[
-  object,
-  oleautomation,
-  uuid(1C642CED-CA3B-4013-A9DF-CA6CE5FF6503),
-  helpstring("GoogleUpdate UI-specific events Interface"),
-  pointer_default(unique)
-]
-interface IProgressWndEvents : IUnknown {
-  // The UI is closing down. The user has clicked on either the "X" or the
-  // other buttons of the UI to close the window.
-  HRESULT DoClose();
-
-  // Pause has been clicked on.
-  HRESULT DoPause();
-
-  // Resume has been clicked on.
-  HRESULT DoResume();
-
-  // RestartBrowsers button has been clicked on.
-  HRESULT DoRestartBrowsers();
-
-  // Reboot button has been clicked on.
-  HRESULT DoReboot();
-
-  // Launch Browser.
-  HRESULT DoLaunchBrowser([in, string] const WCHAR* url);
-};
-
-
-[
-  object,
-  oleautomation,
-  uuid(49D7563B-2DDB-4831-88C8-768A53833837),
-  helpstring("IJobObserver Interface"),
-  pointer_default(unique)
-]
-interface IJobObserver : IUnknown {
-  HRESULT OnShow();
-  HRESULT OnCheckingForUpdate();
-  HRESULT OnUpdateAvailable([in, string] const WCHAR* version_string);
-  HRESULT OnWaitingToDownload();
-  HRESULT OnDownloading([in] int time_remaining_ms, [in] int pos);
-  HRESULT OnWaitingToInstall();
-  HRESULT OnInstalling();
-  HRESULT OnPause();
-  HRESULT OnComplete([in] CompletionCodes code,
-                     [in, string] const WCHAR* reserved);
-  HRESULT SetEventSink([in] IProgressWndEvents* ui_sink);
-};
-
-[
-  object,
-  oleautomation,
-  uuid(31AC3F11-E5EA-4a85-8A3D-8E095A39C27B),
-  helpstring("IGoogleUpdate Interface"),
-  pointer_default(unique)
-]
-interface IGoogleUpdate : IUnknown {
-  // @param guid The guid for the app to be updated.
-  // @param observer The eventing interface.
-  HRESULT CheckForUpdate([in, string] const WCHAR* guid,
-                         [in] IJobObserver* observer);
-
-  // @param guid The guid for the app to be updated.
-  // @param observer The eventing interface.
-  HRESULT Update([in, string] const WCHAR* guid,
-                 [in] IJobObserver* observer);
-};
-
-// IGoogleUpdateCore is an internal Omaha interface.
-[
-  object,
-  oleautomation,
-  uuid(909489C2-85A6-4322-AA56-D25278649D67),
-  helpstring("Google Update Core Interface"),
-  pointer_default(unique)
-]
-interface IGoogleUpdateCore : IUnknown
-{
-  // Runs a command elevated.
-  //
-  // @param app_id Unique id to identify the calling client application
-  // @param event_id Unique id for the command
-  // @param caller_proc_id The process id of the calling process
-  // @param proc_handle The process handle valid in the caller's context
-  HRESULT LaunchCmdElevated([in, string] const WCHAR* app_guid,
-                            [in, string] const WCHAR* cmd_id,
-                            [in] DWORD caller_proc_id,
-                            [out] ULONG_PTR* proc_handle);
-};
-
-[
-  uuid(7E6CD20B-8688-4960-96D9-B979471577B8),
-  version(1.0),
-  helpstring("Google Update Type Library")
-]
-library GoogleUpdateLib {
-  importlib("stdole2.tlb");
-  [
-    uuid(ABC01078-F197-4b0b-ADBC-CFE684B39C82),
-    helpstring("ProcessLauncherClass Class")
-  ]
-  coclass ProcessLauncherClass {
-    [default] interface IProcessLauncher;
-  }
-
-  // This coclass declaration exists only for the purpose of forcing
-  // ::RegisterTypeLib() to register the interfaces within. This is
-  // required so that we can marshal/unmarshal the interfaces using the TypeLib
-  // marshaler.
-  [
-    uuid(9564861C-3469-4c9a-956A-74D5690790E6),
-    helpstring("InterfaceRegistrar Class")
-  ]
-  coclass InterfaceRegistrar {
-    [default] interface IBrowserHttpRequest2;
-    interface IJobObserver;
-    interface IProgressWndEvents;
-  }
-
-  [
-    uuid(2F0E2680-9FF5-43c0-B76E-114A56E93598),
-    helpstring("OnDemand updates for per-user applications.")
-  ]
-  coclass OnDemandUserAppsClass {
-    [default] interface IGoogleUpdate;
-  }
-
-  [
-    uuid(6F8BD55B-E83D-4a47-85BE-81FFA8057A69),
-    helpstring("OnDemand updates for per-machine applications.")
-  ]
-  coclass OnDemandMachineAppsClass {
-    [default] interface IGoogleUpdate;
-  }
-
-  [
-    uuid(E225E692-4B47-4777-9BED-4FD7FE257F0E),
-    helpstring("GoogleUpdateCore Class")
-  ]
-  coclass GoogleUpdateCoreClass
-  {
-    [default] interface IGoogleUpdateCore;
-  }
-
-};
-
diff --git a/goopdate/google_update_idl_datax.c b/goopdate/google_update_idl_datax.c
deleted file mode 100644
index 5f3c91e..0000000
--- a/goopdate/google_update_idl_datax.c
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// google_update_idl_datax.c: Wrapper for the MIDL-generated proxy/stub.
-
-#pragma warning(push)
-// C4255: no function prototype given: converting '()' to '(void)'
-#pragma warning(disable : 4255)
-// C4152: nonstandard extension, function/data pointer conversion in expression
-#pragma warning(disable : 4152)
-
-#define REGISTER_PROXY_DLL
-#define USE_STUBLESS_PROXY
-#define ENTRY_PREFIX      Prx
-
-// Undefine the __purecall provided by rpcproxy.h so that it does not conflict
-// with the libc definition.
-#include <rpcproxy.h>
-#ifdef DLLDUMMYPURECALL
-#undef DLLDUMMYPURECALL
-#define DLLDUMMYPURECALL
-#endif
-
-#include "goopdate/google_update_idl_data.c"
-#include "goopdate/google_update_idl_p.c"
-
-#pragma warning(pop)
-
diff --git a/goopdate/google_update_idl_datax.h b/goopdate/google_update_idl_datax.h
deleted file mode 100644
index 3cb26af..0000000
--- a/goopdate/google_update_idl_datax.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// google_update_idl_datax.h: Declarations for the MIDL-generated entry points
-// for the proxy/stub.
-
-#ifndef OMAHA_GOOPDATE_GOOGLE_UPDATE_IDL_DATAX_H_
-#define OMAHA_GOOPDATE_GOOGLE_UPDATE_IDL_DATAX_H_
-
-extern "C" {
-  BOOL WINAPI PrxDllMain(HINSTANCE instance, DWORD reason, LPVOID res);
-  STDAPI PrxDllCanUnloadNow();
-  STDAPI PrxDllGetClassObject(REFCLSID refclsid, REFIID refiid, LPVOID* ptr);
-  STDAPI PrxDllRegisterServer();
-  STDAPI PrxDllUnregisterServer();
-}
-
-#endif  // OMAHA_GOOPDATE_GOOGLE_UPDATE_IDL_DATAX_H_
-
diff --git a/goopdate/google_update_idl_ps.def b/goopdate/google_update_idl_ps.def
new file mode 100644
index 0000000..5248b7c
--- /dev/null
+++ b/goopdate/google_update_idl_ps.def
@@ -0,0 +1,20 @@
+; Copyright 2010 Google Inc.
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+; ========================================================================
+
+
+EXPORTS        DllGetClassObject    PRIVATE
+               DllCanUnloadNow      PRIVATE
+               DllRegisterServer    PRIVATE
+               DllUnregisterServer  PRIVATE
diff --git a/goopdate/google_update_proxy.h b/goopdate/google_update_proxy.h
index 668702e..6b39a47 100644
--- a/goopdate/google_update_proxy.h
+++ b/goopdate/google_update_proxy.h
@@ -14,7 +14,7 @@
 // ========================================================================
 //
 // Defines SharedMemoryProxy to encapsulate marshaling and unmarshaling of
-// IGoogleUpdate and other interface pointers across process boundaries.
+// IGoogleUpdate3 and other interface pointers across process boundaries.
 //
 // TODO(omaha): seems possible to make it general purpose and move it to common.
 #ifndef OMAHA_GOOPDATE_GOOGLE_UPDATE_PROXY_H__
@@ -23,14 +23,13 @@
 #include <atlbase.h>
 #include <atlsecurity.h>
 #include "base/basictypes.h"
-#include "goopdate/google_update_idl.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/shared_memory_ptr.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/shared_memory_ptr.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
 
 namespace omaha {
 
diff --git a/goopdate/google_update_ps.cc b/goopdate/google_update_ps.cc
new file mode 100644
index 0000000..3de7216
--- /dev/null
+++ b/goopdate/google_update_ps.cc
@@ -0,0 +1,104 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <atlbase.h>
+#include "base/basictypes.h"
+#include "omaha/base/utils.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/goopdate/com_proxy.h"
+#include "omaha/goopdate/current_state.h"
+#include "omaha/goopdate/omaha3_idl_datax.h"
+
+namespace omaha {
+
+// TODO(omaha): Try to see if a single proxy DLL can be used for both user and
+// machine.
+
+#if IS_MACHINE_HANDLER
+  OBJECT_ENTRY_AUTO(__uuidof(GoogleComProxyMachineClass), ComProxy)
+  OBJECT_ENTRY_AUTO(__uuidof(CurrentStateMachineClass), CurrentAppState)
+#else
+  OBJECT_ENTRY_AUTO(__uuidof(GoogleComProxyUserClass), ComProxy)
+  OBJECT_ENTRY_AUTO(__uuidof(CurrentStateUserClass), CurrentAppState)
+#endif
+
+namespace {
+
+class GoogleUpdatePSModule
+    : public CAtlDllModuleT<GoogleUpdatePSModule> {
+ public:
+  GoogleUpdatePSModule() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GoogleUpdatePSModule);  // NOLINT
+} _AtlModule;
+
+}  // namespace
+
+}  // namespace omaha
+
+BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) {
+  HRESULT hr = omaha::_AtlModule.DllMain(reason, reserved);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return PrxDllMain(instance, reason, reserved);
+}
+
+STDAPI DllCanUnloadNow() {
+  if (omaha::_AtlModule.DllCanUnloadNow() == S_OK &&
+      PrxDllCanUnloadNow() == S_OK) {
+    return S_OK;
+  }
+
+  return S_FALSE;
+}
+
+STDAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void** ptr) {
+  HRESULT hr_atl = omaha::_AtlModule.DllGetClassObject(clsid, iid, ptr);
+  if (SUCCEEDED(hr_atl)) {
+    return hr_atl;
+  }
+
+  HRESULT hr_prx = PrxDllGetClassObject(clsid, iid, ptr);
+  if (FAILED(hr_prx)) {
+    CORE_LOG(LE, (_T("[DllGetClassObject failed][%s][%s][0x%x][0x%x]"),
+        omaha::GuidToString(clsid), omaha::GuidToString(iid), hr_atl, hr_prx));
+  }
+
+  return hr_prx;
+}
+
+STDAPI DllRegisterServer() {
+  HRESULT hr = omaha::_AtlModule.DllRegisterServer(false);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[DllRegisterServer failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return PrxDllRegisterServer();
+}
+
+STDAPI DllUnregisterServer() {
+  HRESULT hr = PrxDllUnregisterServer();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[DllUnregisterServer failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return omaha::_AtlModule.DllUnregisterServer(false);
+}
+
diff --git a/goopdate/google_update_ps.def b/goopdate/google_update_ps.def
new file mode 100644
index 0000000..5248b7c
--- /dev/null
+++ b/goopdate/google_update_ps.def
@@ -0,0 +1,20 @@
+; Copyright 2010 Google Inc.
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+; ========================================================================
+
+
+EXPORTS        DllGetClassObject    PRIVATE
+               DllCanUnloadNow      PRIVATE
+               DllRegisterServer    PRIVATE
+               DllUnregisterServer  PRIVATE
diff --git a/goopdate/google_update_ps_resource.h b/goopdate/google_update_ps_resource.h
new file mode 100644
index 0000000..8592e2c
--- /dev/null
+++ b/goopdate/google_update_ps_resource.h
@@ -0,0 +1,22 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_GOOGLE_UPDATE_PS_RESOURCE_H_
+#define OMAHA_GOOPDATE_GOOGLE_UPDATE_PS_RESOURCE_H_
+
+#define IDR_INPROC_SERVER_RGS                    101
+#define IDR_COM_PROXY_RGS                        103
+
+#endif  // OMAHA_GOOPDATE_GOOGLE_UPDATE_PS_RESOURCE_H_
diff --git a/goopdate/google_update_ps_resource.rc b/goopdate/google_update_ps_resource.rc
new file mode 100644
index 0000000..9875924
--- /dev/null
+++ b/goopdate/google_update_ps_resource.rc
@@ -0,0 +1,26 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// These include headers with absolute paths, which Resource Editor cannot load.
+#ifndef APSTUDIO_INVOKED
+#include "omaha/base/const_config.h"
+#include "omaha/goopdate/google_update_ps_resource.h"
+#endif  // APSTUDIO_INVOKED
+
+IDR_INPROC_SERVER_RGS REGISTRY "omaha/base/generic_reg_file_dll.rgs"
+IDR_COM_PROXY_RGS     REGISTRY "omaha/base/generic_reg_file_dll_handler.rgs"
+
+1 TYPELIB "goopdate\\omaha3_idl.tlb"
+
diff --git a/goopdate/goopdate-internal.h b/goopdate/goopdate-internal.h
deleted file mode 100644
index e20b104..0000000
--- a/goopdate/goopdate-internal.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_GOOPDATE_GOOPDATE_INTERNAL_H_
-#define OMAHA_GOOPDATE_GOOPDATE_INTERNAL_H_
-
-#include <windows.h>
-
-namespace omaha {
-
-namespace internal {
-
-// Marks Google Update's EULA as accepted if an app EULA has been accepted.
-HRESULT PromoteAppEulaAccepted(bool is_machine);
-
-}  // namespace internal
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_GOOPDATE_INTERNAL_H_
-
diff --git a/goopdate/goopdate.cc b/goopdate/goopdate.cc
index 974cf2f..aeda0aa 100644
--- a/goopdate/goopdate.cc
+++ b/goopdate/goopdate.cc
@@ -19,108 +19,86 @@
 // before potentially waiting on a firewall prompt.
 //
 // Debugging notes:
-//  * Omaha (and app) initial install:
-//   * File install can be debugged with the following command arguments:
+//  * Omaha initial install:
 //      /install "appguid={8A69D345-D564-463C-AFF1-A69D9E530F96}&appname=Google%20Chrome&needsadmin=False&lang=en"  // NOLINT
 //      /install "appguid={283EAF47-8817-4c2b-A801-AD1FADFB7BAA}&appname=Gears&needsadmin=True&lang=en"  // NOLINT
-//   * If elevation is required, another instance is launched. Run elevated to
-//     continue debugging.
-//   * Once the initial files have been copied, another instance is launched
-//     from the installed location. To continue debugging, use the following
-//     command arguments:
-//      /ig "appguid={8A69D345-D564-463C-AFF1-A69D9E530F96}&appname=Google%20Chrome&needsadmin=False&lang=en"  // NOLINT
-//      /ig "appguid={283EAF47-8817-4c2b-A801-AD1FADFB7BAA}&appname=Gears&needsadmin=True&lang=en"  // NOLINT
-//  * App hand-off initial install or over-install:
+//  * App install:
 //      /handoff "appguid={8A69D345-D564-463C-AFF1-A69D9E530F96}&appname=Google%20Chrome&needsadmin=False&lang=en"  // NOLINT
 //      /handoff "appguid={283EAF47-8817-4c2b-A801-AD1FADFB7BAA}&appname=Gears&needsadmin=True&lang=en"  // NOLINT
 //  * Silent install:
 //   * Add "/silent" to any of the above command lines (not to the tag).
 //  * Google Update self-update:
-//   * File install can be debugged with the following command arguments:
 //      /update
-//   * Once the initial files have been copied, another instance is launched
-//     from the installed location. To continue debugging, use the following
-//     command arguments:
-//      /ug
 //  * Update check for apps that need it:
 //      /ua
-//  * Legacy hand-off install (Occurs from the machine install location)
-//   * First /UI /lang en legacy_manifest_path
-//   * This then launches a worker with:
-//      /handoff "appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&appname=YouTube Uploader&needsadmin=False" /lang en  // NOLINT
 //  * Core:
 //      /c
 //  * Cod Red check:
 //      /cr
 //  * Cod Red repair:
-//   * Determining whether to elevate and non-elevated file install can be
-//     debugged with the following command arguments:
 //      /recover [/machine]
-//   * Once the initial files have been copied, another instance is launched
-//     from the installed location. To continue debugging, use the following
-//     command arguments:
-//      /ug [/machine]
 //  * OneClick:
 //      /pi "http://www.google.com/" "/install%20%22appguid=%7B8A69D345-D564-463C-AFF1-A69D9E530F96%7D%26lang=en%26appname=Google%2520Chrome%26needsadmin=false" /installsource oneclick  // NOLINT
+//  * COM server:
+//      -Embedding
 
 #include "omaha/goopdate/goopdate.h"
 
 #include <atlstr.h>
 #include <new>
 #include "base/scoped_ptr.h"
-#include "omaha/common/browser_utils.h"
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/google_update_recovery.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/module_utils.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/proc_utils.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/common/system_info.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/crash_if_specific_error.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/module_utils.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/proc_utils.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/client/client_utils.h"
+#include "omaha/client/install.h"
+#include "omaha/client/install_apps.h"
+#include "omaha/client/install_self.h"
+#include "omaha/client/resource.h"  // IDS_* are used in client modes only.
+#include "omaha/client/ua.h"
+#include "omaha/common/app_registry_utils.h"
+#include "omaha/common/command_line.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/ping.h"
+#include "omaha/common/lang.h"
+#include "omaha/common/oem_install_utils.h"
+#include "omaha/common/stats_uploader.h"
+#include "omaha/common/webplugin_utils.h"
 #include "omaha/core/core.h"
 #include "omaha/core/crash_handler.h"
-#include "omaha/goopdate/browser_launcher.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/command_line_builder.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
+#include "omaha/goopdate/code_red_check.h"
 #include "omaha/goopdate/crash.h"
-#include "omaha/goopdate/goopdate_helper.h"
 #include "omaha/goopdate/google_update.h"
+#include "omaha/goopdate/goopdate_internal.h"
 #include "omaha/goopdate/goopdate_metrics.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/goopdate_xml_parser.h"
-#include "omaha/goopdate/goopdate-internal.h"
-#include "omaha/goopdate/stats_uploader.h"
-#include "omaha/goopdate/webplugin_utils.h"
+#include "omaha/goopdate/resource_manager.h"
 #include "omaha/net/net_diags.h"
-#include "omaha/net/browser_request.h"
-#include "omaha/net/network_config.h"
-#include "omaha/net/network_request.h"
-#include "omaha/net/bits_request.h"
-#include "omaha/net/simple_request.h"
-#include "omaha/recovery/repair_exe/repair_goopdate.h"
 #include "omaha/service/service_main.h"
-#include "omaha/setup/setup.h"
 #include "omaha/setup/setup_service.h"
-#include "omaha/worker/app_request_data.h"
-#include "omaha/worker/application_data.h"
-#include "omaha/worker/application_manager.h"
-#include "omaha/worker/ping.h"
-#include "omaha/worker/worker.h"
-#include "omaha/worker/worker_com_wrapper.h"
-#include "omaha/worker/worker_event_logger.h"
 #include "third_party/breakpad/src/client/windows/sender/crash_report_sender.h"
 #include "third_party/breakpad/src/client/windows/handler/exception_handler.h"
 
-// Generated by MIDL into $OBJ_ROOT/goopdate.
-#include "goopdate/google_update_idl.h"
+// TODO(omaha3): Where should we put this? In Omaha 2, it was in worker.cc.
+// TODO(omaha): fix this clunkiness and require explicit registration of the
+// http creators with the factory. Not ideal but better then linker options.
+#pragma comment(linker, "/INCLUDE:_kRegisterWinHttp")
 
 namespace omaha {
 
@@ -138,6 +116,61 @@
 const TCHAR* const kOfficialBuild = _T("dev");
 #endif
 
+#if DEBUG
+// Returns true if the binary's version matches the installed version or this
+// mode does not require the versions to match.
+bool CheckRegisteredVersion(const CString& version,
+                            bool is_machine,
+                            CommandLineMode mode) {
+  switch (mode) {
+    // Modes that may run before or during installation or otherwise do not
+    // need to match the installed version.
+    case COMMANDLINE_MODE_UNKNOWN:
+    case COMMANDLINE_MODE_NOARGS:
+    case COMMANDLINE_MODE_REGSERVER:
+    case COMMANDLINE_MODE_UNREGSERVER:
+    case COMMANDLINE_MODE_NETDIAGS:
+    case COMMANDLINE_MODE_CRASH:
+    case COMMANDLINE_MODE_REPORTCRASH:
+    case COMMANDLINE_MODE_INSTALL:
+    case COMMANDLINE_MODE_UPDATE:
+    case COMMANDLINE_MODE_RECOVER:
+    case COMMANDLINE_MODE_WEBPLUGIN:
+    case COMMANDLINE_MODE_REGISTER_PRODUCT:
+    case COMMANDLINE_MODE_UNREGISTER_PRODUCT:
+    case COMMANDLINE_MODE_SERVICE_REGISTER:
+    case COMMANDLINE_MODE_SERVICE_UNREGISTER:
+    case COMMANDLINE_MODE_PING:
+      return true;
+
+    // COM servers and services that should only run after installation.
+    case COMMANDLINE_MODE_CORE:
+    case COMMANDLINE_MODE_SERVICE:
+    case COMMANDLINE_MODE_CODE_RED_CHECK:
+    case COMMANDLINE_MODE_COMSERVER:
+    case COMMANDLINE_MODE_CRASH_HANDLER:
+    case COMMANDLINE_MODE_COMBROKER:
+    case COMMANDLINE_MODE_ONDEMAND:
+    case COMMANDLINE_MODE_MEDIUM_SERVICE:
+
+    // Clients that should only run after installation and should match the
+    // installed version.
+    case COMMANDLINE_MODE_HANDOFF_INSTALL:
+    case COMMANDLINE_MODE_UA:
+    case COMMANDLINE_MODE_UNINSTALL:
+
+    default:
+      // This binary's version should be the installed version.
+      CString installed_version;
+      VERIFY1(SUCCEEDED(RegKey::GetValue(
+          ConfigManager::Instance()->registry_update(is_machine),
+          kRegValueInstalledVersion,
+          &installed_version)));
+      return version == installed_version;
+  }
+}
+#endif
+
 }  // namespace
 
 namespace detail {
@@ -149,25 +182,37 @@
 
   HRESULT Main(HINSTANCE instance, const TCHAR* cmd_line, int cmd_show);
 
-  bool is_local_system() const { return is_local_system_; }
+  HRESULT QueueUserWorkItem(UserWorkItem* work_item, uint32 flags);
 
-  CommandLineArgs args() const { return args_; }
-  CString cmd_line() const { return cmd_line_; }
+  void Stop();
+
+  bool is_local_system() const { return is_local_system_; }
 
  private:
   HRESULT DoMain(HINSTANCE instance, const TCHAR* cmd_line, int cmd_show);
 
+  // Performs initialization that must be done as soon as the command line has
+  // been parsed and loads the resources. If this method succeeds, we can use
+  // the resources - for example, to display error messagse.
+  HRESULT InitializeGoopdateAndLoadResources();
+
+  // Executes the mode determined by DoMain().
+  HRESULT ExecuteMode(bool* has_ui_been_displayed);
+
   // Returns whether a process is a machine process.
   // Does not determine whether the process has the appropriate privileges.
   bool IsMachineProcess();
 
+  bool ShouldCheckShutdownEvent(CommandLineMode mode);
+  bool IsShutdownEventSet();
+
   HRESULT LoadResourceDllIfNecessary(CommandLineMode mode,
                                      const CString& resource_dir);
 
   HRESULT SetUsageStatsEnable();
 
-  // Handles error conditions by showing UI.
-  HRESULT HandleError(HRESULT hr);
+  // Handles error conditions by showing UI if appropriate.
+  void HandleError(HRESULT hr, bool has_ui_been_displayed);
 
   // Handles response to /pi command.
   HRESULT HandleWebPlugin();
@@ -175,45 +220,37 @@
   // Handles responses to /cr command.
   HRESULT HandleCodeRedCheck();
 
-  // Handles /ui command.
-  HRESULT HandleLegacyUI();
-
   // Handles /report command.
   HRESULT HandleReportCrash();
 
-  // Handles /uiuser command.
-  HRESULT HandleLegacyManifestHandoff();
+  // Installs apps for the /handoff instance.
+  HRESULT DoHandoff(bool* has_ui_been_displayed);
 
-  // Download Callback for Code Red.
-  static HRESULT CodeRedDownloadCallback(const TCHAR* url,
-                                         const TCHAR* file_path,
-                                         void*);
-  HRESULT DoWorker();
+  // Updates all registered apps for the /ua instance.
+  HRESULT DoUpdateAllApps(bool* has_ui_been_displayed);
 
   // Generates a divide by zero to trigger breakpad dump.
   // Is only enabled in debug builds.
   HRESULT DoCrash();
 
-  // Register a product with Goopdate and install Goopdate.
-  HRESULT DoRegisterProduct();
+  // Install Omaha.
+  HRESULT DoInstall(bool* has_ui_been_displayed);
 
-  // Does the work for DoRegisterProduct().
-  HRESULT DoRegisterProductHelper(bool is_machine,
-                                  AppManager* app_manager,
-                                  AppData* app_data);
-
-  // Unregister a product from Goopdate.
-  HRESULT DoUnregisterProduct();
-
-  // Setup phase1 for self update.
+  // Silently update Omaha.
   HRESULT DoSelfUpdate();
 
-  // Setup phase2 for self update.
-  HRESULT DoCompleteSelfUpdate();
-
   // Handles the recover command in Google Update.
   HRESULT DoRecover();
 
+  // Uninstalls Omaha if appropriate.
+  HRESULT HandleUninstall();
+
+  // Pings the Omaha server with a string.
+  HRESULT HandlePing();
+
+  // TODO(omaha): Reconcile the two uninstall functions and paths.
+  void MaybeUninstallGoogleUpdate();
+
   // Uninstalls Google Update if a /install process failed to install itself
   // or the app and there are no other apps registered.
   HRESULT UninstallIfNecessary();
@@ -222,6 +259,8 @@
   // a request for additional storage.
   static void OutOfMemoryHandler();
 
+  static HRESULT CaptureOSMetrics();
+
   HINSTANCE module_instance_;  // Current module instance.
   CString cmd_line_;           // Command line, as provided by the OS.
   int cmd_show_;
@@ -239,7 +278,8 @@
   // Language identifier for the current user locale.
   CString user_default_language_id_;
 
-  scoped_ptr<ResourceManager> resource_manager_;
+  scoped_ptr<ThreadPool>      thread_pool_;
+
   Goopdate* goopdate_;
 
   DISALLOW_EVIL_CONSTRUCTORS(GoopdateImpl);
@@ -248,7 +288,6 @@
 GoopdateImpl::GoopdateImpl(Goopdate* goopdate, bool is_local_system)
     : module_instance_(NULL),
       cmd_show_(0),
-      is_machine_(false),
       is_local_system_(is_local_system),
       has_uninstalled_(false),
       goopdate_(goopdate) {
@@ -256,10 +295,10 @@
 
   ++metric_goopdate_constructor;
 
-  // The command line needs to be parsed to accurately determine if the
-  // current process is a machine process or not. Use the value of
-  // is_local_system until that.
-  is_machine_ = is_local_system_;
+  // The command line needs to be parsed to accurately determine if the current
+  // process is a machine process or not. Take an upfront guess before that.
+  is_machine_ = vista_util::IsUserAdmin() &&
+      goopdate_utils::IsRunningFromOfficialGoopdateDir(true);
 
   // Install an error-handling mechanism which gets called when new operator
   // fails to allocate memory.
@@ -268,8 +307,28 @@
   // Install the exception handler.
   VERIFY1(SUCCEEDED(Crash::InstallCrashHandler(is_machine_)));
 
+  // Hints network configure manager how to create its singleton.
+  NetworkConfigManager::set_is_machine(is_machine_);
+
   // Initialize the global metrics collection.
   stats_report::g_global_metrics.Initialize();
+
+  // TODO(omaha): Support multiple HRESULT codes to crash on.
+  // TODO(omaha): Support passing in HRESULT codes via the command line,
+  // especially for "/update".
+  DWORD crash_specific_error = 0;
+  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                                 kRegValueNameCrashIfSpecificError,
+                                 &crash_specific_error))) {
+    omaha::g_crash_specific_error = static_cast<HRESULT>(crash_specific_error);
+  }
+
+  static const int kThreadPoolShutdownDelayMs = 60000;
+  thread_pool_.reset(new ThreadPool);
+  HRESULT hr = thread_pool_->Initialize(kThreadPoolShutdownDelayMs);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[thread_pool_->Initialize failed][0x%08x]"), hr));
+  }
 }
 
 GoopdateImpl::~GoopdateImpl() {
@@ -277,6 +336,8 @@
 
   ++metric_goopdate_destructor;
 
+  Stop();
+
   // Bug 994348 does not repro anymore.
   // If the assert fires, clean up the key, and fix the code if we have unit
   // tests or application code that create the key.
@@ -296,62 +357,67 @@
 
   // Reset the new handler.
   set_new_handler(NULL);
-
-  // This check must be the last thing before exiting the process.
-  if (COMMANDLINE_MODE_INSTALL == args_.mode && args_.is_oem_set ||
-      ConfigManager::Instance()->IsOemInstalling(is_machine_)) {
-    ASSERT1(RegKey::HasValue(MACHINE_REG_UPDATE, kRegValueOemInstallTimeSec) ||
-            !is_machine_ ||
-            !vista_util::IsUserAdmin() ||
-            !ConfigManager::Instance()->IsWindowsInstalling());
-  }
 }
 
-HRESULT GoopdateImpl::HandleError(HRESULT hr) {
-  // TODO(Omaha): An error dialog should ideally be shown for all
-  // errors that Main encounters. This will require a bit of work to
-  // ensure that we do not end up showing multiple dialogs, that we
-  // have resources to show a localized error message, that we show
-  // a message correctly even if we have not parsed the command line,
-  // or encounter an error in command line parsing, etc.
+HRESULT GoopdateImpl::QueueUserWorkItem(UserWorkItem* work_item, uint32 flags) {
+  CORE_LOG(L3, (_T("[GoopdateImpl::QueueUserWorkItem]")));
+  ASSERT1(work_item);
 
-#pragma warning(push)
-// C4061: enumerator 'xxx' in switch of enum 'yyy' is not explicitly handled by
-// a case label.
-#pragma warning(disable : 4061)
-  switch (args_.mode) {
-    case COMMANDLINE_MODE_HANDOFF_INSTALL:
-    case COMMANDLINE_MODE_IG:
-    case COMMANDLINE_MODE_INSTALL: {
-      CString primary_app_name;
-      ASSERT1(!args_.extra.apps.empty());
-      if (!args_.extra.apps.empty()) {
-        primary_app_name = args_.extra.apps[0].app_name;
-      }
+  ASSERT1(thread_pool_.get());
 
-      CString error_text;
-      switch (hr) {
-        // TODO(omaha): It would be nice if we could display this in the full
-        // UI so we can provide a link to the Help Center.
-        case OMAHA_NET_E_WINHTTP_NOT_AVAILABLE:
-          error_text.FormatMessage(IDS_WINDOWS_IS_NOT_UP_TO_DATE,
-                                   primary_app_name);
-          break;
-        default:
-          error_text.FormatMessage(IDS_SETUP_FAILED, hr);
-          break;
-      }
+  return thread_pool_->QueueUserWorkItem(work_item, flags);
+}
 
-      if (!args_.is_silent_set) {
-        goopdate_utils::DisplayErrorInMessageBox(error_text, primary_app_name);
-      }
-      return S_OK;
-    }
+void GoopdateImpl::Stop() {
+  // The thread pool destructor waits for any remaining jobs to complete.
+  thread_pool_.reset();
+}
 
-    default:
-      return E_NOTIMPL;
+// Assumes the resources are loaded and members are initialized.
+void GoopdateImpl::HandleError(HRESULT hr, bool has_ui_been_displayed) {
+  CORE_LOG(L3, (_T("[GoopdateImpl::HandleError][0x%x][%u]"),
+      hr, has_ui_been_displayed));
+
+  if (has_ui_been_displayed ||
+      !internal::CanDisplayUi(args_.mode, args_.is_silent_set)) {
+    return;
   }
-#pragma warning(pop)
+
+  const CString& bundle_name = args_.extra.bundle_name;
+  CString primary_app_guid;
+  if (!args_.extra.apps.empty()) {
+    primary_app_guid = GuidToString(args_.extra.apps[0].app_guid);
+  }
+
+  CString error_text;
+  switch (hr) {
+    case GOOPDATE_E_UA_ALREADY_RUNNING:
+      error_text.FormatMessage(IDS_APPLICATION_ALREADY_INSTALLING,
+                               client_utils::GetUpdateAllAppsBundleName());
+      break;
+    case OMAHA_NET_E_WINHTTP_NOT_AVAILABLE:
+      ASSERT1(!bundle_name.IsEmpty());
+      error_text.FormatMessage(IDS_WINDOWS_IS_NOT_UP_TO_DATE,
+                               bundle_name);
+      break;
+    default:
+      // TODO(omaha3): This currently assumes that any error returned here is
+      // related to Setup.
+      CString product_name;
+      VERIFY1(product_name.LoadString(IDS_PRODUCT_DISPLAY_NAME));
+      error_text.FormatMessage(IDS_SETUP_FAILED, product_name, hr);
+      break;
+  }
+
+  VERIFY1(client_utils::DisplayError(is_machine_,
+               bundle_name,
+               hr,
+               0,
+               error_text,
+               primary_app_guid,
+               args_.extra.language,
+               args_.extra.installation_id,
+               args_.extra.brand_code));
 }
 
 HRESULT GoopdateImpl::Main(HINSTANCE instance,
@@ -361,18 +427,17 @@
 
   HRESULT hr = DoMain(instance, cmd_line, cmd_show);
 
-  CORE_LOG(L2, (_T("[has_uninstalled is %d]"), has_uninstalled_));
+  CORE_LOG(L2, (_T("[has_uninstalled_ is %d]"), has_uninstalled_));
 
   // For install processes, verify the Google Update EULA has been accepted and
   // we can use the network unless a) the command line specifies EULA is
   // required or b) in OEM installing mode, which also prevents network use.
   if ((COMMANDLINE_MODE_INSTALL == args_.mode ||
-       COMMANDLINE_MODE_IG == args_.mode ||
        COMMANDLINE_MODE_HANDOFF_INSTALL == args_.mode) &&
        SUCCEEDED(hr)) {
-    ASSERT1(ConfigManager::Instance()->CanUseNetwork(is_machine_) ||
-            ConfigManager::Instance()->IsOemInstalling(is_machine_) ||
-            args_.is_eula_required_set);
+    ASSERT1(args_.is_eula_required_set ||
+            ConfigManager::Instance()->CanUseNetwork(is_machine_) ||
+            oem_install_utils::IsOemInstalling(is_machine_));
   }
 
   // In the /install case, clean up if Google Update and/or app install did not
@@ -391,15 +456,17 @@
     }
   }
 
+  Worker::DeleteInstance();
+
   // Uninitializing the network configuration must happen after reporting the
   // metrics. The call succeeds even if the network has not been initialized
   // due to errors up the execution path.
-  NetworkConfig::DeleteInstance();
+  NetworkConfigManager::DeleteInstance();
 
   if (COMMANDLINE_MODE_INSTALL == args_.mode &&
       args_.is_oem_set &&
       SUCCEEDED(hr) &&
-      !ConfigManager::Instance()->IsOemInstalling(is_machine_)) {
+      !oem_install_utils::IsOemInstalling(is_machine_)) {
     ASSERT1(false);
     hr = GOOPDATE_E_OEM_INSTALL_SUCCEEDED_BUT_NOT_IN_OEM_INSTALLING_MODE;
   }
@@ -413,6 +480,7 @@
   // * /install instance that would not have called Setup.Uninstall().
   // * /install instance when Uninstall failed for some reason since the
   //   the consistency check may expect the wrong state.
+  // * /update instance that failed due to an install/uninstall in progress.
   if (COMMANDLINE_MODE_REGSERVER != args_.mode &&
       COMMANDLINE_MODE_UNREGSERVER != args_.mode &&
       COMMANDLINE_MODE_COMSERVER != args_.mode &&
@@ -423,10 +491,14 @@
       !(COMMANDLINE_MODE_INSTALL == args_.mode &&
         is_machine_ &&
         !vista_util::IsUserAdmin()) &&
+      !(COMMANDLINE_MODE_UPDATE == args_.mode &&
+        GOOPDATE_E_FAILED_TO_GET_LOCK == hr) &&
       !did_install_uninstall_fail) {
-    Setup::CheckInstallStateConsistency(is_machine_);
+    install_self::CheckInstallStateConsistency(is_machine_);
   }
 
+  ResourceManager::Delete();
+
   return hr;
 }
 
@@ -439,48 +511,115 @@
 
   // The system terminates the process without displaying a retry dialog box
   // for the user. GoogleUpdate has no user state to be saved, therefore
-  // prompting the user is meaningless.
+  // prompting the user for input is meaningless.
   VERIFY1(SUCCEEDED(SetProcessSilentShutdown()));
 
-  int major_version(0);
-  int minor_version(0);
-  int service_pack_major(0);
-  int service_pack_minor(0);
-  TCHAR name[MAX_PATH] = {0};
-  VERIFY1(SystemInfo::GetSystemVersion(&major_version,
-                                       &minor_version,
-                                       &service_pack_major,
-                                       &service_pack_minor,
-                                       name,
-                                       arraysize(name)));
-  metric_windows_major_version    = major_version;
-  metric_windows_minor_version    = minor_version;
-  metric_windows_sp_major_version = service_pack_major;
-  metric_windows_sp_minor_version = service_pack_minor;
+  VERIFY1(SUCCEEDED(CaptureOSMetrics()));
 
   InitializeVersionFromModule(module_instance_);
   this_version_ = GetVersionString();
 
   TCHAR path[MAX_PATH] = {0};
-  VERIFY1(::GetModuleFileName(instance, path, MAX_PATH));
+  VERIFY1(::GetModuleFileName(module_instance_, path, MAX_PATH));
   OPT_LOG(L1, (_T("[%s][version %s][%s][%s]"),
                path, this_version_, kBuildType, kOfficialBuild));
 
-  CORE_LOG(L2,
-      (_T("[is system %d][elevated admin %d][non-elevated admin %d]"),
-       is_local_system_,
-       vista_util::IsUserAdmin(),
-       vista_util::IsUserNonElevatedAdmin()));
+  CORE_LOG(L2, (_T("[is system %d]")
+                _T("[elevated admin %d]")
+                _T("[non-elevated admin %d]")
+                _T("[testsource %s]"),
+                is_local_system_,
+                vista_util::IsUserAdmin(),
+                vista_util::IsUserNonElevatedAdmin(),
+                ConfigManager::Instance()->GetTestSource()));
 
-  HRESULT hr = omaha::ParseCommandLine(cmd_line, &args_);
+  HRESULT parse_hr = omaha::ParseCommandLine(cmd_line_, &args_);
+  if (FAILED(parse_hr)) {
+    CORE_LOG(LE, (_T("[Parse cmd line failed][0x%08x]"), parse_hr));
+    args_.mode = COMMANDLINE_MODE_UNKNOWN;
+    // Continue because we want to load the resources and display an error.
+  }
+
+  // TODO(omaha3): Interactive updates might be useful for debugging or even
+  // on-demand updates of all apps. Figure out how to expose this. For now, no
+  // install source, which should not happen normally, is used as the trigger.
+  // The simplest way to make this work is to set args_.is_silent_set
+  // accordingly. However, we also need a way for this to work for per-machine
+  // instances since IsMachineProcess() relies on being Local System. When we
+  // settle on a mechanism, we should update the parser and remove this.
+  if (args_.mode == COMMANDLINE_MODE_UA) {
+    args_.is_silent_set = !args_.install_source.IsEmpty();
+  }
+
+  HRESULT hr = InitializeGoopdateAndLoadResources();
   if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[Parse cmd line failed][0x%08x]"), hr));
+    CORE_LOG(LE,
+             (_T("[InitializeGoopdateAndLoadResources failed][0x%08x]"), hr));
+    if (internal::CanDisplayUi(args_.mode, args_.is_silent_set)) {
+      // The resources are unavaliable, so we must use hard-coded text.
+      const TCHAR* const kMsgBoxTitle = _T("Google Installer");
+      CString message;
+      message.Format(_T("Installation failed with error 0x%08x."), hr);
+      VERIFY1(IDOK == ::MessageBox(NULL, message, kMsgBoxTitle, MB_OK));
+    }
+    return hr;
+  }
+  // The resources are now loaded and available if applicable for this instance.
+  // If there was no bundle name specified on the command line, we take the
+  // bundle name from the first app's name; if that has no name (or if there is
+  // no apps, as in a runtime-only install) it will be an empty string.
+  // Replace it with the localized installer name.
+  if (args_.extra.bundle_name.IsEmpty()) {
+    args_.extra.bundle_name.LoadString(IDS_PRODUCT_DISPLAY_NAME);
+  }
+
+  CORE_LOG(L2, (_T("[can use network %d]")
+                _T("[can collect stats %d]"),
+                ConfigManager::Instance()->CanUseNetwork(is_machine_),
+                ConfigManager::Instance()->CanCollectStats(is_machine_)));
+
+  bool has_ui_been_displayed = false;
+
+  if (!is_machine_ && vista_util::IsElevatedWithUACMaybeOn()) {
+    CORE_LOG(LW, (_T("User GoogleUpdate is possibly running in an unsupported ")
+                  _T("way, at High integrity with UAC possibly enabled.")));
+  }
+
+  if (FAILED(parse_hr)) {
+    ASSERT1(args_.mode == COMMANDLINE_MODE_UNKNOWN);
+    hr = parse_hr;
+  } else {
+    ASSERT1(args_.mode != COMMANDLINE_MODE_UNKNOWN);
+    // TODO(omaha): I would like to pass the mode as an argument, but there
+    // are so many uses for args_.mode and they could easily creep in. Consider
+    // eliminating the args_ member.
+    hr = ExecuteMode(&has_ui_been_displayed);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[ExecuteMode failed][0x%08x]"), hr));
+      // Continue and display error.
+    }
+  }
+
+  if (FAILED(hr)) {
+    HandleError(hr, has_ui_been_displayed);
     return hr;
   }
 
+  return S_OK;
+}
+
+// Assumes the command line has been parsed.
+HRESULT GoopdateImpl::InitializeGoopdateAndLoadResources() {
   // IsMachineProcess requires the command line be parsed first.
   is_machine_ = IsMachineProcess();
-  CORE_LOG(L2, (_T("[is machine %d]"), is_machine_));
+  OPT_LOG(L1, (_T("[is machine: %d]"), is_machine_));
+
+  if (!::SetEnvironmentVariable(kEnvVariableIsMachine,
+                                is_machine_ ? _T("1") : _T("0"))) {
+    HRESULT hr = HRESULTFromLastError();
+    CORE_LOG(LW, (_T("[::SetEnvironmentVariable failed][%s][0x%x]"),
+                  kEnvVariableIsMachine, hr));
+  }
 
   // After parsing the command line, reinstall the crash handler to match the
   // state of the process.
@@ -488,9 +627,13 @@
     VERIFY1(SUCCEEDED(Crash::InstallCrashHandler(is_machine_)));
   }
 
+  // We have parsed the command line, and we are now resetting is_machine.
+  NetworkConfigManager::set_is_machine(
+      is_machine_ && vista_util::IsUserAdmin());
+
   // Set the current directory to be the one that the DLL was launched from.
   TCHAR module_directory[MAX_PATH] = {0};
-  if (!GetModuleDirectory(instance, module_directory)) {
+  if (!GetModuleDirectory(module_instance_, module_directory)) {
      return HRESULTFromLastError();
   }
   if (!::SetCurrentDirectory(module_directory)) {
@@ -504,54 +647,70 @@
 
   VERIFY1(SUCCEEDED(internal::PromoteAppEulaAccepted(is_machine_)));
 
-  hr = LoadResourceDllIfNecessary(args_.mode, module_directory);
+  if (ShouldCheckShutdownEvent(args_.mode) && IsShutdownEventSet()) {
+    return GOOPDATE_E_SHUTDOWN_SIGNALED;
+  }
+
+  HRESULT hr = LoadResourceDllIfNecessary(args_.mode, module_directory);
   if (FAILED(hr)) {
     CORE_LOG(LE, (_T("[LoadResourceDllIfNecessary failed][0x%08x]"), hr));
     return hr;
   }
 
-  user_default_language_id_ = ResourceManager::GetDefaultUserLanguage();
+  return S_OK;
+}
+
+// Assumes Goopdate is initialized and resources are loaded.
+HRESULT GoopdateImpl::ExecuteMode(bool* has_ui_been_displayed) {
+  ASSERT1(has_ui_been_displayed);
+
+  // Save the mode on the stack for post-mortem debugging purposes.
+  volatile CommandLineMode mode = args_.mode;
+
+  user_default_language_id_ = lang::GetDefaultLanguage(is_local_system_);
+
+  ASSERT1(CheckRegisteredVersion(GetVersionString(), is_machine_, mode));
 
 #pragma warning(push)
 // C4061: enumerator 'xxx' in switch of enum 'yyy' is not explicitly handled by
 // a case label.
 #pragma warning(disable : 4061)
-  // Save the mode on the stack for post-mortem debugging purposes.
-  volatile CommandLineMode mode = args_.mode;
-  switch (args_.mode) {
+  switch (mode) {
     // Delegate to the service or the core. Both have reliability requirements
     // and resource constraints. Generally speaking, they do not use COM nor
     // networking code.
     case COMMANDLINE_MODE_SERVICE:
-      return omaha::ServiceModule().Main(cmd_show);
+      return omaha::Update3ServiceModule().Main(SW_HIDE);
 
-    case COMMANDLINE_MODE_SERVICE_REGISTER:
-      return SetupService::InstallService(app_util::GetModulePath(NULL));
+    case COMMANDLINE_MODE_MEDIUM_SERVICE:
+      return omaha::UpdateMediumServiceModule().Main(SW_HIDE);
 
-    case COMMANDLINE_MODE_SERVICE_UNREGISTER:
-      return SetupService::UninstallService();
+    case COMMANDLINE_MODE_SERVICE_REGISTER: {
+      HRESULT hr = SetupUpdate3Service::InstallService(
+                       app_util::GetModulePath(NULL));
+      if (FAILED(hr)) {
+        return hr;
+      }
+      return SetupUpdateMediumService::InstallService(
+                 app_util::GetModulePath(NULL));
+    }
+
+    case COMMANDLINE_MODE_SERVICE_UNREGISTER: {
+      HRESULT hr = SetupUpdate3Service::UninstallService();
+      if (FAILED(hr)) {
+        return hr;
+      }
+      return SetupUpdateMediumService::UninstallService();
+    }
 
     default: {
       scoped_co_init init_com_apt(COINIT_MULTITHREADED);
-      hr = init_com_apt.hresult();
-      if (FAILED(hr)) {
-        return hr;
-      }
-      hr = ::CoInitializeSecurity(
-          NULL,
-          -1,
-          NULL,   // Let COM choose what authentication services to register.
-          NULL,
-          RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  // Data integrity and encryption.
-          RPC_C_IMP_LEVEL_IDENTIFY,       // Only allow a server to identify.
-          NULL,
-          EOAC_DYNAMIC_CLOAKING,
-          NULL);
+      HRESULT hr = init_com_apt.hresult();
       if (FAILED(hr)) {
         return hr;
       }
 
-      switch (args_.mode) {
+      switch (mode) {
         case COMMANDLINE_MODE_CORE:
           return omaha::Core().Main(is_local_system_,
                                     !args_.is_crash_handler_disabled);
@@ -560,30 +719,27 @@
           return omaha::CrashHandler().Main(is_local_system_);
 
         case COMMANDLINE_MODE_NOARGS:
-          // TODO(omaha): Supported only for legacy Compatibility. Error out
-          // when legacy handoff support is removed as Omaha should not be
-          // called without arguments otherwise.
-          return S_OK;
-
-        case COMMANDLINE_MODE_LEGACYUI:
-          return HandleLegacyUI();
-
-        case COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF:
-          return HandleLegacyManifestHandoff();
+          return GOOPDATE_E_NO_ARGS;
 
         case COMMANDLINE_MODE_UNREGISTER_PRODUCT:
-          return DoUnregisterProduct();
+          // TODO(omaha3): Eliminate the need for this mode.
+          return E_FAIL;
+
+        case COMMANDLINE_MODE_COMBROKER:
+          return omaha::GoogleUpdate(is_machine_,
+                                     omaha::GoogleUpdate::kBrokerMode).Main();
+
+        case COMMANDLINE_MODE_ONDEMAND:
+         return omaha::GoogleUpdate(
+             is_machine_,
+             omaha::GoogleUpdate::kOnDemandMode).Main();
 
         default: {
-          hr = goopdate_utils::ConfigureNetwork(
-              is_machine_ && vista_util::IsUserAdmin(),
-              is_local_system_);
-          if (FAILED(hr)) {
-            VERIFY1(SUCCEEDED(HandleError(hr)));
-            return hr;
-          }
+          // Reference the network instance here so the singleton can be
+          // created before possible impersonation.
+          NetworkConfigManager::Instance();
 
-          switch (args_.mode) {
+          switch (mode) {
             case COMMANDLINE_MODE_WEBPLUGIN:
               return HandleWebPlugin();
 
@@ -594,22 +750,24 @@
               return NetDiags().Main();
 
             case COMMANDLINE_MODE_REGISTER_PRODUCT:
-              return DoRegisterProduct();
+              // TODO(omaha3): Eliminate the need for this mode.
+              return E_FAIL;
+
+            case COMMANDLINE_MODE_INSTALL:
+              return DoInstall(has_ui_been_displayed);
 
             case COMMANDLINE_MODE_UPDATE:
               return DoSelfUpdate();
 
-            case COMMANDLINE_MODE_UG:
-              return DoCompleteSelfUpdate();
-
             case COMMANDLINE_MODE_RECOVER:
               return DoRecover();
 
             case COMMANDLINE_MODE_HANDOFF_INSTALL:
-            case COMMANDLINE_MODE_IG:
-            case COMMANDLINE_MODE_INSTALL:
+              return DoHandoff(has_ui_been_displayed);
+
             case COMMANDLINE_MODE_UA:
-              return DoWorker();
+              return DoUpdateAllApps(has_ui_been_displayed);
+
             case COMMANDLINE_MODE_CRASH:
               return DoCrash();
 
@@ -617,9 +775,30 @@
               return HandleReportCrash();
 
             case COMMANDLINE_MODE_REGSERVER:
-            case COMMANDLINE_MODE_UNREGSERVER:
+            case COMMANDLINE_MODE_UNREGSERVER: {
+              hr = omaha::GoogleUpdate(
+                  is_machine_, omaha::GoogleUpdate::kUpdate3Mode).Main();
+              if (FAILED(hr)) {
+                return hr;
+              }
+              hr = omaha::GoogleUpdate(
+                  is_machine_, omaha::GoogleUpdate::kBrokerMode).Main();
+              if (FAILED(hr)) {
+                return hr;
+              }
+              return omaha::GoogleUpdate(
+                  is_machine_, omaha::GoogleUpdate::kOnDemandMode).Main();
+            }
+
             case COMMANDLINE_MODE_COMSERVER:
-              return omaha::GoogleUpdate().Main();
+              return omaha::GoogleUpdate(
+                  is_machine_, omaha::GoogleUpdate::kUpdate3Mode).Main();
+
+            case COMMANDLINE_MODE_UNINSTALL:
+              return HandleUninstall();
+
+            case COMMANDLINE_MODE_PING:
+              return HandlePing();
 
             default:
               // We have a COMMANDLINE_MODE_ that isn't being handled.
@@ -637,11 +816,11 @@
 bool GoopdateImpl::IsMachineProcess() {
   Tristate needs_admin(TRISTATE_NONE);
   if (!args_.extra.apps.empty()) {
-    needs_admin = args_.extra.apps[0].needs_admin ? TRISTATE_TRUE :
-                                                    TRISTATE_FALSE;
+    needs_admin = args_.extra.apps[0].needs_admin != NEEDS_ADMIN_NO ?
+        TRISTATE_TRUE : TRISTATE_FALSE;
   }
 
-  return goopdate_utils::IsMachineProcess(
+  return internal::IsMachineProcess(
       args_.mode,
       goopdate_utils::IsRunningFromOfficialGoopdateDir(true),
       is_local_system_,
@@ -659,17 +838,22 @@
   // in certain interactive modes.
   HRESULT hr = S_OK;
   __try {
-    // Crashes are uploaded always in the out-of-process case. This is handled
-    // in Crash::Report().
-    // Google Update crashes are uploaded only for for the users that have opted
-    // in, when network use is allowed, and from systems that are not
-    // development or test.
+    // Crashes are uploaded always in the out-of-process case.
+    //
+    // Google Update internal crashes are handled in-process. They are uploaded
+    // only for the users that have opted in sending usage stats and when
+    // network use is allowed, and from systems that are not development
+    // nor test.
+    // This default behavior can be overriden by an UpdatedDev parameter that
+    // allows crashes to be uploaded always.
+    //
     // All GoogleUpdate crashes are logged in the Windows event log for
     // applications, unless the logging is disabled by the administrator.
-    const bool can_upload_in_process =
-        ConfigManager::Instance()->CanCollectStats(is_machine_) &&
-        ConfigManager::Instance()->CanUseNetwork(is_machine_) &&
-        !goopdate_utils::IsTestSource();
+    ConfigManager* cm = ConfigManager::Instance();
+    const bool can_upload_in_process = cm->AlwaysAllowCrashUploads() ||
+        (cm->CanCollectStats(is_machine_) &&
+         cm->CanUseNetwork(is_machine_)   &&
+         !goopdate_utils::IsTestSource());
     hr = Crash::Report(can_upload_in_process,
                        args_.crash_filename,
                        args_.custom_info_filename,
@@ -681,116 +865,131 @@
   return hr;
 }
 
-HRESULT GoopdateImpl::HandleLegacyManifestHandoff() {
-  // TODO(omaha): Once the core supports metric aggregation, move this metric
-  // to LegacyManifestHandler::HandleEvent().
-  ++metric_handoff_legacy_user;
-
-  // The user core launches the is_legacy_user_manifest_cmd worker to
-  // process the manifest file that is dropped into the initial manifest
-  // directory by the omaha1 installers.
-  if (!goopdate_utils::IsRunningFromOfficialGoopdateDir(false)) {
-    // TODO(omaha): If the /UI process fails the legacy installer will
-    // not display anything to the user. We might have to launch UI here.
-    ASSERT1(false);
-    return E_FAIL;
-  }
-
-  return goopdate_utils::HandleLegacyManifestHandoff(
-      args_.legacy_manifest_path,
-      false);
-}
-
-HRESULT GoopdateImpl::HandleLegacyUI() {
-  ++metric_handoff_legacy_machine;
-
-  // A omaha1 installer does a handoff install using
-  // the /UI switch. The format of the command line is as follows:
-  // /UI /lang <lang> manfest_filename or /UI legacy_manifest_path
-  // The legacy installer should have elevated and we should
-  // be running from the program files directory since this command can
-  // only be passed to a registered, machine omaha.
-  if (!vista_util::IsUserAdmin() ||
-      !goopdate_utils::IsRunningFromOfficialGoopdateDir(true)) {
-    // TODO(omaha): If the /UI process fails the legacy installer will
-    // not display anything to the user. We might have to launch UI here.
-    ASSERT1(false);
-    return E_FAIL;
-  }
-
-  // We ignore the /lang switch that is passed on the cmd line,
-  // since we get the language from the manifest file and pass that to the
-  // worker.
-  return goopdate_utils::HandleLegacyManifestHandoff(
-      args_.legacy_manifest_path, true);
-}
-
-// The resource dll is loaded only in the following cases:
-// 1. Initial setup: /install
-// 2. Google Update and app install: /ig
-// 3. Handoff install: /handoff
-// 4. App update worker: /ua
-// 5. Self-Update: /ug
-HRESULT GoopdateImpl::LoadResourceDllIfNecessary(CommandLineMode mode,
-                                                 const CString& resource_dir) {
+bool GoopdateImpl::ShouldCheckShutdownEvent(CommandLineMode mode) {
   switch (mode) {
-    case COMMANDLINE_MODE_INSTALL:          // has UI on errors
-    case COMMANDLINE_MODE_IG:               // has UI
-    case COMMANDLINE_MODE_HANDOFF_INSTALL:  // has UI
-    case COMMANDLINE_MODE_UG:               // TODO(omaha): Why is it loaded?
-    case COMMANDLINE_MODE_UA:               // Worker, etc. load strings.
-    case COMMANDLINE_MODE_COMSERVER:        // Returns strings to caller.
-    case COMMANDLINE_MODE_SERVICE_REGISTER:
-    case COMMANDLINE_MODE_SERVICE_UNREGISTER:
-      // Load the resource DLL for these modes.
-      break;
+    // Modes that may run before or during installation or otherwise do not
+    // need to listen to shutdown.
     case COMMANDLINE_MODE_UNKNOWN:
     case COMMANDLINE_MODE_NOARGS:
-    case COMMANDLINE_MODE_CORE:
-    case COMMANDLINE_MODE_CRASH_HANDLER:
-    case COMMANDLINE_MODE_SERVICE:
     case COMMANDLINE_MODE_REGSERVER:
     case COMMANDLINE_MODE_UNREGSERVER:
     case COMMANDLINE_MODE_NETDIAGS:
     case COMMANDLINE_MODE_CRASH:
     case COMMANDLINE_MODE_REPORTCRASH:
-    case COMMANDLINE_MODE_UPDATE:
     case COMMANDLINE_MODE_RECOVER:
+    case COMMANDLINE_MODE_SERVICE_REGISTER:
+    case COMMANDLINE_MODE_SERVICE_UNREGISTER:
+
+    case COMMANDLINE_MODE_INSTALL:
+    case COMMANDLINE_MODE_UPDATE:
     case COMMANDLINE_MODE_WEBPLUGIN:
     case COMMANDLINE_MODE_CODE_RED_CHECK:
-    case COMMANDLINE_MODE_LEGACYUI:
-    case COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF:
     case COMMANDLINE_MODE_REGISTER_PRODUCT:
     case COMMANDLINE_MODE_UNREGISTER_PRODUCT:
+    case COMMANDLINE_MODE_PING:
+      return false;
+
+    // Modes that should honor shutdown.
+    case COMMANDLINE_MODE_CORE:
+    case COMMANDLINE_MODE_SERVICE:
+    case COMMANDLINE_MODE_COMSERVER:
+    case COMMANDLINE_MODE_CRASH_HANDLER:
+    case COMMANDLINE_MODE_COMBROKER:
+    case COMMANDLINE_MODE_ONDEMAND:
+    case COMMANDLINE_MODE_MEDIUM_SERVICE:
+
+    case COMMANDLINE_MODE_HANDOFF_INSTALL:
+    case COMMANDLINE_MODE_UA:
+    case COMMANDLINE_MODE_UNINSTALL:
+      return true;
+
     default:
-      // Thse modes do not need the resource DLL.
+      ASSERT1(false);
+      return true;
+  }
+}
+
+bool GoopdateImpl::IsShutdownEventSet() {
+  NamedObjectAttributes attr;
+  GetNamedObjectAttributes(kShutdownEvent, is_machine_, &attr);
+  scoped_event shutdown_event(::OpenEvent(SYNCHRONIZE, false, attr.name));
+  if (!shutdown_event) {
+    return false;
+  }
+
+  return WAIT_OBJECT_0 == ::WaitForSingleObject(get(shutdown_event), 0);
+}
+
+// The resource dll is loaded only in the following cases:
+// 1. Initial setup: /install
+// 2. Handoff install: /handoff
+// 3. App update worker: /ua
+// 4. Various registrations.
+// 5. Modes where an error message needs to be displayed.
+HRESULT GoopdateImpl::LoadResourceDllIfNecessary(CommandLineMode mode,
+                                                 const CString& resource_dir) {
+  switch (mode) {
+    case COMMANDLINE_MODE_UNKNOWN:             // Displays an error using UI.
+    case COMMANDLINE_MODE_NOARGS:              // Displays an error using UI.
+    case COMMANDLINE_MODE_INSTALL:             // Has UI on errors.
+    case COMMANDLINE_MODE_UPDATE:              // Task and Service descriptions.
+    case COMMANDLINE_MODE_RECOVER:             // Writes strings to registry.
+    case COMMANDLINE_MODE_HANDOFF_INSTALL:     // Has optional UI.
+    case COMMANDLINE_MODE_UA:                  // Has optional UI.
+    case COMMANDLINE_MODE_COMSERVER:           // Returns strings to caller.
+    case COMMANDLINE_MODE_SERVICE:             // Returns strings to caller.
+    case COMMANDLINE_MODE_MEDIUM_SERVICE:      // TODO(omaha): Check & explain.
+    case COMMANDLINE_MODE_SERVICE_REGISTER:    // Requires the RGS resources.
+    case COMMANDLINE_MODE_SERVICE_UNREGISTER:  // Requires the RGS resources.
+    case COMMANDLINE_MODE_ONDEMAND:            // Worker, etc. load strings.
+      // Load the resource DLL for these modes.
+      break;
+
+    // For the Core, the resource DLL needs to be loaded when the Core is
+    // servicing IGoogleUpdate3. The Core loads the resource DLL after the Code
+    // Red kickoff, from within core.cc.
+    case COMMANDLINE_MODE_CORE:
+    case COMMANDLINE_MODE_REGSERVER:
+    case COMMANDLINE_MODE_UNREGSERVER:
+    case COMMANDLINE_MODE_NETDIAGS:
+    case COMMANDLINE_MODE_CRASH:
+    case COMMANDLINE_MODE_REPORTCRASH:
+    case COMMANDLINE_MODE_WEBPLUGIN:
+    case COMMANDLINE_MODE_CODE_RED_CHECK:
+    case COMMANDLINE_MODE_REGISTER_PRODUCT:
+    case COMMANDLINE_MODE_UNREGISTER_PRODUCT:
+    case COMMANDLINE_MODE_CRASH_HANDLER:
+    case COMMANDLINE_MODE_COMBROKER:
+    case COMMANDLINE_MODE_UNINSTALL:
+    case COMMANDLINE_MODE_PING:
+    default:
+      // These modes do not need the resource DLL.
+      ASSERT1(!internal::CanDisplayUi(mode, false));
       return S_OK;
   }
 
-  resource_manager_.reset(new ResourceManager(is_machine_, resource_dir));
-
-  HRESULT hr = resource_manager_->LoadResourceDll(args_.extra.language);
+  // TODO(omaha3): Consider not using ResourceManager in this file.
+  HRESULT hr = ResourceManager::Create(
+      is_machine_,
+      resource_dir,
+      lang::GetLanguageForProcess(args_.extra.language));
   if (FAILED(hr)) {
-    ++metric_load_resource_dll_failed;
-    ASSERT(false, (_T("LoadResourceDll failed with 0x%08x"), hr));
+    ASSERT(false, (_T("ResourceManager::Create failed with 0x%08x"), hr));
+    return hr;
   }
-  return hr;
+
+  return S_OK;
 }
 
 // Writes the information for the primary app to enable Omaha to send usage
 // stats now. It will be set for each app in the args when they are installed.
 HRESULT GoopdateImpl::SetUsageStatsEnable() {
-  if (COMMANDLINE_MODE_UPDATE == args_.mode) {
-    VERIFY1(SUCCEEDED(
-        goopdate_utils::ConvertLegacyUsageStats(is_machine_)));
-  }
-
   if (args_.extra.apps.empty()) {
     return S_OK;
   }
 
-  HRESULT hr = goopdate_utils::SetUsageStatsEnable(
-      args_.extra.apps[0].needs_admin,
+  HRESULT hr = app_registry_utils::SetUsageStatsEnable(
+      args_.extra.apps[0].needs_admin != NEEDS_ADMIN_NO,
       GuidToString(args_.extra.apps[0].app_guid),
       args_.extra.usage_stats_enable);
   if (FAILED(hr)) {
@@ -814,11 +1013,11 @@
 
   // Call the utils method instead of member method because we want to execute
   // as little code as possible.
-  bool is_machine = goopdate_utils::IsMachineProcess(args_.mode,
-                                                     false,   // machine dir
-                                                     is_local_system_,
-                                                     args_.is_machine_set,
-                                                     TRISTATE_NONE);
+  bool is_machine = internal::IsMachineProcess(args_.mode,
+                                               false,   // machine dir
+                                               is_local_system_,
+                                               args_.is_machine_set,
+                                               TRISTATE_NONE);
   ASSERT1(IsMachineProcess() == is_machine);
 
   if (!ConfigManager::Instance()->CanUseNetwork(is_machine)) {
@@ -827,70 +1026,14 @@
     return GOOPDATE_E_CANNOT_USE_NETWORK;
   }
 
-  HRESULT hr = FixGoogleUpdate(kGoogleUpdateAppId,
-                               this_version_,
-                               _T(""),     // Goopdate doesn't have a language.
-                               is_machine,
-                               &GoopdateImpl::CodeRedDownloadCallback,
-                               NULL);
-  CORE_LOG(L2, (_T("[FixGoogleUpdate returned 0x%08x]"), hr));
+  CheckForCodeRed(is_machine, this_version_);
   return S_OK;
 }
 
-// Returns S_OK when the download of the Code Red file succeeds and E_FAIL
-// otherwise.
-HRESULT GoopdateImpl::CodeRedDownloadCallback(const TCHAR* url,
-                                              const TCHAR* file_path,
-                                              void*) {
-  ++metric_cr_callback_total;
-
-  NetworkConfig& network_config = NetworkConfig::Instance();
-  NetworkRequest network_request(network_config.session());
-
-  network_request.AddHttpRequest(new SimpleRequest);
-
-  // BITS takes the job to BG_JOB_STATE_TRANSIENT_ERROR when the server returns
-  // 204. After the "no progress time out", the BITS job errors out. Since
-  // BITS follows the WinHTTP in the fallback chain, the code is expected to
-  // execute only if WinHTTP fails to get a response from the server.
-
-  // Assumes the caller is not logged on if the function failed.
-  // BITS transfers files only when the job owner is logged on.
-  bool is_logged_on(false);
-  HRESULT hr = IsUserLoggedOn(&is_logged_on);
-  ASSERT1(SUCCEEDED(hr) || !is_logged_on);
-  if (is_logged_on) {
-    BitsRequest* bits_request(new BitsRequest);
-    bits_request->set_minimum_retry_delay(60);
-    bits_request->set_no_progress_timeout(15);
-    network_request.AddHttpRequest(bits_request);
-  }
-
-  network_request.AddHttpRequest(new BrowserRequest);
-  hr = network_request.DownloadFile(CString(url), CString(file_path));
-  if (FAILED(hr)) {
-    return E_FAIL;
-  }
-
-  switch (network_request.http_status_code()) {
-    case HTTP_STATUS_OK:
-      ++metric_cr_callback_status_200;
-      return S_OK;
-    case HTTP_STATUS_NO_CONTENT:
-      ++metric_cr_callback_status_204;
-      return E_FAIL;
-    default:
-      ++metric_cr_callback_status_other;
-      return E_FAIL;
-  }
-}
-
-// Until bug 1135173 is fixed:
-// Running a OneClick cross-install at the same time as Setup has undefined
-// behavior. If Setup wins the race to the lock, it may delete the files that
-// the cross-install wants to copy. Also, since the plugin and /plugin instance
-// do not check the Setup Lock, they may try to invoke a version that is being
-// installed, removed, or rolled back.
+// Even though http://b/1135173 is fixed, there is still a possibility that only
+// some of the files will be copied if Setup is currently running.
+// TODO(omaha3): If we save and use the metainstaller for OneClick, that may
+// address this.
 
 // If we're called with the /webplugin command, we need to handle it and exit.
 // This is called from the browser and the command line arguments come from the
@@ -900,117 +1043,51 @@
   return webplugin_utils::DoOneClickInstall(args_);
 }
 
-HRESULT GoopdateImpl::DoCompleteSelfUpdate() {
-  OPT_LOG(L1, (_T("[GoopdateImpl::DoCompleteSelfUpdate]")));
-  // TODO(omaha): The use of is_machine_set is very non-intuituve.
-  // It is used only for machine repair situations when elevation is required
-  // in vista or in case of XP. For all other cases this is not used and plain
-  // /update is used to invoke recovery.
-  // Another consequence of this is that in some cases we run
-  // the full Omaha update logic i.e. ShouldInstall etc for recovery.
-  // Consider making recovery not go through all this code.
-  OPT_LOG(L1, (_T("[self update][is_machine=%d]"), is_machine_));
+HRESULT GoopdateImpl::DoInstall(bool* has_ui_been_displayed) {
+  OPT_LOG(L1, (_T("[GoopdateImpl::DoInstall]")));
+  ASSERT1(has_ui_been_displayed);
 
-  Ping ping;
-  return FinishGoogleUpdateInstall(args_,
-                                   is_machine_,
-                                   true,
-                                   &ping,
-                                   NULL);
-}
-
-// Calls DoRegisterProductHelper and sends a ping with the result.
-// TODO(omaha): Use a more common code flow with normal installs or remove
-// registerproduct altogether when redesigning Omaha.
-HRESULT GoopdateImpl::DoRegisterProduct() {
-  OPT_LOG(L1, (_T("[GoopdateImpl::DoRegisterProduct]")));
-
-  ASSERT1(!goopdate_utils::IsRunningFromOfficialGoopdateDir(false) &&
-          !goopdate_utils::IsRunningFromOfficialGoopdateDir(true));
-
-  ASSERT1(!args_.extra.apps.empty());
-  // TODO(omaha): Support bundles. Only supports one app currently.
-  ASSERT1(1 == args_.extra.apps.size());
-  bool extra_needs_admin = args_.extra.apps[0].needs_admin;
-
-  AppManager app_manager(extra_needs_admin);
-  ProductDataVector products;
-  app_manager.ConvertCommandLineToProductData(args_, &products);
-  AppData app_data = products[0].app_data();
-
-  HRESULT hr = DoRegisterProductHelper(extra_needs_admin,
-                                       &app_manager,
-                                       &app_data);
-
-  const PingEvent::Results event_result = SUCCEEDED(hr) ?
-                                          PingEvent::EVENT_RESULT_SUCCESS :
-                                          PingEvent::EVENT_RESULT_ERROR;
-
-  AppRequestData app_request_data(app_data);
-  PingEvent ping_event(PingEvent::EVENT_REGISTER_PRODUCT_COMPLETE,
-                       event_result,
-                       hr,  // error code
-                       0,  // extra code 1
-                       app_data.previous_version());
-  app_request_data.AddPingEvent(ping_event);
-  AppRequest app_request(app_request_data);
-  Request request(extra_needs_admin);
-  request.AddAppRequest(app_request);
-
-  Ping ping;
-  ping.SendPing(&request);
-
-  return hr;
-}
-
-HRESULT GoopdateImpl::DoRegisterProductHelper(bool is_machine,
-                                              AppManager* app_manager,
-                                              AppData* app_data) {
-  ASSERT1(app_data);
-  ASSERT1(app_manager);
-  // Ensure we're running as elevated admin if needs_admin is true.
-  if (is_machine && !vista_util::IsUserAdmin()) {
-    CORE_LOG(LE, (_T("[DoRegisterProductHelper][machine & user not admin]")));
-    return GOOPDATE_E_NONADMIN_INSTALL_ADMIN_APP;
+  // Some /install command lines, such as /silent /install will not necessarily
+  // have an install source. To differentiate these from other sources, such as
+  // other clients using the COM API, specify a generic source for /install.
+  // TODO(omaha3): Updating two types of command line/args is undesirable, and
+  // this does not use CommandLineBuilder. This could be addressed in several
+  // ways. See the TODO above install.cc::LaunchHandoffProcess().
+  ASSERT1(args_.mode == COMMANDLINE_MODE_INSTALL);
+  CString install_command_line = cmd_line_;
+  CommandLineArgs install_args = args_;
+  if (args_.install_source.IsEmpty()) {
+    ASSERT1(-1 == cmd_line_.Find(kCmdLineInstallSource));
+    SafeCStringAppendFormat(&install_command_line, _T(" /%s %s"),
+                            kCmdLineInstallSource,
+                            kCmdLineInstallSource_InstallDefault);
+    install_args.install_source = kCmdLineInstallSource_InstallDefault;
   }
 
-  // Get product GUID from args and register it in CLIENTS.
-  HRESULT hr = app_manager->RegisterProduct(args_.extra.apps[0].app_guid,
-                                            args_.extra.apps[0].app_name);
+  HRESULT hr = S_OK;
+  if (args_.is_oem_set) {
+    hr = OemInstall(!args_.is_silent_set,       // is_interactive
+                    !args_.extra.runtime_only,  // is_app_install
+                    args_.is_eula_required_set,
+                    args_.is_install_elevated,
+                    install_command_line,
+                    install_args,
+                    &is_machine_,
+                    has_ui_been_displayed);
+  } else {
+    hr = Install(!args_.is_silent_set,          // is_interactive
+                 !args_.extra.runtime_only,     // is_app_install
+                 args_.is_eula_required_set,
+                 false,
+                 args_.is_install_elevated,
+                 install_command_line,
+                 install_args,
+                 &is_machine_,
+                 has_ui_been_displayed);
+  }
+
   if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[AppManager::RegisterProduct failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  // TODO(omaha): Fix http://b/1390770. The brand code, etc. do not get written
-  // because the second phase of Setup uses /ug, which doesn't take these args.
-  Setup setup(is_machine, &args_);
-  hr = setup.InstallSelfSilently();
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[Setup::InstallSelfSilently failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  // Populate the app's ClientState key.
-  VERIFY1(SUCCEEDED(app_manager->WritePreInstallData(*app_data)));
-  VERIFY1(SUCCEEDED(app_manager->InitializeApplicationState(app_data)));
-
-  return S_OK;
-}
-
-HRESULT GoopdateImpl::DoUnregisterProduct() {
-  bool extra_needs_admin = args_.extra.apps[0].needs_admin;
-
-  // Ensure we're running as elevated admin if needs_admin is true.
-  if (extra_needs_admin && !vista_util::IsUserAdmin()) {
-    CORE_LOG(LE, (_T("[DoUnregisterProduct][needs admin & user not admin]")));
-    return GOOPDATE_E_NONADMIN_INSTALL_ADMIN_APP;
-  }
-
-  AppManager app_manager(extra_needs_admin);
-  HRESULT hr = app_manager.UnregisterProduct(args_.extra.apps[0].app_guid);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[AppManager::UnregisterProduct failed][0x%08x]"), hr));
+    CORE_LOG(LE, (_T("[Install failed][0x%08x]"), hr));
     return hr;
   }
 
@@ -1020,10 +1097,9 @@
 HRESULT GoopdateImpl::DoSelfUpdate() {
   OPT_LOG(L1, (_T("[GoopdateImpl::DoSelfUpdate]")));
 
-  Setup setup(is_machine_, &args_);
-  HRESULT hr = setup.UpdateSelfSilently();
+  HRESULT hr = install_self::UpdateSelf(is_machine_, args_.session_id);
   if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[Setup::UpdateSelfSilently failed][0x%08x]"), hr));
+    CORE_LOG(LE, (_T("[UpdateSelf failed][0x%08x]"), hr));
     return hr;
   }
 
@@ -1036,6 +1112,8 @@
 HRESULT GoopdateImpl::DoRecover() {
   OPT_LOG(L1, (_T("[GoopdateImpl::DoRecover()]")));
 
+// TODO(omaha3): Enable. Maybe build without the builder.
+#if 0
   CommandLineBuilder builder(COMMANDLINE_MODE_UPDATE);
 
   HRESULT hr = S_OK;
@@ -1046,13 +1124,15 @@
     ASSERT1(SUCCEEDED(hr));
     return S_OK;
   }
+#else
+  HRESULT hr = S_OK;
+#endif
 
   if (FAILED(hr)) {
     OPT_LOG(LW, (_T("[LaunchRepairFileElevated failed][0x%08x]"), hr));
   }
 
-  Setup setup(is_machine_, &args_);
-  hr = setup.RepairSilently();
+  hr = install_self::Repair(is_machine_);
   if (FAILED(hr)) {
     OPT_LOG(LE, (_T("[Non-elevated repair failed][0x%08x]"), hr));
     return hr;
@@ -1064,29 +1144,92 @@
 // Clears the EULA flag in the handoff instance in case an older installer that
 // does not know about the EULA flag is used to launch the install.
 // Failing to clear flag fails installation because this would prevent updates.
-HRESULT GoopdateImpl::DoWorker() {
-  if (COMMANDLINE_MODE_HANDOFF_INSTALL == args_.mode &&
-      !args_.is_eula_required_set) {
-    HRESULT hr = Setup::SetEulaAccepted(is_machine_);
+HRESULT GoopdateImpl::DoHandoff(bool* has_ui_been_displayed) {
+  OPT_LOG(L1, (_T("[GoopdateImpl::DoHandoff]")));
+  ASSERT1(has_ui_been_displayed);
+
+  if (!args_.is_eula_required_set) {
+    HRESULT hr = install_self::SetEulaAccepted(is_machine_);
     if (FAILED(hr)) {
-      CORE_LOG(LE, (_T("[Setup::SetEulaAccepted failed][0x%08x]"), hr));
+      CORE_LOG(LE, (_T("[install_self::SetEulaAccepted failed][0x%08x]"), hr));
       return hr;
     }
   }
 
-  omaha::Worker worker(is_machine_);
-  HRESULT hr = worker.Main(goopdate_);
-  has_uninstalled_ = worker.has_uninstalled();
+  HRESULT hr = InitializeClientSecurity();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // If /sessionid wasn't specified on the command line, generate a random GUID
+  // to use for the session ID.  (This can happen if a metainstaller from the
+  // prior version does a handoff to a newer version.)
+  CString session_id = args_.session_id;
+  if (session_id.IsEmpty()) {
+    VERIFY1(SUCCEEDED(GetGuid(&session_id)));
+  }
+
+  hr = InstallApps(is_machine_,
+                   !args_.is_silent_set,  // is_interactive.
+                   !args_.is_eula_required_set,  // is_eula_accepted.
+                   args_.is_oem_set,
+                   args_.is_offline_set,
+                   args_.offline_dir,
+                   args_.extra,
+                   args_.install_source,
+                   session_id,
+                   has_ui_been_displayed);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Install failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT GoopdateImpl::DoUpdateAllApps(bool* has_ui_been_displayed ) {
+  OPT_LOG(L1, (_T("[GoopdateImpl::DoUpdateAllApps]")));
+  ASSERT1(has_ui_been_displayed);
+
+  bool is_interactive_update = !args_.is_silent_set;
+
+  // TODO(omaha3): Interactive is used as an indication of an on-demand request.
+  // It might also be useful to allow on-demand silent update requests.
+  // This was a request when we added the ability to disable updates.
+  // TODO(omaha3): These on-demand requests should also allow updates if the
+  // app update policy is set to on-demand in addition to silent auto.
+  const bool is_on_demand = is_interactive_update;
+
+  CString install_source = args_.install_source;
+
+  if (is_on_demand && install_source.IsEmpty()) {
+    // Set an install source for interactive/on-demand update all apps.
+    install_source = kCmdLineInstallSource_OnDemandUA;
+  }
+
+  // TODO(omaha): Consider moving InitializeClientSecurity calls inside
+  // install_apps.cc or maybe to update3_utils::CreateGoogleUpdate3Class().
+  HRESULT hr = InitializeClientSecurity();
+  if (FAILED(hr)) {
+    ASSERT1(false);
+    return is_interactive_update ? hr : S_OK;
+  }
+
+  hr = UpdateApps(is_machine_,
+                  is_interactive_update,
+                  is_on_demand,
+                  install_source,
+                  args_.extra.language,
+                  has_ui_been_displayed);
+  OPT_LOG(L2, (_T("[Update all apps process finished][0x%x]"), hr));
 
   // The UA worker always returns S_OK. UA can be launched by the scheduled task
   // or the core, neither of which wait for a return code. On Vista, returning
   // an error code from the scheduled task reportedly causes issues with the
   // task scheduler in rare cases, so returning S_OK helps with that as well.
-  if (args_.mode == COMMANDLINE_MODE_UA) {
-    return S_OK;
-  }
-
-  return hr;
+  // However, in interactive cases, we should return the actual error so that it
+  // can be reported to the user if necessary.
+  return is_interactive_update ? hr : S_OK;
 }
 
 HRESULT GoopdateImpl::DoCrash() {
@@ -1097,6 +1240,69 @@
 #endif
 }
 
+HRESULT GoopdateImpl::HandleUninstall() {
+  // TODO(omaha3): Why don't we always acquire this lock before uninstalling?
+  // We don't in the /install case. I guess it's important that we uninstall
+  // the first time in that case, but there's a chance that the case in the
+  // referenced bug could occur while a user is installing.
+
+  // Attempt a conditional uninstall and always return S_OK to avoid
+  // executing error handling code in the case of an actual uninstall.
+  // Do not attempt to uninstall if MSI is busy to avoid spurious uninstalls.
+  // See http://b/1436223. The call to WaitForMSIExecute blocks with a
+  // timeout. It is better to block here than block while holding the setup
+  // lock.
+  HRESULT hr = WaitForMSIExecute(kWaitForMSIExecuteMs);
+  CORE_LOG(L2, (_T("[WaitForMSIExecute returned 0x%08x]"), hr));
+  if (SUCCEEDED(hr)) {
+    MaybeUninstallGoogleUpdate();
+  }
+  return S_OK;
+}
+
+HRESULT GoopdateImpl::HandlePing() {
+  return Ping::HandlePing(is_machine_, args_.ping_string);
+}
+
+// TODO(omaha3): In Omaha 2, this was also called when /ig failed. There is a
+// separate call to UninstallSelf for /install in goopdate.cc. Should we call
+// this instead to ensure we ping? Should we try to only call from one location?
+// If we move it, make sure it is called regardless of LastChecked. See
+// http://b/2663423.
+
+// Uninstall is a tricky use case. Uninstall can primarily happen in three cases
+// and there are two mechanisms to uninstall. The cases in which Omaha
+// uninstalls are:
+// 1. The last registered application uninstalls. If Omaha is long-running,
+// Omaha monitors the Client keys and it will trigger an immediate uninstall in
+// this case.
+// 2. The core starts an update worker, if there are no registered
+// applications, the update worker will do the uninstall.
+// 3. An error, including user cancel, happens during Omaha or app installation
+// and there are no registered applications.
+// The uninstall is implemented in terms of the following mechanisms:
+// * An update worker launched with "/ua /uninstalled" by the core, in the
+// first two cases above.
+// * A direct uninstall, in the case of errors or user cancellations, in the
+// last case above.
+//
+// Omaha can uninstall only if there are no install workers running and no
+// registered applications. This check is done under the setup lock protection.
+// In addition, the uninstall worker takes the update worker lock. Acquiring
+// this lock is important since the silent installers can modify the
+// registration of apps and trigger uninstalls workers. Therefore, both
+// setup lock and the update worker locks are needed.
+//
+// In the direct uninstall case there is a small race condition, since there is
+// no other single lock that can be acquired to prevent changes to the
+// application registration. The code looks for install workers but the test is
+// racy if not protected by locks.
+void GoopdateImpl::MaybeUninstallGoogleUpdate() {
+  CORE_LOG(L1, (_T("[MaybeUninstallGoogleUpdate]")));
+  has_uninstalled_ =
+      !!SUCCEEDED(install_self::UninstallSelf(is_machine_, true));
+}
+
 // In dbg builds, also checks the post conditions of a /install process to
 // ensure that the registry is correctly populated or has been cleaned up.
 HRESULT GoopdateImpl::UninstallIfNecessary() {
@@ -1105,14 +1311,12 @@
 
   if (is_machine_ && !vista_util::IsUserAdmin()) {
     // The non-elevated instance, so do not try to uninstall.
-    ASSERT1(!args_.is_install_elevated);
     return S_OK;
   } else {
     CORE_LOG(L2, (_T("[GoopdateImpl::Main /install][Uninstall if necessary]")));
     // COM must be initialized in order to uninstall the scheduled task(s).
     scoped_co_init init_com_apt(COINIT_MULTITHREADED);
-    Setup setup(is_machine_, &args_);
-    return setup.Uninstall();
+    return install_self::UninstallSelf(is_machine_, false);
   }
 }
 
@@ -1123,10 +1327,34 @@
                    NULL);
 }
 
+HRESULT GoopdateImpl::CaptureOSMetrics() {
+  int major_version(0);
+  int minor_version(0);
+  int service_pack_major(0);
+  int service_pack_minor(0);
+  TCHAR name[MAX_PATH] = {0};
+
+  HRESULT hr = SystemInfo::GetSystemVersion(&major_version,
+                                            &minor_version,
+                                            &service_pack_major,
+                                            &service_pack_minor,
+                                            name,
+                                            arraysize(name));
+  if (SUCCEEDED(hr)) {
+    metric_windows_major_version    = major_version;
+    metric_windows_minor_version    = minor_version;
+    metric_windows_sp_major_version = service_pack_major;
+    metric_windows_sp_minor_version = service_pack_minor;
+  }
+
+  return hr;
+}
+
 }  // namespace detail
 
 namespace internal {
 
+// TODO(omaha3): Consider moving to app_registry_utils.
 // Returns early if Google Update's EULA is already accepted, as indicated by
 // the lack of eulaaccepted in the Update key.
 // The apps' values are not modified or deleted.
@@ -1158,24 +1386,215 @@
       continue;
     }
 
-    if (goopdate_utils::IsAppEulaAccepted(is_machine, sub_key_name, true)) {
+    if (app_registry_utils::IsAppEulaAccepted(is_machine, sub_key_name, true)) {
       ASSERT1(kGoogleUpdateAppId != sub_key_name);
-      return Setup::SetEulaAccepted(is_machine);
+      return install_self::SetEulaAccepted(is_machine);
     }
   }
 
   return S_OK;
 }
 
+// TODO(omaha): Use a registry override instead.
+#if !OFFICIAL_BUILD
+bool IsOmahaShellRunningFromStaging() {
+  return !app_util::GetModuleName(NULL).CompareNoCase(kOmahaShellFileName) &&
+         app_util::GetModuleDirectory(NULL).Right(_tcslen(_T("staging"))) ==
+         _T("staging");
+}
+#endif
+
+// TODO(omaha): needs_admin is only used for one case. Can we avoid it?
+bool IsMachineProcess(CommandLineMode mode,
+                      bool is_running_from_official_machine_directory,
+                      bool is_local_system,
+                      bool is_machine_override,
+                      Tristate needs_admin) {
+  switch (mode) {
+    // These "install" operations may not be running from the installed
+    // location.
+    case COMMANDLINE_MODE_INSTALL:
+    case COMMANDLINE_MODE_HANDOFF_INSTALL:
+    case COMMANDLINE_MODE_REGISTER_PRODUCT:
+    case COMMANDLINE_MODE_UNREGISTER_PRODUCT:
+      ASSERT1(TRISTATE_NONE != needs_admin);
+      return TRISTATE_TRUE == needs_admin;
+
+    // The following is a Code Red repair executable, which runs from temp dir.
+    case COMMANDLINE_MODE_RECOVER:
+      return is_machine_override;
+
+    // The following always runs as the user and may provide UI for on-demand
+    // installs and browser launches.
+    // The install location determines user vs. machine.
+    case COMMANDLINE_MODE_COMSERVER:
+    case COMMANDLINE_MODE_ONDEMAND:
+#if !OFFICIAL_BUILD
+      // Return machine == true. This is to facilitate unit tests such as
+      // GoogleUpdateCoreTest.LaunchCmdElevated_LocalServerRegistered.
+      if (IsOmahaShellRunningFromStaging()) {
+        return true;
+      }
+#endif
+
+      ASSERT1(goopdate_utils::IsRunningFromOfficialGoopdateDir(false) ||
+              goopdate_utils::IsRunningFromOfficialGoopdateDir(true) ||
+              _T("omaha_unittest.exe") == app_util::GetCurrentModuleName() ||
+              _T("GoogleUpdate_unsigned.exe") ==
+                  app_util::GetModuleName(NULL));  // Running in debugger.
+      return is_running_from_official_machine_directory;
+
+    // The broker forwarder is elevatable and always runs as the user it was
+    // created as.
+    case COMMANDLINE_MODE_COMBROKER:
+      return is_running_from_official_machine_directory;
+
+    // The following always runs as the user and is user-initiated.
+    case COMMANDLINE_MODE_WEBPLUGIN:
+      // The install location determines user vs. machine.
+      // This may not be the desired value when doing a cross-install or using
+      // the opposite plugin (i.e. user plugin is often used before the machine
+      // one).
+      ASSERT1(goopdate_utils::IsRunningFromOfficialGoopdateDir(false) ||
+              goopdate_utils::IsRunningFromOfficialGoopdateDir(true) ||
+              _T("omaha_unittest.exe") == app_util::GetCurrentModuleName());
+      return is_running_from_official_machine_directory;
+
+    // The following all run silently as the user for user installs or Local
+    // System for machine installs.
+    case COMMANDLINE_MODE_UPDATE:
+    case COMMANDLINE_MODE_CODE_RED_CHECK:
+      return is_local_system;
+
+    // The following all run silently as the user for user installs or Local
+    // System for machine installs.
+    case COMMANDLINE_MODE_UA:
+      return is_local_system ? true : is_machine_override;
+
+    // /ua runs silently as the user for user installs or Local System for
+    // machine installs. Interactive machine /ua may be run as the user if
+    // /machine is specified.
+    case COMMANDLINE_MODE_CORE:
+    case COMMANDLINE_MODE_CRASH_HANDLER:
+      return is_local_system;
+
+    // The following runs silently as Local System.
+    case COMMANDLINE_MODE_SERVICE:
+    case COMMANDLINE_MODE_MEDIUM_SERVICE:
+      ASSERT1(is_local_system);
+      return is_local_system;
+
+    // The following run as machine for all installs.
+    case COMMANDLINE_MODE_SERVICE_REGISTER:
+    case COMMANDLINE_MODE_SERVICE_UNREGISTER:
+      return true;
+
+    // The crashing process determines whether it was a machine or user omaha
+    // and correctly sets the /machine switch.
+    case COMMANDLINE_MODE_REPORTCRASH:
+      return is_machine_override;
+
+    // The following all run silently as the user for all installs.
+    case COMMANDLINE_MODE_REGSERVER:
+    case COMMANDLINE_MODE_UNREGSERVER:
+#if !OFFICIAL_BUILD
+      // Return machine == true. This is to facilitate unit tests such as
+      // GoogleUpdateCoreTest.LaunchCmdElevated_LocalServerRegistered.
+      if (IsOmahaShellRunningFromStaging()) {
+        return true;
+      }
+#endif
+
+      ASSERT1(goopdate_utils::IsRunningFromOfficialGoopdateDir(false) ||
+              goopdate_utils::IsRunningFromOfficialGoopdateDir(true) ||
+              _T("omaha_unittest.exe") == app_util::GetCurrentModuleName());
+      return is_running_from_official_machine_directory;
+
+    // The following may run as user or Local System. Thus, use the directory.
+    case COMMANDLINE_MODE_UNINSTALL:
+    case COMMANDLINE_MODE_PING:
+      ASSERT1(goopdate_utils::IsRunningFromOfficialGoopdateDir(false) ||
+              goopdate_utils::IsRunningFromOfficialGoopdateDir(true) ||
+              _T("omaha_unittest.exe") == app_util::GetCurrentModuleName());
+      return is_running_from_official_machine_directory;
+
+    // The following are miscellaneous modes that we do not expect to be running
+    // in the wild.
+    case COMMANDLINE_MODE_NOARGS:
+    case COMMANDLINE_MODE_UNKNOWN:
+    case COMMANDLINE_MODE_NETDIAGS:
+    case COMMANDLINE_MODE_CRASH:
+    default:
+      return is_running_from_official_machine_directory;
+  }
+}
+
+bool CanDisplayUi(CommandLineMode mode, bool is_silent) {
+  switch (mode) {
+    case COMMANDLINE_MODE_UNKNOWN:
+      // This mode is not one of our known silent modes. Therefore, we can
+      // display an error UI.
+      return true;
+
+    case COMMANDLINE_MODE_INSTALL:
+    case COMMANDLINE_MODE_HANDOFF_INSTALL:
+    case COMMANDLINE_MODE_UA:
+      // These modes have UI unless they are silent.
+      // UA is usually silent, but follows the same logic.
+      return !is_silent;
+
+    case COMMANDLINE_MODE_NOARGS:
+    case COMMANDLINE_MODE_CORE:
+    case COMMANDLINE_MODE_SERVICE:
+    case COMMANDLINE_MODE_REGSERVER:
+    case COMMANDLINE_MODE_UNREGSERVER:
+    case COMMANDLINE_MODE_NETDIAGS:
+    case COMMANDLINE_MODE_CRASH:
+    case COMMANDLINE_MODE_REPORTCRASH:
+    case COMMANDLINE_MODE_UPDATE:
+    case COMMANDLINE_MODE_RECOVER:
+    case COMMANDLINE_MODE_WEBPLUGIN:
+    case COMMANDLINE_MODE_CODE_RED_CHECK:
+    case COMMANDLINE_MODE_COMSERVER:
+    case COMMANDLINE_MODE_REGISTER_PRODUCT:
+    case COMMANDLINE_MODE_UNREGISTER_PRODUCT:
+    case COMMANDLINE_MODE_SERVICE_REGISTER:
+    case COMMANDLINE_MODE_SERVICE_UNREGISTER:
+    case COMMANDLINE_MODE_CRASH_HANDLER:
+    case COMMANDLINE_MODE_COMBROKER:
+    case COMMANDLINE_MODE_ONDEMAND:
+    case COMMANDLINE_MODE_MEDIUM_SERVICE:
+    case COMMANDLINE_MODE_UNINSTALL:
+    case COMMANDLINE_MODE_PING:
+    default:
+      // These modes are always silent.
+      return false;
+  }
+}
+
 }  // namespace internal
 
+Goopdate* Goopdate::instance_              = NULL;
+
+Goopdate& Goopdate::Instance() {
+  ASSERT1(instance_);
+  return *instance_;
+}
+
 Goopdate::Goopdate(bool is_local_system) {
   CORE_LOG(L2, (_T("[Goopdate::Goopdate]")));
   impl_.reset(new detail::GoopdateImpl(this, is_local_system));
+
+  ASSERT1(!instance_);
+  instance_ = this;
 }
 
 Goopdate::~Goopdate() {
   CORE_LOG(L2, (_T("[Goopdate::~Goopdate]")));
+
+  Stop();
+
+  instance_ = NULL;
 }
 
 HRESULT Goopdate::Main(HINSTANCE instance,
@@ -1184,20 +1603,16 @@
   return impl_->Main(instance, cmd_line, cmd_show);
 }
 
+HRESULT Goopdate::QueueUserWorkItem(UserWorkItem* work_item, uint32 flags) {
+  return impl_->QueueUserWorkItem(work_item, flags);
+}
+
+void Goopdate::Stop() {
+  return impl_->Stop();
+}
+
 bool Goopdate::is_local_system() const {
   return impl_->is_local_system();
 }
 
-CommandLineArgs Goopdate::args() const {
-  return impl_->args();
-}
-
-CString Goopdate::cmd_line() const {
-  return impl_->cmd_line();
-}
-
-OBJECT_ENTRY_AUTO(__uuidof(ProcessLauncherClass), ProcessLauncher)
-OBJECT_ENTRY_AUTO(__uuidof(OnDemandUserAppsClass), OnDemandCOMClass)
-OBJECT_ENTRY_AUTO(__uuidof(OnDemandMachineAppsClass), OnDemandCOMClassMachine)
-
 }  // namespace omaha
diff --git a/goopdate/goopdate.def b/goopdate/goopdate.def
index 174440b..25f7cbb 100644
--- a/goopdate/goopdate.def
+++ b/goopdate/goopdate.def
@@ -1,10 +1,10 @@
-; Copyright 2003-2009 Google Inc.
+; Copyright 2009 Google Inc.
 ;
 ; Licensed under the Apache License, Version 2.0 (the "License");
 ; you may not use this file except in compliance with the License.
 ; You may obtain a copy of the License at
 ;
-;      http:;www.apache.org/licenses/LICENSE-2.0
+;      http://www.apache.org/licenses/LICENSE-2.0
 ;
 ; Unless required by applicable law or agreed to in writing, software
 ; distributed under the License is distributed on an "AS IS" BASIS,
@@ -16,6 +16,4 @@
 
 EXPORTS
   DllEntry            PRIVATE
-  DllCanUnloadNow     PRIVATE
-  DllGetClassObject   PRIVATE
 
diff --git a/goopdate/goopdate.h b/goopdate/goopdate.h
index 84863fa..30b692b 100644
--- a/goopdate/goopdate.h
+++ b/goopdate/goopdate.h
@@ -21,6 +21,7 @@
 #include <atlstr.h>
 #include "base/basictypes.h"
 #include "base/scoped_ptr.h"
+#include "omaha/base/thread_pool.h"
 
 namespace omaha {
 
@@ -39,14 +40,22 @@
   explicit Goopdate(bool is_local_system);
   ~Goopdate();
 
+  // Gets the singleton instance of the class.
+  static Goopdate& Instance();
+
   // Runs the entry point for the application.
   HRESULT Main(HINSTANCE instance, const TCHAR* cmd_line, int cmd_show);
 
+  HRESULT QueueUserWorkItem(UserWorkItem* work_item, uint32 flags);
+
+  void Stop();
+
   bool is_local_system() const;
   CommandLineArgs args() const;
-  CString cmd_line() const;
 
  private:
+  static Goopdate* instance_;
+
   // Uses pimpl idiom to minimize dependencies on implementation details.
   scoped_ptr<detail::GoopdateImpl> impl_;
 
diff --git a/goopdate/goopdate.rc b/goopdate/goopdate.rc
index 1b4e1c6..1ede05b 100644
--- a/goopdate/goopdate.rc
+++ b/goopdate/goopdate.rc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -13,15 +13,20 @@
 // limitations under the License.
 // ========================================================================
 
+// TODO(omaha3): Figure out what category of resources this file includes and
+// rename it and non_localized_resource.h. Is it DLL resources?
+
 #include <afxres.h>
-#include "omaha/goopdate/resource.h"
+#include "omaha/goopdate/non_localized_resource.h"
 
 LANGUAGE LANG_NEUTRAL, SUBLANG_DEFAULT
-IDR_GOOGLE_UPDATE_WORKER_CLASS          REGISTRY "omaha/common/generic_reg_file_local_server.rgs"
-IDR_GOOGLE_UPDATE_WORKER_CLASS_MACHINE  REGISTRY "omaha/common/generic_reg_file_elevation_localserver.rgs"
+IDI_ELEVATION_MONIKER_ICON        ICON "omaha/google_update/google_update.ico"
 
-IDR_GOOGLE_UPDATE_SERVICE_APPID         REGISTRY "omaha/common/generic_reg_file_appid.rgs"
-IDR_GOOGLE_UPDATE_CORE_CLASS            REGISTRY "omaha/common/generic_reg_file_localservice.rgs"
+IDR_LOCAL_SERVER_RGS              REGISTRY "omaha/base/generic_reg_file_local_server.rgs"
+IDR_LOCAL_SERVER_ELEVATION_RGS    REGISTRY "omaha/base/generic_reg_file_elevation_localserver.rgs"
+IDR_LOCAL_SERVER_IE_LOW_RGS       REGISTRY "omaha/base/generic_reg_file_ie_low_local_server.rgs"
+IDR_LOCAL_SERVICE_RGS             REGISTRY "omaha/base/generic_reg_file_localservice.rgs"
 
-IDI_ELEVATION_MONIKER_ICON              ICON "omaha/goopdate/goopdate.ico"
+IDR_GOOGLE_UPDATE3_SERVICE_APPID  REGISTRY "omaha/base/generic_reg_file_appid.rgs"
 
+1 TYPELIB "goopdate\\omaha3_idl.tlb"
diff --git a/goopdate/goopdate_command_line_validator.cc b/goopdate/goopdate_command_line_validator.cc
deleted file mode 100644
index 25393f0..0000000
--- a/goopdate/goopdate_command_line_validator.cc
+++ /dev/null
@@ -1,536 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/goopdate/goopdate_command_line_validator.h"
-
-#include "omaha/common/const_cmd_line.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/string.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/command_line_parser.h"
-#include "omaha/goopdate/command_line_validator.h"
-#include "omaha/goopdate/extra_args_parser.h"
-
-namespace omaha {
-
-GoopdateCommandLineValidator::GoopdateCommandLineValidator() {
-}
-
-GoopdateCommandLineValidator::~GoopdateCommandLineValidator() {
-}
-
-HRESULT GoopdateCommandLineValidator::Setup() {
-  validator_.reset(new CommandLineValidator);
-
-  CString cmd_line;
-
-  // gu.exe
-  cmd_line.Empty();
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnNoArgs);
-
-  // gu.exe /c [/nocrashserver
-  cmd_line.Format(_T("/%s [/%s"), kCmdLineCore, kCmdLineNoCrashHandler);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnCore);
-
-  // gu.exe /crashhandler
-  cmd_line.Format(_T("/%s"), kCmdLineCrashHandler);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnCrashHandler);
-
-  // gu.exe /svc
-  cmd_line.Format(_T("/%s"), kCmdLineService);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnService);
-
-  // gu.exe /regsvc
-  cmd_line.Format(_T("/%s"), kCmdLineRegisterService);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnServiceRegister);
-
-  // gu.exe /unregsvc
-  cmd_line.Format(_T("/%s"), kCmdLineUnregisterService);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnServiceUnregister);
-
-  // gu.exe /regserver
-  cmd_line.Format(_T("/%s"), kCmdRegServer);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnRegServer);
-
-  // gu.exe /unregserver
-  cmd_line.Format(_T("/%s"), kCmdUnregServer);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnUnregServer);
-
-  // gu.exe /netdiags
-  cmd_line.Format(_T("/%s"), kCmdLineNetDiags);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnNetDiags);
-
-  // gu.exe /crash
-  cmd_line.Format(_T("/%s"), kCmdLineCrash);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnCrash);
-
-  // gu.exe -Embedding. The -Embedding text is injected via COM.
-  CreateScenario(kCmdLineComServerDash,
-                 &GoopdateCommandLineValidator::OnComServer);
-
-  // gu.exe /install <extraargs> [/appargs <appargs> [/installsource source
-  //        [/silent [/eularequired [/oem [/installelevated
-  cmd_line.Format(_T("/%s extra [/%s appargs [/%s src [/%s [/%s [/%s [/%s"),
-                  kCmdLineInstall,
-                  kCmdLineAppArgs,
-                  kCmdLineInstallSource,
-                  kCmdLineSilent,
-                  kCmdLineEulaRequired,
-                  kCmdLineOem,
-                  kCmdLineInstallElevated);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnInstall);
-
-  // gu.exe /update
-  cmd_line.Format(_T("/%s"), kCmdLineUpdate);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnUpdate);
-
-  // gu.exe /ig <extraargs> [/appargs <appargs> [/installsource source
-  //        [/silent [/eularequired [/offlineinstall
-  cmd_line.Format(_T("/%s extraargs [/%s appargs [/%s source [/%s [/%s [/%s"),
-                  kCmdLineFinishGoogleUpdateInstall,
-                  kCmdLineAppArgs,
-                  kCmdLineInstallSource,
-                  kCmdLineSilent,
-                  kCmdLineEulaRequired,
-                  kCmdLineOfflineInstall);
-  CreateScenario(cmd_line,
-                 &GoopdateCommandLineValidator::OnFinishInstallGoopdate);
-
-  // gu.exe /handoff <extraargs> [/appargs <appargs> [/installsource source
-  //        [/silent [/eularequired [/offlineinstall
-  cmd_line.Format(_T("/%s extra [/%s appargs [/%s source [/%s [/%s [/%s"),
-                  kCmdLineAppHandoffInstall,
-                  kCmdLineAppArgs,
-                  kCmdLineInstallSource,
-                  kCmdLineSilent,
-                  kCmdLineEulaRequired,
-                  kCmdLineOfflineInstall);
-  CreateScenario(cmd_line,
-                 &GoopdateCommandLineValidator::OnInstallHandoffWorker);
-
-  // gu.exe /ua [/installsource source [/uninstall
-  cmd_line.Format(_T("/%s [/%s source [/%s"),
-                  kCmdLineUpdateApps, kCmdLineInstallSource, kCmdLineUninstall);
-  CreateScenario(cmd_line,
-                 &GoopdateCommandLineValidator::OnUpdateApps);
-
-  // gu.exe /ug [/machine
-  cmd_line.Format(_T("/%s [/%s"),
-                  kCmdLineFinishGoogleUpdateUpdate, kCmdLineMachine);
-  CreateScenario(cmd_line,
-                 &GoopdateCommandLineValidator::OnFinishUpdateGoopdate);
-
-  // gu.exe /report <crash_filename> [/machine
-  //        [/custom_info <custom_info_filename>
-  cmd_line.Format(_T("/%s filename [/%s [/%s customfilename"),
-                  kCmdLineReport,
-                  kCmdLineMachine,
-                  kCmdLineCustomInfoFileName);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnReportCrash);
-
-  // gu.exe /report /i <crash_filename> [/machine
-  cmd_line.Format(_T("/%s /%s filename [/%s"),
-                  kCmdLineReport,
-                  kCmdLineInteractive,
-                  kCmdLineMachine);
-  CreateScenario(cmd_line,
-                 &GoopdateCommandLineValidator::OnReportCrashInteractive);
-
-  // gu.exe /pi <domainurl> <args> /installsource oneclick
-  cmd_line.Format(_T("/%s domainurl args /%s oneclick"),
-                  kCmdLineWebPlugin,
-                  kCmdLineInstallSource);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnWebPlugin);
-
-  // gu.exe /cr
-  cmd_line.Format(_T("/%s"), kCmdLineCodeRedCheck);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnCodeRed);
-
-  // gu.exe /recover <repair_file>
-  cmd_line.Format(_T("/%s repairfile"), kCmdLineRecover);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnRecover);
-
-  // gu.exe /recover /machine <repair_file>
-  cmd_line.Format(_T("/%s /%s repairfile"), kCmdLineRecover, kCmdLineMachine);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnRecoverMachine);
-
-  // gu.exe /registerproduct "extraargs" [/installsource source
-  cmd_line.Format(_T("/%s extraargs [/%s source"),
-                  kCmdLineRegisterProduct,
-                  kCmdLineInstallSource);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnRegisterProduct);
-
-  // gu.exe /unregisterproduct "extraargs"
-  cmd_line.Format(_T("/%s extraargs"), kCmdLineUnregisterProduct);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnUnregisterProduct);
-
-  //
-  // Legacy support command lines.
-  //
-
-  // gu.exe /uiuser <manifestfilename>
-  cmd_line.Format(_T("/%s filename"),
-                  kCmdLineLegacyUserManifest);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnUiUserManifest);
-
-  // TODO(omaha):  Can we remove this case or is it here for back compat?
-  // gu.exe /ui /lang en <manifestfilename>
-  cmd_line.Format(_T("/%s /%s en filename"),
-                  kCmdLineLegacyUi,
-                  kCmdLineLegacyLang);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnUiLangManifest);
-
-  // gu.exe /ui <manifestfilename>
-  cmd_line.Format(_T("/%s filename"), kCmdLineLegacyUi);
-  CreateScenario(cmd_line, &GoopdateCommandLineValidator::OnUiManifest);
-
-  // gu.exe /handoff <extraargs> /lang <en> [/installsource source
-  cmd_line.Format(_T("/%s extraargs /%s en [/%s source"),
-                  kCmdLineAppHandoffInstall,
-                  kCmdLineLegacyLang,
-                  kCmdLineInstallSource);
-  CreateScenario(cmd_line,
-                 &GoopdateCommandLineValidator::OnInstallHandoffWorkerLegacy);
-
-  // gu.exe /install <extraargs> /installsource source /lang en.
-  cmd_line.Format(_T("/%s extraargs /%s source /%s en"),
-                  kCmdLineInstall, kCmdLineInstallSource, kCmdLineLegacyLang);
-  CreateScenario(cmd_line,
-                 &GoopdateCommandLineValidator::OnInstallWithSourceLegacy);
-  return S_OK;
-}
-
-// TODO(Omaha): Add check that each scenario is unique and does not overlap an
-// existing one in DBG builds.
-HRESULT GoopdateCommandLineValidator::Validate(const CommandLineParser* parser,
-                                               CommandLineArgs* args) {
-  ASSERT1(parser);
-  ASSERT1(args);
-
-  parser_ = parser;
-  args_ = args;
-
-  CString scenario_name;
-  HRESULT hr = validator_->Validate(*parser_, &scenario_name);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[GoopdateCommandLineValidator::Validate Failed][0x%x]"),
-                  hr));
-    return hr;
-  }
-
-  MapScenarioHandlersIter iter = scenario_handlers_.find(scenario_name);
-  if (iter == scenario_handlers_.end()) {
-    ASSERT1(false);
-    return GOOGLEUPDATE_COMMANDLINE_E_NO_SCENARIO_HANDLER;
-  }
-
-  ScenarioHandler handler = (*iter).second;
-  return (this->*handler)();
-}
-
-void GoopdateCommandLineValidator::CreateScenario(const TCHAR* cmd_line,
-                                                  ScenarioHandler handler) {
-  // Prepend the program name onto the cmd_line.
-  CString scenario_cmd_line;
-  scenario_cmd_line.Format(_T("prog.exe %s"), cmd_line);
-
-  CString scenario_name;
-  validator_->CreateScenarioFromCmdLine(scenario_cmd_line, &scenario_name);
-  // TODO(omaha): Make sure it doesn't already exist.
-  scenario_handlers_[scenario_name] = handler;
-}
-
-HRESULT GoopdateCommandLineValidator::GetExtraAndAppArgs(const CString& name) {
-  HRESULT hr = parser_->GetSwitchArgumentValue(name,
-                                               0,
-                                               &args_->extra_args_str);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = parser_->GetSwitchArgumentValue(kCmdLineAppArgs,
-                                       0,
-                                       &args_->app_args_str);
-  if (FAILED(hr)) {
-    args_->app_args_str.Empty();
-  }
-
-  ExtraArgsParser extra_args_parser;
-  return extra_args_parser.Parse(args_->extra_args_str,
-                                 args_->app_args_str,
-                                 &(args_->extra));
-}
-
-HRESULT GoopdateCommandLineValidator::OnNoArgs() {
-  args_->mode = COMMANDLINE_MODE_NOARGS;
-  return S_OK;
-}
-
-HRESULT GoopdateCommandLineValidator::OnCore() {
-  args_->mode = COMMANDLINE_MODE_CORE;
-  args_->is_crash_handler_disabled = parser_->HasSwitch(kCmdLineNoCrashHandler);
-
-  return S_OK;
-}
-
-HRESULT GoopdateCommandLineValidator::OnCrashHandler() {
-  args_->mode = COMMANDLINE_MODE_CRASH_HANDLER;
-  return S_OK;
-}
-
-HRESULT GoopdateCommandLineValidator::OnService() {
-  args_->mode = COMMANDLINE_MODE_SERVICE;
-  return S_OK;
-}
-
-HRESULT GoopdateCommandLineValidator::OnServiceRegister() {
-  args_->mode = COMMANDLINE_MODE_SERVICE_REGISTER;
-  return S_OK;
-}
-
-HRESULT GoopdateCommandLineValidator::OnServiceUnregister() {
-  args_->mode = COMMANDLINE_MODE_SERVICE_UNREGISTER;
-  return S_OK;
-}
-
-HRESULT GoopdateCommandLineValidator::OnRegServer() {
-  args_->mode = COMMANDLINE_MODE_REGSERVER;
-  return S_OK;
-}
-
-HRESULT GoopdateCommandLineValidator::OnUnregServer() {
-  args_->mode = COMMANDLINE_MODE_UNREGSERVER;
-  return S_OK;
-}
-
-HRESULT GoopdateCommandLineValidator::OnNetDiags() {
-  args_->mode = COMMANDLINE_MODE_NETDIAGS;
-  return S_OK;
-}
-
-HRESULT GoopdateCommandLineValidator::OnCrash() {
-  args_->mode = COMMANDLINE_MODE_CRASH;
-  return S_OK;
-}
-
-HRESULT GoopdateCommandLineValidator::OnComServer() {
-  args_->mode = COMMANDLINE_MODE_COMSERVER;
-  return S_OK;
-}
-
-HRESULT GoopdateCommandLineValidator::OnInstall() {
-  args_->mode = COMMANDLINE_MODE_INSTALL;
-  parser_->GetSwitchArgumentValue(kCmdLineInstallSource,
-                                  0,
-                                  &(args_->install_source));
-  args_->is_silent_set = parser_->HasSwitch(kCmdLineSilent);
-  args_->is_eula_required_set = parser_->HasSwitch(kCmdLineEulaRequired);
-  args_->is_oem_set = parser_->HasSwitch(kCmdLineOem);
-  args_->is_install_elevated = parser_->HasSwitch(kCmdLineInstallElevated);
-  return GetExtraAndAppArgs(kCmdLineInstall);
-}
-
-HRESULT GoopdateCommandLineValidator::OnInstallWithSourceLegacy() {
-  args_->mode = COMMANDLINE_MODE_INSTALL;
-  parser_->GetSwitchArgumentValue(kCmdLineInstallSource,
-                                  0,
-                                  &(args_->install_source));
-  parser_->GetSwitchArgumentValue(kCmdLineLegacyLang,
-                                  0,
-                                  &(args_->extra.language));
-  return GetExtraAndAppArgs(kCmdLineInstall);
-}
-
-HRESULT GoopdateCommandLineValidator::OnUpdate() {
-  args_->mode = COMMANDLINE_MODE_UPDATE;
-  return S_OK;
-}
-
-HRESULT GoopdateCommandLineValidator::OnInstallHandoffWorker() {
-  args_->mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  parser_->GetSwitchArgumentValue(kCmdLineInstallSource,
-                                  0,
-                                  &(args_->install_source));
-  args_->is_silent_set = parser_->HasSwitch(kCmdLineSilent);
-  args_->is_eula_required_set = parser_->HasSwitch(kCmdLineEulaRequired);
-  args_->is_offline_set = parser_->HasSwitch(kCmdLineOfflineInstall);
-  return GetExtraAndAppArgs(kCmdLineAppHandoffInstall);
-}
-
-HRESULT GoopdateCommandLineValidator::OnInstallHandoffWorkerLegacy() {
-  args_->mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  HRESULT hr = GetExtraAndAppArgs(kCmdLineAppHandoffInstall);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  parser_->GetSwitchArgumentValue(kCmdLineInstallSource,
-                                  0,
-                                  &(args_->install_source));
-  return parser_->GetSwitchArgumentValue(kCmdLineLegacyLang,
-                                         0,
-                                         &(args_->extra.language));
-}
-
-HRESULT GoopdateCommandLineValidator::OnUpdateApps() {
-  args_->mode = COMMANDLINE_MODE_UA;
-  args_->is_uninstall_set = parser_->HasSwitch(kCmdLineUninstall);
-  parser_->GetSwitchArgumentValue(kCmdLineInstallSource,
-                                  0,
-                                  &(args_->install_source));
-  return S_OK;
-}
-
-HRESULT GoopdateCommandLineValidator::OnFinishInstallGoopdate() {
-  args_->mode = COMMANDLINE_MODE_IG;
-  parser_->GetSwitchArgumentValue(kCmdLineInstallSource,
-                                  0,
-                                  &(args_->install_source));
-  args_->is_silent_set = parser_->HasSwitch(kCmdLineSilent);
-  args_->is_eula_required_set = parser_->HasSwitch(kCmdLineEulaRequired);
-  args_->is_offline_set = parser_->HasSwitch(kCmdLineOfflineInstall);
-  return GetExtraAndAppArgs(kCmdLineFinishGoogleUpdateInstall);
-}
-
-HRESULT GoopdateCommandLineValidator::OnFinishUpdateGoopdate() {
-  args_->mode = COMMANDLINE_MODE_UG;
-  args_->is_machine_set = parser_->HasSwitch(kCmdLineMachine);
-  return S_OK;
-}
-
-HRESULT GoopdateCommandLineValidator::OnReportCrash() {
-  args_->mode = COMMANDLINE_MODE_REPORTCRASH;
-  args_->is_machine_set = parser_->HasSwitch(kCmdLineMachine);
-  parser_->GetSwitchArgumentValue(kCmdLineCustomInfoFileName,
-                                  0,
-                                  &(args_->custom_info_filename));
-  return parser_->GetSwitchArgumentValue(kCmdLineReport,
-                                         0,
-                                         &(args_->crash_filename));
-}
-
-HRESULT GoopdateCommandLineValidator::OnReportCrashInteractive() {
-  args_->mode = COMMANDLINE_MODE_REPORTCRASH;
-  args_->is_interactive_set = true;
-  args_->is_machine_set = parser_->HasSwitch(kCmdLineMachine);
-  return parser_->GetSwitchArgumentValue(kCmdLineInteractive,
-                                         0,
-                                         &(args_->crash_filename));
-}
-
-HRESULT GoopdateCommandLineValidator::OnUiManifest() {
-  args_->mode = COMMANDLINE_MODE_LEGACYUI;
-  return parser_->GetSwitchArgumentValue(kCmdLineLegacyUi,
-                                         0,
-                                         &(args_->legacy_manifest_path));
-}
-
-HRESULT GoopdateCommandLineValidator::OnUiLangManifest() {
-  args_->mode = COMMANDLINE_MODE_LEGACYUI;
-  HRESULT hr = parser_->GetSwitchArgumentValue(kCmdLineLegacyLang,
-                                               0,
-                                               &(args_->extra.language));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return parser_->GetSwitchArgumentValue(kCmdLineLegacyLang,
-                                         1,
-                                         &(args_->legacy_manifest_path));
-}
-
-HRESULT GoopdateCommandLineValidator::OnUiUserManifest() {
-  args_->mode = COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF;
-  return parser_->GetSwitchArgumentValue(kCmdLineLegacyUserManifest,
-                                         0,
-                                         &(args_->legacy_manifest_path));
-}
-
-HRESULT GoopdateCommandLineValidator::OnWebPlugin() {
-  HRESULT hr = parser_->GetSwitchArgumentValue(kCmdLineInstallSource,
-                                               0,
-                                               &(args_->install_source));
-  if (FAILED(hr)) {
-    return hr;
-  }
-  // Validate install_source value.
-  args_->install_source.MakeLower();
-  if (args_->install_source.Compare(kCmdLineInstallSource_OneClick) != 0) {
-    args_->install_source.Empty();
-    return E_INVALIDARG;
-  }
-
-  args_->mode = COMMANDLINE_MODE_WEBPLUGIN;
-
-  CString urldomain;
-  hr = parser_->GetSwitchArgumentValue(kCmdLineWebPlugin,
-                                       0,
-                                       &urldomain);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  hr = StringUnescape(urldomain, &(args_->webplugin_urldomain));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  CString webplugin_args;
-  hr = parser_->GetSwitchArgumentValue(kCmdLineWebPlugin,
-                                       1,
-                                       &webplugin_args);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  return StringUnescape(webplugin_args, &(args_->webplugin_args));
-}
-
-HRESULT GoopdateCommandLineValidator::OnCodeRed() {
-  args_->mode = COMMANDLINE_MODE_CODE_RED_CHECK;
-  return S_OK;
-}
-
-HRESULT GoopdateCommandLineValidator::OnRecover() {
-  args_->mode = COMMANDLINE_MODE_RECOVER;
-  return parser_->GetSwitchArgumentValue(
-      kCmdLineRecover,
-      0,
-      &(args_->code_red_metainstaller_path));
-}
-
-HRESULT GoopdateCommandLineValidator::OnRecoverMachine() {
-  args_->mode = COMMANDLINE_MODE_RECOVER;
-  args_->is_machine_set = true;
-  return parser_->GetSwitchArgumentValue(
-      kCmdLineMachine,
-      0,
-      &(args_->code_red_metainstaller_path));
-}
-
-HRESULT GoopdateCommandLineValidator::OnRegisterProduct() {
-  args_->mode = COMMANDLINE_MODE_REGISTER_PRODUCT;
-  parser_->GetSwitchArgumentValue(kCmdLineInstallSource,
-                                  0,
-                                  &(args_->install_source));
-  return GetExtraAndAppArgs(kCmdLineRegisterProduct);
-}
-
-HRESULT GoopdateCommandLineValidator::OnUnregisterProduct() {
-  args_->mode = COMMANDLINE_MODE_UNREGISTER_PRODUCT;
-  return GetExtraAndAppArgs(kCmdLineUnregisterProduct);
-}
-
-}  // namespace omaha
-
diff --git a/goopdate/goopdate_command_line_validator.h b/goopdate/goopdate_command_line_validator.h
deleted file mode 100644
index aad133f..0000000
--- a/goopdate/goopdate_command_line_validator.h
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_GOOPDATE_GOOPDATE_COMMAND_LINE_VALIDATOR_H__
-#define OMAHA_GOOPDATE_GOOPDATE_COMMAND_LINE_VALIDATOR_H__
-
-#include <windows.h>
-#include <atlstr.h>
-
-#include <map>
-
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-
-namespace omaha {
-
-struct CommandLineArgs;
-class CommandLineParser;
-class CommandLineValidator;
-
-// Validates all of the command line permutations for googleupdate.exe.
-class GoopdateCommandLineValidator {
- public:
-  typedef HRESULT (GoopdateCommandLineValidator::*ScenarioHandler)();
-  typedef std::map<CString, ScenarioHandler> MapScenarioHandlers;
-  typedef MapScenarioHandlers::iterator MapScenarioHandlersIter;
-
-  GoopdateCommandLineValidator();
-  ~GoopdateCommandLineValidator();
-
-  // Sets up the scenarios.
-  HRESULT Setup();
-
-  // Validates a pre-parsed parser against the scenarios and returns a
-  // CommandLineArgs structure filled in with the proper values.
-  HRESULT Validate(const CommandLineParser* parser, CommandLineArgs* args);
-
- private:
-  // Specific command-line scenario handlers.
-  HRESULT OnNoArgs();
-  HRESULT OnCore();
-  HRESULT OnCrashHandler();
-  HRESULT OnService();
-  HRESULT OnServiceRegister();
-  HRESULT OnServiceUnregister();
-  HRESULT OnRegServer();
-  HRESULT OnUnregServer();
-  HRESULT OnNetDiags();
-  HRESULT OnCrash();
-  HRESULT OnComServer();
-  HRESULT OnInstall();
-  HRESULT OnInstallWithSourceLegacy();
-  HRESULT OnUpdate();
-  HRESULT OnInstallHandoffWorker();
-  HRESULT OnInstallHandoffWorkerLegacy();
-  HRESULT OnUpdateApps();
-  HRESULT OnFinishInstallGoopdate();
-  HRESULT OnFinishUpdateGoopdate();
-  HRESULT OnReportCrash();
-  HRESULT OnReportCrashInteractive();
-  HRESULT OnUiManifest();
-  HRESULT OnUiLangManifest();
-  HRESULT OnUiUserManifest();
-  HRESULT OnWebPlugin();
-  HRESULT OnCodeRed();
-  HRESULT OnRecover();
-  HRESULT OnRecoverMachine();
-  HRESULT OnRegisterProduct();
-  HRESULT OnUnregisterProduct();
-
-  void CreateScenario(const TCHAR* cmd_line, ScenarioHandler handler);
-
-  HRESULT GetExtraAndAppArgs(const CString& switch_name);
-
-  const CommandLineParser* parser_;
-  CommandLineArgs* args_;
-  scoped_ptr<CommandLineValidator> validator_;
-  MapScenarioHandlers scenario_handlers_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(GoopdateCommandLineValidator);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_GOOPDATE_COMMAND_LINE_VALIDATOR_H__
-
diff --git a/goopdate/goopdate_helper.cc b/goopdate/goopdate_helper.cc
deleted file mode 100644
index fb22665..0000000
--- a/goopdate/goopdate_helper.cc
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-
-#include <windows.h>
-
-#include "omaha/goopdate/goopdate_helper.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/resource.h"
-#include "omaha/setup/setup.h"
-#include "omaha/worker/application_manager.h"
-#include "omaha/worker/job_observer.h"
-#include "omaha/worker/ping_utils.h"
-#include "omaha/worker/worker_metrics.h"
-
-namespace omaha {
-
-// job_observer can be NULL.
-HRESULT FinishGoogleUpdateInstall(const CommandLineArgs& args,
-                                  bool is_machine,
-                                  bool is_self_update,
-                                  Ping* ping,
-                                  JobObserver* job_observer) {
-  CORE_LOG(L2, (_T("[FinishGoogleUpdateInstall]")));
-  ASSERT1(args.mode == COMMANDLINE_MODE_IG ||
-          args.mode == COMMANDLINE_MODE_UG);
-  ASSERT1(ping);
-
-  // Get the previous version before updating it.
-  AppManager app_manager(is_machine);
-  ProductData product_data;
-  HRESULT hr = app_manager.ReadProductDataFromStore(kGoopdateGuid,
-                                                    &product_data);
-  if (FAILED(hr)) {
-    CORE_LOG(L2, (_T("[ReadProductDataFromStore failed][0x%08x]"), hr));
-  }
-  const CString& previous_version = product_data.app_data().previous_version();
-
-  // Send a ping for the "start" of Setup. This is actually the start of setup
-  // phase 2. We can't ping at the true beginning of Setup because we can't
-  // ping from the temp directory.
-  HRESULT hr_ping = ping_utils::SendGoopdatePing(
-      is_machine,
-      args.extra,
-      args.install_source,
-      is_self_update ? PingEvent::EVENT_SETUP_UPDATE_BEGIN :
-                       PingEvent::EVENT_SETUP_INSTALL_BEGIN,
-      S_OK,
-      0,
-      NULL,
-      ping);
-  if (FAILED(hr_ping)) {
-    CORE_LOG(LW, (_T("[SendSetupPing(started) failed][0x%08x]"), hr_ping));
-  }
-
-  Setup setup(is_machine, &args);
-  hr = setup.SetupGoogleUpdate();
-  // Do not return until the ping is sent.
-
-  if (SUCCEEDED(hr)) {
-    CORE_LOG(L1, (_T("[Setup successfully completed]")));
-    // All Omaha installs are "offline" because there is no update check.
-    app_manager.RecordSuccessfulInstall(GUID_NULL,
-                                        kGoopdateGuid,
-                                        is_self_update,
-                                        !is_self_update);  // is_offline
-
-    if (is_self_update) {
-      ++metric_worker_self_updates_succeeded;
-    }
-  } else {
-    CORE_LOG(LE, (_T("[Setup::SetupGoogleUpdate failed][0x%08x]"), hr));
-
-    if (job_observer) {
-      ASSERT1(!is_self_update);
-      CString message;
-      message.FormatMessage(IDS_SETUP_FAILED, hr);
-      job_observer->OnComplete(COMPLETION_CODE_ERROR, message, hr);
-    }
-  }
-
-  // Send a ping to report Setup has completed.
-  hr_ping = ping_utils::SendPostSetupPing(hr,
-                                          setup.extra_code1(),
-                                          previous_version,
-                                          is_machine,
-                                          is_self_update,
-                                          args.extra,
-                                          args.install_source,
-                                          ping);
-  if (FAILED(hr_ping)) {
-    CORE_LOG(LW, (_T("[SendSetupPing(completed) failed][0x%08x]"), hr_ping));
-  }
-
-  return hr;
-}
-
-}  // namespace omaha
diff --git a/goopdate/goopdate_helper.h b/goopdate/goopdate_helper.h
deleted file mode 100644
index 11f3ee2..0000000
--- a/goopdate/goopdate_helper.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_GOOPDATE_GOOPDATE_HELPER_H__
-#define OMAHA_GOOPDATE_GOOPDATE_HELPER_H__
-
-#include <windows.h>
-
-
-namespace omaha {
-
-class JobObserver;
-class Ping;
-struct CommandLineArgs;
-
-// Sends the GoopdateStartedPing, calls setup to finish setup and sends
-// the finish setup complete ping.
-HRESULT FinishGoogleUpdateInstall(const CommandLineArgs& args,
-                                  bool is_machine,
-                                  bool is_self_update,
-                                  Ping* ping,
-                                  JobObserver* job_observer);
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_GOOPDATE_HELPER_H__
-
diff --git a/goopdate/goopdate_internal.h b/goopdate/goopdate_internal.h
new file mode 100644
index 0000000..69d18c7
--- /dev/null
+++ b/goopdate/goopdate_internal.h
@@ -0,0 +1,47 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_GOOPDATE_INTERNAL_H_
+#define OMAHA_GOOPDATE_GOOPDATE_INTERNAL_H_
+
+#include <windows.h>
+#include "omaha/base/constants.h"
+#include "omaha/common/command_line.h"
+
+namespace omaha {
+
+namespace internal {
+
+// Marks Google Update's EULA as accepted if an app EULA has been accepted.
+HRESULT PromoteAppEulaAccepted(bool is_machine);
+
+// Returns whether a process is a machine process.
+// Does not determine whether the process has the appropriate privileges.
+bool IsMachineProcess(
+    CommandLineMode mode,
+    bool is_running_from_official_machine_directory,
+    bool is_local_system,  // Whether process is running as local system.
+    bool is_machine_override,  // True if machine cmd line override specified.
+    Tristate needs_admin);  // needsadmin value for primary app if present.
+
+// Returns whether UI can be displayed.
+bool CanDisplayUi(CommandLineMode mode, bool is_silent);
+
+}  // namespace internal
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_GOOPDATE_INTERNAL_H_
+
diff --git a/goopdate/goopdate_metrics.cc b/goopdate/goopdate_metrics.cc
index 18013ef..679f443 100644
--- a/goopdate/goopdate_metrics.cc
+++ b/goopdate/goopdate_metrics.cc
@@ -22,11 +22,6 @@
 DEFINE_METRIC_integer(windows_sp_major_version);
 DEFINE_METRIC_integer(windows_sp_minor_version);
 
-DEFINE_METRIC_count(handoff_legacy_user);
-DEFINE_METRIC_count(handoff_legacy_machine);
-DEFINE_METRIC_count(handoff_legacy_10);
-DEFINE_METRIC_count(handoff_legacy_11);
-
 DEFINE_METRIC_count(crashes_total);
 DEFINE_METRIC_count(crashes_uploaded);
 DEFINE_METRIC_count(crashes_throttled);
diff --git a/goopdate/goopdate_metrics.h b/goopdate/goopdate_metrics.h
index 9eb3e2b..2b7dfdf 100644
--- a/goopdate/goopdate_metrics.h
+++ b/goopdate/goopdate_metrics.h
@@ -27,17 +27,6 @@
 DECLARE_METRIC_integer(windows_sp_major_version);
 DECLARE_METRIC_integer(windows_sp_minor_version);
 
-// How many times we received a user/machine legacy handoff from an Omaha 1.0.x
-// or 1.1.x metainstaller. These are determined early on in the handoff process
-// whereas the 1.0 and 1.1 metrics are not determined until the XML is parsed so
-// the version metrics may not sum up to the user/machine sum if errors occur.
-DECLARE_METRIC_count(handoff_legacy_user);
-DECLARE_METRIC_count(handoff_legacy_machine);
-// How many times we received a handoff from an Omaha 1.0.x metainstaller.
-DECLARE_METRIC_count(handoff_legacy_10);
-// How many times we received a handoff from an Omaha 1.1.x metainstaller.
-DECLARE_METRIC_count(handoff_legacy_11);
-
 // Crash metrics.
 //
 // A crash can be handled in one of the following ways: uploaded, rejected by
diff --git a/goopdate/goopdate_unittest.cc b/goopdate/goopdate_unittest.cc
index 97b82ef..1950ab8 100644
--- a/goopdate/goopdate_unittest.cc
+++ b/goopdate/goopdate_unittest.cc
@@ -13,34 +13,38 @@
 // limitations under the License.
 // ========================================================================
 
-#include "omaha/common/constants.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/goopdate/goopdate-internal.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/common/command_line.h"
+#include "omaha/goopdate/goopdate_internal.h"
 #include "omaha/testing/unit_test.h"
 
+namespace omaha {
+
 namespace {
 
 const TCHAR* const kAppMachineClientStatePath =
-    _T("HKLM\\Software\\Google\\Update\\ClientState\\")
-    _T("{19BE47E4-CF32-48c1-94C4-046507F6A8A6}\\");
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\{19BE47E4-CF32-48c1-94C4-046507F6A8A6}\\");
 const TCHAR* const kApp2MachineClientStatePath =
-    _T("HKLM\\Software\\Google\\Update\\ClientState\\")
-    _T("{553B2D8C-E6A7-43ed-ACC9-A8BA5D34395F}\\");
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\{553B2D8C-E6A7-43ed-ACC9-A8BA5D34395F}\\");
 const TCHAR* const kAppUserClientStatePath =
-    _T("HKCU\\Software\\Google\\Update\\ClientState\\")
-    _T("{19BE47E4-CF32-48c1-94C4-046507F6A8A6}\\");
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\{19BE47E4-CF32-48c1-94C4-046507F6A8A6}\\");
 const TCHAR* const kApp2UserClientStatePath =
-    _T("HKCU\\Software\\Google\\Update\\ClientState\\")
-    _T("{553B2D8C-E6A7-43ed-ACC9-A8BA5D34395F}\\");
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\{553B2D8C-E6A7-43ed-ACC9-A8BA5D34395F}\\");
 
 const TCHAR* const kAppMachineClientStateMediumPath =
-    _T("HKLM\\Software\\Google\\Update\\ClientStateMedium\\")
-    _T("{19BE47E4-CF32-48c1-94C4-046507F6A8A6}\\");
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientStateMedium\\{19BE47E4-CF32-48c1-94C4-046507F6A8A6}\\");
+
+// Update this when new modes are added.
+const int kLastMode = COMMANDLINE_MODE_PING;
 
 }  // namespace
 
-namespace omaha {
-
 class GoopdateRegistryProtectedTest : public testing::Test {
  protected:
   GoopdateRegistryProtectedTest()
@@ -626,8 +630,8 @@
 TEST_F(GoopdateRegistryProtectedTest,
        PromoteAppEulaAccepted_User_UpdateZero_MediumAppValueOneAndStateKey) {
   const TCHAR* const kAppUserClientStateMediumPath =
-      _T("HKCU\\Software\\Google\\Update\\ClientStateMedium\\")
-      _T("{19BE47E4-CF32-48c1-94C4-046507F6A8A6}\\");
+      _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+      _T("\\ClientStateMedium\\{19BE47E4-CF32-48c1-94C4-046507F6A8A6}\\");
 
   EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStatePath));
   EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
@@ -653,4 +657,346 @@
   EXPECT_EQ(1, value);
 }
 
+//
+// IsMachineProcess tests.
+//
+
+class GoopdateIsMachineProcessTest : public testing::Test {
+ protected:
+  bool FromMachineDirHelper(CommandLineMode mode) {
+    return internal::IsMachineProcess(mode,
+                                      true,
+                                      false,
+                                      false,
+                                      TRISTATE_NONE);
+  }
+
+  bool IsLocalSystemHelper(CommandLineMode mode) {
+    return internal::IsMachineProcess(mode,
+                                      false,
+                                      true,
+                                      false,
+                                      TRISTATE_NONE);
+  }
+
+  bool MachineOverrideHelper(CommandLineMode mode) {
+    return internal::IsMachineProcess(mode,
+                                      false,
+                                      false,
+                                      true,
+                                      TRISTATE_NONE);
+  }
+
+  bool NeedsAdminFalseHelper(CommandLineMode mode) {
+    return internal::IsMachineProcess(mode,
+                                      false,
+                                      false,
+                                      false,
+                                      TRISTATE_FALSE);
+  }
+
+  bool NeedsAdminTrueHelper(CommandLineMode mode) {
+    return internal::IsMachineProcess(mode,
+                                      false,
+                                      false,
+                                      false,
+                                      TRISTATE_TRUE);
+  }
+};
+
+// Unused function. Its sole purpose is to make sure that the unit tests below
+// were correctly updated when new modes were added.
+#pragma warning(push)
+// enumerator 'identifier' in switch of enum 'enumeration' is not explicitly
+// handled by a case label.
+// enumerator 'identifier' in switch of enum 'enumeration' is not handled.
+#pragma warning(1: 4061 4062)
+static void EnsureUnitTestUpdatedWithNewModes() {
+  CommandLineMode unused_mode;
+  switch (unused_mode) {
+    case COMMANDLINE_MODE_UNKNOWN:
+    case COMMANDLINE_MODE_NOARGS:
+    case COMMANDLINE_MODE_CORE:
+    case COMMANDLINE_MODE_SERVICE:
+    case COMMANDLINE_MODE_REGSERVER:
+    case COMMANDLINE_MODE_UNREGSERVER:
+    case COMMANDLINE_MODE_NETDIAGS:
+    case COMMANDLINE_MODE_CRASH:
+    case COMMANDLINE_MODE_REPORTCRASH:
+    case COMMANDLINE_MODE_INSTALL:
+    case COMMANDLINE_MODE_UPDATE:
+    case COMMANDLINE_MODE_HANDOFF_INSTALL:
+    case COMMANDLINE_MODE_UA:
+    case COMMANDLINE_MODE_RECOVER:
+    case COMMANDLINE_MODE_WEBPLUGIN:
+    case COMMANDLINE_MODE_CODE_RED_CHECK:
+    case COMMANDLINE_MODE_COMSERVER:
+    case COMMANDLINE_MODE_REGISTER_PRODUCT:
+    case COMMANDLINE_MODE_UNREGISTER_PRODUCT:
+    case COMMANDLINE_MODE_SERVICE_REGISTER:
+    case COMMANDLINE_MODE_SERVICE_UNREGISTER:
+    case COMMANDLINE_MODE_CRASH_HANDLER:
+    case COMMANDLINE_MODE_COMBROKER:
+    case COMMANDLINE_MODE_ONDEMAND:
+    case COMMANDLINE_MODE_MEDIUM_SERVICE:
+    case COMMANDLINE_MODE_UNINSTALL:
+    case COMMANDLINE_MODE_PING:
+    //
+    // When adding a new mode, be sure to update kLastMode too.
+    //
+      break;
+  }
+}
+#pragma warning(pop)
+
+TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_MachineDirOnly) {
+  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_UNKNOWN));
+  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_NOARGS));
+  EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_CORE));
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_SERVICE));
+  }
+  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_REGSERVER));
+  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_UNREGSERVER));
+  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_NETDIAGS));
+  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_CRASH));
+  // TODO(omaha): Change to machine.
+  EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_REPORTCRASH));
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_INSTALL));
+  }
+  EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_UPDATE));
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_HANDOFF_INSTALL));
+  }
+  EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_UA));
+  EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_RECOVER));
+  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_WEBPLUGIN));
+  EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_CODE_RED_CHECK));
+  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_COMSERVER));
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_REGISTER_PRODUCT));
+  }
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_UNREGISTER_PRODUCT));
+  }
+  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_SERVICE_REGISTER));
+  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_SERVICE_UNREGISTER));
+  EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_CRASH_HANDLER));
+  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_COMBROKER));
+  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_ONDEMAND));
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_MEDIUM_SERVICE));
+  }
+  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_UNINSTALL));
+  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_PING));
+  EXPECT_TRUE(FromMachineDirHelper(
+      static_cast<CommandLineMode>(kLastMode + 1)));
+}
+
+TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_IsLocalSystemOnly) {
+  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_UNKNOWN));
+  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_NOARGS));
+  EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_CORE));
+  EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_SERVICE));
+  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_REGSERVER));
+  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_UNREGSERVER));
+  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_NETDIAGS));
+  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_CRASH));
+  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_REPORTCRASH));
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_INSTALL));
+  }
+  EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_UPDATE));
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_HANDOFF_INSTALL));
+  }
+  EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_UA));
+  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_RECOVER));
+  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_WEBPLUGIN));
+  EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_CODE_RED_CHECK));
+  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_COMSERVER));
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_REGISTER_PRODUCT));
+  }
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_UNREGISTER_PRODUCT));
+  }
+  EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_SERVICE_REGISTER));
+  EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_SERVICE_UNREGISTER));
+  EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_CRASH_HANDLER));
+  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_COMBROKER));
+  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_ONDEMAND));
+  EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_MEDIUM_SERVICE));
+  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_UNINSTALL));
+  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_PING));
+  EXPECT_FALSE(IsLocalSystemHelper(
+      static_cast<CommandLineMode>(kLastMode + 1)));
+}
+
+TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_MachineOverrideOnly) {
+  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_UNKNOWN));
+  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_NOARGS));
+  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_CORE));
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_SERVICE));
+  }
+  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_REGSERVER));
+  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_UNREGSERVER));
+  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_NETDIAGS));
+  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_CRASH));
+  EXPECT_TRUE(MachineOverrideHelper(COMMANDLINE_MODE_REPORTCRASH));
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_INSTALL));
+  }
+  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_UPDATE));
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_HANDOFF_INSTALL));
+  }
+  EXPECT_TRUE(MachineOverrideHelper(COMMANDLINE_MODE_UA));
+  EXPECT_TRUE(MachineOverrideHelper(COMMANDLINE_MODE_RECOVER));
+  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_WEBPLUGIN));
+  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_CODE_RED_CHECK));
+  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_COMSERVER));
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_REGISTER_PRODUCT));
+  }
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_UNREGISTER_PRODUCT));
+  }
+  EXPECT_TRUE(MachineOverrideHelper(COMMANDLINE_MODE_SERVICE_REGISTER));
+  EXPECT_TRUE(MachineOverrideHelper(COMMANDLINE_MODE_SERVICE_UNREGISTER));
+  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_CRASH_HANDLER));
+  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_COMBROKER));
+  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_ONDEMAND));
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_MEDIUM_SERVICE));
+  }
+  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_UNINSTALL));
+  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_PING));
+  EXPECT_FALSE(MachineOverrideHelper(
+      static_cast<CommandLineMode>(kLastMode + 1)));
+}
+
+TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_NeedsAdminFalseOnly) {
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_UNKNOWN));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_NOARGS));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_CORE));
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_SERVICE));
+  }
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_REGSERVER));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_UNREGSERVER));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_NETDIAGS));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_CRASH));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_REPORTCRASH));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_INSTALL));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_UPDATE));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_HANDOFF_INSTALL));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_UA));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_RECOVER));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_WEBPLUGIN));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_CODE_RED_CHECK));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_COMSERVER));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_REGISTER_PRODUCT));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_UNREGISTER_PRODUCT));
+  EXPECT_TRUE(NeedsAdminFalseHelper(COMMANDLINE_MODE_SERVICE_REGISTER));
+  EXPECT_TRUE(NeedsAdminFalseHelper(COMMANDLINE_MODE_SERVICE_UNREGISTER));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_CRASH_HANDLER));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_COMBROKER));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_ONDEMAND));
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_MEDIUM_SERVICE));
+  }
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_UNINSTALL));
+  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_PING));
+  EXPECT_FALSE(NeedsAdminFalseHelper(
+      static_cast<CommandLineMode>(kLastMode + 1)));
+}
+
+TEST_F(GoopdateIsMachineProcessTest, IsMachineProcess_NeedsAdminTrueOnly) {
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_UNKNOWN));
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_NOARGS));
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_CORE));
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_SERVICE));
+  }
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_REGSERVER));
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_UNREGSERVER));
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_NETDIAGS));
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_CRASH));
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_REPORTCRASH));
+  EXPECT_TRUE(NeedsAdminTrueHelper(COMMANDLINE_MODE_INSTALL));
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_UPDATE));
+  EXPECT_TRUE(NeedsAdminTrueHelper(COMMANDLINE_MODE_HANDOFF_INSTALL));
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_UA));
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_RECOVER));
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_WEBPLUGIN));
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_CODE_RED_CHECK));
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_COMSERVER));
+  EXPECT_TRUE(NeedsAdminTrueHelper(COMMANDLINE_MODE_REGISTER_PRODUCT));
+  EXPECT_TRUE(NeedsAdminTrueHelper(COMMANDLINE_MODE_UNREGISTER_PRODUCT));
+  EXPECT_TRUE(NeedsAdminTrueHelper(COMMANDLINE_MODE_SERVICE_REGISTER));
+  EXPECT_TRUE(NeedsAdminTrueHelper(COMMANDLINE_MODE_SERVICE_UNREGISTER));
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_CRASH_HANDLER));
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_COMBROKER));
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_ONDEMAND));
+  {
+    ExpectAsserts expect_asserts;
+    EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_MEDIUM_SERVICE));
+  }
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_UNINSTALL));
+  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_PING));
+  EXPECT_FALSE(NeedsAdminTrueHelper(
+      static_cast<CommandLineMode>(kLastMode + 1)));
+}
+
+// Tests all modes plus an undefined one.
+TEST(GoopdateTest, CanDisplayUi_NotSilent) {
+  for (int mode = 0; mode <= kLastMode + 1; ++mode) {
+    const bool kExpected = mode == COMMANDLINE_MODE_UNKNOWN ||
+                           mode == COMMANDLINE_MODE_INSTALL ||
+                           mode == COMMANDLINE_MODE_HANDOFF_INSTALL ||
+                           mode == COMMANDLINE_MODE_UA;
+
+    EXPECT_EQ(kExpected,
+              internal::CanDisplayUi(static_cast<CommandLineMode>(mode),
+                                     false));
+  }
+}
+
+TEST(GoopdateTest, CanDisplayUi_Silent) {
+  int mode = 0;
+  // These two modes always return true.
+  for (; mode <= COMMANDLINE_MODE_UNKNOWN; ++mode) {
+    EXPECT_TRUE(internal::CanDisplayUi(static_cast<CommandLineMode>(mode),
+                                       true));
+  }
+
+  // Tests the remaining modes plus an undefined one.
+  for (; mode <= kLastMode + 1; ++mode) {
+    EXPECT_FALSE(internal::CanDisplayUi(static_cast<CommandLineMode>(mode),
+                                        true));
+  }
+}
+
 }  // namespace omaha
diff --git a/goopdate/goopdate_utils-internal.h b/goopdate/goopdate_utils-internal.h
deleted file mode 100644
index 049977c..0000000
--- a/goopdate/goopdate_utils-internal.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_GOOPDATE_GOOPDATE_UTILS_INTERNAL_H__
-#define OMAHA_GOOPDATE_GOOPDATE_UTILS_INTERNAL_H__
-
-namespace omaha {
-
-namespace goopdate_utils {
-
-namespace internal {
-
-// Installs a scheduled task. The task will run as either as SYSTEM or the
-// current user. The task will be triggered at each user logon, and/or
-// fixed intervals.
-HRESULT InstallScheduledTask(const TCHAR* task_name,
-                             const TCHAR* task_path,
-                             const TCHAR* task_parameters,
-                             const TCHAR* task_comment,
-                             bool is_machine,
-                             bool create_logon_trigger,
-                             bool create_daily_trigger,
-                             bool create_hourly_trigger);
-
-// Deletes a scheduled task.
-HRESULT UninstallScheduledTask(const TCHAR* task_name);
-
-// Deletes all scheduled tasks with the given prefix.
-HRESULT UninstallScheduledTasks(const TCHAR* task_prefix);
-
-// Runs a scheduled task immediately.
-HRESULT StartScheduledTask(const TCHAR* task_name);
-
-// Returns true if the scheduled task exists.
-bool IsInstalledScheduledTask(const TCHAR* task_name);
-
-// Returns a status code on success. List of status codes at
-// http://msdn2.microsoft.com/en-us/library/aa381263.aspx
-HRESULT GetScheduledTaskStatus(const TCHAR* task_name);
-
-// Stops the task if it is already running.
-HRESULT StopScheduledTask(const TCHAR* task_name);
-
-}  // namespace internal
-
-}  // namespace goopdate_utils
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_GOOPDATE_UTILS_INTERNAL_H__
-
diff --git a/goopdate/goopdate_utils.cc b/goopdate/goopdate_utils.cc
deleted file mode 100644
index bce3b05..0000000
--- a/goopdate/goopdate_utils.cc
+++ /dev/null
@@ -1,2944 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Defines: GoopdateUtils, helper functions for goopdate.
-
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/goopdate_utils-internal.h"
-
-#include <windows.h>
-#include <corerror.h>
-#include <lmcons.h>
-#include <atlpath.h>
-#include <atlsecurity.h>
-#include <map>
-#include <utility>
-#include <vector>
-#include "omaha/common/app_util.h"
-#include "omaha/common/browser_utils.h"
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/const_cmd_line.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/const_utils.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/path.h"
-#include "omaha/common/proc_utils.h"
-#include "omaha/common/process.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/scoped_impersonation.h"
-#include "omaha/common/scoped_ptr_cotask.h"
-#include "omaha/common/service_utils.h"
-#include "omaha/common/shell.h"
-#include "omaha/common/signatures.h"
-#include "omaha/common/string.h"
-#include "omaha/common/system.h"
-#include "omaha/common/system_info.h"
-#include "omaha/common/time.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/common/vista_utils.h"
-#include "omaha/common/window_utils.h"
-#include "omaha/goopdate/command_line_builder.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/event_logger.h"
-#include "omaha/goopdate/goopdate_xml_parser.h"
-#include "omaha/goopdate/resources/goopdateres/goopdate.grh"
-#include "omaha/goopdate/stats_uploader.h"
-#include "omaha/goopdate/update_response.h"
-#include "omaha/net/http_client.h"
-#include "omaha/net/network_config.h"
-#include "omaha/net/network_request.h"
-
-// Generated by MIDL in the "BUILD_MODE.OBJ_ROOT + SETTINGS.SUBDIR".
-#include "goopdate/google_update_idl.h"
-// Generated by MIDL in the "BUILD_MODE.OBJ_ROOT + SETTINGS.SUBDIR".
-#include "goopdate/google_update_idl_i.c"
-
-namespace omaha {
-
-namespace goopdate_utils {
-
-namespace {
-
-const int kApplicationGuidOffset = 38;
-const int kTerminateBrowserTimeoutMs = 60000;
-
-const TCHAR* const kTrue = _T("true");
-const TCHAR* const kFalse = _T("false");
-
-// Query element name-value pair.
-typedef std::pair<CString, CString> QueryElement;
-
-// Builds a query string from the provided name-value pairs.
-// The string begins with a '&' and ends with the last value.
-// query must be empty.
-HRESULT BuildQueryString(const std::vector<QueryElement>& elements,
-                         CString* query) {
-  ASSERT1(query);
-
-  if (elements.empty() || !query->IsEmpty()) {
-    return E_INVALIDARG;
-  }
-
-  for (size_t i = 0; i < elements.size(); ++i) {
-    CString escaped_str;
-    HRESULT hr = StringEscape(elements[i].second, false, &escaped_str);
-    if (FAILED(hr)) {
-      CORE_LOG(LEVEL_WARNING, (_T("[StringEscape failed][0x%08x]"), hr));
-      return hr;
-    }
-
-    CString element;
-    element.Format(_T("%s%=%s&"), elements[i].first, escaped_str);
-    query->Append(element);
-  }
-
-  ASSERT1(!query->IsEmpty());
-  if (_T('&') == query->GetAt(query->GetLength() - 1)) {
-    query->Truncate(query->GetLength() - 1);
-  }
-
-  return S_OK;
-}
-
-bool IsMachineProcessWithoutPrivileges(bool is_machine_process) {
-  return is_machine_process && !vista_util::IsUserAdmin();
-}
-
-HRESULT LaunchImpersonatedCmdLine(const CString& cmd_line) {
-  CORE_LOG(L3, (_T("[LaunchImpersonatedCmdLine][%s]"), cmd_line));
-
-  scoped_handle impersonation_token;
-  HRESULT hr = vista::GetLoggedOnUserToken(address(impersonation_token));
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[GetLoggedOnUserToken failed][0x%x]"), hr));
-    return hr;
-  }
-
-  scoped_impersonation impersonate_user(get(impersonation_token));
-  hr = HRESULT_FROM_WIN32(impersonate_user.result());
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[impersonation failed][0x%x]"), hr));
-    return hr;
-  }
-
-  CComPtr<IProcessLauncher> launcher;
-  hr = launcher.CoCreateInstance(CLSID_ProcessLauncherClass,
-                                 NULL,
-                                 CLSCTX_LOCAL_SERVER);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[CoCreateInstance IProcessLauncher failed][0x%x]"), hr));
-    return hr;
-  }
-
-  hr = launcher->LaunchCmdLine(cmd_line);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[IProcessLauncher.LaunchBrowser failed][0x%x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT LaunchImpersonatedBrowser(BrowserType type, const CString& url) {
-  CORE_LOG(L3, (_T("[LaunchImpersonatedBrowser][%u][%s]"), type, url));
-
-  scoped_handle impersonation_token;
-  HRESULT hr = vista::GetLoggedOnUserToken(address(impersonation_token));
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[GetLoggedOnUserToken failed][0x%x]"), hr));
-    return hr;
-  }
-
-  scoped_impersonation impersonate_user(get(impersonation_token));
-  hr = HRESULT_FROM_WIN32(impersonate_user.result());
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[impersonation failed][0x%x]"), hr));
-    return hr;
-  }
-
-  CComPtr<IProcessLauncher> launcher;
-  hr = launcher.CoCreateInstance(CLSID_ProcessLauncherClass,
-                                 NULL,
-                                 CLSCTX_LOCAL_SERVER);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[CoCreateInstance IProcessLauncher failed][0x%x]"), hr));
-    return hr;
-  }
-
-  hr = launcher->LaunchBrowser(type, url);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[IProcessLauncher.LaunchBrowser failed][0x%x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-}  // namespace
-
-namespace internal {
-
-bool IsInstalledScheduledTask(const TCHAR* task_name) {
-  ASSERT1(task_name && *task_name);
-
-  CComPtr<ITaskScheduler> scheduler;
-  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
-                                          NULL,
-                                          CLSCTX_INPROC_SERVER);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITaskScheduler.CoCreateInstance failed 0x%x"), hr));
-    return false;
-  }
-
-  CComPtr<ITask> task;
-  hr = scheduler->Activate(task_name,
-                           __uuidof(ITask),
-                           reinterpret_cast<IUnknown**>(&task));
-
-  CORE_LOG(L3, (_T("[IsInstalledScheduledTask returned][0x%x]"), hr));
-  return COR_E_FILENOTFOUND == hr ? false : true;
-}
-
-HRESULT GetScheduledTaskStatus(const TCHAR* task_name) {
-  ASSERT1(task_name && *task_name);
-
-  CComPtr<ITaskScheduler> scheduler;
-  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
-                                          NULL,
-                                          CLSCTX_INPROC_SERVER);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITaskScheduler.CoCreateInstance failed 0x%x"), hr));
-    return hr;
-  }
-
-  CComPtr<ITask> task;
-  hr = scheduler->Activate(task_name,
-                           __uuidof(ITask),
-                           reinterpret_cast<IUnknown**>(&task));
-
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[GetScheduledTaskStatus: Activate failed][0x%x]"), hr));
-    return hr;
-  }
-
-  HRESULT task_status(S_OK);
-  hr = task->GetStatus(&task_status);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITask.GetStatus failed 0x%x"), hr));
-    return hr;
-  }
-
-  return task_status;
-}
-
-HRESULT GetScheduledTaskExitCode(const TCHAR* task_name) {
-  ASSERT1(task_name && *task_name);
-
-  CComPtr<ITaskScheduler> scheduler;
-  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
-                                          NULL,
-                                          CLSCTX_INPROC_SERVER);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITaskScheduler.CoCreateInstance failed 0x%x"), hr));
-    return hr;
-  }
-
-  CComPtr<ITask> task;
-  hr = scheduler->Activate(task_name,
-                           __uuidof(ITask),
-                           reinterpret_cast<IUnknown**>(&task));
-
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[ITask.Activate failed][0x%x]"), hr));
-    return hr;
-  }
-
-  DWORD exit_code(0);
-  hr = task->GetExitCode(&exit_code);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("ITask.GetExitCode failed 0x%x"), hr));
-    return hr;
-  }
-
-  return hr == SCHED_S_TASK_HAS_NOT_RUN ? hr : exit_code;
-}
-
-HRESULT StartScheduledTask(const TCHAR* task_name) {
-  ASSERT1(task_name && *task_name);
-
-  if (GetScheduledTaskStatus(task_name) == SCHED_S_TASK_RUNNING) {
-    return S_OK;
-  }
-
-  CComPtr<ITaskScheduler> scheduler;
-  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
-                                          NULL,
-                                          CLSCTX_INPROC_SERVER);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITaskScheduler.CoCreateInstance failed 0x%x"), hr));
-    return hr;
-  }
-
-  CComPtr<ITask> task;
-  hr = scheduler->Activate(task_name,
-                           __uuidof(ITask),
-                           reinterpret_cast<IUnknown**>(&task));
-
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITaskScheduler.Activate failed 0x%x"), hr));
-    return hr;
-  }
-
-  hr = task->Run();
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITask.Run failed 0x%x"), hr));
-    return hr;
-  }
-
-  return hr;
-}
-
-HRESULT StopScheduledTask(const TCHAR* task_name) {
-  ASSERT1(task_name && *task_name);
-
-  if (GetScheduledTaskStatus(task_name) != SCHED_S_TASK_RUNNING) {
-    return S_OK;
-  }
-
-  CComPtr<ITaskScheduler> scheduler;
-  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
-                                          NULL,
-                                          CLSCTX_INPROC_SERVER);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITaskScheduler.CoCreateInstance failed 0x%x"), hr));
-    return hr;
-  }
-
-  CComPtr<ITask> task;
-  hr = scheduler->Activate(task_name,
-                           __uuidof(ITask),
-                           reinterpret_cast<IUnknown**>(&task));
-
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITaskScheduler.Activate failed 0x%x"), hr));
-    return hr;
-  }
-
-  hr = task->Terminate();
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITask.Run failed 0x%x"), hr));
-    return hr;
-  }
-
-  return hr;
-}
-
-HRESULT CreateLogonTrigger(ITask* task) {
-  ASSERT1(task);
-
-  CComPtr<ITaskTrigger> trigger;
-  WORD index = 0;
-
-  // Create a trigger to run on every user logon.
-  HRESULT hr = task->CreateTrigger(&index, &trigger);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITask.CreateTrigger failed 0x%x"), hr));
-    return hr;
-  }
-
-  TASK_TRIGGER trigger_config = {0};
-  trigger_config.cbTriggerSize = sizeof(trigger_config);
-  // These are required parameters. A past start date is good.
-  trigger_config.wBeginDay = 1;
-  trigger_config.wBeginMonth = 1;
-  trigger_config.wBeginYear = 1999;
-
-  // Run on every user logon.
-  trigger_config.TriggerType = TASK_EVENT_TRIGGER_AT_LOGON;
-
-  hr = trigger->SetTrigger(&trigger_config);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITaskTrigger.SetTrigger failed 0x%x"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT CreatePeriodicTrigger(ITask* task, bool create_hourly_trigger) {
-  ASSERT1(task);
-
-  CComPtr<ITaskTrigger> trigger;
-  WORD index = 0;
-
-  // Create a trigger to run every day.
-  HRESULT hr = task->CreateTrigger(&index, &trigger);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITask.CreateTrigger failed 0x%x"), hr));
-    return hr;
-  }
-
-  // Start time set to 5 minutes from the current time.
-  time64 start_time = GetCurrent100NSTime() + (5 * kMinsTo100ns);
-  SYSTEMTIME sys_time = Time64ToSystemTime(start_time);
-  SYSTEMTIME locale_time = {0};
-  hr = SystemTimeToTzSpecificLocalTime(NULL, &sys_time, &locale_time);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("SystemTimeToTzSpecificLocalTime failed 0x%x"), hr));
-    return hr;
-  }
-
-  TASK_TRIGGER trigger_config = {0};
-  trigger_config.cbTriggerSize = sizeof(trigger_config);
-  trigger_config.wBeginYear = locale_time.wYear;
-  trigger_config.wBeginMonth = locale_time.wMonth;
-  trigger_config.wBeginDay = locale_time.wDay;
-  trigger_config.wStartHour = locale_time.wHour;
-  trigger_config.wStartMinute = locale_time.wMinute;
-
-  trigger_config.TriggerType = TASK_TIME_TRIGGER_DAILY;
-  trigger_config.Type.Daily.DaysInterval = 1;  // every 1 day
-
-  if (create_hourly_trigger) {
-    // The task will be run daily at 24 hour intervals. And the task will be
-    // repeated every au_timer_interval_minutes within a single 24 hour
-    // interval.
-    const DWORD kTaskTrigger24HoursDuration = 24 * 60;
-    int au_timer_interval_minutes =
-        ConfigManager::Instance()->GetAutoUpdateTimerIntervalMs() / (60 * 1000);
-    ASSERT1(au_timer_interval_minutes > 0 &&
-            au_timer_interval_minutes < kTaskTrigger24HoursDuration);
-
-    trigger_config.MinutesDuration = kTaskTrigger24HoursDuration;
-    trigger_config.MinutesInterval = au_timer_interval_minutes;
-  }
-
-  hr = trigger->SetTrigger(&trigger_config);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITaskTrigger.SetTrigger failed 0x%x"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT CreateScheduledTask(ITask* task,
-                            const TCHAR* task_path,
-                            const TCHAR* task_parameters,
-                            const TCHAR* task_comment,
-                            bool is_machine,
-                            bool create_logon_trigger,
-                            bool create_daily_trigger,
-                            bool create_hourly_trigger) {
-  ASSERT1(task);
-  ASSERT1(task_path && *task_path);
-  ASSERT1(task_parameters);
-  ASSERT1(task_comment && *task_comment);
-  ASSERT1(create_logon_trigger || create_daily_trigger);
-  ASSERT1(!create_logon_trigger || (create_logon_trigger && is_machine));
-  ASSERT1(!create_hourly_trigger ||
-          (create_hourly_trigger && create_daily_trigger));
-
-  CORE_LOG(L3, (_T("CreateScheduledTask[%s][%s][%d]"),
-                task_path, task_parameters, is_machine));
-
-  HRESULT hr = task->SetApplicationName(task_path);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITask.SetApplicationName failed 0x%x"), hr));
-    return hr;
-  }
-
-  hr = task->SetParameters(task_parameters);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITask.SetParameters failed 0x%x"), hr));
-    return hr;
-  }
-
-  hr = task->SetComment(task_comment);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITask.SetComment failed 0x%x"), hr));
-    return hr;
-  }
-
-  if (is_machine) {
-    // Run using SYSTEM credentials, by passing in an empty username string.
-    hr = task->SetAccountInformation(_T(""), NULL);
-  } else {
-    // Run as current user.
-    // For the user task, we set TASK_FLAG_RUN_ONLY_IF_LOGGED_ON, so that we do
-    // not need the user password for task creation.
-    hr = task->SetFlags(TASK_FLAG_RUN_ONLY_IF_LOGGED_ON);
-    if (FAILED(hr)) {
-      ASSERT(false, (_T("ITask.SetFlags failed 0x%x"), hr));
-      return hr;
-    }
-
-    CString user_name;
-    DWORD buffer_size = UNLEN + 1;
-    if (!::GetUserName(CStrBuf(user_name, buffer_size), &buffer_size)) {
-      hr = HRESULTFromLastError();
-      ASSERT(false, (_T("::GetUserName failed 0x%x"), hr));
-      return hr;
-    }
-    hr = task->SetAccountInformation(user_name, NULL);
-  }
-
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITask.SetAccountInformation failed 0x%x"), hr));
-    return hr;
-  }
-
-  // The default is to run for a finite number of days. We want to run
-  // indefinitely.
-  // Due to a bug introduced in Vista, and propogated to Windows 7, setting the
-  // MaxRunTime to INFINITE results in the task only running for 72 hours. For
-  // these operating systems, setting the RunTime to "INFINITE - 1" gets the
-  // desired behavior of allowing an "infinite" run of the task.
-  DWORD max_time = INFINITE - (SystemInfo::IsRunningOnVistaOrLater() ? 1 : 0);
-  hr = task->SetMaxRunTime(max_time);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITask.SetMaxRunTime failed 0x%x"), hr));
-    return hr;
-  }
-
-  CComPtr<ITaskTrigger> trigger;
-  WORD index = 0;
-
-  if (create_logon_trigger && is_machine) {
-    // Create a trigger to run on every user logon. Non-admin users are not able
-    // to create logon triggers, so we create only for machine.
-    hr = CreateLogonTrigger(task);
-    if (FAILED(hr)) {
-      return hr;
-    }
-  }
-
-  if (create_daily_trigger) {
-    hr = CreatePeriodicTrigger(task, create_hourly_trigger);
-    if (FAILED(hr)) {
-      return hr;
-    }
-  }
-
-  // Save task.
-  CComQIPtr<IPersistFile> persist(task);
-  if (!persist) {
-    hr = E_NOINTERFACE;
-    ASSERT(false, (_T("ITask.QueryInterface IPersistFile failed 0x%x"), hr));
-    return hr;
-  }
-
-  hr = persist->Save(NULL, TRUE);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("IPersistFile.Save failed 0x%x"), hr));
-    return hr;
-  }
-
-  if (is_machine) {
-    return S_OK;
-  }
-
-  // Adjust privileges to explicitly allow the current user to be able to
-  // manipulate this task. User applications, and consequently, Omaha, can be
-  // installed in an elevated mode. This can happen, for instance, if the user
-  // installs on XP, then upgrades to Vista. Or chooses "Run as Administrator"
-  // when running the meta-installer on Vista. Subsequently, Omaha running at
-  // medium integrity needs to be able to manipulate the installed task.
-  scoped_ptr_cotask<OLECHAR> job_file;
-  hr = persist->GetCurFile(address(job_file));
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("IPersistFile.GetCurFile failed 0x%x"), hr));
-    return hr;
-  }
-
-  persist.Release();
-
-  CAccessToken token;
-  CSid current_sid;
-  if (!token.GetEffectiveToken(TOKEN_QUERY) || !token.GetUser(&current_sid)) {
-    hr = HRESULTFromLastError();
-    ASSERT(false, (_T("[Failed to get current user sid[0x%x]"), hr));
-    return hr;
-  }
-
-  hr = AddAllowedAce(job_file.get(),
-                     SE_FILE_OBJECT,
-                     current_sid,
-                     FILE_ALL_ACCESS,
-                     0);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("Could not adjust DACL[%s][0x%x]"), job_file.get(), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT UpgradeScheduledTask(const TCHAR* task_name,
-                             const TCHAR* task_path,
-                             const TCHAR* task_parameters,
-                             const TCHAR* task_comment,
-                             bool is_machine,
-                             bool create_logon_trigger,
-                             bool create_daily_trigger,
-                             bool create_hourly_trigger) {
-  ASSERT1(task_name && *task_name);
-  ASSERT1(IsInstalledScheduledTask(task_name));
-
-  CORE_LOG(L3, (_T("UpgradeScheduledTask[%s][%s][%s][%d]"),
-                task_name, task_path, task_parameters, is_machine));
-
-  // TODO(Omaha): Perhaps pass the ITaskScheduler around where possible.
-  CComPtr<ITaskScheduler> scheduler;
-  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
-                                          NULL,
-                                          CLSCTX_INPROC_SERVER);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITaskScheduler.CoCreateInstance failed 0x%x"), hr));
-    return hr;
-  }
-
-  CComPtr<ITask> task;
-  hr = scheduler->Activate(task_name,
-                           __uuidof(ITask),
-                           reinterpret_cast<IUnknown**>(&task));
-
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("UpgradeScheduledTask: Activate failed[0x%x]"), hr));
-    return hr;
-  }
-
-  // Delete existing triggers. CreateScheduledTask() will recreate them anew.
-  WORD trigger_count(0);
-  hr = task->GetTriggerCount(&trigger_count);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITaskScheduler.GetTriggerCount failed 0x%x"), hr));
-    return hr;
-  }
-
-  for (int i = 0; i < trigger_count; ++i) {
-    hr = task->DeleteTrigger(0);
-    if (FAILED(hr)) {
-      ASSERT(false, (_T("ITaskScheduler.DeleteTrigger failed 0x%x"), hr));
-      return hr;
-    }
-  }
-
-  return CreateScheduledTask(task,
-                             task_path,
-                             task_parameters,
-                             task_comment,
-                             is_machine,
-                             create_logon_trigger,
-                             create_daily_trigger,
-                             create_hourly_trigger);
-}
-
-// TODO(Omaha): Change the apis to avoid specifying hourly and daily triggers.
-HRESULT InstallScheduledTask(const TCHAR* task_name,
-                             const TCHAR* task_path,
-                             const TCHAR* task_parameters,
-                             const TCHAR* task_comment,
-                             bool is_machine,
-                             bool create_logon_trigger,
-                             bool create_daily_trigger,
-                             bool create_hourly_trigger) {
-  if (IsInstalledScheduledTask(task_name)) {
-    return UpgradeScheduledTask(task_name,
-                                task_path,
-                                task_parameters,
-                                task_comment,
-                                is_machine,
-                                create_logon_trigger,
-                                create_daily_trigger,
-                                create_hourly_trigger);
-  }
-
-  CComPtr<ITaskScheduler> scheduler;
-  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
-                                          NULL,
-                                          CLSCTX_INPROC_SERVER);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITaskScheduler.CoCreateInstance failed 0x%x"), hr));
-    return hr;
-  }
-
-  CComPtr<ITask> task;
-  hr = scheduler->NewWorkItem(task_name,
-                              CLSID_CTask,
-                              __uuidof(ITask),
-                              reinterpret_cast<IUnknown**>(&task));
-
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITaskScheduler.NewWorkItem failed 0x%x"), hr));
-    return hr;
-  }
-
-  return CreateScheduledTask(task,
-                             task_path,
-                             task_parameters,
-                             task_comment,
-                             is_machine,
-                             create_logon_trigger,
-                             create_daily_trigger,
-                             create_hourly_trigger);
-}
-
-HRESULT UninstallScheduledTask(const TCHAR* task_name) {
-  ASSERT1(task_name && *task_name);
-
-  CComPtr<ITaskScheduler> scheduler;
-  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
-                                          NULL,
-                                          CLSCTX_INPROC_SERVER);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITaskScheduler.CoCreateInstance failed 0x%x"), hr));
-    return hr;
-  }
-
-  // Stop the task before deleting it. Ignore return value.
-  VERIFY1(SUCCEEDED(StopScheduledTask(task_name)));
-
-  // delete the task.
-  hr = scheduler->Delete(task_name);
-  if (FAILED(hr) && COR_E_FILENOTFOUND != hr) {
-    CORE_LOG(LE, (_T("GetScheduledTaskStatus: Delete failed[0x%x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT UninstallScheduledTasks(const TCHAR* task_prefix) {
-  ASSERT1(task_prefix && *task_prefix);
-
-  CComPtr<ITaskScheduler> scheduler;
-  HRESULT hr = scheduler.CoCreateInstance(CLSID_CTaskScheduler,
-                                          NULL,
-                                          CLSCTX_INPROC_SERVER);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITaskScheduler.CoCreateInstance failed 0x%x"), hr));
-    return hr;
-  }
-
-  CComPtr<IEnumWorkItems> enum_items;
-  hr = scheduler->Enum(&enum_items);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("ITaskScheduler.Enum failed 0x%x"), hr));
-    return hr;
-  }
-
-  TCHAR** task_names = NULL;
-  DWORD task_count = 0;
-  while (enum_items->Next(1, &task_names, &task_count) == S_OK) {
-    ASSERT1(task_count == 1);
-    scoped_co_task_ptr task_names_guard(task_names);
-    scoped_co_task_ptr task_name_guard(task_names[0]);
-
-    if (String_StartsWith(task_names[0], task_prefix, true)) {
-      UninstallScheduledTask(task_names[0]);
-    }
-  }
-
-  return S_OK;
-}
-
-// Returns the task name Omaha used to install in Omaha 1.2.x.
-CString GetOmaha1LegacyTaskName(bool is_machine) {
-  const TCHAR* const kLegacyOmaha1TaskNameMachine = _T("GoogleUpdateTask");
-  const TCHAR* const kLegacyOmaha1TaskNameUser = _T("GoogleUpdateTaskUser");
-  return is_machine ? kLegacyOmaha1TaskNameMachine : kLegacyOmaha1TaskNameUser;
-}
-
-// Returns the task name Omaha used to install in Omaha 2 before the
-// "GoogleUpdate.exe does not run all the time" refactoring.
-CString GetOmaha2LegacyTaskName(bool is_machine) {
-  const TCHAR* kLegacyOmaha2TaskNameUserPrefix = _T("GoogleUpdateTaskUser");
-  const TCHAR* kLegacyOmaha2TaskNameMachine = _T("GoogleUpdateTaskMachine");
-  if (is_machine) {
-    return kLegacyOmaha2TaskNameMachine;
-  }
-
-  CString task_name_user = kLegacyOmaha2TaskNameUserPrefix;
-  CString user_sid;
-  VERIFY1(SUCCEEDED(user_info::GetCurrentUser(NULL, NULL, &user_sid)));
-  task_name_user += user_sid;
-  return task_name_user;
-}
-
-}  // namespace internal
-
-CString GetAppClientsKey(bool is_machine, const CString& app_guid) {
-  return AppendRegKeyPath(
-      ConfigManager::Instance()->registry_clients(is_machine),
-      app_guid);
-}
-
-CString GetAppClientStateKey(bool is_machine, const CString& app_guid) {
-  return AppendRegKeyPath(
-      ConfigManager::Instance()->registry_client_state(is_machine),
-      app_guid);
-}
-
-CString GetAppClientStateMediumKey(bool is_machine, const CString& app_guid) {
-  ASSERT1(is_machine);
-  UNREFERENCED_PARAMETER(is_machine);
-  return AppendRegKeyPath(
-      ConfigManager::Instance()->machine_registry_client_state_medium(),
-      app_guid);
-}
-
-// Returns the application registration location given the user SID.
-CString GetUserAllAppsStatePath(const CString& user_sid) {
-  return AppendRegKeyPath(USERS_KEY, user_sid,
-                          GOOPDATE_REG_RELATIVE_CLIENT_STATE);
-}
-
-// Returns the application state path for a user given the user SID.
-CString GetUserAllAppsRegPath(const CString& user_sid) {
-  return AppendRegKeyPath(USERS_KEY, user_sid, GOOPDATE_REG_RELATIVE_CLIENTS);
-}
-
-// Returns the application state path for a particular user and for the
-// given application id.
-CString GetUserAppStatePath(const CString& user_sid,
-                            const CString& app_guid) {
-  return AppendRegKeyPath(GetUserAllAppsStatePath(user_sid), app_guid);
-}
-
-// Returns the application registrration path for a particular user
-// and for the given application id.
-CString GetUserAppRegPath(const CString& user_sid,
-                          const CString& app_guid) {
-  return AppendRegKeyPath(GetUserAllAppsRegPath(user_sid), app_guid);
-}
-
-CString BuildGoogleUpdateExeDir(bool is_machine) {
-  ConfigManager& cm = *ConfigManager::Instance();
-  return is_machine ? cm.GetMachineGoopdateInstallDir() :
-                      cm.GetUserGoopdateInstallDir();
-}
-
-CString BuildGoogleUpdateExePath(bool is_machine) {
-  CORE_LOG(L3, (_T("[BuildGoogleUpdateExePath][%d]"), is_machine));
-
-  CPath full_file_path(BuildGoogleUpdateExeDir(is_machine));
-  VERIFY1(full_file_path.Append(kGoopdateFileName));
-
-  return full_file_path;
-}
-
-CString BuildGoogleUpdateServicesPath(bool is_machine) {
-  CORE_LOG(L3, (_T("[BuildGoogleUpdateServicesPath][%d]"), is_machine));
-
-  CPath full_file_path(
-      goopdate_utils::BuildInstallDirectory(is_machine, GetVersionString()));
-  VERIFY1(full_file_path.Append(kGoopdateCrashHandlerFileName));
-
-  return full_file_path;
-}
-
-HRESULT StartElevatedSelfWithArgsAndWait(const TCHAR* args) {
-  ASSERT1(args);
-  CORE_LOG(L3, (_T("[StartElevatedSelfWithArgsAndWait]")));
-
-  // Get the process executable.
-  TCHAR filename[MAX_PATH] = {0};
-  if (::GetModuleFileName(NULL, filename, MAX_PATH) == 0) {
-    HRESULT hr = HRESULTFromLastError();
-    CORE_LOG(LEVEL_ERROR, (_T("[GetModuleFileName failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  // Launch self elevated and wait.
-  DWORD exit_code = 0;
-  CORE_LOG(L1,
-      (_T("[RunElevated filename='%s'][arguments='%s']"), filename, args));
-  // According to the MSDN documentation for ::ShowWindow: "nCmdShow. This
-  // parameter is ignored the first time an application calls ShowWindow, if
-  // the program that launched the application provides a STARTUPINFO
-  // structure.". We want to force showing the UI window. So we pass in
-  // SW_SHOWNORMAL.
-  HRESULT hr = vista_util::RunElevated(filename,
-                                       args,
-                                       SW_SHOWNORMAL,
-                                       &exit_code);
-  CORE_LOG(L2, (_T("[elevated instance exit code][%u]"), exit_code));
-  if (FAILED(hr)) {
-    CORE_LOG(LEVEL_ERROR, (_T("[RunElevated failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT StartGoogleUpdateWithArgs(bool is_machine,
-                                  const TCHAR* args,
-                                  HANDLE* process) {
-  CORE_LOG(L3, (_T("[StartGoogleUpdateWithArgs][%d][%s]"),
-                is_machine, args ? args : _T("")));
-
-  CString exe_path = BuildGoogleUpdateExePath(is_machine);
-
-  CORE_LOG(L3, (_T("[command line][%s][%s]"), exe_path, args ? args : _T("")));
-
-  HRESULT hr = System::ShellExecuteProcess(exe_path, args, NULL, process);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[can't start process][%s][0x%08x]"), exe_path, hr));
-    return hr;
-  }
-  return S_OK;
-}
-
-bool IsRunningFromOfficialGoopdateDir(bool is_machine) {
-  const ConfigManager& cm = *ConfigManager::Instance();
-  bool is_official_dir = is_machine ?
-                         cm.IsRunningFromMachineGoopdateInstallDir() :
-                         cm.IsRunningFromUserGoopdateInstallDir();
-  CORE_LOG(L3, (_T("[running from official dir][%d]"), is_official_dir));
-  return is_official_dir;
-}
-
-CString GetHKRoot() {
-  return IsRunningFromOfficialGoopdateDir(true) ? _T("HKLM") : _T("HKCU");
-}
-
-HRESULT InitializeSecurity() {
-  // Creates a security descriptor in absolute format and includes the owner
-  // and the primary group.  We grant access to admins and system.
-  CSecurityDesc security_descriptor;
-  if (SystemInfo::IsRunningOnVistaOrLater()) {
-    // To allow for low-integrity IE to call into IGoogleUpdate.
-    security_descriptor.FromString(LOW_INTEGRITY_SDDL_SACL);
-  }
-  security_descriptor.SetOwner(Sids::Admins());
-  security_descriptor.SetGroup(Sids::Admins());
-  CDacl dacl;
-  dacl.AddAllowedAce(Sids::System(), COM_RIGHTS_EXECUTE);
-  dacl.AddAllowedAce(Sids::Admins(), COM_RIGHTS_EXECUTE);
-  dacl.AddAllowedAce(Sids::AuthenticatedUser(), COM_RIGHTS_EXECUTE);
-
-  security_descriptor.SetDacl(dacl);
-  security_descriptor.MakeAbsolute();
-
-  SECURITY_DESCRIPTOR* sd = const_cast<SECURITY_DESCRIPTOR*>(
-      security_descriptor.GetPSECURITY_DESCRIPTOR());
-
-  return ::CoInitializeSecurity(
-      sd,
-      -1,
-      NULL,   // Let COM choose what authentication services to register.
-      NULL,
-      RPC_C_AUTHN_LEVEL_PKT_PRIVACY,  // Data integrity and encryption.
-      RPC_C_IMP_LEVEL_IDENTIFY,       // Only allow a server to identify.
-      NULL,
-      EOAC_DYNAMIC_CLOAKING | EOAC_DISABLE_AAA | EOAC_NO_CUSTOM_MARSHAL,
-      NULL);
-}
-
-// This is only used for legacy handoff support.
-CString GetProductName(const CString& app_guid) {
-  const TCHAR* product_name = NULL;
-  const TCHAR gears_guid[]   = _T("{283EAF47-8817-4c2b-A801-AD1FADFB7BAA}");
-  const TCHAR google_talk_plugin[]  =
-      _T("{D0AB2EBC-931B-4013-9FEB-C9C4C2225C8C}");
-  const TCHAR youtube_uploader_guid[] =
-      _T("{A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}");
-
-  if (app_guid.CompareNoCase(gears_guid) == 0) {
-    product_name = _T("Gears");
-  } else if (app_guid.CompareNoCase(google_talk_plugin) == 0) {
-      product_name = _T("Google Talk Plugin");
-  } else if (app_guid.CompareNoCase(youtube_uploader_guid) == 0) {
-      product_name = _T("YouTube Uploader");
-  } else {
-      product_name = _T("Google App");
-  }
-  return product_name;
-}
-
-HRESULT ReadPersistentId(const CString& key_name,
-                         const CString& value_name,
-                         CString* id) {
-  ASSERT1(id);
-  CString key_path = AppendRegKeyPath(key_name, GOOPDATE_MAIN_KEY);
-  return RegKey::GetValue(key_path, value_name, id);
-}
-
-HRESULT BuildHttpGetString(const CString& url,
-                           DWORD error_code,
-                           DWORD extra_code1,
-                           DWORD extra_code2,
-                           const CString& app_guid,
-                           const CString& goopdate_version,
-                           bool is_machine,
-                           const CString& language,
-                           const GUID& iid,
-                           const CString& brand_code,
-                           const CString& source_id,
-                           CString* get_request) {
-  ASSERT1(get_request);
-  if (url.IsEmpty()) {
-    return E_INVALIDARG;
-  }
-  ASSERT1(_T('?') == url.GetAt(url.GetLength() - 1) ||
-          _T('&') == url.GetAt(url.GetLength() - 1));
-
-  CString errorcode_str;
-  CString extracode1_str;
-  CString extracode2_str;
-  errorcode_str.Format(_T("0x%08x"), error_code);
-  extracode1_str.Format(_T("0x%08x"), extra_code1);
-  extracode2_str.Format(_T("%u"), extra_code2);
-
-  CString os_version;
-  CString service_pack;
-  HRESULT hr = GetOSInfo(&os_version, &service_pack);
-  if (FAILED(hr)) {
-    CORE_LOG(LEVEL_WARNING, (_T("[GetOSInfo failed][0x%08x]"), hr));
-  }
-  const CString iid_string = ::IsEqualGUID(GUID_NULL, iid) ? _T("") :
-                                                             GuidToString(iid);
-
-  std::vector<QueryElement> elements;
-  elements.push_back(QueryElement(_T("hl"), language));
-  elements.push_back(QueryElement(_T("errorcode"), errorcode_str));
-  elements.push_back(QueryElement(_T("extracode1"), extracode1_str));
-  elements.push_back(QueryElement(_T("extracode2"), extracode2_str));
-  elements.push_back(QueryElement(_T("app"), app_guid));
-  elements.push_back(QueryElement(_T("guver"), goopdate_version));
-  elements.push_back(QueryElement(_T("ismachine"),
-                                  is_machine ? _T("1") : _T("0")));
-  elements.push_back(QueryElement(_T("os"), os_version));
-  elements.push_back(QueryElement(_T("sp"), service_pack));
-  elements.push_back(QueryElement(_T("iid"), iid_string));
-  elements.push_back(QueryElement(_T("brand"), brand_code));
-  elements.push_back(QueryElement(_T("source"), source_id));
-
-  CString test_source = ConfigManager::Instance()->GetTestSource();
-  if (!test_source.IsEmpty()) {
-    elements.push_back(QueryElement(_T("testsource"), test_source));
-  }
-
-  CString query;
-  hr = BuildQueryString(elements, &query);
-  if (FAILED(hr)) {
-    CORE_LOG(LEVEL_WARNING, (_T("[BuildQueryString failed][0x%08x]"), hr));
-    return hr;
-  }
-  get_request->Format(_T("%s%s"), url, query);
-
-  // The length should be smaller than the maximum allowed get length.
-  ASSERT1(get_request->GetLength() <= INTERNET_MAX_URL_LENGTH);
-  if (get_request->GetLength() > INTERNET_MAX_URL_LENGTH) {
-    return E_FAIL;
-  }
-
-  return S_OK;
-}
-
-HRESULT RedirectHKCR(bool is_machine) {
-  RegKey classes_key;
-  HRESULT hr = classes_key.Open(is_machine ?
-                                HKEY_LOCAL_MACHINE :
-                                HKEY_CURRENT_USER,
-                                _T("Software\\Classes"),
-                                KEY_ALL_ACCESS);
-  if (FAILED(hr)) {
-    ASSERT(FALSE, (_T("RedirectHKCR - key.Open(%d) fail %d"), is_machine, hr));
-    return hr;
-  }
-
-  LONG result = ::RegOverridePredefKey(HKEY_CLASSES_ROOT, classes_key.Key());
-  if (result != ERROR_SUCCESS) {
-    ASSERT(false, (_T("RedirectHKCR - RegOverridePredefKey fail %d"), result));
-    return HRESULT_FROM_WIN32(result);
-  }
-
-  return S_OK;
-}
-
-HRESULT RemoveRedirectHKCR() {
-  LONG result = ::RegOverridePredefKey(HKEY_CLASSES_ROOT, NULL);
-  if (result != ERROR_SUCCESS) {
-    ASSERT(FALSE, (_T("RemoveRedirectHKCR - RegOverridePredefKey %d"), result));
-    return HRESULT_FROM_WIN32(result);
-  }
-
-  return S_OK;
-}
-
-HRESULT RegisterTypeLib(bool is_admin,
-                        const CComBSTR& path,
-                        ITypeLib* type_lib) {
-  // Typelib registration.
-  CORE_LOG(L3, (_T("[Registering TypeLib]")));
-  HRESULT hr = S_OK;
-  if (!is_admin &&
-      SUCCEEDED(goopdate_utils::RegisterTypeLibForUser(type_lib, path, NULL))) {
-    return S_OK;
-  }
-
-  // For Admin cases, we use ::RegisterTypeLib().
-  // For platforms where ::RegisterTypeLibForUser is not available, we register
-  // with ::RegisterTypeLib, and rely on HKCR=>HKCU redirection.
-  hr = ::RegisterTypeLib(type_lib, path, NULL);
-  ASSERT(SUCCEEDED(hr), (_T("[TypeLib registration failed][0x%08x]"), hr));
-  return hr;
-}
-
-HRESULT UnRegisterTypeLib(bool is_admin, const CComBSTR&, ITypeLib* type_lib) {
-  // Typelib unregistration.
-  CORE_LOG(L3, (_T("[Unregistering Typelib]")));
-  TLIBATTR* tlib_attr = NULL;
-  HRESULT hr = type_lib->GetLibAttr(&tlib_attr);
-  ASSERT(SUCCEEDED(hr), (_T("[GetLibAttr failed][0x%08x]"), hr));
-  if (FAILED(hr)) {
-    return hr;
-  }
-  ON_SCOPE_EXIT_OBJ(*type_lib, &ITypeLib::ReleaseTLibAttr, tlib_attr);
-
-  if (!is_admin &&
-      SUCCEEDED(goopdate_utils::UnRegisterTypeLibForUser(
-          tlib_attr->guid,
-          tlib_attr->wMajorVerNum,
-          tlib_attr->wMinorVerNum,
-          tlib_attr->lcid,
-          tlib_attr->syskind))) {
-    return S_OK;
-  }
-
-  // For Admin cases, we use ::UnRegisterTypeLib().
-  // For platforms where ::UnRegisterTypeLibForUser is not available, we
-  // unregister with ::UnRegisterTypeLib, and rely on HKCR=>HKCU redirection.
-  hr = ::UnRegisterTypeLib(tlib_attr->guid,
-                           tlib_attr->wMajorVerNum,
-                           tlib_attr->wMinorVerNum,
-                           tlib_attr->lcid,
-                           tlib_attr->syskind);
-
-  // We assert before the check for TYPE_E_REGISTRYACCESS below because we want
-  // to catch the case where we're trying to unregister more than once because
-  // that would be a bug.
-  ASSERT(SUCCEEDED(hr),
-         (_T("[UnRegisterTypeLib failed.  ")
-          _T("This is likely a multiple unregister bug.][0x%08x]"), hr));
-
-  // If you try to unregister a type library that's already unregistered,
-  // it will return with this failure, which is OK.
-  if (hr == TYPE_E_REGISTRYACCESS) {
-    hr = S_OK;
-  }
-
-  return hr;
-}
-
-HRESULT RegisterOrUnregisterModule(bool register_server,
-                                   RegisterOrUnregisterFunction registrar) {
-  ASSERT1(registrar);
-
-  bool is_machine = IsRunningFromOfficialGoopdateDir(true);
-  // ATL by default registers the control to HKCR and we want to register
-  // either in HKLM, or in HKCU, depending on whether we are laying down
-  // the system googleupdate, or the user googleupdate.
-  // We solve this for the user goopdate case by:
-  // * Having the RGS file take a HKROOT parameter that translates to either
-  //   HKLM or HKCU.
-  // * Redirecting HKCR to HKCU\software\classes, for a user installation, to
-  //   cover Proxy registration.
-  // For the machine case, we still redirect HKCR to HKLM\\Software\\Classes,
-  // to ensure that Proxy registration happens in HKLM.
-  HRESULT hr = RedirectHKCR(is_machine);
-  ASSERT1(SUCCEEDED(hr));
-  if (FAILED(hr)) {
-    return hr;
-  }
-  // We need to stop redirecting at the end of this function.
-  ON_SCOPE_EXIT(RemoveRedirectHKCR);
-
-  hr = (*registrar)(register_server);
-  if (FAILED(hr)) {
-    CORE_LOG(LW, (_T("[RegisterOrUnregisterModule failed][%d][0x%08x]"),
-                  register_server, hr));
-    ASSERT1(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr && !register_server);
-  }
-
-  return hr;
-}
-
-HRESULT RegisterOrUnregisterModuleWithTypelib(
-    bool register_server,
-    RegisterOrUnregisterFunction registrar) {
-  ASSERT1(registrar);
-
-  bool is_machine = IsRunningFromOfficialGoopdateDir(true);
-  // By default, ATL registers the control to HKCR and we want to register
-  // either in HKLM, or in HKCU, depending on whether we are laying down
-  // the machine googleupdate, or the user googleupdate.
-  // We solve this for the user goopdate case by:
-  // * Having the RGS file take a HKROOT parameter that translates to either
-  //   HKLM or HKCU.
-  // * Redirecting HKCR to HKCU\software\classes, for a user installation, to
-  //   cover AppId and TypeLib registration
-  // * All the above makes ATL work correctly for 2K/XP. However on Win2K3
-  //   and Vista, redirection does not work by itself, because in these
-  //   platforms, RegisterTypeLib writes explicitly to HKLM\Software\Classes.
-  //   We need to specifically call the new RegisterTypeLibForUser() API.
-  //   So, we do that as well.
-  // For the machine case, we still redirect HKCR to HKLM\\Software\\Classes,
-  // because otherwise RegisterTypeLib ends up overwriting HKCU if the key
-  // already exists in HKCU.
-  HRESULT hr = RedirectHKCR(is_machine);
-  ASSERT1(SUCCEEDED(hr));
-  if (FAILED(hr)) {
-    return hr;
-  }
-  // We need to stop redirecting at the end of this function.
-  ON_SCOPE_EXIT(RemoveRedirectHKCR);
-
-  // load the type library.
-  CComPtr<ITypeLib> type_lib;
-  CComBSTR path;
-  hr = ::AtlLoadTypeLib(_AtlBaseModule.GetModuleInstance(), NULL, &path,
-                        &type_lib);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("[AtlLoadTypeLib failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  if (register_server) {
-    hr = (*registrar)(register_server);
-    if (FAILED(hr)) {
-      ASSERT(false, (_T("[Module registration failed][0x%08x]"), hr));
-      return hr;
-    }
-
-    return RegisterTypeLib(is_machine, path, type_lib);
-  } else {
-    hr = UnRegisterTypeLib(is_machine, path, type_lib);
-    if (FAILED(hr)) {
-      ASSERT(false, (_T("[UnRegisterTypeLib failed][0x%08x]"), hr));
-      return hr;
-    }
-
-    return (*registrar)(register_server);
-  }
-}
-
-HRESULT RegisterTypeLibForUser(ITypeLib* lib,
-                               OLECHAR* path,
-                               OLECHAR* help_dir) {
-  CORE_LOG(L3, (_T("[RegisterTypeLibForUser]")));
-  ASSERT1(lib);
-  ASSERT1(path);
-  // help_dir can be NULL.
-
-  const TCHAR* library_name = _T("oleaut32.dll");
-  scoped_library module(static_cast<HINSTANCE>(::LoadLibrary(library_name)));
-  if (!module) {
-    HRESULT hr = HRESULTFromLastError();
-    CORE_LOG(LEVEL_ERROR,
-        (_T("[LoadLibrary failed][%s][0x%08x]"), library_name, hr));
-    return hr;
-  }
-
-  // RegisterTypeLibForUser function from oleaut32.dll.
-  typedef HRESULT(__stdcall *PF)(ITypeLib*, OLECHAR*, OLECHAR*);
-
-  const char* function_name = "RegisterTypeLibForUser";
-  PF fp = reinterpret_cast<PF>(::GetProcAddress(get(module), function_name));
-  if (!fp) {
-    HRESULT hr = HRESULTFromLastError();
-    CORE_LOG(LEVEL_ERROR,
-             (_T("[GetProcAddress failed][%s][0x%08x]"),
-              function_name, library_name, hr));
-    return hr;
-  }
-
-  CORE_LOG(L3, (_T("[Calling RegisterTypelibForUser in oleaut]")));
-  HRESULT hr = fp(lib, path, help_dir);
-  if (FAILED(hr)) {
-    CORE_LOG(LEVEL_ERROR, (_T("[regtypelib_for_user failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT UnRegisterTypeLibForUser(REFGUID lib_id,
-                                 WORD major_ver_num,
-                                 WORD minor_ver_num,
-                                 LCID lcid,
-                                 SYSKIND syskind) {
-  CORE_LOG(L3, (_T("[UnRegisterTypeLibForUser]")));
-
-  const TCHAR* library_name = _T("oleaut32.dll");
-  scoped_library module(static_cast<HINSTANCE>(::LoadLibrary(library_name)));
-  if (!module) {
-    HRESULT hr = HRESULTFromLastError();
-    CORE_LOG(LEVEL_ERROR,
-        (_T("[LoadLibrary failed][%s][0x%08x]"), library_name, hr));
-    return hr;
-  }
-
-  // UnRegisterTypeLibForUser function from oleaut32.dll.
-  typedef HRESULT (__stdcall *PF)(REFGUID, WORD, WORD, LCID, SYSKIND);
-
-  const char* function_name = "UnRegisterTypeLibForUser";
-  PF fp = reinterpret_cast<PF>(::GetProcAddress(get(module), function_name));
-  if (!fp) {
-    HRESULT hr = HRESULTFromLastError();
-    CORE_LOG(LEVEL_ERROR,
-             (_T("[GetProcAddress failed][%s][0x%08x]"),
-              function_name, library_name, hr));
-    return hr;
-  }
-
-  CORE_LOG(L3, (_T("[Calling UnRegisterTypeLibForUser in oleaut]")));
-  HRESULT hr = fp(lib_id, major_ver_num, minor_ver_num, lcid, syskind);
-  if (FAILED(hr)) {
-    CORE_LOG(LEVEL_ERROR, (_T("[unregtypelib_for_user failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-// This method assumes that the caller has permissions to open
-// the process token of the user's explorer.exe process.
-HRESULT GetImpersonationToken(bool is_interactive,
-                              uint32 explorer_pid,
-                              HANDLE* out_token) {
-  CORE_LOG(L3, (_T("[GetImpersonationToken]")));
-  ASSERT1(out_token);
-
-  // If the job is an interactive job, then we can use the explorer pid
-  // that is passed in for impersonation. If this is an
-  // update job, then we need to get a list of all the logged on users
-  // and pick one to perform the impersonation.
-  // TODO(omaha): Try to use the token of the user that is a domain account,
-  // since we are trying to solve the integrated proxy authentication issue.
-  // One way to do this might be to try the GetWindowsAccountDomainSid API.
-  if (is_interactive) {
-    scoped_handle exp(::OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
-                                    false, explorer_pid));
-    if (!exp) {
-      HRESULT hr = HRESULTFromLastError();
-      CORE_LOG(LEVEL_ERROR, (_T("[OpenProcess failed][0x%08x]"), hr));
-      return hr;
-    }
-
-    if (!::OpenProcessToken(get(exp),
-                            TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
-                            out_token)) {
-      HRESULT hr = HRESULTFromLastError();
-      CORE_LOG(LEVEL_ERROR, (_T("[OpenProcessToken failed][0x%08x]"), hr));
-      return hr;
-    }
-  } else {
-    HRESULT hr = vista::GetExplorerTokenForLoggedInUser(out_token);
-    if (FAILED(hr)) {
-      CORE_LOG(LEVEL_ERROR, (_T("[GetExplorerTokenForLoggedInUser failed]")
-                             _T("[0x%08x]"), hr));
-      return hr;
-    }
-  }
-
-  return S_OK;
-}
-
-HRESULT UndoImpersonation(bool impersonated) {
-  CORE_LOG(L3, (_T("[UndoImpersonation]")));
-
-  if (impersonated && !::RevertToSelf()) {
-    // TODO(omaha): For now we assume that this never fails, change this
-    // impersonation to occur on a different thread, so that even if this fails,
-    // we can simply kill the thread.
-    // If this function call fails, we have a problem. We need to shut down the
-    // googleupdate process, since we are now running the system googleupdate
-    // as the user, and a number of assumptions about the code will fail.
-    HRESULT hr = HRESULTFromLastError();
-    CORE_LOG(LEVEL_ERROR, (_T("[RevertToSelf failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT ImpersonateUser(bool is_interactive, uint32 explorer_pid) {
-  CORE_LOG(L3, (_T("[ImpersonateUser]")));
-
-  HANDLE handle = NULL;
-  HRESULT hr = GetImpersonationToken(is_interactive, explorer_pid, &handle);
-  if (FAILED(hr)) {
-    CORE_LOG(LEVEL_ERROR, (_T("[GetImpersonationToken failed][0x%08x]"), hr));
-    return hr;
-  }
-  ASSERT1(handle);
-
-  // TODO(omaha): The impersonation will fail if the user is running on a
-  // Win2K SP3 or earlier, or WinXP SP1 or earlier. This is because the
-  // seImpersonateProvilage is needed to call this method, and this privilege
-  // does not exist in these systems.
-  // One way to work around this would be to launch a separate process
-  // using CreateProcessAsUser on these systems.
-  scoped_handle token(handle);
-  if (!::ImpersonateLoggedOnUser(get(token))) {
-    hr = HRESULTFromLastError();
-    CORE_LOG(LEVEL_ERROR, (_T("[ImpersonateLoggedOnUser failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-// The EULA is assumed to be accepted unless eualaccepted=0 in the ClientState
-// key. For machine apps in this case, eulaccepted=1 in ClientStateMedium also
-// indicates acceptance and the value in ClientState is updated.
-bool IsAppEulaAccepted(bool is_machine,
-                       const CString& app_guid,
-                       bool require_explicit_acceptance) {
-  const CString state_key = GetAppClientStateKey(is_machine, app_guid);
-
-  DWORD eula_accepted = 0;
-  if (SUCCEEDED(RegKey::GetValue(state_key,
-                                 kRegValueEulaAccepted,
-                                 &eula_accepted))) {
-    if (0 != eula_accepted) {
-      return true;
-    }
-  } else {
-    if (!require_explicit_acceptance) {
-      return true;
-    }
-  }
-
-  if (!is_machine) {
-    return false;
-  }
-
-  eula_accepted = 0;
-  if (SUCCEEDED(RegKey::GetValue(
-                    GetAppClientStateMediumKey(is_machine, app_guid),
-                    kRegValueEulaAccepted,
-                    &eula_accepted))) {
-    if (0 == eula_accepted) {
-      return false;
-    }
-  } else {
-    return false;
-  }
-
-  VERIFY1(SUCCEEDED(RegKey::SetValue(state_key,
-                                     kRegValueEulaAccepted,
-                                     eula_accepted)));
-  return true;
-}
-
-// Does not need to set ClientStateMedium.
-HRESULT SetAppEulaNotAccepted(bool is_machine, const CString& app_guid) {
-  return RegKey::SetValue(GetAppClientStateKey(is_machine, app_guid),
-                          kRegValueEulaAccepted,
-                          static_cast<DWORD>(0));
-}
-
-// Deletes eulaaccepted from ClientState and ClientStateMedium.
-HRESULT ClearAppEulaNotAccepted(bool is_machine, const CString& app_guid) {
-  const CString state_key = GetAppClientStateKey(is_machine, app_guid);
-  if (RegKey::HasKey(state_key)) {
-    HRESULT hr = RegKey::DeleteValue(state_key, kRegValueEulaAccepted);
-    if (FAILED(hr)) {
-      return hr;
-    }
-  }
-
-  if (!is_machine) {
-    return S_OK;
-  }
-
-  const CString state_medium_key =
-      GetAppClientStateMediumKey(is_machine, app_guid);
-  if (RegKey::HasKey(state_medium_key)) {
-    HRESULT hr = RegKey::DeleteValue(state_medium_key, kRegValueEulaAccepted);
-    if (FAILED(hr)) {
-      return hr;
-    }
-  }
-
-  return S_OK;
-}
-
-// For machine apps, ClientStateMedium takes precedence.
-// Does not propogate the ClientStateMedium value to ClientState.
-bool AreAppUsageStatsEnabled(bool is_machine, const CString& app_guid) {
-  if (is_machine) {
-    DWORD stats_enabled = 0;
-    if (SUCCEEDED(RegKey::GetValue(GetAppClientStateMediumKey(is_machine,
-                                                              app_guid),
-                                   kRegValueUsageStats,
-                                   &stats_enabled))) {
-      return (TRISTATE_TRUE == stats_enabled);
-    }
-  }
-
-  DWORD stats_enabled = 0;
-  if (SUCCEEDED(RegKey::GetValue(GetAppClientStateKey(is_machine, app_guid),
-                              kRegValueUsageStats,
-                              &stats_enabled))) {
-    return (TRISTATE_TRUE == stats_enabled);
-  }
-
-  return false;
-}
-
-// Does nothing if usage_stats_enable is TRISTATE_NONE.
-// For machine apps, clears ClientStateMedium because the app may be reading it
-// if present.
-HRESULT SetUsageStatsEnable(bool is_machine,
-                            const CString& app_guid,
-                            Tristate usage_stats_enable) {
-  if (TRISTATE_NONE == usage_stats_enable) {
-    return S_OK;
-  }
-
-  const DWORD stats_enabled = (TRISTATE_TRUE == usage_stats_enable) ? 1 : 0;
-
-  HRESULT hr = RegKey::SetValue(GetAppClientStateKey(is_machine, app_guid),
-                                kRegValueUsageStats,
-                                stats_enabled);
-  if (FAILED(hr)) {
-    CORE_LOG(LW, (_T("[Failed to set usagestats][0x%08x]"), hr));
-    return hr;
-  }
-
-  if (!is_machine) {
-    return S_OK;
-  }
-
-  const CString state_medium_key =
-      GetAppClientStateMediumKey(is_machine, app_guid);
-  if (RegKey::HasKey(state_medium_key)) {
-    hr = RegKey::DeleteValue(state_medium_key, kRegValueUsageStats);
-    if (FAILED(hr)) {
-      return hr;
-    }
-  }
-
-  return S_OK;
-}
-
-// Writes usagestats in Google Update's ClientState key. This is the only case
-// in which usagestats is written to Google Update's ClientState key. Normally,
-// we write to the ClientState key(s) for the specific app(s).
-HRESULT ConvertLegacyUsageStats(bool is_machine) {
-  DWORD existing_usage_stats(0);
-
-  ConfigManager& config_mgr = *ConfigManager::Instance();
-  const CString legacy_key_name = config_mgr.registry_update(is_machine);
-  if (FAILED(RegKey::GetValue(legacy_key_name,
-                              kLegacyRegValueCollectUsageStats,
-                              &existing_usage_stats))) {
-    return S_OK;
-  }
-
-  const CString new_key_name = GetAppClientStateKey(is_machine,
-                                                    kGoogleUpdateAppId);
-  HRESULT hr = RegKey::SetValue(new_key_name,
-                                kRegValueUsageStats,
-                                existing_usage_stats);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  VERIFY1(SUCCEEDED(RegKey::DeleteValue(legacy_key_name,
-                                        kLegacyRegValueCollectUsageStats)));
-  return S_OK;
-}
-
-CString GetDefaultGoopdateTaskName(bool is_machine, CommandLineMode mode) {
-  ASSERT1(mode == COMMANDLINE_MODE_CORE || mode == COMMANDLINE_MODE_UA);
-
-  CString task_name;
-  if (is_machine) {
-    task_name = kScheduledTaskNameMachinePrefix;
-  } else {
-    task_name = kScheduledTaskNameUserPrefix;
-    CString user_sid;
-    VERIFY1(SUCCEEDED(user_info::GetCurrentUser(NULL, NULL, &user_sid)));
-    task_name += user_sid;
-  }
-
-  task_name += (mode == COMMANDLINE_MODE_CORE) ? kScheduledTaskNameCoreSuffix :
-                                                 kScheduledTaskNameUASuffix;
-  return task_name;
-}
-
-HRESULT InstallGoopdateTaskForMode(const TCHAR* task_path,
-                                   bool is_machine,
-                                   CommandLineMode mode) {
-  ASSERT1(mode == COMMANDLINE_MODE_CORE || mode == COMMANDLINE_MODE_UA);
-
-  CommandLineBuilder builder(mode);
-  if (mode == COMMANDLINE_MODE_UA) {
-    builder.set_install_source(kCmdLineInstallSourceScheduler);
-  }
-
-  CString task_description;
-  VERIFY1(task_description.LoadString(IDS_SCHEDULED_TASK_DESCRIPTION));
-
-  CString task_name(mode == COMMANDLINE_MODE_CORE ?
-                    ConfigManager::GetCurrentTaskNameCore(is_machine) :
-                    ConfigManager::GetCurrentTaskNameUA(is_machine));
-  if (internal::IsInstalledScheduledTask(task_name)) {
-    HRESULT hr = internal::InstallScheduledTask(task_name,
-                                                task_path,
-                                                builder.GetCommandLineArgs(),
-                                                task_description,
-                                                is_machine,
-                                                mode == COMMANDLINE_MODE_CORE &&
-                                                is_machine,
-                                                true,
-                                                mode == COMMANDLINE_MODE_UA);
-
-    if (SUCCEEDED(hr)) {
-      return hr;
-    }
-
-    // Try to uninstall the task that we failed to upgrade. Then create a new
-    // task name, and fall through to install that.
-    internal::UninstallScheduledTask(task_name);
-    if (mode == COMMANDLINE_MODE_CORE) {
-      VERIFY1(SUCCEEDED(
-      ConfigManager::CreateAndSetVersionedTaskNameCoreInRegistry(is_machine)));
-      task_name = ConfigManager::GetCurrentTaskNameCore(is_machine);
-    } else {
-      VERIFY1(SUCCEEDED(
-      ConfigManager::CreateAndSetVersionedTaskNameUAInRegistry(is_machine)));
-      task_name = ConfigManager::GetCurrentTaskNameUA(is_machine);
-    }
-    ASSERT1(!internal::IsInstalledScheduledTask(task_name));
-  }
-
-  return internal::InstallScheduledTask(task_name,
-                                        task_path,
-                                        builder.GetCommandLineArgs(),
-                                        task_description,
-                                        is_machine,
-                                        mode == COMMANDLINE_MODE_CORE &&
-                                        is_machine,
-                                        true,
-                                        mode == COMMANDLINE_MODE_UA);
-}
-
-HRESULT InstallGoopdateTasks(const TCHAR* task_path, bool is_machine) {
-  HRESULT hr = InstallGoopdateTaskForMode(task_path,
-                                          is_machine,
-                                          COMMANDLINE_MODE_CORE);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return InstallGoopdateTaskForMode(task_path, is_machine, COMMANDLINE_MODE_UA);
-}
-
-HRESULT UninstallGoopdateTasks(bool is_machine) {
-  VERIFY1(SUCCEEDED(internal::UninstallScheduledTask(
-      ConfigManager::GetCurrentTaskNameCore(is_machine))));
-  VERIFY1(SUCCEEDED(internal::UninstallScheduledTask(
-      ConfigManager::GetCurrentTaskNameUA(is_machine))));
-
-  // Try to uninstall any tasks that we failed to update during a previous
-  // overinstall. It is possible that we fail to uninstall these again here.
-  VERIFY1(SUCCEEDED(internal::UninstallScheduledTasks(
-      goopdate_utils::GetDefaultGoopdateTaskName(is_machine,
-                                                 COMMANDLINE_MODE_CORE))));
-  VERIFY1(SUCCEEDED(internal::UninstallScheduledTasks(
-      goopdate_utils::GetDefaultGoopdateTaskName(is_machine,
-                                                 COMMANDLINE_MODE_UA))));
-  return S_OK;
-}
-
-HRESULT UninstallLegacyGoopdateTasks(bool is_machine) {
-  const CString& legacy_omaha1_task =
-      internal::GetOmaha1LegacyTaskName(is_machine);
-  VERIFY1(SUCCEEDED(internal::UninstallScheduledTask(legacy_omaha1_task)));
-
-  const CString& legacy_omaha2_task =
-      internal::GetOmaha2LegacyTaskName(is_machine);
-  VERIFY1(SUCCEEDED(internal::UninstallScheduledTask(legacy_omaha2_task)));
-
-  return S_OK;
-}
-
-HRESULT StartGoopdateTaskCore(bool is_machine) {
-  return internal::StartScheduledTask(
-             ConfigManager::GetCurrentTaskNameCore(is_machine));
-}
-
-bool IsInstalledGoopdateTaskUA(bool is_machine) {
-  return internal::IsInstalledScheduledTask(
-                             ConfigManager::GetCurrentTaskNameUA(is_machine));
-}
-
-bool IsDisabledGoopdateTaskUA(bool is_machine) {
-  const CString& task_name(ConfigManager::GetCurrentTaskNameUA(is_machine));
-  return internal::GetScheduledTaskStatus(task_name) == SCHED_S_TASK_DISABLED;
-}
-
-HRESULT GetExitCodeGoopdateTaskUA(bool is_machine) {
-  const CString& task_name(ConfigManager::GetCurrentTaskNameUA(is_machine));
-  return internal::GetScheduledTaskExitCode(task_name);
-}
-
-HRESULT GetClientsStringValueFromRegistry(bool is_machine,
-                                          const CString& app_guid,
-                                          const CString& value_name,
-                                          CString* value) {
-  CORE_LOG(L3, (_T("[GetClientsStringValueFromRegistry][%d][%s][%s]"),
-                is_machine, app_guid, value_name));
-
-  ASSERT1(value);
-
-  CString app_client_key_name = GetAppClientsKey(is_machine, app_guid);
-
-  return RegKey::GetValue(app_client_key_name, value_name, value);
-}
-
-HRESULT GetVerFromRegistry(bool is_machine,
-                           const CString& app_guid,
-                           CString* version) {
-  ASSERT1(version);
-  return GetClientsStringValueFromRegistry(is_machine,
-                                           app_guid,
-                                           kRegValueProductVersion,
-                                           version);
-}
-
-HRESULT TerminateAllBrowsers(
-    BrowserType type,
-    TerminateBrowserResult* browser_res,
-    TerminateBrowserResult* default_res) {
-  UTIL_LOG(L3, (_T("[TerminateAllBrowsers][%d]"), type));
-  ASSERT1(default_res);
-  ASSERT1(browser_res);
-
-  if (type == BROWSER_UNKNOWN ||
-      type == BROWSER_DEFAULT ||
-      type >= BROWSER_MAX) {
-    ASSERT1(false);
-    return E_INVALIDARG;
-  }
-
-  const BrowserType kFirstBrowser = BROWSER_IE;
-  const int kNumSupportedBrowsers = BROWSER_MAX - kFirstBrowser;
-
-  BrowserType default_type = BROWSER_UNKNOWN;
-  HRESULT hr = GetDefaultBrowserType(&default_type);
-  if (FAILED(hr)) {
-    UTIL_LOG(LW, (_T("[GetDefaultBrowserType failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  TerminateBrowserResult terminate_results[kNumSupportedBrowsers];
-
-  for (int browser = 0; browser < kNumSupportedBrowsers; ++browser) {
-    const BrowserType browser_type =
-        static_cast<BrowserType>(kFirstBrowser + browser);
-    hr = TerminateBrowserProcess(browser_type,
-                                 CString(),
-                                 0,
-                                 &terminate_results[browser].found);
-    if (FAILED(hr)) {
-      UTIL_LOG(LW, (_T("[TerminateBrowserProcess failed][%u][0x%08x]"),
-                    browser_type, hr));
-    }
-  }
-
-  // Now wait for the all browser instances to die.
-  // TODO(omaha): Wait for all processes at once rather than waiting for
-  // (kTerminateBrowserTimeoutMs * # supported browsers) ms.
-  for (int browser = 0; browser < kNumSupportedBrowsers; ++browser) {
-    const BrowserType browser_type =
-        static_cast<BrowserType>(kFirstBrowser + browser);
-    hr = WaitForBrowserToDie(browser_type,
-                             CString(),
-                             kTerminateBrowserTimeoutMs);
-    if (FAILED(hr)) {
-      UTIL_LOG(LW, (_T("[WaitForBrowserToDie failed][%u][0x%08x]"),
-                    browser_type, hr));
-    } else {
-      terminate_results[browser].could_terminate = true;
-    }
-  }
-
-  *browser_res = terminate_results[type - kFirstBrowser];
-  *default_res = terminate_results[default_type - kFirstBrowser];
-
-  return S_OK;
-}
-
-// default_type can be BROWSER_UNKNOWN.
-// If browsers that must be closed could not be terminated, false is returned.
-// This method and TerminateBrowserProcesses assume the user did not shutdown
-// the specified browser. They restart and shutdown, respectively, the default
-// browser when the specified browser is not found. The reason for this may have
-// been that the the specified (stamped) browser could be in a bad state on the
-// machine and trying to start it would fail.
-// This may also be required to support hosted cases (i.e. AOL and Maxthon).
-// TODO(omaha): If we assume the stamped browser is okay, check whether the
-// specified browser is installed rather than relying on whether the browser was
-// running. It is perfectly valid for the browser to not be running.
-// TODO(omaha): Why not try the default browser if browsers that require
-// shutdown failed to terminate.
-bool GetBrowserToRestart(BrowserType type,
-                         BrowserType default_type,
-                         const TerminateBrowserResult& res,
-                         const TerminateBrowserResult& def_res,
-                         BrowserType* browser_type) {
-  ASSERT1(browser_type);
-  ASSERT1(type != BROWSER_UNKNOWN &&
-          type != BROWSER_DEFAULT &&
-          type < BROWSER_MAX);
-  ASSERT1(default_type != BROWSER_DEFAULT && default_type < BROWSER_MAX);
-  UTIL_LOG(L3, (_T("[GetBrowserToRestart][%d]"), type));
-
-  *browser_type = BROWSER_UNKNOWN;
-
-  if (res.found) {
-    switch (type) {
-      case BROWSER_IE:
-        *browser_type = BROWSER_IE;
-        return true;
-      case BROWSER_FIREFOX:   // Only one process.
-      case BROWSER_CHROME:    // One process per plug-in, even for upgrades.
-        if (res.could_terminate) {
-          *browser_type = type;
-          return true;
-        }
-        return false;
-      case BROWSER_UNKNOWN:
-      case BROWSER_DEFAULT:
-      case BROWSER_MAX:
-      default:
-        break;
-    }
-  }
-
-  // We did not find the browser that we wanted to restart. Hence we need to
-  // determine if we could shutdown the default browser.
-  switch (default_type) {
-    case BROWSER_IE:
-      *browser_type = BROWSER_IE;
-      return true;
-    case BROWSER_FIREFOX:
-    case BROWSER_CHROME:
-      if (!def_res.found || def_res.found && def_res.could_terminate) {
-        *browser_type = default_type;
-        return true;
-      }
-      break;
-    case BROWSER_UNKNOWN:
-    case BROWSER_DEFAULT:
-    case BROWSER_MAX:
-    default:
-      break;
-  }
-
-  return false;
-}
-
-// See the comments about the default browser above GetBrowserToRestart.
-HRESULT TerminateBrowserProcesses(BrowserType type,
-                                  TerminateBrowserResult* browser_res,
-                                  TerminateBrowserResult* default_res) {
-  UTIL_LOG(L3, (_T("[TerminateBrowserProcesses][%d]"), type));
-  ASSERT1(browser_res);
-  ASSERT1(default_res);
-
-  browser_res->could_terminate = false;
-  default_res->could_terminate = false;
-
-  if (type == BROWSER_UNKNOWN ||
-      type == BROWSER_DEFAULT ||
-      type >= BROWSER_MAX) {
-    ASSERT1(false);
-    return E_UNEXPECTED;
-  }
-
-  CString sid;
-  HRESULT hr = user_info::GetCurrentUser(NULL, NULL, &sid);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[GetCurrentUser failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  hr = TerminateBrowserProcess(type,
-                               sid,
-                               kTerminateBrowserTimeoutMs,
-                               &browser_res->found);
-  if (FAILED(hr)) {
-    UTIL_LOG(LW, (_T("[TerminateBrowserProcess failed][0x%08x]"), hr));
-  } else {
-    browser_res->could_terminate = true;
-  }
-
-  // Since no instances of the browser type exist, we try to find and kill
-  // all instances of the default browser.
-  if (!browser_res->found) {
-    // We dont want to try and terminate the default browser, if it is the
-    // same as the browser that we tried above.
-
-    BrowserType default_type = BROWSER_UNKNOWN;
-    hr = GetDefaultBrowserType(&default_type);
-    if (FAILED(hr)) {
-      UTIL_LOG(LW, (_T("[GetDefaultBrowserType failed][0x%08x]"), hr));
-    }
-
-    UTIL_LOG(L3, (_T("[Trying to kill the default browser %d]"), default_type));
-    if (default_type != type) {
-      hr = TerminateBrowserProcess(BROWSER_DEFAULT,
-                                   sid,
-                                   kTerminateBrowserTimeoutMs,
-                                   &default_res->found);
-      if (FAILED(hr)) {
-        UTIL_LOG(LW, (_T("[TerminateBrowserProcess failed][0x%08x]"), hr));
-      } else {
-        default_res->could_terminate = true;
-      }
-    }
-  }
-
-  return hr;
-}
-
-HRESULT StartBrowserWithProcessToken(bool is_machine,
-                                     BrowserType type,
-                                     const CString& url,
-                                     uint32 explorer_pid) {
-  UTIL_LOG(L3, (_T("[StartBrowserWithProcessToken.]")
-                _T("[is_machine = %d][type = %d]"), is_machine, type));
-
-  // In case of machine goopdate we need to CreateProcessAsUser, else we ask
-  // the shell to create a new browser process.
-  if (is_machine) {
-    CString browser_path;
-    HRESULT hr = GetBrowserImagePath(type, &browser_path);
-    if (FAILED(hr)) {
-      UTIL_LOG(LEVEL_ERROR, (_T("[GetBrowserImagePath failed.][0x%08x]"), hr));
-      return hr;
-    }
-    ASSERT1(!browser_path.IsEmpty());
-
-    CString command_line;
-    EnclosePath(&browser_path);
-    command_line.Format(_T("%s %s"), browser_path, url);
-    UTIL_LOG(L3, (_T("[Executing command line %s.]"), command_line));
-    hr = vista::StartProcessWithTokenOfProcess(explorer_pid, command_line);
-    if (FAILED(hr)) {
-      UTIL_LOG(LW, (_T("[StartProcessWithTokenOfProcess failed][0x%08x]"), hr));
-      return hr;
-    }
-  } else {
-    return ShellExecuteBrowser(type, url);
-  }
-
-  return S_OK;
-}
-
-HRESULT GetBrowserImagePathFromProcess(BrowserType type,
-                                       uint32 explorer_pid,
-                                       CString* path) {
-  ASSERT1(path);
-
-  if (type == BROWSER_UNKNOWN || type >= BROWSER_MAX) {
-    ASSERT1(false);
-    return E_UNEXPECTED;
-  }
-
-  if (type == BROWSER_DEFAULT) {
-    return GetDefaultBrowserPath(path);
-  }
-
-  CString user_sid;
-  HRESULT hr = Process::GetProcessOwner(explorer_pid, &user_sid);
-  if (FAILED(hr)) {
-    UTIL_LOG(LEVEL_WARNING, (_T("[GetProcessOwner failed.][0x%08x]"), hr));
-    return hr;
-  }
-
-  CString browser_name;
-  hr = BrowserTypeToProcessName(type, &browser_name);
-  if (FAILED(hr)) {
-    UTIL_LOG(LW, (_T("[BrowserTypeToProcessName failed.][0x%08x]"), hr));
-    return hr;
-  }
-
-  hr = Process::GetImagePath(browser_name, user_sid, path);
-  if (FAILED(hr)) {
-    UTIL_LOG(LW, (_T("[GetImagePath failed.][0x%08x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT ConvertStringToBrowserType(const CString& text, BrowserType* type) {
-  ASSERT1(type != NULL);
-
-  if (text.GetLength() != 1) {
-    return GOOPDATEUTILS_E_BROWSERTYPE;
-  }
-
-  int browser_type = 0;
-  if (!String_StringToDecimalIntChecked(text, &browser_type)) {
-    return GOOPDATEUTILS_E_BROWSERTYPE;
-  }
-
-  if (browser_type >= BROWSER_MAX) {
-    return GOOPDATEUTILS_E_BROWSERTYPE;
-  }
-
-  *type = static_cast<BrowserType>(browser_type);
-  return S_OK;
-}
-
-CString ConvertBrowserTypeToString(BrowserType type) {
-  CString text = itostr(static_cast<int>(type));
-  ASSERT1(!text.IsEmpty());
-  return text;
-}
-
-bool IsServiceInstalled() {
-  return ServiceInstall::IsServiceInstalled(
-                             ConfigManager::GetCurrentServiceName());
-}
-
-HRESULT GetOSInfo(CString* os_version, CString* service_pack) {
-  ASSERT1(os_version);
-  ASSERT1(service_pack);
-
-  OSVERSIONINFO os_version_info = { 0 };
-  os_version_info.dwOSVersionInfoSize = sizeof(os_version_info);
-  if (!::GetVersionEx(&os_version_info)) {
-    HRESULT hr = HRESULTFromLastError();
-    UTIL_LOG(LW, (_T("[GetVersionEx failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  os_version->Format(_T("%d.%d"),
-                     os_version_info.dwMajorVersion,
-                     os_version_info.dwMinorVersion);
-  *service_pack = os_version_info.szCSDVersion;
-  return S_OK;
-}
-
-CPath BuildInstallDirectory(bool is_machine, const CString& version) {
-  ConfigManager& cm = *ConfigManager::Instance();
-  CPath install_dir(is_machine ? cm.GetMachineGoopdateInstallDir() :
-                                 cm.GetUserGoopdateInstallDir());
-  VERIFY1(install_dir.Append(version));
-
-  return install_dir;
-}
-
-HRESULT LaunchCmdLine(const CString& cmd_line) {
-  CORE_LOG(L3, (_T("[LaunchCmdLine][%s]"), cmd_line));
-
-  bool is_split_token = false;
-  HRESULT hr = vista_util::IsUserRunningSplitToken(&is_split_token);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[IsUserRunningSplitToken failed][0x%x]"), hr));
-    return hr;
-  }
-
-  bool run_with_lower_privileges = is_split_token && vista_util::IsUserAdmin();
-
-  if (run_with_lower_privileges) {
-    return LaunchImpersonatedCmdLine(cmd_line);
-  }
-
-  hr = System::ShellExecuteCommandLine(cmd_line, NULL, NULL);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[ShellExecuteCommandLine failed][0x%x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT LaunchBrowser(BrowserType type, const CString& url) {
-  CORE_LOG(L3, (_T("[LaunchBrowser][%u][%s]"), type, url));
-
-  bool is_split_token = false;
-  HRESULT hr = vista_util::IsUserRunningSplitToken(&is_split_token);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[IsUserRunningSplitToken failed][0x%x]"), hr));
-    return hr;
-  }
-
-  bool run_with_lower_privileges = is_split_token && vista_util::IsUserAdmin();
-
-  if (run_with_lower_privileges) {
-    // Other than having a service launch the browser using CreateProcessAsUser,
-    // there is no easy solution if we are unable to launch the browser
-    // impersonated.
-    return LaunchImpersonatedBrowser(type, url);
-  }
-
-  hr = ShellExecuteBrowser(type, url);
-  if (FAILED(hr)) {
-    UTIL_LOG(LE, (_T("[ShellExecuteBrowser failed][0x%x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-// This method formats all the data that is present inside the UpdateResponse
-// in the form of the extra arguments command line.
-// The values that are converted include:
-// appguid, appname (will not be present in pre-I18N builds.), needsadmin
-// iid, ap, browser; values not used in pre-I18N builds are not supported.
-HRESULT ConvertResponseDataToExtraArgs(const UpdateResponseData& response_data,
-                                       CString* extra) {
-  ASSERT1(extra);
-  *extra = _T("");
-
-  // Append the application guid.
-  if (response_data.guid() == GUID_NULL) {
-    // The guid should always be present.
-    return E_INVALIDARG;
-  }
-  CString str_guid = GuidToString(response_data.guid());
-  ASSERT1(!str_guid.IsEmpty());
-  extra->AppendFormat(_T("%s=%s"), kExtraArgAppGuid, str_guid);
-
-  // Convert the application name into the command line format. In case of
-  // pre-I18N installers, the name is not known to the installer, hence if
-  // the name is empty we use a hard coded name.
-  CString product_name;
-  if (!response_data.app_name().IsEmpty()) {
-    HRESULT hr = WideStringToUtf8UrlEncodedString(response_data.app_name(),
-                                                  &product_name);
-    if (FAILED(hr)) {
-      return hr;
-    }
-  } else {
-    product_name = GetProductName(str_guid);
-  }
-  ASSERT1(!product_name.IsEmpty());
-  extra->AppendFormat(_T("&%s=%s"), kExtraArgAppName, product_name);
-
-  // Append needs admin.
-  CString needs_admin_str;
-  HRESULT hr = ConvertNeedsAdminToString(response_data.needs_admin(),
-                                         &needs_admin_str);
-
-  if (FAILED(hr)) {
-    return hr;
-  }
-  ASSERT1(!needs_admin_str.IsEmpty());
-  extra->AppendFormat(_T("&%s=%s"), kExtraArgNeedsAdmin, needs_admin_str);
-
-  if (response_data.installation_id() != GUID_NULL) {
-    CString str_installationid = GuidToString(response_data.installation_id());
-    extra->AppendFormat(_T("&%s=%s"), kExtraArgInstallationId,
-                        str_installationid);
-  }
-  // Append the browser tag.
-  if (response_data.browser_type() != BROWSER_UNKNOWN) {
-    CString browser_type =
-        ConvertBrowserTypeToString(response_data.browser_type());
-    extra->AppendFormat(_T("&%s=%s"), kExtraArgBrowserType, browser_type);
-  }
-
-  // Append ap tag.
-  if (!response_data.ap().IsEmpty()) {
-    extra->AppendFormat(_T("&%s=%s"),
-                        kExtraArgAdditionalParameters,
-                        response_data.ap());
-  }
-
-  // Append TT tag.
-  if (!response_data.tt_token().IsEmpty()) {
-    extra->AppendFormat(_T("&%s=%s"),
-                        kExtraArgTTToken,
-                        response_data.tt_token());
-  }
-
-  return S_OK;
-}
-
-HRESULT ConvertNeedsAdminToString(NeedsAdmin needs_admin, CString* text) {
-  ASSERT1(text);
-  switch (needs_admin) {
-    case omaha::NEEDS_ADMIN_YES:
-      *text = kTrue;
-      break;
-    case omaha::NEEDS_ADMIN_NO:
-      *text = kFalse;
-      break;
-    default:
-      return E_INVALIDARG;
-  }
-
-  return S_OK;
-}
-
-HRESULT ConvertStringToNeedsAdmin(const CString& text, NeedsAdmin* admin) {
-  ASSERT1(admin);
-  if (_tcsicmp(text, kTrue) == 0) {
-    *admin = omaha::NEEDS_ADMIN_YES;
-  } else if (_tcsicmp(text, kFalse) == 0) {
-    *admin = omaha::NEEDS_ADMIN_NO;
-  } else {
-    return GOOPDATEXML_E_NEEDSADMIN;
-  }
-
-  return S_OK;
-}
-
-HRESULT HandleLegacyManifestHandoff(const CString& manifest_filename,
-                                    bool is_machine) {
-  OPT_LOG(L1, (_T("[HandleLegacyManifestHandoff]")));
-
-  // Read the manifest file.
-  std::vector<byte> xml_contents;
-  HRESULT hr = GoopdateXmlParser::LoadXmlFileToMemory(manifest_filename,
-                                                      &xml_contents);
-  if (FAILED(hr)) {
-    OPT_LOG(LE, (_T("[Could not load manifest file to memory][%s][0x%08x]"),
-                 manifest_filename, hr));
-    // There is something wrong with the file. Move the file to a .bad file.
-    CString bad(manifest_filename);
-    bad.Append(_T(".bad"));
-    File::Move(manifest_filename, bad, true);
-    return hr;
-  }
-  VERIFY1(SUCCEEDED(File::Remove(manifest_filename)));
-
-  // Parse the manifest.
-  UpdateResponses responses;
-  hr = GoopdateXmlParser::ParseManifestBytes(xml_contents, &responses);
-  if (FAILED(hr)) {
-    OPT_LOG(LE, (_T("[Could not parse manifest][%s]"), manifest_filename));
-    return hr;
-  }
-  ASSERT1(!responses.empty());
-
-  // We we support one application in legacy Omaha.
-  ASSERT1(1 == responses.size());
-  UpdateResponses::const_iterator iter = responses.begin();
-  const UpdateResponse& response = (*iter).second;
-  if (response.update_response_data().needs_admin() ==
-      NEEDS_ADMIN_YES && !is_machine) {
-    return GOOPDATE_E_NON_ADMINS_CANNOT_INSTALL_ADMIN;
-  }
-
-  // Convert the contents of the manifest file into the extraargs command line
-  // format which is the standard omaha2 worker command line.
-  // We re-launch the worker with the new command line instead of handling the
-  // legacy handoff right here, as this allows the other parts of setup
-  // and worker to only know about the new command line and not have to deal
-  // with the legacy commandline. One place where setup and the worker
-  // get simplified because of this is during search for processes with
-  // needsadmin=true.
-  CString extra_args;
-  hr = ConvertResponseDataToExtraArgs(response.update_response_data(),
-                                      &extra_args);
-  if (FAILED(hr)) {
-    OPT_LOG(LE, (_T("[ConvertResponseDataToExtraArgs failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  // /handoff "extraargs"
-  CommandLineBuilder builder(COMMANDLINE_MODE_HANDOFF_INSTALL);
-  builder.set_extra_args(extra_args);
-  CString cmd_line = builder.GetCommandLineArgs();
-
-  hr = StartGoogleUpdateWithArgs(is_machine, cmd_line, NULL);
-  if (FAILED(hr)) {
-    OPT_LOG(LE, (_T("[StartGoogleUpdateWithArgs failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-// This method does a very specific job of searching for install workers,
-// for the user and machine omaha. It also includes the on-demand updates COM
-// server, because we treat it similar to interactive installs.
-//
-// In machine case we search in all the accounts since the install worker can be
-// running in any admin account and the machine update worker runs as SYSTEM.
-// In the user case, we only search the user's account.
-// In both cases, the Needsadmin command line parameter is checked for
-// true/false in the machine/user case, respectively.
-//
-// Only adds processes to the input vector; does not clear it.
-//
-// TODO(omaha): For now we search for the needs_admin=true in the command
-// line to determine a machine install. Another option of identifying omaha's
-// is to use the presence of a named mutex. So the user omaha will create
-// Global\<sid>\Mutex and the machine will create Global\Mutex, in here then
-// we can test for the presence of the name to decide if an interactive
-// omaha is running.
-// TODO(omaha): Consider further filtering the processes based on whether
-// the owner is elevated in case of machine omaha.
-HRESULT GetInstallWorkerProcesses(bool is_machine,
-                                  std::vector<uint32>* processes) {
-  ASSERT1(processes);
-
-  CString user_sid;
-  DWORD flags = EXCLUDE_CURRENT_PROCESS |
-                INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING;
-
-  std::vector<CString> command_lines;
-  CString command_line_to_include;
-  command_line_to_include.Format(_T("/%s"), kCmdLineAppHandoffInstall);
-  command_lines.push_back(command_line_to_include);
-  command_line_to_include.Format(_T("/%s"), kCmdLineFinishGoogleUpdateInstall);
-  command_lines.push_back(command_line_to_include);
-  command_lines.push_back(kCmdLineComServerDash);
-
-  if (!is_machine) {
-    // Search only the same sid as the current user.
-    flags |= INCLUDE_ONLY_PROCESS_OWNED_BY_USER;
-
-    HRESULT hr = user_info::GetCurrentUser(NULL, NULL, &user_sid);
-    if (FAILED(hr)) {
-      CORE_LOG(LE, (_T("[GetCurrentUser failed][0x%08x]"), hr));
-      return hr;
-    }
-  }
-
-  std::vector<uint32> all_install_worker_processes;
-  HRESULT hr = Process::FindProcesses(flags,
-                                      kGoopdateFileName,
-                                      true,
-                                      user_sid,
-                                      command_lines,
-                                      &all_install_worker_processes);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[FindProcesses failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  CString needsadmin_arg;
-  needsadmin_arg.Format(_T("%s=%s"), kExtraArgNeedsAdmin, is_machine ?
-                                                          _T("True") :
-                                                          _T("False"));
-  needsadmin_arg.MakeLower();
-
-  CString official_path;
-  hr = GetFolderPath(is_machine ? CSIDL_PROGRAM_FILES : CSIDL_LOCAL_APPDATA,
-                     &official_path);
-  ASSERT1(SUCCEEDED(hr));
-  ASSERT1(!official_path.IsEmpty());
-
-  for (size_t i = 0; i < all_install_worker_processes.size(); ++i) {
-    CString cmd_line;
-    const uint32 process = all_install_worker_processes[i];
-    if (SUCCEEDED(Process::GetCommandLine(process, &cmd_line))) {
-      cmd_line.MakeLower();
-      // TODO(omaha): FindProcess method does not allow regex's to be specified
-      // along with the include command line. Change Process to allow this.
-      if (cmd_line.Find(needsadmin_arg) != -1) {
-        CORE_LOG(L4, (_T("[Including process][%s]"), cmd_line));
-        processes->push_back(process);
-      }
-
-      // The -Embedding does not have a needsAdmin. Decide whether to include it
-      // if it matches the official path for the requested instance type.
-      CString exe_path;
-      if (cmd_line.Find(kCmdLineComServerDash) != -1 &&
-          SUCCEEDED(GetExePathFromCommandLine(cmd_line, &exe_path)) &&
-          String_StrNCmp(official_path, exe_path, official_path.GetLength(),
-                         true) == 0) {
-        CORE_LOG(L4, (_T("[Including process][%s]"), cmd_line));
-        processes->push_back(process);
-      }
-    }
-  }
-
-  return S_OK;
-}
-
-// The event name saved to the environment variable does not contain the
-// decoration added by GetNamedObjectAttributes.
-HRESULT CreateUniqueEventInEnvironment(const CString& var_name,
-                                       bool is_machine,
-                                       HANDLE* unique_event) {
-  ASSERT1(unique_event);
-
-  GUID event_guid = GUID_NULL;
-  HRESULT hr = ::CoCreateGuid(&event_guid);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[::CoCreateGuid failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  CString event_name(GuidToString(event_guid));
-  NamedObjectAttributes attr;
-  GetNamedObjectAttributes(event_name, is_machine, &attr);
-
-  hr = CreateEvent(&attr, unique_event);
-  if (FAILED(hr)) {
-    CORE_LOG(LW, (_T("[CreateEvent failed in CreateUniqueEventInEnvironment]"),
-                  _T("[%s][0x%08x]"), var_name, hr));
-    return hr;
-  }
-
-  CORE_LOG(L3, (_T("[created unique event][%s][%s]"), var_name, event_name));
-
-  if (!::SetEnvironmentVariable(var_name, event_name)) {
-    DWORD error = ::GetLastError();
-    CORE_LOG(LE, (_T("[::SetEnvironmentVariable failed][%d]"), error));
-    return HRESULT_FROM_WIN32(error);
-  }
-
-  return S_OK;
-}
-
-HRESULT OpenUniqueEventFromEnvironment(const CString& var_name,
-                                       bool is_machine,
-                                       HANDLE* unique_event) {
-  ASSERT1(unique_event);
-
-  TCHAR event_name[MAX_PATH] = {0};
-  if (!::GetEnvironmentVariable(var_name, event_name, arraysize(event_name))) {
-    DWORD error = ::GetLastError();
-    CORE_LOG(LW, (_T("[Failed to read environment variable][%s][%d]"),
-                  var_name, error));
-    return HRESULT_FROM_WIN32(error);
-  }
-
-  CORE_LOG(L3, (_T("[read unique event][%s][%s]"), var_name, event_name));
-
-  NamedObjectAttributes attr;
-  GetNamedObjectAttributes(event_name, is_machine, &attr);
-  *unique_event = ::OpenEvent(EVENT_ALL_ACCESS, false, attr.name);
-
-  if (!*unique_event) {
-    DWORD error = ::GetLastError();
-    CORE_LOG(LW, (_T("[::OpenEvent failed][%s][%d]"), attr.name, error));
-    return HRESULT_FROM_WIN32(error);
-  }
-
-  return S_OK;
-}
-
-// The caller is responsible for reseting the event and closing the handle.
-HRESULT CreateEvent(NamedObjectAttributes* event_attr, HANDLE* event_handle) {
-  ASSERT1(event_handle);
-  ASSERT1(event_attr);
-  ASSERT1(!event_attr->name.IsEmpty());
-  *event_handle = ::CreateEvent(&event_attr->sa,
-                                true,   // manual reset
-                                false,  // not signaled
-                                event_attr->name);
-
-  if (!*event_handle) {
-    DWORD error = ::GetLastError();
-    CORE_LOG(LEVEL_ERROR, (_T("[::SetEvent failed][%d]"), error));
-    return HRESULT_FROM_WIN32(error);
-  }
-
-  return S_OK;
-}
-
-HRESULT ConfigureNetwork(bool is_machine, bool is_local_system) {
-  CORE_LOG(L3, (_T("[goopdate_utils::ConfigureNetwork]")));
-  scoped_handle impersonation_token;
-  if (is_local_system) {
-    // Get an impersonation token corresponding to a primary
-    // token for any of the logged on users.
-    vista::GetLoggedOnUserToken(address(impersonation_token));
-  }
-  NetworkConfig& network_config = NetworkConfig::Instance();
-  HRESULT hr = network_config.Initialize(is_machine, get(impersonation_token));
-  if (FAILED(hr)) {
-    NET_LOG(LE, (_T("[NetworkConfig::Initialize() failed][0x%x]"), hr));
-    return hr;
-  }
-
-  // The NetworkConfig singleton owns the impersonation token.
-  release(impersonation_token);
-
-  // Detecting the default browser requires impersonation so that calling code
-  // running as system is able to detect user proxy settings.
-  HANDLE token = network_config.session().impersonation_token;
-  scoped_impersonation impersonate_user(token);
-  if (token) {
-    DWORD result = impersonate_user.result();
-    ASSERT(result == ERROR_SUCCESS, (_T("impersonation failed %d"), result));
-  }
-  network_config.Add(new GoogleProxyDetector(MACHINE_REG_UPDATE_DEV));
-  BrowserType browser_type(BROWSER_UNKNOWN);
-  GetDefaultBrowserType(&browser_type);
-  if (browser_type == BROWSER_FIREFOX) {
-    network_config.Add(new FirefoxProxyDetector());
-  }
-  // There is no Chrome detector because it uses the same proxy settings as IE.
-  network_config.Add(new IEProxyDetector());
-  network_config.Add(new DefaultProxyDetector);
-
-  // Use a global network configuration override if available.
-  ConfigManager* config_manager = ConfigManager::Instance();
-  CString net_config;
-  if (SUCCEEDED(config_manager->GetNetConfig(&net_config))) {
-    Config configuration_override = NetworkConfig::ParseNetConfig(net_config);
-    network_config.SetConfigurationOverride(&configuration_override);
-  }
-
-  return S_OK;
-}
-
-bool IsTestSource() {
-  return !ConfigManager::Instance()->GetTestSource().IsEmpty();
-}
-
-HRESULT ReadNameValuePairsFromFile(const CString& file_path,
-                                   const CString& group_name,
-                                   std::map<CString, CString>* pairs) {
-  ASSERT1(pairs);
-
-  if (!File::Exists(file_path)) {
-    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
-  }
-
-  pairs->clear();
-
-  TCHAR str_buf[32768] = {0};
-
-  // Retrieve all key names in the section requested.
-  DWORD buf_count = ::GetPrivateProfileString(group_name,
-                                              NULL,
-                                              NULL,
-                                              str_buf,
-                                              arraysize(str_buf),
-                                              file_path);
-
-  DWORD offset = 0;
-  while (offset < buf_count) {
-    TCHAR val_buf[1024] = {0};
-    CString current_key = &(str_buf[offset]);
-    DWORD val_count = ::GetPrivateProfileString(group_name,
-                                                current_key,
-                                                NULL,
-                                                val_buf,
-                                                arraysize(val_buf),
-                                                file_path);
-    (*pairs)[current_key] = val_buf;
-    offset += current_key.GetLength() + 1;
-  }
-
-  return S_OK;
-}
-
-HRESULT WriteNameValuePairsToFile(const CString& file_path,
-                                  const CString& group_name,
-                                  const std::map<CString, CString>& pairs) {
-  std::map<CString, CString>::const_iterator it = pairs.begin();
-  for (; it != pairs.end(); ++it) {
-    if (!::WritePrivateProfileString(group_name,
-                                     it->first,
-                                     it->second,
-                                     file_path)) {
-      return HRESULTFromLastError();
-    }
-  }
-
-  return S_OK;
-}
-
-// Google Update does not have a referral_id. Everything else is the same as for
-// apps.
-HRESULT SetGoogleUpdateBranding(const CString& client_state_key_path,
-                                const CString& brand_code,
-                                const CString& client_id) {
-  HRESULT hr(SetAppBranding(client_state_key_path,
-                            brand_code,
-                            client_id,
-                            CString()));
-
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  RegKey state_key;
-  hr = state_key.Open(client_state_key_path);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // Legacy support for older versions that do not write the FirstInstallTime.
-  // This code ensures that FirstInstallTime always has a valid non-zero value.
-  DWORD install_time(0);
-  if (FAILED(state_key.GetValue(kRegValueInstallTimeSec, &install_time)) ||
-      !install_time) {
-    const DWORD now = Time64ToInt32(GetCurrent100NSTime());
-    VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueInstallTimeSec, now)));
-    SETUP_LOG(L3, (_T("[InstallTime missing. Setting it here.][%u]"), now));
-  }
-
-  return S_OK;
-}
-
-// Branding information is only written if a brand code is not already present.
-// We should only write it if this is the first install of Omaha to avoid giving
-// undue credit to a later installer source. Writing a default brand code
-// prevents future branded installations from setting their brand.
-// As suggested by PSO, there is no default client ID.
-// Assumes the specified Client State key has been created.
-HRESULT SetAppBranding(const CString& client_state_key_path,
-                       const CString& brand_code,
-                       const CString& client_id,
-                       const CString& referral_id) {
-  SETUP_LOG(L3, (_T("[goopdate_utils::SetAppBranding][%s][%s][%s][%s]"),
-                 client_state_key_path, brand_code, client_id, referral_id));
-
-  if (brand_code.GetLength() > kBrandIdLength) {
-    return E_INVALIDARG;
-  }
-
-  RegKey state_key;
-  HRESULT hr = state_key.Open(client_state_key_path);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  CString existing_brand_code;
-  hr = state_key.GetValue(kRegValueBrandCode, &existing_brand_code);
-  if (!existing_brand_code.IsEmpty()) {
-    ASSERT1(SUCCEEDED(hr));
-    if (existing_brand_code.GetLength() > kBrandIdLength) {
-      // Bug 1358852: Brand code garbled with one click.
-      VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueBrandCode,
-                            existing_brand_code.Left(kBrandIdLength))));
-    }
-    return S_OK;
-  }
-
-  const TCHAR* brand_code_to_write = brand_code.IsEmpty() ?
-                                     kDefaultGoogleUpdateBrandCode :
-                                     brand_code;
-  hr = state_key.SetValue(kRegValueBrandCode, brand_code_to_write);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  if (!client_id.IsEmpty()) {
-    hr = state_key.SetValue(kRegValueClientId, client_id);
-    if (FAILED(hr)) {
-      return hr;
-    }
-  }
-
-  if (!referral_id.IsEmpty()) {
-    hr = state_key.SetValue(kRegValueReferralId, referral_id);
-    if (FAILED(hr)) {
-      return hr;
-    }
-  }
-
-  const DWORD now = Time64ToInt32(GetCurrent100NSTime());
-  VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueInstallTimeSec, now)));
-
-  return S_OK;
-}
-
-bool IsAppInstallWorkerRunning(bool is_machine) {
-  CORE_LOG(L3, (_T("[IsAppInstallWorkerRunning][%d]"), is_machine));
-  std::vector<uint32> processes;
-  VERIFY1(SUCCEEDED(GetInstallWorkerProcesses(is_machine, &processes)));
-  return !processes.empty();
-}
-
-// TODO(omaha): needs_admin is only used for one case. Can we avoid it?
-bool IsMachineProcess(CommandLineMode mode,
-                      bool is_running_from_official_machine_directory,
-                      bool is_local_system,
-                      bool is_machine_override,
-                      Tristate needs_admin) {
-  switch (mode) {
-    // These "install" operations may not be running from the installed
-    // location.
-    case COMMANDLINE_MODE_INSTALL:
-    case COMMANDLINE_MODE_IG:
-    case COMMANDLINE_MODE_HANDOFF_INSTALL:
-    case COMMANDLINE_MODE_REGISTER_PRODUCT:
-    case COMMANDLINE_MODE_UNREGISTER_PRODUCT:
-      ASSERT1(TRISTATE_NONE != needs_admin);
-      return TRISTATE_TRUE == needs_admin;
-
-    // The following is a Code Red repair executable, which runs from temp dir.
-    case COMMANDLINE_MODE_RECOVER:
-      return is_machine_override;
-
-    // The following are all user-initiated installs with UI.
-    // They always run as the user for both user and machine installs.
-    // Silent installs for Pack should go here too.
-    case COMMANDLINE_MODE_NOARGS:  // Legacy install
-    case COMMANDLINE_MODE_LEGACYUI:
-    case COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF:
-      return is_running_from_official_machine_directory;
-
-    // The following always runs as the user and may provide UI for on-demand
-    // installs and browser launches.
-    // The install location determines user vs. machine.
-    case COMMANDLINE_MODE_COMSERVER:
-      return is_running_from_official_machine_directory;
-
-    // The following always runs as the user and is user-initiated.
-    case COMMANDLINE_MODE_WEBPLUGIN:
-      // The install location determines user vs. machine.
-      // This may not be the desired value when doing a cross-install or using
-      // the opposite plugin (i.e. user plugin is often used before the machine
-      // one).
-      return is_running_from_official_machine_directory;
-
-    // The following all run silently as the user for user installs or Local
-    // System for machine installs.
-    case COMMANDLINE_MODE_UPDATE:
-    case COMMANDLINE_MODE_UA:
-    case COMMANDLINE_MODE_CODE_RED_CHECK:
-      return is_local_system;
-
-    // The following normally runs silently as the user for user installs or
-    // Local System for machine installs. When launched by recovery it can also
-    // a machine instance can also be running as an elevated admin.
-    case COMMANDLINE_MODE_UG:
-      return is_machine_override || is_local_system;
-
-    // The following runs silently as the user for user installs or Local System
-    // for machine installs.
-    case COMMANDLINE_MODE_CORE:
-    case COMMANDLINE_MODE_CRASH_HANDLER:
-      return is_local_system;
-
-    // The following runs silently as Local System.
-    case COMMANDLINE_MODE_SERVICE:
-      ASSERT1(is_local_system);
-      return is_local_system;
-
-    // The following run as machine for all installs.
-    case COMMANDLINE_MODE_SERVICE_REGISTER:
-    case COMMANDLINE_MODE_SERVICE_UNREGISTER:
-      return true;
-
-    // The crashing process determines whether it was a machine or user omaha
-    // and correctly sets the /machine switch.
-    case COMMANDLINE_MODE_REPORTCRASH:
-      return is_machine_override;
-
-    // The following all run silently as the user for all installs.
-    case COMMANDLINE_MODE_REGSERVER:
-    case COMMANDLINE_MODE_UNREGSERVER:
-      return is_running_from_official_machine_directory;
-
-    // The following are miscellaneous modes that we do not expect to be running
-    // in the wild.
-    case COMMANDLINE_MODE_UNKNOWN:
-    case COMMANDLINE_MODE_NETDIAGS:
-    case COMMANDLINE_MODE_CRASH:
-    default:
-      return is_running_from_official_machine_directory;
-  }
-}
-
-// Returns true if the version does not begin with "1.0." or "1.1.".
-bool IsGoogleUpdate2OrLater(const CString& version) {
-  const ULONGLONG kFirstOmaha2Version = MAKEDLLVERULL(1, 2, 0, 0);
-  ULONGLONG version_number = VersionFromString(version);
-  ASSERT1(0 != version_number);
-
-  if (kFirstOmaha2Version <= version_number) {
-    return true;
-  }
-
-  return false;
-}
-
-bool FormatMessageForNetworkError(HRESULT error,
-                                  const CString app_name,
-                                  CString* msg) {
-  ASSERT1(msg);
-
-  switch (error) {
-    case GOOPDATE_E_NO_NETWORK:
-      msg->FormatMessage(IDS_NO_NETWORK_PRESENT_ERROR,
-                         kGoopdateFileName,
-                         error);
-      break;
-    case GOOPDATE_E_NETWORK_UNAUTHORIZED:
-      msg->FormatMessage(IDS_ERROR_HTTPSTATUS_UNAUTHORIZED, app_name, error);
-      break;
-    case GOOPDATE_E_NETWORK_FORBIDDEN:
-      msg->FormatMessage(IDS_ERROR_HTTPSTATUS_FORBIDDEN, app_name, error);
-      break;
-    case GOOPDATE_E_NETWORK_PROXYAUTHREQUIRED:
-      msg->FormatMessage(IDS_ERROR_HTTPSTATUS_PROXY_AUTH_REQUIRED,
-                         app_name,
-                         error);
-      break;
-    default:
-      msg->FormatMessage(IDS_NO_NETWORK_PRESENT_ERROR,
-                         kGoopdateFileName,
-                         error);
-      return false;
-  }
-
-  return true;
-}
-
-void AddNetworkRequestDataToEventLog(NetworkRequest* network_request,
-                                     HRESULT hr) {
-  ASSERT1(network_request);
-
-  CString msg;
-  msg.Format(_T("Network Request Error.\r\n")
-             _T("Error: 0x%x. Http status code: %d.\r\n%s"),
-             hr, network_request->http_status_code(), network_request->trace());
-
-  LogEvent(static_cast<WORD>(EVENTLOG_ERROR_TYPE), kNetworkRequestEventId, msg);
-}
-
-HRESULT WriteInstallerDataToTempFile(const CString& installer_data,
-                                     CString* installer_data_file_path) {
-  ASSERT1(installer_data_file_path);
-
-  // TODO(omaha): consider eliminating the special case and simply create an
-  // empty file.
-  CORE_LOG(L2, (_T("[WriteInstallerDataToTempFile][data=%s]"), installer_data));
-  if (installer_data.IsEmpty()) {
-    return S_FALSE;
-  }
-
-  CString temp_file;
-  if (!::GetTempFileName(app_util::GetTempDir(),
-                         _T("gui"),
-                         0,
-                         CStrBuf(temp_file, MAX_PATH))) {
-    HRESULT hr = HRESULTFromLastError();
-    CORE_LOG(LE, (_T("[::GetTempFileName failed][0x08%x]"), hr));
-    return hr;
-  }
-
-  scoped_handle file_handle(::CreateFile(temp_file,
-                                         GENERIC_WRITE,
-                                         FILE_SHARE_READ,
-                                         NULL,
-                                         CREATE_ALWAYS,
-                                         FILE_ATTRIBUTE_NORMAL,
-                                         NULL));
-  if (!file_handle) {
-    HRESULT hr = HRESULTFromLastError();
-    CORE_LOG(LE, (_T("[::CreateFile failed][0x08%x]"), hr));
-    return hr;
-  }
-
-  CStringA installer_data_utf8_bom;
-  installer_data_utf8_bom.Format("%c%c%c%s",
-                                 0xEF, 0xBB, 0xBF, WideToUtf8(installer_data));
-  DWORD bytes_written = 0;
-  if (!::WriteFile(get(file_handle),
-                   installer_data_utf8_bom,
-                   installer_data_utf8_bom.GetLength(),
-                   &bytes_written,
-                   NULL)) {
-    HRESULT hr = HRESULTFromLastError();
-    CORE_LOG(LE, (_T("[::WriteFile failed][0x08%x]"), hr));
-    return hr;
-  }
-
-  *installer_data_file_path = temp_file;
-  return S_OK;
-}
-
-HRESULT GetNumClients(bool is_machine, size_t* num_clients) {
-  ASSERT1(num_clients);
-  RegKey reg_key;
-  HKEY root_key = is_machine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
-  HRESULT hr = reg_key.Open(root_key, GOOPDATE_REG_RELATIVE_CLIENTS, KEY_READ);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  DWORD num_subkeys(0);
-  LONG error = ::RegQueryInfoKey(reg_key.Key(), NULL, NULL, NULL, &num_subkeys,
-                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL);
-  if (error != ERROR_SUCCESS) {
-    return HRESULT_FROM_WIN32(error);
-  }
-  *num_clients = num_subkeys;
-  return S_OK;
-}
-
-HRESULT ValidateDownloadedFile(const CString& file_name,
-                               const CString& hash,
-                               uint32 size) {
-  CORE_LOG(L3, (_T("[ValidateDownloadedFile][%s][%s][%u]"),
-                file_name, hash, size));
-  ASSERT1(File::Exists(file_name));
-
-  std::vector<CString> files;
-  files.push_back(file_name);
-  HRESULT hr = AuthenticateFiles(files, hash);
-  if (SUCCEEDED(hr)) {
-    return hr;
-  }
-
-  uint32 file_size(0);
-  if (FAILED(File::GetFileSizeUnopen(file_name, &file_size))) {
-    return hr;
-  }
-
-  if (0 == file_size) {
-    return GOOPDATEDOWNLOAD_E_FILE_SIZE_ZERO;
-  } else if (file_size < size) {
-    return GOOPDATEDOWNLOAD_E_FILE_SIZE_SMALLER;
-  } else if (file_size > size) {
-    return GOOPDATEDOWNLOAD_E_FILE_SIZE_LARGER;
-  }
-
-  ASSERT1(size == file_size);
-  return hr;
-}
-
-// Make sure this is not a silent process before calling this method.
-// Uses primary_app_name in the title if provided, otherwise displays a generic
-// title.
-void DisplayErrorInMessageBox(const CString& error_text,
-                              const CString& primary_app_name) {
-  CString msg_box_title;
-  if (primary_app_name.IsEmpty()) {
-    VERIFY1(msg_box_title.LoadString(IDS_GENERIC_INSTALLER_DISPLAY_NAME));
-  } else {
-    msg_box_title.FormatMessage(IDS_WINDOW_TITLE, primary_app_name);
-  }
-  VERIFY1(IDOK == ::MessageBox(NULL, error_text, msg_box_title, MB_OK));
-}
-
-}  // namespace goopdate_utils
-
-}  // namespace omaha
-
diff --git a/goopdate/goopdate_utils.h b/goopdate/goopdate_utils.h
deleted file mode 100644
index aef30bb..0000000
--- a/goopdate/goopdate_utils.h
+++ /dev/null
@@ -1,407 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Defines: GoopdateUtils, helper functions for goopdate.
-//
-// TODO(omaha): move to namespace.
-
-#ifndef OMAHA_GOOPDATE_GOOPDATE_UTILS_H__
-#define OMAHA_GOOPDATE_GOOPDATE_UTILS_H__
-
-#include <mstask.h>
-#include <oaidl.h>
-#include <atlcomcli.h>
-#include <atlpath.h>
-#include <atlstr.h>
-#include <map>
-#include <vector>
-#include "omaha/common/constants.h"
-#include "omaha/common/shell.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/request.h"
-#include "omaha/goopdate/update_response.h"
-
-struct NamedObjectAttributes;
-
-namespace omaha {
-
-class UpdateResponse;
-class NetworkRequest;
-
-// TODO(omaha): Move all browser related functions into browser_utils.
-// Represents the Result of an attempt to terminate the browser.
-struct TerminateBrowserResult {
-  TerminateBrowserResult()
-      : found(false),
-        could_terminate(false) {
-  }
-
-  TerminateBrowserResult(bool f, bool terminate)
-      : found(f),
-        could_terminate(terminate) {
-  }
-
-  bool found;
-  bool could_terminate;
-};
-
-// Utility functions for goopdate.
-namespace goopdate_utils {
-
-typedef HRESULT (*RegisterOrUnregisterFunction)(bool is_register);
-
-// Returns the application registration path for the specified app.
-CString GetAppClientsKey(bool is_machine, const CString& app_guid);
-
-// Returns the application state path for the specified app.
-CString GetAppClientStateKey(bool is_machine, const CString& app_guid);
-
-// Returns the medium integrity application state path for the specified app.
-CString GetAppClientStateMediumKey(bool is_machine, const CString& app_guid);
-
-// Returns the application state path for a user given the user SID.
-CString GetUserAllAppsStatePath(const CString& user_sid);
-
-// Returns the application registration location given the user SID.
-CString GetUserAllAppsRegPath(const CString& user_sid);
-
-// Returns the application state path for a particular user and for the
-// given application id.
-CString GetUserAppStatePath(const CString& user_sid,
-                            const CString& app_guid);
-
-// Returns the application registration path for a particular user
-// and for the given application id.
-CString GetUserAppRegPath(const CString& user_sid,
-                          const CString& app_guid);
-
-// Builds the directory of the Google Update executable.
-CString BuildGoogleUpdateExeDir(bool is_machine);
-
-// Builds the path of the Google Update version found in the registry. The
-// command line is of the form "<install location>\googleupdate.exe"
-CString BuildGoogleUpdateExePath(bool is_machine);
-
-CString BuildGoogleUpdateServicesPath(bool is_machine);
-
-// Returns true if the currently executing binary is running from the
-// Machine/User Goopdate directory, or a directory under it.
-bool IsRunningFromOfficialGoopdateDir(bool is_machine);
-
-// If running the installed machine instance, returns HKLM. Else returns HKCU.
-CString GetHKRoot();
-
-// Starts an instance of the Google Update version found in the registry.
-// Only use to start interactive processes because it uses ::ShellExecuteEx().
-// args can be NULL.
-// process can be NULL. If not NULL, caller is responsible for closing handle.
-HRESULT StartGoogleUpdateWithArgs(bool is_machine,
-                                  const TCHAR* args,
-                                  HANDLE* process);
-
-// Starts self in an elevated mode using the "Runas" verb.
-HRESULT StartElevatedSelfWithArgsAndWait(const TCHAR* args);
-
-// Registers security and sets the security values for the GoogleUpdate
-// process when running as a COM server.
-HRESULT InitializeSecurity();
-
-// GetProductName is temporary and must be removed after the TT release.
-// Gets the product name for a app guid.
-CString GetProductName(const CString& app_guid);
-
-// Returns true if it is a development or test machine.
-bool IsTestSource();
-
-// Creates the query string based on the passed url and arguments.
-// url must end in a '?' or '&'.
-HRESULT BuildHttpGetString(const CString& url,
-                           DWORD error_code,
-                           DWORD extra_code1,
-                           DWORD extra_code2,
-                           const CString& product_guid,
-                           const CString& goopdate_version,
-                           bool is_machine,
-                           const CString& language,
-                           const GUID& iid,
-                           const CString& brand_code,
-                           const CString& source_id,
-                           CString* get_request);
-
-HRESULT RedirectHKCR(bool is_machine);
-
-HRESULT RemoveRedirectHKCR();
-
-HRESULT RegisterTypeLib(bool is_admin,
-                        const CComBSTR& path,
-                        ITypeLib* type_lib);
-HRESULT UnRegisterTypeLib(bool is_admin,
-                          const CComBSTR&,
-                          ITypeLib* type_lib);
-
-HRESULT RegisterOrUnregisterModule(bool register_server,
-                                   RegisterOrUnregisterFunction registrar);
-
-HRESULT RegisterOrUnregisterModuleWithTypelib(
-    bool register_server,
-    RegisterOrUnregisterFunction registrar);
-
-// Registers the typelib that is passed in.
-// Wrapper for the RegisterTypeLibForUser that is defined in the
-// Vista oleaut32. Uses GetProcAddress to call into the method.
-HRESULT RegisterTypeLibForUser(ITypeLib* lib,
-                               OLECHAR* path,
-                               OLECHAR* help_dir);
-
-// Unregisters the typelib that is passed in.
-// Wrapper for the UnRegisterTypeLibForUser in Vista ole. Uses GetProcAddress
-// to call into the real method.
-HRESULT UnRegisterTypeLibForUser(REFGUID lib_id,
-                                 WORD major_ver_num,
-                                 WORD minor_ver_num,
-                                 LCID lcid,
-                                 SYSKIND syskind);
-
-// Impersonates the user in case of interactive installs, and
-// the logged in user in case of non-interactive updates.
-HRESULT ImpersonateUser(bool is_interactive, uint32 explorer_pid);
-
-// Reverts the calling thread to self if impersonated.
-HRESULT UndoImpersonation(bool impersonated);
-
-// Returns the user's token either from the request or from the logged on
-// user's explorer process.
-HRESULT GetImpersonationToken(bool is_interactive,
-                              uint32 explorer_pid,
-                              HANDLE* out_token);
-
-// Returns whether the EULA is accepted for the app.
-bool IsAppEulaAccepted(bool is_machine,
-                       const CString& app_guid,
-                       bool require_explicit_acceptance);
-
-// Sets eulaaccepted=0 in the app's ClientState.
-HRESULT SetAppEulaNotAccepted(bool is_machine, const CString& app_guid);
-
-// Clears any eulaaccepted=0 values for the app.
-HRESULT ClearAppEulaNotAccepted(bool is_machine, const CString& app_guid);
-
-// Determines whether usage stats are enabled for a specific app.
-bool AreAppUsageStatsEnabled(bool is_machine, const CString& app_guid);
-
-// Configures Omaha's collection of usage stats and crash reports.
-HRESULT SetUsageStatsEnable(bool is_machine,
-                            const CString& app_guid,
-                            Tristate usage_stats_enable);
-
-// Copies legacy usage stats value to the new name and location then deletes
-// the old value.
-// TODO(omaha): Remove along with legacy Omaha 1.
-HRESULT ConvertLegacyUsageStats(bool is_machine);
-
-//
-// Scheduled Task methods.
-
-// This method will return the default scheduled task name. This default value
-// is also used as the prefix for generating unique task names.
-CString GetDefaultGoopdateTaskName(bool is_machine, CommandLineMode mode);
-HRESULT InstallGoopdateTasks(const TCHAR* task_path, bool is_machine);
-HRESULT UninstallGoopdateTasks(bool is_machine);
-HRESULT UninstallLegacyGoopdateTasks(bool is_machine);
-HRESULT StartGoopdateTaskCore(bool is_machine);
-bool IsInstalledGoopdateTaskUA(bool is_machine);
-bool IsDisabledGoopdateTaskUA(bool is_machine);
-HRESULT GetExitCodeGoopdateTaskUA(bool is_machine);
-
-// Gets the specified value from the registry for the application.
-HRESULT GetClientsStringValueFromRegistry(bool is_machine,
-                                          const CString& app_guid,
-                                          const CString& value_name,
-                                          CString* value);
-
-// Gets the application's version from the registry.
-HRESULT GetVerFromRegistry(bool is_machine,
-                           const CString& app_guid,
-                           CString* version);
-
-// Returns the absolute path of the browser image.
-HRESULT GetBrowserImagePathFromProcess(BrowserType type,
-                                       uint32 explorer_pid,
-                                       CString* path);
-
-// Terminates all browser processes for the current user.
-HRESULT TerminateBrowserProcesses(BrowserType type,
-                                  TerminateBrowserResult* browser_res,
-                                  TerminateBrowserResult* default_res);
-
-// Terminates both firefox and IE instances.
-HRESULT TerminateAllBrowsers(BrowserType type,
-                             TerminateBrowserResult* browser_res,
-                             TerminateBrowserResult* default_res);
-
-
-// Starts an instance of the browser. The explorer_pid indicates the user to
-// launch the browser as, in case of machine goopdate.
-HRESULT StartBrowserWithProcessToken(bool is_machine,
-                                     BrowserType type,
-                                     const CString& url,
-                                     uint32 explorer_pid);
-
-// Converts from string to the BrowserType enum.
-HRESULT ConvertStringToBrowserType(const CString& text, BrowserType* type);
-
-// Converts from BrowserType to string.
-CString ConvertBrowserTypeToString(BrowserType type);
-
-// Converts a string to the needs admin enum. Takes care of case.
-HRESULT ConvertStringToNeedsAdmin(const CString& text, NeedsAdmin* admin);
-
-// Converts the NeedsAdmin to a string.
-HRESULT ConvertNeedsAdminToString(NeedsAdmin needs_admin, CString* text);
-
-// Returns the browser to restart.
-bool GetBrowserToRestart(BrowserType type,
-                         BrowserType default_type,
-                         const TerminateBrowserResult& res,
-                         const TerminateBrowserResult& def_res,
-                         BrowserType* browser_type);
-
-// Returns whether the Goopdate service is installed.
-bool IsServiceInstalled();
-
-// Reads the value of the persistent id.
-HRESULT ReadPersistentId(const CString& key_name,
-                         const CString& value_name,
-                         CString* id);
-
-// Obtains the OS version and service pack.
-HRESULT GetOSInfo(CString* os_version, CString* service_pack);
-
-// Returns the install directory for the specified version.
-CPath BuildInstallDirectory(bool is_machine, const CString& version);
-
-// Launches the browser. On Vista and later, for a machine install, this method
-// will launch the browser at medium/low integrity, by impersonating the medium
-// integrity token of the active user.
-HRESULT LaunchBrowser(BrowserType type, const CString& url);
-
-// Launches the command line. On Vista and later, for a machine install, this
-// method will launch the process at medium/low integrity, by impersonating the
-// medium integrity token of the active user.
-HRESULT LaunchCmdLine(const CString& cmd_line);
-
-// Converts the data contained in the response to the extra argument format.
-HRESULT ConvertResponseDataToExtraArgs(const UpdateResponseData& response,
-                                       CString* extra);
-
-// Converts the manifest_filename parameter to extra args and starts
-// an omaha2 install worker from the install directory.
-HRESULT HandleLegacyManifestHandoff(const CString& manifest_filename,
-                                    bool is_machine);
-
-// Gets a list of install worker processes relevant to user/machine instances.
-HRESULT GetInstallWorkerProcesses(bool is_machine,
-                                  std::vector<uint32>* processes);
-
-// Creates a unique event name and stores it in the specified environment var.
-HRESULT CreateUniqueEventInEnvironment(const CString& var_name,
-                                       bool is_machine,
-                                       HANDLE* unique_event);
-
-// Obtains a unique event name from specified environment var and opens it.
-HRESULT OpenUniqueEventFromEnvironment(const CString& var_name,
-                                       bool is_machine,
-                                       HANDLE* unique_event);
-
-// Creates an event based on the provided attributes.
-HRESULT CreateEvent(NamedObjectAttributes* event_attr, HANDLE* event_handle);
-
-// Initializes the network stack and adds the proxy detectors.
-HRESULT ConfigureNetwork(bool is_machine, bool is_local_system);
-
-HRESULT ReadNameValuePairsFromFile(const CString& file_path,
-                                   const CString& group_name,
-                                   std::map<CString, CString>* pairs);
-
-HRESULT WriteNameValuePairsToFile(const CString& file_path,
-                                  const CString& group_name,
-                                  const std::map<CString, CString>& pairs);
-
-// Writes branding information for Google Update in the registry if it does not
-// already exist. Otherwise, the information remains unchanged.
-// Writes a default Omaha-specific brand code if one is not specified in args.
-HRESULT SetGoogleUpdateBranding(const CString& client_state_key_path,
-                                const CString& brand_code,
-                                const CString& client_id);
-
-
-// Writes branding information for apps in the registry if it does not
-// already exist. Otherwise, the information remains unchanged.
-// Writes a default Omaha-specific brand code if one is not specified in args.
-HRESULT SetAppBranding(const CString& client_state_key_path,
-                       const CString& brand_code,
-                       const CString& client_id,
-                       const CString& referral_id);
-
-// Returns true is any of the install workers is running.
-bool IsAppInstallWorkerRunning(bool is_machine);
-
-// Returns whether a process is a machine process.
-// Does not determine whether the process has the appropriate privileges.
-bool IsMachineProcess(
-    CommandLineMode mode,
-    bool is_running_from_official_machine_directory,
-    bool is_local_system,  // Whether process is running as local system.
-    bool is_machine_override,  // True if machine cmd line override specified.
-    Tristate needs_admin);  // needsadmin value for primary app if present.
-
-// Returns whether the version is an "Omaha 2" version or later.
-bool IsGoogleUpdate2OrLater(const CString& version);
-
-// Formats an error message for network errors. Returns true if the error has
-// a specific message. Otherwise, formats a generic network connection message
-// and returns false.
-bool FormatMessageForNetworkError(HRESULT error,
-                                  const CString app_name,
-                                  CString* msg);
-
-// Adds details about the network_request to the event log as an error.
-void AddNetworkRequestDataToEventLog(NetworkRequest* network_request,
-                                     HRESULT hr);
-
-// Converts the installer_data value to UTF8. Then writes this UTF8 data
-// prefixed with the UTF8 BOM of EF BB BF to a temp file. Returns the path to
-// temp file that was created.  The returned path will be quote-enclosed by
-// EnclosePath().
-HRESULT WriteInstallerDataToTempFile(const CString& installer_data,
-                                     CString* installer_data_file_path);
-
-// Returns the number of clients registered under the "Clients" sub key.
-HRESULT GetNumClients(bool is_machine, size_t* num_clients);
-
-// Validates the hash of the file. If the hash does not match, checks the size
-// to determine whether the problem was corruption or file size.
-HRESULT ValidateDownloadedFile(const CString& file_name,
-                               const CString& hash,
-                               uint32 size);
-
-void DisplayErrorInMessageBox(const CString& error_text,
-                              const CString& primary_app_name);
-
-}  // namespace goopdate_utils
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_GOOPDATE_UTILS_H__
diff --git a/goopdate/goopdate_utils_unittest.cc b/goopdate/goopdate_utils_unittest.cc
deleted file mode 100644
index 7c29056..0000000
--- a/goopdate/goopdate_utils_unittest.cc
+++ /dev/null
@@ -1,4614 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <windows.h>
-#include <atlpath.h>
-#include <atlsecurity.h>
-#include <atlstr.h>
-#include <map>
-#include <vector>
-#include "omaha/common/app_util.h"
-#include "omaha/common/browser_utils.h"
-#include "omaha/common/const_utils.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/path.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scoped_ptr_cotask.h"
-#include "omaha/common/string.h"
-#include "omaha/common/time.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/common/vista_utils.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/extra_args_parser.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/goopdate_utils-internal.h"
-#include "omaha/testing/resource.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-namespace {
-
-#define DUMMY_CLSID  _T("{6FC94136-0D4C-450e-99C2-BCDA72A9C8F0}")
-const TCHAR* hkcr_key_name = _T("HKCR\\CLSID\\") DUMMY_CLSID;
-const TCHAR* hklm_key_name = _T("HKLM\\Software\\Classes\\CLSID\\")
-                             DUMMY_CLSID;
-const TCHAR* hkcu_key_name = _T("HKCU\\Software\\Classes\\CLSID\\")
-                             DUMMY_CLSID;
-
-#define APP_GUID _T("{B7BAF788-9D64-49c3-AFDC-B336AB12F332}")
-const TCHAR* const kAppGuid = APP_GUID;
-const TCHAR* const kAppMachineClientStatePath =
-    _T("HKLM\\Software\\Google\\Update\\ClientState\\") APP_GUID;
-const TCHAR* const kAppUserClientStatePath =
-    _T("HKCU\\Software\\Google\\Update\\ClientState\\") APP_GUID;
-const TCHAR* const kAppMachineClientStateMediumPath =
-    _T("HKLM\\Software\\Google\\Update\\ClientStateMedium\\") APP_GUID;
-
-// This should never exist. This contant is only used to verify it is not used.
-const TCHAR* const kAppUserClientStateMediumPath =
-    _T("HKCU\\Software\\Google\\Update\\ClientStateMedium\\") APP_GUID;
-
-// Constants used in the scheduled task tests.
-const int kMaxWaitForProcessMs              = 60000;
-const int kWaitForProcessIntervalMs         = 100;
-const int kMaxWaitForProcessIterations      =
-    kMaxWaitForProcessMs / kWaitForProcessIntervalMs;
-
-// Verifies that one of the expected OS strings was found in the url.
-// Returns the position along with the length of the OS string.
-int VerifyOSInUrl(const CString& url, int* length) {
-  ASSERT1(length);
-  *length = 0;
-
-  // The strings are in descending version order to avoid breaking on a
-  // substring of the version we are looking for.
-  const TCHAR* kExpectedOsStrings[] = {_T("6.0&sp=Service%20Pack%201"),
-                                       _T("6.0&sp="),
-                                       _T("5.2&sp=Service%20Pack%202"),
-                                       _T("5.2&sp=Service%20Pack%201"),
-                                       _T("5.1&sp=Service%20Pack%203"),
-                                       _T("5.1&sp=Service%20Pack%202"),
-                                      };
-
-  bool found = false;
-  int this_pos = 0;
-
-  for (int i = 0; i < arraysize(kExpectedOsStrings); ++i) {
-    this_pos = url.Find(kExpectedOsStrings[i]);
-    if (-1 != this_pos) {
-      found = true;
-      *length = _tcslen(kExpectedOsStrings[i]);
-      break;
-    }
-  }
-
-  EXPECT_TRUE(found);
-  return this_pos;
-}
-
-}  // namespace
-
-namespace goopdate_utils {
-
-namespace internal {
-
-TEST(GoopdateUtilsTest, ScheduledTasks) {
-  const TCHAR kSchedTestTaskName[]            = _T("TestScheduledTask");
-  const TCHAR kScheduledTaskExecutable[]      = _T("netstat.exe");
-  const TCHAR kScheduledTaskParameters[]      = _T("20");
-  const TCHAR kSchedTestTaskComment[]         = _T("Google Test Task");
-
-  CString task_path = ConcatenatePath(app_util::GetSystemDir(),
-                                      kScheduledTaskExecutable);
-  // Install/uninstall.
-  EXPECT_SUCCEEDED(InstallScheduledTask(kSchedTestTaskName,
-                                        task_path,
-                                        _T(""),
-                                        kSchedTestTaskComment,
-                                        vista_util::IsUserAdmin(),
-                                        vista_util::IsUserAdmin(),
-                                        true,
-                                        true));
-  EXPECT_SUCCEEDED(UninstallScheduledTask(kSchedTestTaskName));
-
-  // Calling InstallScheduledTask twice should succeed.
-  for (int i = 0; i < 2; ++i) {
-    EXPECT_SUCCEEDED(InstallScheduledTask(kSchedTestTaskName,
-                                          task_path,
-                                          _T(""),
-                                          kSchedTestTaskComment,
-                                          vista_util::IsUserAdmin(),
-                                          vista_util::IsUserAdmin(),
-                                          true,
-                                          true));
-  }
-
-  // "Upgrade" to a new version, which now has parameters.
-  EXPECT_SUCCEEDED(InstallScheduledTask(kSchedTestTaskName,
-                                        task_path,
-                                        kScheduledTaskParameters,
-                                        kSchedTestTaskComment,
-                                        vista_util::IsUserAdmin(),
-                                        vista_util::IsUserAdmin(),
-                                        true,
-                                        true));
-
-  // Start and stop.
-  EXPECT_SUCCEEDED(StartScheduledTask(kSchedTestTaskName));
-  HRESULT hr = SCHED_S_TASK_HAS_NOT_RUN;
-  int tries = 0;
-  for (tries = 0;
-       tries < kMaxWaitForProcessIterations && SCHED_S_TASK_RUNNING != hr;
-       ++tries) {
-    EXPECT_EQ(SCHED_S_TASK_HAS_NOT_RUN, hr);  // For debugging test failures.
-    ::Sleep(kWaitForProcessIntervalMs);
-    hr = GetScheduledTaskStatus(kSchedTestTaskName);
-  }
-  EXPECT_EQ(SCHED_S_TASK_RUNNING, hr);
-  EXPECT_LT(tries, kMaxWaitForProcessIterations) << _T("Loops exhausted.");
-  std::wcout << _T("\tLine ") << __LINE__ << _T(": Iterations=") << tries
-             << std::endl;
-
-  // Without a wait in between the start and stop, stop failed intermittently.
-  ::Sleep(500);
-
-  EXPECT_SUCCEEDED(StopScheduledTask(kSchedTestTaskName));
-  hr = SCHED_S_TASK_RUNNING;
-  for (tries = 0;
-       tries < kMaxWaitForProcessIterations && SCHED_S_TASK_RUNNING == hr;
-       ++tries) {
-    ::Sleep(kWaitForProcessIntervalMs);
-    hr = GetScheduledTaskStatus(kSchedTestTaskName);
-  }
-  EXPECT_NE(SCHED_S_TASK_RUNNING, hr);
-  EXPECT_LT(tries, kMaxWaitForProcessIterations) << _T("Loops exhausted.");
-  std::wcout << _T("\tLine ") << __LINE__ << _T(": Iterations=") << tries
-             << std::endl;
-  EXPECT_EQ(SCHED_S_TASK_READY, hr);
-
-  // Finally, uninstall.
-  EXPECT_SUCCEEDED(UninstallScheduledTask(kSchedTestTaskName));
-}
-
-}  // namespace internal
-
-static void Cleanup() {
-  ASSERT_SUCCEEDED(goopdate_utils::RemoveRedirectHKCR());
-
-  RegKey::DeleteKey(hkcr_key_name, true);
-  RegKey::DeleteKey(hklm_key_name, true);
-  RegKey::DeleteKey(hkcu_key_name, true);
-}
-
-static void TestGetBrowserToRestart(BrowserType stamped,
-                                    bool found1,
-                                    bool killed1,
-                                    BrowserType def_browser,
-                                    bool found2,
-                                    bool killed2,
-                                    BrowserType expected) {
-  TerminateBrowserResult res(found1, killed1);
-  TerminateBrowserResult def(found2, killed2);
-
-  BrowserType type = BROWSER_UNKNOWN;
-  if (expected == BROWSER_UNKNOWN) {
-    EXPECT_FALSE(goopdate_utils::GetBrowserToRestart(stamped,
-                                                     def_browser,
-                                                     res,
-                                                     def,
-                                                     &type))
-        << _T("stamped: ") << stamped << _T(" ") << found1 << _T(" ") << killed1
-        << _T("   default: ") << def_browser << _T(" ") << found2 << _T(" ")
-        << killed2;
-  } else {
-    EXPECT_TRUE(goopdate_utils::GetBrowserToRestart(stamped,
-                                                    def_browser,
-                                                    res,
-                                                    def,
-                                                    &type))
-        << _T("stamped: ") << stamped << _T(" ") << found1 << _T(" ") << killed1
-        << _T("   default: ") << def_browser << _T(" ") << found2 << _T(" ")
-        << killed2;
-  }
-  EXPECT_EQ(expected, type)
-      << _T("stamped: ") << stamped << _T(" ") << found1 << _T(" ") << killed1
-      << _T("   default: ") << def_browser << _T(" ") << found2 << _T(" ")
-      << killed2;
-}
-
-TEST(GoopdateUtilsTest, GetAppClientsKey) {
-  const TCHAR kAppGuid[] = _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}");
-
-  EXPECT_STREQ(_T("HKCU\\Software\\Google\\Update\\Clients\\")
-               _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"),
-               goopdate_utils::GetAppClientsKey(false, kAppGuid));
-  EXPECT_STREQ(_T("HKLM\\Software\\Google\\Update\\Clients\\")
-               _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"),
-               goopdate_utils::GetAppClientsKey(true, kAppGuid));
-}
-
-TEST(GoopdateUtilsTest, GetAppClientStateKey) {
-  const TCHAR kAppGuid[] = _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}");
-
-  EXPECT_STREQ(_T("HKCU\\Software\\Google\\Update\\ClientState\\")
-               _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"),
-               goopdate_utils::GetAppClientStateKey(false, kAppGuid));
-  EXPECT_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientState\\")
-               _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"),
-               goopdate_utils::GetAppClientStateKey(true, kAppGuid));
-}
-
-// This is an invalid case and causes an assert. Always returns HKLM path.
-TEST(GoopdateUtilsTest, GetAppClientStateMediumKey_User) {
-  const TCHAR kAppGuid[] = _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}");
-  ExpectAsserts expect_asserts;
-  EXPECT_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientStateMedium\\")
-               _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"),
-               goopdate_utils::GetAppClientStateMediumKey(false, kAppGuid));
-}
-
-TEST(GoopdateUtilsTest, GetAppClientStateMediumKey_Machine) {
-  const TCHAR kAppGuid[] = _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}");
-  EXPECT_STREQ(_T("HKLM\\Software\\Google\\Update\\ClientStateMedium\\")
-               _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}"),
-               goopdate_utils::GetAppClientStateMediumKey(true, kAppGuid));
-}
-
-// This is an invalid case and causes an assert.
-TEST(GoopdateUtilsTest, GetAppClientStateMediumKey_UserAndMachineAreSame) {
-  const TCHAR kAppGuid[] = _T("{F998D7E0-0CD3-434e-96B9-B8D3A295C3FB}");
-  ExpectAsserts expect_asserts;
-  EXPECT_STREQ(goopdate_utils::GetAppClientStateMediumKey(true, kAppGuid),
-               goopdate_utils::GetAppClientStateMediumKey(false, kAppGuid));
-}
-
-TEST(GoopdateUtilsTest, TerminateAllBrowsers_BrowserUnknown) {
-  TerminateBrowserResult browser_res;
-  TerminateBrowserResult default_res;
-  ExpectAsserts expect_asserts;
-  EXPECT_EQ(E_INVALIDARG, goopdate_utils::TerminateAllBrowsers(BROWSER_UNKNOWN,
-                                                               &browser_res,
-                                                               &default_res));
-}
-
-TEST(GoopdateUtilsTest, TerminateAllBrowsers_BrowserDefault) {
-  TerminateBrowserResult browser_res;
-  TerminateBrowserResult default_res;
-  ExpectAsserts expect_asserts;
-  EXPECT_EQ(E_INVALIDARG, goopdate_utils::TerminateAllBrowsers(BROWSER_DEFAULT,
-                                                               &browser_res,
-                                                               &default_res));
-}
-
-TEST(GoopdateUtilsTest, TerminateAllBrowsers_BrowserMax) {
-  TerminateBrowserResult browser_res;
-  TerminateBrowserResult default_res;
-  ExpectAsserts expect_asserts;
-  EXPECT_EQ(E_INVALIDARG, goopdate_utils::TerminateAllBrowsers(BROWSER_MAX,
-                                                               &browser_res,
-                                                               &default_res));
-}
-
-// TerminateAllBrowsers is not tested with valid browser values because the
-// tests would terminate developers' browsers.
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_StampedUnknown) {
-  ExpectAsserts expect_asserts;
-  TestGetBrowserToRestart(BROWSER_UNKNOWN, false, false,
-                          BROWSER_IE, false, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_UNKNOWN, true, false,
-                          BROWSER_IE, false, false,
-                          BROWSER_IE);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_DefaultUnknown) {
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_UNKNOWN, false, false,
-                          BROWSER_UNKNOWN);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_StampedAndDefaultUnknown) {
-  ExpectAsserts expect_asserts;
-  TestGetBrowserToRestart(BROWSER_UNKNOWN, false, false,
-                          BROWSER_UNKNOWN, false, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_UNKNOWN, true, false,
-                          BROWSER_UNKNOWN, false, false,
-                          BROWSER_UNKNOWN);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_StampedDefault) {
-  ExpectAsserts expect_asserts;
-  TestGetBrowserToRestart(BROWSER_DEFAULT, false, false,
-                          BROWSER_IE, false, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_DEFAULT, true, false,
-                          BROWSER_IE, false, false,
-                          BROWSER_IE);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_DefaultDefault) {
-  ExpectAsserts expect_asserts;
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_DEFAULT, false, false,
-                          BROWSER_UNKNOWN);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_StampedAndDefaultDefault) {
-  ExpectAsserts expect_asserts;
-  TestGetBrowserToRestart(BROWSER_DEFAULT, false, false,
-                          BROWSER_DEFAULT, false, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_DEFAULT, true, false,
-                          BROWSER_DEFAULT, false, false,
-                          BROWSER_UNKNOWN);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_StampedMax) {
-  ExpectAsserts expect_asserts;
-  TestGetBrowserToRestart(BROWSER_MAX, false, false,
-                          BROWSER_IE, false, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_MAX, true, false,
-                          BROWSER_IE, false, false,
-                          BROWSER_IE);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_DefaultMax) {
-  ExpectAsserts expect_asserts;
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_MAX, false, false,
-                          BROWSER_UNKNOWN);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_StampedAndDefaultMax) {
-  ExpectAsserts expect_asserts;
-  TestGetBrowserToRestart(BROWSER_MAX, false, false,
-                          BROWSER_MAX, false, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_MAX, true, false,
-                          BROWSER_MAX, false, false,
-                          BROWSER_UNKNOWN);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeIE_DefaultIE) {
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_IE, false, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_IE, false, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_IE, true, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_IE, true, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, false, true,
-                          BROWSER_IE, false, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, false, true,
-                          BROWSER_IE, false, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, false, true,
-                          BROWSER_IE, true, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, false, true,
-                          BROWSER_IE, true, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, false,
-                          BROWSER_IE, false, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, false,
-                          BROWSER_IE, false, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, false,
-                          BROWSER_IE, true, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, false,
-                          BROWSER_IE, true, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, true,
-                          BROWSER_IE, false, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, true,
-                          BROWSER_IE, false, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, true,
-                          BROWSER_IE, true, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, true,
-                          BROWSER_IE, true, true,
-                          BROWSER_IE);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeIE_DefaultFirefox) {
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_FIREFOX, false, false,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_FIREFOX, false, true,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_FIREFOX, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_FIREFOX, true, true,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_IE, false, true,
-                          BROWSER_FIREFOX, false, false,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_IE, false, true,
-                          BROWSER_FIREFOX, false, true,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_IE, false, true,
-                          BROWSER_FIREFOX, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_IE, false, true,
-                          BROWSER_FIREFOX, true, true,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_IE, true, false,
-                          BROWSER_FIREFOX, false, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, false,
-                          BROWSER_FIREFOX, false, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, false,
-                          BROWSER_FIREFOX, true, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, false,
-                          BROWSER_FIREFOX, true, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, true,
-                          BROWSER_FIREFOX, false, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, true,
-                          BROWSER_FIREFOX, false, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, true,
-                          BROWSER_FIREFOX, true, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, true,
-                          BROWSER_FIREFOX, true, true,
-                          BROWSER_IE);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeIE_DefaultChrome) {
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_CHROME, false, false,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_CHROME, false, true,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_CHROME, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_CHROME, true, true,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_IE, false, true,
-                          BROWSER_CHROME, false, false,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_IE, false, true,
-                          BROWSER_CHROME, false, true,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_IE, false, true,
-                          BROWSER_CHROME, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_IE, false, true,
-                          BROWSER_CHROME, true, true,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_IE, true, false,
-                          BROWSER_CHROME, false, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, false,
-                          BROWSER_CHROME, false, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, false,
-                          BROWSER_CHROME, true, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, false,
-                          BROWSER_CHROME, true, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, true,
-                          BROWSER_CHROME, false, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, true,
-                          BROWSER_CHROME, false, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, true,
-                          BROWSER_CHROME, true, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, true,
-                          BROWSER_CHROME, true, true,
-                          BROWSER_IE);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeIE_DefaultUnknown) {
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_UNKNOWN, false, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_UNKNOWN, false, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_UNKNOWN, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_IE, false, false,
-                          BROWSER_UNKNOWN, true, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_IE, false, true,
-                          BROWSER_UNKNOWN, false, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_IE, false, true,
-                          BROWSER_UNKNOWN, false, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_IE, false, true,
-                          BROWSER_UNKNOWN, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_IE, false, true,
-                          BROWSER_UNKNOWN, true, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_IE, true, false,
-                          BROWSER_UNKNOWN, false, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, false,
-                          BROWSER_UNKNOWN, false, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, false,
-                          BROWSER_UNKNOWN, true, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, false,
-                          BROWSER_UNKNOWN, true, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, true,
-                          BROWSER_UNKNOWN, false, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, true,
-                          BROWSER_UNKNOWN, false, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, true,
-                          BROWSER_UNKNOWN, true, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_IE, true, true,
-                          BROWSER_UNKNOWN, true, true,
-                          BROWSER_IE);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeFirefox_DefaultIE) {
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
-                          BROWSER_IE, false, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
-                          BROWSER_IE, false, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
-                          BROWSER_IE, true, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
-                          BROWSER_IE, true, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
-                          BROWSER_IE, false, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
-                          BROWSER_IE, false, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
-                          BROWSER_IE, true, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
-                          BROWSER_IE, true, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
-                          BROWSER_IE, false, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
-                          BROWSER_IE, false, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
-                          BROWSER_IE, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
-                          BROWSER_IE, true, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
-                          BROWSER_IE, false, false,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
-                          BROWSER_IE, false, true,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
-                          BROWSER_IE, true, false,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
-                          BROWSER_IE, true, true,
-                          BROWSER_FIREFOX);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeFirefox_DefaultFirefox) {
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
-                          BROWSER_FIREFOX, false, false,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
-                          BROWSER_FIREFOX, false, true,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
-                          BROWSER_FIREFOX, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
-                          BROWSER_FIREFOX, true, true,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
-                          BROWSER_FIREFOX, false, false,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
-                          BROWSER_FIREFOX, false, true,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
-                          BROWSER_FIREFOX, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
-                          BROWSER_FIREFOX, true, true,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
-                          BROWSER_FIREFOX, false, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
-                          BROWSER_FIREFOX, false, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
-                          BROWSER_FIREFOX, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
-                          BROWSER_FIREFOX, true, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
-                          BROWSER_FIREFOX, false, false,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
-                          BROWSER_FIREFOX, false, true,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
-                          BROWSER_FIREFOX, true, false,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
-                          BROWSER_FIREFOX, true, true,
-                          BROWSER_FIREFOX);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeFirefox_DefaultChrome) {
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
-                          BROWSER_CHROME, false, false,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
-                          BROWSER_CHROME, false, true,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
-                          BROWSER_CHROME, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
-                          BROWSER_CHROME, true, true,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
-                          BROWSER_CHROME, false, false,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
-                          BROWSER_CHROME, false, true,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
-                          BROWSER_CHROME, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
-                          BROWSER_CHROME, true, true,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
-                          BROWSER_CHROME, false, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
-                          BROWSER_CHROME, false, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
-                          BROWSER_CHROME, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
-                          BROWSER_CHROME, true, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
-                          BROWSER_CHROME, false, false,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
-                          BROWSER_CHROME, false, true,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
-                          BROWSER_CHROME, true, false,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
-                          BROWSER_CHROME, true, true,
-                          BROWSER_FIREFOX);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeFirefox_DefaultUnknown) {
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
-                          BROWSER_UNKNOWN, false, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
-                          BROWSER_UNKNOWN, false, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
-                          BROWSER_UNKNOWN, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, false,
-                          BROWSER_UNKNOWN, true, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
-                          BROWSER_UNKNOWN, false, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
-                          BROWSER_UNKNOWN, false, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
-                          BROWSER_UNKNOWN, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, false, true,
-                          BROWSER_UNKNOWN, true, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
-                          BROWSER_UNKNOWN, false, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
-                          BROWSER_UNKNOWN, false, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
-                          BROWSER_UNKNOWN, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, false,
-                          BROWSER_UNKNOWN, true, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
-                          BROWSER_UNKNOWN, false, false,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
-                          BROWSER_UNKNOWN, false, true,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
-                          BROWSER_UNKNOWN, true, false,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_FIREFOX, true, true,
-                          BROWSER_UNKNOWN, true, true,
-                          BROWSER_FIREFOX);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeChrome_DefaultIE) {
-  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
-                          BROWSER_IE, false, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
-                          BROWSER_IE, false, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
-                          BROWSER_IE, true, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
-                          BROWSER_IE, true, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
-                          BROWSER_IE, false, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
-                          BROWSER_IE, false, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
-                          BROWSER_IE, true, false,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
-                          BROWSER_IE, true, true,
-                          BROWSER_IE);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
-                          BROWSER_IE, false, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
-                          BROWSER_IE, false, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
-                          BROWSER_IE, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
-                          BROWSER_IE, true, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
-                          BROWSER_IE, false, false,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
-                          BROWSER_IE, false, true,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
-                          BROWSER_IE, true, false,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
-                          BROWSER_IE, true, true,
-                          BROWSER_CHROME);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeChrome_DefaultFirefox) {
-  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
-                          BROWSER_FIREFOX, false, false,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
-                          BROWSER_FIREFOX, false, true,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
-                          BROWSER_FIREFOX, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
-                          BROWSER_FIREFOX, true, true,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
-                          BROWSER_FIREFOX, false, false,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
-                          BROWSER_FIREFOX, false, true,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
-                          BROWSER_FIREFOX, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
-                          BROWSER_FIREFOX, true, true,
-                          BROWSER_FIREFOX);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
-                          BROWSER_FIREFOX, false, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
-                          BROWSER_FIREFOX, false, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
-                          BROWSER_FIREFOX, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
-                          BROWSER_FIREFOX, true, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
-                          BROWSER_FIREFOX, false, false,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
-                          BROWSER_FIREFOX, false, true,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
-                          BROWSER_FIREFOX, true, false,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
-                          BROWSER_FIREFOX, true, true,
-                          BROWSER_CHROME);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeChrome_DefaultChrome) {
-  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
-                          BROWSER_CHROME, false, false,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
-                          BROWSER_CHROME, false, true,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
-                          BROWSER_CHROME, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
-                          BROWSER_CHROME, true, true,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
-                          BROWSER_CHROME, false, false,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
-                          BROWSER_CHROME, false, true,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
-                          BROWSER_CHROME, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
-                          BROWSER_CHROME, true, true,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
-                          BROWSER_CHROME, false, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
-                          BROWSER_CHROME, false, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
-                          BROWSER_CHROME, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
-                          BROWSER_CHROME, true, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
-                          BROWSER_CHROME, false, false,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
-                          BROWSER_CHROME, false, true,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
-                          BROWSER_CHROME, true, false,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
-                          BROWSER_CHROME, true, true,
-                          BROWSER_CHROME);
-}
-
-TEST(GoopdateUtilsTest, GetBrowserToRestart_TypeChrome_DefaultUnknown) {
-  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
-                          BROWSER_UNKNOWN, false, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
-                          BROWSER_UNKNOWN, false, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
-                          BROWSER_UNKNOWN, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, false,
-                          BROWSER_UNKNOWN, true, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
-                          BROWSER_UNKNOWN, false, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
-                          BROWSER_UNKNOWN, false, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
-                          BROWSER_UNKNOWN, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, false, true,
-                          BROWSER_UNKNOWN, true, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
-                          BROWSER_UNKNOWN, false, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
-                          BROWSER_UNKNOWN, false, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
-                          BROWSER_UNKNOWN, true, false,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, false,
-                          BROWSER_UNKNOWN, true, true,
-                          BROWSER_UNKNOWN);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
-                          BROWSER_UNKNOWN, false, false,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
-                          BROWSER_UNKNOWN, false, true,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
-                          BROWSER_UNKNOWN, true, false,
-                          BROWSER_CHROME);
-  TestGetBrowserToRestart(BROWSER_CHROME, true, true,
-                          BROWSER_UNKNOWN, true, true,
-                          BROWSER_CHROME);
-}
-
-TEST(GoopdateUtilsTest, ConvertStringToBrowserType) {
-  BrowserType type = BROWSER_UNKNOWN;
-  ASSERT_SUCCEEDED(goopdate_utils::ConvertStringToBrowserType(_T("0"), &type));
-  ASSERT_EQ(BROWSER_UNKNOWN, type);
-
-  ASSERT_SUCCEEDED(goopdate_utils::ConvertStringToBrowserType(_T("1"), &type));
-  ASSERT_EQ(BROWSER_DEFAULT, type);
-
-  ASSERT_SUCCEEDED(goopdate_utils::ConvertStringToBrowserType(_T("2"), &type));
-  ASSERT_EQ(BROWSER_IE, type);
-
-  ASSERT_SUCCEEDED(goopdate_utils::ConvertStringToBrowserType(_T("3"), &type));
-  ASSERT_EQ(BROWSER_FIREFOX, type);
-
-  ASSERT_SUCCEEDED(goopdate_utils::ConvertStringToBrowserType(_T("4"), &type));
-  ASSERT_EQ(BROWSER_CHROME, type);
-
-  ASSERT_FAILED(goopdate_utils::ConvertStringToBrowserType(_T("5"), &type));
-  ASSERT_FAILED(goopdate_utils::ConvertStringToBrowserType(_T("asdf"), &type));
-  ASSERT_FAILED(goopdate_utils::ConvertStringToBrowserType(_T("234"), &type));
-  ASSERT_FAILED(goopdate_utils::ConvertStringToBrowserType(_T("-1"), &type));
-}
-
-TEST(GoopdateUtilsTest, RedirectHKCRTest) {
-  RegKey key;
-  Cleanup();
-
-  if (vista_util::IsUserAdmin()) {
-    // Only run this part of the test for Admins, because non-admins cannot
-    // write to HKLM.
-
-    // Without redirection, a HKCR write should write HKLM\Software\Classes,
-    // assuming that the key does not already exist in HKCU.
-    ASSERT_SUCCEEDED(key.Create(hkcr_key_name));
-    ASSERT_TRUE(RegKey::HasKey(hklm_key_name));
-    ASSERT_FALSE(RegKey::HasKey(hkcu_key_name));
-
-    Cleanup();
-
-    ASSERT_SUCCEEDED(goopdate_utils::RedirectHKCR(true));
-
-    // With HKLM redirection, a HKCR write should write HKLM\Software\Classes.
-    ASSERT_SUCCEEDED(key.Create(hkcr_key_name));
-    ASSERT_TRUE(RegKey::HasKey(hklm_key_name));
-    ASSERT_FALSE(RegKey::HasKey(hkcu_key_name));
-
-    Cleanup();
-  } else {
-    std::wcout << _T("\tPart of this test did not run because the user ")
-                  _T("is not an admin.") << std::endl;
-  }
-
-  ASSERT_SUCCEEDED(goopdate_utils::RedirectHKCR(false));
-
-  // With HKCU redirection, a HKCR write should write HKCU\Software\Classes.
-  ASSERT_SUCCEEDED(key.Create(hkcr_key_name));
-  ASSERT_FALSE(RegKey::HasKey(hklm_key_name));
-  ASSERT_TRUE(RegKey::HasKey(hkcu_key_name));
-
-  ASSERT_SUCCEEDED(goopdate_utils::RemoveRedirectHKCR());
-
-  if (vista_util::IsUserAdmin()) {
-    // Without redirection, the following HKCR writes should write
-    // HKCU\Software\Classes.
-    // This is because the key already exists in HKCU from the writes above.
-    ASSERT_SUCCEEDED(key.Create(hkcr_key_name));
-    ASSERT_FALSE(RegKey::HasKey(hklm_key_name));
-    ASSERT_TRUE(RegKey::HasKey(hkcu_key_name));
-  } else {
-    std::wcout << _T("\tPart of this test did not run because the user ")
-                  _T("is not an admin.") << std::endl;
-  }
-
-  Cleanup();
-}
-
-TEST(GoopdateUtilsTest, GetImpersonationTokenNotInteractivePid0) {
-  HANDLE token = NULL;
-  ASSERT_SUCCEEDED(goopdate_utils::GetImpersonationToken(false, 0, &token));
-  ASSERT_TRUE(token != NULL);
-  ASSERT_TRUE(::CloseHandle(token));
-  token = NULL;
-}
-
-TEST(GoopdateUtilsTest, GetImpersonationTokenNotInteractiveRealPid) {
-  HANDLE token = NULL;
-  DWORD pid = ::GetCurrentProcessId();
-  ASSERT_SUCCEEDED(goopdate_utils::GetImpersonationToken(false, pid, &token));
-  ASSERT_TRUE(token != NULL);
-  ASSERT_TRUE(::CloseHandle(token));
-  token = NULL;
-}
-
-TEST(GoopdateUtilsTest, GetImpersonationTokenInteractiveValidPid) {
-  HANDLE token = NULL;
-  DWORD pid = ::GetCurrentProcessId();
-  ASSERT_SUCCEEDED(goopdate_utils::GetImpersonationToken(true, pid, &token));
-  ASSERT_TRUE(token != NULL);
-  ASSERT_TRUE(::CloseHandle(token));
-}
-
-TEST(GoopdateUtilsTest, GetImpersonationTokenInteractiveInvalidPid) {
-  HANDLE token = NULL;
-  ASSERT_EQ(E_INVALIDARG,
-            goopdate_utils::GetImpersonationToken(true, 0, &token));
-  ASSERT_TRUE(token == NULL);
-}
-
-TEST(GoopdateUtilsTest, ImpersonateUser) {
-  DWORD pid = ::GetCurrentProcessId();
-
-  if (vista_util::IsUserAdmin()) {
-    ASSERT_SUCCEEDED(goopdate_utils::ImpersonateUser(false, 0));
-    ASSERT_SUCCEEDED(goopdate_utils::UndoImpersonation(true));
-
-    ASSERT_SUCCEEDED(goopdate_utils::ImpersonateUser(false, pid));
-    ASSERT_SUCCEEDED(goopdate_utils::UndoImpersonation(true));
-
-    ASSERT_SUCCEEDED(goopdate_utils::ImpersonateUser(true, pid));
-    ASSERT_SUCCEEDED(goopdate_utils::UndoImpersonation(true));
-
-    ASSERT_EQ(E_INVALIDARG, goopdate_utils::ImpersonateUser(true, 0));
-  } else {
-    std::wcout << _T("\tThis test did not run because the user ")
-                  _T("is not an admin.") << std::endl;
-  }
-}
-
-TEST(GoopdateUtilsTest, GoopdateTasks) {
-  CString task_path = ConcatenatePath(
-      app_util::GetCurrentModuleDirectory(),
-      _T("unittest_support\\does_not_shutdown\\GoogleUpdate.exe"));
-  // Install/uninstall.
-  EXPECT_SUCCEEDED(InstallGoopdateTasks(task_path,
-                                        vista_util::IsUserAdmin()));
-  EXPECT_SUCCEEDED(UninstallGoopdateTasks(vista_util::IsUserAdmin()));
-
-  // Calling InstallGoopdateTask twice should succeed.
-  for (int i = 0; i < 2; ++i) {
-    EXPECT_SUCCEEDED(InstallGoopdateTasks(task_path,
-                                          vista_util::IsUserAdmin()));
-  }
-
-  // Start and stop.
-  EXPECT_SUCCEEDED(StartGoopdateTaskCore(vista_util::IsUserAdmin()));
-  HRESULT hr = SCHED_S_TASK_HAS_NOT_RUN;
-  int tries = 0;
-  for (tries = 0;
-       tries < kMaxWaitForProcessIterations && SCHED_S_TASK_RUNNING != hr;
-       ++tries) {
-    EXPECT_EQ(SCHED_S_TASK_HAS_NOT_RUN, hr);  // For debugging test failures.
-    ::Sleep(kWaitForProcessIntervalMs);
-    hr = internal::GetScheduledTaskStatus(
-        ConfigManager::GetCurrentTaskNameCore(vista_util::IsUserAdmin()));
-  }
-  EXPECT_EQ(SCHED_S_TASK_RUNNING, hr);
-  EXPECT_LT(tries, kMaxWaitForProcessIterations) << _T("Loops exhausted.");
-  std::wcout << _T("\tLine ") << __LINE__ << _T(": Iterations=") << tries
-             << std::endl;
-
-  // Without a wait in between the start and stop, stop failed intermittently.
-  ::Sleep(500);
-
-  EXPECT_SUCCEEDED(internal::StopScheduledTask(
-      ConfigManager::GetCurrentTaskNameCore(vista_util::IsUserAdmin())));
-  hr = SCHED_S_TASK_RUNNING;
-  for (tries = 0;
-       tries < kMaxWaitForProcessIterations && SCHED_S_TASK_RUNNING == hr;
-       ++tries) {
-    ::Sleep(kWaitForProcessIntervalMs);
-    hr = internal::GetScheduledTaskStatus(
-        ConfigManager::GetCurrentTaskNameCore(vista_util::IsUserAdmin()));
-  }
-  EXPECT_NE(SCHED_S_TASK_RUNNING, hr);
-  EXPECT_LT(tries, kMaxWaitForProcessIterations) << _T("Loops exhausted.");
-  std::wcout << _T("\tLine ") << __LINE__ << _T(": Iterations=") << tries
-             << std::endl;
-  EXPECT_EQ(SCHED_S_TASK_READY, hr);
-
-  // Finally, uninstall.
-  EXPECT_SUCCEEDED(UninstallGoopdateTasks(vista_util::IsUserAdmin()));
-}
-
-TEST(GoopdateUtilsTest, GoopdateTaskInUseOverinstall) {
-  CString task_path = ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                      _T("LongRunningSilent.exe"));
-  EXPECT_SUCCEEDED(InstallGoopdateTasks(task_path,
-                                        vista_util::IsUserAdmin()));
-
-  CString original_task_name(
-      ConfigManager::GetCurrentTaskNameCore(vista_util::IsUserAdmin()));
-
-  // Open the file underlying the current task in exclusive mode, so that
-  // InstallGoopdateTasks() is forced to create a new task.
-  CComPtr<ITaskScheduler> scheduler;
-  EXPECT_SUCCEEDED(scheduler.CoCreateInstance(CLSID_CTaskScheduler,
-                                              NULL,
-                                              CLSCTX_INPROC_SERVER));
-  CComPtr<ITask> task;
-  EXPECT_SUCCEEDED(scheduler->Activate(original_task_name,
-                                       __uuidof(ITask),
-                                       reinterpret_cast<IUnknown**>(&task)));
-  CComQIPtr<IPersistFile> persist(task);
-  EXPECT_TRUE(persist);
-  scoped_ptr_cotask<OLECHAR> job_file;
-  EXPECT_SUCCEEDED(persist->GetCurFile(address(job_file)));
-  persist.Release();
-
-  File file;
-  EXPECT_SUCCEEDED(file.OpenShareMode(job_file.get(), false, false, 0));
-
-  EXPECT_SUCCEEDED(InstallGoopdateTasks(task_path,
-                                        vista_util::IsUserAdmin()));
-  CString new_task_name(
-      ConfigManager::GetCurrentTaskNameCore(vista_util::IsUserAdmin()));
-  EXPECT_STRNE(original_task_name, new_task_name);
-
-  // Cleanup.
-  file.Close();
-  EXPECT_SUCCEEDED(UninstallGoopdateTasks(vista_util::IsUserAdmin()));
-}
-
-TEST(GoopdateUtilsTest, GetExitCodeGoopdateTaskUA) {
-  CString task_path = ConcatenatePath(
-                          app_util::GetCurrentModuleDirectory(),
-                          _T("unittest_support\\SaveArguments.exe"));
-  EXPECT_SUCCEEDED(InstallGoopdateTasks(task_path,
-                                        vista_util::IsUserAdmin()));
-  EXPECT_EQ(SCHED_S_TASK_HAS_NOT_RUN,
-            GetExitCodeGoopdateTaskUA(vista_util::IsUserAdmin()));
-
-  EXPECT_SUCCEEDED(internal::StartScheduledTask(
-      ConfigManager::GetCurrentTaskNameUA(vista_util::IsUserAdmin())));
-
-  // Because SaveArguments.exe is not a long-lived process, the task could have
-  // run and finished at any point.
-  HRESULT hr = SCHED_S_TASK_HAS_NOT_RUN;
-  int tries = 0;
-  for (tries = 0;
-       tries < kMaxWaitForProcessIterations && SCHED_S_TASK_HAS_NOT_RUN == hr;
-       ++tries) {
-    ::Sleep(kWaitForProcessIntervalMs);
-    hr = internal::GetScheduledTaskStatus(
-        ConfigManager::GetCurrentTaskNameUA(vista_util::IsUserAdmin()));
-  }
-  EXPECT_NE(SCHED_S_TASK_HAS_NOT_RUN, hr);
-  EXPECT_LT(tries, kMaxWaitForProcessIterations) << _T("Loops exhausted.");
-  std::wcout << _T("\tLine ") << __LINE__ << _T(": Iterations=") << tries
-             << std::endl;
-  EXPECT_TRUE(SCHED_S_TASK_RUNNING == hr || SCHED_S_TASK_READY == hr);
-
-  hr = SCHED_S_TASK_RUNNING;
-  for (tries = 0;
-       tries < kMaxWaitForProcessIterations && SCHED_S_TASK_RUNNING == hr;
-       ++tries) {
-    ::Sleep(kWaitForProcessIntervalMs);
-    hr = internal::GetScheduledTaskStatus(
-        ConfigManager::GetCurrentTaskNameUA(vista_util::IsUserAdmin()));
-  }
-  EXPECT_NE(SCHED_S_TASK_RUNNING, hr);
-  EXPECT_LT(tries, kMaxWaitForProcessIterations) << _T("Loops exhausted.");
-  std::wcout << _T("\tLine ") << __LINE__ << _T(": Iterations=") << tries
-             << std::endl;
-  EXPECT_EQ(SCHED_S_TASK_READY, hr);
-
-  EXPECT_EQ(S_OK, GetExitCodeGoopdateTaskUA(vista_util::IsUserAdmin()));
-
-  EXPECT_SUCCEEDED(File::Remove(
-      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                      _T("unittest_support\\saved_arguments.txt"))));
-  EXPECT_SUCCEEDED(UninstallGoopdateTasks(vista_util::IsUserAdmin()));
-}
-
-TEST(GoopdateUtilsTest, GetOSInfo) {
-  const TCHAR kXpOSVersion[] = _T("5.1");
-  const TCHAR k2003OSVersion[] = _T("5.2");
-  const TCHAR kVistaOSVersion[] = _T("6.0");
-  const TCHAR kNoSp[] = _T("");
-  const TCHAR kSp1[] = _T("Service Pack 1");
-  const TCHAR kSp2[] = _T("Service Pack 2");
-  const TCHAR kSp3[] = _T("Service Pack 3");
-
-  CString os_version;
-  CString service_pack;
-  EXPECT_SUCCEEDED(goopdate_utils::GetOSInfo(&os_version, &service_pack));
-
-  EXPECT_TRUE((kXpOSVersion == os_version && kSp2 == service_pack) ||
-              (kXpOSVersion == os_version && kSp3 == service_pack) ||
-              (k2003OSVersion == os_version && kSp1 == service_pack) ||
-              (k2003OSVersion == os_version && kSp2 == service_pack) ||
-              (kVistaOSVersion == os_version && kNoSp == service_pack) ||
-              (kVistaOSVersion == os_version && kSp1 == service_pack));
-}
-
-class GoopdateUtilsRegistryProtectedTest : public testing::Test {
- protected:
-  GoopdateUtilsRegistryProtectedTest()
-      : hive_override_key_name_(kRegistryHiveOverrideRoot) {
-  }
-
-  CString hive_override_key_name_;
-
-  virtual void SetUp() {
-    RegKey::DeleteKey(hive_override_key_name_, true);
-    OverrideRegistryHives(hive_override_key_name_);
-  }
-
-  virtual void TearDown() {
-    RestoreRegistryHives();
-    ASSERT_SUCCEEDED(RegKey::DeleteKey(hive_override_key_name_, true));
-  }
-};
-
-// Some methods used by goopdate_utils rely on registry entries that are
-// overridden in the registry, so we need to write it.
-class GoopdateUtilsRegistryProtectedWithMachineFolderPathsTest
-    : public GoopdateUtilsRegistryProtectedTest {
- protected:
-  virtual void SetUp() {
-    const TCHAR kWindowsCurrentVersionKeyPath[] =
-        _T("HKLM\\Software\\Microsoft\\Windows\\CurrentVersion");
-    const TCHAR kProgramFilesDirValueName[] = _T("ProgramFilesDir");
-    const TCHAR kProgramFilesPath[] = _T("C:\\Program Files");
-
-    GoopdateUtilsRegistryProtectedTest::SetUp();
-    ASSERT_SUCCEEDED(RegKey::SetValue(kWindowsCurrentVersionKeyPath,
-                                      kProgramFilesDirValueName,
-                                      kProgramFilesPath));
-  }
-};
-
-// Some methods used by goopdate_utils rely on registry entries that are
-// overridden in the registry, so we need to write it.
-class GoopdateUtilsRegistryProtectedWithUserFolderPathsTest
-    : public GoopdateUtilsRegistryProtectedTest {
- protected:
-  virtual void SetUp() {
-  const TCHAR kUserShellKeyPath[] =
-        _T("HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\")
-        _T("User Shell Folders");
-    const TCHAR kLocalAppDataValueDirName[] = _T("Local AppData");
-    const TCHAR kLocalAppDataPath[] =
-        _T("%USERPROFILE%\\Local Settings\\Application Data");
-
-    GoopdateUtilsRegistryProtectedTest::SetUp();
-    ASSERT_SUCCEEDED(RegKey::SetValueExpandSZ(kUserShellKeyPath,
-                                              kLocalAppDataValueDirName,
-                                              kLocalAppDataPath));
-  }
-};
-
-// pv should be ignored.
-TEST_F(GoopdateUtilsRegistryProtectedWithMachineFolderPathsTest,
-       BuildGoogleUpdateExePath_MachineVersionFound) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENTS_GOOPDATE,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  CString path = goopdate_utils::BuildGoogleUpdateExePath(true);
-  CString program_files_path;
-  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &program_files_path));
-  EXPECT_STREQ(program_files_path + _T("\\Google\\Update\\GoogleUpdate.exe"),
-               path);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedWithMachineFolderPathsTest,
-       BuildGoogleUpdateExePath_MachineVersionNotFound) {
-  // Test when the key doesn't exist.
-  CString path = goopdate_utils::BuildGoogleUpdateExePath(true);
-  CString program_files_path;
-  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &program_files_path));
-  EXPECT_STREQ(program_files_path + _T("\\Google\\Update\\GoogleUpdate.exe"),
-               path);
-
-  // Test when the key exists but the value doesn't.
-  ASSERT_SUCCEEDED(RegKey::CreateKey(MACHINE_REG_CLIENTS_GOOPDATE));
-  path = goopdate_utils::BuildGoogleUpdateExePath(true);
-  EXPECT_STREQ(program_files_path + _T("\\Google\\Update\\GoogleUpdate.exe"),
-               path);
-}
-
-// pv should be ignored.
-TEST_F(GoopdateUtilsRegistryProtectedWithUserFolderPathsTest,
-       BuildGoogleUpdateExePath_UserVersionFound) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENTS_GOOPDATE,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  CString path = goopdate_utils::BuildGoogleUpdateExePath(false);
-
-  CString user_appdata;
-  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_LOCAL_APPDATA, &user_appdata));
-  CString expected_path;
-  expected_path.Format(_T("%s\\Google\\Update\\GoogleUpdate.exe"),
-                       user_appdata);
-  EXPECT_STREQ(expected_path, path);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedWithUserFolderPathsTest,
-       BuildGoogleUpdateExePath_UserVersionNotFound) {
-  CString user_appdata;
-  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_LOCAL_APPDATA, &user_appdata));
-  CString expected_path;
-  expected_path.Format(_T("%s\\Google\\Update\\GoogleUpdate.exe"),
-                       user_appdata);
-
-  // Test when the key doesn't exist.
-  CString path = goopdate_utils::BuildGoogleUpdateExePath(false);
-  EXPECT_STREQ(expected_path, path);
-
-  // Test when the key exists but the value doesn't.
-  ASSERT_SUCCEEDED(RegKey::CreateKey(USER_REG_CLIENTS_GOOPDATE));
-  path = goopdate_utils::BuildGoogleUpdateExePath(false);
-  EXPECT_STREQ(expected_path, path);
-}
-
-// The version is no longer used by StartGoogleUpdateWithArgs, so the return
-// value depends on whether program_files\Google\Update\GoogleUpdate.exe exists.
-TEST_F(GoopdateUtilsRegistryProtectedWithMachineFolderPathsTest,
-       StartGoogleUpdateWithArgs_MachineVersionVersionDoesNotExist) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENTS_GOOPDATE,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  const TCHAR* kArgs = _T("/foo");
-  HRESULT hr = goopdate_utils::StartGoogleUpdateWithArgs(true, kArgs, NULL);
-  EXPECT_TRUE(S_OK == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr);
-}
-
-// The version is no longer used by StartGoogleUpdateWithArgs, so the return
-// value depends on whether <user_folder>\Google\Update\GoogleUpdate.exe exists.
-// Also tests NULL args parameter
-TEST_F(GoopdateUtilsRegistryProtectedWithUserFolderPathsTest,
-       StartGoogleUpdateWithArgs_UserVersionVersionDoesNotExist) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENTS_GOOPDATE,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  HRESULT hr = goopdate_utils::StartGoogleUpdateWithArgs(false, NULL, NULL);
-  EXPECT_TRUE(S_OK == hr || HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_NotExplicit_NoKey) {
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, false));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_NotExplicit_ClientStateExists) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, false));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_NotExplicit_ClientStateOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, false));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_NotExplicit_ClientStateNegativeOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(-1)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, false));
-  EXPECT_EQ(-1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_NotExplicit_ClientStateZero_MediumNotExist) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, false));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_NotExplicit_ClientStateZero_MediumExists) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStateMediumPath));
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, false));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_NotExplicit_ClientStateZero_MediumOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, false));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-    IsAppEulaAccepted_Machine_NotExplicit_ClientStateZero_MediumNegativeOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(-1)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, false));
-  EXPECT_EQ(-1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(-1, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-// Also tests that user values are not used.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_NotExplicit_ClientStateZero_MediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, false));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-// ClientStateMedium does not override ClientState.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_NotExplicit_ClientStateOne_MediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, false));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-// Implicitly accepted because of the absence of eualaccepted=0.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_NotExplicit_ClientStateNotExist_MediumOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, false));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-// Implicitly accepted because of the absence of eualaccepted=0.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_NotExplicit_ClientStateNotExist_MediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, false));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_Explicit_NoKey) {
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, true));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_Explicit_ClientStateExists) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, true));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_Explicit_ClientStateOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, true));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_Explicit_ClientStateNegativeOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(-1)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, true));
-  EXPECT_EQ(-1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_Explicit_ClientStateZero_MediumNotExist) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, true));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_Explicit_ClientStateZero_MediumExists) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStateMediumPath));
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, true));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_Explicit_ClientStateZero_MediumOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, true));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_Explicit_ClientStateZero_MediumNegativeOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(-1)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, true));
-  EXPECT_EQ(-1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(-1, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-// Also tests that user values are not used.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_Explicit_ClientStateZero_MediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, true));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-// ClientStateMedium does not override ClientState.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_Explicit_ClientStateOne_MediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, true));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_Explicit_ClientStateNotExist_MediumOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, true));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_Machine_Explicit_ClientStateNotExist_MediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(true, kAppGuid, true));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-// ClientStateMedium is not supported for user apps.
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_User_NotExplicit_NoKey) {
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(false, kAppGuid, false));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_User_NotExplicit_ClientStateExists) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStatePath));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(false, kAppGuid, false));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_User_NotExplicit_ClientStateOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(false, kAppGuid, false));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_User_NotExplicit_ClientStateNegativeOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(-1)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(false, kAppGuid, false));
-  EXPECT_EQ(-1, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_User_NotExplicit_ClientStateZero_MediumNotExist) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(false, kAppGuid, false));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_User_NotExplicit_ClientStateZero_MediumExists) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStateMediumPath));
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(false, kAppGuid, false));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_User_NotExplicit_ClientStateZero_MediumOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(false, kAppGuid, false));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_User_NotExplicit_ClientStateZero_MediumNegativeOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(-1)));
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(false, kAppGuid, false));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(-1, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-// Also tests that machine values are not used.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_User_NotExplicit_ClientStateZero_MediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(false, kAppGuid, false));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-// ClientStateMedium is not used.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_User_NotExplicit_ClientStateOne_MediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(false, kAppGuid, false));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-// Implicitly accepted because of the absence of eualaccepted=0.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_User_NotExplicit_ClientStateNotExist_MediumOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(false, kAppGuid, false));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-// Implicitly accepted because of the absence of eualaccepted=0.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_User_NotExplicit_ClientStateNotExist_MediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(false, kAppGuid, false));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_User_Explicit_NoKey) {
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(false, kAppGuid, true));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_User_Explicit_ClientStateExists) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStatePath));
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(false, kAppGuid, true));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_User_Explicit_ClientStateOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(goopdate_utils::IsAppEulaAccepted(false, kAppGuid, true));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_User_Explicit_ClientStateNotExist_MediumOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(false, kAppGuid, true));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       IsAppEulaAccepted_User_Explicit_ClientStateNotExist_MediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_FALSE(goopdate_utils::IsAppEulaAccepted(false, kAppGuid, true));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppEulaNotAccepted_Machine_NoKey) {
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppEulaNotAccepted(true, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppEulaNotAccepted_Machine_ClientStateExists) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppEulaNotAccepted(true, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppEulaNotAccepted_Machine_ClientStateOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppEulaNotAccepted(true, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppEulaNotAccepted_Machine_ClientStateZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppEulaNotAccepted(true, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppEulaNotAccepted_Machine_ClientStateZero_ClientStateMediumOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppEulaNotAccepted(true, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-// Also tests that user values are not affected.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppEulaNotAccepted_Machine_ClientStateZero_ClientStateMediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppEulaNotAccepted(true, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppEulaNotAccepted_User_NoKey) {
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppEulaNotAccepted(false, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppEulaNotAccepted_User_ClientStateExists) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStatePath));
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppEulaNotAccepted(false, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppEulaNotAccepted_User_ClientStateOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppEulaNotAccepted(false, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppEulaNotAccepted_User_ClientStateZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppEulaNotAccepted(false, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppEulaNotAccepted_User_ClientStateZero_ClientStateMediumOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppEulaNotAccepted(false, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-// Also tests that machine values are not affected.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppEulaNotAccepted_User_ClientStateZero_ClientStateMediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppEulaNotAccepted(false, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       ClearAppEulaNotAccepted_Machine_NoKey) {
-  EXPECT_SUCCEEDED(goopdate_utils::ClearAppEulaNotAccepted(true, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       ClearAppEulaNotAccepted_Machine_ClientStateExists) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
-  EXPECT_SUCCEEDED(goopdate_utils::ClearAppEulaNotAccepted(true, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       ClearAppEulaNotAccepted_Machine_ClientStateOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(goopdate_utils::ClearAppEulaNotAccepted(true, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       ClearAppEulaNotAccepted_Machine_ClientStateZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(goopdate_utils::ClearAppEulaNotAccepted(true, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       ClearAppEulaNotAccepted_Machine_ClientStateZero_ClientStateMediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(goopdate_utils::ClearAppEulaNotAccepted(true, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-}
-
-// Also tests that user values are not affected.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       ClearAppEulaNotAccepted_Machine_ClientStateNone_ClientStateMediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(goopdate_utils::ClearAppEulaNotAccepted(true, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("eulaaccepted")));
-  EXPECT_EQ(0,
-            GetDwordValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(0,
-            GetDwordValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       ClearAppEulaNotAccepted_User_NoKey) {
-  EXPECT_SUCCEEDED(goopdate_utils::ClearAppEulaNotAccepted(false, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       ClearAppEulaNotAccepted_User_ClientStateExists) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStatePath));
-  EXPECT_SUCCEEDED(goopdate_utils::ClearAppEulaNotAccepted(false, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       ClearAppEulaNotAccepted_User_ClientStateOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(goopdate_utils::ClearAppEulaNotAccepted(false, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       ClearAppEulaNotAccepted_User_ClientStateZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(goopdate_utils::ClearAppEulaNotAccepted(false, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       ClearAppEulaNotAccepted_User_ClientStateZero_ClientStateMediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(goopdate_utils::ClearAppEulaNotAccepted(false, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(0,
-            GetDwordValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-}
-
-// Also tests that machine values are not affected.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       ClearAppEulaNotAccepted_User_ClientStateNone_ClientStateMediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(goopdate_utils::ClearAppEulaNotAccepted(false, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(0,
-            GetDwordValue(kAppUserClientStateMediumPath, _T("eulaaccepted")));
-  EXPECT_EQ(0,
-            GetDwordValue(kAppMachineClientStatePath, _T("eulaaccepted")));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("eulaaccepted")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_Machine_NoKey) {
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(true, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("usagestats")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_Machine_ClientStateExists) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(true, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("usagestats")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_Machine_ClientStateOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(goopdate_utils::AreAppUsageStatsEnabled(true, kAppGuid));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_Machine_ClientStateNegativeOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(-1)));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(true, kAppGuid));
-  EXPECT_EQ(-1, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_Machine_ClientStateZero_MediumNotExist) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(true, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_Machine_ClientStateZero_MediumExists) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStateMediumPath));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(true, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
-}
-
-// ClientStateMedium overrides ClientState.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_Machine_ClientStateZero_MediumOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(goopdate_utils::AreAppUsageStatsEnabled(true, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-    AreAppUsageStatsEnabled_Machine_ClientStateZero_MediumNegativeOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(-1)));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(true, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
-  EXPECT_EQ(-1, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_Machine_ClientStateZero_MediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(true, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("usagestats")));
-}
-
-// ClientStateMedium overrides ClientState.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_Machine_ClientStateOne_MediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(true, kAppGuid));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_Machine_ClientStateNotExist_MediumOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(goopdate_utils::AreAppUsageStatsEnabled(true, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("usagestats")));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_Machine_ClientStateNotExist_MediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(true, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("usagestats")));
-  EXPECT_EQ(0, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("usagestats")));
-}
-
-// User does not affect machine.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_Machine_UserOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(true, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStatePath, _T("usagestats")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("usagestats")));
-}
-
-// ClientStateMedium is not supported for user apps.
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_User_NoKey) {
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(false, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("usagestats")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_User_ClientStateExists) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStatePath));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(false, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("usagestats")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_User_ClientStateOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_TRUE(goopdate_utils::AreAppUsageStatsEnabled(false, kAppGuid));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_User_ClientStateNegativeOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(-1)));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(false, kAppGuid));
-  EXPECT_EQ(-1, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_User_ClientStateZero_MediumNotExist) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(false, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_User_ClientStateZero_MediumExists) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kAppUserClientStateMediumPath));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(false, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("usagestats")));
-}
-
-// ClientStateMedium is not used.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_User_ClientStateZero_MediumOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(false, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_User_ClientStateZero_MediumNegativeOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(-1)));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(false, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
-  EXPECT_EQ(-1, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_User_ClientStateZero_MediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(false, kAppGuid));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("usagestats")));
-}
-
-// ClientStateMedium is not used.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_User_ClientStateOne_MediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_TRUE(goopdate_utils::AreAppUsageStatsEnabled(false, kAppGuid));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStatePath, _T("usagestats")));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_User_ClientStateNotExist_MediumOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(false, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("usagestats")));
-  EXPECT_EQ(1, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_User_ClientStateNotExist_MediumZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(false, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("usagestats")));
-  EXPECT_EQ(0, GetDwordValue(kAppUserClientStateMediumPath,
-                             _T("usagestats")));
-}
-
-// Machine does not affect user.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       AreAppUsageStatsEnabled_User_MachineOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_FALSE(goopdate_utils::AreAppUsageStatsEnabled(false, kAppGuid));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStatePath, _T("usagestats")));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppUserClientStateMediumPath, _T("usagestats")));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStatePath, _T("usagestats")));
-  EXPECT_EQ(1, GetDwordValue(kAppMachineClientStateMediumPath,
-                             _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetUsageStatsEnable_VerifyLegacyLocationNotSet) {
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(true, kAppGuid, TRISTATE_TRUE));
-
-  ASSERT_TRUE(RegKey::HasKey(MACHINE_REG_UPDATE));
-  ASSERT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE,
-                                kLegacyRegValueCollectUsageStats));
-  ASSERT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest, SetUsageStatsEnable_Machine_Off) {
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(true, kAppGuid, TRISTATE_FALSE));
-
-  ASSERT_TRUE(RegKey::HasKey(kAppMachineClientStatePath));
-  ASSERT_TRUE(RegKey::HasValue(kAppMachineClientStatePath,
-                               _T("usagestats")));
-  ASSERT_FALSE(RegKey::HasKey(kAppUserClientStatePath));
-
-  DWORD enable_value = 1;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    &enable_value));
-  EXPECT_EQ(0, enable_value);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest, SetUsageStatsEnable_User_Off) {
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(false, kAppGuid, TRISTATE_FALSE));
-
-  ASSERT_TRUE(RegKey::HasKey(kAppUserClientStatePath));
-  ASSERT_TRUE(RegKey::HasValue(kAppUserClientStatePath,
-                               _T("usagestats")));
-  ASSERT_FALSE(RegKey::HasKey(kAppMachineClientStatePath));
-
-  DWORD enable_value = 1;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppUserClientStatePath,
-                                    _T("usagestats"),
-                                    &enable_value));
-  EXPECT_EQ(0, enable_value);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest, SetUsageStatsEnable_Machine_On) {
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(true, kAppGuid, TRISTATE_TRUE));
-
-  ASSERT_TRUE(RegKey::HasKey(kAppMachineClientStatePath));
-  ASSERT_TRUE(RegKey::HasValue(kAppMachineClientStatePath,
-                               _T("usagestats")));
-  ASSERT_FALSE(RegKey::HasKey(kAppUserClientStatePath));
-
-  DWORD enable_value = 0;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    &enable_value));
-  EXPECT_EQ(1, enable_value);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest, SetUsageStatsEnable_User_On) {
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(false, kAppGuid, TRISTATE_TRUE));
-
-  ASSERT_TRUE(RegKey::HasKey(kAppUserClientStatePath));
-  ASSERT_TRUE(RegKey::HasValue(kAppUserClientStatePath,
-                               _T("usagestats")));
-  ASSERT_FALSE(RegKey::HasKey(kAppMachineClientStatePath));
-
-  DWORD enable_value = 0;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppUserClientStatePath,
-                                    _T("usagestats"),
-                                    &enable_value));
-  EXPECT_EQ(1, enable_value);
-}
-
-
-TEST_F(GoopdateUtilsRegistryProtectedTest, SetUsageStatsEnable_Machine_None) {
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(true, kAppGuid, TRISTATE_NONE));
-  ASSERT_FALSE(RegKey::HasKey(MACHINE_REG_UPDATE));
-  ASSERT_FALSE(RegKey::HasKey(USER_REG_UPDATE));
-
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(false, kAppGuid, TRISTATE_NONE));
-  ASSERT_FALSE(RegKey::HasKey(USER_REG_UPDATE));
-  ASSERT_FALSE(RegKey::HasKey(MACHINE_REG_UPDATE));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetUsageStatsEnable_Machine_Overwrite) {
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(true, kAppGuid, TRISTATE_FALSE));
-
-  ASSERT_TRUE(RegKey::HasKey(kAppMachineClientStatePath));
-  ASSERT_TRUE(RegKey::HasValue(kAppMachineClientStatePath,
-                               _T("usagestats")));
-  ASSERT_FALSE(RegKey::HasKey(kAppUserClientStatePath));
-
-  DWORD enable_value = 1;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    &enable_value));
-  EXPECT_EQ(0, enable_value);
-
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(true, kAppGuid, TRISTATE_TRUE));
-
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    &enable_value));
-  EXPECT_EQ(1, enable_value);
-
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(true, kAppGuid, TRISTATE_FALSE));
-
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    &enable_value));
-  EXPECT_EQ(0, enable_value);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetUsageStatsEnable_Machine_NoneDoesNotOverwrite) {
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(true, kAppGuid, TRISTATE_FALSE));
-
-  ASSERT_TRUE(RegKey::HasKey(kAppMachineClientStatePath));
-  ASSERT_TRUE(RegKey::HasValue(kAppMachineClientStatePath,
-                               _T("usagestats")));
-  ASSERT_FALSE(RegKey::HasKey(kAppUserClientStatePath));
-
-  DWORD enable_value = 1;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    &enable_value));
-  EXPECT_EQ(0, enable_value);
-
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(true, kAppGuid, TRISTATE_NONE));
-
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    &enable_value));
-  EXPECT_EQ(0, enable_value);
-
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(true, kAppGuid, TRISTATE_TRUE));
-
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    &enable_value));
-  EXPECT_EQ(1, enable_value);
-
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(true, kAppGuid, TRISTATE_NONE));
-
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    _T("usagestats"),
-                                    &enable_value));
-  EXPECT_EQ(1, enable_value);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetUsageStatsEnable_Machine_ClientStateMediumCleared) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(true, kAppGuid, TRISTATE_TRUE));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(true, kAppGuid, TRISTATE_FALSE));
-  EXPECT_FALSE(
-      RegKey::HasValue(kAppMachineClientStateMediumPath, _T("usagestats")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetUsageStatsEnable_Machine_NoneDoesNotClearClientStateMedium) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(true, kAppGuid, TRISTATE_NONE));
-
-  DWORD enable_value = 0;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStateMediumPath,
-                                    _T("usagestats"),
-                                    &enable_value));
-  EXPECT_EQ(1, enable_value);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetUsageStatsEnable_User_ClientStateMediumNotCleared) {
-  // User and machine values should not be cleared.
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(0)));
-
-  // True does not clear them.
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(false, kAppGuid, TRISTATE_TRUE));
-  DWORD enable_value = 1;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppUserClientStateMediumPath,
-                                    _T("usagestats"),
-                                    &enable_value));
-  EXPECT_EQ(0, enable_value);
-  enable_value = 1;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStateMediumPath,
-                                    _T("usagestats"),
-                                    &enable_value));
-  EXPECT_EQ(0, enable_value);
-
-  // False does not clear them.
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStateMediumPath,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetUsageStatsEnable(false, kAppGuid, TRISTATE_FALSE));
-  enable_value = 0;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppUserClientStateMediumPath,
-                                    _T("usagestats"),
-                                    &enable_value));
-  EXPECT_EQ(1, enable_value);
-  enable_value = 0;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStateMediumPath,
-                                    _T("usagestats"),
-                                    &enable_value));
-  EXPECT_EQ(1, enable_value);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest, ConvertLegacyUsageStats_NotPresent) {
-  EXPECT_SUCCEEDED(goopdate_utils::ConvertLegacyUsageStats(true));
-  ASSERT_FALSE(RegKey::HasKey(MACHINE_REG_CLIENT_STATE));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest, ConvertLegacyUsageStats_Present) {
-  DWORD val = 1;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    kLegacyRegValueCollectUsageStats,
-                                    val));
-
-  EXPECT_SUCCEEDED(goopdate_utils::ConvertLegacyUsageStats(true));
-
-  ASSERT_TRUE(RegKey::HasKey(MACHINE_REG_CLIENT_STATE_GOOPDATE));
-  ASSERT_TRUE(RegKey::HasValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                               _T("usagestats")));
-
-  DWORD enable_value = 0;
-  EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("usagestats"),
-                                    &enable_value));
-  EXPECT_EQ(1, enable_value);
-
-  ASSERT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE,
-                               kLegacyRegValueCollectUsageStats));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest, GetClientsStringValueFromRegistry) {
-  CString value;
-
-  // Test IsMachine = true
-  const TCHAR* dummy_machine_clients_key = MACHINE_REG_CLIENTS DUMMY_CLSID;
-
-  ASSERT_FALSE(RegKey::HasKey(dummy_machine_clients_key));
-  EXPECT_FAILED(goopdate_utils::GetClientsStringValueFromRegistry(
-                                    true, DUMMY_CLSID, _T("name"), &value));
-  ASSERT_SUCCEEDED(RegKey::DeleteKey(dummy_machine_clients_key));
-
-  ASSERT_SUCCEEDED(RegKey::SetValue(dummy_machine_clients_key,
-                                    _T("name"),
-                                    _T("dummy")));
-  EXPECT_SUCCEEDED(goopdate_utils::GetClientsStringValueFromRegistry(
-                                      true, DUMMY_CLSID, _T("name"), &value));
-  EXPECT_EQ(_T("dummy"), value);
-
-  // Test IsMachine = false
-  const TCHAR* dummy_user_clients_key = USER_REG_CLIENTS DUMMY_CLSID;
-
-  ASSERT_FALSE(RegKey::HasKey(dummy_user_clients_key));
-  EXPECT_FAILED(goopdate_utils::GetClientsStringValueFromRegistry(
-                                   false, DUMMY_CLSID, _T("name"), &value));
-
-  ASSERT_SUCCEEDED(RegKey::SetValue(dummy_user_clients_key,
-                                    _T("name"),
-                                    _T("dummy2")));
-  EXPECT_SUCCEEDED(goopdate_utils::GetClientsStringValueFromRegistry(
-                                      false, DUMMY_CLSID, _T("name"), &value));
-  EXPECT_EQ(_T("dummy2"), value);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest, GetVerFromRegistry) {
-  CString version;
-
-  // Test IsMachine = true
-  const TCHAR* dummy_machine_clients_key = MACHINE_REG_CLIENTS DUMMY_CLSID;
-
-  ASSERT_FALSE(RegKey::HasKey(dummy_machine_clients_key));
-  EXPECT_FAILED(goopdate_utils::GetVerFromRegistry(true,
-                                                   DUMMY_CLSID,
-                                                   &version));
-  ASSERT_SUCCEEDED(RegKey::DeleteKey(dummy_machine_clients_key));
-
-  ASSERT_SUCCEEDED(RegKey::SetValue(dummy_machine_clients_key,
-                                    _T("pv"),
-                                    _T("1.0.101.0")));
-  EXPECT_SUCCEEDED(goopdate_utils::GetVerFromRegistry(true,
-                                                      DUMMY_CLSID,
-                                                      &version));
-  EXPECT_EQ(_T("1.0.101.0"), version);
-
-  // Test IsMachine = false
-  const TCHAR* dummy_user_clients_key = USER_REG_CLIENTS DUMMY_CLSID;
-
-  ASSERT_FALSE(RegKey::HasKey(dummy_user_clients_key));
-  EXPECT_FAILED(goopdate_utils::GetVerFromRegistry(false,
-                                                   DUMMY_CLSID,
-                                                   &version));
-
-  ASSERT_SUCCEEDED(RegKey::SetValue(dummy_user_clients_key,
-                                    _T("pv"),
-                                    _T("1.0.102.0")));
-  EXPECT_SUCCEEDED(goopdate_utils::GetVerFromRegistry(false,
-                                                      DUMMY_CLSID,
-                                                      &version));
-  EXPECT_EQ(_T("1.0.102.0"), version);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       BuildHttpGetString_MachineNoTestSource) {
-  CString expected_str_before_os(
-      _T("http://www.google.com/hello.py?code=123&")
-      _T("hl=en&errorcode=0x0000000a&extracode1=0x00000016&extracode2=0&")
-      _T("app=%7BB7BAF788-9D64-49c3-AFDC-B336AB12F332%7D&")
-      _T("guver=1.0.51.0&ismachine=1&os="));
-  CString expected_str_after_os(
-      _T("&iid=%7B0F973A20-C484-462B-952C-5D9A459E3326%7D")  // Upper case 'B'.
-      _T("&brand=GoOG&source=click"));
-  bool expected_test_source = false;
-
-#if defined(DEBUG) || !OFFICIAL_BUILD
-  // TestSource is always set for these builds. It may be set for opt official
-  // builds but this is not guaranteed.
-  expected_str_after_os.Append(_T("&testsource="));
-  expected_test_source = true;
-#endif
-
-  CString url_req;
-  EXPECT_SUCCEEDED(goopdate_utils::BuildHttpGetString(
-      _T("http://www.google.com/hello.py?code=123&"),
-      10,
-      22,
-      0,
-      APP_GUID,
-      _T("1.0.51.0"),
-      true,
-      _T("en"),
-      StringToGuid(_T("{0F973A20-C484-462b-952C-5D9A459E3326}")),
-      _T("GoOG"),
-      _T("click"),
-      &url_req));
-
-  EXPECT_EQ(-1, url_req.FindOneOf(_T("{}")));
-
-  EXPECT_LE(expected_str_before_os.GetLength(), url_req.GetLength());
-  EXPECT_EQ(0, url_req.Find(expected_str_before_os)) <<
-      _T("Expected: ") << expected_str_before_os.GetString() << std::endl <<
-      _T("At beginning of: ") << url_req.GetString();
-  int os_fragment_len = 0;
-  EXPECT_EQ(expected_str_before_os.GetLength(),
-            VerifyOSInUrl(url_req, &os_fragment_len)) <<
-      _T("Expected OS string not found in: ") << url_req.GetString();
-
-  EXPECT_EQ(expected_str_before_os.GetLength() + os_fragment_len,
-            url_req.Find(expected_str_after_os)) <<
-      _T("Expected: ") << expected_str_after_os.GetString() << std::endl <<
-      _T("At end of: ") << url_req.GetString();
-
-  if (expected_test_source) {
-    CString expected_testsource_str =
-        ConfigManager::Instance()->GetTestSource();
-    int expected_testsource_start = expected_str_before_os.GetLength() +
-                                    os_fragment_len +
-                                    expected_str_after_os.GetLength();
-    EXPECT_EQ(expected_testsource_start, url_req.Find(expected_testsource_str));
-    EXPECT_EQ(expected_testsource_start + expected_testsource_str.GetLength(),
-              url_req.GetLength());
-  } else {
-    EXPECT_EQ(expected_str_before_os.GetLength() +
-              os_fragment_len +
-              expected_str_after_os.GetLength(),
-              url_req.GetLength());
-
-    EXPECT_EQ(-1, url_req.Find(_T("testsource")));
-  }
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       BuildHttpGetString_UserWithTestSource) {
-  #define APP_GUID _T("{B7BAF788-9D64-49c3-AFDC-B336AB12F332}")
-
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                    kRegValueTestSource,
-                                    _T("dev")));
-
-  const CString expected_str_before_os(
-      _T("http://www.google.com/hello.py?")
-      _T("hl=de&errorcode=0xffffffff&extracode1=0x00000000&extracode2=99&")
-      _T("app=%7BB7BAF788-9D64-49c3-AFDC-B336AB12F332%7D&")
-      _T("guver=foo%20bar&ismachine=0&os="));
-  const CString expected_str_after_os(
-      _T("&iid=%7B0F973A20-C484-462B-952C-5D9A459E3326%7D")  // Upper case 'B'.
-      _T("&brand=GGLE&source=clack")
-      _T("&testsource="));
-
-  CString url_req;
-  EXPECT_SUCCEEDED(goopdate_utils::BuildHttpGetString(
-      _T("http://www.google.com/hello.py?"),
-      0xffffffff,
-      0,
-      99,
-      APP_GUID,
-      _T("foo bar"),
-      false,
-      _T("de"),
-      StringToGuid(_T("{0F973A20-C484-462b-952C-5D9A459E3326}")),
-      _T("GGLE"),
-      _T("clack"),
-      &url_req));
-  EXPECT_LE(expected_str_before_os.GetLength(), url_req.GetLength());
-  EXPECT_EQ(0, url_req.Find(expected_str_before_os));
-
-  int os_fragment_len = 0;
-  EXPECT_EQ(expected_str_before_os.GetLength(),
-            VerifyOSInUrl(url_req, &os_fragment_len)) <<
-      _T("Expected: ") << expected_str_before_os.GetString() << std::endl <<
-      _T("At beginning of: ") << url_req.GetString();
-
-  EXPECT_EQ(expected_str_before_os.GetLength() + os_fragment_len,
-            url_req.Find(expected_str_after_os)) <<
-      _T("Expected OS string not found in: ") << url_req.GetString();
-
-  const CString expected_testsource_str = _T("dev");
-
-  int expected_testsource_start = expected_str_before_os.GetLength() +
-                                  os_fragment_len +
-                                  expected_str_after_os.GetLength();
-  EXPECT_EQ(expected_testsource_start, url_req.Find(expected_testsource_str));
-  EXPECT_EQ(expected_testsource_start + expected_testsource_str.GetLength(),
-            url_req.GetLength());
-}
-
-// MID and UID automatically get generated when they are read, so it is not
-// possible for them to be empty. IID and brand code is emtpy if not present.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       BuildHttpGetString_NoMidUidIidBrandCode) {
-  const CString expected_str_before_os(
-      _T("http://www.google.com/hello.py?")
-      _T("hl=en&errorcode=0xffffffff&extracode1=0x00000000&extracode2=99&")
-      _T("app=%7BB7BAF788-9D64-49c3-AFDC-B336AB12F332%7D&")
-      _T("guver=foo%20bar&ismachine=1&os="));
-  const CString expected_str_after_uid(
-      _T("&mid=&uid=&iid=&brand=&source=cluck"));
-
-  CString url_req;
-  EXPECT_SUCCEEDED(goopdate_utils::BuildHttpGetString(
-      _T("http://www.google.com/hello.py?"),
-      0xffffffff,
-      0,
-      99,
-      _T("{B7BAF788-9D64-49c3-AFDC-B336AB12F332}"),
-      _T("foo bar"),
-      true,
-      _T("en"),
-      GUID_NULL,
-      _T(""),
-      _T("cluck"),
-      &url_req));
-
-  EXPECT_EQ(0, url_req.Find(expected_str_before_os)) <<
-      _T("Expected: ") << expected_str_before_os.GetString() << std::endl <<
-      _T("At beginning of: ") << url_req.GetString();
-
-  EXPECT_GT(0, url_req.Find(expected_str_after_uid));
-
-  CString expected_test_src;
-#if defined(DEBUG) || !OFFICIAL_BUILD
-  expected_test_src = _T("&testsource=auto");
-#endif
-  const CString expected_iid_str(_T("&iid=&brand=&source=cluck"));
-  EXPECT_EQ(url_req.GetLength() -
-            expected_iid_str.GetLength() -
-            expected_test_src.GetLength(),
-            url_req.Find(expected_iid_str));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest, GetNumClients) {
-  size_t num_clients(0);
-
-  // Fails when no "Clients" key.
-  EXPECT_HRESULT_FAILED(GetNumClients(true, &num_clients));
-  EXPECT_HRESULT_FAILED(GetNumClients(false, &num_clients));
-
-  // Tests no subkeys.
-  const TCHAR* keys_to_create[] = { MACHINE_REG_CLIENTS, USER_REG_CLIENTS };
-  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKeys(keys_to_create,
-                                              arraysize(keys_to_create)));
-  EXPECT_HRESULT_SUCCEEDED(GetNumClients(true, &num_clients));
-  EXPECT_EQ(0, num_clients);
-  EXPECT_HRESULT_SUCCEEDED(GetNumClients(false, &num_clients));
-  EXPECT_EQ(0, num_clients);
-
-  // Subkeys should be counted. Values should not be counted.
-  RegKey machine_key;
-  EXPECT_HRESULT_SUCCEEDED(machine_key.Open(HKEY_LOCAL_MACHINE,
-                                            GOOPDATE_REG_RELATIVE_CLIENTS));
-  EXPECT_HRESULT_SUCCEEDED(machine_key.SetValue(_T("name"), _T("value")));
-  EXPECT_HRESULT_SUCCEEDED(GetNumClients(true, &num_clients));
-  EXPECT_EQ(0, num_clients);
-
-  const TCHAR* app_id = _T("{AA5523E3-40C0-4b85-B074-4BBA09559CCD}");
-  EXPECT_HRESULT_SUCCEEDED(machine_key.Create(machine_key.Key(), app_id));
-  EXPECT_HRESULT_SUCCEEDED(GetNumClients(true, &num_clients));
-  EXPECT_EQ(1, num_clients);
-
-  // Tests user scenario.
-  RegKey user_key;
-  EXPECT_HRESULT_SUCCEEDED(user_key.Open(HKEY_CURRENT_USER,
-                                         GOOPDATE_REG_RELATIVE_CLIENTS));
-  EXPECT_HRESULT_SUCCEEDED(user_key.SetValue(_T("name"), _T("value")));
-  EXPECT_HRESULT_SUCCEEDED(GetNumClients(false, &num_clients));
-  EXPECT_EQ(0, num_clients);
-
-  EXPECT_HRESULT_SUCCEEDED(user_key.Create(user_key.Key(), app_id));
-  EXPECT_HRESULT_SUCCEEDED(GetNumClients(false, &num_clients));
-  EXPECT_EQ(1, num_clients);
-}
-
-TEST(GoopdateUtilsTest, BuildInstallDirectory_Machine) {
-  const CPath dir = goopdate_utils::BuildInstallDirectory(true, _T("1.2.3.0"));
-  CString program_files_path;
-  EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &program_files_path));
-  EXPECT_STREQ(program_files_path + _T("\\Google\\Update\\1.2.3.0"), dir);
-}
-
-TEST(GoopdateUtilsTest, BuildInstallDirectory_User) {
-  CPath expected_path(GetGoogleUpdateUserPath());
-  expected_path.Append(_T("4.5.6.7"));
-  EXPECT_STREQ(expected_path,
-               goopdate_utils::BuildInstallDirectory(false, _T("4.5.6.7")));
-}
-
-TEST(GoopdateUtilsTest, ConvertBrowserTypeToString) {
-  for (int i = 0; i < BROWSER_MAX; ++i) {
-    CString str_type = goopdate_utils::ConvertBrowserTypeToString(
-        static_cast<BrowserType>(i));
-    BrowserType type = BROWSER_UNKNOWN;
-    ASSERT_HRESULT_SUCCEEDED(
-        goopdate_utils::ConvertStringToBrowserType(str_type, &type));
-    ASSERT_EQ(static_cast<int>(type), i);
-  }
-}
-
-void CompareArgsAndUpdateResponseData(const UpdateResponseData& response_data,
-                                      const CString& app_name,
-                                      const CommandLineExtraArgs& args) {
-  ASSERT_STREQ(GuidToString(response_data.guid()),
-               GuidToString(args.apps[0].app_guid));
-  ASSERT_STREQ(app_name, args.apps[0].app_name);
-  ASSERT_EQ(response_data.needs_admin() == NEEDS_ADMIN_YES ? true : false,
-            args.apps[0].needs_admin);
-  ASSERT_STREQ(response_data.ap(), args.apps[0].ap);
-  ASSERT_STREQ(response_data.tt_token(), args.apps[0].tt_token);
-
-  ASSERT_EQ(GuidToString(response_data.installation_id()),
-            GuidToString(args.installation_id));
-  ASSERT_TRUE(args.brand_code.IsEmpty());
-  ASSERT_TRUE(args.client_id.IsEmpty());
-  ASSERT_TRUE(args.referral_id.IsEmpty());
-  ASSERT_EQ(response_data.browser_type(), args.browser_type);
-  ASSERT_EQ(TRISTATE_NONE, args.usage_stats_enable);
-}
-
-TEST(GoopdateUtilsTest, ConvertResponseDataToExtraArgsRequired) {
-  // These unit tests create an update response and then call the test method,
-  // to create the command line. Next the command line is parsed using the
-  // command line class and the results are validated.
-  UpdateResponseData input;
-  input.set_guid(StringToGuid(_T("{8B59B82E-5543-4807-8590-84BF484AE2F6}")));
-
-  CString unicode_name;
-  ASSERT_TRUE(unicode_name.LoadString(IDS_ESCAPE_TEST));
-  CString encoded_name;
-  ASSERT_HRESULT_SUCCEEDED(WideStringToUtf8UrlEncodedString(unicode_name,
-                                                            &encoded_name));
-  input.set_app_name(encoded_name);
-  input.set_needs_admin(NEEDS_ADMIN_YES);
-
-  CString extra_args;
-  ASSERT_HRESULT_SUCCEEDED(
-      goopdate_utils::ConvertResponseDataToExtraArgs(input, &extra_args));
-
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  ASSERT_HRESULT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-
-  CompareArgsAndUpdateResponseData(input, unicode_name, args);
-}
-
-
-TEST(GoopdateUtilsTest, ConvertResponseDataToExtraArgsAll) {
-  UpdateResponseData input;
-  input.set_guid(StringToGuid(_T("{8B59B82E-5543-4807-8590-84BF484AE2F6}")));
-
-  CString unicode_name;
-  ASSERT_TRUE(unicode_name.LoadString(IDS_ESCAPE_TEST));
-  CString encoded_name;
-  ASSERT_HRESULT_SUCCEEDED(WideStringToUtf8UrlEncodedString(unicode_name,
-                                                            &encoded_name));
-  input.set_app_name(encoded_name);
-  input.set_needs_admin(NEEDS_ADMIN_YES);
-  input.set_installation_id(
-      StringToGuid(_T("{E314A405-FCC5-4ed1-BFA4-CBC22F1873BF}")));
-  input.set_ap(_T("Test_ap"));
-  input.set_tt_token(_T("Test_tt_token"));
-  input.set_browser_type(BROWSER_IE);
-
-  CString extra_args;
-  ASSERT_HRESULT_SUCCEEDED(
-      goopdate_utils::ConvertResponseDataToExtraArgs(input, &extra_args));
-
-  CommandLineExtraArgs args;
-  ExtraArgsParser parser;
-  ASSERT_HRESULT_SUCCEEDED(parser.Parse(extra_args, NULL, &args));
-
-  CompareArgsAndUpdateResponseData(input, unicode_name, args);
-}
-
-TEST(GoopdateUtilsTest, UniqueEventInEnvironment_User) {
-  const TCHAR* kEnvVarName = _T("SOME_ENV_VAR_FOR_TEST");
-  scoped_event created_event;
-  scoped_event opened_event;
-
-  ASSERT_HRESULT_SUCCEEDED(goopdate_utils::CreateUniqueEventInEnvironment(
-      kEnvVarName,
-      false,
-      address(created_event)));
-  ASSERT_TRUE(created_event);
-  EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(get(created_event), 0));
-
-  TCHAR event_name[MAX_PATH] = {0};
-  EXPECT_TRUE(
-      ::GetEnvironmentVariable(kEnvVarName, event_name, arraysize(event_name)));
-
-  ASSERT_HRESULT_SUCCEEDED(goopdate_utils::OpenUniqueEventFromEnvironment(
-      kEnvVarName,
-      false,
-      address(opened_event)));
-  ASSERT_TRUE(opened_event);
-
-  EXPECT_TRUE(::SetEvent(get(opened_event)));
-  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(created_event), 0));
-
-  EXPECT_TRUE(::SetEnvironmentVariable(kEnvVarName, NULL));
-}
-
-TEST(GoopdateUtilsTest, UniqueEventInEnvironment_Machine) {
-  const TCHAR* kEnvVarName = _T("OTHER_ENV_VAR_FOR_TEST");
-  scoped_event created_event;
-  scoped_event opened_event;
-  TCHAR event_name[MAX_PATH] = {0};
-
-  if (!vista_util::IsUserAdmin()) {
-    EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INVALID_OWNER),
-              goopdate_utils::CreateUniqueEventInEnvironment(
-                  kEnvVarName,
-                  true,
-                  address(created_event)));
-    EXPECT_FALSE(created_event);
-
-    EXPECT_FALSE(::GetEnvironmentVariable(kEnvVarName,
-                                          event_name,
-                                          arraysize(event_name)));
-    return;
-  }
-
-  ASSERT_HRESULT_SUCCEEDED(goopdate_utils::CreateUniqueEventInEnvironment(
-      kEnvVarName,
-      true,
-      address(created_event)));
-  ASSERT_TRUE(created_event);
-  EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(get(created_event), 0));
-
-  EXPECT_TRUE(
-      ::GetEnvironmentVariable(kEnvVarName, event_name, arraysize(event_name)));
-
-  ASSERT_HRESULT_SUCCEEDED(goopdate_utils::OpenUniqueEventFromEnvironment(
-      kEnvVarName,
-      true,
-      address(opened_event)));
-  ASSERT_TRUE(opened_event);
-
-  EXPECT_TRUE(::SetEvent(get(opened_event)));
-  EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(created_event), 0));
-
-  EXPECT_TRUE(::SetEnvironmentVariable(kEnvVarName, NULL));
-}
-
-TEST(GoopdateUtilsTest, UniqueEventInEnvironment_UserMachineMismatch) {
-  const TCHAR* kEnvVarName = _T("ENV_VAR_FOR_MIXED_TEST");
-  scoped_event created_event;
-  scoped_event opened_event;
-
-  ASSERT_HRESULT_SUCCEEDED(goopdate_utils::CreateUniqueEventInEnvironment(
-      kEnvVarName,
-      false,
-      address(created_event)));
-  ASSERT_TRUE(created_event);
-  EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(get(created_event), 0));
-
-  TCHAR event_name[MAX_PATH] = {0};
-  EXPECT_TRUE(
-      ::GetEnvironmentVariable(kEnvVarName, event_name, arraysize(event_name)));
-
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            goopdate_utils::OpenUniqueEventFromEnvironment(
-                kEnvVarName,
-                true,
-                address(opened_event)));
-
-  EXPECT_TRUE(::SetEnvironmentVariable(kEnvVarName, NULL));
-}
-
-TEST(GoopdateUtilsTest, OpenUniqueEventFromEnvironment_EnvVarDoesNotExist) {
-  const TCHAR* kEnvVarName = _T("ANOTHER_ENV_VAR_FOR_TEST");
-  scoped_event opened_event;
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND),
-            goopdate_utils::OpenUniqueEventFromEnvironment(
-                kEnvVarName,
-                false,
-                address(opened_event)));
-}
-
-TEST(GoopdateUtilsTest, OpenUniqueEventFromEnvironment_EventDoesNotExist) {
-  const TCHAR* kEnvVarName = _T("YET_ANOTHER_ENV_VAR_FOR_TEST");
-
-  EXPECT_TRUE(::SetEnvironmentVariable(kEnvVarName, _T("foo")));
-
-  scoped_event opened_event;
-    EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-              goopdate_utils::OpenUniqueEventFromEnvironment(
-                  kEnvVarName,
-                  false,
-                  address(opened_event)));
-
-  EXPECT_TRUE(::SetEnvironmentVariable(kEnvVarName, NULL));
-}
-
-
-CString GetTempFile() {
-  TCHAR temp_path[MAX_PATH] = {0};
-  TCHAR temp_file[MAX_PATH] = {0};
-
-  EXPECT_LT(::GetTempPath(arraysize(temp_path), temp_path),
-            arraysize(temp_path));
-  EXPECT_NE(0, ::GetTempFileName(temp_path, _T("ut_"), 0, temp_file));
-  return CString(temp_file);
-}
-
-typedef std::map<CString, CString> StringMap;
-typedef StringMap::const_iterator StringMapIter;
-
-TEST(GoopdateUtilsTest, ReadNameValuePairsFromFileTest_MissingFile) {
-  CString temp_file = GetTempFile();
-  ::DeleteFile(temp_file);
-
-  ASSERT_FALSE(File::Exists(temp_file));
-
-  StringMap pairs_read;
-  ASSERT_FAILED(goopdate_utils::ReadNameValuePairsFromFile(temp_file,
-                                                           _T("my_group"),
-                                                           &pairs_read));
-  ASSERT_EQ(0, pairs_read.size());
-}
-
-TEST(GoopdateUtilsTest, ReadNameValuePairsFromFileTest_ReadEmpty) {
-  CString temp_file = GetTempFile();
-  File file_write;
-  EXPECT_SUCCEEDED(file_write.Open(temp_file, true, false));
-  file_write.Close();
-
-  StringMap pairs_read;
-  ASSERT_SUCCEEDED(goopdate_utils::ReadNameValuePairsFromFile(temp_file,
-                                                              _T("my_group"),
-                                                              &pairs_read));
-  ASSERT_EQ(0, pairs_read.size());
-}
-
-void ValidateStringMapEquality(const StringMap& expected,
-                               const StringMap& actual) {
-  ASSERT_EQ(expected.size(), actual.size());
-
-  StringMapIter it_expected = expected.begin();
-  for (; it_expected != expected.end(); ++it_expected) {
-    StringMapIter it_actual = actual.find(it_expected->first);
-    ASSERT_TRUE(it_actual != actual.end());
-    ASSERT_STREQ(it_expected->second, it_actual->second);
-  }
-}
-
-TEST(GoopdateUtilsTest, ReadNameValuePairsFromFileTest_ReadOnePair) {
-  CString group = _T("my_group");
-
-  StringMap pairs_write;
-  pairs_write[_T("some_name")] = _T("some_value");
-
-  CString temp_file = GetTempFile();
-  ASSERT_SUCCEEDED(goopdate_utils::WriteNameValuePairsToFile(temp_file,
-                                                             group,
-                                                             pairs_write));
-  ASSERT_TRUE(File::Exists(temp_file));
-
-  StringMap pairs_read;
-  ASSERT_SUCCEEDED(goopdate_utils::ReadNameValuePairsFromFile(temp_file,
-                                                              group,
-                                                              &pairs_read));
-
-  ValidateStringMapEquality(pairs_write, pairs_read);
-}
-
-TEST(GoopdateUtilsTest, ReadNameValuePairsFromFileTest_ReadManyPairs) {
-  CString group = _T("my_group");
-
-  StringMap pairs_write;
-  const int kCountPairs = 10;
-  for (int i = 1; i <= kCountPairs; ++i) {
-    CString name;
-    name.Format(_T("name%d"), i);
-    CString value;
-    value.Format(_T("value%d"), i);
-    pairs_write[name] = value;
-  }
-
-  CString temp_file = GetTempFile();
-  ASSERT_SUCCEEDED(goopdate_utils::WriteNameValuePairsToFile(temp_file,
-                                                             group,
-                                                             pairs_write));
-  ASSERT_TRUE(File::Exists(temp_file));
-
-  StringMap pairs_read;
-  ASSERT_SUCCEEDED(goopdate_utils::ReadNameValuePairsFromFile(temp_file,
-                                                              group,
-                                                              &pairs_read));
-
-  ValidateStringMapEquality(pairs_write, pairs_read);
-}
-
-// This test verifies that InstallTime is created afresh for Omaha if it does
-// not exist, and even if the brand code is already set.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetGoogleUpdateBranding_BrandAlreadyExistsAllEmpty) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    _T("EFGH")));
-
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetGoogleUpdateBranding(kAppMachineClientStatePath,
-                                              _T(""),
-                                              _T("")));
-
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("EFGH"), value);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueClientId,
-                             &value));
-  DWORD install_time = 0;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueInstallTimeSec,
-                                    &install_time));
-  EXPECT_GE(now, install_time);
-  EXPECT_GE(static_cast<uint32>(200), now - install_time);
-}
-
-// This test verifies that InstallTime remains unchanged for Omaha if it already
-// exists and the brand code is already set.
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetGoogleUpdateBranding_AllAlreadyExistAllEmpty) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    _T("EFGH")));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    _T("existing_partner")));
-  const DWORD kInstallTime = 1234567890;
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueInstallTimeSec,
-                                    kInstallTime));
-
-  EXPECT_SUCCEEDED(
-      goopdate_utils::SetGoogleUpdateBranding(kAppMachineClientStatePath,
-                                              _T(""),
-                                              _T("")));
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("EFGH"), value);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    &value));
-  EXPECT_STREQ(_T("existing_partner"), value);
-  EXPECT_EQ(kInstallTime,
-            GetDwordValue(kAppMachineClientStatePath, kRegValueInstallTimeSec));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest, SetAppBranding_KeyDoesNotExist) {
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                           _T("ABCD"),
-                                           _T("some_partner"),
-                                           _T("referrer")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest, SetAppBranding_AllEmpty) {
-  ASSERT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T(""),
-                                                  _T(""),
-                                                  _T("")));
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("GGLS"), value);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueClientId,
-                             &value));
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueReferralId,
-                             &value));
-  DWORD install_time = 0;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueInstallTimeSec,
-                                    &install_time));
-  EXPECT_GE(now, install_time);
-  EXPECT_GE(static_cast<uint32>(200), now - install_time);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest, SetAppBranding_BrandCodeOnly) {
-  ASSERT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T("ABCD"),
-                                                  _T(""),
-                                                  _T("")));
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("ABCD"), value);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueClientId,
-                             &value));
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueReferralId,
-                             &value));
-  DWORD install_time = 0;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueInstallTimeSec,
-                                    &install_time));
-  EXPECT_GE(now, install_time);
-  EXPECT_GE(static_cast<uint32>(200), now - install_time);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest, SetAppBranding_BrandCodeTooLong) {
-  EXPECT_EQ(E_INVALIDARG, goopdate_utils::SetAppBranding(
-                                              kAppMachineClientStatePath,
-                                              _T("CHMGon.href)}"),
-                                              _T(""),
-                                              _T("")));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest, SetAppBranding_ClientIdOnly) {
-  ASSERT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T(""),
-                                                  _T("some_partner"),
-                                                  _T("")));
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("GGLS"), value);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    &value));
-  EXPECT_STREQ(_T("some_partner"), value);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueReferralId,
-                             &value));
-  DWORD install_time = 0;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueInstallTimeSec,
-                                    &install_time));
-  EXPECT_GE(now, install_time);
-  EXPECT_GE(static_cast<uint32>(200), now - install_time);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest, SetAppBranding_AllValid) {
-  ASSERT_SUCCEEDED(RegKey::CreateKey(kAppMachineClientStatePath));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T("ABCD"),
-                                                  _T("some_partner"),
-                                                  _T("referrer")));
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("ABCD"), value);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    &value));
-  EXPECT_STREQ(_T("some_partner"), value);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueReferralId,
-                                    &value));
-  EXPECT_STREQ(_T("referrer"), value);
-  DWORD install_time(0);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueInstallTimeSec,
-                                    &install_time));
-  EXPECT_GE(now, install_time);
-  EXPECT_GE(static_cast<uint32>(200), now - install_time);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppBranding_BrandAlreadyExistsAllEmpty) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    _T("EFGH")));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T(""),
-                                                  _T(""),
-                                                  _T("")));
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("EFGH"), value);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueClientId,
-                             &value));
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueReferralId,
-                             &value));
-  DWORD dword_value(0);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueInstallTimeSec,
-                             &dword_value));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppBranding_BrandAlreadyExistsBrandCodeOnly) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    _T("EFGH")));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T("ABCD"),
-                                                  _T(""),
-                                                  _T("")));
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("EFGH"), value);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueClientId,
-                             &value));
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueReferralId,
-                             &value));
-  DWORD dword_value(0);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueInstallTimeSec,
-                             &dword_value));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppBranding_ExistingBrandTooLong) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    _T("CHMG4CUTNt")));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T("ABCD"),
-                                                  _T(""),
-                                                  _T("")));
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("CHMG"), value);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueClientId,
-                             &value));
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueReferralId,
-                             &value));
-  DWORD dword_value(0);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueInstallTimeSec,
-                             &dword_value));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppBranding_BrandAlreadyExistsCliendIdOnly) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    _T("EFGH")));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T(""),
-                                                  _T("some_partner"),
-                                                  _T("")));
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("EFGH"), value);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueClientId,
-                             &value));
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueReferralId,
-                             &value));
-  DWORD dword_value(0);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueInstallTimeSec,
-                             &dword_value));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppBranding_BrandAlreadyExistsBothValid) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    _T("EFGH")));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T("ABCD"),
-                                                  _T("some_partner"),
-                                                  _T("")));
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("EFGH"), value);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueClientId,
-                             &value));
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueReferralId,
-                             &value));
-  DWORD dword_value(0);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueInstallTimeSec,
-                             &dword_value));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppBranding_ClientIdAlreadyExistsAllEmtpy) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    _T("existing_partner")));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T(""),
-                                                  _T(""),
-                                                  _T("")));
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("GGLS"), value);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    &value));
-  EXPECT_STREQ(_T("existing_partner"), value);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueReferralId,
-                             &value));
-  DWORD install_time = 0;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueInstallTimeSec,
-                                    &install_time));
-  EXPECT_GE(now, install_time);
-  EXPECT_GE(static_cast<uint32>(200), now - install_time);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppBranding_ClientIdAlreadyExistsBrandCodeOnly) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    _T("existing_partner")));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T("ABCE"),
-                                                  _T(""),
-                                                  _T("")));
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("ABCE"), value);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    &value));
-  EXPECT_STREQ(_T("existing_partner"), value);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueReferralId,
-                             &value));
-  DWORD install_time = 0;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueInstallTimeSec,
-                                    &install_time));
-  EXPECT_GE(now, install_time);
-  EXPECT_GE(static_cast<uint32>(200), now - install_time);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppBranding_ClientIdAlreadyExistsCliendIdOnly) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    _T("existing_partner")));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T(""),
-                                                  _T("some_partner"),
-                                                  _T("")));
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("GGLS"), value);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    &value));
-  EXPECT_STREQ(_T("some_partner"), value);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueReferralId,
-                             &value));
-  DWORD install_time = 0;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueInstallTimeSec,
-                                    &install_time));
-  EXPECT_GE(now, install_time);
-  EXPECT_GE(static_cast<uint32>(200), now - install_time);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppBranding_ClientIdAlreadyExistsBothValid) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    _T("existing_partner")));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T("ABCD"),
-                                                  _T("some_partner"),
-                                                  _T("")));
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("ABCD"), value);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    &value));
-  EXPECT_STREQ(_T("some_partner"), value);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueReferralId,
-                             &value));
-  DWORD install_time = 0;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueInstallTimeSec,
-                                    &install_time));
-  EXPECT_GE(now, install_time);
-  EXPECT_GE(static_cast<uint32>(200), now - install_time);
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppBranding_AllAlreadyExistAllEmpty) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    _T("EFGH")));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    _T("existing_partner")));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueReferralId,
-                                    __T("existingreferrerid")));
-  const DWORD kInstallTime = 1234567890;
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueInstallTimeSec,
-                                    kInstallTime));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T(""),
-                                                  _T(""),
-                                                  _T("")));
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("EFGH"), value);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    &value));
-  EXPECT_STREQ(_T("existing_partner"), value);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueReferralId,
-                                    &value));
-  EXPECT_STREQ(__T("existingreferrerid"), value);
-  EXPECT_EQ(kInstallTime,
-            GetDwordValue(kAppMachineClientStatePath, kRegValueInstallTimeSec));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppBranding_AllAlreadyExistBrandCodeOnly) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    _T("EFGH")));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    _T("existing_partner")));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueReferralId,
-                                    __T("existingreferrerid")));
-  const DWORD kInstallTime = 1234567890;
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueInstallTimeSec,
-                                    kInstallTime));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T("ABCD"),
-                                                  _T(""),
-                                                  _T("")));
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("EFGH"), value);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    &value));
-  EXPECT_STREQ(_T("existing_partner"), value);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueReferralId,
-                                    &value));
-  EXPECT_STREQ(__T("existingreferrerid"), value);
-  EXPECT_EQ(kInstallTime,
-            GetDwordValue(kAppMachineClientStatePath, kRegValueInstallTimeSec));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppBranding_BothAlreadyExistCliendIdOnly) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    _T("EFGH")));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    _T("existing_partner")));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueReferralId,
-                                    __T("existingreferrerid")));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T(""),
-                                                  _T("some_partner"),
-                                                  _T("")));
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("EFGH"), value);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    &value));
-  EXPECT_STREQ(_T("existing_partner"), value);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueReferralId,
-                                    &value));
-  EXPECT_STREQ(__T("existingreferrerid"), value);
-  DWORD dword_value(0);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueInstallTimeSec,
-                             &dword_value));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppBranding_BothAlreadyExistBothValid) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    _T("EFGH")));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    _T("existing_partner")));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueReferralId,
-                                    _T("existingreferrerid")));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T("ABCD"),
-                                                  _T("some_partner"),
-                                                  _T("")));
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("EFGH"), value);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueClientId,
-                                    &value));
-  EXPECT_STREQ(_T("existing_partner"), value);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueReferralId,
-                                    &value));
-  EXPECT_STREQ(_T("existingreferrerid"), value);
-  DWORD dword_value(0);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueInstallTimeSec,
-                             &dword_value));
-}
-
-TEST_F(GoopdateUtilsRegistryProtectedTest,
-       SetAppBranding_InstallTimeAlreadyExistsBrandCodeOnly) {
-  const DWORD kExistingInstallTime = 1234567890;
-  ASSERT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    kRegValueInstallTimeSec,
-                                    kExistingInstallTime));
-
-  EXPECT_SUCCEEDED(goopdate_utils::SetAppBranding(kAppMachineClientStatePath,
-                                                  _T("ABCE"),
-                                                  _T(""),
-                                                  _T("")));
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueBrandCode,
-                                    &value));
-  EXPECT_STREQ(_T("ABCE"), value);
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueClientId,
-                             &value));
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            RegKey::GetValue(kAppMachineClientStatePath,
-                             kRegValueReferralId,
-                             &value));
-  DWORD install_time = 0;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                                    kRegValueInstallTimeSec,
-                                    &install_time));
-  EXPECT_NE(kExistingInstallTime, install_time);
-  EXPECT_GE(now, install_time);
-  EXPECT_GE(static_cast<uint32>(200), now - install_time);
-}
-
-//
-// IsMachineProcess tests.
-//
-
-class GoopdateUtilsIsMachineProcessTest : public testing::Test {
- protected:
-  bool FromMachineDirHelper(CommandLineMode mode) {
-    return goopdate_utils::IsMachineProcess(mode,
-                                            true,
-                                            false,
-                                            false,
-                                            TRISTATE_NONE);
-  }
-
-  bool IsLocalSystemHelper(CommandLineMode mode) {
-    return goopdate_utils::IsMachineProcess(mode,
-                                            false,
-                                            true,
-                                            false,
-                                            TRISTATE_NONE);
-  }
-
-  bool MachineOverrideHelper(CommandLineMode mode) {
-    return goopdate_utils::IsMachineProcess(mode,
-                                            false,
-                                            false,
-                                            true,
-                                            TRISTATE_NONE);
-  }
-
-  bool NeedsAdminFalseHelper(CommandLineMode mode) {
-    return goopdate_utils::IsMachineProcess(mode,
-                                            false,
-                                            false,
-                                            false,
-                                            TRISTATE_FALSE);
-  }
-
-  bool NeedsAdminTrueHelper(CommandLineMode mode) {
-    return goopdate_utils::IsMachineProcess(mode,
-                                            false,
-                                            false,
-                                            false,
-                                            TRISTATE_TRUE);
-  }
-};
-
-TEST_F(GoopdateUtilsIsMachineProcessTest,
-       IsMachineProcess_MachineDirOnly) {
-  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_UNKNOWN));
-  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_NOARGS));
-  EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_CORE));
-  {
-    ExpectAsserts expect_asserts;
-    EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_SERVICE));
-  }
-  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_REGSERVER));
-  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_UNREGSERVER));
-  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_NETDIAGS));
-  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_CRASH));
-  // TODO(omaha): Change to machine.
-  EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_REPORTCRASH));
-  {
-    ExpectAsserts expect_asserts;
-    EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_INSTALL));
-  }
-  EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_UPDATE));
-  {
-    ExpectAsserts expect_asserts;
-    EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_IG));
-    EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_HANDOFF_INSTALL));
-  }
-  EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_UG));
-  EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_UA));
-  EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_RECOVER));
-  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_WEBPLUGIN));
-  EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_CODE_RED_CHECK));
-  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_COMSERVER));
-  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_LEGACYUI));
-  EXPECT_TRUE(FromMachineDirHelper(COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF));
-  {
-    ExpectAsserts expect_asserts;
-    EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_REGISTER_PRODUCT));
-  }
-  {
-    ExpectAsserts expect_asserts;
-    EXPECT_FALSE(FromMachineDirHelper(COMMANDLINE_MODE_UNREGISTER_PRODUCT));
-  }
-  EXPECT_TRUE(FromMachineDirHelper(
-      static_cast<CommandLineMode>(
-          COMMANDLINE_MODE_CRASH_HANDLER + 1)));
-}
-
-TEST_F(GoopdateUtilsIsMachineProcessTest,
-       IsMachineProcess_IsLocalSystemOnly) {
-  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_UNKNOWN));
-  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_NOARGS));
-  EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_CORE));
-  EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_SERVICE));
-  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_REGSERVER));
-  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_UNREGSERVER));
-  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_NETDIAGS));
-  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_CRASH));
-  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_REPORTCRASH));
-  {
-    ExpectAsserts expect_asserts;
-    EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_INSTALL));
-  }
-  EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_UPDATE));
-  {
-    ExpectAsserts expect_asserts;
-    EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_IG));
-    EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_HANDOFF_INSTALL));
-  }
-  EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_UG));
-  EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_UA));
-  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_RECOVER));
-  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_WEBPLUGIN));
-  EXPECT_TRUE(IsLocalSystemHelper(COMMANDLINE_MODE_CODE_RED_CHECK));
-  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_COMSERVER));
-  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_LEGACYUI));
-  EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF));
-  {
-    ExpectAsserts expect_asserts;
-    EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_REGISTER_PRODUCT));
-  }
-  {
-    ExpectAsserts expect_asserts;
-    EXPECT_FALSE(IsLocalSystemHelper(COMMANDLINE_MODE_UNREGISTER_PRODUCT));
-  }
-  EXPECT_FALSE(IsLocalSystemHelper(
-      static_cast<CommandLineMode>(
-          COMMANDLINE_MODE_CRASH_HANDLER + 1)));
-}
-
-TEST_F(GoopdateUtilsIsMachineProcessTest,
-       IsMachineProcess_MachineOverrideOnly) {
-  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_UNKNOWN));
-  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_NOARGS));
-  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_CORE));
-  {
-    ExpectAsserts expect_asserts;
-    EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_SERVICE));
-  }
-  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_REGSERVER));
-  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_UNREGSERVER));
-  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_NETDIAGS));
-  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_CRASH));
-  EXPECT_TRUE(MachineOverrideHelper(COMMANDLINE_MODE_REPORTCRASH));
-  {
-    ExpectAsserts expect_asserts;
-    EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_INSTALL));
-  }
-  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_UPDATE));
-  {
-    ExpectAsserts expect_asserts;
-    EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_IG));
-    EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_HANDOFF_INSTALL));
-  }
-  EXPECT_TRUE(MachineOverrideHelper(COMMANDLINE_MODE_UG));
-  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_UA));
-  EXPECT_TRUE(MachineOverrideHelper(COMMANDLINE_MODE_RECOVER));
-  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_WEBPLUGIN));
-  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_CODE_RED_CHECK));
-  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_COMSERVER));
-  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_LEGACYUI));
-  EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF));
-  {
-    ExpectAsserts expect_asserts;
-    EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_REGISTER_PRODUCT));
-  }
-  {
-    ExpectAsserts expect_asserts;
-    EXPECT_FALSE(MachineOverrideHelper(COMMANDLINE_MODE_UNREGISTER_PRODUCT));
-  }
-  EXPECT_FALSE(MachineOverrideHelper(
-      static_cast<CommandLineMode>(
-          COMMANDLINE_MODE_CRASH_HANDLER + 1)));
-}
-
-TEST_F(GoopdateUtilsIsMachineProcessTest,
-       IsMachineProcess_NeedsAdminFalseOnly) {
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_UNKNOWN));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_NOARGS));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_CORE));
-  {
-    ExpectAsserts expect_asserts;
-    EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_SERVICE));
-  }
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_REGSERVER));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_UNREGSERVER));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_NETDIAGS));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_CRASH));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_REPORTCRASH));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_INSTALL));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_UPDATE));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_IG));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_HANDOFF_INSTALL));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_UG));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_UA));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_RECOVER));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_WEBPLUGIN));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_CODE_RED_CHECK));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_COMSERVER));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_LEGACYUI));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_REGISTER_PRODUCT));
-  EXPECT_FALSE(NeedsAdminFalseHelper(COMMANDLINE_MODE_UNREGISTER_PRODUCT));
-  EXPECT_FALSE(NeedsAdminFalseHelper(
-      static_cast<CommandLineMode>(
-          COMMANDLINE_MODE_CRASH_HANDLER + 1)));
-}
-
-TEST_F(GoopdateUtilsIsMachineProcessTest,
-       IsMachineProcess_NeedsAdminTrueOnly) {
-  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_UNKNOWN));
-  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_NOARGS));
-  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_CORE));
-  {
-    ExpectAsserts expect_asserts;
-    EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_SERVICE));
-  }
-  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_REGSERVER));
-  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_UNREGSERVER));
-  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_NETDIAGS));
-  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_CRASH));
-  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_REPORTCRASH));
-  EXPECT_TRUE(NeedsAdminTrueHelper(COMMANDLINE_MODE_INSTALL));
-  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_UPDATE));
-  EXPECT_TRUE(NeedsAdminTrueHelper(COMMANDLINE_MODE_IG));
-  EXPECT_TRUE(NeedsAdminTrueHelper(COMMANDLINE_MODE_HANDOFF_INSTALL));
-  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_UG));
-  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_UA));
-  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_RECOVER));
-  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_WEBPLUGIN));
-  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_CODE_RED_CHECK));
-  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_COMSERVER));
-  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_LEGACYUI));
-  EXPECT_FALSE(NeedsAdminTrueHelper(COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF));
-  EXPECT_TRUE(NeedsAdminTrueHelper(COMMANDLINE_MODE_REGISTER_PRODUCT));
-  EXPECT_TRUE(NeedsAdminTrueHelper(COMMANDLINE_MODE_UNREGISTER_PRODUCT));
-  EXPECT_FALSE(NeedsAdminTrueHelper(
-      static_cast<CommandLineMode>(
-          COMMANDLINE_MODE_CRASH_HANDLER + 1)));
-}
-
-TEST(GoopdateUtilsTest, IsGoogleUpdate2OrLater_LegacyVersions) {
-  EXPECT_FALSE(goopdate_utils::IsGoogleUpdate2OrLater(_T("1.0.0.0")));
-  EXPECT_FALSE(goopdate_utils::IsGoogleUpdate2OrLater(_T("1.1.103.9")));
-  EXPECT_FALSE(goopdate_utils::IsGoogleUpdate2OrLater(_T("1.1.65535.65535")));
-}
-
-TEST(GoopdateUtilsTest, IsGoogleUpdate2OrLater_Omaha2AndLater) {
-  EXPECT_TRUE(goopdate_utils::IsGoogleUpdate2OrLater(_T("1.2.0.0")));
-  EXPECT_TRUE(goopdate_utils::IsGoogleUpdate2OrLater(_T("1.2.0111.2222")));
-  EXPECT_TRUE(goopdate_utils::IsGoogleUpdate2OrLater(_T("1.3.456.7890")));
-  EXPECT_TRUE(goopdate_utils::IsGoogleUpdate2OrLater(_T("2.0.0.0")));
-}
-
-TEST(GoopdateUtilsTest, IsGoogleUpdate2OrLater_VersionZero) {
-  ExpectAsserts expect_asserts;
-  EXPECT_FALSE(
-      goopdate_utils::IsGoogleUpdate2OrLater(_T("0.0.0.0")));
-}
-
-TEST(GoopdateUtilsTest, IsGoogleUpdate2OrLater_VersionUpperLimits) {
-  EXPECT_TRUE(
-      goopdate_utils::IsGoogleUpdate2OrLater(_T("65535.65535.65535.65535")));
-
-  ExpectAsserts expect_asserts;
-  EXPECT_FALSE(
-      goopdate_utils::IsGoogleUpdate2OrLater(_T("65536.65536.65536.65536")));
-  EXPECT_FALSE(
-      goopdate_utils::IsGoogleUpdate2OrLater(_T("1.2.65536.65536")));
-  EXPECT_FALSE(
-      goopdate_utils::IsGoogleUpdate2OrLater(_T("1.1.65536.65536")));
-}
-
-TEST(GoopdateUtilsTest, IsGoogleUpdate2OrLater_TooFewElements) {
-  ExpectAsserts expect_asserts;
-  EXPECT_FALSE(goopdate_utils::IsGoogleUpdate2OrLater(_T("1.1.1")));
-}
-
-TEST(GoopdateUtilsTest, IsGoogleUpdate2OrLater_ExtraPeriod) {
-  ExpectAsserts expect_asserts;
-  EXPECT_FALSE(goopdate_utils::IsGoogleUpdate2OrLater(_T("1.1.2.3.")));
-}
-
-TEST(GoopdateUtilsTest, IsGoogleUpdate2OrLater_TooManyElements) {
-  ExpectAsserts expect_asserts;
-  EXPECT_FALSE(goopdate_utils::IsGoogleUpdate2OrLater(_T("1.1.2.3.4")));
-}
-
-TEST(GoopdateUtilsTest, IsGoogleUpdate2OrLater_Char) {
-  ExpectAsserts expect_asserts;
-  EXPECT_FALSE(goopdate_utils::IsGoogleUpdate2OrLater(_T("1.B.3.4")));
-  EXPECT_FALSE(goopdate_utils::IsGoogleUpdate2OrLater(_T("1.2.3.B")));
-  EXPECT_FALSE(goopdate_utils::IsGoogleUpdate2OrLater(_T("1.2.3.9B")));
-}
-
-TEST(GoopdateUtilsTest, FormatMessageForNetworkError) {
-  const TCHAR* const kTestAppName = _T("Test App");
-  CString message;
-  EXPECT_EQ(true, FormatMessageForNetworkError(GOOPDATE_E_NO_NETWORK,
-                                               kTestAppName,
-                                               &message));
-  EXPECT_STREQ(
-      _T("Installation failed. Ensure that your computer is connected to the ")
-      _T("Internet and that your firewall allows GoogleUpdate.exe to connect ")
-      _T("and then try again. Error code = 0x80040801."),
-      message);
-
-  EXPECT_EQ(true, FormatMessageForNetworkError(GOOPDATE_E_NETWORK_UNAUTHORIZED,
-                                               kTestAppName,
-                                               &message));
-  EXPECT_STREQ(
-      _T("The Test App installer could not connect to the Internet because of ")
-      _T("an HTTP 401 Unauthorized response. This is likely a proxy ")
-      _T("configuration issue.  Please configure the proxy server to allow ")
-      _T("network access and try again or contact your network administrator. ")
-      _T("Error code = 0x80042191"),
-      message);
-
-  EXPECT_EQ(true, FormatMessageForNetworkError(GOOPDATE_E_NETWORK_FORBIDDEN,
-                                               kTestAppName,
-                                               &message));
-  EXPECT_STREQ(
-      _T("The Test App installer could not connect to the Internet because of ")
-      _T("an HTTP 403 Forbidden response. This is likely a proxy ")
-      _T("configuration issue.  Please configure the proxy server to allow ")
-      _T("network access and try again or contact your network administrator. ")
-      _T("Error code = 0x80042193"),
-      message);
-
-  EXPECT_EQ(true,
-            FormatMessageForNetworkError(GOOPDATE_E_NETWORK_PROXYAUTHREQUIRED,
-                                         kTestAppName,
-                                         &message));
-  EXPECT_STREQ(
-      _T("The Test App installer could not connect to the Internet because a ")
-      _T("proxy server required user authentication. Please configure the ")
-      _T("proxy server to allow network access and try again or contact your ")
-      _T("network administrator. Error code = 0x80042197"),
-      message);
-
-  EXPECT_EQ(false, FormatMessageForNetworkError(E_FAIL,
-                                                kTestAppName,
-                                                &message));
-  EXPECT_STREQ(
-      _T("Installation failed. Ensure that your computer is connected to the ")
-      _T("Internet and that your firewall allows GoogleUpdate.exe to connect ")
-      _T("and then try again. Error code = 0x80004005."),
-      message);
-}
-
-TEST(GoopdateUtilsTest, WriteInstallerDataToTempFile) {
-  CStringA utf8_bom;
-  utf8_bom.Format("%c%c%c", 0xEF, 0xBB, 0xBF);
-
-  std::vector<CString> list_installer_data;
-
-  list_installer_data.push_back(_T(""));
-  list_installer_data.push_back(_T("hello\n"));
-  list_installer_data.push_back(_T("good bye"));
-  list_installer_data.push_back(_T("  there  you\n     go "));
-  list_installer_data.push_back(_T("\"http://foo.bar.org/?q=stuff&h=other\""));
-  list_installer_data.push_back(_T("foo\r\nbar\n"));
-  list_installer_data.push_back(_T("foo\n\rbar"));    // LFCR is not recognized.
-
-  std::vector<CStringA> expected_installer_data;
-  expected_installer_data.push_back("");
-  expected_installer_data.push_back("hello\n");
-  expected_installer_data.push_back("good bye");
-  expected_installer_data.push_back("  there  you\n     go ");
-  expected_installer_data.push_back("\"http://foo.bar.org/?q=stuff&h=other\"");
-  expected_installer_data.push_back("foo\r\nbar\n");
-  expected_installer_data.push_back("foo\n\rbar");
-
-  ASSERT_EQ(expected_installer_data.size(), list_installer_data.size());
-
-  for (size_t i = 0; i < list_installer_data.size(); ++i) {
-    CString installer_data = list_installer_data[i];
-    SCOPED_TRACE(installer_data);
-
-    CString file_path;
-    HRESULT hr = goopdate_utils::WriteInstallerDataToTempFile(
-        installer_data,
-        &file_path);
-    EXPECT_SUCCEEDED(hr);
-
-    // TODO(omaha): consider eliminating the special case.
-    // WriteInstallerDataToTempFile() will return S_FALSE with "" data.
-    if (S_OK == hr) {
-      File file;
-      const int kBufferLen = 1000;
-      std::vector<byte> data_line(kBufferLen);
-      EXPECT_SUCCEEDED(file.Open(file_path, false, false));
-      uint32 bytes_read(0);
-      EXPECT_SUCCEEDED(file.Read(data_line.size(),
-                                 &data_line.front(),
-                                 &bytes_read));
-      data_line.resize(bytes_read);
-      data_line.push_back(0);
-      EXPECT_STREQ(utf8_bom + expected_installer_data[i],
-                   reinterpret_cast<const char*>(&data_line.front()));
-      EXPECT_SUCCEEDED(file.Close());
-    } else {
-      EXPECT_TRUE(installer_data.IsEmpty());
-    }
-  }
-}
-
-TEST(GoopdateUtilsTest, GetDefaultGoopdateTaskName_Core_Machine) {
-  CString expected_task_name(kScheduledTaskNameMachinePrefix);
-  expected_task_name += kScheduledTaskNameCoreSuffix;
-
-  EXPECT_STREQ(
-      expected_task_name,
-      goopdate_utils::GetDefaultGoopdateTaskName(true, COMMANDLINE_MODE_CORE));
-}
-
-TEST(GoopdateUtilsTest, GetDefaultGoopdateTaskName_Core_User) {
-  CString expected_task_name_user = kScheduledTaskNameUserPrefix;
-  CString user_sid;
-  EXPECT_SUCCEEDED(user_info::GetCurrentUser(NULL, NULL, &user_sid));
-  expected_task_name_user += user_sid;
-  expected_task_name_user += kScheduledTaskNameCoreSuffix;
-  EXPECT_STREQ(
-      expected_task_name_user,
-      goopdate_utils::GetDefaultGoopdateTaskName(false, COMMANDLINE_MODE_CORE));
-}
-
-TEST(GoopdateUtilsTest, GetDefaultGoopdateTaskName_UA_Machine) {
-  CString expected_task_name(kScheduledTaskNameMachinePrefix);
-  expected_task_name += kScheduledTaskNameUASuffix;
-
-  EXPECT_STREQ(
-      expected_task_name,
-      goopdate_utils::GetDefaultGoopdateTaskName(true, COMMANDLINE_MODE_UA));
-}
-
-TEST(GoopdateUtilsTest, GetDefaultGoopdateTaskName_UA_User) {
-  CString expected_task_name_user = kScheduledTaskNameUserPrefix;
-  CString user_sid;
-  EXPECT_SUCCEEDED(user_info::GetCurrentUser(NULL, NULL, &user_sid));
-  expected_task_name_user += user_sid;
-  expected_task_name_user += kScheduledTaskNameUASuffix;
-  EXPECT_STREQ(
-      expected_task_name_user,
-      goopdate_utils::GetDefaultGoopdateTaskName(false, COMMANDLINE_MODE_UA));
-}
-
-}  // namespace goopdate_utils
-
-}  // namespace omaha
-
diff --git a/goopdate/goopdate_version.rc b/goopdate/goopdate_version.rc
index 270595e..e2f94c9 100644
--- a/goopdate/goopdate_version.rc
+++ b/goopdate/goopdate_version.rc
@@ -1,4 +1,4 @@
-// Copyright 2007-2010 Google Inc.
+// Copyright 2007 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,64 +12,62 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // ========================================================================
+//
+// This file contains the unlocalized goopdate DLL version resources.
 
 #include <afxres.h>
 
-// TODO(omaha): using this as a common resource for the activex/nsplugin as
-// well as goopdate. Need to clean this up a bit.
-// For example, I've changed the FILEFLAGSMASK to 0x3FL (which appears to be
-// "standard" at least amongst plugins), not sure why it's 0x17L in here.
-// Also, the "040904e4" (for codepage 1252 Ansi Latin) is required for
-// the netscape plugin stuff to see the MIMETYPE value in the resource block.
-// However, this resource file WAS using "040904B0" (codepage 1200 which is
-// unicode little endian).  Not sure why we're using that or if we really
-// need it to be that value.
-
-// TODO(omaha): FileDescription is what shows up in "about:plugins" for
-// Mozilla. Format the description to include the url of the project.
 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
 
 VS_VERSION_INFO VERSIONINFO
+// Resource Editor does not handle constants from main.scons.
+#ifndef APSTUDIO_INVOKED
  FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_PATCH
  PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_PATCH
-// FILEFLAGSMASK 0x17L
- FILEFLAGSMASK 0x3FL
-// #ifdef _DEBUG
-// FILEFLAGS 0x1L
-//#else
+#endif  // APSTUDIO_INVOKED
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#if defined _DEBUG && OFFICIAL_BUILD
+ FILEFLAGS VS_FF_DEBUG
+#elif defined _DEBUG
+ FILEFLAGS VS_FF_DEBUG | VS_FF_PRIVATEBUILD
+#elif !OFFICIAL_BUILD
+ FILEFLAGS VS_FF_PRIVATEBUILD
+#else
  FILEFLAGS 0x0L
-//#endif
- FILEOS 0x4L
- FILETYPE 0x0L
- FILESUBTYPE 0x0L
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE VFT2_UNKNOWN
 BEGIN
     BLOCK "StringFileInfo"
     BEGIN
-        BLOCK "040904e4"
+        BLOCK "040904b0"
         BEGIN
-            VALUE "CompanyName", "Google Inc."
-            VALUE "FileDescription", "Google Update"
+// Requires constants from mains.scons that cannot be loaded in Resource Editor.
+#ifndef APSTUDIO_INVOKED
+            VALUE "CompanyName", FULL_COMPANY_NAME_ANSI
+            VALUE "FileDescription", OMAHA_APP_NAME_ANSI
             VALUE "FileVersion", VERSION_NUMBER_STRING
-            VALUE "InternalName", "Google Update"
-            VALUE "LegalCopyright", "Copyright 2007-2010 Google Inc."
-            VALUE "OriginalFilename", "GoogleUpdate"
-            VALUE "ProductName", "Google Update"
+            VALUE "InternalName", OMAHA_APP_NAME_ANSI
+            VALUE "LegalCopyright", OMAHA_COPYRIGHT_STRING_ENGLISH
+            VALUE "OriginalFilename", MAIN_DLL_BASE_NAME_ANSI ".dll"
+            VALUE "ProductName", OMAHA_APP_NAME_ANSI
             VALUE "ProductVersion", VERSION_NUMBER_STRING
-#ifdef _DEBUG
+  #ifdef _DEBUG
             VALUE "Debug", ""
-#endif
-#if !OFFICIAL_BUILD
-            VALUE "Privatebuild", ""
-#endif
-
-    #ifdef MIME_TYPE
-            VALUE "MIMEType",         MIME_TYPE
-    #endif
-
+  #endif
+  #if !OFFICIAL_BUILD
+            VALUE "PrivateBuild", BUILD_NUMBER
+  #endif
+#else
+            VALUE "_SpecialView",
+                  "Most values are not shown in Resource Editor because they "
+                  "require build file constants."
+#endif  // APSTUDIO_INVOKED
         END
     END
     BLOCK "VarFileInfo"
     BEGIN
-        VALUE "Translation", 0x409, 1252
+        VALUE "Translation", 0x0409, 1200
     END
 END
diff --git a/goopdate/goopdate_xml_parser.cc b/goopdate/goopdate_xml_parser.cc
deleted file mode 100644
index 1fdeb98..0000000
--- a/goopdate/goopdate_xml_parser.cc
+++ /dev/null
@@ -1,1627 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// goopdate_xml_parser for parsing the xml manifest files that are compiled
-// into the meta-installer and sent by the auto-update server.
-
-#include "omaha/goopdate/goopdate_xml_parser.h"
-
-#include <stdlib.h>
-#include <msxml2.h>
-#include <vector>
-#include "base/basictypes.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/error.h"
-#include "omaha/common/string.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/xml_utils.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_metrics.h"
-#include "omaha/goopdate/goopdate_utils.h"
-
-namespace omaha {
-
-namespace {
-
-// Loads the specified document and obtains the DOM and Element interfaces.
-HRESULT GetDocumentDomAndElement(const CString& file_name,
-                                 IXMLDOMDocument** document,
-                                 IXMLDOMElement** document_element) {
-  CORE_LOG(L3, (_T("[GetDocumentDomAndElement]")));
-  ASSERT1(document);
-  ASSERT1(document_element);
-
-  HRESULT hr = LoadXMLFromFile(file_name, true, document);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[LoadXMLFromFile failed][0x%x]"), hr));
-    return hr;
-  }
-
-  hr = (*document)->get_documentElement(document_element);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[get_documentElement failed][0x%x]"), hr));
-    return hr;
-  }
-  if (!*document_element) {  // Protect against msxml S_FALSE return.
-    CORE_LOG(LE, (_T("[*document_element is NULL]")));
-    return E_FAIL;
-  }
-
-  return S_OK;
-}
-
-// Loads the specified document and obtains the DOM interface.
-HRESULT GetDocumentElement(const CString& file_name,
-                           IXMLDOMElement** document_element) {
-  CComPtr<IXMLDOMDocument> document;
-  return GetDocumentDomAndElement(file_name, &document, document_element);
-}
-
-}  // namespace
-
-
-const int kGuidLen = 38;
-CString GoopdateXmlParser::seed_protocol_version;
-
-// Constants for creating the xml request.
-namespace Xml {
-  const TCHAR* const kHeaderText =
-    _T("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
-  const TCHAR* const kProcessingText =
-    _T("version=\"1.0\" encoding=\"UTF-8\"");
-
-  namespace Namespace {
-    const TCHAR* const kRequest = _T("http://www.google.com/update2/request");
-    const TCHAR* const kResponse = _T("http://www.google.com/update2/response");
-    const TCHAR* const kSeed = _T("http://www.google.com/update2/install");
-  }
-  namespace Element {
-    const TCHAR* const kXml = _T("xml");
-    const TCHAR* const kRequests = _T("gupdate");
-    const TCHAR* const kOmahaVersion = _T("updaterversion");
-    const TCHAR* const kOs = _T("os");
-    const TCHAR* const kApp = _T("app");
-    const TCHAR* const kUpdateCheck = _T("updatecheck");
-    const TCHAR* const kPing = _T("ping");
-    const TCHAR* const kEvent = _T("event");
-    const TCHAR* const kComponents = _T("components");
-    const TCHAR* const kComponent = _T("component");
-
-    const TCHAR* const kResponses = _T("gupdate");
-    const TCHAR* const kData = _T("data");
-
-    const TCHAR* const kInstall = _T("install");
-    const TCHAR* const kResponse = _T("response");
-    const TCHAR* const kNameValue = _T("attr");
-    const TCHAR* const kDayStart = _T("daystart");
-  }
-  namespace Attribute {
-    const TCHAR* const kActive = _T("active");
-    const TCHAR* const kAdditionalParameter = _T("ap");
-    const TCHAR* const kAppGuid = _T("appguid");
-    const TCHAR* const kApplicationName = _T("appname");
-    const TCHAR* const kAppId = _T("appid");
-    const TCHAR* const kArguments = _T("arguments");
-    const TCHAR* const kBrandCode = _T("brand");
-    const TCHAR* const kBrowserType = _T("browser");
-    const TCHAR* const kClientId = _T("client");
-    const TCHAR* const kCodebase = _T("codebase");
-    const TCHAR* const kCountry = _T("country");
-    const TCHAR* const kDaysSinceLastActivePing = _T("a");
-    const TCHAR* const kDaysSinceLastRollCall = _T("r");
-    const TCHAR* const kErrorCode = _T("errorcode");
-    const TCHAR* const kEventResult = _T("eventresult");
-    const TCHAR* const kEventType = _T("eventtype");
-    const TCHAR* const kErrorUrl = _T("errorurl");
-    const TCHAR* const kExtraCode1 = _T("extracode1");
-    const TCHAR* const kHash = _T("hash");
-    const TCHAR* const kIndex = _T("index");
-    const TCHAR* const kInstalledAgeDays = _T("installage");
-    const TCHAR* const kIsMachine = _T("ismachine");
-    const TCHAR* const kInstallationId = _T("iid");
-    const TCHAR* const kInstallSource = _T("installsource");
-    const TCHAR* const kLang = _T("lang");
-    const TCHAR* const kName = _T("name");
-    const TCHAR* const kNeedsAdmin = _T("needsadmin");
-    const TCHAR* const kParameter = _T("parameter");
-    const TCHAR* const kPeriodOverrideSec = _T("periodoverridesec");
-    const TCHAR* const kPlatform = _T("platform");
-    const TCHAR* const kPreviousVersion = _T("previousversion");
-    const TCHAR* const kProtocol = _T("protocol");
-    const TCHAR* const kRequestId  = _T("requestid");
-    const TCHAR* const kServicePack = _T("sp");
-    const TCHAR* const kSessionId = _T("sessionid");
-    const TCHAR* const kSignature = _T("signature");
-    const TCHAR* const kSize = _T("size");
-    const TCHAR* const kStatus = _T("status");
-    const TCHAR* const kSuccessAction = _T("onsuccess");
-    const TCHAR* const kSuccessUrl = _T("successurl");
-    const TCHAR* const kTag = _T("tag");
-    const TCHAR* const kTestSource = _T("testsource");
-    const TCHAR* const kTerminateAllBrowsers = _T("terminateallbrowsers");
-    const TCHAR* const kTTToken = _T("tttoken");
-    const TCHAR* const kUpdateDisabled = _T("updatedisabled");
-    const TCHAR* const kVersion = _T("version");
-    const TCHAR* const kXmlns = _T("xmlns");
-    const TCHAR* const kElapsedSeconds = _T("elapsed_seconds");
-  }
-
-  namespace Value {
-    const TCHAR* const kRequestType = _T("UpdateRequest");
-    const TCHAR* const kProtocol = _T("2.0");
-    const TCHAR* const kVersion2 = _T("2.0");
-    const TCHAR* const kVersion3 = _T("3.0");
-    const TCHAR* const kTrue = _T("true");
-    const TCHAR* const kFalse = _T("false");
-    const TCHAR* const kStatusError = _T("error");
-    const TCHAR* const kSuccessActionDefault = _T("default");
-    const TCHAR* const kSuccessActionExitSilently = _T("exitsilently");
-    const TCHAR* const kSuccessActionExitSilentlyOnLaunchCmd =
-        _T("exitsilentlyonlaunchcmd");
-
-    const TCHAR* const kStatusOk = kResponseStatusOkValue;
-    const TCHAR* const kInstallData = _T("install");
-  }
-}
-
-SuccessfulInstallAction GoopdateXmlParser::ConvertStringToSuccessAction(
-    const CString& text) {
-  if (text.IsEmpty() ||
-      _tcsicmp(text, Xml::Value::kSuccessActionDefault) == 0) {
-    return SUCCESS_ACTION_DEFAULT;
-  } else if (_tcsicmp(text, Xml::Value::kSuccessActionExitSilently) == 0) {
-    return SUCCESS_ACTION_EXIT_SILENTLY;
-  } else if (_tcsicmp(text,
-                      Xml::Value::kSuccessActionExitSilentlyOnLaunchCmd) == 0) {
-    return SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD;
-  } else {
-    ASSERT(false, (_T("[Unrecognized success action][%s]"), text));
-    // Use the default action. This allows Omaha to be forward-compatible with
-    // new SuccessActions, meaning older versions will not fail if a config
-    // uses a new action.
-    return SUCCESS_ACTION_DEFAULT;
-  }
-}
-
-// Implementation of the GoopdateXmlParser.
-HRESULT GoopdateXmlParser::ReadBooleanAttribute(IXMLDOMNode* node,
-                                                const TCHAR* attr_name,
-                                                bool* value) {
-  CORE_LOG(L3, (_T("[ReadBooleanAttribute][%s]"), attr_name));
-  ASSERT1(node != NULL);
-  ASSERT1(attr_name != NULL);
-  ASSERT1(value != NULL);
-
-  CComBSTR node_value;
-  HRESULT hr = ReadAttribute(node, attr_name, &node_value);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[ReadAttribute failed][%s][0x%x]"), attr_name, hr));
-    return hr;
-  }
-
-  hr = String_StringToBool(static_cast<TCHAR*>(node_value),
-                           value);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[String_StringToBool failed][0x%x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT GoopdateXmlParser::ReadIntAttribute(IXMLDOMNode* node,
-                                            const TCHAR* attr_name,
-                                            int* value) {
-  CORE_LOG(L3, (_T("[ReadIntAttribute][%s]"), attr_name));
-  ASSERT1(node != NULL);
-  ASSERT1(attr_name != NULL);
-  ASSERT1(value != NULL);
-
-  CComBSTR node_value;
-  HRESULT hr = ReadAttribute(node, attr_name, &node_value);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[ReadAttribute failed][%s][0x%x]"), attr_name, hr));
-    return hr;
-  }
-
-  if (!String_StringToDecimalIntChecked(
-          static_cast<const TCHAR*>(node_value), value)) {
-          return GOOPDATEXML_E_STRTOUINT;
-  }
-  return S_OK;
-}
-
-HRESULT GoopdateXmlParser::ReadGuidAttribute(IXMLDOMNode* node,
-                                             const TCHAR* attr_name,
-                                             GUID* value) {
-  CORE_LOG(L3, (_T("[ReadGuidAttribute][%s]"), attr_name));
-  ASSERT1(node != NULL);
-  ASSERT1(attr_name != NULL);
-  ASSERT1(value != NULL);
-
-  CComBSTR node_value;
-  HRESULT hr = ReadAttribute(node, attr_name, &node_value);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[ReadAttribute failed][%s][0x%x]"), attr_name, hr));
-    return hr;
-  }
-
-  hr = ::CLSIDFromString(static_cast<TCHAR*>(node_value), value);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[CLSIDFromString failed][0x%x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT GoopdateXmlParser::ReadStringAttribute(IXMLDOMNode* node,
-                                               const TCHAR* attr_name,
-                                               CString* value) {
-  CORE_LOG(L3, (_T("[ReadStringAttribute][%s]"), attr_name));
-  ASSERT1(node != NULL);
-  ASSERT1(attr_name != NULL);
-  ASSERT1(value != NULL);
-
-  CComBSTR node_value;
-  HRESULT hr = ReadAttribute(node, attr_name, &node_value);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[ReadAttribute failed][%s][0x%x]"), attr_name, hr));
-    return hr;
-  }
-
-  // Will extract the underlying string.
-  *value = static_cast<TCHAR*>(node_value);
-
-  return S_OK;
-}
-
-HRESULT GoopdateXmlParser::ReadAttribute(IXMLDOMNode* node,
-                                         const TCHAR* attr_name,
-                                         BSTR* value) {
-  CORE_LOG(L4, (_T("[ReadAttribute][%s]"), attr_name));
-  ASSERT1(node != NULL);
-  ASSERT1(attr_name != NULL);
-  ASSERT1(value != NULL);
-
-  // First read the attributes.
-  CComPtr<IXMLDOMNamedNodeMap> attributes;
-  HRESULT hr = node->get_attributes(&attributes);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[get_attributes failed][0x%x]"), hr));
-    return hr;
-  }
-
-  if (!attributes) {
-    CORE_LOG(LE, (_T("[Msxml S_FALSE return.]")));
-    return E_FAIL;  // Protect against msxml S_FALSE return.
-  }
-
-  CComPtr<IXMLDOMNode> attribute_node;
-  CComVariant node_value;
-  CComBSTR temp_attr_name(attr_name);
-
-  // Get the attribute using a named node.
-  hr = attributes->getNamedItem(static_cast<BSTR>(temp_attr_name),
-                                &attribute_node);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[getNamedItem failed][0x%x]"), hr));
-    return hr;
-  }
-
-  if (!attribute_node) {
-    CORE_LOG(LE, (_T("[Msxml S_FALSE return.]")));
-    return E_FAIL;  // Protect against msxml S_FALSE return.
-  }
-
-  hr = attribute_node->get_nodeValue(&node_value);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[get_nodeValue failed][0x%x]"), hr));
-    return hr;
-  }
-
-  if (node_value.vt == VT_EMPTY) {
-    CORE_LOG(LE, (_T("[node_value.vt == VT_EMPTY]")));
-    return E_FAIL;
-  }
-
-  // Extract the variant into a BSTR.
-  node_value.CopyTo(value);
-
-  return S_OK;
-}
-
-HRESULT GoopdateXmlParser::ReadStringValue(IXMLDOMNode* node,
-                                           CString* value) {
-  CORE_LOG(L4, (_T("[ReadStringValue]")));
-  ASSERT1(node != NULL);
-  ASSERT1(value != NULL);
-
-  CComPtr<IXMLDOMNodeList> child_nodes;
-  HRESULT hr = node->get_childNodes(&child_nodes);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[get_childNodes failed][0x%x]"), hr));
-    return hr;
-  }
-  if (!child_nodes) {
-    CORE_LOG(LE, (_T("[Msxml S_FALSE return.]")));
-    return E_FAIL;  // Protect against msxml S_FALSE return.
-  }
-
-  long count = 0;  // NOLINT
-  hr = child_nodes->get_length(&count);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  ASSERT(count == 1, (_T("count: %u"), count));
-  CComPtr<IXMLDOMNode> child_node;
-  hr = child_nodes->nextNode(&child_node);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  DOMNodeType type = NODE_INVALID;
-  hr = child_node->get_nodeType(&type);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[get_nodeType failed][0x%x]"), hr));
-    return hr;
-  }
-
-  if (type != NODE_TEXT) {
-    CORE_LOG(LE, (_T("[Invalid nodeType][%d]"), type));
-    return E_INVALIDARG;
-  }
-
-  CComVariant node_value;
-  hr = child_node->get_nodeValue(&node_value);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[get_nodeValue failed][0x%x]"), hr));
-    return hr;
-  }
-
-  if (node_value.vt != VT_BSTR) {
-    CORE_LOG(LE, (_T("[node_value.vt != VT_BSTR][%d]"), node_value.vt));
-    return E_INVALIDARG;
-  }
-
-  *value = V_BSTR(&node_value);
-  return S_OK;
-}
-
-HRESULT GoopdateXmlParser::ReadUpdateResponse(IXMLDOMNode* node,
-                                              UpdateResponse* response) {
-  CORE_LOG(L4, (_T("[ReadUpdateResponse]")));
-  ASSERT1(node != NULL);
-  ASSERT1(response != NULL);
-
-  UpdateResponseData response_data;
-
-  // Read GUID first since we need the GUID even on errors in order
-  // to remove the corresponding request from the jobs list.
-  GUID guid = {0};
-  HRESULT hr = ReadGuidAttribute(node, Xml::Attribute::kAppId, &guid);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[ReadGuidAttribute failed][0x%x]"), hr));
-    return hr;
-  }
-  response_data.set_guid(guid);
-
-  CString str;
-  // Any response status but "ok" for the "app" element stops the "parsing".
-  hr = ReadStringAttribute(node, Xml::Attribute::kStatus, &str);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[ReadStringAttribute failed][0x%x]"), hr));
-    return hr;
-  }
-  if (str != Xml::Value::kStatusOk) {
-    response_data.set_status(str);
-    response->set_update_response_data(response_data);
-    return S_OK;
-  }
-
-  // Now try and read the children nodes of the response.
-  CComPtr<IXMLDOMNodeList> child_nodes;
-  // Get all the children of the document.
-  hr = node->get_childNodes(&child_nodes);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[get_childNodes failed][0x%x]"), hr));
-    return hr;
-  }
-  if (!child_nodes) {
-    CORE_LOG(LE, (_T("[Msxml S_FALSE return.]")));
-    return E_FAIL;  // Protect against msxml S_FALSE return.
-  }
-
-  // Go over all the children and handle the children. We ignore all the
-  // children that we do not understand. Note that we expect the nodes that
-  // we want to be present. Although we are not enforcing this for now.
-  CComPtr<IXMLDOMNode> child_node;
-  while (child_nodes->nextNode(&child_node) != S_FALSE) {
-    XMLFQName node_name;
-    hr = GetXMLFQName(child_node, &node_name);
-    if (FAILED(hr)) {
-      CORE_LOG(LE, (_T("[GetXMLFQName failed][0x%x]"), hr));
-      return hr;
-    }
-
-    if (node_name.base == Xml::Element::kUpdateCheck) {
-      hr = ReadStringAttribute(child_node, Xml::Attribute::kTTToken, &str);
-      if (SUCCEEDED(hr) && !str.IsEmpty()) {
-        response_data.set_tt_token(str);
-      }
-
-      hr = ReadStringAttribute(child_node, Xml::Attribute::kStatus, &str);
-      if (FAILED(hr)) { return hr; }
-      response_data.set_status(str);
-
-      if (str == Xml::Value::kStatusOk) {
-        int size = 0;
-        hr = ReadIntAttribute(child_node, Xml::Attribute::kSize, &size);
-        if (FAILED(hr)) { return hr; }
-        if (size < 0) {
-          return GOOPDATEXML_E_STRTOUINT;
-        }
-        response_data.set_size(size);
-
-        hr = ReadStringAttribute(child_node, Xml::Attribute::kHash, &str);
-        if (FAILED(hr)) { return hr; }
-        response_data.set_hash(str);
-
-        hr = ReadStringAttribute(child_node, Xml::Attribute::kCodebase, &str);
-        if (FAILED(hr)) { return hr; }
-        response_data.set_url(str);
-
-        hr = ReadStringAttribute(child_node, Xml::Attribute::kNeedsAdmin, &str);
-        if (FAILED(hr)) { return hr; }
-        NeedsAdmin needs_admin;
-        hr = goopdate_utils::ConvertStringToNeedsAdmin(str, &needs_admin);
-        if (FAILED(hr)) { return hr; }
-        response_data.set_needs_admin(needs_admin);
-
-        hr = ReadStringAttribute(child_node, Xml::Attribute::kArguments, &str);
-        // arguments is optional
-        if (SUCCEEDED(hr)) {
-          response_data.set_arguments(str);
-        }
-
-        hr = ReadStringAttribute(child_node, Xml::Attribute::kSuccessUrl, &str);
-        if (SUCCEEDED(hr)) {
-          response_data.set_success_url(str);
-        }
-
-        bool terminate_all_browsers = false;
-        hr = ReadBooleanAttribute(child_node,
-                                  Xml::Attribute::kTerminateAllBrowsers,
-                                  &terminate_all_browsers);
-        if (SUCCEEDED(hr)) {
-          response_data.set_terminate_all_browsers(terminate_all_browsers);
-        }
-
-        hr = ReadStringAttribute(child_node,
-                                 Xml::Attribute::kSuccessAction,
-                                 &str);
-        if (SUCCEEDED(hr)) {
-          response_data.set_success_action(ConvertStringToSuccessAction(str));
-        }
-
-        // If no version exists in the server response, we will still indicate
-        // when an update is available. However, we will not provide version
-        // information to any JobObserver.
-        hr = ReadStringAttribute(child_node,
-                                 Xml::Attribute::kVersion,
-                                 &str);
-        if (SUCCEEDED(hr)) {
-          response_data.set_version(str);
-        }
-      }
-
-      // Always look for the error URL because it is used in errors.
-      hr = ReadStringAttribute(child_node, Xml::Attribute::kErrorUrl, &str);
-      if (SUCCEEDED(hr)) {
-        response_data.set_error_url(str);
-      }
-    } else if (node_name.base == Xml::Element::kData) {
-      hr = ReadStringAttribute(child_node, Xml::Attribute::kStatus, &str);
-      if (FAILED(hr)) {
-        return hr;
-      }
-
-      if (str == Xml::Value::kStatusOk) {
-        // <data name="install" index="foo" status="ok">foo bar</data>.
-        hr = ReadStringAttribute(child_node, Xml::Attribute::kName, &str);
-        if (FAILED(hr)) {
-          return hr;
-        }
-        if (str.CompareNoCase(Xml::Value::kInstallData)) {
-          CORE_LOG(LW, (_T("[Skipping unsupported data][%s]"), str));
-          continue;
-        }
-
-        CString install_data_index;
-        hr = ReadStringAttribute(child_node,
-                                 Xml::Attribute::kIndex,
-                                 &install_data_index);
-        if (FAILED(hr)) {
-          return hr;
-        }
-
-        CString install_data;
-        hr = ReadStringValue(child_node, &install_data);
-        if (FAILED(hr)) {
-          return hr;
-        }
-        response_data.SetInstallData(install_data_index, install_data);
-      }
-    } else if (node_name.base == Xml::Element::kPing) {
-      // nothing to do here (yet)
-    } else if (node_name.base == Xml::Element::kEvent) {
-      // nothing to do here (yet)
-    } else if (node_name.base == Xml::Element::kComponents) {
-      VERIFY1(SUCCEEDED(ReadComponentsResponses(child_node, response)));
-    }
-    child_node = NULL;
-  }
-
-  response->set_update_response_data(response_data);
-
-  return S_OK;
-}
-
-HRESULT GoopdateXmlParser::ReadComponentsResponses(IXMLDOMNode* node,
-                                                   UpdateResponse* response) {
-  CORE_LOG(L4, (_T("[ReadComponentsResponses]")));
-  ASSERT1(node);
-  ASSERT1(response);
-
-  CComPtr<IXMLDOMNodeList> child_nodes;
-  HRESULT hr = node->get_childNodes(&child_nodes);
-  if (FAILED(hr)) { return hr; }
-  // If S_FALSE, then there are no children components.
-  if (S_FALSE == hr) { return S_OK; }
-
-  CComPtr<IXMLDOMNode> child_node;
-  while (child_nodes->nextNode(&child_node) != S_FALSE) {
-    XMLFQName node_name;
-    hr = GetXMLFQName(child_node, &node_name);
-    if (FAILED(hr)) { return hr; }
-
-    if (node_name.base == Xml::Element::kComponent) {
-      UpdateResponseData component_response_data;
-      hr = ReadComponentResponseData(child_node, &component_response_data);
-      if (FAILED(hr)) { return hr; }
-      response->AddComponentResponseData(component_response_data);
-    } else {
-      ASSERT1(false);
-    }
-
-    child_node = NULL;
-  }
-
-  return S_OK;
-}
-
-HRESULT GoopdateXmlParser::ReadComponentResponseData(
-    IXMLDOMNode* node,
-    UpdateResponseData* response_data) {
-  CORE_LOG(L4, (_T("[ReadComponentsResponseData]")));
-  // TODO(omaha): consolidate component/product parsing. They have several
-  // similar attributes.
-  // Read GUID first since we need the GUID even on errors in order to remove
-  // the corresponding request from the jobs list.
-  GUID guid = {0};
-  HRESULT hr = ReadGuidAttribute(node, Xml::Attribute::kAppId, &guid);
-  if (FAILED(hr)) { return hr; }
-  response_data->set_guid(guid);
-
-  // Any response status but "ok" for the "app" element stops the "parsing".
-  CString str;
-  hr = ReadStringAttribute(node, Xml::Attribute::kStatus, &str);
-  if (FAILED(hr)) { return hr; }
-  if (str != Xml::Value::kStatusOk) {
-    response_data->set_status(str);
-    return S_OK;
-  }
-
-  // Now try and read the children nodes of the response.
-  CComPtr<IXMLDOMNodeList> child_nodes;
-  // Get all the children of the document.
-  hr = node->get_childNodes(&child_nodes);
-  if (FAILED(hr)) { return hr; }
-  if (!child_nodes) { return E_FAIL; }  // Protect against msxml S_FALSE return.
-
-  // Go over all the children and handle the children. We ignore all the
-  // children that we do not understand. Note that we expect the nodes that
-  // we want to be present. Although we are not enforcing this for now.
-  CComPtr<IXMLDOMNode> child_node;
-  while (child_nodes->nextNode(&child_node) != S_FALSE) {
-    XMLFQName node_name;
-    hr = GetXMLFQName(child_node, &node_name);
-    if (FAILED(hr)) { return hr; }
-
-    if (node_name.base == Xml::Element::kUpdateCheck) {
-      hr = ReadStringAttribute(child_node, Xml::Attribute::kStatus, &str);
-      if (FAILED(hr)) { return hr; }
-      response_data->set_status(str);
-      // TODO(omaha):  Check why we have kStatusOk at both levels of the XML
-      // (same for product parsing) and consolidate/remove as necessary.
-      if (str == Xml::Value::kStatusOk) {
-        int size = 0;
-        hr = ReadIntAttribute(child_node, Xml::Attribute::kSize, &size);
-        if (FAILED(hr)) { return hr; }
-        if (size < 0) {
-          return GOOPDATEXML_E_STRTOUINT;
-        }
-        response_data->set_size(size);
-
-        hr = ReadStringAttribute(child_node, Xml::Attribute::kHash, &str);
-        if (FAILED(hr)) { return hr; }
-        response_data->set_hash(str);
-
-        hr = ReadStringAttribute(child_node, Xml::Attribute::kCodebase, &str);
-        if (FAILED(hr)) { return hr; }
-        response_data->set_url(str);
-
-        hr = ReadStringAttribute(child_node, Xml::Attribute::kArguments, &str);
-        // arguments is optional
-        if (SUCCEEDED(hr)) {
-          response_data->set_arguments(str);
-        }
-      }
-    } else {
-      ASSERT1(false);
-    }
-
-    child_node = NULL;
-  }
-
-  return S_OK;
-}
-
-// This exists only to support handoffs from 1.0.x and 1.1.x metainstallers.
-HRESULT GoopdateXmlParser::ReadInstallElement(
-    IXMLDOMNode* node,
-    UpdateResponseData* response_data) {
-  ASSERT1(node);
-  ASSERT1(response_data);
-
-  HRESULT hr = ReadRequiredInstallAttributes(node, response_data);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return ReadOptionalInstallAttributes(node, response_data);
-}
-
-// This exists only to support handoffs from 1.0.x and 1.1.x metainstallers.
-HRESULT GoopdateXmlParser::ReadRequiredInstallAttributes(
-    IXMLDOMNode* node,
-    UpdateResponseData* response_data) {
-  ASSERT1(node);
-  ASSERT1(response_data);
-
-  // Read the guid and whether the application needs admin. Both the attributes
-  // are required.
-  GUID guid_value = {0};
-  HRESULT hr = ReadGuidAttribute(node, Xml::Attribute::kAppGuid,
-                                 &guid_value);
-  if (FAILED(hr)) { return hr; }
-
-  response_data->set_guid(guid_value);
-
-  CString needs_admin_str;
-  hr = ReadStringAttribute(node, Xml::Attribute::kNeedsAdmin,
-                           &needs_admin_str);
-  if (FAILED(hr)) { return hr; }
-
-  NeedsAdmin needs_admin;
-  hr = goopdate_utils::ConvertStringToNeedsAdmin(needs_admin_str, &needs_admin);
-  if (FAILED(hr)) { return hr; }
-
-  response_data->set_needs_admin(needs_admin);
-
-  // We only read the language and the application name from the seed manifest
-  // if the version of the seed manifest is greater than 2.0.
-  if (String_StringToDouble(seed_protocol_version) >
-      String_StringToDouble(Xml::Value::kVersion2)) {
-    ++metric_handoff_legacy_11;
-
-    CString app_name;
-    hr = ReadStringAttribute(node, Xml::Attribute::kApplicationName, &app_name);
-    if (FAILED(hr)) { return hr; }
-    response_data->set_app_name(app_name);
-
-    CString language;
-    hr = ReadStringAttribute(node, Xml::Attribute::kLang, &language);
-    if (FAILED(hr)) { return hr; }
-    response_data->set_language(language);
-  } else {
-    ++metric_handoff_legacy_10;
-  }
-
-  return S_OK;
-}
-
-// Since all of these attributes are optional, read failures do not cause this
-// method to fail.
-// This exists only to support handoffs from 1.0.x and 1.1.x metainstallers.
-HRESULT GoopdateXmlParser::ReadOptionalInstallAttributes(
-    IXMLDOMNode* node,
-    UpdateResponseData* response_data) {
-  ASSERT1(node);
-  ASSERT1(response_data);
-
-  CString iid;
-  HRESULT hr = ReadStringAttribute(node, Xml::Attribute::kInstallationId, &iid);
-  if (SUCCEEDED(hr)) {
-    response_data->set_installation_id(StringToGuid(iid));
-  }
-
-  CString ap;
-  hr = ReadStringAttribute(node, Xml::Attribute::kAdditionalParameter, &ap);
-  if (SUCCEEDED(hr)) {
-    response_data->set_ap(ap);
-  }
-
-  CString browser_type;
-  hr = ReadStringAttribute(node, Xml::Attribute::kBrowserType, &browser_type);
-  if (SUCCEEDED(hr)) {
-    BrowserType type = BROWSER_UNKNOWN;
-    hr = goopdate_utils::ConvertStringToBrowserType(browser_type, &type);
-    if (SUCCEEDED(hr)) {
-      response_data->set_browser_type(type);
-    }
-  }
-
-  return S_OK;
-}
-
-// Assumes all higher level validity checks have been done
-HRESULT GoopdateXmlParser::ReadUpdateResponses(
-    IXMLDOMNode* node,
-    UpdateResponses* responses) {
-  ASSERT1(node != NULL);
-  ASSERT1(responses != NULL);
-
-  int time_since_midnight_sec(0);
-  HRESULT hr = ReadTimeSinceMidnightSec(node, &time_since_midnight_sec);
-  if (FAILED(hr)) { return hr; }
-
-  CComPtr<IXMLDOMNodeList> child_nodes;
-  // Get all the children of the Node.
-  hr = node->get_childNodes(&child_nodes);
-  if (FAILED(hr)) { return hr; }
-  if (!child_nodes) { return E_FAIL; }  // Protect against msxml S_FALSE return.
-
-  // Go Over all the children and read each of them. we will ignore ones that
-  // we dont understand.
-  hr = child_nodes->reset();
-  if (FAILED(hr)) { return hr; }
-
-  CComPtr<IXMLDOMNode> child_node;
-  while (child_nodes->nextNode(&child_node) != S_FALSE) {
-    XMLFQName child_node_name;
-    hr = GetXMLFQName(child_node, &child_node_name);
-    if (FAILED(hr)) { return hr; }
-
-    if (child_node_name.base == Xml::Element::kApp) {
-      // we got a response we should read that in.
-      UpdateResponse response;
-      response.set_time_since_midnight_sec(time_since_midnight_sec);
-      hr = ReadUpdateResponse(child_node, &response);
-      if (FAILED(hr)) { return hr; }
-
-      const GUID& response_guid = response.update_response_data().guid();
-      ASSERT1(responses->find(response_guid) == responses->end());
-      (*responses)[response_guid] = response;
-    }
-    child_node = NULL;
-  }
-  return S_OK;
-}
-
-// This exists only to support handoffs from 1.0.x and 1.1.x metainstallers.
-HRESULT GoopdateXmlParser::ReadSeedInstalls(IXMLDOMNode* node,
-                                            UpdateResponses* responses) {
-  ASSERT1(node != NULL);
-  ASSERT1(responses != NULL);
-
-  CComPtr<IXMLDOMNodeList> child_nodes;
-  // Get all the children of the Node.
-  HRESULT hr = node->get_childNodes(&child_nodes);
-  if (FAILED(hr)) { return hr; }
-  if (!child_nodes) { return E_FAIL; }  // Protect against msxml S_FALSE return.
-
-  // Go Over all the children and read each of them. Since seed files are
-  // always of a version <= to this code, error if we run into a child we don't
-  // recognize.
-  hr = child_nodes->reset();
-  if (FAILED(hr)) { return hr; }
-
-  CComPtr<IXMLDOMNode> child_node;
-  while (child_nodes->nextNode(&child_node) != S_FALSE) {
-    XMLFQName child_node_name;
-    hr = GetXMLFQName(child_node, &child_node_name);
-    if (FAILED(hr)) { return hr; }
-
-    if (child_node_name.base == Xml::Element::kInstall) {
-      // We found an install node. We should read that in.
-      UpdateResponseData response_data;
-      hr = ReadInstallElement(child_node, &response_data);
-      if (FAILED(hr)) { return hr; }
-
-      UpdateResponse response(response_data);
-      const GUID& response_guid =  response.update_response_data().guid();
-      ASSERT1(responses->find(response_guid) == responses->end());
-      (*responses)[response_guid] = response;
-
-      child_node = NULL;
-    } else {
-      child_node = NULL;
-      return E_FAIL;
-    }
-  }
-  return S_OK;
-}
-
-HRESULT GoopdateXmlParser::GetProtocolVersion(IXMLDOMElement* document_element,
-                                              CString* version) {
-  ASSERT1(document_element);
-  ASSERT1(version);
-
-  CString protocol_version;
-  return ReadStringAttribute(document_element,
-                             Xml::Attribute::kProtocol,
-                             version);
-}
-
-HRESULT GoopdateXmlParser::VerifyElementProtocolCompatibility(
-    IXMLDOMElement* document_element,
-    const CString& expected_version) {
-  ASSERT1(document_element);
-
-  CString protocol_version;
-  HRESULT hr = GetProtocolVersion(document_element, &protocol_version);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return VerifyProtocolCompatibility(protocol_version, expected_version);
-}
-
-// Returns GOOPDATEXML_E_XMLVERSION when the actual_ver is not between
-// the start_ver and the end_ver, both inclusive.
-HRESULT GoopdateXmlParser::VerifyProtocolRange(const CString& actual_ver,
-                                               const CString& start_ver,
-                                               const CString& end_ver) {
-  const double version = String_StringToDouble(actual_ver);
-  const double start = String_StringToDouble(start_ver);
-  const double end = String_StringToDouble(end_ver);
-  if (version < start || version > end) {
-    return GOOPDATEXML_E_XMLVERSION;
-  }
-
-  return S_OK;
-}
-
-// Verify that the protocol version is one we understand.  We accept
-// all version numbers where major version is the same as kExpectedVersion
-// which are greater than or equal to kExpectedVersion. In other words,
-// we handle future minor version number increases which should be
-// compatible.
-HRESULT GoopdateXmlParser::VerifyProtocolCompatibility(
-    const CString& actual_version,
-    const CString& expected_version) {
-  if (_tcscmp(actual_version, expected_version) != 0) {
-    const double version = String_StringToDouble(actual_version);
-    const double expected = String_StringToDouble(expected_version);
-    if (expected > version) {
-      return GOOPDATEXML_E_XMLVERSION;
-    }
-    const int version_major = static_cast<int>(version);
-    const int expected_major = static_cast<int>(expected);
-    if (version_major != expected_major) {
-      return GOOPDATEXML_E_XMLVERSION;
-    }
-  }
-
-  return S_OK;
-}
-
-// Currently, this method only validates the name and protocol version.
-HRESULT GoopdateXmlParser::ValidateResponseElement(
-    IXMLDOMElement* document_element) {
-  CComBSTR root_name;
-  HRESULT hr = document_element->get_baseName(&root_name);
-  if (FAILED(hr)) { return hr; }
-  XMLFQName doc_element_name(Xml::Namespace::kResponse, root_name);
-  if (doc_element_name.base != Xml::Element::kResponses) {
-    return GOOPDATEXML_E_RESPONSESNODE;
-  }
-
-  return VerifyElementProtocolCompatibility(document_element,
-                                            Xml::Value::kVersion2);
-}
-
-// When the server sends the update response back to client, it includes an
-// XML elements as <daystart elapsed_seconds='###'> where ### is the number
-// of seconds since the midnight on server. The client then uses this number to
-// figure out the corresponding local UTC time. This local time stamp is used
-// to determine whether a following ping need to be sent or postponed. Basically
-// client should not send the same type of ping in each server day separated by
-// server's midnight.
-// Since the clock time used on client side is relative value, it doesn't
-// matter if the client's clock is off. But if it's clock runs slower or
-// faster, it may affect the calculation, esp. when server and client don't
-// have time sync-ed for a long time.
-HRESULT GoopdateXmlParser::ReadTimeSinceMidnightSec(IXMLDOMNode* node,
-    int* time_since_midnight_sec) {
-  ASSERT1(node != NULL);
-  ASSERT1(time_since_midnight_sec != NULL);
-
-  CComQIPtr<IXMLDOMElement> manifest = node;
-  CComPtr<IXMLDOMNodeList> child_nodes;
-  CComBSTR target_node_name(Xml::Element::kDayStart);
-
-  HRESULT hr = manifest->getElementsByTagName(
-                             static_cast<BSTR>(target_node_name),
-                             &child_nodes);
-  if (FAILED(hr)) { return hr; }
-  if (!child_nodes) { return E_FAIL; }  // Protect against msxml S_FALSE return.
-
-  long num_elements(0);   // NOLINT
-  hr = child_nodes->get_length(&num_elements);
-  if (FAILED(hr)) { return hr; }
-  if (num_elements == 0) {
-    return S_FALSE;
-  } else if (num_elements != 1) {
-    CORE_LOG(LE,
-             (_T("[ReadTimeSinceMidnightSec sees %d element(s) with name %s]"),
-             num_elements, Xml::Element::kDayStart));
-    return E_FAIL;
-  }
-
-  hr = child_nodes->reset();
-  if (FAILED(hr)) { return hr; }
-
-  CComPtr<IXMLDOMNode> child_node;
-  hr = child_nodes->get_item(0, &child_node);
-  if (FAILED(hr)) { return hr; }
-
-  *time_since_midnight_sec = 0;
-  hr = ReadIntAttribute(child_node,
-                        Xml::Attribute::kElapsedSeconds,
-                        time_since_midnight_sec);
-  if (FAILED(hr)) {
-    CORE_LOG(LE,
-             (_T("[ReadTimeSinceMidnightSec: read attribute %s failed][0x%x]"),
-             Xml::Attribute::kElapsedSeconds, hr));
-    return hr;
-  }
-  if (*time_since_midnight_sec < 0 ||
-      *time_since_midnight_sec >= kMaxTimeSinceMidnightSec) {
-    CORE_LOG(LE,
-             (_T("[ReadTimeSinceMidnightSec: attribute %s value invalid: %d]"),
-             Xml::Attribute::kElapsedSeconds, *time_since_midnight_sec));
-    return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
-  }
-
-  return S_OK;
-}
-
-
-HRESULT GoopdateXmlParser::ParseManifestFile(const CString& file_name,
-                                             UpdateResponses* responses) {
-  if (responses == NULL) {
-    return E_INVALIDARG;
-  }
-
-  CComPtr<IXMLDOMElement> document_element;
-  HRESULT hr = GetDocumentElement(file_name, &document_element);
-  if (FAILED(hr)) { return hr; }
-
-  return ParseManifest(document_element, responses);
-}
-
-HRESULT GoopdateXmlParser::ParseManifestBytes(
-    const std::vector<byte>& manifest_bytes,
-    UpdateResponses* responses) {
-  ASSERT1(responses);
-  CComPtr<IXMLDOMDocument> document;
-  HRESULT hr = LoadXMLFromRawData(manifest_bytes, false, &document);
-  if (FAILED(hr)) { return hr; }
-
-  CComPtr<IXMLDOMElement> document_element;
-  hr = document->get_documentElement(&document_element);
-  if (FAILED(hr)) { return hr; }
-  if (!document_element) {  // Protect against msxml S_FALSE return.
-    return E_FAIL;
-  }
-
-  return ParseManifest(document_element, responses);
-}
-
-HRESULT GoopdateXmlParser::ParseManifest(IXMLDOMElement* manifest,
-                                         UpdateResponses* responses) {
-  ASSERT1(manifest);
-  ASSERT1(responses);
-
-  CComBSTR uri;
-  HRESULT hr = manifest->get_namespaceURI(&uri);
-  if (FAILED(hr)) { return hr; }
-
-  if (uri == Xml::Namespace::kResponse) {
-    hr = ValidateResponseElement(manifest);
-    if (FAILED(hr)) { return hr; }
-
-    hr = ReadUpdateResponses(manifest, responses);
-    if (FAILED(hr)) { return hr; }
-  } else if (uri == Xml::Namespace::kSeed) {
-    // TODO(omaha): We should probably verify the name too.
-    hr = GetProtocolVersion(manifest, &seed_protocol_version);
-    if (FAILED(hr)) { return hr; }
-
-    hr = VerifyProtocolRange(seed_protocol_version,
-                             Xml::Value::kVersion2,
-                             Xml::Value::kVersion3);
-    if (FAILED(hr)) { return hr; }
-
-    hr = ReadSeedInstalls(manifest, responses);
-    if (FAILED(hr)) { return hr; }
-  } else {
-    return GOOPDATEXML_E_UNEXPECTED_URI;
-  }
-
-  return S_OK;
-}
-
-HRESULT GoopdateXmlParser::CreateRequestElement(
-    const TCHAR* xml_element_name,
-    const CString& value,
-    IXMLDOMDocument* document,
-    IXMLDOMNode** request_element) {
-
-  ASSERT1(xml_element_name != NULL);
-  ASSERT1(document != NULL);
-  ASSERT1(request_element != NULL);
-
-  // request element names get o: prepended to avoid a size explosion where
-  // the namespace attribute gets automatically added to every element by
-  // msxml
-  CString name;
-  name.Format(_T("o:%s"), xml_element_name);
-
-  HRESULT hr = CreateXMLNode(document,
-                             NODE_ELEMENT,
-                             name,
-                             Xml::Namespace::kRequest,
-                             value,
-                             request_element);
-  return hr;
-}
-
-HRESULT GoopdateXmlParser::CreateAndAddRequestElement(
-    const TCHAR* xml_element_name,
-    const CString& value,
-    IXMLDOMDocument* document,
-    IXMLDOMNode* parent) {
-
-  ASSERT1(xml_element_name != NULL);
-  ASSERT1(document != NULL);
-  ASSERT1(parent != NULL);
-
-  CComPtr<IXMLDOMNode> element;
-  HRESULT hr = CreateRequestElement(xml_element_name,
-                                    value,
-                                    document,
-                                    &element);
-  if (FAILED(hr)) { return hr; }
-
-  hr = parent->appendChild(element, NULL);
-  if (FAILED(hr)) { return hr; }
-
-  return S_OK;
-}
-
-HRESULT GoopdateXmlParser::AddAttributesToNode(
-    const CString& namespace_uri,
-    const std::vector<XMLNameValuePair>& attributes,
-    IXMLDOMNode* element) {
-  ASSERT1(element != NULL);
-  HRESULT hr;
-  for (size_t i = 0; i < attributes.size(); ++i) {
-    hr = AddXMLAttributeNode(element,
-                             namespace_uri,
-                             attributes[i].first,
-                             attributes[i].second);
-    if (FAILED(hr)) { return hr; }
-  }
-  return S_OK;
-}
-
-HRESULT GoopdateXmlParser::CreateAppRequestElementHelper(
-    const AppRequest& app_request,
-    IXMLDOMDocument* document,
-    IXMLDOMNode** app_element) {
-  ASSERT1(app_element);
-  ASSERT1(document);
-
-  const AppData& app_data = app_request.request_data().app_data();
-
-  // Create the request node.
-  HRESULT hr = CreateRequestElement(Xml::Element::kApp,
-                                    _T(""),
-                                    document,
-                                    app_element);
-  if (FAILED(hr)) { return hr; }
-
-  // Add the appid to the app node.
-  // appid=""       // The application Id.
-  const int guid_len = kGuidLen;
-  TCHAR guid_str[guid_len + 1] = { _T('\0') };
-  if (StringFromGUID2(app_data.app_guid(), guid_str, guid_len + 1) <= 0) {
-    return E_FAIL;
-  }
-  if (FAILED(hr)) { return hr; }
-
-  // Saves about 600 bytes of code size by creating and destroying the
-  // array inside of the statement block.
-  {
-    // Create the list of attributes and the values that need to be added.
-    const XMLNameValuePair elements[] = {
-        std::make_pair(Xml::Attribute::kAppId, guid_str),
-        std::make_pair(Xml::Attribute::kVersion, app_data.version()),
-        std::make_pair(Xml::Attribute::kLang, app_data.language()),
-        std::make_pair(Xml::Attribute::kBrandCode, app_data.brand_code()),
-        std::make_pair(Xml::Attribute::kClientId, app_data.client_id())
-    };
-
-    for (size_t i = 0; i < arraysize(elements); ++i) {
-      hr = AddXMLAttributeNode(*app_element,
-                               Xml::Namespace::kRequest,
-                               elements[i].first,
-                               elements[i].second);
-      if (FAILED(hr)) { return hr; }
-    }
-  }
-
-  // 0 seconds indicates unknown install time. A new install uses -1 days.
-  if (0 != app_data.install_time_diff_sec()) {
-    const int installed_full_days =
-        static_cast<int>(app_data.install_time_diff_sec()) / kSecondsPerDay;
-    ASSERT1(installed_full_days >= 0 || -1 == installed_full_days);
-    hr = AddXMLAttributeNode(*app_element,
-                             Xml::Namespace::kRequest,
-                             Xml::Attribute::kInstalledAgeDays,
-                             itostr(installed_full_days));
-    if (FAILED(hr)) { return hr; }
-  }
-
-  if (GUID_NULL != app_data.iid()) {
-    hr = AddXMLAttributeNode(*app_element,
-                             Xml::Namespace::kRequest,
-                             Xml::Attribute::kInstallationId,
-                             GuidToString(app_data.iid()));
-    if (FAILED(hr)) { return hr; }
-  }
-
-  if (!app_data.install_source().IsEmpty()) {
-    hr = AddXMLAttributeNode(*app_element,
-                             Xml::Namespace::kRequest,
-                             Xml::Attribute::kInstallSource,
-                             app_data.install_source());
-    if (FAILED(hr)) { return hr; }
-  }
-
-  // Create and add component elements to the app element.
-  if (app_request.num_components() > 0) {
-    CComPtr<IXMLDOMNode> components_node;
-    hr = CreateRequestElement(Xml::Element::kComponents,
-                              _T(""),
-                              document,
-                              &components_node);
-    if (FAILED(hr)) { return hr; }
-
-    hr = (*app_element)->appendChild(components_node, NULL);
-    if (FAILED(hr)) { return hr; }
-
-    AppRequestDataVector::const_iterator it;
-    for (it = app_request.components_begin();
-         it != app_request.components_end();
-         ++it) {
-      const AppRequestData& component_request_data = *it;
-      const AppData& component_app_data = component_request_data.app_data();
-      CComPtr<IXMLDOMNode> component_node;
-      hr = CreateRequestElement(Xml::Element::kComponent,
-                                _T(""),
-                                document,
-                                &component_node);
-      if (FAILED(hr)) { return hr; }
-
-      hr = AddXMLAttributeNode(component_node,
-                               Xml::Namespace::kRequest,
-                               Xml::Attribute::kAppId,
-                               GuidToString(component_app_data.app_guid()));
-      if (FAILED(hr)) { return hr; }
-
-      hr = AddXMLAttributeNode(component_node,
-                               Xml::Namespace::kRequest,
-                               Xml::Attribute::kVersion,
-                               component_app_data.version());
-      if (FAILED(hr)) { return hr; }
-
-      hr = components_node->appendChild(component_node, NULL);
-      if (FAILED(hr)) { return hr; }
-
-      // TODO(omaha):  Create and add event elements to the component element
-      // by traversing the ping_events within component_request_data.
-      // Probably want to make the PingEvent traversal from below into a
-      // separate function.
-    }
-  }
-
-  return S_OK;
-}
-
-HRESULT GoopdateXmlParser::CreateUpdateAppRequestElement(
-    const AppRequest& app_request,
-    IXMLDOMDocument* document,
-    IXMLDOMNode** app_element) {
-  HRESULT hr = CreateAppRequestElementHelper(app_request,
-                                             document,
-                                             app_element);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  const AppData& app_data = app_request.request_data().app_data();
-
-  // update check element
-  CComPtr<IXMLDOMNode> update_check;
-  hr = CreateRequestElement(Xml::Element::kUpdateCheck,
-                            _T(""),
-                            document,
-                            &update_check);
-  if (FAILED(hr)) { return hr; }
-
-  if (app_data.is_update_disabled()) {
-    hr = AddXMLAttributeNode(update_check,
-                             Xml::Namespace::kRequest,
-                             Xml::Attribute::kUpdateDisabled,
-                             Xml::Value::kTrue);
-    if (FAILED(hr)) { return hr; }
-  }
-
-  // tag is optional
-  if (!app_data.ap().IsEmpty()) {
-    hr = AddXMLAttributeNode(update_check,
-                             Xml::Namespace::kRequest,
-                             Xml::Attribute::kTag,
-                             app_data.ap());
-    if (FAILED(hr)) { return hr; }
-  }
-
-  // Add the Trusted Tester token under the "updatecheck" element.
-  if (!app_data.tt_token().IsEmpty()) {
-    hr = AddXMLAttributeNode(update_check,
-                             Xml::Namespace::kRequest,
-                             Xml::Attribute::kTTToken,
-                             app_data.tt_token());
-    if (FAILED(hr)) { return hr; }
-  }
-
-  hr = (*app_element)->appendChild(update_check, NULL);
-  if (FAILED(hr)) { return hr; }
-
-  if (!app_data.install_data_index().IsEmpty()) {
-    // data element.
-    CComPtr<IXMLDOMNode> install_data;
-    hr = CreateRequestElement(Xml::Element::kData,
-                              _T(""),
-                              document,
-                              &install_data);
-    if (FAILED(hr)) { return hr; }
-
-    hr = AddXMLAttributeNode(install_data,
-                             Xml::Namespace::kRequest,
-                             Xml::Attribute::kName,
-                             Xml::Value::kInstallData);
-    if (FAILED(hr)) { return hr; }
-
-    hr = AddXMLAttributeNode(install_data,
-                             Xml::Namespace::kRequest,
-                             Xml::Attribute::kIndex,
-                             app_data.install_data_index());
-    if (FAILED(hr)) { return hr; }
-
-    hr = (*app_element)->appendChild(install_data, NULL);
-    if (FAILED(hr)) { return hr; }
-  }
-
-  bool was_active = app_data.did_run() == AppData::ACTIVE_RUN;
-  bool need_active = app_data.did_run() != AppData::ACTIVE_UNKNOWN;
-  bool has_sent_a_today = app_data.days_since_last_active_ping() == 0;
-  bool need_a = was_active && !has_sent_a_today;
-  bool need_r = app_data.days_since_last_roll_call() != 0;
-
-  if (need_active || need_a || need_r) {
-    // didrun element. The server calls it "ping" for legacy reasons.
-    CComPtr<IXMLDOMNode> ping;
-    hr = CreateRequestElement(Xml::Element::kPing,
-                              _T(""),
-                              document,
-                              &ping);
-    if (FAILED(hr)) { return hr; }
-
-    // TODO(omaha): Remove "active" attribute after transition.
-    if (need_active) {
-      const TCHAR* active_str(was_active ? _T("1") : _T("0"));
-      hr = AddXMLAttributeNode(ping,
-                               Xml::Namespace::kRequest,
-                               Xml::Attribute::kActive,
-                               active_str);
-      if (FAILED(hr)) { return hr; }
-    }
-
-    if (need_a) {
-      hr = AddXMLAttributeNode(ping,
-                               Xml::Namespace::kRequest,
-                               Xml::Attribute::kDaysSinceLastActivePing,
-                               itostr(app_data.days_since_last_active_ping()));
-      if (FAILED(hr)) { return hr; }
-    }
-
-    if (need_r) {
-      hr = AddXMLAttributeNode(ping,
-                               Xml::Namespace::kRequest,
-                               Xml::Attribute::kDaysSinceLastRollCall,
-                               itostr(app_data.days_since_last_roll_call()));
-      if (FAILED(hr)) { return hr; }
-    }
-
-    hr = (*app_element)->appendChild(ping, NULL);
-    if (FAILED(hr)) { return hr; }
-  }
-
-  return S_OK;
-}
-
-HRESULT GoopdateXmlParser::CreatePingAppRequestElement(
-    const AppRequest& app_request,
-    IXMLDOMDocument* document,
-    IXMLDOMNode** app_element) {
-  HRESULT hr = CreateAppRequestElementHelper(app_request,
-                                             document,
-                                             app_element);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // create and add Ping elements to the app element. Server calls ping elements
-  // "event" elements for legacy reasons.
-  std::vector<PingEvent>::const_iterator it;
-  for (it = app_request.request_data().ping_events_begin();
-       it != app_request.request_data().ping_events_end();
-       ++it) {
-    const PingEvent& app_event = *it;
-    CComPtr<IXMLDOMNode> ev;
-    hr = CreateRequestElement(Xml::Element::kEvent,
-                              _T(""),
-                              document,
-                              &ev);
-    if (FAILED(hr)) { return hr; }
-    CString str;
-    str.Format(_T("%d"), app_event.event_type());
-    hr = AddXMLAttributeNode(ev,
-                             Xml::Namespace::kRequest,
-                             Xml::Attribute::kEventType,
-                             str);
-    if (FAILED(hr)) { return hr; }
-    str.Format(_T("%d"), app_event.event_result());
-    hr = AddXMLAttributeNode(ev,
-                             Xml::Namespace::kRequest,
-                             Xml::Attribute::kEventResult,
-                             str);
-    if (FAILED(hr)) { return hr; }
-    str.Format(_T("%d"), app_event.error_code());
-    hr = AddXMLAttributeNode(ev,
-                             Xml::Namespace::kRequest,
-                             Xml::Attribute::kErrorCode,
-                             str);
-    if (FAILED(hr)) { return hr; }
-    str.Format(_T("%d"), app_event.extra_code1());
-    hr = AddXMLAttributeNode(ev,
-                             Xml::Namespace::kRequest,
-                             Xml::Attribute::kExtraCode1,
-                             str);
-    if (FAILED(hr)) { return hr; }
-    str = app_event.previous_version();
-    if (!str.IsEmpty()) {
-      hr = AddXMLAttributeNode(ev,
-                               Xml::Namespace::kRequest,
-                               Xml::Attribute::kPreviousVersion,
-                               str);
-      if (FAILED(hr)) { return hr; }
-    }
-
-    hr = (*app_element)->appendChild(ev, NULL);
-    if (FAILED(hr)) { return hr; }
-  }
-
-  return S_OK;
-}
-
-HRESULT GoopdateXmlParser::GenerateRequest(const Request& req,
-                                           bool is_update_check,
-                                           CString* request_buffer) {
-  CORE_LOG(L3, (_T("[GenerateRequest]")));
-  ASSERT1(request_buffer);
-  if (!request_buffer) {
-    return E_INVALIDARG;
-  }
-
-  // Create the XML document.
-  CComPtr<IXMLDOMDocument> document;
-  HRESULT hr = CoCreateSafeDOMDocument(&document);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[CoCreateSafeDOMDocument failed][0x%x]"), hr));
-    return hr;
-  }
-
-  // Create the AppRequests Element.
-  CComPtr<IXMLDOMNode> update_requests;
-  hr = CreateRequestElement(Xml::Element::kRequests,
-                            _T(""),
-                            document,
-                            &update_requests);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[CreateRequestElement failed][0x%x]"), hr));
-    return hr;
-  }
-
-  // Add the update requests node to the document.
-  CComPtr<IXMLDOMElement> update_requests_node;
-  hr = update_requests->QueryInterface(&update_requests_node);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[update_requests->QueryInterface][0x%x]"), hr));
-    return hr;
-  }
-  hr = document->putref_documentElement(update_requests_node);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[putref_documentElement failed][0x%x]"), hr));
-    return hr;
-  }
-
-  // add attributes to the top element:
-  // * protocol - protocol version
-  // * version - omaha version
-  // * testsource - test source
-  // * requestid - Unique request id
-  hr = AddXMLAttributeNode(update_requests,
-                           Xml::Namespace::kRequest,
-                           Xml::Attribute::kProtocol,
-                           Xml::Value::kProtocol);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[AddXMLAttributeNode failed][0x%x]"), hr));
-    return hr;
-  }
-  hr = AddXMLAttributeNode(update_requests,
-                           Xml::Namespace::kRequest,
-                           Xml::Attribute::kVersion,
-                           req.version());
-  if (FAILED(hr)) { return hr; }
-  hr = AddXMLAttributeNode(update_requests,
-                           Xml::Namespace::kRequest,
-                           Xml::Attribute::kIsMachine,
-                           req.is_machine() ? _T("1") : _T("0"));
-  if (FAILED(hr)) { return hr; }
-  if (!req.test_source().IsEmpty()) {
-    hr = AddXMLAttributeNode(update_requests,
-                             Xml::Namespace::kRequest,
-                             Xml::Attribute::kTestSource,
-                             req.test_source());
-    if (FAILED(hr)) { return hr; }
-  }
-  if (!req.request_id().IsEmpty()) {
-    hr = AddXMLAttributeNode(update_requests,
-                             Xml::Namespace::kRequest,
-                             Xml::Attribute::kRequestId,
-                             req.request_id());
-    if (FAILED(hr)) { return hr; }
-  }
-
-  bool is_period_overridden = false;
-  const int check_period_sec =
-      ConfigManager::Instance()->GetLastCheckPeriodSec(&is_period_overridden);
-  if (is_period_overridden) {
-    hr = AddXMLAttributeNode(update_requests,
-                             Xml::Namespace::kRequest,
-                             Xml::Attribute::kPeriodOverrideSec,
-                             itostr(check_period_sec));
-    ASSERT1(SUCCEEDED(hr));
-  }
-
-  // Create os elements and add to the requests element.
-  CComPtr<IXMLDOMNode> os_element;
-  hr = CreateRequestElement(Xml::Element::kOs, _T(""), document, &os_element);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[CreateRequestElement failed][0x%x]"), hr));
-    return hr;
-  }
-  hr = AddXMLAttributeNode(os_element,
-                           Xml::Namespace::kRequest,
-                           Xml::Attribute::kPlatform,
-                           kPlatformWin);
-  hr = AddXMLAttributeNode(os_element,
-                           Xml::Namespace::kRequest,
-                           Xml::Attribute::kVersion,
-                           req.os_version());
-  hr = AddXMLAttributeNode(os_element,
-                           Xml::Namespace::kRequest,
-                           Xml::Attribute::kServicePack,
-                           req.os_service_pack());
-  if (FAILED(hr)) { return hr; }
-  hr = update_requests->appendChild(os_element, NULL);
-  if (FAILED(hr)) { return hr; }
-
-  // Create and add request's to the requests node.
-  AppRequestVector::const_iterator i;
-  for (i = req.app_requests_begin(); i != req.app_requests_end(); ++i) {
-    const AppRequest& app_request = *i;
-    // Create each of the request elements and add to the requests element.
-    CComPtr<IXMLDOMNode> request_element;
-    hr = is_update_check ?
-         CreateUpdateAppRequestElement(app_request,
-                                       document,
-                                       &request_element) :
-         CreatePingAppRequestElement(app_request,
-                                     document,
-                                     &request_element);
-    if (FAILED(hr)) { return hr; }
-
-    // Add the update request node to the AppRequests node.
-    hr = update_requests->appendChild(request_element, NULL);
-    if (FAILED(hr)) { return hr; }
-  }
-
-  // Extract the string out of the DOM, and add the initial processing
-  // instruction. We cannot use the xml processing instruction as the get_xml
-  // method returns utf-16 encoded string which causes the utf-8 marker to be
-  // removed. We will convert to utf-8 before the actual save.
-  CComBSTR xml_value(Xml::kHeaderText);
-  CComBSTR xml_body;
-  document->get_xml(&xml_body);
-  xml_value += xml_body;
-
-  *request_buffer = static_cast<TCHAR*>(xml_value);
-
-  return S_OK;
-}
-
-HRESULT GoopdateXmlParser::LoadXmlFileToMemory(const CString& file_name,
-                                               std::vector<byte>* buffer) {
-  ASSERT1(buffer);
-
-  CComPtr<IXMLDOMDocument> document;
-  HRESULT hr = LoadXMLFromFile(file_name, false, &document);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return SaveXMLToRawData(document, buffer);
-}
-
-}  // namespace omaha
diff --git a/goopdate/goopdate_xml_parser.h b/goopdate/goopdate_xml_parser.h
deleted file mode 100644
index 059dec1..0000000
--- a/goopdate/goopdate_xml_parser.h
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Declares the Goopdate XML parser. This XML schema is used in the following:
-// 1. Creating update pings.
-// 2. Parsing update responses.
-// 3. Parsing the Install manifest.
-
-#ifndef OMAHA_GOOPDATE_GOOPDATE_XML_PARSER_H__
-#define OMAHA_GOOPDATE_GOOPDATE_XML_PARSER_H__
-
-#include <map>
-#include <vector>
-#include "base/basictypes.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/request.h"
-#include "omaha/goopdate/update_response.h"
-
-namespace omaha {
-
-const int kInvalidId = -1;
-
-// name and value of an element or attribute, used in internal loops.
-typedef std::pair<CString, CString> XMLNameValuePair;
-
-// Reader/writer for the Update request. The class is a simple wrapper on
-// top of XML DOM. The class performs validation of the response schema,
-// without calling into the xml schema validation of DOM.
-class GoopdateXmlParser {
- public:
-  // Parses the manifest file.
-  static HRESULT ParseManifestFile(const CString& file_name,
-                                   UpdateResponses* responses);
-
-  // Parses the manifest.
-  static HRESULT ParseManifestBytes(const std::vector<byte>& manifest_bytes,
-                                    UpdateResponses* responses);
-
-  // Generates the update request from the request node.
-  static HRESULT GenerateRequest(const Request& request,
-                                 bool is_update_check,
-                                 CString* request_string);
-
-  // Loads an XML file into memory.
-  static HRESULT LoadXmlFileToMemory(const CString& file_name,
-                                     std::vector<byte>* buffer);
-
- private:
-  // Reads the protocol version of the xml file.
-  static HRESULT GetProtocolVersion(IXMLDOMElement* document_element,
-                                    CString* version);
-
-  // Reads the app elements into individual UpdateResponse elements
-  // and adds them to responses.
-  static HRESULT ReadUpdateResponses(IXMLDOMNode* gupdate,
-                                     UpdateResponses* responses);
-
-  // Reads the UpdateResponse element.
-  static HRESULT ReadUpdateResponse(IXMLDOMNode* app,
-                                    UpdateResponse* response);
-
-  // Reads the components section of a response.
-  static HRESULT ReadComponentsResponses(IXMLDOMNode* node,
-                                         UpdateResponse* response);
-
-  // Reads an individual component of a response.
-  static HRESULT ReadComponentResponseData(IXMLDOMNode* node,
-                                           UpdateResponseData* response_data);
-
-  // Reads the install elements into individual UpdateResponse elements
-  // and adds them to responses.
-  static HRESULT ReadSeedInstalls(IXMLDOMNode* gupdate,
-                                  UpdateResponses* responses);
-
-  // Reads the InstallsElement.
-  static HRESULT ReadInstallElement(IXMLDOMNode* node,
-                                    UpdateResponseData* response_data);
-
-  // Reads the attributes that must be in an install node.
-  static HRESULT ReadRequiredInstallAttributes(
-      IXMLDOMNode* node,
-      UpdateResponseData* response_data);
-
-  // Reads the attributes that are optional in an install node.
-  static HRESULT ReadOptionalInstallAttributes(
-      IXMLDOMNode* node,
-      UpdateResponseData* response_data);
-
-  // Reads the string value, either TEXT or CDATA, within the given node.
-  static HRESULT ReadStringValue(IXMLDOMNode* node, CString* value);
-
-  // Reads an attribute, given the node and the name of the attribute.
-  static HRESULT ReadAttribute(IXMLDOMNode* node, const TCHAR* attr_name,
-                               BSTR* value);
-
-  // Reads and parses an attribute that contains a boolean value.
-  static HRESULT ReadBooleanAttribute(IXMLDOMNode* node,
-                                      const TCHAR* attr_name,
-                                      bool* value);
-
-  // Reads an attribute that contains a guid.
-  static HRESULT ReadGuidAttribute(IXMLDOMNode* node,
-                                   const TCHAR* attr_name,
-                                   GUID* value);
-
-  // Reads a string attribute given the node and the attribute name.
-  static HRESULT ReadStringAttribute(IXMLDOMNode* node,
-                                     const TCHAR* attr_name,
-                                     CString* value);
-
-  // Reads an int attribute given the node and the attribute name.
-  static HRESULT ReadIntAttribute(IXMLDOMNode* node,
-                                  const TCHAR* attr_name,
-                                  int* value);
-
-  // creates an element in the request namespace
-  static HRESULT CreateRequestElement(const TCHAR* xml_element_name,
-                                      const CString& value,
-                                      IXMLDOMDocument* document,
-                                      IXMLDOMNode** request_element);
-
-  // Creates an element in the request namespace and adds it to parent
-  static HRESULT CreateAndAddRequestElement(const TCHAR* xml_element_name,
-                                            const CString& value,
-                                            IXMLDOMDocument* document,
-                                            IXMLDOMNode* parent);
-
-  // Converts a string to the SuccessfulInstallAction enum.
-  static SuccessfulInstallAction ConvertStringToSuccessAction(
-      const CString& text);
-
-  // Adds the attributes to the node.
-  static HRESULT AddAttributesToNode(
-      const CString& namespace_uri,
-      const std::vector<XMLNameValuePair>& attributes,
-      IXMLDOMNode* element);
-
-  // Verifies that document element's version is compatible.
-  static HRESULT VerifyElementProtocolCompatibility(
-      IXMLDOMElement* document_element,
-      const CString& expected_version);
-
-  // Verifies that the versions are compatible.
-  static HRESULT VerifyProtocolCompatibility(const CString& actual_version,
-                                             const CString& expected_version);
-
-  // Verifies that the response node name and version are valid.
-  static HRESULT ValidateResponseElement(IXMLDOMElement* document_element);
-
-  // Parses the manifest.
-  static HRESULT ParseManifest(IXMLDOMElement* manifest,
-                               UpdateResponses* responses);
-
-  // Verify that the protocol version is between the versions.
-  static HRESULT VerifyProtocolRange(const CString& actual_ver,
-                                     const CString& start_ver,
-                                     const CString& end_ver);
-
-  static HRESULT ReadTimeSinceMidnightSec(IXMLDOMNode* node,
-                                          int* time_since_midnight_sec);
-
- private:
-  // Helper for CreateUpdateAppRequestElement and CreatePingAppRequestElement.
-  static HRESULT CreateAppRequestElementHelper(const AppRequest& app_request,
-                                               IXMLDOMDocument* document,
-                                               IXMLDOMNode** app_element);
-
-  // Creates the main app element, including the update element, and all its
-  // children.
-  static HRESULT CreateUpdateAppRequestElement(const AppRequest& app_request,
-                                               IXMLDOMDocument* document,
-                                               IXMLDOMNode** app_element);
-
-  // Creates the main app element, including the ping element, and all its
-  // children.
-  static HRESULT CreatePingAppRequestElement(const AppRequest& app_request,
-                                             IXMLDOMDocument* document,
-                                             IXMLDOMNode** app_element);
-
-  static CString seed_protocol_version;
-
-  friend class GoopdateXmlParserTest;
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(GoopdateXmlParser);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_GOOPDATE_XML_PARSER_H__
diff --git a/goopdate/goopdate_xml_parser_unittest.cc b/goopdate/goopdate_xml_parser_unittest.cc
deleted file mode 100644
index 9d0fe8f..0000000
--- a/goopdate/goopdate_xml_parser_unittest.cc
+++ /dev/null
@@ -1,1039 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// Goopdate Xml Parser unit tests.
-
-#include <windows.h>
-#include <utility>
-#include <vector>
-#include "base/scoped_ptr.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/path.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/string.h"
-#include "omaha/common/xml_utils.h"
-#include "omaha/goopdate/goopdate_xml_parser.h"
-#include "omaha/goopdate/update_response.h"
-#include "omaha/goopdate/resources/goopdateres/goopdate.grh"
-#include "omaha/testing/resource.h"
-#include "omaha/testing/unit_test.h"
-
-namespace {
-
-const int kSeedManifestFileCount = 1;
-const int kSeedManifestResponseCount = 7;
-
-const TCHAR* const kPolicyKey =
-    _T("HKLM\\Software\\Policies\\Google\\Update\\");
-
-}  // namespace
-
-namespace omaha {
-
-const int kExpectedRequestLength = 2048;
-
-// Do NOT override the registry as this causes the XML Parser to fail on Vista.
-// Saves and restores registry values to prevent reading developer's update
-// check period override from impacting tests.
-class GoopdateXmlParserTest : public testing::Test {
- protected:
-  GoopdateXmlParserTest()
-      : is_updatedev_check_period_override_present_(false),
-        updatedev_check_period_override_(0),
-        is_policy_check_period_override_present_(false),
-        policy_check_period_override_(0) {
-  }
-
-  virtual void SetUp() {
-    is_updatedev_check_period_override_present_ =
-        SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
-                                   kRegValueLastCheckPeriodSec,
-                                   &updatedev_check_period_override_));
-    RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, kRegValueLastCheckPeriodSec);
-    is_policy_check_period_override_present_ =
-        SUCCEEDED(RegKey::GetValue(kPolicyKey,
-                                   _T("AutoUpdateCheckPeriodMinutes"),
-                                   &policy_check_period_override_));
-    RegKey::DeleteValue(kPolicyKey, _T("AutoUpdateCheckPeriodMinutes"));
-  }
-
-  virtual void TearDown() {
-    if (is_updatedev_check_period_override_present_) {
-      EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                        kRegValueLastCheckPeriodSec,
-                                        updatedev_check_period_override_));
-    } else {
-      RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV, kRegValueLastCheckPeriodSec);
-    }
-    if (is_policy_check_period_override_present_) {
-      EXPECT_SUCCEEDED(RegKey::SetValue(kPolicyKey,
-                                        _T("AutoUpdateCheckPeriodMinutes"),
-                                        policy_check_period_override_));
-    } else {
-      RegKey::DeleteValue(kPolicyKey, _T("AutoUpdateCheckPeriodMinutes"));
-    }
-  }
-
-  void CreateBaseAppData(bool is_machine, AppData* app_data) {
-    ASSERT_TRUE(app_data != NULL);
-
-    AppData data(
-        StringToGuid(_T("{83F0F399-FA78-4a94-A45E-253D4F42A4C5}")),
-        is_machine);
-    data.set_version(_T("1.0.101.0"));
-    data.set_language(_T("en_us"));
-    *app_data = data;
-  }
-
-  SuccessfulInstallAction ConvertStringToSuccessAction(const CString& text) {
-    return GoopdateXmlParser::ConvertStringToSuccessAction(text);
-  }
-
-  HRESULT VerifyProtocolCompatibility(const CString& actual_version,
-                                      const CString& expected_version) {
-    return GoopdateXmlParser::VerifyProtocolCompatibility(actual_version,
-                                                          expected_version);
-  }
-
-  static HRESULT ReadStringValue(IXMLDOMNode* node, CString* value) {
-    return GoopdateXmlParser::ReadStringValue(node, value);
-  }
-
-  bool is_updatedev_check_period_override_present_;
-  DWORD updatedev_check_period_override_;
-  bool is_policy_check_period_override_present_;
-  DWORD policy_check_period_override_;
-
-  static const int kServerManifestResponseCount = 5;
-  static const int kServerManifestComponentsResponseCount = 4;
-};
-
-TEST_F(GoopdateXmlParserTest, GenerateRequest_Test1) {
-  TCHAR expected_value[kExpectedRequestLength] = {0};
-  EXPECT_TRUE(::LoadString(NULL, IDS_EXPECTED_UPDATE_REQUEST1, expected_value,
-                           kExpectedRequestLength) != 0);
-
-  Request req(false);
-  req.set_request_id(_T("{8CD4D4C7-D42E-49B7-9E1A-DDDC8F8F77A8}"));
-  req.set_os_version(_T("5.1"));
-  req.set_os_service_pack(_T(""));
-  req.set_version(_T("0.0.0.0"));
-  req.set_test_source(_T("dev"));
-
-  AppData app_data1;
-  CreateBaseAppData(req.is_machine(), &app_data1);
-  app_data1.set_did_run(AppData::ACTIVE_NOTRUN);
-  app_data1.set_days_since_last_active_ping(3);
-  app_data1.set_days_since_last_roll_call(2);
-  app_data1.set_ap(_T("dev"));
-  app_data1.set_iid(StringToGuid(_T("{A972BB39-CCA3-4f25-9737-3308F5FA19B5}")));
-  app_data1.set_client_id(_T("_one_client"));
-  app_data1.set_install_source(_T("oneclick"));
-  app_data1.set_brand_code(_T("GGLG"));
-  // This 32-bit unsigned value gets reported as a negative value due to casting
-  // in place to handle the -1 case. This is okay because there are 68 positive
-  // years in INT_MAX.
-  app_data1.set_install_time_diff_sec(3123456789);
-  app_data1.set_install_source(_T("oneclick"));
-
-  AppRequestData app_request_data1(app_data1);
-  AppRequest app_request1(app_request_data1);
-  req.AddAppRequest(app_request1);
-
-  AppData app_data2;
-  CreateBaseAppData(req.is_machine(), &app_data2);
-  app_data2.set_did_run(AppData::ACTIVE_NOTRUN);
-  app_data2.set_days_since_last_active_ping(5);
-  app_data2.set_days_since_last_roll_call(0);
-  app_data2.set_iid(StringToGuid(_T("{E9EF60A1-B254-4898-A1B3-6C9B60FAC94A}")));
-  app_data2.set_client_id(_T("_another_client"));
-  app_data2.set_brand_code(_T("GooG"));
-  app_data2.set_install_time_diff_sec(30);
-  AppRequestData app_request_data2(app_data2);
-  AppRequest app_request2(app_request_data2);
-  req.AddAppRequest(app_request2);
-
-  CString request_string;
-  ExpectAsserts expect_asserts;  // set_install_time_diff_sec overflow.
-  ASSERT_SUCCEEDED(
-      GoopdateXmlParser::GenerateRequest(req, true, &request_string));
-
-  ASSERT_STREQ(expected_value, request_string);
-}
-
-// Also tests the presence of periodoverridesec.
-TEST_F(GoopdateXmlParserTest, GenerateRequest_TestTTToken) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kPolicyKey,
-                                    _T("AutoUpdateCheckPeriodMinutes"),
-                                    static_cast<DWORD>(123456)));
-
-  TCHAR expected_value[kExpectedRequestLength] = {0};
-  EXPECT_TRUE(::LoadString(NULL, IDS_EXPECTED_UPDATE_REQUEST_TTTOKEN,
-                           expected_value, kExpectedRequestLength) != 0);
-
-  Request req(false);
-  req.set_request_id(_T("{8CD4D4C7-D42E-49B7-9E1A-DDDC8F8F77A8}"));
-  req.set_os_version(_T("5.1"));
-  req.set_os_service_pack(_T(""));
-  req.set_version(_T("0.0.0.0"));
-  req.set_test_source(_T("dev"));
-
-  AppData app_data1;
-  CreateBaseAppData(req.is_machine(), &app_data1);
-  app_data1.set_did_run(AppData::ACTIVE_RUN);
-  app_data1.set_days_since_last_active_ping(7);
-  app_data1.set_days_since_last_roll_call(0);
-  app_data1.set_ap(_T("dev"));
-  app_data1.set_tt_token(_T("foobar"));
-  app_data1.set_iid(StringToGuid(_T("{A972BB39-CCA3-4f25-9737-3308F5FA19B5}")));
-  app_data1.set_client_id(_T("_one_client"));
-  app_data1.set_install_source(_T("oneclick"));
-  app_data1.set_brand_code(_T("GGLG"));
-  app_data1.set_install_time_diff_sec(static_cast<uint32>(-1 * kSecondsPerDay));
-  app_data1.set_install_source(_T("oneclick"));
-
-  AppRequestData app_request_data1(app_data1);
-  AppRequest app_request1(app_request_data1);
-  req.AddAppRequest(app_request1);
-
-  AppData app_data2;
-  CreateBaseAppData(req.is_machine(), &app_data2);
-  app_data2.set_did_run(AppData::ACTIVE_RUN);
-  app_data2.set_days_since_last_active_ping(0);
-  app_data2.set_days_since_last_roll_call(4);
-  app_data2.set_iid(StringToGuid(_T("{E9EF60A1-B254-4898-A1B3-6C9B60FAC94A}")));
-  app_data2.set_client_id(_T("_another_client"));
-  app_data2.set_brand_code(_T("GooG"));
-  AppRequestData app_request_data2(app_data2);
-  AppRequest app_request2(app_request_data2);
-  req.AddAppRequest(app_request2);
-
-  CString request_string;
-  ASSERT_SUCCEEDED(
-      GoopdateXmlParser::GenerateRequest(req, true, &request_string));
-
-  ASSERT_STREQ(expected_value, request_string);
-}
-
-TEST_F(GoopdateXmlParserTest, GenerateRequest_TestUpdateDisabled) {
-  TCHAR expected_value[kExpectedRequestLength] = {0};
-  EXPECT_TRUE(::LoadString(NULL, IDS_EXPECTED_UPDATE_REQUEST_UPDATE_DISABLED,
-                           expected_value, kExpectedRequestLength) != 0);
-
-  Request req(false);
-  req.set_request_id(_T("{8CD4D4C7-D42E-49B7-9E1A-DDDC8F8F77A8}"));
-  req.set_os_version(_T("5.1"));
-  req.set_os_service_pack(_T(""));
-  req.set_version(_T("0.0.0.0"));
-  req.set_test_source(_T("dev"));
-
-  AppData app_data1;
-  CreateBaseAppData(req.is_machine(), &app_data1);
-  app_data1.set_did_run(AppData::ACTIVE_NOTRUN);
-  app_data1.set_days_since_last_active_ping(1);
-  app_data1.set_days_since_last_roll_call(1);
-  app_data1.set_ap(_T("dev"));
-  app_data1.set_tt_token(_T("foobar"));
-  app_data1.set_iid(StringToGuid(_T("{A972BB39-CCA3-4f25-9737-3308F5FA19B5}")));
-  app_data1.set_client_id(_T("_one_client"));
-  app_data1.set_install_source(_T("oneclick"));
-  app_data1.set_brand_code(_T("GGLG"));
-  app_data1.set_install_source(_T("oneclick"));
-  app_data1.set_is_update_disabled(true);
-
-  AppRequestData app_request_data1(app_data1);
-  AppRequest app_request1(app_request_data1);
-  req.AddAppRequest(app_request1);
-
-  AppData app_data2;
-  CreateBaseAppData(req.is_machine(), &app_data2);
-  app_data2.set_did_run(AppData::ACTIVE_RUN);
-  app_data2.set_days_since_last_active_ping(2);
-  app_data2.set_days_since_last_roll_call(2);
-  app_data2.set_iid(StringToGuid(_T("{E9EF60A1-B254-4898-A1B3-6C9B60FAC94A}")));
-  app_data2.set_client_id(_T("_another_client"));
-  app_data2.set_brand_code(_T("GooG"));
-  AppRequestData app_request_data2(app_data2);
-  AppRequest app_request2(app_request_data2);
-  req.AddAppRequest(app_request2);
-
-  CString request_string;
-  ASSERT_SUCCEEDED(
-      GoopdateXmlParser::GenerateRequest(req, true, &request_string));
-
-  ASSERT_STREQ(expected_value, request_string);
-}
-
-TEST_F(GoopdateXmlParserTest, GenerateRequest_Test2) {
-  TCHAR expected_value[kExpectedRequestLength] = {0};
-  EXPECT_TRUE(::LoadString(NULL, IDS_EXPECTED_UPDATE_REQUEST2, expected_value,
-                           kExpectedRequestLength) != 0);
-
-  Request req(true);
-  req.set_request_id(_T("{8CD4D4C7-D42E-49B7-9E1A-DDDC8F8F77A8}"));
-  req.set_os_version(_T("5.1"));
-  req.set_os_service_pack(_T("Service Pack 2"));
-  req.set_version(_T("8.9.10.11"));
-  req.set_test_source(_T("qa"));
-
-  AppData app_data1;
-  CreateBaseAppData(req.is_machine(), &app_data1);
-  AppRequestData app_request_data1(app_data1);
-  PingEvent ping_event(PingEvent::EVENT_INSTALL_COMPLETE,
-                       PingEvent::EVENT_RESULT_ERROR,
-                       1234,
-                       E_FAIL,
-                       _T("Install error"));
-  app_request_data1.AddPingEvent(ping_event);
-  EXPECT_EQ(0, app_data1.install_time_diff_sec());
-  AppRequest app_request1(app_request_data1);
-  req.AddAppRequest(app_request1);
-
-  AppData app_data2;
-  CreateBaseAppData(req.is_machine(), &app_data2);
-  app_data2.set_ap(_T("stable"));
-  app_data2.set_tt_token(_T("foobar"));
-  app_data2.set_iid(StringToGuid(_T("{E9EF60A1-B254-4898-A1B3-6C9B60FAC94A}")));
-  app_data2.set_client_id(_T("_some_client"));
-  app_data2.set_brand_code(_T("GooG"));
-  app_data2.set_install_time_diff_sec(7 * 24 * 60 * 60 - 1);
-  AppRequestData app_request_data2(app_data2);
-  AppRequest app_request2(app_request_data2);
-  req.AddAppRequest(app_request2);
-
-  CString request_string;
-  ASSERT_SUCCEEDED(GoopdateXmlParser::GenerateRequest(req,
-                                                      false,
-                                                      &request_string));
-
-  ASSERT_STREQ(expected_value, request_string);
-}
-
-// Also tests the presence of periodoverridesec in non-update checks and integer
-// overflow of the registry value.
-TEST_F(GoopdateXmlParserTest, GenerateRequest_Test3_Components) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kPolicyKey,
-                                    _T("AutoUpdateCheckPeriodMinutes"),
-                                    static_cast<DWORD>(UINT_MAX / 60)));
-
-  TCHAR expected_value[kExpectedRequestLength] = {0};
-  EXPECT_TRUE(::LoadString(NULL, IDS_EXPECTED_UPDATE_REQUEST3, expected_value,
-                           kExpectedRequestLength) != 0);
-
-  Request req(true);
-  req.set_request_id(_T("{8CD4D4C7-D42E-49B7-9E1A-DDDC8F8F77A8}"));
-  req.set_os_version(_T("5.1"));
-  req.set_os_service_pack(_T("Service Pack 2"));
-  req.set_version(_T("8.9.10.11"));
-  req.set_test_source(_T("qa"));
-
-  AppData app_data1;
-  CreateBaseAppData(req.is_machine(), &app_data1);
-
-  const TCHAR* kComponent1Guid = _T("{AC001D35-5F30-473A-9D7B-8FD3877AC28E}");
-  const TCHAR* kComponent1Version = _T("1.0.3.1");
-
-  const TCHAR* kComponent2Guid = _T("{F4E490BE-83BB-4D5A-8386-263DE047255E}");
-  const TCHAR* kComponent2Version = _T("1.1.4.2");
-
-  AppRequest app_request1;
-  AppRequestData app_request_data1(app_data1);
-
-  AppData component1;
-  component1.set_app_guid(StringToGuid(kComponent1Guid));
-  component1.set_is_machine_app(req.is_machine());
-  component1.set_version(kComponent1Version);
-  AppRequestData app_request_data_component1(component1);
-  app_request1.AddComponentRequest(app_request_data_component1);
-
-  AppData component2;
-  component2.set_app_guid(StringToGuid(kComponent2Guid));
-  component2.set_is_machine_app(req.is_machine());
-  component2.set_version(kComponent2Version);
-  AppRequestData app_request_data_component2(component2);
-  app_request1.AddComponentRequest(app_request_data_component2);
-
-  PingEvent ping_event(PingEvent::EVENT_INSTALL_COMPLETE,
-                       PingEvent::EVENT_RESULT_ERROR,
-                       1234,
-                       E_FAIL,
-                       _T("Install error"));
-  app_request_data1.AddPingEvent(ping_event);
-
-  app_request1.set_request_data(app_request_data1);
-  req.AddAppRequest(app_request1);
-
-  AppData app_data2;
-  CreateBaseAppData(req.is_machine(), &app_data2);
-  app_data2.set_ap(_T("stable"));
-  app_data2.set_iid(StringToGuid(_T("{E9EF60A1-B254-4898-A1B3-6C9B60FAC94A}")));
-  app_data2.set_client_id(_T("_some_client"));
-  app_data2.set_brand_code(_T("GooG"));
-  AppRequestData app_request_data2(app_data2);
-  AppRequest app_request2(app_request_data2);
-  req.AddAppRequest(app_request2);
-
-  CString request_string;
-  bool encrypt = false;
-  ASSERT_SUCCEEDED(GoopdateXmlParser::GenerateRequest(req,
-                                                      false,
-                                                      &request_string));
-
-  ASSERT_FALSE(encrypt);
-  ASSERT_STREQ(expected_value, request_string);
-}
-
-TEST_F(GoopdateXmlParserTest, GenerateRequest_TestInstallDataIndex) {
-  TCHAR expected_value[kExpectedRequestLength] = {0};
-  EXPECT_TRUE(::LoadString(NULL, IDS_EXPECTED_UPDATE_REQUEST4, expected_value,
-                           kExpectedRequestLength) != 0);
-
-  Request req(false);
-  req.set_request_id(_T("{8CD4D4C7-D42E-49B7-9E1A-DDDC8F8F77A8}"));
-  req.set_os_version(_T("5.1"));
-  req.set_os_service_pack(_T(""));
-  req.set_version(_T("0.0.0.0"));
-  req.set_test_source(_T("dev"));
-
-  AppData app_data1;
-  CreateBaseAppData(req.is_machine(), &app_data1);
-  app_data1.set_did_run(AppData::ACTIVE_RUN);
-  app_data1.set_days_since_last_active_ping(365);
-  app_data1.set_days_since_last_roll_call(31);
-  app_data1.set_ap(_T("dev"));
-  app_data1.set_iid(StringToGuid(_T("{A972BB39-CCA3-4f25-9737-3308F5FA19B5}")));
-  app_data1.set_brand_code(_T("GGLG"));
-  app_data1.set_client_id(_T("_one_client"));
-  app_data1.set_install_time_diff_sec(7 * 24 * 60 * 60);
-  app_data1.set_install_source(_T("oneclick"));
-  app_data1.set_install_data_index(_T("foobar"));
-
-  AppRequestData app_request_data1(app_data1);
-  AppRequest app_request1(app_request_data1);
-  req.AddAppRequest(app_request1);
-
-  AppData app_data2;
-  CreateBaseAppData(req.is_machine(), &app_data2);
-  app_data2.set_did_run(AppData::ACTIVE_NOTRUN);
-  app_data2.set_days_since_last_active_ping(-1);
-  app_data2.set_days_since_last_roll_call(-1);
-  app_data2.set_iid(StringToGuid(_T("{E9EF60A1-B254-4898-A1B3-6C9B60FAC94A}")));
-  app_data2.set_client_id(_T("_another_client"));
-  app_data2.set_brand_code(_T("GooG"));
-  app_data2.set_install_time_diff_sec(7 * 24 * 60 * 60 + 1);
-  AppRequestData app_request_data2(app_data2);
-  AppRequest app_request2(app_request_data2);
-  req.AddAppRequest(app_request2);
-
-  CString request_string;
-  ASSERT_SUCCEEDED(
-      GoopdateXmlParser::GenerateRequest(req, true, &request_string));
-
-  ASSERT_STREQ(expected_value, request_string);
-}
-
-// TODO(omaha): Enable this test once 'active' attribute is removed
-// from ping attribute list. Until legacy active is removed, it sends a ping
-// and makes this test fail.
-TEST_F(GoopdateXmlParserTest, DISABLED_GenerateRequest_NoPingSend) {
-  // Test that no ping is sent if both last active ping and last roll count
-  // was sent within one time unit
-  TCHAR expected_value[kExpectedRequestLength] = {0};
-  EXPECT_TRUE(::LoadString(NULL, IDS_EXPECTED_UPDATE_REQUEST5, expected_value,
-                           kExpectedRequestLength) != 0);
-
-  Request req(false);
-  req.set_request_id(_T("{8CD4D4C7-D42E-49B7-9E1A-DDDC8F8F77A8}"));
-  req.set_os_version(_T("5.1"));
-  req.set_os_service_pack(_T(""));
-  req.set_version(_T("0.0.0.0"));
-  req.set_test_source(_T("dev"));
-
-  AppData app_data1;
-  CreateBaseAppData(req.is_machine(), &app_data1);
-  app_data1.set_did_run(AppData::ACTIVE_RUN);
-  app_data1.set_days_since_last_active_ping(0);
-  app_data1.set_days_since_last_roll_call(0);
-  app_data1.set_ap(_T("dev"));
-  app_data1.set_iid(StringToGuid(_T("{A972BB39-CCA3-4f25-9737-3308F5FA19B5}")));
-  app_data1.set_client_id(_T("_one_client"));
-  app_data1.set_install_source(_T("oneclick"));
-  app_data1.set_brand_code(_T("GGLG"));
-  // This 32-bit unsigned value gets reported as a negative value due to casting
-  // in place to handle the -1 case. This is okay because there are 68 positive
-  // years in INT_MAX.
-  app_data1.set_install_time_diff_sec(3123456789);
-  app_data1.set_install_source(_T("oneclick"));
-
-  AppRequestData app_request_data1(app_data1);
-  AppRequest app_request1(app_request_data1);
-  req.AddAppRequest(app_request1);
-
-  AppData app_data2;
-  CreateBaseAppData(req.is_machine(), &app_data2);
-  app_data2.set_did_run(AppData::ACTIVE_NOTRUN);
-  app_data2.set_days_since_last_active_ping(0);
-  app_data2.set_days_since_last_roll_call(0);
-  app_data2.set_iid(StringToGuid(_T("{E9EF60A1-B254-4898-A1B3-6C9B60FAC94A}")));
-  app_data2.set_client_id(_T("_another_client"));
-  app_data2.set_brand_code(_T("GooG"));
-  app_data2.set_install_time_diff_sec(30);
-  AppRequestData app_request_data2(app_data2);
-  AppRequest app_request2(app_request_data2);
-  req.AddAppRequest(app_request2);
-
-  CString request_string;
-  ExpectAsserts expect_asserts;  // set_install_time_diff_sec overflow.
-  ASSERT_SUCCEEDED(
-      GoopdateXmlParser::GenerateRequest(req, true, &request_string));
-
-  ASSERT_STREQ(expected_value, request_string);
-}
-
-TEST_F(GoopdateXmlParserTest, ReadStringValue) {
-  const TCHAR* verbose_log = _T("\n  {\n    \"distribution\": {\n      ")
-                             _T("\"verbose_logging\": true\n    }\n  }\n  ");
-
-  CString file_name(ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                    _T("server_manifest.xml")));
-  CComPtr<IXMLDOMDocument> document;
-  ASSERT_SUCCEEDED(LoadXMLFromFile(file_name, false, &document));
-
-  CComBSTR data_element_name(_T("data"));
-  CComPtr<IXMLDOMNodeList> data_elements;
-  ASSERT_SUCCEEDED(document->getElementsByTagName(data_element_name,
-                                                  &data_elements));
-
-  CComPtr<IXMLDOMNode> data_element;
-  ASSERT_SUCCEEDED(data_elements->nextNode(&data_element));
-  ASSERT_TRUE(data_element);
-
-  CString value;
-  ASSERT_SUCCEEDED(ReadStringValue(data_element, &value));
-  ASSERT_STREQ(verbose_log, value);
-}
-
-TEST_F(GoopdateXmlParserTest, ParseManifestFile_SeedManifest) {
-  CString filename_v2(ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                      _T("seed_manifest.xml")));
-  CString *filenames[1] = {&filename_v2};
-
-  for (int j = 0; j < kSeedManifestFileCount; j++) {
-    UpdateResponses responses;
-    ASSERT_SUCCEEDED(GoopdateXmlParser::ParseManifestFile(*filenames[j],
-                                                          &responses));
-    ASSERT_EQ(kSeedManifestResponseCount, responses.size());
-
-    GUID expected_guids[] = {
-        StringToGuid(_T("{D6B08267-B440-4c85-9F79-E195E80D9937}")),
-        StringToGuid(_T("{D6B08267-B440-4c85-9F79-E195E80D9938}")),
-        StringToGuid(_T("{D6B08267-B440-4c85-9F79-E195E80D9939}")),
-        StringToGuid(_T("{D6B08267-B440-4c85-9F79-E195E80D9940}")),
-        StringToGuid(_T("{D6B08267-B440-4c85-9F79-E195E80D9941}")),
-        StringToGuid(_T("{D6B08267-B440-4c85-9F79-E195E80D9942}")),
-        StringToGuid(_T("{D6B08267-B440-4C85-9F79-E195E80D9943}")),
-    };
-
-    BrowserType expected_types[] = {
-        BROWSER_UNKNOWN,
-        BROWSER_UNKNOWN,
-        BROWSER_DEFAULT,
-        BROWSER_IE,
-        BROWSER_FIREFOX,
-        BROWSER_CHROME,
-        BROWSER_UNKNOWN
-    };
-
-    for (int i = 0; i < kSeedManifestResponseCount; i++) {
-      UpdateResponse response = responses[expected_guids[i]];
-      const UpdateResponseData& response_data = response.update_response_data();
-      EXPECT_TRUE(response_data.url().IsEmpty());
-      EXPECT_EQ(0, response_data.size());
-      EXPECT_TRUE(response_data.hash().IsEmpty());
-      EXPECT_EQ(NEEDS_ADMIN_NO, response_data.needs_admin());
-      EXPECT_TRUE(response_data.arguments().IsEmpty());
-      EXPECT_TRUE(expected_guids[i] == response_data.guid());
-      EXPECT_TRUE(response_data.status().IsEmpty());
-      EXPECT_TRUE(GUID_NULL == response_data.installation_id());
-      EXPECT_TRUE(response_data.ap().IsEmpty());
-      EXPECT_TRUE(response_data.success_url().IsEmpty());
-      EXPECT_TRUE(response_data.error_url().IsEmpty());
-      EXPECT_EQ(expected_types[i], response_data.browser_type());
-      EXPECT_STREQ(_T("en-US"), response_data.language());
-      EXPECT_STREQ(_T("Test App"), response_data.app_name());
-    }
-  }
-}
-
-TEST_F(GoopdateXmlParserTest, ParseManifestFile_ServerManifest) {
-  const TCHAR* kManifestFileNames[] = {
-      _T("server_manifest.xml"),
-      _T("server_manifest_with_unsupported_tags.xml"),
-  };
-
-  for (int i = 0; i < arraysize(kManifestFileNames); ++i) {
-    UpdateResponses responses;
-    CString file_name(ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                      kManifestFileNames[i]));
-    GUID guid  = StringToGuid(_T("{D6B08267-B440-4C85-9F79-E195E80D9937}"));
-    GUID guid2 = StringToGuid(_T("{104844D6-7DDA-460B-89F0-FBF8AFDD0A67}"));
-    GUID guid3 = StringToGuid(_T("{884a01d9-fb67-430a-b491-28f960dd7309}"));
-    GUID guid4 = StringToGuid(_T("{D6B08267-B440-4C85-9F79-E195E80D9936}"));
-    GUID guid5 = StringToGuid(_T("{8CF15C17-7BB5-433a-8E6C-C018D79D00B1}"));
-
-    const TCHAR* kVerboseLog = _T("\n  {\n    \"distribution\": {\n      ")
-                               _T("\"verbose_logging\": true\n    }\n  }\n  ");
-    const TCHAR* kSkipFirstRun = _T("{\n    \"distribution\": {\n      \"")
-                               _T("skip_first_run_ui\": true,\n    }\n  }\n  ");
-
-    ASSERT_SUCCEEDED(GoopdateXmlParser::ParseManifestFile(file_name,
-                                                          &responses));
-    ASSERT_EQ(kServerManifestResponseCount, responses.size());
-
-    UpdateResponseData response_data =
-        responses[guid].update_response_data();
-    EXPECT_STREQ(
-        _T("http://dl.google.com/foo/1.0.101.0/test_foo_v1.0.101.0.msi"),
-        response_data.url());
-    EXPECT_STREQ(_T("6bPU7OnbKAGJ1LOw6fpIUuQl1FQ="), response_data.hash());
-    EXPECT_EQ(80896, response_data.size());
-    EXPECT_EQ(NEEDS_ADMIN_YES, response_data.needs_admin());
-    EXPECT_TRUE(response_data.arguments().IsEmpty());
-    EXPECT_TRUE(guid == response_data.guid());
-    EXPECT_STREQ(kResponseStatusOkValue, response_data.status());
-    EXPECT_TRUE(GUID_NULL == response_data.installation_id());
-    EXPECT_TRUE(response_data.ap().IsEmpty());
-    EXPECT_STREQ(_T("http://testsuccessurl.com"), response_data.success_url());
-    EXPECT_TRUE(response_data.error_url().IsEmpty());
-    EXPECT_TRUE(response_data.terminate_all_browsers());
-    EXPECT_EQ(SUCCESS_ACTION_EXIT_SILENTLY, response_data.success_action());
-    EXPECT_EQ(0, responses[guid].num_components());
-    EXPECT_STREQ(kVerboseLog,
-                 response_data.GetInstallData(_T("verboselogging")));
-    EXPECT_STREQ(kSkipFirstRun,
-                 response_data.GetInstallData(_T("skipfirstrun")));
-    EXPECT_TRUE(response_data.GetInstallData(_T("foobar")).IsEmpty());
-
-    response_data = responses[guid4].update_response_data();
-    EXPECT_TRUE(response_data.url().IsEmpty());
-    EXPECT_EQ(0, response_data.size());
-    EXPECT_TRUE(response_data.hash().IsEmpty());
-    EXPECT_EQ(NEEDS_ADMIN_NO, response_data.needs_admin());
-    EXPECT_TRUE(response_data.arguments().IsEmpty());
-    EXPECT_TRUE(guid4 == response_data.guid());
-    EXPECT_STREQ(kResponseStatusNoUpdate, response_data.status());
-    EXPECT_TRUE(GUID_NULL == response_data.installation_id());
-    EXPECT_TRUE(response_data.ap().IsEmpty());
-    EXPECT_TRUE(response_data.success_url().IsEmpty());
-    EXPECT_TRUE(response_data.error_url().IsEmpty());
-    EXPECT_FALSE(response_data.terminate_all_browsers());
-    EXPECT_EQ(SUCCESS_ACTION_DEFAULT, response_data.success_action());
-    EXPECT_EQ(0, responses[guid4].num_components());
-
-    response_data = responses[guid2].update_response_data();
-    EXPECT_STREQ(
-        _T("http://dl.google.com/foo/1.0.102.0/user_foo_v1.0.102.0.msi"),
-        response_data.url());
-    EXPECT_STREQ(_T("/XzRh1rpwqrDr6ashpmQnYZIzDI="), response_data.hash());
-    EXPECT_EQ(630152,               response_data.size());
-    EXPECT_EQ(NEEDS_ADMIN_NO,      response_data.needs_admin());
-    EXPECT_STREQ(kResponseStatusOkValue, response_data.status());
-    EXPECT_STREQ(_T("/install"),    response_data.arguments());
-    EXPECT_TRUE(guid2 ==            response_data.guid());
-    EXPECT_TRUE(response_data.success_url().IsEmpty());
-    EXPECT_TRUE(response_data.error_url().IsEmpty());
-    EXPECT_FALSE(response_data.terminate_all_browsers());
-    EXPECT_EQ(SUCCESS_ACTION_DEFAULT, response_data.success_action());
-    EXPECT_EQ(0, responses[guid2].num_components());
-
-    response_data = responses[guid3].update_response_data();
-    EXPECT_TRUE(guid3 == response_data.guid());
-    EXPECT_STREQ(kResponseStatusRestrictedExportCountry,
-                 response_data.status());
-    EXPECT_FALSE(response_data.terminate_all_browsers());
-    EXPECT_EQ(SUCCESS_ACTION_DEFAULT, response_data.success_action());
-    EXPECT_EQ(0, responses[guid3].num_components());
-
-    response_data = responses[guid5].update_response_data();
-    EXPECT_TRUE(response_data.url().IsEmpty());
-    EXPECT_TRUE(response_data.hash().IsEmpty());
-    EXPECT_EQ(0, response_data.size());
-    EXPECT_EQ(NEEDS_ADMIN_NO, response_data.needs_admin());
-    EXPECT_TRUE(response_data.arguments().IsEmpty());
-    EXPECT_TRUE(guid5 == response_data.guid());
-    EXPECT_STREQ(kResponseStatusOsNotSupported, response_data.status());
-    EXPECT_TRUE(GUID_NULL == response_data.installation_id());
-    EXPECT_TRUE(response_data.ap().IsEmpty());
-    EXPECT_TRUE(response_data.success_url().IsEmpty());
-    EXPECT_STREQ(_T("http://foo.google.com/support/article.py?id=12345&")
-                 _T("hl=es-419&os=5.1"),
-                 response_data.error_url());
-    EXPECT_FALSE(response_data.terminate_all_browsers());
-    EXPECT_EQ(SUCCESS_ACTION_DEFAULT, response_data.success_action());
-    EXPECT_EQ(0, responses[guid5].num_components());
-  }
-}
-
-TEST_F(GoopdateXmlParserTest, ParseManifestFile_ServerManifest_Components) {
-  UpdateResponses responses;
-  CString file_name(ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                    _T("server_manifest_components.xml")));
-
-  // App GUIDs.
-  GUID guid  = StringToGuid(_T("{D6B08267-B440-4C85-9F79-E195E80D9937}"));
-  GUID guid2 = StringToGuid(_T("{104844D6-7DDA-460B-89F0-FBF8AFDD0A67}"));
-  GUID guid3 = StringToGuid(_T("{884a01d9-fb67-430a-b491-28f960dd7309}"));
-  GUID guid4 = StringToGuid(_T("{D6B08267-B440-4C85-9F79-E195E80D9936}"));
-
-  ASSERT_SUCCEEDED(GoopdateXmlParser::ParseManifestFile(file_name, &responses));
-  ASSERT_EQ(kServerManifestComponentsResponseCount, responses.size());
-
-  UpdateResponse response(responses[guid]);
-  UpdateResponseData response_data(response.update_response_data());
-  EXPECT_STREQ(_T("http://dl.google.com/foo/1.0.101.0/test_foo_v1.0.101.0.msi"),
-               response_data.url());
-  EXPECT_STREQ(_T("6bPU7OnbKAGJ1LOw6fpIUuQl1FQ="), response_data.hash());
-  EXPECT_EQ(80896, response_data.size());
-  EXPECT_EQ(NEEDS_ADMIN_YES, response_data.needs_admin());
-  EXPECT_TRUE(response_data.arguments().IsEmpty());
-  EXPECT_TRUE(guid == response_data.guid());
-  EXPECT_STREQ(kResponseStatusOkValue, response_data.status());
-  EXPECT_TRUE(GUID_NULL == response_data.installation_id());
-  EXPECT_TRUE(response_data.ap().IsEmpty());
-  EXPECT_STREQ(_T("http://testsuccessurl.com"), response_data.success_url());
-  EXPECT_TRUE(response_data.error_url().IsEmpty());
-  EXPECT_TRUE(response_data.terminate_all_browsers());
-  EXPECT_EQ(SUCCESS_ACTION_EXIT_SILENTLY, response_data.success_action());
-
-  UpdateResponseDatas expected_components;
-  UpdateResponseData temp;
-  temp.set_guid(StringToGuid(_T("{65C42695-84A0-41C4-B70F-D2786F674592}")));
-  temp.set_status(_T("ok"));
-  temp.set_url(_T("http://dl.google.com/foo/set_comp1.msi"));
-  temp.set_size(66324);
-  temp.set_hash(_T("6bPU7OnbKAGJ1LOw6fpIUuQl1FQ="));
-  expected_components.insert(
-      std::pair<GUID, UpdateResponseData>(temp.guid(), temp));
-
-  UpdateResponseData temp2;
-  temp2.set_guid(StringToGuid(_T("{B318029C-3607-48EB-8DBB-33E8BA17BAF1}")));
-  temp2.set_status(_T("noupdate"));
-  expected_components.insert(
-      std::pair<GUID, UpdateResponseData>(temp2.guid(), temp2));
-
-  UpdateResponseData temp3;
-  temp3.set_guid(StringToGuid(_T("{D76AE6FC-1633-4131-B782-896804795DCB}")));
-  temp3.set_status(_T("ok"));
-  temp3.set_url(_T("http://tools.google.com/happy/some_comp_inst.msi"));
-  temp3.set_size(829984);
-  temp3.set_hash(_T("6bPU7OnbKAGJ1LOw6fpIUuQl1FQ="));
-  expected_components.insert(
-      std::pair<GUID, UpdateResponseData>(temp3.guid(), temp3));
-
-  UpdateResponseData temp4;
-  temp4.set_guid(StringToGuid(_T("{67A52AEE-6E9F-4411-B425-F210B962CD6F}")));
-  temp4.set_status(_T("noupdate"));
-  expected_components.insert(
-      std::pair<GUID, UpdateResponseData>(temp4.guid(), temp4));
-
-  EXPECT_EQ(expected_components.size(), response.num_components());
-
-  UpdateResponseDatas::const_iterator it;
-  UpdateResponseDatas::const_iterator it_exp;
-  for (it = response.components_begin(), it_exp = expected_components.begin();
-       it_exp != expected_components.end();
-       ++it, ++it_exp) {
-    const UpdateResponseData& component = (*it).second;
-    const UpdateResponseData& component_expected = (*it_exp).second;
-
-    EXPECT_STREQ(component_expected.url(), component.url());
-    EXPECT_STREQ(component_expected.hash(), component.hash());
-    EXPECT_EQ(component_expected.size(), component.size());
-    EXPECT_EQ(component_expected.needs_admin(), component.needs_admin());
-    EXPECT_STREQ(component_expected.arguments(), component.arguments());
-    EXPECT_TRUE(::IsEqualGUID(component_expected.guid(), component.guid()));
-    EXPECT_STREQ(component_expected.status(), component.status());
-    EXPECT_TRUE(::IsEqualGUID(component_expected.installation_id(),
-                              component.installation_id()));
-    EXPECT_STREQ(component_expected.ap(), component.ap());
-    EXPECT_STREQ(component_expected.success_url(), component.success_url());
-    EXPECT_TRUE(response_data.error_url().IsEmpty());
-    EXPECT_EQ(component_expected.terminate_all_browsers(),
-              component.terminate_all_browsers());
-    EXPECT_EQ(component_expected.success_action(), component.success_action());
-  }
-
-  response = responses[guid4];
-  response_data = response.update_response_data();
-  EXPECT_TRUE(response_data.url().IsEmpty());
-  EXPECT_EQ(0, response_data.size());
-  EXPECT_TRUE(response_data.hash().IsEmpty());
-  EXPECT_EQ(NEEDS_ADMIN_NO, response_data.needs_admin());
-  EXPECT_TRUE(response_data.arguments().IsEmpty());
-  EXPECT_TRUE(guid4 == response_data.guid());
-  EXPECT_STREQ(kResponseStatusNoUpdate, response_data.status());
-  EXPECT_TRUE(GUID_NULL == response_data.installation_id());
-  EXPECT_TRUE(response_data.ap().IsEmpty());
-  EXPECT_TRUE(response_data.success_url().IsEmpty());
-  EXPECT_TRUE(response_data.error_url().IsEmpty());
-  EXPECT_FALSE(response_data.terminate_all_browsers());
-  EXPECT_EQ(SUCCESS_ACTION_DEFAULT, response_data.success_action());
-  EXPECT_EQ(1, response.num_components());
-
-  response = responses[guid2];
-  response_data = response.update_response_data();
-  EXPECT_STREQ(_T("http://dl.google.com/foo/1.0.102.0/user_foo_v1.0.102.0.msi"),
-               response_data.url());
-  EXPECT_STREQ(_T("/XzRh1rpwqrDr6ashpmQnYZIzDI="), response_data.hash());
-  EXPECT_EQ(630152,               response_data.size());
-  EXPECT_EQ(NEEDS_ADMIN_NO,      response_data.needs_admin());
-  EXPECT_STREQ(kResponseStatusOkValue, response_data.status());
-  EXPECT_STREQ(_T("/install"),    response_data.arguments());
-  EXPECT_TRUE(guid2 ==            response_data.guid());
-  EXPECT_TRUE(response_data.success_url().IsEmpty());
-  EXPECT_TRUE(response_data.error_url().IsEmpty());
-  EXPECT_FALSE(response_data.terminate_all_browsers());
-  EXPECT_EQ(SUCCESS_ACTION_DEFAULT, response_data.success_action());
-  EXPECT_EQ(0, response.num_components());
-
-  response = responses[guid3];
-  response_data = response.update_response_data();
-  EXPECT_TRUE(guid3 == response_data.guid());
-  EXPECT_STREQ(kResponseStatusRestrictedExportCountry, response_data.status());
-  EXPECT_FALSE(response_data.terminate_all_browsers());
-  EXPECT_EQ(SUCCESS_ACTION_DEFAULT, response_data.success_action());
-  EXPECT_EQ(0, response.num_components());
-}
-
-TEST_F(GoopdateXmlParserTest, ParseManifestFile_EmptyFilename) {
-  CString empty_filename;
-  UpdateResponses responses;
-  EXPECT_EQ(E_INVALIDARG, GoopdateXmlParser::ParseManifestFile(empty_filename,
-                                                               &responses));
-}
-
-TEST_F(GoopdateXmlParserTest, ParseManifestFile_NoSuchFile) {
-  CString no_such_file(_T("no_such_file.xml"));
-  UpdateResponses responses;
-  EXPECT_EQ(0x800c0005, GoopdateXmlParser::ParseManifestFile(no_such_file,
-                                                             &responses));
-}
-
-// This is a duplicate of the ParseManifestFile test except that it uses
-// LoadXmlFileToMemory() and ParseManifestBytes().
-TEST_F(GoopdateXmlParserTest, ParseManifestBytes_ServerManifest) {
-  UpdateResponses responses;
-  CString file_name(ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                    _T("server_manifest.xml")));
-  GUID guid  = StringToGuid(_T("{D6B08267-B440-4C85-9F79-E195E80D9937}"));
-  GUID guid2 = StringToGuid(_T("{104844D6-7DDA-460B-89F0-FBF8AFDD0A67}"));
-  GUID guid3 = StringToGuid(_T("{884a01d9-fb67-430a-b491-28f960dd7309}"));
-  GUID guid4 = StringToGuid(_T("{D6B08267-B440-4C85-9F79-E195E80D9936}"));
-  GUID guid5 = StringToGuid(_T("{8CF15C17-7BB5-433a-8E6C-C018D79D00B1}"));
-
-  std::vector<byte> manifest_contents;
-  EXPECT_SUCCEEDED(GoopdateXmlParser::LoadXmlFileToMemory(file_name,
-                                                          &manifest_contents));
-
-  ASSERT_SUCCEEDED(GoopdateXmlParser::ParseManifestBytes(manifest_contents,
-                                                         &responses));
-  ASSERT_EQ(kServerManifestResponseCount, responses.size());
-
-  UpdateResponse response(responses[guid]);
-  UpdateResponseData response_data = response.update_response_data();
-  EXPECT_STREQ(_T("http://dl.google.com/foo/1.0.101.0/test_foo_v1.0.101.0.msi"),
-               response_data.url());
-  EXPECT_STREQ(_T("6bPU7OnbKAGJ1LOw6fpIUuQl1FQ="), response_data.hash());
-  EXPECT_EQ(80896, response_data.size());
-  EXPECT_EQ(NEEDS_ADMIN_YES, response_data.needs_admin());
-  EXPECT_TRUE(response_data.arguments().IsEmpty());
-  EXPECT_TRUE(guid == response_data.guid());
-  EXPECT_STREQ(kResponseStatusOkValue, response_data.status());
-  EXPECT_TRUE(GUID_NULL == response_data.installation_id());
-  EXPECT_TRUE(response_data.ap().IsEmpty());
-  EXPECT_STREQ(_T("http://testsuccessurl.com"), response_data.success_url());
-  EXPECT_TRUE(response_data.error_url().IsEmpty());
-  EXPECT_TRUE(response_data.terminate_all_browsers());
-  EXPECT_EQ(SUCCESS_ACTION_EXIT_SILENTLY, response_data.success_action());
-
-  response = responses[guid4];
-  response_data = response.update_response_data();
-  EXPECT_TRUE(response_data.url().IsEmpty());
-  EXPECT_EQ(0, response_data.size());
-  EXPECT_TRUE(response_data.hash().IsEmpty());
-  EXPECT_EQ(NEEDS_ADMIN_NO, response_data.needs_admin());
-  EXPECT_TRUE(response_data.arguments().IsEmpty());
-  EXPECT_TRUE(guid4 == response_data.guid());
-  EXPECT_STREQ(kResponseStatusNoUpdate, response_data.status());
-  EXPECT_TRUE(GUID_NULL == response_data.installation_id());
-  EXPECT_TRUE(response_data.ap().IsEmpty());
-  EXPECT_TRUE(response_data.success_url().IsEmpty());
-  EXPECT_TRUE(response_data.error_url().IsEmpty());
-  EXPECT_FALSE(response_data.terminate_all_browsers());
-  EXPECT_EQ(SUCCESS_ACTION_DEFAULT, response_data.success_action());
-
-  response = responses[guid2];
-  response_data = response.update_response_data();
-  EXPECT_STREQ(_T("http://dl.google.com/foo/1.0.102.0/user_foo_v1.0.102.0.msi"),
-               response_data.url());
-  EXPECT_STREQ(_T("/XzRh1rpwqrDr6ashpmQnYZIzDI="), response_data.hash());
-  EXPECT_EQ(630152,               response_data.size());
-  EXPECT_EQ(NEEDS_ADMIN_NO,      response_data.needs_admin());
-  EXPECT_STREQ(kResponseStatusOkValue, response_data.status());
-  EXPECT_STREQ(_T("/install"),    response_data.arguments());
-  EXPECT_TRUE(guid2 ==            response_data.guid());
-  EXPECT_TRUE(response_data.success_url().IsEmpty());
-  EXPECT_TRUE(response_data.error_url().IsEmpty());
-  EXPECT_FALSE(response_data.terminate_all_browsers());
-  EXPECT_EQ(SUCCESS_ACTION_DEFAULT, response_data.success_action());
-
-  response = responses[guid3];
-  response_data = response.update_response_data();
-  EXPECT_TRUE(guid3 == response_data.guid());
-  EXPECT_STREQ(kResponseStatusRestrictedExportCountry, response_data.status());
-  EXPECT_FALSE(response_data.terminate_all_browsers());
-  EXPECT_EQ(SUCCESS_ACTION_DEFAULT, response_data.success_action());
-
-  response = responses[guid5];
-  response_data = response.update_response_data();
-  EXPECT_TRUE(response_data.url().IsEmpty());
-  EXPECT_TRUE(response_data.hash().IsEmpty());
-  EXPECT_EQ(0, response_data.size());
-  EXPECT_EQ(NEEDS_ADMIN_NO, response_data.needs_admin());
-  EXPECT_TRUE(response_data.arguments().IsEmpty());
-  EXPECT_TRUE(guid5 == response_data.guid());
-  EXPECT_STREQ(kResponseStatusOsNotSupported, response_data.status());
-  EXPECT_TRUE(GUID_NULL == response_data.installation_id());
-  EXPECT_TRUE(response_data.ap().IsEmpty());
-  EXPECT_TRUE(response_data.success_url().IsEmpty());
-  EXPECT_STREQ(_T("http://foo.google.com/support/article.py?id=12345&")
-               _T("hl=es-419&os=5.1"),
-               response_data.error_url());
-  EXPECT_FALSE(response_data.terminate_all_browsers());
-  EXPECT_EQ(SUCCESS_ACTION_DEFAULT, response_data.success_action());
-  EXPECT_EQ(0, response.num_components());
-}
-
-TEST_F(GoopdateXmlParserTest, ParseManifestBytes_EmptyString) {
-  std::vector<byte> empty_contents;
-  UpdateResponses responses;
-  EXPECT_EQ(E_INVALIDARG, GoopdateXmlParser::ParseManifestBytes(empty_contents,
-                                                                &responses));
-}
-
-TEST_F(GoopdateXmlParserTest, ParseManifestBytes_ManifestNotXml) {
-  std::vector<byte> not_xml;
-  CString not_xml_string(_T("<this> is not XML"));
-  not_xml.resize(not_xml_string.GetLength() * sizeof(TCHAR));
-  memcpy(&not_xml.front(), not_xml_string, not_xml.size());
-
-  UpdateResponses responses;
-  EXPECT_EQ(0xC00CE508, GoopdateXmlParser::ParseManifestBytes(not_xml,
-                                                              &responses));
-}
-
-TEST_F(GoopdateXmlParserTest, VerifyProtocolCompatibility) {
-  // Compatible versions (same major, actual minor >= expected)
-  EXPECT_SUCCEEDED(VerifyProtocolCompatibility(_T("2.0"), _T("2.0")));
-  EXPECT_SUCCEEDED(VerifyProtocolCompatibility(_T("2.00"), _T("2.0")));
-  EXPECT_SUCCEEDED(VerifyProtocolCompatibility(_T("2.001"), _T("2.0")));
-  EXPECT_SUCCEEDED(VerifyProtocolCompatibility(_T("2.1"), _T("2.0")));
-  EXPECT_SUCCEEDED(VerifyProtocolCompatibility(_T("2.9"), _T("2.0")));
-  EXPECT_SUCCEEDED(VerifyProtocolCompatibility(_T("2.9"), _T("2.4")));
-
-  // Incompatible versions (actual < expected)
-  EXPECT_EQ(GOOPDATEXML_E_XMLVERSION,
-            VerifyProtocolCompatibility(_T("2.0"), _T("2.1")));
-  EXPECT_EQ(GOOPDATEXML_E_XMLVERSION,
-            VerifyProtocolCompatibility(_T("2.1"), _T("3.0")));
-
-  // Incompatible versions (actual major < expected major)
-  EXPECT_EQ(GOOPDATEXML_E_XMLVERSION,
-            VerifyProtocolCompatibility(_T("3.0"), _T("2.0")));
-  EXPECT_EQ(GOOPDATEXML_E_XMLVERSION,
-            VerifyProtocolCompatibility(_T("3.0"), _T("2.1")));
-  EXPECT_EQ(GOOPDATEXML_E_XMLVERSION,
-            VerifyProtocolCompatibility(_T("3.0"), _T("2.9")));
-
-  // VerifyProtocolCompatibility isn't perfect.
-  // This test case succeeds but should return GOOPDATEXML_E_XMLVERSION.
-  // We shouldn't ever see this case in a real file.
-  EXPECT_SUCCEEDED(VerifyProtocolCompatibility(
-                       _T("3.0"), _T("2.99999999999999999999999999999")));
-}
-
-TEST_F(GoopdateXmlParserTest, LoadXmlFileToMemory) {
-  CString base_seed_path(ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                         _T("seed_manifest_with_args.xml")));
-  const CString kExpectedManifestContents(
-      _T("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n")
-      _T("<gupdate protocol=\"2.0\" signature=\"\" xmlns=\"http://www.google.com/update2/install\">\r\n")   // NOLINT
-      _T("\t<install appguid=\"{283EAF47-8817-4c2b-A801-AD1FADFB7BAA}\" needsadmin=\"true\" iid=\"{874E4D29-8671-40C8-859F-4DECA4819999}\" client=\"someclient\" ap=\"1.0-dev\"/>\r\n")   // NOLINT
-      _T("</gupdate>\r\n"));
-
-  std::vector<byte> manifest_contents;
-  EXPECT_SUCCEEDED(GoopdateXmlParser::LoadXmlFileToMemory(base_seed_path,
-                                                          &manifest_contents));
-
-  CString manifest_string(Utf8BufferToWideChar(manifest_contents));
-  EXPECT_STREQ(kExpectedManifestContents, manifest_string);
-}
-
-TEST_F(GoopdateXmlParserTest, LoadXmlFileToMemory_EmptyFilename) {
-  CString empty_filename;
-  std::vector<byte> manifest_contents;
-  EXPECT_EQ(E_INVALIDARG, GoopdateXmlParser::LoadXmlFileToMemory(
-                              empty_filename, &manifest_contents));
-}
-
-TEST_F(GoopdateXmlParserTest, LoadXmlFileToMemory_NoSuchFile) {
-  CString no_such_file(_T("no_such_file.xml"));
-  std::vector<byte> manifest_contents;
-  EXPECT_EQ(0x800c0005, GoopdateXmlParser::LoadXmlFileToMemory(
-                            no_such_file, &manifest_contents));
-}
-
-TEST_F(GoopdateXmlParserTest, ConvertStringToSuccessAction_EmptyString) {
-  EXPECT_EQ(SUCCESS_ACTION_DEFAULT, ConvertStringToSuccessAction(_T("")));
-}
-
-TEST_F(GoopdateXmlParserTest, ConvertStringToSuccessAction_Default) {
-  EXPECT_EQ(SUCCESS_ACTION_DEFAULT,
-            ConvertStringToSuccessAction(_T("default")));
-}
-
-TEST_F(GoopdateXmlParserTest, ConvertStringToSuccessAction_ExitSilently) {
-  EXPECT_EQ(SUCCESS_ACTION_EXIT_SILENTLY,
-            ConvertStringToSuccessAction(_T("exitsilently")));
-}
-
-TEST_F(GoopdateXmlParserTest,
-       ConvertStringToSuccessAction_ExitSilentlyOnLaunchCmd) {
-  EXPECT_EQ(SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD,
-            ConvertStringToSuccessAction(_T("exitsilentlyonlaunchcmd")));
-}
-
-TEST_F(GoopdateXmlParserTest, ConvertStringToSuccessAction_UnknownAction) {
-  ExpectAsserts expect_asserts;
-  EXPECT_EQ(SUCCESS_ACTION_DEFAULT,
-            ConvertStringToSuccessAction(_T("foo bar")));
-}
-
-}  // namespace omaha
-
diff --git a/goopdate/install_manager.cc b/goopdate/install_manager.cc
new file mode 100644
index 0000000..9522f98
--- /dev/null
+++ b/goopdate/install_manager.cc
@@ -0,0 +1,361 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/install_manager.h"
+#include <vector>
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/install_manifest.h"
+#include "omaha/goopdate/app_manager.h"
+#include "omaha/goopdate/installer_wrapper.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/server_resource.h"
+#include "omaha/goopdate/string_formatter.h"
+
+namespace omaha {
+
+namespace {
+
+// Number of tries when the MSI service is busy.
+// Updates are silent so we can wait longer.
+const int kNumMsiAlreadyRunningInteractiveMaxTries = 4;  // Up to 35 seconds.
+const int kNumMsiAlreadyRunningSilentMaxTries      = 7;  // Up to 6.25 minutes.
+
+// TODO(omaha): there can be more install actions for each install event.
+bool GetInstallActionForEvent(
+    const std::vector<xml::InstallAction>& install_actions,
+    xml::InstallAction::InstallEvent install_event,
+    xml::InstallAction* action) {
+  ASSERT1(action);
+
+  for (size_t i = 0; i < install_actions.size(); ++i) {
+    if (install_actions[i].install_event == install_event) {
+      *action = install_actions[i];
+      return true;
+    }
+  }
+
+  return false;
+}
+
+}  // namespace
+
+InstallManager::InstallManager(const Lockable* model_lock, bool is_machine)
+    : model_lock_(model_lock),
+      is_machine_(is_machine) {
+  CORE_LOG(L3, (_T("[InstallManager::InstallManager][%d]"), is_machine_));
+
+  install_working_dir_ =
+      is_machine ?
+      ConfigManager::Instance()->GetMachineInstallWorkingDir() :
+      ConfigManager::Instance()->GetUserInstallWorkingDir();
+  CORE_LOG(L3, (_T("[install_working_dir][%s]"), install_working_dir()));
+
+  VERIFY1(SUCCEEDED(DeleteDirectory(install_working_dir_)));
+  VERIFY1(SUCCEEDED(CreateDir(install_working_dir_, NULL)));
+
+  installer_wrapper_.reset(new InstallerWrapper(is_machine_));
+}
+
+InstallManager::~InstallManager() {
+  CORE_LOG(L3, (_T("[InstallManager::~InstallManager]")));
+}
+
+HRESULT InstallManager::Initialize() {
+  return installer_wrapper_->Initialize();
+}
+
+CString InstallManager::install_working_dir() const {
+  __mutexScope(model_lock_);
+  return install_working_dir_;
+}
+
+// For each app, set the state to STATE_INSTALLING, install it, and update the
+// state of the model after it completes.
+void InstallManager::InstallApp(App* app,  const CString& dir) {
+  CORE_LOG(L3, (_T("[InstallManager::InstallApp][0x%p]"), app));
+  ASSERT1(app);
+
+  const ConfigManager& cm = *ConfigManager::Instance();
+  // TODO(omaha): Since we don't currently have is_manual, check the least
+  // restrictive case of true. It would be nice if we had is_manual. We'll see.
+  ASSERT(SUCCEEDED(app->CheckGroupPolicy()),
+         (_T("Installing/updating app for which this is not allowed.")));
+  ASSERT(app->is_eula_accepted() ||
+         app->is_install() && app->app_bundle()->is_offline_install(),
+         (_T("update/online install of app for which EULA is not accepted.")));
+
+  // TODO(omaha3): This needs to be set/passed per app/bundle.
+  const int priority = app->app_bundle()->priority();
+  const int num_tries = (priority < INSTALL_PRIORITY_HIGH) ?
+                             kNumMsiAlreadyRunningSilentMaxTries :
+                             kNumMsiAlreadyRunningInteractiveMaxTries;
+  installer_wrapper_->set_num_tries_when_msi_busy(num_tries);
+
+  AppVersion* next_version = app->next_version();
+  ASSERT1(app->app_bundle()->is_machine() == is_machine_);
+
+  const CString current_version_string = app->current_version()->version();
+
+  HANDLE primary_token(app->app_bundle()->primary_token());
+
+  HRESULT hr = InstallApp(is_machine_,
+                          primary_token,
+                          current_version_string,
+                          *model_lock_,
+                          installer_wrapper_.get(),
+                          app,
+                          dir);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[InstallApp failed][0x%p][0x%08x]"), app, hr));
+  }
+
+  app->LogTextAppendFormat(_T("Install result=0x%08x"), hr);
+
+  ASSERT1(FAILED(hr) == (app->state() == STATE_ERROR));
+}
+
+HRESULT InstallManager::InstallApp(bool is_machine,
+                                   HANDLE user_token,
+                                   const CString& existing_version,
+                                   const Lockable& model_lock,
+                                   InstallerWrapper* installer_wrapper,
+                                   App* app,
+                                   const CString& dir) {
+  UNREFERENCED_PARAMETER(is_machine);
+  ASSERT1(installer_wrapper);
+  ASSERT1(app);
+
+  const bool is_update = app->is_update();
+
+  CString display_name;
+  GUID app_guid = {0};
+  CString installer_path;
+  // TODO(omaha3): Consider generating the installerdata file external to the
+  // InstallerWrapper and just adding the path to the arguments.
+  CString manifest_arguments;
+  CString installer_data;
+  CString expected_version;
+
+  AppManager& app_manager = *AppManager::Instance();
+  __mutexScope(app_manager.GetRegistryStableStateLock());
+
+  // TODO(omaha): If this does not get much simpler, extract method.
+  AppVersion& next_version = *(app->next_version());
+  ASSERT1(app->app_bundle()->is_machine() == is_machine);
+
+  CString language = app->app_bundle()->display_language();
+
+  // TODO(omaha): review the need for locking below.
+  __mutexBlock(model_lock) {
+    app->Installing();
+
+    app_guid = app->app_guid();
+
+    // The first package is always the Package Manager for the app.
+    // TODO(omaha3): Use program_to_run here instead of the installer_path. This
+    // will introduce complexity such as having to find the full path and
+    // handling the case where the file is not in the cache.
+    ASSERT1(next_version.GetNumberOfPackages() > 0);
+    if (next_version.GetNumberOfPackages() <= 0) {
+      HRESULT hr = E_FAIL;
+      const CString message = InstallerWrapper::GetMessageForError(hr,
+                                                                   CString(),
+                                                                   language);
+      app->Error(ErrorContext(hr), message);
+      return hr;
+    }
+    const Package& package_manager = *(next_version.GetPackage(0));
+    installer_path = ConcatenatePath(dir, package_manager.filename());
+
+    xml::InstallAction action;
+    const bool is_event_found = GetInstallActionForEvent(
+        next_version.install_manifest()->install_actions,
+        is_update ? xml::InstallAction::kUpdate : xml::InstallAction::kInstall,
+        &action);
+    ASSERT1(is_event_found);
+    if (is_event_found) {
+      manifest_arguments = action.program_arguments;
+
+      // If this is an Omaha self-update, append a switch to the argument list
+      // to set the session ID.
+      if (is_update && ::IsEqualGUID(app_guid, kGoopdateGuid)) {
+        SafeCStringAppendFormat(&manifest_arguments, _T(" /%s \"%s\""),
+                                kCmdLineSessionId,
+                                app->app_bundle()->session_id());
+      }
+    }
+
+    installer_data = app->GetInstallData();
+
+    expected_version = next_version.install_manifest()->version;
+
+    // TODO(omaha3): All app key registry writes and reads must be protected by
+    // some lock to prevent race conditions caused by multiple bundles
+    // installing the same app. This includes while writing the pre-install
+    // data, while the installer is running, and while checking application
+    // registration (basically the rest of this method). An app-specific lock
+    // similar to the app install lock in Omaha 2 seems to be appropriate.
+    // (Omaha 2 only took that during install - not updates - though.)
+    // Some app installers may also check the state of other apps (i.e. to check
+    // for version compatibility). Should we protect this case as well?
+    // Is it safest to use the installer lock for this? What is the performance
+    // impact. If we do use the installer lock, it would need to be acquired
+    // here instead of in the InstallerWrapper::InstallApp path.
+    if (!is_update) {
+      HRESULT hr = app_manager.WritePreInstallData(*app);
+      if (FAILED(hr)) {
+        CORE_LOG(LE, (_T("[AppManager::WritePreInstallData failed][0x%08x]")));
+        const CString message =
+            InstallerWrapper::GetMessageForError(hr, CString(), language);
+        app->Error(ErrorContext(hr), message);
+        return hr;
+      }
+    }
+  }
+
+  OPT_LOG(L1, (_T("[Installing][%s][%s][%s][%s][%s]"),
+               app->display_name(),
+               GuidToString(app_guid),
+               installer_path,
+               manifest_arguments,
+               installer_data));
+
+  InstallerResultInfo result_info;
+
+  HRESULT hr = installer_wrapper->InstallApp(user_token,
+                                             app_guid,
+                                             installer_path,
+                                             manifest_arguments,
+                                             installer_data,
+                                             language,
+                                             &result_info);
+
+  OPT_LOG(L1, (_T("[InstallApp returned][0x%x][%s][type:%d][code: %d][%s][%s]"),
+               hr, GuidToString(app_guid), result_info.type, result_info.code,
+               result_info.text, result_info.post_install_launch_command_line));
+
+  __mutexScope(model_lock);
+
+  if (SUCCEEDED(hr)) {
+    ASSERT1(result_info.type == INSTALLER_RESULT_SUCCESS);
+    // Skip checking application registration for Omaha self-updates because the
+    // installer has not completed.
+    if (!::IsEqualGUID(kGoopdateGuid, app_guid)) {
+      hr = app_manager.ReadInstallerRegistrationValues(app);
+      if (SUCCEEDED(hr)) {
+        hr = installer_wrapper->CheckApplicationRegistration(
+            app_guid,
+            next_version.version(),
+            expected_version,
+            existing_version,
+            is_update);
+      }
+    }
+  } else {
+    ASSERT1(result_info.type != INSTALLER_RESULT_SUCCESS);
+    ASSERT1(!result_info.text.IsEmpty() ||
+            result_info.type == INSTALLER_RESULT_UNKNOWN);
+  }
+
+  if (FAILED(hr)) {
+    // If we failed the install job and the product wasn't registered, it's safe
+    // to delete the ClientState key.  We need to remove it because it contains
+    // data like "ap", browsertype, language, etc. that need to be cleaned up in
+    // case user tries to install again in the future.
+    if (!is_update && !app_manager.IsAppRegistered(app->app_guid())) {
+      app_manager.RemoveClientState(app->app_guid());
+    }
+
+    if (hr == GOOPDATEINSTALL_E_INSTALLER_FAILED) {
+      ASSERT1(!result_info.text.IsEmpty());
+      app->ReportInstallerComplete(result_info);
+    } else {
+      // TODO(omaha3): If we end up having the installer filename above, pass it
+      // instead of installer_path.
+      const CString message = InstallerWrapper::GetMessageForError(
+                                  hr, installer_path, language);
+      app->Error(ErrorContext(hr), message);
+    }
+
+    return hr;
+  }
+
+  PopulateSuccessfulInstallResultInfo(app, &result_info);
+
+  app->ReportInstallerComplete(result_info);
+  return S_OK;
+}
+
+// Call this function only when installer succeeded.
+// This function sets the post install action and url based on installer result
+// info and app manifest. Note that the action may already have been set by
+// InstallerWrapper::GetInstallerResultHelper() in some cases. This function
+// only sets the action when necessary.
+void InstallManager::PopulateSuccessfulInstallResultInfo(
+    const App* app,
+    InstallerResultInfo* result_info) {
+  ASSERT1(result_info);
+  ASSERT1(result_info->type == INSTALLER_RESULT_SUCCESS);
+  ASSERT1(app);
+  ASSERT1(app->next_version()->install_manifest());
+
+  xml::InstallAction action;
+  if (GetInstallActionForEvent(
+          app->next_version()->install_manifest()->install_actions,
+          xml::InstallAction::kPostInstall,
+          &action)) {
+    result_info->post_install_url = action.success_url;
+
+    if (result_info->post_install_launch_command_line.IsEmpty() &&
+        action.success_action == SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD) {
+      CORE_LOG(LW, (_T("[Success action specified launchcmd, but cmd empty]")));
+    }
+
+    if (result_info->post_install_action ==
+            POST_INSTALL_ACTION_LAUNCH_COMMAND &&
+        action.success_action == SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD) {
+      result_info->post_install_action =
+          POST_INSTALL_ACTION_EXIT_SILENTLY_ON_LAUNCH_COMMAND;
+    } else if (result_info->post_install_action ==
+        POST_INSTALL_ACTION_DEFAULT) {
+      if (action.success_action == SUCCESS_ACTION_EXIT_SILENTLY) {
+        result_info->post_install_action = POST_INSTALL_ACTION_EXIT_SILENTLY;
+      } else if (!result_info->post_install_url.IsEmpty()) {
+        result_info->post_install_action = action.terminate_all_browsers ?
+            POST_INSTALL_ACTION_RESTART_ALL_BROWSERS :
+            POST_INSTALL_ACTION_RESTART_BROWSER;
+      }
+    }
+  }
+
+  // Load message based on post install action if not overridden.
+  if (result_info->text.IsEmpty()) {
+    StringFormatter formatter(app->app_bundle()->display_language());
+    VERIFY1(SUCCEEDED(formatter.LoadString(
+                          IDS_APPLICATION_INSTALLED_SUCCESSFULLY,
+                          &result_info->text)));
+  }
+}
+
+}  // namespace omaha
diff --git a/goopdate/install_manager.h b/goopdate/install_manager.h
new file mode 100644
index 0000000..3bfa248
--- /dev/null
+++ b/goopdate/install_manager.h
@@ -0,0 +1,88 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Defines a command object to be executed in the thread pool when the Install
+// method is called.
+
+#ifndef OMAHA_GOOPDATE_INSTALL_MANAGER_H_
+#define OMAHA_GOOPDATE_INSTALL_MANAGER_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/thread_pool.h"
+
+namespace omaha {
+
+class App;
+class AppVersion;
+class InstallerWrapper;
+struct InstallerResultInfo;
+struct Lockable;
+
+// Public interface for the InstallManager.
+class InstallManagerInterface {
+ public:
+  virtual ~InstallManagerInterface() {}
+  virtual CString install_working_dir() const = 0;
+  virtual HRESULT Initialize() = 0;
+  virtual void InstallApp(App* app, const CString& dir) = 0;
+};
+
+class InstallManager : public InstallManagerInterface {
+ public:
+  InstallManager(const Lockable* model_lock, bool is_machine_);
+  virtual ~InstallManager();
+
+  // Returns the base directory where the InstallManager expects application
+  // packages to be available before the install.
+  virtual CString install_working_dir() const;
+
+  virtual HRESULT Initialize();
+
+  // Installs an application. Expects the application packages to be present
+  // in the specified directory.
+  virtual void InstallApp(App* app, const CString& dir);
+
+ private:
+  // TODO(omaha): Rename to avoid overload.
+  static HRESULT InstallApp(bool is_machine,
+                            HANDLE user_token,
+                            const CString& existing_version,
+                            const Lockable& model_lock,
+                            InstallerWrapper* installer_wrapper,
+                            App* app,
+                            const CString& dir);
+  static void PopulateSuccessfulInstallResultInfo(
+      const App* app,
+      InstallerResultInfo* result_info);
+
+  const Lockable* model_lock_;
+  const bool is_machine_;
+
+  // Base path where verified application packages are copied before install.
+  CString install_working_dir_;
+
+  scoped_ptr<InstallerWrapper> installer_wrapper_;
+
+  friend class InstallManagerInstallAppTest;
+
+  DISALLOW_COPY_AND_ASSIGN(InstallManager);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_INSTALL_MANAGER_H_
diff --git a/goopdate/install_manager_unittest.cc b/goopdate/install_manager_unittest.cc
new file mode 100644
index 0000000..96f0f79
--- /dev/null
+++ b/goopdate/install_manager_unittest.cc
@@ -0,0 +1,1143 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <atlpath.h>
+#include <atlstr.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/shell.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/system.h"
+#include "omaha/base/timer.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/install_manifest.h"
+#include "omaha/common/ping_event.h"
+#include "omaha/goopdate/app_bundle_state_initialized.h"
+#include "omaha/goopdate/app_state_waiting_to_install.h"
+#include "omaha/goopdate/app_unittest_base.h"
+#include "omaha/goopdate/installer_wrapper.h"
+#include "omaha/goopdate/install_manager.h"
+#include "omaha/testing/unit_test.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace omaha {
+
+// TODO(omaha): there is a problem with this unit test. The model is built
+// bottom up. This makes it impossible to set the references to parents. Will
+// have to fix the code, eventually using Builder DP to create a bunch of
+// models containing bundles, apps, and such.
+
+namespace {
+
+const TCHAR kAppId[] = _T("{B18BC01B-E0BD-4BF0-A33E-1133055E5FDE}");
+const GUID kAppGuid = {0xB18BC01B, 0xE0BD, 0x4BF0,
+                       {0xA3, 0x3E, 0x11, 0x33, 0x05, 0x5E, 0x5F, 0xDE}};
+const TCHAR kApp2Id[] = _T("{85794B39-42E5-457c-B567-4A0F2A0FB272}");
+
+const TCHAR kFullAppClientsKeyPath[] =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\Clients\\{B18BC01B-E0BD-4BF0-A33E-1133055E5FDE}");
+const TCHAR kFullAppClientStateKeyPath[] =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\{B18BC01B-E0BD-4BF0-A33E-1133055E5FDE}");
+const TCHAR kFullFooAppClientKeyPath[] =
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\Clients\\{D6B08267-B440-4C85-9F79-E195E80D9937}");
+const TCHAR kFullFooAppClientStateKeyPath[] =
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\{D6B08267-B440-4C85-9F79-E195E80D9937}");
+
+const TCHAR kFullApp2ClientsKeyPath[] =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\Clients\\{85794B39-42E5-457c-B567-4A0F2A0FB272}");
+
+const TCHAR kSetupFooV1RelativeLocation[] =
+  _T("unittest_support\\test_foo_v1.0.101.0.msi");
+const TCHAR kFooId[] = _T("{D6B08267-B440-4C85-9F79-E195E80D9937}");
+const TCHAR kFooVersion[] = _T("1.0.101.0");
+const TCHAR kFooInstallerBarPropertyArg[] = _T("PROPBAR=7");
+const TCHAR kFooInstallerBarValueName[] = _T("propbar");
+
+// Values related to using cmd.exe as an "installer".
+const TCHAR kCmdExecutable[] = _T("cmd.exe");
+const TCHAR kExecuteCommandAndTerminateSwitch[] = _T("/c %s");
+const TCHAR kExecuteTwoCommandsFormat[] = _T("\"%s & %s\"");
+
+const TCHAR kMsiInstallerBusyErrorMessage[] =
+    _T("Installation failed because the Windows Installer is busy. Please ")
+    _T("wait for any installers to finish, close all installer windows, and ")
+    _T("try installing again. If this problem persists, you may need ")
+    _T("to reboot your computer.");
+
+const TCHAR kMsiLogFormat[] = _T("%s.log");
+
+// brand, InstallTime, and LastCheckSuccess are automatically populated.
+const int kNumAutoPopulatedValues = 3;
+
+}  // namespace
+
+// Values and functions in installer_wrapper_unittest.cc.
+extern const TCHAR kRegExecutable[];
+extern const TCHAR kSetInstallerResultTypeMsiErrorRegCmdArgs[];
+extern const TCHAR kMsiInstallerBusyExitCodeCmd[];
+extern const TCHAR kError1619MessagePrefix[];
+extern const int kError1619MessagePrefixLength;
+void VerifyStringIsMsiPackageOpenFailedString(const CString& str);
+void UninstallTestMsi(const CString& installer_path);
+void AdjustMsiTries(InstallerWrapper* installer_wrapper);
+
+// TODO(omaha3): Test the rest of InstallManager.
+class InstallManagerTest : public testing::Test {
+  virtual void SetUp() {}
+  virtual void TearDown() {}
+};
+
+class InstallManagerInstallAppTest : public AppTestBaseWithRegistryOverride {
+ protected:
+  explicit InstallManagerInstallAppTest(bool is_machine)
+      : AppTestBaseWithRegistryOverride(is_machine, false) {}
+
+  static void SetUpTestCase() {
+    CString system_path;
+    EXPECT_SUCCEEDED(Shell::GetSpecialFolder(CSIDL_SYSTEM,
+                                             false,
+                                             &system_path));
+    EXPECT_FALSE(system_path.IsEmpty());
+    cmd_exe_dir_ += system_path;
+
+    CPath cmd_exe_path;
+    cmd_exe_path.Combine(system_path, kCmdExecutable);
+    EXPECT_TRUE(File::Exists(cmd_exe_path));
+
+    CPath reg_path;
+    reg_path.Combine(system_path, kRegExecutable);
+    set_installer_result_type_msi_error_cmd_.Format(
+        _T("%s %s"),
+        reg_path, kSetInstallerResultTypeMsiErrorRegCmdArgs);
+  }
+
+  virtual void SetUp() {
+    AppTestBaseWithRegistryOverride::SetUp();
+
+    installer_wrapper_.reset(new InstallerWrapper(is_machine_));
+    EXPECT_SUCCEEDED(installer_wrapper_->Initialize());
+
+    ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kAppId), &app_));
+
+    SetAppStateWaitingToInstall(app_);
+  }
+
+  HRESULT InstallApp(const CString& existing_version,
+                     App* app,
+                     const CString& dir) {
+    ASSERT1(app);
+    return InstallManager::InstallApp(is_machine_,
+                                      NULL,
+                                      existing_version,
+                                      app->model()->lock(),
+                                      installer_wrapper_.get(),
+                                      app,
+                                      dir);
+  }
+
+  void SetArgumentsInManifest(const CString& arguments,
+                              const CString& expected_version,
+                              App* app) {
+    ASSERT1(app);
+
+    // TODO(omaha3): Consider using a mock object.
+    xml::InstallManifest* install_manifest = new xml::InstallManifest;
+    install_manifest->version = expected_version;
+
+    xml::InstallAction install_event_action;
+    install_event_action.install_event = xml::InstallAction::kInstall;
+    install_event_action.needs_admin = is_machine_ ? NEEDS_ADMIN_YES :
+                                                     NEEDS_ADMIN_NO;
+    // TODO(omaha3): Set install_event_action.program_to_run?
+    install_event_action.program_arguments = arguments;
+
+    install_manifest->install_actions.push_back(install_event_action);
+    app->next_version()->set_install_manifest(install_manifest);
+  }
+
+  void SetPostInstallActionInManifest(SuccessfulInstallAction success_action,
+                                      const CString& success_url,
+                                      bool terminate_all_browsers,
+                                      App* app) {
+    xml::InstallManifest* install_manifest = new xml::InstallManifest;
+    install_manifest->version = _T("1.2.3.4");
+
+    xml::InstallAction post_install_event_action;
+    post_install_event_action.install_event = xml::InstallAction::kPostInstall;
+    post_install_event_action.needs_admin = is_machine_ ? NEEDS_ADMIN_YES :
+                                                          NEEDS_ADMIN_NO;
+    post_install_event_action.success_url = success_url;
+    post_install_event_action.terminate_all_browsers = terminate_all_browsers;
+    post_install_event_action.success_action = success_action;
+    install_manifest->install_actions.push_back(post_install_event_action);
+    app->next_version()->set_install_manifest(install_manifest);
+  }
+
+  static void SetAppStateWaitingToInstall(App* app) {
+    SetAppStateForUnitTest(app, new fsm::AppStateWaitingToInstall);
+  }
+
+  static HRESULT GetResultCode(const App* app) {
+    return app->error_context_.error_code;
+  }
+
+  static CString GetCompletionMessage(const App* app) {
+    return app->completion_message_;
+  }
+
+  static PingEvent::Results GetCompletionResult(const App* app) {
+    return app->completion_result_;
+  }
+
+  static CString GetPostInstallLaunchCommandLine(const App* app) {
+    return app->post_install_launch_command_line_;
+  }
+
+  static CString GetPostInstallUrl(const App* app) {
+    return app->post_install_url_;
+  }
+
+  static PostInstallAction GetPostInstallAction(const App* app) {
+    return app->post_install_action_;
+  }
+
+  static void PopulateSuccessfulInstallResultInfo(
+      const App* app, InstallerResultInfo* result_info) {
+    return InstallManager::PopulateSuccessfulInstallResultInfo(
+        app, result_info);
+  }
+
+  scoped_ptr<InstallerWrapper> installer_wrapper_;
+
+  App* app_;
+
+  static CPath cmd_exe_dir_;
+  static CString set_installer_result_type_msi_error_cmd_;
+};
+
+CPath InstallManagerInstallAppTest::cmd_exe_dir_;
+CString InstallManagerInstallAppTest::set_installer_result_type_msi_error_cmd_;
+
+class InstallManagerInstallAppMachineTest
+    : public InstallManagerInstallAppTest {
+ protected:
+  InstallManagerInstallAppMachineTest()
+    : InstallManagerInstallAppTest(true) {
+  }
+};
+
+class InstallManagerInstallAppUserTest : public InstallManagerInstallAppTest {
+ protected:
+  InstallManagerInstallAppUserTest()
+    : InstallManagerInstallAppTest(false) {
+  }
+};
+
+//
+// Helper method tests
+//
+
+// TODO(omaha3): We may replace these with the tests from
+// installer_wrapper_unittest.cc if CheckApplicationRegistration() is moved to
+// InstallManager.
+#if 0
+TEST_F(InstallManagerInstallAppUserTest,
+       CheckApplicationRegistration_FailsWhenClientsKeyAbsent) {
+  AppData app_data(kAppGuid, is_machine_);
+  Job job(false, &ping_);
+  job.set_app_data(app_data);
+
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY,
+            CheckApplicationRegistration(CString(), &job));
+
+  EXPECT_FALSE(RegKey::HasKey(kFullAppClientsKeyPath));
+  EXPECT_FALSE(RegKey::HasKey(kFullAppClientStateKeyPath));
+}
+
+TEST_F(InstallManagerInstallAppUserTest,
+       CheckApplicationRegistration_FailsWhenVersionValueAbsent) {
+  ASSERT_SUCCEEDED(RegKey::CreateKey(kFullAppClientsKeyPath));
+
+  AppData app_data(kAppGuid, is_machine_);
+  Job job(false, &ping_);
+  job.set_app_data(app_data);
+
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY,
+            CheckApplicationRegistration(CString(), &job));
+
+  EXPECT_TRUE(RegKey::HasKey(kFullAppClientsKeyPath));
+  EXPECT_FALSE(RegKey::HasKey(kFullAppClientStateKeyPath));
+}
+
+TEST_F(InstallManagerInstallAppUserTest,
+       CheckApplicationRegistration_SucceedsWhenStateKeyAbsent) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kFullAppClientsKeyPath,
+                                    kRegValueProductVersion,
+                                    _T("0.9.68.4")));
+
+  AppData app_data(kAppGuid, is_machine_);
+  Job job(false, &ping_);
+  job.set_app_data(app_data);
+
+  EXPECT_SUCCEEDED(CheckApplicationRegistration(CString(), &job));
+}
+
+TEST_F(InstallManagerInstallAppUserTest,
+       CheckApplicationRegistration_SucceedsWhenStateKeyPresent) {
+  const TCHAR* keys_to_create[] = {kFullAppClientsKeyPath,
+                                   kFullAppClientStateKeyPath};
+  ASSERT_SUCCEEDED(RegKey::CreateKeys(keys_to_create, 2));
+  ASSERT_TRUE(RegKey::HasKey(kFullAppClientsKeyPath));
+  ASSERT_TRUE(RegKey::HasKey(kFullAppClientStateKeyPath));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kFullAppClientsKeyPath,
+                                    kRegValueProductVersion,
+                                    _T("0.9.70.0")));
+
+  AppData app_data(kAppGuid, is_machine_);
+  Job job(false, &ping_);
+  job.set_app_data(app_data);
+
+  // The install should succeed even if the version is the same.
+  EXPECT_SUCCEEDED(CheckApplicationRegistration(_T("0.9.70.0"), &job));
+}
+
+TEST_F(InstallManagerInstallAppUserTest,
+       CheckApplicationRegistration_UpdateSucceeds) {
+  const TCHAR* keys_to_create[] = {kFullAppClientsKeyPath,
+                                   kFullAppClientStateKeyPath};
+  ASSERT_SUCCEEDED(RegKey::CreateKeys(keys_to_create, 2));
+  ASSERT_TRUE(RegKey::HasKey(kFullAppClientsKeyPath));
+  ASSERT_TRUE(RegKey::HasKey(kFullAppClientStateKeyPath));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kFullAppClientsKeyPath,
+                                    kRegValueProductVersion,
+                                    _T("0.9.70.0")));
+
+  AppData app_data(kAppGuid, is_machine_);
+  Job job(true, &ping_);
+  job.set_app_data(app_data);
+
+  EXPECT_SUCCEEDED(CheckApplicationRegistration(_T("0.9.70.1"), &job));
+}
+
+TEST_F(InstallManagerInstallAppUserTest,
+       CheckApplicationRegistration_UpdateFailsWhenVersionDoesNotChange) {
+  const TCHAR* keys_to_create[] = {kFullAppClientsKeyPath,
+                                   kFullAppClientStateKeyPath};
+  ASSERT_SUCCEEDED(RegKey::CreateKeys(keys_to_create,
+                                      arraysize(keys_to_create)));
+  ASSERT_TRUE(RegKey::HasKey(kFullAppClientsKeyPath));
+  ASSERT_TRUE(RegKey::HasKey(kFullAppClientStateKeyPath));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kFullAppClientsKeyPath,
+                                    kRegValueProductVersion,
+                                    _T("0.9.70.0")));
+
+  AppData app_data(kAppGuid, is_machine_);
+  Job job(true, &ping_);
+  job.set_app_data(app_data);
+
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_CHANGE_VERSION,
+            CheckApplicationRegistration(_T("0.9.70.0"), &job));
+}
+#endif
+
+//
+// Negative Tests
+//
+
+TEST_F(InstallManagerInstallAppUserTest,
+       InstallApp_InstallerWithoutFilenameExtension) {
+  app_->next_version()->AddPackage(_T("foo"), 100, _T("hash"));
+
+  // TODO(omaha): We should be able to eliminate this.
+  SetArgumentsInManifest(CString(), _T("1.2.3.4"), app_);
+
+  EXPECT_EQ(GOOPDATEINSTALL_E_FILENAME_INVALID,
+            InstallApp(_T(""), app_, _T("c:\\temp")));
+
+  EXPECT_EQ(STATE_ERROR, app_->state());
+  EXPECT_EQ(PingEvent::EVENT_RESULT_ERROR, GetCompletionResult(app_));
+  EXPECT_EQ(GOOPDATEINSTALL_E_FILENAME_INVALID, GetResultCode(app_));
+  EXPECT_STREQ(
+      _T("The installer filename c:\\temp\\foo is invalid or unsupported."),
+      GetCompletionMessage(app_));
+  EXPECT_TRUE(GetPostInstallLaunchCommandLine(app_).IsEmpty());
+  EXPECT_TRUE(GetPostInstallUrl(app_).IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, GetPostInstallAction(app_));
+}
+
+TEST_F(InstallManagerInstallAppUserTest,
+       InstallApp_UnsupportedInstallerFilenameExtension) {
+  app_->next_version()->AddPackage(_T("foo.bar"), 100, _T("hash"));
+
+  // TODO(omaha): We should be able to eliminate this.
+  SetArgumentsInManifest(CString(), _T("1.2.3.4"), app_);
+
+  EXPECT_EQ(GOOPDATEINSTALL_E_FILENAME_INVALID,
+            InstallApp(_T(""), app_, _T("c:\\temp")));
+
+  EXPECT_EQ(STATE_ERROR, app_->state());
+  EXPECT_EQ(PingEvent::EVENT_RESULT_ERROR, GetCompletionResult(app_));
+  EXPECT_EQ(GOOPDATEINSTALL_E_FILENAME_INVALID, GetResultCode(app_));
+  EXPECT_STREQ(
+      _T("The installer filename c:\\temp\\foo.bar is invalid or unsupported."),
+      GetCompletionMessage(app_));
+  EXPECT_TRUE(GetPostInstallLaunchCommandLine(app_).IsEmpty());
+  EXPECT_TRUE(GetPostInstallUrl(app_).IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, GetPostInstallAction(app_));
+}
+
+TEST_F(InstallManagerInstallAppUserTest, InstallApp_InstallerEmtpyFilename) {
+  // Package asserts that the filename and file path are not NULL.
+  ExpectAsserts expect_asserts;
+
+  app_->next_version()->AddPackage(_T(""), 100, _T("hash"));
+  // This test does not call
+  // app_->next_version()->GetPackage(0)->set_local_file_path().
+
+  // TODO(omaha): We should be able to eliminate this.
+  SetArgumentsInManifest(CString(), _T("1.2.3.4"), app_);
+
+  EXPECT_EQ(GOOPDATEINSTALL_E_FILENAME_INVALID,
+            InstallApp(_T(""), app_, NULL));
+
+  EXPECT_EQ(STATE_ERROR, app_->state());
+  EXPECT_EQ(PingEvent::EVENT_RESULT_ERROR, GetCompletionResult(app_));
+  EXPECT_EQ(GOOPDATEINSTALL_E_FILENAME_INVALID, GetResultCode(app_));
+  EXPECT_STREQ(_T("The installer filename \\ is invalid or unsupported."),
+               GetCompletionMessage(app_));
+  EXPECT_TRUE(GetPostInstallLaunchCommandLine(app_).IsEmpty());
+  EXPECT_TRUE(GetPostInstallUrl(app_).IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, GetPostInstallAction(app_));
+}
+
+TEST_F(InstallManagerInstallAppUserTest, InstallApp_NoPackage) {
+  // InstallApp asserts that there is at least one package.
+  ExpectAsserts expect_asserts;
+
+  EXPECT_EQ(E_FAIL, InstallApp(_T(""), app_, NULL));
+
+  EXPECT_EQ(STATE_ERROR, app_->state());
+  EXPECT_EQ(PingEvent::EVENT_RESULT_ERROR, GetCompletionResult(app_));
+  EXPECT_EQ(E_FAIL, GetResultCode(app_));
+  EXPECT_STREQ(
+      _T("Installation failed. Please try again."),
+      GetCompletionMessage(app_));
+  EXPECT_TRUE(GetPostInstallLaunchCommandLine(app_).IsEmpty());
+  EXPECT_TRUE(GetPostInstallUrl(app_).IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, GetPostInstallAction(app_));
+}
+
+TEST_F(InstallManagerInstallAppUserTest, InstallApp_ExeFileDoesNotExist) {
+  app_->next_version()->AddPackage(_T("foo.exe"), 100, _T("hash"));
+
+  // TODO(omaha): We should be able to eliminate this.
+  SetArgumentsInManifest(CString(), _T("1.2.3.4"), app_);
+
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED_START,
+            InstallApp(_T(""), app_, _T("c:\\temp")));
+
+  EXPECT_EQ(STATE_ERROR, app_->state());
+  EXPECT_EQ(PingEvent::EVENT_RESULT_ERROR, GetCompletionResult(app_));
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED_START, GetResultCode(app_));
+  EXPECT_STREQ(_T("The installer failed to start."),
+               GetCompletionMessage(app_));
+
+  EXPECT_FALSE(RegKey::HasKey(kFullAppClientsKeyPath));
+  EXPECT_FALSE(RegKey::HasKey(kFullAppClientStateKeyPath));
+  EXPECT_TRUE(GetPostInstallLaunchCommandLine(app_).IsEmpty());
+  EXPECT_TRUE(GetPostInstallUrl(app_).IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, GetPostInstallAction(app_));
+}
+
+//
+// EXE Installer Tests
+//
+
+// TODO(omaha3): Add InstallApp_ExeInstallerWithoutArgumentsSucceeds using
+// SaveArguments.exe. Do the same in InstallerWrapperUserTest.
+
+// TODO(omaha3): Add InstallApp tests that cause
+// ReadInstallerRegistrationValues & CheckApplicationRegistration to fail.
+
+// This test uses cmd.exe as an installer that leaves the payload
+// kPayloadFileName.
+TEST_F(InstallManagerInstallAppUserTest,
+       InstallApp_ExeInstallerWithArgumentsSucceeds) {
+  const TCHAR kPayloadFileName[] = _T("exe_payload.txt");
+  const TCHAR kCommandToExecute[] = _T("echo \"hi\" > %s");
+
+  CString full_command_to_execute;
+  full_command_to_execute.Format(kCommandToExecute, kPayloadFileName);
+  CString arguments;
+  arguments.Format(kExecuteCommandAndTerminateSwitch, full_command_to_execute);
+
+  EXPECT_SUCCEEDED(File::Remove(kPayloadFileName));
+  EXPECT_FALSE(File::Exists(kPayloadFileName));
+
+  // Create the Clients key since this isn't an actual installer.
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kFullAppClientsKeyPath));
+  EXPECT_TRUE(RegKey::HasKey(kFullAppClientsKeyPath));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kFullAppClientsKeyPath,
+                                    kRegValueProductVersion,
+                                    _T("0.10.69.5")));
+
+  app_->next_version()->AddPackage(kCmdExecutable, 100, _T("hash"));
+  EXPECT_SUCCEEDED(app_->put_displayName(CComBSTR(_T("Exe App"))));
+
+  SetArgumentsInManifest(arguments, _T("0.10.69.5"), app_);
+
+  EXPECT_SUCCEEDED(InstallApp(_T(""), app_, cmd_exe_dir_));
+
+  EXPECT_EQ(STATE_INSTALL_COMPLETE, app_->state());
+  EXPECT_EQ(PingEvent::EVENT_RESULT_SUCCESS, GetCompletionResult(app_));
+  EXPECT_EQ(S_OK, GetResultCode(app_));
+  EXPECT_STREQ(_T("Thanks for installing."), GetCompletionMessage(app_));
+  EXPECT_TRUE(GetPostInstallLaunchCommandLine(app_).IsEmpty());
+  EXPECT_TRUE(GetPostInstallUrl(app_).IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, GetPostInstallAction(app_));
+
+  RegKey state_key;
+  EXPECT_SUCCEEDED(state_key.Open(kFullAppClientStateKeyPath));
+  EXPECT_EQ(1 + kNumAutoPopulatedValues, state_key.GetValueCount());
+  EXPECT_STREQ(_T("0.10.69.5"), GetSzValue(kFullAppClientStateKeyPath,
+                                           kRegValueProductVersion));
+
+  EXPECT_TRUE(File::Exists(kPayloadFileName));
+  EXPECT_SUCCEEDED(File::Remove(kPayloadFileName));
+}
+
+TEST_F(InstallManagerInstallAppUserTest,
+       InstallApp_ExeInstallerReturnsNonZeroExitCode) {
+  const TCHAR kCommandToExecute[] = _T("exit 1");
+
+  CString arguments;
+  arguments.Format(kExecuteCommandAndTerminateSwitch, kCommandToExecute);
+
+  // Create the Clients key since this isn't an actual installer.
+  ASSERT_SUCCEEDED(RegKey::CreateKey(kFullAppClientsKeyPath));
+  ASSERT_TRUE(RegKey::HasKey(kFullAppClientsKeyPath));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kFullAppClientsKeyPath,
+                                    kRegValueProductVersion,
+                                    _T("0.10.69.5")));
+
+  app_->next_version()->AddPackage(kCmdExecutable, 100, _T("hash"));
+
+  SetArgumentsInManifest(arguments, _T("0.10.69.5"), app_);
+
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED,
+            InstallApp(_T(""), app_, cmd_exe_dir_));
+
+  EXPECT_EQ(STATE_ERROR, app_->state());
+  EXPECT_EQ(PingEvent::EVENT_RESULT_INSTALLER_ERROR_OTHER,
+            GetCompletionResult(app_));
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED, GetResultCode(app_));
+  EXPECT_STREQ(_T("The installer encountered error 1."),
+               GetCompletionMessage(app_));
+  EXPECT_TRUE(GetPostInstallLaunchCommandLine(app_).IsEmpty());
+  EXPECT_TRUE(GetPostInstallUrl(app_).IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, GetPostInstallAction(app_));
+}
+
+TEST_F(InstallManagerInstallAppMachineTest, InstallApp_MsiInstallerSucceeds) {
+  if (!vista_util::IsUserAdmin()) {
+    std::wcout << _T("\tTest did not run because the user is not an admin.")
+               << std::endl;
+    return;
+  }
+
+  const CString kIid = _T("{F7598FEE-4BA9-4755-A1FC-6EB0A6F3D126}");
+
+  // We can't fake the registry keys because we are interacting with a real
+  // installer.
+  RestoreRegistryHives();
+
+  AdjustMsiTries(installer_wrapper_.get());
+
+  CString installer_dir(app_util::GetCurrentModuleDirectory());
+
+  CString installer_full_path(
+      ConcatenatePath(installer_dir, kSetupFooV1RelativeLocation));
+  ASSERT_TRUE(File::Exists(installer_full_path));
+
+  CString installer_log_full_path;
+  installer_log_full_path.Format(kMsiLogFormat, installer_full_path);
+
+  ASSERT_SUCCEEDED(File::Remove(installer_log_full_path));
+  ASSERT_FALSE(File::Exists(installer_log_full_path));
+
+  RegKey::DeleteKey(kFullFooAppClientKeyPath);
+  ASSERT_FALSE(RegKey::HasKey(kFullFooAppClientKeyPath));
+  RegKey::DeleteKey(kFullFooAppClientStateKeyPath);
+  ASSERT_FALSE(RegKey::HasKey(kFullFooAppClientStateKeyPath));
+
+  // Accepting the EULA prevents "eulaaccepted" value from being written.
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE));
+
+  // TODO(omaha): This should be just a filename.
+  app_->next_version()->AddPackage(kSetupFooV1RelativeLocation,
+                                   100, _T("hash"));
+  app_->set_app_guid(StringToGuid(kFooId));
+  EXPECT_SUCCEEDED(app_->put_displayName(CComBSTR(_T("Foo"))));
+  EXPECT_SUCCEEDED(app_->put_iid(CComBSTR(kIid)));
+
+  // TODO(omaha): We should be able to eliminate this.
+  SetArgumentsInManifest(CString(), kFooVersion, app_);
+
+  EXPECT_SUCCEEDED(InstallApp(_T(""), app_, installer_dir));
+
+  EXPECT_EQ(STATE_INSTALL_COMPLETE, app_->state());
+  EXPECT_EQ(PingEvent::EVENT_RESULT_SUCCESS, GetCompletionResult(app_));
+  EXPECT_EQ(S_OK, GetResultCode(app_));
+  EXPECT_STREQ(_T("Thanks for installing."),
+               GetCompletionMessage(app_));
+  EXPECT_TRUE(GetPostInstallLaunchCommandLine(app_).IsEmpty());
+  EXPECT_TRUE(GetPostInstallUrl(app_).IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, GetPostInstallAction(app_));
+
+  EXPECT_TRUE(File::Exists(installer_log_full_path));
+
+  EXPECT_TRUE(RegKey::HasKey(kFullFooAppClientKeyPath));
+  EXPECT_TRUE(RegKey::HasKey(kFullFooAppClientStateKeyPath));
+  RegKey state_key;
+  EXPECT_SUCCEEDED(state_key.Open(kFullFooAppClientStateKeyPath));
+  EXPECT_EQ(2 + kNumAutoPopulatedValues, state_key.GetValueCount());
+  EXPECT_STREQ(kFooVersion, GetSzValue(kFullFooAppClientStateKeyPath,
+                                       kRegValueProductVersion));
+  EXPECT_FALSE(RegKey::HasValue(kFullFooAppClientStateKeyPath,
+                                kRegValueLanguage));
+  EXPECT_STREQ(kIid, GetSzValue(kFullFooAppClientStateKeyPath,
+                                kRegValueInstallationId));
+
+  // Verify the installer did not write a value that is to be written only in
+  // the presence of an MSI property that was not specified.
+  EXPECT_FALSE(RegKey::HasValue(kFullFooAppClientKeyPath,
+                                kFooInstallerBarValueName));
+
+  UninstallTestMsi(installer_full_path);
+
+  EXPECT_FALSE(RegKey::HasKey(kFullFooAppClientKeyPath));
+  EXPECT_SUCCEEDED(RegKey::DeleteKey(kFullFooAppClientKeyPath));
+}
+
+TEST_F(InstallManagerInstallAppMachineTest,
+       InstallApp_MsiInstallerWithArgumentSucceeds) {
+  if (!vista_util::IsUserAdmin()) {
+    std::wcout << _T("\tTest did not run because the user is not an admin.")
+               << std::endl;
+    return;
+  }
+
+  // We can't fake the registry keys because we are interacting with a real
+  // installer.
+  RestoreRegistryHives();
+
+  AdjustMsiTries(installer_wrapper_.get());
+
+  CString installer_dir(app_util::GetCurrentModuleDirectory());
+
+  CString installer_full_path(
+      ConcatenatePath(installer_dir, kSetupFooV1RelativeLocation));
+  ASSERT_TRUE(File::Exists(installer_full_path));
+
+  CString installer_log_full_path;
+  installer_log_full_path.Format(kMsiLogFormat, installer_full_path);
+
+  ASSERT_SUCCEEDED(File::Remove(installer_log_full_path));
+  ASSERT_FALSE(File::Exists(installer_log_full_path));
+
+  RegKey::DeleteKey(kFullFooAppClientKeyPath);
+  ASSERT_FALSE(RegKey::HasKey(kFullFooAppClientKeyPath));
+  RegKey::DeleteKey(kFullFooAppClientStateKeyPath);
+  ASSERT_FALSE(RegKey::HasKey(kFullFooAppClientStateKeyPath));
+
+  // Write an iid to verify it gets deleted below.
+  EXPECT_SUCCEEDED(
+      RegKey::SetValue(kFullFooAppClientStateKeyPath,
+                       kRegValueInstallationId,
+                       _T("{A30B6C0A-B491-473e-9D24-E1AC1BC1D42F}")));
+
+  // Accepting the EULA prevents "eulaaccepted" value from being written.
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE));
+
+  // TODO(omaha): This should be just a filename.
+  app_->next_version()->AddPackage(kSetupFooV1RelativeLocation,
+                                   100, _T("hash"));
+  app_->set_app_guid(StringToGuid(kFooId));
+  EXPECT_SUCCEEDED(app_->put_displayName(CComBSTR(_T("Foo"))));
+
+  SetArgumentsInManifest(kFooInstallerBarPropertyArg, kFooVersion, app_);
+
+  EXPECT_SUCCEEDED(InstallApp(_T(""), app_, installer_dir));
+
+  EXPECT_EQ(STATE_INSTALL_COMPLETE, app_->state());
+  EXPECT_EQ(PingEvent::EVENT_RESULT_SUCCESS, GetCompletionResult(app_));
+  EXPECT_EQ(S_OK, GetResultCode(app_));
+  EXPECT_STREQ(_T("Thanks for installing."),
+               GetCompletionMessage(app_));
+  EXPECT_TRUE(GetPostInstallLaunchCommandLine(app_).IsEmpty());
+  EXPECT_TRUE(GetPostInstallUrl(app_).IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, GetPostInstallAction(app_));
+
+  EXPECT_TRUE(File::Exists(installer_log_full_path));
+
+  EXPECT_TRUE(RegKey::HasKey(kFullFooAppClientKeyPath));
+  EXPECT_TRUE(RegKey::HasKey(kFullFooAppClientStateKeyPath));
+  RegKey state_key;
+  EXPECT_SUCCEEDED(state_key.Open(kFullFooAppClientStateKeyPath));
+  EXPECT_EQ(1 + kNumAutoPopulatedValues, state_key.GetValueCount());
+  EXPECT_STREQ(kFooVersion, GetSzValue(kFullFooAppClientStateKeyPath,
+                                       kRegValueProductVersion));
+  EXPECT_FALSE(RegKey::HasValue(kFullFooAppClientStateKeyPath,
+                                kRegValueLanguage));
+  EXPECT_FALSE(RegKey::HasValue(kFullFooAppClientStateKeyPath,
+                                kRegValueInstallationId));
+
+  EXPECT_TRUE(RegKey::HasValue(kFullFooAppClientKeyPath,
+                               kFooInstallerBarValueName));
+  DWORD barprop_value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kFullFooAppClientKeyPath,
+                                    kFooInstallerBarValueName,
+                                    &barprop_value));
+  EXPECT_EQ(7, barprop_value);
+
+  UninstallTestMsi(installer_full_path);
+
+  EXPECT_FALSE(RegKey::HasKey(kFullFooAppClientKeyPath));
+  EXPECT_SUCCEEDED(RegKey::DeleteKey(kFullFooAppClientKeyPath));
+}
+
+// The use of kGoogleUpdateAppId is the key to this test.
+// Note that the version is not changed - this is the normal self-update case.
+// Among other things, this test verifies that CheckApplicationRegistration() is
+// not called for self-updates.
+TEST_F(InstallManagerInstallAppUserTest, InstallApp_UpdateOmahaSucceeds) {
+  CString arguments;
+  arguments.Format(kExecuteCommandAndTerminateSwitch, _T(""));
+
+  const CString kExistingVersion(_T("0.9.69.5"));
+
+  app_->next_version()->AddPackage(kCmdExecutable, 100, _T("hash"));
+  app_->set_app_guid(StringToGuid(kGoogleUpdateAppId));
+
+  // TODO(omaha3): This isn't supported yet.
+#if 0
+  isupdate = true
+#endif
+
+  SetArgumentsInManifest(arguments, _T("1.2.9.8"), app_);
+
+  // Because we don't actually run the Omaha installer, we need to make sure
+  // its Clients key and pv value exist to avoid an error.
+  ASSERT_SUCCEEDED(RegKey::CreateKey(USER_REG_CLIENTS_GOOPDATE));
+  ASSERT_TRUE(RegKey::HasKey(USER_REG_CLIENTS_GOOPDATE));
+  ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENTS_GOOPDATE,
+                                    kRegValueProductVersion,
+                                    kExistingVersion));
+
+  EXPECT_SUCCEEDED(InstallApp(kExistingVersion, app_, cmd_exe_dir_));
+
+  EXPECT_EQ(STATE_INSTALL_COMPLETE, app_->state());
+  EXPECT_EQ(PingEvent::EVENT_RESULT_SUCCESS, GetCompletionResult(app_));
+  EXPECT_EQ(S_OK, GetResultCode(app_));
+  EXPECT_STREQ(_T("Thanks for installing."), GetCompletionMessage(app_));
+  EXPECT_TRUE(GetPostInstallLaunchCommandLine(app_).IsEmpty());
+  EXPECT_TRUE(GetPostInstallUrl(app_).IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, GetPostInstallAction(app_));
+
+  EXPECT_TRUE(RegKey::HasKey(USER_REG_CLIENTS_GOOPDATE));
+  CString version;
+  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_CLIENTS_GOOPDATE,
+                                    kRegValueProductVersion,
+                                    &version));
+  EXPECT_STREQ(kExistingVersion, version);
+}
+
+// The main purpose of this test is to ensure that self-updates don't fail if
+// Omaha's Clients key doesn't exist for some reason.
+// In other words, it tests that CheckApplicationRegistration() is not called.
+TEST_F(InstallManagerInstallAppUserTest,
+       InstallApp_UpdateOmahaSucceedsWhenClientsKeyAbsent) {
+  CString arguments;
+  arguments.Format(kExecuteCommandAndTerminateSwitch, _T(""));
+
+  const CString kExistingVersion(_T("0.9.69.5"));
+
+  app_->next_version()->AddPackage(kCmdExecutable, 100, _T("hash"));
+  app_->set_app_guid(StringToGuid(kGoogleUpdateAppId));
+
+  // TODO(omaha3): This isn't supported yet.
+#if 0
+  isupdate = true
+#endif
+
+  SetArgumentsInManifest(arguments, _T("1.2.9.8"), app_);
+
+  EXPECT_SUCCEEDED(InstallApp(kExistingVersion, app_, cmd_exe_dir_));
+
+  EXPECT_EQ(STATE_INSTALL_COMPLETE, app_->state());
+  EXPECT_EQ(PingEvent::EVENT_RESULT_SUCCESS, GetCompletionResult(app_));
+  EXPECT_EQ(S_OK, GetResultCode(app_));
+  EXPECT_STREQ(_T("Thanks for installing."), GetCompletionMessage(app_));
+  EXPECT_TRUE(GetPostInstallLaunchCommandLine(app_).IsEmpty());
+  EXPECT_TRUE(GetPostInstallUrl(app_).IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, GetPostInstallAction(app_));
+
+  EXPECT_FALSE(RegKey::HasKey(USER_REG_CLIENTS_GOOPDATE));
+}
+
+TEST_F(InstallManagerInstallAppUserTest,
+       InstallApp_InstallerDoesNotWriteClientsKey) {
+  CString arguments;
+  arguments.Format(kExecuteCommandAndTerminateSwitch, _T(""));
+
+  app_->next_version()->AddPackage(kCmdExecutable, 100, _T("hash"));
+  EXPECT_SUCCEEDED(app_->put_displayName(CComBSTR(_T("Some App"))));
+
+  SetArgumentsInManifest(arguments, _T("5.6.7.8"), app_);
+
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY,
+            InstallApp(_T(""), app_, cmd_exe_dir_));
+
+  EXPECT_FALSE(RegKey::HasKey(kFullAppClientsKeyPath));
+  EXPECT_FALSE(RegKey::HasKey(kFullAppClientStateKeyPath));
+
+  EXPECT_EQ(STATE_ERROR, app_->state());
+  EXPECT_EQ(PingEvent::EVENT_RESULT_ERROR, GetCompletionResult(app_));
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY,
+            GetResultCode(app_));
+  EXPECT_STREQ(
+      _T("Installation failed. Please try again."),
+      GetCompletionMessage(app_));
+  EXPECT_TRUE(GetPostInstallLaunchCommandLine(app_).IsEmpty());
+  EXPECT_TRUE(GetPostInstallUrl(app_).IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, GetPostInstallAction(app_));
+}
+
+// TODO(omaha3): Test for GOOPDATEINSTALL_E_INSTALLER_DID_NOT_CHANGE_VERSION
+// once is_update is supported.
+
+TEST_F(InstallManagerInstallAppUserTest,
+       InstallApp_InstallerFailureMsiFileDoesNotExist) {
+  CPath msi_dir(app_util::GetTempDir());
+  CPath msi_path(msi_dir);
+  msi_path.Append(_T("foo.msi"));
+  const CString log_path = msi_path + _T(".log");
+
+  AdjustMsiTries(installer_wrapper_.get());
+
+  ASSERT_SUCCEEDED(File::Remove(log_path));
+  ASSERT_FALSE(File::Exists(log_path));
+
+  app_->next_version()->AddPackage(_T("foo.msi"), 100, _T("hash"));
+
+  // TODO(omaha): We should be able to eliminate this.
+  SetArgumentsInManifest(CString(), _T("1.2.3.4"), app_);
+
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED,
+            InstallApp(_T(""), app_, msi_dir));
+
+  EXPECT_EQ(STATE_ERROR, app_->state());
+  EXPECT_EQ(PingEvent::EVENT_RESULT_INSTALLER_ERROR_MSI,
+            GetCompletionResult(app_));
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED, GetResultCode(app_));
+  const CString message = GetCompletionMessage(app_);
+  EXPECT_STREQ(kError1619MessagePrefix,
+               message.Left(kError1619MessagePrefixLength));
+  VerifyStringIsMsiPackageOpenFailedString(
+      message.Mid(kError1619MessagePrefixLength));
+  EXPECT_TRUE(GetPostInstallLaunchCommandLine(app_).IsEmpty());
+  EXPECT_TRUE(GetPostInstallUrl(app_).IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, GetPostInstallAction(app_));
+
+  // msiexec creates an empty log file.
+  EXPECT_TRUE(File::Exists(log_path));
+  EXPECT_SUCCEEDED(File::Remove(log_path));
+
+  EXPECT_FALSE(RegKey::HasKey(kFullAppClientsKeyPath));
+  EXPECT_FALSE(RegKey::HasKey(kFullAppClientStateKeyPath));
+}
+
+// Simulates the MSI busy error by having an exe installer return the error
+// as its exit code and specifying MSI error using Installer Result API.
+// Assumes reg.exe is in the path.
+// This works because the number of retries is set before InstallApp() and thus
+// defaults to 1 in the InstallerWrapper.
+TEST_F(InstallManagerInstallAppUserTest, InstallApp_MsiIsBusy_NoRetries) {
+  CString commands;
+  commands.Format(kExecuteTwoCommandsFormat,
+                  set_installer_result_type_msi_error_cmd_,
+                  kMsiInstallerBusyExitCodeCmd);
+
+  CString arguments;
+  arguments.Format(kExecuteCommandAndTerminateSwitch, commands);
+
+  app_->next_version()->AddPackage(kCmdExecutable, 100, _T("hash"));
+  EXPECT_SUCCEEDED(app_->put_displayName(CComBSTR(_T("Some App"))));
+
+  SetArgumentsInManifest(arguments, _T("1.2.3.4"), app_);
+
+  LowResTimer install_timer(true);
+
+  EXPECT_EQ(GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING,
+            InstallApp(_T(""), app_, cmd_exe_dir_));
+
+  EXPECT_GT(2, install_timer.GetSeconds());  // Check Omaha did not retry.
+
+  EXPECT_EQ(STATE_ERROR, app_->state());
+  // Even though the error came from the installer, the error type is not
+  // set because we have a custom error for this case.
+  EXPECT_EQ(PingEvent::EVENT_RESULT_ERROR, GetCompletionResult(app_));
+  EXPECT_EQ(GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING,
+            GetResultCode(app_));
+  EXPECT_STREQ(kMsiInstallerBusyErrorMessage, GetCompletionMessage(app_));
+  EXPECT_TRUE(GetPostInstallLaunchCommandLine(app_).IsEmpty());
+  EXPECT_TRUE(GetPostInstallUrl(app_).IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, GetPostInstallAction(app_));
+}
+
+// This test uses cmd.exe as an installer that leaves the payload files.
+TEST_F(InstallManagerInstallAppUserTest, InstallApp_InstallMultipleApps) {
+  const TCHAR kPayloadFileName1[] = _T("exe_payload1.txt");
+  const TCHAR kPayloadFileName2[] = _T("exe_payload2.txt");
+  const TCHAR kCommandToExecute[] = _T("echo \"hi\" > %s");
+
+  CString full_command_to_execute;
+  full_command_to_execute.Format(kCommandToExecute, kPayloadFileName1);
+  CString arguments1;
+  arguments1.Format(kExecuteCommandAndTerminateSwitch, full_command_to_execute);
+
+  full_command_to_execute.Format(kCommandToExecute, kPayloadFileName2);
+  CString arguments2;
+  arguments2.Format(kExecuteCommandAndTerminateSwitch, full_command_to_execute);
+
+  EXPECT_SUCCEEDED(File::Remove(kPayloadFileName1));
+  EXPECT_FALSE(File::Exists(kPayloadFileName1));
+  EXPECT_SUCCEEDED(File::Remove(kPayloadFileName2));
+  EXPECT_FALSE(File::Exists(kPayloadFileName2));
+
+  // Create the Clients key since this isn't an actual installer.
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kFullAppClientsKeyPath));
+  EXPECT_TRUE(RegKey::HasKey(kFullAppClientsKeyPath));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kFullAppClientsKeyPath,
+                                    kRegValueProductVersion,
+                                    _T("0.10.69.5")));
+
+  app_->next_version()->AddPackage(kCmdExecutable, 100, _T("hash"));
+  EXPECT_SUCCEEDED(app_->put_displayName(CComBSTR(_T("Exe App"))));
+
+  SetArgumentsInManifest(arguments1, _T("0.10.69.5"), app_);
+
+  EXPECT_SUCCEEDED(InstallApp(_T(""), app_, cmd_exe_dir_));
+
+  EXPECT_EQ(STATE_INSTALL_COMPLETE, app_->state());
+  EXPECT_EQ(PingEvent::EVENT_RESULT_SUCCESS, GetCompletionResult(app_));
+  EXPECT_EQ(S_OK, GetResultCode(app_));
+  EXPECT_STREQ(_T("Thanks for installing."), GetCompletionMessage(app_));
+  EXPECT_TRUE(GetPostInstallLaunchCommandLine(app_).IsEmpty());
+  EXPECT_TRUE(GetPostInstallUrl(app_).IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, GetPostInstallAction(app_));
+
+  EXPECT_TRUE(File::Exists(kPayloadFileName1));
+  EXPECT_SUCCEEDED(File::Remove(kPayloadFileName1));
+
+  // Run the second installer.
+
+  App* app2 = NULL;
+  ASSERT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kApp2Id), &app2));
+  SetAppStateWaitingToInstall(app2);
+
+  // Create the Clients key since this isn't an actual installer.
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kFullAppClientsKeyPath));
+  EXPECT_TRUE(RegKey::HasKey(kFullAppClientsKeyPath));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kFullApp2ClientsKeyPath,
+                                    kRegValueProductVersion,
+                                    _T("0.10.69.5")));
+
+  app2->next_version()->AddPackage(kCmdExecutable, 100, _T("hash"));
+  EXPECT_SUCCEEDED(app2->put_displayName(CComBSTR(_T("Exe App"))));
+
+  SetArgumentsInManifest(arguments2, _T("0.10.69.5"), app2);
+
+  EXPECT_SUCCEEDED(InstallApp(_T(""), app2, cmd_exe_dir_));
+
+  EXPECT_EQ(STATE_INSTALL_COMPLETE, app2->state());
+  EXPECT_EQ(PingEvent::EVENT_RESULT_SUCCESS, GetCompletionResult(app2));
+  EXPECT_EQ(S_OK, GetResultCode(app2));
+  EXPECT_STREQ(_T("Thanks for installing."), GetCompletionMessage(app2));
+  EXPECT_TRUE(GetPostInstallLaunchCommandLine(app_).IsEmpty());
+  EXPECT_TRUE(GetPostInstallUrl(app_).IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, GetPostInstallAction(app_));
+
+  EXPECT_TRUE(File::Exists(kPayloadFileName2));
+  EXPECT_SUCCEEDED(File::Remove(kPayloadFileName2));
+}
+
+TEST_F(InstallManagerInstallAppUserTest,
+       PopulateSuccessfulInstallResultInfo_ExitSilentlyOnLaunchCommandWithNoCommand) {  // NOLINT
+  SetPostInstallActionInManifest(SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD,
+                                 _T(""),
+                                 false,
+                                 app_);
+
+  InstallerResultInfo result_info;
+  result_info.type = INSTALLER_RESULT_SUCCESS;
+  PopulateSuccessfulInstallResultInfo(app_, &result_info);
+  EXPECT_TRUE(GetPostInstallLaunchCommandLine(app_).IsEmpty());
+  EXPECT_TRUE(GetPostInstallUrl(app_).IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info.post_install_action);
+}
+
+// Verify that launch command is converted to silently launch command
+// if success action is silently launch command. Also tests that launch cmd
+// takes precedence over URL.
+TEST_F(InstallManagerInstallAppUserTest,
+       PopulateSuccessfulInstallResultInfo_ExitSilentlyOnLaunchCommand) {
+  SetPostInstallActionInManifest(SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD,
+                                 _T("http://post_install_succeeded"),
+                                 true,
+                                 app_);
+
+  InstallerResultInfo result_info;
+  result_info.type = INSTALLER_RESULT_SUCCESS;
+  result_info.post_install_action = POST_INSTALL_ACTION_LAUNCH_COMMAND;
+  result_info.post_install_launch_command_line = _T("notepad.exe");
+  PopulateSuccessfulInstallResultInfo(app_, &result_info);
+
+  EXPECT_EQ(POST_INSTALL_ACTION_EXIT_SILENTLY_ON_LAUNCH_COMMAND,
+            result_info.post_install_action);
+}
+
+TEST_F(InstallManagerInstallAppUserTest,
+    PopulateSuccessfulInstallResultInfo_LaunchCommandWithDefaultSuccessAction) {
+  SetPostInstallActionInManifest(SUCCESS_ACTION_DEFAULT,
+                                 _T(""),
+                                 true,
+                                 app_);
+
+  InstallerResultInfo result_info;
+  result_info.type = INSTALLER_RESULT_SUCCESS;
+  result_info.post_install_action = POST_INSTALL_ACTION_LAUNCH_COMMAND;
+  result_info.post_install_launch_command_line = _T("notepad.exe");
+  PopulateSuccessfulInstallResultInfo(app_, &result_info);
+
+  EXPECT_EQ(POST_INSTALL_ACTION_LAUNCH_COMMAND,
+            result_info.post_install_action);
+}
+
+// Verify that default install action is converted to exit silently if
+// if success action is exit silently.
+TEST_F(InstallManagerInstallAppUserTest,
+       PopulateSuccessfulInstallResultInfo_ExitSilently) {
+  SetPostInstallActionInManifest(SUCCESS_ACTION_EXIT_SILENTLY,
+                                 _T(""),
+                                 false,
+                                 app_);
+
+  InstallerResultInfo result_info;
+  result_info.type = INSTALLER_RESULT_SUCCESS;
+  result_info.post_install_action = POST_INSTALL_ACTION_DEFAULT;
+  PopulateSuccessfulInstallResultInfo(app_, &result_info);
+
+  EXPECT_EQ(POST_INSTALL_ACTION_EXIT_SILENTLY, result_info.post_install_action);
+}
+
+TEST_F(InstallManagerInstallAppUserTest,
+       PopulateSuccessfulInstallResultInfo_RestartBrowser) {
+  SetPostInstallActionInManifest(SUCCESS_ACTION_DEFAULT,
+                                 _T("http://www.google.com/foo/installed_ok"),
+                                 false,
+                                 app_);
+
+  InstallerResultInfo result_info;
+  result_info.type = INSTALLER_RESULT_SUCCESS;
+  result_info.post_install_action = POST_INSTALL_ACTION_DEFAULT;
+  PopulateSuccessfulInstallResultInfo(app_, &result_info);
+
+  EXPECT_EQ(POST_INSTALL_ACTION_RESTART_BROWSER,
+            result_info.post_install_action);
+}
+
+TEST_F(InstallManagerInstallAppUserTest,
+       PopulateSuccessfulInstallResultInfo_RestartAllBrowsers) {
+  SetPostInstallActionInManifest(SUCCESS_ACTION_DEFAULT,
+                                 _T("http://www.google.com/gears/installed_ok"),
+                                 true,
+                                 app_);
+
+  InstallerResultInfo result_info;
+  result_info.type = INSTALLER_RESULT_SUCCESS;
+  result_info.post_install_action = POST_INSTALL_ACTION_DEFAULT;
+  PopulateSuccessfulInstallResultInfo(app_, &result_info);
+
+  EXPECT_EQ(POST_INSTALL_ACTION_RESTART_ALL_BROWSERS,
+            result_info.post_install_action);
+}
+
+TEST_F(InstallManagerInstallAppUserTest,
+       PopulateSuccessfulInstallResultInfo_NoPostInstallUrl) {
+  SetPostInstallActionInManifest(SUCCESS_ACTION_DEFAULT,
+                                 _T(""),
+                                 true,
+                                 app_);
+
+  InstallerResultInfo result_info;
+  result_info.type = INSTALLER_RESULT_SUCCESS;
+  result_info.post_install_action = POST_INSTALL_ACTION_DEFAULT;
+  PopulateSuccessfulInstallResultInfo(app_, &result_info);
+
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info.post_install_action);
+}
+
+
+TEST_F(InstallManagerInstallAppUserTest,
+       PopulateSuccessfulInstallResultInfo_RebootShouldNotBeOverridden) {
+  const SuccessfulInstallAction kSuccessActions[] = {
+    SUCCESS_ACTION_DEFAULT,
+    SUCCESS_ACTION_EXIT_SILENTLY,
+    SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD,
+  };
+
+  for (int i = 0; i < arraysize(kSuccessActions); ++i) {
+    SetPostInstallActionInManifest(kSuccessActions[i],
+                                   _T("http://foo/bar"),
+                                   true,
+                                   app_);
+
+    InstallerResultInfo result_info;
+    result_info.type = INSTALLER_RESULT_SUCCESS;
+    result_info.post_install_action = POST_INSTALL_ACTION_REBOOT;
+    result_info.post_install_launch_command_line = _T("foo.exe");
+    PopulateSuccessfulInstallResultInfo(app_, &result_info);
+
+    EXPECT_EQ(POST_INSTALL_ACTION_REBOOT, result_info.post_install_action);
+  }
+}
+
+}  // namespace omaha
diff --git a/goopdate/installer_result_info.h b/goopdate/installer_result_info.h
new file mode 100644
index 0000000..d90de14
--- /dev/null
+++ b/goopdate/installer_result_info.h
@@ -0,0 +1,44 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_INSTALLER_RESULT_INFO_H_
+#define OMAHA_GOOPDATE_INSTALLER_RESULT_INFO_H_
+
+#include <atlstr.h>
+#include "goopdate/omaha3_idl.h"
+#include "omaha/common/const_goopdate.h"
+
+namespace omaha {
+
+// TODO(omaha3): perhaps rename "text" to "message".
+struct InstallerResultInfo {
+  InstallerResultInfo()
+      : type(INSTALLER_RESULT_UNKNOWN),
+        code(0),
+        extra_code1(0),
+        post_install_action(POST_INSTALL_ACTION_DEFAULT) {}
+
+  InstallerResultType type;
+  DWORD code;
+  DWORD extra_code1;
+  CString text;
+  CString post_install_launch_command_line;
+  CString post_install_url;
+  PostInstallAction post_install_action;
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_INSTALLER_RESULT_INFO_H_
diff --git a/goopdate/installer_wrapper.cc b/goopdate/installer_wrapper.cc
new file mode 100644
index 0000000..ce9fea0
--- /dev/null
+++ b/goopdate/installer_wrapper.cc
@@ -0,0 +1,723 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/installer_wrapper.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/const_utils.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/process.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/string.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/goopdate/app_manager.h"
+#include "omaha/goopdate/server_resource.h"
+#include "omaha/goopdate/string_formatter.h"
+#include "omaha/goopdate/worker_metrics.h"
+
+namespace omaha {
+
+namespace {
+
+CString BuildMsiCommandLine(const CString& arguments,
+                            const CString& msi_file_path,
+                            const CString& enclosed_installer_data_file_path) {
+  CORE_LOG(L3, (_T("[CreateMsiCommandLine]")));
+
+  CString command_line;
+  // Suppressing reboots can lead to an inconsistent state until the user
+  // reboots, but automatically rebooting is unacceptable. The user will be
+  // informed by the string for ERROR_SUCCESS_REBOOT_REQUIRED that a reboot is
+  // necessary. See http://b/1184091 for details.
+
+  if (!enclosed_installer_data_file_path.IsEmpty()) {
+    SafeCStringFormat(&command_line, _T("INSTALLERDATA=%s "),
+                      enclosed_installer_data_file_path);
+  }
+
+  SafeCStringAppendFormat(&command_line, _T("%s %s /qn /i \"%s\""),
+                          arguments,
+                          kMsiSuppressAllRebootsCmdLine,
+                          msi_file_path);
+
+  // The msiexec version in XP SP2 (V 3.01) and higher supports the /log switch.
+  if (SystemInfo::OSWinXPSP2OrLater()) {
+    CString logfile(msi_file_path);
+    logfile.Append(_T(".log"));
+
+    SafeCStringAppendFormat(&command_line, _T(" /log \"%s\""), logfile);
+  }
+
+  CORE_LOG(L2, (_T("[msiexec command line][%s]"), command_line));
+  return command_line;
+}
+
+// Gets the installer exit code.
+HRESULT GetInstallerExitCode(const Process& p, uint32* exit_code) {
+  ASSERT1(exit_code);
+
+  if (p.Running()) {
+    ASSERT(false,
+           (_T("GetInstallerExitCode called while the process is running.")));
+    return GOOPDATEINSTALL_E_INSTALLER_INTERNAL_ERROR;
+  }
+
+  if (!p.GetExitCode(exit_code)) {
+    ASSERT(false,
+           (_T("[Failed to get the installer exit code for some reason.]")));
+    return GOOPDATEINSTALL_E_INSTALLER_INTERNAL_ERROR;
+  }
+
+  CORE_LOG(L2, (_T("[Installer exit code][%u]"), *exit_code));
+
+  return S_OK;
+}
+
+// Gets the errors string for the specified system error.
+// Assumes error_code represents a system error.
+void GetSystemErrorString(uint32 error_code,
+                          const CString& language,
+                          CString* error_string) {
+  ASSERT1(error_string);
+  ASSERT1(ERROR_SUCCESS != error_code);
+
+  const CString error_code_string = FormatErrorCode(error_code);
+
+  StringFormatter formatter(language);
+
+  const CString error_message(GetMessageForSystemErrorCode(error_code));
+  if (!error_message.IsEmpty()) {
+    VERIFY1(SUCCEEDED(formatter.FormatMessage(error_string,
+                                              IDS_INSTALLER_FAILED_WITH_MESSAGE,
+                                              error_code_string,
+                                              error_message)));
+  } else {
+    VERIFY1(SUCCEEDED(formatter.FormatMessage(error_string,
+                                              IDS_INSTALLER_FAILED_NO_MESSAGE,
+                                              error_code_string)));
+  }
+
+  OPT_LOG(LEVEL_ERROR, (_T("[installer system error][%u][%s]"),
+                        error_code, *error_string));
+  ASSERT1(!error_string->IsEmpty());
+}
+
+}  // namespace
+
+InstallerWrapper::InstallerWrapper(bool is_machine)
+    : is_machine_(is_machine),
+      num_tries_when_msi_busy_(1) {
+  CORE_LOG(L3, (_T("[InstallerWrapper::InstallerWrapper]")));
+}
+
+InstallerWrapper::~InstallerWrapper() {
+  CORE_LOG(L3, (_T("[InstallerWrapper::~InstallerWrapper]")));
+}
+
+HRESULT InstallerWrapper::Initialize() {
+  NamedObjectAttributes lock_attr;
+  // TODO(omaha3): We might want to move this lock to the InstallManager.
+  GetNamedObjectAttributes(kInstallManagerSerializer, is_machine_, &lock_attr);
+  if (!installer_lock_.InitializeWithSecAttr(lock_attr.name, &lock_attr.sa)) {
+    OPT_LOG(LEVEL_ERROR, (_T("[Could not init Install Manager lock]")));
+    return GOOPDATEINSTALL_E_FAILED_INIT_INSTALLER_LOCK;
+  }
+
+  return S_OK;
+}
+
+// result_* will be populated if the installer ran and exited, regardless of the
+// return value.
+// Assumes the call is protected by some mechanism providing exclusive access
+// to the app's registry keys in Clients and ClientState.
+HRESULT InstallerWrapper::InstallApp(HANDLE user_token,
+                                   const GUID& app_guid,
+                                   const CString& installer_path,
+                                   const CString& arguments,
+                                   const CString& installer_data,
+                                   const CString& language,
+                                   InstallerResultInfo* result_info) {
+  ASSERT1(result_info);
+
+  HRESULT hr = DoInstallApp(user_token,
+                            app_guid,
+                            installer_path,
+                            arguments,
+                            installer_data,
+                            language,
+                            result_info);
+
+  ASSERT1((SUCCEEDED(hr) && result_info->type == INSTALLER_RESULT_SUCCESS) ||
+          (GOOPDATEINSTALL_E_INSTALLER_FAILED == hr &&
+           (result_info->type == INSTALLER_RESULT_ERROR_MSI ||
+            result_info->type == INSTALLER_RESULT_ERROR_SYSTEM ||
+            result_info->type == INSTALLER_RESULT_ERROR_OTHER)) ||
+          (GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING == hr &&
+           (result_info->type == INSTALLER_RESULT_ERROR_MSI ||
+            result_info->type == INSTALLER_RESULT_ERROR_SYSTEM)) ||
+          (FAILED(hr) && result_info->type == INSTALLER_RESULT_UNKNOWN));
+  ASSERT1(!result_info->text.IsEmpty() ==
+              (GOOPDATEINSTALL_E_INSTALLER_FAILED == hr ||
+               GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING == hr) ||
+          SUCCEEDED(hr));  // Successes may or may not have messages.
+
+  CORE_LOG(L3, (_T("[InstallApp result][0x%x][%s][type: %d][code: %d][%s][%s]"),
+                hr, GuidToString(app_guid), result_info->type,
+                result_info->code, result_info->text,
+                result_info->post_install_launch_command_line));
+  return hr;
+}
+
+CString InstallerWrapper::GetMessageForError(HRESULT error_code,
+                                             const CString& installer_filename,
+                                             const CString& language) {
+  CString message;
+  StringFormatter formatter(language);
+
+  switch (error_code) {
+    case GOOPDATEINSTALL_E_FILENAME_INVALID:
+      VERIFY1(SUCCEEDED(formatter.FormatMessage(&message,
+                                                IDS_INVALID_INSTALLER_FILENAME,
+                                                installer_filename)));
+      break;
+    case GOOPDATEINSTALL_E_INSTALLER_FAILED_START:
+      VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALLER_FAILED_TO_START,
+                                             &message)));
+      break;
+    case GOOPDATEINSTALL_E_INSTALLER_TIMED_OUT:
+      VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALLER_TIMED_OUT,
+                                             &message)));
+      break;
+    case GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY:
+    case GOOPDATEINSTALL_E_INSTALLER_DID_NOT_CHANGE_VERSION:
+    case GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH:
+      VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)));
+      break;
+    case GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING:
+      VERIFY1(SUCCEEDED(formatter.LoadString(IDS_MSI_INSTALL_ALREADY_RUNNING,
+                                             &message)));
+      break;
+    case GOOPDATEINSTALL_E_INSTALLER_FAILED:
+      ASSERT(false,
+             (_T("[GetOmahaErrorTextToReport]")
+              _T("GOOPDATEINSTALL_E_INSTALLER_FAILED should never be reported ")
+              _T("directly. The installer error string should be reported.")));
+      VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)));
+      break;
+    case GOOPDATEINSTALL_E_INSTALLER_INTERNAL_ERROR:
+    default:
+      ASSERT(false, (_T("[GetOmahaErrorTextToReport]")
+                     _T("[An Omaha error occurred that this method does not ")
+                     _T("know how to report.][0x%08x]"), error_code));
+      VERIFY1(SUCCEEDED(formatter.LoadString(IDS_INSTALL_FAILED, &message)));
+      break;
+  }
+
+  ASSERT1(!message.IsEmpty());
+  return message;
+}
+
+void InstallerWrapper::set_num_tries_when_msi_busy(
+    int num_tries_when_msi_busy) {
+  ASSERT1(num_tries_when_msi_busy >= 1);
+  num_tries_when_msi_busy_ = num_tries_when_msi_busy;
+}
+
+HRESULT InstallerWrapper::BuildCommandLineFromFilename(
+    const CString& file_path,
+    const CString& arguments,
+    const CString& installer_data,
+    CString* executable_path,
+    CString* command_line,
+    InstallerType* installer_type) {
+  CORE_LOG(L3, (_T("[BuildCommandLineFromFilename]")));
+
+  ASSERT1(executable_path);
+  ASSERT1(command_line);
+  ASSERT1(installer_type);
+
+  *executable_path = _T("");
+  *command_line = _T("");
+  *installer_type = UNKNOWN_INSTALLER;
+
+  // The app's installer owns the lifetime of installer data file if it has been
+  // created, so Omaha does not delete it.
+  CString enclosed_installer_data_file_path;
+
+  VERIFY1(SUCCEEDED(goopdate_utils::WriteInstallerDataToTempFile(
+      installer_data,
+      &enclosed_installer_data_file_path)));
+  if (!enclosed_installer_data_file_path.IsEmpty()) {
+    EnclosePath(&enclosed_installer_data_file_path);
+  }
+
+  // PathFindExtension returns the address of the trailing NUL character if an
+  // extension is not found. It does not return NULL.
+  const TCHAR* ext = ::PathFindExtension(file_path);
+  ASSERT1(ext);
+  if (*ext != _T('\0')) {
+    ext++;  // Skip the period.
+    if (0 == lstrcmpi(ext, _T("exe"))) {
+      *executable_path = file_path;
+      if (enclosed_installer_data_file_path.IsEmpty()) {
+        *command_line = arguments;
+      } else {
+        SafeCStringFormat(command_line, _T("%s /installerdata=%s"),
+                          arguments,
+                          enclosed_installer_data_file_path);
+      }
+      *installer_type = CUSTOM_INSTALLER;
+
+      CORE_LOG(L2, (_T("[BuildCommandLineFromFilename][exe][%s][%s]"),
+                    *executable_path, *command_line));
+    } else if (0 == lstrcmpi(ext, _T("msi"))) {
+      *executable_path = _T("msiexec");
+      *command_line = BuildMsiCommandLine(arguments,
+                                          file_path,
+                                          enclosed_installer_data_file_path);
+      *installer_type = MSI_INSTALLER;
+
+      CORE_LOG(L2, (_T("[BuildCommandLineFromFilename][msi][%s]"),
+                    *command_line));
+    } else {
+      *executable_path = _T("");
+      *command_line = _T("");
+      *installer_type = UNKNOWN_INSTALLER;
+
+      OPT_LOG(LE, (_T("[Unsupported extension '%s' in %s]"), ext, file_path));
+      return GOOPDATEINSTALL_E_FILENAME_INVALID;
+    }
+  } else {
+    OPT_LOG(LE, (_T("[No extension found in %s]"), file_path));
+    return GOOPDATEINSTALL_E_FILENAME_INVALID;
+  }
+
+  return S_OK;
+}
+
+// Calls DoExecuteAndWaitForInstaller to do the work. If an MSI installer
+// returns, ERROR_INSTALL_ALREADY_RUNNING waits and retries several times or
+// until the installation succeeds.
+HRESULT InstallerWrapper::ExecuteAndWaitForInstaller(
+    HANDLE user_token,
+    const GUID& app_guid,
+    const CString& executable_path,
+    const CString& command_line,
+    InstallerType installer_type,
+    const CString& language,
+    InstallerResultInfo* result_info) {
+  CORE_LOG(L3, (_T("[InstallerWrapper::ExecuteAndWaitForInstaller]")));
+  ASSERT1(result_info);
+  ASSERT1(num_tries_when_msi_busy_ >= 1);
+
+  ++metric_worker_install_execute_total;
+  if (MSI_INSTALLER == installer_type) {
+    ++metric_worker_install_execute_msi_total;
+  }
+
+  // Run the installer, retrying if necessary.
+  int retry_delay = kMsiAlreadyRunningRetryDelayBaseMs;
+  int num_tries(0);
+  HRESULT hr = GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING;
+  for (num_tries = 0;
+       hr == GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING &&
+       num_tries < num_tries_when_msi_busy_;
+       ++num_tries) {
+    // Reset the result info - it contains the previous error when retrying.
+    *result_info = InstallerResultInfo();
+
+    if (0 < num_tries) {
+      // Retrying - wait between attempts.
+      CORE_LOG(L1, (_T("[Retrying][%d]"), num_tries));
+      ::Sleep(retry_delay);
+      retry_delay *= 2;  // Double the retry delay next time.
+    }
+
+    hr = DoExecuteAndWaitForInstaller(user_token,
+                                      app_guid,
+                                      executable_path,
+                                      command_line,
+                                      installer_type,
+                                      language,
+                                      result_info);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[DoExecuteAndWaitForInstaller failed][0x%08x]"), hr));
+      return hr;
+    }
+    CORE_LOG(L1, (_T("[Installer result][%d][%d][%s]"),
+                  result_info->type, result_info->code, result_info->text));
+    ASSERT1(result_info->type != INSTALLER_RESULT_UNKNOWN);
+
+    if ((INSTALLER_RESULT_ERROR_MSI == result_info->type ||
+         INSTALLER_RESULT_ERROR_SYSTEM == result_info->type) &&
+        ERROR_INSTALL_ALREADY_RUNNING == result_info->code) {
+      hr = GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING;
+    }
+  }
+
+  if (1 < num_tries) {
+    // Record metrics about the ERROR_INSTALL_ALREADY_RUNNING retries.
+// TODO(omaha3): If we're willing to have a single metric for installs and
+// updates, we can avoid knowing is_update.
+#if 0
+    if (!app_version_->is_update()) {
+#endif
+      ++metric_worker_install_msi_in_progress_detected_install;
+      if (result_info->type == INSTALLER_RESULT_SUCCESS) {
+        ++metric_worker_install_msi_in_progress_retry_succeeded_install;
+        metric_worker_install_msi_in_progress_retry_succeeded_tries_install
+            = num_tries;
+      }
+#if 0
+    } else {
+      ++metric_worker_install_msi_in_progress_detected_update;
+      if (result_info->type == INSTALLER_RESULT_SUCCESS) {
+        ++metric_worker_install_msi_in_progress_retry_succeeded_update;
+        metric_worker_install_msi_in_progress_retry_succeeded_tries_update
+            = num_tries;
+      }
+    }
+#endif
+  }
+
+  return hr;
+}
+
+HRESULT InstallerWrapper::DoExecuteAndWaitForInstaller(
+    HANDLE user_token,
+    const GUID& app_guid,
+    const CString& executable_path,
+    const CString& command_line,
+    InstallerType installer_type,
+    const CString& language,
+    InstallerResultInfo* result_info) {
+  OPT_LOG(L1, (_T("[Running installer][%s][%s][%s]"),
+               executable_path, command_line, GuidToString(app_guid)));
+  ASSERT1(result_info);
+
+  AppManager::Instance()->ClearInstallerResultApiValues(app_guid);
+
+  Process p(executable_path, NULL);
+  HRESULT hr = p.Start(command_line, user_token);
+  if (FAILED(hr)) {
+    OPT_LOG(LE, (_T("[p.Start fail][hr][%s][%s]"),
+        hr, executable_path, command_line));
+    set_error_extra_code1(static_cast<int>(hr));
+    return GOOPDATEINSTALL_E_INSTALLER_FAILED_START;
+  }
+
+  // TODO(omaha): InstallerWrapper should not special case Omaha. It is better
+  // to have an abstraction such as waiting or not for the installer to
+  // exit and let the App state machine special case Omaha. It's too low level
+  // to make a decision like this in the InstallerWrapper. Same for all
+  // kinds of tests on the call stack above this call that the app_guid is
+  // Omaha's guid.
+  if (::IsEqualGUID(app_guid, kGoopdateGuid)) {
+    // Do not wait for the installer when installing Omaha.
+    result_info->type = INSTALLER_RESULT_SUCCESS;
+    return S_OK;
+  }
+
+  if (!p.WaitUntilDead(kInstallerCompleteIntervalMs)) {
+    OPT_LOG(LEVEL_WARNING, (_T("[Installer has timed out]")
+                            _T("[%s][%s]"), executable_path, command_line));
+    return GOOPDATEINSTALL_E_INSTALLER_TIMED_OUT;
+  }
+
+  hr = GetInstallerResult(app_guid,
+                          installer_type,
+                          p,
+                          language,
+                          result_info);
+  if (FAILED(hr)) {
+    CORE_LOG(LEVEL_ERROR, (_T("[GetInstallerResult failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  if (result_info->type != INSTALLER_RESULT_SUCCESS) {
+    OPT_LOG(LE, (_T("[Installer failed][%s][%s][%u]"),
+                 executable_path, command_line, result_info->code));
+  }
+
+  return S_OK;
+}
+
+HRESULT InstallerWrapper::GetInstallerResult(const GUID& app_guid,
+                                           InstallerType installer_type,
+                                           const Process& p,
+                                           const CString& language,
+                                           InstallerResultInfo* result_info) {
+  CORE_LOG(L3, (_T("[InstallerWrapper::GetInstallerResult]")));
+  ASSERT1(result_info);
+
+  uint32 exit_code = 0;
+  HRESULT hr = GetInstallerExitCode(p, &exit_code);
+  if (FAILED(hr)) {
+    CORE_LOG(LEVEL_ERROR, (_T("[GetInstallerExitCode failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  GetInstallerResultHelper(app_guid,
+                           installer_type,
+                           exit_code,
+                           language,
+                           result_info);
+  return S_OK;
+}
+
+// The default InstallerResult behavior can be overridden in the registry.
+// By default, error_code is the exit code. For some InstallerResults, it
+// can be overridden by InstallerError in the registry.
+// The success string cannot be overridden.
+void InstallerWrapper::GetInstallerResultHelper(
+    const GUID& app_guid,
+    InstallerType installer_type,
+    uint32 exit_code,
+    const CString& language,
+    InstallerResultInfo* result_info) {
+  ASSERT1(result_info);
+
+  AppManager::InstallerResult installer_result =
+      AppManager::INSTALLER_RESULT_DEFAULT;
+  InstallerResultInfo result;
+
+  result.code = exit_code;
+
+  AppManager& app_manager = *AppManager::Instance();
+  app_manager.ReadInstallerResultApiValues(
+      app_guid,
+      &installer_result,
+      &result.code,
+      &result.extra_code1,
+      &result.text,
+      &result.post_install_launch_command_line);
+  OPT_LOG(L1, (_T("[InstallerResult][%s][%u]"),
+               GuidToString(app_guid), installer_result));
+
+  switch (installer_result) {
+    case AppManager::INSTALLER_RESULT_SUCCESS:
+      result.type = INSTALLER_RESULT_SUCCESS;
+      // TODO(omaha3): Support custom success messages.
+      break;
+    case AppManager::INSTALLER_RESULT_FAILED_CUSTOM_ERROR:
+      result.type = INSTALLER_RESULT_ERROR_OTHER;
+      break;
+    case AppManager::INSTALLER_RESULT_FAILED_MSI_ERROR:
+      result.type = INSTALLER_RESULT_ERROR_MSI;
+      break;
+    case AppManager::INSTALLER_RESULT_FAILED_SYSTEM_ERROR:
+      result.type = INSTALLER_RESULT_ERROR_SYSTEM;
+      break;
+    case AppManager::INSTALLER_RESULT_EXIT_CODE:
+      ASSERT(result.code == exit_code, (_T("InstallerError overridden")));
+      if (0 == exit_code) {
+        result.type = INSTALLER_RESULT_SUCCESS;
+        result.code = 0;
+      } else {
+        switch (installer_type) {
+          case MSI_INSTALLER:
+            result.type = INSTALLER_RESULT_ERROR_MSI;
+            break;
+          case UNKNOWN_INSTALLER:
+          case CUSTOM_INSTALLER:
+          case MAX_INSTALLER:
+          default:
+            result.type = INSTALLER_RESULT_ERROR_OTHER;
+            break;
+        }
+      }
+      break;
+    case AppManager::INSTALLER_RESULT_MAX:
+    default:
+      ASSERT1(false);
+      break;
+  }
+
+  // Handle the reboot required case.
+  if ((INSTALLER_RESULT_ERROR_MSI == result.type ||
+       INSTALLER_RESULT_ERROR_SYSTEM == result.type) &&
+      (ERROR_SUCCESS_REBOOT_REQUIRED == result.code)) {
+    // Reboot takes precedence over other actions.
+    result.type = INSTALLER_RESULT_SUCCESS;
+    result.code = 0;
+    result.post_install_action = POST_INSTALL_ACTION_REBOOT;
+  } else if (!result.post_install_launch_command_line.IsEmpty()) {
+    result.post_install_action = POST_INSTALL_ACTION_LAUNCH_COMMAND;
+  }
+
+  // InstallerResultInfo status has been finalized. Make sure all errors
+  // have error strings.
+  switch (result.type) {
+    case INSTALLER_RESULT_SUCCESS:
+      break;
+
+    case INSTALLER_RESULT_ERROR_MSI:
+    case INSTALLER_RESULT_ERROR_SYSTEM:
+      GetSystemErrorString(result.code, language, &result.text);
+      break;
+
+    case INSTALLER_RESULT_ERROR_OTHER:
+      if (result.text.IsEmpty()) {
+        result.text.FormatMessage(
+              IDS_INSTALLER_FAILED_NO_MESSAGE,
+              FormatErrorCode(result.code));
+      }
+      break;
+
+    case INSTALLER_RESULT_UNKNOWN:
+    default:
+      ASSERT1(false);
+  }
+
+  // TODO(omaha3): Serialize InstallerResultInfo.
+  // OPT_LOG(L1, (_T("[%s]"), result_info->ToString()));
+  *result_info = result;
+}
+
+// TODO(omaha3): Consider moving this method out of this class, maybe into
+// InstallManager.
+HRESULT InstallerWrapper::CheckApplicationRegistration(
+    const GUID& app_guid,
+    const CString& registered_version,
+    const CString& expected_version,
+    const CString& previous_version,
+    bool is_update) const {
+  const CString app_guid_string = GuidToString(app_guid);
+  CORE_LOG(L2, (_T("[InstallerWrapper::CheckApplicationRegistration][%s]"),
+                app_guid_string));
+  ASSERT(!::IsEqualGUID(kGoopdateGuid, app_guid),
+         (_T("Probably do not want to call this method for Omaha")));
+
+  if (registered_version.IsEmpty()) {
+    return GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY;
+  }
+
+  CORE_LOG(L2, (_T("[CheckApplicationRegistration]")
+                _T("[guid=%s][registered=%s][expected=%s][previous=%s]"),
+                app_guid_string, registered_version, expected_version,
+                previous_version));
+
+  if (!expected_version.IsEmpty() && registered_version != expected_version) {
+    OPT_LOG(LE, (_T("[Registered version does not match expected][%s][%s][%s]"),
+                 app_guid_string, registered_version, expected_version));
+    // This is expected if a newer version is already installed. Do not fail
+    // here in that case. This only works for four-element version strings.
+    // If the version format is not recognized, VersionFromString() returns 0.
+
+    ULONGLONG registered_version_number = VersionFromString(registered_version);
+    ULONGLONG expected_version_number = VersionFromString(expected_version);
+
+    // Check that the version did not change, the registered version is newer,
+    // and neither VersionFromString() call failed.
+    if (is_update && registered_version != previous_version ||
+        registered_version_number < expected_version_number ||
+        !registered_version_number ||
+        !expected_version_number) {
+      return GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH;
+    }
+
+    CORE_LOG(L1, (_T("[Newer version already registered]")));
+  }
+
+  if (is_update && previous_version == registered_version) {
+    ASSERT1(!previous_version.IsEmpty());
+    ASSERT(expected_version.IsEmpty() ||
+           VersionFromString(expected_version) >
+               VersionFromString(previous_version) ||
+           VersionFromString(expected_version) == 0 ||
+           VersionFromString(previous_version) == 0,
+           (_T("expected_version should be > previous_version when ")
+            _T("is_update - possibly a bad update rule.")));
+
+    OPT_LOG(LE, (_T("[Installer did not change version][%s][%s]"),
+                 app_guid_string, previous_version));
+    return GOOPDATEINSTALL_E_INSTALLER_DID_NOT_CHANGE_VERSION;
+  }
+
+  return S_OK;
+}
+
+// Assumes installer_lock_ has been initialized.
+HRESULT InstallerWrapper::DoInstallApp(HANDLE user_token,
+                                     const GUID& app_guid,
+                                     const CString& installer_path,
+                                     const CString& arguments,
+                                     const CString& installer_data,
+                                     const CString& language,
+                                     InstallerResultInfo* result_info) {
+  CORE_LOG(L1, (_T("[InstallerWrapper::DoInstallApp][%s][%s][%s]"),
+               GuidToString(app_guid), installer_path, arguments));
+  ASSERT1(result_info);
+
+  CString executable_path;
+  CString command_line;
+  InstallerType installer_type = UNKNOWN_INSTALLER;
+
+  // TODO(omaha): Remove when http://b/1443404 is addressed.
+  const TCHAR* const kChromeGuid = _T("{8A69D345-D564-463C-AFF1-A69D9E530F96}");
+  const TCHAR* const kChromePerMachineArg = _T("--system-level");
+  CString modified_arguments = arguments;
+  if (kChromeGuid == GuidToString(app_guid) && is_machine_) {
+    modified_arguments.AppendFormat(_T(" %s"), kChromePerMachineArg);
+  }
+
+  HRESULT hr = BuildCommandLineFromFilename(installer_path,
+                                            modified_arguments,
+                                            installer_data,
+                                            &executable_path,
+                                            &command_line,
+                                            &installer_type);
+
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[BuildCommandLineFromFilename failed][0x%08x]"), hr));
+    ASSERT1(GOOPDATEINSTALL_E_FILENAME_INVALID == hr);
+    return hr;
+  }
+
+  // Acquire the global lock here. This will ensure that we are the only
+  // installer running of the multiple goopdates.
+  __mutexBlock(installer_lock_) {
+    hr = ExecuteAndWaitForInstaller(user_token,
+                                    app_guid,
+                                    executable_path,
+                                    command_line,
+                                    installer_type,
+                                    language,
+                                    result_info);
+  }
+
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[ExecuteAndWaitForInstaller failed][0x%08x][%s]"),
+                  hr, GuidToString(app_guid)));
+    return hr;
+  }
+
+  if (result_info->type != INSTALLER_RESULT_SUCCESS) {
+    CORE_LOG(LE, (_T("[Installer failed][%d]"), result_info->type));
+    return GOOPDATEINSTALL_E_INSTALLER_FAILED;
+  }
+
+  return S_OK;
+}
+
+}  // namespace omaha
diff --git a/goopdate/installer_wrapper.h b/goopdate/installer_wrapper.h
new file mode 100644
index 0000000..6cafe12
--- /dev/null
+++ b/goopdate/installer_wrapper.h
@@ -0,0 +1,169 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// InstallerWrapper supports installing one app at a time. Installs from
+// multiple instances are serialized by a mutex.
+
+#ifndef OMAHA_GOOPDATE_INSTALLER_WRAPPER_H_
+#define OMAHA_GOOPDATE_INSTALLER_WRAPPER_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include <queue>
+#include <utility>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/goopdate/installer_result_info.h"
+
+// TODO(omaha): consider removing this dependency on the model.
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+class AppVersion;
+class Process;
+
+
+class InstallerWrapper {
+ public:
+  explicit InstallerWrapper(bool is_machine);
+  ~InstallerWrapper();
+  HRESULT Initialize();
+
+  // Installs the specified app.
+  // This is a blocking call. All errors are reported through the return
+  // value. Depending on the return value, messages may be obtained as follows:
+  //  * SUCCEEDED(hr): result_info may contain a custom success message.
+  //  * GOOPDATEINSTALL_E_INSTALLER_FAILED: output parameters contain
+  //    information and a message for the installer error.
+  //  * Other error values: Callers may use GetMessageForError() to convert the
+  //    error value to an error message.
+  HRESULT InstallApp(HANDLE user_token,
+                     const GUID& app_guid,
+                     const CString& installer_path,
+                     const CString& arguments,
+                     const CString& installer_data,
+                     const CString& language,
+                     InstallerResultInfo* result_info);
+
+  // Validate that the installer wrote the client key and the product version.
+  HRESULT CheckApplicationRegistration(const GUID& app_guid,
+                                       const CString& registered_version,
+                                       const CString& expected_version,
+                                       const CString& previous_version,
+                                       bool is_update) const;
+
+  // Obtains the localized text for Omaha errors that may occur during install.
+  static CString GetMessageForError(HRESULT error_code,
+                                    const CString& installer_filename,
+                                    const CString& language);
+
+  void set_num_tries_when_msi_busy(int num_tries_when_msi_busy);
+
+ private:
+  // Types of installers that Omaha supports.
+  enum InstallerType {
+    UNKNOWN_INSTALLER = 0,
+    CUSTOM_INSTALLER,
+    MSI_INSTALLER,
+    MAX_INSTALLER  // Last Installer Type value.
+  };
+
+  // Determines the executable, command line, and installer type for
+  // the installation based on the filename.
+  static HRESULT BuildCommandLineFromFilename(const CString& filename,
+                                              const CString& arguments,
+                                              const CString& installer_data,
+                                              CString* executable_name,
+                                              CString* command_line,
+                                              InstallerType* installer_type);
+
+  // Executes the installer and waits for it to complete. Retries if necessary.
+  HRESULT ExecuteAndWaitForInstaller(HANDLE user_token,
+                                     const GUID& app_guid,
+                                     const CString& executable_name,
+                                     const CString& command_line,
+                                     InstallerType installer_type,
+                                     const CString& language,
+                                     InstallerResultInfo* result_info);
+
+  // Executes the installer for ExecuteAndWaitForInstaller.
+  HRESULT DoExecuteAndWaitForInstaller(HANDLE user_token,
+                                       const GUID& app_guid,
+                                       const CString& executable_name,
+                                       const CString& command_line,
+                                       InstallerType installer_type,
+                                       const CString& language,
+                                       InstallerResultInfo* result_info);
+
+  // Determines whether the installer succeeded and returns completion info.
+  HRESULT GetInstallerResult(const GUID& app_guid,
+                             InstallerType installer_type,
+                             const Process& p,
+                             const CString& language,
+                             InstallerResultInfo* result_info);
+
+  // Does most of the work for GetInstallerResult.
+  void GetInstallerResultHelper(const GUID& app_guid,
+                                InstallerType installer_type,
+                                uint32 exit_code,
+                                const CString& language,
+                                InstallerResultInfo* result_info);
+
+  // Cleans up the registry from an installer that set custom result values.
+  void ClearInstallerResultApiValues(const CString& app_guid);
+
+  // Installs the specified application and reports the results.
+  HRESULT DoInstallApp(HANDLE user_token,
+                       const GUID& app_guid,
+                       const CString& installer_path,
+                       const CString& arguments,
+                       const CString& installer_data,
+                       const CString& language,
+                       InstallerResultInfo* result_info);
+
+  // Whether this object is running in a machine Goopdate instance.
+  const bool is_machine_;
+
+  // The number of times to try installing an MSI when an MSI install is
+  // already running. There is an exponential backoff starting from
+  // kMsiAlreadyRunningRetryDelayBaseMs.
+  int num_tries_when_msi_busy_;
+
+  // This is the base retry delay between retries when msiexec returns
+  // ERROR_INSTALL_ALREADY_RUNNING. We exponentially backoff from this value.
+  // Note that there is an additional delay for the MSI call, so the tries may
+  // be a few seconds further apart.
+  static const int kMsiAlreadyRunningRetryDelayBaseMs = 5000;
+
+  // Interval to wait for installer completion.
+  static const int kInstallerCompleteIntervalMs = 15 * 60 * 1000;
+
+  // Ensures that a single installer is run by us at a time.
+  // Not sure if we can run installers in different sessions without
+  // interference. In that case we can use a local lock instead of a
+  // global lock.
+  GLock installer_lock_;
+
+  friend class InstallerWrapperTest;
+
+  DISALLOW_COPY_AND_ASSIGN(InstallerWrapper);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_INSTALLER_WRAPPER_H_
+
diff --git a/goopdate/installer_wrapper_unittest.cc b/goopdate/installer_wrapper_unittest.cc
new file mode 100644
index 0000000..460c2a3
--- /dev/null
+++ b/goopdate/installer_wrapper_unittest.cc
@@ -0,0 +1,1894 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <atlpath.h>
+#include <atlstr.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/process.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/shell.h"
+#include "omaha/base/system.h"
+#include "omaha/base/timer.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/app_registry_utils.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/install_manifest.h"
+#include "omaha/goopdate/app_manager.h"
+#include "omaha/goopdate/installer_wrapper.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/resource_manager.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+const TCHAR kAppId[] = _T("{B18BC01B-E0BD-4BF0-A33E-1133055E5FDE}");
+const GUID kAppGuid = {0xB18BC01B, 0xE0BD, 0x4BF0,
+                       {0xA3, 0x3E, 0x11, 0x33, 0x05, 0x5E, 0x5F, 0xDE}};
+
+const TCHAR kFullAppClientsKeyPath[] =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\")
+    PRODUCT_NAME _T("\\Clients\\{B18BC01B-E0BD-4BF0-A33E-1133055E5FDE}");
+const TCHAR kFullAppClientStateKeyPath[] =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\")
+    PRODUCT_NAME _T("\\ClientState\\{B18BC01B-E0BD-4BF0-A33E-1133055E5FDE}");
+const TCHAR kFullFooAppClientKeyPath[] =
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\")
+    PRODUCT_NAME _T("\\Clients\\{D6B08267-B440-4C85-9F79-E195E80D9937}");
+const TCHAR kFullFooAppClientStateKeyPath[] =
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\")
+    PRODUCT_NAME _T("\\ClientState\\{D6B08267-B440-4C85-9F79-E195E80D9937}");
+
+const TCHAR kSetupFooV1RelativeLocation[] =
+  _T("unittest_support\\test_foo_v1.0.101.0.msi");
+const TCHAR kFooGuid[] = _T("{D6B08267-B440-4C85-9F79-E195E80D9937}");
+const TCHAR kFooInstallerBarPropertyArg[] = _T("PROPBAR=7");
+const TCHAR kFooInstallerBarValueName[] = _T("propbar");
+
+// Values related to using cmd.exe as an "installer".
+const TCHAR kCmdExecutable[] = _T("cmd.exe");
+const TCHAR kExecuteCommandAndTerminateSwitch[] = _T("/c %s");
+const TCHAR kExecuteTwoCommandsFormat[] = _T("\"%s & %s\"");
+
+const TCHAR kMsiLogFormat[] = _T("%s.log");
+
+const TCHAR kMsiUninstallArguments[] = _T("/quiet /uninstall \"%s\"");
+const TCHAR kMsiCommand[] = _T("msiexec");
+
+const DWORD kInitialErrorValue = 5;
+const TCHAR kMeaninglessErrorString[] = _T("This is an error string.");
+
+// Some error strings are slightly different on Vista than XP because spaces
+// were removed. Therefore, the comparison must ignore that space.
+
+// The US English error string for ERROR_INSTALL_PACKAGE_OPEN_FAILED.
+const TCHAR kMsiPackageOpenFailedStringPartA[] =
+    _T("This installation package could not be opened. ");
+const TCHAR kMsiPackageOpenFailedStringPartB[] =
+    _T("Verify that the package exists and that you can access it, ")
+    _T("or contact the application vendor to verify that this is a ")
+    _T("valid Windows Installer package. ");
+
+// The US English error string for ERROR_INSTALL_ALREADY_RUNNING.
+const TCHAR kMsiBusyStringPartA[] =
+    _T("Another installation is already in progress. ");
+const TCHAR kMsiBusyStringPartB[] =
+    _T("Complete that installation before proceeding with this install. ");
+
+const TCHAR* const kError1603Text =
+    _T("The installer encountered error 1603: Fatal error during ")
+    _T("installation. ");
+
+const TCHAR kError1618MessagePrefix[] =
+    _T("The installer encountered error 1618: ");
+const int kError1618MessagePrefixLength =
+    arraysize(kError1618MessagePrefix) - 1;
+
+const TCHAR* const kError0x800B010FText =
+    _T("The installer encountered error 0x800b010f: ")
+    _T("The certificate's CN name does not match the passed value. ");
+
+const TCHAR* const kLaunchCmdLine =
+      _T("\"C:\\Local\\Google\\Chrome\\Application\\chrome.exe\" -home");
+
+const TCHAR* const kLanguageEnglish = _T("en");
+
+const int kMsiAlreadyRunningRetryDelayBaseMs = 5000;
+const int kNumMsiTriesDefault = 4;  // Up to 35 seconds.
+const int kNumMsiTriesOnBuildSystem = 7;  // Up to 6.25 minutes.
+
+int GetNumMsiTries() {
+  return IsBuildSystem() ? kNumMsiTriesOnBuildSystem : kNumMsiTriesDefault;
+}
+
+}  // namespace
+
+extern const TCHAR kRegExecutable[] = _T("reg.exe");
+extern const TCHAR kSetInstallerResultTypeMsiErrorRegCmdArgs[] =
+    _T("add HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\UnitTest\\HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\")
+    PRODUCT_NAME _T("\\ClientState\\{B18BC01B-E0BD-4BF0-A33E-1133055E5FDE} ")
+    _T("/v InstallerResult /t REG_DWORD /d 2");
+extern const TCHAR kMsiInstallerBusyExitCodeCmd[] = _T("exit 1618");
+
+extern const TCHAR kError1619MessagePrefix[] =
+    _T("The installer encountered error 1619: ");
+extern const int kError1619MessagePrefixLength =
+    arraysize(kError1619MessagePrefix) - 1;
+
+void VerifyStringIsMsiPackageOpenFailedString(const CString& str) {
+  EXPECT_STREQ(kMsiPackageOpenFailedStringPartA,
+               str.Left(arraysize(kMsiPackageOpenFailedStringPartA) - 1));
+  EXPECT_STREQ(kMsiPackageOpenFailedStringPartB,
+               str.Right(arraysize(kMsiPackageOpenFailedStringPartB) - 1));
+}
+
+void VerifyStringIsMsiBusyString(const CString& str) {
+  EXPECT_STREQ(kMsiBusyStringPartA,
+               str.Left(arraysize(kMsiBusyStringPartA) - 1));
+  EXPECT_STREQ(kMsiBusyStringPartB,
+               str.Right(arraysize(kMsiBusyStringPartB) - 1));
+}
+
+// Unit tests may run while other updaters are running on the build system.
+// Give the tests lots of time to run to avoid false negatives.
+void AdjustMsiTries(InstallerWrapper* installer_wrapper) {
+  ASSERT1(installer_wrapper);
+  installer_wrapper->set_num_tries_when_msi_busy(GetNumMsiTries());
+}
+
+// Waits for the uninstall to complete to avoid race conditions with other tests
+// that install the same MSI. It appears msiexec causes an an asynchronous
+// request that may be processed out of order.
+// Retries when ERROR_INSTALL_ALREADY_RUNNING is encountered.
+void UninstallTestMsi(const CString& installer_path) {
+  CString uninstall_arguments;
+  uninstall_arguments.Format(kMsiUninstallArguments, installer_path);
+
+  const int max_tries = GetNumMsiTries();
+  int retry_delay = kMsiAlreadyRunningRetryDelayBaseMs;
+  int num_tries(0);
+  uint32 exit_code = ERROR_INSTALL_ALREADY_RUNNING;
+  for (num_tries = 0;
+       exit_code == ERROR_INSTALL_ALREADY_RUNNING && num_tries < max_tries;
+       ++num_tries) {
+    if (0 < num_tries) {
+      // Retrying - wait between attempts.
+      ::Sleep(retry_delay);
+      retry_delay *= 2;  // Double the retry delay next time.
+    }
+
+    Process p(kMsiCommand, NULL);
+    EXPECT_HRESULT_SUCCEEDED(p.Start(uninstall_arguments, NULL));
+    EXPECT_TRUE(p.WaitUntilDead(10000));
+
+    EXPECT_TRUE(p.GetExitCode(&exit_code));
+  }
+
+  EXPECT_EQ(0, exit_code);
+}
+
+class InstallerWrapperTest : public testing::Test {
+ protected:
+  explicit InstallerWrapperTest(bool is_machine)
+      : is_machine_(is_machine),
+        hive_override_key_name_(kRegistryHiveOverrideRoot) {
+  }
+
+  static void SetUpTestCase() {
+    CString system_path;
+    EXPECT_SUCCEEDED(Shell::GetSpecialFolder(CSIDL_SYSTEM,
+                                             false,
+                                             &system_path));
+    EXPECT_FALSE(system_path.IsEmpty());
+    cmd_exe_path_.Combine(system_path, kCmdExecutable);
+    EXPECT_TRUE(File::Exists(cmd_exe_path_));
+
+    CPath reg_path;
+    reg_path.Combine(system_path, kRegExecutable);
+    set_installer_result_type_msi_error_cmd_.Format(
+        _T("%s %s"),
+        reg_path, kSetInstallerResultTypeMsiErrorRegCmdArgs);
+  }
+
+  virtual void SetUp() {
+    RegKey::DeleteKey(hive_override_key_name_, true);
+    OverrideRegistryHivesWithExecutionPermissions(hive_override_key_name_);
+
+    EXPECT_SUCCEEDED(AppManager::CreateInstance(is_machine_));
+
+    im_.reset(new InstallerWrapper(is_machine_));
+    EXPECT_SUCCEEDED(im_->Initialize());
+
+    EXPECT_SUCCEEDED(ResourceManager::Create(
+          is_machine_, app_util::GetCurrentModuleDirectory(), _T("en")));
+  }
+
+  virtual void TearDown() {
+    AppManager::DeleteInstance();
+
+    RestoreRegistryHives();
+    ASSERT_SUCCEEDED(RegKey::DeleteKey(hive_override_key_name_, true));
+    ResourceManager::Delete();
+  }
+
+  void SetupInstallerResultRegistry(const CString& app_guid,
+                                    bool set_installer_result,
+                                    DWORD installer_result,
+                                    bool set_installer_error,
+                                    DWORD installer_error,
+                                    bool set_installer_extra_code1,
+                                    DWORD installer_extra_code1,
+                                    bool set_installer_result_uistring,
+                                    const CString& installer_result_uistring,
+                                    bool set_installer_launch_cmd_line,
+                                    const CString& installer_launch_cmd_line) {
+    CString app_client_state_key =
+        app_registry_utils::GetAppClientStateKey(is_machine_, app_guid);
+    RegKey::CreateKey(app_client_state_key);
+    if (set_installer_result) {
+      RegKey::SetValue(app_client_state_key,
+                       kRegValueInstallerResult,
+                       installer_result);
+    }
+
+    if (set_installer_error) {
+      RegKey::SetValue(app_client_state_key,
+                       kRegValueInstallerError,
+                       installer_error);
+    }
+
+    if (set_installer_extra_code1) {
+      RegKey::SetValue(app_client_state_key,
+                       kRegValueInstallerExtraCode1,
+                       installer_extra_code1);
+    }
+
+    if (set_installer_result_uistring) {
+      RegKey::SetValue(app_client_state_key,
+                       kRegValueInstallerResultUIString,
+                       installer_result_uistring);
+    }
+
+    if (set_installer_launch_cmd_line) {
+      RegKey::SetValue(app_client_state_key,
+                       kRegValueInstallerSuccessLaunchCmdLine,
+                       installer_launch_cmd_line);
+    }
+  }
+
+  void VerifyLastRegistryValues(const CString& app_guid,
+                                bool expect_installer_result,
+                                DWORD expected_installer_result,
+                                bool expect_installer_error,
+                                DWORD expected_installer_error,
+                                bool expect_installer_extra_code1,
+                                DWORD expected_installer_extra_code1,
+                                bool expect_installer_result_uistring,
+                                const CString& expected_result_uistring,
+                                bool expect_installer_launch_cmd_line,
+                                const CString& expected_launch_cmd_line) {
+    ASSERT_TRUE(expect_installer_result || !expected_installer_result);
+    ASSERT_TRUE(expect_installer_error || !expected_installer_error);
+    ASSERT_TRUE(expect_installer_extra_code1 ||
+                !expected_installer_extra_code1);
+    ASSERT_TRUE(expect_installer_result_uistring ||
+                expected_result_uistring.IsEmpty());
+    ASSERT_TRUE(expect_installer_launch_cmd_line ||
+                expected_launch_cmd_line.IsEmpty());
+
+    CString app_client_state_key =
+        app_registry_utils::GetAppClientStateKey(is_machine_, app_guid);
+    EXPECT_FALSE(RegKey::HasValue(app_client_state_key,
+                                  kRegValueInstallerResult));
+    EXPECT_FALSE(RegKey::HasValue(app_client_state_key,
+                                  kRegValueInstallerError));
+    EXPECT_FALSE(RegKey::HasValue(app_client_state_key,
+                                  kRegValueInstallerExtraCode1));
+    EXPECT_FALSE(RegKey::HasValue(app_client_state_key,
+                                  kRegValueInstallerResultUIString));
+
+    if (expect_installer_result) {
+      EXPECT_TRUE(RegKey::HasValue(app_client_state_key,
+                                   kRegValueLastInstallerResult));
+      DWORD last_installer_result = 0;
+      EXPECT_SUCCEEDED(RegKey::GetValue(app_client_state_key,
+                                        kRegValueLastInstallerResult,
+                                        &last_installer_result));
+      EXPECT_EQ(expected_installer_result, last_installer_result);
+    } else {
+      EXPECT_FALSE(RegKey::HasValue(app_client_state_key,
+                                    kRegValueLastInstallerResult));
+    }
+
+    if (expect_installer_error) {
+      EXPECT_TRUE(RegKey::HasValue(app_client_state_key,
+                                   kRegValueLastInstallerError));
+      DWORD last_installer_error = 0;
+      EXPECT_SUCCEEDED(RegKey::GetValue(app_client_state_key,
+                                        kRegValueLastInstallerError,
+                                        &last_installer_error));
+      EXPECT_EQ(expected_installer_error, last_installer_error);
+    } else {
+      EXPECT_FALSE(RegKey::HasValue(app_client_state_key,
+                                    kRegValueLastInstallerError));
+    }
+
+    if (expect_installer_extra_code1) {
+      EXPECT_TRUE(RegKey::HasValue(app_client_state_key,
+                                   kRegValueLastInstallerExtraCode1));
+      DWORD last_installer_extracode1 = 0;
+      EXPECT_SUCCEEDED(RegKey::GetValue(app_client_state_key,
+                                        kRegValueLastInstallerExtraCode1,
+                                        &last_installer_extracode1));
+      EXPECT_EQ(expected_installer_extra_code1, last_installer_extracode1);
+    } else {
+      EXPECT_FALSE(RegKey::HasValue(app_client_state_key,
+                                    kRegValueLastInstallerExtraCode1));
+    }
+
+    if (expect_installer_result_uistring) {
+      EXPECT_TRUE(RegKey::HasValue(app_client_state_key,
+                                   kRegValueLastInstallerResultUIString));
+      CString last_installer_result_uistring;
+      EXPECT_SUCCEEDED(RegKey::GetValue(app_client_state_key,
+                                        kRegValueLastInstallerResultUIString,
+                                        &last_installer_result_uistring));
+      EXPECT_STREQ(expected_result_uistring,
+                   last_installer_result_uistring);
+    } else {
+      EXPECT_FALSE(RegKey::HasValue(app_client_state_key,
+                                    kRegValueLastInstallerResultUIString));
+    }
+
+    if (expect_installer_launch_cmd_line) {
+      EXPECT_TRUE(RegKey::HasValue(app_client_state_key,
+                                   kRegValueLastInstallerSuccessLaunchCmdLine));
+      CString last_installer_launch_cmd_line;
+      EXPECT_SUCCEEDED(
+          RegKey::GetValue(app_client_state_key,
+                           kRegValueLastInstallerSuccessLaunchCmdLine,
+                           &last_installer_launch_cmd_line));
+      EXPECT_STREQ(expected_launch_cmd_line,
+                   last_installer_launch_cmd_line);
+    } else {
+      EXPECT_FALSE(RegKey::HasValue(
+          app_client_state_key,
+          kRegValueLastInstallerSuccessLaunchCmdLine));
+    }
+  }
+
+  void VerifyNoLastRegistryValues(const CString& app_guid) {
+    VerifyLastRegistryValues(app_guid,
+                             false, 0,
+                             false, 0,
+                             false, 0,
+                             false, _T(""),
+                             false, _T(""));
+  }
+
+  void CallGetInstallerResultHelper(const GUID& app_guid,
+                                    int installer_type,
+                                    uint32 exit_code,
+                                    InstallerResultInfo* result_info) {
+    ASSERT1(result_info);
+
+    im_->GetInstallerResultHelper(
+        app_guid,
+        static_cast<InstallerWrapper::InstallerType>(installer_type),
+        exit_code,
+        kLanguageEnglish,
+        result_info);
+  }
+
+  static const int kResultSuccess = AppManager::INSTALLER_RESULT_SUCCESS;
+  static const int kResultFailedCustomError =
+      AppManager::INSTALLER_RESULT_FAILED_CUSTOM_ERROR;
+  static const int kResultFailedMsiError =
+      AppManager::INSTALLER_RESULT_FAILED_MSI_ERROR;
+  static const int kResultFailedSystemError =
+      AppManager::INSTALLER_RESULT_FAILED_SYSTEM_ERROR;
+  static const int kResultExitCode = AppManager::INSTALLER_RESULT_EXIT_CODE;
+
+  static const int kMsiInstaller = InstallerWrapper::MSI_INSTALLER;
+  static const int kOtherInstaller = InstallerWrapper::CUSTOM_INSTALLER;
+
+  bool is_machine_;
+  CString hive_override_key_name_;
+  scoped_ptr<InstallerWrapper> im_;
+
+  // Used as an argument to various functions.
+  InstallerResultInfo result_info_;
+
+  static CPath cmd_exe_path_;
+  static CString set_installer_result_type_msi_error_cmd_;
+};
+
+CPath InstallerWrapperTest::cmd_exe_path_;
+CString InstallerWrapperTest::set_installer_result_type_msi_error_cmd_;
+
+class InstallerWrapperMachineTest : public InstallerWrapperTest {
+ protected:
+  InstallerWrapperMachineTest()
+    : InstallerWrapperTest(true) {
+  }
+};
+
+class InstallerWrapperUserTest : public InstallerWrapperTest {
+ protected:
+  InstallerWrapperUserTest()
+    : InstallerWrapperTest(false) {
+  }
+};
+
+class InstallerWrapperUserGetInstallerResultHelperTest
+    : public InstallerWrapperUserTest {
+ protected:
+  InstallerWrapperUserGetInstallerResultHelperTest()
+      : InstallerWrapperUserTest() {
+    result_info_.text = kMeaninglessErrorString;
+  }
+
+  virtual void SetUp() {
+    InstallerWrapperUserTest::SetUp();
+    AppManager::Instance()->GetRegistryStableStateLock().Lock();
+  }
+
+  virtual void TearDown() {
+    AppManager::Instance()->GetRegistryStableStateLock().Unlock();
+    InstallerWrapperUserTest::TearDown();
+  }
+
+  // Shorter names used to make test calls fit on one line.
+  static const int kMsi = kMsiInstaller;
+  static const int kOther = kOtherInstaller;
+};
+
+//
+// Helper method tests
+//
+TEST(InstallerWrapperTest, GetMessageForSystemErrorCode) {
+  VerifyStringIsMsiPackageOpenFailedString(
+      GetMessageForSystemErrorCode(ERROR_INSTALL_PACKAGE_OPEN_FAILED));
+}
+
+// CheckApplicationRegistration does not read the registry. This is verified by
+// not setting any registry values before calling it.
+
+TEST_F(InstallerWrapperUserTest,
+       CheckApplicationRegistration_EmptyRegisteredVersion) {
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T(""), _T(""), _T(""), false));
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T(""), _T(""), _T(""), true));
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T(""), _T("1.2.3.4"), _T(""), false));
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T(""), _T("1.2.3.4"), _T(""), true));
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T(""), _T("1.2.3.4"), _T("1.2.3.3"), false));
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T(""), _T("1.2.3.4"), _T("1.2.3.3"), true));
+}
+
+TEST_F(InstallerWrapperUserTest,
+       CheckApplicationRegistration_NoExpectedOrPreviousVersion) {
+  EXPECT_SUCCEEDED(im_->CheckApplicationRegistration(
+                       kAppGuid, _T("0.9.6.4"), _T(""), _T(""), false));
+  EXPECT_SUCCEEDED(im_->CheckApplicationRegistration(
+                       kAppGuid, _T("0.9.6.4"), _T(""), _T(""), true));
+}
+
+TEST_F(InstallerWrapperUserTest,
+       CheckApplicationRegistration_ExpectedMatch_NoPreviousVersion) {
+  EXPECT_SUCCEEDED(im_->CheckApplicationRegistration(
+                       kAppGuid, _T("0.9.6.4"), _T("0.9.6.4"), _T(""), false));
+  EXPECT_SUCCEEDED(im_->CheckApplicationRegistration(
+                       kAppGuid, _T("0.9.6.4"), _T("0.9.6.4"), _T(""), true));
+}
+
+TEST_F(InstallerWrapperUserTest,
+       CheckApplicationRegistration_NoExpectedVersion_PreviousVersionOlder) {
+  EXPECT_SUCCEEDED(im_->CheckApplicationRegistration(
+                       kAppGuid, _T("0.9.6.4"), _T(""), _T("0.9.6.3"), false));
+  EXPECT_SUCCEEDED(im_->CheckApplicationRegistration(
+                       kAppGuid, _T("0.9.6.4"), _T(""), _T("0.9.6.3"), true));
+}
+
+TEST_F(InstallerWrapperUserTest,
+       CheckApplicationRegistration_ExpectedMatch_PreviousVersionSame_UnchangedAllowed) {  // NOLINT
+  EXPECT_SUCCEEDED(
+      im_->CheckApplicationRegistration(
+          kAppGuid, _T("0.9.6.4"), _T(""), _T("0.9.6.4"), false));
+  EXPECT_SUCCEEDED(
+      im_->CheckApplicationRegistration(
+          kAppGuid, _T("0.9.6.4"), _T("0.9.6.4"), _T("0.9.6.4"), false));
+}
+
+TEST_F(InstallerWrapperUserTest,
+       CheckApplicationRegistration_ExpectedMatch_PreviousVersionSame_UnchangedNotAllowed) {  // NOLINT
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_CHANGE_VERSION,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.4"), _T(""), _T("0.9.6.4"), true));
+
+  ExpectAsserts expect_asserts;  // expected_version is expected to be greater.
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_CHANGE_VERSION,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.4"), _T("0.9.6.4"), _T("0.9.6.4"), true));
+}
+
+TEST_F(InstallerWrapperUserTest,
+       CheckApplicationRegistration_ExpectedMatch_PreviousVersionOlder) {
+  EXPECT_SUCCEEDED(
+      im_->CheckApplicationRegistration(
+          kAppGuid, _T("0.9.6.4"), _T("0.9.6.4"), _T("0.9.6.3"), false));
+  EXPECT_SUCCEEDED(
+      im_->CheckApplicationRegistration(
+          kAppGuid, _T("0.9.6.4"), _T("0.9.6.4"), _T("0.9.6.3"), true));
+}
+
+TEST_F(InstallerWrapperUserTest,
+       CheckApplicationRegistration_ExpectedMatch_PreviousVersionNewer) {
+  EXPECT_SUCCEEDED(
+      im_->CheckApplicationRegistration(
+          kAppGuid, _T("0.9.6.4"), _T("0.9.6.4"), _T("0.9.6.5"), false));
+  EXPECT_SUCCEEDED(
+      im_->CheckApplicationRegistration(
+          kAppGuid, _T("0.9.6.4"), _T("0.9.6.4"), _T("0.9.6.5"), true));
+}
+
+// This is the over-install when a newer version is present case. This might
+// happen if a dev track version is installed and a stable track version is
+// installed. For installs, this is okay.
+TEST_F(InstallerWrapperUserTest,
+       CheckApplicationRegistration_ExpectedMatch_RegisteredNewer_NoPreviousVersion_NewInstall) {  // NOLINT
+  EXPECT_SUCCEEDED(
+      im_->CheckApplicationRegistration(
+          kAppGuid, _T("0.9.6.4"), _T("0.9.6.3"), _T(""), false));
+}
+
+TEST_F(InstallerWrapperUserTest,
+       CheckApplicationRegistration_ExpectedMismatch_RegisteredNewer_NoPreviousVersion_Update) {  // NOLINT
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.4"), _T("0.9.6.3"), _T(""), true));
+}
+
+// This is the over-install when a newer version is present case. This might
+// happen if a dev track version is installed and a stable track version is
+// installed. For installs, this is okay.
+TEST_F(InstallerWrapperUserTest,
+       CheckApplicationRegistration_ExpectedMismatch_RegisteredNewer_PreviousVersionSame) {  // NOLINT
+  EXPECT_SUCCEEDED(
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.4"), _T("0.9.6.3"), _T("0.9.6.4"), false));
+  ExpectAsserts expect_asserts;  // expected_version is expected to be greater.
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_CHANGE_VERSION,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.4"), _T("0.9.6.3"), _T("0.9.6.4"), true));
+}
+
+TEST_F(InstallerWrapperUserTest,
+       CheckApplicationRegistration_ExpectedMismatch_RegisteredNewer_PreviousVersionDifferent) {  // NOLINT
+  EXPECT_SUCCEEDED(
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.4"), _T("0.9.6.3"), _T("0.9.6.3"), false));
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.4"), _T("0.9.6.3"), _T("0.9.6.3"), true));
+}
+
+TEST_F(InstallerWrapperUserTest,
+       CheckApplicationRegistration_ExpectedMismatch_RegisteredOlder_NoPreviousVersion) {  // NOLINT
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.2"), _T("0.9.6.3"), _T(""), false));
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.2"), _T("0.9.6.3"), _T(""), true));
+}
+
+TEST_F(InstallerWrapperUserTest,
+       CheckApplicationRegistration_ExpectedMismatch_RegisteredOlder_PreviousVersionSame) {  // NOLINT
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.2"), _T("0.9.6.3"), _T("0.9.6.2"), false));
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.2"), _T("0.9.6.3"), _T("0.9.6.2"), true));
+}
+
+TEST_F(InstallerWrapperUserTest,
+       CheckApplicationRegistration_ExpectedMismatch_RegisteredOlder_PreviousVersionDifferent) {  // NOLINT
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.2"), _T("0.9.6.3"), _T("0.9.6.1"), false));
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.2"), _T("0.9.6.3"), _T("0.9.6.1"), true));
+}
+
+TEST_F(InstallerWrapperUserTest,
+       CheckApplicationRegistration_ExpectedMismatch_RegisteredNewer_PreviousVersionSame_UnrecognizedVersions) {  // NOLINT
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.4.0"), _T("0.9.6.3"), _T("0.9.6.4.0"),
+                false));
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.4.0"), _T("0.9.6.3"), _T("0.9.6.4.0"),
+                true));
+
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.4"), _T("0.9.6.3.0"), _T("0.9.6.4"),
+                false));
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.4"), _T("0.9.6.3.0"), _T("0.9.6.4"),
+                true));
+
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.4.0"), _T("0.9.6.3.0"), _T("0.9.6.4.0"),
+                false));
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6.4.0"), _T("0.9.6.3.0"), _T("0.9.6.4.0"),
+                true));
+
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6"), _T("0.9.5"), _T("0.9.6"), false));
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_VERSION_MISMATCH,
+            im_->CheckApplicationRegistration(
+                kAppGuid, _T("0.9.6"), _T("0.9.5"), _T("0.9.6"), true));
+}
+
+//
+// Negative Tests
+//
+
+TEST_F(InstallerWrapperUserTest, InstallApp_InstallerWithoutFilenameExtension) {
+  EXPECT_EQ(GOOPDATEINSTALL_E_FILENAME_INVALID,
+            im_->InstallApp(NULL,
+                            kAppGuid,
+                            _T("c:\\temp\\foo"),
+                            _T(""),  // Arguments.
+                            _T(""),  // Installer data.
+                            kLanguageEnglish,
+                            &result_info_));
+
+  EXPECT_EQ(INSTALLER_RESULT_UNKNOWN, result_info_.type);
+  EXPECT_EQ(S_OK, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+}
+
+TEST_F(InstallerWrapperUserTest,
+       InstallApp_UnsupportedInstallerFilenameExtension) {
+  EXPECT_EQ(GOOPDATEINSTALL_E_FILENAME_INVALID,
+            im_->InstallApp(NULL,
+                            kAppGuid,
+                            _T("c:\\temp\\foo.bar"),
+                            _T(""),  // Arguments.
+                            _T(""),  // Installer data.
+                            kLanguageEnglish,
+                            &result_info_));
+
+  EXPECT_EQ(INSTALLER_RESULT_UNKNOWN, result_info_.type);
+  EXPECT_EQ(S_OK, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+}
+
+TEST_F(InstallerWrapperUserTest, InstallApp_InstallerEmtpyFilename) {
+  EXPECT_EQ(GOOPDATEINSTALL_E_FILENAME_INVALID,
+            im_->InstallApp(NULL,
+                            kAppGuid,
+                            _T(""),
+                            _T(""),  // Arguments.
+                            _T(""),  // Installer data.
+                            kLanguageEnglish,
+                            &result_info_));
+
+  EXPECT_EQ(INSTALLER_RESULT_UNKNOWN, result_info_.type);
+  EXPECT_EQ(S_OK, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+}
+
+TEST_F(InstallerWrapperUserTest, InstallApp_ExeFileDoesNotExist) {
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED_START,
+            im_->InstallApp(NULL,
+                            kAppGuid,
+                            _T("c:\\temp\\foo.exe"),
+                            _T(""),  // Arguments.
+                            _T(""),  // Installer data.
+                            kLanguageEnglish,
+                            &result_info_));
+
+  EXPECT_EQ(INSTALLER_RESULT_UNKNOWN, result_info_.type);
+  EXPECT_EQ(S_OK, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  EXPECT_FALSE(RegKey::HasKey(kFullAppClientsKeyPath));
+  EXPECT_FALSE(RegKey::HasKey(kFullAppClientStateKeyPath));
+}
+
+//
+// EXE Installer Tests
+//
+
+// This test uses cmd.exe as an installer that leaves the payload
+// kPayloadFileName.
+TEST_F(InstallerWrapperUserTest, InstallApp_ExeInstallerWithArgumentsSucceeds) {
+  const TCHAR kPayloadFileName[] = _T("exe_payload.txt");
+  const TCHAR kCommandToExecute[] = _T("echo \"hi\" > %s");
+
+  CString full_command_to_execute;
+  full_command_to_execute.Format(kCommandToExecute, kPayloadFileName);
+  CString arguments;
+  arguments.Format(kExecuteCommandAndTerminateSwitch, full_command_to_execute);
+
+  EXPECT_SUCCEEDED(File::Remove(kPayloadFileName));
+  EXPECT_FALSE(File::Exists(kPayloadFileName));
+
+  // Create the Clients key since this isn't an actual installer.
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kFullAppClientsKeyPath));
+  EXPECT_TRUE(RegKey::HasKey(kFullAppClientsKeyPath));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kFullAppClientsKeyPath,
+                                    kRegValueProductVersion,
+                                    _T("0.10.69.5")));
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  EXPECT_SUCCEEDED(im_->InstallApp(NULL,
+                                   kAppGuid,
+                                   cmd_exe_path_,
+                                   arguments,
+                                   _T(""),  // Installer data.
+                                   kLanguageEnglish,
+                                   &result_info_));
+
+  EXPECT_EQ(INSTALLER_RESULT_SUCCESS, result_info_.type);
+  EXPECT_EQ(S_OK, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  // EXPECT_SUCCEEDED(
+  //     im_->CheckApplicationRegistration(kAppGuid, _T("0.9.70.1"), false));
+
+  EXPECT_TRUE(File::Exists(kPayloadFileName));
+  EXPECT_SUCCEEDED(File::Remove(kPayloadFileName));
+}
+
+TEST_F(InstallerWrapperUserTest,
+       InstallApp_ExeInstallerReturnsNonZeroExitCode) {
+  const TCHAR kCommandToExecute[] = _T("exit 1");
+
+  CString arguments;
+  arguments.Format(kExecuteCommandAndTerminateSwitch, kCommandToExecute);
+
+  // Create the Clients key since this isn't an actual installer.
+  ASSERT_SUCCEEDED(RegKey::CreateKey(kFullAppClientsKeyPath));
+  ASSERT_TRUE(RegKey::HasKey(kFullAppClientsKeyPath));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kFullAppClientsKeyPath,
+                                    kRegValueProductVersion,
+                                    _T("0.10.69.5")));
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED,
+            im_->InstallApp(NULL,
+                            kAppGuid,
+                            cmd_exe_path_,
+                            arguments,
+                            _T(""),  // Installer data.
+                            kLanguageEnglish,
+                            &result_info_));
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_OTHER, result_info_.type);
+  EXPECT_EQ(1, result_info_.code);
+  EXPECT_STREQ(_T("The installer encountered error 1."), result_info_.text);
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+}
+
+/* TODO(omaha): Figure out a way to perform this test.
+   ClearInstallerResultApiValues clears the result values it sets.
+   TODO(omaha): Add another test that reports an error in using registry API.
+// Also tests that the launch cmd is set.
+TEST_F(InstallerWrapperUserTest,
+       InstallApp_ExeInstallerReturnsNonZeroExitCode_InstallerResultSuccess) {
+  const TCHAR kCommandToExecute[] = _T("exit 1");
+
+  CString arguments;
+  arguments.Format(kExecuteCommandAndTerminateSwitch, kCommandToExecute);
+
+  // Create the Clients key since this isn't an actual installer.
+  ASSERT_SUCCEEDED(RegKey::CreateKey(kFullAppClientsKeyPath));
+  ASSERT_TRUE(RegKey::HasKey(kFullAppClientsKeyPath));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kFullAppClientsKeyPath,
+                                    kRegValueProductVersion,
+                                    _T("0.10.69.5")));
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultSuccess,
+                               false, 0,
+                               false, _T(""),
+                               true, kLaunchCmdLine);
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  EXPECT_SUCCEEDED(im_->InstallApp(kAppGuid,
+                                   cmd_exe_path_,
+                                   arguments,
+                                   _T(""),  // Installer data.
+                                   &result_info_));
+
+  EXPECT_EQ(INSTALLER_RESULT_SUCCESS, result_info_.type);
+  EXPECT_EQ(1, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_STREQ(kLaunchCmdLine, result_info_.post_install_launch_command_line);
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_LAUNCH_COMMAND,
+            result_info_.post_install_action);
+
+  EXPECT_SUCCEEDED(
+      im_->CheckApplicationRegistration(kAppGuid, _T("0.9.70.1"), false));
+
+  VerifyLastRegistryValues(kAppId,
+                           true, kResultSuccess,
+                           false, 0,
+                           false, _T(""),
+                           true, kLaunchCmdLine);
+}
+*/
+
+TEST_F(InstallerWrapperMachineTest, InstallApp_MsiInstallerSucceeds) {
+  if (!vista_util::IsUserAdmin()) {
+    std::wcout << _T("\tTest did not run because the user is not an admin.")
+               << std::endl;
+    return;
+  }
+  // We can't fake the registry keys because we are interacting with a real
+  // installer.
+  RestoreRegistryHives();
+
+  AdjustMsiTries(im_.get());
+
+  CString installer_full_path(
+      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                      kSetupFooV1RelativeLocation));
+  ASSERT_TRUE(File::Exists(installer_full_path));
+
+  CString installer_log_full_path;
+  installer_log_full_path.Format(kMsiLogFormat, installer_full_path);
+
+  ASSERT_SUCCEEDED(File::Remove(installer_log_full_path));
+  ASSERT_FALSE(File::Exists(installer_log_full_path));
+
+  RegKey::DeleteKey(kFullFooAppClientKeyPath);
+  ASSERT_FALSE(RegKey::HasKey(kFullFooAppClientKeyPath));
+  RegKey::DeleteKey(kFullFooAppClientStateKeyPath);
+  ASSERT_FALSE(RegKey::HasKey(kFullFooAppClientStateKeyPath));
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  EXPECT_SUCCEEDED(im_->InstallApp(NULL,
+                                   StringToGuid(kFooGuid),
+                                   installer_full_path,
+                                   _T(""),  // Arguments.
+                                   _T(""),  // Installer data.
+                                   kLanguageEnglish,
+                                   &result_info_));
+
+  EXPECT_EQ(INSTALLER_RESULT_SUCCESS, result_info_.type);
+  EXPECT_EQ(S_OK, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  EXPECT_TRUE(File::Exists(installer_log_full_path));
+
+  EXPECT_TRUE(RegKey::HasKey(kFullFooAppClientKeyPath));
+  EXPECT_FALSE(RegKey::HasKey(kFullFooAppClientStateKeyPath));
+  // Verify the installer did not write a value that is to be written only in
+  // the presence of an MSI property that was not specified.
+  EXPECT_FALSE(RegKey::HasValue(kFullFooAppClientKeyPath,
+                                kFooInstallerBarValueName));
+  // EXPECT_SUCCEEDED(im_->CheckApplicationRegistration(
+  //                      StringToGuid(kFooGuid), _T("0.9.70.1"), false));
+
+  UninstallTestMsi(installer_full_path);
+
+  EXPECT_FALSE(RegKey::HasKey(kFullFooAppClientKeyPath));
+  EXPECT_SUCCEEDED(RegKey::DeleteKey(kFullFooAppClientKeyPath));
+}
+
+TEST_F(InstallerWrapperMachineTest,
+       InstallApp_MsiInstallerWithArgumentSucceeds) {
+  if (!vista_util::IsUserAdmin()) {
+    std::wcout << _T("\tTest did not run because the user is not an admin.")
+               << std::endl;
+    return;
+  }
+
+  // We can't fake the registry keys because we are interacting with a real
+  // installer.
+  RestoreRegistryHives();
+
+  AdjustMsiTries(im_.get());
+
+  CString installer_full_path(
+      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                      kSetupFooV1RelativeLocation));
+  ASSERT_TRUE(File::Exists(installer_full_path));
+
+  CString installer_log_full_path;
+  installer_log_full_path.Format(kMsiLogFormat, installer_full_path);
+
+  ASSERT_SUCCEEDED(File::Remove(installer_log_full_path));
+  ASSERT_FALSE(File::Exists(installer_log_full_path));
+
+  RegKey::DeleteKey(kFullFooAppClientKeyPath);
+  ASSERT_FALSE(RegKey::HasKey(kFullFooAppClientKeyPath));
+  RegKey::DeleteKey(kFullFooAppClientStateKeyPath);
+  ASSERT_FALSE(RegKey::HasKey(kFullFooAppClientStateKeyPath));
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  EXPECT_SUCCEEDED(im_->InstallApp(NULL,
+                                   StringToGuid(kFooGuid),
+                                   installer_full_path,
+                                   kFooInstallerBarPropertyArg,
+                                   _T(""),  // Installer data.
+                                   kLanguageEnglish,
+                                   &result_info_));
+
+  EXPECT_EQ(INSTALLER_RESULT_SUCCESS, result_info_.type);
+  EXPECT_EQ(S_OK, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  EXPECT_TRUE(File::Exists(installer_log_full_path));
+
+  EXPECT_TRUE(RegKey::HasKey(kFullFooAppClientKeyPath));
+  EXPECT_FALSE(RegKey::HasKey(kFullFooAppClientStateKeyPath));
+  EXPECT_TRUE(RegKey::HasValue(kFullFooAppClientKeyPath,
+                               kFooInstallerBarValueName));
+  DWORD barprop_value = 0;
+  EXPECT_SUCCEEDED(RegKey::GetValue(kFullFooAppClientKeyPath,
+                                    kFooInstallerBarValueName,
+                                    &barprop_value));
+  EXPECT_EQ(7, barprop_value);
+
+  // EXPECT_SUCCEEDED(im_->CheckApplicationRegistration(
+  //                      StringToGuid(kFooGuid), _T("0.9.70.1"), false));
+
+  UninstallTestMsi(installer_full_path);
+
+  EXPECT_FALSE(RegKey::HasKey(kFullFooAppClientKeyPath));
+  EXPECT_SUCCEEDED(RegKey::DeleteKey(kFullFooAppClientKeyPath));
+}
+
+// The use of kGoogleUpdateAppId is the key to this test.
+// Note that the version is not changed - this is the normal self-update case.
+TEST_F(InstallerWrapperUserTest, InstallApp_UpdateOmahaSucceeds) {
+  CString arguments;
+  arguments.Format(kExecuteCommandAndTerminateSwitch, _T(""));
+
+  const CString kExistingVersion(_T("0.9.69.5"));
+
+  // Because we don't actually run the Omaha installer, we need to make sure
+  // its Clients key and pv value exist to avoid an error.
+  ASSERT_SUCCEEDED(RegKey::CreateKey(USER_REG_CLIENTS_GOOPDATE));
+  ASSERT_TRUE(RegKey::HasKey(USER_REG_CLIENTS_GOOPDATE));
+  ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENTS_GOOPDATE,
+                                    kRegValueProductVersion,
+                                    kExistingVersion));
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  EXPECT_SUCCEEDED(im_->InstallApp(NULL,
+                                   kGoopdateGuid,
+                                   cmd_exe_path_,
+                                   arguments,
+                                   _T(""),  // Installer data.
+                                   kLanguageEnglish,
+                                   &result_info_));
+
+  EXPECT_EQ(INSTALLER_RESULT_SUCCESS, result_info_.type);
+  EXPECT_EQ(S_OK, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  EXPECT_TRUE(RegKey::HasKey(USER_REG_CLIENTS_GOOPDATE));
+  CString version;
+  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_CLIENTS_GOOPDATE,
+                                    kRegValueProductVersion,
+                                    &version));
+  EXPECT_STREQ(kExistingVersion, version);
+
+  // Do not call CheckApplicationRegistration for Omaha.
+}
+
+// The main purpose of this test is to ensure that self-updates don't fail if
+// Omaha's Clients key doesn't exist for some reason.
+TEST_F(InstallerWrapperUserTest,
+       InstallApp_UpdateOmahaSucceedsWhenClientsKeyAbsent) {
+  CString arguments;
+  arguments.Format(kExecuteCommandAndTerminateSwitch, _T(""));
+
+  const CString kExistingVersion(_T("0.9.69.5"));
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  EXPECT_SUCCEEDED(im_->InstallApp(NULL,
+                                   kGoopdateGuid,
+                                   cmd_exe_path_,
+                                   arguments,
+                                   _T(""),  // Installer data.
+                                   kLanguageEnglish,
+                                   &result_info_));
+
+  EXPECT_EQ(INSTALLER_RESULT_SUCCESS, result_info_.type);
+  EXPECT_EQ(S_OK, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  EXPECT_FALSE(RegKey::HasKey(USER_REG_CLIENTS_GOOPDATE));
+
+  // Do not call CheckApplicationRegistration for Omaha.
+}
+
+TEST_F(InstallerWrapperUserTest, InstallApp_InstallerDoesNotWriteClientsKey) {
+  CString arguments;
+  arguments.Format(kExecuteCommandAndTerminateSwitch, _T(""));
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  EXPECT_SUCCEEDED(im_->InstallApp(NULL,
+                                   kAppGuid,
+                                   cmd_exe_path_,
+                                   arguments,
+                                   _T(""),  // Installer data.
+                                   kLanguageEnglish,
+                                   &result_info_));
+
+  EXPECT_FALSE(RegKey::HasKey(kFullAppClientsKeyPath));
+  EXPECT_FALSE(RegKey::HasKey(kFullAppClientStateKeyPath));
+
+  EXPECT_EQ(INSTALLER_RESULT_SUCCESS, result_info_.type);
+  EXPECT_EQ(S_OK, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  // EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENTS_KEY,
+  //           im_->CheckApplicationRegistration(kAppGuid, _T(""), true));
+}
+
+TEST_F(InstallerWrapperUserTest,
+       InstallApp_InstallerFailureMsiFileDoesNotExist) {
+  CPath msi_path(app_util::GetTempDir());
+  msi_path.Append(_T("foo.msi"));
+  const CString log_path = msi_path + _T(".log");
+
+  AdjustMsiTries(im_.get());
+
+  ASSERT_SUCCEEDED(File::Remove(log_path));
+  ASSERT_FALSE(File::Exists(log_path));
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED,
+            im_->InstallApp(NULL,
+                            kAppGuid,
+                            msi_path,
+                            _T(""),  // Arguments.
+                            _T(""),  // Installer data.
+                            kLanguageEnglish,
+                            &result_info_));
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_MSI, result_info_.type);
+  EXPECT_EQ(ERROR_INSTALL_PACKAGE_OPEN_FAILED, result_info_.code);
+  EXPECT_STREQ(kError1619MessagePrefix,
+               result_info_.text.Left(kError1619MessagePrefixLength));
+  VerifyStringIsMsiPackageOpenFailedString(
+      result_info_.text.Mid(kError1619MessagePrefixLength));
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  // msiexec creates an empty log file.
+  EXPECT_TRUE(File::Exists(log_path));
+  EXPECT_SUCCEEDED(File::Remove(log_path));
+
+  EXPECT_FALSE(RegKey::HasKey(kFullAppClientsKeyPath));
+  EXPECT_FALSE(RegKey::HasKey(kFullAppClientStateKeyPath));
+}
+
+// Simulates the MSI busy error by having an exe installer return the error
+// as its exit code and specifying MSI error using Installer Result API.
+// Assumes reg.exe is in the path.
+TEST_F(InstallerWrapperUserTest, InstallApp_MsiIsBusy_NoRetries) {
+  CString commands;
+  commands.Format(kExecuteTwoCommandsFormat,
+                  set_installer_result_type_msi_error_cmd_,
+                  kMsiInstallerBusyExitCodeCmd);
+
+  CString arguments;
+  arguments.Format(kExecuteCommandAndTerminateSwitch, commands);
+
+  LowResTimer install_timer(true);
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  EXPECT_EQ(GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING,
+            im_->InstallApp(NULL,
+                            kAppGuid,
+                            cmd_exe_path_,
+                            arguments,
+                            _T(""),  // Installer data.
+                            kLanguageEnglish,
+                            &result_info_));
+
+  EXPECT_GT(2, install_timer.GetSeconds());  // Check Omaha did not retry.
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_MSI, result_info_.type);
+  EXPECT_EQ(ERROR_INSTALL_ALREADY_RUNNING, result_info_.code);
+  EXPECT_STREQ(kError1618MessagePrefix,
+               result_info_.text.Left(kError1618MessagePrefixLength));
+  VerifyStringIsMsiBusyString(
+      result_info_.text.Mid(kError1618MessagePrefixLength));
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+}
+
+// This test takes at least 5 seconds, so it is not run all the time.
+TEST_F(InstallerWrapperUserTest, InstallApp_MsiIsBusy_TwoTries) {
+  if (!ShouldRunLargeTest()) {
+    return;
+  }
+
+  CString commands;
+  commands.Format(kExecuteTwoCommandsFormat,
+                  set_installer_result_type_msi_error_cmd_,
+                  kMsiInstallerBusyExitCodeCmd);
+
+  CString arguments;
+  arguments.Format(kExecuteCommandAndTerminateSwitch, commands);
+
+  im_->set_num_tries_when_msi_busy(2);
+
+  LowResTimer install_timer(true);
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+  EXPECT_EQ(GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING,
+            im_->InstallApp(NULL,
+                            kAppGuid,
+                            cmd_exe_path_,
+                            arguments,
+                            _T(""),  // Installer data.
+                            kLanguageEnglish,
+                            &result_info_));
+
+  EXPECT_LE(5, install_timer.GetSeconds());  // Check Omaha did retry.
+  EXPECT_GT(10, install_timer.GetSeconds());
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_MSI, result_info_.type);
+  EXPECT_EQ(ERROR_INSTALL_ALREADY_RUNNING, result_info_.code);
+  EXPECT_STREQ(kError1618MessagePrefix,
+               result_info_.text.Left(kError1618MessagePrefixLength));
+  VerifyStringIsMsiBusyString(
+      result_info_.text.Mid(kError1618MessagePrefixLength));
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+}
+
+// This test uses cmd.exe as an installer that leaves the payload files.
+TEST_F(InstallerWrapperUserTest, InstallApp_InstallMultipleApps) {
+  const TCHAR kPayloadFileName1[] = _T("exe_payload1.txt");
+  const TCHAR kPayloadFileName2[] = _T("exe_payload2.txt");
+  const TCHAR kCommandToExecute[] = _T("echo \"hi\" > %s");
+
+  CString full_command_to_execute;
+  full_command_to_execute.Format(kCommandToExecute, kPayloadFileName1);
+  CString arguments1;
+  arguments1.Format(kExecuteCommandAndTerminateSwitch, full_command_to_execute);
+
+  full_command_to_execute.Format(kCommandToExecute, kPayloadFileName2);
+  CString arguments2;
+  arguments2.Format(kExecuteCommandAndTerminateSwitch, full_command_to_execute);
+
+  EXPECT_SUCCEEDED(File::Remove(kPayloadFileName1));
+  EXPECT_FALSE(File::Exists(kPayloadFileName1));
+  EXPECT_SUCCEEDED(File::Remove(kPayloadFileName2));
+  EXPECT_FALSE(File::Exists(kPayloadFileName2));
+
+  // Create the Clients key since this isn't an actual installer.
+  EXPECT_SUCCEEDED(RegKey::CreateKey(kFullAppClientsKeyPath));
+  EXPECT_TRUE(RegKey::HasKey(kFullAppClientsKeyPath));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kFullAppClientsKeyPath,
+                                    kRegValueProductVersion,
+                                    _T("0.10.69.5")));
+
+  __mutexScope(AppManager::Instance()->GetRegistryStableStateLock());
+
+  EXPECT_SUCCEEDED(im_->InstallApp(NULL,
+                                   kAppGuid,
+                                   cmd_exe_path_,
+                                   arguments1,
+                                   _T(""),  // Installer data.
+                                   kLanguageEnglish,
+                                   &result_info_));
+
+  EXPECT_EQ(INSTALLER_RESULT_SUCCESS, result_info_.type);
+  EXPECT_EQ(S_OK, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  // EXPECT_SUCCEEDED(
+  //     im_->CheckApplicationRegistration(kAppGuid, _T("0.9.70.1"), false));
+
+  EXPECT_TRUE(File::Exists(kPayloadFileName1));
+  EXPECT_SUCCEEDED(File::Remove(kPayloadFileName1));
+
+  // Run the second installer.
+
+  result_info_.type = INSTALLER_RESULT_UNKNOWN;
+  result_info_.code = kInitialErrorValue;
+
+  EXPECT_SUCCEEDED(im_->InstallApp(NULL,
+                                   kAppGuid,
+                                   cmd_exe_path_,
+                                   arguments2,
+                                   _T(""),  // Installer data.
+                                   kLanguageEnglish,
+                                   &result_info_));
+
+  EXPECT_EQ(INSTALLER_RESULT_SUCCESS, result_info_.type);
+  EXPECT_EQ(S_OK, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  // EXPECT_SUCCEEDED(
+  //     im_->CheckApplicationRegistration(kAppGuid, _T("0.9.70.1"), false));
+
+  EXPECT_TRUE(File::Exists(kPayloadFileName2));
+  EXPECT_SUCCEEDED(File::Remove(kPayloadFileName2));
+}
+
+//
+// GetInstallerResultHelper tests
+//
+
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_NoRegistry_MSI_ZeroExitCode) {
+  CallGetInstallerResultHelper(kAppGuid, kMsi, 0, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_SUCCESS, result_info_.type);
+  EXPECT_EQ(0, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  VerifyNoLastRegistryValues(kAppId);
+}
+
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_NoRegistry_MSI_NonZeroExitCode) {
+  CallGetInstallerResultHelper(kAppGuid, kMsi, 1603, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_MSI, result_info_.type);
+  EXPECT_EQ(1603, result_info_.code);
+  EXPECT_STREQ(kError1603Text, result_info_.text);
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  VerifyNoLastRegistryValues(kAppId);
+}
+
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_NoRegistry_EXE_ZeroExitCode) {
+  CallGetInstallerResultHelper(kAppGuid, kOther, 0, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_SUCCESS, result_info_.type);
+  EXPECT_EQ(0, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  VerifyNoLastRegistryValues(kAppId);
+}
+
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_NoRegistry_EXE_NonZeroExitCode_SmallNumber) {
+  CallGetInstallerResultHelper(kAppGuid, kOther, 8, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_OTHER, result_info_.type);
+  EXPECT_EQ(8, result_info_.code);
+  EXPECT_STREQ(_T("The installer encountered error 8."), result_info_.text);
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  VerifyNoLastRegistryValues(kAppId);
+}
+
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_NoRegistry_EXE_NonZeroExitCode_HRESULTFailure) {
+  CallGetInstallerResultHelper(kAppGuid, kOther, 0x80004005, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_OTHER, result_info_.type);
+  EXPECT_EQ(0x80004005, result_info_.code);
+  EXPECT_STREQ(_T("The installer encountered error 0x80004005."),
+               result_info_.text);
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  VerifyNoLastRegistryValues(kAppId);
+}
+
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_ExitCode_MSI) {
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultExitCode,
+                               false, 0,
+                               false, 0,
+                               false, _T(""),
+                               false, _T(""));
+
+  CallGetInstallerResultHelper(kAppGuid, kMsi, 1603, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_MSI, result_info_.type);
+  EXPECT_EQ(1603, result_info_.code);
+  EXPECT_STREQ(kError1603Text, result_info_.text);
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  VerifyLastRegistryValues(kAppId,
+                           true, kResultExitCode,
+                           false, 0,
+                           false, 0,
+                           false, _T(""),
+                           false, _T(""));
+}
+
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_NoRegistry_MSI_RebootRequired) {
+  CallGetInstallerResultHelper(kAppGuid, kMsi, ERROR_SUCCESS_REBOOT_REQUIRED,
+                               &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_SUCCESS, result_info_.type);
+  EXPECT_EQ(0, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_REBOOT, result_info_.post_install_action);
+
+  VerifyNoLastRegistryValues(kAppId);
+}
+
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_SystemError_EXE_RebootRequired) {
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultFailedSystemError,
+                               true, ERROR_SUCCESS_REBOOT_REQUIRED,
+                               false, 0,
+                               false, _T(""),
+                               true, kLaunchCmdLine);
+
+  CallGetInstallerResultHelper(kAppGuid, kOther, 1, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_SUCCESS, result_info_.type);
+  EXPECT_EQ(0, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_REBOOT, result_info_.post_install_action);
+
+  VerifyLastRegistryValues(kAppId,
+                           true, kResultFailedSystemError,
+                           true, ERROR_SUCCESS_REBOOT_REQUIRED,
+                           false, 0,
+                           false, _T(""),
+                           true, kLaunchCmdLine);
+}
+
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_CustomError_ShouldNotReboot) {
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultFailedCustomError,
+                               true, ERROR_SUCCESS_REBOOT_REQUIRED,
+                               false, 0,
+                               false, _T(""),
+                               false, _T(""));
+
+  CallGetInstallerResultHelper(kAppGuid, kMsi, 0, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_OTHER, result_info_.type);
+  EXPECT_EQ(ERROR_SUCCESS_REBOOT_REQUIRED, result_info_.code);
+  EXPECT_STREQ(_T("The installer encountered error 3010."), result_info_.text);
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+}
+
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_Success_NoErrorCode) {
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultSuccess,
+                               false, 0,
+                               false, 0,
+                               false, _T(""),
+                               false, _T(""));
+
+  CallGetInstallerResultHelper(kAppGuid, kOther, 99, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_SUCCESS, result_info_.type);
+  EXPECT_EQ(99, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  VerifyLastRegistryValues(kAppId,
+                           true, kResultSuccess,
+                           false, 0,
+                           false, 0,
+                           false, _T(""),
+                           false, _T(""));
+}
+
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_Success_AllValues) {
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultSuccess,
+                               true, 555,
+                               false, 0,
+                               true, _T("an ignored error"),
+                               true, kLaunchCmdLine);
+
+  CallGetInstallerResultHelper(kAppGuid, kOther, 99, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_SUCCESS, result_info_.type);
+  EXPECT_EQ(555, result_info_.code) <<
+      _T("InstallerError overwrites exit code.");
+  EXPECT_FALSE(result_info_.text.IsEmpty()) <<
+      _T("UIString is ignored for Success.");
+  EXPECT_STREQ(kLaunchCmdLine, result_info_.post_install_launch_command_line);
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_LAUNCH_COMMAND,
+            result_info_.post_install_action);
+
+  VerifyLastRegistryValues(kAppId,
+                           true, kResultSuccess,
+                           true, 555,
+                           false, 0,
+                           true, _T("an ignored error"),
+                           true, kLaunchCmdLine);
+}
+
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_LaunchCmdOnly_MSI_ZeroExitCode) {
+  SetupInstallerResultRegistry(kAppId,
+                               false, 0,
+                               false, 0,
+                               false, 0,
+                               false, _T(""),
+                               true, kLaunchCmdLine);
+
+  CallGetInstallerResultHelper(kAppGuid, kMsi, 0, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_SUCCESS, result_info_.type);
+  EXPECT_EQ(0, result_info_.code);
+  EXPECT_TRUE(result_info_.text.IsEmpty());
+  EXPECT_STREQ(kLaunchCmdLine, result_info_.post_install_launch_command_line);
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_LAUNCH_COMMAND,
+            result_info_.post_install_action);
+
+  VerifyLastRegistryValues(kAppId,
+                           false, 0,
+                           false, 0,
+                           false, 0,
+                           false, _T(""),
+                           true, kLaunchCmdLine);
+}
+
+// Exit code is used when no error code is present. It's interpreted as a system
+// error even though the installer is not an MSI.
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_Failed_NoErrorCodeOrUiString) {
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultFailedCustomError,
+                               false, 0,
+                               false, 0,
+                               false, _T(""),
+                               false, _T(""));
+
+  CallGetInstallerResultHelper(kAppGuid, kOther, 8, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_OTHER, result_info_.type);
+  EXPECT_EQ(8, result_info_.code);
+  EXPECT_STREQ(_T("The installer encountered error 8."), result_info_.text);
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  VerifyLastRegistryValues(kAppId,
+                           true, kResultFailedCustomError,
+                           false, 0,
+                           false, 0,
+                           false, _T(""),
+                           false, _T(""));
+}
+
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_Failed_WithErrorCode) {
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultFailedCustomError,
+                               true, 8,
+                               false, 0,
+                               false, _T(""),
+                               false, _T(""));
+
+  CallGetInstallerResultHelper(kAppGuid, kOther, 1618, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_OTHER, result_info_.type);
+  EXPECT_EQ(8, result_info_.code);
+  EXPECT_STREQ(_T("The installer encountered error 8."), result_info_.text);
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  VerifyLastRegistryValues(kAppId,
+                           true, kResultFailedCustomError,
+                           true, 8,
+                           false, 0,
+                           false, _T(""),
+                           false, _T(""));
+}
+
+// This test shows that command line is read and
+// POST_INSTALL_ACTION_LAUNCH_COMMAND is set.
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_Failed_AllValues) {
+  const DWORD kInstallerErrorValue = 8;
+  const TCHAR* const kUiString = _T("a message from the installer");
+
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultFailedCustomError,
+                               true, kInstallerErrorValue,
+                               false, 0,
+                               true, kUiString,
+                               true, kLaunchCmdLine);
+
+  CallGetInstallerResultHelper(kAppGuid, kOther, 1618, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_OTHER, result_info_.type);
+  EXPECT_EQ(kInstallerErrorValue, result_info_.code);
+  EXPECT_STREQ(kUiString, result_info_.text);
+  EXPECT_STREQ(kLaunchCmdLine, result_info_.post_install_launch_command_line);
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_LAUNCH_COMMAND,
+            result_info_.post_install_action);
+
+  VerifyLastRegistryValues(kAppId,
+                           true, kResultFailedCustomError,
+                           true, kInstallerErrorValue,
+                           false, 0,
+                           true, kUiString,
+                           true, kLaunchCmdLine);
+}
+
+// Exit code is used and interpreted as MSI error when no error code present.
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_FailedMsiError_NoErrorCode) {
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultFailedMsiError,
+                               false, 0,
+                               false, 0,
+                               false, _T(""),
+                               false, _T(""));
+
+  CallGetInstallerResultHelper(kAppGuid, kOther, 1603, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_MSI, result_info_.type);
+  EXPECT_EQ(1603, result_info_.code);
+  EXPECT_STREQ(kError1603Text, result_info_.text);
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  VerifyLastRegistryValues(kAppId,
+                           true, kResultFailedMsiError,
+                           false, 0,
+                           false, 0,
+                           false, _T(""),
+                           false, _T(""));
+}
+
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_FailedMsiError_WithErrorCode) {
+  const DWORD kInstallerErrorValue = 1603;
+
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultFailedMsiError,
+                               true, kInstallerErrorValue,
+                               false, 0,
+                               false, _T(""),
+                               false, _T(""));
+
+  CallGetInstallerResultHelper(kAppGuid, kOther, 1618, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_MSI, result_info_.type);
+  EXPECT_EQ(kInstallerErrorValue, result_info_.code);
+  EXPECT_STREQ(kError1603Text, result_info_.text);
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  VerifyLastRegistryValues(kAppId,
+                           true, kResultFailedMsiError,
+                           true, kInstallerErrorValue,
+                           false, 0,
+                           false, _T(""),
+                           false, _T(""));
+}
+
+// This test shows that command line is read and
+// POST_INSTALL_ACTION_LAUNCH_COMMAND is set.
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_FailedMsiError_AllValues) {
+  const DWORD kInstallerErrorValue = 1603;
+
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultFailedMsiError,
+                               true, kInstallerErrorValue,
+                               false, 0,
+                               true, _T("an ignored error"),
+                               true, kLaunchCmdLine);
+
+  CallGetInstallerResultHelper(kAppGuid, kOther, 1618, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_MSI, result_info_.type);
+  EXPECT_EQ(kInstallerErrorValue, result_info_.code);
+  EXPECT_STREQ(kError1603Text, result_info_.text) << _T("UIString is ignored.");
+  EXPECT_STREQ(kLaunchCmdLine, result_info_.post_install_launch_command_line);
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_LAUNCH_COMMAND,
+            result_info_.post_install_action);
+
+  VerifyLastRegistryValues(kAppId,
+                           true, kResultFailedMsiError,
+                           true, kInstallerErrorValue,
+                           false, 0,
+                           true, _T("an ignored error"),
+                           true, kLaunchCmdLine);
+}
+
+// Exit code is used and interpreted as system error when no error code present.
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_FailedSystemError_NoErrorCode) {
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultFailedSystemError,
+                               false, 0,
+                               false, 0,
+                               false, _T(""),
+                               false, _T(""));
+
+  CallGetInstallerResultHelper(kAppGuid, kOther, 1603, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_SYSTEM, result_info_.type);
+  EXPECT_EQ(1603, result_info_.code);
+  EXPECT_STREQ(kError1603Text, result_info_.text);
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  VerifyLastRegistryValues(kAppId,
+                           true, kResultFailedSystemError,
+                           false, 0,
+                           false, 0,
+                           false, _T(""),
+                           false, _T(""));
+}
+
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_FailedSystemError_WithErrorCode) {
+  const DWORD kInstallerErrorValue = 1603;
+
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultFailedSystemError,
+                               true, kInstallerErrorValue,
+                               false, 0,
+                               false, _T(""),
+                               false, _T(""));
+
+  CallGetInstallerResultHelper(kAppGuid, kOther, 1618, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_SYSTEM, result_info_.type);
+  EXPECT_EQ(kInstallerErrorValue, result_info_.code);
+  EXPECT_STREQ(kError1603Text, result_info_.text);
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  VerifyLastRegistryValues(kAppId,
+                           true, kResultFailedSystemError,
+                           true, kInstallerErrorValue,
+                           false, 0,
+                           false, _T(""),
+                           false, _T(""));
+}
+
+// INSTALLER_RESULT_FAILED_SYSTEM_ERROR supports values beyond the basic
+// "System Error Codes" and their HRESULT equivalents.
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_FailedSystemError_WithHRESULTSystemError) {
+  const DWORD kInstallerErrorValue = 0x800B010F;
+
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultFailedSystemError,
+                               true, kInstallerErrorValue,
+                               false, 0,
+                               false, _T(""),
+                               false, _T(""));
+
+  CallGetInstallerResultHelper(kAppGuid, kOther, 1618, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_SYSTEM, result_info_.type);
+  EXPECT_EQ(kInstallerErrorValue, result_info_.code);
+  EXPECT_STREQ(kError0x800B010FText, result_info_.text);
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  VerifyLastRegistryValues(kAppId,
+                           true, kResultFailedSystemError,
+                           true, kInstallerErrorValue,
+                           false, 0,
+                           false, _T(""),
+                           false, _T(""));
+}
+
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_FailedSystemError_WithUnrecognizedError) {
+  const DWORD kInstallerErrorValue = 0x80040200;
+
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultFailedSystemError,
+                               true, kInstallerErrorValue,
+                               false, 0,
+                               false, _T(""),
+                               false, _T(""));
+
+  CallGetInstallerResultHelper(kAppGuid, kOther, 1618, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_SYSTEM, result_info_.type);
+  EXPECT_EQ(kInstallerErrorValue, result_info_.code);
+  EXPECT_STREQ(_T("The installer encountered error 0x80040200."),
+               result_info_.text);
+  EXPECT_TRUE(result_info_.post_install_launch_command_line.IsEmpty());
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_DEFAULT, result_info_.post_install_action);
+
+  VerifyLastRegistryValues(kAppId,
+                           true, kResultFailedSystemError,
+                           true, kInstallerErrorValue,
+                           false, 0,
+                           false, _T(""),
+                           false, _T(""));
+}
+
+// This test shows that command line is read and
+// POST_INSTALL_ACTION_LAUNCH_COMMAND is set.
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_FailedSystemError_AllValues) {
+  const DWORD kInstallerErrorValue = 1603;
+
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultFailedSystemError,
+                               true, kInstallerErrorValue,
+                               false, 0,
+                               true, _T("an ignored error"),
+                               true, kLaunchCmdLine);
+
+  CallGetInstallerResultHelper(kAppGuid, kOther, 1618, &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_SYSTEM, result_info_.type);
+  EXPECT_EQ(kInstallerErrorValue, result_info_.code);
+  EXPECT_STREQ(kError1603Text, result_info_.text) << _T("UIString is ignored.");
+  EXPECT_STREQ(kLaunchCmdLine, result_info_.post_install_launch_command_line);
+  EXPECT_TRUE(result_info_.post_install_url.IsEmpty());
+  EXPECT_EQ(POST_INSTALL_ACTION_LAUNCH_COMMAND,
+            result_info_.post_install_action);
+
+  VerifyLastRegistryValues(kAppId,
+                           true, kResultFailedSystemError,
+                           true, kInstallerErrorValue,
+                           false, 0,
+                           true, _T("an ignored error"),
+                           true, kLaunchCmdLine);
+}
+
+TEST_F(InstallerWrapperUserGetInstallerResultHelperTest,
+       GetInstallerResultHelper_ExtraCode1) {
+  const DWORD kInstallerErrorValue = 10;
+  const DWORD kInstallerExtraCode1 = 0xabcd;
+
+  SetupInstallerResultRegistry(kAppId,
+                               true, kResultFailedCustomError,
+                               true, kInstallerErrorValue,
+                               true, kInstallerExtraCode1,
+                               false, _T(""),
+                               false, _T(""));
+
+  CallGetInstallerResultHelper(kAppGuid,
+                               kOther,
+                               kInstallerErrorValue,
+                               &result_info_);
+
+  EXPECT_EQ(INSTALLER_RESULT_ERROR_OTHER, result_info_.type);
+  EXPECT_EQ(kInstallerErrorValue, result_info_.code);
+  EXPECT_EQ(kInstallerExtraCode1, result_info_.extra_code1);
+
+  VerifyLastRegistryValues(kAppId,
+                           true, kResultFailedCustomError,
+                           true, kInstallerErrorValue,
+                           true, kInstallerExtraCode1,
+                           false, _T(""),
+                           false, _T(""));
+}
+
+// TODO(omaha): Add a machine test.
+
+}  // namespace omaha
diff --git a/goopdate/job_observer.cc b/goopdate/job_observer.cc
new file mode 100644
index 0000000..b5ebd06
--- /dev/null
+++ b/goopdate/job_observer.cc
@@ -0,0 +1,199 @@
+// Copyright 2008-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/goopdate/job_observer.h"
+
+namespace omaha {
+
+JobObserverCOMDecorator::JobObserverCOMDecorator()
+    : job_observer_(NULL),
+      on_demand_events_(NULL),
+      worker_job_thread_id_(::GetCurrentThreadId()) {
+}
+
+JobObserverCOMDecorator::~JobObserverCOMDecorator() {
+  Uninitialize();
+}
+
+void JobObserverCOMDecorator::Initialize(IJobObserver* job_observer) {
+  ASSERT1(job_observer);
+
+  job_observer_ = job_observer;
+  job_observer->SetEventSink(this);
+}
+
+void JobObserverCOMDecorator::Uninitialize() {
+}
+
+void JobObserverCOMDecorator::OnCheckingForUpdate() {
+  ASSERT1(job_observer_);
+  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
+
+  job_observer_->OnCheckingForUpdate();
+}
+
+void JobObserverCOMDecorator::OnUpdateAvailable(const CString& app_name,
+                                                const CString& version_string) {
+  UNREFERENCED_PARAMETER(app_name);
+  ASSERT1(job_observer_);
+  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
+
+  job_observer_->OnUpdateAvailable(version_string);
+}
+
+void JobObserverCOMDecorator::OnWaitingToDownload(const CString& app_name) {
+  UNREFERENCED_PARAMETER(app_name);
+  ASSERT1(job_observer_);
+  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
+
+  job_observer_->OnWaitingToDownload();
+}
+
+void JobObserverCOMDecorator::OnDownloading(const CString& app_name,
+                                            int time_remaining_ms,
+                                            int pos) {
+  UNREFERENCED_PARAMETER(app_name);
+  ASSERT1(job_observer_);
+  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
+
+  job_observer_->OnDownloading(time_remaining_ms, pos);
+}
+
+void JobObserverCOMDecorator::OnWaitingRetryDownload(const CString& app_name,
+                                                     time64 next_retry_time) {
+  UNREFERENCED_PARAMETER(app_name);
+  UNREFERENCED_PARAMETER(next_retry_time);
+}
+
+void JobObserverCOMDecorator::OnWaitingToInstall(const CString& app_name,
+                                                 bool* can_start_install) {
+  UNREFERENCED_PARAMETER(app_name);
+  ASSERT1(job_observer_);
+  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
+
+  job_observer_->OnWaitingToInstall();
+  *can_start_install = true;
+}
+
+void JobObserverCOMDecorator::OnInstalling(const CString& app_name) {
+  UNREFERENCED_PARAMETER(app_name);
+  ASSERT1(job_observer_);
+  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
+
+  job_observer_->OnInstalling();
+}
+
+void JobObserverCOMDecorator::OnPause() {
+  ASSERT1(job_observer_);
+  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
+
+  job_observer_->OnPause();
+}
+
+void JobObserverCOMDecorator::OnComplete(const ObserverCompletionInfo& info) {
+  if (job_observer_) {
+    ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
+
+    LegacyCompletionCodes completion_code =
+        static_cast<LegacyCompletionCodes>(info.completion_code);
+
+    // We do not want to send Omaha strings to the application, so pass an empty
+    // string instead.
+    job_observer_->OnComplete(completion_code, L"");
+    job_observer_ = NULL;
+  }
+
+  OnDemandEventsInterface* on_demand_events(on_demand_events());
+  if (on_demand_events) {
+    on_demand_events->DoExit();
+    SetEventSink(NULL);
+  }
+
+  // The message loop for OnDemand is on a separate thread, which needs to be
+  // signaled to exit.
+  ::PostQuitMessage(0);
+}
+
+void JobObserverCOMDecorator::SetEventSink(
+    OnDemandEventsInterface* event_sink) {
+  set_job_events(event_sink);
+}
+
+// TODO(omaha): Need to add a DoPause() to OnDemandEventsInterface. We never
+// expect these to be used since Chrome never did.
+STDMETHODIMP JobObserverCOMDecorator::DoPause() {
+  OnDemandEventsInterface* on_demand_events(on_demand_events());
+  if (!on_demand_events) {
+    return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL;
+  }
+
+  return E_NOTIMPL;
+}
+
+// TODO(omaha): Need to add a DoResume() to OnDemandEventsInterface.
+STDMETHODIMP JobObserverCOMDecorator::DoResume() {
+  OnDemandEventsInterface* on_demand_events(on_demand_events());
+  if (!on_demand_events) {
+    return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL;
+  }
+
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP JobObserverCOMDecorator::DoClose() {
+  OnDemandEventsInterface* on_demand_events(on_demand_events());
+  if (!on_demand_events) {
+    return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL;
+  }
+
+  on_demand_events->DoClose();
+  return S_OK;
+}
+
+// TODO(omaha): Reconcile IJobObserver::DoRestartBrowsers() with
+// OnDemandEventsInterface::DoRestartBrowser().
+STDMETHODIMP JobObserverCOMDecorator::DoRestartBrowsers() {
+  OnDemandEventsInterface* on_demand_events(on_demand_events());
+  if (!on_demand_events) {
+    return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL;
+  }
+
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP JobObserverCOMDecorator::DoReboot() {
+  OnDemandEventsInterface* on_demand_events(on_demand_events());
+  if (!on_demand_events) {
+    return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL;
+  }
+
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP JobObserverCOMDecorator::DoLaunchBrowser(const WCHAR* url) {
+  UNREFERENCED_PARAMETER(url);
+  OnDemandEventsInterface* on_demand_events(on_demand_events());
+  if (!on_demand_events) {
+    return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL;
+  }
+
+  return E_NOTIMPL;
+}
+
+}  // namespace omaha
diff --git a/goopdate/job_observer.h b/goopdate/job_observer.h
new file mode 100644
index 0000000..269f000
--- /dev/null
+++ b/goopdate/job_observer.h
@@ -0,0 +1,95 @@
+// Copyright 2008-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// TODO(omaha3): Rename the files and class to ondemand_com_decorator.* and
+// OnDemandCOMDecorator, respectively.
+
+#ifndef OMAHA_GOOPDATE_JOB_OBSERVER_H_
+#define OMAHA_GOOPDATE_JOB_OBSERVER_H_
+
+#include <windows.h>
+#include <atlbase.h>
+#include <atlcom.h>
+#include "base/scoped_ptr.h"
+#include "omaha/client/install_apps.h"
+#include "goopdate/omaha3_idl.h"
+
+namespace omaha {
+
+class JobObserverCOMDecorator
+  : public CComObjectRootEx<CComMultiThreadModel>,
+    public OnDemandObserver,
+    public IProgressWndEvents {
+ public:
+  BEGIN_COM_MAP(JobObserverCOMDecorator)
+    COM_INTERFACE_ENTRY(IProgressWndEvents)
+  END_COM_MAP()
+
+  JobObserverCOMDecorator();
+  virtual ~JobObserverCOMDecorator();
+  void Initialize(IJobObserver* job_observer);
+
+  // OnDemandObserver implementation.
+  virtual void OnCheckingForUpdate();
+  virtual void OnUpdateAvailable(const CString& app_name,
+                                 const CString& version_string);
+  virtual void OnWaitingToDownload(const CString& app_name);
+  virtual void OnDownloading(const CString& app_name,
+                             int time_remaining_ms,
+                             int pos);
+  virtual void OnWaitingRetryDownload(const CString& app_name,
+                                      time64 next_retry_time);
+  virtual void OnWaitingToInstall(const CString& app_name,
+                                  bool* can_start_install);
+  virtual void OnInstalling(const CString& app_name);
+  virtual void OnPause();
+  virtual void OnComplete(const ObserverCompletionInfo& observer_info);
+  virtual void SetEventSink(OnDemandEventsInterface* event_sink);
+
+  void Uninitialize();
+
+  // IProgressWndEvents.
+  STDMETHOD(DoPause)();
+  STDMETHOD(DoResume)();
+  STDMETHOD(DoClose)();
+  STDMETHOD(DoRestartBrowsers)();
+  STDMETHOD(DoReboot)();
+  STDMETHOD(DoLaunchBrowser)(const WCHAR* url);
+
+ private:
+  OnDemandEventsInterface* on_demand_events() {
+    return on_demand_events_;
+  }
+
+  // Since the code is running in an MTA, write access to
+  // on_demand_events_ must be atomic.
+  void set_job_events(OnDemandEventsInterface* on_demand_events) {
+    // InterlockedExchangePointer is broken due to ATL defining a function with
+    // the same name in the global namespace and hiding the Win32 API.
+    // InterlockedExchange introduces a full memory barrier.
+    ::InterlockedExchange(
+        reinterpret_cast<volatile LONG*>(&on_demand_events_),
+        reinterpret_cast<LONG>(on_demand_events));
+  }
+
+  CComPtr<IJobObserver> job_observer_;
+  OnDemandEventsInterface* volatile on_demand_events_;
+  DWORD thread_id_;
+  DWORD worker_job_thread_id_;
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_JOB_OBSERVER_H_
diff --git a/goopdate/main.cc b/goopdate/main.cc
index 265695e..cb4d182 100644
--- a/goopdate/main.cc
+++ b/goopdate/main.cc
@@ -14,10 +14,11 @@
 // ========================================================================
 
 #include <windows.h>
-#include "omaha/common/logging.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/google_update_idl_datax.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/utils.h"
 #include "omaha/goopdate/goopdate.h"
+#include "omaha/goopdate/omaha3_idl_datax.h"
 
 namespace {
 
@@ -28,10 +29,11 @@
 // Captures the module instance. Never call ::DisableThreadLibraryCalls in a
 // module that statically links with LIBC and it is not using
 // _beginthreadex for the thread creation. This will leak the _tiddata.
-extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void* res) {
+extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void*) {
   switch (reason) {
     case DLL_PROCESS_ATTACH:
       dll_instance = instance;
+      omaha::InitializeVersionFromModule(instance);
       break;
     case DLL_THREAD_ATTACH:
     case DLL_THREAD_DETACH:
@@ -40,7 +42,7 @@
       break;
   }
 
-  return PrxDllMain(instance, reason, res);
+  return TRUE;
 }
 
 // Since this entry point is called by a tiny shell we've built without
@@ -56,6 +58,7 @@
 
   bool is_local_system =  false;
   HRESULT hr = omaha::IsSystemProcess(&is_local_system);
+  assert(SUCCEEDED(hr));   // Assert because we cannot display UI here.
   if (SUCCEEDED(hr)) {
     omaha::Goopdate goopdate(is_local_system);
     hr = goopdate.Main(dll_instance, cmd_line, cmd_show);
@@ -68,11 +71,3 @@
   return static_cast<int>(hr);
 }
 
-extern "C" STDAPI DllCanUnloadNow() {
-  return PrxDllCanUnloadNow();
-}
-
-extern "C" STDAPI DllGetClassObject(REFCLSID clsid, REFIID iid, LPVOID* ptr) {
-  return PrxDllGetClassObject(clsid, iid, ptr);
-}
-
diff --git a/goopdate/main_unittest.cc b/goopdate/main_unittest.cc
index 2e4d54c..142bbbb 100644
--- a/goopdate/main_unittest.cc
+++ b/goopdate/main_unittest.cc
@@ -15,28 +15,27 @@
 
 
 #include <shlwapi.h>
-
+#include "omaha/base/constants.h"
+#include "omaha/common/const_goopdate.h"
 #include "omaha/goopdate/main.h"
-#include "omaha/goopdate/const_goopdate.h"
 #include "omaha/testing/unit_test.h"
 
 namespace omaha {
 
-TEST(MainTest, EntryPoint) {
+  TEST(MainTest, EntryPoint) {
   TCHAR path[MAX_PATH] = {0};
   ASSERT_TRUE(::GetModuleFileName(NULL, path, MAX_PATH));
   ::PathRemoveFileSpec(path);
-  ASSERT_TRUE(::PathAppend(path, omaha::kGoopdateDllName));
+  ASSERT_TRUE(::PathAppend(path, kOmahaDllName));
 
   HMODULE module(::LoadLibraryEx(path, NULL, 0));
   ASSERT_TRUE(module);
 
   DllEntry dll_entry = reinterpret_cast<DllEntry>(
-      ::GetProcAddress(module, omaha::kGoopdateDllEntryAnsi));
+      ::GetProcAddress(module, kGoopdateDllEntryAnsi));
   ASSERT_TRUE(dll_entry);
 
   ::FreeLibrary(module);
 }
 
 }  // namespace omaha
-
diff --git a/goopdate/model.cc b/goopdate/model.cc
new file mode 100644
index 0000000..c0fffa8
--- /dev/null
+++ b/goopdate/model.cc
@@ -0,0 +1,170 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/model.h"
+#include <algorithm>
+#include <functional>
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/goopdate/worker.h"
+
+namespace omaha {
+
+Model::Model(WorkerModelInterface* worker) : worker_(NULL) {
+  CORE_LOG(L3, (_T("[Model::Model]")));
+  ASSERT1(worker);
+
+  // Initialization of the object must be thread safe.
+  __mutexScope(lock_);
+  worker_ = worker;
+}
+
+Model::~Model() {
+  CORE_LOG(L3, (_T("[Model::~Model]")));
+
+  __mutexScope(lock_);
+  ASSERT1(app_bundles_.empty());
+  worker_ = NULL;
+}
+
+shared_ptr<AppBundle> Model::CreateAppBundle(bool is_machine) {
+  __mutexScope(lock_);
+
+  shared_ptr<AppBundle> app_bundle(new AppBundle(is_machine, this));
+  app_bundles_.push_back(AppBundleWeakPtr(app_bundle));
+
+  const int lock_count(worker_->Lock());
+
+  CORE_LOG(L3, (_T("[Model::CreateAppBundle][bundle %p][module lock %i]"),
+      app_bundle.get(), lock_count));
+
+  return app_bundle;
+}
+
+// Erases the expired AppBundle weak pointers.
+void Model::CleanupExpiredAppBundles() {
+  __mutexScope(lock_);
+
+  typedef std::vector<AppBundleWeakPtr>::iterator Iterator;
+  Iterator it = remove_if(app_bundles_.begin(),
+                          app_bundles_.end(),
+                          std::mem_fun_ref(&AppBundleWeakPtr::expired));
+
+  const size_t num_bundles = distance(it, app_bundles_.end());
+
+  app_bundles_.erase(it, app_bundles_.end());
+  for (size_t i = 0; i != num_bundles; ++i) {
+    const int lock_count(worker_->Unlock());
+
+    CORE_LOG(L3, (_T("[Model::CleanupExpiredAppBundles][module unlock %i]"),
+        lock_count));
+  }
+}
+
+size_t Model::GetNumberOfAppBundles() const {
+  __mutexScope(lock_);
+  return app_bundles_.size();
+}
+
+shared_ptr<AppBundle> Model::GetAppBundle(size_t index) const {
+  __mutexScope(lock_);
+  ASSERT1(!app_bundles_[index].expired());
+  return app_bundles_[index].lock();
+}
+
+HRESULT Model::CheckForUpdate(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[Model::CheckForUpdate][0x%p]"), app_bundle));
+
+  __mutexScope(lock_);
+
+  return worker_->CheckForUpdateAsync(app_bundle);
+}
+
+HRESULT Model::Download(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[Model::Download][0x%p]"), app_bundle));
+
+  __mutexScope(lock_);
+
+  return worker_->DownloadAsync(app_bundle);
+}
+
+HRESULT Model::DownloadAndInstall(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[Model::DownloadAndInstall][0x%p]"), app_bundle));
+
+  __mutexScope(lock_);
+
+  return worker_->DownloadAndInstallAsync(app_bundle);
+}
+
+HRESULT Model::UpdateAllApps(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[Model::UpdateAllApps][0x%p]"), app_bundle));
+
+  __mutexScope(lock_);
+
+  return worker_->UpdateAllAppsAsync(app_bundle);
+}
+
+HRESULT Model::Stop(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[Model::Stop][0x%p]"), app_bundle));
+
+  __mutexScope(lock_);
+
+  return worker_->Stop(app_bundle);
+}
+
+HRESULT Model::Pause(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[Model::Pause][0x%p]"), app_bundle));
+
+  __mutexScope(lock_);
+
+  return worker_->Pause(app_bundle);
+}
+
+HRESULT Model::Resume(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[Model::Resume][0x%p]"), app_bundle));
+
+  __mutexScope(lock_);
+
+  return worker_->Resume(app_bundle);
+}
+
+HRESULT Model::DownloadPackage(Package* package) {
+  CORE_LOG(L3, (_T("[Model::DownloadPackage][0x%p]"), package));
+
+  __mutexScope(lock_);
+
+  return worker_->DownloadPackageAsync(package);
+}
+
+HRESULT Model::GetPackage(const Package* package, const CString& dir) const {
+  __mutexScope(lock_);
+
+  return worker_->GetPackage(package, dir);
+}
+
+bool Model::IsPackageAvailable(const Package* package) const {
+  __mutexScope(lock_);
+
+  return worker_->IsPackageAvailable(package);
+}
+
+HRESULT Model::PurgeAppLowerVersions(const CString& app_id,
+                                     const CString& version) const {
+  __mutexScope(lock_);
+
+  return worker_->PurgeAppLowerVersions(app_id, version);
+}
+
+}  // namespace omaha
diff --git a/goopdate/model.h b/goopdate/model.h
new file mode 100644
index 0000000..d789a94
--- /dev/null
+++ b/goopdate/model.h
@@ -0,0 +1,104 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Defines the root of the object model. This is not exposed as a COM object.
+
+#ifndef OMAHA_GOOPDATE_MODEL_H_
+#define OMAHA_GOOPDATE_MODEL_H_
+
+#ifndef _ATL_FREE_THREADED
+#error Must use _ATL_FREE_THREADED to avoid differences in member offsets.
+#endif
+
+#include <windows.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/debug.h"
+#include "base/scoped_ptr.h"
+#include "base/synchronized.h"
+#include "omaha/goopdate/app.h"
+#include "omaha/goopdate/app_bundle.h"
+#include "omaha/goopdate/app_version.h"
+#include "omaha/goopdate/current_state.h"
+#include "omaha/goopdate/package.h"
+#include "third_party/bar/shared_ptr.h"
+
+namespace omaha {
+
+class WorkerModelInterface;
+
+class Model {
+ public:
+  explicit Model(WorkerModelInterface* worker);
+  virtual ~Model();
+
+  const Lockable& lock() const { return lock_; }
+
+  // Returns true if the model lock is held by the calling thread.
+  bool IsLockedByCaller() const {
+    return ::GetCurrentThreadId() == lock_.GetOwner();
+  }
+
+  // Creates an AppBundle object in the model.
+  shared_ptr<AppBundle> CreateAppBundle(bool is_machine);
+
+  // Removes the AppBundle objects that have no outstanding strong references.
+  void CleanupExpiredAppBundles();
+
+  size_t GetNumberOfAppBundles() const;
+
+  shared_ptr<AppBundle> GetAppBundle(size_t index) const;
+
+  // Initiates an update check for all apps in the bundle.
+  HRESULT CheckForUpdate(AppBundle* app_bundle);
+
+  // Initiates download of files necessary to install all apps in the bundle.
+  HRESULT Download(AppBundle* app_bundle);
+
+  // Initiates Download, if necessary, and install all app in the bundle.
+  HRESULT DownloadAndInstall(AppBundle* app_bundle);
+
+  // Initiates an update of all registered apps and performs periodic tasks
+  // related to all apps. Primarily for use by Omaha's /ua client. Includes
+  // update check, download and install.
+  HRESULT UpdateAllApps(AppBundle* app_bundle);
+
+  HRESULT Stop(AppBundle* app_bundle);
+  HRESULT Pause(AppBundle* app_bundle);
+  HRESULT Resume(AppBundle* app_bundle);
+
+  HRESULT DownloadPackage(Package* package);
+  HRESULT GetPackage(const Package* package, const CString& dir) const;
+
+  bool IsPackageAvailable(const Package* package) const;
+
+  HRESULT PurgeAppLowerVersions(const CString& app_id,
+                                const CString& version) const;
+
+ private:
+  typedef weak_ptr<AppBundle> AppBundleWeakPtr;
+
+  // Serializes access to the model objects. Consider replacing with SWMR lock.
+  LLock lock_;
+
+  std::vector<AppBundleWeakPtr> app_bundles_;
+  WorkerModelInterface* worker_;
+
+  DISALLOW_COPY_AND_ASSIGN(Model);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_MODEL_H_
diff --git a/goopdate/model_object.cc b/goopdate/model_object.cc
new file mode 100644
index 0000000..e3eb5d2
--- /dev/null
+++ b/goopdate/model_object.cc
@@ -0,0 +1,26 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/model_object.h"
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+bool IsModelLockedByCaller(const Model* model) {
+  return model->IsLockedByCaller();
+}
+
+}  // namespace omaha
+
diff --git a/goopdate/model_object.h b/goopdate/model_object.h
new file mode 100644
index 0000000..5c79b0f
--- /dev/null
+++ b/goopdate/model_object.h
@@ -0,0 +1,68 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Defines the base class of classes in the model. Provides access to the root
+// of the model.
+
+#ifndef OMAHA_GOOPDATE_MODEL_OBJECT_H_
+#define OMAHA_GOOPDATE_MODEL_OBJECT_H_
+
+#include <windows.h>
+#include "base/basictypes.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/utils.h"
+
+namespace omaha {
+
+class Model;
+
+bool IsModelLockedByCaller(const Model* model);
+
+class ModelObject {
+ public:
+
+  Model* model() {
+    return omaha::interlocked_exchange_pointer(&model_, model_);
+  }
+
+  const Model* model() const {
+    return omaha::interlocked_exchange_pointer(&model_, model_);
+  }
+
+ protected:
+
+  explicit ModelObject(Model* model) : model_(NULL) {
+    ASSERT1(model);
+    ASSERT1(IsModelLockedByCaller(model));
+
+    omaha::interlocked_exchange_pointer(&model_, model);
+  }
+
+  ~ModelObject() {
+    omaha::interlocked_exchange_pointer(&model_, static_cast<Model*>(NULL));
+  }
+
+ private:
+
+  // C++ root of the object model. Not owned by this instance.
+  mutable Model* volatile model_;
+
+  DISALLOW_COPY_AND_ASSIGN(ModelObject);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_MODEL_OBJECT_H_
+
diff --git a/goopdate/model_unittest.cc b/goopdate/model_unittest.cc
new file mode 100644
index 0000000..ac5b4e6
--- /dev/null
+++ b/goopdate/model_unittest.cc
@@ -0,0 +1,27 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/model.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+class ModelTest : public testing::Test {
+  virtual void SetUp() {}
+  virtual void TearDown() {}
+};
+
+}  // namespace omaha
+
diff --git a/goopdate/non_localized_resource.h b/goopdate/non_localized_resource.h
new file mode 100644
index 0000000..e47dfb0
--- /dev/null
+++ b/goopdate/non_localized_resource.h
@@ -0,0 +1,26 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_NON_LOCALIZED_RESOURCE_H_
+#define OMAHA_GOOPDATE_NON_LOCALIZED_RESOURCE_H_
+
+#define IDI_ELEVATION_MONIKER_ICON        1004
+#define IDR_LOCAL_SERVER_RGS              2005
+#define IDR_LOCAL_SERVER_ELEVATION_RGS    2006
+#define IDR_LOCAL_SERVER_IE_LOW_RGS       2007
+#define IDR_LOCAL_SERVICE_RGS             2008
+#define IDR_GOOGLE_UPDATE3_SERVICE_APPID  2009
+
+#endif  // OMAHA_GOOPDATE_NON_LOCALIZED_RESOURCE_H_
diff --git a/goopdate/offline_utils.cc b/goopdate/offline_utils.cc
new file mode 100644
index 0000000..163dcf3
--- /dev/null
+++ b/goopdate/offline_utils.cc
@@ -0,0 +1,95 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/offline_utils.h"
+#include <atlpath.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+
+namespace omaha {
+
+namespace offline_utils {
+
+CString GetV2OfflineManifest(const CString& app_id,
+                             const CString& offline_dir) {
+  CPath manifest_filename(app_id);
+  VERIFY1(manifest_filename.AddExtension(_T(".gup")));
+  return ConcatenatePath(offline_dir, manifest_filename);
+}
+
+HRESULT FindV2OfflinePackagePath(const CString& offline_app_dir,
+                                 CString* package_path) {
+  ASSERT1(!offline_app_dir.IsEmpty());
+  ASSERT1(package_path);
+  package_path->Empty();
+
+  CORE_LOG(L3, (_T("[FindV2OfflinePackagePath][%s]"), offline_app_dir));
+
+  CString pattern(_T("*"));
+  std::vector<CString> files;
+  HRESULT hr = FindFiles(offline_app_dir, pattern, &files);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (files.size() != 3) {
+    CORE_LOG(LE, (_T("[Cannot guess filename with multiple files]")));
+    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+  }
+
+  CString local_package_path;
+  // Skip over "." and "..".
+  size_t i = 0;
+  for (; i < files.size(); ++i) {
+    local_package_path = ConcatenatePath(offline_app_dir, files[i]);
+    if (!File::IsDirectory(local_package_path)) {
+      break;
+    }
+  }
+  if (i == files.size()) {
+    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+  }
+
+  CORE_LOG(L3, (_T("[Non-standard/legacy package][%s]"), local_package_path));
+  *package_path = local_package_path;
+  return S_OK;
+}
+
+HRESULT ParseOfflineManifest(const CString& app_id,
+                             const CString& offline_dir,
+                             xml::UpdateResponse* update_response) {
+  CORE_LOG(L3, (_T("[ParseOfflineManifest][%s][%s]"), app_id, offline_dir));
+  ASSERT1(update_response);
+
+  CString manifest_path(ConcatenatePath(offline_dir, kOfflineManifestFileName));
+  if (!File::Exists(manifest_path)) {
+    manifest_path = GetV2OfflineManifest(app_id, offline_dir);
+  }
+
+  HRESULT hr = update_response->DeserializeFromFile(manifest_path);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[DeserializeFromFile failed][%s][0x%x]"),
+                  manifest_path, hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+}  // namespace offline_utils
+
+}  // namespace omaha
diff --git a/goopdate/offline_utils.h b/goopdate/offline_utils.h
new file mode 100644
index 0000000..0e9305f
--- /dev/null
+++ b/goopdate/offline_utils.h
@@ -0,0 +1,40 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_OFFLINE_UTILS_H_
+#define OMAHA_GOOPDATE_OFFLINE_UTILS_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include "omaha/common/update_response.h"
+
+namespace omaha {
+
+namespace offline_utils {
+
+CString GetV2OfflineManifest(const CString& app_id,
+                             const CString& offline_dir);
+
+HRESULT FindV2OfflinePackagePath(const CString& offline_app_dir,
+                                 CString* package_path);
+
+HRESULT ParseOfflineManifest(const CString& app_id,
+                             const CString& offline_dir,
+                             xml::UpdateResponse* update_response);
+}  // namespace offline_utils
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_OFFLINE_UTILS_H_
diff --git a/goopdate/offline_utils_unittest.cc b/goopdate/offline_utils_unittest.cc
new file mode 100644
index 0000000..4524ab0
--- /dev/null
+++ b/goopdate/offline_utils_unittest.cc
@@ -0,0 +1,187 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/offline_utils.h"
+#include <atlpath.h>
+#include "omaha/base/app_util.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/utils.h"
+#include "omaha/goopdate/update_response_utils.h"
+#include "omaha/testing/resource.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+const TCHAR* kAppId1 = _T("{CDABE316-39CD-43BA-8440-6D1E0547AEE6}");
+
+void CheckResponse(const xml::response::Response& xml_response,
+                   const TCHAR* expected_protocol_version) {
+  EXPECT_STREQ(expected_protocol_version, xml_response.protocol);
+  EXPECT_EQ(1, xml_response.apps.size());
+
+  const xml::response::App& app(xml_response.apps[0]);
+  EXPECT_STREQ(kAppId1, app.appid);
+  EXPECT_STREQ(_T("ok"), app.status);
+
+  const xml::response::UpdateCheck& update_check(app.update_check);
+  EXPECT_STREQ(_T("ok"), update_check.status);
+  EXPECT_EQ(1, update_check.urls.size());
+  EXPECT_STREQ(_T("http://dl.google.com/foo/install/1.2.3.4/"),
+               update_check.urls[0]);
+
+  const xml::InstallManifest& install_manifest(update_check.install_manifest);
+  EXPECT_STREQ(_T("1.2.3.4"), install_manifest.version);
+  EXPECT_EQ(1, install_manifest.packages.size());
+
+  const xml::InstallPackage& install_package(install_manifest.packages[0]);
+  EXPECT_STREQ(_T("foo_installer.exe"), install_package.name);
+  EXPECT_TRUE(install_package.is_required);
+  EXPECT_EQ(12345678, install_package.size);
+  EXPECT_STREQ(_T("abcdef"), install_package.hash);
+
+  EXPECT_EQ(2, install_manifest.install_actions.size());
+
+  const xml::InstallAction* install_action(
+      &install_manifest.install_actions[0]);
+  EXPECT_EQ(xml::InstallAction::kInstall, install_action->install_event);
+  EXPECT_EQ(NEEDS_ADMIN_NO, install_action->needs_admin);
+  EXPECT_STREQ(_T("foo_installer.exe"), install_action->program_to_run);
+  EXPECT_STREQ(_T("-baz"), install_action->program_arguments);
+  EXPECT_FALSE(install_action->terminate_all_browsers);
+  EXPECT_EQ(SUCCESS_ACTION_DEFAULT, install_action->success_action);
+
+  install_action = &install_manifest.install_actions[1];
+  EXPECT_EQ(xml::InstallAction::kPostInstall, install_action->install_event);
+  EXPECT_EQ(NEEDS_ADMIN_NO, install_action->needs_admin);
+  EXPECT_FALSE(install_action->terminate_all_browsers);
+  EXPECT_EQ(SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD,
+            install_action->success_action);
+
+  EXPECT_EQ(0, app.events.size());
+
+  CString value;
+  CString verboselogging_install_data(_T("\n      {\n        \"distribution\": {\n          \"verbose_logging\": true\n        }\n      }\n    "));  // NOLINT
+  EXPECT_SUCCEEDED(update_response_utils::GetInstallData(app.data,
+                                                         _T("verboselogging"),
+                                                         &value));
+  EXPECT_STREQ(verboselogging_install_data, value);
+
+  CString foobarapp_install_data(_T("\n      {\n        \"distribution\": {\n          \"skip_first_run_ui\": true,\n          \"show_welcome_page\": true,\n          \"import_search_engine\": true,\n          \"import_history\": false,\n          \"create_all_shortcuts\": true,\n          \"do_not_launch_foo\": true,\n          \"make_foo_default\": false,\n          \"verbose_logging\": false\n        }\n      }\n    "));  // NOLINT
+  EXPECT_SUCCEEDED(update_response_utils::GetInstallData(app.data,
+                                                         _T("foobarapp"),
+                                                         &value));
+  EXPECT_STREQ(foobarapp_install_data, value);
+
+  EXPECT_EQ(GOOPDATE_E_INVALID_INSTALL_DATA_INDEX,
+            update_response_utils::GetInstallData(app.data, _T("foo"), &value));
+}
+
+void ParseAndCheck(const TCHAR* source_manifest_extension,
+                   const TCHAR* target_manifest_filename,
+                   const TCHAR* expected_protocol_version) {
+  CString source_manifest_path = ConcatenatePath(
+      app_util::GetCurrentModuleDirectory(), _T("unittest_support"));
+  source_manifest_path = ConcatenatePath(source_manifest_path, kAppId1);
+  source_manifest_path += source_manifest_extension;
+
+  CString target_manifest_path = ConcatenatePath(
+      app_util::GetCurrentModuleDirectory(), target_manifest_filename);
+
+  EXPECT_SUCCEEDED(File::Copy(source_manifest_path, target_manifest_path,
+                              true));
+
+  scoped_ptr<xml::UpdateResponse> update_response(
+      xml::UpdateResponse::Create());
+  EXPECT_SUCCEEDED(offline_utils::ParseOfflineManifest(
+                       kAppId1,
+                       app_util::GetCurrentModuleDirectory(),
+                       update_response.get()));
+
+  CheckResponse(update_response->response(), expected_protocol_version);
+
+  EXPECT_SUCCEEDED(File::Remove(target_manifest_path));
+}
+
+}  // namespace
+
+namespace offline_utils {
+
+TEST(OfflineUtilsTest, GetV2OfflineManifest) {
+  CString manifest_path = offline_utils::GetV2OfflineManifest(
+      kAppId1, app_util::GetCurrentModuleDirectory());
+
+  EXPECT_STREQ(ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                               CString(kAppId1) + _T(".gup")),
+               manifest_path);
+}
+
+TEST(OfflineUtilsTest, FindV2OfflinePackagePath_Success) {
+  CString installer_exe = _T("foo_installer.exe");
+  CString installer_path = ConcatenatePath(
+                                app_util::GetCurrentModuleDirectory(),
+                                kAppId1);
+  EXPECT_SUCCEEDED(CreateDir(installer_path, NULL));
+  EXPECT_SUCCEEDED(File::Copy(
+      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                      _T("unittest_support\\SaveArguments.exe")),
+      ConcatenatePath(installer_path, installer_exe),
+      true));
+
+  CString package_path;
+  EXPECT_SUCCEEDED(offline_utils::FindV2OfflinePackagePath(installer_path,
+                                                           &package_path));
+  EXPECT_STREQ(ConcatenatePath(installer_path, installer_exe), package_path);
+
+  EXPECT_SUCCEEDED(DeleteDirectory(installer_path));
+}
+
+TEST(OfflineUtilsTest, FindV2OfflinePackagePath_Failure) {
+  CString package_path;
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND),
+            offline_utils::FindV2OfflinePackagePath(
+                ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                                kAppId1),
+                &package_path));
+  EXPECT_TRUE(package_path.IsEmpty());
+}
+
+TEST(OfflineUtilsTest, ParseOfflineManifest_v3_Success) {
+  ParseAndCheck(_T(".v3.gup"), kOfflineManifestFileName, _T("3.0"));
+}
+
+TEST(OfflineUtilsTest, ParseOfflineManifest_v2_Success) {
+  CString target_manifest_filename = CString(kAppId1) + _T(".gup");
+  ParseAndCheck(_T(".v2.gup"), target_manifest_filename, _T("2.0"));
+}
+
+TEST(OfflineUtilsTest, ParseOfflineManifest_FileDoesNotExist) {
+  scoped_ptr<xml::UpdateResponse> update_response(
+      xml::UpdateResponse::Create());
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            offline_utils::ParseOfflineManifest(
+                               kAppId1,
+                               app_util::GetCurrentModuleDirectory(),
+                               update_response.get()));
+}
+
+}  // namespace offline_utils
+
+}  // namespace omaha
diff --git a/goopdate/omaha3_idl.idl b/goopdate/omaha3_idl.idl
new file mode 100644
index 0000000..3cf9985
--- /dev/null
+++ b/goopdate/omaha3_idl.idl
@@ -0,0 +1,972 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+import "oaidl.idl";
+import "ocidl.idl";
+
+// When adding interfaces to this file:
+//  * Do not use "Google" or "GoogleUpdate" directly. Instead, use preprocessor
+//    defines.
+//  * Add a test for the Google-specific value to
+//    omaha_customization_goopdate_apis_unittest.cc.
+
+//
+// Enums.
+// These values can be passed to interface methods and/or compared to their
+// output.
+//
+
+// Must be kept in sync with the enum in base/browser_utils.h.
+typedef enum BrowserType {
+  BROWSER_UNKNOWN           = 0,
+  BROWSER_DEFAULT           = 1,
+  BROWSER_INTERNET_EXPLORER = 2,
+  BROWSER_FIREFOX           = 3,
+  BROWSER_CHROME            = 4,
+} BrowserType;
+
+// The normal install flow proceeds from STATE_INIT through
+// STATE_INSTALL_COMPLETE in order, skipping states that are not relevant.
+// All exceptions and terminal states are start with STATE_INSTALL_COMPLETE.
+typedef enum CurrentState {
+  STATE_INIT = 1,
+  STATE_WAITING_TO_CHECK_FOR_UPDATE = 2,
+  STATE_CHECKING_FOR_UPDATE = 3,
+  STATE_UPDATE_AVAILABLE = 4,
+  STATE_WAITING_TO_DOWNLOAD = 5,
+  STATE_RETRYING_DOWNLOAD = 6,
+  STATE_DOWNLOADING = 7,
+  STATE_DOWNLOAD_COMPLETE = 8,
+  STATE_EXTRACTING = 9,
+  STATE_APPLYING_DIFFERENTIAL_PATCH = 10,
+  // TODO(omaha3): Should we move STATE_DOWNLOAD_COMPLETE here and eliminate
+  // STATE_READY_TO_INSTALL?
+  STATE_READY_TO_INSTALL = 11,
+  STATE_WAITING_TO_INSTALL = 12,
+  STATE_INSTALLING = 13,
+  STATE_INSTALL_COMPLETE = 14,
+  STATE_PAUSED = 15,
+  STATE_NO_UPDATE = 16,
+  STATE_ERROR = 17,
+} CurrentState;
+
+typedef enum InstallPriority {
+  INSTALL_PRIORITY_LOW = 0,
+  INSTALL_PRIORITY_HIGH = 10,
+} InstallPriority;
+
+// Specifies what the client should do after installation.
+typedef enum PostInstallAction {
+  POST_INSTALL_ACTION_DEFAULT = 0,
+
+  // Caller should exit silently.
+  POST_INSTALL_ACTION_EXIT_SILENTLY = 1,
+
+  // Caller should launch the command.
+  POST_INSTALL_ACTION_LAUNCH_COMMAND = 2,
+
+  // Caller should launch the command and exit silently.
+  POST_INSTALL_ACTION_EXIT_SILENTLY_ON_LAUNCH_COMMAND = 3,
+
+  // The caller should ask the user to restart the browser. If the value of
+  // IApp's browser is supported and postInstallUrl is valid, the client should
+  // offer to restart the browser. If the user chooses to do so, the client
+  // should launch the ICurrentState::postInstallUrl after shutting down and
+  // restarting the browser.
+  POST_INSTALL_ACTION_RESTART_BROWSER = 4,
+
+  // Similar to POST_INSTALL_ACTION_RESTART_BROWSER, but ask the user to shut
+  // down all browsers.
+  POST_INSTALL_ACTION_RESTART_ALL_BROWSERS = 5,
+
+  // The caller should ask the user to reboot the machine.
+  POST_INSTALL_ACTION_REBOOT = 6,
+} PostInstallAction;
+
+[
+  object,
+  dual,
+  uuid(76607e7e-c0ff-4dd3-b134-7fc3724ee66a),
+  helpstring("IGoogleUpdate3 Interface"),
+  pointer_default(unique)
+]
+interface IGoogleUpdate3 : IDispatch {
+  // TODO(Omaha): Perhaps this interface exposes helpers such as
+  // RestartBrowsers, etc.
+
+  // Returns the count of the AppBundles in this IGoogleUpdate3 interface.
+  [id(1), propget] HRESULT Count([out, retval] long* count);
+
+  // Returns an IDispatch of the AppBundle in this IGoogleUpdate3 interface at
+  // the specified 0-based index. This property has the dispid of DISPID_VALUE
+  // to make it the default property of IGoogleUpdate3.
+  [id(DISPID_VALUE), propget] HRESULT Item([in] long index,
+                                           [out, retval] IDispatch** bundle);
+  // Returns an IDispatch to a newly created empty AppBundle.
+  [id(2)] HRESULT createAppBundle([out, retval] IDispatch** app_bundle);
+}
+
+[
+  object,
+  dual,
+  uuid(9a527c99-02cb-49eb-a14f-225c25cb76dc),
+  helpstring("IAppBundle Interface"),
+  pointer_default(unique)
+]
+interface IAppBundle : IDispatch {
+  // TODO(omaha3): AppBundle::display_name_ is never used. Should we remove?
+  [propget] HRESULT displayName([out, retval] BSTR*);
+  [propput] HRESULT displayName([in] BSTR);
+
+  [propget] HRESULT displayLanguage([out, retval] BSTR*);
+  [propput] HRESULT displayLanguage([in] BSTR);
+
+  [propget] HRESULT installSource([out, retval] BSTR*);
+  [propput] HRESULT installSource([in] BSTR);
+
+  [propget] HRESULT originURL([out, retval] BSTR*);
+  [propput] HRESULT originURL([in] BSTR);
+
+  [propget] HRESULT offlineDirectory([out, retval] BSTR* offline_dir);
+  [propput] HRESULT offlineDirectory([in] BSTR offline_dir);
+
+  [propget] HRESULT sessionId([out, retval] BSTR* session_id);
+  [propput] HRESULT sessionId([in] BSTR session_id);
+
+  // The priority property determines download speed/priority and the number/
+  // frequency of retries.  Use values from the InstallPriority enum.
+  [propget] HRESULT priority([out, retval] long* priority);
+  [propput] HRESULT priority([in] long priority);
+
+  // Returns the count of the Apps in the AppBundle.
+  [id(1), propget] HRESULT Count([out, retval] long* count);
+
+  // Returns an IDispatch of the App in the AppBundle at the specified 0-based
+  // index. This property has the dispid of DISPID_VALUE to make it the default
+  // property of IAppBundle.
+  [id(DISPID_VALUE), propget] HRESULT Item([in] long index,
+                                           [out, retval] IDispatch** app);
+
+  // Impersonation and primary tokens set by the client. Typically only
+  // set by the gupdatem service. The gupdatem service exposes a narrow
+  // interface to medium integrity clients. When a medium integrity client calls
+  // into the gupdatem service, the gupdatem service captures the token of the
+  // caller, and then calls put_altTokens() on the gupdate service, so that the
+  // gupdate service can use it for future download() and install() requests.
+  [propput] HRESULT altTokens([in] ULONG_PTR impersonation_token,
+                              [in] ULONG_PTR primary_token,
+                              [in] DWORD caller_proc_id);
+
+  // Sets a HWND to associate with the client, if any.  This will be used as
+  // the parent window for any dialogs that the server may need to display.
+  [propput] HRESULT parentHWND([in] ULONG_PTR hwnd);
+
+  // Initializes the bundle with the properties that have been set.
+  [id(2)] HRESULT initialize();
+
+  // Returns an IDispatch to a new App for the specified app id.
+  // The App is added to the Bundle.
+  [id(3)] HRESULT createApp([in] BSTR app_id,
+                            [out, retval] IDispatch** app);
+
+  // Returns an IDispatch to a newly created App for the specified app ID. The
+  // App is populated with information from the existing installation and added
+  // to the Bundle. Fails if the specified app is not installed.
+  [id(4)] HRESULT createInstalledApp([in] BSTR app_id,
+                                     [out, retval] IDispatch** app);
+
+  // Creates App instances for all installed apps managed by this Omaha
+  // instance. Each App is populated with information from the existing install.
+  [id(5)] HRESULT createAllInstalledApps();
+
+  // These methods are non-blocking. The operation is scheduled.
+  [id(6)] HRESULT checkForUpdate();
+  [id(7)] HRESULT download();
+  [id(8)] HRESULT install();
+
+  // All-in-one function for automatically updating all apps. Populates the
+  // bundle then schedules the update, which includes the update check and
+  // download and install, if necessary.
+  [id(9)] HRESULT updateAllApps();
+
+  // These three methods are non-blocking. The operation is requested.
+  [id(10)] HRESULT stop();
+  [id(11)] HRESULT pause();
+  [id(12)] HRESULT resume();
+
+  // Returns true if the bundle has an uncompleted non-blocking request.
+  [id(13)] HRESULT isBusy([out, retval] VARIANT_BOOL* is_busy);
+
+  // Downloads a package of an installed application.
+  [id(14)] HRESULT downloadPackage([in] BSTR app_id, [in] BSTR package_name);
+
+  // TODO(omaha): Define this aggregated bundle state. Is this really a property
+  // or should it be getCurrentState?
+  // The server and bundle are the only thing that can provide aggregated
+  // time estimates for downloads. Also, aggregate percentage is not currently
+  // available to the client because the total bytes to download is not
+  // available from App in all post-update check states.
+  // To do this, we will probably need to know the total expected download
+  // size for all packages to be installed - those that are required or in use -
+  // by the time the update check phase is complete.
+  [id(15), propget] HRESULT currentState([out, retval] VARIANT* current_state);
+};
+
+[
+  object,
+  dual,
+  uuid(2ea2902f-31b3-45fe-a27a-62bb796e74c0),
+  helpstring("IApp Interface"),
+  pointer_default(unique)
+]
+interface IApp : IDispatch {
+  // Returns a version IDispatch object.
+  [id(1), propget] HRESULT currentVersion([out, retval] IDispatch** current);
+  [id(2), propget] HRESULT nextVersion([out, retval] IDispatch** next);
+
+  [propget] HRESULT appId([out, retval] BSTR*);
+
+  [propget] HRESULT displayName([out, retval] BSTR*);
+  [propput] HRESULT displayName([in] BSTR);
+
+  [propget] HRESULT language([out, retval] BSTR*);
+  [propput] HRESULT language([in] BSTR);
+
+  [propget] HRESULT ap([out, retval] BSTR*);
+  [propput] HRESULT ap([in] BSTR);
+
+  [propget] HRESULT ttToken([out, retval] BSTR*);
+  [propput] HRESULT ttToken([in] BSTR);
+
+  [propget] HRESULT iid([out, retval] BSTR*);
+  [propput] HRESULT iid([in] BSTR);
+
+  [propget] HRESULT brandCode([out, retval] BSTR*);
+  [propput] HRESULT brandCode([in] BSTR);
+
+  [propget] HRESULT clientId([out, retval] BSTR*);
+  [propput] HRESULT clientId([in] BSTR);
+
+  [propget] HRESULT labels([out, retval] BSTR*);
+  [propput] HRESULT labels([in] BSTR);
+
+  [propget] HRESULT referralId([out, retval] BSTR*);
+  [propput] HRESULT referralId([in] BSTR);
+
+  // Use values from the BrowserType enum.
+  [propget] HRESULT browserType([out, retval] UINT*);
+  [propput] HRESULT browserType([in] UINT);
+
+  [propget] HRESULT clientInstallData([out, retval] BSTR*);
+  [propput] HRESULT clientInstallData([in] BSTR);
+
+  [propget] HRESULT serverInstallDataIndex([out, retval] BSTR*);
+  [propput] HRESULT serverInstallDataIndex([in] BSTR);
+
+  // Set as soon as possible. Error pings are disabled until set to true.
+  [propget] HRESULT isEulaAccepted([out, retval] VARIANT_BOOL*);
+  [propput] HRESULT isEulaAccepted([in] VARIANT_BOOL);
+
+  [propget] HRESULT usageStatsEnable([out, retval] UINT*);
+  [propput] HRESULT usageStatsEnable([in] UINT);
+
+  [propget] HRESULT installTimeDiffSec([out, retval] UINT*);
+
+  // Returns an ICurrentState interface. The object underlying the interface has
+  // static data that does not get updated as the server state changes. To get
+  // the most "current" state, the currentState property needs to be queried
+  // again.
+  [propget] HRESULT currentState([out, retval] IDispatch**);
+};
+
+[
+  object,
+  dual,
+  uuid(6f5a84a3-867a-47bb-848b-a4cd8ffa8fce),
+  helpstring("IAppVersion Interface"),
+  pointer_default(unique)
+]
+interface IAppVersion : IDispatch {
+  [propget] HRESULT version([out, retval] BSTR*);
+
+  // [propget] HRESULT installManifest([out, retval] BSTR*);
+
+  // Returns the count of the Packages in the AppVersion.
+  [propget] HRESULT packageCount([out, retval] long* count);
+
+  // Returns an IDispatch of the Package in the AppVersion at the specified
+  // 0-based index.
+  [propget] HRESULT package([in] long index,
+                            [out, retval] IDispatch** package);
+};
+
+[
+  object,
+  dual,
+  uuid(593e29d1-4adf-4f5d-ade8-a0836155c8a3),
+  helpstring("IPackage Interface"),
+  pointer_default(unique)
+]
+interface IPackage : IDispatch {
+  // Retrieves the package from the package cache and copies it to the
+  // directory provided. Returns an error is the package is not available
+  // locally.
+  [id(1)] HRESULT get([in] BSTR dir);
+
+  // Returns true if the package has been downloaded and is available
+  // locally.
+  [propget] HRESULT isAvailable([out, retval] VARIANT_BOOL*);
+
+  // Returns the manifest name of the package.
+  [propget] HRESULT filename([out, retval] BSTR*);
+};
+
+// TODO(omaha3): We should figure out what else we are going to want in this
+// interface before dogfood even if we do not implement it.
+[
+  object,
+  dual,
+  uuid(9df22aa7-720e-4216-9cb4-727781edac1a),
+  helpstring("ICurrentState Interface"),
+  pointer_default(unique)
+]
+interface ICurrentState : IDispatch {
+  // This interface is exposed to web clients!
+  // TODO(omaha3): Update valid comments once we settle on an implementation.
+
+  // A value from the CurrentState enum. This value determines which of the
+  // properties below are valid.
+  [propget] HRESULT stateValue([out, retval] LONG*);
+
+  // The remaining properties are only valid in the specified states. For all
+  // other states, the values are not specified.
+
+  // This property is valid only when stateValue is STATE_UPDATE_AVAILABLE.
+  [propget] HRESULT availableVersion([out, retval] BSTR*);
+
+  // The following three properties are only valid when stateValue is
+  // STATE_WAITING_TO_DOWNLOAD, STATE_RETRYING_DOWNLOAD, STATE_DOWNLOADING,
+  // STATE_DOWNLOAD_COMPLETE, STATE_EXTRACTING,
+  // STATE_APPLYING_DIFFERENTIAL_PATCH, or STATE_READY_TO_INSTALL.
+
+  // Bytes downloaded so far.
+  [propget] HRESULT bytesDownloaded([out, retval] ULONG*);
+
+  // Total bytes to download.
+  [propget] HRESULT totalBytesToDownload([out, retval] ULONG*);
+
+  // Estimated download time remaining in ms. -1 indicates unknown.
+  // Progress may not always be available, so clients should handle the -1 case.
+  [propget] HRESULT downloadTimeRemainingMs([out, retval] LONG*);
+
+  [propget] HRESULT nextRetryTime([out, retval] ULONGLONG*);
+
+  // TODO(omaha): Need some way to indicate reconnecting, retrying, etc.
+
+  // The following two properties are only valid when stateValue is
+  // STATE_INSTALLING or STATE_INSTALL_COMPLETE.
+
+  // Current install progress in percentage from 0 to 100. -1 indicates unknown.
+  // Progress may not always be available, so clients should handle the -1 case.
+  [propget] HRESULT installProgress([out, retval] LONG*);
+
+  // Estimated download time remaining in ms. -1 indicates unknown.
+  // Progress may not always be available, so clients should handle the -1 case.
+  [propget] HRESULT installTimeRemainingMs([out, retval] LONG*);
+
+  // The following four properties are only valid when stateValue is
+  // STATE_ERROR:
+
+  // Returns true if the app has been canceled.
+  [propget] HRESULT isCanceled([out, retval] VARIANT_BOOL* is_canceled);
+
+  // Error code.
+  [propget] HRESULT errorCode([out, retval] LONG*);
+
+  // Error extra code.
+  [propget] HRESULT extraCode1([out, retval] LONG*);
+
+  // The following three properties are only valid when stateValue is
+  // STATE_ERROR or STATE_INSTALL_COMPLETE.
+  // TODO(omaha3): If STATE_DOWNLOAD_COMPLETE or STATE_READY_TO_INSTALL becomes
+  // a terminal state, does it support completion messages?
+
+  // Completion message, localized in the specified language.
+  // TODO(omaha3): If we're going to have bundle error messages too, should the
+  // language be at bundle level? Should bundle have its own language setter?
+  [propget] HRESULT completionMessage([out, retval] BSTR*);
+
+  // Application installer result code. This is to be used as additional
+  // information only. Success/failure should be determined using errorCode.
+  // This is an error if errorCode is GOOPDATEINSTALL_E_INSTALLER_FAILED.
+  [propget] HRESULT installerResultCode([out, retval] LONG*);
+
+  // Application installer extra code.
+  [propget] HRESULT installerResultExtraCode1([out, retval] LONG*);
+
+  // A command that needs to be launched by the client after installation.
+  [propget] HRESULT postInstallLaunchCommandLine([out, retval] BSTR*);
+
+  // URL to be launched after restarting the browser.
+  [propget] HRESULT postInstallUrl([out, retval] BSTR*);
+
+  // Returns a PostInstallAction value indicating the action to be taken by the
+  // client after installation.
+  [propget] HRESULT postInstallAction([out, retval] LONG*);
+}
+
+[
+  object,
+  dual,
+  uuid(9f2f5f08-a28f-414f-8fe5-31d77b2af211),
+  helpstring("IRegistrationUpdateHook Interface"),
+  pointer_default(unique),
+]
+interface IRegistrationUpdateHook : IDispatch {
+  HRESULT UpdateRegistry([in] BSTR app_id, [in] VARIANT_BOOL is_machine);
+};
+
+[
+  object,
+  uuid(a3c270b9-98bd-4998-91a4-3f11adad0377),
+  helpstring("ICredentialDialog Interface"),
+  pointer_default(unique),
+]
+interface ICredentialDialog : IUnknown {
+  HRESULT QueryUserForCredentials([in] ULONG_PTR owner_hwnd,
+                                  [in] BSTR server,
+                                  [in] BSTR message,
+                                  [out] BSTR* username,
+                                  [out] BSTR* password);
+};
+
+// BEGIN gupdatem interfaces.
+
+// The following interfaces are exposed as a narrower version of the
+// IGoogleUpdate3 interface from the gupdatem service. These interfaces are
+// meant for use from medium and low integrity clients.
+
+[
+  object,
+  dual,
+  uuid(c90fc75b-9bdc-47aa-9f3c-59f416c91aef),
+  helpstring("IGoogleUpdate3Web Interface"),
+  pointer_default(unique),
+]
+interface IGoogleUpdate3Web : IDispatch {
+  HRESULT createAppBundleWeb([out, retval] IDispatch** app_bundle_web);
+};
+
+[
+  object,
+  uuid(d8f0740e-8970-44e4-ad03-46394ba7d3e7),
+  helpstring("IGoogleUpdate3WebSecurity Interface"),
+  pointer_default(unique),
+]
+interface IGoogleUpdate3WebSecurity : IUnknown {
+  HRESULT setOriginURL([in] BSTR origin_url);
+};
+
+[
+  object,
+  dual,
+  uuid(3aaa8bbc-dc93-49d9-9b87-c8ded3b2bb40),
+  helpstring("IAppBundleWeb Interface"),
+  pointer_default(unique),
+]
+interface IAppBundleWeb : IDispatch {
+  [id(2)] HRESULT createApp([in] BSTR app_guid,
+                            [in] BSTR brand_code,
+                            [in] BSTR language,
+                            [in] BSTR ap);
+  [id(3)] HRESULT createInstalledApp([in] BSTR app_id);
+  [id(4)] HRESULT createAllInstalledApps();
+
+  [propget] HRESULT displayLanguage([out, retval] BSTR*);
+  [propput] HRESULT displayLanguage([in] BSTR);
+
+  [propput] HRESULT parentHWND([in] ULONG_PTR hwnd);
+
+  [propget] HRESULT length([out, retval] int* index);
+  [id(DISPID_VALUE), propget] HRESULT appWeb(
+      [in] int index, [out, retval] IDispatch** app_web);
+
+  HRESULT initialize();
+
+  HRESULT checkForUpdate();
+  HRESULT download();
+  HRESULT install();
+
+  HRESULT pause();
+  HRESULT resume();
+  HRESULT cancel();
+
+  HRESULT downloadPackage([in] BSTR app_id, [in] BSTR package_name);
+
+  [propget] HRESULT currentState([out, retval] VARIANT* current_state);
+};
+
+[
+  object,
+  dual,
+  uuid(973d746a-1df2-404a-b66b-ea632417866f),
+  helpstring("IAppWeb Interface"),
+  pointer_default(unique),
+]
+interface IAppWeb : IDispatch {
+  [propget] HRESULT appId([out, retval] BSTR*);
+
+  // Returns an IAppVersionWeb IDispatch object.
+  [propget] HRESULT currentVersionWeb([out, retval] IDispatch** current);
+  [propget] HRESULT nextVersionWeb([out, retval] IDispatch** next);
+
+  HRESULT cancel();
+  [propget] HRESULT currentState([out, retval] IDispatch** current_state);
+  HRESULT launch();
+  HRESULT uninstall();
+};
+
+[
+  object,
+  dual,
+  uuid(afc15738-2cc4-4aac-9b19-c1831428d04f),
+  helpstring("IAppVersionWeb Interface"),
+  pointer_default(unique)
+]
+interface IAppVersionWeb : IDispatch {
+  [propget] HRESULT version([out, retval] BSTR*);
+
+  // Returns the count of the Packages in the AppVersion.
+  [propget] HRESULT packageCount([out, retval] long* count);
+
+  // TODO(omaha3): Implement this after a security review.
+  // Returns an IDispatch of the Package in the AppVersion at the specified
+  // 0-based index.
+  [propget] HRESULT packageWeb([in] long index,
+                               [out, retval] IDispatch** package);
+};
+
+[
+  object,
+  dual,
+  uuid(4dafb7ab-e8d8-4003-87b8-678e15b4c221),
+  helpstring("ICoCreateAsyncStatus Interface"),
+  pointer_default(unique)
+]
+interface ICoCreateAsyncStatus : IDispatch {
+  [propget] HRESULT isDone([out, retval] VARIANT_BOOL* is_done);
+  [propget] HRESULT completionHResult([out, retval] LONG* hr);
+  [propget] HRESULT createdInstance([out, retval] IDispatch** instance);
+};
+
+[
+  object,
+  uuid(7fbb29e7-6703-4624-ad84-c8500f57c5c4),
+  helpstring("ICoCreateAsync Interface"),
+  pointer_default(unique)
+]
+interface ICoCreateAsync : IUnknown {
+  HRESULT createOmahaMachineServerAsync(
+      [in] BSTR origin_url,
+      [in] BOOL create_elevated,
+      [out, retval] ICoCreateAsyncStatus** status);
+};
+
+// END gupdatem interfaces.
+
+// BEGIN Legacy google_update_idl interfaces.
+
+[
+  object,
+  uuid(791df12c-01ff-43a1-a3d7-5b1b98a9248a),
+  oleautomation,
+  nonextensible,
+  pointer_default(unique)
+]
+interface IBrowserHttpRequest2 : IUnknown {
+  // This method will send request/data from the browser process.
+  // @param url                     URL where request will be send.
+  // @param post_data               POST data, if any. Can be NULL.
+  // @param request_headers         HTTP request headers, if any. Can be NULL.
+  // @param response_headers_needed HTTP response headers that are needed.
+  //                                Should be one of the values listed here:
+  //                                    http://msdn.microsoft.com/aa385351.aspx
+  //                                The input is a SAFEARRAY of DWORD. Can be a
+  //                                VT_EMPTY.
+  // @param response_headers        HTTP response headers, returned as SAFEARRAY
+  //                                of BSTR. The values corresponding one-to-one
+  //                                with the response_headers_needed values. Can
+  //                                be NULL if response_headers_needed==VT_EMPTY
+  // @param response_code           HTTP response code.
+  // @param cache_filename          Cache file that contains the response data.
+  HRESULT Send([in] BSTR url,
+               [in] BSTR post_data,
+               [in] BSTR request_headers,
+               [in] VARIANT response_headers_needed,
+               [out] VARIANT* response_headers,
+               [out] DWORD* response_code,
+               [out] BSTR* cache_filename);
+};
+
+[
+  object,
+  oleautomation,
+  uuid(28839a52-cddf-4c7c-ac21-b1c4fc1be7ef),
+  helpstring("Google Update IProcessLauncher Interface"),
+  pointer_default(unique)
+]
+interface IProcessLauncher : IUnknown {
+  // @param cmd_line The full command line to execute.
+  HRESULT LaunchCmdLine([in, string] const WCHAR* cmd_line);
+
+  // @param browser_type The browser to start.
+  // @param url The url to launch the browser with.
+  HRESULT LaunchBrowser([in] DWORD browser_type,
+                        [in, string] const WCHAR* url);
+
+  // @param app_id Unique id to identify the calling client application
+  // @param event_id Unique id for the command
+  // @param caller_proc_id The process id of the calling process
+  // @param proc_handle The process handle valid in the caller's context
+  HRESULT LaunchCmdElevated([in, string] const WCHAR* app_guid,
+                            [in, string] const WCHAR* cmd_id,
+                            [in] DWORD caller_proc_id,
+                            [out] ULONG_PTR* proc_handle);
+};
+
+[
+  object,
+  oleautomation,
+  uuid(4d3a05b9-5ef2-46f9-a4fe-81b01637ed47),
+  helpstring("Google Update IOneClickProcessLauncher Interface"),
+  pointer_default(unique)
+]
+interface IOneClickProcessLauncher : IUnknown {
+  HRESULT LaunchAppCommand([in, string] const WCHAR* app_guid,
+                           [in, string] const WCHAR* cmd_id);
+};
+
+typedef enum {
+  COMPLETION_CODE_SUCCESS = 1,
+  COMPLETION_CODE_SUCCESS_CLOSE_UI,
+  COMPLETION_CODE_ERROR,
+  COMPLETION_CODE_RESTART_ALL_BROWSERS,
+  COMPLETION_CODE_REBOOT,
+  COMPLETION_CODE_RESTART_BROWSER,
+  COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY,
+  COMPLETION_CODE_REBOOT_NOTICE_ONLY,
+  COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY,
+  COMPLETION_CODE_RUN_COMMAND,
+} LegacyCompletionCodes;
+
+[
+  object,
+  oleautomation,
+  uuid(cd858925-6866-4f02-b9e9-b5b08e353be7),
+  helpstring("GoogleUpdate UI-specific events Interface"),
+  pointer_default(unique)
+]
+interface IProgressWndEvents : IUnknown {
+  // The UI is closing down. The user has clicked on either the "X" or the
+  // other buttons of the UI to close the window.
+  HRESULT DoClose();
+
+  // Pause has been clicked on.
+  HRESULT DoPause();
+
+  // Resume has been clicked on.
+  HRESULT DoResume();
+
+  // RestartBrowsers button has been clicked on.
+  HRESULT DoRestartBrowsers();
+
+  // Reboot button has been clicked on.
+  HRESULT DoReboot();
+
+  // Launch Browser.
+  HRESULT DoLaunchBrowser([in, string] const WCHAR* url);
+};
+
+
+[
+  object,
+  oleautomation,
+  uuid(ff612905-d787-4f53-ab5e-5ab5971b10a7),
+  helpstring("IJobObserver Interface"),
+  pointer_default(unique)
+]
+interface IJobObserver : IUnknown {
+  HRESULT OnShow();
+  HRESULT OnCheckingForUpdate();
+  HRESULT OnUpdateAvailable([in, string] const WCHAR* version_string);
+  HRESULT OnWaitingToDownload();
+  HRESULT OnDownloading([in] int time_remaining_ms, [in] int pos);
+  HRESULT OnWaitingToInstall();
+  HRESULT OnInstalling();
+  HRESULT OnPause();
+  HRESULT OnComplete([in] LegacyCompletionCodes code,
+                     [in, string] const WCHAR* reserved);
+  HRESULT SetEventSink([in] IProgressWndEvents* ui_sink);
+};
+
+[
+  object,
+  oleautomation,
+  uuid(a62c03e7-56ef-4109-a920-d3c06665223e),
+  helpstring("IGoogleUpdate Interface"),
+  pointer_default(unique)
+]
+interface IGoogleUpdate : IUnknown {
+  // @param guid The guid for the app to be updated.
+  // @param observer The eventing interface.
+  HRESULT CheckForUpdate([in, string] const WCHAR* guid,
+                         [in] IJobObserver* observer);
+
+  // @param guid The guid for the app to be updated.
+  // @param observer The eventing interface.
+  HRESULT Update([in, string] const WCHAR* guid,
+                 [in] IJobObserver* observer);
+};
+
+// IGoogleUpdateCore is an internal Omaha interface.
+[
+  object,
+  oleautomation,
+  uuid(857a3d7a-6b13-45f3-a19b-75870d0c2823),
+  helpstring("Google Update Core Interface"),
+  pointer_default(unique)
+]
+interface IGoogleUpdateCore : IUnknown
+{
+  // Runs a command elevated.
+  //
+  // @param app_id Unique id to identify the calling client application
+  // @param event_id Unique id for the command
+  // @param caller_proc_id The process id of the calling process
+  // @param proc_handle The process handle valid in the caller's context
+  HRESULT LaunchCmdElevated([in, string] const WCHAR* app_guid,
+                            [in, string] const WCHAR* cmd_id,
+                            [in] DWORD caller_proc_id,
+                            [out] ULONG_PTR* proc_handle);
+};
+
+// END Legacy google_update_idl interfaces.
+
+[
+  uuid(5e3de9e9-0248-4fab-ac1c-01b86cf9790e),
+  version(1.0),
+  helpstring("Omaha 3.0 Type Library")
+]
+library GoogleUpdate3Lib {
+  importlib("stdole2.tlb");
+
+  // These Interfaces are forward declared to ensure that they are described in
+  // the generated TLB file. This is required for ATL to correctly implement the
+  // corresponding IDispatch interfaces.
+  interface IGoogleUpdate3;
+  interface IAppBundle;
+  interface IApp;
+  interface IAppVersion;
+  interface IPackage;
+  interface ICurrentState;
+
+  interface IGoogleUpdate3Web;
+  interface IAppBundleWeb;
+  interface IAppWeb;
+  interface IAppVersionWeb;
+  interface ICoCreateAsyncStatus;
+
+  [
+    uuid(cec92c56-68e4-4494-ba79-eb3a71ad8473),
+    helpstring("GoogleUpdate3 Class for per-user applications")
+  ]
+  coclass GoogleUpdate3UserClass {
+    [default] interface IDispatch;
+  }
+
+  [
+    uuid(edc2caa1-2001-4835-a04a-565a5cde5f7f),
+    helpstring("GoogleUpdate3 Service Class for machine applications")
+  ]
+  coclass GoogleUpdate3ServiceClass {
+    [default] interface IDispatch;
+  }
+
+  [
+    uuid(b5bae3dd-809e-4be3-a421-f5d3f17a03db),
+    helpstring("GoogleUpdate3Web for user applications")
+  ]
+  coclass GoogleUpdate3WebUserClass {
+    [default] interface IDispatch;
+  }
+
+  [
+    uuid(31e52f9c-5dba-4a20-b7d4-4ade46fbf9e9),
+    helpstring("Pass-through broker for the GoogleUpdate3WebServiceClass")
+  ]
+  coclass GoogleUpdate3WebMachineClass {
+    [default] interface IDispatch;
+  }
+
+  [
+    uuid(2e119b1c-8a13-4e21-92c2-1b34cab1f028),
+    helpstring("GoogleUpdate3Web")
+  ]
+  coclass GoogleUpdate3WebServiceClass {
+    [default] interface IDispatch;
+  }
+
+  [
+    uuid(0e2ee4d0-7ef1-45d0-aae8-2506c0ba649c),
+    helpstring("Fallback mechanism if GoogleUpdate3WebServiceClass fails")
+  ]
+  coclass GoogleUpdate3WebMachineFallbackClass {
+    [default] interface IDispatch;
+  }
+
+  [
+    uuid(733eb758-7cf0-4927-a3b9-75eb28490613),
+    helpstring("CurrentStateUserClass")
+  ]
+  coclass CurrentStateUserClass {
+    [default] interface ICurrentState;
+  }
+
+  [
+    uuid(dad290ab-07df-443d-9a5a-4434474353ed),
+    helpstring("CurrentStateMachineClass")
+  ]
+  coclass CurrentStateMachineClass {
+    [default] interface ICurrentState;
+  }
+
+  [
+    uuid(5e79da64-77df-4c1f-9184-c6b5f035e3d9),
+    helpstring("CoCreateAsyncClass")
+  ]
+  coclass CoCreateAsyncClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(a663a8e3-b533-4153-8ef6-3c5823de9280),
+    helpstring("CredentialDialogUserClass")
+  ]
+  coclass CredentialDialogUserClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(341390f2-6214-4981-a2ae-738205e73590),
+    helpstring("CredentialDialogMachineClass")
+  ]
+  coclass CredentialDialogMachineClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(___AUTO_GENERATED_GUID___),
+    helpstring("GoogleComProxyMachineClass")
+  ]
+  coclass GoogleComProxyMachineClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(___AUTO_GENERATED_GUID___),
+    helpstring("GoogleComProxyUserClass")
+  ]
+  coclass GoogleComProxyUserClass {
+    [default] interface IUnknown;
+  }
+
+  // BEGIN Legacy google_update_idl coclasses.
+
+  [
+    uuid(f235f88c-3de7-4195-a0e9-105ba38344cc),
+    helpstring("ProcessLauncherClass Class")
+  ]
+  coclass ProcessLauncherClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(bb42bfd1-b0eb-449c-8882-fb810c2738d0),
+    helpstring("OneClickUserProcessLauncherClass Class")
+  ]
+  coclass OneClickUserProcessLauncherClass {
+    [default] interface IOneClickProcessLauncher;
+  }
+
+  [
+    uuid(5457eff7-c0b3-4672-a6b4-58f93951f1e0),
+    helpstring("OneClickMachineProcessLauncherClass Class")
+  ]
+  coclass OneClickMachineProcessLauncherClass {
+    [default] interface IOneClickProcessLauncher;
+  }
+
+  [
+    uuid(660baf72-a9ef-4fb1-81ae-9395ca6c39cf),
+    helpstring("OnDemand updates for per-user applications.")
+  ]
+  coclass OnDemandUserAppsClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(0dd31efc-0cf5-4ec5-9799-f1a8819acea4),
+    helpstring("OnDemand pass-through broker for machine applications.")
+  ]
+  coclass OnDemandMachineAppsClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(19c06b1a-7202-40b4-9903-62927d08f943),
+    helpstring("OnDemand updates for per-machine applications.")
+  ]
+  coclass OnDemandMachineAppsServiceClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(107e2437-bfe7-4e17-91d9-2866e1373e00),
+    helpstring("Fallback for if OnDemandMachineAppsServiceClass fails.")
+  ]
+  coclass OnDemandMachineAppsFallbackClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(b0f72540-f2a8-4027-aac8-a39deb0470ad),
+    helpstring("GoogleUpdateCore Class")
+  ]
+  coclass GoogleUpdateCoreClass
+  {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(b15e7b58-2a81-4f3e-8381-f5db7d3a1f84),
+    helpstring("GoogleUpdateCore Machine Class")
+  ]
+  coclass GoogleUpdateCoreMachineClass
+  {
+    [default] interface IUnknown;
+  }
+
+  // END Legacy google_update_idl coclasses.
+};
diff --git a/goopdate/omaha3_idl_datax.c b/goopdate/omaha3_idl_datax.c
new file mode 100644
index 0000000..e0829cc
--- /dev/null
+++ b/goopdate/omaha3_idl_datax.c
@@ -0,0 +1,50 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+//
+// Wrapper for the MIDL-generated proxy/stub.
+
+#pragma warning(push)
+// C4152: nonstandard extension, function/data pointer conversion in expression
+#pragma warning(disable : 4152)
+
+#define REGISTER_PROXY_DLL
+#define USE_STUBLESS_PROXY
+#define PROXY_DELEGATION
+#define ENTRY_PREFIX      Prx
+
+// PROXY_CLSID_IS_MACHINE/USER is defined in main.scons.
+#undef PROXY_CLSID_IS
+#if IS_MACHINE_HANDLER
+  #define PROXY_CLSID_IS  PROXY_CLSID_IS_MACHINE
+#else
+  #define PROXY_CLSID_IS  PROXY_CLSID_IS_USER
+#endif
+
+// Undefine the __purecall provided by rpcproxy.h so that it does not conflict
+// with the libc definition.
+#include <rpcproxy.h>
+#ifdef DLLDUMMYPURECALL
+#undef DLLDUMMYPURECALL
+#define DLLDUMMYPURECALL
+#endif
+
+#include "goopdate/omaha3_idl_data.c"
+#include "goopdate/omaha3_idl_p.c"
+
+#undef PROXY_CLSID_IS
+
+#pragma warning(pop)
+
diff --git a/goopdate/omaha3_idl_datax.h b/goopdate/omaha3_idl_datax.h
new file mode 100644
index 0000000..94b7699
--- /dev/null
+++ b/goopdate/omaha3_idl_datax.h
@@ -0,0 +1,31 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+//
+// Declarations for the MIDL-generated entry points for the proxy/stub.
+
+#ifndef OMAHA_GOOPDATE_OMAHA3_IDL_DATAX_H_
+#define OMAHA_GOOPDATE_OMAHA3_IDL_DATAX_H_
+
+extern "C" {
+  BOOL WINAPI PrxDllMain(HINSTANCE instance, DWORD reason, LPVOID res);
+  STDAPI PrxDllCanUnloadNow();
+  STDAPI PrxDllGetClassObject(REFCLSID refclsid, REFIID refiid, LPVOID* ptr);
+  STDAPI PrxDllRegisterServer();
+  STDAPI PrxDllUnregisterServer();
+}
+
+#endif  // OMAHA_GOOPDATE_OMAHA3_IDL_DATAX_H_
+
diff --git a/goopdate/omaha_customization_goopdate_apis_unittest.cc b/goopdate/omaha_customization_goopdate_apis_unittest.cc
new file mode 100644
index 0000000..0d856bf
--- /dev/null
+++ b/goopdate/omaha_customization_goopdate_apis_unittest.cc
@@ -0,0 +1,661 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Tests the constants that vary depending on the customization of Omaha.
+// The test checks for the Google Update variations, but can be modified for
+// your purposes.
+
+#include <windows.h>
+#include <tchar.h>
+#include <atlbase.h>
+#include <oleauto.h>
+#include "omaha/base/browser_utils.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/const_goopdate.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/testing/omaha_customization_test.h"
+
+// TODO(omaha): Add tests for to detect interface changes that would require
+// rolling _OMAHA3_IDL_PROXY_CLSID_IS. These include:
+// 1) interface changes invovlving the number or signature of methods
+// 2) or that new interfaces have been added
+// For #2, we already have the InvalidIndex test for interfaces in the TypeLib,
+// so we just need to add checks for interfaces not in the TypeLib.
+//
+// ITypeLib and ITypeInfo methods might be useful. See:
+// http://msdn.microsoft.com/en-us/library/aa912648.aspx
+// http://msdn.microsoft.com/en-us/library/aa909031.aspx
+//
+// I do not know how to get information about interfaces not in a TypeLib.
+// Fortunately, most Omaha 3 interfaces are in one.
+//
+// If we can not get all the information we need, we can always save a "golden"
+// idl.h file and diff against it.
+
+
+// Most of the tests are intentionally not using the omaha namespace. Most of
+// the values being tested are not in this namespace, and being in the global
+// namespace is required by TEST_GU_INT_F to catch conflicts with Google types
+// when building non-Google versions.
+
+class OmahaCustomizationGoopdateComInterfaceTest
+    : public OmahaCustomizationTypeLibComInterfaceTest {
+ protected:
+  OmahaCustomizationGoopdateComInterfaceTest()
+      : OmahaCustomizationTypeLibComInterfaceTest(omaha::kOmahaDllName) {
+  }
+};
+
+// Fixture for testing interfaces that are not in a TypeLib.
+// We can only verify the uuid of the interfaces and classes.
+class OmahaCustomizationGoopdateComInterfaceNoTypeLibTest
+    : public testing::Test {
+};
+
+//
+// Omaha 3 COM Constants.
+//
+
+namespace omaha {
+
+// TODO(omaha): We should probably move these to a separate
+// const_com_customization.h in goopdate\.
+TEST(OmahaCustomizationTest, Constants_ComProgIds) {
+  EXPECT_GU_STREQ(_T("GoogleUpdate.OnDemandCOMClassUser"), kProgIDOnDemandUser);
+  EXPECT_GU_STREQ(_T("GoogleUpdate.OnDemandCOMClassMachine"),
+                  kProgIDOnDemandMachine);
+  EXPECT_GU_STREQ(_T("GoogleUpdate.OnDemandCOMClassSvc"), kProgIDOnDemandSvc);
+
+  EXPECT_GU_STREQ(_T("GoogleUpdate.Update3WebUser"), kProgIDUpdate3WebUser);
+  EXPECT_GU_STREQ(_T("GoogleUpdate.Update3WebMachine"),
+                  kProgIDUpdate3WebMachine);
+  EXPECT_GU_STREQ(_T("GoogleUpdate.Update3WebSvc"), kProgIDUpdate3WebSvc);
+
+  EXPECT_GU_STREQ(_T("GoogleUpdate.CoreClass"), kProgIDGoogleUpdateCoreService);
+
+  EXPECT_GU_STREQ(_T("GoogleUpdate.ProcessLauncher"), kProgIDProcessLauncher);
+}
+
+}  // namespace omaha
+
+//
+// Omaha 3 COM Interfaces Enums.
+//
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceNoTypeLibTest, BrowserType) {
+  EXPECT_EQ(0, BROWSER_UNKNOWN);
+  EXPECT_EQ(1, BROWSER_DEFAULT);
+  EXPECT_EQ(2, BROWSER_INTERNET_EXPLORER);
+  EXPECT_EQ(3, BROWSER_FIREFOX);
+  EXPECT_EQ(4, BROWSER_CHROME);
+}
+
+// There are two different BrowserType definitions, one in the IDL and one
+// in browser_utils. Verify they are identical.
+TEST_F(OmahaCustomizationGoopdateComInterfaceNoTypeLibTest,
+       BrowserType_DefinitionsMatch) {
+  EXPECT_EQ(::BROWSER_UNKNOWN,            omaha::BROWSER_UNKNOWN);
+  EXPECT_EQ(::BROWSER_DEFAULT,            omaha::BROWSER_DEFAULT);
+  EXPECT_EQ(::BROWSER_INTERNET_EXPLORER,  omaha::BROWSER_IE);
+  EXPECT_EQ(::BROWSER_FIREFOX,            omaha::BROWSER_FIREFOX);
+  EXPECT_EQ(::BROWSER_CHROME,             omaha::BROWSER_CHROME);
+
+  EXPECT_EQ(::BROWSER_CHROME + 1, omaha::BROWSER_MAX)
+      << _T("A browser has been added without updating test and/or the IDL");
+}
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceNoTypeLibTest, CurrentState) {
+  EXPECT_EQ(1,  STATE_INIT);
+  EXPECT_EQ(2,  STATE_WAITING_TO_CHECK_FOR_UPDATE);
+  EXPECT_EQ(3,  STATE_CHECKING_FOR_UPDATE);
+  EXPECT_EQ(4,  STATE_UPDATE_AVAILABLE);
+  EXPECT_EQ(5,  STATE_WAITING_TO_DOWNLOAD);
+  EXPECT_EQ(6,  STATE_RETRYING_DOWNLOAD);
+  EXPECT_EQ(7,  STATE_DOWNLOADING);
+  EXPECT_EQ(8,  STATE_DOWNLOAD_COMPLETE);
+  EXPECT_EQ(9,  STATE_EXTRACTING);
+  EXPECT_EQ(10, STATE_APPLYING_DIFFERENTIAL_PATCH);
+  EXPECT_EQ(11, STATE_READY_TO_INSTALL);
+  EXPECT_EQ(12, STATE_WAITING_TO_INSTALL);
+  EXPECT_EQ(13, STATE_INSTALLING);
+  EXPECT_EQ(14, STATE_INSTALL_COMPLETE);
+  EXPECT_EQ(15, STATE_PAUSED);
+  EXPECT_EQ(16, STATE_NO_UPDATE);
+  EXPECT_EQ(17, STATE_ERROR);
+}
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceNoTypeLibTest, InstallPriority) {
+  EXPECT_EQ(0,  INSTALL_PRIORITY_LOW);
+  EXPECT_EQ(10, INSTALL_PRIORITY_HIGH);
+}
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceNoTypeLibTest, PostInstallAction) {
+  EXPECT_EQ(0, POST_INSTALL_ACTION_DEFAULT);
+  EXPECT_EQ(1, POST_INSTALL_ACTION_EXIT_SILENTLY);
+  EXPECT_EQ(2, POST_INSTALL_ACTION_LAUNCH_COMMAND);
+  EXPECT_EQ(3, POST_INSTALL_ACTION_EXIT_SILENTLY_ON_LAUNCH_COMMAND);
+  EXPECT_EQ(4, POST_INSTALL_ACTION_RESTART_BROWSER);
+  EXPECT_EQ(5, POST_INSTALL_ACTION_RESTART_ALL_BROWSERS);
+  EXPECT_EQ(6, POST_INSTALL_ACTION_REBOOT);
+}
+
+//
+// Omaha 3 COM Interfaces.
+//
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceTest, TypeLib) {
+  EXPECT_GU_ID_EQ(_T("{655DD85A-3C0D-4674-9C58-AF7168C5861E}"),
+                  LIBID_GoogleUpdate3Lib);
+
+  EXPECT_SUCCEEDED(GetDocumentation(-1));
+  EXPECT_STREQ(_T("GoogleUpdate3Lib"), item_name_);
+  EXPECT_GU_STREQ(_T("Google Update 3.0 Type Library"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest, IGoogleUpdate3) {
+  // TODO(omaha): Test uuid constants after extracting from IDLs.
+  EXPECT_GU_ID_EQ(_T("{6DB17455-4E85-46e7-9D23-E555E4B005AF}"),
+                  __uuidof(IGoogleUpdate3));
+
+  EXPECT_SUCCEEDED(GetDocumentation(0));
+  EXPECT_STREQ(_T("IGoogleUpdate3"), item_name_);
+  EXPECT_STREQ(_T("IGoogleUpdate3 Interface"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+// The IAppBundle interface name does not change for non-Google builds, but the
+// ID must. The same is true for many of the interfaces.
+TEST_F(OmahaCustomizationGoopdateComInterfaceTest, IAppBundle) {
+  EXPECT_GU_ID_EQ(_T("{313cfb25-4888-4fc6-9e19-764d8c5fc8f8}"),
+                  __uuidof(IAppBundle));
+
+  EXPECT_SUCCEEDED(GetDocumentation(1));
+  EXPECT_STREQ(_T("IAppBundle"), item_name_);
+  EXPECT_STREQ(_T("IAppBundle Interface"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+// This appears in the typelib for unknown reasons.
+TEST_F(OmahaCustomizationGoopdateComInterfaceTest, ULONG_PTR) {
+  EXPECT_SUCCEEDED(GetDocumentation(2));
+  EXPECT_STREQ(_T("ULONG_PTR"), item_name_);
+  EXPECT_TRUE(!item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceTest, IApp) {
+  EXPECT_GU_ID_EQ(_T("{D999CE21-98B3-4894-BACB-A49A1D50848F}"),
+                  __uuidof(IApp));
+
+  EXPECT_SUCCEEDED(GetDocumentation(3));
+  EXPECT_STREQ(_T("IApp"), item_name_);
+  EXPECT_STREQ(_T("IApp Interface"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceTest, IAppVersion) {
+  EXPECT_GU_ID_EQ(_T("{BCDCB538-01C0-46d1-A6A7-52F4D021C272}"),
+                  __uuidof(IAppVersion));
+
+  EXPECT_SUCCEEDED(GetDocumentation(4));
+  EXPECT_STREQ(_T("IAppVersion"), item_name_);
+  EXPECT_STREQ(_T("IAppVersion Interface"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceTest, IPackage) {
+  EXPECT_GU_ID_EQ(_T("{DCAB8386-4F03-4dbd-A366-D90BC9F68DE6}"),
+                  __uuidof(IPackage));
+
+  EXPECT_SUCCEEDED(GetDocumentation(5));
+  EXPECT_STREQ(_T("IPackage"), item_name_);
+  EXPECT_STREQ(_T("IPackage Interface"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceTest, ICurrentState) {
+  EXPECT_GU_ID_EQ(_T("{247954F9-9EDC-4E68-8CC3-150C2B89EADF}"),
+                  __uuidof(ICurrentState));
+
+  EXPECT_SUCCEEDED(GetDocumentation(6));
+  EXPECT_STREQ(_T("ICurrentState"), item_name_);
+  EXPECT_STREQ(_T("ICurrentState Interface"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+// Not in the TypeLib because it derives from IUnknown.
+TEST_F(OmahaCustomizationGoopdateComInterfaceNoTypeLibTest,
+       IRegistrationUpdateHook) {
+  EXPECT_GU_ID_EQ(_T("{4E223325-C16B-4eeb-AEDC-19AA99A237FA}"),
+                  __uuidof(IRegistrationUpdateHook));
+}
+
+// Not in the TypeLib because it derives from IUnknown.
+TEST_F(OmahaCustomizationGoopdateComInterfaceNoTypeLibTest, ICoCreateAsync) {
+  EXPECT_GU_ID_EQ(_T("{DAB1D343-1B2A-47f9-B445-93DC50704BFE}"),
+                  __uuidof(ICoCreateAsync));
+}
+
+// Not in the TypeLib because it derives from IUnknown.
+TEST_F(OmahaCustomizationGoopdateComInterfaceNoTypeLibTest, ICredentialDialog) {
+  EXPECT_GU_ID_EQ(_T("{b3a47570-0a85-4aea-8270-529d47899603}"),
+                  __uuidof(ICredentialDialog));
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest, IGoogleUpdate3Web) {
+  EXPECT_GU_ID_EQ(_T("{494B20CF-282E-4BDD-9F5D-B70CB09D351E}"),
+                  __uuidof(IGoogleUpdate3Web));
+
+  EXPECT_SUCCEEDED(GetDocumentation(7));
+  EXPECT_STREQ(_T("IGoogleUpdate3Web"), item_name_);
+  EXPECT_STREQ(_T("IGoogleUpdate3Web Interface"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+// Not in the TypeLib because it derives from IUnknown.
+TEST_F(OmahaCustomizationGoopdateComInterfaceNoTypeLibTest,
+       IGoogleUpdate3WebSecurity) {
+  EXPECT_GU_ID_EQ(_T("{2D363682-561D-4c3a-81C6-F2F82107562A}"),
+                  __uuidof(IGoogleUpdate3WebSecurity));
+}
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceTest, IAppBundleWeb) {
+  EXPECT_GU_ID_EQ(_T("{DD42475D-6D46-496a-924E-BD5630B4CBBA}"),
+                  __uuidof(IAppBundleWeb));
+
+  EXPECT_SUCCEEDED(GetDocumentation(8));
+  EXPECT_STREQ(_T("IAppBundleWeb"), item_name_);
+  EXPECT_STREQ(_T("IAppBundleWeb Interface"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceTest, IAppWeb) {
+  EXPECT_GU_ID_EQ(_T("{C6398F88-69CE-44ac-B6A7-1D3E2AA46679}"),
+                  __uuidof(IAppWeb));
+
+  EXPECT_SUCCEEDED(GetDocumentation(9));
+  EXPECT_STREQ(_T("IAppWeb"), item_name_);
+  EXPECT_STREQ(_T("IAppWeb Interface"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceTest, IAppVersionWeb) {
+  EXPECT_GU_ID_EQ(_T("{0CD01D1E-4A1C-489d-93B9-9B6672877C57}"),
+                  __uuidof(IAppVersionWeb));
+
+  EXPECT_SUCCEEDED(GetDocumentation(10));
+  EXPECT_STREQ(_T("IAppVersionWeb"), item_name_);
+  EXPECT_STREQ(_T("IAppVersionWeb Interface"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceTest, ICoCreateAsyncStatus) {
+  EXPECT_GU_ID_EQ(_T("{2E629606-312A-482f-9B12-2C4ABF6F0B6D}"),
+                  __uuidof(ICoCreateAsyncStatus));
+
+  EXPECT_SUCCEEDED(GetDocumentation(11));
+  EXPECT_STREQ(_T("ICoCreateAsyncStatus"), item_name_);
+  EXPECT_STREQ(_T("ICoCreateAsyncStatus Interface"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              GoogleUpdate3UserClass) {
+  EXPECT_GU_ID_EQ(_T("{022105BD-948A-40c9-AB42-A3300DDF097F}"),
+                  __uuidof(GoogleUpdate3UserClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(12));
+  EXPECT_STREQ(_T("GoogleUpdate3UserClass"), item_name_);
+  EXPECT_STREQ(_T("GoogleUpdate3 Class for per-user applications"),
+               item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              GoogleUpdate3ServiceClass) {
+  EXPECT_GU_ID_EQ(_T("{4EB61BAC-A3B6-4760-9581-655041EF4D69}"),
+                  __uuidof(GoogleUpdate3ServiceClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(13));
+  EXPECT_STREQ(_T("GoogleUpdate3ServiceClass"), item_name_);
+  EXPECT_STREQ(_T("GoogleUpdate3 Service Class for machine applications"),
+               item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              GoogleUpdate3WebUserClass) {
+  EXPECT_GU_ID_EQ(_T("{22181302-A8A6-4f84-A541-E5CBFC70CC43}"),
+                  __uuidof(GoogleUpdate3WebUserClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(14));
+  EXPECT_STREQ(_T("GoogleUpdate3WebUserClass"), item_name_);
+  EXPECT_STREQ(_T("GoogleUpdate3Web for user applications"),
+                  item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              GoogleUpdate3WebMachineClass) {
+  EXPECT_GU_ID_EQ(_T("{8A1D4361-2C08-4700-A351-3EAA9CBFF5E4}"),
+                  __uuidof(GoogleUpdate3WebMachineClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(15));
+  EXPECT_STREQ(_T("GoogleUpdate3WebMachineClass"), item_name_);
+  EXPECT_STREQ(
+      _T("Pass-through broker for the GoogleUpdate3WebServiceClass"),
+      item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              GoogleUpdate3WebServiceClass) {
+  EXPECT_GU_ID_EQ(_T("{534F5323-3569-4f42-919D-1E1CF93E5BF6}"),
+                  __uuidof(GoogleUpdate3WebServiceClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(16));
+  EXPECT_STREQ(_T("GoogleUpdate3WebServiceClass"), item_name_);
+  EXPECT_STREQ(_T("GoogleUpdate3Web"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              GoogleUpdate3WebMachineFallbackClass) {
+  EXPECT_GU_ID_EQ(_T("{598FE0E5-E02D-465d-9A9D-37974A28FD42}"),
+                  __uuidof(GoogleUpdate3WebMachineFallbackClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(17));
+  EXPECT_STREQ(_T("GoogleUpdate3WebMachineFallbackClass"), item_name_);
+  EXPECT_STREQ(L"Fallback mechanism if GoogleUpdate3WebServiceClass fails",
+               item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              CurrentStateUserClass) {
+  EXPECT_GU_ID_EQ(_T("{E8CF3E55-F919-49d9-ABC0-948E6CB34B9F}"),
+                  __uuidof(CurrentStateUserClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(18));
+  EXPECT_STREQ(_T("CurrentStateUserClass"), item_name_);
+  EXPECT_STREQ(_T("CurrentStateUserClass"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              CurrentStateMachineClass) {
+  EXPECT_GU_ID_EQ(_T("{9D6AA569-9F30-41ad-885A-346685C74928}"),
+                  __uuidof(CurrentStateMachineClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(19));
+  EXPECT_STREQ(_T("CurrentStateMachineClass"), item_name_);
+  EXPECT_STREQ(_T("CurrentStateMachineClass"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              CoCreateAsyncClass) {
+  EXPECT_GU_ID_EQ(_T("{7DE94008-8AFD-4c70-9728-C6FBFFF6A73E}"),
+                  __uuidof(CoCreateAsyncClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(20));
+  EXPECT_STREQ(_T("CoCreateAsyncClass"), item_name_);
+  EXPECT_STREQ(_T("CoCreateAsyncClass"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              CredentialDialogUserClass) {
+  EXPECT_GU_ID_EQ(_T("{e67be843-bbbe-4484-95fb-05271ae86750}"),
+                  __uuidof(CredentialDialogUserClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(21));
+  EXPECT_STREQ(_T("CredentialDialogUserClass"), item_name_);
+  EXPECT_STREQ(_T("CredentialDialogUserClass"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              CredentialDialogMachineClass) {
+  EXPECT_GU_ID_EQ(_T("{25461599-633d-42b1-84fb-7cd68d026e53}"),
+                  __uuidof(CredentialDialogMachineClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(22));
+  EXPECT_STREQ(_T("CredentialDialogMachineClass"), item_name_);
+  EXPECT_STREQ(_T("CredentialDialogMachineClass"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              GoogleComProxyMachineClass) {
+  EXPECT_SUCCEEDED(GetDocumentation(23));
+  EXPECT_STREQ(_T("GoogleComProxyMachineClass"), item_name_);
+  EXPECT_STREQ(_T("GoogleComProxyMachineClass"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              GoogleComProxyUserClass) {
+  EXPECT_SUCCEEDED(GetDocumentation(24));
+  EXPECT_STREQ(_T("GoogleComProxyUserClass"), item_name_);
+  EXPECT_STREQ(_T("GoogleComProxyUserClass"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              ProcessLauncherClass) {
+  EXPECT_GU_ID_EQ(_T("{ABC01078-F197-4b0b-ADBC-CFE684B39C82}"),
+                  __uuidof(ProcessLauncherClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(25));
+  EXPECT_STREQ(_T("ProcessLauncherClass"), item_name_);
+  EXPECT_STREQ(_T("ProcessLauncherClass Class"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              OneClickUserProcessLauncherClass) {
+  EXPECT_GU_ID_EQ(_T("{51F9E8EF-59D7-475b-A106-C7EA6F30C119}"),
+                  __uuidof(OneClickUserProcessLauncherClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(26));
+  EXPECT_STREQ(_T("OneClickUserProcessLauncherClass"), item_name_);
+  EXPECT_STREQ(_T("OneClickUserProcessLauncherClass Class"),
+               item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              IOneClickProcessLauncher) {
+  EXPECT_GU_ID_EQ(_T("{5CCCB0EF-7073-4516-8028-4C628D0C8AAB}"),
+                  __uuidof(IOneClickProcessLauncher));
+
+  EXPECT_SUCCEEDED(GetDocumentation(27));
+  EXPECT_STREQ(_T("IOneClickProcessLauncher"), item_name_);
+  EXPECT_STREQ(_T("Google Update IOneClickProcessLauncher Interface"),
+               item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              OneClickMachineProcessLauncherClass) {
+  EXPECT_GU_ID_EQ(_T("{AAD4AE2E-D834-46d4-8B09-490FAC9C722B}"),
+                  __uuidof(OneClickMachineProcessLauncherClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(28));
+  EXPECT_STREQ(_T("OneClickMachineProcessLauncherClass"), item_name_);
+  EXPECT_STREQ(_T("OneClickMachineProcessLauncherClass Class"),
+               item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              OnDemandUserAppsClass) {
+  EXPECT_GU_ID_EQ(_T("{2F0E2680-9FF5-43c0-B76E-114A56E93598}"),
+                  __uuidof(OnDemandUserAppsClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(29));
+  EXPECT_STREQ(_T("OnDemandUserAppsClass"), item_name_);
+  EXPECT_STREQ(_T("OnDemand updates for per-user applications."),
+               item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              OnDemandMachineAppsClass) {
+  EXPECT_GU_ID_EQ(_T("{6F8BD55B-E83D-4a47-85BE-81FFA8057A69}"),
+                  __uuidof(OnDemandMachineAppsClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(30));
+  EXPECT_STREQ(_T("OnDemandMachineAppsClass"), item_name_);
+  EXPECT_STREQ(_T("OnDemand pass-through broker for machine applications."),
+               item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              OnDemandMachineAppsServiceClass) {
+  EXPECT_GU_ID_EQ(_T("{9465B4B4-5216-4042-9A2C-754D3BCDC410}"),
+                  __uuidof(OnDemandMachineAppsServiceClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(31));
+  EXPECT_STREQ(_T("OnDemandMachineAppsServiceClass"), item_name_);
+  EXPECT_STREQ(_T("OnDemand updates for per-machine applications."),
+               item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              OnDemandMachineAppsFallbackClass) {
+  EXPECT_GU_ID_EQ(_T("{B3D28DBD-0DFA-40e4-8071-520767BADC7E}"),
+                  __uuidof(OnDemandMachineAppsFallbackClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(32));
+  EXPECT_STREQ(_T("OnDemandMachineAppsFallbackClass"), item_name_);
+  EXPECT_STREQ(_T("Fallback for if OnDemandMachineAppsServiceClass fails."),
+               item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              GoogleUpdateCoreClass) {
+  EXPECT_GU_ID_EQ(_T("{E225E692-4B47-4777-9BED-4FD7FE257F0E}"),
+                  __uuidof(GoogleUpdateCoreClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(33));
+  EXPECT_STREQ(_T("GoogleUpdateCoreClass"), item_name_);
+  EXPECT_STREQ(_T("GoogleUpdateCore Class"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationGoopdateComInterfaceTest,
+              GoogleUpdateCoreMachineClass) {
+  EXPECT_GU_ID_EQ(_T("{9B2340A0-4068-43d6-B404-32E27217859D}"),
+                  __uuidof(GoogleUpdateCoreMachineClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(34));
+  EXPECT_STREQ(_T("GoogleUpdateCoreMachineClass"), item_name_);
+  EXPECT_STREQ(_T("GoogleUpdateCore Machine Class"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+// Verifies there are no new interfaces in the TypeLib.
+TEST_F(OmahaCustomizationGoopdateComInterfaceTest, VerifyNoNewInterfaces) {
+  EXPECT_EQ(TYPE_E_ELEMENTNOTFOUND, GetDocumentation(35))
+      << _T("A new interface may have been added. If so, roll ")
+      << _T("PROXY_CLSID_IS_MACHINE/USER and GoogleComProxyMachine/UserClass, ")
+      << _T("add the interface to kIIDsToRegister, and add test(s) for new ")
+      << _T("interface(s).");
+}
+
+//
+// Omaha 2 COM Interfaces.
+//
+// TODO(omaha): We should make it so open source versions do not need these
+// legacy interfaces.
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceNoTypeLibTest,
+       IBrowserHttpRequest2) {
+  EXPECT_GU_ID_EQ(_T("{5B25A8DC-1780-4178-A629-6BE8B8DEFAA2}"),
+                  __uuidof(IBrowserHttpRequest2));
+}
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceNoTypeLibTest,
+       IProcessLauncher) {
+  EXPECT_GU_ID_EQ(_T("{128C2DA6-2BC0-44c0-B3F6-4EC22E647964}"),
+                  __uuidof(IProcessLauncher));
+}
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceNoTypeLibTest,
+       IProgressWndEvents) {
+  EXPECT_GU_ID_EQ(_T("{1C642CED-CA3B-4013-A9DF-CA6CE5FF6503}"),
+                  __uuidof(IProgressWndEvents));
+}
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceNoTypeLibTest,
+       IJobObserver) {
+  EXPECT_GU_ID_EQ(_T("{49D7563B-2DDB-4831-88C8-768A53833837}"),
+                  __uuidof(IJobObserver));
+}
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceNoTypeLibTest,
+       IGoogleUpdate) {
+  EXPECT_GU_ID_EQ(_T("{31AC3F11-E5EA-4a85-8A3D-8E095A39C27B}"),
+                  __uuidof(IGoogleUpdate));
+}
+
+TEST_F(OmahaCustomizationGoopdateComInterfaceNoTypeLibTest,
+       IGoogleUpdateCore) {
+  EXPECT_GU_ID_EQ(_T("{909489C2-85A6-4322-AA56-D25278649D67}"),
+                  __uuidof(IGoogleUpdateCore));
+}
+
diff --git a/goopdate/ondemand.cc b/goopdate/ondemand.cc
new file mode 100644
index 0000000..7a56231
--- /dev/null
+++ b/goopdate/ondemand.cc
@@ -0,0 +1,80 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/ondemand.h"
+#include "omaha/base/debug.h"
+#include "omaha/client/bundle_installer.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/lang.h"
+#include "omaha/common/update3_utils.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/goopdate/job_observer.h"
+
+namespace omaha {
+
+namespace internal {
+
+namespace {
+
+HRESULT CreateJobObserverForOnDemand(DWORD job_observer_git_cookie,
+                                     JobObserverCOMDecorator** job_observer) {
+  ASSERT1(job_observer);
+  *job_observer = NULL;
+
+  CComGITPtr<IJobObserver> job_observer_git(job_observer_git_cookie);
+  CComPtr<IJobObserver> ijob_observer;
+  HRESULT hr = job_observer_git.CopyTo(&ijob_observer);
+  ASSERT1(ijob_observer);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[job_observer_git.CopyTo failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComObject<JobObserverCOMDecorator>* job_observer_com = NULL;
+  hr = CComObject<JobObserverCOMDecorator>::CreateInstance(&job_observer_com);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[JobObserverCOMDecorator creation failed][0x%x]"), hr));
+    return hr;
+  }
+
+  job_observer_com->Initialize(ijob_observer);
+  job_observer_com->AddRef();
+  *job_observer = job_observer_com;
+  return S_OK;
+}
+
+}  // namespace
+
+HRESULT DoOnDemand(bool is_machine, OnDemandParameters on_demand_params) {
+  CComPtr<JobObserverCOMDecorator> job_observer;
+  HRESULT hr = internal::CreateJobObserverForOnDemand(
+      on_demand_params.job_observer_git_cookie, &job_observer);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CreateJobObserver failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return UpdateAppOnDemand(is_machine,
+                           on_demand_params.app_id,
+                           on_demand_params.is_update_check_only,
+                           on_demand_params.session_id,
+                           on_demand_params.impersonation_token,
+                           on_demand_params.primary_token,
+                           job_observer);
+}
+
+}  // namespace internal
+
+}  // namespace omaha
diff --git a/goopdate/ondemand.h b/goopdate/ondemand.h
new file mode 100644
index 0000000..5aaaeac
--- /dev/null
+++ b/goopdate/ondemand.h
@@ -0,0 +1,294 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// TODO(omaha3): This really is a client. Where should it and similar code live?
+
+#ifndef OMAHA_GOOPDATE_ONDEMAND_H_
+#define OMAHA_GOOPDATE_ONDEMAND_H_
+
+#include <atlbase.h>
+#include <oaidl.h>
+#include <oleauto.h>
+#include "base/basictypes.h"
+#include "base/debug.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/atlregmapex.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/preprocessor_fun.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/thread_pool_callback.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/goopdate/com_proxy.h"
+#include "omaha/goopdate/elevation_moniker_resource.h"
+#include "goopdate/omaha3_idl.h"
+// TODO(omaha3): Worker is convenient, but it is very odd to include part of the
+// Omaha 3 COM server in one of its clients.
+#include "omaha/goopdate/goopdate.h"
+
+namespace omaha {
+
+namespace internal {
+
+struct OnDemandParameters {
+ public:
+  OnDemandParameters(const CString& guid,
+                     DWORD job_observer_cookie,
+                     bool is_check_only,
+                     const CString& sess_id,
+                     HANDLE caller_impersonation_token,
+                     HANDLE caller_primary_token)
+      : app_id(guid),
+        job_observer_git_cookie(job_observer_cookie),
+        is_update_check_only(is_check_only),
+        session_id(sess_id),
+        impersonation_token(caller_impersonation_token),
+        primary_token(caller_primary_token) {
+    ASSERT1(guid.GetLength() > 0);
+    ASSERT1(IsGuid(session_id));
+    ASSERT1(job_observer_cookie);
+  }
+
+  CString app_id;
+  DWORD job_observer_git_cookie;
+  bool is_update_check_only;
+  CString session_id;
+  HANDLE impersonation_token;
+  HANDLE primary_token;
+};
+
+HRESULT DoOnDemand(bool is_machine,
+                   OnDemandParameters on_demand_params);
+
+}  // namespace internal
+
+#pragma warning(push)
+
+// Construction of local static object is not thread-safe
+#pragma warning(disable:4640)
+
+template <typename T>
+class ATL_NO_VTABLE OnDemand
+    : public CComObjectRootEx<CComObjectThreadModel>,
+      public CComCoClass<OnDemand<T> >,
+      public IGoogleUpdate,
+      public StdMarshalInfo {
+ public:
+  // IGoogleUpdate::CheckForUpdate().
+  STDMETHOD(CheckForUpdate)(const WCHAR* guid, IJobObserver* observer) {
+    return DoOnDemandInternalAsync(guid, observer, true);
+  }
+
+  // IGoogleUpdate::Update().
+  STDMETHOD(Update)(const WCHAR* guid, IJobObserver* observer) {
+    // Verify that the caller is an administrator for the machine case.
+    if (T::is_machine() &&
+        !UserRights::TokenIsAdmin(impersonation_token_.GetHandle())) {
+      CORE_LOG(LE, (_T("[User is not an admin]")));
+      return E_ACCESSDENIED;
+    }
+
+    return DoOnDemandInternalAsync(guid, observer, false);
+  }
+
+  HRESULT FinalConstruct() {
+    CORE_LOG(L2, (_T("[OnDemand::FinalConstruct]")));
+
+    if (!T::is_machine()) {
+      return S_OK;
+    }
+
+    HRESULT hr = UserRights::GetCallerToken(&impersonation_token_);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[GetCallerToken failed][0x%x]"), hr));
+      return hr;
+    }
+
+    if (!impersonation_token_.CreatePrimaryToken(&primary_token_)) {
+      HRESULT hr = HRESULTFromLastError();
+      CORE_LOG(LE, (_T("[CreatePrimaryToken failed][%d]"), hr));
+      return hr;
+    }
+
+    return S_OK;
+  }
+
+ protected:
+  OnDemand() : StdMarshalInfo(T::is_machine()) {
+    CORE_LOG(L2, (_T("[OnDemand::OnDemand]")));
+  }
+
+  virtual ~OnDemand() {
+    CORE_LOG(L2, (_T("[OnDemand::~OnDemand]")));
+  }
+
+  void FinalRelease() {
+    CORE_LOG(L2, (_T("[OnDemand::FinalRelease]")));
+  }
+
+  HRESULT DoOnDemandInternalAsync(const WCHAR* guid,
+                                  IJobObserver* observer,
+                                  bool is_update_check_only) {
+    CORE_LOG(L2, (_T("[DoOnDemandInternalAsync][%s][%d]"),
+                  guid, is_update_check_only));
+
+    CComGITPtr<IJobObserver> job_observer_git;
+    HRESULT hr = job_observer_git.Attach(observer);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[job_observer_git.Attach failed][0x%x]"), hr));
+      return hr;
+    }
+
+    CAccessToken dup_impersonation_token;
+    CAccessToken dup_primary_token;
+    if (T::is_machine()) {
+      hr = DuplicateTokenIntoCurrentProcess(::GetCurrentProcess(),
+                                            impersonation_token_.GetHandle(),
+                                            &dup_impersonation_token);
+      if (FAILED(hr)) {
+        CORE_LOG(LE, (_T("[Failed to duplicate impersonation token][0x%x]"),
+                      hr));
+        return hr;
+      }
+
+      hr = DuplicateTokenIntoCurrentProcess(::GetCurrentProcess(),
+                                            primary_token_.GetHandle(),
+                                            &dup_primary_token);
+      if (FAILED(hr)) {
+        CORE_LOG(LE, (_T("[Failed to duplicate primary token][0x%x]"), hr));
+        return hr;
+      }
+    }
+
+    if (session_id_.IsEmpty()) {
+      VERIFY1(SUCCEEDED(GetGuid(&session_id_)));
+    }
+
+    // Create a thread pool work item for deferred execution of the on demand
+    // check. The thread pool owns this call back object. The thread owns the
+    // impersonation and primary tokens.
+    typedef StaticThreadPoolCallBack1<internal::OnDemandParameters> Callback;
+    scoped_ptr<Callback> callback(
+        new Callback(&OnDemand::DoOnDemandInternal,
+                     internal::OnDemandParameters(
+                         guid,
+                         job_observer_git.Detach(),
+                         is_update_check_only,
+                         session_id_,
+                         dup_impersonation_token.GetHandle(),
+                         dup_primary_token.GetHandle())));
+
+    hr = Goopdate::Instance().QueueUserWorkItem(callback.get(),
+                                                WT_EXECUTELONGFUNCTION);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[QueueUserWorkItem failed][0x%x]"), hr));
+      return hr;
+    }
+
+    if (T::is_machine()) {
+      dup_impersonation_token.Detach();
+      dup_primary_token.Detach();
+    }
+
+    callback.release();
+
+    return S_OK;
+  }
+
+  static void DoOnDemandInternal(
+      internal::OnDemandParameters on_demand_params) {
+    CORE_LOG(L2, (_T("[DoOnDemandInternal][%d]"),
+                  on_demand_params.is_update_check_only));
+    _pAtlModule->Lock();
+    ON_SCOPE_EXIT_OBJ(*_pAtlModule, &CAtlModule::Unlock);
+
+    scoped_handle impersonation_token(on_demand_params.impersonation_token);
+    scoped_handle primary_token(on_demand_params.primary_token);
+
+    scoped_co_init init_com_apt(COINIT_APARTMENTTHREADED);
+    HRESULT hr = init_com_apt.hresult();
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[init_com_apt failed][0x%x]"), hr));
+      return;
+    }
+
+    hr = internal::DoOnDemand(T::is_machine(), on_demand_params);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[DoOnDemand failed][0x%x]"), hr));
+      return;
+    }
+  }
+
+  DECLARE_NOT_AGGREGATABLE(OnDemand)
+  DECLARE_REGISTRY_RESOURCEID_EX(T::registry_res_id())
+
+  BEGIN_REGISTRY_MAP()
+    REGMAP_ENTRY(_T("HKROOT"), T::hk_root())
+    REGMAP_ENTRY(_T("VERSION"), _T("1.0"))
+    REGMAP_ENTRY(_T("PROGID"), T::prog_id())
+    REGMAP_ENTRY(_T("DESCRIPTION"), _T("Google Update Legacy On Demand"))
+    REGMAP_ENTRY(_T("CLSID"), T::class_id())
+    REGMAP_MODULE2(_T("MODULE"), kOmahaOnDemandFileName)
+    REGMAP_ENTRY(_T("ICONRESID"), PP_STRINGIZE(IDI_ELEVATION_MONIKER_ICON))
+    REGMAP_ENTRY(_T("STRINGRESID"),
+                 PP_STRINGIZE(IDS_ELEVATION_MONIKER_DISPLAYNAME))
+  END_REGISTRY_MAP()
+
+  BEGIN_COM_MAP(OnDemand)
+    COM_INTERFACE_ENTRY(IGoogleUpdate)
+    COM_INTERFACE_ENTRY(IStdMarshalInfo)
+  END_COM_MAP()
+
+ private:
+  CAccessToken impersonation_token_;
+  CAccessToken primary_token_;
+  CString session_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(OnDemand);
+};
+
+struct OnDemandModeUser {
+  static bool is_machine() { return false; }
+  static const TCHAR* const prog_id() { return kProgIDOnDemandUser; }
+  static GUID class_id() { return __uuidof(OnDemandUserAppsClass); }
+  static UINT registry_res_id() { return IDR_LOCAL_SERVER_RGS; }
+  static const TCHAR* const hk_root() { return _T("HKCU"); }
+};
+
+struct OnDemandModeMachineFallback {
+  static bool is_machine() { return true; }
+  static const TCHAR* const prog_id() { return kProgIDOnDemandMachineFallback; }
+  static GUID class_id() { return __uuidof(OnDemandMachineAppsFallbackClass); }
+  static UINT registry_res_id() { return IDR_LOCAL_SERVER_ELEVATION_RGS; }
+  static const TCHAR* const hk_root() { return _T("HKLM"); }
+};
+
+struct OnDemandModeService {
+  static bool is_machine() { return true; }
+  static const TCHAR* const prog_id() { return kProgIDOnDemandSvc; }
+  static GUID class_id() { return __uuidof(OnDemandMachineAppsServiceClass); }
+  static UINT registry_res_id() { return IDR_LOCAL_SERVICE_RGS; }
+  static const TCHAR* const hk_root() { return _T("HKLM"); }
+};
+
+typedef OnDemand<OnDemandModeUser> OnDemandUser;
+typedef OnDemand<OnDemandModeMachineFallback> OnDemandMachineFallback;
+typedef OnDemand<OnDemandModeService> OnDemandService;
+
+#pragma warning(pop)
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_ONDEMAND_H_
diff --git a/goopdate/oneclick_process_launcher.cc b/goopdate/oneclick_process_launcher.cc
new file mode 100644
index 0000000..d2d3bf4
--- /dev/null
+++ b/goopdate/oneclick_process_launcher.cc
@@ -0,0 +1,89 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/oneclick_process_launcher.h"
+
+#include "base/scoped_ptr.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/goopdate/app_command.h"
+
+namespace omaha {
+
+OneClickProcessLauncher::OneClickProcessLauncher() {}
+
+OneClickProcessLauncher::~OneClickProcessLauncher() {}
+
+STDMETHODIMP OneClickProcessLauncher::LaunchAppCommand(
+    const WCHAR* app_guid, const WCHAR* cmd_id) {
+  ASSERT1(app_guid);
+  ASSERT1(cmd_id);
+
+  if (!app_guid || !cmd_id) {
+    return E_INVALIDARG;
+  }
+
+  CORE_LOG(L3, (_T("[OneClickProcessLauncher::LaunchAppCommand]")
+                _T("[app %s][cmd %s]"), app_guid, cmd_id));
+
+  // Allocate a session ID for the ping that this call will generate.
+  // TODO(omaha3): Are there any situations where this control can be
+  // instantiated outside of the context of an Update3Web/OneClick
+  // webpage?  If not, we should consider adding a function to
+  // OneClickProcessLauncher() to modify the session ID it uses.
+  CString session_id;
+  GetGuid(&session_id);
+
+  scoped_ptr<AppCommand> app_command;
+  HRESULT hr = AppCommand::Load(app_guid,
+                                is_machine(),
+                                cmd_id,
+                                session_id,
+                                address(app_command));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[failed to load command configuration][0x%x]"), hr));
+    return hr;
+  }
+
+  if (!app_command->is_web_accessible()) {
+    return E_ACCESSDENIED;
+  }
+
+  if (!is_machine()) {
+    // Execute directly at medium integrity for user-level mode
+    scoped_process process;
+    return app_command->Execute(address(process));
+  }
+
+  // Elevate to high integrity for machine-level mode
+  CComPtr<IProcessLauncher> process_launcher;
+  hr = process_launcher.CoCreateInstance(__uuidof(ProcessLauncherClass));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  ULONG_PTR phandle = NULL;
+  DWORD process_id = ::GetCurrentProcessId();
+
+  hr = process_launcher->LaunchCmdElevated(
+      app_guid, cmd_id, process_id, &phandle);
+
+  if (SUCCEEDED(hr)) {
+    ::CloseHandle(reinterpret_cast<HANDLE>(phandle));
+  }
+
+  return hr;
+}
+
+}  // namespace omaha
diff --git a/goopdate/oneclick_process_launcher.h b/goopdate/oneclick_process_launcher.h
new file mode 100644
index 0000000..86a6000
--- /dev/null
+++ b/goopdate/oneclick_process_launcher.h
@@ -0,0 +1,86 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_ONECLICK_PROCESS_LAUNCHER_H__
+#define OMAHA_GOOPDATE_ONECLICK_PROCESS_LAUNCHER_H__
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include "base/basictypes.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/base/atlregmapex.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/goopdate/non_localized_resource.h"
+
+namespace omaha {
+
+const TCHAR* const kOneClickProcessLauncherTlbVersion = _T("1.0");
+const TCHAR* const kOneClickProcessLauncherDescription =
+    _T(SHORT_COMPANY_NAME_ANSI) _T(".OneClickProcessLauncher");
+
+class ATL_NO_VTABLE OneClickProcessLauncher
+    : public CComObjectRootEx<CComMultiThreadModel>,
+      public CComCoClass<OneClickProcessLauncher>,
+      public IOneClickProcessLauncher {
+ public:
+  OneClickProcessLauncher();
+  virtual ~OneClickProcessLauncher();
+
+  DECLARE_NOT_AGGREGATABLE(OneClickProcessLauncher)
+  DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+  DECLARE_REGISTRY_RESOURCEID_EX(IDR_LOCAL_SERVER_IE_LOW_RGS)
+
+  #pragma warning(push)
+  // C4640: construction of local static object is not thread-safe
+  #pragma warning(disable : 4640)
+  BEGIN_REGISTRY_MAP()
+    REGMAP_ENTRY(_T("HKROOT"),       goopdate_utils::GetHKRoot())
+    REGMAP_MODULE2(_T("MODULE"),
+                   is_machine() ? kOmahaBrokerFileName : kOmahaOnDemandFileName)
+    REGMAP_ENTRY(_T("VERSION"),      kOneClickProcessLauncherTlbVersion)
+    REGMAP_ENTRY(_T("PROGID"),
+                 is_machine() ?
+                     kProgIDOneClickProcessLauncherMachine :
+                     kProgIDOneClickProcessLauncherUser)
+    REGMAP_ENTRY(_T("DESCRIPTION"),  kOneClickProcessLauncherDescription)
+    REGMAP_UUID(_T("CLSID"),
+                is_machine() ?
+                    __uuidof(OneClickMachineProcessLauncherClass) :
+                    __uuidof(OneClickUserProcessLauncherClass))
+  END_REGISTRY_MAP()
+  #pragma warning(pop)
+
+  // C4505: unreferenced IUnknown local functions have been removed
+  #pragma warning(disable : 4505)
+  BEGIN_COM_MAP(OneClickProcessLauncher)
+    COM_INTERFACE_ENTRY(IOneClickProcessLauncher)
+  END_COM_MAP()
+
+  STDMETHOD(LaunchAppCommand)(const WCHAR* app_guid,
+                              const WCHAR* cmd_id);
+
+ private:
+  static bool is_machine() {
+    return goopdate_utils::IsRunningFromOfficialGoopdateDir(true);
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(OneClickProcessLauncher);
+};  // class OneClickProcessLauncher
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_ONECLICK_PROCESS_LAUNCHER_H__
diff --git a/goopdate/package.cc b/goopdate/package.cc
new file mode 100644
index 0000000..6f22796
--- /dev/null
+++ b/goopdate/package.cc
@@ -0,0 +1,206 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/package.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/time.h"
+#include "omaha/base/utils.h"
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+Package::Package(AppVersion* app_version)
+    : ModelObject(app_version->model()),
+      app_version_(app_version),
+      expected_size_(0),
+      bytes_downloaded_(0),
+      bytes_total_(0),
+      next_download_retry_time_(0),
+      progress_sampler_(5 * kMsPerSec,    // Max sample time range.
+                        1 * kMsPerSec),   // Min range for meaningful average.
+      is_downloading_(false) {
+}
+
+Package::~Package() {
+}
+
+AppVersion* Package::app_version() {
+  __mutexScope(model()->lock());
+  return app_version_;
+}
+
+const AppVersion* Package::app_version() const {
+  __mutexScope(model()->lock());
+  return app_version_;
+}
+
+STDMETHODIMP Package::get(BSTR dir) const {
+  return model()->GetPackage(this, CString(dir));
+}
+
+STDMETHODIMP Package::get_isAvailable(VARIANT_BOOL* is_available) const {
+  ASSERT1(is_available);
+  *is_available = model()->IsPackageAvailable(this);
+  return S_OK;
+}
+
+STDMETHODIMP Package::get_filename(BSTR* filename_as_bstr) const {
+  __mutexScope(model()->lock());
+  ASSERT1(filename_as_bstr);
+  *filename_as_bstr = CComBSTR(filename()).Detach();
+  return S_OK;
+}
+
+// status_text can be NULL.
+// TODO(omaha): Change bytes and bytes_total to uint64. Any logging will
+// need to use %llu.
+void Package::OnProgress(int bytes,
+                         int bytes_total,
+                         int status,
+                         const TCHAR* status_text) {
+  __mutexScope(model()->lock());
+
+  UNREFERENCED_PARAMETER(status);
+  UNREFERENCED_PARAMETER(status_text);
+  ASSERT1(status == WINHTTP_CALLBACK_STATUS_READ_COMPLETE ||
+          status == WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER);
+
+  CORE_LOG(L5, (_T("[Package::OnProgress][bytes %d][bytes_total %d][status %d]")
+                _T("[status_text '%s']"),
+                bytes, bytes_total, status, status_text));
+
+  // TODO(omaha): What do we do if the following condition - bytes_total
+  // is not what we expect - fails? Omaha 2 just divided bytes by bytes_total
+  // to come up with the percentage, ignoring size. It didn't have to deal with
+  // multiple downloads (much) and pre-determined total download size.
+  // As an example, App::GetDownloadProgress() needs to know the expected
+  // size before we've downloaded any bytes, but it might also want to know if
+  // we are going to download less bytes. This could happen, for example, if
+  // a proxy returned an HTML page instead. (I had to disable the first assert
+  // because it fails when the actual file does not match the expected size.)
+  // Even worse, what if the bytes_total has a different non-zero number on
+  // successive calls?
+
+  // ASSERT1(bytes_total == 0 ||
+  //         bytes_total == static_cast<int>(expected_size_));
+  ASSERT1(bytes <= bytes_total);
+
+  bytes_downloaded_ = bytes;
+  bytes_total_ = bytes_total;
+
+  progress_sampler_.AddSampleWithCurrentTimeStamp(bytes_downloaded_);
+}
+
+void Package::OnRequestBegin() {
+  __mutexScope(model()->lock());
+  next_download_retry_time_ = 0;
+  bytes_downloaded_ = 0;
+  bytes_total_ = 0;
+  progress_sampler_.Reset();
+}
+
+void Package::OnRequestRetryScheduled(time64 next_download_retry_time) {
+  __mutexScope(model()->lock());
+  ASSERT1(next_download_retry_time >= GetCurrent100NSTime());
+  next_download_retry_time_ = next_download_retry_time;
+}
+
+void Package::SetFileInfo(const CString& filename,
+                          uint64 size,
+                          const CString& hash) {
+  __mutexScope(model()->lock());
+
+  ASSERT1(!filename.IsEmpty());
+  ASSERT1(0 < size);
+  ASSERT1(!hash.IsEmpty());
+
+  filename_ = filename;
+  expected_size_ = size;
+  expected_hash_ = hash;
+}
+
+CString Package::filename() const {
+  __mutexScope(model()->lock());
+  ASSERT1(!filename_.IsEmpty());
+  return filename_;
+}
+
+uint64 Package::expected_size() const {
+  __mutexScope(model()->lock());
+  return expected_size_;
+}
+
+CString Package::expected_hash() const {
+  __mutexScope(model()->lock());
+  ASSERT1(!expected_hash_.IsEmpty());
+  return expected_hash_;
+}
+
+uint64 Package::bytes_downloaded() const {
+  __mutexScope(model()->lock());
+  return bytes_downloaded_;
+}
+
+time64 Package::next_download_retry_time() const {
+  __mutexScope(model()->lock());
+  return next_download_retry_time_;
+}
+
+LONG Package::GetEstimatedRemainingDownloadTimeMs() const {
+  __mutexScope(model()->lock());
+
+  const LONG kUnknownRemainingTime = -1;
+
+  if (bytes_total_ == 0) {  // Don't know how many bytes to download.
+    return kUnknownRemainingTime;
+  }
+
+  if (bytes_total_ == bytes_downloaded_) {
+    return 0;
+  }
+
+  LONG time_remaining_ms = kUnknownRemainingTime;
+  int average_speed = progress_sampler_.GetAverageProgressPerMs();
+  if (average_speed == ProgressSampler<int>::kUnknownProgressPerMs) {
+    return kUnknownRemainingTime;
+  }
+
+  if (bytes_total_ >= bytes_downloaded_ && average_speed > 0) {
+    time_remaining_ms = static_cast<LONG>(
+        CeilingDivide(bytes_total_ - bytes_downloaded_, average_speed));
+  }
+
+  return time_remaining_ms;
+}
+
+STDMETHODIMP PackageWrapper::get(BSTR dir) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get(dir);
+}
+
+STDMETHODIMP PackageWrapper::get_isAvailable(VARIANT_BOOL* is_available) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_isAvailable(is_available);
+}
+
+STDMETHODIMP PackageWrapper::get_filename(BSTR* filename) {
+  __mutexScope(model()->lock());
+  return wrapped_obj()->get_filename(filename);
+}
+
+}  // namespace omaha
diff --git a/goopdate/package.h b/goopdate/package.h
new file mode 100644
index 0000000..151354f
--- /dev/null
+++ b/goopdate/package.h
@@ -0,0 +1,128 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Defines the Package COM object exposed by the model.
+
+// TODO(omaha3): Protect all public members with the model lock and assert in
+// all non-public members that the model has been locked by the caller.
+
+#ifndef OMAHA_GOOPDATE_PACKAGE_H_
+#define OMAHA_GOOPDATE_PACKAGE_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include "base/basictypes.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/time.h"
+#include "omaha/common/progress_sampler.h"
+#include "omaha/goopdate/com_wrapper_creator.h"
+#include "omaha/goopdate/model_object.h"
+// TODO(omaha): Consider implementing the NetworkRequestCallback portion in a
+// PImpl or similar pattern. As it is, every file that includes model.h also
+// becomes dependent on most of net/.
+#include "omaha/net/network_request.h"
+
+namespace omaha {
+
+class AppVersion;
+struct Lockable;
+
+class Package
+    : public ModelObject,
+      public NetworkRequestCallback {
+ public:
+  explicit Package(AppVersion* parent_app_version);
+  virtual ~Package();
+
+  STDMETHOD(get)(BSTR dir) const;
+  STDMETHOD(get_isAvailable)(VARIANT_BOOL* is_available) const;
+  STDMETHOD(get_filename)(BSTR* filename) const;
+
+  // NetworkRequestCallback.
+  virtual void OnProgress(int bytes,
+                          int bytes_total,
+                          int status,
+                          const TCHAR* status_text);
+  virtual void OnRequestBegin();
+  virtual void OnRequestRetryScheduled(time64 next_download_retry_time);
+
+  void SetFileInfo(const CString& filename, uint64 size, const CString& hash);
+
+  // Returns the name of the file specified in the manifest.
+  CString filename() const;
+  // Returns the expected size of the file in bytes.
+  uint64 expected_size() const;
+  // Returns the expected SHA-1 hash of the file.
+  CString expected_hash() const;
+
+  uint64 bytes_downloaded() const;
+
+  time64 next_download_retry_time() const;
+
+  AppVersion* app_version();
+  const AppVersion* app_version() const;
+
+  LONG GetEstimatedRemainingDownloadTimeMs() const;
+
+ private:
+  // Weak reference to the parent of the package.
+  AppVersion* app_version_;
+
+  // The name of the package as it appears in the manifest.
+  CString filename_;
+  uint64 expected_size_;
+  CString expected_hash_;
+
+  int bytes_downloaded_;
+  int bytes_total_;
+  time64 next_download_retry_time_;
+
+  ProgressSampler<int> progress_sampler_;
+
+  // True if the package is being downloaded.
+  // TODO(omaha): implement this.
+  bool is_downloading_;
+
+  DISALLOW_COPY_AND_ASSIGN(Package);
+};
+
+class ATL_NO_VTABLE PackageWrapper
+    : public ComWrapper<PackageWrapper, Package>,
+      public IDispatchImpl<IPackage,
+                           &__uuidof(IPackage),
+                           &CAtlModule::m_libid,
+                           kMajorTypeLibVersion,
+                           kMinorTypeLibVersion> {
+ public:
+
+  // IPackage.
+  STDMETHOD(get)(BSTR dir);
+  STDMETHOD(get_isAvailable)(VARIANT_BOOL* is_available);
+  STDMETHOD(get_filename)(BSTR* filename);
+
+ protected:
+  PackageWrapper() {}
+  virtual ~PackageWrapper() {}
+
+  BEGIN_COM_MAP(PackageWrapper)
+    COM_INTERFACE_ENTRY(IPackage)
+    COM_INTERFACE_ENTRY(IDispatch)
+  END_COM_MAP()
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_PACKAGE_H_
diff --git a/goopdate/package_cache.cc b/goopdate/package_cache.cc
new file mode 100644
index 0000000..0b178b1
--- /dev/null
+++ b/goopdate/package_cache.cc
@@ -0,0 +1,494 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/package_cache.h"
+#include <shlwapi.h>
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/string.h"
+#include "omaha/base/signatures.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/goopdate/package_cache_internal.h"
+#include "omaha/goopdate/worker_metrics.h"
+
+namespace omaha {
+
+namespace internal {
+
+bool PackageSortByTimePredicate(const PackageInfo& package1,
+                                const PackageInfo& package2) {
+  return ::CompareFileTime(&package1.file_time, &package2.file_time) > 0;
+}
+
+bool IsSpecialDirectoryFindData(const WIN32_FIND_DATA& find_data) {
+  return find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
+         (String_StrNCmp(find_data.cFileName, _T("."), 2, false) == 0 ||
+          String_StrNCmp(find_data.cFileName, _T(".."), 3, false) == 0);
+}
+
+bool IsSubDirectoryFindData(const WIN32_FIND_DATA& find_data) {
+  return find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
+         !IsSpecialDirectoryFindData(find_data);
+}
+
+bool IsFileFindData(const WIN32_FIND_DATA& find_data) {
+  return ((find_data.dwFileAttributes == FILE_ATTRIBUTE_NORMAL) ||
+          !(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
+}
+
+HRESULT FindAllPackagesInfo(const CString& cache_root,
+                            std::vector<PackageInfo>* packages_info) {
+  ASSERT1(packages_info);
+  ASSERT1(packages_info->empty());
+  return FindDirectoryPackagesInfo(cache_root,
+                                   CACHE_DIRECTORY_ROOT,
+                                   packages_info);
+}
+
+HRESULT FindAppPackagesInfo(const CString& app_dir,
+                            std::vector<PackageInfo>* packages_info) {
+  return FindDirectoryPackagesInfo(app_dir, CACHE_DIRECTORY_APP, packages_info);
+}
+
+HRESULT FindVersionPackagesInfo(const CString& version_dir,
+                                std::vector<PackageInfo>* packages_info) {
+  return FindDirectoryPackagesInfo(version_dir,
+                                   CACHE_DIRECTORY_VERSION,
+                                   packages_info);
+}
+
+HRESULT FindFilePackagesInfo(const CString& version_dir,
+                             const WIN32_FIND_DATA& find_data,
+                             std::vector<PackageInfo>* packages_info) {
+  PackageInfo package_info;
+  package_info.file_name = ConcatenatePath(version_dir, find_data.cFileName);
+  package_info.file_time = ::CompareFileTime(&find_data.ftCreationTime,
+                                             &find_data.ftLastWriteTime) ?
+                               find_data.ftCreationTime :
+                               find_data.ftLastWriteTime;
+  package_info.file_size.LowPart = find_data.nFileSizeLow;
+  package_info.file_size.HighPart = find_data.nFileSizeHigh;
+  packages_info->push_back(package_info);
+
+  return S_OK;
+}
+
+HRESULT FindDirectoryPackagesInfo(const CString& dir_path,
+                                  CacheDirectoryType dir_type,
+                                  std::vector<PackageInfo>* packages_info) {
+  CORE_LOG(L4, (_T("[FindDirectoryPackagesInfo][%s][%d]"), dir_path, dir_type));
+
+  WIN32_FIND_DATA find_data = {0};
+  scoped_hfind hfind(::FindFirstFile(dir_path + _T("\\*"), &find_data));
+  if (!hfind) {
+    HRESULT hr = HRESULTFromLastError();
+    CORE_LOG(L4, (_T("[FindDirectoryPackagesInfo failed][0x%x]"), hr));
+    return hr;
+  }
+
+  HRESULT hr = S_OK;
+  do {
+    switch (dir_type) {
+      case CACHE_DIRECTORY_ROOT:
+        if (IsSubDirectoryFindData(find_data)) {
+          CString app_dir = ConcatenatePath(dir_path, find_data.cFileName);
+          hr = FindAppPackagesInfo(app_dir, packages_info);
+        }
+        break;
+      case CACHE_DIRECTORY_APP:
+        if (IsSubDirectoryFindData(find_data)) {
+          CString version_dir = ConcatenatePath(dir_path, find_data.cFileName);
+          hr = FindVersionPackagesInfo(version_dir, packages_info);
+        }
+        break;
+      case CACHE_DIRECTORY_VERSION:
+        if (IsFileFindData(find_data)) {
+          hr = FindFilePackagesInfo(dir_path, find_data, packages_info);
+        }
+        break;
+    }
+
+    if (FAILED(hr)) {
+      CORE_LOG(L4, (_T("[FindDirectoryPackagesInfo failed][0x%x]"), hr));
+      return hr;
+    }
+  } while (::FindNextFile(get(hfind), &find_data));
+
+  return S_OK;
+}
+
+void SortPackageInfoByTime(std::vector<PackageInfo>* packages_info) {
+  std::sort(packages_info->begin(),
+            packages_info->end(),
+            PackageSortByTimePredicate);
+}
+
+}  // namespace internal
+
+PackageCache::PackageCache() {
+  cache_time_limit_days_ =
+    ConfigManager::Instance()->GetPackageCacheExpirationTimeDays();
+
+  cache_size_limit_bytes_ = 1024 * 1024 * static_cast<uint64>(
+    ConfigManager::Instance()->GetPackageCacheSizeLimitMBytes());
+}
+
+PackageCache::~PackageCache() {
+}
+
+HRESULT PackageCache::Initialize(const CString& cache_root) {
+  CORE_LOG(L3, (_T("[PackageCache::Initialize][%s]"), cache_root));
+
+  __mutexScope(cache_lock_);
+
+  if (!IsAbsolutePath(cache_root)) {
+    return E_INVALIDARG;
+  }
+
+  HRESULT hr = CreateDir(cache_root, NULL);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CreateDir failed][0x%x][%s]"), hr, cache_root));
+    return hr;
+  }
+
+  cache_root_ = cache_root;
+
+  return S_OK;
+}
+
+bool PackageCache::IsCached(const Key& key, const CString& hash) const {
+  CORE_LOG(L3, (_T("[PackageCache::IsCached][key '%s'][hash %s]"),
+                key.ToString(), hash));
+
+  __mutexScope(cache_lock_);
+
+  CString filename;
+  HRESULT hr = BuildCacheFileNameForKey(key, &filename);
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  return File::Exists(filename) && SUCCEEDED(AuthenticateFile(filename, hash));
+}
+
+HRESULT PackageCache::Put(const Key& key,
+                          const CString& source_file,
+                          const CString& hash) {
+  ++metric_worker_package_cache_put_total;
+  CORE_LOG(L3, (_T("[PackageCache::Put][key '%s'][source_file '%s'][hash %s]"),
+                key.ToString(), source_file, hash));
+
+  __mutexScope(cache_lock_);
+
+  if (key.app_id().IsEmpty() || key.version().IsEmpty() ||
+      key.package_name().IsEmpty() ) {
+    return E_INVALIDARG;
+  }
+
+  CString destination_file;
+  HRESULT hr = BuildCacheFileNameForKey(key, &destination_file);
+  CORE_LOG(L3, (_T("[destination file '%s']"), destination_file));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = CreateDir(GetDirectoryFromPath(destination_file), NULL);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[failed to create cache directory][0x%08x][%s]"),
+                  hr, destination_file));
+    return hr;
+  }
+
+  // TODO(omaha): consider not overwriting the file if the file is
+  // in the cache and it is valid.
+
+  // When not impersonated, File::Copy resets the ownership of the destination
+  // file and it inherits ACEs from the new parent directory.
+  hr = File::Copy(source_file, destination_file, true);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[failed to copy file to cache][0x%08x][%s]"),
+                  hr, destination_file));
+    return hr;
+  }
+
+  hr = AuthenticateFile(destination_file, hash);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[failed to authenticate '%s'][%s]"),
+                  destination_file, hash));
+    VERIFY1(SUCCEEDED(::DeleteFile(destination_file)));
+    return hr;
+  }
+
+  ++metric_worker_package_cache_put_succeeded;
+  return S_OK;
+}
+
+HRESULT PackageCache::Get(const Key& key,
+                          const CString& destination_file,
+                          const CString& hash) const {
+  CORE_LOG(L3, (_T("[PackageCache::Get][key '%s'][dest file '%s'][hash '%s']"),
+                key.ToString(), destination_file, hash));
+
+  __mutexScope(cache_lock_);
+
+  if (key.app_id().IsEmpty() || key.version().IsEmpty() ||
+      key.package_name().IsEmpty() ) {
+    return E_INVALIDARG;
+  }
+
+  CString source_file;
+  HRESULT hr = BuildCacheFileNameForKey(key, &source_file);
+  CORE_LOG(L3, (_T("[source file '%s']"), source_file));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (!File::Exists(source_file)) {
+    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+  }
+
+  hr = AuthenticateFile(source_file, hash);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[failed to authenticate '%s']"), source_file));
+    return hr;
+  }
+
+  return File::Copy(source_file, destination_file, true);
+}
+
+HRESULT PackageCache::Purge(const Key& key) {
+  CORE_LOG(L3, (_T("[PackageCache::Purge][key '%s']"), key.ToString()));
+
+  __mutexScope(cache_lock_);
+
+  return Delete(key.app_id(), key.version(), key.package_name());
+}
+
+HRESULT PackageCache::PurgeVersion(const CString& app_id,
+                                   const CString& version) {
+  CORE_LOG(L3, (_T("[PackageCache::PurgeVersion][app_id '%s'][version '%s']"),
+                app_id, version));
+
+  __mutexScope(cache_lock_);
+
+  return Delete(app_id, version, _T(""));
+}
+
+HRESULT PackageCache::PurgeApp(const CString& app_id) {
+  CORE_LOG(L3, (_T("[PackageCache::PurgeApp][app_id '%s']"), app_id));
+
+  __mutexScope(cache_lock_);
+
+  return Delete(app_id, _T(""), _T(""));
+}
+
+HRESULT PackageCache::PurgeAppLowerVersions(const CString& app_id,
+                                            const CString& version) {
+  CORE_LOG(L3, (_T("[PackageCache::PurgeAppLowerVersions][%s][%s]"),
+                app_id, version));
+
+  __mutexScope(cache_lock_);
+
+  ULONGLONG my_version = VersionFromString(version);
+  if (!my_version) {
+    return E_INVALIDARG;
+  }
+
+  CString app_id_path;
+  HRESULT hr = BuildCacheFileName(app_id, CString(), CString(), &app_id_path);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[BuildCacheFileName fail][%s][0x%x]"), app_id_path, hr));
+    return hr;
+  }
+
+  WIN32_FIND_DATA find_data = {0};
+  scoped_hfind hfind(::FindFirstFile(app_id_path + _T("\\*"), &find_data));
+  if (!hfind) {
+    HRESULT hr = HRESULTFromLastError();
+    CORE_LOG(L4, (_T("[FindFirstFile failed][%s][0x%x]"), app_id_path, hr));
+    return hr;
+  }
+
+  do {
+    if (internal::IsSpecialDirectoryFindData(find_data)) {
+      continue;
+    }
+    ASSERT1(internal::IsSubDirectoryFindData(find_data));
+
+    ULONGLONG found_version = VersionFromString(find_data.cFileName);
+    if (!found_version || found_version >= my_version) {
+      CORE_LOG(L2, (_T("[Not purging version][%s]"), find_data.cFileName));
+      continue;
+    }
+
+    CString version_dir = ConcatenatePath(app_id_path, find_data.cFileName);
+    hr = DeleteBeforeOrAfterReboot(version_dir);
+    CORE_LOG(L3, (_T("[Purge version][%s][0x%x]"), version_dir, hr));
+  } while (::FindNextFile(get(hfind), &find_data));
+
+  return S_OK;
+}
+
+HRESULT PackageCache::PurgeAll() {
+  CORE_LOG(L3, (_T("[PackageCache::PurgeAll]")));
+
+  __mutexScope(cache_lock_);
+
+  // Deletes the cache root including all the cache entries.
+  HRESULT hr = Delete(_T(""), _T(""), _T(""));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Recreate the cache root.
+  hr = CreateDir(cache_root_, NULL);
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[CreateDir failed][0x%x][%s]"), hr, cache_root_));
+    return hr;
+  }
+
+  return hr;
+}
+
+FILETIME PackageCache::GetCacheExpirationTime() const {
+  FILETIME current_time = {0};
+  ::GetSystemTimeAsFileTime(&current_time);
+  ULARGE_INTEGER now = {0};
+  now.LowPart = current_time.dwLowDateTime;
+  now.HighPart = current_time.dwHighDateTime;
+
+  const uint64 kNum100NanoSecondsInDay = 1000LL * 1000 * 10 * kSecondsPerDay;
+  now.QuadPart -= kNum100NanoSecondsInDay * cache_time_limit_days_;
+
+  FILETIME expiration_time = {0};
+  expiration_time.dwLowDateTime = now.LowPart;
+  expiration_time.dwHighDateTime = now.HighPart;
+
+  return expiration_time;
+}
+
+HRESULT PackageCache::PurgeOldPackagesIfNecessary() const {
+  __mutexScope(cache_lock_);
+
+  std::vector<internal::PackageInfo> packages_info;
+  HRESULT hr = internal::FindAllPackagesInfo(cache_root_, &packages_info);
+
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[internal::FindAllPackagesInfo failed][0x%x]"), hr));
+    return hr;
+  }
+  internal::SortPackageInfoByTime(&packages_info);
+
+  FILETIME expiration_time = GetCacheExpirationTime();
+
+  // Delete cached package based on the package info.
+  std::vector<internal::PackageInfo>::const_iterator it;
+  uint64 total_cache_size = 0;
+  for (it = packages_info.begin(); it != packages_info.end(); ++it) {
+    total_cache_size += it->file_size.QuadPart;
+    if (total_cache_size > cache_size_limit_bytes_) {
+      break;  // Remaining packages should be deleted as size limit is reached.
+    }
+    if (::CompareFileTime(&it->file_time, &expiration_time) < 0) {
+      break;  // Remaining packages should be deleted as they are expired.
+    }
+  }
+
+  for (; it != packages_info.end(); ++it) {
+    hr = DeleteBeforeOrAfterReboot(it->file_name);
+  }
+
+  return hr;
+}
+
+HRESULT PackageCache::Delete(const CString& app_id,
+                             const CString& version,
+                             const CString& package_name) {
+  CString filename;
+  HRESULT hr = BuildCacheFileName(app_id, version, package_name, &filename);
+  CORE_LOG(L3, (_T("[PackageCache::Delete '%s']"), filename));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return DeleteBeforeOrAfterReboot(filename);
+}
+
+CString PackageCache::cache_root() const {
+  __mutexScope(cache_lock_);
+
+  ASSERT1(!cache_root_.IsEmpty());
+  ASSERT1(File::Exists(cache_root_));
+
+  return cache_root_;
+}
+
+uint64 PackageCache::Size() const {
+  uint64 result(0);
+  return SUCCEEDED(GetDirectorySize(cache_root_, &result)) ? result : 0;
+}
+
+HRESULT PackageCache::BuildCacheFileNameForKey(const Key& key,
+                                               CString* filename) const {
+  ASSERT1(filename);
+
+  return BuildCacheFileName(key.app_id(),
+                            key.version(),
+                            key.package_name(),
+                            filename);
+}
+
+HRESULT PackageCache::BuildCacheFileName(const CString& app_id,
+                                         const CString& version,
+                                         const CString& package_name,
+                                         CString* filename) const {
+  ASSERT1(filename);
+
+  // Validate the package name does not contain the "..".
+  if (package_name.Find(_T("..")) != -1) {
+    return E_INVALIDARG;
+  }
+
+  CString tmp_filename;
+  tmp_filename = ConcatenatePath(cache_root_, app_id);
+  tmp_filename = ConcatenatePath(tmp_filename, version);
+  tmp_filename = ConcatenatePath(tmp_filename, package_name);
+
+  *filename = tmp_filename;
+
+  return S_OK;
+}
+
+HRESULT PackageCache::AuthenticateFile(const CString& filename,
+                                       const CString& hash) {
+  CORE_LOG(L3, (_T("[PackageCache::AuthenticateFile][%s][%s]"),
+                filename, hash));
+  HighresTimer authentication_timer;
+
+  std::vector<CString> files;
+  files.push_back(filename);
+  HRESULT hr = AuthenticateFiles(files, hash);
+  CORE_LOG(L3, (_T("[PackageCache::AuthenticateFile completed][0x%08x][%d ms]"),
+                hr, authentication_timer.GetElapsedMs()));
+
+  return hr;
+}
+
+}  // namespace omaha
+
diff --git a/goopdate/package_cache.h b/goopdate/package_cache.h
new file mode 100644
index 0000000..9215e65
--- /dev/null
+++ b/goopdate/package_cache.h
@@ -0,0 +1,139 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_PACKAGE_CACHE_H_
+#define OMAHA_GOOPDATE_PACKAGE_CACHE_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/synchronized.h"
+
+namespace omaha {
+
+class PackageCache {
+ public:
+
+  // Defines the key that uniquely identifies the packages in the cache.
+  // The key uses a default version value in the case the application does
+  // not provide a version string.
+  class Key {
+   public:
+    Key(const CString& app_id,
+        const CString& version,
+        const CString& package_name)
+        : app_id_(app_id),
+          version_(version.IsEmpty() ? _T("0.0.0.0") : version),
+          package_name_(package_name) {}
+
+    CString app_id() const { return app_id_; }
+    CString version() const { return version_; }
+    CString package_name() const { return package_name_; }
+
+    CString ToString() const {
+      CString result;
+      result.Format(_T("appid=%s; version=%s; package_name=%s"),
+                    app_id_, version_, package_name_);
+      return result;
+    }
+
+   private:
+    const CString app_id_;
+    const CString version_;
+    const CString package_name_;
+
+    DISALLOW_COPY_AND_ASSIGN(Key);
+  };
+
+  PackageCache();
+  ~PackageCache();
+
+  HRESULT Initialize(const CString& cache_root);
+
+  HRESULT Put(const Key& key,
+              const CString& source_file,
+              const CString& hash);
+
+  HRESULT Get(const Key& key,
+              const CString& destination_file,
+              const CString& hash) const;
+
+  bool IsCached(const Key& key, const CString& hash) const;
+
+  HRESULT Purge(const Key& key);
+
+  HRESULT PurgeVersion(const CString& app_id, const CString& version);
+
+  HRESULT PurgeApp(const CString& app_id);
+
+  // Purges version directories lower than a 'version' of the form 1.2.3.4. If
+  // the version format is not recognized, returns E_INVALIDARG.
+  HRESULT PurgeAppLowerVersions(const CString& app_id, const CString& version);
+
+  HRESULT PurgeAll();
+
+  // Purges expired packages and keeps total cache size below the limit by
+  // purging oldest ones.
+  HRESULT PurgeOldPackagesIfNecessary() const;
+
+  // Returns the total size of all files in the cache. Returns 0 if the size
+  // cannot be determined or the cache is empty.
+  uint64 Size() const;
+
+  CString cache_root() const;
+
+  static HRESULT AuthenticateFile(const CString& filename,
+                                  const CString& hash);
+
+ private:
+  friend class PackageCacheTest;
+
+  HRESULT BuildCacheFileNameForKey(const Key& key, CString* filename) const;
+  HRESULT BuildCacheFileName(const CString& app_id,
+                             const CString& version,
+                             const CString& package_name,
+                             CString* filename) const;
+
+  // Deletes the cache entries that match the app_id, version, and package_name.
+  // If the parameters are empty, the function deletes the packages of versions
+  // of apps, respectively.
+  HRESULT Delete(const CString& app_id,
+                 const CString& version,
+                 const CString& package_name);
+
+  // Returns the cache expiration time. All files in the cache before that time
+  // are considered as expired and should be purged.
+  FILETIME GetCacheExpirationTime() const;
+
+  // The cache duration, specified as a count of days.  (This is converted to
+  // an absolute time by GetCacheExpirationTime().)
+  int cache_time_limit_days_;
+
+  // The maximum allowed cache size, in bytes. If the cache grows over this
+  // size, files will be purged using a least-recently-added metric.
+  uint64 cache_size_limit_bytes_;
+
+  CString cache_root_;
+
+  LLock cache_lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(PackageCache);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_PACKAGE_CACHE_H_
+
diff --git a/goopdate/package_cache_internal.h b/goopdate/package_cache_internal.h
new file mode 100644
index 0000000..e116e30
--- /dev/null
+++ b/goopdate/package_cache_internal.h
@@ -0,0 +1,85 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_PACKAGE_CACHE_INTERNAL_H_
+#define OMAHA_GOOPDATE_PACKAGE_CACHE_INTERNAL_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/synchronized.h"
+
+namespace omaha {
+
+namespace internal {
+
+enum CacheDirectoryType {
+  CACHE_DIRECTORY_ROOT,
+  CACHE_DIRECTORY_APP,
+  CACHE_DIRECTORY_VERSION,
+};
+
+struct PackageInfo {
+  PackageInfo() {
+    file_time.dwLowDateTime = 0;
+    file_time.dwHighDateTime = 0;
+    file_size.QuadPart = 0;
+  }
+
+  CString file_name;
+  FILETIME file_time;
+  ULARGE_INTEGER file_size;
+};
+
+// TODO(omaha): add tests for some of the functions below.
+bool PackageSortByTimePredicate(const PackageInfo& package1,
+                                const PackageInfo& package2);
+
+bool IsSpecialDirectoryFindData(const WIN32_FIND_DATA& find_data);
+
+bool IsSubDirectoryFindData(const WIN32_FIND_DATA& find_data);
+
+bool IsFileFindData(const WIN32_FIND_DATA& find_data);
+
+HRESULT FindDirectoryPackagesInfo(const CString& dir_path,
+                                  CacheDirectoryType dir_type,
+                                  std::vector<PackageInfo>* packages_info);
+
+HRESULT FindAllPackagesInfo(const CString& cache_root,
+                            std::vector<PackageInfo>* packages_info);
+
+HRESULT FindAppPackagesInfo(const CString& app_dir,
+                            std::vector<PackageInfo>* packages_info);
+
+HRESULT FindVersionPackagesInfo(const CString& version_dir,
+                                std::vector<PackageInfo>* packages_info);
+
+HRESULT FindFilePackagesInfo(const CString& version_dir,
+                             const WIN32_FIND_DATA& find_data,
+                             std::vector<PackageInfo>* packages_info);
+
+HRESULT FindDirectoryPackagesInfo(const CString& dir_path,
+                                  CacheDirectoryType dir_type,
+                                  std::vector<PackageInfo>* packages_info);
+
+void SortPackageInfoByTime(std::vector<PackageInfo>* packages_info);
+
+}  // namespace internal
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_PACKAGE_CACHE_INTERNAL_H_
+
diff --git a/goopdate/package_cache_unittest.cc b/goopdate/package_cache_unittest.cc
new file mode 100644
index 0000000..640c490
--- /dev/null
+++ b/goopdate/package_cache_unittest.cc
@@ -0,0 +1,520 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/app_util.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
+#include "omaha/goopdate/package_cache.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+class PackageCacheTest : public testing::Test {
+ protected:
+  typedef PackageCache::Key Key;
+
+  PackageCacheTest()
+      : cache_root_(GetUniqueTempDirectoryName()),
+      size_file1_(0),
+      size_file2_(0) {
+    EXPECT_FALSE(cache_root_.IsEmpty());
+
+    CString executable_path(app_util::GetCurrentModuleDirectory());
+
+    source_file1_ = ConcatenatePath(
+        executable_path,
+        _T("unittest_support\\download_cache_test\\")
+        _T("{89640431-FE64-4da8-9860-1A1085A60E13}\\gears-win32-opt.msi"));
+
+    hash_file1_ = _T("ImV9skETZqGFMjs32vbZTvzAYJU=");
+
+    size_file1_ = 870400;
+
+    source_file2_ = ConcatenatePath(
+        executable_path,
+        _T("unittest_support\\download_cache_test\\")
+        _T("{7101D597-3481-4971-AD23-455542964072}\\livelysetup.exe"));
+
+    hash_file2_ = _T("Igq6bYaeXFJCjH770knXyJ6V53s=");
+
+    size_file2_ = 479848;
+
+    EXPECT_TRUE(File::Exists(source_file1_));
+    EXPECT_TRUE(File::Exists(source_file2_));
+  }
+
+  virtual void SetUp() {
+    EXPECT_FALSE(String_EndsWith(cache_root_, _T("\\"), true));
+    EXPECT_HRESULT_SUCCEEDED(package_cache_.Initialize(cache_root_));
+    EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeAll());
+  }
+
+  virtual void TearDown() {
+    EXPECT_HRESULT_SUCCEEDED(DeleteDirectory(cache_root_));
+  }
+
+  HRESULT BuildCacheFileNameForKey(const Key& key, CString* filename) const {
+    return package_cache_.BuildCacheFileNameForKey(key, filename);
+  }
+
+  HRESULT ExpireCache(const Key& key) {
+    FILETIME expiration_time = package_cache_.GetCacheExpirationTime();
+
+    CString cached_file_name;
+    EXPECT_HRESULT_SUCCEEDED(BuildCacheFileNameForKey(key, &cached_file_name));
+
+    // Change file time a little big earlier than the expiration time.
+    ULARGE_INTEGER file_time = {0};
+    file_time.LowPart = expiration_time.dwLowDateTime;
+    file_time.HighPart = expiration_time.dwHighDateTime;
+    file_time.QuadPart -= 1;
+
+    expiration_time.dwLowDateTime = file_time.LowPart;
+    expiration_time.dwHighDateTime = file_time.HighPart;
+
+    return File::SetFileTime(cached_file_name,
+                             &expiration_time,
+                             &expiration_time,
+                             &expiration_time);
+  }
+
+  void SetCacheSizeLimitMB(int limit_mb) {
+    package_cache_.cache_size_limit_bytes_ = 1024 * 1024 *
+      static_cast<uint64>(limit_mb);
+  }
+
+  void SetCacheTimeLimitDays(int limit_days) {
+    package_cache_.cache_time_limit_days_ = limit_days;
+  }
+
+  const CString cache_root_;
+  CString source_file1_;
+  CString hash_file1_;
+  uint64  size_file1_;
+
+  CString source_file2_;
+  CString hash_file2_;
+  uint64  size_file2_;
+
+  PackageCache package_cache_;
+};
+
+// Tests the members of key when the constructor arguments are empty strings.
+TEST_F(PackageCacheTest, DefaultVersion) {
+  Key key(_T(""), _T(""), _T(""));
+  EXPECT_STREQ(_T(""), key.app_id());
+  EXPECT_STREQ(_T("0.0.0.0"), key.version());
+  EXPECT_STREQ(_T(""), key.package_name());
+}
+
+TEST_F(PackageCacheTest, Initialize) {
+  EXPECT_FALSE(package_cache_.cache_root().IsEmpty());
+  EXPECT_STREQ(cache_root_, package_cache_.cache_root());
+  EXPECT_EQ(0, package_cache_.Size());
+}
+
+TEST_F(PackageCacheTest, InitializeErrors) {
+  PackageCache package_cache;
+  EXPECT_EQ(E_INVALIDARG, package_cache.Initialize(NULL));
+  EXPECT_EQ(E_INVALIDARG, package_cache.Initialize(_T("")));
+  EXPECT_EQ(E_INVALIDARG, package_cache.Initialize(_T("foo")));
+}
+
+TEST_F(PackageCacheTest, BuildCacheFileName) {
+  CString actual;
+  EXPECT_HRESULT_SUCCEEDED(
+      BuildCacheFileNameForKey(Key(_T("1"), _T("2"), _T("3")), &actual));
+  CString expected = cache_root_ + _T("\\1\\2\\3");
+  EXPECT_STREQ(expected, actual);
+
+  EXPECT_HRESULT_SUCCEEDED(
+      BuildCacheFileNameForKey(Key(_T("1"), _T("2"), _T("3\\4")), &actual));
+  expected = cache_root_ + _T("\\1\\2\\3\\4");
+  EXPECT_STREQ(expected, actual);
+
+  EXPECT_EQ(E_INVALIDARG,
+            BuildCacheFileNameForKey(Key(_T("1"), _T("2"), _T("..\\3")),
+                                     &actual));
+}
+
+// Tests Put, Get, IsCached, and Purge calls.
+TEST_F(PackageCacheTest, BasicTest) {
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeAll());
+
+  Key key1(_T("app1"), _T("ver1"), _T("package1"));
+
+  // Check the file is not in the cache.
+  EXPECT_FALSE(package_cache_.IsCached(key1, hash_file1_));
+
+  // Cache one file.
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key1,
+                                              source_file1_,
+                                              hash_file1_));
+  EXPECT_EQ(size_file1_, package_cache_.Size());
+
+  // Check the file is in the cache.
+  EXPECT_TRUE(package_cache_.IsCached(key1, hash_file1_));
+
+  // Check the source file is not deleted after caching it.
+  EXPECT_TRUE(File::Exists(source_file1_));
+
+  // Get the package from the cache into a temporary file.
+  CString destination_file;
+  EXPECT_TRUE(::GetTempFileName(app_util::GetTempDir(), _T(""), 0,
+                                CStrBuf(destination_file, MAX_PATH)));
+
+  // Get the file two times.
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Get(key1,
+                                              destination_file,
+                                              hash_file1_));
+  EXPECT_TRUE(File::Exists(destination_file));
+  EXPECT_HRESULT_SUCCEEDED(PackageCache::AuthenticateFile(destination_file,
+                                                          hash_file1_));
+
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Get(key1,
+                                              destination_file,
+                                              hash_file1_));
+  EXPECT_TRUE(File::Exists(destination_file));
+  EXPECT_HRESULT_SUCCEEDED(PackageCache::AuthenticateFile(destination_file,
+                                                          hash_file1_));
+
+  EXPECT_TRUE(::DeleteFile(destination_file));
+
+  // Cache another file.
+  Key key2(_T("app2"), _T("ver2"), _T("package2"));
+
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key2,
+                                              source_file2_,
+                                              hash_file2_));
+  EXPECT_EQ(size_file1_ + size_file2_, package_cache_.Size());
+  EXPECT_TRUE(package_cache_.IsCached(key2, hash_file2_));
+
+  // Cache the same file again. It should be idempotent.
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key2,
+                                              source_file2_,
+                                              hash_file2_));
+  EXPECT_EQ(size_file1_ + size_file2_, package_cache_.Size());
+
+  EXPECT_TRUE(package_cache_.IsCached(key2, hash_file2_));
+  EXPECT_TRUE(File::Exists(source_file2_));
+
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Purge(key1));
+  EXPECT_FALSE(package_cache_.IsCached(key1, hash_file1_));
+  EXPECT_EQ(size_file2_, package_cache_.Size());
+
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Purge(key2));
+  EXPECT_FALSE(package_cache_.IsCached(key2, hash_file2_));
+  EXPECT_EQ(0, package_cache_.Size());
+
+  // Try getting a purged files.
+  EXPECT_HRESULT_FAILED(package_cache_.Get(key1,
+                                           destination_file,
+                                           hash_file1_));
+  EXPECT_FALSE(File::Exists(destination_file));
+
+  EXPECT_HRESULT_FAILED(package_cache_.Get(key2,
+                                           destination_file,
+                                           hash_file2_));
+  EXPECT_FALSE(File::Exists(destination_file));
+}
+
+TEST_F(PackageCacheTest, PutBadHashTest) {
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeAll());
+
+  Key key1(_T("app1"), _T("ver1"), _T("package1"));
+
+  // Check the file is not in the cache.
+  EXPECT_FALSE(package_cache_.IsCached(key1, hash_file1_));
+
+  // Try caching one file when the hash is not correct.
+  const CString bad_hash = _T("JmV9skETZqGFMjs32vbZTvzAYJU=");
+  EXPECT_EQ(SIGS_E_INVALID_SIGNATURE, package_cache_.Put(key1,
+                                                         source_file1_,
+                                                         bad_hash));
+  // Check the file is not the cache.
+  EXPECT_FALSE(package_cache_.IsCached(key1, hash_file1_));
+}
+
+// The key must include the app id, version, and package name for Put and Get
+// operations. If the version is not provided, "0.0.0.0" is used internally.
+TEST_F(PackageCacheTest, BadKeyTest) {
+  Key key_empty_app(_T(""), _T("b"), _T("c"));
+  Key key_empty_name(_T("a"), _T("b"), _T(""));
+
+  EXPECT_EQ(E_INVALIDARG, package_cache_.Get(key_empty_app, _T("a"), _T("b")));
+  EXPECT_EQ(E_INVALIDARG, package_cache_.Get(key_empty_name, _T("a"), _T("b")));
+
+  EXPECT_EQ(E_INVALIDARG, package_cache_.Put(key_empty_app, _T("a"), _T("b")));
+  EXPECT_EQ(E_INVALIDARG, package_cache_.Put(key_empty_name, _T("a"), _T("b")));
+}
+
+TEST_F(PackageCacheTest, PurgeVersionTest) {
+  // Cache two files for two versions of the same app.
+  Key key11(_T("app1"), _T("ver1"), _T("package1"));
+  Key key12(_T("app1"), _T("ver1"), _T("package2"));
+  Key key21(_T("app1"), _T("ver2"), _T("package1"));
+  Key key22(_T("app1"), _T("ver2"), _T("package2"));
+
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key11,
+                                              source_file1_,
+                                              hash_file1_));
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key12,
+                                              source_file2_,
+                                              hash_file2_));
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key21,
+                                              source_file1_,
+                                              hash_file1_));
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key22,
+                                              source_file2_,
+                                              hash_file2_));
+
+  EXPECT_TRUE(package_cache_.IsCached(key11, hash_file1_));
+  EXPECT_TRUE(package_cache_.IsCached(key12, hash_file2_));
+  EXPECT_TRUE(package_cache_.IsCached(key21, hash_file1_));
+  EXPECT_TRUE(package_cache_.IsCached(key22, hash_file2_));
+
+  EXPECT_EQ(2 * (size_file1_ + size_file2_), package_cache_.Size());
+
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeVersion(_T("app1"), _T("ver1")));
+
+  EXPECT_FALSE(package_cache_.IsCached(key11, hash_file1_));
+  EXPECT_FALSE(package_cache_.IsCached(key12, hash_file2_));
+  EXPECT_TRUE(package_cache_.IsCached(key21, hash_file1_));
+  EXPECT_TRUE(package_cache_.IsCached(key22, hash_file2_));
+
+  EXPECT_EQ(size_file1_ + size_file2_, package_cache_.Size());
+
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeVersion(_T("app1"), _T("ver2")));
+
+  EXPECT_FALSE(package_cache_.IsCached(key11, hash_file1_));
+  EXPECT_FALSE(package_cache_.IsCached(key12, hash_file2_));
+  EXPECT_FALSE(package_cache_.IsCached(key21, hash_file1_));
+  EXPECT_FALSE(package_cache_.IsCached(key22, hash_file2_));
+
+  EXPECT_EQ(0, package_cache_.Size());
+}
+
+TEST_F(PackageCacheTest, PurgeAppTest) {
+  // Cache two files for two apps.
+  Key key11(_T("app1"), _T("ver1"), _T("package1"));
+  Key key12(_T("app1"), _T("ver1"), _T("package2"));
+  Key key21(_T("app2"), _T("ver2"), _T("package1"));
+  Key key22(_T("app2"), _T("ver2"), _T("package2"));
+
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key11,
+                                              source_file1_,
+                                              hash_file1_));
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key12,
+                                              source_file2_,
+                                              hash_file2_));
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key21,
+                                              source_file1_,
+                                              hash_file1_));
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key22,
+                                              source_file2_,
+                                              hash_file2_));
+
+  EXPECT_TRUE(package_cache_.IsCached(key11, hash_file1_));
+  EXPECT_TRUE(package_cache_.IsCached(key12, hash_file2_));
+  EXPECT_TRUE(package_cache_.IsCached(key21, hash_file1_));
+  EXPECT_TRUE(package_cache_.IsCached(key22, hash_file2_));
+
+  EXPECT_EQ(2 * (size_file1_ + size_file2_), package_cache_.Size());
+
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeApp(_T("app1")));
+
+  EXPECT_FALSE(package_cache_.IsCached(key11, hash_file1_));
+  EXPECT_FALSE(package_cache_.IsCached(key12, hash_file2_));
+  EXPECT_TRUE(package_cache_.IsCached(key21, hash_file1_));
+  EXPECT_TRUE(package_cache_.IsCached(key22, hash_file2_));
+
+  EXPECT_EQ(size_file1_ + size_file2_, package_cache_.Size());
+
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeApp(_T("app2")));
+
+  EXPECT_FALSE(package_cache_.IsCached(key11, hash_file1_));
+  EXPECT_FALSE(package_cache_.IsCached(key12, hash_file2_));
+  EXPECT_FALSE(package_cache_.IsCached(key21, hash_file1_));
+  EXPECT_FALSE(package_cache_.IsCached(key22, hash_file2_));
+
+  EXPECT_EQ(0, package_cache_.Size());
+}
+
+TEST_F(PackageCacheTest, PurgeAppLowerVersionsTest) {
+  EXPECT_EQ(E_INVALIDARG, package_cache_.PurgeAppLowerVersions(_T("app1"),
+                                                               _T("1")));
+
+  Key key_10_1(_T("app1"), _T("1.0.0.0"), _T("package1"));
+  Key key_10_2(_T("app1"), _T("1.0.0.0"), _T("package2"));
+  Key key_11_1(_T("app1"), _T("1.1.0.0"), _T("package1"));
+  Key key_21_2(_T("app1"), _T("2.1.0.0"), _T("package2"));
+
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key_10_1,
+                                              source_file1_,
+                                              hash_file1_));
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key_10_2,
+                                              source_file2_,
+                                              hash_file2_));
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key_11_1,
+                                              source_file1_,
+                                              hash_file1_));
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key_21_2,
+                                              source_file2_,
+                                              hash_file2_));
+
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeAppLowerVersions(_T("app1"),
+                                                                _T("1.0.0.0")));
+  EXPECT_TRUE(package_cache_.IsCached(key_10_1, hash_file1_));
+  EXPECT_TRUE(package_cache_.IsCached(key_10_2, hash_file2_));
+  EXPECT_TRUE(package_cache_.IsCached(key_11_1, hash_file1_));
+  EXPECT_TRUE(package_cache_.IsCached(key_21_2, hash_file2_));
+
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeAppLowerVersions(_T("app1"),
+                                                                _T("1.1.0.0")));
+  EXPECT_FALSE(package_cache_.IsCached(key_10_1, hash_file1_));
+  EXPECT_FALSE(package_cache_.IsCached(key_10_2, hash_file2_));
+  EXPECT_TRUE(package_cache_.IsCached(key_11_1, hash_file1_));
+  EXPECT_TRUE(package_cache_.IsCached(key_21_2, hash_file2_));
+
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeAppLowerVersions(_T("app1"),
+                                                                _T("2.1.0.0")));
+  EXPECT_FALSE(package_cache_.IsCached(key_11_1, hash_file1_));
+  EXPECT_TRUE(package_cache_.IsCached(key_21_2, hash_file2_));
+
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeAppLowerVersions(_T("app1"),
+                                                                _T("2.2.0.0")));
+  EXPECT_FALSE(package_cache_.IsCached(key_21_2, hash_file2_));
+
+  EXPECT_EQ(0, package_cache_.Size());
+}
+
+TEST_F(PackageCacheTest, PurgeAll) {
+  // Cache two files for two apps.
+  Key key11(_T("app1"), _T("ver1"), _T("package1"));
+  Key key12(_T("app1"), _T("ver1"), _T("package2"));
+  Key key21(_T("app2"), _T("ver2"), _T("package1"));
+  Key key22(_T("app2"), _T("ver2"), _T("package2"));
+
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key11,
+                                              source_file1_,
+                                              hash_file1_));
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key12,
+                                              source_file2_,
+                                              hash_file2_));
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key21,
+                                              source_file1_,
+                                              hash_file1_));
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key22,
+                                              source_file2_,
+                                              hash_file2_));
+
+  EXPECT_TRUE(package_cache_.IsCached(key11, hash_file1_));
+  EXPECT_TRUE(package_cache_.IsCached(key12, hash_file2_));
+  EXPECT_TRUE(package_cache_.IsCached(key21, hash_file1_));
+  EXPECT_TRUE(package_cache_.IsCached(key22, hash_file2_));
+
+  EXPECT_EQ(2 * (size_file1_ + size_file2_), package_cache_.Size());
+
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeAll());
+
+  EXPECT_FALSE(package_cache_.IsCached(key11, hash_file1_));
+  EXPECT_FALSE(package_cache_.IsCached(key12, hash_file2_));
+  EXPECT_FALSE(package_cache_.IsCached(key21, hash_file1_));
+  EXPECT_FALSE(package_cache_.IsCached(key22, hash_file2_));
+
+  EXPECT_EQ(0, package_cache_.Size());
+}
+
+TEST_F(PackageCacheTest, PurgeOldPackagesIfOverSizeLimit) {
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeAll());
+
+  const int kCacheSizeLimitMB = 2;
+  SetCacheSizeLimitMB(kCacheSizeLimitMB);
+
+  const uint64 kSizeLimitBytes = 1024LL * 1024 * kCacheSizeLimitMB;
+
+  Key key0(_T("app0"), _T("version0"), _T("package0"));
+
+  // Keep adding packages until we exceed cache limit.
+  uint64 current_size = 0;
+  int i = 0;
+  while (current_size <= kSizeLimitBytes) {
+    CString app;
+    CString version;
+    CString package;
+    app.Format(_T("app%d"), i);
+    version.Format(_T("version%d"), i);
+    package.Format(_T("package%d"), i);
+    EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(Key(app, version, package),
+                                                source_file1_,
+                                                hash_file1_));
+    current_size += size_file1_;
+    EXPECT_EQ(current_size, package_cache_.Size());
+    ++i;
+  }
+
+  // Verify that cache size limit is exceeded.
+  EXPECT_GT(package_cache_.Size(), kSizeLimitBytes);
+  EXPECT_TRUE(package_cache_.IsCached(key0, hash_file1_));
+
+  package_cache_.PurgeOldPackagesIfNecessary();
+
+  // Verify that the oldes package is purged and the cache size is below limit.
+  EXPECT_FALSE(package_cache_.IsCached(key0, hash_file1_));
+  EXPECT_LE(package_cache_.Size(), kSizeLimitBytes);
+}
+
+TEST_F(PackageCacheTest, PurgeExpiredCacheFiles) {
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.PurgeAll());
+
+  const int kCacheLifeLimitDays = 100;
+  SetCacheTimeLimitDays(kCacheLifeLimitDays);
+
+  Key key1(_T("app1"), _T("version1"), _T("package1"));
+  Key key2(_T("app2"), _T("version2"), _T("package2"));
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key1,
+                                              source_file1_,
+                                              hash_file1_));
+  EXPECT_HRESULT_SUCCEEDED(package_cache_.Put(key2,
+                                              source_file2_,
+                                              hash_file2_));
+  EXPECT_TRUE(package_cache_.IsCached(key1, hash_file1_));
+  EXPECT_TRUE(package_cache_.IsCached(key2, hash_file2_));
+
+  // Expires one package and verifies it is purged.
+  EXPECT_HRESULT_SUCCEEDED(ExpireCache(key1));
+  package_cache_.PurgeOldPackagesIfNecessary();
+  EXPECT_FALSE(package_cache_.IsCached(key1, hash_file1_));
+  EXPECT_TRUE(package_cache_.IsCached(key2, hash_file2_));
+
+  // Expires another package and verifies it is purged.
+  EXPECT_HRESULT_SUCCEEDED(ExpireCache(key2));
+  package_cache_.PurgeOldPackagesIfNecessary();
+  EXPECT_FALSE(package_cache_.IsCached(key1, hash_file1_));
+  EXPECT_FALSE(package_cache_.IsCached(key2, hash_file2_));
+}
+
+TEST_F(PackageCacheTest, AuthenticateFile) {
+  EXPECT_HRESULT_SUCCEEDED(PackageCache::AuthenticateFile(source_file1_,
+                                                          hash_file1_));
+  EXPECT_EQ(SIGS_E_INVALID_SIGNATURE,
+            PackageCache::AuthenticateFile(source_file1_, hash_file2_));
+}
+
+}  // namespace omaha
+
diff --git a/goopdate/process_launcher.cc b/goopdate/process_launcher.cc
new file mode 100644
index 0000000..3b77287
--- /dev/null
+++ b/goopdate/process_launcher.cc
@@ -0,0 +1,128 @@
+// Copyright 2008-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include "omaha/goopdate/process_launcher.h"
+#include "omaha/base/browser_utils.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/exception_barrier.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/system.h"
+#include "omaha/base/vista_utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/core/google_update_core.h"
+
+namespace omaha {
+
+ProcessLauncher::ProcessLauncher() : StdMarshalInfo(true) {
+  CORE_LOG(L6, (_T("[ProcessLauncher::ProcessLauncher]")));
+}
+
+ProcessLauncher::~ProcessLauncher() {
+  CORE_LOG(L6, (_T("[ProcessLauncher::~ProcessLauncher]")));
+}
+
+STDMETHODIMP ProcessLauncher::LaunchCmdLine(const TCHAR* cmd_line) {
+  CORE_LOG(L1, (_T("[ProcessLauncher::LaunchCmdLine][%s]"), cmd_line));
+  // The exception barrier is needed, because any exceptions that are thrown
+  // in this method will get caught by the COM run time. We compile with
+  // exceptions off, and do not expect to throw any exceptions. This barrier
+  // will treat an exception in this method as a unhandled exception.
+  ExceptionBarrier barrier;
+  if (cmd_line == NULL) {
+    return E_INVALIDARG;
+  }
+
+  // http://b/3329538: In the impersonated case, need to create a fresh
+  // environment block and ::CreateProcess. RunAsCurrentUser does just that.
+  HRESULT hr = vista::RunAsCurrentUser(cmd_line);
+  if (FAILED(hr)) {
+    UTIL_LOG(LW, (_T("[RunAsCurrentUser failed][0x%x]"), hr));
+  }
+  return hr;
+}
+
+STDMETHODIMP ProcessLauncher::LaunchBrowser(DWORD type, const TCHAR* url) {
+  CORE_LOG(L1, (_T("[ProcessLauncher::LaunchBrowser][%d][%s]"), type, url));
+  // The exception barrier is needed, because any exceptions that are thrown
+  // in this method will get caught by the COM run time. We compile with
+  // exceptions off, and do not expect to throw any exceptions. This barrier
+  // will treat an exception in this method as a unhandled exception.
+  ExceptionBarrier barrier;
+  if (type >= BROWSER_MAX || url == NULL) {
+    return E_INVALIDARG;
+  }
+  return RunBrowser(static_cast<BrowserType>(type), url);
+}
+
+// This method delegates to the internal interface exposed by the system
+// service, and if the service cannot be instantiated, exposed by a Local COM
+// Server.
+//
+// Non elevated callers can request a command to be run elevated.
+// The command must be registered before by elevated code to prevent
+// launching untrusted commands. The security of the command is based on
+// having the correct registry ACLs for the machine Omaha registry.
+STDMETHODIMP ProcessLauncher::LaunchCmdElevated(const WCHAR* app_guid,
+                                                const WCHAR* cmd_id,
+                                                DWORD caller_proc_id,
+                                                ULONG_PTR* proc_handle) {
+  CORE_LOG(L3, (_T("[ProcessLauncher::LaunchCmdElevated]")
+                _T("[app %s][cmd %s][pid %d]"),
+                app_guid, cmd_id, caller_proc_id));
+
+  ExceptionBarrier barrier;
+
+  ASSERT1(app_guid);
+  ASSERT1(cmd_id);
+  ASSERT1(proc_handle);
+
+  CComPtr<IGoogleUpdateCore> google_update_core;
+  HRESULT hr =
+      google_update_core.CoCreateInstance(__uuidof(GoogleUpdateCoreClass));
+
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CoCreate GoogleUpdateCoreClass failed][0x%x]"), hr));
+
+    if (!vista_util::IsVistaOrLater() && !vista_util::IsUserAdmin()) {
+      return hr;
+    }
+
+    hr = System::CoCreateInstanceAsAdmin(NULL,
+                                         __uuidof(GoogleUpdateCoreMachineClass),
+                                         IID_PPV_ARGS(&google_update_core));
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[GoogleUpdateCoreMachineClass failed][0x%x]"), hr));
+      return hr;
+    }
+  }
+
+  ASSERT1(google_update_core);
+  hr = ::CoSetProxyBlanket(google_update_core, RPC_C_AUTHN_DEFAULT,
+      RPC_C_AUTHZ_DEFAULT, COLE_DEFAULT_PRINCIPAL, RPC_C_AUTHN_LEVEL_DEFAULT,
+      RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_DEFAULT);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return google_update_core->LaunchCmdElevated(app_guid,
+                                               cmd_id,
+                                               caller_proc_id,
+                                               proc_handle);
+}
+
+}  // namespace omaha
+
diff --git a/goopdate/process_launcher.h b/goopdate/process_launcher.h
new file mode 100644
index 0000000..8272a17
--- /dev/null
+++ b/goopdate/process_launcher.h
@@ -0,0 +1,96 @@
+// Copyright 2008-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Contains ProcessLauncher class to launch a process using a COM interface. The
+// COM object is typically created by the caller as a medium integrity object
+// impersonating the identity of the active user. This is to allow for launching
+// a medium-integrity process from a high-integrity process.
+
+#ifndef OMAHA_GOOPDATE_PROCESS_LAUNCHER_H__
+#define OMAHA_GOOPDATE_PROCESS_LAUNCHER_H__
+
+#include <windows.h>
+#include <atlbase.h>
+#include <atlcom.h>
+#include <atlstr.h>
+#include "omaha/base/atlregmapex.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/goopdate/com_proxy.h"
+#include "omaha/goopdate/non_localized_resource.h"
+
+// Generated by MIDL in the "BUILD_MODE.OBJ_ROOT + SETTINGS.SUBDIR".
+#include "goopdate/omaha3_idl.h"
+
+namespace omaha {
+
+const TCHAR* const kTlbVersion    = _T("1.0");
+const TCHAR* const kProcessWorkerDescription =
+    _T("Google Update Process Launcher Class");
+
+class ATL_NO_VTABLE ProcessLauncher
+    : public CComObjectRootEx<CComMultiThreadModel>,
+      public CComCoClass<ProcessLauncher, &__uuidof(ProcessLauncherClass)>,
+      public IProcessLauncher,
+      public StdMarshalInfo {
+ public:
+  ProcessLauncher();
+  virtual ~ProcessLauncher();
+
+  DECLARE_NOT_AGGREGATABLE(ProcessLauncher)
+  DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+  DECLARE_REGISTRY_RESOURCEID_EX(IDR_LOCAL_SERVER_RGS)
+
+  #pragma warning(push)
+  // C4640: construction of local static object is not thread-safe
+  #pragma warning(disable : 4640)
+  BEGIN_REGISTRY_MAP()
+    REGMAP_ENTRY(_T("HKROOT"),       goopdate_utils::GetHKRoot())
+    REGMAP_MODULE2(_T("MODULE"),     kOmahaOnDemandFileName)
+    REGMAP_ENTRY(_T("VERSION"),      kTlbVersion)
+    REGMAP_ENTRY(_T("PROGID"),       kProgIDProcessLauncher)
+    REGMAP_ENTRY(_T("DESCRIPTION"),  kProcessWorkerDescription)
+    REGMAP_UUID(_T("CLSID"),         __uuidof(ProcessLauncherClass))
+  END_REGISTRY_MAP()
+  #pragma warning(pop)
+
+  // C4505: unreferenced IUnknown local functions have been removed
+  #pragma warning(disable : 4505)
+  BEGIN_COM_MAP(ProcessLauncher)
+    COM_INTERFACE_ENTRY(IProcessLauncher)
+    COM_INTERFACE_ENTRY(IStdMarshalInfo)
+  END_COM_MAP()
+
+  // Launches a command line at medium integrity.
+  STDMETHOD(LaunchCmdLine)(const TCHAR* cmd_line);
+
+  // Launches the appropriate browser.
+  STDMETHOD(LaunchBrowser)(DWORD type, const TCHAR* url);
+
+  // Launches a command line elevated.
+  STDMETHOD(LaunchCmdElevated)(const WCHAR* app_guid,
+                               const WCHAR* cmd_id,
+                               DWORD caller_proc_id,
+                               ULONG_PTR* proc_handle);
+
+ private:
+  DISALLOW_EVIL_CONSTRUCTORS(ProcessLauncher);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_PROCESS_LAUNCHER_H__
diff --git a/goopdate/program_instance.cc b/goopdate/program_instance.cc
deleted file mode 100644
index ba16544..0000000
--- a/goopdate/program_instance.cc
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-
-#include "omaha/goopdate/program_instance.h"
-#include "omaha/common/debug.h"
-
-namespace omaha {
-
-// The manner in which we prevent multiple instances of
-// goopdate running in the user session is to
-// use a local name for the singleton mutex, and
-// grab the mutex till the end of our lifetime.
-bool ProgramInstance::EnsureSingleInstance() {
-  return CheckSingleInstance();
-}
-
-bool ProgramInstance::CheckSingleInstance() {
-  ASSERT1(!mutex_name_.IsEmpty());
-
-  reset(mutex_, ::CreateMutex(NULL, false, mutex_name_));
-  if (!mutex_) {
-    // We were not able to create the mutex instance for some reason.
-    return false;
-  }
-  DWORD error = ::GetLastError();
-  if (error == ERROR_ALREADY_EXISTS) {
-    // The program instance is already running since the mutex already exists.
-    return false;
-  }
-  return true;
-}
-
-}  // namespace omaha
-
diff --git a/goopdate/program_instance.h b/goopdate/program_instance.h
deleted file mode 100644
index 98b0a4b..0000000
--- a/goopdate/program_instance.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// TODO(omaha): move the code to common.
-
-#ifndef OMAHA_GOOPDATE_PROGRAM_INSTANCE_H__
-#define OMAHA_GOOPDATE_PROGRAM_INSTANCE_H__
-
-#include <atlstr.h>
-#include "base/basictypes.h"
-#include "omaha/common/constants.h"
-#include "omaha/third_party/smartany/auto_any.h"
-
-namespace omaha {
-
-  // Helps limit the number of instances of a program. The class itself does not
-  // limit the number of instances. The calling code is expected to take action
-  // based on the return of EnsureSingleInstance function.
-  class ProgramInstance {
-   public:
-    explicit ProgramInstance(const TCHAR* mutex_name)
-        : mutex_name_(mutex_name) {}
-    virtual ~ProgramInstance() {}
-    bool EnsureSingleInstance();
-   private:
-    bool CheckSingleInstance();
-    CString mutex_name_;
-    auto_mutex mutex_;
-    DISALLOW_EVIL_CONSTRUCTORS(ProgramInstance);
-  };
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_PROGRAM_INSTANCE_H__
diff --git a/goopdate/request.cc b/goopdate/request.cc
deleted file mode 100644
index ac30fa7..0000000
--- a/goopdate/request.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// goopdate server request
-
-#include "omaha/goopdate/request.h"
-#include <stdlib.h>
-#include <vector>
-#include "omaha/common/commontypes.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/goopdate_utils.h"
-
-namespace omaha {
-
-Request::Request(bool is_machine)
-    : is_machine_(is_machine),
-      version_(GetVersionString()),
-      test_source_(ConfigManager::Instance()->GetTestSource()) {
-  GUID req_id = GUID_NULL;
-  VERIFY1(SUCCEEDED(::CoCreateGuid(&req_id)));
-  request_id_ = GuidToString(req_id);
-  VERIFY1(SUCCEEDED(goopdate_utils::GetOSInfo(&os_version_,
-                                              &os_service_pack_)));
-}
-
-Request::~Request() {
-}
-
-void Request::AddAppRequest(const AppRequest& app_request) {
-  app_requests_.push_back(app_request);
-}
-
-}  // namespace
diff --git a/goopdate/request.h b/goopdate/request.h
deleted file mode 100644
index 6248cdd..0000000
--- a/goopdate/request.h
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// goopdate server request
-
-#ifndef OMAHA_GOOPDATE_REQUEST_H__
-#define OMAHA_GOOPDATE_REQUEST_H__
-
-#include <atlstr.h>
-#include <map>
-#include <vector>
-#include "base/basictypes.h"
-#include "omaha/common/utils.h"
-#include "omaha/worker/application_data.h"
-#include "omaha/worker/app_request.h"
-#include "omaha/worker/ping_event.h"
-
-namespace omaha {
-
-class PingMock;
-
-class Request {
- public:
-  explicit Request(bool is_machine);
-  ~Request();
-
-  bool is_machine() const { return is_machine_; }
-  void set_is_machine(bool is_machine) { is_machine_ = is_machine; }
-  CString version() const { return version_; }
-  void set_version(const CString& version) { version_ = version; }
-  CString os_version() const { return os_version_; }
-  void set_os_version(const CString& os_version) { os_version_ = os_version; }
-  CString os_service_pack() const { return os_service_pack_; }
-  void set_os_service_pack(const CString& os_service_pack) {
-    os_service_pack_ = os_service_pack;
-  }
-  CString test_source() const { return test_source_; }
-  void set_test_source(const CString& test_source) {
-    test_source_ = test_source;
-  }
-  CString request_id() const { return request_id_; }
-  void set_request_id(const CString& request_id) {
-    request_id_ = request_id;
-  }
-
-  size_t get_request_count() const { return app_requests_.size(); }
-  AppRequestVector::const_iterator app_requests_begin() const {
-    return app_requests_.begin();
-  }
-  AppRequestVector::const_iterator app_requests_end() const {
-    return app_requests_.end();
-  }
-
-  // Request will hold onto and manage/release the app_request instance.
-  void AddAppRequest(const AppRequest& app_request);
-
- private:
-
-  bool is_machine_;
-  CString version_;
-  CString os_version_;
-  CString os_service_pack_;
-
-  // Identifies the source of the request as a test/production prober system.
-  CString test_source_;
-
-  // Unique identifier for this request, used to associate the same request
-  // received multiple times on the server.
-  CString request_id_;
-  AppRequestVector app_requests_;
-
-  friend class PingMock;
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(Request);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_REQUEST_H__
diff --git a/goopdate/request.xsd b/goopdate/request.xsd
deleted file mode 100644
index a5eafcd..0000000
--- a/goopdate/request.xsd
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<xs:schema 
-  attributeFormDefault="unqualified" 
-  elementFormDefault="qualified" 
-  targetNamespace="http://www.google.com/omaha/request" 
-  xmlns:xs="http://www.w3.org/2001/XMLSchema"
-  xmlns:ts="http://www.google.com/omaha/request">
-  <xs:simpleType name="RequestType">
-    <xs:restriction base="xs:string">
-      <xs:enumeration value="UpdateRequest" />
-    </xs:restriction>
-  </xs:simpleType>
-  <xs:simpleType name="GuidType">
-    <xs:restriction base="xs:string">
-      <xs:pattern value="{[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}}" />
-    </xs:restriction>
-  </xs:simpleType>
-  <xs:element name="requests">
-    <xs:complexType>
-      <xs:sequence>
-        <xs:element maxOccurs="1" minOccurs="1" name="omaha">
-          <xs:complexType>
-            <xs:sequence>
-              <xs:element name="version" type="xs:string" />
-              <xs:element name="lang" type="xs:string" />
-              <xs:element name="country" type="xs:string" />
-            </xs:sequence>
-          </xs:complexType>
-        </xs:element>
-        <xs:element maxOccurs="unbounded" name="request">
-          <xs:complexType>
-            <xs:sequence>
-              <xs:element name="version" type="xs:string" />
-              <xs:element name="lang" type="xs:string" />
-              <xs:element name="country" type="xs:string" />
-              <xs:element name="brand" type="xs:string" />
-              <xs:element minOccurs="0" name="rlz">
-                <xs:complexType mixed="true">
-                  <xs:simpleContent>
-                    <xs:extension base="xs:string">
-                      <xs:attribute name="appid" type="xs:string" use="optional" />
-                    </xs:extension>
-                  </xs:simpleContent>
-                </xs:complexType>
-              </xs:element>
-              <xs:element minOccurs="0" maxOccurs="unbounded" name="attr">
-                <xs:complexType mixed="true">
-                  <xs:attribute name="name" type="xs:string" />
-                </xs:complexType>
-              </xs:element>
-              <xs:element name="arguments" type="xs:string" />
-            </xs:sequence>
-            <xs:attribute name="appid" type="ts:GuidType" use="required" />
-            <xs:attribute name="appname" type="xs:string" use="optional" />
-            <xs:attribute name="type" type="ts:RequestType" use="required" />
-          </xs:complexType>
-        </xs:element>
-      </xs:sequence>
-      <xs:attribute name="ver" type="xs:decimal" use="required" />
-    </xs:complexType>
-  </xs:element>
-</xs:schema>
diff --git a/goopdate/resource.h b/goopdate/resource.h
deleted file mode 100644
index ee53aad..0000000
--- a/goopdate/resource.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_GOOPDATE_RESOURCE_H__
-#define OMAHA_GOOPDATE_RESOURCE_H__
-
-// For the IDI_APP
-#include "omaha/google_update/resource.h"
-
-// For IDD_INSTALL_STOPPED used in ui.
-#include "omaha/goopdate/resources/goopdateres/goopdate.grh"
-
-#define IDR_GOOGLE_UPDATE_WORKER_CLASS          1000
-#define IDR_GOOGLE_UPDATE_WORKER_CLASS_MACHINE  1001
-#define IDR_GOOGLE_UPDATE_SERVICE_APPID         1002
-#define IDR_GOOGLE_UPDATE_CORE_CLASS            1003
-#define IDI_ELEVATION_MONIKER_ICON              1004
-
-#endif  // OMAHA_GOOPDATE_RESOURCE_H__
-
diff --git a/goopdate/resource_manager.cc b/goopdate/resource_manager.cc
index 56d0ff2..7d731f2 100644
--- a/goopdate/resource_manager.cc
+++ b/goopdate/resource_manager.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,91 +12,160 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // ========================================================================
-//
-#include "omaha/goopdate/resource_manager.h"
 
+#include "omaha/goopdate/resource_manager.h"
 #include <windows.h>
 #include <map>
 #include <vector>
-#include "omaha/common/constants.h"
-#include "omaha/common/commontypes.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/file_ver.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/path.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/commontypes.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/file_ver.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/lang.h"
 
 namespace omaha {
 
+ResourceManager* ResourceManager::instance_ = NULL;
+
+HRESULT ResourceManager::CreateForDefaultLanguage(bool is_machine,
+                                                  const CString& resource_dir) {
+  return Create(is_machine,
+                resource_dir,
+                lang::GetDefaultLanguage(is_machine));
+}
+
+HRESULT ResourceManager::Create(
+    bool is_machine, const CString& resource_dir, const CString& lang) {
+  CString language = lang;
+  if (language.IsEmpty() || !lang::IsLanguageSupported(language)) {
+    language = lang::GetDefaultLanguage(is_machine);
+  }
+
+  if (!instance_) {
+    instance_ = new ResourceManager(is_machine, resource_dir);
+    return instance_->SetDefaultResourceByLanguage(language);
+  }
+  return S_OK;
+}
+
+void ResourceManager::Delete() {
+  ResourceManager* instance = NULL;
+  instance = omaha::interlocked_exchange_pointer(&instance_, instance);
+
+  delete instance;
+}
+
+ResourceManager& ResourceManager::Instance() {
+  ASSERT1(instance_);
+  return *instance_;
+}
+
 ResourceManager::ResourceManager(bool is_machine, const CString& resource_dir)
-    : resource_dll_(NULL),
-      is_machine_(is_machine),
-      resource_dir_(resource_dir) {
+    : is_machine_(is_machine),
+      resource_dir_(resource_dir),
+      saved_atl_resource_(NULL) {
 }
 
 ResourceManager::~ResourceManager() {
+  if (saved_atl_resource_) {
+    _AtlBaseModule.SetResourceInstance(saved_atl_resource_);
+  }
 }
 
-HRESULT ResourceManager::LoadResourceDll(const CString& language) {
-  HRESULT hr = LoadResourceDllInternal(language);
+HRESULT ResourceManager::SetDefaultResourceByLanguage(const CString& language) {
+  ResourceDllInfo dll_info;
+  HRESULT hr = GetResourceDllInfo(language, &dll_info);
   if (FAILED(hr)) {
-    OPT_LOG(LE, (_T("[Resource dll load failed.][0x%08x]"), hr));
     return hr;
   }
 
-  // All CString.LoadString and CreateDialog calls will use this module.
-  _AtlBaseModule.SetResourceInstance(resource_dll_);
+  // All CString.LoadString and CreateDialog calls should use the resource of
+  // the default language.
+  saved_atl_resource_ = _AtlBaseModule.SetResourceInstance(dll_info.dll_handle);
 
-  FileVer file_ver;
-  VERIFY1(file_ver.Open(resource_dll_filepath_));
-  language_ = file_ver.QueryValue(kLanguageVersionName);
-  OPT_LOG(L1, (_T("[Loaded resource dll %s]"), resource_dll_filepath_));
+  return hr;
+}
 
-  ASSERT1(!language_.IsEmpty());
-  ASSERT1(!resource_dll_filepath_.IsEmpty());
-  ASSERT1(resource_dll_);
+HRESULT ResourceManager::GetResourceDll(const CString& language,
+                                        HINSTANCE* dll_handle) {
+  ASSERT1(dll_handle);
 
+  ResourceDllInfo dll_info;
+  HRESULT hr = GetResourceDllInfo(language, &dll_info);
+  if (FAILED(hr)) {
+    return  hr;
+  }
+  *dll_handle = dll_info.dll_handle;
   return S_OK;
 }
 
-HRESULT ResourceManager::LoadResourceDllInternal(const CString& language) {
-  // First try to load the resource dll for the language parameter.
-  if (!language.IsEmpty() &&
-      SUCCEEDED(LoadLibraryAsDataFile(GetResourceDllName(language)))) {
+HRESULT ResourceManager::GetResourceDllInfo(const CString& language,
+                                            ResourceDllInfo* dll_info) {
+  ASSERT1(dll_info);
+  __mutexScope(lock_);
+
+  ASSERT1(lang::IsLanguageSupported(language));
+
+  LanguageToResourceMap::const_iterator it = resource_map_.find(language);
+  if (it != resource_map_.end()) {
+    *dll_info = it->second;
     return S_OK;
   }
+  return LoadResourceDll(language, dll_info);
+}
 
-  // If for some reason we don't have a language, look up the user
-  // locale and convert it into our lang string names and load that.
-  HRESULT hr = LoadLibraryAsDataFile(
-      GetResourceDllName(GetDefaultUserLanguage()));
+// Assumes that the language has not been loaded previously.
+HRESULT ResourceManager::LoadResourceDll(const CString& language,
+                                         ResourceDllInfo* dll_info) {
+  ASSERT1(dll_info);
+  ASSERT1(lang::IsLanguageSupported(language));
+  dll_info->dll_handle = NULL;
+
+  __mutexScope(lock_);
+
+  ASSERT1(resource_map_.find(language) == resource_map_.end());
+
+  // First try to load the resource dll for the language parameter.
+  HRESULT hr = LoadLibraryAsDataFile(GetResourceDllName(language), dll_info);
   if (FAILED(hr)) {
-    OPT_LOG(LE, (_T("[Could not load any language dll.]")));
+    CORE_LOG(LE, (_T("[Resource dll load failed.][0x%08x]"), hr));
     return hr;
   }
 
+  FileVer file_ver;
+  VERIFY1(file_ver.Open(dll_info->file_path));
+  dll_info->language = file_ver.QueryValue(kLanguageVersionName);
+  CORE_LOG(L1, (_T("[Loaded resource dll %s]"), dll_info->file_path));
+
+  resource_map_.insert(std::make_pair(language, *dll_info));
   return S_OK;
 }
 
-HRESULT ResourceManager::LoadLibraryAsDataFile(const CString& filename) {
+HRESULT ResourceManager::LoadLibraryAsDataFile(
+    const CString& filename,
+    ResourceDllInfo* dll_info) const {
   ASSERT1(!filename.IsEmpty());
+  ASSERT1(dll_info);
   ASSERT1(!resource_dir_.IsEmpty());
 
-  resource_dll_filepath_ = ConcatenatePath(resource_dir_, filename);
-  if (resource_dll_filepath_.IsEmpty()) {
+  dll_info->file_path = ConcatenatePath(resource_dir_, filename);
+  if (dll_info->file_path.IsEmpty()) {
     ASSERT1(false);
     return GOOPDATE_E_RESOURCE_DLL_PATH_EMPTY;
   }
-  resource_dll_ = ::LoadLibraryEx(resource_dll_filepath_,
-                                  NULL,
-                                  LOAD_LIBRARY_AS_DATAFILE);
-  if (!resource_dll_) {
+  dll_info->dll_handle = ::LoadLibraryEx(dll_info->file_path,
+                                         NULL,
+                                         LOAD_LIBRARY_AS_DATAFILE);
+  if (!dll_info->dll_handle) {
     HRESULT hr = HRESULTFromLastError();
-    OPT_LOG(L2, (_T("[Could not load resource dll %s.]"),
-                 resource_dll_filepath_));
+    CORE_LOG(L2, (_T("[Could not load resource dll %s.]"),
+                 dll_info->file_path));
     return hr;
   }
 
@@ -106,324 +175,24 @@
 CString ResourceManager::GetResourceDllName(const CString& language) {
   ASSERT1(!language.IsEmpty());
 
-  CString actual_language(language);
-  // Handle specific special cases
-  // * Chinese (Hong Kong) - separate language but uses the zh-TW resource DLL.
-  // * Hebrew synonyms - convert he to iw.
-  if (!language.CompareNoCase(_T("zh-HK"))) {
-    actual_language = _T("zh-TW");
-  } else if (!language.CompareNoCase(_T("he"))) {
-    actual_language = _T("iw");
-  }
-
   CString filename;
-  filename.Format(kGoopdateResourceDllName, actual_language);
+  filename.Format(kOmahaResourceDllNameFormat,
+                  lang::GetWrittenLanguage(language));
   return filename;
 }
 
-CString ResourceManager::GetDefaultUserLanguage() {
-  return GetLanguageForLangID(::GetUserDefaultLangID());
-}
-
-// If an exact match is not found, returns "en".
-CString ResourceManager::GetLanguageForLangID(LANGID langid) {
-  const TCHAR* const kUltimateFallbackLang = _T("en");
-
-  // Check for the default case.
-  if (!langid) {
-    return kUltimateFallbackLang;
-  }
-
-  // First try to find the exact match.
-  for (int i = 0; kLanguageTranslationTable[i].langid; i++) {
-    if (kLanguageTranslationTable[i].langid == langid) {
-      return kLanguageTranslationTable[i].lang;
-    }
-  }
-
-  return kUltimateFallbackLang;
-}
-
-bool ResourceManager::IsLanguageStringSupported(const CString& language) {
-  std::map<CString, bool> languages;
-  GetDistinctLanguageMapFromTranslationTable(&languages);
-  return languages.find(language) != languages.end();
-}
-
-void ResourceManager::GetSupportedLanguages(
-    std::vector<CString>* codes) {
-  ASSERT1(codes);
-  std::map<CString, bool> languages;
-  GetDistinctLanguageMapFromTranslationTable(&languages);
-  for (std::map<CString, bool>::const_iterator it = languages.begin();
-       it != languages.end();
-       ++it) {
-    codes->push_back(it->first);
-  }
-}
-
 void ResourceManager::GetSupportedLanguageDllNames(
     std::vector<CString>* filenames) {
   std::vector<CString> codes;
-  GetSupportedLanguages(&codes);
+  lang::GetSupportedLanguages(&codes);
 
   for (size_t i = 0; i < codes.size(); ++i) {
-    if (!codes[i].CompareNoCase(_T("zh-HK"))) {
-      // There is not a separate DLL for this language because we use zh-TW DLL.
+    if (lang::DoesSupportedLanguageUseDifferentId(codes[i])) {
+      // There is not a separate DLL for this language.
       continue;
     }
     filenames->push_back(GetResourceDllName(codes[i]));
   }
 }
 
-void ResourceManager::GetDistinctLanguageMapFromTranslationTable(
-    std::map<CString, bool>* languages) {
-  ASSERT1(languages);
-
-  for (int i = 0; kLanguageTranslationTable[i].langid; i++) {
-    CString lang = kLanguageTranslationTable[i].lang;
-    languages->insert(std::make_pair(lang, true));
-  }
-}
-
-
-const ResourceManager::LangIDAndPath
-    ResourceManager::kLanguageTranslationTable[] = {
-  // First we collect all main languages here which need special treatment.
-  // Special treatment might be necessary because of:
-  //  1.) Google has a special region code which is "Google specific". They
-  //      trump everything and should be at the beginning. (e.g. "iw")
-  //  2.) Usually a lot of languages are like "en-US", "en-GB",... and start
-  //      with the main language. However some languages like Norwegian do not
-  //      follow this scheme and we put them therefore at the top.
-  // The matching algorithm will look first for the full match, but at the same
-  // time it will look for a partial match. We want to add therefore the partial
-  // matches as high as possible to get them as a fallback.
-  { 0x0409, _T("en")         },  // _T("English (United States)")
-  { 0x040d, _T("iw")         },  // _T("Hebrew")
-  { 0x0464, _T("fil")        },  // _T("Tagalog")
-  { 0x0414, _T("no")         },  // _T("Norwegian (Bokmal, Norway)")
-
-  // and then we have all dialects here:
-  // { 0x041c, _T("sq-AL")      },  // _T("Albanian (Albania)")")
-  // { 0x0484, _T("gsw-FR")     },  // _T("Alsatian (France)")
-  // { 0x045e, _T("am-ET")      },  // _T("Amharic (Ethiopia)")
-  { 0x1401, _T("ar")  },  // _T("ar-DZ")      },  // _T("Arabic (Algeria)")
-  { 0x3c01, _T("ar")  },  // _T("ar-BH")      },  // _T("Arabic (Bahrain)")
-  { 0x0c01, _T("ar")  },  // _T("ar-EG")      },  // _T("Arabic (Egypt)")
-  { 0x0801, _T("ar")  },  // _T("ar-IQ")      },  // _T("Arabic (Iraq)")
-  { 0x2c01, _T("ar")  },  // _T("ar-JO")      },  // _T("Arabic (Jordan)")
-  { 0x3401, _T("ar")  },  // _T("ar-KW")      },  // _T("Arabic (Kuwait)")
-  { 0x3001, _T("ar")  },  // _T("ar-LB")      },  // _T("Arabic (Lebanon)")
-  { 0x1001, _T("ar")  },  // _T("ar-LY")      },  // _T("Arabic (Libya)")
-  { 0x1801, _T("ar")  },  // _T("ar-MA")      },  // _T("Arabic (Morocco)")
-  { 0x2001, _T("ar")  },  // _T("ar-OM")      },  // _T("Arabic (Oman)")
-  { 0x4001, _T("ar")  },  // _T("ar-QA")      },  // _T("Arabic (Qatar)")
-  { 0x0401, _T("ar")  },  // _T("ar-SA")      },  // _T("Arabic (Saudi Arabia)")
-  { 0x2801, _T("ar")  },  // _T("ar-SY")      },  // _T("Arabic (Syria)")
-  { 0x1c01, _T("ar")  },  // _T("ar-TN")      },  // _T("Arabic (Tunisia)")
-  { 0x3801, _T("ar")  },  // _T("ar-AE")      },  // _T("Arabic (U.A.E.)")
-  { 0x2401, _T("ar")  },  // _T("ar-YE")      },  // _T("Arabic (Yemen)")
-  { 0x042b, _T("ar")  },  // _T("hy-AM")      },  // _T("Armenian (Armenia)")
-  // { 0x044d, _T("as-IN")      },  // _T("Assamese (India)")
-  // { 0x082c, _T("az-Cyrl-AZ") },  // _T("Azeri (Azerbaijan, Cyrillic)")
-  // { 0x042c, _T("az-Latn-AZ") },  // _T("Azeri (Azerbaijan, Latin)")
-  // { 0x046d, _T("ba-RU")      },  // _T("Bashkir (Russia)")
-  // { 0x042d, _T("eu-ES")      },  // _T("Basque (Basque)")
-  // { 0x0423, _T("be-BY")      },  // _T("Belarusian (Belarus)")
-  { 0x0445, _T("bn")  },  // _T("bn-IN")      },  // _T("Bengali (India)")
-  // { 0x201a, _T("bs-Cyrl-BA") },  // _T("Bosnian (Bosnia and Herzegovina,
-  //                                       Cyrillic)")
-  // { 0x141a, _T("bs-Latn-BA") },  // _T("Bosnian (Bosnia and Herzegovina,
-  //                                       Latin)")
-  // { 0x047e, _T("br-FR")      },  // _T("Breton (France)")
-  { 0x0402, _T("bg")  },  // _T("bg-BG")   },  // _T("Bulgarian (Bulgaria)")
-  { 0x0403, _T("ca")  },  // _T("ca-ES")   },  // _T("Catalan (Catalan)")
-  { 0x0c04, _T("zh-HK") },  // _T("zh-HK")     // _T("Chinese
-                            //   (Hong Kong SAR, PRC)")
-  { 0x1404, _T("zh-CN") },  // _T("zh-MO")   },  // _T("Chinese (Macao SAR)")
-  { 0x0804, _T("zh-CN") },  // _T("zh-CN")   },  // _T("Chinese (PRC)")
-  { 0x1004, _T("zh-CN") },  // _T("zh-SG")   },  // _T("Chinese (Singapore)")
-  { 0x0404, _T("zh-TW") },  // _T("Chinese (Taiwan)")
-  { 0x101a, _T("hr")    },  // _T("hr-BA")   },  // _T("Croatian
-                            //   (Bosnia and Herzegovina, Latin)")
-  { 0x041a, _T("hr")    },  // _T("hr-HR")   },  // _T("Croatian (Croatia)")
-  { 0x0405, _T("cs")    },  // _T("cs-CZ")   },  // _T("Czech (Czech Republic)")
-  { 0x0406, _T("da")    },  // _T("da-DK")   },  // _T("Danish (Denmark)")
-  // { 0x048c, _T("gbz-AF")     },  // _T("Dari (Afghanistan)")
-  // { 0x0465, _T("dv-MV")      },  // _T("Divehi (Maldives)")
-  { 0x0813, _T("nl")  },  // _T("nl-BE")   },  // _T("Dutch (Belgium)")
-  { 0x0413, _T("nl")  },  // _T("nl-NL")   },  // _T("Dutch (Netherlands)")
-  { 0x0c09, _T("en")  },  // _T("en-AU")   },  // _T("English (Australia)")
-  { 0x2809, _T("en")  },  // _T("en-BZ")   },  // _T("English (Belize)")
-  { 0x1009, _T("en")  },  // _T("en-CA")   },  // _T("English (Canada)")
-  { 0x2409, _T("en")  },  // _T("en-029")  },  // _T("English (Caribbean)")
-  { 0x4009, _T("en")  },  // _T("en-IN")   },  // _T("English (India)")
-  { 0x1809, _T("en")  },  // _T("en-IE")   },  // _T("English (Ireland)")
-  { 0x2009, _T("en")  },  // _T("en-JM")   },  // _T("English (Jamaica)")
-  { 0x4409, _T("en")  },  // _T("en-MY")   },  // _T("English (Malaysia)")
-  { 0x1409, _T("en")  },  // _T("en-NZ")   },  // _T("English (New Zealand)")
-  { 0x3409, _T("en")  },  // _T("en-PH")   },  // _T("English (Philippines)")
-  { 0x4809, _T("en")  },  // _T("en-SG")   },  // _T("English (Singapore)")
-  { 0x1c09, _T("en")  },  // _T("en-ZA")   },  // _T("English (South Africa)")
-  { 0x2c09, _T("en")  },  // _T("en-TT")   },  // _T("English
-                          //   (Trinidad and Tobago)")
-  { 0x0809, _T("en-GB") },  // _T("English (United Kingdom)")
-  { 0x3009, _T("en")    },  // _T("en-ZW")      },  // _T("English (Zimbabwe)")
-  { 0x0425, _T("et")  },  // _T("et-EE")   },  // _T("Estonian (Estonia)")
-  // { 0x0438, _T("fo-FO")  },  // _T("Faroese (Faroe Islands)")
-  { 0x0464, _T("fil") },  // _T("fil-PH")    },  // _T("Filipino (Philippines)")
-  { 0x040b, _T("fi")  },  // _T("fi-FI")      },  // _T("Finnish (Finland)")
-  { 0x080c, _T("fr")  },  // _T("fr-BE")      },  // _T("French (Belgium)")
-  { 0x0c0c, _T("fr")  },  // _T("fr-CA")      },  // _T("French (Canada)")
-  { 0x040c, _T("fr")  },  // _T("fr-FR")      },  // _T("French (France)")
-  { 0x140c, _T("fr")  },  // _T("fr-LU")      },  // _T("French (Luxembourg)")
-  { 0x180c, _T("fr")  },  // _T("fr-MC")      },  // _T("French (Monaco)")
-  { 0x100c, _T("fr")  },  // _T("fr-CH")      },  // _T("French (Switzerland)")
-  // { 0x0462, _T("fy-NL")      },  // _T("Frisian (Netherlands)")
-  // { 0x0456, _T("gl-ES")      },  // _T("Galician (Spain)")
-  // { 0x0437, _T("ka-GE")      },  // _T("Georgian (Georgia)")
-  { 0x0c07, _T("de")  },  // _T("de-AT")   },  // _T("German (Austria)")
-  { 0x0407, _T("de")  },  // _T("de-DE")   },  // _T("German (Germany)")
-  { 0x1407, _T("de")  },  // _T("de-LI")   },  // _T("German (Liechtenstein)")
-  { 0x1007, _T("de")  },  // _T("de-LU")   },  // _T("German (Luxembourg)")
-  { 0x0807, _T("de")  },  // _T("de-CH")   },  // _T("German (Switzerland)")
-  { 0x0408, _T("el")  },  // _T("el-GR")   },  // _T("Greek (Greece)")
-  // { 0x046f, _T("kl-GL")      },  // _T("Greenlandic (Greenland)")
-  { 0x0447, _T("gu")  },  // _T("gu-IN")      },  // _T("Gujarati (India)")
-  // { 0x0468, _T("ha-Latn-NG") },  // _T("Hausa (Nigeria, Latin)")
-  // { 0x040d, _T("he-IL")      },  // _T("Hebrew (Israel)")
-  { 0x0439, _T("hi")  },  // _T("hi-IN")   },  // _T("Hindi (India)")
-  { 0x040e, _T("hu")  },  // _T("hu-HU")   },  // _T("Hungarian (Hungary)")
-  { 0x040f, _T("is")  },  // _T("is-IS")      },  // _T("Icelandic (Iceland)")
-  // { 0x0470, _T("ig-NG")      },  // _T("Igbo (Nigeria)")
-  { 0x0421, _T("id")  },  // _T("id-ID")    },  // _T("Indonesian (Indonesia)")
-  // { 0x085d, _T("iu-Latn-CA") },  // _T("Inuktitut (Canada, Latin)")
-  // { 0x045d, _T("iu-Cans-CA") },  // _T("Inuktitut (Canada, Syllabics)")
-  // { 0x083c, _T("ga-IE")      },  // _T("Irish (Ireland)")
-  { 0x0410, _T("it")  },  // _T("it-IT")      },  // _T("Italian (Italy)")
-  { 0x0810, _T("it")  },  // _T("it-CH")      },  // _T("Italian (Switzerland)")
-  { 0x0411, _T("ja")  },  // _T("ja-JP")      },  // _T("Japanese (Japan)")
-  { 0x044b, _T("kn")  },  // _T("kn-IN")      },  // _T("Kannada (India)")
-  // { 0x043f, _T("kk-KZ")      },  // _T("Kazakh (Kazakhstan)")
-  // { 0x0453, _T("kh-KH")      },  // _T("Khmer (Cambodia)")
-  // { 0x0486, _T("qut-GT")     },  // _T("K'iche (Guatemala)")
-  // { 0x0487, _T("rw-RW")      },  // _T("Kinyarwanda (Rwanda)")
-  // { 0x0457, _T("kok-IN")     },  // _T("Konkani (India)")
-  { 0x0812, _T("ko")         },  // _T("ko-Jo")      },  // _T("Korean (Johab)")
-  { 0x0412, _T("ko")         },  // _T("ko-KR")      },  // _T("Korean (Korea)")
-  // { 0x0440, _T("ky-KG")      },  // _T("Kyrgyz (Kyrgyzstan)")
-  // { 0x0454, _T("lo-LA")      },  // _T("Lao (Lao PDR)")
-  { 0x0426, _T("lv")  },  // _T("lv-LV")   },  // _T("Latvian (Latvia)")
-  { 0x0427, _T("lt")  },  // _T("lt-LT")   },  // _T("Lithuanian (Lithuania)")
-  // { 0x082e, _T("dsb-DE")     },  // _T("Lower Sorbian (Germany)")
-  // { 0x046e, _T("lb-LU")      },  // _T("Luxembourgish (Luxembourg)")
-  // { 0x042f, _T("mk-MK")      },  // _T("Macedonian (Macedonia, FYROM)")
-  { 0x083e, _T("ms")  },  // _T("ms-BN")  },  // _T("Malay (Brunei Darussalam)")
-  { 0x043e, _T("ms")  },  // _T("ms-MY")      },  // _T("Malay (Malaysia)")
-  { 0x044c, _T("ml")  },  // _T("ml-IN")      },  // _T("Malayalam (India)")
-  // { 0x043a, _T("mt-MT")      },  // _T("Maltese (Malta)")
-  // { 0x0481, _T("mi-NZ")      },  // _T("Maori (New Zealand)")
-  // { 0x047a, _T("arn-CL")     },  // _T("Mapudungun (Chile)")
-  { 0x044e, _T("mr")  },  // _T("mr-IN")      },  // _T("Marathi (India)")
-  // { 0x047c, _T("moh-CA")     },  // _T("Mohawk (Canada)")
-  // { 0x0450, _T("mn-Cyrl-MN") },  // _T("Mongolian (Mongolia)")
-  // { 0x0850, _T("mn-Mong-CN") },  // _T("Mongolian (PRC)")
-  // { 0x0461, _T("ne-NP")      },  // _T("Nepali (Nepal)")
-  // { 0x0414, _T("nb-NO")      },  // _T("Norwegian (Bokmal, Norway)")
-  // { 0x0814, _T("nn-NO")      },  // _T("Norwegian (Nynorsk, Norway)")
-  // { 0x0482, _T("oc-FR")      },  // _T("Occitan (France)")
-  { 0x0448, _T("or")  },  // _T("or-IN") },  // _T("Oriya (India)")
-  // { 0x0463, _T("ps-AF") },  // _T("Pashto (Afghanistan)")
-  { 0x0429, _T("fa")  },  // _T("fa-IR")      },  // _T("Persian (Iran)")
-  { 0x0415, _T("pl")  },  // _T("pl-PL")      },  // _T("Polish (Poland)")
-  { 0x0416, _T("pt-BR")      },  // _T("Portuguese (Brazil)")
-  { 0x0816, _T("pt-PT")      },  // _T("Portuguese (Portugal)")
-  // { 0x0446, _T("pa-IN")      },  // _T("Punjabi (India)")
-  // { 0x046b, _T("quz-BO")     },  // _T("Quechua (Bolivia)")
-  // { 0x086b, _T("quz-EC")     },  // _T("Quechua (Ecuador)")
-  // { 0x0c6b, _T("quz-PE")     },  // _T("Quechua (Peru)")
-  { 0x0418, _T("ro")  },  // _T("ro-RO")      },  // _T("Romanian (Romania)")
-  // { 0x0417, _T("rm-CH")      },  // _T("Romansh (Switzerland)")
-  { 0x0419, _T("ru")  },  // _T("ru-RU")      },  // _T("Russian (Russia)")
-  // { 0x243b, _T("smn-FI")     },  // _T("Sami (Inari, Finland)")
-  // { 0x103b, _T("smj-NO")     },  // _T("Sami (Lule, Norway)")
-  // { 0x143b, _T("smj-SE")     },  // _T("Sami (Lule, Sweden)")
-  // { 0x0c3b, _T("se-FI")      },  // _T("Sami (Northern, Finland)")
-  // { 0x043b, _T("se-NO")      },  // _T("Sami (Northern, Norway)")
-  // { 0x083b, _T("se-SE")      },  // _T("Sami (Northern, Sweden)")
-  // { 0x203b, _T("sms-FI")     },  // _T("Sami (Skolt, Finland)")
-  // { 0x183b, _T("sma-NO")     },  // _T("Sami (Southern, Norway)")
-  // { 0x1c3b, _T("sma-SE")     },  // _T("Sami (Southern, Sweden)")
-  // { 0x044f, _T("sa-IN")      },  // _T("Sanskrit (India)")
-  { 0x1c1a, _T("sr")  },  // _T("sr-Cyrl-BA") },  // _T("Serbian
-                          //   (Bosnia and Herzegovina, Cyrillic)")
-  { 0x181a, _T("sr")  },  // _T("sr-Latn-BA") },  // _T("Serbian
-                          //   (Bosnia and Herzegovina, Latin)")
-  { 0x0c1a, _T("sr")  },  // _T("sr-Cyrl-CS") },  // _T("Serbian
-                          //   (Serbia and Montenegro, Cyrillic)")
-  { 0x081a, _T("sr")  },  // _T("sr-Latn-CS") },  // _T("Serbian
-                          //   (Serbia and Montenegro, Latin)")
-  // { 0x046c, _T("ns-ZA")      },  // _T("Sesotho sa Leboa/Northern Sotho
-  //                                       (South Africa)")
-  // { 0x0432, _T("tn-ZA")      },  // _T("Setswana/Tswana (South Africa)")
-  // { 0x045b, _T("si-LK")      },  // _T("Sinhala (Sri Lanka)")
-  { 0x041b, _T("sk")  },  // _T("sk-SK")   },  // _T("Slovak (Slovakia)")
-  { 0x0424, _T("sl")  },  // _T("sl-SI")   },  // _T("Slovenian (Slovenia)")
-  { 0x2c0a, _T("es-419") },  // _T("es-AR")   },  // _T("Spanish (Argentina)")
-  { 0x400a, _T("es-419") },  // _T("es-BO")   },  // _T("Spanish (Bolivia)")
-  { 0x340a, _T("es-419") },  // _T("es-CL")   },  // _T("Spanish (Chile)")
-  { 0x240a, _T("es-419") },  // _T("es-CO")   },  // _T("Spanish (Colombia)")
-  { 0x140a, _T("es-419") },  // _T("es-CR")   },  // _T("Spanish (Costa Rica)")
-  { 0x1c0a, _T("es-419") },  // _T("es-DO")   },  // _T("Spanish
-                                                  //     (Dominican Republic)")
-  { 0x300a, _T("es-419") },  // _T("es-EC")   },  // _T("Spanish (Ecuador)")
-  { 0x440a, _T("es-419") },  // _T("es-SV")   },  // _T("Spanish (El Salvador)")
-  { 0x100a, _T("es-419") },  // _T("es-GT")   },  // _T("Spanish (Guatemala)")
-  { 0x480a, _T("es-419") },  // _T("es-HN")   },  // _T("Spanish (Honduras)")
-  { 0x080a, _T("es-419") },  // _T("es-MX")   },  // _T("Spanish (Mexico)")
-  { 0x4c0a, _T("es-419") },  // _T("es-NI")   },  // _T("Spanish (Nicaragua)")
-  { 0x180a, _T("es-419") },  // _T("es-PA")   },  // _T("Spanish (Panama)")
-  { 0x3c0a, _T("es-419") },  // _T("es-PY")   },  // _T("Spanish (Paraguay)")
-  { 0x280a, _T("es-419") },  // _T("es-PE")   },  // _T("Spanish (Peru)")
-  { 0x500a, _T("es-419") },  // _T("es-PR")   },  // _T("Spanish (Puerto Rico)")
-  { 0x0c0a, _T("es")     },  // _T("es-ES")   },  // _T("Spanish (Spain)")
-  { 0x040a, _T("es")     },  // _T("es-ES_tradnl")   },
-                             // _T("Spanish (Spain, Traditional Sort)")
-  { 0x540a, _T("es-419") },  // _T("es-US")   },  // _T("Spanish
-                                                  //     (United States)")
-  { 0x380a, _T("es-419") },  // _T("es-UY")   },  // _T("Spanish (Uruguay)")
-  { 0x200a, _T("es-419") },  // _T("es-VE")   },  // _T("Spanish (Venezuela)")
-  // { 0x0441, _T("sw-KE")      },  // _T("Swahili (Kenya)")
-  { 0x081d, _T("sv")  },  // _T("sv-FI")      },  // _T("Swedish (Finland)")
-  { 0x041d, _T("sv")  },  // _T("sv-SE")      },  // _T("Swedish (Sweden)")
-  // { 0x045a, _T("syr-SY")     },  // _T("Syriac (Syria)")
-  // { 0x0428, _T("tg-Cyrl-TJ") },  // _T("Tajik (Tajikistan)")
-  // { 0x085f, _T("tmz-Latn-DZ")},  // _T("Tamazight (Algeria, Latin)")
-  { 0x0449, _T("ta")  },  // _T("ta-IN")      },  // _T("Tamil (India)")
-  // { 0x0444, _T("tt-RU") },  // _T("Tatar (Russia)")
-  { 0x044a, _T("te")  },  // _T("te-IN") }, // _T("Telugu (India)")
-  { 0x041e, _T("th")  },  // _T("th-TH")      },  // _T("Thai (Thailand)")
-  // { 0x0851, _T("bo-BT")      },  // _T("Tibetan (Bhutan)")
-  // { 0x0451, _T("bo-CN")      },  // _T("Tibetan (PRC)")
-  { 0x041f, _T("tr")  },  // _T("tr-TR")      },  // _T("Turkish (Turkey)")
-  // { 0x0442, _T("tk-TM")      },  // _T("Turkmen (Turkmenistan)")
-  // { 0x0480, _T("ug-CN")      },  // _T("Uighur (PRC)")
-  { 0x0422, _T("uk")  },  // _T("uk-UA")      },  // _T("Ukrainian (Ukraine)")
-  // { 0x042e, _T("wen-DE")     },  // _T("Upper Sorbian (Germany)")
-  // { 0x0820,  _T("tr-IN")      },  // _T("Urdu(India)")
-  { 0x0420, _T("ur")  },  // _T("ur-PK") },  // _T("Urdu (Pakistan)")
-  // { 0x0843, _T("uz-Cyrl-UZ") }, // _T("Uzbek (Uzbekistan, Cyrillic)")
-  // { 0x0443, _T("uz-Latn-UZ") },  // _T("Uzbek (Uzbekistan, Latin)")
-  { 0x042a, _T("vi")  },  // _T("vi-VN")      },  // _T("Vietnamese (Vietnam)")
-  // { 0x0452, _T("cy-GB")      },  // _T("Welsh (United Kingdom)")
-  // { 0x0488, _T("wo-SN")      },  // _T("Wolof (Senegal)")
-  // { 0x0434, _T("xh-ZA")      },  // _T("Xhosa/isiXhosa (South Africa)")
-  // { 0x0485, _T("sah-RU")     },  // _T("Yakut (Russia)")
-  // { 0x0478, _T("ii-CN")      },  // _T("Yi (PRC)")
-  // { 0x046a, _T("yo-NG") }, // _T("Yoruba (Nigeria)")
-  // { 0x0435, _T("zu-ZA") }, // _T("Zulu/isiZulu (South Africa)")
-  { 0x0000, _T("")           }   // The list termination.
-};
-
-}  // namespace omaha.
+}  // namespace omaha
diff --git a/goopdate/resource_manager.h b/goopdate/resource_manager.h
index 26a20be..66348ba 100644
--- a/goopdate/resource_manager.h
+++ b/goopdate/resource_manager.h
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -14,7 +14,6 @@
 // ========================================================================
 //
 // Omaha resource manager.
-// Design Notes: Currently the resource manager only supports one language.
 
 #ifndef OMAHA_GOOPDATE_RESOURCE_MANAGER_H__
 #define OMAHA_GOOPDATE_RESOURCE_MANAGER_H__
@@ -23,59 +22,72 @@
 #include <map>
 #include <vector>
 #include "base/basictypes.h"
+#include "base/synchronized.h"
 
 namespace omaha {
 
 class ResourceManager {
  public:
+  // Create must be called before going multithreaded.
+  static HRESULT CreateForDefaultLanguage(bool is_machine,
+                                          const CString& resource_dir);
+  static HRESULT Create(bool is_machine,
+                        const CString& resource_dir,
+                        const CString& lang);
+  static void Delete();
+
+  static ResourceManager& Instance();
+
+  static void GetSupportedLanguageDllNames(std::vector<CString>* filenames);
+
+  // Gets resource DLL handle for the given language. DLL will be loaded if
+  // necessary.
+  HRESULT GetResourceDll(const CString& language, HINSTANCE* dll_handle);
+
+ private:
+  struct ResourceDllInfo {
+    ResourceDllInfo() : dll_handle(NULL) {}
+
+    HMODULE dll_handle;
+    CString file_path;
+    CString language;
+  };
+
   ResourceManager(bool is_machine, const CString& resource_dir);
   ~ResourceManager();
 
-  // Loads the resource dll and sets it as the default resource dll in ATL.
   // The resource manager tries to load the resource dll corresponding to
   // the language in the following order:
   // 1. Language parameter.
   // 2. Language in the registry.
   // 3. First file returned by NTFS in the module directory.
-  HRESULT LoadResourceDll(const CString& language);
-  static CString GetDefaultUserLanguage();
-  static CString GetLanguageForLangID(LANGID langid);
-  static bool IsLanguageStringSupported(const CString& language);
-  static void GetSupportedLanguages(std::vector<CString>* codes);
-  static void GetSupportedLanguageDllNames(std::vector<CString>* filenames);
-  HMODULE resource_dll() const { return resource_dll_; }
-  CString language() const { return language_; }
-  CString resource_dll_filepath() const { return resource_dll_filepath_; }
+  HRESULT LoadResourceDll(const CString& language, ResourceDllInfo* dll_info);
+  HRESULT SetDefaultResourceByLanguage(const CString& language);
+  HRESULT LoadLibraryAsDataFile(const CString& filename,
+                                ResourceDllInfo* dll_info) const;
 
- private:
+  // Gets resource DLL info for the given language. DLL will be loaded if
+  // necessary.
+  HRESULT GetResourceDllInfo(const CString& language,
+                             ResourceDllInfo* dll_info);
+
   static CString GetResourceDllName(const CString& language);
-  HRESULT LoadResourceDllInternal(const CString& language);
-  HRESULT LoadLibraryAsDataFile(const CString& filename);
 
-  // The bool is only here as a key for the map.  This could be a hash_set but
-  // the compiler doesn't like hash_set<CString>.
-  static void GetDistinctLanguageMapFromTranslationTable(
-      std::map<CString, bool>* map_lang);
+  LLock lock_;
+  typedef std::map<CString, ResourceDllInfo> LanguageToResourceMap;
 
-  HMODULE resource_dll_;
   bool is_machine_;
   CString resource_dir_;
-  CString language_;
-  CString resource_dll_filepath_;
+  LanguageToResourceMap resource_map_;
+  HINSTANCE saved_atl_resource_;
 
-  // This is the structure of the table which contains the language identifier
-  // and the associated language string.
-  struct LangIDAndPath {
-    LANGID langid;
-    TCHAR lang[12];
-  };
-  static const LangIDAndPath kLanguageTranslationTable[];
+  static ResourceManager* instance_;
 
   friend class ResourceManagerTest;
 
   DISALLOW_EVIL_CONSTRUCTORS(ResourceManager);
 };
 
-}  // namespace omaha.
+}  // namespace omaha
 
 #endif  // OMAHA_GOOPDATE_RESOURCE_MANAGER_H__
diff --git a/goopdate/resource_manager_unittest.cc b/goopdate/resource_manager_unittest.cc
index d850050..235856b 100644
--- a/goopdate/resource_manager_unittest.cc
+++ b/goopdate/resource_manager_unittest.cc
@@ -17,61 +17,112 @@
 
 #include <map>
 #include <vector>
-#include "omaha/common/app_util.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/file.h"
-#include "omaha/common/path.h"
-#include "omaha/common/string.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/string.h"
+#include "omaha/common/lang.h"
 #include "omaha/goopdate/resource_manager.h"
 #include "omaha/goopdate/resources/goopdateres/goopdate.grh"
 #include "omaha/testing/unit_test.h"
 
 namespace omaha {
 
+namespace {
+
+const int kNumberOfLanguageDlls = 55;
+
+}  // namespace
+
 class ResourceManagerTest : public testing::Test {
  protected:
   virtual void SetUp() {
-    path_ = ConcatenatePath(app_util::GetModuleDirectory(NULL),
-                            _T("unittest_support\\Omaha_1.2.x_resources"));
-    manager_.reset(new ResourceManager(false, path_));
+    path_ = app_util::GetModuleDirectory(NULL);
+    EXPECT_HRESULT_SUCCEEDED(
+        ResourceManager::CreateForDefaultLanguage(false, path_));
   }
 
   virtual void TearDown() {
+    ResourceManager::Delete();
   }
 
   void SetMachine(bool is_machine) {
-    manager_->is_machine_ = is_machine;
+    ResourceManager::instance_->is_machine_ = is_machine;
   }
 
   void SetResourceDir(const CString& resource_dir) {
-    manager_->resource_dir_ = resource_dir;
+    ResourceManager::instance_->resource_dir_ = resource_dir;
   }
 
-  void GetDistinctLanguageMapFromTranslationTable(
-      std::map<CString, bool>* languages) {
-    manager_->GetDistinctLanguageMapFromTranslationTable(languages);
+  CString GetResourceDir() const {
+    return ResourceManager::instance_->resource_dir_;
   }
 
   CString GetLang(LANGID langid) {
-    return manager_->GetLanguageForLangID(langid);
+    return lang::GetLanguageForLangID(langid);
+  }
+
+  void VerifyLoadingResourceDll(const CString& lang, bool is_success) {
+    ResourceManager::ResourceDllInfo dll_info;
+
+    HRESULT hr = ResourceManager::Instance().GetResourceDllInfo(lang,
+                                                                &dll_info);
+    if (is_success) {
+      EXPECT_HRESULT_SUCCEEDED(hr);
+      EXPECT_TRUE(dll_info.dll_handle != NULL);
+      EXPECT_STREQ(lang, dll_info.language);
+
+      CString expected_file_name;
+      expected_file_name.Format(kOmahaResourceDllNameFormat, lang);
+      CString expected_path = ConcatenatePath(path_, expected_file_name);
+      EXPECT_STREQ(expected_path, dll_info.file_path);
+    } else {
+      EXPECT_HRESULT_FAILED(hr);
+      EXPECT_EQ(NULL, dll_info.dll_handle);
+      EXPECT_STREQ(_T(""), dll_info.language);
+    }
   }
 
   static CString GetResourceDllName(const CString& language) {
     return ResourceManager::GetResourceDllName(language);
   }
 
-  scoped_ptr<ResourceManager> manager_;
   CString path_;
 };
 
+// Disables the default resources used for unit testing and restores them after
+// the test.
+// For some reason, the _AtlBaseModule.SetResourceInstance() call in
+// ResourceManager does not replace the existing resources, so they must be
+// unloaded first.
+class ResourceManagerResourcesProtectedTest : public ResourceManagerTest {
+ protected:
+  // Assumes that the default resources are the first loaded at index 0.
+  virtual void SetUp() {
+    ResourceManagerTest::SetUp();
+
+     default_resources_ = _AtlBaseModule.GetHInstanceAt(0);
+    _AtlBaseModule.RemoveResourceInstance(default_resources_);
+  }
+
+  virtual void TearDown() {
+    _AtlBaseModule.AddResourceInstance(default_resources_);
+
+    ResourceManagerTest::TearDown();
+  }
+
+ private:
+  HINSTANCE default_resources_;
+};
+
 TEST_F(ResourceManagerTest, GetResourceDllName) {
   const CString kLang(_T("en"));
   CString ret = GetResourceDllName(kLang);
 
   CString expected_filename;
-  expected_filename.Format(kGoopdateResourceDllName, kLang);
+  expected_filename.Format(kOmahaResourceDllNameFormat, kLang);
   EXPECT_STREQ(expected_filename, ret);
 }
 
@@ -87,245 +138,36 @@
 
 TEST_F(ResourceManagerTest, LoadResourceFail) {
   SetMachine(false);
+
+  CString original_resoruce_dir = GetResourceDir();
   SetResourceDir(_T("non_existing\\abcddir"));
 
-  CString lang(_T("en"));
-  EXPECT_HRESULT_FAILED(manager_->LoadResourceDll(lang));
-  EXPECT_FALSE(manager_->resource_dll());
-  EXPECT_STREQ(manager_->language(), _T(""));
+  // Loading resource from a non-existing directory should fail. The language
+  // being loaded here should not be loaded previously. Otherwise the resource
+  // manager will return the cached value instead of doing actual load.
+  VerifyLoadingResourceDll(_T("ca"), false);
+
+  SetResourceDir(original_resoruce_dir);
 }
 
 TEST_F(ResourceManagerTest, LoadResourceDllCmdLine) {
   SetMachine(false);
 
-  CString lang(_T("ca"));
-  EXPECT_HRESULT_SUCCEEDED(manager_->LoadResourceDll(lang));
-  EXPECT_TRUE(manager_->resource_dll());
-  EXPECT_STREQ(manager_->language(), lang);
-
-  CString expected_filename;
-  expected_filename.Format(kGoopdateResourceDllName, lang);
-  CString expected_path = ConcatenatePath(path_, expected_filename);
-  EXPECT_STREQ(expected_path, manager_->resource_dll_filepath());
+  CString lang = _T("ca");
+  VerifyLoadingResourceDll(lang, true);
 }
 
 TEST_F(ResourceManagerTest, LoadResourceDllCmdLineMachine) {
   SetMachine(true);
 
-  CString lang(_T("ca"));
-  EXPECT_HRESULT_SUCCEEDED(manager_->LoadResourceDll(lang));
-  EXPECT_TRUE(manager_->resource_dll());
-  EXPECT_STREQ(manager_->language(), lang);
-
-  CString expected_filename;
-  expected_filename.Format(kGoopdateResourceDllName, lang);
-  CString expected_path = ConcatenatePath(path_, expected_filename);
-  EXPECT_STREQ(expected_path, manager_->resource_dll_filepath());
-}
-
-TEST_F(ResourceManagerTest, GetLanguageForLangID_NoLangID) {
-  EXPECT_STREQ(_T("en"), ResourceManager::GetLanguageForLangID(0));
-}
-
-TEST_F(ResourceManagerTest, GetLanguageForLangID_SupportedIds) {
-  EXPECT_STREQ(_T("ar"), GetLang(MAKELANGID(LANG_ARABIC, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("bg"), GetLang(MAKELANGID(LANG_BULGARIAN, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("ca"), GetLang(MAKELANGID(LANG_CATALAN, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("cs"), GetLang(MAKELANGID(LANG_CZECH, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("da"), GetLang(MAKELANGID(LANG_DANISH, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("de"), GetLang(MAKELANGID(LANG_GERMAN, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("el"), GetLang(MAKELANGID(LANG_GREEK, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("en"), GetLang(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("en-GB"), GetLang(MAKELANGID(LANG_ENGLISH,
-                                               SUBLANG_ENGLISH_UK)));
-  EXPECT_STREQ(_T("es"), GetLang(MAKELANGID(LANG_SPANISH,
-                                            SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("es"), GetLang(MAKELANGID(LANG_SPANISH,
-                                            SUBLANG_SPANISH)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
-                                                SUBLANG_SPANISH_MEXICAN)));
-  EXPECT_STREQ(_T("es"), GetLang(MAKELANGID(LANG_SPANISH,
-                                            SUBLANG_SPANISH_MODERN)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
-                                                SUBLANG_SPANISH_GUATEMALA)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
-                                                SUBLANG_SPANISH_COSTA_RICA)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
-                                                SUBLANG_SPANISH_PANAMA)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(
-      LANG_SPANISH,
-      SUBLANG_SPANISH_DOMINICAN_REPUBLIC)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
-                                                SUBLANG_SPANISH_VENEZUELA)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
-                                                SUBLANG_SPANISH_COLOMBIA)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
-                                                SUBLANG_SPANISH_PERU)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
-                                                SUBLANG_SPANISH_ARGENTINA)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
-                                                SUBLANG_SPANISH_ECUADOR)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
-                                                SUBLANG_SPANISH_CHILE)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
-                                                SUBLANG_SPANISH_URUGUAY)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
-                                                SUBLANG_SPANISH_PARAGUAY)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
-                                                SUBLANG_SPANISH_BOLIVIA)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
-                                                SUBLANG_SPANISH_EL_SALVADOR)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
-                                                SUBLANG_SPANISH_HONDURAS)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
-                                                SUBLANG_SPANISH_NICARAGUA)));
-  EXPECT_STREQ(_T("es-419"), GetLang(MAKELANGID(LANG_SPANISH,
-                                                SUBLANG_SPANISH_PUERTO_RICO)));
-  EXPECT_STREQ(_T("et"), GetLang(MAKELANGID(LANG_ESTONIAN,
-                                            SUBLANG_ESTONIAN_ESTONIA)));
-  EXPECT_STREQ(_T("fi"), GetLang(MAKELANGID(LANG_FINNISH, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("fil"), GetLang(MAKELANGID(LANG_FILIPINO, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("fr"), GetLang(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("hi"), GetLang(MAKELANGID(LANG_HINDI, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("hr"), GetLang(MAKELANGID(LANG_CROATIAN, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("hr"), GetLang(MAKELANGID(LANG_SERBIAN,
-                                            SUBLANG_SERBIAN_CROATIA)));
-  EXPECT_STREQ(_T("hu"), GetLang(MAKELANGID(LANG_HUNGARIAN, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("id"), GetLang(MAKELANGID(LANG_INDONESIAN, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("it"), GetLang(MAKELANGID(LANG_ITALIAN, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("iw"), GetLang(MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("ja"), GetLang(MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("ko"), GetLang(MAKELANGID(LANG_KOREAN, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("lt"), GetLang(MAKELANGID(LANG_LITHUANIAN, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("lv"), GetLang(MAKELANGID(LANG_LATVIAN, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("nl"), GetLang(MAKELANGID(LANG_DUTCH, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("no"), GetLang(MAKELANGID(LANG_NORWEGIAN, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("pl"), GetLang(MAKELANGID(LANG_POLISH, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("pt-BR"), GetLang(MAKELANGID(LANG_PORTUGUESE,
-                                               SUBLANG_PORTUGUESE_BRAZILIAN)));
-  EXPECT_STREQ(_T("pt-PT"), GetLang(MAKELANGID(LANG_PORTUGUESE,
-                                               SUBLANG_PORTUGUESE)));
-  EXPECT_STREQ(_T("ro"), GetLang(MAKELANGID(LANG_ROMANIAN, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("ru"), GetLang(MAKELANGID(LANG_RUSSIAN, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("sk"), GetLang(MAKELANGID(LANG_SLOVAK, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("sl"), GetLang(MAKELANGID(LANG_SLOVENIAN, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("sr"), GetLang(
-      MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_CYRILLIC)));
-  EXPECT_STREQ(_T("sr"), GetLang(
-      MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_LATIN)));
-  EXPECT_STREQ(_T("sr"), GetLang(MAKELANGID(LANG_SERBIAN,
-                                            SUBLANG_SERBIAN_CYRILLIC)));
-  EXPECT_STREQ(_T("sr"), GetLang(MAKELANGID(LANG_SERBIAN,
-                                            SUBLANG_SERBIAN_LATIN)));
-  EXPECT_STREQ(_T("sv"), GetLang(MAKELANGID(LANG_SWEDISH, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("th"), GetLang(MAKELANGID(LANG_THAI, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("tr"), GetLang(MAKELANGID(LANG_TURKISH, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("uk"), GetLang(MAKELANGID(LANG_UKRAINIAN, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("vi"), GetLang(MAKELANGID(LANG_VIETNAMESE, SUBLANG_DEFAULT)));
-  EXPECT_STREQ(_T("zh-HK"), GetLang(MAKELANGID(LANG_CHINESE,
-                                               SUBLANG_CHINESE_HONGKONG)));
-  EXPECT_STREQ(_T("zh-CN"), GetLang(MAKELANGID(LANG_CHINESE,
-                                               SUBLANG_CHINESE_MACAU)));
-  EXPECT_STREQ(_T("zh-CN"), GetLang(MAKELANGID(LANG_CHINESE,
-                                               SUBLANG_CHINESE_SIMPLIFIED)));
-  EXPECT_STREQ(_T("zh-CN"), GetLang(MAKELANGID(LANG_CHINESE,
-                                               SUBLANG_CHINESE_SINGAPORE)));
-  EXPECT_STREQ(_T("zh-TW"), GetLang(MAKELANGID(LANG_CHINESE,
-                                               SUBLANG_CHINESE_TRADITIONAL)));
-}
-
-// Unsupported languages and sublanguages fall back to "en".
-TEST_F(ResourceManagerTest, GetLanguageForLangID_UnsupportedSubLang) {
-  // LANG_NEUTRAL is unsupported.
-  EXPECT_STREQ(_T("en"), GetLang(MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)));
-  // LANG_AFRIKAANS is unsupported.
-  EXPECT_STREQ(_T("en"), GetLang(MAKELANGID(LANG_AFRIKAANS, SUBLANG_NEUTRAL)));
-  // SUBLANG_NEUTRAL is unsupported.
-  EXPECT_STREQ(_T("en"), GetLang(MAKELANGID(LANG_SPANISH, SUBLANG_NEUTRAL)));
-  // SUBLANG_SYS_DEFAULT is unsupported. It happens to be 2, which is not
-  // supported for Hungarian but is for English, Spanish, and others/
-  EXPECT_STREQ(_T("en"),
-               GetLang(MAKELANGID(LANG_HUNGARIAN, SUBLANG_SYS_DEFAULT)));
-  EXPECT_STREQ(_T("es-419"),
-               GetLang(MAKELANGID(LANG_SPANISH, SUBLANG_SYS_DEFAULT)));
-  // 0x3f is an invalid sublang. There is a "es" file.
-  EXPECT_STREQ(_T("en"), GetLang(MAKELANGID(LANG_SPANISH, 0x3f)));
-  // 0x3f is an invalid sublang. There is not a "zh" file.
-  EXPECT_STREQ(_T("en"), GetLang(MAKELANGID(LANG_CHINESE, 0x3f)));
-}
-
-TEST_F(ResourceManagerTest, TestCountLanguagesInTranslationTable) {
-  std::map<CString, bool> languages;
-  GetDistinctLanguageMapFromTranslationTable(&languages);
-  // Number of language DLLs + zh-HK special case.
-  EXPECT_EQ(54 + 1, languages.size());
-}
-
-TEST_F(ResourceManagerTest, TestAppropriateLanguagesInTranslationTable) {
-  std::map<CString, bool> languages;
-  GetDistinctLanguageMapFromTranslationTable(&languages);
-
-  EXPECT_TRUE(languages.find(_T("ar")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("bg")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("bn")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("ca")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("cs")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("da")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("de")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("el")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("en-GB")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("en")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("es-419")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("es")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("et")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("fa")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("fi")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("fil")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("fr")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("gu")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("hi")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("hr")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("hu")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("id")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("is")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("it")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("iw")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("ja")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("kn")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("ko")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("lt")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("lv")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("ml")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("mr")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("ms")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("nl")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("no")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("or")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("pl")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("pt-BR")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("pt-PT")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("ro")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("ru")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("sk")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("sl")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("sr")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("sv")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("ta")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("te")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("th")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("tr")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("uk")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("ur")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("vi")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("zh-CN")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("zh-HK")) != languages.end());
-  EXPECT_TRUE(languages.find(_T("zh-TW")) != languages.end());
+  CString lang = _T("ca");
+  VerifyLoadingResourceDll(lang, true);
 }
 
 TEST_F(ResourceManagerTest, TestCountLanguageDlls) {
   std::vector<CString> filenames;
   ResourceManager::GetSupportedLanguageDllNames(&filenames);
-  EXPECT_EQ(54, filenames.size());
+  EXPECT_EQ(kNumberOfLanguageDlls, filenames.size());
 }
 
 TEST_F(ResourceManagerTest, TestAppropriateLanguageDlls) {
@@ -334,6 +176,7 @@
 
   std::vector<CString>::iterator iter = filenames.begin();
 
+  EXPECT_STREQ(_T("goopdateres_am.dll"), *iter++);
   EXPECT_STREQ(_T("goopdateres_ar.dll"), *iter++);
   EXPECT_STREQ(_T("goopdateres_bg.dll"), *iter++);
   EXPECT_STREQ(_T("goopdateres_bn.dll"), *iter++);
@@ -369,7 +212,6 @@
   EXPECT_STREQ(_T("goopdateres_ms.dll"), *iter++);
   EXPECT_STREQ(_T("goopdateres_nl.dll"), *iter++);
   EXPECT_STREQ(_T("goopdateres_no.dll"), *iter++);
-  EXPECT_STREQ(_T("goopdateres_or.dll"), *iter++);
   EXPECT_STREQ(_T("goopdateres_pl.dll"), *iter++);
   EXPECT_STREQ(_T("goopdateres_pt-BR.dll"), *iter++);
   EXPECT_STREQ(_T("goopdateres_pt-PT.dll"), *iter++);
@@ -379,6 +221,7 @@
   EXPECT_STREQ(_T("goopdateres_sl.dll"), *iter++);
   EXPECT_STREQ(_T("goopdateres_sr.dll"), *iter++);
   EXPECT_STREQ(_T("goopdateres_sv.dll"), *iter++);
+  EXPECT_STREQ(_T("goopdateres_sw.dll"), *iter++);
   EXPECT_STREQ(_T("goopdateres_ta.dll"), *iter++);
   EXPECT_STREQ(_T("goopdateres_te.dll"), *iter++);
   EXPECT_STREQ(_T("goopdateres_th.dll"), *iter++);
@@ -391,24 +234,24 @@
   EXPECT_STREQ(_T("goopdateres_zh-TW.dll"), *iter++);
 }
 
-TEST_F(ResourceManagerTest, RussianResourcesValid) {
-  SetResourceDir(app_util::GetModuleDirectory(NULL));
+TEST_F(ResourceManagerResourcesProtectedTest, RussianResourcesValid) {
+  ResourceManager::Delete();
 
   CString lang(_T("ru"));
-  EXPECT_HRESULT_SUCCEEDED(manager_->LoadResourceDll(lang));
-  EXPECT_TRUE(manager_->resource_dll());
-  EXPECT_STREQ(lang, manager_->language());
+  EXPECT_HRESULT_SUCCEEDED(ResourceManager::Create(false, path_, lang));
 
   CString install_success(FormatResourceMessage(
-      IDS_APPLICATION_INSTALLED_SUCCESSFULLY, _T("Gears")));
-  EXPECT_STREQ("Благодарим вас за установку Gears.",
+      IDS_BUNDLE_INSTALLED_SUCCESSFULLY, _T("Google Gears")));
+
+  EXPECT_STREQ("Благодарим за установку Google Gears.",
                WideToUtf8(install_success));
 
   CString install_fail(FormatResourceMessage(IDS_INSTALLER_FAILED_WITH_MESSAGE,
-                            _T("12345"), _T("Action failed.")));
+                                             _T("12345"),
+                                             _T("Action failed.")));
+
   EXPECT_STREQ("Ошибка установщика 12345: Action failed.",
                WideToUtf8(install_fail));
 }
 
-}  // namespace omaha.
-
+}  // namespace omaha
diff --git a/goopdate/resources/build.scons b/goopdate/resources/build.scons
index 47c2928..378de18 100644
--- a/goopdate/resources/build.scons
+++ b/goopdate/resources/build.scons
@@ -76,12 +76,12 @@
         'resdll_main.cc',
         lang_res,
         # Needed to prevent rebuilding of the lib.
-        '$MAIN_DIR/installers/resource_only_dll.def'
+        'resource_only_dll.def'
         ]
 
     # Build the (unsigned) DLL
     unsigned_dll = lang_env.ComponentLibrary(
-        lib_name='%s/%sgoopdateres_unsigned_%s.dll' % (lang, prefix, lang),
+        lib_name='%s/%sgoopdateres_unsigned_%s' % (lang, prefix, lang),
         source=lang_inputs
     )
 
@@ -91,4 +91,3 @@
     )
 
     env.Replicate('$STAGING_DIR', signed_dll)
-
diff --git a/goopdate/resources/goopdate_dll/generated_resources_am.rc b/goopdate/resources/goopdate_dll/generated_resources_am.rc
index 0bae39a..536d555 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_am.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_am.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_ar.rc b/goopdate/resources/goopdate_dll/generated_resources_ar.rc
index 9818600..be5b72f 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_ar.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_ar.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_bg.rc b/goopdate/resources/goopdate_dll/generated_resources_bg.rc
index 9e124f0..70247f9 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_bg.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_bg.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_bn.rc b/goopdate/resources/goopdate_dll/generated_resources_bn.rc
index b5bfa9e..d51b155 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_bn.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_bn.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_ca.rc b/goopdate/resources/goopdate_dll/generated_resources_ca.rc
index 65fd420..15dcf7a 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_ca.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_ca.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_cs.rc b/goopdate/resources/goopdate_dll/generated_resources_cs.rc
index d45f26e..2c3833e 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_cs.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_cs.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_da.rc b/goopdate/resources/goopdate_dll/generated_resources_da.rc
index 235235d..a6ee391 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_da.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_da.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_de.rc b/goopdate/resources/goopdate_dll/generated_resources_de.rc
index 6b0fed5..b624965 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_de.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_de.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_el.rc b/goopdate/resources/goopdate_dll/generated_resources_el.rc
index f09b282..50913e8 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_el.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_el.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_en-GB.rc b/goopdate/resources/goopdate_dll/generated_resources_en-GB.rc
index 1ad5846..5e31fd2 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_en-GB.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_en-GB.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_en.rc b/goopdate/resources/goopdate_dll/generated_resources_en.rc
index 16048ab..4133c1c 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_en.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_en.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_es-419.rc b/goopdate/resources/goopdate_dll/generated_resources_es-419.rc
index ba9373e..b595e14 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_es-419.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_es-419.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_es.rc b/goopdate/resources/goopdate_dll/generated_resources_es.rc
index e822101..8880958 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_es.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_es.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_et.rc b/goopdate/resources/goopdate_dll/generated_resources_et.rc
index 9437258..1614f41 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_et.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_et.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_fa.rc b/goopdate/resources/goopdate_dll/generated_resources_fa.rc
index 786661e..0061da9 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_fa.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_fa.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_fi.rc b/goopdate/resources/goopdate_dll/generated_resources_fi.rc
index a7b4d17..dfd56ba 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_fi.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_fi.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_fil.rc b/goopdate/resources/goopdate_dll/generated_resources_fil.rc
index 631d5d2..a82b588 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_fil.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_fil.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_fr.rc b/goopdate/resources/goopdate_dll/generated_resources_fr.rc
index f376696..18f5493 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_fr.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_fr.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_gu.rc b/goopdate/resources/goopdate_dll/generated_resources_gu.rc
index b8f65b0..df36d49 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_gu.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_gu.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_hi.rc b/goopdate/resources/goopdate_dll/generated_resources_hi.rc
index ff28d56..8be4fee 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_hi.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_hi.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_hr.rc b/goopdate/resources/goopdate_dll/generated_resources_hr.rc
index f6baa7f..d742c46 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_hr.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_hr.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_hu.rc b/goopdate/resources/goopdate_dll/generated_resources_hu.rc
index f67c28d..827891d 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_hu.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_hu.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_id.rc b/goopdate/resources/goopdate_dll/generated_resources_id.rc
index 6137e41..684a379 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_id.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_id.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_is.rc b/goopdate/resources/goopdate_dll/generated_resources_is.rc
index df6d210..757fc50 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_is.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_is.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_it.rc b/goopdate/resources/goopdate_dll/generated_resources_it.rc
index 17e24ca..f68283e 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_it.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_it.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_iw.rc b/goopdate/resources/goopdate_dll/generated_resources_iw.rc
index 4f3aba9..38806f3 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_iw.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_iw.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_ja.rc b/goopdate/resources/goopdate_dll/generated_resources_ja.rc
index 3f8087b..0646256 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_ja.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_ja.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_kn.rc b/goopdate/resources/goopdate_dll/generated_resources_kn.rc
index eae1521..a3b160d 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_kn.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_kn.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_ko.rc b/goopdate/resources/goopdate_dll/generated_resources_ko.rc
index 201ff88..ae8a9dc 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_ko.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_ko.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_lt.rc b/goopdate/resources/goopdate_dll/generated_resources_lt.rc
index ac89b1c..804782a 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_lt.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_lt.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_lv.rc b/goopdate/resources/goopdate_dll/generated_resources_lv.rc
index ea89ace..f50b1f9 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_lv.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_lv.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_ml.rc b/goopdate/resources/goopdate_dll/generated_resources_ml.rc
index 5fd5c68..1e54285 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_ml.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_ml.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_mr.rc b/goopdate/resources/goopdate_dll/generated_resources_mr.rc
index 71b78e8..b17588c 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_mr.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_mr.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_ms.rc b/goopdate/resources/goopdate_dll/generated_resources_ms.rc
index 1c576cc..cd2ecb4 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_ms.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_ms.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_nl.rc b/goopdate/resources/goopdate_dll/generated_resources_nl.rc
index 51cec99..e4d2626 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_nl.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_nl.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_no.rc b/goopdate/resources/goopdate_dll/generated_resources_no.rc
index c30e48a..b3b64d5 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_no.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_no.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_or.rc b/goopdate/resources/goopdate_dll/generated_resources_or.rc
index 0ecf616..91473b3 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_or.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_or.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_pl.rc b/goopdate/resources/goopdate_dll/generated_resources_pl.rc
index 69bbe24..24dca43 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_pl.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_pl.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_pt-BR.rc b/goopdate/resources/goopdate_dll/generated_resources_pt-BR.rc
index 3c7bf6e..1ed0a43 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_pt-BR.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_pt-BR.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_pt-PT.rc b/goopdate/resources/goopdate_dll/generated_resources_pt-PT.rc
index 3d057d5..7039162 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_pt-PT.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_pt-PT.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_ro.rc b/goopdate/resources/goopdate_dll/generated_resources_ro.rc
index 623f8d5..11194ca 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_ro.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_ro.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_ru.rc b/goopdate/resources/goopdate_dll/generated_resources_ru.rc
index 48a6492..1315ac5 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_ru.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_ru.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_sk.rc b/goopdate/resources/goopdate_dll/generated_resources_sk.rc
index b617ae5..9942cc9 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_sk.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_sk.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_sl.rc b/goopdate/resources/goopdate_dll/generated_resources_sl.rc
index 98a096b..22c6fbe 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_sl.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_sl.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_sr.rc b/goopdate/resources/goopdate_dll/generated_resources_sr.rc
index b56fb39..8f60172 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_sr.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_sr.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_sv.rc b/goopdate/resources/goopdate_dll/generated_resources_sv.rc
index c8bed0c..a3b6e6a 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_sv.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_sv.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_sw.rc b/goopdate/resources/goopdate_dll/generated_resources_sw.rc
index 996a5d8..27d8dcf 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_sw.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_sw.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_ta.rc b/goopdate/resources/goopdate_dll/generated_resources_ta.rc
index ca5a34d..8fc1bdb 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_ta.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_ta.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_te.rc b/goopdate/resources/goopdate_dll/generated_resources_te.rc
index a8c0a4c..1a3483c 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_te.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_te.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_th.rc b/goopdate/resources/goopdate_dll/generated_resources_th.rc
index 408f6b9..236d565 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_th.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_th.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_tr.rc b/goopdate/resources/goopdate_dll/generated_resources_tr.rc
index 7510bda..e672ee9 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_tr.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_tr.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_uk.rc b/goopdate/resources/goopdate_dll/generated_resources_uk.rc
index 27b897e..f017ff6 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_uk.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_uk.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_ur.rc b/goopdate/resources/goopdate_dll/generated_resources_ur.rc
index ab8f397..f99f5c7 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_ur.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_ur.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_userdefault.rc b/goopdate/resources/goopdate_dll/generated_resources_userdefault.rc
index 2d6d963..2d90d74 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_userdefault.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_userdefault.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_vi.rc b/goopdate/resources/goopdate_dll/generated_resources_vi.rc
index 3e2c46a..e334ce6 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_vi.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_vi.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_zh-CN.rc b/goopdate/resources/goopdate_dll/generated_resources_zh-CN.rc
index 80d297d..8142a97 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_zh-CN.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_zh-CN.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_zh-HK.rc b/goopdate/resources/goopdate_dll/generated_resources_zh-HK.rc
index 2712fc3..d1b87a2 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_zh-HK.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_zh-HK.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/generated_resources_zh-TW.rc b/goopdate/resources/goopdate_dll/generated_resources_zh-TW.rc
index d33d11e..d3e30d6 100644
--- a/goopdate/resources/goopdate_dll/generated_resources_zh-TW.rc
+++ b/goopdate/resources/goopdate_dll/generated_resources_zh-TW.rc
Binary files differ
diff --git a/goopdate/resources/goopdate_dll/goopdate_dll.grh b/goopdate/resources/goopdate_dll/goopdate_dll.grh
index 2b7b1e0..e1b0c40 100644
--- a/goopdate/resources/goopdate_dll/goopdate_dll.grh
+++ b/goopdate/resources/goopdate_dll/goopdate_dll.grh
@@ -1,4 +1,4 @@
-// Copyright 2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -13,12 +13,12 @@
 // limitations under the License.
 // ========================================================================
 // This file is automatically generated by GRIT.  Do not edit.
-// Built on Tue Oct 13 11:29:10 2009
+// Built on Thu Nov 18 11:01:39 2010
 
-#ifndef RESOURCE_849889627643__
-#define RESOURCE_849889627643__
+#ifndef RESOURCE_751581924418__
+#define RESOURCE_751581924418__
 
 
 #define IDS_ELEVATION_MONIKER_DISPLAYNAME 3000
 
-#endif // RESOURCE_849889627643__
+#endif // RESOURCE_751581924418__
diff --git a/goopdate/resources/goopdateres/generated_resources_am.rc b/goopdate/resources/goopdateres/generated_resources_am.rc
new file mode 100644
index 0000000..aa23522
--- /dev/null
+++ b/goopdate/resources/goopdateres/generated_resources_am.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_ar.rc b/goopdate/resources/goopdateres/generated_resources_ar.rc
index d281d5c..6a3bfe4 100644
--- a/goopdate/resources/goopdateres/generated_resources_ar.rc
+++ b/goopdate/resources/goopdateres/generated_resources_ar.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_bg.rc b/goopdate/resources/goopdateres/generated_resources_bg.rc
index 490f86b..0acbabb 100644
--- a/goopdate/resources/goopdateres/generated_resources_bg.rc
+++ b/goopdate/resources/goopdateres/generated_resources_bg.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_bn.rc b/goopdate/resources/goopdateres/generated_resources_bn.rc
index a61885a..243e42d 100644
--- a/goopdate/resources/goopdateres/generated_resources_bn.rc
+++ b/goopdate/resources/goopdateres/generated_resources_bn.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_ca.rc b/goopdate/resources/goopdateres/generated_resources_ca.rc
index 4d2c505..8a66f24 100644
--- a/goopdate/resources/goopdateres/generated_resources_ca.rc
+++ b/goopdate/resources/goopdateres/generated_resources_ca.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_cs.rc b/goopdate/resources/goopdateres/generated_resources_cs.rc
index cf1c156..f292a70 100644
--- a/goopdate/resources/goopdateres/generated_resources_cs.rc
+++ b/goopdate/resources/goopdateres/generated_resources_cs.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_da.rc b/goopdate/resources/goopdateres/generated_resources_da.rc
index 2b44477..3fcf9c2 100644
--- a/goopdate/resources/goopdateres/generated_resources_da.rc
+++ b/goopdate/resources/goopdateres/generated_resources_da.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_de.rc b/goopdate/resources/goopdateres/generated_resources_de.rc
index 201f16c..3c3084d 100644
--- a/goopdate/resources/goopdateres/generated_resources_de.rc
+++ b/goopdate/resources/goopdateres/generated_resources_de.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_el.rc b/goopdate/resources/goopdateres/generated_resources_el.rc
index 44e2f06..7e1b726 100644
--- a/goopdate/resources/goopdateres/generated_resources_el.rc
+++ b/goopdate/resources/goopdateres/generated_resources_el.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_en-GB.rc b/goopdate/resources/goopdateres/generated_resources_en-GB.rc
index 64a800e..b79b7e7 100644
--- a/goopdate/resources/goopdateres/generated_resources_en-GB.rc
+++ b/goopdate/resources/goopdateres/generated_resources_en-GB.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_en.rc b/goopdate/resources/goopdateres/generated_resources_en.rc
index aa8e30c..f08b98a 100644
--- a/goopdate/resources/goopdateres/generated_resources_en.rc
+++ b/goopdate/resources/goopdateres/generated_resources_en.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_es-419.rc b/goopdate/resources/goopdateres/generated_resources_es-419.rc
index 5a2d3db..63081db 100644
--- a/goopdate/resources/goopdateres/generated_resources_es-419.rc
+++ b/goopdate/resources/goopdateres/generated_resources_es-419.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_es.rc b/goopdate/resources/goopdateres/generated_resources_es.rc
index d0f97bd..e166df9 100644
--- a/goopdate/resources/goopdateres/generated_resources_es.rc
+++ b/goopdate/resources/goopdateres/generated_resources_es.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_et.rc b/goopdate/resources/goopdateres/generated_resources_et.rc
index b31f8a9..05e7a1b 100644
--- a/goopdate/resources/goopdateres/generated_resources_et.rc
+++ b/goopdate/resources/goopdateres/generated_resources_et.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_fa.rc b/goopdate/resources/goopdateres/generated_resources_fa.rc
index 044aba7..21d9d15 100644
--- a/goopdate/resources/goopdateres/generated_resources_fa.rc
+++ b/goopdate/resources/goopdateres/generated_resources_fa.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_fi.rc b/goopdate/resources/goopdateres/generated_resources_fi.rc
index a43fc3e..48e3665 100644
--- a/goopdate/resources/goopdateres/generated_resources_fi.rc
+++ b/goopdate/resources/goopdateres/generated_resources_fi.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_fil.rc b/goopdate/resources/goopdateres/generated_resources_fil.rc
index 30484c6..32115fb 100644
--- a/goopdate/resources/goopdateres/generated_resources_fil.rc
+++ b/goopdate/resources/goopdateres/generated_resources_fil.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_fr.rc b/goopdate/resources/goopdateres/generated_resources_fr.rc
index 14e29b7..3cbfba1 100644
--- a/goopdate/resources/goopdateres/generated_resources_fr.rc
+++ b/goopdate/resources/goopdateres/generated_resources_fr.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_gu.rc b/goopdate/resources/goopdateres/generated_resources_gu.rc
index 9c0d265..5616e4e 100644
--- a/goopdate/resources/goopdateres/generated_resources_gu.rc
+++ b/goopdate/resources/goopdateres/generated_resources_gu.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_hi.rc b/goopdate/resources/goopdateres/generated_resources_hi.rc
index f70a648..d0d8b69 100644
--- a/goopdate/resources/goopdateres/generated_resources_hi.rc
+++ b/goopdate/resources/goopdateres/generated_resources_hi.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_hr.rc b/goopdate/resources/goopdateres/generated_resources_hr.rc
index ede1e7a..0ec88c1 100644
--- a/goopdate/resources/goopdateres/generated_resources_hr.rc
+++ b/goopdate/resources/goopdateres/generated_resources_hr.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_hu.rc b/goopdate/resources/goopdateres/generated_resources_hu.rc
index 18a6dba..31eaa2f 100644
--- a/goopdate/resources/goopdateres/generated_resources_hu.rc
+++ b/goopdate/resources/goopdateres/generated_resources_hu.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_id.rc b/goopdate/resources/goopdateres/generated_resources_id.rc
index f088e1f..de06268 100644
--- a/goopdate/resources/goopdateres/generated_resources_id.rc
+++ b/goopdate/resources/goopdateres/generated_resources_id.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_is.rc b/goopdate/resources/goopdateres/generated_resources_is.rc
index cf7ab87..9c37c37 100644
--- a/goopdate/resources/goopdateres/generated_resources_is.rc
+++ b/goopdate/resources/goopdateres/generated_resources_is.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_it.rc b/goopdate/resources/goopdateres/generated_resources_it.rc
index 61e2cb0..f90a62c 100644
--- a/goopdate/resources/goopdateres/generated_resources_it.rc
+++ b/goopdate/resources/goopdateres/generated_resources_it.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_iw.rc b/goopdate/resources/goopdateres/generated_resources_iw.rc
index aa1b093..84fcf0d 100644
--- a/goopdate/resources/goopdateres/generated_resources_iw.rc
+++ b/goopdate/resources/goopdateres/generated_resources_iw.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_ja.rc b/goopdate/resources/goopdateres/generated_resources_ja.rc
index 7fd8672..4a66de7 100644
--- a/goopdate/resources/goopdateres/generated_resources_ja.rc
+++ b/goopdate/resources/goopdateres/generated_resources_ja.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_kn.rc b/goopdate/resources/goopdateres/generated_resources_kn.rc
index fa0d96d..ebfbc38 100644
--- a/goopdate/resources/goopdateres/generated_resources_kn.rc
+++ b/goopdate/resources/goopdateres/generated_resources_kn.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_ko.rc b/goopdate/resources/goopdateres/generated_resources_ko.rc
index d582a74..d3ca110 100644
--- a/goopdate/resources/goopdateres/generated_resources_ko.rc
+++ b/goopdate/resources/goopdateres/generated_resources_ko.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_lt.rc b/goopdate/resources/goopdateres/generated_resources_lt.rc
index 96906dc..ab5faff 100644
--- a/goopdate/resources/goopdateres/generated_resources_lt.rc
+++ b/goopdate/resources/goopdateres/generated_resources_lt.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_lv.rc b/goopdate/resources/goopdateres/generated_resources_lv.rc
index 67298b9..2fbc828 100644
--- a/goopdate/resources/goopdateres/generated_resources_lv.rc
+++ b/goopdate/resources/goopdateres/generated_resources_lv.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_ml.rc b/goopdate/resources/goopdateres/generated_resources_ml.rc
index ed45820..6d3436c 100644
--- a/goopdate/resources/goopdateres/generated_resources_ml.rc
+++ b/goopdate/resources/goopdateres/generated_resources_ml.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_mr.rc b/goopdate/resources/goopdateres/generated_resources_mr.rc
index c459a3f..8e719cc 100644
--- a/goopdate/resources/goopdateres/generated_resources_mr.rc
+++ b/goopdate/resources/goopdateres/generated_resources_mr.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_ms.rc b/goopdate/resources/goopdateres/generated_resources_ms.rc
index b384392..1518d2e 100644
--- a/goopdate/resources/goopdateres/generated_resources_ms.rc
+++ b/goopdate/resources/goopdateres/generated_resources_ms.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_nl.rc b/goopdate/resources/goopdateres/generated_resources_nl.rc
index d024d8a..ca52bdd 100644
--- a/goopdate/resources/goopdateres/generated_resources_nl.rc
+++ b/goopdate/resources/goopdateres/generated_resources_nl.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_no.rc b/goopdate/resources/goopdateres/generated_resources_no.rc
index bec2278..aabfa4b 100644
--- a/goopdate/resources/goopdateres/generated_resources_no.rc
+++ b/goopdate/resources/goopdateres/generated_resources_no.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_or.rc b/goopdate/resources/goopdateres/generated_resources_or.rc
deleted file mode 100644
index f54145a..0000000
--- a/goopdate/resources/goopdateres/generated_resources_or.rc
+++ /dev/null
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_pl.rc b/goopdate/resources/goopdateres/generated_resources_pl.rc
index b8932be..ab52f68 100644
--- a/goopdate/resources/goopdateres/generated_resources_pl.rc
+++ b/goopdate/resources/goopdateres/generated_resources_pl.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_pt-BR.rc b/goopdate/resources/goopdateres/generated_resources_pt-BR.rc
index 5e463ae..3334504 100644
--- a/goopdate/resources/goopdateres/generated_resources_pt-BR.rc
+++ b/goopdate/resources/goopdateres/generated_resources_pt-BR.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_pt-PT.rc b/goopdate/resources/goopdateres/generated_resources_pt-PT.rc
index fd1e20e..0f60bb7 100644
--- a/goopdate/resources/goopdateres/generated_resources_pt-PT.rc
+++ b/goopdate/resources/goopdateres/generated_resources_pt-PT.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_ro.rc b/goopdate/resources/goopdateres/generated_resources_ro.rc
index 5dc1a88..df48882 100644
--- a/goopdate/resources/goopdateres/generated_resources_ro.rc
+++ b/goopdate/resources/goopdateres/generated_resources_ro.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_ru.rc b/goopdate/resources/goopdateres/generated_resources_ru.rc
index fcaf0bf..7c2c5b8 100644
--- a/goopdate/resources/goopdateres/generated_resources_ru.rc
+++ b/goopdate/resources/goopdateres/generated_resources_ru.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_sk.rc b/goopdate/resources/goopdateres/generated_resources_sk.rc
index 2709846..01f80d4 100644
--- a/goopdate/resources/goopdateres/generated_resources_sk.rc
+++ b/goopdate/resources/goopdateres/generated_resources_sk.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_sl.rc b/goopdate/resources/goopdateres/generated_resources_sl.rc
index 6a89fca..a43e1ff 100644
--- a/goopdate/resources/goopdateres/generated_resources_sl.rc
+++ b/goopdate/resources/goopdateres/generated_resources_sl.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_sr.rc b/goopdate/resources/goopdateres/generated_resources_sr.rc
index 4dcd279..50e9a6d 100644
--- a/goopdate/resources/goopdateres/generated_resources_sr.rc
+++ b/goopdate/resources/goopdateres/generated_resources_sr.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_sv.rc b/goopdate/resources/goopdateres/generated_resources_sv.rc
index 5d3db54..7a453f2 100644
--- a/goopdate/resources/goopdateres/generated_resources_sv.rc
+++ b/goopdate/resources/goopdateres/generated_resources_sv.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_sw.rc b/goopdate/resources/goopdateres/generated_resources_sw.rc
new file mode 100644
index 0000000..4e5f16a
--- /dev/null
+++ b/goopdate/resources/goopdateres/generated_resources_sw.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_ta.rc b/goopdate/resources/goopdateres/generated_resources_ta.rc
index cf28db1..fe5b333 100644
--- a/goopdate/resources/goopdateres/generated_resources_ta.rc
+++ b/goopdate/resources/goopdateres/generated_resources_ta.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_te.rc b/goopdate/resources/goopdateres/generated_resources_te.rc
index 8a4bd23..fa241f0 100644
--- a/goopdate/resources/goopdateres/generated_resources_te.rc
+++ b/goopdate/resources/goopdateres/generated_resources_te.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_th.rc b/goopdate/resources/goopdateres/generated_resources_th.rc
index 7a3fc13..a643c63 100644
--- a/goopdate/resources/goopdateres/generated_resources_th.rc
+++ b/goopdate/resources/goopdateres/generated_resources_th.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_tr.rc b/goopdate/resources/goopdateres/generated_resources_tr.rc
index 4381b49..b80db8d 100644
--- a/goopdate/resources/goopdateres/generated_resources_tr.rc
+++ b/goopdate/resources/goopdateres/generated_resources_tr.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_uk.rc b/goopdate/resources/goopdateres/generated_resources_uk.rc
index d2dea41..804e209 100644
--- a/goopdate/resources/goopdateres/generated_resources_uk.rc
+++ b/goopdate/resources/goopdateres/generated_resources_uk.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_ur.rc b/goopdate/resources/goopdateres/generated_resources_ur.rc
index f7a8492..4ee8417 100644
--- a/goopdate/resources/goopdateres/generated_resources_ur.rc
+++ b/goopdate/resources/goopdateres/generated_resources_ur.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_vi.rc b/goopdate/resources/goopdateres/generated_resources_vi.rc
index bacd286..f7f8b76 100644
--- a/goopdate/resources/goopdateres/generated_resources_vi.rc
+++ b/goopdate/resources/goopdateres/generated_resources_vi.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_zh-CN.rc b/goopdate/resources/goopdateres/generated_resources_zh-CN.rc
index bc8dce4..c48d116 100644
--- a/goopdate/resources/goopdateres/generated_resources_zh-CN.rc
+++ b/goopdate/resources/goopdateres/generated_resources_zh-CN.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/generated_resources_zh-TW.rc b/goopdate/resources/goopdateres/generated_resources_zh-TW.rc
index 178bc5b..c883b15 100644
--- a/goopdate/resources/goopdateres/generated_resources_zh-TW.rc
+++ b/goopdate/resources/goopdateres/generated_resources_zh-TW.rc
Binary files differ
diff --git a/goopdate/resources/goopdateres/goopdate.grh b/goopdate/resources/goopdateres/goopdate.grh
index 984daf2..063858e 100644
--- a/goopdate/resources/goopdateres/goopdate.grh
+++ b/goopdate/resources/goopdateres/goopdate.grh
@@ -1,4 +1,4 @@
-// Copyright 2009 Google Inc.
+// Copyright 2007-2011 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -13,73 +13,89 @@
 // limitations under the License.
 // ========================================================================
 // This file is automatically generated by GRIT.  Do not edit.
-// Built on Mon Apr 06 16:27:45 2009
+// Built on Fri Aug 12 10:57:04 2011
 
-#ifndef RESOURCE_350927934028__
-#define RESOURCE_350927934028__
+#ifndef RESOURCE_48542623907__
+#define RESOURCE_48542623907__
 
 
-#define IDS_SERVICE_NAME 3000
-#define IDS_GENERIC_INSTALLER_DISPLAY_NAME 3001
-#define IDS_SERVICE_DISPLAY_NAME 3002
-#define IDS_DOWNLOADING 3003
-#define IDS_INSTALLING 3004
-#define IDS_WAITING_TO_CONNECT 3005
-#define IDS_WAITING_TO_DOWNLOAD 3006
-#define IDS_WAITING_TO_INSTALL 3007
-#define IDS_TEXT_RESTART_BROWSER 3008
-#define IDS_TEXT_RESTART_ALL_BROWSERS 3009
-#define IDS_CLOSE 3010
-#define IDS_RESTART_BROWSER_NOW 3011
-#define IDS_RESTART_BROWSER_LATER 3012
-#define IDS_RESTART_ALL_BROWSERS_NOW 3013
-#define IDS_RESTART_ALL_BROWSERS_LATER 3014
-#define IDS_WINDOW_TITLE 3015
-#define IDS_INSTALLATION_STOPPED_WINDOW_TITLE 3016
-#define IDS_INSTALL_STOPPED 3017
-#define IDS_UNKNOWN_APPLICATION 3018
-#define IDS_DOWNLOAD_HASH_MISMATCH 3019
-#define IDS_DOWNLOAD_ERROR 3020
-#define IDS_APPLICATION_INSTALLED_SUCCESSFULLY 3021
-#define IDS_INSTALL_FAILED 3022
-#define IDS_RESTRICTED_RESPONSE_FROM_SERVER 3023
-#define IDS_NON_OK_RESPONSE_FROM_SERVER 3024
-#define IDS_NEED_ADMIN_TO_INSTALL 3025
-#define IDS_NO_UPDATE_RESPONSE 3026
-#define IDS_APPLICATION_ALREADY_INSTALLING 3027
-#define IDS_INVALID_INSTALLER_FILENAME 3028
-#define IDS_INSTALLER_FAILED_TO_START 3029
-#define IDS_INSTALLER_TIMED_OUT 3030
-#define IDS_INSTALLER_FAILED_WITH_MESSAGE 3031
-#define IDS_INSTALLER_FAILED_NO_MESSAGE 3032
-#define IDS_MSI_INSTALL_ALREADY_RUNNING 3033
-#define IDS_RESUME_INSTALLATION 3034
-#define IDS_CANCEL_INSTALLATION 3035
-#define IDS_INITIALIZING 3036
-#define IDS_SETUP_FAILED 3037
-#define IDS_ELEVATION_FAILED 3038
-#define IDS_APPLICATION_INSTALLING_GOOGLE_UPDATE 3039
-#define IDS_INSTANCES_RUNNING_AFTER_SHUTDOWN 3040
-#define IDS_HELP_ME_FIX_THIS_TEXT 3041
-#define IDS_BUNDLE_INSTALL_FAILED 3042
-#define IDS_OS_NOT_SUPPORTED 3043
-#define IDS_WINDOWS_IS_NOT_UP_TO_DATE 3044
-#define IDS_HANDOFF_FAILED 3045
-#define IDS_TEXT_REBOOT 3046
-#define IDS_RESTART_NOW 3047
-#define IDS_RESTART_LATER 3048
-#define IDS_DOWNLOAD_PAUSED 3049
-#define IDS_PAUSE 3050
-#define IDS_RESUME 3051
-#define IDS_SERVICE_DESCRIPTION 3052
-#define IDS_SCHEDULED_TASK_DESCRIPTION 3053
-#define IDS_NO_NETWORK_PRESENT_ERROR 3054
-#define IDS_ERROR_HTTPSTATUS_UNAUTHORIZED 3055
-#define IDS_ERROR_HTTPSTATUS_FORBIDDEN 3056
-#define IDS_ERROR_HTTPSTATUS_PROXY_AUTH_REQUIRED 3057
-#define IDS_APP_INSTALL_DISABLED_BY_GROUP_POLICY 3058
-#define IDS_INSTALLER_OLD 3059
-#define IDS_PROXY_PROMPT_MESSAGE 3060
+#define IDS_FRIENDLY_COMPANY_NAME 3000
+#define IDS_PRODUCT_DISPLAY_NAME 3001
+#define IDS_DEFAULT_APP_DISPLAY_NAME 3002
+#define IDS_APPLICATION_INSTALLED_SUCCESSFULLY 3003
+#define IDS_NO_UPDATE_RESPONSE 3004
+#define IDS_INSTALLER_OLD 3005
+#define IDS_SERVICE_NAME 3006
+#define IDS_SERVICE_DISPLAY_NAME 3007
+#define IDS_SERVICE_DESCRIPTION 3008
+#define IDS_SCHEDULED_TASK_DESCRIPTION 3009
+#define IDS_NO_NETWORK_PRESENT_ERROR 3010
+#define IDS_ERROR_HTTPSTATUS_UNAUTHORIZED 3011
+#define IDS_ERROR_HTTPSTATUS_FORBIDDEN 3012
+#define IDS_ERROR_HTTPSTATUS_PROXY_AUTH_REQUIRED 3013
+#define IDS_UNKNOWN_APPLICATION 3014
+#define IDS_RESTRICTED_RESPONSE_FROM_SERVER 3015
+#define IDS_NON_OK_RESPONSE_FROM_SERVER 3016
+#define IDS_OS_NOT_SUPPORTED 3017
+#define IDS_DOWNLOAD_HASH_MISMATCH 3018
+#define IDS_DOWNLOAD_ERROR 3019
+#define IDS_CACHING_ERROR 3020
+#define IDS_INVALID_INSTALLER_FILENAME 3021
+#define IDS_INSTALLER_FAILED_TO_START 3022
+#define IDS_INSTALLER_TIMED_OUT 3023
+#define IDS_INSTALLER_FAILED_WITH_MESSAGE 3024
+#define IDS_INSTALLER_FAILED_NO_MESSAGE 3025
+#define IDS_MSI_INSTALL_ALREADY_RUNNING 3026
+#define IDS_INSTALL_FAILED 3027
+#define IDS_CANCELED 3028
+#define IDS_APP_INSTALL_DISABLED_BY_GROUP_POLICY 3029
+#define IDS_INSTALLER_DISPLAY_NAME 3030
+#define IDS_INITIALIZING 3031
+#define IDS_WAITING_TO_CONNECT 3032
+#define IDS_DOWNLOAD_RETRY 3033
+#define IDS_WAITING_TO_DOWNLOAD 3034
+#define IDS_DOWNLOADING_SHORT 3035
+#define IDS_DOWNLOADING_LONG 3036
+#define IDS_DOWNLOADING_VERY_LONG 3037
+#define IDS_DOWNLOADING_COMPLETED 3038
+#define IDS_DOWNLOADING 3039
+#define IDS_WAITING_TO_INSTALL 3040
+#define IDS_INSTALLING 3041
+#define IDS_CANCELING 3043
+#define IDS_TEXT_RESTART_BROWSER 3044
+#define IDS_TEXT_RESTART_ALL_BROWSERS 3045
+#define IDS_TEXT_RESTART_COMPUTER 3046
+#define IDS_CLOSE 3047
+#define IDS_RESTART_NOW 3050
+#define IDS_RESTART_LATER 3051
+#define IDS_GET_HELP_TEXT 3052
+#define IDS_INSTALLATION_STOPPED_WINDOW_TITLE 3053
+#define IDS_INSTALL_STOPPED 3054
+#define IDS_RESUME_INSTALLATION 3055
+#define IDS_CANCEL_INSTALLATION 3056
+#define IDS_SPLASH_SCREEN_MESSAGE 3057
+#define IDS_CONTINUE_AS_NONADMIN 3058
+#define IDS_YES 3059
+#define IDS_NO 3060
+#define IDS_WINDOWS_IS_NOT_UP_TO_DATE 3061
+#define IDS_NEED_ADMIN_TO_INSTALL 3062
+#define IDS_SETUP_FAILED 3063
+#define IDS_ELEVATION_FAILED 3064
+#define IDS_APPLICATION_INSTALLING_GOOGLE_UPDATE 3065
+#define IDS_INSTANCES_RUNNING_AFTER_SHUTDOWN 3066
+#define IDS_APPLICATION_ALREADY_INSTALLING 3067
+#define IDS_HANDOFF_FAILED 3068
+#define IDS_USER_SHOULD_NOT_RUN_ELEVATED_WITH_UAC_ON 3069
+#define IDS_INSTALL_FAILED_WITH_ERROR_CODE 3070
+#define IDS_BUNDLE_INSTALLED_SUCCESSFULLY 3071
+#define IDS_BUNDLE_INSTALLED_SUCCESSFULLY_AFTER_CANCEL 3072
+#define IDS_BUNDLE_MIXED_RESULTS_MESSAGE_ONE_FAILURE 3073
+#define IDS_BUNDLE_MIXED_RESULTS_MESSAGE_MULTIPLE_FAILURES 3074
+#define IDS_BUNDLE_MIXED_RESULTS_SUCCEEDED_APPS 3075
+#define IDS_BUNDLE_MIXED_RESULTS_FAILED_APPS 3076
+#define IDS_BUNDLE_MIXED_RESULTS_CANCELED_APPS 3077
+#define IDS_APPLICATION_NAME_CONCATENATION 3078
+#define IDS_PROXY_PROMPT_MESSAGE 3079
 #define IDI_SUCCEESS 1000
 #define IDD_PROGRESS 2000
 #define IDC_CLOSE 2001
@@ -95,5 +111,7 @@
 #define IDC_IMAGE 2011
 #define IDD_INSTALL_STOPPED 2012
 #define IDC_INSTALL_STOPPED_TEXT 2013
+#define IDD_YES_NO 2014
+#define IDC_YES_NO_TEXT 2015
 
-#endif // RESOURCE_350927934028__
+#endif // RESOURCE_48542623907__
diff --git a/goopdate/resources/resource_only_dll.def b/goopdate/resources/resource_only_dll.def
new file mode 100644
index 0000000..078c46b
--- /dev/null
+++ b/goopdate/resources/resource_only_dll.def
@@ -0,0 +1,16 @@
+; Copyright 2007-2009 Google Inc.
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+; ========================================================================
+;
+; dummy def file to create implib
diff --git a/goopdate/resources/shared_resources.rc b/goopdate/resources/shared_resources.rc
deleted file mode 100644
index 6370022..0000000
--- a/goopdate/resources/shared_resources.rc
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// This file contains the dialog templates for goopdate resources.
-// The file is used by GRIT to create the language specific resources, hence
-// we do not need to include any header files in here. GRIT automatically
-// generates the resource header.
-
-#include "afxres.h"
-#include "goopdateres/goopdate.grh"
-
-#ifndef APSTUDIO_INVOKED
-
-VS_VERSION_INFO VERSIONINFO
- FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_PATCH
- PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_PATCH
- FILEFLAGSMASK 0x17L
-#ifdef _DEBUG
- FILEFLAGS 0x1L
-#else
- FILEFLAGS 0x0L
-#endif
- FILEOS 0x4L
- FILETYPE 0x0L
- FILESUBTYPE 0x0L
-BEGIN
-    BLOCK "StringFileInfo"
-    BEGIN
-        BLOCK "[GRITVERLANGCHARSETHEX]"
-        BEGIN
-            VALUE "CompanyName", "Google Inc."
-            VALUE "FileDescription", "Google Update Resource DLL"
-            VALUE "FileVersion", VERSION_NUMBER_STRING
-            VALUE "InternalName", "Google Update Resource DLL"
-            VALUE "LegalCopyright", "Copyright 2007-2010 Google Inc."
-            VALUE "OriginalFilename", "Goopdateres.dll"
-            VALUE "ProductName", "Google Update"
-            VALUE "ProductVersion", VERSION_NUMBER_STRING
-            VALUE "LanguageId", LANGUAGE_STRING
-#ifdef _DEBUG
-            VALUE "Debug", ""
-#endif
-#if !OFFICIAL_BUILD
-            VALUE "Privatebuild", ""
-#endif
-        END
-    END
-    BLOCK "VarFileInfo"
-    BEGIN
-        VALUE "Translation", [GRITVERLANGID], [GRITVERCHARSETID]
-    END
-END
-
-#endif  // APSTUDIO_INVOKED
-
-
-IDD_PROGRESS DIALOGEX 0, 0, 325, 80
-STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | WS_MINIMIZEBOX |
-    WS_POPUP | WS_CLIPSIBLINGS | WS_CAPTION | WS_SYSMENU
-FONT 8, "MS Shell Dlg 2", 0, 0, 0x0
-BEGIN
-    PUSHBUTTON  "",IDC_CLOSE,248,57,70,16,NOT WS_VISIBLE
-    PUSHBUTTON  "",IDC_BUTTON1,7,57,152,16,NOT WS_VISIBLE
-    PUSHBUTTON  "",IDC_BUTTON2,166,57,152,16,NOT WS_VISIBLE
-    CONTROL     "",IDC_PROGRESS,"msctls_progress32",NOT WS_VISIBLE,7,36,311,10
-    LTEXT       "",IDC_INSTALLER_STATE_TEXT,7,7,197,16,NOT WS_VISIBLE
-    LTEXT       "",IDC_INFO_TEXT,7,54,311,8,NOT WS_VISIBLE
-    LTEXT       "",IDC_PAUSE_RESUME_TEXT,234,25,33,8,SS_NOTIFY | NOT WS_VISIBLE
-    LTEXT       "",IDC_COMPLETE_TEXT,39,7,279,43,SS_NOTIFY | NOT WS_VISIBLE
-    LTEXT       "",IDC_ERROR_TEXT,7,7,311,42,SS_NOTIFY | NOT WS_VISIBLE
-    LTEXT       "",IDC_GET_HELP_TEXT,7,60,220,16,SS_NOTIFY | NOT WS_VISIBLE
-    ICON           IDI_SUCCEESS,IDC_IMAGE,7,7,17,17
-END
-
-IDD_INSTALL_STOPPED DIALOGEX 0, 0, 260, 70
-STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION
-FONT 8, "MS Shell Dlg 2", 400, 0, 0x1
-BEGIN
-    DEFPUSHBUTTON   "",IDOK,133,49,120,14
-    PUSHBUTTON      "",IDCANCEL,7,49,120,14
-    LTEXT           "",IDC_INSTALL_STOPPED_TEXT,7,7,246,34
-END
diff --git a/goopdate/resources/shared_resources_bidi.rc b/goopdate/resources/shared_resources_bidi.rc
deleted file mode 100644
index 9812df5..0000000
--- a/goopdate/resources/shared_resources_bidi.rc
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// This file contains the dialog templates for BIDI languages.
-// The file is used by GRIT to create the language specific resources, hence
-// we do not need to include any header files in here. GRIT automatically
-// generates the resource header.
-
-#include "afxres.h"
-#include "goopdateres/goopdate.grh"
-
-#ifndef APSTUDIO_INVOKED
-
-VS_VERSION_INFO VERSIONINFO
- FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_PATCH
- PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_PATCH
- FILEFLAGSMASK 0x17L
-#ifdef _DEBUG
- FILEFLAGS 0x1L
-#else
- FILEFLAGS 0x0L
-#endif
- FILEOS 0x4L
- FILETYPE 0x0L
- FILESUBTYPE 0x0L
-BEGIN
-    BLOCK "StringFileInfo"
-    BEGIN
-        BLOCK "[GRITVERLANGCHARSETHEX]"
-        BEGIN
-            VALUE "CompanyName", "Google Inc."
-            VALUE "FileDescription", "Google Update Resource DLL"
-            VALUE "FileVersion", VERSION_NUMBER_STRING
-            VALUE "InternalName", "Google Update Resource DLL"
-            VALUE "LegalCopyright", "Copyright 2007-2010 Google Inc."
-            VALUE "OriginalFilename", "Goopdateres.dll"
-            VALUE "ProductName", "Google Update"
-            VALUE "ProductVersion", VERSION_NUMBER_STRING
-            VALUE "LanguageId", LANGUAGE_STRING
-#ifdef _DEBUG
-            VALUE "Debug", ""
-#endif
-#if !OFFICIAL_BUILD
-            VALUE "Privatebuild", ""
-#endif
-        END
-    END
-    BLOCK "VarFileInfo"
-    BEGIN
-        VALUE "Translation", [GRITVERLANGID], [GRITVERCHARSETID]
-    END
-END
-
-#endif  // APSTUDIO_INVOKED
-
-IDD_PROGRESS DIALOGEX 0, 0, 325, 80
-STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | WS_MINIMIZEBOX |
-    WS_POPUP | WS_CLIPSIBLINGS | WS_CAPTION | WS_SYSMENU
-FONT 8, "MS Shell Dlg 2", 0, 0, 0x0
-EXSTYLE WS_EX_LAYOUTRTL
-BEGIN
-    PUSHBUTTON  "",IDC_CLOSE,248,57,70,16,NOT WS_VISIBLE
-    PUSHBUTTON  "",IDC_BUTTON1,7,57,152,16,NOT WS_VISIBLE
-    PUSHBUTTON  "",IDC_BUTTON2,166,57,152,16,NOT WS_VISIBLE
-    CONTROL     "",IDC_PROGRESS,"msctls_progress32",NOT WS_VISIBLE,7,36,311,10
-    LTEXT       "",IDC_INSTALLER_STATE_TEXT,7,7,197,16,NOT WS_VISIBLE
-    LTEXT       "",IDC_INFO_TEXT,7,54,311,8,NOT WS_VISIBLE
-    LTEXT       "",IDC_PAUSE_RESUME_TEXT,234,25,33,8,SS_NOTIFY | NOT WS_VISIBLE
-    LTEXT       "",IDC_COMPLETE_TEXT,39,7,279,43,SS_NOTIFY | NOT WS_VISIBLE
-    LTEXT       "",IDC_ERROR_TEXT,7,7,311,42,SS_NOTIFY | NOT WS_VISIBLE
-    LTEXT       "",IDC_GET_HELP_TEXT,7,60,220,16,SS_NOTIFY | NOT WS_VISIBLE
-    ICON           IDI_SUCCEESS,IDC_IMAGE,7,7,17,17
-END
-
-IDD_INSTALL_STOPPED DIALOGEX 0, 0, 260, 70
-STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION
-FONT 8, "MS Shell Dlg 2", 400, 0, 0x1
-EXSTYLE WS_EX_LAYOUTRTL
-BEGIN
-    DEFPUSHBUTTON   "",IDOK,133,49,120,14
-    PUSHBUTTON      "",IDCANCEL,7,49,120,14
-    LTEXT           "",IDC_INSTALL_STOPPED_TEXT,7,7,246,34
-END
diff --git a/goopdate/response.xsd b/goopdate/response.xsd
deleted file mode 100644
index 8b9b07a..0000000
--- a/goopdate/response.xsd
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" 
-           targetNamespace="http://www.google.com/omaha/response" 
-            xmlns:xs="http://www.w3.org/2001/XMLSchema"
-            xmlns:ts="http://www.google.com/omaha/response">
-  <xs:simpleType name="ResponseType">
-    <xs:restriction base="xs:string">
-      <xs:enumeration value="ok" />
-      <xs:enumeration value="noupdate" />
-      <xs:enumeration value="unknownapplication" />
-    </xs:restriction>
-  </xs:simpleType>
-  <xs:simpleType name="NeedsAdminType">
-    <xs:restriction base="xs:string">
-      <xs:enumeration value="true" />
-      <xs:enumeration value="false" />
-      <xs:enumeration value="prefers" />
-    </xs:restriction>
-  </xs:simpleType>
-  <xs:simpleType name="GuidType">
-    <xs:restriction base="xs:string">
-      <xs:pattern value="{[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}}" />
-    </xs:restriction>
-  </xs:simpleType>
-  <xs:element name="responses">
-    <xs:complexType>
-      <xs:choice>
-        <xs:element minOccurs="0" maxOccurs="unbounded" name="response">
-          <xs:complexType>
-            <xs:sequence minOccurs="0">
-              <xs:element name="codebase" type="xs:string" />
-              <xs:element name="needsadmin" type="ts:NeedsAdminType" />
-              <xs:element name="hash" type="xs:string" />
-              <xs:element name="rlz" type="xs:string" />
-            </xs:sequence>
-            <xs:attribute name="status" type="ts:ResponseType" use="required" />
-            <xs:attribute name="appid" type="ts:GuidType" use="required" />
-          </xs:complexType>
-        </xs:element>
-        <xs:element minOccurs="0" maxOccurs="unbounded" name="install">
-          <xs:complexType>
-            <xs:attribute name="needsadmin" type="ts:NeedsAdminType" />
-            <xs:attribute name="appguid" type="ts:GuidType" />
-          </xs:complexType>
-        </xs:element>
-      </xs:choice>
-      <xs:attribute name="ver" type="xs:decimal" use="required" />
-      <xs:attribute name="signature" type="xs:string" use="required" />
-    </xs:complexType>
-  </xs:element>
-</xs:schema>
\ No newline at end of file
diff --git a/goopdate/server_resource.h b/goopdate/server_resource.h
new file mode 100644
index 0000000..8f88763
--- /dev/null
+++ b/goopdate/server_resource.h
@@ -0,0 +1,21 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_SERVER_RESOURCE_H_
+#define OMAHA_GOOPDATE_SERVER_RESOURCE_H_
+
+#include "omaha/goopdate/resources/goopdateres/goopdate.grh"
+
+#endif  // OMAHA_GOOPDATE_SERVER_RESOURCE_H_
diff --git a/goopdate/stats_uploader.cc b/goopdate/stats_uploader.cc
deleted file mode 100644
index 7e6eebb..0000000
--- a/goopdate/stats_uploader.cc
+++ /dev/null
@@ -1,260 +0,0 @@
-// Copyright 2008-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include "omaha/goopdate/stats_uploader.h"
-#include <atlbase.h>
-#include <atlconv.h>
-#include <atlstr.h>
-#include <ctime>
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/net/browser_request.h"
-#include "omaha/net/network_config.h"
-#include "omaha/net/network_request.h"
-#include "omaha/net/simple_request.h"
-#include "omaha/statsreport/aggregator-win32.h"
-#include "omaha/statsreport/const-win32.h"
-#include "omaha/statsreport/formatter.h"
-#include "omaha/statsreport/metrics.h"
-#include "omaha/statsreport/persistent_iterator-win32.h"
-
-using stats_report::g_global_metrics;
-
-using stats_report::kCountsKeyName;
-using stats_report::kTimingsKeyName;
-using stats_report::kIntegersKeyName;
-using stats_report::kBooleansKeyName;
-using stats_report::kStatsKeyFormatString;
-using stats_report::kLastTransmissionTimeValueName;
-
-using stats_report::Formatter;
-using stats_report::MetricsAggregatorWin32;
-using stats_report::PersistentMetricsIteratorWin32;
-
-namespace omaha {
-
-namespace {
-
-HRESULT ResetPersistentMetrics(RegKey* key) {
-  ASSERT1(key);
-  HRESULT result = S_OK;
-  DWORD now_sec = static_cast<DWORD>(time(NULL));
-  HRESULT hr = key->SetValue(kLastTransmissionTimeValueName, now_sec);
-  if (FAILED(hr)) {
-    result = hr;
-  }
-  hr = key->DeleteSubKey(kCountsKeyName);
-  if (FAILED(hr)) {
-    result = hr;
-  }
-  hr = key->DeleteSubKey(kTimingsKeyName);
-  if (FAILED(hr)) {
-    result = hr;
-  }
-  hr = key->DeleteSubKey(kIntegersKeyName);
-  if (FAILED(hr)) {
-    result = hr;
-  }
-  hr = key->DeleteSubKey(kBooleansKeyName);
-  if (FAILED(hr)) {
-    result = hr;
-  }
-  return result;
-}
-
-// Returns S_OK without uploading in OEM mode.
-HRESULT UploadMetrics(bool is_machine,
-                      const TCHAR* extra_url_data,
-                      const TCHAR* content) {
-  ASSERT1(content);
-
-  // Do not access the network during an OEM install.
-  if (!ConfigManager::Instance()->CanUseNetwork(is_machine)) {
-    CORE_LOG(L1, (_T("[Stats not uploaded because network use prohibited]")));
-    return GOOPDATE_E_CANNOT_USE_NETWORK;
-  }
-
-  const TCHAR* version = GetVersionString();
-  CString test_source(ConfigManager::Instance()->GetTestSource());
-
-  CString url(kUrlUsageStatsReport);
-  url.AppendFormat(_T("?%s=%s&%s=%s&%s=%s&%s=%s&%s"),
-      kMetricsServerParamSourceId,  kMetricsProductName,
-      kMetricsServerParamVersion,   version,
-      kMetricsServerParamIsMachine, is_machine ? _T("1") : _T("0"),
-      kMetricsServerTestSource,     test_source,
-      extra_url_data);
-
-  CORE_LOG(L3, (_T("[upload usage stats][%s]"), content));
-
-  const NetworkConfig::Session& session(NetworkConfig::Instance().session());
-  NetworkRequest network_request(session);
-  network_request.set_num_retries(1);
-  network_request.AddHttpRequest(new SimpleRequest);
-  network_request.AddHttpRequest(new BrowserRequest);
-
-  // PostRequest falls back to https.
-  CString response;
-  return PostRequest(&network_request, true, url, content, &response);
-}
-
-HRESULT ReportMetrics(bool is_machine,
-                      const TCHAR* extra_url_data,
-                      DWORD interval) {
-  PersistentMetricsIteratorWin32 it(kMetricsProductName, is_machine), end;
-  Formatter formatter(CT2A(kMetricsProductName), interval);
-
-  for (; it != end; ++it) {
-    formatter.AddMetric(*it);
-  }
-
-  return UploadMetrics(is_machine, extra_url_data, CA2T(formatter.output()));
-}
-
-HRESULT DoResetMetrics(bool is_machine) {
-  CString key_name;
-  key_name.Format(kStatsKeyFormatString, kMetricsProductName);
-  HKEY parent_key = is_machine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
-  RegKey key;
-  HRESULT hr = key.Create(parent_key, key_name);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[Unable to create metrics key][0x%08x]"), hr));
-    return hr;
-  }
-  return ResetPersistentMetrics(&key);
-}
-
-HRESULT DoAggregateMetrics(bool is_machine) {
-  MetricsAggregatorWin32 aggregator(g_global_metrics,
-                                    kMetricsProductName,
-                                    is_machine);
-  if (!aggregator.AggregateMetrics()) {
-    CORE_LOG(LW, (_T("[Metrics aggregation failed for unknown reasons]")));
-    return GOOPDATE_E_METRICS_AGGREGATE_FAILED;
-  }
-  return S_OK;
-}
-
-HRESULT DoAggregateAndReportMetrics(bool is_machine, bool force_report) {
-  CString key_name;
-  key_name.Format(kStatsKeyFormatString, kMetricsProductName);
-  HKEY parent_key = is_machine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
-  RegKey key;
-  HRESULT hr = key.Create(parent_key, key_name);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[Unable to create metrics key][0x%08x]"), hr));
-    return hr;
-  }
-
-  DWORD now = static_cast<DWORD>(time(NULL));
-
-  DWORD last_transmission_time(0);
-  hr = key.GetValue(kLastTransmissionTimeValueName, &last_transmission_time);
-
-  // Reset and start over if last transmission time is missing or hinky.
-  if (FAILED(hr) || last_transmission_time > now) {
-    CORE_LOG(LW, (_T("[hinky or missing last transmission time]")));
-    ResetPersistentMetrics(&key);
-    return S_OK;
-  }
-
-  hr = DoAggregateMetrics(is_machine);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[DoAggregateMetrics failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  DWORD time_since_last_transmission = now - last_transmission_time;
-  if (force_report ||
-      time_since_last_transmission >= kMetricsUploadIntervalSec) {
-    // Report the metrics, reset the metrics, and update 'LastTransmission'.
-    HRESULT hr = ReportMetrics(is_machine, NULL, time_since_last_transmission);
-    if (SUCCEEDED(hr)) {
-      VERIFY1(SUCCEEDED(ResetPersistentMetrics(&key)));
-      DWORD now_sec = static_cast<DWORD>(time(NULL));
-      CORE_LOG(L3, (_T("[Stats upload successful]")));
-      return S_OK;
-    } else {
-      CORE_LOG(LE, (_T("[Stats upload failed][0x%08x]"), hr));
-      return hr;
-    }
-  }
-
-  return S_OK;
-}
-
-bool InitializeLock(GLock* lock, bool is_machine) {
-  ASSERT1(lock);
-  NamedObjectAttributes attributes;
-  GetNamedObjectAttributes(kMetricsSerializer, is_machine, &attributes);
-  return lock->InitializeWithSecAttr(attributes.name, &attributes.sa);
-}
-
-}  // namespace
-
-
-HRESULT ResetMetrics(bool is_machine) {
-  CORE_LOG(L2, (_T("[ResetMetrics]")));
-  GLock lock;
-  if (!InitializeLock(&lock, is_machine)) {
-    return GOOPDATE_E_METRICS_LOCK_INIT_FAILED;
-  }
-  __mutexScope(lock);
-  return DoResetMetrics(is_machine);
-}
-
-HRESULT AggregateMetrics(bool is_machine) {
-  CORE_LOG(L2, (_T("[AggregateMetrics]")));
-
-  if (!ConfigManager::Instance()->CanCollectStats(is_machine)) {
-    return S_OK;
-  }
-
-  GLock lock;
-  if (!InitializeLock(&lock, is_machine)) {
-    return GOOPDATE_E_METRICS_LOCK_INIT_FAILED;
-  }
-  __mutexScope(lock);
-  return DoAggregateMetrics(is_machine);
-}
-
-HRESULT AggregateAndReportMetrics(bool is_machine, bool force_report) {
-  CORE_LOG(L2, (_T("[AggregateAndReportMetrics]")));
-
-  if (!ConfigManager::Instance()->CanCollectStats(is_machine)) {
-    return S_OK;
-  }
-
-  GLock lock;
-  if (!InitializeLock(&lock, is_machine)) {
-    return GOOPDATE_E_METRICS_LOCK_INIT_FAILED;
-  }
-  __mutexScope(lock);
-  return DoAggregateAndReportMetrics(is_machine, force_report);
-}
-
-}  // namespace omaha
-
diff --git a/goopdate/stats_uploader.h b/goopdate/stats_uploader.h
deleted file mode 100644
index ab679f7..0000000
--- a/goopdate/stats_uploader.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2008-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// Constants for the statsreport library.
-
-#ifndef OMAHA_GOOPDATE_STATS_UPLOADER_H__
-#define OMAHA_GOOPDATE_STATS_UPLOADER_H__
-
-#include <windows.h>
-#include <tchar.h>
-
-namespace omaha {
-
-// The product name is chosen so that the stats are persisted under
-// the Google Update registry key for the machine or user, respectively.
-const TCHAR* const kMetricsProductName           = _T("Update");
-
-const TCHAR* const kMetricsServerParamSourceId   = _T("sourceid");
-const TCHAR* const kMetricsServerParamVersion    = _T("v");
-const TCHAR* const kMetricsServerParamIsMachine  = _T("ismachine");
-const TCHAR* const kMetricsServerTestSource      = _T("testsource");
-
-// Metrics are uploaded every 25 hours.
-const int kMetricsUploadIntervalSec              = 25 * 60 * 60;
-
-// Deletes existing metrics and initializes 'LastTransmission' to current time.
-HRESULT ResetMetrics(bool is_machine);
-
-// Aggregates metrics by saving them in registry.
-HRESULT AggregateMetrics(bool is_machine);
-
-// Aggregates and reports the metrics if needed, as defined by the metrics
-// upload interval. The interval is ignored when 'force_report' is true.
-HRESULT AggregateAndReportMetrics(bool is_machine, bool force_report);
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_STATS_UPLOADER_H__
-
diff --git a/goopdate/stats_uploader_unittest.cc b/goopdate/stats_uploader_unittest.cc
deleted file mode 100644
index a974774..0000000
--- a/goopdate/stats_uploader_unittest.cc
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// All tests are user only.
-
-#include <windows.h>
-#include <limits.h>
-#include <ctime>
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/error.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/goopdate/stats_uploader.h"
-#include "omaha/statsreport/metrics.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-namespace {
-
-DEFINE_METRIC_bool(test_bool);
-
-}  // namespace
-
-class StatsUploaderTest : public testing::Test {
- protected:
-  virtual void SetUp() {
-    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
-
-    // Overriding HKLM prevents the Windows DNS resolver from working.
-    // Only override HKCU and run the tests as user.
-    OverrideSpecifiedRegistryHives(kRegistryHiveOverrideRoot, false, true);
-    stats_report::g_global_metrics.Initialize();
-
-    // These tests assume that metric collection is enabled.
-    ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
-                                      kRegValueForceUsageStats,
-                                      static_cast<DWORD>(1)));
-  }
-
-  virtual void TearDown() {
-    stats_report::g_global_metrics.Uninitialize();
-    RestoreRegistryHives();
-    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
-  }
-
-  HRESULT GetMetricValue(const TCHAR* value_name, bool* value) {
-    CString key_name = key_name_ + CString(_T("Booleans"));
-
-    scoped_array<byte> buffer;
-    DWORD byte_count(0);
-    HRESULT hr = RegKey::GetValue(key_name,
-                                  value_name,
-                                  address(buffer),
-                                  &byte_count);
-    if (FAILED(hr)) {
-      return hr;
-    }
-    if (byte_count != sizeof(uint32)) {                         // NOLINT
-      return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
-    }
-    *value = *reinterpret_cast<uint32*>(buffer.get()) != 0;
-    return S_OK;
-  }
-
-  HRESULT GetLastTrasmission(DWORD* last_transmission) {
-    const TCHAR value_name[] = _T("LastTransmission");
-    return RegKey::GetValue(key_name_, value_name, last_transmission);
-  }
-
-  HRESULT SetLastTransmission(DWORD last_transmission) {
-    const TCHAR value_name[] = _T("LastTransmission");
-    return RegKey::SetValue(key_name_, value_name, last_transmission);
-  }
-
-  bool AreMetricsEmpty() {
-    RegKey reg_key;
-    HRESULT hr = reg_key.Open(key_name_, KEY_READ);
-    if (FAILED(hr)) {
-      return true;
-    }
-    return reg_key.GetSubkeyCount() == 0;
-  }
-
-  static const TCHAR key_name_[];
-  static const TCHAR metric_name_[];
-};
-
-const TCHAR StatsUploaderTest::key_name_[] =
-    _T("HKCU\\Software\\Google\\Update\\UsageStats\\Daily\\");
-const TCHAR StatsUploaderTest::metric_name_[] =  _T("test_bool");
-
-
-TEST_F(StatsUploaderTest, AggregateMetrics) {
-  bool value = false;
-  EXPECT_HRESULT_FAILED(GetMetricValue(metric_name_, &value));
-
-  metric_test_bool = true;
-  EXPECT_HRESULT_SUCCEEDED(AggregateMetrics(false));    // User.
-
-  EXPECT_HRESULT_SUCCEEDED(GetMetricValue(metric_name_, &value));
-  EXPECT_EQ(true, value);
-
-  metric_test_bool = false;
-  EXPECT_HRESULT_SUCCEEDED(AggregateMetrics(false));    // User.
-
-  EXPECT_HRESULT_SUCCEEDED(GetMetricValue(metric_name_, &value));
-  EXPECT_EQ(false, value);
-}
-
-TEST_F(StatsUploaderTest, AggregateAndReportMetrics) {
-  metric_test_bool = true;
-
-  // Metrics are not in the registry until they are aggregated.
-  bool value = false;
-  EXPECT_HRESULT_FAILED(GetMetricValue(metric_name_, &value));
-
-  // AggregateAndReportMetrics resets metrics and updates 'LastTransmission' to
-  // the current time since there was no 'LastTransmission'.
-  EXPECT_HRESULT_SUCCEEDED(AggregateAndReportMetrics(false, false));
-  EXPECT_TRUE(AreMetricsEmpty());
-  DWORD last_transmission(0);
-  EXPECT_HRESULT_SUCCEEDED(GetLastTrasmission(&last_transmission));
-  EXPECT_NE(0, last_transmission);
-
-  // AggregateAndReportMetrics aggregates but it does not report since
-  // 'LastTransmission is current.
-  EXPECT_HRESULT_SUCCEEDED(AggregateAndReportMetrics(false, false));
-  EXPECT_FALSE(AreMetricsEmpty());
-  EXPECT_HRESULT_SUCCEEDED(GetMetricValue(metric_name_, &value));
-  EXPECT_EQ(true, value);
-  DWORD previous_last_transmission = last_transmission;
-  EXPECT_HRESULT_SUCCEEDED(GetLastTrasmission(&last_transmission));
-  EXPECT_EQ(previous_last_transmission, last_transmission);
-
-  // Roll back 'Last Trasmission' by 26 hours. AggregateAndReportMetrics
-  // aggregates, reports metrics, and updates 'LastTransmission'.
-  metric_test_bool = true;
-  last_transmission -= 26 * 60 * 60;
-  EXPECT_HRESULT_SUCCEEDED(SetLastTransmission(last_transmission));
-  EXPECT_HRESULT_SUCCEEDED(AggregateAndReportMetrics(false, false));
-  EXPECT_TRUE(AreMetricsEmpty());
-  previous_last_transmission = last_transmission;
-  EXPECT_HRESULT_SUCCEEDED(GetLastTrasmission(&last_transmission));
-  EXPECT_NE(previous_last_transmission, last_transmission);
-
-  // Roll forward the 'LastTransmission' by 60 seconds.
-  // AggregateAndReportMetrics resets metrics and updates 'LastTransmission' to
-  // the current time since there 'LastTransmission' was in the future.
-  metric_test_bool = true;
-  last_transmission = static_cast<DWORD>(time(NULL)) + 60;
-  EXPECT_HRESULT_SUCCEEDED(SetLastTransmission(last_transmission));
-  EXPECT_HRESULT_SUCCEEDED(AggregateAndReportMetrics(false, false));
-  EXPECT_TRUE(AreMetricsEmpty());
-  EXPECT_HRESULT_SUCCEEDED(GetLastTrasmission(&last_transmission));
-  EXPECT_NE(0, last_transmission);
-
-  // Force reporting the metrics.
-  metric_test_bool = true;
-  EXPECT_HRESULT_SUCCEEDED(AggregateAndReportMetrics(false, true));
-  EXPECT_TRUE(AreMetricsEmpty());
-}
-
-TEST_F(StatsUploaderTest, ResetPersistentMetricsTest) {
-  const TCHAR* keys[] = {
-    _T("HKCU\\Software\\Google\\Update\\UsageStats\\Daily\\Timings"),
-    _T("HKCU\\Software\\Google\\Update\\UsageStats\\Daily\\Counts"),
-    _T("HKCU\\Software\\Google\\Update\\UsageStats\\Daily\\Integers"),
-    _T("HKCU\\Software\\Google\\Update\\UsageStats\\Daily\\Booleans"),
-  };
-  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKeys(keys, arraysize(keys)));
-  EXPECT_HRESULT_SUCCEEDED(ResetMetrics(false));    // User.
-
-  for (size_t i = 0; i != arraysize(keys); ++i) {
-    EXPECT_FALSE(RegKey::HasKey(keys[i]));
-  }
-  EXPECT_TRUE(AreMetricsEmpty());
-
-  DWORD last_transmission(ULONG_MAX);
-  EXPECT_HRESULT_SUCCEEDED(GetLastTrasmission(&last_transmission));
-  EXPECT_NE(0, last_transmission);
-}
-
-// AggregateAndReportMetrics aggregates, but is unable to report metrics and
-// does not update 'LastTransmission'.
-TEST_F(StatsUploaderTest,
-       AggregateAndReportMetrics_GoogleUpdateEulaNotAccepted_DoNotForce) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  metric_test_bool = true;
-  DWORD last_transmission = 12345678;
-  EXPECT_HRESULT_SUCCEEDED(SetLastTransmission(last_transmission));
-  EXPECT_EQ(GOOPDATE_E_CANNOT_USE_NETWORK,
-            AggregateAndReportMetrics(false, false));
-  EXPECT_FALSE(AreMetricsEmpty());
-  DWORD previous_last_transmission = last_transmission;
-  EXPECT_HRESULT_SUCCEEDED(GetLastTrasmission(&last_transmission));
-  EXPECT_EQ(12345678, last_transmission);
-}
-
-// AggregateAndReportMetrics aggregates, but is unable to report metrics and
-// does not update 'LastTransmission'.
-TEST_F(StatsUploaderTest,
-       AggregateAndReportMetrics_GoogleUpdateEulaNotAccepted_Force) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  metric_test_bool = true;
-  DWORD last_transmission = 12345678;
-  EXPECT_HRESULT_SUCCEEDED(SetLastTransmission(last_transmission));
-  EXPECT_EQ(GOOPDATE_E_CANNOT_USE_NETWORK,
-            AggregateAndReportMetrics(false, true));
-  EXPECT_FALSE(AreMetricsEmpty());
-  DWORD previous_last_transmission = last_transmission;
-  EXPECT_HRESULT_SUCCEEDED(GetLastTrasmission(&last_transmission));
-  EXPECT_EQ(12345678, last_transmission);
-}
-
-}  // namespace omaha
diff --git a/goopdate/string_formatter.cc b/goopdate/string_formatter.cc
new file mode 100644
index 0000000..19e3aba
--- /dev/null
+++ b/goopdate/string_formatter.cc
@@ -0,0 +1,76 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/string_formatter.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/utils.h"
+#include "omaha/goopdate/resource_manager.h"
+
+namespace omaha {
+
+StringFormatter::StringFormatter(const CString& language)
+    : language_(language) {
+  ASSERT1(!language.IsEmpty());
+}
+
+HRESULT StringFormatter::LoadString(int32 resource_id, CString* result) {
+  ASSERT1(result);
+
+  HINSTANCE resource_handle = NULL;
+  HRESULT hr = ResourceManager::Instance().GetResourceDll(language_,
+                                                          &resource_handle);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  const TCHAR* resource_string = NULL;
+  int string_length = ::LoadString(
+      resource_handle,
+      resource_id,
+      reinterpret_cast<TCHAR*>(&resource_string),
+      0);
+  if (string_length <= 0) {
+    return HRESULTFromLastError();
+  }
+  ASSERT1(resource_string && *resource_string);
+
+  // resource_string is the string starting point but not null-terminated, so
+  // explicitly copy from it for string_length characters.
+  result->SetString(resource_string, string_length);
+
+  return S_OK;
+}
+
+HRESULT StringFormatter::FormatMessage(CString* result, int32 format_id, ...) {
+  ASSERT1(result);
+  ASSERT1(format_id != 0);
+
+  CString format_string;
+  HRESULT hr = LoadString(format_id, &format_string);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  va_list arguments;
+  va_start(arguments, format_id);
+  result->FormatMessageV(format_string, &arguments);
+  va_end(arguments);
+
+  return S_OK;
+}
+
+}  // namespace omaha
diff --git a/goopdate/string_formatter.h b/goopdate/string_formatter.h
new file mode 100644
index 0000000..374d516
--- /dev/null
+++ b/goopdate/string_formatter.h
@@ -0,0 +1,45 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_STRING_FORMATTER_H_
+#define OMAHA_GOOPDATE_STRING_FORMATTER_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+
+namespace omaha {
+
+class StringFormatter {
+ public:
+  explicit StringFormatter(const CString& language);
+  ~StringFormatter() {}
+
+  // Loads string from the language resource DLL.
+  HRESULT LoadString(int32 resource_id, CString* result);
+
+  // Loads string for format_id from the language resource DLL and then use
+  // that as the format string to create the result string.
+  HRESULT FormatMessage(CString* result, int32 format_id, ...);
+
+ private:
+  CString language_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(StringFormatter);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_STRING_FORMATTER_H_
diff --git a/goopdate/string_formatter_unittest.cc b/goopdate/string_formatter_unittest.cc
new file mode 100644
index 0000000..95d3c3e
--- /dev/null
+++ b/goopdate/string_formatter_unittest.cc
@@ -0,0 +1,105 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "base/scoped_ptr.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/string.h"
+#include "omaha/common/lang.h"
+#include "omaha/goopdate/resource_manager.h"
+#include "omaha/goopdate/server_resource.h"
+#include "omaha/goopdate/string_formatter.h"
+#include "omaha/testing/unit_test.h"
+
+using ::testing::_;
+
+namespace omaha {
+
+class StringFormatterTest : public testing::Test {
+ protected:
+  StringFormatterTest() {}
+
+  static void SetUpTestCase() {
+    CString resource_dir = app_util::GetModuleDirectory(NULL);
+    EXPECT_HRESULT_SUCCEEDED(
+        ResourceManager::CreateForDefaultLanguage(false, resource_dir));
+  }
+  static void TearDownTestCase() {
+    ResourceManager::Delete();
+  }
+
+  virtual void SetUp() {}
+
+  virtual void TearDown() {}
+};
+
+TEST_F(StringFormatterTest, LoadStringTest) {
+  CString loaded_string;
+  StringFormatter formatter_en(_T("en"));
+  EXPECT_HRESULT_SUCCEEDED(formatter_en.LoadString(IDS_CLOSE, &loaded_string));
+  EXPECT_STREQ(_T("Close"), loaded_string);
+
+  StringFormatter formatter_de(_T("de"));
+  EXPECT_HRESULT_SUCCEEDED(
+      formatter_de.LoadString(IDS_DOWNLOADING, &loaded_string));
+  // The loaded string should keep the raw format ('%1!s!') untouched.
+  EXPECT_STREQ(_T("%1!s! wird heruntergeladen..."), loaded_string);
+
+  // Test that loading non-existing language resource returns error.
+  {
+    StringFormatter formatter_unknown(_T("non-existing"));
+    ExpectAsserts expect_asserts;
+    EXPECT_HRESULT_FAILED(
+        formatter_unknown.LoadString(IDS_CLOSE, &loaded_string));
+  }
+}
+
+TEST_F(StringFormatterTest, FormatMessageTest) {
+  CString format_result;
+
+  // Test FormatMessage loads string from correct language resource file.
+  StringFormatter formatter_en(_T("en"));
+  EXPECT_HRESULT_SUCCEEDED(
+      formatter_en.FormatMessage(&format_result, IDS_CLOSE));
+  EXPECT_STREQ(_T("Close"), format_result);
+
+  StringFormatter formatter_de(_T("de"));
+  EXPECT_HRESULT_SUCCEEDED(
+      formatter_de.FormatMessage(&format_result, IDS_CLOSE));
+  EXPECT_STREQ(_T("Schließen"), format_result);   // NOLINT
+
+  StringFormatter formatter_fr(_T("fr"));
+  EXPECT_HRESULT_SUCCEEDED(
+      formatter_fr.FormatMessage(&format_result, IDS_CLOSE));
+  EXPECT_STREQ(_T("Fermer"), format_result);
+
+  // Test FormatMessage with additional argument(s).
+  EXPECT_HRESULT_SUCCEEDED(formatter_en.FormatMessage(&format_result,
+                                                      IDS_DOWNLOADING,
+                                                      _T("English")));
+  EXPECT_STREQ(_T("Downloading English..."), format_result);
+
+  EXPECT_HRESULT_SUCCEEDED(formatter_de.FormatMessage(&format_result,
+                                                      IDS_DOWNLOADING,
+                                                      _T("German")));
+  EXPECT_STREQ(_T("German wird heruntergeladen..."), format_result);
+
+  EXPECT_HRESULT_SUCCEEDED(formatter_fr.FormatMessage(&format_result,
+                                                      IDS_DOWNLOADING,
+                                                      _T("French")));
+  EXPECT_STREQ(_T("Téléchargement de French..."), format_result);   // NOLINT
+}
+
+}  // namespace omaha
+
diff --git a/goopdate/success.ico b/goopdate/success.ico
deleted file mode 100644
index 178e2b6..0000000
--- a/goopdate/success.ico
+++ /dev/null
Binary files differ
diff --git a/goopdate/ui_displayed_event.cc b/goopdate/ui_displayed_event.cc
deleted file mode 100644
index b44b2c1..0000000
--- a/goopdate/ui_displayed_event.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/goopdate/ui_displayed_event.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/goopdate/goopdate_utils.h"
-
-namespace omaha {
-
-HRESULT UIDisplayedEventManager::CreateEvent(bool is_machine) {
-  ASSERT1(!IsEventHandleInitialized());
-  return goopdate_utils::CreateUniqueEventInEnvironment(
-        kUiDisplayedEventEnvironmentVariableName,
-        is_machine,
-        address(ui_displayed_event_));
-}
-
-// Caller does not own the event handle and must not close it.
-// There is a single event handle for each process. The handle is closed when
-// the process exits.
-HRESULT UIDisplayedEventManager::GetEvent(bool is_machine,
-                                          HANDLE* ui_displayed_event) {
-  ASSERT1(ui_displayed_event);
-  *ui_displayed_event = NULL;
-  if (IsEventHandleInitialized()) {
-    *ui_displayed_event = get(ui_displayed_event_);
-    return S_OK;
-  }
-
-  HRESULT hr = goopdate_utils::OpenUniqueEventFromEnvironment(
-      kUiDisplayedEventEnvironmentVariableName,
-      is_machine,
-      address(ui_displayed_event_));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  *ui_displayed_event = get(ui_displayed_event_);
-  return S_OK;
-}
-
-// Creates the event if it does not already exist in the environment.
-void UIDisplayedEventManager::SignalEvent(bool is_machine) {
-  CORE_LOG(L2, (_T("[SignalEvent]")));
-
-  if (!IsEventHandleInitialized()) {
-    HRESULT hr = GetEvent(is_machine, address(ui_displayed_event_));
-    if (HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND) == hr) {
-      // The event was not created by an earlier process. This can happen when
-      // developers run the /handoff process directly.
-      hr = CreateEvent(is_machine);
-    }
-    if (FAILED(hr)) {
-      reset(ui_displayed_event_);
-      // We may display two UIs.
-      return;
-    }
-  }
-
-  ASSERT1(IsEventHandleInitialized());
-  VERIFY1(::SetEvent(get(ui_displayed_event_)));
-}
-
-// Returns false if the event does not exist in the environment variable.
-bool UIDisplayedEventManager::HasUIBeenDisplayed(bool is_machine) {
-  HANDLE ui_displayed_event(NULL);
-  HRESULT hr = GetEvent(is_machine, &ui_displayed_event);
-  if (FAILED(hr)) {
-    return false;
-  }
-
-  int res = ::WaitForSingleObject(ui_displayed_event, 0);
-  ASSERT1(WAIT_OBJECT_0 == res || WAIT_TIMEOUT == res);
-  return WAIT_OBJECT_0 == res;
-}
-
-bool UIDisplayedEventManager::IsEventHandleInitialized() {
-  return valid(ui_displayed_event_);
-}
-
-scoped_handle UIDisplayedEventManager::ui_displayed_event_;
-
-}  // namespace omaha
diff --git a/goopdate/ui_displayed_event.h b/goopdate/ui_displayed_event.h
deleted file mode 100644
index 0c24186..0000000
--- a/goopdate/ui_displayed_event.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_GOOPDATE_UI_DISPLAYED_EVENT_H_
-#define OMAHA_GOOPDATE_UI_DISPLAYED_EVENT_H_
-
-#include <windows.h>
-#include "omaha/common/scoped_any.h"
-
-namespace omaha {
-
-// Manages the UI Displayed Event, which is used to communicate whether a UI
-// has been displayed between processes.
-// This class is not thread safe.
-class UIDisplayedEventManager {
- public:
-  // Creates the event and sets its name in the environment variable.
-  static HRESULT CreateEvent(bool is_machine);
-
-  // Gets the event from the name in the environment variable.
-  static HRESULT GetEvent(bool is_machine, HANDLE* ui_displayed_event);
-
-  // Signals the event. Creates it if its name does not already exist in the
-  // environment variable.
-  static void SignalEvent(bool is_machine);
-
-  // Returns whether the event has been signaled.
-  static bool HasUIBeenDisplayed(bool is_machine);
-
- private:
-  // Returns whether this process's event handle has been initialized.
-  static bool IsEventHandleInitialized();
-
-  // A single instance of the UI Displayed Event handle to be used for the
-  // lifetime of this process.
-  static scoped_handle ui_displayed_event_;
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_UI_DISPLAYED_EVENT_H_
diff --git a/goopdate/update3web.cc b/goopdate/update3web.cc
new file mode 100644
index 0000000..7aefe69
--- /dev/null
+++ b/goopdate/update3web.cc
@@ -0,0 +1,528 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/update3web.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/error.h"
+#include "omaha/base/user_rights.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/update3_utils.h"
+#include "omaha/common/lang.h"
+
+namespace omaha {
+
+namespace {
+
+template <typename Base, typename T, typename Z>
+HRESULT ComInitHelper(T data, Z** p) {
+  *p = NULL;
+  CComObject<Base>* object;
+  HRESULT hr = CComObject<Base>::CreateInstance(&object);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  CComPtr<IUnknown> object_releaser = object;
+  hr = object->Init(data);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return object->QueryInterface(IID_PPV_ARGS(p));
+}
+
+class ATL_NO_VTABLE AppBundleWeb
+    : public CComObjectRootEx<CComObjectThreadModel>,
+      public IDispatchImpl<IAppBundleWeb,
+                           &__uuidof(IAppBundleWeb),
+                           &CAtlModule::m_libid,
+                           kMajorTypeLibVersion,
+                           kMinorTypeLibVersion> {
+ public:
+  AppBundleWeb();
+  HRESULT Init(Update3WebBase* update3web);
+
+  DECLARE_NOT_AGGREGATABLE(AppBundleWeb);
+  DECLARE_NO_REGISTRY();
+
+  BEGIN_COM_MAP(AppBundleWeb)
+    COM_INTERFACE_ENTRY(IDispatch)
+    COM_INTERFACE_ENTRY(IAppBundleWeb)
+  END_COM_MAP()
+
+  STDMETHOD(createApp)(BSTR app_id, BSTR brand_code, BSTR language, BSTR ap);
+  STDMETHOD(createInstalledApp)(BSTR app_id);
+  STDMETHOD(createAllInstalledApps)();
+  STDMETHOD(get_displayLanguage)(BSTR* language);
+  STDMETHOD(put_displayLanguage)(BSTR language);
+  STDMETHOD(put_parentHWND)(ULONG_PTR hwnd);
+  STDMETHOD(get_length)(int* number);
+  STDMETHOD(get_appWeb)(int index, IDispatch** app_web);
+  STDMETHOD(initialize)();
+  STDMETHOD(checkForUpdate)();
+  STDMETHOD(download)();
+  STDMETHOD(install)();
+  STDMETHOD(pause)();
+  STDMETHOD(resume)();
+  STDMETHOD(cancel)();
+  STDMETHOD(downloadPackage)(BSTR app_id, BSTR package_name);
+  STDMETHOD(get_currentState)(VARIANT* current_state);
+
+ protected:
+  virtual ~AppBundleWeb();
+
+ private:
+  Update3WebBase* update3web_;
+  CComPtr<IAppBundle> app_bundle_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppBundleWeb);
+};
+
+class ATL_NO_VTABLE AppWeb
+    : public CComObjectRootEx<CComObjectThreadModel>,
+      public IDispatchImpl<IAppWeb,
+                           &__uuidof(IAppWeb),
+                           &CAtlModule::m_libid,
+                           kMajorTypeLibVersion,
+                           kMinorTypeLibVersion> {
+ public:
+  AppWeb();
+  HRESULT Init(IApp* app);
+
+  DECLARE_NOT_AGGREGATABLE(AppWeb);
+  DECLARE_NO_REGISTRY();
+
+  BEGIN_COM_MAP(AppWeb)
+    COM_INTERFACE_ENTRY(IDispatch)
+    COM_INTERFACE_ENTRY(IAppWeb)
+  END_COM_MAP()
+
+  STDMETHOD(get_appId)(BSTR* app_id);
+  STDMETHOD(get_currentVersionWeb)(IDispatch** current);
+  STDMETHOD(get_nextVersionWeb)(IDispatch** next);
+  STDMETHOD(cancel)();
+  STDMETHOD(get_currentState)(IDispatch** current_state);
+  STDMETHOD(launch)();
+  STDMETHOD(uninstall)();
+
+ protected:
+  virtual ~AppWeb();
+
+ private:
+  CComPtr<IApp> app_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppWeb);
+};
+
+class ATL_NO_VTABLE AppVersionWeb
+    : public CComObjectRootEx<CComObjectThreadModel>,
+      public IDispatchImpl<IAppVersionWeb,
+                           &__uuidof(IAppVersionWeb),
+                           &CAtlModule::m_libid,
+                           kMajorTypeLibVersion,
+                           kMinorTypeLibVersion> {
+ public:
+  AppVersionWeb();
+  HRESULT Init(IAppVersion* app_version);
+
+  DECLARE_NOT_AGGREGATABLE(AppVersionWeb);
+  DECLARE_NO_REGISTRY();
+
+  BEGIN_COM_MAP(AppVersionWeb)
+    COM_INTERFACE_ENTRY(IDispatch)
+    COM_INTERFACE_ENTRY(IAppVersionWeb)
+  END_COM_MAP()
+
+  STDMETHOD(get_version)(BSTR* version);
+  STDMETHOD(get_packageCount)(long* count);  // NOLINT
+  STDMETHOD(get_packageWeb)(long index, IDispatch** package);  // NOLINT
+
+ protected:
+  virtual ~AppVersionWeb();
+
+ private:
+  CComPtr<IAppVersion> app_version_;
+
+  DISALLOW_COPY_AND_ASSIGN(AppVersionWeb);
+};
+
+HRESULT AppBundleWeb::Init(Update3WebBase* update3web) {
+  ASSERT1(update3web);
+
+  update3web_ = update3web;
+  update3web_->AddRef();
+
+  HRESULT hr = update3_utils::CreateAppBundle(update3web_->omaha_server(),
+                                              &app_bundle_);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CreateAppBundle failed][0x%x]"), hr));
+    return hr;
+  }
+
+  // ::CoSetProxyBlanket() settings are per proxy. For Update3Web, after
+  // unmarshaling the interface, we need to set the blanket on this new proxy.
+  // The proxy blanket on the IAppBundle interface are set explicitly for
+  // Update3Web, because Update3Web is a unique case of being a COM server as
+  // well as a COM client. The default security settings set for the Update3Web
+  // COM server are more restrictive and rightly so, as compared to the settings
+  // that we set for a COM client such as the Omaha3 UI. Hence the need to
+  // explicitly set the proxy blanket settings and lower the security
+  // requirements only when calling out on this interface.
+  hr = update3_utils::SetProxyBlanketAllowImpersonate(app_bundle_);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = app_bundle_->put_originURL(CComBSTR(update3web_->origin_url()));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[put_originURL failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = app_bundle_->put_displayLanguage(
+      CComBSTR(lang::GetLanguageForProcess(CString())));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[put_displayLanguage failed][0x%x]"), hr));
+    return hr;
+  }
+
+  // TODO(omaha3): Expose setting the display name to the plugin client.
+  hr = app_bundle_->put_displayName(CComBSTR(_T("App")));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[put_displayName failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = app_bundle_->put_installSource(
+      CComBSTR(kCmdLineInstallSource_Update3Web));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[put_installSource failed][0x%x]"), hr));
+    return hr;
+  }
+
+  if (update3web_->is_machine_install()) {
+    hr = app_bundle_->put_altTokens(
+        reinterpret_cast<ULONG_PTR>(update3web_->impersonation_token()),
+        reinterpret_cast<ULONG_PTR>(update3web_->primary_token()),
+        ::GetCurrentProcessId());
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[put_altTokens failed][0x%x]"), hr));
+      return hr;
+    }
+  }
+
+  return S_OK;
+}
+
+STDMETHODIMP AppBundleWeb::get_displayLanguage(BSTR* language) {
+  return app_bundle_->get_displayLanguage(language);
+}
+
+STDMETHODIMP AppBundleWeb::put_displayLanguage(BSTR language) {
+  return app_bundle_->put_displayLanguage(language);
+}
+
+STDMETHODIMP AppBundleWeb::put_parentHWND(ULONG_PTR hwnd) {
+  return app_bundle_->put_parentHWND(hwnd);
+}
+
+STDMETHODIMP AppBundleWeb::get_length(int* number) {
+  long long_number = 0;  // NOLINT(runtime/int)
+  HRESULT hr = app_bundle_->get_Count(&long_number);
+  *number = long_number;
+  return hr;
+}
+
+STDMETHODIMP AppBundleWeb::get_appWeb(int index, IDispatch** app_web) {
+  *app_web = NULL;
+
+  CComPtr<IApp> app;
+  HRESULT hr = update3_utils::GetApp(app_bundle_, index, &app);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[GetApp failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return ComInitHelper<AppWeb>(app.p, app_web);
+}
+
+AppBundleWeb::AppBundleWeb() : update3web_(NULL) {
+}
+
+STDMETHODIMP AppBundleWeb::createApp(BSTR app_id,
+                                     BSTR brand_code,
+                                     BSTR language,
+                                     BSTR ap) {
+  CComPtr<IApp> app;
+  HRESULT hr = update3_utils::CreateApp(app_id, app_bundle_, &app);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CreateApp failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = app->put_brandCode(brand_code);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = app->put_language(language);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = app->put_ap(ap);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = app->put_isEulaAccepted(VARIANT_TRUE);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = app_bundle_->put_installSource(
+      CComBSTR(kCmdLineInstallSource_Update3Web_NewApps));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+STDMETHODIMP AppBundleWeb::createInstalledApp(BSTR app_id) {
+  CComPtr<IApp> app;
+  HRESULT hr = update3_utils::CreateInstalledApp(app_id, app_bundle_, &app);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CreateInstalledApp failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = app_bundle_->put_installSource(
+      CComBSTR(kCmdLineInstallSource_Update3Web_OnDemand));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT AppBundleWeb::createAllInstalledApps() {
+  HRESULT hr = update3_utils::CreateAllInstalledApps(app_bundle_);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CreateAllInstalledApps failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+AppBundleWeb::~AppBundleWeb() {
+  update3web_->Release();
+}
+
+STDMETHODIMP AppBundleWeb::initialize() {
+  return app_bundle_->initialize();
+}
+
+STDMETHODIMP AppBundleWeb::checkForUpdate() {
+  return app_bundle_->checkForUpdate();
+}
+
+STDMETHODIMP AppBundleWeb::download() {
+  return app_bundle_->download();
+}
+
+STDMETHODIMP AppBundleWeb::install() {
+  if (update3web_->is_machine_install() &&
+      !UserRights::TokenIsAdmin(update3web_->impersonation_token())) {
+    CORE_LOG(LE, (_T("[Need to be an admin to call this method]")));
+    return E_ACCESSDENIED;
+  }
+
+  return app_bundle_->install();
+}
+
+STDMETHODIMP AppBundleWeb::pause() {
+  return app_bundle_->pause();
+}
+
+STDMETHODIMP AppBundleWeb::resume() {
+  return app_bundle_->resume();
+}
+
+STDMETHODIMP AppBundleWeb::cancel() {
+  if (update3web_->is_machine_install() &&
+      !UserRights::TokenIsAdmin(update3web_->impersonation_token())) {
+    CORE_LOG(LE, (_T("[Need to be an admin to cancel]")));
+    return E_ACCESSDENIED;
+  }
+
+  return app_bundle_->stop();
+}
+
+STDMETHODIMP AppBundleWeb::downloadPackage(BSTR app_id, BSTR package_name) {
+  CORE_LOG(L1, (_T("[AppBundleWeb::downloadPackage][%s][%s]"),
+                app_id, package_name));
+
+  HRESULT hr = app_bundle_->put_installSource(
+      CComBSTR(kCmdLineInstallSource_Update3Web_Components));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return app_bundle_->downloadPackage(app_id, package_name);
+}
+
+STDMETHODIMP AppBundleWeb::get_currentState(VARIANT* current_state) {
+  return app_bundle_->get_currentState(current_state);
+}
+
+AppWeb::AppWeb() {
+}
+
+HRESULT AppWeb::Init(IApp* app) {
+  app_ = app;
+  return S_OK;
+}
+
+STDMETHODIMP AppWeb::get_appId(BSTR* app_id) {
+  ASSERT1(app_id);
+  return app_->get_appId(app_id);
+}
+
+STDMETHODIMP AppWeb::get_currentVersionWeb(IDispatch** current) {
+  ASSERT1(current);
+  *current = NULL;
+
+  CComPtr<IAppVersion> app_version;
+  HRESULT hr = update3_utils::GetCurrentAppVersion(app_, &app_version);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return ComInitHelper<AppVersionWeb>(app_version.p, current);
+}
+
+STDMETHODIMP AppWeb::get_nextVersionWeb(IDispatch** next) {
+  ASSERT1(next);
+  *next = NULL;
+
+  CComPtr<IAppVersion> app_version;
+  HRESULT hr = update3_utils::GetNextAppVersion(app_, &app_version);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return ComInitHelper<AppVersionWeb>(app_version.p, next);
+}
+
+STDMETHODIMP AppWeb::cancel() {
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP AppWeb::get_currentState(IDispatch** current_state) {
+  *current_state = NULL;
+  return app_->get_currentState(current_state);
+}
+
+STDMETHODIMP AppWeb::launch() {
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP AppWeb::uninstall() {
+  // This method should check for adminness when implemented.
+  return E_NOTIMPL;
+}
+
+AppWeb::~AppWeb() {
+}
+
+AppVersionWeb::AppVersionWeb() {
+}
+
+HRESULT AppVersionWeb::Init(IAppVersion* app_version) {
+  app_version_ = app_version;
+  return S_OK;
+}
+
+STDMETHODIMP AppVersionWeb::get_version(BSTR* version) {
+  return app_version_->get_version(version);
+}
+
+STDMETHODIMP AppVersionWeb::get_packageCount(long* count) {  // NOLINT
+  return app_version_->get_packageCount(count);
+}
+
+STDMETHODIMP AppVersionWeb::get_packageWeb(long index,  // NOLINT
+                                           IDispatch** package) {
+  UNREFERENCED_PARAMETER(index);
+  ASSERT1(package);
+  *package = NULL;
+
+  // TODO(omaha3): Implement this after a security review.
+  return E_NOTIMPL;
+}
+
+AppVersionWeb::~AppVersionWeb() {
+}
+
+}  // namespace
+
+HRESULT Update3WebBase::FinalConstruct() {
+  HRESULT hr =
+      update3_utils::CreateGoogleUpdate3Class(is_machine_, &omaha_server_);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Update3WebBase::FinalConstruct failed][0x%x]"), hr));
+    return hr;
+  }
+
+  if (!is_machine_) {
+    return S_OK;
+  }
+
+  hr = UserRights::GetCallerToken(&impersonation_token_);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[GetCallerToken failed][0x%x]"), hr));
+    return hr;
+  }
+
+  if (!impersonation_token_.CreatePrimaryToken(&primary_token_)) {
+    HRESULT hr = HRESULTFromLastError();
+    CORE_LOG(LE, (_T("[CreatePrimaryToken failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+STDMETHODIMP Update3WebBase::createAppBundleWeb(IDispatch** app_bundle_web) {
+  ASSERT1(app_bundle_web);
+
+  *app_bundle_web = NULL;
+
+  return ComInitHelper<AppBundleWeb>(this, app_bundle_web);
+}
+
+STDMETHODIMP Update3WebBase::setOriginURL(BSTR origin_url) {
+  CORE_LOG(L3, (_T("[Update3WebBase::setOriginURL][%s]"), origin_url));
+
+  if (!origin_url || !wcslen(origin_url)) {
+    return E_INVALIDARG;
+  }
+
+  origin_url_ = origin_url;
+  return S_OK;
+}
+
+}  // namespace omaha
+
diff --git a/goopdate/update3web.h b/goopdate/update3web.h
new file mode 100644
index 0000000..d3e1d3f
--- /dev/null
+++ b/goopdate/update3web.h
@@ -0,0 +1,158 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Defines the Google Update 3 web broker. It defines a narrow set of interfaces
+// to reduce the attack surface from low and medium integrity processes.
+// The web broker used to be a COM elevation point as well, but that
+// functionality has moved into the broker class factory. Note that since
+// Update3Web is a COM service now, ::CoSetProxyBlanket must be called on any
+// interfaces that need to impersonate.
+
+#ifndef OMAHA_GOOPDATE_UPDATE3WEB_H_
+#define OMAHA_GOOPDATE_UPDATE3WEB_H_
+
+#include <windows.h>
+#include <atlbase.h>
+#include <atlcom.h>
+#include "base/basictypes.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/base/atlregmapex.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/preprocessor_fun.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/goopdate/com_proxy.h"
+#include "omaha/goopdate/non_localized_resource.h"
+
+namespace omaha {
+
+#pragma warning(push)
+// Construction of local static object is not thread-safe
+#pragma warning(disable:4640)
+
+class ATL_NO_VTABLE Update3WebBase
+    : public CComObjectRootEx<CComObjectThreadModel>,
+      public IDispatchImpl<IGoogleUpdate3Web,
+                           &__uuidof(IGoogleUpdate3Web),
+                           &CAtlModule::m_libid,
+                           kMajorTypeLibVersion,
+                           kMinorTypeLibVersion>,
+      public IGoogleUpdate3WebSecurity,
+      public StdMarshalInfo {
+ public:
+  explicit Update3WebBase(bool is_machine) : StdMarshalInfo(is_machine),
+                                             is_machine_(is_machine) {}
+
+  BEGIN_COM_MAP(Update3WebBase)
+    COM_INTERFACE_ENTRY(IDispatch)
+    COM_INTERFACE_ENTRY(IGoogleUpdate3Web)
+    COM_INTERFACE_ENTRY(IGoogleUpdate3WebSecurity)
+    COM_INTERFACE_ENTRY(IStdMarshalInfo)
+  END_COM_MAP()
+
+  HRESULT FinalConstruct();
+
+  // IGoogleUpdate3Web
+  STDMETHOD(createAppBundleWeb)(IDispatch** app_bundle_web);
+
+  // IGoogleUpdate3WebSecurity
+  STDMETHOD(setOriginURL)(BSTR origin_url);
+
+  IGoogleUpdate3* omaha_server() const { return omaha_server_.p; }
+  HANDLE impersonation_token() const {
+    return impersonation_token_.GetHandle();
+  }
+  HANDLE primary_token() const { return primary_token_.GetHandle(); }
+  bool is_machine_install() const { return is_machine_; }
+  CString origin_url() const { return origin_url_; }
+
+ protected:
+  virtual ~Update3WebBase() {}
+
+ private:
+  CComPtr<IGoogleUpdate3> omaha_server_;
+  CAccessToken impersonation_token_;
+  CAccessToken primary_token_;
+  bool is_machine_;
+  CString origin_url_;
+
+  DISALLOW_COPY_AND_ASSIGN(Update3WebBase);
+};
+
+template <typename T>
+class ATL_NO_VTABLE Update3Web
+    : public Update3WebBase,
+      public CComCoClass<Update3Web<T> > {
+ public:
+  Update3Web() : Update3WebBase(T::is_machine()) {}
+
+  DECLARE_NOT_AGGREGATABLE(Update3Web);
+  DECLARE_REGISTRY_RESOURCEID_EX(T::registry_res_id())
+
+  BEGIN_REGISTRY_MAP()
+    REGMAP_ENTRY(_T("HKROOT"), T::hk_root())
+    REGMAP_ENTRY(_T("VERSION"), _T("1.0"))
+    REGMAP_ENTRY(_T("PROGID"), T::prog_id())
+    REGMAP_ENTRY(_T("DESCRIPTION"), _T("GoogleUpdate Update3Web"))
+    REGMAP_ENTRY(_T("CLSID"), T::class_id())
+    REGMAP_MODULE2(_T("MODULE"), kOmahaOnDemandFileName)
+    REGMAP_ENTRY(_T("ICONRESID"), PP_STRINGIZE(IDI_ELEVATION_MONIKER_ICON))
+    REGMAP_ENTRY(_T("STRINGRESID"),
+                 PP_STRINGIZE(IDS_ELEVATION_MONIKER_DISPLAYNAME))
+  END_REGISTRY_MAP()
+
+ protected:
+  virtual ~Update3Web() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Update3Web);
+};
+
+struct Update3WebModeUser {
+  static bool is_machine() { return false; }
+  static const TCHAR* const prog_id() { return kProgIDUpdate3WebUser; }
+  static GUID class_id() { return __uuidof(GoogleUpdate3WebUserClass); }
+  static UINT registry_res_id() { return IDR_LOCAL_SERVER_RGS; }
+  static const TCHAR* const hk_root() { return _T("HKCU"); }
+};
+
+struct Update3WebModeMachineFallback {
+  static bool is_machine() { return true; }
+  static const TCHAR* const prog_id() {
+    return kProgIDUpdate3WebMachineFallback;
+  }
+  static GUID class_id() {
+    return __uuidof(GoogleUpdate3WebMachineFallbackClass);
+  }
+  static UINT registry_res_id() { return IDR_LOCAL_SERVER_ELEVATION_RGS; }
+  static const TCHAR* const hk_root() { return _T("HKLM"); }
+};
+
+struct Update3WebModeService {
+  static bool is_machine() { return true; }
+  static const TCHAR* const prog_id() { return kProgIDUpdate3WebSvc; }
+  static GUID class_id() { return __uuidof(GoogleUpdate3WebServiceClass); }
+  static UINT registry_res_id() { return IDR_LOCAL_SERVICE_RGS; }
+  static const TCHAR* const hk_root() { return _T("HKLM"); }
+};
+
+typedef Update3Web<Update3WebModeUser> Update3WebUser;
+typedef Update3Web<Update3WebModeMachineFallback> Update3WebMachineFallback;
+typedef Update3Web<Update3WebModeService> Update3WebService;
+
+#pragma warning(pop)
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_UPDATE3WEB_H_
diff --git a/goopdate/update_request_utils.cc b/goopdate/update_request_utils.cc
new file mode 100644
index 0000000..ebfb565
--- /dev/null
+++ b/goopdate/update_request_utils.cc
@@ -0,0 +1,79 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/update_request_utils.h"
+#include "omaha/base/logging.h"
+#include "omaha/goopdate/model.h"
+
+namespace omaha {
+
+namespace update_request_utils {
+
+void BuildRequest(const App* app,
+                  bool is_update_check,
+                  xml::UpdateRequest* update_request) {
+  ASSERT1(app);
+  ASSERT1(update_request);
+
+  if (!is_update_check && app->ping_events().empty()) {
+    return;
+  }
+
+  if (!app->is_eula_accepted()) {
+    CORE_LOG(L3, (_T("[App EULA not accepted - not including app in ping][%s]"),
+                  app->app_guid_string()));
+    return;
+  }
+
+  xml::request::App request_app;
+
+  // Pick up the current and next versions.
+  request_app.version       = app->current_version()->version();
+  request_app.next_version  = app->next_version()->version();
+
+  request_app.app_id        = app->app_guid_string();
+  request_app.lang          = app->language();
+  request_app.iid           = GuidToString(app->iid());
+  request_app.brand_code    = app->brand_code();
+  request_app.client_id     = app->client_id();
+  request_app.experiments   = app->GetExperimentLabels();
+  request_app.ap            = app->ap();
+
+  // referral_id is not sent.
+
+  request_app.install_time_diff_sec   = app->install_time_diff_sec();
+  request_app.data.install_data_index = app->server_install_data_index();
+
+  if (is_update_check) {
+    request_app.ping.active = app->did_run();
+    request_app.ping.days_since_last_active_ping  =
+        app->days_since_last_active_ping();
+    request_app.ping.days_since_last_roll_call    =
+        app->days_since_last_roll_call();
+
+    request_app.update_check.is_valid             = true;
+    request_app.update_check.is_update_disabled   =
+        FAILED(app->CheckGroupPolicy());
+    request_app.update_check.tt_token             = app->tt_token();
+  }
+
+  request_app.ping_events = app->ping_events();
+
+  update_request->AddApp(request_app);
+}
+
+}  // namespace update_request_utils
+
+}  // namespace omaha
diff --git a/goopdate/update_request_utils.h b/goopdate/update_request_utils.h
new file mode 100644
index 0000000..67ef6e0
--- /dev/null
+++ b/goopdate/update_request_utils.h
@@ -0,0 +1,43 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_UPDATE_REQUEST_UTILS_H_
+#define OMAHA_GOOPDATE_UPDATE_REQUEST_UTILS_H_
+
+#include <windows.h>
+#include "omaha/common/update_request.h"
+
+namespace omaha {
+
+class App;
+
+namespace update_request_utils {
+
+// TODO(omaha): missing unit test.
+//
+// Builds an UpdateRequest object by adding an xml request corresponding to
+// the App object passed as a parameter. This function avoids creating an empty
+// element in the cases where an update checks is not requested and no ping
+// events exist.
+void BuildRequest(const App* app,
+                  bool is_update_check,
+                  xml::UpdateRequest* update_request);
+
+}  // namespace update_request_utils
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_UPDATE_REQUEST_UTILS_H_
+
diff --git a/goopdate/update_request_utils_unittest.cc b/goopdate/update_request_utils_unittest.cc
new file mode 100644
index 0000000..a2c59d4
--- /dev/null
+++ b/goopdate/update_request_utils_unittest.cc
@@ -0,0 +1,204 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/reg_key.h"
+#include "omaha/common/const_group_policy.h"
+#include "omaha/common/update_response.h"
+#include "omaha/goopdate/app_manager.h"
+#include "omaha/goopdate/app_unittest_base.h"
+#include "omaha/goopdate/update_request_utils.h"
+#include "omaha/testing/unit_test.h"
+
+using ::testing::Return;
+
+namespace omaha {
+
+namespace update_request_utils {
+
+namespace {
+
+#define USER_UPDATE_KEY _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME _T("\\")
+#define APP_ID1 _T("{DDE97E2B-A82C-4790-A630-FCA02F64E8BE}");
+const TCHAR* const kAppId1 = APP_ID1
+const TCHAR* const kAppId1ClientsKeyPathUser =
+    USER_UPDATE_KEY _T("Clients\\") APP_ID1;
+const TCHAR* const kAppId1ClientStateKeyPathUser =
+    USER_UPDATE_KEY _T("ClientState\\") APP_ID1;
+const TCHAR* const kInstallPolicyApp1 = _T("Install") APP_ID1;
+const TCHAR* const kUpdatePolicyApp1 = _T("Update") APP_ID1;
+const TCHAR* const kAppDidRunValueName = _T("dr");
+
+void SetPolicy(const CString& policy, DWORD value) {
+  EXPECT_SUCCEEDED(RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
+                                    policy,
+                                    value));
+}
+
+}  // namespace
+
+class UpdateRequestUtilsTest : public AppTestBaseWithRegistryOverride {
+ protected:
+  UpdateRequestUtilsTest()
+      : AppTestBaseWithRegistryOverride(false,  // is_machine
+                                        true),  // use_strict_mock
+        app_(NULL) {}
+
+  virtual void SetUp() {
+    AppTestBaseWithRegistryOverride::SetUp();
+
+    update_request_.reset(xml::UpdateRequest::Create(is_machine_,
+                                                     _T("unittest"),
+                                                     _T("unittest"),
+                                                     CString()));
+
+    EXPECT_SUCCEEDED(
+        app_bundle_->createApp(CComBSTR(kAppId1), &app_));
+    ASSERT_TRUE(app_);
+  }
+
+  App* app_;
+  scoped_ptr<xml::UpdateRequest> update_request_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(UpdateRequestUtilsTest);
+};
+
+// TODO(omaha): write tests.
+
+// For now, this test is just checking !update_check.is_valid. Add more checks.
+TEST_F(UpdateRequestUtilsTest, BuildRequest_Ping) {
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE));
+
+  PingEventPtr ping_event(
+      new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE,
+                    PingEvent::EVENT_RESULT_ERROR, E_FAIL, 0));
+  app_->AddPingEvent(ping_event);
+
+  BuildRequest(app_, false, update_request_.get());
+
+  const xml::request::Request& request = update_request_->request();
+  ASSERT_EQ(1, request.apps.size());
+
+  const xml::request::App& app = request.apps[0];
+  const xml::request::UpdateCheck& update_check = app.update_check;
+  EXPECT_FALSE(update_check.is_valid);
+}
+
+TEST_F(UpdateRequestUtilsTest, BuildRequest_Ping_NoEvents) {
+  BuildRequest(app_, false, update_request_.get());
+
+  const xml::request::Request& request = update_request_->request();
+  ASSERT_EQ(0, request.apps.size());
+}
+
+TEST_F(UpdateRequestUtilsTest, BuildRequest_Ping_EulaNotAccepted) {
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_FALSE));
+
+  PingEventPtr ping_event(
+      new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE,
+                    PingEvent::EVENT_RESULT_ERROR, E_FAIL, 0));
+  app_->AddPingEvent(ping_event);
+
+  BuildRequest(app_, false, update_request_.get());
+
+  const xml::request::Request& request = update_request_->request();
+  ASSERT_EQ(0, request.apps.size());
+}
+
+// For now, this test is just checking is_update_disabled. Add more checks.
+TEST_F(UpdateRequestUtilsTest, BuildRequest_UpdateCheck) {
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE));
+
+  BuildRequest(app_, true, update_request_.get());
+
+  const xml::request::Request& request = update_request_->request();
+  ASSERT_EQ(1, request.apps.size());
+
+  const xml::request::App& app = request.apps[0];
+  const xml::request::UpdateCheck& update_check = app.update_check;
+  EXPECT_TRUE(update_check.is_valid);
+  EXPECT_FALSE(update_check.is_update_disabled);
+}
+
+TEST_F(UpdateRequestUtilsTest,
+       BuildRequest_UpdateCheck_GroupPolicy_InstallDisabled) {
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE));
+
+  SetPolicy(kInstallPolicyApp1, kPolicyDisabled);
+
+  BuildRequest(app_, true, update_request_.get());
+
+  const xml::request::Request& request = update_request_->request();
+  ASSERT_EQ(1, request.apps.size());
+
+  const xml::request::App& app = request.apps[0];
+  const xml::request::UpdateCheck& update_check = app.update_check;
+  EXPECT_TRUE(update_check.is_valid);
+  EXPECT_TRUE(update_check.is_update_disabled);
+}
+
+TEST_F(UpdateRequestUtilsTest,
+       BuildRequest_DoNotPickUpDidRunValueWhenNotDoingUpdateCheck) {
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE));
+  PingEventPtr ping_event(
+      new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE,
+                    PingEvent::EVENT_RESULT_ERROR, E_FAIL, 0));
+  app_->AddPingEvent(ping_event);
+
+  RegKey key;
+  ASSERT_SUCCEEDED(key.Create(kAppId1ClientStateKeyPathUser));
+  ASSERT_SUCCEEDED(key.SetValue(kAppDidRunValueName, _T("1")));
+  __mutexScope(app_->model()->lock());
+  AppManager::Instance()->ReadAppPersistentData(app_);
+
+  BuildRequest(app_, false, update_request_.get());
+
+  const xml::request::Request& request = update_request_->request();
+  ASSERT_EQ(1, request.apps.size());
+
+  const xml::request::Ping& ping = request.apps[0].ping;
+  EXPECT_EQ(ACTIVE_UNKNOWN, ping.active);
+  EXPECT_EQ(0, ping.days_since_last_active_ping);
+  EXPECT_EQ(0, ping.days_since_last_roll_call);
+}
+
+TEST_F(UpdateRequestUtilsTest,
+       BuildRequest_UpdateCheckShouldSendDidRunValue) {
+  EXPECT_SUCCEEDED(app_->put_isEulaAccepted(VARIANT_TRUE));
+  PingEventPtr ping_event(
+      new PingEvent(PingEvent::EVENT_INSTALL_COMPLETE,
+                    PingEvent::EVENT_RESULT_ERROR, E_FAIL, 0));
+  app_->AddPingEvent(ping_event);
+
+  RegKey key;
+  ASSERT_SUCCEEDED(key.Create(kAppId1ClientStateKeyPathUser));
+  ASSERT_SUCCEEDED(key.SetValue(kAppDidRunValueName, _T("1")));
+  __mutexScope(app_->model()->lock());
+  AppManager::Instance()->ReadAppPersistentData(app_);
+
+  BuildRequest(app_, true, update_request_.get());
+
+  const xml::request::Request& request = update_request_->request();
+  ASSERT_EQ(1, request.apps.size());
+
+  const xml::request::Ping& ping = request.apps[0].ping;
+  EXPECT_EQ(ACTIVE_RUN, ping.active);
+  EXPECT_EQ(-1, ping.days_since_last_active_ping);
+  EXPECT_EQ(-1, ping.days_since_last_roll_call);
+}
+
+}  // namespace update_request_utils
+
+}  // namespace omaha
diff --git a/goopdate/update_response.h b/goopdate/update_response.h
deleted file mode 100644
index ec5657e..0000000
--- a/goopdate/update_response.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2008-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// update_response.h:  The hierarchical response for a product and its
-// components from a server request for install/update.
-
-#ifndef OMAHA_GOOPDATE_UPDATE_RESPONSE_H__
-#define OMAHA_GOOPDATE_UPDATE_RESPONSE_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include <map>
-#include "base/basictypes.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/update_response_data.h"
-
-namespace omaha {
-
-class UpdateResponse {
- public:
-  UpdateResponse() : time_since_midnight_sec_(0) {}
-  explicit UpdateResponse(const UpdateResponseData& response_data)
-      : time_since_midnight_sec_(0) {
-    response_data_ = response_data;
-  }
-  ~UpdateResponse() {}
-
-  void set_time_since_midnight_sec(int time_since_midnight_sec) {
-    time_since_midnight_sec_ = time_since_midnight_sec;
-  }
-  int time_since_midnight_sec() const { return time_since_midnight_sec_; }
-
-  void set_update_response_data(const UpdateResponseData& response_data) {
-    response_data_ = response_data;
-  }
-
-  const UpdateResponseData& update_response_data() const {
-    return response_data_;
-  }
-
-  // Adds the component. Returns false if the component already exists, and
-  // does not overwrite the value.
-  bool AddComponentResponseData(const UpdateResponseData& component_data) {
-    std::pair<GUID, UpdateResponseData> data(component_data.guid(),
-                                             component_data);
-    return components_.insert(data).second;
-  }
-
-  UpdateResponseDatas::const_iterator components_begin() const {
-    return components_.begin();
-  }
-
-  UpdateResponseDatas::const_iterator components_end() const {
-    return components_.end();
-  }
-
-  bool IsComponentPresent(const GUID& guid) const {
-    return components_.find(guid) != components_.end();
-  }
-
-  const UpdateResponseData& GetComponentData(const GUID& guid) const {
-    UpdateResponseDatas::const_iterator iter = components_.find(guid);
-    ASSERT1(iter != components_.end());
-    return (*iter).second;
-  }
-
-  size_t num_components() const { return components_.size(); }
-
- private:
-  UpdateResponseData response_data_;
-  UpdateResponseDatas components_;
-  int time_since_midnight_sec_;
-};
-
-// Map of UpdateResponses, key=Guid, value=UpdateResponse.
-typedef std::map<GUID, UpdateResponse, GuidComparer> UpdateResponses;
-
-}  // namespace omaha.
-
-#endif  // OMAHA_GOOPDATE_UPDATE_RESPONSE_H__
-
diff --git a/goopdate/update_response_data.h b/goopdate/update_response_data.h
deleted file mode 100644
index 99ed801..0000000
--- a/goopdate/update_response_data.h
+++ /dev/null
@@ -1,181 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// update_response_data.h: The app/component data for a response including what
-// to download, how big it is, hash, etc..
-
-#ifndef OMAHA_GOOPDATE_UPDATE_RESPONSE_DATA_H__
-#define OMAHA_GOOPDATE_UPDATE_RESPONSE_DATA_H__
-
-#include <functional>
-#include <map>
-#include <vector>
-#include "base/basictypes.h"
-#include "omaha/common/browser_utils.h"
-
-namespace omaha {
-
-// key=IndexString, value=InstallDataString.
-typedef std::map<CString, CString> InstallDataMap;
-
-// Represents the values that are used by the application to indicate its
-// requirement for admin.
-enum NeedsAdmin {
-  NEEDS_ADMIN_YES = 0,  // The application will install machine-wide.
-  NEEDS_ADMIN_NO,       // The application will install per user.
-};
-
-// What Omaha should do on successful installation.
-enum SuccessfulInstallAction {
-  SUCCESS_ACTION_DEFAULT = 0,
-  SUCCESS_ACTION_EXIT_SILENTLY,
-  SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD,
-};
-
-// Represents the information that is parsed from the response returned by
-// the server. The class also represents the information that is parsed
-// from the manifest that is downloaded as part of the meta-installer.
-// The UpdateResponseData contains this information for one app or a component.
-// For an entire product, the UpdateResponse will contain a hierarchy of these,
-// one for the main app and for each component.
-class UpdateResponseData {
- public:
-  UpdateResponseData()
-      : size_(0),
-        needs_admin_(omaha::NEEDS_ADMIN_NO),
-        guid_(GUID_NULL),
-        installation_id_(GUID_NULL),
-        browser_type_(BROWSER_UNKNOWN),
-        terminate_all_browsers_(false),
-        success_action_(SUCCESS_ACTION_DEFAULT) {
-  }
-
-  // Getters and setters for the private members.
-  CString url() const { return url_; }
-  void set_url(const CString& url) { url_ = url; }
-  int size() const { return size_; }
-  void set_size(const int& size) { size_ = size; }
-  CString hash() const { return hash_; }
-  void set_hash(const CString& hash) { hash_ = hash; }
-  NeedsAdmin needs_admin() const { return needs_admin_; }
-  void set_needs_admin(const NeedsAdmin& needs_admin) {
-    needs_admin_ = needs_admin;
-  }
-  CString arguments() const { return arguments_; }
-  void set_arguments(const CString& arguments) { arguments_ = arguments; }
-  GUID guid() const { return guid_; }
-  void set_guid(const GUID& guid) { guid_ = guid; }
-  CString app_name() const { return app_name_; }
-  void set_app_name(const CString& app_name) { app_name_ = app_name; }
-  CString language() const { return language_; }
-  void set_language(const CString& language) { language_ = language; }
-  CString status() const { return status_; }
-  void set_status(const CString& status) { status_ = status; }
-  GUID installation_id() const { return installation_id_; }
-  void set_installation_id(const GUID& installation_id) {
-    installation_id_ = installation_id;
-  }
-  CString ap() const { return ap_; }
-  void set_ap(const CString& ap) { ap_ = ap; }
-
-  CString tt_token() const { return tt_token_; }
-  void set_tt_token(const CString& tt_token) { tt_token_ = tt_token; }
-
-  CString success_url() const { return success_url_; }
-  void set_success_url(const CString& success_url) {
-    success_url_ = success_url;
-  }
-
-  CString error_url() const { return error_url_; }
-  void set_error_url(const CString& error_url) {
-    error_url_ = error_url;
-  }
-
-  BrowserType browser_type() const { return browser_type_; }
-  void set_browser_type(BrowserType type) {
-    browser_type_ = type;
-  }
-
-  bool terminate_all_browsers() const { return terminate_all_browsers_; }
-  void set_terminate_all_browsers(bool terminate_all) {
-    terminate_all_browsers_ = terminate_all;
-  }
-
-  SuccessfulInstallAction success_action() const { return success_action_; }
-  void set_success_action(SuccessfulInstallAction success_action) {
-    success_action_ = success_action;
-  }
-
-  CString version() const { return version_; }
-  void set_version(const CString& version) { version_ = version; }
-
-  CString GetInstallData(const CString& index) const {
-    InstallDataMap::const_iterator iter = install_data_.find(index);
-    if (iter == install_data_.end()) {
-      return CString();
-    }
-    return iter->second;
-  }
-  void SetInstallData(const CString& index, const CString& value) {
-    ASSERT1(install_data_.find(index) == install_data_.end());
-    install_data_[index] = value;
-  }
-
- private:
-  CString url_;               // The url for the application installer.
-  int size_;                  // The size of the download.
-  CString hash_;              // The 160bit SHA1 hash of the downloaded file.
-  NeedsAdmin needs_admin_;    // If the application needs admin to install.
-  CString arguments_;         // Arguments for application installation.
-  GUID guid_;                 // Uniquely represents the application.
-  // Remove these 5 members when legacy support is removed.
-  CString app_name_;          // Application name for display.
-  CString language_;          // The language for this application.
-  GUID installation_id_;      // Uniquely represents this install.
-  CString ap_;                // ap value to be set in the registry.
-  BrowserType browser_type_;  // Browser to launch.
-
-  // The status of the response. There needs to be a protocol established
-  // between the server and client to determine what the action should be
-  // on downloading the application installer.
-  CString status_;
-
-  CString tt_token_;          // TT value to be set in the registry.
-  CString success_url_;       // URL to launch the browser on success.
-  CString error_url_;         // URL describing error.
-  bool terminate_all_browsers_;  // Whether to restart all browsers.
-  SuccessfulInstallAction success_action_;  // Action after install success.
-  CString version_;           // The version of the application itself.
-  InstallDataMap install_data_;  // Written to a file and passed as argument to
-                                 // app installer.
-};
-
-struct GuidComparer : public std::less<GUID> {
-  bool operator()(const GUID& lhs, const GUID& rhs) const {
-    CString lhs_guid_str = GuidToString(lhs);
-    CString rhs_guid_str = GuidToString(rhs);
-    return (_tcsicmp(lhs_guid_str, rhs_guid_str) < 0);
-  }
-};
-
-// Represent the list of requests and responses that are used to communicate
-// with the AU server.
-typedef std::map<GUID, UpdateResponseData, GuidComparer> UpdateResponseDatas;
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_UPDATE_RESPONSE_DATA_H__
-
diff --git a/goopdate/update_response_utils.cc b/goopdate/update_response_utils.cc
new file mode 100644
index 0000000..04a7a29
--- /dev/null
+++ b/goopdate/update_response_utils.cc
@@ -0,0 +1,316 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/update_response_utils.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/common/lang.h"
+#include "omaha/common/experiment_labels.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/server_resource.h"
+#include "omaha/goopdate/string_formatter.h"
+
+namespace omaha {
+
+namespace update_response_utils {
+
+// TODO(omaha): unit test the functions below.
+
+// Returns a pointer to the app object corresponding to the appid in the
+// response object. Returns NULL if the app is not found.
+const xml::response::App* GetApp(const xml::response::Response& response,
+                                 const CString& appid) {
+  size_t app_index = 0;
+  for (; app_index < response.apps.size(); ++app_index) {
+    if (!appid.CompareNoCase(response.apps[app_index].appid)) {
+      return &response.apps[app_index];
+    }
+  }
+  return NULL;
+}
+
+HRESULT GetInstallData(const std::vector<xml::response::Data>& data,
+                       const CString& install_data_index,
+                       CString* install_data) {
+  ASSERT1(install_data);
+  if (install_data_index.IsEmpty()) {
+    return S_OK;
+  }
+
+  std::vector<xml::response::Data>::const_iterator it;
+  for (it = data.begin(); it != data.end(); ++it) {
+    if (install_data_index != it->install_data_index) {
+      continue;
+    }
+
+    if (it->status != kResponseStatusOkValue) {
+      ASSERT1(it->status == kResponseDataStatusNoData);
+      return GOOPDATE_E_INVALID_INSTALL_DATA_INDEX;
+    }
+
+    ASSERT1(!it->install_data.IsEmpty());
+    *install_data = it->install_data;
+    return S_OK;
+  }
+
+  return GOOPDATE_E_INVALID_INSTALL_DATA_INDEX;
+}
+
+// Check the outer elements first, then check any child elements only if the
+// outer element was successful.
+CString GetAppResponseStatus(const xml::response::App& app) {
+  if (_tcsicmp(kResponseStatusOkValue, app.status) != 0) {
+    ASSERT1(!app.status.IsEmpty());
+    return app.status;
+  }
+
+  if (!app.update_check.status.IsEmpty() &&
+      _tcsicmp(kResponseStatusOkValue, app.update_check.status) != 0) {
+    return app.update_check.status;
+  }
+
+  std::vector<xml::response::Data>::const_iterator data;
+  for (data = app.data.begin(); data != app.data.end(); ++data) {
+    if (!data->status.IsEmpty() &&
+        _tcsicmp(kResponseStatusOkValue, data->status) != 0) {
+      return data->status;
+    }
+  }
+
+  if (!app.ping.status.IsEmpty() &&
+      _tcsicmp(kResponseStatusOkValue, app.ping.status) != 0) {
+    return app.ping.status;
+  }
+
+  std::vector<xml::response::Event>::const_iterator it;
+  for (it = app.events.begin(); it != app.events.end(); ++it) {
+    if (!it->status.IsEmpty() &&
+        _tcsicmp(kResponseStatusOkValue, it->status) != 0) {
+      return it->status;
+    }
+  }
+
+  // TODO(omaha): verify that no other elements can report errors
+  // once we've finalized the protocol.
+
+  return app.status;
+}
+
+HRESULT BuildApp(const xml::UpdateResponse* update_response,
+                 HRESULT code,
+                 App* app) {
+  ASSERT1(update_response);
+  ASSERT1(SUCCEEDED(code) || code == GOOPDATE_E_NO_UPDATE_RESPONSE);
+  ASSERT1(app);
+
+  AppVersion* next_version = app->next_version();
+
+  const CString& app_id = app->app_guid_string();
+
+  const xml::response::App* response_app(GetApp(update_response->response(),
+                                                app_id));
+  ASSERT1(response_app);
+  const xml::response::UpdateCheck& update_check = response_app->update_check;
+
+  VERIFY1(SUCCEEDED(app->put_ttToken(CComBSTR(update_check.tt_token))));
+
+  if (code == GOOPDATE_E_NO_UPDATE_RESPONSE) {
+    return S_OK;
+  }
+
+  for (size_t i = 0; i < update_check.urls.size(); ++i) {
+    HRESULT hr = next_version->AddDownloadBaseUrl(update_check.urls[i]);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  for (size_t i = 0; i < update_check.install_manifest.packages.size(); ++i) {
+    const xml::InstallPackage& package(
+        update_check.install_manifest.packages[i]);
+    HRESULT hr = next_version->AddPackage(package.name,
+                                          package.size,
+                                          package.hash);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  CString server_install_data;
+  HRESULT hr = GetInstallData(response_app->data,
+                              app->server_install_data_index(),
+                              &server_install_data);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  app->set_server_install_data(server_install_data);
+
+  ASSERT1(!next_version->install_manifest());
+  next_version->set_install_manifest(
+      new xml::InstallManifest(update_check.install_manifest));
+
+  // TODO(omaha): it appears the version_ below holds either the manifest
+  // version or the "pv" version, written by the installer. If this is the case,
+  // then it is confusing and perhaps we need to have two different members to
+  // hold these values.
+  ASSERT1(next_version->version().IsEmpty());
+  next_version->set_version(next_version->install_manifest()->version);
+
+  return S_OK;
+}
+
+// "noupdate" is an error for fresh installs, but a successful completion in
+// the cases of silent and on demand updates. The caller is responsible for
+// interpreting "noupdate" as it sees fit.
+xml::UpdateResponseResult GetResult(const xml::UpdateResponse* update_response,
+                                    const CString& appid,
+                                    const CString& language) {
+  ASSERT1(update_response);
+  const xml::response::App* response_app(GetApp(update_response->response(),
+                                                appid));
+
+  StringFormatter formatter(language);
+  CString text;
+
+  if (!response_app) {
+    CORE_LOG(L1, (_T("[UpdateResponse::GetResult][app not found][%s]"), appid));
+    VERIFY1(SUCCEEDED(formatter.LoadString(IDS_UNKNOWN_APPLICATION, &text)));
+    return std::make_pair(GOOPDATE_E_NO_SERVER_RESPONSE, text);
+  }
+
+  const xml::response::UpdateCheck& update_check = response_app->update_check;
+  const CString& status = GetAppResponseStatus(*response_app);
+  const CString& display_name = update_check.install_manifest.name;
+
+  ASSERT1(!status.IsEmpty());
+  CORE_LOG(L1, (_T("[UpdateResponse::GetResult][%s][%s][%s]"),
+                appid, status, display_name));
+
+  // ok
+  if (_tcsicmp(kResponseStatusOkValue, status) == 0) {
+    return std::make_pair(S_OK, CString());
+  }
+
+  // noupdate
+  if (_tcsicmp(kResponseStatusNoUpdate, status) == 0) {
+    VERIFY1(SUCCEEDED(formatter.LoadString(IDS_NO_UPDATE_RESPONSE, &text)));
+    return std::make_pair(GOOPDATE_E_NO_UPDATE_RESPONSE, text);
+  }
+
+  // "restricted"
+  if (_tcsicmp(kResponseStatusRestrictedExportCountry, status) == 0) {
+    VERIFY1(SUCCEEDED(formatter.LoadString(IDS_RESTRICTED_RESPONSE_FROM_SERVER,
+                                           &text)));
+    return std::make_pair(GOOPDATE_E_RESTRICTED_SERVER_RESPONSE, text);
+  }
+
+  // "error-UnKnownApplication"
+  if (_tcsicmp(kResponseStatusUnKnownApplication, status) == 0) {
+    VERIFY1(SUCCEEDED(formatter.LoadString(IDS_UNKNOWN_APPLICATION, &text)));
+    return std::make_pair(GOOPDATE_E_UNKNOWN_APP_SERVER_RESPONSE, text);
+  }
+
+  // "error-OsNotSupported"
+  if (_tcsicmp(kResponseStatusOsNotSupported, status) == 0) {
+    const CString& error_url(update_check.error_url);
+    VERIFY1(SUCCEEDED(formatter.LoadString(IDS_OS_NOT_SUPPORTED, &text)));
+    if (!error_url.IsEmpty()) {
+      // TODO(omaha3): The error URL is no longer in the error string. Either
+      // we need to provide this URL to the client or we need to deprecate
+      // error_url and put this information in the Get Help redirect.
+      // Alternatively, we could have the COM server build error URLs in all
+      // cases. The current UI would still need to build an URL for the entire
+      // bundle, though, because it does not have per-app links.
+    }
+    return std::make_pair(GOOPDATE_E_OS_NOT_SUPPORTED, text);
+  }
+
+  // "error-internal"
+  if (_tcsicmp(kResponseStatusInternalError, status) == 0) {
+    VERIFY1(SUCCEEDED(formatter.FormatMessage(&text,
+                                              IDS_NON_OK_RESPONSE_FROM_SERVER,
+                                              status)));
+    return std::make_pair(GOOPDATE_E_INTERNAL_ERROR_SERVER_RESPONSE, text);
+  }
+
+  // "error-hash"
+  if (_tcsicmp(kResponseStatusHashError, status) == 0) {
+    VERIFY1(SUCCEEDED(formatter.FormatMessage(&text,
+                                              IDS_NON_OK_RESPONSE_FROM_SERVER,
+                                              status)));
+    return std::make_pair(GOOPDATE_E_SERVER_RESPONSE_NO_HASH, text);
+  }
+
+  // "error-unsupportedprotocol"
+  if (_tcsicmp(kResponseStatusUnsupportedProtocol, status) == 0) {
+    // TODO(omaha): Ideally, we would provide an app-specific URL instead of
+    // just the publisher name. If it was a link, we could use point to a
+    // redirect URL and provide the app GUID rather than somehow obtaining the
+    // app-specific URL.
+    VERIFY1(SUCCEEDED(formatter.FormatMessage(&text,
+                                              IDS_INSTALLER_OLD,
+                                              kShortCompanyName)));
+    return std::make_pair(GOOPDATE_E_SERVER_RESPONSE_UNSUPPORTED_PROTOCOL,
+                          text);
+  }
+
+  VERIFY1(SUCCEEDED(formatter.FormatMessage(&text,
+                                            IDS_NON_OK_RESPONSE_FROM_SERVER,
+                                            status)));
+  return std::make_pair(GOOPDATE_E_UNKNOWN_SERVER_RESPONSE, text);
+}
+
+bool IsOmahaUpdateAvailable(const xml::UpdateResponse* update_response) {
+  ASSERT1(update_response);
+  xml::UpdateResponseResult update_response_result(
+      update_response_utils::GetResult(update_response,
+                                       kGoogleUpdateAppId,
+                                       lang::GetDefaultLanguage(true)));
+  return update_response_result.first == S_OK;
+}
+
+HRESULT ApplyExperimentLabelDeltas(bool is_machine,
+                                   const xml::UpdateResponse* update_response) {
+  ASSERT1(update_response);
+
+  for (size_t app_index = 0;
+       app_index < update_response->response().apps.size();
+       ++app_index) {
+    const xml::response::App& app = update_response->response().apps[app_index];
+    if (!app.experiments.IsEmpty()) {
+      VERIFY1(IsGuid(app.appid));
+
+      ExperimentLabels labels;
+      VERIFY1(SUCCEEDED(labels.ReadFromRegistry(is_machine, app.appid)));
+
+      if (!labels.DeserializeAndApplyDelta(app.experiments)) {
+        return E_FAIL;
+      }
+
+      HRESULT hr = labels.WriteToRegistry(is_machine, app.appid);
+      if (FAILED(hr)) {
+        return hr;
+      }
+    }
+  }
+
+  return S_OK;
+}
+
+}  // namespace update_response_utils
+
+}  // namespace omaha
+
diff --git a/goopdate/update_response_utils.h b/goopdate/update_response_utils.h
new file mode 100644
index 0000000..18002ec
--- /dev/null
+++ b/goopdate/update_response_utils.h
@@ -0,0 +1,64 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_UPDATE_RESPONSE_UTILS_H_
+#define OMAHA_GOOPDATE_UPDATE_RESPONSE_UTILS_H_
+
+#include <windows.h>
+#include <vector>
+#include "omaha/common/update_response.h"
+
+namespace omaha {
+
+class App;
+
+namespace update_response_utils {
+
+const xml::response::App* GetApp(const xml::response::Response& response,
+                                 const CString& appid);
+
+// Retrieves the install_data string corresponding to the install_data_index
+// in the response data object. Returns an error if the status of the data
+// object is not ok or the index is not found.
+HRESULT GetInstallData(const std::vector<xml::response::Data>& data,
+                       const CString& index,
+                       CString* value);
+
+// Builds an App object from its corresponding representation in the
+// update response.
+HRESULT BuildApp(const xml::UpdateResponse* update_response,
+                 HRESULT code,
+                 App* app);
+
+// Returns the result of the update response for an app. The string member of
+// the result is formatted in the specified language.
+xml::UpdateResponseResult GetResult(const xml::UpdateResponse* update_response,
+                                    const CString& appid,
+                                    const CString& language);
+
+// Returns true if the update response contains an update for Omaha.
+bool IsOmahaUpdateAvailable(const xml::UpdateResponse* update_response);
+
+// Extracts a set of experiment label deltas from a response, merges them with
+// existing labels in the Registry, and writes the resulting set back.
+HRESULT ApplyExperimentLabelDeltas(bool is_machine,
+                                   const xml::UpdateResponse* update_response);
+
+}  // namespace update_response_utils
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_UPDATE_RESPONSE_UTILS_H_
+
diff --git a/goopdate/update_response_utils_unittest.cc b/goopdate/update_response_utils_unittest.cc
new file mode 100644
index 0000000..7d30005
--- /dev/null
+++ b/goopdate/update_response_utils_unittest.cc
@@ -0,0 +1,211 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "base/scoped_ptr.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/error.h"
+#include "omaha/goopdate/resource_manager.h"
+#include "omaha/goopdate/update_response_utils.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace update_response_utils {
+
+using xml::UpdateResponseResult;
+
+namespace {
+
+const TCHAR* const kAppId1 = _T("{CE9C207B-232D-492b-AF03-E590A8FBE8FB}");
+const TCHAR* const kAppId2 = _T("{5881940A-72E4-4194-9DA5-4EA4089F867C}");
+const TCHAR* const kAppId3 = _T("{2DE92FA0-C8A1-4368-8654-16FDBA91817D}");
+
+const TCHAR* const kAppIdWithLowerCase =
+    _T("{C38D11BA-6244-45e3-AC2A-21F2077F2C10}");
+const TCHAR* const kAppIdWithLowerCaseAllUpperCase =
+    _T("{C38D11BA-6244-45E3-AC2A-21F2077F2C10}");
+
+
+const TCHAR* const kGOOPDATE_E_NO_SERVER_RESPONSEString =
+    _T("Installation failed due to a server side error. Please try again ")
+    _T("later. We apologize for the inconvenience.");
+
+const UpdateResponseResult kUpdateAvailableResult =
+    std::make_pair(S_OK, _T(""));
+
+const UpdateResponseResult kAppNotFoundResult = std::make_pair(
+      GOOPDATE_E_NO_SERVER_RESPONSE,
+      kGOOPDATE_E_NO_SERVER_RESPONSEString);
+
+}  // namespace
+
+
+class UpdateResponseUtilsGetResultTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    // Needed for error strings.
+    EXPECT_SUCCEEDED(ResourceManager::Create(
+      false, app_util::GetCurrentModuleDirectory(), _T("en")));
+
+    update_response_.reset(xml::UpdateResponse::Create());
+  }
+
+  virtual void TearDown() {
+    ResourceManager::Delete();
+  }
+
+  scoped_ptr<xml::UpdateResponse> update_response_;
+};
+
+
+// TODO(omaha): write tests.
+
+TEST(UpdateResponseUtilsGetAppTest, AppNotFound) {
+  xml::response::Response response;
+  xml::response::App app;
+  app.status = kResponseStatusOkValue;
+  app.appid = kAppId1;
+  response.apps.push_back(app);
+
+  EXPECT_TRUE(NULL != GetApp(response, kAppId1));
+  EXPECT_EQ(NULL, GetApp(response, kAppId2));
+}
+
+TEST(UpdateResponseUtilsGetAppTest, MultipleApps) {
+  xml::response::Response response;
+  xml::response::App app;
+  app.status = kResponseStatusOkValue;
+  app.appid = kAppId1;
+  response.apps.push_back(app);
+  app.appid = kAppId2;
+  response.apps.push_back(app);
+
+  EXPECT_EQ(&response.apps[0], GetApp(response, kAppId1));
+  EXPECT_EQ(&response.apps[1], GetApp(response, kAppId2));
+  EXPECT_EQ(&response.apps[1], GetApp(response, kAppId2));
+  EXPECT_EQ(&response.apps[0], GetApp(response, kAppId1));
+  EXPECT_NE(GetApp(response, kAppId1), GetApp(response, kAppId2));
+}
+
+TEST(UpdateResponseUtilsGetAppTest, ResponseAppIdHasLowerCase) {
+  xml::response::Response response;
+  xml::response::App app;
+  app.status = kResponseStatusOkValue;
+  app.appid = kAppIdWithLowerCase;
+  response.apps.push_back(app);
+
+  EXPECT_EQ(&response.apps[0], GetApp(response, kAppIdWithLowerCase));
+  EXPECT_EQ(&response.apps[0],
+            GetApp(response, kAppIdWithLowerCaseAllUpperCase));
+}
+
+TEST(UpdateResponseUtilsGetAppTest, ResponseAppIdAllUpperCase) {
+  xml::response::Response response;
+  xml::response::App app;
+  app.status = kResponseStatusOkValue;
+  app.appid = kAppIdWithLowerCaseAllUpperCase;
+  response.apps.push_back(app);
+
+  EXPECT_EQ(&response.apps[0],
+            GetApp(response, kAppIdWithLowerCaseAllUpperCase));
+  EXPECT_EQ(&response.apps[0], GetApp(response, kAppIdWithLowerCase));
+}
+
+TEST_F(UpdateResponseUtilsGetResultTest, EmptyResponse) {
+  EXPECT_TRUE(kAppNotFoundResult ==
+              GetResult(update_response_.get(), kAppId1, _T("en")));
+}
+
+TEST_F(UpdateResponseUtilsGetResultTest, AppFound) {
+  xml::response::Response response;
+  xml::response::App app;
+  app.status = kResponseStatusOkValue;
+  app.update_check.status = kResponseStatusOkValue;
+  app.appid = kAppId1;
+  response.apps.push_back(app);
+  SetResponseForUnitTest(update_response_.get(), response);
+
+  EXPECT_TRUE(kUpdateAvailableResult ==
+              GetResult(update_response_.get(), kAppId1, _T("en")));
+}
+
+TEST_F(UpdateResponseUtilsGetResultTest, AppNotFound) {
+  xml::response::Response response;
+  xml::response::App app;
+  app.status = kResponseStatusOkValue;
+  app.update_check.status = kResponseStatusOkValue;
+  app.appid = kAppId1;
+  response.apps.push_back(app);
+  SetResponseForUnitTest(update_response_.get(), response);
+
+  EXPECT_TRUE(kAppNotFoundResult ==
+              GetResult(update_response_.get(), kAppId2, _T("en")));
+}
+
+TEST_F(UpdateResponseUtilsGetResultTest, MultipleApps) {
+  xml::response::Response response;
+  xml::response::App app;
+  app.status = kResponseStatusOkValue;
+  app.update_check.status = kResponseStatusOkValue;
+  app.appid = kAppId1;
+  response.apps.push_back(app);
+  app.appid = kAppId2;
+  response.apps.push_back(app);
+  SetResponseForUnitTest(update_response_.get(), response);
+
+  EXPECT_TRUE(kUpdateAvailableResult ==
+              GetResult(update_response_.get(), kAppId1, _T("en")));
+  EXPECT_TRUE(kUpdateAvailableResult ==
+              GetResult(update_response_.get(), kAppId2, _T("en")));
+  EXPECT_TRUE(kAppNotFoundResult ==
+              GetResult(update_response_.get(), kAppId3, _T("en")));
+}
+
+TEST_F(UpdateResponseUtilsGetResultTest, ResponseAppIdHasLowerCase) {
+  xml::response::Response response;
+  xml::response::App app;
+  app.status = kResponseStatusOkValue;
+  app.appid = kAppIdWithLowerCase;
+  response.apps.push_back(app);
+  SetResponseForUnitTest(update_response_.get(), response);
+
+  EXPECT_TRUE(kUpdateAvailableResult ==
+              GetResult(update_response_.get(), kAppIdWithLowerCase, _T("en")));
+  EXPECT_TRUE(kUpdateAvailableResult ==
+              GetResult(update_response_.get(), kAppIdWithLowerCaseAllUpperCase,
+                        _T("en")));
+}
+
+TEST_F(UpdateResponseUtilsGetResultTest, ResponseAppIdAllUpperCase) {
+  xml::response::Response response;
+  xml::response::App app;
+  app.status = kResponseStatusOkValue;
+  app.appid = kAppIdWithLowerCaseAllUpperCase;
+  response.apps.push_back(app);
+  SetResponseForUnitTest(update_response_.get(), response);
+
+  EXPECT_TRUE(kUpdateAvailableResult ==
+              GetResult(update_response_.get(), kAppIdWithLowerCaseAllUpperCase,
+                        _T("en")));
+  EXPECT_TRUE(kUpdateAvailableResult ==
+              GetResult(update_response_.get(), kAppIdWithLowerCase, _T("en")));
+}
+
+// TODO(omaha3): Add tests for GetResult from Omaha2's job_creator_unittest.cc.
+
+}  // namespace update_response_utils
+
+}  // namespace omaha
diff --git a/goopdate/webplugin_utils.cc b/goopdate/webplugin_utils.cc
deleted file mode 100644
index 551097e..0000000
--- a/goopdate/webplugin_utils.cc
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include "omaha/goopdate/webplugin_utils.h"
-
-#include "omaha/common/app_util.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/path.h"
-#include "omaha/common/string.h"
-#include "omaha/common/system.h"
-#include "omaha/common/xml_utils.h"
-#include "omaha/goopdate/command_line_builder.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/resource_manager.h"
-#include "omaha/net/browser_request.h"
-#include "omaha/net/cup_request.h"
-#include "omaha/net/network_request.h"
-#include "omaha/net/simple_request.h"
-
-namespace omaha {
-
-namespace webplugin_utils {
-
-HRESULT BuildOneClickRequestString(const CommandLineArgs& args,
-                                   CString* request_str) {
-  if (NULL == request_str) {
-    return E_INVALIDARG;
-  }
-
-  // If we're not /webplugin or the urldomain is empty, something's wrong.
-  if (args.mode != COMMANDLINE_MODE_WEBPLUGIN ||
-      args.webplugin_urldomain.IsEmpty()) {
-    return E_UNEXPECTED;
-  }
-
-  // TODO(omaha): Format() only handles up to 1024 characters.
-  const TCHAR* request_string_template = _T("?du=%s&args=%s");
-  CString request;
-
-  CString urldomain_escaped;
-  CString pluginargs_escaped;
-
-  StringEscape(args.webplugin_urldomain, false, &urldomain_escaped);
-  StringEscape(args.webplugin_args, false, &pluginargs_escaped);
-
-  request.Format(request_string_template,
-                 urldomain_escaped,
-                 pluginargs_escaped);
-
-  *request_str = request;
-  return S_OK;
-}
-
-// Need to verify we can load the resources required for the language the user
-// is requesting.  If for some reason the dll has been deleted on the machine,
-// then the /install instance of GoogleUpdate.exe will fail since it can't
-// load the resource dll.
-HRESULT VerifyResourceLanguage(const CString& webplugin_args) {
-  CString cmd_line;
-  cmd_line.Format(_T("gu.exe %s"), webplugin_args);
-  CommandLineArgs parsed_args;
-  HRESULT hr = ParseCommandLine(cmd_line, &parsed_args);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[ParseCommandLine failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  bool is_machine = goopdate_utils::IsRunningFromOfficialGoopdateDir(true);
-  ResourceManager resource_manager(is_machine,
-                                   app_util::GetCurrentModuleDirectory());
-  hr = resource_manager.LoadResourceDll(parsed_args.extra.language);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[LoadResourceDll failed for lang][%s][0x%08x]"),
-                  parsed_args.extra.language, hr));
-    return GOOPDATE_E_ONECLICK_NO_LANGUAGE_RESOURCE;
-  }
-
-  return S_OK;
-}
-
-HRESULT BuildOneClickWorkerArgs(const CommandLineArgs& args,
-                                CString* oneclick_args) {
-  ASSERT1(oneclick_args);
-
-  // Since this is being called via WebPlugin only, we can rebuild the
-  // command line arguments from the valid params we can send on.
-  // For example, the web plugin will not send crash_cmd or debug_cmd
-  // or reg_server or unreg_server so we don't have to worry about those here.
-  CString cmd_line_args;
-  CommandLineArgs webplugin_cmdline_args;
-
-  // ParseCommandLine assumes the first argument is the program being run.
-  // Don't want to enforce that constraint on our callers, so we prepend with a
-  // fake exe name.
-  CString args_to_parse;
-  args_to_parse.Format(_T("%s %s"), kGoopdateFileName, args.webplugin_args);
-
-  // Parse the arguments we received as the second parameter to /webplugin.
-  HRESULT hr = ParseCommandLine(args_to_parse, &webplugin_cmdline_args);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // Silent and other non-standard installs could be malicious. Prevent them.
-  if (webplugin_cmdline_args.mode != COMMANDLINE_MODE_INSTALL) {
-    return E_INVALIDARG;
-  }
-  if (webplugin_cmdline_args.is_silent_set ||
-      webplugin_cmdline_args.is_eula_required_set) {
-    return E_INVALIDARG;
-  }
-
-  CommandLineBuilder builder(COMMANDLINE_MODE_INSTALL);
-  builder.set_extra_args(webplugin_cmdline_args.extra_args_str);
-
-  // We expect this value from the plugin.
-  ASSERT1(!args.install_source.IsEmpty());
-  if (args.install_source.IsEmpty()) {
-    return E_INVALIDARG;
-  }
-
-  builder.set_install_source(args.install_source);
-
-  *oneclick_args = builder.GetCommandLineArgs();
-
-  return S_OK;
-}
-
-HRESULT CopyGoopdateToTempDir(const CPath& current_goopdate_path,
-                              CPath* goopdate_temp_path) {
-  ASSERT1(goopdate_temp_path);
-
-  // Create a unique directory in the user's temp directory.
-  TCHAR pathbuf[MAX_PATH] = {0};
-  DWORD ret = ::GetTempPath(arraysize(pathbuf), pathbuf);
-  if (0 == ret) {
-    return HRESULTFromLastError();
-  }
-  if (ret >= arraysize(pathbuf)) {
-    return E_FAIL;
-  }
-
-  GUID guid = GUID_NULL;
-  HRESULT hr = ::CoCreateGuid(&guid);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  CString guid_str = GuidToString(guid);
-  CPath temp_path = pathbuf;
-  temp_path.Append(guid_str);
-  temp_path.Canonicalize();
-
-  hr = CreateDir(temp_path, NULL);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = File::CopyWildcards(current_goopdate_path, temp_path, _T("*.*"), true);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  CORE_LOG(L2, (_T("[CopyGoopdateToTempDir][temp_path = %s]"), temp_path));
-  *goopdate_temp_path = temp_path;
-  return S_OK;
-}
-
-HRESULT DoOneClickInstall(const CommandLineArgs& args) {
-  CString cmd_line_args;
-  HRESULT hr = BuildOneClickWorkerArgs(args, &cmd_line_args);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[BuildOneClickWorkerArgs failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  CORE_LOG(L2, (_T("[DoOneClickInstall][cmd_line_args: %s]"), cmd_line_args));
-
-  // Check if we're running from the machine dir.
-  // If we're not, we must be running from user directory since OneClick only
-  // works against installed versions of Omaha.
-  CPath current_goopdate_path(app_util::GetCurrentModuleDirectory());
-  CPath goopdate_temp_path;
-  hr = CopyGoopdateToTempDir(current_goopdate_path, &goopdate_temp_path);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[CopyGoopdateToTempDir failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  CPath goopdate_temp_exe_path = goopdate_temp_path;
-  goopdate_temp_exe_path.Append(kGoopdateFileName);
-
-  // Launch goopdate again with the updated command line arguments.
-  hr = System::ShellExecuteProcess(goopdate_temp_exe_path,
-                                   cmd_line_args,
-                                   NULL,
-                                   NULL);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[ShellExecuteProcess failed][%s][0x%08x]"),
-                  goopdate_temp_exe_path, hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-}  // namespace webplugin_utils
-
-}  // namespace omaha
-
diff --git a/goopdate/webplugin_utils.h b/goopdate/webplugin_utils.h
deleted file mode 100644
index ea35146..0000000
--- a/goopdate/webplugin_utils.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_GOOPDATE_WEBPLUGIN_UTILS_H__
-#define OMAHA_GOOPDATE_WEBPLUGIN_UTILS_H__
-
-#include <windows.h>
-#include <atlpath.h>
-#include <atlstr.h>
-#include "base/basictypes.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/config_manager.h"
-
-namespace omaha {
-
-namespace webplugin_utils {
-
-// Verifies that the resource DLL we're going to load within googleupdate.exe is
-// available and loadable.
-HRESULT VerifyResourceLanguage(const CString& webplugin_args);
-
-// Copies required Goopdate files to a temp location before installing.
-HRESULT CopyGoopdateToTempDir(const CPath& current_goopdate_path,
-                              CPath* goopdate_temp_path);
-
-// Launches google_update.exe based on parameters sent with /webplugin.
-HRESULT DoOneClickInstall(const CommandLineArgs& args);
-
-// Creates request string for the webplugin URL check webservice call.
-HRESULT BuildOneClickRequestString(const CommandLineArgs& args,
-                                   CString* request_str);
-
-// Builds up the command line arguments to re-launch google_update.exe
-// when called with /pi.
-HRESULT BuildOneClickWorkerArgs(const CommandLineArgs& args, CString* args_out);
-
-}  // namespace webplugin_utils
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_WEBPLUGIN_UTILS_H__
diff --git a/goopdate/webplugin_utils_unittest.cc b/goopdate/webplugin_utils_unittest.cc
deleted file mode 100644
index 2b4d31a..0000000
--- a/goopdate/webplugin_utils_unittest.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/app_util.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/path.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/webplugin_utils.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-namespace webplugin_utils {
-
-#define YOUTUBEUPLOADEREN_TAG \
-    _T("\"appguid={A4F7B07B-B9BD-4a33-B136-96D2ADFB60CB}&") \
-    _T("appname=YouTubeUploader&needsadmin=False&lang=en\"")
-
-
-TEST(WebPluginUtilsTest, BuildOneClickRequestString_NullOutParam) {
-  CommandLineArgs args;
-  EXPECT_EQ(E_INVALIDARG, BuildOneClickRequestString(args, NULL));
-}
-
-TEST(WebPluginUtilsTest, BuildOneClickRequestString_WrongArgs) {
-  CommandLineArgs args;
-  CString request;
-  EXPECT_EQ(E_UNEXPECTED, BuildOneClickRequestString(args, &request));
-}
-
-TEST(WebPluginUtilsTest, BuildOneClickRequestString_NoUrlDomain) {
-  CommandLineArgs args;
-  CString request;
-
-  args.mode = COMMANDLINE_MODE_WEBPLUGIN;
-  EXPECT_EQ(E_UNEXPECTED, BuildOneClickRequestString(args, &request));
-}
-
-TEST(WebPluginUtilsTest, BuildOneClickRequestString_Valid) {
-  CommandLineArgs args;
-  CString request;
-
-  args.mode = COMMANDLINE_MODE_WEBPLUGIN;
-  args.webplugin_urldomain = _T("http://www.google.com/");
-  args.webplugin_args = _T("/install \"appguid=")
-      _T("{8A69D345-D564-463c-AFF1-A69D9E530F96}")
-      _T("&appname=Google Chrome&needsadmin=true&lang=en\"");
-  EXPECT_EQ(S_OK, BuildOneClickRequestString(args, &request));
-
-  EXPECT_STREQ(_T("?du=http://www.google.com/&args=/install%20")
-               _T("%22appguid=%7B8A69D345-D564-463c-AFF1-A69D9E530F96")
-               _T("%7D%26appname=Google%20Chrome%26needsadmin=true%26")
-               _T("lang=en%22"),
-               request);
-}
-
-TEST(WebPluginUtilsTest, BuildOneClickWorkerArgs_Valid) {
-  CommandLineArgs args;
-  CString oneclick_args;
-
-  args.install_source = _T("oneclick");
-  args.webplugin_args = _T("/install \"appguid=")
-      _T("{8A69D345-D564-463c-AFF1-A69D9E530F96}")
-      _T("&appname=Google Chrome&needsadmin=true&lang=en\"");
-  EXPECT_EQ(S_OK, BuildOneClickWorkerArgs(args, &oneclick_args));
-
-  EXPECT_STREQ(_T("/install ")
-               _T("\"appguid={8A69D345-D564-463c-AFF1-A69D9E530F96}")
-               _T("&appname=Google Chrome&needsadmin=true&lang=en\" ")
-               _T("/installsource oneclick"),
-               oneclick_args);
-}
-
-// This tests valid command line args that are not valid to be sent through
-// to google_update.exe (e.g. /install).
-TEST(WebPluginUtilsTest, BuildOneClickWorkerArgs_Invalid) {
-  CommandLineArgs args;
-  CString oneclick_args;
-
-  args.install_source = _T("oneclick");
-
-  args.webplugin_args = _T("/handoff ") YOUTUBEUPLOADEREN_TAG;
-  EXPECT_EQ(E_INVALIDARG, BuildOneClickWorkerArgs(args, &oneclick_args));
-
-  args.webplugin_args = _T("/regserver");
-  EXPECT_EQ(E_INVALIDARG, BuildOneClickWorkerArgs(args, &oneclick_args));
-
-  args.webplugin_args = _T("/unregserver");
-  EXPECT_EQ(E_INVALIDARG, BuildOneClickWorkerArgs(args, &oneclick_args));
-
-  args.webplugin_args = _T("/install ") YOUTUBEUPLOADEREN_TAG _T(" /silent");
-  EXPECT_EQ(E_INVALIDARG, BuildOneClickWorkerArgs(args, &oneclick_args));
-}
-
-TEST(WebPluginUtilsTest, CopyGoopdateToTempDir) {
-  CPath current_goopdate_path(app_util::GetCurrentModuleDirectory());
-  current_goopdate_path.Append(_T("unittest_support\\omaha_1.2.x\\"));
-  CPath goopdate_temp_path;
-  ASSERT_SUCCEEDED(CopyGoopdateToTempDir(current_goopdate_path,
-                                         &goopdate_temp_path));
-
-  std::vector<CString> files;
-  EXPECT_HRESULT_SUCCEEDED(FindFilesEx(goopdate_temp_path, _T("*.*"), &files));
-
-  EXPECT_EQ(3, files.size());
-
-  std::map<CString, int> files_map;
-  for (size_t file_index = 0; file_index < files.size(); ++file_index) {
-    files_map[files[file_index]] = 1;
-  }
-
-  EXPECT_TRUE(files_map.find(_T("GoogleUpdate.exe")) != files_map.end());
-  EXPECT_TRUE(files_map.find(_T("goopdate.dll")) != files_map.end());
-  EXPECT_TRUE(files_map.find(_T("goopdateres_en.dll")) != files_map.end());
-
-  EXPECT_HRESULT_SUCCEEDED(DeleteDirectory(goopdate_temp_path));
-}
-
-TEST(WebPluginUtilsTest, VerifyResourceLanguage_InvalidArgs) {
-  CString args = _T("/en");
-  EXPECT_FAILED(VerifyResourceLanguage(args));
-}
-
-TEST(WebPluginUtilsTest, VerifyResourceLanguage_LangOK) {
-  CString args = _T("/install \"appguid=")
-                 _T("{8A69D345-D564-463c-AFF1-A69D9E530F96}")
-                 _T("&appname=Google Chrome&needsadmin=true&lang=en\"");
-  EXPECT_SUCCEEDED(VerifyResourceLanguage(args));
-}
-
-TEST(WebPluginUtilsTest, VerifyResourceLanguage_LangNotFound) {
-  CString args = _T("/install \"appguid=")
-                 _T("{8A69D345-D564-463c-AFF1-A69D9E530F96}")
-                 _T("&appname=Google Chrome&needsadmin=true&lang=en\"");
-
-  CPath path_orig(app_util::GetCurrentModuleDirectory());
-  path_orig.Append(_T("goopdateres_en.dll"));
-  CPath path_moved(app_util::GetCurrentModuleDirectory());
-  path_moved.Append(_T("goopdateres_en_moved.dll"));
-
-  EXPECT_SUCCEEDED(File::Move(path_orig, path_moved, true));
-  EXPECT_EQ(GOOPDATE_E_ONECLICK_NO_LANGUAGE_RESOURCE,
-            VerifyResourceLanguage(args));
-  EXPECT_SUCCEEDED(File::Move(path_moved, path_orig, true));
-}
-
-}  // namespace webplugin_utils
-
-}  // namespace omaha
-
diff --git a/goopdate/worker.cc b/goopdate/worker.cc
new file mode 100644
index 0000000..69821a6
--- /dev/null
+++ b/goopdate/worker.cc
@@ -0,0 +1,977 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/worker.h"
+#include "omaha/goopdate/worker_internal.h"
+#include <atlbase.h>
+#include <atlstr.h>
+#include "omaha/base/app_util.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/firewall_product_detection.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reactor.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/scoped_impersonation.h"
+#include "omaha/base/system.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/thread_pool_callback.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/app_registry_utils.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/event_logger.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/ping.h"
+#include "omaha/common/update_request.h"
+#include "omaha/common/update_response.h"
+#include "omaha/common/web_services_client.h"
+#include "omaha/goopdate/app_manager.h"
+#include "omaha/goopdate/download_manager.h"
+#include "omaha/goopdate/goopdate.h"
+#include "omaha/goopdate/install_manager.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/offline_utils.h"
+#include "omaha/goopdate/server_resource.h"
+#include "omaha/goopdate/string_formatter.h"
+#include "omaha/goopdate/update_request_utils.h"
+#include "omaha/goopdate/update_response_utils.h"
+#include "omaha/goopdate/worker_metrics.h"
+#include "omaha/goopdate/worker_utils.h"
+
+namespace omaha {
+
+namespace internal {
+
+void RecordUpdateAvailableUsageStats() {
+  AppManager& app_manager = *AppManager::Instance();
+
+  DWORD update_responses(0);
+  DWORD64 time_since_first_response_ms(0);
+  app_manager.ReadUpdateAvailableStats(kGoopdateGuid,
+                                       &update_responses,
+                                       &time_since_first_response_ms);
+  if (update_responses) {
+    metric_worker_self_update_responses = update_responses;
+  }
+  if (time_since_first_response_ms) {
+    metric_worker_self_update_response_time_since_first_ms =
+        time_since_first_response_ms;
+  }
+
+  AppIdVector registered_app_ids;
+  HRESULT hr = app_manager.GetRegisteredApps(&registered_app_ids);
+  if (FAILED(hr)) {
+    ASSERT1(false);
+    return;
+  }
+
+  // These store information about the app with the most update responses.
+  GUID max_responses_app(GUID_NULL);
+  DWORD max_responses(0);
+  DWORD64 max_responses_time_since_first_response_ms(0);
+
+  for (size_t i = 0; i < registered_app_ids.size(); ++i) {
+    const CString& app_id = registered_app_ids[i];
+    GUID app_guid = GUID_NULL;
+
+    if (FAILED(StringToGuidSafe(app_id, &app_guid))) {
+      ASSERT(false, (_T("Invalid App ID: %s"), app_id));
+      continue;
+    }
+
+    if (::IsEqualGUID(kGoopdateGuid, app_guid)) {
+      continue;
+    }
+
+    DWORD update_responses(0);
+    DWORD64 time_since_first_response_ms(0);
+    app_manager.ReadUpdateAvailableStats(app_guid,
+                                         &update_responses,
+                                         &time_since_first_response_ms);
+
+    if (max_responses < update_responses) {
+      max_responses_app = app_guid;
+      max_responses = update_responses;
+      max_responses_time_since_first_response_ms = time_since_first_response_ms;
+    }
+  }
+
+  if (max_responses) {
+    metric_worker_app_max_update_responses_app_high =
+        GetGuidMostSignificantUint64(max_responses_app);
+    metric_worker_app_max_update_responses = max_responses;
+    metric_worker_app_max_update_responses_ms_since_first =
+        max_responses_time_since_first_response_ms;
+  }
+}
+
+// Will block if any apps are being installed.
+HRESULT AddUninstalledAppsPings(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[AddUninstalledAppsPings]")));
+  ASSERT1(app_bundle);
+
+  AppManager& app_manager = *AppManager::Instance();
+
+  // Ensure that no installers are running while determining uninstalled apps
+  // and information about them.
+  __mutexScope(app_manager.GetRegistryStableStateLock());
+
+  AppIdVector uninstalled_app_ids;
+  HRESULT hr = app_manager.GetUninstalledApps(&uninstalled_app_ids);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[GetUninstalledApps failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  if (!uninstalled_app_ids.size()) {
+    return S_OK;
+  }
+
+  for (size_t i = 0; i < uninstalled_app_ids.size(); ++i) {
+    CString app_id = uninstalled_app_ids[i];
+    CORE_LOG(L3, (_T("[found uninstalled product][%s]"), app_id));
+
+    // Omaha uninstall ping is sent by the Uninstall function in setup.
+    if (app_id == kGoogleUpdateAppId) {
+      CORE_LOG(L3, (_T("[skipping Omaha]")));
+      continue;
+    }
+
+    App* app = NULL;
+    hr = app_bundle->CreateUninstalledApp(app_id, &app);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    PingEventPtr ping_event(
+        new PingEvent(PingEvent::EVENT_UNINSTALL,
+                      PingEvent::EVENT_RESULT_SUCCESS,
+                      0,    // error code
+                      0));  // extra code 1
+    app->AddPingEvent(ping_event);
+
+    // TODO(omaha3) It would be nice to call RemoveClientState() only after the
+    // uninstall ping is sent so that the values are not deleted if the ping
+    // fails. However, this would allow race conditions between installers
+    // and the uninstall ping unless app_manager.GetRegistryStableStateLock() is
+    // held from the ping building through the send, which is not currently
+    // feasible since the ping is sent in the AppBundle destructor, and the
+    // AppBundle's lifetime is controlled by the client. Improving the ping
+    // architecture, such as having a ping queue managed by the Worker, may
+    // enable this.
+    VERIFY1(SUCCEEDED(app_manager.RemoveClientState(app->app_guid())));
+  }
+
+  return S_OK;
+}
+
+HRESULT SendOemInstalledPing(bool is_machine, const CString& session_id) {
+  CORE_LOG(L3, (_T("[SendOemInstalledPing]")));
+
+  std::vector<CString> oem_installed_apps;
+  HRESULT hr = AppManager::Instance()->GetOemInstalledAndEulaAcceptedApps(
+      &oem_installed_apps);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  PingEventPtr oem_ping_event(
+      new PingEvent(PingEvent::EVENT_INSTALL_OEM_FIRST_CHECK,
+                    PingEvent::EVENT_RESULT_SUCCESS,
+                    0,
+                    0));
+  Ping ping(is_machine, session_id, CString());
+  ping.LoadAppDataFromRegistry(oem_installed_apps);
+  ping.BuildAppsPing(oem_ping_event);
+  hr = ping.Send(false);
+  if (FAILED(hr)) {
+    CORE_LOG(L3, (_T("[SendOemInstalledPing failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  AppManager::Instance()->ClearOemInstalled(oem_installed_apps);
+
+  return S_OK;
+}
+
+}  // namespace internal
+
+Worker::Worker()
+    : is_machine_(false) {
+  CORE_LOG(L1, (_T("[Worker::Worker]")));
+}
+
+Worker::~Worker() {
+  CORE_LOG(L1, (_T("[Worker::~Worker]")));
+
+  // TODO(omaha3): Remove when Run() is used. See TODO in GoogleUpdate::Main().
+  Stop();
+
+  AppManager::DeleteInstance();
+}
+
+Worker* const Worker::kInvalidInstance = reinterpret_cast<Worker* const>(-1);
+Worker* Worker::instance_              = NULL;
+
+Worker& Worker::Instance() {
+  // Getting the instance after the instance has been deleted is a bug in
+  // the logic of the program.
+  ASSERT1(instance_ != kInvalidInstance);
+  if (!instance_) {
+    instance_ = new Worker();
+  }
+  return *instance_;
+}
+
+int Worker::Lock() {
+  return _pAtlModule->Lock();
+}
+
+int Worker::Unlock() {
+  return _pAtlModule->Unlock();
+}
+
+HRESULT Worker::Initialize(bool is_machine) {
+  CORE_LOG(L1, (_T("[Worker::Initialize][%d]"), is_machine));
+
+  is_machine_ = is_machine;
+
+  reactor_.reset(new Reactor);
+  shutdown_handler_.reset(new ShutdownHandler);
+  model_.reset(new Model(this));
+
+  ASSERT1(!single_instance_.get());
+  NamedObjectAttributes attr;
+  GetNamedObjectAttributes(kGoogleUpdate3SingleInstance, is_machine, &attr);
+  single_instance_.reset(new ProgramInstance(attr.name));
+  if (!single_instance_.get()) {
+    CORE_LOG(LE, (_T("[Failed to create Worker Single Instance]")));
+    return E_OUTOFMEMORY;
+  }
+
+  if (!single_instance_->EnsureSingleInstance()) {
+    CORE_LOG(LW, (_T("[Another Worker instance already running]")));
+    return GOOPDATE_E_INSTANCES_RUNNING;
+  }
+
+  HRESULT hr = AppManager::CreateInstance(is_machine_);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  download_manager_.reset(new DownloadManager(is_machine_));
+
+  hr = download_manager_->Initialize();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  install_manager_.reset(new InstallManager(&model_->lock(), is_machine_));
+  hr = install_manager_->Initialize();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return S_OK;
+}
+
+void Worker::Stop() {
+  // Stop the concurrent objects to avoid spurious events.
+  shutdown_handler_.reset();
+  reactor_.reset();
+
+  // TODO(omaha3): Remove when Run() is used. See TODO in GoogleUpdate::Main().
+  // Until then this call is necessary to wait for threads to complete on
+  // destruction.
+  // TODO(omaha3): Is it correct that the thread pool does not wait for the
+  // threads if !shutdown_event_?
+  Goopdate::Instance().Stop();
+}
+
+HRESULT Worker::Run() {
+  HRESULT hr = DoRun();
+  Stop();
+
+  return hr;
+}
+
+HRESULT Worker::DoRun() {
+  CORE_LOG(L1, (_T("[Worker::DoRun]")));
+
+  HRESULT hr = InitializeShutDownHandler();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return hr;
+}
+
+HRESULT Worker::InitializeShutDownHandler() {
+  CORE_LOG(L3, (_T("[InitializeShutDownHandler]")));
+  return shutdown_handler_->Initialize(reactor_.get(), this, is_machine_);
+}
+
+HRESULT Worker::Shutdown() {
+  CORE_LOG(L2, (_T("[Worker::Shutdown]")));
+  return S_OK;
+}
+
+void Worker::CollectAmbientUsageStats() {
+#if 0
+  // The firewall detection code has been proved to block on some
+  // computers in the IWbemLocator::ConnectServer call even though a
+  // timeout was specified in the call.
+  CString name, version;
+  HRESULT hr = firewall_detection::Detect(&name, &version);
+  bool has_software_firewall = SUCCEEDED(hr) && !name.IsEmpty();
+  metric_worker_has_software_firewall.Set(has_software_firewall);
+#endif
+
+  if (System::IsRunningOnBatteries()) {
+    ++metric_worker_silent_update_running_on_batteries;
+  }
+
+  metric_worker_shell_version = app_util::GetVersionFromModule(NULL);
+
+  metric_worker_is_windows_installing.Set(IsWindowsInstalling());
+  metric_worker_is_uac_disabled.Set(vista_util::IsVistaOrLater() &&
+                                    !vista_util::IsUACMaybeOn());
+  metric_worker_is_clickonce_disabled.Set(IsClickOnceDisabled());
+}
+
+HRESULT Worker::CheckForUpdateAsync(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[Worker::CheckForUpdateAsync][0x%p]"), app_bundle));
+
+  ASSERT1(app_bundle);
+  ASSERT1(model_->IsLockedByCaller());
+
+  HRESULT hr = QueueDeferredFunctionCall0(app_bundle, &Worker::CheckForUpdate);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
+    App* app = app_bundle->GetApp(i);
+    app->QueueUpdateCheck();
+  }
+
+  return S_OK;
+}
+
+void Worker::CheckForUpdate(shared_ptr<AppBundle> app_bundle) {
+  CORE_LOG(L3, (_T("[Worker::CheckForUpdate][0x%p]"), app_bundle.get()));
+  ASSERT1(app_bundle.get());
+
+  bool is_check_successful = false;
+  CheckForUpdateHelper(app_bundle.get(), &is_check_successful);
+
+  app_bundle->CompleteAsyncCall();
+}
+
+// *is_check_successful is true if the network request was successful and the
+// response was successfully parsed. The elements' status need not be success,
+// but an invalid response, such as HTML from a proxy, should result in false.
+// TODO(omaha): Unit test this by mocking update_check_client.
+void Worker::CheckForUpdateHelper(AppBundle* app_bundle,
+                                  bool* is_check_successful) {
+  ASSERT1(app_bundle);
+  ASSERT1(is_check_successful);
+  *is_check_successful = false;
+
+  if (ConfigManager::Instance()->CanUseNetwork(is_machine_)) {
+    VERIFY1(SUCCEEDED(internal::SendOemInstalledPing(
+        is_machine_, app_bundle->session_id())));
+  }
+
+  scoped_impersonation impersonate_user(app_bundle->impersonation_token());
+  HRESULT hr = impersonate_user.result();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr));
+    return;
+  }
+
+  scoped_ptr<xml::UpdateRequest> update_request(
+      xml::UpdateRequest::Create(is_machine_,
+                                 app_bundle->session_id(),
+                                 app_bundle->install_source(),
+                                 app_bundle->origin_url()));
+
+  scoped_ptr<xml::UpdateResponse> update_response(
+      xml::UpdateResponse::Create());
+
+  CallAsSelfAndImpersonate2(this,
+                            &Worker::DoPreUpdateCheck,
+                            app_bundle,
+                            update_request.get());
+
+  // This is a blocking call on the network.
+  hr = DoUpdateCheck(app_bundle,
+                     update_request.get(),
+                     update_response.get());
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[DoUpdateCheck failed][0x%08x]"), hr));
+  }
+  *is_check_successful = SUCCEEDED(hr);
+
+  CallAsSelfAndImpersonate3(this,
+                            &Worker::DoPostUpdateCheck,
+                            app_bundle,
+                            hr,
+                            update_response.get());
+
+  CString event_description;
+  event_description.Format(_T("Update check. Status = 0x%08x"), hr);
+  CString event_text;
+  CString url;
+  VERIFY1(SUCCEEDED(ConfigManager::Instance()->GetUpdateCheckUrl(&url)));
+  SafeCStringFormat(&event_text, _T("url=%s\n%s"),
+                    url,
+                    app_bundle->FetchAndResetLogText());
+
+  WriteEventLog(EVENTLOG_INFORMATION_TYPE,
+                kUpdateEventId,
+                event_description,
+                event_text);
+}
+
+HRESULT Worker::DownloadAsync(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[Worker::DownloadAsync][0x%p]"), app_bundle));
+
+  ASSERT1(app_bundle);
+  ASSERT1(model_->IsLockedByCaller());
+
+  HRESULT hr = QueueDeferredFunctionCall0(app_bundle, &Worker::Download);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
+    App* app = app_bundle->GetApp(i);
+    app->QueueDownload();
+  }
+
+  return S_OK;
+}
+
+void Worker::Download(shared_ptr<AppBundle> app_bundle) {
+  CORE_LOG(L3, (_T("[Worker::Download][0x%p]"), app_bundle.get()));
+  ASSERT1(app_bundle.get());
+
+  scoped_impersonation impersonate_user(app_bundle->impersonation_token());
+  HRESULT hr = impersonate_user.result();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr));
+    return;
+  }
+
+  const size_t num_apps = app_bundle->GetNumberOfApps();
+
+  for (size_t i = 0; i != num_apps; ++i) {
+    App* app = app_bundle->GetApp(i);
+
+    ASSERT1(app->state() == STATE_WAITING_TO_DOWNLOAD ||
+            app->state() == STATE_NO_UPDATE ||
+            app->state() == STATE_ERROR);
+
+    // This is a blocking call on the network.
+    app->Download(download_manager_.get());
+
+    ASSERT1(app->state() == STATE_READY_TO_INSTALL ||
+            app->state() == STATE_NO_UPDATE ||
+            app->state() == STATE_ERROR);
+  }
+
+  WriteEventLog(EVENTLOG_INFORMATION_TYPE,
+                kDownloadEventId,
+                _T("Bundle download"),
+                app_bundle->FetchAndResetLogText());
+  app_bundle->CompleteAsyncCall();
+}
+
+HRESULT Worker::DownloadAndInstallAsync(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[Worker::DownloadAndInstallAsync][0x%p]"), app_bundle));
+
+  ASSERT1(app_bundle);
+  ASSERT1(model_->IsLockedByCaller());
+
+  HRESULT hr = QueueDeferredFunctionCall0(app_bundle,
+                                          &Worker::DownloadAndInstall);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
+    App* app = app_bundle->GetApp(i);
+    // Queue the download or install depending on the current state. The correct
+    // one must be queued so that all apps are moved into waiting now and remain
+    // there until the download or install starts for that app.
+    app->QueueDownloadOrInstall();
+  }
+
+  return S_OK;
+}
+
+void Worker::DownloadAndInstall(shared_ptr<AppBundle> app_bundle) {
+  CORE_LOG(L3, (_T("[Worker::DownloadAndInstall][0x%p]"), app_bundle.get()));
+  ASSERT1(app_bundle.get());
+
+  DownloadAndInstallHelper(app_bundle.get());
+
+  app_bundle->CompleteAsyncCall();
+}
+
+void Worker::DownloadAndInstallHelper(AppBundle* app_bundle) {
+  ASSERT1(app_bundle);
+
+  scoped_impersonation impersonate_user(app_bundle->impersonation_token());
+  HRESULT hr = impersonate_user.result();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr));
+    return;
+  }
+
+  const size_t num_apps = app_bundle->GetNumberOfApps();
+
+  for (size_t i = 0; i != num_apps; ++i) {
+    App* app = app_bundle->GetApp(i);
+
+    ASSERT1(app->state() == STATE_WAITING_TO_DOWNLOAD ||
+            app->state() == STATE_WAITING_TO_INSTALL ||
+            app->state() == STATE_NO_UPDATE ||
+            app->state() == STATE_ERROR);
+
+    // Download the app if it has not already been downloaded.
+    // This is a blocking call on the network.
+    app->Download(download_manager_.get());
+
+    ASSERT1(app->state() == STATE_READY_TO_INSTALL ||    // Downloaded above.
+            app->state() == STATE_WAITING_TO_INSTALL ||  // Downloaded earlier.
+            app->state() == STATE_NO_UPDATE ||
+            app->state() == STATE_ERROR);
+
+    app->QueueInstall();
+
+    // This is a blocking call on the app installer.
+    CallAsSelfAndImpersonate1(
+        app,
+        &App::Install,
+        install_manager_.get());
+
+    ASSERT1(app->state() == STATE_INSTALL_COMPLETE ||
+            app->state() == STATE_NO_UPDATE ||
+            app->state() == STATE_ERROR);
+  }
+
+  WriteEventLog(EVENTLOG_INFORMATION_TYPE,
+                kUpdateEventId,
+                _T("Application update/install"),
+                app_bundle->FetchAndResetLogText());
+}
+
+
+HRESULT Worker::UpdateAllAppsAsync(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[Worker::UpdateAllAppsAsync][0x%p]"), app_bundle));
+
+  ASSERT1(app_bundle);
+  ASSERT1(model_->IsLockedByCaller());
+
+  HRESULT hr = QueueDeferredFunctionCall0(app_bundle, &Worker::UpdateAllApps);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
+    App* app = app_bundle->GetApp(i);
+    app->QueueUpdateCheck();
+  }
+
+  return S_OK;
+}
+
+// Runs through all steps regardless of error. App state machine handles errors.
+void Worker::UpdateAllApps(shared_ptr<AppBundle> app_bundle) {
+  CORE_LOG(L3, (_T("[Worker::UpdateAllApps][0x%p]"), app_bundle.get()));
+  ASSERT1(app_bundle.get());
+
+  bool is_check_successful = false;
+  CheckForUpdateHelper(app_bundle.get(), &is_check_successful);
+
+  if (is_check_successful) {
+    HRESULT hr = goopdate_utils::UpdateLastChecked(is_machine_);
+    ASSERT(SUCCEEDED(hr), (_T("UpdateLastChecked failed with 0x%08x"), hr));
+  }
+
+  for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
+    App* app = app_bundle->GetApp(i);
+    app->QueueDownloadOrInstall();
+  }
+
+  DownloadAndInstallHelper(app_bundle.get());
+
+  internal::RecordUpdateAvailableUsageStats();
+  CollectAmbientUsageStats();
+
+  HRESULT hr = internal::AddUninstalledAppsPings(app_bundle.get());
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[AddUninstalledAppsPings failed][0x%08x]"), hr));
+  }
+
+  app_bundle->CompleteAsyncCall();
+}
+
+HRESULT Worker::DownloadPackageAsync(Package* package) {
+  ASSERT1(package);
+  AppBundle* app_bundle = package->app_version()->app()->app_bundle();
+  ASSERT1(app_bundle);
+
+  ASSERT1(model_->IsLockedByCaller());
+
+  CORE_LOG(L3, (_T("[Worker::DownloadPackageAsync][0x%p][0x%p]"),
+      app_bundle, package));
+
+  HRESULT hr = QueueDeferredFunctionCall1<Package*>(app_bundle,
+                                                    package,
+                                                    &Worker::DownloadPackage);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  App* app = package->app_version()->app();
+  app->QueueDownload();
+
+  return S_OK;
+}
+
+void Worker::DownloadPackage(shared_ptr<AppBundle> app_bundle,
+                             Package* package) {
+  CORE_LOG(L3, (_T("[Worker::DownloadPackage][0x%p][0x%p]"),
+      app_bundle.get(), package));
+  ASSERT1(app_bundle.get());
+  ASSERT1(package);
+
+  scoped_impersonation impersonate_user(app_bundle->impersonation_token());
+  HRESULT hr = impersonate_user.result();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Impersonation failed][0x%08x]"), hr));
+    return;
+  }
+
+  UNREFERENCED_PARAMETER(package);
+
+  // TODO(omaha): implement downloading a package.
+
+  // TODO(omaha): The event log function should be modified depends on
+  // how the download is implemented.
+  //   * whether HRESULT is needed.
+  //   * whether the app_bundle has the log text.
+  //   * additional information needed?
+  CString event_text;
+  SafeCStringFormat(&event_text, _T("Package: %s\n%s"),
+                    package->filename(),
+                    app_bundle->FetchAndResetLogText());
+
+  WriteEventLog(EVENTLOG_INFORMATION_TYPE,
+                kDownloadEventId,
+                _T("Package download"),
+                event_text);
+  app_bundle->CompleteAsyncCall();
+}
+
+// TODO(omaha3): Implement this and enforce the postcondition that the
+// bundle object is not busy before the function returns.
+HRESULT Worker::Stop(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[Worker::Stop][0x%p]"), app_bundle));
+
+  ASSERT1(app_bundle);
+  ASSERT1(model_->IsLockedByCaller());
+
+  // Cancels update check client but not the ping client since we need to send
+  // cancellation ping.
+  WebServicesClientInterface* update_check_client(
+      app_bundle->update_check_client());
+  if (update_check_client) {
+    update_check_client->Cancel();
+  }
+
+  // TODO(omaha3): What do we do with active installs? We can at least cancel
+  // the InstallManager/InstallerWrapper if it has not started.
+
+  // Cancel any apps that might have pending operations.
+  for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
+    App* app = app_bundle->GetApp(i);
+    download_manager_->Cancel(app);
+    app->Cancel();
+  }
+
+  return S_OK;
+}
+
+HRESULT Worker::Pause(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[Worker::Pause][0x%p]"), app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(model_->IsLockedByCaller());
+  UNREFERENCED_PARAMETER(app_bundle);
+
+  return E_NOTIMPL;
+}
+
+HRESULT Worker::Resume(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[Worker::Resume][0x%p]"), app_bundle));
+  ASSERT1(app_bundle);
+  ASSERT1(model_->IsLockedByCaller());
+  UNREFERENCED_PARAMETER(app_bundle);
+
+  return E_NOTIMPL;
+}
+
+HRESULT Worker::GetPackage(const Package* package, const CString& dir) {
+  CORE_LOG(L3, (_T("[Worker::GetPackage]")));
+  ASSERT1(model_->IsLockedByCaller());
+  return download_manager_->GetPackage(package, dir);
+}
+
+bool Worker::IsPackageAvailable(const Package* package) const {
+  CORE_LOG(L3, (_T("[Worker::IsPackageAvailable]")));
+  ASSERT1(model_->IsLockedByCaller());
+  return download_manager_->IsPackageAvailable(package);
+}
+
+HRESULT Worker::CacheOfflinePackages(AppBundle* app_bundle) {
+  CORE_LOG(L3, (_T("[Worker::CacheOfflinePackages]")));
+  ASSERT1(app_bundle);
+
+  for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
+    App* app = app_bundle->GetApp(i);
+    AppVersion* app_version = app->working_version();
+    const size_t num_packages = app_version->GetNumberOfPackages();
+
+    for (size_t i = 0; i < num_packages; ++i) {
+      Package* package(app_version->GetPackage(i));
+      if (download_manager_->IsPackageAvailable(package)) {
+        continue;
+      }
+
+      CString offline_app_dir = ConcatenatePath(app_bundle->offline_dir(),
+                                                app->app_guid_string());
+      CString offline_package_path = ConcatenatePath(offline_app_dir,
+                                                     package->filename());
+      if (!File::Exists(offline_package_path)) {
+        HRESULT hr = offline_utils::FindV2OfflinePackagePath(
+            offline_app_dir, &offline_package_path);
+        if (FAILED(hr)) {
+          CORE_LOG(LE, (_T("[FindOfflinePackagePath failed][0x%x]"), hr));
+          return hr;
+        }
+      }
+
+      HRESULT hr = download_manager_->CachePackage(package,
+                                                   &offline_package_path);
+      if (FAILED(hr)) {
+        CORE_LOG(LE, (_T("[CachePackage failed][%s][%s][0x%x][%Iu]"),
+                      app->app_guid_string(), offline_package_path, hr, i));
+        return hr;
+      }
+    }
+  }
+
+  return S_OK;
+}
+
+HRESULT Worker::PurgeAppLowerVersions(const CString& app_id,
+                                      const CString& version) {
+  return download_manager_->PurgeAppLowerVersions(app_id, version);
+}
+
+// metric_worker_apps_not_*ed_group_policy are integers, not a counter, so they
+// should be set to a value, not incremented. Otherwise the same app could be
+// counted twice if the same COM server instance was used for multiple bundles
+// of the same app(s). For this reason and to avoid overwriting valid counts
+// with 0, the number of disabled apps is accumulated then the appropriate
+// metric is set only if the count is non-zero.
+void Worker::DoPreUpdateCheck(AppBundle* app_bundle,
+                              xml::UpdateRequest* update_request) {
+  ASSERT1(app_bundle);
+  ASSERT1(update_request);
+
+  for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
+    App* app = app_bundle->GetApp(i);
+    app->PreUpdateCheck(update_request);
+  }
+
+  size_t num_disabled_apps = 0;
+  const std::vector<xml::request::App>& apps = update_request->request().apps;
+
+  // apps.size is 0 if update check was cancelled. Its size can also be less
+  // than the number of apps in app_bundle if EULA is not accepted for some
+  // apps.
+  ASSERT1(apps.size() <= app_bundle->GetNumberOfApps());
+
+  for (size_t i = 0; i != apps.size(); ++i) {
+    ASSERT1(apps[i].update_check.is_valid);
+    if (apps[i].update_check.is_update_disabled) {
+      ++num_disabled_apps;
+    }
+  }
+
+  if (num_disabled_apps) {
+    // Assumes that all apps are either updates or installs.
+    if (app_bundle->GetNumberOfApps() && app_bundle->GetApp(0)->is_update()) {
+      metric_worker_apps_not_updated_group_policy = num_disabled_apps;
+    } else {
+      metric_worker_apps_not_installed_group_policy = num_disabled_apps;
+    }
+  }
+}
+
+HRESULT Worker::DoUpdateCheck(AppBundle* app_bundle,
+                              const xml::UpdateRequest* update_request,
+                              xml::UpdateResponse* update_response) {
+  ASSERT1(app_bundle);
+  ASSERT1(update_request);
+  ASSERT1(update_response);
+
+  ASSERT1(app_bundle->GetNumberOfApps() > 0);
+
+  if (app_bundle->is_offline_install()) {
+    return offline_utils::ParseOfflineManifest(
+        app_bundle->GetApp(0)->app_guid_string(), app_bundle->offline_dir(),
+        update_response);
+  }
+
+  if (!ConfigManager::Instance()->CanUseNetwork(is_machine_)) {
+    CORE_LOG(L1, (_T("[Update check failed because network use prohibited]")));
+    return GOOPDATE_E_CANNOT_USE_NETWORK;
+  }
+
+  const bool is_update = app_bundle->is_auto_update();
+
+  if (is_update) {
+    ++metric_worker_update_check_total;
+  }
+
+  // This is a blocking call on the network.
+  HRESULT hr = app_bundle->update_check_client()->Send(update_request,
+                                                       update_response);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Send failed][0x%08x]"), hr));
+    worker_utils::AddHttpRequestDataToEventLog(
+        hr,
+        app_bundle->update_check_client()->http_status_code(),
+        app_bundle->update_check_client()->http_trace(),
+        is_machine_);
+
+    // TODO(omaha3): Omaha 2 would launch a web browser here for installs by
+    // calling goopdate_utils::LaunchBrowser(). Browser launch needs to be in
+    // the client but it currently has no way of knowing that it was a network
+    // error. Even here, we don't know that the it wasn't a parsing, etc. error.
+    return hr;
+  }
+
+  if (is_update) {
+    ++metric_worker_update_check_succeeded;
+  }
+
+  return S_OK;
+}
+
+void Worker::DoPostUpdateCheck(AppBundle* app_bundle,
+                               HRESULT update_check_result,
+                               xml::UpdateResponse* update_response) {
+  ASSERT1(app_bundle);
+  ASSERT1(update_response);
+
+  VERIFY1(SUCCEEDED(update_response_utils::ApplyExperimentLabelDeltas(
+      is_machine_,
+      update_response)));
+
+  for (size_t i = 0; i != app_bundle->GetNumberOfApps(); ++i) {
+    App* app = app_bundle->GetApp(i);
+    app->PostUpdateCheck(update_check_result, update_response);
+
+    ASSERT(app->state() == STATE_UPDATE_AVAILABLE ||
+           app->state() == STATE_NO_UPDATE ||
+           app->state() == STATE_ERROR,
+           (_T("App %Iu state is %u"), i, app->state()));
+  }
+
+  if (app_bundle->is_offline_install()) {
+    VERIFY1(SUCCEEDED(CacheOfflinePackages(app_bundle)));
+    VERIFY1(SUCCEEDED(DeleteDirectory(app_bundle->offline_dir())));
+  }
+}
+
+// Creates a thread pool work item for deferred execution of deferred_function.
+// The thread pool owns this callback object.
+HRESULT Worker::QueueDeferredFunctionCall0(
+    AppBundle* app_bundle,
+    void (Worker::*deferred_function)(shared_ptr<AppBundle>)) {
+  ASSERT1(app_bundle);
+  ASSERT1(deferred_function);
+
+  typedef ThreadPoolCallBack1<Worker, shared_ptr<AppBundle> > Callback;
+  scoped_ptr<Callback> callback(new Callback(this,
+                                             deferred_function,
+                                             app_bundle->controlling_ptr()));
+  HRESULT hr = Goopdate::Instance().QueueUserWorkItem(callback.get(),
+                                                      WT_EXECUTELONGFUNCTION);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Transfers the ownership of the callback from Worker to ThreadPool.
+  app_bundle->set_user_work_item(callback.release());
+  return S_OK;
+}
+
+// Creates a thread pool work item for deferred execution of deferred_function.
+// The thread pool owns this callback object.
+template <typename P1>
+HRESULT Worker::QueueDeferredFunctionCall1(
+    AppBundle* app_bundle,
+    P1 p1,
+    void (Worker::*deferred_function)(shared_ptr<AppBundle>, P1)) {
+  ASSERT1(app_bundle);
+  ASSERT1(deferred_function);
+
+  typedef ThreadPoolCallBack2<Worker, shared_ptr<AppBundle>, P1> Callback;
+  scoped_ptr<Callback> callback(new Callback(this,
+                                             deferred_function,
+                                             app_bundle->controlling_ptr(),
+                                             p1));
+  HRESULT hr = Goopdate::Instance().QueueUserWorkItem(callback.get(),
+                                                      WT_EXECUTELONGFUNCTION);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Transfers the ownership of the callback from Worker to ThreadPool.
+  app_bundle->set_user_work_item(callback.release());
+  return S_OK;
+}
+
+void Worker::WriteEventLog(int event_type,
+                           int event_id,
+                           const CString& event_description,
+                           const CString& event_text) {
+  GoogleUpdateLogEvent log_event(event_type, event_id, is_machine_);
+  log_event.set_event_desc(event_description);
+  log_event.set_event_text(event_text);
+  log_event.WriteEvent();
+}
+
+}  // namespace omaha
diff --git a/goopdate/worker.h b/goopdate/worker.h
new file mode 100644
index 0000000..2bd364f
--- /dev/null
+++ b/goopdate/worker.h
@@ -0,0 +1,219 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_WORKER_H_
+#define OMAHA_GOOPDATE_WORKER_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include "base/basictypes.h"
+#include "omaha/base/program_instance.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/shutdown_callback.h"
+#include "omaha/base/shutdown_handler.h"
+#include "omaha/base/wtl_atlapp_wrapper.h"
+#include "third_party/bar/shared_ptr.h"
+
+namespace omaha {
+
+namespace xml {
+
+class UpdateRequest;
+class UpdateResponse;
+
+}  // namespace xml
+
+class AppBundle;
+class DownloadManagerInterface;
+class InstallManagerInterface;
+class Model;
+class Package;
+class Reactor;
+
+// Limited subset of Worker interface that the Model needs.
+class WorkerModelInterface {
+ public:
+  virtual ~WorkerModelInterface() {}
+  virtual HRESULT CheckForUpdateAsync(AppBundle* app_bundle) = 0;
+  virtual HRESULT DownloadAsync(AppBundle* app_bundle) = 0;
+  virtual HRESULT DownloadAndInstallAsync(AppBundle* app_bundle) = 0;
+  virtual HRESULT UpdateAllAppsAsync(AppBundle* app_bundle) = 0;
+  virtual HRESULT DownloadPackageAsync(Package* package) = 0;
+  virtual HRESULT Stop(AppBundle* app_bundle) = 0;
+  virtual HRESULT Pause(AppBundle* app_bundle) = 0;
+  virtual HRESULT Resume(AppBundle* app_bundle) = 0;
+  virtual HRESULT GetPackage(const Package* package, const CString& dir) = 0;
+  virtual bool IsPackageAvailable(const Package* package) const = 0;
+  virtual HRESULT PurgeAppLowerVersions(const CString& app_id,
+                                        const CString& version) = 0;
+  virtual int Lock() = 0;
+  virtual int Unlock() = 0;
+};
+
+// Worker is a singleton.
+class Worker : public WorkerModelInterface, public ShutdownCallback {
+ public:
+  // Instance, Initialize, and DeleteInstance methods below are not thread safe.
+  // The caller must initialize and cleanup the instance before going
+  // multithreaded.
+
+  // Gets the singleton instance of the class.
+  static Worker& Instance();
+
+  // Initializes the instance.
+  HRESULT Initialize(bool is_machine);
+
+  // Cleans up the class instance.
+  static void DeleteInstance();
+
+  HRESULT Run();
+
+  HRESULT Shutdown();
+  HRESULT InitializeShutDownHandler();
+
+  // TODO(omaha): not clear how to make this an atomic operation. Consider
+  // making the model instance a bare pointer instead of smart pointer.
+  Model* model() { return model_.get(); }
+  const Model* model() const { return model_.get(); }
+
+  // Initiates an update check for all apps in the bundle.
+  virtual HRESULT CheckForUpdateAsync(AppBundle* app_bundle);
+
+  // Initiates download of files necessary to install all apps in the bundle.
+  virtual HRESULT DownloadAsync(AppBundle* app_bundle);
+
+  // Initiates Download, if necessary, and install all app in the bundle.
+  virtual HRESULT DownloadAndInstallAsync(AppBundle* app_bundle);
+
+  // Initiates an update of all registered apps and performs periodic tasks
+  // related to all apps. Primarily for use by Omaha's /ua client. Includes
+  // update check, download and install.
+  virtual HRESULT UpdateAllAppsAsync(AppBundle* app_bundle);
+
+  // Initiates download of a package.
+  virtual HRESULT DownloadPackageAsync(Package* package);
+
+  virtual HRESULT Stop(AppBundle* app_bundle);
+  virtual HRESULT Pause(AppBundle* app_bundle);
+  virtual HRESULT Resume(AppBundle* app_bundle);
+
+  virtual HRESULT GetPackage(const Package*, const CString& dir);
+
+  virtual bool IsPackageAvailable(const Package* package) const;
+
+  virtual HRESULT PurgeAppLowerVersions(const CString& app_id,
+                                        const CString& version);
+
+  // Locks and unlocks the server module by incrementing or decrementing
+  // the lock count of the module.
+  virtual int Lock();
+  virtual int Unlock();
+
+ private:
+  Worker();
+  ~Worker();
+
+  HRESULT DoRun();
+
+  // These functions execute code in the thread pool. They hold an outstanding
+  // reference to the application bundle to prevent the application bundle
+  // object from being deleted before the functions complete.
+  void CheckForUpdate(shared_ptr<AppBundle> app_bundle);
+  void Download(shared_ptr<AppBundle> app_bundle);
+  void DownloadAndInstall(shared_ptr<AppBundle> app_bundle);
+  void DownloadPackage(shared_ptr<AppBundle> app_bundle, Package* package);
+  void UpdateAllApps(shared_ptr<AppBundle> app_bundle);
+
+  // These functions do the work for the corresponding functions but do not call
+  // CompleteAsyncCall().
+  void CheckForUpdateHelper(AppBundle* app_bundle, bool* is_check_successful);
+  void DownloadAndInstallHelper(AppBundle* app_bundle);
+
+  // Stops and destroys the Worker and its members.
+  // TODO(omaha): rename this as it overloads WorkerModelInterface::Stop.
+  void Stop();
+
+  bool EnsureSingleInstance();
+
+  void CollectAmbientUsageStats();
+
+  void DoPreUpdateCheck(AppBundle* app_bundle,
+                        xml::UpdateRequest* update_request);
+
+  HRESULT CacheOfflinePackages(AppBundle* app_bundle);
+
+  HRESULT DoUpdateCheck(AppBundle* app_bundle,
+                        const xml::UpdateRequest* update_request,
+                        xml::UpdateResponse* update_response);
+  void DoPostUpdateCheck(AppBundle* app_bundle,
+                         HRESULT update_check_result,
+                         xml::UpdateResponse* update_response);
+
+  HRESULT QueueDeferredFunctionCall0(
+      AppBundle* app_bundle,
+      void (Worker::*deferred_function)(shared_ptr<AppBundle>));
+
+  template <typename P1>
+  HRESULT QueueDeferredFunctionCall1(
+      AppBundle* app_bundle,
+      P1 p1,
+      void (Worker::*deferred_function)(shared_ptr<AppBundle>, P1));
+
+  void WriteEventLog(int event_type,
+                     int event_id,
+                     const CString& event_description,
+                     const CString& event_text);
+
+  bool is_machine_;
+  scoped_ptr<ProgramInstance> single_instance_;
+  scoped_ptr<Reactor>         reactor_;
+  scoped_ptr<ShutdownHandler> shutdown_handler_;
+  scoped_ptr<Model>           model_;
+  scoped_ptr<DownloadManagerInterface> download_manager_;
+  scoped_ptr<InstallManagerInterface> install_manager_;
+
+  CMessageLoop message_loop_;
+
+  static Worker* const kInvalidInstance;
+  static Worker* instance_;
+
+  friend class WorkerTest;
+
+  DISALLOW_COPY_AND_ASSIGN(Worker);
+};
+
+// For unittests, where creation and termination of Worker instances may be
+// required, the implementation disables the dead reference detection. This
+// forces an inline of the code below for unit tests, so different behavior
+// can be achieved, even though the rest of the implementation compiles in
+// a library. It is somehow brittle but good enough for now.
+
+#ifdef UNITTEST
+__forceinline
+#else
+  inline
+#endif
+void Worker::DeleteInstance() {
+  delete instance_;
+#ifdef UNITTEST
+  instance_ = NULL;
+#else
+  instance_ = kInvalidInstance;
+#endif
+}
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_WORKER_H_
diff --git a/goopdate/worker_internal.h b/goopdate/worker_internal.h
new file mode 100644
index 0000000..fe48e4b
--- /dev/null
+++ b/goopdate/worker_internal.h
@@ -0,0 +1,42 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_WORKER_INTERNAL_H_
+#define OMAHA_GOOPDATE_WORKER_INTERNAL_H_
+
+#include <windows.h>
+
+namespace omaha {
+
+namespace xml {
+
+class UpdateRequest;
+
+}  // namespace xml
+
+namespace internal {
+
+void RecordUpdateAvailableUsageStats();
+
+// Looks for uninstalled apps, adds them to app_bundle and adds an uninstall
+// event to each. The pings will be sent along with other pings when app_bundle
+// is destroyed.
+HRESULT AddUninstalledAppsPings(AppBundle* app_bundle);
+
+}  // namespace internal
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_WORKER_INTERNAL_H_
diff --git a/goopdate/worker_metrics.cc b/goopdate/worker_metrics.cc
new file mode 100644
index 0000000..330925d
--- /dev/null
+++ b/goopdate/worker_metrics.cc
@@ -0,0 +1,77 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include "omaha/goopdate/worker_metrics.h"
+
+namespace omaha {
+
+DEFINE_METRIC_count(worker_download_total);
+DEFINE_METRIC_count(worker_download_succeeded);
+
+DEFINE_METRIC_count(worker_package_cache_put_total);
+DEFINE_METRIC_count(worker_package_cache_put_succeeded);
+
+DEFINE_METRIC_count(worker_install_execute_total);
+DEFINE_METRIC_count(worker_install_execute_msi_total);
+
+DEFINE_METRIC_count(worker_install_msi_in_progress_detected_update);
+DEFINE_METRIC_count(worker_install_msi_in_progress_retry_succeeded_update);
+DEFINE_METRIC_integer(
+    worker_install_msi_in_progress_retry_succeeded_tries_update);
+
+DEFINE_METRIC_count(worker_install_msi_in_progress_detected_install);
+DEFINE_METRIC_count(worker_install_msi_in_progress_retry_succeeded_install);
+DEFINE_METRIC_integer(
+    worker_install_msi_in_progress_retry_succeeded_tries_install);
+
+DEFINE_METRIC_integer(worker_shell_version);
+
+DEFINE_METRIC_bool(worker_is_windows_installing);
+
+DEFINE_METRIC_bool(worker_is_uac_disabled);
+
+DEFINE_METRIC_bool(worker_is_clickonce_disabled);
+
+DEFINE_METRIC_bool(worker_has_software_firewall);
+
+DEFINE_METRIC_count(worker_silent_update_running_on_batteries);
+
+DEFINE_METRIC_count(worker_update_check_total);
+DEFINE_METRIC_count(worker_update_check_succeeded);
+
+DEFINE_METRIC_integer(worker_apps_not_updated_eula);
+DEFINE_METRIC_integer(worker_apps_not_updated_group_policy);
+DEFINE_METRIC_integer(worker_apps_not_installed_group_policy);
+
+DEFINE_METRIC_count(worker_skipped_app_update_for_self_update);
+
+DEFINE_METRIC_count(worker_self_updates_available);
+DEFINE_METRIC_count(worker_self_updates_succeeded);
+
+DEFINE_METRIC_count(worker_app_updates_available);
+DEFINE_METRIC_count(worker_app_updates_succeeded);
+
+DEFINE_METRIC_integer(worker_self_update_responses);
+DEFINE_METRIC_integer(worker_self_update_response_time_since_first_ms);
+
+DEFINE_METRIC_integer(worker_app_max_update_responses_app_high);
+DEFINE_METRIC_integer(worker_app_max_update_responses);
+DEFINE_METRIC_integer(worker_app_max_update_responses_ms_since_first);
+
+DEFINE_METRIC_timing(ping_failed_ms);
+DEFINE_METRIC_timing(ping_succeeded_ms);
+
+}  // namespace omaha
diff --git a/goopdate/worker_metrics.h b/goopdate/worker_metrics.h
new file mode 100644
index 0000000..479665b
--- /dev/null
+++ b/goopdate/worker_metrics.h
@@ -0,0 +1,141 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Declares the usage metrics used by the worker module.
+
+#ifndef OMAHA_GOOPDATE_WORKER_METRICS_H__
+#define OMAHA_GOOPDATE_WORKER_METRICS_H__
+
+#include "omaha/statsreport/metrics.h"
+
+namespace omaha {
+
+// How many times the download manager attempted to download a file.
+DECLARE_METRIC_count(worker_download_total);
+// How many times the download manager successfully downloaded a file.
+DECLARE_METRIC_count(worker_download_succeeded);
+
+// How many times the package cache attempted to put the temporary file
+// to the cache directory.
+DECLARE_METRIC_count(worker_package_cache_put_total);
+// How many times the package cache successfully copied the temporary file
+// to the cache directory.
+DECLARE_METRIC_count(worker_package_cache_put_succeeded);
+
+// How many times ExecuteAndWaitForInstaller was called.
+DECLARE_METRIC_count(worker_install_execute_total);
+// How many times ExecuteAndWaitForInstaller was called for an MSI.
+DECLARE_METRIC_count(worker_install_execute_msi_total);
+
+// How many MSI install attempts encountered ERROR_INSTALL_ALREADY_RUNNING
+// during an update.
+DECLARE_METRIC_count(worker_install_msi_in_progress_detected_update);
+// How many times successfully installed MSI in a retry after
+// encountering ERROR_INSTALL_ALREADY_RUNNING during an update.
+DECLARE_METRIC_count(worker_install_msi_in_progress_retry_succeeded_update);
+// Number of retries attempted because of ERROR_INSTALL_ALREADY_RUNNING before
+// succeeded during an update.
+DECLARE_METRIC_integer(
+    worker_install_msi_in_progress_retry_succeeded_tries_update);
+
+// How many MSI install attempts encountered ERROR_INSTALL_ALREADY_RUNNING
+// during an install.
+DECLARE_METRIC_count(worker_install_msi_in_progress_detected_install);
+// How many times successfully installed MSI in a retry after
+// encountering ERROR_INSTALL_ALREADY_RUNNING during an install.
+DECLARE_METRIC_count(worker_install_msi_in_progress_retry_succeeded_install);
+// Number of retries attempted because of ERROR_INSTALL_ALREADY_RUNNING before
+// succeeded during an install.
+DECLARE_METRIC_integer(
+    worker_install_msi_in_progress_retry_succeeded_tries_install);
+
+// Version of the GoogleUpdate.exe shell in use.
+DECLARE_METRIC_integer(worker_shell_version);
+
+// True if Windows is installing (is in audit mode). This should never be true.
+DECLARE_METRIC_bool(worker_is_windows_installing);
+
+// True if UAC is disabled.
+DECLARE_METRIC_bool(worker_is_uac_disabled);
+
+// True if ClickOnce is disabled for the Internet zone for the current user.
+DECLARE_METRIC_bool(worker_is_clickonce_disabled);
+
+// True if a software firewall is detected.
+DECLARE_METRIC_bool(worker_has_software_firewall);
+
+// How many times the computer was on batteries when doing an update check
+// for apps.
+DECLARE_METRIC_count(worker_silent_update_running_on_batteries);
+
+// How many times an update check was attempted. Does not include installs.
+DECLARE_METRIC_count(worker_update_check_total);
+// How many times an update check succeeded. Does not include installs.
+DECLARE_METRIC_count(worker_update_check_succeeded);
+
+// Number of apps for which update checks skipped because EULA is not accepted.
+DECLARE_METRIC_integer(worker_apps_not_updated_eula);
+// Number of apps for which update checks included updatedisabled because of
+// update Group Policy.
+DECLARE_METRIC_integer(worker_apps_not_updated_group_policy);
+// Number of apps for which update checks included updatedisabled because of
+// install Group Policy.
+DECLARE_METRIC_integer(worker_apps_not_installed_group_policy);
+
+// How many times Omaha did not update an app because an Omaha update was
+// available at the same time. Only incremented if both an Omaha and app update
+// are available in the same update check. Max one increment per update check.
+DECLARE_METRIC_count(worker_skipped_app_update_for_self_update);
+
+// How many times a self update was available.
+// Note: These are updated for all update checks whereas Omaha 2 only updated
+// them for auto-updates.
+DECLARE_METRIC_count(worker_self_updates_available);
+// How many times a self update succeeded.
+DECLARE_METRIC_count(worker_self_updates_succeeded);
+
+// How many updates have been available. Each app in an update check is counted.
+// If a self-update was available, no apps are counted.
+// Updates will be counted even if updates are disabled by Group Policy. This is
+// a change from Omaha 2.
+DECLARE_METRIC_count(worker_app_updates_available);
+// How many app updates succeeded.
+DECLARE_METRIC_count(worker_app_updates_succeeded);
+
+// Number of times Omaha has received a self-update response without
+// successfully updating.
+DECLARE_METRIC_integer(worker_self_update_responses);
+// The time (ms) since the first time Omaha received a self-update response.
+// Only reported if Omaha fails to update after first such response.
+DECLARE_METRIC_integer(worker_self_update_response_time_since_first_ms);
+
+// The most significant/left half of the GUID for the app for which Omaha has
+// received the most update responses without successfully updating.
+DECLARE_METRIC_integer(worker_app_max_update_responses_app_high);
+// Maximum number of times for any app that Omaha has received an update
+// response without successfully updating.
+DECLARE_METRIC_integer(worker_app_max_update_responses);
+// The time (ms) since the first time Omaha received an update response for the
+// app with the most update failures.
+DECLARE_METRIC_integer(worker_app_max_update_responses_ms_since_first);
+
+// Time (ms) spent in SendPing() when DoSendPing() fails.
+DECLARE_METRIC_timing(ping_failed_ms);
+// Time (ms) spent in SendPing() when the ping succeeds.
+DECLARE_METRIC_timing(ping_succeeded_ms);
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_WORKER_METRICS_H__
diff --git a/goopdate/worker_mock.h b/goopdate/worker_mock.h
new file mode 100644
index 0000000..e89b8c4
--- /dev/null
+++ b/goopdate/worker_mock.h
@@ -0,0 +1,57 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_WORKER_MOCK_H_
+#define OMAHA_GOOPDATE_WORKER_MOCK_H_
+
+#include "omaha/goopdate/worker.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+class MockWorker : public WorkerModelInterface {
+ public:
+  MOCK_METHOD1(CheckForUpdateAsync,
+      HRESULT(AppBundle* app_bundle));
+  MOCK_METHOD1(DownloadAsync,
+      HRESULT(AppBundle* app_bundle));
+  MOCK_METHOD1(DownloadAndInstallAsync,
+      HRESULT(AppBundle* app_bundle));
+  MOCK_METHOD1(UpdateAllAppsAsync,
+      HRESULT(AppBundle* app_bundle));
+  MOCK_METHOD1(DownloadPackageAsync,
+      HRESULT(Package* package));
+  MOCK_METHOD1(Stop,
+      HRESULT(AppBundle* app_bundle));
+  MOCK_METHOD1(Pause,
+      HRESULT(AppBundle* app_bundle));
+  MOCK_METHOD1(Resume,
+      HRESULT(AppBundle* app_bundle));
+  MOCK_METHOD2(GetPackage,
+      HRESULT(const Package* package, const CString& dir));
+  MOCK_CONST_METHOD1(IsPackageAvailable,
+      bool(const Package* package));      // NOLINT
+  MOCK_METHOD2(PurgeAppLowerVersions,
+      HRESULT(const CString&, const CString&));
+  MOCK_METHOD0(Lock,
+      int());
+  MOCK_METHOD0(Unlock,
+      int());
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_WORKER_MOCK_H_
+
diff --git a/goopdate/worker_unittest.cc b/goopdate/worker_unittest.cc
new file mode 100644
index 0000000..1e5784a
--- /dev/null
+++ b/goopdate/worker_unittest.cc
@@ -0,0 +1,874 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "base/scoped_ptr.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/web_services_client.h"
+#include "omaha/goopdate/app_bundle_state_busy.h"
+#include "omaha/goopdate/app_manager.h"
+#include "omaha/goopdate/app_state_ready_to_install.h"
+#include "omaha/goopdate/app_state_update_available.h"
+#include "omaha/goopdate/download_manager.h"
+#include "omaha/goopdate/goopdate.h"
+#include "omaha/goopdate/install_manager.h"
+#include "omaha/goopdate/installer_result_info.h"
+#include "omaha/goopdate/model.h"
+#include "omaha/goopdate/resource_manager.h"
+#include "omaha/goopdate/worker.h"
+#include "omaha/goopdate/worker_internal.h"
+#include "omaha/goopdate/worker_metrics.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace {
+
+// TODO(omaha): Change these to invalid IDs once the TODO above
+// UpdateResponse::GetResult is fixed. Until then, invalid IDs cause asserts.
+// Also, the large tests need valid files to download.
+#if 0
+const TCHAR* const kGuid1 = _T("{8A001254-1003-465e-A970-0748961C5293}");
+const TCHAR* const kGuid2 = _T("{058ADDBE-BF10-4ba1-93C0-6F4A52C03C7E}");
+#else
+const TCHAR* const kGuid1 = _T("{ADDE8406-A0F3-4AC2-8878-ADC0BD37BD86}");
+const TCHAR* const kGuid2 = _T("{D0AB2EBC-931B-4013-9FEB-C9C4C2225C8C}");
+#endif
+
+// The alphabetical order of these is important for
+// RecordUpdateAvailableUsageStatsTest.
+const TCHAR* const kApp1 = _T("{0C480772-AC73-418f-9603-66303DA4C7AA}");
+const TCHAR* const kApp2 = _T("{89906BCD-4D12-4c9b-B5BA-8286051CB8D9}");
+const TCHAR* const kApp3 = _T("{F5A1FE97-CF5A-47b8-8B28-2A72F9A57A45}");
+
+const uint64 kApp1GuidUpper = 0x0C480772AC73418f;
+const uint64 kApp2GuidUpper = 0x89906BCD4D124c9b;
+
+const TCHAR* const kApp1ClientsKeyPathUser =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\Clients\\{0C480772-AC73-418f-9603-66303DA4C7AA}");
+const TCHAR* const kApp2ClientsKeyPathUser =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\Clients\\{89906BCD-4D12-4c9b-B5BA-8286051CB8D9}");
+const TCHAR* const kApp3ClientsKeyPathUser =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\Clients\\{F5A1FE97-CF5A-47b8-8B28-2A72F9A57A45}");
+
+const TCHAR* const kApp1ClientStateKeyPathUser =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\{0C480772-AC73-418f-9603-66303DA4C7AA}");
+const TCHAR* const kApp2ClientStateKeyPathUser =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\{89906BCD-4D12-4c9b-B5BA-8286051CB8D9}");
+const TCHAR* const kApp3ClientStateKeyPathUser =
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\{F5A1FE97-CF5A-47b8-8B28-2A72F9A57A45}");
+
+void SetAppStateUpdateAvailable(App* app) {
+  SetAppStateForUnitTest(app, new fsm::AppStateUpdateAvailable);
+}
+
+void SetAppStateReadyToInstall(App* app) {
+  SetAppStateForUnitTest(app, new fsm::AppStateReadyToInstall);
+}
+
+class MockWebServicesClient : public WebServicesClientInterface {
+ public:
+  MOCK_METHOD2(Send,
+      HRESULT(const xml::UpdateRequest* update_request,
+              xml::UpdateResponse* update_response));
+  MOCK_METHOD2(SendString,
+      HRESULT(const CString* request_string,
+              xml::UpdateResponse* update_response));
+  MOCK_METHOD0(Cancel,
+      void());
+  MOCK_METHOD1(set_proxy_auth_config,
+      void(const ProxyAuthConfig& config));
+  MOCK_CONST_METHOD0(is_http_success,
+      bool());
+  MOCK_CONST_METHOD0(http_status_code,
+      int());
+  MOCK_CONST_METHOD0(http_trace,
+      CString());
+};
+
+class MockDownloadManager : public DownloadManagerInterface {
+ public:
+  MOCK_METHOD0(Initialize,
+      HRESULT());
+  MOCK_METHOD2(PurgeAppLowerVersions,
+      HRESULT(const CString&, const CString&));
+  MOCK_METHOD2(CachePackage,
+      HRESULT(const Package*, const CString*));
+  MOCK_METHOD1(DownloadApp,
+      HRESULT(App* app));
+  MOCK_METHOD1(DownloadPackage,
+      HRESULT(Package* package));
+  MOCK_CONST_METHOD2(GetPackage,
+      HRESULT(const Package*, const CString&));
+  MOCK_METHOD1(Cancel,
+      void(App* app));
+  MOCK_METHOD0(CancelAll,
+      void());
+  MOCK_CONST_METHOD0(IsBusy,
+      bool());
+  MOCK_CONST_METHOD1(IsPackageAvailable,
+      bool(const Package* package));      // NOLINT
+};
+
+class MockInstallManager : public InstallManagerInterface {
+ public:
+  MOCK_METHOD0(Initialize,
+      HRESULT());
+  MOCK_CONST_METHOD0(install_working_dir,
+      CString());
+  MOCK_METHOD2(InstallApp,
+      void(App* app, const CString& dir));
+};
+
+ACTION(SimulateDownloadAppStateTransition) {
+  UNREFERENCED_ACTION_PARAMETERS;
+  arg0->Downloading();
+  arg0->DownloadComplete();
+  // TODO(omaha3): Simulate extract and differential update once implemented.
+  arg0->MarkReadyToInstall();
+  return 0;
+}
+
+ACTION(SimulateInstallAppStateTransition) {
+  UNREFERENCED_ACTION_PARAMETERS;
+  arg0->Installing();
+
+  AppManager& app_manager = *AppManager::Instance();
+  __mutexScope(app_manager.GetRegistryStableStateLock());
+
+  InstallerResultInfo result_info;
+  result_info.type = INSTALLER_RESULT_SUCCESS;
+  result_info.text = _T("success");
+  arg0->ReportInstallerComplete(result_info);
+}
+
+void WaitForAppToEnterState(const App& app,
+                            CurrentState expected_state,
+                            int timeout_sec) {
+  const int kPeriodMs = 50;
+  const int max_tries = timeout_sec * 1000 / kPeriodMs;
+  for (int tries = 0; tries < max_tries; ++tries) {
+    if (expected_state == app.state()) {
+        break;
+    }
+    ::Sleep(kPeriodMs);
+  }
+  EXPECT_EQ(expected_state, app.state());
+}
+
+// Assumes the caller has verified the bundle is busy. Otherwise, this could
+// return before the bundle enters the busy state.
+void WaitForBundleToBeReady(const AppBundle& app_bundle, int timeout_sec) {
+  const int kPeriodMs = 50;
+  const int max_tries = timeout_sec * 1000 / kPeriodMs;
+  for (int tries = 0; tries < max_tries; ++tries) {
+    if (!app_bundle.IsBusy()) {
+      return;
+    }
+    ::Sleep(kPeriodMs);
+  }
+  ADD_FAILURE() << _T("Timed out waiting for AppBundle to be ready.");
+}
+
+}  // namespace
+
+// All tests use a user instance of the Worker.
+
+class WorkerTest : public testing::Test {
+ protected:
+  WorkerTest() : is_machine_(false), goopdate_(is_machine_), worker_(NULL) {}
+
+  virtual void SetUp() {
+    worker_ = &Worker::Instance();
+
+    worker_->Initialize(is_machine_);
+
+    EXPECT_SUCCEEDED(ResourceManager::Create(
+      is_machine_, app_util::GetCurrentModuleDirectory(), _T("en")));
+  }
+
+  virtual void TearDown() {
+    worker_ = NULL;
+    Worker::DeleteInstance();
+    ResourceManager::Delete();
+  }
+
+  // Overrides the update check client of the bundle.
+  void SetUpdateCheckClient(AppBundle* app_bundle,
+                            WebServicesClientInterface* web_services_client) {
+    app_bundle->update_check_client_.reset(web_services_client);
+  }
+
+  void SetWorkerDownloadManager(DownloadManagerInterface* download_manager) {
+    ASSERT_TRUE(download_manager);
+    worker_->download_manager_.reset(download_manager);
+  }
+
+  void SetWorkerInstallManager(InstallManagerInterface* install_manager) {
+    ASSERT_TRUE(install_manager);
+    worker_->install_manager_.reset(install_manager);
+  }
+
+  const bool is_machine_;
+  Goopdate goopdate_;
+  Worker* worker_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WorkerTest);
+};
+
+class WorkerWithBundleTest : public WorkerTest {
+ protected:
+  WorkerWithBundleTest() : WorkerTest() {}
+
+  virtual void SetUp() {
+    WorkerTest::SetUp();
+
+    app_bundle_ = worker_->model()->CreateAppBundle(is_machine_);
+    ASSERT_TRUE(app_bundle_.get());
+
+    EXPECT_SUCCEEDED(app_bundle_->put_displayName(CComBSTR(_T("My Bundle"))));
+    EXPECT_SUCCEEDED(app_bundle_->put_displayLanguage(CComBSTR(_T("en"))));
+    EXPECT_SUCCEEDED(app_bundle_->initialize());
+
+    CString update_check_url;
+    ConfigManager::Instance()->GetUpdateCheckUrl(&update_check_url);
+    WebServicesClient* web_service_client = new WebServicesClient(is_machine_);
+    EXPECT_HRESULT_SUCCEEDED(web_service_client->Initialize(update_check_url,
+                                                            HeadersVector(),
+                                                            true));
+    SetUpdateCheckClient(app_bundle_.get(), web_service_client);
+  }
+
+  virtual void TearDown() {
+    app_bundle_.reset();
+
+    WorkerTest::TearDown();
+  }
+
+  shared_ptr<AppBundle> app_bundle_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WorkerWithBundleTest);
+};
+
+// The EULA is accepted for both apps.
+class WorkerWithTwoAppsTest : public WorkerWithBundleTest {
+ protected:
+  WorkerWithTwoAppsTest() : WorkerWithBundleTest(), app1_(NULL), app2_(NULL) {}
+
+  virtual void SetUp() {
+    WorkerWithBundleTest::SetUp();
+
+    EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid1), &app1_));
+    EXPECT_SUCCEEDED(app_bundle_->createApp(CComBSTR(kGuid2), &app2_));
+
+    EXPECT_SUCCEEDED(app1_->put_isEulaAccepted(VARIANT_TRUE));
+    EXPECT_SUCCEEDED(app2_->put_isEulaAccepted(VARIANT_TRUE));
+  }
+
+  virtual void TearDown() {
+    WorkerWithBundleTest::TearDown();
+  }
+
+  App* app1_;
+  App* app2_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WorkerWithTwoAppsTest);
+};
+
+// Mocks the DownloadManager and InstallManager.
+class WorkerMockedManagersTest : public WorkerWithTwoAppsTest {
+ protected:
+  WorkerMockedManagersTest()
+      : WorkerWithTwoAppsTest(),
+        mock_web_services_client_(),
+        mock_download_manager_(NULL),
+        mock_install_manager_(NULL) {
+  }
+
+  virtual void SetUp() {
+    WorkerWithTwoAppsTest::SetUp();
+
+    // By default, no methods should be called on web service client and install
+    // manager, so make them StrictMock. Override this behavior for specific
+    // methods in the individual test cases.
+    mock_web_services_client_ = new testing::StrictMock<MockWebServicesClient>;
+    mock_install_manager_ = new testing::StrictMock<MockInstallManager>;
+
+    // Some functions will be called on the mock download manager, so make it
+    // NiceMock.
+    mock_download_manager_ = new testing::NiceMock<MockDownloadManager>;
+
+    // The App Bundle takes ownership.
+    SetUpdateCheckClient(app_bundle_.get(), mock_web_services_client_);
+
+    // The Worker takes ownership of the Manager mocks.
+    SetWorkerDownloadManager(mock_download_manager_);
+    SetWorkerInstallManager(mock_install_manager_);
+  }
+
+  virtual void TearDown() {
+    WorkerWithTwoAppsTest::TearDown();
+  }
+
+  // Pointers used by tests to set behavior. Not owned by this instance.
+  MockWebServicesClient* mock_web_services_client_;
+  MockDownloadManager* mock_download_manager_;
+  MockInstallManager* mock_install_manager_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WorkerMockedManagersTest);
+};
+
+TEST_F(WorkerMockedManagersTest, CheckForUpdateAsync) {
+  EXPECT_CALL(*mock_web_services_client_, Send(_, _))
+      .Times(1);
+
+  __mutexBlock(worker_->model()->lock()) {
+    EXPECT_SUCCEEDED(worker_->CheckForUpdateAsync(app_bundle_.get()));
+
+    EXPECT_EQ(STATE_WAITING_TO_CHECK_FOR_UPDATE, app1_->state());
+    EXPECT_EQ(STATE_WAITING_TO_CHECK_FOR_UPDATE, app2_->state());
+
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStateBusy);
+    EXPECT_TRUE(app_bundle_->IsBusy());
+  }
+
+  // TODO(omaha3): The update check fails because there is not a real response.
+  // Fix this.
+  WaitForBundleToBeReady(*app_bundle_, 5);
+  EXPECT_FALSE(app_bundle_->IsBusy());
+  EXPECT_EQ(STATE_ERROR, app1_->state());
+  EXPECT_EQ(STATE_ERROR, app2_->state());
+}
+
+TEST_F(WorkerMockedManagersTest, DownloadAsync) {
+  SetAppStateUpdateAvailable(app1_);
+  SetAppStateUpdateAvailable(app2_);
+
+  {
+    ::testing::InSequence dummy;
+    EXPECT_CALL(*mock_download_manager_, DownloadApp(app1_))
+        .WillOnce(SimulateDownloadAppStateTransition());
+    EXPECT_CALL(*mock_download_manager_, DownloadApp(app2_))
+        .WillOnce(SimulateDownloadAppStateTransition());
+  }
+
+  // Holding the lock prevents the state from changing in the other thread,
+  // ensuring consistent results.
+  __mutexBlock(worker_->model()->lock()) {
+    EXPECT_SUCCEEDED(worker_->DownloadAsync(app_bundle_.get()));
+
+    EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app1_->state());
+    EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app2_->state());
+
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStateBusy);
+    EXPECT_TRUE(app_bundle_->IsBusy());
+  }
+
+  WaitForBundleToBeReady(*app_bundle_, 5);
+  EXPECT_EQ(STATE_READY_TO_INSTALL, app1_->state());
+  EXPECT_EQ(STATE_READY_TO_INSTALL, app2_->state());
+}
+
+TEST_F(WorkerMockedManagersTest, DownloadAndInstallAsync_AlreadyDownloaded) {
+  SetAppStateReadyToInstall(app1_);
+  SetAppStateReadyToInstall(app2_);
+
+  EXPECT_CALL(*mock_install_manager_, install_working_dir())
+      .WillRepeatedly(Return(app_util::GetTempDir()));
+
+  {
+    ::testing::InSequence dummy;
+    EXPECT_CALL(*mock_install_manager_, InstallApp(app1_, _))
+        .WillOnce(SimulateInstallAppStateTransition());
+    EXPECT_CALL(*mock_install_manager_, InstallApp(app2_, _))
+        .WillOnce(SimulateInstallAppStateTransition());
+  }
+
+  __mutexBlock(worker_->model()->lock()) {
+    EXPECT_SUCCEEDED(worker_->DownloadAndInstallAsync(app_bundle_.get()));
+
+    EXPECT_EQ(STATE_WAITING_TO_INSTALL, app1_->state());
+    EXPECT_EQ(STATE_WAITING_TO_INSTALL, app2_->state());
+
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStateBusy);
+    EXPECT_TRUE(app_bundle_->IsBusy());
+  }
+
+  WaitForBundleToBeReady(*app_bundle_, 5);
+  EXPECT_EQ(STATE_INSTALL_COMPLETE, app1_->state());
+  EXPECT_EQ(STATE_INSTALL_COMPLETE, app2_->state());
+}
+
+TEST_F(WorkerMockedManagersTest, DownloadAndInstallAsync_NotAlreadyDownloaded) {
+  SetAppStateUpdateAvailable(app1_);
+  SetAppStateUpdateAvailable(app2_);
+
+  EXPECT_CALL(*mock_install_manager_, install_working_dir())
+      .WillRepeatedly(Return(app_util::GetTempDir()));
+
+  {
+    ::testing::InSequence dummy;
+    EXPECT_CALL(*mock_download_manager_, DownloadApp(app1_))
+        .WillOnce(SimulateDownloadAppStateTransition());
+    EXPECT_CALL(*mock_install_manager_, InstallApp(app1_, _))
+        .WillOnce(SimulateInstallAppStateTransition());
+    EXPECT_CALL(*mock_download_manager_, DownloadApp(app2_))
+        .WillOnce(SimulateDownloadAppStateTransition());
+    EXPECT_CALL(*mock_install_manager_, InstallApp(app2_, _))
+        .WillOnce(SimulateInstallAppStateTransition());
+  }
+
+  __mutexBlock(worker_->model()->lock()) {
+    EXPECT_SUCCEEDED(worker_->DownloadAndInstallAsync(app_bundle_.get()));
+
+    EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app1_->state());
+    EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app2_->state());
+
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStateBusy);
+    EXPECT_TRUE(app_bundle_->IsBusy());
+  }
+
+  WaitForBundleToBeReady(*app_bundle_, 5);
+  EXPECT_EQ(STATE_INSTALL_COMPLETE, app1_->state());
+  EXPECT_EQ(STATE_INSTALL_COMPLETE, app2_->state());
+}
+
+TEST_F(WorkerMockedManagersTest, DownloadAsync_Then_DownloadAndInstallAsync) {
+  SetAppStateUpdateAvailable(app1_);
+  SetAppStateUpdateAvailable(app2_);
+
+  {
+    ::testing::InSequence dummy;
+    EXPECT_CALL(*mock_download_manager_, DownloadApp(app1_))
+        .WillOnce(SimulateDownloadAppStateTransition());
+    EXPECT_CALL(*mock_download_manager_, DownloadApp(app2_))
+        .WillOnce(SimulateDownloadAppStateTransition());
+  }
+
+  __mutexBlock(worker_->model()->lock()) {
+    EXPECT_SUCCEEDED(worker_->DownloadAsync(app_bundle_.get()));
+
+    EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app1_->state());
+    EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app2_->state());
+
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStateBusy);
+    EXPECT_TRUE(app_bundle_->IsBusy());
+  }
+
+  WaitForBundleToBeReady(*app_bundle_, 5);
+  EXPECT_EQ(STATE_READY_TO_INSTALL, app1_->state());
+  EXPECT_EQ(STATE_READY_TO_INSTALL, app2_->state());
+
+  EXPECT_CALL(*mock_install_manager_, install_working_dir())
+      .WillRepeatedly(Return(app_util::GetTempDir()));
+
+  {
+    ::testing::InSequence dummy;
+    EXPECT_CALL(*mock_install_manager_, InstallApp(app1_, _))
+        .WillOnce(SimulateInstallAppStateTransition());
+    EXPECT_CALL(*mock_install_manager_, InstallApp(app2_, _))
+        .WillOnce(SimulateInstallAppStateTransition());
+  }
+  __mutexBlock(worker_->model()->lock()) {
+    EXPECT_SUCCEEDED(worker_->DownloadAndInstallAsync(app_bundle_.get()));
+
+    EXPECT_EQ(STATE_WAITING_TO_INSTALL, app1_->state());
+    EXPECT_EQ(STATE_WAITING_TO_INSTALL, app2_->state());
+
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStateBusy);
+    EXPECT_TRUE(app_bundle_->IsBusy());
+  }
+
+  WaitForBundleToBeReady(*app_bundle_, 5);
+  EXPECT_EQ(STATE_INSTALL_COMPLETE, app1_->state());
+  EXPECT_EQ(STATE_INSTALL_COMPLETE, app2_->state());
+}
+
+TEST_F(WorkerMockedManagersTest, UpdateAllAppsAsync) {
+  EXPECT_CALL(*mock_web_services_client_, Send(_, _))
+      .Times(1);
+
+  __mutexBlock(worker_->model()->lock()) {
+    EXPECT_SUCCEEDED(worker_->UpdateAllAppsAsync(app_bundle_.get()));
+
+    EXPECT_EQ(STATE_WAITING_TO_CHECK_FOR_UPDATE, app1_->state());
+    EXPECT_EQ(STATE_WAITING_TO_CHECK_FOR_UPDATE, app2_->state());
+
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStateBusy);
+    EXPECT_TRUE(app_bundle_->IsBusy());
+  }
+
+  // TODO(omaha3): The update check fails because there is not a real response.
+  // Fix this and continue the test, ensuring it gets to install complete.
+  WaitForBundleToBeReady(*app_bundle_, 5);
+  EXPECT_FALSE(app_bundle_->IsBusy());
+  EXPECT_EQ(STATE_ERROR, app1_->state());
+  EXPECT_EQ(STATE_ERROR, app2_->state());
+}
+
+// TODO(omaha): Add tests for app already in error state, app failing download
+// or install, all apps failed or failing, etc.
+
+//
+// Large Tests
+// These are large tests because they use threads and access the network.
+// TODO(omaha): Move these to a separate test executable when using Hammer to
+// run tests.
+//
+
+TEST_F(WorkerWithTwoAppsTest, CheckForUpdateAsync_Large) {
+  __mutexBlock(worker_->model()->lock()) {
+    EXPECT_SUCCEEDED(worker_->CheckForUpdateAsync(app_bundle_.get()));
+
+    EXPECT_EQ(STATE_WAITING_TO_CHECK_FOR_UPDATE, app1_->state());
+    EXPECT_EQ(STATE_WAITING_TO_CHECK_FOR_UPDATE, app2_->state());
+
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStateBusy);
+    EXPECT_TRUE(app_bundle_->IsBusy());
+  }
+
+  WaitForBundleToBeReady(*app_bundle_, 10);
+  EXPECT_EQ(STATE_UPDATE_AVAILABLE, app1_->state());
+  EXPECT_EQ(STATE_UPDATE_AVAILABLE, app2_->state());
+}
+
+TEST_F(WorkerWithTwoAppsTest,
+       DownloadAsyncThenDownloadAndInstallAsync_Large) {
+  // Update Check: Request then wait for it to complete in the thread pool.
+
+  __mutexBlock(worker_->model()->lock()) {
+    EXPECT_SUCCEEDED(worker_->CheckForUpdateAsync(app_bundle_.get()));
+
+    EXPECT_EQ(STATE_WAITING_TO_CHECK_FOR_UPDATE, app1_->state());
+    EXPECT_EQ(STATE_WAITING_TO_CHECK_FOR_UPDATE, app2_->state());
+
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStateBusy);
+    EXPECT_TRUE(app_bundle_->IsBusy());
+  }
+
+  WaitForBundleToBeReady(*app_bundle_, 10);
+  EXPECT_EQ(STATE_UPDATE_AVAILABLE, app1_->state());
+  EXPECT_EQ(STATE_UPDATE_AVAILABLE, app2_->state());
+
+  // Download: Request then wait for it to complete in the thread pool.
+
+  __mutexBlock(worker_->model()->lock()) {
+    EXPECT_SUCCEEDED(worker_->DownloadAsync(app_bundle_.get()));
+
+    EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app1_->state());
+    EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app2_->state());
+
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStateBusy);
+    EXPECT_TRUE(app_bundle_->IsBusy());
+  }
+
+  WaitForBundleToBeReady(*app_bundle_, 25);
+  EXPECT_EQ(STATE_READY_TO_INSTALL, app1_->state());
+  EXPECT_EQ(STATE_READY_TO_INSTALL, app2_->state());
+
+  // Install: Request then wait for it to complete in the thread pool.
+
+  // TODO(omaha): Make User Foo installer available from production, change
+  // GUID(s), and enable the code below. Be sure to uninstall the app when done.
+#if 0
+
+  __mutexBlock(worker_->model()->lock()) {
+    EXPECT_SUCCEEDED(worker_->DownloadAndInstallAsync(app_bundle_.get()));
+
+    EXPECT_EQ(STATE_WAITING_TO_INSTALL, app1_->state());
+    EXPECT_EQ(STATE_WAITING_TO_INSTALL, app2_->state());
+
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStateBusy);
+    EXPECT_TRUE(app_bundle_->IsBusy());
+  }
+
+  WaitForBundleToBeReady(*app_bundle_, 10);
+  EXPECT_EQ(STATE_INSTALL_COMPLETE, app1_->state());
+  EXPECT_EQ(STATE_INSTALL_COMPLETE, app2_->state());
+#endif
+}
+
+TEST_F(WorkerWithTwoAppsTest, DownloadAndInstallAsyncWithoutDownload_Large) {
+  // Update Check: Request then wait for it to complete in the thread pool.
+
+  __mutexBlock(worker_->model()->lock()) {
+    EXPECT_SUCCEEDED(worker_->CheckForUpdateAsync(app_bundle_.get()));
+
+    EXPECT_EQ(STATE_WAITING_TO_CHECK_FOR_UPDATE, app1_->state());
+    EXPECT_EQ(STATE_WAITING_TO_CHECK_FOR_UPDATE, app2_->state());
+
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStateBusy);
+    EXPECT_TRUE(app_bundle_->IsBusy());
+  }
+
+  WaitForBundleToBeReady(*app_bundle_, 10);
+  EXPECT_EQ(STATE_UPDATE_AVAILABLE, app1_->state());
+  EXPECT_EQ(STATE_UPDATE_AVAILABLE, app2_->state());
+
+  // Download and install: Request then wait for it to complete in thread pool.
+
+  // TODO(omaha): Make User Foo installer available from production, change
+  // GUID(s), and enable the code below. Be sure to uninstall the app when done.
+#if 0
+  __mutexBlock(worker_->model()->lock()) {
+    EXPECT_SUCCEEDED(worker_->DownloadAndInstallAsync(app_bundle_.get()));
+
+    EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app1_->state());
+    EXPECT_EQ(STATE_WAITING_TO_DOWNLOAD, app2_->state());
+
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStateBusy);
+    EXPECT_TRUE(app_bundle_->IsBusy());
+  }
+
+  WaitForBundleToBeReady(*app_bundle_, 10);
+  EXPECT_EQ(STATE_INSTALL_COMPLETE, app1_->state());
+  EXPECT_EQ(STATE_INSTALL_COMPLETE, app2_->state());
+#endif
+}
+
+// Also tests cancellation of a bundle during the update check phase.
+TEST_F(WorkerWithTwoAppsTest, UpdateAllAppsAsync_Large) {
+  __mutexBlock(worker_->model()->lock()) {
+    EXPECT_SUCCEEDED(worker_->UpdateAllAppsAsync(app_bundle_.get()));
+
+    EXPECT_EQ(STATE_WAITING_TO_CHECK_FOR_UPDATE, app1_->state());
+    EXPECT_EQ(STATE_WAITING_TO_CHECK_FOR_UPDATE, app2_->state());
+
+    SetAppBundleStateForUnitTest(app_bundle_.get(),
+                                 new fsm::AppBundleStateBusy);
+    EXPECT_TRUE(app_bundle_->IsBusy());
+
+    // Stop the bundle to prevent it from actually trying to update app.
+    EXPECT_SUCCEEDED(app_bundle_->stop());
+  }
+}
+
+class RecordUpdateAvailableUsageStatsTest : public testing::Test {
+ protected:
+  RecordUpdateAvailableUsageStatsTest() : is_machine_(false) {}
+
+  static void SetUpTestCase() {
+    stats_report::g_global_metrics.Initialize();
+  }
+
+  static void TearDownTestCase() {
+    // The global metrics collection must be uninitialized before the metrics
+    // destructors are called.
+    stats_report::g_global_metrics.Uninitialize();
+  }
+
+  virtual void SetUp() {
+    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
+    OverrideRegistryHives(kRegistryHiveOverrideRoot);
+
+    EXPECT_SUCCEEDED(AppManager::CreateInstance(is_machine_));
+
+    metric_worker_self_update_responses.Set(0);
+    metric_worker_self_update_response_time_since_first_ms.Set(0);
+    metric_worker_app_max_update_responses_app_high.Set(0);
+    metric_worker_app_max_update_responses.Set(0);
+    metric_worker_app_max_update_responses_ms_since_first.Set(0);
+
+    ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENTS_GOOPDATE,
+                                      kRegValueProductVersion,
+                                      _T("0.1.0.0")));
+    ASSERT_SUCCEEDED(RegKey::SetValue(kApp1ClientsKeyPathUser,
+                                      kRegValueProductVersion,
+                                      _T("0.1")));
+
+    ASSERT_SUCCEEDED(RegKey::CreateKey(USER_REG_CLIENT_STATE_GOOPDATE));
+    ASSERT_SUCCEEDED(RegKey::CreateKey(kApp1ClientStateKeyPathUser));
+  }
+
+  int GetNumProducts() {
+    AppManager& app_manager = *AppManager::Instance();
+    AppIdVector registered_app_ids;
+    VERIFY1(SUCCEEDED(app_manager.GetRegisteredApps(&registered_app_ids)));
+    return registered_app_ids.size();
+  }
+
+  virtual void TearDown() {
+    AppManager::DeleteInstance();
+
+    RestoreRegistryHives();
+    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
+  }
+
+  bool is_machine_;
+};
+
+TEST_F(RecordUpdateAvailableUsageStatsTest, NoData) {
+  ASSERT_EQ(2, GetNumProducts());
+
+  internal::RecordUpdateAvailableUsageStats();
+
+  EXPECT_EQ(0, metric_worker_self_update_responses.value());
+  EXPECT_EQ(0, metric_worker_self_update_response_time_since_first_ms.value());
+  EXPECT_EQ(0, metric_worker_app_max_update_responses_app_high.value());
+  EXPECT_EQ(0, metric_worker_app_max_update_responses.value());
+  EXPECT_EQ(0, metric_worker_app_max_update_responses_ms_since_first.value());
+}
+
+TEST_F(RecordUpdateAvailableUsageStatsTest, OmahaDataOnly) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                    _T("UpdateAvailableCount"),
+                                    static_cast<DWORD>(123456)));
+  ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                    _T("UpdateAvailableSince"),
+                                    static_cast<DWORD64>(10)));
+
+  ASSERT_EQ(2, GetNumProducts());
+
+  const time64 current_time_100ns(GetCurrent100NSTime());
+  const int64 expected_ms_since_first_update =
+      (current_time_100ns - 10) / kMillisecsTo100ns;
+
+  internal::RecordUpdateAvailableUsageStats();
+
+  EXPECT_EQ(123456, metric_worker_self_update_responses.value());
+
+  EXPECT_LE(expected_ms_since_first_update,
+            metric_worker_self_update_response_time_since_first_ms.value());
+  EXPECT_GT(expected_ms_since_first_update + 10 * kMsPerSec,
+            metric_worker_self_update_response_time_since_first_ms.value());
+
+  EXPECT_EQ(0, metric_worker_app_max_update_responses_app_high.value());
+  EXPECT_EQ(0, metric_worker_app_max_update_responses.value());
+  EXPECT_EQ(0, metric_worker_app_max_update_responses_ms_since_first.value());
+}
+
+TEST_F(RecordUpdateAvailableUsageStatsTest, OneAppOnly) {
+  ASSERT_SUCCEEDED(RegKey::SetValue(kApp1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableCount"),
+                                    static_cast<DWORD>(123456)));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kApp1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableSince"),
+                                    static_cast<DWORD64>(10)));
+
+  ASSERT_EQ(2, GetNumProducts());
+
+  const time64 current_time_100ns(GetCurrent100NSTime());
+  const int64 expected_ms_since_first_update =
+      (current_time_100ns - 10) / kMillisecsTo100ns;
+
+  internal::RecordUpdateAvailableUsageStats();
+
+  EXPECT_EQ(0, metric_worker_self_update_responses.value());
+  EXPECT_EQ(0, metric_worker_self_update_response_time_since_first_ms.value());
+
+  EXPECT_EQ(kApp1GuidUpper,
+            metric_worker_app_max_update_responses_app_high.value());
+  EXPECT_EQ(123456, metric_worker_app_max_update_responses.value());
+  EXPECT_LE(expected_ms_since_first_update,
+            metric_worker_app_max_update_responses_ms_since_first.value());
+  EXPECT_GT(expected_ms_since_first_update + 10 * kMsPerSec,
+            metric_worker_app_max_update_responses_ms_since_first.value());
+}
+
+// It is important that Omaha's count is the largest.
+// All app data should be from app 2, which has the greatest count, a middle
+// time, and an alphabetically middle GUID
+TEST_F(RecordUpdateAvailableUsageStatsTest, OmahaAndSeveralApps) {
+  const DWORD64 kApp2SinceTime = 1000 * kSecsTo100ns;
+
+  ASSERT_SUCCEEDED(RegKey::SetValue(kApp2ClientsKeyPathUser,
+                                    kRegValueProductVersion,
+                                    _T("1.2")));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kApp3ClientsKeyPathUser,
+                                    kRegValueProductVersion,
+                                    _T("2.3")));
+
+  ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                    _T("UpdateAvailableCount"),
+                                    static_cast<DWORD>(0x99887766)));
+  ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
+                                    _T("UpdateAvailableSince"),
+                                    static_cast<DWORD64>(1)));
+
+  ASSERT_SUCCEEDED(RegKey::SetValue(kApp1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableCount"),
+                                    static_cast<DWORD>(1)));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kApp1ClientStateKeyPathUser,
+                                    _T("UpdateAvailableSince"),
+                                    static_cast<DWORD64>(1)));
+
+  ASSERT_SUCCEEDED(RegKey::SetValue(kApp2ClientStateKeyPathUser,
+                                    _T("UpdateAvailableCount"),
+                                    static_cast<DWORD>(9876543)));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kApp2ClientStateKeyPathUser,
+                                    _T("UpdateAvailableSince"),
+                                    kApp2SinceTime));
+
+  ASSERT_SUCCEEDED(RegKey::SetValue(kApp3ClientStateKeyPathUser,
+                                    _T("UpdateAvailableCount"),
+                                    static_cast<DWORD>(234)));
+  ASSERT_SUCCEEDED(RegKey::SetValue(kApp3ClientStateKeyPathUser,
+                                    _T("UpdateAvailableSince"),
+                                    static_cast<DWORD64>(128580000000000000)));
+
+  ASSERT_EQ(4, GetNumProducts());
+
+  const time64 current_time_100ns(GetCurrent100NSTime());
+  const int64 goopdate_expected_ms_since_first_update =
+      (current_time_100ns - 1) / kMillisecsTo100ns;
+
+  const int64 app_expected_ms_since_first_update =
+      (current_time_100ns - kApp2SinceTime) / kMillisecsTo100ns;
+
+  internal::RecordUpdateAvailableUsageStats();
+
+  EXPECT_EQ(0x99887766, metric_worker_self_update_responses.value());
+  EXPECT_LE(goopdate_expected_ms_since_first_update,
+            metric_worker_self_update_response_time_since_first_ms.value());
+  EXPECT_GT(goopdate_expected_ms_since_first_update + 10 * kMsPerSec,
+            metric_worker_self_update_response_time_since_first_ms.value());
+
+  EXPECT_EQ(kApp2GuidUpper,
+            metric_worker_app_max_update_responses_app_high.value());
+  EXPECT_EQ(9876543, metric_worker_app_max_update_responses.value());
+  EXPECT_LE(app_expected_ms_since_first_update,
+            metric_worker_app_max_update_responses_ms_since_first.value());
+  EXPECT_GT(app_expected_ms_since_first_update + 10 * kMsPerSec,
+            metric_worker_app_max_update_responses_ms_since_first.value());
+}
+
+}  // namespace omaha
diff --git a/goopdate/worker_utils.cc b/goopdate/worker_utils.cc
new file mode 100644
index 0000000..8ba153f
--- /dev/null
+++ b/goopdate/worker_utils.cc
@@ -0,0 +1,110 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/goopdate/worker_utils.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/signatures.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/event_logger.h"
+#include "omaha/goopdate/server_resource.h"
+#include "omaha/goopdate/string_formatter.h"
+#include "omaha/net/network_request.h"
+
+namespace omaha {
+
+namespace worker_utils {
+
+bool FormatMessageForNetworkError(HRESULT error,
+                                  const CString& language,
+                                  CString* msg) {
+  ASSERT1(msg);
+  StringFormatter formatter(language);
+
+  switch (error) {
+    case GOOPDATE_E_NO_NETWORK:
+      VERIFY1(SUCCEEDED(formatter.FormatMessage(msg,
+                                                IDS_NO_NETWORK_PRESENT_ERROR,
+                                                kOmahaShellFileName)));
+      break;
+    case GOOPDATE_E_NETWORK_UNAUTHORIZED:
+      VERIFY1(SUCCEEDED(
+          formatter.LoadString(IDS_ERROR_HTTPSTATUS_UNAUTHORIZED, msg)));
+      break;
+    case GOOPDATE_E_NETWORK_FORBIDDEN:
+      VERIFY1(SUCCEEDED(
+          formatter.LoadString(IDS_ERROR_HTTPSTATUS_FORBIDDEN, msg)));
+      break;
+    case GOOPDATE_E_NETWORK_PROXYAUTHREQUIRED:
+      VERIFY1(SUCCEEDED(
+          formatter.LoadString(IDS_ERROR_HTTPSTATUS_PROXY_AUTH_REQUIRED, msg)));
+      break;
+    default:
+      VERIFY1(SUCCEEDED(formatter.FormatMessage(msg,
+                                                IDS_NO_NETWORK_PRESENT_ERROR,
+                                                kOmahaShellFileName)));
+      return false;
+  }
+
+  return true;
+}
+
+void AddHttpRequestDataToEventLog(HRESULT hr,
+                                  int http_status_code,
+                                  const CString& http_trace,
+                                  bool is_machine) {
+  CString msg;
+  SafeCStringFormat(&msg,
+                    _T("Http Request Error.\r\n")
+                    _T("Error: 0x%08x. Http status code: %d.\r\n%s"),
+                    hr,
+                    http_status_code,
+                    http_trace);
+
+  GoogleUpdateLogEvent http_request_event(EVENTLOG_INFORMATION_TYPE,
+                                          kNetworkRequestEventId,
+                                          is_machine);
+  http_request_event.set_event_desc(msg);
+  http_request_event.WriteEvent();
+}
+
+// TODO(omaha): there can be more install actions for each install event.
+// Minor thing: the return value and the out params are redundant, meaning
+// there is no need to have them both. This eliminates an assert at the call
+// site.
+bool GetInstallActionForEvent(
+    const std::vector<xml::InstallAction>& install_actions,
+    xml::InstallAction::InstallEvent install_event,
+    const xml::InstallAction** action) {
+  ASSERT1(action);
+
+  for (size_t i = 0; i < install_actions.size(); ++i) {
+    if (install_actions[i].install_event == install_event) {
+      *action = &install_actions[i];
+      return true;
+    }
+  }
+
+  return false;
+}
+
+}  // namespace worker_utils
+
+}  // namespace omaha
+
diff --git a/goopdate/worker_utils.h b/goopdate/worker_utils.h
new file mode 100644
index 0000000..7c61693
--- /dev/null
+++ b/goopdate/worker_utils.h
@@ -0,0 +1,52 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_GOOPDATE_WORKER_UTILS_H_
+#define OMAHA_GOOPDATE_WORKER_UTILS_H_
+
+#include <windows.h>
+#include <atlstr.h>
+#include <vector>
+#include "omaha/common/install_manifest.h"
+
+namespace omaha {
+
+class NetworkRequest;
+
+namespace worker_utils {
+
+// Formats an error message for network errors. Returns true if the error has
+// a specific message. Otherwise, formats a generic network connection message
+// and returns false.
+bool FormatMessageForNetworkError(HRESULT error,
+                                  const CString& language,
+                                  CString* msg);
+
+// Adds http request details to the event log.
+void AddHttpRequestDataToEventLog(HRESULT hr,
+                                  int http_status_code,
+                                  const CString& http_trace,
+                                  bool is_machine);
+
+bool GetInstallActionForEvent(
+    const std::vector<xml::InstallAction>& install_actions,
+    xml::InstallAction::InstallEvent install_event,
+    const xml::InstallAction** action);
+
+}  // namespace worker_utils
+
+}  // namespace omaha
+
+#endif  // OMAHA_GOOPDATE_WORKER_UTILS_H_
diff --git a/goopdate/worker_utils_unittest.cc b/goopdate/worker_utils_unittest.cc
new file mode 100644
index 0000000..ccaaf4c
--- /dev/null
+++ b/goopdate/worker_utils_unittest.cc
@@ -0,0 +1,87 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <windows.h>
+#include <atlstr.h>
+#include "omaha/base/app_util.h"
+#include "omaha/base/error.h"
+#include "omaha/goopdate/worker_utils.h"
+#include "omaha/goopdate/resource_manager.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace worker_utils {
+
+TEST(WorkerUtilsTest, FormatMessageForNetworkError) {
+  const TCHAR* const kEnglish = _T("en");
+  EXPECT_SUCCEEDED(ResourceManager::Create(
+      false, app_util::GetCurrentModuleDirectory(), kEnglish));
+
+  const TCHAR* const kTestAppName = _T("Test App");
+  CString message;
+  EXPECT_EQ(true, FormatMessageForNetworkError(GOOPDATE_E_NO_NETWORK,
+                                               kEnglish,
+                                               &message));
+  EXPECT_STREQ(
+      _T("The installer could not connect to the Internet. Ensure that your ")
+      _T("computer is connected to the Internet and your firewall allows ")
+      _T("GoogleUpdate.exe to connect then try again."),
+      message);
+
+  EXPECT_EQ(true, FormatMessageForNetworkError(GOOPDATE_E_NETWORK_UNAUTHORIZED,
+                                               kEnglish,
+                                               &message));
+  EXPECT_STREQ(
+      _T("The installer could not connect to the Internet because of ")
+      _T("an HTTP 401 Unauthorized response. This is likely a proxy ")
+      _T("configuration issue. Please configure the proxy server to allow ")
+      _T("network access and try again or contact your network administrator."),
+      message);
+
+  EXPECT_EQ(true, FormatMessageForNetworkError(GOOPDATE_E_NETWORK_FORBIDDEN,
+                                               kEnglish,
+                                               &message));
+  EXPECT_STREQ(
+      _T("The installer could not connect to the Internet because of ")
+      _T("an HTTP 403 Forbidden response. This is likely a proxy ")
+      _T("configuration issue. Please configure the proxy server to allow ")
+      _T("network access and try again or contact your network administrator."),
+      message);
+
+  EXPECT_EQ(true,
+            FormatMessageForNetworkError(GOOPDATE_E_NETWORK_PROXYAUTHREQUIRED,
+                                         kEnglish,
+                                         &message));
+  EXPECT_STREQ(
+      _T("The installer could not connect to the Internet because a ")
+      _T("proxy server required user authentication. Please configure the ")
+      _T("proxy server to allow network access and try again or contact your ")
+      _T("network administrator."),
+      message);
+
+  EXPECT_EQ(false, FormatMessageForNetworkError(E_FAIL, kEnglish, &message));
+  EXPECT_STREQ(
+      _T("The installer could not connect to the Internet. Ensure that your ")
+      _T("computer is connected to the Internet and your firewall allows ")
+      _T("GoogleUpdate.exe to connect then try again."),
+      message);
+
+  ResourceManager::Delete();
+}
+
+}  // namespace job_controller_utils
+
+}  // namespace omaha
diff --git a/hammer.bat b/hammer.bat
index 2900563..7dd816f 100644
--- a/hammer.bat
+++ b/hammer.bat
@@ -1,2 +1,14 @@
-@call %SCT_DIR%\hammer.bat %*
+@echo off
+
+setlocal
+
+set PROXY_CLSID_TARGET=%~dp0proxy_clsids.txt
+set CUSTOMIZATION_UT_TARGET=%~dp0common\omaha_customization_proxy_clsid.h
+
+call %SCT_DIR%\hammer.bat %*
+
+if /i {%1} == {-c} (
+  del /q /f "%PROXY_CLSID_TARGET%" 2> NUL
+  del /q /f "%CUSTOMIZATION_UT_TARGET%" 2> NUL
+)
 
diff --git a/installers/build.scons b/installers/build.scons
index 4491cc4..8300f92 100644
--- a/installers/build.scons
+++ b/installers/build.scons
@@ -25,7 +25,8 @@
 
 from installers import build_metainstaller
 
-_RECOVERY_MARKUP_DLL = 'recovery_markup.dll'
+_RECOVERY_MARKUP_DLL_BASE_NAME = 'recovery_markup'
+_RECOVERY_MARKUP_DLL = _RECOVERY_MARKUP_DLL_BASE_NAME + '.dll'
 
 _CLICKONCE_DEPLOY_DIR = '$TARGET_ROOT/clickonce_deployment'
 
@@ -59,18 +60,32 @@
   )
 
   # Get the localized 'Google Installer' string.
-  mi_generated_res = (
+  mi_generated_resource = (
       '$MAIN_DIR/mi_exe_stub/mi_generated_resources_%s.rc' % language)
-  f_in = codecs.open(env.File(mi_generated_res).abspath, 'r', 'utf16')
+  f_in = codecs.open(env.File(mi_generated_resource).abspath, 'r', 'utf16')
   mi_resource_contents = f_in.read()
   f_in.close()
 
+  # Get and format strings necessary to generate the display name.
   # index() will throw and abort the build if there is no match.
+
+  # First, get the company name.
+  company_name_start = (mi_resource_contents.index('IDS_FRIENDLY_COMPANY_NAME'))
+  company_name_start = mi_resource_contents.index('"', company_name_start)
+  company_name_end = mi_resource_contents.index('"', company_name_start + 1)
+  # Since it is inserted into the display name, the quotes must be dropped.
+  company_name = mi_resource_contents[company_name_start + 1:company_name_end]
+  if -1 != company_name.find('"'):
+    raise Exception('Slice indexes are incorrect!')
+
+  # Now get the installer display name and replace the placeholder with the
+  # company name.
   display_name_start = (
-      mi_resource_contents.index('IDS_GENERIC_INSTALLER_DISPLAY_NAME'))
+      mi_resource_contents.index('IDS_INSTALLER_DISPLAY_NAME'))
   display_name_start = mi_resource_contents.index('"', display_name_start)
   display_name_end = mi_resource_contents.index('"', display_name_start + 1)
   display_name = mi_resource_contents[display_name_start:display_name_end + 1]
+  display_name = display_name.replace('%1!s!', company_name)
 
   # display_name is utf8 encoded to allow the commands and the default codec to
   # pass it through.
@@ -103,7 +118,7 @@
 def _BuildSetup(omaha_versions_info, is_repair = False):
   # Build the meta-installer for each version.
   _PRODUCT_NAME = 'GoogleUpdate'
-  
+
   for omaha_version_info in omaha_versions_info:
     prefix = omaha_version_info.filename_prefix
 
@@ -210,6 +225,6 @@
                   '$MAIN_DIR/recovery/recovery_markup.rc')
       ]
 
-  dll_env.ComponentLibrary(_RECOVERY_MARKUP_DLL, dll_inputs)
+  dll_env.ComponentLibrary(_RECOVERY_MARKUP_DLL_BASE_NAME, dll_inputs)
 
 
diff --git a/installers/build_metainstaller.py b/installers/build_metainstaller.py
index d1509f8..fbd14f2 100644
--- a/installers/build_metainstaller.py
+++ b/installers/build_metainstaller.py
@@ -26,19 +26,21 @@
 """
 
 
-def BuildMetaInstaller(env,
-                       target_name,
-                       omaha_version_info,
-                       empty_metainstaller_path,
-                       omaha_files_path,
-                       prefix='',
-                       suffix='',
-                       additional_payload_contents=None,
-                       additional_payload_contents_dependencies=None,
-                       output_dir='$STAGING_DIR',
-                       installers_sources_path='$MAIN_DIR/installers',
-                       lzma_path='$MAIN_DIR/third_party/lzma/lzma.exe',
-                       resmerge_path='$MAIN_DIR/tools/resmerge.exe'):
+def BuildMetaInstaller(
+    env,
+    target_name,
+    omaha_version_info,
+    empty_metainstaller_path,
+    omaha_files_path,
+    prefix='',
+    suffix='',
+    additional_payload_contents=None,
+    additional_payload_contents_dependencies=None,
+    output_dir='$STAGING_DIR',
+    installers_sources_path='$MAIN_DIR/installers',
+    lzma_path='$MAIN_DIR/third_party/lzma/v4_65/files/lzma.exe',
+    resmerge_path='$MAIN_DIR/tools/resmerge.exe',
+    bcj2_path='$OBJ_ROOT/mi_exe_stub/x86_encoder/bcj2.exe'):
   """Build a meta-installer.
 
     Builds a full meta-installer, which is a meta-installer containing a full
@@ -63,6 +65,7 @@
         for building the metainstaller
     lzma_path: path to lzma.exe
     resmerge_path: path to resmerge.exe
+    bcj2_path: path to bcj2.exe
 
   Returns:
     Target nodes.
@@ -71,12 +74,6 @@
     Nothing.
   """
 
-  if additional_payload_contents is None:
-    additional_payload_contents = []
-
-  if additional_payload_contents_dependencies is None:
-    additional_payload_contents_dependencies = []
-
   # Payload .tar.lzma
   tarball_filename = '%spayload%s.tar' % (prefix, suffix)
   payload_filename = tarball_filename + '.lzma'
@@ -86,7 +83,8 @@
 
   payload_contents = [omaha_files_path + '/' + file_name
                       for file_name in payload_file_names]
-  payload_contents += additional_payload_contents
+  if additional_payload_contents:
+    payload_contents += additional_payload_contents
 
   # Create the tarball
   tarball_output = env.Command(
@@ -97,15 +95,28 @@
   )
 
   # Add potentially hidden dependencies
-  if additional_payload_contents and additional_payload_contents_dependencies:
-    env.Depends(tarball_output, additional_payload_contents)
+  if additional_payload_contents_dependencies:
     env.Depends(tarball_output, additional_payload_contents_dependencies)
 
-  # Compress the tarball
-  lzma_output = env.Command(
-      target=payload_filename,
+  # Preprocess the tarball to increase compressibility
+  bcj_filename = '%spayload%s.tar.bcj' % (prefix, suffix)
+  # TODO(omaha): Add the bcj2 path as an optional parameter.
+  bcj_output = env.Command(
+      target=bcj_filename,
       source=tarball_output,
-      action=lzma_path + ' e $SOURCES $TARGET',
+      action='%s "$SOURCES" "$TARGET"' % bcj2_path,
+  )
+  env.Depends(bcj_output, bcj2_path)
+
+  # Compress the tarball
+  lzma_env = env.Clone()
+  lzma_env.Append(
+      LZMAFLAGS=[],
+  )
+  lzma_output = lzma_env.Command(
+      target=payload_filename,
+      source=bcj_output,
+      action='%s e $SOURCES $TARGET $LZMAFLAGS' % lzma_path,
   )
 
   # Construct the resource generation script
@@ -143,7 +154,7 @@
       ]
 
   dll_output = dll_env.ComponentLibrary(
-      lib_name='%spayload%s.dll' % (prefix, suffix),
+      lib_name='%spayload%s' % (prefix, suffix),
       source=dll_inputs,
   )
 
@@ -151,11 +162,11 @@
   dll_output_name = [f for f in dll_output if f.suffix == '.dll']
 
   # Build the target setup executable by merging the empty metafile
-  # with the resource dll built above
+  # with the resource DLL built above.
   merged_output = env.Command(
       target='unsigned_' + target_name,
       source=[empty_metainstaller_path, dll_output_name],
-      action=resmerge_path + ' --copyappend $SOURCES $TARGET'
+      action= '%s --copyappend $SOURCES $TARGET' % resmerge_path
   )
 
   signed_exe = env.SignedBinary(
diff --git a/installers/get_payload_files.py b/installers/get_payload_files.py
deleted file mode 100644
index 977abe6..0000000
--- a/installers/get_payload_files.py
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/python2.4
-# Copyright 2009 Google Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# ========================================================================
-
-
-def GetListOfPayloadFiles(prefix,
-                          activex_filename,
-                          bho_filename,
-                          languages,
-                          product_version):
-  payload_files = [
-      'GoogleUpdate.exe',
-      'GoogleCrashHandler.exe',
-      '%sgoopdate.dll' % (prefix),
-      '%s%s' % (prefix, activex_filename),  # One-Click DLL
-      '%s%s' % (prefix, bho_filename),      # BHO proxy DLL
-      'GoogleUpdateHelper.msi',
-      ]
-
-  for language in languages:
-    payload_files += ['%sgoopdateres_%s.dll' % (prefix, language)]
-
-  return payload_files
-
-
diff --git a/installers/resource.rc.in b/installers/resource.rc.in
index 3e38f4d..08b7b39 100644
--- a/installers/resource.rc.in
+++ b/installers/resource.rc.in
@@ -16,7 +16,7 @@
 // The __ symbols are replaced by a script that creates the rc file.
 #include "__RESOURCE_FILENAME__"
 
-#include "afxres.h"
+#include <afxres.h>
 
 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
 IDR_PAYLOAD B "__PAYLOAD_FILENAME__"
diff --git a/installers/update_version.py b/installers/update_version.py
deleted file mode 100644
index fe129d6..0000000
--- a/installers/update_version.py
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/usr/bin/python2.4
-# Copyright 2009 Google Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# ========================================================================
-
-
-#
-# Update these values to match the checked in version.
-#
-_LANGUAGES_NOT_IN_BUILD = []
-
-
-# Sets the version and plug-in filenames to the values for the checked in build.
-# Removes languages that are in the current mk_common but not in the checked in
-# build.
-# Returns list of removed languages.
-def UpdateSettingsForCheckedInVersion(env,
-                                      version_major,
-                                      version_minor,
-                                      version_build,
-                                      version_patch,
-                                      oneclick_plugin_version):
-  print
-  print '******* CHANGING VERSION! *******'
-  env.SetProductVersion(
-      version_major, version_minor, version_build, version_patch)
-
-  print
-  print '*********************************'
-  print 'Plug-in version: ' + oneclick_plugin_version
-  print '*********************************'
-  env.SetActiveXFilenames(oneclick_plugin_version)
-
-  for language in _LANGUAGES_NOT_IN_BUILD:
-    if language in env['languages']:
-      env['languages'].remove(language)
-      print '*** Removing language: ' + language
-
-  return _LANGUAGES_NOT_IN_BUILD
-
-
diff --git a/main.scons b/main.scons
index f4825a8..1b2a612 100644
--- a/main.scons
+++ b/main.scons
@@ -51,6 +51,7 @@
 
 import copy
 import os
+import sys
 
 import omaha_version_utils
 
@@ -60,54 +61,84 @@
 # VC2008/VC90 is 1500.
 _msc_ver = 1400
 
-# TODO(Omaha) - Change this to match the official product name
-_PUBLISHER_NAME = 'Google'
+_sdk_path = os.environ['OMAHA_VISTASDK_DIR']
+
+_signtool_path = os.path.join(_sdk_path, 'bin/signtool.exe')
+
+# Build the "Google Update", the Google-specific version of Omaha.
+# TODO(omaha): Set based on an environment variable and/or Hammer arg.
+is_google_update_build = False
+
+#
+# Begin vendor-specific constants.
+#
+# These values must be changed when customizing open source Omaha.
+# You may also wish to replace the language-neutral constants in the individual
+# *generated_resources_*.rc files with localized strings.
+_FULL_COMPANY_NAME = 'OmahaCompanyName Inc.'
+_SHORT_COMPANY_NAME = 'OmahaCompanyName'
 _PRODUCT_NAME = 'Update'
+_COMPANY_DOMAIN_BASE = 'google'
+_COMPANY_DOMAIN = _COMPANY_DOMAIN_BASE + '.com'
+_MAIN_EXE_BASE_NAME = 'GoogleUpdate'
+# TODO(omaha): Use this throughout the build files where goopdate and
+# goopdateres are referenced.
+_MAIN_DLL_BASE_NAME = 'goopdate'
+# "Google Inc." must not be removed from the copyright string. This literal also
+# appears as LegalCopyright in the VERSIONINFO section of .rc files.
+# TODO(omaha): Use this variable in .wxs files, etc.
+_OMAHA_COPYRIGHT_STRING_ENGLISH = 'Copyright 2007-2010 Google Inc.'
+
+
+# TODO(omaha): Allow open source Omaha to be built without the Recovery MSI,
+# which requires an extra certificate file and is unlikely to be used, or
+# ClickOnce, which requires specifying a certificate hash. Disabling Recovery
+# by default also means the open source build will work by default without
+# having to check in SaveArguments.exe to SVN every 100 days.
+# TODO(omaha3): Allow open source Omaha to be built without support for
+# Omaha 2's COM APIs.
 
 # The hash comes from the Thumbprint item in the Details tab of the file
 # properties for the public key .cer file.
 # TODO(omaha): Can we automate reading this and/or pass it on the command line?
-_BUILD_SERVER_CERTIFICATE_HASH = 'fe5008fe0da7a2033816752d6eafe95214f5a7e1'
+_BUILD_SERVER_CERTIFICATE_HASH = 'cafd39335d6e76f0e26d81296e7cbbfbdf16a720'
 
-# COM proxy setting.
-# The proxy clsid needs to be changed anytime the interfaces in
-# google_update_idl.idl change, or when a new interface is added. Use
-# guidgen.exe to generate new clsids in the format 'static const struct GUID'.
-# PROXY_CLSID_IS = {29A96789-9595-4947-BEDB-0FCC776F7DB8}
-_PROXY_CLSID_IS = ('{0x29a96789, 0x9595, 0x4947, '
-    '{0xbe, 0xdb, 0xf, 0xcc, 0x77, 0x6f, 0x7d, 0xb8}}')
+#
+# End vendor-specific constants.
+#
 
 
 # Windows is the only environment we bulid for, so create a specialized base
 # environment for Windows.
-win_env = Environment()
-
-# Make sure we use the Vista SDK.
-# NOTE: This must be set before the 'component_setup' tool is used.
-win_env['PLATFORM_SDK_DIR'] = '$PLATFORM_SDK_VISTA_6_0_DIR'
-
-win_env.Tool('component_setup')
-win_env.Tool('target_platform_windows')
-
-# Need to use 'masm' to override the 'as' tool currently used
-# by default in 'target_platform_windows'
-win_env.Tool('masm')
-
-win_env.Tool('atlmfc_vc80')
-
-# Visual Studio 2008 does not ship the sign tool. Use the sign tool from
-# the Platform SDK.
-win_env.Tool('code_signing')
-_signtool_path = os.path.join(os.environ['OMAHA_VISTASDK_DIR'],
-                             'bin\\signtool.exe')
-win_env['SIGNTOOL'] = '"' + _signtool_path + '"'
+win_env = Environment(
+    # For internal builds only, it is a good idea to have set before the
+    # 'component_setup' tool is used even though we add the SDK directories to
+    # the appropriate environment variables manually below.
+    PLATFORM_SDK_DIR = _sdk_path,
+    tools=[
+        'component_setup',
+        'target_platform_windows',
+        # Need to use 'masm' to override the 'as' tool currently used
+        # by default in 'target_platform_windows'
+        'masm',
+        'atlmfc_vc80',
+        'code_signing',
+        'component_targets_msvs',
+        'omaha_builders',
+    ],
+    msc_ver = _msc_ver,
+    build_server_certificate_hash = _BUILD_SERVER_CERTIFICATE_HASH,
+    # Visual Studio 2008 does not ship the sign tool. Use the sign tool from
+    # the Platform SDK. This must come after the 'code_signing' tool is used.
+    # Remove this if http://code.google.com/p/swtoolkit/issues/detail?id=16 is
+    # fixed.
+    SIGNTOOL = '"' + _signtool_path + '"',
+)
 
 # Remove this value because it conflicts with a #define
 # in shlwapi.h in the Vista SDK
 win_env.FilterOut(CPPDEFINES = ['OS_WINDOWS=OS_WINDOWS'])
 
-win_env.Tool('component_targets_msvs')
-
 # We pre-generate our own manifests, so make sure hammer does not generate
 # default ones for us
 del win_env['MANIFEST_FILE']
@@ -122,10 +153,6 @@
 # Work around http://code.google.com/p/swtoolkit/issues/detail?id=10.
 win_env['COMPONENT_TEST_SUBSYSTEM_WINDOWS'] = 1
 
-win_env['msc_ver'] = _msc_ver;
-
-win_env['build_server_certificate_hash'] = _BUILD_SERVER_CERTIFICATE_HASH
-
 # Declare command line options relating to code signing
 # authenticode_file and authenticode_password are used by the normal signing
 # tool and to sign manifests for ClickOnce.
@@ -166,6 +193,33 @@
     default=None
 )
 
+# Declare option for specifying the set of official app installers to build.
+# The value describes the name of the directory containing the official
+# installer manifests and definitions.
+AddOption(
+    '--official_installer_app',
+    action='store',
+    nargs=1,
+    type='string',
+    default=None
+)
+
+AddOption(
+    '--official_installer_file',
+    action='store',
+    nargs=1,
+    type='string',
+    default=None
+)
+
+AddOption(
+    '--build_number',
+    action='store',
+    nargs=1,
+    type='string',
+    default=''
+)
+
 # Declare various boolean states.
 DeclareBit('use_precompiled_headers', 'Use precompiled headers in build')
 DeclareBit('official_installers', 'Building using checked-in binaries')
@@ -176,6 +230,7 @@
 DeclareBit('min', 'Building minimal set of projects')
 DeclareBit('all', 'Building all Projects')
 DeclareBit('msvs', 'Building Visual Studio solution files')
+DeclareBit('no-tests', 'Do not build the unit tests')
 
 # Build official installers if --official_installers is on the command line.
 win_env.SetBitFromOption('official_installers', False)
@@ -198,6 +253,9 @@
 # Build Visual Studio solution files if --msvs is on the command line.
 win_env.SetBitFromOption('msvs', False)
 
+# Do not build the unit tests if the bit is set.
+win_env.SetBitFromOption('no-tests', False)
+
 # Build all directories and two versions if this is the build server.
 if win_env.Bit('build_server'):
   win_env.SetBits('all')
@@ -243,8 +301,6 @@
 if win_env.Bit('use_precompiled_headers'):
   print 'Using precompiled headers.'
 
-win_env.Tool('omaha_builders')
-
 
 #
 # Set up version info.
@@ -261,8 +317,20 @@
 print 'Building versions: %s' % ', '.join(
     [version_info.GetVersionString() for version_info in omaha_versions_info])
 
+build_number = GetOption('build_number')
+if build_number:
+  print 'Build number: %s' % build_number
+
 win_env['omaha_versions_info'] = omaha_versions_info
 
+if is_google_update_build:
+    win_env.Append(
+        CPPDEFINES = ['GOOGLE_UPDATE_BUILD'],
+        RCFLAGS = ['/DGOOGLE_UPDATE_BUILD=1'],
+    )
+
+# Make sure python.exe can be located.
+win_env.AppendENVPath('PATH', os.environ['OMAHA_PYTHON_DIR'])
 
 win_env.Append(
     # Add windows specific compiler flags.
@@ -315,6 +383,7 @@
         '$MAIN_DIR',
         '$MAIN_DIR/..',
         '$MAIN_DIR/third_party/chrome',
+        '$MAIN_DIR/third_party/gtest/include',
         ],
 
     # Defines for windows environment.
@@ -334,6 +403,7 @@
         '_ATL_NO_PERF_SUPPORT',
         '_ATL_NO_TRACK_HEAP',
         '_ATL_NO_UUIDOF',
+        '_ATL_STATIC_REGISTRY',
         '_CRT_RAND_S',      # rand_s support available in Windows XP only.
         '_WTL_NO_CSTRING',  # WTL uses ATL CString instead.
 
@@ -352,23 +422,35 @@
         # don't define min and max in windef.h
         'NOMINMAX',
 
-        # Logging is enabled in all modes for
-        # diagnostics purposes.
+        # Logging is enabled in all modes for diagnostics purposes.
         'LOGGING',
-        'PUBLISHER_NAME_ANSI=\\"%s\\"' % _PUBLISHER_NAME,
+
+        'FULL_COMPANY_NAME_ANSI=\\"%s\\"' % _FULL_COMPANY_NAME,
+        'SHORT_COMPANY_NAME_ANSI=\\"%s\\"' % _SHORT_COMPANY_NAME,
         'PRODUCT_NAME_ANSI=\\"%s\\"' % _PRODUCT_NAME,
-        # TODO(omaha): Rename to ONECLICK_ACTIVEX_NAME in source files.
-        'ACTIVEX_NAME=_T(\\"%s\\")' % (
-            omaha_version_utils.GetONECLICK_ACTIVEX_NAME()),
-        'PROXY_CLSID_IS=%s' % _PROXY_CLSID_IS,
+        'COMPANY_DOMAIN_BASE_ANSI=\\"%s\\"' % _COMPANY_DOMAIN_BASE,
+        'COMPANY_DOMAIN_ANSI=\\"%s\\"' % _COMPANY_DOMAIN,
+        'OMAHA_APP_NAME_ANSI=\\"%s %s\\"' % (
+            _SHORT_COMPANY_NAME, _PRODUCT_NAME),
+        'MAIN_EXE_BASE_NAME_ANSI=\\"%s\\"' % _MAIN_EXE_BASE_NAME,
+        'MAIN_DLL_BASE_NAME_ANSI=\\"%s\\"' % _MAIN_DLL_BASE_NAME,
         'OFFICIAL_BUILD=%d' % win_env.Bit('build_server'),
         'TEST_CERTIFICATE=%d' % win_env.Bit('test_certificate'),
-        'ACTIVEX_FILENAME=_T(\\"%s\\")' % (
-            omaha_version_info.oneclick_plugin_filename),
-        'ACTIVEX_VERSION_ANSI=\\"%d\\"' % (
+        'ONECLICK_PLUGIN_NAME=_T(\\"%s\\")' % (
+            omaha_version_utils.GetONECLICK_PLUGIN_NAME()),
+        'ONECLICK_PLUGIN_VERSION_ANSI=\\"%d\\"' % (
             omaha_version_info.oneclick_plugin_version),
+        'ONECLICK_PLUGIN_FILENAME=_T(\\"%s\\")' % (
+            omaha_version_info.oneclick_plugin_filename),
+        'UPDATE_PLUGIN_NAME=_T(\\"%s\\")' % (
+            omaha_version_utils.GetUPDATE_PLUGIN_NAME()),
+        'UPDATE_PLUGIN_VERSION_ANSI=\\"%d\\"' % (
+            omaha_version_info.update_plugin_version),
+        'UPDATE_PLUGIN_FILENAME=_T(\\"%s\\")' % (
+            omaha_version_info.update_plugin_filename),
         'BHO_NAME=_T(\\"%s\\")' % omaha_version_utils.GetBHO_NAME(),
         'BHO_FILENAME=_T(\\"%s\\")' % omaha_version_info.bho_filename,
+        'CRASH_HANDLER_NAME=_T(\\"%s\\")' % omaha_version_utils.GetCRASH_HANDLER_NAME(),
         ],
 
     # Link in some windows libraries.
@@ -395,6 +477,9 @@
         '/RELEASE',
         '/MAP',
         '/NODEFAULTLIB',
+        '/DYNAMICBASE',   # Enable ASLR. See http://goo.gl/k2IE.
+        '/NXCOMPAT',      # Enable NX support. See http://goo.gl/k2IE.
+        '/SAFESEH',
         ],
 
     # Shared library specific linker flags.
@@ -405,19 +490,12 @@
         ],
 
     # Resource compiler flags.
+    # Defines in CCFLAGS are automatically included.
     RCFLAGS = [
         '/l 1033',  # /l == default language ID
-        '/DOFFICIAL_BUILD=%d' % win_env.Bit('build_server'),
-        '/DPUBLISHER_NAME_ANSI=\\"%s\\"' % _PUBLISHER_NAME,
-        '/DPRODUCT_NAME_ANSI=\\"%s\\"' % _PRODUCT_NAME,
-        '/DACTIVEX_NAME=_T(\\"%s\\")' % (
-            omaha_version_utils.GetONECLICK_ACTIVEX_NAME()),
-        '/DACTIVEX_FILENAME=_T(\\"%s\\")' % (
-            omaha_version_info.oneclick_plugin_filename),
-        '/DACTIVEX_VERSION_ANSI=\\"%d\\"' % (
-            omaha_version_info.oneclick_plugin_version),
-        '/DBHO_NAME=_T(\\"%s\\")' % omaha_version_utils.GetBHO_NAME(),
-        '/DBHO_FILENAME=_T(\\"%s\\")' % omaha_version_info.bho_filename,
+        '/DBUILD_NUMBER=\\"%s\\"' % build_number,
+        '/DOMAHA_COPYRIGHT_STRING_ENGLISH=\\"%s\\"' % (
+            _OMAHA_COPYRIGHT_STRING_ENGLISH),
         ],
 )
 
@@ -427,11 +505,11 @@
   print 'TEST_CERTIFICATE=%d' % win_env.Bit('test_certificate')
 
 
-# Make sure python.exe can be located.
-win_env.AppendENVPath('PATH', os.environ['OMAHA_PYTHON_DIR'])
+# Add the parent directory of the main omaha directory to the Python path so
+# that we can import using the format "omaha.subdir.module".
+sys.path.append(os.path.split(win_env.Dir('$MAIN_DIR').abspath)[0])
 
 # Make sure Vista SDK in all the important paths earlier in path than VC80.
-_sdk_path = os.environ['OMAHA_VISTASDK_DIR']
 for mid_dir in ['', 'vc']:
   for env_var, sub_dir in [('PATH', 'bin'),
                            ('INCLUDE', 'include'),
@@ -446,73 +524,97 @@
   # Make sure csc.exe can be located.
   win_env.AppendENVPath('PATH', os.environ['OMAHA_NET_DIR'])
 
+  sys.path.append('tools')  # for import proxy_clsid_utils.py
+  import proxy_clsid_utils
+
+  # Generate uniqe proxy CLSIDs for each build.
+  win_env.Execute('python $MAIN_DIR\\tools\\proxy_clsid_utils.py')
+  win_env.Append(
+      CPPDEFINES = [
+        'PROXY_CLSID_IS_MACHINE=%s' % proxy_clsid_utils.GetMachineProxyClsid(),
+        'PROXY_CLSID_IS_USER=%s' % proxy_clsid_utils.GetUserProxyClsid(),
+      ],
+  )
+
 # WiX path has to be added before the WiX tool can be called.
 win_env.AppendENVPath('PATH', os.environ['OMAHA_WIX_DIR'])
 
 win_env.Tool('wix')
 
+
 _base_dirs = [
-    '.',      # ./build.scons is used to copy some files to staging.
+    '.',
+    'base',
+    'clickonce',
+    'client',
     'common',
     'core',
-    'goopdate',
     'google_update',
-    'mi_exe_stub',
+    'goopdate',
     'net',
-    'recovery',
     'service',
     'setup',
     'statsreport',
-    'testing',
     'third_party',
     'tools',
-    'worker',
+    'ui',
     ]
 
 _normal_dirs = [
-    'bho',
-    'clickonce',
     'installers',
+    'mi_exe_stub',
     'plugins',
+    'recovery',
     ]
 
 _official_installers_dirs = [
-    'enterprise',
     'installers',
     ]
 
 _extra_dirs = [
     'enterprise',
     'standalone',
-    'test',
     ]
 
 #
 # Need to decide which subdirs need to be built.
 #
-_dirs_to_build = set()
+_dirs_to_build_set = set()
 
 if win_env.Bit('official_installers'):
   # Only want to build very specific subdirs.
-  _dirs_to_build.update(_official_installers_dirs)
+  win_env.SetBits('no-tests')
+  _dirs_to_build_set.update(_official_installers_dirs)
 elif not win_env.Bit('bin'):
   # All other configs get the base dirs.
-  _dirs_to_build.update(_base_dirs)
+  _dirs_to_build_set.update(_base_dirs)
 
   if win_env.Bit('min'):
     print '*** Building Minimal Set of Projects ***'
   else:
-    _dirs_to_build.update(_normal_dirs)
+    _dirs_to_build_set.update(_normal_dirs)
 
   if win_env.Bit('all'):
-    _dirs_to_build.update(_extra_dirs)
+    _dirs_to_build_set.update(_extra_dirs)
 
 # Build Google application-specific metainstallers.
 if os.path.exists(win_env.Dir('$MAIN_DIR/internal').abspath):
-  _dirs_to_build.update(['internal'])
+  _dirs_to_build_set.update(['internal'])
+
+_dirs_to_build = list(_dirs_to_build_set)
+
+# This must be the last directory.
+if not win_env.Bit('no-tests'):
+  _dirs_to_build.append('testing')
 
 # Instruct Hammer which dirs to build.
-win_env['BUILD_SCONSCRIPTS'] = list(_dirs_to_build)
+win_env['BUILD_SCONSCRIPTS'] = _dirs_to_build
+
+# These are used by the Omaha Builder OmahaUnittest(). They must be added to the
+# environment because there must be one per-mode.
+win_env['all_in_one_unittest_sources'] = []
+win_env['all_in_one_unittest_libs'] = set()
+
 
 # Create the leaf debug Windows environment.
 windows_debug_env = win_env.Clone(
@@ -590,15 +692,17 @@
 
 # Create a target that covers everything in the staging dir, as many of those
 # files will be required for the unittests to run successfully.
+# TODO(omaha3): This may not be necessary when using ComponentTestProgram. If it
+# is, it needs to be changed to use test/ instead of $STAGING_DIR/.
 windows_coverage_env.Alias(
-    'run_omaha_unittest',
+    'run_omaha_unittest_for_coverage',
     '$STAGING_DIR',
     '$STAGING_DIR/omaha_unittest.exe'
 )
 windows_coverage_env.Append(
     # TODO(omaha): We cannot run our unit tests on the new build system. Ensure
     # coverage works with the new test execution system.
-    # COVERAGE_TARGETS=['run_omaha_unittest'],
+    # COVERAGE_TARGETS=['run_omaha_unittest_for_coverage'],
     COVERAGE_INSTRUMENTATION_PATHS=['$STAGING_DIR'],
     # This value should only be used in code if absolutely necessary.
     CPPDEFINES=['COVERAGE_ENABLED'],
@@ -693,6 +797,16 @@
     ]
 )
 
+if 'HAMMER_RUNS_TESTS' in os.environ.keys():
+  # Hammer sets the default target to 'scons-out'. This causes run_* aliases
+  # to also be built, which means the tests run by default. To avoid this, clear
+  # Default and set the default to just build the programs.
+  Default(None)
+  # TODO(omaha): Not all of our core binaries are included in these three
+  # aliases. This is because SignedBinary() and Command() do not add the outputs
+  # to a group. Fix this.
+  Default(['all_programs', 'all_libraries', 'all_test_programs'])
+
 if win_env.Bit('msvs'):
   source_project = win_env.ComponentVSDirProject('all_source', ['$MAIN_DIR'])
 
diff --git a/mi_exe_stub/build.scons b/mi_exe_stub/build.scons
index dc7ba70..3f99023 100644
--- a/mi_exe_stub/build.scons
+++ b/mi_exe_stub/build.scons
@@ -15,46 +15,81 @@
 
 Import('env')
 
-def AddCommonOptions(env):
-  # Make the target compile mbcs by removing the UNICODE define and defining
-  # MBCS instead.
-  env.FilterOut(CPPDEFINES=['UNICODE', '_UNICODE'])
+# Build subdirectories before switching to use WDK
+env.BuildSConscript('x86_encoder')
+
+# Build at warning level 4.
+env.FilterOut(CCFLAGS=['/W3'])
+env.Append(
+    CCFLAGS = [
+        '/W4',
+        '/Wall',
+    ],
+)
+
+def _UseWdkCrt(env):
+  env.Prepend(
+      WDK_PATH = '$GOOGLECLIENT/third_party/windows_driver_kit_vc80/files',
+      CPPPATH = [
+          '$WDK_PATH/inc/api/crt/stl70',
+          '$WDK_PATH/inc/crt',
+          '$WDK_PATH/inc/crt/atl71',
+      ],
+      LIBPATH = [
+          '$WDK_PATH/lib/atl/i386',
+          '$WDK_PATH/lib/crt/i386',
+      ],
+  )
+  # Magic for STL to work. Note that even though we're using ATL, a few things
+  # from STL are still required to correctly build, e.g. the iosfwd header.
   env.Append(
       CPPDEFINES = [
-          '_MBCS',
-          '_ATL_MIN_CRT',
-          '_ATL_SECURE_NO_DEPRECATE',
           '_CRT_SECURE_NO_WARNINGS',
-          ],
+          '_STATIC_CPPLIB',   # Taken from WDK makefile.
+          '_STL70_',          # Taken from WDK makefile.
+          # Prefer ATL CRT replacements to system CRT. This makes a big
+          # difference on calls to CStringT::Format style functions--in testing,
+          # I measured a 5 KiB difference.
+          '_ATL_MIN_CRT',
+          '_ATL_CSTRING_NO_CRT',
+      ],
       CCFLAGS = [
           '/D_SECURE_SCL=0',
-          '/D_SECURE_ATL=0',
-          ],
+          '/EHsc',
+          '/FI$MAIN_DIR/mi_exe_stub/ptrdiff_t.h',
+          '/wd4068',  # unknown pragma (ATL uses PreFAST pragmas)
+      ],
   )
-
+  env.FilterOut(
+      CPPDEFINES = [
+          'NOMINMAX',
+      ],
+      CCFLAGS = [
+          '/D_HAS_EXCEPTIONS=0',
+          '/RTC1',
+      ],
+  )
 
 for omaha_version_info in env['omaha_versions_info']:
   prefix = omaha_version_info.filename_prefix
-
   temp_env = env.Clone()
-  AddCommonOptions(temp_env)
 
   if prefix == 'TEST_':
     temp_env['OBJPREFIX'] = temp_env.subst('test/$OBJPREFIX')
   elif prefix:
     raise Exception('ERROR: Unrecognized prefix "%s"' % prefix)
 
-  # MI stub links with the minicrt.lib in the non-debug builds.
-  # This gets its size down to about 27K and it saves about 40K in library code
   temp_env.Append(
       LIBS = [
-          'lzma.lib',
-          'mi_exe_stub.lib',
-          ('atls.lib',  'atlsd.lib')[temp_env.Bit('debug')],
-          ('minicrt.lib', 'libcmtd.lib')[temp_env.Bit('debug')],
-          ('',          'libcpmtd.lib')[temp_env.Bit('debug')],
-          'shlwapi.lib',
-          'version.lib',
+          'lzma',
+          'mi_exe_stub_lib',
+          ('atls', 'atlsd')[temp_env.Bit('debug')],
+          ('msvcrt', 'libcmtd')[temp_env.Bit('debug')],
+          ('msvcprt', 'libcpmtd')[temp_env.Bit('debug')],
+          # 'msvcprt_btowc.lib',
+          # 'ntstc_msvcrt',  # Only required if STL classes are used.
+          'shlwapi',
+          'version',
           ],
       RCFLAGS = [
           '/DVERSION_MAJOR=%d' % omaha_version_info.version_major,
@@ -75,7 +110,9 @@
 
   exe_inputs = temp_env.RES(
       target=prefix + 'mi.res',
-      source='mi.rc'
+      source=[
+          'mi.rc',
+      ],
   )
 
   # Force a rebuild when the version changes.
@@ -88,22 +125,30 @@
         source=base_name + '.rc',
     )
 
-  temp_env.ComponentProgram(prefix + 'mi_exe_stub.exe', exe_inputs)
+  if not temp_env.Bit('debug'):
+    # Stubs for W2k compatibility.
+    exe_inputs += '$WDK_PATH/lib/w2k/i386/msvcrt_win2000.obj',
+
+  temp_env.ComponentProgram(
+      prog_name = prefix + 'mi_exe_stub',
+      source = exe_inputs,
+  )
 
 
 local_env = env.Clone()
-AddCommonOptions(local_env)
 
 # Avoid target conflicts over extractor.obj
 local_env['OBJSUFFIX'] = '_mi' + local_env['OBJSUFFIX']
 
 local_inputs = [
-    # "../test/step_test.cc",  # This is commented until dependencies of libs
-                                # are sorted out, such as printf.
     'mi.cc',
     'process.cc',
     'tar.cc',
-    '../common/extractor.cc'
-    ]
+    '../base/extractor.cc',
+]
 
-local_env.ComponentLibrary('mi_exe_stub', local_inputs)
+# Precompiled headers cannot be used because debug builds must link against
+# base.lib due to base/ inclusions in precompile.h.
+local_env.ComponentStaticLibrary('mi_exe_stub_lib',
+                                 local_inputs,
+                                 use_pch_default=False)
diff --git a/mi_exe_stub/mi.cc b/mi_exe_stub/mi.cc
index 0f1eab5..474cb99 100644
--- a/mi_exe_stub/mi.cc
+++ b/mi_exe_stub/mi.cc
@@ -20,29 +20,31 @@
 // If found, the contents of the signature tag are also passed to the
 // executable unmodified.
 
-#pragma warning(push)
-// C4548: expression before comma has no effect
-#pragma warning(disable : 4548)
-#include <windows.h>
-#include <atlstr.h>
+#include <tchar.h>
 #include <atlsimpcoll.h>
-#pragma warning(pop)
-#include <msxml2.h>
+#include <atlstr.h>
 #include <shellapi.h>
-#include <algorithm>
+#include <shlwapi.h>
+#include <windows.h>
 
+#pragma warning(push)
+// C4310: cast truncates constant value
+#pragma warning(disable : 4310)
+#include "base/basictypes.h"
+#pragma warning(pop)
 #include "base/scoped_ptr.h"
-#include "omaha/common/constants.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/error.h"
+#include "omaha/base/extractor.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/system_info.h"
 #include "omaha/common/const_cmd_line.h"
-#include "omaha/common/error.h"
-#include "omaha/common/extractor.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/system_info.h"
 #include "omaha/mi_exe_stub/process.h"
 #include "omaha/mi_exe_stub/mi.grh"
 #include "omaha/mi_exe_stub/tar.h"
 extern "C" {
-#include "third_party/lzma/LzmaStateDecode.h"
+#include "third_party/lzma/v4_65/files/C/Bcj2.h"
+#include "third_party/lzma/v4_65/files/C/LzmaDec.h"
 }
 
 namespace omaha  {
@@ -124,32 +126,14 @@
     }
   }
 
-  CString ConvertByteArrayToWideCharArray(const char* input, size_t len) {
-    _ASSERTE(input != NULL);
-    CString out_str;
-    TCHAR* out = out_str.GetBufferSetLength(len);
-    for (size_t i = 0; i < len; ++i) {
-      out[i] = static_cast<TCHAR>(input[i]);
-    }
-    return out_str;
-  }
-
   int ExtractAndRun() {
     if (CreateUniqueTempDirectory() != 0) {
       return -1;
     }
-    CString tarball_filename(ExtractTarballToTempLocation());
-    if (tarball_filename.IsEmpty()) {
+    scoped_hfile tarball_file(ExtractTarballToTempLocation());
+    if (!valid(tarball_file)) {
       return -1;
     }
-    scoped_hfile tarball_file(::CreateFile(tarball_filename,
-                                           GENERIC_READ,
-                                           FILE_SHARE_READ,
-                                           NULL,
-                                           OPEN_EXISTING,
-                                           0,
-                                           NULL));
-    if (!tarball_file) return -1;
 
     // Extract files from the archive and run the first EXE we find in it.
     Tar tar(temp_dir_, get(tarball_file), true);
@@ -175,29 +159,26 @@
       ::PathQuoteSpaces(CStrBuf(command_line, MAX_PATH));
 
       scoped_array<char> tag(GetTag());
-      CString wide_tag;
-      if (tag.get()) {
-        wide_tag = ConvertByteArrayToWideCharArray(tag.get(),
-                                                           strlen(tag.get()));
-      }
-
       if (cmd_line_.IsEmpty()) {
         // Run-by-user case.
-        if (wide_tag.IsEmpty()) {
-          _ASSERTE(!"Must provide arguments with an untagged metainstaller.");
+        if (!tag.get()) {
+          _ASSERTE(!_T("Must provide arguments with untagged metainstaller."));
           HRESULT hr = GOOPDATE_E_UNTAGGED_METAINSTALLER;
           HandleError(hr);
           return hr;
         }
-        command_line.AppendFormat(" /%s", kCmdLineInstall);
+        command_line.AppendFormat(_T(" /%s %s /%s"),
+                                  kCmdLineInstallSource,
+                                  kCmdLineInstallSource_TaggedMetainstaller,
+                                  kCmdLineInstall);
       } else {
-        command_line.AppendFormat(" %s", cmd_line_);
+        command_line.AppendFormat(_T(" %s"), cmd_line_);
 
         CheckAndHandleRecoveryCase(&command_line);
       }
 
-      if (!wide_tag.IsEmpty()) {
-        command_line.AppendFormat(" \"%s\"", wide_tag);
+      if (tag.get()) {
+        command_line.AppendFormat(_T(" \"%s\""), CString(tag.get()));
       }
 
       RunAndWait(command_line, &exit_code_);
@@ -221,7 +202,7 @@
   // Determines whether this is a silent install.
   bool IsSilentInstall() {
     CString silent_argument;
-    silent_argument.Format("/%s", kCmdLineSilent);
+    silent_argument.Format(_T("/%s"), kCmdLineSilent);
 
     return silent_argument == cmd_line_;
   }
@@ -234,12 +215,12 @@
     _ASSERTE(command_line);
 
     CString recover_argument;
-    recover_argument.Format("/%s", kCmdLineRecover);
+    recover_argument.Format(_T("/%s"), kCmdLineRecover);
 
     if (cmd_line_.Left(recover_argument.GetLength()) == recover_argument) {
-      TCHAR current_path[MAX_PATH] = {0};
-      if (::GetModuleFileName(NULL, current_path, MAX_PATH - 1)) {
-        command_line->AppendFormat(" \"%s\"", current_path);
+      TCHAR current_path[MAX_PATH] = {};
+      if (::GetModuleFileName(NULL, current_path, arraysize(current_path))) {
+        command_line->AppendFormat(_T(" \"%s\""), current_path);
       }
     }
   }
@@ -266,12 +247,13 @@
     return 0;
   }
 
-  CString ExtractTarballToTempLocation() {
-    CString tarball_filename;
+  HANDLE ExtractTarballToTempLocation() {
+    HANDLE tarball_file = INVALID_HANDLE_VALUE;
+    TCHAR tarball_filename[MAX_PATH] = {0};
     if (::GetTempFileName(temp_root_dir_,
                           _T("GUT"),
                           0,  // form a unique filename
-                          CStrBuf(tarball_filename, MAX_PATH))) {
+                          tarball_filename)) {
       files_to_delete_.Add(tarball_filename);
       HRSRC res_info = ::FindResource(NULL,
                                       MAKEINTRESOURCE(IDR_PAYLOAD),
@@ -281,33 +263,37 @@
         if (NULL != resource) {
           LPVOID resource_pointer = ::LockResource(resource);
           if (NULL != resource_pointer) {
-            scoped_hfile tarball_file(::CreateFile(tarball_filename,
-                                                   GENERIC_READ | GENERIC_WRITE,
-                                                   0,
-                                                   NULL,
-                                                   OPEN_ALWAYS,
-                                                   0,
-                                                   NULL));
-            if (valid(tarball_file)) {
-              int error = DecompressBufferToFile(
-                              resource_pointer,
-                              ::SizeofResource(NULL, res_info),
-                              get(tarball_file));
-              if (error == 0) {
-                return tarball_filename;
+            tarball_file = ::CreateFile(tarball_filename,
+                                        GENERIC_READ | GENERIC_WRITE,
+                                        0,
+                                        NULL,
+                                        OPEN_ALWAYS,
+                                        0,
+                                        NULL);
+            if (INVALID_HANDLE_VALUE != tarball_file) {
+              LARGE_INTEGER file_position = {};
+              if (0 != DecompressBufferToFile(
+                      static_cast<const uint8*>(resource_pointer),
+                      ::SizeofResource(NULL, res_info),
+                      tarball_file) ||
+                  !::SetFilePointerEx(tarball_file, file_position, NULL,
+                                      FILE_BEGIN)) {
+                ::CloseHandle(tarball_file);
+                tarball_file = INVALID_HANDLE_VALUE;
               }
             }
           }
         }
       }
     }
-    return CString();
+    return tarball_file;
   }
 
   char* GetTag() const {
     // Get this module file name.
-    TCHAR module_file_name[MAX_PATH] = {0};
-    if (!::GetModuleFileName(instance_, module_file_name, MAX_PATH)) {
+    TCHAR module_file_name[MAX_PATH] = {};
+    if (!::GetModuleFileName(instance_, module_file_name,
+                             arraysize(module_file_name))) {
       _ASSERTE(false);
       return NULL;
     }
@@ -315,15 +301,15 @@
     return ExtractTag(module_file_name);
   }
 
-  static CString GetFilespec(const CString &path) {
+  static CString GetFilespec(const CString& path) {
     int pos = path.ReverseFind('\\');
-    if (pos != -1) {
+    if (pos >= 0) {
       return path.Mid(pos + 1);
     }
     return path;
   }
 
-  void HandleTarFile(const char *filename) {
+  void HandleTarFile(const TCHAR* filename) {
     CString new_filename(filename);
     files_to_delete_.Add(new_filename);
     CString filespec(GetFilespec(new_filename));
@@ -332,7 +318,7 @@
     if (filespec.GetLength() > 4) {
       CString extension(filespec.Mid(filespec.GetLength() - 4));
 
-      if (extension == ".exe") {
+      if (extension == _T(".exe")) {
         // We're interested in remembering only the first exe in the tarball.
         if (exe_path_.IsEmpty()) {
           exe_path_ = new_filename;
@@ -341,87 +327,106 @@
     }
   }
 
-  static void TarFileCallback(void *context, const char *filename) {
-    MetaInstaller *mi = reinterpret_cast<MetaInstaller *>(context);
+  static void TarFileCallback(void* context, const TCHAR* filename) {
+    MetaInstaller* mi = reinterpret_cast<MetaInstaller*>(context);
     mi->HandleTarFile(filename);
   }
 
+  // TODO(omaha): reimplement the relevant files in the LZMA SDK to optimize
+  // for size. We'll have to release the modifications (LZMA SDK is CDDL/CDL),
+  // which shouldn't be a problem.
+  static void* MyAlloc(void* p, size_t size) {
+    UNREFERENCED_PARAMETER(p);
+    return new uint8[size];
+  }
+
+  static void MyFree(void* p, void* address) {
+    UNREFERENCED_PARAMETER(p);
+    delete[] address;
+  }
+
   // Decompress the content of the memory buffer into the file
-  // This code stolen from jeanluc in //googleclient/bar
-  static int DecompressBufferToFile(const void *buf,
-                                    size_t buf_len,
+  static int DecompressBufferToFile(const uint8* packed_buffer,
+                                    size_t packed_size,
                                     HANDLE file) {
     // need header and len minimally
-    if (buf_len < LZMA_PROPERTIES_SIZE + 8) {
+    if (packed_size < LZMA_PROPS_SIZE + 8) {
       return -1;
     }
 
-    CLzmaDecoderState decoder = {};
-    const unsigned char *pos = reinterpret_cast<const unsigned char*>(buf);
+    // Note this code won't properly handle decoding large files, since uint32
+    // is used in several places to count size.
+    ISzAlloc allocators = { &MyAlloc, &MyFree };
+    CLzmaDec lzma_state;
+    LzmaDec_Construct(&lzma_state);
+    LzmaDec_Allocate(&lzma_state, packed_buffer, LZMA_PROPS_SIZE, &allocators);
+    LzmaDec_Init(&lzma_state);
+    packed_buffer += LZMA_PROPS_SIZE;
+    packed_size -= LZMA_PROPS_SIZE;
 
-    // get properties
-    int res_info = LzmaDecodeProperties(&decoder.Properties, pos,
-      LZMA_PROPERTIES_SIZE);
-    if (LZMA_RESULT_OK != res_info) {
+    // TODO(omaha): make this independent of endianness.
+    uint64 unpacked_size_64 = *reinterpret_cast<const uint64*>(packed_buffer);
+    size_t unpacked_size = static_cast<size_t>(unpacked_size_64);
+    packed_buffer += sizeof(unpacked_size_64);
+    packed_size -= sizeof(unpacked_size_64);
+
+    scoped_array<uint8> unpacked_buffer(new uint8[unpacked_size]);
+
+    ELzmaStatus status = static_cast<ELzmaStatus>(0);
+    SRes result = LzmaDec_DecodeToBuf(
+        &lzma_state,
+        unpacked_buffer.get(),
+        &unpacked_size,
+        packed_buffer,
+        &packed_size,
+        LZMA_FINISH_END,
+        &status);
+    LzmaDec_Free(&lzma_state, &allocators);
+    if (SZ_OK != result) {
       return -1;
     }
 
-    // advance buffer past header
-    pos += LZMA_PROPERTIES_SIZE;
-    buf_len -= LZMA_PROPERTIES_SIZE;
+#if 0
+    // Reverse BCJ coding.
+    uint32 x86_conversion_state;
+    x86_Convert_Init(x86_conversion_state);
+    x86_Convert(unpacked_buffer.get(), unpacked_size, 0, &x86_conversion_state,
+                0 /* decoding */);
+#else
+    // Reverse BCJ2 coding.
+    const uint8* p = unpacked_buffer.get();
+    uint32 original_size = *reinterpret_cast<const uint32*>(p);
+    p += sizeof(uint32);  // NOLINT
+    uint32 stream0_size = *reinterpret_cast<const uint32*>(p);
+    p += sizeof(uint32);  // NOLINT
+    uint32 stream1_size = *reinterpret_cast<const uint32*>(p);
+    p += sizeof(uint32);  // NOLINT
+    uint32 stream2_size = *reinterpret_cast<const uint32*>(p);
+    p += sizeof(uint32);  // NOLINT
+    uint32 stream3_size = *reinterpret_cast<const uint32*>(p);
+    p += sizeof(uint32);  // NOLINT
 
-    // get the length
-    ULONGLONG size;
-    memcpy(&size, pos, sizeof(size));
-
-    pos += sizeof(size);
-    buf_len -= sizeof(size);
-
-    // allocate the dictionary buffer
-    CAutoVectorPtr<CProb> probs;
-    if (!probs.Allocate(LzmaGetNumProbs(&decoder.Properties))) {
-      return -1;
+    scoped_array<uint8> output_buffer(new uint8[original_size]);
+    if (SZ_OK != Bcj2_Decode(p,
+                             stream0_size,
+                             p + stream0_size,
+                             stream1_size,
+                             p + stream0_size + stream1_size,
+                             stream2_size,
+                             p + stream0_size + stream1_size + stream2_size,
+                             stream3_size,
+                             output_buffer.get(), original_size)) {
+      return 1;
     }
+#endif
 
-    CAutoVectorPtr<unsigned char> dict;
-    if (!dict.Allocate(decoder.Properties.DictionarySize)) {
-      return -1;
-    }
-
-    // and initialize the decoder
-    decoder.Dictionary = dict.m_p;
-    decoder.Probs = probs.m_p;
-
-    LzmaDecoderInit(&decoder);
-
-    while (0 != size || 0 != buf_len) {
-      SizeT in_consumed = 0;
-      SizeT out_produced = 0;
-      unsigned char chunk[8192];
-
-      // extract a chunk - note that the decompresser barfs on us if we
-      // extract too much data from it, so make sure to bound out_len
-      // to the amount of data left.
-      SizeT out_size = std::min(static_cast<SizeT>(size), sizeof(chunk));
-      res_info = LzmaDecode(&decoder, pos, buf_len, &in_consumed, chunk,
-        out_size, &out_produced, buf_len == 0);
-      if (LZMA_RESULT_OK != res_info) {
+    DWORD written;
+    if (!::WriteFile(file, output_buffer.get(), original_size, &written,
+                     NULL) ||
+        written != original_size) {
         return -1;
-      }
-
-      pos += in_consumed;
-      buf_len -= in_consumed;
-      size -= out_produced;
-
-      DWORD written;
-      if (!::WriteFile(file, chunk, out_produced, &written, NULL)) {
-        return -1;
-      }
-
-      if (written != out_produced) {
-        return -1;
-      }
     }
+
     return 0;
   }
 
@@ -439,24 +444,38 @@
                                            GOOPDATE_E_RUNNING_INFERIOR_WINDOWS;
 }
 
-HRESULT HandleError(HRESULT hr) {
-  CString msg_box_title;
+CString GetCompanyDisplayName() {
+  CString company_name;
+  company_name.LoadString(IDS_FRIENDLY_COMPANY_NAME);
+  _ASSERTE(!company_name.IsEmpty());
+  return company_name;
+}
+
+CString GetUiTitle() {
+  CString title;
+  title.FormatMessage(IDS_INSTALLER_DISPLAY_NAME, GetCompanyDisplayName());
+  return title;
+}
+
+HRESULT HandleError(HRESULT result) {
+  _ASSERTE(FAILED(result));
   CString msg_box_text;
 
-  msg_box_title.LoadString(IDS_GENERIC_INSTALLER_DISPLAY_NAME);
-  switch (hr) {
+  switch (result) {
     case GOOPDATE_E_RUNNING_INFERIOR_WINDOWS:
-      msg_box_text.LoadString(IDS_RUNNING_INFERIOR_WINDOWS);
+      msg_box_text.FormatMessage(IDS_RUNNING_INFERIOR_WINDOWS,
+                                 GetCompanyDisplayName());
       break;
 
     case GOOPDATE_E_UNTAGGED_METAINSTALLER:
     default:
       msg_box_text.LoadString(IDS_GENERIC_ERROR);
+      _ASSERTE(!msg_box_text.IsEmpty());
       break;
   }
 
-  ::MessageBox(NULL, msg_box_text, msg_box_title, MB_OK);
-  return hr;
+  ::MessageBox(NULL, msg_box_text, GetUiTitle(), MB_OK);
+  return result;
 }
 
 }  // namespace
diff --git a/mi_exe_stub/mi.grh b/mi_exe_stub/mi.grh
index b9fbdfc..3c40a84 100644
--- a/mi_exe_stub/mi.grh
+++ b/mi_exe_stub/mi.grh
@@ -1,4 +1,4 @@
-// Copyright 2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -13,14 +13,15 @@
 // limitations under the License.
 // ========================================================================
 // This file is automatically generated by GRIT.  Do not edit.
-// Built on Mon Apr 06 11:07:10 2009
+// Built on Thu Nov 18 11:20:39 2010
 
-#ifndef RESOURCE_894478371968__
-#define RESOURCE_894478371968__
+#ifndef RESOURCE_91994245977__
+#define RESOURCE_91994245977__
 
 
-#define IDS_GENERIC_INSTALLER_DISPLAY_NAME 21837
-#define IDS_GENERIC_ERROR 21838
-#define IDS_RUNNING_INFERIOR_WINDOWS 21839
+#define IDS_FRIENDLY_COMPANY_NAME 21128
+#define IDS_INSTALLER_DISPLAY_NAME 21129
+#define IDS_GENERIC_ERROR 21130
+#define IDS_RUNNING_INFERIOR_WINDOWS 21131
 
-#endif // RESOURCE_894478371968__
+#endif // RESOURCE_91994245977__
diff --git a/mi_exe_stub/mi.rc b/mi_exe_stub/mi.rc
index b5354a9..eacfb8f 100644
--- a/mi_exe_stub/mi.rc
+++ b/mi_exe_stub/mi.rc
@@ -12,10 +12,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // ========================================================================
+//
+// This file contains the unlocalized metainstaller resources.
 
 #include <winres.h>
 #include <winresrc.h>
-#include "afxres.h"
+#include <afxres.h>
 #include "resource.h"
 
 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
@@ -23,38 +25,51 @@
 IDI_APP            ICON               "mi.ico"
 
 VS_VERSION_INFO VERSIONINFO
+// Resource Editor does not handle constants from main.scons.
+#ifndef APSTUDIO_INVOKED
  FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_PATCH
  PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_PATCH
- FILEFLAGSMASK 0x17L
-#ifdef _DEBUG
- FILEFLAGS 0x1L
+#endif  // APSTUDIO_INVOKED
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#if defined _DEBUG && OFFICIAL_BUILD
+ FILEFLAGS VS_FF_DEBUG
+#elif defined _DEBUG
+ FILEFLAGS VS_FF_DEBUG | VS_FF_PRIVATEBUILD
+#elif !OFFICIAL_BUILD
+ FILEFLAGS VS_FF_PRIVATEBUILD
 #else
  FILEFLAGS 0x0L
 #endif
- FILEOS 0x4L
- FILETYPE 0x0L
- FILESUBTYPE 0x0L
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE VFT2_UNKNOWN
 BEGIN
     BLOCK "StringFileInfo"
     BEGIN
         BLOCK "040904b0"
         BEGIN
-            VALUE "CompanyName", "Google Inc."
-            VALUE "FileDescription", "Setup"
+// Requires constants from mains.scons that cannot be loaded in Resource Editor.
+#ifndef APSTUDIO_INVOKED
+            VALUE "CompanyName", FULL_COMPANY_NAME_ANSI
+            VALUE "FileDescription", OMAHA_APP_NAME_ANSI " Setup"
             VALUE "FileVersion", VERSION_NUMBER_STRING
-            VALUE "InternalName", "Setup"
-            VALUE "LegalCopyright", "Copyright 2007-2010 Google Inc."
-            VALUE "OriginalFilename", "Setup"
-            VALUE "ProductName", "Setup"
+            VALUE "InternalName", OMAHA_APP_NAME_ANSI " Setup"
+            VALUE "LegalCopyright", OMAHA_COPYRIGHT_STRING_ENGLISH
+            VALUE "OriginalFilename", MAIN_EXE_BASE_NAME_ANSI "Setup.exe"
+            VALUE "ProductName", OMAHA_APP_NAME_ANSI
             VALUE "ProductVersion", VERSION_NUMBER_STRING
             VALUE "LanguageId", LANGUAGE_STRING
-#ifdef _DEBUG
+  #ifdef _DEBUG
             VALUE "Debug", ""
-#endif
-#if !OFFICIAL_BUILD
-            VALUE "Privatebuild", ""
-#endif
-
+  #endif
+  #if !OFFICIAL_BUILD
+            VALUE "PrivateBuild", BUILD_NUMBER
+  #endif
+#else
+            VALUE "_SpecialView",
+                  "Most values are not shown in Resource Editor because they "
+                  "require build file constants."
+#endif  // APSTUDIO_INVOKED
         END
     END
     BLOCK "VarFileInfo"
@@ -63,7 +78,7 @@
     END
 END
 
-// Allow the TB component installer to elevate the Omaha metainstaller.
+// Special external requirement.
 LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
 #define IDR_GOOGLEUPDATE 1
 IDR_GOOGLEUPDATE GOOGLEUPDATE { 1L }
diff --git a/mi_exe_stub/mi_generated_resources_am.rc b/mi_exe_stub/mi_generated_resources_am.rc
new file mode 100644
index 0000000..1a50d6d
--- /dev/null
+++ b/mi_exe_stub/mi_generated_resources_am.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_ar.rc b/mi_exe_stub/mi_generated_resources_ar.rc
index d252123..b6e5977 100644
--- a/mi_exe_stub/mi_generated_resources_ar.rc
+++ b/mi_exe_stub/mi_generated_resources_ar.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_bg.rc b/mi_exe_stub/mi_generated_resources_bg.rc
index 332d92e..7efaa0b 100644
--- a/mi_exe_stub/mi_generated_resources_bg.rc
+++ b/mi_exe_stub/mi_generated_resources_bg.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_bn.rc b/mi_exe_stub/mi_generated_resources_bn.rc
index 5557886..a17746b 100644
--- a/mi_exe_stub/mi_generated_resources_bn.rc
+++ b/mi_exe_stub/mi_generated_resources_bn.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_ca.rc b/mi_exe_stub/mi_generated_resources_ca.rc
index fbd4706..55cac09 100644
--- a/mi_exe_stub/mi_generated_resources_ca.rc
+++ b/mi_exe_stub/mi_generated_resources_ca.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_cs.rc b/mi_exe_stub/mi_generated_resources_cs.rc
index 33b381a..1b55104 100644
--- a/mi_exe_stub/mi_generated_resources_cs.rc
+++ b/mi_exe_stub/mi_generated_resources_cs.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_da.rc b/mi_exe_stub/mi_generated_resources_da.rc
index ff28065..2cce49e 100644
--- a/mi_exe_stub/mi_generated_resources_da.rc
+++ b/mi_exe_stub/mi_generated_resources_da.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_de.rc b/mi_exe_stub/mi_generated_resources_de.rc
index e75fa18..c4afbe7 100644
--- a/mi_exe_stub/mi_generated_resources_de.rc
+++ b/mi_exe_stub/mi_generated_resources_de.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_el.rc b/mi_exe_stub/mi_generated_resources_el.rc
index 2ac56cb..64db785 100644
--- a/mi_exe_stub/mi_generated_resources_el.rc
+++ b/mi_exe_stub/mi_generated_resources_el.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_en-GB.rc b/mi_exe_stub/mi_generated_resources_en-GB.rc
index 43002ec..aff9e7f 100644
--- a/mi_exe_stub/mi_generated_resources_en-GB.rc
+++ b/mi_exe_stub/mi_generated_resources_en-GB.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_en.rc b/mi_exe_stub/mi_generated_resources_en.rc
index 8cec252..f83d287 100644
--- a/mi_exe_stub/mi_generated_resources_en.rc
+++ b/mi_exe_stub/mi_generated_resources_en.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_es-419.rc b/mi_exe_stub/mi_generated_resources_es-419.rc
index be2e37c..6cd9839 100644
--- a/mi_exe_stub/mi_generated_resources_es-419.rc
+++ b/mi_exe_stub/mi_generated_resources_es-419.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_es.rc b/mi_exe_stub/mi_generated_resources_es.rc
index 8bde5f1..594996c 100644
--- a/mi_exe_stub/mi_generated_resources_es.rc
+++ b/mi_exe_stub/mi_generated_resources_es.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_et.rc b/mi_exe_stub/mi_generated_resources_et.rc
index 747b884..a44f9e1 100644
--- a/mi_exe_stub/mi_generated_resources_et.rc
+++ b/mi_exe_stub/mi_generated_resources_et.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_fa.rc b/mi_exe_stub/mi_generated_resources_fa.rc
index 6e87012..7728210 100644
--- a/mi_exe_stub/mi_generated_resources_fa.rc
+++ b/mi_exe_stub/mi_generated_resources_fa.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_fi.rc b/mi_exe_stub/mi_generated_resources_fi.rc
index 121a94a..094380f 100644
--- a/mi_exe_stub/mi_generated_resources_fi.rc
+++ b/mi_exe_stub/mi_generated_resources_fi.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_fil.rc b/mi_exe_stub/mi_generated_resources_fil.rc
index c146299..9e66413 100644
--- a/mi_exe_stub/mi_generated_resources_fil.rc
+++ b/mi_exe_stub/mi_generated_resources_fil.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_fr.rc b/mi_exe_stub/mi_generated_resources_fr.rc
index 9d02427..c9b972d 100644
--- a/mi_exe_stub/mi_generated_resources_fr.rc
+++ b/mi_exe_stub/mi_generated_resources_fr.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_gu.rc b/mi_exe_stub/mi_generated_resources_gu.rc
index 62f28e2..23020a7 100644
--- a/mi_exe_stub/mi_generated_resources_gu.rc
+++ b/mi_exe_stub/mi_generated_resources_gu.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_hi.rc b/mi_exe_stub/mi_generated_resources_hi.rc
index ec7bb67..c77e0a9 100644
--- a/mi_exe_stub/mi_generated_resources_hi.rc
+++ b/mi_exe_stub/mi_generated_resources_hi.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_hr.rc b/mi_exe_stub/mi_generated_resources_hr.rc
index cce1c54..17959ea 100644
--- a/mi_exe_stub/mi_generated_resources_hr.rc
+++ b/mi_exe_stub/mi_generated_resources_hr.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_hu.rc b/mi_exe_stub/mi_generated_resources_hu.rc
index 06408f6..276f203 100644
--- a/mi_exe_stub/mi_generated_resources_hu.rc
+++ b/mi_exe_stub/mi_generated_resources_hu.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_id.rc b/mi_exe_stub/mi_generated_resources_id.rc
index e6e9929..e94cd17 100644
--- a/mi_exe_stub/mi_generated_resources_id.rc
+++ b/mi_exe_stub/mi_generated_resources_id.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_is.rc b/mi_exe_stub/mi_generated_resources_is.rc
index b506d42..372bc6f 100644
--- a/mi_exe_stub/mi_generated_resources_is.rc
+++ b/mi_exe_stub/mi_generated_resources_is.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_it.rc b/mi_exe_stub/mi_generated_resources_it.rc
index 3656a86..3b95288 100644
--- a/mi_exe_stub/mi_generated_resources_it.rc
+++ b/mi_exe_stub/mi_generated_resources_it.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_iw.rc b/mi_exe_stub/mi_generated_resources_iw.rc
index fcfa03e..6bf8623 100644
--- a/mi_exe_stub/mi_generated_resources_iw.rc
+++ b/mi_exe_stub/mi_generated_resources_iw.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_ja.rc b/mi_exe_stub/mi_generated_resources_ja.rc
index 7c48cd2..7141b90 100644
--- a/mi_exe_stub/mi_generated_resources_ja.rc
+++ b/mi_exe_stub/mi_generated_resources_ja.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_kn.rc b/mi_exe_stub/mi_generated_resources_kn.rc
index 509de29..221b807 100644
--- a/mi_exe_stub/mi_generated_resources_kn.rc
+++ b/mi_exe_stub/mi_generated_resources_kn.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_ko.rc b/mi_exe_stub/mi_generated_resources_ko.rc
index dbefc61..bfdcc5c 100644
--- a/mi_exe_stub/mi_generated_resources_ko.rc
+++ b/mi_exe_stub/mi_generated_resources_ko.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_lt.rc b/mi_exe_stub/mi_generated_resources_lt.rc
index 9d5a672..640edf3 100644
--- a/mi_exe_stub/mi_generated_resources_lt.rc
+++ b/mi_exe_stub/mi_generated_resources_lt.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_lv.rc b/mi_exe_stub/mi_generated_resources_lv.rc
index e390d3b..14f06b0 100644
--- a/mi_exe_stub/mi_generated_resources_lv.rc
+++ b/mi_exe_stub/mi_generated_resources_lv.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_ml.rc b/mi_exe_stub/mi_generated_resources_ml.rc
index f0ed163..7c63044 100644
--- a/mi_exe_stub/mi_generated_resources_ml.rc
+++ b/mi_exe_stub/mi_generated_resources_ml.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_mr.rc b/mi_exe_stub/mi_generated_resources_mr.rc
index 6892af0..2c65cba 100644
--- a/mi_exe_stub/mi_generated_resources_mr.rc
+++ b/mi_exe_stub/mi_generated_resources_mr.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_ms.rc b/mi_exe_stub/mi_generated_resources_ms.rc
index 514d065..1a6ad34 100644
--- a/mi_exe_stub/mi_generated_resources_ms.rc
+++ b/mi_exe_stub/mi_generated_resources_ms.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_nl.rc b/mi_exe_stub/mi_generated_resources_nl.rc
index 2590902..5da788c 100644
--- a/mi_exe_stub/mi_generated_resources_nl.rc
+++ b/mi_exe_stub/mi_generated_resources_nl.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_no.rc b/mi_exe_stub/mi_generated_resources_no.rc
index b6b215b..1e209f1 100644
--- a/mi_exe_stub/mi_generated_resources_no.rc
+++ b/mi_exe_stub/mi_generated_resources_no.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_or.rc b/mi_exe_stub/mi_generated_resources_or.rc
deleted file mode 100644
index d6cf1ce..0000000
--- a/mi_exe_stub/mi_generated_resources_or.rc
+++ /dev/null
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_pl.rc b/mi_exe_stub/mi_generated_resources_pl.rc
index f060a03..6029355 100644
--- a/mi_exe_stub/mi_generated_resources_pl.rc
+++ b/mi_exe_stub/mi_generated_resources_pl.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_pt-BR.rc b/mi_exe_stub/mi_generated_resources_pt-BR.rc
index 10512d3..f58afb3 100644
--- a/mi_exe_stub/mi_generated_resources_pt-BR.rc
+++ b/mi_exe_stub/mi_generated_resources_pt-BR.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_pt-PT.rc b/mi_exe_stub/mi_generated_resources_pt-PT.rc
index 1a82839..370e15f 100644
--- a/mi_exe_stub/mi_generated_resources_pt-PT.rc
+++ b/mi_exe_stub/mi_generated_resources_pt-PT.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_ro.rc b/mi_exe_stub/mi_generated_resources_ro.rc
index 4ecf61a..84cf228 100644
--- a/mi_exe_stub/mi_generated_resources_ro.rc
+++ b/mi_exe_stub/mi_generated_resources_ro.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_ru.rc b/mi_exe_stub/mi_generated_resources_ru.rc
index 4a9141e..93df4b0 100644
--- a/mi_exe_stub/mi_generated_resources_ru.rc
+++ b/mi_exe_stub/mi_generated_resources_ru.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_sk.rc b/mi_exe_stub/mi_generated_resources_sk.rc
index 598efc1..6e21580 100644
--- a/mi_exe_stub/mi_generated_resources_sk.rc
+++ b/mi_exe_stub/mi_generated_resources_sk.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_sl.rc b/mi_exe_stub/mi_generated_resources_sl.rc
index 639a30f..1192c9d 100644
--- a/mi_exe_stub/mi_generated_resources_sl.rc
+++ b/mi_exe_stub/mi_generated_resources_sl.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_sr.rc b/mi_exe_stub/mi_generated_resources_sr.rc
index 0dcb313..902fa3a 100644
--- a/mi_exe_stub/mi_generated_resources_sr.rc
+++ b/mi_exe_stub/mi_generated_resources_sr.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_sv.rc b/mi_exe_stub/mi_generated_resources_sv.rc
index c3061cc..fb1dc03 100644
--- a/mi_exe_stub/mi_generated_resources_sv.rc
+++ b/mi_exe_stub/mi_generated_resources_sv.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_sw.rc b/mi_exe_stub/mi_generated_resources_sw.rc
new file mode 100644
index 0000000..33a28e8
--- /dev/null
+++ b/mi_exe_stub/mi_generated_resources_sw.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_ta.rc b/mi_exe_stub/mi_generated_resources_ta.rc
index 5fad2a8..0b5004b 100644
--- a/mi_exe_stub/mi_generated_resources_ta.rc
+++ b/mi_exe_stub/mi_generated_resources_ta.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_te.rc b/mi_exe_stub/mi_generated_resources_te.rc
index 2f1470a..ad6f992 100644
--- a/mi_exe_stub/mi_generated_resources_te.rc
+++ b/mi_exe_stub/mi_generated_resources_te.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_th.rc b/mi_exe_stub/mi_generated_resources_th.rc
index 604304b..b853fe5 100644
--- a/mi_exe_stub/mi_generated_resources_th.rc
+++ b/mi_exe_stub/mi_generated_resources_th.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_tr.rc b/mi_exe_stub/mi_generated_resources_tr.rc
index 2f13870..8252c7b 100644
--- a/mi_exe_stub/mi_generated_resources_tr.rc
+++ b/mi_exe_stub/mi_generated_resources_tr.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_uk.rc b/mi_exe_stub/mi_generated_resources_uk.rc
index 68192f8..b3db549 100644
--- a/mi_exe_stub/mi_generated_resources_uk.rc
+++ b/mi_exe_stub/mi_generated_resources_uk.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_ur.rc b/mi_exe_stub/mi_generated_resources_ur.rc
index d4dd5d6..e757e2f 100644
--- a/mi_exe_stub/mi_generated_resources_ur.rc
+++ b/mi_exe_stub/mi_generated_resources_ur.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_vi.rc b/mi_exe_stub/mi_generated_resources_vi.rc
index 222f4bc..53b5ebd 100644
--- a/mi_exe_stub/mi_generated_resources_vi.rc
+++ b/mi_exe_stub/mi_generated_resources_vi.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_zh-CN.rc b/mi_exe_stub/mi_generated_resources_zh-CN.rc
index 623a6b7..61a7008 100644
--- a/mi_exe_stub/mi_generated_resources_zh-CN.rc
+++ b/mi_exe_stub/mi_generated_resources_zh-CN.rc
Binary files differ
diff --git a/mi_exe_stub/mi_generated_resources_zh-TW.rc b/mi_exe_stub/mi_generated_resources_zh-TW.rc
index 97da1fe..fd75477 100644
--- a/mi_exe_stub/mi_generated_resources_zh-TW.rc
+++ b/mi_exe_stub/mi_generated_resources_zh-TW.rc
Binary files differ
diff --git a/mi_exe_stub/process.cc b/mi_exe_stub/process.cc
index b7ce951..46c852f 100644
--- a/mi_exe_stub/process.cc
+++ b/mi_exe_stub/process.cc
@@ -14,19 +14,21 @@
 // ========================================================================
 
 #include "omaha/mi_exe_stub/process.h"
-#include <strsafe.h>
 
-static bool run_and_wait(const CString &command_line,
-                         DWORD* exit_code,
-                         bool wait,
-                         int cmd_show) {
+namespace omaha {
+
+namespace {
+
+bool run_and_wait(const CString& command_line,
+                  DWORD* exit_code,
+                  bool wait,
+                  int cmd_show) {
   CString cmd_line(command_line);
-  STARTUPINFO si = {0};
-  PROCESS_INFORMATION pi = {0};
+  STARTUPINFO si = {};
+  PROCESS_INFORMATION pi = {};
 
   GetStartupInfo(&si);
-  si.dwFlags |= STARTF_FORCEOFFFEEDBACK;
-  si.dwFlags |= STARTF_USESHOWWINDOW;
+  si.dwFlags |= STARTF_FORCEOFFFEEDBACK | STARTF_USESHOWWINDOW;
   si.wShowWindow = static_cast<WORD>(cmd_show);
 
   BOOL create_process_result = CreateProcess(NULL,
@@ -59,14 +61,18 @@
   return result;
 }
 
-bool RunAndWaitHidden(const CString &command_line, DWORD *exit_code) {
+}  // namespace
+
+bool RunAndWaitHidden(const CString& command_line, DWORD *exit_code) {
   return run_and_wait(command_line, exit_code, true, SW_HIDE);
 }
 
-bool RunAndWait(const CString &command_line, DWORD *exit_code) {
+bool RunAndWait(const CString& command_line, DWORD *exit_code) {
   return run_and_wait(command_line, exit_code, true, SW_SHOWNORMAL);
 }
 
-bool Run(const CString &command_line) {
+bool Run(const CString& command_line) {
   return run_and_wait(command_line, NULL, false, SW_SHOWNORMAL);
 }
+
+}  // namespace omaha
diff --git a/mi_exe_stub/process.h b/mi_exe_stub/process.h
index 5ed66bb..fe6a1e1 100644
--- a/mi_exe_stub/process.h
+++ b/mi_exe_stub/process.h
@@ -13,27 +13,26 @@
 // limitations under the License.
 // ========================================================================
 
-
 #ifndef OMAHA_MI_EXE_STUB_PROCESS_H_
 #define OMAHA_MI_EXE_STUB_PROCESS_H_
 
-#pragma warning(push)
-#pragma warning(disable : 4548)
-// C4548: expression before comma has no effect
-#include <atlbase.h>
 #include <atlstr.h>
-#pragma warning(pop)
+#include <windows.h>
+
+namespace omaha {
 
 // Convenience function to launch a process. Returns true if we successfully
 // launched it.
-bool Run(const CString &command_line);
+bool Run(const CString& command_line);
 
 // Convenience function to launch a process, wait for it to finish,
 // and return the result.
-bool RunAndWait(const CString &command_line, DWORD *exit_code);
+bool RunAndWait(const CString& command_line, DWORD *exit_code);
 
 // Convenience function to launch a process with the initial window hidden,
 // wait for it to finish and return the result.
-bool RunAndWaitHidden(const CString &command_line, DWORD *exit_code);
+bool RunAndWaitHidden(const CString& command_line, DWORD *exit_code);
+
+}  // namespace omaha
 
 #endif  // OMAHA_MI_EXE_STUB_PROCESS_H_
diff --git a/mi_exe_stub/ptrdiff_t.h b/mi_exe_stub/ptrdiff_t.h
new file mode 100644
index 0000000..0f94fd4
--- /dev/null
+++ b/mi_exe_stub/ptrdiff_t.h
@@ -0,0 +1,32 @@
+// Copyright 2006-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// The WDK CRT headers do not define ptrdiff_t, which is used by
+// scoped_array.
+
+#ifndef OMAHA_MI_EXE_STUB_PTRDIFF_H_
+#define OMAHA_MI_EXE_STUB_PTRDIFF_H_
+
+namespace std {
+
+#ifdef _WIN64
+typedef __intr64 ptrdiff_t;
+#else
+typedef int ptrdiff_t;
+#endif
+
+}  // namespace std
+
+#endif  // OMAHA_MI_EXE_STUB_PTRDIFF_H_
diff --git a/mi_exe_stub/tar.cc b/mi_exe_stub/tar.cc
index 654eecc..1f3249d 100644
--- a/mi_exe_stub/tar.cc
+++ b/mi_exe_stub/tar.cc
@@ -15,18 +15,23 @@
 
 
 #include "omaha/mi_exe_stub/tar.h"
-#include <strsafe.h>
+#include <windows.h>
+#pragma warning(push)
+// C4310: cast truncates constant value
+#pragma warning(disable : 4310)
+#include "base/basictypes.h"
+#pragma warning(pop)
 
-#define USTAR_MAGIC "ustar"
-#define USTAR_OFFSET 257
-#define USTAR_DONE "\0\0\0\0\0"
+namespace omaha {
 
-template <typename T, size_t N>
-char (&ArraySizeHelper(T (&array)[N]))[N];    // NOLINT
-#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+namespace {
 
+const char kUstarMagic[] = "ustar";
+const char kUstarDone[5] = { '\0', '\0', '\0', '\0', '\0' };
 
-Tar::Tar(const char *target_dir, HANDLE file_handle, bool delete_when_done)
+}  // namespace
+
+Tar::Tar(const CString& target_dir, HANDLE file_handle, bool delete_when_done)
     : target_directory_name_(target_dir),
       file_handle_(file_handle),
       delete_when_done_(delete_when_done),
@@ -55,48 +60,48 @@
   bool result = true;
   BOOL file_result;
 
-  file_result = ReadFile(file_handle_, &header, sizeof(USTARHeader),
-    &bytes_handled, NULL);
+  file_result = ::ReadFile(file_handle_, &header, sizeof(USTARHeader),
+      &bytes_handled, NULL);
   if (!file_result || bytes_handled != sizeof(USTARHeader)) {
     return false;
   }
-  if (0 == memcmp(header.magic, USTAR_DONE, arraysize(USTAR_DONE) - 1)) {
+  if (0 == memcmp(header.magic, kUstarDone, arraysize(kUstarDone) - 1)) {
     // We're probably done, since we read the final block of all zeroes.
     *done = true;
     return true;
   }
-  if (0 != memcmp(header.magic, USTAR_MAGIC, arraysize(USTAR_MAGIC) - 1)) {
+  if (0 != memcmp(header.magic, kUstarMagic, arraysize(kUstarMagic) - 1)) {
     return false;
   }
   CString new_filename(target_directory_name_);
   new_filename += "\\";
   new_filename += header.name;
-  HANDLE new_file = CreateFile(new_filename, GENERIC_WRITE, 0, NULL,
-    CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
+  HANDLE new_file = ::CreateFile(new_filename, GENERIC_WRITE, 0, NULL,
+      CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
   if (new_file == INVALID_HANDLE_VALUE) {
     return false;
   }
   // We don't check for conversion errors because the input data is fixed at
   // build time, so it'll either always work or never work, and we won't ship
   // one that never works.
-  DWORD tar_file_size = strtol(header.size, NULL, 8);
-  DWORD next_offset = SetFilePointer(file_handle_, 0, NULL, FILE_CURRENT) +
+  DWORD tar_file_size = strtol(header.size, NULL, 8);  // NOLINT
+  DWORD next_offset = ::SetFilePointer(file_handle_, 0, NULL, FILE_CURRENT) +
       tar_file_size + (512 - tar_file_size & 0x1ff);
   while (tar_file_size > 0) {
-    const int COPY_BUFFER_SIZE = 256 * 1024;
-    char copy_buffer[COPY_BUFFER_SIZE];
-    DWORD bytes_to_handle = COPY_BUFFER_SIZE;
+    const int kCopyBufferSize = 256 * 1024;
+    char copy_buffer[kCopyBufferSize];
+    DWORD bytes_to_handle = kCopyBufferSize;
     if (bytes_to_handle > tar_file_size) {
       bytes_to_handle = tar_file_size;
     }
-    file_result = ReadFile(file_handle_, copy_buffer, bytes_to_handle,
-      &bytes_handled, NULL);
+    file_result = ::ReadFile(file_handle_, copy_buffer, bytes_to_handle,
+        &bytes_handled, NULL);
     if (!file_result) {
       result = false;
       break;
     } else {
-      file_result = WriteFile(new_file, copy_buffer, bytes_to_handle,
-        &bytes_handled, NULL);
+      file_result = ::WriteFile(new_file, copy_buffer, bytes_to_handle,
+          &bytes_handled, NULL);
       if (!file_result) {
         result = false;
         break;
@@ -121,3 +126,5 @@
 
   return result;
 }
+
+}  // namespace omaha
diff --git a/mi_exe_stub/tar.h b/mi_exe_stub/tar.h
index 29125b8..709156a 100644
--- a/mi_exe_stub/tar.h
+++ b/mi_exe_stub/tar.h
@@ -17,18 +17,17 @@
 #ifndef OMAHA_MI_EXE_STUB_TAR_H_
 #define OMAHA_MI_EXE_STUB_TAR_H_
 
-#pragma warning(push)
-// C4548: expression before comma has no effect
-#pragma warning(disable : 4548)
-#include <atlbase.h>
-#include <atlstr.h>
+#include <tchar.h>
 #include <atlsimpcoll.h>
-#pragma warning(pop)
+#include <atlstr.h>
+#include <windows.h>
 
-static const int NAME_SIZE = 100;
+namespace omaha {
+
+static const int kNameSize = 100;
 
 typedef struct {
-  char name[NAME_SIZE];
+  char name[kNameSize];
   char mode[8];
   char uid[8];
   char gid[8];
@@ -36,7 +35,7 @@
   char mtime[12];
   char chksum[8];
   char typeflag;
-  char linkname[NAME_SIZE];
+  char linkname[kNameSize];
   char magic[6];
   char version[2];
   char uname[32];
@@ -51,12 +50,12 @@
 // doesn't work with everything in the USTAR format.
 class Tar {
  public:
-  Tar(const char *target_dir, HANDLE file_handle, bool delete_when_done);
+  Tar(const CString& target_dir, HANDLE file_handle, bool delete_when_done);
   ~Tar();
 
-  typedef void (*TarFileCallback)(void *context, const char *filename);
+  typedef void (*TarFileCallback)(void* context, const TCHAR* filename);
 
-  void SetCallback(TarFileCallback callback, void *callback_context) {
+  void SetCallback(TarFileCallback callback, void* callback_context) {
     callback_ = callback;
     callback_context_ = callback_context;
   }
@@ -71,9 +70,11 @@
   bool delete_when_done_;
   CSimpleArray<CString> files_to_delete_;
   TarFileCallback callback_;
-  void *callback_context_;
+  void* callback_context_;
 
-  bool ExtractOneFile(bool *done);
+  bool ExtractOneFile(bool* done);
 };
 
+}  // namespace omaha
+
 #endif  // OMAHA_MI_EXE_STUB_TAR_H_
diff --git a/mi_exe_stub/x86_encoder/bcj.cc b/mi_exe_stub/x86_encoder/bcj.cc
new file mode 100644
index 0000000..9d0d19a
--- /dev/null
+++ b/mi_exe_stub/x86_encoder/bcj.cc
@@ -0,0 +1,78 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// BCJ encodes a file to increase its compressibility.
+
+#include <windows.h>
+#include <shellapi.h>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "third_party/smartany/scoped_any.h"
+
+extern "C" {
+#include "third_party/lzma/v4_65/files/C/Bra.h"
+}
+
+int wmain(int argc, WCHAR* argv[], WCHAR* env[]) {
+  UNREFERENCED_PARAMETER(env);
+
+  if (argc < 3) {
+    return 1;
+  }
+
+  // argv[1] is the input file, argv[2] is the output file.
+  scoped_hfile file(::CreateFile(argv[1], GENERIC_READ, 0,
+                                 NULL, OPEN_EXISTING, 0, NULL));
+  if (!valid(file)) {
+    return 2;
+  }
+
+  LARGE_INTEGER file_size_data;
+  if (!::GetFileSizeEx(get(file), &file_size_data)) {
+    return 3;
+  }
+
+  DWORD file_size = static_cast<DWORD>(file_size_data.QuadPart);
+  scoped_array<uint8> buffer(new uint8[file_size]);
+  DWORD bytes_read = 0;
+  if (!::ReadFile(get(file), buffer.get(), file_size, &bytes_read, NULL) ||
+      bytes_read != file_size) {
+    return 4;
+  }
+
+  uint32 conversion_state;
+  x86_Convert_Init(conversion_state);
+  // processed might be less than bytes read. This is apparently OK, although
+  // I don't understand why!
+  uint32 processed = x86_Convert(buffer.get(),
+                                 bytes_read,
+                                 0,
+                                 &conversion_state,
+                                 1 /* encoding */);
+
+  reset(file, ::CreateFile(argv[2], GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0,
+                           NULL));
+  if (!valid(file)) {
+    return 6;
+  }
+
+  DWORD bytes_written = 0;
+  if (!::WriteFile(get(file), buffer.get(), bytes_read, &bytes_written, NULL)) {
+    return 7;
+  }
+
+  return 0;
+}
diff --git a/mi_exe_stub/x86_encoder/bcj2.cc b/mi_exe_stub/x86_encoder/bcj2.cc
new file mode 100644
index 0000000..10d6349
--- /dev/null
+++ b/mi_exe_stub/x86_encoder/bcj2.cc
@@ -0,0 +1,139 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// BCJ encodes a file to increase its compressibility.
+
+#include <windows.h>
+#include <shellapi.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/mi_exe_stub/x86_encoder/bcj2_encoder.h"
+#include "third_party/smartany/scoped_any.h"
+
+int wmain(int argc, WCHAR* argv[], WCHAR* env[]) {
+  UNREFERENCED_PARAMETER(env);
+
+  if (argc < 3) {
+    return 1;
+  }
+
+  // argv[1] is the input file, argv[2] is the output file.
+  scoped_hfile file(::CreateFile(argv[1], GENERIC_READ, 0,
+                                 NULL, OPEN_EXISTING, 0, NULL));
+  if (!valid(file)) {
+    return 2;
+  }
+
+  LARGE_INTEGER file_size_data;
+  if (!::GetFileSizeEx(get(file), &file_size_data)) {
+    return 3;
+  }
+
+  DWORD file_size = static_cast<DWORD>(file_size_data.QuadPart);
+  scoped_array<uint8> buffer(new uint8[file_size]);
+  DWORD bytes_read = 0;
+  if (!::ReadFile(get(file), buffer.get(), file_size, &bytes_read, NULL) ||
+      bytes_read != file_size) {
+    return 4;
+  }
+
+  std::string out1;
+  std::string out2;
+  std::string out3;
+  std::string out4;
+  if (!omaha::Bcj2Encode(std::string(reinterpret_cast<char*>(buffer.get()),
+                                     file_size),
+                         &out1, &out2, &out3, &out4)) {
+    return 5;
+  }
+
+  // The format of BCJ2 file is very primitive.
+  // Header is 5 32-bit ints, with the following information:
+  //   unpacked size
+  //   size of stream 1
+  //   size of stream 2
+  //   size of stream 3
+  //   size of stream 4
+  const DWORD output_buffer_length = 5 * sizeof(uint32) +  // NOLINT
+                                     out1.size() + out2.size() + out3.size() +
+                                     out4.size();
+  DWORD buffer_remaining = output_buffer_length;
+  scoped_array<uint8> output_buffer(new uint8[output_buffer_length]);
+  uint8* p = output_buffer.get();
+  *reinterpret_cast<uint32*>(p) = bytes_read;
+  p += sizeof(uint32);  // NOLINT
+  buffer_remaining -= sizeof(uint32);  // NOLINT
+  *reinterpret_cast<uint32*>(p) = out1.size();
+  p += sizeof(uint32);  // NOLINT
+  buffer_remaining -= sizeof(uint32);  // NOLINT
+  *reinterpret_cast<uint32*>(p) = out2.size();
+  p += sizeof(uint32);  // NOLINT
+  buffer_remaining -= sizeof(uint32);  // NOLINT
+  *reinterpret_cast<uint32*>(p) = out3.size();
+  p += sizeof(uint32);  // NOLINT
+  buffer_remaining -= sizeof(uint32);  // NOLINT
+  *reinterpret_cast<uint32*>(p) = out4.size();
+  p += sizeof(uint32);  // NOLINT
+  buffer_remaining -= sizeof(uint32);  // NOLINT
+  if (!out1.empty()) {
+    if (0 != memcpy_s(p, buffer_remaining, &out1[0], out1.size())) {
+      return 9;
+    }
+    p += out1.size();
+    buffer_remaining -= out1.size();
+  }
+  if (!out2.empty()) {
+    if (0 != memcpy_s(p, buffer_remaining, &out2[0], out2.size())) {
+      return 10;
+    }
+    p += out2.size();
+    buffer_remaining -= out2.size();
+  }
+  if (!out3.empty()) {
+    if (0 != memcpy_s(p, buffer_remaining, &out3[0], out3.size())) {
+      return 11;
+    }
+    p += out3.size();
+    buffer_remaining -= out3.size();
+  }
+  if (!out4.empty()) {
+    if (0 != memcpy_s(p, buffer_remaining, &out4[0], out4.size())) {
+      return 12;
+    }
+    p += out4.size();
+    buffer_remaining -= out4.size();
+  }
+  if (p != output_buffer.get() + output_buffer_length ||
+      0 != buffer_remaining) {
+    return 8;
+  }
+
+  reset(file, ::CreateFile(argv[2], GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0,
+                           NULL));
+  if (!valid(file)) {
+    return 6;
+  }
+
+  DWORD bytes_written = 0;
+  if (!::WriteFile(get(file), output_buffer.get(), output_buffer_length,
+                   &bytes_written, NULL)) {
+    return 7;
+  }
+
+  return 0;
+}
diff --git a/mi_exe_stub/x86_encoder/bcj2_encoder.cc b/mi_exe_stub/x86_encoder/bcj2_encoder.cc
new file mode 100644
index 0000000..bce69db
--- /dev/null
+++ b/mi_exe_stub/x86_encoder/bcj2_encoder.cc
@@ -0,0 +1,168 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// The BCJ2 algorithm takes advantage of the fact that a lot of relative jumps
+// in x86 code are to the same address. It essentially performs the moral
+// equivalent of the following conversion:
+// ...
+// JMP 8 bytes back
+// ...
+// JMP 28 bytes back
+// ...
+// JMP 40 bytes back
+// ...
+// to:
+// ...
+// JMP 0x1000
+// ...
+// JMP 0x1000
+// ...
+// JMP 0x1000
+// ...
+//
+// The second form has a lot more repetition, and standard entropy coding can
+// compress it further.
+//
+// Details:
+// TODO(omaha): figure out what the byte before a CALL (0xE8) instruction means.
+// TODO(omaha): document exactly how the range encoding is used. There are 258
+// range encoding bits. The first 256 are used for CALL instructions based on
+// the value of the previous byte; byte 256 is used for JMP, and byte 257 is
+// used for JCC.
+//
+// BCJ2 converts the targets for the CALL (0xE8), JMP (0xE9), and certain JCC
+// (0xF80-0xF8F) instructions into absolute jumps.  This is not a full x86 op
+// code interpreter, and it is almost certain that bytes that are data rather
+// than instructions will be encoded.
+// The algorithm uses the following steps to perform the conversion:
+// 1. Iterate through each byte while the current position is at least 5 bytes
+//    away from the end.
+// 1. If the byte is not a CALL , JMP, or JCC instruction, copy the byte to the
+//    output and go back to step 1.
+// 2. Otherwise, calculate the target of the jump. If the target of this jump is
+//    not within this file (e.g. past the end of the input), we do not rewrite
+//    this jump: write 0 to the range encoder to indicate the target was not
+//    processed and go back to step 1.
+// 3. If the instruction is CALL, write the absolute target to the call output
+//    stream. Otherwise, write the absolute target to the jump output stream.
+//    Write a 1 to the range encoder to indicate the target was processed and go
+//    back to step 1.
+// 4. Once within 5 bytes of the end of the input, flush out the remaining
+//    bytes. If one of the bytes happens to be one of the op codes that should
+//    be handled, write a 0 to the range encoder to indicate that it was not
+//    processed.
+
+#include "omaha/mi_exe_stub/x86_encoder/bcj2_encoder.h"
+
+#include "base/basictypes.h"
+#include "omaha/mi_exe_stub/x86_encoder/range_encoder.h"
+
+namespace omaha {
+
+namespace {
+
+bool IsJcc(uint8 byte0, uint8 byte1) {
+  return (byte0 == 0x0F && (byte1 & 0xF0) == 0x80);
+}
+
+bool IsJ(uint8 byte0, uint8 byte1) {
+  return ((byte1 & 0xFE) == 0xE8 || IsJcc(byte0, byte1));
+}
+
+int GetIndex(uint8 byte0, uint8 byte1) {
+  return ((byte1 == 0xE8) ? byte0 : ((byte1 == 0xE9) ? 256 : 257));
+}
+
+}  // namespace
+
+bool Bcj2Encode(const std::string& input,
+                std::string* main_output,
+                std::string* call_output,
+                std::string* jump_output,
+                std::string* misc_output) {
+  if (!main_output || !call_output || !jump_output || !misc_output) {
+    return false;
+  }
+
+  size_t input_position = 0;
+
+  static const int kNumberOfMoveBits = 5;
+  RangeEncoder range_encoder(misc_output);
+  RangeEncoderBit<kNumberOfMoveBits> status_encoder[256 + 2];
+
+  uint8 previous_byte = 0;
+
+  while (true) {
+    if (input.size() - input_position < 5) {
+      for (; input_position < input.size(); ++input_position) {
+        uint8 byte = input[input_position];
+        *main_output += byte;
+
+        size_t index;
+        if (0xE8 == byte) {
+          index = previous_byte;
+        } else if (0xE9 == byte) {
+          index = 256;
+        } else if (IsJcc(previous_byte, byte)) {
+          index = 257;
+        } else {
+          previous_byte = byte;
+          continue;
+        }
+        status_encoder[index].Encode(0, &range_encoder);
+        previous_byte = byte;
+      }
+
+      range_encoder.Flush();
+      return true;
+    }
+
+    while (input_position <= input.size() - 5) {
+      uint8 byte = input[input_position];
+      *main_output += byte;
+
+      if (!IsJ(previous_byte, byte)) {
+        input_position++;
+        previous_byte = byte;
+        continue;
+      }
+
+      uint8 next_byte = input[input_position + 4];
+      uint32 src =
+        static_cast<uint32>(next_byte) << 24 |
+        static_cast<uint32>(input[input_position + 3]) << 16 |
+        static_cast<uint32>(input[input_position + 2]) << 8 |
+        input[input_position + 1];
+      uint32 dst = input_position + src + 5;
+
+      uint32 index = GetIndex(previous_byte, byte);
+      if (dst < input.size()) {
+        status_encoder[index].Encode(1, &range_encoder);
+        input_position += 5;
+        std::string* s = (byte == 0xE8) ? call_output : jump_output;
+        for (int i = 24; i >= 0; i -= 8) {
+          *s += static_cast<uint8>(dst >> i);
+        }
+        previous_byte = next_byte;
+      } else {
+        status_encoder[index].Encode(0, &range_encoder);
+        input_position++;
+        previous_byte = byte;
+      }
+    }
+  }
+}
+
+}  // namespace omaha
diff --git a/mi_exe_stub/x86_encoder/bcj2_encoder.h b/mi_exe_stub/x86_encoder/bcj2_encoder.h
new file mode 100644
index 0000000..f70516f
--- /dev/null
+++ b/mi_exe_stub/x86_encoder/bcj2_encoder.h
@@ -0,0 +1,41 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Implementation taken from Bcj2Coder implementation in LZMA SDK and converted
+// to use std::string as the interface.
+
+#ifndef OMAHA_MI_EXE_STUB_X86_ENCODER_BCJ2_ENCODER_H_
+#define OMAHA_MI_EXE_STUB_X86_ENCODER_BCJ2_ENCODER_H_
+
+#include <string>
+
+namespace omaha {
+
+// TODO(omaha): this is currently a single-shot interface. The entire buffer
+// to be encoded must be loaded in memory. It'd be nice to make work in chunks.
+// TODO(omaha): consider converting this interface to use std::vector. The
+// reason std::string is used is for the auto-resize convenience.
+// All input/output parameters from this function are *binary* strings.
+// Use std::string::size() to get the length of the buffer. Do not use
+// std::string::c_str() on these strings.
+bool Bcj2Encode(const std::string& input,
+                std::string* main_output,
+                std::string* call_output,
+                std::string* jump_output,
+                std::string* misc_output);
+
+}  // namespace omaha
+
+#endif  // OMAHA_MI_EXE_STUB_X86_ENCODER_BCJ2_ENCODER_H_
diff --git a/mi_exe_stub/x86_encoder/bcj2_encoder_unittest.cc b/mi_exe_stub/x86_encoder/bcj2_encoder_unittest.cc
new file mode 100644
index 0000000..164d94b
--- /dev/null
+++ b/mi_exe_stub/x86_encoder/bcj2_encoder_unittest.cc
@@ -0,0 +1,73 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/mi_exe_stub/x86_encoder/bcj2_encoder.h"
+#include <string>
+#include <vector>
+#include "omaha/base/utils.h"
+#include "omaha/testing/unit_test.h"
+extern "C" {
+#include "omaha/third_party/lzma/v4_65/files/C/Bcj2.h"
+}
+
+namespace omaha {
+
+TEST(Bcj2EncoderTest, EmptyBuffer) {
+  std::string input;
+  std::string output1;
+  std::string output2;
+  std::string output3;
+  std::string output4;
+  ASSERT_TRUE(Bcj2Encode(input, &output1, &output2, &output3, &output4));
+  EXPECT_EQ("", output1);
+  EXPECT_EQ("", output2);
+  EXPECT_EQ("", output3);
+  EXPECT_EQ(std::string(5, '\0'), output4);
+}
+
+// Test that the transform is reversible.
+TEST(Bcj2EncoderTest, Reversible) {
+  // The victim program is the unit test itself.
+  WCHAR module_path[MAX_PATH];
+  ASSERT_LT(static_cast<DWORD>(0), ::GetModuleFileName(NULL,
+                                                       module_path,
+                                                       arraysize(module_path)));
+  std::vector<byte> raw_file;
+  ASSERT_HRESULT_SUCCEEDED(
+      ReadEntireFileShareMode(module_path, 0, FILE_SHARE_READ, &raw_file));
+  const std::string input(reinterpret_cast<char*>(&raw_file[0]),
+                          raw_file.size());
+  std::string output1;
+  std::string output2;
+  std::string output3;
+  std::string output4;
+  ASSERT_TRUE(Bcj2Encode(input, &output1, &output2, &output3, &output4));
+
+  std::string decoded_output;
+  decoded_output.resize(raw_file.size());
+  ASSERT_EQ(SZ_OK, Bcj2_Decode(reinterpret_cast<const uint8*>(output1.data()),
+                               output1.size(),
+                               reinterpret_cast<const uint8*>(output2.data()),
+                               output2.size(),
+                               reinterpret_cast<const uint8*>(output3.data()),
+                               output2.size(),
+                               reinterpret_cast<const uint8*>(output4.data()),
+                               output4.size(),
+                               reinterpret_cast<uint8*>(&decoded_output[0]),
+                               decoded_output.size()));
+  EXPECT_EQ(input, decoded_output);
+}
+
+}  // namespace omaha
diff --git a/mi_exe_stub/x86_encoder/build.scons b/mi_exe_stub/x86_encoder/build.scons
new file mode 100644
index 0000000..05f9e2c
--- /dev/null
+++ b/mi_exe_stub/x86_encoder/build.scons
@@ -0,0 +1,69 @@
+# Copyright 2008-2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========================================================================
+
+Import('env')
+
+local_env = env.Clone()
+local_env.FilterOut(CPPDEFINES=['_ATL_DEBUG_INTERFACES'])
+
+bcj2_lib = local_env.ComponentStaticLibrary(
+    lib_name='bcj2_lib',
+    source=[
+        'bcj2_encoder.cc',
+        'range_encoder.cc',
+    ],
+)
+
+bin_env = local_env.Clone()
+bin_env.FilterOut(LINKFLAGS=['/SUBSYSTEM:WINDOWS'])
+bin_env.Append(
+    LINKFLAGS=['/SUBSYSTEM:CONSOLE'],
+    LIBS=[
+        ('libcmt.lib', 'libcmtd.lib')[local_env.Bit('debug')],
+        ('libcpmt.lib', 'libcpmtd.lib')[local_env.Bit('debug')],
+        'kernel32.lib',
+        '$LIB_DIR/lzma.lib',
+    ],
+)
+
+bin_env.ComponentTool(
+    prog_name='bcj',
+    source=[
+        'bcj.cc',
+    ],
+)
+
+bjc2_env = bin_env.Clone()
+bjc2_env.Append(
+    LIBS=[ bcj2_lib ],
+)
+bjc2_env.ComponentTool(
+    prog_name='bcj2',
+    source=[
+        'bcj2.cc',
+    ],
+)
+
+local_env.OmahaUnittest(
+    name='bcj2_encoder_unittest',
+    source='bcj2_encoder_unittest.cc',
+    LIBS=[
+        bcj2_lib,
+        '$LIB_DIR/common.lib',
+        '$LIB_DIR/lzma.lib',
+    ],
+    all_in_one=False,
+    COMPONENT_TEST_SIZE='small',
+)
diff --git a/mi_exe_stub/x86_encoder/range_encoder.cc b/mi_exe_stub/x86_encoder/range_encoder.cc
new file mode 100644
index 0000000..31bfd51
--- /dev/null
+++ b/mi_exe_stub/x86_encoder/range_encoder.cc
@@ -0,0 +1,61 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Derived from RangeCoder.h and RangeCoderBit.h in the LZMA SDK
+
+#include "omaha/mi_exe_stub/x86_encoder/range_encoder.h"
+
+#include "base/basictypes.h"
+
+namespace omaha {
+
+RangeEncoder::RangeEncoder(std::string* output)
+    : output_(output),
+      cache_(0),
+      cache_size_(1),
+      low_(0),
+      range_(0xFFFFFFFF) {
+}
+
+void RangeEncoder::Encode(uint32 start, uint32 size, uint32 total) {
+  low_ += start * (range_ /= total);
+  range_ *= size;
+  while (range_ < kTopValue) {
+    range_ <<= 8;
+    ShiftLow();
+  }
+}
+
+void RangeEncoder::ShiftLow() {
+  if (static_cast<uint32>(low_) < static_cast<uint32>(0xFF000000) ||
+      static_cast<int>(low_ >> 32) != 0) {
+    uint8 temp = cache_;
+    do {
+      *output_ += static_cast<uint8>(temp + static_cast<uint8>(low_ >> 32));
+      temp = 0xFF;
+    } while (--cache_size_ != 0);
+    cache_ = static_cast<uint8>(static_cast<uint32>(low_) >> 24);
+  }
+  cache_size_++;
+  low_ = static_cast<uint32>(low_) << 8;
+}
+
+void RangeEncoder::Flush() {
+  for (int i = 0; i < 5; ++i) {
+    ShiftLow();
+  }
+}
+
+}  // namespace
diff --git a/mi_exe_stub/x86_encoder/range_encoder.h b/mi_exe_stub/x86_encoder/range_encoder.h
new file mode 100644
index 0000000..a4485b1
--- /dev/null
+++ b/mi_exe_stub/x86_encoder/range_encoder.h
@@ -0,0 +1,92 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Derived from RangeCoder.h and RangeCoderBit.h in the LZMA SDK
+//
+// A description of how range encoding works can be found at
+// http://en.wikipedia.org/wiki/Range_encoding
+
+#ifndef OMAHA_MI_EXE_STUB_X86_ENCODER_RANGE_ENCODER_H_
+#define OMAHA_MI_EXE_STUB_X86_ENCODER_RANGE_ENCODER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace omaha {
+
+const int kNumberOfBitsModelTotalBits = 11;
+const uint32 kBitModelTotal = 1 << kNumberOfBitsModelTotalBits;
+
+const int kNumTopBits = 24;
+const uint32 kTopValue = 1 << kNumTopBits;
+
+class RangeEncoder {
+ public:
+  // RangeEncoder does not take ownership. Lifetime must be managed by caller.
+  explicit RangeEncoder(std::string* output);
+  void Encode(uint32 start, uint32 size, uint32 total);
+  void ShiftLow();
+  void Flush();
+
+  uint64 low() const { return low_; }
+  void set_low(uint64 low) { low_ = low; }
+  uint32 range() const { return range_; }
+  void set_range(uint32 range) { range_ = range; }
+
+ private:
+  uint8 cache_;
+  uint32 cache_size_;
+
+  uint64 low_;
+  uint32 range_;
+
+  std::string* output_;
+
+  DISALLOW_COPY_AND_ASSIGN(RangeEncoder);
+};
+
+template<int kNumberOfMoveBits>
+class RangeEncoderBit {
+ public:
+  RangeEncoderBit() : probability_(kBitModelTotal / 2) {
+  }
+
+  void Encode(uint32 symbol, RangeEncoder* encoder) {
+    uint32 new_bound = (encoder->range() >> kNumberOfBitsModelTotalBits) *
+                       probability_;
+    if (0 == symbol) {
+      encoder->set_range(new_bound);
+      probability_ += (kBitModelTotal - probability_) >> kNumberOfMoveBits;
+    } else {
+      encoder->set_low(encoder->low() + new_bound);
+      encoder->set_range(encoder->range() - new_bound);
+      probability_ -= probability_ >> kNumberOfMoveBits;
+    }
+    if (encoder->range() < kTopValue) {
+      encoder->set_range(encoder->range() << 8);
+      encoder->ShiftLow();
+    }
+  }
+
+ private:
+  uint32 probability_;
+
+  DISALLOW_COPY_AND_ASSIGN(RangeEncoderBit);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_MI_EXE_STUB_X86_ENCODER_RANGE_ENCODER_H_
diff --git a/net/bind_status_callback.cc b/net/bind_status_callback.cc
index acc715c..43a5ab8 100644
--- a/net/bind_status_callback.cc
+++ b/net/bind_status_callback.cc
@@ -17,10 +17,10 @@
 
 #include "omaha/net/bind_status_callback.h"
 #include <wininet.h>
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/utils.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/utils.h"
 
 namespace omaha {
 
@@ -54,81 +54,29 @@
 BindStatusCallback::BindStatusCallback()
     : http_verb_(BINDVERB_GET),
       post_data_byte_count_(0),
-      response_headers_(NULL),
       response_code_(0) {
 }
 
-HRESULT BindStatusCallback::CreateAndSend(BSTR url,
-                                          BSTR data,
-                                          BSTR request_headers,
-                                          VARIANT response_headers_needed,
-                                          VARIANT* response_headers,
-                                          DWORD* response_code,
-                                          BSTR* cache_filename) {
-  if (!url || !*url) {
-    return E_INVALIDARG;
-  }
-  if (!cache_filename) {
-    return E_INVALIDARG;
-  }
-  *cache_filename = NULL;
-
-  // Using CComObjectNoLock. For the BHO, the DLL is always locked into memory,
-  // and for the exe case, goopdate.dll is always loaded, so using the NoLock
-  // version works well.
-  CComObjectNoLock<BindStatusCallback>* bsc_obj =
-      new CComObjectNoLock<BindStatusCallback>;
-  if (!bsc_obj) {
-    CORE_LOG(LE, (_T("[BindStatusCallback creation failed]")));
-    return E_OUTOFMEMORY;
-  }
-
-  // Implicit AddRef() to bring the refcount to 1.
-  CComPtr<IBindStatusCallback> bsc(bsc_obj);
-  HRESULT hr = bsc_obj->Init(data,
-                             request_headers,
-                             response_headers_needed,
-                             response_headers,
-                             response_code);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  CString filename;
-  hr = ::URLDownloadToCacheFile(NULL,
-                                url,
-                                CStrBuf(filename, MAX_PATH),
-                                MAX_PATH,
-                                0,
-                                bsc);
-  CORE_LOG(L2, (_T("URLDownloadToCacheFile 0x%x %s"), hr, url));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  ASSERT1(!filename.IsEmpty());
-  CORE_LOG(L2, (_T("[BrowserHttpRequest::Send][cache file][%s]"), filename));
-  *cache_filename = filename.AllocSysString();
-  return hr;
-}
-
-HRESULT BindStatusCallback::Init(BSTR post_data,
+HRESULT BindStatusCallback::Send(BSTR url,
+                                 BSTR post_data,
                                  BSTR request_headers,
                                  VARIANT response_headers_needed,
                                  VARIANT* response_headers,
-                                 DWORD* response_code) {
-  ASSERT1(response_code);
-  if (!response_code) {
+                                 DWORD* response_code,
+                                 BSTR* cache_filename) {
+  if (!url || !*url || !response_code || !cache_filename) {
     return E_INVALIDARG;
   }
+
   *response_code = 0;
+  *cache_filename = NULL;
 
   if (V_VT(&response_headers_needed) != VT_EMPTY) {
     if ((V_VT(&response_headers_needed) != (VT_ARRAY | VT_UI4)) ||
         !response_headers) {
       return E_INVALIDARG;
     }
-    response_headers->vt = VT_EMPTY;
+    V_VT(response_headers) = VT_NULL;
     response_headers_needed_ = response_headers_needed.parray;
     if (!response_headers_needed_.GetCount()) {
       return E_INVALIDARG;
@@ -136,34 +84,56 @@
   }
 
   request_headers_ = request_headers;
-  response_code_ = response_code;
-  response_headers_ = response_headers;
   if (!post_data) {
     http_verb_ = BINDVERB_GET;
-    return S_OK;
+  } else {
+    http_verb_ = BINDVERB_POST;
+    post_data_byte_count_ = ::SysStringByteLen(post_data);
+    reset(post_data_, ::GlobalAlloc(GPTR, post_data_byte_count_));
+    if (!post_data_) {
+      HRESULT hr = HRESULTFromLastError();
+      CORE_LOG(LE, (_T("[::GlobalAlloc failed][0x%x]"), hr));
+      return hr;
+    }
+
+    memcpy(get(post_data_), post_data, post_data_byte_count_);
   }
 
-  http_verb_ = BINDVERB_POST;
-  post_data_byte_count_ = ::SysStringByteLen(post_data);
-  reset(post_data_, ::GlobalAlloc(GPTR, post_data_byte_count_));
-  if (!post_data_) {
-    HRESULT hr = HRESULTFromLastError();
-    CORE_LOG(LE, (_T("[BindStatusCallback Init failed 0x%x]"), hr));
+  CComPtr<IBindStatusCallback> bsc(this);
+  CString filename;
+  HRESULT hr = ::URLDownloadToCacheFile(NULL,
+                                        url,
+                                        CStrBuf(filename, MAX_PATH),
+                                        MAX_PATH,
+                                        0,
+                                        bsc);
+
+  if (response_headers) {
+    response_headers_.Detach(response_headers);
+  }
+  *response_code = response_code_;
+
+  CORE_LOG(L2, (_T("[URLDownloadToCacheFile][0x%x][%s]"), hr, url));
+  if (FAILED(hr)) {
     return hr;
   }
 
-  memcpy(get(post_data_), post_data, post_data_byte_count_);
-  return S_OK;
+  ASSERT1(!filename.IsEmpty());
+  CORE_LOG(L2, (_T("[BindStatusCallback::Send][cache file][%s]"), filename));
+  *cache_filename = filename.AllocSysString();
+  return hr;
 }
 
 // IBindStatusCallback methods.
 
 STDMETHODIMP BindStatusCallback::OnStartBinding(DWORD, IBinding* binding) {
-  binding_ = binding;
+  __mutexScope(lock_);
+  binding_git_.Attach(binding);
   return S_OK;
 }
 
-STDMETHODIMP BindStatusCallback::GetPriority(LONG*) {
+STDMETHODIMP BindStatusCallback::GetPriority(LONG* priority) {
+  UNREFERENCED_PARAMETER(priority);
   return E_NOTIMPL;
 }
 
@@ -176,7 +146,22 @@
 }
 
 STDMETHODIMP BindStatusCallback::OnStopBinding(HRESULT, LPCWSTR) {
-  CComQIPtr<IWinInetHttpInfo> http_info(binding_);
+  CComPtr<IBinding> binding;
+
+  __mutexBlock(lock_) {
+    if (!binding_git_) {
+      return S_OK;
+    }
+
+    HRESULT hr = binding_git_.CopyTo(&binding);
+    VERIFY1(SUCCEEDED(binding_git_.Revoke()));
+    if (FAILED(hr)) {
+      CORE_LOG(LW, (_T("[binding_git_.CopyTo failed][0x%x]"), hr));
+      return S_OK;
+    }
+  }
+
+  CComQIPtr<IWinInetHttpInfo> http_info(binding);
   if (!http_info) {
     return S_OK;
   }
@@ -186,13 +171,12 @@
                               HTTP_QUERY_STATUS_CODE,
                               &response_code_buf)) &&
       !response_code_buf.IsEmpty()) {
-    *response_code_ = _ttoi(response_code_buf);
+    response_code_ = _ttoi(response_code_buf);
   }
 
   if (!response_headers_needed_) {
     return S_OK;
   }
-  ASSERT1(response_headers_);
   int count = response_headers_needed_.GetCount();
   ASSERT1(count > 0);
   int lower_bound = response_headers_needed_.GetLowerBound();
@@ -205,8 +189,8 @@
     response_array[i] = response_header_buf.AllocSysString();
   }
 
-  response_headers_->vt = VT_ARRAY | VT_BSTR;
-  response_headers_->parray = response_array.Detach();
+  V_VT(&response_headers_) = VT_ARRAY | VT_BSTR;
+  V_ARRAY(&response_headers_) = response_array.Detach();
   return S_OK;
 }
 
@@ -315,5 +299,23 @@
   return S_OK;
 }
 
+HRESULT BindStatusCallback::Cancel() {
+  CComPtr<IBinding> binding;
+
+  __mutexBlock(lock_) {
+    if (!binding_git_) {
+      return S_OK;
+    }
+
+    HRESULT hr = binding_git_.CopyTo(&binding);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[binding_git_.CopyTo failed][0x%x]"), hr));
+      return hr;
+    }
+  }
+
+  return binding->Abort();
+}
+
 }  // namespace omaha
 
diff --git a/net/bind_status_callback.h b/net/bind_status_callback.h
index c505081..3afb72c 100644
--- a/net/bind_status_callback.h
+++ b/net/bind_status_callback.h
@@ -24,7 +24,8 @@
 #include <atlcom.h>
 #include <atlsafe.h>
 #include <atlstr.h>
-#include "omaha/common/scoped_any.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/synchronized.h"
 
 namespace omaha {
 
@@ -33,13 +34,15 @@
       public IBindStatusCallback,
       public IHttpNegotiate {
  public:
-  static HRESULT CreateAndSend(BSTR url,
-                               BSTR post_data,
-                               BSTR request_headers,
-                               VARIANT response_headers_needed,
-                               VARIANT* response_headers,
-                               DWORD* response_code,
-                               BSTR* cache_filename);
+  HRESULT Send(BSTR url,
+               BSTR post_data,
+               BSTR request_headers,
+               VARIANT response_headers_needed,
+               VARIANT* response_headers,
+               DWORD* response_code,
+               BSTR* cache_filename);
+
+  HRESULT Cancel();
 
   // C4505: unreferenced IUnknown local functions have been removed
   #pragma warning(disable : 4505)
@@ -73,21 +76,15 @@
   virtual ~BindStatusCallback() {}
 
  private:
-  HRESULT Init(BSTR post_data,
-               BSTR request_headers,
-               VARIANT response_headers_needed,
-               VARIANT* response_headers,
-               DWORD* response_code);
-
- private:
+  LLock lock_;
   BINDVERB              http_verb_;
   scoped_hglobal        post_data_;
   DWORD                 post_data_byte_count_;
   CString               request_headers_;
   CComSafeArray<DWORD>  response_headers_needed_;
-  VARIANT*              response_headers_;
-  DWORD*                response_code_;
-  CComPtr<IBinding>     binding_;
+  CComVariant           response_headers_;
+  DWORD                 response_code_;
+  CComGITPtr<IBinding>  binding_git_;
 };
 
 }  // namespace omaha
diff --git a/net/bits_job_callback.cc b/net/bits_job_callback.cc
new file mode 100644
index 0000000..02943cb
--- /dev/null
+++ b/net/bits_job_callback.cc
@@ -0,0 +1,90 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/net/bits_job_callback.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/utils.h"
+#include "omaha/net/bits_request.h"
+#include "omaha/net/bits_utils.h"
+
+namespace omaha {
+
+HRESULT BitsJobCallback::Create(BitsRequest* bits_request,
+                                BitsJobCallback** bits_job_callback) {
+  ASSERT1(bits_request);
+  ASSERT1(bits_job_callback);
+
+  *bits_job_callback = NULL;
+  scoped_ptr<CComObjectNoLock<BitsJobCallback> > callback_obj(
+      new CComObjectNoLock<BitsJobCallback>);
+  if (callback_obj == NULL) {
+    return E_OUTOFMEMORY;
+  }
+
+  callback_obj->AddRef();
+  callback_obj->bits_request_ = bits_request;
+  *bits_job_callback = callback_obj.release();
+
+  return S_OK;
+}
+
+void BitsJobCallback::RemoveReferenceToBitsRequest() {
+  __mutexScope(lock_);
+  bits_request_ = NULL;
+}
+
+HRESULT BitsJobCallback::JobTransferred(IBackgroundCopyJob* bits_job) {
+  UNREFERENCED_PARAMETER(bits_job);
+
+  __mutexScope(lock_);
+  if (bits_request_) {
+    bits_request_->OnBitsJobStateChanged();
+  }
+
+  // Returns S_OK to avoid BITS continuing to call this callback.
+  return S_OK;
+}
+
+HRESULT BitsJobCallback::JobError(IBackgroundCopyJob* bits_job,
+                                  IBackgroundCopyError* error) {
+  UNREFERENCED_PARAMETER(bits_job);
+  UNREFERENCED_PARAMETER(error);
+
+  __mutexScope(lock_);
+  if (bits_request_) {
+    bits_request_->OnBitsJobStateChanged();
+  }
+
+  // Returns S_OK to avoid BITS continuing to call this callback.
+  return S_OK;
+}
+
+HRESULT BitsJobCallback::JobModification(IBackgroundCopyJob* bits_job,
+                                         DWORD reserved) {
+  ASSERT1(bits_job);
+  UNREFERENCED_PARAMETER(bits_job);
+  UNREFERENCED_PARAMETER(reserved);
+
+  __mutexScope(lock_);
+  if (bits_request_) {
+    bits_request_->OnBitsJobStateChanged();
+  }
+  return S_OK;
+}
+
+}  // namespace omaha
+
diff --git a/net/bits_job_callback.h b/net/bits_job_callback.h
new file mode 100644
index 0000000..eb3a9cb
--- /dev/null
+++ b/net/bits_job_callback.h
@@ -0,0 +1,66 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+
+#ifndef OMAHA_NET_BITS_JOB_CALLBACK_H_
+#define OMAHA_NET_BITS_JOB_CALLBACK_H_
+
+#include <windows.h>
+#include <bits.h>
+#include <atlbase.h>
+#include <atlcom.h>
+#include "base/basictypes.h"
+#include "omaha/base/synchronized.h"
+
+namespace omaha {
+
+class BitsRequest;
+
+// BitsJobCallback receives notifications that a BITS job is complete, has been
+// modified, or is in error.
+class ATL_NO_VTABLE BitsJobCallback
+    : public CComObjectRootEx<CComObjectThreadModel>,
+      public IBackgroundCopyCallback {
+ public:
+  BitsJobCallback() {}
+  virtual ~BitsJobCallback() {
+    bits_request_ = NULL;
+  }
+
+  static HRESULT Create(BitsRequest* bits_request,
+                        BitsJobCallback** bits_job_callback);
+
+  void RemoveReferenceToBitsRequest();
+
+  BEGIN_COM_MAP(BitsJobCallback)
+    COM_INTERFACE_ENTRY(IBackgroundCopyCallback)
+  END_COM_MAP()
+
+  // IBackgroundCopyCallback methods.
+  STDMETHODIMP JobTransferred(IBackgroundCopyJob* job);
+  STDMETHODIMP JobError(IBackgroundCopyJob* job, IBackgroundCopyError* error);
+  STDMETHODIMP JobModification(IBackgroundCopyJob* job, DWORD reserved);
+
+ private:
+  BitsRequest* bits_request_;
+  LLock lock_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(BitsJobCallback);
+};
+
+}   // namespace omaha
+
+#endif  // OMAHA_NET_BITS_JOB_CALLBACK_H_
+
diff --git a/net/bits_request.cc b/net/bits_request.cc
index 7bcb772..10c29f2 100644
--- a/net/bits_request.cc
+++ b/net/bits_request.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -32,11 +32,13 @@
 #include <atlbase.h>
 #include <atlstr.h>
 #include <functional>
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/utils.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_impersonation.h"
+#include "omaha/base/utils.h"
+#include "omaha/net/bits_job_callback.h"
 #include "omaha/net/bits_utils.h"
 #include "omaha/net/http_client.h"
 #include "omaha/net/network_request.h"
@@ -46,12 +48,14 @@
 
 namespace {
 
-const TCHAR* const kJobDescription = _T("Google Update");
+const TCHAR* const kJobDescription = kAppName;
 
-// TODO(omaha): expose polling interval, as it is likely that polling for
-// downloading files must be different than polling for retrieving smaller
-// files.
-const int kPollingIntervalMs = 1000;
+// During BITS job downloading, we setup the notification callback so that
+// BITS can notify BitsRequest whenever BITS job state changes. To avoid
+// potential notification loss we also use a max polling interval (1s) and
+// when that amount of time passes, we'll do a poll anyway even if no BITS
+// notification is received at that time.
+const LONG kPollingIntervalMs = 1000;
 
 // Returns the job priority or -1 in case of errors.
 int GetJobPriority(IBackgroundCopyJob* job) {
@@ -62,18 +66,25 @@
 
 }   // namespace
 
-
 BitsRequest::BitsRequest()
     : request_buffer_(NULL),
       request_buffer_length_(0),
+      proxy_auth_config_(NULL, CString()),
       low_priority_(false),
       is_canceled_(false),
       callback_(NULL),
       minimum_retry_delay_(-1),
       no_progress_timeout_(-1),
       current_auth_scheme_(0),
+      bits_request_callback_(NULL),
+      last_progress_report_tick_(0),
       creds_set_scheme_unknown_(false) {
-  VERIFY1(SUCCEEDED(GetBitsManager(&bits_manager_)));
+  GetBitsManager(&bits_manager_);
+
+  // Creates a auto-reset event for BITS job change notifications.
+  reset(bits_job_status_changed_event_,
+        ::CreateEvent(NULL, false, false, NULL));
+  ASSERT1(valid(bits_job_status_changed_event_));
 }
 
 // Once this instance connects to a BITS job, it either completes the job
@@ -91,9 +102,64 @@
   }
 }
 
+HRESULT BitsRequest::SetupBitsCallback() {
+  ASSERT1(request_state_.get());
+  ASSERT1(request_state_->bits_job);
+
+  __mutexScope(lock_);
+
+  VERIFY1(::ResetEvent(get(bits_job_status_changed_event_)));
+
+  RemoveBitsCallback();
+
+  HRESULT hr = BitsJobCallback::Create(this, &bits_request_callback_);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // In the /ua case, the high-integrity COM server is running as SYSTEM, and
+  // impersonating a medium-integrity token. Calling SetNotifyInterface() with
+  // impersonation will give E_ACCESSDENIED. This is because the COM server only
+  // accepts high integrity incoming calls, as per the DACL set in
+  // InitializeServerSecurity(). Calling AsSelf with EOAC_DYNAMIC_CLOAKING
+  // sets high-integrity SYSTEM as the identity for the callback COM proxy on
+  // the BITS side.
+  hr = StdCallAsSelfAndImpersonate1(
+      request_state_->bits_job.p,
+      &IBackgroundCopyJob::SetNotifyInterface,
+      static_cast<IUnknown*>(bits_request_callback_));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = request_state_->bits_job->SetNotifyFlags(BG_NOTIFY_JOB_TRANSFERRED |
+                                                BG_NOTIFY_JOB_ERROR |
+                                                BG_NOTIFY_JOB_MODIFICATION);
+  return hr;
+}
+
+void BitsRequest::RemoveBitsCallback() {
+  if (bits_request_callback_ != NULL) {
+    bits_request_callback_->RemoveReferenceToBitsRequest();
+
+    bits_request_callback_->Release();
+    bits_request_callback_ = NULL;
+  }
+
+  if (request_state_.get() && request_state_->bits_job) {
+    request_state_->bits_job->SetNotifyInterface(NULL);
+  }
+}
+
+void BitsRequest::OnBitsJobStateChanged() {
+  VERIFY1(::SetEvent(get(bits_job_status_changed_event_)));
+}
+
 HRESULT BitsRequest::Close() {
   NET_LOG(L3, (_T("[BitsRequest::Close]")));
   __mutexBlock(lock_) {
+    RemoveBitsCallback();
+
     if (request_state_.get()) {
       VERIFY1(SUCCEEDED(CancelBitsJob(request_state_->bits_job)));
     }
@@ -105,11 +171,35 @@
 HRESULT BitsRequest::Cancel() {
   NET_LOG(L3, (_T("[BitsRequest::Cancel]")));
   __mutexBlock(lock_) {
+    RemoveBitsCallback();
+
     is_canceled_ = true;
     if (request_state_.get()) {
       VERIFY1(SUCCEEDED(CancelBitsJob(request_state_->bits_job)));
     }
   }
+
+  OnBitsJobStateChanged();
+  return S_OK;
+}
+
+HRESULT BitsRequest::Pause() {
+  NET_LOG(L3, (_T("[BitsRequest::Pause]")));
+  __mutexBlock(lock_) {
+    if (request_state_.get()) {
+      VERIFY1(SUCCEEDED(PauseBitsJob(request_state_->bits_job)));
+    }
+  }
+  return S_OK;
+}
+
+HRESULT BitsRequest::Resume() {
+  NET_LOG(L3, (_T("[BitsRequest::Resume]")));
+  __mutexBlock(lock_) {
+    if (request_state_.get()) {
+      VERIFY1(SUCCEEDED(ResumeBitsJob(request_state_->bits_job)));
+    }
+  }
   return S_OK;
 }
 
@@ -213,6 +303,7 @@
   if (FAILED(hr)) {
     return hr;
   }
+
   return S_OK;
 }
 
@@ -232,33 +323,72 @@
       return hr;
     }
   }
-  if (no_progress_timeout_ != -1) {
-    ASSERT1(no_progress_timeout_ >= 0);
-    hr = request_state_->bits_job->SetNoProgressTimeout(no_progress_timeout_);
+
+  // Always set no_progress_timeout to 0 for foreground jobs which means the
+  // jobs in transient error state will be immediately moved to error state.
+  int no_progress_timeout = low_priority_ ? no_progress_timeout_ : 0;
+
+  if (no_progress_timeout != -1) {
+    ASSERT1(no_progress_timeout >= 0);
+    hr = request_state_->bits_job->SetNoProgressTimeout(no_progress_timeout);
     if (FAILED(hr)) {
       return hr;
     }
   }
+
+  SetJobCustomHeaders();
+  return S_OK;
+}
+
+HRESULT BitsRequest::SetJobCustomHeaders() {
+  NET_LOG(L3, (_T("[BitsRequest::SetJobCustomHeaders][%s]"),
+               additional_headers_));
+  ASSERT1(request_state_.get());
+  ASSERT1(request_state_->bits_job);
+
+  if (additional_headers_.IsEmpty()) {
+    return S_OK;
+  }
+
+  CComPtr<IBackgroundCopyJobHttpOptions> http_options;
+  HRESULT hr = request_state_->bits_job->QueryInterface(&http_options);
+  if (FAILED(hr)) {
+    NET_LOG(LW, (_T("[QI IBackgroundCopyJobHttpOptions failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = http_options->SetCustomHeaders(additional_headers_);
+  if (FAILED(hr)) {
+    NET_LOG(LE, (_T("[SetCustomHeaders failed][0x%x]"), hr));
+    return hr;
+  }
+
   return S_OK;
 }
 
 HRESULT BitsRequest::DetectManualProxy() {
-  if (NetworkConfig::GetAccessType(network_config_) !=
+  if (NetworkConfig::GetAccessType(proxy_config_) !=
       WINHTTP_ACCESS_TYPE_AUTO_DETECT) {
     return S_OK;
   }
 
+  NetworkConfig* network_config = NULL;
+  NetworkConfigManager& network_manager = NetworkConfigManager::Instance();
+  HRESULT hr = network_manager.GetUserNetworkConfig(&network_config);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
   HttpClient::ProxyInfo proxy_info = {0};
-  HRESULT hr = NetworkConfig::Instance().GetProxyForUrl(
-      url_,
-      network_config_.auto_config_url,
-      &proxy_info);
+  hr = network_config->GetProxyForUrl(url_,
+                                      proxy_config_.auto_config_url,
+                                      &proxy_info);
   if (SUCCEEDED(hr) &&
       proxy_info.access_type == WINHTTP_ACCESS_TYPE_NAMED_PROXY) {
-    network_config_.auto_detect = false;
-    network_config_.auto_config_url.Empty();
-    network_config_.proxy = proxy_info.proxy;
-    network_config_.proxy_bypass = proxy_info.proxy_bypass;
+    proxy_config_.auto_detect = false;
+    proxy_config_.auto_config_url.Empty();
+    proxy_config_.proxy = proxy_info.proxy;
+    proxy_config_.proxy_bypass = proxy_info.proxy_bypass;
   }
 
   ::GlobalFree(const_cast<wchar_t*>(proxy_info.proxy));
@@ -277,13 +407,13 @@
 
   DetectManualProxy();
 
-  int access_type = NetworkConfig::GetAccessType(network_config_);
+  int access_type = NetworkConfig::GetAccessType(proxy_config_);
   if (access_type == WINHTTP_ACCESS_TYPE_AUTO_DETECT) {
     proxy_usage = BG_JOB_PROXY_USAGE_AUTODETECT;
   } else if (access_type == WINHTTP_ACCESS_TYPE_NAMED_PROXY) {
     proxy_usage = BG_JOB_PROXY_USAGE_OVERRIDE;
-    proxy = network_config_.proxy;
-    proxy_bypass = network_config_.proxy_bypass;
+    proxy = proxy_config_.proxy;
+    proxy_bypass = proxy_config_.proxy_bypass;
   }
   HRESULT hr = request_state_->bits_job->SetProxySettings(proxy_usage,
                                                           proxy,
@@ -316,7 +446,7 @@
   NET_LOG(L3, (_T("[BitsRequest::DoSend]")));
 
   if (is_canceled_) {
-    return OMAHA_NET_E_REQUEST_CANCELLED;
+    return GOOPDATE_E_CANCELLED;
   }
 
   HRESULT hr = SetJobProperties();
@@ -327,6 +457,10 @@
   if (FAILED(hr)) {
     return hr;
   }
+  hr = SetupBitsCallback();
+  if (FAILED(hr)) {
+    return hr;
+  }
   hr = request_state_->bits_job->Resume();
   if (FAILED(hr)) {
     return hr;
@@ -345,7 +479,7 @@
 
   for (;;) {
     if (is_canceled_) {
-      return OMAHA_NET_E_REQUEST_CANCELLED;
+      return GOOPDATE_E_CANCELLED;
     }
 
     BG_JOB_STATE job_state = BG_JOB_STATE_ERROR;
@@ -389,22 +523,31 @@
         return request_state_->http_status_code ? S_OK : hr;
 
       case BG_JOB_STATE_TRANSFERRED:
+        NotifyProgress();
         hr = request_state_->bits_job->Complete();
         if (SUCCEEDED(hr) || BG_S_UNABLE_TO_DELETE_FILES == hr) {
           // Assume the status code is 200 if the transfer completed. BITS does
-          // ot provide access to the status code.
+          // not provide access to the status code.
           request_state_->http_status_code = HTTP_STATUS_OK;
 
           if (creds_set_scheme_unknown_) {
             // Bits job completed successfully. If we have a valid username, we
             // record the auth scheme with the NetworkConfig, so it can be used
             // in the future within this process.
-            ASSERT1(BitsToWinhttpProxyAuthScheme(current_auth_scheme_) !=
-                    UNKNOWN_AUTH_SCHEME);
+            uint32 win_http_scheme =
+                BitsToWinhttpProxyAuthScheme(current_auth_scheme_);
+            ASSERT1(win_http_scheme != UNKNOWN_AUTH_SCHEME);
             bool is_https = String_StartsWith(url_, kHttpsProtoScheme, true);
-            VERIFY1(SUCCEEDED(NetworkConfig::Instance().SetProxyAuthScheme(
-                network_config_.proxy, is_https,
-                BitsToWinhttpProxyAuthScheme(current_auth_scheme_))));
+
+            NetworkConfig* network_config = NULL;
+            NetworkConfigManager& nm = NetworkConfigManager::Instance();
+            HRESULT hr = nm.GetUserNetworkConfig(&network_config);
+            if (FAILED(hr)) {
+              return hr;
+            }
+
+            VERIFY1(SUCCEEDED(network_config->SetProxyAuthScheme(
+                proxy_config_.proxy, is_https, win_http_scheme)));
           }
 
           return S_OK;
@@ -413,40 +556,36 @@
         }
 
       case BG_JOB_STATE_SUSPENDED:
-        // Pausing downloads is not supported yet.
-        ASSERT1(false);
-        return E_FAIL;
+        break;
 
       case BG_JOB_STATE_ACKNOWLEDGED:
         ASSERT1(false);
         return S_OK;
 
       case BG_JOB_STATE_CANCELLED:
-        return OMAHA_NET_E_REQUEST_CANCELLED;
+        return GOOPDATE_E_CANCELLED;
     };
 
-    ::Sleep(kPollingIntervalMs);
+    DWORD wait_result = ::WaitForSingleObject(
+        get(bits_job_status_changed_event_), kPollingIntervalMs);
+    if (wait_result == WAIT_FAILED) {
+      ::Sleep(kPollingIntervalMs);
+    }
   }
 }
 
 HRESULT BitsRequest::OnStateTransferring() {
-  if (!callback_) {
+  // BITS could call JobModification very often during transfer so we
+  // do report meter here to avoid too many progress notifications.
+  uint32 now = GetTickCount();
+
+  if (now >= last_progress_report_tick_ &&
+      now - last_progress_report_tick_ < kJobProgressReportMinimumIntervalMs) {
     return S_OK;
   }
-  BG_JOB_PROGRESS progress = {0};
-  HRESULT hr = request_state_->bits_job->GetProgress(&progress);
-  if (FAILED(hr)) {
-    return hr;
-  }
 
-  ASSERT1(progress.FilesTotal == 1);
-  ASSERT1(progress.BytesTransferred <= INT_MAX);
-  ASSERT1(progress.BytesTotal <= INT_MAX);
-  callback_->OnProgress(static_cast<int>(progress.BytesTransferred),
-                        static_cast<int>(progress.BytesTotal),
-                        WINHTTP_CALLBACK_STATUS_READ_COMPLETE,
-                        NULL);
-  return S_OK;
+  last_progress_report_tick_ = now;
+  return NotifyProgress();
 }
 
 HRESULT BitsRequest::OnStateError() {
@@ -468,8 +607,8 @@
   request_state_->http_status_code = GetHttpStatusFromBitsError(error_code);
 
   if (error_code == BG_E_HTTP_ERROR_407) {
-    hr = !creds_set_scheme_unknown_ ? HandleProxyAuthenticationError() :
-                                      HandleProxyAuthenticationErrorCredsSet();
+    hr = creds_set_scheme_unknown_ ? HandleProxyAuthenticationErrorCredsSet() :
+                                     HandleProxyAuthenticationError();
     if (SUCCEEDED(hr)) {
       return S_OK;
     }
@@ -479,15 +618,42 @@
   return error_code;
 }
 
+HRESULT BitsRequest::NotifyProgress() {
+  if (!callback_) {
+    return S_OK;
+  }
+  BG_JOB_PROGRESS progress = {0};
+  HRESULT hr = request_state_->bits_job->GetProgress(&progress);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  ASSERT1(progress.FilesTotal == 1);
+  ASSERT1(progress.BytesTransferred <= INT_MAX);
+  ASSERT1(progress.BytesTotal <= INT_MAX);
+  callback_->OnProgress(static_cast<int>(progress.BytesTransferred),
+                        static_cast<int>(progress.BytesTotal),
+                        WINHTTP_CALLBACK_STATUS_READ_COMPLETE,
+                        NULL);
+  return S_OK;
+}
+
 HRESULT BitsRequest::GetProxyCredentials() {
   CString username;
   CString password;
   uint32 auth_scheme = UNKNOWN_AUTH_SCHEME;
   bool is_https = String_StartsWith(url_, kHttpsProtoScheme, true);
 
-  if (!NetworkConfig::Instance().GetProxyCredentials(true, false,
-          network_config_.proxy, is_https, &username, &password,
-          &auth_scheme)) {
+  NetworkConfig* network_config = NULL;
+  NetworkConfigManager& network_manager = NetworkConfigManager::Instance();
+  HRESULT hr = network_manager.GetUserNetworkConfig(&network_config);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  if (!network_config->GetProxyCredentials(true, false,
+          proxy_config_.proxy, proxy_auth_config_, is_https, &username,
+          &password, &auth_scheme)) {
     OPT_LOG(LE, (_T("[BitsRequest::GetProxyCredentials failed]")));
     return E_ACCESSDENIED;
   }
@@ -507,9 +673,9 @@
   // as well, however, we do not want to leak passwords by mistake.
   for (int scheme = BG_AUTH_SCHEME_DIGEST; scheme <= BG_AUTH_SCHEME_NEGOTIATE;
        ++scheme) {
-    HRESULT hr = SetProxyAuthCredentials(request_state_->bits_job,
-                                         CStrBuf(username), CStrBuf(password),
-                                         static_cast<BG_AUTH_SCHEME>(scheme));
+    hr = SetProxyAuthCredentials(request_state_->bits_job,
+                                 CStrBuf(username), CStrBuf(password),
+                                 static_cast<BG_AUTH_SCHEME>(scheme));
     if (FAILED(hr)) {
       OPT_LOG(LE, (_T("[BitsRequest::GetProxyCredentials][0x%08x][%s]"),
                    hr, BitsAuthSchemeToString(scheme)));
diff --git a/net/bits_request.h b/net/bits_request.h
index 6c5ec5b..aa1aa91 100644
--- a/net/bits_request.h
+++ b/net/bits_request.h
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -19,7 +19,6 @@
 // BITS is sending the following string as user agent:
 //    User-Agent: Microsoft BITS/6.6
 // where the version seems to be the version of %windir%\System32\QMgr.dll.
-// The user agent of BITS can't be controlled programmatically.
 //
 // TODO(omaha): the class interface is not stable yet, as a few more
 // getters and setters are still needed.
@@ -31,12 +30,15 @@
 #include <bits.h>
 #include <vector>
 #include "base/basictypes.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/common/utils.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/utils.h"
 #include "omaha/net/http_request.h"
 
 namespace omaha {
 
+class BitsJobCallback;
+
 class BitsRequest : public HttpRequestInterface {
  public:
   BitsRequest();
@@ -48,6 +50,10 @@
 
   virtual HRESULT Cancel();
 
+  virtual HRESULT Pause();
+
+  virtual HRESULT Resume();
+
   virtual std::vector<uint8> GetResponse() const {
     return std::vector<uint8>();
   }
@@ -78,8 +84,8 @@
     request_buffer_length_ = buffer_length;
   }
 
-  virtual void set_network_configuration(const Config& network_config) {
-    network_config_ = network_config;
+  virtual void set_proxy_configuration(const ProxyConfig& proxy_config) {
+    proxy_config_ = proxy_config;
   }
 
   // Sets the filename to receive the response instead of the memory buffer.
@@ -97,12 +103,22 @@
     additional_headers_ = additional_headers;
   }
 
+  // This request always uses the specified protocol so it is fine to ignore
+  // this attribute.
+  virtual void set_preserve_protocol(bool preserve_protocol) {
+    UNREFERENCED_PARAMETER(preserve_protocol);
+  }
+
   virtual CString user_agent() const { return user_agent_; }
 
   virtual void set_user_agent(const CString& user_agent) {
     user_agent_ = user_agent;
   }
 
+  virtual void set_proxy_auth_config(const ProxyAuthConfig& proxy_auth_config) {
+    proxy_auth_config_ = proxy_auth_config;
+  }
+
   // Sets the minimum length of time that BITS waits after encountering a
   // transient error condition before trying to transfer the file.
   // The default value is 600 seconds.
@@ -118,6 +134,9 @@
     no_progress_timeout_ = no_progress_timeout;
   }
 
+  // Handles that BITS job state has changed.
+  void OnBitsJobStateChanged();
+
  private:
   // Sets invariant job properties, such as the filename and the description.
   // These parameters can't change over the job life time.
@@ -126,6 +145,10 @@
   // Sets non-invariant job properties.
   HRESULT SetJobProperties();
 
+  // Sets additional_headers_ on the Job if IBackgroundCopyJobHttpOptions is
+  // supported.
+  HRESULT SetJobCustomHeaders();
+
   // Uses the SimpleRequest HttpClient to detect the proxy for the current
   // request.
   HRESULT DetectManualProxy();
@@ -156,9 +179,20 @@
   // are already set on the BITS job.
   HRESULT HandleProxyAuthenticationErrorCredsSet();
 
+  // Calls back with progress information if available.
+  HRESULT NotifyProgress();
+
   int WinHttpToBitsProxyAuthScheme(uint32 winhttp_scheme);
   uint32 BitsToWinhttpProxyAuthScheme(int bits_scheme);
 
+  // Sets up BITS callback so BITS can send job status changes to this class.
+  HRESULT SetupBitsCallback();
+
+  // Stops BITS callback to send further job change notifications to this class.
+  // It is important to do this before the object goes out of scope since BITS
+  // callback needs to reference this object.
+  void RemoveBitsCallback();
+
   // Creates or opens an existing job.
   // 'is_created' is true if the job has been created or false if the job
   // has been opened.
@@ -187,7 +221,8 @@
   size_t      request_buffer_length_;   // Length of the request body.
   CString additional_headers_;
   CString user_agent_;
-  Config network_config_;
+  ProxyAuthConfig proxy_auth_config_;
+  ProxyConfig proxy_config_;
   bool low_priority_;
   bool is_canceled_;
   HINTERNET session_handle_;  // Not owned by this class.
@@ -201,11 +236,22 @@
   // them out in sequence.
   bool creds_set_scheme_unknown_;
 
+  // Event that is set when the BITS job state is changed.
+  scoped_event bits_job_status_changed_event_;
+
+  BitsJobCallback*  bits_request_callback_;
+  uint32 last_progress_report_tick_;
+
   scoped_ptr<TransientRequestState> request_state_;
 
   // See http://b/1189928
   CComPtr<IBackgroundCopyManager> bits_manager_;
 
+  // BITS could call JobModification() callback very often during job transfer.
+  // This minumum interval is to prevent reporting job progress too often to
+  // BitsRequest.
+  static const int kJobProgressReportMinimumIntervalMs = 200;
+
   DISALLOW_EVIL_CONSTRUCTORS(BitsRequest);
 };
 
diff --git a/net/bits_request_unittest.cc b/net/bits_request_unittest.cc
index cb8f426..764809c 100644
--- a/net/bits_request_unittest.cc
+++ b/net/bits_request_unittest.cc
@@ -15,7 +15,7 @@
 
 #include <windows.h>
 #include <winhttp.h>
-#include "omaha/common/app_util.h"
+#include "omaha/base/app_util.h"
 #include "omaha/net/bits_request.h"
 #include "omaha/testing/unit_test.h"
 
@@ -23,6 +23,10 @@
 
 // Moves the job to error state if no progress at all is made for 10 seconds.
 TEST(BitsRequestTest, Send) {
+  if (IsTestRunByLocalSystem()) {
+    return;
+  }
+
   BitsRequest bits_request;
   bits_request.set_no_progress_timeout(10);   // 10 seconds.
   CString temp_dir = app_util::GetTempDir();
diff --git a/net/bits_utils.cc b/net/bits_utils.cc
index f8beb5c..37a4ed9 100644
--- a/net/bits_utils.cc
+++ b/net/bits_utils.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -18,11 +18,11 @@
 #include <windows.h>
 #include <winhttp.h>
 #include <cstring>
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/scoped_ptr_cotask.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/scoped_ptr_cotask.h"
 
 namespace omaha {
 
@@ -133,6 +133,39 @@
   return S_OK;
 }
 
+HRESULT PauseBitsJob(IBackgroundCopyJob* job) {
+  if (job) {
+    BG_JOB_STATE job_state = BG_JOB_STATE_ERROR;
+    HRESULT hr = job->GetState(&job_state);
+    if (SUCCEEDED(hr) &&
+        job_state != BG_JOB_STATE_TRANSFERRED &&
+        job_state != BG_JOB_STATE_ACKNOWLEDGED &&
+        job_state != BG_JOB_STATE_CANCELLED) {
+      HRESULT hr = job->Suspend();
+      if (FAILED(hr)) {
+        NET_LOG(LW, (_T("[PauseBitsJob failed][0x%08x]"), hr));
+      }
+      return hr;
+    }
+  }
+  return S_OK;
+}
+
+HRESULT ResumeBitsJob(IBackgroundCopyJob* job) {
+  if (job) {
+    BG_JOB_STATE job_state = BG_JOB_STATE_ERROR;
+    HRESULT hr = job->GetState(&job_state);
+    if (SUCCEEDED(hr) && job_state == BG_JOB_STATE_SUSPENDED) {
+      HRESULT hr = job->Suspend();
+      if (FAILED(hr)) {
+        NET_LOG(LW, (_T("[ResumeBitsJob failed][0x%08x]"), hr));
+      }
+      return hr;
+    }
+  }
+  return S_OK;
+}
+
 #define RETURN_TSTR(x) case (x): return _T(#x)
 CString JobStateToString(BG_JOB_STATE job_state) {
   switch (job_state) {
diff --git a/net/bits_utils.h b/net/bits_utils.h
index bc79e25..9a9318c 100644
--- a/net/bits_utils.h
+++ b/net/bits_utils.h
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -93,6 +93,10 @@
 // Cancels a job.
 HRESULT CancelBitsJob(IBackgroundCopyJob* job);
 
+HRESULT PauseBitsJob(IBackgroundCopyJob* job);
+
+HRESULT ResumeBitsJob(IBackgroundCopyJob* job);
+
 // Converts a job state to a string.
 CString JobStateToString(BG_JOB_STATE job_state);
 
diff --git a/net/browser_request.cc b/net/browser_request.cc
index 51aad09..d55eae6 100644
--- a/net/browser_request.cc
+++ b/net/browser_request.cc
@@ -37,9 +37,9 @@
 #include "omaha/net/browser_request.h"
 #include <atlbase.h>
 #include <atlcom.h>
-#include "omaha/common/logging.h"
-#include "omaha/common/system.h"
-#include "omaha/common/vista_utils.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/system.h"
+#include "omaha/base/vista_utils.h"
 #include "omaha/goopdate/google_update_proxy.h"
 
 namespace omaha {
diff --git a/net/browser_request.h b/net/browser_request.h
index f7ffd2e..831f9e7 100644
--- a/net/browser_request.h
+++ b/net/browser_request.h
@@ -20,6 +20,7 @@
 #define OMAHA_NET_BROWSER_REQUEST_H__
 
 #include <list>
+#include "goopdate/omaha3_idl.h"
 #include "omaha/net/urlmon_request.h"
 
 namespace omaha {
diff --git a/net/browser_request_unittest.cc b/net/browser_request_unittest.cc
index ad02a0e..494bbae 100644
--- a/net/browser_request_unittest.cc
+++ b/net/browser_request_unittest.cc
@@ -16,8 +16,8 @@
 
 #include <windows.h>
 #include <winhttp.h>
-#include "omaha/common/app_util.h"
-#include "omaha/common/string.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/string.h"
 #include "omaha/net/browser_request.h"
 #include "omaha/testing/unit_test.h"
 
diff --git a/net/build.scons b/net/build.scons
index 907550c..52d959b 100644
--- a/net/build.scons
+++ b/net/build.scons
@@ -37,8 +37,9 @@
 inputs = [
     'bind_status_callback.cc',
     'bits_request.cc',
+    'bits_job_callback.cc',
     'bits_utils.cc',
-    'browser_request.cc',
+    # 'browser_request.cc',   # it has a dependency on goopdate's idl
     'cup_request.cc',
     'cup_utils.cc',
     'detector.cc',
@@ -53,8 +54,9 @@
     'proxy_auth.cc',
     #'wininet.cc',      # we don't have support for wininet yet
     'winhttp.cc',
+    'winhttp_adapter.cc',
     'winhttp_vtable.cc',
 ]
 
 # Build these into a library.
-local_env.ComponentLibrary('net', inputs)
+local_env.ComponentStaticLibrary('net', inputs)
diff --git a/net/cup_request.cc b/net/cup_request.cc
index 66bc957..87db7f6 100644
--- a/net/cup_request.cc
+++ b/net/cup_request.cc
@@ -1,4 +1,4 @@
-// Copyright 2008-2009 Google Inc.
+// Copyright 2008-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -21,18 +21,19 @@
 #include <atlconv.h>
 #include <atlstr.h>
 #include <vector>
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/encrypt.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/path.h"
-#include "omaha/common/security/b64.h"
-#include "omaha/common/security/hmac.h"
-#include "omaha/common/security/rsa.h"
-#include "omaha/common/security/sha.h"
-#include "omaha/common/string.h"
-#include "omaha/common/utils.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/encrypt.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/security/b64.h"
+#include "omaha/base/security/hmac.h"
+#include "omaha/base/security/rsa.h"
+#include "omaha/base/security/sha.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
 #include "omaha/net/cup_utils.h"
 #include "omaha/net/http_client.h"
 #include "omaha/net/net_utils.h"
@@ -53,6 +54,8 @@
   HRESULT Close();
   HRESULT Send();
   HRESULT Cancel();
+  HRESULT Pause();
+  HRESULT Resume();
   std::vector<uint8> GetResponse() const;
   HRESULT QueryHeadersString(uint32 info_level,
                                     const TCHAR* name,
@@ -65,13 +68,15 @@
   void set_session_handle(HINTERNET session_handle);
   void set_url(const CString& url);
   void set_request_buffer(const void* buffer, size_t buffer_length);
-  void set_network_configuration(const Config& network_config);
+  void set_proxy_configuration(const ProxyConfig& proxy_config);
   void set_filename(const CString& filename);
   void set_low_priority(bool low_priority);
   void set_callback(NetworkRequestCallback* callback);
   void set_additional_headers(const CString& additional_headers);
+  void set_preserve_protocol(bool preserve_protocol);
   CString user_agent() const;
   void set_user_agent(const CString& user_agent);
+  void set_proxy_auth_config(const ProxyAuthConfig& proxy_auth_config);
 
  private:
   HRESULT DoSend();
@@ -87,8 +92,9 @@
   // Saves the {sk, c} credentials. The key is encrypted before saving it.
   HRESULT SaveCredentials(const std::vector<uint8>& sk, const CStringA& c);
 
-  // Replaces the https protocol scheme with http.
-  CString BuildInnerRequestUrl(const CString& url);
+  // Replaces the https protocol scheme with http if changing protocol is
+  // allowed.
+  HRESULT BuildInnerRequestUrl(const CString& url, CString* inner_url);
 
   // The transient state of the request, so that we can start always with a
   // clean slate even though the same instance is being reuse across requests.
@@ -114,6 +120,9 @@
   scoped_ptr<TransientCupState> cup_;
 
   CStringA url_;                        // The original url.
+  CString additional_headers_;
+  bool preserve_protocol_;              // Should not change request scheme if
+                                        // true.
   const void* request_buffer_;          // Contains the request body for POST.
   size_t      request_buffer_length_;   // Length of the request body.
   std::vector<uint8> entropy_;          // Optional entropy pass by the caller,
@@ -146,7 +155,8 @@
 ;   // NOLINT
 
 CupRequestImpl::CupRequestImpl(HttpRequestInterface* http_request)
-    : request_buffer_(NULL),
+    : preserve_protocol_(false),
+      request_buffer_(NULL),
       request_buffer_length_(0),
       public_key_(NULL) {
   ASSERT1(http_request);
@@ -230,9 +240,9 @@
 
   // Compute the versioned challenge (v|w) as
   // decimal-v:base64-encoded-rsa-wrapper.
-  cup_->vw.Format("%d:%s",
-                  rsa_->version(),
-                  cup_utils::B64Encode(&cup_->r.front(), cup_->r.size()));
+  SafeCStringAFormat(&cup_->vw, "%d:%s",
+                     rsa_->version(),
+                     cup_utils::B64Encode(&cup_->r.front(), cup_->r.size()));
 
   // Compute the url of the CUP request.
   // Append a query string or append to the existing query string if any.
@@ -404,62 +414,55 @@
 HRESULT CupRequestImpl::LoadCredentials(std::vector<uint8>* sk, CStringA* c) {
   ASSERT1(sk);
   ASSERT1(c);
-  NetworkConfig& network_config = NetworkConfig::Instance();
+  NetworkConfig* network_config = NULL;
+  NetworkConfigManager& network_manager = NetworkConfigManager::Instance();
+  HRESULT hr = network_manager.GetUserNetworkConfig(&network_config);
+  if (FAILED(hr)) {
+    return hr;
+  }
   CupCredentials cup_credentials;
-  HRESULT hr = network_config.GetCupCredentials(&cup_credentials);
-  if (FAILED(hr)) {
-    return hr;
+  hr = network_config->GetCupCredentials(&cup_credentials);
+  if (SUCCEEDED(hr)) {
+    sk->swap(cup_credentials.sk);
+    *c = cup_credentials.c;
   }
-  std::vector<uint8> decrypted_sk;
-  hr = DecryptData(NULL,
-                   0,
-                   &cup_credentials.sk.front(),
-                   cup_credentials.sk.size(),
-                   &decrypted_sk);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  sk->swap(decrypted_sk);
-  c->SetString(cup_credentials.c);
-  return S_OK;
+  return hr;
 }
 
 HRESULT CupRequestImpl::SaveCredentials(const std::vector<uint8>& sk,
                                         const CStringA& c) {
-  // Update the client persistent state if the response has been validated.
-  // The client key is encrypted while on the disk.
-  NetworkConfig& network_config = NetworkConfig::Instance();
-  CupCredentials cup_credentials;
-  cup_credentials.c.SetString(c);
-  HRESULT hr = EncryptData(NULL,
-                           0,
-                           &sk.front(),
-                           sk.size(),
-                           &cup_credentials.sk);
+  NetworkConfig* network_config = NULL;
+  NetworkConfigManager& network_manager = NetworkConfigManager::Instance();
+  HRESULT hr = network_manager.GetUserNetworkConfig(&network_config);
   if (FAILED(hr)) {
     return hr;
   }
-  // Try to persist the new client credentials.
-  hr = network_config.SetCupCredentials(&cup_credentials);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  return S_OK;
+
+  CupCredentials  cup_credentials;
+  cup_credentials.sk = sk;
+  cup_credentials.c = c;
+  return network_config->SetCupCredentials(&cup_credentials);
 }
 
 HRESULT CupRequestImpl::DoSend() {
   // The url of the inner request includes the versioned challenge.
   // It replaces the protocol from https to http since CUP over https is
   // not supported.
-  http_request_->set_url(BuildInnerRequestUrl(CString(cup_->request_url)));
+  CString inner_request_url;
+  HRESULT hr = BuildInnerRequestUrl(CString(cup_->request_url),
+                                    &inner_request_url);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  http_request_->set_url(inner_request_url);
 
   // Prepare additional headers to send.
-  CString additional_headers;
+  CString additional_headers(additional_headers_);
 
   // The client proof (cp) is sent as the "If-Match" header.
   ASSERT1(!cup_->cp.IsEmpty());
   CStringA if_match_header;
-  if_match_header.Format("\"%s\"", cup_->cp);
+  SafeCStringAFormat(&if_match_header, "\"%s\"", cup_->cp);
   additional_headers += HttpClient::BuildRequestHeader(_T("If-Match"),
                                                        CA2T(if_match_header));
 
@@ -473,13 +476,14 @@
   NET_LOG(L5, (_T("[CUP request][%s]"),
                BufferToPrintableString(request_buffer_,
                                        request_buffer_length_)));
-  HRESULT hr = http_request_->Send();
+  hr = http_request_->Send();
   if (FAILED(hr)) {
     return hr;
   }
   http_request_->GetResponse().swap(cup_->response);
   int status_code(http_request_->GetHttpStatusCode());
-  if (status_code != HTTP_STATUS_OK) {
+  if (status_code != HTTP_STATUS_OK &&
+      status_code != HTTP_STATUS_PARTIAL_CONTENT) {
     return HRESULTFromHttpStatusCode(status_code);
   }
   NET_LOG(L5, (_T("[CUP response][%s]"),
@@ -508,6 +512,14 @@
   return http_request_->Cancel();
 }
 
+HRESULT CupRequestImpl::Pause() {
+  return http_request_->Pause();
+}
+
+HRESULT CupRequestImpl::Resume() {
+  return http_request_->Resume();
+}
+
 std::vector<uint8> CupRequestImpl::GetResponse() const {
   return http_request_->GetResponse();
 }
@@ -545,8 +557,8 @@
   http_request_->set_request_buffer(request_buffer_, request_buffer_length_);
 }
 
-void CupRequestImpl::set_network_configuration(const Config& network_config) {
-  http_request_->set_network_configuration(network_config);
+void CupRequestImpl::set_proxy_configuration(const ProxyConfig& proxy_config) {
+  http_request_->set_proxy_configuration(proxy_config);
 }
 
 void CupRequestImpl::set_filename(const CString& filename) {
@@ -562,7 +574,11 @@
 }
 
 void CupRequestImpl::set_additional_headers(const CString& additional_headers) {
-  http_request_->set_additional_headers(additional_headers);
+  additional_headers_ = additional_headers;
+}
+
+void CupRequestImpl::set_preserve_protocol(bool preserve_protocol) {
+  preserve_protocol_ = preserve_protocol;
 }
 
 CString CupRequestImpl::user_agent() const {
@@ -573,6 +589,10 @@
   http_request_->set_user_agent(user_agent);
 }
 
+void CupRequestImpl::set_proxy_auth_config(const ProxyAuthConfig& config) {
+  http_request_->set_proxy_auth_config(config);
+}
+
 void CupRequestImpl::SetEntropy(const void* data, size_t data_length) {
   ASSERT(false, (_T("Do not call from production code")));
   ASSERT1(data);
@@ -594,13 +614,23 @@
   return S_OK;
 }
 
-CString CupRequestImpl::BuildInnerRequestUrl(const CString& url) {
-  CString result(url);
-  if (String_StartsWith(result, kHttpsProtoScheme, true)) {
-    result = url.Mid(_tcslen(kHttpsProtoScheme));
-    result.Insert(0, kHttpProtoScheme);
+HRESULT CupRequestImpl::BuildInnerRequestUrl(const CString& url,
+                                             CString* inner_url) {
+  ASSERT1(inner_url);
+  if (!String_StartsWith(url, kHttpsProtoScheme, true)) {
+    *inner_url = url;
+    return S_OK;
   }
-  return result;
+
+  if (preserve_protocol_) {
+    // Request must be sent through https. So return error here so we can
+    // fall back to the next request type in the chain without CUP.
+    return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
+  }
+
+  *inner_url = url.Mid(_tcslen(kHttpsProtoScheme));
+  inner_url->Insert(0, kHttpProtoScheme);
+  return S_OK;
 }
 
 }   // namespace detail
@@ -626,6 +656,14 @@
   return impl_->Cancel();
 }
 
+HRESULT CupRequest::Pause() {
+  return impl_->Pause();
+}
+
+HRESULT CupRequest::Resume() {
+  return impl_->Resume();
+}
+
 std::vector<uint8> CupRequest::GetResponse() const {
   return impl_->GetResponse();
 }
@@ -660,8 +698,8 @@
   impl_->set_request_buffer(buffer, buffer_length);
 }
 
-void CupRequest::set_network_configuration(const Config& network_config) {
-  impl_->set_network_configuration(network_config);
+void CupRequest::set_proxy_configuration(const ProxyConfig& proxy_config) {
+  impl_->set_proxy_configuration(proxy_config);
 }
 
 void CupRequest::set_filename(const CString& filename) {
@@ -680,6 +718,10 @@
   impl_->set_additional_headers(additional_headers);
 }
 
+void CupRequest::set_preserve_protocol(bool preserve_protocol) {
+  impl_->set_preserve_protocol(preserve_protocol);
+}
+
 CString CupRequest::user_agent() const {
   return impl_->user_agent();
 }
@@ -688,6 +730,10 @@
   impl_->set_user_agent(user_agent);
 }
 
+void CupRequest::set_proxy_auth_config(const ProxyAuthConfig& config) {
+  impl_->set_proxy_auth_config(config);
+}
+
 void CupRequest::SetEntropy(const void* data, size_t data_length) {
   return impl_->SetEntropy(data, data_length);
 }
diff --git a/net/cup_request.h b/net/cup_request.h
index 12686f3..a26f92c 100644
--- a/net/cup_request.h
+++ b/net/cup_request.h
@@ -1,4 +1,4 @@
-// Copyright 2008-2009 Google Inc.
+// Copyright 2008-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -27,22 +27,6 @@
 
 namespace omaha {
 
-// The cup credentials are persisted across sessions. The sk is encrypted
-// while on the disk so only a user with the same login credentials as
-// the encryptor can decrypt it. The credentials are protected
-// using the system default security, so users can't modify each other's
-// credentials. In case of elevated administrators, the credentials are
-// protected from the non-elevated administrators, so the latter can't
-// read the keys and attack the elevated administrator.
-//
-// Cup credentials can be negotiated using either production keys or
-// test keys. There is a registry value override to specify that test keys
-// be used. For the change to be effective, the old credentials must be cleared.
-struct CupCredentials {
-  std::vector<uint8> sk;             // shared key (sk)
-  CStringA c;                        // client cookie (c)
-};
-
 namespace detail {
 
 // The implementation uses the pimpl idiom or bridge design pattern to
@@ -65,6 +49,10 @@
 
   virtual HRESULT Cancel();
 
+  virtual HRESULT Pause();
+
+  virtual HRESULT Resume();
+
   virtual std::vector<uint8> GetResponse() const;
 
   virtual HRESULT QueryHeadersString(uint32 info_level,
@@ -82,7 +70,7 @@
 
   virtual void set_request_buffer(const void* buffer, size_t buffer_length);
 
-  virtual void set_network_configuration(const Config& network_config);
+  virtual void set_proxy_configuration(const ProxyConfig& proxy_config);
 
   // Sets the filename to receive the response instead of the memory buffer.
   virtual void set_filename(const CString& filename);
@@ -93,10 +81,14 @@
 
   virtual void set_additional_headers(const CString& additional_headers);
 
+  virtual void set_preserve_protocol(bool preserve_protocol);
+
   virtual CString user_agent() const;
 
   virtual void set_user_agent(const CString& user_agent);
 
+  virtual void set_proxy_auth_config(const ProxyAuthConfig& proxy_auth_config);
+
   // Sets random bytes provided by the caller. This is useful for testing
   // purposes and it is not be called by production code.
   // Otherwise, when entropy is not provided, the implementation
diff --git a/net/cup_request_unittest.cc b/net/cup_request_unittest.cc
index dafac7d..9698393 100644
--- a/net/cup_request_unittest.cc
+++ b/net/cup_request_unittest.cc
@@ -18,9 +18,8 @@
 #include <iostream>
 #include <vector>
 #include "base/scoped_ptr.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/net/browser_request.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/vistautil.h"
 #include "omaha/net/cup_request.h"
 #include "omaha/net/network_config.h"
 #include "omaha/net/simple_request.h"
@@ -39,25 +38,26 @@
 
 class CupRequestTest : public testing::Test {
  protected:
-  CupRequestTest() : are_browser_objects_available(false) {
-    BrowserRequest request;
-    are_browser_objects_available = !request.objects_.empty();
-  }
+  CupRequestTest() {}
 
   static void SetUpTestCase() {
-    NetworkConfig& network_config(NetworkConfig::Instance());
-    network_config.SetCupCredentials(NULL);
+    NetworkConfig* network_config = NULL;
+    EXPECT_HRESULT_SUCCEEDED(
+        NetworkConfigManager::Instance().GetUserNetworkConfig(&network_config));
+    network_config->SetCupCredentials(NULL);
 
     // For debugging purposes, try FF configuration first.
-    network_config.Clear();
-    network_config.Add(new FirefoxProxyDetector());
-    EXPECT_HRESULT_SUCCEEDED(network_config.Detect());
+    network_config->Clear();
+    network_config->Add(new FirefoxProxyDetector());
+    EXPECT_HRESULT_SUCCEEDED(network_config->Detect());
   }
 
   static void TearDownTestCase() {
-    NetworkConfig& network_config(NetworkConfig::Instance());
-    network_config.SetCupCredentials(NULL);
-    network_config.Clear();
+    NetworkConfig* network_config = NULL;
+    EXPECT_HRESULT_SUCCEEDED(
+        NetworkConfigManager::Instance().GetUserNetworkConfig(&network_config));
+    network_config->SetCupCredentials(NULL);
+    network_config->Clear();
   }
 
   void DoRequest(HttpRequestInterface* contained_request,
@@ -66,18 +66,27 @@
                  size_t request_buffer_lenght) {
     scoped_ptr<CupRequest> http_request(new CupRequest(contained_request));
 
+    NetworkConfig* network_config = NULL;
+    EXPECT_HRESULT_SUCCEEDED(
+        NetworkConfigManager::Instance().GetUserNetworkConfig(&network_config));
+    network_config->SetCupCredentials(NULL);
+
+    // For debugging purposes, try FF configuration first.
+    network_config->Clear();
+    network_config->Add(new FirefoxProxyDetector());
+    EXPECT_HRESULT_SUCCEEDED(network_config->Detect());
+
     // This will use Firefox or direct connection if FF is not installed.
-    NetworkConfig& network_config(NetworkConfig::Instance());
-    std::vector<Config> network_configurations(
-        network_config.GetConfigurations());
-    network_configurations.push_back(Config());
+    std::vector<ProxyConfig> proxy_configurations(
+        network_config->GetConfigurations());
+    proxy_configurations.push_back(ProxyConfig());
 
     // Clear CUP credentials.
-    network_config.SetCupCredentials(NULL);
+    network_config->SetCupCredentials(NULL);
 
-    HINTERNET handle = NetworkConfig::Instance().session().session_handle;
+    HINTERNET handle = network_config->session().session_handle;
     http_request->set_session_handle(handle);
-    http_request->set_network_configuration(network_configurations[0]);
+    http_request->set_proxy_configuration(proxy_configurations[0]);
     http_request->set_url(url);
     if (request_buffer) {
       http_request->set_request_buffer(request_buffer, request_buffer_lenght);
@@ -85,77 +94,59 @@
 
     // First request goes with a fresh set of client credentials.
     EXPECT_HRESULT_SUCCEEDED(http_request->Send());
-    EXPECT_EQ(http_request->GetHttpStatusCode(), HTTP_STATUS_OK);
+    int http_status = http_request->GetHttpStatusCode();
+    EXPECT_TRUE(http_status == HTTP_STATUS_OK ||
+                http_status == HTTP_STATUS_PARTIAL_CONTENT);
     std::vector<uint8> response(http_request->GetResponse());
 
     // Second request goes with cached client credentials.
     EXPECT_HRESULT_SUCCEEDED(http_request->Send());
-    EXPECT_EQ(http_request->GetHttpStatusCode(), HTTP_STATUS_OK);
+    http_status = http_request->GetHttpStatusCode();
+    EXPECT_TRUE(http_status == HTTP_STATUS_OK ||
+                http_status == HTTP_STATUS_PARTIAL_CONTENT);
     response = http_request->GetResponse();
 
-    // Check the request has a user agent.
-    CString user_agent;
-    http_request->QueryHeadersString(
-      WINHTTP_QUERY_FLAG_REQUEST_HEADERS | WINHTTP_QUERY_USER_AGENT,
-      WINHTTP_HEADER_NAME_BY_INDEX,
-      &user_agent);
-    EXPECT_STREQ(http_request->user_agent(), user_agent);
-
     // After each test run we should have some {sk, c} persisted in the
     // registry. The CUP credentials are written back when the CUP request
     // that has created them is destroyed.
     http_request.reset();
     CupCredentials cup_creds;
-    EXPECT_HRESULT_SUCCEEDED(network_config.GetCupCredentials(&cup_creds));
+    EXPECT_HRESULT_SUCCEEDED(network_config->GetCupCredentials(&cup_creds));
     EXPECT_FALSE(cup_creds.sk.empty());
     EXPECT_FALSE(cup_creds.c.IsEmpty());
 
-    network_config.SetCupCredentials(NULL);
-    EXPECT_HRESULT_FAILED(network_config.GetCupCredentials(&cup_creds));
+    network_config->SetCupCredentials(NULL);
+    EXPECT_HRESULT_FAILED(network_config->GetCupCredentials(&cup_creds));
   }
-
-  bool are_browser_objects_available;
 };
 
 TEST_F(CupRequestTest, GetSimpleRequest) {
+  if (IsTestRunByLocalSystem()) {
+    return;
+  }
+
   DoRequest(new SimpleRequest, kGetUrl, NULL, 0);
   DoRequest(new SimpleRequest, kGetUrlNoResponseBody, NULL, 0);
 }
 
-TEST_F(CupRequestTest, DISABLED_GetUrlmonRequest) {
+TEST_F(CupRequestTest, GetUrlmonRequest) {
   DoRequest(new UrlmonRequest, kGetUrl, NULL, 0);
   DoRequest(new UrlmonRequest, kGetUrlNoResponseBody, NULL, 0);
 }
 
-TEST_F(CupRequestTest, GetBrowserRequest) {
-  if (are_browser_objects_available) {
-    DoRequest(new BrowserRequest, kGetUrl, NULL, 0);
-    DoRequest(new BrowserRequest, kGetUrlNoResponseBody, NULL, 0);
-  } else {
-    std::wcout << "\tTest did not run because no browser object was available"
-               << std::endl;
-  }
-}
-
 TEST_F(CupRequestTest, PostSimpleRequest) {
+  if (IsTestRunByLocalSystem()) {
+    return;
+  }
+
   DoRequest(new SimpleRequest, kPostUrl,
             kRequestBuffer, arraysize(kRequestBuffer) - 1);
 }
 
-TEST_F(CupRequestTest, DISABLED_PostUrlmonRequest) {
+TEST_F(CupRequestTest, PostUrlmonRequest) {
   DoRequest(new UrlmonRequest, kPostUrl,
             kRequestBuffer, arraysize(kRequestBuffer) - 1);
 }
 
-TEST_F(CupRequestTest, PostBrowserRequest) {
-  if (are_browser_objects_available) {
-    DoRequest(new BrowserRequest, kPostUrl,
-              kRequestBuffer, arraysize(kRequestBuffer) - 1);
-  } else {
-    std::wcout << "\tTest did not run because no browser object was available"
-               << std::endl;
-  }
-}
-
 }   // namespace omaha
 
diff --git a/net/cup_utils.cc b/net/cup_utils.cc
index 283d1d6..2486ed5 100644
--- a/net/cup_utils.cc
+++ b/net/cup_utils.cc
@@ -17,10 +17,10 @@
 
 #include <algorithm>
 #include <vector>
-#include "omaha/common/debug.h"
-#include "omaha/common/security/b64.h"
-#include "omaha/common/security/hmac.h"
-#include "omaha/common/security/sha.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/security/b64.h"
+#include "omaha/base/security/hmac.h"
+#include "omaha/base/security/sha.h"
 
 namespace omaha {
 
diff --git a/net/cup_utils_unittest.cc b/net/cup_utils_unittest.cc
index cead7ff..7ec09af 100644
--- a/net/cup_utils_unittest.cc
+++ b/net/cup_utils_unittest.cc
@@ -16,9 +16,9 @@
 #include <windows.h>
 #include <cstring>
 #include <vector>
-#include "omaha/common/security/sha.h"
-#include "omaha/common/string.h"
-#include "omaha/common/utils.h"
+#include "omaha/base/security/sha.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
 #include "omaha/net/cup_utils.h"
 #include "omaha/testing/unit_test.h"
 
diff --git a/net/detector.cc b/net/detector.cc
index a02e1f9..47d2858 100644
--- a/net/detector.cc
+++ b/net/detector.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -20,43 +20,24 @@
 #include "omaha/net/detector.h"
 
 #include "base/scoped_ptr.h"
-#include "omaha/common/atl_regexp.h"
-#include "omaha/common/browser_utils.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/file_reader.h"
-#include "omaha/common/path.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/string.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/time.h"
+#include "omaha/base/atl_regexp.h"
+#include "omaha/base/browser_utils.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/file_reader.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/string.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/time.h"
 #include "omaha/net/http_client.h"
 #include "omaha/net/network_config.h"
 
 namespace omaha {
 
-namespace  {
-
-// Returns true if the caller's impersonation or process access token user
-// is LOCAL_SYSTEM.
-bool IsRunningAsSystem() {
-  CString sid;
-  HRESULT hr = user_info::GetCurrentThreadUser(&sid);
-  if (SUCCEEDED(hr)) {
-    return IsLocalSystemSid(sid);
-  }
-  sid.Empty();
-  hr = user_info::GetCurrentUser(NULL, NULL, &sid);
-  if (SUCCEEDED(hr)) {
-    return IsLocalSystemSid(sid);
-  }
-  return false;
-}
-
-}  // namespace
-
-HRESULT GoogleProxyDetector::Detect(Config* config) {
+HRESULT RegistryOverrideProxyDetector::Detect(ProxyConfig* config) {
   ASSERT1(config);
   RegKey reg_key;
   HRESULT hr = reg_key.Open(reg_path_, KEY_READ);
@@ -73,23 +54,27 @@
   if (FAILED(hr)) {
     return hr;
   }
-  *config = Config();
-  config->proxy.Format(_T("%s:%d"), proxy_host, proxy_port);
-  config->source = _T("Google");
+  *config = ProxyConfig();
+  SafeCStringFormat(&config->proxy, _T("%s:%d"), proxy_host, proxy_port);
+  config->source = source();
+  config->priority = ProxyConfig::PROXY_PRIORITY_OVERRIDE;
   return S_OK;
 }
 
+UpdateDevProxyDetector::UpdateDevProxyDetector()
+    : registry_detector_(MACHINE_REG_UPDATE_DEV) {
+}
+
 FirefoxProxyDetector::FirefoxProxyDetector()
     : cached_prefs_last_modified_(0),
-      cached_config_(new Config) {}
+      cached_config_(new ProxyConfig) {
+}
 
-FirefoxProxyDetector::~FirefoxProxyDetector() {}
-
-HRESULT FirefoxProxyDetector::Detect(Config* config) {
+HRESULT FirefoxProxyDetector::Detect(ProxyConfig* config) {
   ASSERT1(config);
 
   // The Firefox profile is not available when running as a local system.
-  if (IsRunningAsSystem()) {
+  if (user_info::IsRunningAsSystem()) {
     return E_FAIL;
   }
 
@@ -123,6 +108,12 @@
 
   hr = ParsePrefsFile(name, path, config);
   if (SUCCEEDED(hr) && last_modified) {
+    // If FireFox is the default brower, promotes its proxy priority.
+    BrowserType browser_type(BROWSER_UNKNOWN);
+    if (SUCCEEDED(GetDefaultBrowserType(&browser_type)) &&
+        browser_type == BROWSER_FIREFOX) {
+      config->priority = ProxyConfig::PROXY_PRIORITY_DEFAULT_BROWSER;
+    }
     NET_LOG(L4, (_T("[cache FF profile][%s]"), path));
     cached_prefs_name_          = name;
     cached_prefs_file_path_     = path;
@@ -148,13 +139,13 @@
 // user_pref("network.proxy.type", 4);
 HRESULT FirefoxProxyDetector::ParsePrefsFile(const TCHAR* name,
                                              const TCHAR* file_path,
-                                             Config* config) {
+                                             ProxyConfig* config) {
   ASSERT1(name);
   ASSERT1(file_path);
   ASSERT1(config);
 
-  *config = Config();
-  config->source = _T("FireFox");
+  *config = ProxyConfig();
+  config->source = source();
 
   // TODO(omaha): implement optimization not to parse the file again if it
   // did not change.
@@ -272,9 +263,9 @@
   // Format the proxy string.
   CString str;
   if (!http_host.IsEmpty()) {
-    str.AppendFormat(_T("http=%s"), http_host);
+    SafeCStringAppendFormat(&str, _T("http=%s"), http_host);
     if (!http_port.IsEmpty()) {
-      str.AppendFormat(_T(":%s"), http_port);
+      SafeCStringAppendFormat(&str, _T(":%s"), http_port);
     }
   }
   if (!ssl_host.IsEmpty()) {
@@ -282,9 +273,9 @@
     if (!str.IsEmpty()) {
       str += _T(';');
     }
-    str.AppendFormat(_T("https=%s"), ssl_host);
+    SafeCStringAppendFormat(&str, _T("https=%s"), ssl_host);
     if (!ssl_port.IsEmpty()) {
-      str.AppendFormat(_T(":%s"), ssl_port);
+      SafeCStringAppendFormat(&str, _T(":%s"), ssl_port);
     }
   }
 
@@ -335,7 +326,7 @@
   }
 }
 
-HRESULT DefaultProxyDetector::Detect(Config* config) {
+HRESULT DefaultProxyDetector::Detect(ProxyConfig* config) {
   ASSERT1(config);
 
   scoped_ptr<HttpClient> http_client(CreateHttpClient());
@@ -355,8 +346,8 @@
     return hr;
   }
   if (proxy_info.access_type == WINHTTP_ACCESS_TYPE_NAMED_PROXY) {
-    Config proxy_config;
-    proxy_config.source = _T("winhttp");
+    ProxyConfig proxy_config;
+    proxy_config.source = source();
     proxy_config.proxy = proxy_info.proxy;
     proxy_config.proxy_bypass = proxy_info.proxy_bypass;
     *config = proxy_config;
@@ -366,12 +357,12 @@
   }
 }
 
-HRESULT IEProxyDetector::Detect(Config* config) {
+HRESULT IEProxyDetector::Detect(ProxyConfig* config) {
   ASSERT1(config);
 
   // Internet Explorer proxy configuration is not available when running as
   // local system.
-  if (IsRunningAsSystem()) {
+  if (user_info::IsRunningAsSystem()) {
     return E_FAIL;
   }
 
@@ -391,12 +382,19 @@
   if (FAILED(hr)) {
     return hr;
   }
-  config->source = _T("IE");
+  config->source = source();
   config->auto_detect = ie_proxy_config.auto_detect;
   config->auto_config_url = ie_proxy_config.auto_config_url;
   config->proxy = ie_proxy_config.proxy;
   config->proxy_bypass = ie_proxy_config.proxy_bypass;
 
+  // If IE is the default brower, promotes its proxy priority.
+  BrowserType browser_type(BROWSER_UNKNOWN);
+  if (SUCCEEDED(GetDefaultBrowserType(&browser_type)) &&
+      browser_type == BROWSER_IE) {
+    config->priority = ProxyConfig::PROXY_PRIORITY_DEFAULT_BROWSER;
+  }
+
   ::GlobalFree(const_cast<TCHAR*>(ie_proxy_config.auto_config_url));
   ::GlobalFree(const_cast<TCHAR*>(ie_proxy_config.proxy));
   ::GlobalFree(const_cast<TCHAR*>(ie_proxy_config.proxy_bypass));
diff --git a/net/detector.h b/net/detector.h
index bc02a0e..fdbbef4 100644
--- a/net/detector.h
+++ b/net/detector.h
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -23,25 +23,39 @@
 
 namespace omaha {
 
-struct Config;
+struct ProxyConfig;
 
 class ProxyDetectorInterface {
  public:
   // Detects proxy information.
-  virtual HRESULT Detect(Config* config) = 0;
+  virtual HRESULT Detect(ProxyConfig* config) = 0;
+  virtual const TCHAR* source() = 0;
   virtual ~ProxyDetectorInterface() {}
 };
 
 // Detects proxy override information in the specified registry key.
-class GoogleProxyDetector : public ProxyDetectorInterface {
+class RegistryOverrideProxyDetector : public ProxyDetectorInterface {
  public:
-  explicit GoogleProxyDetector(const CString& reg_path)
+  explicit RegistryOverrideProxyDetector(const CString& reg_path)
       : reg_path_(reg_path) {}
 
-  virtual HRESULT Detect(Config* config);
+  virtual HRESULT Detect(ProxyConfig* config);
+  virtual const TCHAR* source() { return _T("RegistryOverride"); }
  private:
   CString reg_path_;
-  DISALLOW_EVIL_CONSTRUCTORS(GoogleProxyDetector);
+  DISALLOW_EVIL_CONSTRUCTORS(RegistryOverrideProxyDetector);
+};
+
+class UpdateDevProxyDetector : public ProxyDetectorInterface {
+ public:
+   UpdateDevProxyDetector();
+  virtual HRESULT Detect(ProxyConfig* config) {
+    return registry_detector_.Detect(config);
+  }
+  virtual const TCHAR* source() { return _T("UpdateDev"); }
+ private:
+  RegistryOverrideProxyDetector registry_detector_;
+  DISALLOW_EVIL_CONSTRUCTORS(UpdateDevProxyDetector);
 };
 
 // Detects winhttp proxy information. This is what the winhttp proxy
@@ -51,8 +65,8 @@
 class DefaultProxyDetector : public ProxyDetectorInterface {
  public:
   DefaultProxyDetector() {}
-  virtual HRESULT Detect(Config* config);
-
+  virtual HRESULT Detect(ProxyConfig* config);
+  virtual const TCHAR* source() { return _T("winhttp"); }
  private:
   DISALLOW_EVIL_CONSTRUCTORS(DefaultProxyDetector);
 };
@@ -70,15 +84,14 @@
   };
 
   FirefoxProxyDetector();
-  virtual ~FirefoxProxyDetector();
 
-  virtual HRESULT Detect(Config* config);
-
+  virtual HRESULT Detect(ProxyConfig* config);
+  virtual const TCHAR* source() { return _T("Firefox"); }
  private:
   // Parses the prefs.js file.
   HRESULT ParsePrefsFile(const TCHAR* name,
                          const TCHAR* file_path,
-                         Config* config);
+                         ProxyConfig* config);
 
   // Parse one line of the prefs file.
   void ParsePrefsLine(const char* ansi_line,
@@ -100,7 +113,7 @@
   CString            cached_prefs_name_;
   CString            cached_prefs_file_path_;
   int64              cached_prefs_last_modified_;
-  scoped_ptr<Config> cached_config_;
+  scoped_ptr<ProxyConfig> cached_config_;
 
   friend class FirefoxProxyDetectorTest;
   DISALLOW_EVIL_CONSTRUCTORS(FirefoxProxyDetector);
@@ -112,8 +125,8 @@
 class IEProxyDetector : public ProxyDetectorInterface {
  public:
   IEProxyDetector() {}
-  virtual HRESULT Detect(Config* config);
-
+  virtual HRESULT Detect(ProxyConfig* config);
+  virtual const TCHAR* source() { return _T("IE"); }
  private:
   DISALLOW_EVIL_CONSTRUCTORS(IEProxyDetector);
 };
diff --git a/net/detector_unittest.cc b/net/detector_unittest.cc
index 82b85bc..ba14875 100644
--- a/net/detector_unittest.cc
+++ b/net/detector_unittest.cc
@@ -17,8 +17,8 @@
 #include <vector>
 #include "base/basictypes.h"
 #include "base/scoped_ptr.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/browser_utils.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/browser_utils.h"
 #include "omaha/net/detector.h"
 #include "omaha/net/network_config.h"
 #include "omaha/testing/unit_test.h"
@@ -67,7 +67,7 @@
 
   HRESULT ParsePrefsFile(const TCHAR* name,
                          const TCHAR* file_path,
-                         Config* config) {
+                         ProxyConfig* config) {
     return detector_->ParsePrefsFile(name, file_path, config);
   }
 
@@ -252,7 +252,7 @@
                             _T("8080"),
                             &prefs_file);
   ASSERT_TRUE(res);
-  Config config;
+  ProxyConfig config;
   EXPECT_SUCCEEDED(ParsePrefsFile(_T(""), prefs_file, &config));
   EXPECT_FALSE(config.auto_detect);
   EXPECT_TRUE(config.auto_config_url.IsEmpty());
@@ -269,7 +269,7 @@
                        _T("8080"),
                        &prefs_file);
   ASSERT_TRUE(res);
-  config = Config();
+  config = ProxyConfig();
   EXPECT_SUCCEEDED(ParsePrefsFile(_T(""), prefs_file, &config));
   EXPECT_FALSE(config.auto_detect);
   EXPECT_TRUE(config.auto_config_url.IsEmpty());
@@ -286,7 +286,7 @@
                        _T("8080"),
                        &prefs_file);
   ASSERT_TRUE(res);
-  config = Config();
+  config = ProxyConfig();
   EXPECT_SUCCEEDED(ParsePrefsFile(_T(""), prefs_file, &config));
   EXPECT_FALSE(config.auto_detect);
   EXPECT_STREQ(config.auto_config_url, _T("http://foobar"));
@@ -303,7 +303,7 @@
                        _T("8080"),
                        &prefs_file);
   ASSERT_TRUE(res);
-  config = Config();
+  config = ProxyConfig();
   EXPECT_SUCCEEDED(ParsePrefsFile(_T(""), prefs_file, &config));
   EXPECT_TRUE(config.auto_detect);
   EXPECT_TRUE(config.auto_config_url.IsEmpty());
@@ -318,7 +318,7 @@
   if (FAILED(GetFirefoxDefaultProfile(&name, &path))) {
     return;
   }
-  Config config;
+  ProxyConfig config;
   EXPECT_SUCCEEDED(detector_->Detect(&config));
 }
 
diff --git a/net/http_client.cc b/net/http_client.cc
index d632494..3fec183 100644
--- a/net/http_client.cc
+++ b/net/http_client.cc
@@ -18,7 +18,8 @@
 
 #include "omaha/net/http_client.h"
 
-#include "omaha/common/debug.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/safe_format.h"
 
 namespace omaha {
 
@@ -49,7 +50,7 @@
   ASSERT1(name && *name);
   ASSERT1(value && *value);
   CString header;
-  header.Format(_T("%s: %s\r\n"), name, value);
+  SafeCStringFormat(&header, _T("%s: %s\r\n"), name, value);
   return header;
 }
 
diff --git a/net/http_client.h b/net/http_client.h
index da7208a..833f154 100644
--- a/net/http_client.h
+++ b/net/http_client.h
@@ -66,7 +66,7 @@
 #define WINHTTP_ACCESS_TYPE_AUTO_DETECT 2
 
 #include "base/basictypes.h"
-#include "omaha/common/object_factory.h"
+#include "omaha/base/object_factory.h"
 
 namespace omaha {
 
@@ -195,6 +195,7 @@
                        uint32 access_type,
                        const TCHAR* proxy_name,
                        const TCHAR* proxy_bypass,
+                       DWORD flags,
                        HINTERNET* session_handle) = 0;
 
   // Specifies the http request.
@@ -250,7 +251,8 @@
                               DWORD headers_length,
                               const void* optional_data,
                               DWORD optional_data_length,
-                              DWORD content_length) = 0;
+                              DWORD content_length,
+                              DWORD_PTR context) = 0;
 
   // Sets the authentication credentials.
   virtual HRESULT SetCredentials(HINTERNET request_handle,
@@ -337,6 +339,10 @@
 // WinHttp is preferred over WinInet.
 HttpClient* CreateHttpClient();
 
+const HttpClient::StatusCallback kInvalidStatusCallback =
+    reinterpret_cast<HttpClient::StatusCallback>(
+        WINHTTP_INVALID_STATUS_CALLBACK);
+
 }  // namespace omaha
 
 #endif  // OMAHA_NET_HTTP_CLIENT_H__
diff --git a/net/http_client_unittest.cc b/net/http_client_unittest.cc
index 897c984..038a464 100644
--- a/net/http_client_unittest.cc
+++ b/net/http_client_unittest.cc
@@ -20,7 +20,7 @@
 #include <vector>
 #include "base/basictypes.h"
 #include "base/scoped_ptr.h"
-#include "omaha/common/omaha_version.h"
+#include "omaha/base/omaha_version.h"
 #include "omaha/net/http_client.h"
 #include "omaha/testing/unit_test.h"
 
@@ -80,6 +80,7 @@
                                       HttpClient::kAccessTypeNoProxy,
                                       NULL,
                                       NULL,
+                                      0,    // Synchronous mode.
                                       &session_handle));
   if (use_proxy) {
     HttpClient::AutoProxyOptions autoproxy_options = {0};
@@ -119,7 +120,7 @@
                                              flags,
                                              &request_handle));
   ASSERT_SUCCEEDED(http_client_->SendRequest(request_handle,
-                                             NULL, 0, NULL, 0, 0));
+                                             NULL, 0, NULL, 0, 0, 0));
   ASSERT_SUCCEEDED(http_client_->ReceiveResponse(request_handle));
   CString content_type;
   EXPECT_SUCCEEDED(http_client_->QueryHeadersString(request_handle,
@@ -162,10 +163,18 @@
 }
 
 TEST_F(HttpClientTest, Get) {
+  if (IsTestRunByLocalSystem()) {
+    return;
+  }
+
   GetUrl(kTestUrlGet, false);
 }
 
 TEST_F(HttpClientTest, SecureGet) {
+  if (IsTestRunByLocalSystem()) {
+    return;
+  }
+
   GetUrl(kTestSecureUrlGet, false);
 }
 
diff --git a/net/http_request.h b/net/http_request.h
index c9a396e..a9b3ca3 100644
--- a/net/http_request.h
+++ b/net/http_request.h
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -18,6 +18,10 @@
 //
 // TODO(omaha): the class interface is not stable yet, as a few more
 // getters and setters are still needed.
+//
+// TODO(omaha): provide a way to query how many bytes have been downloaded
+// when the Send() returns so that the code doesn't have to rely on a final
+// callback to update the progress information.
 
 #ifndef OMAHA_NET_HTTP_REQUEST_H__
 #define OMAHA_NET_HTTP_REQUEST_H__
@@ -43,6 +47,10 @@
 
   virtual HRESULT Cancel() = 0;
 
+  virtual HRESULT Pause() = 0;
+
+  virtual HRESULT Resume() = 0;
+
   virtual std::vector<uint8> GetResponse() const = 0;
 
   virtual int GetHttpStatusCode() const = 0;
@@ -62,7 +70,7 @@
   virtual void set_request_buffer(const void* buffer,
                                   size_t buffer_length) = 0;
 
-  virtual void set_network_configuration(const Config& network_config) = 0;
+  virtual void set_proxy_configuration(const ProxyConfig& proxy_config) = 0;
 
   // Sets the filename to receive the response instead of the memory buffer.
   virtual void set_filename(const CString& filename) = 0;
@@ -73,6 +81,8 @@
 
   virtual void set_additional_headers(const CString& additional_headers) = 0;
 
+  virtual void set_preserve_protocol(bool preserve_protocol) = 0;
+
   // Gets the user agent for this http request. The default user agent has
   // the following format: Google Update/a.b.c.d;req1;req2 where a.b.c.d is
   // the version of the client code and req1, req2,... are appended by
@@ -82,6 +92,8 @@
   virtual CString user_agent() const = 0;
 
   virtual void set_user_agent(const CString& user_agent) = 0;
+
+  virtual void set_proxy_auth_config(const ProxyAuthConfig& config) = 0;
 };
 
 }   // namespace omaha
diff --git a/net/net_diags.cc b/net/net_diags.cc
index 2fc99d6..fd2b256 100644
--- a/net/net_diags.cc
+++ b/net/net_diags.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -19,11 +19,12 @@
 #include <windows.h>
 #include <stdarg.h>
 #include <vector>
-#include "omaha/common/app_util.h"
-#include "omaha/common/browser_utils.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/system_info.h"
-#include "omaha/common/utils.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/browser_utils.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/time.h"
+#include "omaha/base/utils.h"
 #include "omaha/net/network_config.h"
 #include "omaha/net/network_request.h"
 
@@ -81,19 +82,25 @@
     ::ExitProcess(1);
   }
 
+  NetworkConfig* network_config = NULL;
+  hr = NetworkConfigManager::Instance().GetUserNetworkConfig(&network_config);
+  if (FAILED(hr)) {
+    PrintToConsole(_T("Failed to GetUserNetworkConfig() [0x%x]\n"), hr);
+    ::ExitProcess(1);
+  }
+
   // Initialize the detection chain: GoogleProxy, FireFox if it is the
   // default browser, and IE.
-  NetworkConfig& network_config(NetworkConfig::Instance());
-  network_config.Clear();
+  network_config->Clear();
   BrowserType browser_type(BROWSER_UNKNOWN);
   GetDefaultBrowserType(&browser_type);
   if (browser_type == BROWSER_FIREFOX) {
     PrintToConsole(_T("Default browser is Firefox\n"));
-    network_config.Add(new FirefoxProxyDetector());
+    network_config->Add(new FirefoxProxyDetector());
   }
-  network_config.Add(new IEProxyDetector());
+  network_config->Add(new IEProxyDetector());
 
-  std::vector<Config> configs = network_config.GetConfigurations();
+  std::vector<ProxyConfig> configs = network_config->GetConfigurations();
   if (configs.empty()) {
     PrintToConsole(_T("No Network Configurations to display\n"));
   } else {
@@ -106,14 +113,36 @@
   PrintToConsole(_T("\n[Downloading %d of %d]\n"), bytes, bytes_total);
 }
 
+void NetDiags::OnRequestBegin() {
+  PrintToConsole(_T("\n[Download begins]\n"));
+}
+
+void NetDiags::OnRequestRetryScheduled(time64 next_retry_time) {
+  time64 now = GetCurrent100NSTime();
+  ASSERT1(next_retry_time > now);
+
+  if (next_retry_time > now) {
+    PrintToConsole(_T("\n[Download will retry in %d seconds]\n"),
+                      CeilingDivide(next_retry_time - now, kSecsTo100ns));
+  }
+}
+
 // http get.
 void NetDiags::DoGet(const CString& url) {
   PrintToConsole(_T("\nGET request for [%s]\n"), url);
-  NetworkRequest network_request(NetworkConfig::Instance().session());
+  NetworkConfig* network_config = NULL;
+  NetworkConfigManager& network_manager = NetworkConfigManager::Instance();
+  HRESULT hr = network_manager.GetUserNetworkConfig(&network_config);
+  if (FAILED(hr)) {
+    PrintToConsole(_T("Failed to GetUserNetworkConfig() [0x%x]\n"), hr);
+    return;
+  }
+  NetworkRequest network_request(network_config->session());
+
   network_request.set_callback(this);
   network_request.set_num_retries(2);
-  CString response;
-  HRESULT hr = GetRequest(&network_request, url, &response);
+  std::vector<uint8> response;
+  hr = GetRequest(&network_request, url, &response);
   int status = network_request.http_status_code();
   if (FAILED(hr)) {
     PrintToConsole(_T("GET request failed. HRESULT=[0x%x], HTTP Status=[%d]\n"),
@@ -122,13 +151,21 @@
   }
 
   PrintToConsole(_T("HTTP Status=[%d]\n"), status);
-  PrintToConsole(_T("HTTP Response=\n[%s]\n"), response);
+  PrintToConsole(_T("HTTP Response=\n[%s]\n"), Utf8BufferToWideChar(response));
 }
 
 // http download.
 void NetDiags::DoDownload(const CString& url) {
   PrintToConsole(_T("\nDownload request for [%s]\n"), url);
-  NetworkRequest network_request(NetworkConfig::Instance().session());
+  NetworkConfig* network_config = NULL;
+  NetworkConfigManager& network_manager = NetworkConfigManager::Instance();
+  HRESULT hr = network_manager.GetUserNetworkConfig(&network_config);
+  if (FAILED(hr)) {
+    PrintToConsole(_T("Failed to GetUserNetworkConfig() [0x%x]\n"), hr);
+    return;
+  }
+  NetworkRequest network_request(network_config->session());
+
   network_request.set_callback(this);
   network_request.set_num_retries(2);
 
@@ -142,7 +179,7 @@
     return;
   }
 
-  HRESULT hr = network_request.DownloadFile(url, temp_file);
+  hr = network_request.DownloadFile(url, temp_file);
   int status = network_request.http_status_code();
   if (FAILED(hr)) {
     PrintToConsole(_T("Download failed. HRESULT=[0x%x], HTTP Status=[%d]\n"),
diff --git a/net/net_diags.h b/net/net_diags.h
index bd92e60..c145af8 100644
--- a/net/net_diags.h
+++ b/net/net_diags.h
@@ -20,6 +20,7 @@
 #include <tchar.h>
 #include <atlstr.h>
 #include "base/basictypes.h"
+#include "base/time.h"
 #include "omaha/net/network_request.h"
 
 namespace omaha {
@@ -35,6 +36,8 @@
  private:
   void Initialize();
   virtual void OnProgress(int bytes, int bytes_total, int, const TCHAR*);
+  virtual void OnRequestBegin();
+  virtual void OnRequestRetryScheduled(time64 next_retry_time);
 
   // http get.
   void DoGet(const CString& url);
diff --git a/net/net_utils.cc b/net/net_utils.cc
index e186a3c..8d76116 100644
--- a/net/net_utils.cc
+++ b/net/net_utils.cc
@@ -16,9 +16,9 @@
 #include "omaha/net/net_utils.h"
 #include <iphlpapi.h>
 #include "base/scoped_ptr.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/utils.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/utils.h"
 
 namespace omaha {
 
diff --git a/net/network_config.cc b/net/network_config.cc
index f3aefbe..3b10c33 100644
--- a/net/network_config.cc
+++ b/net/network_config.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -18,33 +18,39 @@
 #include <winhttp.h>
 #include <atlconv.h>
 #include <atlsecurity.h>
+#include <algorithm>
 #include <hash_set>
 #include <vector>
+#include "base/error.h"
 #include "base/scoped_ptr.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/path.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/common/string.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
+#include "base/scope_guard.h"
+#include "omaha/base/browser_utils.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/encrypt.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/string.h"
+#include "omaha/base/system.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/config_manager.h"
 #include "omaha/net/cup_request.h"
 #include "omaha/net/http_client.h"
-#include "omaha/goopdate/resource.h"
 
-using omaha::user_info::GetCurrentUser;
+using omaha::encrypt::EncryptData;
+using omaha::encrypt::DecryptData;
 
 namespace omaha {
 
-// Computes the hash value of a Config object. Names in the stdext namespace are
-// not currently part of the ISO C++ standard.
-uint32 hash_value(const Config& config) {
+// Computes the hash value of a ProxyConfig object. Names in the stdext
+// namespace are not currently part of the ISO C++ standard.
+uint32 hash_value(const ProxyConfig& config) {
   uint32 hash = stdext::hash_value(config.auto_detect)                 ^
                 stdext::hash_value(config.auto_config_url.GetString()) ^
                 stdext::hash_value(config.proxy.GetString())           ^
@@ -52,19 +58,21 @@
   return hash;
 }
 
-NetworkConfig* const NetworkConfig::kInvalidInstance  =
-    reinterpret_cast<NetworkConfig* const>(-1);
-NetworkConfig* NetworkConfig::instance_               = NULL;
-
-const TCHAR* const NetworkConfig::kNetworkSubkey      = _T("network");
-const TCHAR* const NetworkConfig::kNetworkCupSubkey   = _T("secure");
-const TCHAR* const NetworkConfig::kCupClientSecretKey = _T("sk");
-const TCHAR* const NetworkConfig::kCupClientCookie    = _T("c");
+const TCHAR* const NetworkConfigManager::kNetworkSubkey      = _T("network");
+const TCHAR* const NetworkConfigManager::kNetworkCupSubkey   = _T("secure");
+const TCHAR* const NetworkConfigManager::kCupClientSecretKey = _T("sk");
+const TCHAR* const NetworkConfigManager::kCupClientCookie    = _T("c");
 
 const TCHAR* const NetworkConfig::kUserAgent = _T("Google Update/%s");
 
-NetworkConfig::NetworkConfig()
-    : is_machine_(false),
+const TCHAR* const NetworkConfig::kRegKeyProxy = GOOPDATE_MAIN_KEY _T("proxy");
+const TCHAR* const NetworkConfig::kRegValueSource = _T("source");
+
+const TCHAR* const NetworkConfig::kWPADIdentifier = _T("auto");
+const TCHAR* const NetworkConfig::kDirectConnectionIdentifier = _T("direct");
+
+NetworkConfig::NetworkConfig(bool is_machine)
+    : is_machine_(is_machine),
       is_initialized_(false) {}
 
 NetworkConfig::~NetworkConfig() {
@@ -72,23 +80,9 @@
     http_client_->Close(session_.session_handle);
     session_.session_handle = NULL;
   }
-  if (session_.impersonation_token) {
-    ::CloseHandle(session_.impersonation_token);
-    session_.impersonation_token = NULL;
-  }
   Clear();
 }
 
-NetworkConfig& NetworkConfig::Instance() {
-  // Getting the instance after the instance has been deleted is a bug in
-  // the logic of the program.
-  ASSERT1(instance_ != kInvalidInstance);
-  if (!instance_) {
-    instance_ = new NetworkConfig();
-  }
-  return *instance_;
-}
-
 // Initialize creates or opens a global lock to synchronize access to
 // registry where CUP credentials are stored. Each user including non-elevated
 // admins stores network configuration data, such as the CUP password in
@@ -108,27 +102,9 @@
 // The CUP credentials must be protected with ACLs so non-elevated admins can't
 // read elevated-admins' keys and attack the protocol.
 //
-// Also, an Internet session is created and associated with the impersonation
-// token.
-HRESULT NetworkConfig::Initialize(bool is_machine,
-                                  HANDLE impersonation_token) {
+// Also, an Internet session is created.
+HRESULT NetworkConfig::Initialize() {
   ASSERT1(!is_initialized_);
-  is_machine_ = is_machine;
-  HRESULT hr = GetCurrentUser(NULL, NULL, &sid_);
-  if (FAILED(hr)) {
-    NET_LOG(LE, (_T("[GetCurrentUser failed][0x%x]"), hr));
-    return hr;
-  }
-  hr = InitializeLock();
-  if (FAILED(hr)) {
-    NET_LOG(LE, (_T("[InitializeLock failed][0x%x]"), hr));
-    return hr;
-  }
-  hr = InitializeRegistryKey();
-  if (FAILED(hr)) {
-    NET_LOG(LE, (_T("[InitializeRegistryKey failed][0x%x]"), hr));
-    return hr;
-  }
 
   http_client_.reset(CreateHttpClient());
   ASSERT1(http_client_.get());
@@ -136,7 +112,7 @@
     NET_LOG(LE, (_T("[CreateHttpClient failed]")));
     return E_UNEXPECTED;
   }
-  hr = http_client_->Initialize();
+  HRESULT hr = http_client_->Initialize();
   if (FAILED(hr)) {
     // TODO(omaha): This makes an assumption that only WinHttp is
     // supported by the network code.
@@ -144,74 +120,44 @@
     return OMAHA_NET_E_WINHTTP_NOT_AVAILABLE;
   }
 
+  // Initializes the WinHttp session and configures WinHttp to work in
+  // asynchronous mode. In this mode, the network requests are non-blocking
+  // and asynchronous events are generated when a request is complete.
   hr = http_client_->Open(NULL,
                           WINHTTP_ACCESS_TYPE_NO_PROXY,
                           WINHTTP_NO_PROXY_NAME,
                           WINHTTP_NO_PROXY_BYPASS,
+                          WINHTTP_FLAG_ASYNC,
                           &session_.session_handle);
   if (FAILED(hr)) {
     NET_LOG(LE, (_T("[http_client_->Open() failed][0x%x]"), hr));
     return hr;
   }
 
-  session_.impersonation_token = impersonation_token;
+  Add(new UpdateDevProxyDetector);
+  BrowserType browser_type(BROWSER_UNKNOWN);
+  GetDefaultBrowserType(&browser_type);
+  if (browser_type == BROWSER_FIREFOX) {
+    Add(new FirefoxProxyDetector);
+  }
+  // There is no Chrome detector because it uses the same proxy settings as IE.
+  Add(new IEProxyDetector);
+  Add(new DefaultProxyDetector);
+
+  // Use a global network configuration override if available.
+  ConfigManager* config_manager = ConfigManager::Instance();
+  CString net_config;
+  if (SUCCEEDED(config_manager->GetNetConfig(&net_config))) {
+    ProxyConfig config_override = NetworkConfig::ParseNetConfig(net_config);
+    SetConfigurationOverride(&config_override);
+  }
+
+  ConfigureProxyAuth();
+
   is_initialized_ = true;
   return S_OK;
 }
 
-HRESULT NetworkConfig::InitializeLock() {
-  NamedObjectAttributes lock_attr;
-  GetNamedObjectAttributes(kNetworkConfigLock, is_machine_, &lock_attr);
-  return global_lock_.InitializeWithSecAttr(lock_attr.name, &lock_attr.sa) ?
-         S_OK : E_FAIL;
-}
-
-HRESULT NetworkConfig::InitializeRegistryKey() {
-  // The registry path under which to store persistent network configuration.
-  // The "network" subkey is created with default security. Below "network",
-  // the "secure" key is created so that only system and administrators have
-  // access to it.
-  registry_update_network_path_ = is_machine_ ? MACHINE_REG_UPDATE :
-                                                USER_REG_UPDATE;
-  registry_update_network_path_ =
-      AppendRegKeyPath(registry_update_network_path_, kNetworkSubkey);
-  RegKey reg_key_network;
-  DWORD disposition = 0;
-  HRESULT hr = reg_key_network.Create(registry_update_network_path_,
-                                      NULL,                 // Class.
-                                      0,                    // Options.
-                                      KEY_CREATE_SUB_KEY,   // SAM desired.
-                                      NULL,                 // Security attrs.
-                                      &disposition);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // When initializing for machine, grant access to administrators and system.
-  scoped_ptr<CSecurityAttributes> sa;
-  if (is_machine_) {
-    sa.reset(new CSecurityAttributes);
-    GetAdminDaclSecurityAttributes(sa.get(), GENERIC_ALL);
-  }
-
-  disposition = 0;
-  RegKey reg_key_network_secure;
-  CString subkey_name = is_machine_ ?
-                        JoinStrings(kNetworkCupSubkey, sid_, _T("-")) :
-                        kNetworkCupSubkey;
-  hr = reg_key_network_secure.Create(reg_key_network.Key(),  // Parent.
-                                     subkey_name,            // Subkey name.
-                                     NULL,                   // Class.
-                                     0,                      // Options.
-                                     KEY_READ,               // SAM desired.
-                                     sa.get(),               // Security attrs.
-                                     &disposition);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  return S_OK;
-}
-
 void NetworkConfig::Add(ProxyDetectorInterface* detector) {
   ASSERT1(detector);
   __mutexBlock(lock_) {
@@ -231,27 +177,93 @@
 
 HRESULT NetworkConfig::Detect() {
   __mutexBlock(lock_) {
-    std::vector<Config> configurations;
+    std::vector<ProxyConfig> configurations;
+
     for (size_t i = 0; i != detectors_.size(); ++i) {
-      Config config;
+      ProxyConfig config;
       if (SUCCEEDED(detectors_[i]->Detect(&config))) {
         configurations.push_back(config);
       }
     }
     configurations_.swap(configurations);
   }
+
   return S_OK;
 }
 
-std::vector<Config> NetworkConfig::GetConfigurations() const {
-  std::vector<Config> configurations;
+void NetworkConfig::SortProxies(std::vector<ProxyConfig>* configurations) {
+  ASSERT1(configurations);
+
+  std::stable_sort(configurations->begin(), configurations->end(),
+                   ProxySortPredicate);
+}
+
+HRESULT NetworkConfig::ConfigFromIdentifier(const CString& id,
+                                            ProxyConfig* config) {
+  ASSERT1(config);
+
+  *config = ProxyConfig();
+  if (id == kWPADIdentifier) {
+    config->source = kWPADIdentifier;
+    config->auto_detect = true;
+  } else if (id == kDirectConnectionIdentifier) {
+    config->source = kDirectConnectionIdentifier;
+  } else {
+    return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
+  }
+
+  return S_OK;
+}
+
+void NetworkConfig::AppendLastKnownGoodProxyConfig(
+    std::vector<ProxyConfig>* configurations) const {
+  ASSERT1(configurations);
+  ProxyConfig last_known_good_config;
+  if (SUCCEEDED(LoadProxyConfig(&last_known_good_config))) {
+    configurations->push_back(last_known_good_config);
+  }
+}
+
+void NetworkConfig::AppendStaticProxyConfigs(
+    std::vector<ProxyConfig>* configurations) {
+  ASSERT1(configurations);
+  ProxyConfig config;
+
+  HRESULT hr = ConfigFromIdentifier(kWPADIdentifier, &config);
+  if (SUCCEEDED(hr)) {
+    configurations->push_back(config);
+  }
+
+  hr = ConfigFromIdentifier(kDirectConnectionIdentifier, &config);
+  if (SUCCEEDED(hr)) {
+    configurations->push_back(config);
+  }
+}
+
+HRESULT NetworkConfig::Detect(const CString& proxy_source,
+                              ProxyConfig* config) const {
+  ASSERT1(config);
+  __mutexBlock(lock_) {
+    std::vector<ProxyConfig> configurations;
+    for (size_t i = 0; i != detectors_.size(); ++i) {
+      if (proxy_source == detectors_[i]->source()) {
+        return detectors_[i]->Detect(config);
+      }
+    }
+  }
+
+  return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
+}
+
+std::vector<ProxyConfig> NetworkConfig::GetConfigurations() const {
+  std::vector<ProxyConfig> configurations;
   __mutexBlock(lock_) {
     configurations = configurations_;
   }
   return configurations;
 }
 
-HRESULT NetworkConfig::GetConfigurationOverride(Config* config) {
+HRESULT NetworkConfig::GetConfigurationOverride(ProxyConfig* config) {
   ASSERT1(config);
   __mutexBlock(lock_) {
     if (configuration_override_.get()) {
@@ -263,22 +275,40 @@
 }
 
 void NetworkConfig::SetConfigurationOverride(
-    const Config* configuration_override) {
+    const ProxyConfig* configuration_override) {
   __mutexBlock(lock_) {
     if (configuration_override) {
-      configuration_override_.reset(new Config);
+      configuration_override_.reset(new ProxyConfig);
       *configuration_override_ = *configuration_override;
+      configuration_override_->source = _T("updatedev/netconfig");
     } else {
       configuration_override_.reset();
     }
   }
 }
 
-// Serializes configurations for debugging purposes.
+HRESULT NetworkConfig::GetCupCredentials(
+    CupCredentials* cup_credentials) const {
+  return NetworkConfigManager::Instance().GetCupCredentials(cup_credentials);
+}
 
-CString NetworkConfig::ToString(const Config& config) {
+HRESULT NetworkConfig::SetCupCredentials(
+    const CupCredentials* cup_credentials) const {
+  NetworkConfigManager& network_manager = NetworkConfigManager::Instance();
+  if (cup_credentials == NULL) {
+    network_manager.ClearCupCredentials();
+    return S_OK;
+  }
+
+  return network_manager.SetCupCredentials(*cup_credentials);
+}
+
+// Serializes configurations for debugging purposes.
+CString NetworkConfig::ToString(const ProxyConfig& config) {
   CString result;
-  result.AppendFormat(_T("source=%s, "), config.source);
+  result.AppendFormat(_T("priority=%u, source=%s, "),
+                      config.priority, config.source);
+
   switch (GetAccessType(config)) {
     case WINHTTP_ACCESS_TYPE_NO_PROXY:
       result.AppendFormat(_T("direct connection"));
@@ -298,16 +328,16 @@
   return result;
 }
 
-CString NetworkConfig::ToString(const std::vector<Config>& configurations) {
+CString NetworkConfig::ToString(const std::vector<ProxyConfig>& config) {
   CString result;
-  for (size_t i = 0; i != configurations.size(); ++i) {
-    result.Append(NetworkConfig::ToString(configurations[i]));
+  for (size_t i = 0; i != config.size(); ++i) {
+    result.Append(NetworkConfig::ToString(config[i]));
     result.Append(_T("\r\n"));
   }
   return result;
 }
 
-int NetworkConfig::GetAccessType(const Config& config) {
+int NetworkConfig::GetAccessType(const ProxyConfig& config) {
   if (config.auto_detect || !config.auto_config_url.IsEmpty()) {
     return WINHTTP_ACCESS_TYPE_AUTO_DETECT;
   } else if (!config.proxy.IsEmpty()) {
@@ -317,72 +347,6 @@
   }
 }
 
-HRESULT NetworkConfig::GetCupCredentials(CupCredentials* cup_credentials) {
-  ASSERT1(cup_credentials);
-  ASSERT1(is_initialized_);
-  __mutexBlock(global_lock_) {
-    CString subkey_name = is_machine_ ?
-                          JoinStrings(kNetworkCupSubkey, sid_, _T("-")) :
-                          kNetworkCupSubkey;
-    CString key_name = AppendRegKeyPath(registry_update_network_path_,
-                                        subkey_name);
-    RegKey reg_key;
-    HRESULT hr = reg_key.Open(key_name, KEY_READ);
-    if (FAILED(hr)) {
-      return hr;
-    }
-    scoped_array<byte> buf;
-    DWORD buf_length = 0;
-    hr = reg_key.GetValue(kCupClientSecretKey, address(buf), &buf_length);
-    if (FAILED(hr)) {
-      return hr;
-    }
-    CString cookie;
-    hr = reg_key.GetValue(kCupClientCookie, &cookie);
-    if (FAILED(hr)) {
-      return hr;
-    }
-    cup_credentials->sk.resize(buf_length);
-    memcpy(&cup_credentials->sk.front(), buf.get(), buf_length);
-    cup_credentials->c = CT2A(cookie);
-  }
-  return S_OK;
-}
-
-HRESULT NetworkConfig::SetCupCredentials(
-            const CupCredentials* cup_credentials) {
-  ASSERT1(is_initialized_);
-  __mutexBlock(global_lock_) {
-    CString subkey_name = is_machine_ ?
-                          JoinStrings(kNetworkCupSubkey, sid_, _T("-")) :
-                          kNetworkCupSubkey;
-    CString key_name = AppendRegKeyPath(registry_update_network_path_,
-                                        subkey_name);
-    RegKey reg_key;
-    HRESULT hr = reg_key.Open(key_name, KEY_WRITE);
-    if (FAILED(hr)) {
-      NET_LOG(L2, (_T("[Registry key open failed][%s][0x%08x]"), key_name, hr));
-      return hr;
-    }
-    if (!cup_credentials) {
-      HRESULT hr1 = reg_key.DeleteValue(kCupClientSecretKey);
-      HRESULT hr2 = reg_key.DeleteValue(kCupClientCookie);
-      return (SUCCEEDED(hr1) && SUCCEEDED(hr2)) ? S_OK : HRESULTFromLastError();
-    }
-    hr = reg_key.SetValue(kCupClientSecretKey,
-             static_cast<const byte*>(&cup_credentials->sk.front()),
-             cup_credentials->sk.size());
-    if (FAILED(hr)) {
-      return hr;
-    }
-    hr = reg_key.SetValue(kCupClientCookie, CA2T(cup_credentials->c));
-    if (FAILED(hr)) {
-      return hr;
-    }
-  }
-  return S_OK;
-}
-
 bool NetworkConfig::IsUsingCupTestKeys() {
   DWORD value = 0;
   if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
@@ -394,17 +358,15 @@
   }
 }
 
-void NetworkConfig::ConfigureProxyAuth(const CString& caption,
-                                       const CString& message,
-                                       HWND parent,
-                                       uint32 cancel_prompt_threshold) {
-  return proxy_auth_.ConfigureProxyAuth(caption, message, parent,
-                                        cancel_prompt_threshold);
+void NetworkConfig::ConfigureProxyAuth() {
+  const uint32 kProxyMaxPrompts = 1;
+  return proxy_auth_.ConfigureProxyAuth(is_machine_, kProxyMaxPrompts);
 }
 
 bool NetworkConfig::GetProxyCredentials(bool allow_ui,
                                         bool force_ui,
                                         const CString& proxy_settings,
+                                        const ProxyAuthConfig& config,
                                         bool is_https,
                                         CString* username,
                                         CString* password,
@@ -412,9 +374,11 @@
   ASSERT1(username);
   ASSERT1(password);
   ASSERT1(auth_scheme);
+
   const CString& proxy = ProxyAuth::ExtractProxy(proxy_settings, is_https);
   return proxy_auth_.GetProxyCredentials(allow_ui, force_ui, proxy,
-                                         username, password, auth_scheme);
+                                         config, username,
+                                         password, auth_scheme);
 }
 
 HRESULT NetworkConfig::SetProxyAuthScheme(const CString& proxy_settings,
@@ -432,6 +396,9 @@
                                       const CString& auto_config_url,
                                       HttpClient::ProxyInfo* proxy_info) {
   ASSERT1(proxy_info);
+
+  NET_LOG(L3, (_T("[NetworkConfig::GetProxyForUrl][%s]"), url));
+
   HttpClient::AutoProxyOptions auto_proxy_options = {0};
   auto_proxy_options.flags = WINHTTP_AUTOPROXY_AUTO_DETECT;
   auto_proxy_options.auto_detect_flags = WINHTTP_AUTO_DETECT_TYPE_DHCP |
@@ -442,10 +409,28 @@
   }
   auto_proxy_options.auto_logon_if_challenged = true;
 
-  return http_client_->GetProxyForUrl(session_.session_handle,
-                                      url,
-                                      &auto_proxy_options,
-                                      proxy_info);
+  HRESULT hr = http_client_->GetProxyForUrl(session_.session_handle,
+                                            url,
+                                            &auto_proxy_options,
+                                            proxy_info);
+
+  if (FAILED(hr) && ::UrlIsFileUrl(auto_config_url)) {
+    // Some HttpClient implementations, namely WinHTTP, only support PAC files
+    // with http or https schemes.  Attempt an alternate resolution scheme using
+    // jsproxy.dll if the initial attempt fails.
+    ASSERT1(user_info::IsThreadImpersonating());
+
+    CString local_file;
+    hr = ConvertFileUriToLocalPath(auto_config_url, &local_file);
+    if (FAILED(hr)) {
+      NET_LOG(LE, (_T("[ConvertFileUriToLocalPath failed][0x%08x]"), hr));
+      return hr;
+    }
+
+    hr = GetProxyForUrlLocal(url, local_file, proxy_info);
+  }
+
+  return hr;
 }
 
 CString NetworkConfig::GetUserAgent() {
@@ -454,6 +439,12 @@
   return user_agent;
 }
 
+CString NetworkConfig::GetMID() {
+  CString mid;
+  RegKey::GetValue(MACHINE_REG_UPDATE_DEV, kRegValueMID, &mid);
+  return mid;
+}
+
 CString NetworkConfig::JoinStrings(const TCHAR* s1,
                                    const TCHAR* s2,
                                    const TCHAR* delim) {
@@ -467,15 +458,15 @@
 // adds about 1.5K. Usually, there are only five detected configurations so
 // an O(n^2) algorithm would work well. The advantage of the current
 // implementation is simplicity. It also does not handle conflicts. Conflicts
-// are not expected, due to how the Config structure is being used.
+// are not expected, due to how the ProxyConfig structure is being used.
 // TODO(omaha): consider not using the hash_set and save about 1K of code.
-void NetworkConfig::RemoveDuplicates(std::vector<Config>* config) {
+void NetworkConfig::RemoveDuplicates(std::vector<ProxyConfig>* config) {
   ASSERT1(config);
 
   // Iterate over the input configurations, remember the hash of each
   // distinct configuration, and remove the duplicates by skipping the
   // configurations seen before.
-  std::vector<Config> input(*config);
+  std::vector<ProxyConfig> input(*config);
   config->clear();
 
   typedef stdext::hash_set<uint32> Keys;
@@ -488,8 +479,75 @@
   }
 }
 
-Config NetworkConfig::ParseNetConfig(const CString& net_config) {
-  Config config;
+HRESULT NetworkConfig::CreateProxyConfigRegKey(RegKey* key) {
+  ASSERT1(key);
+  CString config_root;
+
+  if (!user_info::IsRunningAsSystem()) {
+    scoped_hkey user_root_key;
+    HRESULT hr = ::RegOpenCurrentUser(KEY_READ | KEY_WRITE,
+                                      address(user_root_key));
+    if (FAILED(hr)) {
+        return hr;
+    }
+    return key->Create(get(user_root_key), kRegKeyProxy);
+  } else {
+    return key->Create(HKEY_LOCAL_MACHINE, kRegKeyProxy);
+  }
+}
+
+HRESULT NetworkConfig::SaveProxyConfig(const ProxyConfig& config) {
+  const CString& new_configuration = config.source;
+  NET_LOG(L3, (_T("[NetworkConfig::SaveProxyConfig][%s]"), new_configuration));
+
+  RegKey key;
+  HRESULT hr = CreateProxyConfigRegKey(&key);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CString current_configuration;
+  if (SUCCEEDED(key.GetValue(kRegValueSource, &current_configuration)) &&
+      current_configuration != new_configuration) {
+    NET_LOG(L3, (_T("[Network configuration changed from %s to %s"),
+        current_configuration, new_configuration));
+  }
+
+  return key.SetValue(kRegValueSource, new_configuration);
+}
+
+HRESULT NetworkConfig::LoadProxyConfig(ProxyConfig* config) const {
+  ASSERT1(config);
+
+  *config = ProxyConfig();
+
+  RegKey key;
+  HRESULT hr = CreateProxyConfigRegKey(&key);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CString source;
+  hr = key.GetValue(kRegValueSource, &source);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = NetworkConfig::ConfigFromIdentifier(source, config);
+  if (FAILED(hr)) {
+    hr = Detect(source, config);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  }
+
+  config->priority = ProxyConfig::PROXY_PRIORITY_LAST_KNOWN_GOOD;
+
+  return S_OK;
+}
+
+ProxyConfig NetworkConfig::ParseNetConfig(const CString& net_config) {
+  ProxyConfig config;
   int pos(0);
   CString token = net_config.Tokenize(_T(";"), pos);
   while (pos != -1) {
@@ -510,5 +568,424 @@
   return config;
 }
 
+// Note: The jsproxy functions are exposed to public users as part of the
+// DOJ consent decree and are not formally supported by Microsoft.
+
+GPA_WRAP(jsproxy.dll,
+         InternetInitializeAutoProxyDll,
+         (DWORD dwVersion, LPSTR lpszDownloadedTempFile, LPSTR lpszMime, LPCVOID lpAutoProxyCallbacks, LPCVOID lpAutoProxyScriptBuffer),  // NOLINT
+         (dwVersion, lpszDownloadedTempFile, lpszMime, lpAutoProxyCallbacks, lpAutoProxyScriptBuffer),  // NOLINT
+         WINAPI,
+         BOOL,
+         FALSE);
+
+GPA_WRAP(jsproxy.dll,
+         InternetGetProxyInfo,
+         (LPCSTR lpszUrl, DWORD dwUrlLength, LPSTR lpszUrlHostName, DWORD dwUrlHostNameLength, LPSTR *lplpszProxyHostName, LPDWORD lpdwProxyHostNameLength),  // NOLINT
+         (lpszUrl, dwUrlLength, lpszUrlHostName, dwUrlHostNameLength, lplpszProxyHostName, lpdwProxyHostNameLength),  // NOLINT
+         WINAPI,
+         BOOL,
+         FALSE);
+
+GPA_WRAP(jsproxy.dll,
+         InternetDeInitializeAutoProxyDll,
+         (LPSTR lpszMime, DWORD dwReserved),
+         (lpszMime, dwReserved),
+         WINAPI,
+         BOOL,
+         FALSE);
+
+HRESULT NetworkConfig::GetProxyForUrlLocal(const CString& url,
+                                           const CString& path_to_pac_file,
+                                           HttpClient::ProxyInfo* proxy_info) {
+  scoped_library jsproxy_lib(::LoadLibrary(_T("jsproxy.dll")));
+  ASSERT1(jsproxy_lib);
+  if (!jsproxy_lib) {
+    HRESULT hr = HRESULTFromLastError();
+    NET_LOG(LE, (_T("[GetProxyForUrlLocal][jsproxy not loaded][0x%08x]"), hr));
+    return hr;
+  }
+
+  // Convert the inputs to ANSI, and call into JSProxy to execute the PAC
+  // script; we should get back out a PAC-format list of proxies to use.
+  //
+  // TODO(omaha3): The MSDN prototypes specify LPSTR, and I've assumed this
+  // implies CP_ACP.  However, depending on how this was implemented internally,
+  // conversion to UTF8 might work better.  Investigate this later and confirm.
+  CStringA path_a(path_to_pac_file);
+
+  if (FALSE == InternetInitializeAutoProxyDllWrap(0, CStrBufA(path_a, MAX_PATH),
+                                                  NULL, NULL, NULL)) {
+    HRESULT hr = HRESULTFromLastError();
+    NET_LOG(LE, (_T("[GetProxyForUrlLocal][jsproxy init failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  ON_SCOPE_EXIT(InternetDeInitializeAutoProxyDllWrap, (LPSTR)NULL, 0);
+
+  CStringA url_a(url);
+  CStringA url_hostname_a(GetUriHostNameHostOnly(url, false));
+
+  scoped_hglobal proxy_ptr;
+  DWORD proxy_len = 0;
+  if (FALSE == InternetGetProxyInfoWrap(
+      url_a,
+      url_a.GetLength(),
+      CStrBufA(url_hostname_a, url_hostname_a.GetLength()),
+      url_hostname_a.GetLength(),
+      reinterpret_cast<LPSTR*>(address(proxy_ptr)),
+      &proxy_len)) {
+    HRESULT hr = HRESULTFromLastError();
+    NET_LOG(LE, (_T("[GetProxyForUrlLocal][jsproxy failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  ASSERT1(proxy_ptr && proxy_len > 0);
+  CStringA proxy(reinterpret_cast<LPSTR>(get(proxy_ptr)), proxy_len);
+  ConvertPacResponseToProxyInfo(proxy, proxy_info);
+  return S_OK;
+}
+
+void NetworkConfig::ConvertPacResponseToProxyInfo(
+    const CStringA& response,
+    HttpClient::ProxyInfo* proxy_info) {
+  ASSERT1(proxy_info);
+
+  NET_LOG(L4, (_T("[ConvertPacResponseToProxyInfo][%s]"), CString(response)));
+
+  // The proxy list response from a PAC file for a file is a string of proxies
+  // to attempt in order, delimited by semicolons, with a keyword denoting how
+  // to use the proxy.  For example:
+  //
+  // PROXY prx1.samp.com; PROXY prx2.test.com:8080; SOCKS prx3.test.com; DIRECT
+  //
+  // We convert this to a direct semicolon-separated list of host/ports.  We
+  // stop parsing if we see DIRECT; we omit any non-PROXY entries.
+  CString proxy_list;
+  for (int start = 0; start >= 0 && start < response.GetLength();) {
+    int semi_pos = response.Find(';', start);
+    if (semi_pos < 0) {
+      semi_pos = response.GetLength();
+    }
+
+    CStringA entry = response.Mid(start, semi_pos - start).Trim().MakeLower();
+    if (entry == "direct") {
+      break;
+    }
+    if (0 == entry.Find("proxy ")) {
+      // This is a valid proxy entry.  Strip the leading "PROXY " and add it
+      // to our parsed list.
+      if (!proxy_list.IsEmpty()) {
+        proxy_list.AppendChar(_T(';'));
+      }
+      proxy_list.Append(CString(entry.Mid(6)));
+    }
+
+    start = semi_pos + 1;
+  }
+
+  if (proxy_list.IsEmpty()) {
+    proxy_info->access_type = WINHTTP_ACCESS_TYPE_NO_PROXY;
+    proxy_info->proxy = NULL;
+    proxy_info->proxy_bypass = NULL;
+  } else {
+    // The convention is that any strings in a WINHTTP_PROXY_INFO are expected
+    // to be freed by the caller using GlobalFree().  Convert our intermediary
+    // CString to a GlobalAlloc() buffer and write that out.
+    size_t list_len = (proxy_list.GetLength() + 1) * sizeof(TCHAR);
+    TCHAR* list_hglob = reinterpret_cast<TCHAR*>(::GlobalAlloc(GPTR, list_len));
+    memcpy(list_hglob, proxy_list.GetString(), list_len);
+    proxy_info->access_type = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
+    proxy_info->proxy = list_hglob;
+    proxy_info->proxy_bypass = NULL;
+  }
+}
+
+const NetworkConfigManager* const NetworkConfigManager::kInvalidInstance  =
+    reinterpret_cast<const NetworkConfigManager* const>(-1);
+NetworkConfigManager* NetworkConfigManager::instance_ = NULL;
+LLock NetworkConfigManager::instance_lock_;
+bool NetworkConfigManager::is_machine_ = false;
+
+NetworkConfigManager::NetworkConfigManager() {
+}
+
+NetworkConfigManager::~NetworkConfigManager() {
+  SaveCupCredentialsToRegistry();
+}
+
+HRESULT NetworkConfigManager::CreateInstance() {
+  __mutexScope(instance_lock_);
+  ASSERT1(instance_ != kInvalidInstance);
+  if (!instance_) {
+    NET_LOG(L1, (_T("[NetworkConfigManager::CreateInstance][is_machine: %d]"),
+         is_machine_));
+    instance_ = new NetworkConfigManager();
+    VERIFY1(SUCCEEDED(instance_->InitializeLock()));
+    VERIFY1(SUCCEEDED(instance_->InitializeRegistryKey()));
+    instance_->LoadCupCredentialsFromRegistry();
+  }
+
+  return S_OK;
+}
+
+void NetworkConfigManager::DeleteInstance() {
+  ASSERT1(instance_ != kInvalidInstance);
+
+  NetworkConfigManager* instance =
+      omaha::interlocked_exchange_pointer(&instance_, kInvalidInstance);
+
+  if (kInvalidInstance != instance && NULL != instance) {
+    instance->DeleteInstanceInternal();
+    delete instance;
+  }
+}
+
+NetworkConfigManager& NetworkConfigManager::Instance() {
+  __mutexScope(instance_lock_);
+  if (!instance_) {
+    VERIFY1(SUCCEEDED(NetworkConfigManager::CreateInstance()));
+  }
+  return *instance_;
+}
+
+void NetworkConfigManager::set_is_machine(bool is_machine) {
+  __mutexScope(instance_lock_);
+  if (instance_) {
+    NET_LOG(LE, (_T("set_is_machine called after instance created.")));
+  }
+
+  is_machine_ = is_machine;
+}
+
+void NetworkConfigManager::DeleteInstanceInternal() {
+  __mutexBlock(lock_) {
+    std::map<CString, NetworkConfig*>::iterator it;
+
+    for (it = user_network_config_map_.begin();
+         it != user_network_config_map_.end();
+         ++it) {
+      if (NULL != it->second) {
+        delete it->second;
+      }
+    }
+    user_network_config_map_.clear();
+  }
+}
+
+HRESULT NetworkConfigManager::GetUserNetworkConfig(
+    NetworkConfig** network_config) {
+  CString sid;
+  HRESULT hr = user_info::GetEffectiveUserSid(&sid);
+  if (FAILED(hr)) {
+    NET_LOG(LE, (_T("[GetEffectiveUserSid failed][0x%x]"), hr));
+    return hr;
+  }
+
+  __mutexBlock(lock_) {
+    std::map<CString, NetworkConfig*>::iterator it;
+    it = user_network_config_map_.find(sid);
+
+    if (user_network_config_map_.end() != it) {
+      *network_config = it->second;
+      return S_OK;
+    }
+
+    hr = CreateNetworkConfigInstance(network_config, is_machine_);
+    if (SUCCEEDED(hr)) {
+      user_network_config_map_.insert(std::make_pair(sid, *network_config));
+    }
+
+    return hr;
+  }
+
+  return E_FAIL;
+}
+
+HRESULT NetworkConfigManager::CreateNetworkConfigInstance(
+    NetworkConfig** network_config_ptr,
+    bool is_machine) {
+  ASSERT1(network_config_ptr);
+
+  NetworkConfig* network_config(new NetworkConfig(is_machine));
+  HRESULT hr = network_config->Initialize();
+  if (FAILED(hr)) {
+    NET_LOG(LE, (_T("[NetworkConfig::Initialize() failed][0x%x]"), hr));
+    delete network_config;
+    return hr;
+  }
+
+  *network_config_ptr = network_config;
+  return S_OK;
+}
+
+HRESULT NetworkConfigManager::InitializeLock() {
+  NamedObjectAttributes lock_attr;
+  GetNamedObjectAttributes(kNetworkConfigLock, is_machine_, &lock_attr);
+  return global_lock_.InitializeWithSecAttr(lock_attr.name, &lock_attr.sa) ?
+         S_OK : E_FAIL;
+}
+
+HRESULT NetworkConfigManager::InitializeRegistryKey() {
+  // The registry path under which to store persistent network configuration.
+  // The "network" subkey is created with default security. Below "network",
+  // the "secure" key is created so that only system and administrators have
+  // access to it.
+  CString reg_path = is_machine_ ? MACHINE_REG_UPDATE : USER_REG_UPDATE;
+  reg_path = AppendRegKeyPath(reg_path, kNetworkSubkey);
+  RegKey reg_key_network;
+  DWORD disposition = 0;
+  HRESULT hr = reg_key_network.Create(reg_path,
+                                      NULL,                 // Class.
+                                      0,                    // Options.
+                                      KEY_CREATE_SUB_KEY,   // SAM desired.
+                                      NULL,                 // Security attrs.
+                                      &disposition);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // When initializing for machine, grant access to administrators and system.
+  scoped_ptr<CSecurityAttributes> sa;
+  if (is_machine_) {
+    sa.reset(new CSecurityAttributes);
+    GetAdminDaclSecurityAttributes(sa.get(), GENERIC_ALL);
+  }
+
+  disposition = 0;
+  RegKey reg_key_network_secure;
+  hr = reg_key_network_secure.Create(reg_key_network.Key(),  // Parent.
+                                     kNetworkCupSubkey,      // Subkey name.
+                                     NULL,                   // Class.
+                                     0,                      // Options.
+                                     KEY_READ,               // SAM desired.
+                                     sa.get(),               // Security attrs.
+                                     &disposition);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return S_OK;
+}
+
+HRESULT NetworkConfigManager::SetCupCredentials(
+    const CupCredentials& cup_credentials) {
+  __mutexScope(lock_);
+
+  const std::vector<uint8>& sk_in(cup_credentials.sk);
+
+  if (sk_in.empty()) {
+    return E_INVALIDARG;
+  }
+
+  std::vector<uint8> sk_out;
+  HRESULT hr = EncryptData(NULL, 0, &sk_in.front(), sk_in.size(), &sk_out);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  cup_credentials_.reset(new CupCredentials);
+
+  cup_credentials_->sk.swap(sk_out);
+  cup_credentials_->c.SetString(cup_credentials.c);
+
+  return S_OK;
+}
+
+HRESULT NetworkConfigManager::GetCupCredentials(
+    CupCredentials* cup_credentials) {
+  ASSERT1(cup_credentials);
+  __mutexScope(lock_);
+  if (cup_credentials_ == NULL || cup_credentials_->sk.empty()) {
+    return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
+  }
+
+  std::vector<uint8> decrypted_sk;
+  HRESULT hr = DecryptData(NULL,
+                           0,
+                           &cup_credentials_->sk.front(),
+                           cup_credentials_->sk.size(),
+                           &decrypted_sk);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  cup_credentials->sk.swap(decrypted_sk);
+  cup_credentials->c.SetString(cup_credentials_->c);
+
+  return S_OK;
+}
+
+// This function should be called in singleton creation stage,
+// thus no lock is needed.
+HRESULT NetworkConfigManager::LoadCupCredentialsFromRegistry() {
+  __mutexScope(global_lock_);
+
+  CString reg_path = is_machine_ ? MACHINE_REG_UPDATE : USER_REG_UPDATE;
+  reg_path = AppendRegKeyPath(reg_path, kNetworkSubkey);
+  CString key_name = AppendRegKeyPath(reg_path, kNetworkCupSubkey);
+  RegKey reg_key;
+  HRESULT hr = reg_key.Open(key_name, KEY_READ);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  scoped_array<byte> buf;
+  DWORD buf_length = 0;
+  hr = reg_key.GetValue(kCupClientSecretKey, address(buf), &buf_length);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  CString cookie;
+  hr = reg_key.GetValue(kCupClientCookie, &cookie);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  if (buf_length == 0) {
+    return E_FAIL;
+  }
+  cup_credentials_.reset(new CupCredentials);
+  cup_credentials_->sk.resize(buf_length);
+  memcpy(&cup_credentials_->sk.front(), buf.get(), buf_length);
+  cup_credentials_->c = CT2A(cookie);
+
+  return S_OK;
+}
+
+// This function is called in the destructor only thus no lock is needed.
+HRESULT NetworkConfigManager::SaveCupCredentialsToRegistry() {
+  __mutexScope(global_lock_);
+
+  CString reg_path = is_machine_ ? MACHINE_REG_UPDATE : USER_REG_UPDATE;
+  reg_path = AppendRegKeyPath(reg_path, kNetworkSubkey);
+  CString key_name = AppendRegKeyPath(reg_path, kNetworkCupSubkey);
+  RegKey reg_key;
+  HRESULT hr = reg_key.Open(key_name, KEY_WRITE);
+  if (FAILED(hr)) {
+    NET_LOG(L2, (_T("[Registry key open failed][%s][0x%08x]"), key_name, hr));
+    return hr;
+  }
+
+  if (cup_credentials_ == NULL || cup_credentials_->sk.empty()) {
+    HRESULT hr1 = reg_key.DeleteValue(kCupClientSecretKey);
+    HRESULT hr2 = reg_key.DeleteValue(kCupClientCookie);
+    return (SUCCEEDED(hr1) && SUCCEEDED(hr2)) ? S_OK : HRESULTFromLastError();
+  }
+
+  hr = reg_key.SetValue(kCupClientSecretKey,
+           static_cast<const byte*>(&cup_credentials_->sk.front()),
+           cup_credentials_->sk.size());
+  if (FAILED(hr)) {
+    return hr;
+  }
+  hr = reg_key.SetValue(kCupClientCookie, CA2T(cup_credentials_->c));
+  if (FAILED(hr)) {
+    return hr;
+  }
+  return S_OK;
+}
+
+void NetworkConfigManager::ClearCupCredentials() {
+  __mutexScope(lock_);
+  cup_credentials_.reset(NULL);
+}
+
 }  // namespace omaha
 
diff --git a/net/network_config.h b/net/network_config.h
index ce16cb7..abb3be6 100644
--- a/net/network_config.h
+++ b/net/network_config.h
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -13,12 +13,6 @@
 // limitations under the License.
 // ========================================================================
 
-// TODO(omaha): provide a way to remember the last good network configuration.
-
-// TODO(omaha): maybe reconsider the singleton and allow multiple instances
-// of it to exist, to allow for testability. The app will most likely use it as
-// a singleton but the class itself should allow for multiple instances.
-
 // TODO(omaha): might need to remove dependency on winhttp.h when implementing
 // support for wininet; see http://b/1119232
 
@@ -28,10 +22,12 @@
 #include <windows.h>
 #include <winhttp.h>
 #include <atlstr.h>
+#include <map>
 #include <vector>
 #include "base/basictypes.h"
 #include "base/scoped_ptr.h"
-#include "omaha/common/synchronized.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/synchronized.h"
 #include "omaha/net/detector.h"
 #include "omaha/net/http_client.h"
 #include "omaha/net/proxy_auth.h"
@@ -44,6 +40,24 @@
 
 namespace omaha {
 
+class RegKey;
+
+// The cup credentials are persisted across sessions. The sk is encrypted
+// while on the disk so only a user with the same login credentials as
+// the encryptor can decrypt it. The credentials are protected
+// using the system default security, so users can't modify each other's
+// credentials. In case of elevated administrators, the credentials are
+// protected from the non-elevated administrators, so the latter can't
+// read the keys and attack the elevated administrator.
+//
+// Cup credentials can be negotiated using either production keys or
+// test keys. There is a registry value override to specify that test keys
+// be used. For the change to be effective, the old credentials must be cleared.
+struct CupCredentials {
+  std::vector<uint8> sk;             // shared key (sk)
+  CStringA c;                        // client cookie (c)
+};
+
 // There are three ways by which an application could connect to the Internet:
 // 1. Direct connection.
 //    The config for the direction connection must not specify WPAD information
@@ -54,10 +68,10 @@
 //    The config for proxy auto detection should include either the auto-detect
 //    flag or the auto configuration url. Named proxy information is discarded
 //    if present.
-struct Config {
-  Config() : auto_detect(false) {}
+struct ProxyConfig {
+  ProxyConfig() : auto_detect(false), priority(PROXY_PRIORITY_DEFAULT_NORMAL) {}
 
-  // Mostly used for debugging purposes to identify who created the instance.
+  // Used to uniquely identify a proxy.
   CString source;
 
   // Specifies the configuration is WPAD.
@@ -76,44 +90,30 @@
   // result in compatibility problems with BITS. Fix this.
   CString proxy;
   CString proxy_bypass;
-};
 
-struct CupCredentials;
+  // Suggested priority of the proxy config. When establishing network
+  // connections, it is a good idea to try higher priority proxy first.
+  enum Priority {
+    PROXY_PRIORITY_DEFAULT_NORMAL = 0,
+    PROXY_PRIORITY_DEFAULT_BROWSER = 1,
+    PROXY_PRIORITY_LAST_KNOWN_GOOD = 2,
+    PROXY_PRIORITY_OVERRIDE = 3,
+  } priority;
+};
 
 // Manages the network configurations.
 class NetworkConfig {
-  public:
-
+ public:
   // Abstracts the Internet session, as provided by winhttp or wininet.
   // A winhttp session should map to one and only one identity. in other words,
   // a winhttp session is used to manage the network traffic of a single
   // authenticated user, or a group of anonymous users.
   struct Session {
-    Session()
-        : session_handle(NULL),
-          impersonation_token(NULL) {}
+    Session() : session_handle(NULL) {}
 
     HINTERNET session_handle;
-    HANDLE    impersonation_token;
   };
 
-  // Instance, Initialize, and DeleteInstance methods below are not thread safe.
-  // The caller must initialize and cleanup the instance before going
-  // multithreaded.
-  //
-  // Gets the singleton instance of the class.
-  // TODO(omaha): rename to GetInstance for consistency
-  static NetworkConfig& Instance();
-
-  // Initializes the instance. It takes ownership of the impersonation token
-  // provided.
-  HRESULT Initialize(bool is_machine, HANDLE impersonation_token);
-
-  bool is_initialized() const { return is_initialized_; }
-
-  // Cleans up the class instance.
-  static void DeleteInstance();
-
   // Hooks up a proxy detector. The class takes ownership of the detector.
   void Add(ProxyDetectorInterface* detector);
 
@@ -125,27 +125,43 @@
   // Detects the network configuration for each of the registered detectors.
   HRESULT Detect();
 
+  // Detects the network configuration for the given source.
+  HRESULT Detect(const CString& proxy_source, ProxyConfig* config) const;
+
+  static HRESULT ConfigFromIdentifier(const CString& id, ProxyConfig* config);
+
+  static bool ProxySortPredicate(const ProxyConfig& config1,
+                                 const ProxyConfig& config2) {
+    return config1.priority > config2.priority;
+  }
+
+  // Sort the proxy configs based on their priorities. Proxy with higher
+  // priority precedes that with lower priority.
+  static void SortProxies(std::vector<ProxyConfig>* configurations);
+
+  void AppendLastKnownGoodProxyConfig(
+      std::vector<ProxyConfig>* configurations) const;
+
+  // Adds static configurations (WPAD & direct) to current detected network
+  // configuration list.
+  static void AppendStaticProxyConfigs(
+      std::vector<ProxyConfig>* configurations);
+
   // Returns the detected configurations.
-  std::vector<Config> GetConfigurations() const;
+  std::vector<ProxyConfig> GetConfigurations() const;
 
   // Gets the persisted CUP credentials.
-  HRESULT GetCupCredentials(CupCredentials* cup_credentials);
+  HRESULT GetCupCredentials(CupCredentials* cup_credentials) const;
 
   // Saves the CUP credentials in persistent storage. If the parameter is null,
   // it clears the credentials.
-  HRESULT SetCupCredentials(const CupCredentials* cup_credentials);
-
-  // The caption is an unformatted string. The message is a formatted string
-  // that accepts one FormatMessage parameter, the proxy server. The
-  // cancel_prompt_threshold is the max number of times the user will see
-  // prompts for this process.
-  void ConfigureProxyAuth(const CString& caption, const CString& message,
-                          HWND parent, uint32 cancel_prompt_threshold);
+  HRESULT SetCupCredentials(const CupCredentials* cup_credentials) const;
 
   // Prompts for credentials, or gets cached credentials if they exist.
   bool GetProxyCredentials(bool allow_ui,
                            bool force_ui,
                            const CString& proxy_settings,
+                           const ProxyAuthConfig& proxy_auth_config,
                            bool is_https,
                            CString* username,
                            CString* password,
@@ -168,11 +184,11 @@
   Session session() const { return session_; }
 
   // Returns the global configuration override if available.
-  HRESULT GetConfigurationOverride(Config* configuration_override);
+  HRESULT GetConfigurationOverride(ProxyConfig* configuration_override);
 
   // Sets the global configuration override. The function clears the existing
   // configuration if the parameter is NULL.
-  void SetConfigurationOverride(const Config* configuration_override);
+  void SetConfigurationOverride(const ProxyConfig* configuration_override);
 
   // True if the CUP test keys are being used to negotiate the CUP
   // credentials.
@@ -181,20 +197,29 @@
   // Returns the prefix of the user agent string.
   static CString GetUserAgent();
 
+  // Returns the MID value under UpdateDev.
+  static CString GetMID();
+
   // Eliminates the redundant configurations, for example, if multiple
   // direct connection or proxy auto-detect occur.
-  static void RemoveDuplicates(std::vector<Config>*);
+  static void RemoveDuplicates(std::vector<ProxyConfig>*);
+
+  // Saves/loads a proxy source and auto_detect information to the registry
+  // so that that proxy can be tried with high priority when establishing
+  // network connections later on.
+  static HRESULT SaveProxyConfig(const ProxyConfig& config);
+  HRESULT LoadProxyConfig(ProxyConfig* config) const;
 
   // Parses a network configuration string. The format of the string is:
   // wpad=[false|true];script=script_url;proxy=host:port
   // Ignores the names and the values it does not understand.
-  static Config ParseNetConfig(const CString& net_config);
+  static ProxyConfig ParseNetConfig(const CString& net_config);
 
   // Serializes configurations for debugging purposes.
-  static CString ToString(const std::vector<Config>& configurations);
-  static CString ToString(const Config& configuration);
+  static CString ToString(const std::vector<ProxyConfig>& configurations);
+  static CString ToString(const ProxyConfig& configuration);
 
-  static int GetAccessType(const Config& config);
+  static int GetAccessType(const ProxyConfig& config);
 
   // Returns s1 + delim + s2. Consider making it an utility function if
   // more usage patterns are found.
@@ -202,50 +227,50 @@
                              const TCHAR* s2,
                              const TCHAR* delim);
 
+  // Uses jsproxy to use a PAC proxy configuration file stored on the local
+  // drive, instead of one sourced from WPAD.
+  static HRESULT GetProxyForUrlLocal(const CString& url,
+                                     const CString& path_to_pac_file,
+                                     HttpClient::ProxyInfo* proxy_info);
+
  private:
-  NetworkConfig();
+  explicit NetworkConfig(bool is_machine);
   ~NetworkConfig();
 
-  HRESULT InitializeLock();
-  HRESULT InitializeRegistryKey();
+  HRESULT Initialize();
 
-  static NetworkConfig* const kInvalidInstance;
-  static NetworkConfig* instance_;
+  // Configures the proxy auth credentials options. Called by Initialize().
+  void ConfigureProxyAuth();
 
-  // Registry sub key where network configuration is persisted.
-  static const TCHAR* const kNetworkSubkey;
+  // Creates the proxy configuration registry key for the calling user
+  // identified by the token.
+  static HRESULT CreateProxyConfigRegKey(RegKey* key);
 
-  // Registry sub key where CUP configuration is persisted.
-  static const TCHAR* const kNetworkCupSubkey;
-
-  // The secret key must be encrypted by the caller. This class does not do any
-  // encryption.
-  static const TCHAR* const kCupClientSecretKey;      // CUP sk.
-  static const TCHAR* const kCupClientCookie;         // CUP c.
+  // Converts a response string from a PAC script into an WinHTTP proxy
+  // descriptor struct.
+  static void ConvertPacResponseToProxyInfo(const CStringA& response,
+                                            HttpClient::ProxyInfo* proxy_info);
 
   static const TCHAR* const kUserAgent;
 
+  static const TCHAR* const kRegKeyProxy;
+  static const TCHAR* const kRegValueSource;
+
+  static const TCHAR* const kWPADIdentifier;
+  static const TCHAR* const kDirectConnectionIdentifier;
+
   bool is_machine_;     // True if the instance is initialized for machine.
 
-  std::vector<Config> configurations_;
+  std::vector<ProxyConfig> configurations_;
   std::vector<ProxyDetectorInterface*> detectors_;
 
-  CString sid_;   // The user sid.
-
-  // The registry path under which to store persistent network configuration.
-  CString registry_update_network_path_;
-
   // Synchronizes access to per-process instance data, which includes
   // the detectors and configurations.
   LLock lock_;
 
-  // Synchronizes access to per-session instance data, such as the
-  // CUP credentials.
-  GLock global_lock_;
-
   bool is_initialized_;
 
-  scoped_ptr<Config> configuration_override_;
+  scoped_ptr<ProxyConfig> configuration_override_;
 
   Session session_;
   scoped_ptr<HttpClient> http_client_;
@@ -258,28 +283,70 @@
   // ConfigureProxyAuth().
   ProxyAuth proxy_auth_;
 
+  friend class NetworkConfigManager;
   DISALLOW_EVIL_CONSTRUCTORS(NetworkConfig);
 };
 
-// For unittests, where creation and termination of NetworkConfig instances
-// is required, the implementation disables the dead reference detection. This
-// forces an inline of the code below for unit tests, so different behavior
-// can be achieved, even though the rest of the implementation compiles in
-// a library. It is somehow brittle but good enough for now.
+class NetworkConfigManager {
+ public:
+  static NetworkConfigManager& Instance();
+  static void DeleteInstance();
 
-#ifdef UNITTEST
-__forceinline
-#else
-  inline
-#endif
-void NetworkConfig::DeleteInstance() {
-  delete instance_;
-#ifdef UNITTEST
-  instance_ = NULL;
-#else
-  instance_ = kInvalidInstance;
-#endif
-}
+  // Directs this singleton class to create machine or user instance.
+  static void set_is_machine(bool is_machine);
+
+  HRESULT GetUserNetworkConfig(NetworkConfig** network_config);
+
+  // Gets the persisted CUP credentials.
+  HRESULT GetCupCredentials(CupCredentials* cup_credentials);
+
+  // Saves the CUP credentials in persistent storage.
+  HRESULT SetCupCredentials(const CupCredentials& cup_credentials);
+
+  void ClearCupCredentials();
+
+ private:
+  explicit NetworkConfigManager();
+  ~NetworkConfigManager();
+
+  static HRESULT CreateInstance();
+
+  void DeleteInstanceInternal();
+
+  HRESULT InitializeLock();
+  HRESULT InitializeRegistryKey();
+
+  HRESULT CreateNetworkConfigInstance(NetworkConfig** network_config_ptr,
+                                      bool is_machine);
+  HRESULT LoadCupCredentialsFromRegistry();
+  HRESULT SaveCupCredentialsToRegistry();
+
+  std::map<CString, NetworkConfig*> user_network_config_map_;
+  scoped_ptr<CupCredentials> cup_credentials_;
+
+  LLock lock_;
+
+  // Synchronizes access to CUP registry.
+  GLock global_lock_;
+
+  // Registry sub key where network configuration is persisted.
+  static const TCHAR* const kNetworkSubkey;
+
+  // Registry sub key where CUP configuration is persisted.
+  static const TCHAR* const kNetworkCupSubkey;
+
+  // The secret key must be encrypted by the caller. This class does not do any
+  // encryption.
+  static const TCHAR* const kCupClientSecretKey;      // CUP sk.
+  static const TCHAR* const kCupClientCookie;         // CUP c.
+
+  static const NetworkConfigManager* const kInvalidInstance;
+  static NetworkConfigManager* instance_;
+  static LLock instance_lock_;
+  static bool is_machine_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(NetworkConfigManager);
+};
 
 }   // namespace omaha
 
diff --git a/net/network_config_unittest.cc b/net/network_config_unittest.cc
index d751252..68b958b 100644
--- a/net/network_config_unittest.cc
+++ b/net/network_config_unittest.cc
@@ -15,12 +15,14 @@
 
 #include <windows.h>
 #include <atlconv.h>
+#include <algorithm>
 #include <cstring>
 #include "base/basictypes.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
+#include "omaha/base/module_utils.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
 #include "omaha/net/cup_request.h"
 #include "omaha/net/cup_utils.h"
 #include "omaha/net/http_client.h"
@@ -43,37 +45,39 @@
 };
 
 TEST_F(NetworkConfigTest, GetAccessType) {
-  EXPECT_EQ(NetworkConfig::GetAccessType(Config()),
+  EXPECT_EQ(NetworkConfig::GetAccessType(ProxyConfig()),
             WINHTTP_ACCESS_TYPE_NO_PROXY);
 
-  Config config;
+  ProxyConfig config;
   config.auto_detect = true;
   EXPECT_EQ(NetworkConfig::GetAccessType(config),
             WINHTTP_ACCESS_TYPE_AUTO_DETECT);
 
-  config = Config();
+  config = ProxyConfig();
   config.auto_config_url = _T("http://foo");
   EXPECT_EQ(NetworkConfig::GetAccessType(config),
             WINHTTP_ACCESS_TYPE_AUTO_DETECT);
 
-  config = Config();
+  config = ProxyConfig();
   config.auto_detect = true;
   config.proxy = _T("foo");
   EXPECT_EQ(NetworkConfig::GetAccessType(config),
             WINHTTP_ACCESS_TYPE_AUTO_DETECT);
 
-  config = Config();
+  config = ProxyConfig();
   config.proxy = _T("foo");
   EXPECT_EQ(NetworkConfig::GetAccessType(config),
             WINHTTP_ACCESS_TYPE_NAMED_PROXY);
 }
 
 TEST_F(NetworkConfigTest, CupCredentials) {
-  NetworkConfig& network_config = NetworkConfig::Instance();
-  EXPECT_HRESULT_SUCCEEDED(network_config.SetCupCredentials(NULL));
+  NetworkConfig* network_config = NULL;
+  EXPECT_HRESULT_SUCCEEDED(
+      NetworkConfigManager::Instance().GetUserNetworkConfig(&network_config));
+  EXPECT_HRESULT_SUCCEEDED(network_config->SetCupCredentials(NULL));
 
   CupCredentials cup_credentials1;
-  EXPECT_HRESULT_FAILED(network_config.GetCupCredentials(&cup_credentials1));
+  EXPECT_HRESULT_FAILED(network_config->GetCupCredentials(&cup_credentials1));
 
   // Start with some random bytes. Persist them as sk and as B64-encoded c.
   // Read back and verify they match.
@@ -85,18 +89,20 @@
   cup_credentials1.sk.insert(cup_credentials1.sk.begin(), first, last);
   cup_credentials1.c = cup_utils::B64Encode(data, arraysize(data));
 
-  EXPECT_HRESULT_SUCCEEDED(network_config.SetCupCredentials(&cup_credentials1));
+  EXPECT_HRESULT_SUCCEEDED(
+      network_config->SetCupCredentials(&cup_credentials1));
 
   CupCredentials cup_credentials2;
-  EXPECT_HRESULT_SUCCEEDED(network_config.GetCupCredentials(&cup_credentials2));
+  EXPECT_HRESULT_SUCCEEDED(
+      network_config->GetCupCredentials(&cup_credentials2));
   EXPECT_EQ(cup_credentials1.sk.size(), cup_credentials2.sk.size());
   EXPECT_TRUE(memcmp(&cup_credentials1.sk.front(),
                      &cup_credentials2.sk.front(),
                      cup_credentials1.sk.size()) == 0);
   EXPECT_STREQ(cup_credentials1.c, cup_credentials2.c);
 
-  EXPECT_HRESULT_SUCCEEDED(network_config.SetCupCredentials(NULL));
-  EXPECT_HRESULT_FAILED(network_config.GetCupCredentials(&cup_credentials1));
+  EXPECT_HRESULT_SUCCEEDED(network_config->SetCupCredentials(NULL));
+  EXPECT_HRESULT_FAILED(network_config->GetCupCredentials(&cup_credentials1));
 }
 
 TEST_F(NetworkConfigTest, JoinStrings) {
@@ -120,10 +126,10 @@
 // Hosts names used in the test are only used as string literals.
 TEST_F(NetworkConfigTest, RemoveDuplicates) {
   // 'source' is not considered in the hash computation.
-  std::vector<Config> configurations;
-  Config cfg1;
+  std::vector<ProxyConfig> configurations;
+  ProxyConfig cfg1;
   cfg1.source = "foo";
-  Config cfg2;
+  ProxyConfig cfg2;
   cfg2.source = "bar";
   configurations.push_back(cfg1);
   configurations.push_back(cfg2);
@@ -132,7 +138,7 @@
   configurations.clear();
 
   // Remove redundant direct connection configurations.
-  Config direct_config;
+  ProxyConfig direct_config;
   configurations.push_back(direct_config);
   configurations.push_back(direct_config);
   NetworkConfig::RemoveDuplicates(&configurations);
@@ -140,7 +146,7 @@
   configurations.clear();
 
   // Remove redundant WPAD configurations.
-  Config wpad_config;
+  ProxyConfig wpad_config;
   wpad_config.auto_detect = true;
   configurations.push_back(wpad_config);
   configurations.push_back(wpad_config);
@@ -149,7 +155,7 @@
   configurations.clear();
 
   // Remove redundant WPAD with auto config url configurations.
-  Config wpad_url_config;
+  ProxyConfig wpad_url_config;
   wpad_url_config.auto_detect = true;
   wpad_url_config.auto_config_url = _T("http://www.google.com/wpad.dat");
   configurations.push_back(wpad_url_config);
@@ -159,7 +165,7 @@
   configurations.clear();
 
   // Remove redundant named proxy configurations.
-  Config named_proxy_config;
+  ProxyConfig named_proxy_config;
   named_proxy_config.proxy = _T("www1.google.com:3128");
   configurations.push_back(named_proxy_config);
   configurations.push_back(named_proxy_config);
@@ -168,7 +174,7 @@
   configurations.clear();
 
   // Does not remove distinct configurations.
-  Config named_proxy_config_alt;
+  ProxyConfig named_proxy_config_alt;
   named_proxy_config_alt.proxy = _T("www2.google.com:3128");
   configurations.push_back(named_proxy_config);
   configurations.push_back(named_proxy_config_alt);
@@ -180,7 +186,7 @@
 }
 
 TEST_F(NetworkConfigTest, ParseNetConfig) {
-  Config config = NetworkConfig::ParseNetConfig(_T(""));
+  ProxyConfig config = NetworkConfig::ParseNetConfig(_T(""));
   EXPECT_EQ(false, config.auto_detect);
   EXPECT_EQ(true,  config.auto_config_url.IsEmpty());
   EXPECT_EQ(true,  config.proxy.IsEmpty());
@@ -207,16 +213,98 @@
 }
 
 TEST_F(NetworkConfigTest, ConfigurationOverride) {
-  NetworkConfig& network_config = NetworkConfig::Instance();
+  NetworkConfig* network_config = NULL;
+  EXPECT_HRESULT_SUCCEEDED(
+      NetworkConfigManager::Instance().GetUserNetworkConfig(&network_config));
 
-  Config actual, expected;
+  ProxyConfig actual, expected;
   expected.auto_detect = true;
-  network_config.SetConfigurationOverride(&expected);
-  EXPECT_HRESULT_SUCCEEDED(network_config.GetConfigurationOverride(&actual));
+  network_config->SetConfigurationOverride(&expected);
+  EXPECT_HRESULT_SUCCEEDED(network_config->GetConfigurationOverride(&actual));
   EXPECT_EQ(expected.auto_detect, actual.auto_detect);
 
-  network_config.SetConfigurationOverride(NULL);
-  EXPECT_EQ(E_FAIL, network_config.GetConfigurationOverride(&actual));
+  network_config->SetConfigurationOverride(NULL);
+  EXPECT_EQ(E_FAIL, network_config->GetConfigurationOverride(&actual));
+}
+
+TEST_F(NetworkConfigTest, GetProxyForUrlLocal) {
+  TCHAR module_directory[MAX_PATH] = {0};
+  ASSERT_TRUE(GetModuleDirectory(NULL, module_directory));
+  CString pac_file_path;
+  pac_file_path.Format(_T("%s\\unittest_support\\localproxytest.pac"),
+                       module_directory);
+
+  HttpClient::ProxyInfo proxy_info = {};
+
+  // The PAC file should emit a preset response for any URL with a hostname
+  // matching *.omahaproxytest.com and DIRECT otherwise.
+
+  EXPECT_HRESULT_SUCCEEDED(NetworkConfig::GetProxyForUrlLocal(
+    _T("http://regex.matches.domain.omahaproxytest.com/test_url/index.html"),
+    pac_file_path, &proxy_info));
+  EXPECT_EQ(WINHTTP_ACCESS_TYPE_NAMED_PROXY, proxy_info.access_type);
+  EXPECT_STREQ(_T("omaha_unittest1;omaha_unittest2:8080"),
+               CString(proxy_info.proxy));
+  EXPECT_EQ(NULL, proxy_info.proxy_bypass);
+
+  if (proxy_info.proxy) {
+    ::GlobalFree(const_cast<TCHAR*>(proxy_info.proxy));
+  }
+  if (proxy_info.proxy_bypass) {
+    ::GlobalFree(const_cast<TCHAR*>(proxy_info.proxy_bypass));
+  }
+
+  EXPECT_HRESULT_SUCCEEDED(NetworkConfig::GetProxyForUrlLocal(
+    _T("http://should.not.match.domain.example.com/test_url/index.html"),
+    pac_file_path, &proxy_info));
+  EXPECT_EQ(WINHTTP_ACCESS_TYPE_NO_PROXY, proxy_info.access_type);
+  EXPECT_EQ(NULL, proxy_info.proxy);
+  EXPECT_EQ(NULL, proxy_info.proxy_bypass);
+}
+
+class NetworkConfigManagerTest : public testing::Test {
+ protected:
+  NetworkConfigManagerTest() {}
+
+  static void SetUpTestCase() {}
+
+  static void TearDownTestCase() {}
+
+  virtual void SetUp() {
+    NetworkConfigManager::Instance().ClearCupCredentials();
+  }
+
+  virtual void TearDown() {
+    NetworkConfigManager::Instance().ClearCupCredentials();
+  }
+};
+
+TEST_F(NetworkConfigManagerTest, CupCredentials) {
+  NetworkConfigManager& ncm(NetworkConfigManager::Instance());
+
+  CupCredentials cup_credentials;
+  EXPECT_EQ(E_INVALIDARG, ncm.SetCupCredentials(cup_credentials));
+
+  ncm.ClearCupCredentials();
+
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_NOT_FOUND),
+            ncm.GetCupCredentials(&cup_credentials));
+
+  const int kKeySizeBytes = 16;
+  cup_credentials.sk.resize(kKeySizeBytes);
+  EXPECT_TRUE(GenRandom(&cup_credentials.sk.front(),
+                         cup_credentials.sk.size()));
+  cup_credentials.c = "a cookie";
+
+  EXPECT_HRESULT_SUCCEEDED(ncm.SetCupCredentials(cup_credentials));
+
+  CupCredentials actual_cup_credentials;
+  EXPECT_HRESULT_SUCCEEDED(ncm.GetCupCredentials(&actual_cup_credentials));
+
+  EXPECT_TRUE(std::equal(actual_cup_credentials.sk.begin(),
+                         actual_cup_credentials.sk.end(),
+                         cup_credentials.sk.begin()));
+  EXPECT_STREQ(actual_cup_credentials.c, cup_credentials.c);
 }
 
 }  // namespace omaha
diff --git a/net/network_request.cc b/net/network_request.cc
index b3283fe..7893c38 100644
--- a/net/network_request.cc
+++ b/net/network_request.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -53,6 +53,10 @@
   return impl_->Pause();
 }
 
+HRESULT NetworkRequest::Resume() {
+  return impl_->Resume();
+}
+
 HRESULT NetworkRequest::Cancel() {
   return impl_->Cancel();
 }
@@ -65,6 +69,10 @@
   return impl_->http_status_code();
 }
 
+void NetworkRequest::set_proxy_auth_config(const ProxyAuthConfig& config) {
+  return impl_->set_proxy_auth_config(config);
+}
+
 void NetworkRequest::set_num_retries(int num_retries) {
   return impl_->set_num_retries(num_retries);
 }
@@ -77,6 +85,10 @@
   return impl_->set_callback(callback);
 }
 
+void NetworkRequest::set_preserve_protocol(bool preserve_protocol) {
+  return impl_->set_preserve_protocol(preserve_protocol);
+}
+
 CString NetworkRequest::response_headers() const {
   return impl_->response_headers();
 }
@@ -95,16 +107,16 @@
   return impl_->set_low_priority(low_priority);
 }
 
-void NetworkRequest::set_network_configuration(
-    const Config* network_configuration) {
-  return impl_->set_network_configuration(network_configuration);
+void NetworkRequest::set_proxy_configuration(
+    const ProxyConfig* proxy_configuration) {
+  return impl_->set_proxy_configuration(proxy_configuration);
 }
 
 HRESULT PostRequest(NetworkRequest* network_request,
                     bool fallback_to_https,
                     const CString& url,
                     const CString& request_string,
-                    CString* response) {
+                    std::vector<uint8>* response) {
   return detail::PostRequest(network_request,
                              fallback_to_https,
                              url,
@@ -114,7 +126,7 @@
 
 HRESULT GetRequest(NetworkRequest* network_request,
                    const CString& url,
-                   CString* response) {
+                   std::vector<uint8>* response) {
   return detail::GetRequest(network_request, url, response);
 }
 
diff --git a/net/network_request.h b/net/network_request.h
index c424e9c..678bcf0 100644
--- a/net/network_request.h
+++ b/net/network_request.h
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -30,7 +30,8 @@
 #include <vector>
 #include "base/basictypes.h"
 #include "base/scoped_ptr.h"
-#include "omaha/common/string.h"
+#include "omaha/base/string.h"
+#include "omaha/base/time.h"
 #include "omaha/net/network_config.h"
 
 namespace omaha {
@@ -45,17 +46,23 @@
  public:
   virtual ~NetworkRequestCallback() {}
 
+  // Notifies download begins. This gives the callee a chance to initialize its
+  // download state, such as resetting the download progress.
+  virtual void OnRequestBegin() = 0;
+
   // Indicates the progress of the NetworkRequest.
   //
   // bytes - Current number of bytes transferred, relative to the expected
   //         maximum indicated by the bytes_total.
-  // bytes_max - The expected number of total bytes to transfer This is usually
-  //             the content length value or 0 if the content length is not
-  //             available.
+  // bytes_total - The expected total number of bytes to transfer. This is
+  //               usually the content length value or 0 if the content length
+  //               is not available.
   // status - WinHttp status codes regarding the progress of the request.
   // status_text - Additional information, when available.
   virtual void OnProgress(int bytes, int bytes_total,
                           int status, const TCHAR* status_text) = 0;
+
+  virtual void OnRequestRetryScheduled(time64 next_retry_time) = 0;
 };
 
 class  HttpRequestInterface;
@@ -122,9 +129,15 @@
   // Downloads a url to a file.
   HRESULT DownloadFile(const CString& url, const CString& filename);
 
-  // Temporarily stops the network request.
+  // Enables a separate thread to temporarily stops the network downloading.
+  // The downloading thread will be blocked inside NetworkRequest::Post/Get
+  // infinitely by an event until Resume/Cancel/Close is called.
   HRESULT Pause();
 
+  // Enables a separate thread to resume current network request if it is in
+  // pause state.
+  HRESULT Resume();
+
   // Adds a request header. The header with the same name is only added once.
   // The method is only good enough for what Omaha needs and it is not good
   // for general purpose header manipulation, which is quite sophisticated.
@@ -145,6 +158,8 @@
   // Returns a trace of the request for logging purposes.
   CString trace() const;
 
+  void set_proxy_auth_config(const ProxyAuthConfig& proxy_auth_config);
+
   // Sets the number of retries for the request. The retry mechanism uses
   // exponential backoff to decrease the rate of the retries.
   void set_num_retries(int num_retries);
@@ -163,7 +178,9 @@
   // Overrides detecting the network configuration and uses the configuration
   // specified. If parameter is NULL, it defaults to detecting the configuration
   // automatically.
-  void set_network_configuration(const Config* network_configuration);
+  void set_proxy_configuration(const ProxyConfig* proxy_configuration);
+
+  void set_preserve_protocol(bool preserve_protocol);
 
  private:
   // Uses pimpl idiom to minimize dependencies on implementation details.
@@ -179,12 +196,12 @@
                     bool fallback_to_https,
                     const CString& url,
                     const CString& request_string,
-                    CString* response);
+                    std::vector<uint8>* response);
 
 // Gets a request.
 HRESULT GetRequest(NetworkRequest* network_request,
                    const CString& url,
-                   CString* response);
+                   std::vector<uint8>* response);
 
 }   // namespace omaha
 
diff --git a/net/network_request_impl.cc b/net/network_request_impl.cc
index 458e287..5437c77 100644
--- a/net/network_request_impl.cc
+++ b/net/network_request_impl.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -21,13 +21,15 @@
 #include <functional>
 #include <vector>
 #include "base/basictypes.h"
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/scoped_impersonation.h"
-#include "omaha/common/string.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/string.h"
+#include "omaha/base/time.h"
 #include "omaha/net/http_client.h"
 #include "omaha/net/net_utils.h"
 #include "omaha/net/network_config.h"
@@ -71,9 +73,12 @@
 NetworkRequestImpl::NetworkRequestImpl(
     const NetworkConfig::Session& network_session)
       : cur_http_request_(NULL),
-        cur_network_config_(NULL),
-        last_network_error_(S_OK),
+        cur_proxy_config_(NULL),
+        cur_retry_count_(0),
+        last_hr_(S_OK),
+        last_http_status_code_(0),
         http_status_code_(0),
+        proxy_auth_config_(NULL, CString()),
         num_retries_(0),
         low_priority_(false),
         time_between_retries_ms_(kDefaultTimeBetweenRetriesMs),
@@ -82,7 +87,8 @@
         request_buffer_length_(0),
         response_(NULL),
         network_session_(network_session),
-        is_canceled_(false) {
+        is_canceled_(false),
+        preserve_protocol_(false) {
   // NetworkConfig::Initialize must be called before using NetworkRequest.
   // If Winhttp cannot be loaded, this handle will be NULL.
   if (!network_session.session_handle) {
@@ -94,6 +100,11 @@
   // the retry loop and return control to the caller.
   reset(event_cancel_, ::CreateEvent(NULL, true, false, NULL));
   ASSERT1(event_cancel_);
+
+  const CString mid(NetworkConfig::GetMID());
+  if (!mid.IsEmpty()) {
+    AddHeader(kHeaderXMID, mid);
+  }
 }
 
 NetworkRequestImpl::~NetworkRequestImpl() {
@@ -107,10 +118,12 @@
     response_->clear();
   }
   response_headers_.Empty();
-  http_status_code_   = 0;
-  last_network_error_ = S_OK;
-  cur_http_request_   = NULL;
-  cur_network_config_ = NULL;
+  http_status_code_      = 0;
+  cur_http_request_      = NULL;
+  cur_proxy_config_      = NULL;
+  cur_retry_count_       = 0;
+  last_hr_               = S_OK;
+  last_http_status_code_ = 0;
 }
 
 HRESULT NetworkRequestImpl::Close() {
@@ -172,47 +185,75 @@
 }
 
 HRESULT NetworkRequestImpl::Pause() {
-  return E_NOTIMPL;
+  NET_LOG(L3, (_T("[NetworkRequestImpl::Pause]")));
+  HRESULT hr = S_OK;
+
+  for (size_t i = 0; i != http_request_chain_.size(); ++i) {
+    HRESULT hr2 = http_request_chain_[i]->Pause();
+
+    // Only overwrite hr if it doesn't have useful error information.
+    if (SUCCEEDED(hr)) {
+      hr = hr2;
+    }
+  }
+  return hr;
+}
+
+HRESULT NetworkRequestImpl::Resume() {
+  NET_LOG(L3, (_T("[NetworkRequestImpl::Pause]")));
+  HRESULT hr = S_OK;
+
+  for (size_t i = 0; i != http_request_chain_.size(); ++i) {
+    HRESULT hr2 = http_request_chain_[i]->Resume();
+
+    // Only overwrite hr if it doesn't have useful error information.
+    if (SUCCEEDED(hr)) {
+      hr = hr2;
+    }
+  }
+  return hr;
 }
 
 HRESULT NetworkRequestImpl::DoSendWithRetries() {
   ASSERT1(num_retries_ >= 0);
   ASSERT1(response_ || !filename_.IsEmpty());
 
-  // Impersonate the user if a valid impersonation token is presented.
-  // Continue unimpersonated if the impersonation fails.
-  HANDLE impersonation_token = network_session_.impersonation_token;
-  scoped_impersonation impersonate_user(impersonation_token);
-  if (impersonation_token) {
-    DWORD result = impersonate_user.result();
-    ASSERT(result == ERROR_SUCCESS, (_T("impersonation failed %d"), result));
-    NET_LOG(L3, (_T("[impersonating %s]"), GetTokenUser(impersonation_token)));
-  }
-
   Reset();
 
   int http_status_code(0);
   CString response_headers;
   std::vector<uint8> response;
 
-  trace_.AppendFormat(_T("Url=%s\r\n"), url_);
+  SafeCStringAppendFormat(&trace_, _T("Url=%s\r\n"), url_);
 
   HRESULT hr = S_OK;
   int wait_interval_ms = time_between_retries_ms_;
-  for (int i = 0; i < 1 + num_retries_; ++i) {
+  for (cur_retry_count_ = 0;
+       cur_retry_count_ < 1 + num_retries_;
+       ++cur_retry_count_) {
     if (IsHandleSignaled(get(event_cancel_))) {
       ASSERT1(is_canceled_);
 
       // There is no state to be set when the request is canceled.
-      return OMAHA_NET_E_REQUEST_CANCELLED;
+      return GOOPDATE_E_CANCELLED;
     }
 
     // Wait before retrying if there are retries to be done.
-    if (i > 0) {
+    if (cur_retry_count_ > 0) {
+      if (callback_) {
+        const time64 next_retry_time = GetCurrent100NSTime() +
+                                       wait_interval_ms * kMillisecsTo100ns;
+        callback_->OnRequestRetryScheduled(next_retry_time);
+      }
+
       NET_LOG(L3, (_T("[wait %d ms]"), wait_interval_ms));
       VERIFY1(::WaitForSingleObject(get(event_cancel_),
                                     wait_interval_ms) != WAIT_FAILED);
 
+      if (callback_) {
+        callback_->OnRequestBegin();
+      }
+
       // Compute the next wait interval and check for multiplication overflow.
       if (wait_interval_ms > INT_MAX / kTimeBetweenRetriesMultiplier) {
         ASSERT1(false);
@@ -222,16 +263,16 @@
       wait_interval_ms *= kTimeBetweenRetriesMultiplier;
     }
 
-    DetectNetworkConfiguration(&network_configurations_);
-    ASSERT1(!network_configurations_.empty());
+    DetectProxyConfiguration(&proxy_configurations_);
+    ASSERT1(!proxy_configurations_.empty());
     OPT_LOG(L2, (_T("[detected configurations][\r\n%s]"),
-                 NetworkConfig::ToString(network_configurations_)));
+                 NetworkConfig::ToString(proxy_configurations_)));
 
     hr = DoSend(&http_status_code, &response_headers, &response);
     HttpClient::StatusCodeClass status_code_class =
         HttpClient::GetStatusCodeClass(http_status_code);
     if (SUCCEEDED(hr) ||
-        hr == OMAHA_NET_E_REQUEST_CANCELLED ||
+        hr == GOOPDATE_E_CANCELLED ||
         status_code_class == HttpClient::STATUS_CODE_CLIENT_ERROR) {
       break;
     }
@@ -272,9 +313,9 @@
   // TODO(omaha): remember the last good configuration and prefer that for
   // future requests.
   HRESULT hr = S_OK;
-  ASSERT1(!network_configurations_.empty());
-  for (size_t i = 0; i != network_configurations_.size(); ++i) {
-    cur_network_config_ = &network_configurations_[i];
+  ASSERT1(!proxy_configurations_.empty());
+  for (size_t i = 0; i != proxy_configurations_.size(); ++i) {
+    cur_proxy_config_ = &proxy_configurations_[i];
     hr = DoSendWithConfig(http_status_code, response_headers, response);
     if (i == 0 && FAILED(hr)) {
       error_hr = hr;
@@ -284,16 +325,19 @@
     }
 
     if (SUCCEEDED(hr) ||
-        hr == OMAHA_NET_E_REQUEST_CANCELLED ||
+        hr == GOOPDATE_E_CANCELLED ||
+        hr == CI_E_BITS_DISABLED ||
         *http_status_code == HTTP_STATUS_NOT_FOUND) {
       break;
     }
   }
 
-  // There are only three possible outcomes: success, cancel, and an error
-  // other than cancel.
+  // There are only four possible outcomes: success, cancel, BITS disabled
+  // and an error other than these.
   HRESULT result = S_OK;
-  if (SUCCEEDED(hr) || hr == OMAHA_NET_E_REQUEST_CANCELLED) {
+  if (SUCCEEDED(hr) ||
+      hr == GOOPDATE_E_CANCELLED ||
+      hr == CI_E_BITS_DISABLED) {
     result = hr;
   } else {
     // In case of errors, log the error and the response returned by the first
@@ -304,8 +348,8 @@
     response->swap(error_response);
   }
 
-  OPT_LOG(L3, (_T("[Send response received][%s]"),
-      VectorToPrintableString(*response)));
+  OPT_LOG(L3, (_T("[Send response received][result 0x%x][status code %d][%s]"),
+      result, *http_status_code, VectorToPrintableString(*response)));
 
 #ifdef DEBUG
   if (!filename_.IsEmpty()) {
@@ -325,18 +369,19 @@
   ASSERT1(response);
   ASSERT1(http_status_code);
 
+  bool    first_error_from_http_request_saved = false;
   HRESULT error_hr = S_OK;
   int     error_http_status_code = 0;
   CString error_response_headers;
   std::vector<uint8> error_response;
 
-  ASSERT1(cur_network_config_);
+  ASSERT1(cur_proxy_config_);
 
   CString msg;
-  msg.Format(_T("Trying config: %s"),
-             NetworkConfig::ToString(*cur_network_config_));
-  NET_LOG(L3, (_T("[msg %s]"), msg));
-  trace_.AppendFormat(_T("%s.\r\n"), msg);
+  SafeCStringFormat(&msg, _T("Trying config: %s"),
+                    NetworkConfig::ToString(*cur_proxy_config_));
+  NET_LOG(L3, (_T("[%s]"), msg));
+  SafeCStringAppendFormat(&trace_, _T("%s.\r\n"), msg);
 
   HRESULT hr = S_OK;
   ASSERT1(!http_request_chain_.empty());
@@ -344,22 +389,25 @@
     cur_http_request_ = http_request_chain_[i];
 
     CString msg;
-    msg.Format(_T("trying %s"), cur_http_request_->ToString());
+    SafeCStringFormat(&msg, _T("trying %s"), cur_http_request_->ToString());
     NET_LOG(L3, (_T("[%s]"), msg));
-    trace_.AppendFormat(_T("%s.\r\n"), msg);
+    SafeCStringAppendFormat(&trace_, _T("%s.\r\n"), msg);
 
     hr = DoSendHttpRequest(http_status_code, response_headers, response);
 
-    msg.Format(_T("Send request returned 0x%08x. Http status code %d"),
-               hr, *http_status_code);
+    SafeCStringFormat(&msg,
+                      _T("Send request returned 0x%08x. Http status code %d"),
+                      hr, *http_status_code);
     NET_LOG(L3, (_T("[%s]"), msg));
-    trace_.AppendFormat(_T("%s.\r\n"), msg);
+    SafeCStringAppendFormat(&trace_, _T("%s.\r\n"), msg);
 
-    if (i == 0 && FAILED(hr)) {
+    if (!first_error_from_http_request_saved && FAILED(hr) &&
+        hr != CI_E_BITS_DISABLED) {
       error_hr = hr;
       error_http_status_code = cur_http_request_->GetHttpStatusCode();
       error_response_headers = cur_http_request_->GetResponseHeaders();
       cur_http_request_->GetResponse().swap(error_response);
+      first_error_from_http_request_saved = true;
     }
 
     // The chain traversal stops when the request is successful or
@@ -367,7 +415,7 @@
     // In the case of 404 response, all HttpRequests are likely to return
     // the same 404 response.
     if (SUCCEEDED(hr) ||
-        hr == OMAHA_NET_E_REQUEST_CANCELLED ||
+        hr == GOOPDATE_E_CANCELLED ||
         *http_status_code == HTTP_STATUS_NOT_FOUND) {
       break;
     }
@@ -376,17 +424,27 @@
   // There are only three possible outcomes: success, cancel, and an error
   // other than cancel.
   HRESULT result = S_OK;
-  if (SUCCEEDED(hr) || hr == OMAHA_NET_E_REQUEST_CANCELLED) {
+  if (SUCCEEDED(hr) || hr == GOOPDATE_E_CANCELLED) {
     result = hr;
   } else {
-    // In case of errors, log the error and the response returned by the first
-    // http request object.
-    result = error_hr;
-    *http_status_code = error_http_status_code;
-    *response_headers = error_response_headers;
-    response->swap(error_response);
+    ASSERT1(first_error_from_http_request_saved);
+    if (first_error_from_http_request_saved) {
+      // In case of errors, log the error and the response returned by the first
+      // active http request object.
+      result = error_hr;
+      *http_status_code = error_http_status_code;
+      *response_headers = error_response_headers;
+      response->swap(error_response);
+    } else {
+      ASSERT1(false);   // BITS is the onlay channel and is disabled.
+      NET_LOG(LE, (_T("Possiblly no viable network channel is available.")));
+      result = E_UNEXPECTED;
+    }
   }
 
+  if (SUCCEEDED(hr)) {
+    NetworkConfig::SaveProxyConfig(*cur_proxy_config_);
+  }
   return result;
 }
 
@@ -408,11 +466,17 @@
   cur_http_request_->set_filename(filename_);
   cur_http_request_->set_low_priority(low_priority_);
   cur_http_request_->set_callback(callback_);
-  cur_http_request_->set_additional_headers(additional_headers_);
-  cur_http_request_->set_network_configuration(*cur_network_config_);
+  cur_http_request_->set_additional_headers(BuildPerRequestHeaders());
+  cur_http_request_->set_proxy_configuration(*cur_proxy_config_);
+  cur_http_request_->set_preserve_protocol(preserve_protocol_);
+  cur_http_request_->set_proxy_auth_config(proxy_auth_config_);
 
   if (IsHandleSignaled(get(event_cancel_))) {
-    return OMAHA_NET_E_REQUEST_CANCELLED;
+    return GOOPDATE_E_CANCELLED;
+  }
+
+  if (callback_) {
+    callback_->OnRequestBegin();
   }
 
   // The algorithm is very rough meaning it does not look at the error
@@ -420,27 +484,29 @@
   // it may not make sense to retry at all, for example, let's say the
   // error is ERROR_DISK_FULL.
   NET_LOG(L3, (_T("[%s]"), url_));
-  HRESULT hr = cur_http_request_->Send();
-  NET_LOG(L3, (_T("[HttpRequestInterface::Send returned 0x%08x]"), hr));
+  last_hr_ = cur_http_request_->Send();
+  NET_LOG(L3, (_T("[HttpRequestInterface::Send returned 0x%08x]"), last_hr_));
 
-  if (hr == OMAHA_NET_E_REQUEST_CANCELLED) {
-    return hr;
+  if (last_hr_ == GOOPDATE_E_CANCELLED) {
+    return last_hr_;
   }
 
+  last_http_status_code_ = cur_http_request_->GetHttpStatusCode();
+
   *http_status_code = cur_http_request_->GetHttpStatusCode();
   *response_headers = cur_http_request_->GetResponseHeaders();
   cur_http_request_->GetResponse().swap(*response);
 
   // Check if the computer is connected to the network.
-  if (FAILED(hr)) {
-    hr = IsMachineConnectedToNetwork() ? hr : GOOPDATE_E_NO_NETWORK;
-    return hr;
+  if (FAILED(last_hr_)) {
+    last_hr_ = IsMachineConnectedToNetwork() ? last_hr_ : GOOPDATE_E_NO_NETWORK;
+    return last_hr_;
   }
 
   // Status code must be available if the http request is successful. This
   // is the contract that http requests objects in the fallback chain must
   // implement.
-  ASSERT1(SUCCEEDED(hr) && *http_status_code);
+  ASSERT1(SUCCEEDED(last_hr_) && *http_status_code);
   ASSERT1(HTTP_STATUS_FIRST <= *http_status_code &&
           *http_status_code <= HTTP_STATUS_LAST);
 
@@ -449,14 +515,35 @@
     case HTTP_STATUS_NO_CONTENT:        // 204
     case HTTP_STATUS_PARTIAL_CONTENT:   // 206
     case HTTP_STATUS_NOT_MODIFIED:      // 304
-      hr = S_OK;
+      last_hr_ = S_OK;
       break;
 
     default:
-      hr = HRESULTFromHttpStatusCode(*http_status_code);
+      last_hr_ = HRESULTFromHttpStatusCode(*http_status_code);
       break;
   }
-  return hr;
+  return last_hr_;
+}
+
+CString NetworkRequestImpl::BuildPerRequestHeaders() const {
+  CString headers(additional_headers_);
+
+  const CString& user_agent(cur_http_request_->user_agent());
+  if (!user_agent.IsEmpty()) {
+    SafeCStringAppendFormat(&headers, _T("%s: %s\r\n"),
+                                      kHeaderUserAgent, user_agent);
+  }
+
+  SafeCStringAppendFormat(&headers, _T("%s: 0x%x\r\n"),
+                                    kHeaderXLastHR, last_hr_);
+  SafeCStringAppendFormat(&headers,
+                          _T("%s: %d\r\n"),
+                          kHeaderXLastHTTPStatusCode, last_http_status_code_);
+
+  SafeCStringAppendFormat(&headers, _T("%s: %d\r\n"),
+                                    kHeaderXRetryCount, cur_retry_count_);
+
+  return headers;
 }
 
 void NetworkRequestImpl::AddHeader(const TCHAR* name, const TCHAR* value) {
@@ -466,7 +553,7 @@
     return;
   }
   // Documentation specifies each header must be terminated by \r\n.
-  additional_headers_.AppendFormat(_T("%s: %s\r\n"), name, value);
+  SafeCStringAppendFormat(&additional_headers_, _T("%s: %s\r\n"), name, value);
 }
 
 HRESULT NetworkRequestImpl::QueryHeadersString(uint32 info_level,
@@ -480,99 +567,93 @@
   return cur_http_request_->QueryHeadersString(info_level, name, value);
 }
 
-void NetworkRequestImpl::DetectNetworkConfiguration(
-    std::vector<Config>* network_configurations) const {
-  ASSERT1(network_configurations);
+void NetworkRequestImpl::DetectProxyConfiguration(
+    std::vector<ProxyConfig>* proxy_configurations) const {
+  ASSERT1(proxy_configurations);
 
-  network_configurations->clear();
+  proxy_configurations->clear();
 
   // Use this object's configuration override if one is set.
-  if (network_configuration_.get()) {
-    network_configurations->push_back(*network_configuration_);
+  if (proxy_configuration_.get()) {
+    proxy_configurations->push_back(*proxy_configuration_);
     return;
   }
 
   // Use the global configuration override if the network config has one.
-  NetworkConfig& network_config(NetworkConfig::Instance());
-  Config config;
-  if (SUCCEEDED(network_config.GetConfigurationOverride(&config))) {
-    network_configurations->push_back(config);
-    return;
-  }
-
-  // Detect the configurations if no configuration override is specified.
-  HRESULT hr = network_config.Detect();
+  NetworkConfig* network_config = NULL;
+  NetworkConfigManager& network_manager = NetworkConfigManager::Instance();
+  HRESULT hr = network_manager.GetUserNetworkConfig(&network_config);
   if (SUCCEEDED(hr)) {
-    network_config.GetConfigurations().swap(*network_configurations);
+    ProxyConfig config;
+    if (SUCCEEDED(network_config->GetConfigurationOverride(&config))) {
+      proxy_configurations->push_back(config);
+      return;
+    }
+
+    // Detect the configurations if no configuration override is specified.
+    hr = network_config->Detect();
+    if (SUCCEEDED(hr)) {
+      network_config->GetConfigurations().swap(*proxy_configurations);
+    } else {
+      NET_LOG(LW, (_T("[failed to detect net config][0x%08x]"), hr));
+    }
+
+    network_config->AppendLastKnownGoodProxyConfig(proxy_configurations);
   } else {
-    NET_LOG(LW, (_T("[failed to detect net config][0x%08x]"), hr));
+    NET_LOG(LW, (_T("[failed to get network config instance][0x%08x]"), hr));
   }
 
-  config = Config();
-  config.source = _T("auto");
-
-  // Always try WPAD. Might be important to try this first in the case of
-  // corporate network, where direct connection might not be available at all.
-  config.auto_detect = true;
-  network_configurations->push_back(config);
-
-  // Default to direct connection as a last resort.
-  network_configurations->push_back(Config());
+  NetworkConfig::AppendStaticProxyConfigs(proxy_configurations);
+  NetworkConfig::SortProxies(proxy_configurations);
 
   // Some of the configurations might occur multiple times. To avoid retrying
   // the same configuration over, try to remove duplicates while preserving
   // the order of existing configurations.
-  NetworkConfig::RemoveDuplicates(network_configurations);
-  ASSERT1(!network_configurations->empty());
+  NetworkConfig::RemoveDuplicates(proxy_configurations);
+  ASSERT1(!proxy_configurations->empty());
 }
 
 HRESULT PostRequest(NetworkRequest* network_request,
                     bool fallback_to_https,
                     const CString& url,
                     const CString& request_string,
-                    CString* response) {
+                    std::vector<uint8>* response) {
   ASSERT1(network_request);
   ASSERT1(response);
 
   const CStringA utf8_request_string(WideToUtf8(request_string));
-  std::vector<uint8> response_buffer;
   HRESULT hr = network_request->PostUtf8String(url,
                                                utf8_request_string,
-                                               &response_buffer);
-  bool is_canceled(hr == OMAHA_NET_E_REQUEST_CANCELLED);
+                                               response);
+  bool is_canceled(hr == GOOPDATE_E_CANCELLED);
   if (FAILED(hr) && !is_canceled && fallback_to_https) {
     // Replace http with https and resend the request.
-    if (String_StartsWith(url, kHttpProtoScheme, true)) {
-      CString https_url = url.Mid(_tcslen(kHttpProtoScheme));
-      https_url.Insert(0, kHttpsProtoScheme);
+    if (String_StartsWith(url, kHttpProto, true)) {
+      CString https_url = url.Mid(_tcslen(kHttpProto));
+      https_url.Insert(0, kHttpsProto);
 
       NET_LOG(L3, (_T("[network request fallback to %s]"), https_url));
-      response_buffer.clear();
+      response->clear();
       if (SUCCEEDED(network_request->PostUtf8String(https_url,
                                                     utf8_request_string,
-                                                    &response_buffer))) {
+                                                    response))) {
         hr = S_OK;
       }
     }
   }
-  if (!response_buffer.empty()) {
-    *response = Utf8BufferToWideChar(response_buffer);
-  }
   return hr;
 }
 
+// TODO(omaha): Eliminate this function if no longer a need for it.  It's only
+// used in NetDiags, and its value has been eliminated since we switched to all
+// network functions returning uint8 buffers.
 HRESULT GetRequest(NetworkRequest* network_request,
                    const CString& url,
-                   CString* response) {
+                   std::vector<uint8>* response) {
   ASSERT1(network_request);
   ASSERT1(response);
 
-  std::vector<uint8> response_buffer;
-  HRESULT hr = network_request->Get(url, &response_buffer);
-  if (!response_buffer.empty()) {
-    *response = Utf8BufferToWideChar(response_buffer);
-  }
-  return hr;
+  return network_request->Get(url, response);
 }
 
 }   // namespace detail
diff --git a/net/network_request_impl.h b/net/network_request_impl.h
index a24c20e..b12845f 100644
--- a/net/network_request_impl.h
+++ b/net/network_request_impl.h
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -30,8 +30,9 @@
 #include <atlstr.h>
 #include <vector>
 #include "base/basictypes.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/synchronized.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/synchronized.h"
 #include "omaha/net/network_config.h"
 #include "omaha/net/network_request.h"
 #include "omaha/net/http_request.h"
@@ -57,6 +58,7 @@
   HRESULT DownloadFile(const CString& url, const CString& filename);
 
   HRESULT Pause();
+  HRESULT Resume();
   HRESULT Cancel();
 
   void AddHeader(const TCHAR* name, const TCHAR* value);
@@ -69,6 +71,10 @@
 
   CString response_headers() const { return response_headers_; }
 
+  void set_proxy_auth_config(const ProxyAuthConfig& proxy_auth_config) {
+    proxy_auth_config_ = proxy_auth_config;
+  }
+
   void set_num_retries(int num_retries) { num_retries_ = num_retries; }
 
   void set_time_between_retries(int time_between_retries_ms) {
@@ -81,21 +87,25 @@
 
   void set_low_priority(bool low_priority) { low_priority_ = low_priority; }
 
-  void set_network_configuration(const Config* network_configuration) {
-    if (network_configuration) {
-      network_configuration_.reset(new Config);
-      *network_configuration_ = *network_configuration;
+  void set_proxy_configuration(const ProxyConfig* proxy_configuration) {
+    if (proxy_configuration) {
+      proxy_configuration_.reset(new ProxyConfig);
+      *proxy_configuration_ = *proxy_configuration;
     } else {
-      network_configuration_.reset();
+      proxy_configuration_.reset();
     }
   }
 
+  void set_preserve_protocol(bool preserve_protocol) {
+    preserve_protocol_ = preserve_protocol;
+  }
+
   CString trace() const { return trace_; }
 
-  // Detects the available network configurations and returns the chain of
+  // Detects the available proxy configurations and returns the chain of
   // configurations to be used.
-  void DetectNetworkConfiguration(
-      std::vector<Config>* network_configurations) const;
+  void DetectProxyConfiguration(
+      std::vector<ProxyConfig>* proxy_configurations) const;
 
  private:
   // Resets the state of the output data members.
@@ -126,15 +136,18 @@
                             CString* response_headers,
                             std::vector<uint8>* response) const;
 
+  // Builds headers for the current HttpRequest and network configuration.
+  CString BuildPerRequestHeaders() const;
+
   // Specifies the chain of HttpRequestInterface to handle the request.
   std::vector<HttpRequestInterface*> http_request_chain_;
 
-  // Specifies the detected network configurations.
-  std::vector<Config> network_configurations_;
+  // Specifies the detected proxy configurations.
+  std::vector<ProxyConfig> proxy_configurations_;
 
-  // Specifies the network configuration override. When set, the network
+  // Specifies the proxy configuration override. When set, the proxy
   // configurations are not auto detected.
-  scoped_ptr<Config> network_configuration_;
+  scoped_ptr<ProxyConfig> proxy_configuration_;
 
   // Input data members.
   // The request and response buffers are owner by the caller.
@@ -142,7 +155,9 @@
   const void* request_buffer_;     // Contains the request body for POST.
   size_t   request_buffer_length_;  // Length of the request body.
   CString  filename_;              // Contains the response for downloads.
-  CString  additional_headers_;    // Each header is separated by \r\n.
+  CString  additional_headers_;    // Headers common to all requests.
+                                   // Each header is separated by \r\n.
+  ProxyAuthConfig proxy_auth_config_;
   int      num_retries_;
   bool     low_priority_;
   int time_between_retries_ms_;
@@ -151,20 +166,29 @@
   int      http_status_code_;
   CString  response_headers_;      // Each header is separated by \r\n.
   std::vector<uint8>* response_;   // Contains the response for Post and Get.
-  HRESULT  last_network_error_;    // TODO(omaha): not implemented.
 
   const NetworkConfig::Session  network_session_;
   NetworkRequestCallback*       callback_;
 
   // The http request and the network configuration currently in use.
   mutable HttpRequestInterface* cur_http_request_;
-  mutable const Config*         cur_network_config_;
+  mutable const ProxyConfig*    cur_proxy_config_;
+
+  // The HRESULT and HTTP status code updated by the prior
+  // DoSendHttpRequest() call.
+  mutable HRESULT  last_hr_;
+  mutable int      last_http_status_code_;
+
+  // The current retry count defined by the outermost DoSendWithRetries() call.
+  int cur_retry_count_;
 
   volatile LONG is_canceled_;
   scoped_event event_cancel_;
 
   LLock lock_;
 
+  bool preserve_protocol_;
+
   // Contains the trace of the request as handled by the fallback chain.
   mutable CString trace_;
 
@@ -178,11 +202,11 @@
                     bool fallback_to_https,
                     const CString& url,
                     const CString& request_string,
-                    CString* response);
+                    std::vector<uint8>* response);
 
 HRESULT GetRequest(NetworkRequest* network_request,
                    const CString& url,
-                   CString* response);
+                   std::vector<uint8>* response);
 
 }   // namespace detail
 
diff --git a/net/network_request_unittest.cc b/net/network_request_unittest.cc
index dac357b..e27bbb3 100644
--- a/net/network_request_unittest.cc
+++ b/net/network_request_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -18,19 +18,19 @@
 #include <vector>
 #include "base/scoped_ptr.h"
 #include "base/basictypes.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/browser_utils.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/queue_timer.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vista_utils.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/browser_utils.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/queue_timer.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/time.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vista_utils.h"
 #include "omaha/net/bits_request.h"
-#include "omaha/net/browser_request.h"
 #include "omaha/net/cup_request.h"
 #include "omaha/net/network_config.h"
 #include "omaha/net/network_request.h"
@@ -49,16 +49,19 @@
   static void SetUpTestCase() {
     // Initialize the detection chain: GoogleProxy, FireFox if it is the
     // default browser, and IE.
-    NetworkConfig& network_config(NetworkConfig::Instance());
-    network_config.Clear();
-    network_config.Add(new GoogleProxyDetector(MACHINE_REG_UPDATE_DEV));
+    NetworkConfig* network_config = NULL;
+    EXPECT_HRESULT_SUCCEEDED(
+      NetworkConfigManager::Instance().GetUserNetworkConfig(&network_config));
+
+    network_config->Clear();
+    network_config->Add(new UpdateDevProxyDetector);
     BrowserType browser_type(BROWSER_UNKNOWN);
     GetDefaultBrowserType(&browser_type);
     if (browser_type == BROWSER_FIREFOX) {
-      network_config.Add(new FirefoxProxyDetector());
+      network_config->Add(new FirefoxProxyDetector);
     }
-    network_config.Add(new IEProxyDetector());
-    network_config.Add(new DefaultProxyDetector);
+    network_config->Add(new IEProxyDetector);
+    network_config->Add(new DefaultProxyDetector);
 
     vista::GetLoggedOnUserToken(&token_);
   }
@@ -67,12 +70,20 @@
     if (token_) {
       ::CloseHandle(token_);
     }
-    NetworkConfig::Instance().Clear();
+
+    NetworkConfig* network_config = NULL;
+    EXPECT_HRESULT_SUCCEEDED(
+      NetworkConfigManager::Instance().GetUserNetworkConfig(&network_config));
+
+    network_config->Clear();
   }
 
   virtual void SetUp() {
-    const NetworkConfig::Session& session(NetworkConfig::Instance().session());
-    network_request_.reset(new NetworkRequest(session));
+    NetworkConfig* network_config = NULL;
+    EXPECT_HRESULT_SUCCEEDED(
+      NetworkConfigManager::Instance().GetUserNetworkConfig(&network_config));
+
+    network_request_.reset(new NetworkRequest(network_config->session()));
   }
 
   virtual void TearDown() {}
@@ -83,6 +94,20 @@
     NET_LOG(L3, (_T("[downloading %d of %d]"), bytes, bytes_total));
   }
 
+  virtual void OnRequestBegin() {
+    NET_LOG(L3, (_T("[download starts]")));
+  }
+
+  virtual void OnRequestRetryScheduled(time64 next_retry_time) {
+    UNREFERENCED_PARAMETER(next_retry_time);
+
+    time64 now = GetCurrent100NSTime();
+    ASSERT1(next_retry_time > now);
+
+    NET_LOG(L3, (_T("\n[Download will retry in %d seconds]\n"),
+                 CeilingDivide(next_retry_time - now, kSecsTo100ns)));
+  }
+
   static void CancelCallback(QueueTimer* queue_timer) {
     ASSERT_TRUE(queue_timer);
     ASSERT_TRUE(queue_timer->ctx());
@@ -99,7 +124,10 @@
     CString url = _T("http://www.google.com/robots.txt");
     network_request_->set_num_retries(2);
     EXPECT_HRESULT_SUCCEEDED(network_request_->Get(url, &response));
-    EXPECT_EQ(network_request_->http_status_code(), HTTP_STATUS_OK);
+
+    int http_status = network_request_->http_status_code();
+    EXPECT_TRUE(http_status == HTTP_STATUS_OK ||
+                http_status == HTTP_STATUS_PARTIAL_CONTENT);
   }
 
   // https get.
@@ -108,7 +136,10 @@
     CString url = _T("https://www.google.com/robots.txt");
     network_request_->set_num_retries(2);
     EXPECT_HRESULT_SUCCEEDED(network_request_->Get(url, &response));
-    EXPECT_EQ(network_request_->http_status_code(), HTTP_STATUS_OK);
+
+    int http_status = network_request_->http_status_code();
+    EXPECT_TRUE(http_status == HTTP_STATUS_OK ||
+                http_status == HTTP_STATUS_PARTIAL_CONTENT);
   }
 
   // http post.
@@ -123,21 +154,28 @@
                                                     request,
                                                     arraysize(request) - 1,
                                                     &response));
-    EXPECT_EQ(network_request_->http_status_code(), HTTP_STATUS_OK);
+
+    int http_status = network_request_->http_status_code();
+    EXPECT_TRUE(http_status == HTTP_STATUS_OK ||
+                http_status == HTTP_STATUS_PARTIAL_CONTENT);
 
     // Post an UTF8 string.
     CStringA utf8_request(reinterpret_cast<const char*>(request));
     EXPECT_HRESULT_SUCCEEDED(network_request_->PostUtf8String(url,
                                                               utf8_request,
                                                               &response));
-    EXPECT_EQ(network_request_->http_status_code(), HTTP_STATUS_OK);
+    http_status = network_request_->http_status_code();
+    EXPECT_TRUE(http_status == HTTP_STATUS_OK ||
+                http_status == HTTP_STATUS_PARTIAL_CONTENT);
 
     // Post a Unicode string.
     CString unicode_request(reinterpret_cast<const char*>(request));
     EXPECT_HRESULT_SUCCEEDED(network_request_->PostString(url,
                                                           unicode_request,
                                                           &response));
-    EXPECT_EQ(network_request_->http_status_code(), HTTP_STATUS_OK);
+    http_status = network_request_->http_status_code();
+    EXPECT_TRUE(http_status == HTTP_STATUS_OK ||
+                http_status == HTTP_STATUS_PARTIAL_CONTENT);
   }
 
   // Download http file.
@@ -153,7 +191,10 @@
     network_request_->set_callback(this);
     EXPECT_HRESULT_SUCCEEDED(network_request_->DownloadFile(url, temp_file));
     EXPECT_TRUE(::DeleteFile(temp_file));
-    EXPECT_EQ(network_request_->http_status_code(), HTTP_STATUS_OK);
+
+    int http_status = network_request_->http_status_code();
+    EXPECT_TRUE(http_status == HTTP_STATUS_OK ||
+                http_status == HTTP_STATUS_PARTIAL_CONTENT);
   }
 
   void MultipleRequestsHelper() {
@@ -166,12 +207,15 @@
                                                       request,
                                                       arraysize(request) - 1,
                                                       &response));
-      EXPECT_EQ(network_request_->http_status_code(), HTTP_STATUS_OK);
+
+      int http_status = network_request_->http_status_code();
+      EXPECT_TRUE(http_status == HTTP_STATUS_OK ||
+                  http_status == HTTP_STATUS_PARTIAL_CONTENT);
     }
   }
 
   void PostRequestHelper() {
-    CString response;
+    std::vector<uint8> response;
     CString url = _T("http://tools.google.com/service/update2");
     CString request = _T("<o:gupdate xmlns:o=\"http://www.google.com/update2/request\" testsource=\"dev\"/>");  // NOLINT
     EXPECT_HRESULT_SUCCEEDED(PostRequest(network_request_.get(),
@@ -182,7 +226,7 @@
   }
 
   void PostRequestNegativeTestHelper() {
-    CString response;
+    std::vector<uint8> response;
     CString url = _T("http://no_such_host.google.com/service/update2");
     CString request = _T("<o:gupdate xmlns:o=\"http://www.google.com/update2/request\" testsource=\"dev\"/>");  // NOLINT
     EXPECT_HRESULT_FAILED(PostRequest(network_request_.get(),
@@ -197,8 +241,8 @@
     // the retries are used up. Urlmon request is using IE's settings.
     // Therefore, it is possible a proxy is used. In this case, the http
     // response is '503 Service Unavailable'.
-    Config config;
-    network_request_->set_network_configuration(&config);
+    ProxyConfig config;
+    network_request_->set_proxy_configuration(&config);
     network_request_->set_num_retries(2);
     network_request_->set_time_between_retries(10);   // 10 miliseconds.
     std::vector<uint8> response;
@@ -224,18 +268,18 @@
 
     // Try a direct connection to a non-existent host and keep retrying until
     // canceled by the timer.
-    Config config;
-    network_request_->set_network_configuration(&config);
+    ProxyConfig config;
+    network_request_->set_proxy_configuration(&config);
     network_request_->set_num_retries(10);
     network_request_->set_time_between_retries(10);  // 10 miliseconds.
     std::vector<uint8> response;
 
     CString url = _T("http://nohost/nofile");
 
-    EXPECT_EQ(OMAHA_NET_E_REQUEST_CANCELLED,
+    EXPECT_EQ(GOOPDATE_E_CANCELLED,
               network_request_->Get(url, &response));
 
-    EXPECT_EQ(OMAHA_NET_E_REQUEST_CANCELLED,
+    EXPECT_EQ(GOOPDATE_E_CANCELLED,
               network_request_->Get(url, &response));
   }
 
@@ -248,28 +292,24 @@
 // http get.
 TEST_F(NetworkRequestTest, HttpGet) {
   network_request_->AddHttpRequest(new SimpleRequest);
-  network_request_->AddHttpRequest(new BrowserRequest);
   HttpGetHelper();
 }
 
 // http get.
 TEST_F(NetworkRequestTest, HttpGetUrlmon) {
   network_request_->AddHttpRequest(new UrlmonRequest);
-  network_request_->AddHttpRequest(new BrowserRequest);
   HttpGetHelper();
 }
 
 // https get.
 TEST_F(NetworkRequestTest, HttpsGet) {
   network_request_->AddHttpRequest(new SimpleRequest);
-  network_request_->AddHttpRequest(new BrowserRequest);
   HttpsGetHelper();
 }
 
 // https get.
 TEST_F(NetworkRequestTest, HttpsGetUrlmon) {
   network_request_->AddHttpRequest(new UrlmonRequest);
-  network_request_->AddHttpRequest(new BrowserRequest);
   HttpsGetHelper();
 }
 
@@ -277,7 +317,6 @@
 TEST_F(NetworkRequestTest, HttpPost) {
   network_request_->AddHttpRequest(new CupRequest(new SimpleRequest));
   network_request_->AddHttpRequest(new SimpleRequest);
-  network_request_->AddHttpRequest(new CupRequest(new BrowserRequest));
   HttpPostHelper();
 }
 
@@ -285,7 +324,6 @@
 TEST_F(NetworkRequestTest, HttpPostUrlmon) {
   network_request_->AddHttpRequest(new CupRequest(new UrlmonRequest));
   network_request_->AddHttpRequest(new UrlmonRequest);
-  network_request_->AddHttpRequest(new CupRequest(new BrowserRequest));
   HttpPostHelper();
 }
 
@@ -301,7 +339,6 @@
 
   network_request_->AddHttpRequest(bits_request);
   network_request_->AddHttpRequest(new SimpleRequest);
-  network_request_->AddHttpRequest(new BrowserRequest);
   DownloadHelper();
 }
 
@@ -314,7 +351,6 @@
 
   network_request_->AddHttpRequest(bits_request);
   network_request_->AddHttpRequest(new UrlmonRequest);
-  network_request_->AddHttpRequest(new BrowserRequest);
   DownloadHelper();
 }
 
@@ -323,7 +359,7 @@
   MultipleRequestsHelper();
 }
 
-TEST_F(NetworkRequestTest, DISABLED_MultipleRequestsUrlmon) {
+TEST_F(NetworkRequestTest, MultipleRequestsUrlmon) {
   network_request_->AddHttpRequest(new CupRequest(new UrlmonRequest));
   MultipleRequestsHelper();
 }
@@ -364,7 +400,7 @@
   std::vector<uint8> response;
 
   CString url = _T("https://www.google.com/robots.txt");
-  EXPECT_EQ(OMAHA_NET_E_REQUEST_CANCELLED,
+  EXPECT_EQ(GOOPDATE_E_CANCELLED,
             network_request_->Get(url, &response));
 }
 
@@ -380,8 +416,8 @@
 
   // Try a direct connection to a non-existent host and keep retrying until
   // canceled by the timer.
-  Config config;
-  network_request_->set_network_configuration(&config);
+  ProxyConfig config;
+  network_request_->set_proxy_configuration(&config);
 
   BitsRequest* bits_request(new BitsRequest);
   bits_request->set_minimum_retry_delay(60);
@@ -393,10 +429,10 @@
 
   CString url = _T("http://nohost/nofile");
 
-  EXPECT_EQ(OMAHA_NET_E_REQUEST_CANCELLED,
+  EXPECT_EQ(GOOPDATE_E_CANCELLED,
             network_request_->DownloadFile(url, _T("c:\\foo")));
 
-  EXPECT_EQ(OMAHA_NET_E_REQUEST_CANCELLED,
+  EXPECT_EQ(GOOPDATE_E_CANCELLED,
             network_request_->DownloadFile(url, _T("c:\\foo")));
 }
 
@@ -405,10 +441,5 @@
   CancelTest_GetHelper();
 }
 
-TEST_F(NetworkRequestTest, CancelTest_GetUrlmon) {
-  network_request_->AddHttpRequest(new UrlmonRequest);
-  CancelTest_GetHelper();
-}
-
 }  // namespace omaha
 
diff --git a/net/proxy_auth.cc b/net/proxy_auth.cc
index 3a9372a..96f40a5 100644
--- a/net/proxy_auth.cc
+++ b/net/proxy_auth.cc
@@ -17,11 +17,13 @@
 #include <pstore.h>
 #include <wincred.h>
 #include <wincrypt.h>
-#include "omaha/common/encrypt.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/smart_handle.h"
-#include "omaha/common/string.h"
-#include "omaha/common/system_info.h"
+#include "omaha/base/commontypes.h"
+#include "omaha/base/encrypt.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/smart_handle.h"
+#include "omaha/base/string.h"
+#include "omaha/base/system_info.h"
+#include "omaha/goopdate/cred_dialog.h"
 #include "omaha/third_party/smartany/scoped_any.h"
 
 using omaha::encrypt::EncryptData;
@@ -77,93 +79,50 @@
   return proxy_settings;
 }
 
-void ProxyAuth::ConfigureProxyAuth(const CString& caption,
-                                   const CString& message,
-                                   HWND parent,
+void ProxyAuth::ConfigureProxyAuth(bool is_machine,
                                    uint32 cancel_prompt_threshold) {
-  ASSERT1(!caption.IsEmpty());
-  ASSERT1(!message.IsEmpty());
-  ASSERT1(parent);
   ASSERT1(cancel_prompt_threshold);
 
-  proxy_prompt_caption_ = caption;
-  proxy_prompt_message_ = message;
-  parent_hwnd_ = parent;
+  proxy_prompt_is_machine_ = is_machine;
   cancel_prompt_threshold_ = cancel_prompt_threshold;
 }
 
-bool ProxyAuth::PromptUser(const CString& server) {
-  scoped_library credui_lib(::LoadLibrary(L"credui.dll"));
-  ASSERT1(credui_lib);
-  if (!credui_lib)
-    return false;
+bool ProxyAuth::PromptUser(const CString& server,
+                           const ProxyAuthConfig& proxy_auth_config) {
+  NET_LOG(L3, (_T("[ProxyAuth::PromptUser][%s][%s]"),
+               server, proxy_auth_config.ToString()));
 
-  typedef BOOL (__stdcall *CredUIPromptForCredentialsW_type)(
-      PCREDUI_INFO pUiInfo,
-      PCTSTR pszTargetName,
-      PCtxtHandle Reserved,
-      DWORD dwAuthError,
-      PCTSTR pszUserName,
-      ULONG ulUserNameMaxChars,
-      PCTSTR pszPassword,
-      ULONG ulPasswordMaxChars,
-      PBOOL pfSave,
-      DWORD dwFlags);
-  CredUIPromptForCredentialsW_type CredUIPromptForCredentialsW_fn =
-      reinterpret_cast<CredUIPromptForCredentialsW_type>(
-          GetProcAddress(get(credui_lib), "CredUIPromptForCredentialsW"));
-  ASSERT1(CredUIPromptForCredentialsW_fn || SystemInfo::IsRunningOnW2K());
-  if (!CredUIPromptForCredentialsW_fn)
-    return false;
+  CString user;
+  CString pass;
 
-  wchar_t username[CREDUI_MAX_USERNAME_LENGTH + 1];
-  wchar_t password[CREDUI_MAX_PASSWORD_LENGTH + 1];
-  BOOL check;
-  CREDUI_INFO info = {0};
-  info.cbSize = sizeof(info);
+  HRESULT hr = LaunchCredentialDialog(
+      proxy_prompt_is_machine_,
+      proxy_auth_config.parent_hwnd,
+      server,
+      proxy_auth_config.prompt_caption,
+      &user,
+      &pass);
 
-  ASSERT1(parent_hwnd_);
-  info.hwndParent = parent_hwnd_;
-
-  ASSERT1(!proxy_prompt_message_.IsEmpty());
-  ASSERT1(!proxy_prompt_caption_.IsEmpty());
-  CString message;
-  message.FormatMessage(proxy_prompt_message_, server);
-  info.pszMessageText = message.GetString();
-  info.pszCaptionText = proxy_prompt_caption_;
-
-  DWORD result;
-  do {
-    username[0] = L'\0';
-    password[0] = L'\0';
-    result = CredUIPromptForCredentialsW_fn(
-        &info,
-        server,
-        NULL,
-        0,
-        username,
-        CREDUI_MAX_USERNAME_LENGTH,
-        password,
-        CREDUI_MAX_PASSWORD_LENGTH,
-        &check,
-        CREDUI_FLAGS_ALWAYS_SHOW_UI | CREDUI_FLAGS_GENERIC_CREDENTIALS |
-        CREDUI_FLAGS_DO_NOT_PERSIST);
-    NET_LOG(L3, (_T("[CredUIPromptForCredentialsW returned %d]"), result));
-  } while (result == NO_ERROR && (!username[0] || !password[0]));
-
-  if (result == NO_ERROR) {
-    AddCred(server, username, password);
-  } else if (result == ERROR_CANCELLED) {
+  if (SUCCEEDED(hr)) {
+    AddCred(server, user, pass);
+  } else if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) {
     PromptCancelled();
   }
 
-  return result == NO_ERROR;
+  SecureZeroMemory(user.GetBuffer(), user.GetAllocLength() * sizeof(TCHAR));
+  SecureZeroMemory(pass.GetBuffer(), pass.GetAllocLength() * sizeof(TCHAR));
+
+  return SUCCEEDED(hr);
 }
 
 bool ProxyAuth::GetProxyCredentials(bool allow_ui, bool force_ui,
                                     const CString& proxy_server,
+                                    const ProxyAuthConfig& proxy_auth_config,
                                     CString* username, CString* password,
                                     uint32* auth_scheme) {
+  NET_LOG(L3, (_T("[ProxyAuth::GetProxyCredentials][%d][%s]"),
+               allow_ui, proxy_auth_config.ToString()));
+
   CString server(proxy_server);
   if (server.IsEmpty()) {
     server = kDefaultProxyServer;
@@ -176,7 +135,8 @@
 
   if (i == -1) {
     if (ReadFromIE7(server) || ReadFromPreIE7(server) ||
-        (allow_ui && parent_hwnd_ && IsPromptAllowed() && PromptUser(server))) {
+        (allow_ui && proxy_auth_config.parent_hwnd && IsPromptAllowed() &&
+         PromptUser(server, proxy_auth_config))) {
       i = servers_.GetSize() - 1;
     }
   }
diff --git a/net/proxy_auth.h b/net/proxy_auth.h
index 43cdf40..4f077d7 100644
--- a/net/proxy_auth.h
+++ b/net/proxy_auth.h
@@ -22,7 +22,8 @@
 #include <atlsimpcoll.h>
 #include <atlstr.h>
 #include <vector>
-#include "omaha/common/synchronized.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/common/goopdate_utils.h"
 
 #define UNKNOWN_AUTH_SCHEME  0x0000FFFF
 const TCHAR* const kDefaultProxyServer = _T("<Default Proxy>");
@@ -30,17 +31,32 @@
 
 namespace omaha {
 
+struct ProxyAuthConfig {
+  ProxyAuthConfig(HWND hwnd, const CString& caption)
+      : parent_hwnd(hwnd), prompt_caption(caption) {}
+
+  CString ToString() const {
+    CString result;
+    result.Format(_T("[ProxyAuthConfig][0x%x][%s]"),
+                  parent_hwnd, prompt_caption);
+    return result;
+  }
+
+  HWND parent_hwnd;
+  CString prompt_caption;
+};
+
 // A class that reads and stores the Internet Explorer saved proxy
 // authentication info.  Works with versions of IE up to and including 7.
 class ProxyAuth {
  public:
   ProxyAuth() : prompt_cancelled_(0),
-                cancel_prompt_threshold_(kDefaultCancelPromptThreshold),
-                parent_hwnd_(NULL) {}
+                proxy_prompt_is_machine_(
+                    goopdate_utils::IsRunningFromOfficialGoopdateDir(true)),
+                cancel_prompt_threshold_(kDefaultCancelPromptThreshold) {}
   ~ProxyAuth() {}
 
-  void ConfigureProxyAuth(const CString& caption, const CString& message,
-                          HWND parent, uint32 cancel_prompt_threshold);
+  void ConfigureProxyAuth(bool is_machine, uint32 cancel_prompt_threshold);
 
   // Retrieves the saved proxy credentials for Internet Explorer currently.
   // In the future, there may be other sources of credentials.
@@ -51,8 +67,8 @@
   // @param password The stored password for this proxy server
   // @returns true if credentials were found, otherwise false
   bool GetProxyCredentials(bool allow_ui, bool force_ui, const CString& server,
-                           CString* username, CString* password,
-                           uint32* auth_scheme);
+                           const ProxyAuthConfig& config, CString* username,
+                           CString* password, uint32* auth_scheme);
 
   static CString ExtractProxy(const CString& proxy_settings, bool isHttps);
 
@@ -83,15 +99,11 @@
   // after this many authentication prompt cancellations, stop prompting.
   uint32 cancel_prompt_threshold_;
 
-  CString proxy_prompt_caption_;
-  CString proxy_prompt_message_;
-
-  // Parent window for the credentials prompt.
-  HWND parent_hwnd_;
+  bool proxy_prompt_is_machine_;
 
   bool ReadFromIE7(const CString& server);
   bool ReadFromPreIE7(const CString& server);
-  bool PromptUser(const CString& server);
+  bool PromptUser(const CString& server, const ProxyAuthConfig& config);
 
   DISALLOW_EVIL_CONSTRUCTORS(ProxyAuth);
 };
diff --git a/net/simple_request.cc b/net/simple_request.cc
index 46a97d5..9ae69b6 100644
--- a/net/simple_request.cc
+++ b/net/simple_request.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -18,61 +18,174 @@
 // The transient state of the request is maintained by request_state_.
 // The state is created by SimpleRequest::Send and it is destroyed by
 // SimpleRequest::Close. The only concurrent access to the object state can
-// happened during calling SimpleRequest::Cancel. Cancel closes the
-// connection and the request handles. This makes any of the WinHttp calls
-// on these handles fail and SimpleRequest::Send return to the caller.
+// happen during calling SimpleRequest::Cancel(), Pause() and Resume(). Cancel
+// closes the connection and the request handles. This makes any of the WinHttp
+// calls on these handles fail and SimpleRequest::Send return to the caller.
+// Pause() also closes the handles but SimpleRequest automatically reopens
+// them when Resume() is called. During resume stage, SimpleRequest sends a
+// range request to continue download. During these actions, the caller is still
+// blocked.
 
 #include "omaha/net/simple_request.h"
-
 #include <atlconv.h>
 #include <climits>
+#include <memory>
 #include <vector>
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/string.h"
-#include "omaha/net/http_client.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/string.h"
 #include "omaha/net/network_config.h"
 #include "omaha/net/network_request.h"
 #include "omaha/net/proxy_auth.h"
+#include "omaha/net/winhttp_adapter.h"
 
 namespace omaha {
 
 SimpleRequest::SimpleRequest()
     : request_buffer_(NULL),
       request_buffer_length_(0),
+      proxy_auth_config_(NULL, CString()),
       is_canceled_(false),
+      is_closed_(false),
       session_handle_(NULL),
       low_priority_(false),
-      callback_(NULL) {
+      callback_(NULL),
+      download_completed_(false),
+      pause_happened_(false) {
   user_agent_.Format(_T("%s;winhttp"), NetworkConfig::GetUserAgent());
-  http_client_.reset(CreateHttpClient());
+
+  // Create a manual reset event to wait on during network transfer.
+  // The event is signaled by default meaning the network transferring is
+  // enabled. Pause() resets this event to stop network activity. Resume()
+  // does the opposite as Pause(). Close() and Cancel() also set the event.
+  // This unlocks the thread if it is in paused state and returns control
+  // to the caller.
+  reset(event_resume_, ::CreateEvent(NULL, true, true, NULL));
+  ASSERT1(valid(event_resume_));
 }
 
+// TODO(omaha): we should attempt to cleanup the file only if we
+// created it in the first place.
 SimpleRequest::~SimpleRequest() {
   Close();
   callback_ = NULL;
+
+  // If download failed, try to clean up the target file.
+  if (!download_completed_ && !filename_.IsEmpty()) {
+    if (!::DeleteFile(filename_) && ::GetLastError() != ERROR_FILE_NOT_FOUND) {
+      NET_LOG(LW, (_T("[SimpleRequest][Failed to delete file: %s][0x%08x]."),
+                   filename_.GetString(), HRESULTFromLastError()));
+    }
+  }
+}
+
+void SimpleRequest::set_url(const CString& url) {
+  __mutexScope(lock_);
+  if (url_ != url) {
+    url_ = url;
+    CloseHandles();
+    request_state_.reset();
+  }
+}
+
+void SimpleRequest::set_filename(const CString& filename) {
+  __mutexScope(lock_);
+  if (filename_ != filename) {
+    filename_ = filename;
+    CloseHandles();
+    request_state_.reset();
+  }
 }
 
 HRESULT SimpleRequest::Close() {
   NET_LOG(L3, (_T("[SimpleRequest::Close]")));
-  __mutexBlock(lock_) {
-    CloseHandles(request_state_.get());
-    request_state_.reset();
-  }
-  return S_OK;
+
+  __mutexScope(lock_);
+  is_closed_ = true;
+  CloseHandles();
+  request_state_.reset();
+  winhttp_adapter_.reset();
+
+  // Resume the downloading thread if it is blocked. It is still fine if the
+  // event is set since the operation is like no-op in that case.
+  return Resume();
 }
 
 HRESULT SimpleRequest::Cancel() {
   NET_LOG(L3, (_T("[SimpleRequest::Cancel]")));
-  __mutexBlock(lock_) {
-    is_canceled_ = true;
-    CloseHandles(request_state_.get());
+
+  __mutexScope(lock_);
+  is_canceled_ = true;
+  CloseHandles();
+
+  // Resume the downloading thread if it is blocked. It is still fine if the
+  // event is set since the operation is like no-op in that case.
+  return Resume();
+}
+
+void SimpleRequest::CloseHandles() {
+  if (winhttp_adapter_.get()) {
+    winhttp_adapter_->CloseHandles();
   }
-  return S_OK;
+}
+
+bool SimpleRequest::IsResumeNeeded() const {
+  __mutexScope(lock_);
+  if (!IsPauseSupported() || is_canceled_ || is_closed_) {
+    return false;
+  }
+
+  return pause_happened_;
+}
+
+bool SimpleRequest::IsPauseSupported() const {
+  __mutexScope(lock_);
+  return valid(event_resume_) && !IsPostRequest() && !filename_.IsEmpty();
+}
+
+HRESULT SimpleRequest::Pause() {
+  NET_LOG(L3, (_T("[SimpleRequest::Pause]")));
+
+  if (!IsPauseSupported()) {
+    return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
+  }
+
+  __mutexScope(ready_to_pause_lock_);
+  __mutexScope(lock_);
+
+  pause_happened_ = true;
+  CloseHandles();
+  return ::ResetEvent(get(event_resume_)) ? S_OK : HRESULTFromLastError();
+}
+
+HRESULT SimpleRequest::Resume() {
+  NET_LOG(L3, (_T("[SimpleRequest::Resume]")));
+
+  __mutexScope(lock_);
+  HRESULT hr = S_OK;
+  if (IsPauseSupported()) {
+    hr = ::SetEvent(get(event_resume_)) ? S_OK : HRESULTFromLastError();
+  }
+  return hr;
+}
+
+void SimpleRequest::WaitForResumeEvent() {
+  if (!event_resume_) {
+    return;
+  }
+
+  // Reset pause_happened_ state to indicate that pause has not yet happened
+  // during new resume stage.
+  __mutexBlock(lock_) {
+    pause_happened_ = false;
+  }
+
+  VERIFY1(::WaitForSingleObject(get(event_resume_), INFINITE) != WAIT_FAILED);
 }
 
 HRESULT SimpleRequest::Send() {
@@ -80,147 +193,251 @@
 
   ASSERT1(!url_.IsEmpty());
   if (!session_handle_) {
-    // Winhttp could not be loaded.
-    NET_LOG(LW, (_T("[SimpleRequest: session_handle_ is NULL.]")));
-    // TODO(omaha): This makes an assumption that only WinHttp is
-    // supported by the network code.
+    NET_LOG(LE, (_T("[SimpleRequest: session_handle_ is NULL]")));
     return OMAHA_NET_E_WINHTTP_NOT_AVAILABLE;
   }
 
-  __mutexBlock(lock_) {
-    CloseHandles(request_state_.get());
-    request_state_.reset(new TransientRequestState);
+  HRESULT hr = S_OK;
+
+  __mutexBlock(ready_to_pause_lock_) {
+    __mutexBlock(lock_) {
+      winhttp_adapter_.reset(new WinHttpAdapter());
+      hr = winhttp_adapter_->Initialize();
+      if (FAILED(hr)) {
+        return hr;
+      }
+
+      if (!IsPauseSupported() || request_state_ == NULL) {
+        request_state_.reset(new TransientRequestState);
+      } else {
+        // Discard all previous download states except content_length and
+        // current_bytes for resume purpose. These two states will be validated
+        // against the previously (partially) downloaded file when reopens the
+        // target file.
+        scoped_ptr<TransientRequestState> request_state(
+            new TransientRequestState);
+        request_state->content_length = request_state_->content_length;
+        request_state->current_bytes = request_state_->current_bytes;
+
+        request_state_.swap(request_state);
+      }
+    }
   }
 
-  HRESULT hr = DoSend();
-  int status_code(GetHttpStatusCode());
-  if (hr == HRESULT_FROM_WIN32(ERROR_WINHTTP_OPERATION_CANCELLED) ||
-      is_canceled_) {
-    hr = OMAHA_NET_E_REQUEST_CANCELLED;
+  for (bool first_time = true, cancelled = false;
+       !cancelled;
+       first_time = false) {
+    scoped_hfile file_handle;
+
+    __mutexBlock(ready_to_pause_lock_) {
+      if (!first_time) {
+        if (IsResumeNeeded()) {
+          NET_LOG(L3, (_T("[SimpleRequest::Send paused.]")));
+          WaitForResumeEvent();
+          NET_LOG(L3, (_T("[SimpleRequest::Send resumed.]")));
+        } else {
+          if (is_canceled_) {
+            hr = GOOPDATE_E_CANCELLED;
+
+            // Once cancelled, we should set downloaded bytes to 0
+            request_state_->current_bytes = 0;
+          }
+
+          // Macro __mutexBlock has a hidden loop built-in and thus one break
+          // is not enough to exit the loop. Uses a flag instead.
+          cancelled = true;
+        }
+      }
+
+      if (!cancelled) {
+        hr = PrepareRequest(address(file_handle));
+      }
+    }
+
+    if (SUCCEEDED(hr) && !cancelled) {
+      ASSERT1(request_state_->current_bytes <= request_state_->content_length);
+
+      // Only requests data if there is more data to download.
+      if (request_state_->content_length == 0 ||
+          request_state_->current_bytes < request_state_->content_length) {
+        hr = RequestData(get(file_handle));
+      }
+    }
   }
-  NET_LOG(L3, (_T("[SimpleRequest::Send][0x%08x][%d]"), hr, status_code));
+
+  NET_LOG(L3,
+          (_T("[SimpleRequest::Send][0x%08x][%d]"), hr, GetHttpStatusCode()));
   return hr;
 }
 
-HRESULT SimpleRequest::DoSend() {
-  // First chance to see if it is canceled.
-  if (is_canceled_) {
-    return OMAHA_NET_E_REQUEST_CANCELLED;
-  }
-
-  HRESULT hr = http_client_->Initialize();
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = http_client_->CrackUrl(url_,
-                              ICU_DECODE,
-                              &request_state_->scheme,
-                              &request_state_->server,
-                              &request_state_->port,
-                              &request_state_->url_path,
-                              NULL);
+HRESULT SimpleRequest::Connect() {
+  HRESULT hr = winhttp_adapter_->CrackUrl(url_,
+                                          ICU_DECODE,
+                                          &request_state_->scheme,
+                                          &request_state_->server,
+                                          &request_state_->port,
+                                          &request_state_->url_path,
+                                          NULL);
   if (FAILED(hr)) {
     return hr;
   }
   ASSERT1(!request_state_->scheme.CompareNoCase(kHttpProtoScheme) ||
           !request_state_->scheme.CompareNoCase(kHttpsProtoScheme));
 
-  hr = http_client_->Connect(session_handle_,
-                             request_state_->server,
-                             request_state_->port,
-                             &request_state_->connection_handle);
+  hr = winhttp_adapter_->Connect(session_handle_,
+                                 request_state_->server,
+                                 request_state_->port);
   if (FAILED(hr)) {
     return hr;
   }
 
   // TODO(omaha): figure out the accept types.
   //              figure out more flags.
+  request_state_->is_https = false;
   DWORD flags = WINHTTP_FLAG_REFRESH;
-  bool is_https = false;
   if (request_state_->scheme == kHttpsProtoScheme) {
-    is_https = true;
+    request_state_->is_https = true;
     flags |= WINHTTP_FLAG_SECURE;
   }
   const TCHAR* verb = IsPostRequest() ? _T("POST") : _T("GET");
-  hr = http_client_->OpenRequest(request_state_->connection_handle,
-                                 verb, request_state_->url_path,
-                                 NULL, WINHTTP_NO_REFERER,
-                                 WINHTTP_DEFAULT_ACCEPT_TYPES, flags,
-                                 &request_state_->request_handle);
+  hr = winhttp_adapter_->OpenRequest(verb, request_state_->url_path,
+                                     NULL, WINHTTP_NO_REFERER,
+                                     WINHTTP_DEFAULT_ACCEPT_TYPES, flags);
   if (FAILED(hr)) {
     return hr;
   }
 
   // Disable redirects for POST requests.
   if (IsPostRequest()) {
-    VERIFY1(SUCCEEDED(http_client_->SetOptionInt(request_state_->request_handle,
-                                                 WINHTTP_OPTION_DISABLE_FEATURE,
-                                                 WINHTTP_DISABLE_REDIRECTS)));
+    VERIFY1(SUCCEEDED(
+        winhttp_adapter_->SetRequestOptionInt(WINHTTP_OPTION_DISABLE_FEATURE,
+                                              WINHTTP_DISABLE_REDIRECTS)));
   }
 
-  additional_headers_.AppendFormat(_T("User-Agent: %s\r\n"), user_agent_);
-  uint32 header_flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE;
-  hr = http_client_->AddRequestHeaders(request_state_->request_handle,
-                                       additional_headers_,
-                                       -1,
-                                       header_flags);
-  if (FAILED(hr)) {
-    return hr;
+  CString additional_headers = additional_headers_;
+
+  // If the target has been partially downloaded, send a range request to resume
+  // download, instead of starting from scratch again.
+  if (request_state_->current_bytes != 0 &&
+      request_state_->current_bytes != request_state_->content_length) {
+    ASSERT1(request_state_->current_bytes < request_state_->content_length);
+    additional_headers.AppendFormat(_T("Range: bytes=%d-\r\n"),
+                                    request_state_->current_bytes);
+  }
+  if (!additional_headers.IsEmpty()) {
+    uint32 header_flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE;
+    hr = winhttp_adapter_->AddRequestHeaders(additional_headers,
+                                             -1,
+                                             header_flags);
+    if (FAILED(hr)) {
+      return hr;
+    }
   }
 
   // If the WPAD detection fails, allow the request to go direct connection.
   SetProxyInformation();
 
-  // The purpose of the status callback is informational only.
-  HttpClient::StatusCallback old_callback =
-      http_client_->SetStatusCallback(request_state_->request_handle,
-                                      &SimpleRequest::StatusCallback,
-                                      WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS);
-  // No previous callback should be there for this handle or the callback
-  // could not be installed, for example, when the request handle has been
-  // canceled already.
-  const HttpClient::StatusCallback kInvalidStatusCallback =
-      reinterpret_cast<HttpClient::StatusCallback>(
-          WINHTTP_INVALID_STATUS_CALLBACK);
-  ASSERT1(old_callback == NULL ||
-          old_callback == kInvalidStatusCallback);
+  return S_OK;
+}
 
-  // Up to this point nothing has been sent over the wire.
-  // Second chance to see if it is canceled.
-  if (is_canceled_) {
-    return OMAHA_NET_E_REQUEST_CANCELLED;
+HRESULT SimpleRequest::OpenDestinationFile(HANDLE* file_handle) {
+  ASSERT1(!filename_.IsEmpty());
+  ASSERT1(file_handle);
+
+  DWORD create_disposition = request_state_->content_length == 0 ?
+                             CREATE_ALWAYS : OPEN_ALWAYS;
+
+  scoped_hfile file(::CreateFile(filename_, GENERIC_WRITE, 0, NULL,
+                                 create_disposition, FILE_ATTRIBUTE_NORMAL,
+                                 NULL));
+
+  if (!file) {
+    return HRESULTFromLastError();
   }
 
+  if (request_state_->content_length != 0) {
+    DWORD raw_file_size = ::GetFileSize(get(file), NULL);
+    if (INVALID_FILE_SIZE == raw_file_size || raw_file_size > INT_MAX) {
+      return E_FAIL;
+    }
+    int file_size = static_cast<int>(raw_file_size);
+
+    // Local file size should not be greater than remote file size and file
+    // size must match the number of bytes we previously downloaded. If not,
+    // reset the local file.
+    bool need_reset_file = file_size > request_state_->content_length;
+    need_reset_file |= file_size != request_state_->current_bytes;
+
+    if (need_reset_file) {
+      // Need to download from byte 0. Reopen the file with truncation.
+      request_state_->current_bytes = 0;
+      reset(file, ::CreateFile(filename_, GENERIC_WRITE, 0, NULL,
+                               CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
+
+      if (!file) {
+        return HRESULTFromLastError();
+      }
+    } else {
+      LARGE_INTEGER start_pos;
+      start_pos.LowPart = static_cast<DWORD>(request_state_->current_bytes);
+      start_pos.HighPart = 0;
+      if (!::SetFilePointerEx(get(file), start_pos, NULL, FILE_BEGIN)) {
+        return HRESULTFromLastError();
+      }
+    }
+  } else {
+    // Always start from byte 0 if we don't know remote file size.
+    request_state_->current_bytes = 0;
+  }
+
+  *file_handle = release(file);
+  return S_OK;
+}
+
+HRESULT SimpleRequest::SendRequest() {
   int proxy_retry_count = 0;
   int max_proxy_retries = 1;
   CString username;
   CString password;
+  HRESULT hr = S_OK;
 
   bool done = false;
   while (!done) {
     uint32& request_scheme = request_state_->proxy_authentication_scheme;
     if (request_scheme) {
-      NET_LOG(L3, (_T("[SR::DoSend][auth_scheme][%d]"), request_scheme));
-      http_client_->SetCredentials(request_state_->request_handle,
-                                   WINHTTP_AUTH_TARGET_PROXY,
-                                   request_scheme,
-                                   username, password);
+      NET_LOG(L3, (_T("[SimpleRequest::SendRequest][auth_scheme][%d]"),
+          request_scheme));
+      winhttp_adapter_->SetCredentials(WINHTTP_AUTH_TARGET_PROXY,
+                                       request_scheme,
+                                       username, password);
+
+      CString headers;
+      headers.Format(_T("%s: %d\r\n"),
+                     kHeaderXProxyRetryCount, proxy_retry_count);
+      if (!username.IsEmpty()) {
+        headers.AppendFormat(_T("%s: 1\r\n"), kHeaderXProxyManualAuth);
+      }
+      uint32 flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE;
+      VERIFY1(SUCCEEDED(winhttp_adapter_->AddRequestHeaders(headers,
+                                                            -1,
+                                                            flags)));
     }
 
     size_t bytes_to_send = request_buffer_length_;
-    hr = http_client_->SendRequest(request_state_->request_handle,
-                                   NULL,
-                                   0,
-                                   request_buffer_,
-                                   bytes_to_send,
-                                   bytes_to_send);
+    hr = winhttp_adapter_->SendRequest(NULL,
+                                       0,
+                                       request_buffer_,
+                                       bytes_to_send,
+                                       bytes_to_send);
     if (FAILED(hr)) {
       return hr;
     }
-    NET_LOG(L3, (_T("[SimpleRequest::DoSend][request sent]")));
+    NET_LOG(L3,
+        (_T("[SimpleRequest::SendRequest][request sent][server: %s][IP: %s]"),
+         winhttp_adapter_->server_name(),
+         winhttp_adapter_->server_ip()));
 
-    hr = http_client_->ReceiveResponse(request_state_->request_handle);
+    hr = winhttp_adapter_->ReceiveResponse();
 #if DEBUG
     LogResponseHeaders();
 #endif
@@ -232,11 +449,11 @@
       return hr;
     }
 
-    hr = http_client_->QueryHeadersInt(request_state_->request_handle,
-                                       WINHTTP_QUERY_STATUS_CODE,
-                                       NULL,
-                                       &request_state_->http_status_code,
-                                       NULL);
+    hr = winhttp_adapter_->QueryRequestHeadersInt(
+        WINHTTP_QUERY_STATUS_CODE,
+        NULL,
+        &request_state_->http_status_code,
+        NULL);
     if (FAILED(hr)) {
       return hr;
     }
@@ -245,6 +462,7 @@
       return E_FAIL;
     }
 
+    NetworkConfigManager& network_manager = NetworkConfigManager::Instance();
     switch (request_state_->http_status_code) {
       case HTTP_STATUS_DENIED:
         // 401 responses are not supported. Omaha does not have to authenticate
@@ -263,17 +481,17 @@
         }
         if (!request_scheme) {
           uint32 supported_schemes(0), first_scheme(0), auth_target(0);
-          hr = http_client_->QueryAuthSchemes(request_state_->request_handle,
-                                              &supported_schemes,
-                                              &first_scheme,
-                                              &auth_target);
+          hr = winhttp_adapter_->QueryAuthSchemes(&supported_schemes,
+                                                  &first_scheme,
+                                                  &auth_target);
           if (FAILED(hr)) {
             return hr;
           }
           ASSERT1(auth_target == WINHTTP_AUTH_TARGET_PROXY);
           request_scheme = ChooseProxyAuthScheme(supported_schemes);
           ASSERT1(request_scheme);
-          NET_LOG(L3, (_T("[SR::DoSend][Auth scheme][%d]"), request_scheme));
+          NET_LOG(L3, (_T("[SimpleRequest::SendRequest][Auth scheme][%d]"),
+              request_scheme));
           if (request_scheme == WINHTTP_AUTH_SCHEME_NEGOTIATE ||
               request_scheme == WINHTTP_AUTH_SCHEME_NTLM) {
             // Increases the retry count. Tries to do an autologon at first, and
@@ -285,15 +503,21 @@
 
         uint32 auth_scheme = UNKNOWN_AUTH_SCHEME;
         // May prompt the user for credentials, or get cached credentials.
-        NetworkConfig& network_config = NetworkConfig::Instance();
-        if (!network_config.GetProxyCredentials(true,
-                                                false,
-                                                request_state_->proxy,
-                                                is_https,
-                                                &username,
-                                                &password,
-                                                &auth_scheme)) {
-          NET_LOG(LE, (_T("[SimpleRequest::DoSend][GetProxyCreds failed]")));
+        NetworkConfig* network_config = NULL;
+        hr = network_manager.GetUserNetworkConfig(&network_config);
+        if (FAILED(hr)) {
+          return hr;
+        }
+        if (!network_config->GetProxyCredentials(true,
+                                                 false,
+                                                 request_state_->proxy,
+                                                 proxy_auth_config_,
+                                                 request_state_->is_https,
+                                                 &username,
+                                                 &password,
+                                                 &auth_scheme)) {
+          NET_LOG(LE,
+                  (_T("[SimpleRequest::SendRequest][GetProxyCreds failed]")));
           done = true;
           break;
         }
@@ -307,46 +531,48 @@
       default:
         // We got some kind of response. If we have a valid username, we
         // record the auth scheme with the NetworkConfig, so it can be cached
-        // for future use within this process.
+        // for future use within this process for current user..
         if (!username.IsEmpty()) {
-          VERIFY1(SUCCEEDED(NetworkConfig::Instance().SetProxyAuthScheme(
-              request_state_->proxy, is_https, request_scheme)));
+          NetworkConfig* network_config = NULL;
+          hr = network_manager.GetUserNetworkConfig(&network_config);
+          if (SUCCEEDED(hr)) {
+            VERIFY1(SUCCEEDED(network_config->SetProxyAuthScheme(
+                request_state_->proxy, request_state_->is_https,
+                request_scheme)));
+          }
         }
         done = true;
         break;
     }
   }
 
+  return hr;
+}
+
+HRESULT SimpleRequest::ReceiveData(HANDLE file_handle) {
+  ASSERT1(file_handle != INVALID_HANDLE_VALUE || filename_.IsEmpty());
+
+  HRESULT hr = S_OK;
+
   // In the case of a "204 No Content" response, WinHttp blocks when
   // querying or reading the available data. According to the RFC,
   // the 204 response must not include a message-body, and thus is always
   // terminated by the first empty line after the header fields.
-  // It appears WinHttp does not internally handles the 204 response.If this,
+  // It appears WinHttp does not internally handles the 204 response. If this,
   // condition is not handled here explicitly, WinHttp will timeout when
   // waiting for the data instead of returning right away.
   if (request_state_->http_status_code == HTTP_STATUS_NO_CONTENT) {
     return S_OK;
   }
 
-  http_client_->QueryHeadersInt(request_state_->request_handle,
-                                WINHTTP_QUERY_CONTENT_LENGTH,
-                                WINHTTP_HEADER_NAME_BY_INDEX,
-                                &request_state_->content_length,
-                                WINHTTP_NO_HEADER_INDEX);
-
-  // Read the remaining bytes of the body. If we have a file to save the
-  // response into, create the file.
-  // TODO(omaha): we should attempt to cleanup the file only if we
-  // created it in the first place.
-  ScopeGuard delete_file_guard = MakeGuard(::DeleteFile, filename_);
-  scoped_hfile file_handle;
-  if (!filename_.IsEmpty()) {
-    reset(file_handle, ::CreateFile(filename_, GENERIC_WRITE, 0, NULL,
-                                    CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
-                                    NULL));
-    if (!file_handle) {
-      return HRESULTFromLastError();
-    }
+  int content_length = 0;
+  winhttp_adapter_->QueryRequestHeadersInt(WINHTTP_QUERY_CONTENT_LENGTH,
+                                           WINHTTP_HEADER_NAME_BY_INDEX,
+                                           &content_length,
+                                           WINHTTP_NO_HEADER_INDEX);
+  if (request_state_->content_length == 0) {
+    request_state_->content_length = content_length;
+    request_state_->current_bytes = 0;
   }
 
   const bool is_http_success =
@@ -356,17 +582,35 @@
   std::vector<uint8> buffer;
   do  {
     DWORD bytes_available(0);
-    http_client_->QueryDataAvailable(request_state_->request_handle,
-                                     &bytes_available);
+    winhttp_adapter_->QueryDataAvailable(&bytes_available);
     buffer.resize(1 + bytes_available);
-    hr = http_client_->ReadData(request_state_->request_handle,
-                                &buffer.front(),
-                                buffer.size(),
-                                &bytes_available);
+    hr = winhttp_adapter_->ReadData(&buffer.front(),
+                                    buffer.size(),
+                                    &bytes_available);
     if (FAILED(hr)) {
       return hr;
     }
 
+    buffer.resize(bytes_available);
+    if (!buffer.empty()) {
+      if (!filename_.IsEmpty()) {
+        DWORD num_bytes(0);
+        if (!::WriteFile(file_handle,
+                         reinterpret_cast<const char*>(&buffer.front()),
+                         buffer.size(), &num_bytes, NULL)) {
+          return HRESULTFromLastError();
+        }
+        ASSERT1(num_bytes == buffer.size());
+      } else {
+        request_state_->response.insert(request_state_->response.end(),
+                                        buffer.begin(),
+                                        buffer.end());
+      }
+    }
+
+    // Update current_bytes after those bytes are serialized in case we
+    // pause before current_bytes is updated, we can throw away the last
+    // batch of bytes received and resume.
     request_state_->current_bytes += bytes_available;
     if (request_state_->content_length) {
       ASSERT1(request_state_->current_bytes <= request_state_->content_length);
@@ -379,34 +623,44 @@
                             WINHTTP_CALLBACK_STATUS_READ_COMPLETE,
                             NULL);
     }
-
-    buffer.resize(bytes_available);
-    if (!buffer.empty()) {
-      if (!filename_.IsEmpty()) {
-        DWORD num_bytes(0);
-        if (!::WriteFile(get(file_handle),
-                         reinterpret_cast<const char*>(&buffer.front()),
-                         buffer.size(), &num_bytes, NULL)) {
-          return HRESULTFromLastError();
-        }
-        ASSERT1(num_bytes == buffer.size());
-      } else {
-        request_state_->response.insert(request_state_->response.end(),
-                                        buffer.begin(),
-                                        buffer.end());
-      }
-    }
   } while (!buffer.empty());
 
   NET_LOG(L3, (_T("[bytes downloaded %d]"), request_state_->current_bytes));
-  if (file_handle) {
+  if (file_handle != INVALID_HANDLE_VALUE) {
     // All bytes must be written to the file in the file download case.
-    ASSERT1(::SetFilePointer(get(file_handle), 0, NULL, FILE_CURRENT) ==
+    ASSERT1(::SetFilePointer(file_handle, 0, NULL, FILE_CURRENT) ==
             static_cast<DWORD>(request_state_->current_bytes));
   }
 
-  delete_file_guard.Dismiss();
-  return S_OK;
+  download_completed_ = true;
+  return hr;
+}
+
+HRESULT SimpleRequest::PrepareRequest(HANDLE* file_handle) {
+  // Read the remaining bytes of the body. If we have a file to save the
+  // response into, create the file.
+  HRESULT hr = S_OK;
+  if (!filename_.IsEmpty()) {
+    hr = OpenDestinationFile(file_handle);
+    if (FAILED(hr)) {
+      return hr;
+    }
+  } else {
+    // Always restarts if downloading to memory.
+    request_state_->current_bytes = 0;
+    request_state_->response.clear();
+  }
+
+  return Connect();
+}
+
+HRESULT SimpleRequest::RequestData(HANDLE file_handle) {
+  HRESULT hr = SendRequest();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return ReceiveData(file_handle);
 }
 
 std::vector<uint8> SimpleRequest::GetResponse() const {
@@ -418,28 +672,21 @@
                                           const TCHAR* name,
                                           CString* value) const {
   // Name can be null when the info_level specifies the header to query.
-  ASSERT1(value);
-  if (!http_client_.get() ||
-      !request_state_.get() ||
-      !request_state_->request_handle) {
+  if (winhttp_adapter_.get()) {
+    return winhttp_adapter_->QueryRequestHeadersString(info_level,
+                                                       name,
+                                                       value,
+                                                       WINHTTP_NO_HEADER_INDEX);
+  } else {
     return E_UNEXPECTED;
   }
-
-  return http_client_->QueryHeadersString(request_state_->request_handle,
-                                          info_level,
-                                          name,
-                                          value,
-                                          WINHTTP_NO_HEADER_INDEX);
 }
 
 CString SimpleRequest::GetResponseHeaders() const {
   CString response_headers;
-  if (http_client_.get() &&
-      request_state_.get() &&
-      request_state_->request_handle) {
+  if (winhttp_adapter_.get()) {
     CString response_headers;
-    if (SUCCEEDED(http_client_->QueryHeadersString(
-        request_state_->request_handle,
+    if (SUCCEEDED(winhttp_adapter_->QueryRequestHeadersString(
         WINHTTP_QUERY_RAW_HEADERS_CRLF,
         WINHTTP_HEADER_NAME_BY_INDEX,
         &response_headers,
@@ -450,7 +697,6 @@
   return CString();
 }
 
-
 uint32 SimpleRequest::ChooseProxyAuthScheme(uint32 supported_schemes) {
   // It is the server's responsibility only to accept
   // authentication schemes that provide a sufficient level
@@ -481,35 +727,42 @@
 void SimpleRequest::SetProxyInformation() {
   bool uses_proxy = false;
   CString proxy, proxy_bypass;
-  int access_type = NetworkConfig::GetAccessType(network_config_);
+  int access_type = NetworkConfig::GetAccessType(proxy_config_);
   if (access_type == WINHTTP_ACCESS_TYPE_AUTO_DETECT) {
     HttpClient::ProxyInfo proxy_info = {0};
-    HRESULT hr = NetworkConfig::Instance().GetProxyForUrl(
-        url_,
-        network_config_.auto_config_url,
-        &proxy_info);
+    NetworkConfig* network_config = NULL;
+    NetworkConfigManager& network_manager = NetworkConfigManager::Instance();
+    HRESULT hr = network_manager.GetUserNetworkConfig(&network_config);
     if (SUCCEEDED(hr)) {
-      // The result of proxy auto-detection could be that either a proxy is
-      // found, or direct connection is allowed for the specified url.
-      ASSERT(proxy_info.access_type == WINHTTP_ACCESS_TYPE_NAMED_PROXY ||
-             proxy_info.access_type == WINHTTP_ACCESS_TYPE_NO_PROXY,
-             (_T("[Unexpected access_type][%d]"), proxy_info.access_type));
+      hr = network_config->GetProxyForUrl(
+          url_,
+          proxy_config_.auto_config_url,
+          &proxy_info);
+      if (SUCCEEDED(hr)) {
+        // The result of proxy auto-detection could be that either a proxy is
+        // found, or direct connection is allowed for the specified url.
+        ASSERT(proxy_info.access_type == WINHTTP_ACCESS_TYPE_NAMED_PROXY ||
+               proxy_info.access_type == WINHTTP_ACCESS_TYPE_NO_PROXY,
+               (_T("[Unexpected access_type][%d]"), proxy_info.access_type));
 
-      uses_proxy = proxy_info.access_type == WINHTTP_ACCESS_TYPE_NAMED_PROXY;
+        uses_proxy = proxy_info.access_type == WINHTTP_ACCESS_TYPE_NAMED_PROXY;
 
-      proxy = proxy_info.proxy;
-      proxy_bypass = proxy_info.proxy_bypass;
+        proxy = proxy_info.proxy;
+        proxy_bypass = proxy_info.proxy_bypass;
 
-      ::GlobalFree(const_cast<wchar_t*>(proxy_info.proxy));
-      ::GlobalFree(const_cast<wchar_t*>(proxy_info.proxy_bypass));
+        ::GlobalFree(const_cast<wchar_t*>(proxy_info.proxy));
+        ::GlobalFree(const_cast<wchar_t*>(proxy_info.proxy_bypass));
+        } else {
+        ASSERT1(!uses_proxy);
+        NET_LOG(LW, (_T("[GetProxyForUrl failed][0x%08x]"), hr));
+      }
     } else {
-      ASSERT1(!uses_proxy);
-      NET_LOG(LW, (_T("[GetProxyForUrl failed][0x%08x]"), hr));
+      NET_LOG(LW, (_T("[GetUserNetworkConfig failed][0x%08x]"), hr));
     }
   } else if (access_type == WINHTTP_ACCESS_TYPE_NAMED_PROXY) {
     uses_proxy = true;
-    proxy = network_config_.proxy;
-    proxy_bypass = network_config_.proxy_bypass;
+    proxy = proxy_config_.proxy;
+    proxy_bypass = proxy_config_.proxy_bypass;
   }
 
   // If a proxy is going to be used, modify the state of the object and
@@ -526,99 +779,20 @@
     proxy_info.proxy_bypass = request_state_->proxy_bypass;
 
     NET_LOG(L3, (_T("[using proxy %s]"), proxy_info.proxy));
-    VERIFY1(SUCCEEDED(http_client_->SetOption(request_state_->request_handle,
-                                              WINHTTP_OPTION_PROXY,
-                                              &proxy_info,
-                                              sizeof(proxy_info))));
+    VERIFY1(SUCCEEDED(winhttp_adapter_->SetRequestOption(WINHTTP_OPTION_PROXY,
+                                                         &proxy_info,
+                                                         sizeof(proxy_info))));
   }
 }
 
 void SimpleRequest::LogResponseHeaders() {
   CString response_headers;
-  http_client_->QueryHeadersString(request_state_->request_handle,
-                                   WINHTTP_QUERY_RAW_HEADERS_CRLF,
-                                   WINHTTP_HEADER_NAME_BY_INDEX,
-                                   &response_headers,
-                                   WINHTTP_NO_HEADER_INDEX);
+  winhttp_adapter_->QueryRequestHeadersString(WINHTTP_QUERY_RAW_HEADERS_CRLF,
+                                              WINHTTP_HEADER_NAME_BY_INDEX,
+                                              &response_headers,
+                                              WINHTTP_NO_HEADER_INDEX);
   NET_LOG(L3, (_T("[response headers...]\r\n%s"), response_headers));
 }
 
-void __stdcall SimpleRequest::StatusCallback(HINTERNET handle,
-                                             uint32 context,
-                                             uint32 status,
-                                             void* info,
-                                             size_t info_len) {
-  UNREFERENCED_PARAMETER(context);
-
-  CString status_string;
-  CString info_string;
-  switch (status) {
-    case WINHTTP_CALLBACK_STATUS_RESOLVING_NAME:
-      status_string = _T("resolving");
-      info_string.SetString(static_cast<TCHAR*>(info), info_len);  // host name
-      break;
-    case WINHTTP_CALLBACK_STATUS_NAME_RESOLVED:
-      status_string = _T("resolved");
-      info_string.SetString(static_cast<TCHAR*>(info), info_len);  // host ip
-      break;
-    case WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER:
-      status_string = _T("connecting");
-      info_string.SetString(static_cast<TCHAR*>(info), info_len);  // host ip
-      break;
-    case WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER:
-      status_string = _T("connected");
-      info_string.SetString(static_cast<TCHAR*>(info), info_len);  // host ip
-      break;
-    case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
-      status_string = _T("sending");
-      break;
-    case WINHTTP_CALLBACK_STATUS_REQUEST_SENT:
-      status_string = _T("sent");
-      break;
-    case WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE:
-      status_string = _T("receiving");
-      break;
-    case WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED:
-      status_string = _T("received");
-      break;
-    case WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION:
-      status_string = _T("closing");
-      break;
-    case WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED:
-      status_string = _T("closed");
-      break;
-    case WINHTTP_CALLBACK_STATUS_REDIRECT:
-      status_string = _T("redirect");
-      info_string.SetString(static_cast<TCHAR*>(info), info_len);  // url
-      break;
-    default:
-      break;
-  }
-  CString log_line;
-  log_line.AppendFormat(_T("[HttpClient::StatusCallback][0x%08x]"), handle);
-  if (!status_string.IsEmpty()) {
-    log_line.AppendFormat(_T("[%s]"), status_string);
-  } else {
-    log_line.AppendFormat(_T("[0x%08x]"), status);
-  }
-  if (!info_string.IsEmpty()) {
-    log_line.AppendFormat(_T("[%s]"), info_string);
-  }
-  NET_LOG(L3, (_T("%s"), log_line));
-}
-
-void SimpleRequest::CloseHandles(TransientRequestState* request_state) {
-  if (request_state) {
-    if (request_state->request_handle) {
-      http_client_->Close(request_state->request_handle);
-      request_state->request_handle = NULL;
-    }
-    if (request_state->connection_handle) {
-      http_client_->Close(request_state->connection_handle);
-      request_state->connection_handle = NULL;
-    }
-  }
-}
-
 }  // namespace omaha
 
diff --git a/net/simple_request.h b/net/simple_request.h
index 374b0b9..bed0ced 100644
--- a/net/simple_request.h
+++ b/net/simple_request.h
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -20,21 +20,23 @@
 //
 // TODO(omaha): receiving a response into a file is not implemented yet.
 
-#ifndef OMAHA_NET_SIMPLE_REQUEST_H__
-#define OMAHA_NET_SIMPLE_REQUEST_H__
+#ifndef OMAHA_NET_SIMPLE_REQUEST_H_
+#define OMAHA_NET_SIMPLE_REQUEST_H_
 
 #include <atlstr.h>
 #include <vector>
 #include "base/basictypes.h"
 #include "base/scoped_ptr.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/net/http_client.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/synchronized.h"
 #include "omaha/net/http_request.h"
 #include "omaha/net/network_config.h"
 
 namespace omaha {
 
+class WinHttpAdapter;
+
 class SimpleRequest : public HttpRequestInterface {
  public:
   SimpleRequest();
@@ -46,6 +48,10 @@
 
   virtual HRESULT Cancel();
 
+  virtual HRESULT Pause();
+
+  virtual HRESULT Resume();
+
   virtual std::vector<uint8> GetResponse() const;
 
   virtual int GetHttpStatusCode() const {
@@ -58,25 +64,25 @@
 
   virtual CString GetResponseHeaders() const;
 
-  virtual CString ToString() const { return _T("WinHTTP"); }
+  virtual CString ToString() const { return _T("winhttp"); }
 
   virtual void set_session_handle(HINTERNET session_handle) {
     session_handle_ = session_handle;
   }
 
-  virtual void set_url(const CString& url) { url_ = url; }
+  virtual void set_url(const CString& url);
 
   virtual void set_request_buffer(const void* buffer, size_t buffer_length) {
     request_buffer_ = buffer;
     request_buffer_length_ = buffer_length;
   }
 
-  virtual void set_network_configuration(const Config& network_config) {
-    network_config_ = network_config;
+  virtual void set_proxy_configuration(const ProxyConfig& proxy_config) {
+    proxy_config_ = proxy_config;
   }
 
   // Sets the filename to receive the response instead of the memory buffer.
-  virtual void set_filename(const CString& filename) { filename_ = filename; }
+  virtual void set_filename(const CString& filename);
 
   virtual void set_low_priority(bool low_priority) {
     low_priority_ = low_priority;
@@ -90,14 +96,31 @@
     additional_headers_ = additional_headers;
   }
 
+  // This request always uses the specified protocol so it is fine to ignore
+  // this attribute.
+  virtual void set_preserve_protocol(bool preserve_protocol) {
+    UNREFERENCED_PARAMETER(preserve_protocol);
+  }
+
   virtual CString user_agent() const { return user_agent_; }
 
   virtual void set_user_agent(const CString& user_agent) {
     user_agent_ = user_agent;
   }
 
+  virtual void set_proxy_auth_config(const ProxyAuthConfig& proxy_auth_config) {
+    proxy_auth_config_ = proxy_auth_config;
+  }
+
  private:
-  HRESULT DoSend();
+  HRESULT OpenDestinationFile(HANDLE* file_handle);
+  HRESULT PrepareRequest(HANDLE* file_handle);
+  HRESULT Connect();
+  HRESULT SendRequest();
+  HRESULT ReceiveData(HANDLE file_handle);
+  HRESULT RequestData(HANDLE file_handle);
+  bool IsResumeNeeded() const;
+  bool IsPauseSupported() const;
 
   void LogResponseHeaders();
 
@@ -105,20 +128,18 @@
   void SetProxyInformation();
 
   struct TransientRequestState;
-  void CloseHandles(TransientRequestState* transient_request_state);
+  void CloseHandles();
 
   static uint32 ChooseProxyAuthScheme(uint32 supported_schemes);
 
-  static void __stdcall StatusCallback(HINTERNET handle,
-                                       uint32 context,
-                                       uint32 status,
-                                       void* info,
-                                       uint32 info_len);
-
   // Returns true if the request is a POST request, in other words, if there
   // is a request buffer to be sent to the server.
   bool IsPostRequest() const { return request_buffer_ != NULL; }
 
+  // When in pause state, caller will be blocked until Resume() is called.
+  // Returns immediately otherwise.
+  void WaitForResumeEvent();
+
   // Holds the transient state corresponding to a single http request. We
   // prefer to isolate the state of a request to avoid dirty state.
   struct TransientRequestState {
@@ -126,19 +147,15 @@
         : port(0),
           http_status_code(0),
           proxy_authentication_scheme(0),
+          is_https(false),
           content_length(0),
-          current_bytes(0),
-          connection_handle(NULL),
-          request_handle(NULL) {}
-    ~TransientRequestState() {
-      ASSERT1(connection_handle == NULL);
-      ASSERT1(request_handle == NULL);
-    }
+          current_bytes(0) {}
 
     CString scheme;
     CString server;
     int     port;
     CString url_path;
+    bool    is_https;
 
     std::vector<uint8> response;
     int http_status_code;
@@ -147,13 +164,13 @@
     CString proxy_bypass;
     int content_length;
     int current_bytes;
-
-    HINTERNET connection_handle;
-    HINTERNET request_handle;
   };
 
   LLock lock_;
   volatile bool is_canceled_;
+  volatile bool is_closed_;
+  volatile bool pause_happened_;
+  LLock ready_to_pause_lock_;
   HINTERNET session_handle_;  // Not owned by this class.
   CString url_;
   CString filename_;
@@ -161,16 +178,19 @@
   size_t      request_buffer_length_;   // Length of the request body.
   CString additional_headers_;
   CString user_agent_;
-  Config network_config_;
+  ProxyAuthConfig proxy_auth_config_;
+  ProxyConfig proxy_config_;
   bool low_priority_;
   NetworkRequestCallback* callback_;
-  scoped_ptr<HttpClient> http_client_;
+  scoped_ptr<WinHttpAdapter> winhttp_adapter_;
   scoped_ptr<TransientRequestState> request_state_;
+  scoped_event event_resume_;
+  bool download_completed_;
 
-  DISALLOW_EVIL_CONSTRUCTORS(SimpleRequest);
+  DISALLOW_COPY_AND_ASSIGN(SimpleRequest);
 };
 
 }   // namespace omaha
 
-#endif  // OMAHA_NET_SIMPLE_REQUEST_H__
+#endif  // OMAHA_NET_SIMPLE_REQUEST_H_
 
diff --git a/net/simple_request_unittest.cc b/net/simple_request_unittest.cc
index 71b8c95..c72dce3 100644
--- a/net/simple_request_unittest.cc
+++ b/net/simple_request_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -22,43 +22,99 @@
 #include <winhttp.h>
 #include <atlstr.h>
 #include "base/basictypes.h"
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/error.h"
-#include "omaha/common/string.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/error.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/string.h"
 #include "omaha/net/network_config.h"
 #include "omaha/net/simple_request.h"
 #include "omaha/testing/unit_test.h"
 
 namespace omaha {
 
+const TCHAR* kBigFileUrl =
+    _T("http://dl.google.com/dl/edgedl/update2/UpdateData_10M.bin");
+
+DWORD WINAPI PauseAndResumeThreadProc(void* parameter) {
+  SimpleRequest* simple_request = reinterpret_cast<SimpleRequest*>(parameter);
+  ASSERT1(simple_request);
+
+  bool request_paused = false;
+
+  // Loop even times so the download thread won't be blocked by Pause() after
+  // loop exit.
+  for (int i = 0; i < 10; ++i) {
+    ::Sleep(100);
+
+    if (request_paused) {
+      simple_request->Resume();
+    } else {
+      simple_request->Pause();
+    }
+
+    request_paused = !request_paused;
+  }
+
+  return 0;
+}
+
+DWORD WINAPI CancelRequestThreadProc(void* parameter) {
+  SimpleRequest* simple_request = reinterpret_cast<SimpleRequest*>(parameter);
+  ASSERT1(simple_request);
+
+  // Wait a short period of time so the download can start. Assumes the file
+  // is large enough so that download will not complete within the sleep time.
+  ::Sleep(500);
+
+  simple_request->Cancel();
+  return 0;
+}
+
 class SimpleRequestTest : public testing::Test {
  protected:
   SimpleRequestTest() {}
 
-  void SimpleGet(const CString& url, const Config& config);
+  void SimpleGet(const CString& url, const ProxyConfig& config);
+  void SimpleDownloadFile(const CString& url,
+                          const CString& filename,
+                          const ProxyConfig& config);
+  void SimpleDownloadFilePauseAndResume(const CString& url,
+                                        const CString& filename,
+                                        const ProxyConfig& config);
+  void SimpleDownloadFileCancellation(const CString& filename, bool do_cancel);
+  void SimpleGetHostNotFound(const CString& url, const ProxyConfig& config);
 
-  void SimpleGetHostNotFound(const CString& url, const Config& config);
+  void SimpleGetFileNotFound(const CString& url, const ProxyConfig& config);
 
-  void SimpleGetFileNotFound(const CString& url, const Config& config);
-
-  void SimpleGetRedirect(const CString& url, const Config& config);
+  void SimpleGetRedirect(const CString& url, const ProxyConfig& config);
 
   void PrepareRequest(const CString& url,
-                      const Config& config,
+                      const ProxyConfig& config,
                       SimpleRequest* simple_request);
 };
 
 void SimpleRequestTest::PrepareRequest(const CString& url,
-                                       const Config& config,
+                                       const ProxyConfig& config,
                                        SimpleRequest* simple_request) {
   ASSERT_TRUE(simple_request);
-  HINTERNET handle = NetworkConfig::Instance().session().session_handle;
+  NetworkConfig* network_config = NULL;
+  EXPECT_HRESULT_SUCCEEDED(
+      NetworkConfigManager::Instance().GetUserNetworkConfig(&network_config));
+
+  HINTERNET handle = network_config->session().session_handle;
   simple_request->set_session_handle(handle);
   simple_request->set_url(url);
-  simple_request->set_network_configuration(config);
+  simple_request->set_proxy_configuration(config);
+
+  CString user_agent_header;
+  user_agent_header.Format(_T("User-Agent: %s\r\n"),
+                           simple_request->user_agent());
+  simple_request->set_additional_headers(user_agent_header);
 }
 
-void SimpleRequestTest::SimpleGet(const CString& url, const Config& config) {
+void SimpleRequestTest::SimpleGet(const CString& url,
+                                  const ProxyConfig& config) {
   SimpleRequest simple_request;
   PrepareRequest(url, config, &simple_request);
   EXPECT_HRESULT_SUCCEEDED(simple_request.Send());
@@ -91,8 +147,90 @@
   EXPECT_STREQ(simple_request.user_agent(), user_agent);
 }
 
+void SimpleRequestTest::SimpleDownloadFile(const CString& url,
+                                           const CString& filename,
+                                           const ProxyConfig& config) {
+  SimpleRequest simple_request;
+  PrepareRequest(url, config, &simple_request);
+  simple_request.set_filename(filename);
+
+  EXPECT_HRESULT_SUCCEEDED(simple_request.Send());
+
+  int http_status = simple_request.GetHttpStatusCode();
+  EXPECT_TRUE(http_status == HTTP_STATUS_OK ||
+              http_status == HTTP_STATUS_PARTIAL_CONTENT);
+}
+
+void SimpleRequestTest::SimpleDownloadFilePauseAndResume(
+    const CString& url,
+    const CString& filename,
+    const ProxyConfig& config) {
+  const int kNumThreads = 3;
+  const int kMaxWaitTimeMs = 10000;
+  SimpleRequest simple_request;
+  PrepareRequest(url, config, &simple_request);
+  simple_request.set_filename(filename);
+
+  // Testing call Pause before Send.
+  simple_request.Pause();
+  simple_request.Resume();
+  HANDLE pause_and_resume_threads[kNumThreads] = { NULL };
+
+  // Now create some threads to run Pause/Resume in the middle of sending.
+  for (int i = 0; i < kNumThreads; ++i) {
+    pause_and_resume_threads[i] = ::CreateThread(NULL,
+                                                 0,
+                                                 PauseAndResumeThreadProc,
+                                                 &simple_request,
+                                                 0,
+                                                 NULL);
+    EXPECT_TRUE(pause_and_resume_threads[i] != NULL);
+  }
+
+  EXPECT_HRESULT_SUCCEEDED(simple_request.Send());
+
+  int http_status = simple_request.GetHttpStatusCode();
+  EXPECT_TRUE(http_status == HTTP_STATUS_OK ||
+              http_status == HTTP_STATUS_PARTIAL_CONTENT);
+
+  DWORD result = ::WaitForMultipleObjects(arraysize(pause_and_resume_threads),
+                                          pause_and_resume_threads,
+                                          true,   // Wait all threads to exit.
+                                          kMaxWaitTimeMs);
+  EXPECT_NE(result, WAIT_FAILED);
+  EXPECT_NE(result, WAIT_TIMEOUT);
+
+  for (int i = 0; i < kNumThreads; ++i) {
+    ::CloseHandle(pause_and_resume_threads[i]);
+  }
+}
+
+void SimpleRequestTest::SimpleDownloadFileCancellation(
+    const CString& filename, bool do_cancel) {
+  SimpleRequest simple_request;
+  PrepareRequest(kBigFileUrl, ProxyConfig(), &simple_request);
+  simple_request.set_filename(filename);
+
+  scoped_handle cancel_thread_handle;
+  if (do_cancel) {
+    reset(cancel_thread_handle, ::CreateThread(NULL,
+                                               0,
+                                               CancelRequestThreadProc,
+                                               &simple_request,
+                                               0, NULL));
+  }
+
+  HRESULT hr = simple_request.Send();
+  if (do_cancel) {
+    EXPECT_TRUE(hr == GOOPDATE_E_CANCELLED || SUCCEEDED(hr));
+    ::WaitForSingleObject(get(cancel_thread_handle), INFINITE);
+  } else {
+    EXPECT_HRESULT_SUCCEEDED(hr);
+  }
+}
+
 void SimpleRequestTest::SimpleGetHostNotFound(const CString& url,
-                                              const Config& config) {
+                                              const ProxyConfig& config) {
   SimpleRequest simple_request;
   PrepareRequest(url, config, &simple_request);
 
@@ -122,7 +260,7 @@
 }
 
 void SimpleRequestTest::SimpleGetFileNotFound(const CString& url,
-                                              const Config& config) {
+                                              const ProxyConfig& config) {
   SimpleRequest simple_request;
   PrepareRequest(url, config, &simple_request);
   EXPECT_HRESULT_SUCCEEDED(simple_request.Send());
@@ -130,11 +268,14 @@
 }
 
 void SimpleRequestTest::SimpleGetRedirect(const CString& url,
-                                          const Config& config) {
+                                          const ProxyConfig& config) {
   SimpleRequest simple_request;
   PrepareRequest(url, config, &simple_request);
   EXPECT_HRESULT_SUCCEEDED(simple_request.Send());
-  EXPECT_EQ(HTTP_STATUS_OK, simple_request.GetHttpStatusCode());
+
+  int http_status = simple_request.GetHttpStatusCode();
+  EXPECT_TRUE(http_status == HTTP_STATUS_OK ||
+              http_status == HTTP_STATUS_PARTIAL_CONTENT);
 }
 
 //
@@ -142,36 +283,130 @@
 //
 // http get, direct connection.
 TEST_F(SimpleRequestTest, HttpGetDirect) {
-  SimpleGet(_T("http://www.google.com/robots.txt"), Config());
+  if (IsTestRunByLocalSystem()) {
+    return;
+  }
+
+  SimpleGet(_T("http://www.google.com/robots.txt"), ProxyConfig());
+}
+
+// http get, direct connection, download to file
+TEST_F(SimpleRequestTest, HttpDownloadDirect) {
+  if (IsTestRunByLocalSystem()) {
+    return;
+  }
+
+  CString temp_file;
+  EXPECT_TRUE(::GetTempFileName(app_util::GetModuleDirectory(NULL),
+                                _T("SRT"),
+                                0,
+                                CStrBuf(temp_file, MAX_PATH)));
+  ScopeGuard guard = MakeGuard(::DeleteFile, temp_file);
+
+  SimpleDownloadFile(
+      _T("http://dl.google.com/update2/UpdateData.bin"),
+      temp_file, ProxyConfig());
+}
+
+// http get, direct connection, download to file, pause/resume
+// Download same file twice with and without pause/resume. The downloaded
+// files should be same.
+TEST_F(SimpleRequestTest, HttpDownloadDirectPauseAndResume) {
+  if (!ShouldRunLargeTest()) {
+    return;
+  }
+
+  // Download the same URL with and without pause/resume.
+  CString temp_file1;
+  EXPECT_TRUE(::GetTempFileName(app_util::GetModuleDirectory(NULL),
+                                _T("SRT"),
+                                0,
+                                CStrBuf(temp_file1, MAX_PATH)));
+  ScopeGuard guard = MakeGuard(::DeleteFile, temp_file1);
+
+  SimpleDownloadFilePauseAndResume(kBigFileUrl, temp_file1, ProxyConfig());
+
+  CString temp_file2;
+  EXPECT_TRUE(::GetTempFileName(app_util::GetModuleDirectory(NULL),
+                                _T("SRT"),
+                                0,
+                                CStrBuf(temp_file2, MAX_PATH)));
+  ScopeGuard guard2 = MakeGuard(::DeleteFile, temp_file2);
+
+  SimpleDownloadFile(kBigFileUrl, temp_file2, ProxyConfig());
+
+  // Compares that the downloaded files are equal
+  scoped_hfile file_handle1(::CreateFile(temp_file1, GENERIC_READ,
+      FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
+  EXPECT_NE(get(file_handle1), INVALID_HANDLE_VALUE);
+
+  scoped_hfile file_handle2(::CreateFile(temp_file2, GENERIC_READ,
+      FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
+
+  // Compare file size
+  DWORD file_size = GetFileSize(get(file_handle1), NULL);
+  EXPECT_EQ(file_size, GetFileSize(get(file_handle2), NULL));
+
+  // Compare file contents
+  const int kBufferLength = 1024;
+  BYTE buffer1[kBufferLength] = {0};
+  BYTE buffer2[kBufferLength] = {0};
+
+  for (int i = static_cast<int>(file_size); i > 0; i -= kBufferLength) {
+    int bytes_to_read = std::min(kBufferLength, i);
+    DWORD num_bytes_got = 0;
+
+    EXPECT_TRUE(ReadFile(get(file_handle1), buffer1,
+                         bytes_to_read, &num_bytes_got, NULL));
+    EXPECT_EQ(num_bytes_got, static_cast<DWORD>(bytes_to_read));
+    EXPECT_TRUE(ReadFile(get(file_handle2), buffer2,
+                         bytes_to_read, &num_bytes_got, NULL));
+    EXPECT_EQ(num_bytes_got, static_cast<DWORD>(bytes_to_read));
+    EXPECT_EQ(memcmp(buffer1, buffer2, bytes_to_read), 0);
+  }
+
+  // Make sure that file handles are closed first so that the delete guards
+  // can delete the temp files.
+  reset(file_handle1);
+  reset(file_handle2);
 }
 
 // http get, direct connection, negative test.
 TEST_F(SimpleRequestTest, HttpGetDirectHostNotFound) {
-  SimpleGetHostNotFound(_T("http://no_such_host.google.com/"), Config());
+  if (IsTestRunByLocalSystem()) {
+    return;
+  }
+
+  SimpleGetHostNotFound(_T("http://no_such_host.google.com/"), ProxyConfig());
 }
 
 // http get, direct connection, negative test.
 TEST_F(SimpleRequestTest, HttpGetDirectFileNotFound) {
-  SimpleGetFileNotFound(_T("http://tools.google.com/no_such_file"), Config());
+  if (IsTestRunByLocalSystem()) {
+    return;
+  }
+
+  SimpleGetFileNotFound(_T("http://tools.google.com/no_such_file"),
+                        ProxyConfig());
 }
 
 // http get, proxy wpad.
 TEST_F(SimpleRequestTest, HttpGetProxy) {
-  Config config;
+  ProxyConfig config;
   config.auto_detect = true;
   SimpleGet(_T("http://www.google.com/robots.txt"), config);
 }
 
 // http get, proxy wpad, negative test.
 TEST_F(SimpleRequestTest, HttpGetProxyHostNotFound) {
-  Config config;
+  ProxyConfig config;
   config.auto_detect = true;
   SimpleGetHostNotFound(_T("http://no_such_host.google.com/"), config);
 }
 
 // http get, proxy wpad.
 TEST_F(SimpleRequestTest, HttpGetProxyFileNotFound) {
-  Config config;
+  ProxyConfig config;
   config.auto_detect = true;
   SimpleGetFileNotFound(_T("http://tools.google.com/no_such_file"), config);
 }
@@ -182,36 +417,45 @@
 //
 // https get, direct.
 TEST_F(SimpleRequestTest, HttpsGetDirect) {
-  SimpleGet(_T("https://www.google.com/robots.txt"), Config());
+  if (IsTestRunByLocalSystem()) {
+    return;
+  }
+
+  SimpleGet(_T("https://www.google.com/robots.txt"), ProxyConfig());
 }
 
 // https get, direct, negative test.
 TEST_F(SimpleRequestTest, HttpsGetDirectHostNotFound) {
-  SimpleGetHostNotFound(_T("https://no_such_host.google.com/"), Config());
+  SimpleGetHostNotFound(_T("https://no_such_host.google.com/"), ProxyConfig());
 }
 
 // https get, direct connection, negative test.
 TEST_F(SimpleRequestTest, HttpsGetDirectFileNotFound) {
-  SimpleGetFileNotFound(_T("https://tools.google.com/no_such_file"), Config());
+  if (IsTestRunByLocalSystem()) {
+    return;
+  }
+
+  SimpleGetFileNotFound(_T("https://tools.google.com/no_such_file"),
+                        ProxyConfig());
 }
 
 // https get, proxy wpad.
 TEST_F(SimpleRequestTest, HttpsGetProxy) {
-  Config config;
+  ProxyConfig config;
   config.auto_detect = true;
   SimpleGet(_T("https://www.google.com/robots.txt"), config);
 }
 
 // https get, proxy wpad, negative test.
 TEST_F(SimpleRequestTest, HttpsGetProxyHostNotFound) {
-  Config config;
+  ProxyConfig config;
   config.auto_detect = true;
   SimpleGetHostNotFound(_T("https://no_such_host.google.com/"), config);
 }
 
 // https get, proxy wpad, negative test.
 TEST_F(SimpleRequestTest, HttpsGetProxyFileNotFound) {
-  Config config;
+  ProxyConfig config;
   config.auto_detect = true;
   SimpleGetFileNotFound(_T("https://tools.google.com/no_such_file"), config);
 }
@@ -219,11 +463,33 @@
 // Should not be able to reuse the object once canceled, even if closed.
 TEST_F(SimpleRequestTest, Cancel_CannotReuse) {
   SimpleRequest simple_request;
-  PrepareRequest(_T("http:\\foo\\"), Config(), &simple_request);
+  PrepareRequest(_T("http:\\foo\\"), ProxyConfig(), &simple_request);
   EXPECT_HRESULT_SUCCEEDED(simple_request.Cancel());
-  EXPECT_EQ(OMAHA_NET_E_REQUEST_CANCELLED, simple_request.Send());
+  EXPECT_EQ(GOOPDATE_E_CANCELLED, simple_request.Send());
   EXPECT_HRESULT_SUCCEEDED(simple_request.Close());
-  EXPECT_EQ(OMAHA_NET_E_REQUEST_CANCELLED, simple_request.Send());
+  EXPECT_EQ(GOOPDATE_E_CANCELLED, simple_request.Send());
+}
+
+TEST_F(SimpleRequestTest, Cancel_ShouldDeleteTempFile) {
+  CString temp_file;
+  EXPECT_TRUE(::GetTempFileName(app_util::GetModuleDirectory(NULL),
+                                _T("SRT"),
+                                0,
+                                CStrBuf(temp_file, MAX_PATH)));
+  ScopeGuard guard = MakeGuard(::DeleteFile, temp_file);
+
+  // Verify that cancellation should remove partially downloaded file.
+  bool do_cancel = true;
+  SimpleDownloadFileCancellation(temp_file, do_cancel);
+  EXPECT_EQ(INVALID_FILE_ATTRIBUTES, ::GetFileAttributes(temp_file));
+  EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
+
+  if (ShouldRunLargeTest()) {
+    // Verify that target file is preserved if download is not cancelled.
+    do_cancel = false;
+    SimpleDownloadFileCancellation(temp_file, do_cancel);
+    EXPECT_NE(INVALID_FILE_ATTRIBUTES, ::GetFileAttributes(temp_file));
+  }
 }
 
 // Http get request should follow redirects. The url below redirects to
@@ -232,8 +498,12 @@
 // TODO(omaha): Pick a new URL since this service is now obsolete and could
 // be removed at some point.
 TEST_F(SimpleRequestTest, HttpGet_Redirect) {
+  if (IsTestRunByLocalSystem()) {
+    return;
+  }
+
   SimpleGetRedirect(_T("http://tools.google.com/service/update2/oneclick"),
-                    Config());
+                    ProxyConfig());
 }
 
 }  // namespace omaha
diff --git a/net/urlmon_request.cc b/net/urlmon_request.cc
index 54c2fc3..333f02f 100644
--- a/net/urlmon_request.cc
+++ b/net/urlmon_request.cc
@@ -1,4 +1,4 @@
-// Copyright 2008-2009 Google Inc.
+// Copyright 2008-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -19,12 +19,11 @@
 #include <atlcom.h>
 #include <atlcomcli.h>
 #include <vector>
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/string.h"
-#include "omaha/common/utils.h"
-#include "omaha/net/bind_status_callback.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/string.h"
+#include "omaha/base/utils.h"
 #include "omaha/net/http_client.h"
 #include "omaha/net/network_request.h"
 #include "omaha/net/network_config.h"
@@ -42,6 +41,7 @@
 UrlmonRequest::UrlmonRequest()
     : request_buffer_(NULL),
       request_buffer_length_(0),
+      proxy_auth_config_(NULL, CString()),
       http_status_code_(0),
       is_cancelled_(false) {
   NET_LOG(L3, (_T("[UrlmonRequest::UrlmonRequest]")));
@@ -60,23 +60,6 @@
   return S_OK;
 }
 
-CComBSTR BuildRequestHeaders(const CString& user_agent,
-                             const CString& additional_headers) {
-  CString headers_to_send;
-  if (!user_agent.IsEmpty()) {
-    headers_to_send += _T("User-Agent: ");
-    headers_to_send += user_agent;
-    headers_to_send = String_MakeEndWith(headers_to_send, _T("\r\n"), false);
-  }
-
-  if (!additional_headers.IsEmpty()) {
-    headers_to_send += additional_headers;
-    headers_to_send = String_MakeEndWith(headers_to_send, _T("\r\n"), false);
-  }
-
-  return CComBSTR(headers_to_send);
-}
-
 HRESULT UrlmonRequest::ProcessResponseHeaders(
                             const CComVariant& headers,
                             const CComSafeArray<DWORD>& headers_needed) {
@@ -132,13 +115,14 @@
                                    CComVariant* response_headers,
                                    DWORD* response_code,
                                    BSTR* cache_filename) {
-  HRESULT hr = BindStatusCallback::CreateAndSend(url,
-                                                 post_data,
-                                                 request_headers,
-                                                 response_headers_needed,
-                                                 response_headers,
-                                                 response_code,
-                                                 cache_filename);
+  HRESULT hr = bsc_.Send(url,
+                         post_data,
+                         request_headers,
+                         response_headers_needed,
+                         response_headers,
+                         response_code,
+                         cache_filename);
+
   NET_LOG(L3, (_T("[UrlmonRequest::SendRequest][0x%x][%d][%s]"),
                hr, *response_code, *cache_filename));
   if (!*response_code) {
@@ -151,12 +135,11 @@
 HRESULT UrlmonRequest::Send() {
   NET_LOG(L3, (_T("[UrlmonRequest::Send]")));
   if (is_cancelled_) {
-    return OMAHA_NET_E_REQUEST_CANCELLED;
+    return GOOPDATE_E_CANCELLED;
   }
 
   ASSERT1(url_.Length() > 0);
-  CComBSTR headers_to_send(BuildRequestHeaders(user_agent(),
-                                               additional_headers_));
+  CComBSTR headers_to_send(additional_headers_);
   CComBSTR post_data;
   if (request_buffer_) {
     post_data.AppendBytes(static_cast<const char*>(request_buffer_),
@@ -186,11 +169,18 @@
   return S_OK;
 }
 
-// TODO(omaha): cancel the underlying IBindStatusCallback object.
 HRESULT UrlmonRequest::Cancel() {
   NET_LOG(L2, (_T("[UrlmonRequest::Cancel]")));
   ::InterlockedExchange(&is_cancelled_, true);
-  return S_OK;
+  return bsc_.Cancel();
+}
+
+HRESULT UrlmonRequest::Pause() {
+  return E_NOTIMPL;
+}
+
+HRESULT UrlmonRequest::Resume() {
+  return E_NOTIMPL;
 }
 
 }  // namespace omaha
diff --git a/net/urlmon_request.h b/net/urlmon_request.h
index 7756229..2f84ac8 100644
--- a/net/urlmon_request.h
+++ b/net/urlmon_request.h
@@ -1,4 +1,4 @@
-// Copyright 2008-2009 Google Inc.
+// Copyright 2008-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -24,8 +24,8 @@
 #include <map>
 #include <vector>
 #include "base/basictypes.h"
-#include "goopdate/google_update_idl.h"
-#include "omaha/common/debug.h"
+#include "omaha/base/debug.h"
+#include "omaha/net/bind_status_callback.h"
 #include "omaha/net/http_request.h"
 
 namespace omaha {
@@ -42,6 +42,10 @@
 
   virtual HRESULT Cancel();
 
+  virtual HRESULT Pause();
+
+  virtual HRESULT Resume();
+
   virtual std::vector<uint8> GetResponse() const {
     return response_body_;
   }
@@ -82,32 +86,46 @@
     request_buffer_length_ = buffer_length;
   }
 
-  virtual void set_network_configuration(const Config& network_config) {
-    network_config;
+  virtual void set_proxy_configuration(const ProxyConfig& proxy_config) {
+    proxy_config_ = proxy_config;
+  }
+
+  virtual void set_network_configuration(const ProxyConfig& network_config) {
+    UNREFERENCED_PARAMETER(network_config);
   }
 
   // Sets the filename to receive the response instead of the memory buffer.
   virtual void set_filename(const CString& filename) { filename_ = filename; }
 
   virtual void set_low_priority(bool low_priority) {
-    low_priority;
+    UNREFERENCED_PARAMETER(low_priority);
   }
 
   virtual void set_callback(NetworkRequestCallback* callback) {
     // TODO(Omaha) - Provide events.
-    callback;
+    UNREFERENCED_PARAMETER(callback);
   }
 
   virtual void set_additional_headers(const CString& additional_headers) {
     additional_headers_ = additional_headers;
   }
 
+  // This request always uses the specified protocol so it is fine to ignore
+  // this attribute.
+  virtual void set_preserve_protocol(bool preserve_protocol) {
+    UNREFERENCED_PARAMETER(preserve_protocol);
+  }
+
   virtual CString user_agent() const { return user_agent_; }
 
   virtual void set_user_agent(const CString& user_agent) {
     user_agent_ = user_agent;
   }
 
+  virtual void set_proxy_auth_config(const ProxyAuthConfig& proxy_auth_config) {
+    proxy_auth_config_ = proxy_auth_config;
+  }
+
   virtual HRESULT SendRequest(BSTR url,
                               BSTR post_data,
                               BSTR request_headers,
@@ -118,6 +136,8 @@
 
  protected:
   CString user_agent_;
+  ProxyAuthConfig proxy_auth_config_;
+  ProxyConfig proxy_config_;
 
  private:
   HRESULT ProcessResponseHeaders(const CComVariant& headers,
@@ -125,6 +145,7 @@
   HRESULT ProcessResponseFile(const CComBSTR& cache_filename);
   bool CreateBrowserHttpRequest();
 
+  CComObjectStackEx<BindStatusCallback> bsc_;
   CComBSTR url_;
   CString filename_;
   const void* request_buffer_;          // Contains the request body for POST.
diff --git a/net/winhttp.cc b/net/winhttp.cc
index e2de416..cc361d5 100644
--- a/net/winhttp.cc
+++ b/net/winhttp.cc
@@ -15,17 +15,16 @@
 //
 // Internet-access utility functions via winhttp
 
-#include "omaha/net/http_client.h"
-
-#include <vector>                   // NOLINT
+#include "omaha/net/winhttp.h"
+#include <vector>
 #include "base/basictypes.h"
 #include "base/scoped_ptr.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/string.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/common/utils.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/string.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/utils.h"
 #include "omaha/net/winhttp_vtable.h"
 
 namespace omaha {
@@ -72,6 +71,7 @@
                        uint32 access_type,
                        const TCHAR* proxy_name,
                        const TCHAR* proxy_bypass,
+                       DWORD flags,
                        HINTERNET* session_handle);
   virtual HRESULT OpenRequest(HINTERNET connection_handle,
                               const TCHAR* verb,
@@ -107,7 +107,8 @@
                               DWORD headers_length,
                               const void* optional_data,
                               DWORD optional_data_length,
-                              DWORD content_length);
+                              DWORD content_length,
+                              DWORD_PTR context);
   virtual HRESULT SetCredentials(HINTERNET request_handle,
                                  uint32 auth_targets,
                                  uint32 auth_scheme,
@@ -153,15 +154,14 @@
 }
 
 HRESULT WinHttp::Initialize() {
+  __mutexScope(WinHttp::lock_);
   if (is_initialized_) {
     return S_OK;
   }
-  __mutexBlock(WinHttp::lock_) {
-    if (!winhttp_.Load()) {
-      HRESULT hr = HRESULTFromLastError();
-      NET_LOG(LEVEL_ERROR, (_T("[failed to load winhttp][0x%08x]"), hr));
-      return hr;
-    }
+  if (!winhttp_.Load()) {
+    HRESULT hr = HRESULTFromLastError();
+    NET_LOG(LEVEL_ERROR, (_T("[failed to load winhttp][0x%08x]"), hr));
+    return hr;
   }
   is_initialized_ = true;
   return S_OK;
@@ -171,30 +171,16 @@
                       uint32 access_type,
                       const TCHAR* proxy_name,
                       const TCHAR* proxy_bypass,
+                      DWORD flags,
                       HINTERNET* session_handle) {
   *session_handle = winhttp_.WinHttpOpen(user_agent,
                                          access_type,
                                          proxy_name,
                                          proxy_bypass,
-                                         0);   // Synchronous mode always.
+                                         flags);
   return *session_handle ? S_OK : HRESULTFromLastError();
 }
 
-// Quoting from the MSDN documentation:
-//    "Operations on a WinHTTP request handle should be synchronized.
-//    For example, an application should avoid closing a request handle on one
-//    thread while another thread is sending or receiving a request.
-//    To terminate an asynchronous request, close the request handle during
-//    a callback notification. To terminate a synchronous request, close the
-//    handle when the previous WinHTTP call returns."
-// Synchronizing on the request handle adds quite a bit of complexity to the
-// code. Blocking WinHTTP calls can block for quite a while: by default the
-// connect time out is 60 seconds, the send or receive timeout is 30 seconds,
-// and the receive response timeout is 90 seconds. Waiting as much to
-// cancel a request in progress is not acceptable. We noticed that closing
-// the request handle from a different thread makes the blocking WinHTTP
-// thread return right away. We are not fixing this for now, as the benefit
-// seems to be minimal while increasing the complexity of the code.
 HRESULT WinHttp::Close(HINTERNET handle) {
   ASSERT1(handle);
   return winhttp_.WinHttpCloseHandle(handle) ? S_OK : HRESULTFromLastError();
@@ -208,9 +194,9 @@
   ASSERT1(port <= INTERNET_MAX_PORT_NUMBER_VALUE);
 
   *connection_handle = winhttp_.WinHttpConnect(session_handle,
-                                              server,
-                                              static_cast<INTERNET_PORT>(port),
-                                              0);
+                                               server,
+                                               static_cast<INTERNET_PORT>(port),
+                                               0);
   return *connection_handle ? S_OK : HRESULTFromLastError();
 }
 
@@ -237,7 +223,8 @@
                              DWORD headers_length,
                              const void* optional_data,
                              DWORD optional_data_length,
-                             DWORD content_length) {
+                             DWORD content_length,
+                             DWORD_PTR context) {
   bool res = !!winhttp_.WinHttpSendRequest(
                             request_handle,
                             headers,
@@ -245,7 +232,7 @@
                             const_cast<void*>(optional_data),
                             optional_data_length,
                             content_length,
-                            reinterpret_cast<DWORD_PTR>(this));
+                            context);
   return res ? S_OK : HRESULTFromLastError();
 }
 
@@ -256,12 +243,7 @@
 
 HRESULT WinHttp::QueryDataAvailable(HINTERNET request_handle,
                                     DWORD* num_bytes) {
-  ASSERT1(num_bytes);
-
-  DWORD bytes_available = 0;
-  bool res = !!winhttp_.WinHttpQueryDataAvailable(request_handle,
-                                                  &bytes_available);
-  *num_bytes = bytes_available;
+  bool res = !!winhttp_.WinHttpQueryDataAvailable(request_handle, num_bytes);
   return res ? S_OK : HRESULTFromLastError();
 }
 
@@ -279,22 +261,16 @@
 }
 
 HRESULT WinHttp::ReadData(HINTERNET request_handle,
-                         void* buffer,
-                         DWORD buffer_length,
-                         DWORD* bytes_read) {
+                          void* buffer,
+                          DWORD buffer_length,
+                          DWORD* bytes_read) {
   ASSERT1(buffer);
-  ASSERT1(bytes_read);
 
-  DWORD bytes_read_local = 0;
   bool res = !!winhttp_.WinHttpReadData(request_handle,
                                         buffer,
                                         buffer_length,
-                                        &bytes_read_local);
-  if (!res) {
-    return HRESULTFromLastError();
-  }
-  *bytes_read = bytes_read_local;
-  return S_OK;
+                                        bytes_read);
+  return res ? S_OK : HRESULTFromLastError();
 }
 
 HRESULT WinHttp::WriteData(HINTERNET request_handle,
@@ -302,14 +278,11 @@
                            DWORD bytes_to_write,
                            DWORD* bytes_written) {
   ASSERT1(buffer);
-  ASSERT1(bytes_written);
 
-  DWORD bytes_written_local = 0;
   bool res = !!winhttp_.WinHttpWriteData(request_handle,
                                          buffer,
                                          bytes_to_write,
-                                         &bytes_written_local);
-  *bytes_written = bytes_written_local;
+                                         bytes_written);
   return res ? S_OK : HRESULTFromLastError();
 }
 
diff --git a/net/winhttp.h b/net/winhttp.h
new file mode 100644
index 0000000..f961ed4
--- /dev/null
+++ b/net/winhttp.h
@@ -0,0 +1,21 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_NET_WINHTTP_H_
+#define OMAHA_NET_WINHTTP_H_
+
+#include "omaha/net/http_client.h"
+
+#endif  // OMAHA_NET_WINHTTP_H_
diff --git a/net/winhttp_adapter.cc b/net/winhttp_adapter.cc
new file mode 100644
index 0000000..512382e
--- /dev/null
+++ b/net/winhttp_adapter.cc
@@ -0,0 +1,492 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/net/winhttp_adapter.h"
+#include <memory>
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+
+namespace omaha {
+
+WinHttpAdapter::WinHttpAdapter()
+    : connection_handle_(NULL),
+      request_handle_(NULL),
+      async_call_type_(0),
+      async_call_is_error_(0),
+      async_bytes_available_(0),
+      async_bytes_read_(0) {
+  memset(&async_call_result_, 0, sizeof(async_call_result_));
+}
+
+HRESULT WinHttpAdapter::Initialize() {
+  __mutexScope(lock_);
+
+  http_client_.reset(CreateHttpClient());
+
+  HRESULT hr = http_client_->Initialize();
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  reset(async_completion_event_, ::CreateEvent(NULL, true, false, NULL));
+
+  return async_completion_event_ ? S_OK : HRESULTFromLastError();
+}
+
+void WinHttpAdapter::CloseHandles() {
+  __mutexScope(lock_);
+
+  if (request_handle_) {
+    VERIFY1(SUCCEEDED(http_client_->Close(request_handle_)));
+    request_handle_ = NULL;
+  }
+  if (connection_handle_) {
+    VERIFY1(SUCCEEDED(http_client_->Close(connection_handle_)));
+    connection_handle_ = NULL;
+  }
+}
+
+HRESULT WinHttpAdapter::Connect(HINTERNET session_handle,
+                                const TCHAR* server,
+                                int port) {
+  __mutexScope(lock_);
+
+  HRESULT hr = http_client_->Connect(session_handle,
+                                     server,
+                                     port,
+                                     &connection_handle_);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  HttpClient::StatusCallback old_callback =
+      http_client_->SetStatusCallback(connection_handle_,
+                                      &WinHttpAdapter::WinHttpStatusCallback,
+                                      WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS);
+  ASSERT1(old_callback == NULL || old_callback == kInvalidStatusCallback);
+
+  return http_client_->SetOptionInt(connection_handle_,
+                                    WINHTTP_OPTION_CONTEXT_VALUE,
+                                    reinterpret_cast<int>(this));
+}
+
+HRESULT WinHttpAdapter::OpenRequest(const TCHAR* verb,
+                                    const TCHAR* uri,
+                                    const TCHAR* version,
+                                    const TCHAR* referrer,
+                                    const TCHAR** accept_types,
+                                    uint32 flags) {
+  __mutexScope(lock_);
+
+  return http_client_->OpenRequest(connection_handle_,
+                                   verb,
+                                   uri,
+                                   version,
+                                   referrer,
+                                   accept_types,
+                                   flags,
+                                   &request_handle_);
+}
+
+HRESULT WinHttpAdapter::AddRequestHeaders(const TCHAR* headers,
+                                          int length,
+                                          uint32 modifiers) {
+  __mutexScope(lock_);
+
+  return http_client_->AddRequestHeaders(request_handle_,
+                                         headers,
+                                         length,
+                                         modifiers);
+}
+
+HRESULT WinHttpAdapter::QueryAuthSchemes(uint32* supported_schemes,
+                                         uint32* first_scheme,
+                                         uint32* auth_target) {
+  __mutexScope(lock_);
+
+  return http_client_->QueryAuthSchemes(request_handle_,
+                                        supported_schemes,
+                                        first_scheme,
+                                        auth_target);
+}
+
+HRESULT WinHttpAdapter::QueryRequestHeadersInt(uint32 info_level,
+                                               const TCHAR* name,
+                                               int* value,
+                                               DWORD* index) {
+  __mutexScope(lock_);
+
+  return http_client_->QueryHeadersInt(request_handle_,
+                                       info_level,
+                                       name,
+                                       value,
+                                       index);
+}
+
+HRESULT WinHttpAdapter::QueryRequestHeadersString(uint32 info_level,
+                                                  const TCHAR* name,
+                                                  CString* value,
+                                                  DWORD* index) {
+  __mutexScope(lock_);
+
+  return http_client_->QueryHeadersString(request_handle_,
+                                          info_level,
+                                          name,
+                                          value,
+                                          index);
+}
+
+HRESULT WinHttpAdapter::SetCredentials(uint32 auth_targets,
+                                       uint32 auth_scheme,
+                                       const TCHAR* user_name,
+                                       const TCHAR* password) {
+  __mutexScope(lock_);
+
+  return http_client_->SetCredentials(request_handle_,
+                                      auth_targets,
+                                      auth_scheme,
+                                      user_name,
+                                      password);
+}
+
+HRESULT WinHttpAdapter::SendRequest(const TCHAR* headers,
+                                    DWORD headers_length,
+                                    const void* optional_data,
+                                    DWORD optional_data_length,
+                                    DWORD content_length) {
+  __mutexScope(lock_);
+
+  HRESULT hr = AsyncCallBegin(API_SEND_REQUEST);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  const DWORD_PTR context = reinterpret_cast<DWORD_PTR>(this);
+  hr = http_client_->SendRequest(request_handle_,
+                                 headers,
+                                 headers_length,
+                                 optional_data,
+                                 optional_data_length,
+                                 content_length,
+                                 context);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return AsyncCallEnd(API_SEND_REQUEST);
+}
+
+HRESULT WinHttpAdapter::ReceiveResponse() {
+  __mutexScope(lock_);
+
+  HRESULT hr = AsyncCallBegin(API_RECEIVE_RESPONSE);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = http_client_->ReceiveResponse(request_handle_);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return AsyncCallEnd(API_RECEIVE_RESPONSE);
+}
+
+HRESULT WinHttpAdapter::QueryDataAvailable(DWORD* num_bytes) {
+  __mutexScope(lock_);
+
+  HRESULT hr = AsyncCallBegin(API_QUERY_DATA_AVAILABLE);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  async_bytes_available_ = 0;
+
+  hr = http_client_->QueryDataAvailable(request_handle_, NULL);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr =  AsyncCallEnd(API_QUERY_DATA_AVAILABLE);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  *num_bytes = async_bytes_available_;
+
+  return S_OK;
+}
+
+HRESULT WinHttpAdapter::ReadData(void* buffer,
+                                 DWORD buffer_length,
+                                 DWORD* bytes_read) {
+  __mutexScope(lock_);
+
+  HRESULT hr = AsyncCallBegin(API_READ_DATA);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  async_bytes_read_ = 0;
+
+  hr = http_client_->ReadData(request_handle_,
+                              buffer,
+                              buffer_length,
+                              NULL);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = AsyncCallEnd(API_READ_DATA);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  *bytes_read = async_bytes_read_;
+
+  return S_OK;
+}
+
+HRESULT WinHttpAdapter::SetRequestOptionInt(uint32 option, int value) {
+  __mutexScope(lock_);
+
+  return http_client_->SetOptionInt(request_handle_, option, value);
+}
+
+HRESULT WinHttpAdapter::SetRequestOption(uint32 option,
+                                         const void* buffer,
+                                         DWORD buffer_length) {
+  __mutexScope(lock_);
+
+  ASSERT1(buffer && buffer_length);
+
+  return http_client_->SetOption(request_handle_,
+                                 option,
+                                 buffer,
+                                 buffer_length);
+}
+
+HRESULT WinHttpAdapter::AsyncCallBegin(DWORD async_call_type) {
+  async_call_type_  = async_call_type;
+  async_call_is_error_ = false;
+
+  memset(&async_call_result_, 0, sizeof(async_call_result_));
+
+  return ::ResetEvent(get(async_completion_event_)) ? S_OK :
+                                                      HRESULTFromLastError();
+}
+
+// Waits for the WinHttp notification to arrive and handles the result of
+// the asynchronous call.
+HRESULT WinHttpAdapter::AsyncCallEnd(DWORD async_call_type) {
+  UNREFERENCED_PARAMETER(async_call_type);
+
+  const DWORD result = ::WaitForSingleObject(get(async_completion_event_),
+                                             INFINITE);
+  ASSERT1(result == WAIT_OBJECT_0);
+  switch (result) {
+    case WAIT_OBJECT_0:
+      break;
+    case WAIT_TIMEOUT:
+      return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
+    case WAIT_FAILED:
+      return HRESULTFromLastError();
+  }
+
+  if (async_call_is_error_) {
+    ASSERT1(async_call_result_.dwResult == async_call_type);
+    ASSERT1(async_call_result_.dwError != ERROR_SUCCESS);
+    return HRESULT_FROM_WIN32(async_call_result_.dwError);
+  }
+
+  return S_OK;
+}
+
+void WinHttpAdapter::StatusCallback(HINTERNET handle,
+                                    uint32 status,
+                                    void* info,
+                                    uint32 info_len) {
+  UNREFERENCED_PARAMETER(handle);
+
+  switch (status) {
+    case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
+      ASSERT1(async_call_type_ == API_QUERY_DATA_AVAILABLE);
+
+      ASSERT1(info_len == sizeof(async_bytes_available_));
+      ASSERT1(info);
+      async_bytes_available_ = *reinterpret_cast<DWORD*>(info);
+      break;
+
+    case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
+      ASSERT1(async_call_type_ == API_RECEIVE_RESPONSE);
+      break;
+
+    case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
+      ASSERT1(async_call_type_ == API_READ_DATA);
+
+      ASSERT1(info);
+      async_bytes_read_ = info_len;
+      break;
+
+    case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
+      ASSERT1(async_call_type_ == API_SEND_REQUEST);
+      break;
+
+    case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
+      ASSERT1(async_call_type_ == API_WRITE_DATA);
+      break;
+
+    case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
+      ASSERT1(async_call_type_ == API_QUERY_DATA_AVAILABLE  ||
+              async_call_type_ == API_RECEIVE_RESPONSE      ||
+              async_call_type_ == API_READ_DATA             ||
+              async_call_type_ == API_SEND_REQUEST          ||
+              async_call_type_ == API_WRITE_DATA);
+
+      ASSERT1(info_len == sizeof(async_call_result_));
+      ASSERT1(info);
+      async_call_result_ = *reinterpret_cast<WINHTTP_ASYNC_RESULT*>(info);
+      async_call_is_error_ = true;
+      break;
+
+    default:
+      break;
+  }
+
+  if (status == WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE        ||
+      status == WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE     ||
+      status == WINHTTP_CALLBACK_STATUS_READ_COMPLETE         ||
+      status == WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE  ||
+      status == WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE        ||
+      status == WINHTTP_CALLBACK_STATUS_REQUEST_ERROR) {
+    ASSERT1(!IsHandleSignaled(get(async_completion_event_)));
+    VERIFY1(::SetEvent(get(async_completion_event_)));
+  }
+}
+
+void __stdcall WinHttpAdapter::WinHttpStatusCallback(HINTERNET handle,
+                                                     uint32 context,
+                                                     uint32 status,
+                                                     void* info,
+                                                     uint32 info_len) {
+  ASSERT1(handle);
+  ASSERT1(context);
+  WinHttpAdapter* http_adapter = reinterpret_cast<WinHttpAdapter*>(context);
+
+  CString status_string;
+  CString info_string;
+  switch (status) {
+    case WINHTTP_CALLBACK_STATUS_HANDLE_CREATED:
+      status_string = _T("handle created");
+      break;
+      case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:
+      status_string = _T("handle closing");
+      break;
+    case WINHTTP_CALLBACK_STATUS_RESOLVING_NAME:
+      status_string = _T("resolving");
+      info_string.SetString(static_cast<TCHAR*>(info), info_len);  // host name
+      http_adapter->server_name_ = info_string;
+      break;
+    case WINHTTP_CALLBACK_STATUS_NAME_RESOLVED:
+      status_string = _T("resolved");
+      info_string.SetString(static_cast<TCHAR*>(info), info_len);  // host ip
+      http_adapter->server_ip_ = info_string;
+      break;
+    case WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER:
+      status_string = _T("connecting");
+      info_string.SetString(static_cast<TCHAR*>(info), info_len);  // host ip
+
+      // Server name resolving may be skipped in some cases. So populate server
+      // name and IP if not yet done.
+      if (http_adapter->server_name_.IsEmpty()) {
+        http_adapter->server_name_= info_string;
+      }
+      if (http_adapter->server_ip_.IsEmpty()) {
+        http_adapter->server_ip_ = info_string;
+      }
+      break;
+    case WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER:
+      status_string = _T("connected");
+      info_string.SetString(static_cast<TCHAR*>(info), info_len);  // host ip
+      break;
+    case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
+      status_string = _T("sending");
+      break;
+    case WINHTTP_CALLBACK_STATUS_REQUEST_SENT:
+      status_string = _T("sent");
+      break;
+    case WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE:
+      status_string = _T("receiving");
+      break;
+    case WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED:
+      status_string = _T("received");
+      break;
+    case WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION:
+      status_string = _T("connection closing");
+      break;
+    case WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED:
+      status_string = _T("connection closed");
+      break;
+    case WINHTTP_CALLBACK_STATUS_REDIRECT:
+      status_string = _T("redirect");
+      info_string.SetString(static_cast<TCHAR*>(info), info_len);  // url
+      break;
+    case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
+      status_string = _T("data available");
+      break;
+    case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
+      status_string = _T("headers available");
+      break;
+    case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
+      status_string = _T("read complete");
+      break;
+    case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
+      status_string = _T("send request complete");
+      break;
+    case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
+      status_string = _T("write complete");
+      break;
+    case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
+      status_string = _T("request error");
+      break;
+    case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
+      status_string = _T("https failure");
+      ASSERT1(info);
+      ASSERT1(info_len == sizeof(DWORD));
+      info_string.Format(_T("0x%x"), *static_cast<DWORD*>(info));
+      break;
+    default:
+      break;
+  }
+
+  CString log_line;
+  log_line.AppendFormat(_T("[WinHttp status callback][handle=0x%08x]"), handle);
+  if (!status_string.IsEmpty()) {
+    log_line.AppendFormat(_T("[%s]"), status_string);
+  } else {
+    log_line.AppendFormat(_T("[0x%08x]"), status);
+  }
+  if (!info_string.IsEmpty()) {
+    log_line.AppendFormat(_T("[%s]"), info_string);
+  }
+  NET_LOG(L3, (_T("%s"), log_line));
+
+  http_adapter->StatusCallback(handle, status, info, info_len);
+}
+
+
+}  // namespace omaha
+
diff --git a/net/winhttp_adapter.h b/net/winhttp_adapter.h
new file mode 100644
index 0000000..4f74f00
--- /dev/null
+++ b/net/winhttp_adapter.h
@@ -0,0 +1,152 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_NET_WINHTTP_ADAPTER_H_
+#define OMAHA_NET_WINHTTP_ADAPTER_H_
+
+#include <windows.h>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/net/winhttp.h"
+
+namespace omaha {
+
+// Provides a sync-async adapter between the caller and the asynchronous
+// WinHttp client. Solves the issue of reliably canceling of WinHttp calls by
+// closing the handles and avoding the race condition between handle closing
+// and the incoming WinHttp call.
+// The class manages the connection and the request handles. It registers a
+// callback for all WinHttp status notifications. Once an asynchrous WinHttp
+// call is made, the code blocks waiting for the corresponding notification
+// to arrive, handle the completion result, and then return to the caller.
+// WinHttp is guaranteed to send a notification callback for all asynchronous
+// request calls that have succeeded.
+// TODO(omaha): consider eliminating this class and implementing the same
+// functionality in the WinHttp class. Most likely, another class is needed
+// to manage the WinHttp session handle.
+class WinHttpAdapter {
+ public:
+  WinHttpAdapter();
+
+  HRESULT Initialize();
+
+  HRESULT Connect(HINTERNET session_handle, const TCHAR* server, int port);
+
+  HRESULT OpenRequest(const TCHAR* verb,
+                      const TCHAR* uri,
+                      const TCHAR* version,
+                      const TCHAR* referrer,
+                      const TCHAR** accept_types,
+                      uint32 flags);
+
+  HRESULT AddRequestHeaders(const TCHAR* headers,
+                            int length,
+                            uint32 modifiers);
+
+  HRESULT SendRequest(const TCHAR* headers,
+                      DWORD headers_length,
+                      const void* optional_data,
+                      DWORD optional_data_length,
+                      DWORD content_length);
+
+  HRESULT SetCredentials(uint32 auth_targets,
+                         uint32 auth_scheme,
+                         const TCHAR* user_name,
+                         const TCHAR* password);
+
+  HRESULT ReceiveResponse();
+
+  HRESULT QueryAuthSchemes(uint32* supported_schemes,
+                           uint32* first_scheme,
+                           uint32* auth_target);
+
+  HRESULT QueryRequestHeadersInt(uint32 info_level,
+                                 const TCHAR* name,
+                                 int* value,
+                                 DWORD* index);
+
+  HRESULT QueryRequestHeadersString(uint32 info_level,
+                                    const TCHAR* name,
+                                    CString* value,
+                                    DWORD* index);
+
+  HRESULT QueryDataAvailable(DWORD* num_bytes);
+
+  HRESULT ReadData(void* buffer, DWORD buffer_length, DWORD* bytes_read);
+
+  HRESULT SetRequestOptionInt(uint32 option, int value);
+
+  HRESULT SetRequestOption(uint32 option,
+                           const void* buffer,
+                           DWORD buffer_length);
+
+  void CloseHandles();
+
+  HRESULT CrackUrl(const TCHAR* url,
+                   uint32 flags,
+                   CString* scheme,
+                   CString* server,
+                   int* port,
+                   CString* url_path,
+                   CString* extra_info) {
+    return http_client_->CrackUrl(
+        url, flags, scheme, server, port, url_path, extra_info);
+  }
+
+  CString server_name() const { return server_name_; }
+  CString server_ip() const { return server_ip_; }
+
+ private:
+
+  HRESULT AsyncCallBegin(DWORD async_call_type);
+  HRESULT AsyncCallEnd(DWORD async_call_type);
+
+  void StatusCallback(HINTERNET handle,
+                      uint32 status,
+                      void* info,
+                      uint32 info_len);
+
+  static void __stdcall WinHttpStatusCallback(HINTERNET handle,
+                                              uint32 context,
+                                              uint32 status,
+                                              void* info,
+                                              uint32 info_len);
+
+  scoped_ptr<HttpClient> http_client_;
+
+  HINTERNET              connection_handle_;
+  HINTERNET              request_handle_;
+
+  CString                server_name_;
+  CString                server_ip_;
+
+  DWORD                  async_call_type_;
+  bool                   async_call_is_error_;
+  WINHTTP_ASYNC_RESULT   async_call_result_;
+  DWORD                  async_bytes_available_;
+  DWORD                  async_bytes_read_;
+  scoped_event           async_completion_event_;
+
+  LLock                  lock_;
+
+  DISALLOW_COPY_AND_ASSIGN(WinHttpAdapter);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_NET_WINHTTP_ADAPTER_H_
+
diff --git a/net/winhttp_adapter_unittest.cc b/net/winhttp_adapter_unittest.cc
new file mode 100644
index 0000000..7bd6870
--- /dev/null
+++ b/net/winhttp_adapter_unittest.cc
@@ -0,0 +1,65 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <windows.h>
+#include "base/scoped_ptr.h"
+#include "omaha/net/winhttp_adapter.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+// Tests null WinHttp handles are allowed for WinHttp calls. WinHttp may or
+// may have not been initialized up to this point. This affects the return
+// value of some of the calls below.
+TEST(WinHttpAdapter, NullHandles) {
+  WinHttpAdapter winhttp_adapter;
+  EXPECT_HRESULT_SUCCEEDED(winhttp_adapter.Initialize());
+
+  // Null session handle.
+  HRESULT hr = winhttp_adapter.Connect(NULL, _T("127.0.0.1"), 80);
+  EXPECT_TRUE(hr == HRESULT_FROM_WIN32(ERROR_WINHTTP_NOT_INITIALIZED) ||
+              hr == HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE));
+
+  // Opens a session, connects, closes the connection handle, and then
+  // tries to open a request for the null connection.
+  scoped_ptr<HttpClient> http_client(CreateHttpClient());
+  EXPECT_HRESULT_SUCCEEDED(http_client->Initialize());
+
+  HINTERNET session_handle = NULL;
+  EXPECT_HRESULT_SUCCEEDED(http_client->Open(NULL,
+                                             WINHTTP_ACCESS_TYPE_NO_PROXY,
+                                             WINHTTP_NO_PROXY_NAME,
+                                             WINHTTP_NO_PROXY_BYPASS,
+                                             0,  // Synchronous mode.
+                                             &session_handle));
+  EXPECT_NE(static_cast<HINTERNET>(NULL), session_handle);
+  EXPECT_HRESULT_SUCCEEDED(winhttp_adapter.Connect(session_handle,
+                                                   _T("127.0.0.1"),
+                                                   80));
+  winhttp_adapter.CloseHandles();
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE),
+            winhttp_adapter.OpenRequest(NULL,  // verb
+                                        NULL,  // uri,
+                                        NULL,  // version
+                                        NULL,  // referrer
+                                        NULL,  // accept_types
+                                        0));   // flags
+
+  // Null request handle.
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE),
+            winhttp_adapter.QueryDataAvailable(NULL));
+}
+
+}  // namespace omaha
diff --git a/net/winhttp_vtable.h b/net/winhttp_vtable.h
index 7799f75..2249e8c 100644
--- a/net/winhttp_vtable.h
+++ b/net/winhttp_vtable.h
@@ -28,7 +28,7 @@
 
 #include <windows.h>
 #include <winhttp.h>
-#include "omaha/common/debug.h"
+#include "omaha/base/debug.h"
 
 namespace omaha {
 
diff --git a/net/wininet.cc b/net/wininet.cc
index 58479f8..7182ab3 100644
--- a/net/wininet.cc
+++ b/net/wininet.cc
@@ -21,13 +21,13 @@
 #include "omaha/net/http_client.h"
 
 #include "base/scoped_ptr.h"
-#include "omaha/common/atl_regexp.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/string.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/common/utils.h"
+#include "omaha/base/atl_regexp.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/string.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/utils.h"
 
 namespace omaha {
 
diff --git a/official/README.txt b/official/README.txt
new file mode 100644
index 0000000..125f0e8
--- /dev/null
+++ b/official/README.txt
@@ -0,0 +1,4 @@
+The set of files under omaha\official contains the differences between the
+public Omaha release and the official Google Update.  If you are interested in
+building your own Google Update executable, simply copy the contents of this
+directory tree over the one below it.
diff --git a/official/base/const_object_names.h b/official/base/const_object_names.h
new file mode 100644
index 0000000..5afde3c
--- /dev/null
+++ b/official/base/const_object_names.h
@@ -0,0 +1,131 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Kernel object names.
+
+#ifndef OMAHA_BASE_CONST_OBJECT_NAMES_H_
+#define OMAHA_BASE_CONST_OBJECT_NAMES_H_
+
+#include <tchar.h>
+#include "omaha/base/constants.h"
+
+namespace omaha {
+
+// The prefix to use for global names in the win32 API's.
+const TCHAR* const kGlobalPrefix = _T("Global\\G");
+
+const TCHAR kCrashPipeNamePrefix[] =
+    _T("\\\\.\\pipe\\") SHORT_COMPANY_NAME _T("CrashServices");
+
+// Ensures that only one instance of machine or user Omaha is trying to setup at
+// a time.
+const TCHAR* const kSetupMutex = _T("{A9A86B93-B54E-4570-BE89-42418507707B}");
+
+// TODO(omaha3): Update this comment.
+// Signals the process to exit. Currently the core and the worker listen to
+// this event.
+// TODO(omaha): Consider making all our processes listen to it. Maybe not the
+// service, since the SCM controls the life time of the service.
+const TCHAR* const kShutdownEvent =
+    _T("{A0C1F415-D2CE-4ddc-9B48-14E56FD55162}");
+
+// This is for Omaha2 backwards compatibility.
+// The installed Omaha3 handoff process sets an event to tell an Omaha2 setup
+// worker running from the temp directory that a UI has been displayed so that
+// the Omaha2 worker will not display a second UI on error. The event's name is
+// passed in this environment variable name by the Omaha2 worker.
+const TCHAR* const kLegacyUiDisplayedEventEnvironmentVariableName =
+    _T("GOOGLE_UPDATE_UI_DISPLAYED_EVENT_NAME");
+
+// Ensures the Core only runs one instance per machine and one instance per
+// each user session.
+const TCHAR* const kCoreSingleInstance =
+    _T("{B5665124-2B19-40e2-A7BC-B44321E72C4B}");
+
+// Ensures the Crash Handler only runs one instance per machine and one
+// instance per each user session.
+const TCHAR* const kCrashHandlerSingleInstance =
+    _T("{C4F406E5-F024-4e3f-89A7-D5AB7663C3CD}");
+
+// Ensures the /ua process only runs one instance per machine and one
+// instance per each user session.
+const TCHAR* const kUpdateAppsSingleInstance =
+    _T("{D0BB2EF1-C183-4cdb-B218-040922092869}");
+
+// Ensures only one installer for an app is running in a session.
+// The %s is replaced with the application ID.
+const TCHAR* const kInstallAppSingleInstance =
+    _T("%s-{F707E94F-D66B-4525-AD84-B1DA87D6A971}");
+
+// Ensures the GoogleUpdate3 server only runs one instance per machine and one
+// instance per each user session.
+const TCHAR* const kGoogleUpdate3SingleInstance =
+    _T("{6885AE8E-C070-458d-9711-37B9BEAB65F6}");
+
+// TODO(omaha): Delete Job Object code.
+
+// Base name of job object for Setup phase 1 processes except self updates.
+// These may not be running as Local System for machine installs like
+// self-updates do.
+const TCHAR* const kSetupPhase1NonSelfUpdateJobObject =
+    _T("{5A913EF1-4160-48bc-B688-4D67EAEB698A}");
+
+// Base name of job object for interactive install processes except /install.
+const TCHAR* const kAppInstallJobObject =
+    _T("{8AD051DB-4FE6-458b-B103-7DCC78D56013}");
+
+// Base name of job object for silent processes that are okay to kill.
+const TCHAR* const kSilentJobObject =
+    _T("{A2300FD6-CBED-48a6-A3CB-B35C38A42F8E}");
+
+// Base name of job object for silent processes that should not be killed.
+const TCHAR* const kSilentDoNotKillJobObject =
+    _T("{D33A8A53-F57D-4fd9-A32D-238FD69B4BC4}");
+
+// The global lock to ensure that a single app is being installed for this
+// user/machine at a given time.
+const TCHAR* const kInstallManagerSerializer =
+    _T("{0A175FBE-AEEC-4fea-855A-2AA549A88846}");
+
+// Serializes access to metrics stores, machine and user, respectively.
+const TCHAR* const kMetricsSerializer =
+    _T("{C68009EA-1163-4498-8E93-D5C4E317D8CE}");
+
+// Serializes access to the global network configuration, such as the CUP keys.
+const TCHAR* const kNetworkConfigLock =
+    _T("{0E900C7B-04B0-47f9-81B0-F8D94F2DF01B}");
+
+// Serializes access to the registry for application state.
+const TCHAR* const kRegistryAccessMutex =
+    _T("{66CC0160-ABB3-4066-AE47-1CA6AD5065C8}");
+
+// Serializes opt user id generation.
+const TCHAR* const kOptUserIdLock =
+    _T("{D19BAF17-7C87-467E-8D63-6C4B1C836373}");
+
+// The name of the shared memory objects containing the serialized COM
+// interface pointers exposed by the machine core.
+// TODO(omaha): Rename these constants to remove "GoogleUpdate".
+// TODO(omaha): Consider following the kGlobalPrefix convention with the 'G'
+// for the new shared Omaha 3 name  and building this from the same #define as
+// kGlobalPrefix.
+const TCHAR* const kGoogleUpdate3SharedMemoryName =
+    _T("Global\\") APP_NAME_IDENTIFIER _T("3");
+const TCHAR* const kGoogleUpdateCoreSharedMemoryName =
+    _T("Global\\") APP_NAME_IDENTIFIER _T("Core");
+
+}  // namespace omaha
+
+#endif  // OMAHA_BASE_CONST_OBJECT_NAMES_H_
diff --git a/official/common/const_goopdate.h b/official/common/const_goopdate.h
new file mode 100644
index 0000000..d29c90b
--- /dev/null
+++ b/official/common/const_goopdate.h
@@ -0,0 +1,294 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Goopdate constants - Reduces dependencies on goopdate.h for names that
+// are needed outside Goopdate.
+//
+// TODO(omaha): it appears that the string constants below are not
+// optimized out the program image even if not used. Figure out why they still
+// show up in the tiny shell in optimized builds.
+
+#ifndef OMAHA_COMMON_CONST_GOOPDATE_H_
+#define OMAHA_COMMON_CONST_GOOPDATE_H_
+
+#include <tchar.h>
+#include "omaha/base/constants.h"
+
+// TODO(omaha3): Many of these values are specific to the COM server and a few
+// may apply to the client. Move most of them to the goopdate directory.
+
+namespace omaha {
+
+enum ActiveStates {
+  ACTIVE_NOTRUN = 0,
+  ACTIVE_RUN,
+  ACTIVE_UNKNOWN
+};
+
+// Specifies what Omaha should do on successful installation.
+enum SuccessfulInstallAction {
+  SUCCESS_ACTION_DEFAULT = 0,
+  SUCCESS_ACTION_EXIT_SILENTLY,
+  SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD,
+};
+
+// Specifies the type of result the app installer returned.
+enum InstallerResultType {
+  INSTALLER_RESULT_UNKNOWN,
+  INSTALLER_RESULT_SUCCESS,
+  INSTALLER_RESULT_ERROR_MSI,
+  INSTALLER_RESULT_ERROR_SYSTEM,
+  INSTALLER_RESULT_ERROR_OTHER,
+};
+
+// The enumeration of the events. Some of the events are used in IPC,
+// these are named events, the rest are not named. Of the named events there
+// are two types the global and the local events.
+// Global Events:
+// The events which are global are indicated with the Global suffix in the
+// enum. These events have the Global prefix attached to the event name and
+// are used for IPC across terminal server sessions, these events are used
+// as barriers, i.e. all waiting threads are release on these events.
+// Local Events:
+// The local counter parts of these events have the Local prefix attached
+// to their names, and dont have any suffix in the enum names. The local events
+// are used to release only one thread, this works as we know that there is
+// only one user goopdate process in a user session and one machine goopdate.
+// The local events also have the user sid added to the event name. This is to
+// work around a bug in win2K and on XP where in case of TS, the event names
+// will collide, inspite of the name changes.
+enum GoopdateEvents {
+  EVENT_INVALID = -1,
+  EVENT_KILL_MESSAGE_LOOP = 0,
+  EVENT_UPDATE_TIMER,
+  EVENT_NEW_MANIFEST,          // Used in IPC.
+  EVENT_QUIET_MODE,            // Used in IPC.
+  EVENT_LEGACY_QUIET_MODE,     // Only used to shut down pre-i18n goopdates.
+  EVENT_CODE_RED_TIMER,
+};
+
+// Represents the values that are used by the application to indicate its
+// requirement for admin.
+enum NeedsAdmin {
+  NEEDS_ADMIN_NO = 0,   // The application will install per user.
+  NEEDS_ADMIN_YES,      // The application will install machine-wide.
+  NEEDS_ADMIN_PREFERS,  // The application will install machine-wide if
+                        // permissions allow, else will install per-user.
+};
+
+// Using extern or intern linkage for these strings yields the same code size
+// for the executable DLL.
+
+// The string representation of the NeedsAdmin enum as specified in the tag or
+// command line. This is eventually parsed into the needs_admin member of
+// CommandLineAppArgs.
+const TCHAR* const kNeedsAdminNo = _T("&needsadmin=false");
+const TCHAR* const kNeedsAdminYes = _T("&needsadmin=true");
+const TCHAR* const kNeedsAdminPrefers = _T("&needsadmin=prefers");
+
+// Environment variable inherited by an installer child process that indicates
+// whether GoogleUpdate is running as user or machine.
+const TCHAR* const kEnvVariableIsMachine = APP_NAME_IDENTIFIER _T("IsMachine");
+
+// Registry values read from the Clients key for transmitting custom install
+// errors, messages, etc. On an update, the InstallerXXX values are renamed to
+// LastInstallerXXX values. The LastInstallerXXX values remain around until the
+// next update.
+const TCHAR* const kRegValueInstallerResult      = _T("InstallerResult");
+const TCHAR* const kRegValueInstallerError       = _T("InstallerError");
+const TCHAR* const kRegValueInstallerExtraCode1  = _T("InstallerExtraCode1");
+const TCHAR* const kRegValueInstallerResultUIString =
+    _T("InstallerResultUIString");
+const TCHAR* const kRegValueInstallerSuccessLaunchCmdLine =
+    _T("InstallerSuccessLaunchCmdLine");
+
+const TCHAR* const kRegValueLastInstallerResult =
+    _T("LastInstallerResult");
+const TCHAR* const kRegValueLastInstallerError =
+    _T("LastInstallerError");
+const TCHAR* const kRegValueLastInstallerExtraCode1 =
+    _T("LastInstallerExtraCode1");
+const TCHAR* const kRegValueLastInstallerResultUIString =
+    _T("LastInstallerResultUIString");
+const TCHAR* const kRegValueLastInstallerSuccessLaunchCmdLine =
+    _T("LastInstallerSuccessLaunchCmdLine");
+
+
+// Registry subkey in an app's Clients key that contains its commands.
+const TCHAR* const kCommandsRegKeyName       = _T("Commands");
+
+// Registry values read from the Clients commands key.
+const TCHAR* const kRegValueCommandLine      = _T("CommandLine");
+const TCHAR* const kRegValueSendsPings       = _T("SendsPings");
+const TCHAR* const kRegValueWebAccessible    = _T("WebAccessible");
+const TCHAR* const kRegValueReportingId      = _T("ReportingId");
+
+// Registry value in an app's Clients key that contains a registration update
+// hook CLSID.
+const TCHAR* const kRegValueUpdateHookClsid  = _T("RegistrationUpdateHook");
+
+// Registry values read from the Clients key and stored in the ClientState key.
+const TCHAR* const kRegValueLanguage         = _T("lang");
+const TCHAR* const kRegValueAppName          = _T("name");
+const TCHAR* const kRegValueProductVersion   = _T("pv");
+
+// Registry values stored in the ClientState key.
+const TCHAR* const kRegValueAdditionalParams = _T("ap");
+const TCHAR* const kRegValueBrandCode        = _T("brand");
+const TCHAR* const kRegValueBrowser          = _T("browser");
+const TCHAR* const kRegValueClientId         = _T("client");
+const TCHAR* const kRegValueDidRun           = _T("dr");
+const TCHAR* const kRegValueExperimentLabels = _T("experiment_labels");
+const TCHAR* const kRegValueInstallationId   = _T("iid");
+const TCHAR* const kRegValueOemInstall       = _T("oeminstall");
+const TCHAR* const kRegValueReferralId       = _T("referral");
+
+// This two registries hold client UTC timestamp of server's midnight of the day
+// that last active ping/roll call happened.
+const TCHAR* const kRegValueActivePingDayStartSec = _T("ActivePingDayStartSec");
+const TCHAR* const kRegValueRollCallDayStartSec   = _T("RollCallDayStartSec");
+
+// Registry values stored in the ClientState key related to Omaha's actions.
+// A "successful check" means "noupdate" received from the server or an update
+// was successfully applied.
+const TCHAR* const kRegValueInstallTimeSec          = _T("InstallTime");
+const TCHAR* const kRegValueLastSuccessfulCheckSec  = _T("LastCheckSuccess");
+const TCHAR* const kRegValueLastUpdateTimeSec       = _T("UpdateTime");
+
+// Registry values stored in the ClientState or ClientStateMedium keys.
+// Use accessor methods rather than reading them directly.
+const TCHAR* const kRegValueEulaAccepted     = _T("eulaaccepted");
+const TCHAR* const kRegValueUsageStats       = _T("usagestats");
+
+// Registry values stored in the ClientState key for Omaha's internal use.
+const TCHAR* const kRegValueTTToken               = _T("tttoken");
+const TCHAR* const kRegValueUpdateAvailableCount  = _T("UpdateAvailableCount");
+const TCHAR* const kRegValueUpdateAvailableSince  = _T("UpdateAvailableSince");
+
+// Registry values stored in the Update key.
+const TCHAR* const kRegValueDelayOmahaUninstall   = _T("DelayUninstall");
+const TCHAR* const kRegValueOmahaEulaAccepted     = _T("eulaaccepted");
+// TODO(omaha3): Consider renaming these if there is not a upgrade problem.
+// If we can't consider moving all "gupdate" values to the customization file.
+const TCHAR* const kRegValueServiceName           = _T("gupdate_service_name");
+const TCHAR* const kRegValueMediumServiceName     = _T("gupdatem_service_name");
+const TCHAR* const kRegValueTaskNameC             = _T("gupdate_task_name_c");
+const TCHAR* const kRegValueTaskNameUA            = _T("gupdate_task_name_ua");
+const TCHAR* const kRegValueLastChecked           = _T("LastChecked");
+const TCHAR* const kRegValueOemInstallTimeSec     = _T("OemInstallTime");
+const TCHAR* const kRegValueCacheSizeLimitMBytes  = _T("PackageCacheSizeLimit");
+const TCHAR* const kRegValueCacheLifeLimitDays    = _T("PackageCacheLifeLimit");
+const TCHAR* const kRegValueInstalledPath         = _T("path");
+const TCHAR* const kRegValueUserId                = _T("uid");
+const TCHAR* const kRegValueSelfUpdateExtraCode1  = _T("UpdateCode1");
+const TCHAR* const kRegValueSelfUpdateErrorCode   = _T("UpdateError");
+const TCHAR* const kRegValueSelfUpdateVersion     = _T("UpdateVersion");
+const TCHAR* const kRegValueInstalledVersion      = _T("version");
+
+// TODO(omaha3): Consider moving all "gupdate" values to the customization file.
+// Use a non-gupdate name for the new medium service.
+const TCHAR* const kServicePrefix               = _T("gupdate");
+const TCHAR* const kMediumServicePrefix         = _T("gupdatem");
+
+const TCHAR* const kScheduledTaskNameUserPrefix =
+    APP_NAME_IDENTIFIER _T("TaskUser");
+const TCHAR* const kScheduledTaskNameMachinePrefix =
+    APP_NAME_IDENTIFIER _T("TaskMachine");
+const TCHAR* const kScheduledTaskNameCoreSuffix = _T("Core");
+const TCHAR* const kScheduledTaskNameUASuffix   = _T("UA");
+
+const TCHAR* const kServiceFileName              = kOmahaShellFileName;
+const char*  const kGoopdateDllEntryAnsi         = "DllEntry";
+
+
+// Event Id's used for reporting in the event log.
+// Crash Report events.
+const int kCrashReportEventId        = 1;
+const int kCrashUploadEventId        = 2;
+
+// Update Check events.
+const int kUpdateCheckEventId        = 11;
+const int kUpdateEventId             = 12;
+const int kUninstallEventId          = 13;
+const int kWorkerStartEventId        = 14;
+const int kDownloadEventId           = 15;
+
+// Network Request events.
+const int kNetworkRequestEventId     = 20;
+
+// Maximum value the server can respond for elapsed_seconds attribute in
+// <daystart ...> element. The value is one day plus an hour ("fall back"
+// daylight savings).
+const int kMaxTimeSinceMidnightSec   = ((24 + 1) * 60 * 60);
+
+// Maximum time to keep the Installation ID. If the app was installed longer
+// than this time ago, the Installation ID will be deleted regardless of
+// whether the application has been run or not.
+const int kMaxLifeOfInstallationIDSec = (7 * 24 * 60 * 60);  // 7 days
+
+// Documented in the IDL for certain properties of ICurrentState.
+const int kCurrentStateProgressUnknown = -1;
+
+// COM ProgIDs.
+#define kProgIDUpdate3COMClassUser \
+    APP_NAME_IDENTIFIER _T(".Update3COMClassUser")
+#define kProgIDUpdate3COMClassService \
+    APP_NAME_IDENTIFIER _T(".Update3COMClassService")
+
+const TCHAR* const kProgIDOnDemandUser =
+    APP_NAME_IDENTIFIER _T(".OnDemandCOMClassUser");
+#define kProgIDOnDemandMachine \
+    APP_NAME_IDENTIFIER _T(".OnDemandCOMClassMachine")
+const TCHAR* const kProgIDOnDemandMachineFallback =
+    APP_NAME_IDENTIFIER _T(".OnDemandCOMClassMachineFallback");
+const TCHAR* const kProgIDOnDemandSvc =
+    APP_NAME_IDENTIFIER _T(".OnDemandCOMClassSvc");
+
+const TCHAR* const kProgIDUpdate3WebUser =
+    APP_NAME_IDENTIFIER _T(".Update3WebUser");
+#define kProgIDUpdate3WebMachine \
+    APP_NAME_IDENTIFIER _T(".Update3WebMachine")
+const TCHAR* const kProgIDUpdate3WebMachineFallback =
+    APP_NAME_IDENTIFIER _T(".Update3WebMachineFallback");
+const TCHAR* const kProgIDUpdate3WebSvc =
+    APP_NAME_IDENTIFIER _T(".Update3WebSvc");
+
+const TCHAR* const kProgIDGoogleUpdateCoreService =
+    APP_NAME_IDENTIFIER _T(".CoreClass");
+const TCHAR* const kProgIDGoogleUpdateCoreMachine =
+    APP_NAME_IDENTIFIER _T(".CoreMachineClass");
+
+const TCHAR* const kProgIDProcessLauncher =
+    APP_NAME_IDENTIFIER _T(".ProcessLauncher");
+
+const TCHAR* const kProgIDOneClickProcessLauncherUser =
+    _T(SHORT_COMPANY_NAME_ANSI) _T(".OneClickProcessLauncherUser");
+const TCHAR* const kProgIDOneClickProcessLauncherMachine =
+    _T(SHORT_COMPANY_NAME_ANSI) _T(".OneClickProcessLauncherMachine");
+
+const TCHAR* const kProgIDCoCreateAsync =
+    APP_NAME_IDENTIFIER _T(".CoCreateAsync");
+
+const TCHAR* const kProgIDCredentialDialogUser =
+    APP_NAME_IDENTIFIER _T(".CredentialDialogUser");
+const TCHAR* const kProgIDCredentialDialogMachine =
+    APP_NAME_IDENTIFIER _T(".CredentialDialogMachine");
+
+// Offline v3 manifest name.
+const TCHAR* const kOfflineManifestFileName = _T("OfflineManifest.gup");
+
+}  // namespace omaha
+
+#endif  // OMAHA_COMMON_CONST_GOOPDATE_H_
diff --git a/official/goopdate/omaha3_idl.idl b/official/goopdate/omaha3_idl.idl
new file mode 100644
index 0000000..87d8e14
--- /dev/null
+++ b/official/goopdate/omaha3_idl.idl
@@ -0,0 +1,972 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+import "oaidl.idl";
+import "ocidl.idl";
+
+// When adding interfaces to this file:
+//  * Do not use "Google" or "GoogleUpdate" directly. Instead, use preprocessor
+//    defines.
+//  * Add a test for the Google-specific value to
+//    omaha_customization_goopdate_apis_unittest.cc.
+
+//
+// Enums.
+// These values can be passed to interface methods and/or compared to their
+// output.
+//
+
+// Must be kept in sync with the enum in base/browser_utils.h.
+typedef enum BrowserType {
+  BROWSER_UNKNOWN           = 0,
+  BROWSER_DEFAULT           = 1,
+  BROWSER_INTERNET_EXPLORER = 2,
+  BROWSER_FIREFOX           = 3,
+  BROWSER_CHROME            = 4,
+} BrowserType;
+
+// The normal install flow proceeds from STATE_INIT through
+// STATE_INSTALL_COMPLETE in order, skipping states that are not relevant.
+// All exceptions and terminal states are start with STATE_INSTALL_COMPLETE.
+typedef enum CurrentState {
+  STATE_INIT = 1,
+  STATE_WAITING_TO_CHECK_FOR_UPDATE = 2,
+  STATE_CHECKING_FOR_UPDATE = 3,
+  STATE_UPDATE_AVAILABLE = 4,
+  STATE_WAITING_TO_DOWNLOAD = 5,
+  STATE_RETRYING_DOWNLOAD = 6,
+  STATE_DOWNLOADING = 7,
+  STATE_DOWNLOAD_COMPLETE = 8,
+  STATE_EXTRACTING = 9,
+  STATE_APPLYING_DIFFERENTIAL_PATCH = 10,
+  // TODO(omaha3): Should we move STATE_DOWNLOAD_COMPLETE here and eliminate
+  // STATE_READY_TO_INSTALL?
+  STATE_READY_TO_INSTALL = 11,
+  STATE_WAITING_TO_INSTALL = 12,
+  STATE_INSTALLING = 13,
+  STATE_INSTALL_COMPLETE = 14,
+  STATE_PAUSED = 15,
+  STATE_NO_UPDATE = 16,
+  STATE_ERROR = 17,
+} CurrentState;
+
+typedef enum InstallPriority {
+  INSTALL_PRIORITY_LOW = 0,
+  INSTALL_PRIORITY_HIGH = 10,
+} InstallPriority;
+
+// Specifies what the client should do after installation.
+typedef enum PostInstallAction {
+  POST_INSTALL_ACTION_DEFAULT = 0,
+
+  // Caller should exit silently.
+  POST_INSTALL_ACTION_EXIT_SILENTLY = 1,
+
+  // Caller should launch the command.
+  POST_INSTALL_ACTION_LAUNCH_COMMAND = 2,
+
+  // Caller should launch the command and exit silently.
+  POST_INSTALL_ACTION_EXIT_SILENTLY_ON_LAUNCH_COMMAND = 3,
+
+  // The caller should ask the user to restart the browser. If the value of
+  // IApp's browser is supported and postInstallUrl is valid, the client should
+  // offer to restart the browser. If the user chooses to do so, the client
+  // should launch the ICurrentState::postInstallUrl after shutting down and
+  // restarting the browser.
+  POST_INSTALL_ACTION_RESTART_BROWSER = 4,
+
+  // Similar to POST_INSTALL_ACTION_RESTART_BROWSER, but ask the user to shut
+  // down all browsers.
+  POST_INSTALL_ACTION_RESTART_ALL_BROWSERS = 5,
+
+  // The caller should ask the user to reboot the machine.
+  POST_INSTALL_ACTION_REBOOT = 6,
+} PostInstallAction;
+
+[
+  object,
+  dual,
+  uuid(6DB17455-4E85-46e7-9D23-E555E4B005AF),
+  helpstring("IGoogleUpdate3 Interface"),
+  pointer_default(unique)
+]
+interface IGoogleUpdate3 : IDispatch {
+  // TODO(Omaha): Perhaps this interface exposes helpers such as
+  // RestartBrowsers, etc.
+
+  // Returns the count of the AppBundles in this IGoogleUpdate3 interface.
+  [id(1), propget] HRESULT Count([out, retval] long* count);
+
+  // Returns an IDispatch of the AppBundle in this IGoogleUpdate3 interface at
+  // the specified 0-based index. This property has the dispid of DISPID_VALUE
+  // to make it the default property of IGoogleUpdate3.
+  [id(DISPID_VALUE), propget] HRESULT Item([in] long index,
+                                           [out, retval] IDispatch** bundle);
+  // Returns an IDispatch to a newly created empty AppBundle.
+  [id(2)] HRESULT createAppBundle([out, retval] IDispatch** app_bundle);
+}
+
+[
+  object,
+  dual,
+  uuid(313cfb25-4888-4fc6-9e19-764d8c5fc8f8),
+  helpstring("IAppBundle Interface"),
+  pointer_default(unique)
+]
+interface IAppBundle : IDispatch {
+  // TODO(omaha3): AppBundle::display_name_ is never used. Should we remove?
+  [propget] HRESULT displayName([out, retval] BSTR*);
+  [propput] HRESULT displayName([in] BSTR);
+
+  [propget] HRESULT displayLanguage([out, retval] BSTR*);
+  [propput] HRESULT displayLanguage([in] BSTR);
+
+  [propget] HRESULT installSource([out, retval] BSTR*);
+  [propput] HRESULT installSource([in] BSTR);
+
+  [propget] HRESULT originURL([out, retval] BSTR*);
+  [propput] HRESULT originURL([in] BSTR);
+
+  [propget] HRESULT offlineDirectory([out, retval] BSTR* offline_dir);
+  [propput] HRESULT offlineDirectory([in] BSTR offline_dir);
+
+  [propget] HRESULT sessionId([out, retval] BSTR* session_id);
+  [propput] HRESULT sessionId([in] BSTR session_id);
+
+  // The priority property determines download speed/priority and the number/
+  // frequency of retries.  Use values from the InstallPriority enum.
+  [propget] HRESULT priority([out, retval] long* priority);
+  [propput] HRESULT priority([in] long priority);
+
+  // Returns the count of the Apps in the AppBundle.
+  [id(1), propget] HRESULT Count([out, retval] long* count);
+
+  // Returns an IDispatch of the App in the AppBundle at the specified 0-based
+  // index. This property has the dispid of DISPID_VALUE to make it the default
+  // property of IAppBundle.
+  [id(DISPID_VALUE), propget] HRESULT Item([in] long index,
+                                           [out, retval] IDispatch** app);
+
+  // Impersonation and primary tokens set by the client. Typically only
+  // set by the gupdatem service. The gupdatem service exposes a narrow
+  // interface to medium integrity clients. When a medium integrity client calls
+  // into the gupdatem service, the gupdatem service captures the token of the
+  // caller, and then calls put_altTokens() on the gupdate service, so that the
+  // gupdate service can use it for future download() and install() requests.
+  [propput] HRESULT altTokens([in] ULONG_PTR impersonation_token,
+                              [in] ULONG_PTR primary_token,
+                              [in] DWORD caller_proc_id);
+
+  // Sets a HWND to associate with the client, if any.  This will be used as
+  // the parent window for any dialogs that the server may need to display.
+  [propput] HRESULT parentHWND([in] ULONG_PTR hwnd);
+
+  // Initializes the bundle with the properties that have been set.
+  [id(2)] HRESULT initialize();
+
+  // Returns an IDispatch to a new App for the specified app id.
+  // The App is added to the Bundle.
+  [id(3)] HRESULT createApp([in] BSTR app_id,
+                            [out, retval] IDispatch** app);
+
+  // Returns an IDispatch to a newly created App for the specified app ID. The
+  // App is populated with information from the existing installation and added
+  // to the Bundle. Fails if the specified app is not installed.
+  [id(4)] HRESULT createInstalledApp([in] BSTR app_id,
+                                     [out, retval] IDispatch** app);
+
+  // Creates App instances for all installed apps managed by this Omaha
+  // instance. Each App is populated with information from the existing install.
+  [id(5)] HRESULT createAllInstalledApps();
+
+  // These methods are non-blocking. The operation is scheduled.
+  [id(6)] HRESULT checkForUpdate();
+  [id(7)] HRESULT download();
+  [id(8)] HRESULT install();
+
+  // All-in-one function for automatically updating all apps. Populates the
+  // bundle then schedules the update, which includes the update check and
+  // download and install, if necessary.
+  [id(9)] HRESULT updateAllApps();
+
+  // These three methods are non-blocking. The operation is requested.
+  [id(10)] HRESULT stop();
+  [id(11)] HRESULT pause();
+  [id(12)] HRESULT resume();
+
+  // Returns true if the bundle has an uncompleted non-blocking request.
+  [id(13)] HRESULT isBusy([out, retval] VARIANT_BOOL* is_busy);
+
+  // Downloads a package of an installed application.
+  [id(14)] HRESULT downloadPackage([in] BSTR app_id, [in] BSTR package_name);
+
+  // TODO(omaha): Define this aggregated bundle state. Is this really a property
+  // or should it be getCurrentState?
+  // The server and bundle are the only thing that can provide aggregated
+  // time estimates for downloads. Also, aggregate percentage is not currently
+  // available to the client because the total bytes to download is not
+  // available from App in all post-update check states.
+  // To do this, we will probably need to know the total expected download
+  // size for all packages to be installed - those that are required or in use -
+  // by the time the update check phase is complete.
+  [id(15), propget] HRESULT currentState([out, retval] VARIANT* current_state);
+};
+
+[
+  object,
+  dual,
+  uuid(D999CE21-98B3-4894-BACB-A49A1D50848F),
+  helpstring("IApp Interface"),
+  pointer_default(unique)
+]
+interface IApp : IDispatch {
+  // Returns a version IDispatch object.
+  [id(1), propget] HRESULT currentVersion([out, retval] IDispatch** current);
+  [id(2), propget] HRESULT nextVersion([out, retval] IDispatch** next);
+
+  [propget] HRESULT appId([out, retval] BSTR*);
+
+  [propget] HRESULT displayName([out, retval] BSTR*);
+  [propput] HRESULT displayName([in] BSTR);
+
+  [propget] HRESULT language([out, retval] BSTR*);
+  [propput] HRESULT language([in] BSTR);
+
+  [propget] HRESULT ap([out, retval] BSTR*);
+  [propput] HRESULT ap([in] BSTR);
+
+  [propget] HRESULT ttToken([out, retval] BSTR*);
+  [propput] HRESULT ttToken([in] BSTR);
+
+  [propget] HRESULT iid([out, retval] BSTR*);
+  [propput] HRESULT iid([in] BSTR);
+
+  [propget] HRESULT brandCode([out, retval] BSTR*);
+  [propput] HRESULT brandCode([in] BSTR);
+
+  [propget] HRESULT clientId([out, retval] BSTR*);
+  [propput] HRESULT clientId([in] BSTR);
+
+  [propget] HRESULT labels([out, retval] BSTR*);
+  [propput] HRESULT labels([in] BSTR);
+
+  [propget] HRESULT referralId([out, retval] BSTR*);
+  [propput] HRESULT referralId([in] BSTR);
+
+  // Use values from the BrowserType enum.
+  [propget] HRESULT browserType([out, retval] UINT*);
+  [propput] HRESULT browserType([in] UINT);
+
+  [propget] HRESULT clientInstallData([out, retval] BSTR*);
+  [propput] HRESULT clientInstallData([in] BSTR);
+
+  [propget] HRESULT serverInstallDataIndex([out, retval] BSTR*);
+  [propput] HRESULT serverInstallDataIndex([in] BSTR);
+
+  // Set as soon as possible. Error pings are disabled until set to true.
+  [propget] HRESULT isEulaAccepted([out, retval] VARIANT_BOOL*);
+  [propput] HRESULT isEulaAccepted([in] VARIANT_BOOL);
+
+  [propget] HRESULT usageStatsEnable([out, retval] UINT*);
+  [propput] HRESULT usageStatsEnable([in] UINT);
+
+  [propget] HRESULT installTimeDiffSec([out, retval] UINT*);
+
+  // Returns an ICurrentState interface. The object underlying the interface has
+  // static data that does not get updated as the server state changes. To get
+  // the most "current" state, the currentState property needs to be queried
+  // again.
+  [propget] HRESULT currentState([out, retval] IDispatch**);
+};
+
+[
+  object,
+  dual,
+  uuid(BCDCB538-01C0-46d1-A6A7-52F4D021C272),
+  helpstring("IAppVersion Interface"),
+  pointer_default(unique)
+]
+interface IAppVersion : IDispatch {
+  [propget] HRESULT version([out, retval] BSTR*);
+
+  // [propget] HRESULT installManifest([out, retval] BSTR*);
+
+  // Returns the count of the Packages in the AppVersion.
+  [propget] HRESULT packageCount([out, retval] long* count);
+
+  // Returns an IDispatch of the Package in the AppVersion at the specified
+  // 0-based index.
+  [propget] HRESULT package([in] long index,
+                            [out, retval] IDispatch** package);
+};
+
+[
+  object,
+  dual,
+  uuid(DCAB8386-4F03-4dbd-A366-D90BC9F68DE6),
+  helpstring("IPackage Interface"),
+  pointer_default(unique)
+]
+interface IPackage : IDispatch {
+  // Retrieves the package from the package cache and copies it to the
+  // directory provided. Returns an error is the package is not available
+  // locally.
+  [id(1)] HRESULT get([in] BSTR dir);
+
+  // Returns true if the package has been downloaded and is available
+  // locally.
+  [propget] HRESULT isAvailable([out, retval] VARIANT_BOOL*);
+
+  // Returns the manifest name of the package.
+  [propget] HRESULT filename([out, retval] BSTR*);
+};
+
+// TODO(omaha3): We should figure out what else we are going to want in this
+// interface before dogfood even if we do not implement it.
+[
+  object,
+  dual,
+  uuid(247954F9-9EDC-4E68-8CC3-150C2B89EADF),
+  helpstring("ICurrentState Interface"),
+  pointer_default(unique)
+]
+interface ICurrentState : IDispatch {
+  // This interface is exposed to web clients!
+  // TODO(omaha3): Update valid comments once we settle on an implementation.
+
+  // A value from the CurrentState enum. This value determines which of the
+  // properties below are valid.
+  [propget] HRESULT stateValue([out, retval] LONG*);
+
+  // The remaining properties are only valid in the specified states. For all
+  // other states, the values are not specified.
+
+  // This property is valid only when stateValue is STATE_UPDATE_AVAILABLE.
+  [propget] HRESULT availableVersion([out, retval] BSTR*);
+
+  // The following three properties are only valid when stateValue is
+  // STATE_WAITING_TO_DOWNLOAD, STATE_RETRYING_DOWNLOAD, STATE_DOWNLOADING,
+  // STATE_DOWNLOAD_COMPLETE, STATE_EXTRACTING,
+  // STATE_APPLYING_DIFFERENTIAL_PATCH, or STATE_READY_TO_INSTALL.
+
+  // Bytes downloaded so far.
+  [propget] HRESULT bytesDownloaded([out, retval] ULONG*);
+
+  // Total bytes to download.
+  [propget] HRESULT totalBytesToDownload([out, retval] ULONG*);
+
+  // Estimated download time remaining in ms. -1 indicates unknown.
+  // Progress may not always be available, so clients should handle the -1 case.
+  [propget] HRESULT downloadTimeRemainingMs([out, retval] LONG*);
+
+  [propget] HRESULT nextRetryTime([out, retval] ULONGLONG*);
+
+  // TODO(omaha 3): Need some way to indicate reconnecting, retrying, etc.
+
+  // The following two properties are only valid when stateValue is
+  // STATE_INSTALLING or STATE_INSTALL_COMPLETE.
+
+  // Current install progress in percentage from 0 to 100. -1 indicates unknown.
+  // Progress may not always be available, so clients should handle the -1 case.
+  [propget] HRESULT installProgress([out, retval] LONG*);
+
+  // Estimated download time remaining in ms. -1 indicates unknown.
+  // Progress may not always be available, so clients should handle the -1 case.
+  [propget] HRESULT installTimeRemainingMs([out, retval] LONG*);
+
+  // The following four properties are only valid when stateValue is
+  // STATE_ERROR:
+
+  // Returns true if the app has been canceled.
+  [propget] HRESULT isCanceled([out, retval] VARIANT_BOOL* is_canceled);
+
+  // Error code.
+  [propget] HRESULT errorCode([out, retval] LONG*);
+
+  // Error extra code.
+  [propget] HRESULT extraCode1([out, retval] LONG*);
+
+  // The following three properties are only valid when stateValue is
+  // STATE_ERROR or STATE_INSTALL_COMPLETE.
+  // TODO(omaha3): If STATE_DOWNLOAD_COMPLETE or STATE_READY_TO_INSTALL becomes
+  // a terminal state, does it support completion messages?
+
+  // Completion message, localized in the specified language.
+  // TODO(omaha3): If we're going to have bundle error messages too, should the
+  // language be at bundle level? Should bundle have its own language setter?
+  [propget] HRESULT completionMessage([out, retval] BSTR*);
+
+  // Application installer result code. This is to be used as additional
+  // information only. Success/failure should be determined using errorCode.
+  // This is an error if errorCode is GOOPDATEINSTALL_E_INSTALLER_FAILED.
+  [propget] HRESULT installerResultCode([out, retval] LONG*);
+
+  // Application installer extra code.
+  [propget] HRESULT installerResultExtraCode1([out, retval] LONG*);
+
+  // A command that needs to be launched by the client after installation.
+  [propget] HRESULT postInstallLaunchCommandLine([out, retval] BSTR*);
+
+  // URL to be launched after restarting the browser.
+  [propget] HRESULT postInstallUrl([out, retval] BSTR*);
+
+  // Returns a PostInstallAction value indicating the action to be taken by the
+  // client after installation.
+  [propget] HRESULT postInstallAction([out, retval] LONG*);
+}
+
+[
+  object,
+  dual,
+  uuid(4E223325-C16B-4eeb-AEDC-19AA99A237FA),
+  helpstring("IRegistrationUpdateHook Interface"),
+  pointer_default(unique),
+]
+interface IRegistrationUpdateHook : IDispatch {
+  HRESULT UpdateRegistry([in] BSTR app_id, [in] VARIANT_BOOL is_machine);
+};
+
+[
+  object,
+  uuid(b3a47570-0a85-4aea-8270-529d47899603),
+  helpstring("ICredentialDialog Interface"),
+  pointer_default(unique),
+]
+interface ICredentialDialog : IUnknown {
+  HRESULT QueryUserForCredentials([in] ULONG_PTR owner_hwnd,
+                                  [in] BSTR server,
+                                  [in] BSTR message,
+                                  [out] BSTR* username,
+                                  [out] BSTR* password);
+};
+
+// BEGIN gupdatem interfaces.
+
+// The following interfaces are exposed as a narrower version of the
+// IGoogleUpdate3 interface from the gupdatem service. These interfaces are
+// meant for use from medium and low integrity clients.
+
+[
+  object,
+  dual,
+  uuid(494B20CF-282E-4BDD-9F5D-B70CB09D351E),
+  helpstring("IGoogleUpdate3Web Interface"),
+  pointer_default(unique),
+]
+interface IGoogleUpdate3Web : IDispatch {
+  HRESULT createAppBundleWeb([out, retval] IDispatch** app_bundle_web);
+};
+
+[
+  object,
+  uuid(2D363682-561D-4c3a-81C6-F2F82107562A),
+  helpstring("IGoogleUpdate3WebSecurity Interface"),
+  pointer_default(unique),
+]
+interface IGoogleUpdate3WebSecurity : IUnknown {
+  HRESULT setOriginURL([in] BSTR origin_url);
+};
+
+[
+  object,
+  dual,
+  uuid(DD42475D-6D46-496a-924E-BD5630B4CBBA),
+  helpstring("IAppBundleWeb Interface"),
+  pointer_default(unique),
+]
+interface IAppBundleWeb : IDispatch {
+  [id(2)] HRESULT createApp([in] BSTR app_guid,
+                            [in] BSTR brand_code,
+                            [in] BSTR language,
+                            [in] BSTR ap);
+  [id(3)] HRESULT createInstalledApp([in] BSTR app_id);
+  [id(4)] HRESULT createAllInstalledApps();
+
+  [propget] HRESULT displayLanguage([out, retval] BSTR*);
+  [propput] HRESULT displayLanguage([in] BSTR);
+
+  [propput] HRESULT parentHWND([in] ULONG_PTR hwnd);
+
+  [propget] HRESULT length([out, retval] int* index);
+  [id(DISPID_VALUE), propget] HRESULT appWeb(
+      [in] int index, [out, retval] IDispatch** app_web);
+
+  HRESULT initialize();
+
+  HRESULT checkForUpdate();
+  HRESULT download();
+  HRESULT install();
+
+  HRESULT pause();
+  HRESULT resume();
+  HRESULT cancel();
+
+  HRESULT downloadPackage([in] BSTR app_id, [in] BSTR package_name);
+
+  [propget] HRESULT currentState([out, retval] VARIANT* current_state);
+};
+
+[
+  object,
+  dual,
+  uuid(C6398F88-69CE-44ac-B6A7-1D3E2AA46679),
+  helpstring("IAppWeb Interface"),
+  pointer_default(unique),
+]
+interface IAppWeb : IDispatch {
+  [propget] HRESULT appId([out, retval] BSTR*);
+
+  // Returns an IAppVersionWeb IDispatch object.
+  [propget] HRESULT currentVersionWeb([out, retval] IDispatch** current);
+  [propget] HRESULT nextVersionWeb([out, retval] IDispatch** next);
+
+  HRESULT cancel();
+  [propget] HRESULT currentState([out, retval] IDispatch** current_state);
+  HRESULT launch();
+  HRESULT uninstall();
+};
+
+[
+  object,
+  dual,
+  uuid(0CD01D1E-4A1C-489d-93B9-9B6672877C57),
+  helpstring("IAppVersionWeb Interface"),
+  pointer_default(unique)
+]
+interface IAppVersionWeb : IDispatch {
+  [propget] HRESULT version([out, retval] BSTR*);
+
+  // Returns the count of the Packages in the AppVersion.
+  [propget] HRESULT packageCount([out, retval] long* count);
+
+  // TODO(omaha3): Implement this after a security review.
+  // Returns an IDispatch of the Package in the AppVersion at the specified
+  // 0-based index.
+  [propget] HRESULT packageWeb([in] long index,
+                               [out, retval] IDispatch** package);
+};
+
+[
+  object,
+  dual,
+  uuid(2E629606-312A-482f-9B12-2C4ABF6F0B6D),
+  helpstring("ICoCreateAsyncStatus Interface"),
+  pointer_default(unique)
+]
+interface ICoCreateAsyncStatus : IDispatch {
+  [propget] HRESULT isDone([out, retval] VARIANT_BOOL* is_done);
+  [propget] HRESULT completionHResult([out, retval] LONG* hr);
+  [propget] HRESULT createdInstance([out, retval] IDispatch** instance);
+};
+
+[
+  object,
+  uuid(DAB1D343-1B2A-47f9-B445-93DC50704BFE),
+  helpstring("ICoCreateAsync Interface"),
+  pointer_default(unique)
+]
+interface ICoCreateAsync : IUnknown {
+  HRESULT createOmahaMachineServerAsync(
+      [in] BSTR origin_url,
+      [in] BOOL create_elevated,
+      [out, retval] ICoCreateAsyncStatus** status);
+};
+
+// END gupdatem interfaces.
+
+// BEGIN Legacy google_update_idl interfaces.
+
+[
+  object,
+  uuid(5B25A8DC-1780-4178-A629-6BE8B8DEFAA2),
+  oleautomation,
+  nonextensible,
+  pointer_default(unique)
+]
+interface IBrowserHttpRequest2 : IUnknown {
+  // This method will send request/data from the browser process.
+  // @param url                     URL where request will be send.
+  // @param post_data               POST data, if any. Can be NULL.
+  // @param request_headers         HTTP request headers, if any. Can be NULL.
+  // @param response_headers_needed HTTP response headers that are needed.
+  //                                Should be one of the values listed here:
+  //                                    http://msdn.microsoft.com/aa385351.aspx
+  //                                The input is a SAFEARRAY of DWORD. Can be a
+  //                                VT_EMPTY.
+  // @param response_headers        HTTP response headers, returned as SAFEARRAY
+  //                                of BSTR. The values corresponding one-to-one
+  //                                with the response_headers_needed values. Can
+  //                                be NULL if response_headers_needed==VT_EMPTY
+  // @param response_code           HTTP response code.
+  // @param cache_filename          Cache file that contains the response data.
+  HRESULT Send([in] BSTR url,
+               [in] BSTR post_data,
+               [in] BSTR request_headers,
+               [in] VARIANT response_headers_needed,
+               [out] VARIANT* response_headers,
+               [out] DWORD* response_code,
+               [out] BSTR* cache_filename);
+};
+
+[
+  object,
+  oleautomation,
+  uuid(128C2DA6-2BC0-44c0-B3F6-4EC22E647964),
+  helpstring("Google Update IProcessLauncher Interface"),
+  pointer_default(unique)
+]
+interface IProcessLauncher : IUnknown {
+  // @param cmd_line The full command line to execute.
+  HRESULT LaunchCmdLine([in, string] const WCHAR* cmd_line);
+
+  // @param browser_type The browser to start.
+  // @param url The url to launch the browser with.
+  HRESULT LaunchBrowser([in] DWORD browser_type,
+                        [in, string] const WCHAR* url);
+
+  // @param app_id Unique id to identify the calling client application
+  // @param event_id Unique id for the command
+  // @param caller_proc_id The process id of the calling process
+  // @param proc_handle The process handle valid in the caller's context
+  HRESULT LaunchCmdElevated([in, string] const WCHAR* app_guid,
+                            [in, string] const WCHAR* cmd_id,
+                            [in] DWORD caller_proc_id,
+                            [out] ULONG_PTR* proc_handle);
+};
+
+[
+  object,
+  oleautomation,
+  uuid(5CCCB0EF-7073-4516-8028-4C628D0C8AAB),
+  helpstring("Google Update IOneClickProcessLauncher Interface"),
+  pointer_default(unique)
+]
+interface IOneClickProcessLauncher : IUnknown {
+  HRESULT LaunchAppCommand([in, string] const WCHAR* app_guid,
+                           [in, string] const WCHAR* cmd_id);
+};
+
+typedef enum {
+  COMPLETION_CODE_SUCCESS = 1,
+  COMPLETION_CODE_SUCCESS_CLOSE_UI,
+  COMPLETION_CODE_ERROR,
+  COMPLETION_CODE_RESTART_ALL_BROWSERS,
+  COMPLETION_CODE_REBOOT,
+  COMPLETION_CODE_RESTART_BROWSER,
+  COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY,
+  COMPLETION_CODE_REBOOT_NOTICE_ONLY,
+  COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY,
+  COMPLETION_CODE_RUN_COMMAND,
+} LegacyCompletionCodes;
+
+[
+  object,
+  oleautomation,
+  uuid(1C642CED-CA3B-4013-A9DF-CA6CE5FF6503),
+  helpstring("GoogleUpdate UI-specific events Interface"),
+  pointer_default(unique)
+]
+interface IProgressWndEvents : IUnknown {
+  // The UI is closing down. The user has clicked on either the "X" or the
+  // other buttons of the UI to close the window.
+  HRESULT DoClose();
+
+  // Pause has been clicked on.
+  HRESULT DoPause();
+
+  // Resume has been clicked on.
+  HRESULT DoResume();
+
+  // RestartBrowsers button has been clicked on.
+  HRESULT DoRestartBrowsers();
+
+  // Reboot button has been clicked on.
+  HRESULT DoReboot();
+
+  // Launch Browser.
+  HRESULT DoLaunchBrowser([in, string] const WCHAR* url);
+};
+
+
+[
+  object,
+  oleautomation,
+  uuid(49D7563B-2DDB-4831-88C8-768A53833837),
+  helpstring("IJobObserver Interface"),
+  pointer_default(unique)
+]
+interface IJobObserver : IUnknown {
+  HRESULT OnShow();
+  HRESULT OnCheckingForUpdate();
+  HRESULT OnUpdateAvailable([in, string] const WCHAR* version_string);
+  HRESULT OnWaitingToDownload();
+  HRESULT OnDownloading([in] int time_remaining_ms, [in] int pos);
+  HRESULT OnWaitingToInstall();
+  HRESULT OnInstalling();
+  HRESULT OnPause();
+  HRESULT OnComplete([in] LegacyCompletionCodes code,
+                     [in, string] const WCHAR* reserved);
+  HRESULT SetEventSink([in] IProgressWndEvents* ui_sink);
+};
+
+[
+  object,
+  oleautomation,
+  uuid(31AC3F11-E5EA-4a85-8A3D-8E095A39C27B),
+  helpstring("IGoogleUpdate Interface"),
+  pointer_default(unique)
+]
+interface IGoogleUpdate : IUnknown {
+  // @param guid The guid for the app to be updated.
+  // @param observer The eventing interface.
+  HRESULT CheckForUpdate([in, string] const WCHAR* guid,
+                         [in] IJobObserver* observer);
+
+  // @param guid The guid for the app to be updated.
+  // @param observer The eventing interface.
+  HRESULT Update([in, string] const WCHAR* guid,
+                 [in] IJobObserver* observer);
+};
+
+// IGoogleUpdateCore is an internal Omaha interface.
+[
+  object,
+  oleautomation,
+  uuid(909489C2-85A6-4322-AA56-D25278649D67),
+  helpstring("Google Update Core Interface"),
+  pointer_default(unique)
+]
+interface IGoogleUpdateCore : IUnknown
+{
+  // Runs a command elevated.
+  //
+  // @param app_id Unique id to identify the calling client application
+  // @param event_id Unique id for the command
+  // @param caller_proc_id The process id of the calling process
+  // @param proc_handle The process handle valid in the caller's context
+  HRESULT LaunchCmdElevated([in, string] const WCHAR* app_guid,
+                            [in, string] const WCHAR* cmd_id,
+                            [in] DWORD caller_proc_id,
+                            [out] ULONG_PTR* proc_handle);
+};
+
+// END Legacy google_update_idl interfaces.
+
+[
+  uuid(655DD85A-3C0D-4674-9C58-AF7168C5861E),
+  version(1.0),
+  helpstring("Google Update 3.0 Type Library")
+]
+library GoogleUpdate3Lib {
+  importlib("stdole2.tlb");
+
+  // These Interfaces are forward declared to ensure that they are described in
+  // the generated TLB file. This is required for ATL to correctly implement the
+  // corresponding IDispatch interfaces.
+  interface IGoogleUpdate3;
+  interface IAppBundle;
+  interface IApp;
+  interface IAppVersion;
+  interface IPackage;
+  interface ICurrentState;
+
+  interface IGoogleUpdate3Web;
+  interface IAppBundleWeb;
+  interface IAppWeb;
+  interface IAppVersionWeb;
+  interface ICoCreateAsyncStatus;
+
+  [
+    uuid(022105BD-948A-40c9-AB42-A3300DDF097F),
+    helpstring("GoogleUpdate3 Class for per-user applications")
+  ]
+  coclass GoogleUpdate3UserClass {
+    [default] interface IDispatch;
+  }
+
+  [
+    uuid(4EB61BAC-A3B6-4760-9581-655041EF4D69),
+    helpstring("GoogleUpdate3 Service Class for machine applications")
+  ]
+  coclass GoogleUpdate3ServiceClass {
+    [default] interface IDispatch;
+  }
+
+  [
+    uuid(22181302-A8A6-4f84-A541-E5CBFC70CC43),
+    helpstring("GoogleUpdate3Web for user applications")
+  ]
+  coclass GoogleUpdate3WebUserClass {
+    [default] interface IDispatch;
+  }
+
+  [
+    uuid(8A1D4361-2C08-4700-A351-3EAA9CBFF5E4),
+    helpstring("Pass-through broker for the GoogleUpdate3WebServiceClass")
+  ]
+  coclass GoogleUpdate3WebMachineClass {
+    [default] interface IDispatch;
+  }
+
+  [
+    uuid(534F5323-3569-4f42-919D-1E1CF93E5BF6),
+    helpstring("GoogleUpdate3Web")
+  ]
+  coclass GoogleUpdate3WebServiceClass {
+    [default] interface IDispatch;
+  }
+
+  [
+    uuid(598FE0E5-E02D-465d-9A9D-37974A28FD42),
+    helpstring("Fallback mechanism if GoogleUpdate3WebServiceClass fails")
+  ]
+  coclass GoogleUpdate3WebMachineFallbackClass {
+    [default] interface IDispatch;
+  }
+
+  [
+    uuid(E8CF3E55-F919-49d9-ABC0-948E6CB34B9F),
+    helpstring("CurrentStateUserClass")
+  ]
+  coclass CurrentStateUserClass {
+    [default] interface ICurrentState;
+  }
+
+  [
+    uuid(9D6AA569-9F30-41ad-885A-346685C74928),
+    helpstring("CurrentStateMachineClass")
+  ]
+  coclass CurrentStateMachineClass {
+    [default] interface ICurrentState;
+  }
+
+  [
+    uuid(7DE94008-8AFD-4c70-9728-C6FBFFF6A73E),
+    helpstring("CoCreateAsyncClass")
+  ]
+  coclass CoCreateAsyncClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(e67be843-bbbe-4484-95fb-05271ae86750),
+    helpstring("CredentialDialogUserClass")
+  ]
+  coclass CredentialDialogUserClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(25461599-633d-42b1-84fb-7cd68d026e53),
+    helpstring("CredentialDialogMachineClass")
+  ]
+  coclass CredentialDialogMachineClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(___AUTO_GENERATED_GUID___),
+    helpstring("GoogleComProxyMachineClass")
+  ]
+  coclass GoogleComProxyMachineClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(___AUTO_GENERATED_GUID___),
+    helpstring("GoogleComProxyUserClass")
+  ]
+  coclass GoogleComProxyUserClass {
+    [default] interface IUnknown;
+  }
+
+  // BEGIN Legacy google_update_idl coclasses.
+
+  [
+    uuid(ABC01078-F197-4b0b-ADBC-CFE684B39C82),
+    helpstring("ProcessLauncherClass Class")
+  ]
+  coclass ProcessLauncherClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(51F9E8EF-59D7-475b-A106-C7EA6F30C119),
+    helpstring("OneClickUserProcessLauncherClass Class")
+  ]
+  coclass OneClickUserProcessLauncherClass {
+    [default] interface IOneClickProcessLauncher;
+  }
+
+  [
+    uuid(AAD4AE2E-D834-46d4-8B09-490FAC9C722B),
+    helpstring("OneClickMachineProcessLauncherClass Class")
+  ]
+  coclass OneClickMachineProcessLauncherClass {
+    [default] interface IOneClickProcessLauncher;
+  }
+
+  [
+    uuid(2F0E2680-9FF5-43c0-B76E-114A56E93598),
+    helpstring("OnDemand updates for per-user applications.")
+  ]
+  coclass OnDemandUserAppsClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(6F8BD55B-E83D-4a47-85BE-81FFA8057A69),
+    helpstring("OnDemand pass-through broker for machine applications.")
+  ]
+  coclass OnDemandMachineAppsClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(9465B4B4-5216-4042-9A2C-754D3BCDC410),
+    helpstring("OnDemand updates for per-machine applications.")
+  ]
+  coclass OnDemandMachineAppsServiceClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(B3D28DBD-0DFA-40e4-8071-520767BADC7E),
+    helpstring("Fallback for if OnDemandMachineAppsServiceClass fails.")
+  ]
+  coclass OnDemandMachineAppsFallbackClass {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(E225E692-4B47-4777-9BED-4FD7FE257F0E),
+    helpstring("GoogleUpdateCore Class")
+  ]
+  coclass GoogleUpdateCoreClass
+  {
+    [default] interface IUnknown;
+  }
+
+  [
+    uuid(9B2340A0-4068-43d6-B404-32E27217859D),
+    helpstring("GoogleUpdateCore Machine Class")
+  ]
+  coclass GoogleUpdateCoreMachineClass
+  {
+    [default] interface IUnknown;
+  }
+
+  // END Legacy google_update_idl coclasses.
+};
diff --git a/official/main.scons b/official/main.scons
new file mode 100644
index 0000000..2cb0155
--- /dev/null
+++ b/official/main.scons
@@ -0,0 +1,820 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2009-2010 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========================================================================
+
+
+# This main.scons file is the root description of the build specified in this
+# directory and those under it. Individual components and the source files
+# used to build them are described in subsidiary files called 'build.scons'.
+#
+# To build this project change the current directory to googleclient/Omaha.
+# Then run hammer.bat
+#
+# A number of command line options can be specified. For a full list, use the
+# -h and -H command options.
+#
+# Some useful options include:
+#   -h   :   Prints a list of which modules and build variants are available
+#            in this project.
+#   -H   :   Print SCons specific help. SCons is a build tool which this
+#            project uses in tandem with the Software Construction Toolkit
+#            to build.
+#   -c   :   Clean the project. I.e. delete all build output.
+#   -j3  :   Run up to 3 build steps at once.
+#
+# Some useful build targets include:
+#    all_programs    :   Build all programs generated by this project.
+#    all_libraries   :   Build all libraries generated by this project.
+#    all_tests       :   Build all tests generated by this project.
+#    run_all_tests   :   Build and run all tests generated by this project.
+#
+# Some examples:
+#   Build all programs and libraries in debug mode:
+#     hammer
+#   Build and run all tests in debug mode on windows:
+#     hammer MODE=dbg-win run_all_tests
+#   Build arbitrary library in the default mode:
+#     hammer <library name>
+
+import copy
+import os
+import sys
+
+import omaha_version_utils
+
+# To switch the build, simply change the value of the msc_ver variable below.
+# VC2003/VC71 is 1310 but not supported by the current build.
+# VC2005/VC80 is 1400.
+# VC2008/VC90 is 1500.
+_msc_ver = 1400
+
+_sdk_path = os.environ['OMAHA_VISTASDK_DIR']
+
+_signtool_path = os.path.join(_sdk_path, 'bin/signtool.exe')
+
+# Build the "Google Update", the Google-specific version of Omaha.
+# TODO(omaha): Set based on an environment variable and/or Hammer arg.
+is_google_update_build = True
+
+#
+# Begin vendor-specific constants.
+#
+# These values must be changed when customizing open source Omaha.
+# You may also wish to replace the language-neutral constants in the individual
+# *generated_resources_*.rc files with localized strings.
+_FULL_COMPANY_NAME = 'Google Inc.'
+_SHORT_COMPANY_NAME = 'Google'
+_PRODUCT_NAME = 'Update'
+_COMPANY_DOMAIN_BASE = 'google'
+_COMPANY_DOMAIN = _COMPANY_DOMAIN_BASE + '.com'
+_MAIN_EXE_BASE_NAME = 'GoogleUpdate'
+# TODO(omaha): Use this throughout the build files where goopdate and
+# goopdateres are referenced.
+_MAIN_DLL_BASE_NAME = 'goopdate'
+# "Google Inc." must not be removed from the copyright string. This literal also
+# appears as LegalCopyright in the VERSIONINFO section of .rc files.
+# TODO(omaha): Use this variable in .wxs files, etc.
+_OMAHA_COPYRIGHT_STRING_ENGLISH = 'Copyright 2007-2010 Google Inc.'
+
+
+# TODO(omaha): Allow open source Omaha to be built without the Recovery MSI,
+# which requires an extra certificate file and is unlikely to be used, or
+# ClickOnce, which requires specifying a certificate hash. Disabling Recovery
+# by default also means the open source build will work by default without
+# having to check in SaveArguments.exe to SVN every 100 days.
+# TODO(omaha3): Allow open source Omaha to be built without support for
+# Omaha 2's COM APIs.
+
+# The hash comes from the Thumbprint item in the Details tab of the file
+# properties for the public key .cer file.
+# TODO(omaha): Can we automate reading this and/or pass it on the command line?
+_BUILD_SERVER_CERTIFICATE_HASH = 'cafd39335d6e76f0e26d81296e7cbbfbdf16a720'
+
+#
+# End vendor-specific constants.
+#
+
+
+# Windows is the only environment we bulid for, so create a specialized base
+# environment for Windows.
+win_env = Environment(
+    # For internal builds only, it is a good idea to have set before the
+    # 'component_setup' tool is used even though we add the SDK directories to
+    # the appropriate environment variables manually below.
+    PLATFORM_SDK_DIR = _sdk_path,
+    tools=[
+        'component_setup',
+        'target_platform_windows',
+        # Need to use 'masm' to override the 'as' tool currently used
+        # by default in 'target_platform_windows'
+        'masm',
+        'atlmfc_vc80',
+        'code_signing',
+        'component_targets_msvs',
+        'omaha_builders',
+    ],
+    msc_ver = _msc_ver,
+    build_server_certificate_hash = _BUILD_SERVER_CERTIFICATE_HASH,
+    # Visual Studio 2008 does not ship the sign tool. Use the sign tool from
+    # the Platform SDK. This must come after the 'code_signing' tool is used.
+    # Remove this if http://code.google.com/p/swtoolkit/issues/detail?id=16 is
+    # fixed.
+    SIGNTOOL = '"' + _signtool_path + '"',
+)
+
+# Remove this value because it conflicts with a #define
+# in shlwapi.h in the Vista SDK
+win_env.FilterOut(CPPDEFINES = ['OS_WINDOWS=OS_WINDOWS'])
+
+# We pre-generate our own manifests, so make sure hammer does not generate
+# default ones for us
+del win_env['MANIFEST_FILE']
+
+# Hack to work around bug in Hammer (http://b/1585388).
+# TODO(Omaha): Remove when bug is fixed.
+if win_env['ENV'].has_key('SYSTEMROOT'):
+  if win_env['ENV'].has_key('SystemRoot'):
+    del win_env['ENV']['SYSTEMROOT']
+    del os.environ['SYSTEMROOT']
+
+# Work around http://code.google.com/p/swtoolkit/issues/detail?id=10.
+win_env['COMPONENT_TEST_SUBSYSTEM_WINDOWS'] = 1
+
+# Declare command line options relating to code signing
+# authenticode_file and authenticode_password are used by the normal signing
+# tool and to sign manifests for ClickOnce.
+# patching_certificate is used to create patchable MSI installers and MSPs.
+# authenticode_file and authenticode_password are only used if !build_server.
+# patching_certificate is used in all cases.
+AddOption(
+    '--authenticode_file',
+    action='store',
+    nargs=1,
+    type='string',
+    default='$MAIN_DIR/data/OmahaTestCert.pfx'
+)
+
+default_cert_password = 'test'
+AddOption(
+    '--authenticode_password',
+    action='store',
+    nargs=1,
+    type='string',
+    default=default_cert_password
+)
+
+AddOption(
+    '--patching_certificate',
+    action='store',
+    nargs=1,
+    type='string',
+    default='$MAIN_DIR/data/OmahaTestCert.cer'
+)
+
+# Declare option for specifying path to new official build files
+AddOption(
+    '--official_build_path',
+    action='store',
+    nargs=1,
+    type='string',
+    default=None
+)
+
+# Declare option for specifying the set of official app installers to build.
+# The value describes the name of the directory containing the official
+# installer manifests and definitions.
+AddOption(
+    '--official_installer_app',
+    action='store',
+    nargs=1,
+    type='string',
+    default=None
+)
+
+AddOption(
+    '--official_installer_file',
+    action='store',
+    nargs=1,
+    type='string',
+    default=None
+)
+
+AddOption(
+    '--build_number',
+    action='store',
+    nargs=1,
+    type='string',
+    default=''
+)
+
+# Declare various boolean states.
+DeclareBit('use_precompiled_headers', 'Use precompiled headers in build')
+DeclareBit('official_installers', 'Building using checked-in binaries')
+DeclareBit('build_server', 'Running on the build server')
+DeclareBit('build_two_versions', 'Build second version for self-update testing')
+DeclareBit('test_certificate', 'Files will be signed with the test certificate')
+DeclareBit('bin', 'Building from pre-built binaries')
+DeclareBit('min', 'Building minimal set of projects')
+DeclareBit('all', 'Building all Projects')
+DeclareBit('msvs', 'Building Visual Studio solution files')
+DeclareBit('no-tests', 'Do not build the unit tests')
+
+# Build official installers if --official_installers is on the command line.
+win_env.SetBitFromOption('official_installers', False)
+
+# Build as a build server if --build_server is on the command line.
+win_env.SetBitFromOption('build_server', False)
+
+# Build two versions if --build_two_versions is on the command line.
+win_env.SetBitFromOption('build_two_versions', False)
+
+# Store new versions of pre-built binaries if --bin is on the command line.
+win_env.SetBitFromOption('bin', False)
+
+# Build minimal set of libs if --min is on the command line.
+win_env.SetBitFromOption('min', False)
+
+# Build all libs if --all is on the command line.
+win_env.SetBitFromOption('all', False)
+
+# Build Visual Studio solution files if --msvs is on the command line.
+win_env.SetBitFromOption('msvs', False)
+
+# Do not build the unit tests if the bit is set.
+win_env.SetBitFromOption('no-tests', False)
+
+# Build all directories and two versions if this is the build server.
+if win_env.Bit('build_server'):
+  win_env.SetBits('all')
+  win_env.SetBits('build_two_versions')
+
+# Make sure 'all' overrides 'min'.
+if win_env.Bit('all'):
+  win_env.ClearBits('min')
+
+# Allow use of command-line-specified certificates to sign with, but
+# only if we're not on the build server.
+if not win_env.Bit('build_server'):
+  win_env.Replace(
+    CERTIFICATE_PATH=GetOption('authenticode_file'),
+    CERTIFICATE_PASSWORD=GetOption('authenticode_password'),
+  )
+
+  # Store whether we're using the default test cert separately, because
+  # we won't always know what the default password was.
+  if GetOption('authenticode_password') is default_cert_password:
+    win_env.SetBits('test_certificate')
+
+
+# The precompiled headers are to be used as an optional build speed up
+# build facility. Individual compilation units in the project must build with
+# or without precompiled headers. Building without precompiled headers is sort
+# of meaningless, since all the time we should build with it. However,
+# eliminating the dependency is desirable from a few reasons:
+# 1. making sure the files in the project include all the definitions they need
+# 2. creating different precompile headers if needed.
+# 3. making sure the precompile headers do not add to the size bloat.
+# There are two current limitations with the current setup.
+# First, due to pushing the warning level to W4 and WAll, we rely on the
+# common precompile.h to properly turn off the warnings inside system and
+# library code.
+# Second, to override the ATLASSERT, a file must be included before any of
+# the atl headers. To do this on a case by case basis is impractical and
+# error prone.
+# Therefore, when building with precompile headers off, the code is
+# building on W3 and it is not taking over the ATL asserts.
+win_env.SetBitFromOption('use_precompiled_headers', True)
+
+if win_env.Bit('use_precompiled_headers'):
+  print 'Using precompiled headers.'
+
+
+#
+# Set up version info.
+#
+omaha_version_info = omaha_version_utils.OmahaVersionInfo('VERSION')
+omaha_versions_info = [omaha_version_info]
+
+if win_env.Bit('build_two_versions'):
+  omaha_test_version_info = copy.deepcopy(omaha_version_info)
+  omaha_test_version_info.MakeTestVersion()
+  omaha_versions_info.append(omaha_test_version_info)
+
+# Print the version(s) being built.
+print 'Building versions: %s' % ', '.join(
+    [version_info.GetVersionString() for version_info in omaha_versions_info])
+
+build_number = GetOption('build_number')
+if build_number:
+  print 'Build number: %s' % build_number
+
+win_env['omaha_versions_info'] = omaha_versions_info
+
+if is_google_update_build:
+    win_env.Append(
+        CPPDEFINES = ['GOOGLE_UPDATE_BUILD'],
+        RCFLAGS = ['/DGOOGLE_UPDATE_BUILD=1'],
+    )
+
+# Make sure python.exe can be located.
+win_env.AppendENVPath('PATH', os.environ['OMAHA_PYTHON_DIR'])
+
+win_env.Append(
+    # Add windows specific compiler flags.
+    CCFLAGS = [
+        '/nologo',
+        '/c',
+        '/Zc:forScope',
+        '/D_HAS_EXCEPTIONS=0',
+        '/DCOMPILER_MSVC',
+        '/J',
+        '/DSTL_MSVC',
+        '/GR-',
+        '/D_CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES=1',
+        '/D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1',
+        '/D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1',
+        '/WX',      # warnings as errors
+
+        #
+        # Disable the following level 4 warnings below.
+        #
+        '/wd4127',  # conditional expression is constant
+        '/wd4189',  # local variable is initialized but not referenced
+        '/wd4505',  # unreferenced local function has been removed
+
+        #
+        # Disable the pedantic warnings below.
+        #
+        '/wd4191',  # unsafe conversion from 'type of expression' to
+                    #     'type required'
+        '/wd4217',  # member template functions cannot be used for
+                    #     copy-assignment...
+        '/wd4365',  # conversion from 'type_1' to 'type_2',
+                    #     signed/unsigned mismatch
+        '/wd4512',  # assignment operator could not be generated
+        '/wd4514',  # unreferenced inline function has been removed
+        '/wd4555',  # expression has no effect
+        '/wd4619',  # #pragma warning : there is no warning number 'number'
+        '/wd4623',  # default constructor could not be generated...
+        '/wd4625',  # copy constructor could not be generated...
+        '/wd4626',  # assignment operator could not be generated...
+        '/wd4668',  # not defined as a preprocessor macro, replacing with '0'.
+        '/wd4710',  # function not inlined
+        '/wd4711',  # function 'function' selected for inline expansion
+        '/wd4738',  # storing 32-bit float result in memory...
+        '/wd4820',  # bytes padding added after construct 'member_name'
+        ],
+
+    # Where to look for include files.
+    CPPPATH = [
+        '$MAIN_DIR',
+        '$MAIN_DIR/..',
+        '$MAIN_DIR/third_party/chrome',
+        '$MAIN_DIR/third_party/gtest/include',
+        ],
+
+    # Defines for windows environment.
+    CPPDEFINES = [
+        'WIN32', '_WINDOWS',
+        'UNICODE', '_UNICODE',
+        'WIN32_LEAN_AND_MEAN',
+        'STRICT',
+        'SECURITY_WIN32',
+        '_ATL_ALL_WARNINGS',
+        '_ATL_CSTRING_EXPLICIT_CONSTRUCTORS',
+        '_ATL_CSTRING_NO_CRT',
+        '_ATL_NO_ACLAPI',
+        '_ATL_NO_DEFAULT_LIBS',
+        '_ATL_NO_EXCEPTIONS',
+        '_ATL_NO_GLOBAL_SOCKET_STARTUP',
+        '_ATL_NO_PERF_SUPPORT',
+        '_ATL_NO_TRACK_HEAP',
+        '_ATL_NO_UUIDOF',
+        '_ATL_STATIC_REGISTRY',
+        '_CRT_RAND_S',      # rand_s support available in Windows XP only.
+        '_WTL_NO_CSTRING',  # WTL uses ATL CString instead.
+
+        # '_ATL_NO_CONNECTION_POINTS',
+        # '_ATL_NO_DOCHOSTUIHANDLER',
+        # '_ATL_NO_HOSTING',
+
+        # Set these C_DEFINES when APPVER=5.0
+        # (see win32.mak in Platform SDK)
+        # Target Windows XP and IE 6.0 and above.
+        'WINVER=0x0501',
+        '_WIN32_WINNT=0x0501',
+        '_WIN32_IE=0x0600',
+        '_RICHEDIT_VER=0x0010',
+
+        # don't define min and max in windef.h
+        'NOMINMAX',
+
+        # Logging is enabled in all modes for diagnostics purposes.
+        'LOGGING',
+
+        'FULL_COMPANY_NAME_ANSI=\\"%s\\"' % _FULL_COMPANY_NAME,
+        'SHORT_COMPANY_NAME_ANSI=\\"%s\\"' % _SHORT_COMPANY_NAME,
+        'PRODUCT_NAME_ANSI=\\"%s\\"' % _PRODUCT_NAME,
+        'COMPANY_DOMAIN_BASE_ANSI=\\"%s\\"' % _COMPANY_DOMAIN_BASE,
+        'COMPANY_DOMAIN_ANSI=\\"%s\\"' % _COMPANY_DOMAIN,
+        'OMAHA_APP_NAME_ANSI=\\"%s %s\\"' % (
+            _SHORT_COMPANY_NAME, _PRODUCT_NAME),
+        'MAIN_EXE_BASE_NAME_ANSI=\\"%s\\"' % _MAIN_EXE_BASE_NAME,
+        'MAIN_DLL_BASE_NAME_ANSI=\\"%s\\"' % _MAIN_DLL_BASE_NAME,
+        'OFFICIAL_BUILD=%d' % win_env.Bit('build_server'),
+        'TEST_CERTIFICATE=%d' % win_env.Bit('test_certificate'),
+        'ONECLICK_PLUGIN_NAME=_T(\\"%s\\")' % (
+            omaha_version_utils.GetONECLICK_PLUGIN_NAME()),
+        'ONECLICK_PLUGIN_VERSION_ANSI=\\"%d\\"' % (
+            omaha_version_info.oneclick_plugin_version),
+        'ONECLICK_PLUGIN_FILENAME=_T(\\"%s\\")' % (
+            omaha_version_info.oneclick_plugin_filename),
+        'UPDATE_PLUGIN_NAME=_T(\\"%s\\")' % (
+            omaha_version_utils.GetUPDATE_PLUGIN_NAME()),
+        'UPDATE_PLUGIN_VERSION_ANSI=\\"%d\\"' % (
+            omaha_version_info.update_plugin_version),
+        'UPDATE_PLUGIN_FILENAME=_T(\\"%s\\")' % (
+            omaha_version_info.update_plugin_filename),
+        'BHO_NAME=_T(\\"%s\\")' % omaha_version_utils.GetBHO_NAME(),
+        'BHO_FILENAME=_T(\\"%s\\")' % omaha_version_info.bho_filename,
+        'CRASH_HANDLER_NAME=_T(\\"%s\\")' % omaha_version_utils.GetCRASH_HANDLER_NAME(),
+        ],
+
+    # Link in some windows libraries.
+    LIBS = [
+        'advapi32',
+        'comdlg32',
+        'gdi32',
+        'kernel32',
+        'odbc32',
+        'odbccp32',
+        'ole32',
+        'oleaut32',
+        'shell32',
+        'user32',
+        'uuid',
+        'winspool',
+        ],
+
+    # Common linker flags.
+    LINKFLAGS = [
+        '/nologo',
+        '/SUBSYSTEM:WINDOWS',
+        '/MACHINE:X86',
+        '/RELEASE',
+        '/MAP',
+        '/NODEFAULTLIB',
+        '/DYNAMICBASE',   # Enable ASLR. See http://goo.gl/k2IE.
+        '/NXCOMPAT',      # Enable NX support. See http://goo.gl/k2IE.
+        '/SAFESEH',
+        ],
+
+    # Shared library specific linker flags.
+    SHLINKFLAGS = [
+        '/nologo',
+        '/SUBSYSTEM:WINDOWS',
+        '/MACHINE:x86',
+        ],
+
+    # Resource compiler flags.
+    # Defines in CCFLAGS are automatically included.
+    RCFLAGS = [
+        '/l 1033',  # /l == default language ID
+        '/DBUILD_NUMBER=\\"%s\\"' % build_number,
+        '/DOMAHA_COPYRIGHT_STRING_ENGLISH=\\"%s\\"' % (
+            _OMAHA_COPYRIGHT_STRING_ENGLISH),
+        ],
+)
+
+# Allow verification of these settings in the build log.
+if win_env.Bit('build_server'):
+  print 'OFFICIAL_BUILD=1'
+  print 'TEST_CERTIFICATE=%d' % win_env.Bit('test_certificate')
+
+
+# Add the parent directory of the main omaha directory to the Python path so
+# that we can import using the format "omaha.subdir.module".
+sys.path.append(os.path.split(win_env.Dir('$MAIN_DIR').abspath)[0])
+
+# Make sure Vista SDK in all the important paths earlier in path than VC80.
+for mid_dir in ['', 'vc']:
+  for env_var, sub_dir in [('PATH', 'bin'),
+                           ('INCLUDE', 'include'),
+                           ('LIB', 'lib')]:
+    var_path = os.path.join(_sdk_path, mid_dir, sub_dir)
+    if os.path.exists(var_path):
+      win_env.PrependENVPath(env_var, var_path)
+
+if not win_env.Bit('official_installers'):
+  win_env.Append(CPPPATH = os.environ['OMAHA_WTL_DIR'])
+
+  # Make sure csc.exe can be located.
+  win_env.AppendENVPath('PATH', os.environ['OMAHA_NET_DIR'])
+
+  sys.path.append('tools')  # for import proxy_clsid_utils.py
+  import proxy_clsid_utils
+
+  # Generate uniqe proxy CLSIDs for each build.
+  win_env.Execute('python $MAIN_DIR\\tools\\proxy_clsid_utils.py')
+  win_env.Append(
+      CPPDEFINES = [
+        'PROXY_CLSID_IS_MACHINE=%s' % proxy_clsid_utils.GetMachineProxyClsid(),
+        'PROXY_CLSID_IS_USER=%s' % proxy_clsid_utils.GetUserProxyClsid(),
+      ],
+  )
+
+# WiX path has to be added before the WiX tool can be called.
+win_env.AppendENVPath('PATH', os.environ['OMAHA_WIX_DIR'])
+
+win_env.Tool('wix')
+
+
+_base_dirs = [
+    '.',
+    'base',
+    'clickonce',
+    'client',
+    'common',
+    'core',
+    'google_update',
+    'goopdate',
+    'net',
+    'service',
+    'setup',
+    'statsreport',
+    'third_party',
+    'tools',
+    'ui',
+    ]
+
+_normal_dirs = [
+    'google_update_b4451148',
+    'installers',
+    'mi_exe_stub',
+    'plugins',
+    'recovery',
+    ]
+
+_official_installers_dirs = [
+    'installers',
+    ]
+
+_extra_dirs = [
+    'enterprise',
+    'standalone',
+    ]
+
+#
+# Need to decide which subdirs need to be built.
+#
+_dirs_to_build_set = set()
+
+if win_env.Bit('official_installers'):
+  # Only want to build very specific subdirs.
+  win_env.SetBits('no-tests')
+  _dirs_to_build_set.update(_official_installers_dirs)
+elif not win_env.Bit('bin'):
+  # All other configs get the base dirs.
+  _dirs_to_build_set.update(_base_dirs)
+
+  if win_env.Bit('min'):
+    print '*** Building Minimal Set of Projects ***'
+  else:
+    _dirs_to_build_set.update(_normal_dirs)
+
+  if win_env.Bit('all'):
+    _dirs_to_build_set.update(_extra_dirs)
+
+# Build Google application-specific metainstallers.
+if os.path.exists(win_env.Dir('$MAIN_DIR/internal').abspath):
+  _dirs_to_build_set.update(['internal'])
+
+_dirs_to_build = list(_dirs_to_build_set)
+
+# This must be the last directory.
+if not win_env.Bit('no-tests'):
+  _dirs_to_build.append('testing')
+
+# Instruct Hammer which dirs to build.
+win_env['BUILD_SCONSCRIPTS'] = _dirs_to_build
+
+# These are used by the Omaha Builder OmahaUnittest(). They must be added to the
+# environment because there must be one per-mode.
+win_env['all_in_one_unittest_sources'] = []
+win_env['all_in_one_unittest_libs'] = set()
+
+
+# Create the leaf debug Windows environment.
+windows_debug_env = win_env.Clone(
+    # Give this build a name and a description.
+    BUILD_TYPE = 'dbg-win',
+    BUILD_TYPE_DESCRIPTION = 'Windows debug build',
+)
+
+# Use common debug settings.
+windows_debug_env.Tool('target_debug')
+
+windows_debug_env.Append(
+    CCFLAGS = [
+        '/RTC1',
+        '/Od',
+        '/MTd',
+        ],
+    CPPDEFINES = [
+        '_DEBUG',
+        'DEBUG',
+        ],
+)
+
+
+# Create the leaf optimized Windows environment.
+windows_optimized_env = win_env.Clone(
+    # Give this build a name and a description.
+    BUILD_TYPE = 'opt-win',
+    BUILD_TYPE_DESCRIPTION = 'Windows optimized build',
+)
+
+# Use common optimized settings.
+windows_optimized_env.Tool('target_optimized')
+
+windows_optimized_env.Append(
+    CCFLAGS = [
+        '/O1',        # Optimize for small size.
+        '/GS',
+        '/FD',
+        '/GL',        # Global optimization goes with link flag '/LTCG'
+        '/MT',
+        ],
+    CPPDEFINES = [
+        'NDEBUG',
+        'SHIPPING'    # code in 'common' needs this
+        ],
+    ARFLAGS = [
+        '/LTCG',      # Set LTCG for creation of .lib files too.
+        ],
+    LINKFLAGS = [
+        '/incremental:no',
+        '/opt:ref',
+        '/opt:icf=32',
+        '/opt:nowin98',
+        '/LTCG',      # Link-time code generation goes with cl flag '/GL'
+        ],
+)
+
+
+# Create an environment for coverage test builds, based on the dbg build.
+windows_coverage_env = windows_debug_env.Clone(
+    BUILD_TYPE = 'coverage-win',
+    BUILD_TYPE_DESCRIPTION = 'Windows coverage build',
+)
+# The Coverage build require additional tools that not everyone has. Therefore,
+# it should build as part of the all group.
+windows_coverage_env.FilterOut(BUILD_GROUPS=['all'])
+
+windows_coverage_env.Tool('code_coverage')
+
+# Coverage will run omaha_unittest.exe, which requires some extra environment.
+for env_var in os.environ:
+  if not env_var in windows_coverage_env['ENV']:
+    windows_coverage_env['ENV'][env_var] = os.environ[env_var]
+
+# Create a target that covers everything in the staging dir, as many of those
+# files will be required for the unittests to run successfully.
+# TODO(omaha3): This may not be necessary when using ComponentTestProgram. If it
+# is, it needs to be changed to use test/ instead of $STAGING_DIR/.
+windows_coverage_env.Alias(
+    'run_omaha_unittest_for_coverage',
+    '$STAGING_DIR',
+    '$STAGING_DIR/omaha_unittest.exe'
+)
+windows_coverage_env.Append(
+    # TODO(omaha): We cannot run our unit tests on the new build system. Ensure
+    # coverage works with the new test execution system.
+    # COVERAGE_TARGETS=['run_omaha_unittest_for_coverage'],
+    COVERAGE_INSTRUMENTATION_PATHS=['$STAGING_DIR'],
+    # This value should only be used in code if absolutely necessary.
+    CPPDEFINES=['COVERAGE_ENABLED'],
+)
+
+# TODO(omaha): Prevent the analyzer, which will fail, from running until we can
+# run unit tests on the build system. See the TODO above.
+windows_coverage_env['COVERAGE_START_CMD'] = '@echo Not starting coverage'
+windows_coverage_env['COVERAGE_STOP_CMD'] = '@echo Not ending coverage'
+
+# Skip signing in coverage build until the last step.
+windows_coverage_env['SIGNTOOL_ORIG'] = windows_coverage_env['SIGNTOOL']
+windows_coverage_env['SIGNTOOL'] = '@echo Signing deferred: '
+
+def SigningCommand(env, filename):
+  # Only do signing if there is a certificate file or certificate name.
+  if env.subst('$CERTIFICATE_PATH') or env.subst('$CERTIFICATE_NAME'):
+    # The command used to do signing (target added on below).
+    signing_cmd = '$SIGNTOOL_ORIG sign '
+    # Add in certificate file if any.
+    if env.subst('$CERTIFICATE_PATH'):
+      signing_cmd += ' /f "$CERTIFICATE_PATH"'
+      # Add certificate password if any.
+      if env.subst('$CERTIFICATE_PASSWORD'):
+        signing_cmd += ' /p "$CERTIFICATE_PASSWORD"'
+    # Add certificate store if any.
+    if env.subst('$CERTIFICATE_NAME'):
+      # The command used to do signing (target added on below).
+      signing_cmd += ' /s "$CERTIFICATE_STORE" /n "$CERTIFICATE_NAME"'
+    # Add timestamp server if any.
+    if env.subst('$TIMESTAMP_SERVER'):
+      signing_cmd += ' /t "$TIMESTAMP_SERVER"'
+    # Add in target name
+    signing_cmd += ' "%s"' % filename
+    return signing_cmd
+  else:
+    return 'echo no signing needed'
+
+def _IsInstrumentableFileType(file):
+  if (file.endswith('.exe') or
+      file.endswith('.dll')):
+    return True
+  return False
+
+def _IsSignableFileType(file):
+  if (file.endswith('.exe') or
+      file.endswith('.dll') or
+      file.endswith('.msi') or
+      file.endswith('.msp')):
+    return True
+  return False
+
+# Sign files during the install stage, since instrumentation invalidates the
+# signature. Signing within the individual build files was disabled above.
+# Do not sign intermediate "_unsigned" or "_unittest" files.
+# Instrumented files must be signed after installing becuase install is what
+# does the instrumentation. This also seems to be required to avoid
+# unnecessarily rebuilding non-instrumentable files.
+def _PostCoverageSigningInstall(dest, source, env):
+  if _IsInstrumentableFileType(dest) or not _IsSignableFileType(dest):
+    # Install the file to staging. Includes instrumentation if appropriate.
+    env['PRECOVERAGE_SIGN_INSTALL'](dest, source, env)
+  else:
+    # For signable but not instrumentable files, copy the files rather than
+    # using PRECOVERAGE_SIGN_INSTALL as this works around unnecessary rebuilds
+    # caused by http://code.google.com/p/swtoolkit/issues/detail?id=13.
+    env.Execute('copy "%s" "%s"' % (source, dest))
+
+  if (_IsSignableFileType(dest) and
+      (-1 == dest.find('_unsigned')) and
+      (-1 == dest.find('_unittest')) and
+      os.path.split(os.path.split(dest)[0])[1] == 'staging'):
+    env.Execute(SigningCommand(env, dest))
+
+windows_coverage_env['PRECOVERAGE_SIGN_INSTALL'] = (
+    windows_coverage_env['INSTALL'])
+windows_coverage_env['INSTALL'] = _PostCoverageSigningInstall
+
+
+# Make debug the default build after any copies of it have been made.
+windows_debug_env.Append(BUILD_GROUPS = ['default'])
+
+# ----------------------------------------------------------
+
+# Build the variants listed above.
+# This step will call each of the SConscripts (build.scons) listed,
+# once for each variant currently being built.
+BuildEnvironments(
+    [ windows_debug_env,
+      windows_optimized_env,
+      windows_coverage_env,
+    ]
+)
+
+if 'HAMMER_RUNS_TESTS' in os.environ.keys():
+  # Hammer sets the default target to 'scons-out'. This causes run_* aliases
+  # to also be built, which means the tests run by default. To avoid this, clear
+  # Default and set the default to just build the programs.
+  Default(None)
+  # TODO(omaha): Not all of our core binaries are included in these three
+  # aliases. This is because SignedBinary() and Command() do not add the outputs
+  # to a group. Fix this.
+  Default(['all_programs', 'all_libraries', 'all_test_programs'])
+
+if win_env.Bit('msvs'):
+  source_project = win_env.ComponentVSDirProject('all_source', ['$MAIN_DIR'])
+
+  # 'all_*' values do not appear to be populated until after BuildEnvironments
+  # is called. Thus, the solution will be specific to either debug or optimized.
+  # ComponentVSSourceProject() might be more desirable, but it does not appear
+  # to work.
+  windows_debug_env.ComponentVSSolution('omaha_dbg',
+                                        ['all_programs', 'all_libraries'],
+                                        projects=[source_project])
diff --git a/official/plugins/update/activex/update_control_idl.idl b/official/plugins/update/activex/update_control_idl.idl
new file mode 100644
index 0000000..a84cc1c
--- /dev/null
+++ b/official/plugins/update/activex/update_control_idl.idl
@@ -0,0 +1,103 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+import "oaidl.idl";
+
+[
+  object,
+  uuid(57E37502-65A5-484a-A035-C1608B2626EA),
+  dual,
+  helpstring("GoogleUpdate3Web Control"),
+  pointer_default(unique)
+]
+interface IGoogleUpdate3WebControl : IDispatch {
+  [id(1)] HRESULT createOmahaMachineServerAsync(
+      [in] VARIANT_BOOL create_elevated,
+      [out, retval] IDispatch** async_status);
+
+  [id(2)] HRESULT createOmahaUserServer([out, retval] IDispatch** server);
+
+  [id(3)] HRESULT getInstalledVersion([in] BSTR guid_string,
+                                      [in] VARIANT_BOOL is_machine,
+                                      [out, retval] BSTR* version_string);
+
+  [id(4)] HRESULT crossInstall([in] BSTR extra_args);
+
+  [id(5)] HRESULT launchAppCommand([in] BSTR guid_string,
+                                   [in] VARIANT_BOOL is_machine,
+                                   [in] BSTR cmd_id);
+};
+
+[
+  object,
+  uuid(6F65D62B-2F32-4483-9028-176C30B2389D),
+  dual,
+  helpstring("Google Update OneClick Control"),
+  pointer_default(unique)
+]
+interface IGoogleUpdateOneClick : IDispatch
+{
+  // Deprecated. Will be removed in the next release of OneClick.
+  [id(1), helpstring("Install")] HRESULT Install(
+      [in] BSTR cmd_line_args,
+      [in] VARIANT* success_callback,
+      [in] VARIANT* failure_callback);
+
+  // New, easier way of calling Install. Use this for newer web pages.
+  [id(2), helpstring("Install2")] HRESULT Install2([in] BSTR extra_args);
+
+  [id(3), helpstring("GetInstalledVersion")] HRESULT GetInstalledVersion(
+      [in] BSTR guid_string,
+      [in] VARIANT_BOOL is_machine,
+      [out, retval] BSTR* version_string);
+
+  [id(4), helpstring("GetOneClickVersion")] HRESULT GetOneClickVersion(
+      [out, retval] long* version);
+
+  // TODO(omaha): Remove this if LaunchAppCommand only ships in Omaha3
+  [id(5), helpstring("LaunchAppCommand")] HRESULT LaunchAppCommand(
+      [in] BSTR app_guid,
+      [in] VARIANT_BOOL is_machine,
+      [in] BSTR cmd_id);
+};
+
+[
+  uuid(b627c883-e979-4873-80b3-ddd0b658b56a),
+  version(1.0),
+  helpstring("Google Update Browser Plugins 3.0 Type Library")
+]
+library GoogleUpdateControlLib {
+  importlib("stdole2.tlb");
+
+  interface IGoogleUpdateOneClick;
+  interface IGoogleUpdate3WebControl;
+
+  [
+    uuid(c442ac41-9200-4770-8cc0-7cdb4f245c55),
+    helpstring("Google Update OneClick Control Class")
+  ]
+  coclass GoogleUpdateOneClickControlCoClass
+  {
+    [default] interface IGoogleUpdateOneClick;
+  };
+
+  [
+    uuid(c3101a8b-0ee1-4612-bfe9-41ffc1a3c19d),
+    helpstring("GoogleUpdate3Web Control Class")
+  ]
+  coclass GoogleUpdate3WebControlCoClass {
+    [default] interface IDispatch;
+  };
+};
diff --git a/omaha_version_utils.py b/omaha_version_utils.py
index 4457521..2b8f565 100644
--- a/omaha_version_utils.py
+++ b/omaha_version_utils.py
@@ -17,11 +17,14 @@
 
 """Constants and utilities related to Omaha versions."""
 
-_ONECLICK_ACTIVEX_NAME = 'npGoogleOneClick'
+_ONECLICK_PLUGIN_NAME = 'npGoogleOneClick'
+_UPDATE_PLUGIN_NAME = 'npGoogleUpdate'
 _BHO_NAME = 'GoopdateBho'
+_CRASH_HANDLER_NAME = 'GoogleCrashHandler'
 
 # List of languages that are fully supported in the current build.
 _OMAHA_LANGUAGES = [
+    'am',
     'ar',
     'bg',
     'bn',
@@ -57,7 +60,6 @@
     'ms',
     'nl',
     'no',
-    'or',
     'pl',
     'pt-BR',
     'pt-PT',
@@ -67,6 +69,7 @@
     'sl',
     'sr',
     'sv',
+    'sw',
     'ta',
     'te',
     'th',
@@ -82,36 +85,65 @@
 # 'userdefault' addresses apps that don't look up the resource for the OS
 # language. See http://b/1328652.
 _ADDITIONAL_SHELL_LANGUAGES = [
-    'am',
-    'sw',
+    'or',
     'userdefault',
     'zh-HK',
     ]
 
 
+def _IsSupportedOmaha2Version(omaha_version):
+  """Returns true if omaha_version is an Omaha 2 version and is supported."""
+  return (omaha_version[0] == 1 and
+          omaha_version[1] == 2 and
+          omaha_version[2] >= 183)
+
+
 # All languages supported by this script currently have the same set of
 # languages, so the omaha_version_info parameter is unused.
 def _GetMetainstallerPayloadFilenames(prefix,
-                                      activex_filename,
+                                      update_plugin_filename,
                                       bho_filename,
                                       languages,
                                       omaha_version):
   """Returns list of metainstaller payload files for specified Omaha version."""
-  if (omaha_version[0] < 1 or
-      omaha_version[1] < 2 or
-      omaha_version[2] < 183):
-    raise Exception('Unsupported version: ' +
-                    ConvertVersionToString(omaha_version))
+  plugin_dll_name = '%s%s' % (prefix, update_plugin_filename)
+  bho_dll_name = '%s%s' % (prefix, bho_filename)
 
+  # The list of files below needs to be kept in sync with the list in
+  # SetupFiles::BuildFileLists().
+  # TODO(omaha): Move the other filename defines in main.scons into this file
+  # and allow all filenames to be customized.  At the moment, while the plugin
+  # names are generated in one place due to version numbers, most of the other
+  # files (googleupdate.exe, goopdateres_*.dll, etc.) are hardcoded all over
+  # the place, and require a ton of point fixes to customize.
   payload_files = [
       'GoogleUpdate.exe',
-      'GoogleCrashHandler.exe',
+      '%s.exe' % _CRASH_HANDLER_NAME,
       '%sgoopdate.dll' % (prefix),
-      '%s%s' % (prefix, activex_filename),  # One-Click DLL
-      '%s%s' % (prefix, bho_filename),      # BHO proxy DLL
+      plugin_dll_name,
+      bho_dll_name,
       'GoogleUpdateHelper.msi',
+      'GoogleUpdateBroker.exe',
+      'GoogleUpdateOnDemand.exe',
+      '%spsmachine.dll' % (prefix),
+      '%spsuser.dll' % (prefix),
       ]
 
+  if (omaha_version[0] == 1 and
+      omaha_version[1] == 3 and
+      omaha_version[2] >= 13):
+    # The BHO is not built yet.
+    payload_files.remove(bho_dll_name)
+  elif _IsSupportedOmaha2Version(omaha_version):
+    payload_files.remove(plugin_dll_name)
+    payload_files.remove('GoogleUpdateBroker.exe')
+    payload_files.remove('GoogleUpdateOnDemand.exe')
+    payload_files.remove('psmachine.dll')
+    payload_files.remove('psuser.dll')
+  else:
+    raise Exception('Unsupported version: ' +
+                    ConvertVersionToString(omaha_version))
+
   for language in languages:
     payload_files += ['%sgoopdateres_%s.dll' % (prefix, language)]
 
@@ -123,9 +155,14 @@
   return '%d.%d.%d.%d' % (version[0], version[1], version[2], version[3])
 
 
-def GetONECLICK_ACTIVEX_NAME():  # pylint: disable-msg=C6409
-  """Returns the value of the ONECLICK_ACTIVEX_NAME define for the C++ code."""
-  return _ONECLICK_ACTIVEX_NAME
+def GetONECLICK_PLUGIN_NAME():  # pylint: disable-msg=C6409
+  """Returns the value of the ONECLICK_PLUGIN_NAME define for the C++ code."""
+  return _ONECLICK_PLUGIN_NAME
+
+
+def GetUPDATE_PLUGIN_NAME():  # pylint: disable-msg=C6409
+  """Returns the value of the UPDATE_PLUGIN_NAME define for the C++ code."""
+  return _UPDATE_PLUGIN_NAME
 
 
 def GetBHO_NAME():  # pylint: disable-msg=C6409
@@ -133,19 +170,33 @@
   return _BHO_NAME
 
 
+def GetCRASH_HANDLER_NAME():  # pylint: disable-msg=C6409
+  """Returns the value of the CRASH_HANDLER_NAME define for the C++ code."""
+  return _CRASH_HANDLER_NAME
+
+
 def GetLanguagesForVersion(omaha_version):
   """Returns a list of languages supported by omaha_version."""
-  if (omaha_version[0] < 1 or
-      omaha_version[1] < 2 or
-      omaha_version[2] < 183):
-    raise Exception('Unsupported version: ' +
-                    ConvertVersionToString(omaha_version))
-
-  supported_languages = _OMAHA_LANGUAGES
+  # Make a copy in case the list is modified below.
+  supported_languages = list(_OMAHA_LANGUAGES)
 
   # When languages are added, add a version check for older versions without the
   # new languages and remove the new languages from supported_languages.
 
+  if (omaha_version[0] == 1 and
+      omaha_version[1] == 3 and
+      omaha_version[2] >= 21):
+    # All languages are supported.
+    pass
+  elif _IsSupportedOmaha2Version(omaha_version):
+    # All current languages are supported. 'or' was also supported.
+    supported_languages += ['or']
+    supported_languages.remove('am')
+    supported_languages.remove('sw')
+  else:
+    raise Exception('Unsupported version: ' +
+                    ConvertVersionToString(omaha_version))
+
   return supported_languages
 
 
@@ -170,7 +221,10 @@
     version_patch: Patch version.
     oneclick_plugin_version: Version of the OneClick plug-in.
     oneclick_plugin_filename: Name of the signed OneClick DLL.
+    update_plugin_version: Version of the Omaha 3 plug-in.
+    update_plugin_filename: Name of the signed Omaha 3 plug-in DLL.
     bho_filename:  Name of the signed BHO DLL.
+    crash_handler_filename: Name of the Crash Handler EXE.
     oneclick_signed_file_info: SignedFileInfo object for the OneClick DLL.
     bho_signed_file_info: SignedFileInfo object for the BHO DLL.
 
@@ -184,15 +238,21 @@
 
     # Objects containing more properties used to build the file.
     self.oneclick_signed_file_info = SignedFileInfo(
-        _ONECLICK_ACTIVEX_NAME,
+        _ONECLICK_PLUGIN_NAME,
         'dll',
         self.oneclick_plugin_version)
+    self.plugin_signed_file_info = SignedFileInfo(
+        _UPDATE_PLUGIN_NAME,
+        'dll',
+        self.update_plugin_version)
     self.bho_signed_file_info = SignedFileInfo(_BHO_NAME, 'dll')
 
     # Simple properties for callers that only need the final filename. Not
     # affected by internal build changes.
     self.oneclick_plugin_filename = self.oneclick_signed_file_info.filename
+    self.update_plugin_filename = self.plugin_signed_file_info.filename
     self.bho_filename = self.bho_signed_file_info.filename
+    self.crash_handler_filename = _CRASH_HANDLER_NAME
 
   def _ReadFile(self, version_file):
     """Reads and stores data from a VERSION file."""
@@ -221,6 +281,15 @@
 
     self.oneclick_plugin_version = oneclick_plugin_version
 
+    # update_plugin_version does not exist in Omaha 2 VERSION file. Handle this.
+    try:
+      self.update_plugin_version = update_plugin_version
+    except NameError:
+      if _IsSupportedOmaha2Version(self.GetVersion()):
+        self.update_plugin_version = -1
+      else:
+        raise
+
     # pylint: enable-msg=E0602
 
   def MakeTestVersion(self, delta=1):
@@ -256,7 +325,7 @@
   def GetMetainstallerPayloadFilenames(self):
     """Returns list of metainstaller payload files for this version of Omaha."""
     return _GetMetainstallerPayloadFilenames(self.filename_prefix,
-                                             self.oneclick_plugin_filename,
+                                             self.update_plugin_filename,
                                              self.bho_filename,
                                              self.GetSupportedLanguages(),
                                              self.GetVersion())
diff --git a/plugins/base/build.scons b/plugins/base/build.scons
new file mode 100644
index 0000000..a2afbeb
--- /dev/null
+++ b/plugins/base/build.scons
@@ -0,0 +1,29 @@
+# Copyright 2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========================================================================
+
+Import('env')
+
+# Build plugin base.
+plugin_base_env = env.Clone()
+# TODO(omaha3): make this configurable for 64-bit support
+plugin_base_env['CCFLAGS'] += ['/D_X86_',]
+plugin_base_env.ComponentLibrary(
+    lib_name='plugin_base',
+    source=[
+      'np_entry.cc',
+      'npn_gate.cc',
+      'npp_gate.cc',
+    ],
+)
diff --git a/plugins/base/np_entry.cc b/plugins/base/np_entry.cc
new file mode 100644
index 0000000..c3ad7b3
--- /dev/null
+++ b/plugins/base/np_entry.cc
@@ -0,0 +1,321 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: NPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the NPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the NPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+//////////////////////////////////////////////////////////////
+//
+// Main plugin entry point implementation -- exports from the
+// plugin library
+//
+
+#include <algorithm>
+#include "omaha/plugins/base/npplat.h"
+#include "omaha/plugins/base/pluginbase.h"
+
+#ifdef _WINDOWS
+#define OSCALL WINAPI
+#else
+#define OSCALL
+#endif
+
+NPNetscapeFuncs NPNFuncs = {sizeof(NPNFuncs)};
+
+NPError OSCALL NP_Shutdown()
+{
+  NS_PluginShutdown();
+  return NPERR_NO_ERROR;
+}
+
+static NPError fillPluginFunctionTable(NPPluginFuncs* aNPPFuncs)
+{
+  if (aNPPFuncs == NULL || aNPPFuncs->size < sizeof(NPPluginFuncs)) {
+    return NPERR_INVALID_FUNCTABLE_ERROR;
+  }
+
+  // Set up the plugin function table that Netscape will use to
+  // call us. Netscape needs to know about our version and size
+  // and have a UniversalProcPointer for every function we implement.
+
+  aNPPFuncs->version       = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
+#ifdef XP_MAC
+  aNPPFuncs->newp          = NewNPP_NewProc(Private_New);
+  aNPPFuncs->destroy       = NewNPP_DestroyProc(Private_Destroy);
+  aNPPFuncs->setwindow     = NewNPP_SetWindowProc(Private_SetWindow);
+  aNPPFuncs->newstream     = NewNPP_NewStreamProc(Private_NewStream);
+  aNPPFuncs->destroystream = NewNPP_DestroyStreamProc(Private_DestroyStream);
+  aNPPFuncs->asfile        = NewNPP_StreamAsFileProc(Private_StreamAsFile);
+  aNPPFuncs->writeready    = NewNPP_WriteReadyProc(Private_WriteReady);
+  aNPPFuncs->write         = NewNPP_WriteProc(Private_Write);
+  aNPPFuncs->print         = NewNPP_PrintProc(Private_Print);
+  aNPPFuncs->event         = NewNPP_HandleEventProc(Private_HandleEvent);
+  aNPPFuncs->urlnotify     = NewNPP_URLNotifyProc(Private_URLNotify);
+  aNPPFuncs->getvalue      = NewNPP_GetValueProc(Private_GetValue);
+  aNPPFuncs->setvalue      = NewNPP_SetValueProc(Private_SetValue);
+#else
+  aNPPFuncs->newp          = NPP_New;
+  aNPPFuncs->destroy       = NPP_Destroy;
+  aNPPFuncs->setwindow     = NPP_SetWindow;
+  aNPPFuncs->newstream     = NPP_NewStream;
+  aNPPFuncs->destroystream = NPP_DestroyStream;
+  aNPPFuncs->asfile        = NPP_StreamAsFile;
+  aNPPFuncs->writeready    = NPP_WriteReady;
+  aNPPFuncs->write         = NPP_Write;
+  aNPPFuncs->print         = NPP_Print;
+  aNPPFuncs->event         = NPP_HandleEvent;
+  aNPPFuncs->urlnotify     = NPP_URLNotify;
+  aNPPFuncs->getvalue      = NPP_GetValue;
+  aNPPFuncs->setvalue      = NPP_SetValue;
+#endif
+#ifdef OJI
+  aNPPFuncs->javaClass     = NULL;
+#endif
+
+  return NPERR_NO_ERROR;
+}
+
+static NPError fillNetscapeFunctionTable(NPNetscapeFuncs* aNPNFuncs)
+{
+  if (aNPNFuncs == NULL) {
+    return NPERR_INVALID_FUNCTABLE_ERROR;
+  }
+
+  if (HIBYTE(aNPNFuncs->version) > NP_VERSION_MAJOR) {
+    return NPERR_INCOMPATIBLE_VERSION_ERROR;
+  }
+
+  memcpy(&NPNFuncs, aNPNFuncs, std::min(static_cast<size_t>(aNPNFuncs->size),
+                                                            sizeof(NPNFuncs)));
+  
+  if (!NPNFuncs.memalloc ||
+      !NPNFuncs.memfree ||
+      !NPNFuncs.createobject ||
+      !NPNFuncs.retainobject ||
+      !NPNFuncs.releaseobject ||
+      !NPNFuncs.utf8fromidentifier ||
+      !NPNFuncs.releasevariantvalue ||
+      !NPNFuncs.getstringidentifier ||
+      !NPNFuncs.getvalue ||
+      !NPNFuncs.setexception ||
+      !NPNFuncs.getproperty ||
+      // Used only by oneclick plugin.
+      !NPNFuncs.invokeDefault) {
+    return NPERR_INVALID_FUNCTABLE_ERROR;
+  }
+
+  return NPERR_NO_ERROR;
+}
+
+//
+// Some exports are different on different platforms
+//
+
+/**************************************************/
+/*                                                */
+/*                   Windows                      */
+/*                                                */
+/**************************************************/
+#ifdef XP_WIN
+
+NPError OSCALL NP_Initialize(NPNetscapeFuncs* aNPNFuncs)
+{
+  NPError rv = fillNetscapeFunctionTable(aNPNFuncs);
+  if(rv != NPERR_NO_ERROR)
+    return rv;
+
+  return NS_PluginInitialize();
+}
+
+NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* aNPPFuncs)
+{
+  return fillPluginFunctionTable(aNPPFuncs);
+}
+
+#endif //XP_WIN
+
+/**************************************************/
+/*                                                */
+/*                    Unix                        */
+/*                                                */
+/**************************************************/
+#ifdef XP_UNIX
+
+NPError NP_Initialize(NPNetscapeFuncs* aNPNFuncs, NPPluginFuncs* aNPPFuncs)
+{
+  NPError rv = fillNetscapeFunctionTable(aNPNFuncs);
+  if(rv != NPERR_NO_ERROR)
+    return rv;
+
+  rv = fillPluginFunctionTable(aNPPFuncs);
+  if(rv != NPERR_NO_ERROR)
+    return rv;
+
+  return NS_PluginInitialize();
+}
+
+char * NP_GetMIMEDescription(void)
+{
+  return NPP_GetMIMEDescription();
+}
+
+NPError NP_GetValue(void *future, NPPVariable aVariable, void *aValue)
+{
+  return NS_PluginGetValue(aVariable, aValue);
+}
+
+#endif //XP_UNIX
+
+/**************************************************/
+/*                                                */
+/*                     Mac                        */
+/*                                                */
+/**************************************************/
+#ifdef XP_MAC
+
+#if !TARGET_API_MAC_CARBON
+QDGlobals* gQDPtr; // Pointer to Netscape's QuickDraw globals
+#endif
+
+short gResFile; // Refnum of the plugin's resource file
+
+NPError Private_Initialize(void)
+{
+  NPError rv = NS_PluginInitialize();
+  return rv;
+}
+
+void Private_Shutdown(void)
+{
+  NS_PluginShutdown();
+  __destroy_global_chain();
+}
+
+void SetUpQD(void);
+
+void SetUpQD(void)
+{
+  ProcessSerialNumber PSN;
+  FSSpec              myFSSpec;
+  Str63               name;
+  ProcessInfoRec      infoRec;
+  OSErr               result = noErr;
+  CFragConnectionID   connID;
+  Str255              errName;
+
+  // Memorize the plugin¹s resource file refnum for later use.
+  gResFile = CurResFile();
+
+#if !TARGET_API_MAC_CARBON
+  // Ask the system if CFM is available.
+  long response;
+  OSErr err = Gestalt(gestaltCFMAttr, &response);
+  Boolean hasCFM = BitTst(&response, 31-gestaltCFMPresent);
+
+  if (hasCFM) {
+    // GetProcessInformation takes a process serial number and
+    // will give us back the name and FSSpec of the application.
+    // See the Process Manager in IM.
+    infoRec.processInfoLength = sizeof(ProcessInfoRec);
+    infoRec.processName = name;
+    infoRec.processAppSpec = &myFSSpec;
+
+    PSN.highLongOfPSN = 0;
+    PSN.lowLongOfPSN = kCurrentProcess;
+
+    result = GetProcessInformation(&PSN, &infoRec);
+  }
+  else
+    // If no CFM installed, assume it must be a 68K app.
+    result = -1;
+
+  if (result == noErr) {
+    // Now that we know the app name and FSSpec, we can call GetDiskFragment
+    // to get a connID to use in a subsequent call to FindSymbol (it will also
+    // return the address of ³main² in app, which we ignore).  If GetDiskFragment
+    // returns an error, we assume the app must be 68K.
+    Ptr mainAddr;
+    result =  GetDiskFragment(infoRec.processAppSpec, 0L, 0L, infoRec.processName,
+                              kReferenceCFrag, &connID, (Ptr*)&mainAddr, errName);
+  }
+
+  if (result == noErr) {
+    // The app is a PPC code fragment, so call FindSymbol
+    // to get the exported ³qd² symbol so we can access its
+    // QuickDraw globals.
+    CFragSymbolClass symClass;
+    result = FindSymbol(connID, "\pqd", (Ptr*)&gQDPtr, &symClass);
+  }
+  else {
+    // The app is 68K, so use its A5 to compute the address
+    // of its QuickDraw globals.
+    gQDPtr = (QDGlobals*)(*((long*)SetCurrentA5()) - (sizeof(QDGlobals) - sizeof(GrafPtr)));
+  }
+#endif /* !TARGET_API_MAC_CARBON */
+}
+
+NPError main(NPNetscapeFuncs* nsTable, NPPluginFuncs* pluginFuncs, NPP_ShutdownUPP* unloadUpp);
+
+#if !TARGET_API_MAC_CARBON
+#pragma export on
+#if GENERATINGCFM
+RoutineDescriptor mainRD = BUILD_ROUTINE_DESCRIPTOR(uppNPP_MainEntryProcInfo, main);
+#endif
+#pragma export off
+#endif /* !TARGET_API_MAC_CARBON */
+
+
+NPError main(NPNetscapeFuncs* aNPNFuncs, NPPluginFuncs* aNPPFuncs, NPP_ShutdownUPP* aUnloadUpp)
+{
+  NPError rv = NPERR_NO_ERROR;
+
+  if (aUnloadUpp == NULL)
+    rv = NPERR_INVALID_FUNCTABLE_ERROR;
+
+  if (rv == NPERR_NO_ERROR)
+    rv = fillNetscapeFunctionTable(aNPNFuncs);
+
+  if (rv == NPERR_NO_ERROR) {
+    // defer static constructors until the global functions are initialized.
+    __InitCode__();
+    rv = fillPluginFunctionTable(aNPPFuncs);
+  }
+
+  *aUnloadUpp = NewNPP_ShutdownProc(Private_Shutdown);
+  SetUpQD();
+  rv = Private_Initialize();
+
+  return rv;
+}
+#endif //XP_MAC
diff --git a/plugins/base/npn_gate.cc b/plugins/base/npn_gate.cc
new file mode 100644
index 0000000..5aee8f3
--- /dev/null
+++ b/plugins/base/npn_gate.cc
@@ -0,0 +1,259 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: NPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the NPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the NPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+////////////////////////////////////////////////////////////
+//
+// Implementation of Netscape entry points (NPN_*)
+//
+#include "omaha/plugins/base/npplat.h"
+
+extern NPNetscapeFuncs NPNFuncs;
+
+/*
+void NPN_Version(int* plugin_major, int* plugin_minor, int* netscape_major, int* netscape_minor)
+{
+  *plugin_major   = NP_VERSION_MAJOR;
+  *plugin_minor   = NP_VERSION_MINOR;
+  *netscape_major = HIBYTE(NPNFuncs.version);
+  *netscape_minor = LOBYTE(NPNFuncs.version);
+}
+
+NPError NPN_GetURLNotify(NPP instance, const char *url, const char *target, void* notifyData)
+{
+  int navMinorVers = NPNFuncs.version & 0xFF;
+  NPError rv = NPERR_NO_ERROR;
+
+  if( navMinorVers >= NPVERS_HAS_NOTIFICATION )
+    rv = CallNPN_GetURLNotifyProc(NPNFuncs.geturlnotify, instance, url, target, notifyData);
+  else
+    rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
+
+  return rv;
+}
+
+NPError NPN_GetURL(NPP instance, const char *url, const char *target)
+{
+  NPError rv = CallNPN_GetURLProc(NPNFuncs.geturl, instance, url, target);
+  return rv;
+}
+
+NPError NPN_PostURLNotify(NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file, void* notifyData)
+{
+  int navMinorVers = NPNFuncs.version & 0xFF;
+  NPError rv = NPERR_NO_ERROR;
+
+  if( navMinorVers >= NPVERS_HAS_NOTIFICATION )
+    rv = CallNPN_PostURLNotifyProc(NPNFuncs.posturlnotify, instance, url, window, len, buf, file, notifyData);
+  else
+    rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
+
+  return rv;
+}
+
+NPError NPN_PostURL(NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file)
+{
+  NPError rv = CallNPN_PostURLProc(NPNFuncs.posturl, instance, url, window, len, buf, file);
+  return rv;
+}
+
+NPError NPN_RequestRead(NPStream* stream, NPByteRange* rangeList)
+{
+  NPError rv = CallNPN_RequestReadProc(NPNFuncs.requestread, stream, rangeList);
+  return rv;
+}
+
+NPError NPN_NewStream(NPP instance, NPMIMEType type, const char* target, NPStream** stream)
+{
+  int navMinorVersion = NPNFuncs.version & 0xFF;
+
+  NPError rv = NPERR_NO_ERROR;
+
+  if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT )
+    rv = CallNPN_NewStreamProc(NPNFuncs.newstream, instance, type, target, stream);
+  else
+    rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
+
+  return rv;
+}
+
+int32 NPN_Write(NPP instance, NPStream *stream, int32 len, void *buffer)
+{
+  int navMinorVersion = NPNFuncs.version & 0xFF;
+  int32 rv = 0;
+
+  if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT )
+    rv = CallNPN_WriteProc(NPNFuncs.write, instance, stream, len, buffer);
+  else
+    rv = -1;
+
+  return rv;
+}
+
+NPError NPN_DestroyStream(NPP instance, NPStream* stream, NPError reason)
+{
+  int navMinorVersion = NPNFuncs.version & 0xFF;
+  NPError rv = NPERR_NO_ERROR;
+
+  if( navMinorVersion >= NPVERS_HAS_STREAMOUTPUT )
+    rv = CallNPN_DestroyStreamProc(NPNFuncs.destroystream, instance, stream, reason);
+  else
+    rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
+
+  return rv;
+}
+
+void NPN_Status(NPP instance, const char *message)
+{
+  CallNPN_StatusProc(NPNFuncs.status, instance, message);
+}
+
+const char* NPN_UserAgent(NPP instance)
+{
+  const char * rv = NULL;
+  rv = CallNPN_UserAgentProc(NPNFuncs.uagent, instance);
+  return rv;
+}
+*/
+
+void* NPN_MemAlloc(uint32 size)
+{
+  void* rv = NULL;
+  return NPNFuncs.memalloc(size);
+}
+
+void NPN_MemFree(void* ptr)
+{
+  NPNFuncs.memfree(ptr);
+}
+
+/*
+uint32 NPN_MemFlush(uint32 size)
+{
+  uint32 rv = CallNPN_MemFlushProc(NPNFuncs.memflush, size);
+  return rv;
+}
+
+void NPN_ReloadPlugins(NPBool reloadPages)
+{
+  CallNPN_ReloadPluginsProc(NPNFuncs.reloadplugins, reloadPages);
+}
+
+#ifdef OJI
+JRIEnv* NPN_GetJavaEnv(void)
+{
+  JRIEnv * rv = NULL;
+  rv = CallNPN_GetJavaEnvProc(NPNFuncs.getJavaEnv);
+  return rv;
+}
+
+jref NPN_GetJavaPeer(NPP instance)
+{
+  jref rv;
+  rv = CallNPN_GetJavaPeerProc(NPNFuncs.getJavaPeer, instance);
+  return rv;
+}
+#endif
+*/
+
+NPError NPN_GetValue(NPP instance, NPNVariable variable, void *value)
+{
+  return NPNFuncs.getvalue(instance, variable, value);
+}
+
+/*
+NPError NPN_SetValue(NPP instance, NPPVariable variable, void *value)
+{
+  NPError rv = CallNPN_SetValueProc(NPNFuncs.setvalue, instance, variable, value);
+  return rv;
+}
+
+void NPN_InvalidateRect(NPP instance, NPRect *invalidRect)
+{
+  CallNPN_InvalidateRectProc(NPNFuncs.invalidaterect, instance, invalidRect);
+}
+
+void NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion)
+{
+  CallNPN_InvalidateRegionProc(NPNFuncs.invalidateregion, instance, invalidRegion);
+}
+
+void NPN_ForceRedraw(NPP instance)
+{
+  CallNPN_ForceRedrawProc(NPNFuncs.forceredraw, instance);
+}
+*/
+
+// Used by the Omaha 3 plugin.
+NPUTF8* NPN_UTF8FromIdentifier(NPIdentifier identifier) {
+  return NPNFuncs.utf8fromidentifier(identifier);
+}
+
+NPObject* NPN_RetainObject(NPObject* obj) {
+  return NPNFuncs.retainobject(obj);
+}
+
+void NPN_ReleaseObject(NPObject* obj) {
+  NPNFuncs.releaseobject(obj);
+}
+
+NPIdentifier NPN_GetStringIdentifier(const NPUTF8* name) {
+  return NPNFuncs.getstringidentifier(name);
+}
+
+NPObject* NPN_CreateObject(NPP npp, NPClass* aClass) {
+  return NPNFuncs.createobject(npp, aClass);
+}
+
+void NPN_SetException(NPObject* obj, const NPUTF8* message) {
+  NPNFuncs.setexception(obj, message);
+}
+
+void NPN_ReleaseVariantValue(NPVariant* variant) {
+  NPNFuncs.releasevariantvalue(variant);
+}
+
+bool NPN_GetProperty(NPP npp, NPObject* obj, NPIdentifier propertyName,
+                     NPVariant* result) {
+  return NPNFuncs.getproperty(npp, obj, propertyName, result);
+}
+
+// Some additional stuff that's used by the oneclick plugin
+bool NPN_InvokeDefault(NPP npp, NPObject* obj, const NPVariant* args,
+                       uint32_t argCount, NPVariant* result) {
+  return NPNFuncs.invokeDefault(npp, obj, args, argCount, result);
+}
diff --git a/plugins/base/npp_gate.cc b/plugins/base/npp_gate.cc
new file mode 100644
index 0000000..ee9da88
--- /dev/null
+++ b/plugins/base/npp_gate.cc
@@ -0,0 +1,358 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: NPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the NPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the NPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+////////////////////////////////////////////////////////////
+//
+// Implementation of plugin entry points (NPP_*)
+//
+#include "omaha/plugins/base/pluginbase.h"
+
+// here the plugin creates a plugin instance object which
+// will be associated with this newly created NPP instance and
+// will do all the neccessary job
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved)
+{
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  NPError rv = NPERR_NO_ERROR;
+
+  // create a new plugin instance object
+  // initialization will be done when the associated window is ready
+  nsPluginCreateData ds;
+
+  ds.instance = instance;
+  ds.type     = pluginType;
+  ds.mode     = mode;
+  ds.argc     = argc;
+  ds.argn     = argn;
+  ds.argv     = argv;
+  ds.saved    = saved;
+
+  nsPluginInstanceBase * plugin = NS_NewPluginInstance(&ds);
+  if(plugin == NULL)
+    return NPERR_OUT_OF_MEMORY_ERROR;
+
+  // associate the plugin instance object with NPP instance
+  instance->pdata = (void *)plugin;
+  return rv;
+}
+
+// here is the place to clean up and destroy the nsPluginInstance object
+NPError NPP_Destroy (NPP instance, NPSavedData** save)
+{
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  NPError rv = NPERR_NO_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin != NULL) {
+    plugin->shut();
+    NS_DestroyPluginInstance(plugin);
+  }
+  return rv;
+}
+
+// during this call we know when the plugin window is ready or
+// is about to be destroyed so we can do some gui specific
+// initialization and shutdown
+NPError NPP_SetWindow (NPP instance, NPWindow* pNPWindow)
+{
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  NPError rv = NPERR_NO_ERROR;
+
+  if(pNPWindow == NULL)
+    return NPERR_GENERIC_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+
+  if(plugin == NULL)
+    return NPERR_GENERIC_ERROR;
+
+  // window just created
+  if(!plugin->isInitialized() && (pNPWindow->window != NULL)) {
+    if(!plugin->init(pNPWindow)) {
+      NS_DestroyPluginInstance(plugin);
+      return NPERR_MODULE_LOAD_FAILED_ERROR;
+    }
+  }
+
+  // window goes away
+  if((pNPWindow->window == NULL) && plugin->isInitialized())
+    return plugin->SetWindow(pNPWindow);
+
+  // window resized?
+  if(plugin->isInitialized() && (pNPWindow->window != NULL))
+    return plugin->SetWindow(pNPWindow);
+
+  // this should not happen, nothing to do
+  if((pNPWindow->window == NULL) && !plugin->isInitialized())
+    return plugin->SetWindow(pNPWindow);
+
+  return rv;
+}
+
+NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype)
+{
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL)
+    return NPERR_GENERIC_ERROR;
+
+  NPError rv = plugin->NewStream(type, stream, seekable, stype);
+  return rv;
+}
+
+int32 NPP_WriteReady (NPP instance, NPStream *stream)
+{
+  if(instance == NULL)
+    return 0x0fffffff;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL)
+    return 0x0fffffff;
+
+  int32 rv = plugin->WriteReady(stream);
+  return rv;
+}
+
+int32 NPP_Write (NPP instance, NPStream *stream, int32 offset, int32 len, void *buffer)
+{
+  if(instance == NULL)
+    return len;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL)
+    return len;
+
+  int32 rv = plugin->Write(stream, offset, len, buffer);
+  return rv;
+}
+
+NPError NPP_DestroyStream (NPP instance, NPStream *stream, NPError reason)
+{
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL)
+    return NPERR_GENERIC_ERROR;
+
+  NPError rv = plugin->DestroyStream(stream, reason);
+  return rv;
+}
+
+void NPP_StreamAsFile (NPP instance, NPStream* stream, const char* fname)
+{
+  if(instance == NULL)
+    return;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL)
+    return;
+
+  plugin->StreamAsFile(stream, fname);
+}
+
+void NPP_Print (NPP instance, NPPrint* printInfo)
+{
+  if(instance == NULL)
+    return;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL)
+    return;
+
+  plugin->Print(printInfo);
+}
+
+void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
+{
+  if(instance == NULL)
+    return;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL)
+    return;
+
+  plugin->URLNotify(url, reason, notifyData);
+}
+
+NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value)
+{
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL)
+    return NPERR_GENERIC_ERROR;
+
+  NPError rv = plugin->GetValue(variable, value);
+  return rv;
+}
+
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value)
+{
+  if(instance == NULL)
+    return NPERR_INVALID_INSTANCE_ERROR;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL)
+    return NPERR_GENERIC_ERROR;
+
+  NPError rv = plugin->SetValue(variable, value);
+  return rv;
+}
+
+int16 NPP_HandleEvent(NPP instance, void* event)
+{
+  if(instance == NULL)
+    return 0;
+
+  nsPluginInstanceBase * plugin = (nsPluginInstanceBase *)instance->pdata;
+  if(plugin == NULL)
+    return 0;
+
+  uint16 rv = plugin->HandleEvent(event);
+  return rv;
+}
+
+#ifdef OJI
+jref NPP_GetJavaClass (void)
+{
+  return NULL;
+}
+#endif
+
+/**************************************************/
+/*                                                */
+/*                     Mac                        */
+/*                                                */
+/**************************************************/
+
+// Mac needs these wrappers, see npplat.h for more info
+
+#ifdef XP_MAC
+
+NPError Private_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved)
+{
+  NPError rv = NPP_New(pluginType, instance, mode, argc, argn, argv, saved);
+  return rv;
+}
+
+NPError Private_Destroy(NPP instance, NPSavedData** save)
+{
+  NPError rv = NPP_Destroy(instance, save);
+  return rv;
+}
+
+NPError Private_SetWindow(NPP instance, NPWindow* window)
+{
+  NPError rv = NPP_SetWindow(instance, window);
+  return rv;
+}
+
+NPError Private_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype)
+{
+  NPError rv = NPP_NewStream(instance, type, stream, seekable, stype);
+  return rv;
+}
+
+int32 Private_WriteReady(NPP instance, NPStream* stream)
+{
+  int32 rv = NPP_WriteReady(instance, stream);
+  return rv;
+}
+
+int32 Private_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer)
+{
+  int32 rv = NPP_Write(instance, stream, offset, len, buffer);
+  return rv;
+}
+
+void Private_StreamAsFile(NPP instance, NPStream* stream, const char* fname)
+{
+  NPP_StreamAsFile(instance, stream, fname);
+}
+
+
+NPError Private_DestroyStream(NPP instance, NPStream* stream, NPError reason)
+{
+  NPError rv = NPP_DestroyStream(instance, stream, reason);
+  return rv;
+}
+
+int16 Private_HandleEvent(NPP instance, void* event)
+{
+  int16 rv = NPP_HandleEvent(instance, event);
+  return rv;
+}
+
+void Private_Print(NPP instance, NPPrint* platformPrint)
+{
+  NPP_Print(instance, platformPrint);
+}
+
+void Private_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
+{
+  NPP_URLNotify(instance, url, reason, notifyData);
+}
+
+jref Private_GetJavaClass(void)
+{
+  return NULL;
+}
+
+NPError Private_GetValue(NPP instance, NPPVariable variable, void *result)
+{
+  NPError rv = NPP_GetValue(instance, variable, result);
+  return rv;
+}
+
+NPError Private_SetValue(NPP instance, NPNVariable variable, void *value)
+{
+  NPError rv = NPP_SetValue(instance, variable, value);
+  return rv;
+}
+
+#endif //XP_MAC
diff --git a/plugins/base/npplat.h b/plugins/base/npplat.h
new file mode 100644
index 0000000..e9700c7
--- /dev/null
+++ b/plugins/base/npplat.h
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: NPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the NPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the NPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef OMAHA_PLUGINS_COMMON_NPPLAT_H_
+#define OMAHA_PLUGINS_COMMON_NPPLAT_H_
+
+/**************************************************/
+/*                                                */
+/*                   Windows                      */
+/*                                                */
+/**************************************************/
+#ifdef XP_WIN
+#include "windows.h"
+#endif //XP_WIN
+
+/**************************************************/
+/*                                                */
+/*                    Unix                        */
+/*                                                */
+/**************************************************/
+#ifdef XP_UNIX
+#include <stdio.h>
+#endif //XP_UNIX
+
+/**************************************************/
+/*                                                */
+/*                     Mac                        */
+/*                                                */
+/**************************************************/
+#ifdef XP_MAC
+
+#include <Processes.h>
+#include <Gestalt.h>
+#include <CodeFragments.h>
+#include <Timer.h>
+#include <Resources.h>
+#include <ToolUtils.h>
+
+#include "jri.h"
+
+// The Mixed Mode procInfos defined in npupp.h assume Think C-
+// style calling conventions.  These conventions are used by
+// Metrowerks with the exception of pointer return types, which
+// in Metrowerks 68K are returned in A0, instead of the standard
+// D0. Thus, since NPN_MemAlloc and NPN_UserAgent return pointers,
+// Mixed Mode will return the values to a 68K plugin in D0, but
+// a 68K plugin compiled by Metrowerks will expect the result in
+// A0.  The following pragma forces Metrowerks to use D0 instead.
+//
+#ifdef __MWERKS__
+#ifndef powerc
+#pragma pointers_in_D0
+#endif
+#endif
+
+#ifdef __MWERKS__
+#ifndef powerc
+#pragma pointers_in_A0
+#endif
+#endif
+
+// The following fix for static initializers (which fixes a preious
+// incompatibility with some parts of PowerPlant, was submitted by
+// Jan Ulbrich.
+#ifdef __MWERKS__
+  #ifdef __cplusplus
+  extern "C" {
+  #endif
+    #ifndef powerc
+      extern void __InitCode__(void);
+    #else
+      extern void __sinit(void);
+      #define __InitCode__ __sinit
+    #endif
+    extern void __destroy_global_chain(void);
+  #ifdef __cplusplus
+  }
+  #endif // __cplusplus
+#endif // __MWERKS__
+
+// Wrapper functions for all calls from Netscape to the plugin.
+// These functions let the plugin developer just create the APIs
+// as documented and defined in npapi.h, without needing to
+// install those functions in the function table or worry about
+// setting up globals for 68K plugins.
+NPError Private_Initialize(void);
+void    Private_Shutdown(void);
+NPError Private_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved);
+NPError Private_Destroy(NPP instance, NPSavedData** save);
+NPError Private_SetWindow(NPP instance, NPWindow* window);
+NPError Private_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype);
+NPError Private_DestroyStream(NPP instance, NPStream* stream, NPError reason);
+int32   Private_WriteReady(NPP instance, NPStream* stream);
+int32   Private_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer);
+void    Private_StreamAsFile(NPP instance, NPStream* stream, const char* fname);
+void    Private_Print(NPP instance, NPPrint* platformPrint);
+int16   Private_HandleEvent(NPP instance, void* event);
+void    Private_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData);
+jref    Private_GetJavaClass(void);
+NPError Private_GetValue(NPP instance, NPPVariable variable, void *result);
+NPError Private_SetValue(NPP instance, NPNVariable variable, void *value);
+
+#endif //XP_MAC
+
+#include "third_party/npapi/bindings/nphostapi.h"
+
+#ifndef HIBYTE
+#define HIBYTE(i) (i >> 8)
+#endif
+
+#ifndef LOBYTE
+#define LOBYTE(i) (i & 0xff)
+#endif
+
+#endif  // OMAHA_PLUGINS_COMMON_NPPLAT_H_
diff --git a/plugins/base/pluginbase.h b/plugins/base/pluginbase.h
new file mode 100644
index 0000000..259bc78
--- /dev/null
+++ b/plugins/base/pluginbase.h
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: NPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the NPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the NPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef OMAHA_PLUGINS_COMMON_PLUGINBASE_H_
+#define OMAHA_PLUGINS_COMMON_PLUGINBASE_H_
+
+#include "omaha/plugins/base/npplat.h"
+
+struct nsPluginCreateData
+{
+  NPP instance;
+  NPMIMEType type;
+  uint16 mode;
+  int16 argc;
+  char** argn;
+  char** argv;
+  NPSavedData* saved;
+};
+
+#pragma warning(push)
+#pragma warning(disable:4100)  // unreferenced formal parameter
+class nsPluginInstanceBase
+{
+public:
+  // these three methods must be implemented in the derived
+  // class platform specific way
+  virtual NPBool init(NPWindow* aWindow) = 0;
+  virtual void shut() = 0;
+  virtual NPBool isInitialized() = 0;
+
+  // implement all or part of those methods in the derived
+  // class as needed
+  virtual NPError SetWindow(NPWindow* pNPWindow)                    { return NPERR_NO_ERROR; }
+  virtual NPError NewStream(NPMIMEType type, NPStream* stream,
+                            NPBool seekable, uint16* stype)         { return NPERR_NO_ERROR; }
+  virtual NPError DestroyStream(NPStream *stream, NPError reason)   { return NPERR_NO_ERROR; }
+  virtual void    StreamAsFile(NPStream* stream, const char* fname) { return; }
+  virtual int32   WriteReady(NPStream *stream)                      { return 0x0fffffff; }
+  virtual int32   Write(NPStream *stream, int32 offset, 
+                        int32 len, void *buffer)                    { return len; }
+  virtual void    Print(NPPrint* printInfo)                         { return; }
+  virtual uint16  HandleEvent(void* event)                          { return 0; }
+  virtual void    URLNotify(const char* url, NPReason reason,
+                            void* notifyData)                       { return; }
+  virtual NPError GetValue(NPPVariable variable, void *value)       { return NPERR_NO_ERROR; }
+  virtual NPError SetValue(NPNVariable variable, void *value)       { return NPERR_NO_ERROR; }
+};
+#pragma warning(pop)
+
+// functions that should be implemented for each specific plugin
+
+// creation and destruction of the object of the derived class
+nsPluginInstanceBase * NS_NewPluginInstance(nsPluginCreateData * aCreateDataStruct);
+void NS_DestroyPluginInstance(nsPluginInstanceBase * aPlugin);
+
+// global plugin initialization and shutdown
+NPError NS_PluginInitialize();
+void NS_PluginShutdown();
+
+#ifdef XP_UNIX
+// global to get plugins name & description
+NPError NS_PluginGetValue(NPPVariable aVariable, void *aValue);
+#endif
+
+#endif  // OMAHA_PLUGINS_COMMON_PLUGINBASE_H_
diff --git a/plugins/baseplugin.cc b/plugins/baseplugin.cc
deleted file mode 100644
index dd3ac71..0000000
--- a/plugins/baseplugin.cc
+++ /dev/null
@@ -1,255 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Base implementation and build blocks for NPAPI Plugins
-// using the new NPRuntime supported by Firefox and others
-//
-// Mozilla doesn't currently have a framework in place for
-// building plug-ins using the new NPRuntime model, so
-// I had to roll my own.
-
-#include "plugins/baseplugin.h"
-#include "omaha/third_party/gecko/include/npupp.h"
-
-BasePlugin::BasePlugin(NPP npp)
-    : npp_(npp) {
-  NPN_GetValue(npp_, NPNVWindowNPObject, &window_object_);
-  NPN_RetainObject(window_object_);
-}
-
-BasePlugin::~BasePlugin() {
-  if (window_object_) {
-    NPN_ReleaseObject(window_object_);
-  }
-}
-
-void BasePlugin::InitializePluginMap() {
-  // Go through the list of IDs
-  PLUGIN_MAP* pDispMap = GetPluginMap();
-  PLUGIN_MAP_ENTRY* pEntry;
-
-  if (pDispMap != NULL) {
-    // populate all entries
-    pEntry = pDispMap->entries;
-    while (pEntry->name != NULL) {
-      pEntry->np_id = NPN_GetStringIdentifier(pEntry->name);
-      ++pEntry;
-    }
-  }
-}
-
-PLUGIN_MAP_ENTRY* BasePlugin::GetPluginEntry(NPIdentifier np_id)  {
-  PLUGIN_MAP* pDispMap = GetPluginMap();
-  PLUGIN_MAP_ENTRY* pEntry;
-
-  if (pDispMap != NULL) {
-    // search for entry
-    pEntry = pDispMap->entries;
-    while (pEntry->np_id != NULL) {
-      if (pEntry->np_id == np_id) {
-        return pEntry;
-      }
-      ++pEntry;
-    }
-  }
-
-  return NULL;  // name not found
-}
-
-bool BasePlugin::HasMethod(NPIdentifier name)  {
-  PLUGIN_MAP_ENTRY* pEntry = GetPluginEntry(name);
-  return pEntry && !pEntry->function_get;
-}
-
-bool BasePlugin::InvokeDefault(const NPVariant *args,
-                               uint32_t argument_count,
-                               NPVariant *result) {
-  UNREFERENCED_PARAMETER(args);
-  UNREFERENCED_PARAMETER(argument_count);
-  UNREFERENCED_PARAMETER(result);
-  return false;
-}
-
-
-bool BasePlugin::Invoke(NPIdentifier name,
-                        const NPVariant *args,
-                        uint32_t argument_count,
-                        NPVariant *result)  {
-  // get entry for the member ID
-  PLUGIN_MAP_ENTRY* pEntry = GetPluginEntry(name);
-  if (pEntry == NULL || pEntry->function_get) {
-    return false;
-  }
-
-  // do standard method call
-  return (this->*(pEntry->function))(args, argument_count, result);
-}
-
-bool BasePlugin::HasProperty(NPIdentifier name)  {
-  PLUGIN_MAP_ENTRY* pEntry = GetPluginEntry(name);
-  return pEntry && pEntry->function_get;
-}
-
-bool BasePlugin::GetProperty(NPIdentifier name, NPVariant *result) {
-  PLUGIN_MAP_ENTRY* pEntry = GetPluginEntry(name);
-  if (pEntry == NULL || !pEntry->function_get) {
-    return false;
-  }
-
-  // do standard method call
-  return (this->*(pEntry->function_get))(result);
-}
-
-bool BasePlugin::SetProperty(NPIdentifier name,
-                             const NPVariant *value) {
-  // get entry for the member ID
-  PLUGIN_MAP_ENTRY* pEntry = GetPluginEntry(name);
-  if (pEntry == NULL || !pEntry->function_get) {
-    return false;
-  }
-
-  // do standard method call
-  return (this->*(pEntry->function_set))(value);
-}
-
-
-void BasePlugin::Invalidate() {
-}
-
-bool BasePlugin::RemoveProperty(NPIdentifier name)  {
-  UNREFERENCED_PARAMETER(name);
-  return false;
-}
-
-void BasePlugin::Shutdown() {
-}
-
-// static
-void BasePlugin::_Deallocate(NPObject *npobj) {
-  // Call the virtual destructor.
-  delete static_cast<BasePlugin *>(npobj);
-}
-
-// static
-void BasePlugin::_Invalidate(NPObject *npobj) {
-  (static_cast<BasePlugin *>(npobj))->Invalidate();
-}
-
-// static
-bool BasePlugin::_HasMethod(NPObject *npobj, NPIdentifier name)  {
-  return (static_cast<BasePlugin *>(npobj))->HasMethod(name);
-}
-
-// static
-bool BasePlugin::_Invoke(NPObject *npobj,
-                         NPIdentifier name,
-                         const NPVariant *args,
-                         uint32_t argument_count,
-                         NPVariant *result)  {
-  return (static_cast<BasePlugin *>(npobj))->Invoke(
-      name,
-      args,
-      argument_count,
-      result);
-}
-
-// static
-bool BasePlugin::_InvokeDefault(NPObject *npobj,
-                                const NPVariant *args,
-                                uint32_t argument_count,
-                                NPVariant *result) {
-  return (static_cast<BasePlugin *>(npobj))->InvokeDefault(
-      args,
-      argument_count,
-      result);
-}
-
-// static
-bool BasePlugin::_HasProperty(NPObject * npobj, NPIdentifier name)  {
-  return (static_cast<BasePlugin *>(npobj))->HasProperty(name);
-}
-
-// static
-bool BasePlugin::_GetProperty(NPObject *npobj,
-                              NPIdentifier name,
-                              NPVariant *result) {
-  return (static_cast<BasePlugin *>(npobj))->GetProperty(name, result);
-}
-
-// static
-bool BasePlugin::_SetProperty(NPObject *npobj,
-                              NPIdentifier name,
-                              const NPVariant *value)  {
-  return (static_cast<BasePlugin *>(npobj))->SetProperty(name, value);
-}
-
-// static
-bool BasePlugin::_RemoveProperty(NPObject *npobj, NPIdentifier name)  {
-  return (static_cast<BasePlugin *>(npobj))->RemoveProperty(name);
-}
-
-CPlugin::CPlugin(NPP pNPInstance)
-    : np_instance_(pNPInstance),
-      bInitialized_(FALSE),
-      scriptable_object_(NULL)  {
-}
-
-CPlugin::~CPlugin() {
-  if (scriptable_object_) {
-    NPN_ReleaseObject(scriptable_object_);
-  }
-}
-
-NPBool CPlugin::init(NPWindow* np_window) {
-  bInitialized_ = TRUE;
-  NPBool returnvalue = TRUE;
-  if (the_npclass_staticinit) {
-      returnvalue = (*the_npclass_staticinit)
-          (np_instance_, np_window)? TRUE : FALSE;
-  }
-
-  return returnvalue;
-}
-
-void CPlugin::shut()  {
-  if (scriptable_object_) {
-    (static_cast<BasePlugin*>(scriptable_object_))->Shutdown();
-  }
-  bInitialized_ = FALSE;
-}
-
-NPBool CPlugin::isInitialized() {
-  return bInitialized_;
-}
-
-int16 CPlugin::handleEvent(void* event) {
-  UNREFERENCED_PARAMETER(event);
-  return 0;
-}
-
-NPObject* CPlugin::GetScriptableObject()  {
-  if (!scriptable_object_) {
-    scriptable_object_ = NPN_CreateObject(
-        np_instance_,
-        &CPlugin::the_npclass);
-  }
-
-  if (scriptable_object_) {
-    NPN_RetainObject(scriptable_object_);
-  }
-
-  return scriptable_object_;
-}
-
diff --git a/plugins/baseplugin.h b/plugins/baseplugin.h
deleted file mode 100644
index 2f3d41d..0000000
--- a/plugins/baseplugin.h
+++ /dev/null
@@ -1,229 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Base implementation and build blocks for NPAPI Plugins
-// using the new NPRuntime supported by Firefox and others
-//
-// Mozilla doesn't currently have a framework in place for
-// building plug-ins using the new NPRuntime model, so
-// I had to roll my own.
-//
-// TODO(omaha) - better document this code so other Google
-// projects can use this framework.
-
-#ifndef OMAHA_PLUGINS_BASEPLUGIN_H__
-#define OMAHA_PLUGINS_BASEPLUGIN_H__
-
-#include <windows.h>
-#include <windowsx.h>
-#include "omaha/third_party/gecko/include/npapi.h"
-#include "omaha/third_party/gecko/include/npruntime.h"
-
-class BasePlugin;
-typedef bool (BasePlugin::*PLUGIN_FUNC)(
-  const NPVariant *args, uint32_t argument_count, NPVariant *result);
-typedef bool (BasePlugin::*PLUGIN_PROPGET)(NPVariant *result);
-typedef bool (BasePlugin::*PLUGIN_PROPSET)(const NPVariant *value);
-typedef NPBool (*PLUGIN_STATIC_INIT)(NPP np_instance, NPWindow* np_window);
-
-struct PLUGIN_MAP_ENTRY {
-  LPCSTR name;                  // member/property name
-  NPIdentifier np_id;           // NPIdentifier
-  PLUGIN_FUNC function;         // member Function
-  PLUGIN_PROPGET function_get;  // member for Get<property>
-  PLUGIN_PROPSET function_set;  // member for Set<property>
-  uint32_t argument_count;      // argument_count for Invoke()
-};
-
-struct PLUGIN_MAP {
-  PLUGIN_MAP_ENTRY* entries;
-};
-
-class CPlugin {
- private:
-  NPP np_instance_;
-  NPBool bInitialized_;
-  NPObject *scriptable_object_;
-
- public:
-  explicit CPlugin(NPP pNPInstance);
-  ~CPlugin();
-
-  NPBool init(NPWindow* np_window);
-  void shut();
-  NPBool isInitialized();
-
-  int16 handleEvent(void* event);
-
-  NPObject *GetScriptableObject();
-  static NPClass the_npclass;
-  static PLUGIN_STATIC_INIT the_npclass_staticinit;
-};
-
-
-
-// Helper class that can be used to map calls to the NPObject hooks
-// into virtual methods on instances of classes that derive from this
-// class.
-class BasePlugin : public NPObject  {
- public:
-  explicit BasePlugin(NPP npp);
-  virtual ~BasePlugin();
-
-  // Virtual functions that a derived class can override
-  virtual void Invalidate();
-  virtual bool HasMethod(NPIdentifier name);
-  virtual bool Invoke(NPIdentifier name, const NPVariant *args,
-                      uint32_t argument_count, NPVariant *result);
-  virtual bool InvokeDefault(const NPVariant *args, uint32_t argument_count,
-                             NPVariant *result);
-  virtual bool HasProperty(NPIdentifier name);
-  virtual bool GetProperty(NPIdentifier name, NPVariant *result);
-  virtual bool SetProperty(NPIdentifier name, const NPVariant *value);
-  virtual bool RemoveProperty(NPIdentifier name);
-  virtual void Shutdown();
-
-  virtual PLUGIN_MAP_ENTRY* GetPluginEntry(NPIdentifier np_id);
-
- protected:
-  // Virtual functions that a derived class can override
-  virtual PLUGIN_MAP* GetPluginMap() {return NULL;}
-
-  // Helpers:
-  void InitializePluginMap();
-  NPObject* window_object() {return window_object_;}
-
- public:
-  // Statics
-  static void _Deallocate(NPObject *npobj);
-  static void _Invalidate(NPObject *npobj);
-  static bool _HasMethod(NPObject *npobj, NPIdentifier name);
-  static bool _Invoke(NPObject *npobj, NPIdentifier name,
-                      const NPVariant *args, uint32_t argument_count,
-                      NPVariant *result);
-  static bool _InvokeDefault(NPObject *npobj, const NPVariant *args,
-                             uint32_t argument_count, NPVariant *result);
-  static bool _HasProperty(NPObject * npobj, NPIdentifier name);
-  static bool _GetProperty(NPObject *npobj, NPIdentifier name,
-                           NPVariant *result);
-  static bool _SetProperty(NPObject *npobj, NPIdentifier name,
-                           const NPVariant *value);
-  static bool _RemoveProperty(NPObject *npobj, NPIdentifier name);
-
- protected:
-  NPP npp_;
-  NPObject *window_object_;
-};
-
-// Only one of these can be declared:
-#define DECLARE_CPLUGIN_NPCLASS() \
-  static NPBool init(NPP np_instance, NPWindow* np_window);
-
-#define DEFINE_CPLUGIN_NPCLASS(the_class) \
-  NPClass CPlugin::the_npclass = the_class::get_NPClass(); \
-  PLUGIN_STATIC_INIT CPlugin::the_npclass_staticinit = the_class::init; \
-  NPBool the_class::init(NPP np_instance, NPWindow* np_window)  {            \
-    UNREFERENCED_PARAMETER(np_instance);                                     \
-    UNREFERENCED_PARAMETER(np_window);                                       \
-
-#define END_DEFINE_CPLUGIN_NPCLASS() \
-  }
-
-#define DECLARE_PLUGIN_CLASS(the_class)                                      \
-class the_class : public BasePlugin {                                        \
-private:                                                                     \
-  static PLUGIN_MAP_ENTRY plugin_entries_[];                                 \
-  static NPClass the_class##_NPClass;                                        \
-protected:                                                                   \
-  virtual PLUGIN_MAP* GetPluginMap();                                        \
-  static PLUGIN_MAP plugin_map;                                              \
-  static NPObject* AllocateScriptablePluginObject(NPP npp,                   \
-                       NPClass *theclass) {                                  \
-    UNREFERENCED_PARAMETER(theclass);                                        \
-    return new the_class(npp);                                               \
-  }                                                                          \
-public:                                                                      \
-  static NPClass& get_NPClass() {return the_class##_NPClass;}                \
-  explicit the_class(NPP npp);                                               \
-  virtual ~the_class();                                                      \
-
-#define DECLARE_PLUGIN_FUNCTION(member_function)                             \
-  bool member_function(const NPVariant *args,                                \
-           uint32_t argument_count, NPVariant *result);
-
-#define DECLARE_PLUGIN_PROPERTY(member_function)     \
-  bool Get##member_function(NPVariant *result);      \
-  bool Set##member_function(const NPVariant *value);
-
-#define END_DECLARE_PLUGIN_CLASS(the_class)    \
-  };
-
-// Macros to define plugin methods
-#define BEGIN_PLUGIN_CLASS(the_class)                         \
-  NPClass the_class::the_class##_NPClass = {                  \
-  NP_CLASS_STRUCT_VERSION,                                    \
-  AllocateScriptablePluginObject,                             \
-  BasePlugin::_Deallocate,                                    \
-  BasePlugin::_Invalidate,                                    \
-  BasePlugin::_HasMethod,                                     \
-  BasePlugin::_Invoke,                                        \
-  BasePlugin::_InvokeDefault,                                 \
-  BasePlugin::_HasProperty,                                   \
-  BasePlugin::_GetProperty,                                   \
-  BasePlugin::_SetProperty,                                   \
-  BasePlugin::_RemoveProperty                                 \
-};                                                            \
-PLUGIN_MAP* the_class::GetPluginMap() {                       \
-  static bool initialized = false;                            \
-  if (!initialized) {                                         \
-    initialized = true;                                       \
-    InitializePluginMap();                                    \
-  }                                                           \
-  return &the_class::plugin_map;                              \
-  }                                                           \
-PLUGIN_MAP the_class::plugin_map =                            \
-  { &the_class::plugin_entries_[0] };                         \
-PLUGIN_MAP_ENTRY the_class::plugin_entries_[] =  {            \
-                                                                    // NO_LINT
-#define PLUGIN_FUNCTION(the_class, member_function, argument_count)       \
-  { #member_function, NULL,                                               \
-    (PLUGIN_FUNC)&member_function, NULL, NULL, argument_count             \
-    },
-
-#define PLUGIN_PROPERTY(the_class, member_function)                       \
-  { #member_function, NULL,                                               \
-  NULL, (PLUGIN_PROPGET)&Get##member_function,                            \
-  (PLUGIN_PROPSET)&Set##member_function,                                  \
-    0},
-
-#define END_BEGIN_PLUGIN_CLASS \
-  { NULL, NULL,                \
-    NULL, NULL, NULL, 0,       \
-    }, };
-
-#define BEGIN_PLUGIN_CLASS_CTOR(the_class)                                   \
-  the_class::the_class(NPP npp) : BasePlugin(npp) {                          \
-                                                                    // NO_LINT
-#define END_PLUGIN_CLASS_CTOR                                                \
-  }
-
-#define BEGIN_PLUGIN_CLASS_DTOR(the_class)                                   \
-  the_class::~the_class() {                                                  \
-                                                                    // NO_LINT
-#define END_PLUGIN_CLASS_DTOR                                                \
-  }
-
-#endif  // OMAHA_PLUGINS_BASEPLUGIN_H__
-
diff --git a/plugins/build.scons b/plugins/build.scons
index a4d419f..15a2345 100644
--- a/plugins/build.scons
+++ b/plugins/build.scons
@@ -15,212 +15,7 @@
 # limitations under the License.
 # ========================================================================
 
-
 Import('env')
 
-for omaha_version_info in env['omaha_versions_info']:
-  version_string = omaha_version_info.GetVersionString()
-  prefix = omaha_version_info.filename_prefix
-
-  temp_env = env.Clone(COMPONENT_STATIC = False)
-
-  if prefix == 'TEST_':
-    temp_env['OBJPREFIX'] = temp_env.subst('test/$OBJPREFIX')
-  elif prefix:
-    raise Exception('ERROR: Unrecognized prefix "%s"' % prefix)
-
-  temp_env.Append(
-      RCFLAGS = [
-          '/DVERSION_MAJOR=%d' % omaha_version_info.version_major,
-          '/DVERSION_MINOR=%d' % omaha_version_info.version_minor,
-          '/DVERSION_BUILD=%d' % omaha_version_info.version_build,
-          '/DVERSION_PATCH=%d' % omaha_version_info.version_patch,
-          '/DVERSION_NUMBER_STRING=\\"%s\\"' % version_string,
-          ],
-
-      CPPDEFINES = [
-          '_ATL_APARTMENT_THREADED',
-          'VERSION_NUMBER_STRING=\\"%s\\"' % version_string,
-          ],
-
-      CPPPATH = [
-          '$OBJ_ROOT',
-          ],
-
-      LIBS = [
-          '$LIB_DIR/breakpad.lib',
-          '$LIB_DIR/common.lib',
-          '$LIB_DIR/goopdate_dll.lib',
-          '$LIB_DIR/logging.lib',
-          '$LIB_DIR/npOneClick_dll.lib',
-          '$LIB_DIR/npOneClick_dll_no_pch.lib',
-          '$LIB_DIR/npOneClickPlugin.lib',
-          '$LIB_DIR/net.lib',
-          '$LIB_DIR/security.lib',
-          '$LIB_DIR/statsreport.lib',
-          '$LIB_DIR/worker.lib',
-
-          ('atls.lib', 'atlsd.lib')[temp_env.Bit('debug')],
-          ('libcmt.lib', 'libcmtd.lib')[temp_env.Bit('debug')],
-          ('libcpmt.lib', 'libcpmtd.lib')[temp_env.Bit('debug')],
-          'comctl32.lib',
-          'crypt32.lib',
-          'Iphlpapi.lib',
-          'Mstask.lib',
-          'Netapi32.lib',
-          'psapi.lib',
-          'rasapi32.lib',
-          'shlwapi.lib',
-          'Version.lib',
-          'urlmon.lib',
-          'userenv.lib',
-          'wininet.lib',
-          'Wintrust.lib',
-          'WtsApi32.lib',
-          ],
-  )
-
-  resource_res = temp_env.RES(
-      target=prefix + 'resource.res',
-      source='resource.rc',
-  )
-
-  # Force a rebuild when the version changes.
-  temp_env.Depends(resource_res,
-                   ['$MAIN_DIR/VERSION', '$OBJ_ROOT/plugins/oneclick_idl.tlb'])
-
-  # TODO(Omaha): Remove plugin dependencies on goopdate and all its
-  # dependencies.
-
-  target_name = (prefix +
-                 omaha_version_info.oneclick_signed_file_info.unsigned_filename)
-
-  temp_inputs = [
-      'dllmain.cc',
-      'oneclick.def',
-      resource_res,
-      ]
-
-  if env.Bit('use_precompiled_headers'):
-    pch_obj = temp_env.EnablePrecompile(target_name)
-
-    # Force a rebuild of the precompiled headers when the version changes.
-    # This is necessary in this project because VERSION_NUMBER_STRING is added
-    # to the CL flags whereas for other projects it is only in the RC flags.
-    temp_env.Depends(pch_obj, '$MAIN_DIR/VERSION')
-
-    temp_inputs += [pch_obj]
-
-
-  unsigned_dll = temp_env.ComponentLibrary(
-      lib_name=target_name,
-      source=temp_inputs
-  )
-
-  signed_dll = temp_env.SignedBinary(
-      target=prefix + omaha_version_info.oneclick_signed_file_info.filename,
-      source=unsigned_dll,
-  )
-
-  env.Replicate('$STAGING_DIR', signed_dll)
-  env.Replicate('$STAGING_DIR', [f for f in unsigned_dll if f.suffix == '.pdb'])
-
-
-#
-# Generate oneclick_idl.idl. The output is an IDL file with a variant CLSID
-# for coclass GoopdateOneClickControl.
-#
-generated_idl = env.Command(
-    target='oneclick_idl.idl',
-    source='$MAIN_DIR/plugins/oneclick_idl.idl',
-    action=('python %s/plugins/generate_oneclick_idl.py --idl_template_file '
-        '$SOURCE --idl_output_file $TARGET' % env['MAIN_DIR'])
-)
-
-
-#
-# Build the type library
-#
-midl_env = env.Clone()
-midl_env.Tool('midl')
-
-midl_env['MIDLFLAGS'] += [
-    '/Oicf',  # generate optimized stubless proxy/stub code
-    ]
-
-# Create the type library, taking the generated idl file as input
-midl_env.TypeLibrary(generated_idl)
-
-
-#
-# Generate the GUIDs with no precompile option set, otherwise we get an error.
-#
-no_precomp_env = env.Clone()
-no_precomp_env.Append(
-    CCFLAGS = [
-        '/wd4255',  # no function prototype given: converting '()' to '(void)'
-        ],
-    CPPPATH = [
-        '$OBJ_ROOT',  # Needed for generated files
-        ],
-)
-
-no_precomp_env.ComponentLibrary(
-    lib_name='npOneClick_dll_no_pch.lib',
-    source='$OBJ_ROOT/plugins/oneclick_idl_i.c',
-)
-
-
-#
-# Build npOneClick_dll.lib
-#
-one_click_env = env.Clone()
-one_click_env.Append(
-    CCFLAGS = [
-        '/wd4946',  # reinterpret_cast used between related classes
-        ],
-    CPPDEFINES = [
-        '_ATL_APARTMENT_THREADED',
-        ],
-    CPPPATH = [
-        '$OBJ_ROOT',  # Needed for generated files
-        ],
-)
-
-target_name = 'npOneClick_dll.lib'
-
-one_click_inputs = [
-    'oneclick.cc',
-    'oneclick_browser_callback_activex.cc',
-    'oneclick_worker.cc',
-    'oneclick_atl_module.cc',
-    ]
-if env.Bit('use_precompiled_headers'):
-  one_click_inputs += one_click_env.EnablePrecompile(target_name)
-
-one_click_env.ComponentLibrary(
-    lib_name=target_name,
-    source=one_click_inputs,
-)
-
-
-#
-# Build npOneClickPlugin.lib
-#
-ocp_env = env.Clone()
-ocp_env['CCFLAGS'] += [
-    '/D_X86_',  # needed for NPAPI code to build NT headers properly
-    ]
-
-ocp_env.ComponentLibrary(
-    lib_name='npOneClickPlugin.lib',
-    source=[
-        'np_entry.cc',
-        'baseplugin.cc',
-        'npOneClick.cc',
-        'npn_gate.cc',
-        'npp_gate.cc',
-        'oneclick_browser_callback_npapi.cc',
-        ]
-)
-
+env.BuildSConscript('base')
+env.BuildSConscript('update')
diff --git a/plugins/dllmain.cc b/plugins/dllmain.cc
deleted file mode 100644
index 8b662ba..0000000
--- a/plugins/dllmain.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// DLLMain boilerplate
-//
-
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-
-// Force the linker to include the ATLModule instance
-// (see plugins/oneclick_atl_module.cpp)
-#pragma comment(linker, "/INCLUDE:__AtlModule")
-
-// Force the linker to include the object map definition. Each class that
-// requires COM registration must be added here, otherwise its object entry
-// will be missing from the object map.
-#pragma comment(linker, "/INCLUDE:___pobjMap_GoopdateCtrl")
-
-void OneClickOutOfMemoryHandler() {
-  ::RaiseException(EXCEPTION_ACCESS_VIOLATION,
-                   EXCEPTION_NONCONTINUABLE,
-                   0,
-                   NULL);
-}
-
-extern "C" {
-DLLEXPORT
-BOOL WINAPI DllMain(HINSTANCE, DWORD reason, LPVOID) {
-  switch (reason) {
-    case DLL_PROCESS_ATTACH:
-      VERIFY1(set_new_handler(&OneClickOutOfMemoryHandler) == 0);
-      break;
-
-    case DLL_THREAD_ATTACH:
-      break;
-
-    case DLL_THREAD_DETACH:
-      break;
-
-    case DLL_PROCESS_DETACH:
-      break;
-
-    default:
-      break;
-  }
-  return TRUE;
-}
-}
diff --git a/plugins/npOneClick.cc b/plugins/npOneClick.cc
deleted file mode 100644
index 926903b..0000000
--- a/plugins/npOneClick.cc
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// The OneClick NPAPI Plugin implementation
-// using the new NPRuntime supported by Firefox and others
-//
-//
-
-#pragma warning(push)
-#pragma warning(disable : 4201 4265)
-// 4201: nonstandard extension used : nameless struct/union
-// 4265: class has virtual functions, but destructor is not virtual
-
-#include "base/scoped_ptr.h"
-#include "omaha/plugins/npOneClick.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/string.h"
-#include "omaha/plugins/oneclick_browser_callback_npapi.h"
-#include "omaha/plugins/oneclick_worker.h"
-
-#pragma warning(pop)
-
-using omaha::ScopeGuard;
-using omaha::MakeGuard;
-
-// Convert NPString to a const wide string.
-CString ConvertNPStringToString(const NPString utf8_str) {
-  CString utf16_str;
-  if (!utf8_str.utf8characters || !utf8_str.utf8length) {
-    return utf16_str;
-  }
-
-  int len = ::MultiByteToWideChar(CP_UTF8,
-                                  0,
-                                  utf8_str.utf8characters,
-                                  utf8_str.utf8length,
-                                  NULL,
-                                  0);
-  ATLASSERT(len);
-  if (!len) {
-    return utf16_str;
-  }
-  ATLVERIFY(::MultiByteToWideChar(CP_UTF8,
-                                  0,
-                                  utf8_str.utf8characters,
-                                  utf8_str.utf8length,
-                                  CStrBuf(utf16_str, len, CStrBuf::SET_LENGTH),
-                                  len));
-
-  PLUGIN_LOG(L2, (_T("[ConvertNPStringToString][%s]"), utf16_str));
-  return utf16_str;
-}
-
-// The primary plug-in class, NPOneClickClass.
-BEGIN_PLUGIN_CLASS(NPOneClickClass)
-  PLUGIN_FUNCTION(NPOneClickClass, Install, 3)
-  PLUGIN_FUNCTION(NPOneClickClass, Install2, 1)
-  PLUGIN_FUNCTION(NPOneClickClass, GetInstalledVersion, 2)
-  PLUGIN_FUNCTION(NPOneClickClass, GetOneClickVersion, 0)
-END_BEGIN_PLUGIN_CLASS
-
-DEFINE_CPLUGIN_NPCLASS(NPOneClickClass)
-  PLUGIN_LOG(L2, (_T("[NPOneClickClass::Static constructor]")));
-  return TRUE;
-END_DEFINE_CPLUGIN_NPCLASS()
-
-BEGIN_PLUGIN_CLASS_CTOR(NPOneClickClass)
-  PLUGIN_LOG(L2, (_T("[NPOneClickClass::constructor]")));
-
-  is_worker_url_set_ = false;
-  oneclick_worker_.reset(new omaha::OneClickWorker());
-  RETTRACE_IF_FAILED(oneclick_worker_->Initialize());
-END_PLUGIN_CLASS_CTOR
-
-BEGIN_PLUGIN_CLASS_DTOR(NPOneClickClass)
-  PLUGIN_LOG(L2, (_T("[NPOneClickClass::destructor]")));
-END_PLUGIN_CLASS_DTOR
-
-void NPOneClickClass::Shutdown() {
-  VERIFY1(SUCCEEDED(oneclick_worker_->Shutdown()));
-}
-
-HRESULT NPOneClickClass::EnsureWorkerUrlSet() {
-  if (is_worker_url_set_) {
-    return S_OK;
-  }
-
-  CString browser_url_str;
-  HRESULT hr = GetUrl(&browser_url_str);
-  if (FAILED(hr)) {
-    PLUGIN_LOG(LE, (_T("[GetUrl failed][0x%x]"), hr));
-    return hr;
-  }
-
-  oneclick_worker_->set_browser_url(browser_url_str);
-  is_worker_url_set_ = true;
-  return S_OK;
-}
-
-// STDMETHOD(Install)(BSTR cmd_line_args,
-//                    VARIANT* success_callback,
-//                    VARIANT* failure_callback);
-bool NPOneClickClass::Install(const NPVariant* args,
-                              uint32_t argCount,
-                              NPVariant* result) {
-  UNREFERENCED_PARAMETER(result);
-
-  PLUGIN_LOG(L2, (_T("[NPOneClickClass::Install]")));
-  RETTRACE_EXCEPTION_IF_FAILED(EnsureWorkerUrlSet());
-
-  RETTRACE_EXCEPTION_IF_FALSE(argCount == 3 &&
-                              args &&
-                              NPVARIANT_IS_STRING(args[0]));
-
-  omaha::OneClickBrowserCallbackNpapi browser_callback;
-  RETTRACE_EXCEPTION_IF_FAILED(browser_callback.Initialize(npp_,
-                                                           args[1],
-                                                           args[2]));
-
-  RETTRACE_EXCEPTION_IF_FAILED(oneclick_worker_->DoOneClickInstall(
-      ConvertNPStringToString(NPVARIANT_TO_STRING(args[0])),
-      &browser_callback));
-
-  return true;
-}
-
-// STDMETHOD(Install2)(BSTR extra_args);
-bool NPOneClickClass::Install2(const NPVariant* args,
-                               uint32_t argCount,
-                               NPVariant* result) {
-  UNREFERENCED_PARAMETER(result);
-
-  PLUGIN_LOG(L2, (_T("[NPOneClickClass::Install2]")));
-  RETTRACE_EXCEPTION_IF_FAILED(EnsureWorkerUrlSet());
-
-  RETTRACE_EXCEPTION_IF_FALSE(argCount == 1 &&
-                              args &&
-                              NPVARIANT_IS_STRING(args[0]));
-
-  RETTRACE_EXCEPTION_IF_FAILED(oneclick_worker_->DoOneClickInstall2(
-      ConvertNPStringToString(NPVARIANT_TO_STRING(args[0]))));
-
-  return true;
-}
-
-// STDMETHOD(GetInstalledVersion)(BSTR guid_string,
-//                                VARIANT_BOOL is_machine,
-//                                BSTR* version_string);
-bool NPOneClickClass::GetInstalledVersion(const NPVariant* args,
-                                          uint32_t argCount,
-                                          NPVariant* result) {
-  PLUGIN_LOG(L2, (_T("[NPOneClickClass::GetInstalledVersion]")));
-  RETTRACE_EXCEPTION_IF_FAILED(EnsureWorkerUrlSet());
-
-  NULL_TO_NPVARIANT(*result);
-  RETTRACE_EXCEPTION_IF_FALSE(argCount == 2 &&
-                              args &&
-                              NPVARIANT_IS_STRING(args[0]) &&
-                              NPVARIANT_IS_BOOLEAN(args[1]));
-
-  CString version;
-  RETTRACE_EXCEPTION_IF_FAILED(oneclick_worker_->GetInstalledVersion(
-      ConvertNPStringToString(NPVARIANT_TO_STRING(args[0])),
-      NPVARIANT_TO_BOOLEAN(args[1]),
-      &version));
-
-  size_t version_length = version.GetLength() + 1;
-  RETTRACE_EXCEPTION_IF_FALSE(version_length);
-  char* version_out = reinterpret_cast<char *>(NPN_MemAlloc(version_length));
-  RETTRACE_EXCEPTION_IF_FALSE(version_out);
-  RETTRACE_EXCEPTION_IF_FALSE(
-      ::lstrcpynA(version_out, CStringA(version), version_length));
-
-  PLUGIN_LOG(L2, (_T("[GetInstalledVersion][%S]"), version_out));
-  STRINGZ_TO_NPVARIANT(version_out, *result);
-  return true;
-}
-
-// STDMETHOD(GetOneClickVersion)(long* version);
-bool NPOneClickClass::GetOneClickVersion(const NPVariant* args,
-                                         uint32_t argCount,
-                                         NPVariant* result) {
-  UNREFERENCED_PARAMETER(args);
-  PLUGIN_LOG(L2, (_T("[NPOneClickClass::GetOneClickVersion]")));
-  RETTRACE_EXCEPTION_IF_FAILED(EnsureWorkerUrlSet());
-
-  NULL_TO_NPVARIANT(*result);
-  RETTRACE_EXCEPTION_IF_FALSE(argCount == 0);
-
-  int32 version = 0;
-  RETTRACE_EXCEPTION_IF_FAILED(oneclick_worker_->GetOneClickVersion(&version));
-
-  INT32_TO_NPVARIANT(version, *result);
-  return true;
-}
-
-// get the URL that we are currently hosted in
-// Essentially, we return window.location.href
-HRESULT NPOneClickClass::GetUrl(CString* url_str) {
-  PLUGIN_LOG(L2, (_T("[NPOneClickClass::GetUrl]")));
-  // If npp_ is NULL, Init() has not been called
-  ATLASSERT(npp_);
-  ATLASSERT(url_str != NULL);
-  if (npp_ == NULL || url_str == NULL) {
-    return E_POINTER;
-  }
-
-  NPObject* window_object = NULL;
-
-  // Reference count not bumped up, do not release window_object
-  NPN_GetValue(npp_, NPNVWindowNPObject, &window_object);
-  if (!window_object)  {
-    return E_UNEXPECTED;
-  }
-
-  NPIdentifier location_id = NPN_GetStringIdentifier("location");
-  NPIdentifier href_id = NPN_GetStringIdentifier("href");
-
-  NPVariant locationv;
-  // Initialize the variant to NULL
-  NULL_TO_NPVARIANT(locationv);
-  NPN_GetProperty(npp_, window_object, location_id, &locationv);
-  ON_SCOPE_EXIT(NPN_ReleaseVariantValue, &locationv);
-
-  NPObject* location = NULL;
-  if (NPVARIANT_IS_OBJECT(locationv)) {
-    location = NPVARIANT_TO_OBJECT(locationv);
-  }
-
-  if (!location) {
-    return E_UNEXPECTED;
-  }
-
-  NPVariant hrefv;
-  // Initialize the variant to NULL
-  NULL_TO_NPVARIANT(hrefv);
-  NPN_GetProperty(npp_, location, href_id, &hrefv);
-  ON_SCOPE_EXIT(NPN_ReleaseVariantValue, &hrefv);
-
-  if (NPVARIANT_IS_STRING(hrefv)) {
-    CString url(ConvertNPStringToString(NPVARIANT_TO_STRING(hrefv)));
-    ATLASSERT(!url.IsEmpty());
-    if (!url.IsEmpty()) {
-      *url_str = url;
-    }
-  }
-
-  return S_OK;
-}
-
diff --git a/plugins/npOneClick.h b/plugins/npOneClick.h
deleted file mode 100644
index 9549617..0000000
--- a/plugins/npOneClick.h
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// The OneClick NPAPI Plugin implementation
-// using the new NPRuntime supported by Firefox and other
-
-#ifndef OMAHA_PLUGINS_NPONECLICK_H__
-#define OMAHA_PLUGINS_NPONECLICK_H__
-
-#include "omaha/third_party/gecko/include/npupp.h"
-
-#include <atlsafe.h>              // NOLINT
-
-#include "omaha/plugins/baseplugin.h"
-
-// These files need to be included _after_ the headers above because
-// npupp.h includes windows.h in a special way and defines uint32 and so
-// forth on its own.  If we include these "standard" headers first, then
-// the type system gets all confused.  However, including these afterwards
-// makes lint angry since you're not normally supposed to do this.  But that's
-// why there is the NOLINT comment after each of the header lines below.
-#include <ATLBase.h>              // NOLINT
-#include <ATLSafe.h>              // NOLINT
-#include <ATLCom.h>               // NOLINT
-#include <ATLStr.h>               // NOLINT
-#include <ATLConv.h>              // NOLINT
-#include <ObjSafe.h>              // NOLINT
-
-// Need to do the following, rather than #include common/basictypes.h
-// because some defines/types in basictypes.h conflict with the NPAPI
-// headers
-
-// Force basictypes.h to not be included
-#define BASE_BASICTYPES_H_
-
-// #undef ATLASSERT, we will use the one from atlassert.h #included below
-#undef ATLASSERT
-
-// Copied from basictypes.h
-// Typedefing some values that are needed within logging.h
-typedef uint64 time64;
-
-// Copied from basictypes.h
-// A macro to disallow the evil copy constructor and operator= functions
-// This should be used in the private: declarations for a class
-#define DISALLOW_EVIL_CONSTRUCTORS(TypeName)    \
-  TypeName(const TypeName&);                    \
-  TypeName &operator=(const TypeName&)
-
-#include "common/atlconvfix.h"
-// logging.h contains definitions for PLUGIN_LOG
-#include "common/logging.h"
-#include "common/atlassert.h"
-// debug.h contains definitions for ASSERT
-#include "common/debug.h"
-
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(x) (sizeof(x) / sizeof(*(x)))
-#endif
-
-// Helper macros
-// TRACE, ASSERT and return if the first argument evaluates to false
-#define RETTRACE_IF_FALSE(x)                                \
-  do {                                                      \
-    BOOL success(!!(x));                                    \
-    if (!success) {                                         \
-      PLUGIN_LOG(L2, (_T("[") _T(#x) _T("] is FALSE")));    \
-      ASSERT(FALSE, (_T("[") _T(#x) _T("] is FALSE")));     \
-      return;                                               \
-    }                                                       \
-  } while (false)                                           \
-
-// TRACE, ASSERT and return if the HRESULT argument is a COM error
-#define RETTRACE_IF_FAILED(x)  RETTRACE_IF_FALSE(SUCCEEDED(x))
-
-// TRACE, throw exception and return if the first argument evaluates to false
-#define RETTRACE_EXCEPTION_IF_FALSE(x)                      \
-  do {                                                      \
-    BOOL success(!!(x));                                    \
-    if (!success) {                                         \
-      PLUGIN_LOG(L2, (_T("[") _T(#x) _T("] is FALSE")));    \
-      ASSERT(FALSE, (_T("[") _T(#x) _T("] is FALSE")));     \
-      NPN_SetException(this, "false");                      \
-      return true;                                          \
-    }                                                       \
-  } while (false)                                           \
-
-// TRACE, throw exception and return if the HRESULT argument is a COM error
-#define RETTRACE_EXCEPTION_IF_FAILED(x)                     \
-  do {                                                      \
-    HRESULT hr = (x);                                       \
-    if (FAILED(hr)) {                                       \
-      PLUGIN_LOG(L2, (_T("[") _T(#x) _T("] is FAILED")));   \
-      ASSERT(FALSE, (_T("[") _T(#x) _T("] is FAILED")));    \
-      char buf[32];                                         \
-      ::wnsprintfA(buf, ARRAYSIZE(buf), "0x%08x", hr);      \
-      NPN_SetException(this, buf);                          \
-      return true;                                          \
-    }                                                       \
-  } while (false)                                           \
-
-// Simple helper macro for the class to define bool based properties
-#define DEFINE_BOOL_PROPERTY(the_class, x, instance, method)                  \
-bool the_class::Get##x(NPVariant *result) {                                   \
-  PLUGIN_LOG(L2, (_T("[") _T(#the_class) _T("::Get") _T(#x) _T("]")));        \
-  RETTRACE_EXCEPTION_IF_FALSE(instance != NULL);                              \
-  VARIANT_BOOL yes = VARIANT_FALSE;                                           \
-  RETTRACE_EXCEPTION_IF_FAILED(instance->method(&yes));                       \
-  BOOLEAN_TO_NPVARIANT(yes == VARIANT_TRUE, *result);                         \
-  return true;                                                                \
-}                                                                             \
-                                                                              \
-bool the_class::Set##x(const NPVariant *value) {                              \
-  PLUGIN_LOG(L2, (_T("[") _T(#the_class) _T("::Set") _T(#x)                   \
-                  _T("] - Not Implemented")));                                \
-  /* Script should not be calling this! */                                    \
-  ATLASSERT(FALSE);                                                           \
-  return false;                                                               \
-}
-
-// Simple helper macro for the class to define string based properties
-#define DEFINE_STRING_PROPERTY(the_class, x, instance, method)                \
-bool the_class::Get##x(NPVariant *result) {                                   \
-  PLUGIN_LOG(L2, (_T("[") _T(#the_class) _T("::Get") _T(#x) _T("]")));        \
-  RETTRACE_EXCEPTION_IF_FALSE(instance);                                      \
-  CComBSTR bstr;                                                              \
-  instance->method(&bstr);                                                    \
-  size_t cch = bstr.Length() + 1;                                             \
-  RETTRACE_EXCEPTION_IF_FALSE(cch);                                           \
-  /* Use NPN_MemAlloc() to allocate [out] parameter memory */                 \
-  char* result_##x = reinterpret_cast<char *>(NPN_MemAlloc(cch));             \
-  RETTRACE_EXCEPTION_IF_FALSE(::lstrcpynA(result_##x, CW2A(bstr), cch));      \
-  PLUGIN_LOG(L2, (_T("[") _T(#the_class) _T("::Get") _T(#x)                   \
-                  _T("][retval=%S]"), result_##x));                           \
-  STRINGZ_TO_NPVARIANT(result_##x, *result);                                  \
-  return true;                                                                \
-}                                                                             \
-                                                                              \
-bool the_class::Set##x(const NPVariant *value) {                              \
-  PLUGIN_LOG(L2, (_T("[") _T(#the_class) _T("::Set") _T(#x)                   \
-                  _T("] - Not Implemented")));                                \
-  /* Script should not be calling this! */                                    \
-  ATLASSERT(FALSE);                                                           \
-  return false;                                                               \
-}                                                                             \
-
-namespace omaha {
-
-class OneClickWorker;
-
-}  // namespace omaha
-
-
-// The primary plug-in class, NPOneClickClass.
-DECLARE_PLUGIN_CLASS(NPOneClickClass)
-  virtual void Shutdown();
-  DECLARE_PLUGIN_FUNCTION(Install)
-  DECLARE_PLUGIN_FUNCTION(Install2)
-  DECLARE_PLUGIN_FUNCTION(GetInstalledVersion)
-  DECLARE_PLUGIN_FUNCTION(GetOneClickVersion)
-  DECLARE_CPLUGIN_NPCLASS()
- private:
-  HRESULT EnsureWorkerUrlSet();
-  HRESULT GetUrl(CString* url_str);
-
-  scoped_ptr<omaha::OneClickWorker> oneclick_worker_;
-  bool is_worker_url_set_;
-END_DECLARE_PLUGIN_CLASS(NPOneClickClass)
-
-#endif  // OMAHA_PLUGINS_NPONECLICK_H__
-
diff --git a/plugins/np_entry.cc b/plugins/np_entry.cc
deleted file mode 100644
index 6c99449..0000000
--- a/plugins/np_entry.cc
+++ /dev/null
@@ -1,193 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: NPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the NPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the NPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-//////////////////////////////////////////////////////////////
-//
-// Main plugin entry point implementation
-//
-
-#pragma warning(push)
-#pragma warning(disable: 4201)  // c4201: nonstandard extension used
-                                //        nameless struct/union
-
-#include "omaha/third_party/gecko/include/npapi.h"
-#include "omaha/third_party/gecko/include/npupp.h"
-
-#pragma warning(pop)
-
-#ifndef HIBYTE
-#define HIBYTE(x) (((static_cast<uint32>(x)) & 0xff00) >> 8)
-#endif
-
-NPNetscapeFuncs NPNFuncs;
-
-#ifdef XP_WIN
-
-NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* pFuncs) {
-  if (pFuncs == NULL) {
-    return NPERR_INVALID_FUNCTABLE_ERROR;
-  }
-
-  if (pFuncs->size < sizeof(NPPluginFuncs)) {
-    return NPERR_INVALID_FUNCTABLE_ERROR;
-  }
-
-  pFuncs->version       = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
-  pFuncs->newp          = NPP_New;
-  pFuncs->destroy       = NPP_Destroy;
-  pFuncs->setwindow     = NPP_SetWindow;
-  pFuncs->newstream     = NPP_NewStream;
-  pFuncs->destroystream = NPP_DestroyStream;
-  pFuncs->asfile        = NPP_StreamAsFile;
-  pFuncs->writeready    = NPP_WriteReady;
-  pFuncs->write         = NPP_Write;
-  pFuncs->print         = NPP_Print;
-  pFuncs->event         = NPP_HandleEvent;
-  pFuncs->urlnotify     = NPP_URLNotify;
-  pFuncs->getvalue      = NPP_GetValue;
-  pFuncs->setvalue      = NPP_SetValue;
-  pFuncs->javaClass     = NULL;
-
-  return NPERR_NO_ERROR;
-}
-
-#endif /* XP_WIN */
-
-char* NPP_GetMIMEDescription();
-
-char* NP_GetMIMEDescription() {
-  return NPP_GetMIMEDescription();
-}
-
-NPError NP_GetValue(void* future, NPPVariable variable, void *value) {
-  return NPP_GetValue(reinterpret_cast<NPP_t *>(future), variable, value);
-}
-
-NPError OSCALL NP_Initialize(NPNetscapeFuncs* pFuncs
-#ifdef XP_UNIX
-                             , NPPluginFuncs* pluginFuncs
-#endif
-                             ) {
-  if (pFuncs == NULL) {
-    return NPERR_INVALID_FUNCTABLE_ERROR;
-  }
-
-  if (HIBYTE(pFuncs->version) > NP_VERSION_MAJOR) {
-    return NPERR_INCOMPATIBLE_VERSION_ERROR;
-  }
-
-  // TODO(omaha): This is not matching, find out why
-  // if(pFuncs->size < sizeof(NPNetscapeFuncs))
-  //  return NPERR_INVALID_FUNCTABLE_ERROR;
-
-  NPNFuncs.size                    = pFuncs->size;
-  NPNFuncs.version                 = pFuncs->version;
-  NPNFuncs.geturlnotify            = pFuncs->geturlnotify;
-  NPNFuncs.geturl                  = pFuncs->geturl;
-  NPNFuncs.posturlnotify           = pFuncs->posturlnotify;
-  NPNFuncs.posturl                 = pFuncs->posturl;
-  NPNFuncs.requestread             = pFuncs->requestread;
-  NPNFuncs.newstream               = pFuncs->newstream;
-  NPNFuncs.write                   = pFuncs->write;
-  NPNFuncs.destroystream           = pFuncs->destroystream;
-  NPNFuncs.status                  = pFuncs->status;
-  NPNFuncs.uagent                  = pFuncs->uagent;
-  NPNFuncs.memalloc                = pFuncs->memalloc;
-  NPNFuncs.memfree                 = pFuncs->memfree;
-  NPNFuncs.memflush                = pFuncs->memflush;
-  NPNFuncs.reloadplugins           = pFuncs->reloadplugins;
-  NPNFuncs.getJavaEnv              = pFuncs->getJavaEnv;
-  NPNFuncs.getJavaPeer             = pFuncs->getJavaPeer;
-  NPNFuncs.getvalue                = pFuncs->getvalue;
-  NPNFuncs.setvalue                = pFuncs->setvalue;
-  NPNFuncs.invalidaterect          = pFuncs->invalidaterect;
-  NPNFuncs.invalidateregion        = pFuncs->invalidateregion;
-  NPNFuncs.forceredraw             = pFuncs->forceredraw;
-  NPNFuncs.getstringidentifier     = pFuncs->getstringidentifier;
-  NPNFuncs.getstringidentifiers    = pFuncs->getstringidentifiers;
-  NPNFuncs.getintidentifier        = pFuncs->getintidentifier;
-  NPNFuncs.identifierisstring      = pFuncs->identifierisstring;
-  NPNFuncs.utf8fromidentifier      = pFuncs->utf8fromidentifier;
-  NPNFuncs.intfromidentifier       = pFuncs->intfromidentifier;
-  NPNFuncs.createobject            = pFuncs->createobject;
-  NPNFuncs.retainobject            = pFuncs->retainobject;
-  NPNFuncs.releaseobject           = pFuncs->releaseobject;
-  NPNFuncs.invoke                  = pFuncs->invoke;
-  NPNFuncs.invokeDefault           = pFuncs->invokeDefault;
-  NPNFuncs.evaluate                = pFuncs->evaluate;
-  NPNFuncs.getproperty             = pFuncs->getproperty;
-  NPNFuncs.setproperty             = pFuncs->setproperty;
-  NPNFuncs.removeproperty          = pFuncs->removeproperty;
-  NPNFuncs.hasproperty             = pFuncs->hasproperty;
-  NPNFuncs.hasmethod               = pFuncs->hasmethod;
-  NPNFuncs.releasevariantvalue     = pFuncs->releasevariantvalue;
-  NPNFuncs.setexception            = pFuncs->setexception;
-
-#ifdef XP_UNIX
-  /*
-   * Set up the plugin function table that Netscape will use to
-   * call us.  Netscape needs to know about our version and size
-   * and have a UniversalProcPointer for every function we
-   * implement.
-   */
-  pluginFuncs->version    = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR;
-  pluginFuncs->size       = sizeof(NPPluginFuncs);
-  pluginFuncs->newp       = NewNPP_NewProc(NPP_New);
-  pluginFuncs->destroy    = NewNPP_DestroyProc(NPP_Destroy);
-  pluginFuncs->setwindow  = NewNPP_SetWindowProc(NPP_SetWindow);
-  pluginFuncs->newstream  = NewNPP_NewStreamProc(NPP_NewStream);
-  pluginFuncs->destroystream = NewNPP_DestroyStreamProc(NPP_DestroyStream);
-  pluginFuncs->asfile     = NewNPP_StreamAsFileProc(NPP_StreamAsFile);
-  pluginFuncs->writeready = NewNPP_WriteReadyProc(NPP_WriteReady);
-  pluginFuncs->write      = NewNPP_WriteProc(NPP_Write);
-  pluginFuncs->print      = NewNPP_PrintProc(NPP_Print);
-  pluginFuncs->urlnotify  = NewNPP_URLNotifyProc(NPP_URLNotify);
-  pluginFuncs->event      = NULL;
-  pluginFuncs->getvalue   = NewNPP_GetValueProc(NPP_GetValue);
-#ifdef OJI
-  pluginFuncs->javaClass  = NPP_GetJavaClass();
-#endif
-
-  NPP_Initialize();
-#endif
-
-  return NPERR_NO_ERROR;
-}
-
-NPError OSCALL NP_Shutdown() {
-  return NPERR_NO_ERROR;
-}
diff --git a/plugins/npn_gate.cc b/plugins/npn_gate.cc
deleted file mode 100644
index 1281004..0000000
--- a/plugins/npn_gate.cc
+++ /dev/null
@@ -1,340 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: NPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the NPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the NPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-////////////////////////////////////////////////////////////
-//
-// Implementation of Netscape entry points (NPN_*)
-//
-
-#pragma warning(push)
-#pragma warning(disable: 4201)  // c4201: nonstandard extension used
-                                //        nameless struct/union
-
-#include "omaha/third_party/gecko/include/npapi.h"
-#include "omaha/third_party/gecko/include/npupp.h"
-
-#pragma warning(pop)
-
-#ifndef HIBYTE
-#define HIBYTE(x) (((static_cast<uint32>(x)) & 0xff00) >> 8)
-#endif
-
-#ifndef LOBYTE
-#define LOBYTE(W) ((W) & 0xFF)
-#endif
-
-extern NPNetscapeFuncs NPNFuncs;
-
-void NPN_Version(int* plugin_major,
-                 int* plugin_minor,
-                 int* netscape_major,
-                 int* netscape_minor) {
-  *plugin_major   = NP_VERSION_MAJOR;
-  *plugin_minor   = NP_VERSION_MINOR;
-  *netscape_major = HIBYTE(NPNFuncs.version);
-  *netscape_minor = LOBYTE(NPNFuncs.version);
-}
-
-NPError NPN_GetURLNotify(NPP instance,
-                         const char *url,
-                         const char *target,
-                         void* notifyData) {
-  int navMinorVers = NPNFuncs.version & 0xFF;
-  NPError rv = NPERR_NO_ERROR;
-
-  if (navMinorVers >= NPVERS_HAS_NOTIFICATION) {
-    rv = NPNFuncs.geturlnotify(instance, url, target, notifyData);
-  } else {
-    rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
-  }
-
-  return rv;
-}
-
-NPError NPN_GetURL(NPP instance, const char *url, const char *target) {
-  NPError rv = NPNFuncs.geturl(instance, url, target);
-  return rv;
-}
-
-NPError NPN_PostURLNotify(NPP instance,
-                          const char* url,
-                          const char* window,
-                          uint32 len,
-                          const char* buf,
-                          NPBool file,
-                          void* notifyData) {
-  int navMinorVers = NPNFuncs.version & 0xFF;
-  NPError rv = NPERR_NO_ERROR;
-
-  if (navMinorVers >= NPVERS_HAS_NOTIFICATION) {
-    rv = NPNFuncs.posturlnotify(instance,
-                                url,
-                                window,
-                                len,
-                                buf,
-                                file,
-                                notifyData);
-  } else {
-    rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
-  }
-
-  return rv;
-}
-
-NPError NPN_PostURL(NPP instance,
-                    const char* url,
-                    const char* window,
-                    uint32 len,
-                    const char* buf,
-                    NPBool file) {
-  NPError rv = NPNFuncs.posturl(instance, url, window, len, buf, file);
-  return rv;
-}
-
-NPError NPN_RequestRead(NPStream* stream, NPByteRange* rangeList) {
-  NPError rv = NPNFuncs.requestread(stream, rangeList);
-  return rv;
-}
-
-NPError NPN_NewStream(NPP instance,
-                      NPMIMEType type,
-                      const char* target,
-                      NPStream** stream) {
-  int navMinorVersion = NPNFuncs.version & 0xFF;
-
-  NPError rv = NPERR_NO_ERROR;
-
-  if (navMinorVersion >= NPVERS_HAS_STREAMOUTPUT) {
-    rv = NPNFuncs.newstream(instance, type, target, stream);
-  } else {
-    rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
-  }
-
-  return rv;
-}
-
-int32 NPN_Write(NPP instance, NPStream *stream, int32 len, void *buffer) {
-  int navMinorVersion = NPNFuncs.version & 0xFF;
-  int32 rv = 0;
-
-  if (navMinorVersion >= NPVERS_HAS_STREAMOUTPUT) {
-    rv = NPNFuncs.write(instance, stream, len, buffer);
-  } else {
-    rv = -1;
-  }
-
-  return rv;
-}
-
-NPError NPN_DestroyStream(NPP instance, NPStream* stream, NPError reason) {
-  int navMinorVersion = NPNFuncs.version & 0xFF;
-  NPError rv = NPERR_NO_ERROR;
-
-  if (navMinorVersion >= NPVERS_HAS_STREAMOUTPUT) {
-    rv = NPNFuncs.destroystream(instance, stream, reason);
-  } else {
-    rv = NPERR_INCOMPATIBLE_VERSION_ERROR;
-  }
-
-  return rv;
-}
-
-void NPN_Status(NPP instance, const char *message) {
-  NPNFuncs.status(instance, message);
-}
-
-const char* NPN_UserAgent(NPP instance) {
-  const char * rv = NULL;
-  rv = NPNFuncs.uagent(instance);
-  return rv;
-}
-
-void* NPN_MemAlloc(uint32 size) {
-  void * rv = NULL;
-  rv = NPNFuncs.memalloc(size);
-  return rv;
-}
-
-void NPN_MemFree(void* ptr) {
-  NPNFuncs.memfree(ptr);
-}
-
-uint32 NPN_MemFlush(uint32 size) {
-  uint32 rv = NPNFuncs.memflush(size);
-  return rv;
-}
-
-void NPN_ReloadPlugins(NPBool reloadPages) {
-  NPNFuncs.reloadplugins(reloadPages);
-}
-
-JRIEnv* NPN_GetJavaEnv(void) {
-  JRIEnv * rv = NULL;
-  rv = NPNFuncs.getJavaEnv();
-  return rv;
-}
-
-jref NPN_GetJavaPeer(NPP instance) {
-  jref rv;
-  rv = NPNFuncs.getJavaPeer(instance);
-  return rv;
-}
-
-NPError NPN_GetValue(NPP instance, NPNVariable variable, void *value) {
-  NPError rv = NPNFuncs.getvalue(instance, variable, value);
-  return rv;
-}
-
-NPError NPN_SetValue(NPP instance, NPPVariable variable, void *value) {
-  NPError rv = NPNFuncs.setvalue(instance, variable, value);
-  return rv;
-}
-
-void NPN_InvalidateRect(NPP instance, NPRect *invalidRect) {
-  NPNFuncs.invalidaterect(instance, invalidRect);
-}
-
-void NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion) {
-  NPNFuncs.invalidateregion(instance, invalidRegion);
-}
-
-void NPN_ForceRedraw(NPP instance) {
-  NPNFuncs.forceredraw(instance);
-}
-
-NPIdentifier NPN_GetStringIdentifier(const NPUTF8 *name) {
-  return NPNFuncs.getstringidentifier(name);
-}
-
-void NPN_GetStringIdentifiers(const NPUTF8 **names,
-                              uint32_t nameCount,
-                              NPIdentifier *identifiers) {
-  return NPNFuncs.getstringidentifiers(names, nameCount, identifiers);
-}
-
-NPIdentifier NPN_GetStringIdentifier(int32_t intid) {
-  return NPNFuncs.getintidentifier(intid);
-}
-
-bool NPN_IdentifierIsString(NPIdentifier identifier) {
-  return NPNFuncs.identifierisstring(identifier);
-}
-
-NPUTF8 *NPN_UTF8FromIdentifier(NPIdentifier identifier) {
-  return NPNFuncs.utf8fromidentifier(identifier);
-}
-
-int32_t NPN_IntFromIdentifier(NPIdentifier identifier) {
-  return NPNFuncs.intfromidentifier(identifier);
-}
-
-NPObject *NPN_CreateObject(NPP npp, NPClass *aClass) {
-  return NPNFuncs.createobject(npp, aClass);
-}
-
-NPObject *NPN_RetainObject(NPObject *obj) {
-  return NPNFuncs.retainobject(obj);
-}
-
-void NPN_ReleaseObject(NPObject *obj) {
-  return NPNFuncs.releaseobject(obj);
-}
-
-bool NPN_Invoke(NPP npp,
-                NPObject* obj,
-                NPIdentifier methodName,
-                const NPVariant *args,
-                uint32_t argCount,
-                NPVariant *result) {
-  return NPNFuncs.invoke(npp, obj, methodName, args, argCount, result);
-}
-
-bool NPN_InvokeDefault(NPP npp,
-                       NPObject* obj,
-                       const NPVariant *args,
-                       uint32_t argCount,
-                       NPVariant *result) {
-  return NPNFuncs.invokeDefault(npp, obj, args, argCount, result);
-}
-
-bool NPN_Evaluate(NPP npp,
-                  NPObject* obj,
-                  NPString *script,
-                  NPVariant *result) {
-  return NPNFuncs.evaluate(npp, obj, script, result);
-}
-
-bool NPN_GetProperty(NPP npp,
-                     NPObject* obj,
-                     NPIdentifier propertyName,
-                     NPVariant *result) {
-  return NPNFuncs.getproperty(npp, obj, propertyName, result);
-}
-
-bool NPN_SetProperty(NPP npp,
-                     NPObject* obj,
-                     NPIdentifier propertyName,
-                     const NPVariant *value) {
-  return NPNFuncs.setproperty(npp, obj, propertyName, value);
-}
-
-bool NPN_RemoveProperty(NPP npp,
-                        NPObject* obj,
-                        NPIdentifier propertyName) {
-  return NPNFuncs.removeproperty(npp, obj, propertyName);
-}
-
-bool NPN_HasProperty(NPP npp,
-                     NPObject* obj,
-                     NPIdentifier propertyName) {
-  return NPNFuncs.hasproperty(npp, obj, propertyName);
-}
-
-bool NPN_HasMethod(NPP npp,
-                   NPObject* obj,
-                   NPIdentifier methodName) {
-  return NPNFuncs.hasmethod(npp, obj, methodName);
-}
-
-void NPN_ReleaseVariantValue(NPVariant *variant) {
-  NPNFuncs.releasevariantvalue(variant);
-}
-
-void NPN_SetException(NPObject* obj, const NPUTF8 *message) {
-  NPNFuncs.setexception(obj, message);
-}
-
diff --git a/plugins/npp_gate.cc b/plugins/npp_gate.cc
deleted file mode 100644
index 2c377f1..0000000
--- a/plugins/npp_gate.cc
+++ /dev/null
@@ -1,300 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: NPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the NPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the NPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-////////////////////////////////////////////////////////////
-//
-// Implementation of plugin entry points (NPP_*)
-// most are just empty stubs for this particular plugin
-//
-#include "plugins/baseplugin.h"
-#include "common/const_config.h"
-
-#pragma warning(push)
-#pragma warning(disable: 4100 4061)
-// 4100: unreferenced formal parameter
-// 4061: enumerator 'x' in switch of enum 'y'
-//       is not explicitly handled by a case label
-
-
-char* NPP_GetMIMEDescription() {
-  return kOneClickPluginMimeDescriptionAnsi;
-}
-
-NPError NPP_Initialize() {
-  return NPERR_NO_ERROR;
-}
-
-void NPP_Shutdown() {
-}
-
-// here the plugin creates an instance of our CPlugin object which
-// will be associated with this newly created plugin instance and
-// will do all the neccessary job
-NPError NPP_New(NPMIMEType plugin_type,
-                NPP instance,
-                uint16 mode,
-                int16 argc,
-                char* argn[],
-                char* argv[],
-                NPSavedData* saved) {
-  if (instance == NULL) {
-    return NPERR_INVALID_INSTANCE_ERROR;
-  }
-
-  NPError rv = NPERR_NO_ERROR;
-
-  CPlugin* plugin = new CPlugin(instance);
-  if (plugin == NULL) {
-    return NPERR_OUT_OF_MEMORY_ERROR;
-  }
-
-  instance->pdata = reinterpret_cast<void*>(plugin);
-  return rv;
-}
-
-// here is the place to clean up and destroy the CPlugin object
-NPError NPP_Destroy(NPP instance, NPSavedData** save) {
-  if (instance == NULL) {
-    return NPERR_INVALID_INSTANCE_ERROR;
-  }
-
-  NPError rv = NPERR_NO_ERROR;
-
-  CPlugin* plugin = reinterpret_cast<CPlugin*>(instance->pdata);
-  if (plugin != NULL) {
-    plugin->shut();
-    delete plugin;
-  }
-  return rv;
-}
-
-// during this call we know when the plugin window is ready or
-// is about to be destroyed so we can do some gui specific
-// initialization and shutdown
-NPError NPP_SetWindow(NPP instance, NPWindow* np_window) {
-  if (instance == NULL) {
-    return NPERR_INVALID_INSTANCE_ERROR;
-  }
-
-  NPError rv = NPERR_NO_ERROR;
-
-  if (np_window == NULL) {
-    return NPERR_GENERIC_ERROR;
-  }
-
-  CPlugin* plugin = reinterpret_cast<CPlugin*>(instance->pdata);
-
-  if (plugin == NULL) {
-    return NPERR_GENERIC_ERROR;
-  }
-
-  // window just created
-  if (!plugin->isInitialized() && (np_window->window != NULL)) {
-    if (!plugin->init(np_window)) {
-      delete plugin;
-      plugin = NULL;
-      instance->pdata = NULL;
-      return NPERR_MODULE_LOAD_FAILED_ERROR;
-    }
-  }
-
-  // window goes away
-  if ((np_window->window == NULL) && plugin->isInitialized()) {
-    return NPERR_NO_ERROR;
-  }
-
-  // window resized
-  if (plugin->isInitialized() && (np_window->window != NULL)) {
-    return NPERR_NO_ERROR;
-  }
-
-  // this should not happen, nothing to do
-  if ((np_window->window == NULL) && !plugin->isInitialized()) {
-    return NPERR_NO_ERROR;
-  }
-
-  return rv;
-}
-
-// ==============================
-// ! Scriptability related code !
-// ==============================
-//
-// here the plugin is asked by Mozilla to tell if it is scriptable
-// we should return a valid interface id and a pointer to
-// nsScriptablePeer interface which we should have implemented
-// and which should be defined in the corressponding *.xpt file
-// in the bin/components folder
-NPError NPP_GetValue(NPP instance, NPPVariable variable, void* value) {
-  if (instance == NULL) {
-    return NPERR_INVALID_INSTANCE_ERROR;
-  }
-
-  NPError rv = NPERR_NO_ERROR;
-
-  if (instance == NULL) {
-    return NPERR_GENERIC_ERROR;
-  }
-
-  CPlugin* plugin = reinterpret_cast<CPlugin*>(instance->pdata);
-  if (plugin == NULL) {
-    return NPERR_GENERIC_ERROR;
-  }
-
-  switch (variable) {
-  case NPPVpluginNameString:
-    *(reinterpret_cast<char**>(value)) = "NPRuntimeTest";
-    break;
-  case NPPVpluginDescriptionString:
-    *(reinterpret_cast<char**>(value)) =
-        "NPRuntime scriptability API test plugin";
-    break;
-  case NPPVpluginScriptableNPObject:
-    *(reinterpret_cast<NPObject**>(value)) = plugin->GetScriptableObject();
-    break;
-  default:
-    rv = NPERR_GENERIC_ERROR;
-    break;
-  }
-
-  return rv;
-}
-
-NPError NPP_NewStream(NPP instance,
-                      NPMIMEType type,
-                      NPStream* stream,
-                      NPBool seekable,
-                      uint16* stype) {
-  if (instance == NULL) {
-    return NPERR_INVALID_INSTANCE_ERROR;
-  }
-
-  NPError rv = NPERR_NO_ERROR;
-  return rv;
-}
-
-int32 NPP_WriteReady(NPP instance, NPStream* stream) {
-  if (instance == NULL) {
-    return NPERR_INVALID_INSTANCE_ERROR;
-  }
-
-  int32 rv = 0x0fffffff;
-  return rv;
-}
-
-int32 NPP_Write(NPP instance,
-                NPStream* stream,
-                int32 offset,
-                int32 len,
-                void* buffer) {
-  if (instance == NULL) {
-    return NPERR_INVALID_INSTANCE_ERROR;
-  }
-
-  int32 rv = len;
-  return rv;
-}
-
-NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPError reason) {
-  if (instance == NULL) {
-    return NPERR_INVALID_INSTANCE_ERROR;
-  }
-
-  NPError rv = NPERR_NO_ERROR;
-  return rv;
-}
-
-void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname) {
-  if (instance == NULL) {
-    return;
-  }
-}
-
-void NPP_Print(NPP instance, NPPrint* print_info) {
-  if (instance == NULL) {
-    return;
-  }
-}
-
-void NPP_URLNotify(NPP instance,
-                   const char* url,
-                   NPReason reason,
-                   void* notify_data) {
-  if (instance == NULL) {
-    return;
-  }
-}
-
-NPError NPP_SetValue(NPP instance, NPNVariable variable, void* value) {
-  if (instance == NULL) {
-    return NPERR_INVALID_INSTANCE_ERROR;
-  }
-
-  NPError rv = NPERR_NO_ERROR;
-  return rv;
-}
-
-int16 NPP_HandleEvent(NPP instance, void* event) {
-  if (instance == NULL) {
-    return 0;
-  }
-
-  int16 rv = 0;
-  CPlugin* plugin = reinterpret_cast<CPlugin*>(instance->pdata);
-  if (plugin) {
-    rv = plugin->handleEvent(event);
-  }
-
-  return rv;
-}
-
-NPObject* NPP_GetScriptableInstance(NPP instance) {
-  if (!instance) {
-    return 0;
-  }
-
-  NPObject *npobj = 0;
-  CPlugin* plugin = reinterpret_cast<CPlugin*>(instance->pdata);
-  if (!plugin) {
-    npobj = plugin->GetScriptableObject();
-  }
-
-  return npobj;
-}
-
-#pragma warning(pop)
-
diff --git a/plugins/oneclick.cc b/plugins/oneclick.cc
deleted file mode 100644
index 9649521..0000000
--- a/plugins/oneclick.cc
+++ /dev/null
@@ -1,148 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Implementation of the OneClick Plugin.
-
-#include "omaha/plugins/oneclick.h"
-
-#include <atlsafe.h>
-
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/string.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/plugins/oneclick_browser_callback_activex.h"
-
-namespace omaha {
-
-GoopdateCtrl::GoopdateCtrl() : is_worker_url_set_(false) {
-  CORE_LOG(L2, (_T("[GoopdateCtrl::GoopdateCtrl]")));
-}
-
-GoopdateCtrl::~GoopdateCtrl() {
-  CORE_LOG(L2, (_T("[GoopdateCtrl::~GoopdateCtrl]")));
-}
-
-void GoopdateCtrl::EnsureWorkerUrlSet() {
-  if (is_worker_url_set_) {
-    return;
-  }
-
-  CComBSTR browser_url_bstr;
-  if (!GetOurUrl(&browser_url_bstr)) {
-    ASSERT(false, (_T("[SetSite] failed GetOurUrl() call")));
-    return;
-  }
-
-  CORE_LOG(L2, (_T("[EnsureWorkerUrlSet][url=%s]"), browser_url_bstr));
-  oneclick_worker_->set_browser_url(browser_url_bstr);
-  is_worker_url_set_ = true;
-}
-
-STDMETHODIMP GoopdateCtrl::Install(BSTR cmd_line_args,
-                                   VARIANT* success_callback,
-                                   VARIANT* failure_callback) {
-  EnsureWorkerUrlSet();
-  ASSERT1(cmd_line_args && cmd_line_args[0]);
-
-  if (!cmd_line_args || !cmd_line_args[0]) {
-    return E_INVALIDARG;
-  }
-
-  CORE_LOG(L2, (_T("[GoopdateCtrl::Install][cmd_line \"%s\"]"),
-                CW2CT(cmd_line_args)));
-
-  OneClickBrowserCallbackActiveX browser_callback;
-  HRESULT hr = browser_callback.Initialize(success_callback, failure_callback);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  CString cmd_line_args_str(cmd_line_args);
-  hr = oneclick_worker_->DoOneClickInstall(cmd_line_args_str,
-                                           &browser_callback);
-
-  return hr;
-}
-
-STDMETHODIMP GoopdateCtrl::Install2(BSTR extra_args) {
-  EnsureWorkerUrlSet();
-  ASSERT1(extra_args && extra_args[0]);
-
-  if (!extra_args || !extra_args[0]) {
-    return E_INVALIDARG;
-  }
-
-  CORE_LOG(L2, (_T("[GoopdateCtrl::Install2][extra_args \"%s\"]"),
-                CW2CT(extra_args)));
-
-  CString extra_args_str(extra_args);
-  return oneclick_worker_->DoOneClickInstall2(extra_args_str);
-}
-
-STDMETHODIMP GoopdateCtrl::GetInstalledVersion(BSTR guid_string,
-                                               VARIANT_BOOL is_machine,
-                                               BSTR* version_string) {
-  CORE_LOG(L2, (_T("[GoopdateCtrl::GetInstalledVersion][%s][%d]"),
-                guid_string, is_machine));
-  EnsureWorkerUrlSet();
-
-  if (!version_string) {
-    return E_POINTER;
-  }
-  *version_string = NULL;
-
-  CString version;
-  HRESULT hr = oneclick_worker_->GetInstalledVersion(guid_string,
-                                                     is_machine == VARIANT_TRUE,
-                                                     &version);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  *version_string = version.AllocSysString();
-  return S_OK;
-}
-
-STDMETHODIMP GoopdateCtrl::GetOneClickVersion(long* version) {
-  CORE_LOG(L2, (_T("[GoopdateCtrl::GetOneClickVersion]")));
-  EnsureWorkerUrlSet();
-
-  return oneclick_worker_->GetOneClickVersion(version);
-}
-
-HRESULT GoopdateCtrl::FinalConstruct() {
-  CORE_LOG(L2, (_T("[GoopdateCtrl::FinalConstruct]")));
-  oneclick_worker_.reset(new OneClickWorker());
-  return oneclick_worker_->Initialize();
-}
-
-void GoopdateCtrl::FinalRelease() {
-  CORE_LOG(L2, (_T("[GoopdateCtrl::FinalRelease]")));
-}
-
-CString GoopdateCtrl::GetGoopdateShellPathForRegMap() {
-  return goopdate_utils::BuildGoogleUpdateExeDir(
-      goopdate_utils::IsRunningFromOfficialGoopdateDir(true));
-}
-
-OBJECT_ENTRY_AUTO(__uuidof(GoopdateOneClickControl), GoopdateCtrl)
-
-}  // namespace omaha
-
-// 4505: unreferenced local function has been removed
-#pragma warning(disable : 4505)
-
diff --git a/plugins/oneclick.def b/plugins/oneclick.def
deleted file mode 100644
index 6ba661a..0000000
--- a/plugins/oneclick.def
+++ /dev/null
@@ -1,26 +0,0 @@
-; Copyright 2007-2009 Google Inc.
-;
-; Licensed under the Apache License, Version 2.0 (the "License");
-; you may not use this file except in compliance with the License.
-; You may obtain a copy of the License at
-;
-;      http://www.apache.org/licenses/LICENSE-2.0
-;
-; Unless required by applicable law or agreed to in writing, software
-; distributed under the License is distributed on an "AS IS" BASIS,
-; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-; See the License for the specific language governing permissions and
-; limitations under the License.
-; ========================================================================
-;
-; OneClick.def : COM entry points.
-
-EXPORTS
-  NP_GetEntryPoints     @1
-  NP_Initialize         @2
-  NP_Shutdown           @3
-  DllCanUnloadNow       PRIVATE
-  DllGetClassObject     PRIVATE
-  DllRegisterServer     PRIVATE
-  DllUnregisterServer   PRIVATE
-
diff --git a/plugins/oneclick.h b/plugins/oneclick.h
deleted file mode 100644
index 7931438..0000000
--- a/plugins/oneclick.h
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// One-click support for Omaha returning users.
-
-#ifndef OMAHA_PLUGINS_ONECLICK_H__
-#define OMAHA_PLUGINS_ONECLICK_H__
-
-// TODO(omaha): We may want to move sitelock.h to be the "standard" sitelock.h
-// file from Microsoft (and move that file to omaha/external) and then have our
-// modifications to sitelock be in a derived class within the plugins
-// directory.
-
-#include <objsafe.h>
-#include <shellapi.h>
-#include <winhttp.h>
-#include <atlbase.h>
-#include <atlcom.h>
-#include <atlctl.h>
-#include "base/scoped_ptr.h"
-#include "omaha/common/ATLRegMapEx.h"
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/const_config.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/debug.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/plugins/resource.h"
-#include "omaha/plugins/oneclick_worker.h"
-#include "plugins/sitelock.h"
-#include "plugins/oneclick_idl.h"
-
-// This doesn't get generated into the oneclick_idl.h as an extern and our
-// build environment doesn't let us include oneclick_idl_i.c
-// since we do it differently so extern define this here.
-extern "C" const IID DIID__GoogleUpdateOneClickEvents;
-
-namespace omaha {
-
-class GoopdateCtrl;
-
-typedef IObjectSafetyImpl<GoopdateCtrl, INTERFACESAFE_FOR_UNTRUSTED_CALLER>
-            ObjectSafety;
-
-typedef CSiteLock<GoopdateCtrl> SiteLock;
-
-// Using 0xffff for the major/minor versions in the IDispatchImpl template will
-// make ATL load the typelib directly from the DLL instead of looking up typelib
-// registration in registry. The big benefit is that we do not need to register
-// the typelib. Also, this is needed for Vista SP1 with UAC off, in which
-// oleaut32 does not read typelib information from HKCU, because of a bug.
-class ATL_NO_VTABLE GoopdateCtrl
-    : public CComObjectRootEx<CComObjectThreadModel>,
-      public CComCoClass<GoopdateCtrl, &__uuidof(GoopdateOneClickControl)>,
-      public IDispatchImpl<IGoogleUpdateOneClick,
-                           &__uuidof(IGoogleUpdateOneClick),
-                           &LIBID_OneClickLib, 0xffff, 0xffff>,
-      public ObjectSafety,
-      public SiteLock,
-      public IObjectWithSiteImpl<GoopdateCtrl> {
- public:
-  GoopdateCtrl();
-  virtual ~GoopdateCtrl();
-
-  DECLARE_REGISTRY_RESOURCEID_EX(IDR_ONECLICK)
-
-  #pragma warning(push)
-  // C4640: construction of local static object is not thread-safe
-  #pragma warning(disable : 4640)
-  BEGIN_REGISTRY_MAP()
-    REGMAP_ENTRY(_T("HKROOT"),            goopdate_utils::GetHKRoot())
-    REGMAP_ENTRY(_T("PROGID"),            kOneClickProgId)
-    REGMAP_ENTRY(_T("CLSID"),             __uuidof(GoopdateOneClickControl))
-    REGMAP_ENTRY(_T("PLUGINDOMAIN"),      kGoopdateServer)
-    REGMAP_ENTRY(_T("PLUGINVERSION"),     ACTIVEX_VERSION_ANSI)
-    REGMAP_ENTRY(_T("PLUGINDESCRIPTION"), kCiProgram)
-    REGMAP_ENTRY(_T("PLUGINPRODUCT"),     kCiProgram)
-    REGMAP_ENTRY(_T("PLUGINVENDOR"),      PUBLISHER_NAME_ANSI)
-    REGMAP_ENTRY(_T("PLUGINMIMETYPE"),    kOneClickPluginMimeTypeAnsi)
-    REGMAP_ENTRY(_T("SHELLNAME"),         kGoopdateFileName)
-    // Not fatal if "SHELLPATH" is empty because the side-effect would be that
-    // on Vista, the user will get prompted on invoking one-click.
-    REGMAP_ENTRY(_T("SHELLPATH"),         GetGoopdateShellPathForRegMap())
-    REGMAP_MODULE2(_T("NPONECLICK.DLL"),  ACTIVEX_FILENAME)
-  END_REGISTRY_MAP()
-  #pragma warning(pop)
-
-  BEGIN_COM_MAP(GoopdateCtrl)
-    COM_INTERFACE_ENTRY(IDispatch)
-    COM_INTERFACE_ENTRY(IObjectWithSite)
-    COM_INTERFACE_ENTRY(IObjectSafety)
-  END_COM_MAP()
-
-  DECLARE_NOT_AGGREGATABLE(GoopdateCtrl)
-  DECLARE_PROTECT_FINAL_CONSTRUCT();
-
-  // Installs the application that the passed-in manifest corresponds to.
-  STDMETHOD(Install)(BSTR cmd_line_args,
-                     VARIANT* success_callback,
-                     VARIANT* failure_callback);
-  STDMETHOD(Install2)(BSTR extra_args);
-
-  // Gets the version of the passed in application guid. If the application is
-  // not installed, returns an empty string.
-  STDMETHOD(GetInstalledVersion)(BSTR guid_string,
-                                 VARIANT_BOOL is_machine,
-                                 BSTR* version_string);
-
-  // Gets the version of the plugin.  This value will be ACTIVEX_VERSION_ANSI.
-  STDMETHOD(GetOneClickVersion)(long* version);
-
-  HRESULT FinalConstruct();
-  void FinalRelease();
-
- private:
-  void EnsureWorkerUrlSet();
-
-  scoped_ptr<OneClickWorker> oneclick_worker_;
-  bool is_worker_url_set_;
-
-  // If Admin, returns the path for Machine Goopdate. Else returns path for User
-  // Goopdate.
-  static CString GetGoopdateShellPathForRegMap();
-};
-
-}  // namespace omaha.
-
-#endif  // OMAHA_PLUGINS_ONECLICK_H__
-
diff --git a/plugins/oneclick.rgs b/plugins/oneclick.rgs
deleted file mode 100644
index 857ee85..0000000
--- a/plugins/oneclick.rgs
+++ /dev/null
@@ -1,97 +0,0 @@
-%HKROOT%
-{
-  NoRemove SOFTWARE
-  {
-    NoRemove MozillaPlugins
-    {
-      ForceRemove '@%PLUGINDOMAIN%/%PLUGINPRODUCT%;version=%PLUGINVERSION%'
-      {
-        val Path = s '%NPONECLICK.DLL%'
-        val Description = s '%PLUGINDESCRIPTION%'
-        val ProductName = s '%PLUGINPRODUCT%'
-        val Vendor = s '%PLUGINVENDOR%'
-        val Version = s '%PLUGINVERSION%'
-        MimeTypes
-        {
-          '%PLUGINMIMETYPE%'
-        }
-      }
-    }
-    NoRemove Microsoft
-    {
-      NoRemove Windows
-      {
-        NoRemove CurrentVersion
-        {
-          NoRemove Ext
-          {
-            NoRemove PreApproved
-            {
-              ForceRemove '%CLSID%'
-            }
-            NoRemove Stats
-            {
-              ForceRemove '%CLSID%'
-              {
-                iexplore
-                {
-                  AllowedDomains
-                  {
-                    '*'
-                  }
-                }
-              }
-            }
-          }
-        }
-      }
-      NoRemove 'Internet Explorer'
-      {
-        NoRemove 'Low Rights'
-        {
-          NoRemove 'ElevationPolicy'
-          {
-            ForceRemove '%CLSID%'
-            {
-              val AppName = s '%SHELLNAME%'
-              val AppPath = s '%SHELLPATH%'
-              val Policy = d '3'
-            }
-          }
-        }
-      }
-    }
-    NoRemove Classes
-    {
-      ForceRemove '%PROGID%' = s '%PLUGINPRODUCT% Plugin'
-      {
-        CLSID = s '%CLSID%'
-      }
-      NoRemove CLSID
-      {
-        ForceRemove '%CLSID%' = s '%PLUGINPRODUCT% Plugin'
-        {
-          ProgID = s '%PROGID%'
-          InprocServer32 = s '%MODULE%'
-          {
-            val ThreadingModel = s 'Apartment'
-          }
-        }
-      }
-      NoRemove MIME
-      {
-        NoRemove Database
-        {
-          NoRemove 'Content Type'
-          {
-            ForceRemove '%PLUGINMIMETYPE%'
-            {
-              val CLSID = s '%CLSID%'
-            }
-          }
-        }
-      }
-    }
-  }
-}
-
diff --git a/plugins/oneclick_atl_module.cc b/plugins/oneclick_atl_module.cc
deleted file mode 100644
index 221c222..0000000
--- a/plugins/oneclick_atl_module.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// The ATL module definition and module instance.
-
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "plugins/oneclick_idl.h"
-
-namespace omaha {
-
-class OneClickModule : public CAtlDllModuleT<OneClickModule> {
- public :
-  OneClickModule() {}
-  DECLARE_LIBID(LIBID_OneClickLib)
-};
-
-extern "C" {
-  OneClickModule _AtlModule;
-}
-
-}  // namespace omaha.
-
-HRESULT RegisterOrUnregisterDll(bool is_register) {
-  HRESULT hr = is_register ? omaha::_AtlModule.DllRegisterServer(false) :
-                             omaha::_AtlModule.DllUnregisterServer(false);
-  ASSERT(SUCCEEDED(hr), (_T("[RegisterOrUnregisterDll failed][%d][0x%08x]"),
-                         is_register, hr));
-  return hr;
-}
-
-// The standard COM entry points below are exported using a .def file.
-STDAPI DllCanUnloadNow() {
-  return omaha::_AtlModule.DllCanUnloadNow();
-}
-
-STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv) {
-  return omaha::_AtlModule.DllGetClassObject(rclsid, riid, ppv);
-}
-
-STDAPI DllRegisterServer() {
-  return omaha::goopdate_utils::RegisterOrUnregisterModule(
-      true,
-      &RegisterOrUnregisterDll);
-}
-
-STDAPI DllUnregisterServer() {
-  return omaha::goopdate_utils::RegisterOrUnregisterModule(
-      false,
-      &RegisterOrUnregisterDll);
-}
-
diff --git a/plugins/oneclick_browser_callback.h b/plugins/oneclick_browser_callback.h
deleted file mode 100644
index 25e0701..0000000
--- a/plugins/oneclick_browser_callback.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// OneClick browser-specific callback classes.  This is the abstract base class
-// for a browser-callback class to derive from.
-
-#ifndef OMAHA_PLUGINS_ONECLICK_BROWSER_CALLBACK_H__
-#define OMAHA_PLUGINS_ONECLICK_BROWSER_CALLBACK_H__
-
-#include <windows.h>
-
-#include "base/basictypes.h"
-
-namespace omaha {
-
-class OneClickBrowserCallback {
- public:
-  OneClickBrowserCallback() {}
-  virtual ~OneClickBrowserCallback() {}
-
-  virtual void DoSuccessCallback() = 0;
-  virtual void DoFailureCallback(HRESULT hr_error) = 0;
-
- private:
-  DISALLOW_EVIL_CONSTRUCTORS(OneClickBrowserCallback);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_PLUGINS_ONECLICK_BROWSER_CALLBACK_H__
-
diff --git a/plugins/oneclick_browser_callback_activex.cc b/plugins/oneclick_browser_callback_activex.cc
deleted file mode 100644
index a45d04a..0000000
--- a/plugins/oneclick_browser_callback_activex.cc
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// OneClick callback helper for ActiveX browsers.
-
-#include "omaha/plugins/oneclick_browser_callback_activex.h"
-
-#include <dispex.h>
-#include <winhttp.h>
-#include <atlbase.h>
-#include <atlcom.h>
-
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/string.h"
-#include "omaha/goopdate/goopdate_utils.h"
-
-// Note:  IE gives a dispinterface JScriptTypeInfo as our onxxxxx property
-// values. classid: C59C6B12-F6C1-11CF-8835-00A0C911E8B2
-// We invoke "dispatch id 0" to call the associated function.
-// See http://www.ddj.com/windows/184404200
-
-// We prefer to call things thru IDispatchEx in order to use DISPID_THIS.
-// Note: strangely, the parameters passed thru IDispatch are in reverse order as
-// compared to the method signature being invoked.
-
-namespace omaha {
-
-OneClickBrowserCallbackActiveX::OneClickBrowserCallbackActiveX() {
-}
-
-OneClickBrowserCallbackActiveX::~OneClickBrowserCallbackActiveX() {
-}
-
-HRESULT OneClickBrowserCallbackActiveX::Initialize(VARIANT* success_callback,
-                                                   VARIANT* failure_callback) {
-  // These two parameters are optional, but if they're valid VARIANTs then they
-  // need to be of type VT_DISPATCH or we fail.
-
-  if (!VariantIsNullOrUndefined(success_callback)) {
-    if (success_callback->vt == VT_DISPATCH) {
-      success_callback_dispatch_ = success_callback->pdispVal;
-    } else {
-      return E_INVALIDARG;
-    }
-  }
-
-  if (!VariantIsNullOrUndefined(failure_callback)) {
-    if (failure_callback->vt == VT_DISPATCH) {
-      failure_callback_dispatch_ = failure_callback->pdispVal;
-    } else {
-      return E_INVALIDARG;
-    }
-  }
-
-  return S_OK;
-}
-
-bool OneClickBrowserCallbackActiveX::VariantIsNullOrUndefined(
-    const VARIANT* var) {
-  return var->vt == VT_NULL || var->vt == VT_EMPTY;
-}
-
-void OneClickBrowserCallbackActiveX::DoSuccessCallback() {
-  CORE_LOG(L2, (_T("[DoSuccessCallback entered]")));
-
-  if (!success_callback_dispatch_) {
-    return;
-  }
-
-  const DISPID kDispId0 = 0;
-
-  HRESULT hr = 0;
-  DISPPARAMS dispparams = {0};
-  CComQIPtr<IDispatchEx> dispatchex =
-      static_cast<IDispatch*>(success_callback_dispatch_);
-
-  if (dispatchex) {
-    DISPID disp_this = DISPID_THIS;
-    VARIANT var[1];
-    var[0].vt = VT_DISPATCH;
-    var[0].pdispVal = dispatchex;
-
-    dispparams.rgvarg = var;
-    dispparams.rgdispidNamedArgs = &disp_this;
-    dispparams.cNamedArgs = 1;
-    dispparams.cArgs = 1;
-
-    hr = dispatchex->InvokeEx(kDispId0, LOCALE_USER_DEFAULT,
-                              DISPATCH_METHOD, &dispparams,
-                              NULL, NULL, NULL);
-  } else if (success_callback_dispatch_) {
-    // Fallback on IDispatch if needed.
-    UINT arg_err = 0;
-    dispparams.cArgs = 0;
-
-    hr = success_callback_dispatch_->Invoke(kDispId0,
-                                            IID_NULL,
-                                            LOCALE_SYSTEM_DEFAULT,
-                                            DISPATCH_METHOD,
-                                            &dispparams,
-                                            NULL,
-                                            NULL,
-                                            &arg_err);
-  }
-}
-
-void OneClickBrowserCallbackActiveX::DoFailureCallback(HRESULT hr_error) {
-  CORE_LOG(L2, (_T("[DoFailureCallback entered][hr_error=0x%x]"), hr_error));
-
-  if (!failure_callback_dispatch_) {
-    return;
-  }
-
-  const DISPID kDispId0 = 0;
-
-  HRESULT hr = 0;
-  DISPPARAMS dispparams = {0};
-  CComQIPtr<IDispatchEx> dispatchex =
-      static_cast<IDispatch*>(failure_callback_dispatch_);
-
-  if (dispatchex) {
-    DISPID disp_this = DISPID_THIS;
-    VARIANT var[2];
-    var[0].vt = VT_DISPATCH;
-    var[0].pdispVal = dispatchex;
-    var[1].vt = VT_I4;
-    var[1].intVal = hr_error;
-
-    dispparams.rgvarg = var;
-    dispparams.rgdispidNamedArgs = &disp_this;
-    dispparams.cNamedArgs = 1;
-    dispparams.cArgs = 2;
-
-    hr = dispatchex->InvokeEx(kDispId0, LOCALE_USER_DEFAULT,
-                              DISPATCH_METHOD, &dispparams,
-                              NULL, NULL, NULL);
-  } else if (failure_callback_dispatch_) {
-    // Fallback on IDispatch if needed.
-    UINT arg_err = 0;
-    VARIANT var[1];
-    var[0].vt = VT_I4;
-    var[0].intVal = hr_error;
-
-    dispparams.rgvarg = var;
-    dispparams.cArgs = 1;
-
-    hr = failure_callback_dispatch_->Invoke(kDispId0,
-                                            IID_NULL,
-                                            LOCALE_SYSTEM_DEFAULT,
-                                            DISPATCH_METHOD,
-                                            &dispparams,
-                                            NULL,
-                                            NULL,
-                                            &arg_err);
-  }
-}
-
-}  // namespace omaha
-
diff --git a/plugins/oneclick_browser_callback_activex.h b/plugins/oneclick_browser_callback_activex.h
deleted file mode 100644
index 8424665..0000000
--- a/plugins/oneclick_browser_callback_activex.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-// OneClick browser-specific callback class for IE/ActiveX.
-
-#ifndef OMAHA_PLUGINS_ONECLICK_BROWSER_CALLBACK_ACTIVEX_H__
-#define OMAHA_PLUGINS_ONECLICK_BROWSER_CALLBACK_ACTIVEX_H__
-
-#include <atlbase.h>
-#include <atlcom.h>
-
-#include "omaha/plugins/oneclick_browser_callback.h"
-
-namespace omaha {
-
-class OneClickBrowserCallbackActiveX : public OneClickBrowserCallback {
- public:
-  OneClickBrowserCallbackActiveX();
-  virtual ~OneClickBrowserCallbackActiveX();
-
-  // Initialize the callback with two VARIANT values which are Javascript
-  // functions to be called.  IE passes Javascript classes as an IDispatch
-  // wrapped inside a VARIANT.
-  // The VARIANT parameters are optional which means we won't call a function in
-  // those cases.
-  HRESULT Initialize(VARIANT* success_callback, VARIANT* failure_callback);
-
-  // Overrides from abstract base class.
-  virtual void DoSuccessCallback();
-  virtual void DoFailureCallback(HRESULT hr_error);
-
- private:
-  CComPtr<IDispatch> success_callback_dispatch_;
-  CComPtr<IDispatch> failure_callback_dispatch_;
-
-  // Returns true if var is VT_NULL or VT_EMPTY.
-  bool VariantIsNullOrUndefined(const VARIANT* var);
-
-  DISALLOW_EVIL_CONSTRUCTORS(OneClickBrowserCallbackActiveX);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_PLUGINS_ONECLICK_BROWSER_CALLBACK_ACTIVEX_H__
-
diff --git a/plugins/oneclick_browser_callback_npapi.cc b/plugins/oneclick_browser_callback_npapi.cc
deleted file mode 100644
index e2de655..0000000
--- a/plugins/oneclick_browser_callback_npapi.cc
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// OneClick helper functions.
-
-// Note:  oneclick_browser_callback_npapi.h cannot be included before system
-// headers due to conflicts between base/windows code and NPAPI
-// definitions.
-
-#pragma warning(push)
-#pragma warning(disable : 4201 4265)
-// 4201: nonstandard extension used : nameless struct/union
-// 4265: class has virtual functions, but destructor is not virtual
-
-#include "base/scoped_ptr.h"
-#include "omaha/plugins/npOneClick.h"
-#include "omaha/plugins/oneclick_browser_callback_npapi.h"
-
-#pragma warning(pop)
-
-namespace omaha {
-
-OneClickBrowserCallbackNpapi::OneClickBrowserCallbackNpapi()
-    : npp_(NULL), success_callback_(NULL), failure_callback_(NULL) {
-}
-
-OneClickBrowserCallbackNpapi::~OneClickBrowserCallbackNpapi() {
-  if (success_callback_) {
-    NPN_ReleaseObject(success_callback_);
-    success_callback_ = NULL;
-  }
-  if (failure_callback_) {
-    NPN_ReleaseObject(failure_callback_);
-    failure_callback_ = NULL;
-  }
-}
-
-HRESULT OneClickBrowserCallbackNpapi::Initialize(NPP npp,
-                                                 NPVariant success_callback,
-                                                 NPVariant failure_callback) {
-  npp_ = npp;
-
-  if (NPVARIANT_IS_OBJECT(success_callback)) {
-    success_callback_ = NPVARIANT_TO_OBJECT(success_callback);
-    NPN_RetainObject(success_callback_);
-  }
-
-  if (NPVARIANT_IS_OBJECT(failure_callback)) {
-    failure_callback_ = NPVARIANT_TO_OBJECT(failure_callback);
-    NPN_RetainObject(failure_callback_);
-  }
-
-  return S_OK;
-}
-
-void OneClickBrowserCallbackNpapi::DoSuccessCallback() {
-  CORE_LOG(L2, (_T("[DoSuccessCallback entered]")));
-
-  if (!success_callback_) {
-    CORE_LOG(L2, (_T("[DoSuccessCallback entered][NO success_callback_]")));
-    return;
-  }
-
-  CORE_LOG(L2, (_T("[DoSuccessCallback entered][valid success_callback_]")));
-  NPVariant rval;
-  NULL_TO_NPVARIANT(rval);
-
-  NPN_InvokeDefault(npp_,
-                    success_callback_,
-                    NULL,
-                    0,
-                    &rval);
-}
-
-void OneClickBrowserCallbackNpapi::DoFailureCallback(HRESULT hr_error) {
-  CORE_LOG(L2, (_T("[DoFailureCallback entered][hr_error=0x%x]"), hr_error));
-
-  if (!failure_callback_) {
-    CORE_LOG(L2, (_T("[DoFailureCallback entered][NO failure_callback_]")));
-    return;
-  }
-
-  CORE_LOG(L2, (_T("[DoFailureCallback entered][valid failure_callback_]")));
-  NPVariant rval;
-  NULL_TO_NPVARIANT(rval);
-
-  NPVariant arg;
-  NULL_TO_NPVARIANT(arg);
-  INT32_TO_NPVARIANT(hr_error, arg);
-
-  NPN_InvokeDefault(npp_,
-                    failure_callback_,
-                    &arg,
-                    1,
-                    &rval);
-}
-
-}  // namespace omaha
-
diff --git a/plugins/oneclick_browser_callback_npapi.h b/plugins/oneclick_browser_callback_npapi.h
deleted file mode 100644
index a93316d..0000000
--- a/plugins/oneclick_browser_callback_npapi.h
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-// OneClick browser-specific callback class for NPAPI based browsers.
-
-#ifndef OMAHA_PLUGINS_ONECLICK_BROWSER_CALLBACK_NPAPI_H__
-#define OMAHA_PLUGINS_ONECLICK_BROWSER_CALLBACK_NPAPI_H__
-
-#include "omaha/plugins/oneclick_browser_callback.h"
-
-namespace omaha {
-
-class OneClickBrowserCallbackNpapi : public OneClickBrowserCallback {
- public:
-  OneClickBrowserCallbackNpapi();
-  virtual ~OneClickBrowserCallbackNpapi();
-
-  // Initializes the callback class with the NPP and two NPVariant values which
-  // represent the Javascript functions to be called.  NPAPI passes Javascript
-  // functions as an NPObject wrapped within an NPVariant.
-  // The NPVariant parameters are optional (can be internally NULL) which means
-  // we won't call a function in those cases.
-  HRESULT Initialize(NPP npp,
-                     NPVariant success_callback,
-                     NPVariant failure_callback);
-
-  // Overrides from abstract base class.
-  virtual void DoSuccessCallback();
-  virtual void DoFailureCallback(HRESULT hr_error);
-
- private:
-  NPP npp_;
-  NPObject* success_callback_;
-  NPObject* failure_callback_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(OneClickBrowserCallbackNpapi);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_PLUGINS_ONECLICK_BROWSER_CALLBACK_NPAPI_H__
-
diff --git a/plugins/oneclick_idl.idl b/plugins/oneclick_idl.idl
deleted file mode 100644
index b21e3ed..0000000
--- a/plugins/oneclick_idl.idl
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Template IDL file for the OneClick ActiveX control. This template is a
-// complete IDL file in all but one respect; It has one replaceable entry
-// for the CLSID for GoopdateOneClickControl. This template is processed by
-// generate_oneclick_idl.py, which generates a GUID using UUIDGEN.EXE, and
-// writes out a (now complete) IDL file with the new CLSID.
-//
-// Background Information:
-// We generate new CLSIDs for each fresh build of Omaha. But we keep the MIME
-// Type constant, as long as we do not change the interface for the control.
-// The control is created with an OBJECT tag of the form:
-//   <OBJECT ID="OneClickCtrl" TYPE="application/x-vnd.Google.OneClickCtrl.1">
-//   </OBJECT>
-// And we register under HKCR\MIME\Database\ContentType, where we keep the MIME
-// type constant, but change the CLSID with each build. This allows us to "hot
-// swap" the control, and we don't face the issue of IE always using the older
-// version of the control unless it is restarted.
-
-import "oaidl.idl";
-import "ocidl.idl";
-
-[
-  object,
-  uuid(65CAE76D-A7FC-4629-A7C3-9AF61876894B),
-  dual,
-  helpstring("Google Update One Click Control Automation Interface"),
-  pointer_default(unique)
-]
-interface IGoogleUpdateOneClick : IDispatch
-{
-  // Deprecated. Will be removed in the next release of OneClick.
-  [id(1), helpstring("Install")] HRESULT Install(
-      [in] BSTR cmd_line_args,
-      [in] VARIANT* success_callback,
-      [in] VARIANT* failure_callback);
-
-  // New, easier way of calling Install. Use this for newer web pages.
-  [id(2), helpstring("Install2")] HRESULT Install2([in] BSTR extra_args);
-
-  [id(3), helpstring("GetInstalledVersion")] HRESULT GetInstalledVersion(
-      [in] BSTR guid_string,
-      [in] VARIANT_BOOL is_machine,
-      [out, retval] BSTR* version_string);
-
-  [id(4), helpstring("GetOneClickVersion")] HRESULT GetOneClickVersion(
-      [out, retval] long* version);
-};
-
-[
-  uuid(BBEFAF3A-5D73-4cc5-A2A1-34E5C55169B0),
-  version(1.0),
-  helpstring("Google Update One Click 1.0 Type Library")
-]
-library OneClickLib
-{
-  importlib("stdole2.tlb");
-  importlib("stdole32.tlb");
-
-  [
-    uuid(%s),
-    helpstring("Google Update One Click Control Class")
-  ]
-  coclass GoopdateOneClickControl
-  {
-    [default] interface IGoogleUpdateOneClick;
-  };
-};
diff --git a/plugins/oneclick_worker.cc b/plugins/oneclick_worker.cc
deleted file mode 100644
index 893d460..0000000
--- a/plugins/oneclick_worker.cc
+++ /dev/null
@@ -1,287 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// OneClick worker to handle threads and manage callbacks to the browser.
-
-#include "omaha/plugins/oneclick_worker.h"
-
-#include <dispex.h>
-#include <wininet.h>
-#include <atlbase.h>
-#include <atlcom.h>
-
-#include "omaha/common/app_util.h"
-#include "omaha/common/atl_regexp.h"
-#include "omaha/common/const_cmd_line.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/string.h"
-#include "omaha/goopdate/command_line_builder.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/webplugin_utils.h"
-#include "omaha/worker/application_manager.h"
-
-#define INITGUID
-#include <guiddef.h>  // NOLINT
-
-namespace omaha {
-
-const TCHAR* site_lock_pattern_strings[] = {
-  _T("^https?://(gears)|(mail)|(tools)|(www)|(desktop)\\.google\\.com/"),
-  _T("^https?://www\\.google\\.(ad)|(bg)|(ca)|(cn)|(cz)|(de)|(es)|(fi)|(fr)|(gr)|(hr)|(hu)|(it)|(ki)|(kr)|(lt)|(lv)|(nl)|(no)|(pl)|(pt)|(ro)|(ru)|(sk)|(sg)|(sl)|(sr)|(vn)/"),  // NOLINT
-  _T("^https?://www\\.google\\.co\\.(hu)|(id)|(il)|(it)|(jp)|(kr)|(th)|(uk)/"),
-  _T("^https?://www\\.google\\.com\\.(ar)|(au)|(br)|(cn)|(et)|(gr)|(hr)|(ki)|(lv)|(om)|(pl)|(pt)|(ru)|(sg)|(sv)|(tr)|(vn)/"),  // NOLINT
-};
-
-typedef std::vector<AtlRegExp*> HostRegexp;
-
-class SiteLockPatterns {
- public:
-  SiteLockPatterns() {}
-  ~SiteLockPatterns();
-  bool AddPattern(const CString& host_pattern);
-  bool Match(const CString& url);
-
- private:
-  HostRegexp hosts_;
-};
-
-bool SiteLockPatterns::AddPattern(const CString& host_pattern) {
-  scoped_ptr<AtlRegExp> regex(new AtlRegExp);
-  REParseError error = regex->Parse(host_pattern);
-  if (error != REPARSE_ERROR_OK) {
-    return false;
-  }
-
-  hosts_.push_back(regex.release());
-  return true;
-}
-
-bool SiteLockPatterns::Match(const CString& url) {
-  if (url.IsEmpty()) {
-    return false;
-  }
-
-  ASSERT1(!hosts_.empty());
-  for (size_t i = 0; i < hosts_.size(); ++i) {
-    AtlMatchContext url_match;
-    if (hosts_[i]->Match(url, &url_match)) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-SiteLockPatterns::~SiteLockPatterns() {
-  for (size_t i = 0; i < hosts_.size(); ++i) {
-    delete hosts_[i];
-  }
-}
-
-OneClickWorker::OneClickWorker() {
-  CORE_LOG(L2, (_T("OneClickWorker::OneClickWorker()")));
-
-  site_lock_patterns_.reset(new SiteLockPatterns());
-  // TODO(Omaha): Download new patterns on the fly.
-  for (int i = 0; i < arraysize(site_lock_pattern_strings); ++i) {
-    VERIFY1(site_lock_patterns_->AddPattern(site_lock_pattern_strings[i]));
-  }
-
-  CString update_dev_host_pattern;
-  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
-                                 kRegValueOneClickHostPattern,
-                                 &update_dev_host_pattern)) &&
-      !update_dev_host_pattern.IsEmpty()) {
-    VERIFY1(site_lock_patterns_->AddPattern(update_dev_host_pattern));
-  }
-}
-
-OneClickWorker::~OneClickWorker() {
-  CORE_LOG(L2, (_T("OneClickWorker::~OneClickWorker()")));
-}
-
-HRESULT OneClickWorker::Initialize() {
-  CORE_LOG(L2, (_T("[OneClickWorker::Initialize]")));
-
-  return S_OK;
-}
-
-HRESULT OneClickWorker::Shutdown() {
-  CORE_LOG(L2, (_T("[OneClickWorker::Shutdown]")));
-
-  return S_OK;
-}
-
-bool OneClickWorker::InApprovedDomain() {
-  ASSERT1(!browser_url_.IsEmpty());
-  return site_lock_patterns_->Match(browser_url_);
-}
-
-HRESULT OneClickWorker::DoOneClickInstall(
-    const TCHAR* cmd_line_args,
-    OneClickBrowserCallback* browser_callback) {
-  CORE_LOG(L2, (_T("[OneClickWorker::DoOneClickInstall]")
-                _T("[cmd_line_args=%s][browser_url=%s]"),
-                cmd_line_args, browser_url_));
-  ASSERT1(browser_callback);
-
-  HRESULT hr = DoOneClickInstallInternal(cmd_line_args);
-  if (SUCCEEDED(hr)) {
-    browser_callback->DoSuccessCallback();
-  } else {
-    CORE_LOG(LE, (_T("[DoOneClickInstallInternal failed][0x%x]"), hr));
-    browser_callback->DoFailureCallback(hr);
-  }
-
-  // Return success in all cases. The failure callback has already been called
-  // above, and we don't want to cause a failure path to be called again when
-  // the JavaScript catches the exception.
-  return S_OK;
-}
-
-HRESULT OneClickWorker::DoOneClickInstall2(const TCHAR* extra_args) {
-  CommandLineBuilder builder(COMMANDLINE_MODE_INSTALL);
-  builder.set_extra_args(extra_args);
-  return DoOneClickInstallInternal(builder.GetCommandLineArgs());
-}
-
-HRESULT OneClickWorker::DoOneClickInstallInternal(const TCHAR* cmd_line_args) {
-  if (!InApprovedDomain()) {
-    return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED;
-  }
-
-#ifdef _DEBUG
-  // If the args are exactly __DIRECTNOTIFY__ then just fire the event
-  // out of this thread.  This allows for easy testing of the
-  // browser interface without requiring launch of
-  // google_update.exe.
-  if (0 == _wcsicmp(L"__DIRECTNOTIFY__", cmd_line_args)) {
-    return S_OK;
-  }
-#endif
-
-  HRESULT hr = webplugin_utils::VerifyResourceLanguage(cmd_line_args);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[VerifyResourceLanguage failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  CString url_domain;
-  hr = GetUrlDomain(browser_url_, &url_domain);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  CString url_domain_encoded;
-  CString cmd_line_args_encoded;
-  hr = StringEscape(url_domain, false, &url_domain_encoded);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = StringEscape(cmd_line_args, false, &cmd_line_args_encoded);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  CommandLineBuilder builder(COMMANDLINE_MODE_WEBPLUGIN);
-  builder.set_webplugin_url_domain(url_domain_encoded);
-  builder.set_webplugin_args(cmd_line_args_encoded);
-  builder.set_install_source(kCmdLineInstallSource_OneClick);
-  CString final_cmd_line_args = builder.GetCommandLineArgs();
-
-  CORE_LOG(L2, (_T("[OneClickWorker::DoOneClickInstallInternal]")
-                _T("[Final command line params: %s]"),
-                final_cmd_line_args));
-
-  scoped_process process_goopdate;
-
-  hr = goopdate_utils::StartGoogleUpdateWithArgs(
-          goopdate_utils::IsRunningFromOfficialGoopdateDir(true),
-          final_cmd_line_args,
-          address(process_goopdate));
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[OneClickWorker::DoOneClickInstallInternal]")
-                  _T("[Failed StartGoogleUpdateWithArgs: 0x%x"),
-                  hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT OneClickWorker::GetInstalledVersion(const TCHAR* guid_string,
-                                            bool is_machine,
-                                            CString* version_string) {
-  CORE_LOG(L2, (_T("[GoopdateCtrl::GetInstalledVersion][%s][%d]"),
-                guid_string, is_machine));
-  if (!InApprovedDomain()) {
-    return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED;
-  }
-
-  ASSERT1(version_string);
-  version_string->Empty();
-
-  AppManager app_manager(is_machine);
-  ProductData product_data;
-  HRESULT hr = app_manager.ReadProductDataFromStore(StringToGuid(guid_string),
-                                                    &product_data);
-  if (SUCCEEDED(hr) && !product_data.app_data().is_uninstalled()) {
-    *version_string = product_data.app_data().version();
-  }
-  return S_OK;
-}
-
-HRESULT OneClickWorker::GetOneClickVersion(long* version) {   // NOLINT
-  CORE_LOG(L2, (_T("[OneClickWorker::GetOneClickVersion]")));
-  if (!InApprovedDomain()) {
-    return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED;
-  }
-
-  if (!version) {
-    return E_POINTER;
-  }
-
-  *version = atoi(ACTIVEX_VERSION_ANSI);  // NOLINT
-  return S_OK;
-}
-
-HRESULT OneClickWorker::GetUrlDomain(const CString& url, CString* url_domain) {
-  ASSERT1(url_domain);
-  url_domain->Empty();
-
-  URL_COMPONENTS urlComponents = {0};
-  urlComponents.dwStructSize = sizeof(urlComponents);
-  urlComponents.dwSchemeLength = 1;
-  urlComponents.dwHostNameLength = 1;
-  if (!::InternetCrackUrl(url, 0, 0, &urlComponents)) {
-    HRESULT hr = HRESULTFromLastError();
-    CORE_LOG(L2, (_T("[OneClickWorker failed InternetCrackUrl hr=0x%x]"), hr));
-    return hr;
-  }
-
-  CString scheme(urlComponents.lpszScheme, urlComponents.dwSchemeLength);
-  CString host_name(urlComponents.lpszHostName, urlComponents.dwHostNameLength);
-  ASSERT1(!scheme.IsEmpty());
-  ASSERT1(!host_name.IsEmpty());
-
-  url_domain->Format(_T("%s://%s/"), scheme, host_name);
-  return S_OK;
-}
-
-}  // namespace omaha
-
diff --git a/plugins/oneclick_worker.h b/plugins/oneclick_worker.h
deleted file mode 100644
index f9c2f7e..0000000
--- a/plugins/oneclick_worker.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// OneClick worker class shared by the different plugin architectures.
-// This class does most of the OneClick heavy lifting.
-
-#ifndef OMAHA_PLUGINS_ONECLICK_WORKER_H__
-#define OMAHA_PLUGINS_ONECLICK_WORKER_H__
-
-#include <windows.h>
-
-#include "base/scoped_ptr.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/plugins/oneclick_browser_callback.h"
-
-namespace omaha {
-
-class SiteLockPatterns;
-
-class OneClickWorker {
- public:
-  OneClickWorker();
-  ~OneClickWorker();
-
-  HRESULT Initialize();
-  HRESULT Shutdown();
-
-  // Performs a OneClick install.
-  // cmd_line_args - arguments to eventually be passed to googleupdate.exe.
-  // browser_callback - Callback class to fire success/failure events to.
-  // NOTE:  OneClickWorker assumes memory ownership of browser_callback.
-  HRESULT DoOneClickInstall(const TCHAR* cmd_line_args,
-                            OneClickBrowserCallback* browser_callback);
-  // The incoming extra_args are used to construct an "/install" command line.
-  HRESULT DoOneClickInstall2(const TCHAR* extra_args);
-
-  HRESULT GetInstalledVersion(const TCHAR* guid_string,
-                              bool is_machine,
-                              CString* version_string);
-
-  HRESULT GetOneClickVersion(long* version);
-
-  bool InApprovedDomain();
-
-  void set_browser_url(const TCHAR* browser_url) {
-    browser_url_ = browser_url;
-    browser_url_.MakeLower();
-  }
-
- private:
-  HRESULT DoOneClickInstallInternal(const TCHAR* cmd_line_args);
-  HRESULT GetUrlDomain(const CString& url, CString* url_domain);
-
-  CString browser_url_;
-  scoped_ptr<SiteLockPatterns> site_lock_patterns_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(OneClickWorker);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_PLUGINS_ONECLICK_WORKER_H__
-
diff --git a/plugins/plugin_version.rc b/plugins/plugin_version.rc
new file mode 100644
index 0000000..4970160
--- /dev/null
+++ b/plugins/plugin_version.rc
@@ -0,0 +1,85 @@
+// Copyright 2007 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// This file contains the unlocalized plugin version resources.
+
+#include <afxres.h>
+
+// The "040904e4" block (for codepage 1252 Ansi Latin) is required for
+// the Netscape Plugin stuff to see the MIMETYPE value in the resource block.
+
+// TODO(omaha): FileDescription is what shows up in "about:plugins" for
+// Mozilla. Format the description to include the url of the project.
+// Update: July 2010. I'm not sure why this comment requests the URL. We should
+// include the version, though, since that is used by version-checking websites.
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+// Resource Editor does not handle constants from main.scons.
+#ifndef APSTUDIO_INVOKED
+ FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_PATCH
+ PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_PATCH
+#endif  // APSTUDIO_INVOKED
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#if defined _DEBUG && OFFICIAL_BUILD
+ FILEFLAGS VS_FF_DEBUG
+#elif defined _DEBUG
+ FILEFLAGS VS_FF_DEBUG | VS_FF_PRIVATEBUILD
+#elif !OFFICIAL_BUILD
+ FILEFLAGS VS_FF_PRIVATEBUILD
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904e4"
+        BEGIN
+// Requires constants from mains.scons that cannot be loaded in Resource Editor.
+#ifndef APSTUDIO_INVOKED
+            VALUE "CompanyName", FULL_COMPANY_NAME_ANSI
+            VALUE "FileDescription", OMAHA_APP_NAME_ANSI
+            VALUE "FileVersion", VERSION_NUMBER_STRING
+            VALUE "InternalName", OMAHA_APP_NAME_ANSI
+            VALUE "LegalCopyright", OMAHA_COPYRIGHT_STRING_ENGLISH
+            VALUE "OriginalFilename", PLUGIN_FILENAME
+            VALUE "ProductName", OMAHA_APP_NAME_ANSI
+            VALUE "ProductVersion", VERSION_NUMBER_STRING
+  #ifdef _DEBUG
+            VALUE "Debug", ""
+  #endif
+  #if !OFFICIAL_BUILD
+            VALUE "PrivateBuild", BUILD_NUMBER
+  #endif
+
+  #ifdef MERGED_MIME_TYPE
+            VALUE "MIMEType", MERGED_MIME_TYPE
+  #endif
+#else
+            VALUE "_SpecialView",
+                  "Most values are not shown in Resource Editor because they "
+                  "require build file constants."
+#endif  // APSTUDIO_INVOKED
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1252
+    END
+END
diff --git a/plugins/resource.h b/plugins/resource.h
deleted file mode 100644
index ac22f29..0000000
--- a/plugins/resource.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Symbol definitions for the OneClick DLL resources.
-
-#ifndef OMAHA_PLUGINS_RESOURCE_H__
-#define OMAHA_PLUGINS_RESOURCE_H__
-
-// The id of the ATL COM registration script
-#define IDR_ONECLICK              100
-
-#endif  // OMAHA_PLUGINS_RESOURCE_H__
-
diff --git a/plugins/resource.rc b/plugins/resource.rc
deleted file mode 100644
index 54fe76c..0000000
--- a/plugins/resource.rc
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2005-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// One-Click resource file.
-
-
-
-#include "common/const_config.h"
-
-#define MIME_TYPE kOneClickPluginMimeTypeAnsi
-
-#include "goopdate/goopdate_version.rc"
-
-
-// COM type library.
-// This MIDL-generated file goes under /build/MODE/obj, which is in our RC path
-1             TYPELIB   "plugins\\oneclick_idl.tlb"
-
-#include "omaha/goopdate/resource.h"
-
-// ATL COM registration file.
-100              REGISTRY        "oneclick.rgs"
-
-
-
diff --git a/plugins/sitelock.h b/plugins/sitelock.h
deleted file mode 100644
index 7449fcf..0000000
--- a/plugins/sitelock.h
+++ /dev/null
@@ -1,1049 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// note:  this file is originally from Microsoft for SiteLock, but has been
-// modified to support getting the browser URL via NPAPI so we can use this
-// for validating within Firefox and other browsers as well
-
-#ifndef OMAHA_PLUGINS_SITELOCK_H__
-#define OMAHA_PLUGINS_SITELOCK_H__
-
-///////////////////////////////////////////////////////////////////////////////
-// SiteLock 1.14
-// ATL sample code for restricting activation of ActiveX controls.
-// Copyright Microsoft Corporation. All rights reserved.
-// Last updated: July 19 2007
-
-
-// Includes
-#include <atlctl.h>
-#include <comdef.h>
-#include <shlguid.h>
-#include <dispex.h>   // IObjectIdentity
-
-// Pragmas
-#pragma once
-
-#include "common/logging.h"
-// Version information
-#define SITELOCK_VERSION 0x00010014   // 1.14
-
-#ifdef SITELOCK_SUPPORT_DOWNLOAD
-#pragma message("sitelock.h : This version of SiteLock " \
-                "does not support downloading.")
-#endif
-
-// Overview:
-// To enable scripting, developers must declare their ActiveX controls as
-// "safe for scripting".
-// This is done by implementing interface IObjectSafety, which comes with very
-// important safety assumptions. Once marked as "safe for scripting", ActiveX
-// controls may be activated by untrusted web sites. Therefore "safe for
-// scripting" controls must guarantee all their methods are safe regardless of
-// the activation context. Practically however, it may not be possible for an
-// ActiveX control to guarantee safety in all activation contexts. The site lock
-// framework allows developers to specify which zones and domains can
-// instantiate ActiveX controls. For example, this may allow a developer
-// to implement methods that can only be called if the activation context is the
-// intranet zone.
-
-// Usage:
-// 1/ Include current header file sitelock.h (after including ATL header files).
-//
-// 2/ Derive from "public IObjectSafetySiteLockImpl
-//                   <CYourClass, INTERFACESAFE_FOR...>,".
-//    This replaces the default IObjectSafetyImpl interface implementation.
-//
-// 3/ Add the following to your control's COM map:
-//    COM_INTERFACE_ENTRY(IObjectSafety)
-//    COM_INTERFACE_ENTRY(IObjectSafetySiteLock)
-//
-// 4/ Add one of the following to specify allowed activation contexts:
-//    a) A public (or friend) member variable, for example:
-//    const CYourClass::SiteList CYourClass::rgslTrustedSites[6] =
-//       {{ SiteList::Deny,  L"http",  L"users.microsoft.com" },
-//        { SiteList::Allow, L"http",  L"microsoft com"       },
-//        { SiteList::Allow, L"http",  SITELOCK_INTRANET_ZONE },
-//        { SiteList::Deny,  L"https", L"users.microsoft.com" },
-//        { SiteList::Allow, L"https", L"microsoft.com"       },
-//        { SiteList::Allow, L"https", SITELOCK_INTRANET_ZONE }};
-//
-//    b) A set of site lock macros, for example:
-//    #define SITELOCK_USE_MAP (prior to including sitelock.h)
-//    BEGIN_SITELOCK_MAP()
-//        SITELOCK_DENYHTTP    ( L"users.microsoft.com" )
-//        SITELOCK_ALLOWHTTP  ( L"microsoft com"       )
-//        SITELOCK_ALLOWHTTP  ( SITELOCK_INTRANET_ZONE )
-//        SITELOCK_DENYHTTPS  ( L"users.microsoft.com" )
-//        SITELOCK_ALLOWHTTPS  ( L"microsoft.com"       )
-//        SITELOCK_ALLOWHTTPS  ( SITELOCK_INTRANET_ZONE )
-//    END_SITELOCK_MAP()
-//
-//    The examples above block "*.users.microsoft.com" sites (http and https).
-//    The examples above allow "*.microsoft.com" sites (http and https).
-//    The examples above allow intranet sites (http and https).
-//
-// 5/ Choose an expiry lifespan:
-//    You can specify the lifespan of your control one of two ways.
-//    By declaring an enumeration (slightly more efficient):
-//       enum { dwControlLifespan = (lifespan in days) };
-//    By declaring a member variable:
-//       static const DWORD dwControlLifespan = (lifespan in days);
-//    When in doubt, choose a shorter duration rather than a longer one.
-//    Expiration can be disabled by adding #define SITELOCK_NO_EXPIRY before
-//    including sitelock.h.
-//
-// 6/ Implement IObjectWithSite or IOleObject:
-//    IObjectWithSite is a lightweight interface able to indicate the
-//      activation URL to site lock.
-//    IOleObject is a heavier interface providing additional OLE capabilities.
-//    If you need IOleObject, add #define SITELOCK_USE_IOLEOBJECT before
-//      including sitelock.h.
-//    Otherwise, simply implement IObjectWithSite:
-//      - Derive from "IObjectWithSiteImpl<CYourClass>".
-//      - Add COM_INTERFACE_ENTRY(IObjectWithSite) to your control's COM map.
-//    You should never implement both IObjectWithSite and IOleObject.
-//
-// 7/ Link with urlmon.lib.
-
-// Detailed usage:
-//   --- Entries ---:
-//   Site lock entries are defined by the following elements:
-//   iAllowType is:
-//    SiteList::Allow:    allowed location
-//      SiteList::Deny:      blocked location
-//   szScheme is:
-//    L"http":        non-SSL location
-//    L"https":        SSL-enabled location
-//    Other:          in rare cases, the scheme may be outlook:, ms-help:, etc.
-//   szDomain is:
-//    Doman:          a string defining a domain
-//    Zone:          a constant specifying a zone
-//
-//    --- Ordering ---:
-//    Entries are matched in the order they appear in.
-//    The first entry that matches will be accepted.
-//    Deny entries should therefore be placed before allow entries.
-//
-//    --- Protocols ---:
-//    To support multiple protocols (http and https), define separate entries.
-//
-//    --- Domain names ---:
-//    This sample code performs a case-sensitive comparison after domain
-//    normalization. Whether domain normalization converts strings to lower
-//    case depends on the scheme provider.
-//
-//    If a domain does not contain any special indicator, only domains with the
-//    right suffix will match. For example:
-//    An entry of "microsoft.com" will match "microsoft.com".
-//      An entry of "microsoft.com" will match "office.microsoft.com"
-//      An entry of "microsoft.com" will not match "mymicrosoft.com"
-//    An entry of "microsoft.com" will not match "www.microsoft.com.hacker.com"
-//
-//    If a domain begins with "*.", only child domains will match.
-//    For example:
-//    An entry of "*.microsoft.com" will match "foo.microsoft.com".
-//    An entry of "*.microsoft.com" will not match "microsoft.com".
-//
-//    If a domain begins with "=", only the specified domain will match.
-//    For example:
-//    An entry of "=microsoft.com" will match "microsoft.com".
-//    An entry of "=microsoft.com" will not match "foo.microsoft.com".
-//
-//    If a domain is set to "*", all domains will match.
-//    This is useful to only restrict to specific schemes (ex: http vs. https).
-//
-//    If a domain name is NULL, then the scheme provider should return an error
-//    when asking for the domain. This is appropriate for protocols
-//    (outlook: or ms-help:) that do not use server names.
-//
-//    If a domain name is SITELOCK_INTRANET_ZONE, then any server in the
-//    Intranet zone will match. Due to a zone limitation, sites in the user's
-//    Trusted Sites list will also match. However, since Trusted Sites
-//    typically permit downloading and running of unsigned, unsafe controls,
-//    security is limited for those sites anyway.
-//
-//    If a domain name is SITELOCK_MYCOMPUTER_ZONE, then any page residing on
-//    the user's local machine will match.
-//
-//    If a domain name is SITELOCK_TRUSTED_ZONE, then any page residing in the
-//    user's Trusted Sites list will match.
-
-
-// Language checks
-#ifndef __cplusplus
-#error ATL Requires C++
-#endif
-
-// Windows constants
-#if (WINVER < 0x0600)
-#define IDN_USE_STD3_ASCII_RULES  0x02  // Enforce STD3 ASCII restrictions
-#endif
-
-// Function prototypes
-typedef int (WINAPI * PFN_IdnToAscii)(DWORD, LPCWSTR, int, LPWSTR, int);
-
-// Macros
-#ifndef cElements
-template<typename T> static char cElementsVerify(void const *, T) throw() {
-  return 0;
-}
-
-template<typename T> static void cElementsVerify(T *const, T *const *) throw() {
-};
-
-#define cElements(arr) (sizeof(cElementsVerify(arr, &(arr))) * \
-                        (sizeof(arr)/sizeof(*(arr))))
-#endif
-
-// Restrictions
-#define SITELOCK_INTRANET_ZONE    ((const OLECHAR *)-1)
-#define SITELOCK_MYCOMPUTER_ZONE  ((const OLECHAR *)-2)
-#define SITELOCK_TRUSTED_ZONE    ((const OLECHAR *)-3)
-
-#ifndef SITELOCK_NO_EXPIRY
-// Helper functions for expiry
-#if defined(_WIN64) && defined(_M_IA64)
-#pragma section(".base", long, read, write)           // NO_LINT
-extern "C" __declspec(allocate(".base")) extern IMAGE_DOS_HEADER __ImageBase;
-#else
-extern "C" IMAGE_DOS_HEADER __ImageBase;
-#endif
-#define ImageNtHeaders(pBase) ((PIMAGE_NT_HEADERS)((PCHAR)(pBase) + \
-                                    ((PIMAGE_DOS_HEADER)(pBase))->e_lfanew))
-
-#define LODWORD(_qw)    ((DWORD)(_qw))
-#define HIDWORD(_qw)    ((DWORD)(((_qw) >> 32) & 0xffffffff))
-inline void _UNIXTimeToFILETIME(time_t t, LPFILETIME ft) {
-  // The time_t is a 32-bit value for the number of seconds since
-  // January 1, 1970.
-  // A FILETIME is a 64-bit for the number of 100-nanosecond periods since
-  // January 1, 1601.
-  // Convert by multiplying the time_t value by 1e+7 to get to the same base
-  // granularity, then add the numeric equivalent of January 1, 1970 as
-  // FILETIME.
-
-  ULONGLONG qw = ((ULONGLONG)t * 10000000ui64) + 116444736000000000ui64;
-  ft->dwHighDateTime = HIDWORD(qw);
-  ft->dwLowDateTime = LODWORD(qw);
-}
-inline time_t _FILETIMEToUNIXTime(LPFILETIME ft) {
-  ULONGLONG qw = (((ULONGLONG)ft->dwHighDateTime)<<32) + ft->dwLowDateTime;
-  return (time_t)((qw - 116444736000000000ui64) / 10000000ui64);
-}
-#endif  // SITELOCK_NO_EXPIRY
-
-// Interface declaring "safe for scripting" methods with additional site lock
-// capabilities
-class __declspec(uuid("7FEB54AE-E3F9-40FC-AB5A-28A545C0F193"))
-    ATL_NO_VTABLE IObjectSafetySiteLock : public IObjectSafety {
- public:
-  // Site lock entry definition
-  struct SiteList {
-    enum SiteListCategory {
-      Allow,                // permit
-      Deny,                // disallow
-      Download              // OBSOLETE, do not use
-    } iAllowType;
-    const OLECHAR*    szScheme;    // scheme (http or https)
-    const OLECHAR*    szDomain;    // domain
-  };
-
-  // Capability definition
-  enum Capability {
-    CanDownload    = 0x00000001,  // OBSOLETE. Here for backwards compat only.
-    UsesIOleObject = 0x00000002,  // Use IOleObject instead of IObjectWithSite.
-    HasExpiry     = 0x00000004,  // Control will expire when lifespan elapsed.
-  };
-
-  // Returns capabilities (this can be used by testing tools to query for
-  // custom capabilities or version information)
-  STDMETHOD(GetCapabilities)(DWORD* pdwCapability) = 0;
-
-  // Returns site lock entries controlling activation
-  STDMETHOD(GetApprovedSites)(const SiteList** pSiteList, DWORD* cSites)  = 0;
-
-  // Returns lifespan as number of days and date (version 1.05 or higher)
-  STDMETHOD(GetExpiryDate)(DWORD* pdwLifespan, FILETIME* pExpiryDate)    = 0;
-};
-
-
-inline LPCTSTR const UrlZoneToString(DWORD dwZone) {
-  switch (dwZone) {
-    case URLZONE_LOCAL_MACHINE: return _T("URLZONE_LOCAL_MACHINE");
-    case URLZONE_INTRANET:      return _T("URLZONE_INTRANET");
-    case URLZONE_TRUSTED:       return _T("URLZONE_TRUSTED");
-    case URLZONE_INTERNET:      return _T("URLZONE_INTERNET");
-    case URLZONE_UNTRUSTED:     return _T("URLZONE_UNTRUSTED");
-    default:                    return _T("URLZONE_UNKNOWN");
-  }
-}
-
-bool AreObjectsEqual(IDispatch* disp1, IDispatch* disp2) {
-  // if the arguments are equal then the objects are equal
-  if (disp1 == disp2) return true;
-
-  // if the arguments are not equal, then compare the IUnknowns
-  if (disp1 && disp2) {
-    CComPtr<IUnknown> unk1;
-    CComPtr<IUnknown> unk2;
-    // This must always succeed.
-    VERIFY1(SUCCEEDED(disp1->QueryInterface(&unk1)));
-    VERIFY1(SUCCEEDED(disp2->QueryInterface(&unk2)));
-    ASSERT1(unk1 && unk2);
-
-    if (unk1 == unk2) return true;
-
-    // Not all the hope is lost. If the IUnknown pointers are different, try
-    // to query for object identity and use that to compare.
-    if (unk1 && unk2) {
-      CComPtr<IObjectIdentity> object_identity;
-      if (SUCCEEDED(unk1.QueryInterface(&object_identity))) {
-        if (object_identity) {
-          HRESULT hr = object_identity->IsEqualObject(unk2);
-          ASSERT(SUCCEEDED(hr), (_T("")));
-          return hr == S_OK;
-        }
-      }
-    }
-  }
-
-  return false;
-}
-
-
-#ifdef SITELOCK_USE_MAP
-// Site lock actual map entry macro
-#define SITELOCK_ALLOWHTTPS(domain) \
-    {IObjectSafetySiteLock::SiteList::Allow,  L"https",  domain},
-#define SITELOCK_DENYHTTPS(domain)  \
-    {IObjectSafetySiteLock::SiteList::Deny,    L"https",  domain},
-#define SITELOCK_ALLOWHTTP(domain)  \
-    {IObjectSafetySiteLock::SiteList::Allow,  L"http",  domain},
-#define SITELOCK_DENYHTTP(domain)   \
-    {IObjectSafetySiteLock::SiteList::Deny,    L"http",  domain},
-
-// Site lock begin map entry macro
-#define BEGIN_SITELOCK_MAP() \
-  static const IObjectSafetySiteLock::SiteList *                             \
-      GetSiteLockMapAndCount(DWORD* dwCount)                                 \
-{                                                                            \
-  static IObjectSafetySiteLock::SiteList rgslTrustedSites[] = {              \
-                                                                    // NO_LINT
-// Site lock end map entry macro
-#define END_SITELOCK_MAP()                                                   \
-{(IObjectSafetySiteLock::SiteList::SiteListCategory)0, 0, 0}};               \
-  *dwCount = cElements(rgslTrustedSites) - 1;                                \
-  return rgslTrustedSites;                                                   \
-}                                                                            \
-  static const IObjectSafetySiteLock::SiteList * GetSiteLockMap()            \
-{                                                                            \
-  DWORD dwCount = 0;                                                         \
-  return GetSiteLockMapAndCount(&dwCount);                                   \
-}                                                                            \
-  static DWORD GetSiteLockMapCount()                                         \
-{                                                                            \
-  DWORD dwCount = 0;                                                         \
-  GetSiteLockMapAndCount(&dwCount);                                          \
-  return dwCount;                                                            \
-}                                                                            \
-
-#endif  // SITELOCK_USE_MAP
-
-///////////////////////////////////////////////////////////////////////////////
-// CSiteLock - Site lock templated class
-template <typename T>
-class ATL_NO_VTABLE CSiteLock {
- public:
-#ifdef SITELOCK_NO_EXPIRY
-  bool ControlExpired(DWORD = 0)    { return false; }
-#else
-  bool ControlExpired(DWORD dwExpiresDays = T::dwControlLifespan) {
-    SYSTEMTIME st = {0};
-    FILETIME ft = {0};
-
-    GetSystemTime(&st);
-    if (!SystemTimeToFileTime(&st, &ft))
-      return true;
-
-    time_t ttTime = _FILETIMEToUNIXTime(&ft);
-    time_t ttExpire = ImageNtHeaders(&__ImageBase)->FileHeader.TimeDateStamp;
-    ttExpire += dwExpiresDays*86400;
-
-    return (ttTime > ttExpire);
-  }
-#endif
-
-#ifdef SITELOCK_USE_MAP
-  // Checks if the activation URL is in an allowed domain / zone
-  bool InApprovedDomain(
-      const IObjectSafetySiteLock::SiteList* rgslTrustedSites =
-          T::GetSiteLockMap(),
-      int cTrustedSites = T::GetSiteLockMapCount()) {
-#else
-  // Checks if the activation URL is in an allowed domain / zone
-  bool InApprovedDomain(
-      const IObjectSafetySiteLock::SiteList* rgslTrustedSites =
-          T::rgslTrustedSites,
-      int cTrustedSites = cElements(T::rgslTrustedSites)) {
-#endif
-    // Retrieve the activation URL
-    CComBSTR  bstrUrl;
-
-    if (!GetOurUrl(bstrUrl)) {
-      CORE_LOG(LEVEL_ERROR,
-               (_T("[CSiteLock::InApprovedDomain]")
-                _T("[unsafe: failed to get the url]")));
-      return false;
-    }
-
-    return InApprovedDomain(bstrUrl, rgslTrustedSites, cTrustedSites);
-  }
-
- protected:
-
-  // Retrieves the activation URL
-  bool GetOurUrl(BSTR* bstr_url) {
-    if (NULL == bstr_url) {
-      return false;
-    }
-    CComPtr<IServiceProvider> spSrvProv;
-    if (!GetServiceProvider(&spSrvProv)) {
-      return false;
-    }
-    ASSERT1(spSrvProv);
-
-    // See if we're hosted within IE
-    // Query the site for a web browser object
-    CComPtr<IWebBrowser2> spWebBrowser;
-    CComPtr<IHTMLDocument2> spHTMLDocument2;
-    HRESULT hr = spSrvProv->QueryService(
-        SID_SWebBrowserApp,
-        IID_IWebBrowser2,
-        reinterpret_cast<void**>(&spWebBrowser));
-
-    if (FAILED(hr)) {
-#ifdef SITELOCK_USE_IOLEOBJECT
-      return false;
-#else
-      // Local declarations
-      CComPtr<IOleContainer> spContainer;
-      CComPtr<IOleClientSite> spClientSite;
-
-      // Get the client site, container, document, and url.
-      T* pT = static_cast<T*>(this);
-      hr = pT->GetSite(IID_IOleClientSite,
-                       reinterpret_cast<void**>(&spClientSite));
-      if (FAILED(hr)) {
-        return false;
-      }
-      hr = spClientSite->GetContainer(&spContainer);
-      if (FAILED(hr)) {
-        return false;
-      }
-      hr = spContainer.QueryInterface(&spHTMLDocument2);
-      if (FAILED(hr)) {
-        return false;
-      }
-      if (FAILED(spHTMLDocument2->get_URL(bstr_url))) {
-        return false;
-      }
-#endif
-    } else {
-      ASSERT1(spWebBrowser);
-
-      CComPtr<IDispatch> spDocument;
-      hr = spWebBrowser->get_Document(&spDocument);
-      if (FAILED(hr) || !spDocument) {
-        return false;
-      }
-
-      hr = spDocument.QueryInterface(&spHTMLDocument2);
-      if (FAILED(hr) || !spHTMLDocument2) {
-        return false;
-      }
-
-      // Retrieves the URL of the document hosting the control.
-      hr = spHTMLDocument2->get_URL(bstr_url);
-      if (FAILED(hr) || !*bstr_url) {
-        return false;
-      }
-    }
-
-#ifdef DEBUG
-    // A little bit of debug code to dump the urls that we care about.
-    // Find out if this is a top level window or a frame.
-    CComPtr<IHTMLWindow2> spWindow;
-    hr = spHTMLDocument2->get_parentWindow(&spWindow);
-    if (FAILED(hr) || !spWindow) {
-      return false;
-    }
-
-    // Get our window. This is the window.self
-    CComPtr<IHTMLWindow2> spWindowCur;
-    hr = spWindow->get_self(&spWindowCur);
-    if (FAILED(hr) || !spWindowCur) {
-      return false;
-    }
-
-    // Get the top level window.
-    CComPtr<IHTMLWindow2> spWindowTop;
-    hr = spWindow->get_top(&spWindowTop);
-    if (FAILED(hr) || !spWindowTop) {
-      return false;
-    }
-
-    // in a frameless situation, the windows are the same.
-    bool isTopLevel = AreObjectsEqual(spWindowCur, spWindowTop);
-    if (!isTopLevel) {
-      CORE_LOG(L3,
-               (_T("[CSiteLock::GetOurUrl][We are hosted in a frame][url=%s]"),
-               (*bstr_url)));
-    }
-
-    // Retrieves the URL of the resource that Microsoft Internet Explorer
-    // is currently displaying.
-    CComBSTR browserUrl;
-    VERIFY1(SUCCEEDED(spWebBrowser->get_LocationURL(&browserUrl)));
-
-    // Retrieves the URL of the current window and document hosting the control.
-    CComPtr<IHTMLLocation> spLocation;
-    VERIFY1(SUCCEEDED(spWindowCur->get_location(&spLocation)));
-
-    CComBSTR curUrl;
-    VERIFY1(SUCCEEDED(spLocation->get_href(&curUrl)));
-
-    // Retrieves the URL of the top window.
-    spLocation = 0;
-    VERIFY1(SUCCEEDED(spWindowTop->get_location(&spLocation)));
-
-    // In a cross-scripting situation we won't be able to get the href property
-    CComBSTR topUrl;
-    if (FAILED(spLocation->get_href(&topUrl))) {
-      topUrl = _T("null");
-    }
-
-    ASSERT1(*bstr_url && curUrl && topUrl && browserUrl);
-    CORE_LOG(L3,
-             (_T("[CSiteLock::GetOurUrl][doc=%s][self=%s][top=%s][browser=%s]"),
-             *bstr_url,
-             curUrl,
-             topUrl,
-             browserUrl));
-
-#endif
-
-    ASSERT(*bstr_url, (_T("post-condition")));
-    return true;
-  }
-
- private:
-
-  bool InApprovedDomain(CComBSTR bstrUrl,
-    const IObjectSafetySiteLock::SiteList *rgslTrustedSites,
-    int cTrustedSites) {
-
-    if (!bstrUrl) {
-      return false;
-    }
-
-    DWORD dwZone = URLZONE_UNTRUSTED;
-    if (!GetUrlZone(bstrUrl, &dwZone)) {
-      CORE_LOG(LEVEL_ERROR, (_T("[CSiteLock::InApprovedDomain]")
-                             _T("[unsafe: failed to get url zone][url=%s]"),
-                             bstrUrl));
-      return false;
-    }
-
-    // Check if the activation URL is in an allowed domain / zone
-    if (!FApprovedDomain(bstrUrl, dwZone, rgslTrustedSites, cTrustedSites)) {
-      ASSERT(FALSE, (_T("[CSiteLock::InApprovedDomain][unsafe: not in approved")
-                     _T(" domain][url=%s][zone=%s]")
-                     _T("- Check your ciconfig.ini to make sure you have the ")
-                     _T("right server in there!"),
-        bstrUrl, UrlZoneToString(dwZone)));
-      CORE_LOG(LEVEL_ERROR, (_T("[CSiteLock::InApprovedDomain][unsafe: not in")
-                             _T(" approved domain][url=%s][zone=%s]"),
-        bstrUrl, UrlZoneToString(dwZone)));
-      return false;
-    }
-
-    CORE_LOG(LEVEL_INF3,
-             (_T("[CSiteLock::InApprovedDomain][safe][%s][%s]"),
-             bstrUrl,
-             UrlZoneToString(dwZone)));
-    return true;
-  }
-
-  bool GetUrlZone(CComBSTR bstrURL, DWORD* zone_out) {
-    if (!bstrURL || !zone_out) {
-      return false;
-    }
-    CComPtr<IServiceProvider> spSrvProv;
-    CComPtr<IInternetSecurityManager> spInetSecMgr;
-    if (!GetServiceProvider(&spSrvProv) ||
-        FAILED(spSrvProv->QueryService(
-            SID_SInternetSecurityManager,
-            __uuidof(IInternetSecurityManager),
-            reinterpret_cast<void **>(&spInetSecMgr)))) {
-      RET_FALSE_IF_FAILED(::CoInternetCreateSecurityManager(NULL,
-                                                            &spInetSecMgr,
-                                                            NULL));
-    }
-    ASSERT1(spInetSecMgr);
-    RET_FALSE_IF_FAILED(spInetSecMgr->MapUrlToZone(bstrURL, zone_out, 0));
-    return true;
-  }
-
-  bool GetServiceProvider(IServiceProvider** ppSrvProv) {
-    if (NULL == ppSrvProv) {
-      return false;
-    }
-    // Get the current pointer as an instance of the template class
-    T* pT = static_cast<T*>(this);
-
-#ifdef SITELOCK_USE_IOLEOBJECT
-    CComPtr<IOleClientSite> spClientSite;
-    RET_FALSE_IF_FAILED(pT->GetClientSite(
-        reinterpret_cast<IOleClientSite**>(ppSrvProv)));
-    RET_IF_FALSE(spClientSite, false);
-    RET_FALSE_IF_FAILED(spClientSite->QueryInterface(
-        IID_IServiceProvider,
-        reinterpret_cast<void **>(ppSrvProv)));
-#else  // USE_IOBJECTWITHSITE
-    if FAILED(pT->GetSite(IID_IServiceProvider,
-                          reinterpret_cast<void**>(ppSrvProv))) {
-      return false;
-    }
-#endif
-
-    ASSERT(ppSrvProv, (_T("post-condition")));  // Post-condition.
-    return true;
-  }
-
-  // Checks if an activation URL is in an allowed domain / zone
-  bool FApprovedDomain(const OLECHAR* wzUrl,
-                       DWORD dwZone,
-                       const IObjectSafetySiteLock::SiteList* rgslTrustedSites,
-                       int cTrustedSites) {
-    // Declarations
-    HRESULT hr = S_OK;
-    OLECHAR wzDomain[INTERNET_MAX_HOST_NAME_LENGTH + 1] = {0};
-    OLECHAR wzScheme[INTERNET_MAX_SCHEME_LENGTH + 1] = {0};
-
-    // Retrieve the normalized domain and scheme
-    hr = GetDomainAndScheme(wzUrl,
-                            wzScheme,
-                            cElements(wzScheme),
-                            wzDomain,
-                            cElements(wzDomain));
-    if (FAILED(hr)) {
-      return false;
-    }
-
-    // Try to match the activation URL with each entry in order
-    DWORD cbScheme = (::lstrlenW(wzScheme) + 1) * sizeof(OLECHAR);
-    for (int i = 0; i < cTrustedSites; i++) {
-      // Try to match by scheme
-      DWORD cbSiteScheme = (::lstrlenW(rgslTrustedSites[i].szScheme) + 1) *
-                           sizeof(OLECHAR);
-      if (cbScheme != cbSiteScheme) {
-        continue;
-      }
-      if (0 != ::memcmp(wzScheme, rgslTrustedSites[i].szScheme, cbScheme)) {
-        continue;
-      }
-
-      // Try to match by zone
-      if (rgslTrustedSites[i].szDomain == SITELOCK_INTRANET_ZONE) {
-        if ((dwZone == URLZONE_INTRANET) || (dwZone == URLZONE_TRUSTED)) {
-          return (rgslTrustedSites[i].iAllowType ==
-                  IObjectSafetySiteLock::SiteList::Allow);
-        }
-      } else if (rgslTrustedSites[i].szDomain == SITELOCK_MYCOMPUTER_ZONE) {
-        if (dwZone == URLZONE_LOCAL_MACHINE) {
-          return (rgslTrustedSites[i].iAllowType ==
-                  IObjectSafetySiteLock::SiteList::Allow);
-        }
-      } else if (rgslTrustedSites[i].szDomain == SITELOCK_TRUSTED_ZONE) {
-        if (dwZone == URLZONE_TRUSTED) {
-          return (rgslTrustedSites[i].iAllowType ==
-                  IObjectSafetySiteLock::SiteList::Allow);
-        }
-        // Try to match by domain name
-      } else if (MatchDomains(rgslTrustedSites[i].szDomain, wzDomain)) {
-        return (rgslTrustedSites[i].iAllowType ==
-                IObjectSafetySiteLock::SiteList::Allow);
-      }
-    }
-    return false;
-  };
-
-  // Normalizes an international domain name
-  HRESULT NormalizeDomain(OLECHAR * wzDomain, int cchDomain) {
-    // Data validation
-    if (!wzDomain) {
-      return E_POINTER;
-    }
-
-    // If the domain is only 7-bit ASCII, normalization is not required
-    bool fFoundUnicode = false;
-    for (const OLECHAR * wz = wzDomain; *wz != 0; wz++) {
-      if (0x80 <= *wz) {
-        fFoundUnicode = true;
-        break;
-      }
-    }
-    if (!fFoundUnicode) {
-      return S_OK;
-    }
-
-    // Construct a fully qualified path to the Windows system directory
-    static const WCHAR wzNormaliz[] = L"normaliz.dll";
-    static const int cchNormaliz = cElements(wzNormaliz);
-    WCHAR wzDllPath[MAX_PATH + 1] = {0};
-    if (!::GetSystemDirectoryW(wzDllPath,
-                               cElements(wzDllPath) - cchNormaliz - 1)) {
-      return E_FAIL;
-    }
-    int cchDllPath = ::lstrlenW(wzDllPath);
-    if (!cchDllPath) {
-      return E_FAIL;
-    }
-    if (wzDllPath[cchDllPath-1] != L'\\') {
-      wzDllPath[cchDllPath++] = L'\\';
-    }
-    ::CopyMemory(wzDllPath + cchDllPath,
-                 wzNormaliz,
-                 cchNormaliz * sizeof(WCHAR));
-
-    // Load the DLL used for domain normalization
-    HMODULE hNormaliz = ::LoadLibraryExW(wzDllPath,
-                                         NULL,
-                                         LOAD_WITH_ALTERED_SEARCH_PATH);
-    if (!hNormaliz) {
-      return E_FAIL;
-    }
-
-    HRESULT hr = E_FAIL;
-
-    // Locate the entry point used for domain normalization
-    PFN_IdnToAscii pfnIdnToAscii =
-        (PFN_IdnToAscii)::GetProcAddress(hNormaliz, "IdnToAscii");
-    if (!pfnIdnToAscii) {
-      goto cleanup;
-    }
-
-    // Normalize the domain name
-    WCHAR wzEncoded[INTERNET_MAX_HOST_NAME_LENGTH + 1];
-    int cchEncode = pfnIdnToAscii(IDN_USE_STD3_ASCII_RULES,
-                                  wzDomain,
-                                  ::lstrlenW(wzDomain),
-                                  wzEncoded,
-                                  cElements(wzEncoded));
-    if (0 == cchEncode) {
-      hr = HRESULT_FROM_WIN32(::GetLastError());
-      goto cleanup;
-    }
-
-    // Copy results to the input buffer
-    if (cchEncode >= cchDomain) {
-      hr = E_OUTOFMEMORY;
-      goto cleanup;
-    }
-
-    ::CopyMemory(wzDomain, wzEncoded, cchEncode * sizeof(WCHAR));
-    hr = S_OK;
-
- cleanup:
-    if (hNormaliz) {
-      ::CloseHandle(hNormaliz);
-    }
-
-    return hr;
-  }
-
-  // Extracts a normalized domain and scheme from an activation URL
-  HRESULT GetDomainAndScheme(const OLECHAR* wzUrl,
-                             OLECHAR* wzScheme,
-                             DWORD cchScheme,
-                             OLECHAR* wzDomain,
-                             DWORD cchDomain) {
-    // Data validation
-    if (!wzDomain || !wzScheme) {
-      return E_POINTER;
-    }
-
-    // Extract the scheme
-    HRESULT hr = ::UrlGetPartW(wzUrl, wzScheme, &cchScheme, URL_PART_SCHEME, 0);
-    if (FAILED(hr)) {
-      return E_FAIL;
-    }
-
-    // Extract the host name
-    DWORD cchDomain2 = cchDomain;
-    hr = ::UrlGetPartW(wzUrl, wzDomain, &cchDomain2, URL_PART_HOSTNAME, 0);
-    if (FAILED(hr)) {
-      *wzDomain = 0;
-    }
-
-    // Exclude any URL specifying a user name or password
-    if ((0 == ::_wcsicmp(wzScheme, L"http")) ||
-        (0 == ::_wcsicmp(wzScheme, L"https"))) {
-      DWORD cch = 1;
-      WCHAR wzTemp[1] = {0};
-      ::UrlGetPartW(wzUrl, wzTemp, &cch, URL_PART_USERNAME, 0);
-      if (1 < cch) {
-        return E_FAIL;
-      }
-      ::UrlGetPartW(wzUrl, wzTemp, &cch, URL_PART_PASSWORD, 0);
-      if (1 < cch) {
-        return E_FAIL;
-      }
-    }
-
-    // Normalize the domain name
-    return NormalizeDomain(wzDomain, cchDomain);
-  }
-
-  // Attempts to match an activation URL with a domain name
-  bool MatchDomains(const OLECHAR* wzTrustedDomain,
-                    const OLECHAR* wzOurDomain) {
-    // Data validation
-    if (!wzTrustedDomain) {
-      return (0 == *wzOurDomain);  // match only if empty
-    }
-
-    // Declarations
-    int cchTrusted = ::lstrlenW(wzTrustedDomain);
-    int cchOur = ::lstrlenW(wzOurDomain);
-    bool fForcePrefix = false;
-    bool fDenyPrefix = false;
-
-    // Check if all activation URLs should be matched
-    if (0 == ::wcscmp(wzTrustedDomain, L"*")) {
-      return true;
-    }
-
-    // Check if the entry is like *. and setup the comparison range
-    if ((2 < cchTrusted) &&
-        (L'*' == wzTrustedDomain[0]) &&
-        (L'.' == wzTrustedDomain[1])) {
-      fForcePrefix = true;
-      wzTrustedDomain += 2;
-      cchTrusted -= 2;
-
-      // Check if the entry is like = and setup the comparison range
-    } else if ((1 < cchTrusted) && (L'=' == wzTrustedDomain[0])) {
-      fDenyPrefix = true;
-      wzTrustedDomain++;
-      cchTrusted--;
-    };
-
-    // Check if there is a count mismatch
-    if (cchTrusted > cchOur) {
-      return false;
-    }
-
-    // Compare URLs on the desired character range
-    if (0 != ::memcmp(wzOurDomain + cchOur - cchTrusted,
-                      wzTrustedDomain,
-                      cchTrusted * sizeof(OLECHAR))) {
-      return false;
-    }
-
-    // Compare URLs without allowing child domains
-    if (!fForcePrefix && (cchTrusted == cchOur)) {
-      return true;
-    }
-
-    // Compare URLs requiring child domains
-    if (!fDenyPrefix && (wzOurDomain[cchOur - cchTrusted - 1] == L'.')) {
-      return true;
-    }
-
-    return false;
-  }
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// IObjectSafetySiteLockImpl - "Safe for scripting" template
-template <typename T, DWORD dwSupportedSafety>
-class ATL_NO_VTABLE IObjectSafetySiteLockImpl
-    : public IObjectSafetySiteLock,
-      public CSiteLock<T> {
- public:
-  // Constructor
-  IObjectSafetySiteLockImpl(): m_dwCurrentSafety(0) {}
-
-  // Returns safety options
-  STDMETHOD(GetInterfaceSafetyOptions)(REFIID riid,
-                                       DWORD * pdwSupportedOptions,
-                                       DWORD * pdwEnabledOptions) {
-    // Data validation
-    if (!pdwSupportedOptions || !pdwEnabledOptions) {
-      return E_POINTER;
-    }
-
-    // Declarations
-    HRESULT hr = S_OK;
-    IUnknown* pUnk = NULL;
-
-    // Get the current pointer as an instance of the template class
-    T * pT = static_cast<T*>(this);
-
-    // Check if the requested COM interface is supported
-    hr = pT->GetUnknown()->QueryInterface(riid,
-                                          reinterpret_cast<void**>(&pUnk));
-    if (FAILED(hr)) {
-      *pdwSupportedOptions = 0;
-      *pdwEnabledOptions   = 0;
-      return hr;
-    }
-
-    // Release the interface
-    pUnk->Release();
-
-    // Check expiry and if the activation URL is allowed
-    if (!ControlExpired() && InApprovedDomain()) {
-      *pdwSupportedOptions = dwSupportedSafety;
-      *pdwEnabledOptions   = m_dwCurrentSafety;
-    } else {
-      *pdwSupportedOptions = dwSupportedSafety;
-      *pdwEnabledOptions   = 0;
-    }
-    return S_OK;
-  }
-
-  // Sets safety options
-  STDMETHOD(SetInterfaceSafetyOptions)(REFIID riid,
-                                       DWORD dwOptionSetMask,
-                                       DWORD dwEnabledOptions) {
-    // Declarations
-    HRESULT hr = S_OK;
-    IUnknown* pUnk = NULL;
-
-    // Get the current pointer as an instance of the template class
-    T * pT = static_cast<T*>(this);
-
-    // Check if we support the interface and return E_NOINTERFACE if we don't
-    // Check if the requested COM interface is supported
-    hr = pT->GetUnknown()->QueryInterface(riid,
-                                          reinterpret_cast<void**>(&pUnk));
-    if (FAILED(hr)) {
-      return hr;
-    }
-
-    // Release the interface
-    pUnk->Release();
-
-    // Reject unsupported requests
-    if (dwOptionSetMask & ~dwSupportedSafety) {
-      return E_FAIL;
-    }
-
-    // Calculate safety options
-    DWORD dwNewSafety = (m_dwCurrentSafety & ~dwOptionSetMask) |
-                        (dwOptionSetMask & dwEnabledOptions);
-    if (m_dwCurrentSafety != dwNewSafety) {
-      // Check expiry and if the activation URL is allowed
-      if (ControlExpired() || !InApprovedDomain()) {
-        return E_FAIL;
-      }
-
-      // Set safety options
-      m_dwCurrentSafety = dwNewSafety;
-    }
-    return S_OK;
-  }
-
-  // Returns capabilities (this can be used by testing tools to query for
-  // custom capabilities or version information)
-  STDMETHOD(GetCapabilities)(DWORD * pdwCapability) {
-    // Data validation
-    if (!pdwCapability) {
-      return E_POINTER;
-    }
-
-    // Return the version if 0 is passed in
-    if (0 == *pdwCapability) {
-      *pdwCapability = SITELOCK_VERSION;
-      return S_OK;
-    }
-
-    // Return the options if 1 is passed in
-    if (1 == *pdwCapability) {
-      *pdwCapability =
-#ifdef SITELOCK_USE_IOLEOBJECT
-        Capability::UsesIOleObject |
-#endif
-#ifndef SITELOCK_NO_EXPIRY
-        Capability::HasExpiry |
-#endif
-        0;
-      return S_OK;
-    }
-
-    // Return not implemented otherwise
-    *pdwCapability = 0;
-    return E_NOTIMPL;
-  }
-
-  // Returns site lock entries controlling activation
-  STDMETHOD(GetApprovedSites)(const SiteList ** pSiteList, DWORD * pcEntries) {
-    // Data validation
-    if (!pSiteList || !pcEntries) {
-      return E_POINTER;
-    }
-
-    // Return specified site lock entries
-#ifdef SITELOCK_USE_MAP
-    // Use the site lock map
-    *pSiteList = T::GetSiteLockMapAndCount(*pcEntries);
-#else
-    // Use the static member
-    *pSiteList = T::rgslTrustedSites;
-    *pcEntries = cElements(T::rgslTrustedSites);
-#endif
-    return S_OK;
-  }
-
-  STDMETHOD(GetExpiryDate)(DWORD * pdwLifespan, FILETIME * pExpiryDate) {
-    if (!pdwLifespan || !pExpiryDate) {
-      return E_POINTER;
-    }
-
-#ifdef SITELOCK_NO_EXPIRY
-    *pdwLifespan = 0;
-    ::ZeroMemory(reinterpret_cast<void*>(pExpiryDate), sizeof(FILETIME));
-    return E_NOTIMPL;
-#else
-    *pdwLifespan = T::dwControlLifespan;
-
-    // Calculate expiry date from life span
-    time_t ttExpire = ImageNtHeaders(&__ImageBase)->FileHeader.TimeDateStamp;
-    ttExpire += T::dwControlLifespan*86400;  // seconds per day
-    _UNIXTimeToFILETIME(ttExpire, pExpiryDate);
-
-    return S_OK;
-#endif
-  }
-
- private:
-  // Current safety
-  DWORD m_dwCurrentSafety;
-};
-
-#endif  // OMAHA_PLUGINS_SITELOCK_H__
diff --git a/plugins/update/activex/oneclick_control.cc b/plugins/update/activex/oneclick_control.cc
new file mode 100644
index 0000000..6729e86
--- /dev/null
+++ b/plugins/update/activex/oneclick_control.cc
@@ -0,0 +1,335 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Implementation of the OneClick Plugin.
+
+#include "omaha/plugins/update/activex/oneclick_control.h"
+
+#include <dispex.h>
+#include <atlsafe.h>
+#include <atlbase.h>
+#include <atlcom.h>
+
+#include "omaha/base/error.h"
+#include "omaha/base/exception_barrier.h"
+#include "omaha/base/file.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/scoped_ptr_address.h"
+#include "omaha/base/string.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/command_line.h"
+#include "omaha/common/command_line_builder.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/webplugin_utils.h"
+#include "omaha/goopdate/app_command.h"
+#include "omaha/goopdate/app_manager.h"
+#include "goopdate/omaha3_idl.h"
+
+namespace omaha {
+
+OneClickControl::OneClickControl() {
+  CORE_LOG(L2, (_T("[OneClickControl::OneClickControl]")));
+}
+
+OneClickControl::~OneClickControl() {
+  CORE_LOG(L2, (_T("[OneClickControl::~OneClickControl]")));
+}
+
+STDMETHODIMP OneClickControl::Install(BSTR cmd_line_args,
+                                      VARIANT* success_callback,
+                                      VARIANT* failure_callback) {
+  ASSERT1(cmd_line_args && cmd_line_args[0]);
+  ASSERT1(VariantIsValidCallback(success_callback));
+  ASSERT1(VariantIsValidCallback(failure_callback));
+
+  if (!site_lock_.InApprovedDomain(this)) {
+    return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED;
+  }
+
+  if (!cmd_line_args ||
+      !cmd_line_args[0] ||
+      !VariantIsValidCallback(success_callback) ||
+      !VariantIsValidCallback(failure_callback) ) {
+    return E_INVALIDARG;
+  }
+
+  CORE_LOG(L2, (_T("[OneClickControl::Install][cmd_line \"%s\"]"),
+                CW2CT(cmd_line_args)));
+
+  HRESULT hr = DoInstall(CString(cmd_line_args));
+  if (SUCCEEDED(hr)) {
+    InvokeJavascriptCallback(success_callback, NULL);
+  } else {
+    CORE_LOG(LE, (_T("[DoOneClickInstallInternal failed][0x%08x]"), hr));
+    InvokeJavascriptCallback(failure_callback, &hr);
+  }
+
+  // Return success in all cases. The failure callback has already been called
+  // above, and we don't want to cause a failure path to be called again when
+  // the JavaScript catches the exception.
+
+  return S_OK;
+}
+
+STDMETHODIMP OneClickControl::Install2(BSTR extra_args) {
+  ASSERT1(extra_args && extra_args[0]);
+
+  if (!site_lock_.InApprovedDomain(this)) {
+    return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED;
+  }
+
+  if (!extra_args || !extra_args[0]) {
+    return E_INVALIDARG;
+  }
+
+  CORE_LOG(L2, (_T("[OneClickControl::Install2][extra_args \"%s\"]"),
+                CW2CT(extra_args)));
+
+  CommandLineBuilder builder(COMMANDLINE_MODE_INSTALL);
+  builder.set_extra_args(CString(extra_args));
+  return DoInstall(builder.GetCommandLineArgs());
+}
+
+STDMETHODIMP OneClickControl::GetInstalledVersion(BSTR guid_string,
+                                                  VARIANT_BOOL is_machine,
+                                                  BSTR* version_string) {
+  if (!site_lock_.InApprovedDomain(this)) {
+    return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED;
+  }
+
+  if (!guid_string || !version_string) {
+    return E_POINTER;
+  }
+  *version_string = NULL;
+
+  CORE_LOG(L2, (_T("[OneClickControl::GetInstalledVersion][%s][%d]"),
+                guid_string, is_machine));
+
+  CString version;
+  HRESULT hr = DoGetInstalledVersion(guid_string,
+                                     is_machine == VARIANT_TRUE,
+                                     &version);
+  if (SUCCEEDED(hr)) {
+    *version_string = version.AllocSysString();
+  }
+
+  return S_OK;
+}
+
+STDMETHODIMP OneClickControl::GetOneClickVersion(long* version) {  // NOLINT
+  ASSERT1(version);
+
+  if (!site_lock_.InApprovedDomain(this)) {
+    return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED;
+  }
+
+  CORE_LOG(L2, (_T("[OneClickControl::GetOneClickVersion]")));
+
+  if (!version) {
+    return E_POINTER;
+  }
+
+  *version = atoi(ONECLICK_PLUGIN_VERSION_ANSI);  // NOLINT
+  return S_OK;
+}
+
+STDMETHODIMP OneClickControl::LaunchAppCommand(BSTR app_guid,
+                                               VARIANT_BOOL is_machine,
+                                               BSTR cmd_id) {
+  if (!site_lock_.InApprovedDomain(this)) {
+    return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED;
+  }
+
+  CORE_LOG(L2, (_T("[OneClickControl::LaunchAppCommand]")));
+
+  ExceptionBarrier barrier;
+
+  CComPtr<IOneClickProcessLauncher> process_launcher;
+  HRESULT hr = E_UNEXPECTED;
+
+  hr = process_launcher.CoCreateInstance(
+      is_machine ?
+          CLSID_OneClickMachineProcessLauncherClass :
+          CLSID_OneClickUserProcessLauncherClass);
+
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[OneClickControl::LaunchAppCommand]")
+                  _T("[Failed to CoCreate OneClickMachine/User ")
+                  _T("ProcessLauncher implementation: 0x%x"),
+                  hr));
+    return hr;
+  }
+
+  return process_launcher->LaunchAppCommand(app_guid, cmd_id);
+}
+
+HRESULT OneClickControl::DoGetInstalledVersion(const TCHAR* guid_string,
+                                               bool is_machine,
+                                               CString* version_string) {
+  ASSERT1(guid_string);
+  ASSERT1(version_string);
+
+  GUID app_guid = GUID_NULL;
+  HRESULT hr = StringToGuidSafe(guid_string, &app_guid);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return AppManager::ReadAppVersionNoLock(is_machine, app_guid, version_string);
+}
+
+HRESULT OneClickControl::DoInstall(const TCHAR* cmd_line_args) {
+  ASSERT1(cmd_line_args);
+
+  CORE_LOG(L2, (_T("[OneClickControl::DoInstall][%s]"), cmd_line_args));
+
+#ifdef _DEBUG
+  // If the args are exactly __DIRECTNOTIFY__ then just fire the event
+  // out of this thread.  This allows for easy testing of the
+  // browser interface without requiring launch of
+  // google_update.exe.
+  if (0 == _wcsicmp(L"__DIRECTNOTIFY__", cmd_line_args)) {
+    return S_OK;
+  }
+#endif
+
+  CString browser_url;
+  HRESULT hr = site_lock_.GetCurrentBrowserUrl(this, &browser_url);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CString url_domain;
+  hr = SiteLock::GetUrlDomain(browser_url, &url_domain);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CString url_domain_encoded;
+  CString cmd_line_args_encoded;
+  hr = StringEscape(url_domain, false, &url_domain_encoded);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = StringEscape(cmd_line_args, false, &cmd_line_args_encoded);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CommandLineBuilder builder(COMMANDLINE_MODE_WEBPLUGIN);
+  builder.set_webplugin_url_domain(url_domain_encoded);
+  builder.set_webplugin_args(cmd_line_args_encoded);
+  builder.set_install_source(kCmdLineInstallSource_OneClick);
+  CString final_cmd_line_args = builder.GetCommandLineArgs();
+
+  CORE_LOG(L2, (_T("[OneClickControl::DoInstall]")
+                _T("[Final command line params: %s]"),
+                final_cmd_line_args));
+
+  scoped_process process_goopdate;
+
+  hr = goopdate_utils::StartGoogleUpdateWithArgs(
+          goopdate_utils::IsRunningFromOfficialGoopdateDir(true),
+          final_cmd_line_args,
+          address(process_goopdate));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[OneClickControl::DoInstall]")
+                  _T("[Failed StartGoogleUpdateWithArgs][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+bool OneClickControl::VariantIsValidCallback(const VARIANT* callback) {
+  return callback &&
+         (callback->vt == VT_NULL ||
+          callback->vt == VT_EMPTY ||
+          (callback->vt == VT_DISPATCH && callback->pdispVal));
+}
+
+HRESULT OneClickControl::InvokeJavascriptCallback(VARIANT* callback,
+                                                  const HRESULT* opt_param) {
+  if (!callback || callback->vt == VT_NULL || callback->vt == VT_EMPTY) {
+    return S_FALSE;
+  }
+
+  if (callback->vt != VT_DISPATCH || !callback->pdispVal) {
+    return E_FAIL;
+  }
+
+  const DISPID kDispId0 = 0;
+  DISPPARAMS dispparams = {0};
+
+  CComQIPtr<IDispatchEx> dispatchex = callback->pdispVal;
+  if (dispatchex) {
+    DISPID disp_this = DISPID_THIS;
+    VARIANT var[2];
+    var[0].vt = VT_DISPATCH;
+    var[0].pdispVal = dispatchex;
+    if (opt_param) {
+      var[1].vt = VT_I4;
+      var[1].intVal = *opt_param;
+    }
+
+    dispparams.rgvarg = var;
+    dispparams.rgdispidNamedArgs = &disp_this;
+    dispparams.cNamedArgs = 1;
+    dispparams.cArgs = opt_param ? 2 : 1;
+
+    return dispatchex->InvokeEx(kDispId0, LOCALE_USER_DEFAULT,
+                                DISPATCH_METHOD, &dispparams,
+                                NULL, NULL, NULL);
+  } else {
+    // Fallback on IDispatch if needed.  (This route will be used for NPAPI
+    // functions wrapped via NPFunctionHost.)
+
+    UINT arg_err = 0;
+    VARIANT var[1];
+
+    if (opt_param) {
+      var[0].vt = VT_I4;
+      var[0].intVal = *opt_param;
+      dispparams.rgvarg = var;
+      dispparams.cArgs = 1;
+    }
+
+    return callback->pdispVal->Invoke(kDispId0,
+                                      IID_NULL,
+                                      LOCALE_SYSTEM_DEFAULT,
+                                      DISPATCH_METHOD,
+                                      &dispparams,
+                                      NULL,
+                                      NULL,
+                                      &arg_err);
+  }
+}
+
+// This works during Setup because IsRunningFromOfficialGoopdateDir checks the
+// location of the DLL not the exe, which is running from a temp location.
+CString OneClickControl::GetGoopdateShellPathForRegMap() {
+  ASSERT1(goopdate_utils::IsRunningFromOfficialGoopdateDir(false) ||
+          goopdate_utils::IsRunningFromOfficialGoopdateDir(true));
+  return goopdate_utils::BuildGoogleUpdateExeDir(
+      goopdate_utils::IsRunningFromOfficialGoopdateDir(true));
+}
+
+}  // namespace omaha
+
+// 4505: unreferenced local function has been removed
+#pragma warning(disable : 4505)
+
diff --git a/plugins/update/activex/oneclick_control.h b/plugins/update/activex/oneclick_control.h
new file mode 100644
index 0000000..dc7fbf7
--- /dev/null
+++ b/plugins/update/activex/oneclick_control.h
@@ -0,0 +1,148 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// One-click support for Omaha returning users.
+
+#ifndef OMAHA_PLUGINS_UPDATE_ACTIVEX_ONECLICK_CONTROL_H_
+#define OMAHA_PLUGINS_UPDATE_ACTIVEX_ONECLICK_CONTROL_H_
+
+// TODO(omaha): We may want to move sitelock.h to be the "standard" sitelock.h
+// file from Microsoft (and move that file to omaha/external) and then have our
+// modifications to sitelock be in a derived class within the plugins
+// directory.
+
+#include <objsafe.h>
+#include <shellapi.h>
+#include <winhttp.h>
+#include <atlbase.h>
+#include <atlcom.h>
+#include <atlctl.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/ATLRegMapEx.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/debug.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/plugins/update/config.h"
+#include "omaha/plugins/update/resource.h"
+#include "omaha/plugins/update/site_lock.h"
+#include "plugins/update/activex/update_control_idl.h"
+
+namespace omaha {
+
+class OneClickControl;
+
+typedef IObjectSafetyImpl<OneClickControl, INTERFACESAFE_FOR_UNTRUSTED_CALLER>
+            OneClickControlSafety;
+
+// Using 0xffff for the major/minor versions in the IDispatchImpl template will
+// make ATL load the typelib directly from the DLL instead of looking up typelib
+// registration in registry. The big benefit is that we do not need to register
+// the typelib. Also, this is needed for Vista SP1 with UAC off, in which
+// oleaut32 does not read typelib information from HKCU, because of a bug.
+class ATL_NO_VTABLE OneClickControl
+    : public CComObjectRootEx<CComObjectThreadModel>,
+      public CComCoClass<OneClickControl,
+                         &__uuidof(GoogleUpdateOneClickControlCoClass)>,
+      public IDispatchImpl<IGoogleUpdateOneClick,
+                           &__uuidof(IGoogleUpdateOneClick),
+                           &LIBID_GoogleUpdateControlLib, 0xffff, 0xffff>,
+      public OneClickControlSafety,
+      public IObjectWithSiteImpl<OneClickControl> {
+ public:
+  OneClickControl();
+  virtual ~OneClickControl();
+
+  DECLARE_REGISTRY_RESOURCEID_EX(IDR_ONECLICK_RGS)
+
+#pragma warning(push)
+// C4640: construction of local static object is not thread-safe
+#pragma warning(disable : 4640)
+  BEGIN_REGISTRY_MAP()
+    REGMAP_ENTRY(_T("CLSID"),             GetObjectCLSID())
+    REGMAP_ENTRY(_T("PROGID"),            kOneclickControlProgId)
+    REGMAP_ENTRY(_T("HKROOT"),            goopdate_utils::GetHKRoot())
+    REGMAP_ENTRY(_T("SHELLNAME"),         kOmahaShellFileName)
+    // Not fatal if "SHELLPATH" is empty because the side-effect would be that
+    // on Vista, the user will get prompted on invoking one-click.
+    REGMAP_ENTRY(_T("SHELLPATH"),         GetGoopdateShellPathForRegMap())
+
+    // The following entries are actually for the NPAPI plugin
+    REGMAP_ENTRY(_T("PLUGINDESCRIPTION"), kAppName)
+    REGMAP_ENTRY(_T("PLUGINDOMAIN"),      kGoopdateServer)
+    REGMAP_ENTRY(_T("PLUGINVENDOR"),      kFullCompanyName)
+    REGMAP_ENTRY(_T("PLUGINVERSION"),     kOneclickPluginVersion)
+    REGMAP_ENTRY(_T("PLUGINPRODUCT"),     kAppName)
+    REGMAP_ENTRY(_T("PLUGINMIMETYPE"),    ONECLICK_MIME_TYPE)
+  END_REGISTRY_MAP()
+#pragma warning(pop)
+
+  BEGIN_COM_MAP(OneClickControl)
+    COM_INTERFACE_ENTRY(IDispatch)
+    COM_INTERFACE_ENTRY(IObjectWithSite)
+    COM_INTERFACE_ENTRY(IObjectSafety)
+  END_COM_MAP()
+
+  DECLARE_NOT_AGGREGATABLE(OneClickControl)
+  DECLARE_PROTECT_FINAL_CONSTRUCT();
+
+  // Installs the application that the passed-in manifest corresponds to.
+  STDMETHOD(Install)(BSTR cmd_line_args,
+                     VARIANT* success_callback,
+                     VARIANT* failure_callback);
+
+  STDMETHOD(Install2)(BSTR extra_args);
+
+  // Gets the version of the passed in application guid. If the application is
+  // not installed, returns an empty string.
+  STDMETHOD(GetInstalledVersion)(BSTR guid_string,
+                                 VARIANT_BOOL is_machine,
+                                 BSTR* version_string);
+
+  // Gets the version of the plugin. The value is ONECLICK_PLUGIN_VERSION_ANSI.
+  // TODO(omaha3): If possible without causing incompatibilities, change version
+  // to a preferred type here and in OneClickWorker.
+  STDMETHOD(GetOneClickVersion)(long* version);  // NOLINT
+
+  // Launches a command defined by an installed application. Fails if the
+  // command is not successfully started, succeeds otherwise. Returns without
+  // waiting for the command to complete.
+  STDMETHOD(LaunchAppCommand)(BSTR app_guid,
+                              VARIANT_BOOL is_machine,
+                              BSTR cmd_id);
+
+ private:
+  HRESULT DoGetInstalledVersion(const TCHAR* guid_string,
+                                bool is_machine,
+                                CString* version_string);
+
+  HRESULT DoInstall(const TCHAR* cmd_line_args);
+
+  static bool VariantIsValidCallback(const VARIANT* callback);
+  static HRESULT InvokeJavascriptCallback(VARIANT* callback,
+                                          const HRESULT* opt_param);
+
+  SiteLock site_lock_;
+
+  // If Admin, returns the path for Machine Goopdate. Else returns path for User
+  // Goopdate.
+  static CString GetGoopdateShellPathForRegMap();
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_PLUGINS_UPDATE_ACTIVEX_ONECLICK_CONTROL_H_
+
diff --git a/plugins/update/activex/update3web_control.cc b/plugins/update/activex/update3web_control.cc
new file mode 100644
index 0000000..6d49ff3
--- /dev/null
+++ b/plugins/update/activex/update3web_control.cc
@@ -0,0 +1,366 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/plugins/update/activex/update3web_control.h"
+#include <objbase.h>
+#include <objidl.h>
+#include "omaha/base/error.h"
+#include "omaha/base/system.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/command_line.h"
+#include "omaha/common/command_line_builder.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/webplugin_utils.h"
+#include "omaha/goopdate/app_manager.h"
+#include "goopdate/omaha3_idl.h"
+
+namespace omaha {
+
+Update3WebControl::Update3WebControl() {
+}
+
+// There is a code generation bug in VC8. If a base class with template
+// arguments and virtual members is not the first base class with virtual
+// methods, the generated code for calling a virtual method on that base class
+// will adjust the this pointer one extra time. This results in all sorts of
+// strange things happening; typically, the program will crash at a later point.
+// To avoid this, all calls to base class methods that have template arguments
+// should have all the template arguments specified, since this seems to prevent
+// the code generation bug from occuring.
+
+STDMETHODIMP Update3WebControl::createOmahaMachineServerAsync(
+    VARIANT_BOOL create_elevated, IDispatch** async_status) {
+  ASSERT1(async_status);
+
+  CString url;
+  HRESULT hr = SiteLock::GetCurrentBrowserUrl(this, &url);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (L"[GetCurrentBrowserUrl failed][0x%08x]", hr));
+    return hr;
+  }
+
+  if (!site_lock_.InApprovedDomain(url)) {
+    return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED;
+  }
+
+  if (!async_status) {
+    return E_POINTER;
+  }
+
+  CComPtr<ICoCreateAsync> cocreate_async;
+  hr = cocreate_async.CoCreateInstance(__uuidof(CoCreateAsyncClass));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (L"[CoCreate CoCreateAsyncClass failed][0x%08x]", hr));
+    return hr;
+  }
+
+  CComPtr<ICoCreateAsyncStatus> status;
+  hr = cocreate_async->createOmahaMachineServerAsync(CComBSTR(url),
+                                                     create_elevated,
+                                                     &status);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (L"[CreateInstanceAsync failed][0x%08x]", hr));
+    return hr;
+  }
+
+  hr = status->QueryInterface(async_status);
+  CORE_LOG(L3, (L"[createOmahaMachineServerAsync][0x%p][0x%08x]", this, hr));
+  return hr;
+}
+
+STDMETHODIMP Update3WebControl::createOmahaUserServer(IDispatch** server) {
+  ASSERT1(server);
+
+  CString url;
+  HRESULT hr = SiteLock::GetCurrentBrowserUrl(this, &url);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (L"[GetCurrentBrowserUrl failed][0x%08x]", hr));
+    return hr;
+  }
+
+  if (!site_lock_.InApprovedDomain(url)) {
+    return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED;
+  }
+
+  if (!server) {
+    return E_POINTER;
+  }
+
+  CComPtr<IGoogleUpdate3WebSecurity> security;
+  hr = security.CoCreateInstance(__uuidof(GoogleUpdate3WebUserClass));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[CoCreate failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  hr = security->setOriginURL(CComBSTR(url));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[setOriginURL failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  hr = security->QueryInterface(server);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[QueryInterface failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  CORE_LOG(L3, (L"[createOmahaUserServer][0x%p][0x%p]", this, *server));
+  return hr;
+}
+
+STDMETHODIMP Update3WebControl::getInstalledVersion(BSTR guid_string,
+                                                    VARIANT_BOOL is_machine,
+                                                    BSTR* version_string) {
+  if (!site_lock_.InApprovedDomain(this)) {
+    return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED;
+  }
+
+  if (!guid_string || !version_string) {
+    return E_POINTER;
+  }
+  *version_string = NULL;
+
+  CORE_LOG(L2, (_T("[Update3WebControl::getInstalledVersion][%s][%d]"),
+                guid_string, is_machine));
+
+  CString version;
+  HRESULT hr = GetVersionUsingCOMServer(guid_string, is_machine == VARIANT_TRUE,
+                                        &version);
+  if (FAILED(hr)) {
+    hr = GetVersionUsingRegistry(guid_string, is_machine == VARIANT_TRUE,
+                                 &version);
+  }
+
+  if (SUCCEEDED(hr)) {
+    *version_string = version.AllocSysString();
+  }
+
+  return S_OK;
+}
+
+HRESULT Update3WebControl::GetVersionUsingCOMServer(const TCHAR* guid_string,
+                                                    bool is_machine,
+                                                    CString* version_string) {
+  CORE_LOG(L2, (_T("[GoopdateCtrl::GetVersionUsingCOMServer][%s][%d]"),
+                guid_string, is_machine));
+  ASSERT1(guid_string);
+  ASSERT1(version_string);
+
+  CComPtr<IGoogleUpdate3Web> update3web;
+  HRESULT hr = update3web.CoCreateInstance(
+      is_machine ? __uuidof(GoogleUpdate3WebMachineClass) :
+                   __uuidof(GoogleUpdate3WebUserClass));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[update3web.CoCreateInstance failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<IDispatch> app_bundle_dispatch;
+  hr = update3web->createAppBundleWeb(&app_bundle_dispatch);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[update3web.createAppBundleWeb failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<IAppBundleWeb> app_bundle_web;
+  hr = app_bundle_dispatch->QueryInterface(&app_bundle_web);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[QueryInterface for IAppBundleWeb failed][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = app_bundle_web->initialize();
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[initialize fail][0x%x]"), hr));
+    return hr;
+  }
+
+  hr = app_bundle_web->createInstalledApp(CComBSTR(guid_string));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[createInstalledApp fail][%s][0x%x]"), guid_string, hr));
+    return hr;
+  }
+
+  CComPtr<IDispatch> app_dispatch;
+  hr = app_bundle_web->get_appWeb(0, &app_dispatch);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[get_appWeb failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<IAppWeb> app_web;
+  hr = app_dispatch->QueryInterface(&app_web);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[QueryInterface for IAppWeb failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<IDispatch> app_version_dispatch;
+  hr = app_web->get_currentVersionWeb(&app_version_dispatch);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[get_currentVersionWeb failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComPtr<IAppVersionWeb> app_version_web;
+  hr = app_version_dispatch->QueryInterface(&app_version_web);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[QueryInterface for IAppVersionWeb failed][0x%x]"), hr));
+    return hr;
+  }
+
+  CComBSTR version;
+  hr = app_version_web->get_version(&version);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[get_version failed][0x%x]"), hr));
+    return hr;
+  }
+
+  *version_string = version;
+  CORE_LOG(L2, (_T("[Update3WebControl::GetVersionUsingCOMServer][%s][%d][%s]"),
+                guid_string, is_machine, *version_string));
+  return S_OK;
+}
+
+HRESULT Update3WebControl::GetVersionUsingRegistry(const TCHAR* guid_string,
+                                                   bool is_machine,
+                                                   CString* version_string) {
+  CORE_LOG(L2, (_T("[GoopdateCtrl::GetVersionUsingRegistry][%s][%d]"),
+                guid_string, is_machine));
+  ASSERT1(guid_string);
+  ASSERT1(version_string);
+
+  GUID app_guid = GUID_NULL;
+  HRESULT hr = StringToGuidSafe(guid_string, &app_guid);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return AppManager::ReadAppVersionNoLock(is_machine, app_guid, version_string);
+}
+
+HRESULT Update3WebControl::crossInstall(BSTR extra_args) {
+  ASSERT1(extra_args);
+
+  if (!site_lock_.InApprovedDomain(this)) {
+    return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED;
+  }
+
+  if (!extra_args || !extra_args[0]) {
+    return E_INVALIDARG;
+  }
+
+  CORE_LOG(L2, (_T("[Update3WebControl::crossInstall][%s]"), extra_args));
+
+  // Build the full command line as it should eventually be run.  (This command
+  // line will be escaped, including the /install, and stowed in a /pi later.)
+
+  CommandLineBuilder inner_builder(COMMANDLINE_MODE_INSTALL);
+  inner_builder.set_extra_args(CString(extra_args));
+  CString inner_cmd_line_args = inner_builder.GetCommandLineArgs();
+
+  HRESULT hr = webplugin_utils::IsLanguageSupported(inner_cmd_line_args);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[IsLanguageSupported failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  CString browser_url;
+  hr = site_lock_.GetCurrentBrowserUrl(this, &browser_url);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CString url_domain;
+  hr = SiteLock::GetUrlDomain(browser_url, &url_domain);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Build the outer command line using /pi.
+
+  CString url_domain_encoded;
+  CString cmd_line_encoded;
+  hr = StringEscape(url_domain, false, &url_domain_encoded);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = StringEscape(inner_cmd_line_args, false, &cmd_line_encoded);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CommandLineBuilder outer_builder(COMMANDLINE_MODE_WEBPLUGIN);
+  outer_builder.set_webplugin_url_domain(url_domain_encoded);
+  outer_builder.set_webplugin_args(cmd_line_encoded);
+  outer_builder.set_install_source(kCmdLineInstallSource_Update3Web);
+  CString final_cmd_line_args = outer_builder.GetCommandLineArgs();
+
+  CORE_LOG(L2, (_T("[Update3WebControl::crossInstall]")
+                _T("[Final command line params: %s]"),
+                final_cmd_line_args));
+
+  // Spawn a gu process.
+
+  scoped_process process_goopdate;
+
+  hr = goopdate_utils::StartGoogleUpdateWithArgs(is_machine(),
+                                                 final_cmd_line_args,
+                                                 address(process_goopdate));
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Update3WebControl::crossInstall]")
+                  _T("[Failed StartGoogleUpdateWithArgs][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+HRESULT Update3WebControl::launchAppCommand(BSTR guid_string,
+                                            VARIANT_BOOL is_machine,
+                                            BSTR cmd_id) {
+  if (!site_lock_.InApprovedDomain(this)) {
+    return GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED;
+  }
+
+  CORE_LOG(L2, (_T("[Update3WebControl::launchAppCommand]")));
+
+  CComPtr<IOneClickProcessLauncher> process_launcher;
+  HRESULT hr = E_UNEXPECTED;
+
+  hr = process_launcher.CoCreateInstance(
+      is_machine ?
+          CLSID_OneClickMachineProcessLauncherClass :
+          CLSID_OneClickUserProcessLauncherClass);
+
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[Update3WebControl::launchAppCommand]")
+                  _T("[Failed to CoCreate OneClickMachine/User ")
+                  _T("ProcessLauncher implementation: 0x%x"),
+                  hr));
+    return hr;
+  }
+
+  return process_launcher->LaunchAppCommand(guid_string, cmd_id);
+}
+
+Update3WebControl::~Update3WebControl() {
+}
+
+}  // namespace omaha
diff --git a/plugins/update/activex/update3web_control.h b/plugins/update/activex/update3web_control.h
new file mode 100644
index 0000000..0300d33
--- /dev/null
+++ b/plugins/update/activex/update3web_control.h
@@ -0,0 +1,133 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_PLUGINS_UPDATE_ACTIVEX_UPDATE3WEB_CONTROL_H_
+#define OMAHA_PLUGINS_UPDATE_ACTIVEX_UPDATE3WEB_CONTROL_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include <atlctl.h>
+
+#include "base/basictypes.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/base/atlregmapex.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/omaha_version.h"
+#include "common/goopdate_utils.h"
+#include "omaha/plugins/update/config.h"
+#include "omaha/plugins/update/resource.h"
+#include "omaha/plugins/update/site_lock.h"
+#include "plugins/update/activex/update_control_idl.h"
+
+namespace omaha {
+
+class Update3WebControl;
+
+typedef IObjectSafetyImpl<Update3WebControl, INTERFACESAFE_FOR_UNTRUSTED_CALLER>
+    Update3WebControlSafety;
+
+class ATL_NO_VTABLE Update3WebControl
+    : public CComObjectRootEx<CComObjectThreadModel>,
+      public CComCoClass<Update3WebControl,
+                         &__uuidof(GoogleUpdate3WebControlCoClass)>,
+      public IDispatchImpl<IGoogleUpdate3WebControl,
+                           &__uuidof(IGoogleUpdate3WebControl),
+                           &LIBID_GoogleUpdateControlLib, 0xffff, 0xffff>,
+      public Update3WebControlSafety,
+      public IObjectWithSiteImpl<Update3WebControl> {
+ public:
+  Update3WebControl();
+
+  DECLARE_NOT_AGGREGATABLE(Update3WebControl)
+  DECLARE_REGISTRY_RESOURCEID_EX(IDR_ONECLICK_RGS)
+
+#pragma warning(push)
+// Construction of local static object is not thread-safe
+#pragma warning(disable:4640)
+  BEGIN_REGISTRY_MAP()
+    REGMAP_ENTRY(L"CLSID",              GetObjectCLSID())
+    REGMAP_ENTRY(L"PROGID",             kUpdate3WebControlProgId)
+    REGMAP_ENTRY(L"HKROOT",             goopdate_utils::GetHKRoot())
+    REGMAP_ENTRY(L"SHELLNAME",          is_machine() ? kOmahaBrokerFileName :
+                                                       kOmahaOnDemandFileName)
+    REGMAP_ENTRY(L"SHELLPATH",          GetShellPathForRegMap())
+    // The following entries are actually for the NPAPI plugin
+    REGMAP_ENTRY(L"PLUGINDESCRIPTION",  kAppName)
+    REGMAP_ENTRY(L"PLUGINDOMAIN",       kGoopdateServer)
+    REGMAP_ENTRY(L"PLUGINVENDOR",       kFullCompanyName)
+    REGMAP_ENTRY(L"PLUGINVERSION",      kUpdate3WebPluginVersion)
+    REGMAP_ENTRY(L"PLUGINPRODUCT",      kAppName)
+    REGMAP_ENTRY(L"PLUGINMIMETYPE",     UPDATE3WEB_MIME_TYPE)
+  END_REGISTRY_MAP()
+#pragma warning(pop)
+
+  BEGIN_COM_MAP(Update3WebControl)
+    COM_INTERFACE_ENTRY(IDispatch)
+    COM_INTERFACE_ENTRY(IObjectSafety)
+    COM_INTERFACE_ENTRY(IObjectWithSite)
+  END_COM_MAP()
+
+  // IGoogleUpdate3WebControl methods.
+  STDMETHOD(createOmahaMachineServerAsync)(VARIANT_BOOL create_elevated,
+                                           IDispatch** async_status);
+  STDMETHOD(createOmahaUserServer)(IDispatch** server);
+
+  // Gets the version of the passed in application guid. If the application is
+  // not installed, returns an empty string.
+  STDMETHOD(getInstalledVersion)(BSTR guid_string,
+                                 VARIANT_BOOL is_machine,
+                                 BSTR* version_string);
+
+  // OneClick-equivalent API, used for cross-installs.
+  STDMETHOD(crossInstall)(BSTR extra_args);
+
+  // Launches a command defined by an installed application. Fails if the
+  // command is not successfully started, succeeds otherwise. Returns without
+  // waiting for the command to complete.
+  STDMETHOD(launchAppCommand)(BSTR guid_string,
+                              VARIANT_BOOL is_machine,
+                              BSTR cmd_id);
+
+ protected:
+  virtual ~Update3WebControl();
+
+ private:
+  static bool is_machine() {
+    return goopdate_utils::IsRunningFromOfficialGoopdateDir(true);
+  }
+
+  static CString GetShellPathForRegMap() {
+    return goopdate_utils::BuildInstallDirectory(is_machine(),
+                                                 GetVersionString());
+  }
+
+  HRESULT GetVersionUsingCOMServer(const TCHAR* guid_string,
+                                   bool is_machine,
+                                   CString* version_string);
+  HRESULT GetVersionUsingRegistry(const TCHAR* guid_string,
+                                  bool is_machine,
+                                  CString* version_string);
+
+  SiteLock site_lock_;
+
+  friend class Update3WebControlTest;
+
+  DISALLOW_COPY_AND_ASSIGN(Update3WebControl);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_PLUGINS_UPDATE_ACTIVEX_UPDATE3WEB_CONTROL_H_
diff --git a/plugins/update/activex/update3web_control_unittest.cc b/plugins/update/activex/update3web_control_unittest.cc
new file mode 100644
index 0000000..804f23c
--- /dev/null
+++ b/plugins/update/activex/update3web_control_unittest.cc
@@ -0,0 +1,341 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/plugins/update/activex/update3web_control.h"
+#include <atlbase.h>
+#include <atlcom.h>
+#include <windows.h>
+#include "base/basictypes.h"
+#include "base/error.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+namespace {
+
+#define STUBMETHOD(identifier, ...) \
+  STDMETHOD(identifier)(__VA_ARGS__) {\
+    return E_NOTIMPL;\
+  }
+
+#define STUBGETPROP(identifier, type) \
+  STUBMETHOD(get_ ## identifier, type*)
+
+#define STUBPUTPROP(identifier, type) \
+  STUBMETHOD(put_ ## identifier, type)
+
+#define STUBPROP(identifier, type) \
+  STUBGETPROP(identifier, type)\
+  STUBPUTPROP(identifier, type)
+
+class ATL_NO_VTABLE MockWebBrowser2
+    : public CComObjectRootEx<CComObjectThreadModel>,
+      public IServiceProviderImpl<MockWebBrowser2>,
+      public IWebBrowser2 {
+ public:
+  MockWebBrowser2() : url_("something-like-a-url") {}
+  virtual ~MockWebBrowser2() {}
+
+  DECLARE_NOT_AGGREGATABLE(MockWebBrowser2);
+
+  BEGIN_COM_MAP(MockWebBrowser2)
+    COM_INTERFACE_ENTRY(IServiceProvider)
+    COM_INTERFACE_ENTRY(IWebBrowser2)
+  END_COM_MAP()
+
+  BEGIN_SERVICE_MAP(MockWebBrowser2)
+    SERVICE_ENTRY(SID_SWebBrowserApp)
+  END_SERVICE_MAP()
+
+  // IDispatch
+  STUBMETHOD(GetIDsOfNames, REFIID, OLECHAR**, unsigned int, LCID, DISPID*);
+  STUBMETHOD(GetTypeInfo, unsigned int, LCID, ITypeInfo**);
+  STUBMETHOD(GetTypeInfoCount, UINT*);
+  STUBMETHOD(Invoke, DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*,
+                     EXCEPINFO*, unsigned int*);
+
+  // IWebBrowser2
+  STUBPROP(AddressBar, VARIANT_BOOL);
+  STUBGETPROP(Application, IDispatch*);
+  STUBGETPROP(Busy, VARIANT_BOOL);
+  STUBMETHOD(ClientToWindow, int*, int*);
+  STUBGETPROP(Container, IDispatch*);
+  STUBGETPROP(Document, IDispatch*);
+  STUBMETHOD(ExecWB, OLECMDID, OLECMDEXECOPT, VARIANT*, VARIANT*);
+  STUBGETPROP(FullName, BSTR);
+  STUBPROP(FullScreen, VARIANT_BOOL);
+  STUBMETHOD(GetProperty, BSTR, VARIANT*);
+  STUBMETHOD(GoBack, VOID);
+  STUBMETHOD(GoForward, VOID);
+  STUBMETHOD(GoHome, VOID);
+  STUBMETHOD(GoSearch, VOID);
+  STUBPROP(Height, LONG);
+  STUBGETPROP(HWND, LONG);
+  STUBPROP(Left, LONG);
+  STUBGETPROP(LocationName, BSTR);
+  STDMETHOD(get_LocationURL)(BSTR* url) {
+    *url = CComBSTR(url_).Detach();
+    return S_OK;
+  }
+  STUBPROP(MenuBar, VARIANT_BOOL);
+  STUBGETPROP(Name, BSTR);
+  STUBMETHOD(Navigate, BSTR, VARIANT*, VARIANT*, VARIANT*, VARIANT*);
+  STUBMETHOD(Navigate2, VARIANT*, VARIANT*, VARIANT*, VARIANT*, VARIANT*);
+  STUBPROP(Offline, VARIANT_BOOL);
+  STUBGETPROP(Path, BSTR);
+  STUBGETPROP(Parent, IDispatch*);
+  STUBMETHOD(PutProperty, BSTR, VARIANT);
+  STUBMETHOD(QueryStatusWB, OLECMDID, OLECMDF*);
+  STUBMETHOD(Quit, VOID);
+  STUBGETPROP(ReadyState, READYSTATE);
+  STUBMETHOD(Refresh, VOID);
+  STUBMETHOD(Refresh2, VARIANT*);
+  STUBPROP(RegisterAsBrowser, VARIANT_BOOL);
+  STUBPROP(RegisterAsDropTarget, VARIANT_BOOL);
+  STUBPROP(Resizable, VARIANT_BOOL);
+  STUBMETHOD(ShowBrowserBar, VARIANT*, VARIANT*, VARIANT*);
+  STUBPROP(Silent, VARIANT_BOOL);
+  STUBPROP(StatusBar, VARIANT_BOOL);
+  STUBPROP(StatusText, BSTR);
+  STUBMETHOD(Stop, VOID);
+  STUBPROP(TheaterMode, VARIANT_BOOL);
+  STUBPROP(ToolBar, int);
+  STUBPROP(Top, LONG);
+  STUBGETPROP(TopLevelContainer, VARIANT_BOOL);
+  STUBPROP(Type, BSTR);
+  STUBPROP(Visible, VARIANT_BOOL);
+  STUBPROP(Width, LONG);
+
+  void set_url(const char* url) { url_ = url; }
+
+ private:
+  CString url_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockWebBrowser2);
+};
+
+class ATL_NO_VTABLE MockHTMLDocument2
+    : public CComObjectRootEx<CComObjectThreadModel>,
+      public IServiceProvider,
+      public IOleClientSite,
+      public IOleContainer,
+      public IHTMLDocument2 {
+ public:
+  MockHTMLDocument2() {}
+  virtual ~MockHTMLDocument2() {}
+
+  DECLARE_NOT_AGGREGATABLE(MockHTMLDocument2);
+
+  BEGIN_COM_MAP(MockHTMLDocument2)
+    COM_INTERFACE_ENTRY(IServiceProvider)
+    COM_INTERFACE_ENTRY(IOleClientSite)
+    COM_INTERFACE_ENTRY(IOleContainer)
+    COM_INTERFACE_ENTRY(IHTMLDocument2)
+  END_COM_MAP()
+
+  // IServiceProvider
+  STUBMETHOD(QueryService, REFGUID, REFIID, void**);
+
+  // IOleContainer
+  STDMETHOD(GetContainer)(IOleContainer** container) {
+    return QueryInterface(IID_PPV_ARGS(container));
+  }
+  STUBMETHOD(GetMoniker, DWORD, DWORD, IMoniker**);
+  STUBMETHOD(OnShowWindow, BOOL);
+  STUBMETHOD(RequestNewObjectLayout);
+  STUBMETHOD(SaveObject);
+  STUBMETHOD(ShowObject);
+
+  // IOleContainer
+  STUBMETHOD(EnumObjects, DWORD, IEnumUnknown**);
+  STUBMETHOD(LockContainer, BOOL);
+
+  // IDispatch
+  STUBMETHOD(GetIDsOfNames, REFIID, OLECHAR**, unsigned int, LCID, DISPID*);
+  STUBMETHOD(GetTypeInfo, unsigned int, LCID, ITypeInfo**);
+  STUBMETHOD(GetTypeInfoCount, UINT*);
+  STUBMETHOD(Invoke, DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*,
+                     EXCEPINFO*, unsigned int*);
+
+  // IParseDisplayName
+  STUBMETHOD(ParseDisplayName, IBindCtx*, LPOLESTR, ULONG*, IMoniker**);
+
+  // IHTMLDocument2
+  STUBGETPROP(activeElement, IHTMLElement*);
+  STUBPROP(alinkColor, VARIANT);
+  STUBGETPROP(all, IHTMLElementCollection*);
+  STUBGETPROP(anchors, IHTMLElementCollection*);
+  STUBGETPROP(applets, IHTMLElementCollection*);
+  STUBPROP(bgColor, VARIANT);
+  STUBGETPROP(body, IHTMLElement*);
+  STUBPROP(charset, BSTR);
+  STUBMETHOD(clear, VOID);
+  STUBMETHOD(close, VOID);
+  STUBPROP(cookie, BSTR);
+  STUBMETHOD(createElement, BSTR, IHTMLElement**);
+  STUBMETHOD(createStyleSheet, BSTR, LONG, IHTMLStyleSheet**);
+  STUBPROP(defaultCharset, BSTR);
+  STUBPROP(designMode, BSTR);
+  STUBPROP(domain, BSTR);
+  STUBPROP(elementFromPoint, BSTR);
+  STUBMETHOD(elementFromPoint, LONG, LONG, IHTMLElement**);
+  STUBGETPROP(embeds, IHTMLElementCollection*);
+  STUBMETHOD(execCommand, BSTR, VARIANT_BOOL, VARIANT, VARIANT_BOOL*);
+  STUBMETHOD(execCommandShowHelp, BSTR, VARIANT_BOOL*);
+  STUBPROP(expando, VARIANT_BOOL);
+  STUBPROP(fgColor, VARIANT);
+  STUBGETPROP(fileCreatedDate, BSTR);
+  STUBGETPROP(fileModifiedDate, BSTR);
+  STUBGETPROP(fileSize, BSTR);
+  STUBGETPROP(fileUpdatedDate, BSTR);
+  STUBGETPROP(forms, IHTMLElementCollection*);
+  STUBGETPROP(frames, IHTMLFramesCollection2*);
+  STUBGETPROP(images, IHTMLElementCollection*);
+  STUBGETPROP(lastModified, BSTR);
+  STUBPROP(linkColor, VARIANT);
+  STUBGETPROP(links, IHTMLElementCollection*);
+  STUBGETPROP(location, IHTMLLocation*);
+  STUBGETPROP(mimeType, BSTR);
+  STUBGETPROP(nameProp, BSTR);
+  STUBPROP(onafterupdate, VARIANT);
+  STUBPROP(onbeforeupdate, VARIANT);
+  STUBPROP(onclick, VARIANT);
+  STUBPROP(ondblclick, VARIANT);
+  STUBPROP(ondragstart, VARIANT);
+  STUBPROP(onerrorupdate, VARIANT);
+  STUBPROP(onhelp, VARIANT);
+  STUBPROP(onkeydown, VARIANT);
+  STUBPROP(onkeypress, VARIANT);
+  STUBPROP(onkeyup, VARIANT);
+  STUBPROP(onmousedown, VARIANT);
+  STUBPROP(onmousemove, VARIANT);
+  STUBPROP(onmouseout, VARIANT);
+  STUBPROP(onmouseover, VARIANT);
+  STUBPROP(onmouseup, VARIANT);
+  STUBPROP(onreadystatechange, VARIANT);
+  STUBPROP(onrowenter, VARIANT);
+  STUBPROP(onrowexit, VARIANT);
+  STUBPROP(onselectstart, VARIANT);
+  STUBMETHOD(open, BSTR, VARIANT, VARIANT, VARIANT, IDispatch**);
+  STUBGETPROP(parentWindow, IHTMLWindow2*);
+  STUBGETPROP(plugins, IHTMLElementCollection*);
+  STUBPROP(protocol, BSTR);
+  STUBMETHOD(queryCommandEnabled, BSTR, VARIANT_BOOL*);
+  STUBMETHOD(queryCommandIndeterm, BSTR, VARIANT_BOOL*);
+  STUBMETHOD(queryCommandState, BSTR, VARIANT_BOOL*);
+  STUBMETHOD(queryCommandSupported, BSTR, VARIANT_BOOL*);
+  STUBMETHOD(queryCommandText, BSTR, BSTR*);
+  STUBMETHOD(queryCommandValue, BSTR, VARIANT*);
+  STUBGETPROP(readyState, BSTR);
+  STUBGETPROP(referrer, BSTR);
+  STUBGETPROP(Script, IDispatch*);
+  STUBGETPROP(security, BSTR);
+  STUBGETPROP(selection, IHTMLSelectionObject*);
+  STUBGETPROP(scripts, IHTMLElementCollection*);
+  STUBGETPROP(styleSheets, IHTMLStyleSheetsCollection*);
+  STUBPROP(title, BSTR);
+  STUBMETHOD(toString, BSTR*);
+  STDMETHOD(get_URL)(BSTR* url) {
+    *url = CComBSTR("something-else-like-a-url").Detach();
+    return S_OK;
+  }
+  STUBPUTPROP(URL, BSTR);
+  STUBPROP(vlinkColor, VARIANT);
+  STUBMETHOD(write, SAFEARRAY*);
+  STUBMETHOD(writeln, SAFEARRAY*);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockHTMLDocument2);
+};
+
+#undef STUBMETHOD
+#undef STUBGETPROP
+#undef STUBPUTPROP
+#undef STUBPROP
+
+template <typename T>
+HRESULT CComObjectCreatorHelper(CComObject<T>** ptr) {
+  if (!ptr) {
+    return E_POINTER;
+  }
+  CComObject<T>* raw_ptr = NULL;
+  RET_IF_FAILED(CComObject<T>::CreateInstance(&raw_ptr));
+  raw_ptr->AddRef();
+  *ptr = raw_ptr;
+  return S_OK;
+}
+
+
+}  // namespace
+
+class Update3WebControlTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    ASSERT_SUCCEEDED(CComObjectCreatorHelper(&control_));
+  }
+
+  virtual void TearDown() {
+  }
+
+  HRESULT GetCurrentBrowserUrl(CString* url) {
+    return control_->site_lock_.GetCurrentBrowserUrl(control_, url);
+  }
+
+  CComPtr<CComObject<Update3WebControl> > control_;
+};
+
+TEST_F(Update3WebControlTest, SiteLock) {
+  CComPtr<CComObject<MockWebBrowser2> > browser;
+  ASSERT_SUCCEEDED(CComObjectCreatorHelper(&browser));
+  CComPtr<IUnknown> unknown;
+  ASSERT_SUCCEEDED(browser.QueryInterface(&unknown));
+  ASSERT_SUCCEEDED(control_->SetSite(unknown));
+  browser->set_url("http://www.google.com/pack/page.html");
+  EXPECT_EQ(E_POINTER,
+      control_->getInstalledVersion(CComBSTR(), VARIANT_FALSE, NULL));
+}
+
+TEST_F(Update3WebControlTest, SiteLock_Negative) {
+  CComPtr<IWebBrowser2> browser;
+  ASSERT_SUCCEEDED(CComCoClass<MockWebBrowser2>::CreateInstance(&browser));
+  ASSERT_SUCCEEDED(control_->SetSite(browser));
+  EXPECT_EQ(GOOPDATE_E_ONECLICK_HOSTCHECK_FAILED,
+      control_->getInstalledVersion(CComBSTR(), VARIANT_FALSE, NULL));
+}
+
+TEST_F(Update3WebControlTest, GetCurrentBrowserUrl_FailIfNoSite) {
+  CString url;
+  EXPECT_FALSE(SUCCEEDED(GetCurrentBrowserUrl(&url)));
+}
+
+TEST_F(Update3WebControlTest, GetCurrentBrowserUrl_WithIWebBrowser2) {
+  CComPtr<IUnknown> browser;
+  ASSERT_SUCCEEDED(CComCoClass<MockWebBrowser2>::CreateInstance(&browser));
+  ASSERT_SUCCEEDED(control_->SetSite(browser));
+  CString url;
+  EXPECT_EQ(S_OK, GetCurrentBrowserUrl(&url));
+  EXPECT_STREQ(L"something-like-a-url", url);
+}
+
+TEST_F(Update3WebControlTest, GetCurrentBrowserUrl_WithIHTMLDocument2) {
+  CComPtr<IUnknown> document;
+  ASSERT_SUCCEEDED(CComCoClass<MockHTMLDocument2>::CreateInstance(&document));
+  ASSERT_SUCCEEDED(control_->SetSite(document));
+  CString url;
+  EXPECT_EQ(S_OK, GetCurrentBrowserUrl(&url));
+  EXPECT_STREQ(L"something-else-like-a-url", url);
+}
+
+}  // namespace omaha
diff --git a/plugins/update/activex/update_control_idl.idl b/plugins/update/activex/update_control_idl.idl
new file mode 100644
index 0000000..8db79b0
--- /dev/null
+++ b/plugins/update/activex/update_control_idl.idl
@@ -0,0 +1,103 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+import "oaidl.idl";
+
+[
+  object,
+  uuid(c40a1df0-d2d8-4f01-9b1d-3c0d4f226dd9),
+  dual,
+  helpstring("GoogleUpdate3Web Control"),
+  pointer_default(unique)
+]
+interface IGoogleUpdate3WebControl : IDispatch {
+  [id(1)] HRESULT createOmahaMachineServerAsync(
+      [in] VARIANT_BOOL create_elevated,
+      [out, retval] IDispatch** async_status);
+
+  [id(2)] HRESULT createOmahaUserServer([out, retval] IDispatch** server);
+
+  [id(3)] HRESULT getInstalledVersion([in] BSTR guid_string,
+                                      [in] VARIANT_BOOL is_machine,
+                                      [out, retval] BSTR* version_string);
+
+  [id(4)] HRESULT crossInstall([in] BSTR extra_args);
+
+  [id(5)] HRESULT launchAppCommand([in] BSTR guid_string,
+                                   [in] VARIANT_BOOL is_machine,
+                                   [in] BSTR cmd_id);
+};
+
+[
+  object,
+  uuid(b8b0a927-6c2a-4de2-b6d7-a98fe4e42465),
+  dual,
+  helpstring("Google Update OneClick Control"),
+  pointer_default(unique)
+]
+interface IGoogleUpdateOneClick : IDispatch
+{
+  // Deprecated. Will be removed in the next release of OneClick.
+  [id(1), helpstring("Install")] HRESULT Install(
+      [in] BSTR cmd_line_args,
+      [in] VARIANT* success_callback,
+      [in] VARIANT* failure_callback);
+
+  // New, easier way of calling Install. Use this for newer web pages.
+  [id(2), helpstring("Install2")] HRESULT Install2([in] BSTR extra_args);
+
+  [id(3), helpstring("GetInstalledVersion")] HRESULT GetInstalledVersion(
+      [in] BSTR guid_string,
+      [in] VARIANT_BOOL is_machine,
+      [out, retval] BSTR* version_string);
+
+  [id(4), helpstring("GetOneClickVersion")] HRESULT GetOneClickVersion(
+      [out, retval] long* version);
+
+  // TODO(omaha): Remove this if LaunchAppCommand only ships in Omaha3
+  [id(5), helpstring("LaunchAppCommand")] HRESULT LaunchAppCommand(
+      [in] BSTR app_guid,
+      [in] VARIANT_BOOL is_machine,
+      [in] BSTR cmd_id);
+};
+
+[
+  uuid(8c1a5505-5837-47e1-963e-dbc5c3f0f52a),
+  version(1.0),
+  helpstring("Omaha Browser Plugins 3.0 Type Library")
+]
+library GoogleUpdateControlLib {
+  importlib("stdole2.tlb");
+
+  interface IGoogleUpdateOneClick;
+  interface IGoogleUpdate3WebControl;
+
+  [
+    uuid(77e6be0b-059d-4ff6-a534-4aa0970a746c),
+    helpstring("Google Update OneClick Control Class")
+  ]
+  coclass GoogleUpdateOneClickControlCoClass
+  {
+    [default] interface IGoogleUpdateOneClick;
+  };
+
+  [
+    uuid(2f7cf8e2-27d9-4641-845d-3bfedece0326),
+    helpstring("GoogleUpdate3Web Control Class")
+  ]
+  coclass GoogleUpdate3WebControlCoClass {
+    [default] interface IDispatch;
+  };
+};
diff --git a/plugins/update/build.scons b/plugins/update/build.scons
new file mode 100644
index 0000000..a48e914
--- /dev/null
+++ b/plugins/update/build.scons
@@ -0,0 +1,286 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========================================================================
+
+
+Import('env')
+
+# Need to do all these modifications to the non-default env because calling
+# EnablePrecompile on the main environment, which customization_test_env clones.
+# causes the opt build to fail.
+# TODO(omaha): How much of this needs to be used by all libraries that get built
+# and how much can be moved to BuildNpGoogleUpdateDll?
+update_env = env.Clone()
+update_env.Append(
+    CPPDEFINES = [
+        # TODO(omaha): ActiveX plugins are usually apartment-threaded. There's
+        # no problem with compiling at a higher threading level than required,
+        # but there is a slight performance penalty for AddRef()/Release(). At
+        # some point, it may be desirable to switch back to apartment threaded,
+        # once the unit tests are fixed.
+        # We also take a 1KiB penalty for using _ATL_FREE_THREADED =)
+        '_ATL_FREE_THREADED',
+    ],
+    CPPFLAGS = [
+        # TODO(omaha): use PCH so we get /Wall. Need to figure out why half of
+        # the old plugin builds with PCH, and the other half of it builds
+        # without...
+        '/Wall',
+        # omaha/third_party/base/basictypes.h
+        '/wd4310',  # cast truncates constant value
+        # ATL
+        '/wd4263',  # member function does not override any base class virtual
+                    # member function
+        '/wd4264',  # no override available for virtual member function;
+                    # function is hidden
+        '/wd4265',  # class has virtual functions, but destructor is not virtual
+        # vector
+        '/wd4548',  # expression before comma has no effect; expected expression
+                    # with side-effect
+    ],
+    CPPPATH = [
+        '$OBJ_ROOT',  # Needed for generated files.
+    ],
+    LIBS = [
+        '$LIB_DIR/base.lib',
+        '$LIB_DIR/breakpad.lib',
+        '$LIB_DIR/client.lib',
+        '$LIB_DIR/core.lib',
+        '$LIB_DIR/google_update_recovery.lib',
+        '$LIB_DIR/goopdate_lib.lib',
+        '$LIB_DIR/logging.lib',
+        '$LIB_DIR/net.lib',
+        '$LIB_DIR/omaha3_idl.lib',
+        '$LIB_DIR/security.lib',
+        '$LIB_DIR/service.lib',
+        '$LIB_DIR/setup.lib',
+        '$LIB_DIR/statsreport.lib',
+        '$LIB_DIR/ui.lib',
+        ('atls.lib', 'atlsd.lib')[env.Bit('debug')],
+        ('libcmt.lib', 'libcmtd.lib')[env.Bit('debug')],
+        ('libcpmt.lib', 'libcpmtd.lib')[env.Bit('debug')],
+        'bits.lib',
+        'comctl32.lib',
+        'crypt32.lib',
+        'iphlpapi.lib',
+        'msi.lib',
+        'msimg32.lib',
+        'mstask.lib',
+        'psapi.lib',
+        'netapi32.lib',
+        'rasapi32.lib',
+        'shlwapi.lib',
+        'taskschd.lib',
+        'userenv.lib',
+        'version.lib',
+        'wintrust.lib',
+        'wtsapi32.lib',
+    ],
+    LINKFLAGS = [
+        '/DYNAMICBASE',   # Enable ASLR.
+        '/NXCOMPAT',      # Enable NX support.
+    ],
+)
+
+
+# COM stuff.
+midl_env = env.Clone()
+midl_env.Tool('midl')
+# Generate optimized, stubless proxy/stub code.
+midl_env['MIDLFLAGS'] += [ '/Oicf', ]
+
+# For some reason, calling TypeLibrary() twice on the same environment causes a
+# re-link of the test executable when alternating between "hammer" and
+# "hammer run_all_tests".
+# To work around this, clone the original environment and call TypeLibrary once
+# on each. Note that:
+#  1) Cloning midl_env BEFORE TypeLibrary() is called does not solve this
+#     problem. The environment must be cloned from the original env.
+#  2) Moving the following code down near npapi_testing_midl_env.TypeLibrary
+#     does not solve the problem either.
+#  3) This problem does not occur if 'update_npapi_testing.lib' is passed as
+#     a source to other builders instead of as a LIB.
+npapi_testing_midl_env = env.Clone()
+npapi_testing_midl_env.Tool('midl')
+# Generate optimized, stubless proxy/stub code.
+npapi_testing_midl_env['MIDLFLAGS'] += [ '/Oicf', ]
+
+midl_env.TypeLibrary('activex/update_control_idl.idl')
+
+# Generate the GUIDs with no precompile option set, otherwise we get an error.
+update_control_idl_guids_lib = env.ComponentStaticLibrary(
+    lib_name='update_control_idl_guids_lib',
+    source='$OBJ_ROOT/plugins/update/activex/update_control_idl_i.c',
+    use_pch_default=False,
+)
+
+# Built as library to enable unit testing.
+inputs = [
+    'activex/update3web_control.cc',
+    'activex/oneclick_control.cc',
+    'npapi/dispatch_host.cc',
+    'npapi/npfunction_host.cc',
+    'npapi/urlpropbag.cc',
+    'npapi/np_update.cc',
+    'npapi/variant_utils.cc',
+    'config.cc',
+    'site_lock.cc',
+    ]
+update_lib = update_env.ComponentStaticLibrary(
+    lib_name='update',
+    source=inputs,
+)
+
+
+def BuildNpGoogleUpdateDll(omaha_version_info):
+  version_string = omaha_version_info.GetVersionString()
+  prefix = omaha_version_info.filename_prefix
+
+  plugin_env = update_env.Clone(COMPONENT_STATIC = False)
+
+  if prefix == 'TEST_':
+    plugin_env['OBJPREFIX'] = plugin_env.subst('test/$OBJPREFIX')
+  elif prefix:
+    raise Exception('ERROR: Unrecognized prefix "%s"' % prefix)
+
+  plugin_env.Append(
+      LIBS = [
+          '$LIB_DIR/common.lib',
+          '$LIB_DIR/plugin_base.lib',
+          'wininet.lib',
+          update_control_idl_guids_lib,
+          update_lib,
+      ],
+      RCFLAGS = [
+          '/DVERSION_MAJOR=%d' % omaha_version_info.version_major,
+          '/DVERSION_MINOR=%d' % omaha_version_info.version_minor,
+          '/DVERSION_BUILD=%d' % omaha_version_info.version_build,
+          '/DVERSION_PATCH=%d' % omaha_version_info.version_patch,
+          '/DVERSION_NUMBER_STRING=\\"%s\\"' % version_string,
+      ],
+  )
+
+
+  resource = plugin_env.RES(target='%sresource.res' % prefix,
+                            source='resource.rc')
+  plugin_env.Depends(
+      resource,
+      ['$MAIN_DIR/VERSION',
+       '$OBJ_ROOT/plugins/update/activex/update_control_idl.tlb',
+       'oneclick.rgs'])
+
+  target_name = '%s%s' % (
+      prefix,
+      omaha_version_info.plugin_signed_file_info.unsigned_filename_base)
+
+  inputs = [
+      'module.def',
+      'module.cc',
+      resource,
+      ]
+
+  unsigned_dll = plugin_env.ComponentDll(
+      lib_name=target_name,
+      source=inputs,
+  )
+
+  signed_dll = plugin_env.SignedBinary(
+      target='%s%s' % (prefix,
+                       omaha_version_info.plugin_signed_file_info.filename),
+      source=unsigned_dll,
+  )
+
+  env.Replicate('$STAGING_DIR', signed_dll)
+  env.Replicate('$STAGING_DIR', [f for f in unsigned_dll if f.suffix == '.pdb'])
+
+
+for omaha_version_info in env['omaha_versions_info']:
+  BuildNpGoogleUpdateDll(omaha_version_info)
+
+
+#
+# Tests
+#
+
+# NPAPI unit test helper library
+npapi_testing_midl_env.TypeLibrary('npapi/testing/dispatch_host_test_idl.idl')
+inputs = [
+    'npapi/testing/dispatch_host_test_interface.cc',
+    'npapi/testing/stubs.cc',
+    ]
+update_test_helpers_lib = update_env.ComponentStaticLibrary(
+    lib_name='update_test_helpers',
+    source=inputs,
+)
+
+npapi_test_lib_resources = update_env.RES(
+    target='npapi/testing/dispatch_host_test.res',
+    source='npapi/testing/dispatch_host_test.rc'),
+# TODO(omaha): we should really just make a proper .rc file scanner.
+update_env.Depends(
+    npapi_test_lib_resources,
+    '$OBJ_ROOT/plugins/update/npapi/testing/dispatch_host_test_idl.tlb')
+
+# TODO(omaha): Fix OmahaUnittest() so that MODE=all does not break.
+update_env.OmahaUnittest(
+    name='update_plugin_unittest',
+    source=[
+        'activex/update3web_control_unittest.cc',
+        'npapi/dispatch_host_unittest.cc',
+        'npapi/npfunction_host_unittest.cc',
+        'npapi/variant_utils_unittest.cc',
+        'site_lock_unittest.cc',
+        npapi_test_lib_resources,
+    ],
+    LIBS=[
+        '$LIB_DIR/common.lib',
+        'wininet',
+        update_control_idl_guids_lib,
+        update_lib,
+        update_test_helpers_lib,
+    ],
+    all_in_one=False,
+    COMPONENT_TEST_SIZE='small',
+)
+
+
+customization_test_env = env.Clone()
+customization_test_env.Append(
+    LIBS = [
+        '$LIB_DIR/common.lib',
+    ]
+)
+customization_test_env['CPPPATH'] += [
+    '$OBJ_ROOT',  # Needed for generated files.
+    ]
+customization_test = customization_test_env.OmahaUnittest(
+    name='omaha_customization_update_apis_unittest',
+    source=[
+        'omaha_customization_update_apis_unittest.cc',
+        'omaha_customization_update_unittest.cc',
+    ],
+    LIBS=[
+        update_control_idl_guids_lib,  # Needed for LIBID_*.
+        update_lib,
+    ],
+    all_in_one=False,
+    COMPONENT_TEST_SIZE='small',
+)
+
+# The test uses the DLL for its TypeLib.
+customization_test_env.Depends(
+    customization_test,
+    '$STAGING_DIR/%s' % env['omaha_versions_info'][0].update_plugin_filename)
diff --git a/plugins/update/config.cc b/plugins/update/config.cc
new file mode 100644
index 0000000..6a4a2af
--- /dev/null
+++ b/plugins/update/config.cc
@@ -0,0 +1,32 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/plugins/update/config.h"
+#include <tchar.h>
+#include "omaha/base/constants.h"
+
+namespace omaha {
+
+const TCHAR kUpdate3WebPluginVersion[] = _T(UPDATE_PLUGIN_VERSION_ANSI);
+const TCHAR kUpdate3WebControlProgId[] = COMPANY_NAME_IDENTIFIER _T(".") \
+                                         _T("Update3WebControl.") \
+                                         _T(UPDATE_PLUGIN_VERSION_ANSI);
+
+const TCHAR kOneclickPluginVersion[] = _T(ONECLICK_PLUGIN_VERSION_ANSI);
+const TCHAR kOneclickControlProgId[] = COMPANY_NAME_IDENTIFIER _T(".") \
+                                         _T("OneClickCtrl.") \
+                                         _T(ONECLICK_PLUGIN_VERSION_ANSI);
+
+}  // namespace omaha
diff --git a/plugins/update/config.h b/plugins/update/config.h
new file mode 100644
index 0000000..b761984
--- /dev/null
+++ b/plugins/update/config.h
@@ -0,0 +1,47 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_PLUGINS_UPDATE_CONFIG_H_
+#define OMAHA_PLUGINS_UPDATE_CONFIG_H_
+
+#include <windows.h>
+
+namespace omaha {
+
+// TODO(omaha3): The OneClick MIME type is currently defined one other place in
+// the codebase, in base\const_config.h.  I'm putting our own definition here;
+// we should probably delete the copy in base, and potentially moving this file
+// to base as plugin_constants.h.
+//
+// Note that COMPANY_DOMAIN_BASE_ANSI and *_PLUGIN_VERSION_ANSI are defined
+// in omaha\main.scons.
+
+#define UPDATE3WEB_MIME_TYPE   "application/x-vnd." COMPANY_DOMAIN_BASE_ANSI \
+                               ".update3webcontrol." UPDATE_PLUGIN_VERSION_ANSI
+
+#define ONECLICK_MIME_TYPE     "application/x-vnd." COMPANY_DOMAIN_BASE_ANSI \
+                               ".oneclickctrl." ONECLICK_PLUGIN_VERSION_ANSI
+
+#define MERGED_MIME_TYPE UPDATE3WEB_MIME_TYPE "|" ONECLICK_MIME_TYPE
+
+extern const TCHAR kUpdate3WebPluginVersion[];
+extern const TCHAR kUpdate3WebControlProgId[];
+
+extern const TCHAR kOneclickPluginVersion[];
+extern const TCHAR kOneclickControlProgId[];
+
+}  // namespace omaha
+
+#endif  // OMAHA_PLUGINS_UPDATE_CONFIG_H_
diff --git a/plugins/generate_oneclick_idl.py b/plugins/update/generate_plugin_idls.py
similarity index 100%
rename from plugins/generate_oneclick_idl.py
rename to plugins/update/generate_plugin_idls.py
diff --git a/plugins/update/module.cc b/plugins/update/module.cc
new file mode 100644
index 0000000..f52e0a6
--- /dev/null
+++ b/plugins/update/module.cc
@@ -0,0 +1,72 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <atlbase.h>
+#include "base/basictypes.h"
+#include "omaha/base/omaha_version.h"
+#include "goopdate/omaha3_idl.h"
+#include "omaha/plugins/update/activex/update3web_control.h"
+#include "omaha/plugins/update/activex/oneclick_control.h"
+#include "plugins/update/activex/update_control_idl.h"
+
+namespace omaha {
+
+OBJECT_ENTRY_AUTO(__uuidof(GoogleUpdateOneClickControlCoClass), OneClickControl)
+OBJECT_ENTRY_AUTO(__uuidof(GoogleUpdate3WebControlCoClass), Update3WebControl)
+
+namespace {
+
+class GoogleUpdateControlModule
+    : public CAtlDllModuleT<GoogleUpdateControlModule> {
+ public:
+  GoogleUpdateControlModule() {}
+
+  DECLARE_LIBID(LIBID_GoogleUpdateControlLib);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GoogleUpdateControlModule);  // NOLINT
+} _AtlModule;
+
+}  // namespace
+
+}  // namespace omaha
+
+BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) {
+  switch (reason) {
+    case DLL_PROCESS_ATTACH:
+      omaha::InitializeVersionFromModule(instance);
+      break;
+    default:
+      break;
+  }
+
+  return omaha::_AtlModule.DllMain(reason, reserved);
+}
+
+STDAPI DllCanUnloadNow() {
+  return omaha::_AtlModule.DllCanUnloadNow();
+}
+
+STDAPI DllGetClassObject(REFCLSID clsid, REFIID iid, void** ppv) {
+  return omaha::_AtlModule.DllGetClassObject(clsid, iid, ppv);
+}
+
+STDAPI DllRegisterServer() {
+  return omaha::_AtlModule.DllRegisterServer(false);
+}
+
+STDAPI DllUnregisterServer() {
+  return omaha::_AtlModule.DllUnregisterServer(false);
+}
diff --git a/plugins/update/module.def b/plugins/update/module.def
new file mode 100644
index 0000000..e98111e
--- /dev/null
+++ b/plugins/update/module.def
@@ -0,0 +1,23 @@
+; Copyright 2009 Google Inc.
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+;      http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+; ========================================================================
+
+EXPORTS
+  NP_GetEntryPoints   @1
+  NP_Initialize       @2
+  NP_Shutdown         @3
+  DllCanUnloadNow     PRIVATE
+  DllGetClassObject   PRIVATE
+  DllRegisterServer   PRIVATE
+  DllUnregisterServer PRIVATE
diff --git a/plugins/update/npapi/dispatch_host.cc b/plugins/update/npapi/dispatch_host.cc
new file mode 100644
index 0000000..8d7604f
--- /dev/null
+++ b/plugins/update/npapi/dispatch_host.cc
@@ -0,0 +1,266 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// TODO(omaha): use NPN_SetException to return useful error information.
+
+#include "omaha/plugins/update/npapi/dispatch_host.h"
+
+#include "base/logging.h"
+#include "base/scope_guard.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/string.h"
+#include "omaha/plugins/update/npapi/variant_utils.h"
+
+namespace omaha {
+
+namespace {
+
+void SetExceptionIfFailed(NPObject* object, HRESULT result) {
+  if (FAILED(result)) {
+    CStringA message;
+    message.Format("0x%08x", result);
+    NPN_SetException(object, message);
+  }
+}
+
+}  // namespace
+
+DispatchHost* DispatchHost::CreateInstance(NPP npp, IDispatch* dispatch) {
+  ASSERT1(dispatch);
+  DispatchHost* host = static_cast<DispatchHost*>(
+      NPN_CreateObject(npp, &kNPClass_));
+  host->dispatch_ = dispatch;
+  CORE_LOG(L3, (L"[DispatchHost::DispatchHost][this=0x%p][dispatch=0x%p]",
+                host, dispatch));
+  return host;
+}
+
+DispatchHost::DispatchHost(NPP npp) : npp_(npp) {
+}
+
+DispatchHost::~DispatchHost() {
+  CORE_LOG(L3, (L"[DispatchHost::~DispatchHost][this=0x%p][dispatch=0x%p]",
+                this, dispatch_));
+}
+
+DISPID DispatchHost::GetDispatchId(NPIdentifier name) {
+  NPUTF8* utf8_name = NPN_UTF8FromIdentifier(name);
+  CString wide_name = Utf8ToWideChar(utf8_name, lstrlenA(utf8_name));
+  NPN_MemFree(utf8_name);
+  DISPID dispatch_id = DISPID_UNKNOWN;
+  HRESULT hr = dispatch_.GetIDOfName(wide_name, &dispatch_id);
+  if (FAILED(hr)) {
+    return DISPID_UNKNOWN;
+  }
+  return dispatch_id;
+}
+
+// Whether or not a member should be treated as a property by NPAPI. A member is
+// considered a property for NPAPI if either of the following are true:
+// - The property is a getter with exactly one [out, retval] argument.
+// - The property is a putter with exactly one [in] argument.
+// The reason for this limitation is NPAPI does not support passing additional
+// arguments when getting/setting properties. Properties that take additional
+// arguments are handled as methods by NPAPI instead.
+bool DispatchHost::IsProperty(DISPID dispatch_id) {
+  CComPtr<ITypeInfo> type_info;
+  HRESULT hr = dispatch_->GetTypeInfo(0, LOCALE_SYSTEM_DEFAULT, &type_info);
+  if (FAILED(hr)) {
+    ASSERT(false, (L"[IsProperty][failed=0x%08x]", hr));
+    return false;
+  }
+  TYPEATTR* type_attr;
+  hr = type_info->GetTypeAttr(&type_attr);
+  if (FAILED(hr)) {
+    ASSERT(false, (L"[IsProperty][failed=0x%08x]", hr));
+    return false;
+  }
+  ON_SCOPE_EXIT_OBJ(*type_info.p, &ITypeInfo::ReleaseTypeAttr, type_attr);
+
+  for (int i = 0; i < type_attr->cFuncs; ++i) {
+    FUNCDESC* func_desc = NULL;
+    hr = type_info->GetFuncDesc(i, &func_desc);
+    if (FAILED(hr)) {
+      ASSERT(false, (L"[IsProperty][failed=0x%08x]", hr));
+      return false;
+    }
+    ON_SCOPE_EXIT_OBJ(*type_info.p, &ITypeInfo::ReleaseFuncDesc, func_desc);
+    if (dispatch_id == func_desc->memid) {
+      if (((func_desc->invkind & DISPATCH_PROPERTYGET) &&
+            func_desc->cParams == 0) ||
+          ((func_desc->invkind & DISPATCH_PROPERTYPUT) &&
+           func_desc->cParams == 1 &&
+           (func_desc->lprgelemdescParam[0].paramdesc.wParamFlags &
+            PARAMFLAG_FIN))) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+// Simple helper to adapt NPAPI method/property invocations to IDispatch::Invoke
+// by wrapping/unwrapping NPVariants into VARIANTs.
+HRESULT DispatchHost::InvokeHelper(DISPID dispatch_id, WORD flags,
+                                   const NPVariant* args, uint32_t arg_count,
+                                   NPP npp, NPVariant* result) {
+  ASSERT1(args || arg_count == 0);
+  ASSERT1(result);
+  CORE_LOG(L3, (L"[InvokeHelper][this=0x%p][dispatch=0x%p][flags=0x%x]"
+                L"[arg_count=%d]", this, dispatch_, flags, arg_count));
+
+  // Just in case a rogue browser decides to use the return value on failure.
+  VOID_TO_NPVARIANT(*result);
+  scoped_array<CComVariant> dispatch_args(new CComVariant[arg_count]);
+
+  // IDispatch::Invoke expects arguments in "reverse" order
+  for (uint32_t i = 0 ; i < arg_count; ++i) {
+    NPVariantToVariant(npp, args[i], &dispatch_args[arg_count - i - 1]);
+  }
+  DISPPARAMS dispatch_params = {};
+  dispatch_params.rgvarg = dispatch_args.get();
+  dispatch_params.cArgs = arg_count;
+  CComVariant dispatch_result;
+  HRESULT hr = dispatch_->Invoke(dispatch_id, IID_NULL, LOCALE_USER_DEFAULT,
+                                 flags, &dispatch_params, &dispatch_result,
+                                 NULL, NULL);
+  if (FAILED(hr)) {
+    CORE_LOG(L3, (L"[InvokeHelper][failed_hr=0x%p]", hr));
+    return hr;
+  }
+  VariantToNPVariant(npp, dispatch_result, result);
+  return hr;
+}
+
+NPObject* DispatchHost::Allocate(NPP npp, NPClass* class_functions) {
+  UNREFERENCED_PARAMETER(class_functions);
+  return new DispatchHost(npp);
+}
+
+void DispatchHost::Deallocate(NPObject* object) {
+  delete static_cast<DispatchHost*>(object);
+}
+
+bool DispatchHost::HasMethod(NPObject* object, NPIdentifier name) {
+  DispatchHost* host = static_cast<DispatchHost*>(object);
+  DISPID dispatch_id = host->GetDispatchId(name);
+  return dispatch_id != DISPID_UNKNOWN && !host->IsProperty(dispatch_id);
+}
+
+bool DispatchHost::Invoke(NPObject* object, NPIdentifier name,
+                          const NPVariant* args, uint32_t arg_count,
+                          NPVariant* result) {
+  DispatchHost* host = static_cast<DispatchHost*>(object);
+  CORE_LOG(L3, (L"[DispatchHost::Invoke][this=0x%p][dispatch=0x%p]",
+                host, host->dispatch_));
+  HRESULT hr = host->InvokeHelper(host->GetDispatchId(name),
+                                  DISPATCH_METHOD | DISPATCH_PROPERTYGET,
+                                  args,
+                                  arg_count,
+                                  host->npp_,
+                                  result);
+  SetExceptionIfFailed(object, hr);
+  return SUCCEEDED(hr);
+}
+
+bool DispatchHost::InvokeDefault(NPObject* object, const NPVariant* args,
+                                 uint32_t arg_count, NPVariant* result) {
+  DispatchHost* host = static_cast<DispatchHost*>(object);
+  CORE_LOG(L3, (L"[DispatchHost::InvokeDefault][this=0x%p][dispatch=0x%p]",
+                host, host->dispatch_));
+  HRESULT hr = host->InvokeHelper(DISPID_VALUE,
+                                  DISPATCH_METHOD | DISPATCH_PROPERTYGET,
+                                  args,
+                                  arg_count,
+                                  host->npp_,
+                                  result);
+  SetExceptionIfFailed(object, hr);
+  return SUCCEEDED(hr);
+}
+
+bool DispatchHost::HasProperty(NPObject* object, NPIdentifier name) {
+  DispatchHost* host = static_cast<DispatchHost*>(object);
+  DISPID dispatch_id = host->GetDispatchId(name);
+  return dispatch_id != DISPID_UNKNOWN && host->IsProperty(dispatch_id);
+}
+
+bool DispatchHost::GetProperty(NPObject* object, NPIdentifier name,
+                               NPVariant* result) {
+  DispatchHost* host = static_cast<DispatchHost*>(object);
+  CORE_LOG(L3, (L"[DispatchHost::GetProperty][this=0x%p][dispatch=0x%p]",
+                host, host->dispatch_));
+  HRESULT hr = host->InvokeHelper(host->GetDispatchId(name),
+                                  DISPATCH_PROPERTYGET,
+                                  NULL,
+                                  0,
+                                  host->npp_,
+                                  result);
+  SetExceptionIfFailed(object, hr);
+  return SUCCEEDED(hr);
+}
+
+bool DispatchHost::SetProperty(NPObject* object, NPIdentifier name,
+                               const NPVariant* value) {
+  DispatchHost* host = static_cast<DispatchHost*>(object);
+  CORE_LOG(L3, (L"[DispatchHost::SetProperty][this=0x%p][dispatch=0x%p]",
+                host, host->dispatch_));
+  DISPID dispatch_id = host->GetDispatchId(name);
+  CComVariant dispatch_arg;
+  NPVariantToVariant(host->npp_, *value, &dispatch_arg);
+  HRESULT hr = host->dispatch_.PutProperty(dispatch_id, &dispatch_arg);
+  SetExceptionIfFailed(object, hr);
+  return SUCCEEDED(hr);
+}
+
+bool DispatchHost::RemoveProperty(NPObject* object, NPIdentifier name) {
+  UNREFERENCED_PARAMETER(object);
+  UNREFERENCED_PARAMETER(name);
+  return false;
+}
+
+bool DispatchHost::Enumerate(NPObject* object, NPIdentifier** names,
+                             uint32_t* count) {
+  UNREFERENCED_PARAMETER(object);
+  UNREFERENCED_PARAMETER(names);
+  UNREFERENCED_PARAMETER(count);
+  return false;
+}
+
+bool DispatchHost::Construct(NPObject* object, const NPVariant* args,
+                             uint32_t arg_count, NPVariant* result) {
+  UNREFERENCED_PARAMETER(object);
+  UNREFERENCED_PARAMETER(args);
+  UNREFERENCED_PARAMETER(arg_count);
+  UNREFERENCED_PARAMETER(result);
+  return false;
+}
+
+NPClass DispatchHost::kNPClass_ = {
+  NP_CLASS_STRUCT_VERSION,
+  Allocate,
+  Deallocate,
+  NULL,
+  HasMethod,
+  Invoke,
+  InvokeDefault,
+  HasProperty,
+  GetProperty,
+  SetProperty,
+  RemoveProperty,
+  Enumerate,
+  Construct,
+};
+
+}  // namespace omaha
diff --git a/plugins/update/npapi/dispatch_host.h b/plugins/update/npapi/dispatch_host.h
new file mode 100644
index 0000000..7bdcb02
--- /dev/null
+++ b/plugins/update/npapi/dispatch_host.h
@@ -0,0 +1,87 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// DispatchHost hosts an IDispatch object inside a NPObject to allow scripting
+// of COM objects from a NPAPI environment. Types are automatically marshalled
+// between NPVariant and VARIANT using the functions in variant_utils.h.
+// Limitations:
+// - IDispatch methods/properties may only take arguments of type VT_VOID,
+//   VT_NULL, VT_BOOL, VT_I4, VT_R8, and VT_BSTR
+// - Multiple out parameters are not supported.
+// - IDispatch methods/properties may only return a value of type VT_EMPTY,
+//   VT_VOID, VT_NULL, VT_BOOL, VT_I4, VT_UI4, VT_R8, VT_BSTR, and VT_DISPATCH
+// - A method and property a property that takes additional arguments may not
+//   have the same identifier--the method will not be callable through
+//   DispatchHost.
+
+#ifndef OMAHA_PLUGINS_UPDATE_NPAPI_DISPATCH_HOST_H_
+#define OMAHA_PLUGINS_UPDATE_NPAPI_DISPATCH_HOST_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+#include "base/basictypes.h"
+#include "third_party/npapi/bindings/nphostapi.h"
+
+namespace omaha {
+
+class DispatchHostTest;
+
+class DispatchHost : public NPObject {
+ public:
+  static DispatchHost* CreateInstance(NPP npp, IDispatch* dispatch);
+
+ private:
+  explicit DispatchHost(NPP npp);
+  ~DispatchHost();
+
+  DISPID GetDispatchId(NPIdentifier name);
+  bool IsProperty(DISPID dispatch_id);
+  HRESULT InvokeHelper(DISPID dispatch_id, WORD flags, const NPVariant* args,
+                       uint32_t arg_count, NPP npp, NPVariant* result);
+
+  static NPObject* Allocate(NPP npp, NPClass *class_functions);
+  static void Deallocate(NPObject* object);
+  static bool HasMethod(NPObject* object, NPIdentifier name);
+  static bool Invoke(NPObject* object, NPIdentifier name, const NPVariant* args,
+                     uint32_t arg_count, NPVariant* result);
+  static bool InvokeDefault(NPObject* object, const NPVariant* args,
+                            uint32_t arg_count, NPVariant* result);
+  static bool HasProperty(NPObject* object, NPIdentifier name);
+  static bool GetProperty(NPObject* object, NPIdentifier name,
+                          NPVariant* result);
+  static bool SetProperty(NPObject* object, NPIdentifier name,
+                          const NPVariant* value);
+  static bool RemoveProperty(NPObject* object, NPIdentifier name);
+  static bool Enumerate(NPObject* object, NPIdentifier** names,
+                        uint32_t* count);
+  static bool Construct(NPObject* object, const NPVariant* args,
+                        uint32_t arg_count, NPVariant* result);
+
+  NPP npp_;
+  // The hosted dispatch object.
+  CComPtr<IDispatch> dispatch_;
+
+  // The NPObject vtable.
+  static NPClass kNPClass_;
+
+  friend class DispatchHostTest;
+
+  DISALLOW_COPY_AND_ASSIGN(DispatchHost);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_PLUGINS_UPDATE_NPAPI_DISPATCH_HOST_H_
diff --git a/plugins/update/npapi/dispatch_host_unittest.cc b/plugins/update/npapi/dispatch_host_unittest.cc
new file mode 100644
index 0000000..14f310c
--- /dev/null
+++ b/plugins/update/npapi/dispatch_host_unittest.cc
@@ -0,0 +1,229 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/plugins/update/npapi/dispatch_host.h"
+#include <atlbase.h>
+#include <atlcom.h>
+#include <string.h>
+#include <vector>
+#include "omaha/plugins/update/npapi/testing/dispatch_host_test_interface.h"
+#include "omaha/plugins/update/npapi/testing/stubs.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+class DispatchHostTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    CComPtr<IDispatch> dispatch;
+    ASSERT_SUCCEEDED(
+        CComCoClass<DispatchHostTestInterface>::CreateInstance(&dispatch));
+    dispatch_host_ = NPN_CreateObject(NULL, &DispatchHost::kNPClass_);
+    static_cast<DispatchHost*>(dispatch_host_)->dispatch_ = dispatch.Detach();
+    VOID_TO_NPVARIANT(result_);
+  }
+
+  virtual void TearDown() {
+    NPN_ReleaseObject(dispatch_host_);
+    for (std::vector<NPVariant>::iterator it = args_.begin();
+         it != args_.end(); ++it) {
+      NPN_ReleaseVariantValue(&*it);
+    }
+    NPN_ReleaseVariantValue(&result_);
+  }
+
+  void UseTestInterface2() {
+    NPN_ReleaseObject(dispatch_host_);
+    CComPtr<IDispatch> dispatch;
+    ASSERT_SUCCEEDED(
+        CComCoClass<DispatchHostTestInterface2>::CreateInstance(&dispatch));
+    dispatch_host_ = NPN_CreateObject(NULL, &DispatchHost::kNPClass_);
+    static_cast<DispatchHost*>(dispatch_host_)->dispatch_ = dispatch.Detach();
+  }
+
+  void PushArg(bool value) {
+    args_.push_back(NPVariant());
+    BOOLEAN_TO_NPVARIANT(value, args_.back());
+  }
+
+  void PushArg(int32 value) {
+    args_.push_back(NPVariant());
+    INT32_TO_NPVARIANT(value, args_.back());
+  }
+
+  void PushArg(double value) {
+    args_.push_back(NPVariant());
+    DOUBLE_TO_NPVARIANT(value, args_.back());
+  }
+
+  void PushArg(const char* value) {
+    args_.push_back(NPVariant());
+    // TODO(omaha): _strdup is an implementation detail of the stubs.
+    STRINGZ_TO_NPVARIANT(_strdup(value), args_.back());
+  }
+
+  NPObject* dispatch_host_;
+  NPIdentifierFactory id_factory_;
+
+  std::vector<NPVariant> args_;
+  NPVariant result_;
+};
+
+TEST_F(DispatchHostTest, HasMethod) {
+  EXPECT_TRUE(NPN_HasMethod(NULL, dispatch_host_,
+                            id_factory_.Create("Random")));
+  EXPECT_TRUE(NPN_HasMethod(NULL, dispatch_host_,
+                            id_factory_.Create("AddAsMethod")));
+
+  // Property getters with input arguments should be treated as methods.
+  EXPECT_TRUE(NPN_HasMethod(NULL, dispatch_host_,
+                            id_factory_.Create("AddAsProperty")));
+
+  // Properties and non-existent members are not methods.
+  EXPECT_FALSE(NPN_HasMethod(NULL, dispatch_host_,
+                             id_factory_.Create("Property")));
+  EXPECT_FALSE(NPN_HasMethod(NULL, dispatch_host_,
+                             id_factory_.Create("ReadOnlyProperty")));
+  EXPECT_FALSE(NPN_HasMethod(NULL, dispatch_host_,
+                             id_factory_.Create("WriteOnlyProperty")));
+  EXPECT_FALSE(NPN_HasMethod(NULL, dispatch_host_,
+                             id_factory_.Create("DoesNotExist")));
+}
+
+TEST_F(DispatchHostTest, InvokeNoArgs) {
+  EXPECT_TRUE(NPN_Invoke(NULL, dispatch_host_, id_factory_.Create("Random"),
+                         NULL, 0, &result_));
+  EXPECT_TRUE(NPVARIANT_IS_INT32(result_));
+  EXPECT_EQ(42, result_.value.intValue);
+}
+
+TEST_F(DispatchHostTest, InvokeWithArgs) {
+  PushArg(7);
+  PushArg(27);
+  EXPECT_TRUE(NPN_Invoke(NULL, dispatch_host_,
+                         id_factory_.Create("AddAsMethod"), &args_.front(),
+                         args_.size(), &result_));
+  EXPECT_TRUE(NPVARIANT_IS_INT32(result_));
+  EXPECT_EQ(34, result_.value.intValue);
+}
+
+TEST_F(DispatchHostTest, InvokePropertyWithArgs) {
+  // Property getters that have input args should be handle by Invoke
+  PushArg(8);
+  PushArg(15);
+  EXPECT_TRUE(NPN_Invoke(NULL, dispatch_host_,
+                         id_factory_.Create("AddAsProperty"), &args_.front(),
+                         args_.size(), &result_));
+  EXPECT_TRUE(NPVARIANT_IS_INT32(result_));
+  EXPECT_EQ(23, result_.value.intValue);
+}
+
+TEST_F(DispatchHostTest, InvokeNonexistentMethod) {
+  // Non-existent method, should fail.
+  INT32_TO_NPVARIANT(0x19821982, result_);
+  EXPECT_FALSE(NPN_Invoke(NULL, dispatch_host_,
+                          id_factory_.Create("NonExistent"), NULL, 0,
+                          &result_));
+  EXPECT_TRUE(NPVARIANT_IS_VOID(result_));
+}
+
+TEST_F(DispatchHostTest, InvokeWithIncompatibleArgs) {
+  PushArg("Hello World!");
+  PushArg(0x19851985);
+  INT32_TO_NPVARIANT(0x19881988, result_);
+  EXPECT_FALSE(NPN_Invoke(NULL, dispatch_host_,
+                          id_factory_.Create("AddAsMethod"), &args_.front(),
+                          args_.size(), &result_));
+  EXPECT_TRUE(NPVARIANT_IS_VOID(result_));
+}
+
+TEST_F(DispatchHostTest, InvokeWithIncorrectNumberOfArgs) {
+  PushArg("Don't panic.");
+  INT32_TO_NPVARIANT(0x77777777, result_);
+  EXPECT_FALSE(NPN_Invoke(NULL, dispatch_host_, id_factory_.Create("Random"),
+                          &args_.front(), args_.size(), &result_));
+  EXPECT_TRUE(NPVARIANT_IS_VOID(result_));
+}
+
+TEST_F(DispatchHostTest, InvokeDefault) {
+  EXPECT_TRUE(NPN_InvokeDefault(NULL, dispatch_host_, NULL, 0, &result_));
+  EXPECT_TRUE(NPVARIANT_IS_OBJECT(result_));
+}
+
+TEST_F(DispatchHostTest, InvokeDefaultPropertyWithArgs) {
+  UseTestInterface2();
+  PushArg(1048576);
+  EXPECT_TRUE(NPN_InvokeDefault(NULL, dispatch_host_, &args_.front(),
+                                args_.size(), &result_));
+  EXPECT_TRUE(NPVARIANT_IS_INT32(result_));
+  EXPECT_EQ(1048576 * 2, result_.value.intValue);
+}
+
+// TODO(omaha): implement negative test
+
+TEST_F(DispatchHostTest, HasProperty) {
+  EXPECT_TRUE(NPN_HasProperty(NULL, dispatch_host_,
+                              id_factory_.Create("Property")));
+  EXPECT_TRUE(NPN_HasProperty(NULL, dispatch_host_,
+                              id_factory_.Create("ReadOnlyProperty")));
+  EXPECT_TRUE(NPN_HasProperty(NULL, dispatch_host_,
+                              id_factory_.Create("WriteOnlyProperty")));
+
+  // Property getters with input arguments should not be treated as properties.
+  EXPECT_FALSE(NPN_HasProperty(NULL, dispatch_host_,
+                               id_factory_.Create("AddAsProperty")));
+
+  // Methods and non-existent members are not properties.
+  EXPECT_FALSE(NPN_HasProperty(NULL, dispatch_host_,
+                               id_factory_.Create("Random")));
+  EXPECT_FALSE(NPN_HasProperty(NULL, dispatch_host_,
+                               id_factory_.Create("DoesNotExist")));
+}
+
+TEST_F(DispatchHostTest, GetProperty) {
+  EXPECT_TRUE(NPN_GetProperty(NULL, dispatch_host_,
+                              id_factory_.Create("Property"), &result_));
+  EXPECT_TRUE(NPVARIANT_IS_INT32(result_));
+  EXPECT_EQ(0xdeadbeef, result_.value.intValue);
+}
+
+TEST_F(DispatchHostTest, GetPropertyReadOnly) {
+  EXPECT_TRUE(NPN_GetProperty(NULL, dispatch_host_,
+                              id_factory_.Create("ReadOnlyProperty"),
+                              &result_));
+  EXPECT_TRUE(NPVARIANT_IS_INT32(result_));
+  EXPECT_EQ(19700101, result_.value.intValue);
+}
+
+TEST_F(DispatchHostTest, SetProperty) {
+  PushArg(20002000);
+  EXPECT_TRUE(NPN_SetProperty(NULL, dispatch_host_,
+                              id_factory_.Create("Property"), &args_.front()));
+}
+
+TEST_F(DispatchHostTest, SetPropertyWriteOnly) {
+  PushArg(20612061);
+  EXPECT_TRUE(NPN_SetProperty(NULL, dispatch_host_,
+                              id_factory_.Create("WriteOnlyProperty"),
+                              &args_.front()));
+}
+
+TEST_F(DispatchHostTest, Unsupported) {
+  EXPECT_FALSE(NPN_RemoveProperty(NULL, dispatch_host_, NULL));
+  EXPECT_FALSE(NPN_Enumerate(NULL, dispatch_host_, NULL, NULL));
+  EXPECT_FALSE(NPN_Construct(NULL, dispatch_host_, NULL, 0, NULL));
+}
+
+}  // namespace omaha
diff --git a/plugins/update/npapi/np_update.cc b/plugins/update/npapi/np_update.cc
new file mode 100644
index 0000000..948c0fa
--- /dev/null
+++ b/plugins/update/npapi/np_update.cc
@@ -0,0 +1,188 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/plugins/update/npapi/np_update.h"
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+#include "omaha/base/debug.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/string.h"
+#include "omaha/plugins/update/config.h"
+#include "omaha/plugins/update/npapi/dispatch_host.h"
+#include "omaha/plugins/update/npapi/urlpropbag.h"
+#include "plugins/update/activex/update_control_idl.h"
+
+NPError NS_PluginInitialize() {
+  return NPERR_NO_ERROR;
+}
+
+void NS_PluginShutdown() {
+}
+
+nsPluginInstanceBase* NS_NewPluginInstance(nsPluginCreateData* data) {
+  return new omaha::NPUpdate(data->instance, data->type);
+}
+
+void NS_DestroyPluginInstance(nsPluginInstanceBase* plugin) {
+  delete plugin;
+}
+
+namespace omaha {
+
+NPUpdate::NPUpdate(NPP instance, const char* mime_type)
+    : instance_(instance),
+      is_initialized_(false),
+      mime_type_(mime_type),
+      scriptable_object_(NULL) {
+  ASSERT1(instance);
+  // TODO(omaha): initialize COM
+}
+
+NPUpdate::~NPUpdate() {
+  if (scriptable_object_) {
+    NPN_ReleaseObject(scriptable_object_);
+  }
+}
+
+NPBool NPUpdate::init(NPWindow* np_window) {
+  UNREFERENCED_PARAMETER(np_window);
+  is_initialized_ = true;
+  return TRUE;
+}
+
+void NPUpdate::shut() {
+  is_initialized_ = false;
+}
+
+NPBool NPUpdate::isInitialized() {
+  // TODO(omaha): figure the right boolean type to return here...
+  return is_initialized_ ? TRUE : FALSE;
+}
+
+NPError NPUpdate::GetValue(NPPVariable variable, void* value) {
+  if (!instance_) {
+    return NPERR_INVALID_INSTANCE_ERROR;
+  }
+
+  if (NPPVpluginScriptableNPObject != variable || !value) {
+    return NPERR_INVALID_PARAM;
+  }
+
+  CString url;
+  if (!GetCurrentBrowserUrl(&url) || !site_lock_.InApprovedDomain(url)) {
+    return NPERR_INVALID_URL;
+  }
+
+  if (!scriptable_object_) {
+    CComPtr<IDispatch> p;
+
+    CLSID clsid;
+    if (!MapMimeTypeToClsid(&clsid)) {
+      return NPERR_INVALID_PLUGIN_ERROR;
+    }
+    if (FAILED(p.CoCreateInstance(clsid))) {
+      return NPERR_OUT_OF_MEMORY_ERROR;
+    }
+
+    // Store the current URL in a property bag and set it as the site of
+    // the object.
+    CComPtr<IPropertyBag> pb;
+    if (FAILED(UrlPropertyBag::Create(url, &pb))) {
+      return NPERR_GENERIC_ERROR;
+    }
+    CComPtr<IObjectWithSite> sited_obj;
+    if (FAILED(p.QueryInterface(&sited_obj))) {
+      return NPERR_GENERIC_ERROR;
+    }
+    if (FAILED(sited_obj->SetSite(pb))) {
+      return NPERR_GENERIC_ERROR;
+    }
+
+    scriptable_object_ = DispatchHost::CreateInstance(instance_, p);
+  }
+
+  if (scriptable_object_) {
+    NPN_RetainObject(scriptable_object_);
+  } else {
+    return NPERR_OUT_OF_MEMORY_ERROR;
+  }
+
+  *(reinterpret_cast<NPObject**>(value)) = scriptable_object_;
+  return NPERR_NO_ERROR;
+}
+
+bool NPUpdate::MapMimeTypeToClsid(CLSID* clsid) {
+  ASSERT1(clsid);
+  // TODO(omaha): We could probably abstract this out to a map that can
+  // have entries added to it at runtime, making this module fully generic.
+  // We could also consider extracting the MIME_TYPE resource from the current
+  // DLL and populating it from that.
+  if (0 == mime_type_.CompareNoCase(CString(UPDATE3WEB_MIME_TYPE))) {
+    *clsid = __uuidof(GoogleUpdate3WebControlCoClass);
+    return true;
+  }
+  if (0 == mime_type_.CompareNoCase(CString(ONECLICK_MIME_TYPE))) {
+    *clsid = __uuidof(GoogleUpdateOneClickControlCoClass);
+    return true;
+  }
+  return false;
+}
+
+bool NPUpdate::GetCurrentBrowserUrl(CString* url) {
+  ASSERT1(url);
+
+  NPObject* window = NULL;
+  NPError error = NPN_GetValue(instance_, NPNVWindowNPObject, &window);
+  if (NPERR_NO_ERROR != error || !window) {
+    ASSERT(false, (L"NPN_GetValue returned error %d", error));
+    return false;
+  }
+  ON_SCOPE_EXIT(NPN_ReleaseObject, window);
+
+  NPIdentifier location_id = NPN_GetStringIdentifier("location");
+  NPVariant location;
+  NULL_TO_NPVARIANT(location);
+  if (!NPN_GetProperty(instance_, window, location_id, &location)) {
+    ASSERT1(false);
+    return false;
+  }
+  ON_SCOPE_EXIT(NPN_ReleaseVariantValue, &location);
+  if (!NPVARIANT_IS_OBJECT(location)) {
+    ASSERT(false, (L"Variant type: %d", location.type));
+    return false;
+  }
+
+  NPIdentifier href_id = NPN_GetStringIdentifier("href");
+  NPVariant href;
+  NULL_TO_NPVARIANT(href);
+  if (!NPN_GetProperty(instance_, NPVARIANT_TO_OBJECT(location), href_id,
+                       &href)) {
+    ASSERT1(false);
+    return false;
+  }
+  ON_SCOPE_EXIT(NPN_ReleaseVariantValue, &href);
+  if (!NPVARIANT_IS_STRING(href)) {
+    ASSERT(false, (L"Variant type: %d", href.type));
+    return false;
+  }
+
+  *url = Utf8ToWideChar(NPVARIANT_TO_STRING(href).UTF8Characters,
+                        NPVARIANT_TO_STRING(href).UTF8Length);
+  return true;
+}
+
+}  // namespace omaha
diff --git a/plugins/update/npapi/np_update.h b/plugins/update/npapi/np_update.h
new file mode 100644
index 0000000..c8668e7
--- /dev/null
+++ b/plugins/update/npapi/np_update.h
@@ -0,0 +1,57 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_PLUGINS_UPDATE_NPAPI_NP_UPDATE_H_
+#define OMAHA_PLUGINS_UPDATE_NPAPI_NP_UPDATE_H_
+
+#include <atlstr.h>
+#include "base/basictypes.h"
+#include "omaha/plugins/base/pluginbase.h"
+#include "omaha/plugins/update/site_lock.h"
+#include "third_party/npapi/bindings/nphostapi.h"
+
+namespace omaha {
+
+class DispatchHost;
+
+class NPUpdate : public nsPluginInstanceBase {
+ public:
+  explicit NPUpdate(NPP instance, const char* mime_type);
+  virtual ~NPUpdate();
+
+  // nsPluginInstanceBase overrides.
+  virtual NPBool init(NPWindow* np_window);
+  virtual void shut();
+  virtual NPBool isInitialized();
+  virtual NPError GetValue(NPPVariable variable, void* value);
+
+ private:
+  bool GetCurrentBrowserUrl(CString* url);
+  bool MapMimeTypeToClsid(CLSID* clsid);
+
+  NPP instance_;
+  bool is_initialized_;
+  CString mime_type_;
+  SiteLock site_lock_;
+  DispatchHost* scriptable_object_;
+
+  friend class NPUpdateTest;
+
+  DISALLOW_COPY_AND_ASSIGN(NPUpdate);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_PLUGINS_UPDATE_NPAPI_NP_UPDATE_H_
diff --git a/plugins/update/npapi/npfunction_host.cc b/plugins/update/npapi/npfunction_host.cc
new file mode 100644
index 0000000..19426cb
--- /dev/null
+++ b/plugins/update/npapi/npfunction_host.cc
@@ -0,0 +1,151 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/plugins/update/npapi/npfunction_host.h"
+
+#include "omaha/base/debug.h"
+#include "omaha/base/utils.h"
+#include "omaha/plugins/update/npapi/variant_utils.h"
+
+namespace omaha {
+
+typedef CComObject<NpFunctionHost> CComNpFuncHost;
+typedef scoped_any<CComNpFuncHost*, close_release_com, null_t> scoped_host;
+
+HRESULT NpFunctionHost::Create(NPP npp, NPObject* npobj, IDispatch** host) {
+  ASSERT1(npobj);
+  ASSERT1(host);
+
+  if (!npobj || !host) {
+    return E_INVALIDARG;
+  }
+
+  // Create the host and hand off the NPObject to it.
+  scoped_host comobj;
+  HRESULT hr = CComNpFuncHost::CreateInstance(address(comobj));
+  if (FAILED(hr)) {
+    return hr;
+  }
+  get(comobj)->AddRef();
+
+  comobj->npp_ = npp;
+  comobj->obj_ = npobj;
+  NPN_RetainObject(npobj);
+
+  return comobj->QueryInterface(host);
+}
+
+STDMETHODIMP NpFunctionHost::GetTypeInfoCount(UINT* pctinfo) {
+  if (pctinfo == NULL) {
+    return E_INVALIDARG;
+  }
+  *pctinfo = 0;
+  return S_OK;
+}
+
+STDMETHODIMP NpFunctionHost::GetTypeInfo(UINT iTInfo,
+                                         LCID lcid,
+                                         ITypeInfo** ppTInfo) {
+  UNREFERENCED_PARAMETER(iTInfo);
+  UNREFERENCED_PARAMETER(lcid);
+  UNREFERENCED_PARAMETER(ppTInfo);
+
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP NpFunctionHost::GetIDsOfNames(REFIID riid,
+                                           LPOLESTR* rgszNames,
+                                           UINT cNames,
+                                           LCID lcid,
+                                           DISPID* rgDispId) {
+  UNREFERENCED_PARAMETER(riid);
+  UNREFERENCED_PARAMETER(rgszNames);
+  UNREFERENCED_PARAMETER(cNames);
+  UNREFERENCED_PARAMETER(lcid);
+  UNREFERENCED_PARAMETER(rgDispId);
+
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP NpFunctionHost::Invoke(DISPID dispIdMember,
+                                    REFIID riid,
+                                    LCID lcid,
+                                    WORD wFlags,
+                                    DISPPARAMS* pDispParams,
+                                    VARIANT* pVarResult,
+                                    EXCEPINFO* pExcepInfo,
+                                    UINT* puArgErr) {
+  UNREFERENCED_PARAMETER(dispIdMember);
+  UNREFERENCED_PARAMETER(riid);
+  UNREFERENCED_PARAMETER(lcid);
+  UNREFERENCED_PARAMETER(pExcepInfo);
+  UNREFERENCED_PARAMETER(puArgErr);
+
+  if (wFlags != DISPATCH_METHOD) {
+    return DISP_E_MEMBERNOTFOUND;
+  }
+
+  uint32_t num_args = 0;
+  scoped_array<NPVariant> arguments;
+  if (pDispParams) {
+    // Javascript doesn't officially support named args, so the current
+    // implementation ignores any named args that are supplied.  However,
+    // you can cast a function object to a string and it will hold the
+    // argument names as used in the function definition.  Thus, if we
+    // need to support named arguments in the future, we may be able to
+    // get the argument names indirectly using NPN_Evaluate() and emulate.
+    if (pDispParams->cNamedArgs != 0) {
+      return DISP_E_NONAMEDARGS;
+    }
+    if (pDispParams->cArgs != 0) {
+      num_args = pDispParams->cArgs;
+      arguments.reset(new NPVariant[num_args]);
+      for (uint32_t i = 0; i < num_args; ++i) {
+        // Arguments are stored in rgvarg in reverse order.
+        VariantToNPVariant(npp_,
+                           pDispParams->rgvarg[num_args - 1 - i],
+                           &arguments[i]);
+      }
+    }
+  }
+
+  NPVariant retval;
+  VOID_TO_NPVARIANT(retval);
+
+  bool result = NPN_InvokeDefault(npp_,
+                                  obj_,
+                                  arguments.get(),
+                                  num_args,
+                                  &retval);
+  if (result && pVarResult) {
+    NPVariantToVariant(npp_, retval, pVarResult);
+  }
+  NPN_ReleaseVariantValue(&retval);
+
+  return result ? S_OK : E_FAIL;
+}
+
+void NpFunctionHost::FinalRelease() {
+  ASSERT1(obj_);
+  if (obj_) {
+    NPN_ReleaseObject(obj_);
+    obj_ = NULL;
+  }
+}
+
+NpFunctionHost::NpFunctionHost() : npp_(NULL), obj_(NULL) {}
+
+}  // namespace omaha
+
diff --git a/plugins/update/npapi/npfunction_host.h b/plugins/update/npapi/npfunction_host.h
new file mode 100644
index 0000000..11587d4
--- /dev/null
+++ b/plugins/update/npapi/npfunction_host.h
@@ -0,0 +1,86 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// NpFunctionHost hosts an NPObject inside an IDispatch interface to allow
+// invoking NPAPI functions from a COM environment.  Types are automatically
+// marshalled between NPVariant and VARIANT using the functions in
+// variant_utils.h.  (For the reverse -- providing an NPObject interface to
+// a COM object implementing IDispatch -- see DispatchHost.)
+//
+// Note that this currently only supports functions; this does not currently
+// support objects.  NPN_Enumerate() only provides method/property names,
+// not return types or argument counts/types, which makes it impossible to
+// properly implement IDispatch::GetTypeInfo().  (However, we can implement
+// GetIDsOfNames() if we need it in the future.)
+
+#ifndef OMAHA_PLUGINS_UPDATE_NPAPI_NPFUNCTION_HOST_H_
+#define OMAHA_PLUGINS_UPDATE_NPAPI_NPFUNCTION_HOST_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+#include "base/basictypes.h"
+#include "third_party/npapi/bindings/nphostapi.h"
+
+namespace omaha {
+
+class NpFunctionHostTest;
+
+class ATL_NO_VTABLE NpFunctionHost
+    : public CComObjectRootEx<CComObjectThreadModel>,
+      public IDispatch {
+ public:
+  static HRESULT Create(NPP npp, NPObject* npobj, IDispatch** host);
+
+  BEGIN_COM_MAP(NpFunctionHost)
+    COM_INTERFACE_ENTRY(IDispatch)
+  END_COM_MAP()
+
+  // IDispatch methods.
+  STDMETHOD(GetTypeInfoCount)(UINT* pctinfo);
+  STDMETHOD(GetTypeInfo)(UINT iTInfo,
+                         LCID lcid,
+                         ITypeInfo** ppTInfo);
+  STDMETHOD(GetIDsOfNames)(REFIID riid,
+                           LPOLESTR* rgszNames,
+                           UINT cNames,
+                           LCID lcid,
+                           DISPID* rgDispId);
+  STDMETHOD(Invoke)(DISPID dispIdMember,
+                    REFIID riid,
+                    LCID lcid,
+                    WORD wFlags,
+                    DISPPARAMS* pDispParams,
+                    VARIANT* pVarResult,
+                    EXCEPINFO* pExcepInfo,
+                    UINT* puArgErr);
+
+  // CComObjectRootEx overrides.
+  void FinalRelease();
+
+ protected:
+  NpFunctionHost();
+  virtual ~NpFunctionHost() {}
+
+ private:
+  NPP npp_;
+  NPObject* obj_;
+
+  DISALLOW_COPY_AND_ASSIGN(NpFunctionHost);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_PLUGINS_UPDATE_NPAPI_NPFUNCTION_HOST_H_
diff --git a/plugins/update/npapi/npfunction_host_unittest.cc b/plugins/update/npapi/npfunction_host_unittest.cc
new file mode 100644
index 0000000..81d3b9b
--- /dev/null
+++ b/plugins/update/npapi/npfunction_host_unittest.cc
@@ -0,0 +1,317 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/plugins/update/npapi/npfunction_host.h"
+#include <atlbase.h>
+#include <atlcom.h>
+#include <string.h>
+#include <vector>
+#include "omaha/plugins/update/npapi/testing/dispatch_host_test_interface.h"
+#include "omaha/plugins/update/npapi/testing/stubs.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+class NpFunctionHostTest;
+
+class MockFunctionNPObject : public NPObject {
+ public:
+  static MockFunctionNPObject* CreateInstance(NpFunctionHostTest* creator) {
+    NPObject* obj = NPN_CreateObject(NULL, &MockFunctionNPObject::kNPClass_);
+    MockFunctionNPObject* realobj = static_cast<MockFunctionNPObject*>(obj);
+    realobj->creator_ = creator;
+    return realobj;
+  }
+
+  static NPObject* Allocate(NPP npp, NPClass* class_functions) {
+    UNREFERENCED_PARAMETER(class_functions);
+    return new MockFunctionNPObject(npp);
+  }
+
+  static void Deallocate(NPObject* object) {
+    delete static_cast<MockFunctionNPObject*>(object);
+  }
+
+  static bool InvokeDefault(NPObject* object,
+                            const NPVariant* args,
+                            uint32_t arg_count,
+                            NPVariant* result) {
+    MockFunctionNPObject* realobj = static_cast<MockFunctionNPObject*>(object);
+    return realobj->InvokeDefaultLocal(args, arg_count, result);
+  }
+
+  bool InvokeDefaultLocal(const NPVariant* args,
+                          uint32_t arg_count,
+                          NPVariant* result);
+
+ protected:
+  explicit MockFunctionNPObject(NPP npp) : npp_(npp), creator_(NULL) {}
+
+  static NPUTF8* NPN_ReallocateStringZ(const char* string) {
+    uint32 buflen = strlen(string) + 1;
+    NPUTF8* npnstr = reinterpret_cast<NPUTF8*>(NPN_MemAlloc(buflen));
+    memmove(npnstr, string, buflen);
+    return npnstr;
+  }
+
+ private:
+  NPP npp_;
+  NpFunctionHostTest* creator_;
+
+  // The NPObject vtable.
+  static NPClass kNPClass_;
+};
+
+class NpFunctionHostTest : public testing::Test {
+ protected:
+  friend class MockFunctionNPObject;
+
+  virtual void SetUp() {
+    function_ = MockFunctionNPObject::CreateInstance(this);
+    EXPECT_SUCCEEDED(NpFunctionHost::Create(NULL, function_, &host_));
+  }
+
+  virtual void TearDown() {
+  }
+
+  NPObject* function_;
+  CComPtr<IDispatch> host_;
+
+  std::vector<NPVariant> mock_args_;
+  NPVariant mock_result_;
+};
+
+NPClass MockFunctionNPObject::kNPClass_ = {
+  NP_CLASS_STRUCT_VERSION,
+  Allocate,
+  Deallocate,
+  NULL,
+  NULL,
+  NULL,
+  InvokeDefault,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+};
+
+bool MockFunctionNPObject::InvokeDefaultLocal(const NPVariant* args,
+                                              uint32_t arg_count,
+                                              NPVariant* result) {
+  const char* kMultiStringReturn = "multi";
+
+  // The mock NPObject exhibits the following external behavior:
+  // * If no arguments, return nothing
+  // * If one argument, return a boolean (true)
+  // * If two arguments, return a string ("multi")
+  // * Otherwise, treat it as an invoke failure.
+  // It also copies the arguments as supplied, and the intended NPVariant
+  // return value, to the test closure that created it.
+
+  creator_->mock_args_.resize(arg_count);
+  for (uint32_t i = 0; i < arg_count; ++i) {
+    creator_->mock_args_[i] = args[i];
+  }
+
+  switch (arg_count) {
+    case 0:
+      VOID_TO_NPVARIANT(*result);
+      break;
+    case 1:
+      BOOLEAN_TO_NPVARIANT(true, *result);
+      break;
+    case 2:
+      {
+        NPUTF8* utf8string = NPN_ReallocateStringZ(kMultiStringReturn);
+        STRINGZ_TO_NPVARIANT(utf8string, *result);
+      }
+      break;
+    default:
+      return false;
+  }
+
+  creator_->mock_result_ = *result;
+  return true;
+}
+
+TEST_F(NpFunctionHostTest, GetTypeInfoCount) {
+  UINT typeinfos_available = 1;
+  EXPECT_SUCCEEDED(host_->GetTypeInfoCount(&typeinfos_available));
+  EXPECT_EQ(0, typeinfos_available);
+}
+
+TEST_F(NpFunctionHostTest, GetTypeInfo_NotImplemented) {
+  ITypeInfo* typeinfo = NULL;
+
+  EXPECT_EQ(E_NOTIMPL, host_->GetTypeInfo(0, LOCALE_SYSTEM_DEFAULT, &typeinfo));
+}
+
+TEST_F(NpFunctionHostTest, GetIDsOfNames_NotImplemented) {
+  LPOLESTR member_name = L"NonexistentMember";
+  DISPID member_dispid = 0;
+  EXPECT_EQ(E_NOTIMPL, host_->GetIDsOfNames(IID_NULL, &member_name, 1,
+                                            LOCALE_SYSTEM_DEFAULT,
+                                            &member_dispid));
+}
+
+TEST_F(NpFunctionHostTest, Invoke_NonMethod_NotSupported) {
+  EXPECT_EQ(DISP_E_MEMBERNOTFOUND, host_->Invoke(0, IID_NULL,
+                                                 LOCALE_SYSTEM_DEFAULT,
+                                                 DISPATCH_PROPERTYGET,
+                                                 NULL,
+                                                 NULL,
+                                                 NULL,
+                                                 NULL));
+}
+
+TEST_F(NpFunctionHostTest, Invoke_NamedArgs_NotSupported) {
+  DISPID param_name = 12;
+  DISPPARAMS params = {};
+  params.cNamedArgs = 1;
+  params.rgdispidNamedArgs = &param_name;
+  EXPECT_EQ(DISP_E_NONAMEDARGS, host_->Invoke(0, IID_NULL,
+                                              LOCALE_SYSTEM_DEFAULT,
+                                              DISPATCH_METHOD,
+                                              &params,
+                                              NULL,
+                                              NULL,
+                                              NULL));
+}
+
+TEST_F(NpFunctionHostTest, Invoke_NoArgs_NullDispParams) {
+  VARIANT retval = {};
+  EXPECT_SUCCEEDED(host_->Invoke(0, IID_NULL,
+                                 LOCALE_SYSTEM_DEFAULT,
+                                 DISPATCH_METHOD,
+                                 NULL,
+                                 &retval,
+                                 NULL,
+                                 NULL));
+
+  EXPECT_EQ(0, mock_args_.size());
+
+  EXPECT_TRUE(NPVARIANT_IS_VOID(mock_result_));
+  EXPECT_EQ(VT_EMPTY, retval.vt);
+  VariantClear(&retval);
+}
+
+TEST_F(NpFunctionHostTest, Invoke_NoArgs_ValidDispParams) {
+  VARIANT retval = {};
+  DISPPARAMS params = {};
+  EXPECT_SUCCEEDED(host_->Invoke(0, IID_NULL,
+                                 LOCALE_SYSTEM_DEFAULT,
+                                 DISPATCH_METHOD,
+                                 &params,
+                                 &retval,
+                                 NULL,
+                                 NULL));
+
+  EXPECT_EQ(0, mock_args_.size());
+
+  EXPECT_TRUE(NPVARIANT_IS_VOID(mock_result_));
+  EXPECT_EQ(VT_EMPTY, retval.vt);
+  VariantClear(&retval);
+}
+
+TEST_F(NpFunctionHostTest, Invoke_NoArgs_OneParam) {
+  const int kTestIntVal = 0xDEADBEEF;
+
+  VARIANT retval = {};
+  VARIANT firstparam = {};
+  firstparam.vt = VT_I4;
+  firstparam.intVal = kTestIntVal;
+
+  DISPPARAMS dispparams = {};
+  dispparams.cArgs = 1;
+  dispparams.rgvarg = &firstparam;
+
+  EXPECT_SUCCEEDED(host_->Invoke(0, IID_NULL,
+                                 LOCALE_SYSTEM_DEFAULT,
+                                 DISPATCH_METHOD,
+                                 &dispparams,
+                                 &retval,
+                                 NULL,
+                                 NULL));
+
+  EXPECT_EQ(1, mock_args_.size());
+  EXPECT_TRUE(NPVARIANT_IS_INT32(mock_args_[0]));
+  EXPECT_EQ(kTestIntVal, NPVARIANT_TO_INT32(mock_args_[0]));
+
+  EXPECT_TRUE(NPVARIANT_IS_BOOLEAN(mock_result_));
+  EXPECT_EQ(true, NPVARIANT_TO_BOOLEAN(mock_result_));
+  EXPECT_EQ(VT_BOOL, retval.vt);
+  EXPECT_EQ(VARIANT_TRUE, retval.boolVal);
+  VariantClear(&retval);
+}
+
+TEST_F(NpFunctionHostTest, Invoke_NoArgs_TwoParams) {
+  const double kTestFloatVal = 3.1415927;
+
+  VARIANT retval = {};
+  VARIANT params[2] = {};
+  params[0].vt = VT_BOOL;           // Invoke expects args in reverse order
+  params[0].intVal = VARIANT_TRUE;
+  params[1].vt = VT_R8;
+  params[1].dblVal = kTestFloatVal;
+
+  DISPPARAMS dispparams = {};
+  dispparams.cArgs = 2;
+  dispparams.rgvarg = params;
+  EXPECT_SUCCEEDED(host_->Invoke(0, IID_NULL,
+                                 LOCALE_SYSTEM_DEFAULT,
+                                 DISPATCH_METHOD,
+                                 &dispparams,
+                                 &retval,
+                                 NULL,
+                                 NULL));
+
+  EXPECT_EQ(2, mock_args_.size());
+  EXPECT_TRUE(NPVARIANT_IS_DOUBLE(mock_args_[0]));
+  EXPECT_EQ(kTestFloatVal, NPVARIANT_TO_DOUBLE(mock_args_[0]));
+  EXPECT_TRUE(NPVARIANT_IS_BOOLEAN(mock_args_[1]));
+  EXPECT_EQ(true, NPVARIANT_TO_BOOLEAN(mock_args_[1]));
+
+  EXPECT_TRUE(NPVARIANT_IS_STRING(mock_result_));
+  // Don't check mock_result's contents; it will have been released by Invoke()
+  EXPECT_EQ(VT_BSTR, retval.vt);
+  EXPECT_STREQ(CString("multi"), CString(retval.bstrVal));
+  VariantClear(&retval);
+}
+
+TEST_F(NpFunctionHostTest, Invoke_NoArgs_ThreeParams) {
+  VARIANT retval = {};
+  VARIANT params[3] = {};
+  for (int i = 0; i < 3; ++i) {
+    params[i].vt = VT_BOOL;
+    params[i].intVal = VARIANT_TRUE;
+  }
+
+  DISPPARAMS dispparams = {};
+  dispparams.cArgs = 3;
+  dispparams.rgvarg = params;
+  EXPECT_EQ(E_FAIL, host_->Invoke(0, IID_NULL,
+                                  LOCALE_SYSTEM_DEFAULT,
+                                  DISPATCH_METHOD,
+                                  &dispparams,
+                                  &retval,
+                                  NULL,
+                                  NULL));
+  EXPECT_EQ(3, mock_args_.size());
+}
+
+
+}  // namespace omaha
diff --git a/plugins/update/npapi/testing/dispatch_host_test.rc b/plugins/update/npapi/testing/dispatch_host_test.rc
new file mode 100644
index 0000000..fb0991e
--- /dev/null
+++ b/plugins/update/npapi/testing/dispatch_host_test.rc
@@ -0,0 +1,21 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <afxres.h>
+#include "omaha/plugins/update/npapi/testing/resource.h"
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+IDR_DISPATCH_HOST_TEST_TLB  TYPELIB "plugins/update/npapi/testing/dispatch_host_test_idl.tlb"
diff --git a/plugins/update/npapi/testing/dispatch_host_test_idl.idl b/plugins/update/npapi/testing/dispatch_host_test_idl.idl
new file mode 100644
index 0000000..dcb1007
--- /dev/null
+++ b/plugins/update/npapi/testing/dispatch_host_test_idl.idl
@@ -0,0 +1,61 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+import "oaidl.idl";
+import "ocidl.idl";
+
+[
+  object,
+  uuid(d5c74868-1734-44e1-bab2-187a0ad2bd37),
+  dual,
+  pointer_default(unique)
+]
+interface IDispatchHostTestInterface : IDispatch {
+  [id(1)] HRESULT Random([out, retval] INT* x);
+
+  [propget] HRESULT Property([out, retval] INT* x);
+  [propput] HRESULT Property([in] INT x);
+
+  [propget] HRESULT ReadOnlyProperty([out, retval] INT* x);
+  [propput] HRESULT WriteOnlyProperty([in] INT x);
+
+  [id(2)] HRESULT AddAsMethod([in] INT a, [in] INT b, [out, retval] INT* c);
+  [id(3), propget] HRESULT AddAsProperty([in] INT a, [in] INT b,
+                                         [out, retval] INT* c);
+
+  // TODO(omaha): do we need to test multi-argument property putters?
+
+  [id(DISPID_VALUE)] HRESULT DidYouMeanRecursion([out, retval] IDispatch** me);
+};
+
+[
+  object,
+  uuid(0abeb84d-6a9c-4c5c-9394-6123c933baec),
+  dual,
+  pointer_default(unique)
+]
+interface IDispatchHostTestInterface2 : IDispatch {
+  [id(DISPID_VALUE), propget] HRESULT Get([in] INT index, [out, retval] INT* x);
+};
+
+[
+  uuid(65530e7f-4922-49e2-a5ee-c3e172da974a),
+]
+library DispatchHostTestingLib {
+  importlib("stdole2.tlb");
+
+  interface IDispatchHostTestInterface;
+  interface IDispatchHostTestInterface2;
+};
diff --git a/plugins/update/npapi/testing/dispatch_host_test_interface.cc b/plugins/update/npapi/testing/dispatch_host_test_interface.cc
new file mode 100644
index 0000000..0760809
--- /dev/null
+++ b/plugins/update/npapi/testing/dispatch_host_test_interface.cc
@@ -0,0 +1,66 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/plugins/update/npapi/testing/dispatch_host_test_interface.h"
+
+namespace omaha {
+
+STDMETHODIMP DispatchHostTestInterface::Random(INT* x) {
+  *x = 42;
+  return S_OK;
+}
+
+STDMETHODIMP DispatchHostTestInterface::get_Property(INT* x) {
+  *x = 0xdeadbeef;
+  return S_OK;
+}
+
+STDMETHODIMP DispatchHostTestInterface::put_Property(INT x) {
+  UNREFERENCED_PARAMETER(x);
+  return S_OK;
+}
+
+STDMETHODIMP DispatchHostTestInterface::get_ReadOnlyProperty(INT* x) {
+  *x = 19700101;
+  return S_OK;
+}
+
+STDMETHODIMP DispatchHostTestInterface::put_WriteOnlyProperty(INT x) {
+  UNREFERENCED_PARAMETER(x);
+  return S_OK;
+}
+
+STDMETHODIMP DispatchHostTestInterface::AddAsMethod(INT a, INT b, INT* c) {
+  *c = a + b;
+  return S_OK;
+}
+
+STDMETHODIMP DispatchHostTestInterface::get_AddAsProperty(
+    INT a, INT b, INT* c) {
+  *c = a + b;
+  return S_OK;
+}
+
+STDMETHODIMP DispatchHostTestInterface::DidYouMeanRecursion(IDispatch** me) {
+  *me = this;
+  return S_OK;
+}
+
+STDMETHODIMP DispatchHostTestInterface2::get_Get(INT index, INT* x) {
+  *x = index << 1;
+  return S_OK;
+}
+
+}  // namespace omaha
diff --git a/plugins/update/npapi/testing/dispatch_host_test_interface.h b/plugins/update/npapi/testing/dispatch_host_test_interface.h
new file mode 100644
index 0000000..3bbef69
--- /dev/null
+++ b/plugins/update/npapi/testing/dispatch_host_test_interface.h
@@ -0,0 +1,131 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_DISPATCH_HOST_TEST_INTERFACE_H_
+#define OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_DISPATCH_HOST_TEST_INTERFACE_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include <atlstr.h>
+
+#include "base/basictypes.h"
+#include "omaha/base/app_util.h"
+#include "omaha/plugins/update/npapi/testing/resource.h"
+#include "plugins/update/npapi/testing/dispatch_host_test_idl.h"
+
+namespace omaha {
+
+// This class allows for using IDispatchImpl with a specific TypeLib, in a
+// module that has multiple TYPELIB resources. Ordinarily, IDispatchImpl will
+// only load the first TYPELIB resource in the module, which does not work for
+// omaha_unittest.exe. Hence the need for this class. This class loads the TLB
+// with the specified resource id, and uses that for subsequent IDispatch
+// requests.
+// TODO(omaha3): Perhaps move this class into a generic utility header.
+
+template <class T, const IID* piid = &__uuidof(T), const int tlb_res_id = 1>
+class ATL_NO_VTABLE IDispatchImplResId : public IDispatchImpl<T, piid> {
+ public:
+  IDispatchImplResId() {
+    CComPtr<ITypeLib> type_lib;
+    CString tlb_path;
+
+    // Format the path as "ModulePath\\ResourceId". Specifying a ResourceId
+    // allows overriding the default behavior of LoadTypeLib to load the first
+    // TYPELIB resource from the module.
+    tlb_path.Format(_T("%s\\%d"), app_util::GetCurrentModuleName(), tlb_res_id);
+
+    HRESULT hr = LoadTypeLib(tlb_path, &type_lib);
+    if (FAILED(hr)) {
+      return;
+    }
+
+    CComPtr<ITypeInfo> type_info;
+    hr = type_lib->GetTypeInfoOfGuid(*piid, &type_info);
+    if (FAILED(hr)) {
+      return;
+    }
+
+    CComPtr<ITypeInfo2> type_info2;
+    if (SUCCEEDED(type_info->QueryInterface(&type_info2))) {
+      type_info = type_info2;
+    }
+
+    // Override the ITypeInfo in the CComTypeInfoHolder, which will be used in
+    // subsequent calls to the IDispatch methods.
+    _tih.m_pInfo = type_info.Detach();
+  }
+
+  virtual ~IDispatchImplResId() {}
+};
+
+class ATL_NO_VTABLE DispatchHostTestInterface
+    : public CComObjectRootEx<CComObjectThreadModel>,
+      public IDispatchImplResId<IDispatchHostTestInterface,
+                                &__uuidof(IDispatchHostTestInterface),
+                                IDR_DISPATCH_HOST_TEST_TLB> {
+ public:
+  DispatchHostTestInterface() {}
+  virtual ~DispatchHostTestInterface() {}
+
+  DECLARE_NOT_AGGREGATABLE(DispatchHostTestInterface);
+
+  BEGIN_COM_MAP(DispatchHostTestInterface)
+    COM_INTERFACE_ENTRY(IDispatch)
+  END_COM_MAP()
+
+  // IDispatchHostTestInterface methods.
+  STDMETHOD(Random)(INT* x);
+
+  STDMETHOD(get_Property)(INT* x);
+  STDMETHOD(put_Property)(INT x);
+
+  STDMETHOD(get_ReadOnlyProperty)(INT* x);
+  STDMETHOD(put_WriteOnlyProperty)(INT x);
+
+  STDMETHOD(AddAsMethod)(INT a, INT b, INT* c);
+  STDMETHOD(get_AddAsProperty)(INT a, INT b, INT* c);
+
+  STDMETHOD(DidYouMeanRecursion)(IDispatch** me);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DispatchHostTestInterface);
+};
+
+class ATL_NO_VTABLE DispatchHostTestInterface2
+    : public CComObjectRootEx<CComObjectThreadModel>,
+      public IDispatchImplResId<IDispatchHostTestInterface2,
+                                &__uuidof(IDispatchHostTestInterface2),
+                                IDR_DISPATCH_HOST_TEST_TLB> {
+ public:
+  DispatchHostTestInterface2() {}
+  virtual ~DispatchHostTestInterface2() {}
+
+  DECLARE_NOT_AGGREGATABLE(DispatchHostTestInterface2);
+
+  BEGIN_COM_MAP(DispatchHostTestInterface)
+    COM_INTERFACE_ENTRY(IDispatch)
+  END_COM_MAP()
+
+  // IDispatchHostTestInterface2 methods.
+  STDMETHOD(get_Get)(INT index, INT* x);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DispatchHostTestInterface2);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_DISPATCH_HOST_TEST_INTERFACE_H_
diff --git a/plugins/update/npapi/testing/resource.h b/plugins/update/npapi/testing/resource.h
new file mode 100644
index 0000000..6bfedb3
--- /dev/null
+++ b/plugins/update/npapi/testing/resource.h
@@ -0,0 +1,21 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_RESOURCE_H__
+#define OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_RESOURCE_H__
+
+#define IDR_DISPATCH_HOST_TEST_TLB                  3
+
+#endif  // OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_RESOURCE_H__
diff --git a/plugins/update/npapi/testing/stubs.cc b/plugins/update/npapi/testing/stubs.cc
new file mode 100644
index 0000000..30921cc
--- /dev/null
+++ b/plugins/update/npapi/testing/stubs.cc
@@ -0,0 +1,142 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/plugins/update/npapi/testing/stubs.h"
+#include <malloc.h>
+#include <string.h>
+#include "base/debug.h"
+
+namespace omaha {
+
+NPIdentifierFactory::NPIdentifierFactory() {
+}
+
+NPIdentifierFactory::~NPIdentifierFactory() {
+  for (std::vector<NPIdentifier>::const_iterator it = identifiers_.begin();
+       it != identifiers_.end(); ++it) {
+    free(*it);
+  }
+}
+
+NPIdentifier NPIdentifierFactory::Create(const char* name) {
+  NPIdentifier identifier = _strdup(name);
+  identifiers_.push_back(identifier);
+  return identifier;
+}
+
+}  // namespace omaha
+
+extern "C" {
+void* NPN_MemAlloc(uint32 size) {
+  return malloc(size);
+}
+
+void NPN_MemFree(void* ptr) {
+  free(ptr);
+}
+
+NPUTF8* NPN_UTF8FromIdentifier(NPIdentifier identifier) {
+  return _strdup(static_cast<char*>(identifier));
+}
+
+NPObject* NPN_CreateObject(NPP npp, NPClass* class_vtable) {
+  UNREFERENCED_PARAMETER(npp);
+  ASSERT1(class_vtable);
+  NPObject* object = class_vtable->allocate(npp, class_vtable);
+  object->_class = class_vtable;
+  object->referenceCount = 1;
+  return object;
+}
+
+NPObject* NPN_RetainObject(NPObject* object) {
+  ASSERT1(object);
+  ++object->referenceCount;
+  return object;
+}
+
+void NPN_ReleaseObject(NPObject* object) {
+  ASSERT1(object);
+  ASSERT1(object->referenceCount > 0);
+  if (--object->referenceCount == 0) {
+    object->_class->deallocate(object);
+  }
+}
+
+void NPN_ReleaseVariantValue(NPVariant* variant) {
+  if (NPVARIANT_IS_STRING(*variant)) {
+    NPN_MemFree(const_cast<NPUTF8*>(variant->value.stringValue.UTF8Characters));
+  } else if (NPVARIANT_IS_OBJECT(*variant)) {
+    NPN_ReleaseObject(variant->value.objectValue);
+  }
+  VOID_TO_NPVARIANT(*variant);
+  return;
+}
+
+bool NPN_HasMethod(NPP npp, NPObject* object, NPIdentifier name) {
+  UNREFERENCED_PARAMETER(npp);
+  return object->_class->hasMethod(object, name);
+}
+
+bool NPN_Invoke(NPP npp, NPObject* object, NPIdentifier name,
+                const NPVariant* args, uint32_t arg_count, NPVariant* result) {
+  UNREFERENCED_PARAMETER(npp);
+  return object->_class->invoke(object, name, args, arg_count, result);
+}
+
+bool NPN_InvokeDefault(NPP npp, NPObject* object, const NPVariant* args,
+                       uint32_t arg_count, NPVariant* result) {
+  UNREFERENCED_PARAMETER(npp);
+  return object->_class->invokeDefault(object, args, arg_count, result);
+}
+
+bool NPN_HasProperty(NPP npp, NPObject* object, NPIdentifier name) {
+  UNREFERENCED_PARAMETER(npp);
+  return object->_class->hasProperty(object, name);
+}
+
+bool NPN_GetProperty(NPP npp, NPObject* object, NPIdentifier name,
+                     NPVariant* result) {
+  UNREFERENCED_PARAMETER(npp);
+  return object->_class->getProperty(object, name, result);
+}
+
+bool NPN_SetProperty(NPP npp, NPObject* object, NPIdentifier name,
+                     const NPVariant* value) {
+  UNREFERENCED_PARAMETER(npp);
+  return object->_class->setProperty(object, name, value);
+}
+
+bool NPN_RemoveProperty(NPP npp, NPObject* object, NPIdentifier name) {
+  UNREFERENCED_PARAMETER(npp);
+  return object->_class->removeProperty(object, name);
+}
+
+bool NPN_Enumerate(NPP npp, NPObject* object, NPIdentifier** names,
+                   uint32_t* count) {
+  UNREFERENCED_PARAMETER(npp);
+  return object->_class->enumerate(object, names, count);
+}
+
+bool NPN_Construct(NPP npp, NPObject* object, const NPVariant* args,
+                   uint32_t arg_count, NPVariant* result) {
+  UNREFERENCED_PARAMETER(npp);
+  return object->_class->construct(object, args, arg_count, result);
+}
+
+void NPN_SetException(NPObject* object, const NPUTF8* message) {
+  UNREFERENCED_PARAMETER(object);
+  UNREFERENCED_PARAMETER(message);
+}
+}  // extern "C"
diff --git a/plugins/update/npapi/testing/stubs.h b/plugins/update/npapi/testing/stubs.h
new file mode 100644
index 0000000..76ba6b3
--- /dev/null
+++ b/plugins/update/npapi/testing/stubs.h
@@ -0,0 +1,72 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Normally, implementations of these functions are provided by the NPAPI
+// runtime. These stub implementations are intended only for use in unit tests.
+
+#ifndef OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_STUBS_H_
+#define OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_STUBS_H_
+
+#include <vector>
+#include "third_party/npapi/bindings/nphostapi.h"
+
+namespace omaha {
+
+// Not part of NPAPI proper, but useful nonetheless. Note that the stub
+// implementation of NPIdentifier does not conform to NPAPI's idea of what an
+// NPIdentifier ought to be: specifically, uniqueness is not guaranteed.
+class NPIdentifierFactory {
+ public:
+  NPIdentifierFactory();
+  ~NPIdentifierFactory();
+  NPIdentifier Create(const char* name);
+
+ private:
+  std::vector<NPIdentifier> identifiers_;
+
+  DISALLOW_COPY_AND_ASSIGN(NPIdentifierFactory);
+};
+
+}  // namespace omaha
+
+extern "C" {
+void* NPN_MemAlloc(uint32 size);
+void NPN_MemFree(void* ptr);
+NPUTF8* NPN_UTF8FromIdentifier(NPIdentifier identifier);
+NPObject* NPN_CreateObject(NPP npp, NPClass* class_vtable);
+NPObject* NPN_RetainObject(NPObject* object);
+void NPN_ReleaseObject(NPObject* object);
+void NPN_ReleaseVariantValue(NPVariant* variant);
+
+bool NPN_HasMethod(NPP npp, NPObject* object, NPIdentifier name);
+bool NPN_Invoke(NPP npp, NPObject* object, NPIdentifier name,
+                const NPVariant* args, uint32_t arg_count, NPVariant* result);
+bool NPN_InvokeDefault(NPP npp, NPObject* object, const NPVariant* args,
+                       uint32_t arg_count, NPVariant* result);
+bool NPN_HasProperty(NPP npp, NPObject* object, NPIdentifier name);
+bool NPN_GetProperty(NPP npp, NPObject* object, NPIdentifier name,
+                     NPVariant* result);
+bool NPN_SetProperty(NPP npp, NPObject* object, NPIdentifier name,
+                     const NPVariant* value);
+bool NPN_RemoveProperty(NPP npp, NPObject* object, NPIdentifier name);
+bool NPN_Enumerate(NPP npp, NPObject* object, NPIdentifier** names,
+                   uint32_t* count);
+bool NPN_Construct(NPP npp, NPObject* object, const NPVariant* args,
+                   uint32_t arg_count, NPVariant* result);
+void NPN_SetException(NPObject* object, const NPUTF8* message);
+}  // extern "C"
+
+#endif  // OMAHA_PLUGINS_UPDATE_NPAPI_TESTING_STUBS_H_
+
diff --git a/plugins/update/npapi/urlpropbag.cc b/plugins/update/npapi/urlpropbag.cc
new file mode 100644
index 0000000..940a9b4
--- /dev/null
+++ b/plugins/update/npapi/urlpropbag.cc
@@ -0,0 +1,76 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/plugins/update/npapi/urlpropbag.h"
+
+#include "omaha/base/debug.h"
+#include "omaha/base/utils.h"
+#include "omaha/plugins/update/npapi/variant_utils.h"
+
+namespace omaha {
+
+typedef CComObject<UrlPropertyBag> CComUrlBag;
+typedef scoped_any<CComUrlBag*, close_release_com, null_t> scoped_bag;
+
+HRESULT UrlPropertyBag::Create(const TCHAR* url, IPropertyBag** pb) {
+  ASSERT1(url);
+  ASSERT1(pb);
+
+  if (!url || !pb) {
+    return E_INVALIDARG;
+  }
+
+  // Create the host and hand off the string to it.
+  scoped_bag comobj;
+  HRESULT hr = CComUrlBag::CreateInstance(address(comobj));
+  if (FAILED(hr)) {
+    return hr;
+  }
+  get(comobj)->AddRef();
+
+  comobj->url_ = url;
+
+  return comobj->QueryInterface(pb);
+}
+
+STDMETHODIMP UrlPropertyBag::Read(LPCOLESTR pszPropName, VARIANT* pVar,
+                                  IErrorLog* pErrorLog) {
+  UNREFERENCED_PARAMETER(pErrorLog);
+
+  ASSERT1(pszPropName);
+  ASSERT1(pVar);
+  if (!pszPropName || !pVar) {
+    return E_POINTER;
+  }
+
+  if (0 == _tcscmp(pszPropName, kUrlPropertyBag_Url)) {
+    V_VT(pVar) = VT_BSTR;
+    V_BSTR(pVar) = url_.AllocSysString();
+    return S_OK;
+  }
+
+  return E_INVALIDARG;
+}
+
+STDMETHODIMP UrlPropertyBag::Write(LPCOLESTR pszPropName, VARIANT* pVar) {
+  UNREFERENCED_PARAMETER(pszPropName);
+  UNREFERENCED_PARAMETER(pVar);
+  return E_FAIL;
+}
+
+UrlPropertyBag::UrlPropertyBag() {}
+
+}  // namespace omaha
+
diff --git a/plugins/update/npapi/urlpropbag.h b/plugins/update/npapi/urlpropbag.h
new file mode 100644
index 0000000..0090bb2
--- /dev/null
+++ b/plugins/update/npapi/urlpropbag.h
@@ -0,0 +1,52 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_PLUGINS_UPDATE_NPAPI_URLPROPBAG_H_
+#define OMAHA_PLUGINS_UPDATE_NPAPI_URLPROPBAG_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+namespace omaha {
+
+const TCHAR* const kUrlPropertyBag_Url = _T("omaha-urlpropertybag-url");
+
+class ATL_NO_VTABLE UrlPropertyBag
+    : public CComObjectRootEx<CComObjectThreadModel>,
+      public IPropertyBag {
+ public:
+  static HRESULT Create(const TCHAR* url, IPropertyBag** pb);
+
+  BEGIN_COM_MAP(UrlPropertyBag)
+    COM_INTERFACE_ENTRY(IPropertyBag)
+  END_COM_MAP()
+
+  // IPropertyBag methods.
+  STDMETHOD(Read)(LPCOLESTR pszPropName, VARIANT* pVar, IErrorLog* pErrorLog);
+  STDMETHOD(Write)(LPCOLESTR pszPropName, VARIANT* pVar);
+
+ protected:
+  UrlPropertyBag();
+  virtual ~UrlPropertyBag() {}
+
+ private:
+  CString url_;
+
+  DISALLOW_COPY_AND_ASSIGN(UrlPropertyBag);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_PLUGINS_UPDATE_NPAPI_URLPROPBAG_H_
diff --git a/plugins/update/npapi/variant_utils.cc b/plugins/update/npapi/variant_utils.cc
new file mode 100644
index 0000000..125d18b
--- /dev/null
+++ b/plugins/update/npapi/variant_utils.cc
@@ -0,0 +1,134 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// TODO(omaha): verify that the new operator throws on failure to prevent NULL
+// pointer exploits.
+
+#include "omaha/plugins/update/npapi/variant_utils.h"
+#include <atlstr.h>
+#include "base/debug.h"
+#include "base/string.h"
+#include "omaha/plugins/update/npapi/dispatch_host.h"
+#include "omaha/plugins/update/npapi/npfunction_host.h"
+
+namespace omaha {
+
+void NPVariantToVariant(NPP npp,
+                        const NPVariant& source,
+                        VARIANT* destination) {
+  ASSERT1(destination);
+  V_VT(destination) = VT_EMPTY;
+
+  switch (source.type) {
+    case NPVariantType_Void:
+      V_VT(destination) = VT_EMPTY;
+      break;
+    case NPVariantType_Null:
+      V_VT(destination) = VT_NULL;
+      break;
+    case NPVariantType_Bool:
+      V_VT(destination) = VT_BOOL;
+      V_BOOL(destination) = source.value.boolValue ? VARIANT_TRUE
+                                                   : VARIANT_FALSE;
+      break;
+    case NPVariantType_Int32:
+      V_VT(destination) = VT_I4;
+      V_I4(destination) = source.value.intValue;
+      break;
+    case NPVariantType_Double:
+      V_VT(destination) = VT_R8;
+      V_R8(destination) = source.value.doubleValue;
+      break;
+    case NPVariantType_String:
+      V_VT(destination) = VT_BSTR;
+      if (source.value.stringValue.UTF8Length) {
+        CString string = Utf8ToWideChar(source.value.stringValue.UTF8Characters,
+                                        source.value.stringValue.UTF8Length);
+        V_BSTR(destination) = string.AllocSysString();
+      } else {
+        V_BSTR(destination) = CString().AllocSysString();
+      }
+      break;
+    case NPVariantType_Object:
+      V_VT(destination) = VT_DISPATCH;
+      if (source.value.objectValue) {
+        NpFunctionHost::Create(npp, source.value.objectValue,
+                               &V_DISPATCH(destination));
+      } else {
+        V_DISPATCH(destination) = NULL;
+      }
+      break;
+    default:
+      ASSERT1(false);
+      break;
+  }
+}
+
+void VariantToNPVariant(NPP npp,
+                        const VARIANT& source,
+                        NPVariant* destination) {
+  ASSERT1(destination);
+  VOID_TO_NPVARIANT(*destination);
+
+  switch (V_VT(&source)) {
+    case VT_EMPTY:
+      VOID_TO_NPVARIANT(*destination);
+      break;
+    case VT_NULL:
+      NULL_TO_NPVARIANT(*destination);
+      break;
+    case VT_BOOL:
+      BOOLEAN_TO_NPVARIANT(V_BOOL(&source), *destination);
+      break;
+    case VT_I4:
+      INT32_TO_NPVARIANT(V_I4(&source), *destination);
+      break;
+    case VT_UI4:
+      INT32_TO_NPVARIANT(V_UI4(&source), *destination);
+      break;
+    case VT_R8:
+      DOUBLE_TO_NPVARIANT(V_R8(&source), *destination);
+      break;
+    case VT_BSTR:
+      if (V_BSTR(&source)) {
+        int source_length = ::SysStringLen(V_BSTR(&source)) + 1;
+        int buffer_length = ::WideCharToMultiByte(CP_UTF8, 0, V_BSTR(&source),
+                                                  source_length, NULL, 0, NULL,
+                                                  NULL);
+        char* buffer = static_cast<char*>(NPN_MemAlloc(buffer_length));
+        VERIFY1(::WideCharToMultiByte(CP_UTF8, 0, V_BSTR(&source),
+                                      source_length, buffer, buffer_length,
+                                      NULL, NULL) > 0);
+        STRINGN_TO_NPVARIANT(buffer, buffer_length - 1, *destination);
+      } else {
+        char* buffer = static_cast<char*>(NPN_MemAlloc(1));
+        buffer[0] = '\0';
+        STRINGN_TO_NPVARIANT(buffer, 0, *destination);
+      }
+      break;
+    case VT_DISPATCH:
+      if (V_DISPATCH(&source)) {
+        OBJECT_TO_NPVARIANT(
+            DispatchHost::CreateInstance(npp, V_DISPATCH(&source)),
+            *destination);
+      }
+      break;
+    default:
+      ASSERT(false, (L"Unhandled variant type %d", V_VT(&source)));
+      break;
+  }
+}
+
+}  // namespace omaha
diff --git a/plugins/update/npapi/variant_utils.h b/plugins/update/npapi/variant_utils.h
new file mode 100644
index 0000000..d283926
--- /dev/null
+++ b/plugins/update/npapi/variant_utils.h
@@ -0,0 +1,48 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Converts between NPVariant and VARIANT types.
+// The following two-way conversions are supported:
+// NPVariantType_Void <-> VT_EMPTY
+// NPVariantType_Null <-> VT_NULL
+// NPVariantType_Bool <-> VT_BOOL
+// NPVariantType_Int32 <-> VT_I4
+// NPVariantType_Double <-> VT_R8
+// NPVariantType_String <-> VT_BSTR
+//
+// Furthermore, the following one-way conversions are supported:
+// VT_UI4 -> NPVariantType_Int32
+// VT_DISPATCH -> NPVariantType_Object (DispatchHost)
+// NPVariantType_Object -> VT_DISPATCH (NpFunctionHost)
+
+#ifndef OMAHA_PLUGINS_UPDATE_NPAPI_VARIANT_UTILS_H_
+#define OMAHA_PLUGINS_UPDATE_NPAPI_VARIANT_UTILS_H_
+
+#include <oaidl.h>
+#include "third_party/npapi/bindings/nphostapi.h"
+
+namespace omaha {
+
+void NPVariantToVariant(NPP npp,
+                        const NPVariant& source,
+                        VARIANT* destination);
+
+void VariantToNPVariant(NPP npp,
+                        const VARIANT& source,
+                        NPVariant* destination);
+
+}  // namespace omaha
+
+#endif  // OMAHA_PLUGINS_UPDATE_NPAPI_VARIANT_UTILS_H_
diff --git a/plugins/update/npapi/variant_utils_unittest.cc b/plugins/update/npapi/variant_utils_unittest.cc
new file mode 100644
index 0000000..eacc04d
--- /dev/null
+++ b/plugins/update/npapi/variant_utils_unittest.cc
@@ -0,0 +1,320 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/plugins/update/npapi/variant_utils.h"
+#include <atlbase.h>
+#include <string.h>
+#include <stdlib.h>
+#include "omaha/plugins/update/npapi/testing/dispatch_host_test_interface.h"
+#include "omaha/plugins/update/npapi/testing/stubs.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+class VariantUtilsTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    VOID_TO_NPVARIANT(np_variant_);
+  }
+
+  virtual void TearDown() {
+    NPN_ReleaseVariantValue(&np_variant_);
+  }
+
+  void TestNPV2V() {
+    NPVariantToVariant(NULL, np_variant_, &variant_);
+    ExpectTypesEquivalent();
+  }
+
+  void TestV2NPV() {
+    NPN_ReleaseVariantValue(&np_variant_);
+    VariantToNPVariant(NULL, variant_, &np_variant_);
+    ExpectTypesEquivalent();
+  }
+
+  void ExpectTypesEquivalent() {
+    switch (V_VT(&variant_)) {
+      case VT_EMPTY:
+        EXPECT_TRUE(NPVARIANT_IS_VOID(np_variant_));
+        return;
+        break;
+      case VT_NULL:
+        EXPECT_TRUE(NPVARIANT_IS_NULL(np_variant_));
+        return;
+        break;
+      case VT_BOOL:
+        EXPECT_TRUE(NPVARIANT_IS_BOOLEAN(np_variant_));
+        return;
+        break;
+      case VT_I4:
+      case VT_UI4:
+        EXPECT_TRUE(NPVARIANT_IS_INT32(np_variant_));
+        return;
+        break;
+      case VT_R8:
+        EXPECT_TRUE(NPVARIANT_IS_DOUBLE(np_variant_));
+        return;
+        break;
+      case VT_BSTR:
+        EXPECT_TRUE(NPVARIANT_IS_STRING(np_variant_));
+        return;
+        break;
+      case VT_DISPATCH:
+        EXPECT_TRUE(NPVARIANT_IS_OBJECT(np_variant_));
+        return;
+        break;
+      default:
+        break;
+    }
+    ASSERT(false, (L"Expected equivalent types but got the following instead:\n"
+                   L"  np_variant_.type -> %d\n  V_VT(&variant_) -> %d\n",
+                   np_variant_.type, V_VT(&variant_)));
+  }
+
+  NPVariant np_variant_;
+  CComVariant variant_;
+};
+
+TEST_F(VariantUtilsTest, NPVariantToVariant_Void) {
+  VOID_TO_NPVARIANT(np_variant_);
+  TestNPV2V();
+}
+
+TEST_F(VariantUtilsTest, NPVariantToVariant_Null) {
+  NULL_TO_NPVARIANT(np_variant_);
+  TestNPV2V();
+}
+
+TEST_F(VariantUtilsTest, NPVariantToVariant_Bool) {
+  BOOLEAN_TO_NPVARIANT(true, np_variant_);
+  TestNPV2V();
+  EXPECT_EQ(VARIANT_TRUE, V_BOOL(&variant_));
+
+  BOOLEAN_TO_NPVARIANT(false, np_variant_);
+  TestNPV2V();
+  EXPECT_EQ(VARIANT_FALSE, V_BOOL(&variant_));
+}
+
+TEST_F(VariantUtilsTest, NPVariantToVariant_Int32) {
+  INT32_TO_NPVARIANT(kint32min, np_variant_);
+  TestNPV2V();
+  EXPECT_EQ(kint32min, V_I4(&variant_));
+
+  INT32_TO_NPVARIANT(0, np_variant_);
+  TestNPV2V();
+  EXPECT_EQ(0, V_I4(&variant_));
+
+  INT32_TO_NPVARIANT(kint32max, np_variant_);
+  TestNPV2V();
+  EXPECT_EQ(kint32max, V_I4(&variant_));
+}
+
+TEST_F(VariantUtilsTest, NPVariantToVariant_Double) {
+  DOUBLE_TO_NPVARIANT(-1, np_variant_);
+  TestNPV2V();
+  EXPECT_DOUBLE_EQ(-1, V_R8(&variant_));
+
+  DOUBLE_TO_NPVARIANT(0, np_variant_);
+  TestNPV2V();
+  EXPECT_DOUBLE_EQ(0, V_R8(&variant_));
+
+  DOUBLE_TO_NPVARIANT(static_cast<double>(kuint64max),
+                      np_variant_);
+  TestNPV2V();
+  EXPECT_DOUBLE_EQ(static_cast<double>(kuint64max), V_R8(&variant_));
+}
+
+TEST_F(VariantUtilsTest, NPVariantToVariant_String) {
+  // TODO(omaha): _strdup depends on an implementation detail of the stubs.
+  STRINGZ_TO_NPVARIANT(_strdup(""), np_variant_);
+  TestNPV2V();
+  EXPECT_STREQ(L"", V_BSTR(&variant_));
+
+  // Force the length to be zero.
+  STRINGZ_TO_NPVARIANT(_strdup("junk"), np_variant_);
+  np_variant_.value.stringValue.UTF8Length = 0;
+  TestNPV2V();
+  EXPECT_STREQ(L"", V_BSTR(&variant_));
+
+  STRINGZ_TO_NPVARIANT(_strdup("ROBERT'); DROP TABLE Students; --"),
+                       np_variant_);
+  TestNPV2V();
+  EXPECT_STREQ(L"ROBERT'); DROP TABLE Students; --", V_BSTR(&variant_));
+
+  // Check that NPVariantToVariant properly converts UTF-8 to UTF-16.
+  STRINGZ_TO_NPVARIANT(_strdup("one: \xe4\xb8\x80"), np_variant_);
+  TestNPV2V();
+  EXPECT_STREQ(L"one: \x4e00", V_BSTR(&variant_));
+}
+/*
+TEST_F(VariantUtilsTest, NPVariantToVariant_Unsupported) {
+  // NPVariantType_Object -> VT_DISPATCH conversion is not supported.
+  ExpectAsserts expect_asserts;
+  OBJECT_TO_NPVARIANT(NULL, np_variant_);
+  variant_ = 24;
+  NPVariantToVariant(NULL, np_variant_, &variant_);
+  EXPECT_EQ(VT_EMPTY, V_VT(&variant_));
+  // Manual cleanup, since OBJECT_TO_NPVARIANT macro was used with a NULL
+  // NPObject, which is normally illegal.
+  VOID_TO_NPVARIANT(np_variant_);
+}
+*/
+TEST_F(VariantUtilsTest, VariantToNPVariant_VT_EMPTY) {
+  variant_.ChangeType(VT_EMPTY);
+  TestV2NPV();
+}
+
+TEST_F(VariantUtilsTest, VariantToNPVariant_VT_NULL) {
+  variant_.ChangeType(VT_NULL);
+  TestV2NPV();
+}
+
+TEST_F(VariantUtilsTest, VariantToNPVariant_VT_BOOL) {
+  variant_ = true;
+  TestV2NPV();
+  EXPECT_TRUE(np_variant_.value.boolValue);
+
+  variant_ = false;
+  TestV2NPV();
+  EXPECT_FALSE(np_variant_.value.boolValue);
+}
+
+TEST_F(VariantUtilsTest, VariantToNPVariant_VT_I4) {
+  variant_ = kint32max;
+  TestV2NPV();
+  EXPECT_EQ(kint32max, np_variant_.value.intValue);
+
+  variant_ = 0;
+  TestV2NPV();
+  EXPECT_EQ(0, np_variant_.value.intValue);
+
+  variant_ = kint32min;
+  TestV2NPV();
+  EXPECT_EQ(kint32min, np_variant_.value.intValue);
+}
+
+TEST_F(VariantUtilsTest, VariantToNPVariant_VT_UI4) {
+  variant_ = 0U;
+  TestV2NPV();
+  EXPECT_EQ(0, np_variant_.value.intValue);
+
+  variant_ = static_cast<uint32>(kint32max);
+  TestV2NPV();
+  EXPECT_EQ(kint32max, np_variant_.value.intValue);
+
+  // MSIE can natively support VT_UI4. Unfortunately, Firefox cannot.
+  // Check that kuint32max wraps around to -1.
+  variant_ = kuint32max;
+  TestV2NPV();
+  EXPECT_EQ(-1, np_variant_.value.intValue);
+}
+
+TEST_F(VariantUtilsTest, VariantToNPVariant_VT_R8) {
+  variant_ = 0.0;
+  TestV2NPV();
+  EXPECT_DOUBLE_EQ(0.0, np_variant_.value.doubleValue);
+
+  variant_ = -1.0;
+  TestV2NPV();
+  EXPECT_DOUBLE_EQ(-1.0, np_variant_.value.doubleValue);
+
+  variant_ = static_cast<double>(kuint64max);
+  TestV2NPV();
+  EXPECT_DOUBLE_EQ(static_cast<double>(kuint64max),
+                   np_variant_.value.doubleValue);
+}
+
+TEST_F(VariantUtilsTest, VariantToNPVariant_VT_BSTR) {
+  variant_ = "";
+  TestV2NPV();
+  EXPECT_STREQ("", np_variant_.value.stringValue.UTF8Characters);
+
+  variant_ = L"sudo make me a sandwich";
+  TestV2NPV();
+  EXPECT_STREQ("sudo make me a sandwich",
+               np_variant_.value.stringValue.UTF8Characters);
+
+  // A NULL BSTR should be treated as an empty string.
+  V_VT(&variant_) = VT_BSTR;
+  V_BSTR(&variant_) = NULL;
+  TestV2NPV();
+  EXPECT_STREQ("", np_variant_.value.stringValue.UTF8Characters);
+
+  // Check that VariantToNPVariant properly converts UTF-16 to UTF-8.
+  variant_ = L"one: \x4e00";
+  TestV2NPV();
+  EXPECT_STREQ("one: \xe4\xb8\x80",
+               np_variant_.value.stringValue.UTF8Characters);
+}
+
+TEST_F(VariantUtilsTest, VariantToNPVariant_VT_DISPATCH) {
+  CComPtr<IDispatch> dispatch;
+  ASSERT_SUCCEEDED(
+      CComCoClass<DispatchHostTestInterface>::CreateInstance(&dispatch));
+  variant_ = dispatch;
+  TestV2NPV();
+
+  // Check that the wrapped object's methods can be called.
+  NPIdentifierFactory identifier;
+  NPVariant result;
+  VOID_TO_NPVARIANT(result);
+  EXPECT_TRUE(NPN_Invoke(NULL, np_variant_.value.objectValue,
+                         identifier.Create("Random"), NULL, 0, &result));
+  EXPECT_TRUE(NPVARIANT_IS_INT32(result));
+  EXPECT_EQ(42, result.value.intValue);
+}
+
+TEST_F(VariantUtilsTest, VariantToNPVariant_Unsupported) {
+  // Legal variant types inferred from oaidl.idl and wtypes.h. Note that some
+  // types that aren't marked as appearing in VARIANTs still have a VARIANT
+  // field for that type, so they are included anyway...
+
+  // Note that VT_UNKNOWN must not be the last element in the array; otherwise
+  // CComVariant will attempt to call Release() on a NULL pointer.
+  const VARTYPE kUnsupportedSimpleTypes[] = {
+    VT_I2, VT_R4, VT_CY, VT_DATE, VT_ERROR, VT_VARIANT, VT_UNKNOWN, VT_DECIMAL,
+    VT_RECORD, VT_I1, VT_UI1, VT_UI2, VT_I8, VT_UI8, VT_INT, VT_UINT, VT_BYREF
+  };
+  for (int i = 0; i < arraysize(kUnsupportedSimpleTypes); ++i) {
+    ExpectAsserts expect_asserts;
+    V_VT(&variant_) = kUnsupportedSimpleTypes[i];
+    INT32_TO_NPVARIANT(42, np_variant_);
+    TestV2NPV();
+    EXPECT_TRUE(NPVARIANT_IS_VOID(np_variant_));
+  }
+
+  // Compound modifiers.
+  const VARTYPE kCompoundModifiers[] = {
+    VT_ARRAY, VT_BYREF, VT_ARRAY | VT_BYREF
+  };
+  // Compound types.
+  const VARTYPE kCompoundableTypes[] = {
+    VT_I2, VT_I4, VT_R4, VT_R8, VT_CY, VT_DATE, VT_BSTR, VT_DISPATCH, VT_ERROR,
+    VT_BOOL, VT_VARIANT, VT_UNKNOWN, VT_DECIMAL, VT_RECORD, VT_I1, VT_UI1,
+    VT_UI2, VT_UI4, VT_I8, VT_UI8, VT_INT, VT_UINT
+  };
+
+  for (int i = 0; i < arraysize(kCompoundModifiers); ++i) {
+    for (int j = 0; j < arraysize(kCompoundableTypes); ++j) {
+      ExpectAsserts expect_asserts;
+      V_VT(&variant_) = kCompoundModifiers[i] | kCompoundableTypes[j];
+      INT32_TO_NPVARIANT(42, np_variant_);
+      TestV2NPV();
+      EXPECT_TRUE(NPVARIANT_IS_VOID(np_variant_));
+    }
+  }
+}
+
+}  // namespace omaha
diff --git a/plugins/update/omaha_customization_update_apis_unittest.cc b/plugins/update/omaha_customization_update_apis_unittest.cc
new file mode 100644
index 0000000..11e7749
--- /dev/null
+++ b/plugins/update/omaha_customization_update_apis_unittest.cc
@@ -0,0 +1,108 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Tests the constants that vary depending on the customization of Omaha.
+// The test checks for the Google Update variations, but can be modified for
+// your purposes.
+
+#include <windows.h>
+#include <tchar.h>
+#include <atlbase.h>
+#include <oleauto.h>
+#include "omaha/base/utils.h"
+#include "plugins/update/activex/update_control_idl.h"
+#include "omaha/testing/omaha_customization_test.h"
+
+// Most of the tests are intentionally not using the omaha namespace. Most of
+// the values being tested are not in this namespace, and being in the global
+// namespace is required by TEST_GU_INT_F to catch conflicts with Google types
+// when building non-Google versions.
+
+class OmahaCustomizationUpdateComInterfaceTest
+    : public OmahaCustomizationTypeLibComInterfaceTest {
+ protected:
+  OmahaCustomizationUpdateComInterfaceTest()
+      : OmahaCustomizationTypeLibComInterfaceTest(UPDATE_PLUGIN_FILENAME) {
+  }
+};
+
+TEST_F(OmahaCustomizationUpdateComInterfaceTest, TypeLib) {
+  EXPECT_GU_ID_EQ(_T("{b627c883-e979-4873-80b3-ddd0b658b56a}"),
+                  LIBID_GoogleUpdateControlLib);
+
+  EXPECT_SUCCEEDED(GetDocumentation(-1));
+  EXPECT_STREQ(_T("GoogleUpdateControlLib"), item_name_);
+  EXPECT_GU_STREQ(_T("Google Update Browser Plugins 3.0 Type Library"),
+                  item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationUpdateComInterfaceTest,
+              IGoogleUpdateOneClick) {
+  // TODO(omaha): Test uuid constants after extracting from IDLs.
+  EXPECT_GU_ID_EQ(_T("{6F65D62B-2F32-4483-9028-176C30B2389D}"),
+                  __uuidof(IGoogleUpdateOneClick));
+
+  EXPECT_SUCCEEDED(GetDocumentation(0));
+  EXPECT_STREQ(_T("IGoogleUpdateOneClick"), item_name_);
+  EXPECT_STREQ(_T("Google Update OneClick Control"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationUpdateComInterfaceTest,
+              IGoogleUpdate3WebControl) {
+  // TODO(omaha): Test uuid constants after extracting from IDLs.
+  EXPECT_GU_ID_EQ(_T("{57E37502-65A5-484a-A035-C1608B2626EA}"),
+                  __uuidof(IGoogleUpdate3WebControl));
+
+  EXPECT_SUCCEEDED(GetDocumentation(1));
+  EXPECT_STREQ(_T("IGoogleUpdate3WebControl"), item_name_);
+  EXPECT_STREQ(_T("GoogleUpdate3Web Control"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationUpdateComInterfaceTest,
+              GoogleUpdateOneClickControlCoClass) {
+  EXPECT_GU_ID_EQ(_T("{c442ac41-9200-4770-8cc0-7cdb4f245c55}"),
+                  __uuidof(GoogleUpdateOneClickControlCoClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(2));
+  EXPECT_STREQ(_T("GoogleUpdateOneClickControlCoClass"), item_name_);
+  EXPECT_STREQ(_T("Google Update OneClick Control Class"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+TEST_GU_INT_F(OmahaCustomizationUpdateComInterfaceTest,
+              GoogleUpdate3WebControlCoClass) {
+  EXPECT_GU_ID_EQ(_T("{c3101a8b-0ee1-4612-bfe9-41ffc1a3c19d}"),
+                  __uuidof(GoogleUpdate3WebControlCoClass));
+
+  EXPECT_SUCCEEDED(GetDocumentation(3));
+  EXPECT_STREQ(_T("GoogleUpdate3WebControlCoClass"), item_name_);
+  EXPECT_STREQ(_T("GoogleUpdate3Web Control Class"), item_doc_string_);
+  EXPECT_EQ(0, help_context_);
+  EXPECT_TRUE(!help_file_);
+}
+
+// Verifies there are no new interfaces in the TypeLib.
+TEST_F(OmahaCustomizationUpdateComInterfaceTest, VerifyNoNewInterfaces) {
+  EXPECT_EQ(TYPE_E_ELEMENTNOTFOUND, GetDocumentation(4))
+      << _T("A new interface may have been added. If so, roll ")
+      << _T("the plugin version and add test(s) for new interface(s).");
+}
diff --git a/plugins/update/omaha_customization_update_unittest.cc b/plugins/update/omaha_customization_update_unittest.cc
new file mode 100644
index 0000000..fe7fd01
--- /dev/null
+++ b/plugins/update/omaha_customization_update_unittest.cc
@@ -0,0 +1,42 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Tests the constants that vary depending on the customization of Omaha.
+// The test checks for the Google Update variations, but can be modified for
+// your purposes.
+
+#include "omaha/plugins/update/config.h"
+#include "omaha/testing/omaha_customization_test.h"
+
+namespace omaha {
+
+TEST(OmahaCustomizationUpdateTest, Constants_BuildFiles) {
+// The plugin version may or may not match in non-Google Update builds.
+#ifdef GOOGLE_UPDATE_BUILD
+  EXPECT_STREQ(_T("3"), kUpdate3WebPluginVersion);
+  EXPECT_STREQ(_T("9"), kOneclickPluginVersion);
+#else
+  std::wcout << _T("Did not test kPluginVersions.") << std::endl;
+#endif
+
+  EXPECT_GU_STREQ(
+      _T("Google.Update3WebControl.") _T(UPDATE_PLUGIN_VERSION_ANSI),
+      kUpdate3WebControlProgId);
+  EXPECT_GU_STREQ(
+      _T("Google.OneClickCtrl.") _T(ONECLICK_PLUGIN_VERSION_ANSI),
+      kOneclickControlProgId);
+}
+
+}  // namespace omaha
diff --git a/plugins/update/oneclick.rgs b/plugins/update/oneclick.rgs
new file mode 100644
index 0000000..5958f43
--- /dev/null
+++ b/plugins/update/oneclick.rgs
@@ -0,0 +1,97 @@
+'%HKROOT%'
+{
+  NoRemove SOFTWARE
+  {
+    NoRemove MozillaPlugins
+    {
+      ForceRemove '@%PLUGINDOMAIN%/%PLUGINPRODUCT%;version=%PLUGINVERSION%'
+      {
+        val Path = s '%MODULE%'
+        val Description = s '%PLUGINDESCRIPTION%'
+        val ProductName = s '%PLUGINPRODUCT%'
+        val Vendor = s '%PLUGINVENDOR%'
+        val Version = s '%PLUGINVERSION%'
+        MimeTypes
+        {
+          '%PLUGINMIMETYPE%'
+        }
+      }
+    }
+    NoRemove Microsoft
+    {
+      NoRemove Windows
+      {
+        NoRemove CurrentVersion
+        {
+          NoRemove Ext
+          {
+            NoRemove PreApproved
+            {
+              ForceRemove '%CLSID%'
+            }
+            NoRemove Stats
+            {
+              ForceRemove '%CLSID%'
+              {
+                iexplore
+                {
+                  AllowedDomains
+                  {
+                    '*'
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+      NoRemove 'Internet Explorer'
+      {
+        NoRemove 'Low Rights'
+        {
+          NoRemove 'ElevationPolicy'
+          {
+            ForceRemove '%CLSID%'
+            {
+              val AppName = s '%SHELLNAME%'
+              val AppPath = s '%SHELLPATH%'
+              val Policy = d '3'
+            }
+          }
+        }
+      }
+    }
+    NoRemove Classes
+    {
+      ForceRemove '%PROGID%' = s '%PLUGINPRODUCT% Plugin'
+      {
+        CLSID = s '%CLSID%'
+      }
+      NoRemove CLSID
+      {
+        ForceRemove '%CLSID%' = s '%PLUGINPRODUCT% Plugin'
+        {
+          ProgID = s '%PROGID%'
+          InprocServer32 = s '%MODULE%'
+          {
+            val ThreadingModel = s 'Apartment'
+          }
+        }
+      }
+      NoRemove MIME
+      {
+        NoRemove Database
+        {
+          NoRemove 'Content Type'
+          {
+            ForceRemove '%PLUGINMIMETYPE%'
+            {
+              val CLSID = s '%CLSID%'
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
diff --git a/plugins/update/resource.h b/plugins/update/resource.h
new file mode 100644
index 0000000..583ea9d
--- /dev/null
+++ b/plugins/update/resource.h
@@ -0,0 +1,21 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_PLUGINS_UPDATE_RESOURCE_H_
+#define OMAHA_PLUGINS_UPDATE_RESOURCE_H_
+
+#define IDR_ONECLICK_RGS                        102
+
+#endif  // OMAHA_PLUGINS_UPDATE_RESOURCE_H_
diff --git a/plugins/update/resource.rc b/plugins/update/resource.rc
new file mode 100644
index 0000000..6c4f72f
--- /dev/null
+++ b/plugins/update/resource.rc
@@ -0,0 +1,32 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// These include headers with absolute paths, which Resource Editor cannot load.
+#ifndef APSTUDIO_INVOKED
+#include "omaha/base/const_config.h"
+#include "omaha/plugins/update/config.h"
+#include "omaha/plugins/update/resource.h"
+#endif  // APSTUDIO_INVOKED
+
+#define PLUGIN_FILENAME UPDATE_PLUGIN_FILENAME
+#include "../plugin_version.rc"
+
+// Path is in the obj directory, which Resource Editor does not look in.
+#ifndef APSTUDIO_INVOKED
+1 TYPELIB "plugins/update/activex/update_control_idl.tlb"
+#endif  // APSTUDIO_INVOKED
+
+IDR_ONECLICK_RGS               REGISTRY "oneclick.rgs"
+
diff --git a/plugins/update/site_lock.cc b/plugins/update/site_lock.cc
new file mode 100644
index 0000000..973aa3c
--- /dev/null
+++ b/plugins/update/site_lock.cc
@@ -0,0 +1,206 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/plugins/update/site_lock.h"
+#include <wininet.h>
+#include <shlobj.h>
+#include "base/scoped_ptr.h"
+#include "omaha/base/atl_regexp.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/error.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/plugins/update/npapi/urlpropbag.h"
+
+namespace omaha {
+
+SiteLock::SiteLock() {
+  for (int i = 0; i < arraysize(kSiteLockPatternStrings); ++i) {
+    VERIFY1(AddPattern(kSiteLockPatternStrings[i]));
+  }
+
+  // TODO(omaha): should this be wrapped in a #ifdef DEBUG?
+  CString dev_pattern_string;
+  if (SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE_DEV,
+                                 kRegValueOneClickHostPattern,
+                                 &dev_pattern_string)) &&
+      !dev_pattern_string.IsEmpty()) {
+    VERIFY1(AddPattern(dev_pattern_string));
+  }
+}
+
+SiteLock::~SiteLock() {
+  for (size_t i = 0; i < patterns_.size(); ++i) {
+    delete patterns_[i];
+  }
+}
+
+bool SiteLock::InApprovedDomain(IObjectWithSite* plugin) {
+  CString url;
+  if (FAILED(GetCurrentBrowserUrl(plugin, &url))) {
+    return false;
+  }
+  return InApprovedDomain(url);
+}
+
+bool SiteLock::InApprovedDomain(const WCHAR* url) {
+  // TODO(omaha): investigate using CUrl to remove dependency on wininet
+  URL_COMPONENTS components = {sizeof(components)};
+  components.dwHostNameLength = 1;
+  if (!::InternetCrackUrl(url, 0, 0, &components)) {
+    return false;
+  }
+  CString hostname(components.lpszHostName, components.dwHostNameLength);
+  for (std::vector<AtlRegExp*>::const_iterator it = patterns_.begin();
+       it != patterns_.end();
+       ++it) {
+    AtlMatchContext context;
+    if ((*it)->Match(hostname, &context)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+HRESULT SiteLock::GetCurrentBrowserUrl(IObjectWithSite* plugin, CString* url) {
+  if (SUCCEEDED(ExtractUrlFromBrowser(plugin, url))) {
+    return S_OK;
+  }
+  if (SUCCEEDED(ExtractUrlFromPropBag(plugin, url))) {
+    return S_OK;
+  }
+  return E_FAIL;
+}
+
+// TODO(omaha): Move this to common\webplugin_utils.
+HRESULT SiteLock::GetUrlDomain(const CString& url, CString* url_domain) {
+  ASSERT1(url_domain);
+  url_domain->Empty();
+
+  URL_COMPONENTS urlComponents = {0};
+  urlComponents.dwStructSize = sizeof(urlComponents);
+  urlComponents.dwSchemeLength = 1;
+  urlComponents.dwHostNameLength = 1;
+  if (!::InternetCrackUrl(url, 0, 0, &urlComponents)) {
+    HRESULT hr = HRESULTFromLastError();
+    CORE_LOG(L2, (_T("[InternetCrackUrl failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  CString scheme(urlComponents.lpszScheme, urlComponents.dwSchemeLength);
+  CString host_name(urlComponents.lpszHostName, urlComponents.dwHostNameLength);
+  ASSERT1(!scheme.IsEmpty());
+  ASSERT1(!host_name.IsEmpty());
+
+  SafeCStringFormat(url_domain, _T("%s://%s/"), scheme, host_name);
+  return S_OK;
+}
+
+bool SiteLock::AddPattern(const WCHAR* pattern) {
+  ASSERT1(pattern);
+
+  // An empty pattern will match everything...
+  if (!*pattern) {
+    ASSERT1(false);
+    return false;
+  }
+
+  scoped_ptr<AtlRegExp> re(new AtlRegExp);
+  REParseError error = re->Parse(pattern);
+  if (REPARSE_ERROR_OK != error) {
+    ASSERT(false, (L"Failed to parse site lock pattern: %s",
+                   pattern));
+    return false;
+  }
+  patterns_.push_back(re.release());
+  return true;
+}
+
+// If the plugin is being hosted inside an NPAPI environment, NPUpdate will set
+// a UrlPropertyBag object as our object site.  Fetch the URL used to create
+// our object from it.
+HRESULT SiteLock::ExtractUrlFromPropBag(IObjectWithSite* plugin, CString* url) {
+  ASSERT1(plugin);
+  ASSERT1(url);
+
+  CComPtr<IPropertyBag> property_bag;
+  HRESULT hr = plugin->GetSite(IID_PPV_ARGS(&property_bag));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CComVariant var;
+  hr = property_bag->Read(kUrlPropertyBag_Url, &var, NULL);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  if (var.vt != VT_BSTR || !var.bstrVal) {
+    return E_UNEXPECTED;
+  }
+  *url = var.bstrVal;
+  return S_OK;
+}
+
+// If the plugin is hosted in an ActiveX environment, IE will set itself as the
+// object site.  Fetch the current URL from it.
+HRESULT SiteLock::ExtractUrlFromBrowser(IObjectWithSite* plugin, CString* url) {
+  ASSERT1(plugin);
+  ASSERT1(url);
+
+  CComPtr<IServiceProvider> service_provider;
+  HRESULT hr = plugin->GetSite(IID_PPV_ARGS(&service_provider));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  CComPtr<IWebBrowser2> web_browser;
+  hr = service_provider->QueryService(SID_SWebBrowserApp,
+                                      IID_PPV_ARGS(&web_browser));
+
+  CComBSTR bstr_url;
+  if (SUCCEEDED(hr)) {
+    hr = web_browser->get_LocationURL(&bstr_url);
+  } else {
+    // Do things the hard way...
+    CComPtr<IOleClientSite> client_site;
+    hr = plugin->GetSite(IID_PPV_ARGS(&client_site));
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    CComPtr<IOleContainer> container;
+    hr = client_site->GetContainer(&container);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    CComPtr<IHTMLDocument2> html_document;
+    hr = container.QueryInterface(&html_document);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    hr = html_document->get_URL(&bstr_url);
+  }
+
+  if (SUCCEEDED(hr)) {
+    *url = bstr_url;
+  }
+
+  return hr;
+}
+
+}  // namespace omaha
diff --git a/plugins/update/site_lock.h b/plugins/update/site_lock.h
new file mode 100644
index 0000000..e92a8d0
--- /dev/null
+++ b/plugins/update/site_lock.h
@@ -0,0 +1,50 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_PLUGINS_UPDATE_SITE_LOCK_H_
+#define OMAHA_PLUGINS_UPDATE_SITE_LOCK_H_
+
+#include <tchar.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "omaha/base/atl_regexp.h"
+
+namespace omaha {
+
+class SiteLock {
+ public:
+  SiteLock();
+  ~SiteLock();
+
+  bool InApprovedDomain(IObjectWithSite* url_provider);
+  bool InApprovedDomain(const WCHAR* url);
+
+  static HRESULT GetCurrentBrowserUrl(IObjectWithSite* plugin, CString* url);
+  static HRESULT GetUrlDomain(const CString& url, CString* url_domain);
+
+ private:
+  bool AddPattern(const WCHAR* pattern);
+
+  static HRESULT ExtractUrlFromBrowser(IObjectWithSite* plugin, CString* url);
+  static HRESULT ExtractUrlFromPropBag(IObjectWithSite* plugin, CString* url);
+
+  std::vector<AtlRegExp*> patterns_;
+
+  DISALLOW_COPY_AND_ASSIGN(SiteLock);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_PLUGINS_UPDATE_SITE_LOCK_H_
diff --git a/plugins/update/site_lock_unittest.cc b/plugins/update/site_lock_unittest.cc
new file mode 100644
index 0000000..6872999
--- /dev/null
+++ b/plugins/update/site_lock_unittest.cc
@@ -0,0 +1,35 @@
+// Copyright 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/plugins/update/site_lock.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+class SiteLockTest : public testing::Test {
+ protected:
+  SiteLock site_lock_;
+};
+
+TEST_F(SiteLockTest, InApprovedDomain_GoogleDotCom) {
+  EXPECT_TRUE(site_lock_.InApprovedDomain(L"http://www.google.com/"));
+  EXPECT_TRUE(site_lock_.InApprovedDomain(L"http://www.google.com/pack/"));
+  EXPECT_TRUE(site_lock_.InApprovedDomain(L"http://www.google.co.uk"));
+}
+TEST_F(SiteLockTest, InApprovedDomain_EvilHackerDotCom) {
+  EXPECT_FALSE(site_lock_.InApprovedDomain(L"http://www.evilhacker.com/"));
+}
+
+}  // namespace omaha
diff --git a/precompile/precompile.h b/precompile/precompile.h
index 9aa7bac..208242c 100644
--- a/precompile/precompile.h
+++ b/precompile/precompile.h
@@ -28,7 +28,7 @@
 #include <tchar.h>
 #include <strsafe.h>
 
-#include "omaha/common/atlassert.h"   // Redefines ATLASSERT.
+#include "omaha/base/atlassert.h"   // Redefines ATLASSERT.
 
 // C4061: enumerate is not explicitly handled by a case label
 // C4265: class has virtual functions, but destructor is not virtual
@@ -57,7 +57,7 @@
 
 #if (_MSC_VER < 1400)
 // TODO(omaha): fix the atlconv for VC8.
-#include "omaha/common/atlconvfix.h"
+#include "omaha/base/atlconvfix.h"
 #endif
 
 #pragma warning(push)
@@ -66,8 +66,6 @@
 #include "base/basictypes.h"
 #pragma warning(pop)
 
-#ifdef UNITTEST
-#include "third_party/gtest/include/gtest/gtest.h"
-#endif  // UNITTEST
+#include "omaha/third_party/gtest/include/gtest/gtest.h"
 
 #endif  // OMAHA_PRECOMPILE_PRECOMPILE_H__
diff --git a/recovery/build.scons b/recovery/build.scons
index e4d2651..e303456 100644
--- a/recovery/build.scons
+++ b/recovery/build.scons
@@ -21,36 +21,44 @@
 #
 # Build google_update_recovery library for Google app consumption.
 #
-rec_env = env.Clone()
+rec_env = env.Clone(COMPONENT_LIBRARY_PUBLISH=True)
 recovery_inputs = [
-    '../common/google_update_recovery.cc',
-    '../common/signaturevalidator.cc',
+    'client/google_update_recovery.cc',
+    '../base/signaturevalidator.cc',
     ]
 
 # Need to add custom suffix to avoid target conflict with
 # common/signaturevalidator.obj
 rec_env['OBJSUFFIX'] = '_rec' + rec_env['OBJSUFFIX']
 
-rec_env.ComponentLibrary('google_update_recovery.lib', recovery_inputs)
+rec_env.ComponentLibrary('google_update_recovery', recovery_inputs)
 
-# Save the lib to the main build directory, so we have it saved with each
-# official build.
-env.Replicate('$STAGING_DIR', ['$LIB_DIR/google_update_recovery.lib',
-                               '$MAIN_DIR/common/google_update_recovery.h'])
+# Save the header to the main build directory, so we have it saved with each
+# official build. The lib is already published to this directory.
+rec_env.Replicate('$STAGING_DIR',
+                  '$MAIN_DIR/recovery/client/google_update_recovery.h')
 
 
 #
 # Build the test
 #
-test_env = env.Clone()
+# TODO(omaha3): Switch entirely to hammer running tests. For now, we must make
+# the test not runnable because it will be run during the build. See main.scons.
+import os
+if 'HAMMER_RUNS_TESTS' in os.environ.keys():
+  test_env = env.Clone()
+else:
+  test_env = env.Clone(COMPONENT_TEST_RUNNABLE=False)
+
+# This is a console application.
 test_env.FilterOut(LINKFLAGS = ['/SUBSYSTEM:WINDOWS'])
+test_env['LINKFLAGS'] += ['/SUBSYSTEM:CONSOLE']
+
 test_env.Append(
-    LINKFLAGS = [
-        '/SUBSYSTEM:CONSOLE'
-        ],
     LIBS = [
-        '$LIB_DIR/common.lib',
+        '$LIB_DIR/base.lib',
         '$LIB_DIR/net.lib',
+        '$LIB_DIR/common.lib',
         '$LIB_DIR/google_update_recovery.lib',
         '$LIB_DIR/security.lib',
         ('atls.lib', 'atlsd.lib')[test_env.Bit('debug')],
@@ -59,7 +67,7 @@
         'crypt32.lib',
         'wintrust.lib',
 
-        # These are required by common_lib
+        # These are required by base.lib
         'netapi32.lib',
         'psapi.lib',
         'rasapi32.lib',
@@ -81,10 +89,11 @@
 if env.Bit('use_precompiled_headers'):
   test_inputs += test_env.EnablePrecompile(target_name)
 
+# TODO(omaha): This test asserts when run so we cannot include it in
+# run_all_tests. Fix it and consider adding to run_all_tests.
 test_env.ComponentTestProgram(target_name,
                               test_inputs,
-                              COMPONENT_TEST_RUNNABLE=False
-)
+                              COMPONENT_TEST_DISABLED=True)
 
 
 env.BuildSConscript('repair_exe')
diff --git a/recovery/client/google_update_recovery.cc b/recovery/client/google_update_recovery.cc
new file mode 100644
index 0000000..93a7fa8
--- /dev/null
+++ b/recovery/client/google_update_recovery.cc
@@ -0,0 +1,626 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+//
+// Implementation of the Google Update recovery mechanism to be included in
+// Google apps.
+
+#include "omaha/recovery/client/google_update_recovery.h"
+#include <shellapi.h>
+#include <wininet.h>
+#include <atlstr.h>
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/signaturevalidator.h"
+#include "omaha/common/const_group_policy.h"
+#include "omaha/third_party/smartany/scoped_any.h"
+
+namespace omaha {
+
+namespace {
+
+const int kRollbackWindowDays = 100;
+
+const TCHAR* const kMachineRepairArgs = _T("/recover /machine");
+const TCHAR* const kUserRepairArgs = _T("/recover");
+
+// TODO(omaha): Add a Code Red lib version that is manually updated when
+// we check in the lib.
+const TCHAR* const kQueryStringFormat =
+    _T("?appid=%s&appversion=%s&applang=%s&machine=%u")
+    _T("&version=%s&osversion=%s&servicepack=%s");
+
+// Information about where to obtain Omaha info.
+// This must never change in Omaha.
+const TCHAR* const kRegValueProductVersion  = _T("pv");
+const TCHAR* const kRelativeGoopdateRegPath = _T("Software\\Google\\Update\\");
+const TCHAR* const kRelativeClientsGoopdateRegPath =
+    _T("Software\\Google\\Update\\Clients\\")
+    _T("{430FD4D0-B729-4F61-AA34-91526481799D}");
+
+// The UpdateDev registry value to override the Code Red url.
+const TCHAR* const kRegValueNameCodeRedUrl = _T("CodeRedUrl");
+
+// Starts another process via ::CreateProcess.
+HRESULT StartProcess(const TCHAR* process_name, TCHAR* command_line) {
+  if (!process_name && !command_line) {
+    return E_INVALIDARG;
+  }
+
+  PROCESS_INFORMATION pi = {0};
+  STARTUPINFO si = {sizeof(si), 0};
+
+  // Feedback cursor is off while the process is starting.
+  si.dwFlags = STARTF_FORCEOFFFEEDBACK;
+
+  BOOL success = ::CreateProcess(
+      process_name,     // Module name
+      command_line,     // Command line
+      NULL,             // Process handle not inheritable
+      NULL,             // Thread handle not inheritable
+      FALSE,            // Set handle inheritance to FALSE
+      0,                // No creation flags
+      NULL,             // Use parent's environment block
+      NULL,             // Use parent's starting directory
+      &si,              // Pointer to STARTUPINFO structure
+      &pi);             // Pointer to PROCESS_INFORMATION structure
+
+  if (!success) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  ::CloseHandle(pi.hProcess);
+  ::CloseHandle(pi.hThread);
+
+  return S_OK;
+}
+
+// Check if a string starts with another string. Case-sensitive.
+bool StringStartsWith(const TCHAR *str, const TCHAR *start_str) {
+  if (!start_str || !str) {
+    return false;
+  }
+
+  while (0 != *str) {
+    // Check for matching characters
+    TCHAR c1 = *str;
+    TCHAR c2 = *start_str;
+
+    // Reached the end of start_str?
+    if (0 == c2)
+      return true;
+
+    if (c1 != c2)
+      return false;
+
+    ++str;
+    ++start_str;
+  }
+
+  // If str is shorter than start_str, no match.  If equal size, match.
+  return 0 == *start_str;
+}
+
+// Escape and unescape strings (shlwapi-based implementation).
+// The intended usage for these APIs is escaping strings to make up
+// URLs, for example building query strings.
+//
+// Pass false to the flag segment_only to escape the url. This will not
+// cause the conversion of the # (%23), ? (%3F), and / (%2F) characters.
+
+// Characters that must be encoded include any characters that have no
+// corresponding graphic character in the US-ASCII coded character
+// set (hexadecimal 80-FF, which are not used in the US-ASCII coded character
+// set, and hexadecimal 00-1F and 7F, which are control characters),
+// blank spaces, "%" (which is used to encode other characters),
+// and unsafe characters (<, >, ", #, {, }, |, \, ^, ~, [, ], and ').
+//
+// The input and output strings can't be longer than INTERNET_MAX_URL_LENGTH
+
+HRESULT StringEscape(const CString& str_in,
+                     bool segment_only,
+                     CString* escaped_string) {
+  if (!escaped_string) {
+    return E_INVALIDARG;
+  }
+
+  DWORD buf_len = INTERNET_MAX_URL_LENGTH + 1;
+  HRESULT hr = ::UrlEscape(str_in,
+                           escaped_string->GetBufferSetLength(buf_len),
+                           &buf_len,
+                           segment_only ?
+                           URL_ESCAPE_PERCENT | URL_ESCAPE_SEGMENT_ONLY :
+                           URL_ESCAPE_PERCENT);
+  if (SUCCEEDED(hr)) {
+    escaped_string->ReleaseBuffer();
+  }
+  return hr;
+}
+
+// Gets the temporary files directory for the current user.
+// The directory returned may not exist.
+// The returned path ends with a '\'.
+// Fails if the path is longer than MAX_PATH.
+HRESULT GetTempDir(CString* temp_path) {
+  if (!temp_path) {
+    return E_INVALIDARG;
+  }
+
+  temp_path->Empty();
+
+  TCHAR buffer[MAX_PATH] = {0};
+  DWORD num_chars = ::GetTempPath(MAX_PATH, buffer);
+  if (!num_chars) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  } else if (num_chars >= MAX_PATH) {
+    return E_FAIL;
+  }
+
+  *temp_path = buffer;
+  return S_OK;
+}
+
+// Creates the specified directory.
+HRESULT CreateDir(const CString& dir) {
+  if (!::CreateDirectory(dir, NULL)) {
+    DWORD error = ::GetLastError();
+    if (ERROR_FILE_EXISTS != error && ERROR_ALREADY_EXISTS != error) {
+      return HRESULT_FROM_WIN32(error);
+    }
+  }
+  return S_OK;
+}
+
+HRESULT GetAndCreateTempDir(CString* temp_path) {
+  if (!temp_path) {
+    return E_INVALIDARG;
+  }
+
+  HRESULT hr = GetTempDir(temp_path);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  if (temp_path->IsEmpty()) {
+    return E_FAIL;
+  }
+
+  // Create this dir if it doesn't already exist.
+  return CreateDir(*temp_path);
+}
+
+
+// Create a unique temporary file and returns the full path.
+HRESULT CreateUniqueTempFile(const CString& user_temp_dir,
+                             CString* unique_temp_file_path) {
+  if (user_temp_dir.IsEmpty() || !unique_temp_file_path) {
+    return E_INVALIDARG;
+  }
+
+  TCHAR unique_temp_filename[MAX_PATH] = {0};
+  if (!::GetTempFileName(user_temp_dir,
+                         _T("GUR"),  // prefix
+                         0,          // form a unique filename
+                         unique_temp_filename)) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  *unique_temp_file_path = unique_temp_filename;
+  if (unique_temp_file_path->IsEmpty()) {
+    return E_FAIL;
+  }
+
+  return S_OK;
+}
+
+// Obtains the OS version and service pack.
+HRESULT GetOSInfo(CString* os_version, CString* service_pack) {
+  if (!os_version || !service_pack) {
+    return E_INVALIDARG;
+  }
+
+  OSVERSIONINFO os_version_info = { 0 };
+  os_version_info.dwOSVersionInfoSize = sizeof(os_version_info);
+  if (!::GetVersionEx(&os_version_info)) {
+    HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
+    return hr;
+  } else {
+    os_version->Format(_T("%d.%d"),
+                       os_version_info.dwMajorVersion,
+                       os_version_info.dwMinorVersion);
+    *service_pack = os_version_info.szCSDVersion;
+  }
+  return S_OK;
+}
+
+// Reads the specified string value from the specified registry key.
+// Only supports value types REG_SZ and REG_EXPAND_SZ.
+// REG_EXPAND_SZ strings are not expanded.
+HRESULT GetRegStringValue(bool is_machine_key,
+                          const CString& relative_key_path,
+                          const CString& value_name,
+                          CString* value) {
+  if (!value) {
+    return E_INVALIDARG;
+  }
+
+  value->Empty();
+  HKEY root_key = is_machine_key ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+  HKEY key = NULL;
+  LONG res = ::RegOpenKeyEx(root_key, relative_key_path, 0, KEY_READ, &key);
+  if (res != ERROR_SUCCESS) {
+    return HRESULT_FROM_WIN32(res);
+  }
+
+  // First get the size of the string buffer.
+  DWORD type = 0;
+  DWORD byte_count = 0;
+  res = ::RegQueryValueEx(key, value_name, NULL, &type, NULL, &byte_count);
+  if (ERROR_SUCCESS != res) {
+    ::RegCloseKey(key);
+    return HRESULT_FROM_WIN32(res);
+  }
+  if ((type != REG_SZ && type != REG_EXPAND_SZ) || (0 == byte_count)) {
+    ::RegCloseKey(key);
+    return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+  }
+
+  CString local_value;
+  // GetBuffer throws when not able to allocate the requested buffer.
+  TCHAR* buffer = local_value.GetBuffer(byte_count / sizeof(TCHAR));
+  res = ::RegQueryValueEx(key,
+                          value_name,
+                          NULL,
+                          NULL,
+                          reinterpret_cast<BYTE*>(buffer),
+                          &byte_count);
+  ::RegCloseKey(key);
+  if (ERROR_SUCCESS == res) {
+    local_value.ReleaseBufferSetLength(byte_count / sizeof(TCHAR));
+    *value = local_value;
+  }
+
+  return HRESULT_FROM_WIN32(res);
+}
+
+// Reads the specified DWORD value from the specified registry key.
+// Only supports value types REG_DWORD.
+// Assumes DWORD is sufficient buffer, which must be true for valid value type.
+HRESULT GetRegDwordValue(bool is_machine_key,
+                         const CString& relative_key_path,
+                         const CString& value_name,
+                         DWORD* value) {
+  if (!value) {
+    return E_INVALIDARG;
+  }
+
+  HKEY root_key = is_machine_key ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+  HKEY key = NULL;
+  LONG res = ::RegOpenKeyEx(root_key, relative_key_path, 0, KEY_READ, &key);
+  if (res != ERROR_SUCCESS) {
+    return HRESULT_FROM_WIN32(res);
+  }
+
+  DWORD type = 0;
+  DWORD byte_count = sizeof(*value);
+  res = ::RegQueryValueEx(key,
+                          value_name,
+                          NULL,
+                          &type,
+                          reinterpret_cast<BYTE*>(value),
+                          &byte_count);
+  ::RegCloseKey(key);
+  if (ERROR_SUCCESS != res) {
+    return HRESULT_FROM_WIN32(res);
+  }
+  if ((type != REG_DWORD) || (0 == byte_count)) {
+    return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
+  }
+
+  return S_OK;
+}
+
+// Obtains information about the current Omaha installation.
+// Attempts to obtain as much information as possible even if errors occur.
+// Therefore, return values of GetRegStringValue are ignored.
+HRESULT GetOmahaInformation(bool is_machine_app,
+                            CString* omaha_version) {
+  if (!omaha_version) {
+    return E_INVALIDARG;
+  }
+
+  if (FAILED(GetRegStringValue(is_machine_app,
+                               kRelativeClientsGoopdateRegPath,
+                               kRegValueProductVersion,
+                               omaha_version))) {
+    *omaha_version = _T("0.0.0.0");
+  }
+
+  return S_OK;
+}
+
+// Builds the query portion of the recovery url.
+// This method obtains values necessary to build the query that are not provided
+// as parameters.
+// Attempts to build with as much information as possible even if errors occur.
+HRESULT BuildUrlQueryPortion(const CString& app_guid,
+                             const CString& app_version,
+                             const CString& app_language,
+                             bool is_machine_app,
+                             CString* query) {
+  if (!query) {
+    return E_INVALIDARG;
+  }
+
+  CString omaha_version;
+  GetOmahaInformation(is_machine_app, &omaha_version);
+
+  CString os_version;
+  CString os_service_pack;
+  GetOSInfo(&os_version, &os_service_pack);
+
+  // All parameters must be escaped individually before building the query.
+  CString app_guid_escaped;
+  CString app_version_escaped;
+  CString app_language_escaped;
+  CString omaha_version_escaped;
+  CString os_version_escaped;
+  CString os_service_pack_escaped;
+  StringEscape(app_guid, true, &app_guid_escaped);
+  StringEscape(app_version, true, &app_version_escaped);
+  StringEscape(app_language, true, &app_language_escaped);
+  StringEscape(omaha_version, true, &omaha_version_escaped);
+  StringEscape(os_version, true, &os_version_escaped);
+  StringEscape(os_service_pack, true, &os_service_pack_escaped);
+
+  query->Format(kQueryStringFormat,
+                app_guid_escaped,
+                app_version_escaped,
+                app_language_escaped,
+                is_machine_app ? 1 : 0,
+                omaha_version_escaped,
+                os_version_escaped,
+                os_service_pack_escaped);
+
+  return S_OK;
+}
+
+// Returns the full path to save the downloaded file to.
+// The path is based on a unique temporary filename to avoid a conflict
+// between multiple apps downloading to the same location.
+// The path to this file is also returned. The caller is responsible for
+// deleting the temporary file after using the download target path.
+// If it cannot create the unique directory, it attempts to use the user's
+// temporary directory and a constant filename.
+HRESULT GetDownloadTargetPath(CString* download_target_path,
+                              CString* temp_file_path) {
+  if (!download_target_path || !temp_file_path) {
+    return E_INVALIDARG;
+  }
+
+  CString user_temp_dir;
+  HRESULT hr = GetAndCreateTempDir(&user_temp_dir);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  hr = CreateUniqueTempFile(user_temp_dir, temp_file_path);
+  if (SUCCEEDED(hr) && !temp_file_path->IsEmpty()) {
+    *download_target_path = *temp_file_path;
+    // Ignore the return value. A .tmp filename is better than none.
+    download_target_path->Replace(_T(".tmp"), _T(".exe"));
+  } else {
+    // Try a static filename in the temp directory as a fallback.
+    *download_target_path = user_temp_dir + _T("GoogleUpdateSetup.exe");
+    *temp_file_path = _T("");
+  }
+
+  return S_OK;
+}
+
+HRESULT DownloadRepairFile(const CString& download_target_path,
+                           const CString& app_guid,
+                           const CString& app_version,
+                           const CString& app_language,
+                           bool is_machine_app,
+                           DownloadCallback download_callback,
+                           void* context) {
+  CString query;
+  BuildUrlQueryPortion(app_guid,
+                       app_version,
+                       app_language,
+                       is_machine_app,
+                       &query);
+
+  CString url;
+  HRESULT hr = GetRegStringValue(true,
+                                 _T("SOFTWARE\\Google\\UpdateDev"),
+                                 kRegValueNameCodeRedUrl,
+                                 &url);
+  if (FAILED(hr)) {
+    url = omaha::kUrlCodeRedCheck;
+  }
+
+  url += query;
+
+  return download_callback(url, download_target_path, context);
+}
+
+// Makes sure the path is enclosed with double quotation marks.
+void EnclosePath(CString* path) {
+  if (path) {
+    return;
+  }
+
+  if (!path->IsEmpty() && path->GetAt(0) != _T('"')) {
+    path->Insert(0, _T('"'));
+    path->AppendChar(_T('"'));
+  }
+}
+
+HRESULT RunRepairFile(const CString& file_path, bool is_machine_app) {
+  const TCHAR* repair_file_args = is_machine_app ? kMachineRepairArgs :
+                                                   kUserRepairArgs;
+
+  CString command_line(file_path);
+  EnclosePath(&command_line);
+  command_line.AppendChar(_T(' '));
+  command_line.Append(repair_file_args);
+
+  return StartProcess(NULL, command_line.GetBuffer());
+}
+
+}  // namespace
+
+// Verifies the file's integrity and that it is signed by Google.
+// We cannot prevent rollback attacks by using a version because the client
+// may not be able to determine the current version if the files and/or
+// registry entries have been deleted/corrupted.
+// Therefore, we check that the file was signed recently.
+HRESULT VerifyFileSignature(const CString& filename) {
+  // Use Authenticode/WinVerifyTrust to verify the file.
+  // Allow the revocation check to use the network.
+  HRESULT hr = VerifySignature(filename, true);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  // Verify that there is a Google certificate.
+  if (!VerifySigneeIsGoogle(filename)) {
+    return CERT_E_CN_NO_MATCH;
+  }
+
+  // Check that the file was signed recently to limit the window for
+  // rollback attacks.
+  return VerifyFileSignedWithinDays(filename, kRollbackWindowDays);
+}
+
+// Verifies the file contains the special markup resource for repair files.
+HRESULT VerifyRepairFileMarkup(const CString& filename) {
+  const TCHAR* kMarkupResourceName = MAKEINTRESOURCE(1);
+  const TCHAR* kMarkupResourceType = _T("GOOGLEUPDATEREPAIR");
+  const DWORD kMarkupResourceExpectedValue = 1;
+
+  scoped_library module(::LoadLibraryEx(filename, 0, LOAD_LIBRARY_AS_DATAFILE));
+  if (!module) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  HRSRC resource(::FindResource(get(module),
+                                kMarkupResourceName,
+                                kMarkupResourceType));
+  if (!resource) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  if (sizeof(kMarkupResourceExpectedValue) !=
+      ::SizeofResource(get(module), resource)) {
+    return E_UNEXPECTED;
+  }
+
+  HGLOBAL loaded_resource(::LoadResource(get(module), resource));
+  if (!loaded_resource) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  const DWORD* value = static_cast<DWORD*>(::LockResource(loaded_resource));
+  if (!value) {
+    return E_HANDLE;
+  }
+
+  if (kMarkupResourceExpectedValue != *value) {
+    return E_UNEXPECTED;
+  }
+
+  return S_OK;
+}
+
+// Verifies the filename is not UNC name, the file exists, has a valid signature
+// chain, is signed by Google, and contains the special markup resource for
+// repair files.
+HRESULT VerifyIsValidRepairFile(const CString& filename) {
+  // Make sure file exists.
+  if (!::PathFileExists(filename)) {
+    return HRESULT_FROM_WIN32(::GetLastError());
+  }
+
+  HRESULT hr = VerifyFileSignature(filename);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return VerifyRepairFileMarkup(filename);
+}
+
+}  // namespace omaha
+
+// If a repair file is run, the file will not be deleted until reboot. Delete
+// after reboot will only succeed when executed by an admin or LocalSystem.
+// Returns HRESULT_FROM_WIN32(ERROR_ACCESS_DISABLED_BY_POLICY) if automatic
+// update checks are disabled.
+HRESULT FixGoogleUpdate(const TCHAR* app_guid,
+                        const TCHAR* app_version,
+                        const TCHAR* app_language,
+                        bool is_machine_app,
+                        DownloadCallback download_callback,
+                        void* context) {
+  if (!app_guid || !app_version || !app_language || !download_callback) {
+    return E_INVALIDARG;
+  }
+
+  DWORD update_check_period_override_minutes(UINT_MAX);
+  HRESULT hr = omaha::GetRegDwordValue(
+                   true,
+                   GOOPDATE_POLICIES_RELATIVE,
+                   omaha::kRegValueAutoUpdateCheckPeriodOverrideMinutes,
+                   &update_check_period_override_minutes);
+  if (SUCCEEDED(hr) && (0 == update_check_period_override_minutes)) {
+    return HRESULT_FROM_WIN32(ERROR_ACCESS_DISABLED_BY_POLICY);
+  }
+
+  CString download_target_path;
+  CString temp_file_path;
+  hr = omaha::GetDownloadTargetPath(&download_target_path, &temp_file_path);
+  if (FAILED(hr)) {
+    return hr;
+  }
+  if (download_target_path.IsEmpty()) {
+      return E_FAIL;
+  }
+
+  // After calling DownloadRepairFile, don't return until the repair file and
+  // temp file have been deleted.
+  hr = omaha::DownloadRepairFile(download_target_path,
+                                 app_guid,
+                                 app_version,
+                                 app_language,
+                                 is_machine_app,
+                                 download_callback,
+                                 context);
+
+  if (SUCCEEDED(hr)) {
+    hr = omaha::VerifyIsValidRepairFile(download_target_path);
+  }
+
+  if (FAILED(hr)) {
+    ::DeleteFile(download_target_path);
+    ::DeleteFile(temp_file_path);
+    return hr;
+  }
+
+  hr = omaha::RunRepairFile(download_target_path, is_machine_app);
+  ::MoveFileEx(download_target_path, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);
+  ::DeleteFile(temp_file_path);
+
+  return hr;
+}
diff --git a/recovery/client/google_update_recovery.h b/recovery/client/google_update_recovery.h
new file mode 100644
index 0000000..4bd5b2a
--- /dev/null
+++ b/recovery/client/google_update_recovery.h
@@ -0,0 +1,48 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+//
+// Defines the interface to the Google Update recovery mechanism to be included
+// in Google apps.
+
+#ifndef OMAHA_RECOVERY_CLIENT_GOOGLE_UPDATE_RECOVERY_H__
+#define OMAHA_RECOVERY_CLIENT_GOOGLE_UPDATE_RECOVERY_H__
+
+#include <tchar.h>
+#include <windows.h>
+
+#ifndef UNICODE
+#error The distributed library only supports UNICODE.
+#endif
+
+extern "C" {
+typedef HRESULT (*DownloadCallback)(const TCHAR* url,
+                                    const TCHAR* file_path,
+                                    void* context);
+
+// Determines whether there is a Code Red event for the current installation
+// and repairs it if necessary.
+// app_language should follow the external Internet standard
+// Best Common Practice (BCP) 47: http://www.rfc-editor.org/rfc/bcp/bcp47.txt
+// context can be NULL if download_callback does not use it.
+HRESULT FixGoogleUpdate(const TCHAR* app_guid,
+                        const TCHAR* app_version,
+                        const TCHAR* app_language,
+                        bool is_machine_app,
+                        DownloadCallback download_callback,
+                        void* context);
+}  // extern "C"
+
+#endif  // OMAHA_RECOVERY_CLIENT_GOOGLE_UPDATE_RECOVERY_H__
diff --git a/recovery/client/google_update_recovery_unittest.cc b/recovery/client/google_update_recovery_unittest.cc
new file mode 100644
index 0000000..1160bee
--- /dev/null
+++ b/recovery/client/google_update_recovery_unittest.cc
@@ -0,0 +1,942 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+//
+// Unit tests for the Google Update recovery mechanism.
+// All apps that are using the mechanism must also run this test.
+//
+// Unlike the mechanism code, this code relies on code from common because it
+// makes writing the tests much simpler and size is not a concern.
+
+#include <windows.h>
+#include <atlstr.h>
+#include "omaha/base/app_util.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/signaturevalidator.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/const_group_policy.h"
+#include "omaha/net/network_config.h"
+#include "omaha/net/network_request.h"
+#include "omaha/net/simple_request.h"
+#include "omaha/recovery/client/google_update_recovery.h"
+#include "omaha/third_party/gtest/include/gtest/gtest.h"
+
+// TODO(omaha): Replicate some of these tests in signaturevalidator_unittest.cc.
+
+// As of Google Test 1.4.0, expressions get converted to 'bool', resulting in
+// "warning C4800: 'BOOL' : forcing value to bool 'true' or 'false' (performance
+// warning)" in some uses.
+// These must be kept in sync with gtest.h.
+// TODO(omaha): Try to get this fixed in Google Test.
+#undef EXPECT_TRUE
+#define EXPECT_TRUE(condition) \
+  GTEST_TEST_BOOLEAN_(!!(condition), #condition, false, true, \
+                      GTEST_NONFATAL_FAILURE_)
+#undef ASSERT_TRUE
+#define ASSERT_TRUE(condition) \
+  GTEST_TEST_BOOLEAN_(!!(condition), #condition, false, true, \
+                      GTEST_FATAL_FAILURE_)
+
+namespace omaha {
+
+namespace {
+
+const TCHAR kDummyAppGuid[] = _T("{8E472B0D-3E8B-43b1-B89A-E8506AAF1F16}");
+const TCHAR kDummyAppVersion[] = _T("3.4.5.6");
+const TCHAR kDummyAppLang[] = _T("en-us");
+
+const TCHAR kTempDirectory[] = _T("C:\\WINDOWS\\Temp");
+
+const TCHAR kFullMachineOmahaMainKeyPath[] =
+    _T("HKLM\\Software\\Google\\Update\\");
+const TCHAR kFullUserOmahaMainKeyPath[] =
+    _T("HKCU\\Software\\Google\\Update\\");
+const TCHAR kFullMachineOmahaClientKeyPath[] =
+    _T("HKLM\\Software\\Google\\Update\\Clients\\")
+    _T("{430FD4D0-B729-4f61-AA34-91526481799D}");
+const TCHAR kFullUserOmahaClientKeyPath[] =
+    _T("HKCU\\Software\\Google\\Update\\Clients\\")
+    _T("{430FD4D0-B729-4f61-AA34-91526481799D}");
+
+const HRESULT kDummyNoFileError = 0x80041234;
+
+const TCHAR kArgumentSavingExecutableRelativePath[] =
+    _T("unittest_support\\SaveArguments.exe");
+const TCHAR kSavedArgumentsFileName[] = _T("saved_arguments.txt");
+const TCHAR* const kInvalidFileUrl = _T("http://www.google.com/robots.txt");
+
+#define MACHINE_KEY_NAME _T("HKLM")
+#define MACHINE_KEY MACHINE_KEY_NAME _T("\\")
+#define USER_KEY_NAME _T("HKCU")
+#define USER_KEY USER_KEY_NAME _T("\\")
+
+// These methods were copied from omaha/testing/omaha_unittest.cpp.
+const TCHAR kRegistryHiveOverrideRoot[] =
+    _T("HKCU\\Software\\Google\\Update\\UnitTest\\");
+
+const TCHAR kExpectedUrlForDummyAppAndNoOmahaValues[] = _T("http://cr-tools.clients.google.com/service/check2?appid=%7B8E472B0D-3E8B-43b1-B89A-E8506AAF1F16%7D&appversion=3.4.5.6&applang=en-us&machine=1&version=0.0.0.0&osversion=");  // NOLINT
+const int kExpectedUrlForDummyAppAndNoOmahaValuesLength =
+    arraysize(kExpectedUrlForDummyAppAndNoOmahaValues) - 1;
+
+// Overrides the HKLM and HKCU registry hives so that accesses go to the
+// specified registry key instead.
+// This method is most often used in SetUp().
+void OverrideRegistryHives(const CString& hive_override_key_name) {
+  // Override the destinations of HKLM and HKCU to use a special location
+  // for the unit tests so that we don't disturb the actual Omaha state.
+  RegKey machine_key;
+  RegKey user_key;
+  EXPECT_HRESULT_SUCCEEDED(
+      machine_key.Create(hive_override_key_name + MACHINE_KEY));
+  EXPECT_HRESULT_SUCCEEDED(user_key.Create(hive_override_key_name + USER_KEY));
+  EXPECT_HRESULT_SUCCEEDED(::RegOverridePredefKey(HKEY_LOCAL_MACHINE,
+                                                  machine_key.Key()));
+  EXPECT_HRESULT_SUCCEEDED(::RegOverridePredefKey(HKEY_CURRENT_USER,
+                                                  user_key.Key()));
+}
+
+// Restores HKLM and HKCU registry accesses to the real hives.
+// This method is most often used in TearDown().
+void RestoreRegistryHives() {
+  EXPECT_HRESULT_SUCCEEDED(::RegOverridePredefKey(HKEY_LOCAL_MACHINE, NULL));
+  EXPECT_HRESULT_SUCCEEDED(::RegOverridePredefKey(HKEY_CURRENT_USER, NULL));
+}
+
+CString GetTmp() {
+  TCHAR temp_dir[MAX_PATH] = {0};
+  EXPECT_NE(0, ::GetEnvironmentVariable(_T("TMP"), temp_dir, MAX_PATH));
+  return temp_dir;
+}
+
+}  // namespace
+
+HRESULT VerifyFileSignature(const CString& filename);
+HRESULT VerifyRepairFileMarkup(const CString& filename);
+
+class GoogleUpdateRecoveryTest : public testing::Test {
+ public:
+  static void set_saved_url(const CString& saved_url) {
+    saved_url_ = saved_url;
+  }
+
+  static void set_saved_file_path(const CString& saved_file_path) {
+    saved_file_path_ = saved_file_path;
+  }
+
+  static void set_saved_context(void* context) {
+    saved_context_ = context;
+  }
+
+ protected:
+  GoogleUpdateRecoveryTest() {
+    saved_url_.Empty();
+    saved_file_path_.Empty();
+    saved_context_ = NULL;
+  }
+
+  virtual void SetUp() {
+  }
+
+  virtual void TearDown() {
+  }
+
+  void CheckSavedUrlOSFragment() {
+    OSVERSIONINFO os_version_info = { 0 };
+    os_version_info.dwOSVersionInfoSize = sizeof(os_version_info);
+    EXPECT_NE(0, ::GetVersionEx(&os_version_info));
+
+    CString escaped_service_pack;
+    EXPECT_HRESULT_SUCCEEDED(StringEscape(os_version_info.szCSDVersion,
+                                          false,
+                                          &escaped_service_pack));
+
+    CString expected_os_fragment;
+    expected_os_fragment.Format(_T("%d.%d&servicepack="),
+                                os_version_info.dwMajorVersion,
+                                os_version_info.dwMinorVersion);
+    expected_os_fragment += escaped_service_pack;
+
+    EXPECT_TRUE(expected_os_fragment ==
+                saved_url_.Right(expected_os_fragment.GetLength()));
+  }
+
+  void VerifySavedArgumentsFile(const CString& expected_string) {
+    CString saved_arguments_path = ConcatenatePath(
+                                       GetDirectoryFromPath(saved_file_path_),
+                                       kSavedArgumentsFileName);
+    bool is_found = false;
+    for (int tries = 0; tries < 100 && !is_found; ++tries) {
+      ::Sleep(50);
+      is_found = File::Exists(saved_arguments_path);
+    }
+    EXPECT_TRUE(is_found);
+
+    scoped_hfile file(::CreateFile(saved_arguments_path,
+                                   GENERIC_READ,
+                                   0,                     // do not share
+                                   NULL,                  // default security
+                                   OPEN_EXISTING,         // existing file only
+                                   FILE_ATTRIBUTE_NORMAL,
+                                   NULL));                // no template
+    EXPECT_NE(INVALID_HANDLE_VALUE, get(file));
+
+    const int kBufferLen = 50;
+    TCHAR buffer[kBufferLen + 1] = {0};
+    DWORD bytes_read = 0;
+
+    EXPECT_TRUE(::ReadFile(get(file),
+                           buffer,
+                           kBufferLen * sizeof(TCHAR),
+                           &bytes_read,
+                           NULL));
+    EXPECT_EQ(0, bytes_read % sizeof(TCHAR));
+    buffer[bytes_read / sizeof(TCHAR)] = _T('\0');
+
+    EXPECT_STREQ(expected_string, buffer);
+  }
+
+  void VerifyExpectedSavedFilePath(const CString& expected_temp_directory) {
+    const int kMaxUniqueChars = 4;
+    const CString expected_path_part_a = expected_temp_directory + _T("\\GUR");
+    const CString expected_path_part_b = _T(".exe");
+    EXPECT_STREQ(expected_path_part_a,
+                 saved_file_path_.Left(expected_path_part_a.GetLength()));
+    EXPECT_STREQ(expected_path_part_b,
+                 saved_file_path_.Right(expected_path_part_b.GetLength()));
+    const int constant_chars = expected_path_part_a.GetLength() +
+                               expected_path_part_b.GetLength();
+    EXPECT_GT(saved_file_path_.GetLength(), constant_chars);
+    EXPECT_LE(saved_file_path_.GetLength(), constant_chars + kMaxUniqueChars);
+  }
+
+  static CString saved_url_;
+  static CString saved_file_path_;
+  static void* saved_context_;
+
+ protected:
+  // Copies SaveArguments.exe to the specified location.
+  static HRESULT DownloadArgumentSavingFile(const TCHAR* url,
+                                            const TCHAR* file_path,
+                                            void* context) {
+    ASSERT1(url);
+    ASSERT1(file_path);
+
+    GoogleUpdateRecoveryTest::set_saved_url(url);
+    GoogleUpdateRecoveryTest::set_saved_file_path(file_path);
+    GoogleUpdateRecoveryTest::set_saved_context(context);
+
+    CString executable_full_path(app_util::GetCurrentModuleDirectory());
+    VERIFY1(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                         kArgumentSavingExecutableRelativePath));
+
+    if (!::CopyFile(executable_full_path, file_path, false)) {
+      HRESULT hr = HRESULTFromLastError();
+      return hr;
+    }
+
+    return S_OK;
+  }
+
+  // Returns kDummyNoFileError, simulating no file to download.
+  static HRESULT DownloadFileNoFile(const TCHAR* url,
+                                    const TCHAR* file_path,
+                                    void* context) {
+    ASSERT1(url);
+    ASSERT1(file_path);
+
+    GoogleUpdateRecoveryTest::set_saved_url(url);
+    GoogleUpdateRecoveryTest::set_saved_file_path(file_path);
+    GoogleUpdateRecoveryTest::set_saved_context(context);
+
+    return kDummyNoFileError;
+  }
+
+  // Overrides the address to cause a file to be downloaded via HTTP.
+  // Uses a real HTTP stack, so it is similar to a real implementation.
+  // The file is invalid, so signature verification should return
+  // TRUST_E_SUBJECT_FORM_UNKNOWN.
+  static HRESULT DownloadFileInvalidFile(const TCHAR* url,
+                                         const TCHAR* file_path,
+                                         void* context) {
+    ASSERT1(url);
+    UNREFERENCED_PARAMETER(url);
+
+    return DownloadFileFromServer(kInvalidFileUrl, file_path, context);
+  }
+
+  // Uses a real HTTP stack, so it is similar to a real implementation.
+  static HRESULT DownloadFileFromServer(const TCHAR* url,
+                                        const TCHAR* file_path,
+                                        void* context) {
+    UTIL_LOG(L2, (_T("[DownloadFileFromServer][%s][%s]"), url, file_path));
+
+    ASSERT1(url);
+    ASSERT1(file_path);
+
+    GoogleUpdateRecoveryTest::set_saved_url(url);
+    GoogleUpdateRecoveryTest::set_saved_file_path(file_path);
+    GoogleUpdateRecoveryTest::set_saved_context(context);
+
+    NetworkConfig* network_config = NULL;
+    NetworkConfigManager& network_manager = NetworkConfigManager::Instance();
+    HRESULT hr = network_manager.GetUserNetworkConfig(&network_config);
+    if (FAILED(hr)) {
+      UTIL_LOG(LE, (_T("[GetUserNetworkConfig failed][0x%08x]"), hr));
+      return hr;
+    }
+    ON_SCOPE_EXIT_OBJ(*network_config, &NetworkConfig::Clear);
+    NetworkRequest network_request(network_config->session());
+
+    network_config->Clear();
+    network_config->Add(new UpdateDevProxyDetector);
+    network_config->Add(new FirefoxProxyDetector);
+    network_config->Add(new IEProxyDetector);
+
+    network_request.AddHttpRequest(new SimpleRequest);
+
+    hr = network_request.DownloadFile(url, CString(file_path));
+    if (FAILED(hr)) {
+      UTIL_LOG(LE, (_T("[DownloadFile failed][%s][0x%08x]"), url, hr));
+      return hr;
+    }
+
+    int status_code = network_request.http_status_code();
+    UTIL_LOG(L2, (_T("[HTTP status][%u]"), status_code));
+
+    if (HTTP_STATUS_OK == status_code) {
+      return S_OK;
+    } else if (HTTP_STATUS_NO_CONTENT == status_code) {
+      return kDummyNoFileError;
+    } else {
+      // Apps would not have this assumption.
+      ASSERT(false, (_T("Status code %i received. Expected 200 or 204."),
+                     status_code));
+      return E_FAIL;
+    }
+  }
+};
+
+CString GoogleUpdateRecoveryTest::saved_url_;
+CString GoogleUpdateRecoveryTest::saved_file_path_;
+void* GoogleUpdateRecoveryTest::saved_context_;
+
+class GoogleUpdateRecoveryRegistryProtectedTest
+    : public GoogleUpdateRecoveryTest {
+ protected:
+  GoogleUpdateRecoveryRegistryProtectedTest()
+      : hive_override_key_name_(kRegistryHiveOverrideRoot) {
+  }
+
+  CString hive_override_key_name_;
+
+  virtual void SetUp() {
+    GoogleUpdateRecoveryTest::SetUp();
+    RegKey::DeleteKey(hive_override_key_name_, true);
+    OverrideRegistryHives(hive_override_key_name_);
+  }
+
+  virtual void TearDown() {
+    RestoreRegistryHives();
+    EXPECT_HRESULT_SUCCEEDED(RegKey::DeleteKey(hive_override_key_name_, true));
+    GoogleUpdateRecoveryTest::TearDown();
+  }
+};
+
+//
+// FixGoogleUpdate Tests
+//
+
+TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_UseRealHttpClient) {
+  EXPECT_EQ(TRUST_E_SUBJECT_FORM_UNKNOWN,
+            FixGoogleUpdate(kDummyAppGuid,
+                            kDummyAppVersion,
+                            kDummyAppLang,
+                            true,
+                            DownloadFileInvalidFile,
+                            NULL));
+}
+
+TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_FileReturned_Machine) {
+  CString saved_arguments_path = ConcatenatePath(app_util::GetTempDir(),
+                                                 kSavedArgumentsFileName);
+
+  ::DeleteFile(saved_arguments_path);
+  EXPECT_FALSE(File::Exists(saved_arguments_path));
+
+  CString context_string(_T("some context"));
+  EXPECT_HRESULT_SUCCEEDED(FixGoogleUpdate(kDummyAppGuid,
+                                           kDummyAppVersion,
+                                           kDummyAppLang,
+                                           true,
+                                           DownloadArgumentSavingFile,
+                                           &context_string));
+
+  EXPECT_EQ(&context_string, saved_context_);
+  EXPECT_STREQ(_T("some context"), *static_cast<CString*>(saved_context_));
+
+  ::Sleep(200);
+  EXPECT_TRUE(File::Exists(saved_file_path_));
+  VerifySavedArgumentsFile(_T("/recover /machine"));
+
+  EXPECT_TRUE(::DeleteFile(saved_file_path_));
+  EXPECT_TRUE(::DeleteFile(saved_arguments_path));
+}
+
+TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_FileReturned_User) {
+  CString saved_arguments_path = ConcatenatePath(app_util::GetTempDir(),
+                                                 kSavedArgumentsFileName);
+
+  ::DeleteFile(saved_arguments_path);
+  EXPECT_FALSE(File::Exists(saved_arguments_path));
+
+  CString context_string(_T("more context"));
+  EXPECT_HRESULT_SUCCEEDED(FixGoogleUpdate(kDummyAppGuid,
+                                           kDummyAppVersion,
+                                           kDummyAppLang,
+                                           false,
+                                           DownloadArgumentSavingFile,
+                                           &context_string));
+
+  EXPECT_EQ(&context_string, saved_context_);
+  EXPECT_STREQ(_T("more context"), *static_cast<CString*>(saved_context_));
+
+  ::Sleep(200);
+  EXPECT_TRUE(File::Exists(saved_file_path_));
+  VerifySavedArgumentsFile(_T("/recover"));
+
+  EXPECT_TRUE(::DeleteFile(saved_file_path_));
+  EXPECT_TRUE(::DeleteFile(saved_arguments_path));
+}
+
+TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_NoFile_Machine) {
+  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
+                                               kDummyAppVersion,
+                                               kDummyAppLang,
+                                               true,
+                                               DownloadFileNoFile,
+                                               NULL));
+
+  EXPECT_EQ(static_cast<void*>(NULL), saved_context_);
+  EXPECT_FALSE(File::Exists(saved_file_path_));
+
+  TCHAR temp_dir[MAX_PATH] = {0};
+  EXPECT_TRUE(::GetEnvironmentVariable(_T("TMP"), temp_dir, MAX_PATH));
+  EXPECT_TRUE(File::Exists(temp_dir))
+      << _T("The temp directory was deleted or not created.");
+}
+
+TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_NoFile_User) {
+  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
+                                               kDummyAppVersion,
+                                               kDummyAppLang,
+                                               false,
+                                               DownloadFileNoFile,
+                                               NULL));
+
+  EXPECT_EQ(static_cast<void*>(NULL), saved_context_);
+  EXPECT_FALSE(File::Exists(saved_file_path_));
+
+  TCHAR temp_dir[MAX_PATH] = {0};
+  EXPECT_TRUE(::GetEnvironmentVariable(_T("TMP"), temp_dir, MAX_PATH));
+  EXPECT_TRUE(File::Exists(temp_dir))
+      << _T("The temp directory was deleted or not created.");
+}
+
+TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
+       FixGoogleUpdate_AllValues_MachineApp) {
+  const TCHAR kExpectedUrl[] = _T("http://cr-tools.clients.google.com/service/check2?appid=%7B8E472B0D-3E8B-43b1-B89A-E8506AAF1F16%7D&appversion=3.4.5.6&applang=en-us&machine=1&version=5.6.78.1&osversion=");  // NOLINT
+
+  const CString prev_tmp = GetTmp();
+  EXPECT_TRUE(::SetEnvironmentVariable(_T("TMP"), kTempDirectory));
+
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kFullMachineOmahaClientKeyPath,
+                                            _T("pv"),
+                                            _T("5.6.78.1")));
+
+  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
+                                               kDummyAppVersion,
+                                               kDummyAppLang,
+                                               true,
+                                               DownloadFileNoFile,
+                                               NULL));
+
+  EXPECT_STREQ(kExpectedUrl, saved_url_.Left(arraysize(kExpectedUrl) - 1));
+  CheckSavedUrlOSFragment();
+  VerifyExpectedSavedFilePath(kTempDirectory);
+
+  EXPECT_TRUE(::SetEnvironmentVariable(_T("TMP"), prev_tmp));
+}
+
+TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
+       FixGoogleUpdate_AllValues_UserApp) {
+  const TCHAR kExpectedUrl[] = _T("http://cr-tools.clients.google.com/service/check2?appid=%7B8E472B0D-3E8B-43b1-B89A-E8506AAF1F16%7D&appversion=3.4.5.6&applang=en-us&machine=0&version=5.6.78.1&osversion=");  // NOLINT
+
+  const CString prev_tmp = GetTmp();
+  EXPECT_TRUE(::SetEnvironmentVariable(_T("TMP"), kTempDirectory));
+
+  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kFullUserOmahaClientKeyPath,
+                                            _T("pv"),
+                                            _T("5.6.78.1")));
+
+  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
+                                               kDummyAppVersion,
+                                               kDummyAppLang,
+                                               false,
+                                               DownloadFileNoFile,
+                                               NULL));
+
+  EXPECT_STREQ(kExpectedUrl, saved_url_.Left(arraysize(kExpectedUrl) - 1));
+  CheckSavedUrlOSFragment();
+  VerifyExpectedSavedFilePath(kTempDirectory);
+
+  EXPECT_TRUE(::SetEnvironmentVariable(_T("TMP"), prev_tmp));
+}
+
+TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
+       FixGoogleUpdate_NoOmahaRegKeys) {
+  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
+                                               kDummyAppVersion,
+                                               kDummyAppLang,
+                                               true,
+                                               DownloadFileNoFile,
+                                               NULL));
+  EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues,
+               saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength));
+  CheckSavedUrlOSFragment();
+}
+
+TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
+       FixGoogleUpdate_EmptyAppInfo) {
+  const TCHAR kExpectedUrl[] = _T("http://cr-tools.clients.google.com/service/check2?appid=&appversion=&applang=&machine=1&version=0.0.0.0&osversion=");  // NOLINT
+
+  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(_T(""),
+                                               _T(""),
+                                               _T(""),
+                                               true,
+                                               DownloadFileNoFile,
+                                               NULL));
+  EXPECT_STREQ(kExpectedUrl, saved_url_.Left(arraysize(kExpectedUrl) - 1));
+  CheckSavedUrlOSFragment();
+}
+
+TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
+       FixGoogleUpdate_NullArgs) {
+  EXPECT_EQ(E_INVALIDARG, FixGoogleUpdate(NULL,
+                                          _T(""),
+                                          _T(""),
+                                          true,
+                                          DownloadFileNoFile,
+                                          NULL));
+  EXPECT_EQ(E_INVALIDARG, FixGoogleUpdate(_T(""),
+                                          NULL,
+                                          _T(""),
+                                          true,
+                                          DownloadFileNoFile,
+                                          NULL));
+  EXPECT_EQ(E_INVALIDARG, FixGoogleUpdate(_T(""),
+                                          _T(""),
+                                          NULL,
+                                          true,
+                                          DownloadFileNoFile,
+                                          NULL));
+  EXPECT_EQ(E_INVALIDARG, FixGoogleUpdate(_T(""),
+                                          _T(""),
+                                          _T(""),
+                                          true,
+                                          NULL,
+                                          NULL));
+}
+
+// Setting kRegValueAutoUpdateCheckPeriodOverrideMinutes to zero disables
+// Code Red checks just as it does regular update checks.
+TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
+       FixGoogleUpdate_AutoUpdateCheckPeriodMinutesIsZeroDword) {
+  EXPECT_HRESULT_SUCCEEDED(
+      RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
+                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
+                       static_cast<DWORD>(0)));
+
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_ACCESS_DISABLED_BY_POLICY),
+            FixGoogleUpdate(kDummyAppGuid,
+                            kDummyAppVersion,
+                            kDummyAppLang,
+                            true,
+                            DownloadFileNoFile,
+                            NULL));
+  EXPECT_TRUE(saved_url_.IsEmpty());
+}
+
+TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
+       FixGoogleUpdate_AutoUpdateCheckPeriodMinutesIsZeroDwordInHkcu) {
+  EXPECT_HRESULT_SUCCEEDED(
+      RegKey::SetValue(USER_KEY GOOPDATE_POLICIES_RELATIVE,
+                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
+                       static_cast<DWORD>(0)));
+
+  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
+                                               kDummyAppVersion,
+                                               kDummyAppLang,
+                                               true,
+                                               DownloadFileNoFile,
+                                               NULL));
+  EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues,
+               saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength));
+  CheckSavedUrlOSFragment();
+}
+
+TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
+       FixGoogleUpdate_AutoUpdateCheckPeriodMinutesIsNonZeroDword) {
+  EXPECT_HRESULT_SUCCEEDED(
+      RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
+                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
+                       static_cast<DWORD>(1400)));
+
+  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
+                                               kDummyAppVersion,
+                                               kDummyAppLang,
+                                               true,
+                                               DownloadFileNoFile,
+                                               NULL));
+  EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues,
+               saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength));
+  CheckSavedUrlOSFragment();
+}
+
+TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
+       FixGoogleUpdate_AutoUpdateCheckPeriodMinutesIsZeroDword64) {
+  EXPECT_HRESULT_SUCCEEDED(
+      RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
+                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
+                       static_cast<DWORD64>(0)));
+
+  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
+                                               kDummyAppVersion,
+                                               kDummyAppLang,
+                                               true,
+                                               DownloadFileNoFile,
+                                               NULL));
+  EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues,
+               saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength));
+  CheckSavedUrlOSFragment();
+}
+
+TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
+       FixGoogleUpdate_AutoUpdateCheckPeriodMinutesIsNonZeroDword64) {
+  EXPECT_HRESULT_SUCCEEDED(
+      RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
+                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
+                       static_cast<DWORD64>(1400)));
+
+  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
+                                               kDummyAppVersion,
+                                               kDummyAppLang,
+                                               true,
+                                               DownloadFileNoFile,
+                                               NULL));
+  EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues,
+               saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength));
+  CheckSavedUrlOSFragment();
+}
+
+TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
+       FixGoogleUpdate_AutoUpdateCheckPeriodMinutesIsZeroAsString) {
+  EXPECT_HRESULT_SUCCEEDED(
+      RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
+                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
+                       _T("0")));
+
+  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
+                                               kDummyAppVersion,
+                                               kDummyAppLang,
+                                               true,
+                                               DownloadFileNoFile,
+                                               NULL));
+  EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues,
+               saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength));
+  CheckSavedUrlOSFragment();
+}
+
+TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
+       FixGoogleUpdate_AutoUpdateCheckPeriodMinutesIsZeroAsBinary) {
+  const byte zero = 0;
+  EXPECT_HRESULT_SUCCEEDED(
+      RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
+                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
+                       &zero,
+                       sizeof(zero)));
+
+  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
+                                               kDummyAppVersion,
+                                               kDummyAppLang,
+                                               true,
+                                               DownloadFileNoFile,
+                                               NULL));
+  EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues,
+               saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength));
+  CheckSavedUrlOSFragment();
+}
+
+TEST_F(GoogleUpdateRecoveryRegistryProtectedTest,
+       FixGoogleUpdate_GroupPolicyKeyExistsButNoAutoUpdateCheckPeriodMinutes) {
+  EXPECT_HRESULT_SUCCEEDED(RegKey::CreateKey(kRegKeyGoopdateGroupPolicy));
+
+  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
+                                               kDummyAppVersion,
+                                               kDummyAppLang,
+                                               true,
+                                               DownloadFileNoFile,
+                                               NULL));
+  EXPECT_STREQ(kExpectedUrlForDummyAppAndNoOmahaValues,
+               saved_url_.Left(kExpectedUrlForDummyAppAndNoOmahaValuesLength));
+  CheckSavedUrlOSFragment();
+}
+
+// Verifies that the file is saved even if the temp directory doesn't exist.
+TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_SaveToNonExistantDirectory) {
+  const TCHAR kNonExistantDirectory[] = _T("c:\\directory_does_not_exist");
+  DeleteDirectory(kNonExistantDirectory);
+  EXPECT_FALSE(File::Exists(kNonExistantDirectory));
+
+  const CString prev_tmp = GetTmp();
+  EXPECT_TRUE(::SetEnvironmentVariable(_T("TMP"), kNonExistantDirectory));
+
+  EXPECT_EQ(TRUST_E_SUBJECT_FORM_UNKNOWN,
+            FixGoogleUpdate(kDummyAppGuid,
+                            kDummyAppVersion,
+                            kDummyAppLang,
+                            true,
+                            DownloadFileInvalidFile,
+                            NULL));
+
+  VerifyExpectedSavedFilePath(kNonExistantDirectory);
+
+  EXPECT_TRUE(::SetEnvironmentVariable(_T("TMP"), prev_tmp));
+  EXPECT_HRESULT_SUCCEEDED(DeleteDirectory(kNonExistantDirectory));
+}
+
+TEST_F(GoogleUpdateRecoveryTest, FixGoogleUpdate_FileCollision) {
+  const CString prev_tmp = GetTmp();
+  EXPECT_TRUE(::SetEnvironmentVariable(_T("TMP"), kTempDirectory));
+
+  CString saved_arguments_path = ConcatenatePath(app_util::GetTempDir(),
+                                                 kSavedArgumentsFileName);
+
+  EXPECT_HRESULT_SUCCEEDED(FixGoogleUpdate(kDummyAppGuid,
+                                           kDummyAppVersion,
+                                           kDummyAppLang,
+                                           false,
+                                           DownloadArgumentSavingFile,
+                                           NULL));
+
+  EXPECT_TRUE(File::Exists(saved_file_path_));
+  VerifyExpectedSavedFilePath(kTempDirectory);
+
+  CString first_saved_file_path = saved_file_path_;
+
+  // Ensure that the first downloaded file is in use.
+  FileLock lock;
+  EXPECT_HRESULT_SUCCEEDED(lock.Lock(first_saved_file_path));
+
+  EXPECT_HRESULT_SUCCEEDED(FixGoogleUpdate(kDummyAppGuid,
+                                           kDummyAppVersion,
+                                           kDummyAppLang,
+                                           false,
+                                           DownloadArgumentSavingFile,
+                                           NULL));
+  EXPECT_TRUE(File::Exists(saved_file_path_));
+  VerifyExpectedSavedFilePath(kTempDirectory);
+
+  EXPECT_STRNE(first_saved_file_path, saved_file_path_);
+
+  EXPECT_HRESULT_SUCCEEDED(lock.Unlock());
+
+  bool is_deleted = false;
+  for (int tries = 0; tries < 100 && !is_deleted; ++tries) {
+    ::Sleep(50);
+    is_deleted = !!::DeleteFile(saved_file_path_);
+  }
+  EXPECT_TRUE(is_deleted);
+
+  EXPECT_TRUE(::DeleteFile(first_saved_file_path));
+  EXPECT_TRUE(::DeleteFile(saved_arguments_path));
+
+  EXPECT_TRUE(::SetEnvironmentVariable(_T("TMP"), prev_tmp));
+}
+
+//
+// VerifyFileSignature Tests
+//
+TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_SignedValid) {
+  CString executable_full_path(app_util::GetCurrentModuleDirectory());
+  EXPECT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kArgumentSavingExecutableRelativePath));
+  EXPECT_TRUE(File::Exists(executable_full_path));
+  EXPECT_HRESULT_SUCCEEDED(VerifyFileSignature(executable_full_path));
+}
+
+TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_NotSigned) {
+  const TCHAR kUnsignedExecutable[] = _T("GoogleUpdate_unsigned.exe");
+
+  CString executable_full_path(app_util::GetCurrentModuleDirectory());
+  EXPECT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kUnsignedExecutable));
+  EXPECT_TRUE(File::Exists(executable_full_path));
+  EXPECT_EQ(TRUST_E_NOSIGNATURE, VerifyFileSignature(executable_full_path));
+}
+
+// The certificate is still valid, but the executable was signed more than N
+// days ago.
+TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_SignedOldWithValidCert) {
+  const TCHAR kRelativePath[] =
+      _T("unittest_support\\GoogleUpdate_old_signature.exe");
+
+  CString executable_full_path(app_util::GetCurrentModuleDirectory());
+  EXPECT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kRelativePath));
+  EXPECT_TRUE(File::Exists(executable_full_path));
+  EXPECT_EQ(TRUST_E_TIME_STAMP, VerifyFileSignature(executable_full_path));
+}
+
+// The certificate was valid when it was used to sign the executable, but it has
+// since expired.
+// TRUST_E_TIME_STAMP is returned because the file was signed more than the
+// allowable number of dates ago for the repair file. Otherwise, the signature
+// is fine.
+TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_SignedWithNowExpiredCert) {
+  const TCHAR kRelativePath[] =
+      _T("unittest_support\\GoogleUpdate_now_expired_cert.exe");
+
+  CString executable_full_path(app_util::GetCurrentModuleDirectory());
+  EXPECT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kRelativePath));
+  EXPECT_TRUE(File::Exists(executable_full_path));
+  EXPECT_EQ(TRUST_E_TIME_STAMP, VerifyFileSignature(executable_full_path));
+}
+
+TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_UntrustedChain) {
+  const TCHAR kUntrustedChainExecutable[] =
+      _T("unittest_support\\SaveArguments_OmahaTestSigned.exe");
+
+  CString executable_full_path(app_util::GetCurrentModuleDirectory());
+  EXPECT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kUntrustedChainExecutable));
+  EXPECT_TRUE(File::Exists(executable_full_path));
+  EXPECT_EQ(CERT_E_UNTRUSTEDROOT, VerifyFileSignature(executable_full_path));
+}
+
+TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_HashFails) {
+  const TCHAR kCorruptedExecutable[] =
+      _T("unittest_support\\GoogleUpdate_corrupted.exe");
+
+  CString executable_full_path(app_util::GetCurrentModuleDirectory());
+  EXPECT_TRUE(::PathAppend(CStrBuf(executable_full_path, MAX_PATH),
+                           kCorruptedExecutable));
+  EXPECT_TRUE(File::Exists(executable_full_path));
+  EXPECT_EQ(TRUST_E_BAD_DIGEST, VerifyFileSignature(executable_full_path));
+}
+
+// The file for Windows Vista and later may not exist on all systems.
+TEST_F(GoogleUpdateRecoveryTest,
+       VerifyFileSignature_NonGoogleSignature) {
+  CString file_path = SystemInfo::IsRunningOnVistaOrLater() ?
+      _T("%SYSTEM%\\rcagent.exe") : _T("%SYSTEM%\\wuauclt.exe");
+  if (!File::Exists(file_path) && SystemInfo::IsRunningOnVistaOrLater()) {
+    std::wcout << _T("\tTest did not run because '") << file_path
+               << _T("' was not found.") << std::endl;
+    return;
+  }
+  EXPECT_HRESULT_SUCCEEDED(ExpandStringWithSpecialFolders(&file_path));
+  EXPECT_TRUE(File::Exists(file_path));
+  EXPECT_TRUE(SignatureIsValid(file_path, false));
+  EXPECT_EQ(CERT_E_CN_NO_MATCH, VerifyFileSignature(file_path));
+}
+
+TEST_F(GoogleUpdateRecoveryTest, VerifyFileSignature_BadFilenames) {
+  EXPECT_EQ(CRYPT_E_FILE_ERROR, VerifyFileSignature(_T("NoSuchFile.exe")));
+
+  EXPECT_EQ(CRYPT_E_FILE_ERROR, VerifyFileSignature(NULL));
+
+  EXPECT_EQ(CRYPT_E_FILE_ERROR, VerifyFileSignature(_T("")));
+}
+
+//
+// VerifyRepairFileMarkup Tests
+//
+TEST_F(GoogleUpdateRecoveryTest, VerifyRepairFileMarkup_ValidMarkup) {
+  const TCHAR kExecutableWithMarkup[] =
+      _T("unittest_support\\SaveArguments.exe");
+  EXPECT_HRESULT_SUCCEEDED(VerifyRepairFileMarkup(kExecutableWithMarkup));
+}
+
+TEST_F(GoogleUpdateRecoveryTest, VerifyRepairFileMarkup_InvalidMarkups) {
+  const TCHAR kNoResourcesExecutable[] =
+      _T("unittest_support\\SaveArguments_unsigned_no_resources.exe");
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_RESOURCE_DATA_NOT_FOUND),
+            VerifyRepairFileMarkup(kNoResourcesExecutable));
+
+  const TCHAR kResourcesButNoMarkupExecutable[] = _T("GoogleUpdate.exe");
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_RESOURCE_TYPE_NOT_FOUND),
+            VerifyRepairFileMarkup(kResourcesButNoMarkupExecutable));
+
+  const TCHAR kWrongMarkupResourceNameExecutable[] =
+      _T("unittest_support\\SaveArguments_unsigned_wrong_resource_name.exe");
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_RESOURCE_NAME_NOT_FOUND),
+            VerifyRepairFileMarkup(kWrongMarkupResourceNameExecutable));
+
+  const TCHAR kWrongMarkupSizeExecutable[] =
+      _T("unittest_support\\SaveArguments_unsigned_wrong_markup_size.exe");
+  EXPECT_EQ(E_UNEXPECTED, VerifyRepairFileMarkup(kWrongMarkupSizeExecutable));
+
+  const TCHAR kWrongMarkupValueExecutable[] =
+      _T("unittest_support\\SaveArguments_unsigned_wrong_markup_value.exe");
+  EXPECT_EQ(E_UNEXPECTED, VerifyRepairFileMarkup(kWrongMarkupValueExecutable));
+}
+
+TEST_F(GoogleUpdateRecoveryTest, VerifyRepairFileMarkup_BadFilenames) {
+  const TCHAR kMissingFile[] = _T("NoSuchFile.exe");
+  EXPECT_EQ(FALSE, ::PathFileExists(kMissingFile));
+  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
+            VerifyRepairFileMarkup(kMissingFile));
+  EXPECT_HRESULT_FAILED(VerifyRepairFileMarkup(_T("")));
+}
+
+//
+// VerifyRepairFileMarkup Tests
+//
+// TODO(omaha): Unit test VerifyIsValidRepairFile.
+
+//
+// Production Server Response Tests Tests
+//
+TEST_F(GoogleUpdateRecoveryTest, ProductionServerResponseTest) {
+  EXPECT_EQ(kDummyNoFileError, FixGoogleUpdate(kDummyAppGuid,
+                                               kDummyAppVersion,
+                                               kDummyAppLang,
+                                               true,
+                                               DownloadFileFromServer,
+                                               NULL)) <<
+      _T("The production server did not return 204. This may indicate network ")
+      _T("issues or that the Code Red server is configured incorrectly");
+}
+
+}  // namespace omaha
+
diff --git a/recovery/recovery_test.cc b/recovery/recovery_test.cc
index 5bf1d90..a99d33c 100644
--- a/recovery/recovery_test.cc
+++ b/recovery/recovery_test.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -16,19 +16,20 @@
 // Test app for the Google Update recovery mechanism.
 #include <windows.h>
 #include <atlstr.h>
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/google_update_recovery.h"
-#include "omaha/common/logging.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
 #include "omaha/net/network_config.h"
 #include "omaha/net/network_request.h"
+#include "omaha/recovery/client/google_update_recovery.h"
 
-using omaha::GoogleProxyDetector;
+using omaha::UpdateDevProxyDetector;
 using omaha::FirefoxProxyDetector;
 using omaha::IEProxyDetector;
 using omaha::NetworkRequest;
 using omaha::NetworkConfig;
+using omaha::NetworkConfigManager;
 
 namespace {
 
@@ -52,15 +53,23 @@
 #endif
 
   // Initialize the network for user with no impersonation required.
-  NetworkConfig& network_config = NetworkConfig::Instance();
-  network_config.Initialize(false, NULL);
-  network_config.Add(new GoogleProxyDetector(MACHINE_REG_UPDATE_DEV));
-  network_config.Add(new FirefoxProxyDetector());
-  network_config.Add(new IEProxyDetector());
+  NetworkConfigManager::set_is_machine(false);
+  NetworkConfig* network_config = NULL;
+  HRESULT hr = S_OK;
+  hr = NetworkConfigManager::Instance().GetUserNetworkConfig(&network_config);
+  if (FAILED(hr)) {
+    UTIL_LOG(LE, (_T("[GetUserNetworkConfig failed][0x%08x]"), hr));
+    return hr;
+  }
+  network_config->Clear();
+  network_config->Add(new UpdateDevProxyDetector);
+  network_config->Add(new FirefoxProxyDetector);
+  network_config->Add(new IEProxyDetector);
 
-  NetworkRequest network_request(network_config.session());
-  HRESULT hr = network_request.DownloadFile(test_url, CString(file_path));
+  NetworkRequest network_request(network_config->session());
+  hr = network_request.DownloadFile(test_url, CString(file_path));
 
+  hr = network_request.DownloadFile(test_url, CString(file_path));
   if (FAILED(hr)) {
     UTIL_LOG(LE, (_T("[DownloadFile failed][%s][0x%08x]"), test_url, hr));
     return hr;
diff --git a/recovery/repair_exe/build.scons b/recovery/repair_exe/build.scons
index bce4c3e..541d963 100644
--- a/recovery/repair_exe/build.scons
+++ b/recovery/repair_exe/build.scons
@@ -21,26 +21,24 @@
 
 local_env = env.Clone()
 
-target_name = 'repair_goopdate.lib'
-
 inputs = [
     'mspexecutableelevator.cc',
     'repair_goopdate.cc',
     ]
-if env.Bit('use_precompiled_headers'):
-  inputs += local_env.EnablePrecompile(target_name)
 
-local_env.ComponentLibrary(target_name, inputs)
+repair_goopdate_lib = local_env.ComponentStaticLibrary('repair_goopdate',
+                                                       inputs)
 
+local_env.OmahaUnittest(
+    name='repair_exe_custom_action_unittest',
+    source=['mspexecutableelevator_unittest.cc', 'repair_goopdate_unittest.cc'],
+    LIBS=repair_goopdate_lib,
+)
 
 subdirs = [
     'custom_action',
+    'msp',
     ]
 
-if not env.Bit('min'):
-  subdirs += [
-      'msp',
-      ]
-
 for subdir in subdirs:
   env.BuildSConscript(subdir)
diff --git a/recovery/repair_exe/custom_action/build.scons b/recovery/repair_exe/custom_action/build.scons
index ded5c3b..81942ed 100644
--- a/recovery/repair_exe/custom_action/build.scons
+++ b/recovery/repair_exe/custom_action/build.scons
@@ -22,6 +22,31 @@
 #
 # Build executecustomaction.dll
 #
+
+lib_target_name = 'executecustomaction_lib'
+
+lib_env = env.Clone()
+
+lib_inputs = [
+    'execute_repair_file.cc',
+    ]
+if env.Bit('use_precompiled_headers'):
+  lib_inputs += lib_env.EnablePrecompile(lib_target_name)
+
+lib_output = lib_env.ComponentLibrary(
+    lib_name=lib_target_name,
+    source=lib_inputs,
+)
+
+lib_env.OmahaUnittest(
+    name='repair_exe_custom_action_unittest',
+    source='execute_repair_file_unittest.cc',
+    LIBS=lib_target_name + '.lib',
+)
+
+
+dll_target_name = 'executecustomaction'
+
 dll_env = env.Clone(
     # Build a dll, not a lib.
     COMPONENT_STATIC = False,
@@ -32,14 +57,14 @@
         ('atls.lib', 'atlsd.lib')[dll_env.Bit('debug')],
         ('libcmt.lib', 'libcmtd.lib')[dll_env.Bit('debug')],
         ('libcpmt.lib', 'libcpmtd.lib')[dll_env.Bit('debug')],
-        '$LIB_DIR/common.lib',
+        '$LIB_DIR/base.lib',
         '$LIB_DIR/google_update_recovery.lib',
         '$LIB_DIR/repair_goopdate.lib',
         'crypt32.lib',
         'msi.lib',
         'wintrust.lib',
 
-        # These are required by common_lib
+        # These are required by base.lib
         'netapi32.lib',
         'psapi.lib',
         'rasapi32.lib',
@@ -47,6 +72,7 @@
         'userenv.lib',
         'version.lib',
         'wtsapi32.lib',
+        lib_target_name + '.lib',
         ],
 
     CPPDEFINES = [
@@ -54,18 +80,15 @@
         ],
 )
 
-target_name = 'executecustomaction.dll'
-
 dll_inputs = [
-    'execute_repair_file.cc',
     'executecustomaction.cc',
     'executecustomaction.def',
     ]
 if env.Bit('use_precompiled_headers'):
-  dll_inputs += dll_env.EnablePrecompile(target_name)
+  dll_inputs += dll_env.EnablePrecompile(dll_target_name)
 
 dll_env.ComponentLibrary(
-    lib_name=target_name,
+    lib_name=dll_target_name,
     source=dll_inputs,
 )
 
@@ -73,18 +96,18 @@
 #
 # Build testelevateusingmsp.exe
 #
-exe_env = env.Clone()
+test_env = env.Clone()
 
-exe_env['LIBS'] += [
-    ('atls.lib', 'atlsd.lib')[exe_env.Bit('debug')],
-    ('libcmt.lib', 'libcmtd.lib')[exe_env.Bit('debug')],
-    ('libcpmt.lib', 'libcpmtd.lib')[exe_env.Bit('debug')],
-    '$LIB_DIR/common.lib',
+test_env['LIBS'] += [
+    ('atls.lib', 'atlsd.lib')[test_env.Bit('debug')],
+    ('libcmt.lib', 'libcmtd.lib')[test_env.Bit('debug')],
+    ('libcpmt.lib', 'libcpmtd.lib')[test_env.Bit('debug')],
+    '$LIB_DIR/base.lib',
     '$LIB_DIR/repair_goopdate.lib',
     'crypt32.lib',
     'msi.lib',
     'wintrust.lib',
-    # These are required by common_lib
+    # These are required by base.lib
     'netapi32.lib',
     'psapi.lib',
     'rasapi32.lib',
@@ -95,19 +118,20 @@
     ]
 
 # This is a console application.
-exe_env.FilterOut(LINKFLAGS = ['/SUBSYSTEM:WINDOWS'])
-exe_env['LINKFLAGS'] += ['/SUBSYSTEM:CONSOLE']
+test_env.FilterOut(LINKFLAGS = ['/SUBSYSTEM:WINDOWS'])
+test_env['LINKFLAGS'] += ['/SUBSYSTEM:CONSOLE']
 
 target_name = 'testelevateusingmsp'
 
-exe_inputs = [
+test_inputs = [
     'testelevateusingmsp.cc',
     ]
 if env.Bit('use_precompiled_headers'):
-  exe_inputs += exe_env.EnablePrecompile(target_name)
+  test_inputs += test_env.EnablePrecompile(target_name)
 
-exe_env.ComponentTestProgram(
+# This test requires arguments, so do not create a run alias.
+test_env.ComponentTestProgram(
     prog_name=target_name,
-    source=exe_inputs,
+    source=test_inputs,
     COMPONENT_TEST_RUNNABLE=False
 )
diff --git a/recovery/repair_exe/custom_action/execute_repair_file.cc b/recovery/repair_exe/custom_action/execute_repair_file.cc
index b830024..f6d1af1 100644
--- a/recovery/repair_exe/custom_action/execute_repair_file.cc
+++ b/recovery/repair_exe/custom_action/execute_repair_file.cc
@@ -17,14 +17,14 @@
 
 #include "omaha/recovery/repair_exe/custom_action/execute_repair_file.h"
 #include <shlobj.h>
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/path.h"
-#include "omaha/common/string.h"
-#include "omaha/common/system.h"
-#include "omaha/common/utils.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/string.h"
+#include "omaha/base/system.h"
+#include "omaha/base/utils.h"
 
 namespace omaha {
 
diff --git a/recovery/repair_exe/custom_action/execute_repair_file_unittest.cc b/recovery/repair_exe/custom_action/execute_repair_file_unittest.cc
index f534e89..1999970 100644
--- a/recovery/repair_exe/custom_action/execute_repair_file_unittest.cc
+++ b/recovery/repair_exe/custom_action/execute_repair_file_unittest.cc
@@ -16,10 +16,10 @@
 //
 // Unit tests for the file execution module of the MSP custom action.
 
-#include "omaha/common/app_util.h"
-#include "omaha/common/file.h"
-#include "omaha/common/path.h"
-#include "omaha/common/vistautil.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/vistautil.h"
 #include "omaha/recovery/repair_exe/custom_action/execute_repair_file.h"
 #include "omaha/testing/unit_test.h"
 
diff --git a/recovery/repair_exe/custom_action/executecustomaction.cc b/recovery/repair_exe/custom_action/executecustomaction.cc
index 4261b4f..2f1a5d4 100644
--- a/recovery/repair_exe/custom_action/executecustomaction.cc
+++ b/recovery/repair_exe/custom_action/executecustomaction.cc
@@ -20,8 +20,8 @@
 
 
 #include "omaha/recovery/repair_exe/custom_action/executecustomaction.h"
-#include <Msiquery.h>
-#include "omaha/common/debug.h"
+#include <msiquery.h>
+#include "omaha/base/debug.h"
 #include "omaha/recovery/repair_exe/mspexecutableelevator.h"
 #include "omaha/recovery/repair_exe/custom_action/execute_repair_file.h"
 
diff --git a/recovery/repair_exe/custom_action/testelevateusingmsp.cc b/recovery/repair_exe/custom_action/testelevateusingmsp.cc
index d1be88e..f1f9714 100644
--- a/recovery/repair_exe/custom_action/testelevateusingmsp.cc
+++ b/recovery/repair_exe/custom_action/testelevateusingmsp.cc
@@ -18,7 +18,7 @@
 
 #include <windows.h>
 #include <atlstr.h>
-#include "omaha/common/constants.h"
+#include "omaha/base/constants.h"
 #include "omaha/recovery/repair_exe/mspexecutableelevator.h"
 
 int main(int argc, char** argv) {
diff --git a/recovery/repair_exe/msp/build.scons b/recovery/repair_exe/msp/build.scons
index fdcf789..cbe8e6b 100644
--- a/recovery/repair_exe/msp/build.scons
+++ b/recovery/repair_exe/msp/build.scons
@@ -24,9 +24,10 @@
 
 Import('env')
 
+_repair_exe_obj_dir = '$OBJ_ROOT/recovery/repair_exe/'
 
 _custom_actions_path = (
-    '$OBJ_ROOT/recovery/repair_exe/custom_action/executecustomaction.dll')
+    _repair_exe_obj_dir + 'custom_action/executecustomaction.dll')
 _cert_file = env.File(GetOption('patching_certificate')).abspath
 _required_file = '$MAIN_DIR/recovery/repair_exe/msp/requiredfile.txt'
 
@@ -75,13 +76,12 @@
         ],
 )
 
-# Output to a subdirectory to avoid having
-# 2 intermediate wixobj files with the same name and path.
+# Output to a subdirectory to avoid build breaks caused by two different actions
+# referencing files with the same name and path.
 new_unsigned_env['WIXOBJPREFIX'] = new_unsigned_env['WIXOBJPREFIX'] + 'new/'
-new_unsigned_env['WIXMSIPREFIX'] = new_unsigned_env['WIXMSIPREFIX'] + 'new/'
 
 new_unsigned_output = new_unsigned_env.WiX(
-    target='GoogleUpdateHelper_unsigned.msi',
+    target='new/GoogleUpdateHelper_unsigned.msi',
     source='patchableinstaller.wxs'
 )
 
@@ -132,7 +132,7 @@
   rm_rf(_temp_dir)
 
 unsigned_msp_name = 'GoogleUpdateHelperPatch_unsigned.msp'
-unsigned_msp_path = '$OBJ_ROOT/recovery/repair_exe/msp/' + unsigned_msp_name
+unsigned_msp_path = _repair_exe_obj_dir + 'msp/' + unsigned_msp_name
 
 msp_output = msp_env.Command(
     target=unsigned_msp_name,
diff --git a/recovery/repair_exe/msp/patch.wxs b/recovery/repair_exe/msp/patch.wxs
index 7e68cb4..c46dcef 100644
--- a/recovery/repair_exe/msp/patch.wxs
+++ b/recovery/repair_exe/msp/patch.wxs
@@ -16,6 +16,7 @@
       Compressed="no"
       SummaryCodepage="1252" />
 
+    <!--TODO(omaha): Use kUrlMoreInformation for MoreInfoURL.-->
     <PatchMetadata
       Description="Patches Google Update"
       DisplayName="Google Update patch"
diff --git a/recovery/repair_exe/msp/testApplying300Patches.bat b/recovery/repair_exe/msp/testApplying300Patches.bat
index 6936dd2..db59a5b 100644
--- a/recovery/repair_exe/msp/testApplying300Patches.bat
+++ b/recovery/repair_exe/msp/testApplying300Patches.bat
@@ -11,13 +11,13 @@
 
 msiexec /update GoogleUpdateHelperPatch.msp REINSTALL=ALL /qn /L*v patchapply%ii%.log
 echo patch %ii% applied
-if %ii% GEQ 127	pause
+if %ii% GEQ 127 pause
 msiexec /uninstall {E0D0D2C9-5836-4023-AB1D-54EC3B90AD03} /package {A92DAB39-4E2C-4304-9AB6-BC44E68B55E2} /qn /L*v patchremove%ii%.log
 echo patch %ii% removed
 
 set /A ii=%ii%+1
 
-if %ii% NEQ 300	goto repeat
-rem if %ii% NEQ 127	goto repeat
+if %ii% NEQ 300 goto repeat
+rem if %ii% NEQ 127 goto repeat
 
 set ii=
diff --git a/recovery/repair_exe/mspexecutableelevator.cc b/recovery/repair_exe/mspexecutableelevator.cc
index c52a4b3..cdf571b 100644
--- a/recovery/repair_exe/mspexecutableelevator.cc
+++ b/recovery/repair_exe/mspexecutableelevator.cc
@@ -29,8 +29,9 @@
 #include "omaha/recovery/repair_exe/mspexecutableelevator.h"
 #include <atlpath.h>
 #include <msi.h>
-#include "omaha/common/debug.h"
-#include "omaha/common/string.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/string.h"
 
 namespace omaha {
 
@@ -94,12 +95,13 @@
         // shared memory just created, the current process id, the executable
         // to launch, and the executable's arguments.
         CString command_line;
-        command_line.Format(_T("EXECUTABLECOMMANDLINE=\"%s %u \"\"%s\"\" %s\" ")
-                            _T("REINSTALL=ALL"),
-                            shared_memory_name,
-                            GetCurrentProcessId(),
-                            exe,
-                            args);
+        SafeCStringFormat(&command_line,
+                          _T("EXECUTABLECOMMANDLINE=\"%s %u \"\"%s\"\" %s\" ")
+                          _T("REINSTALL=ALL"),
+                          shared_memory_name,
+                          GetCurrentProcessId(),
+                          exe,
+                          args);
         // Generate path to patch using path to current module.
         TCHAR module_name[MAX_PATH] = {0};
         DWORD len = ::GetModuleFileName(_AtlBaseModule.GetModuleInstance(),
diff --git a/recovery/repair_exe/mspexecutableelevator_unittest.cc b/recovery/repair_exe/mspexecutableelevator_unittest.cc
index d3bf36a..4259014 100644
--- a/recovery/repair_exe/mspexecutableelevator_unittest.cc
+++ b/recovery/repair_exe/mspexecutableelevator_unittest.cc
@@ -22,13 +22,13 @@
 
 #include <windows.h>
 #include <msi.h>
-#include "omaha/common/app_util.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/file.h"
-#include "omaha/common/path.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
 #include "omaha/recovery/repair_exe/mspexecutableelevator.h"
 #include "omaha/setup/msi_test_utils.h"
 #include "omaha/testing/unit_test.h"
diff --git a/recovery/repair_exe/repair_goopdate.cc b/recovery/repair_exe/repair_goopdate.cc
index 16cb32f..f881bf4 100644
--- a/recovery/repair_exe/repair_goopdate.cc
+++ b/recovery/repair_exe/repair_goopdate.cc
@@ -17,10 +17,10 @@
 // elevated using the MSP.
 
 #include "omaha/recovery/repair_exe/repair_goopdate.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
 #include "omaha/recovery/repair_exe/mspexecutableelevator.h"
 
 namespace omaha {
diff --git a/recovery/repair_exe/repair_goopdate_unittest.cc b/recovery/repair_exe/repair_goopdate_unittest.cc
index d3422c1..5335098 100644
--- a/recovery/repair_exe/repair_goopdate_unittest.cc
+++ b/recovery/repair_exe/repair_goopdate_unittest.cc
@@ -15,11 +15,11 @@
 //
 // Unit tests for the Omaha repair mechanism.
 
-#include "omaha/common/app_util.h"
-#include "omaha/common/file.h"
-#include "omaha/common/path.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/vistautil.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/vistautil.h"
 #include "omaha/recovery/repair_exe/repair_goopdate.h"
 #include "omaha/setup/msi_test_utils.h"
 #include "omaha/testing/unit_test.h"
diff --git a/run_unit_tests.py b/run_unit_tests.py
new file mode 100644
index 0000000..775c0b6
--- /dev/null
+++ b/run_unit_tests.py
@@ -0,0 +1,128 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2010 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========================================================================
+
+"""Runs a set of unit tests and returns success only if they all succeed.
+
+This script assumes it is being run from the omaha directory.
+
+To run unit tests for Omaha's default set of test directories, just run the file
+from the command line.
+"""
+
+
+import dircache
+import os
+
+TEST_EXECUTABLE_RHS = '_unittest.exe'
+
+# Build paths that contain tests.
+STAGING_PATH = 'scons-out\\dbg-win\\staging'
+TESTS_PATH = 'scons-out\\dbg-win\\tests'
+
+
+def RunTest(test_path):
+  """Runs a test and returns its exit code.
+
+    Assumes the tests can be run from any directory. In other words, it does not
+    chdir.
+
+  Args:
+    test_path: Path to test executables.
+
+  Returns:
+    The exit code from the test process.
+  """
+
+  print '\nRunning %s . . .\n' % test_path
+
+  # Put './' in front of the file name to avoid accidentally running a file with
+  # the same name in some other directory if test_path were just a file name.
+  return os.system(os.path.join('.', test_path))
+
+
+def RunTests(test_paths):
+  """Runs all tests specified by test_paths.
+
+  Args:
+    test_paths: A list of paths to test executables.
+
+  Returns:
+    0 if all tests are successful.
+    1 if some tests fail, or if there is an error.
+  """
+
+  if not test_paths or len(test_paths) < 1:
+    return 1
+
+  print 'Found the following tests to run:'
+  for test in test_paths:
+    print '\t%s' % test
+
+  # Run all tests and remembers those that failed.
+  failed_tests = [t for t in test_paths if RunTest(t)]
+
+  print '\n\n%s test executables were run.' % len(test_paths)
+
+  failed_test_count = len(failed_tests)
+  if failed_test_count:
+    # Lists the executables that failed so the user can investigate them.
+    print 'FAILED!'
+    print 'The following %s tests failed:\n' % failed_test_count
+    for test in failed_tests:
+      print test
+    return 1
+  else:
+    # No, there is none.
+    if test_paths:
+      print 'All of them PASSED!'
+    return 0
+
+
+def GetTestsInDirs(test_dirs):
+  """Returns a list of all unit test executables in test_dirs.
+
+    Does not search subdirectories.
+
+  Args:
+    test_dirs: A list of directories to search.
+
+  Returns:
+    List of all unit tests.
+  """
+
+  tests = []
+
+  for test_dir in test_dirs:
+    # Use dircache.listdir so order is alphabetical and thus deterministic.
+    files = dircache.listdir(test_dir)
+
+    for test_file in files:
+      if test_file.endswith(TEST_EXECUTABLE_RHS):
+        relative_path = os.path.join(test_dir, test_file)
+        if os.path.isfile(relative_path):
+          tests += [relative_path]
+
+  return tests
+
+# Run a unit test when the module is run directly.
+if __name__ == '__main__':
+  # List of paths that contain unit tests to run.
+  dirs_containing_tests = [STAGING_PATH, TESTS_PATH]
+
+  tests_to_run = GetTestsInDirs(dirs_containing_tests)
+
+  RunTests(tests_to_run)
diff --git a/service/service_main.cc b/service/service_main.cc
index 1a1d0fe..95322a6 100644
--- a/service/service_main.cc
+++ b/service/service_main.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2009 Google Inc.
+// Copyright 2009-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -13,442 +13,104 @@
 // limitations under the License.
 // ========================================================================
 //
-// The service can be started in one of two modes:
-//   * As a regular service, typically at system startup.
-//   * As a COM service, typically by an Omaha client using IGoogleUpdateCore.
-// The COM case is distinguished from the regular service case by registering a
-// ServiceParameters command line in the AppID registration.
-//
-// In all cases, the service initializes COM, and allows for IGoogleUpdateCore
-// clients to connect to the service. In the regular service case, the service
-// shuts down after a small idle check timeout, provided that there are no COM
-// clients connected. In the COM server case, and in the case where there are
-// COM clients connected in the regular service case, the service will shut down
-// when the last client disconnects.
-//
-// To be exact, the service will initiate shutdown in all cases when the ATL
-// module count drops to zero.
-//
-// ATL does not allow for directly reusing the delayed COM shutdown mechanism
-// available for Local servers. The assumption likely being that services run
-// forever. Since we do not want our service to run forever, we override some
-// of the functions to get the same effect.
-
+// Contains the ATL service.
 
 #include "omaha/service/service_main.h"
 
-#include "omaha/common/const_cmd_line.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/queue_timer.h"
-#include "omaha/core/google_update_core.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/command_line_builder.h"
-#include "omaha/goopdate/goopdate_utils.h"
-
 namespace omaha {
 
-_ATL_OBJMAP_ENTRY ServiceModule::object_map_[] = {
-  OBJECT_ENTRY(__uuidof(GoogleUpdateCoreClass), GoogleUpdateCore)
+// Template arguments need to be non-const TCHAR arrays.
+TCHAR kHKRootService[] = _T("HKLM");
+TCHAR kProgIDUpdate3COMClassServiceLocal[] = kProgIDUpdate3COMClassService;
+
+// A private object map with custom registration works best, even though this
+// stuff is deprecated. This is because GoogleUpdate.exe has other objects
+// defined elsewhere and we do not want to expose those from the service.
+BEGIN_OBJECT_MAP(object_map_google_update3)
+  OBJECT_ENTRY(__uuidof(GoogleUpdate3ServiceClass), Update3COMClassService)
 END_OBJECT_MAP()
 
-ServiceModule::ServiceModule()
-    : timer_queue_(NULL),
-      service_thread_(NULL),
-      is_service_com_server_(false) {
-  SERVICE_LOG(L1, (_T("[ServiceModule::ServiceModule]")));
-  _tcscpy(m_szServiceName, ConfigManager::GetCurrentServiceName());
+BEGIN_OBJECT_MAP(object_map_google_update_medium)
+  OBJECT_ENTRY(__uuidof(OnDemandMachineAppsServiceClass), OnDemandService)
+  OBJECT_ENTRY(__uuidof(GoogleUpdate3WebServiceClass), Update3WebService)
+  OBJECT_ENTRY(__uuidof(GoogleUpdateCoreClass), GoogleUpdateCoreService)
+END_OBJECT_MAP()
+
+CommandLineMode Update3ServiceMode::commandline_mode() {
+  return COMMANDLINE_MODE_SERVICE;
 }
 
-ServiceModule::~ServiceModule() {
-  SERVICE_LOG(L1, (_T("[ServiceModule::~ServiceModule]")));
-  ASSERT1(!service_thread_);
-  ASSERT1(!timer_queue_);
-  ASSERT1(!idle_check_timer_.get());
-
-  // ServiceModule is typically created on the stack. We therefore reset the
-  // global ATL Module to NULL when ServiceModule is destroyed.
-  _pAtlModule = NULL;
+CString Update3ServiceMode::reg_name() {
+  return kRegValueServiceName;
 }
 
-// Security descriptor for the Service's CoInitializeSecurity
-void GetCOMSecDesc(CSecurityDesc* security_descriptor) {
-  security_descriptor->SetOwner(Sids::Admins());
-  security_descriptor->SetGroup(Sids::Admins());
-  CDacl dacl;
-  dacl.AddAllowedAce(Sids::System(), COM_RIGHTS_EXECUTE);
-  dacl.AddAllowedAce(Sids::Admins(), COM_RIGHTS_EXECUTE);
-  dacl.AddAllowedAce(Sids::Interactive(), COM_RIGHTS_EXECUTE);
-  security_descriptor->SetDacl(dacl);
-  security_descriptor->MakeAbsolute();
+CString Update3ServiceMode::default_name() {
+  return kServicePrefix;
 }
 
-HRESULT ServiceModule::InitializeSecurity() throw() {
-  SERVICE_LOG(L3, (_T("[ServiceModule::InitializeSecurity]")));
-
-  CSecurityDesc security_descriptor;
-  GetCOMSecDesc(&security_descriptor);
-  HRESULT hr = ::CoInitializeSecurity(
-      const_cast<SECURITY_DESCRIPTOR*>(
-          security_descriptor.GetPSECURITY_DESCRIPTOR()),
-      -1,
-      NULL,
-      NULL,
-      RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
-      RPC_C_IMP_LEVEL_IDENTIFY,
-      NULL,
-      EOAC_DYNAMIC_CLOAKING | EOAC_DISABLE_AAA | EOAC_NO_CUSTOM_MARSHAL,
-      NULL);
-  ASSERT(SUCCEEDED(hr), (_T("[ServiceModule::InitializeSecurity][0x%x]"), hr));
-  return hr;
+DWORD Update3ServiceMode::service_start_type() {
+  return SERVICE_AUTO_START;
 }
 
-void ServiceModule::ServiceMain(DWORD argc, LPTSTR* argv) throw() {
-  ASSERT1(argc <= 2);
-  is_service_com_server_ =
-      argc == 2 && !CString(argv[1]).CompareNoCase(kCmdLineServiceComServer);
-  SERVICE_LOG(L3, (_T("[ServiceMain][is_service_com_server_][%d]"),
-                   is_service_com_server_));
-  Base::ServiceMain(argc, argv);
+_ATL_OBJMAP_ENTRY* Update3ServiceMode::object_map() {
+  return object_map_google_update3;
 }
 
-HRESULT ServiceModule::RegisterClassObjects(DWORD class_context,
-                                            DWORD flags) throw() {
-  SERVICE_LOG(L3, (_T("[ServiceModule::RegisterClassObjects]")));
-  for (_ATL_OBJMAP_ENTRY* objmap_entry = object_map_;
-       objmap_entry->pclsid != NULL;
-       objmap_entry++) {
-    HRESULT hr = objmap_entry->RegisterClassObject(class_context, flags);
-    if (FAILED(hr)) {
-      SERVICE_LOG(LE, (_T("[RegisterClassObject failed][%s][0x%x]"),
-                       GuidToString(*objmap_entry->pclsid), hr));
-      return hr;
-    }
-  }
-
-  return S_OK;
+bool Update3ServiceMode::allow_access_from_medium() {
+  return false;
 }
 
-HRESULT ServiceModule::RevokeClassObjects() throw() {
-  SERVICE_LOG(L3, (_T("[ServiceModule::RevokeClassObjects]")));
-  for (_ATL_OBJMAP_ENTRY* objmap_entry = object_map_;
-       objmap_entry->pclsid != NULL;
-       objmap_entry++) {
-    HRESULT hr = objmap_entry->RevokeClassObject();
-    if (FAILED(hr)) {
-      SERVICE_LOG(LE, (_T("[RevokeClassObject failed][%s][0x%x]"),
-                       GuidToString(*objmap_entry->pclsid), hr));
-      return hr;
-    }
-  }
-
-  return S_OK;
+CString Update3ServiceMode::app_id_string() {
+  return GuidToString(__uuidof(GoogleUpdate3ServiceClass));
 }
 
-HRESULT ServiceModule::RegisterServer(BOOL, const CLSID*) throw() {
-  SERVICE_LOG(L3, (_T("[ServiceModule::RegisterServer]")));
-  for (_ATL_OBJMAP_ENTRY* objmap_entry = object_map_;
-       objmap_entry->pclsid != NULL;
-       objmap_entry++) {
-    HRESULT hr = objmap_entry->pfnUpdateRegistry(TRUE);
-    if (FAILED(hr)) {
-      SERVICE_LOG(LE, (_T("[pfnUpdateRegistry failed][%s][0x%x]"),
-                       GuidToString(*objmap_entry->pclsid), hr));
-      return hr;
-    }
-
-    hr = AtlRegisterClassCategoriesHelper(*objmap_entry->pclsid,
-                                          objmap_entry->pfnGetCategoryMap(),
-                                          TRUE);
-    if (FAILED(hr)) {
-      SERVICE_LOG(LE, (_T("[AtlRegisterClassCategoriesHelper fail][%s][0x%x]"),
-                       GuidToString(*objmap_entry->pclsid), hr));
-      return hr;
-    }
-  }
-
-  return S_OK;
+CString Update3ServiceMode::GetCurrentServiceName() {
+  return goopdate_utils::GetCurrentVersionedName(true, reg_name(),
+                                                 default_name());
 }
 
-HRESULT ServiceModule::UnregisterServer(BOOL, const CLSID*) throw() {
-  SERVICE_LOG(L3, (_T("[ServiceModule::UnregisterServer]")));
-  for (_ATL_OBJMAP_ENTRY* objmap_entry = object_map_;
-       objmap_entry->pclsid != NULL;
-       objmap_entry++) {
-    HRESULT hr = AtlRegisterClassCategoriesHelper(*objmap_entry->pclsid,
-                     objmap_entry->pfnGetCategoryMap(),
-                     FALSE);
-    if (FAILED(hr)) {
-      SERVICE_LOG(LE, (_T("[AtlRegisterClassCategoriesHelper fail][%s][0x%x]"),
-                       GuidToString(*objmap_entry->pclsid), hr));
-      return hr;
-    }
-
-    hr = objmap_entry->pfnUpdateRegistry(FALSE);
-    if (FAILED(hr)) {
-      SERVICE_LOG(LE, (_T("[pfnUpdateRegistry failed][%s][0x%x]"),
-                       GuidToString(*objmap_entry->pclsid), hr));
-      return hr;
-    }
-  }
-
-  return S_OK;
-}
-
-HRESULT ServiceModule::RegisterCOMService() {
-  SERVICE_LOG(L3, (_T("[ServiceModule::RegisterCOMService]")));
-  HRESULT hr = UpdateRegistryAppId(TRUE);
-  if (FAILED(hr)) {
-    SERVICE_LOG(LE, (_T("[UpdateRegistryAppId failed][0x%x]"), hr));
-    return hr;
-  }
-
-  RegKey key_app_id;
-  hr = key_app_id.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_WRITE);
-  if (FAILED(hr)) {
-    SERVICE_LOG(LE, (_T("[Could not open HKLM\\AppID][0x%x]"), hr));
-    return hr;
-  }
-
-  RegKey key;
-  hr = key.Create(key_app_id.Key(), GetAppIdT());
-  if (FAILED(hr)) {
-    SERVICE_LOG(LE, (_T("[Fail open HKLM\\AppID\\%s][0x%x]"), GetAppId(), hr));
-    return hr;
-  }
-
-  // m_szServiceName is set in the constructor.
-  hr = key.SetValue(_T("LocalService"), m_szServiceName);
-  if (FAILED(hr)) {
-    SERVICE_LOG(LE, (_T("[Could not set LocalService value][0x%x]"), hr));
-    return hr;
-  }
-
-  // The SCM will pass this switch to ServiceMain() during COM activation.
-  hr = key.SetValue(_T("ServiceParameters"), kCmdLineServiceComServer);
-  if (FAILED(hr)) {
-    SERVICE_LOG(LE, (_T("[Could not set ServiceParameters value][0x%x]"), hr));
-    return hr;
-  }
-
-  return RegisterServer(FALSE);
-}
-
-HRESULT ServiceModule::UnregisterCOMService() {
-  SERVICE_LOG(L3, (_T("[ServiceModule::UnregisterCOMService]")));
-  HRESULT hr = UnregisterServer(FALSE);
-  if (FAILED(hr)) {
-    SERVICE_LOG(LE, (_T("[UnregisterServer failed][0x%x]"), hr));
-    return hr;
-  }
-
-  return UpdateRegistryAppId(FALSE);
-}
-
-HRESULT ServiceModule::PreMessageLoop(int show_cmd) {
-  SERVICE_LOG(L1, (_T("[ServiceModule::PreMessageLoop]")));
-
-  m_dwThreadID = ::GetCurrentThreadId();
-  // Use the delayed monitor thread COM shutdown mechanism.
-  m_bDelayShutdown = true;
-  service_thread_ = ::OpenThread(SYNCHRONIZE, false, m_dwThreadID);
-
-  // Initialize COM, COM security, register COM class objects.
-  HRESULT hr = Base::PreMessageLoop(show_cmd);
-  ASSERT(SUCCEEDED(hr), (_T("[Base::PreMessageLoop failed][0x%x]"), hr));
-  if (is_service_com_server_) {
-    SERVICE_LOG(L3, (_T("[Service in COM server mode.]")));
-    return hr;
-  }
-
-  // This is the regular service case. Increment the ATL module count. Run an
-  // idle timer, at the end of which the module count is decremented. The
-  // service will exit if the module count drops to zero.
-  Lock();
-  VERIFY1(SUCCEEDED(StartIdleCheckTimer()));
-
+HRESULT Update3ServiceMode::PreMessageLoop() {
   SERVICE_LOG(L1, (_T("[Starting Google Update core...]")));
   CommandLineBuilder builder(COMMANDLINE_MODE_CORE);
   CString args = builder.GetCommandLineArgs();
-  hr = goopdate_utils::StartGoogleUpdateWithArgs(true, args, NULL);
-  if (FAILED(hr)) {
-    SERVICE_LOG(LE, (_T("[Starting Google Update failed][0x%08x]"), hr));
-  }
+  return goopdate_utils::StartGoogleUpdateWithArgs(true, args, NULL);
+}
 
+CommandLineMode UpdateMediumServiceMode::commandline_mode() {
+  return COMMANDLINE_MODE_MEDIUM_SERVICE;
+}
+
+CString UpdateMediumServiceMode::reg_name() {
+  return kRegValueMediumServiceName;
+}
+
+CString UpdateMediumServiceMode::default_name() {
+  return kMediumServicePrefix;
+}
+
+DWORD UpdateMediumServiceMode::service_start_type() {
+  return SERVICE_DEMAND_START;
+}
+
+_ATL_OBJMAP_ENTRY* UpdateMediumServiceMode::object_map() {
+  return object_map_google_update_medium;
+}
+
+bool UpdateMediumServiceMode::allow_access_from_medium() {
+  return true;
+}
+
+CString UpdateMediumServiceMode::app_id_string() {
+  return GuidToString(__uuidof(OnDemandMachineAppsServiceClass));
+}
+
+CString UpdateMediumServiceMode::GetCurrentServiceName() {
+  return goopdate_utils::GetCurrentVersionedName(true, reg_name(),
+                                                 default_name());
+}
+
+HRESULT UpdateMediumServiceMode::PreMessageLoop() {
   return S_OK;
 }
 
-HRESULT ServiceModule::PostMessageLoop() {
-  SERVICE_LOG(L1, (_T("[ServiceModule::PostMessageLoop]")));
-  if (!is_service_com_server_) {
-    VERIFY1(SUCCEEDED(StopIdleCheckTimer()));
-  }
-
-  return Base::PostMessageLoop();
-}
-
-int ServiceModule::Main(int show_cmd) {
-  if (CAtlBaseModule::m_bInitFailed) {
-    SERVICE_LOG(LE, (_T("[CAtlBaseModule init failed]")));
-    return -1;
-  }
-  HRESULT hr = Start(show_cmd);
-  return static_cast<int>(hr);
-}
-
-// When ServiceModule::Start executes, it blocks on StartServiceCtrlDispatcher.
-// Internally, the SCM creates a service thread which starts executing the code
-// specified by the SERVICE_TABLE_ENTRY. The ATL code then calls PreMessageLoop
-// and PostMessageLoop on this thread. When the service stops, the execution
-// flow returns from StartServiceCtrlDispatcher and the main thread blocks
-// waiting on the service thread to exit.
-// Before synchronizing the main thread and the service thread, a race condition
-// resulted in http://b/1134747.
-HRESULT ServiceModule::Start(int show_cmd) {
-  SERVICE_LOG(L1, (_T("[ServiceModule::Start]")));
-  UNREFERENCED_PARAMETER(show_cmd);
-
-  SERVICE_TABLE_ENTRY st[] = {
-    { m_szServiceName, Base::_ServiceMain },
-    { NULL, NULL }
-  };
-
-  m_status.dwWin32ExitCode = 0;
-  if (!::StartServiceCtrlDispatcher(st)) {
-    m_status.dwWin32ExitCode = ::GetLastError();
-  }
-
-  if (service_thread_) {
-    DWORD result = ::WaitForSingleObject(service_thread_, kShutdownIntervalMs);
-    ASSERT1(result == WAIT_OBJECT_0);
-    ::CloseHandle(service_thread_);
-    service_thread_ = NULL;
-  }
-
-  return m_status.dwWin32ExitCode;
-}
-
-HRESULT ServiceModule::StartIdleCheckTimer() {
-  SERVICE_LOG(L1, (_T("[ServiceModule::StartIdleCheckTimer]")));
-
-  timer_queue_ = ::CreateTimerQueue();
-  if (!timer_queue_) {
-    HRESULT hr = HRESULTFromLastError();
-    SERVICE_LOG(LE, (_T("[CreateTimerQueue failed][0x%08x]"), hr));
-    return hr;
-  }
-  idle_check_timer_.reset(new QueueTimer(timer_queue_,
-                                         &ServiceModule::IdleCheckTimerCallback,
-                                         this));
-  HRESULT hr = idle_check_timer_->Start(kIdleCheckIntervalMs,
-                                        0,
-                                        WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE);
-  if (FAILED(hr)) {
-    SERVICE_LOG(LE, (_T("[idle check time failed to start][0x%08x]"), hr));
-    return hr;
-  }
-  return S_OK;
-}
-
-HRESULT ServiceModule::StopIdleCheckTimer() {
-  SERVICE_LOG(L1, (_T("[ServiceModule::StopIdleCheckTimer]")));
-  idle_check_timer_.reset();
-  if (timer_queue_) {
-    VERIFY1(::DeleteTimerQueueEx(timer_queue_, INVALID_HANDLE_VALUE));
-  }
-  timer_queue_ = NULL;
-  return S_OK;
-}
-
-
-void ServiceModule::IdleCheckTimerCallback(QueueTimer* timer) {
-  SERVICE_LOG(L1, (_T("[ServiceModule::IdleCheckTimerCallback]")));
-
-  ASSERT1(timer);
-  ASSERT1(timer->ctx());
-
-  ServiceModule* service_module = static_cast<ServiceModule*>(timer->ctx());
-  service_module->Unlock();
-}
-
-// If the module count drops to zero, Unlock() signals an event. There is a
-// monitor thread listening to this event, which initiates shutdown of the
-// service.
-// This is cloned from CAtlExeModuleT.Unlock(). The only difference is the call
-// to "OnStop()" instead of the "::PostThreadMessage(m_dwMainThreadID...".
-// OnStop() correctly sets the service status to SERVICE_STOP_PENDING, and posts
-// a WM_QUIT to the service thread.
-LONG ServiceModule::Unlock() throw() {
-  LONG retval = CComGlobalsThreadModel::Decrement(&m_nLockCnt);
-  SERVICE_LOG(L3, (_T("[ServiceModule::Unlock][%d]"), retval));
-
-  if (retval == 0) {
-    if (m_bDelayShutdown) {
-      m_bActivity = true;
-      ::SetEvent(m_hEventShutdown);
-    } else {
-      OnStop();
-    }
-  }
-
-  return retval;
-}
-
-// This is cloned from CAtlExeModuleT.MonitorShutdown(). The only difference is
-// the call to "OnStop()" instead of the "::PostThreadMessage(m_dwMainThreadID".
-void ServiceModule::MonitorShutdown() throw() {
-  SERVICE_LOG(L3, (_T("[ServiceModule::MonitorShutdown]")));
-
-  while (true) {
-    ::WaitForSingleObject(m_hEventShutdown, INFINITE);
-    SERVICE_LOG(L4, (_T("[Infinite Wait][%d][%d]"), m_bActivity, m_nLockCnt));
-
-    DWORD wait = 0;
-    do {
-      m_bActivity = false;
-      wait = ::WaitForSingleObject(m_hEventShutdown, m_dwTimeOut);
-    } while (wait == WAIT_OBJECT_0);
-
-    SERVICE_LOG(L4, (_T("[MonitorShutdown][%d][%d]"), m_bActivity, m_nLockCnt));
-    if (!m_bActivity && m_nLockCnt == 0) {
-      ::CoSuspendClassObjects();
-      if (m_nLockCnt == 0) {
-        break;
-      }
-    }
-  }
-
-  ::CloseHandle(m_hEventShutdown);
-  OnStop();
-}
-
-// This is cloned from CAtlExeModuleT.StartMonitor().
-HANDLE ServiceModule::StartMonitor() throw() {
-  SERVICE_LOG(L3, (_T("[ServiceModule::StartMonitor]")));
-  m_hEventShutdown = ::CreateEvent(NULL, false, false, NULL);
-  if (m_hEventShutdown == NULL) {
-    return NULL;
-  }
-
-  DWORD thread_id(0);
-  HANDLE monitor = ::CreateThread(NULL, 0, MonitorProc, this, 0, &thread_id);
-  if (monitor == NULL) {
-    ::CloseHandle(m_hEventShutdown);
-  }
-
-  return monitor;
-}
-
-// This is cloned from CAtlExeModuleT.MonitorProc().
-DWORD WINAPI ServiceModule::MonitorProc(void* pv) throw() {
-  SERVICE_LOG(L3, (_T("[ServiceModule::MonitorProc]")));
-  ServiceModule* service_module = static_cast<ServiceModule*>(pv);
-  ASSERT1(service_module);
-
-  service_module->MonitorShutdown();
-  return 0;
-}
-
 }  // namespace omaha
-
diff --git a/service/service_main.h b/service/service_main.h
index 3d3cf05..93b471f 100644
--- a/service/service_main.h
+++ b/service/service_main.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2009 Google Inc.
+// Copyright 2006-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -15,82 +15,475 @@
 
 // The service is a bootstrap for a local system process to start
 // when the computer starts. The service shuts itself down in 30 seconds.
+//
+// The service can be started in one of two modes:
+//   * As a regular service, typically at system startup.
+//   * As a COM service, typically by an Omaha client using IGoogleUpdateCore.
+// The COM case is distinguished from the regular service case by registering a
+// ServiceParameters command line in the AppID registration.
+//
+// In all cases, the service initializes COM, and allows for IGoogleUpdateCore
+// clients to connect to the service. In the regular service case, the service
+// shuts down after a small idle check timeout, provided that there are no COM
+// clients connected. In the COM server case, and in the case where there are
+// COM clients connected in the regular service case, the service will shut down
+// when the last client disconnects.
+//
+// To be exact, the service will initiate shutdown in all cases when the ATL
+// module count drops to zero.
+//
+// ATL does not allow for directly reusing the delayed COM shutdown mechanism
+// available for Local servers. The assumption likely being that services run
+// forever. Since we do not want our service to run forever, we override some
+// of the functions to get the same effect.
 
-#ifndef OMAHA_SERVICE_SERVICE_MAIN_H__
-#define OMAHA_SERVICE_SERVICE_MAIN_H__
+#ifndef OMAHA_SERVICE_SERVICE_MAIN_H_
+#define OMAHA_SERVICE_SERVICE_MAIN_H_
 
 #include <atlbase.h>
 #include <atlcom.h>
 #include "base/basictypes.h"
 #include "base/scoped_ptr.h"
-#include "goopdate/google_update_idl.h"
-#include "omaha/common/atlregmapex.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/resource.h"
+#include "omaha/base/atlregmapex.h"
+#include "omaha/base/debug.h"
+// Use client/resource.h because it does not use StringFormatter and some of the
+// strings are only used during Setup, which is part of the client.
+// TODO(omaha3): It is a little unexpected to access strings in a header. It
+// would be nice to avoid that. Also, this file is included by both client
+// (setup_service.cc) and server (goopdate.cc) code.
+#include "omaha/client/resource.h"
+#include "omaha/common/command_line_builder.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/core/google_update_core.h"
+#include "omaha/goopdate/google_update3.h"
+#include "omaha/goopdate/non_localized_resource.h"
+#include "omaha/goopdate/ondemand.h"
+#include "omaha/goopdate/update3web.h"
+#include "omaha/goopdate/worker.h"
+#include "omaha/net/network_config.h"
 
 namespace omaha {
 
-class QueueTimer;
+class Update3ServiceMode {
+ public:
+  static CommandLineMode commandline_mode();
+  static CString reg_name();
+  static CString default_name();
+  static DWORD service_start_type();
+  static _ATL_OBJMAP_ENTRY* object_map();
+  static bool allow_access_from_medium();
+  static CString app_id_string();
+  static CString GetCurrentServiceName();
+  static HRESULT PreMessageLoop();
+};
 
+class UpdateMediumServiceMode {
+ public:
+  static CommandLineMode commandline_mode();
+  static CString reg_name();
+  static CString default_name();
+  static DWORD service_start_type();
+  static _ATL_OBJMAP_ENTRY* object_map();
+  static bool allow_access_from_medium();
+  static CString app_id_string();
+  static CString GetCurrentServiceName();
+  static HRESULT PreMessageLoop();
+};
+
+#pragma warning(push)
+// C4640: construction of local static object is not thread-safe
+#pragma warning(disable : 4640)
+
+template <typename T>
 class ServiceModule
-    : public CAtlServiceModuleT<ServiceModule, IDS_SERVICE_NAME> {
+    : public CAtlServiceModuleT<ServiceModule<T>, IDS_SERVICE_NAME> {
  public:
   typedef CAtlServiceModuleT<ServiceModule, IDS_SERVICE_NAME> Base;
 
-  #pragma warning(push)
-  // C4640: construction of local static object is not thread-safe
-  #pragma warning(disable : 4640)
-
-  DECLARE_REGISTRY_APPID_RESOURCEID_EX(IDR_GOOGLE_UPDATE_SERVICE_APPID,
-      GuidToString(__uuidof(GoogleUpdateCoreClass)))
+  DECLARE_REGISTRY_APPID_RESOURCEID_EX(IDR_GOOGLE_UPDATE3_SERVICE_APPID,
+                                       T::app_id_string())
 
   BEGIN_REGISTRY_MAP()
-    REGMAP_RESOURCE(_T("DESCRIPTION"), IDS_SERVICE_DESCRIPTION)
-    REGMAP_ENTRY(_T("FILENAME"),       kServiceFileName)
+    REGMAP_ENTRY(_T("DESCRIPTION"),   _T("ServiceModule"))
+    REGMAP_ENTRY(_T("FILENAME"),      kServiceFileName)
   END_REGISTRY_MAP()
 
-  #pragma warning(pop)
+  ServiceModule()
+      : service_thread_(NULL),
+        is_service_com_server_(false) {
+    SERVICE_LOG(L1, (_T("[ServiceModule]")));
+    _tcscpy(m_szServiceName, T::GetCurrentServiceName());
+  }
 
-  ServiceModule();
-  ~ServiceModule();
+  ~ServiceModule() {
+    SERVICE_LOG(L1, (_T("[~ServiceModule]")));
+    ASSERT1(!service_thread_);
 
-  // Runs the entry point for the service.
-  int Main(int show_cmd);
+    // ServiceModule is typically created on the stack. We cannot reset the
+    // _pAtlModule here, because objects are destroyed at destructor unwind
+    // time, and require access to the module.
+  }
 
-  // These methods are called by ATL and they must be public.
-  void ServiceMain(DWORD argc, LPTSTR* argv) throw();
-  HRESULT PreMessageLoop(int show_cmd);
-  HRESULT PostMessageLoop();
-  HRESULT InitializeSecurity() throw();
-  HRESULT RegisterClassObjects(DWORD class_context, DWORD flags) throw();
-  HRESULT RevokeClassObjects() throw();
-  HRESULT RegisterServer(BOOL reg_typelib = FALSE,
-                         const CLSID* clsid = NULL) throw();
-  HRESULT UnregisterServer(BOOL reg_typelib, const CLSID* clsid = NULL) throw();
-  LONG Unlock() throw();
-  void MonitorShutdown() throw();
-  HANDLE StartMonitor() throw();
-  static DWORD WINAPI MonitorProc(void* pv) throw();
+  HRESULT InitializeSecurity() throw() {
+    SERVICE_LOG(L3, (_T("[InitializeSecurity]")));
 
-  // These methods are helpers called by setup for service registration.
-  HRESULT RegisterCOMService();
-  HRESULT UnregisterCOMService();
+    return InitializeServerSecurity(T::allow_access_from_medium());
+  }
+
+  void ServiceMain(DWORD argc, LPTSTR* argv) throw() {
+    ASSERT1(argc <= 2);
+    is_service_com_server_ =
+        argc == 2 && !CString(argv[1]).CompareNoCase(kCmdLineServiceComServer);
+    SERVICE_LOG(L3, (_T("[ServiceMain][is_service_com_server_][%d]"),
+                     is_service_com_server_));
+    Base::ServiceMain(argc, argv);
+  }
+
+  HRESULT RegisterClassObjects(DWORD class_context,
+                                              DWORD flags) throw() {
+    SERVICE_LOG(L3, (_T("[RegisterClassObjects]")));
+    for (_ATL_OBJMAP_ENTRY* objmap_entry = T::object_map();
+         objmap_entry->pclsid != NULL;
+         objmap_entry++) {
+      HRESULT hr = objmap_entry->RegisterClassObject(class_context, flags);
+      if (FAILED(hr)) {
+        SERVICE_LOG(LE, (_T("[RegisterClassObject failed][%s][0x%x]"),
+                         GuidToString(*objmap_entry->pclsid), hr));
+        return hr;
+      }
+    }
+
+    return S_OK;
+  }
+
+  HRESULT RevokeClassObjects() throw() {
+    SERVICE_LOG(L3, (_T("[RevokeClassObjects]")));
+    for (_ATL_OBJMAP_ENTRY* objmap_entry = T::object_map();
+         objmap_entry->pclsid != NULL;
+         objmap_entry++) {
+      HRESULT hr = objmap_entry->RevokeClassObject();
+      if (FAILED(hr)) {
+        SERVICE_LOG(LE, (_T("[RevokeClassObject failed][%s][0x%x]"),
+                         GuidToString(*objmap_entry->pclsid), hr));
+        return hr;
+      }
+    }
+
+    return S_OK;
+  }
+
+  HRESULT RegisterServer(BOOL, const CLSID* = NULL) throw() {
+    SERVICE_LOG(L3, (_T("[RegisterServer]")));
+    for (_ATL_OBJMAP_ENTRY* objmap_entry = T::object_map();
+         objmap_entry->pclsid != NULL;
+         objmap_entry++) {
+      HRESULT hr = objmap_entry->pfnUpdateRegistry(TRUE);
+      if (FAILED(hr)) {
+        SERVICE_LOG(LE, (_T("[pfnUpdateRegistry failed][%s][0x%x]"),
+                         GuidToString(*objmap_entry->pclsid), hr));
+        return hr;
+      }
+
+      hr = AtlRegisterClassCategoriesHelper(*objmap_entry->pclsid,
+                                            objmap_entry->pfnGetCategoryMap(),
+                                            TRUE);
+      if (FAILED(hr)) {
+        SERVICE_LOG(LE, (_T("[RegisterServer fail][%s][0x%x]"),
+                         GuidToString(*objmap_entry->pclsid), hr));
+        return hr;
+      }
+    }
+
+    return S_OK;
+  }
+
+  HRESULT UnregisterServer(BOOL, const CLSID* = NULL) throw() {
+    SERVICE_LOG(L3, (_T("[UnregisterServer]")));
+    for (_ATL_OBJMAP_ENTRY* objmap_entry = T::object_map();
+         objmap_entry->pclsid != NULL;
+         objmap_entry++) {
+      HRESULT hr = AtlRegisterClassCategoriesHelper(*objmap_entry->pclsid,
+                       objmap_entry->pfnGetCategoryMap(),
+                       FALSE);
+      if (FAILED(hr)) {
+        SERVICE_LOG(LE, (_T("[RegisterServer fail][%s][0x%x]"),
+                         GuidToString(*objmap_entry->pclsid), hr));
+        return hr;
+      }
+
+      hr = objmap_entry->pfnUpdateRegistry(FALSE);
+      if (FAILED(hr)) {
+        SERVICE_LOG(LE, (_T("[pfnUpdateRegistry failed][%s][0x%x]"),
+                         GuidToString(*objmap_entry->pclsid), hr));
+        return hr;
+      }
+    }
+
+    return S_OK;
+  }
+
+  HRESULT RegisterCOMService() {
+    SERVICE_LOG(L3, (_T("[RegisterCOMService]")));
+    HRESULT hr = UpdateRegistryAppId(TRUE);
+    if (FAILED(hr)) {
+      SERVICE_LOG(LE, (_T("[UpdateRegistryAppId failed][0x%x]"), hr));
+      return hr;
+    }
+
+    RegKey key_app_id;
+    hr = key_app_id.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_WRITE);
+    if (FAILED(hr)) {
+      SERVICE_LOG(LE, (_T("[Could not open HKLM\\AppID][0x%x]"), hr));
+      return hr;
+    }
+
+    RegKey key;
+    hr = key.Create(key_app_id.Key(), GetAppIdT());
+    if (FAILED(hr)) {
+      SERVICE_LOG(LE, (_T("[Fail open HKLM-AppID-%s][0x%x]"), GetAppId(), hr));
+      return hr;
+    }
+
+    // m_szServiceName is set in the constructor.
+    hr = key.SetValue(_T("LocalService"), m_szServiceName);
+    if (FAILED(hr)) {
+      SERVICE_LOG(LE, (_T("[Could not set LocalService value][0x%x]"), hr));
+      return hr;
+    }
+
+    // The SCM will pass this switch to ServiceMain() during COM activation.
+    hr = key.SetValue(_T("ServiceParameters"), kCmdLineServiceComServer);
+    if (FAILED(hr)) {
+      SERVICE_LOG(LE, (_T("[Set ServiceParameters value failed][0x%x]"), hr));
+      return hr;
+    }
+
+    return RegisterServer(FALSE);
+  }
+
+  HRESULT UnregisterCOMService() {
+    SERVICE_LOG(L3, (_T("[UnregisterCOMService]")));
+    HRESULT hr = UnregisterServer(FALSE);
+    if (FAILED(hr)) {
+      SERVICE_LOG(LE, (_T("[UnregisterServer failed][0x%x]"), hr));
+      return hr;
+    }
+
+    return UpdateRegistryAppId(FALSE);
+  }
+
+  HRESULT PreMessageLoop(int show_cmd) {
+    UNREFERENCED_PARAMETER(show_cmd);
+
+    SERVICE_LOG(L1, (_T("[PreMessageLoop]")));
+
+    m_dwThreadID = ::GetCurrentThreadId();
+    service_thread_ = ::OpenThread(SYNCHRONIZE, false, m_dwThreadID);
+
+    if (is_service_com_server_) {
+      return InitializeCOMServer();
+    }
+
+    // This is the regular service case. Call T::PreMessageLoop() and exit.
+    SetServiceStatus(SERVICE_RUNNING);
+
+    HRESULT hr = T::PreMessageLoop();
+    if (FAILED(hr)) {
+      SERVICE_LOG(LE, (_T("[T::PreMessageLoop() failed][0x%x]"), hr));
+    }
+
+    SetServiceStatus(SERVICE_STOP_PENDING);
+
+    // S_FALSE is returned to exit the service immediately.
+    return S_FALSE;
+  }
+
+  HRESULT PostMessageLoop() {
+    SERVICE_LOG(L1, (_T("[PostMessageLoop]")));
+
+    if (!is_service_com_server_) {
+      return S_OK;
+    }
+
+    return Base::PostMessageLoop();
+  }
+
+  int Main(int show_cmd) {
+    if (CAtlBaseModule::m_bInitFailed) {
+      SERVICE_LOG(LE, (_T("[CAtlBaseModule init failed]")));
+      return -1;
+    }
+
+    return static_cast<int>(Start(show_cmd));
+  }
+
+  // This is cloned from CAtlExeModuleT.Lock(). The one difference is the call
+  // to ::CoAddRefServerProcess(). See the description for Unlock() below for
+  // further information.
+  virtual LONG Lock() throw() {
+    ::CoAddRefServerProcess();
+    LONG retval = CComGlobalsThreadModel::Increment(&m_nLockCnt);
+    SERVICE_LOG(L3, (_T("[ServiceModule::Lock][%d]"), retval));
+    return retval;
+  }
+
+  // This is cloned from CAtlExeModuleT.Unlock(). The differences are:
+  //
+  // * the call to ::CoReleaseServerProcess(), to ensure that the class
+  // factories are suspended once the lock count drops to zero. This fixes a
+  // a race condition where an activation request could come in in the middle
+  // of shutting down. This shutdown mechanism works with free threaded servers.
+  //
+  // There are race issues with the ATL  delayed shutdown mechanism, hence the
+  // associated code has been eliminated, and we have an assert to make sure
+  // m_bDelayShutdown is not set.
+  //
+  // * the call to "OnStop()" instead of the "::PostThreadMessage(m_dwMainThre".
+  // OnStop() correctly sets the service status to SERVICE_STOP_PENDING, and
+  // posts a WM_QUIT to the service thread.
+  virtual LONG Unlock() throw() {
+    ASSERT1(!m_bDelayShutdown);
+
+    ::CoReleaseServerProcess();
+    LONG retval = CComGlobalsThreadModel::Decrement(&m_nLockCnt);
+    SERVICE_LOG(L3, (_T("[ServiceModule::Unlock][%d]"), retval));
+
+    if (retval == 0) {
+      OnStop();
+    }
+
+    return retval;
+  }
+
+  // This is cloned from CAtlExeModuleT.MonitorShutdown(). The only difference
+  // is the call to "OnStop()" instead of the
+  // "::PostThreadMessage(m_dwMainThreadID".
+  void MonitorShutdown() throw() {
+    SERVICE_LOG(L3, (_T("[MonitorShutdown]")));
+
+    while (true) {
+      ::WaitForSingleObject(m_hEventShutdown, INFINITE);
+      SERVICE_LOG(L4, (_T("[Infinite Wait][%d][%d]"), m_bActivity, m_nLockCnt));
+
+      DWORD wait = 0;
+      do {
+        m_bActivity = false;
+        wait = ::WaitForSingleObject(m_hEventShutdown, m_dwTimeOut);
+      } while (wait == WAIT_OBJECT_0);
+
+      SERVICE_LOG(L4, (_T("[MonitorShutdown][%d][%d]"),
+                       m_bActivity, m_nLockCnt));
+      if (!m_bActivity && m_nLockCnt == 0) {
+        ::CoSuspendClassObjects();
+        if (m_nLockCnt == 0) {
+          break;
+        }
+      }
+    }
+
+    ::CloseHandle(m_hEventShutdown);
+    OnStop();
+  }
+
+  // This is cloned from CAtlExeModuleT.StartMonitor().
+  HANDLE StartMonitor() throw() {
+    SERVICE_LOG(L3, (_T("[StartMonitor]")));
+    m_hEventShutdown = ::CreateEvent(NULL, false, false, NULL);
+    if (m_hEventShutdown == NULL) {
+      return NULL;
+    }
+
+    DWORD thread_id(0);
+    HANDLE monitor = ::CreateThread(NULL, 0, MonitorProc, this, 0, &thread_id);
+    if (monitor == NULL) {
+      ::CloseHandle(m_hEventShutdown);
+    }
+
+    return monitor;
+  }
+
+  // This is cloned from CAtlExeModuleT.MonitorProc().
+  static DWORD WINAPI MonitorProc(void* pv) throw() {
+    SERVICE_LOG(L3, (_T("[MonitorProc]")));
+    ServiceModule* service_module = static_cast<ServiceModule*>(pv);
+    ASSERT1(service_module);
+
+    service_module->MonitorShutdown();
+    return 0;
+  }
 
  private:
-  // Calls the service dispatcher to start the service.
-  HRESULT Start(int show_cmd);
+  // Should only be called from the client, such as during Setup, since it only
+  // supports one language.
+  // Assumes the resources have been loaded.
+  static CString GetServiceDescription() {
+    CString company_name;
+    VERIFY1(company_name.LoadString(IDS_FRIENDLY_COMPANY_NAME));
+    CString description;
+    description.FormatMessage(IDS_SERVICE_DESCRIPTION, company_name);
+    return description;
+  }
 
-  // Creates and starts the idle check timer.
-  HRESULT StartIdleCheckTimer();
+  HRESULT InitializeCOMServer() {
+    SERVICE_LOG(L1, (_T("[InitializeCOMServer]")));
 
-  // Stops and destroys the idle check timer.
-  HRESULT StopIdleCheckTimer();
+    // Initialize COM security right at the beginning, because Worker
+    // initialization can cause interface marshaling. The first few lines below
+    // are adapted from the beginning of CAtlServiceModuleT::PreMessageLoop().
+    ASSERT1(m_bService);
+    HRESULT hr = InitializeSecurity();
+    if (FAILED(hr)) {
+      return hr;
+    }
 
-  static void IdleCheckTimerCallback(QueueTimer* timer);
+    DisableCOMExceptionHandling();
 
-  HANDLE timer_queue_;
+    NetworkConfigManager::set_is_machine(true);
+
+    // Create NetworkConfigManager singleton by referencing it.
+    NetworkConfigManager::Instance();
+
+    // Register and resume the COM class objects. We call the CAtlExeModuleT
+    // member instead of CAtlServiceModuleT, because the latter also tries to
+    // initialize security, which we have already done above.
+    return CAtlExeModuleT<ServiceModule>::PreMessageLoop(SW_HIDE);
+  }
+
+  // When Start executes, it blocks on StartServiceCtrlDispatcher.
+  // Internally, the SCM creates a service thread which starts executing code
+  // specified by SERVICE_TABLE_ENTRY. The ATL code then calls PreMessageLoop
+  // and PostMessageLoop on this thread. When the service stops, the execution
+  // flow returns from StartServiceCtrlDispatcher and the main thread blocks
+  // waiting on the service thread to exit.
+  // Before synchronizing the main thread and the service thread, race condition
+  // resulted in http://b/1134747.
+  HRESULT Start(int show_cmd) {
+    SERVICE_LOG(L1, (_T("[Start]")));
+    UNREFERENCED_PARAMETER(show_cmd);
+
+    SERVICE_TABLE_ENTRY st[] = {
+      { m_szServiceName, Base::_ServiceMain },
+      { NULL, NULL }
+    };
+
+    m_status.dwWin32ExitCode = 0;
+    if (!::StartServiceCtrlDispatcher(st)) {
+      m_status.dwWin32ExitCode = ::GetLastError();
+    }
+
+    if (service_thread_) {
+      DWORD result(::WaitForSingleObject(service_thread_, kShutdownIntervalMs));
+      ASSERT1(result == WAIT_OBJECT_0);
+      ::CloseHandle(service_thread_);
+      service_thread_ = NULL;
+    }
+
+    return m_status.dwWin32ExitCode;
+  }
+
   HANDLE service_thread_;   // The service thread provided by the SCM.
-  scoped_ptr<QueueTimer> idle_check_timer_;
   bool is_service_com_server_;  // True if the service is being invoked by COM.
 
   // Service shut down wait timeout. The main thread waits for the service
@@ -109,15 +502,14 @@
   // Time to wait before performing the specified action.
   static const DWORD kActionDelayMs       = 1000L * 60 * 15;  // 15 minutes.
 
-  // A private object map with custom registration works best, even though this
-  // stuff is deprecated. This is because GoogleUpdate.exe has other objects
-  // defined elsewhere and we do not want to expose those from the service.
-  static _ATL_OBJMAP_ENTRY object_map_[2];
-
   DISALLOW_EVIL_CONSTRUCTORS(ServiceModule);
 };
 
+#pragma warning(pop)
+
+typedef ServiceModule<Update3ServiceMode> Update3ServiceModule;
+typedef ServiceModule<UpdateMediumServiceMode> UpdateMediumServiceModule;
+
 }  // namespace omaha
 
-#endif  // OMAHA_SERVICE_SERVICE_MAIN_H__
-
+#endif  // OMAHA_SERVICE_SERVICE_MAIN_H_
diff --git a/setup/build.scons b/setup/build.scons
index b6f12da..6af16a6 100644
--- a/setup/build.scons
+++ b/setup/build.scons
@@ -15,21 +15,21 @@
 
 Import('env')
 
+local_env = env.Clone()
+
 inputs = [
     'setup.cc',
     'setup_files.cc',
     'setup_google_update.cc',
     'setup_metrics.cc',
-    'setup_service.cc',
     ]
 
-local_env = env.Clone()
-
 # Need to look in output dir to find .h files generated by midl compiler.
 # This also allows Hammer to understand dependencies between this subdir
 # and the .idl files in the goopdate folder.
+# TODO(omaha): It would be nice to eliminate references to COM files from setup.
 local_env['CPPPATH'] += ['$OBJ_ROOT']
 
 
 # Build these into a static library.
-local_env.ComponentLibrary('setup', inputs)
+local_env.ComponentStaticLibrary('setup', inputs)
diff --git a/setup/msi_test_utils.cc b/setup/msi_test_utils.cc
index dd2c159..127d492 100644
--- a/setup/msi_test_utils.cc
+++ b/setup/msi_test_utils.cc
@@ -26,7 +26,7 @@
 
 #include <windows.h>
 #include <msi.h>
-#include "omaha/common/constants.h"
+#include "omaha/base/constants.h"
 #include "omaha/testing/unit_test.h"
 
 namespace omaha {
diff --git a/setup/setup.cc b/setup/setup.cc
index 59bd129..fd1a6e6 100644
--- a/setup/setup.cc
+++ b/setup/setup.cc
@@ -16,63 +16,40 @@
 // This class handles the installation of Google Update when it is run with the
 // install or update switch. It is invoked when Google Update is launched from
 // the meta-installer and as part of self-update.
-//
-//
-// *** Documentation for OEM Installs ***
-//
-// A /oem install requires:
-//  * Per-machine install
-//  * Running as admin
-//  * Running in Windows audit mode (ConfigManager::IsWindowsInstalling())
-//  * Standalone installer (determined in LaunchInstalledWorker())
-//
-// If the first three conditions are met, this class writes the OemInstallTime
-// registry value, which is used by ConfigManager::IsOemInstalling() along with
-// other logic to determine whether Google Update is running in an OEM install
-// environment.
-// Other objects use IsOemInstalling() - not IsWindowsInstalling() - to
-// determine whether to run in a disabled mode for OEM factory installations.
-// For example, the core exits immediately without checking for updates or Code
-// Red events, no instances ping, and persistent IDs are not saved.
-// OemInstallTime is never cleared. The logic in IsOemInstalling() determines
-// when the system is no longer in an OEM install mode.
 
 #include "omaha/setup/setup.h"
 #include <regstr.h>
+#include <atlpath.h>
 #include <algorithm>
 #include <functional>
 #include <vector>
-#include "omaha/common/app_util.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/highres_timer-win32.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/process.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/system.h"
+#include "omaha/base/timer.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/app_registry_utils.h"
+#include "omaha/common/command_line.h"
+#include "omaha/common/config_manager.h"
 #include "omaha/common/const_cmd_line.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/highres_timer-win32.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/path.h"
-#include "omaha/common/process.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/common/system.h"
-#include "omaha/common/time.h"
-#include "omaha/common/timer.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/common/xml_utils.h"
-#include "omaha/net/http_client.h"
-#include "omaha/goopdate/command_line_builder.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/event_logger.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/stats_uploader.h"
-#include "omaha/goopdate/ui_displayed_event.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/event_logger.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/ping.h"
+#include "omaha/common/scheduled_task_utils.h"
+#include "omaha/common/stats_uploader.h"
 #include "omaha/setup/setup_files.h"
 #include "omaha/setup/setup_google_update.h"
 #include "omaha/setup/setup_metrics.h"
@@ -82,9 +59,8 @@
 
 namespace {
 
-const int kVersion10 = 10;
-const int kVersion11 = 11;
-const int kVersion11MachineLock = 111;
+const TCHAR* const kUninstallEventDescriptionFormat = _T("%s uninstall");
+
 const int kVersion12 = 12;
 
 void GetShutdownEventAttributes(bool is_machine, NamedObjectAttributes* attr) {
@@ -122,11 +98,6 @@
     case COMMANDLINE_MODE_UNKNOWN:
       ++metric_setup_process_wait_failed_unknown;
       break;
-    case COMMANDLINE_MODE_NOARGS:  // Legacy install
-    case COMMANDLINE_MODE_LEGACYUI:
-    case COMMANDLINE_MODE_LEGACY_MANIFEST_HANDOFF:
-      ++metric_setup_process_wait_failed_legacy;
-      break;
     case COMMANDLINE_MODE_CORE:
       ++metric_setup_process_wait_failed_core;
       break;
@@ -136,25 +107,18 @@
     case COMMANDLINE_MODE_UPDATE:
       ++metric_setup_process_wait_failed_update;
       break;
-    case COMMANDLINE_MODE_IG:
-      ++metric_setup_process_wait_failed_ig;
-      break;
     case COMMANDLINE_MODE_HANDOFF_INSTALL:
       ++metric_setup_process_wait_failed_handoff;
       break;
-    case COMMANDLINE_MODE_UG:
-      ++metric_setup_process_wait_failed_ug;
-      break;
     case COMMANDLINE_MODE_UA:
       ++metric_setup_process_wait_failed_ua;
       break;
     case COMMANDLINE_MODE_CODE_RED_CHECK:
       ++metric_setup_process_wait_failed_cr;
       break;
-    case COMMANDLINE_MODE_CRASH_HANDLER:
+    case COMMANDLINE_MODE_NOARGS:
     case COMMANDLINE_MODE_SERVICE:
-    case COMMANDLINE_MODE_SERVICE_REGISTER:
-    case COMMANDLINE_MODE_SERVICE_UNREGISTER:
+
     case COMMANDLINE_MODE_REGSERVER:
     case COMMANDLINE_MODE_UNREGSERVER:
     case COMMANDLINE_MODE_NETDIAGS:
@@ -165,6 +129,14 @@
     case COMMANDLINE_MODE_COMSERVER:
     case COMMANDLINE_MODE_REGISTER_PRODUCT:
     case COMMANDLINE_MODE_UNREGISTER_PRODUCT:
+    case COMMANDLINE_MODE_SERVICE_REGISTER:
+    case COMMANDLINE_MODE_SERVICE_UNREGISTER:
+    case COMMANDLINE_MODE_CRASH_HANDLER:
+    case COMMANDLINE_MODE_COMBROKER:
+    case COMMANDLINE_MODE_ONDEMAND:
+    case COMMANDLINE_MODE_MEDIUM_SERVICE:
+    case COMMANDLINE_MODE_UNINSTALL:
+    case COMMANDLINE_MODE_PING:
     default:
       ++metric_setup_process_wait_failed_other;
       break;
@@ -184,7 +156,7 @@
   DWORD flags = EXCLUDE_CURRENT_PROCESS |
                 INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING;
   HRESULT hr = Process::FindProcesses(flags,
-                                      kGoopdateFileName,
+                                      kOmahaShellFileName,
                                       true,
                                       CString(),
                                       command_line,
@@ -197,16 +169,17 @@
   return S_OK;
 }
 
-const TCHAR* const kUninstallEventDesc = _T("Google Update uninstall");
-
 void WriteGoogleUpdateUninstallEvent(bool is_machine) {
+  CString description;
+  description.Format(kUninstallEventDescriptionFormat, kAppName);
   GoogleUpdateLogEvent uninstall_event(EVENTLOG_INFORMATION_TYPE,
                                        kUninstallEventId,
                                        is_machine);
-  uninstall_event.set_event_desc(kUninstallEventDesc);
+  uninstall_event.set_event_desc(description);
   uninstall_event.WriteEvent();
 }
 
+// TODO(omaha3): Enable. Will we still need this method?
 // Returns true if the mode can be determined and pid represents a "/c" process.
 bool IsCoreProcess(uint32 pid) {
   CommandLineMode mode(COMMANDLINE_MODE_UNKNOWN);
@@ -218,12 +191,10 @@
 
 bool Setup::did_uninstall_ = false;
 
-Setup::Setup(bool is_machine, const CommandLineArgs* args)
+Setup::Setup(bool is_machine)
     : is_machine_(is_machine),
-      mode_(MODE_UNKNOWN),
-      args_(args),
-      extra_code1_(S_OK),
-      launched_offline_worker_(false) {
+      is_self_update_(false),
+      extra_code1_(S_OK) {
   SETUP_LOG(L2, (_T("[Setup::Setup]")));
 }
 
@@ -231,236 +202,53 @@
   SETUP_LOG(L2, (_T("[Setup::~Setup]")));
 }
 
-// Handles the elevation case, then calls DoInstall to do the real work if
-// elevation is not required.
-// If elevation is required, the method returns when the elevated instance
-// exits because there is nothing more to do in this Omaha instance.
-HRESULT Setup::Install(const CString& cmd_line) {
-  SETUP_LOG(L2, (_T("[Setup::Install][%s]"), cmd_line));
-  ASSERT1(!cmd_line.IsEmpty());
-
-  mode_ = MODE_INSTALL;
-
-  if (args_->is_oem_set) {
-    HRESULT hr = SetOemInstallState();
-    if (FAILED(hr)) {
-      return hr;
-    }
-  }
-
-  ++metric_setup_install_total;
-  if (args_->is_install_elevated) {
-    ++metric_setup_uac_succeeded;
-  }
-
-  // If we ever support NEEDS_ADMIN_PREFERS variants, handle them here.
-  // We will also need to tell the installer how to install.
-
-  if (IsElevationRequired()) {
-    HRESULT hr = ElevateAndWait(cmd_line);
-    if (FAILED(hr)) {
-      SETUP_LOG(LE, (_T("[Setup::ElevateAndWait failed][%s][0x%08x]"),
-                    cmd_line, hr));
-    }
-    return hr;
-  }
-
-  if (vista_util::IsVistaOrLater() &&
-      !is_machine_ &&
-      vista_util::IsUserAdmin()) {
-    ++metric_setup_user_app_admin;
-  }
-
-  HRESULT hr = DoInstall();
-
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  ++metric_setup_install_succeeded;
-  return S_OK;
-}
-
-HRESULT Setup::InstallSelfSilently() {
-  SETUP_LOG(L2, (_T("[Setup::InstallSelfSilently]")));
-  ASSERT1(!IsElevationRequired());
-  ASSERT1(!args_->extra_args_str.IsEmpty());
-
-  // TODO(omaha): add metrics.
-  mode_ = MODE_SELF_INSTALL;
-
-  HRESULT hr = DoInstall();
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // TODO(omaha): add metrics.
-  return S_OK;
-}
-
-HRESULT Setup::UpdateSelfSilently() {
-  SETUP_LOG(L2, (_T("[Setup::UpdateSelfSilently]")));
-  ASSERT1(!IsElevationRequired());
-  ASSERT1(args_->extra_args_str.IsEmpty());
-
-  ++metric_setup_update_self_total;
-  mode_ = MODE_SELF_UPDATE;
-
-  HRESULT hr = DoInstall();
-  if (FAILED(hr)) {
-    PersistUpdateErrorInfo(is_machine_, hr, extra_code1_, GetVersionString());
-    return hr;
-  }
-
-  ++metric_setup_update_self_succeeded;
-  return S_OK;
-}
-
-HRESULT Setup::RepairSilently() {
-  SETUP_LOG(L2, (_T("[Setup::RepairSilently]")));
-  ASSERT1(!IsElevationRequired());
-  ASSERT1(args_->extra_args_str.IsEmpty());
-
-  mode_ = MODE_REPAIR;
-
-  HRESULT hr = DoInstall();
-  if (FAILED(hr)) {
-    // Use the update failed ping for repairs too.
-    PersistUpdateErrorInfo(is_machine_, hr, extra_code1_, GetVersionString());
-    return hr;
-  }
-
-  return S_OK;
-}
-
 // Protects all setup-related operations with:
 // * Setup Lock - Prevents other instances from installing Google Update for
 // this machine/user at the same time.
 // * Shutdown Event - Tells existing other instances and any instances that may
 // start during Setup to exit.
 // Setup-related operations do not include installation of the app.
-// Also tries to acquire the locks for Omaha 1.0 and 1.1, which are lock10 and
-// lock11, respectively.
-// Until we secure the current setup lock (bug 1076207), we return an error if
-// we are unable to acquire the legacy locks.
-// Gets the legacy locks first because we wait a lot longer for them during
-// updates and would not want to hold the setup lock all that time.
-// Legacy Setup Locks are not released until the app is installed.
-//
-// Sets the EULA as accepted when installing and /eularequired is not specified.
-// This can be done outside the locks. Setting EULA not accepted is done inside
-// the lock and process protection in DoProtectedGoogleUpdateInstall.
-HRESULT Setup::DoInstall() {
-  ASSERT1(MODE_UNKNOWN != mode_);
+HRESULT Setup::Install(bool set_keepalive) {
+  SETUP_LOG(L3,
+      (_T("[Admin=%d, NEAdmin=%d, Update3Svc=%d, MedSvc=%d, Machine=%d"),
+       vista_util::IsUserAdmin(),
+       vista_util::IsUserNonElevatedAdmin(),
+       SetupUpdate3Service::IsServiceInstalled(),
+       SetupUpdateMediumService::IsServiceInstalled(),
+       is_machine_));
+
   ASSERT1(!IsElevationRequired());
 
-  if (!args_->is_eula_required_set &&
-      (MODE_INSTALL == mode_ || MODE_SELF_INSTALL == mode_)) {
-    HRESULT hr = SetEulaAccepted(is_machine_);
-    if (FAILED(hr)) {
-      return hr;
-    }
-  }
-
-  // Validate that key OS components are installed.
-  if (!HasXmlParser()) {
-    return GOOPDATE_E_RUNNING_INFERIOR_MSXML;
-  }
-
   // Start the setup timer.
   metrics_timer_.reset(new HighresTimer);
 
-  GLock lock10, lock11_user, lock11_machine, setup_lock;
+  GLock setup_lock;
 
-  // Initialize all locks.
-  if (!InitLegacySetupLocks(&lock10, &lock11_user, &lock11_machine)) {
-    SETUP_LOG(L2, (_T("[Setup::InitLegacySetupLocks failed]")));
-    return GOOPDATE_E_SETUP_LOCK_INIT_FAILED;
-  }
   if (!InitSetupLock(is_machine_, &setup_lock)) {
     SETUP_LOG(L2, (_T("[Setup::InitSetupLock failed]")));
     extra_code1_ = kVersion12;
     return GOOPDATE_E_SETUP_LOCK_INIT_FAILED;
   }
 
-  scoped_process handoff_process;
+  HighresTimer lock_metrics_timer;
 
-  // Attempt to acquire all locks. Acquire them in a new scope so they are
-  // automatically released by ScopeGuards when we no longer need protection.
-  {
-    HighresTimer lock_metrics_timer;
+  if (!setup_lock.Lock(kSetupLockWaitMs)) {
+    OPT_LOG(LE, (_T("[Failed to acquire setup lock]")));
+    return HandleLockFailed(kVersion12);
+  }
+  metric_setup_lock_acquire_ms.AddSample(lock_metrics_timer.GetElapsedMs());
+  SETUP_LOG(L1, (_T("[Setup Locks acquired]")));
 
-    // Try to acquire the 1.0/1.1 locks to prevent these installers from
-    // running, but do not fail if not acquired because this prevents
-    // simultaneous user and machine installs because both used the same name.
-    // See bug 1145609. For simplicity, hold these locks until the function
-    // exits.
-    if (!lock10.Lock(kSetupLockWaitMs)) {
-      OPT_LOG(LW, (_T("[Failed to acquire 1.0 setup lock]")));
-    }
-
-    if (!lock11_user.Lock(kSetupLockWaitMs)) {
-      OPT_LOG(LW, (_T("[Failed to acquire 1.1 user setup lock]")));
-    }
-
-    if (is_machine_) {
-      if (!lock11_machine.Lock(kSetupLockWaitMs)) {
-        OPT_LOG(LE, (_T("[Failed to acquire 1.1 machine setup lock]")));
-        return HandleLockFailed(kVersion11MachineLock);
-      }
-    }
-    ScopeGuard lock11_machine_guard = MakeObjGuard(lock11_machine,
-                                                   &GLock::Unlock);
-    if (!is_machine_) {
-      lock11_machine_guard.Dismiss();
-    }
-
-    if (!setup_lock.Lock(kSetupLockWaitMs)) {
-      OPT_LOG(LE, (_T("[Failed to acquire setup lock]")));
-      return HandleLockFailed(kVersion12);
-    }
-    ON_SCOPE_EXIT_OBJ(setup_lock, &GLock::Unlock);
-
-    metric_setup_lock_acquire_ms.AddSample(lock_metrics_timer.GetElapsedMs());
-    SETUP_LOG(L1, (_T("[Setup Locks acquired]")));
-
-    // Do the installation.
-    HRESULT hr = DoProtectedInstall(address(handoff_process));
-    if (FAILED(hr)) {
-      SETUP_LOG(LE, (_T("[Setup::DoProtectedInstall failed][0x%08x]"), hr));
-      SETUP_LOG(L1, (_T("[Releasing Setup Lock]")));
-      return hr;
-    }
-
-    SETUP_LOG(L1, (_T("[Releasing Setup Lock]")));
-  }  // End lock protection.
-
-  if (handoff_process) {
-    ASSERT1(MODE_INSTALL == mode_);
-
-    // TODO(omaha): Why do we exit once the UI event is signaled? It is not
-    // related to the locks like waiting for /ig and it complicates the code.
-    HRESULT hr = WaitForHandoffWorker(get(handoff_process));
-
-    if (FAILED(hr)) {
-      if (!ShouldWaitForWorkerProcess()) {
-        // Did not wait for the second process to exit. The error may not be the
-        // exit code of the worker process. Report that the handoff failed.
-        OPT_LOG(LE, (_T("[Wait for Google Update hand off failed][0x%08x]"),
-                     hr));
-        extra_code1_ = hr;
-        return GOOPDATE_E_HANDOFF_FAILED;
-      } else {
-        return hr;
-      }
-    }
-
-    ++metric_setup_handoff_only_succeeded;
+  HRESULT hr = DoProtectedInstall(set_keepalive);
+  if (FAILED(hr)) {
+    SETUP_LOG(LE, (_T("[Setup::DoProtectedInstall failed][0x%08x]"), hr));
   }
 
-  return S_OK;
+  SETUP_LOG(L1, (_T("[Releasing Setup Lock]")));
+  return hr;
 }
 
+// TODO(omaha3): Eliminate lock_version if we do not fix http://b/1076207.
 // Sets appropriate metrics and extra_code1_ value. It then tries to determine
 // the scenario that caused this failure and returns an appropriate error.
 // The detected processes may not actually be in conflict with this one, but are
@@ -469,18 +257,6 @@
   ++metric_setup_locks_failed;
 
   switch (lock_version) {
-    case kVersion10:
-      ASSERT1(false);
-      extra_code1_ = kVersion10;
-      break;
-    case kVersion11:
-      extra_code1_ = kVersion11;
-      ++metric_setup_lock11_failed;
-      break;
-    case kVersion11MachineLock:
-      extra_code1_ = kVersion11MachineLock;
-      ++metric_setup_lock11_failed;
-      break;
     case kVersion12:
       extra_code1_ = kVersion12;
       ++metric_setup_lock12_failed;
@@ -504,6 +280,16 @@
     return GOOPDATE_E_FAILED_TO_GET_LOCK_UPDATE_PROCESS_RUNNING;
   }
 
+  switch_to_include.Format(_T("/%s"), kCmdLineUninstall);
+  hr = GetPidsWithArgsForAllUsers(switch_to_include, &matching_pids);
+  if (FAILED(hr)) {
+    ASSERT1(false);
+    return GOOPDATE_E_FAILED_TO_GET_LOCK;
+  }
+  if (!matching_pids.empty()) {
+    return GOOPDATE_E_FAILED_TO_GET_LOCK_UNINSTALL_PROCESS_RUNNING;
+  }
+
   switch_to_include.Format(_T("/%s"), kCmdLineInstall);
   hr = GetPidsWithArgsForAllUsers(switch_to_include, &matching_pids);
   if (FAILED(hr)) {
@@ -527,13 +313,13 @@
   }
 
   // Strip the directory path, which may vary, and executable name.
-  int exe_index = current_cmd_line.Find(kGoopdateFileName);
+  int exe_index = current_cmd_line.Find(kOmahaShellFileName);
   if (-1 == exe_index) {
     ASSERT(false, (_T("Unable to find %s in %s"),
-                   kGoopdateFileName, current_cmd_line));
+                   kOmahaShellFileName, current_cmd_line));
     return GOOPDATE_E_FAILED_TO_GET_LOCK;
   }
-  int args_start = exe_index + _tcslen(kGoopdateFileName);
+  int args_start = exe_index + _tcslen(kOmahaShellFileName);
   // Support enclosed paths; increment past closing double quote.
   if (_T('"') == current_cmd_line.GetAt(args_start)) {
     ++args_start;
@@ -559,10 +345,8 @@
 }
 
 // Assumes the necessary locks have been acquired.
-HRESULT Setup::DoProtectedInstall(HANDLE* handoff_process) {
+HRESULT Setup::DoProtectedInstall(bool set_keepalive) {
   SETUP_LOG(L2, (_T("[Setup::DoProtectedInstall]")));
-  ASSERT1(handoff_process);
-  ASSERT1(MODE_UNKNOWN != mode_);
 
   SetupFiles setup_files(is_machine_);
 
@@ -574,11 +358,18 @@
 
   if (ShouldInstall(&setup_files)) {
     ++metric_setup_do_self_install_total;
+
+    // TODO(omaha3): IMPORTANT: Try to avoid losing users due to firewall
+    // blocking caused by changing the constant shell. Try a simple ping using
+    // the new shell, and if it fails take one of the following actions:
+    //  1) Keep the old shell (if possible).
+    //  2) Fail the self-update. Leave the user on this version. Would need to
+    //     figure out a way to avoid updating the user every 5 hours.
+
     HRESULT hr = DoProtectedGoogleUpdateInstall(&setup_files);
     if (FAILED(hr)) {
       SETUP_LOG(LE, (_T("[DoProtectedGoogleUpdateInstall fail][0x%08x]"), hr));
-      // Do not return until rolling back, releasing the events and restarting
-      // the core.
+      // Do not return until rolling back and releasing the events.
     }
 
     if (FAILED(hr)) {
@@ -593,56 +384,20 @@
     ReleaseShutdownEvents();
 
     if (FAILED(hr)) {
-      // We may have shutdown an existing core. Start it again if possible.
-      // First kill running cores to prevent the stated instance from exiting
-      // because it cannot acquire the single program instance.
-      OPT_LOG(L2, (_T("[Attempting to restart existing core]")));
-
-      VERIFY1(SUCCEEDED(TerminateCoreProcesses()));
-
-      HRESULT start_hr = StartCore();
-      if (FAILED(start_hr)) {
-        SETUP_LOG(LW, (_T("[StartCore failed][0x%08x]"), start_hr));
-      }
-
       return hr;
     }
 
+    // If we've been asked to defer uninstall (typically because we're doing
+    // an Omaha-only install to expose the COM API to a later process), set
+    // it on a successful install.
+    if (set_keepalive) {
+      SetDelayUninstall(true);
+    }
+
     ++metric_setup_do_self_install_succeeded;
-    return S_OK;
-  } else if (MODE_INSTALL == mode_) {
-    // No setup was required. Launch the worker to install the app.
-    // Since we are not doing any setup in the worker, return without waiting
-    // so that we can release the Setup Lock.
-    ++metric_setup_handoff_only_total;
-
-    hr = LaunchInstalledWorker(false,   // Do not run setup phase 2.
-                               handoff_process);
-    if (SUCCEEDED(hr)) {
-      metric_setup_handoff_ms.AddSample(metrics_timer_->GetElapsedMs());
-    } else {
-      OPT_LOG(LE, (_T("[Failed to launch installed instance][0x%08x]"), hr));
-      // TODO(omaha): Consider checking for GoogleUpdate.exe in version
-      // directory in file not found case and copying it to shell location then
-      // relaunching.
-    }
-
-    // Start the core in case one is not already running. If one is already
-    // running, this one will exit quickly.
-    HRESULT start_hr = StartCore();
-    if (FAILED(start_hr)) {
-      SETUP_LOG(LW, (_T("[StartCore failed][0x%08x]"), start_hr));
-    }
-
-    return hr;
-  } else {
-    // Do not launch the worker because there is no app to install.
-    OPT_LOG(L1, (_T("[Not installing Google Update or an app]")));
-
-    // Start the core in case one is not already running. If one is already
-    // running, this one will exit quickly.
-    return StartCore();
   }
+
+  return S_OK;
 }
 
 // Assumes that the shell is the correct version for the existing Omaha version.
@@ -650,6 +405,9 @@
   SETUP_LOG(L2, (_T("[Setup::ShouldInstall]")));
   ASSERT1(setup_files);
 
+  // TODO(omaha3): Figure out a different way to record these stats.
+  bool is_install = true;
+
   ++metric_setup_should_install_total;
 
   ULONGLONG my_version = GetVersion();
@@ -667,7 +425,6 @@
 
   OPT_LOG(L2, (_T("[Existing version: %s][Running version: %s]"),
                existing_version, GetVersionString()));
-  UpdateCoreNotRunningMetric(existing_version);
 
   // If running from the official install directory for this type of install
   // (user/machine), it is most likely a OneClick install. Do not install self.
@@ -676,7 +433,7 @@
     return false;
   }
 
-  if (MODE_INSTALL == mode_) {
+  if (is_install) {
     ++metric_setup_subsequent_install_total;
   }
 
@@ -701,7 +458,7 @@
     }
   }
 
-  if (MODE_INSTALL == mode_ && should_install) {
+  if (is_install && should_install) {
     ++metric_setup_subsequent_install_should_install_true;
   }
 
@@ -753,26 +510,6 @@
   return false;
 }
 
-
-void Setup::UpdateCoreNotRunningMetric(const CString& existing_version) {
-  if (!goopdate_utils::IsGoogleUpdate2OrLater(existing_version)) {
-    return;
-  }
-
-  Pids found_core_pids;
-  HRESULT hr = FindCoreProcesses(&found_core_pids);
-  if (FAILED(hr)) {
-    ASSERT(false, (_T("[FindCoreProcesses failed][0x%08x]"), hr));
-    return;
-  }
-
-  if (found_core_pids.empty()) {
-    ++metric_setup_installed_core_not_running;
-  }
-}
-
-// When installing and /eularequired is specified, calls SetEulaNotAccepted
-// after guaranteeing that no other processes are running.
 HRESULT Setup::DoProtectedGoogleUpdateInstall(SetupFiles* setup_files) {
   ASSERT1(setup_files);
   SETUP_LOG(L2, (_T("[Setup::DoProtectedGoogleUpdateInstall]")));
@@ -786,84 +523,38 @@
     return hr;
   }
 
+// TODO(omaha3): Enable. Prefer to move out of Setup if possible.
+#if 0
   VERIFY1(SUCCEEDED(ResetMetrics(is_machine_)));
+#endif
 
-  if (args_->is_eula_required_set) {
-    if (MODE_INSTALL == mode_ || MODE_SELF_INSTALL == mode_) {
-      hr = SetEulaNotAccepted(is_machine_);
-      if (FAILED(hr)) {
-        return hr;
-      }
-    } else {
-      ASSERT1(false);
-    }
+  hr = RegKey::GetValue(
+             ConfigManager::Instance()->registry_clients_goopdate(is_machine_),
+             kRegValueProductVersion,
+             &saved_version_);
+  if (FAILED(hr)) {
+    SETUP_LOG(L3, (_T("[failed to get existing Omaha version][0x%08x]"), hr));
+    // Continue as this is expected for first installs.
   }
 
   hr = setup_files->Install();
   if (FAILED(hr)) {
     SETUP_LOG(LE, (_T("[SetupFiles::Install failed][0x%08x]"), hr));
-    extra_code1_ = setup_files->extra_code1();
-    return hr;
-  }
-  ASSERT1(!setup_files->extra_code1());
-
-  scoped_event setup_complete_event;
-  VERIFY1(SUCCEEDED(goopdate_utils::CreateUniqueEventInEnvironment(
-      kSetupCompleteEventEnvironmentVariableName,
-      is_machine_,
-      address(setup_complete_event))));
-  // Continue on failure. We just will not be able to release the lock early.
-
-  hr = goopdate_utils::GetVerFromRegistry(is_machine_,
-                                          kGoogleUpdateAppId,
-                                          &saved_version_);
-  if (FAILED(hr)) {
-    SETUP_LOG(L3, (_T("[GetVerFromRegistry failed][0x%08x]"), hr));
-    // Continue as this is expected for first installs.
-  }
-
-  // Set the version so the constant shell will know which version to use.
-  hr = RegKey::SetValue(
-      ConfigManager::Instance()->registry_clients_goopdate(is_machine_),
-      kRegValueProductVersion,
-      GetVersionString());
-  if (FAILED(hr)) {
-    OPT_LOG(LE, (_T("[Failed to set version in registry][0x%08x]"), hr));
-    if (E_ACCESSDENIED == hr) {
-      return GOOPDATE_E_ACCESSDENIED_SETUP_REG_ACCESS;
-    }
+    extra_code1_ =
+        setup_files->extra_code1() | PingEvent::kSetupFilesExtraCodeMask;
     return hr;
   }
 
-  // Start the worker to run setup phase 2 and install the app.
-  scoped_process worker_process;
-  hr = LaunchInstalledWorker(true,  // Run setup phase 2.
-                             address(worker_process));
+  hr = SetupGoogleUpdate();
   if (FAILED(hr)) {
-    OPT_LOG(LE, (_T("[Failed to launch installed instance][0x%08x]"), hr));
+    SETUP_LOG(LE, (_T("[SetupGoogleUpdate failed][0x%08x]"), hr));
     return hr;
   }
 
-  // Wait for setup to complete to ensure the Setup Lock is held though the
-  // end of setup.
-  OPT_LOG(L1, (_T("[Waiting for setup to complete]")));
-  uint32 exit_code(0);
-  hr = WaitForProcessExitOrEvent(get(worker_process),
-                                 get(setup_complete_event),
-                                 &exit_code);
-  if (FAILED(hr)) {
-    OPT_LOG(LE, (_T("[Failed waiting for setup to complete][0x%08x]"), hr));
-    return hr;
-  }
-  if (exit_code) {
-    OPT_LOG(LE, (_T("[Setup exited with error][0x%08x]"), exit_code));
-    ASSERT1(FAILED(exit_code));
-
-    return exit_code;
-  }
-
+  // TODO(omaha3): Maybe move out of Setup.
   metric_setup_install_google_update_total_ms.AddSample(
       metrics_timer_->GetElapsedMs());
+
   return S_OK;
 }
 
@@ -882,6 +573,7 @@
         saved_version_)));
   }
 
+  // TODO(omaha3): Rollback SetupGoogleUpdate.
   VERIFY1(SUCCEEDED(setup_files->RollBack()));
 }
 
@@ -889,57 +581,40 @@
 // The original process holds the lock while it waits for this one to complete.
 HRESULT Setup::SetupGoogleUpdate() {
   SETUP_LOG(L2, (_T("[Setup::SetupGoogleUpdate]")));
-  ASSERT1(!IsElevationRequired());
-  mode_ = MODE_PHASE2;
 
   HighresTimer phase2_metrics_timer;
 
-  omaha::SetupGoogleUpdate setup_google_update(is_machine_, args_);
+  omaha::SetupGoogleUpdate setup_google_update(is_machine_);
 
   HRESULT hr = setup_google_update.FinishInstall();
   if (FAILED(hr)) {
-    SETUP_LOG(LE, (_T("[FinishInstall failed][0x%08x]"), hr));
-
-    // We shutdown the existing core. Start a core - which one depends on where
-    // we failed - again if possible.
-    // There is a chance we have messed up the launch mechanisms and the core
-    // will not even start on reboot.
-    VERIFY1(SUCCEEDED(StartCore()));
-
+    extra_code1_ = setup_google_update.extra_code1();
+    SETUP_LOG(LE, (_T("[FinishInstall failed][0x%x][0x%x]"), hr, extra_code1_));
     return hr;
   }
 
-  // Release the shutdown event, so we can start the core and do not interfere
-  // with other app installs that may be waiting on the Setup Lock.
-  NamedObjectAttributes event_attr;
-  GetShutdownEventAttributes(is_machine_, &event_attr);
-  scoped_event shutdown_event(::OpenEvent(EVENT_MODIFY_STATE,
-                                          false,
-                                          event_attr.name));
-  if (shutdown_event) {
-    VERIFY1(::ResetEvent(get(shutdown_event)));
-  } else {
-    SETUP_LOG(LW, (_T("[::OpenEvent failed][%s][%u]"),
-                  event_attr.name, ::GetLastError()));
+  // Release the shutdown event so that we can start the core if necessary, and
+  // we do not interfere with other app installs that may be waiting on the
+  // Setup Lock.
+  ASSERT1(shutdown_event_);
+  ReleaseShutdownEvents();
+
+  if (!scheduled_task_utils::IsUATaskHealthy(is_machine_)) {
+    HRESULT start_hr = StartCore();
+    if (FAILED(start_hr)) {
+      SETUP_LOG(LW, (_T("[StartCore failed][0x%x]"), start_hr));
+    }
   }
 
-  if (is_machine_) {
-    hr = StartMachineCoreProcess();
-    if (FAILED(hr)) {
-      SETUP_LOG(LE, (_T("[StartMachineCoreProcess failed][0x%08x]"), hr));
-      return hr;
-    }
-  } else {
-    CString core_cmd_line(setup_google_update.BuildCoreProcessCommandLine());
-    hr = StartUserCoreProcess(core_cmd_line);
-    if (FAILED(hr)) {
-      SETUP_LOG(LE, (_T("[StartUserCoreProcess failed][0x%08x]"), hr));
-      return hr;
-    }
+  // Registration of browser plugins is only done after the shutdown event has
+  // been released; this prevents race conditions where a browser could start
+  // a new install while the shutdown event was still being held.
+  HRESULT plugin_hr = setup_google_update.InstallBrowserPlugins();
+  if (FAILED(plugin_hr)) {
+    SETUP_LOG(LE, (_T("[InstallBrowserPlugins failed][0x%08x]"), plugin_hr));
   }
 
   // Setup is now complete.
-  SetSetupCompleteEvent();
 
   metric_setup_phase2_ms.AddSample(phase2_metrics_timer.GetElapsedMs());
   return S_OK;
@@ -950,10 +625,9 @@
 // Does not wait for the processes to exit, except the service.
 // Protects all operations with the setup lock. If MSI is found busy, Omaha
 // won't uninstall.
-HRESULT Setup::Uninstall() {
+HRESULT Setup::Uninstall(bool send_uninstall_ping) {
   OPT_LOG(L1, (_T("[Setup::Uninstall]")));
   ASSERT1(!IsElevationRequired());
-  mode_ = MODE_UNINSTALL;
 
   // Try to get the global setup lock; if the lock is taken, do not block
   // waiting to uninstall; just return.
@@ -964,46 +638,50 @@
     return E_FAIL;
   }
 
-  return DoProtectedUninstall();
+  return DoProtectedUninstall(send_uninstall_ping);
 }
 
 // Aggregates metrics regardless of whether uninstall is allowed.
 // Foces reporting of the metrics if uninstall is allowed.
 // Assumes that the current process holds the Setup Lock.
-HRESULT Setup::DoProtectedUninstall() {
+HRESULT Setup::DoProtectedUninstall(bool send_uninstall_ping) {
   const bool can_uninstall = CanUninstallGoogleUpdate();
   OPT_LOG(L1, (_T("[CanUninstallGoogleUpdate returned %d]"), can_uninstall));
-  ASSERT1(!IsElevationRequired());
 
-  if (can_uninstall) {
-    HRESULT hr = AggregateAndReportMetrics(is_machine_, true);
-    VERIFY1(SUCCEEDED(hr) || GOOPDATE_E_CANNOT_USE_NETWORK == hr);
-  } else {
-    VERIFY1(SUCCEEDED(AggregateMetrics(is_machine_)));
-  }
-
+  HRESULT hr = S_OK;
   if (!can_uninstall) {
-    return GOOPDATE_E_CANT_UNINSTALL;
-  }
-
-  if (is_machine_) {
-    HRESULT hr = SetupService::StopService();
+    hr = GOOPDATE_E_CANT_UNINSTALL;
+  } else {
+    hr = StopGoogleUpdateAndWait();
     if (FAILED(hr)) {
-      SETUP_LOG(LW, (_T("[SetupService::StopService failed][0x%08x]"), hr));
-      ASSERT1(HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) == hr);
+      // If there are any clients that don't listen to the shutdown event,
+      // such as the current Update3Web workers, we'll need to wait until
+      // they can be shut down.
+      // TODO(omaha3): We might want to add a count metric for this case,
+      // and maybe go through with the uninstall anyways after several tries.
+      SETUP_LOG(L1, (_T("[StopGoogleUpdateAndWait returned 0x%08x]"), hr));
+      hr = GOOPDATE_E_CANT_UNINSTALL;
     }
   }
-  VERIFY1(SUCCEEDED(SignalShutdownEvent()));
 
-  // TODO(omaha): Consider waiting for the Omaha processes to exit. This would
-  // need to exclude any processes, such as /ua /uninstall, that can uninstall.
+  if (FAILED(hr)) {
+    VERIFY1(SUCCEEDED(AggregateMetrics(is_machine_)));
+    return hr;
+  }
+  hr = AggregateAndReportMetrics(is_machine_, true);
+  ASSERT1(SUCCEEDED(hr) || GOOPDATE_E_CANNOT_USE_NETWORK == hr);
+
+  bool can_use_network = ConfigManager::Instance()->CanUseNetwork(is_machine_);
+  if (can_use_network && send_uninstall_ping) {
+    SendUninstallPing();
+  }
 
   // Write the event in the event log before uninstalling the program since
   // the event contains version and language information, which are removed
   // during the uninstall.
   WriteGoogleUpdateUninstallEvent(is_machine_);
 
-  omaha::SetupGoogleUpdate setup_google_update(is_machine_, args_);
+  omaha::SetupGoogleUpdate setup_google_update(is_machine_);
   setup_google_update.Uninstall();
 
   SetupFiles setup_files(is_machine_);
@@ -1074,24 +752,7 @@
 HRESULT Setup::StopGoogleUpdate() {
   OPT_LOG(L1, (_T("[Stopping other instances]")));
 
-  // In the machine install case, we should first stop the service.
-  // This means that we should not get the service as a process to wait
-  // on when the machine goopdate tries to enumerate the processes.
-  if (is_machine_) {
-    OPT_LOG(L1, (_T("[Stopping service]")));
-    HRESULT hr = SetupService::StopService();
-    if (FAILED(hr)) {
-      OPT_LOG(LE, (_T("[StopService failed][0x%08x]"), hr));
-    }
-  }
-
-  HRESULT hr = SignalLegacyShutdownEvents();
-  if (FAILED(hr)) {
-    SETUP_LOG(LE, (_T("[SignalLegacyShutdownEvents failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  hr = SignalShutdownEvent();
+  HRESULT hr = SignalShutdownEvent();
   if (FAILED(hr)) {
     SETUP_LOG(LE, (_T("[SignalShutdownEvent failed][0x%08x]"), hr));
     return hr;
@@ -1143,97 +804,13 @@
   return S_OK;
 }
 
-// Signals the quiet mode events for 1.0.x (pre-i18n) and 1.1.x (i18n)
-// processes, causing them to exit.
-HRESULT Setup::SignalLegacyShutdownEvents() {
-  SETUP_LOG(L1, (_T("[Setup::SignalLegacyShutdownEvents]")));
-
-  // Signal 1.0.x (pre-i18n) processes.
-  CString sid;
-  HRESULT hr = GetAppropriateSid(&sid);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  CString legacy_1_0_shutdown_event_name;
-  legacy_1_0_shutdown_event_name.Format(kEventLegacyQuietModeName, sid);
-
-  hr = CreateLegacyEvent(legacy_1_0_shutdown_event_name,
-                         address(legacy_1_0_shutdown_event_));
-  if (FAILED(hr)) {
-    SETUP_LOG(LE, (_T("[CreateLegacyEvent legacy quiet mode failed][0x%08x]"),
-                  hr));
-    return hr;
-  }
-
-
-  // Signal 1.1.x (i18n) processes.
-  // 1.1 used the same event GUID in a different way.
-  CString legacy_1_1_shutdown_event_name(is_machine_ ? kOmaha11GlobalPrefix :
-                                                       kOmaha11LocalPrefix);
-  legacy_1_1_shutdown_event_name.Append(kShutdownEvent);
-
-  hr = CreateLegacyEvent(legacy_1_1_shutdown_event_name,
-                         address(legacy_1_1_shutdown_event_));
-  if (FAILED(hr)) {
-    SETUP_LOG(LE, (_T("[CreateLegacyEvent quiet mode failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  VERIFY1(::SetEvent(get(legacy_1_0_shutdown_event_)));
-  VERIFY1(::SetEvent(get(legacy_1_1_shutdown_event_)));
-
-  return S_OK;
-}
-
-// The caller is responsible for reseting the event and closing the handle.
-HRESULT Setup::CreateLegacyEvent(const CString& event_name,
-                                 HANDLE* event_handle) const {
-  ASSERT1(event_handle);
-  ASSERT1(!event_name.IsEmpty());
-  CSecurityAttributes sa;
-  if (is_machine_) {
-    // Grant access to administrators and system. This allows an admin
-    // instance to open and modify the event even if the system created it.
-    GetAdminDaclSecurityAttributes(&sa, GENERIC_ALL);
-  }
-  *event_handle = ::CreateEvent(&sa,
-                                true,   // manual reset
-                                false,  // not signaled
-                                event_name);
-
-  if (!*event_handle) {
-    DWORD error = ::GetLastError();
-    SETUP_LOG(LEVEL_ERROR, (_T("[::CreateEvent failed][%u]"), error));
-    return HRESULT_FROM_WIN32(error);
-  }
-
-  return S_OK;
-}
-
 void Setup::ReleaseShutdownEvents() {
-  VERIFY1(::ResetEvent(get(shutdown_event_)));
-  reset(shutdown_event_);
-  VERIFY1(::ResetEvent(get(legacy_1_0_shutdown_event_)));
-  reset(legacy_1_0_shutdown_event_);
-  VERIFY1(::ResetEvent(get(legacy_1_1_shutdown_event_)));
-  reset(legacy_1_1_shutdown_event_);
-}
-
-void Setup::SetSetupCompleteEvent() const {
-  scoped_event complete_event;
-  HRESULT hr = goopdate_utils::OpenUniqueEventFromEnvironment(
-      kSetupCompleteEventEnvironmentVariableName,
-      is_machine_,
-      address(complete_event));
-
-  if (FAILED(hr)) {
-    // We just will not be able to release the lock early.
+  if (!shutdown_event_) {
     return;
   }
 
-  ASSERT1(complete_event);
-  VERIFY1(::SetEvent(get(complete_event)));
+  VERIFY1(::ResetEvent(get(shutdown_event_)));
+  reset(shutdown_event_);
 }
 
 // Because this waiting can occur before a UI is generated, we do not want to
@@ -1268,22 +845,19 @@
   if (!handles.empty()) {
     SETUP_LOG(L2, (_T("[Calling ::WaitForMultipleObjects]")));
 
-    // In the /install case, there is no UI so we cannot wait too long.
-    // In other cases, we are silent or some other app is displaying a UI.
-    const int shutdown_wait_ms = IsInteractiveInstall() ?
-                                 kSetupShutdownWaitMsInteractiveNoUi :
-                                 kSetupShutdownWaitMsSilent;
     HighresTimer metrics_timer;
+    const int wait_ms = is_self_update_ ? kSetupUpdateShutdownWaitMs :
+                                          kSetupInstallShutdownWaitMs;
     DWORD res = ::WaitForMultipleObjects(handles.size(),
                                          &handles.front(),
                                          true,  // wait for all
-                                         shutdown_wait_ms);
+                                         wait_ms);
     metric_setup_process_wait_ms.AddSample(metrics_timer.GetElapsedMs());
 
     SETUP_LOG(L2, (_T("[::WaitForMultipleObjects returned]")));
     ASSERT1(WAIT_OBJECT_0 == res || WAIT_TIMEOUT == res);
     if (WAIT_FAILED == res) {
-      DWORD error = ::GetLastError();
+      const DWORD error = ::GetLastError();
       SETUP_LOG(LE, (_T("[::WaitForMultipleObjects failed][%u]"), error));
       hr = HRESULT_FROM_WIN32(error);
     } else if (WAIT_OBJECT_0 != res) {
@@ -1320,6 +894,22 @@
   }
   if (SUCCEEDED(hr)) {
     SETUP_LOG(L3, (_T("[Wait for all processes to exit succeeded]")));
+  } else {
+    for (size_t i = 0; i < handles.size(); ++i) {
+      if (!::TerminateProcess(handles[i], UINT_MAX)) {
+        const uint32 pid = Process::GetProcessIdFromHandle(handles[i]);
+        const DWORD error = ::GetLastError();
+        SETUP_LOG(LW, (_T("[::TerminateProcess failed][%u][%u]"), pid, error));
+      }
+    }
+
+    const int kTerminateWaitMs = 500;
+    DWORD res = ::WaitForMultipleObjects(handles.size(), &handles.front(),
+                                         true, kTerminateWaitMs);
+    if (res != WAIT_OBJECT_0) {
+      const DWORD error = ::GetLastError();
+      SETUP_LOG(LW, (_T("[Wait failed][%u][%u]"), res, error));
+    }
   }
 
   // Close the handles.
@@ -1330,15 +920,12 @@
     }
   }
 
-  return hr;
+  return S_OK;
 }
 
-// Wait for all instances of Omaha running as the current user - or SYSTEM for
-// machine installs - except "/install" instances, which should be blocked by
-// the Setup Lock, which we are holding.
-// For machine installs, also wait for instances running with ("/handoff" OR
-// "/ig") AND "needsadmin=True" running as any user. These instances are
-// installing a machine app as a non-SYSTEM user and may also cause conflicts.
+// Wait for all instances of Omaha running as the current user - or as any user
+// in the case of machine installs - except "/install" or "/registerproduct"
+// instances, which should be blocked by the Setup Lock, which we are holding.
 HRESULT Setup::GetPidsToWaitFor(Pids* pids) const {
   ASSERT1(pids);
 
@@ -1346,128 +933,94 @@
   if (FAILED(hr)) {
     return hr;
   }
-  SETUP_LOG(L3, (_T("[found %d processes using cmd line]"), pids->size()));
 
-  // Remove any copies of the current PID from the list. This can happen when
-  // doing self-updates.
-  const uint32 current_pid = ::GetCurrentProcessId();
-  Pids::iterator it = std::remove(pids->begin(), pids->end(), current_pid);
-  if (pids->end() != it) {
-    SETUP_LOG(L2, (_T("[removing current PID from list of PIDs]")));
-  }
-  pids->erase(it, pids->end());
-
+  ASSERT1(pids->end() == std::find(pids->begin(), pids->end(),
+                                   ::GetCurrentProcessId()));
   SETUP_LOG(L3, (_T("[found %d total processes to wait for]"), pids->size()));
 
   return S_OK;
 }
 
-// Finds legacy processes to wait for based on the command line.
+// Finds processes to wait for based on the command line.
+// Differences between Omaha 2 and this code:
+//  * User's processes running outside AppData are not caught. This should only
+//    be /install or /registerproduct.
+//  * In the user case, "machine" processes running as the user are EXcluded
+//    based on path instead of mode and "needsadmin=true". This additionally
+//    excludes oneclick cross-installs (u-to-m).
+//  * In the machine case, "machine" processes running as the user are INcluded
+//    based on path instead of mode and "needsadmin=true".
+//  * /pi: m-to-u running in PF as user with needsadmin=false: now INcluded.
+//    This is a good idea, since we do not want to delete the in-use file.
+//    /pi: u-to-m running in appdata as user with needsadmin=true: now
+//    EXcluded.
 HRESULT Setup::GetPidsToWaitForUsingCommandLine(Pids* pids) const {
+  CORE_LOG(L3, (_T("[Setup::GetPidsToWaitForUsingCommandLine]")));
+
   ASSERT1(pids);
 
-  CString user_sid;
-  HRESULT hr = GetAppropriateSid(&user_sid);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // Get all processes running as the current user/SYSTEM except those with
+  // Get processes running as the current user in the case of user, and all
+  // users as well as SYSTEM in the case of machine, except those with
   // * "/install" - must be excluded because may be waiting for the Setup Lock.
   // * "/registerproduct" - same as for /install.
-  // * "/installelevated" OR "/ui" - legacy switches used only for machine
-  //   installs but ran as user and could cause false positives. For machine
-  //   installs, we look for these running as any user in a separate search.
   std::vector<CString> command_lines;
   CString switch_to_exclude;
   switch_to_exclude.Format(_T("/%s"), kCmdLineInstall);
   command_lines.push_back(switch_to_exclude);
   switch_to_exclude.Format(_T("/%s"), kCmdLineRegisterProduct);
-  command_lines.push_back(switch_to_exclude);
-  switch_to_exclude.Format(_T("/%s"), kCmdLineLegacyVistaInstall);
-  command_lines.push_back(switch_to_exclude);
-  switch_to_exclude.Format(_T("/%s"), kCmdLineLegacyUi);
-  command_lines.push_back(switch_to_exclude);
 
-  DWORD flags = INCLUDE_ONLY_PROCESS_OWNED_BY_USER |
-                EXCLUDE_CURRENT_PROCESS |
+  CString user_sid;
+  DWORD flags = EXCLUDE_CURRENT_PROCESS |
                 EXCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING;
-  Pids found_pids;
-  hr = Process::FindProcesses(flags,
-                              kGoopdateFileName,
-                              true,
-                              user_sid,
-                              command_lines,
-                              &found_pids);
-  if (FAILED(hr)) {
-    SETUP_LOG(LE, (_T("[FindProcesses failed][0x%08x]"), hr));
-    return hr;
-  }
-  for (size_t i = 0; i < found_pids.size(); ++i) {
-    pids->push_back(found_pids[i]);
-  }
 
-  // Get all processes running as any user with:
-  // * "/handoff" or "/ig" and"needsadmin=True"
-  // * "-Embedding" and running from machine install location.
-  std::vector<uint32> machine_install_worker_pids;
-  hr = goopdate_utils::GetInstallWorkerProcesses(true,
-                                                 &machine_install_worker_pids);
-  if (FAILED(hr)) {
-    SETUP_LOG(LE, (_T("[GetInstallWorkerProcesses failed][0x%08x]"), hr));
-    return hr;
-  }
+  if (!is_machine_) {
+    // Search only the same sid as the current user.
+    flags |= INCLUDE_ONLY_PROCESS_OWNED_BY_USER;
 
-  if (is_machine_) {
-    // Add all machine install worker pids to the list of pids to wait for.
-    for (size_t i = 0; i < machine_install_worker_pids.size(); ++i) {
-      pids->push_back(machine_install_worker_pids[i]);
+    HRESULT hr = user_info::GetProcessUser(NULL, NULL, &user_sid);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[GetProcessUser failed][0x%x]"), hr));
+      return hr;
     }
-  } else {
-    // Remove machine install worker pids from the list of pids to wait for.
-    for (size_t i = 0; i < machine_install_worker_pids.size(); ++i) {
-      std::vector<uint32>::iterator iter = find(pids->begin(),
-                                                pids->end(),
-                                                machine_install_worker_pids[i]);
-      if (pids->end() != iter) {
-        pids->erase(iter);
+  }
+
+  std::vector<uint32> google_update_process_ids;
+  HRESULT hr = Process::FindProcesses(flags,
+                                      kOmahaShellFileName,
+                                      true,
+                                      user_sid,
+                                      command_lines,
+                                      &google_update_process_ids);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T(" [FindProcesses failed][0x%08x]"), hr));
+    return hr;
+  }
+
+  const ConfigManager* cm = ConfigManager::Instance();
+  CString official_path(is_machine_ ?
+                        cm->GetMachineGoopdateInstallDirNoCreate() :
+                        cm->GetUserGoopdateInstallDirNoCreate());
+  ASSERT1(!official_path.IsEmpty());
+
+  // Only include processes running under the official path.
+  Pids pids_to_wait_for;
+  for (size_t i = 0; i < google_update_process_ids.size(); ++i) {
+    CString cmd_line;
+    const uint32 process_id = google_update_process_ids[i];
+    if (SUCCEEDED(Process::GetCommandLine(process_id, &cmd_line))) {
+      cmd_line.MakeLower();
+
+      CString exe_path;
+      if (SUCCEEDED(GetExePathFromCommandLine(cmd_line, &exe_path)) &&
+          String_StrNCmp(official_path, exe_path, official_path.GetLength(),
+                         true) == 0) {
+        CORE_LOG(L4, (_T(" [Including pid][%u][%s]"), process_id, cmd_line));
+        pids_to_wait_for.push_back(process_id);
       }
     }
   }
 
-  if (is_machine_) {
-    // Find legacy machine install processes that run as user. Check all users.
-    std::vector<CString> legacy_machine_command_lines;
-    CString switch_to_include;
-    switch_to_include.Format(_T("/%s"), kCmdLineLegacyVistaInstall);
-    command_lines.push_back(switch_to_include);
-    switch_to_include.Format(_T("/%s"), kCmdLineLegacyUi);
-    command_lines.push_back(switch_to_include);
-
-    DWORD flags = EXCLUDE_CURRENT_PROCESS |
-                  INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING;
-    Pids found_legacy_machine_pids;
-    hr = Process::FindProcesses(flags,
-                                kGoopdateFileName,
-                                true,
-                                user_sid,
-                                legacy_machine_command_lines,
-                                &found_legacy_machine_pids);
-    if (FAILED(hr)) {
-      SETUP_LOG(LE, (_T("[FindProcesses failed][0x%08x]"), hr));
-      return hr;
-    }
-    for (size_t i = 0; i < found_legacy_machine_pids.size(); ++i) {
-      // Because we excluded these processes in the first search, we should not
-      // get any duplicates.
-      ASSERT1(found_pids.end() ==
-              std::find(found_pids.begin(),
-                        found_pids.end(),
-                        found_legacy_machine_pids[i]));
-      pids->push_back(found_legacy_machine_pids[i]);
-    }
-  }
-
+  pids->swap(pids_to_wait_for);
   return S_OK;
 }
 
@@ -1478,244 +1031,6 @@
   return is_machine_ && !vista_util::IsUserAdmin();
 }
 
-// The behavior depends on the OS:
-//  1. OS >= Vista : Try to elevate - causes a UAC dialog.
-//  2. OS < Vista  : Fail with a message box.
-// We should be here only in case of initial machine installs when the user is
-// not an elevated admin.
-HRESULT Setup::ElevateAndWait(const CString& cmd_line) {
-  OPT_LOG(L1, (_T("[Elevating][%s]"), cmd_line));
-  ASSERT1(is_machine_);
-  ASSERT1(!vista_util::IsUserAdmin());
-  ASSERT1(MODE_INSTALL == mode_);
-
-  if (!IsInteractiveInstall()) {
-    return GOOPDATE_E_SILENT_INSTALL_NEEDS_ELEVATION;
-  }
-
-  if (args_->is_install_elevated) {
-    // This can happen if UAC is disabled. See http://b/1187784.
-    SETUP_LOG(LE, (_T("[Install elevated process requires elevation]")));
-    return GOOPDATE_E_INSTALL_ELEVATED_PROCESS_NEEDS_ELEVATION;
-  }
-
-  if (!vista_util::IsVistaOrLater()) {
-    // TODO(omaha): We could consider to ask for credentials here.
-    // This TODO existed in Omaha 1. How would we even do this?
-    SETUP_LOG(LE, (_T("[Non Admin trying to install admin app]")));
-    ++metric_setup_machine_app_non_admin;
-    return GOOPDATE_E_NONADMIN_INSTALL_ADMIN_APP;
-  }
-
-  CString cmd_line_elevated(GetCmdLineTail(cmd_line));
-  cmd_line_elevated.AppendFormat(_T(" /%s"), kCmdLineInstallElevated);
-
-  HRESULT hr = goopdate_utils::StartElevatedSelfWithArgsAndWait(
-      cmd_line_elevated);
-  if (FAILED(hr)) {
-    OPT_LOG(LE, (_T("[Starting elevated GoogleUpdate.exe failed][%s][0x%08x]"),
-                 cmd_line, hr));
-
-    extra_code1_ = hr;
-    if (vista_util::IsUserNonElevatedAdmin()) {
-      return GOOPDATE_E_ELEVATION_FAILED_ADMIN;
-    } else {
-      return GOOPDATE_E_ELEVATION_FAILED_NON_ADMIN;
-    }
-  }
-
-  // TODO(omaha): we might have to look at the exit_code from above.
-  // Do not know what we should do in case of an error.
-
-  return S_OK;
-}
-
-HRESULT Setup::CopyOfflineFilesForGuid(const CString& app_guid,
-                                       const CString& offline_dir) {
-  SETUP_LOG(L3, (_T("[Setup::CopyOfflineFilesForGuid][%s][%s]"),
-                 app_guid, offline_dir));
-
-  CPath setup_temp_dir(app_util::GetCurrentModuleDirectory());
-
-  // Copy offline manifest into "Google\Update\Offline\{guid}.gup".
-  CString manifest_filename = app_guid + _T(".gup");
-  CString source_manifest_path = ConcatenatePath(setup_temp_dir,
-                                                 manifest_filename);
-  if (!File::Exists(source_manifest_path)) {
-    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
-  }
-  if (!File::IsDirectory(offline_dir)) {
-    VERIFY1(SUCCEEDED(CreateDir(offline_dir, NULL)));
-  }
-  CString dest_manifest_path = ConcatenatePath(offline_dir,
-                                               manifest_filename);
-  HRESULT hr = File::Copy(source_manifest_path, dest_manifest_path, true);
-  if (FAILED(hr)) {
-    SETUP_LOG(L4, (_T("[File copy failed][%s][%s][0x%x]"),
-                   source_manifest_path, dest_manifest_path, hr));
-    return hr;
-  }
-
-  CString pattern;
-  // Find the installer file. "Installer.exe.{guid}". Only one file per guid is
-  // supported. Store "Installer.exe" in the directory
-  // "Google\Update\Offline\{guid}".
-  pattern.Format(_T("*.%s"), app_guid);
-  std::vector<CString> files;
-  hr = FindFiles(setup_temp_dir, pattern, &files);
-  if (FAILED(hr)) {
-    SETUP_LOG(L4, (_T("[FindFiles failed][0x%x]"), hr));
-    return hr;
-  }
-  if (files.empty()) {
-    SETUP_LOG(L4, (_T("[FindFiles found no files]")));
-    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
-  }
-
-  ASSERT1(files.size() == 1);
-  ASSERT1(files[0].GetLength() > app_guid.GetLength());
-
-  CString file_path = ConcatenatePath(setup_temp_dir, files[0]);
-  CString renamed_file_name = files[0].Left(files[0].GetLength() -
-                                            app_guid.GetLength() - 1);
-  CString offline_app_dir = ConcatenatePath(offline_dir, app_guid);
-  CString new_file_path = ConcatenatePath(offline_app_dir, renamed_file_name);
-  SETUP_LOG(L4, (_T("[new_file_path][%s]"), new_file_path));
-
-  if (File::IsDirectory(offline_app_dir)) {
-    VERIFY1(SUCCEEDED(DeleteDirectoryFiles(offline_app_dir)));
-  } else {
-    hr = CreateDir(offline_app_dir, NULL);
-    if (FAILED(hr)) {
-      SETUP_LOG(L3, (_T("[CreateDir failed][%s]"), offline_app_dir));
-      return hr;
-    }
-  }
-
-  return File::Copy(file_path, new_file_path, false);
-}
-
-bool Setup::CopyOfflineFiles(const CString& offline_dir) {
-  SETUP_LOG(L3, (_T("[Setup::CopyOfflineFiles][%s]"), offline_dir));
-
-  ASSERT1(!args_->extra.apps.empty());
-  if (args_->extra.apps.empty()) {
-    return false;
-  }
-
-  for (size_t i = 0; i < args_->extra.apps.size(); ++i) {
-    const GUID& app_guid = args_->extra.apps[i].app_guid;
-    HRESULT hr = CopyOfflineFilesForGuid(GuidToString(app_guid), offline_dir);
-    if (FAILED(hr)) {
-      SETUP_LOG(L3, (_T("[CopyOfflineFilesForGuid failed][0x%x]"), hr));
-      return false;
-    }
-  }
-
-  return true;
-}
-
-// The installed worker is in the final location and can access the network to
-// provide stats and error information.
-// worker_pi can be NULL.
-// TODO(omaha): Extract the command line building and unit test it.
-// Starts the file that was just installed or the registered version of Google
-// Update depending on whether Google Update is being installed
-// (do_setup_phase_2).
-// If do_setup_phase_2 is true, assumes the new version has been set in the
-// registry.
-// SelfInstall uses UG with the /machine override because UG silently completes
-// setup without installing an app and SelfInstall may not be running as
-// LocalSystem.
-// Detects whether this is an offline install and stages the files
-// appropriately.
-// Reports errors when when offline files are not present for scenarios that
-// require offline installs.
-HRESULT Setup::LaunchInstalledWorker(bool do_setup_phase_2, HANDLE* process) {
-  SETUP_LOG(L2, (_T("[Setup::LaunchInstalledWorker]")));
-
-  bool is_offline = false;
-  CommandLineMode cmd_line_mode = COMMANDLINE_MODE_UNKNOWN;
-  if (MODE_INSTALL == mode_) {
-    // If offline binaries are found the program enters the offline mode.
-    is_offline = CopyOfflineFiles(is_machine_ ?
-        ConfigManager::Instance()->GetMachineSecureOfflineStorageDir() :
-        ConfigManager::Instance()->GetUserOfflineStorageDir());
-    cmd_line_mode = do_setup_phase_2 ? COMMANDLINE_MODE_IG :
-                                       COMMANDLINE_MODE_HANDOFF_INSTALL;
-  } else {
-    ASSERT1(!args_->is_oem_set);
-    cmd_line_mode = COMMANDLINE_MODE_UG;
-  }
-
-  CommandLineBuilder builder(cmd_line_mode);
-  if (is_offline) {
-    builder.set_is_offline_set(is_offline);
-    // If installsource is present on the command line, it will override this.
-    builder.set_install_source(kCmdLineInstallSource_Offline);
-  } else if (args_->is_oem_set) {
-    return GOOPDATE_E_OEM_WITH_ONLINE_INSTALLER;
-  } else if (args_->is_eula_required_set) {
-    return GOOPDATE_E_EULA_REQURED_WITH_ONLINE_INSTALLER;
-  }
-
-  switch (mode_) {
-    case MODE_INSTALL:
-      builder.set_is_silent_set(args_->is_silent_set);
-      builder.set_is_eula_required_set(args_->is_eula_required_set);
-      ASSERT1(!args_->extra_args_str.IsEmpty());
-      builder.set_extra_args(args_->extra_args_str);
-      builder.set_app_args(args_->app_args_str);
-      if (!args_->install_source.IsEmpty()) {
-        builder.set_install_source(args_->install_source);
-      }
-      break;
-    case MODE_SELF_UPDATE:
-      ASSERT1(do_setup_phase_2);
-      ASSERT1(args_->extra_args_str.IsEmpty());
-      break;
-    case MODE_SELF_INSTALL:
-      ASSERT1(do_setup_phase_2);
-      ASSERT1(!args_->extra_args_str.IsEmpty());
-      builder.set_is_machine_set(is_machine_);
-      break;
-    case MODE_REPAIR:
-      ASSERT1(do_setup_phase_2);
-      ASSERT1(args_->extra_args_str.IsEmpty());
-      builder.set_is_machine_set(is_machine_);
-      break;
-    case MODE_UNKNOWN:
-    case MODE_PHASE2:
-    case MODE_UNINSTALL:
-    default:
-      ASSERT1(false);
-      break;
-  }
-
-  CString cmd_line = builder.GetCommandLineArgs();
-
-  HRESULT hr = goopdate_utils::StartGoogleUpdateWithArgs(is_machine_,
-                                                         cmd_line,
-                                                         process);
-  if (FAILED(hr)) {
-    if (do_setup_phase_2) {
-      OPT_LOG(LE, (_T("[Starting Google Update failed][%s][0x%08x]"),
-                   cmd_line, hr));
-      ASSERT1(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) != hr);
-      return hr;
-    } else {
-      OPT_LOG(LE, (_T("[Google Update hand off failed][%s][0x%08x]"),
-                   cmd_line, hr));
-      extra_code1_ = hr;
-      return GOOPDATE_E_HANDOFF_FAILED;
-    }
-  }
-
-  launched_offline_worker_ = is_offline;
-
-  return S_OK;
-}
-
 // Start the machine core process using one of the launch mechanisms.
 // We know that at least one of the service and scheduled task were installed
 // because otherwise we would have exited fatally.
@@ -1731,7 +1046,7 @@
 
   // Start the service.
   ++metric_setup_start_service_total;
-  HRESULT service_hr = SetupService::StartService();
+  HRESULT service_hr = SetupUpdate3Service::StartService();
   if (SUCCEEDED(service_hr)) {
     metric_setup_start_service_ms.AddSample(metrics_timer.GetElapsedMs());
     OPT_LOG(L1, (_T("[Service started]")));
@@ -1746,7 +1061,7 @@
   // and there are no other apps installed. Guarantee this somehow.
   ++metric_setup_start_task_total;
   const ULONGLONG start_task_start_ms = metrics_timer.GetElapsedMs();
-  HRESULT task_hr = goopdate_utils::StartGoopdateTaskCore(true);
+  HRESULT task_hr = scheduled_task_utils::StartGoopdateTaskCore(true);
   if (SUCCEEDED(task_hr)) {
     const ULONGLONG start_task_end_ms = metrics_timer.GetElapsedMs();
     ASSERT1(start_task_end_ms >= start_task_start_ms);
@@ -1763,10 +1078,10 @@
 }
 
 // Start the user core process directly.
-// do not call this method until the shutdown event has been released or the
+// Do not call this method until the shutdown event has been released or the
 // process may immediately exit.
 HRESULT Setup::StartUserCoreProcess(const CString& core_cmd_line) const {
-  HRESULT hr = System::ShellExecuteCommandLine(core_cmd_line, NULL, NULL);
+  HRESULT hr = System::StartCommandLine(core_cmd_line);
   if (FAILED(hr)) {
     SETUP_LOG(LE, (_T("[Could not start Google Update Core][0x%08x]"), hr));
     return hr;
@@ -1794,7 +1109,7 @@
                 EXCLUDE_CURRENT_PROCESS |
                 INCLUDE_PROCESS_COMMAND_LINE_CONTAINING_STRING;
   hr = Process::FindProcesses(flags,
-                              kGoopdateFileName,
+                              kOmahaShellFileName,
                               true,
                               user_sid,
                               command_lines,
@@ -1862,7 +1177,7 @@
   SETUP_LOG(L2, (_T("[::WaitForMultipleObjects returned]")));
   ASSERT1(WAIT_OBJECT_0 == res || WAIT_TIMEOUT == res);
   if (WAIT_FAILED == res) {
-    DWORD error = ::GetLastError();
+    const DWORD error = ::GetLastError();
     SETUP_LOG(LE, (_T("[::WaitForMultipleObjects failed][%u]"), error));
     hr = HRESULT_FROM_WIN32(error);
   } else {
@@ -1877,11 +1192,10 @@
 }
 
 // Tries to start the core using existing launch methods if present.
-// Uses the the service registry value, scheduled task, and Run key value names
-// for this version; if the ones in the installed version are are different,
-// this method will not be able to start the core.
+// Uses the service or the scheduled task for machine, and the Run key value for
+// user.
 HRESULT Setup::StartCore() const {
-  SETUP_LOG(L2, (_T("[Attempting to start existing core]")));
+  SETUP_LOG(L2, (_T("[Attempting to start core]")));
 
   if (is_machine_) {
     HRESULT hr = StartMachineCoreProcess();
@@ -1889,117 +1203,23 @@
       SETUP_LOG(LW, (_T("[StartMachineCoreProcess failed][0x%08x]"), hr));
       return hr;
     }
-  } else {
-    // Read the Run key entry to determine how to run the version that we
-    // believe is installed, which may not be this instance's version or even
-    // the the version in the registry if it has been over-written by us.
-    CString run_key_path = AppendRegKeyPath(USER_KEY_NAME, REGSTR_PATH_RUN);
-    RegKey key;
-    HRESULT hr = key.Open(run_key_path);
-    if (FAILED(hr)) {
-      SETUP_LOG(LW, (_T("[Failed to open Run key][%s][0x%08x]"),
-                     run_key_path, hr));
-      return hr;
-    }
 
-    CString installed_run_cmd_line;
-    hr = key.GetValue(kRunValueName, &installed_run_cmd_line);
-    if (FAILED(hr)) {
-      SETUP_LOG(LW, (_T("[Failed to get Run value][%s][0x%08x]"),
-                     kRunValueName, hr));
-      return hr;
-    }
-
-    hr = StartUserCoreProcess(installed_run_cmd_line);
-    if (FAILED(hr)) {
-      SETUP_LOG(LW, (_T("[StartUserCoreProcess failed][%s][0x%08x]"),
-                     installed_run_cmd_line, hr));
-      return hr;
-    }
-  }
-
-  return S_OK;
-}
-
-// If the process exits without signaling the event, its exit code is returned.
-// Waits for the process to exit (and ignore the event) unless this is an
-// interactive install.
-// If event is NULL, waits for the process to exit.
-HRESULT Setup::WaitForProcessExitOrEvent(HANDLE process,
-                                         HANDLE event,
-                                         uint32* exit_code) const {
-  SETUP_LOG(L3, (_T("[Setup::WaitForProcessExitOrEvent]")));
-  ASSERT1(process);
-  ASSERT1(exit_code);
-  *exit_code = 0;
-
-  const bool include_event_in_wait = event && !ShouldWaitForWorkerProcess();
-  HANDLE handles[] = {process, event};
-
-  const int num_handles = arraysize(handles) - (include_event_in_wait ? 0 : 1);
-  const int kProcessSignaled = WAIT_OBJECT_0;
-  const int kEventSignaled = WAIT_OBJECT_0 + 1;
-
-  int res = ::WaitForMultipleObjects(num_handles,
-                                     handles,
-                                     false,  // wait for any one
-                                     INFINITE);
-  ASSERT1(kProcessSignaled == res ||
-          (kEventSignaled == res) && include_event_in_wait);
-  if (WAIT_FAILED == res) {
-    DWORD error = ::GetLastError();
-    SETUP_LOG(LE, (_T("[::WaitForMultipleObjects failed][%u]"), error));
-    return HRESULT_FROM_WIN32(error);
-  }
-
-  // If the process exited, get the exit code.
-  if (kProcessSignaled == res) {
-    DWORD local_exit_code = 0;
-    if (::GetExitCodeProcess(process, &local_exit_code)) {
-      SETUP_LOG(L2, (_T("[process exited][PID %u][exit code 0x%08x]"),
-                    Process::GetProcessIdFromHandle(process), local_exit_code));
-      *exit_code = local_exit_code;
-    } else {
-      DWORD error = ::GetLastError();
-      SETUP_LOG(LE, (_T("[::GetExitCodeProcess failed][%u]"), error));
-      return HRESULT_FROM_WIN32(error);
-    }
-  } else {
-    ASSERT1(kEventSignaled == res);
-    ASSERT1(2 == num_handles);
-    SETUP_LOG(L2, (_T("[event received before process exited]")));
-  }
-
-  return S_OK;
-}
-
-// Requires that the UI displayed event and
-// kUiDisplayedEventEnvironmentVariableName environment variable exist.
-HRESULT Setup::WaitForHandoffWorker(HANDLE process) const {
-  HANDLE ui_displayed_event(INVALID_HANDLE_VALUE);
-  HRESULT hr = UIDisplayedEventManager::GetEvent(is_machine_,
-                                                 &ui_displayed_event);
-  if (FAILED(hr)) {
-    // The event was created in this process, so this should always succeed.
-    ASSERT(false, (_T("OpenUniqueEventFromEnvironment failed][0x%08x]"), hr));
-    // Only wait for the process to exit.
-  }
-
-  uint32 exit_code(0);
-  hr = WaitForProcessExitOrEvent(process, ui_displayed_event, &exit_code);
-  if (FAILED(hr)) {
-    SETUP_LOG(LE, (_T("[WaitForProcessExitOrEvent failed][0x%08x]"), hr));
     return hr;
   }
-  if (exit_code) {
-    OPT_LOG(LE, (_T("[Handoff exited with error][0x%08x]"), exit_code));
-    ASSERT1(FAILED(exit_code));
-    return exit_code;
+
+  CString installed_run_cmd_line;
+  HRESULT hr = RegKey::GetValue(USER_KEY REGSTR_PATH_RUN, kRunValueName,
+                                &installed_run_cmd_line);
+  if (FAILED(hr)) {
+    SETUP_LOG(LW, (_T("[Failed to get Run val][%s][0x%x]"), kRunValueName, hr));
+    return hr;
   }
 
-  if (ui_displayed_event &&
-      WAIT_OBJECT_0 == ::WaitForSingleObject(ui_displayed_event, 0)) {
-    metric_setup_handoff_ui_ms.AddSample(metrics_timer_->GetElapsedMs());
+  hr = StartUserCoreProcess(installed_run_cmd_line);
+  if (FAILED(hr)) {
+    SETUP_LOG(LW, (_T("[StartUserCoreProcess failed][%s][0x%x]"),
+                   installed_run_cmd_line, hr));
+    return hr;
   }
 
   return S_OK;
@@ -2011,9 +1231,9 @@
   if (is_machine_) {
     *sid = kLocalSystemSid;
   } else {
-    HRESULT hr = user_info::GetCurrentUser(NULL, NULL, sid);
+    HRESULT hr = user_info::GetProcessUser(NULL, NULL, sid);
     if (FAILED(hr)) {
-      SETUP_LOG(LEVEL_ERROR, (_T("[GetCurrentUser failed][0x%08x]"), hr));
+      SETUP_LOG(LEVEL_ERROR, (_T("[GetProcessUser failed][0x%08x]"), hr));
       return hr;
     }
   }
@@ -2029,205 +1249,130 @@
                                            &setup_lock_attr.sa);
 }
 
-bool Setup::InitLegacySetupLocks(GLock* lock10,
-                                 GLock* lock11_user,
-                                 GLock* lock11_machine) {
-  ASSERT1(lock10);
-  ASSERT1(lock11_user);
-  ASSERT1(lock11_machine);
-
-  // Omaha 1.0 ensures "there is only one instance of goopdate that is trying to
-  // install at a time." Thus, the lock is the same for machine and user.
-  CString mutex10_name(kOmaha10GlobalPrefix);
-  mutex10_name.Append(kSetupMutex);
-  bool is_initialized = lock10->Initialize(mutex10_name);
-  if (!is_initialized) {
-    extra_code1_ = kVersion10;
-    return false;
-  }
-
-  // Omaha 1.1. ensures "there is only one instance of goopdate per machine or
-  // per user that is trying to install at a time." It allowed installs by
-  // different users to be concurrent by using the Global and Local namespaces.
-  // The machine name was only used when running as Local System, so machine
-  // instances need to look for both the user and machine lock to prevent
-  // conflicts with initial installs running as the user.
-  CString lock11_user_name(kOmaha11LocalPrefix);
-  lock11_user_name.Append(kSetupMutex);
-  is_initialized = lock11_user->Initialize(lock11_user_name);
-  if (!is_initialized) {
-    extra_code1_ = kVersion11;
-    return false;
-  }
-
-  if (is_machine_) {
-    CString lock11_machine_name(kOmaha11GlobalPrefix);
-    lock11_machine_name.Append(kSetupMutex);
-    extra_code1_ = kVersion11MachineLock;
-    return lock11_machine->Initialize(lock11_machine_name);
-  } else {
-    return true;
-  }
-}
-
-void Setup::PersistUpdateErrorInfo(bool is_machine,
-                                   HRESULT error,
-                                   int extra_code1,
-                                   const CString& version) {
-  const TCHAR* update_key_name =
-      ConfigManager::Instance()->registry_update(is_machine);
-  VERIFY1(SUCCEEDED(RegKey::SetValue(update_key_name,
-                                     kRegValueSelfUpdateErrorCode,
-                                     static_cast<DWORD>(error))));
-  VERIFY1(SUCCEEDED(RegKey::SetValue(update_key_name,
-                                     kRegValueSelfUpdateExtraCode1,
-                                     static_cast<DWORD>(extra_code1))));
-  VERIFY1(SUCCEEDED(RegKey::SetValue(update_key_name,
-                                     kRegValueSelfUpdateVersion,
-                                     version)));
-}
-
-// Returns false if the values cannot be deleted to avoid skewing the log data
-// with a single user pinging repeatedly with the same data.
-bool Setup::ReadAndClearUpdateErrorInfo(bool is_machine,
-                                        DWORD* error_code,
-                                        DWORD* extra_code1,
-                                        CString* version) {
-  ASSERT1(error_code);
-  ASSERT1(extra_code1);
-  ASSERT1(version);
-
-  const TCHAR* update_key_name =
-      ConfigManager::Instance()->registry_update(is_machine);
-  RegKey update_key;
-  HRESULT hr = update_key.Open(update_key_name);
-  if (FAILED(hr)) {
-    ASSERT1(false);
-    return false;
-  }
-
-  if (!update_key.HasValue(kRegValueSelfUpdateErrorCode)) {
-    ASSERT1(!update_key.HasValue(kRegValueSelfUpdateExtraCode1));
-    return false;
-  }
-
-  VERIFY1(SUCCEEDED(update_key.GetValue(kRegValueSelfUpdateErrorCode,
-                                        error_code)));
-  ASSERT1(FAILED(*error_code));
-
-  VERIFY1(SUCCEEDED(update_key.GetValue(kRegValueSelfUpdateExtraCode1,
-                                        extra_code1)));
-
-  VERIFY1(SUCCEEDED(update_key.GetValue(kRegValueSelfUpdateVersion, version)));
-
-  if (FAILED(update_key.DeleteValue(kRegValueSelfUpdateErrorCode)) ||
-      FAILED(update_key.DeleteValue(kRegValueSelfUpdateExtraCode1)) ||
-      FAILED(update_key.DeleteValue(kRegValueSelfUpdateVersion))) {
-    ASSERT1(false);
-    return false;
-  }
-
-  return true;
-}
-
-bool Setup::HasXmlParser() {
-  CComPtr<IXMLDOMDocument> my_xmldoc;
-  HRESULT hr = CoCreateSafeDOMDocument(&my_xmldoc);
-  const bool ret = SUCCEEDED(hr);
-  CORE_LOG(L3, (_T("[Setup::HasXmlParser returned %d][0x%08x]"), ret, hr));
-  return ret;
-}
-
 // Assumes that the Setup Lock is held.
 // This method is based on the assumption that if another install, which could
 // be modifying the number of clients, is in progress, that it either:
 //  (a) Has the Setup Lock, which is not possible because this process has it.
 //  (b) Has started an install worker.
+// TODO(omaha3): This is flawed: http://b/2764048.
 bool Setup::CanUninstallGoogleUpdate() const {
   CORE_LOG(L2, (_T("[Setup::CanUninstallGoogleUpdate]")));
   if (goopdate_utils::IsAppInstallWorkerRunning(is_machine_)) {
     CORE_LOG(L2, (_T("[Found install workers. Not uninstalling]")));
     return false;
   }
+  if (ShouldDelayUninstall()) {
+    // If the DelayUninstall flag is set, that implies that someone has
+    // installed us with the runtime=true flag, expecting that they can
+    // use our API later from another process.  If 24 hours have passed
+    // since that initial Omaha install, we clear the flag but still
+    // return false for this check.  (That way, if a machine has been
+    // suspended in mid-install, they still have a grace period until
+    // the next /ua to get something installed.)
+    CORE_LOG(L3, (_T("[DelayUninstall is set. Not uninstalling.]")));
+    if (ConfigManager::Instance()->Is24HoursSinceInstall(is_machine_)) {
+      CORE_LOG(L4, (_T("[24 hours elapsed; clearing DelayUninstall.]")));
+      SetDelayUninstall(false);
+    }
+    return false;
+  }
   size_t num_clients(0);
-  if (SUCCEEDED(goopdate_utils::GetNumClients(is_machine_, &num_clients)) &&
+  if (SUCCEEDED(app_registry_utils::GetNumClients(is_machine_, &num_clients)) &&
       num_clients >= 2) {
     CORE_LOG(L3, (_T("[Found products. Not uninstalling]")));
     return false;
   }
+
   return true;
 }
 
-bool Setup::IsInteractiveInstall() const {
-  return (MODE_INSTALL == mode_) && !args_->is_silent_set;
+bool Setup::ShouldDelayUninstall() const {
+  const TCHAR* key = ConfigManager::Instance()->registry_update(is_machine_);
+  if (!RegKey::HasValue(key, kRegValueDelayOmahaUninstall)) {
+    return false;
+  }
+  DWORD should_delay = 0;
+  if (FAILED(RegKey::GetValue(key,
+                              kRegValueDelayOmahaUninstall,
+                              &should_delay))) {
+    return false;
+  }
+  return should_delay != 0;
 }
 
-// The result of this method is only valid after the worker has been launched.
-// The /install instance should wait for worker process (/ig or /handoff) if:
-//  * Running silently: The exit code is only indication of install result.
-//  * Offline install: The main reason we exit early normally is to release the
-//    Setup Locks and allow downloads for multiple app installs to occur
-//    simultaneously. Since offline installers do not download, this is less of
-//    a concern. Also, Pack launches the offline installer interactively if the
-//    user chooses to retry after a failure. See http://b/1543716.
-// Long term, we will refactor the locking and always wait for the worker.
-bool Setup::ShouldWaitForWorkerProcess() const {
-  return !IsInteractiveInstall() || launched_offline_worker_;
+HRESULT Setup::SetDelayUninstall(bool should_delay) const {
+  const TCHAR* key = ConfigManager::Instance()->registry_update(is_machine_);
+  if (should_delay) {
+    return RegKey::SetValue(key, kRegValueDelayOmahaUninstall, 1UL);
+  } else {
+    return RegKey::DeleteValue(key, kRegValueDelayOmahaUninstall);
+  }
 }
 
-HRESULT Setup::SetOemInstallState() {
-  ASSERT1(MODE_INSTALL == mode_);
-  ASSERT1(args_->is_oem_set);
+HRESULT Setup::SendUninstallPing() {
+  CORE_LOG(L3, (_T("[SendUninstallPing]")));
 
-  if (!is_machine_ ||
-      IsElevationRequired() ||
-      !ConfigManager::Instance()->IsWindowsInstalling()) {
-    return GOOPDATE_E_OEM_NOT_MACHINE_AND_PRIVILEGED_AND_AUDIT_MODE;
+  const bool is_eula_accepted =
+      app_registry_utils::IsAppEulaAccepted(is_machine_,
+                                            kGoogleUpdateAppId,
+                                            false);
+
+  if (!is_eula_accepted) {
+    CORE_LOG(LE, (_T("[SendUninstallPing - eula not accepted]")));
+    return E_FAIL;
   }
 
-  const DWORD now = Time64ToInt32(GetCurrent100NSTime());
-  OPT_LOG(L1, (_T("[Beginning OEM install][%u]"), now));
-  HRESULT hr = RegKey::SetValue(
-      ConfigManager::Instance()->machine_registry_update(),
-      kRegValueOemInstallTimeSec,
-      now);
-  if (FAILED(hr)) {
-    return hr;
-  }
-  ASSERT1(ConfigManager::Instance()->IsOemInstalling(is_machine_));
+  PingEventPtr uninstall_ping_event(
+      new PingEvent(PingEvent::EVENT_UNINSTALL,
+                    PingEvent::EVENT_RESULT_SUCCESS,
+                    0,
+                    0));
 
-  return S_OK;
-}
+  // Generate a session ID for uninstall pings.  NOTE: The assumption here is
+  // that if /uninstall was launched by /ua, we have no updates to check for,
+  // so we assume that /ua won't use its own session ID.  If /ua does any
+  // network activity on a no-clients case, we will have to start passing the
+  // session ID from /ua to /uninstall in the future.
+  CString session_id;
+  VERIFY1(SUCCEEDED(GetGuid(&session_id)));
 
-HRESULT Setup::SetEulaAccepted(bool is_machine) {
-  SETUP_LOG(L4, (_T("[SetEulaAccepted][%d]"), is_machine));
-  const TCHAR* update_key_name =
-      ConfigManager::Instance()->registry_update(is_machine);
-  return RegKey::HasKey(update_key_name) ?
-      RegKey::DeleteValue(update_key_name, kRegValueOmahaEulaAccepted) :
-      S_OK;
-}
+  // Send uninstall ping for uninstalled apps.
+  HRESULT hr = S_OK;
+  std::vector<CString> uninstalled_apps;
+  if (SUCCEEDED(app_registry_utils::GetUninstalledApps(is_machine_,
+                                                       &uninstalled_apps))) {
+    Ping apps_uninstall_ping(is_machine_, session_id, kInstallSource_Uninstall);
+    apps_uninstall_ping.LoadAppDataFromRegistry(uninstalled_apps);
+    apps_uninstall_ping.BuildAppsPing(uninstall_ping_event);
 
-// Does not write the registry if Google Update is already installed as
-// determined by the presence of 2 or more registered apps. In those cases, we
-// assume the existing EULA state is correct and do not want to disable updates
-// for an existing installation.
-// Assumes it is called with appropriate synchronization protection such that it
-// can reliably check the number of registered clients.
-HRESULT Setup::SetEulaNotAccepted(bool is_machine) {
-  SETUP_LOG(L4, (_T("[SetEulaNotAccepted][%d]"), is_machine));
-
-  size_t num_clients(0);
-  if (SUCCEEDED(goopdate_utils::GetNumClients(is_machine, &num_clients)) &&
-      num_clients >= 2) {
-    SETUP_LOG(L4, (_T(" [Apps registered. Not setting eulaaccepted=0.]")));
-    return S_OK;
+    hr = apps_uninstall_ping.Send(false);
+    if (FAILED(hr)) {
+      CORE_LOG(LE, (_T("[SendUninstallPing: failed to send app uninstall ping]")
+                    _T("[0x%08x]"), hr));
+    }
   }
 
-  const ConfigManager* cm = ConfigManager::Instance();
-  return RegKey::SetValue(cm->registry_update(is_machine),
-                          kRegValueOmahaEulaAccepted,
-                          static_cast<DWORD>(0));
+  // Send uninstall ping for Omaha.
+  const CString current_omaha_version(GetVersionString());
+  const CString next_omaha_version;     // Empty, in the uninstall case.
+  Ping omaha_uninstall_ping(is_machine_, session_id, kInstallSource_Uninstall);
+  omaha_uninstall_ping.LoadOmahaDataFromRegistry();
+  omaha_uninstall_ping.BuildOmahaPing(current_omaha_version,
+                                      next_omaha_version,
+                                      uninstall_ping_event);
+  hr = omaha_uninstall_ping.Send(false);
+  if (SUCCEEDED(hr)) {
+    // Clears the registry after ping is sent successfully.
+    std::vector<CString> uninstalled_apps;
+    app_registry_utils::GetUninstalledApps(is_machine_, &uninstalled_apps);
+    app_registry_utils::RemoveClientStateForApps(is_machine_, uninstalled_apps);
+  } else {
+    CORE_LOG(LE, (_T("[SendUninstallPing: failed to send Omaha uninstall ping]")
+                  _T("[0x%08x]"), hr));
+  }
+
+  return hr;
 }
 
+
 }  // namespace omaha
diff --git a/setup/setup.h b/setup/setup.h
index ba66b77..f61a9d6 100644
--- a/setup/setup.h
+++ b/setup/setup.h
@@ -13,13 +13,11 @@
 // limitations under the License.
 // ========================================================================
 //
-// There are two phases of setup:
+// There are two phases of setup. Both are done in an instance running from a
+// temp location.
 //  1) Copy the Google Update files to the install location.
-//   * Executed by InstallGoogleUpdateAndApp().
-//   * This is done in an instance running from a temp location.
 //  2) Do everything else to install Google Update.
 //   * Executed by SetupGoogleUpdate().
-//   * This is done in an instance running from the installed location.
 //
 //  Uninstall() undoes both phases of setup.
 //  All methods assume the instance is running with the correct permissions.
@@ -32,7 +30,7 @@
 #include <vector>
 #include "base/basictypes.h"
 #include "base/scoped_ptr.h"
-#include "omaha/common/scoped_any.h"
+#include "omaha/base/scoped_any.h"
 
 namespace omaha {
 
@@ -40,89 +38,47 @@
 class HighresTimer;
 struct NamedObjectAttributes;
 
-struct CommandLineArgs;
 class SetupFiles;
 
 class Setup {
  public:
-  Setup(bool is_machine, const CommandLineArgs* args);
+  explicit Setup(bool is_machine);
   ~Setup();
 
-  // Installs Google Update, if necessary, and launches a worker to install app.
-  // Handles elevation.
-  HRESULT Install(const CString& cmd_line);
+  // Installs Omaha if necessary.
+  HRESULT Install(bool set_keepalive);
 
-  // Installs Google Update silently without installing an app.
-  HRESULT InstallSelfSilently();
+  // Acquires the Setup Lock and uninstalls all Omaha versions if Omaha can be
+  // uninstalled.
+  HRESULT Uninstall(bool send_uninstall_ping);
 
-  // Updates Google Update.
-  HRESULT UpdateSelfSilently();
-
-  // Repairs Google Update for Code Red recovery.
-  HRESULT RepairSilently();
-
-  // Completes installation from the installed location.
-  HRESULT SetupGoogleUpdate();
-
-  // Obtains the Setup Lock and uninstalls all Google Update versions if
-  // Google Update can be uninstalled.
-  HRESULT Uninstall();
-
-  // Verifies that Google Update is either properly installed or uninstalled
-  // completely. Returns whether Google Update is installed.
+  // Verifies that Omaha is either properly installed or uninstalled completely.
   // TODO(omaha): Consider making this and did_uninstall_ non-static after
   // refactoring Setup phases. May require a Setup member in Goopdate.
   static void CheckInstallStateConsistency(bool is_machine);
 
-  // Writes error info for silent updates to the registry so the installed Omaha
-  // can send an update failed ping.
-  static void PersistUpdateErrorInfo(bool is_machine,
-                                     HRESULT error,
-                                     int extra_code1,
-                                     const CString& version);
-
-  // Reads the error info for silent updates from the registry if present and
-  // deletes it. Returns true if the data is valid.
-  static bool ReadAndClearUpdateErrorInfo(bool is_machine,
-                                          DWORD* error_code,
-                                          DWORD* extra_code1,
-                                          CString* version);
-
-  // Marks Google Update EULA as accepted by deleting the registry value.
-  // Does not touch apps' EULA state.
-  static HRESULT SetEulaAccepted(bool is_machine);
-
   int extra_code1() const { return extra_code1_; }
 
- private:
-  // Defines the mode of the instance.
-  enum Mode {
-    MODE_UNKNOWN,
-    MODE_INSTALL,
-    MODE_SELF_INSTALL,
-    MODE_SELF_UPDATE,
-    MODE_REPAIR,
-    MODE_PHASE2,
-    MODE_UNINSTALL,
-  };
+  void set_is_self_update(bool is_self_update) {
+    is_self_update_ = is_self_update;
+  }
 
+ private:
   typedef std::vector<uint32> Pids;
 
-  // Does all non-elevation work for Install().
-  HRESULT DoInstall();
+  // Completes installation.
+  HRESULT SetupGoogleUpdate();
 
   // Handles Setup lock acquisition failures and returns the error to report.
   HRESULT HandleLockFailed(int lock_version);
 
   // Does the install work within all necessary locks, which have already been
-  // obtained.
-  // If handoff_process is not NULL on a successful return, the caller should
-  // wait for this process after releasing the locks.
-  HRESULT DoProtectedInstall(HANDLE* handoff_process);
+  // acquired.
+  HRESULT DoProtectedInstall(bool set_keepalive);
 
   // Uninstalls all Google Update versions after checking if Google Update can
   // be uninstalled.
-  HRESULT DoProtectedUninstall();
+  HRESULT DoProtectedUninstall(bool send_uninstall_ping);
 
   // Returns whether Google Update should be installed.
   bool ShouldInstall(SetupFiles* setup_files);
@@ -130,61 +86,47 @@
   // Returns whether the same version of Google Update should be over-installed.
   bool ShouldOverinstallSameVersion(SetupFiles* setup_files);
 
-  // Increments the usage stat if a core process is not running and the existing
-  // version is >= 1.2.0.0.
-  void UpdateCoreNotRunningMetric(const CString& existing_version);
-
   HRESULT DoProtectedGoogleUpdateInstall(SetupFiles* setup_files);
 
   // Rolls back the changes made during DoProtectedGoogleUpdateInstall().
   // Call when that method fails.
   void RollBack(SetupFiles* setup_files);
 
-  // Installs the Google Update files.
-  HRESULT InstallPhase1();
-
   // Tells other instances to stop.
   HRESULT StopGoogleUpdate();
 
   // Tells other instances to stop then waits for them to exit.
   HRESULT StopGoogleUpdateAndWait();
 
-  // Sets the non-legacy shutdown event to signal other instances for this user
-  // or machine to exit.
+  // Sets the shutdown event to signal other instances for this user or machine
+  // to exit.
   HRESULT SignalShutdownEvent();
 
-  // Signals the legacy quiet mode events to cause all legacy Google Update
-  // processes for this user or machine to exit.
-  HRESULT SignalLegacyShutdownEvents();
-
-  // Creates and sets the specified legacy event.
-  HRESULT CreateLegacyEvent(const CString& event_name,
-                            HANDLE* event_handle) const;
-
   // Releases all the shutdown events.
   void ReleaseShutdownEvents();
 
-  // Sets the setup complete event, signalling the other setup instance to
-  // release the Setup Lock.
-  void SetSetupCompleteEvent() const;
-
   // Waits for other instances of GoogleUpdate.exe to exit.
   HRESULT WaitForOtherInstancesToExit(const Pids& pids);
 
   // Gets the list of all the GoogleUpdate.exe processes to wait for.
   HRESULT GetPidsToWaitFor(Pids* pids) const;
 
-  // Gets the list of GoogleUpdate processes to wait for based on processes'
-  // command line.
+  // Gets a list of GoogleUpdate.exe processes for user or machine that are
+  // running from the respective official directory, except "/install" or
+  // "/registerproduct" instances.
+  // In the machine case we search in all the accounts since the workers can be
+  // running in any admin account and the machine update worker runs as SYSTEM.
+  // In the user case, we only search the user's account.
+  // In both cases, the command line location is used to determine the
+  // machine/user cases.
   HRESULT GetPidsToWaitForUsingCommandLine(Pids* pids) const;
 
   // Returns whether elevation is required to perform this install.
   bool IsElevationRequired() const;
 
-  // Starts Omaha elevated if possible and waits for it to exit.
-  // The same arguments are passed to the elevated instance.
-  HRESULT ElevateAndWait(const CString& cmd_line);
-
+// TODO(omaha3): Support offline builds. Prefer to detect and maybe copy outside
+// Setup.
+#if 0
   // Given a guid, finds and copies the offline manifest and binaries from the
   // current module directory to the offline_dir passed in. offline_dir is
   // typically the Google\Update\Offline\ directory. The offline manifest is
@@ -197,10 +139,7 @@
   // For all the applications that have been requested, copy the offline
   // binaries. Calls CopyOfflineFilesForGuid() for each app_guid.
   bool CopyOfflineFiles(const CString& offline_dir);
-
-  // Launches a worker process from the installed location to run setup phase 2,
-  // if specified, and install the app.
-  HRESULT LaunchInstalledWorker(bool do_setup_phase_2, HANDLE* process);
+#endif
 
   // Starts the core.
   HRESULT StartMachineCoreProcess() const;
@@ -215,22 +154,9 @@
   // Verifies that the appropriate core is running.
   bool IsCoreProcessRunning() const;
 
-  // Starts the installed long-lived process.
+  // Starts the long-lived Core process.
   HRESULT StartCore() const;
 
-  // Waits for the process or an event.
-  // If the process exits before the event is signaled, the exit code is
-  // returned in exit_code.
-  // The event should not be signaled until at least the point where the process
-  // is handling and reporting its own errors.
-  HRESULT WaitForProcessExitOrEvent(HANDLE process,
-                                    HANDLE event,
-                                    uint32* exit_code) const;
-
-  // Waits for the UI in the handoff worker to be initialized such that it can
-  // handle its own errors.
-  HRESULT WaitForHandoffWorker(HANDLE process) const;
-
   // Returns the SID to use for process searches, mutexes, etc. during this
   // installation.
   HRESULT GetAppropriateSid(CString* sid) const;
@@ -238,41 +164,23 @@
   // Initializes the Setup Lock with correct name and security attributes.
   static bool InitSetupLock(bool is_machine, GLock* setup_lock);
 
-  // Initializes legacy Setup Locks with correct name and security attributes.
-  bool InitLegacySetupLocks(GLock* lock10,
-                            GLock* lock11_user,
-                            GLock* lock11_machine);
-
-  // Returns true if it can instantiate MSXML parser.
-  static bool HasXmlParser();
-
   // Returns true if GoogleUpdate can be uninstalled now.
   bool CanUninstallGoogleUpdate() const;
 
-  bool IsInteractiveInstall() const;
+  // Control the state of the DelayUninstall flag.  If set, uninstall will
+  // be delayed for at least 24 hours after initial install.
+  bool ShouldDelayUninstall() const;
+  HRESULT SetDelayUninstall(bool should_delay) const;
 
-  bool ShouldWaitForWorkerProcess() const;
-
-  // Sets values for OEM installs in the registry.
-  HRESULT SetOemInstallState();
-
-  // Marks Google Update EULA as not accepted if it is not already installed.
-  // Does not touch apps' EULA state.
-  static HRESULT SetEulaNotAccepted(bool is_machine);
+  // Sends the uninstall ping and waits for the ping to be sent.
+  HRESULT SendUninstallPing();
 
   const bool is_machine_;
-  Mode mode_;
-  const CommandLineArgs* const args_;
+  bool is_self_update_;
   CString saved_version_;  // Previous version saved for roll back.
-  scoped_event legacy_1_0_shutdown_event_;
-  scoped_event legacy_1_1_shutdown_event_;
   scoped_event shutdown_event_;
   int extra_code1_;
 
-  // Whether an offline worker has been launched. Not valid until worker has
-  // been launched. If true, this Setup instance is for offline metainstaller.
-  bool launched_offline_worker_;
-
   scoped_ptr<HighresTimer> metrics_timer_;
 
   // Whether this process uninstalled Google Update for any reason.
diff --git a/setup/setup_files.cc b/setup/setup_files.cc
index e572be8..a386fc9 100644
--- a/setup/setup_files.cc
+++ b/setup/setup_files.cc
@@ -18,22 +18,24 @@
 #include <atlpath.h>
 #include <vector>
 #include "base/basictypes.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/path.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/scoped_current_directory.h"
-#include "omaha/common/signaturevalidator.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/highres_timer-win32.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/scoped_current_directory.h"
+#include "omaha/base/signatures.h"
+#include "omaha/base/signaturevalidator.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
 #include "omaha/goopdate/resource_manager.h"
 #include "omaha/setup/setup_metrics.h"
 
@@ -127,14 +129,6 @@
   ++metric_setup_files_total;
   HighresTimer metrics_timer;
 
-
-  SETUP_LOG(L3,
-      (_T("[IsAdmin=%d, IsNonElevatedAdmin=%d, SvcInstalled=%d, MachineApp=%d"),
-       vista_util::IsUserAdmin(),
-       vista_util::IsUserNonElevatedAdmin(),
-       goopdate_utils::IsServiceInstalled(),
-       is_machine_));
-
   const bool should_over_install = ConfigManager::Instance()->CanOverInstall();
 
   // Copy the core program files.
@@ -212,18 +206,6 @@
     SETUP_LOG(LE, (_T("[DeleteDirectory failed][%s][0x%08x]"),
                    install_dir, hr));
   }
-
-  // TODO(omaha): Remove this and GetMachineDownloadStorageDir() when legacy
-  // support is removed.
-  // This directory may have been used by previous Omaha versions. Delete it in
-  // case this install was upgraded from one of those.
-  if (is_machine_) {
-    CString dir(ConfigManager::Instance()->GetMachineDownloadStorageDir());
-    hr = DeleteDirectory(dir);
-    if (FAILED(hr)) {
-      SETUP_LOG(LE, (_T("[DeleteDirectory failed][%s][0x%08x]"), dir, hr));
-    }
-  }
 }
 
 HRESULT SetupFiles::CopyShell() {
@@ -246,7 +228,7 @@
     }
 
     std::vector<CString> shell_files;
-    shell_files.push_back(kGoopdateFileName);
+    shell_files.push_back(kOmahaShellFileName);
     CPath shell_dir(shell_path);
     VERIFY1(shell_dir.RemoveFileSpec());
     hr = CopyInstallFiles(shell_files, shell_dir, already_exists);
@@ -270,7 +252,7 @@
   *already_exists = false;
 
   CPath source_shell_path(app_util::GetCurrentModuleDirectory());
-  if (!source_shell_path.Append(kGoopdateFileName)) {
+  if (!source_shell_path.Append(kOmahaShellFileName)) {
     return GOOPDATE_E_PATH_APPEND_FAILED;
   }
 
@@ -323,7 +305,7 @@
                          _T("gsh"),
                          0,
                          CStrBuf(temp_file, MAX_PATH))) {
-    DWORD error = ::GetLastError();
+    const DWORD error = ::GetLastError();
     SETUP_LOG(LEVEL_WARNING, (_T("[::GetTempFileName failed][%d]"), error));
     return HRESULT_FROM_WIN32(error);
   }
@@ -337,22 +319,38 @@
   return S_OK;
 }
 
+// The list of files below needs to be kept in sync with payload_files in
+// omaha_version_utils.py.
 HRESULT SetupFiles::BuildFileLists() {
   ASSERT1(core_program_files_.empty());
   ASSERT1(optional_files_.empty());
 
   core_program_files_.clear();
-  core_program_files_.push_back(kGoopdateFileName);
-  core_program_files_.push_back(kGoopdateDllName);
-  core_program_files_.push_back(kGoopdateCrashHandlerFileName);
+  core_program_files_.push_back(kOmahaShellFileName);
+  core_program_files_.push_back(kOmahaDllName);
+  core_program_files_.push_back(kCrashHandlerFileName);
 
+  // TODO(omaha3): Try to not depend on ResourceManager. Maybe just find the
+  // files using wildcards.
   ResourceManager::GetSupportedLanguageDllNames(&core_program_files_);
 
   core_program_files_.push_back(kHelperInstallerName);
 
+  core_program_files_.push_back(kPSFileNameUser);
+  core_program_files_.push_back(kPSFileNameMachine);
+
+  // If files are removed from this list, unit tests such as
+  // ShouldInstall_SameVersionOptionalFileMissing may need to be updated.
   optional_files_.clear();
-  optional_files_.push_back(ACTIVEX_FILENAME);
+  optional_files_.push_back(UPDATE_PLUGIN_FILENAME);
+  optional_files_.push_back(kOmahaBrokerFileName);
+  optional_files_.push_back(kOmahaOnDemandFileName);
+  // Machine-specific files are always installed, to support cross installs from
+  // user to machine and machine to user.
+  // TODO(omaha3): Enable once it is being built.
+#if 0
   optional_files_.push_back(BHO_FILENAME);
+#endif
 
   return S_OK;
 }
@@ -362,7 +360,7 @@
                                      const CString& destination_dir,
                                      bool overwrite) {
   SETUP_LOG(L1, (_T("[SetupFiles::CopyInstallFiles]")
-                _T("[destination dir=%s][overwrite=%d]"),
+                 _T("[destination dir=%s][overwrite=%d]"),
                 destination_dir, overwrite));
   ASSERT1(!file_names.empty());
 
@@ -408,17 +406,13 @@
       return GOOPDATE_E_PATH_APPEND_FAILED;
     }
     destination_file_paths.push_back(file);
-
-    SETUP_LOG(L2, (_T("[from=%s][to=%s]"),
-                  source_file_paths[i],
-                  destination_file_paths[i]));
   }
 
   hr = CopyAndValidateFiles(source_file_paths,
                             destination_file_paths,
                             overwrite);
 
-  SETUP_LOG(L2, (_T("[SetupFiles::CopyInstallFiles - done")));
+  SETUP_LOG(L2, (_T("[SetupFiles::CopyInstallFiles][Done]")));
   return hr;
 }
 
@@ -447,7 +441,7 @@
         hr = File::DeleteAfterReboot(dot_old);
         if (FAILED(hr)) {
           SETUP_LOG(LW, (_T("DeleteAfterReboot of %s failed with 0x%08x."),
-                        dot_old, hr));
+                         dot_old, hr));
         }
       } else {
         SETUP_LOG(L2, (_T("[failed to move][%s][0x%08x]"), cur_file, hr));
@@ -457,29 +451,40 @@
   }
 
   for (size_t i = 0; i != source_file_paths.size(); ++i) {
+    const CString& source_file = source_file_paths[i];
+    const CString& destination_file = destination_file_paths[i];
+    SETUP_LOG(L2, (_T("[CopyAndValidateFiles][from=%s][to=%s][overwrite=%d]")
+        _T("[destination file exists=%d]"), source_file, destination_file,
+        overwrite, File::Exists(destination_file)));
+
     extra_code1_ = i + 1;  // 1-based; reserves 0 for success or not set.
 
-    HRESULT hr = VerifyFileSignature(source_file_paths[i]);
-    if (FAILED(hr)) {
-      OPT_LOG(LE, (_T("[pre-copy signature validation failed][from=%s][0x%x]"),
-                   source_file_paths[i], hr));
-      ++metric_setup_files_verification_failed_pre;
-      return hr;
+    if (overwrite || !File::Exists(destination_file)) {
+      HRESULT hr = VerifyFileSignature(source_file);
+      if (FAILED(hr)) {
+        OPT_LOG(LE, (_T("[precopy signature validation failed][from=%s][0x%x]"),
+                     source_file, hr));
+        ++metric_setup_files_verification_failed_pre;
+        return hr;
+      }
+
+      hr = File::Copy(source_file, destination_file, true);
+      if (FAILED(hr)) {
+        OPT_LOG(LE, (_T("[copy failed][from=%s][to=%s][0x%08x]"),
+                     source_file, destination_file, hr));
+        return hr;
+      }
     }
 
-    hr = File::Copy(source_file_paths[i], destination_file_paths[i], true);
-    if (FAILED(hr)) {
-      OPT_LOG(LE, (_T("[copy failed][from=%s][to=%s][0x%08x]"),
-                   source_file_paths[i], destination_file_paths[i], hr));
-      return hr;
-    }
+    HRESULT hr = File::AreFilesIdentical(source_file, destination_file) ?
+                    VerifyFileSignature(destination_file) :
+                    GOOPDATE_E_POST_COPY_VERIFICATION_FAILED;
 
-    hr = VerifyFileSignature(destination_file_paths[i]);
     if (FAILED(hr)) {
-      OPT_LOG(LE, (_T("[postcopy signature failed][from=%s][to=%s][0x%x]"),
-                   source_file_paths[i], destination_file_paths[i], hr));
+      OPT_LOG(LE, (_T("[postcopy verification failed][from=%s][to=%s][0x%x]"),
+                   source_file, destination_file, hr));
       ++metric_setup_files_verification_failed_post;
-      VERIFY1(SUCCEEDED(File::Remove(destination_file_paths[i])));
+      VERIFY1(SUCCEEDED(File::Remove(destination_file)));
       return hr;
     }
   }
@@ -495,6 +500,8 @@
     return S_OK;
   }
 
+  HighresTimer verification_timer;
+
   // Verify the Authenticode signature but use use only the local cache for
   // revocation checks.
   HRESULT hr = VerifySignature(filepath, false);
@@ -514,6 +521,8 @@
     return GOOPDATE_E_VERIFY_SIGNEE_IS_GOOGLE_FAILED;
   }
 
+  CORE_LOG(L3, (_T("[SetupFiles::VerifyFileSignature succeeded][%d ms]"),
+                verification_timer.GetElapsedMs()));
   return S_OK;
 }
 
diff --git a/setup/setup_files.h b/setup/setup_files.h
index 38dab47..f0ae734 100644
--- a/setup/setup_files.h
+++ b/setup/setup_files.h
@@ -30,7 +30,7 @@
 class SetupFiles {
  public:
   explicit SetupFiles(bool is_machine);
-  virtual ~SetupFiles();
+  ~SetupFiles();
 
   HRESULT Init();
 
@@ -39,9 +39,7 @@
 
   // Installs Google Update files but does not register or install any other
   // applications. Returns whether Google Update was installed.
-  // TODO(omaha3): Remove the virtual methods from this class. They are only
-  // needed for unit tests and can be eliminated in the upcoming redesign.
-  virtual HRESULT Install();
+  HRESULT Install();
 
   // Rolls back the changes made during Install(). Call when Setup fails.
   // Returns S_OK if there is nothing to do.
diff --git a/setup/setup_files_unittest.cc b/setup/setup_files_unittest.cc
index f3fdcc1..607aafe 100644
--- a/setup/setup_files_unittest.cc
+++ b/setup/setup_files_unittest.cc
@@ -15,15 +15,15 @@
 
 #include <vector>
 #include "base/scoped_ptr.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/path.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/path.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
 #include "omaha/setup/setup_files.h"
 #include "omaha/testing/unit_test.h"
 
@@ -31,9 +31,15 @@
 
 namespace {
 
-const int kNumberOfLanguageDlls = 54;
-const int kNumberOfRequiredFiles = 4;
-const int kNumberOfOptionalFiles = 2;
+// TODO(omaha3): Update the numbers in the else block as we build more files.
+// Eventually use the original values in the if block.
+const int kNumberOfLanguageDlls = 55;
+const int kNumberOfRequiredFiles = 6;
+#if 0
+const int kNumberOfOptionalFiles = 4;
+#else
+const int kNumberOfOptionalFiles = 3;
+#endif
 const int kNumberOfInstalledRequiredFiles =
     kNumberOfLanguageDlls + kNumberOfRequiredFiles;
 // FindFiles returns "." and ".." in addition to the actual files.
@@ -48,43 +54,52 @@
 }  // namespace
 
 void CopyGoopdateFiles(const CString& omaha_path, const CString& version) {
+  EXPECT_SUCCEEDED(CreateDir(omaha_path, NULL));
   const CString version_path = ConcatenatePath(omaha_path, version);
 
-  ASSERT_SUCCEEDED(File::Copy(
+  EXPECT_SUCCEEDED(File::Copy(
       ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                      _T("GoogleUpdate.exe")),
-      ConcatenatePath(omaha_path, _T("GoogleUpdate.exe")),
+                      kOmahaShellFileName),
+      ConcatenatePath(omaha_path, kOmahaShellFileName),
       false));
 
-  ASSERT_SUCCEEDED(CreateDir(version_path, NULL));
+  EXPECT_SUCCEEDED(CreateDir(version_path, NULL));
 
-  const TCHAR* files[] = {_T("GoogleUpdate.exe"),
-                          _T("GoogleUpdateHelper.msi"),
-                          _T("GoogleCrashHandler.exe"),
-                          _T("goopdate.dll"),
+  const TCHAR* files[] = {kCrashHandlerFileName,
+                          kOmahaShellFileName,
+                          kHelperInstallerName,
+                          kOmahaDllName,
+                          kOmahaBrokerFileName,
+                          kOmahaOnDemandFileName,
+// TODO(omaha3): Enable once this is being built.
+#if 0
                           _T("GoopdateBho.dll"),
-                          ACTIVEX_FILENAME};
+#endif
+                          UPDATE_PLUGIN_FILENAME,
+                          kPSFileNameMachine,
+                          kPSFileNameUser,
+                          };
   for (size_t i = 0; i < arraysize(files); ++i) {
-    ASSERT_SUCCEEDED(File::Copy(
+    EXPECT_SUCCEEDED(File::Copy(
         ConcatenatePath(app_util::GetCurrentModuleDirectory(),
                         files[i]),
         ConcatenatePath(version_path, files[i]),
-        false));
+        false)) << _T("Failed copying ") << files[i];
   }
 
-  ASSERT_SUCCEEDED(File::CopyWildcards(app_util::GetCurrentModuleDirectory(),
+  EXPECT_SUCCEEDED(File::CopyWildcards(app_util::GetCurrentModuleDirectory(),
                                        version_path,
                                        _T("goopdateres_\?\?.dll"),
                                        false));
-  ASSERT_SUCCEEDED(File::CopyWildcards(app_util::GetCurrentModuleDirectory(),
+  EXPECT_SUCCEEDED(File::CopyWildcards(app_util::GetCurrentModuleDirectory(),
                                        version_path,
                                        _T("goopdateres_\?\?\?.dll"),
                                        false));
-  ASSERT_SUCCEEDED(File::CopyWildcards(app_util::GetCurrentModuleDirectory(),
+  EXPECT_SUCCEEDED(File::CopyWildcards(app_util::GetCurrentModuleDirectory(),
                                        version_path,
                                        _T("goopdateres_\?\?-\?\?.dll"),
                                        false));
-  ASSERT_SUCCEEDED(File::CopyWildcards(app_util::GetCurrentModuleDirectory(),
+  EXPECT_SUCCEEDED(File::CopyWildcards(app_util::GetCurrentModuleDirectory(),
                                        version_path,
                                        _T("goopdateres_\?\?-\?\?\?.dll"),
                                        false));
@@ -117,7 +132,7 @@
 #endif
   }
 
-  void SetUp() {
+  virtual void SetUp() {
     RegKey::DeleteKey(hive_override_key_name_, true);
     // Do not override HKLM because it contains the CSIDL_* definitions.
     OverrideSpecifiedRegistryHives(hive_override_key_name_, false, true);
@@ -151,7 +166,7 @@
     EXPECT_SUCCEEDED(setup_files_->Install());
 
     EXPECT_TRUE(File::Exists(ConcatenatePath(omaha_path,
-                             _T("GoogleUpdate.exe"))));
+                             kOmahaShellFileName)));
 
     EXPECT_TRUE(File::IsDirectory(version_path));
 
@@ -159,11 +174,17 @@
     EXPECT_SUCCEEDED(FindFiles(version_path, _T("*.*"), &files));
     ASSERT_EQ(kExpectedFilesReturnedByFindFiles, files.size());
     int file_index = kExtraFilesReturnedByFindFiles;
-    EXPECT_STREQ(_T("GoogleCrashHandler.exe"), files[file_index++]);
-    EXPECT_STREQ(_T("GoogleUpdate.exe"), files[file_index++]);
-    EXPECT_STREQ(_T("GoogleUpdateHelper.msi"), files[file_index++]);
-    EXPECT_STREQ(_T("goopdate.dll"), files[file_index++]);
+    EXPECT_STREQ(kCrashHandlerFileName, files[file_index++]);
+    EXPECT_STREQ(kOmahaShellFileName, files[file_index++]);
+    EXPECT_STREQ(kOmahaBrokerFileName, files[file_index++]);
+    EXPECT_STREQ(kHelperInstallerName, files[file_index++]);
+    EXPECT_STREQ(kOmahaOnDemandFileName, files[file_index++]);
+    EXPECT_STREQ(kOmahaDllName, files[file_index++]);
+// TODO(omaha3): Enable as this is built.
+#if 0
     EXPECT_STREQ(_T("GoopdateBho.dll"), files[file_index++]);
+#endif
+    EXPECT_STREQ(_T("goopdateres_am.dll"), files[file_index++]);
     EXPECT_STREQ(_T("goopdateres_ar.dll"), files[file_index++]);
     EXPECT_STREQ(_T("goopdateres_bg.dll"), files[file_index++]);
     EXPECT_STREQ(_T("goopdateres_bn.dll"), files[file_index++]);
@@ -199,7 +220,6 @@
     EXPECT_STREQ(_T("goopdateres_ms.dll"), files[file_index++]);
     EXPECT_STREQ(_T("goopdateres_nl.dll"), files[file_index++]);
     EXPECT_STREQ(_T("goopdateres_no.dll"), files[file_index++]);
-    EXPECT_STREQ(_T("goopdateres_or.dll"), files[file_index++]);
     EXPECT_STREQ(_T("goopdateres_pl.dll"), files[file_index++]);
     EXPECT_STREQ(_T("goopdateres_pt-BR.dll"), files[file_index++]);
     EXPECT_STREQ(_T("goopdateres_pt-PT.dll"), files[file_index++]);
@@ -209,6 +229,7 @@
     EXPECT_STREQ(_T("goopdateres_sl.dll"), files[file_index++]);
     EXPECT_STREQ(_T("goopdateres_sr.dll"), files[file_index++]);
     EXPECT_STREQ(_T("goopdateres_sv.dll"), files[file_index++]);
+    EXPECT_STREQ(_T("goopdateres_sw.dll"), files[file_index++]);
     EXPECT_STREQ(_T("goopdateres_ta.dll"), files[file_index++]);
     EXPECT_STREQ(_T("goopdateres_te.dll"), files[file_index++]);
     EXPECT_STREQ(_T("goopdateres_th.dll"), files[file_index++]);
@@ -218,7 +239,9 @@
     EXPECT_STREQ(_T("goopdateres_vi.dll"), files[file_index++]);
     EXPECT_STREQ(_T("goopdateres_zh-CN.dll"), files[file_index++]);
     EXPECT_STREQ(_T("goopdateres_zh-TW.dll"), files[file_index++]);
-    EXPECT_STREQ(ACTIVEX_FILENAME, files[file_index++]);
+    EXPECT_STREQ(UPDATE_PLUGIN_FILENAME, files[file_index++]);
+    EXPECT_STREQ(kPSFileNameMachine, files[file_index++]);
+    EXPECT_STREQ(kPSFileNameUser, files[file_index++]);
 
     EXPECT_SUCCEEDED(DeleteDirectory(version_path));
   }
@@ -268,7 +291,7 @@
       DeleteDirectory(ConcatenatePath(omaha_path_, this_version_)));
   CString file_path = ConcatenatePath(
                           ConcatenatePath(omaha_path_, this_version_),
-                          _T("goopdate.dll"));
+                          kOmahaDllName);
   ASSERT_FALSE(File::Exists(file_path));
 
   EXPECT_TRUE(setup_files_->ShouldOverinstallSameVersion());
@@ -293,7 +316,7 @@
 
   CopyGoopdateFiles(omaha_path_, this_version_);
   CString path = ConcatenatePath(ConcatenatePath(omaha_path_, this_version_),
-                                 _T("goopdate.dll"));
+                                 kOmahaDllName);
   ASSERT_SUCCEEDED(File::Remove(path));
   ASSERT_FALSE(File::Exists(path));
 
@@ -308,7 +331,7 @@
 
   CopyGoopdateFiles(omaha_path_, this_version_);
   CString path = ConcatenatePath(ConcatenatePath(omaha_path_, this_version_),
-                                 _T("GoopdateBho.dll"));
+                                 UPDATE_PLUGIN_FILENAME);
   ASSERT_SUCCEEDED(File::Remove(path));
   ASSERT_FALSE(File::Exists(path));
 
@@ -322,7 +345,7 @@
                                     this_version_));
 
   CopyGoopdateFiles(omaha_path_, this_version_);
-  CString shell_path = ConcatenatePath(omaha_path_, _T("GoogleUpdate.exe"));
+  CString shell_path = ConcatenatePath(omaha_path_, kOmahaShellFileName);
   ASSERT_TRUE(SUCCEEDED(File::DeleteAfterReboot(shell_path)) ||
               !vista_util::IsUserAdmin());
   ASSERT_FALSE(File::Exists(shell_path));
@@ -337,7 +360,7 @@
                                     kFutureVersionString));
 
   CopyGoopdateFiles(omaha_path_, kFutureVersionString);
-  CString shell_path = ConcatenatePath(omaha_path_, _T("GoogleUpdate.exe"));
+  CString shell_path = ConcatenatePath(omaha_path_, kOmahaShellFileName);
   ASSERT_TRUE(SUCCEEDED(File::DeleteAfterReboot(shell_path)) ||
               !vista_util::IsUserAdmin());
   ASSERT_FALSE(File::Exists(shell_path));
@@ -349,6 +372,8 @@
       DeleteDirectory(ConcatenatePath(omaha_path_, kFutureVersionString)));
 }
 
+// "NotOverInstall" refers to there not being files in the directory.
+// should_over_install/overwrite will be true for unofficial builds.
 TEST_F(SetupFilesMachineTest, Install_NotOverInstall) {
   if (vista_util::IsUserAdmin()) {
     // Fake the version
@@ -376,10 +401,11 @@
   InitializeVersion(module_version);
 }
 
-TEST_F(SetupFilesUserTest, ShouldCopyShell_ExistingIsNewer) {
+// TODO(omaha3): Need a 1.3.x_newer directory.
+TEST_F(SetupFilesUserTest, DISABLED_ShouldCopyShell_ExistingIsNewer) {
   CString target_path = ConcatenatePath(
-      ConcatenatePath(exe_parent_dir_, _T("omaha_1.2.x_newer")),
-      _T("GoogleUpdate.exe"));
+      ConcatenatePath(exe_parent_dir_, _T("omaha_1.3.x_newer")),
+      kOmahaShellFileName);
   ASSERT_TRUE(File::Exists(target_path));
   bool should_copy = false;
   bool already_exists = false;
@@ -390,7 +416,7 @@
 
 TEST_F(SetupFilesUserTest, ShouldCopyShell_ExistingIsSame) {
   CString target_path = ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                        _T("GoogleUpdate.exe"));
+                                        kOmahaShellFileName);
   ASSERT_TRUE(File::Exists(target_path));
   bool should_copy = false;
   bool already_exists = false;
@@ -457,7 +483,7 @@
        ShouldCopyShell_ExistingIsOlderButCompatible_1_2_131_7) {
   CString target_path = ConcatenatePath(
       ConcatenatePath(exe_parent_dir_, _T("omaha_1.2.131.7_shell")),
-      kGoopdateFileName);
+      kOmahaShellFileName);
   ASSERT_TRUE(File::Exists(target_path));
   bool should_copy = false;
   bool already_exists = false;
@@ -470,7 +496,7 @@
        ShouldCopyShell_ExistingIsOlderButCompatible_1_2_183_9) {
   CString target_path = ConcatenatePath(
       ConcatenatePath(exe_parent_dir_, _T("omaha_1.2.183.9_shell")),
-      kGoopdateFileName);
+      kOmahaShellFileName);
   ASSERT_TRUE(File::Exists(target_path));
   bool should_copy = false;
   bool already_exists = false;
@@ -481,8 +507,8 @@
 
 TEST_F(SetupFilesUserTest, ShouldCopyShell_ExistingIsOlderMinor) {
   CString target_path = ConcatenatePath(
-      ConcatenatePath(exe_parent_dir_, _T("omaha_1.1.x")),
-      _T("GoogleUpdate.exe"));
+      ConcatenatePath(exe_parent_dir_, _T("omaha_1.2.x")),
+      kOmahaShellFileName);
   ASSERT_TRUE(File::Exists(target_path));
   bool should_copy = false;
   bool already_exists = false;
@@ -491,13 +517,13 @@
   EXPECT_TRUE(already_exists);
 }
 
-// The 1.2.x directory will not always have an older GoogleUpdate.exe than the
+// The 1.3.x directory will not always have an older GoogleUpdate.exe than the
 // saved version that we use for official builds.
 #if !OFFICIAL_BUILD
 TEST_F(SetupFilesUserTest, ShouldCopyShell_ExistingIsOlderSameMinor) {
   CString target_path = ConcatenatePath(
-      ConcatenatePath(exe_parent_dir_, _T("omaha_1.2.x")),
-      _T("GoogleUpdate.exe"));
+      ConcatenatePath(exe_parent_dir_, _T("omaha_1.3.x")),
+      kOmahaShellFileName);
   ASSERT_TRUE(File::Exists(target_path));
   bool should_copy = false;
   bool already_exists = false;
@@ -511,7 +537,7 @@
 TEST_F(SetupFilesUserTest, ShouldCopyShell_ExistingHasNoVersion) {
   CString target_path = ConcatenatePath(
       ConcatenatePath(exe_parent_dir_, _T("does_not_shutdown")),
-      _T("GoogleUpdate.exe"));
+      kOmahaShellFileName);
   ASSERT_TRUE(File::Exists(target_path));
   bool should_copy = false;
   bool already_exists = false;
@@ -524,7 +550,7 @@
 TEST_F(SetupFilesUserTest, ShouldCopyShell_NoExistingFile) {
   CString target_path = ConcatenatePath(
       ConcatenatePath(exe_parent_dir_, _T("no_such_dir")),
-      _T("GoogleUpdate.exe"));
+      kOmahaShellFileName);
   ASSERT_FALSE(File::Exists(target_path));
   bool should_copy = false;
   bool already_exists = false;
diff --git a/setup/setup_google_update.cc b/setup/setup_google_update.cc
index 6c1ad98..aaee71b 100644
--- a/setup/setup_google_update.cc
+++ b/setup/setup_google_update.cc
@@ -19,41 +19,41 @@
 #include <atlpath.h>
 #include <vector>
 #include "base/basictypes.h"
-#include "goopdate\google_update_idl.h"  // NOLINT
-#include "omaha/common/app_util.h"
-#include "omaha/common/atlregmapex.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/atlregmapex.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/const_utils.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/highres_timer-win32.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/path.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/service_utils.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/user_info.h"
+#include "omaha/common/command_line_builder.h"
+#include "omaha/common/config_manager.h"
 #include "omaha/common/const_cmd_line.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/path.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/service_utils.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/user_info.h"
-#include "omaha/goopdate/command_line_builder.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/resource.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/common/scheduled_task_utils.h"
 #include "omaha/setup/setup_metrics.h"
 #include "omaha/setup/setup_service.h"
-#include "omaha/worker/ping.h"
-#include "omaha/worker/ping_event.h"
-#include "omaha/worker/ping_utils.h"
 
 namespace omaha {
 
 namespace {
 
-const TCHAR* const kMsiSuppressAllRebootsCmdLine = _T("REBOOT=ReallySuppress");
-
 #ifdef _DEBUG
 HRESULT VerifyCOMLocalServerRegistration(bool is_machine) {
+// TODO(omaha3): Implement this for Omaha 3. Specifically, this code assumes
+// Setup is running from the installed location, which is no longer true.
+#if 0
   // Validate the following:
   // * LocalServer32 under CLSID_OnDemandMachineAppsClass or
   //   CLSID_OnDemandUserAppsClass should be ...Google\Update\GoogleUpdate.exe.
@@ -106,20 +106,31 @@
                                      &proxy_interface_value)));
   ASSERT1(!proxy_interface_value.IsEmpty());
   ASSERT1(proxy_interface_value == GuidToString(proxy_clsid));
-
+#else
+  UNREFERENCED_PARAMETER(is_machine);
+#endif
   return S_OK;
 }
 #endif
 
+HRESULT RegisterOrUnregisterService(bool reg, CString service_path) {
+  EnclosePath(&service_path);
+
+  CommandLineBuilder builder(reg ? COMMANDLINE_MODE_SERVICE_REGISTER :
+                                   COMMANDLINE_MODE_SERVICE_UNREGISTER);
+  CString cmd_line = builder.GetCommandLineArgs();
+  return RegisterOrUnregisterExe(service_path, cmd_line);
+}
+
 }  // namespace
 
-SetupGoogleUpdate::SetupGoogleUpdate(bool is_machine,
-                                     const CommandLineArgs* args)
+SetupGoogleUpdate::SetupGoogleUpdate(bool is_machine)
     : is_machine_(is_machine),
+      extra_code1_(S_OK)
 #ifdef _DEBUG
-      have_called_uninstall_previous_versions_(false),
+      , have_called_uninstall_previous_versions_(false)
 #endif
-      args_(args) {
+{  // NOLINT
   this_version_ = GetVersionString();
 }
 
@@ -138,6 +149,9 @@
   HRESULT hr = InstallRegistryValues();
   if (FAILED(hr)) {
     SETUP_LOG(LE, (_T("[InstallRegistryValues failed][0x%08x]"), hr));
+    if (E_ACCESSDENIED == hr) {
+      return GOOPDATE_E_ACCESSDENIED_SETUP_REG_ACCESS;
+    }
     return hr;
   }
 
@@ -150,27 +164,17 @@
   hr = InstallMsiHelper();
   if (FAILED(hr)) {
     SETUP_LOG(L1, (_T("[InstallMsiHelper failed][0x%08x]"), hr));
-    // TODO(omaha): Retry on ERROR_INSTALL_ALREADY_RUNNING like InstallManager
+    // TODO(omaha): Retry on ERROR_INSTALL_ALREADY_RUNNING like InstallerWrapper
     // if we move helper MSI installation after app installation.
     ASSERT1(HRESULT_FROM_WIN32(ERROR_INSTALL_SERVICE_FAILURE) == hr ||
             HRESULT_FROM_WIN32(ERROR_INSTALL_ALREADY_RUNNING) == hr);
   }
 
-  HRESULT hr_register_com_server(RegisterOrUnregisterCOMLocalServer(true));
-  ASSERT1(SUCCEEDED(hr_register_com_server));
-  if (FAILED(hr_register_com_server)) {
-    OPT_LOG(L3, (_T("[RegisterOrUnregisterCOMLocalServer failed][0x%x]"),
-                 hr_register_com_server));
-    Ping ping;
-    VERIFY1(SUCCEEDED(ping_utils::SendGoopdatePing(
-                                      is_machine_,
-                                      args_->extra,
-                                      args_->install_source,
-                                      PingEvent::EVENT_SETUP_COM_SERVER_FAILURE,
-                                      hr_register_com_server,
-                                      0,
-                                      NULL,
-                                      &ping)));
+  hr = RegisterOrUnregisterCOMLocalServer(true);
+  if (FAILED(hr)) {
+    OPT_LOG(LW, (_T("[RegisterOrUnregisterCOMLocalServer failed][0x%x]"), hr));
+
+    // Fall through. Omaha will attempt to install using the in-proc mode.
   }
 
   ASSERT1(SUCCEEDED(VerifyCOMLocalServerRegistration(is_machine_)));
@@ -178,10 +182,9 @@
   // We would prefer to uninstall previous versions last, but the web plugin
   // requires that the old plugin is uninstalled before installing the new one.
   VERIFY1(SUCCEEDED(UninstallPreviousVersions()));
-  VERIFY1(SUCCEEDED(InstallOptionalComponents()));
 
   // Writing this value indicates that this Omaha version was successfully
-  // installed.
+  // installed. This is an artifact of Omaha 2 when pv was set earlier in Setup.
   CString reg_update = ConfigManager::Instance()->registry_update(is_machine_);
   hr = RegKey::SetValue(reg_update, kRegValueInstalledVersion, this_version_);
   if (FAILED(hr)) {
@@ -198,7 +201,7 @@
   return S_OK;
 }
 
-
+// Version values are written at the end of setup, not here.
 HRESULT SetupGoogleUpdate::InstallRegistryValues() {
   OPT_LOG(L3, (_T("[SetupGoogleUpdate::InstallRegistryValues]")));
 
@@ -238,23 +241,49 @@
     return hr;
   }
 
-  // Set all the information for this instance correctly in the registry.
-  const CString goopdate_state_key_name =
-      cm->registry_client_state_goopdate(is_machine_);
+  ASSERT1(!this_version_.IsEmpty());
 
-  // IID and branding are optional, so ignore errors.
-  VERIFY1(SUCCEEDED(SetInstallationId()));
-  VERIFY1(SUCCEEDED(goopdate_utils::SetGoogleUpdateBranding(
-      goopdate_state_key_name,
-      args_->extra.brand_code,
-      args_->extra.client_id)));
+  const CString omaha_clients_key_path =
+      cm->registry_clients_goopdate(is_machine_);
+
+  // Set the version so the constant shell will know which version to use.
+  // TODO(omaha3): This should be the atomic switch of the version, but it must
+  // be called before registering the COM servers because GoogleUpdate.exe needs
+  // the pv to find goopdate.dll. We may need to support rolling this back.
+  hr = RegKey::SetValue(omaha_clients_key_path,
+                        kRegValueProductVersion,
+                        this_version_);
+  if (FAILED(hr)) {
+    SETUP_LOG(LE, (_T("[Failed to set version in registry][0x%08x]"), hr));
+    if (E_ACCESSDENIED == hr) {
+      return GOOPDATE_E_ACCESSDENIED_SETUP_REG_ACCESS;
+    }
+    return hr;
+  }
+
+  // Write Omaha's localized name to the registry. During installation, this
+  // will use the installation language. For self-updates, it will use the
+  // user's/Local System's language.
+  CString omaha_name;
+  if (!omaha_name.LoadString(IDS_PRODUCT_DISPLAY_NAME)) {
+    ASSERT1(false);
+    omaha_name = kAppName;
+  }
+  VERIFY1(SUCCEEDED(RegKey::SetValue(omaha_clients_key_path,
+                                     kRegValueAppName,
+                                     omaha_name)));
 
   // Set pv in ClientState for consistency. Optional, so ignore errors.
-  ASSERT1(!this_version_.IsEmpty());
-  VERIFY1(SUCCEEDED(RegKey::SetValue(goopdate_state_key_name,
+  const CString omaha_client_state_key_path =
+      cm->registry_client_state_goopdate(is_machine_);
+  VERIFY1(SUCCEEDED(RegKey::SetValue(omaha_client_state_key_path,
                                      kRegValueProductVersion,
                                      this_version_)));
 
+  if (is_machine_) {
+    VERIFY1(SUCCEEDED(goopdate_utils::EnableSEHOP(true)));
+  }
+
   return S_OK;
 }
 
@@ -300,21 +329,6 @@
   return S_OK;
 }
 
-// Overwrites any existing IID for Omaha if a new one is specified.
-HRESULT SetupGoogleUpdate::SetInstallationId() {
-  SETUP_LOG(L3, (_T("[SetupGoogleUpdate::SetInstallationId]")));
-
-  if (GUID_NULL != args_->extra.installation_id) {
-    CString goopdate_state_key_name =
-        ConfigManager::Instance()->registry_client_state_goopdate(is_machine_);
-    return RegKey::SetValue(goopdate_state_key_name,
-                            kRegValueInstallationId,
-                            GuidToString(args_->extra.installation_id));
-  }
-
-  return S_OK;
-}
-
 HRESULT SetupGoogleUpdate::InstallLaunchMechanisms() {
   SETUP_LOG(L3, (_T("[SetupGoogleUpdate::InstallLaunchMechanisms]")));
   if (is_machine_) {
@@ -337,14 +351,17 @@
 void SetupGoogleUpdate::UninstallLaunchMechanisms() {
   SETUP_LOG(L3, (_T("[SetupGoogleUpdate::UninstallLaunchMechanisms]")));
   if (is_machine_) {
-    VERIFY1(SUCCEEDED(SetupService::UninstallService()));
+    CString current_dir = app_util::GetModuleDirectory(NULL);
+    CString service_path = ConcatenatePath(current_dir, kServiceFileName);
+
+    VERIFY1(SUCCEEDED(RegisterOrUnregisterService(false, service_path)));
   } else {
     // We only need to do this in case of the user goopdate, as
     // there is no machine Run at startup installation.
     VERIFY1(SUCCEEDED(ConfigureUserRunAtStartup(false)));  // delete entry
   }
 
-  VERIFY1(SUCCEEDED(goopdate_utils::UninstallGoopdateTasks(is_machine_)));
+  VERIFY1(SUCCEEDED(scheduled_task_utils::UninstallGoopdateTasks(is_machine_)));
 }
 
 HRESULT SetupGoogleUpdate::InstallScheduledTask() {
@@ -353,7 +370,8 @@
   HighresTimer metrics_timer;
   const ULONGLONG install_task_start_ms = metrics_timer.GetElapsedMs();
 
-  HRESULT hr = goopdate_utils::InstallGoopdateTasks(exe_path, is_machine_);
+  HRESULT hr = scheduled_task_utils::InstallGoopdateTasks(exe_path,
+                                                          is_machine_);
 
   if (SUCCEEDED(hr)) {
     const ULONGLONG install_task_end_ms = metrics_timer.GetElapsedMs();
@@ -381,8 +399,9 @@
   OPT_LOG(L1, (_T("[Installing service]")));
   HighresTimer metrics_timer;
 
-  HRESULT service_hr = SetupService::InstallService(
+  HRESULT service_hr = RegisterOrUnregisterService(true,
       goopdate_utils::BuildGoogleUpdateExePath(is_machine_));
+  ASSERT(SUCCEEDED(service_hr), (_T("[registration err][0x%x]"), service_hr));
 
   if (SUCCEEDED(service_hr)) {
     metric_setup_install_service_ms.AddSample(
@@ -399,8 +418,13 @@
 
   if (FAILED(service_hr) && FAILED(task_hr)) {
     ++metric_setup_install_service_and_task_failed;
+    extra_code1_ = task_hr;
     return service_hr;
   }
+
+// TODO(omaha3): Setup does not know about OEM mode. Figure out a
+// different way to do this. Maybe just verify that both are installed.
+#if 0
   if (args_->is_oem_set) {
     // OEM installs are on clean systems in a controlled environment. We expect
     // both mechanisms to install.
@@ -411,6 +435,7 @@
       return task_hr;
     }
   }
+#endif
 
   if (SUCCEEDED(service_hr) && SUCCEEDED(task_hr)) {
     ++metric_setup_install_service_and_task_succeeded;
@@ -430,6 +455,7 @@
 
   if (FAILED(run_hr) && FAILED(task_hr)) {
     // We need atleast one launch mechanism.
+    extra_code1_ = task_hr;
     return run_hr;
   }
 
@@ -448,10 +474,10 @@
 HRESULT SetupGoogleUpdate::RegisterOrUnregisterCOMLocalServer(bool reg) {
   SETUP_LOG(L3, (_T("[SetupGoogleUpdate::RegisterOrUnregisterCOMLocalServer]")
                  _T("[%d]"), reg));
-  CString google_update_path = goopdate_utils::BuildGoogleUpdateExePath(
-                                   is_machine_);
+  const CString google_update_path =
+      goopdate_utils::BuildGoogleUpdateExePath(is_machine_);
   CString register_cmd;
-  register_cmd.Format(_T("/%s"), reg ? _T("RegServer") : _T("UnregServer"));
+  register_cmd.Format(_T("/%s"), reg ? kCmdRegServer : kCmdUnregServer);
   HRESULT hr = RegisterOrUnregisterExe(google_update_path, register_cmd);
   if (FAILED(hr)) {
     SETUP_LOG(LE, (_T("[RegisterOrUnregisterExe failed][0x%08x]"), hr));
@@ -472,11 +498,8 @@
   ++metric_setup_helper_msi_install_total;
   HighresTimer metrics_timer;
 
-  CString msi_path(app_util::GetCurrentModuleDirectory());
-  if (!::PathAppend(CStrBuf(msi_path, MAX_PATH), kHelperInstallerName)) {
-    ASSERT1(false);
-    return GOOPDATE_E_PATH_APPEND_FAILED;
-  }
+  const CPath msi_path(BuildSupportFileInstallPath(kHelperInstallerName));
+  ASSERT1(File::Exists(msi_path));
 
   // Setting INSTALLUILEVEL_NONE causes installation to be silent and not
   // create a restore point.
@@ -515,6 +538,7 @@
   // Setting INSTALLUILEVEL_NONE causes installation to be silent and not
   // create a restore point.
   ::MsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
+
   // MSDN says that eInstallState must be INSTALLSTATE_DEFAULT in order for the
   // command line to be used. Therefore, instead of using INSTALLSTATE_ABSENT
   // to uninstall, we must pass REMOVE=ALL in the command line.
@@ -538,20 +562,21 @@
   return S_OK;
 }
 
-HRESULT SetupGoogleUpdate::InstallOptionalComponents() {
-  SETUP_LOG(L3, (_T("[SetupGoogleUpdate::InstallOptionalComponents]")));
+HRESULT SetupGoogleUpdate::InstallBrowserPlugins() {
+  SETUP_LOG(L3, (_T("[SetupGoogleUpdate::InstallBrowserPlugins]")));
   ASSERT1(have_called_uninstall_previous_versions_);
   // Failure of registration of optional components is acceptable in release
   // builds.
   HRESULT hr = S_OK;
 
-  CString goopdate_oneclick_path =
-      BuildSupportFileInstallPath(ACTIVEX_FILENAME);
-  hr = RegisterDll(goopdate_oneclick_path);
+  CString plugin_path =
+      BuildSupportFileInstallPath(UPDATE_PLUGIN_FILENAME);
+  hr = RegisterDll(plugin_path);
   if (FAILED(hr)) {
-    SETUP_LOG(L1, (_T("[Register OneClick DLL failed][0x%08x]"), hr));
+    SETUP_LOG(L1, (_T("[Register plugin DLL failed][0x%08x]"), hr));
   }
 
+// TODO(omaha): Enable when we ship the BHO.
 #if 0
   // Only install the BHO for machine installs. There is no corresponding HKCU
   // registration for BHOs.
@@ -567,18 +592,19 @@
   return hr;
 }
 
-HRESULT SetupGoogleUpdate::UninstallOptionalComponents() {
-  SETUP_LOG(L3, (_T("[SetupGoogleUpdate::UninstallOptionalComponents]")));
+HRESULT SetupGoogleUpdate::UninstallBrowserPlugins() {
+  SETUP_LOG(L3, (_T("[SetupGoogleUpdate::UninstallBrowserPlugins]")));
   // Unregistration. Failure is acceptable in release builds.
   HRESULT hr = S_OK;
 
-  CString goopdate_oneclick_path =
-      BuildSupportFileInstallPath(ACTIVEX_FILENAME);
-  hr = UnregisterDll(goopdate_oneclick_path);
+  CString plugin_path =
+      BuildSupportFileInstallPath(UPDATE_PLUGIN_FILENAME);
+  hr = UnregisterDll(plugin_path);
   if (FAILED(hr)) {
-    SETUP_LOG(L1, (_T("[Unregister OneClick DLL failed][0x%08x]"), hr));
+    SETUP_LOG(L1, (_T("[Unregister plugin DLL failed][0x%08x]"), hr));
   }
 
+// TODO(omaha): Enable when we ship the BHO.
 #if 0
   if (is_machine_) {
     CString goopdate_bho_proxy_path = BuildSupportFileInstallPath(BHO_FILENAME);
@@ -605,9 +631,9 @@
 }
 
 CString SetupGoogleUpdate::BuildCoreProcessCommandLine() const {
+  CString google_update_path =
+      goopdate_utils::BuildGoogleUpdateExePath(is_machine_);
   CommandLineBuilder builder(COMMANDLINE_MODE_CORE);
-  CString google_update_path = goopdate_utils::BuildGoogleUpdateExePath(
-                                   is_machine_);
   return builder.GetCommandLine(google_update_path);
 }
 
@@ -616,7 +642,8 @@
   have_called_uninstall_previous_versions_ = true;
 #endif
 
-  VERIFY1(SUCCEEDED(goopdate_utils::UninstallLegacyGoopdateTasks(is_machine_)));
+  VERIFY1(SUCCEEDED(
+      scheduled_task_utils::UninstallLegacyGoopdateTasks(is_machine_)));
 
   CString install_path(
       is_machine_ ? ConfigManager::Instance()->GetMachineGoopdateInstallDir() :
@@ -644,6 +671,10 @@
     return HRESULT_FROM_WIN32(err);
   }
 
+  // Find the rightmost path element of the download directory.
+  CPath download_dir(OMAHA_REL_DOWNLOAD_STORAGE_DIR);
+  download_dir.StripPath();
+
   BOOL found_next = TRUE;
   for (; found_next; found_next = ::FindNextFile(get(find_handle),
                                                  &file_data)) {
@@ -651,19 +682,18 @@
     VERIFY1(file_or_directory.Append(file_data.cFileName));
     if (!(file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
       // Do not delete the shell as it is used by all versions.
-      if (_tcsicmp(file_data.cFileName, kGoopdateFileName)) {
+      if (_tcsicmp(file_data.cFileName, kOmahaShellFileName)) {
         DeleteBeforeOrAfterReboot(file_or_directory);
       }
     } else if (_tcscmp(file_data.cFileName, _T("..")) &&
                _tcscmp(file_data.cFileName, _T(".")) &&
                _tcsicmp(file_data.cFileName, this_version_) &&
-               (!args_->is_offline_set ||
-                _tcscmp(file_data.cFileName, OFFLINE_DIR_NAME))) {
+               _tcsicmp(file_data.cFileName, download_dir)) {
       // Unregister the previous version OneClick if it exists. Ignore
       // failures. The file is named npGoogleOneClick*.dll.
       CPath old_oneclick(file_or_directory);
-      VERIFY1(old_oneclick.Append(ACTIVEX_NAME _T("*.dll")));
-      WIN32_FIND_DATA old_oneclick_file_data = {0};
+      VERIFY1(old_oneclick.Append(ONECLICK_PLUGIN_NAME _T("*.dll")));
+      WIN32_FIND_DATA old_oneclick_file_data = {};
       scoped_hfind found_oneclick(::FindFirstFile(old_oneclick,
                                                   &old_oneclick_file_data));
       if (found_oneclick) {
@@ -672,7 +702,23 @@
         VERIFY1(SUCCEEDED(UnregisterDll(old_oneclick_file)));
       }
 
+      // Unregister the previous version of the plugin if it exists. Ignore
+      // failures. The file is named npGoogleUpdate*.dll.
+      CPath old_plugin(file_or_directory);
+      VERIFY1(old_plugin.Append(UPDATE_PLUGIN_NAME _T("*.dll")));
+      WIN32_FIND_DATA old_plugin_file_data = {};
+      scoped_hfind found_plugin(::FindFirstFile(old_plugin,
+                                                &old_plugin_file_data));
+      if (found_plugin) {
+        CPath old_plugin_file(file_or_directory);
+        VERIFY1(old_plugin_file.Append(old_plugin_file_data.cFileName));
+        VERIFY1(SUCCEEDED(UnregisterDll(old_plugin_file)));
+      }
+
+
       if (is_machine_) {
+        // TODO(omaha): Enable when we ship the BHO.
+        /*
         // BHO is only installed for the machine case.
         // Unregister the previous version BHO if it exists. Ignore failures.
         CPath old_bho_dll(file_or_directory);
@@ -682,6 +728,7 @@
             SETUP_LOG(LW, (L"[UnregisterDll() failed][%s]", old_bho_dll));
           }
         }
+        */
       }
       // Delete entire sub-directory.
       DeleteBeforeOrAfterReboot(file_or_directory);
@@ -696,11 +743,6 @@
     }
   }
 
-  // Need to clean up old lang key for Goopdate.  Don't care about failure.
-  CString goopdate_client_key =
-      ConfigManager::Instance()->registry_clients_goopdate(is_machine_);
-  RegKey::DeleteValue(goopdate_client_key, kRegValueLanguage);
-
   // Clean up existing machine ID and user ID since they are no longer used.
   // Ignore failures as they may not be present and we may not have permission
   // to HKLM.
@@ -718,19 +760,19 @@
 void SetupGoogleUpdate::Uninstall() {
   OPT_LOG(L1, (_T("[SetupGoogleUpdate::Uninstall]")));
 
-  HRESULT hr = UninstallOptionalComponents();
+  HRESULT hr = UninstallBrowserPlugins();
   if (FAILED(hr)) {
-    SETUP_LOG(LW, (_T("[UninstallOptionalComponents failed][0x%08x]"), hr));
+    SETUP_LOG(LW, (_T("[UninstallBrowserPlugins failed][0x%08x]"), hr));
     ASSERT1(HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND) == hr);
   }
 
-  // TODO(omaha): Some of the checks assume that The uninstall is being
-  // executed from the installed path. This is not the case when /install is
-  // uninstalling and will also not be the case when Setup is merged into one
-  // process.
+  // If running from the installed location instead of a temporary location,
+  // we assume that Omaha had been properly installed and can verify the COM
+  // registration.
   if (goopdate_utils::IsRunningFromOfficialGoopdateDir(is_machine_)) {
     ASSERT1(SUCCEEDED(VerifyCOMLocalServerRegistration(is_machine_)));
   }
+
   hr = RegisterOrUnregisterCOMLocalServer(false);
   if (FAILED(hr)) {
     SETUP_LOG(LW,
@@ -756,6 +798,10 @@
 HRESULT SetupGoogleUpdate::DeleteRegistryKeys() {
   OPT_LOG(L3, (_T("[SetupGoogleUpdate::DeleteRegistryKeys]")));
 
+  if (is_machine_) {
+    VERIFY1(SUCCEEDED(goopdate_utils::EnableSEHOP(false)));
+  }
+
   CString root_key = ConfigManager::Instance()->registry_update(is_machine_);
   ASSERT1(!root_key.IsEmpty());
 
@@ -783,10 +829,9 @@
   }
 
   // Now delete all the values of the root key.
-  // The mi and ui values are not deleted.
   // The Last* values are not deleted.
-  // TODO(Omaha): The above a temporary fix for bug 1539293. Need a better
-  // long-term solution.
+  // TODO(omaha3): The above is a temporary fix for bug 1539293. Need a better
+  // long-term solution in Omaha 3.
   size_t num_values = root.GetValueCount();
   std::vector<CString> value_names;
   for (size_t i = 0; i < num_values; ++i) {
@@ -796,8 +841,10 @@
     ASSERT1(hr == S_OK);
     // TODO(omaha): Remove kRegValueLast* once we have an install API.
     if (SUCCEEDED(hr)) {
-      if (value_name != kRegValueLastInstallerResult &&
+      if (value_name != kRegValueUserId &&
+          value_name != kRegValueLastInstallerResult &&
           value_name != kRegValueLastInstallerError &&
+          value_name != kRegValueLastInstallerExtraCode1 &&
           value_name != kRegValueLastInstallerResultUIString &&
           value_name != kRegValueLastInstallerSuccessLaunchCmdLine) {
         value_names.push_back(value_name);
diff --git a/setup/setup_google_update.h b/setup/setup_google_update.h
index e15cdc7..676acfd 100644
--- a/setup/setup_google_update.h
+++ b/setup/setup_google_update.h
@@ -19,25 +19,27 @@
 #include <windows.h>
 #include <atlstr.h>
 #include "base/basictypes.h"
-#include "omaha/common/constants.h"
+#include "omaha/base/constants.h"
 
 namespace omaha {
 
-struct CommandLineArgs;
-
 class SetupGoogleUpdate {
  public:
-  SetupGoogleUpdate(bool is_machine, const CommandLineArgs* args);
+  explicit SetupGoogleUpdate(bool is_machine);
   ~SetupGoogleUpdate();
 
   HRESULT FinishInstall();
 
+  HRESULT InstallBrowserPlugins();
+
   // Uninstalls Google Update registrations created by FinishInstall().
   void Uninstall();
 
   // Build the command line to execute the installed core.
   CString BuildCoreProcessCommandLine() const;
 
+  int extra_code1() const { return extra_code1_; }
+
  private:
   HRESULT FinishMachineInstall();
 
@@ -81,9 +83,7 @@
   // Uninstalls the helper (MSI).
   HRESULT UninstallMsiHelper();
 
-  HRESULT InstallOptionalComponents();
-
-  HRESULT UninstallOptionalComponents();
+  HRESULT UninstallBrowserPlugins();
 
   // Build the install file path for support files. For example,
   CString BuildSupportFileInstallPath(const CString& filename) const;
@@ -95,20 +95,20 @@
   // Uninstall previous versions after an overinstall of the new version. We do
   // the following:
   //   * Delete all sub-directories under Google\\Update, except the running
-  //     version's directory.
+  //     version's directory and the cache directory.
   //   * Uninstall the BHO, so IE does not load it in the future.
   HRESULT UninstallPreviousVersions();
 
   const bool is_machine_;
-  const CommandLineArgs* const args_;
   CString this_version_;
+  int extra_code1_;
 
 #ifdef _DEBUG
   bool have_called_uninstall_previous_versions_;
 #endif
 
   friend class SetupGoogleUpdateTest;
-  friend class AppManagerTest;
+  friend class AppManagerTestBase;
 
   DISALLOW_EVIL_CONSTRUCTORS(SetupGoogleUpdate);
 };
diff --git a/setup/setup_google_update_unittest.cc b/setup/setup_google_update_unittest.cc
index 38bc99b..aeeffa8 100644
--- a/setup/setup_google_update_unittest.cc
+++ b/setup/setup_google_update_unittest.cc
@@ -15,21 +15,20 @@
 
 #include <windows.h>
 #include <msi.h>
+#include <atlpath.h>
 #include "base/scoped_ptr.h"
-#include "goopdate/google_update_idl.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/atlregmapex.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/atlregmapex.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/time.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/config_manager.h"
 #include "omaha/common/const_cmd_line.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/file.h"
-#include "omaha/common/path.h"
-#include "omaha/common/time.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/common/scheduled_task_utils.h"
 #include "omaha/setup/msi_test_utils.h"
 #include "omaha/setup/setup_google_update.h"
 #include "omaha/testing/unit_test.h"
@@ -45,38 +44,39 @@
 const TCHAR kRunKey[] =
     _T("HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run");
 
-const TCHAR* const kExpectedIid = _T("{A972BB39-CCA3-4F25-9737-3308F5FA19B5}");
-const TCHAR* const kExpectedBrand = _T("GOOG");
-const TCHAR* const kExpectedClientId = _T("some_partner");
-
 const TCHAR* const kAppId1 = _T("{B7E61EF9-AAE5-4cdf-A2D3-E4C8DF975145}");
 const TCHAR* const kAppId2 = _T("{35F1A986-417D-4039-8718-781DD418232A}");
 
 const TCHAR kRegistryHiveOverrideRootInHklm[] =
-    _T("HKLM\\Software\\Google\\Update\\UnitTest\\");
+    _T("HKLM\\Software\\") _T(SHORT_COMPANY_NAME_ANSI)
+    _T("\\") _T(PRODUCT_NAME_ANSI)
+    _T("\\UnitTest\\");
 
 // Copies the shell and DLLs that FinishInstall needs.
 // Does not replace files if they already exist.
 void CopyFilesRequiredByFinishInstall(bool is_machine, const CString& version) {
-  ASSERT_FALSE(is_machine) << _T("machine installs not currently supported");
   const CString omaha_path = is_machine ?
       GetGoogleUpdateMachinePath() : GetGoogleUpdateUserPath();
   const CString expected_shell_path =
-      ConcatenatePath(omaha_path, _T("GoogleUpdate.exe"));
+      ConcatenatePath(omaha_path, kOmahaShellFileName);
   const CString version_path = ConcatenatePath(omaha_path, version);
 
   ASSERT_SUCCEEDED(CreateDir(omaha_path, NULL));
   ASSERT_SUCCEEDED(File::Copy(
       ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                      _T("GoogleUpdate.exe")),
+                      kOmahaShellFileName),
       expected_shell_path,
       false));
 
   ASSERT_SUCCEEDED(CreateDir(version_path, NULL));
 
-  const TCHAR* files[] = {_T("goopdate.dll"),
+  const TCHAR* files[] = {kOmahaDllName,
+                          kHelperInstallerName,
+// TODO(omaha3): Enable once this is being built.
+#if 0
                           _T("GoopdateBho.dll"),
-                          ACTIVEX_FILENAME};
+#endif
+                          UPDATE_PLUGIN_FILENAME};
   for (size_t i = 0; i < arraysize(files); ++i) {
     ASSERT_SUCCEEDED(File::Copy(
         ConcatenatePath(app_util::GetCurrentModuleDirectory(),
@@ -103,6 +103,8 @@
   CString base_clsid_key(is_machine ? _T("HKLM") : _T("HKCU"));
   base_clsid_key += _T("\\Software\\Classes\\CLSID\\");
   CString ondemand_clsid_key(base_clsid_key);
+// TODO(omaha3): Implement this for Omaha 3.
+#if 0
   ondemand_clsid_key += GuidToString(is_machine ?
                                      __uuidof(OnDemandMachineAppsClass) :
                                      __uuidof(OnDemandUserAppsClass));
@@ -133,6 +135,7 @@
   ASSERT_SUCCEEDED(RegKey::SetValue(igoogleupdate_interface_key,
                                     NULL,
                                     proxy_interface_value));
+#endif
 }
 
 void VerifyAccessRightsForTrustee(const CString& key_name,
@@ -181,7 +184,7 @@
 
   CString current_user_sid_string;
   EXPECT_SUCCEEDED(
-      user_info::GetCurrentUser(NULL, NULL, &current_user_sid_string));
+      user_info::GetProcessUser(NULL, NULL, &current_user_sid_string));
   PSID current_user_sid = NULL;
   EXPECT_TRUE(ConvertStringSidToSid(current_user_sid_string,
                                     &current_user_sid));
@@ -264,16 +267,8 @@
       : is_machine_(is_machine) {
   }
 
-  void SetUp() {
-    GUID iid  = StringToGuid(kExpectedIid);
-    CommandLineAppArgs extra;
-    args_.extra.language = _T("en");
-    args_.extra.installation_id = iid;
-    args_.extra.brand_code = kExpectedBrand;
-    args_.extra.client_id = kExpectedClientId;
-    args_.extra.referral_id = _T("should not be used by setup");
-    args_.extra.apps.push_back(extra);
-    setup_google_update_.reset(new SetupGoogleUpdate(is_machine_, &args_));
+  virtual void SetUp() {
+    setup_google_update_.reset(new SetupGoogleUpdate(is_machine_));
   }
 
   HRESULT InstallRegistryValues() {
@@ -302,7 +297,6 @@
 
   bool is_machine_;
   scoped_ptr<SetupGoogleUpdate> setup_google_update_;
-  CommandLineArgs args_;
 };
 
 class SetupGoogleUpdateUserTest : public SetupGoogleUpdateTest {
@@ -326,11 +320,11 @@
       : SetupGoogleUpdateUserTest(),
         hive_override_key_name_(kRegistryHiveOverrideRoot) {
     const CString expected_shell_path =
-        ConcatenatePath(GetGoogleUpdateUserPath(), _T("GoogleUpdate.exe"));
+        ConcatenatePath(GetGoogleUpdateUserPath(), kOmahaShellFileName);
     expected_run_key_value_.Format(_T("\"%s\" /c"), expected_shell_path);
   }
 
-  void SetUp() {
+  virtual void SetUp() {
     RegKey::DeleteKey(hive_override_key_name_, true);
     // Do not override HKLM because it contains the CSIDL_* definitions.
     OverrideSpecifiedRegistryHives(hive_override_key_name_, false, true);
@@ -357,7 +351,7 @@
         hive_override_key_name_(kRegistryHiveOverrideRoot) {
   }
 
-  void SetUp() {
+  virtual void SetUp() {
     RegKey::DeleteKey(hive_override_key_name_, true);
     OverrideRegistryHives(hive_override_key_name_);
 
@@ -429,16 +423,18 @@
   // Check the system state.
 
   CPath expected_shell_path(GetGoogleUpdateUserPath());
-  expected_shell_path.Append(_T("GoogleUpdate.exe"));
+  expected_shell_path.Append(kOmahaShellFileName);
   CString shell_path;
   EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_UPDATE, _T("path"), &shell_path));
   EXPECT_STREQ(expected_shell_path, shell_path);
 
   CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kRunKey, _T("Google Update"), &value));
+  EXPECT_SUCCEEDED(RegKey::GetValue(kRunKey,
+                                    _T(OMAHA_APP_NAME_ANSI),
+                                    &value));
   EXPECT_STREQ(expected_run_key_value_, value);
-  EXPECT_TRUE(goopdate_utils::IsInstalledGoopdateTaskUA(false));
-  EXPECT_FALSE(goopdate_utils::IsDisabledGoopdateTaskUA(false));
+  EXPECT_TRUE(scheduled_task_utils::IsInstalledGoopdateTaskUA(false));
+  EXPECT_FALSE(scheduled_task_utils::IsDisabledGoopdateTaskUA(false));
 
   EXPECT_SUCCEEDED(
       RegKey::GetValue(USER_REG_UPDATE, kRegValueInstalledVersion, &value));
@@ -454,8 +450,8 @@
   // Clean up the launch mechanisms, at least one of which is not in the
   // overriding registry.
   UninstallLaunchMechanisms();
-  EXPECT_FALSE(RegKey::HasValue(kRunKey, _T("Google Update")));
-  EXPECT_FALSE(goopdate_utils::IsInstalledGoopdateTaskUA(false));
+  EXPECT_FALSE(RegKey::HasValue(kRunKey, _T(OMAHA_APP_NAME_ANSI)));
+  EXPECT_FALSE(scheduled_task_utils::IsInstalledGoopdateTaskUA(false));
 
   if (!had_pv) {
     // Delete the pv value. Some tests or shell .exe instances may see this
@@ -469,6 +465,10 @@
 // TODO(omaha): Assumes GoogleUpdate.exe exists in the installed location, which
 // is not always true when run independently.
 TEST_F(SetupGoogleUpdateUserRegistryProtectedTest, InstallRegistryValues) {
+  if (IsTestRunByLocalSystem()) {
+    return;
+  }
+
   // For this test only, we must also override HKLM in order to check that
   // MACHINE_REG_CLIENT_STATE_MEDIUM was not created.
   // Get the correct value of and set the CSIDL value needed by the test.
@@ -476,7 +476,7 @@
   EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROFILE, &profile_path));
   OverrideSpecifiedRegistryHives(hive_override_key_name_, true, true);
   CString user_sid;
-  EXPECT_SUCCEEDED(user_info::GetCurrentUser(NULL, NULL, &user_sid));
+  EXPECT_SUCCEEDED(user_info::GetProcessUser(NULL, NULL, &user_sid));
   const TCHAR* const kProfileListKey =
       _T("HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\")
       _T("ProfileList\\");
@@ -513,37 +513,11 @@
   EXPECT_EQ(1, client_state_key.GetSubkeyCount());
 
   CPath expected_shell_path(GetGoogleUpdateUserPath());
-  expected_shell_path.Append(_T("GoogleUpdate.exe"));
+  expected_shell_path.Append(kOmahaShellFileName);
   CString shell_path;
   EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_UPDATE, _T("path"), &shell_path));
   EXPECT_STREQ(expected_shell_path, shell_path);
 
-  CString iid;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(USER_REG_CLIENT_STATE_GOOPDATE, _T("iid"), &iid));
-  EXPECT_STREQ(kExpectedIid, iid);
-
-  CString brand;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(USER_REG_CLIENT_STATE_GOOPDATE, _T("brand"), &brand));
-  EXPECT_STREQ(kExpectedBrand, brand);
-
-  CString client_id;
-  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("client"),
-                                    &client_id));
-  EXPECT_STREQ(kExpectedClientId, client_id);
-
-  EXPECT_FALSE(
-      RegKey::HasValue(USER_REG_CLIENT_STATE_GOOPDATE, _T("referral")));
-
-  DWORD install_time(0);
-  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("InstallTime"),
-                                    &install_time));
-  EXPECT_GE(now, install_time);
-  EXPECT_GE(static_cast<uint32>(200), now - install_time);
-
   CString product_version;
   EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_CLIENT_STATE_GOOPDATE,
                                     _T("pv"),
@@ -592,38 +566,14 @@
 
   CString expected_shell_path;
   EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &expected_shell_path));
-  expected_shell_path.Append(_T("\\Google\\Update\\GoogleUpdate.exe"));
+  expected_shell_path.Append(_T("\\") SHORT_COMPANY_NAME
+                             _T("\\") PRODUCT_NAME
+                             _T("\\GoogleUpdate.exe"));
   CString shell_path;
   EXPECT_SUCCEEDED(
       RegKey::GetValue(MACHINE_REG_UPDATE, _T("path"), &shell_path));
   EXPECT_STREQ(expected_shell_path, shell_path);
 
-  CString iid;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE, _T("iid"), &iid));
-  EXPECT_STREQ(kExpectedIid, iid);
-
-  CString brand;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE, _T("brand"), &brand));
-  EXPECT_STREQ(kExpectedBrand, brand);
-
-  CString client_id;
-  EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("client"),
-                                    &client_id));
-  EXPECT_STREQ(kExpectedClientId, client_id);
-
-  EXPECT_FALSE(
-      RegKey::HasValue(MACHINE_REG_CLIENT_STATE_GOOPDATE, _T("referral")));
-
-  DWORD install_time(0);
-  EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("InstallTime"),
-                                    &install_time));
-  EXPECT_GE(now, install_time);
-  EXPECT_GE(static_cast<uint32>(200), now - install_time);
-
   CString product_version;
   EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
                                     _T("pv"),
@@ -723,19 +673,23 @@
 
 TEST_F(SetupGoogleUpdateUserRegistryProtectedTest,
        InstallLaunchMechanisms_RunKeyValueExists) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kRunKey, _T("Google Update"), _T("fo /b")));
+  EXPECT_SUCCEEDED(RegKey::SetValue(kRunKey,
+                                    _T(OMAHA_APP_NAME_ANSI),
+                                    _T("fo /b")));
 
   EXPECT_SUCCEEDED(InstallLaunchMechanisms());
 
   CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kRunKey, _T("Google Update"), &value));
+  EXPECT_SUCCEEDED(RegKey::GetValue(kRunKey,
+                                    _T(OMAHA_APP_NAME_ANSI),
+                                    &value));
   EXPECT_STREQ(expected_run_key_value_, value);
-  EXPECT_TRUE(goopdate_utils::IsInstalledGoopdateTaskUA(false));
-  EXPECT_FALSE(goopdate_utils::IsDisabledGoopdateTaskUA(false));
+  EXPECT_TRUE(scheduled_task_utils::IsInstalledGoopdateTaskUA(false));
+  EXPECT_FALSE(scheduled_task_utils::IsDisabledGoopdateTaskUA(false));
 
   UninstallLaunchMechanisms();
-  EXPECT_FALSE(RegKey::HasValue(kRunKey, _T("Google Update")));
-  EXPECT_FALSE(goopdate_utils::IsInstalledGoopdateTaskUA(false));
+  EXPECT_FALSE(RegKey::HasValue(kRunKey, _T(OMAHA_APP_NAME_ANSI)));
+  EXPECT_FALSE(scheduled_task_utils::IsInstalledGoopdateTaskUA(false));
 }
 
 TEST_F(SetupGoogleUpdateUserRegistryProtectedTest,
@@ -745,14 +699,16 @@
   EXPECT_SUCCEEDED(InstallLaunchMechanisms());
 
   CString value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kRunKey, _T("Google Update"), &value));
+  EXPECT_SUCCEEDED(RegKey::GetValue(kRunKey,
+                                    _T(OMAHA_APP_NAME_ANSI),
+                                    &value));
   EXPECT_STREQ(expected_run_key_value_, value);
-  EXPECT_TRUE(goopdate_utils::IsInstalledGoopdateTaskUA(false));
-  EXPECT_FALSE(goopdate_utils::IsDisabledGoopdateTaskUA(false));
+  EXPECT_TRUE(scheduled_task_utils::IsInstalledGoopdateTaskUA(false));
+  EXPECT_FALSE(scheduled_task_utils::IsDisabledGoopdateTaskUA(false));
 
   UninstallLaunchMechanisms();
-  EXPECT_FALSE(RegKey::HasValue(kRunKey, _T("Google Update")));
-  EXPECT_FALSE(goopdate_utils::IsInstalledGoopdateTaskUA(false));
+  EXPECT_FALSE(RegKey::HasValue(kRunKey, _T(OMAHA_APP_NAME_ANSI)));
+  EXPECT_FALSE(scheduled_task_utils::IsInstalledGoopdateTaskUA(false));
 }
 
 // The helper can be installed when the test begins.
@@ -764,6 +720,8 @@
   const TCHAR* MsiInstallRegValueKey =
       ConfigManager::Instance()->machine_registry_update();
 
+  CopyFilesRequiredByFinishInstall(is_machine_, GetVersionString());
+
   if (vista_util::IsUserAdmin()) {
     // Prepare for the test - make sure the helper isn't installed.
     EXPECT_HRESULT_SUCCEEDED(UninstallMsiHelper());
@@ -841,6 +799,8 @@
   const TCHAR* MsiInstallRegValueKey =
       ConfigManager::Instance()->machine_registry_update();
 
+  CopyFilesRequiredByFinishInstall(is_machine_, GetVersionString());
+
   CString different_msi_path(app_util::GetCurrentModuleDirectory());
   ASSERT_TRUE(::PathAppend(CStrBuf(different_msi_path, MAX_PATH),
                            kDifferentMsi));
diff --git a/setup/setup_metrics.cc b/setup/setup_metrics.cc
index 253f377..78464c5 100644
--- a/setup/setup_metrics.cc
+++ b/setup/setup_metrics.cc
@@ -27,16 +27,10 @@
 DEFINE_METRIC_count(setup_do_self_install_total);
 DEFINE_METRIC_count(setup_do_self_install_succeeded);
 
-DEFINE_METRIC_count(setup_handoff_only_total);
-DEFINE_METRIC_count(setup_handoff_only_succeeded);
-
 DEFINE_METRIC_timing(setup_install_google_update_total_ms);
 
-DEFINE_METRIC_timing(setup_handoff_ms);
 DEFINE_METRIC_timing(setup_handoff_ui_ms);
 
-DEFINE_METRIC_count(setup_installed_core_not_running);
-
 DEFINE_METRIC_count(setup_should_install_total);
 DEFINE_METRIC_count(setup_should_install_false_oc);
 DEFINE_METRIC_count(setup_should_install_true_fresh_install);
@@ -90,7 +84,6 @@
 DEFINE_METRIC_timing(setup_helper_msi_install_ms);
 
 DEFINE_METRIC_count(setup_locks_failed);
-DEFINE_METRIC_count(setup_lock11_failed);
 DEFINE_METRIC_count(setup_lock12_failed);
 
 DEFINE_METRIC_timing(setup_lock_acquire_ms);
@@ -100,12 +93,9 @@
 DEFINE_METRIC_count(setup_process_wait_failed_core);
 DEFINE_METRIC_count(setup_process_wait_failed_report);
 DEFINE_METRIC_count(setup_process_wait_failed_update);
-DEFINE_METRIC_count(setup_process_wait_failed_ig);
 DEFINE_METRIC_count(setup_process_wait_failed_handoff);
-DEFINE_METRIC_count(setup_process_wait_failed_ug);
 DEFINE_METRIC_count(setup_process_wait_failed_ua);
 DEFINE_METRIC_count(setup_process_wait_failed_cr);
-DEFINE_METRIC_count(setup_process_wait_failed_legacy);
 DEFINE_METRIC_count(setup_process_wait_failed_other);
 
 DEFINE_METRIC_timing(setup_process_wait_ms);
diff --git a/setup/setup_metrics.h b/setup/setup_metrics.h
index 9b56de0..33c92d1 100644
--- a/setup/setup_metrics.h
+++ b/setup/setup_metrics.h
@@ -1,4 +1,4 @@
-// Copyright 2008-2009 Google Inc.
+// Copyright 2008-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -15,21 +15,24 @@
 
 // Declares the usage metrics used by the setup module.
 
-#ifndef OMAHA_SETUP_SETUP_METRICS_H__
-#define OMAHA_SETUP_SETUP_METRICS_H__
+#ifndef OMAHA_SETUP_SETUP_METRICS_H_
+#define OMAHA_SETUP_SETUP_METRICS_H_
 
 #include "omaha/statsreport/metrics.h"
 
 namespace omaha {
 
-// How many times the Install() was called.
+// TODO(omaha3): These descriptions need to be updated to match Omaha 3.
+// They may also need to be moved to other files.
+
+// How many times the Install() function was called.
 DECLARE_METRIC_count(setup_install_total);
-// How many times the Install() method succeeded.
+// How many times the Install() function succeeded.
 DECLARE_METRIC_count(setup_install_succeeded);
 
-// How many times the UpdateSelfSilently() was called.
+// How many times the UpdateSelfSilently() function was called.
 DECLARE_METRIC_count(setup_update_self_total);
-// How many times the UpdateSelfSilently() method succeeded.
+// How many times the UpdateSelfSilently() function succeeded.
 DECLARE_METRIC_count(setup_update_self_succeeded);
 
 // How many times the Install() resulted in a Google Update install attempt.
@@ -37,26 +40,13 @@
 // How many times the Install() succeeded in installing Google Update.
 DECLARE_METRIC_count(setup_do_self_install_succeeded);
 
-// How many times the Install() resulted in a handoff attempt.
-// Does not include legacy handoffs, which do not go through Setup.
-DECLARE_METRIC_count(setup_handoff_only_total);
-// How many times the Install() successfully handed off.
-// Does not include legacy handoffs, which do not go through Setup.
-DECLARE_METRIC_count(setup_handoff_only_succeeded);
-
 // Total time (ms) spent installing Google Update as measured by time between
 // when DoInstall() is called and Setup phase 2 completes. Excludes handoffs.
 DECLARE_METRIC_timing(setup_install_google_update_total_ms);
 
-// Time (ms) from Setup starts until hands off to existing Omaha installation.
-DECLARE_METRIC_timing(setup_handoff_ms);
 // Time (ms) from Setup starts until the hand off process displays its UI.
 DECLARE_METRIC_timing(setup_handoff_ui_ms);
 
-// How many times Setup was run when Omaha was already installed but the core
-// was not running. Only incremented if existing Omaha version >= 1.2.0.0.
-DECLARE_METRIC_count(setup_installed_core_not_running);
-
 //
 // ShouldInstall metrics
 //
@@ -176,8 +166,6 @@
 
 // How many times Setup failed to get any of the Setup locks.
 DECLARE_METRIC_count(setup_locks_failed);
-// How many times Setup failed to get the 1.1 Setup Lock.
-DECLARE_METRIC_count(setup_lock11_failed);
 // How many times Setup failed to get the 1.2 Setup Lock.
 DECLARE_METRIC_count(setup_lock12_failed);
 
@@ -193,12 +181,9 @@
 DECLARE_METRIC_count(setup_process_wait_failed_core);
 DECLARE_METRIC_count(setup_process_wait_failed_report);
 DECLARE_METRIC_count(setup_process_wait_failed_update);
-DECLARE_METRIC_count(setup_process_wait_failed_ig);
 DECLARE_METRIC_count(setup_process_wait_failed_handoff);
-DECLARE_METRIC_count(setup_process_wait_failed_ug);
 DECLARE_METRIC_count(setup_process_wait_failed_ua);
 DECLARE_METRIC_count(setup_process_wait_failed_cr);
-DECLARE_METRIC_count(setup_process_wait_failed_legacy);  // Any legacy mode.
 DECLARE_METRIC_count(setup_process_wait_failed_other);   // All other modes.
 
 // Time (ms) spent waiting for processes to exit - both successes and failures.
@@ -225,4 +210,4 @@
 
 }  // namespace omaha
 
-#endif  // OMAHA_SETUP_SETUP_METRICS_H__
+#endif  // OMAHA_SETUP_SETUP_METRICS_H_
diff --git a/setup/setup_service.cc b/setup/setup_service.cc
deleted file mode 100644
index b69f878..0000000
--- a/setup/setup_service.cc
+++ /dev/null
@@ -1,290 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include "omaha/setup/setup_service.h"
-
-#include "base/basictypes.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/path.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/service_utils.h"
-#include "omaha/goopdate/command_line_builder.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/service/service_main.h"
-
-namespace omaha {
-
-HRESULT SetupService::StartService() {
-  OPT_LOG(L1, (_T("[SetupService::StartService]")));
-
-  scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
-  if (!scm) {
-    return HRESULTFromLastError();
-  }
-  CString service_name(ConfigManager::GetCurrentServiceName());
-  if (service_name.IsEmpty()) {
-    return GOOPDATE_E_SERVICE_NAME_EMPTY;
-  }
-  scoped_service service(::OpenService(get(scm),
-                                       service_name,
-                                       SERVICE_QUERY_STATUS | SERVICE_START));
-  if (!service) {
-    return HRESULTFromLastError();
-  }
-
-  SERVICE_STATUS status = {0};
-  if (::QueryServiceStatus(get(service), &status)) {
-    if (status.dwCurrentState == SERVICE_RUNNING ||
-        status.dwCurrentState == SERVICE_START_PENDING) {
-      SETUP_LOG(L1, (_T("[SetupService::StartService already running]")));
-      return S_OK;
-    }
-  }
-
-  // Start the service.
-  if (!::StartService(get(service), 0, NULL)) {
-    return HRESULTFromLastError();
-  }
-
-  SETUP_LOG(L1, (_T("[SetupService::StartService started]")));
-  return S_OK;
-}
-
-HRESULT SetupService::StopService() {
-  SETUP_LOG(L1, (_T("[SetupService::StopService]")));
-  CString service_name(ConfigManager::GetCurrentServiceName());
-  if (service_name.IsEmpty()) {
-    return GOOPDATE_E_SERVICE_NAME_EMPTY;
-  }
-
-  return ServiceInstall::StopService(service_name);
-}
-
-HRESULT SetupService::InstallService(const TCHAR* file_path) {
-  ASSERT1(file_path);
-
-  // Quote the FilePath before creating/updating the service. Append the
-  // arguments to be passed to the service entry point.
-  CString file_path_service(file_path);
-  EnclosePath(&file_path_service);
-  CString service_cmd_line(file_path_service);
-  CommandLineBuilder builder(COMMANDLINE_MODE_SERVICE);
-  service_cmd_line.AppendFormat(_T(" %s"), builder.GetCommandLineArgs());
-  SETUP_LOG(L2, (_T("[service command line ][%s]"), service_cmd_line));
-
-  CString service_description;
-  VERIFY1(service_description.LoadString(IDS_SERVICE_DESCRIPTION));
-
-  HRESULT hr = DoInstallService(service_cmd_line, service_description);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return InstallCOMService();
-}
-
-HRESULT SetupService::InstallCOMService() {
-  SETUP_LOG(L1, (_T("[SetupService::InstallCOMService]")));
-
-  return ServiceModule().RegisterCOMService();
-}
-
-HRESULT SetupService::DoInstallService(const TCHAR* service_cmd_line,
-                                       const TCHAR* description) {
-  SETUP_LOG(L1, (_T("[SetupService::DoInstallService][%s]"), service_cmd_line));
-
-  ASSERT1(service_cmd_line);
-  ASSERT1(description);
-
-  if (goopdate_utils::IsServiceInstalled()) {
-    // Lightweight upgrade of existing service.
-    HRESULT hr = UpgradeService(service_cmd_line);
-    ASSERT1(SUCCEEDED(hr));
-    if (SUCCEEDED(hr)) {
-      VERIFY1(SUCCEEDED(SetDescription(ConfigManager::GetCurrentServiceName(),
-                                       description)));
-      return hr;
-    }
-
-    // Delete any previous versions of the service. Then create a new service
-    // name, and fall through to install that.
-    VERIFY1(SUCCEEDED(DeleteServices()));
-    VERIFY1(SUCCEEDED(
-        ConfigManager::CreateAndSetVersionedServiceNameInRegistry()));
-    ASSERT1(!goopdate_utils::IsServiceInstalled());
-  }
-
-  return DoInstallNewService(ConfigManager::GetCurrentServiceName(),
-                             ConfigManager::GetCurrentServiceDisplayName(),
-                             service_cmd_line,
-                             description);
-}
-
-HRESULT SetupService::DoInstallNewService(const TCHAR* service_name,
-                                          const TCHAR* service_display_name,
-                                          const TCHAR* service_cmd_line,
-                                          const TCHAR* description) {
-  ASSERT1(service_name);
-  ASSERT1(service_display_name);
-  ASSERT1(service_cmd_line);
-  ASSERT1(description);
-  if (!*service_name) {
-    return GOOPDATE_E_SERVICE_NAME_EMPTY;
-  }
-
-  scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
-  if (!scm) {
-    HRESULT hr = HRESULTFromLastError();
-    ASSERT(false, (_T("Failed to open SC Manager 0x%08x"), hr));
-    return hr;
-  }
-
-  scoped_service service(::CreateService(
-                               get(scm),
-                               service_name,
-                               service_display_name,
-                               SERVICE_ALL_ACCESS,
-                               SERVICE_WIN32_OWN_PROCESS,
-                               SERVICE_AUTO_START,
-                               SERVICE_ERROR_NORMAL,
-                               service_cmd_line,
-                               NULL,
-                               NULL,
-                               _T("RPCSS\0"),
-                               NULL,
-                               NULL));
-  if (!service) {
-    HRESULT hr = HRESULTFromLastError();
-    SETUP_LOG(LEVEL_ERROR, (_T("[CreateService failed][0x%08x]"), hr));
-    return hr;
-  }
-  VERIFY1(SUCCEEDED(SetDescription(service_name, description)));
-  SETUP_LOG(L1, (_T("[SetupService::DoInstallService][service installed]")));
-  return S_OK;
-}
-
-// Uninstalls the service by:
-// 1. unregistering it
-// 2. deleting it from SCM if needed.
-HRESULT SetupService::UninstallService() {
-  SETUP_LOG(L3, (_T("[SetupService::UninstallService]")));
-
-  HRESULT hr = StopService();
-  if (FAILED(hr)) {
-    SETUP_LOG(LW, (_T("[SetupService::StopService failed][0x%08x]"), hr));
-    ASSERT1(HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) == hr);
-  }
-
-  VERIFY1(SUCCEEDED(ServiceModule().UnregisterCOMService()));
-
-  hr = DeleteServices();
-  if (FAILED(hr)) {
-    OPT_LOG(LEVEL_ERROR, (_T("[Can't delete the service][0x%08x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT SetupService::UpgradeService(const TCHAR* service_cmd_line) {
-  ASSERT1(service_cmd_line);
-  ASSERT1(goopdate_utils::IsServiceInstalled());
-  if (!goopdate_utils::IsServiceInstalled()) {
-    return E_UNEXPECTED;
-  }
-
-  // Modify the configuration of the existing service.
-  HRESULT hr(StopService());
-  if (FAILED(hr)) {
-    ASSERT1(false);
-    SETUP_LOG(LEVEL_ERROR, (_T("[Can't stop the service][0x%08x]"), hr));
-    return hr;
-  }
-
-  scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
-  if (!scm) {
-    hr = HRESULTFromLastError();
-    ASSERT(false, (_T("Failed to open SC Manager 0x%08x"), hr));
-    return hr;
-  }
-
-  CString service_name(ConfigManager::GetCurrentServiceName());
-  if (service_name.IsEmpty()) {
-    return GOOPDATE_E_SERVICE_NAME_EMPTY;
-  }
-  scoped_service service(::OpenService(get(scm),
-                                       service_name,
-                                       SERVICE_CHANGE_CONFIG));
-  if (!service) {
-    hr = HRESULTFromLastError();
-    ASSERT(false, (_T("Failed to open service for update 0x%08x"), hr));
-    return hr;
-  }
-
-  if (!::ChangeServiceConfig(get(service),
-                             SERVICE_WIN32_OWN_PROCESS,
-                             SERVICE_AUTO_START,
-                             SERVICE_ERROR_NORMAL,
-                             service_cmd_line,
-                             NULL,
-                             NULL,
-                             NULL,
-                             NULL,
-                             NULL,
-                             NULL)) {
-    hr = HRESULTFromLastError();
-    ASSERT(false, (_T("Failed to change service config 0x%08x"), hr));
-    return hr;
-  }
-
-  SETUP_LOG(L3, (_T("[ChangeServiceConfig succeeded]")));
-  return S_OK;
-}
-
-HRESULT SetupService::DeleteServices() {
-  return ServiceInstall::UninstallServices(kServicePrefix, NULL);
-}
-
-HRESULT SetupService::SetDescription(const TCHAR* name,
-                                     const TCHAR* description) {
-  ASSERT1(name);
-  ASSERT1(description);
-  scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
-  if (!scm) {
-    return HRESULTFromLastError();
-  }
-
-  // Opening the service with less rights fails the ChangeServiceConfig2 call
-  // with E_ACCESSDENIED.
-  scoped_service service(::OpenService(get(scm), name, SERVICE_ALL_ACCESS));
-  if (!service) {
-    return HRESULTFromLastError();
-  }
-  SERVICE_DESCRIPTION info = { const_cast<TCHAR*>(description) };
-  if (!::ChangeServiceConfig2(get(service),
-                              SERVICE_CONFIG_DESCRIPTION,
-                              &info)) {
-    return HRESULTFromLastError();
-  }
-  SETUP_LOG(L3, (_T("[service description changed successfully]")));
-  return S_OK;
-}
-
-}  // namespace omaha
diff --git a/setup/setup_service.h b/setup/setup_service.h
index b2c0b68..d44fc22 100644
--- a/setup/setup_service.h
+++ b/setup/setup_service.h
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -15,47 +15,516 @@
 //
 // Sets up and controls the Google Update service.
 
-#ifndef OMAHA_SETUP_SETUP_SERVICE_H__
-#define OMAHA_SETUP_SETUP_SERVICE_H__
+// TODO(omaha3): Consolidate all the service related code into one file in base
+// and one file in service.
+
+#ifndef OMAHA_SETUP_SETUP_SERVICE_H_
+#define OMAHA_SETUP_SETUP_SERVICE_H_
 
 #include <windows.h>
 #include "base/basictypes.h"
-#include "omaha/common/constants.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/path.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/service_utils.h"
+#include "omaha/base/system_info.h"
+#include "omaha/client/resource.h"
+#include "omaha/common/command_line_builder.h"
+#include "omaha/common/const_cmd_line.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/service/service_main.h"
 
 namespace omaha {
 
+const uint32 kMaxQueryConfigBufferBytes = 8 * 1024;
+
+template <typename T>
 class SetupService {
  public:
-  // Controls the service.
-  static HRESULT StartService();
-  static HRESULT StopService();
+  static HRESULT StartService() {
+    OPT_LOG(L1, (_T("[StartService]")));
 
-  // Installs and uninstalls the service.
-  static HRESULT InstallService(const TCHAR* file_path);
-  static HRESULT UninstallService();
+    scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
+    if (!scm) {
+      return HRESULTFromLastError();
+    }
+
+    CString service_name(T::GetCurrentServiceName());
+    scoped_service service(::OpenService(get(scm),
+                                         service_name,
+                                         SERVICE_QUERY_STATUS | SERVICE_START));
+    if (!service) {
+      return HRESULTFromLastError();
+    }
+
+    SERVICE_STATUS status = {0};
+    if (::QueryServiceStatus(get(service), &status)) {
+      if (status.dwCurrentState == SERVICE_RUNNING ||
+          status.dwCurrentState == SERVICE_START_PENDING) {
+        SETUP_LOG(L1, (_T("[QueryServiceStatus][Service already running][%u]"),
+                       status.dwCurrentState));
+        return S_OK;
+      }
+    }
+
+    // Start the service.
+    if (::StartService(get(service), 0, NULL)) {
+      SETUP_LOG(L1, (_T("[StartService][started]")));
+      return S_OK;
+    }
+
+    HRESULT hr = HRESULTFromLastError();
+    ASSERT1(hr != HRESULT_FROM_WIN32(ERROR_SERVICE_ALREADY_RUNNING));
+    if (hr == HRESULT_FROM_WIN32(ERROR_SERVICE_ALREADY_RUNNING)) {
+      SETUP_LOG(L1, (_T("[StartService][ERROR_SERVICE_ALREADY_RUNNING]")));
+      return S_OK;
+    }
+
+    return hr;
+  }
+
+  static HRESULT StopService() {
+    SETUP_LOG(L1, (_T("[StopService]")));
+
+    CString service_name(T::GetCurrentServiceName());
+    return ServiceInstall::StopService(service_name);
+  }
+
+  static HRESULT InstallService(const TCHAR* file_path) {
+    ASSERT1(file_path);
+
+    // Append the arguments to be passed to the service entry point.
+
+    CString service_cmd_line(file_path);
+    CommandLineBuilder builder(T::commandline_mode());
+    SafeCStringAppendFormat(&service_cmd_line, _T(" %s"),
+                            builder.GetCommandLineArgs());
+
+    SETUP_LOG(L2, (_T("[service command line][%s]"), service_cmd_line));
+
+    HRESULT hr = DoInstallService(service_cmd_line);
+    if (FAILED(hr)) {
+      SETUP_LOG(LE, (_T("[DoInstallService failed][0x%08x]"), hr));
+      return hr;
+    }
+
+    VERIFY1(SUCCEEDED(SetDescription(GetServiceDescription())));
+    VERIFY1(SUCCEEDED(SetDelayedAutoStart()));
+
+    return InstallCOMService();
+  }
+
+  // Uninstalls the service by:
+  // 1. unregistering it
+  // 2. deleting it from SCM if needed.
+  static HRESULT UninstallService() {
+    SETUP_LOG(L3, (_T("[UninstallService][%s]"), T::GetCurrentServiceName()));
+
+    HRESULT hr = StopService();
+    if (FAILED(hr)) {
+      SETUP_LOG(LW, (_T("[StopService failed][0x%08x]"), hr));
+      ASSERT1(HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) == hr);
+    }
+
+    VERIFY1(SUCCEEDED(UninstallCOMService()));
+
+    hr = DeleteService();
+    if (FAILED(hr)) {
+      OPT_LOG(LEVEL_ERROR, (_T("[Can't delete the service][0x%08x]"), hr));
+      return hr;
+    }
+
+    return S_OK;
+  }
+
+  static bool IsServiceInstalled() {
+    return ServiceInstall::IsServiceInstalled(T::GetCurrentServiceName());
+  }
 
  private:
-  // COM registration for the GoogleUpdateCoreClass.
-  static HRESULT InstallCOMService();
+  static HRESULT InstallCOMService() {
+    SETUP_LOG(L1, (_T("[InstallCOMService]")));
 
-  static HRESULT DoInstallService(const TCHAR* service_cmd_line,
-                                  const TCHAR* desc);
+    HRESULT hr = ServiceModule<T>().RegisterCOMService();
 
-  static HRESULT DoInstallNewService(const TCHAR* service_name,
-                                     const TCHAR* service_display_name,
-                                     const TCHAR* service_cmd_line,
-                                     const TCHAR* description);
+    // We reset the _pAtlModule to allow for the case where multiple instances
+    // of ServiceModule are installed serially.
+    _pAtlModule = NULL;
 
-  // Upgrades the service.
-  static HRESULT UpgradeService(const TCHAR* service_cmd_line);
+    return hr;
+  }
 
-  // Deletes all services prefixed with "gupdate" from the SCM.
-  static HRESULT DeleteServices();
+  static HRESULT UninstallCOMService() {
+    SETUP_LOG(L1, (_T("[UninstallCOMService]")));
 
-  // Sets the service description. If the description is an empty string, the
-  // current description is deleted.
-  static HRESULT SetDescription(const TCHAR* name,
-                                const TCHAR* description);
+    HRESULT hr = ServiceModule<T>().UnregisterCOMService();
+
+    // We reset the _pAtlModule to allow for the case where multiple instances
+    // of ServiceModule are uninstalled serially.
+    _pAtlModule = NULL;
+
+    return hr;
+  }
+
+  static HRESULT DoInstallService(const TCHAR* service_cmd_line) {
+    SETUP_LOG(L1, (_T("[DoInstallService][%s]"), service_cmd_line));
+
+    ASSERT1(service_cmd_line);
+
+    if (IsServiceInstalled()) {
+      // Lightweight upgrade of existing service.
+      HRESULT hr = UpgradeService(service_cmd_line);
+      ASSERT(SUCCEEDED(hr), (_T("[UpgradeService failed][0x%x]"), hr));
+      if (SUCCEEDED(hr)) {
+        return hr;
+      }
+
+      // Delete the previous version of the service. Then create a new service
+      // name, and fall through to install that.
+      VERIFY1(SUCCEEDED(DeleteService()));
+      VERIFY1(SUCCEEDED(CreateAndSetVersionedServiceNameInRegistry()));
+      ASSERT1(!IsServiceInstalled());
+    }
+
+    return DoInstallNewService(service_cmd_line);
+  }
+
+  static HRESULT DoInstallNewService(const TCHAR* service_cmd_line) {
+    ASSERT1(service_cmd_line);
+
+    scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
+    if (!scm) {
+      const DWORD error = ::GetLastError();
+      SETUP_LOG(LE, (_T("[Failed to open SC Manager][%u]"), error));
+      return HRESULT_FROM_WIN32(error);
+    }
+
+    CString service_name(T::GetCurrentServiceName());
+    CString service_display_name(GetCurrentServiceDisplayName());
+    scoped_service service(::CreateService(get(scm),
+                                           service_name,
+                                           service_display_name,
+                                           SERVICE_ALL_ACCESS,
+                                           SERVICE_WIN32_OWN_PROCESS,
+                                           T::service_start_type(),
+                                           SERVICE_ERROR_NORMAL,
+                                           service_cmd_line,
+                                           NULL,
+                                           NULL,
+                                           _T("RPCSS\0"),
+                                           NULL,
+                                           NULL));
+    if (!service) {
+      const DWORD error = ::GetLastError();
+      SETUP_LOG(LE, (_T("[CreateService failed][%u]"), error));
+      return HRESULT_FROM_WIN32(error);
+    }
+    SETUP_LOG(L1, (_T("[DoInstallNewService][service installed]")));
+    return S_OK;
+  }
+
+  static bool IsServiceCorrectlyConfigured(const TCHAR* service_cmd_line) {
+    scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT));
+    if (!scm) {
+      HRESULT hr = HRESULTFromLastError();
+      SETUP_LOG(LE, (_T("[Failed to open SC Manager][0x%x]"), hr));
+      return false;
+    }
+
+    CString service_name(T::GetCurrentServiceName());
+    scoped_service service(::OpenService(get(scm),
+                                         service_name,
+                                         SERVICE_QUERY_CONFIG));
+    if (!service) {
+      HRESULT hr = HRESULTFromLastError();
+      SETUP_LOG(LE, (_T("[OpenService SERVICE_QUERY_CONFIG fail][0x%x]"), hr));
+      return false;
+    }
+
+    // ::QueryServiceConfig expects a buffer of at most 8K bytes, according to
+    // documentation. While the size of the buffer can be dynamically computed,
+    // we just assume the maximum size for simplicity.
+    uint8 buffer[kMaxQueryConfigBufferBytes] = { 0 };
+    DWORD bytes_needed_ignored = 0;
+    QUERY_SERVICE_CONFIG* service_config =
+      reinterpret_cast<QUERY_SERVICE_CONFIG*>(buffer);
+    if (!::QueryServiceConfig(get(service),
+                              service_config,
+                              sizeof(buffer),
+                              &bytes_needed_ignored)) {
+      HRESULT hr = HRESULTFromLastError();
+      SETUP_LOG(LE, (_T("[QueryServiceConfig failed][0x%x]"), hr));
+      return false;
+    }
+
+    bool does_service_cmd_line_match = false;
+    if (service_config->lpBinaryPathName == NULL || service_cmd_line == NULL) {
+      does_service_cmd_line_match =
+          (service_config->lpBinaryPathName == service_cmd_line);
+    } else {
+      does_service_cmd_line_match =
+          !_tcsicmp(service_config->lpBinaryPathName, service_cmd_line);
+    }
+    return service_config->dwServiceType == SERVICE_WIN32_OWN_PROCESS &&
+           service_config->dwStartType == T::service_start_type() &&
+           service_config->dwErrorControl == SERVICE_ERROR_NORMAL &&
+           does_service_cmd_line_match;
+  }
+
+  static HRESULT UpgradeService(const TCHAR* service_cmd_line) {
+    ASSERT1(service_cmd_line);
+    ASSERT1(IsServiceInstalled());
+
+    if (IsServiceCorrectlyConfigured(service_cmd_line)) {
+      return S_OK;
+    }
+
+    // Modify the configuration of the existing service.
+    HRESULT hr(StopService());
+    if (FAILED(hr)) {
+      SETUP_LOG(LE, (_T("[Can't stop the service][0x%08x]"), hr));
+      return hr;
+    }
+
+    scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
+    if (!scm) {
+      const DWORD error = ::GetLastError();
+      SETUP_LOG(LE, (_T("[Failed to open SC Manager][%u]"), error));
+      return HRESULT_FROM_WIN32(error);
+    }
+
+    CString service_name(T::GetCurrentServiceName());
+    scoped_service service(::OpenService(get(scm),
+                                         service_name,
+                                         SERVICE_CHANGE_CONFIG));
+    if (!service) {
+      const DWORD error = ::GetLastError();
+      SETUP_LOG(LE, (_T("[Failed to open service for update][%u]"), error));
+      return HRESULT_FROM_WIN32(error);
+    }
+
+    if (!::ChangeServiceConfig(get(service),
+                               SERVICE_WIN32_OWN_PROCESS,
+                               T::service_start_type(),
+                               SERVICE_ERROR_NORMAL,
+                               service_cmd_line,
+                               NULL,
+                               NULL,
+                               NULL,
+                               NULL,
+                               NULL,
+                               NULL)) {
+      const DWORD error = ::GetLastError();
+      SETUP_LOG(LE, (_T("[Failed to change service config][%u]"), error));
+      return HRESULT_FROM_WIN32(error);
+    }
+
+    SETUP_LOG(L3, (_T("[ChangeServiceConfig succeeded]")));
+    return S_OK;
+  }
+
+  static bool IsDelayedAutoStart() {
+    scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT));
+    if (!scm) {
+      HRESULT hr = HRESULTFromLastError();
+      SETUP_LOG(LE, (_T("[SC_MANAGER_CONNECT failed][0x%x]"), hr));
+      return false;
+    }
+
+    CString name(T::GetCurrentServiceName());
+    scoped_service service(::OpenService(get(scm), name, SERVICE_QUERY_CONFIG));
+    if (!service) {
+      HRESULT hr = HRESULTFromLastError();
+      SETUP_LOG(LE, (_T("[SERVICE_QUERY_CONFIG failed][%s][0x%x]"), name, hr));
+      return false;
+    }
+
+    uint8 buffer[kMaxQueryConfigBufferBytes] = { 0 };
+    DWORD bytes_needed_ignored = 0;
+    SERVICE_DELAYED_AUTO_START_INFO* service_config =
+      reinterpret_cast<SERVICE_DELAYED_AUTO_START_INFO*>(buffer);
+    if (!::QueryServiceConfig2(get(service),
+                               SERVICE_CONFIG_DELAYED_AUTO_START_INFO,
+                               buffer,
+                               sizeof(buffer),
+                               &bytes_needed_ignored)) {
+      HRESULT hr = HRESULTFromLastError();
+      SETUP_LOG(LE, (_T("[Query SERVICE_CONFIG_DELAYED_AUTO_START_INFO failed]")
+                     _T("[0x%x]"), hr));
+      return false;
+    }
+
+    return !!service_config->fDelayedAutostart;
+  }
+
+  static HRESULT SetDelayedAutoStart() {
+    if (!SystemInfo::IsRunningOnVistaOrLater()) {
+      return S_OK;
+    }
+
+    ASSERT1(IsServiceInstalled());
+
+    if (IsDelayedAutoStart()) {
+      return S_OK;
+    }
+
+    scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
+    if (!scm) {
+      HRESULT hr = HRESULTFromLastError();
+      SETUP_LOG(LE, (_T("[OpenSCManager failed][0x%x]"), hr));
+      return hr;
+    }
+
+    CString service_name(T::GetCurrentServiceName());
+    scoped_service service(::OpenService(get(scm),
+                                         service_name,
+                                         SERVICE_CHANGE_CONFIG));
+    if (!service) {
+      HRESULT hr = HRESULTFromLastError();
+      SETUP_LOG(LE, (_T("[OpenService failed][0x%x]"), hr));
+      return hr;
+    }
+
+    SERVICE_DELAYED_AUTO_START_INFO auto_start_info = {TRUE};
+    if (!::ChangeServiceConfig2(get(service),
+                                SERVICE_CONFIG_DELAYED_AUTO_START_INFO,
+                                &auto_start_info)) {
+      HRESULT hr = HRESULTFromLastError();
+      SETUP_LOG(LE, (_T("[ChangeServiceConfig2 failed][0x%x]"), hr));
+      return hr;
+    }
+
+    SETUP_LOG(L3, (_T("[SetDelayedAutoStart succeeded]")));
+    return S_OK;
+  }
+
+  static HRESULT DeleteService() {
+    SETUP_LOG(L3, (_T("[DeleteService][%s]"), T::GetCurrentServiceName()));
+
+    scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
+    if (!scm) {
+      return HRESULTFromLastError();
+    }
+    scoped_service service(::OpenService(get(scm),
+                                         T::GetCurrentServiceName(),
+                                         SERVICE_CHANGE_CONFIG | DELETE));
+    if (!service) {
+      return HRESULTFromLastError();
+    }
+
+    if (!::DeleteService(get(service))) {
+      HRESULT hr = HRESULTFromLastError();
+      SETUP_LOG(LW, (_T("[DeleteService failed][0x%x]"), hr));
+      if (hr != HRESULT_FROM_WIN32(ERROR_SERVICE_MARKED_FOR_DELETE)) {
+        return hr;
+      }
+    }
+
+    return S_OK;
+  }
+
+  static bool DoesDescriptionMatch(const TCHAR* description) {
+    ASSERT1(description);
+
+    scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT));
+    if (!scm) {
+      HRESULT hr = HRESULTFromLastError();
+      SETUP_LOG(LE, (_T("[SC_MANAGER_CONNECT failed][0x%x]"), hr));
+      return false;
+    }
+
+    CString name(T::GetCurrentServiceName());
+    scoped_service service(::OpenService(get(scm), name, SERVICE_QUERY_CONFIG));
+    if (!service) {
+      HRESULT hr = HRESULTFromLastError();
+      SETUP_LOG(LE, (_T("[SERVICE_QUERY_CONFIG failed][%s][0x%x]"), name, hr));
+      return false;
+    }
+
+    uint8 buffer[kMaxQueryConfigBufferBytes] = { 0 };
+    DWORD bytes_needed_ignored = 0;
+    SERVICE_DESCRIPTION* service_config =
+      reinterpret_cast<SERVICE_DESCRIPTION*>(buffer);
+    if (!::QueryServiceConfig2(get(service),
+                               SERVICE_CONFIG_DESCRIPTION,
+                               buffer,
+                               sizeof(buffer),
+                               &bytes_needed_ignored)) {
+      HRESULT hr = HRESULTFromLastError();
+      SETUP_LOG(LE, (_T("[QuerySERVICE_CONFIG_DESCRIPTION failed][0x%x]"), hr));
+      return false;
+    }
+
+    if (service_config->lpDescription == NULL || description == NULL) {
+      return (service_config->lpDescription == description);
+    }
+
+    return !_tcsicmp(service_config->lpDescription, description);
+  }
+
+  static HRESULT SetDescription(const TCHAR* description) {
+    ASSERT1(description);
+
+    if (DoesDescriptionMatch(description)) {
+      return S_OK;
+    }
+
+    scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
+    if (!scm) {
+      return HRESULTFromLastError();
+    }
+
+    CString name(T::GetCurrentServiceName());
+
+    // Opening the service with less rights fails the ChangeServiceConfig2 call
+    // with E_ACCESSDENIED.
+    scoped_service service(::OpenService(get(scm), name, SERVICE_ALL_ACCESS));
+    if (!service) {
+      return HRESULTFromLastError();
+    }
+    SERVICE_DESCRIPTION info = { const_cast<TCHAR*>(description) };
+    if (!::ChangeServiceConfig2(get(service),
+                                SERVICE_CONFIG_DESCRIPTION,
+                                &info)) {
+      return HRESULTFromLastError();
+    }
+    SETUP_LOG(L3, (_T("[service description changed successfully]")));
+    return S_OK;
+  }
+
+  static CString GetCurrentServiceDisplayName() {
+    CORE_LOG(L3, (_T("[GetCurrentServiceDisplayName]")));
+
+    CString product_name;
+    VERIFY1(product_name.LoadString(IDS_PRODUCT_DISPLAY_NAME));
+
+    CString display_name;
+    display_name.FormatMessage(IDS_SERVICE_DISPLAY_NAME, product_name);
+    display_name.AppendFormat(_T(" (%s)"), T::GetCurrentServiceName());
+    return display_name;
+  }
+
+  static CString GetServiceDescription() {
+    // TODO(omaha3): Do we need a different service description for the medium
+    // service?
+
+    CString company_name;
+    VERIFY1(company_name.LoadString(IDS_FRIENDLY_COMPANY_NAME));
+
+    CString service_description;
+    service_description.FormatMessage(IDS_SERVICE_DESCRIPTION, company_name);
+    return service_description;
+  }
+
+  static HRESULT CreateAndSetVersionedServiceNameInRegistry() {
+    CORE_LOG(L3, (_T("CreateAndSetVersionedServiceNameInRegistry")));
+    return goopdate_utils::CreateAndSetVersionedNameInRegistry(true,
+        T::default_name(), T::reg_name());
+  }
 
   friend class SetupServiceTest;
   friend class CoreUtilsTest;
@@ -63,7 +532,9 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(SetupService);
 };
 
+typedef SetupService<Update3ServiceMode> SetupUpdate3Service;
+typedef SetupService<UpdateMediumServiceMode> SetupUpdateMediumService;
+
 }  // namespace omaha
 
-#endif  // OMAHA_SETUP_SETUP_SERVICE_H__
-
+#endif  // OMAHA_SETUP_SETUP_SERVICE_H_
diff --git a/setup/setup_service_unittest.cc b/setup/setup_service_unittest.cc
index 5ac3572..4aa0dbc 100644
--- a/setup/setup_service_unittest.cc
+++ b/setup/setup_service_unittest.cc
@@ -13,13 +13,65 @@
 // limitations under the License.
 // ========================================================================
 
-#include "omaha/testing/unit_test.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/path.h"
+#include "omaha/base/vistautil.h"
 #include "omaha/setup/setup_service.h"
+#include "omaha/testing/unit_test.h"
 
 namespace omaha {
 
+class SetupServiceTest : public testing::Test {
+ protected:
+  static void SetUpTestCase() {
+    // The ServiceModule has it's own ATL module. ATL does not like having
+    // multiple ATL modules. This TestCase saves and restore the original ATL
+    // module to get around ATL's limitation. This is a hack.
+    original_atl_module_ = _pAtlModule;
+    _pAtlModule = NULL;
+  }
+
+  static void TearDownTestCase() {
+    _pAtlModule = original_atl_module_;
+  }
+
+  static HRESULT DeleteUpdate3Service() {
+    return SetupUpdate3Service::DeleteService();
+  }
+
+  static HRESULT DeleteMediumService() {
+    return SetupUpdateMediumService::DeleteService();
+  }
+
+  static CAtlModule* original_atl_module_;
+};
+
+CAtlModule* SetupServiceTest::original_atl_module_ = NULL;
+
 // TODO(omaha): Test SetupServiceTest
-TEST(SetupServiceTest, SetupServiceTest) {
+
+TEST_F(SetupServiceTest, InstallService_FileDoesNotExist) {
+  if (!vista_util::IsUserAdmin()) {
+    std::wcout << _T("\tTest did not run because the user is not an admin.")
+               << std::endl;
+    return;
+  }
+
+  DeleteUpdate3Service();
+  DeleteMediumService();
+
+  CString service_path = ConcatenatePath(app_util::GetCurrentModuleDirectory(),
+                                         _T("NoSuchFile.exe"));
+
+  // The Windows service registration APIs do not rely on the file existing.
+  EXPECT_SUCCEEDED(
+      SetupUpdate3Service::InstallService(service_path));
+
+  EXPECT_SUCCEEDED(
+      SetupUpdateMediumService::InstallService(service_path));
+
+  EXPECT_SUCCEEDED(DeleteUpdate3Service());
+  EXPECT_SUCCEEDED(DeleteMediumService());
 }
 
 }  // namespace omaha
diff --git a/setup/setup_unittest.cc b/setup/setup_unittest.cc
index 1a9de3b..412ae78 100644
--- a/setup/setup_unittest.cc
+++ b/setup/setup_unittest.cc
@@ -17,24 +17,24 @@
 #include <vector>
 #include "base/basictypes.h"
 #include "base/scoped_ptr.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/path.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/process.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/common/system.h"
-#include "omaha/common/thread.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/error.h"
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/process.h"
+#include "omaha/base/scope_guard.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/system.h"
+#include "omaha/base/thread.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/command_line.h"
+#include "omaha/common/config_manager.h"
+#include "omaha/common/const_goopdate.h"
 #include "omaha/setup/setup.h"
 #include "omaha/setup/setup_files.h"
 #include "omaha/testing/unit_test.h"
@@ -48,24 +48,24 @@
 const TCHAR* const kFutureVersionString = _T("9.8.7.6");
 
 const TCHAR* const kAppMachineClientsPath =
-    _T("HKLM\\Software\\Google\\Update\\Clients\\")
-    _T("{50DA5C89-FF97-4536-BF3F-DF54C2F02EA8}\\");
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\Clients\\{50DA5C89-FF97-4536-BF3F-DF54C2F02EA8}\\");
 const TCHAR* const kAppMachineClientStatePath =
-    _T("HKLM\\Software\\Google\\Update\\ClientState\\")
-    _T("{50DA5C89-FF97-4536-BF3F-DF54C2F02EA8}\\");
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\{50DA5C89-FF97-4536-BF3F-DF54C2F02EA8}\\");
 const TCHAR* const kApp2MachineClientsPath =
-    _T("HKLM\\Software\\Google\\Update\\Clients\\")
-    _T("{CB8E8A3C-7295-4529-B083-D5F76DCD4CC2}\\");
+    _T("HKLM\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\Clients\\{CB8E8A3C-7295-4529-B083-D5F76DCD4CC2}\\");
 
 const TCHAR* const kAppUserClientsPath =
-    _T("HKCU\\Software\\Google\\Update\\Clients\\")
-    _T("{50DA5C89-FF97-4536-BF3F-DF54C2F02EA8}\\");
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\Clients\\{50DA5C89-FF97-4536-BF3F-DF54C2F02EA8}\\");
 const TCHAR* const kAppUserClientStatePath =
-    _T("HKCU\\Software\\Google\\Update\\ClientState\\")
-    _T("{50DA5C89-FF97-4536-BF3F-DF54C2F02EA8}\\");
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\ClientState\\{50DA5C89-FF97-4536-BF3F-DF54C2F02EA8}\\");
 const TCHAR* const kApp2UserClientsPath =
-    _T("HKCU\\Software\\Google\\Update\\Clients\\")
-    _T("{CB8E8A3C-7295-4529-B083-D5F76DCD4CC2}\\");
+    _T("HKCU\\Software\\") SHORT_COMPANY_NAME _T("\\") PRODUCT_NAME
+    _T("\\Clients\\{CB8E8A3C-7295-4529-B083-D5F76DCD4CC2}\\");
 
 
 class HoldLock : public Runnable {
@@ -106,22 +106,6 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(HoldLock);
 };
 
-class SetupFilesMockFailInstall : public SetupFiles {
- public:
-  explicit SetupFilesMockFailInstall(bool is_machine)
-      : SetupFiles(is_machine) {
-  }
-
-  virtual HRESULT Install() {
-    return kExpetedError;
-  }
-
-  static const HRESULT kExpetedError = 0x86427531;
-
- private:
-  DISALLOW_EVIL_CONSTRUCTORS(SetupFilesMockFailInstall);
-};
-
 }  // namespace
 
 void CopyGoopdateFiles(const CString& omaha_path, const CString& version);
@@ -131,29 +115,51 @@
 
   typedef std::vector<uint32> Pids;
 
+  // Returns the path to the long-running GoogleUpdate.exe.
+  static CString CopyGoopdateAndLongRunningFiles(const CString& omaha_path,
+                                                 const CString& version) {
+    CopyGoopdateFiles(omaha_path, version);
+
+    CString long_running_target_path = ConcatenatePath(omaha_path,
+                                                       _T("does_not_shutdown"));
+    EXPECT_SUCCEEDED(CreateDir(long_running_target_path, NULL));
+    long_running_target_path = ConcatenatePath(long_running_target_path,
+                                               kOmahaShellFileName);
+    EXPECT_SUCCEEDED(File::Copy(
+        ConcatenatePath(ConcatenatePath(
+                            app_util::GetCurrentModuleDirectory(),
+                            _T("unittest_support\\does_not_shutdown")),
+                        kOmahaShellFileName),
+        long_running_target_path,
+        false));
+
+    return long_running_target_path;
+  }
+
   static void SetUpTestCase() {
-    CString exe_parent_dir = ConcatenatePath(
-                                 app_util::GetCurrentModuleDirectory(),
-                                 _T("unittest_support\\"));
-    omaha_exe_path_ = ConcatenatePath(exe_parent_dir,
-                                      _T("omaha_1.2.x\\GoogleUpdate.exe"));
-    omaha_10_exe_path_ = ConcatenatePath(exe_parent_dir,
-                                         _T("omaha_1.0.x\\GoogleUpdate.exe"));
-    omaha_11_exe_path_ = ConcatenatePath(exe_parent_dir,
-                                         _T("omaha_1.1.x\\GoogleUpdate.exe"));
-    not_listening_exe_path_ = ConcatenatePath(
-                                  exe_parent_dir,
-                                  _T("does_not_shutdown\\GoogleUpdate.exe"));
+    not_listening_machine_exe_path_ =
+        CopyGoopdateAndLongRunningFiles(GetGoogleUpdateMachinePath(),
+                                        GetVersionString());
+    not_listening_user_exe_path_ =
+        CopyGoopdateAndLongRunningFiles(GetGoogleUpdateUserPath(),
+                                        GetVersionString());
   }
 
   explicit SetupTest(bool is_machine)
       : is_machine_(is_machine),
-        omaha_path_(GetGoogleUpdateUserPath()) {
+        omaha_path_(is_machine ? GetGoogleUpdateMachinePath() :
+                                 GetGoogleUpdateUserPath()),
+        not_listening_exe_path_(is_machine ? not_listening_machine_exe_path_ :
+                                             not_listening_user_exe_path_),
+        not_listening_exe_opposite_path_(!is_machine ?
+                                         not_listening_machine_exe_path_ :
+                                         not_listening_user_exe_path_) {
+    omaha_exe_path_ = ConcatenatePath(omaha_path_, _T("GoogleUpdate.exe"));
   }
 
-  void SetUp() {
+  virtual void SetUp() {
     ASSERT_SUCCEEDED(CreateDir(omaha_path_, NULL));
-    setup_.reset(new omaha::Setup(is_machine_, &args_));
+    setup_.reset(new omaha::Setup(is_machine_));
   }
 
   bool ShouldInstall() {
@@ -166,46 +172,10 @@
     return setup_->StopGoogleUpdateAndWait();
   }
 
-  HRESULT LaunchInstalledWorker(bool do_setup_phase_2, HANDLE* process) {
-    return setup_->LaunchInstalledWorker(do_setup_phase_2, process);
-  }
-
   HRESULT TerminateCoreProcesses() const {
     return setup_->TerminateCoreProcesses();
   }
 
-  bool InitLegacySetupLocks(GLock* lock10,
-                            GLock* lock11_user,
-                            GLock* lock11_machine) {
-    return setup_->InitLegacySetupLocks(lock10, lock11_user, lock11_machine);
-  }
-
-  void PersistUpdateErrorInfo(bool is_machine,
-                              HRESULT error,
-                              int extra_code1,
-                              const CString& version) {
-    setup_->PersistUpdateErrorInfo(is_machine, error, extra_code1, version);
-  }
-
-  static bool HasXmlParser() { return Setup::HasXmlParser(); }
-
-  bool launched_offline_worker() { return setup_->launched_offline_worker_; }
-
-  void SetModeInstall() { setup_->mode_ = Setup::MODE_INSTALL; }
-  void SetModeSelfInstall() { setup_->mode_ = Setup::MODE_SELF_INSTALL; }
-  void SetModeSelfUpdate() { setup_->mode_ = Setup::MODE_SELF_UPDATE; }
-
-  // Uses DoInstall() instead of Install() as necessary to avoid the elevation
-  // check in Install().
-  HRESULT TestInstall() {
-    if (!is_machine_ || vista_util::IsUserAdmin()) {
-      return setup_->Install(_T("foo"));
-    } else {
-      setup_->mode_ = Setup::MODE_INSTALL;
-      return setup_->DoInstall();
-    }
-  }
-
   // Acquires the Setup Lock in another thread then calls TestInstall().
   void TestInstallWhileHoldingLock() {
     HoldLock hold_lock(is_machine_);
@@ -214,34 +184,12 @@
     thread.Start(&hold_lock);
     hold_lock.WaitForLockToBeAcquired();
 
-    EXPECT_EQ(GOOPDATE_E_FAILED_TO_GET_LOCK, TestInstall());
+    EXPECT_EQ(GOOPDATE_E_FAILED_TO_GET_LOCK, setup_->Install(false));
 
     hold_lock.Stop();
     thread.WaitTillExit(1000);
   }
 
-  // Acquires the Setup Lock in another thread then calls DoInstall().
-  // Useful for testing modes other than MODE_INSTALL.
-  void TestDoInstallWhileHoldingLock() {
-    HoldLock hold_lock(is_machine_);
-
-    Thread thread;
-    thread.Start(&hold_lock);
-    hold_lock.WaitForLockToBeAcquired();
-
-    EXPECT_EQ(GOOPDATE_E_FAILED_TO_GET_LOCK, setup_->DoInstall());
-
-    hold_lock.Stop();
-    thread.WaitTillExit(1000);
-  }
-
-  void TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles() {
-    SetupFilesMockFailInstall setup_files_mock(is_machine_);
-
-    EXPECT_EQ(SetupFilesMockFailInstall::kExpetedError,
-              setup_->DoProtectedGoogleUpdateInstall(&setup_files_mock));
-  }
-
   void StopGoogleUpdateAndWaitSucceedsTestHelper(bool use_job_objects_only) {
     if (is_machine_ && !vista_util::IsUserAdmin()) {
       std::wcout << _T("\tTest did not run because the user is not an admin.")
@@ -249,19 +197,21 @@
       return;
     }
 
-    // This test has been the most problematic, so do not run on build systems.
-    if (!ShouldRunLargeTest() || IsBuildSystem()) {
+    if (!ShouldRunLargeTest()) {
+      return;
+    }
+    if (IsBuildSystem()) {
+      std::wcout << _T("\tTest not run because it is flaky on build system.")
+                 << std::endl;
       return;
     }
 
     scoped_process core_process;
-    scoped_process omaha_1_0_process;
-    scoped_process omaha_1_1_process;
     scoped_process install_process;
     scoped_process opposite_process;
     scoped_process user_handoff_process;
     scoped_process user_install_goopdate_process;
-    scoped_process user_install_needsadmin_process;
+    scoped_process user_install_slashinstall_process;
     scoped_process setup_phase1_job_process;
     scoped_process setup_phase1_job_opposite_process;
     scoped_process install_job_opposite_process;
@@ -273,12 +223,8 @@
     scoped_job silent_job_opposite;
     scoped_job silent_do_not_kill_job_opposite;
 
-    StartCoreProcessesToShutdown(address(core_process),
-                                 address(omaha_1_0_process),
-                                 address(omaha_1_1_process));
-    EXPECT_TRUE(core_process);
-    EXPECT_TRUE(omaha_1_0_process);
-    EXPECT_TRUE(omaha_1_1_process);
+    StartCoreProcessesToShutdown(address(core_process));
+    ASSERT_TRUE(core_process);
 
     if (use_job_objects_only && is_machine_) {
       // When starting the core process with psexec, there is a race condition
@@ -298,20 +244,22 @@
                   _T("/install"),
                   is_machine_,
                   address(install_process));
-    EXPECT_TRUE(install_process);
+    ASSERT_TRUE(install_process);
     EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(get(install_process), 0));
 
     if (vista_util::IsUserAdmin()) {
-      // Other users are usually ignored - use always ignored command line.
-      LaunchProcess(not_listening_exe_path_,
+      // GoogleUpdate running from the opposite directory should always be
+      // ignored. Using a command line that would not be ignored if it were not
+      // an opposite.
+      LaunchProcess(not_listening_exe_opposite_path_,
                     _T(""),
-                    !is_machine_,
+                    false,
                     address(opposite_process));
       EXPECT_TRUE(opposite_process);
       EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(get(opposite_process), 0));
     } else {
       EXPECT_FALSE(is_machine_)
-          << _T("Unexpected call for for machine when non-admin.");
+          << _T("Unexpected call for machine when non-admin.");
       // We can't launch a system process when non-admin.
       std::wcout << _T("\tPart of this test did not run because the user is ")
                     _T("not an admin.") << std::endl;
@@ -319,34 +267,26 @@
 
     CString same_needsadmin = is_machine_ ? _T("\"needsadmin=True\"") :
                                             _T("\"needsadmin=False\"");
-    CString opposite_needsadmin = is_machine_ ? _T("\"needsadmin=False\"") :
-                                                _T("\"needsadmin=True\"");
-    // Machine setup looks for users running /handoff and /ig with
-    // needsadmin=True and user setup ignores /handoff and /ig with
-    // needsadmin=True.
-    // Launching with needsadmin=<opposite> tests that machine ignores
-    // needsadmin=False and user ignores needsadmin=True.
-    LaunchProcess(not_listening_exe_path_,
-                  _T("/handoff ") + opposite_needsadmin,
+    // Machine setup looks for users running most modes from the machine
+    // official directory, and user setup looks for users running most modes
+    // from the user official directory.
+    // Launching with needsadmin=<same> tests that machine still ignores
+    // needsadmin=True and user ignores needsadmin=False when the opposite
+    // instances are running.
+    LaunchProcess(not_listening_exe_opposite_path_,
+                  _T("/handoff ") + same_needsadmin,
                   false,  // As the user.
                   address(user_handoff_process));
     EXPECT_TRUE(user_handoff_process);
     EXPECT_EQ(WAIT_TIMEOUT,
               ::WaitForSingleObject(get(user_handoff_process), 0));
 
-    LaunchProcess(not_listening_exe_path_,
-                  _T("/ig ") + opposite_needsadmin,
-                  false,  // As the user.
-                  address(user_install_goopdate_process));
-    EXPECT_TRUE(user_install_goopdate_process);
-    EXPECT_EQ(WAIT_TIMEOUT,
-              ::WaitForSingleObject(get(user_install_goopdate_process), 0));
-
-    // This process should be ignored even though it has needsadmin=<same>.
+    // This process should be ignored even though it is running from the correct
+    // official directory.
     LaunchProcess(not_listening_exe_path_,
                   _T("/install ") + same_needsadmin,
                   false,  // As the user.
-                  address(user_install_needsadmin_process));
+                  address(user_install_slashinstall_process));
     EXPECT_TRUE(user_install_goopdate_process);
     EXPECT_EQ(WAIT_TIMEOUT,
               ::WaitForSingleObject(get(user_install_goopdate_process), 0));
@@ -360,7 +300,7 @@
                        kSetupPhase1NonSelfUpdateJobObject,
                        address(setup_phase1_job_process),
                        address(setup_phase1_job));
-      EXPECT_TRUE(setup_phase1_job_process);
+      ASSERT_TRUE(setup_phase1_job_process);
       EXPECT_EQ(WAIT_TIMEOUT,
                 ::WaitForSingleObject(get(setup_phase1_job_process), 0));
     }
@@ -374,7 +314,7 @@
                        kSetupPhase1NonSelfUpdateJobObject,
                        address(setup_phase1_job_opposite_process),
                        address(setup_phase1_job_opposite));
-      EXPECT_TRUE(setup_phase1_job_opposite_process);
+      ASSERT_TRUE(setup_phase1_job_opposite_process);
       EXPECT_EQ(WAIT_TIMEOUT,
                 ::WaitForSingleObject(get(setup_phase1_job_opposite_process),
                                       0));
@@ -384,7 +324,7 @@
                        kAppInstallJobObject,
                        address(install_job_opposite_process),
                        address(install_job_opposite));
-      EXPECT_TRUE(install_job_opposite_process);
+      ASSERT_TRUE(install_job_opposite_process);
       EXPECT_EQ(WAIT_TIMEOUT,
                 ::WaitForSingleObject(get(install_job_opposite_process), 0));
 
@@ -393,7 +333,7 @@
                        kSilentJobObject,
                        address(silent_job_opposite_process),
                        address(silent_job_opposite));
-      EXPECT_TRUE(install_job_opposite_process);
+      ASSERT_TRUE(install_job_opposite_process);
       EXPECT_EQ(WAIT_TIMEOUT,
                 ::WaitForSingleObject(get(install_job_opposite_process), 0));
 
@@ -402,7 +342,7 @@
                        kSilentDoNotKillJobObject,
                        address(silent_do_not_kill_job_opposite_process),
                        address(silent_do_not_kill_job_opposite));
-      EXPECT_TRUE(install_job_opposite_process);
+      ASSERT_TRUE(install_job_opposite_process);
       EXPECT_EQ(WAIT_TIMEOUT,
                 ::WaitForSingleObject(get(install_job_opposite_process), 0));
     }
@@ -410,28 +350,9 @@
     EXPECT_SUCCEEDED(StopGoogleUpdateAndWait());
     EXPECT_EQ(0, setup_->extra_code1());
 
-    // Verify all real processes exited and terminate all the ones that aren't
-    // listening to shutdown.
-    HANDLE processes[] = {get(core_process),
-                          get(omaha_1_0_process),
-                          get(omaha_1_1_process)};
-    if (use_job_objects_only) {
-      // Since we only wait for processes in the Job Objects, legacy processes
-      // may not have stopped by the time StopGoogleUpdateAndWait() returns.
-      EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(core_process), 0));
-
-      // Now wait for the legacy processes to exit to avoid interfering with
-      // other tests.
-      EXPECT_EQ(WAIT_OBJECT_0, ::WaitForMultipleObjects(arraysize(processes),
-                                                        processes,
-                                                        true,  // wait for all
-                                                        8000));
-    } else {
-      EXPECT_EQ(WAIT_OBJECT_0, ::WaitForMultipleObjects(arraysize(processes),
-                                                        processes,
-                                                        true,  // wait for all
-                                                        0));
-    }
+    // Verify the real core process exited and terminate the processes that are
+    // not listening to shutdown.
+    EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(get(core_process), 0));
 
     // Terminate all the processes and wait for them to exit to avoid
     // interfering with other tests.
@@ -442,7 +363,7 @@
     }
     started_processes.push_back(get(user_handoff_process));
     started_processes.push_back(get(user_install_goopdate_process));
-    started_processes.push_back(get(user_install_needsadmin_process));
+    started_processes.push_back(get(user_install_slashinstall_process));
     if (use_job_objects_only) {
       started_processes.push_back(get(setup_phase1_job_process));
     }
@@ -475,7 +396,7 @@
     if (is_machine_) {
       user_sid_to_use = kLocalSystemSid;
     } else {
-      EXPECT_SUCCEEDED(user_info::GetCurrentUser(NULL, NULL, &user_sid_to_use));
+      EXPECT_SUCCEEDED(user_info::GetProcessUser(NULL, NULL, &user_sid_to_use));
     }
 
     DWORD flags = INCLUDE_ONLY_PROCESS_OWNED_BY_USER |
@@ -486,7 +407,7 @@
     command_lines.push_back(_T("/c"));
 
     return Process::FindProcesses(flags,
-                                  _T("GoogleUpdate.exe"),
+                                  kOmahaShellFileName,
                                   true,
                                   user_sid_to_use,
                                   command_lines,
@@ -510,12 +431,8 @@
   // Assumes that psexec blocks until the process exits.
   // TODO(omaha): Start the opposite instances and wait for them in
   // StopGoogleUpdateAndWaitSucceedsTest. They should not close.
-  void StartCoreProcessesToShutdown(HANDLE* core_process,
-                                    HANDLE* omaha_1_0_process,
-                                    HANDLE* omaha_1_1_process) {
+  void StartCoreProcessesToShutdown(HANDLE* core_process) {
     ASSERT_TRUE(core_process);
-    ASSERT_TRUE(omaha_1_0_process);
-    ASSERT_TRUE(omaha_1_1_process);
 
     // Find the core process or start one if necessary.
     Pids core_processes;
@@ -535,13 +452,7 @@
     }
     ASSERT_TRUE(*core_process);
 
-    // Start the legacy versions.
-    LaunchProcess(omaha_10_exe_path_, _T(""), is_machine_, omaha_1_0_process);
-    LaunchProcess(omaha_11_exe_path_, _T(""), is_machine_, omaha_1_1_process);
-
-    HANDLE processes[] = {*core_process,
-                          *omaha_1_0_process,
-                          *omaha_1_1_process};
+    HANDLE processes[] = {*core_process};
     EXPECT_EQ(WAIT_TIMEOUT, ::WaitForMultipleObjects(arraysize(processes),
                                                      processes,
                                                      true,  // wait for all
@@ -550,15 +461,14 @@
 
   // Launches an instance of GoogleUpdate.exe that doesn't exit.
   void StopGoogleUpdateAndWaitProcessesDoNotStopTest() {
-    LaunchProcessAndExpectStopGoogleUpdateAndWaitTimesOut(is_machine_,
-                                                   _T(""),
-                                                   COMMANDLINE_MODE_NOARGS);
+    LaunchProcessAndExpectStopGoogleUpdateAndWaitKillsProcess(
+        is_machine_,
+        _T(""));
   }
 
-  void LaunchProcessAndExpectStopGoogleUpdateAndWaitTimesOut(
+  void LaunchProcessAndExpectStopGoogleUpdateAndWaitKillsProcess(
       bool is_machine_process,
-      const CString& args,
-      CommandLineMode expected_running_process_mode) {
+      const CString& args) {
     ASSERT_TRUE(args);
 
     if (is_machine_ && !vista_util::IsUserAdmin()) {
@@ -575,7 +485,7 @@
                   args,
                   is_machine_process,
                   address(process));
-    EXPECT_TRUE(process);
+    ASSERT_TRUE(process);
     EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(get(process), 0));
 
     // Tests that use this method intermittently fail when run on build systems.
@@ -591,43 +501,8 @@
     }
     EXPECT_SUCCEEDED(hr);
 
-    EXPECT_EQ(GOOPDATE_E_INSTANCES_RUNNING, StopGoogleUpdateAndWait());
-    EXPECT_EQ(expected_running_process_mode, setup_->extra_code1());
-    // Make sure the process is still running to help debug failures.
-    EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(get(process), 0));
-
-
-    EXPECT_TRUE(::TerminateProcess(get(process), 1));
-    EXPECT_EQ(WAIT_OBJECT_0,
-              ::WaitForSingleObject(get(process), kProcessesCleanupWait));
-  }
-
-  // If start using Job Objects again, make sure the test only uses Job Objects.
-  void LaunchProcessInJobAndExpectStopGoogleUpdateAndWaitTimesOut(
-      const CString& job_base_name,
-      bool machine_as_system) {
-    ASSERT_TRUE(is_machine_ || !machine_as_system);
-
-    if (!ShouldRunLargeTest()) {
-      return;
-    }
-
-
-    scoped_process process;
-    scoped_job job;
-
-    LaunchJobProcess(is_machine_,
-                     machine_as_system,
-                     job_base_name,
-                     address(process),
-                     address(job));
-    EXPECT_TRUE(process);
-    EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(get(process), 0));
-
-    EXPECT_EQ(GOOPDATE_E_INSTANCES_RUNNING, StopGoogleUpdateAndWait());
-    EXPECT_EQ(COMMANDLINE_MODE_NOARGS, setup_->extra_code1());
-
-    EXPECT_TRUE(::TerminateProcess(get(process), 1));
+    EXPECT_EQ(S_OK, StopGoogleUpdateAndWait());
+    // Make sure the process has been killed.
     EXPECT_EQ(WAIT_OBJECT_0,
               ::WaitForSingleObject(get(process), kProcessesCleanupWait));
   }
@@ -643,26 +518,26 @@
                   _T("/c"),
                   is_machine_,
                   address(core_process));
-    EXPECT_TRUE(core_process);
+    ASSERT_TRUE(core_process);
     EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(get(core_process), 0));
-    LaunchProcess(not_listening_exe_path_,
+    LaunchProcess(not_listening_exe_opposite_path_,
                   _T("/c"),
                   !is_machine_,
                   address(opposite_core_process));
-    EXPECT_TRUE(opposite_core_process);
+    ASSERT_TRUE(opposite_core_process);
     EXPECT_EQ(WAIT_TIMEOUT,
               ::WaitForSingleObject(get(opposite_core_process), 0));
     LaunchProcess(not_listening_exe_path_,
                   _T("/cr"),
                   is_machine_,
                   address(codered_process));
-    EXPECT_TRUE(codered_process);
+    ASSERT_TRUE(codered_process);
     EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(get(codered_process), 0));
     LaunchProcess(not_listening_exe_path_,
                   _T(""),
                   is_machine_,
                   address(noargs_process));
-    EXPECT_TRUE(noargs_process);
+    ASSERT_TRUE(noargs_process);
     EXPECT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(get(noargs_process), 0));
 
     EXPECT_SUCCEEDED(TerminateCoreProcesses());
@@ -691,139 +566,6 @@
                                                  kProcessesCleanupWait));
   }
 
-  // Sets the legacy setup locks then attempts to run setup with the legacy
-  // versions. When the lock is not held and a newer version is installed,
-  // the legacy executables do a handoff install and exit. Since we have
-  // acquired the locks, the legacy executables should wait and not exit.
-  // Check both after a second.
-  // If Omaha 1.2.x is not found, a fake version is installed to prevent the
-  // legacy installers from installing.
-  // When is_machine_ is true, the legacy installers will start the Service.
-  void AcquireLegacySetupLocksTest() {
-    if (is_machine_ && !vista_util::IsUserAdmin()) {
-      std::wcout << _T("\tTest did not run because the user is not an admin.")
-                 << std::endl;
-      return;
-    }
-
-    if (!ShouldRunLargeTest()) {
-      return;
-    }
-
-    // If Omaha 2 is not already installed, fool the legacy instances into
-    // thinking a newer version is installed so that they don't try to install
-    // when we release the locks. Do this for both user and machine in the
-    // is_machine case since it starts both.
-    // This must be reverted at the end of the test because some tests or shell
-    // EXE instances may see this value and assume Omaha is correctly installed.
-    const TCHAR* const kFakeVersion = _T("1.2.0.654");
-    CString version;
-    bool is_fake_pv_user = false;
-    if (FAILED(RegKey::GetValue(USER_REG_CLIENTS_GOOPDATE,
-                                               _T("pv"),
-                                               &version)) ||
-        (_T("1.2.") != version.Left(4))) {
-      is_fake_pv_user = true;
-      version = kFakeVersion;
-      EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENTS_GOOPDATE,
-                                        _T("pv"),
-                                        version));
-    }
-
-    bool is_fake_pv_machine = false;
-    if (is_machine_ && FAILED(RegKey::GetValue(MACHINE_REG_CLIENTS_GOOPDATE,
-                                _T("pv"),
-                                &version)) ||
-        (_T("1.2.") != version.Left(4))) {
-      is_fake_pv_machine = true;
-      version = kFakeVersion;
-      EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENTS_GOOPDATE,
-                                        _T("pv"),
-                                        version));
-    }
-
-    // Legacy installers check for the files and over-install if they are not
-    // found, so we copy files to the version directory.
-    CString install_dir = ConcatenatePath(
-        is_machine_ ?
-        ConfigManager::Instance()->GetMachineGoopdateInstallDir() :
-        ConfigManager::Instance()->GetUserGoopdateInstallDir(),
-        version);
-    EXPECT_SUCCEEDED(File::CopyTree(
-        ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                        _T("unittest_support\\omaha_1.2.x\\")),
-        install_dir,
-        false));
-
-    // This block of code reproduces what we do in Setup::DoInstall().
-    GLock lock10, lock11_user, lock11_machine;
-    EXPECT_TRUE(InitLegacySetupLocks(&lock10, &lock11_user, &lock11_machine));
-    EXPECT_TRUE(lock10.Lock(500));
-    EXPECT_TRUE(lock11_user.Lock(500));
-    if (is_machine_) {
-      EXPECT_TRUE(lock11_machine.Lock(500));
-    }
-
-    // Start the legacy processes, and verify that they are blocked.
-    scoped_process omaha_1_0_process;
-    scoped_process omaha_1_1_process;
-    scoped_process omaha_1_0_process_as_system;
-    scoped_process omaha_1_1_process_as_system;
-    LaunchProcess(omaha_10_exe_path_,
-                  _T("/install"),
-                  false,  // As the user.
-                  address(omaha_1_0_process));
-    LaunchProcess(omaha_11_exe_path_,
-                  _T("/update"),
-                  false,  // As the user.
-                  address(omaha_1_1_process));
-    if (is_machine_) {
-      // For the machine case, tests the legacy process running both as Local
-      // System and as the user.
-      LaunchProcess(omaha_10_exe_path_,
-                    _T("/install"),
-                    true,  // As Local System.
-                    address(omaha_1_0_process_as_system));
-      LaunchProcess(omaha_11_exe_path_,
-                    _T("/update"),
-                    true,  // As Local System.
-                    address(omaha_1_1_process_as_system));
-    }
-
-
-    HANDLE processes[] = {get(omaha_1_0_process),
-                          get(omaha_1_1_process),
-                          get(omaha_1_0_process_as_system),
-                          get(omaha_1_1_process_as_system)};
-    int num_processes = arraysize(processes) - (is_machine_ ? 0 : 2);
-    EXPECT_EQ(WAIT_TIMEOUT, ::WaitForMultipleObjects(num_processes,
-                                                     processes,
-                                                     false,  // wait for any
-                                                     1000));
-
-    // Release the locks and the legacy processes should exit
-    EXPECT_TRUE(lock10.Unlock());
-    EXPECT_TRUE(lock11_user.Unlock());
-    if (is_machine_) {
-      EXPECT_TRUE(lock11_machine.Unlock());
-    }
-
-    EXPECT_EQ(WAIT_OBJECT_0, ::WaitForMultipleObjects(num_processes,
-                                                      processes,
-                                                      true,  // wait for all
-                                                      kProcessesCleanupWait));
-
-    RestoreRegistryHives();
-    if (is_fake_pv_user) {
-      EXPECT_SUCCEEDED(RegKey::DeleteValue(USER_REG_CLIENTS_GOOPDATE,
-                                           kRegValueProductVersion));
-    }
-    if (is_fake_pv_machine) {
-      EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_CLIENTS_GOOPDATE,
-                                           kRegValueProductVersion));
-    }
-  }
-
   // Starts dummy process that doesn't exit and assigns it to the specified job.
   // The handle returned by ShellExecute does not have PROCESS_SET_QUOTA access
   // rights, so we get a new handle with the correct access rights to return to
@@ -838,7 +580,8 @@
     ASSERT_TRUE(is_machine || !as_system);
 
     scoped_process launched_process;
-    LaunchProcess(not_listening_exe_path_,
+    LaunchProcess(is_machine ? not_listening_machine_exe_path_ :
+                               not_listening_user_exe_path_,
                   _T(""),
                   as_system,
                   address(launched_process));
@@ -880,21 +623,57 @@
         _T("Last Error: ") << ::GetLastError() << std::endl;
   }
 
+  void TestShouldDelayUninstall() {
+    EXPECT_FALSE(setup_->ShouldDelayUninstall());
+
+    const TCHAR* key = ConfigManager::Instance()->registry_update(is_machine_);
+
+    DWORD value = 1;
+    EXPECT_SUCCEEDED(RegKey::SetValue(key,
+                                      kRegValueDelayOmahaUninstall,
+                                      value));
+    EXPECT_TRUE(setup_->ShouldDelayUninstall());
+
+    value = 0;
+    EXPECT_SUCCEEDED(RegKey::SetValue(key,
+                                      kRegValueDelayOmahaUninstall,
+                                      value));
+    EXPECT_FALSE(setup_->ShouldDelayUninstall());
+
+    EXPECT_SUCCEEDED(RegKey::DeleteValue(key,
+                                         kRegValueDelayOmahaUninstall));
+    EXPECT_FALSE(setup_->ShouldDelayUninstall());
+  }
+
+  void TestSetDelayUninstall() {
+    EXPECT_FALSE(setup_->ShouldDelayUninstall());
+
+    EXPECT_SUCCEEDED(setup_->SetDelayUninstall(true));
+    EXPECT_TRUE(setup_->ShouldDelayUninstall());
+
+    EXPECT_SUCCEEDED(setup_->SetDelayUninstall(true));
+    EXPECT_TRUE(setup_->ShouldDelayUninstall());
+
+    EXPECT_SUCCEEDED(setup_->SetDelayUninstall(false));
+    EXPECT_FALSE(setup_->ShouldDelayUninstall());
+
+    EXPECT_SUCCEEDED(setup_->SetDelayUninstall(false));
+    EXPECT_FALSE(setup_->ShouldDelayUninstall());
+  }
+
   const bool is_machine_;
   const CString omaha_path_;
+  CString omaha_exe_path_;
+  CString not_listening_exe_path_;
+  CString not_listening_exe_opposite_path_;
   scoped_ptr<omaha::Setup> setup_;
-  CommandLineArgs args_;
 
-  static CString omaha_exe_path_;
-  static CString omaha_10_exe_path_;
-  static CString omaha_11_exe_path_;
-  static CString not_listening_exe_path_;
+  static CString not_listening_user_exe_path_;
+  static CString not_listening_machine_exe_path_;
 };
 
-CString SetupTest::omaha_exe_path_;
-CString SetupTest::omaha_10_exe_path_;
-CString SetupTest::omaha_11_exe_path_;
-CString SetupTest::not_listening_exe_path_;
+CString SetupTest::not_listening_user_exe_path_;
+CString SetupTest::not_listening_machine_exe_path_;
 
 class SetupMachineTest : public SetupTest {
  protected:
@@ -925,10 +704,6 @@
   }
 
   virtual void SetUp() {
-    args_.extra_args_str = _T("\"appname=foo&lang=en\"");
-    CommandLineAppArgs app_args;
-    app_args.app_guid = StringToGuid(kAppGuid_);
-    args_.extra.apps.push_back(app_args);
     SetupUserTest::SetUp();
 
     // Save the existing version if present.
@@ -954,28 +729,28 @@
 
   void InstallFutureVersion() {
     DeleteDirectory(future_version_path_);
-    ASSERT_FALSE(File::IsDirectory(future_version_path_));
+    EXPECT_FALSE(File::IsDirectory(future_version_path_));
 
-    ASSERT_SUCCEEDED(CreateDir(future_version_path_, NULL));
+    EXPECT_SUCCEEDED(CreateDir(future_version_path_, NULL));
 
-    ASSERT_SUCCEEDED(File::Copy(
+    EXPECT_SUCCEEDED(File::Copy(
         ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                        _T("GoogleUpdate.exe")),
-        omaha_path_ + _T("GoogleUpdate.exe"),
+                        kOmahaShellFileName),
+        omaha_path_ + kOmahaShellFileName,
         false));
 
-    ASSERT_SUCCEEDED(File::Copy(
+    EXPECT_SUCCEEDED(File::Copy(
         ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                        _T("goopdate.dll")),
-        ConcatenatePath(future_version_path_, _T("goopdate.dll")),
+                        kOmahaDllName),
+        ConcatenatePath(future_version_path_, kOmahaDllName),
         false));
-    ASSERT_SUCCEEDED(File::Copy(
+    EXPECT_SUCCEEDED(File::Copy(
         ConcatenatePath(app_util::GetCurrentModuleDirectory(),
                         _T("goopdateres_en.dll")),
         ConcatenatePath(future_version_path_, _T("goopdateres_en.dll")),
         false));
 
-    ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENTS_GOOPDATE,
+    EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENTS_GOOPDATE,
                                       kRegValueProductVersion,
                                       kFutureVersionString));
   }
@@ -1043,11 +818,6 @@
   virtual void SetUp() {
     SetupMachineTest::SetUp();
 
-    // Prime the cache of CSIDL_PROGRAM_FILES so it is available even after
-    // we override HKLM.
-    CString program_files_path;
-    EXPECT_SUCCEEDED(GetFolderPath(CSIDL_PROGRAM_FILES, &program_files_path));
-
     RegKey::DeleteKey(hive_override_key_name_, true);
     OverrideRegistryHives(hive_override_key_name_);
   }
@@ -1062,153 +832,11 @@
   const CString hive_override_key_name_;
 };
 
-class SetupOfflineInstallerTest : public testing::Test {
- protected:
-  static bool CallCopyOfflineFiles(const CommandLineArgs& args,
-                                   const CString& target_location) {
-    omaha::Setup setup(false, &args);
-    return setup.CopyOfflineFiles(target_location);
-  }
-
-  static HRESULT CallCopyOfflineFilesForGuid(const CString& app_guid,
-                                             const CString& target_location) {
-    return omaha::Setup::CopyOfflineFilesForGuid(app_guid, target_location);
-  }
-};
-
-
-TEST_F(SetupFutureVersionInstalledUserTest, Install_HandoffWithShellMissing) {
-  CString shell_path = ConcatenatePath(omaha_path_, _T("GoogleUpdate.exe"));
-  ASSERT_TRUE(SUCCEEDED(File::DeleteAfterReboot(shell_path)) ||
-              !vista_util::IsUserAdmin());
-  ASSERT_FALSE(File::Exists(shell_path));
-
-  scoped_event ui_displayed_event;
-  ASSERT_SUCCEEDED(goopdate_utils::CreateUniqueEventInEnvironment(
-      kUiDisplayedEventEnvironmentVariableName,
-      is_machine_,
-      address(ui_displayed_event)));
-
-  EXPECT_EQ(GOOPDATE_E_HANDOFF_FAILED, setup_->Install(_T("/foo")));
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), setup_->extra_code1());
-}
-
-TEST_F(SetupFutureVersionInstalledUserTest,
-       Install_HandoffWithGoopdateDllMissing) {
-  CString dll_path = ConcatenatePath(future_version_path_, _T("goopdate.dll"));
-  ASSERT_SUCCEEDED(File::Remove(dll_path));
-  ASSERT_FALSE(File::Exists(dll_path));
-
-  scoped_event ui_displayed_event;
-  ASSERT_SUCCEEDED(goopdate_utils::CreateUniqueEventInEnvironment(
-      kUiDisplayedEventEnvironmentVariableName,
-      is_machine_,
-      address(ui_displayed_event)));
-
-  EXPECT_EQ(GOOPDATE_E_HANDOFF_FAILED, setup_->Install(_T("/foo")));
-  EXPECT_EQ(GOOGLEUPDATE_E_DLL_NOT_FOUND, setup_->extra_code1());
-}
-
-// The setup would cause a failure but we shouldn't see it because the event is
-// already signaled.
-// There is a race condition, especially on build machines, where the process
-// may have exited by the time we check for it and the event. Thus, there are
-// two expected cases.
-TEST_F(SetupFutureVersionInstalledUserTest,
-       Install_HandoffWithEventSignaledBeforeExit) {
-  CString dll_path = ConcatenatePath(future_version_path_, _T("goopdate.dll"));
-  ASSERT_SUCCEEDED(File::Remove(dll_path));
-  ASSERT_FALSE(File::Exists(dll_path));
-
-  scoped_event ui_displayed_event;
-  ASSERT_SUCCEEDED(goopdate_utils::CreateUniqueEventInEnvironment(
-      kUiDisplayedEventEnvironmentVariableName,
-      is_machine_,
-      address(ui_displayed_event)));
-  ASSERT_TRUE(::SetEvent(get(ui_displayed_event)));
-
-  HRESULT hr = setup_->Install(_T("/foo"));
-  EXPECT_TRUE(SUCCEEDED(hr) || GOOPDATE_E_HANDOFF_FAILED == hr);
-  if (SUCCEEDED(hr)) {
-    EXPECT_EQ(0, setup_->extra_code1());
-
-    // There is a timing issue where GoogleUpdate.exe may not look at pv in the
-    // registry until after the existing_version_ has been restored to the
-    // registry. This causes GoogleUpdate.exe to find goopdate.dll in the
-    // existing_version_ and run with a UI. Wait so that this does not happen.
-    ::Sleep(IsBuildSystem() ? 8000 : 1000);
-  } else {
-    EXPECT_EQ(GOOGLEUPDATE_E_DLL_NOT_FOUND, setup_->extra_code1());
-  }
-}
-
-// Silent installs ignore the event, so we should always get the exit code.
-// Also, exit code is reported directly instead of GOOPDATE_E_HANDOFF_FAILED.
-TEST_F(SetupFutureVersionInstalledUserTest,
-       Install_SilentHandoffWithEventSignaledBeforeExit) {
-  CString dll_path = ConcatenatePath(future_version_path_, _T("goopdate.dll"));
-  ASSERT_SUCCEEDED(File::Remove(dll_path));
-  ASSERT_FALSE(File::Exists(dll_path));
-
-  scoped_event ui_displayed_event;
-  ASSERT_SUCCEEDED(goopdate_utils::CreateUniqueEventInEnvironment(
-      kUiDisplayedEventEnvironmentVariableName,
-      is_machine_,
-      address(ui_displayed_event)));
-  ASSERT_TRUE(::SetEvent(get(ui_displayed_event)));
-
-  args_.is_silent_set = true;
-  EXPECT_EQ(GOOGLEUPDATE_E_DLL_NOT_FOUND, setup_->Install(_T("/foo")));
-  EXPECT_EQ(0, setup_->extra_code1());
-}
-
-// Offline installs ignore the event regardless of whether they are silent, so
-// we should always get the exit code.
-// Also, exit code is reported directly instead of GOOPDATE_E_HANDOFF_FAILED.
-TEST_F(SetupFutureVersionInstalledUserTest,
-       Install_InteractiveOfflineHandoffWithEventSignaledBeforeExit) {
-  CString dll_path = ConcatenatePath(future_version_path_, _T("goopdate.dll"));
-  ASSERT_SUCCEEDED(File::Remove(dll_path));
-  ASSERT_FALSE(File::Exists(dll_path));
-
-  scoped_event ui_displayed_event;
-  ASSERT_SUCCEEDED(goopdate_utils::CreateUniqueEventInEnvironment(
-      kUiDisplayedEventEnvironmentVariableName,
-      is_machine_,
-      address(ui_displayed_event)));
-  ASSERT_TRUE(::SetEvent(get(ui_displayed_event)));
-
-  // Make Setup think this is an offline install.
-  CString manifest_path;
-  manifest_path.Format(_T("%s\\%s.gup"),
-                           app_util::GetCurrentModuleDirectory(),
-                           kAppGuid_);
-  File manifest_file;
-  ASSERT_SUCCEEDED(manifest_file.Open(manifest_path, true, false));
-  ASSERT_SUCCEEDED(manifest_file.Touch());
-  ASSERT_SUCCEEDED(manifest_file.Close());
-
-  CString installer_path;
-  installer_path.Format(_T("%s\\unittest_installer.exe.%s"),
-                            app_util::GetCurrentModuleDirectory(),
-                            kAppGuid_);
-  File installer_file;
-  EXPECT_SUCCEEDED(installer_file.Open(installer_path, true, false));
-  EXPECT_SUCCEEDED(installer_file.Touch());
-  EXPECT_SUCCEEDED(installer_file.Close());
-
-  EXPECT_EQ(GOOGLEUPDATE_E_DLL_NOT_FOUND, setup_->Install(_T("/foo")));
-  EXPECT_EQ(0, setup_->extra_code1());
-
-  EXPECT_FALSE(args_.is_silent_set);  // Ensure not silent as voids this test.
-  EXPECT_TRUE(launched_offline_worker());
-
-  EXPECT_TRUE(::DeleteFile(manifest_path));
-  EXPECT_TRUE(::DeleteFile(installer_path));
-}
-
-TEST_F(SetupFutureVersionInstalledUserTest,
-       InstallSelfSilently_NoRunKey) {
+// TODO(omaha3): These tests are left over from Omaha2's InstallSelfSilently(),
+// which like Omaha3's Install(), does not install the app. It did, however,
+// start another process to complete installation. This would cause it to fail
+// if the shell or main DLL were missing.
+TEST_F(SetupFutureVersionInstalledUserTest, DISABLED_Install_NoRunKey) {
   RegKey::DeleteKey(kRegistryHiveOverrideRoot, true);
   OverrideSpecifiedRegistryHives(kRegistryHiveOverrideRoot, false, true);
 
@@ -1217,20 +845,20 @@
                                     kRegValueProductVersion,
                                     kFutureVersionString));
 
-  CString dll_path = ConcatenatePath(future_version_path_, _T("goopdate.dll"));
+  CString dll_path = ConcatenatePath(future_version_path_, kOmahaDllName);
   ASSERT_SUCCEEDED(File::Remove(dll_path));
   ASSERT_FALSE(File::Exists(dll_path));
 
   EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            setup_->InstallSelfSilently());
+            setup_->Install(false));
   EXPECT_EQ(0, setup_->extra_code1());
 
   RestoreRegistryHives();
   ASSERT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
 }
 
-TEST_F(SetupFutureVersionInstalledUserTest,
-       InstallSelfSilently_ValidRunKey) {
+// Command line must be valid to avoid displaying invalid command line error.
+TEST_F(SetupFutureVersionInstalledUserTest, Install_ValidRunKey) {
   RegKey::DeleteKey(kRegistryHiveOverrideRoot, true);
   OverrideSpecifiedRegistryHives(kRegistryHiveOverrideRoot, false, true);
 
@@ -1241,27 +869,22 @@
 
   const TCHAR kRunKey[] =
       _T("HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run");
-  const CString shell_path = ConcatenatePath(GetGoogleUpdateUserPath(),
-                                             _T("GoogleUpdate.exe"));
+  const CString shell_path = ConcatenatePath(omaha_path_, kOmahaShellFileName);
   CString run_value;
-  run_value.Format(_T("\"%s\" /just_exit"), shell_path);
+  run_value.Format(_T("\"%s\" /cr"), shell_path);
   ASSERT_SUCCEEDED(RegKey::SetValue(kRunKey, _T("Google Update"), run_value));
 
-  CString dll_path = ConcatenatePath(future_version_path_, _T("goopdate.dll"));
+  CString dll_path = ConcatenatePath(future_version_path_, kOmahaDllName);
   ASSERT_SUCCEEDED(File::Remove(dll_path));
   ASSERT_FALSE(File::Exists(dll_path));
 
-  EXPECT_SUCCEEDED(setup_->InstallSelfSilently());
+  EXPECT_SUCCEEDED(setup_->Install(false));
   EXPECT_EQ(0, setup_->extra_code1());
 
   RestoreRegistryHives();
   ASSERT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
 }
 
-TEST_F(SetupUserTest, HasXmlParser) {
-  EXPECT_TRUE(HasXmlParser());
-}
-
 TEST_F(SetupUserTest, Install_LockTimedOut) {
   TestInstallWhileHoldingLock();
 }
@@ -1276,149 +899,6 @@
   TestInstallWhileHoldingLock();
 }
 
-TEST_F(SetupRegistryProtectedUserTest, Install_OEM) {
-  // The test fixture only disables HKCU by default. Disable HKLM too.
-  OverrideSpecifiedRegistryHives(hive_override_key_name_, true, true);
-
-  if (vista_util::IsVistaOrLater()) {
-    EXPECT_SUCCEEDED(RegKey::SetValue(
-        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-        _T("ImageState"),
-        _T("IMAGE_STATE_UNDEPLOYABLE")));
-  } else {
-    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
-                                      _T("AuditInProgress"),
-                                      static_cast<DWORD>(1)));
-  }
-
-  EXPECT_TRUE(ConfigManager::Instance()->IsWindowsInstalling());
-
-  args_.is_oem_set = true;
-  EXPECT_EQ(GOOPDATE_E_OEM_NOT_MACHINE_AND_PRIVILEGED_AND_AUDIT_MODE,
-            setup_->Install(_T("foo")));
-
-  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, kRegValueOemInstallTimeSec));
-  EXPECT_FALSE(
-      RegKey::HasValue(MACHINE_REG_UPDATE, kRegValueOemInstallTimeSec));
-}
-
-TEST_F(SetupRegistryProtectedMachineTest, Install_OemElevationRequired) {
-  if (vista_util::IsUserAdmin()) {
-    std::wcout << _T("\tTest did not run because the user IS an admin.")
-               << std::endl;
-    return;
-  }
-
-  if (vista_util::IsVistaOrLater()) {
-    EXPECT_SUCCEEDED(RegKey::SetValue(
-        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-        _T("ImageState"),
-        _T("IMAGE_STATE_UNDEPLOYABLE")));
-  } else {
-    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
-                                      _T("AuditInProgress"),
-                                      static_cast<DWORD>(1)));
-  }
-
-  EXPECT_TRUE(ConfigManager::Instance()->IsWindowsInstalling());
-
-  args_.is_oem_set = true;
-  EXPECT_EQ(GOOPDATE_E_OEM_NOT_MACHINE_AND_PRIVILEGED_AND_AUDIT_MODE,
-            setup_->Install(_T("foo")));
-
-  EXPECT_FALSE(
-      RegKey::HasValue(MACHINE_REG_UPDATE, kRegValueOemInstallTimeSec));
-}
-
-TEST_F(SetupRegistryProtectedMachineTest, Install_OemNotAuditMode) {
-  if (!vista_util::IsUserAdmin()) {
-    std::wcout << _T("\tTest did not run because the user is not an admin.")
-               << std::endl;
-    return;
-  }
-
-  args_.is_oem_set = true;
-  EXPECT_EQ(GOOPDATE_E_OEM_NOT_MACHINE_AND_PRIVILEGED_AND_AUDIT_MODE,
-            setup_->Install(_T("foo")));
-  EXPECT_FALSE(
-      RegKey::HasValue(MACHINE_REG_UPDATE, kRegValueOemInstallTimeSec));
-}
-
-// Prevents the install from continuing by holding the Setup Lock.
-TEST_F(SetupRegistryProtectedMachineTest, Install_OemFails) {
-  if (!vista_util::IsUserAdmin()) {
-    std::wcout << _T("\tTest did not run because the user is not an admin.")
-               << std::endl;
-    return;
-  }
-
-  if (vista_util::IsVistaOrLater()) {
-    EXPECT_SUCCEEDED(RegKey::SetValue(
-        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-        _T("ImageState"),
-        _T("IMAGE_STATE_UNDEPLOYABLE")));
-
-    // TODO(omaha): Overriding HKLM causes HasXmlParser() to fail on Vista,
-    // preventing this test from running correctly.
-    return;
-  } else {
-    EXPECT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
-                                      _T("AuditInProgress"),
-                                      static_cast<DWORD>(1)));
-  }
-
-  EXPECT_TRUE(ConfigManager::Instance()->IsWindowsInstalling());
-
-  HoldLock hold_lock(is_machine_);
-  Thread thread;
-  thread.Start(&hold_lock);
-  hold_lock.WaitForLockToBeAcquired();
-
-  args_.is_oem_set = true;
-  EXPECT_EQ(GOOPDATE_E_FAILED_TO_GET_LOCK, setup_->Install(_T("foo")));
-
-  EXPECT_TRUE(
-      RegKey::HasValue(MACHINE_REG_UPDATE, kRegValueOemInstallTimeSec));
-
-  hold_lock.Stop();
-  thread.WaitTillExit(1000);
-}
-
-TEST_F(SetupMachineTest, LaunchInstalledWorker_OemInstallNotOffline) {
-  SetModeInstall();
-  CommandLineAppArgs app1;
-  args_.extra.apps.push_back(app1);
-  args_.is_oem_set = true;
-  EXPECT_EQ(GOOPDATE_E_OEM_WITH_ONLINE_INSTALLER,
-            LaunchInstalledWorker(true, NULL));
-}
-
-TEST_F(SetupMachineTest, LaunchInstalledWorker_OemUpdateNotOffline) {
-  SetModeSelfUpdate();
-  args_.is_oem_set = true;
-  ExpectAsserts expect_asserts;
-  EXPECT_EQ(GOOPDATE_E_OEM_WITH_ONLINE_INSTALLER,
-            LaunchInstalledWorker(true, NULL));
-}
-
-TEST_F(SetupMachineTest, LaunchInstalledWorker_EulaRequiredNotOffline) {
-  SetModeInstall();
-  CommandLineAppArgs app1;
-  args_.extra.apps.push_back(app1);
-  args_.is_eula_required_set = true;
-  EXPECT_EQ(GOOPDATE_E_EULA_REQURED_WITH_ONLINE_INSTALLER,
-            LaunchInstalledWorker(true, NULL));
-}
-
-TEST_F(SetupUserTest, LaunchInstalledWorker_EulaRequiredNotOffline) {
-  SetModeInstall();
-  CommandLineAppArgs app1;
-  args_.extra.apps.push_back(app1);
-  args_.is_eula_required_set = true;
-  EXPECT_EQ(GOOPDATE_E_EULA_REQURED_WITH_ONLINE_INSTALLER,
-            LaunchInstalledWorker(true, NULL));
-}
-
 //
 // ShouldInstall tests.
 //
@@ -1449,7 +929,7 @@
       DeleteDirectory(ConcatenatePath(omaha_path_, this_version_)));
   CString file_path = ConcatenatePath(
                           ConcatenatePath(omaha_path_, this_version_),
-                          _T("goopdate.dll"));
+                          kOmahaDllName);
   ASSERT_FALSE(File::Exists(file_path));
 
   EXPECT_TRUE(ShouldInstall());
@@ -1564,7 +1044,7 @@
 
   CopyGoopdateFiles(omaha_path_, this_version_);
   CString path = ConcatenatePath(ConcatenatePath(omaha_path_, this_version_),
-                                 _T("goopdate.dll"));
+                                 kOmahaDllName);
   ASSERT_SUCCEEDED(File::Remove(path));
   ASSERT_FALSE(File::Exists(path));
 
@@ -1582,11 +1062,28 @@
 
   CopyGoopdateFiles(omaha_path_, this_version_);
   CString path = ConcatenatePath(ConcatenatePath(omaha_path_, this_version_),
-                                 _T("GoopdateBho.dll"));
+                                 UPDATE_PLUGIN_FILENAME);
   ASSERT_SUCCEEDED(File::Remove(path));
   ASSERT_FALSE(File::Exists(path));
 
+  DWORD existing_overinstall(0);
+  bool had_existing_overinstall = SUCCEEDED(RegKey::GetValue(
+                                                MACHINE_REG_UPDATE_DEV,
+                                                kRegValueNameOverInstall,
+                                                &existing_overinstall));
+  if (had_existing_overinstall) {
+    EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE_DEV,
+                                         kRegValueNameOverInstall));
+  }
+
   EXPECT_TRUE(ShouldInstall());
+
+  // Restore "overinstall"
+  if (had_existing_overinstall) {
+    EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE_DEV,
+                                      kRegValueNameOverInstall,
+                                      existing_overinstall));
+  }
 }
 
 TEST_F(SetupRegistryProtectedUserTest, ShouldInstall_SameVersionShellMissing) {
@@ -1598,7 +1095,7 @@
                                     this_version_));
 
   CopyGoopdateFiles(omaha_path_, this_version_);
-  CString shell_path = ConcatenatePath(omaha_path_, _T("GoogleUpdate.exe"));
+  CString shell_path = ConcatenatePath(omaha_path_, kOmahaShellFileName);
   ASSERT_TRUE(SUCCEEDED(File::DeleteAfterReboot(shell_path)) ||
               !vista_util::IsUserAdmin());
   ASSERT_FALSE(File::Exists(shell_path));
@@ -1615,7 +1112,7 @@
                                     kFutureVersionString));
 
   CopyGoopdateFiles(omaha_path_, kFutureVersionString);
-  CString shell_path = ConcatenatePath(omaha_path_, _T("GoogleUpdate.exe"));
+  CString shell_path = ConcatenatePath(omaha_path_, kOmahaShellFileName);
   ASSERT_TRUE(SUCCEEDED(File::DeleteAfterReboot(shell_path)) ||
               !vista_util::IsUserAdmin());
   ASSERT_FALSE(File::Exists(shell_path));
@@ -1627,6 +1124,26 @@
 }
 
 //
+// ShouldDelayUninstall/SetDelayUninstall tests.
+//
+
+TEST_F(SetupRegistryProtectedUserTest, ShouldDelayUninstall) {
+  TestShouldDelayUninstall();
+}
+
+TEST_F(SetupRegistryProtectedUserTest, SetDelayUninstall) {
+  TestSetDelayUninstall();
+}
+
+TEST_F(SetupRegistryProtectedMachineTest, ShouldDelayUninstall) {
+  TestShouldDelayUninstall();
+}
+
+TEST_F(SetupRegistryProtectedMachineTest, SetDelayUninstall) {
+  TestSetDelayUninstall();
+}
+
+//
 // StopGoogleUpdateAndWait tests.
 //
 // These are "large" tests.
@@ -1637,16 +1154,16 @@
 // Using a previous build ensures that the shutdown event hasn't changed.
 // There is no test directly that this version can shutdown itself, but that is
 // much less likely to break.
-// The tests also start 1.0.x and 1.1.x legacy instances.
 // The Succeeds tests will fail if any processes that don't listen to the
 // shutdown event are running.
 //
 
-TEST_F(SetupUserTest, StopGoogleUpdateAndWait_Succeeds) {
+// TODO(omaha3): Make these tests pass.
+TEST_F(SetupUserTest, DISABLED_StopGoogleUpdateAndWait_Succeeds) {
   StopGoogleUpdateAndWaitSucceedsTest();
 }
 
-TEST_F(SetupMachineTest, StopGoogleUpdateAndWait_Succeeds) {
+TEST_F(SetupMachineTest, DISABLED_StopGoogleUpdateAndWait_Succeeds) {
   StopGoogleUpdateAndWaitSucceedsTest();
 }
 
@@ -1671,34 +1188,32 @@
 
 TEST_F(SetupMachineTest,
        StopGoogleUpdateAndWait_MachineHandoffWorkerRunningAsUser) {
-  LaunchProcessAndExpectStopGoogleUpdateAndWaitTimesOut(
+  LaunchProcessAndExpectStopGoogleUpdateAndWaitKillsProcess(
       false,
-      _T("/handoff \"needsadmin=True\""),
-      COMMANDLINE_MODE_HANDOFF_INSTALL);
+      _T("/handoff \"needsadmin=True\""));
 }
 
+// Process mode is unknown because Omaha 3 does not recognize IG.
 TEST_F(SetupMachineTest,
-       StopGoogleUpdateAndWait_MachineInstallGoogleUpdateWorkerRunningAsUser) {
-  LaunchProcessAndExpectStopGoogleUpdateAndWaitTimesOut(
+       StopGoogleUpdateAndWait_MachineLegacyInstallGoogleUpdateWorkerRunningAsUser) {   // NOLINT
+  LaunchProcessAndExpectStopGoogleUpdateAndWaitKillsProcess(
       false,
-      _T("/ig \"needsadmin=True\""),
-      COMMANDLINE_MODE_IG);
+      _T("/ig \"needsadmin=True\""));
 }
 
 TEST_F(SetupMachineTest,
        StopGoogleUpdateAndWait_UserHandoffWorkerRunningAsSystem) {
-  LaunchProcessAndExpectStopGoogleUpdateAndWaitTimesOut(
+  LaunchProcessAndExpectStopGoogleUpdateAndWaitKillsProcess(
       true,
-      _T("/handoff \"needsadmin=False\""),
-      COMMANDLINE_MODE_HANDOFF_INSTALL);
+      _T("/handoff \"needsadmin=False\""));
 }
 
+// Process mode is unknown because Omaha 3 does not recognize IG.
 TEST_F(SetupMachineTest,
-       StopGoogleUpdateAndWait_UserInstallGoogleUpdateWorkerRunningAsSystem) {
-  LaunchProcessAndExpectStopGoogleUpdateAndWaitTimesOut(
+       StopGoogleUpdateAndWait_UserLegacyInstallGoogleUpdateWorkerRunningAsSystem) {   // NOLINT
+  LaunchProcessAndExpectStopGoogleUpdateAndWaitKillsProcess(
       true,
-      _T("/ig \"needsadmin=False\""),
-      COMMANDLINE_MODE_IG);
+      _T("/ig \"needsadmin=False\""));
 }
 
 TEST_F(SetupUserTest, TerminateCoreProcesses_NoneRunning) {
@@ -1728,1090 +1243,4 @@
   TestTerminateCoreProcessesWithBothTypesRunningAndOtherProcesses();
 }
 
-TEST_F(SetupUserTest, AcquireLegacySetupLocks_LegacyExesWait) {
-  AcquireLegacySetupLocksTest();
-}
-
-TEST_F(SetupMachineTest, AcquireLegacySetupLocks_LegacyExesWait) {
-  AcquireLegacySetupLocksTest();
-}
-
-TEST_F(SetupRegistryProtectedUserTest, PersistUpdateErrorInfo) {
-  PersistUpdateErrorInfo(is_machine_, 0x98765432, 77, _T("1.2.3.4"));
-
-  DWORD value(0);
-  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_UPDATE,
-                                    kRegValueSelfUpdateErrorCode,
-                                    &value));
-  EXPECT_EQ(0x98765432, value);
-
-  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_UPDATE,
-                                    kRegValueSelfUpdateExtraCode1,
-                                    &value));
-  EXPECT_EQ(77, value);
-
-  CString version;
-  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_UPDATE,
-                                    kRegValueSelfUpdateVersion,
-                                    &version));
-  EXPECT_FALSE(version.IsEmpty());
-  EXPECT_STREQ(_T("1.2.3.4"), version);
-}
-
-TEST_F(SetupRegistryProtectedMachineTest, PersistUpdateErrorInfo) {
-  PersistUpdateErrorInfo(is_machine_, 0x98765430, 0x12345678, _T("2.3.4.5"));
-
-  DWORD value(0);
-  EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE,
-                                    kRegValueSelfUpdateErrorCode,
-                                    &value));
-  EXPECT_EQ(0x98765430, value);
-
-  EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE,
-                                    kRegValueSelfUpdateExtraCode1,
-                                    &value));
-  EXPECT_EQ(0x12345678, value);
-
-  CString version;
-  EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_UPDATE,
-                                    kRegValueSelfUpdateVersion,
-                                    &version));
-  EXPECT_FALSE(version.IsEmpty());
-  EXPECT_STREQ(_T("2.3.4.5"), version);
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       ReadAndClearUpdateErrorInfo_KeyDoesNotExist) {
-  DWORD self_update_error_code(0);
-  DWORD self_update_extra_code1(0);
-  CString self_update_version;
-  ExpectAsserts expect_asserts;
-  EXPECT_FALSE(Setup::ReadAndClearUpdateErrorInfo(false,
-                                                  &self_update_error_code,
-                                                  &self_update_extra_code1,
-                                                  &self_update_version));
-}
-
-TEST_F(SetupRegistryProtectedMachineTest,
-       ReadAndClearUpdateErrorInfo_KeyDoesNotExist) {
-  DWORD self_update_error_code(0);
-  DWORD self_update_extra_code1(0);
-  CString self_update_version;
-  ExpectAsserts expect_asserts;
-  EXPECT_FALSE(Setup::ReadAndClearUpdateErrorInfo(true,
-                                                  &self_update_error_code,
-                                                  &self_update_extra_code1,
-                                                  &self_update_version));
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       ReadAndClearUpdateErrorInfo_UpdateErrorCodeDoesNotExist) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(USER_REG_UPDATE));
-  DWORD self_update_error_code(0);
-  DWORD self_update_extra_code1(0);
-  CString self_update_version;
-  EXPECT_FALSE(Setup::ReadAndClearUpdateErrorInfo(false,
-                                                  &self_update_error_code,
-                                                  &self_update_extra_code1,
-                                                  &self_update_version));
-}
-
-TEST_F(SetupRegistryProtectedMachineTest,
-       ReadAndClearUpdateErrorInfo_UpdateErrorCodeDoesNotExist) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(MACHINE_REG_UPDATE));
-  DWORD self_update_error_code(0);
-  DWORD self_update_extra_code1(0);
-  CString self_update_version;
-  EXPECT_FALSE(Setup::ReadAndClearUpdateErrorInfo(true,
-                                                  &self_update_error_code,
-                                                  &self_update_extra_code1,
-                                                  &self_update_version));
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       ReadAndClearUpdateErrorInfo_AllValuesPresent) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    kRegValueSelfUpdateErrorCode,
-                                    static_cast<DWORD>(0x87654321)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    kRegValueSelfUpdateExtraCode1,
-                                    static_cast<DWORD>(55)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    kRegValueSelfUpdateVersion,
-                                    _T("0.2.4.8")));
-
-  DWORD self_update_error_code(0);
-  DWORD self_update_extra_code1(0);
-  CString self_update_version;
-  EXPECT_TRUE(Setup::ReadAndClearUpdateErrorInfo(false,
-                                                 &self_update_error_code,
-                                                 &self_update_extra_code1,
-                                                 &self_update_version));
-
-  EXPECT_EQ(0x87654321, self_update_error_code);
-  EXPECT_EQ(55, self_update_extra_code1);
-  EXPECT_STREQ(_T("0.2.4.8"), self_update_version);
-}
-
-TEST_F(SetupRegistryProtectedMachineTest,
-       ReadAndClearUpdateErrorInfo_AllValuesPresent) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    kRegValueSelfUpdateErrorCode,
-                                    static_cast<DWORD>(0x87654321)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    kRegValueSelfUpdateExtraCode1,
-                                    static_cast<DWORD>(55)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    kRegValueSelfUpdateVersion,
-                                    _T("0.2.4.8")));
-
-  DWORD self_update_error_code(0);
-  DWORD self_update_extra_code1(0);
-  CString self_update_version;
-  EXPECT_TRUE(Setup::ReadAndClearUpdateErrorInfo(true,
-                                                 &self_update_error_code,
-                                                 &self_update_extra_code1,
-                                                 &self_update_version));
-
-  EXPECT_EQ(0x87654321, self_update_error_code);
-  EXPECT_EQ(55, self_update_extra_code1);
-  EXPECT_STREQ(_T("0.2.4.8"), self_update_version);
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       ReadAndClearUpdateErrorInfo_ValuesPresentInMachineOnly) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    kRegValueSelfUpdateErrorCode,
-                                    static_cast<DWORD>(0x87654321)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    kRegValueSelfUpdateExtraCode1,
-                                    static_cast<DWORD>(55)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    kRegValueSelfUpdateVersion,
-                                    _T("0.2.4.8")));
-
-  DWORD self_update_error_code(0);
-  DWORD self_update_extra_code1(0);
-  CString self_update_version;
-  ExpectAsserts expect_asserts;
-  EXPECT_FALSE(Setup::ReadAndClearUpdateErrorInfo(false,
-                                                  &self_update_error_code,
-                                                  &self_update_extra_code1,
-                                                  &self_update_version));
-  // Clean up HKLM, which isn't overridden.
-  EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE,
-                                       kRegValueSelfUpdateErrorCode));
-  EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE,
-                                       kRegValueSelfUpdateExtraCode1));
-  EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE,
-                                       kRegValueSelfUpdateVersion));
-}
-
-TEST_F(SetupOfflineInstallerTest, ValidOfflineInstaller) {
-  CString guid_string = _T("{CDABE316-39CD-43BA-8440-6D1E0547AEE6}");
-
-  CString offline_manifest_path(guid_string);
-  offline_manifest_path += _T(".gup");
-  offline_manifest_path = ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                          offline_manifest_path);
-  ASSERT_SUCCEEDED(File::Copy(
-      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                      _T("server_manifest_one_app.xml")),
-      offline_manifest_path,
-      false));
-
-  CString installer_exe = _T("foo_installer.exe");
-  CString tarred_installer_path;
-  tarred_installer_path.Format(_T("%s.%s"), installer_exe, guid_string);
-  tarred_installer_path = ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                          tarred_installer_path);
-
-  ASSERT_SUCCEEDED(File::Copy(
-      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                      _T("GoogleUpdate.exe")),
-      tarred_installer_path,
-      false));
-
-  CommandLineArgs args;
-  CommandLineAppArgs app1;
-  app1.app_guid = StringToGuid(guid_string);
-  args.extra.apps.push_back(app1);
-  CString target_location = ConcatenatePath(
-                                app_util::GetCurrentModuleDirectory(),
-                                _T("offline_test"));
-
-  ASSERT_TRUE(CallCopyOfflineFiles(args, target_location));
-
-  CString target_manifest = ConcatenatePath(target_location,
-                                            guid_string + _T(".gup"));
-  EXPECT_TRUE(File::Exists(target_manifest));
-  CString target_file = ConcatenatePath(
-      ConcatenatePath(target_location, guid_string), installer_exe);
-  EXPECT_TRUE(File::Exists(target_file));
-
-  EXPECT_SUCCEEDED(DeleteDirectory(target_location));
-  EXPECT_SUCCEEDED(File::Remove(tarred_installer_path));
-  EXPECT_SUCCEEDED(File::Remove(offline_manifest_path));
-}
-
-TEST_F(SetupOfflineInstallerTest, NoOfflineInstaller) {
-  CString guid_string = _T("{CDABE316-39CD-43BA-8440-6D1E0547AEE6}");
-  CommandLineArgs args;
-  CommandLineAppArgs app1;
-  app1.app_guid = StringToGuid(guid_string);
-  args.extra.apps.push_back(app1);
-  CString target_location = ConcatenatePath(
-                                app_util::GetCurrentModuleDirectory(),
-                                _T("offline_test"));
-
-  EXPECT_FALSE(CallCopyOfflineFiles(args, target_location));
-}
-
-TEST_F(SetupOfflineInstallerTest, ValidCopyOfflineFilesForGuid) {
-  CString guid_string = _T("{CDABE316-39CD-43BA-8440-6D1E0547AEE6}");
-
-  CString offline_manifest_path(guid_string);
-  offline_manifest_path += _T(".gup");
-  offline_manifest_path = ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                          offline_manifest_path);
-  ASSERT_SUCCEEDED(File::Copy(
-      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                      _T("server_manifest_one_app.xml")),
-      offline_manifest_path,
-      false));
-
-  CString installer_exe = _T("foo_installer.exe");
-  CString tarred_installer_path;
-  tarred_installer_path.Format(_T("%s.%s"), installer_exe, guid_string);
-  tarred_installer_path = ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                          tarred_installer_path);
-
-  ASSERT_SUCCEEDED(File::Copy(
-      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                      _T("GoogleUpdate.exe")),
-      tarred_installer_path,
-      false));
-
-  CString target_location = ConcatenatePath(
-                                app_util::GetCurrentModuleDirectory(),
-                                _T("offline_test"));
-
-  ASSERT_SUCCEEDED(CallCopyOfflineFilesForGuid(guid_string, target_location));
-
-  CString target_manifest = ConcatenatePath(target_location,
-                                            guid_string + _T(".gup"));
-  EXPECT_TRUE(File::Exists(target_manifest));
-  CString target_file = ConcatenatePath(
-      ConcatenatePath(target_location, guid_string), installer_exe);
-  EXPECT_TRUE(File::Exists(target_file));
-
-  EXPECT_SUCCEEDED(DeleteDirectory(target_location));
-  EXPECT_SUCCEEDED(File::Remove(tarred_installer_path));
-  EXPECT_SUCCEEDED(File::Remove(offline_manifest_path));
-}
-
-TEST_F(SetupOfflineInstallerTest, NoCopyOfflineFilesForGuid) {
-  CString guid_string = _T("{CDABE316-39CD-43BA-8440-6D1E0547AEE6}");
-  CString target_location = ConcatenatePath(
-                                app_util::GetCurrentModuleDirectory(),
-                                _T("offline_test"));
-
-  ASSERT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            CallCopyOfflineFilesForGuid(guid_string, target_location));
-}
-
-// A few tests for the public method. The bulk of the EULA cases are covered by
-// Install() and DoProtectedGoogleUpdateInstall() tests.
-TEST_F(SetupRegistryProtectedMachineTest, SetEulaAccepted_KeyDoesNotExist) {
-  EXPECT_EQ(S_OK, Setup::SetEulaAccepted(true));
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedMachineTest, SetEulaAccepted_ValueDoesNotExist) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(MACHINE_REG_UPDATE));
-  EXPECT_EQ(S_FALSE, Setup::SetEulaAccepted(true));
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedMachineTest, SetEulaAccepted_ExistsZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  EXPECT_SUCCEEDED(Setup::SetEulaAccepted(true));
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
-
-  // ClientState for Google Update (never used) and other apps is not affected.
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                   _T("eulaaccepted"),
-                   &value));
-  EXPECT_EQ(0, value);
-  value = UINT_MAX;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                   _T("eulaaccepted"),
-                   &value));
-  EXPECT_EQ(0, value);
-}
-
-TEST_F(SetupRegistryProtectedUserTest, SetEulaAccepted_KeyDoesNotExist) {
-  EXPECT_EQ(S_OK, Setup::SetEulaAccepted(false));
-  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedUserTest, SetEulaAccepted_ValueDoesNotExist) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(USER_REG_UPDATE));
-  EXPECT_EQ(S_FALSE, Setup::SetEulaAccepted(false));
-  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedUserTest, SetEulaAccepted_ExistsZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  EXPECT_SUCCEEDED(Setup::SetEulaAccepted(false));
-  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
-
-  // ClientState for Google Update (never used) and other apps is not affected.
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                   _T("eulaaccepted"),
-                   &value));
-  EXPECT_EQ(0, value);
-  value = UINT_MAX;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppUserClientStatePath,
-                   _T("eulaaccepted"),
-                   &value));
-  EXPECT_EQ(0, value);
-}
-
-TEST_F(SetupRegistryProtectedMachineTest,
-       Install_EulaNotRequired_UpdateKeyDoesNotExist) {
-  args_.is_eula_required_set = false;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedMachineTest,
-       Install_EulaNotRequired_EulaValueDoesNotExist) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(MACHINE_REG_UPDATE));
-  args_.is_eula_required_set = false;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedMachineTest,
-       Install_EulaNotRequired_EulaValueExistsZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  args_.is_eula_required_set = false;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
-
-  // ClientState for Google Update (never used) and other apps is not affected.
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                   _T("eulaaccepted"),
-                   &value));
-  EXPECT_EQ(0, value);
-  value = UINT_MAX;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                   _T("eulaaccepted"),
-                   &value));
-  EXPECT_EQ(0, value);
-}
-
-TEST_F(SetupRegistryProtectedMachineTest,
-       Install_EulaNotRequired_EulaValueExistsOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  args_.is_eula_required_set = false;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedMachineTest,
-       Install_EulaNotRequired_EulaValueExistsOther) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(8000)));
-  args_.is_eula_required_set = false;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedMachineTest,
-       Install_EulaNotRequired_EulaValueExistsString) {
-  EXPECT_SUCCEEDED(
-      RegKey::SetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), _T("0")));
-  args_.is_eula_required_set = false;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedMachineTest,
-       Install_EulaNotRequired_EulaValueDoesNotExistAlreadyInstalled) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  args_.is_eula_required_set = false;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedMachineTest,
-       Install_EulaNotRequired_EulaValueExistsZeroAlreadyInstalled) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  args_.is_eula_required_set = false;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedMachineTest,
-       DoInstall_SelfInstall_EulaNotRequired_EulaValueExistsZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  SetModeSelfInstall();
-  args_.is_eula_required_set = false;
-  TestDoInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
-}
-
-// Self-update does not clear eulaaccepted. This case should never happen.
-TEST_F(SetupRegistryProtectedMachineTest,
-       DoInstall_SelfUpdate_EulaNotRequired_EulaValueExistsZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  SetModeSelfUpdate();
-  args_.is_eula_required_set = false;
-  TestDoInstallWhileHoldingLock();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-// EULA required is not handled by DoInstall(). It is handled later by
-// DoProtectedGoogleUpdateInstall().
-TEST_F(SetupRegistryProtectedMachineTest,
-       Install_EulaRequired_EulaValueDoesNotExist) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(MACHINE_REG_UPDATE));
-  args_.is_eula_required_set = true;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedMachineTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_UpdateKeyDoesNotExist) {
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-
-  EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedMachineTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueExistsZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-
-  // ClientState for Google Update (never used) and other apps is not affected.
-  value = UINT_MAX;
-  EXPECT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                   _T("eulaaccepted"),
-                   &value));
-  EXPECT_EQ(0, value);
-  value = UINT_MAX;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppMachineClientStatePath,
-                   _T("eulaaccepted"),
-                   &value));
-  EXPECT_EQ(0, value);
-}
-
-// The existing value is ignored if there are not two registered apps. This is
-// an artifact of the implementation and not a requirement.
-TEST_F(SetupRegistryProtectedMachineTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueExistsOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-TEST_F(SetupRegistryProtectedMachineTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueExistsOther) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(8000)));
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-TEST_F(SetupRegistryProtectedMachineTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueExistsString) {
-  EXPECT_SUCCEEDED(
-      RegKey::SetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), _T("0")));
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-// One app is not sufficient for detecting that Google Update is already
-// installed.
-TEST_F(SetupRegistryProtectedMachineTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueDoesNotExistOneAppRegistered) {  // NOLINT
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientsPath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-// Even Google Update registered is not sufficient for detecting that Google
-// Update is already installed.
-TEST_F(SetupRegistryProtectedMachineTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueDoesNotExistGoogleUpdateRegistered) {  // NOLINT
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-// Important: The existing state is not changed because two apps are registered.
-TEST_F(SetupRegistryProtectedMachineTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueDoesNotExistTwoAppsRegistered) {  // NOLINT
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientsPath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2MachineClientsPath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
-}
-
-// The existing state is not changed because Google Update is already
-// installed, but there is no way to differentiate this from writing 0.
-TEST_F(SetupRegistryProtectedMachineTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueExistsZeroTwoAppsRegistered) {  // NOLINT
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientsPath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2MachineClientsPath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-// The existing state is not changed because Google Update is already installed.
-TEST_F(SetupRegistryProtectedMachineTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueExistsOneTwoAppsRegistered) {  // NOLINT
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppMachineClientsPath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2MachineClientsPath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(1, value);
-}
-
-TEST_F(SetupRegistryProtectedMachineTest,
-       DoProtectedGoogleUpdateInstall_SelfInstall_EulaRequired_UpdateKeyDoesNotExist) {  // NOLINT
-  SetModeSelfInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-// Self-update does not set eulaaccepted. This case should never happen.
-TEST_F(SetupRegistryProtectedMachineTest,
-       DoProtectedGoogleUpdateInstall_SelfUpdate_EulaRequired_UpdateKeyDoesNotExist) {  // NOLINT
-  SetModeSelfUpdate();
-  args_.is_eula_required_set = true;
-  ExpectAsserts expect_asserts;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_UPDATE, _T("eulaaccepted")));
-}
-
-// EULA not required is not handled by DoProtectedGoogleUpdateInstall(). It
-// would have already been handled by DoInstall().
-TEST_F(SetupRegistryProtectedMachineTest,
-       DoProtectedGoogleUpdateInstall_EulaNotRequired_EulaValueExistsZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  SetModeInstall();
-  args_.is_eula_required_set = false;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(MACHINE_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       Install_EulaNotRequired_UpdateKeyDoesNotExist) {
-  args_.is_eula_required_set = false;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       Install_EulaNotRequired_EulaValueDoesNotExist) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(USER_REG_UPDATE));
-  args_.is_eula_required_set = false;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       Install_EulaNotRequired_EulaValueExistsZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  args_.is_eula_required_set = false;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
-
-  // ClientState for Google Update (never used) and other apps is not affected.
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                   _T("eulaaccepted"),
-                   &value));
-  EXPECT_EQ(0, value);
-  value = UINT_MAX;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppUserClientStatePath,
-                   _T("eulaaccepted"),
-                   &value));
-  EXPECT_EQ(0, value);
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       Install_EulaNotRequired_EulaValueExistsOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  args_.is_eula_required_set = false;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       Install_EulaNotRequired_EulaValueExistsOther) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(8000)));
-  args_.is_eula_required_set = false;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       Install_EulaNotRequired_EulaValueExistsString) {
-  EXPECT_SUCCEEDED(
-      RegKey::SetValue(USER_REG_UPDATE, _T("eulaaccepted"), _T("0")));
-  args_.is_eula_required_set = false;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       Install_EulaNotRequired_EulaValueDoesNotExistAlreadyInstalled) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  args_.is_eula_required_set = false;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       Install_EulaNotRequired_EulaValueExistsZeroAlreadyInstalled) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  args_.is_eula_required_set = false;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       DoInstall_SelfInstall_EulaNotRequired_EulaValueExistsZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  SetModeSelfInstall();
-  args_.is_eula_required_set = false;
-  TestDoInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
-}
-
-// Self-update does not clear eulaaccepted. This case should never happen.
-TEST_F(SetupRegistryProtectedUserTest,
-       DoInstall_SelfUpdate_EulaNotRequired_EulaValueExistsZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  SetModeSelfUpdate();
-  args_.is_eula_required_set = false;
-  TestDoInstallWhileHoldingLock();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-// EULA required is not handled by DoInstall(). It is handled later by
-// DoProtectedGoogleUpdateInstall().
-TEST_F(SetupRegistryProtectedUserTest,
-       Install_EulaRequired_EulaValueDoesNotExist) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(USER_REG_UPDATE));
-  args_.is_eula_required_set = true;
-  TestInstallWhileHoldingLock();
-  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_UpdateKeyDoesNotExist) {
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueExistsZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientStatePath,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-
-  // ClientState for Google Update (never used) and other apps is not affected.
-  value = UINT_MAX;
-  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                   _T("eulaaccepted"),
-                   &value));
-  EXPECT_EQ(0, value);
-  value = UINT_MAX;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kAppUserClientStatePath,
-                   _T("eulaaccepted"),
-                   &value));
-  EXPECT_EQ(0, value);
-}
-
-// The existing value is ignored if there are not two registered apps. This is
-// an artifact of the implementation and not a requirement.
-TEST_F(SetupRegistryProtectedUserTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueExistsOne) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueExistsOther) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(8000)));
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueExistsString) {
-  EXPECT_SUCCEEDED(
-      RegKey::SetValue(USER_REG_UPDATE, _T("eulaaccepted"), _T("0")));
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-// One app is not sufficient for detecting that Google Update is already
-// installed.
-TEST_F(SetupRegistryProtectedUserTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueDoesNotExistOneAppRegistered) {  // NOLINT
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientsPath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-// Even Google Update registered is not sufficient for detecting that Google
-// Update is already installed.
-TEST_F(SetupRegistryProtectedUserTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueDoesNotExistGoogleUpdateRegistered) {  // NOLINT
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-// Important: The existing state is not changed because two apps are registered.
-TEST_F(SetupRegistryProtectedUserTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueDoesNotExistTwoAppsRegistered) {  // NOLINT
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientsPath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2UserClientsPath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
-}
-
-// The existing state is not changed because Google Update is already
-// installed, but there is no way to differentiate this from writing 0.
-TEST_F(SetupRegistryProtectedUserTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueExistsZeroTwoAppsRegistered) {  // NOLINT
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientsPath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2UserClientsPath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-// The existing state is not changed because Google Update is already installed.
-TEST_F(SetupRegistryProtectedUserTest,
-       DoProtectedGoogleUpdateInstall_EulaRequired_EulaValueExistsOneTwoAppsRegistered) {  // NOLINT
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kAppUserClientsPath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2UserClientsPath,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  SetModeInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(1, value);
-}
-
-TEST_F(SetupRegistryProtectedUserTest,
-       DoProtectedGoogleUpdateInstall_SelfInstall_EulaRequired_UpdateKeyDoesNotExist) {  // NOLINT
-  SetModeSelfInstall();
-  args_.is_eula_required_set = true;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
-// Self-update does not set eulaaccepted. This case should never happen.
-TEST_F(SetupRegistryProtectedUserTest,
-       DoProtectedGoogleUpdateInstall_SelfUpdate_EulaRequired_UpdateKeyDoesNotExist) {  // NOLINT
-  SetModeSelfUpdate();
-  args_.is_eula_required_set = true;
-  ExpectAsserts expect_asserts;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_FALSE(RegKey::HasValue(USER_REG_UPDATE, _T("eulaaccepted")));
-}
-
-// EULA not required is not handled by DoProtectedGoogleUpdateInstall(). It
-// would have already been handled by DoInstall().
-TEST_F(SetupRegistryProtectedUserTest,
-       DoProtectedGoogleUpdateInstall_EulaNotRequired_EulaValueExistsZero) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  SetModeInstall();
-  args_.is_eula_required_set = false;
-  TestDoProtectedGoogleUpdateInstallWithFailingSetupFiles();
-  DWORD value = UINT_MAX;
-  EXPECT_SUCCEEDED(
-      RegKey::GetValue(USER_REG_UPDATE, _T("eulaaccepted"), &value));
-  EXPECT_EQ(0, value);
-}
-
 }  // namespace omaha
diff --git a/site_scons/site_init.py b/site_scons/site_init.py
index c6631a6..84858e4 100644
--- a/site_scons/site_init.py
+++ b/site_scons/site_init.py
@@ -15,6 +15,8 @@
 # limitations under the License.
 # ========================================================================
 
+"""Omaha site init for SCons."""
+
 # Nothing to see here.  This file just needs to exist so that SCons can load
 # the tools in the site_scons subdirectory.  Customization of environments and
 # their methods should be done via those tools.
diff --git a/site_scons/site_tools/omaha_builders.py b/site_scons/site_tools/omaha_builders.py
index e2aa53d..5b15cd0 100644
--- a/site_scons/site_tools/omaha_builders.py
+++ b/site_scons/site_tools/omaha_builders.py
@@ -17,13 +17,22 @@
 
 """Omaha builders tool for SCons."""
 
+import os.path
 import SCons.Action
 import SCons.Builder
 import SCons.Tool
 
 
-def _EnablePrecompile(env, target_name):
-  """Enable use of precompiled headers for target_name."""
+def EnablePrecompile(env, target_name):
+  """Enable use of precompiled headers for target_name.
+
+  Args:
+    env: The environment.
+    target_name: Name of component.
+
+  Returns:
+    The pch .obj file.
+  """
   if env.Bit('use_precompiled_headers'):
     # We enable all warnings on all levels. The goal is to fix the code that
     # we have written and to programmatically disable the warnings for the
@@ -61,7 +70,17 @@
     return [pch_output[1]]
 
 
-def _SignDotNetManifest(env, target, unsigned_manifest):
+def SignDotNetManifest(env, target, unsigned_manifest):
+  """Signs a .NET manifest.
+
+  Args:
+    env: The environment.
+    target: Name of signed manifest.
+    unsigned_manifest: Unsigned manifest.
+
+  Returns:
+    Output node list from env.Command().
+  """
   sign_manifest_cmd = ('@mage -Sign $SOURCE -ToFile $TARGET -TimestampUri '
                        'http://timestamp.verisign.com/scripts/timstamp.dll ')
 
@@ -85,61 +104,280 @@
   return signed_manifest
 
 
-_all_in_one_unittest_sources = []
-_all_in_one_unittest_libs = set()
+#
+# Custom Library and Program builders.
+#
+# These builders have additional cababilities, including enabling precompiled
+# headers when appropriate and signing DLLs and EXEs.
+#
+
+# TODO(omaha): Make all build files use these builders instead of Hammer's.
+# This will eliminate many lines in build.scons files related to enabling
+# precompiled header and signing binaries.
 
 
-def _OmahaUnittest(env,  # pylint: disable-msg=C6409
-                   unused_name,
-                   source,
-                   LIBS=None,
-                   all_in_one=True):
+def _ConditionallyEnablePrecompile(env, target_name, *args, **kwargs):
+  """Enables precompiled headers for target_name when appropriate.
+
+  Enables precompiled headers if they are enabled for the build unless
+  use_pch_default = False. This requires that the source files are specified in
+  sources or in a list as the first argument after target_name.
+
+  Args:
+    env: Environment in which we were called.
+    target_name: Name of the build target.
+    args: Positional arguments.
+    kwargs: Keyword arguments.
+  """
+  use_pch_default = kwargs.get('use_pch_default', True)
+
+  if use_pch_default and env.Bit('use_precompiled_headers'):
+    pch_output = env.EnablePrecompile(target_name)
+
+    # Search the keyworded list first.
+    for key in ['source', 'sources', 'input', 'inputs']:
+      if key in kwargs:
+        kwargs[key] += pch_output
+        return
+
+    # If the keyword was not found, assume the sources are the first argument in
+    # the non-keyworded list.
+    if args:
+      args[0].append(pch_output[0])
+
+
+def ComponentStaticLibrary(env, lib_name, *args, **kwargs):
+  """Pseudo-builder for static library.
+
+  Enables precompiled headers if they are enabled for the build unless
+  use_pch_default = False. This requires that the source files are specified in
+  sources or in a list as the first argument after lib_name.
+
+  Args:
+    env: Environment in which we were called.
+    lib_name: Static library name.
+    args: Positional arguments.
+    kwargs: Keyword arguments.
+
+  Returns:
+    Output node list from env.ComponentLibrary().
+  """
+  _ConditionallyEnablePrecompile(env, lib_name, *args, **kwargs)
+
+  return env.ComponentLibrary(lib_name, *args, **kwargs)
+
+
+# TODO(omaha): Add signing.
+def ComponentDll(env, lib_name, *args, **kwargs):
+  """Pseudo-builder for DLL.
+
+  Enables precompiled headers if they are enabled for the build unless
+  use_pch_default = False. This requires that the source files are specified in
+  sources or in a list as the first argument after lib_name.
+
+  Args:
+    env: Environment in which we were called.
+    lib_name: DLL name.
+    args: Positional arguments.
+    kwargs: Keyword arguments.
+
+  Returns:
+    Output node list from env.ComponentLibrary().
+  """
+  env.Append(COMPONENT_STATIC=False)
+
+  _ConditionallyEnablePrecompile(env, lib_name, *args, **kwargs)
+
+  return env.ComponentLibrary(lib_name, *args, **kwargs)
+
+
+# TODO(omaha): Add signing.
+def ComponentSignedProgram(env, prog_name, *args, **kwargs):
+  """Pseudo-builder for signed EXEs.
+
+  Enables precompiled headers if they are enabled for the build unless
+  use_pch_default = False. This requires that the source files are specified in
+  sources or in a list as the first argument after prog_name.
+
+  Args:
+    env: Environment in which we were called.
+    prog_name: Executable name.
+    args: Positional arguments.
+    kwargs: Keyword arguments.
+
+  Returns:
+    Output node list from env.ComponentProgram().
+  """
+  _ConditionallyEnablePrecompile(env, prog_name, *args, **kwargs)
+
+  return env.ComponentProgram(prog_name, *args, **kwargs)
+
+
+# TODO(omaha): Put these in a tools/ directory instead of staging.
+def ComponentTool(env, prog_name, *args, **kwargs):
+  """Pseudo-builder for utility programs that do not need to be signed.
+
+  Enables precompiled headers if they are enabled for the build unless
+  use_pch_default = False. This requires that the source files are specified in
+  sources or in a list as the first argument after prog_name.
+
+  Args:
+    env: Environment in which we were called.
+    prog_name: Executable name.
+    args: Positional arguments.
+    kwargs: Keyword arguments.
+
+  Returns:
+    Output node list from env.ComponentProgram().
+  """
+  _ConditionallyEnablePrecompile(env, prog_name, *args, **kwargs)
+
+  return env.ComponentProgram(prog_name, *args, **kwargs)
+
+
+#
+# Unit Test Builders
+#
+
+
+def OmahaUnittest(env,  # pylint: disable-msg=C6409
+                  name,
+                  source,
+                  LIBS=None,
+                  all_in_one=True,
+                  COMPONENT_TEST_SIZE='large',
+                  is_small_tests_using_resources=False):
   """Declares a new unit test.
 
   Args:
     env: The environment.
-    unused_name: Name of the unit test.
+    name: Name of the unit test.
     source: Sources for the unittest.
     LIBS: Any libs required for the unit test.
     all_in_one: If true, the test will be added to an executable containing
         all tests.
+    COMPONENT_TEST_SIZE: small, medium, or large.
+    is_small_tests_using_resources: True if COMPONENT_TEST_SIZE='small' and
+        the test requires resources, such as strings.
+
+  If !all_in_one and COMPONENT_TEST_SIZE is 'small', a main is automatically
+  provided. Otherwise, one must be provided in source or LIBS. The small main
+  is selected based on is_small_tests_using_resources.
 
   Returns:
-    Nothing.
+    Output node list from env.ComponentTestProgram().
+
+  Raises:
+      Exception: Invalid combination of arguments.
   """
-  source = env.Flatten(source)
+  test_env = env.Clone()
+
+  source = test_env.Flatten(source)
+
+  if COMPONENT_TEST_SIZE != 'small' and is_small_tests_using_resources:
+    raise Exception('is_small_tests_using_resources set for non-small test.')
+
   if all_in_one:
-    _all_in_one_unittest_sources.extend(env.File(source))
+    test_env['all_in_one_unittest_sources'].extend(test_env.File(source))
     if LIBS:
-      _all_in_one_unittest_libs.update(env.File(env.Flatten(LIBS)))
-    # TODO(omaha): this should return the name of the all-in-one unit test.
+      test_env['all_in_one_unittest_libs'].update(
+          test_env.File(test_env.Flatten(LIBS)))
+    # TODO(omaha): Get the node list automatically.
+    if 'HAMMER_RUNS_TESTS' in os.environ.keys():
+      test_program_dir = '$TESTS_DIR'
+    else:
+      test_program_dir = '$STAGING_DIR'
+    output = [os.path.join(test_program_dir, 'omaha_unittest.exe'),
+              os.path.join(test_program_dir, 'omaha_unittest.pdb')]
   else:
-    # TODO(omaha): implement this.
-    raise NotImplementedError('Want to volunteer?')
+    test_env.FilterOut(LINKFLAGS=['/NODEFAULTLIB', '/SUBSYSTEM:WINDOWS'])
+    if LIBS:
+      test_env.Append(
+          LIBS=test_env.Flatten(LIBS),
+      )
+    # TODO(omaha): Let's try to eliminate this giant list of Win32 .libs here.
+    # They are generally dependencies of Omaha base, common, or net; it makes
+    # more sense for unit test authors to stay aware of dependencies and pass
+    # them in as part of the LIBS argument.
+    test_env.Append(
+        CPPPATH=[
+            '$MAIN_DIR/third_party/gmock/include',
+            '$MAIN_DIR/third_party/gtest/include',
+        ],
+        LIBS=[
+            '$LIB_DIR/base',
+            '$LIB_DIR/gmock',
+            '$LIB_DIR/gtest',
+            ('atls', 'atlsd')[test_env.Bit('debug')],
+
+            # Required by base/process.h, which is used by unit_test.cc.
+            'psapi',
+
+            # Required by omaha_version.h, which is used by omaha_unittest.cc.
+            'version',
+
+            # Rquired if base/utils.h, which is used by several modules used by
+            # omaha_unittest.cc, is used.
+            'netapi32',
+            'rasapi32',
+            'shlwapi',
+            'wtsapi32',
+        ],
+    )
+
+    if COMPONENT_TEST_SIZE == 'small':
+      if is_small_tests_using_resources:
+        test_env.Append(LIBS=['$LIB_DIR/unittest_base_small_with_resources'])
+      else:
+        test_env.Append(LIBS=['$LIB_DIR/unittest_base_small'])
+
+    if env.Bit('use_precompiled_headers'):
+      source += test_env.EnablePrecompile(name)
+
+    # Set environment variables specific to the tests.
+    for env_var in os.environ:
+      if (not env_var in test_env['ENV'] and
+          (env_var.startswith('GTEST_') or env_var.startswith('OMAHA_TEST_'))):
+        test_env['ENV'][env_var] = os.environ[env_var]
+
+    output = test_env.ComponentTestProgram(
+        name,
+        source + ['$OBJ_ROOT/testing/run_as_invoker.res'],
+        COMPONENT_TEST_SIZE=COMPONENT_TEST_SIZE,
+    )
+
+  # Add a manual dependency on the resource file used by omaha_unittest.cc to
+  # ensure it is always available before the test runs, which could be during
+  # the build.
+  test_env.Depends(output, '$TESTS_DIR/goopdateres_en.dll')
+
+  return output
 
 
-def _GetAllInOneUnittestSources(unused_env):
+def GetAllInOneUnittestSources(env):
   """Returns a list of source files for the all-in-one unit test.
 
   Args:
-    unused_env: the environment.
+    env: The environment.
 
   Returns:
     A list of sources for the all-in-one unit test.
   """
-  return _all_in_one_unittest_sources
+  return env['all_in_one_unittest_sources']
 
 
-def _GetAllInOneUnittestLibs(unused_env):
+def GetAllInOneUnittestLibs(env):
   """Returns a list of libs to be linked into the all-in-one unit test.
 
   Args:
-    unused_env: the environment.
+    env: The environment.
 
   Returns:
-    A set of libs for the all-in-one unit test.
+    A list of libs for the all-in-one unit test.
   """
-  return list(_all_in_one_unittest_libs)
+  # Sort to prevent spurious rebuilds caused by indeterminate ordering of a set.
+  return sorted(env['all_in_one_unittest_libs'],
+                key=SCons.Node.FS.Base.get_abspath)
 
 
 # If a .idl file does not result in any generated proxy code (no foo_p.c and
@@ -154,15 +392,52 @@
   return (filter(IsNonProxyGeneratedFile, t), source)
 
 
+def IsCoverageBuild(env):
+  """Returns true if this is a coverage build.
+
+  Args:
+    env: The environment.
+
+  Returns:
+    whether this is a coverage build.
+  """
+  return 'coverage' in env.subst('$BUILD_TYPE')
+
+
+def CopyFileToDirectory(env, target, source):
+  """Copies the file to the directory using the DOS copy command.
+
+  In general, Replicate() should be used, but there are specific cases where
+  an explicit copy is required.
+
+  Args:
+    env: The environment.
+    target: The target directory.
+    source: The full path to the source file.
+
+  Returns:
+    Output node list from env.Command().
+  """
+  (_, source_filename) = os.path.split(source)
+  return env.Command(target=os.path.join(target, source_filename),
+                     source=source,
+                     action='@copy /y $SOURCE $TARGET')
+
+
 # NOTE: SCons requires the use of this name, which fails gpylint.
 def generate(env):  # pylint: disable-msg=C6409
   """SCons entry point for this tool."""
-  env.AddMethod(_EnablePrecompile, 'EnablePrecompile')
-  env.AddMethod(_SignDotNetManifest, 'SignDotNetManifest')
-  # These are not used by Omaha 2.
-  #env.AddMethod(_OmahaUnittest, 'OmahaUnittest')
-  #env.AddMethod(_GetAllInOneUnittestSources, 'GetAllInOneUnittestSources')
-  #env.AddMethod(_GetAllInOneUnittestLibs, 'GetAllInOneUnittestLibs')
+  env.AddMethod(EnablePrecompile)
+  env.AddMethod(SignDotNetManifest)
+  env.AddMethod(ComponentStaticLibrary)
+  env.AddMethod(ComponentDll)
+  env.AddMethod(ComponentSignedProgram)
+  env.AddMethod(ComponentTool)
+  env.AddMethod(OmahaUnittest)
+  env.AddMethod(GetAllInOneUnittestSources)
+  env.AddMethod(GetAllInOneUnittestLibs)
+  env.AddMethod(IsCoverageBuild)
+  env.AddMethod(CopyFileToDirectory)
 
   env['MIDLNOPROXYCOM'] = ('$MIDL $MIDLFLAGS /tlb ${TARGETS[0]} '
                            '/h ${TARGETS[1]} /iid ${TARGETS[2]} '
diff --git a/site_scons/site_tools/wix.py b/site_scons/site_tools/wix.py
index dc7aa1a..6dfd9fa 100644
--- a/site_scons/site_tools/wix.py
+++ b/site_scons/site_tools/wix.py
@@ -8,7 +8,7 @@
 """
 
 #
-# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
 #
 # Permission is hereby granted, free of charge, to any person obtaining
 # a copy of this software and associated documentation files (the
@@ -30,14 +30,13 @@
 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #
 
-__revision__ = "src/engine/SCons/Tool/wix.py 3603 2008/10/10 05:46:45 scons"
+__revision__ = "src/engine/SCons/Tool/wix.py 3897 2009/01/13 06:45:54 scons"
 
 import SCons.Builder
 import SCons.Action
 import os
 import string
 
-
 def generate(env):
     """Add Builders and construction variables for WiX to an Environment."""
     if not exists(env):
@@ -49,23 +48,34 @@
 
     env['WIXLIGHTFLAGS'].append( '-nologo' )
     env['WIXLIGHTCOM'] = "$WIXLIGHT $WIXLIGHTFLAGS -out ${TARGET} ${SOURCES}"
-    env['WIXSRCSUFFIX'] = '.wxs'
+
+#BEGIN_OMAHA_ADDITION
+# Necessary to build multiple MSIs from a single .wxs without explicitly
+# building the .wixobj.
+# Follows the convention of obj file prefixes in other tools, such as
+# OBJPREFIX in msvc.py.
     env['WIXOBJPREFIX'] = ''
-    env['WIXOBJSUFFIX'] = '.wxiobj'
-    env['WIXMSIPREFIX'] = ''
-    env['WIXMSISUFFIX'] = '.msi'
+#END_OMAHA_ADDITION
 
     object_builder = SCons.Builder.Builder(
         action      = '$WIXCANDLECOM',
+#BEGIN_OMAHA_ADDITION
         prefix      = '$WIXOBJPREFIX',
-        suffix      = '$WIXOBJSUFFIX',
-        src_suffix  = '$WIXSRCSUFFIX')
+#END_OMAHA_ADDITION
+#BEGIN_OMAHA_CHANGE
+# The correct/default suffix is .wixobj, not .wxiobj.
+#       suffix      = '.wxiobj',
+        suffix      = '.wixobj',
+#END_OMAHA_CHANGE
+        src_suffix  = '.wxs')
 
     linker_builder = SCons.Builder.Builder(
         action      = '$WIXLIGHTCOM',
-        prefix      = '$WIXMSIPREFIX',
-        suffix      = '$WIXMSISUFFIX',
-        src_suffix  = '$WIXOBJSUFFIX',
+#BEGIN_OMAHA_CHANGE
+# The correct/default suffix is .wixobj, not .wxiobj.
+#       src_suffix  = '.wxiobj',
+        src_suffix  = '.wixobj',
+#END_OMAHA_CHANGE
         src_builder = object_builder)
 
     env['BUILDERS']['WiX'] = linker_builder
@@ -73,15 +83,19 @@
 def exists(env):
     env['WIXCANDLE'] = 'candle.exe'
     env['WIXLIGHT']  = 'light.exe'
-    env['WIXLIGHTFLAGS'] = []
 
+#BEGIN_OMAHA_CHANGE
+#   # try to find the candle.exe and light.exe tools and 
     # try to find the candle.exe and light.exe tools and
+#END_OMAHA_CHANGE
     # add the install directory to light libpath.
-    # for path in os.environ['PATH'].split(os.pathsep).
-    # also look in env['ENV']['PATH'] first.
-    wix_paths = string.split(env['ENV'].get('PATH', ''), os.pathsep)
-    wix_paths += string.split(os.environ['PATH'], os.pathsep)
-    for path in wix_paths:
+#BEGIN_OMAHA_CHANGE
+
+    # For backwards compatibility, search PATH environment variable for tools.
+#   #for path in os.environ['PATH'].split(os.pathsep):
+#   for path in string.split(os.environ['PATH'], os.pathsep):
+    for path in os.environ['PATH'].split(os.pathsep):
+#END_OMAHA_CHANGE
         if not path:
             continue
 
@@ -94,19 +108,47 @@
 
         # search for the tools in the PATH environment variable
         try:
-            if env['WIXCANDLE'] in os.listdir(path) and\
-               env['WIXLIGHT']  in os.listdir(path):
-                   env.PrependENVPath('PATH', path)
-                   extra_files = [os.path.join(i) for i in [
-                       'wixui.wixlib',
-                       'WixUI_en-us.wxl']]
-                   if (os.path.exists(extra_files[0]) and
-                       os.path.exists(extra_files[1])):
-                       env.Append(WIXLIGHTFLAGS=[
-                           extra_files[0],
-                           '-loc', extra_files[1]])
-                   return 1
+#BEGIN_OMAHA_CHANGE
+#           if env['WIXCANDLE'] in os.listdir(path) and\
+#              env['WIXLIGHT']  in os.listdir(path):
+            files = os.listdir(path)
+            if (env['WIXCANDLE'] in files and
+                env['WIXLIGHT']  in files):
+#                  env.PrependENVPath('PATH', path)
+                env.PrependENVPath('PATH', path)
+#                  env['WIXLIGHTFLAGS'] = [ os.path.join( path, 'wixui.wixlib' ),
+#                                           '-loc',
+#                                           os.path.join( path, 'WixUI_en-us.wxl' ) ]
+#                  return 1
+                break
+#END_OMAHA_CHANGE
         except OSError:
             pass # ignore this, could be a stale PATH entry.
 
+#BEGIN_OMAHA_ADDITION
+    # Search for the tools in the SCons paths.
+    for path in env['ENV'].get('PATH', '').split(os.pathsep):
+        try:
+            files = os.listdir(path)
+            if (env['WIXCANDLE'] in files and
+                env['WIXLIGHT']  in files):
+                # The following is for compatibility with versions prior to 3.
+                # Version 3 no longer has these files.
+                extra_files = [os.path.join(i) for i in ['wixui.wixlib',
+                                                         'WixUI_en-us.wxl']]
+                if (os.path.exists(extra_files[0]) and
+                    os.path.exists(extra_files[1])):
+                    env.Append(WIXLIGHTFLAGS=[
+                        extra_files[0],
+                        '-loc', extra_files[1]])
+                else:
+                    # Create empty variable so the append in generate() works.
+                    env.Append(WIXLIGHTFLAGS=[])
+
+                # WiX was found.
+                return 1
+        except OSError:
+            pass # ignore this, could be a stale PATH entry.
+#END_OMAHA_ADDITION
+
     return None
diff --git "a/standalone/manifests/\1737DD1EF7B-D075-47c0-BD51-F624ED87CCF0\175.gup" "b/standalone/manifests/\1737DD1EF7B-D075-47c0-BD51-F624ED87CCF0\175.gup"
index e80fecd..c87fb9c 100644
--- "a/standalone/manifests/\1737DD1EF7B-D075-47c0-BD51-F624ED87CCF0\175.gup"
+++ "b/standalone/manifests/\1737DD1EF7B-D075-47c0-BD51-F624ED87CCF0\175.gup"
@@ -1,12 +1,18 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
+<response protocol="3.0">
   <app appid="{7DD1EF7B-D075-47c0-BD51-F624ED87CCF0}" status="ok">
-    <updatecheck Version="${INSTALLER_VERSION}"
-                 arguments="standalone installer arguments"
-                 codebase="http://dl.google.com/foo/${INSTALLER_VERSION}/SaveArguments.exe"
-                 hash="${INSTALLER_HASH}"
-                 needsadmin="true"
-                 size="${INSTALLER_SIZE}"
-                 status="ok"/>
+    <updatecheck status="ok">
+      <urls>
+        <url codebase="http://dl.google.com/foo/${INSTALLER_VERSION}/"/>
+      </urls>
+      <manifest>
+        <packages>
+          <package name="SaveArguments.exe" hash="${INSTALLER_HASH}" size="${INSTALLER_SIZE}" required="true"/>
+        </packages>
+        <actions>
+          <action event="install" run="SaveArguments.exe" arguments="standalone installer arguments" needsadmin="true"/>
+        </actions>
+       </manifest>
+    </updatecheck>
   </app>
-</gupdate>
+</response>
diff --git "a/standalone/manifests/\173D6B08267-B440-4C85-9F79-E195E80D9937\175.gup" "b/standalone/manifests/\173D6B08267-B440-4C85-9F79-E195E80D9937\175.gup"
index 9b6fcce..245257d 100644
--- "a/standalone/manifests/\173D6B08267-B440-4C85-9F79-E195E80D9937\175.gup"
+++ "b/standalone/manifests/\173D6B08267-B440-4C85-9F79-E195E80D9937\175.gup"
@@ -1,12 +1,18 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
+<response protocol="3.0">
   <app appid="{D6B08267-B440-4C85-9F79-E195E80D9937}" status="ok">
-    <updatecheck Version="${INSTALLER_VERSION}"
-                 arguments="REGISTER_LAUNCH_COMMAND=1"
-                 codebase="http://dl.google.com/foo/test_foo_v${INSTALLER_VERSION}.msi"
-                 hash="${INSTALLER_HASH}"
-                 needsadmin="true"
-                 size="${INSTALLER_SIZE}"
-                 status="ok"/>
+    <updatecheck status="ok">
+      <urls>
+        <url codebase="http://dl.google.com/foo/"/>
+      </urls>
+      <manifest version="${INSTALLER_VERSION}">
+        <packages>
+          <package name="test_foo_v${INSTALLER_VERSION}.msi" hash="${INSTALLER_HASH}" size="${INSTALLER_SIZE}" required="true"/>
+        </packages>
+        <actions>
+          <action event="install" run="test_foo_v${INSTALLER_VERSION}.msi" arguments="REGISTER_LAUNCH_COMMAND=1" needsadmin="true"/>
+        </actions>
+       </manifest>
+    </updatecheck>
   </app>
-</gupdate>
+</response>
diff --git a/standalone/standalone_installer.py b/standalone/standalone_installer.py
index 59af2b4..2800ae6 100644
--- a/standalone/standalone_installer.py
+++ b/standalone/standalone_installer.py
@@ -43,7 +43,6 @@
   def __init__(self,
                friendly_product_name,
                exe_base_name,
-               version,
                binaries,
                msi_base_name,
                custom_tag_params,
@@ -53,7 +52,6 @@
                installers_txt_filename):
     self.friendly_product_name = friendly_product_name
     self.exe_base_name = exe_base_name
-    self.version = version
     self.binaries = binaries
     self.msi_base_name = msi_base_name
     self.custom_tag_params = custom_tag_params
@@ -82,7 +80,6 @@
     if len(line) and not line.startswith('#'):
       (friendly_product_name,
        exe_base_name,
-       version,
        binaries,
        msi_base_name,
        custom_tag_params,
@@ -92,7 +89,6 @@
        installers_txt_filename) = eval(line)
       installer = OfflineInstaller(friendly_product_name,
                                    exe_base_name,
-                                   version,
                                    binaries,
                                    msi_base_name,
                                    custom_tag_params,
@@ -143,6 +139,77 @@
     )
 
 
+def _GenerateUpdateResponseFile(target, source, env):
+  """Generate GUP file based on a list of sources.
+
+  Don't call function directly from this script. source may be
+  generated as part of build. Use function as action in env.Command.
+
+  Args:
+    target: Target GUP file name.
+    source: A list of source files. Source files should be listed as manifest1,
+      binary1, manifest2, binary2 and so on. Order is important so that
+      manifests and installers can be differentiated and 'INSTALLER_VERSIONS'
+      can be applied properly.
+    env: Construct environment. This environment must contain environment
+      variable 'INSTALLER_VERSIONS', which contains a list of versions for
+      corresponding binaries in source and should be in same order.
+
+  Raises:
+    Exception: When build encounters error.
+  """
+  xml_header = '<?xml version="1.0" encoding="UTF-8"?>\n'
+  response_header = '<response protocol="3.0">'
+  response_footer = '</response>'
+
+  local_env = env.Clone()
+
+  version_list = local_env['INSTALLER_VERSIONS']
+  if not version_list:
+    raise Exception('INSTALLER_VERSIONS is missing from environment.')
+
+  manifest_content_list = [xml_header, response_header]
+  for file_index in xrange(0, len(source), 2):
+    source_manifest_path = source[file_index]
+    binary_path = source[file_index + 1]
+    size = os.stat(binary_path.abspath).st_size
+    installer_file = open(binary_path.abspath, mode='rb')
+    data = array.array('B')
+    data.fromfile(installer_file, size)
+    installer_file.close()
+    s = sha.new(data)
+    hash_value = base64.b64encode(s.digest())
+
+    manifest_file = open(source_manifest_path.abspath)
+    manifest_content = manifest_file.read()
+    response_body_start_index = manifest_content.find('<response')
+    if response_body_start_index < 0:
+      raise Exception('GUP file does not contain response element.')
+    # + 1 to include the closing > in header
+    response_body_start_index = manifest_content.find(
+        '>', response_body_start_index)
+    if response_body_start_index < 0:
+      raise Exception('GUP file does not contain response element.')
+    response_body_start_index += 1
+    response_body_end_index = manifest_content.find(
+        '</response>', response_body_start_index)
+    if response_body_end_index < 0:
+      raise Exception('GUP file is not in valid response format.')
+    local_env['INSTALLER_SIZE'] = str(size)
+    local_env['INSTALLER_HASH'] = hash_value
+    local_env['INSTALLER_VERSION'] = version_list[file_index/2]
+    manifest_content_list.append(local_env.subst(
+        manifest_content[response_body_start_index:response_body_end_index],
+        raw=1))
+    manifest_file.close()
+  manifest_content_list.append(response_footer)
+
+  manifest_content_str = ''.join(manifest_content_list)
+  output_file = open(target[0].abspath, 'w')
+  output_file.write(manifest_content_str)
+  output_file.close()
+
+
 def BuildOfflineInstaller(
     env,
     offline_installer,
@@ -155,7 +222,7 @@
     is_official=False,
     installers_sources_path='$MAIN_DIR/installers',
     enterprise_installers_sources_path='$MAIN_DIR/enterprise/installer',
-    lzma_path='$MAIN_DIR/third_party/lzma/lzma.exe',
+    lzma_path='$MAIN_DIR/third_party/lzma/v4_65/files/lzma.exe',
     resmerge_path='$MAIN_DIR/tools/resmerge'):
   """Builds the standalone installers specified by offline_installer.
 
@@ -184,7 +251,6 @@
   Raises:
     Exception: Missing or invalid data specified in offline_installer.
   """
-
   standalone_installer_base_name = offline_installer.exe_base_name
   if not standalone_installer_base_name:
     raise Exception('Product name not specified.')
@@ -210,63 +276,53 @@
   additional_payload_contents = []
   if not offline_installer.binaries:
     raise Exception('No binaries specified.')
-  manifests_to_dump = []
+
+  manifest_target = ''
+  manifest_source = []
+  version_list = []
   for binary in offline_installer.binaries:
-    (binary_name, guid) = binary
-    if not binary_name or not guid:
-      raise Exception('Binary specification incomplete.')
+    (version, installer_path, guid) = binary
+    if not installer_path or not guid or not version:
+      raise Exception('Application specification is incomplete.')
 
-    output_file = os.path.basename(binary_name) + '.' + guid
-
-    def _SubstituteStringsInUpdateResponseFile(target, source, env):
-      # Don't call funtion directly from this script. source may be
-      # generated as part of build. Use function as action in env.Command.
-      source_manifest_path = source[0]
-      binary_path = source[1]
-      size = os.stat(binary_path.abspath).st_size
-      installer_file = open(binary_path.abspath, mode='rb')
-      data = array.array('B')
-      data.fromfile(installer_file, size)
-      installer_file.close()
-      s = sha.new(data)
-      hash_value = base64.b64encode(s.digest())
-      manifest_content = open(source_manifest_path.abspath).read()
-      env = env.Clone(
-          INSTALLER_SIZE='%d' % size,
-          INSTALLER_HASH=hash_value
-      )
-      output_file = open(target[0].abspath, 'w')
-      output_file.write(env.subst(manifest_content, raw=1))
-      output_file.close()
-
-    # Place the generated manifests in a subdirectory. This allows a single
-    # build to generate installers for multiple versions of the same app.
-    manifest_file_path = env.Command(
-        target=guid + offline_installer.version + '/' + guid + '.gup',
-        source=[
-            manifest_files_path + '/' + guid + '.gup',
-            output_file,
-        ],
-        action=[_SubstituteStringsInUpdateResponseFile],
-        # TODO(omaha): This does not work for bundled apps because the
-        # .txt file only has one version for all apps in the bundle.
-        INSTALLER_VERSION=offline_installer.version,
-    )
-
+    installer_path_modified = os.path.basename(installer_path) + '.' + guid
     # Have to use Command('copy') here instead of replicate, as the
     # file is being renamed in the process.
     env.Command(
-        target=output_file,
-        source=binary_name,
+        target=installer_path_modified,
+        source=installer_path,
         action='@copy /y $SOURCES $TARGET'
     )
 
-    manifests_to_dump += [manifest_file_path]
-    additional_payload_contents += [output_file, manifest_file_path]
+    manifest_source.extend([
+        manifest_files_path + '/' + guid + '.gup', installer_path_modified])
+    version_list.append(version)
+    additional_payload_contents.append(installer_path_modified)
+
+    # TODO(omaha): Use full guid and version to generate unique string, use
+    #   hash of the unique string as target directory name.
+    manifest_target += guid[0:4] + version
 
     # Log info about the app.
     log_text += '\n\n*** App: ' + guid + ' ***\n'
-    log_text += '\nINSTALLER:\n' + binary_name + '\n'
+    log_text += '\nVersion:' + version + '\n'
+    log_text += '\nINSTALLER:\n' + installer_path + '\n'
+
+  # Place the generated manifests in a subdirectory. This allows a single
+  # build to generate installers for multiple versions of the same app.
+  manifest_target += '/OfflineManifest.gup'
+  manifest_file_path = env.Command(
+      target=manifest_target,
+      source=manifest_source,
+      action=[_GenerateUpdateResponseFile],
+      INSTALLER_VERSIONS=version_list
+      )
+
+  # Use the BCJ2 tool from the official build we're using to generate this
+  # metainstaller, not the current build directory.
+  bcj2_path = omaha_files_path + '/bcj2.exe'
+
+  additional_payload_contents.append(manifest_file_path)
 
   def WriteLog(target, source, env):
     """Writes the log of what is being built."""
@@ -288,7 +344,7 @@
 
   env.Command(
       target='%s/%s' % (output_dir, log_name),
-      source=manifests_to_dump,
+      source=manifest_file_path,
       action=WriteLog,
       write_data=log_text
   )
@@ -307,7 +363,8 @@
       output_dir=output_dir,
       installers_sources_path=installers_sources_path,
       lzma_path=lzma_path,
-      resmerge_path=resmerge_path
+      resmerge_path=resmerge_path,
+      bcj2_path=bcj2_path
   )
 
   standalone_installer_path = '%s/%s' % (output_dir, target_name)
@@ -315,21 +372,23 @@
   # Build an enterprise installer.
   if offline_installer.should_build_enterprise_msi:
     # TODO(omaha): Add support for bundles here and to
-    # BuildEnterpriseInstallerFromStandaloneInstaller().
+    #   BuildEnterpriseInstallerFromStandaloneInstaller().
+    # TODO(omaha): Determine how product_version should be decided for MSI in
+    #   bundle scenarios.
+    # TODO(omaha): custom tag, silent uninstall args, distribution data may need
+    #   to be made per-app.
     if 1 < len(offline_installer.binaries):
       raise Exception('Enterprise installers do not currently support bundles.')
-    (binary_name, product_guid) = offline_installer.binaries[0]
+    (product_version, installer_path, product_guid) = offline_installer.binaries[0]
 
     # Note: msi_base_name should not include version info and cannot change!
-
     friendly_product_name = offline_installer.friendly_product_name
-    product_version = offline_installer.version
     msi_base_name = offline_installer.msi_base_name
     custom_tag_params = offline_installer.custom_tag_params
     silent_uninstall_args = offline_installer.silent_uninstall_args
     msi_installer_data = offline_installer.msi_installer_data
 
-    # Only custom_tag_params is optional.
+    # custom_tag_params and msi_installer_data are optional.
     if (not product_version or not friendly_product_name or not msi_base_name or
         not silent_uninstall_args):
       raise Exception('Field required to build enterprise MSI is missing.')
@@ -347,6 +406,7 @@
                     silent_uninstall_args,
                     msi_installer_data,
                     standalone_installer_path,
+                    omaha_files_path + '/show_error_action.dll',
                     prefix + msi_base_name,
                     enterprise_installers_sources_path,
                     output_dir=output_dir
diff --git a/standalone/standalone_installers.txt b/standalone/standalone_installers.txt
index d692892..6a8f48e 100644
--- a/standalone/standalone_installers.txt
+++ b/standalone/standalone_installers.txt
@@ -10,15 +10,16 @@
 #      easy to differentiate different copies of the standalone installer. The
 #      version properties of the standalone installer have Omaha's version, not
 #      the app's, so the only way to determine the app version is to install it.
-#  * Version of the app (required).
-#  * List of (App Installer Location, App ID) tuples (required).
+#  * List of (App Version, App Installer Location, App ID) tuples (required).
 #  * MSI installer name base (required for MSI) - everything but '.msi'. Cannot change! Thus, should not include version info. Also, the resulting MSI must not be renamed.
 #  * Custom tag parameters for MSI installer (optional). Example: '&brand=FOOB&ap=enterprise'
 #  * Silent uninstall args (required for MSI). Args to add to the default uninstall command line to cause the uninstall to be silent.
 #  * Whether to build an enterprise MSI - True/False.
+#  * MSI Installer data (optional)
 #  * File Containing Tagging Info (optional)
 
-('Test Foo', 'TestFooStandaloneInstaller', '1.0.101.0', [('$STAGING_DIR/unittest_support/test_foo_v1.0.101.0.msi', '{D6B08267-B440-4C85-9F79-E195E80D9937}')], None, None, None, False, '', '$MAIN_DIR/installers/SampleApp_installers.txt')
+('Test Foo', 'TestFooStandaloneInstaller', [('1.0.101.0', '$STAGING_DIR/unittest_support/test_foo_v1.0.101.0.msi', '{D6B08267-B440-4C85-9F79-E195E80D9937}')], None, None, None, False, '', '$MAIN_DIR/installers/SampleApp_installers.txt')
 
 # Not a real installer.
-('Save Arguments', 'SaveArgumentsStandaloneInstaller', '0.1.2.0', [('$STAGING_DIR/unittest_support/SaveArguments.exe', '{7DD1EF7B-D075-47c0-BD51-F624ED87CCF0}')], 'SaveArgumentsStandaloneEnterprise', '', '/silentuninstall', True, '', '')
+('Save Arguments', 'SaveArgumentsStandaloneInstaller', [('0.1.2.0', '$STAGING_DIR/unittest_support/SaveArguments.exe', '{7DD1EF7B-D075-47c0-BD51-F624ED87CCF0}')], 'SaveArgumentsStandaloneEnterprise', '', '/silentuninstall', True, '', '')
+('Bundle_Foo_Argument', 'Bundle_Foo_Argument_StandaloneInstaller', [('0.1.2.0', '$STAGING_DIR/unittest_support/SaveArguments.exe', '{7DD1EF7B-D075-47c0-BD51-F624ED87CCF0}'), ('1.0.101.0', '$STAGING_DIR/unittest_support/test_foo_v1.0.101.0.msi', '{D6B08267-B440-4C85-9F79-E195E80D9937}')], None, None, None, False, '', '')
diff --git a/statsreport/aggregator-win32.cc b/statsreport/aggregator-win32.cc
index c1c8df7..b08468a 100644
--- a/statsreport/aggregator-win32.cc
+++ b/statsreport/aggregator-win32.cc
@@ -78,7 +78,7 @@
 
 void MetricsAggregatorWin32::Aggregate(CountMetric &metric) {
   // do as little as possible if no value
-  uint64 value = metric.Reset();
+  int64 value = metric.Reset();
   if (0 == value)
     return;
 
@@ -86,13 +86,13 @@
     return;
 
   CString name(metric.name());
-  uint64 reg_value = 0;
+  int64 reg_value = 0;
   if (!GetData(count_key_, name, &reg_value)) {
     // TODO(omaha): clean up??
   }
   reg_value += value;
 
-  DWORD err = count_key_.SetBinaryValue(name, &reg_value, sizeof(reg_value));
+  LONG err = count_key_.SetBinaryValue(name, &reg_value, sizeof(reg_value));
 }
 
 void MetricsAggregatorWin32::Aggregate(TimingMetric &metric) {
@@ -115,20 +115,20 @@
     reg_value.maximum = std::max(reg_value.maximum, value.maximum);
   }
 
-  DWORD err = timing_key_.SetBinaryValue(name, &reg_value, sizeof(reg_value));
+  LONG err = timing_key_.SetBinaryValue(name, &reg_value, sizeof(reg_value));
 }
 
 void MetricsAggregatorWin32::Aggregate(IntegerMetric &metric) {
   // do as little as possible if no value
-  uint64 value = metric.value();
+  int64 value = metric.value();
   if (0 == value)
     return;
 
   if (!EnsureKey(kIntegersKeyName, &integer_key_))
     return;
 
-  DWORD err = integer_key_.SetBinaryValue(CString(metric.name()),
-                                          &value, sizeof(value));
+  LONG err = integer_key_.SetBinaryValue(CString(metric.name()),
+                                         &value, sizeof(value));
 }
 
 void MetricsAggregatorWin32::Aggregate(BoolMetric &metric) {
@@ -140,8 +140,8 @@
   if (!EnsureKey(kBooleansKeyName, &bool_key_))
     return;
 
-  DWORD err = bool_key_.SetBinaryValue(CString(metric.name()),
-                                       &value, sizeof(value));
+  LONG err = bool_key_.SetBinaryValue(CString(metric.name()),
+                                      &value, sizeof(value));
 }
 
 } // namespace stats_report
diff --git a/statsreport/aggregator-win32_unittest.cc b/statsreport/aggregator-win32_unittest.cc
index 3a23360..229b5b7 100644
--- a/statsreport/aggregator-win32_unittest.cc
+++ b/statsreport/aggregator-win32_unittest.cc
@@ -22,7 +22,7 @@
 using namespace stats_report;
 
 #define APP_NAME_STRING L"aggregator-win32_unittest"
-#define PREFIX_KEY_STRING L"Software\\Google\\" 
+#define PREFIX_KEY_STRING L"Software\\" _T(SHORT_COMPANY_NAME_ANSI) L"\\"
 #define SUFFIX_KEY_STRING L"\\UsageStats\\Daily"
 #define ROOT_KEY_STRING PREFIX_KEY_STRING APP_NAME_STRING 
 #define KEY_STRING ROOT_KEY_STRING SUFFIX_KEY_STRING
diff --git a/statsreport/build.scons b/statsreport/build.scons
index f8a39b9..febf93d 100644
--- a/statsreport/build.scons
+++ b/statsreport/build.scons
@@ -21,7 +21,8 @@
 
 local_env.Append(
     CCFLAGS = [
-        '/wd4018',  # signed/unsigned mismatch
+        '/we4018',
+        '/we4365',
 
         # enumerator in switch is not explicitly handled by a case label
         '/wd4061',
@@ -38,4 +39,4 @@
     ]
 
 # Build these into a library.
-local_env.ComponentLibrary('statsreport', inputs)
+local_env.ComponentStaticLibrary('statsreport', inputs)
diff --git a/statsreport/const-win32.cc b/statsreport/const-win32.cc
index 265fd25..79106ff 100644
--- a/statsreport/const-win32.cc
+++ b/statsreport/const-win32.cc
@@ -22,8 +22,9 @@
 const wchar_t kCountsKeyName[] = L"Counts";
 const wchar_t kIntegersKeyName[] = L"Integers";
 const wchar_t kBooleansKeyName[] = L"Booleans";
-const wchar_t kStatsKeyFormatString[] = L"Software\\Google\\"
-                                        L"%ws\\UsageStats\\Daily";
+const wchar_t kStatsKeyFormatString[] = L"Software\\"
+                                        _T(SHORT_COMPANY_NAME_ANSI)
+                                        L"\\%ws\\UsageStats\\Daily";
 const wchar_t kLastTransmissionTimeValueName[] = L"LastTransmission";
 
-} // namespace stats_report 
+} // namespace stats_report
diff --git a/statsreport/formatter.cc b/statsreport/formatter.cc
index 3ddd761..6ac4681 100644
--- a/statsreport/formatter.cc
+++ b/statsreport/formatter.cc
@@ -25,17 +25,17 @@
 Formatter::~Formatter() {
 }
 
-void Formatter::AddCount(const char *name, uint64 value) {
+void Formatter::AddCount(const char *name, int64 value) {
   output_ << "&" << name << ":c=" << value;
 }
 
-void Formatter::AddTiming(const char *name, uint64 num, uint64 avg,
-                          uint64 min, uint64 max) {
+void Formatter::AddTiming(const char *name, int64 num, int64 avg,
+                          int64 min, int64 max) {
   output_ << "&" << name << ":t=" << num << ";"
                                   << avg << ";" << min << ";" << max;
 }
 
-void Formatter::AddInteger(const char *name, uint64 value) {
+void Formatter::AddInteger(const char *name, int64 value) {
   output_ << "&" << name << ":i=" << value;
 }
 
diff --git a/statsreport/formatter.h b/statsreport/formatter.h
index 1c1db6c..d219d89 100644
--- a/statsreport/formatter.h
+++ b/statsreport/formatter.h
@@ -38,10 +38,10 @@
 
   /// Add typed metrics to the output string
   /// @{
-  void AddCount(const char *name, uint64 value);
-  void AddTiming(const char *name, uint64 num, uint64 avg, uint64 min,
-                 uint64 max);
-  void AddInteger(const char *name, uint64 value);
+  void AddCount(const char *name, int64 value);
+  void AddTiming(const char *name, int64 num, int64 avg, int64 min,
+                 int64 max);
+  void AddInteger(const char *name, int64 value);
   void AddBoolean(const char *name, bool value);
   /// @}
 
diff --git a/statsreport/metrics.cc b/statsreport/metrics.cc
index 1c80bf9..6cf6a97 100644
--- a/statsreport/metrics.cc
+++ b/statsreport/metrics.cc
@@ -15,8 +15,7 @@
 //
 // Implements metrics and metrics collections
 #include "omaha/statsreport/metrics.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/synchronized.h"
+#include "omaha/base/synchronized.h"
 
 namespace stats_report {
 // Make sure global stats collection is placed in zeroed storage so as to avoid
@@ -87,16 +86,14 @@
   }
 }
 
-void IntegerMetricBase::Set(uint64 value) {
+void IntegerMetricBase::Set(int64 value) {
   ObjectLock lock(this);
-  ASSERT1(value != kint64max);
   value_ = value;
 }
 
-uint64 IntegerMetricBase::value() const {
+int64 IntegerMetricBase::value() const {
   ObjectLock lock(this);
-  uint64 ret = value_;
-  ASSERT1(ret != kint64max);
+  int64 ret = value_;
   return ret;
 }
 
@@ -110,12 +107,12 @@
   --value_;
 }
 
-void IntegerMetricBase::Add(uint64 value){
+void IntegerMetricBase::Add(int64 value){
   ObjectLock lock(this);
   value_ += value;
 }
 
-void IntegerMetricBase::Subtract(uint64 value) {
+void IntegerMetricBase::Subtract(int64 value) {
   ObjectLock lock(this);
   if (value_ < value)
     value_ = 0;
@@ -123,9 +120,9 @@
     value_ -= value;
 }
 
-uint64 CountMetric::Reset() {
+int64 CountMetric::Reset() {
   ObjectLock lock(this);
-  uint64 ret = value_;
+  int64 ret = value_;
   value_ = 0;
   return ret;
 }
@@ -143,28 +140,28 @@
   return ret;
 }
 
-uint64 TimingMetric::sum() const {
+int64 TimingMetric::sum() const {
   ObjectLock lock(this);
-  uint64 ret = data_.sum;
+  int64 ret = data_.sum;
   return ret;
 }
 
-uint64 TimingMetric::minimum() const {
+int64 TimingMetric::minimum() const {
   ObjectLock lock(this);
-  uint64 ret = data_.minimum;
+  int64 ret = data_.minimum;
   return ret;
 }
 
-uint64 TimingMetric::maximum() const {
+int64 TimingMetric::maximum() const {
   ObjectLock lock(this);
-  uint64 ret = data_.maximum;
+  int64 ret = data_.maximum;
   return ret;
 }
 
-uint64 TimingMetric::average() const {
+int64 TimingMetric::average() const {
   ObjectLock lock(this);
 
-  uint64 ret = 0;
+  int64 ret = 0;
   if (0 == data_.count) {
     DCHECK_EQ(0, data_.sum);
   } else {
@@ -173,7 +170,7 @@
   return ret;
 }
 
-void TimingMetric::AddSample(uint64 time_ms) {
+void TimingMetric::AddSample(int64 time_ms) {
   ObjectLock lock(this);
   if (0 == data_.count) {
     data_.minimum = time_ms;
@@ -188,11 +185,11 @@
   data_.sum += time_ms;
 }
 
-void TimingMetric::AddSamples(uint64 count, uint64 total_time_ms) {
+void TimingMetric::AddSamples(int64 count, int64 total_time_ms) {
   if (0 == count)
     return;
 
-  uint64 time_ms = total_time_ms / count;
+  int64 time_ms = total_time_ms / count;
 
   ObjectLock lock(this);
   if (0 == data_.count) {
diff --git a/statsreport/metrics.h b/statsreport/metrics.h
index 87a2369..3603705 100644
--- a/statsreport/metrics.h
+++ b/statsreport/metrics.h
@@ -19,8 +19,8 @@
 
 #include <iterator>
 #include "base/basictypes.h"
-#include "omaha/common/highres_timer-win32.h"
-#include "omaha/common/logging/logging.h"
+#include "omaha/base/highres_timer-win32.h"
+#include "omaha/base/logging/logging.h"
 
 /// Macros to declare & define named & typed metrics.
 /// Put declarations in headers or in cpp files, where you need access
@@ -269,14 +269,14 @@
 class IntegerMetricBase: public MetricBase {
 public:
   /// Sets the current value
-  void Set(uint64 value);
+  void Set(int64 value);
 
   /// Retrieves the current value
-  uint64 value() const;
+  int64 value() const;
 
   void operator ++ ()     { Increment(); }
   void operator ++ (int)  { Increment(); }
-  void operator += (uint64 addend) { Add(addend); }
+  void operator += (int64 addend) { Add(addend); }
 
 protected:
   IntegerMetricBase(const char *name,
@@ -284,16 +284,16 @@
                     MetricCollectionBase *coll)
       : MetricBase(name, type, coll), value_(0) {
   }
-  IntegerMetricBase(const char *name, MetricType type, uint64 value)
+  IntegerMetricBase(const char *name, MetricType type, int64 value)
       : MetricBase(name, type), value_(value) {
   }
 
   void Increment();
   void Decrement();
-  void Add(uint64 value);
-  void Subtract(uint64 value);
+  void Add(int64 value);
+  void Subtract(int64 value);
 
-  uint64 value_;
+  int64 value_;
 
 private:
   DISALLOW_EVIL_CONSTRUCTORS(IntegerMetricBase);
@@ -306,12 +306,12 @@
       : IntegerMetricBase(name, kCountType, coll) {
   }
 
-  CountMetric(const char *name, uint64 value)
+  CountMetric(const char *name, int64 value)
       : IntegerMetricBase(name, kCountType, value) {
   }
 
   /// Nulls the metric and returns the current values.
-  uint64 Reset();
+  int64 Reset();
 
 private:
   DISALLOW_EVIL_CONSTRUCTORS(CountMetric);
@@ -323,9 +323,9 @@
     uint32 count;
     uint32 align; // allow access to the alignment gap between count and sum,
                   // makes it esier to unittest.
-    uint64 sum; // ms
-    uint64 minimum; // ms
-    uint64 maximum; // ms
+    int64 sum; // ms
+    int64 minimum; // ms
+    int64 maximum; // ms
   };
 
   TimingMetric(const char *name, MetricCollectionBase *coll)
@@ -338,14 +338,14 @@
   }
 
   uint32 count() const;
-  uint64 sum() const;
-  uint64 minimum() const;
-  uint64 maximum() const;
-  uint64 average() const;
+  int64 sum() const;
+  int64 minimum() const;
+  int64 maximum() const;
+  int64 average() const;
 
   /// Adds a single sample to the metric
   /// @param time_ms time (in milliseconds) for this sample
-  void AddSample(uint64 time_ms);
+  void AddSample(int64 time_ms);
 
   /// Adds count samples to the metric
   /// @note use this when capturing time over a variable number of items to
@@ -356,7 +356,7 @@
   /// @note if count == 0, no sample will be recorded
   /// @param count number of samples to add
   /// @param total_time_ms the total time consumed by all the "count" samples
-  void AddSamples(uint64 count, uint64 total_time_ms);
+  void AddSamples(int64 count, int64 total_time_ms);
 
   /// Nulls the metric and returns the current values.
   TimingData Reset();
@@ -420,15 +420,15 @@
       : IntegerMetricBase(name, kIntegerType, coll) {
   }
 
-  IntegerMetric(const char *name, uint64 value)
+  IntegerMetric(const char *name, int64 value)
       : IntegerMetricBase(name, kIntegerType, value) {
   }
 
-  void operator = (uint64 value)   { Set(value); }
+  void operator = (int64 value)   { Set(value); }
 
   void operator -- ()     { Decrement(); }
   void operator -- (int)  { Decrement(); }
-  void operator -= (uint64 sub)    { Subtract(sub); }
+  void operator -= (int64 sub)    { Subtract(sub); }
 
 private:
   DISALLOW_EVIL_CONSTRUCTORS(IntegerMetric);
diff --git a/statsreport/persistent_iterator-win32.cc b/statsreport/persistent_iterator-win32.cc
index b9eca5c..5b527e1 100644
--- a/statsreport/persistent_iterator-win32.cc
+++ b/statsreport/persistent_iterator-win32.cc
@@ -78,9 +78,10 @@
 
       // Get the next key and value
       LONG err = ::RegEnumValue(sub_key_, value_index_,
-                    CStrBuf(wide_value_name, value_name_len), &value_name_len,
-                    0, &value_type,
-                    buf, &value_len);
+                     CStrBuf(wide_value_name, static_cast<int>(value_name_len)),
+                     &value_name_len,
+                     0, &value_type,
+                     buf, &value_len);
 
       ++value_index_;
 
@@ -99,10 +100,10 @@
 
         switch (state_) {
          case kCounts:
-          if (value_len != sizeof(uint64))
+          if (value_len != sizeof(int64))
             continue;
           current_value_.reset(new CountMetric(current_value_name_ .GetString(),
-                                          *reinterpret_cast<uint64*>(&buf[0])));
+                                          *reinterpret_cast<int64*>(&buf[0])));
           break;
          case kTimings:
           if (value_len != sizeof(TimingMetric::TimingData))
@@ -111,11 +112,11 @@
                         *reinterpret_cast<TimingMetric::TimingData*>(&buf[0])));
           break;
          case kIntegers:
-          if (value_len != sizeof(uint64))
+          if (value_len != sizeof(int64))
             continue;
           current_value_.reset(new IntegerMetric(
                                       current_value_name_.GetString(),
-                                      *reinterpret_cast<uint64*>(&buf[0])));
+                                      *reinterpret_cast<int64*>(&buf[0])));
           break;
          case kBooleans:
           if (value_len != sizeof(uint32))
diff --git a/testing/build.scons b/testing/build.scons
index 7c4f8f9..7c7c9aa 100644
--- a/testing/build.scons
+++ b/testing/build.scons
@@ -15,13 +15,14 @@
 # limitations under the License.
 # ========================================================================
 
+import os
 
 Import('env')
 
 
 def _AddCommonOptions(local_env):
   local_env['CPPDEFINES'] += [
-      '_ATL_APARTMENT_THREADED',
+      '_ATL_FREE_THREADED',
       'UNITTEST',
       ]
 
@@ -30,9 +31,99 @@
   local_env.FilterOut(LINKFLAGS = ['/SUBSYSTEM:WINDOWS'])
   local_env['LINKFLAGS'] += ['/SUBSYSTEM:CONSOLE']
 
+#=============omaha_unittest Dependencies======================================
+# TODO(omaha): Replace $STAGING_DIR with $TESTS_DIR when HAMMER_RUNS_TESTS
+# becomes the default.
 
-#env.BuildSconscript('runtime')
-env.BuildSConscript('ui')
+# Install files from the testing/unittest_support/ directory.
+unittest_support = env.Replicate('$STAGING_DIR/unittest_support/', [
+    # Files used by the common unit tests.
+    'unittest_support/certificate-with-private-key.pfx',
+    'unittest_support/certificate-without-private-key.cer',
+    'unittest_support/declaration.txt',
+    'unittest_support/manifest.xml',
+
+    # Installer files used by the Install Manager unit tests.
+    'unittest_support/test_foo_v1.0.101.0.msi',
+
+    'unittest_support/GoogleUpdate_corrupted.exe',
+    'unittest_support/GoogleUpdate_now_expired_cert.exe',
+    'unittest_support/GoogleUpdate_old_signature.exe',
+    'unittest_support/GoogleUpdateHelper.msi',
+    'unittest_support/SaveArguments.exe',
+    'unittest_support/SaveArguments_different_ou.exe',
+    'unittest_support/SaveArguments_multiple_cn.exe',
+    'unittest_support/SaveArguments_no_cn.exe',
+    'unittest_support/SaveArguments_OmahaTestSigned.exe',
+    'unittest_support/SaveArguments_unsigned_no_resources.exe',
+    'unittest_support/SaveArguments_unsigned_wrong_markup_size.exe',
+    'unittest_support/SaveArguments_unsigned_wrong_markup_value.exe',
+    'unittest_support/SaveArguments_unsigned_wrong_resource_name.exe',
+    'unittest_support/SaveArguments_wrong_cn.exe',
+
+    # Minidump file for the crash unit test.
+    'unittest_support/minidump.dmp',
+    'unittest_support/minidump.txt',
+
+    # PAC file for testing local PAC file support.
+    'unittest_support/localproxytest.pac',
+
+    # Files used by offline_utils_unittest.
+    'unittest_support/{CDABE316-39CD-43BA-8440-6D1E0547AEE6}.v2.gup',
+    'unittest_support/{CDABE316-39CD-43BA-8440-6D1E0547AEE6}.v3.gup',
+    ])
+
+# Saved versions of Google Update for the Setup tests.
+unittest_support += env.Replicate(
+    '$STAGING_DIR/unittest_support/omaha_1.2.131.7_shell/', [
+    'unittest_support/omaha_1.2.131.7_shell/GoogleUpdate.exe',
+    ])
+unittest_support += env.Replicate(
+    '$STAGING_DIR/unittest_support/omaha_1.2.183.9_shell/', [
+    'unittest_support/omaha_1.2.183.9_shell/GoogleUpdate.exe',
+    ])
+unittest_support += env.Replicate('$STAGING_DIR/unittest_support/omaha_1.2.x/',
+    'unittest_support/omaha_1.2.x/GoogleUpdate.exe'
+    )
+unittest_support += env.Replicate('$STAGING_DIR/unittest_support/omaha_1.3.x/',
+    [ 'unittest_support/omaha_1.3.x/GoogleUpdate.exe',
+      'unittest_support/omaha_1.3.x/goopdate.dll',
+      'unittest_support/omaha_1.3.x/goopdateres_en.dll',
+    ])
+
+# Newer versions of Google Update for the Setup tests.
+#unittest_support += env.Replicate(
+#              '$STAGING_DIR/unittest_support/omaha_1.3.x_newer/',
+#              'unittest_support/omaha_1.2.x_newer/GoogleUpdate.exe')
+
+# Copy longrunning.exe to GoogleUpdate.exe for use in Setup.
+unittest_support += env.Replicate(
+    target='$STAGING_DIR/unittest_support/does_not_shutdown/',
+    source='$MAIN_DIR/testing/unittest_support/LongRunningSilent.exe',
+    REPLICATE_REPLACE=[('LongRunningSilent\\.exe', 'GoogleUpdate.exe')],
+)
+
+# download_cache test files
+loc_guid = 'download_cache_test/{7101D597-3481-4971-AD23-455542964072}'
+unittest_support += env.Replicate(
+                        '$STAGING_DIR/unittest_support/' + loc_guid,
+                        'unittest_support/%s/livelysetup.exe' % loc_guid)
+
+loc_guid = 'download_cache_test/{89640431-FE64-4da8-9860-1A1085A60E13}'
+unittest_support += env.Replicate(
+                        '$STAGING_DIR/unittest_support/' + loc_guid,
+                        'unittest_support/%s/gears-win32-opt.msi' % loc_guid)
+
+#=============General Unit Test Dependencies===================================
+# Many unit tests rely on string resources. omaha_unittest.cc loads them but
+# assumes they are in the same directory as the tests.
+# For coverage builds, use a custom command instead of Replicate() because
+# Replicate() will cause the DLL to be re-instrumented.
+resource_dll = '$STAGING_DIR/goopdateres_en.dll'
+if env.IsCoverageBuild():
+  env.CopyFileToDirectory('$TESTS_DIR', resource_dll)
+else:
+  env.Replicate('$TESTS_DIR', resource_dll)
 
 #=============UnitTests========================================================
 
@@ -40,6 +131,9 @@
 omaha_version_info = env['omaha_versions_info'][0]
 version_string = omaha_version_info.GetVersionString()
 
+run_as_invoker = env.RES('run_as_invoker.res',
+                         '$MAIN_DIR/base/run_as_invoker.rc')
+
 #
 # Builds omaha_unittest
 #
@@ -48,12 +142,63 @@
 
 omaha_unittest_env.FilterOut(LINKFLAGS = ['/NODEFAULTLIB'])
 
+omaha_unittest_libs = [
+    ('atls.lib', 'atlsd.lib')[omaha_unittest_env.Bit('debug')],
+
+    '$LIB_DIR/base.lib',
+    '$LIB_DIR/breakpad.lib',
+    '$LIB_DIR/client.lib',  # TODO(omaha): Might be able to make separate exe.
+    '$LIB_DIR/common.lib',
+    '$LIB_DIR/core.lib',
+    '$LIB_DIR/gmock.lib',
+    '$LIB_DIR/google_update_recovery.lib',
+    '$LIB_DIR/goopdate_lib.lib',
+    '$LIB_DIR/gtest.lib',
+    '$LIB_DIR/logging.lib',
+    '$LIB_DIR/net.lib',
+    '$LIB_DIR/omaha3_idl.lib',
+    '$LIB_DIR/security.lib',
+    '$LIB_DIR/service.lib',
+    '$LIB_DIR/setup.lib',
+    '$LIB_DIR/statsreport.lib',
+    '$LIB_DIR/ui.lib',
+    '$LIB_DIR/unittest_base_large_with_network.lib',
+
+    'advapi32.lib',
+    'bits.lib',
+    'comctl32.lib',
+    'crypt32.lib',
+    'dbghelp.lib',
+    'delayimp.lib',       # For delay loading
+    'iphlpapi.lib',
+    'msi.lib',
+    'mstask.lib',
+    'netapi32.lib',
+    'ole32.lib',
+    'oleaut32.lib',
+    'psapi.lib',
+    'rasapi32.lib',
+    'rpcns4.lib',
+    'rpcrt4.lib',
+    'shlwapi.lib',
+    'taskschd.lib',
+    'urlmon.lib',
+    'userenv.lib',
+    'version.lib',
+    'wbemuuid.lib',
+    'wininet.lib',
+    'wintrust.lib',
+    'ws2_32.lib',
+    'wtsapi32.lib',
+]
+omaha_unittest_libs += omaha_unittest_env.GetAllInOneUnittestLibs()
+
 omaha_unittest_env.Append(
     CPPPATH = [
         '$OBJ_ROOT',                        # Needed for the generated files
-        '$MAIN_DIR/goopdate/resources',     # Needed for success.ico
         '$MAIN_DIR/third_party/breakpad/src',
         '$MAIN_DIR/third_party/c99/include',  # C99 inttypes.h for security
+        '$MAIN_DIR/third_party/gmock/include',
         '$MAIN_DIR/third_party/gtest/include',
         ],
     CCFLAGS = [
@@ -69,51 +214,7 @@
             omaha_version_info.version_patch),
         'OMAHA_BUILD_VERSION_STRING=_T(\\"%s\\")' % version_string,
         ],
-    LIBS = [
-        ('atls.lib', 'atlsd.lib')[omaha_unittest_env.Bit('debug')],
-
-        '$LIB_DIR/breakpad.lib',
-        '$LIB_DIR/common.lib',
-        '$LIB_DIR/core.lib',
-        '$LIB_DIR/goopdate_dll.lib',
-        '$LIB_DIR/google_update_ps.lib',
-        '$LIB_DIR/logging.lib',
-        '$LIB_DIR/net.lib',
-        '$LIB_DIR/google_update_recovery.lib',
-        '$LIB_DIR/repair_goopdate.lib',
-        '$LIB_DIR/security.lib',
-        '$LIB_DIR/service.lib',
-        '$LIB_DIR/setup.lib',
-        '$LIB_DIR/statsreport.lib',
-        '$LIB_DIR/gtest.lib',
-        '$LIB_DIR/worker.lib',
-        '$LIB_DIR/goopdump.lib',
-
-        'advapi32.lib',
-        'comctl32.lib',
-        'crypt32.lib',
-        'dbghelp.lib',
-        'delayimp.lib',       # For delay loading
-        'iphlpapi.lib',
-        'msi.lib',
-        'mstask.lib',
-        'netapi32.lib',
-        'ole32.lib',
-        'oleaut32.lib',
-        'psapi.lib',
-        'rasapi32.lib',
-        'rpcns4.lib',
-        'rpcrt4.lib',
-        'shlwapi.lib',
-        'urlmon.lib',
-        'userenv.lib',
-        'version.lib',
-        'wbemuuid.lib',
-        'wininet.lib',
-        'ws2_32.lib',
-        'wtsapi32.lib',
-        'wintrust.lib',
-        ],
+    LIBS = omaha_unittest_libs,
     LINKFLAGS = [
         '/DELAYLOAD:shlwapi.dll',
         '/DELAYLOAD:shell32.dll',
@@ -131,63 +232,135 @@
         ],
 )
 
+
+# TODO(omaha): Separate this environment as much as appropriate.
+unittest_base_env = omaha_unittest_env.Clone()
+
+unittest_base_env.Append(
+    LIBS = [
+        '$LIB_DIR/common.lib',
+    ],
+)
+
+unittest_base_env.ComponentStaticLibrary(
+  'unittest_base',
+  [ 'omaha_unittest.cc', 'unit_test.cc', ]
+)
+
+unittest_base_env.ComponentStaticLibrary(
+    'unittest_base_large_with_network',
+    [ 'unittest_base.lib',
+      'omaha_unittest_main.cc',
+      'omaha_unittest_network.cc',
+    ]
+)
+
+unittest_base_env.ComponentStaticLibrary(
+    'unittest_base_small',
+    [ 'unittest_base.lib', 'omaha_unittest_main_small_tests.cc', ]
+)
+
+unittest_base_env.ComponentStaticLibrary(
+    'unittest_base_small_with_resources',
+    [ 'unittest_base.lib',
+      'omaha_unittest_main_small_tests_with_resources.cc',
+    ]
+)
+
+
+# TODO(omaha3): Add tests from mainline that were removed during the integrate.
+
 omaha_unittest_inputs = [
-    'omaha_unittest.cc',
-    '../common/app_util_unittest.cc',
-    '../common/apply_tag.cc',
-    '../common/atlassert_unittest.cc',
-    '../common/atl_regexp_unittest.cc',
-    '../common/browser_utils_unittest.cc',
-    '../common/cgi_unittest.cc',
-    '../common/commands_unittest.cc',
-    '../common/disk_unittest.cc',
-    '../common/dynamic_link_kernel32_unittest.cc',
-    '../common/encrypt_test.cc',
-    '../common/error_unittest.cc',
-    '../common/extractor_unittest.cc',
-    '../common/file_reader_unittest.cc',
-    '../common/file_store_unittest.cc',
-    '../common/file_unittest.cc',
-    '../common/firewall_product_detection_unittest.cc',
-    '../common/highres_timer_unittest.cc',
-    '../common/localization_unittest.cc',
-    '../common/lock_ptr_unittest.cc',
-    '../common/logging_unittest.cc',
-    '../common/md5_unittest.cc',
-    '../common/module_utils_unittest.cc',
-    '../common/omaha_version_unittest.cc',
-    '../common/path_unittest.cc',
-    '../common/pe_utils_unittest.cc',
-    '../common/proc_utils_unittest.cc',
-    '../common/process_unittest.cc',
-    '../common/queue_timer_unittest.cc',
-    '../common/reactor_unittest.cc',
-    '../common/reg_key_unittest.cc',
-    '../common/registry_monitor_manager_unittest.cc',
-    '../common/registry_store_unittest.cc',
-    '../common/scoped_impersonation_unittest.cc',
-    '../common/scoped_ptr_cotask_unittest.cc',
-    '../common/serializable_object_unittest.cc',
-    '../common/service_utils_unittest.cc',
-    '../common/sha_unittest.cc',
-    '../common/shell_unittest.cc',
-    '../common/signatures_unittest.cc',
-    '../common/signaturevalidator_unittest.cc',
-    '../common/sta_unittest.cc',
-    '../common/string_unittest.cc',
-    '../common/system_unittest.cc',
-    '../common/system_info_unittest.cc',
-    '../common/thread_pool_unittest.cc',
-    '../common/time_unittest.cc',
-    '../common/timer_unittest.cc',
-    '../common/tr_rand_unittest.cc',
-    '../common/user_info_unittest.cc',
-    '../common/user_rights_unittest.cc',
-    '../common/utils_unittest.cc',
-    '../common/vistautil_unittest.cc',
-    '../common/vista_utils_unittest.cc',
-    '../common/wmi_query_unittest.cc',
-    '../common/xml_utils_unittest.cc',
+    # Base unit tests
+    '../base/app_util_unittest.cc',
+    '../base/apply_tag.cc',
+    '../base/atlassert_unittest.cc',
+    '../base/atl_regexp_unittest.cc',
+    '../base/browser_utils_unittest.cc',
+    '../base/cgi_unittest.cc',
+    '../base/command_line_parser_unittest.cc',
+    '../base/command_line_validator_unittest.cc',
+    '../base/commands_unittest.cc',
+    '../base/disk_unittest.cc',
+    '../base/dynamic_link_kernel32_unittest.cc',
+    '../base/encrypt_test.cc',
+    '../base/error_unittest.cc',
+    '../base/etw_log_writer_unittest.cc',
+    '../base/event_trace_consumer_unittest.cc',
+    '../base/event_trace_controller_unittest.cc',
+    '../base/event_trace_provider_unittest.cc',
+    '../base/extractor_unittest.cc',
+    '../base/file_reader_unittest.cc',
+    '../base/file_store_unittest.cc',
+    '../base/file_unittest.cc',
+    '../base/firewall_product_detection_unittest.cc',
+    '../base/highres_timer_unittest.cc',
+    '../base/localization_unittest.cc',
+    '../base/lock_ptr_unittest.cc',
+    '../base/logging_unittest.cc',
+    '../base/md5_unittest.cc',
+    '../base/module_utils_unittest.cc',
+    '../base/omaha_version_unittest.cc',
+    '../base/path_unittest.cc',
+    '../base/pe_utils_unittest.cc',
+    '../base/proc_utils_unittest.cc',
+    '../base/process_unittest.cc',
+    '../base/queue_timer_unittest.cc',
+    '../base/reactor_unittest.cc',
+    '../base/reg_key_unittest.cc',
+    '../base/registry_monitor_manager_unittest.cc',
+    '../base/registry_store_unittest.cc',
+    '../base/safe_format_unittest.cc',
+    '../base/scoped_impersonation_unittest.cc',
+    '../base/scoped_ptr_cotask_unittest.cc',
+    '../base/serializable_object_unittest.cc',
+    '../base/service_utils_unittest.cc',
+    '../base/shell_unittest.cc',
+    '../base/signatures_unittest.cc',
+    '../base/signaturevalidator_unittest.cc',
+    '../base/sta_unittest.cc',
+    '../base/string_unittest.cc',
+    '../base/synchronized_unittest.cc',
+    '../base/system_unittest.cc',
+    '../base/system_info_unittest.cc',
+    '../base/thread_pool_unittest.cc',
+    '../base/time_unittest.cc',
+    '../base/timer_unittest.cc',
+    '../base/tr_rand_unittest.cc',
+    '../base/user_info_unittest.cc',
+    '../base/user_rights_unittest.cc',
+    '../base/utils_unittest.cc',
+    '../base/vistautil_unittest.cc',
+    '../base/vista_utils_unittest.cc',
+    '../base/wmi_query_unittest.cc',
+    '../base/xml_utils_unittest.cc',
+
+    # Client unit tests
+    '../client/bundle_creator_test.cc',
+    '../client/bundle_installer_unittest.cc',
+    '../client/install_apps_unittest.cc',
+    '../client/install_self_unittest.cc',
+    '../client/install_unittest.cc',
+
+    # Common unit tests
+    '../common/app_registry_utils_unittest.cc',
+    '../common/command_line_unittest.cc',
+    '../common/command_line_builder_unittest.cc',
+    '../common/config_manager_unittest.cc',
+    '../common/event_logger_unittest.cc',
+    '../common/experiment_labels_unittest.cc',
+    '../common/extra_args_parser_unittest.cc',
+    '../common/goopdate_utils_unittest.cc',
+    '../common/lang_unittest.cc',
+    '../common/oem_install_utils_test.cc',
+    '../common/ping_test.cc',
+    '../common/protocol_definition_test.cc',
+    '../common/scheduled_task_utils_unittest.cc',
+    '../common/stats_uploader_unittest.cc',
+    '../common/update_request_unittest.cc',
+    '../common/webplugin_utils_unittest.cc',
+    '../common/web_services_client_unittest.cc',
+    '../common/xml_parser_unittest.cc',
 
     # Core unit tests
     '../core/core_unittest.cc',
@@ -197,27 +370,34 @@
     # Google Update unit tests.
     '../google_update/google_update_unittest.cc',
 
-    # Goopdate unit tests.
-    '../goopdate/command_line_builder_unittest.cc',
-    '../goopdate/command_line_unittest.cc',
-    '../goopdate/command_line_parser_unittest.cc',
-    '../goopdate/command_line_validator_unittest.cc',
-    '../goopdate/config_manager_unittest.cc',
+    # Goopdate unit tests
+    '../goopdate/application_usage_data_unittest.cc',
+    '../goopdate/app_unittest.cc',
+    '../goopdate/app_command_unittest.cc',
+    '../goopdate/app_bundle_unittest.cc',
+    '../goopdate/app_manager_unittest.cc',
+    '../goopdate/app_version_unittest.cc',
     '../goopdate/crash_unittest.cc',
-    '../goopdate/extra_args_parser_unittest.cc',
-    '../goopdate/event_logger_unittest.cc',
+    '../goopdate/cred_dialog_unittest.cc',
+    '../goopdate/download_manager_unittest.cc',
+    '../goopdate/download_complete_ping_event_test.cc',
     '../goopdate/goopdate_unittest.cc',
-    '../goopdate/goopdate_utils_unittest.cc',
-    '../goopdate/goopdate_xml_parser_unittest.cc',
+    '../goopdate/install_manager_unittest.cc',
+    '../goopdate/installer_wrapper_unittest.cc',
     '../goopdate/main_unittest.cc',
+    '../goopdate/model_unittest.cc',
+    '../goopdate/offline_utils_unittest.cc',
+    '../goopdate/string_formatter_unittest.cc',
+    '../goopdate/package_cache_unittest.cc',
     '../goopdate/resource_manager_unittest.cc',
-    '../goopdate/stats_uploader_unittest.cc',
-    '../goopdate/webplugin_utils_unittest.cc',
+    '../goopdate/update_request_utils_unittest.cc',
+    '../goopdate/update_response_utils_unittest.cc',
+    '../goopdate/worker_unittest.cc',
+    '../goopdate/worker_utils_unittest.cc',
 
-    # Net unit tests
+    # Net unit tests.
     '../net/bits_request_unittest.cc',
     '../net/bits_utils_unittest.cc',
-    '../net/browser_request_unittest.cc',
     '../net/cup_request_unittest.cc',
     '../net/cup_utils_unittest.cc',
     '../net/detector_unittest.cc',
@@ -226,14 +406,14 @@
     '../net/network_config_unittest.cc',
     '../net/network_request_unittest.cc',
     '../net/simple_request_unittest.cc',
+    '../net/winhttp_adapter_unittest.cc',
     '../net/winhttp_vtable_unittest.cc',
 
+    # Plugin unit tests are specified in the individual build.scons files.
+
     # Code Red-related unit tests.
-    '../common/google_update_recovery_unittest.cc',
-    '../recovery/repair_exe/mspexecutableelevator_unittest.cc',
-    '../recovery/repair_exe/repair_goopdate_unittest.cc',
-    '../recovery/repair_exe/custom_action/execute_repair_file_unittest.cc',
-    '../recovery/repair_exe/custom_action/execute_repair_file.cc',
+    # Others are specified in the individual build.scons files.
+    '../recovery/client/google_update_recovery_unittest.cc',
 
     # Setup unit tests.
     '../setup/msi_test_utils.cc',
@@ -250,68 +430,88 @@
     '../statsreport/persistent_iterator-win32_unittest.cc',
 
     # Resource files.
-    omaha_unittest_env.RES('../testing/omaha_unittest.rc'),
+    omaha_unittest_env.RES('omaha_unittest.rc'),
+    omaha_unittest_env.RES('omaha_unittest_version.rc'),
+    run_as_invoker,
 
-    # Testing unit tests
-    '../testing/unit_test.cc',
-    '../testing/unittest_debug_helper_unittest.cc',
+    # Testing unit tests.
+    'unit_test_unittest.cc',
+    'unittest_debug_helper_unittest.cc',
+]
+omaha_unittest_inputs += omaha_unittest_env.GetAllInOneUnittestSources()
 
-    # Worker unit tests.
-    '../worker/application_data_unittest.cc',
-    '../worker/application_manager_unittest.cc',
-    '../worker/application_usage_data_unittest.cc',
-    '../worker/download_manager_unittest.cc',
-    '../worker/install_manager_unittest.cc',
-    '../worker/job_creator_unittest.cc',
-    '../worker/job_unittest.cc',
-    '../worker/ping_unittest.cc',
-    '../worker/ping_utils_unittest.cc',
-    '../worker/worker_job_unittest.cc',
-    '../worker/worker_unittest.cc',
-
-    # Goopdump unit tests
-    # TODO(Omaha): This unit test is failing for some reason. Fix and uncomment.
-    # '../tools/goopdump/process_monitor_unittest.cc',
-    '../tools/goopdump/process_commandline_unittest.cc',
-    ]
-
-# Force a rebuild when the version changes.
-# Also force a dependency on the RC files included in omaha_unittest.rc as these
-# do not appear to be picked up automatically.
-omaha_unittest_env.Depends('$OBJ_ROOT/testing/omaha_unittest.res', [
-    '$MAIN_DIR/VERSION',
-    '$MAIN_DIR/goopdate/goopdate.rc',
-    '$MAIN_DIR/goopdate/resources/goopdateres/generated_resources_en.rc',
-    ]
+# Force a rebuild when the version changes and when the header changes since
+# the .rc file scanner does not.
+omaha_unittest_env.Depends(
+    '$OBJ_ROOT/testing/omaha_unittest.res',
+    [ '$MAIN_DIR/VERSION', 'resource.h' ]
 )
 
 # Ensure that obj files don't collide with ones from non-test build
+# TODO(omaha): We should try to avoid rebuilding production code files. Doing so
+# should make this unnecessary.
 omaha_unittest_env['OBJPREFIX'] = omaha_unittest_env['OBJPREFIX'] + 'testing/'
 
-target_name = 'omaha_unittest.exe'
+target_name = 'omaha_unittest'
 
 if env.Bit('use_precompiled_headers'):
   omaha_unittest_inputs += omaha_unittest_env.EnablePrecompile(target_name)
 
+# omaha_unittest can be built as a test program in 'tests\' or a normal program
+# in 'staging\'.
+# TODO(omaha3): Switch entirely to the former.
+if 'HAMMER_RUNS_TESTS' in os.environ.keys():
+  print 'If build fails, you may need to delete test executables from staging\.'
+  # Copy all the files from staging to the tests directory so that the unit
+  # tests can use them.
+  # TODO(omaha3): Consider using the files from staging, though that may cause
+  # staging to be polluted.
+  omaha_unittest_env.Publish(target_name, 'test_input', '$STAGING_DIR/*')
+  # UnitTestHelpersTest.GetLocalAppDataPath requires 'USERNAME'.
+  omaha_unittest_env['ENV']['USERNAME'] = os.environ['USERNAME']
+  # ConfigManagerTest.GetDir requires 'USERPROFILE'.
+  omaha_unittest_env['ENV']['USERPROFILE'] = os.environ['USERPROFILE']
+  # UtilsTest.GetEnvironmentVariableAsString requires 'OS'.
+  omaha_unittest_env['ENV']['OS'] = os.environ['OS']
+  # Tests that use psexec require 'OMAHA_PSEXEC_DIR'
+  omaha_unittest_env['ENV']['OMAHA_PSEXEC_DIR'] = os.environ['OMAHA_PSEXEC_DIR']
 
-omaha_unittest_env.ComponentProgram(target_name, omaha_unittest_inputs)
+  # Set environment variables specific to the tests.
+  for env_var in os.environ:
+    if (not env_var in omaha_unittest_env['ENV'] and
+        (env_var.startswith('GTEST_') or env_var.startswith('OMAHA_TEST_'))):
+      omaha_unittest_env['ENV'][env_var] = os.environ[env_var]
 
+  test = omaha_unittest_env.ComponentTestProgram(target_name,
+                                                 omaha_unittest_inputs,
+                                                 COMPONENT_TEST_SIZE='large')
 
+  # The tests depend on the unittest_support directory.
+  omaha_unittest_env.Depends(test, unittest_support)
 
-# This builds and signs an executable used by unit tests. Because the tests
-# require an official build signed by Google Inc we do not need to build it
-# at other times.
-if env.Bit('build_server'):
-  official_env = env.Clone()
-  official_env.Append(
+  # resource_manager_unittest.cc uses the Russian resources.
+  omaha_unittest_env.Depends(test, '$TESTS_DIR/goopdateres_ru.dll')
+
+else:
+  test = omaha_unittest_env.ComponentProgram(target_name, omaha_unittest_inputs)
+
+  # The tests depend on the unittest_support directory.
+  omaha_unittest_env.Depends(test, unittest_support)
+
+  # resource_manager_unittest.cc uses the Russian resources.
+  omaha_unittest_env.Depends(test, '$STAGING_DIR/goopdateres_ru.dll')
+
+if env.Bit('all'):
+  save_args_env = env.Clone()
+  save_args_env.Append(
       CPPPATH = [
           '$OBJ_ROOT',    # Needed for the generated files
           ],
       LIBS = [
-          ('atls.lib', 'atlsd.lib')[official_env.Bit('debug')],
-          ('libcmt.lib', 'libcmtd.lib')[official_env.Bit('debug')],
-          ('libcpmt.lib', 'libcpmtd.lib')[official_env.Bit('debug')],
-          '$LIB_DIR/common.lib',
+          ('atls.lib', 'atlsd.lib')[save_args_env.Bit('debug')],
+          ('libcmt.lib', 'libcmtd.lib')[save_args_env.Bit('debug')],
+          ('libcpmt.lib', 'libcpmtd.lib')[save_args_env.Bit('debug')],
+          '$LIB_DIR/base.lib',
 
           # These are required by common_lib
           'netapi32.lib',
@@ -332,198 +532,37 @@
           ],
   )
 
-  official_env.FilterOut(LINKFLAGS = ['/SUBSYSTEM:WINDOWS'])
-  official_env['LINKFLAGS'] += ['/SUBSYSTEM:CONSOLE']
+  save_args_env.FilterOut(LINKFLAGS = ['/SUBSYSTEM:WINDOWS'])
+  save_args_env['LINKFLAGS'] += ['/SUBSYSTEM:CONSOLE']
 
   target_name = 'SaveArguments_unsigned'
 
   unsigned_inputs = [
       'save_arguments.cc',
-      official_env.RES('run_as_invoker.res',
-                       '$MAIN_DIR/common/run_as_invoker.rc'),
-      official_env.RES('recovery_markup.res',
-                       '$MAIN_DIR/recovery/recovery_markup.rc'),
-      '$OBJ_ROOT/goopdate/resources/generated_resources_en.res',
+      save_args_env.RES('save_arguments_version.rc'),
+      run_as_invoker,
+      save_args_env.RES('recovery_markup.res',
+                        '$MAIN_DIR/recovery/recovery_markup.rc'),
       ]
+
+  # Force a rebuild when the version changes.
+  save_args_env.Depends('$OBJ_ROOT/testing/save_arguments_version.res',
+                        '$MAIN_DIR/VERSION'
+  )
+
   if env.Bit('use_precompiled_headers'):
-    unsigned_inputs += official_env.EnablePrecompile(target_name)
+    unsigned_inputs += save_args_env.EnablePrecompile(target_name)
 
   # Build the *unsigned* executeable
-  unsigned_output = official_env.ComponentTestProgram(
+  unsigned_output = save_args_env.ComponentTestProgram(
       prog_name=target_name,
       source=unsigned_inputs,
       COMPONENT_TEST_RUNNABLE=False
   )
 
-  signed_output = official_env.SignedBinary(
+  signed_output = save_args_env.SignedBinary(
       target='SaveArguments.exe',
       source=unsigned_output,
   )
 
-  official_env.Replicate('$TESTS_DIR', signed_output)
-
-
-# Copy a few test files needed by the unit test.
-# The files are installed side by side with the unit test executable
-# in the build directory.
-env.Replicate('$STAGING_DIR', [
-    # goopdate_xml_parser_unittest files.
-    '$MAIN_DIR/data/seed_manifest_v1.xml',
-    '$MAIN_DIR/data/seed_manifest.xml',
-    '$MAIN_DIR/data/seed_manifest_v9.xml',
-    '$MAIN_DIR/data/server_manifest.xml',
-    '$MAIN_DIR/data/server_manifest_with_unsupported_tags.xml',
-    '$MAIN_DIR/data/server_manifest_components.xml',
-
-    # goopdate_unittest files.
-    '$MAIN_DIR/data/seed_manifest_with_args.xml',
-    '$MAIN_DIR/data/server_manifest_one_app.xml',
-    '$MAIN_DIR/data/server_manifest_one_app.xml'
-    ])
-
-# Install files from the testing/unittest_support/ directory.
-env.Replicate('$STAGING_DIR/unittest_support/', [
-    # Files used by the common unit tests.
-    'unittest_support/certificate-with-private-key.pfx',
-    'unittest_support/certificate-without-private-key.cer',
-    'unittest_support/declaration.txt',
-    'unittest_support/manifest.xml',
-
-    # Installer files used by the Install Manager unit tests.
-    'unittest_support/test_foo_v1.0.101.0.msi',
-
-    # Files used by the Recovery unit tests.
-    'unittest_support/GoogleUpdate_corrupted.exe',
-    'unittest_support/GoogleUpdate_now_expired_cert.exe',
-    'unittest_support/GoogleUpdate_old_signature.exe',
-    'unittest_support/GoogleUpdateHelper.msi',
-    'unittest_support/SaveArguments.exe',
-    'unittest_support/SaveArguments_different_ou.exe',
-    'unittest_support/SaveArguments_multiple_cn.exe',
-    'unittest_support/SaveArguments_no_cn.exe',
-    'unittest_support/SaveArguments_OmahaTestSigned.exe',
-    'unittest_support/SaveArguments_unsigned_no_resources.exe',
-    'unittest_support/SaveArguments_unsigned_wrong_markup_size.exe',
-    'unittest_support/SaveArguments_unsigned_wrong_markup_value.exe',
-    'unittest_support/SaveArguments_unsigned_wrong_resource_name.exe',
-    'unittest_support/SaveArguments_wrong_cn.exe',
-
-    # Minidump file for the crash unit test.
-    'unittest_support/minidump.dmp',
-    'unittest_support/minidump.txt',
-    ])
-
-# Saved versions of Google Update for the Setup tests.
-env.Replicate('$STAGING_DIR/unittest_support/omaha_1.0.x/', [
-    'unittest_support/omaha_1.0.x/GoogleUpdate.exe',
-    'unittest_support/omaha_1.0.x/goopdate.dll',
-    ])
-env.Replicate('$STAGING_DIR/unittest_support/omaha_1.1.x/', [
-    'unittest_support/omaha_1.1.x/GoogleUpdate.exe',
-    'unittest_support/omaha_1.1.x/goopdate.dll',
-    'unittest_support/omaha_1.1.x/goopdateres_en.dll',
-    ])
-env.Replicate('$STAGING_DIR/unittest_support/omaha_1.2.131.7_shell/', [
-    'unittest_support/omaha_1.2.131.7_shell/GoogleUpdate.exe',
-    ])
-env.Replicate('$STAGING_DIR/unittest_support/omaha_1.2.183.9_shell/', [
-    'unittest_support/omaha_1.2.183.9_shell/GoogleUpdate.exe',
-    ])
-env.Replicate('$STAGING_DIR/unittest_support/omaha_1.2.x/', [
-    'unittest_support/omaha_1.2.x/GoogleUpdate.exe',
-    'unittest_support/omaha_1.2.x/goopdate.dll',
-    'unittest_support/omaha_1.2.x/goopdateres_en.dll',
-    ])
-
-# Newer versions of Google Update for the Setup tests.
-env.Replicate('$STAGING_DIR/unittest_support/omaha_1.2.x_newer/',
-              'unittest_support/omaha_1.2.x_newer/GoogleUpdate.exe')
-
-# Copy longrunning.exe to GoogleUpdate.exe for use in Setup and WorkerJob tests.
-env.Replicate(
-    target='$STAGING_DIR/unittest_support/does_not_shutdown/',
-    source='$MAIN_DIR/testing/unittest_support/LongRunningSilent.exe',
-    REPLICATE_REPLACE=[('LongRunningSilent\\.exe', 'GoogleUpdate.exe')],
-)
-
-# Copy over the files for resource manager test.
-env.Replicate('$STAGING_DIR/unittest_support/omaha_1.2.x_resources/',
-              'unittest_support/omaha_1.2.x_resources/goopdateres_ar.dll')
-env.Replicate('$STAGING_DIR/unittest_support/omaha_1.2.x_resources/',
-              'unittest_support/omaha_1.2.x_resources/goopdateres_bg.dll')
-env.Replicate('$STAGING_DIR/unittest_support/omaha_1.2.x_resources/',
-              'unittest_support/omaha_1.2.x_resources/goopdateres_ca.dll')
-
-
-# download_cache test files
-loc_guid = 'download_cache_test/{7101D597-3481-4971-AD23-455542964072}'
-env.Replicate('$STAGING_DIR/unittest_support/' + loc_guid,
-              'unittest_support/%s/livelysetup.exe' % loc_guid)
-
-loc_guid = 'download_cache_test/{89640431-FE64-4da8-9860-1A1085A60E13}'
-env.Replicate('$STAGING_DIR/unittest_support/' + loc_guid,
-              'unittest_support/%s/gears-win32-opt.msi' % loc_guid)
-
-loc_guid = 'download_cache_test/{C5CC8735-9BE0-45c5-804C-F117E96047C7}'
-env.Replicate('$STAGING_DIR/unittest_support/' + loc_guid,
-              'unittest_support/%s/GoogleUpdateSetup.exe' % loc_guid)
-
-#
-# On Demand system level test.
-#
-ondemandsystem_unittest_env = env.Clone()
-_AddCommonOptions(ondemandsystem_unittest_env)
-
-ondemandsystem_unittest_env.FilterOut(LINKFLAGS = ['/NODEFAULTLIB'])
-ondemandsystem_unittest_env.Append(
-    CPPPATH = [
-        '$MAIN_DIR/third_party/gtest/include',
-        '$OBJ_ROOT',                # Needed for generated files
-        ],
-    CPPDEFINES = [
-        'UNICODE',
-        '_UNICODE'
-        ],
-    LIBS = [
-        ('atls.lib', 'atlsd.lib')[env.Bit('debug')],
-        'comctl32.lib',
-        'crypt32.lib',
-        'Iphlpapi.lib',
-        'mstask.lib',
-        'netapi32.lib',
-        'psapi.lib',
-        'rasapi32.lib',
-        'shlwapi.lib',
-        'urlmon.lib',
-        'userenv.lib',
-        'version.lib',
-        'wininet.lib',
-        'wtsapi32.lib',
-        '$LIB_DIR/common.lib',
-        '$LIB_DIR/goopdate_dll.lib',
-        '$LIB_DIR/logging.lib',
-        '$LIB_DIR/net.lib',
-        '$LIB_DIR/gtest.lib',
-        '$LIB_DIR/statsreport.lib',
-        ],
-)
-
-# Exe is console application
-ondemandsystem_unittest_env.FilterOut(LINKFLAGS = ['/SUBSYSTEM:WINDOWS'])
-ondemandsystem_unittest_env['LINKFLAGS'] += ['/SUBSYSTEM:CONSOLE']
-
-target_name = 'ondemandsystem_unittest'
-
-ondemandsystem_unittest_inputs = [
-    'ondemandsystem_unittest.cc',
-    ]
-if env.Bit('use_precompiled_headers'):
-  ondemandsystem_unittest_inputs += (
-      ondemandsystem_unittest_env.EnablePrecompile(target_name))
-
-
-ondemandsystem_unittest_env.ComponentTestProgram(
-    prog_name=target_name,
-    source=ondemandsystem_unittest_inputs,
-    COMPONENT_TEST_RUNNABLE=False
-)
+  save_args_env.Replicate('$TESTS_DIR', signed_output)
diff --git a/testing/omaha_customization_test.h b/testing/omaha_customization_test.h
new file mode 100644
index 0000000..a4ee955
--- /dev/null
+++ b/testing/omaha_customization_test.h
@@ -0,0 +1,91 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// Common include file for Omaha customization tests.
+
+#ifndef OMAHA_TESTING_OMAHA_CUSTOMIZATION_TEST_H_
+#define OMAHA_TESTING_OMAHA_CUSTOMIZATION_TEST_H_
+
+#include "omaha/testing/unit_test.h"
+
+#ifdef GOOGLE_UPDATE_BUILD
+// For Google Update builds, expect the values to be equal and for the
+// interface names to exist.
+
+// Test fixture for a Google Update-specific interface name.
+// Tests using this fixture must be in the global namespace.
+#define TEST_GU_INT_F(test_fixture, test_name) TEST_F(test_fixture, test_name)
+
+// Expect the values to be equal only in the case of Google Update builds.
+#define EXPECT_GU_EQ(expected, actual) EXPECT_EQ(expected, actual)
+#define EXPECT_GU_STREQ(expected, actual) EXPECT_STREQ(expected, actual)
+#define EXPECT_GU_TRUE(condition) EXPECT_TRUE(condition)
+#define EXPECT_GU_FALSE(condition) EXPECT_FALSE(condition)
+
+// Expect an interface name that is Google Update-specific to have the uuid.
+#define EXPECT_GU_ID_EQ(uuid, interface_id) \
+    EXPECT_STREQ(CString(uuid).MakeUpper(), \
+                 omaha::GuidToString(interface_id));
+
+#else
+// For open source builds, expect the values to not be equal.  (Interfaces
+// should still exist.)
+
+#define TEST_GU_INT_F(test_fixture, test_name) TEST_F(test_fixture, test_name)
+
+#define EXPECT_GU_EQ(expected, actual) EXPECT_NE(expected, actual)
+#define EXPECT_GU_STREQ(expected, actual) EXPECT_STRNE(expected, actual)
+#define EXPECT_GU_TRUE(condition) EXPECT_FALSE(condition)
+#define EXPECT_GU_FALSE(condition) EXPECT_TRUE(condition)
+
+#define EXPECT_GU_ID_EQ(uuid, interface_id) \
+    EXPECT_STRNE(CString(uuid).MakeUpper(), \
+                 omaha::GuidToString(interface_id));
+
+#endif
+
+
+class OmahaCustomizationTypeLibComInterfaceTest : public testing::Test {
+ protected:
+  explicit OmahaCustomizationTypeLibComInterfaceTest(const CString& dll_name)
+      : dll_name_(dll_name),
+        type_lib_(NULL),
+        help_context_(UINT_MAX) {
+  }
+
+  virtual void SetUp() {
+    CString omaha_dll_path;
+    omaha_dll_path.Format(_T("..\\staging\\%s"), dll_name_);
+    EXPECT_SUCCEEDED(::LoadTypeLib(omaha_dll_path, &type_lib_));
+  }
+
+  HRESULT GetDocumentation(int type_description_index) {
+    return type_lib_->GetDocumentation(type_description_index,
+                                      &item_name_,
+                                      &item_doc_string_,
+                                      &help_context_,
+                                      &help_file_);
+  }
+
+  CString dll_name_;
+  ITypeLib* type_lib_;
+
+  CComBSTR item_name_;
+  CComBSTR item_doc_string_;
+  unsigned long help_context_;  // NOLINT
+  CComBSTR help_file_;
+};
+
+#endif  // OMAHA_TESTING_OMAHA_CUSTOMIZATION_TEST_H_
diff --git a/testing/omaha_unittest.cc b/testing/omaha_unittest.cc
index a102e2c..20d8588 100644
--- a/testing/omaha_unittest.cc
+++ b/testing/omaha_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,25 +12,18 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // ========================================================================
-//
-// The entry point for the omaha unit tests.
 
-#include <windows.h>
+#include "omaha/testing/omaha_unittest.h"
 #include <atlpath.h>
 #include <atlstr.h>
-#include <vector>
 #include "base/basictypes.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/file.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/net/network_config.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/file.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/vistautil.h"
 #include "omaha/testing/unit_test.h"
 
 namespace omaha {
@@ -117,6 +110,20 @@
                environment_variables));
 }
 
+// Sets values based on environment variables.
+void ProcessEnvironmentVariables() {
+  if (IsEnvironmentVariableSet(_T("OMAHA_TEST_BUILD_SYSTEM"))) {
+    SetIsBuildSystem();
+  }
+
+  TCHAR psexec_dir[MAX_PATH] = {0};
+  if (::GetEnvironmentVariable(_T("OMAHA_PSEXEC_DIR"),
+                                psexec_dir,
+                                arraysize(psexec_dir))) {
+    SetPsexecDir(psexec_dir);
+  }
+}
+
 bool ParseOmahaArgPsexecDir(const CString& arg) {
   CString psexec_dir_arg_begin;
   psexec_dir_arg_begin.Format(_T("%s="), kOmahaArgPsexecDir);
@@ -148,7 +155,6 @@
   if (_tcsicmp(arg, kOmahaArgIsBuildSystem)) {
     return false;
   }
-
   SetIsBuildSystem();
   return true;
 }
@@ -201,20 +207,37 @@
   return false;
 }
 
-int RunTests(int argc, TCHAR** argv) {
+}  // namespace
+
+// If a test launches or checks other processes, uses shared resources, or uses
+// the network, it is a medium or larger test.
+// COM is always initialized.
+int RunTests(bool is_medium_or_large_test,
+             bool load_resources,
+             int argc,
+             TCHAR** argv) {
+  ASSERT1(!is_medium_or_large_test || load_resources);
+
+  // TODO(omaha): Add executable name.
   OPT_LOG(L1, (_T("[Starting Omaha unit tests]")));
   LogCommandLineAndEnvironment(argc, argv);
 
-  InitializeVersionFromModule(NULL);
+  // Process the environment variables before the args to allow the args to take
+  // precedence.
+  ProcessEnvironmentVariables();
+
   if (!ParseUnitTestArgs(argc, argv)) {
     return -1;
   }
   FailOnAssert fail_on_assert;
 
+  InitializeVersionFromModule(NULL);
+
   scoped_co_init co_init(COINIT_MULTITHREADED);
   VERIFY1(SUCCEEDED(co_init.hresult()));
 
   const bool is_build_system = IsBuildSystem();
+
   if (is_build_system) {
     // Some tests only run as admin. We want to know if the build system is no
     // longer running unit tests as admin.
@@ -222,11 +245,6 @@
       _tprintf(_T("\nUser is not an admin. All tests may not run.\n"));
     }
 
-    // TODO(omaha): Once tests are broken up into small, medium, and large,
-    // and/or support running as non-admin on the build system, only call this
-    // when running large tests and/or set this outside the tests.
-    SetBuildSystemTestSource();
-
     // TODO(omaha): Remove this and the app_util.h, file.h, and atlpath.h
     // includes once the test system does this for us.
     const TCHAR* const kDllRequiredForCoverageRuns = _T("VSCover80.dll");
@@ -240,51 +258,46 @@
     }
   }
 
-  TerminateAllGoogleUpdateProcesses();
-
-  // Ensure that any system running unittests has testsource set.
-  // Some unit tests generate pings, and these must be filtered.
-  CString value;
-  HRESULT hr =
-      RegKey::GetValue(MACHINE_REG_UPDATE_DEV, kRegValueTestSource, &value);
-  if (FAILED(hr) || value.IsEmpty()) {
-    ADD_FAILURE() << _T("'") << kRegValueTestSource << _T("'")
-                  << _T(" is not present in ")
-                  << _T("'") << MACHINE_REG_UPDATE_DEV << _T("'")
-                  << _T(" or it is empty. Since you are running Omaha unit ")
-                  << _T("tests, it should probably be set to 'dev' or 'qa'.");
-    return -1;
+  if (is_medium_or_large_test) {
+    TerminateAllGoogleUpdateProcesses();
   }
 
-  // Many unit tests require the network configuration be initialized.
-  // On Windows Vista only admins can write to HKLM therefore the
-  // initialization of the NetworkConfig must correspond to the integrity
-  // level the user is running as.
-  bool is_machine = vista_util::IsUserAdmin();
-  VERIFY1(SUCCEEDED(goopdate_utils::ConfigureNetwork(is_machine, false)));
+  int result = InitializeNetwork();
+  if (result) {
+    return result;
+  }
+
+  if (load_resources) {
+    // Load a resource DLL so that strings can be loaded during tests and add it
+    // to the list of modules used for CString.LoadString and CreateDialog
+    // calls. The unittest executable includes unittest-specific resources.
+    HMODULE resource_dll = ::LoadLibraryEx(_T("goopdateres_en.dll"),
+                            NULL,
+                            LOAD_LIBRARY_AS_DATAFILE);
+    ASSERT1(resource_dll);
+    _AtlBaseModule.AddResourceInstance(resource_dll);
+  }
+
+  // A COM module is required to create COM objects.
+  // Create it regardless of whether COM is actually used by this executable.
+  CComModule module;
 
   // Add an event listener. Google Test takes the ownership.
   ::testing::TestEventListeners& listeners =
       ::testing::UnitTest::GetInstance()->listeners();
   listeners.Append(new TestLogger);
 
-  int result = RUN_ALL_TESTS();
+  result = RUN_ALL_TESTS();
 
-  NetworkConfig::DeleteInstance();
+  DeinitializeNetwork();
 
-  if (is_build_system) {
+  if (is_build_system && is_medium_or_large_test) {
     TerminateAllGoogleUpdateProcesses();
   }
 
   return result;
 }
 
-}  // namespace
-
 int g_assert_count = 0;
 
 }  // namespace omaha
-
-int _tmain(int argc, TCHAR** argv) {
-  return omaha::RunTests(argc, argv);
-}
diff --git a/testing/omaha_unittest.h b/testing/omaha_unittest.h
new file mode 100644
index 0000000..ab4c0cb
--- /dev/null
+++ b/testing/omaha_unittest.h
@@ -0,0 +1,39 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_TESTING_OMAHA_UNITTEST_H_
+#define OMAHA_TESTING_OMAHA_UNITTEST_H_
+
+#include <windows.h>
+
+namespace omaha {
+
+int RunTests(bool is_medium_or_large_test,
+             bool load_resources,
+             int argc,
+             TCHAR** argv);
+
+// These functions must be implemented by users of RunTests and linked into the
+// test binary.
+
+// Initializes the network if necessary for the test.
+int InitializeNetwork();
+
+// Performs and teardown of the network if necessary.
+int DeinitializeNetwork();
+
+}  // namespace omaha
+
+#endif  // OMAHA_TESTING_OMAHA_UNITTEST_H_
diff --git a/testing/omaha_unittest.rc b/testing/omaha_unittest.rc
index b6f862c..9d8d04b 100644
--- a/testing/omaha_unittest.rc
+++ b/testing/omaha_unittest.rc
Binary files differ
diff --git a/testing/omaha_unittest_main.cc b/testing/omaha_unittest_main.cc
new file mode 100644
index 0000000..6923678
--- /dev/null
+++ b/testing/omaha_unittest_main.cc
@@ -0,0 +1,34 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <windows.h>
+#include <shellapi.h>
+
+#include "omaha/testing/omaha_unittest.h"
+
+// The entry point for the Omaha unit tests.
+// We use main instead of _tmain since _tmain doesn't like being part of a
+// static library...
+int main(int unused_argc, char** unused_argv) {
+  UNREFERENCED_PARAMETER(unused_argc);
+  UNREFERENCED_PARAMETER(unused_argv);
+
+  int argc = 0;
+  WCHAR** argv = ::CommandLineToArgvW(::GetCommandLine(), &argc);
+  return omaha::RunTests(true,  // is_medium_or_large_test.
+                         true,  // load_resources.
+                         argc,
+                         argv);
+}
diff --git a/testing/omaha_unittest_main_small_tests.cc b/testing/omaha_unittest_main_small_tests.cc
new file mode 100644
index 0000000..fef078c
--- /dev/null
+++ b/testing/omaha_unittest_main_small_tests.cc
@@ -0,0 +1,48 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <windows.h>
+#include <shellapi.h>
+
+#include "omaha/testing/omaha_unittest.h"
+
+// The entry point for the Omaha unit tests.
+// We use main instead of _tmain since _tmain doesn't like being part of a
+// static library...
+int main(int unused_argc, char** unused_argv) {
+  UNREFERENCED_PARAMETER(unused_argc);
+  UNREFERENCED_PARAMETER(unused_argv);
+
+  int argc = 0;
+  WCHAR** argv = ::CommandLineToArgvW(::GetCommandLine(), &argc);
+  return omaha::RunTests(false,  // is_medium_or_large_test.
+                         false,  // load_resources.
+                         argc,
+                         argv);
+}
+
+namespace omaha {
+
+// The network is not needed for small tests.
+
+int InitializeNetwork() {
+  return 0;
+}
+
+int DeinitializeNetwork() {
+  return 0;
+}
+
+}  // namespace omaha
diff --git a/testing/omaha_unittest_main_small_tests_with_resources.cc b/testing/omaha_unittest_main_small_tests_with_resources.cc
new file mode 100644
index 0000000..4c1fc63
--- /dev/null
+++ b/testing/omaha_unittest_main_small_tests_with_resources.cc
@@ -0,0 +1,48 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <windows.h>
+#include <shellapi.h>
+
+#include "omaha/testing/omaha_unittest.h"
+
+// The entry point for the Omaha unit tests.
+// We use main instead of _tmain since _tmain doesn't like being part of a
+// static library...
+int main(int unused_argc, char** unused_argv) {
+  UNREFERENCED_PARAMETER(unused_argc);
+  UNREFERENCED_PARAMETER(unused_argv);
+
+  int argc = 0;
+  WCHAR** argv = ::CommandLineToArgvW(::GetCommandLine(), &argc);
+  return omaha::RunTests(false,  // is_medium_or_large_test.
+                         true,   // load_resources.
+                         argc,
+                         argv);
+}
+
+namespace omaha {
+
+// The network is not needed for small tests.
+
+int InitializeNetwork() {
+  return 0;
+}
+
+int DeinitializeNetwork() {
+  return 0;
+}
+
+}  // namespace omaha
diff --git a/testing/omaha_unittest_network.cc b/testing/omaha_unittest_network.cc
new file mode 100644
index 0000000..985c0e2
--- /dev/null
+++ b/testing/omaha_unittest_network.cc
@@ -0,0 +1,63 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/vistautil.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/common/const_goopdate.h"
+#include "omaha/net/network_config.h"
+#include "omaha/testing/omaha_unittest.h"
+#include "omaha/testing/unit_test.h"
+
+namespace omaha {
+
+int InitializeNetwork() {
+  if (IsBuildSystem()) {
+    // Until testsource can be set outside the tests on the build system,
+    // tests that use the network must be run as admin so the it can be set.
+    ASSERT1(vista_util::IsUserAdmin());
+    SetBuildSystemTestSource();
+  }
+
+  // Ensure that any system running unittests has testsource set.
+  // Some unit tests generate pings, and these must be filtered.
+  CString value;
+  HRESULT hr =
+      RegKey::GetValue(MACHINE_REG_UPDATE_DEV, kRegValueTestSource, &value);
+  if (FAILED(hr) || value.IsEmpty()) {
+    ADD_FAILURE() << _T("'") << kRegValueTestSource << _T("'")
+                  << _T(" is not present in ")
+                  << _T("'") << MACHINE_REG_UPDATE_DEV << _T("'")
+                  << _T(" or it is empty. Since you are running Omaha unit ")
+                  << _T("tests, it should probably be set to 'dev' or 'qa'.");
+    return -1;
+  }
+
+  // Many unit tests require the network configuration be initialized.
+  // Referencing the singleton instance creats it if not exist.
+  // On Windows Vista only admins can write to HKLM therefore the
+  // initialization of the NetworkConfig must correspond to the integrity
+  // level the user is running as.
+  NetworkConfigManager::set_is_machine(vista_util::IsUserAdmin());
+  NetworkConfigManager::Instance();
+
+  return 0;
+}
+
+int DeinitializeNetwork() {
+  NetworkConfigManager::DeleteInstance();
+  return 0;
+}
+
+}  // namespace omaha
diff --git a/testing/omaha_unittest_version.rc b/testing/omaha_unittest_version.rc
new file mode 100644
index 0000000..4e3b5cc
--- /dev/null
+++ b/testing/omaha_unittest_version.rc
@@ -0,0 +1,71 @@
+// Copyright 2007 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <afxres.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+// Resource Editor does not handle constants from main.scons.
+#ifndef APSTUDIO_INVOKED
+ FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_PATCH
+ PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_PATCH
+#endif  // APSTUDIO_INVOKED
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#if defined _DEBUG && OFFICIAL_BUILD
+ FILEFLAGS VS_FF_DEBUG
+#elif defined _DEBUG
+ FILEFLAGS VS_FF_DEBUG | VS_FF_PRIVATEBUILD
+#elif !OFFICIAL_BUILD
+ FILEFLAGS VS_FF_PRIVATEBUILD
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+// Requires constants from mains.scons that cannot be loaded in Resource Editor.
+#ifndef APSTUDIO_INVOKED
+            VALUE "CompanyName", FULL_COMPANY_NAME_ANSI
+            VALUE "FileDescription", OMAHA_APP_NAME_ANSI " unit test"
+            VALUE "FileVersion", VERSION_NUMBER_STRING
+            VALUE "InternalName", OMAHA_APP_NAME_ANSI
+            VALUE "LegalCopyright", OMAHA_COPYRIGHT_STRING_ENGLISH
+            VALUE "OriginalFilename", "omaha_unittest.exe"
+            VALUE "ProductName", OMAHA_APP_NAME_ANSI
+            VALUE "ProductVersion", VERSION_NUMBER_STRING
+  #ifdef _DEBUG
+            VALUE "Debug", ""
+  #endif
+  #if !OFFICIAL_BUILD
+            VALUE "PrivateBuild", BUILD_NUMBER
+  #endif
+#else
+            VALUE "_SpecialView",
+                  "Most values are not shown in Resource Editor because they "
+                  "require build file constants."
+#endif  // APSTUDIO_INVOKED
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x0409, 1200
+    END
+END
diff --git a/testing/ondemandsystem_unittest.cc b/testing/ondemandsystem_unittest.cc
index 604f9f5..272d95c 100644
--- a/testing/ondemandsystem_unittest.cc
+++ b/testing/ondemandsystem_unittest.cc
@@ -23,7 +23,7 @@
 #include "omaha/testing/unit_test.h"
 #include "omaha/third_party/gtest/include/gtest/gtest.h"
 #include "omaha/tools/performondemand/performondemand.h"
-#include "omaha/common/utils.h"
+#include "omaha/base/utils.h"
 #include <windows.h>
 #include <atltime.h>
 
diff --git a/testing/resource.h b/testing/resource.h
index 530b558..28b1d7f 100644
--- a/testing/resource.h
+++ b/testing/resource.h
@@ -13,18 +13,17 @@
 // limitations under the License.
 // ========================================================================
 
-#ifndef OMAHA_TESTING_RESOURCE_H__
-#define OMAHA_TESTING_RESOURCE_H__
+#ifndef OMAHA_TESTING_RESOURCE_H_
+#define OMAHA_TESTING_RESOURCE_H_
 
-#define IDS_EXPECTED_UPDATE_REQUEST1                8102
-#define IDS_EXPECTED_UPDATE_REQUEST_TTTOKEN         8103
-#define IDS_EXPECTED_UPDATE_REQUEST_UPDATE_DISABLED 8104
-#define IDS_EXPECTED_UPDATE_REQUEST2                8105
-#define IDS_EXPECTED_UPDATE_REQUEST3                8106
-#define IDS_EXPECTED_UPDATE_REQUEST4                8107
-#define IDS_EXPECTED_UPDATE_REQUEST5                8108
 #define IDS_ESCAPE_TEST                             8120
 #define IDS_ESCAPE_TEST1                            8121
+#define IDS_BUNDLE_INSTALLED_SUCCESSFULLY           8130
+#define IDS_INSTALLER_FAILED_WITH_MESSAGE           8131
 
-#endif  // OMAHA_TESTING_RESOURCE_H__
+#define IDR_OMAHA3_TLB                              1
 
+// Needed for SetupServiceTest.
+#include "omaha/goopdate/non_localized_resource.h"
+
+#endif  // OMAHA_TESTING_RESOURCE_H_
diff --git a/testing/save_arguments.cc b/testing/save_arguments.cc
index adb9fd0..1640772 100644
--- a/testing/save_arguments.cc
+++ b/testing/save_arguments.cc
@@ -20,8 +20,8 @@
 #include <stdio.h>
 #include <tchar.h>
 #include <windows.h>
-#include "omaha/common/app_util.h"
-#include "omaha/common/scoped_any.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/scoped_any.h"
 
 namespace {
 
diff --git a/testing/save_arguments_version.rc b/testing/save_arguments_version.rc
new file mode 100644
index 0000000..08e779d
--- /dev/null
+++ b/testing/save_arguments_version.rc
@@ -0,0 +1,71 @@
+// Copyright 2007 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <afxres.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+// Resource Editor does not handle constants from main.scons.
+#ifndef APSTUDIO_INVOKED
+ FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_PATCH
+ PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_BUILD,VERSION_PATCH
+#endif  // APSTUDIO_INVOKED
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#if defined _DEBUG && OFFICIAL_BUILD
+ FILEFLAGS VS_FF_DEBUG
+#elif defined _DEBUG
+ FILEFLAGS VS_FF_DEBUG | VS_FF_PRIVATEBUILD
+#elif !OFFICIAL_BUILD
+ FILEFLAGS VS_FF_PRIVATEBUILD
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+// Requires constants from mains.scons that cannot be loaded in Resource Editor.
+#ifndef APSTUDIO_INVOKED
+            VALUE "CompanyName", FULL_COMPANY_NAME_ANSI
+            VALUE "FileDescription", OMAHA_APP_NAME_ANSI " SaveArguments"
+            VALUE "FileVersion", VERSION_NUMBER_STRING
+            VALUE "InternalName", OMAHA_APP_NAME_ANSI
+            VALUE "LegalCopyright", OMAHA_COPYRIGHT_STRING_ENGLISH
+            VALUE "OriginalFilename", "SaveArguments.exe"
+            VALUE "ProductName", OMAHA_APP_NAME_ANSI
+            VALUE "ProductVersion", VERSION_NUMBER_STRING
+  #ifdef _DEBUG
+            VALUE "Debug", ""
+  #endif
+  #if !OFFICIAL_BUILD
+            VALUE "PrivateBuild", BUILD_NUMBER
+  #endif
+#else
+            VALUE "_SpecialView",
+                  "Most values are not shown in Resource Editor because they "
+                  "require build file constants."
+#endif  // APSTUDIO_INVOKED
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x0409, 1200
+    END
+END
diff --git a/testing/unit_test.cc b/testing/unit_test.cc
index 486af3f..96ae11c 100644
--- a/testing/unit_test.cc
+++ b/testing/unit_test.cc
@@ -1,4 +1,4 @@
-// Copyright 2007-2009 Google Inc.
+// Copyright 2007-2010 Google Inc.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -14,16 +14,19 @@
 // ========================================================================
 
 #include "testing/unit_test.h"
-#include "omaha/common/app_util.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/path.h"
-#include "omaha/common/process.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/system.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/const_goopdate.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/path.h"
+#include "omaha/base/process.h"
+#include "omaha/base/reg_key.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/system.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/command_line.h"
+#include "omaha/common/command_line_builder.h"
+#include "omaha/common/const_goopdate.h"
 
 namespace omaha {
 
@@ -38,10 +41,18 @@
     return true;
   }
 
+  EXPECT_FALSE(IsEnvironmentVariableSet(_T("OMAHA_RUN_ALL_TESTS")))
+      << _T("Use OMAHA_TEST_RUN_ALL instead of OMAHA_RUN_ALL_TESTS.");
+
+  return IsEnvironmentVariableSet(_T("OMAHA_TEST_RUN_ALL"));
+}
+
+}  // namespace
+
+bool IsEnvironmentVariableSet(const TCHAR* name) {
+  ASSERT1(name);
   TCHAR var[100] = {0};
-  DWORD res = ::GetEnvironmentVariable(_T("OMAHA_RUN_ALL_TESTS"),
-                                       var,
-                                       arraysize(var));
+  DWORD res = ::GetEnvironmentVariable(name, var, arraysize(var));
   if (0 == res) {
     ASSERT1(ERROR_ENVVAR_NOT_FOUND == ::GetLastError());
     return false;
@@ -50,7 +61,9 @@
   }
 }
 
-}  // namespace
+bool IsTestRunByLocalSystem() {
+  return user_info::IsRunningAsSystem();
+}
 
 CString GetLocalAppDataPath() {
   CString expected_local_app_data_path;
@@ -61,17 +74,20 @@
 }
 
 CString GetGoogleUserPath() {
-  return GetLocalAppDataPath() + _T("Google\\");
+  return GetLocalAppDataPath() + SHORT_COMPANY_NAME + _T("\\");
 }
 
+// TODO(omaha): make GetGoogleUpdateUserPath and GetGoogleUpdateMachinePath
+// consistent. They should end with \ or not.
 CString GetGoogleUpdateUserPath() {
-  return GetGoogleUserPath() + _T("Update\\");
+  return GetGoogleUserPath() + PRODUCT_NAME + _T("\\");
 }
 
 CString GetGoogleUpdateMachinePath() {
   CString program_files;
   GetFolderPath(CSIDL_PROGRAM_FILES, &program_files);
-  return program_files + _T("\\Google\\Update");
+  return program_files + _T("\\") + SHORT_COMPANY_NAME
+                        + _T("\\") + PRODUCT_NAME;
 }
 
 DWORD GetDwordValue(const CString& full_key_name, const CString& value_name) {
@@ -80,6 +96,18 @@
   return value;
 }
 
+CString GetSzValue(const CString& full_key_name, const CString& value_name) {
+  CString value;
+  EXPECT_SUCCEEDED(RegKey::GetValue(full_key_name, value_name, &value));
+  return value;
+}
+
+GUID StringToGuid(const CString& str) {
+  GUID guid(GUID_NULL);
+  VERIFY(SUCCEEDED(StringToGuidSafe(str, &guid)), (_T("guid '%s'"), str));
+  return guid;
+}
+
 void OverrideRegistryHives(const CString& hive_override_key_name) {
   OverrideSpecifiedRegistryHives(hive_override_key_name, true, true);
 }
@@ -142,15 +170,8 @@
   _tcscpy_s(psexec_dir, arraysize(psexec_dir), dir.GetString());
 }
 
-// If psexec_dir is not already set - by SetPsexecDir or a previous call to this
-// method - read the environment variable.
 CString GetPsexecDir() {
-  if (!_tcsnlen(psexec_dir, arraysize(psexec_dir))) {
-    EXPECT_TRUE(::GetEnvironmentVariable(_T("OMAHA_PSEXEC_DIR"),
-                                         psexec_dir,
-                                         arraysize(psexec_dir)));
-  }
-
+  EXPECT_TRUE(_tcsnlen(psexec_dir, arraysize(psexec_dir)));
   return psexec_dir;
 }
 
@@ -186,18 +207,13 @@
     return true;
   }
 
-  TCHAR var[100] = {0};
-  DWORD res = ::GetEnvironmentVariable(_T("OMAHA_RUN_LARGE_TESTS"),
-                                       var,
-                                       arraysize(var));
-  if (0 == res) {
-    ASSERT1(ERROR_ENVVAR_NOT_FOUND == ::GetLastError());
+  if (IsEnvironmentVariableSet(_T("OMAHA_TEST_RUN_LARGE"))) {
+    return true;
+  } else {
     std::wcout << _T("\tThis large test did not run because neither ")
-                  _T("'OMAHA_RUN_LARGE_TESTS' or 'OMAHA_RUN_ALL_TESTS' is set ")
+                  _T("'OMAHA_TEST_RUN_LARGE' or 'OMAHA_TEST_RUN_ALL' is set ")
                   _T("in the environment.") << std::endl;
     return false;
-  } else {
-    return true;
   }
 }
 
@@ -207,7 +223,7 @@
   }
 
   std::wcout << _T("\tThis large test did not run because ")
-              _T("'OMAHA_RUN_ALL_TESTS' is not set in the environment.")
+              _T("'OMAHA_TEST_RUN_ALL' is not set in the environment.")
            << std::endl;
   return false;
 }
@@ -229,8 +245,8 @@
 }
 
 void TerminateAllGoogleUpdateProcesses() {
-  TerminateAllProcessesByName(kGoopdateFileName);
-  TerminateAllProcessesByName(kGoopdateCrashHandlerFileName);
+  TerminateAllProcessesByName(kOmahaShellFileName);
+  TerminateAllProcessesByName(kCrashHandlerFileName);
 }
 
 // The exit code of psexec is the pid it started when -d is used.
@@ -269,14 +285,16 @@
       << last_error << _T(".");
 }
 
-void LaunchProcess(const CString& cmd_line,
+void LaunchProcess(const CString& exe_path,
                    const CString& args,
                    bool as_system,
                    HANDLE* process) {
   ASSERT_TRUE(process);
   *process = NULL;
 
-  CString launch_cmd = cmd_line + (args.IsEmpty() ? _T("") : _T(" ") + args);
+  CString launch_cmd = exe_path;
+  EnclosePath(&launch_cmd);
+  launch_cmd += args.IsEmpty() ? _T("") : _T(" ") + args;
 
   if (as_system) {
     // Retry the process launch if the process handle is invalid. Hopefully this
@@ -297,26 +315,57 @@
   ASSERT_TRUE(*process);
 }
 
-//
-// Unit tests for helper functions in this file.
-//
+void RegistryProtectedTest::SetUp() {
+  RegKey::DeleteKey(hive_override_key_name_, true);
+  OverrideRegistryHives(hive_override_key_name_);
+}
 
-TEST(UnitTestHelpersTest, GetLocalAppDataPath) {
-  const TCHAR kUserXpLocalAppDataPathFormat[] =
-      _T("C:\\Documents and Settings\\%s\\Local Settings\\Application Data\\");
-  const TCHAR kUserVistaLocalAppDataPathFormat[] =
-      _T("C:\\Users\\%s\\AppData\\Local\\");
+void RegistryProtectedTest::TearDown() {
+  RestoreRegistryHives();
+  ASSERT_SUCCEEDED(RegKey::DeleteKey(hive_override_key_name_, true));
+}
 
-  TCHAR username[MAX_PATH] = {0};
-  EXPECT_TRUE(::GetEnvironmentVariable(_T("USERNAME"),
-                                       username,
-                                       arraysize(username)));
-  CString expected_path;
-  expected_path.Format(vista_util::IsVistaOrLater() ?
-                           kUserVistaLocalAppDataPathFormat :
-                           kUserXpLocalAppDataPathFormat,
-                       username);
-  EXPECT_STREQ(expected_path, GetLocalAppDataPath());
+CString GetUniqueTempDirectoryName() {
+  CString guid;
+  EXPECT_HRESULT_SUCCEEDED(GetGuid(&guid));
+  return ConcatenatePath(app_util::GetTempDir(), guid);
+}
+
+void RunAsAdmin(const CString& exe_path, const CString& cmd_line) {
+  if (vista_util::IsUserAdmin()) {
+    EXPECT_SUCCEEDED(RegisterOrUnregisterExe(exe_path, cmd_line));
+    return;
+  }
+
+  // Elevate for medium integrity users on Vista and above.
+  DWORD exit_code(S_OK);
+  EXPECT_SUCCEEDED(vista_util::RunElevated(exe_path,
+                                           cmd_line,
+                                           SW_SHOWNORMAL,
+                                           &exit_code));
+  EXPECT_SUCCEEDED(exit_code);
+}
+
+void RegisterOrUnregisterGoopdateLocalServer(bool reg) {
+  CString server_path = ConcatenatePath(GetGoogleUpdateMachinePath(),
+                                        kOmahaShellFileName);
+  EnclosePath(&server_path);
+
+  CommandLineBuilder builder(reg ? COMMANDLINE_MODE_REGSERVER :
+                                   COMMANDLINE_MODE_UNREGSERVER);
+  CString cmd_line = builder.GetCommandLineArgs();
+  RunAsAdmin(server_path, cmd_line);
+}
+
+void RegisterOrUnregisterGoopdateService(bool reg) {
+  CString service_path = ConcatenatePath(GetGoogleUpdateMachinePath(),
+                                         kServiceFileName);
+  EnclosePath(&service_path);
+
+  CommandLineBuilder builder(reg ? COMMANDLINE_MODE_SERVICE_REGISTER :
+                                   COMMANDLINE_MODE_SERVICE_UNREGISTER);
+  CString cmd_line = builder.GetCommandLineArgs();
+  RunAsAdmin(service_path, cmd_line);
 }
 
 }  // namespace omaha
diff --git a/testing/unit_test.h b/testing/unit_test.h
index c278c65..b861f6c 100644
--- a/testing/unit_test.h
+++ b/testing/unit_test.h
@@ -22,10 +22,21 @@
 #include <atlstr.h>
 #include "base/scoped_ptr.h"
 #include "omaha/testing/unittest_debug_helper.h"
+#pragma warning(push)
+// C4628: digraphs not supported with -Ze.
+#pragma warning(disable : 4628)
+// C4826: Conversion from 'TYPE *' to 'testing::internal::UInt64' is
+// sign-extended. This may cause unexpected runtime behavior.
+// Caused by a hack in DefaultPrintTo.
+#pragma warning(disable : 4826)
+#include "omaha/third_party/gmock/include/gmock/gmock.h"
+#pragma warning(pop)
 #include "omaha/third_party/gtest/include/gtest/gtest.h"
 
 namespace omaha {
 
+const TCHAR* const kUnittestName = _T("omaha_unittest.exe");
+
 // Predicates needed by ASSERT_PRED1 for function returning an HRESULT.
 inline testing::AssertionResult Succeeded(const char* s, HRESULT hr) {
   if (SUCCEEDED(hr)) {
@@ -51,6 +62,12 @@
   }
 }
 
+// Returns true if the variable exists in the environment, even if it is "0".
+bool IsEnvironmentVariableSet(const TCHAR* name);
+
+// Returns true if current unit test process owner is LOCALSYSTEM.
+bool IsTestRunByLocalSystem();
+
 // Returns the path to the base local app data directory for the user on the
 // current OS.
 CString GetLocalAppDataPath();
@@ -70,8 +87,17 @@
 // Useful for inline comparisons in EXPECT_EQ.
 DWORD GetDwordValue(const CString& full_key_name, const CString& value_name);
 
+// Returns a SZ registry value from the registry. Assumes the value exists.
+// Useful for inline comparisons in EXPECT_STREQ.
+CString GetSzValue(const CString& full_key_name, const CString& value_name);
+
+// Converts string to GUID. Assumes the string is a valid GUID.
+GUID StringToGuid(const CString& str);
+
 const TCHAR* const kRegistryHiveOverrideRoot =
-    _T("HKCU\\Software\\Google\\Update\\UnitTest\\");
+    _T("HKCU\\Software\\") _T(SHORT_COMPANY_NAME_ANSI)
+    _T("\\") _T(PRODUCT_NAME_ANSI)
+    _T("\\UnitTest\\");
 const TCHAR* const kCsidlSystemIdsRegKey =
     _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion");
 const TCHAR* const kCsidlProgramFilesRegValue =
@@ -131,12 +157,12 @@
 void SetBuildSystemTestSource();
 
 // Returns whether large tests should be run. Large tests are always run on the
-// build system and if the "OMAHA_RUN_LARGE_TESTS" or "OMAHA_RUN_ALL_TESTS"
+// build system and if the "OMAHA_TEST_RUN_LARGE" or "OMAHA_TEST_RUN_ALL"
 // environment variable is set.
 bool ShouldRunLargeTest();
 
 // Returns whether enourmous tests should be run. Enormous tests are always run
-// on the build system and if the "OMAHA_RUN_ALL_TESTS" environment variable is
+// on the build system and if the "OMAHA_TEST_RUN_ALL" environment variable is
 // set. This method should be used sparingly and only by tests that take a
 // really long time to complete.
 bool ShouldRunEnormousTest();
@@ -145,7 +171,7 @@
 void TerminateAllGoogleUpdateProcesses();
 
 // Launches a process and returns its handle.
-void LaunchProcess(const CString& cmd_line,
+void LaunchProcess(const CString& exe_path,
                    const CString& args,
                    bool as_system,
                    HANDLE* process);
@@ -154,8 +180,35 @@
 // psexec to run the process.
 void LaunchProcessAsSystem(const CString& launch_cmd, HANDLE* process);
 
+// Copies Omaha installation files under omaha_path.
+void CopyGoopdateFiles(const CString& omaha_path, const CString& version);
+
+// A generic test fixture that overrides the HKLM and HKCU hives.
+class RegistryProtectedTest : public testing::Test {
+ protected:
+  RegistryProtectedTest()
+      : hive_override_key_name_(kRegistryHiveOverrideRoot) {
+  }
+
+  virtual void SetUp();
+  virtual void TearDown();
+
+  const CString hive_override_key_name_;
+};
+
+// Returns the full path of a unique directory under the user temp directory.
+CString GetUniqueTempDirectoryName();
+
+// Runs the command as an administrator.
+void RunAsAdmin(const CString& exe_path, const CString& cmd_line);
+
+void RegisterOrUnregisterGoopdateLocalServer(bool reg);
+
+void RegisterOrUnregisterGoopdateService(bool reg);
+
 }  // namespace omaha
 
+// TODO(omaha): Replace custom predicates with EXPECT_HRESULT_SUCCEEDED/FAILED.
 #define ASSERT_SUCCEEDED(x) ASSERT_PRED_FORMAT1(omaha::Succeeded, x)
 #define EXPECT_SUCCEEDED(x) EXPECT_PRED_FORMAT1(omaha::Succeeded, x)
 #define ASSERT_FAILED(x) ASSERT_PRED_FORMAT1(omaha::Failed, x)
@@ -175,6 +228,20 @@
   GTEST_TEST_BOOLEAN_(!!(condition), #condition, false, true, \
                       GTEST_FATAL_FAILURE_)
 
-#define kUnittestName _T("omaha_unittest.exe")
+// GMock's ACTION* macros have 10 parameters, most of which go unused.
+// This macro can be used inside ACTION* definitions to suppress warning
+// C4100: unreferenced formal parameter.
+#define UNREFERENCED_ACTION_PARAMETERS \
+    UNREFERENCED_PARAMETER(args); \
+    UNREFERENCED_PARAMETER(arg0); \
+    UNREFERENCED_PARAMETER(arg1); \
+    UNREFERENCED_PARAMETER(arg2); \
+    UNREFERENCED_PARAMETER(arg3); \
+    UNREFERENCED_PARAMETER(arg4); \
+    UNREFERENCED_PARAMETER(arg5); \
+    UNREFERENCED_PARAMETER(arg6); \
+    UNREFERENCED_PARAMETER(arg7); \
+    UNREFERENCED_PARAMETER(arg8); \
+    UNREFERENCED_PARAMETER(arg9)
 
 #endif  // OMAHA_TESTING_UNIT_TEST_H_
diff --git a/testing/unit_test_unittest.cc b/testing/unit_test_unittest.cc
new file mode 100644
index 0000000..6f50e1f
--- /dev/null
+++ b/testing/unit_test_unittest.cc
@@ -0,0 +1,97 @@
+// Copyright 2009-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/utils.h"
+#include "omaha/base/user_info.h"
+#include "omaha/base/vistautil.h"
+#include "testing/unit_test.h"
+
+namespace omaha {
+
+TEST(UnitTestHelpersTest, GetLocalAppDataPath) {
+  if (IsTestRunByLocalSystem()) {
+    return;
+  }
+
+  const TCHAR kUserXpLocalAppDataPathFormat[] =
+      _T("C:\\Documents and Settings\\%s\\Local Settings\\Application Data\\");
+  const TCHAR kUserVistaLocalAppDataPathFormat[] =
+      _T("C:\\Users\\%s\\AppData\\Local\\");
+
+  TCHAR username[MAX_PATH] = {0};
+  EXPECT_TRUE(::GetEnvironmentVariable(_T("USERNAME"),
+                                       username,
+                                       arraysize(username)));
+  CString expected_path;
+  expected_path.Format(vista_util::IsVistaOrLater() ?
+                           kUserVistaLocalAppDataPathFormat :
+                           kUserXpLocalAppDataPathFormat,
+                       username);
+  EXPECT_STREQ(expected_path, GetLocalAppDataPath());
+}
+
+// GUIDs cannot be compared in GTest because there is no << operator. Therefore,
+// we must treat them as strings. All these tests rely on GuidToString working.
+#define EXPECT_GUID_EQ(expected, actual) \
+    EXPECT_STREQ(GuidToString(expected), GuidToString(actual))
+
+TEST(UnitTestHelpersTest, StringToGuid_InvalidString) {
+  ExpectAsserts expect_asserts;  // Invalid strings cause an assert.
+
+  EXPECT_GUID_EQ(GUID_NULL, StringToGuid(_T("")));
+  EXPECT_GUID_EQ(GUID_NULL, StringToGuid(_T("{}")));
+  EXPECT_GUID_EQ(GUID_NULL, StringToGuid(_T("a")));
+  EXPECT_GUID_EQ(GUID_NULL,
+                 StringToGuid(_T("CA3045BFA6B14fb8A0EFA615CEFE452C")));
+
+  // Missing {}
+  EXPECT_GUID_EQ(GUID_NULL,
+                 StringToGuid(_T("CA3045BF-A6B1-4fb8-A0EF-A615CEFE452C")));
+
+  // Invalid char X
+  EXPECT_GUID_EQ(GUID_NULL,
+                 StringToGuid(_T("{XA3045BF-A6B1-4fb8-A0EF-A615CEFE452C}")));
+
+  // Invalid binary char 0x200
+  EXPECT_GUID_EQ(
+      GUID_NULL,
+      StringToGuid(_T("{\0x200a3045bf-a6b1-4fb8-a0ef-a615cefe452c}")));
+
+  // Missing -
+  EXPECT_GUID_EQ(GUID_NULL,
+                 StringToGuid(_T("{CA3045BFA6B14fb8A0EFA615CEFE452C}")));
+
+  // Double quotes
+  EXPECT_GUID_EQ(
+      GUID_NULL,
+      StringToGuid(_T("\"{ca3045bf-a6b1-4fb8-a0ef-a615cefe452c}\"")));
+}
+
+TEST(UnitTestHelpersTest, StringToGuid_ValidString) {
+  const GUID kExpectedGuid = {0xCA3045BF, 0xA6B1, 0x4FB8,
+                              {0xA0, 0xEF, 0xA6, 0x15, 0xCE, 0xFE, 0x45, 0x2C}};
+
+  // Converted successfully, but indistinguishable from failures.
+  EXPECT_GUID_EQ(GUID_NULL,
+                 StringToGuid(_T("{00000000-0000-0000-0000-000000000000}")));
+
+  EXPECT_GUID_EQ(kExpectedGuid,
+                 StringToGuid(_T("{CA3045BF-A6B1-4fb8-A0EF-A615CEFE452C}")));
+
+  EXPECT_GUID_EQ(kExpectedGuid,
+                 StringToGuid(_T("{ca3045bf-a6b1-4fb8-a0ef-a615cefe452c}")));
+}
+
+}  // namespace omaha
diff --git a/testing/unittest_debug_helper.h b/testing/unittest_debug_helper.h
index 4a95c34..ac1787c 100644
--- a/testing/unittest_debug_helper.h
+++ b/testing/unittest_debug_helper.h
@@ -21,7 +21,7 @@
 #define OMAHA_TESTING_UNITTEST_DEBUG_HELPER_H__
 
 #include "base/basictypes.h"
-#include "omaha/common/debug.h"
+#include "omaha/base/debug.h"
 #include "third_party/gtest/include/gtest/gtest.h"
 
 namespace omaha {
diff --git a/testing/unittest_debug_helper_unittest.cc b/testing/unittest_debug_helper_unittest.cc
index 2d5c574..fae8691 100644
--- a/testing/unittest_debug_helper_unittest.cc
+++ b/testing/unittest_debug_helper_unittest.cc
@@ -14,7 +14,7 @@
 // ========================================================================
 
 
-#include "omaha/common/debug.h"
+#include "omaha/base/debug.h"
 #include "omaha/testing/unit_test.h"
 
 namespace omaha{
diff --git a/testing/unittest_support/GoogleUpdate_old.exe b/testing/unittest_support/GoogleUpdate_old.exe
deleted file mode 100644
index 99e597f..0000000
--- a/testing/unittest_support/GoogleUpdate_old.exe
+++ /dev/null
Binary files differ
diff --git a/testing/unittest_support/Readme.txt b/testing/unittest_support/Readme.txt
index de28a5a..26ab8de 100644
--- a/testing/unittest_support/Readme.txt
+++ b/testing/unittest_support/Readme.txt
@@ -11,5 +11,8 @@
   - One use is testing the quiet mode (shutdown) event.
 * omaha_1.2.x contains an older version of Omaha 1.2.x.
   - One use is testing the shutdown event works with older versions.
-* omaha_1.2.x_newer contains a version that is newer than any expected build.
-  - GoogleUpdate.exe was generated by hardcoding the four version values in generated_resources_en.rc and building.
\ No newline at end of file
+* omaha_1.3.x contains an older version of Omaha 1.3.x.
+  - One use is testing the shutdown event works with older versions.
+* omaha_1.3.x_newer contains a version that is newer than any expected build.
+  - GoogleUpdate.exe was generated by hardcoding the four version values in generated_resources_en.rc and building.
+  
\ No newline at end of file
diff --git a/testing/unittest_support/SaveArguments.exe b/testing/unittest_support/SaveArguments.exe
index 7d3c60d..91789a4 100644
--- a/testing/unittest_support/SaveArguments.exe
+++ b/testing/unittest_support/SaveArguments.exe
Binary files differ
diff --git a/testing/unittest_support/localproxytest.pac b/testing/unittest_support/localproxytest.pac
new file mode 100644
index 0000000..057c85c
--- /dev/null
+++ b/testing/unittest_support/localproxytest.pac
@@ -0,0 +1,7 @@
+function FindProxyForURL(url, host) {
+  if (shExpMatch(host, "*.omahaproxytest.com")) {
+    return "PROXY omaha_unittest1; SOCKS should_not_appear:12345; PROXY omaha_unittest2:8080; DIRECT; PROXY should_not_be_included";
+  }
+  return "DIRECT";
+}
+
diff --git a/testing/unittest_support/omaha_1.3.x/GoogleUpdate.exe b/testing/unittest_support/omaha_1.3.x/GoogleUpdate.exe
new file mode 100644
index 0000000..1105e67
--- /dev/null
+++ b/testing/unittest_support/omaha_1.3.x/GoogleUpdate.exe
Binary files differ
diff --git a/testing/unittest_support/omaha_1.3.x/goopdate.dll b/testing/unittest_support/omaha_1.3.x/goopdate.dll
new file mode 100644
index 0000000..1950a81
--- /dev/null
+++ b/testing/unittest_support/omaha_1.3.x/goopdate.dll
Binary files differ
diff --git a/testing/unittest_support/omaha_1.3.x/goopdateres_en.dll b/testing/unittest_support/omaha_1.3.x/goopdateres_en.dll
new file mode 100644
index 0000000..f36ddad
--- /dev/null
+++ b/testing/unittest_support/omaha_1.3.x/goopdateres_en.dll
Binary files differ
diff --git "a/testing/unittest_support/\173CDABE316-39CD-43BA-8440-6D1E0547AEE6\175.v2.gup" "b/testing/unittest_support/\173CDABE316-39CD-43BA-8440-6D1E0547AEE6\175.v2.gup"
new file mode 100644
index 0000000..b87c348
--- /dev/null
+++ "b/testing/unittest_support/\173CDABE316-39CD-43BA-8440-6D1E0547AEE6\175.v2.gup"
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
+  <app appid="{CDABE316-39CD-43BA-8440-6D1E0547AEE6}" status="ok">
+    <updatecheck Version="1.2.3.4"
+                 arguments="-baz"
+                 codebase="http://dl.google.com/foo/install/1.2.3.4/foo_installer.exe"
+                 hash="abcdef" needsadmin="false"
+                 onsuccess="exitsilentlyonlaunchcmd" size="12345678"
+                 status="ok"/>
+    <!-- "system_level" is not included in any of the definitions. needsadmin
+          controls this. -->
+    <data index="verboselogging" name="install" status="ok">
+      {
+        "distribution": {
+          "verbose_logging": true
+        }
+      }
+    </data>
+    <data index="foobarapp" name="install" status="ok">
+      {
+        "distribution": {
+          "skip_first_run_ui": true,
+          "show_welcome_page": true,
+          "import_search_engine": true,
+          "import_history": false,
+          "create_all_shortcuts": true,
+          "do_not_launch_foo": true,
+          "make_foo_default": false,
+          "verbose_logging": false
+        }
+      }
+    </data>
+  </app>
+</gupdate>
diff --git "a/testing/unittest_support/\173CDABE316-39CD-43BA-8440-6D1E0547AEE6\175.v3.gup" "b/testing/unittest_support/\173CDABE316-39CD-43BA-8440-6D1E0547AEE6\175.v3.gup"
new file mode 100644
index 0000000..2aaaa46
--- /dev/null
+++ "b/testing/unittest_support/\173CDABE316-39CD-43BA-8440-6D1E0547AEE6\175.v3.gup"
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<response protocol="3.0">
+  <app appid="{CDABE316-39CD-43BA-8440-6D1E0547AEE6}" status="ok">
+    <updatecheck status="ok">
+      <urls>
+        <url codebase="http://dl.google.com/foo/install/1.2.3.4/"/>
+      </urls>
+      <manifest version="1.2.3.4">
+        <packages>
+          <package hash="abcdef" name="foo_installer.exe" required="true" size="12345678"/>
+        </packages>
+        <actions>
+          <action event="install" needsadmin="false" run="foo_installer.exe" arguments="-baz"/>
+          <action event="postinstall" onsuccess="exitsilentlyonlaunchcmd"/>
+        </actions>
+      </manifest>
+    </updatecheck>
+    <!-- "system_level" is not included in any of the definitions. needsadmin
+          controls this. -->
+    <data index="verboselogging" name="install" status="ok">
+      {
+        "distribution": {
+          "verbose_logging": true
+        }
+      }
+    </data>
+    <data index="foobarapp" name="install" status="ok">
+      {
+        "distribution": {
+          "skip_first_run_ui": true,
+          "show_welcome_page": true,
+          "import_search_engine": true,
+          "import_history": false,
+          "create_all_shortcuts": true,
+          "do_not_launch_foo": true,
+          "make_foo_default": false,
+          "verbose_logging": false
+        }
+      }
+    </data>
+  </app>
+</response>
diff --git a/third_party/bar/shared_ptr.h b/third_party/bar/shared_ptr.h
new file mode 100644
index 0000000..5f70388
--- /dev/null
+++ b/third_party/bar/shared_ptr.h
@@ -0,0 +1,438 @@
+// Copyright 2006 and onwards Google Inc.
+// Author: Michael Ellman (with suggestions from jrvb, m3b, toddw, jwray)
+//
+// A simple reference counted pointer implementation. It is a subset
+// of the boost/tr1 shared_ptr class, which is expected to be part of
+// the next C++ standard.  See section 20.8.10 [util.smartptr] of the
+// draft standard for a full description of the standard interface.
+//
+// Standard features that have been omitted from this implementation include:
+//   - no custom deallocators - uses delete
+//   - shared_ptr<T>'s constructor isn't templated: its argument is just T*.
+//   - no support for smart pointer casts
+//   - no support for unowned pointers
+//   - no support for variadic templates or rvalue references
+//   - no integration with auto_ptr or unique_ptr
+//   - not exception-safe
+//   - no overloaded comparison operators (e.g. operator<). They're
+//     convenient, but they can be explicitly defined outside the class.
+//
+// It's usually the case that when you want to share an object, there
+// is a clear owner that outlives the other users.  If that's the case,
+// the owner can use scoped_ptr and the rest can use a raw pointer.
+//
+// A somewhat common design pattern that doesn't have a clear object
+// owner is when there is a shared container in which older versions
+// of an object are replaced with newer versions.  The objects should be
+// deleted only when (a) they are replaced with a new version and (b)
+// there are no outside users of the old version.  Replacing raw pointers
+// in the implementation with shared_ptr's ensures that the accounting
+// and object lifetimes are handled appropriately.
+//
+// The typical usage is as follows.
+//
+//  1. Functions using shared_ptr's should declare shared_ptr parameters to
+//     be of type const reference since the caller will still have its own
+//     shared_ptr for the entire call.
+//
+//       void foo(const shared_ptr<T>& param)
+//
+//  2. Functions setting shared_ptr's should declare shared_ptr parameters
+//     to be of pointer type.
+//
+//     typedef map<Key, shared_ptr<Value> > MyMap;
+//     void GetAndSharedObject(const Key& key, shared_ptr<Value>* value) {
+//       ReaderMutexLock l(&lock_);
+//       MyMap::iterator iter = shared_container.find(key);
+//       *value = iter->second;
+//     }
+//
+// Thread Safety:
+//   Once constructed, a shared_ptr has the same thread-safety as built-in
+//   types.  In particular, it is safe to read a shared object simultaneously
+//   from multiple threads.
+//
+// Weak ptrs
+//   The weak_ptr auxiliary class (see clause 20.8.10.3 of the draft standard)
+//   is used to break ownership cycles. A weak_ptr points to an object that's
+//   owned by a shared_ptr, but the weak_ptr is an observer, not an owner. When
+//   the last shared_ptr that points to the object disappears, the weak_ptr
+//   expires, at which point the expired() member function will return true.
+//
+//   You can't directly get a raw pointer from weak_ptr, i.e. it has no get()
+//   or operator*() member function. (These features were intentionally left out
+//   to avoid the risk of dangling pointers.) To access a weak_ptr's pointed-to
+//   object, use lock() to obtain a temporary shared_ptr.
+//
+// enable_shared_from_this
+//   A user-defined class T can inherit from enable_shared_from_this<T> (see
+//   clause 20.8.10.5 of the draft standard) to inherit T::shared_from_this(),
+//   which returns a shared_ptr pointing to *this. It is similar to weak_ptr in
+//   that there must already be at least one shared_ptr instance that owns
+//   *this.
+
+#ifndef BAR_COMMON_SHARED_PTR_H_
+#define BAR_COMMON_SHARED_PTR_H_
+
+#include <windows.h>
+#include <algorithm>  // for swap
+
+template <typename T> class shared_ptr;
+template <typename T> class weak_ptr;
+
+// This class is an internal implementation detail for shared_ptr. If two
+// shared_ptrs point to the same object, they also share a control block.
+// An "empty" shared_pointer refers to NULL and also has a NULL control block.
+// It contains all of the state that's needed for reference counting or any
+// other kind of resource management. In this implementation the control block
+// happens to consist of two atomic words, the reference count (the number
+// of shared_ptrs that share ownership of the object) and the weak count
+// (the number of weak_ptrs that observe the object, plus 1 if the
+// refcount is nonzero).
+//
+// The "plus 1" is to prevent a race condition in the shared_ptr and
+// weak_ptr destructors. We need to make sure the control block is
+// only deleted once, so we need to make sure that at most one
+// object sees the weak count decremented from 1 to 0.
+class SharedPtrControlBlock {
+  template <typename T> friend class shared_ptr;
+  template <typename T> friend class weak_ptr;
+ private:
+  SharedPtrControlBlock() : refcount_(1), weak_count_(1) { }
+  LONG refcount_;
+  LONG weak_count_;
+};
+
+// Forward declaration. The class is defined below.
+template <typename T> class enable_shared_from_this;
+
+template <typename T>
+class shared_ptr {
+  template <typename U> friend class weak_ptr;
+ public:
+  typedef T element_type;
+
+  explicit shared_ptr(T* ptr = NULL)
+      : ptr_(ptr),
+        control_block_(ptr != NULL ? new SharedPtrControlBlock : NULL) {
+    // If p is non-null and T inherits from enable_shared_from_this, we
+    // set up the data that shared_from_this needs.
+    MaybeSetupWeakThis(ptr);
+  }
+
+  // Copy constructor: makes this object a copy of ptr, and increments
+  // the reference count.
+  template <typename U>
+  shared_ptr(const shared_ptr<U>& ptr)
+      : ptr_(NULL),
+        control_block_(NULL) {
+    Initialize(ptr);
+  }
+  // Need non-templated version to prevent the compiler-generated default
+  shared_ptr(const shared_ptr<T>& ptr)
+      : ptr_(NULL),
+        control_block_(NULL) {
+    Initialize(ptr);
+  }
+
+  // Assignment operator. Replaces the existing shared_ptr with ptr.
+  // Increment ptr's reference count and decrement the one being replaced.
+  template <typename U>
+  shared_ptr<T>& operator=(const shared_ptr<U>& ptr) {
+    if (ptr_ != ptr.ptr_) {
+      shared_ptr<T> me(ptr);   // will hold our previous state to be destroyed.
+      swap(me);
+    }
+    return *this;
+  }
+
+  // Need non-templated version to prevent the compiler-generated default
+  shared_ptr<T>& operator=(const shared_ptr<T>& ptr) {
+    if (ptr_ != ptr.ptr_) {
+      shared_ptr<T> me(ptr);   // will hold our previous state to be destroyed.
+      swap(me);
+    }
+    return *this;
+  }
+
+  // TODO(austern): Consider providing this constructor. The draft C++ standard
+  // (20.8.10.2.1) includes it. However, it says that this constructor throws
+  // a bad_weak_ptr exception when ptr is expired. Is it better to provide this
+  // constructor and make it do something else, like fail with a CHECK, or to
+  // leave this constructor out entirely?
+  //
+  // template <typename U>
+  // shared_ptr(const weak_ptr<U>& ptr);
+
+  ~shared_ptr() {
+    if (ptr_ != NULL) {
+      if (::InterlockedDecrement(&control_block_->refcount_) == 0) {
+        delete ptr_;
+
+        // weak_count_ is defined as the number of weak_ptrs that observe
+        // ptr_, plus 1 if refcount_ is nonzero.
+        if (::InterlockedDecrement(&control_block_->weak_count_) == 0) {
+          delete control_block_;
+        }
+      }
+    }
+  }
+
+  // Replaces underlying raw pointer with the one passed in.  The reference
+  // count is set to one (or zero if the pointer is NULL) for the pointer
+  // being passed in and decremented for the one being replaced.
+  void reset(T* p = NULL) {
+    if (p != ptr_) {
+      shared_ptr<T> tmp(p);
+      tmp.swap(*this);
+    }
+  }
+
+  // Exchanges the contents of this with the contents of r.  This function
+  // supports more efficient swapping since it eliminates the need for a
+  // temporary shared_ptr object.
+  void swap(shared_ptr<T>& r) {
+    std::swap(ptr_, r.ptr_);
+    std::swap(control_block_, r.control_block_);
+  }
+
+  // The following function is useful for gaining access to the underlying
+  // pointer when a shared_ptr remains in scope so the reference-count is
+  // known to be > 0 (e.g. for parameter passing).
+  T* get() const {
+    return ptr_;
+  }
+
+  T& operator*() const {
+    return *ptr_;
+  }
+
+  T* operator->() const {
+    return ptr_;
+  }
+
+  LONG use_count() const {
+    return control_block_ ? control_block_->refcount_ : 1;
+  }
+
+  bool unique() const {
+    return use_count() == 1;
+  }
+
+ private:
+  // If r is non-empty, initialize *this to share ownership with r,
+  // increasing the underlying reference count.
+  // If r is empty, *this remains empty.
+  // Requires: this is empty, namely this->ptr_ == NULL.
+  template <typename U>
+  void Initialize(const shared_ptr<U>& r) {
+    if (r.control_block_ != NULL) {
+      ::InterlockedIncrement(&r.control_block_->refcount_);
+
+      ptr_ = r.ptr_;
+      control_block_ = r.control_block_;
+    }
+  }
+
+  // Helper function for the constructor that takes a raw pointer. If T
+  // doesn't inherit from enable_shared_from_this<T> then we have nothing to
+  // do, so this function is trivial and inline. The other version is declared
+  // out of line, after the class definition of enable_shared_from_this.
+  void MaybeSetupWeakThis(enable_shared_from_this<T>* ptr);
+  void MaybeSetupWeakThis(...) { }
+
+  T* ptr_;
+  SharedPtrControlBlock* control_block_;
+
+  template <typename U>
+  friend class shared_ptr;
+};
+
+// Matches the interface of std::swap as an aid to generic programming.
+template <typename T> void swap(shared_ptr<T>& r, shared_ptr<T>& s) {
+  r.swap(s);
+}
+
+// See comments at the top of the file for a description of why this
+// class exists, and the draft C++ standard (as of July 2009 the
+// latest draft is N2914) for the detailed specification.
+template <typename T>
+class weak_ptr {
+  template <typename U> friend class weak_ptr;
+ public:
+  typedef T element_type;
+
+  // Create an empty (i.e. already expired) weak_ptr.
+  weak_ptr() : ptr_(NULL), control_block_(NULL) { }
+
+  // Create a weak_ptr that observes the same object that ptr points
+  // to.  Note that there is no race condition here: we know that the
+  // control block can't disappear while we're looking at it because
+  // it is owned by at least one shared_ptr, ptr.
+  template <typename U> weak_ptr(const shared_ptr<U>& ptr) {
+    CopyFrom(ptr.ptr_, ptr.control_block_);
+  }
+
+  // Copy a weak_ptr. The object it points to might disappear, but we
+  // don't care: we're only working with the control block, and it can't
+  // disappear while we're looking at because it's owned by at least one
+  // weak_ptr, ptr.
+  template <typename U> weak_ptr(const weak_ptr<U>& ptr) {
+    CopyFrom(ptr.ptr_, ptr.control_block_);
+  }
+
+  // Need non-templated version to prevent default copy constructor
+  weak_ptr(const weak_ptr& ptr) {
+    CopyFrom(ptr.ptr_, ptr.control_block_);
+  }
+
+  // Destroy the weak_ptr. If no shared_ptr owns the control block, and if
+  // we are the last weak_ptr to own it, then it can be deleted. Note that
+  // weak_count_ is defined as the number of weak_ptrs sharing this control
+  // block, plus 1 if there are any shared_ptrs. We therefore know that it's
+  // safe to delete the control block when weak_count_ reaches 0, without
+  // having to perform any additional tests.
+  ~weak_ptr() {
+    if (control_block_ != NULL &&
+        ::InterlockedDecrement(&control_block_->weak_count_) == 0) {
+      delete control_block_;
+    }
+  }
+
+  weak_ptr& operator=(const weak_ptr& ptr) {
+    if (&ptr != this) {
+      weak_ptr tmp(ptr);
+      tmp.swap(*this);
+    }
+    return *this;
+  }
+  template <typename U> weak_ptr& operator=(const weak_ptr<U>& ptr) {
+    weak_ptr tmp(ptr);
+    tmp.swap(*this);
+    return *this;
+  }
+  template <typename U> weak_ptr& operator=(const shared_ptr<U>& ptr) {
+    weak_ptr tmp(ptr);
+    tmp.swap(*this);
+    return *this;
+  }
+
+  void swap(weak_ptr& ptr) {
+    std::swap(ptr_, ptr.ptr_);
+    std::swap(control_block_, ptr.control_block_);
+  }
+
+  void reset() {
+    weak_ptr tmp;
+    tmp.swap(*this);
+  }
+
+  // Return the number of shared_ptrs that own the object we are observing.
+  // Note that this number can be 0 (if this pointer has expired).
+  LONG use_count() const {
+    return control_block_ != NULL ? control_block_->refcount_ : 0;
+  }
+
+  bool expired() const { return use_count() == 0; }
+
+  // Return a shared_ptr that owns the object we are observing. If we
+  // have expired, the shared_ptr will be empty. We have to be careful
+  // about concurrency, though, since some other thread might be
+  // destroying the last owning shared_ptr while we're in this
+  // function.  We want to increment the refcount only if it's nonzero
+  // and get the new value, and we want that whole operation to be
+  // atomic.
+  shared_ptr<T> lock() const {
+    shared_ptr<T> result;
+    if (control_block_ != NULL) {
+      LONG old_refcount;
+      do {
+        old_refcount = control_block_->refcount_;
+        if (old_refcount == 0)
+          break;
+      } while (old_refcount !=
+               ::InterlockedCompareExchange(
+                      &control_block_->refcount_, old_refcount + 1,
+                      old_refcount));
+      if (old_refcount > 0) {
+        result.ptr_ = ptr_;
+        result.control_block_ = control_block_;
+      }
+    }
+
+    return result;
+  }
+
+ private:
+  void CopyFrom(T* ptr, SharedPtrControlBlock* control_block) {
+    ptr_ = ptr;
+    control_block_ = control_block;
+    if (control_block_ != NULL)
+      ::InterlockedIncrement(&control_block_->weak_count_);
+  }
+
+ private:
+  element_type* ptr_;
+  SharedPtrControlBlock* control_block_;
+};
+
+template <typename T> void swap(weak_ptr<T>& r, weak_ptr<T>& s) {
+  r.swap(s);
+}
+
+// See comments at the top of the file for a description of why this class
+// exists, and section 20.8.10.5 of the draft C++ standard (as of July 2009
+// the latest draft is N2914) for the detailed specification.
+template <typename T>
+class enable_shared_from_this {
+  friend class shared_ptr<T>;
+ public:
+  // Precondition: there must be a shared_ptr that owns *this and that was
+  // created, directly or indirectly, from a raw pointer of type T*. (The
+  // latter part of the condition is technical but not quite redundant; it
+  // rules out some complicated uses involving inheritance hierarchies.)
+  shared_ptr<T> shared_from_this() {
+    // Behavior is undefined if the precondition isn't satisfied; we choose
+    // to die with an access violation exception.
+#if DEBUG
+    if (weak_this_.expired()) {
+      // No shared_ptr owns this object.
+      *static_cast<int*>(NULL) = 0;
+    }
+#endif
+    return weak_this_.lock();
+  }
+  shared_ptr<const T> shared_from_this() const {
+#if DEBUG
+    if (weak_this_.expired()) {
+      // No shared_ptr owns this object.
+      *static_cast<int*>(NULL) = 0;
+    }
+#endif
+    return weak_this_.lock();
+  }
+
+ protected:
+  enable_shared_from_this() { }
+  enable_shared_from_this(const enable_shared_from_this& other) { }
+  enable_shared_from_this& operator=(const enable_shared_from_this& other) {
+    return *this;
+  }
+  ~enable_shared_from_this() { }
+
+ private:
+  weak_ptr<T> weak_this_;
+};
+
+// This is a helper function called by shared_ptr's constructor from a raw
+// pointer. If T inherits from enable_shared_from_this<T>, it sets up
+// weak_this_ so that shared_from_this works correctly. If T does not inherit
+// from weak_this we get a different overload, defined inline, which does
+// nothing.
+template<typename T>
+void shared_ptr<T>::MaybeSetupWeakThis(enable_shared_from_this<T>* ptr) {
+  if (ptr)
+    ptr->weak_this_ = *this;
+}
+
+#endif  // BAR_COMMON_SHARED_PTR_H_
diff --git a/third_party/breakpad/build.scons b/third_party/breakpad/build.scons
index c95c491..9f5c0ba 100644
--- a/third_party/breakpad/build.scons
+++ b/third_party/breakpad/build.scons
@@ -28,8 +28,8 @@
         '$MAIN_DIR/third_party/breakpad/src/',
         ],
     CCFLAGS = [
-        # Remove this after submitting breakpad fix.
-        '/wd4706',  # assignment within conditional expression
+        '/wd4061',  # enumerator in switch is not handled by a case label
+        '/wd4826',  # conversion is sign-extended
         ],
     CPPDEFINES = [
         # Make breakpad not use a TerminateThread call to clean up one of its
@@ -38,7 +38,7 @@
         ],
 )
 
-target_name = 'breakpad.lib'
+target_name = 'breakpad'
 
 breakpad_inputs = [
     'src/common/windows/guid_string.cc',
diff --git a/third_party/breakpad/src/client/windows/common/ipc_protocol.h b/third_party/breakpad/src/client/windows/common/ipc_protocol.h
index cba8699..7d101d3 100644
--- a/third_party/breakpad/src/client/windows/common/ipc_protocol.h
+++ b/third_party/breakpad/src/client/windows/common/ipc_protocol.h
@@ -95,7 +95,7 @@
 
 struct CustomClientInfo {
   const CustomInfoEntry* entries;
-  int count;
+  size_t count;
 };
 
 // Message structure for IPC between crash client and crash server.
diff --git a/third_party/breakpad/src/client/windows/crash_generation/client_info.cc b/third_party/breakpad/src/client/windows/crash_generation/client_info.cc
index 4752c4a..94f9c3c 100644
--- a/third_party/breakpad/src/client/windows/crash_generation/client_info.cc
+++ b/third_party/breakpad/src/client/windows/crash_generation/client_info.cc
@@ -101,26 +101,16 @@
   }
 }
 
-bool ClientInfo::UnregisterWaits() {
-  bool success = true;
-
+void ClientInfo::UnregisterWaits() {
   if (dump_request_wait_handle_) {
-    if (!UnregisterWait(dump_request_wait_handle_)) {
-      success = false;
-    } else {
-      dump_request_wait_handle_ = NULL;
-    }
+    UnregisterWait(dump_request_wait_handle_);
+    dump_request_wait_handle_ = NULL;
   }
 
   if (process_exit_wait_handle_) {
-    if (!UnregisterWait(process_exit_wait_handle_)) {
-      success = false;
-    } else {
-      process_exit_wait_handle_ = NULL;
-    }
+    UnregisterWait(process_exit_wait_handle_);
+    process_exit_wait_handle_ = NULL;
   }
-
-  return success;
 }
 
 bool ClientInfo::GetClientExceptionInfo(EXCEPTION_POINTERS** ex_info) const {
diff --git a/third_party/breakpad/src/client/windows/crash_generation/client_info.h b/third_party/breakpad/src/client/windows/crash_generation/client_info.h
index 774816f..47a5d21 100644
--- a/third_party/breakpad/src/client/windows/crash_generation/client_info.h
+++ b/third_party/breakpad/src/client/windows/crash_generation/client_info.h
@@ -83,7 +83,7 @@
   }
 
   // Unregister all waits for the client.
-  bool UnregisterWaits();
+  void UnregisterWaits();
 
   bool Initialize();
   bool GetClientExceptionInfo(EXCEPTION_POINTERS** ex_info) const;
diff --git a/third_party/breakpad/src/client/windows/crash_generation/crash_generation_client.cc b/third_party/breakpad/src/client/windows/crash_generation/crash_generation_client.cc
index 197807a..5e4e3cb 100644
--- a/third_party/breakpad/src/client/windows/crash_generation/crash_generation_client.cc
+++ b/third_party/breakpad/src/client/windows/crash_generation/crash_generation_client.cc
@@ -271,7 +271,8 @@
   return crash_event_ != NULL;
 }
 
-bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) {
+bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info,
+                                        MDRawAssertionInfo* assert_info) {
   if (!IsRegistered()) {
     return false;
   }
@@ -279,33 +280,23 @@
   exception_pointers_ = ex_info;
   thread_id_ = GetCurrentThreadId();
 
-  assert_info_.line = 0;
-  assert_info_.type = 0;
-  assert_info_.expression[0] = 0;
-  assert_info_.file[0] = 0;
-  assert_info_.function[0] = 0;
-
-  return SignalCrashEventAndWait();
-}
-
-bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) {
-  if (!IsRegistered()) {
-    return false;
-  }
-
-  exception_pointers_ = NULL;
-
   if (assert_info) {
     memcpy(&assert_info_, assert_info, sizeof(assert_info_));
   } else {
     memset(&assert_info_, 0, sizeof(assert_info_));
   }
 
-  thread_id_ = GetCurrentThreadId();
-
   return SignalCrashEventAndWait();
 }
 
+bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) {
+  return RequestDump(ex_info, NULL);
+}
+
+bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) {
+  return RequestDump(NULL, assert_info);
+}
+
 bool CrashGenerationClient::SignalCrashEventAndWait() {
   assert(crash_event_);
   assert(crash_generated_);
diff --git a/third_party/breakpad/src/client/windows/crash_generation/crash_generation_client.h b/third_party/breakpad/src/client/windows/crash_generation/crash_generation_client.h
index 81b0e6c..01d13dd 100644
--- a/third_party/breakpad/src/client/windows/crash_generation/crash_generation_client.h
+++ b/third_party/breakpad/src/client/windows/crash_generation/crash_generation_client.h
@@ -27,8 +27,8 @@
 // (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 CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H__
-#define CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H__
+#ifndef CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
+#define CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
 
 #include <windows.h>
 #include <dbghelp.h>
@@ -73,6 +73,9 @@
   // Returns true if the registration is successful; false otherwise.
   bool Register();
 
+  bool RequestDump(EXCEPTION_POINTERS* ex_info,
+                   MDRawAssertionInfo* assert_info);
+
   // Requests the crash server to generate a dump with the given
   // exception information.
   //
@@ -156,4 +159,4 @@
 
 }  // namespace google_breakpad
 
-#endif  // CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H__
+#endif  // CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
diff --git a/third_party/breakpad/src/client/windows/crash_generation/crash_generation_server.cc b/third_party/breakpad/src/client/windows/crash_generation/crash_generation_server.cc
index ac76e59..61af1b2 100644
--- a/third_party/breakpad/src/client/windows/crash_generation/crash_generation_server.cc
+++ b/third_party/breakpad/src/client/windows/crash_generation/crash_generation_server.cc
@@ -34,6 +34,8 @@
 #include "client/windows/common/auto_critical_section.h"
 #include "processor/scoped_ptr.h"
 
+#include "client/windows/crash_generation/client_info.h"
+
 namespace google_breakpad {
 
 // Output buffer size.
@@ -113,7 +115,7 @@
       exit_context_(exit_context),
       generate_dumps_(generate_dumps),
       dump_generator_(NULL),
-      server_state_(IPC_SERVER_STATE_INITIAL),
+      server_state_(IPC_SERVER_STATE_UNINITIALIZED),
       shutting_down_(false),
       overlapped_(),
       client_info_(NULL),
@@ -197,10 +199,18 @@
     CloseHandle(server_alive_handle_);
   }
 
+  if (overlapped_.hEvent) {
+    CloseHandle(overlapped_.hEvent);
+  }
+
   DeleteCriticalSection(&clients_sync_);
 }
 
 bool CrashGenerationServer::Start() {
+  if (server_state_ != IPC_SERVER_STATE_UNINITIALIZED) {
+    return false;
+  }
+
   server_state_ = IPC_SERVER_STATE_INITIAL;
 
   server_alive_handle_ = CreateMutex(NULL, TRUE, NULL);
@@ -218,12 +228,14 @@
   }
 
   // Register a callback with the thread pool for the client connection.
-  RegisterWaitForSingleObject(&pipe_wait_handle_,
-                              overlapped_.hEvent,
-                              OnPipeConnected,
-                              this,
-                              INFINITE,
-                              kPipeIOThreadFlags);
+  if (!RegisterWaitForSingleObject(&pipe_wait_handle_,
+                                   overlapped_.hEvent,
+                                   OnPipeConnected,
+                                   this,
+                                   INFINITE,
+                                   kPipeIOThreadFlags)) {
+    return false;
+  }
 
   pipe_ = CreateNamedPipe(pipe_name_.c_str(),
                           kPipeAttr,
@@ -237,9 +249,12 @@
     return false;
   }
 
-  // Signal the event to start a separate thread to handle
-  // client connections.
-  return SetEvent(overlapped_.hEvent) != FALSE;
+  // Kick-start the state machine. This will initiate an asynchronous wait
+  // for client connections.
+  HandleInitialState();
+
+  // If we are in error state, it's because we failed to start listening.
+  return server_state_ != IPC_SERVER_STATE_ERROR;
 }
 
 // If the server thread serving clients ever gets into the
@@ -281,33 +296,29 @@
   assert(server_state_ == IPC_SERVER_STATE_INITIAL);
 
   if (!ResetEvent(overlapped_.hEvent)) {
-    server_state_ = IPC_SERVER_STATE_ERROR;
+    EnterErrorState();
     return;
   }
 
   bool success = ConnectNamedPipe(pipe_, &overlapped_) != FALSE;
+  DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
 
   // From MSDN, it is not clear that when ConnectNamedPipe is used
   // in an overlapped mode, will it ever return non-zero value, and
   // if so, in what cases.
   assert(!success);
 
-  DWORD error_code = GetLastError();
   switch (error_code) {
     case ERROR_IO_PENDING:
-      server_state_ = IPC_SERVER_STATE_CONNECTING;
+      EnterStateWhenSignaled(IPC_SERVER_STATE_CONNECTING);
       break;
 
     case ERROR_PIPE_CONNECTED:
-      if (SetEvent(overlapped_.hEvent)) {
-        server_state_ = IPC_SERVER_STATE_CONNECTED;
-      } else {
-        server_state_ = IPC_SERVER_STATE_ERROR;
-      }
+      EnterStateImmediately(IPC_SERVER_STATE_CONNECTED);
       break;
 
     default:
-      server_state_ = IPC_SERVER_STATE_ERROR;
+      EnterErrorState();
       break;
   }
 }
@@ -326,14 +337,14 @@
                                      &overlapped_,
                                      &bytes_count,
                                      FALSE) != FALSE;
+  DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
 
   if (success) {
-    server_state_ = IPC_SERVER_STATE_CONNECTED;
-    return;
-  }
-
-  if (GetLastError() != ERROR_IO_INCOMPLETE) {
-    server_state_ = IPC_SERVER_STATE_DISCONNECTING;
+    EnterStateImmediately(IPC_SERVER_STATE_CONNECTED);
+  } else if (error_code != ERROR_IO_INCOMPLETE) {
+    EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
+  } else {
+    // remain in CONNECTING state
   }
 }
 
@@ -351,16 +362,17 @@
                           sizeof(msg_),
                           &bytes_count,
                           &overlapped_) != FALSE;
+  DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
 
   // Note that the asynchronous read issued above can finish before the
   // code below executes. But, it is okay to change state after issuing
   // the asynchronous read. This is because even if the asynchronous read
   // is done, the callback for it would not be executed until the current
   // thread finishes its execution.
-  if (success || GetLastError() == ERROR_IO_PENDING) {
-    server_state_ = IPC_SERVER_STATE_READING;
+  if (success || error_code == ERROR_IO_PENDING) {
+    EnterStateWhenSignaled(IPC_SERVER_STATE_READING);
   } else {
-    server_state_ = IPC_SERVER_STATE_DISCONNECTING;
+    EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
   }
 }
 
@@ -376,21 +388,18 @@
                                      &overlapped_,
                                      &bytes_count,
                                      FALSE) != FALSE;
+  DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
 
   if (success && bytes_count == sizeof(ProtocolMessage)) {
-    server_state_ = IPC_SERVER_STATE_READ_DONE;
-    return;
+    EnterStateImmediately(IPC_SERVER_STATE_READ_DONE);
+  } else {
+    // We should never get an I/O incomplete since we should not execute this
+    // unless the Read has finished and the overlapped event is signaled. If
+    // we do get INCOMPLETE, we have a bug in our code.
+    assert(error_code != ERROR_IO_INCOMPLETE);
+
+    EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
   }
-
-  DWORD error_code;
-  error_code = GetLastError();
-
-  // We should never get an I/O incomplete since we should not execute this
-  // unless the Read has finished and the overlapped event is signaled. If
-  // we do get INCOMPLETE, we have a bug in our code.
-  assert(error_code != ERROR_IO_INCOMPLETE);
-
-  server_state_ = IPC_SERVER_STATE_DISCONNECTING;
 }
 
 // When the server thread serving the client is in the READ_DONE state,
@@ -403,7 +412,7 @@
   assert(server_state_ == IPC_SERVER_STATE_READ_DONE);
 
   if (!IsClientRequestValid(msg_)) {
-    server_state_ = IPC_SERVER_STATE_DISCONNECTING;
+    EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
     return;
   }
 
@@ -417,22 +426,26 @@
                      msg_.custom_client_info));
 
   if (!client_info->Initialize()) {
-    server_state_ = IPC_SERVER_STATE_DISCONNECTING;
+    EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
     return;
   }
 
+  // Issues an asynchronous WriteFile call if successful.
+  // Iff successful, assigns ownership of the client_info pointer to the server
+  // instance, in which case we must be sure not to free it in this function.
   if (!RespondToClient(client_info.get())) {
-    server_state_ = IPC_SERVER_STATE_DISCONNECTING;
+    EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
     return;
   }
 
+  client_info_ = client_info.release();
+
   // Note that the asynchronous write issued by RespondToClient function
   // can finish before  the code below executes. But it is okay to change
   // state after issuing the asynchronous write. This is because even if
   // the asynchronous write is done, the callback for it would not be
   // executed until the current thread finishes its execution.
-  server_state_ = IPC_SERVER_STATE_WRITING;
-  client_info_ = client_info.release();
+  EnterStateWhenSignaled(IPC_SERVER_STATE_WRITING);
 }
 
 // When the server thread serving the clients is in the WRITING state,
@@ -447,21 +460,19 @@
                                      &overlapped_,
                                      &bytes_count,
                                      FALSE) != FALSE;
+  DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
 
   if (success) {
-    server_state_ = IPC_SERVER_STATE_WRITE_DONE;
+    EnterStateImmediately(IPC_SERVER_STATE_WRITE_DONE);
     return;
   }
 
-  DWORD error_code;
-  error_code = GetLastError();
-
   // We should never get an I/O incomplete since we should not execute this
   // unless the Write has finished and the overlapped event is signaled. If
   // we do get INCOMPLETE, we have a bug in our code.
   assert(error_code != ERROR_IO_INCOMPLETE);
 
-  server_state_ = IPC_SERVER_STATE_DISCONNECTING;
+  EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
 }
 
 // When the server thread serving the clients is in the WRITE_DONE state,
@@ -471,23 +482,20 @@
 void CrashGenerationServer::HandleWriteDoneState() {
   assert(server_state_ == IPC_SERVER_STATE_WRITE_DONE);
 
-  server_state_ = IPC_SERVER_STATE_READING_ACK;
-
   DWORD bytes_count = 0;
   bool success = ReadFile(pipe_,
-                          &msg_,
-                          sizeof(msg_),
-                          &bytes_count,
-                          &overlapped_) != FALSE;
+                           &msg_,
+                           sizeof(msg_),
+                           &bytes_count,
+                           &overlapped_) != FALSE;
+  DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
 
   if (success) {
-    return;
-  }
-
-  DWORD error_code = GetLastError();
-
-  if (error_code != ERROR_IO_PENDING) {
-    server_state_ = IPC_SERVER_STATE_DISCONNECTING;
+    EnterStateImmediately(IPC_SERVER_STATE_READING_ACK);
+  } else if (error_code == ERROR_IO_PENDING) {
+    EnterStateWhenSignaled(IPC_SERVER_STATE_READING_ACK);
+  } else {
+    EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
   }
 }
 
@@ -501,6 +509,7 @@
                                      &overlapped_,
                                      &bytes_count,
                                      FALSE) != FALSE;
+  DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
 
   if (success) {
     // The connection handshake with the client is now complete; perform
@@ -509,15 +518,13 @@
       connect_callback_(connect_context_, client_info_);
     }
   } else {
-    DWORD error_code = GetLastError();
-
     // We should never get an I/O incomplete since we should not execute this
     // unless the Read has finished and the overlapped event is signaled. If
     // we do get INCOMPLETE, we have a bug in our code.
     assert(error_code != ERROR_IO_INCOMPLETE);
   }
 
-  server_state_ = IPC_SERVER_STATE_DISCONNECTING;
+  EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
 }
 
 // When the server thread serving the client is in the DISCONNECTING state,
@@ -537,12 +544,12 @@
   overlapped_.Pointer = NULL;
 
   if (!ResetEvent(overlapped_.hEvent)) {
-    server_state_ = IPC_SERVER_STATE_ERROR;
+    EnterErrorState();
     return;
   }
 
   if (!DisconnectNamedPipe(pipe_)) {
-    server_state_ = IPC_SERVER_STATE_ERROR;
+    EnterErrorState();
     return;
   }
 
@@ -552,7 +559,21 @@
     return;
   }
 
-  server_state_ = IPC_SERVER_STATE_INITIAL;
+  EnterStateImmediately(IPC_SERVER_STATE_INITIAL);
+}
+
+void CrashGenerationServer::EnterErrorState() {
+  SetEvent(overlapped_.hEvent);
+  server_state_ = IPC_SERVER_STATE_ERROR;
+}
+
+void CrashGenerationServer::EnterStateWhenSignaled(IPCServerState state) {
+  server_state_ = state;
+}
+
+void CrashGenerationServer::EnterStateImmediately(IPCServerState state) {
+  server_state_ = state;
+
   if (!SetEvent(overlapped_.hEvent)) {
     server_state_ = IPC_SERVER_STATE_ERROR;
   }
@@ -624,18 +645,25 @@
     return false;
   }
 
+  DWORD bytes_count = 0;
+  bool success = WriteFile(pipe_,
+                            &reply,
+                            sizeof(reply),
+                            &bytes_count,
+                            &overlapped_) != FALSE;
+  DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
+
+  if (!success && error_code != ERROR_IO_PENDING) {
+    return false;
+  }
+
+  // Takes over ownership of client_info. We MUST return true if AddClient
+  // succeeds.
   if (!AddClient(client_info)) {
     return false;
   }
 
-  DWORD bytes_count = 0;
-  bool success = WriteFile(pipe_,
-                           &reply,
-                           sizeof(reply),
-                           &bytes_count,
-                           &overlapped_) != FALSE;
-
-  return success || GetLastError() == ERROR_IO_PENDING;
+  return true;
 }
 
 // The server thread servicing the clients runs this method. The method
@@ -737,7 +765,7 @@
 
 // static
 void CALLBACK CrashGenerationServer::OnPipeConnected(void* context, BOOLEAN) {
-  assert (context);
+  assert(context);
 
   CrashGenerationServer* obj =
       reinterpret_cast<CrashGenerationServer*>(context);
diff --git a/third_party/breakpad/src/client/windows/crash_generation/crash_generation_server.h b/third_party/breakpad/src/client/windows/crash_generation/crash_generation_server.h
index cacb639..31a353b 100644
--- a/third_party/breakpad/src/client/windows/crash_generation/crash_generation_server.h
+++ b/third_party/breakpad/src/client/windows/crash_generation/crash_generation_server.h
@@ -33,11 +33,11 @@
 #include <list>
 #include <string>
 #include "client/windows/common/ipc_protocol.h"
-#include "client/windows/crash_generation/client_info.h"
 #include "client/windows/crash_generation/minidump_generator.h"
 #include "processor/scoped_ptr.h"
 
 namespace google_breakpad {
+class ClientInfo;
 
 // Abstraction for server side implementation of out-of-process crash
 // generation protocol for Windows platform only. It generates Windows
@@ -91,7 +91,8 @@
 
   ~CrashGenerationServer();
 
-  // Performs initialization steps needed to start listening to clients.
+  // Performs initialization steps needed to start listening to clients. Upon
+  // successful return clients may connect to this server's pipe.
   //
   // Returns true if initialization is successful; false otherwise.
   bool Start();
@@ -100,6 +101,9 @@
   // Various states the client can be in during the handshake with
   // the server.
   enum IPCServerState {
+    // Server starts in this state.
+    IPC_SERVER_STATE_UNINITIALIZED,
+
     // Server is in error state and it cannot serve any clients.
     IPC_SERVER_STATE_ERROR,
 
@@ -192,6 +196,21 @@
   // Generates dump for the given client.
   bool GenerateDump(const ClientInfo& client, std::wstring* dump_path);
 
+  // Puts the server in a permanent error state and sets a signal such that
+  // the state will be immediately entered after the current state transition
+  // is complete.
+  void EnterErrorState();
+
+  // Puts the server in the specified state and sets a signal such that the
+  // state is immediately entered after the current state transition is
+  // complete.
+  void EnterStateImmediately(IPCServerState state);
+
+  // Puts the server in the specified state. No signal will be set, so the state
+  // transition will only occur when signaled manually or by completion of an
+  // asynchronous IO operation.
+  void EnterStateWhenSignaled(IPCServerState state);
+
   // Sync object for thread-safe access to the shared list of clients.
   CRITICAL_SECTION clients_sync_;
 
diff --git a/third_party/breakpad/src/client/windows/crash_generation/minidump_generator.cc b/third_party/breakpad/src/client/windows/crash_generation/minidump_generator.cc
index c03b191..37bd55e 100644
--- a/third_party/breakpad/src/client/windows/crash_generation/minidump_generator.cc
+++ b/third_party/breakpad/src/client/windows/crash_generation/minidump_generator.cc
@@ -68,6 +68,23 @@
                                       MINIDUMP_TYPE dump_type,
                                       bool is_client_pointers,
                                       wstring* dump_path) {
+  // Just call the full WriteMinidump with NULL as the full_dump_path.
+  return this->WriteMinidump(process_handle, process_id, thread_id,
+                             requesting_thread_id, exception_pointers,
+                             assert_info, dump_type, is_client_pointers,
+                             dump_path, NULL);
+}
+
+bool MinidumpGenerator::WriteMinidump(HANDLE process_handle,
+                                      DWORD process_id,
+                                      DWORD thread_id,
+                                      DWORD requesting_thread_id,
+                                      EXCEPTION_POINTERS* exception_pointers,
+                                      MDRawAssertionInfo* assert_info,
+                                      MINIDUMP_TYPE dump_type,
+                                      bool is_client_pointers,
+                                      wstring* dump_path,
+                                      wstring* full_dump_path) {
   MiniDumpWriteDumpType write_dump = GetWriteDump();
   if (!write_dump) {
     return false;
@@ -223,6 +240,9 @@
   if (result && dump_path) {
     *dump_path = dump_file_path;
   }
+  if (result && full_memory_dump && full_dump_path) {
+    *full_dump_path = full_dump_file_path;
+  }
 
   return result;
 }
@@ -275,7 +295,7 @@
   UUID id = {0};
 
   UuidCreateType create_uuid = GetCreateUuid();
-  if(!create_uuid) {
+  if (!create_uuid) {
     return false;
   }
 
diff --git a/third_party/breakpad/src/client/windows/crash_generation/minidump_generator.h b/third_party/breakpad/src/client/windows/crash_generation/minidump_generator.h
index 8ab6a8f..5f9e4b5 100644
--- a/third_party/breakpad/src/client/windows/crash_generation/minidump_generator.h
+++ b/third_party/breakpad/src/client/windows/crash_generation/minidump_generator.h
@@ -27,8 +27,8 @@
 // (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 CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATION_H__
-#define CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATION_H__
+#ifndef CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_
+#define CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_
 
 #include <windows.h>
 #include <dbghelp.h>
@@ -61,6 +61,20 @@
                      bool is_client_pointers,
                      std::wstring* dump_path);
 
+  // Writes the minidump with the given parameters. Stores the dump file
+  // path in the dump_path (and full_dump_path) parameter if dump
+  // generation succeeds. full_dump_path and dump_path can be NULL.
+  bool WriteMinidump(HANDLE process_handle,
+                     DWORD process_id,
+                     DWORD thread_id,
+                     DWORD requesting_thread_id,
+                     EXCEPTION_POINTERS* exception_pointers,
+                     MDRawAssertionInfo* assert_info,
+                     MINIDUMP_TYPE dump_type,
+                     bool is_client_pointers,
+                     std::wstring* dump_path,
+                     std::wstring* full_dump_path);
+
  private:
   // Function pointer type for MiniDumpWriteDump, which is looked up
   // dynamically.
@@ -118,4 +132,4 @@
 
 }  // namespace google_breakpad
 
-#endif  // CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATION_H__
+#endif  // CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_
diff --git a/third_party/breakpad/src/client/windows/handler/exception_handler.cc b/third_party/breakpad/src/client/windows/handler/exception_handler.cc
index 89e8410..ec5397d 100644
--- a/third_party/breakpad/src/client/windows/handler/exception_handler.cc
+++ b/third_party/breakpad/src/client/windows/handler/exception_handler.cc
@@ -29,6 +29,7 @@
 
 #include <ObjBase.h>
 
+#include <algorithm>
 #include <cassert>
 #include <cstdio>
 
@@ -38,11 +39,20 @@
 #include "client/windows/handler/exception_handler.h"
 #include "common/windows/guid_string.h"
 
+typedef VOID (WINAPI *RtlCaptureContextPtr) (PCONTEXT pContextRecord);
+
 namespace google_breakpad {
 
 static const int kWaitForHandlerThreadMs = 60000;
 static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024;
 
+// This is passed as the context to the MinidumpWriteDump callback.
+typedef struct {
+  ULONG64 memory_base;
+  ULONG memory_size;
+  bool finished;
+} MinidumpCallbackContext;
+
 vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
 LONG ExceptionHandler::handler_stack_index_ = 0;
 CRITICAL_SECTION ExceptionHandler::handler_stack_critical_section_;
@@ -249,12 +259,12 @@
       // TODO(mmentovai): use advapi32!ReportEvent to log the warning to the
       // system's application event log.
       fprintf(stderr, "warning: removing Breakpad handler out of order\n");
-      for (vector<ExceptionHandler*>::iterator iterator =
-               handler_stack_->begin();
-           iterator != handler_stack_->end();
-           ++iterator) {
+      vector<ExceptionHandler*>::iterator iterator = handler_stack_->begin();
+      while (iterator != handler_stack_->end()) {
         if (*iterator == this) {
-          handler_stack_->erase(iterator);
+          iterator = handler_stack_->erase(iterator);
+        } else {
+          ++iterator;
         }
       }
     }
@@ -285,9 +295,9 @@
 #else
     TerminateThread(handler_thread_, 1);
 #endif  // BREAKPAD_NO_TERMINATE_THREAD
-    ::CloseHandle(handler_thread_);
-    handler_thread_ = NULL;
 
+    CloseHandle(handler_thread_);
+    handler_thread_ = NULL;
     DeleteCriticalSection(&handler_critical_section_);
     CloseHandle(handler_start_semaphore_);
     CloseHandle(handler_finish_semaphore_);
@@ -345,7 +355,7 @@
   AutoExceptionHandler() {
     // Increment handler_stack_index_ so that if another Breakpad handler is
     // registered using this same HandleException function, and it needs to be
-    // called while this handler is running (either becaause this handler
+    // called while this handler is running (either because this handler
     // declines to handle the exception, or an exception occurs during
     // handling), HandleException will find the appropriate ExceptionHandler
     // object in handler_stack_ to deliver the exception to.
@@ -363,7 +373,6 @@
     handler_ = ExceptionHandler::handler_stack_->at(
         ExceptionHandler::handler_stack_->size() -
         ++ExceptionHandler::handler_stack_index_);
-    LeaveCriticalSection(&ExceptionHandler::handler_stack_critical_section_);
 
     // In case another exception occurs while this handler is doing its thing,
     // it should be delivered to the previous filter.
@@ -382,7 +391,6 @@
 #endif  // _MSC_VER >= 1400
     _set_purecall_handler(ExceptionHandler::HandlePureVirtualCall);
 
-    EnterCriticalSection(&ExceptionHandler::handler_stack_critical_section_);
     --ExceptionHandler::handler_stack_index_;
     LeaveCriticalSection(&ExceptionHandler::handler_stack_critical_section_);
   }
@@ -482,16 +490,47 @@
   assertion.line = line;
   assertion.type = MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER;
 
+  // Make up an exception record for the current thread and CPU context
+  // to make it possible for the crash processor to classify these
+  // as do regular crashes, and to make it humane for developers to
+  // analyze them.
+  EXCEPTION_RECORD exception_record = {};
+  CONTEXT exception_context = {};
+  EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context };
+
+  EXCEPTION_POINTERS* exinfo = NULL;
+
+  RtlCaptureContextPtr fnRtlCaptureContext = (RtlCaptureContextPtr)
+    GetProcAddress(GetModuleHandleW(L"kernel32"), "RtlCaptureContext");
+  if (fnRtlCaptureContext) {
+    fnRtlCaptureContext(&exception_context);
+
+    exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
+
+    // We store pointers to the the expression and function strings,
+    // and the line as exception parameters to make them easy to
+    // access by the developer on the far side.
+    exception_record.NumberParameters = 3;
+    exception_record.ExceptionInformation[0] =
+        reinterpret_cast<ULONG_PTR>(&assertion.expression);
+    exception_record.ExceptionInformation[1] =
+        reinterpret_cast<ULONG_PTR>(&assertion.file);
+    exception_record.ExceptionInformation[2] = assertion.line;
+
+    exinfo = &exception_ptrs;
+  }
+
   bool success = false;
   // In case of out-of-process dump generation, directly call
   // WriteMinidumpWithException since there is no separate thread running.
   if (current_handler->IsOutOfProcess()) {
     success = current_handler->WriteMinidumpWithException(
         GetCurrentThreadId(),
-        NULL,
+        exinfo,
         &assertion);
   } else {
-    success = current_handler->WriteMinidumpOnHandlerThread(NULL, &assertion);
+    success = current_handler->WriteMinidumpOnHandlerThread(exinfo,
+                                                            &assertion);
   }
 
   if (!success) {
@@ -530,6 +569,8 @@
 
 // static
 void ExceptionHandler::HandlePureVirtualCall() {
+  // This is an pure virtual function call, not an exception.  It's safe to
+  // play with sprintf here.
   AutoExceptionHandler auto_exception_handler;
   ExceptionHandler* current_handler = auto_exception_handler.get_handler();
 
@@ -537,6 +578,36 @@
   memset(&assertion, 0, sizeof(assertion));
   assertion.type = MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL;
 
+  // Make up an exception record for the current thread and CPU context
+  // to make it possible for the crash processor to classify these
+  // as do regular crashes, and to make it humane for developers to
+  // analyze them.
+  EXCEPTION_RECORD exception_record = {};
+  CONTEXT exception_context = {};
+  EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context };
+
+  EXCEPTION_POINTERS* exinfo = NULL;
+
+  RtlCaptureContextPtr fnRtlCaptureContext = (RtlCaptureContextPtr)
+    GetProcAddress(GetModuleHandleW(L"kernel32"), "RtlCaptureContext");
+  if (fnRtlCaptureContext) {
+    fnRtlCaptureContext(&exception_context);
+
+    exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
+
+    // We store pointers to the the expression and function strings,
+    // and the line as exception parameters to make them easy to
+    // access by the developer on the far side.
+    exception_record.NumberParameters = 3;
+    exception_record.ExceptionInformation[0] =
+        reinterpret_cast<ULONG_PTR>(&assertion.expression);
+    exception_record.ExceptionInformation[1] =
+        reinterpret_cast<ULONG_PTR>(&assertion.file);
+    exception_record.ExceptionInformation[2] = assertion.line;
+
+    exinfo = &exception_ptrs;
+  }
+
   bool success = false;
   // In case of out-of-process dump generation, directly call
   // WriteMinidumpWithException since there is no separate thread running.
@@ -544,10 +615,11 @@
   if (current_handler->IsOutOfProcess()) {
     success = current_handler->WriteMinidumpWithException(
         GetCurrentThreadId(),
-        NULL,
+        exinfo,
         &assertion);
   } else {
-    success = current_handler->WriteMinidumpOnHandlerThread(NULL, &assertion);
+    success = current_handler->WriteMinidumpOnHandlerThread(exinfo,
+                                                            &assertion);
   }
 
   if (!success) {
@@ -648,13 +720,7 @@
 
   bool success = false;
   if (IsOutOfProcess()) {
-    // Use the EXCEPTION_POINTERS overload for RequestDump if
-    // both exinfo and assertion are NULL.
-    if (!assertion) {
-      success = crash_generation_client_->RequestDump(exinfo);
-    } else {
-      success = crash_generation_client_->RequestDump(assertion);
-    }
+    success = crash_generation_client_->RequestDump(exinfo, assertion);
   } else {
     if (minidump_write_dump_) {
       HANDLE dump_file = CreateFile(next_minidump_path_c_,
@@ -670,12 +736,13 @@
         except_info.ExceptionPointers = exinfo;
         except_info.ClientPointers = FALSE;
 
-        // Add an MDRawBreakpadInfo stream to the minidump, to provide additional
-        // information about the exception handler to the Breakpad processor.  The
-        // information will help the processor determine which threads are
-        // relevant.  The Breakpad processor does not require this information but
-        // can function better with Breakpad-generated dumps when it is present.
-        // The native debugger is not harmed by the presence of this information.
+        // Add an MDRawBreakpadInfo stream to the minidump, to provide
+        // additional information about the exception handler to the Breakpad
+        // processor. The information will help the processor determine which
+        // threads are relevant.  The Breakpad processor does not require this
+        // information but can function better with Breakpad-generated dumps
+        // when it is present. The native debugger is not harmed by the
+        // presence of this information.
         MDRawBreakpadInfo breakpad_info;
         breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
                                MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
@@ -699,6 +766,50 @@
           ++user_streams.UserStreamCount;
         }
 
+        MINIDUMP_CALLBACK_INFORMATION callback;
+        MinidumpCallbackContext context;
+        MINIDUMP_CALLBACK_INFORMATION* callback_pointer = NULL;
+        // Older versions of DbgHelp.dll don't correctly put the memory around
+        // the faulting instruction pointer into the minidump. This
+        // callback will ensure that it gets included.
+        if (exinfo) {
+          // Find a memory region of 256 bytes centered on the
+          // faulting instruction pointer.
+          const ULONG64 instruction_pointer = 
+#if defined(_M_IX86)
+            exinfo->ContextRecord->Eip;
+#elif defined(_M_AMD64)
+            exinfo->ContextRecord->Rip;
+#else
+#error Unsupported platform
+#endif
+
+          MEMORY_BASIC_INFORMATION info;
+          if (VirtualQuery(reinterpret_cast<LPCVOID>(instruction_pointer),
+                           &info,
+                           sizeof(MEMORY_BASIC_INFORMATION)) != 0 &&
+              info.State == MEM_COMMIT) {
+            // Attempt to get 128 bytes before and after the instruction
+            // pointer, but settle for whatever's available up to the
+            // boundaries of the memory region.
+            const ULONG64 kIPMemorySize = 256;
+            context.memory_base =
+              (std::max)(reinterpret_cast<ULONG64>(info.BaseAddress),
+                       instruction_pointer - (kIPMemorySize / 2));
+            ULONG64 end_of_range =
+              (std::min)(instruction_pointer + (kIPMemorySize / 2),
+                       reinterpret_cast<ULONG64>(info.BaseAddress)
+                       + info.RegionSize);
+            context.memory_size =
+                static_cast<ULONG>(end_of_range - context.memory_base);
+
+            context.finished = false;
+            callback.CallbackRoutine = MinidumpWriteDumpCallback;
+            callback.CallbackParam = reinterpret_cast<void*>(&context);
+            callback_pointer = &callback;
+          }
+        }
+
         // The explicit comparison to TRUE avoids a warning (C4800).
         success = (minidump_write_dump_(GetCurrentProcess(),
                                         GetCurrentProcessId(),
@@ -706,7 +817,7 @@
                                         dump_type_,
                                         exinfo ? &except_info : NULL,
                                         &user_streams,
-                                        NULL) == TRUE);
+                                        callback_pointer) == TRUE);
 
         CloseHandle(dump_file);
       }
@@ -725,6 +836,45 @@
   return success;
 }
 
+// static
+BOOL CALLBACK ExceptionHandler::MinidumpWriteDumpCallback(
+    PVOID context,
+    const PMINIDUMP_CALLBACK_INPUT callback_input,
+    PMINIDUMP_CALLBACK_OUTPUT callback_output) {
+  switch (callback_input->CallbackType) {
+  case MemoryCallback: {
+    MinidumpCallbackContext* callback_context =
+        reinterpret_cast<MinidumpCallbackContext*>(context);
+    if (callback_context->finished)
+      return FALSE;
+
+    // Include the specified memory region.
+    callback_output->MemoryBase = callback_context->memory_base;
+    callback_output->MemorySize = callback_context->memory_size;
+    callback_context->finished = true;
+    return TRUE;
+  }
+    
+    // Include all modules.
+  case IncludeModuleCallback:
+  case ModuleCallback:
+    return TRUE;
+
+    // Include all threads.
+  case IncludeThreadCallback:
+  case ThreadCallback:
+    return TRUE;
+
+    // Stop receiving cancel callbacks.
+  case CancelCallback:
+    callback_output->CheckCancel = FALSE;
+    callback_output->Cancel = FALSE;
+    return TRUE;
+  }
+  // Ignore other callback types.
+  return FALSE;
+}
+
 void ExceptionHandler::UpdateNextID() {
   assert(uuid_create_);
   UUID id = {0};
diff --git a/third_party/breakpad/src/client/windows/handler/exception_handler.h b/third_party/breakpad/src/client/windows/handler/exception_handler.h
index 2cacdc3..2c2e7b7 100644
--- a/third_party/breakpad/src/client/windows/handler/exception_handler.h
+++ b/third_party/breakpad/src/client/windows/handler/exception_handler.h
@@ -277,6 +277,13 @@
                                   EXCEPTION_POINTERS* exinfo,
                                   MDRawAssertionInfo* assertion);
 
+  // This function is used as a callback when calling MinidumpWriteDump,
+  // in order to add additional memory regions to the dump.
+  static BOOL CALLBACK MinidumpWriteDumpCallback(
+      PVOID context,
+      const PMINIDUMP_CALLBACK_INPUT callback_input,
+      PMINIDUMP_CALLBACK_OUTPUT callback_output);
+
   // Generates a new ID and stores it in next_minidump_id_, and stores the
   // path of the next minidump to be written in next_minidump_path_.
   void UpdateNextID();
diff --git a/third_party/breakpad/src/client/windows/sender/crash_report_sender.cc b/third_party/breakpad/src/client/windows/sender/crash_report_sender.cc
index 3d16ef2..ecf626d 100644
--- a/third_party/breakpad/src/client/windows/sender/crash_report_sender.cc
+++ b/third_party/breakpad/src/client/windows/sender/crash_report_sender.cc
@@ -75,8 +75,7 @@
   if (result) {
     ReportSent(today);
     return RESULT_SUCCEEDED;
-  } else if (http_response == 400) {  // TODO: update if/when the server
-                                      //       switches to a different code
+  } else if (http_response >= 400 && http_response < 500) {
     return RESULT_REJECTED;
   } else {
     return RESULT_FAILED;
diff --git a/third_party/breakpad/src/common/windows/http_upload.cc b/third_party/breakpad/src/common/windows/http_upload.cc
index 686c2ab..ddf61f7 100644
--- a/third_party/breakpad/src/common/windows/http_upload.cc
+++ b/third_party/breakpad/src/common/windows/http_upload.cc
@@ -30,7 +30,7 @@
 #include <assert.h>
 
 // Disable exception handler warnings.
-#pragma warning( disable : 4530 ) 
+#pragma warning( disable : 4530 )
 
 #include <fstream>
 
@@ -122,6 +122,7 @@
   }
 
   DWORD http_open_flags = secure ? INTERNET_FLAG_SECURE : 0;
+  http_open_flags |= INTERNET_FLAG_NO_COOKIES;
   AutoInternetHandle request(HttpOpenRequest(connection.get(),
                                              L"POST",
                                              path,
@@ -211,17 +212,19 @@
 
   DWORD bytes_available;
   DWORD total_read = 0;
-  bool return_code;
+  BOOL return_code;
 
-  while ((return_code = InternetQueryDataAvailable(request, &bytes_available,
-                                                   0, 0) != 0) &&
-          bytes_available > 0) {
+  while (((return_code = InternetQueryDataAvailable(request, &bytes_available,
+	  0, 0)) != 0) && bytes_available > 0) {
+
     vector<char> response_buffer(bytes_available);
     DWORD size_read;
 
-    if ((return_code = InternetReadFile(request, &response_buffer[0],
-                                        bytes_available, &size_read) != 0) &&
-        size_read > 0) {
+    return_code = InternetReadFile(request,
+                                   &response_buffer[0],
+                                   bytes_available, &size_read);
+
+    if (return_code && size_read > 0) {
       total_read += size_read;
       response_body.append(&response_buffer[0], size_read);
     } else {
@@ -271,8 +274,7 @@
                                      const wstring &boundary,
                                      string *request_body) {
   vector<char> contents;
-  GetFileContents(upload_file, &contents);
-  if (contents.empty()) {
+  if (!GetFileContents(upload_file, &contents)) {
     return false;
   }
 
@@ -319,7 +321,7 @@
 }
 
 // static
-void HTTPUpload::GetFileContents(const wstring &filename,
+bool HTTPUpload::GetFileContents(const wstring &filename,
                                  vector<char> *contents) {
   // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
   // wchar_t* filename, so use _wfopen directly in that case.  For VC8 and
@@ -333,16 +335,16 @@
 #endif  // _MSC_VER >= 1400
   if (file.is_open()) {
     file.seekg(0, ios::end);
-    int length = file.tellg();
+    std::streamoff length = file.tellg();
     contents->resize(length);
     if (length != 0) {
-        file.seekg(0, ios::beg);
-        file.read(&((*contents)[0]), length);
+      file.seekg(0, ios::beg);
+      file.read(&((*contents)[0]), length);
     }
     file.close();
-  } else {
-    contents->clear();
+    return true;
   }
+  return false;
 }
 
 // static
diff --git a/third_party/breakpad/src/common/windows/http_upload.h b/third_party/breakpad/src/common/windows/http_upload.h
index f7c69f1..8a17aab 100644
--- a/third_party/breakpad/src/common/windows/http_upload.h
+++ b/third_party/breakpad/src/common/windows/http_upload.h
@@ -98,7 +98,7 @@
                                   string *request_body);
 
   // Fills the supplied vector with the contents of filename.
-  static void GetFileContents(const wstring &filename, vector<char> *contents);
+  static bool GetFileContents(const wstring &filename, vector<char> *contents);
 
   // Converts a UTF8 string to UTF16.
   static wstring UTF8ToWide(const string &utf8);
diff --git a/third_party/breakpad/src/common/windows/string_utils-inl.h b/third_party/breakpad/src/common/windows/string_utils-inl.h
index 6f65081..d281aaa 100644
--- a/third_party/breakpad/src/common/windows/string_utils-inl.h
+++ b/third_party/breakpad/src/common/windows/string_utils-inl.h
@@ -87,6 +87,9 @@
   // without setting wcs.
   static bool safe_mbstowcs(const string &mbs, wstring *wcs);
 
+  // The inverse of safe_mbstowcs.
+  static bool safe_wcstombs(const wstring &wcs, string *mbs);
+
   // Returns the base name of a file, e.g. strips off the path.
   static wstring GetBaseName(const wstring &filename);
 
diff --git a/third_party/breakpad/src/google_breakpad/common/minidump_cpu_arm.h b/third_party/breakpad/src/google_breakpad/common/minidump_cpu_arm.h
new file mode 100644
index 0000000..14d8146
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/common/minidump_cpu_arm.h
@@ -0,0 +1,140 @@
+/* Copyright (c) 2009, 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. */
+
+/* minidump_format.h: A cross-platform reimplementation of minidump-related
+ * portions of DbgHelp.h from the Windows Platform SDK.
+ *
+ * (This is C99 source, please don't corrupt it with C++.)
+ *
+ * This file contains the necessary definitions to read minidump files
+ * produced on ARM.  These files may be read on any platform provided
+ * that the alignments of these structures on the processing system are
+ * identical to the alignments of these structures on the producing system.
+ * For this reason, precise-sized types are used.  The structures defined
+ * by this file have been laid out to minimize alignment problems by
+ * ensuring that all members are aligned on their natural boundaries.
+ * In some cases, tail-padding may be significant when different ABIs specify
+ * different tail-padding behaviors.  To avoid problems when reading or
+ * writing affected structures, MD_*_SIZE macros are provided where needed,
+ * containing the useful size of the structures without padding.
+ *
+ * Structures that are defined by Microsoft to contain a zero-length array
+ * are instead defined here to contain an array with one element, as
+ * zero-length arrays are forbidden by standard C and C++.  In these cases,
+ * *_minsize constants are provided to be used in place of sizeof.  For a
+ * cleaner interface to these sizes when using C++, see minidump_size.h.
+ *
+ * These structures are also sufficient to populate minidump files.
+ *
+ * Because precise data type sizes are crucial for this implementation to
+ * function properly and portably, a set of primitive types with known sizes
+ * are used as the basis of each structure defined by this file.
+ *
+ * Author: Julian Seward
+ */
+
+/*
+ * ARM support
+ */
+
+#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_ARM_H__
+#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_ARM_H__
+
+#define MD_FLOATINGSAVEAREA_ARM_FPR_COUNT 32
+#define MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT 8
+
+/*
+ * Note that these structures *do not* map directly to the CONTEXT
+ * structure defined in WinNT.h in the Windows Mobile SDK. That structure
+ * does not accomodate VFPv3, and I'm unsure if it was ever used in the
+ * wild anyway, as Windows CE only seems to produce "cedumps" which
+ * are not exactly minidumps.
+ */
+typedef struct {
+  u_int64_t	fpscr;      /* FPU status register */
+
+  /* 32 64-bit floating point registers, d0 .. d31. */
+  u_int64_t	regs[MD_FLOATINGSAVEAREA_ARM_FPR_COUNT];
+
+  /* Miscellaneous control words */
+  u_int32_t     extra[MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT];
+} MDFloatingSaveAreaARM;
+
+#define MD_CONTEXT_ARM_GPR_COUNT 16
+
+typedef struct {
+  /* The next field determines the layout of the structure, and which parts
+   * of it are populated
+   */
+  u_int32_t	context_flags;
+
+  /* 16 32-bit integer registers, r0 .. r15
+   * Note the following fixed uses:
+   *   r13 is the stack pointer
+   *   r14 is the link register
+   *   r15 is the program counter
+   */
+  u_int32_t     iregs[MD_CONTEXT_ARM_GPR_COUNT];
+
+  /* CPSR (flags, basically): 32 bits:
+        bit 31 - N (negative)
+        bit 30 - Z (zero)
+        bit 29 - C (carry)
+        bit 28 - V (overflow)
+        bit 27 - Q (saturation flag, sticky)
+     All other fields -- ignore */
+  u_int32_t    cpsr;
+
+  /* The next field is included with MD_CONTEXT_ARM_FLOATING_POINT */
+  MDFloatingSaveAreaARM float_save;
+
+} MDRawContextARM;
+
+/* Indices into iregs for registers with a dedicated or conventional 
+ * purpose.
+ */
+enum MDARMRegisterNumbers {
+  MD_CONTEXT_ARM_REG_FP = 11,
+  MD_CONTEXT_ARM_REG_SP = 13,
+  MD_CONTEXT_ARM_REG_LR = 14,
+  MD_CONTEXT_ARM_REG_PC = 15
+};
+
+/* For (MDRawContextARM).context_flags.  These values indicate the type of
+ * context stored in the structure. */
+#define MD_CONTEXT_ARM_INTEGER           (MD_CONTEXT_ARM | 0x00000002)
+#define MD_CONTEXT_ARM_FLOATING_POINT    (MD_CONTEXT_ARM | 0x00000004)
+
+#define MD_CONTEXT_ARM_FULL              (MD_CONTEXT_ARM_INTEGER | \
+                                          MD_CONTEXT_ARM_FLOATING_POINT)
+
+#define MD_CONTEXT_ARM_ALL               (MD_CONTEXT_ARM_INTEGER | \
+                                          MD_CONTEXT_ARM_FLOATING_POINT)
+
+#endif  /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_ARM_H__ */
diff --git a/third_party/breakpad/src/google_breakpad/common/minidump_exception_solaris.h b/third_party/breakpad/src/google_breakpad/common/minidump_exception_solaris.h
index d48632a..f18ddf4 100644
--- a/third_party/breakpad/src/google_breakpad/common/minidump_exception_solaris.h
+++ b/third_party/breakpad/src/google_breakpad/common/minidump_exception_solaris.h
@@ -34,7 +34,7 @@
  *
  * Author: Mark Mentovai
  * Split into its own file: Neal Sidhwaney */
- 
+
 
 #ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_SOLARIS_H__
 #define GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_SOLARIS_H__
@@ -69,7 +69,7 @@
   MD_EXCEPTION_CODE_SOL_SIGPWR = 19,     /* power-fail restart */
   MD_EXCEPTION_CODE_SOL_SIGWINCH = 20,   /* window size change */
   MD_EXCEPTION_CODE_SOL_SIGURG = 21,     /* urgent socket condition */
-  MD_EXCEPTION_CODE_SOL_SIGPOLL = 22,    /* pollable event occured */
+  MD_EXCEPTION_CODE_SOL_SIGPOLL = 22,    /* pollable event occurred */
   MD_EXCEPTION_CODE_SOL_SIGIO = 22,      /* socket I/O possible (SIGPOLL alias) */
   MD_EXCEPTION_CODE_SOL_SIGSTOP = 23,    /* stop (cannot be caught or ignored) */
   MD_EXCEPTION_CODE_SOL_SIGTSTP = 24,    /* user stop requested from tty */
diff --git a/third_party/breakpad/src/google_breakpad/common/minidump_exception_win32.h b/third_party/breakpad/src/google_breakpad/common/minidump_exception_win32.h
index 7fd4bc4..458a705 100644
--- a/third_party/breakpad/src/google_breakpad/common/minidump_exception_win32.h
+++ b/third_party/breakpad/src/google_breakpad/common/minidump_exception_win32.h
@@ -34,7 +34,7 @@
  *
  * Author: Mark Mentovai
  * Split into its own file: Neal Sidhwaney */
- 
+
 
 #ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_WIN32_H__
 #define GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_WIN32_H__
@@ -94,9 +94,23 @@
       /* EXCEPTION_PRIV_INSTRUCTION */
   MD_EXCEPTION_CODE_WIN_STACK_OVERFLOW           = 0xc00000fd,
       /* EXCEPTION_STACK_OVERFLOW */
-  MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK        = 0xc0000194
+  MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK        = 0xc0000194,
       /* EXCEPTION_POSSIBLE_DEADLOCK */
+  MD_EXCEPTION_CODE_WIN_STACK_BUFFER_OVERRUN     = 0xc0000409,
+      /* STATUS_STACK_BUFFER_OVERRUN */
+  MD_EXCEPTION_CODE_WIN_HEAP_CORRUPTION          = 0xc0000374,
+      /* STATUS_HEAP_CORRUPTION */
+  MD_EXCEPTION_CODE_WIN_UNHANDLED_CPP_EXCEPTION  = 0xe06d7363
+      /* Per http://support.microsoft.com/kb/185294,
+         generated by Visual C++ compiler */
 } MDExceptionCodeWin;
 
+// These constants are defined in the MSDN documentation of
+// the EXCEPTION_RECORD structure.
+typedef enum {
+  MD_ACCESS_VIOLATION_WIN_READ  = 0,
+  MD_ACCESS_VIOLATION_WIN_WRITE = 1,
+  MD_ACCESS_VIOLATION_WIN_EXEC  = 8
+} MDAccessViolationTypeWin;
 
 #endif  /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_WIN32_H__ */
diff --git a/third_party/breakpad/src/google_breakpad/common/minidump_format.h b/third_party/breakpad/src/google_breakpad/common/minidump_format.h
index 4d9e767..77f28ca 100644
--- a/third_party/breakpad/src/google_breakpad/common/minidump_format.h
+++ b/third_party/breakpad/src/google_breakpad/common/minidump_format.h
@@ -112,12 +112,12 @@
   u_int32_t context_flags;
 } MDRawContextBase;
 
-#include "minidump_cpu_sparc.h"
-#include "minidump_cpu_x86.h"
+#include "minidump_cpu_amd64.h"
+#include "minidump_cpu_arm.h"
 #include "minidump_cpu_ppc.h"
 #include "minidump_cpu_ppc64.h"
-#include "minidump_cpu_amd64.h"
-
+#include "minidump_cpu_sparc.h"
+#include "minidump_cpu_x86.h"
 
 /*
  * WinVer.h
@@ -291,7 +291,12 @@
   MD_WITHOUT_OPTIONAL_DATA             = 0x00000400,
   MD_WITH_FULL_MEMORY_INFO             = 0x00000800,
   MD_WITH_THREAD_INFO                  = 0x00001000,
-  MD_WITH_CODE_SEGS                    = 0x00002000
+  MD_WITH_CODE_SEGS                    = 0x00002000,
+  MD_WITHOUT_AUXILLIARY_SEGS           = 0x00004000,
+  MD_WITH_FULL_AUXILLIARY_STATE        = 0x00008000,
+  MD_WITH_PRIVATE_WRITE_COPY_MEMORY    = 0x00010000,
+  MD_IGNORE_INACCESSIBLE_MEMORY        = 0x00020000,
+  MD_WITH_TOKEN_INFORMATION            = 0x00040000
 } MDType;  /* MINIDUMP_TYPE */
 
 
@@ -318,10 +323,13 @@
   MD_FUNCTION_TABLE_STREAM       = 13,
   MD_UNLOADED_MODULE_LIST_STREAM = 14,
   MD_MISC_INFO_STREAM            = 15,  /* MDRawMiscInfo */
+  MD_MEMORY_INFO_LIST_STREAM     = 16,  /* MDRawMemoryInfoList */
+  MD_THREAD_INFO_LIST_STREAM     = 17,
+  MD_HANDLE_OPERATION_LIST_STREAM = 18,
   MD_LAST_RESERVED_STREAM        = 0x0000ffff,
 
   /* Breakpad extension types.  0x4767 = "Gg" */
-  MD_BREAKPAD_INFO_STREAM          = 0x47670001,  /* MDRawBreakpadInfo */
+  MD_BREAKPAD_INFO_STREAM        = 0x47670001,  /* MDRawBreakpadInfo */
   MD_ASSERTION_INFO_STREAM       = 0x47670002   /* MDRawAssertionInfo */
 } MDStreamType;  /* MINIDUMP_STREAM_TYPE */
 
@@ -645,6 +653,69 @@
       /* MINIDUMP_MISC1_PROCESSOR_POWER_INFO */
 } MDMiscInfoFlags1;
 
+/* 
+ * Around DbgHelp version 6.0, the style of new LIST structures changed
+ * from including an array of length 1 at the end of the struct to
+ * represent the variable-length data to including explicit
+ * "size of header", "size of entry" and "number of entries" fields
+ * in the header, presumably to allow backwards-compatibly-extending
+ * the structures in the future. The actual list entries follow the
+ * header data directly in this case.
+ */
+
+typedef struct {
+  u_int32_t size_of_header;    /* sizeof(MDRawMemoryInfoList) */
+  u_int32_t size_of_entry;     /* sizeof(MDRawMemoryInfo) */
+  u_int64_t number_of_entries;
+} MDRawMemoryInfoList;  /* MINIDUMP_MEMORY_INFO_LIST */
+
+typedef struct {
+  u_int64_t base_address;           /* Base address of a region of pages */
+  u_int64_t allocation_base;        /* Base address of a range of pages
+                                     * within this region. */
+  u_int32_t allocation_protection;  /* Memory protection when this region
+                                     * was originally allocated:
+                                     * MDMemoryProtection */
+  u_int32_t __alignment1;
+  u_int64_t region_size;
+  u_int32_t state;                  /* MDMemoryState */
+  u_int32_t protection;             /* MDMemoryProtection */
+  u_int32_t type;                   /* MDMemoryType */
+  u_int32_t __alignment2;
+} MDRawMemoryInfo;  /* MINIDUMP_MEMORY_INFO */
+
+/* For (MDRawMemoryInfo).state */
+typedef enum {
+  MD_MEMORY_STATE_COMMIT   = 0x1000,  /* physical storage has been allocated */
+  MD_MEMORY_STATE_RESERVE  = 0x2000,  /* reserved, but no physical storage */
+  MD_MEMORY_STATE_FREE     = 0x10000  /* available to be allocated */
+} MDMemoryState;
+
+/* For (MDRawMemoryInfo).allocation_protection and .protection */
+typedef enum {
+  MD_MEMORY_PROTECT_NOACCESS          = 0x01,  /* PAGE_NOACCESS */
+  MD_MEMORY_PROTECT_READONLY          = 0x02,  /* PAGE_READONLY */
+  MD_MEMORY_PROTECT_READWRITE         = 0x04,  /* PAGE_READWRITE */
+  MD_MEMORY_PROTECT_WRITECOPY         = 0x08,  /* PAGE_WRITECOPY */
+  MD_MEMORY_PROTECT_EXECUTE           = 0x10,  /* PAGE_EXECUTE */
+  MD_MEMORY_PROTECT_EXECUTE_READ      = 0x20,  /* PAGE_EXECUTE_READ */
+  MD_MEMORY_PROTECT_EXECUTE_READWRITE = 0x40,  /* PAGE_EXECUTE_READWRITE */
+  MD_MEMORY_PROTECT_EXECUTE_WRITECOPY = 0x80,  /* PAGE_EXECUTE_WRITECOPY */
+  /* These options can be combined with the previous flags. */
+  MD_MEMORY_PROTECT_GUARD             = 0x100,  /* PAGE_GUARD */
+  MD_MEMORY_PROTECT_NOCACHE           = 0x200,  /* PAGE_NOCACHE */
+  MD_MEMORY_PROTECT_WRITECOMBINE      = 0x400,  /* PAGE_WRITECOMBINE */
+} MDMemoryProtection;
+
+/* Used to mask the mutually exclusive options from the combinable flags. */
+const u_int32_t MD_MEMORY_PROTECTION_ACCESS_MASK = 0xFF;
+
+/* For (MDRawMemoryInfo).type */
+typedef enum {
+  MD_MEMORY_TYPE_PRIVATE = 0x20000,   /* not shared by other processes */
+  MD_MEMORY_TYPE_MAPPED  = 0x40000,   /* mapped into the view of a section */
+  MD_MEMORY_TYPE_IMAGE   = 0x1000000  /* mapped into the view of an image */
+} MDMemoryType;
 
 /*
  * Breakpad extension types
diff --git a/third_party/breakpad/src/google_breakpad/common/minidump_size.h b/third_party/breakpad/src/google_breakpad/common/minidump_size.h
new file mode 100644
index 0000000..918544b
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/common/minidump_size.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2007, 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. */
+
+// minidump_size.h: Provides a C++ template for programmatic access to
+// the sizes of various types defined in minidump_format.h.
+//
+// Author: Mark Mentovai
+
+#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_SIZE_H__
+#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_SIZE_H__
+
+#include <sys/types.h>
+
+#include "google_breakpad/common/minidump_format.h"
+
+namespace google_breakpad {
+
+template<typename T>
+class minidump_size {
+ public:
+  static size_t size() { return sizeof(T); }
+};
+
+// Explicit specializations for variable-length types.  The size returned
+// for these should be the size for an object without its variable-length
+// section.
+
+template<>
+class minidump_size<MDString> {
+ public:
+  static size_t size() { return MDString_minsize; }
+};
+
+template<>
+class minidump_size<MDRawThreadList> {
+ public:
+  static size_t size() { return MDRawThreadList_minsize; }
+};
+
+template<>
+class minidump_size<MDCVInfoPDB20> {
+ public:
+  static size_t size() { return MDCVInfoPDB20_minsize; }
+};
+
+template<>
+class minidump_size<MDCVInfoPDB70> {
+ public:
+  static size_t size() { return MDCVInfoPDB70_minsize; }
+};
+
+template<>
+class minidump_size<MDImageDebugMisc> {
+ public:
+  static size_t size() { return MDImageDebugMisc_minsize; }
+};
+
+template<>
+class minidump_size<MDRawModuleList> {
+ public:
+  static size_t size() { return MDRawModuleList_minsize; }
+};
+
+template<>
+class minidump_size<MDRawMemoryList> {
+ public:
+  static size_t size() { return MDRawMemoryList_minsize; }
+};
+
+// Explicit specialization for MDRawModule, for which sizeof may include
+// tail-padding on some architectures but not others.
+
+template<>
+class minidump_size<MDRawModule> {
+ public:
+  static size_t size() { return MD_MODULE_SIZE; }
+};
+
+}  // namespace google_breakpad
+
+#endif  // GOOGLE_BREAKPAD_COMMON_MINIDUMP_SIZE_H__
diff --git a/third_party/breakpad/src/google_breakpad/processor/basic_source_line_resolver.h b/third_party/breakpad/src/google_breakpad/processor/basic_source_line_resolver.h
new file mode 100644
index 0000000..f77b3bb
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/basic_source_line_resolver.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2010 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.
+
+// basic_source_line_resolver.h: BasicSourceLineResolver is derived from
+// SourceLineResolverBase, and is a concrete implementation of
+// SourceLineResolverInterface, using address map files produced by a
+// compatible writer, e.g. PDBSourceLineWriter.
+//
+// see "processor/source_line_resolver_base.h"
+// and "source_line_resolver_interface.h" for more documentation.
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_H__
+#define GOOGLE_BREAKPAD_PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_H__
+
+#include <map>
+
+#include "google_breakpad/processor/source_line_resolver_base.h"
+
+namespace google_breakpad {
+
+using std::string;
+using std::map;
+
+class BasicSourceLineResolver : public SourceLineResolverBase {
+ public:
+  BasicSourceLineResolver();
+  virtual ~BasicSourceLineResolver() { }
+
+  using SourceLineResolverBase::LoadModule;
+  using SourceLineResolverBase::LoadModuleUsingMapBuffer;
+  using SourceLineResolverBase::LoadModuleUsingMemoryBuffer;
+  using SourceLineResolverBase::ShouldDeleteMemoryBufferAfterLoadModule;
+  using SourceLineResolverBase::UnloadModule;
+  using SourceLineResolverBase::HasModule;
+  using SourceLineResolverBase::FillSourceLineInfo;
+  using SourceLineResolverBase::FindWindowsFrameInfo;
+  using SourceLineResolverBase::FindCFIFrameInfo;
+
+ private:
+  // friend declarations:
+  friend class BasicModuleFactory;
+  friend class ModuleComparer;
+  friend class ModuleSerializer;
+  template<class> friend class SimpleSerializer;
+
+  // Function derives from SourceLineResolverBase::Function.
+  struct Function;
+  // Module implements SourceLineResolverBase::Module interface.
+  class Module;
+
+  // Disallow unwanted copy ctor and assignment operator
+  BasicSourceLineResolver(const BasicSourceLineResolver&);
+  void operator=(const BasicSourceLineResolver&);
+};
+
+}  // namespace google_breakpad
+
+#endif  // GOOGLE_BREAKPAD_PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_H__
diff --git a/third_party/breakpad/src/google_breakpad/processor/call_stack.h b/third_party/breakpad/src/google_breakpad/processor/call_stack.h
new file mode 100644
index 0000000..21f595e
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/call_stack.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2006, 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.
+
+// call_stack.h: A call stack comprised of stack frames.
+//
+// This class manages a vector of stack frames.  It is used instead of
+// exposing the vector directly to allow the CallStack to own StackFrame
+// pointers without having to publicly export the linked_ptr class.  A
+// CallStack must be composed of pointers instead of objects to allow for
+// CPU-specific StackFrame subclasses.
+//
+// By convention, the stack frame at index 0 is the innermost callee frame,
+// and the frame at the highest index in a call stack is the outermost
+// caller.  CallStack only allows stacks to be built by pushing frames,
+// beginning with the innermost callee frame.
+//
+// Author: Mark Mentovai
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_CALL_STACK_H__
+#define GOOGLE_BREAKPAD_PROCESSOR_CALL_STACK_H__
+
+#include <vector>
+
+namespace google_breakpad {
+
+using std::vector;
+
+struct StackFrame;
+template<typename T> class linked_ptr;
+
+class CallStack {
+ public:
+  CallStack() { Clear(); }
+  ~CallStack();
+
+  // Resets the CallStack to its initial empty state
+  void Clear();
+  
+  const vector<StackFrame*>* frames() const { return &frames_; }
+
+ private:
+  // Stackwalker is responsible for building the frames_ vector.
+  friend class Stackwalker;
+
+  // Storage for pushed frames.
+  vector<StackFrame*> frames_;
+};
+
+}  // namespace google_breakpad
+
+#endif  // GOOGLE_BREAKPAD_PROCSSOR_CALL_STACK_H__
diff --git a/third_party/breakpad/src/google_breakpad/processor/code_module.h b/third_party/breakpad/src/google_breakpad/processor/code_module.h
new file mode 100644
index 0000000..38ee956
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/code_module.h
@@ -0,0 +1,94 @@
+// Copyright (c) 2006, 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.
+
+// code_module.h: Carries information about code modules that are loaded
+// into a process.
+//
+// Author: Mark Mentovai
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULE_H__
+#define GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULE_H__
+
+#include <string>
+#include "google_breakpad/common/breakpad_types.h"
+
+namespace google_breakpad {
+
+using std::string;
+
+class CodeModule {
+ public:
+  virtual ~CodeModule() {}
+
+  // The base address of this code module as it was loaded by the process.
+  // (u_int64_t)-1 on error.
+  virtual u_int64_t base_address() const = 0;
+
+  // The size of the code module.  0 on error.
+  virtual u_int64_t size() const = 0;
+
+  // The path or file name that the code module was loaded from.  Empty on
+  // error.
+  virtual string code_file() const = 0;
+
+  // An identifying string used to discriminate between multiple versions and
+  // builds of the same code module.  This may contain a uuid, timestamp,
+  // version number, or any combination of this or other information, in an
+  // implementation-defined format.  Empty on error.
+  virtual string code_identifier() const = 0;
+
+  // The filename containing debugging information associated with the code
+  // module.  If debugging information is stored in a file separate from the
+  // code module itself (as is the case when .pdb or .dSYM files are used),
+  // this will be different from code_file.  If debugging information is
+  // stored in the code module itself (possibly prior to stripping), this
+  // will be the same as code_file.  Empty on error.
+  virtual string debug_file() const = 0;
+
+  // An identifying string similar to code_identifier, but identifies a
+  // specific version and build of the associated debug file.  This may be
+  // the same as code_identifier when the debug_file and code_file are
+  // identical or when the same identifier is used to identify distinct
+  // debug and code files.
+  virtual string debug_identifier() const = 0;
+
+  // A human-readable representation of the code module's version.  Empty on
+  // error.
+  virtual string version() const = 0;
+
+  // Creates a new copy of this CodeModule object, which the caller takes
+  // ownership of.  The new CodeModule may be of a different concrete class
+  // than the CodeModule being copied, but will behave identically to the
+  // copied CodeModule as far as the CodeModule interface is concerned.
+  virtual const CodeModule* Copy() const = 0;
+};
+
+}  // namespace google_breakpad
+
+#endif  // GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULE_H__
diff --git a/third_party/breakpad/src/google_breakpad/processor/code_modules.h b/third_party/breakpad/src/google_breakpad/processor/code_modules.h
new file mode 100644
index 0000000..29c55d4
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/code_modules.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2006, 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.
+
+// code_modules.h: Contains all of the CodeModule objects that were loaded
+// into a single process.
+//
+// Author: Mark Mentovai
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULES_H__
+#define GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULES_H__
+
+#include "google_breakpad/common/breakpad_types.h"
+
+namespace google_breakpad {
+
+class CodeModule;
+
+class CodeModules {
+ public:
+  virtual ~CodeModules() {}
+
+  // The number of contained CodeModule objects.
+  virtual unsigned int module_count() const = 0;
+
+  // Random access to modules.  Returns the module whose code is present
+  // at the address indicated by |address|.  If no module is present at this
+  // address, returns NULL.  Ownership of the returned CodeModule is retained
+  // by the CodeModules object; pointers returned by this method are valid for
+  // comparison with pointers returned by the other Get methods.
+  virtual const CodeModule* GetModuleForAddress(u_int64_t address) const = 0;
+
+  // Returns the module corresponding to the main executable.  If there is
+  // no main executable, returns NULL.  Ownership of the returned CodeModule
+  // is retained by the CodeModules object; pointers returned by this method
+  // are valid for comparison with pointers returned by the other Get
+  // methods.
+  virtual const CodeModule* GetMainModule() const = 0;
+
+  // Sequential access to modules.  A sequence number of 0 corresponds to the
+  // module residing lowest in memory.  If the sequence number is out of
+  // range, returns NULL.  Ownership of the returned CodeModule is retained
+  // by the CodeModules object; pointers returned by this method are valid for
+  // comparison with pointers returned by the other Get methods.
+  virtual const CodeModule* GetModuleAtSequence(
+      unsigned int sequence) const = 0;
+
+  // Sequential access to modules.  This is similar to GetModuleAtSequence,
+  // except no ordering requirement is enforced.  A CodeModules implementation
+  // may return CodeModule objects from GetModuleAtIndex in any order it
+  // wishes, provided that the order remain the same throughout the life of
+  // the CodeModules object.  Typically, GetModuleAtIndex would be used by
+  // a caller to enumerate all CodeModule objects quickly when the enumeration
+  // does not require any ordering.  If the index argument is out of range,
+  // returns NULL.  Ownership of the returned CodeModule is retained by
+  // the CodeModules object; pointers returned by this method are valid for
+  // comparison with pointers returned by the other Get methods.
+  virtual const CodeModule* GetModuleAtIndex(unsigned int index) const = 0;
+
+  // Creates a new copy of this CodeModules object, which the caller takes
+  // ownership of.  The new object will also contain copies of the existing
+  // object's child CodeModule objects.  The new CodeModules object may be of
+  // a different concrete class than the object being copied, but will behave
+  // identically to the copied object as far as the CodeModules and CodeModule
+  // interfaces are concerned, except that the order that GetModuleAtIndex
+  // returns objects in may differ between a copy and the original CodeModules
+  // object.
+  virtual const CodeModules* Copy() const = 0;
+};
+
+}  // namespace google_breakpad
+
+#endif  // GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULES_H__
diff --git a/third_party/breakpad/src/google_breakpad/processor/exploitability.h b/third_party/breakpad/src/google_breakpad/processor/exploitability.h
new file mode 100644
index 0000000..225206d
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/exploitability.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2010 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.
+
+// exploitability_engine.h: Generic exploitability engine.
+//
+// The Exploitability class is an abstract base class providing common
+// generic methods that apply to exploitability engines for specific platforms.
+// Specific implementations will extend this class by providing run
+// methods to fill in the exploitability_ enumeration of the ProcessState
+// for a crash.
+//
+// Author: Cris Neckar
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_H_
+#define GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_H_
+
+#include "google_breakpad/common/breakpad_types.h"
+#include "google_breakpad/processor/minidump.h"
+#include "google_breakpad/processor/process_state.h"
+
+namespace google_breakpad {
+
+class Exploitability {
+ public:
+  virtual ~Exploitability() {}
+
+  static Exploitability *ExploitabilityForPlatform(Minidump *dump,
+                                                   ProcessState *process_state);
+
+  ExploitabilityRating CheckExploitability();
+  bool AddressIsAscii(u_int64_t);
+
+ protected:
+  Exploitability(Minidump *dump,
+                 ProcessState *process_state);
+
+  Minidump *dump_;
+  ProcessState *process_state_;
+  SystemInfo *system_info_;
+
+ private:
+  virtual ExploitabilityRating CheckPlatformExploitability() = 0;
+};
+
+}  // namespace google_breakpad
+
+#endif  // GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_H_
diff --git a/third_party/breakpad/src/google_breakpad/processor/fast_source_line_resolver.h b/third_party/breakpad/src/google_breakpad/processor/fast_source_line_resolver.h
new file mode 100644
index 0000000..60f6dfc
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/fast_source_line_resolver.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2010 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.
+//
+// fast_source_line_resolver.h: FastSourceLineResolver is derived from
+// SourceLineResolverBase, and is a concrete implementation of
+// SourceLineResolverInterface.
+//
+// FastSourceLineResolver is a sibling class of BasicSourceLineResolver.  The
+// difference is FastSourceLineResolver loads a serialized memory chunk of data
+// which can be used directly a Module without parsing or copying of underlying
+// data.  Therefore loading a symbol in FastSourceLineResolver is much faster
+// and more memory-efficient than BasicSourceLineResolver.
+//
+// See "source_line_resolver_base.h" and
+// "google_breakpad/source_line_resolver_interface.h" for more reference.
+//
+// Author: Siyang Xie (lambxsy@google.com)
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_FAST_SOURCE_LINE_RESOLVER_H__
+#define GOOGLE_BREAKPAD_PROCESSOR_FAST_SOURCE_LINE_RESOLVER_H__
+
+#include <map>
+#include <string>
+
+#include "google_breakpad/processor/source_line_resolver_base.h"
+
+namespace google_breakpad {
+
+using std::map;
+
+class FastSourceLineResolver : public SourceLineResolverBase {
+ public:
+  FastSourceLineResolver();
+  virtual ~FastSourceLineResolver() { }
+
+  using SourceLineResolverBase::FillSourceLineInfo;
+  using SourceLineResolverBase::FindCFIFrameInfo;
+  using SourceLineResolverBase::FindWindowsFrameInfo;
+  using SourceLineResolverBase::HasModule;
+  using SourceLineResolverBase::LoadModule;
+  using SourceLineResolverBase::LoadModuleUsingMapBuffer;
+  using SourceLineResolverBase::LoadModuleUsingMemoryBuffer;
+  using SourceLineResolverBase::UnloadModule;
+
+ private:
+  // Friend declarations.
+  friend class ModuleComparer;
+  friend class ModuleSerializer;
+  friend class FastModuleFactory;
+
+  // Nested types that will derive from corresponding nested types defined in
+  // SourceLineResolverBase.
+  struct Line;
+  struct Function;
+  struct PublicSymbol;
+  class Module;
+
+  // Deserialize raw memory data to construct a WindowsFrameInfo object.
+  static WindowsFrameInfo CopyWFI(const char *raw_memory);
+
+  // FastSourceLineResolver requires the memory buffer stays alive during the
+  // lifetime of a corresponding module, therefore it needs to redefine this
+  // virtual method.
+  virtual bool ShouldDeleteMemoryBufferAfterLoadModule();
+
+  // Disallow unwanted copy ctor and assignment operator
+  FastSourceLineResolver(const FastSourceLineResolver&);
+  void operator=(const FastSourceLineResolver&);
+};
+
+}  // namespace google_breakpad
+
+#endif  // GOOGLE_BREAKPAD_PROCESSOR_FAST_SOURCE_LINE_RESOLVER_H__
diff --git a/third_party/breakpad/src/google_breakpad/processor/memory_region.h b/third_party/breakpad/src/google_breakpad/processor/memory_region.h
new file mode 100644
index 0000000..15e23dd
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/memory_region.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2010 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.
+
+// memory_region.h: Access to memory regions.
+//
+// A MemoryRegion provides virtual access to a range of memory.  It is an
+// abstraction allowing the actual source of memory to be independent of
+// methods which need to access a virtual memory space.
+//
+// Author: Mark Mentovai
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_MEMORY_REGION_H__
+#define GOOGLE_BREAKPAD_PROCESSOR_MEMORY_REGION_H__
+
+
+#include "google_breakpad/common/breakpad_types.h"
+
+
+namespace google_breakpad {
+
+
+class MemoryRegion {
+ public:
+  virtual ~MemoryRegion() {}
+
+  // The base address of this memory region.
+  virtual u_int64_t GetBase() const = 0;
+
+  // The size of this memory region.
+  virtual u_int32_t GetSize() const = 0;
+
+  // Access to data of various sizes within the memory region.  address
+  // is a pointer to read, and it must lie within the memory region as
+  // defined by its base address and size.  The location pointed to by
+  // value is set to the value at address.  Byte-swapping is performed
+  // if necessary so that the value is appropriate for the running
+  // program.  Returns true on success.  Fails and returns false if address
+  // is out of the region's bounds (after considering the width of value),
+  // or for other types of errors.
+  virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t*  value) const =0;
+  virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) const =0;
+  virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) const =0;
+  virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) const =0;
+};
+
+
+}  // namespace google_breakpad
+
+
+#endif  // GOOGLE_BREAKPAD_PROCESSOR_MEMORY_REGION_H__
diff --git a/third_party/breakpad/src/google_breakpad/processor/minidump.h b/third_party/breakpad/src/google_breakpad/processor/minidump.h
new file mode 100644
index 0000000..012a813
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/minidump.h
@@ -0,0 +1,1032 @@
+// Copyright (c) 2010 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.
+
+// minidump.h: A minidump reader.
+//
+// The basic structure of this module tracks the structure of the minidump
+// file itself.  At the top level, a minidump file is represented by a
+// Minidump object.  Like most other classes in this module, Minidump
+// provides a Read method that initializes the object with information from
+// the file.  Most of the classes in this file are wrappers around the
+// "raw" structures found in the minidump file itself, and defined in
+// minidump_format.h.  For example, each thread is represented by a
+// MinidumpThread object, whose parameters are specified in an MDRawThread
+// structure.  A properly byte-swapped MDRawThread can be obtained from a
+// MinidumpThread easily by calling its thread() method.
+//
+// Most of the module lazily reads only the portion of the minidump file
+// necessary to fulfill the user's request.  Calling Minidump::Read
+// only reads the minidump's directory.  The thread list is not read until
+// it is needed, and even once it's read, the memory regions for each
+// thread's stack aren't read until they're needed.  This strategy avoids
+// unnecessary file input, and allocating memory for data in which the user
+// has no interest.  Note that although memory allocations for a typical
+// minidump file are not particularly large, it is possible for legitimate
+// minidumps to be sizable.  A full-memory minidump, for example, contains
+// a snapshot of the entire mapped memory space.  Even a normal minidump,
+// with stack memory only, can be large if, for example, the dump was
+// generated in response to a crash that occurred due to an infinite-
+// recursion bug that caused the stack's limits to be exceeded.  Finally,
+// some users of this library will unfortunately find themselves in the
+// position of having to process potentially-hostile minidumps that might
+// attempt to cause problems by forcing the minidump processor to over-
+// allocate memory.
+//
+// Memory management in this module is based on a strict
+// you-don't-own-anything policy.  The only object owned by the user is
+// the top-level Minidump object, the creation and destruction of which
+// must be the user's own responsibility.  All other objects obtained
+// through interaction with this module are ultimately owned by the
+// Minidump object, and will be freed upon the Minidump object's destruction.
+// Because memory regions can potentially involve large allocations, a
+// FreeMemory method is provided by MinidumpMemoryRegion, allowing the user
+// to release data when it is no longer needed.  Use of this method is
+// optional but recommended.  If freed data is later required, it will
+// be read back in from the minidump file again.
+//
+// There is one exception to this memory management policy:
+// Minidump::ReadString will return a string object to the user, and the user
+// is responsible for its deletion.
+//
+// Author: Mark Mentovai
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_H__
+#define GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_H__
+
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "google_breakpad/common/minidump_format.h"
+#include "google_breakpad/processor/code_module.h"
+#include "google_breakpad/processor/code_modules.h"
+#include "google_breakpad/processor/memory_region.h"
+
+
+namespace google_breakpad {
+
+
+using std::map;
+using std::string;
+using std::vector;
+
+
+class Minidump;
+template<typename AddressType, typename EntryType> class RangeMap;
+
+
+// MinidumpObject is the base of all Minidump* objects except for Minidump
+// itself.
+class MinidumpObject {
+ public:
+  virtual ~MinidumpObject() {}
+
+  bool valid() const { return valid_; }
+
+ protected:
+  explicit MinidumpObject(Minidump* minidump);
+
+  // Refers to the Minidump object that is the ultimate parent of this
+  // Some MinidumpObjects are owned by other MinidumpObjects, but at the
+  // root of the ownership tree is always a Minidump.  The Minidump object
+  // is kept here for access to its seeking and reading facilities, and
+  // for access to data about the minidump file itself, such as whether
+  // it should be byte-swapped.
+  Minidump* minidump_;
+
+  // MinidumpObjects are not valid when created.  When a subclass populates
+  // its own fields, it can set valid_ to true.  Accessors and mutators may
+  // wish to consider or alter the valid_ state as they interact with
+  // objects.
+  bool      valid_;
+};
+
+
+// This class exists primarily to provide a virtual destructor in a base
+// class common to all objects that might be stored in
+// Minidump::mStreamObjects.  Some object types (MinidumpContext) will
+// never be stored in Minidump::mStreamObjects, but are represented as
+// streams and adhere to the same interface, and may be derived from
+// this class.
+class MinidumpStream : public MinidumpObject {
+ public:
+  virtual ~MinidumpStream() {}
+
+ protected:
+  explicit MinidumpStream(Minidump* minidump);
+
+ private:
+  // Populate (and validate) the MinidumpStream.  minidump_ is expected
+  // to be positioned at the beginning of the stream, so that the next
+  // read from the minidump will be at the beginning of the stream.
+  // expected_size should be set to the stream's length as contained in
+  // the MDRawDirectory record or other identifying record.  A class
+  // that implements MinidumpStream can compare expected_size to a
+  // known size as an integrity check.
+  virtual bool Read(u_int32_t expected_size) = 0;
+};
+
+
+// MinidumpContext carries a CPU-specific MDRawContext structure, which
+// contains CPU context such as register states.  Each thread has its
+// own context, and the exception record, if present, also has its own
+// context.  Note that if the exception record is present, the context it
+// refers to is probably what the user wants to use for the exception
+// thread, instead of that thread's own context.  The exception thread's
+// context (as opposed to the exception record's context) will contain
+// context for the exception handler (which performs minidump generation),
+// and not the context that caused the exception (which is probably what the
+// user wants).
+class MinidumpContext : public MinidumpStream {
+ public:
+  virtual ~MinidumpContext();
+
+  // Returns an MD_CONTEXT_* value such as MD_CONTEXT_X86 or MD_CONTEXT_PPC
+  // identifying the CPU type that the context was collected from.  The
+  // returned value will identify the CPU only, and will have any other
+  // MD_CONTEXT_* bits masked out.  Returns 0 on failure.
+  u_int32_t GetContextCPU() const;
+
+  // Returns raw CPU-specific context data for the named CPU type.  If the
+  // context data does not match the CPU type or does not exist, returns
+  // NULL.
+  const MDRawContextAMD64* GetContextAMD64() const;
+  const MDRawContextARM*   GetContextARM() const;
+  const MDRawContextPPC*   GetContextPPC() const;
+  const MDRawContextSPARC* GetContextSPARC() const;
+  const MDRawContextX86*   GetContextX86() const;
+ 
+  // Print a human-readable representation of the object to stdout.
+  void Print();
+
+ private:
+  friend class MinidumpThread;
+  friend class MinidumpException;
+
+  explicit MinidumpContext(Minidump* minidump);
+
+  bool Read(u_int32_t expected_size);
+
+  // Free the CPU-specific context structure.
+  void FreeContext();
+
+  // If the minidump contains a SYSTEM_INFO_STREAM, makes sure that the
+  // system info stream gives an appropriate CPU type matching the context
+  // CPU type in context_cpu_type.  Returns false if the CPU type does not
+  // match.  Returns true if the CPU type matches or if the minidump does
+  // not contain a system info stream.
+  bool CheckAgainstSystemInfo(u_int32_t context_cpu_type);
+
+  // Store this separately because of the weirdo AMD64 context
+  u_int32_t context_flags_;
+
+  // The CPU-specific context structure.
+  union {
+    MDRawContextBase*  base;
+    MDRawContextX86*   x86;
+    MDRawContextPPC*   ppc;
+    MDRawContextAMD64* amd64;
+    // on Solaris SPARC, sparc is defined as a numeric constant,
+    // so variables can NOT be named as sparc
+    MDRawContextSPARC* ctx_sparc;
+    MDRawContextARM*   arm;
+  } context_;
+};
+
+
+// MinidumpMemoryRegion does not wrap any MDRaw structure, and only contains
+// a reference to an MDMemoryDescriptor.  This object is intended to wrap
+// portions of a minidump file that contain memory dumps.  In normal
+// minidumps, each MinidumpThread owns a MinidumpMemoryRegion corresponding
+// to the thread's stack memory.  MinidumpMemoryList also gives access to
+// memory regions in its list as MinidumpMemoryRegions.  This class
+// adheres to MemoryRegion so that it may be used as a data provider to
+// the Stackwalker family of classes.
+class MinidumpMemoryRegion : public MinidumpObject,
+                             public MemoryRegion {
+ public:
+  virtual ~MinidumpMemoryRegion();
+
+  static void set_max_bytes(u_int32_t max_bytes) { max_bytes_ = max_bytes; }
+  static u_int32_t max_bytes() { return max_bytes_; }
+
+  // Returns a pointer to the base of the memory region.  Returns the
+  // cached value if available, otherwise, reads the minidump file and
+  // caches the memory region.
+  const u_int8_t* GetMemory() const;
+
+  // The address of the base of the memory region.
+  u_int64_t GetBase() const;
+
+  // The size, in bytes, of the memory region.
+  u_int32_t GetSize() const;
+
+  // Frees the cached memory region, if cached.
+  void FreeMemory();
+
+  // Obtains the value of memory at the pointer specified by address.
+  bool GetMemoryAtAddress(u_int64_t address, u_int8_t*  value) const;
+  bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) const;
+  bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) const;
+  bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) const;
+
+  // Print a human-readable representation of the object to stdout.
+  void Print();
+
+ private:
+  friend class MinidumpThread;
+  friend class MinidumpMemoryList;
+
+  explicit MinidumpMemoryRegion(Minidump* minidump);
+
+  // Identify the base address and size of the memory region, and the
+  // location it may be found in the minidump file.
+  void SetDescriptor(MDMemoryDescriptor* descriptor);
+
+  // Implementation for GetMemoryAtAddress
+  template<typename T> bool GetMemoryAtAddressInternal(u_int64_t address,
+                                                       T*        value) const;
+
+  // The largest memory region that will be read from a minidump.  The
+  // default is 1MB.
+  static u_int32_t max_bytes_;
+
+  // Base address and size of the memory region, and its position in the
+  // minidump file.
+  MDMemoryDescriptor* descriptor_;
+
+  // Cached memory.
+  mutable vector<u_int8_t>* memory_;
+};
+
+
+// MinidumpThread contains information about a thread of execution,
+// including a snapshot of the thread's stack and CPU context.  For
+// the thread that caused an exception, the context carried by
+// MinidumpException is probably desired instead of the CPU context
+// provided here.
+class MinidumpThread : public MinidumpObject {
+ public:
+  virtual ~MinidumpThread();
+
+  const MDRawThread* thread() const { return valid_ ? &thread_ : NULL; }
+  MinidumpMemoryRegion* GetMemory();
+  MinidumpContext* GetContext();
+
+  // The thread ID is used to determine if a thread is the exception thread,
+  // so a special getter is provided to retrieve this data from the
+  // MDRawThread structure.  Returns false if the thread ID cannot be
+  // determined.
+  bool GetThreadID(u_int32_t *thread_id) const;
+
+  // Print a human-readable representation of the object to stdout.
+  void Print();
+
+ private:
+  // These objects are managed by MinidumpThreadList.
+  friend class MinidumpThreadList;
+
+  explicit MinidumpThread(Minidump* minidump);
+
+  // This works like MinidumpStream::Read, but is driven by
+  // MinidumpThreadList.  No size checking is done, because
+  // MinidumpThreadList handles that directly.
+  bool Read();
+
+  MDRawThread           thread_;
+  MinidumpMemoryRegion* memory_;
+  MinidumpContext*      context_;
+};
+
+
+// MinidumpThreadList contains all of the threads (as MinidumpThreads) in
+// a process.
+class MinidumpThreadList : public MinidumpStream {
+ public:
+  virtual ~MinidumpThreadList();
+
+  static void set_max_threads(u_int32_t max_threads) {
+    max_threads_ = max_threads;
+  }
+  static u_int32_t max_threads() { return max_threads_; }
+
+  unsigned int thread_count() const {
+    return valid_ ? thread_count_ : 0;
+  }
+
+  // Sequential access to threads.
+  MinidumpThread* GetThreadAtIndex(unsigned int index) const;
+
+  // Random access to threads.
+  MinidumpThread* GetThreadByID(u_int32_t thread_id);
+
+  // Print a human-readable representation of the object to stdout.
+  void Print();
+
+ private:
+  friend class Minidump;
+
+  typedef map<u_int32_t, MinidumpThread*> IDToThreadMap;
+  typedef vector<MinidumpThread> MinidumpThreads;
+
+  static const u_int32_t kStreamType = MD_THREAD_LIST_STREAM;
+
+  explicit MinidumpThreadList(Minidump* aMinidump);
+
+  bool Read(u_int32_t aExpectedSize);
+
+  // The largest number of threads that will be read from a minidump.  The
+  // default is 256.
+  static u_int32_t max_threads_;
+
+  // Access to threads using the thread ID as the key.
+  IDToThreadMap    id_to_thread_map_;
+
+  // The list of threads.
+  MinidumpThreads* threads_;
+  u_int32_t        thread_count_;
+};
+
+
+// MinidumpModule wraps MDRawModule, which contains information about loaded
+// code modules.  Access is provided to various data referenced indirectly
+// by MDRawModule, such as the module's name and a specification for where
+// to locate debugging information for the module.
+class MinidumpModule : public MinidumpObject,
+                       public CodeModule {
+ public:
+  virtual ~MinidumpModule();
+
+  static void set_max_cv_bytes(u_int32_t max_cv_bytes) {
+    max_cv_bytes_ = max_cv_bytes;
+  }
+  static u_int32_t max_cv_bytes() { return max_cv_bytes_; }
+
+  static void set_max_misc_bytes(u_int32_t max_misc_bytes) {
+    max_misc_bytes_ = max_misc_bytes;
+  }
+  static u_int32_t max_misc_bytes() { return max_misc_bytes_; }
+
+  const MDRawModule* module() const { return valid_ ? &module_ : NULL; }
+
+  // CodeModule implementation
+  virtual u_int64_t base_address() const {
+    return valid_ ? module_.base_of_image : static_cast<u_int64_t>(-1);
+  }
+  virtual u_int64_t size() const { return valid_ ? module_.size_of_image : 0; }
+  virtual string code_file() const;
+  virtual string code_identifier() const;
+  virtual string debug_file() const;
+  virtual string debug_identifier() const;
+  virtual string version() const;
+  virtual const CodeModule* Copy() const;
+
+  // The CodeView record, which contains information to locate the module's
+  // debugging information (pdb).  This is returned as u_int8_t* because
+  // the data can be of types MDCVInfoPDB20* or MDCVInfoPDB70*, or it may be
+  // of a type unknown to Breakpad, in which case the raw data will still be
+  // returned but no byte-swapping will have been performed.  Check the
+  // record's signature in the first four bytes to differentiate between
+  // the various types.  Current toolchains generate modules which carry
+  // MDCVInfoPDB70 by default.  Returns a pointer to the CodeView record on
+  // success, and NULL on failure.  On success, the optional |size| argument
+  // is set to the size of the CodeView record.
+  const u_int8_t* GetCVRecord(u_int32_t* size);
+
+  // The miscellaneous debug record, which is obsolete.  Current toolchains
+  // do not generate this type of debugging information (dbg), and this
+  // field is not expected to be present.  Returns a pointer to the debugging
+  // record on success, and NULL on failure.  On success, the optional |size|
+  // argument is set to the size of the debugging record.
+  const MDImageDebugMisc* GetMiscRecord(u_int32_t* size);
+
+  // Print a human-readable representation of the object to stdout.
+  void Print();
+
+ private:
+  // These objects are managed by MinidumpModuleList.
+  friend class MinidumpModuleList;
+
+  explicit MinidumpModule(Minidump* minidump);
+
+  // This works like MinidumpStream::Read, but is driven by
+  // MinidumpModuleList.  No size checking is done, because
+  // MinidumpModuleList handles that directly.
+  bool Read();
+
+  // Reads indirectly-referenced data, including the module name, CodeView
+  // record, and miscellaneous debugging record.  This is necessary to allow
+  // MinidumpModuleList to fully construct MinidumpModule objects without
+  // requiring seeks to read a contiguous set of MinidumpModule objects.
+  // All auxiliary data should be available when Read is called, in order to
+  // allow the CodeModule getters to be const methods.
+  bool ReadAuxiliaryData();
+
+  // The largest number of bytes that will be read from a minidump for a
+  // CodeView record or miscellaneous debugging record, respectively.  The
+  // default for each is 1024.
+  static u_int32_t max_cv_bytes_;
+  static u_int32_t max_misc_bytes_;
+
+  // True after a successful Read.  This is different from valid_, which is
+  // not set true until ReadAuxiliaryData also completes successfully.
+  // module_valid_ is only used by ReadAuxiliaryData and the functions it
+  // calls to determine whether the object is ready for auxiliary data to 
+  // be read.
+  bool              module_valid_;
+
+  // True if debug info was read from the module.  Certain modules
+  // may contain debug records in formats we don't support,
+  // so we can just set this to false to ignore them.
+  bool              has_debug_info_;
+
+  MDRawModule       module_;
+
+  // Cached module name.
+  const string*     name_;
+
+  // Cached CodeView record - this is MDCVInfoPDB20 or (likely)
+  // MDCVInfoPDB70, or possibly something else entirely.  Stored as a u_int8_t
+  // because the structure contains a variable-sized string and its exact
+  // size cannot be known until it is processed.
+  vector<u_int8_t>* cv_record_;
+
+  // If cv_record_ is present, cv_record_signature_ contains a copy of the
+  // CodeView record's first four bytes, for ease of determinining the
+  // type of structure that cv_record_ contains.
+  u_int32_t cv_record_signature_;
+
+  // Cached MDImageDebugMisc (usually not present), stored as u_int8_t
+  // because the structure contains a variable-sized string and its exact
+  // size cannot be known until it is processed.
+  vector<u_int8_t>* misc_record_;
+};
+
+
+// MinidumpModuleList contains all of the loaded code modules for a process
+// in the form of MinidumpModules.  It maintains a map of these modules
+// so that it may easily provide a code module corresponding to a specific
+// address.
+class MinidumpModuleList : public MinidumpStream,
+                           public CodeModules {
+ public:
+  virtual ~MinidumpModuleList();
+
+  static void set_max_modules(u_int32_t max_modules) {
+    max_modules_ = max_modules;
+  }
+  static u_int32_t max_modules() { return max_modules_; }
+
+  // CodeModules implementation.
+  virtual unsigned int module_count() const {
+    return valid_ ? module_count_ : 0;
+  }
+  virtual const MinidumpModule* GetModuleForAddress(u_int64_t address) const;
+  virtual const MinidumpModule* GetMainModule() const;
+  virtual const MinidumpModule* GetModuleAtSequence(
+      unsigned int sequence) const;
+  virtual const MinidumpModule* GetModuleAtIndex(unsigned int index) const;
+  virtual const CodeModules* Copy() const;
+
+  // Print a human-readable representation of the object to stdout.
+  void Print();
+
+ private:
+  friend class Minidump;
+
+  typedef vector<MinidumpModule> MinidumpModules;
+
+  static const u_int32_t kStreamType = MD_MODULE_LIST_STREAM;
+
+  explicit MinidumpModuleList(Minidump* minidump);
+
+  bool Read(u_int32_t expected_size);
+
+  // The largest number of modules that will be read from a minidump.  The
+  // default is 1024.
+  static u_int32_t max_modules_;
+
+  // Access to modules using addresses as the key.
+  RangeMap<u_int64_t, unsigned int> *range_map_;
+
+  MinidumpModules *modules_;
+  u_int32_t module_count_;
+};
+
+
+// MinidumpMemoryList corresponds to a minidump's MEMORY_LIST_STREAM stream,
+// which references the snapshots of all of the memory regions contained
+// within the minidump.  For a normal minidump, this includes stack memory
+// (also referenced by each MinidumpThread, in fact, the MDMemoryDescriptors
+// here and in MDRawThread both point to exactly the same data in a
+// minidump file, conserving space), as well as a 256-byte snapshot of memory
+// surrounding the instruction pointer in the case of an exception.  Other
+// types of minidumps may contain significantly more memory regions.  Full-
+// memory minidumps contain all of a process' mapped memory.
+class MinidumpMemoryList : public MinidumpStream {
+ public:
+  virtual ~MinidumpMemoryList();
+
+  static void set_max_regions(u_int32_t max_regions) {
+    max_regions_ = max_regions;
+  }
+  static u_int32_t max_regions() { return max_regions_; }
+
+  unsigned int region_count() const { return valid_ ? region_count_ : 0; }
+
+  // Sequential access to memory regions.
+  MinidumpMemoryRegion* GetMemoryRegionAtIndex(unsigned int index);
+
+  // Random access to memory regions.  Returns the region encompassing
+  // the address identified by address.
+  MinidumpMemoryRegion* GetMemoryRegionForAddress(u_int64_t address);
+
+  // Print a human-readable representation of the object to stdout.
+  void Print();
+
+ private:
+  friend class Minidump;
+
+  typedef vector<MDMemoryDescriptor>   MemoryDescriptors;
+  typedef vector<MinidumpMemoryRegion> MemoryRegions;
+
+  static const u_int32_t kStreamType = MD_MEMORY_LIST_STREAM;
+
+  explicit MinidumpMemoryList(Minidump* minidump);
+
+  bool Read(u_int32_t expected_size);
+
+  // The largest number of memory regions that will be read from a minidump.
+  // The default is 256.
+  static u_int32_t max_regions_;
+
+  // Access to memory regions using addresses as the key.
+  RangeMap<u_int64_t, unsigned int> *range_map_;
+
+  // The list of descriptors.  This is maintained separately from the list
+  // of regions, because MemoryRegion doesn't own its MemoryDescriptor, it
+  // maintains a pointer to it.  descriptors_ provides the storage for this
+  // purpose.
+  MemoryDescriptors *descriptors_;
+
+  // The list of regions.
+  MemoryRegions *regions_;
+  u_int32_t region_count_;
+};
+
+
+// MinidumpException wraps MDRawExceptionStream, which contains information
+// about the exception that caused the minidump to be generated, if the
+// minidump was generated in an exception handler called as a result of
+// an exception.  It also provides access to a MinidumpContext object,
+// which contains the CPU context for the exception thread at the time
+// the exception occurred.
+class MinidumpException : public MinidumpStream {
+ public:
+  virtual ~MinidumpException();
+
+  const MDRawExceptionStream* exception() const {
+    return valid_ ? &exception_ : NULL;
+  }
+
+  // The thread ID is used to determine if a thread is the exception thread,
+  // so a special getter is provided to retrieve this data from the
+  // MDRawExceptionStream structure.  Returns false if the thread ID cannot
+  // be determined.
+  bool GetThreadID(u_int32_t *thread_id) const;
+
+  MinidumpContext* GetContext();
+
+  // Print a human-readable representation of the object to stdout.
+  void Print();
+
+ private:
+  friend class Minidump;
+
+  static const u_int32_t kStreamType = MD_EXCEPTION_STREAM;
+
+  explicit MinidumpException(Minidump* minidump);
+
+  bool Read(u_int32_t expected_size);
+
+  MDRawExceptionStream exception_;
+  MinidumpContext*     context_;
+};
+
+// MinidumpAssertion wraps MDRawAssertionInfo, which contains information
+// about an assertion that caused the minidump to be generated.
+class MinidumpAssertion : public MinidumpStream {
+ public:
+  virtual ~MinidumpAssertion();
+
+  const MDRawAssertionInfo* assertion() const {
+    return valid_ ? &assertion_ : NULL;
+  }
+
+  string expression() const {
+    return valid_ ? expression_ : "";
+  }
+
+  string function() const {
+    return valid_ ? function_ : "";
+  }
+
+  string file() const {
+    return valid_ ? file_ : "";
+  }
+
+  // Print a human-readable representation of the object to stdout.
+  void Print();
+
+ private:
+  friend class Minidump;
+
+  static const u_int32_t kStreamType = MD_ASSERTION_INFO_STREAM;
+
+  explicit MinidumpAssertion(Minidump* minidump);
+
+  bool Read(u_int32_t expected_size);
+
+  MDRawAssertionInfo assertion_;
+  string expression_;
+  string function_;
+  string file_;
+};
+
+
+// MinidumpSystemInfo wraps MDRawSystemInfo and provides information about
+// the system on which the minidump was generated.  See also MinidumpMiscInfo.
+class MinidumpSystemInfo : public MinidumpStream {
+ public:
+  virtual ~MinidumpSystemInfo();
+
+  const MDRawSystemInfo* system_info() const {
+    return valid_ ? &system_info_ : NULL;
+  }
+
+  // GetOS and GetCPU return textual representations of the operating system
+  // and CPU that produced the minidump.  Unlike most other Minidump* methods,
+  // they return string objects, not weak pointers.  Defined values for
+  // GetOS() are "mac", "windows", and "linux".  Defined values for GetCPU
+  // are "x86" and "ppc".  These methods return an empty string when their
+  // values are unknown.
+  string GetOS();
+  string GetCPU();
+
+  // I don't know what CSD stands for, but this field is documented as
+  // returning a textual representation of the OS service pack.  On other
+  // platforms, this provides additional information about an OS version
+  // level beyond major.minor.micro.  Returns NULL if unknown.
+  const string* GetCSDVersion();
+
+  // If a CPU vendor string can be determined, returns a pointer to it,
+  // otherwise, returns NULL.  CPU vendor strings can be determined from
+  // x86 CPUs with CPUID 0.
+  const string* GetCPUVendor();
+
+  // Print a human-readable representation of the object to stdout.
+  void Print();
+
+ private:
+  friend class Minidump;
+
+  static const u_int32_t kStreamType = MD_SYSTEM_INFO_STREAM;
+
+  explicit MinidumpSystemInfo(Minidump* minidump);
+
+  bool Read(u_int32_t expected_size);
+
+  MDRawSystemInfo system_info_;
+
+  // Textual representation of the OS service pack, for minidumps produced
+  // by MiniDumpWriteDump on Windows.
+  const string* csd_version_;
+
+  // A string identifying the CPU vendor, if known.
+  const string* cpu_vendor_;
+};
+
+
+// MinidumpMiscInfo wraps MDRawMiscInfo and provides information about
+// the process that generated the minidump, and optionally additional system
+// information.  See also MinidumpSystemInfo.
+class MinidumpMiscInfo : public MinidumpStream {
+ public:
+  const MDRawMiscInfo* misc_info() const {
+    return valid_ ? &misc_info_ : NULL;
+  }
+
+  // Print a human-readable representation of the object to stdout.
+  void Print();
+
+ private:
+  friend class Minidump;
+
+  static const u_int32_t kStreamType = MD_MISC_INFO_STREAM;
+
+  explicit MinidumpMiscInfo(Minidump* minidump_);
+
+  bool Read(u_int32_t expected_size_);
+
+  MDRawMiscInfo misc_info_;
+};
+
+
+// MinidumpBreakpadInfo wraps MDRawBreakpadInfo, which is an optional stream in
+// a minidump that provides additional information about the process state
+// at the time the minidump was generated.
+class MinidumpBreakpadInfo : public MinidumpStream {
+ public:
+  const MDRawBreakpadInfo* breakpad_info() const {
+    return valid_ ? &breakpad_info_ : NULL;
+  }
+
+  // These thread IDs are used to determine if threads deserve special
+  // treatment, so special getters are provided to retrieve this data from
+  // the MDRawBreakpadInfo structure.  The getters return false if the thread
+  // IDs cannot be determined.
+  bool GetDumpThreadID(u_int32_t *thread_id) const;
+  bool GetRequestingThreadID(u_int32_t *thread_id) const;
+
+  // Print a human-readable representation of the object to stdout.
+  void Print();
+
+ private:
+  friend class Minidump;
+
+  static const u_int32_t kStreamType = MD_BREAKPAD_INFO_STREAM;
+
+  explicit MinidumpBreakpadInfo(Minidump* minidump_);
+
+  bool Read(u_int32_t expected_size_);
+
+  MDRawBreakpadInfo breakpad_info_;
+};
+
+// MinidumpMemoryInfo wraps MDRawMemoryInfo, which provides information
+// about mapped memory regions in a process, including their ranges
+// and protection.
+class MinidumpMemoryInfo : public MinidumpObject {
+ public:
+  const MDRawMemoryInfo* info() const { return valid_ ? &memory_info_ : NULL; }
+
+  // The address of the base of the memory region.
+  u_int64_t GetBase() const { return valid_ ? memory_info_.base_address : 0; }
+
+  // The size, in bytes, of the memory region.
+  u_int32_t GetSize() const { return valid_ ? memory_info_.region_size : 0; }
+
+  // Return true if the memory protection allows execution.
+  bool IsExecutable() const;
+
+  // Return true if the memory protection allows writing.
+  bool IsWritable() const;
+
+  // Print a human-readable representation of the object to stdout.
+  void Print();
+
+ private:
+  // These objects are managed by MinidumpMemoryInfoList.
+  friend class MinidumpMemoryInfoList;
+
+  explicit MinidumpMemoryInfo(Minidump* minidump);
+
+  // This works like MinidumpStream::Read, but is driven by
+  // MinidumpMemoryInfoList.  No size checking is done, because
+  // MinidumpMemoryInfoList handles that directly.
+  bool Read();
+
+  MDRawMemoryInfo memory_info_;
+};
+
+// MinidumpMemoryInfoList contains a list of information about
+// mapped memory regions for a process in the form of MDRawMemoryInfo.
+// It maintains a map of these structures so that it may easily provide
+// info corresponding to a specific address.
+class MinidumpMemoryInfoList : public MinidumpStream {
+ public:
+  virtual ~MinidumpMemoryInfoList();
+
+  unsigned int info_count() const { return valid_ ? info_count_ : 0; }
+
+  const MinidumpMemoryInfo* GetMemoryInfoForAddress(u_int64_t address) const;
+  const MinidumpMemoryInfo* GetMemoryInfoAtIndex(unsigned int index) const;
+
+  // Print a human-readable representation of the object to stdout.
+  void Print();
+
+ private:
+  friend class Minidump;
+
+  typedef vector<MinidumpMemoryInfo> MinidumpMemoryInfos;
+
+  static const u_int32_t kStreamType = MD_MEMORY_INFO_LIST_STREAM;
+
+  explicit MinidumpMemoryInfoList(Minidump* minidump);
+
+  bool Read(u_int32_t expected_size);
+
+  // Access to memory info using addresses as the key.
+  RangeMap<u_int64_t, unsigned int> *range_map_;
+
+  MinidumpMemoryInfos* infos_;
+  u_int32_t info_count_;
+};
+
+
+// Minidump is the user's interface to a minidump file.  It wraps MDRawHeader
+// and provides access to the minidump's top-level stream directory.
+class Minidump {
+ public:
+  // path is the pathname of a file containing the minidump.
+  explicit Minidump(const string& path);
+  // input is an istream wrapping minidump data. Minidump holds a
+  // weak pointer to input, and the caller must ensure that the stream
+  // is valid as long as the Minidump object is.
+  explicit Minidump(std::istream& input);
+
+  virtual ~Minidump();
+
+  // path may be empty if the minidump was not opened from a file
+  virtual string path() const {
+    return path_;
+  }
+  static void set_max_streams(u_int32_t max_streams) {
+    max_streams_ = max_streams;
+  }
+  static u_int32_t max_streams() { return max_streams_; }
+
+  static void set_max_string_length(u_int32_t max_string_length) {
+    max_string_length_ = max_string_length;
+  }
+  static u_int32_t max_string_length() { return max_string_length_; }
+
+  virtual const MDRawHeader* header() const { return valid_ ? &header_ : NULL; }
+
+  // Reads the minidump file's header and top-level stream directory.
+  // The minidump is expected to be positioned at the beginning of the
+  // header.  Read() sets up the stream list and map, and validates the
+  // Minidump object.
+  virtual bool Read();
+
+  // The next set of methods are stubs that call GetStream.  They exist to
+  // force code generation of the templatized API within the module, and
+  // to avoid exposing an ugly API (GetStream needs to accept a garbage
+  // parameter).
+  virtual MinidumpThreadList* GetThreadList();
+  MinidumpModuleList* GetModuleList();
+  MinidumpMemoryList* GetMemoryList();
+  MinidumpException* GetException();
+  MinidumpAssertion* GetAssertion();
+  MinidumpSystemInfo* GetSystemInfo();
+  MinidumpMiscInfo* GetMiscInfo();
+  MinidumpBreakpadInfo* GetBreakpadInfo();
+  MinidumpMemoryInfoList* GetMemoryInfoList();
+
+  // The next set of methods are provided for users who wish to access
+  // data in minidump files directly, while leveraging the rest of
+  // this class and related classes to handle the basic minidump
+  // structure and known stream types.
+
+  unsigned int GetDirectoryEntryCount() const {
+    return valid_ ? header_.stream_count : 0;
+  }
+  const MDRawDirectory* GetDirectoryEntryAtIndex(unsigned int index) const;
+
+  // The next 2 methods are lower-level I/O routines.  They use fd_.
+
+  // Reads count bytes from the minidump at the current position into
+  // the storage area pointed to by bytes.  bytes must be of sufficient
+  // size.  After the read, the file position is advanced by count.
+  bool ReadBytes(void* bytes, size_t count);
+
+  // Sets the position of the minidump file to offset.
+  bool SeekSet(off_t offset);
+
+  // Returns the current position of the minidump file.
+  off_t Tell();
+
+  // The next 2 methods are medium-level I/O routines.
+
+  // ReadString returns a string which is owned by the caller!  offset
+  // specifies the offset that a length-encoded string is stored at in the
+  // minidump file.
+  string* ReadString(off_t offset);
+
+  // SeekToStreamType positions the file at the beginning of a stream
+  // identified by stream_type, and informs the caller of the stream's
+  // length by setting *stream_length.  Because stream_map maps each stream
+  // type to only one stream in the file, this might mislead the user into
+  // thinking that the stream that this seeks to is the only stream with
+  // type stream_type.  That can't happen for streams that these classes
+  // deal with directly, because they're only supposed to be present in the
+  // file singly, and that's verified when stream_map_ is built.  Users who
+  // are looking for other stream types should be aware of this
+  // possibility, and consider using GetDirectoryEntryAtIndex (possibly
+  // with GetDirectoryEntryCount) if expecting multiple streams of the same
+  // type in a single minidump file.
+  bool SeekToStreamType(u_int32_t stream_type, u_int32_t* stream_length);
+
+  bool swap() const { return valid_ ? swap_ : false; }
+
+  // Print a human-readable representation of the object to stdout.
+  void Print();
+
+ private:
+  // MinidumpStreamInfo is used in the MinidumpStreamMap.  It lets
+  // the Minidump object locate interesting streams quickly, and
+  // provides a convenient place to stash MinidumpStream objects.
+  struct MinidumpStreamInfo {
+    MinidumpStreamInfo() : stream_index(0), stream(NULL) {}
+    ~MinidumpStreamInfo() { delete stream; }
+
+    // Index into the MinidumpDirectoryEntries vector
+    unsigned int    stream_index;
+
+    // Pointer to the stream if cached, or NULL if not yet populated
+    MinidumpStream* stream;
+  };
+
+  typedef vector<MDRawDirectory> MinidumpDirectoryEntries;
+  typedef map<u_int32_t, MinidumpStreamInfo> MinidumpStreamMap;
+
+  template<typename T> T* GetStream(T** stream);
+
+  // Opens the minidump file, or if already open, seeks to the beginning.
+  bool Open();
+
+  // The largest number of top-level streams that will be read from a minidump.
+  // Note that streams are only read (and only consume memory) as needed,
+  // when directed by the caller.  The default is 128.
+  static u_int32_t max_streams_;
+
+  // The maximum length of a UTF-16 string that will be read from a minidump
+  // in 16-bit words.  The default is 1024.  UTF-16 strings are converted
+  // to UTF-8 when stored in memory, and each UTF-16 word will be represented
+  // by as many as 3 bytes in UTF-8.
+  static unsigned int max_string_length_;
+
+  MDRawHeader               header_;
+
+  // The list of streams.
+  MinidumpDirectoryEntries* directory_;
+
+  // Access to streams using the stream type as the key.
+  MinidumpStreamMap*        stream_map_;
+
+  // The pathname of the minidump file to process, set in the constructor.
+  // This may be empty if the minidump was opened directly from a stream.
+  const string              path_;
+
+  // The stream for all file I/O.  Used by ReadBytes and SeekSet.
+  // Set based on the path in Open, or directly in the constructor.
+  std::istream*             stream_;
+
+  // swap_ is true if the minidump file should be byte-swapped.  If the
+  // minidump was produced by a CPU that is other-endian than the CPU
+  // processing the minidump, this will be true.  If the two CPUs are
+  // same-endian, this will be false.
+  bool                      swap_;
+
+  // Validity of the Minidump structure, false immediately after
+  // construction or after a failed Read(); true following a successful
+  // Read().
+  bool                      valid_;
+};
+
+
+}  // namespace google_breakpad
+
+
+#endif  // GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_H__
diff --git a/third_party/breakpad/src/google_breakpad/processor/minidump_processor.h b/third_party/breakpad/src/google_breakpad/processor/minidump_processor.h
new file mode 100644
index 0000000..384c60c
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/minidump_processor.h
@@ -0,0 +1,169 @@
+// Copyright (c) 2006, 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 GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_PROCESSOR_H__
+#define GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_PROCESSOR_H__
+
+#include <assert.h>
+#include <string>
+#include "google_breakpad/common/breakpad_types.h"
+
+namespace google_breakpad {
+
+using std::string;
+
+class Minidump;
+class ProcessState;
+class SourceLineResolverInterface;
+class SymbolSupplier;
+class SystemInfo;
+// Return type for Process()
+enum ProcessResult {
+  PROCESS_OK,                                 // The minidump was
+                                              // processed
+                                              // successfully.
+
+  PROCESS_ERROR_MINIDUMP_NOT_FOUND,           // The minidump file
+                                              // was not found.
+
+  PROCESS_ERROR_NO_MINIDUMP_HEADER,           // The minidump file
+                                              // had no header
+
+  PROCESS_ERROR_NO_THREAD_LIST,               // The minidump file
+                                              // had no thread list.
+
+  PROCESS_ERROR_GETTING_THREAD,               // There was an error
+                                              // getting one
+                                              // thread's data from
+                                              // the minidump.
+
+  PROCESS_ERROR_GETTING_THREAD_ID,            // There was an error
+                                              // getting a thread id
+                                              // from the thread's
+                                              // data.
+
+  PROCESS_ERROR_DUPLICATE_REQUESTING_THREADS, // There was more than
+                                              // one requesting
+                                              // thread.
+
+  PROCESS_ERROR_NO_MEMORY_FOR_THREAD,         // A thread had no
+                                              // memory region.
+
+  PROCESS_ERROR_NO_STACKWALKER_FOR_THREAD,    // We couldn't
+                                              // determine the
+                                              // StackWalker to walk
+                                              // the minidump's
+                                              // threads.
+
+  PROCESS_SYMBOL_SUPPLIER_INTERRUPTED         // The minidump
+                                              // processing was
+                                              // interrupted by the
+                                              // SymbolSupplier(not
+                                              // fatal)
+};
+
+class MinidumpProcessor {
+ public:
+  // Initializes this MinidumpProcessor.  supplier should be an
+  // implementation of the SymbolSupplier abstract base class.
+  MinidumpProcessor(SymbolSupplier *supplier,
+                    SourceLineResolverInterface *resolver);
+
+  // Initializes the MinidumpProcessor with the option of
+  // enabling the exploitability framework to analyze dumps
+  // for probable security relevance.
+  MinidumpProcessor(SymbolSupplier *supplier,
+                    SourceLineResolverInterface *resolver,
+                    bool enable_exploitability);
+
+  ~MinidumpProcessor();
+
+  // Processes the minidump file and fills process_state with the result.
+  ProcessResult Process(const string &minidump_file,
+                        ProcessState *process_state);
+
+  // Processes the minidump structure and fills process_state with the
+  // result.
+  ProcessResult Process(Minidump *minidump,
+                        ProcessState *process_state);
+  // Populates the cpu_* fields of the |info| parameter with textual
+  // representations of the CPU type that the minidump in |dump| was
+  // produced on.  Returns false if this information is not available in
+  // the minidump.
+  static bool GetCPUInfo(Minidump *dump, SystemInfo *info);
+
+  // Populates the os_* fields of the |info| parameter with textual
+  // representations of the operating system that the minidump in |dump|
+  // was produced on.  Returns false if this information is not available in
+  // the minidump.
+  static bool GetOSInfo(Minidump *dump, SystemInfo *info);
+
+  // Returns a textual representation of the reason that a crash occurred,
+  // if the minidump in dump was produced as a result of a crash.  Returns
+  // an empty string if this information cannot be determined.  If address
+  // is non-NULL, it will be set to contain the address that caused the
+  // exception, if this information is available.  This will be a code
+  // address when the crash was caused by problems such as illegal
+  // instructions or divisions by zero, or a data address when the crash
+  // was caused by a memory access violation.
+  static string GetCrashReason(Minidump *dump, u_int64_t *address);
+
+  // This function returns true if the passed-in error code is
+  // something unrecoverable(i.e. retry should not happen).  For
+  // instance, if the minidump is corrupt, then it makes no sense to
+  // retry as we won't be able to glean additional information.
+  // However, as an example of the other case, the symbol supplier can
+  // return an error code indicating it was 'interrupted', which can
+  // happen of the symbols are fetched from a remote store, and a
+  // retry might be successful later on.
+  // You should not call this method with PROCESS_OK! Test for
+  // that separately before calling this.
+  static bool IsErrorUnrecoverable(ProcessResult p) {
+    assert(p !=  PROCESS_OK);
+    return (p != PROCESS_SYMBOL_SUPPLIER_INTERRUPTED);
+  }
+
+  // Returns a textual representation of an assertion included
+  // in the minidump.  Returns an empty string if this information
+  // does not exist or cannot be determined.
+  static string GetAssertion(Minidump *dump);
+
+ private:
+  SymbolSupplier *supplier_;
+  SourceLineResolverInterface *resolver_;
+
+  // This flag enables the exploitability scanner which attempts to
+  // guess how likely it is that the crash represents an exploitable
+  // memory corruption issue.
+  bool enable_exploitability_;
+};
+
+}  // namespace google_breakpad
+
+#endif  // GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_PROCESSOR_H__
diff --git a/third_party/breakpad/src/google_breakpad/processor/network_source_line_resolver.h b/third_party/breakpad/src/google_breakpad/processor/network_source_line_resolver.h
new file mode 100644
index 0000000..138b2f5
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/network_source_line_resolver.h
@@ -0,0 +1,184 @@
+// Copyright (c) 2010, 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.
+
+// NetworkSourceLineResolver implements SourceLineResolverInterface and
+// SymbolSupplier using a UDP-based network protocol to communicate to a
+// server process which handles the lower-level details of loading symbols
+// and resolving source info. When used, it must be used simultaneously
+// as the SourceLineResolver and SymbolSupplier.
+//
+// See network_source_line_server.h for a description of the protocol used.
+// An implementation of the server side of the protocol is provided there
+// as NetworkSourceLineServer.
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_NETWORK_SOURCE_LINE_RESOLVER_H__
+#define GOOGLE_BREAKPAD_PROCESSOR_NETWORK_SOURCE_LINE_RESOLVER_H__
+
+#include <sys/socket.h>
+
+#include <map>
+#include <set>
+
+#include "google_breakpad/common/breakpad_types.h"
+#include "google_breakpad/processor/source_line_resolver_interface.h"
+#include "google_breakpad/processor/stack_frame.h"
+#include "google_breakpad/processor/symbol_supplier.h"
+#include "processor/binarystream.h"
+#include "processor/linked_ptr.h"
+#include "processor/network_interface.h"
+
+namespace google_breakpad {
+
+using std::string;
+
+class NetworkSourceLineResolver : public SourceLineResolverInterface,
+                                  public SymbolSupplier {
+ public:
+  // The server and port to connect to, and the
+  // maximum time (in milliseconds) to wait for network replies.
+  NetworkSourceLineResolver(const string &server,
+                            unsigned short port,
+                            int wait_milliseconds);
+  // The network interface to connect to, and maximum wait time.
+  NetworkSourceLineResolver(NetworkInterface *net,
+                            int wait_milliseconds);
+  virtual ~NetworkSourceLineResolver();
+  
+  // SourceLineResolverInterface methods, see source_line_resolver_interface.h
+  // for more details.
+
+
+  // These methods are actually NOOPs in this implementation.
+  // The server loads modules as a result of the GetSymbolFile call.
+  // Since we're both the symbol supplier and source line resolver,
+  // this is an optimization.
+  virtual bool LoadModule(const CodeModule *module, const string &map_file);
+  virtual bool LoadModuleUsingMapBuffer(const CodeModule *module,
+                                        const string &map_buffer);
+  virtual bool LoadModuleUsingMemoryBuffer(const CodeModule *module,
+                                           char *memory_buffer);
+
+  // It doesn't matter whether returns true or false, since no memory buffer
+  // will be allocated in GetCStringSymbolData().
+  virtual bool ShouldDeleteMemoryBufferAfterLoadModule() { return true; }
+
+  void UnloadModule(const CodeModule *module);
+
+  virtual bool HasModule(const CodeModule *module);
+
+  virtual void FillSourceLineInfo(StackFrame *frame);
+  virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame);
+  virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame);
+
+  // SymbolSupplier methods, see symbol_supplier.h for more details.
+  // Note that the server will actually load the symbol data
+  // in response to this request, as an optimization.
+  virtual SymbolResult GetSymbolFile(const CodeModule *module,
+                                     const SystemInfo *system_info,
+                                     string *symbol_file);
+  //FIXME: we'll never return symbol_data here, it doesn't make sense.
+  // the SymbolSupplier interface should just state that the supplier
+  // *may* fill in symbol_data if it desires, and clients should
+  // handle it gracefully either way.
+  virtual SymbolResult GetSymbolFile(const CodeModule *module,
+                                     const SystemInfo *system_info,
+                                     string *symbol_file,
+                                     string *symbol_data);
+  // Similar as the above GetSymbolFile() method, see the comment above.
+  virtual SymbolResult GetCStringSymbolData(const CodeModule *module,
+                                            const SystemInfo *system_info,
+                                            string *symbol_file,
+                                            char **symbol_data);
+
+  // Delete the data buffer allocated in GetCStringSymbolData().
+  // Since the above GetCStringSymbolData() won't allocate any memory at all,
+  // this method is no-op.
+  virtual void FreeSymbolData(const CodeModule *module) { }
+
+ private:
+  int wait_milliseconds_;
+  // if false, some part of our network setup failed.
+  bool initialized_;
+  // sequence number of the last request we made
+  u_int16_t sequence_;
+  NetworkInterface *net_;
+  // cached list of loaded modules, so we can quickly answer
+  // HasModule requests for modules we've already queried the
+  // server about, avoiding another network round-trip.
+  std::set<string> module_cache_;
+  // cached list of modules for which we don't have symbols,
+  // so we can short-circuit that as well.
+  std::set<string> no_symbols_cache_;
+
+  // Cached list of source line info, to avoid repeated GET requests
+  // for the same frame. In Multithreaded apps that use the same
+  // framework across threads, it's pretty common to hit the same
+  // exact set of frames in multiple threads.
+  // Data is stored in the cache keyed by instruction pointer
+  typedef std::map<u_int64_t, StackFrame> SourceCache;
+  SourceCache source_line_info_cache_;
+
+  // Cached list of WindowsFrameInfo/CFIFrameInfo, for the same reason.
+  // Stored as serialized strings to avoid shuffling around pointers.
+  typedef std::map<u_int64_t, string> FrameInfoCache;
+
+  typedef enum {
+    kWindowsFrameInfo = 0,
+    kCFIFrameInfo = 1,
+  } FrameInfoType;
+  FrameInfoCache frame_info_cache_[2];
+  
+  // Send a message to the server, wait a certain amount of time for a reply.
+  // Returns true if a response is received, with the response data
+  // in |response|.
+  // Returns false if the response times out.
+  bool SendMessageGetResponse(const binarystream &message,
+                              binarystream &response);
+
+  // See if this stack frame is cached, and fill in the source line info
+  // if so.
+  bool FindCachedSourceLineInfo(StackFrame *frame) const;
+  bool FindCachedFrameInfo(const StackFrame *frame,
+                           FrameInfoType type,
+                           string *info) const;
+
+  // Save this stack frame in the cache
+  void CacheSourceLineInfo(const StackFrame *frame);
+  void CacheFrameInfo(const StackFrame *frame,
+                      FrameInfoType type,
+                      const string &info);
+
+  // Disallow unwanted copy ctor and assignment operator
+  NetworkSourceLineResolver(const NetworkSourceLineResolver&);
+  void operator=(const NetworkSourceLineResolver&);
+};
+
+}  // namespace google_breakpad
+
+#endif  // GOOGLE_BREAKPAD_PROCESSOR_NETWORK_SOURCE_LINE_RESOLVER_H__
diff --git a/third_party/breakpad/src/google_breakpad/processor/process_state.h b/third_party/breakpad/src/google_breakpad/processor/process_state.h
new file mode 100644
index 0000000..f3f2ec4
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/process_state.h
@@ -0,0 +1,168 @@
+// Copyright (c) 2006, 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.
+
+// process_state.h: A snapshot of a process, in a fully-digested state.
+//
+// Author: Mark Mentovai
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_PROCESS_STATE_H__
+#define GOOGLE_BREAKPAD_PROCESSOR_PROCESS_STATE_H__
+
+#include <string>
+#include <vector>
+#include "google_breakpad/common/breakpad_types.h"
+#include "google_breakpad/processor/system_info.h"
+#include "google_breakpad/processor/minidump.h"
+
+namespace google_breakpad {
+
+using std::string;
+using std::vector;
+
+class CallStack;
+class CodeModules;
+
+enum ExploitabilityRating {
+  EXPLOITABILITY_HIGH,                    // The crash likely represents
+                                          // a exploitable memory corruption
+                                          // vulnerability.
+
+  EXPLOITABLITY_MEDIUM,                   // The crash appears to corrupt
+                                          // memory in a way which may be
+                                          // exploitable in some situations.
+
+  EXPLOITABILITY_LOW,                     // The crash either does not corrupt
+                                          // memory directly or control over
+                                          // the effected data is limited. The
+                                          // issue may still be exploitable
+                                          // on certain platforms or situations.
+
+  EXPLOITABILITY_INTERESTING,             // The crash does not appear to be
+                                          // directly exploitable. However it
+                                          // represents a condition which should
+                                          // be furthur analyzed.
+
+  EXPLOITABILITY_NONE,                    // The crash does not appear to represent
+                                          // an exploitable condition.
+
+  EXPLOITABILITY_NOT_ANALYZED,            // The crash was not analyzed for
+                                          // exploitability because the engine
+                                          // was disabled.
+
+  EXPLOITABILITY_ERR_NOENGINE,            // The supplied minidump's platform does
+                                          // not have a exploitability engine
+                                          // associated with it.
+
+  EXPLOITABILITY_ERR_PROCESSING           // An error occured within the
+                                          // exploitability engine and no rating
+                                          // was calculated.
+};
+
+class ProcessState {
+ public:
+  ProcessState() : modules_(NULL) { Clear(); }
+  ~ProcessState();
+
+  // Resets the ProcessState to its default values
+  void Clear();
+
+  // Accessors.  See the data declarations below.
+  u_int32_t time_date_stamp() const { return time_date_stamp_; }
+  bool crashed() const { return crashed_; }
+  string crash_reason() const { return crash_reason_; }
+  u_int64_t crash_address() const { return crash_address_; }
+  string assertion() const { return assertion_; }
+  int requesting_thread() const { return requesting_thread_; }
+  const vector<CallStack*>* threads() const { return &threads_; }
+  const vector<MinidumpMemoryRegion*>* thread_memory_regions() const {
+    return &thread_memory_regions_;
+  }
+  const SystemInfo* system_info() const { return &system_info_; }
+  const CodeModules* modules() const { return modules_; }
+  ExploitabilityRating exploitability() const { return exploitability_; }
+
+ private:
+  // MinidumpProcessor is responsible for building ProcessState objects.
+  friend class MinidumpProcessor;
+
+  // The time-date stamp of the minidump (time_t format)
+  u_int32_t time_date_stamp_;
+
+  // True if the process crashed, false if the dump was produced outside
+  // of an exception handler.
+  bool crashed_;
+
+  // If the process crashed, the type of crash.  OS- and possibly CPU-
+  // specific.  For example, "EXCEPTION_ACCESS_VIOLATION" (Windows),
+  // "EXC_BAD_ACCESS / KERN_INVALID_ADDRESS" (Mac OS X), "SIGSEGV"
+  // (other Unix).
+  string crash_reason_;
+
+  // If the process crashed, and if crash_reason implicates memory,
+  // the memory address that caused the crash.  For data access errors,
+  // this will be the data address that caused the fault.  For code errors,
+  // this will be the address of the instruction that caused the fault.
+  u_int64_t crash_address_;
+
+  // If there was an assertion that was hit, a textual representation
+  // of that assertion, possibly including the file and line at which
+  // it occurred.
+  string assertion_;
+
+  // The index of the thread that requested a dump be written in the
+  // threads vector.  If a dump was produced as a result of a crash, this
+  // will point to the thread that crashed.  If the dump was produced as
+  // by user code without crashing, and the dump contains extended Breakpad
+  // information, this will point to the thread that requested the dump.
+  // If the dump was not produced as a result of an exception and no
+  // extended Breakpad information is present, this field will be set to -1,
+  // indicating that the dump thread is not available.
+  int requesting_thread_;
+
+  // Stacks for each thread (except possibly the exception handler
+  // thread) at the time of the crash.
+  vector<CallStack*> threads_;
+  vector<MinidumpMemoryRegion*> thread_memory_regions_;
+
+  // OS and CPU information.
+  SystemInfo system_info_;
+
+  // The modules that were loaded into the process represented by the
+  // ProcessState.
+  const CodeModules *modules_;
+
+  // The exploitability rating as determined by the exploitability
+  // engine. When the exploitability engine is not enabled this
+  // defaults to EXPLOITABILITY_NONE.
+  ExploitabilityRating exploitability_;
+};
+
+}  // namespace google_breakpad
+
+#endif  // GOOGLE_BREAKPAD_PROCESSOR_PROCESS_STATE_H__
diff --git a/third_party/breakpad/src/google_breakpad/processor/source_line_resolver_base.h b/third_party/breakpad/src/google_breakpad/processor/source_line_resolver_base.h
new file mode 100644
index 0000000..efa76e7
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/source_line_resolver_base.h
@@ -0,0 +1,118 @@
+// Copyright (c) 2010 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.
+//
+// source_line_resolver_base.h: SourceLineResolverBase, an (incomplete)
+// implementation of SourceLineResolverInterface.  It serves as a common base
+// class for concrete implementations: FastSourceLineResolver and
+// BasicSourceLineResolver.  It is designed for refactoring that removes
+// code redundancy in the two concrete source line resolver classes.
+//
+// See "google_breakpad/processor/source_line_resolver_interface.h" for more
+// documentation.
+
+// Author: Siyang Xie (lambxsy@google.com)
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_BASE_H__
+#define GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_BASE_H__
+
+#include <map>
+#include <string>
+
+#include "google_breakpad/processor/source_line_resolver_interface.h"
+
+namespace google_breakpad {
+
+using std::map;
+
+// Forward declaration.
+// ModuleFactory is a simple factory interface for creating a Module instance
+// at run-time.
+class ModuleFactory;
+
+class SourceLineResolverBase : public SourceLineResolverInterface {
+ public:
+  // Read the symbol_data from a file with given file_name.
+  // The part of code was originally in BasicSourceLineResolver::Module's
+  // LoadMap() method.
+  // Place dynamically allocated heap buffer in symbol_data. Caller has the
+  // ownership of the buffer, and should call delete [] to free the buffer.
+  static bool ReadSymbolFile(char **symbol_data, const string &file_name);
+
+ protected:
+  // Users are not allowed create SourceLineResolverBase instance directly.
+  SourceLineResolverBase(ModuleFactory *module_factory);
+  virtual ~SourceLineResolverBase();
+
+  // Virtual methods inherited from SourceLineResolverInterface.
+  virtual bool LoadModule(const CodeModule *module, const string &map_file);
+  virtual bool LoadModuleUsingMapBuffer(const CodeModule *module,
+                                        const string &map_buffer);
+  virtual bool LoadModuleUsingMemoryBuffer(const CodeModule *module,
+                                           char *memory_buffer);
+  virtual bool ShouldDeleteMemoryBufferAfterLoadModule();
+  virtual void UnloadModule(const CodeModule *module);
+  virtual bool HasModule(const CodeModule *module);
+  virtual void FillSourceLineInfo(StackFrame *frame);
+  virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame);
+  virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame);
+
+  // Nested structs and classes.
+  struct Line;
+  struct Function;
+  struct PublicSymbol;
+  struct CompareString {
+    bool operator()(const string &s1, const string &s2) const;
+  };
+  // Module is an interface for an in-memory symbol file.
+  class Module;
+  class AutoFileCloser;
+
+  // All of the modules that are loaded.
+  typedef map<string, Module*, CompareString> ModuleMap;
+  ModuleMap *modules_;
+
+  // All of heap-allocated buffers that are owned locally by resolver.
+  typedef std::map<string, char*, CompareString> MemoryMap;
+  MemoryMap *memory_buffers_;
+
+  // Creates a concrete module at run-time.
+  ModuleFactory *module_factory_;
+
+ private:
+  // ModuleFactory needs to have access to protected type Module.
+  friend class ModuleFactory;
+
+  // Disallow unwanted copy ctor and assignment operator
+  SourceLineResolverBase(const SourceLineResolverBase&);
+  void operator=(const SourceLineResolverBase&);
+};
+
+}  // namespace google_breakpad
+
+#endif  // GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_BASE_H__
diff --git a/third_party/breakpad/src/google_breakpad/processor/source_line_resolver_interface.h b/third_party/breakpad/src/google_breakpad/processor/source_line_resolver_interface.h
new file mode 100644
index 0000000..103f979
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/source_line_resolver_interface.h
@@ -0,0 +1,111 @@
+// -*- mode: C++ -*-
+
+// Copyright (c) 2010 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.
+
+// Abstract interface to return function/file/line info for a memory address.
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_INTERFACE_H__
+#define GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_INTERFACE_H__
+
+#include <string>
+#include "google_breakpad/common/breakpad_types.h"
+#include "google_breakpad/processor/code_module.h"
+
+namespace google_breakpad {
+
+using std::string;
+
+struct StackFrame;
+struct WindowsFrameInfo;
+struct CFIFrameInfo;
+
+class SourceLineResolverInterface {
+ public:
+  typedef u_int64_t MemAddr;
+
+  virtual ~SourceLineResolverInterface() {}
+
+  // Adds a module to this resolver, returning true on success.
+  //
+  // module should have at least the code_file, debug_file,
+  // and debug_identifier members populated.
+  //
+  // map_file should contain line/address mappings for this module.
+  virtual bool LoadModule(const CodeModule *module,
+                          const string &map_file) = 0;
+  // Same as above, but takes the contents of a pre-read map buffer
+  virtual bool LoadModuleUsingMapBuffer(const CodeModule *module,
+                                        const string &map_buffer) = 0;
+
+  // Add an interface to load symbol using C-String data insteading string.
+  // This is useful in the optimization design for avoiding unnecessary copying
+  // of symbol data, in order to improve memory efficiency.
+  // LoadModuleUsingMemoryBuffer() does NOT take ownership of memory_buffer.
+  virtual bool LoadModuleUsingMemoryBuffer(const CodeModule *module,
+                                           char *memory_buffer) = 0;
+
+  // Return true if the memory buffer should be deleted immediately after
+  // LoadModuleUsingMemoryBuffer(). Return false if the memory buffer has to be
+  // alive during the lifetime of the corresponding Module.
+  virtual bool ShouldDeleteMemoryBufferAfterLoadModule() = 0;
+
+  // Request that the specified module be unloaded from this resolver.
+  // A resolver may choose to ignore such a request.
+  virtual void UnloadModule(const CodeModule *module) = 0;
+
+  // Returns true if the module has been loaded.
+  virtual bool HasModule(const CodeModule *module) = 0;
+
+  // Fills in the function_base, function_name, source_file_name,
+  // and source_line fields of the StackFrame.  The instruction and
+  // module_name fields must already be filled in.
+  virtual void FillSourceLineInfo(StackFrame *frame) = 0;
+
+  // If Windows stack walking information is available covering
+  // FRAME's instruction address, return a WindowsFrameInfo structure
+  // describing it. If the information is not available, returns NULL.
+  // A NULL return value does not indicate an error. The caller takes
+  // ownership of any returned WindowsFrameInfo object.
+  virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame) = 0;
+
+  // If CFI stack walking information is available covering ADDRESS,
+  // return a CFIFrameInfo structure describing it. If the information
+  // is not available, return NULL. The caller takes ownership of any
+  // returned CFIFrameInfo object.
+  virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) = 0;
+
+ protected:
+  // SourceLineResolverInterface cannot be instantiated except by subclasses
+  SourceLineResolverInterface() {}
+};
+
+}  // namespace google_breakpad
+
+#endif  // GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_INTERFACE_H__
diff --git a/third_party/breakpad/src/google_breakpad/processor/stack_frame.h b/third_party/breakpad/src/google_breakpad/processor/stack_frame.h
new file mode 100644
index 0000000..c444993
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/stack_frame.h
@@ -0,0 +1,121 @@
+// Copyright (c) 2006, 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 GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_H__
+#define GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_H__
+
+#include <string>
+#include "google_breakpad/common/breakpad_types.h"
+
+namespace google_breakpad {
+
+class CodeModule;
+
+using std::string;
+
+struct StackFrame {
+  // Indicates how well the instruction pointer derived during
+  // stack walking is trusted. Since the stack walker can resort to
+  // stack scanning, it can wind up with dubious frames.
+  // In rough order of "trust metric".
+  enum FrameTrust {
+    FRAME_TRUST_NONE,     // Unknown
+    FRAME_TRUST_SCAN,     // Scanned the stack, found this
+    FRAME_TRUST_CFI_SCAN, // Scanned the stack using call frame info, found this
+    FRAME_TRUST_FP,       // Derived from frame pointer
+    FRAME_TRUST_CFI,      // Derived from call frame info
+    FRAME_TRUST_CONTEXT   // Given as instruction pointer in a context
+  };
+
+  StackFrame()
+      : instruction(),
+        module(NULL),
+        function_name(),
+        function_base(),
+        source_file_name(),
+        source_line(),
+        source_line_base(),
+        trust(FRAME_TRUST_NONE) {}
+  virtual ~StackFrame() {}
+
+  // Return a string describing how this stack frame was found
+  // by the stackwalker.
+  string trust_description() const {
+    switch (trust) {
+      case StackFrame::FRAME_TRUST_CONTEXT:
+        return "given as instruction pointer in context";
+      case StackFrame::FRAME_TRUST_CFI:
+        return "call frame info";
+      case StackFrame::FRAME_TRUST_CFI_SCAN:
+        return "call frame info with scanning";
+      case StackFrame::FRAME_TRUST_FP:
+        return "previous frame's frame pointer";
+      case StackFrame::FRAME_TRUST_SCAN:
+        return "stack scanning";
+      default:
+        return "unknown";
+    }
+  };
+
+  // The program counter location as an absolute virtual address.  For the
+  // innermost called frame in a stack, this will be an exact program counter
+  // or instruction pointer value.  For all other frames, this will be within
+  // the instruction that caused execution to branch to a called function,
+  // but may not necessarily point to the exact beginning of that instruction.
+  u_int64_t instruction;
+
+  // The module in which the instruction resides.
+  const CodeModule *module;
+
+  // The function name, may be omitted if debug symbols are not available.
+  string function_name;
+
+  // The start address of the function, may be omitted if debug symbols
+  // are not available.
+  u_int64_t function_base;
+
+  // The source file name, may be omitted if debug symbols are not available.
+  string source_file_name;
+
+  // The (1-based) source line number, may be omitted if debug symbols are
+  // not available.
+  int source_line;
+
+  // The start address of the source line, may be omitted if debug symbols
+  // are not available.
+  u_int64_t source_line_base;
+
+  // Amount of trust the stack walker has in the instruction pointer
+  // of this frame.
+  FrameTrust trust;
+};
+
+}  // namespace google_breakpad
+
+#endif  // GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_H__
diff --git a/third_party/breakpad/src/google_breakpad/processor/stack_frame_cpu.h b/third_party/breakpad/src/google_breakpad/processor/stack_frame_cpu.h
new file mode 100644
index 0000000..805b6bc
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/stack_frame_cpu.h
@@ -0,0 +1,243 @@
+// -*- mode: c++ -*-
+
+// Copyright (c) 2010 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.
+
+// stack_frame_cpu.h: CPU-specific StackFrame extensions.
+//
+// These types extend the StackFrame structure to carry CPU-specific register
+// state.  They are defined in this header instead of stack_frame.h to
+// avoid the need to include minidump_format.h when only the generic
+// StackFrame type is needed.
+//
+// Author: Mark Mentovai
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_CPU_H__
+#define GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_CPU_H__
+
+#include "google_breakpad/common/minidump_format.h"
+#include "google_breakpad/processor/stack_frame.h"
+
+namespace google_breakpad {
+
+struct WindowsFrameInfo;
+struct CFIFrameInfo;
+
+struct StackFrameX86 : public StackFrame {
+  // ContextValidity has one entry for each relevant hardware pointer
+  // register (%eip and %esp) and one entry for each general-purpose
+  // register. It's worthwhile having validity flags for caller-saves
+  // registers: they are valid in the youngest frame, and such a frame
+  // might save a callee-saves register in a caller-saves register, but
+  // SimpleCFIWalker won't touch registers unless they're marked as valid.
+  enum ContextValidity {
+    CONTEXT_VALID_NONE = 0,
+    CONTEXT_VALID_EIP  = 1 << 0,
+    CONTEXT_VALID_ESP  = 1 << 1,
+    CONTEXT_VALID_EBP  = 1 << 2,
+    CONTEXT_VALID_EAX  = 1 << 3,
+    CONTEXT_VALID_EBX  = 1 << 4,
+    CONTEXT_VALID_ECX  = 1 << 5,
+    CONTEXT_VALID_EDX  = 1 << 6,
+    CONTEXT_VALID_ESI  = 1 << 7,
+    CONTEXT_VALID_EDI  = 1 << 8,
+    CONTEXT_VALID_ALL  = -1
+  };
+
+ StackFrameX86()
+     : context(),
+       context_validity(CONTEXT_VALID_NONE),
+       windows_frame_info(NULL),
+       cfi_frame_info(NULL) {}
+  ~StackFrameX86();
+
+  // Register state.  This is only fully valid for the topmost frame in a
+  // stack.  In other frames, the values of nonvolatile registers may be
+  // present, given sufficient debugging information.  Refer to
+  // context_validity.
+  MDRawContextX86 context;
+
+  // context_validity is actually ContextValidity, but int is used because
+  // the OR operator doesn't work well with enumerated types.  This indicates
+  // which fields in context are valid.
+  int context_validity;
+
+  // Any stack walking information we found describing this.instruction.
+  // These may be NULL if there is no such information for that address.
+  WindowsFrameInfo *windows_frame_info;
+  CFIFrameInfo *cfi_frame_info;
+};
+
+struct StackFramePPC : public StackFrame {
+  // ContextValidity should eventually contain entries for the validity of
+  // other nonvolatile (callee-save) registers as in
+  // StackFrameX86::ContextValidity, but the ppc stackwalker doesn't currently
+  // locate registers other than the ones listed here.
+  enum ContextValidity {
+    CONTEXT_VALID_NONE = 0,
+    CONTEXT_VALID_SRR0 = 1 << 0,
+    CONTEXT_VALID_GPR1 = 1 << 1,
+    CONTEXT_VALID_ALL  = -1
+  };
+
+  StackFramePPC() : context(), context_validity(CONTEXT_VALID_NONE) {}
+
+  // Register state.  This is only fully valid for the topmost frame in a
+  // stack.  In other frames, the values of nonvolatile registers may be
+  // present, given sufficient debugging information.  Refer to
+  // context_validity.
+  MDRawContextPPC context;
+
+  // context_validity is actually ContextValidity, but int is used because
+  // the OR operator doesn't work well with enumerated types.  This indicates
+  // which fields in context are valid.
+  int context_validity;
+};
+
+struct StackFrameAMD64 : public StackFrame {
+  // ContextValidity has one entry for each register that we might be able
+  // to recover.
+  enum ContextValidity {
+    CONTEXT_VALID_NONE  = 0,
+    CONTEXT_VALID_RAX   = 1 << 0,
+    CONTEXT_VALID_RDX   = 1 << 1,
+    CONTEXT_VALID_RCX   = 1 << 2,
+    CONTEXT_VALID_RBX   = 1 << 3,
+    CONTEXT_VALID_RSI   = 1 << 4,
+    CONTEXT_VALID_RDI   = 1 << 5,
+    CONTEXT_VALID_RBP   = 1 << 6,
+    CONTEXT_VALID_RSP   = 1 << 7,
+    CONTEXT_VALID_R8    = 1 << 8,
+    CONTEXT_VALID_R9    = 1 << 9,
+    CONTEXT_VALID_R10   = 1 << 10,
+    CONTEXT_VALID_R11   = 1 << 11,
+    CONTEXT_VALID_R12   = 1 << 12,
+    CONTEXT_VALID_R13   = 1 << 13,
+    CONTEXT_VALID_R14   = 1 << 14,
+    CONTEXT_VALID_R15   = 1 << 15,
+    CONTEXT_VALID_RIP   = 1 << 16,
+    CONTEXT_VALID_ALL  = -1
+  };
+
+  StackFrameAMD64() : context(), context_validity(CONTEXT_VALID_NONE) {}
+
+  // Register state. This is only fully valid for the topmost frame in a
+  // stack. In other frames, which registers are present depends on what
+  // debugging information we had available. Refer to context_validity.
+  MDRawContextAMD64 context;
+
+  // For each register in context whose value has been recovered, we set
+  // the corresponding CONTEXT_VALID_ bit in context_validity.
+  //
+  // context_validity's type should actually be ContextValidity, but
+  // we use int instead because the bitwise inclusive or operator
+  // yields an int when applied to enum values, and C++ doesn't
+  // silently convert from ints to enums.
+  int context_validity;
+};
+
+struct StackFrameSPARC : public StackFrame {
+  // to be confirmed
+  enum ContextValidity {
+    CONTEXT_VALID_NONE = 0,
+    CONTEXT_VALID_PC   = 1 << 0,
+    CONTEXT_VALID_SP   = 1 << 1,
+    CONTEXT_VALID_FP   = 1 << 2,
+    CONTEXT_VALID_ALL  = -1
+  };
+
+  StackFrameSPARC() : context(), context_validity(CONTEXT_VALID_NONE) {}
+
+  // Register state.  This is only fully valid for the topmost frame in a
+  // stack.  In other frames, the values of nonvolatile registers may be
+  // present, given sufficient debugging information.  Refer to
+  // context_validity.
+  MDRawContextSPARC context;
+
+  // context_validity is actually ContextValidity, but int is used because
+  // the OR operator doesn't work well with enumerated types.  This indicates
+  // which fields in context are valid.
+  int context_validity;
+};
+
+struct StackFrameARM : public StackFrame {
+  // A flag for each register we might know.
+  enum ContextValidity {
+    CONTEXT_VALID_NONE = 0,
+    CONTEXT_VALID_R0   = 1 << 0,
+    CONTEXT_VALID_R1   = 1 << 1,
+    CONTEXT_VALID_R2   = 1 << 2,
+    CONTEXT_VALID_R3   = 1 << 3,
+    CONTEXT_VALID_R4   = 1 << 4,
+    CONTEXT_VALID_R5   = 1 << 5,
+    CONTEXT_VALID_R6   = 1 << 6,
+    CONTEXT_VALID_R7   = 1 << 7,
+    CONTEXT_VALID_R8   = 1 << 8,
+    CONTEXT_VALID_R9   = 1 << 9,
+    CONTEXT_VALID_R10  = 1 << 10,
+    CONTEXT_VALID_R11  = 1 << 11,
+    CONTEXT_VALID_R12  = 1 << 12,
+    CONTEXT_VALID_R13  = 1 << 13,
+    CONTEXT_VALID_R14  = 1 << 14,
+    CONTEXT_VALID_R15  = 1 << 15,
+    CONTEXT_VALID_ALL  = ~CONTEXT_VALID_NONE,
+
+    // Aliases for registers with dedicated or conventional roles.
+    CONTEXT_VALID_FP   = CONTEXT_VALID_R11,
+    CONTEXT_VALID_SP   = CONTEXT_VALID_R13,
+    CONTEXT_VALID_LR   = CONTEXT_VALID_R14,
+    CONTEXT_VALID_PC   = CONTEXT_VALID_R15
+  };
+
+  StackFrameARM() : context(), context_validity(CONTEXT_VALID_NONE) {}
+
+  // Return the ContextValidity flag for register rN.
+  static ContextValidity RegisterValidFlag(int n) {
+    return ContextValidity(1 << n);
+  } 
+
+  // Register state.  This is only fully valid for the topmost frame in a
+  // stack.  In other frames, the values of nonvolatile registers may be
+  // present, given sufficient debugging information.  Refer to
+  // context_validity.
+  MDRawContextARM context;
+
+  // For each register in context whose value has been recovered, we set
+  // the corresponding CONTEXT_VALID_ bit in context_validity.
+  //
+  // context_validity's type should actually be ContextValidity, but
+  // we use int instead because the bitwise inclusive or operator
+  // yields an int when applied to enum values, and C++ doesn't
+  // silently convert from ints to enums.
+  int context_validity;
+};
+
+}  // namespace google_breakpad
+
+#endif  // GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_CPU_H__
diff --git a/third_party/breakpad/src/google_breakpad/processor/stackwalker.h b/third_party/breakpad/src/google_breakpad/processor/stackwalker.h
new file mode 100644
index 0000000..6822f16
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/stackwalker.h
@@ -0,0 +1,194 @@
+// Copyright (c) 2010 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.
+
+// stackwalker.h: Generic stackwalker.
+//
+// The Stackwalker class is an abstract base class providing common generic
+// methods that apply to stacks from all systems.  Specific implementations
+// will extend this class by providing GetContextFrame and GetCallerFrame
+// methods to fill in system-specific data in a StackFrame structure.
+// Stackwalker assembles these StackFrame strucutres into a CallStack.
+//
+// Author: Mark Mentovai
+
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__
+#define GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__
+
+#include <set>
+#include <string>
+#include "google_breakpad/common/breakpad_types.h"
+#include "google_breakpad/processor/code_modules.h"
+#include "google_breakpad/processor/memory_region.h"
+
+namespace google_breakpad {
+
+class CallStack;
+class MinidumpContext;
+class SourceLineResolverInterface;
+struct StackFrame;
+class SymbolSupplier;
+class SystemInfo;
+
+using std::set;
+
+
+class Stackwalker {
+ public:
+  virtual ~Stackwalker() {}
+
+  // Populates the given CallStack by calling GetContextFrame and
+  // GetCallerFrame.  The frames are further processed to fill all available
+  // data.  Returns true if the stackwalk completed, or false if it was
+  // interrupted by SymbolSupplier::GetSymbolFile().
+  bool Walk(CallStack *stack);
+
+  // Returns a new concrete subclass suitable for the CPU that a stack was
+  // generated on, according to the CPU type indicated by the context
+  // argument.  If no suitable concrete subclass exists, returns NULL.
+  static Stackwalker* StackwalkerForCPU(const SystemInfo *system_info,
+                                        MinidumpContext *context,
+                                        MemoryRegion *memory,
+                                        const CodeModules *modules,
+                                        SymbolSupplier *supplier,
+                                        SourceLineResolverInterface *resolver);
+
+  static void set_max_frames(u_int32_t max_frames) { max_frames_ = max_frames; }
+  static u_int32_t max_frames() { return max_frames_; }
+
+ protected:
+  // system_info identifies the operating system, NULL or empty if unknown.
+  // memory identifies a MemoryRegion that provides the stack memory
+  // for the stack to walk.  modules, if non-NULL, is a CodeModules
+  // object that is used to look up which code module each stack frame is
+  // associated with.  supplier is an optional caller-supplied SymbolSupplier
+  // implementation.  If supplier is NULL, source line info will not be
+  // resolved.  resolver is an instance of SourceLineResolverInterface
+  // (see source_line_resolver_interface.h and basic_source_line_resolver.h).
+  // If resolver is NULL, source line info will not be resolved.
+  Stackwalker(const SystemInfo *system_info,
+              MemoryRegion *memory,
+              const CodeModules *modules,
+              SymbolSupplier *supplier,
+              SourceLineResolverInterface *resolver);
+
+  // This can be used to filter out potential return addresses when
+  // the stack walker resorts to stack scanning.
+  // Returns true if any of:
+  // * This address is within a loaded module, but we don't have symbols
+  //   for that module.
+  // * This address is within a loaded module for which we have symbols,
+  //   and falls inside a function in that module.
+  // Returns false otherwise.
+  bool InstructionAddressSeemsValid(u_int64_t address);
+
+  // Scan the stack starting at location_start, looking for an address
+  // that looks like a valid instruction pointer. Addresses must
+  // 1) be contained in the current stack memory
+  // 2) pass the checks in InstructionAddressSeemsValid
+  //
+  // Returns true if a valid-looking instruction pointer was found.
+  // When returning true, sets location_found to the address at which
+  // the value was found, and ip_found to the value contained at that
+  // location in memory.
+  template<typename InstructionType>
+  bool ScanForReturnAddress(InstructionType location_start,
+                            InstructionType *location_found,
+                            InstructionType *ip_found) {
+    const int kRASearchWords = 30;
+    for (InstructionType location = location_start;
+         location <= location_start + kRASearchWords * sizeof(InstructionType);
+         location += sizeof(InstructionType)) {
+      InstructionType ip;
+      if (!memory_->GetMemoryAtAddress(location, &ip))
+        break;
+
+      if (modules_ && modules_->GetModuleForAddress(ip) &&
+          InstructionAddressSeemsValid(ip)) {
+
+        *ip_found = ip;
+        *location_found = location;
+        return true;
+      }
+    }
+    // nothing found
+    return false;
+  }
+
+  // Information about the system that produced the minidump.  Subclasses
+  // and the SymbolSupplier may find this information useful.
+  const SystemInfo *system_info_;
+
+  // The stack memory to walk.  Subclasses will require this region to
+  // get information from the stack.
+  MemoryRegion *memory_;
+
+  // A list of modules, for populating each StackFrame's module information.
+  // This field is optional and may be NULL.
+  const CodeModules *modules_;
+
+ protected:
+  // The SourceLineResolver implementation.
+  SourceLineResolverInterface *resolver_;
+
+ private:
+  // Obtains the context frame, the innermost called procedure in a stack
+  // trace.  Returns NULL on failure.  GetContextFrame allocates a new
+  // StackFrame (or StackFrame subclass), ownership of which is taken by
+  // the caller.
+  virtual StackFrame* GetContextFrame() = 0;
+
+  // Obtains a caller frame.  Each call to GetCallerFrame should return the
+  // frame that called the last frame returned by GetContextFrame or
+  // GetCallerFrame.  To aid this purpose, stack contains the CallStack
+  // made of frames that have already been walked.  GetCallerFrame should
+  // return NULL on failure or when there are no more caller frames (when
+  // the end of the stack has been reached).  GetCallerFrame allocates a new
+  // StackFrame (or StackFrame subclass), ownership of which is taken by
+  // the caller.
+  virtual StackFrame* GetCallerFrame(const CallStack *stack) = 0;
+
+  // The optional SymbolSupplier for resolving source line info.
+  SymbolSupplier *supplier_;
+
+  // A list of modules that we haven't found symbols for.  We track
+  // this in order to avoid repeatedly looking them up again within
+  // one minidump.
+  set<std::string> no_symbol_modules_;
+
+  // The maximum number of frames Stackwalker will walk through.
+  // This defaults to 1024 to prevent infinite loops.
+  static u_int32_t max_frames_;
+};
+
+
+}  // namespace google_breakpad
+
+
+#endif  // GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__
diff --git a/third_party/breakpad/src/google_breakpad/processor/symbol_supplier.h b/third_party/breakpad/src/google_breakpad/processor/symbol_supplier.h
new file mode 100644
index 0000000..26f5d7f
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/symbol_supplier.h
@@ -0,0 +1,98 @@
+// Copyright (c) 2006, 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.
+
+// The caller may implement the SymbolSupplier abstract base class
+// to provide symbols for a given module.
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_SYMBOL_SUPPLIER_H__
+#define GOOGLE_BREAKPAD_PROCESSOR_SYMBOL_SUPPLIER_H__
+
+#include <string>
+
+namespace google_breakpad {
+
+using std::string;
+class CodeModule;
+class SystemInfo;
+
+class SymbolSupplier {
+ public:
+  // Result type for GetSymbolFile
+  enum SymbolResult {
+    // no symbols were found, but continue processing
+    NOT_FOUND,
+
+    // symbols were found, and the path has been placed in symbol_file
+    FOUND,
+
+    // stops processing the minidump immediately
+    INTERRUPT
+  };
+
+  virtual ~SymbolSupplier() {}
+
+  // Retrieves the symbol file for the given CodeModule, placing the
+  // path in symbol_file if successful.  system_info contains strings
+  // identifying the operating system and CPU; SymbolSupplier may use
+  // to help locate the symbol file.  system_info may be NULL or its
+  // fields may be empty if these values are unknown.  symbol_file
+  // must be a pointer to a valid string
+  virtual SymbolResult GetSymbolFile(const CodeModule *module,
+                                     const SystemInfo *system_info,
+                                     string *symbol_file) = 0;
+  // Same as above, except also places symbol data into symbol_data.
+  // If symbol_data is NULL, the data is not returned.
+  // TODO(nealsid) Once we have symbol data caching behavior implemented
+  // investigate making all symbol suppliers implement all methods,
+  // and make this pure virtual
+  virtual SymbolResult GetSymbolFile(const CodeModule *module,
+                                     const SystemInfo *system_info,
+                                     string *symbol_file,
+                                     string *symbol_data) = 0;
+
+  // Same as above, except allocates data buffer on heap and then places the
+  // symbol data into the buffer as C-string.
+  // SymbolSupplier is responsible for deleting the data buffer. After the call
+  // to GetCStringSymbolData(), the caller should call FreeSymbolData(const
+  // Module *module) once the data buffer is no longer needed.
+  // If symbol_data is not NULL, symbol supplier won't return FOUND unless it
+  // returns a valid buffer in symbol_data, e.g., returns INTERRUPT on memory
+  // allocation failure.
+  virtual SymbolResult GetCStringSymbolData(const CodeModule *module,
+                                            const SystemInfo *system_info,
+                                            string *symbol_file,
+                                            char **symbol_data) = 0;
+
+  // Frees the data buffer allocated for the module in GetCStringSymbolData.
+  virtual void FreeSymbolData(const CodeModule *module) = 0;
+};
+
+}  // namespace google_breakpad
+
+#endif  // GOOGLE_BREAKPAD_PROCESSOR_SYMBOL_SUPPLIER_H__
diff --git a/third_party/breakpad/src/google_breakpad/processor/system_info.h b/third_party/breakpad/src/google_breakpad/processor/system_info.h
new file mode 100644
index 0000000..fdbdbfd
--- /dev/null
+++ b/third_party/breakpad/src/google_breakpad/processor/system_info.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2006, 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.
+
+// system_info.h: Information about the system that was running a program
+// when a crash report was produced.
+//
+// Author: Mark Mentovai
+
+#ifndef GOOGLE_BREAKPAD_PROCESSOR_SYSTEM_INFO_H__
+#define GOOGLE_BREAKPAD_PROCESSOR_SYSTEM_INFO_H__
+
+#include <string>
+
+namespace google_breakpad {
+
+using std::string;
+
+struct SystemInfo {
+ public:
+  SystemInfo() : os(), os_short(), os_version(), cpu(), cpu_info(),
+    cpu_count(0) {}
+
+  // Resets the SystemInfo object to its default values.
+  void Clear() {
+    os.clear();
+    os_short.clear();
+    os_version.clear();
+    cpu.clear();
+    cpu_info.clear();
+    cpu_count = 0;
+  }
+
+  // A string identifying the operating system, such as "Windows NT",
+  // "Mac OS X", or "Linux".  If the information is present in the dump but
+  // its value is unknown, this field will contain a numeric value.  If
+  // the information is not present in the dump, this field will be empty.
+  string os;
+
+  // A short form of the os string, using lowercase letters and no spaces,
+  // suitable for use in a filesystem.  Possible values are "windows",
+  // "mac", and "linux".  Empty if the information is not present in the dump
+  // or if the OS given by the dump is unknown.  The values stored in this
+  // field should match those used by MinidumpSystemInfo::GetOS.
+  string os_short;
+
+  // A string identifying the version of the operating system, such as
+  // "5.1.2600 Service Pack 2" or "10.4.8 8L2127".  If the dump does not
+  // contain this information, this field will be empty.
+  string os_version;
+
+  // A string identifying the basic CPU family, such as "x86" or "ppc".
+  // If this information is present in the dump but its value is unknown,
+  // this field will contain a numeric value.  If the information is not
+  // present in the dump, this field will be empty.  The values stored in
+  // this field should match those used by MinidumpSystemInfo::GetCPU.
+  string cpu;
+
+  // A string further identifying the specific CPU, such as
+  // "GenuineIntel level 6 model 13 stepping 8".  If the information is not
+  // present in the dump, or additional identifying information is not
+  // defined for the CPU family, this field will be empty.
+  string cpu_info;
+
+  // The number of processors in the system.  Will be greater than one for
+  // multi-core systems.
+  int cpu_count;
+};
+
+}  // namespace google_breakpad
+
+#endif  // GOOGLE_BREAKPAD_PROCESSOR_SYSTEM_INFO_H__
diff --git a/third_party/build.scons b/third_party/build.scons
index efe82c6..669628a 100644
--- a/third_party/build.scons
+++ b/third_party/build.scons
@@ -20,6 +20,7 @@
 
 subdirs = [
     'breakpad',
+    'gmock',
     'gtest',
     'lzma',
     'minicrt',
diff --git a/third_party/chrome/third_party/npapi/bindings/npapi.h b/third_party/chrome/third_party/npapi/bindings/npapi.h
new file mode 100644
index 0000000..1e6e7e2
--- /dev/null
+++ b/third_party/chrome/third_party/npapi/bindings/npapi.h
@@ -0,0 +1,932 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+/*
+ *  Netscape client plug-in API spec
+ */
+
+
+#ifndef _NPAPI_H_
+#define _NPAPI_H_
+
+
+/* BEGIN GOOGLE MODIFICATIONS */
+
+#ifdef __native_client__
+#include <stdint.h>
+#else
+#include "base/basictypes.h"
+#endif  /* __native_client__ */
+
+/* END GOOGLE MODIFICATIONS */
+
+#ifdef INCLUDE_JAVA
+#include "jri.h"                /* Java Runtime Interface */
+#else
+#define jref    void *
+#define JRIEnv  void
+#endif
+
+#ifdef _WIN32
+#    ifndef XP_WIN
+#        define XP_WIN 1
+#    endif /* XP_WIN */
+#endif /* _WIN32 */
+
+/* BEGIN GOOGLE MODIFICATIONS */
+/* On Linux and Mac, be sure to set Mozilla-specific macros. */
+#if defined(USE_X11)
+#define XP_UNIX 1
+#define MOZ_X11 1
+#endif
+/* END GOOGLE MODIFICATIONS */
+
+#ifdef __MWERKS__
+#    define _declspec __declspec
+#    ifdef macintosh
+#        ifndef XP_MAC
+#            define XP_MAC 1
+#        endif /* XP_MAC */
+#    endif /* macintosh */
+#    ifdef __INTEL__
+#        undef NULL
+#        ifndef XP_WIN
+#            define XP_WIN 1
+#        endif /* __INTEL__ */
+#    endif /* XP_PC */
+#endif /* __MWERKS__ */
+
+#ifdef __SYMBIAN32__
+#   ifndef XP_SYMBIAN
+#       define XP_SYMBIAN 1
+#       undef XP_WIN
+#   endif
+#endif  /* __SYMBIAN32__ */
+
+#if defined(__APPLE_CC__) && !defined(__MACOS_CLASSIC__) && !defined(XP_UNIX)
+#   define XP_MACOSX
+#endif
+
+#ifdef XP_MAC
+#include <Quickdraw.h>
+#include <Events.h>
+#endif
+
+#if defined(XP_MACOSX) && defined(__LP64__)
+#define NP_NO_QUICKDRAW
+#define NP_NO_CARBON
+#endif
+
+#ifdef XP_MACOSX
+#include <ApplicationServices/ApplicationServices.h>
+#include <OpenGL/OpenGL.h>
+#ifndef NP_NO_CARBON
+#include <Carbon/Carbon.h>
+#endif
+#endif
+
+#if defined(XP_UNIX) 
+#	include <stdio.h>
+/* BEGIN GOOGLE MODIFICATIONS */
+#if 0
+/* END GOOGLE MODIFICATIONS */
+#	if defined(MOZ_X11)
+#		include <X11/Xlib.h>
+#		include <X11/Xutil.h>
+#	endif
+/* BEGIN GOOGLE MODIFICATIONS */
+#endif
+/* END GOOGLE MODIFICATIONS */
+#endif
+
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+/*----------------------------------------------------------------------*/
+/*             Plugin Version Constants                                 */
+/*----------------------------------------------------------------------*/
+
+#define NP_VERSION_MAJOR 0
+/* BEGIN GOOGLE MODIFICATIONS */
+#define NP_VERSION_MINOR 23 /* maximum version currently supported by Chromium */
+/* END GOOGLE MODIFICATIONS */
+
+
+/*----------------------------------------------------------------------*/
+/*             Definition of Basic Types                                */
+/*----------------------------------------------------------------------*/
+
+/* QNX sets the _INT16 and friends defines, but does not typedef the types */
+#ifdef __QNXNTO__
+#undef _UINT16
+#undef _INT16
+#undef _UINT32
+#undef _INT32
+#endif
+
+#ifndef _UINT16
+#define _UINT16
+typedef unsigned short uint16;
+#endif
+
+#ifndef _UINT32
+#define _UINT32
+#ifdef __LP64__
+typedef unsigned int uint32;
+#else /* __LP64__ */
+typedef unsigned long uint32;
+#endif /* __LP64__ */
+#endif
+
+#ifndef _INT16
+#define _INT16
+typedef short int16;
+#endif
+
+#ifndef _INT32
+#define _INT32
+#ifdef __LP64__
+typedef int int32;
+#else /* __LP64__ */
+typedef long int32;
+#endif /* __LP64__ */
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+#ifndef TRUE
+#define TRUE (1)
+#endif
+#ifndef NULL
+#define NULL (0L)
+#endif
+
+typedef unsigned char    NPBool;
+typedef int16            NPError;
+typedef int16            NPReason;
+typedef char*            NPMIMEType;
+
+
+
+/*----------------------------------------------------------------------*/
+/*                       Structures and definitions                     */
+/*----------------------------------------------------------------------*/
+
+#if !defined(__LP64__)
+#if defined(XP_MAC) || defined(XP_MACOSX)
+#pragma options align=mac68k
+#endif
+#endif /* __LP64__ */
+
+/*
+ *  NPP is a plug-in's opaque instance handle
+ */
+typedef struct _NPP
+{
+  void*	pdata;      /* plug-in private data */
+  void*	ndata;      /* netscape private data */
+} NPP_t;
+
+typedef NPP_t*  NPP;
+
+
+typedef struct _NPStream
+{
+  void*  pdata; /* plug-in private data */
+  void*  ndata; /* netscape private data */
+  const  char* url;
+  uint32 end;
+  uint32 lastmodified;
+  void*  notifyData;
+  const  char* headers; /* Response headers from host.
+                         * Exists only for >= NPVERS_HAS_RESPONSE_HEADERS.
+                         * Used for HTTP only; NULL for non-HTTP.
+                         * Available from NPP_NewStream onwards.
+                         * Plugin should copy this data before storing it.
+                         * Includes HTTP status line and all headers,
+                         * preferably verbatim as received from server,
+                         * headers formatted as in HTTP ("Header: Value"),
+                         * and newlines (\n, NOT \r\n) separating lines.
+                         * Terminated by \n\0 (NOT \n\n\0). */
+} NPStream;
+
+
+typedef struct _NPByteRange
+{
+  int32  offset; /* negative offset means from the end */
+  uint32 length;
+  struct _NPByteRange* next;
+} NPByteRange;
+
+
+typedef struct _NPSavedData
+{
+  int32	len;
+  void*	buf;
+} NPSavedData;
+
+
+typedef struct _NPRect
+{
+  uint16 top;
+  uint16 left;
+  uint16 bottom;
+  uint16 right;
+} NPRect;
+
+
+#ifdef XP_UNIX
+/*
+ * Unix specific structures and definitions
+ */
+
+/*
+ * Callback Structures.
+ *
+ * These are used to pass additional platform specific information.
+ */
+enum {
+  NP_SETWINDOW = 1,
+  NP_PRINT
+};
+
+typedef struct
+{
+  int32 type;
+} NPAnyCallbackStruct;
+
+/* BEGIN GOOGLE MODIFICATIONS */
+typedef struct _NPSetWindowCallbackStruct NPSetWindowCallbackStruct;
+/* END GOOGLE MODIFICATIONS */
+
+typedef struct
+{
+  int32 type;
+  FILE* fp;
+} NPPrintCallbackStruct;
+
+#endif /* XP_UNIX */
+
+
+/*
+ *   The following masks are applied on certain platforms to NPNV and 
+ *   NPPV selectors that pass around pointers to COM interfaces. Newer 
+ *   compilers on some platforms may generate vtables that are not 
+ *   compatible with older compilers. To prevent older plugins from 
+ *   not understanding a new browser's ABI, these masks change the 
+ *   values of those selectors on those platforms. To remain backwards
+ *   compatible with differenet versions of the browser, plugins can 
+ *   use these masks to dynamically determine and use the correct C++
+ *   ABI that the browser is expecting. This does not apply to Windows 
+ *   as Microsoft's COM ABI will likely not change.
+ */
+
+#define NP_ABI_GCC3_MASK  0x10000000
+/*
+ *   gcc 3.x generated vtables on UNIX and OSX are incompatible with 
+ *   previous compilers.
+ */
+#if (defined (XP_UNIX) && defined(__GNUC__) && (__GNUC__ >= 3))
+#define _NP_ABI_MIXIN_FOR_GCC3 NP_ABI_GCC3_MASK
+#else
+#define _NP_ABI_MIXIN_FOR_GCC3 0
+#endif
+
+#define NP_ABI_MACHO_MASK 0x01000000
+/*
+ *   On OSX, the Mach-O executable format is significantly
+ *   different than CFM. In addition to having a different
+ *   C++ ABI, it also has has different C calling convention.
+ *   You must use glue code when calling between CFM and
+ *   Mach-O C functions. 
+ */
+#if (defined(TARGET_RT_MAC_MACHO))
+#define _NP_ABI_MIXIN_FOR_MACHO NP_ABI_MACHO_MASK
+#else
+#define _NP_ABI_MIXIN_FOR_MACHO 0
+#endif
+
+
+#define NP_ABI_MASK (_NP_ABI_MIXIN_FOR_GCC3 | _NP_ABI_MIXIN_FOR_MACHO)
+
+/*
+ * List of variable names for which NPP_GetValue shall be implemented
+ */
+typedef enum {
+  NPPVpluginNameString = 1,
+  NPPVpluginDescriptionString,
+  NPPVpluginWindowBool,
+  NPPVpluginTransparentBool,
+  NPPVjavaClass,                /* Not implemented in Mozilla 1.0 */
+  NPPVpluginWindowSize,
+  NPPVpluginTimerInterval,
+
+  NPPVpluginScriptableInstance = (10 | NP_ABI_MASK),
+  NPPVpluginScriptableIID = 11,
+
+  /* Introduced in Mozilla 0.9.9 */
+  NPPVjavascriptPushCallerBool = 12,
+
+  /* Introduced in Mozilla 1.0 */
+  NPPVpluginKeepLibraryInMemory = 13,
+  NPPVpluginNeedsXEmbed         = 14,
+
+  /* Get the NPObject for scripting the plugin. Introduced in Firefox
+   * 1.0 (NPAPI minor version 14).
+   */
+  NPPVpluginScriptableNPObject  = 15,
+
+  /* Get the plugin value (as \0-terminated UTF-8 string data) for
+   * form submission if the plugin is part of a form. Use
+   * NPN_MemAlloc() to allocate memory for the string data.
+   */
+  NPPVformValue = 16,    /* Not implemented in WebKit */
+  
+  NPPVpluginUrlRequestsDisplayedBool = 17, /* Not implemented in WebKit */
+  
+  /* Checks if the plugin is interested in receiving the http body of
+   * failed http requests (http status != 200).
+   */
+  NPPVpluginWantsAllNetworkStreams = 18,
+  
+  /* Checks to see if the plug-in would like the browser to load the "src" attribute. */
+  NPPVpluginCancelSrcStream = 20
+  
+#ifdef XP_MACOSX
+  /* Used for negotiating drawing models */
+  , NPPVpluginDrawingModel = 1000,
+  /* Used for negotiating event models */
+  NPPVpluginEventModel = 1001,
+  /* In the NPDrawingModelCoreAnimation drawing model, the browser asks the plug-in for a Core Animation layer. */
+  NPPVpluginCoreAnimationLayer = 1003  
+#endif
+} NPPVariable;
+
+/*
+ * List of variable names for which NPN_GetValue is implemented by Mozilla
+ */
+typedef enum {
+  NPNVxDisplay = 1,
+  NPNVxtAppContext,
+  NPNVnetscapeWindow,
+  NPNVjavascriptEnabledBool,
+  NPNVasdEnabledBool,
+  NPNVisOfflineBool,
+
+  /* 10 and over are available on Mozilla builds starting with 0.9.4 */
+  NPNVserviceManager = (10 | NP_ABI_MASK),
+  NPNVDOMElement     = (11 | NP_ABI_MASK),   /* available in Mozilla 1.2 */
+  NPNVDOMWindow      = (12 | NP_ABI_MASK),
+  NPNVToolkit        = (13 | NP_ABI_MASK),
+  NPNVSupportsXEmbedBool = 14,
+
+  /* Get the NPObject wrapper for the browser window. */
+  NPNVWindowNPObject = 15,
+
+  /* Get the NPObject wrapper for the plugins DOM element. */
+  NPNVPluginElementNPObject = 16,
+
+  NPNVSupportsWindowless = 17,
+
+  NPNVprivateModeBool = 18
+
+#ifdef XP_MACOSX
+  /* Used for negotiating drawing models */
+  , NPNVpluginDrawingModel = 1000
+#ifndef NP_NO_QUICKDRAW
+  , NPNVsupportsQuickDrawBool = 2000
+#endif
+  , NPNVsupportsCoreGraphicsBool = 2001
+  , NPNVsupportsOpenGLBool = 2002 /* TRUE if the browser supports the OpenGL drawing model (CGL on Mac) */
+  , NPNVsupportsCoreAnimationBool = 2003 /* TRUE if the browser supports the CoreAnimation drawing model */
+  
+#ifndef NP_NO_CARBON
+  , NPNVsupportsCarbonBool = 3000 /* TRUE if the browser supports the Carbon event model */
+#endif
+  , NPNVsupportsCocoaBool = 3001 /* TRUE if the browser supports the Cocoa event model */
+#endif
+} NPNVariable;
+
+typedef enum {
+  NPNURLVCookie = 501,
+  NPNURLVProxy
+} NPNURLVariable;
+
+/* BEGIN GOOGLE MODIFICATIONS */
+/*
+ * The type of Tookkit the widgets use
+ */
+typedef enum {
+  NPNVGtk12 = 1,
+  NPNVGtk2
+} NPNToolkitType;
+/* END GOOGLE MODIFICATIONS */
+
+/*
+ * The type of a NPWindow - it specifies the type of the data structure
+ * returned in the window field.
+ */
+typedef enum {
+  NPWindowTypeWindow = 1,
+  NPWindowTypeDrawable
+} NPWindowType;
+
+#ifdef XP_MACOSX
+
+/*
+ * The drawing model for a Mac OS X plugin.  These are the possible values for the NPNVpluginDrawingModel variable.
+ */
+
+typedef enum {
+#ifndef NP_NO_QUICKDRAW
+  NPDrawingModelQuickDraw = 0,
+#endif
+  NPDrawingModelCoreGraphics = 1,
+  NPDrawingModelOpenGL = 2,
+  NPDrawingModelCoreAnimation = 3
+} NPDrawingModel;
+
+/*
+ * The event model for a Mac OS X plugin. These are the possible values for the NPNVpluginEventModel variable.
+ */
+
+typedef enum {
+#ifndef NP_NO_CARBON
+  NPEventModelCarbon = 0,
+#endif
+  NPEventModelCocoa = 1
+} NPEventModel;
+
+typedef enum {
+  NPCocoaEventDrawRect = 1,
+  NPCocoaEventMouseDown,
+  NPCocoaEventMouseUp,
+  NPCocoaEventMouseMoved,
+  NPCocoaEventMouseEntered,
+  NPCocoaEventMouseExited,
+  NPCocoaEventMouseDragged,
+  NPCocoaEventKeyDown,
+  NPCocoaEventKeyUp,
+  NPCocoaEventFlagsChanged,
+  NPCocoaEventFocusChanged,
+  NPCocoaEventWindowFocusChanged,
+  NPCocoaEventScrollWheel,
+  NPCocoaEventTextInput
+} NPCocoaEventType;
+
+typedef struct _NPNSString NPNSString;
+typedef struct _NPNSWindow NPNSWindow;
+typedef struct _NPNSMenu NPNSMenu;
+
+typedef struct _NPCocoaEvent {
+  NPCocoaEventType type;
+  uint32 version;
+  
+  union {
+    struct {
+      uint32 modifierFlags;
+      double pluginX;
+      double pluginY;            
+      int32 buttonNumber;
+      int32 clickCount;
+      double deltaX;
+      double deltaY;
+      double deltaZ;
+    } mouse;
+    struct {
+      uint32 modifierFlags;
+      NPNSString *characters;
+      NPNSString *charactersIgnoringModifiers;
+      NPBool isARepeat;
+      uint16 keyCode;
+    } key;
+    struct {
+      CGContextRef context;
+      
+      double x;
+      double y;
+      double width;
+      double height;
+    } draw;
+    struct {
+      NPBool hasFocus;
+    } focus;
+    struct {
+      NPNSString *text;
+    } text;
+  } data;
+} NPCocoaEvent;
+
+#endif
+
+typedef struct _NPWindow
+{
+  void* window;  /* Platform specific window handle */
+  int32 x;       /* Position of top left corner relative */
+  int32 y;       /* to a netscape page.					*/
+  uint32 width;  /* Maximum window size */
+  uint32 height;
+  NPRect clipRect; /* Clipping rectangle in port coordinates */
+                   /* Used by MAC only.			  */
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+  void * ws_info; /* Platform-dependent additonal data */
+#endif /* XP_UNIX */
+  NPWindowType type; /* Is this a window or a drawable? */
+} NPWindow;
+
+
+typedef struct _NPFullPrint
+{
+  NPBool pluginPrinted;/* Set TRUE if plugin handled fullscreen printing */
+  NPBool printOne;		 /* TRUE if plugin should print one copy to default printer */
+  void* platformPrint; /* Platform-specific printing info */
+} NPFullPrint;
+
+typedef struct _NPEmbedPrint
+{
+  NPWindow window;
+  void* platformPrint; /* Platform-specific printing info */
+} NPEmbedPrint;
+
+typedef struct _NPPrint
+{
+  uint16 mode;               /* NP_FULL or NP_EMBED */
+  union
+  {
+    NPFullPrint fullPrint;   /* if mode is NP_FULL */
+    NPEmbedPrint embedPrint; /* if mode is NP_EMBED */
+  } print;
+} NPPrint;
+
+#ifdef XP_MACOSX
+/* BEGIN GOOGLE MODIFICATIONS */
+typedef struct _NPNSMenu NPNSMenu;
+/* END GOOGLE MODIFICATIONS */
+typedef NPNSMenu NPMenu;
+#else
+typedef void * NPMenu;
+#endif
+
+typedef enum {
+  NPCoordinateSpacePlugin = 1,
+  NPCoordinateSpaceWindow,
+  NPCoordinateSpaceFlippedWindow,
+  NPCoordinateSpaceScreen,
+  NPCoordinateSpaceFlippedScreen
+} NPCoordinateSpace;
+
+#if defined(XP_MAC) || defined(XP_MACOSX)
+
+#ifndef NP_NO_CARBON
+typedef EventRecord    NPEvent;
+#endif
+
+#elif defined(XP_WIN)
+typedef struct _NPEvent
+{
+  uint16 event;
+  uint32 wParam;
+  uint32 lParam;
+} NPEvent;
+#elif defined (XP_UNIX) && defined(MOZ_X11)
+/* BEGIN GOOGLE MODIFICATIONS */
+typedef union _XEvent XEvent;
+/* END GOOGLE MODIFICATIONS */
+typedef XEvent NPEvent;
+#else
+typedef void*			NPEvent;
+#endif /* XP_MACOSX */
+
+#if defined(XP_MAC)
+typedef RgnHandle NPRegion;
+#elif defined(XP_MACOSX)
+/* 
+ * NPRegion's type depends on the drawing model specified by the plugin (see NPNVpluginDrawingModel).
+ * NPQDRegion represents a QuickDraw RgnHandle and is used with the QuickDraw drawing model.
+ * NPCGRegion repesents a graphical region when using any other drawing model.
+ */
+typedef void *NPRegion;
+#ifndef NP_NO_QUICKDRAW
+typedef RgnHandle NPQDRegion;
+#endif
+typedef CGPathRef NPCGRegion;
+#elif defined(XP_WIN)
+typedef HRGN NPRegion;
+#elif defined(XP_UNIX)
+/* BEGIN GOOGLE MODIFICATIONS */
+typedef struct _XRegion *Region;	
+/* END GOOGLE MODIFICATIONS */
+typedef Region NPRegion;
+#else
+typedef void *NPRegion;
+#endif /* XP_MAC */
+
+#ifdef XP_MACOSX
+
+/* 
+ * NP_CGContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelCoreGraphics
+ * as its drawing model.
+ */
+
+typedef struct NP_CGContext
+{
+  CGContextRef context;
+#ifdef NP_NO_CARBON
+  NPNSWindow *window;
+#else
+  void *window; /* Can be either an NSWindow or a WindowRef depending on the event model */
+#endif
+} NP_CGContext;
+
+/* 
+ * NP_GLContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelOpenGL as its
+ * drawing model.
+ */
+
+typedef struct NP_GLContext
+{
+  CGLContextObj context;
+#ifdef NP_NO_CARBON
+  NPNSWindow *window;
+#else
+  void *window; /* Can be either an NSWindow or a WindowRef depending on the event model */
+#endif
+} NP_GLContext;
+
+#endif /* XP_MACOSX */
+
+#if defined(XP_MAC) || defined(XP_MACOSX)
+
+/*
+ *  Mac-specific structures and definitions.
+ */
+
+#ifndef NP_NO_QUICKDRAW
+
+/* 
+ * NP_Port is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelQuickDraw as its
+ * drawing model, or the plugin does not specify a drawing model.
+ *
+ * It is not recommended that new plugins use NPDrawingModelQuickDraw or NP_Port, as QuickDraw has been
+ * deprecated in Mac OS X 10.5.  CoreGraphics is the preferred drawing API.
+ *
+ * NP_Port is not available in 64-bit.
+ */
+
+typedef struct NP_Port
+{
+  CGrafPtr     port;        /* Grafport */
+  int32        portx;        /* position inside the topmost window */
+  int32        porty;
+} NP_Port;
+
+#endif /* NP_NO_QUICKDRAW */
+
+/*
+ *  Non-standard event types that can be passed to HandleEvent
+ */
+/* BEGIN GOOGLE MODIFICATIONS */
+#ifndef NP_NO_CARBON
+enum NPEventType {
+  NPEventType_GetFocusEvent = (osEvt + 16),
+  NPEventType_LoseFocusEvent,
+  NPEventType_AdjustCursorEvent,
+  NPEventType_MenuCommandEvent,
+  NPEventType_ClippingChangedEvent,
+  NPEventType_ScrollingBeginsEvent = 1000,
+  NPEventType_ScrollingEndsEvent
+};
+
+#ifdef OBSOLETE
+#define getFocusEvent     (osEvt + 16)
+#define loseFocusEvent    (osEvt + 17)
+#define adjustCursorEvent (osEvt + 18)
+#endif
+#endif /* NP_NO_CARBON */
+/* END GOOGLE MODIFICATIONS */
+
+#endif /* XP_MACOSX */
+
+/*
+ * Values for mode passed to NPP_New:
+ */
+#define NP_EMBED 1
+#define NP_FULL  2
+
+/*
+ * Values for stream type passed to NPP_NewStream:
+ */
+#define NP_NORMAL     1
+#define NP_SEEK       2
+#define NP_ASFILE     3
+#define NP_ASFILEONLY 4
+
+#define NP_MAXREADY	(((unsigned)(~0)<<1)>>1)
+
+#if !defined(__LP64__)
+#if defined(XP_MAC) || defined(XP_MACOSX)
+#pragma options align=reset
+#endif
+#endif /* __LP64__ */
+
+
+/*----------------------------------------------------------------------*/
+/*		     Error and Reason Code definitions			*/
+/*----------------------------------------------------------------------*/
+
+/*
+ * Values of type NPError:
+ */
+#define NPERR_BASE                         0
+#define NPERR_NO_ERROR                    (NPERR_BASE + 0)
+#define NPERR_GENERIC_ERROR               (NPERR_BASE + 1)
+#define NPERR_INVALID_INSTANCE_ERROR      (NPERR_BASE + 2)
+#define NPERR_INVALID_FUNCTABLE_ERROR     (NPERR_BASE + 3)
+#define NPERR_MODULE_LOAD_FAILED_ERROR    (NPERR_BASE + 4)
+#define NPERR_OUT_OF_MEMORY_ERROR         (NPERR_BASE + 5)
+#define NPERR_INVALID_PLUGIN_ERROR        (NPERR_BASE + 6)
+#define NPERR_INVALID_PLUGIN_DIR_ERROR    (NPERR_BASE + 7)
+#define NPERR_INCOMPATIBLE_VERSION_ERROR  (NPERR_BASE + 8)
+#define NPERR_INVALID_PARAM               (NPERR_BASE + 9)
+#define NPERR_INVALID_URL                 (NPERR_BASE + 10)
+#define NPERR_FILE_NOT_FOUND              (NPERR_BASE + 11)
+#define NPERR_NO_DATA                     (NPERR_BASE + 12)
+#define NPERR_STREAM_NOT_SEEKABLE         (NPERR_BASE + 13)
+
+/*
+ * Values of type NPReason:
+ */
+#define NPRES_BASE          0
+#define NPRES_DONE         (NPRES_BASE + 0)
+#define NPRES_NETWORK_ERR  (NPRES_BASE + 1)
+#define NPRES_USER_BREAK   (NPRES_BASE + 2)
+
+/*
+ * Don't use these obsolete error codes any more.
+ */
+#define NP_NOERR  NP_NOERR_is_obsolete_use_NPERR_NO_ERROR
+#define NP_EINVAL NP_EINVAL_is_obsolete_use_NPERR_GENERIC_ERROR
+#define NP_EABORT NP_EABORT_is_obsolete_use_NPRES_USER_BREAK
+
+/*
+ * Version feature information
+ */
+#define NPVERS_HAS_STREAMOUTPUT             8
+#define NPVERS_HAS_NOTIFICATION             9
+#define NPVERS_HAS_LIVECONNECT              9
+#define NPVERS_WIN16_HAS_LIVECONNECT        9
+#define NPVERS_68K_HAS_LIVECONNECT          11
+#define NPVERS_HAS_WINDOWLESS               11
+#define NPVERS_HAS_XPCONNECT_SCRIPTING      13
+#define NPVERS_HAS_NPRUNTIME_SCRIPTING      14
+#define NPVERS_HAS_FORM_VALUES              15
+#define NPVERS_HAS_POPUPS_ENABLED_STATE     16
+#define NPVERS_HAS_RESPONSE_HEADERS         17
+#define NPVERS_HAS_NPOBJECT_ENUM            18
+#define NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL 19
+#define NPVERS_HAS_ALL_NETWORK_STREAMS      20
+#define NPVERS_HAS_URL_AND_AUTH_INFO        21
+#define NPVERS_HAS_PRIVATE_MODE             22
+#define NPVERS_MACOSX_HAS_EVENT_MODELS      23
+#define NPVERS_HAS_CANCEL_SRC_STREAM        24
+
+/*----------------------------------------------------------------------*/
+/*                        Function Prototypes                           */
+/*----------------------------------------------------------------------*/
+
+#if defined(_WINDOWS) && !defined(WIN32)
+#define NP_LOADDS  _loadds
+#else
+#define NP_LOADDS
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * NPP_* functions are provided by the plugin and called by the navigator.
+ */
+
+#ifdef XP_UNIX
+char* NPP_GetMIMEDescription(void);
+#endif /* XP_UNIX */
+
+NPError     NPP_Initialize(void);
+void        NPP_Shutdown(void);
+NPError     NP_LOADDS    NPP_New(NPMIMEType pluginType, NPP instance,
+                                 uint16 mode, int16 argc, char* argn[],
+                                 char* argv[], NPSavedData* saved);
+NPError     NP_LOADDS    NPP_Destroy(NPP instance, NPSavedData** save);
+NPError     NP_LOADDS    NPP_SetWindow(NPP instance, NPWindow* window);
+NPError     NP_LOADDS    NPP_NewStream(NPP instance, NPMIMEType type,
+                                       NPStream* stream, NPBool seekable,
+                                       uint16* stype);
+NPError     NP_LOADDS    NPP_DestroyStream(NPP instance, NPStream* stream,
+                                           NPReason reason);
+int32        NP_LOADDS    NPP_WriteReady(NPP instance, NPStream* stream);
+int32        NP_LOADDS    NPP_Write(NPP instance, NPStream* stream, int32 offset,
+                                    int32 len, void* buffer);
+void        NP_LOADDS    NPP_StreamAsFile(NPP instance, NPStream* stream,
+                                          const char* fname);
+void        NP_LOADDS    NPP_Print(NPP instance, NPPrint* platformPrint);
+int16            NPP_HandleEvent(NPP instance, void* event);
+void        NP_LOADDS    NPP_URLNotify(NPP instance, const char* url,
+                                       NPReason reason, void* notifyData);
+jref        NP_LOADDS            NPP_GetJavaClass(void);
+NPError     NPP_GetValue(NPP instance, NPPVariable variable,
+                         void *value);
+NPError     NPP_SetValue(NPP instance, NPNVariable variable,
+                         void *value);
+
+/*
+ * NPN_* functions are provided by the navigator and called by the plugin.
+ */
+
+void        NPN_Version(int* plugin_major, int* plugin_minor,
+                        int* netscape_major, int* netscape_minor);
+NPError     NPN_GetURLNotify(NPP instance, const char* url,
+                             const char* target, void* notifyData);
+NPError     NPN_GetURL(NPP instance, const char* url,
+                       const char* target);
+NPError     NPN_PostURLNotify(NPP instance, const char* url,
+                              const char* target, uint32 len,
+                              const char* buf, NPBool file,
+                              void* notifyData);
+NPError     NPN_PostURL(NPP instance, const char* url,
+                        const char* target, uint32 len,
+                        const char* buf, NPBool file);
+NPError     NPN_RequestRead(NPStream* stream, NPByteRange* rangeList);
+NPError     NPN_NewStream(NPP instance, NPMIMEType type,
+                          const char* target, NPStream** stream);
+int32        NPN_Write(NPP instance, NPStream* stream, int32 len,
+                       void* buffer);
+NPError     NPN_DestroyStream(NPP instance, NPStream* stream,
+                              NPReason reason);
+void        NPN_Status(NPP instance, const char* message);
+const char*    NPN_UserAgent(NPP instance);
+void*        NPN_MemAlloc(uint32 size);
+void        NPN_MemFree(void* ptr);
+uint32        NPN_MemFlush(uint32 size);
+void        NPN_ReloadPlugins(NPBool reloadPages);
+JRIEnv*     NPN_GetJavaEnv(void);
+jref        NPN_GetJavaPeer(NPP instance);
+NPError     NPN_GetValue(NPP instance, NPNVariable variable,
+                         void *value);
+NPError     NPN_SetValue(NPP instance, NPPVariable variable,
+                         void *value);
+void        NPN_InvalidateRect(NPP instance, NPRect *invalidRect);
+void        NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion);
+void        NPN_ForceRedraw(NPP instance);
+void        NPN_PushPopupsEnabledState(NPP instance, NPBool enabled);
+void        NPN_PopPopupsEnabledState(NPP instance);
+void        NPN_PluginThreadAsyncCall(NPP instance, void (*func) (void *), void *userData);
+NPError     NPN_GetValueForURL(NPP instance, NPNURLVariable variable, const char* url, char** value, uint32* len);
+NPError     NPN_SetValueForURL(NPP instance, NPNURLVariable variable, const char* url, const char* value, uint32 len);
+NPError     NPN_GetAuthenticationInfo(NPP instance, const char* protocol, const char* host, int32 port, const char* scheme, const char *realm, char** username, uint32* ulen, char** password, uint32* plen);
+uint32      NPN_ScheduleTimer(NPP instance, uint32 interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32 timerID));
+void        NPN_UnscheduleTimer(NPP instance, uint32 timerID);
+NPError     NPN_PopUpContextMenu(NPP instance, NPMenu* menu);
+NPBool      NPN_ConvertPoint(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace);
+
+#ifdef __cplusplus
+}  /* end extern "C" */
+#endif
+
+#endif /* _NPAPI_H_ */
diff --git a/third_party/chrome/third_party/npapi/bindings/npapi_extensions.h b/third_party/chrome/third_party/npapi/bindings/npapi_extensions.h
new file mode 100644
index 0000000..f8c05f4
--- /dev/null
+++ b/third_party/chrome/third_party/npapi/bindings/npapi_extensions.h
@@ -0,0 +1,646 @@
+/* Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef _NP_EXTENSIONS_H_
+#define _NP_EXTENSIONS_H_
+
+// Use the shorter include path here so that this file can be used in non-
+// Chromium projects, such as the Native Client SDK.
+#include "npapi.h"
+
+/*
+ * A fake "enum" value for getting browser-implemented Pepper extensions.
+ * The variable returns a pointer to an NPNExtensions structure. */
+#define NPNVPepperExtensions ((NPNVariable) 4000)
+
+/*
+ * A fake "enum" value for getting plugin-implemented Pepper extensions.
+ * The variable returns a pointer to an NPPExtensions structure. */
+#define NPPVPepperExtensions ((NPPVariable) 4001)
+
+typedef void NPDeviceConfig;
+typedef void NPDeviceContext;
+typedef void NPUserData;
+
+/* unique id for each device interface */
+typedef int32 NPDeviceID;
+
+typedef struct _NPPoint {
+  uint16 x;
+  uint16 y;
+} NPPoint;
+
+typedef enum {
+  NPThemeItemScrollbarDownArrow       = 0,
+  NPThemeItemScrollbarLeftArrow       = 1,
+  NPThemeItemScrollbarRightArrow      = 2,
+  NPThemeItemScrollbarUpArrow         = 3,
+  NPThemeItemScrollbarHorizontalThumb = 4,
+  NPThemeItemScrollbarVerticalThumb   = 5,
+  NPThemeItemScrollbarHoriztonalTrack = 6,
+  NPThemeItemScrollbarVerticalTrack   = 7
+} NPThemeItem;
+
+typedef enum {
+  NPThemeStateDisabled = 0,
+  // Mouse is over this item.
+  NPThemeStateHot      = 1,
+  // Mouse is over another part of this component.  This is only used on Windows
+  // Vista and above.  The plugin should pass it in, and the host will convert
+  // it to NPThemeStateNormal if on other platforms or on Windows XP.
+  NPThemeStateHover    = 2,
+  NPThemeStateNormal   = 3,
+  NPThemeStatePressed  = 4
+} NPThemeState;
+
+typedef struct _NPThemeParams {
+  NPThemeItem item;
+  NPThemeState state;
+  NPRect location;
+  // Used for scroll bar tracks, needed for classic theme in Windows which draws
+  // a checkered pattern.
+  NPPoint align;
+} NPThemeParams;
+
+typedef struct _NPDeviceBuffer {
+  void* ptr;
+  size_t size;
+} NPDeviceBuffer;
+
+/* completion callback for flush device */
+typedef void (*NPDeviceFlushContextCallbackPtr)(
+    NPP instance,
+    NPDeviceContext* context,
+    NPError err,
+    NPUserData* userData);
+
+/* query single capabilities of device */
+typedef NPError (
+    *NPDeviceQueryCapabilityPtr)(NPP instance,
+    int32 capability,
+    int32 *value);
+/* query config (configuration == a set of capabilities) */
+typedef NPError (
+    *NPDeviceQueryConfigPtr)(NPP instance,
+    const NPDeviceConfig* request,
+    NPDeviceConfig* obtain);
+/* device initialization */
+typedef NPError (*NPDeviceInitializeContextPtr)(
+    NPP instance,
+    const NPDeviceConfig* config,
+    NPDeviceContext* context);
+/* peek at device state */
+typedef NPError (*NPDeviceGetStateContextPtr) (
+    NPP instance,
+    NPDeviceContext* context,
+    int32 state,
+    intptr_t* value);
+/* poke device state */
+typedef NPError (*NPDeviceSetStateContextPtr) (
+    NPP instance,
+    NPDeviceContext* context,
+    int32 state,
+    intptr_t value);
+/* flush context, if callback, userData are NULL */
+/* this becomes a blocking call */
+typedef NPError (*NPDeviceFlushContextPtr)(
+    NPP instance,
+    NPDeviceContext* context,
+    NPDeviceFlushContextCallbackPtr callback,
+    void* userData);
+/* destroy device context.  Application responsible for */
+/* freeing context, if applicable */
+typedef NPError (*NPDeviceDestroyContextPtr)(
+    NPP instance,
+    NPDeviceContext* context);
+/* Create a buffer associated with a particular context. The usage of the */
+/* buffer is device specific. The lifetime of the buffer is scoped with the */
+/* lifetime of the context. */
+typedef NPError (*NPDeviceCreateBufferPtr)(
+    NPP instance,
+    NPDeviceContext* context,
+    size_t size,
+    int32* id);
+/* Destroy a buffer associated with a particular context. */
+typedef NPError (*NPDeviceDestroyBufferPtr)(
+    NPP instance,
+    NPDeviceContext* context,
+    int32 id);
+/* Map a buffer id to its address. */
+typedef NPError (*NPDeviceMapBufferPtr)(
+    NPP instance,
+    NPDeviceContext* context,
+    int32 id,
+    NPDeviceBuffer* buffer);
+/* Gets the size of the given theme component.  For variable sized items like */
+/* vertical scrollbar tracks, the width will be the required width of the */
+/* track while the height will be the minimum height. */
+typedef NPError (*NPDeviceThemeGetSize)(
+    NPP instance,
+    NPThemeItem item,
+    int* width,
+    int* height);
+/* Draw a themed item (i.e. scrollbar arrow). */
+typedef NPError (*NPDeviceThemePaint)(
+    NPP instance,
+    NPDeviceContext* context,
+    NPThemeParams* params);
+
+
+/* forward decl typdef structs */
+typedef struct NPDevice NPDevice;
+typedef struct NPNExtensions NPNExtensions;
+
+// DEPRECATED: this typedef is just for the NaCl code until they switch to NPNExtensions.
+// PLEASE REMOVE THIS WHEN THE NACL CODE IS UPDATED.
+typedef struct NPNExtensions NPExtensions;
+
+/* generic device interface */
+struct NPDevice {
+  NPDeviceQueryCapabilityPtr queryCapability;
+  NPDeviceQueryConfigPtr queryConfig;
+  NPDeviceInitializeContextPtr initializeContext;
+  NPDeviceSetStateContextPtr setStateContext;
+  NPDeviceGetStateContextPtr getStateContext;
+  NPDeviceFlushContextPtr flushContext;
+  NPDeviceDestroyContextPtr destroyContext;
+  NPDeviceCreateBufferPtr createBuffer;
+  NPDeviceDestroyBufferPtr destroyBuffer;
+  NPDeviceMapBufferPtr mapBuffer;
+  NPDeviceThemeGetSize themeGetSize;
+  NPDeviceThemePaint themePaint;
+};
+
+/* returns NULL if deviceID unavailable / unrecognized */
+typedef NPDevice* (*NPAcquireDevicePtr)(
+    NPP instance,
+    NPDeviceID device);
+
+/* Copy UTF-8 string into clipboard */
+typedef void (*NPCopyTextToClipboardPtr)(
+    NPP instance,
+    const char* content);
+
+/* Updates the number of find results for the current search term.  If
+ * there are no matches 0 should be passed in.  Only when the plugin has
+ * finished searching should it pass in the final count with finalResult set to
+ * true. */
+typedef void (*NPNumberOfFindResultsChangedPtr)(
+    NPP instance,
+    int total,
+    bool finalResult);
+
+ /* Updates the index of the currently selected search item. */
+typedef void (*NPSelectedFindResultChangedPtr)(
+    NPP instance,
+    int index);
+
+/* Supports opening files anywhere on the system after prompting the user to
+ * pick one.
+ *
+ * This API is asynchronous. It will return immediately and the user will be
+ * prompted in parallel to pick a file. The plugin may continue to receive
+ * events while the open file dialog is up, and may continue to paint. Plugins
+ * may want to ignore input events between the call and the callback to avoid
+ * reentrant behavior. If the return value is not NPERR_NO_ERROR, the callback
+ * will NOT be executed.
+ *
+ * It is an error to call BrowseForFile before a previous call has executed
+ * the callback.
+ *
+ * Setting the flags to "Open" requires that the file exist to allow picking.
+ * Setting the flags to "Save" allows selecting nonexistant files (which will
+ * then be created), and will prompt the user if they want to overwrite an
+ * existing file if it exists.
+ *
+ * The plugin may specify a comma-separated list of possible mime types in
+ * the "extensions" parameter. If no extensions are specified, the dialog box
+ * will default to allowing all extensions. The first extension in the list
+ * will be the default.
+ *
+ * TODO(brettw) On Windows the extensions traditionally include a text
+ * description with the extension in the popup, do we want to allow this?
+ * We should probably also allow the ability to put "All files" in the
+ * list on Windows.
+ *
+ * Once the user has picked a file or has canceled the dialog box, the given
+ * callback will be called with the results of the operation and the passed in
+ * "user data" pointer. If the user successfully picked a file, the filename
+ * will be non-NULL and will contain a pointer to an array of strings, one for
+ * each file picked (the first file will be file_paths[0]). This buffer will
+ * become invalid as soon as the call completes, so it is the plugin's
+ * responsibility to copy the filename(sp if it needs future access to them.
+ * A NULL file_paths in the callback means the user canceled the dialog box.
+ *
+ * The filename will be in UTF-8. It may not actually correspond to the actual
+ * file on disk on a Linux system, because we'll do our best to convert it from
+ * the filesystem's locale to UTF-8. Instead, the string will be appropriate for
+ * displaying to the user which file they picked.
+ * */
+typedef enum {
+  NPChooseFile_Open = 1,
+  NPChooseFile_OpenMultiple = 2,
+  NPChooseFile_Save = 3,
+} NPChooseFileMode;
+typedef void (*NPChooseFileCallback)(const char** filePaths,
+                                     uint32 pathCount,
+                                     void* userData);
+typedef NPError (*NPChooseFilePtr)(
+    NPP instance,
+    const char* mimeTypes,
+    NPChooseFileMode mode,
+    NPChooseFileCallback callback,
+    void* userData);
+
+/* Pepper extensions */
+struct NPNExtensions {
+  /* Device interface acquisition */
+  NPAcquireDevicePtr acquireDevice;
+  /* Clipboard functionality */
+  NPCopyTextToClipboardPtr copyTextToClipboard;
+  /* Find */
+  NPNumberOfFindResultsChangedPtr numberOfFindResultsChanged;
+  NPSelectedFindResultChangedPtr selectedFindResultChanged;
+  /* File I/O extensions */
+  NPChooseFilePtr chooseFile;
+};
+
+/* Events -------------------------------------------------------------------*/
+
+typedef enum {
+  NPMouseButton_None    = -1,
+  NPMouseButton_Left    = 0,
+  NPMouseButton_Middle  = 1,
+  NPMouseButton_Right   = 2
+} NPMouseButtons;
+
+typedef enum {
+  NPEventType_Undefined   = -1,
+  NPEventType_MouseDown   = 0,
+  NPEventType_MouseUp     = 1,
+  NPEventType_MouseMove   = 2,
+  NPEventType_MouseEnter  = 3,
+  NPEventType_MouseLeave  = 4,
+  NPEventType_MouseWheel  = 5,
+  NPEventType_RawKeyDown  = 6,
+  NPEventType_KeyDown     = 7,
+  NPEventType_KeyUp       = 8,
+  NPEventType_Char        = 9,
+  NPEventType_Minimize    = 10,
+  NPEventType_Focus       = 11,
+  NPEventType_Device      = 12
+} NPEventTypes;
+
+typedef enum {
+  NPEventModifier_ShiftKey         = 1 << 0,
+  NPEventModifier_ControlKey       = 1 << 1,
+  NPEventModifier_AltKey           = 1 << 2,
+  NPEventModifier_MetaKey          = 1 << 3,
+  NPEventModifier_IsKeyPad         = 1 << 4,
+  NPEventModifier_IsAutoRepeat     = 1 << 5,
+  NPEventModifier_LeftButtonDown   = 1 << 6,
+  NPEventModifier_MiddleButtonDown = 1 << 7,
+  NPEventModifier_RightButtonDown  = 1 << 8
+} NPEventModifiers;
+
+typedef struct _NPKeyEvent
+{
+  uint32 modifier;
+  uint32 normalizedKeyCode;
+} NPKeyEvent;
+
+typedef struct _NPCharacterEvent
+{
+  uint32 modifier;
+  uint16 text[4];
+  uint16 unmodifiedText[4];
+} NPCharacterEvent;
+
+typedef struct _NPMouseEvent
+{
+  uint32 modifier;
+  int32 button;
+  int32 x;
+  int32 y;
+  int32 clickCount;
+} NPMouseEvent;
+
+typedef struct _NPMouseWheelEvent
+{
+  uint32 modifier;
+  float deltaX;
+  float deltaY;
+  float wheelTicksX;
+  float wheelTicksY;
+  uint32 scrollByPage;
+} NPMouseWheelEvent;
+
+typedef struct _NPDeviceEvent {
+  uint32 device_uid;
+  uint32 subtype;
+  /* uint8 generic[0]; */
+} NPDeviceEvent;
+
+typedef struct _NPMinimizeEvent {
+  int32 value;
+} NPMinimizeEvent;
+
+typedef struct _NPFocusEvent {
+  int32 value;
+} NPFocusEvent;
+
+typedef struct _NPPepperEvent
+{
+  uint32 size;
+  int32 type;
+  double timeStampSeconds;
+  union {
+    NPKeyEvent key;
+    NPCharacterEvent character;
+    NPMouseEvent mouse;
+    NPMouseWheelEvent wheel;
+    NPMinimizeEvent minimize;
+    NPFocusEvent focus;
+    NPDeviceEvent device;
+  } u;
+} NPPepperEvent;
+
+/* 2D -----------------------------------------------------------------------*/
+
+#define NPPepper2DDevice 1
+
+typedef struct _NPDeviceContext2DConfig {
+} NPDeviceContext2DConfig;
+
+typedef struct _NPDeviceContext2D
+{
+  /* Internal value used by the browser to identify this device. */
+  void* reserved;
+
+  /* A pointer to the pixel data. This data is 8-bit values in BGRA order in
+   * memory. Each row will start |stride| bytes after the previous one.
+   *
+   * THIS DATA USES PREMULTIPLIED ALPHA. This means that each color channel has
+   * been multiplied with the corresponding alpha, which makes compositing
+   * easier. If any color channels have a value greater than the alpha value,
+   * you'll likely get crazy colors and weird artifacts. */
+  void* region;
+
+  /* Length of each row of pixels in bytes. This may be larger than width * 4
+   * if there is padding at the end of each row to help with alignment. */
+  int32 stride;
+
+  /* The dirty region that the plugin has painted into the buffer. This
+   * will be initialized to the size of the plugin image in
+   * initializeContextPtr. The plugin can change the values to only
+   * update portions of the image. */
+  struct {
+    int32 left;
+    int32 top;
+    int32 right;
+    int32 bottom;
+  } dirty;
+} NPDeviceContext2D;
+
+/* 3D -----------------------------------------------------------------------*/
+
+#define NPPepper3DDevice 2
+
+typedef struct _NPDeviceContext3DConfig {
+  int32 commandBufferSize;
+} NPDeviceContext3DConfig;
+
+typedef enum _NPDeviceContext3DError {
+  // No error has ocurred.
+  NPDeviceContext3DError_NoError,
+
+  // The size of a command was invalid.
+  NPDeviceContext3DError_InvalidSize,
+
+  // An offset was out of bounds.
+  NPDeviceContext3DError_OutOfBounds,
+
+  // A command was not recognized.
+  NPDeviceContext3DError_UnknownCommand,
+
+  // The arguments to a command were invalid.
+  NPDeviceContext3DError_InvalidArguments,
+
+  // The 3D context was lost, for example due to a power management event. The
+  // context must be destroyed and a new one created.
+  NPDeviceContext3DError_LostContext,
+
+  // Any other error.
+  NPDeviceContext3DError_GenericError
+} NPDeviceContext3DError;
+
+typedef struct _NPDeviceContext3D NPDeviceContext3D;
+
+typedef void (*NPDeviceContext3DRepaintPtr)(NPP npp,
+                                            NPDeviceContext3D* context);
+
+typedef struct _NPDeviceContext3D
+{
+  void* reserved;
+
+  // If true, then a flush will only complete once the get offset has advanced
+  // on the GPU thread. If false, then the get offset might have changed but
+  // the GPU thread will respond as quickly as possible without guaranteeing
+  // having made any progress in executing pending commands. Set to true
+  // to ensure that progress is made or when flushing in a loop waiting for the
+  // GPU to reach a certain state, for example in advancing beyond a particular
+  // token. Set to false when flushing to query the current state, for example
+  // whether an error has occurred.
+  bool waitForProgress;
+
+  // Buffer in which commands are stored.
+  void* commandBuffer;
+  int32 commandBufferSize;
+
+  // Offset in command buffer reader has reached. Synchronized on flush.
+  int32 getOffset;
+
+  // Offset in command buffer writer has reached. Synchronized on flush.
+  int32 putOffset;
+
+  // Last processed token. Synchronized on flush.
+  int32 token;
+
+  // Callback invoked on the main thread when the context must be repainted.
+  // TODO(apatrick): move this out of the context struct like the rest of the
+  // fields.
+  NPDeviceContext3DRepaintPtr repaintCallback;
+
+  // Error status. Synchronized on flush.
+  NPDeviceContext3DError error;
+} NPDeviceContext3D;
+
+/* Audio --------------------------------------------------------------------*/
+
+#define NPPepperAudioDevice 3
+
+/* min & max sample frame count */
+typedef enum {
+  NPAudioMinSampleFrameCount = 64,
+  NPAudioMaxSampleFrameCount = 32768
+} NPAudioSampleFrameCounts;
+
+/* supported sample rates */
+typedef enum {
+  NPAudioSampleRate44100Hz = 44100,
+  NPAudioSampleRate48000Hz = 48000,
+  NPAudioSampleRate96000Hz = 96000
+} NPAudioSampleRates;
+
+/* supported sample formats */
+typedef enum {
+  NPAudioSampleTypeInt16   = 0,
+  NPAudioSampleTypeFloat32 = 1
+} NPAudioSampleTypes;
+
+/* supported channel layouts */
+/* there is code that depends on these being the actual number of channels */
+typedef enum {
+  NPAudioChannelNone     = 0,
+  NPAudioChannelMono     = 1,
+  NPAudioChannelStereo   = 2,
+  NPAudioChannelThree    = 3,
+  NPAudioChannelFour     = 4,
+  NPAudioChannelFive     = 5,
+  NPAudioChannelFiveOne  = 6,
+  NPAudioChannelSeven    = 7,
+  NPAudioChannelSevenOne = 8
+} NPAudioChannels;
+
+/* audio context states */
+typedef enum {
+  NPAudioContextStateCallback = 0,
+  NPAudioContextStateUnderrunCounter = 1
+} NPAudioContextStates;
+
+/* audio context state values */
+typedef enum {
+  NPAudioCallbackStop = 0,
+  NPAudioCallbackStart = 1
+} NPAudioContextStateValues;
+
+/* audio query capabilities */
+typedef enum {
+  NPAudioCapabilitySampleRate              = 0,
+  NPAudioCapabilitySampleType              = 1,
+  NPAudioCapabilitySampleFrameCount        = 2,
+  NPAudioCapabilitySampleFrameCount44100Hz = 3,
+  NPAudioCapabilitySampleFrameCount48000Hz = 4,
+  NPAudioCapabilitySampleFrameCount96000Hz = 5,
+  NPAudioCapabilityOutputChannelMap        = 6,
+  NPAudioCapabilityInputChannelMap         = 7
+} NPAudioCapabilities;
+
+typedef struct _NPDeviceContextAudio NPDeviceContextAudio;
+
+/* user supplied callback function */
+typedef void (*NPAudioCallback)(NPDeviceContextAudio *context);
+
+typedef struct _NPDeviceContextAudioConfig {
+  int32 sampleRate;
+  int32 sampleType;
+  int32 outputChannelMap;
+  int32 inputChannelMap;
+  int32 sampleFrameCount;
+  uint32 startThread;
+  uint32 flags;
+  NPAudioCallback callback;
+  void *userData;
+} NPDeviceContextAudioConfig;
+
+struct _NPDeviceContextAudio {
+  NPDeviceContextAudioConfig config;
+  void *outBuffer;
+  void *inBuffer;
+  void *reserved;
+};
+
+/* Printing related APIs ---------------------------------------------------*/
+
+/* Being a print operation. Returns the total number of pages to print at the
+ * given printableArea size and DPI. printableArea is in points (a point is 1/72
+ * of an inch). The plugin is expected to remember the values of printableArea
+ * and printerDPI for use in subsequent print interface calls. These values
+ * should be cleared in printEnd. */
+typedef NPError (*NPPPrintBeginPtr) (
+    NPP instance,
+    NPRect* printableArea,
+    int32 printerDPI,
+    int32* numPages);
+/* Returns the required raster dimensions for the given page. */
+typedef NPError (*NPPGetRasterDimensionsPtr) (
+    NPP instance,
+    int32 pageNumber,
+    int32* widthInPixels,
+    int32* heightInPixels);
+/* Prints the specified page This allows the plugin to print a raster output. */
+typedef NPError (*NPPPrintPageRasterPtr) (
+    NPP instance,
+    int32 pageNumber,
+    NPDeviceContext2D* printSurface);
+/* Ends the print operation */
+typedef NPError (*NPPPrintEndPtr) (NPP instance);
+
+/* TODO(sanjeevr) : Provide a vector interface for printing. We need to decide
+ * on a vector format that can support embedded fonts. A vector format will
+ * greatly reduce the size of the required output buffer. */
+
+typedef struct _NPPPrintExtensions {
+  NPPPrintBeginPtr printBegin;
+  NPPGetRasterDimensionsPtr getRasterDimensions;
+  NPPPrintPageRasterPtr printPageRaster;
+  NPPPrintEndPtr printEnd;
+} NPPPrintExtensions;
+
+/* Returns NULL if the plugin does not support print extensions */
+typedef NPPPrintExtensions* (*NPPGetPrintExtensionsPtr)(NPP instance);
+
+/* Find ---------------------------------------------------------------------*/
+
+/* Finds the given UTF-8 text starting at the current selection.  The number of
+ * results will be updated asynchronously via numberOfFindResultsChanged.  Note
+ * that multiple StartFind calls can happen before StopFind is called in the
+ * case of the search term changing. */
+typedef NPError (*NPPStartFindPtr) (
+    NPP instance,
+    const char* text,
+    bool caseSensitive);
+
+/* Go to the next/previous result. */
+typedef NPError (*NPPSelectFindResultPtr) (
+    NPP instance,
+    bool forward);
+
+/* Tells the plugin that the find operation has stopped, so it should clear
+ * any highlighting. */
+typedef NPError (*NPPStopFindPtr) (
+    NPP instance);
+
+typedef struct _NPPFindExtensions {
+  NPPStartFindPtr startFind;
+  NPPSelectFindResultPtr selectFindResult;
+  NPPStopFindPtr stopFind;
+} NPPFindExtensions;
+
+/* Returns NULL if the plugin does not support find extensions. */
+typedef NPPFindExtensions* (*NPPGetFindExtensionsPtr)(NPP instance);
+
+/* Zooms plugins.  0 means reset, -1 means zoom out, and +1 means zoom in. */
+typedef NPError (*NPPZoomPtr) (
+    NPP instance,
+    int factor);
+
+typedef struct _NPPExtensions {
+  NPPGetPrintExtensionsPtr getPrintExtensions;
+  NPPGetFindExtensionsPtr getFindExtensions;
+  NPPZoomPtr zoom;
+} NPPExtensions;
+
+#endif  /* _NP_EXTENSIONS_H_ */
diff --git a/third_party/chrome/third_party/npapi/bindings/npapi_extensions_private.h b/third_party/chrome/third_party/npapi/bindings/npapi_extensions_private.h
new file mode 100644
index 0000000..3b0a458
--- /dev/null
+++ b/third_party/chrome/third_party/npapi/bindings/npapi_extensions_private.h
@@ -0,0 +1,27 @@
+/* Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef _NP_EXTENSIONS_PRIVATE_H_
+#define _NP_EXTENSIONS_PRIVATE_H_
+
+#include "third_party/npapi/bindings/npapi.h"
+
+// Some reserved GetStateContext/SetStateContext selectors.
+typedef enum {
+  NPExtensionsReservedStateSharedMemory = 66536,
+  // Used by the Device2D and Audio devices to return a pointer to the
+  // structure used to implement the shared memory buffer for the device.
+  NPExtensionsReservedStateSharedMemorySize = 66537,
+  // Used by the Audio device to return a pointer to the
+  // structure used to implement the shared memory buffer for the device.
+  NPExtensionsReservedStateSyncChannel = 66538,
+  // Used by the Audio device to return a pointer to the
+  // structure used to implement the synchronization channel for the device.
+  NPExtensionsReservedStateSharedMemoryChecksum = 66539
+  // Used by the Device2D to return the CRC32 checksum of the content
+  // stored in the shared memory buffer for the device.
+} NPExtensionsReservedStates;
+
+#endif  /* _NP_EXTENSIONS_PRIVATE_H_ */
diff --git a/third_party/chrome/third_party/npapi/bindings/npapi_x11.h b/third_party/chrome/third_party/npapi/bindings/npapi_x11.h
new file mode 100644
index 0000000..c586419
--- /dev/null
+++ b/third_party/chrome/third_party/npapi/bindings/npapi_x11.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef THIRD_PARTY_NPAPI_BINDINGS_NPAPI_X11_H_
+#define THIRD_PARTY_NPAPI_BINDINGS_NPAPI_X11_H_
+
+// This file was split off the original npapi.h, to avoid including intrusive X
+// headers unless necessary.
+
+#include "third_party/npapi/bindings/npapi.h"
+
+#ifdef XP_UNIX
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+typedef struct _NPSetWindowCallbackStruct
+{
+  int32        type;
+#ifdef MOZ_X11
+  Display*     display;
+  Visual*      visual;
+  Colormap     colormap;
+  unsigned int depth;
+#endif
+} NPSetWindowCallbackStruct;
+#endif
+
+#endif  // THIRD_PARTY_NPAPI_BINDINGS_NPAPI_X11_H_
diff --git a/third_party/chrome/third_party/npapi/bindings/nphostapi.h b/third_party/chrome/third_party/npapi/bindings/nphostapi.h
new file mode 100644
index 0000000..69bad70
--- /dev/null
+++ b/third_party/chrome/third_party/npapi/bindings/nphostapi.h
@@ -0,0 +1,325 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO:  Did not implement JRIGlobalRef function yet.  Not sure if this is used?
+
+#ifndef _NPHOSTAPI_H_
+#define _NPHOSTAPI_H_
+
+#include "base/port.h"
+#include "third_party/npapi/bindings/npapi.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+#include "third_party/npapi/bindings/npruntime.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//
+// NPAPI NPP Function Pointers
+//
+typedef NPError      (*NPP_NewProcPtr)(NPMIMEType pluginType,
+                         NPP instance,
+                         uint16 mode,
+                         int16 argc,
+                         char* argn[],
+                         char* argv[],
+                         NPSavedData* saved);
+typedef NPError      (*NPP_DestroyProcPtr)(NPP instance,
+                         NPSavedData** save);
+typedef NPError      (*NPP_SetWindowProcPtr)(NPP instance,
+                         NPWindow* window);
+typedef NPError      (*NPP_NewStreamProcPtr)(NPP instance,
+                         NPMIMEType type,
+                         NPStream* stream,
+                         NPBool seekable,
+                         uint16* stype);
+typedef NPError      (*NPP_DestroyStreamProcPtr)(NPP instance,
+                         NPStream* stream,
+                         NPReason reason);
+typedef int32        (*NPP_WriteReadyProcPtr)(NPP instance,
+                         NPStream* stream);
+typedef int32        (*NPP_WriteProcPtr)(NPP instance,
+                         NPStream* stream,
+                         int32 offset,
+                         int32 len,
+                         void* buffer);
+typedef void         (*NPP_StreamAsFileProcPtr)(NPP instance,
+                         NPStream* stream,
+                         const char* fname);
+typedef void         (*NPP_PrintProcPtr)(NPP instance,
+                         NPPrint* platformPrint);
+typedef int16        (*NPP_HandleEventProcPtr)(NPP instance,
+                         void* event);
+typedef void         (*NPP_URLNotifyProcPtr)(NPP instance,
+                         const char* url,
+                         NPReason reason,
+                         void* notifyData);
+typedef void* JRIGlobalRef; //not using this right now
+typedef NPError      (*NPP_GetValueProcPtr)(NPP instance,
+                         NPPVariable variable,
+                         void *ret_alue);
+typedef NPError      (*NPP_SetValueProcPtr)(NPP instance,
+                         NPNVariable variable,
+                         void *ret_alue);
+
+//
+// NPAPI NPN Function Pointers
+//
+typedef NPError      (*NPN_GetURLProcPtr)(NPP instance,
+                         const char* URL,
+                         const char* window);
+typedef NPError      (*NPN_PostURLProcPtr)(NPP instance,
+                         const char* URL,
+                         const char* window,
+                         uint32 len,
+                         const char* buf,
+                         NPBool file);
+typedef NPError      (*NPN_RequestReadProcPtr)(NPStream* stream,
+                         NPByteRange* rangeList);
+typedef NPError      (*NPN_NewStreamProcPtr)(NPP instance,
+                         NPMIMEType type,
+                         const char* window,
+                         NPStream** stream);
+typedef int32        (*NPN_WriteProcPtr)(NPP instance,
+                         NPStream* stream,
+                         int32 len,
+                         void* buffer);
+typedef NPError      (*NPN_DestroyStreamProcPtr)(NPP instance,
+                         NPStream* stream,
+                         NPReason reason);
+typedef void         (*NPN_StatusProcPtr)(NPP instance,
+                         const char* message);
+typedef const char*  (*NPN_UserAgentProcPtr)(NPP instance);
+typedef void*        (*NPN_MemAllocProcPtr)(uint32 size);
+typedef void         (*NPN_MemFreeProcPtr)(void* ptr);
+typedef uint32       (*NPN_MemFlushProcPtr)(uint32 size);
+typedef void         (*NPN_ReloadPluginsProcPtr)(NPBool reloadPages);
+
+typedef void*        (*NPN_GetJavaEnvProcPtr)(void);
+typedef void*        (*NPN_GetJavaPeerProcPtr)(NPP instance);
+
+typedef NPError      (*NPN_GetURLNotifyProcPtr)(NPP instance,
+                         const char* URL,
+                         const char* window,
+                         void* notifyData);
+typedef NPError      (*NPN_PostURLNotifyProcPtr)(NPP instance,
+                         const char* URL,
+                         const char* window,
+                         uint32 len,
+                         const char* buf,
+                         NPBool file,
+                         void* notifyData);
+typedef NPError      (*NPN_GetValueProcPtr)(NPP instance,
+                         NPNVariable variable,
+                         void *ret_value);
+typedef NPError      (*NPN_SetValueProcPtr)(NPP instance,
+                         NPPVariable variable,
+                         void *value);
+typedef void         (*NPN_InvalidateRectProcPtr)(NPP instance,
+                         NPRect *rect);
+typedef void         (*NPN_InvalidateRegionProcPtr)(NPP instance,
+                         NPRegion region);
+typedef void         (*NPN_ForceRedrawProcPtr)(NPP instance);
+
+typedef void         (*NPN_ReleaseVariantValueProcPtr) (NPVariant *variant);
+
+typedef NPIdentifier (*NPN_GetStringIdentifierProcPtr) (const NPUTF8 *name);
+typedef void         (*NPN_GetStringIdentifiersProcPtr) (const NPUTF8 **names,
+                         int32_t nameCount,
+                         NPIdentifier *identifiers);
+typedef NPIdentifier (*NPN_GetIntIdentifierProcPtr) (int32_t intid);
+typedef int32_t      (*NPN_IntFromIdentifierProcPtr) (NPIdentifier identifier);
+typedef bool         (*NPN_IdentifierIsStringProcPtr) (NPIdentifier identifier);
+typedef NPUTF8 *     (*NPN_UTF8FromIdentifierProcPtr) (NPIdentifier identifier);
+
+typedef NPObject*    (*NPN_CreateObjectProcPtr) (NPP,
+                         NPClass *aClass);
+typedef NPObject*    (*NPN_RetainObjectProcPtr) (NPObject *obj);
+typedef void         (*NPN_ReleaseObjectProcPtr) (NPObject *obj);
+typedef bool         (*NPN_InvokeProcPtr) (NPP npp,
+                         NPObject *obj,
+                         NPIdentifier methodName,
+                         const NPVariant *args,
+                         unsigned argCount,
+                         NPVariant *result);
+typedef bool         (*NPN_InvokeDefaultProcPtr) (NPP npp,
+                         NPObject *obj,
+                         const NPVariant *args,
+                         unsigned argCount,
+                         NPVariant *result);
+typedef bool         (*NPN_EvaluateProcPtr) (NPP npp,
+                         NPObject *obj,
+                         NPString *script,
+                         NPVariant *result);
+typedef bool         (*NPN_GetPropertyProcPtr) (NPP npp,
+                         NPObject *obj,
+                         NPIdentifier propertyName,
+                         NPVariant *result);
+typedef bool         (*NPN_SetPropertyProcPtr) (NPP npp,
+                         NPObject *obj,
+                         NPIdentifier propertyName,
+                         const NPVariant *value);
+typedef bool         (*NPN_HasPropertyProcPtr) (NPP,
+                         NPObject *npobj,
+                         NPIdentifier propertyName);
+typedef bool         (*NPN_HasMethodProcPtr) (NPP npp,
+                         NPObject *npobj,
+                         NPIdentifier methodName);
+typedef bool         (*NPN_RemovePropertyProcPtr) (NPP npp,
+                         NPObject *obj,
+                         NPIdentifier propertyName);
+typedef void         (*NPN_SetExceptionProcPtr) (NPObject *obj,
+                         const NPUTF8 *message);
+typedef void         (*NPN_PushPopupsEnabledStateProcPtr)(NPP npp,
+                         NPBool enabled);
+typedef void         (*NPN_PopPopupsEnabledStateProcPtr)(NPP npp);
+typedef bool         (*NPN_EnumerateProcPtr)(NPP npp,
+                         NPObject *obj,
+                         NPIdentifier **identifier,
+                         uint32_t *count);
+typedef void         (*NPN_PluginThreadAsyncCallProcPtr)(NPP instance,
+                         void (*func)(void *),
+                         void *userData);
+typedef bool         (*NPN_ConstructProcPtr)(NPP npp,
+                         NPObject* obj,
+                         const NPVariant *args,
+                         uint32_t argCount,
+                         NPVariant *result);
+typedef NPError      (*NPN_GetValueForURLPtr)(NPP npp,
+                         NPNURLVariable variable,
+                         const char *url,
+                         char **value,
+                         uint32_t *len);
+typedef NPError      (*NPN_SetValueForURLPtr)(NPP npp,
+                         NPNURLVariable variable,
+                         const char *url,
+                         const char *value,
+                         uint32_t len);
+typedef NPError      (*NPN_GetAuthenticationInfoPtr)(NPP npp,
+                         const char *protocol,
+                         const char *host,
+                         int32_t port,
+                         const char *scheme,
+                         const char *realm,
+                         char **username,
+                         uint32_t *ulen,
+                         char **password,
+                         uint32_t *plen);
+typedef uint32       (*NPN_ScheduleTimerPtr)(NPP npp,
+                         uint32 interval,
+                         NPBool repeat,
+                         void (*timerFunc)(NPP npp, uint32 timerID));
+typedef void         (*NPN_UnscheduleTimerPtr)(NPP npp,
+                         uint32 timerID);
+typedef NPError      (*NPN_PopUpContextMenuPtr)(NPP npp,
+                         NPMenu* menu);
+typedef NPBool       (*NPN_ConvertPointPtr)(NPP npp,
+                         double sourceX,
+                         double sourceY,
+                         NPCoordinateSpace sourceSpace,
+                         double *destX,
+                         double *destY,
+                         NPCoordinateSpace destSpace);
+
+//
+// NPAPI Function table of NPP functions (functions provided by plugin to host)
+//
+typedef struct _NPPluginFuncs {
+    unsigned short size;
+    unsigned short version;
+    NPP_NewProcPtr newp;
+    NPP_DestroyProcPtr destroy;
+    NPP_SetWindowProcPtr setwindow;
+    NPP_NewStreamProcPtr newstream;
+    NPP_DestroyStreamProcPtr destroystream;
+    NPP_StreamAsFileProcPtr asfile;
+    NPP_WriteReadyProcPtr writeready;
+    NPP_WriteProcPtr write;
+    NPP_PrintProcPtr print;
+    NPP_HandleEventProcPtr event;
+    NPP_URLNotifyProcPtr urlnotify;
+    JRIGlobalRef javaClass;
+    NPP_GetValueProcPtr getvalue;
+    NPP_SetValueProcPtr setvalue;
+} NPPluginFuncs;
+
+//
+// NPAPI Function table NPN functions (functions provided by host to plugin)
+//
+typedef struct _NPNetscapeFuncs {
+    uint16 size;
+    uint16 version;
+    NPN_GetURLProcPtr geturl;
+    NPN_PostURLProcPtr posturl;
+    NPN_RequestReadProcPtr requestread;
+    NPN_NewStreamProcPtr newstream;
+    NPN_WriteProcPtr write;
+    NPN_DestroyStreamProcPtr destroystream;
+    NPN_StatusProcPtr status;
+    NPN_UserAgentProcPtr uagent;
+    NPN_MemAllocProcPtr memalloc;
+    NPN_MemFreeProcPtr memfree;
+    NPN_MemFlushProcPtr memflush;
+    NPN_ReloadPluginsProcPtr reloadplugins;
+    NPN_GetJavaEnvProcPtr getJavaEnv;
+    NPN_GetJavaPeerProcPtr getJavaPeer;
+    NPN_GetURLNotifyProcPtr geturlnotify;
+    NPN_PostURLNotifyProcPtr posturlnotify;
+    NPN_GetValueProcPtr getvalue;
+    NPN_SetValueProcPtr setvalue;
+    NPN_InvalidateRectProcPtr invalidaterect;
+    NPN_InvalidateRegionProcPtr invalidateregion;
+    NPN_ForceRedrawProcPtr forceredraw;
+
+    NPN_GetStringIdentifierProcPtr getstringidentifier;
+    NPN_GetStringIdentifiersProcPtr getstringidentifiers;
+    NPN_GetIntIdentifierProcPtr getintidentifier;
+    NPN_IdentifierIsStringProcPtr identifierisstring;
+    NPN_UTF8FromIdentifierProcPtr utf8fromidentifier;
+    NPN_IntFromIdentifierProcPtr intfromidentifier;
+    NPN_CreateObjectProcPtr createobject;
+    NPN_RetainObjectProcPtr retainobject;
+    NPN_ReleaseObjectProcPtr releaseobject;
+    NPN_InvokeProcPtr invoke;
+    NPN_InvokeDefaultProcPtr invokeDefault;
+    NPN_EvaluateProcPtr evaluate;
+    NPN_GetPropertyProcPtr getproperty;
+    NPN_SetPropertyProcPtr setproperty;
+    NPN_RemovePropertyProcPtr removeproperty;
+    NPN_HasPropertyProcPtr hasproperty;
+    NPN_HasMethodProcPtr hasmethod;
+    NPN_ReleaseVariantValueProcPtr releasevariantvalue;
+    NPN_SetExceptionProcPtr setexception;
+    NPN_PushPopupsEnabledStateProcPtr pushpopupsenabledstate;
+    NPN_PopPopupsEnabledStateProcPtr poppopupsenabledstate;
+    NPN_EnumerateProcPtr enumerate;
+    NPN_PluginThreadAsyncCallProcPtr pluginthreadasynccall;
+    NPN_ConstructProcPtr construct;
+    NPN_GetValueForURLPtr getvalueforurl;
+    NPN_SetValueForURLPtr setvalueforurl;
+    NPN_GetAuthenticationInfoPtr getauthenticationinfo;
+    NPN_ScheduleTimerPtr scheduletimer;
+    NPN_UnscheduleTimerPtr unscheduletimer;
+    NPN_PopUpContextMenuPtr popupcontextmenu;
+    NPN_ConvertPointPtr convertpoint;
+} NPNetscapeFuncs;
+
+//
+// NPAPI library entry points
+//
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+typedef NPError (API_CALL * NP_InitializeFunc)(NPNetscapeFuncs* pNFuncs,
+                                               NPPluginFuncs* pPFuncs);
+#else
+typedef NPError (API_CALL * NP_InitializeFunc)(NPNetscapeFuncs* pFuncs);
+typedef NPError (API_CALL * NP_GetEntryPointsFunc)(NPPluginFuncs* pFuncs);
+#endif
+typedef NPError (API_CALL * NP_ShutdownFunc)(void);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // _NPHOSTAPI_H_
diff --git a/third_party/chrome/third_party/npapi/bindings/npruntime.h b/third_party/chrome/third_party/npapi/bindings/npruntime.h
new file mode 100644
index 0000000..e050b21
--- /dev/null
+++ b/third_party/chrome/third_party/npapi/bindings/npruntime.h
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2004, Apple Computer, Inc. and The Mozilla Foundation. 
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the names of Apple Computer, Inc. ("Apple") or The Mozilla
+ * Foundation ("Mozilla") nor the names of their contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY APPLE, MOZILLA AND THEIR 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 APPLE, MOZILLA OR
+ * THEIR 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.
+ *
+ * Revision 1 (March 4, 2004):
+ * Initial proposal.
+ *
+ * Revision 2 (March 10, 2004):
+ * All calls into script were made asynchronous.  Results are
+ * provided via the NPScriptResultFunctionPtr callback.
+ *
+ * Revision 3 (March 10, 2004):
+ * Corrected comments to not refer to class retain/release FunctionPtrs.
+ *
+ * Revision 4 (March 11, 2004):
+ * Added additional convenience NPN_SetExceptionWithUTF8().
+ * Changed NPHasPropertyFunctionPtr and NPHasMethodFunctionPtr to take NPClass
+ * pointers instead of NPObject pointers.
+ * Added NPIsValidIdentifier().
+ *
+ * Revision 5 (March 17, 2004):
+ * Added context parameter to result callbacks from ScriptObject functions.
+ *
+ * Revision 6 (March 29, 2004):
+ * Renamed functions implemented by user agent to NPN_*.  Removed _ from
+ * type names.
+ * Renamed "JavaScript" types to "Script".
+ *
+ * Revision 7 (April 21, 2004):
+ * NPIdentifier becomes a void*, was int32_t
+ * Remove NP_IsValidIdentifier, renamed NP_IdentifierFromUTF8 to NP_GetIdentifier
+ * Added NPVariant and modified functions to use this new type.
+ *
+ * Revision 8 (July 9, 2004):
+ * Updated to joint Apple-Mozilla license.
+ *
+ * Revision 9 (August 12, 2004):
+ * Changed NPVariantType enum values to form PVariantType_XXX
+ * Added NPP arguments to NPObject functions.
+ * Replaced NPVariant functions with macros.
+ */
+#ifndef _NP_RUNTIME_H_
+#define _NP_RUNTIME_H_
+
+
+// BEGIN GOOGLE MODIFICATIONS
+#include "npapi.h"
+#ifndef __native_client__
+typedef uint8 uint8_t;
+typedef int8 int8_t;
+typedef uint16 uint16_t;
+typedef int16 int16_t;
+typedef uint32 uint32_t;
+typedef int32 int32_t;
+typedef int64 int64_t;
+typedef uint64 uint64_t;
+#endif  /* __native_client__ */
+// END GOOGLE MODIFICATIONS
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+    This API is used to facilitate binding code written in C to script
+    objects.  The API in this header does not assume the presence of a
+    user agent.  That is, it can be used to bind C code to scripting
+    environments outside of the context of a user agent.
+    
+    However, the normal use of the this API is in the context of a
+    scripting environment running in a browser or other user agent.
+    In particular it is used to support the extended Netscape
+    script-ability API for plugins (NP-SAP).  NP-SAP is an extension
+    of the Netscape plugin API.  As such we have adopted the use of
+    the "NP" prefix for this API.
+
+    The following NP{N|P}Variables were added to the Netscape plugin
+    API (in npapi.h):
+
+    NPNVWindowNPObject
+    NPNVPluginElementNPObject
+    NPPVpluginScriptableNPObject
+
+    These variables are exposed through NPN_GetValue() and
+    NPP_GetValue() (respectively) and are used to establish the
+    initial binding between the user agent and native code.  The DOM
+    objects in the user agent can be examined and manipulated using
+    the NPN_ functions that operate on NPObjects described in this
+    header.
+
+    To the extent possible the assumptions about the scripting
+    language used by the scripting environment have been minimized.
+*/
+
+
+/*
+    Objects (non-primitive data) passed between 'C' and script is
+    always wrapped in an NPObject.  The 'interface' of an NPObject is
+    described by an NPClass.
+*/
+typedef struct NPObject NPObject;
+typedef struct NPClass NPClass;
+
+typedef char NPUTF8;
+typedef struct _NPString {
+    const NPUTF8 *UTF8Characters;
+    uint32_t UTF8Length;
+} NPString;
+  
+typedef enum {
+    NPVariantType_Void,
+    NPVariantType_Null,
+    NPVariantType_Bool,
+    NPVariantType_Int32,
+    NPVariantType_Double,
+    NPVariantType_String,
+    NPVariantType_Object
+} NPVariantType;
+
+typedef struct _NPVariant {
+    NPVariantType type;
+    union {
+        bool boolValue;
+        int32_t intValue;
+        double doubleValue;
+        NPString stringValue;
+        NPObject *objectValue;
+    } value;
+} NPVariant;
+
+/*
+    NPN_ReleaseVariantValue is called on all 'out' parameters references.
+    Specifically it is called on variants that are resultant out parameters
+    in NPGetPropertyFunctionPtr and NPInvokeFunctionPtr.  Resultant variants
+    from these two functions should be initialized using the
+    NPN_InitializeVariantXXX() functions.
+    
+    After calling NPReleaseVariantValue, the type of the variant will
+    be set to NPVariantUndefinedType.
+*/
+void NPN_ReleaseVariantValue (NPVariant *variant);
+
+#define NPVARIANT_IS_VOID(_v)    ((_v).type == NPVariantType_Void)
+#define NPVARIANT_IS_NULL(_v)    ((_v).type == NPVariantType_Null)
+#define NPVARIANT_IS_BOOLEAN(_v) ((_v).type == NPVariantType_Bool)
+#define NPVARIANT_IS_INT32(_v)   ((_v).type == NPVariantType_Int32)
+#define NPVARIANT_IS_DOUBLE(_v)  ((_v).type == NPVariantType_Double)
+#define NPVARIANT_IS_STRING(_v)  ((_v).type == NPVariantType_String)
+#define NPVARIANT_IS_OBJECT(_v)  ((_v).type == NPVariantType_Object)
+
+#define NPVARIANT_TO_BOOLEAN(_v) ((_v).value.boolValue)
+#define NPVARIANT_TO_INT32(_v)   ((_v).value.intValue)
+#define NPVARIANT_TO_DOUBLE(_v)  ((_v).value.doubleValue)
+#define NPVARIANT_TO_STRING(_v)  ((_v).value.stringValue)
+#define NPVARIANT_TO_OBJECT(_v)  ((_v).value.objectValue)
+
+#define NP_BEGIN_MACRO  do {
+#define NP_END_MACRO    } while (0)
+
+#define VOID_TO_NPVARIANT(_v)                NP_BEGIN_MACRO (_v).type = NPVariantType_Void; (_v).value.objectValue = NULL; NP_END_MACRO
+#define NULL_TO_NPVARIANT(_v)                NP_BEGIN_MACRO (_v).type = NPVariantType_Null; (_v).value.objectValue = NULL; NP_END_MACRO
+#define BOOLEAN_TO_NPVARIANT(_val, _v)       NP_BEGIN_MACRO (_v).type = NPVariantType_Bool; (_v).value.boolValue = !!(_val); NP_END_MACRO
+#define INT32_TO_NPVARIANT(_val, _v)         NP_BEGIN_MACRO (_v).type = NPVariantType_Int32; (_v).value.intValue = _val; NP_END_MACRO
+#define DOUBLE_TO_NPVARIANT(_val, _v)        NP_BEGIN_MACRO (_v).type = NPVariantType_Double; (_v).value.doubleValue = _val; NP_END_MACRO
+#define STRINGZ_TO_NPVARIANT(_val, _v)       NP_BEGIN_MACRO (_v).type = NPVariantType_String; NPString str = { _val, strlen(_val) }; (_v).value.stringValue = str; NP_END_MACRO
+#define STRINGN_TO_NPVARIANT(_val, _len, _v) NP_BEGIN_MACRO (_v).type = NPVariantType_String; NPString str = { _val, _len }; (_v).value.stringValue = str; NP_END_MACRO
+#define OBJECT_TO_NPVARIANT(_val, _v)        NP_BEGIN_MACRO (_v).type = NPVariantType_Object; (_v).value.objectValue = _val; NP_END_MACRO
+
+/*
+        Type mappings (JavaScript types have been used for illustration
+    purposes):
+
+        JavaScript       to             C (NPVariant with type:)
+        undefined                       NPVariantType_Void
+        null                            NPVariantType_Null
+        Boolean                         NPVariantType_Bool
+        Number                          NPVariantType_Double or NPVariantType_Int32
+        String                          NPVariantType_String
+        Object                          NPVariantType_Object
+
+        C (NPVariant with type:)   to   JavaScript
+        NPVariantType_Void              undefined
+        NPVariantType_Null              null
+        NPVariantType_Bool              Boolean 
+        NPVariantType_Int32             Number
+        NPVariantType_Double            Number
+        NPVariantType_String            String
+        NPVariantType_Object            Object
+*/
+
+typedef void *NPIdentifier;
+
+/*
+    NPObjects have methods and properties.  Methods and properties are
+    identified with NPIdentifiers.  These identifiers may be reflected
+    in script.  NPIdentifiers can be either strings or integers, IOW,
+    methods and properties can be identified by either strings or
+    integers (i.e. foo["bar"] vs foo[1]). NPIdentifiers can be
+    compared using ==.  In case of any errors, the requested
+    NPIdentifier(s) will be NULL.
+*/
+NPIdentifier NPN_GetStringIdentifier(const NPUTF8 *name);
+void NPN_GetStringIdentifiers(const NPUTF8 **names, int32_t nameCount, NPIdentifier *identifiers);
+NPIdentifier NPN_GetIntIdentifier(int32_t intid);
+bool NPN_IdentifierIsString(NPIdentifier identifier);
+
+/*
+    The NPUTF8 returned from NPN_UTF8FromIdentifier SHOULD be freed.
+*/
+NPUTF8 *NPN_UTF8FromIdentifier(NPIdentifier identifier);
+
+/*
+    Get the integer represented by identifier. If identifier is not an
+    integer identifier, the behaviour is undefined.
+*/
+int32_t NPN_IntFromIdentifier(NPIdentifier identifier);
+
+/*
+    NPObject behavior is implemented using the following set of
+    callback functions.
+
+    The NPVariant *result argument of these functions (where
+    applicable) should be released using NPN_ReleaseVariantValue().
+*/
+typedef NPObject *(*NPAllocateFunctionPtr)(NPP npp, NPClass *aClass);
+typedef void (*NPDeallocateFunctionPtr)(NPObject *obj);
+typedef void (*NPInvalidateFunctionPtr)(NPObject *obj);
+typedef bool (*NPHasMethodFunctionPtr)(NPObject *obj, NPIdentifier name);
+typedef bool (*NPInvokeFunctionPtr)(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
+typedef bool (*NPInvokeDefaultFunctionPtr)(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result);
+typedef bool (*NPHasPropertyFunctionPtr)(NPObject *obj, NPIdentifier name);
+typedef bool (*NPGetPropertyFunctionPtr)(NPObject *obj, NPIdentifier name, NPVariant *result);
+typedef bool (*NPSetPropertyFunctionPtr)(NPObject *obj, NPIdentifier name, const NPVariant *value);
+typedef bool (*NPRemovePropertyFunctionPtr)(NPObject *npobj, NPIdentifier name);
+typedef bool (*NPEnumerationFunctionPtr)(NPObject *npobj, NPIdentifier **value, uint32_t *count);
+typedef bool (*NPConstructFunctionPtr)(NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result);
+
+/*
+    NPObjects returned by create have a reference count of one.  It is the caller's responsibility
+    to release the returned object.
+
+    NPInvokeFunctionPtr function may return false to indicate a the method could not be invoked.
+    
+    NPGetPropertyFunctionPtr and NPSetPropertyFunctionPtr may return false to indicate a property doesn't
+    exist.
+    
+    NPInvalidateFunctionPtr is called by the scripting environment when the native code is
+    shutdown.  Any attempt to message a NPObject instance after the invalidate
+    callback has been called will result in undefined behavior, even if the
+    native code is still retaining those NPObject instances.
+    (The runtime will typically return immediately, with 0 or NULL, from an attempt to
+    dispatch to a NPObject, but this behavior should not be depended upon.)
+    
+    The NPEnumerationFunctionPtr function may pass an array of                  
+    NPIdentifiers back to the caller. The callee allocs the memory of           
+    the array using NPN_MemAlloc(), and it's the caller's responsibility        
+    to release it using NPN_MemFree().           
+*/
+struct NPClass
+{
+    uint32_t structVersion;
+    NPAllocateFunctionPtr allocate;
+    NPDeallocateFunctionPtr deallocate;
+    NPInvalidateFunctionPtr invalidate;
+    NPHasMethodFunctionPtr hasMethod;
+    NPInvokeFunctionPtr invoke;
+    NPInvokeDefaultFunctionPtr invokeDefault;
+    NPHasPropertyFunctionPtr hasProperty;
+    NPGetPropertyFunctionPtr getProperty;
+    NPSetPropertyFunctionPtr setProperty;
+    NPRemovePropertyFunctionPtr removeProperty;
+    NPEnumerationFunctionPtr enumerate;
+    NPConstructFunctionPtr construct;
+};
+
+#define NP_CLASS_STRUCT_VERSION      3
+#define NP_CLASS_STRUCT_VERSION_ENUM 2
+#define NP_CLASS_STRUCT_VERSION_CTOR 3
+
+#define NP_CLASS_STRUCT_VERSION_HAS_ENUM(npclass)   \
+    ((npclass)->structVersion >= NP_CLASS_STRUCT_VERSION_ENUM)
+#define NP_CLASS_STRUCT_VERSION_HAS_CTOR(npclass)   \
+    ((npclass)->structVersion >= NP_CLASS_STRUCT_VERSION_CTOR)
+
+struct NPObject {
+    NPClass *_class;
+    uint32_t referenceCount;
+    // Additional space may be allocated here by types of NPObjects
+};
+
+/*
+    If the class has an allocate function, NPN_CreateObject invokes that function,
+    otherwise a NPObject is allocated and returned.  If a class has an allocate
+    function it is the responsibility of that implementation to set the initial retain
+    count to 1.
+*/
+NPObject *NPN_CreateObject(NPP npp, NPClass *aClass);
+
+/*
+    Increment the NPObject's reference count.
+*/
+NPObject *NPN_RetainObject (NPObject *obj);
+
+/*
+    Decremented the NPObject's reference count.  If the reference
+    count goes to zero, the class's destroy function is invoke if
+    specified, otherwise the object is freed directly.
+*/
+void NPN_ReleaseObject (NPObject *obj);
+
+/*
+    Functions to access script objects represented by NPObject.
+
+    Calls to script objects are synchronous.  If a function returns a
+    value, it will be supplied via the result NPVariant
+    argument. Successful calls will return true, false will be
+    returned in case of an error.
+    
+    Calls made from plugin code to script must be made from the thread
+    on which the plugin was initialized.
+*/
+bool NPN_Invoke(NPP npp, NPObject *npobj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result);
+bool NPN_InvokeDefault(NPP npp, NPObject *npobj, const NPVariant *args, uint32_t argCount, NPVariant *result);
+bool NPN_Evaluate(NPP npp, NPObject *npobj, NPString *script, NPVariant *result);
+bool NPN_GetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, NPVariant *result);
+bool NPN_SetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName, const NPVariant *value);
+bool NPN_RemoveProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName);
+bool NPN_HasProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName);
+bool NPN_HasMethod(NPP npp, NPObject *npobj, NPIdentifier methodName);
+bool NPN_Enumerate(NPP npp, NPObject *npobj, NPIdentifier **identifier, uint32_t *count);
+bool NPN_Construct(NPP npp, NPObject* obj, const NPVariant *args, uint32_t argCount, NPVariant *result);
+
+// Helper function for evaluating a script in the scope of the NPObject passed in.
+// Parameters
+// npp
+//  The plugin's opaque instance handle (Can be NULL)
+// popups_allowed
+//  Indicates if popups created in the context of the script being executed are
+//  blocked or not.
+// npobj
+//  The NPObject.
+// npscript
+//  The script being executed.
+// result
+//  On return contains the value returned by the script.
+// Returns true on success.
+bool NPN_EvaluateHelper(NPP npp, bool popups_allowed, NPObject* npobj, 
+                        NPString* npscript, NPVariant *result);
+
+// BEGIN GOOGLE MODIFICATIONS
+
+void* NPP_GetJavaClass(void);
+void* NPN_GetJavaEnv(void);
+void* NPN_GetJavaPeer(NPP instance);
+void NPN_PluginThreadAsyncCall(NPP id, void (*func)(void *), void *userData);
+
+// END GOOGLE MODIFICATIONS
+
+/*
+    NPN_SetException may be called to trigger a script exception upon return
+    from entry points into NPObjects.
+*/
+void NPN_SetException (NPObject *obj, const NPUTF8 *message);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/third_party/gecko/README b/third_party/gecko/README
deleted file mode 100644
index d849908..0000000
--- a/third_party/gecko/README
+++ /dev/null
@@ -1,11 +0,0 @@
-This directory contains IDL files from the Mozilla source tree (circa Mozilla
-1.7).  These IDL files are copied here because they do not exist in the Gecko
-SDK.  Eventually, the hope is that the Gecko SDK will expand to include these
-interfaces.  For now, we keep these in a separate directory, with the idea that
-this directory will some day go away.
-
-Some files have been modified slightly, usually to change the name of an
-interface to deal with cases where multiple versions of the same interface are
-needed.  Modified files should include a comment explaining what has changed.
-
--darin
diff --git a/third_party/gecko/imgILoader.idl b/third_party/gecko/imgILoader.idl
deleted file mode 100644
index 8fb423c..0000000
--- a/third_party/gecko/imgILoader.idl
+++ /dev/null
@@ -1,112 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2001
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Stuart Parmenter <pavlov@netscape.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsISupports.idl"
-
-interface imgIDecoderObserver;
-interface imgIRequest;
-
-interface nsIChannel;
-interface nsILoadGroup;
-interface nsIStreamListener;
-interface nsIURI;
-
-interface nsISimpleEnumerator;
-
-#include "nsIRequest.idl" // for nsLoadFlags
-
-/**
- * imgILoader interface
- *
- * @author Stuart Parmenter <pavlov@netscape.com>
- * @version 0.3
- * @see imagelib2
- */
-[scriptable, uuid(a32826ff-9e56-4425-a811-97a8dba64ff5)]
-interface imgILoader : nsISupports
-{
-  /**
-   * Start the load and decode of an image.
-   * @param aURI the URI to load
-   * @param aInitialDocumentURI the URI that 'initiated' the load -- used for 3rd party cookie blocking
-   * @param aReferrerURI the 'referring' URI
-   * @param aLoadGroup Loadgroup to put the image load into
-   * @param aObserver the observer
-   * @param aCX some random data
-   * @param aLoadFlags Load flags for the request
-   * @param aCacheKey cache key to use for a load if the original
-   *                  image came from a request that had post data
-   * @param aRequest A newly created, unused imgIRequest object or NULL for one to
-                     be created for you.
-
-
-   * libpr0n does NOT keep a strong ref to the observer; this prevents
-   * reference cycles.  This means that callers of loadImage should
-   * make sure to Cancel() the resulting request before the observer
-   * goes away.
-   */
-  imgIRequest loadImage(in nsIURI aURI,
-                        in nsIURI aInitialDocumentURL,
-                        in nsIURI aReferrerURI,
-                        in nsILoadGroup aLoadGroup,
-                        in imgIDecoderObserver aObserver,
-                        in nsISupports aCX,
-                        in nsLoadFlags aLoadFlags,
-                        in nsISupports cacheKey,
-                        in imgIRequest aRequest);
-
-  /**
-   * Start the load and decode of an image.
-   * @param uri the URI to load
-   * @param aObserver the observer
-   * @param cx some random data
-   *
-   * libpr0n does NOT keep a strong ref to the observer; this prevents
-   * reference cycles.  This means that callers of loadImageWithChannel should
-   * make sure to Cancel() the resulting request before the observer goes away.
-   */
-  imgIRequest loadImageWithChannel(in nsIChannel aChannel, in imgIDecoderObserver aObserver, in nsISupports cx, out nsIStreamListener aListener);
-
-  /**
-   * Checks if a decoder for the an image with the given mime type is available
-   * @param mimeType The type to find a decoder for
-   * @return true if a decoder is available, false otherwise
-   */
-  boolean supportImageWithMimeType(in string mimeType);
-};
diff --git a/third_party/gecko/imgIRequest.idl b/third_party/gecko/imgIRequest.idl
deleted file mode 100644
index beebdc5..0000000
--- a/third_party/gecko/imgIRequest.idl
+++ /dev/null
@@ -1,97 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2001
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Stuart Parmenter <pavlov@netscape.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsISupports.idl"
-#include "nsIRequest.idl"
-
-interface imgIContainer;
-interface imgIDecoderObserver;
-interface nsIURI;
-
-/**
- * imgIRequest interface
- *
- * @author Stuart Parmenter <pavlov@netscape.com>
- * @version 0.1
- * @see imagelib2
- */
-[scriptable, uuid(ccf705f6-1dd1-11b2-82ef-e18eccf7f7ec)]
-interface imgIRequest : nsIRequest
-{
-  /**
-   * the image container...
-   * @return the image object associated with the request.
-   * @attention NEED DOCS
-   */
-  readonly attribute imgIContainer image;
-
-  /**
-   * Bits set in the return value from imageStatus
-   * @name statusflags
-   */
-  //@{
-  const long STATUS_NONE             = 0x0;
-  const long STATUS_SIZE_AVAILABLE   = 0x1;
-  const long STATUS_LOAD_PARTIAL     = 0x2;
-  const long STATUS_LOAD_COMPLETE    = 0x4;
-  const long STATUS_ERROR            = 0x8;
-  const long STATUS_FRAME_COMPLETE   = 0x10;
-  //@}
-
-  /**
-   * something
-   * @attention NEED DOCS
-   */
-  readonly attribute unsigned long imageStatus;
-
-  readonly attribute nsIURI URI;
-
-  readonly attribute imgIDecoderObserver decoderObserver;
-
-  readonly attribute string mimeType;
-
-  /**
-   * Clone this request; the returned request will have aObserver as the
-   * observer.  aObserver will be notified synchronously (before the clone()
-   * call returns) with all the notifications that have already been dispatched
-   * for this image load.
-   */
-  imgIRequest clone(in imgIDecoderObserver aObserver);
-};
-
diff --git a/third_party/gecko/include/jni.h b/third_party/gecko/include/jni.h
deleted file mode 100644
index 863075a..0000000
--- a/third_party/gecko/include/jni.h
+++ /dev/null
@@ -1,1810 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Java Runtime Interface.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation and Sun Microsystems, Inc.
- * Portions created by the Initial Developer are Copyright (C) 1993-1996
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef JNI_H
-#define JNI_H
-
-#include <stdio.h>
-#include <stdarg.h>
-
-/* jni_md.h contains the machine-dependent typedefs for jbyte, jint 
-   and jlong */ 
-
-#include "jni_md.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * JNI Types
- */
-
-typedef unsigned char	jboolean;
-typedef unsigned short	jchar;
-typedef short		jshort;
-typedef float		jfloat;
-typedef double		jdouble;
-
-typedef jint            jsize;
-
-#ifdef __cplusplus
-
-class _jobject {};
-class _jclass : public _jobject {};
-class _jthrowable : public _jobject {};
-class _jstring : public _jobject {};
-class _jarray : public _jobject {};
-class _jbooleanArray : public _jarray {};
-class _jbyteArray : public _jarray {};
-class _jcharArray : public _jarray {};
-class _jshortArray : public _jarray {};
-class _jintArray : public _jarray {};
-class _jlongArray : public _jarray {};
-class _jfloatArray : public _jarray {};
-class _jdoubleArray : public _jarray {};
-class _jobjectArray : public _jarray {};
-
-typedef _jobject *jobject;
-typedef _jclass *jclass;
-typedef _jthrowable *jthrowable;
-typedef _jstring *jstring;
-typedef _jarray *jarray;
-typedef _jbooleanArray *jbooleanArray;
-typedef _jbyteArray *jbyteArray;
-typedef _jcharArray *jcharArray;
-typedef _jshortArray *jshortArray;
-typedef _jintArray *jintArray;
-typedef _jlongArray *jlongArray;
-typedef _jfloatArray *jfloatArray;
-typedef _jdoubleArray *jdoubleArray;
-typedef _jobjectArray *jobjectArray;
-
-#else
-
-struct _jobject;
-
-typedef struct _jobject *jobject;
-typedef jobject jclass;
-typedef jobject jthrowable;
-typedef jobject jstring;
-typedef jobject jarray;
-typedef jarray jbooleanArray;
-typedef jarray jbyteArray;
-typedef jarray jcharArray;
-typedef jarray jshortArray;
-typedef jarray jintArray;
-typedef jarray jlongArray;
-typedef jarray jfloatArray;
-typedef jarray jdoubleArray;
-typedef jarray jobjectArray;
-
-#endif
-
-#if 0	/* moved to jri_md.h */
-typedef jobject jref; /* For transition---not meant to be part of public 
-			 API anymore.*/
-#endif
-
-typedef union jvalue {
-    jboolean z;
-    jbyte    b;
-    jchar    c;
-    jshort   s;
-    jint     i;
-    jlong    j;
-    jfloat   f;
-    jdouble  d;
-    jobject  l;
-} jvalue;
-
-struct _jfieldID;
-typedef struct _jfieldID *jfieldID;
-
-struct _jmethodID;
-typedef struct _jmethodID *jmethodID;
-
-/*
- * jboolean constants
- */
-
-#define JNI_FALSE 0
-#define JNI_TRUE 1
-
-/*
- * possible return values for JNI functions.
- */
-
-#define JNI_OK 0
-#define JNI_ERR (-1)
-
-/*
- * used in ReleaseScalarArrayElements
- */
-  
-#define JNI_COMMIT 1
-#define JNI_ABORT 2
-
-/*
- * used in RegisterNatives to describe native method name, signature,
- * and function pointer.
- */
-
-typedef struct {
-    char *name;
-    char *signature;
-    void *fnPtr;
-} JNINativeMethod;
-
-/*
- * JNI Native Method Interface.
- */
-
-struct JNINativeInterface_;
-
-struct JNIEnv_;
-
-#ifdef __cplusplus
-typedef JNIEnv_ JNIEnv;
-#else
-typedef const struct JNINativeInterface_ *JNIEnv;
-#endif
-
-/*
- * JNI Invocation Interface.
- */
-
-struct JNIInvokeInterface_;
-
-struct JavaVM_;
-
-#ifdef __cplusplus
-typedef JavaVM_ JavaVM;
-#else
-typedef const struct JNIInvokeInterface_ *JavaVM;
-#endif
-
-struct JNINativeInterface_ {
-    void *reserved0;
-    void *reserved1;
-    void *reserved2;
-
-    void *reserved3;
-    jint (JNICALL *GetVersion)(JNIEnv *env);
-
-    jclass (JNICALL *DefineClass)
-      (JNIEnv *env, const char *name, jobject loader, const jbyte *buf, 
-       jsize len);
-    jclass (JNICALL *FindClass)
-      (JNIEnv *env, const char *name);
-
-    void *reserved4;
-    void *reserved5;
-    void *reserved6;
-
-    jclass (JNICALL *GetSuperclass)
-      (JNIEnv *env, jclass sub);
-    jboolean (JNICALL *IsAssignableFrom)
-      (JNIEnv *env, jclass sub, jclass sup);
-    void *reserved7;
-
-
-    jint (JNICALL *Throw)
-      (JNIEnv *env, jthrowable obj);
-    jint (JNICALL *ThrowNew)
-      (JNIEnv *env, jclass clazz, const char *msg);
-    jthrowable (JNICALL *ExceptionOccurred)
-      (JNIEnv *env);
-    void (JNICALL *ExceptionDescribe)
-      (JNIEnv *env);
-    void (JNICALL *ExceptionClear)
-      (JNIEnv *env);
-    void (JNICALL *FatalError)
-      (JNIEnv *env, const char *msg);
-    void *reserved8;
-    void *reserved9;
-
-    jobject (JNICALL *NewGlobalRef)
-      (JNIEnv *env, jobject lobj);
-    void (JNICALL *DeleteGlobalRef)
-      (JNIEnv *env, jobject gref);
-    void (JNICALL *DeleteLocalRef)
-      (JNIEnv *env, jobject obj);
-    jboolean (JNICALL *IsSameObject)
-      (JNIEnv *env, jobject obj1, jobject obj2);
-    void *reserved10;
-    void *reserved11;
-
-    jobject (JNICALL *AllocObject)
-      (JNIEnv *env, jclass clazz);
-    jobject (JNICALL *NewObject)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
-    jobject (JNICALL *NewObjectV)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
-    jobject (JNICALL *NewObjectA)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args);
-
-    jclass (JNICALL *GetObjectClass)
-      (JNIEnv *env, jobject obj);
-    jboolean (JNICALL *IsInstanceOf)
-      (JNIEnv *env, jobject obj, jclass clazz);
-
-    jmethodID (JNICALL *GetMethodID)
-      (JNIEnv *env, jclass clazz, const char *name, const char *sig);
-
-    jobject (JNICALL *CallObjectMethod)
-      (JNIEnv *env, jobject obj, jmethodID methodID, ...);
-    jobject (JNICALL *CallObjectMethodV)
-      (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
-    jobject (JNICALL *CallObjectMethodA)
-      (JNIEnv *env, jobject obj, jmethodID methodID, jvalue * args);
-
-    jboolean (JNICALL *CallBooleanMethod)
-      (JNIEnv *env, jobject obj, jmethodID methodID, ...);
-    jboolean (JNICALL *CallBooleanMethodV)
-      (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
-    jboolean (JNICALL *CallBooleanMethodA)
-      (JNIEnv *env, jobject obj, jmethodID methodID, jvalue * args);
-
-    jbyte (JNICALL *CallByteMethod)
-      (JNIEnv *env, jobject obj, jmethodID methodID, ...);
-    jbyte (JNICALL *CallByteMethodV)
-      (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
-    jbyte (JNICALL *CallByteMethodA)
-      (JNIEnv *env, jobject obj, jmethodID methodID, jvalue *args);
-
-    jchar (JNICALL *CallCharMethod)
-      (JNIEnv *env, jobject obj, jmethodID methodID, ...);
-    jchar (JNICALL *CallCharMethodV)
-      (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
-    jchar (JNICALL *CallCharMethodA)
-      (JNIEnv *env, jobject obj, jmethodID methodID, jvalue *args);
-
-    jshort (JNICALL *CallShortMethod)
-      (JNIEnv *env, jobject obj, jmethodID methodID, ...);
-    jshort (JNICALL *CallShortMethodV)
-      (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
-    jshort (JNICALL *CallShortMethodA)
-      (JNIEnv *env, jobject obj, jmethodID methodID, jvalue *args);
-
-    jint (JNICALL *CallIntMethod)
-      (JNIEnv *env, jobject obj, jmethodID methodID, ...);
-    jint (JNICALL *CallIntMethodV)
-      (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
-    jint (JNICALL *CallIntMethodA)
-      (JNIEnv *env, jobject obj, jmethodID methodID, jvalue *args);
-
-    jlong (JNICALL *CallLongMethod)
-      (JNIEnv *env, jobject obj, jmethodID methodID, ...);
-    jlong (JNICALL *CallLongMethodV)
-      (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
-    jlong (JNICALL *CallLongMethodA)
-      (JNIEnv *env, jobject obj, jmethodID methodID, jvalue *args);
-
-    jfloat (JNICALL *CallFloatMethod)
-      (JNIEnv *env, jobject obj, jmethodID methodID, ...);
-    jfloat (JNICALL *CallFloatMethodV)
-      (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
-    jfloat (JNICALL *CallFloatMethodA)
-      (JNIEnv *env, jobject obj, jmethodID methodID, jvalue *args);
-
-    jdouble (JNICALL *CallDoubleMethod)
-      (JNIEnv *env, jobject obj, jmethodID methodID, ...);
-    jdouble (JNICALL *CallDoubleMethodV)
-      (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
-    jdouble (JNICALL *CallDoubleMethodA)
-      (JNIEnv *env, jobject obj, jmethodID methodID, jvalue *args);
-
-    void (JNICALL *CallVoidMethod)
-      (JNIEnv *env, jobject obj, jmethodID methodID, ...);
-    void (JNICALL *CallVoidMethodV)
-      (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
-    void (JNICALL *CallVoidMethodA)
-      (JNIEnv *env, jobject obj, jmethodID methodID, jvalue * args);
-
-    jobject (JNICALL *CallNonvirtualObjectMethod)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
-    jobject (JNICALL *CallNonvirtualObjectMethodV)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, 
-       va_list args);
-    jobject (JNICALL *CallNonvirtualObjectMethodA)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, 
-       jvalue * args);
-
-    jboolean (JNICALL *CallNonvirtualBooleanMethod)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
-    jboolean (JNICALL *CallNonvirtualBooleanMethodV)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
-       va_list args);
-    jboolean (JNICALL *CallNonvirtualBooleanMethodA)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
-       jvalue * args);
-
-    jbyte (JNICALL *CallNonvirtualByteMethod)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
-    jbyte (JNICALL *CallNonvirtualByteMethodV)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
-       va_list args);
-    jbyte (JNICALL *CallNonvirtualByteMethodA)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, 
-       jvalue *args);
-
-    jchar (JNICALL *CallNonvirtualCharMethod)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
-    jchar (JNICALL *CallNonvirtualCharMethodV)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
-       va_list args);
-    jchar (JNICALL *CallNonvirtualCharMethodA)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
-       jvalue *args);
-
-    jshort (JNICALL *CallNonvirtualShortMethod)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
-    jshort (JNICALL *CallNonvirtualShortMethodV)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
-       va_list args);
-    jshort (JNICALL *CallNonvirtualShortMethodA)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
-       jvalue *args);
-
-    jint (JNICALL *CallNonvirtualIntMethod)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
-    jint (JNICALL *CallNonvirtualIntMethodV)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
-       va_list args);
-    jint (JNICALL *CallNonvirtualIntMethodA)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
-       jvalue *args);
-
-    jlong (JNICALL *CallNonvirtualLongMethod)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
-    jlong (JNICALL *CallNonvirtualLongMethodV)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
-       va_list args);
-    jlong (JNICALL *CallNonvirtualLongMethodA)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, 
-       jvalue *args);
-
-    jfloat (JNICALL *CallNonvirtualFloatMethod)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
-    jfloat (JNICALL *CallNonvirtualFloatMethodV)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
-       va_list args);
-    jfloat (JNICALL *CallNonvirtualFloatMethodA)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
-       jvalue *args);
-
-    jdouble (JNICALL *CallNonvirtualDoubleMethod)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
-    jdouble (JNICALL *CallNonvirtualDoubleMethodV)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
-       va_list args);
-    jdouble (JNICALL *CallNonvirtualDoubleMethodA)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
-       jvalue *args);
-
-    void (JNICALL *CallNonvirtualVoidMethod)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
-    void (JNICALL *CallNonvirtualVoidMethodV)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
-       va_list args);
-    void (JNICALL *CallNonvirtualVoidMethodA)
-      (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
-       jvalue * args);
-
-    jfieldID (JNICALL *GetFieldID)
-      (JNIEnv *env, jclass clazz, const char *name, const char *sig);
-
-    jobject (JNICALL *GetObjectField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID);
-    jboolean (JNICALL *GetBooleanField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID);
-    jbyte (JNICALL *GetByteField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID);
-    jchar (JNICALL *GetCharField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID);
-    jshort (JNICALL *GetShortField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID);
-    jint (JNICALL *GetIntField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID);
-    jlong (JNICALL *GetLongField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID);
-    jfloat (JNICALL *GetFloatField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID);
-    jdouble (JNICALL *GetDoubleField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID);
-
-    void (JNICALL *SetObjectField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID, jobject val);
-    void (JNICALL *SetBooleanField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID, jboolean val);
-    void (JNICALL *SetByteField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID, jbyte val);
-    void (JNICALL *SetCharField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID, jchar val);
-    void (JNICALL *SetShortField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID, jshort val);
-    void (JNICALL *SetIntField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID, jint val);
-    void (JNICALL *SetLongField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID, jlong val);
-    void (JNICALL *SetFloatField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID, jfloat val);
-    void (JNICALL *SetDoubleField)
-      (JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val);
-
-    jmethodID (JNICALL *GetStaticMethodID)
-      (JNIEnv *env, jclass clazz, const char *name, const char *sig);
-
-    jobject (JNICALL *CallStaticObjectMethod)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
-    jobject (JNICALL *CallStaticObjectMethodV)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
-    jobject (JNICALL *CallStaticObjectMethodA)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args);
-
-    jboolean (JNICALL *CallStaticBooleanMethod)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
-    jboolean (JNICALL *CallStaticBooleanMethodV)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
-    jboolean (JNICALL *CallStaticBooleanMethodA)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args);
-
-    jbyte (JNICALL *CallStaticByteMethod)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
-    jbyte (JNICALL *CallStaticByteMethodV)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
-    jbyte (JNICALL *CallStaticByteMethodA)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args);
-
-    jchar (JNICALL *CallStaticCharMethod)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
-    jchar (JNICALL *CallStaticCharMethodV)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
-    jchar (JNICALL *CallStaticCharMethodA)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args);
-
-    jshort (JNICALL *CallStaticShortMethod)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
-    jshort (JNICALL *CallStaticShortMethodV)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
-    jshort (JNICALL *CallStaticShortMethodA)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args);
-
-    jint (JNICALL *CallStaticIntMethod)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
-    jint (JNICALL *CallStaticIntMethodV)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
-    jint (JNICALL *CallStaticIntMethodA)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args);
-
-    jlong (JNICALL *CallStaticLongMethod)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
-    jlong (JNICALL *CallStaticLongMethodV)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
-    jlong (JNICALL *CallStaticLongMethodA)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args);
-
-    jfloat (JNICALL *CallStaticFloatMethod)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
-    jfloat (JNICALL *CallStaticFloatMethodV)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
-    jfloat (JNICALL *CallStaticFloatMethodA)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args);
-
-    jdouble (JNICALL *CallStaticDoubleMethod)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
-    jdouble (JNICALL *CallStaticDoubleMethodV)
-      (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
-    jdouble (JNICALL *CallStaticDoubleMethodA)       
-      (JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args);
-
-    void (JNICALL *CallStaticVoidMethod)
-      (JNIEnv *env, jclass cls, jmethodID methodID, ...);
-    void (JNICALL *CallStaticVoidMethodV)
-      (JNIEnv *env, jclass cls, jmethodID methodID, va_list args);
-    void (JNICALL *CallStaticVoidMethodA)
-      (JNIEnv *env, jclass cls, jmethodID methodID, jvalue * args);
-
-    jfieldID (JNICALL *GetStaticFieldID)
-      (JNIEnv *env, jclass clazz, const char *name, const char *sig);
-    jobject (JNICALL *GetStaticObjectField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID);
-    jboolean (JNICALL *GetStaticBooleanField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID);
-    jbyte (JNICALL *GetStaticByteField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID);
-    jchar (JNICALL *GetStaticCharField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID);
-    jshort (JNICALL *GetStaticShortField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID);
-    jint (JNICALL *GetStaticIntField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID);
-    jlong (JNICALL *GetStaticLongField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID);
-    jfloat (JNICALL *GetStaticFloatField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID);
-    jdouble (JNICALL *GetStaticDoubleField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID);
-
-    void (JNICALL *SetStaticObjectField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value);
-    void (JNICALL *SetStaticBooleanField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value);
-    void (JNICALL *SetStaticByteField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value);
-    void (JNICALL *SetStaticCharField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value);
-    void (JNICALL *SetStaticShortField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value);
-    void (JNICALL *SetStaticIntField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID, jint value);
-    void (JNICALL *SetStaticLongField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value);
-    void (JNICALL *SetStaticFloatField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value);
-    void (JNICALL *SetStaticDoubleField)
-      (JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value);
-
-    jstring (JNICALL *NewString)
-      (JNIEnv *env, const jchar *unicode, jsize len);
-    jsize (JNICALL *GetStringLength)
-      (JNIEnv *env, jstring str);
-    const jchar *(JNICALL *GetStringChars)
-      (JNIEnv *env, jstring str, jboolean *isCopy);
-    void (JNICALL *ReleaseStringChars)
-      (JNIEnv *env, jstring str, const jchar *chars);
-  
-    jstring (JNICALL *NewStringUTF)
-      (JNIEnv *env, const char *utf);
-    jsize (JNICALL *GetStringUTFLength)
-      (JNIEnv *env, jstring str);
-    const char* (JNICALL *GetStringUTFChars)
-      (JNIEnv *env, jstring str, jboolean *isCopy);
-    void (JNICALL *ReleaseStringUTFChars)
-      (JNIEnv *env, jstring str, const char* chars);
-  
-
-    jsize (JNICALL *GetArrayLength)
-      (JNIEnv *env, jarray array);
-
-    jobjectArray (JNICALL *NewObjectArray)
-      (JNIEnv *env, jsize len, jclass clazz, jobject init);
-    jobject (JNICALL *GetObjectArrayElement)
-      (JNIEnv *env, jobjectArray array, jsize index);
-    void (JNICALL *SetObjectArrayElement)
-      (JNIEnv *env, jobjectArray array, jsize index, jobject val);
-
-    jbooleanArray (JNICALL *NewBooleanArray)
-      (JNIEnv *env, jsize len);
-    jbyteArray (JNICALL *NewByteArray)
-      (JNIEnv *env, jsize len);
-    jcharArray (JNICALL *NewCharArray)
-      (JNIEnv *env, jsize len);
-    jshortArray (JNICALL *NewShortArray)
-      (JNIEnv *env, jsize len);
-    jintArray (JNICALL *NewIntArray)
-      (JNIEnv *env, jsize len);
-    jlongArray (JNICALL *NewLongArray)
-      (JNIEnv *env, jsize len);
-    jfloatArray (JNICALL *NewFloatArray)
-      (JNIEnv *env, jsize len);
-    jdoubleArray (JNICALL *NewDoubleArray)
-      (JNIEnv *env, jsize len);
-
-    jboolean * (JNICALL *GetBooleanArrayElements)
-      (JNIEnv *env, jbooleanArray array, jboolean *isCopy);
-    jbyte * (JNICALL *GetByteArrayElements)
-      (JNIEnv *env, jbyteArray array, jboolean *isCopy);
-    jchar * (JNICALL *GetCharArrayElements)
-      (JNIEnv *env, jcharArray array, jboolean *isCopy);
-    jshort * (JNICALL *GetShortArrayElements)
-      (JNIEnv *env, jshortArray array, jboolean *isCopy);
-    jint * (JNICALL *GetIntArrayElements)
-      (JNIEnv *env, jintArray array, jboolean *isCopy);
-    jlong * (JNICALL *GetLongArrayElements)
-      (JNIEnv *env, jlongArray array, jboolean *isCopy);
-    jfloat * (JNICALL *GetFloatArrayElements)
-      (JNIEnv *env, jfloatArray array, jboolean *isCopy);
-    jdouble * (JNICALL *GetDoubleArrayElements)
-      (JNIEnv *env, jdoubleArray array, jboolean *isCopy);
-
-    void (JNICALL *ReleaseBooleanArrayElements)
-      (JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode);
-    void (JNICALL *ReleaseByteArrayElements)
-      (JNIEnv *env, jbyteArray array, jbyte *elems, jint mode);
-    void (JNICALL *ReleaseCharArrayElements)
-      (JNIEnv *env, jcharArray array, jchar *elems, jint mode);
-    void (JNICALL *ReleaseShortArrayElements)
-      (JNIEnv *env, jshortArray array, jshort *elems, jint mode);
-    void (JNICALL *ReleaseIntArrayElements)
-      (JNIEnv *env, jintArray array, jint *elems, jint mode);
-    void (JNICALL *ReleaseLongArrayElements)
-      (JNIEnv *env, jlongArray array, jlong *elems, jint mode);
-    void (JNICALL *ReleaseFloatArrayElements)
-      (JNIEnv *env, jfloatArray array, jfloat *elems, jint mode);
-    void (JNICALL *ReleaseDoubleArrayElements)
-      (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode);
-
-    void (JNICALL *GetBooleanArrayRegion)
-      (JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf);
-    void (JNICALL *GetByteArrayRegion)
-      (JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf);
-    void (JNICALL *GetCharArrayRegion)
-      (JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf);
-    void (JNICALL *GetShortArrayRegion)
-      (JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf);
-    void (JNICALL *GetIntArrayRegion)
-      (JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf);
-    void (JNICALL *GetLongArrayRegion)
-      (JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf);
-    void (JNICALL *GetFloatArrayRegion)
-      (JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf);
-    void (JNICALL *GetDoubleArrayRegion)
-      (JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf);
-
-    void (JNICALL *SetBooleanArrayRegion)
-      (JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf);
-    void (JNICALL *SetByteArrayRegion)
-      (JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf);
-    void (JNICALL *SetCharArrayRegion)
-      (JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf);
-    void (JNICALL *SetShortArrayRegion)
-      (JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf);
-    void (JNICALL *SetIntArrayRegion)
-      (JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf);
-    void (JNICALL *SetLongArrayRegion)
-      (JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf);
-    void (JNICALL *SetFloatArrayRegion)
-      (JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf);
-    void (JNICALL *SetDoubleArrayRegion)
-      (JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf);
-
-    jint (JNICALL *RegisterNatives)
-      (JNIEnv *env, jclass clazz, const JNINativeMethod *methods, 
-       jint nMethods);
-    jint (JNICALL *UnregisterNatives)
-      (JNIEnv *env, jclass clazz);
-
-    jint (JNICALL *MonitorEnter)
-      (JNIEnv *env, jobject obj);
-    jint (JNICALL *MonitorExit)
-      (JNIEnv *env, jobject obj);
- 
-    jint (JNICALL *GetJavaVM)
-      (JNIEnv *env, JavaVM **vm);
-};
-
-/*
- * We use inlined functions for C++ so that programmers can write:
- * 
- *    env->FindClass("java/lang/String")
- *
- * in C++ rather than:
- *
- *    (*env)->FindClass(env, "java/lang/String")
- *
- * in C.
- */
-
-struct JNIEnv_ {
-    const struct JNINativeInterface_ *functions;
-    void *reserved0;
-    void *reserved1[6];
-#ifdef __cplusplus
-
-    jint GetVersion() {
-        return functions->GetVersion(this);
-    }
-    jclass DefineClass(const char *name, jobject loader, const jbyte *buf,
-		       jsize len) {
-        return functions->DefineClass(this, name, loader, buf, len);
-    }
-    jclass FindClass(const char *name) {
-        return functions->FindClass(this, name);
-    }
-    jclass GetSuperclass(jclass sub) {
-        return functions->GetSuperclass(this, sub);
-    }
-    jboolean IsAssignableFrom(jclass sub, jclass sup) {
-        return functions->IsAssignableFrom(this, sub, sup);
-    }
-
-    jint Throw(jthrowable obj) {
-        return functions->Throw(this, obj);
-    }    
-    jint ThrowNew(jclass clazz, const char *msg) {
-        return functions->ThrowNew(this, clazz, msg);
-    }
-    jthrowable ExceptionOccurred() {
-        return functions->ExceptionOccurred(this);
-    }
-    void ExceptionDescribe() {
-        functions->ExceptionDescribe(this);
-    }
-    void ExceptionClear() {
-        functions->ExceptionClear(this);
-    }
-    void FatalError(const char *msg) {
-        functions->FatalError(this, msg);
-    }
-
-    jobject NewGlobalRef(jobject lobj) {
-        return functions->NewGlobalRef(this,lobj);
-    }
-    void DeleteGlobalRef(jobject gref) {
-        functions->DeleteGlobalRef(this,gref);
-    }
-    void DeleteLocalRef(jobject obj) {
-        functions->DeleteLocalRef(this, obj);
-    }
-
-    jboolean IsSameObject(jobject obj1, jobject obj2) {
-        return functions->IsSameObject(this,obj1,obj2);
-    }
-
-    jobject AllocObject(jclass clazz) {
-        return functions->AllocObject(this,clazz);
-    }
-    jobject NewObject(jclass clazz, jmethodID methodID, ...) {
-        va_list args;
-	jobject result;
-	va_start(args, methodID);
-        result = functions->NewObjectV(this,clazz,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jobject NewObjectV(jclass clazz, jmethodID methodID, 
-		       va_list args) {
-        return functions->NewObjectV(this,clazz,methodID,args);
-    }
-    jobject NewObjectA(jclass clazz, jmethodID methodID, 
-		       jvalue *args) {
-        return functions->NewObjectA(this,clazz,methodID,args);
-    }
-
-    jclass GetObjectClass(jobject obj) {
-        return functions->GetObjectClass(this,obj);
-    }
-    jboolean IsInstanceOf(jobject obj, jclass clazz) {
-        return functions->IsInstanceOf(this,obj,clazz);
-    }
-
-    jmethodID GetMethodID(jclass clazz, const char *name, 
-			  const char *sig) {
-        return functions->GetMethodID(this,clazz,name,sig);
-    }
-
-    jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) {
-        va_list args;
-	jobject result;
-	va_start(args,methodID);
-	result = functions->CallObjectMethodV(this,obj,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jobject CallObjectMethodV(jobject obj, jmethodID methodID, 
-			va_list args) {
-        return functions->CallObjectMethodV(this,obj,methodID,args);
-    }
-    jobject CallObjectMethodA(jobject obj, jmethodID methodID, 
-			jvalue * args) {
-        return functions->CallObjectMethodA(this,obj,methodID,args);
-    }
-
-    jboolean CallBooleanMethod(jobject obj, 
-			       jmethodID methodID, ...) {
-        va_list args;
-	jboolean result;
-	va_start(args,methodID);
-	result = functions->CallBooleanMethodV(this,obj,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jboolean CallBooleanMethodV(jobject obj, jmethodID methodID, 
-				va_list args) {
-        return functions->CallBooleanMethodV(this,obj,methodID,args);
-    }
-    jboolean CallBooleanMethodA(jobject obj, jmethodID methodID, 
-				jvalue * args) {
-        return functions->CallBooleanMethodA(this,obj,methodID, args);
-    }
-
-    jbyte CallByteMethod(jobject obj, jmethodID methodID, ...) {
-        va_list args;
-	jbyte result;
-	va_start(args,methodID);
-	result = functions->CallByteMethodV(this,obj,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jbyte CallByteMethodV(jobject obj, jmethodID methodID, 
-			  va_list args) {
-        return functions->CallByteMethodV(this,obj,methodID,args);
-    }
-    jbyte CallByteMethodA(jobject obj, jmethodID methodID, 
-			  jvalue * args) {
-        return functions->CallByteMethodA(this,obj,methodID,args);
-    }
-
-    jchar CallCharMethod(jobject obj, jmethodID methodID, ...) {
-        va_list args;
-	jchar result;
-	va_start(args,methodID);
-	result = functions->CallCharMethodV(this,obj,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jchar CallCharMethodV(jobject obj, jmethodID methodID, 
-			  va_list args) {
-        return functions->CallCharMethodV(this,obj,methodID,args);
-    }
-    jchar CallCharMethodA(jobject obj, jmethodID methodID, 
-			  jvalue * args) {
-        return functions->CallCharMethodA(this,obj,methodID,args);
-    }
-
-    jshort CallShortMethod(jobject obj, jmethodID methodID, ...) {
-        va_list args;
-	jshort result;
-	va_start(args,methodID);
-	result = functions->CallShortMethodV(this,obj,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jshort CallShortMethodV(jobject obj, jmethodID methodID, 
-			    va_list args) {
-        return functions->CallShortMethodV(this,obj,methodID,args);
-    }
-    jshort CallShortMethodA(jobject obj, jmethodID methodID, 
-			    jvalue * args) {
-        return functions->CallShortMethodA(this,obj,methodID,args);
-    }
-
-    jint CallIntMethod(jobject obj, jmethodID methodID, ...) {
-        va_list args;
-	jint result;
-	va_start(args,methodID);
-	result = functions->CallIntMethodV(this,obj,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jint CallIntMethodV(jobject obj, jmethodID methodID, 
-			va_list args) {
-        return functions->CallIntMethodV(this,obj,methodID,args);
-    }
-    jint CallIntMethodA(jobject obj, jmethodID methodID, 
-			jvalue * args) {
-        return functions->CallIntMethodA(this,obj,methodID,args);
-    }
-
-    jlong CallLongMethod(jobject obj, jmethodID methodID, ...) {
-        va_list args;
-	jlong result;
-	va_start(args,methodID);
-	result = functions->CallLongMethodV(this,obj,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jlong CallLongMethodV(jobject obj, jmethodID methodID, 
-			  va_list args) {
-        return functions->CallLongMethodV(this,obj,methodID,args);
-    }
-    jlong CallLongMethodA(jobject obj, jmethodID methodID, 
-			  jvalue * args) {
-        return functions->CallLongMethodA(this,obj,methodID,args);
-    }
-
-    jfloat CallFloatMethod(jobject obj, jmethodID methodID, ...) {
-        va_list args;
-	jfloat result;
-	va_start(args,methodID);
-	result = functions->CallFloatMethodV(this,obj,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jfloat CallFloatMethodV(jobject obj, jmethodID methodID, 
-			    va_list args) {
-        return functions->CallFloatMethodV(this,obj,methodID,args);
-    }
-    jfloat CallFloatMethodA(jobject obj, jmethodID methodID, 
-			    jvalue * args) {
-        return functions->CallFloatMethodA(this,obj,methodID,args);
-    }
-
-    jdouble CallDoubleMethod(jobject obj, jmethodID methodID, ...) {
-        va_list args;
-	jdouble result;
-	va_start(args,methodID);
-	result = functions->CallDoubleMethodV(this,obj,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jdouble CallDoubleMethodV(jobject obj, jmethodID methodID, 
-			va_list args) {
-        return functions->CallDoubleMethodV(this,obj,methodID,args);
-    }
-    jdouble CallDoubleMethodA(jobject obj, jmethodID methodID, 
-			jvalue * args) {
-        return functions->CallDoubleMethodA(this,obj,methodID,args);
-    }
-
-    void CallVoidMethod(jobject obj, jmethodID methodID, ...) {
-        va_list args;
-	va_start(args,methodID);
-	functions->CallVoidMethodV(this,obj,methodID,args);
-	va_end(args);
-    }
-    void CallVoidMethodV(jobject obj, jmethodID methodID, 
-			 va_list args) {
-        functions->CallVoidMethodV(this,obj,methodID,args);
-    }
-    void CallVoidMethodA(jobject obj, jmethodID methodID, 
-			 jvalue * args) {
-        functions->CallVoidMethodA(this,obj,methodID,args);
-    }
-
-    jobject CallNonvirtualObjectMethod(jobject obj, jclass clazz, 
-				       jmethodID methodID, ...) {
-        va_list args;
-	jobject result;
-	va_start(args,methodID);
-	result = functions->CallNonvirtualObjectMethodV(this,obj,clazz,
-							methodID,args);
-	va_end(args);
-	return result;
-    }
-    jobject CallNonvirtualObjectMethodV(jobject obj, jclass clazz, 
-					jmethodID methodID, va_list args) {
-        return functions->CallNonvirtualObjectMethodV(this,obj,clazz,
-						      methodID,args);
-    }
-    jobject CallNonvirtualObjectMethodA(jobject obj, jclass clazz, 
-					jmethodID methodID, jvalue * args) {
-        return functions->CallNonvirtualObjectMethodA(this,obj,clazz,
-						      methodID,args);
-    }
-
-    jboolean CallNonvirtualBooleanMethod(jobject obj, jclass clazz, 
-					 jmethodID methodID, ...) {
-        va_list args;
-	jboolean result;
-	va_start(args,methodID);
-	result = functions->CallNonvirtualBooleanMethodV(this,obj,clazz,
-							 methodID,args);
-	va_end(args);
-	return result;
-    }
-    jboolean CallNonvirtualBooleanMethodV(jobject obj, jclass clazz, 
-					  jmethodID methodID, va_list args) {
-        return functions->CallNonvirtualBooleanMethodV(this,obj,clazz,
-						       methodID,args);
-    }
-    jboolean CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, 
-					  jmethodID methodID, jvalue * args) {
-        return functions->CallNonvirtualBooleanMethodA(this,obj,clazz,
-						       methodID, args);
-    }
-
-    jbyte CallNonvirtualByteMethod(jobject obj, jclass clazz, 
-				   jmethodID methodID, ...) {
-        va_list args;
-	jbyte result;
-	va_start(args,methodID);
-	result = functions->CallNonvirtualByteMethodV(this,obj,clazz,
-						      methodID,args);
-	va_end(args);
-	return result;
-    }
-    jbyte CallNonvirtualByteMethodV(jobject obj, jclass clazz, 
-				    jmethodID methodID, va_list args) {
-        return functions->CallNonvirtualByteMethodV(this,obj,clazz,
-						    methodID,args);
-    }
-    jbyte CallNonvirtualByteMethodA(jobject obj, jclass clazz, 
-				    jmethodID methodID, jvalue * args) {
-        return functions->CallNonvirtualByteMethodA(this,obj,clazz,
-						    methodID,args);
-    }
-
-    jchar CallNonvirtualCharMethod(jobject obj, jclass clazz, 
-				   jmethodID methodID, ...) {
-        va_list args;
-	jchar result;
-	va_start(args,methodID);
-	result = functions->CallNonvirtualCharMethodV(this,obj,clazz,
-						      methodID,args);
-	va_end(args);
-	return result;
-    }
-    jchar CallNonvirtualCharMethodV(jobject obj, jclass clazz, 
-				    jmethodID methodID, va_list args) {
-        return functions->CallNonvirtualCharMethodV(this,obj,clazz,
-						    methodID,args);
-    }
-    jchar CallNonvirtualCharMethodA(jobject obj, jclass clazz, 
-				    jmethodID methodID, jvalue * args) {
-        return functions->CallNonvirtualCharMethodA(this,obj,clazz,
-						    methodID,args);
-    }
-
-    jshort CallNonvirtualShortMethod(jobject obj, jclass clazz, 
-				     jmethodID methodID, ...) {
-        va_list args;
-	jshort result;
-	va_start(args,methodID);
-	result = functions->CallNonvirtualShortMethodV(this,obj,clazz,
-						       methodID,args);
-	va_end(args);
-	return result;
-    }
-    jshort CallNonvirtualShortMethodV(jobject obj, jclass clazz, 
-				      jmethodID methodID, va_list args) {
-        return functions->CallNonvirtualShortMethodV(this,obj,clazz,
-						     methodID,args);
-    }
-    jshort CallNonvirtualShortMethodA(jobject obj, jclass clazz,
-				      jmethodID methodID, jvalue * args) {
-        return functions->CallNonvirtualShortMethodA(this,obj,clazz,
-						     methodID,args);
-    }
-
-    jint CallNonvirtualIntMethod(jobject obj, jclass clazz, 
-				 jmethodID methodID, ...) {
-        va_list args;
-	jint result;
-	va_start(args,methodID);
-	result = functions->CallNonvirtualIntMethodV(this,obj,clazz,
-						     methodID,args);
-	va_end(args);
-	return result;
-    }
-    jint CallNonvirtualIntMethodV(jobject obj, jclass clazz, 
-				  jmethodID methodID, va_list args) {
-        return functions->CallNonvirtualIntMethodV(this,obj,clazz,
-						   methodID,args);
-    }
-    jint CallNonvirtualIntMethodA(jobject obj, jclass clazz, 
-				  jmethodID methodID, jvalue * args) {
-        return functions->CallNonvirtualIntMethodA(this,obj,clazz,
-						   methodID,args);
-    }
-
-    jlong CallNonvirtualLongMethod(jobject obj, jclass clazz,
-				   jmethodID methodID, ...) {
-        va_list args;
-	jlong result;
-	va_start(args,methodID);
-	result = functions->CallNonvirtualLongMethodV(this,obj,clazz,
-						      methodID,args);
-	va_end(args);
-	return result;
-    }
-    jlong CallNonvirtualLongMethodV(jobject obj, jclass clazz,
-				    jmethodID methodID, va_list args) {
-        return functions->CallNonvirtualLongMethodV(this,obj,clazz,
-						    methodID,args);
-    }
-    jlong CallNonvirtualLongMethodA(jobject obj, jclass clazz, 
-				    jmethodID methodID, jvalue * args) {
-        return functions->CallNonvirtualLongMethodA(this,obj,clazz,
-						    methodID,args);
-    }
-
-    jfloat CallNonvirtualFloatMethod(jobject obj, jclass clazz, 
-				     jmethodID methodID, ...) {
-        va_list args;
-	jfloat result;
-	va_start(args,methodID);
-	result = functions->CallNonvirtualFloatMethodV(this,obj,clazz,
-						       methodID,args);
-	va_end(args);
-	return result;
-    }
-    jfloat CallNonvirtualFloatMethodV(jobject obj, jclass clazz,
-				      jmethodID methodID, 
-				      va_list args) {
-        return functions->CallNonvirtualFloatMethodV(this,obj,clazz,
-						     methodID,args);
-    }
-    jfloat CallNonvirtualFloatMethodA(jobject obj, jclass clazz, 
-				      jmethodID methodID, 
-				      jvalue * args) {
-        return functions->CallNonvirtualFloatMethodA(this,obj,clazz,
-						     methodID,args);
-    }
-
-    jdouble CallNonvirtualDoubleMethod(jobject obj, jclass clazz,
-				       jmethodID methodID, ...) {
-        va_list args;
-	jdouble result;
-	va_start(args,methodID);
-	result = functions->CallNonvirtualDoubleMethodV(this,obj,clazz,
-							methodID,args);
-	va_end(args);
-	return result;
-    }
-    jdouble CallNonvirtualDoubleMethodV(jobject obj, jclass clazz,
-					jmethodID methodID, 
-					va_list args) {
-        return functions->CallNonvirtualDoubleMethodV(this,obj,clazz,
-						      methodID,args);
-    }
-    jdouble CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, 
-					jmethodID methodID, 
-					jvalue * args) {
-        return functions->CallNonvirtualDoubleMethodA(this,obj,clazz,
-						      methodID,args);
-    }
-
-    void CallNonvirtualVoidMethod(jobject obj, jclass clazz,
-				  jmethodID methodID, ...) {
-        va_list args;
-	va_start(args,methodID);
-	functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args);
-	va_end(args);
-    }
-    void CallNonvirtualVoidMethodV(jobject obj, jclass clazz,
-				   jmethodID methodID, 
-				   va_list args) {
-        functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args);
-    }
-    void CallNonvirtualVoidMethodA(jobject obj, jclass clazz,
-				   jmethodID methodID, 
-				   jvalue * args) {
-        functions->CallNonvirtualVoidMethodA(this,obj,clazz,methodID,args);
-    }
-
-    jfieldID GetFieldID(jclass clazz, const char *name, 
-			const char *sig) {
-        return functions->GetFieldID(this,clazz,name,sig);
-    }
-
-    jobject GetObjectField(jobject obj, jfieldID fieldID) {
-        return functions->GetObjectField(this,obj,fieldID);
-    }
-    jboolean GetBooleanField(jobject obj, jfieldID fieldID) {
-        return functions->GetBooleanField(this,obj,fieldID);
-    }
-    jbyte GetByteField(jobject obj, jfieldID fieldID) {
-        return functions->GetByteField(this,obj,fieldID);
-    }
-    jchar GetCharField(jobject obj, jfieldID fieldID) {
-        return functions->GetCharField(this,obj,fieldID);
-    }
-    jshort GetShortField(jobject obj, jfieldID fieldID) {
-        return functions->GetShortField(this,obj,fieldID);
-    }
-    jint GetIntField(jobject obj, jfieldID fieldID) {
-        return functions->GetIntField(this,obj,fieldID);
-    }
-    jlong GetLongField(jobject obj, jfieldID fieldID) {
-        return functions->GetLongField(this,obj,fieldID);
-    }
-    jfloat GetFloatField(jobject obj, jfieldID fieldID) {
-        return functions->GetFloatField(this,obj,fieldID);
-    }
-    jdouble GetDoubleField(jobject obj, jfieldID fieldID) {
-        return functions->GetDoubleField(this,obj,fieldID);
-    }
-
-    void SetObjectField(jobject obj, jfieldID fieldID, jobject val) {
-        functions->SetObjectField(this,obj,fieldID,val);
-    }
-    void SetBooleanField(jobject obj, jfieldID fieldID, 
-			 jboolean val) {
-        functions->SetBooleanField(this,obj,fieldID,val);
-    }
-    void SetByteField(jobject obj, jfieldID fieldID, 
-		      jbyte val) {
-        functions->SetByteField(this,obj,fieldID,val);
-    }
-    void SetCharField(jobject obj, jfieldID fieldID, 
-		      jchar val) {
-        functions->SetCharField(this,obj,fieldID,val);
-    }
-    void SetShortField(jobject obj, jfieldID fieldID,
-		       jshort val) {
-        functions->SetShortField(this,obj,fieldID,val);
-    }
-    void SetIntField(jobject obj, jfieldID fieldID, 
-		     jint val) {
-        functions->SetIntField(this,obj,fieldID,val);
-    }
-    void SetLongField(jobject obj, jfieldID fieldID, 
-		      jlong val) {
-        functions->SetLongField(this,obj,fieldID,val);
-    }
-    void SetFloatField(jobject obj, jfieldID fieldID, 
-		       jfloat val) {
-        functions->SetFloatField(this,obj,fieldID,val);
-    }
-    void SetDoubleField(jobject obj, jfieldID fieldID, 
-			jdouble val) {
-        functions->SetDoubleField(this,obj,fieldID,val);
-    }
-
-    jmethodID GetStaticMethodID(jclass clazz, const char *name, 
-				const char *sig) {
-        return functions->GetStaticMethodID(this,clazz,name,sig);
-    }
-
-    jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, 
-			     ...) {
-        va_list args;
-	jobject result;
-	va_start(args,methodID);
-	result = functions->CallStaticObjectMethodV(this,clazz,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jobject CallStaticObjectMethodV(jclass clazz, jmethodID methodID, 
-			      va_list args) {
-        return functions->CallStaticObjectMethodV(this,clazz,methodID,args);
-    }
-    jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID, 
-			      jvalue *args) {
-        return functions->CallStaticObjectMethodA(this,clazz,methodID,args);
-    }
-
-    jboolean CallStaticBooleanMethod(jclass clazz, 
-				     jmethodID methodID, ...) {
-        va_list args;
-	jboolean result;
-	va_start(args,methodID);
-	result = functions->CallStaticBooleanMethodV(this,clazz,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jboolean CallStaticBooleanMethodV(jclass clazz,
-				      jmethodID methodID, va_list args) {
-        return functions->CallStaticBooleanMethodV(this,clazz,methodID,args);
-    }
-    jboolean CallStaticBooleanMethodA(jclass clazz,
-				      jmethodID methodID, jvalue *args) {
-        return functions->CallStaticBooleanMethodA(this,clazz,methodID,args);
-    }
-
-    jbyte CallStaticByteMethod(jclass clazz,
-			       jmethodID methodID, ...) {
-        va_list args;
-	jbyte result;
-	va_start(args,methodID);
-	result = functions->CallStaticByteMethodV(this,clazz,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jbyte CallStaticByteMethodV(jclass clazz,
-				jmethodID methodID, va_list args) {
-        return functions->CallStaticByteMethodV(this,clazz,methodID,args);
-    }
-    jbyte CallStaticByteMethodA(jclass clazz, 
-				jmethodID methodID, jvalue *args) {
-        return functions->CallStaticByteMethodA(this,clazz,methodID,args);
-    }
-
-    jchar CallStaticCharMethod(jclass clazz,
-			       jmethodID methodID, ...) {
-        va_list args;
-	jchar result;
-	va_start(args,methodID);
-	result = functions->CallStaticCharMethodV(this,clazz,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jchar CallStaticCharMethodV(jclass clazz,
-				jmethodID methodID, va_list args) {
-        return functions->CallStaticCharMethodV(this,clazz,methodID,args);
-    }
-    jchar CallStaticCharMethodA(jclass clazz,
-				jmethodID methodID, jvalue *args) {
-        return functions->CallStaticCharMethodA(this,clazz,methodID,args);
-    }
-
-    jshort CallStaticShortMethod(jclass clazz,
-				 jmethodID methodID, ...) {
-        va_list args;
-	jshort result;
-	va_start(args,methodID);
-	result = functions->CallStaticShortMethodV(this,clazz,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jshort CallStaticShortMethodV(jclass clazz,
-				  jmethodID methodID, va_list args) {
-        return functions->CallStaticShortMethodV(this,clazz,methodID,args);
-    }
-    jshort CallStaticShortMethodA(jclass clazz,
-				  jmethodID methodID, jvalue *args) {
-        return functions->CallStaticShortMethodA(this,clazz,methodID,args);
-    }
-
-    jint CallStaticIntMethod(jclass clazz,
-			     jmethodID methodID, ...) {
-        va_list args;
-	jint result;
-	va_start(args,methodID);
-	result = functions->CallStaticIntMethodV(this,clazz,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jint CallStaticIntMethodV(jclass clazz,
-			      jmethodID methodID, va_list args) {
-        return functions->CallStaticIntMethodV(this,clazz,methodID,args);
-    }
-    jint CallStaticIntMethodA(jclass clazz, 
-			      jmethodID methodID, jvalue *args) {
-        return functions->CallStaticIntMethodA(this,clazz,methodID,args);
-    }
-
-    jlong CallStaticLongMethod(jclass clazz,
-			       jmethodID methodID, ...) {
-        va_list args;
-	jlong result;
-	va_start(args,methodID);
-	result = functions->CallStaticLongMethodV(this,clazz,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jlong CallStaticLongMethodV(jclass clazz, 
-				jmethodID methodID, va_list args) {
-        return functions->CallStaticLongMethodV(this,clazz,methodID,args);
-    }
-    jlong CallStaticLongMethodA(jclass clazz, 
-				jmethodID methodID, jvalue *args) {
-        return functions->CallStaticLongMethodA(this,clazz,methodID,args);
-    }
-
-    jfloat CallStaticFloatMethod(jclass clazz, 
-				 jmethodID methodID, ...) {
-        va_list args;
-	jfloat result;
-	va_start(args,methodID);
-	result = functions->CallStaticFloatMethodV(this,clazz,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jfloat CallStaticFloatMethodV(jclass clazz, 
-				  jmethodID methodID, va_list args) {
-        return functions->CallStaticFloatMethodV(this,clazz,methodID,args);
-    }
-    jfloat CallStaticFloatMethodA(jclass clazz, 
-				  jmethodID methodID, jvalue *args) {
-        return functions->CallStaticFloatMethodA(this,clazz,methodID,args);
-    }
-
-    jdouble CallStaticDoubleMethod(jclass clazz, 
-				   jmethodID methodID, ...) {
-        va_list args;
-	jdouble result;
-	va_start(args,methodID);
-	result = functions->CallStaticDoubleMethodV(this,clazz,methodID,args);
-	va_end(args);
-	return result;
-    }
-    jdouble CallStaticDoubleMethodV(jclass clazz, 
-				    jmethodID methodID, va_list args) {
-        return functions->CallStaticDoubleMethodV(this,clazz,methodID,args);
-    }
-    jdouble CallStaticDoubleMethodA(jclass clazz, 
-				    jmethodID methodID, jvalue *args) {
-        return functions->CallStaticDoubleMethodA(this,clazz,methodID,args);
-    }
-
-    void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...) {
-        va_list args;
-	va_start(args,methodID);
-	functions->CallStaticVoidMethodV(this,cls,methodID,args);
-	va_end(args);
-    }
-    void CallStaticVoidMethodV(jclass cls, jmethodID methodID, 
-			       va_list args) {
-        functions->CallStaticVoidMethodV(this,cls,methodID,args);
-    }
-    void CallStaticVoidMethodA(jclass cls, jmethodID methodID, 
-			       jvalue * args) {
-        functions->CallStaticVoidMethodA(this,cls,methodID,args);
-    }
-
-    jfieldID GetStaticFieldID(jclass clazz, const char *name, 
-			      const char *sig) {
-        return functions->GetStaticFieldID(this,clazz,name,sig);
-    }
-    jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) {
-        return functions->GetStaticObjectField(this,clazz,fieldID);
-    }
-    jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) {
-        return functions->GetStaticBooleanField(this,clazz,fieldID);
-    }
-    jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) {
-        return functions->GetStaticByteField(this,clazz,fieldID);
-    }
-    jchar GetStaticCharField(jclass clazz, jfieldID fieldID) {
-        return functions->GetStaticCharField(this,clazz,fieldID);
-    }
-    jshort GetStaticShortField(jclass clazz, jfieldID fieldID) {
-        return functions->GetStaticShortField(this,clazz,fieldID);
-    }
-    jint GetStaticIntField(jclass clazz, jfieldID fieldID) {
-        return functions->GetStaticIntField(this,clazz,fieldID);
-    }
-    jlong GetStaticLongField(jclass clazz, jfieldID fieldID) {
-        return functions->GetStaticLongField(this,clazz,fieldID);
-    }
-    jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) {
-        return functions->GetStaticFloatField(this,clazz,fieldID);
-    }
-    jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID) {
-        return functions->GetStaticDoubleField(this,clazz,fieldID);
-    }
-
-    void SetStaticObjectField(jclass clazz, jfieldID fieldID,
-			jobject value) {
-      functions->SetStaticObjectField(this,clazz,fieldID,value);
-    }
-    void SetStaticBooleanField(jclass clazz, jfieldID fieldID,
-			jboolean value) {
-      functions->SetStaticBooleanField(this,clazz,fieldID,value);
-    }
-    void SetStaticByteField(jclass clazz, jfieldID fieldID,
-			jbyte value) {
-      functions->SetStaticByteField(this,clazz,fieldID,value);
-    }
-    void SetStaticCharField(jclass clazz, jfieldID fieldID,
-			jchar value) {
-      functions->SetStaticCharField(this,clazz,fieldID,value);
-    }
-    void SetStaticShortField(jclass clazz, jfieldID fieldID,
-			jshort value) {
-      functions->SetStaticShortField(this,clazz,fieldID,value);
-    }
-    void SetStaticIntField(jclass clazz, jfieldID fieldID,
-			jint value) {
-      functions->SetStaticIntField(this,clazz,fieldID,value);
-    }
-    void SetStaticLongField(jclass clazz, jfieldID fieldID,
-			jlong value) {
-      functions->SetStaticLongField(this,clazz,fieldID,value);
-    }
-    void SetStaticFloatField(jclass clazz, jfieldID fieldID,
-			jfloat value) {
-      functions->SetStaticFloatField(this,clazz,fieldID,value);
-    }
-    void SetStaticDoubleField(jclass clazz, jfieldID fieldID,
-			jdouble value) {
-      functions->SetStaticDoubleField(this,clazz,fieldID,value);
-    }
-
-    jstring NewString(const jchar *unicode, jsize len) {
-        return functions->NewString(this,unicode,len);
-    }
-    jsize GetStringLength(jstring str) {
-        return functions->GetStringLength(this,str);
-    }
-    const jchar *GetStringChars(jstring str, jboolean *isCopy) {
-        return functions->GetStringChars(this,str,isCopy);
-    }
-    void ReleaseStringChars(jstring str, const jchar *chars) {
-        functions->ReleaseStringChars(this,str,chars);
-    }
-  
-    jstring NewStringUTF(const char *utf) {
-        return functions->NewStringUTF(this,utf);
-    }
-    jsize GetStringUTFLength(jstring str) {
-        return functions->GetStringUTFLength(this,str);
-    }
-    const char* GetStringUTFChars(jstring str, jboolean *isCopy) {
-        return functions->GetStringUTFChars(this,str,isCopy);
-    }
-    void ReleaseStringUTFChars(jstring str, const char* chars) {
-        functions->ReleaseStringUTFChars(this,str,chars);
-    }
-
-    jsize GetArrayLength(jarray array) {
-        return functions->GetArrayLength(this,array);
-    }
-
-    jobjectArray NewObjectArray(jsize len, jclass clazz, 
-				jobject init) {
-        return functions->NewObjectArray(this,len,clazz,init);
-    }
-    jobject GetObjectArrayElement(jobjectArray array, jsize index) {
-        return functions->GetObjectArrayElement(this,array,index);
-    }
-    void SetObjectArrayElement(jobjectArray array, jsize index, 
-			       jobject val) {
-        functions->SetObjectArrayElement(this,array,index,val);
-    }
-
-    jbooleanArray NewBooleanArray(jsize len) {
-        return functions->NewBooleanArray(this,len);
-    }
-    jbyteArray NewByteArray(jsize len) {
-        return functions->NewByteArray(this,len);
-    }
-    jcharArray NewCharArray(jsize len) {
-        return functions->NewCharArray(this,len);
-    }
-    jshortArray NewShortArray(jsize len) {
-        return functions->NewShortArray(this,len);
-    }
-    jintArray NewIntArray(jsize len) {
-        return functions->NewIntArray(this,len);
-    }
-    jlongArray NewLongArray(jsize len) {
-        return functions->NewLongArray(this,len);
-    }
-    jfloatArray NewFloatArray(jsize len) {
-        return functions->NewFloatArray(this,len);
-    }
-    jdoubleArray NewDoubleArray(jsize len) {
-        return functions->NewDoubleArray(this,len);
-    }
-
-    jboolean * GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy) {
-        return functions->GetBooleanArrayElements(this,array,isCopy);
-    }
-    jbyte * GetByteArrayElements(jbyteArray array, jboolean *isCopy) {
-        return functions->GetByteArrayElements(this,array,isCopy);
-    }
-    jchar * GetCharArrayElements(jcharArray array, jboolean *isCopy) {
-        return functions->GetCharArrayElements(this,array,isCopy);
-    }
-    jshort * GetShortArrayElements(jshortArray array, jboolean *isCopy) {
-        return functions->GetShortArrayElements(this,array,isCopy);
-    }
-    jint * GetIntArrayElements(jintArray array, jboolean *isCopy) {
-        return functions->GetIntArrayElements(this,array,isCopy);
-    }
-    jlong * GetLongArrayElements(jlongArray array, jboolean *isCopy) {
-        return functions->GetLongArrayElements(this,array,isCopy);
-    }
-    jfloat * GetFloatArrayElements(jfloatArray array, jboolean *isCopy) {
-        return functions->GetFloatArrayElements(this,array,isCopy);
-    }
-    jdouble * GetDoubleArrayElements(jdoubleArray array, jboolean *isCopy) {
-        return functions->GetDoubleArrayElements(this,array,isCopy);
-    }
-
-    void ReleaseBooleanArrayElements(jbooleanArray array, 
-				     jboolean *elems,
-				     jint mode) {
-        functions->ReleaseBooleanArrayElements(this,array,elems,mode);
-    }
-    void ReleaseByteArrayElements(jbyteArray array, 
-				  jbyte *elems,
-				  jint mode) {
-        functions->ReleaseByteArrayElements(this,array,elems,mode);
-    }
-    void ReleaseCharArrayElements(jcharArray array, 
-				  jchar *elems,
-				  jint mode) {
-        functions->ReleaseCharArrayElements(this,array,elems,mode);
-    }
-    void ReleaseShortArrayElements(jshortArray array, 
-				   jshort *elems,
-				   jint mode) {
-        functions->ReleaseShortArrayElements(this,array,elems,mode);
-    }
-    void ReleaseIntArrayElements(jintArray array, 
-				 jint *elems,
-				 jint mode) {
-        functions->ReleaseIntArrayElements(this,array,elems,mode);
-    }
-    void ReleaseLongArrayElements(jlongArray array, 
-				  jlong *elems,
-				  jint mode) {
-        functions->ReleaseLongArrayElements(this,array,elems,mode);
-    }
-    void ReleaseFloatArrayElements(jfloatArray array, 
-				   jfloat *elems,
-				   jint mode) {
-        functions->ReleaseFloatArrayElements(this,array,elems,mode);
-    }
-    void ReleaseDoubleArrayElements(jdoubleArray array, 
-				    jdouble *elems,
-				    jint mode) {
-        functions->ReleaseDoubleArrayElements(this,array,elems,mode);
-    }
-
-    void GetBooleanArrayRegion(jbooleanArray array, 
-			       jsize start, jsize len, jboolean *buf) {
-        functions->GetBooleanArrayRegion(this,array,start,len,buf);
-    }
-    void GetByteArrayRegion(jbyteArray array, 
-			    jsize start, jsize len, jbyte *buf) {
-        functions->GetByteArrayRegion(this,array,start,len,buf);
-    }
-    void GetCharArrayRegion(jcharArray array, 
-			    jsize start, jsize len, jchar *buf) {
-        functions->GetCharArrayRegion(this,array,start,len,buf);
-    }
-    void GetShortArrayRegion(jshortArray array, 
-			     jsize start, jsize len, jshort *buf) {
-        functions->GetShortArrayRegion(this,array,start,len,buf);
-    }
-    void GetIntArrayRegion(jintArray array, 
-			   jsize start, jsize len, jint *buf) {
-        functions->GetIntArrayRegion(this,array,start,len,buf);
-    }
-    void GetLongArrayRegion(jlongArray array, 
-			    jsize start, jsize len, jlong *buf) {
-        functions->GetLongArrayRegion(this,array,start,len,buf);
-    }
-    void GetFloatArrayRegion(jfloatArray array, 
-			     jsize start, jsize len, jfloat *buf) {
-        functions->GetFloatArrayRegion(this,array,start,len,buf);
-    }
-    void GetDoubleArrayRegion(jdoubleArray array, 
-			      jsize start, jsize len, jdouble *buf) {
-        functions->GetDoubleArrayRegion(this,array,start,len,buf);
-    }
-
-    void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, 
-			       jboolean *buf) {
-        functions->SetBooleanArrayRegion(this,array,start,len,buf);
-    }
-    void SetByteArrayRegion(jbyteArray array, jsize start, jsize len,
-			    jbyte *buf) {
-        functions->SetByteArrayRegion(this,array,start,len,buf);
-    }
-    void SetCharArrayRegion(jcharArray array, jsize start, jsize len, 
-			    jchar *buf) {
-        functions->SetCharArrayRegion(this,array,start,len,buf);
-    }
-    void SetShortArrayRegion(jshortArray array, jsize start, jsize len, 
-			     jshort *buf) {
-        functions->SetShortArrayRegion(this,array,start,len,buf);
-    }
-    void SetIntArrayRegion(jintArray array, jsize start, jsize len,
-			   jint *buf) {
-        functions->SetIntArrayRegion(this,array,start,len,buf);
-    }
-    void SetLongArrayRegion(jlongArray array, jsize start, jsize len,
-			    jlong *buf) {
-        functions->SetLongArrayRegion(this,array,start,len,buf);
-    }
-    void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, 
-			     jfloat *buf) {
-        functions->SetFloatArrayRegion(this,array,start,len,buf);
-    }
-    void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len,
-			      jdouble *buf) {
-        functions->SetDoubleArrayRegion(this,array,start,len,buf);
-    }
-
-    jint RegisterNatives(jclass clazz, const JNINativeMethod *methods,
-			 jint nMethods) {
-        return functions->RegisterNatives(this,clazz,methods,nMethods);
-    }
-    jint UnregisterNatives(jclass clazz) {
-        return functions->UnregisterNatives(this,clazz);
-    }  
-   
-    jint MonitorEnter(jobject obj) {
-        return functions->MonitorEnter(this,obj);
-    }
-    jint MonitorExit(jobject obj) {
-        return functions->MonitorExit(this,obj);
-    }
-
-    jint GetJavaVM(JavaVM **vm) {
-        return functions->GetJavaVM(this,vm);
-    }
-  
-#endif /* __cplusplus */
-};
-
-/* These structures will be VM-specific. */
-
-typedef struct JDK1_1InitArgs {
-    jint version;
-
-    char **properties;
-    jint checkSource; 
-    jint nativeStackSize;
-    jint javaStackSize;
-    jint minHeapSize;
-    jint maxHeapSize;
-    jint verifyMode;
-    char *classpath;
-
-    jint (JNICALL *vfprintf)(FILE *fp, const char *format, va_list args);
-    void (JNICALL *exit)(jint code);
-    void (JNICALL *abort)();
-    
-    jint enableClassGC;
-    jint enableVerboseGC;
-    jint disableAsyncGC;
-    jint verbose;
-    jboolean debugging;
-    jint debugPort;
-} JDK1_1InitArgs;
-
-typedef struct JDK1_1AttachArgs {
-    void * __padding; /* C compilers don't allow empty structures. */
-} JDK1_1AttachArgs;
-
-/* End VM-specific. */
-
-struct JNIInvokeInterface_ {
-    void *reserved0;
-    void *reserved1;
-    void *reserved2;
-
-    jint (JNICALL *DestroyJavaVM)(JavaVM *vm);
-
-    jint (JNICALL *AttachCurrentThread)
-      (JavaVM *vm, JNIEnv **penv, void *args);
-
-    jint (JNICALL *DetachCurrentThread)(JavaVM *vm);
-};
-
-struct JavaVM_ {
-    const struct JNIInvokeInterface_ *functions;
-    void *reserved0;
-    void *reserved1;
-    void *reserved2;
-#ifdef __cplusplus
-
-    jint DestroyJavaVM() {
-        return functions->DestroyJavaVM(this);
-    }
-    jint AttachCurrentThread(JNIEnv **penv, void *args) {
-        return functions->AttachCurrentThread(this, penv, args);
-    }
-    jint DetachCurrentThread() {
-        return functions->DetachCurrentThread(this);
-    }
-
-#endif
-};
-
-JNI_PUBLIC_API(void) JNI_GetDefaultJavaVMInitArgs(void *);
-
-JNI_PUBLIC_API(jint) JNI_CreateJavaVM(JavaVM **, JNIEnv **, void *);
-
-JNI_PUBLIC_API(jint) JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *);
-JNI_PUBLIC_API(jref) JNI_MakeLocalRef(JNIEnv *pJNIEnv, void *pHObject);
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif /* __cplusplus */
-
-#endif /* JNI_H */
-
-
diff --git a/third_party/gecko/include/jni_md.h b/third_party/gecko/include/jni_md.h
deleted file mode 100644
index d3e96ab..0000000
--- a/third_party/gecko/include/jni_md.h
+++ /dev/null
@@ -1,182 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * The contents of this file are subject to the Netscape Public
- * License Version 1.1 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.mozilla.org/NPL/
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is Netscape
- * Communications Corporation.  Portions created by Netscape are
- * Copyright (C) 1998 Netscape Communications Corporation. All
- * Rights Reserved.
- *
- * Contributor(s): 
- *
- *
- * This Original Code has been modified by IBM Corporation.
- * Modifications made by IBM described herein are
- * Copyright (c) International Business Machines
- * Corporation, 2000
- *
- * Modifications to Mozilla code or documentation
- * identified per MPL Section 3.3
- *
- * Date         Modified by     Description of modification
- * 03/27/2000   IBM Corp.       Set JNICALL to Optlink for
- *                               use in OS2
- */
-
-/*******************************************************************************
- * Netscape version of jni_md.h -- depends on jri_md.h
- ******************************************************************************/
-
-#ifndef JNI_MD_H
-#define JNI_MD_H
-
-#include "prtypes.h" /* needed for _declspec */
-
-/*******************************************************************************
- * WHAT'S UP WITH THIS FILE?
- * 
- * This is where we define the mystical JNI_PUBLIC_API macro that works on all
- * platforms. If you're running with Visual C++, Symantec C, or Borland's 
- * development environment on the PC, you're all set. Or if you're on the Mac
- * with Metrowerks, Symantec or MPW with SC you're ok too. For UNIX it shouldn't
- * matter.
-
- * Changes by sailesh on 9/26 
-
- * There are two symbols used in the declaration of the JNI functions
- * and native code that uses the JNI:
- * JNICALL - specifies the calling convention 
- * JNIEXPORT - specifies export status of the function 
- * 
- * The syntax to specify calling conventions is different in Win16 and
- * Win32 - the brains at Micro$oft at work here. JavaSoft in their
- * infinite wisdom cares for no platform other than Win32, and so they
- * just define these two symbols as:
-
- #define JNIEXPORT __declspec(dllexport)
- #define JNICALL __stdcall
-
- * We deal with this, in the way JRI defines the JRI_PUBLIC_API, by
- * defining a macro called JNI_PUBLIC_API. Any of our developers who
- * wish to use code for Win16 and Win32, _must_ use JNI_PUBLIC_API to
- * be able to export functions properly.
-
- * Since we must also maintain compatibility with JavaSoft, we
- * continue to define the symbol JNIEXPORT. However, use of this
- * internally is deprecated, since it will cause a mess on Win16.
-
- * We _do not_ need a new symbol called JNICALL. Instead we
- * redefine JNICALL in the same way JRI_CALLBACK was defined.
-
- ******************************************************************************/
-
-/* DLL Entry modifiers... */
-#if defined(XP_OS2)
-#  ifdef XP_OS2_VACPP
-#     define JNI_PUBLIC_API(ResultType)      ResultType _System
-#     define JNI_PUBLIC_VAR(VarType)         VarType
-#     define JNICALL                         _Optlink
-#     define JNIEXPORT
-#  else
-#     define JNI_PUBLIC_API(ResultType)	   ResultType
-#     define JNI_PUBLIC_VAR(VarType)         VarType
-#     define JNICALL
-#     define JNIEXPORT
-#  endif
-/* Win32 */
-#elif defined(XP_WIN) || defined(_WINDOWS) || defined(WIN32) || defined(_WIN32)
-#	include <windows.h>
-#	if defined(_MSC_VER) || defined(__GNUC__)
-#		if defined(WIN32) || defined(_WIN32)
-#			define JNI_PUBLIC_API(ResultType)	_declspec(dllexport) ResultType __stdcall
-#			define JNI_PUBLIC_VAR(VarType)		VarType
-#			define JNI_NATIVE_STUB(ResultType)	_declspec(dllexport) ResultType
-#			define JNICALL                          __stdcall
-#		else /* !_WIN32 */
-#		    if defined(_WINDLL)
-#			define JNI_PUBLIC_API(ResultType)	ResultType __cdecl __export __loadds 
-#			define JNI_PUBLIC_VAR(VarType)		VarType
-#			define JNI_NATIVE_STUB(ResultType)	ResultType __cdecl __loadds
-#			define JNICALL			        __loadds
-#		    else /* !WINDLL */
-#			define JNI_PUBLIC_API(ResultType)	ResultType __cdecl __export
-#			define JNI_PUBLIC_VAR(VarType)		VarType
-#			define JNI_NATIVE_STUB(ResultType)	ResultType __cdecl __export
-#			define JNICALL			        __export
-#                   endif /* !WINDLL */
-#		endif /* !_WIN32 */
-#	elif defined(__BORLANDC__)
-#		if defined(WIN32) || defined(_WIN32)
-#			define JNI_PUBLIC_API(ResultType)	__export ResultType
-#			define JNI_PUBLIC_VAR(VarType)		VarType
-#			define JNI_NATIVE_STUB(ResultType)	 __export ResultType
-#			define JNICALL
-#		else /* !_WIN32 */
-#			define JNI_PUBLIC_API(ResultType)	ResultType _cdecl _export _loadds 
-#			define JNI_PUBLIC_VAR(VarType)		VarType
-#			define JNI_NATIVE_STUB(ResultType)	ResultType _cdecl _loadds
-#			define JNICALL			_loadds
-#		endif
-#	else
-#		error Unsupported PC development environment.	
-#	endif
-#	ifndef IS_LITTLE_ENDIAN
-#		define IS_LITTLE_ENDIAN
-#	endif
-	/*  This is the stuff inherited from JavaSoft .. */
-#	define JNIEXPORT __declspec(dllexport)
-
-
-/* Mac */
-#elif macintosh || Macintosh || THINK_C
-#	if defined(__MWERKS__)				/* Metrowerks */
-#		if !__option(enumsalwaysint)
-#			error You need to define 'Enums Always Int' for your project.
-#		endif
-#		if defined(TARGET_CPU_68K) && !TARGET_RT_MAC_CFM 
-#			if !__option(fourbyteints) 
-#				error You need to define 'Struct Alignment: 68k' for your project.
-#			endif
-#		endif /* !GENERATINGCFM */
-#		define JNI_PUBLIC_API(ResultType)	__declspec(export) ResultType 
-#		define JNI_PUBLIC_VAR(VarType)		JNI_PUBLIC_API(VarType)
-#		define JNI_NATIVE_STUB(ResultType)	JNI_PUBLIC_API(ResultType)
-#	elif defined(__SC__)				/* Symantec */
-#		error What are the Symantec defines? (warren@netscape.com)
-#	elif macintosh && applec			/* MPW */
-#		error Please upgrade to the latest MPW compiler (SC).
-#	else
-#		error Unsupported Mac development environment.
-#	endif
-#	define JNICALL
-	/*  This is the stuff inherited from JavaSoft .. */
-#	define JNIEXPORT
-
-/* Unix or else */
-#else
-#	define JNI_PUBLIC_API(ResultType)		ResultType
-#       define JNI_PUBLIC_VAR(VarType)                  VarType
-#       define JNI_NATIVE_STUB(ResultType)              ResultType
-#	define JNICALL
-	/*  This is the stuff inherited from JavaSoft .. */
-#	define JNIEXPORT
-#endif
-
-#ifndef FAR		/* for non-Win16 */
-#define FAR
-#endif
-
-/* Get the rest of the stuff from jri_md.h */
-#include "jri_md.h"
-
-#endif /* JNI_MD_H */
diff --git a/third_party/gecko/include/jri.h b/third_party/gecko/include/jri.h
deleted file mode 100644
index f29945b..0000000
--- a/third_party/gecko/include/jri.h
+++ /dev/null
@@ -1,689 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: NPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is 
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or 
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the NPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the NPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/*******************************************************************************
- * Java Runtime Interface
- ******************************************************************************/
-
-#ifndef JRI_H
-#define JRI_H
-
-#include "jritypes.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-/*******************************************************************************
- * JRIEnv
- ******************************************************************************/
-
-/* The type of the JRIEnv interface. */
-typedef struct JRIEnvInterface	JRIEnvInterface;
-
-/* The type of a JRIEnv instance. */
-typedef const JRIEnvInterface*	JRIEnv;
-
-/*******************************************************************************
- * JRIEnv Operations
- ******************************************************************************/
-
-#define JRI_DefineClass(env, classLoader, buf, bufLen)	\
-	(((*(env))->DefineClass)(env, JRI_DefineClass_op, classLoader, buf, bufLen))
-
-#define JRI_FindClass(env, name)	\
-	(((*(env))->FindClass)(env, JRI_FindClass_op, name))
-
-#define JRI_Throw(env, obj)	\
-	(((*(env))->Throw)(env, JRI_Throw_op, obj))
-
-#define JRI_ThrowNew(env, clazz, message)	\
-	(((*(env))->ThrowNew)(env, JRI_ThrowNew_op, clazz, message))
-
-#define JRI_ExceptionOccurred(env)	\
-	(((*(env))->ExceptionOccurred)(env, JRI_ExceptionOccurred_op))
-
-#define JRI_ExceptionDescribe(env)	\
-	(((*(env))->ExceptionDescribe)(env, JRI_ExceptionDescribe_op))
-
-#define JRI_ExceptionClear(env)	\
-	(((*(env))->ExceptionClear)(env, JRI_ExceptionClear_op))
-
-#define JRI_NewGlobalRef(env, ref)	\
-	(((*(env))->NewGlobalRef)(env, JRI_NewGlobalRef_op, ref))
-
-#define JRI_DisposeGlobalRef(env, gref)	\
-	(((*(env))->DisposeGlobalRef)(env, JRI_DisposeGlobalRef_op, gref))
-
-#define JRI_GetGlobalRef(env, gref)	\
-	(((*(env))->GetGlobalRef)(env, JRI_GetGlobalRef_op, gref))
-
-#define JRI_SetGlobalRef(env, gref, ref)	\
-	(((*(env))->SetGlobalRef)(env, JRI_SetGlobalRef_op, gref, ref))
-
-#define JRI_IsSameObject(env, a, b)	\
-	(((*(env))->IsSameObject)(env, JRI_IsSameObject_op, a, b))
-
-#define JRI_NewObject(env)	((*(env))->NewObject)
-#define JRI_NewObjectV(env, clazz, methodID, args)	\
-	(((*(env))->NewObjectV)(env, JRI_NewObject_op_va_list, clazz, methodID, args))
-#define JRI_NewObjectA(env, clazz, method, args)	\
-	(((*(env))->NewObjectA)(env, JRI_NewObject_op_array, clazz, methodID, args))
-
-#define JRI_GetObjectClass(env, obj)	\
-	(((*(env))->GetObjectClass)(env, JRI_GetObjectClass_op, obj))
-
-#define JRI_IsInstanceOf(env, obj, clazz)	\
-	(((*(env))->IsInstanceOf)(env, JRI_IsInstanceOf_op, obj, clazz))
-
-#define JRI_GetMethodID(env, clazz, name, sig)	\
-	(((*(env))->GetMethodID)(env, JRI_GetMethodID_op, clazz, name, sig))
-
-#define JRI_CallMethod(env)	((*(env))->CallMethod)
-#define JRI_CallMethodV(env, obj, methodID, args)	\
-	(((*(env))->CallMethodV)(env, JRI_CallMethod_op_va_list, obj, methodID, args))
-#define JRI_CallMethodA(env, obj, methodID, args)	\
-	(((*(env))->CallMethodA)(env, JRI_CallMethod_op_array, obj, methodID, args))
-
-#define JRI_CallMethodBoolean(env)	((*(env))->CallMethodBoolean)
-#define JRI_CallMethodBooleanV(env, obj, methodID, args)	\
-	(((*(env))->CallMethodBooleanV)(env, JRI_CallMethodBoolean_op_va_list, obj, methodID, args))
-#define JRI_CallMethodBooleanA(env, obj, methodID, args)	\
-	(((*(env))->CallMethodBooleanA)(env, JRI_CallMethodBoolean_op_array, obj, methodID, args))
-
-#define JRI_CallMethodByte(env)	((*(env))->CallMethodByte)
-#define JRI_CallMethodByteV(env, obj, methodID, args)	\
-	(((*(env))->CallMethodByteV)(env, JRI_CallMethodByte_op_va_list, obj, methodID, args))
-#define JRI_CallMethodByteA(env, obj, methodID, args)	\
-	(((*(env))->CallMethodByteA)(env, JRI_CallMethodByte_op_array, obj, methodID, args))
-
-#define JRI_CallMethodChar(env)	((*(env))->CallMethodChar)
-#define JRI_CallMethodCharV(env, obj, methodID, args)	\
-	(((*(env))->CallMethodCharV)(env, JRI_CallMethodChar_op_va_list, obj, methodID, args))
-#define JRI_CallMethodCharA(env, obj, methodID, args)	\
-	(((*(env))->CallMethodCharA)(env, JRI_CallMethodChar_op_array, obj, methodID, args))
-
-#define JRI_CallMethodShort(env)	((*(env))->CallMethodShort)
-#define JRI_CallMethodShortV(env, obj, methodID, args)	\
-	(((*(env))->CallMethodShortV)(env, JRI_CallMethodShort_op_va_list, obj, methodID, args))
-#define JRI_CallMethodShortA(env, obj, methodID, args)	\
-	(((*(env))->CallMethodShortA)(env, JRI_CallMethodShort_op_array, obj, methodID, args))
-
-#define JRI_CallMethodInt(env)	((*(env))->CallMethodInt)
-#define JRI_CallMethodIntV(env, obj, methodID, args)	\
-	(((*(env))->CallMethodIntV)(env, JRI_CallMethodInt_op_va_list, obj, methodID, args))
-#define JRI_CallMethodIntA(env, obj, methodID, args)	\
-	(((*(env))->CallMethodIntA)(env, JRI_CallMethodInt_op_array, obj, methodID, args))
-
-#define JRI_CallMethodLong(env)	((*(env))->CallMethodLong)
-#define JRI_CallMethodLongV(env, obj, methodID, args)	\
-	(((*(env))->CallMethodLongV)(env, JRI_CallMethodLong_op_va_list, obj, methodID, args))
-#define JRI_CallMethodLongA(env, obj, methodID, args)	\
-	(((*(env))->CallMethodLongA)(env, JRI_CallMethodLong_op_array, obj, methodID, args))
-
-#define JRI_CallMethodFloat(env)	((*(env))->CallMethodFloat)
-#define JRI_CallMethodFloatV(env, obj, methodID, args)	\
-	(((*(env))->CallMethodFloatV)(env, JRI_CallMethodFloat_op_va_list, obj, methodID, args))
-#define JRI_CallMethodFloatA(env, obj, methodID, args)	\
-	(((*(env))->CallMethodFloatA)(env, JRI_CallMethodFloat_op_array, obj, methodID, args))
-
-#define JRI_CallMethodDouble(env)	((*(env))->CallMethodDouble)
-#define JRI_CallMethodDoubleV(env, obj, methodID, args)	\
-	(((*(env))->CallMethodDoubleV)(env, JRI_CallMethodDouble_op_va_list, obj, methodID, args))
-#define JRI_CallMethodDoubleA(env, obj, methodID, args)	\
-	(((*(env))->CallMethodDoubleA)(env, JRI_CallMethodDouble_op_array, obj, methodID, args))
-
-#define JRI_GetFieldID(env, clazz, name, sig)	\
-	(((*(env))->GetFieldID)(env, JRI_GetFieldID_op, clazz, name, sig))
-
-#define JRI_GetField(env, obj, fieldID)	\
-	(((*(env))->GetField)(env, JRI_GetField_op, obj, fieldID))
-
-#define JRI_GetFieldBoolean(env, obj, fieldID)	\
-	(((*(env))->GetFieldBoolean)(env, JRI_GetFieldBoolean_op, obj, fieldID))
-
-#define JRI_GetFieldByte(env, obj, fieldID)	\
-	(((*(env))->GetFieldByte)(env, JRI_GetFieldByte_op, obj, fieldID))
-
-#define JRI_GetFieldChar(env, obj, fieldID)	\
-	(((*(env))->GetFieldChar)(env, JRI_GetFieldChar_op, obj, fieldID))
-
-#define JRI_GetFieldShort(env, obj, fieldID)	\
-	(((*(env))->GetFieldShort)(env, JRI_GetFieldShort_op, obj, fieldID))
-
-#define JRI_GetFieldInt(env, obj, fieldID)	\
-	(((*(env))->GetFieldInt)(env, JRI_GetFieldInt_op, obj, fieldID))
-
-#define JRI_GetFieldLong(env, obj, fieldID)	\
-	(((*(env))->GetFieldLong)(env, JRI_GetFieldLong_op, obj, fieldID))
-
-#define JRI_GetFieldFloat(env, obj, fieldID)	\
-	(((*(env))->GetFieldFloat)(env, JRI_GetFieldFloat_op, obj, fieldID))
-
-#define JRI_GetFieldDouble(env, obj, fieldID)	\
-	(((*(env))->GetFieldDouble)(env, JRI_GetFieldDouble_op, obj, fieldID))
-
-#define JRI_SetField(env, obj, fieldID, value)	\
-	(((*(env))->SetField)(env, JRI_SetField_op, obj, fieldID, value))
-
-#define JRI_SetFieldBoolean(env, obj, fieldID, value)	\
-	(((*(env))->SetFieldBoolean)(env, JRI_SetFieldBoolean_op, obj, fieldID, value))
-
-#define JRI_SetFieldByte(env, obj, fieldID, value)	\
-	(((*(env))->SetFieldByte)(env, JRI_SetFieldByte_op, obj, fieldID, value))
-
-#define JRI_SetFieldChar(env, obj, fieldID, value)	\
-	(((*(env))->SetFieldChar)(env, JRI_SetFieldChar_op, obj, fieldID, value))
-
-#define JRI_SetFieldShort(env, obj, fieldID, value)	\
-	(((*(env))->SetFieldShort)(env, JRI_SetFieldShort_op, obj, fieldID, value))
-
-#define JRI_SetFieldInt(env, obj, fieldID, value)	\
-	(((*(env))->SetFieldInt)(env, JRI_SetFieldInt_op, obj, fieldID, value))
-
-#define JRI_SetFieldLong(env, obj, fieldID, value)	\
-	(((*(env))->SetFieldLong)(env, JRI_SetFieldLong_op, obj, fieldID, value))
-
-#define JRI_SetFieldFloat(env, obj, fieldID, value)	\
-	(((*(env))->SetFieldFloat)(env, JRI_SetFieldFloat_op, obj, fieldID, value))
-
-#define JRI_SetFieldDouble(env, obj, fieldID, value)	\
-	(((*(env))->SetFieldDouble)(env, JRI_SetFieldDouble_op, obj, fieldID, value))
-
-#define JRI_IsSubclassOf(env, a, b)	\
-	(((*(env))->IsSubclassOf)(env, JRI_IsSubclassOf_op, a, b))
-
-#define JRI_GetStaticMethodID(env, clazz, name, sig)	\
-	(((*(env))->GetStaticMethodID)(env, JRI_GetStaticMethodID_op, clazz, name, sig))
-
-#define JRI_CallStaticMethod(env)	((*(env))->CallStaticMethod)
-#define JRI_CallStaticMethodV(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodV)(env, JRI_CallStaticMethod_op_va_list, clazz, methodID, args))
-#define JRI_CallStaticMethodA(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodA)(env, JRI_CallStaticMethod_op_array, clazz, methodID, args))
-
-#define JRI_CallStaticMethodBoolean(env)	((*(env))->CallStaticMethodBoolean)
-#define JRI_CallStaticMethodBooleanV(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodBooleanV)(env, JRI_CallStaticMethodBoolean_op_va_list, clazz, methodID, args))
-#define JRI_CallStaticMethodBooleanA(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodBooleanA)(env, JRI_CallStaticMethodBoolean_op_array, clazz, methodID, args))
-
-#define JRI_CallStaticMethodByte(env)	((*(env))->CallStaticMethodByte)
-#define JRI_CallStaticMethodByteV(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodByteV)(env, JRI_CallStaticMethodByte_op_va_list, clazz, methodID, args))
-#define JRI_CallStaticMethodByteA(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodByteA)(env, JRI_CallStaticMethodByte_op_array, clazz, methodID, args))
-
-#define JRI_CallStaticMethodChar(env)	((*(env))->CallStaticMethodChar)
-#define JRI_CallStaticMethodCharV(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodCharV)(env, JRI_CallStaticMethodChar_op_va_list, clazz, methodID, args))
-#define JRI_CallStaticMethodCharA(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodCharA)(env, JRI_CallStaticMethodChar_op_array, clazz, methodID, args))
-
-#define JRI_CallStaticMethodShort(env)	((*(env))->CallStaticMethodShort)
-#define JRI_CallStaticMethodShortV(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodShortV)(env, JRI_CallStaticMethodShort_op_va_list, clazz, methodID, args))
-#define JRI_CallStaticMethodShortA(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodShortA)(env, JRI_CallStaticMethodShort_op_array, clazz, methodID, args))
-
-#define JRI_CallStaticMethodInt(env)	((*(env))->CallStaticMethodInt)
-#define JRI_CallStaticMethodIntV(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodIntV)(env, JRI_CallStaticMethodInt_op_va_list, clazz, methodID, args))
-#define JRI_CallStaticMethodIntA(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodIntA)(env, JRI_CallStaticMethodInt_op_array, clazz, methodID, args))
-
-#define JRI_CallStaticMethodLong(env)	((*(env))->CallStaticMethodLong)
-#define JRI_CallStaticMethodLongV(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodLongV)(env, JRI_CallStaticMethodLong_op_va_list, clazz, methodID, args))
-#define JRI_CallStaticMethodLongA(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodLongA)(env, JRI_CallStaticMethodLong_op_array, clazz, methodID, args))
-
-#define JRI_CallStaticMethodFloat(env)	((*(env))->CallStaticMethodFloat)
-#define JRI_CallStaticMethodFloatV(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodFloatV)(env, JRI_CallStaticMethodFloat_op_va_list, clazz, methodID, args))
-#define JRI_CallStaticMethodFloatA(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodFloatA)(env, JRI_CallStaticMethodFloat_op_array, clazz, methodID, args))
-
-#define JRI_CallStaticMethodDouble(env)	((*(env))->CallStaticMethodDouble)
-#define JRI_CallStaticMethodDoubleV(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodDoubleV)(env, JRI_CallStaticMethodDouble_op_va_list, clazz, methodID, args))
-#define JRI_CallStaticMethodDoubleA(env, clazz, methodID, args)	\
-	(((*(env))->CallStaticMethodDoubleA)(env, JRI_CallStaticMethodDouble_op_array, clazz, methodID, args))
-
-#define JRI_GetStaticFieldID(env, clazz, name, sig)	\
-	(((*(env))->GetStaticFieldID)(env, JRI_GetStaticFieldID_op, clazz, name, sig))
-
-#define JRI_GetStaticField(env, clazz, fieldID)	\
-	(((*(env))->GetStaticField)(env, JRI_GetStaticField_op, clazz, fieldID))
-
-#define JRI_GetStaticFieldBoolean(env, clazz, fieldID)	\
-	(((*(env))->GetStaticFieldBoolean)(env, JRI_GetStaticFieldBoolean_op, clazz, fieldID))
-
-#define JRI_GetStaticFieldByte(env, clazz, fieldID)	\
-	(((*(env))->GetStaticFieldByte)(env, JRI_GetStaticFieldByte_op, clazz, fieldID))
-
-#define JRI_GetStaticFieldChar(env, clazz, fieldID)	\
-	(((*(env))->GetStaticFieldChar)(env, JRI_GetStaticFieldChar_op, clazz, fieldID))
-
-#define JRI_GetStaticFieldShort(env, clazz, fieldID)	\
-	(((*(env))->GetStaticFieldShort)(env, JRI_GetStaticFieldShort_op, clazz, fieldID))
-
-#define JRI_GetStaticFieldInt(env, clazz, fieldID)	\
-	(((*(env))->GetStaticFieldInt)(env, JRI_GetStaticFieldInt_op, clazz, fieldID))
-
-#define JRI_GetStaticFieldLong(env, clazz, fieldID)	\
-	(((*(env))->GetStaticFieldLong)(env, JRI_GetStaticFieldLong_op, clazz, fieldID))
-
-#define JRI_GetStaticFieldFloat(env, clazz, fieldID)	\
-	(((*(env))->GetStaticFieldFloat)(env, JRI_GetStaticFieldFloat_op, clazz, fieldID))
-
-#define JRI_GetStaticFieldDouble(env, clazz, fieldID)	\
-	(((*(env))->GetStaticFieldDouble)(env, JRI_GetStaticFieldDouble_op, clazz, fieldID))
-
-#define JRI_SetStaticField(env, clazz, fieldID, value)	\
-	(((*(env))->SetStaticField)(env, JRI_SetStaticField_op, clazz, fieldID, value))
-
-#define JRI_SetStaticFieldBoolean(env, clazz, fieldID, value)	\
-	(((*(env))->SetStaticFieldBoolean)(env, JRI_SetStaticFieldBoolean_op, clazz, fieldID, value))
-
-#define JRI_SetStaticFieldByte(env, clazz, fieldID, value)	\
-	(((*(env))->SetStaticFieldByte)(env, JRI_SetStaticFieldByte_op, clazz, fieldID, value))
-
-#define JRI_SetStaticFieldChar(env, clazz, fieldID, value)	\
-	(((*(env))->SetStaticFieldChar)(env, JRI_SetStaticFieldChar_op, clazz, fieldID, value))
-
-#define JRI_SetStaticFieldShort(env, clazz, fieldID, value)	\
-	(((*(env))->SetStaticFieldShort)(env, JRI_SetStaticFieldShort_op, clazz, fieldID, value))
-
-#define JRI_SetStaticFieldInt(env, clazz, fieldID, value)	\
-	(((*(env))->SetStaticFieldInt)(env, JRI_SetStaticFieldInt_op, clazz, fieldID, value))
-
-#define JRI_SetStaticFieldLong(env, clazz, fieldID, value)	\
-	(((*(env))->SetStaticFieldLong)(env, JRI_SetStaticFieldLong_op, clazz, fieldID, value))
-
-#define JRI_SetStaticFieldFloat(env, clazz, fieldID, value)	\
-	(((*(env))->SetStaticFieldFloat)(env, JRI_SetStaticFieldFloat_op, clazz, fieldID, value))
-
-#define JRI_SetStaticFieldDouble(env, clazz, fieldID, value)	\
-	(((*(env))->SetStaticFieldDouble)(env, JRI_SetStaticFieldDouble_op, clazz, fieldID, value))
-
-#define JRI_NewString(env, unicode, len)	\
-	(((*(env))->NewString)(env, JRI_NewString_op, unicode, len))
-
-#define JRI_GetStringLength(env, string)	\
-	(((*(env))->GetStringLength)(env, JRI_GetStringLength_op, string))
-
-#define JRI_GetStringChars(env, string)	\
-	(((*(env))->GetStringChars)(env, JRI_GetStringChars_op, string))
-
-#define JRI_NewStringUTF(env, utf, len)	\
-	(((*(env))->NewStringUTF)(env, JRI_NewStringUTF_op, utf, len))
-
-#define JRI_GetStringUTFLength(env, string)	\
-	(((*(env))->GetStringUTFLength)(env, JRI_GetStringUTFLength_op, string))
-
-#define JRI_GetStringUTFChars(env, string)	\
-	(((*(env))->GetStringUTFChars)(env, JRI_GetStringUTFChars_op, string))
-
-#define JRI_NewScalarArray(env, length, elementSig, initialElements)	\
-	(((*(env))->NewScalarArray)(env, JRI_NewScalarArray_op, length, elementSig, initialElements))
-
-#define JRI_GetScalarArrayLength(env, array)	\
-	(((*(env))->GetScalarArrayLength)(env, JRI_GetScalarArrayLength_op, array))
-
-#define JRI_GetScalarArrayElements(env, array)	\
-	(((*(env))->GetScalarArrayElements)(env, JRI_GetScalarArrayElements_op, array))
-
-#define JRI_NewObjectArray(env, length, elementClass, initialElement)	\
-	(((*(env))->NewObjectArray)(env, JRI_NewObjectArray_op, length, elementClass, initialElement))
-
-#define JRI_GetObjectArrayLength(env, array)	\
-	(((*(env))->GetObjectArrayLength)(env, JRI_GetObjectArrayLength_op, array))
-
-#define JRI_GetObjectArrayElement(env, array, index)	\
-	(((*(env))->GetObjectArrayElement)(env, JRI_GetObjectArrayElement_op, array, index))
-
-#define JRI_SetObjectArrayElement(env, array, index, value)	\
-	(((*(env))->SetObjectArrayElement)(env, JRI_SetObjectArrayElement_op, array, index, value))
-
-#define JRI_RegisterNatives(env, clazz, nameAndSigArray, nativeProcArray)	\
-	(((*(env))->RegisterNatives)(env, JRI_RegisterNatives_op, clazz, nameAndSigArray, nativeProcArray))
-
-#define JRI_UnregisterNatives(env, clazz)	\
-	(((*(env))->UnregisterNatives)(env, JRI_UnregisterNatives_op, clazz))
-
-#define JRI_NewStringPlatform(env, string, len, encoding, encodingLength)	\
-	(((*(env))->NewStringPlatform)(env, JRI_NewStringPlatform_op, string, len, encoding, encodingLength))
-
-#define JRI_GetStringPlatformChars(env, string, encoding, encodingLength)	\
-	(((*(env))->GetStringPlatformChars)(env, JRI_GetStringPlatformChars_op, string, encoding, encodingLength))
-
-
-/*******************************************************************************
- * JRIEnv Interface
- ******************************************************************************/
-
-struct java_lang_ClassLoader;
-struct java_lang_Class;
-struct java_lang_Throwable;
-struct java_lang_Object;
-struct java_lang_String;
-
-struct JRIEnvInterface {
-	void*	reserved0;
-	void*	reserved1;
-	void*	reserved2;
-	void*	reserved3;
-	struct java_lang_Class*	(*FindClass)(JRIEnv* env, jint op, const char* a);
-	void	(*Throw)(JRIEnv* env, jint op, struct java_lang_Throwable* a);
-	void	(*ThrowNew)(JRIEnv* env, jint op, struct java_lang_Class* a, const char* b);
-	struct java_lang_Throwable*	(*ExceptionOccurred)(JRIEnv* env, jint op);
-	void	(*ExceptionDescribe)(JRIEnv* env, jint op);
-	void	(*ExceptionClear)(JRIEnv* env, jint op);
-	jglobal	(*NewGlobalRef)(JRIEnv* env, jint op, void* a);
-	void	(*DisposeGlobalRef)(JRIEnv* env, jint op, jglobal a);
-	void*	(*GetGlobalRef)(JRIEnv* env, jint op, jglobal a);
-	void	(*SetGlobalRef)(JRIEnv* env, jint op, jglobal a, void* b);
-	jbool	(*IsSameObject)(JRIEnv* env, jint op, void* a, void* b);
-	void*	(*NewObject)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, ...);
-	void*	(*NewObjectV)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, va_list c);
-	void*	(*NewObjectA)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, JRIValue* c);
-	struct java_lang_Class*	(*GetObjectClass)(JRIEnv* env, jint op, void* a);
-	jbool	(*IsInstanceOf)(JRIEnv* env, jint op, void* a, struct java_lang_Class* b);
-	jint	(*GetMethodID)(JRIEnv* env, jint op, struct java_lang_Class* a, const char* b, const char* c);
-	void*	(*CallMethod)(JRIEnv* env, jint op, void* a, jint b, ...);
-	void*	(*CallMethodV)(JRIEnv* env, jint op, void* a, jint b, va_list c);
-	void*	(*CallMethodA)(JRIEnv* env, jint op, void* a, jint b, JRIValue* c);
-	jbool	(*CallMethodBoolean)(JRIEnv* env, jint op, void* a, jint b, ...);
-	jbool	(*CallMethodBooleanV)(JRIEnv* env, jint op, void* a, jint b, va_list c);
-	jbool	(*CallMethodBooleanA)(JRIEnv* env, jint op, void* a, jint b, JRIValue* c);
-	jbyte	(*CallMethodByte)(JRIEnv* env, jint op, void* a, jint b, ...);
-	jbyte	(*CallMethodByteV)(JRIEnv* env, jint op, void* a, jint b, va_list c);
-	jbyte	(*CallMethodByteA)(JRIEnv* env, jint op, void* a, jint b, JRIValue* c);
-	jchar	(*CallMethodChar)(JRIEnv* env, jint op, void* a, jint b, ...);
-	jchar	(*CallMethodCharV)(JRIEnv* env, jint op, void* a, jint b, va_list c);
-	jchar	(*CallMethodCharA)(JRIEnv* env, jint op, void* a, jint b, JRIValue* c);
-	jshort	(*CallMethodShort)(JRIEnv* env, jint op, void* a, jint b, ...);
-	jshort	(*CallMethodShortV)(JRIEnv* env, jint op, void* a, jint b, va_list c);
-	jshort	(*CallMethodShortA)(JRIEnv* env, jint op, void* a, jint b, JRIValue* c);
-	jint	(*CallMethodInt)(JRIEnv* env, jint op, void* a, jint b, ...);
-	jint	(*CallMethodIntV)(JRIEnv* env, jint op, void* a, jint b, va_list c);
-	jint	(*CallMethodIntA)(JRIEnv* env, jint op, void* a, jint b, JRIValue* c);
-	jlong	(*CallMethodLong)(JRIEnv* env, jint op, void* a, jint b, ...);
-	jlong	(*CallMethodLongV)(JRIEnv* env, jint op, void* a, jint b, va_list c);
-	jlong	(*CallMethodLongA)(JRIEnv* env, jint op, void* a, jint b, JRIValue* c);
-	jfloat	(*CallMethodFloat)(JRIEnv* env, jint op, void* a, jint b, ...);
-	jfloat	(*CallMethodFloatV)(JRIEnv* env, jint op, void* a, jint b, va_list c);
-	jfloat	(*CallMethodFloatA)(JRIEnv* env, jint op, void* a, jint b, JRIValue* c);
-	jdouble	(*CallMethodDouble)(JRIEnv* env, jint op, void* a, jint b, ...);
-	jdouble	(*CallMethodDoubleV)(JRIEnv* env, jint op, void* a, jint b, va_list c);
-	jdouble	(*CallMethodDoubleA)(JRIEnv* env, jint op, void* a, jint b, JRIValue* c);
-	jint	(*GetFieldID)(JRIEnv* env, jint op, struct java_lang_Class* a, const char* b, const char* c);
-	void*	(*GetField)(JRIEnv* env, jint op, void* a, jint b);
-	jbool	(*GetFieldBoolean)(JRIEnv* env, jint op, void* a, jint b);
-	jbyte	(*GetFieldByte)(JRIEnv* env, jint op, void* a, jint b);
-	jchar	(*GetFieldChar)(JRIEnv* env, jint op, void* a, jint b);
-	jshort	(*GetFieldShort)(JRIEnv* env, jint op, void* a, jint b);
-	jint	(*GetFieldInt)(JRIEnv* env, jint op, void* a, jint b);
-	jlong	(*GetFieldLong)(JRIEnv* env, jint op, void* a, jint b);
-	jfloat	(*GetFieldFloat)(JRIEnv* env, jint op, void* a, jint b);
-	jdouble	(*GetFieldDouble)(JRIEnv* env, jint op, void* a, jint b);
-	void	(*SetField)(JRIEnv* env, jint op, void* a, jint b, void* c);
-	void	(*SetFieldBoolean)(JRIEnv* env, jint op, void* a, jint b, jbool c);
-	void	(*SetFieldByte)(JRIEnv* env, jint op, void* a, jint b, jbyte c);
-	void	(*SetFieldChar)(JRIEnv* env, jint op, void* a, jint b, jchar c);
-	void	(*SetFieldShort)(JRIEnv* env, jint op, void* a, jint b, jshort c);
-	void	(*SetFieldInt)(JRIEnv* env, jint op, void* a, jint b, jint c);
-	void	(*SetFieldLong)(JRIEnv* env, jint op, void* a, jint b, jlong c);
-	void	(*SetFieldFloat)(JRIEnv* env, jint op, void* a, jint b, jfloat c);
-	void	(*SetFieldDouble)(JRIEnv* env, jint op, void* a, jint b, jdouble c);
-	jbool	(*IsSubclassOf)(JRIEnv* env, jint op, struct java_lang_Class* a, struct java_lang_Class* b);
-	jint	(*GetStaticMethodID)(JRIEnv* env, jint op, struct java_lang_Class* a, const char* b, const char* c);
-	void*	(*CallStaticMethod)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, ...);
-	void*	(*CallStaticMethodV)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, va_list c);
-	void*	(*CallStaticMethodA)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, JRIValue* c);
-	jbool	(*CallStaticMethodBoolean)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, ...);
-	jbool	(*CallStaticMethodBooleanV)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, va_list c);
-	jbool	(*CallStaticMethodBooleanA)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, JRIValue* c);
-	jbyte	(*CallStaticMethodByte)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, ...);
-	jbyte	(*CallStaticMethodByteV)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, va_list c);
-	jbyte	(*CallStaticMethodByteA)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, JRIValue* c);
-	jchar	(*CallStaticMethodChar)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, ...);
-	jchar	(*CallStaticMethodCharV)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, va_list c);
-	jchar	(*CallStaticMethodCharA)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, JRIValue* c);
-	jshort	(*CallStaticMethodShort)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, ...);
-	jshort	(*CallStaticMethodShortV)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, va_list c);
-	jshort	(*CallStaticMethodShortA)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, JRIValue* c);
-	jint	(*CallStaticMethodInt)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, ...);
-	jint	(*CallStaticMethodIntV)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, va_list c);
-	jint	(*CallStaticMethodIntA)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, JRIValue* c);
-	jlong	(*CallStaticMethodLong)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, ...);
-	jlong	(*CallStaticMethodLongV)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, va_list c);
-	jlong	(*CallStaticMethodLongA)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, JRIValue* c);
-	jfloat	(*CallStaticMethodFloat)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, ...);
-	jfloat	(*CallStaticMethodFloatV)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, va_list c);
-	jfloat	(*CallStaticMethodFloatA)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, JRIValue* c);
-	jdouble	(*CallStaticMethodDouble)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, ...);
-	jdouble	(*CallStaticMethodDoubleV)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, va_list c);
-	jdouble	(*CallStaticMethodDoubleA)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, JRIValue* c);
-	jint	(*GetStaticFieldID)(JRIEnv* env, jint op, struct java_lang_Class* a, const char* b, const char* c);
-	void*	(*GetStaticField)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b);
-	jbool	(*GetStaticFieldBoolean)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b);
-	jbyte	(*GetStaticFieldByte)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b);
-	jchar	(*GetStaticFieldChar)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b);
-	jshort	(*GetStaticFieldShort)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b);
-	jint	(*GetStaticFieldInt)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b);
-	jlong	(*GetStaticFieldLong)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b);
-	jfloat	(*GetStaticFieldFloat)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b);
-	jdouble	(*GetStaticFieldDouble)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b);
-	void	(*SetStaticField)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, void* c);
-	void	(*SetStaticFieldBoolean)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, jbool c);
-	void	(*SetStaticFieldByte)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, jbyte c);
-	void	(*SetStaticFieldChar)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, jchar c);
-	void	(*SetStaticFieldShort)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, jshort c);
-	void	(*SetStaticFieldInt)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, jint c);
-	void	(*SetStaticFieldLong)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, jlong c);
-	void	(*SetStaticFieldFloat)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, jfloat c);
-	void	(*SetStaticFieldDouble)(JRIEnv* env, jint op, struct java_lang_Class* a, jint b, jdouble c);
-	struct java_lang_String*	(*NewString)(JRIEnv* env, jint op, const jchar* a, jint b);
-	jint	(*GetStringLength)(JRIEnv* env, jint op, struct java_lang_String* a);
-	const jchar*	(*GetStringChars)(JRIEnv* env, jint op, struct java_lang_String* a);
-	struct java_lang_String*	(*NewStringUTF)(JRIEnv* env, jint op, const jbyte* a, jint b);
-	jint	(*GetStringUTFLength)(JRIEnv* env, jint op, struct java_lang_String* a);
-	const jbyte*	(*GetStringUTFChars)(JRIEnv* env, jint op, struct java_lang_String* a);
-	void*	(*NewScalarArray)(JRIEnv* env, jint op, jint a, const char* b, const jbyte* c);
-	jint	(*GetScalarArrayLength)(JRIEnv* env, jint op, void* a);
-	jbyte*	(*GetScalarArrayElements)(JRIEnv* env, jint op, void* a);
-	void*	(*NewObjectArray)(JRIEnv* env, jint op, jint a, struct java_lang_Class* b, void* c);
-	jint	(*GetObjectArrayLength)(JRIEnv* env, jint op, void* a);
-	void*	(*GetObjectArrayElement)(JRIEnv* env, jint op, void* a, jint b);
-	void	(*SetObjectArrayElement)(JRIEnv* env, jint op, void* a, jint b, void* c);
-	void	(*RegisterNatives)(JRIEnv* env, jint op, struct java_lang_Class* a, char** b, void** c);
-	void	(*UnregisterNatives)(JRIEnv* env, jint op, struct java_lang_Class* a);
-	struct java_lang_Class*	(*DefineClass)(JRIEnv* env, jint op, struct java_lang_ClassLoader* a, jbyte* b, jsize bLen);
-	struct java_lang_String*	(*NewStringPlatform)(JRIEnv* env, jint op, const jbyte* a, jint b, const jbyte* c, jint d);
-	const jbyte*	(*GetStringPlatformChars)(JRIEnv* env, jint op, struct java_lang_String* a, const jbyte* b, jint c);
-};
-
-/*
-** ****************************************************************************
-** JRIEnv Operation IDs
-** ***************************************************************************
-*/
-
-typedef enum JRIEnvOperations {
-	JRI_Reserved0_op,
-	JRI_Reserved1_op,
-	JRI_Reserved2_op,
-	JRI_Reserved3_op,
-	JRI_FindClass_op,
-	JRI_Throw_op,
-	JRI_ThrowNew_op,
-	JRI_ExceptionOccurred_op,
-	JRI_ExceptionDescribe_op,
-	JRI_ExceptionClear_op,
-	JRI_NewGlobalRef_op,
-	JRI_DisposeGlobalRef_op,
-	JRI_GetGlobalRef_op,
-	JRI_SetGlobalRef_op,
-	JRI_IsSameObject_op,
-	JRI_NewObject_op,
-	JRI_NewObject_op_va_list,
-	JRI_NewObject_op_array,
-	JRI_GetObjectClass_op,
-	JRI_IsInstanceOf_op,
-	JRI_GetMethodID_op,
-	JRI_CallMethod_op,
-	JRI_CallMethod_op_va_list,
-	JRI_CallMethod_op_array,
-	JRI_CallMethodBoolean_op,
-	JRI_CallMethodBoolean_op_va_list,
-	JRI_CallMethodBoolean_op_array,
-	JRI_CallMethodByte_op,
-	JRI_CallMethodByte_op_va_list,
-	JRI_CallMethodByte_op_array,
-	JRI_CallMethodChar_op,
-	JRI_CallMethodChar_op_va_list,
-	JRI_CallMethodChar_op_array,
-	JRI_CallMethodShort_op,
-	JRI_CallMethodShort_op_va_list,
-	JRI_CallMethodShort_op_array,
-	JRI_CallMethodInt_op,
-	JRI_CallMethodInt_op_va_list,
-	JRI_CallMethodInt_op_array,
-	JRI_CallMethodLong_op,
-	JRI_CallMethodLong_op_va_list,
-	JRI_CallMethodLong_op_array,
-	JRI_CallMethodFloat_op,
-	JRI_CallMethodFloat_op_va_list,
-	JRI_CallMethodFloat_op_array,
-	JRI_CallMethodDouble_op,
-	JRI_CallMethodDouble_op_va_list,
-	JRI_CallMethodDouble_op_array,
-	JRI_GetFieldID_op,
-	JRI_GetField_op,
-	JRI_GetFieldBoolean_op,
-	JRI_GetFieldByte_op,
-	JRI_GetFieldChar_op,
-	JRI_GetFieldShort_op,
-	JRI_GetFieldInt_op,
-	JRI_GetFieldLong_op,
-	JRI_GetFieldFloat_op,
-	JRI_GetFieldDouble_op,
-	JRI_SetField_op,
-	JRI_SetFieldBoolean_op,
-	JRI_SetFieldByte_op,
-	JRI_SetFieldChar_op,
-	JRI_SetFieldShort_op,
-	JRI_SetFieldInt_op,
-	JRI_SetFieldLong_op,
-	JRI_SetFieldFloat_op,
-	JRI_SetFieldDouble_op,
-	JRI_IsSubclassOf_op,
-	JRI_GetStaticMethodID_op,
-	JRI_CallStaticMethod_op,
-	JRI_CallStaticMethod_op_va_list,
-	JRI_CallStaticMethod_op_array,
-	JRI_CallStaticMethodBoolean_op,
-	JRI_CallStaticMethodBoolean_op_va_list,
-	JRI_CallStaticMethodBoolean_op_array,
-	JRI_CallStaticMethodByte_op,
-	JRI_CallStaticMethodByte_op_va_list,
-	JRI_CallStaticMethodByte_op_array,
-	JRI_CallStaticMethodChar_op,
-	JRI_CallStaticMethodChar_op_va_list,
-	JRI_CallStaticMethodChar_op_array,
-	JRI_CallStaticMethodShort_op,
-	JRI_CallStaticMethodShort_op_va_list,
-	JRI_CallStaticMethodShort_op_array,
-	JRI_CallStaticMethodInt_op,
-	JRI_CallStaticMethodInt_op_va_list,
-	JRI_CallStaticMethodInt_op_array,
-	JRI_CallStaticMethodLong_op,
-	JRI_CallStaticMethodLong_op_va_list,
-	JRI_CallStaticMethodLong_op_array,
-	JRI_CallStaticMethodFloat_op,
-	JRI_CallStaticMethodFloat_op_va_list,
-	JRI_CallStaticMethodFloat_op_array,
-	JRI_CallStaticMethodDouble_op,
-	JRI_CallStaticMethodDouble_op_va_list,
-	JRI_CallStaticMethodDouble_op_array,
-	JRI_GetStaticFieldID_op,
-	JRI_GetStaticField_op,
-	JRI_GetStaticFieldBoolean_op,
-	JRI_GetStaticFieldByte_op,
-	JRI_GetStaticFieldChar_op,
-	JRI_GetStaticFieldShort_op,
-	JRI_GetStaticFieldInt_op,
-	JRI_GetStaticFieldLong_op,
-	JRI_GetStaticFieldFloat_op,
-	JRI_GetStaticFieldDouble_op,
-	JRI_SetStaticField_op,
-	JRI_SetStaticFieldBoolean_op,
-	JRI_SetStaticFieldByte_op,
-	JRI_SetStaticFieldChar_op,
-	JRI_SetStaticFieldShort_op,
-	JRI_SetStaticFieldInt_op,
-	JRI_SetStaticFieldLong_op,
-	JRI_SetStaticFieldFloat_op,
-	JRI_SetStaticFieldDouble_op,
-	JRI_NewString_op,
-	JRI_GetStringLength_op,
-	JRI_GetStringChars_op,
-	JRI_NewStringUTF_op,
-	JRI_GetStringUTFLength_op,
-	JRI_GetStringUTFChars_op,
-	JRI_NewScalarArray_op,
-	JRI_GetScalarArrayLength_op,
-	JRI_GetScalarArrayElements_op,
-	JRI_NewObjectArray_op,
-	JRI_GetObjectArrayLength_op,
-	JRI_GetObjectArrayElement_op,
-	JRI_SetObjectArrayElement_op,
-	JRI_RegisterNatives_op,
-	JRI_UnregisterNatives_op,
-	JRI_DefineClass_op,
-	JRI_NewStringPlatform_op,
-	JRI_GetStringPlatformChars_op
-} JRIEnvOperations;
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif /* __cplusplus */
-
-#endif /* JRI_H */
-/******************************************************************************/
diff --git a/third_party/gecko/include/jri_md.h b/third_party/gecko/include/jri_md.h
deleted file mode 100644
index f18093c..0000000
--- a/third_party/gecko/include/jri_md.h
+++ /dev/null
@@ -1,565 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: NPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is 
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or 
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the NPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the NPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/*******************************************************************************
- * Java Runtime Interface - Machine Dependent Types
- ******************************************************************************/
- 
-#ifndef JRI_MD_H
-#define JRI_MD_H
-
-#include <assert.h>
-#include "prtypes.h" /* Needed for HAS_LONG_LONG ifdefs */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*******************************************************************************
- * WHAT'S UP WITH THIS FILE?
- * 
- * This is where we define the mystical JRI_PUBLIC_API macro that works on all
- * platforms. If you're running with Visual C++, Symantec C, or Borland's 
- * development environment on the PC, you're all set. Or if you're on the Mac
- * with Metrowerks, Symantec or MPW with SC you're ok too. For UNIX it shouldn't
- * matter.
- *
- * On UNIX though you probably care about a couple of other symbols though:
- *	IS_LITTLE_ENDIAN must be defined for little-endian systems
- *	HAVE_LONG_LONG must be defined on systems that have 'long long' integers
- *	HAVE_ALIGNED_LONGLONGS must be defined if long-longs must be 8 byte aligned
- *	HAVE_ALIGNED_DOUBLES must be defined if doubles must be 8 byte aligned
- *	IS_64 must be defined on 64-bit machines (like Dec Alpha)
- ******************************************************************************/
-
-/* DLL Entry modifiers... */
-
-/* PC */
-#if defined(XP_OS2)
-#  ifdef XP_OS2_VACPP
-#	  define JRI_PUBLIC_API(ResultType)	    ResultType _Optlink
-#	  define JRI_PUBLIC_VAR(VarType)        VarType
-#     define JRI_CALLBACK
-#  else
-#	  define JRI_PUBLIC_API(ResultType)	    ResultType
-#	  define JRI_PUBLIC_VAR(VarType)        VarType
-#     define JRI_CALLBACK
-#  endif
-#elif defined(XP_WIN) || defined(_WINDOWS) || defined(WIN32) || defined(_WIN32)
-#	include <windows.h>
-#	if defined(_MSC_VER) || defined(__GNUC__)
-#		if defined(WIN32) || defined(_WIN32)
-#			define JRI_PUBLIC_API(ResultType)  __declspec(dllexport) ResultType
-#			define JRI_PUBLIC_VAR(VarType)	   VarType
-#			define JRI_PUBLIC_VAR_EXP(VarType) __declspec(dllexport) VarType
-#			define JRI_PUBLIC_VAR_IMP(VarType) __declspec(dllimport) VarType
-#			define JRI_NATIVE_STUB(ResultType) __declspec(dllexport) ResultType
-#			define JRI_CALLBACK
-#		else /* !_WIN32 */
-#		    if defined(_WINDLL)
-#			define JRI_PUBLIC_API(ResultType)	ResultType __cdecl __export __loadds 
-#			define JRI_PUBLIC_VAR(VarType)		VarType
-#			define JRI_PUBLIC_VAR_EXP(VarType)	JRI_PUBLIC_VAR(VarType)
-#			define JRI_PUBLIC_VAR_IMP(VarType)	JRI_PUBLIC_VAR(VarType)
-#			define JRI_NATIVE_STUB(ResultType)	ResultType __cdecl __loadds
-#			define JRI_CALLBACK			__loadds
-#		else /* !WINDLL */
-#			define JRI_PUBLIC_API(ResultType)	ResultType __cdecl __export
-#			define JRI_PUBLIC_VAR(VarType)		VarType
-#			define JRI_PUBLIC_VAR_EXP(VarType)	JRI_PUBLIC_VAR(VarType)
-#			define JRI_PUBLIC_VAR_IMP(VarType)	JRI_PUBLIC_VAR(VarType)
-#			define JRI_NATIVE_STUB(ResultType)	ResultType __cdecl __export
-#			define JRI_CALLBACK			__export
-#                   endif /* !WINDLL */
-#		endif /* !_WIN32 */
-#	elif defined(__BORLANDC__)
-#		if defined(WIN32) || defined(_WIN32)
-#			define JRI_PUBLIC_API(ResultType)	__export ResultType
-#			define JRI_PUBLIC_VAR(VarType)		VarType
-#			define JRI_PUBLIC_VAR_EXP(VarType)	__export VarType
-#			define JRI_PUBLIC_VAR_IMP(VarType)	__import VarType
-#			define JRI_NATIVE_STUB(ResultType)	 __export ResultType
-#			define JRI_CALLBACK
-#		else /* !_WIN32 */
-#			define JRI_PUBLIC_API(ResultType)	ResultType _cdecl _export _loadds 
-#			define JRI_PUBLIC_VAR(VarType)		VarType
-#			define JRI_PUBLIC_VAR_EXP(VarType)	__cdecl __export VarType
-#			define JRI_PUBLIC_VAR_IMP(VarType)	__cdecl __import VarType
-#			define JRI_NATIVE_STUB(ResultType)	ResultType _cdecl _loadds
-#			define JRI_CALLBACK			_loadds
-#		endif
-#	else
-#		error Unsupported PC development environment.	
-#	endif
-#	ifndef IS_LITTLE_ENDIAN
-#		define IS_LITTLE_ENDIAN
-#	endif
-
-/* Mac */
-#elif defined (macintosh) || Macintosh || THINK_C
-#	if defined(__MWERKS__)				/* Metrowerks */
-#		if !__option(enumsalwaysint)
-#			error You need to define 'Enums Always Int' for your project.
-#		endif
-#		if defined(TARGET_CPU_68K) && !TARGET_RT_MAC_CFM 
-#			if !__option(fourbyteints) 
-#				error You need to define 'Struct Alignment: 68k' for your project.
-#			endif
-#		endif /* !GENERATINGCFM */
-#		define JRI_PUBLIC_API(ResultType)	__declspec(export) ResultType
-#		define JRI_PUBLIC_VAR(VarType)		JRI_PUBLIC_API(VarType)
-#		define JRI_PUBLIC_VAR_EXP(VarType)	JRI_PUBLIC_API(VarType)
-#		define JRI_PUBLIC_VAR_IMP(VarType)	JRI_PUBLIC_API(VarType)
-#		define JRI_NATIVE_STUB(ResultType)	JRI_PUBLIC_API(ResultType)
-#	elif defined(__SC__)				/* Symantec */
-#		error What are the Symantec defines? (warren@netscape.com)
-#	elif macintosh && applec			/* MPW */
-#		error Please upgrade to the latest MPW compiler (SC).
-#	else
-#		error Unsupported Mac development environment.
-#	endif
-#	define JRI_CALLBACK
-
-/* Unix or else */
-#else
-#	define JRI_PUBLIC_API(ResultType)		ResultType
-#   define JRI_PUBLIC_VAR(VarType)          VarType
-#   define JRI_PUBLIC_VAR_EXP(VarType)		JRI_PUBLIC_VAR(VarType)
-#   define JRI_PUBLIC_VAR_IMP(VarType)		JRI_PUBLIC_VAR(VarType)
-#   define JRI_NATIVE_STUB(ResultType)		ResultType
-#	define JRI_CALLBACK
-#endif
-
-#ifndef FAR		/* for non-Win16 */
-#define FAR
-#endif
-
-/******************************************************************************/
-
-/* Java Scalar Types */
-
-#if 0	/* now in jni.h */
-typedef short			jchar;
-typedef short			jshort;
-typedef float			jfloat;
-typedef double			jdouble;
-typedef juint			jsize;
-#endif
-
-/* moved from jni.h -- Sun's new jni.h doesn't have this anymore */
-#ifdef __cplusplus
-typedef class _jobject *jref;
-#else
-typedef struct _jobject *jref;
-#endif
-
-typedef unsigned char	jbool;
-typedef signed char	jbyte;
-#ifdef IS_64 /* XXX ok for alpha, but not right on all 64-bit architectures */
-typedef unsigned int	juint;
-typedef int				jint;
-#else
-typedef unsigned long	juint;
-typedef long			jint;
-#endif
-
-/*******************************************************************************
- * jlong : long long (64-bit signed integer type) support.
- ******************************************************************************/
-
-/*
-** Bit masking macros.  (n must be <= 31 to be portable)
-*/
-#define JRI_BIT(n)			((juint)1 << (n))
-#define JRI_BITMASK(n)		(JRI_BIT(n) - 1)
-
-#ifdef HAVE_LONG_LONG
-
-#ifdef OSF1
-
-/* long is default 64-bit on OSF1, -std1 does not allow long long */
-typedef long                  jlong;
-typedef unsigned long         julong;
-#define jlong_MAXINT          0x7fffffffffffffffL
-#define jlong_MININT          0x8000000000000000L
-#define jlong_ZERO            0x0L
-
-#elif (defined(WIN32) || defined(_WIN32))
-
-typedef LONGLONG              jlong;
-typedef DWORDLONG             julong;
-#define jlong_MAXINT          0x7fffffffffffffffi64
-#define jlong_MININT          0x8000000000000000i64
-#define jlong_ZERO            0x0i64
-
-#else
-
-typedef long long             jlong;
-typedef unsigned long long    julong;
-#define jlong_MAXINT          0x7fffffffffffffffLL
-#define jlong_MININT          0x8000000000000000LL
-#define jlong_ZERO            0x0LL
-
-#endif
-
-#define jlong_IS_ZERO(a)	((a) == 0)
-#define jlong_EQ(a, b)		((a) == (b))
-#define jlong_NE(a, b)		((a) != (b))
-#define jlong_GE_ZERO(a)	((a) >= 0)
-#define jlong_CMP(a, op, b)	((a) op (b))
-
-#define jlong_AND(r, a, b)	((r) = (a) & (b))
-#define jlong_OR(r, a, b)	((r) = (a) | (b))
-#define jlong_XOR(r, a, b)	((r) = (a) ^ (b))
-#define jlong_OR2(r, a)		((r) = (r) | (a))
-#define jlong_NOT(r, a)		((r) = ~(a))
-
-#define jlong_NEG(r, a)		((r) = -(a))
-#define jlong_ADD(r, a, b)	((r) = (a) + (b))
-#define jlong_SUB(r, a, b)	((r) = (a) - (b))
-
-#define jlong_MUL(r, a, b)	((r) = (a) * (b))
-#define jlong_DIV(r, a, b)	((r) = (a) / (b))
-#define jlong_MOD(r, a, b)	((r) = (a) % (b))
-
-#define jlong_SHL(r, a, b)	((r) = (a) << (b))
-#define jlong_SHR(r, a, b)	((r) = (a) >> (b))
-#define jlong_USHR(r, a, b)	((r) = (julong)(a) >> (b))
-#define jlong_ISHL(r, a, b)	((r) = ((jlong)(a)) << (b))
-
-#define jlong_L2I(i, l)		((i) = (int)(l))
-#define jlong_L2UI(ui, l)	((ui) =(unsigned int)(l))
-#define jlong_L2F(f, l)		((f) = (l))
-#define jlong_L2D(d, l)		((d) = (l))
-
-#define jlong_I2L(l, i)		((l) = (i))
-#define jlong_UI2L(l, ui)	((l) = (ui))
-#define jlong_F2L(l, f)		((l) = (f))
-#define jlong_D2L(l, d)		((l) = (d))
-
-#define jlong_UDIVMOD(qp, rp, a, b)  \
-    (*(qp) = ((julong)(a) / (b)), \
-     *(rp) = ((julong)(a) % (b)))
-
-#else  /* !HAVE_LONG_LONG */
-
-typedef struct {
-#ifdef IS_LITTLE_ENDIAN
-    juint lo, hi;
-#else
-    juint hi, lo;
-#endif
-} jlong;
-typedef jlong				julong;
-
-extern jlong jlong_MAXINT, jlong_MININT, jlong_ZERO;
-
-#define jlong_IS_ZERO(a)	(((a).hi == 0) && ((a).lo == 0))
-#define jlong_EQ(a, b)		(((a).hi == (b).hi) && ((a).lo == (b).lo))
-#define jlong_NE(a, b)		(((a).hi != (b).hi) || ((a).lo != (b).lo))
-#define jlong_GE_ZERO(a)	(((a).hi >> 31) == 0)
-
-/*
- * NB: jlong_CMP and jlong_UCMP work only for strict relationals (<, >).
- */
-#define jlong_CMP(a, op, b)	(((int32)(a).hi op (int32)(b).hi) ||          \
-				 (((a).hi == (b).hi) && ((a).lo op (b).lo)))
-#define jlong_UCMP(a, op, b)	(((a).hi op (b).hi) ||                    \
-				 (((a).hi == (b).hi) && ((a).lo op (b).lo)))
-
-#define jlong_AND(r, a, b)	((r).lo = (a).lo & (b).lo,                    \
-				 (r).hi = (a).hi & (b).hi)
-#define jlong_OR(r, a, b)	((r).lo = (a).lo | (b).lo,                    \
-				 (r).hi = (a).hi | (b).hi)
-#define jlong_XOR(r, a, b)	((r).lo = (a).lo ^ (b).lo,                    \
-				 (r).hi = (a).hi ^ (b).hi)
-#define jlong_OR2(r, a)		((r).lo = (r).lo | (a).lo,                    \
-				 (r).hi = (r).hi | (a).hi)
-#define jlong_NOT(r, a)		((r).lo = ~(a).lo,	                          \
-				 (r).hi = ~(a).hi)
-
-#define jlong_NEG(r, a)		((r).lo = -(int32)(a).lo,                     \
-				 (r).hi = -(int32)(a).hi - ((r).lo != 0))
-#define jlong_ADD(r, a, b) {                                              \
-    jlong _a, _b;                                                         \
-    _a = a; _b = b;                                                       \
-    (r).lo = _a.lo + _b.lo;                                               \
-    (r).hi = _a.hi + _b.hi + ((r).lo < _b.lo);                            \
-}
-
-#define jlong_SUB(r, a, b) {                                              \
-    jlong _a, _b;                                                         \
-    _a = a; _b = b;                                                       \
-    (r).lo = _a.lo - _b.lo;                                               \
-    (r).hi = _a.hi - _b.hi - (_a.lo < _b.lo);                             \
-}                                                                         \
-
-/*
- * Multiply 64-bit operands a and b to get 64-bit result r.
- * First multiply the low 32 bits of a and b to get a 64-bit result in r.
- * Then add the outer and inner products to r.hi.
- */
-#define jlong_MUL(r, a, b) {                                              \
-    jlong _a, _b;                                                         \
-    _a = a; _b = b;                                                       \
-    jlong_MUL32(r, _a.lo, _b.lo);                                         \
-    (r).hi += _a.hi * _b.lo + _a.lo * _b.hi;                              \
-}
-
-/* XXX _jlong_lo16(a) = ((a) << 16 >> 16) is better on some archs (not on mips) */
-#define _jlong_lo16(a)		((a) & JRI_BITMASK(16))
-#define _jlong_hi16(a)		((a) >> 16)
-
-/*
- * Multiply 32-bit operands a and b to get 64-bit result r.
- * Use polynomial expansion based on primitive field element (1 << 16).
- */
-#define jlong_MUL32(r, a, b) {                                            \
-     juint _a1, _a0, _b1, _b0, _y0, _y1, _y2, _y3;                        \
-     _a1 = _jlong_hi16(a), _a0 = _jlong_lo16(a);                          \
-     _b1 = _jlong_hi16(b), _b0 = _jlong_lo16(b);                          \
-     _y0 = _a0 * _b0;                                                     \
-     _y1 = _a0 * _b1;                                                     \
-     _y2 = _a1 * _b0;                                                     \
-     _y3 = _a1 * _b1;                                                     \
-     _y1 += _jlong_hi16(_y0);                   /* can't carry */         \
-     _y1 += _y2;                                /* might carry */         \
-     if (_y1 < _y2) _y3 += 1 << 16;             /* propagate */           \
-     (r).lo = (_jlong_lo16(_y1) << 16) + _jlong_lo16(_y0);                \
-     (r).hi = _y3 + _jlong_hi16(_y1);                                     \
-}
-
-/*
- * Divide 64-bit unsigned operand a by 64-bit unsigned operand b, setting *qp
- * to the 64-bit unsigned quotient, and *rp to the 64-bit unsigned remainder.
- * Minimize effort if one of qp and rp is null.
- */
-#define jlong_UDIVMOD(qp, rp, a, b)	jlong_udivmod(qp, rp, a, b)
-
-extern JRI_PUBLIC_API(void)
-jlong_udivmod(julong *qp, julong *rp, julong a, julong b);
-
-#define jlong_DIV(r, a, b) {                                              \
-    jlong _a, _b;                                                         \
-    juint _negative = (int32)(a).hi < 0;                                  \
-    if (_negative) {                                                      \
-	jlong_NEG(_a, a);                                                     \
-    } else {                                                              \
-	_a = a;                                                               \
-    }                                                                     \
-    if ((int32)(b).hi < 0) {                                              \
-	_negative ^= 1;                                                       \
-	jlong_NEG(_b, b);                                                     \
-    } else {                                                              \
-	_b = b;                                                               \
-    }                                                                     \
-    jlong_UDIVMOD(&(r), 0, _a, _b);                                       \
-    if (_negative)                                                        \
-	jlong_NEG(r, r);                                                      \
-}
-
-#define jlong_MOD(r, a, b) {                                              \
-    jlong _a, _b;                                                         \
-    juint _negative = (int32)(a).hi < 0;                                  \
-    if (_negative) {                                                      \
-	jlong_NEG(_a, a);                                                     \
-    } else {                                                              \
-	_a = a;                                                               \
-    }                                                                     \
-    if ((int32)(b).hi < 0) {                                              \
-	jlong_NEG(_b, b);                                                     \
-    } else {                                                              \
-	_b = b;                                                               \
-    }                                                                     \
-    jlong_UDIVMOD(0, &(r), _a, _b);                                       \
-    if (_negative)                                                        \
-	jlong_NEG(r, r);                                                      \
-}
-
-/*
- * NB: b is a juint, not jlong or julong, for the shift ops.
- */
-#define jlong_SHL(r, a, b) {                                              \
-    if (b) {                                                              \
-	jlong _a;                                                             \
-        _a = a;                                                           \
-        if ((b) < 32) {                                                   \
-	    (r).lo = _a.lo << (b);                                            \
-	    (r).hi = (_a.hi << (b)) | (_a.lo >> (32 - (b)));                  \
-	} else {                                                              \
-	    (r).lo = 0;                                                       \
-	    (r).hi = _a.lo << ((b) & 31);                                     \
-	}                                                                     \
-    } else {                                                              \
-	(r) = (a);                                                            \
-    }                                                                     \
-}
-
-/* a is an int32, b is int32, r is jlong */
-#define jlong_ISHL(r, a, b) {                                             \
-    if (b) {                                                              \
-	jlong _a;                                                             \
-	_a.lo = (a);                                                          \
-	_a.hi = 0;                                                            \
-        if ((b) < 32) {                                                   \
-	    (r).lo = (a) << (b);                                              \
-	    (r).hi = ((a) >> (32 - (b)));                                     \
-	} else {                                                              \
-	    (r).lo = 0;                                                       \
-	    (r).hi = (a) << ((b) & 31);                                       \
-	}                                                                     \
-    } else {                                                              \
-	(r).lo = (a);                                                         \
-	(r).hi = 0;                                                           \
-    }                                                                     \
-}
-
-#define jlong_SHR(r, a, b) {                                              \
-    if (b) {                                                              \
-	jlong _a;                                                             \
-        _a = a;                                                           \
-	if ((b) < 32) {                                                       \
-	    (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> (b));                  \
-	    (r).hi = (int32)_a.hi >> (b);                                     \
-	} else {                                                              \
-	    (r).lo = (int32)_a.hi >> ((b) & 31);                              \
-	    (r).hi = (int32)_a.hi >> 31;                                      \
-	}                                                                     \
-    } else {                                                              \
-	(r) = (a);                                                            \
-    }                                                                     \
-}
-
-#define jlong_USHR(r, a, b) {                                             \
-    if (b) {                                                              \
-	jlong _a;                                                             \
-        _a = a;                                                           \
-	if ((b) < 32) {                                                       \
-	    (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> (b));                  \
-	    (r).hi = _a.hi >> (b);                                            \
-	} else {                                                              \
-	    (r).lo = _a.hi >> ((b) & 31);                                     \
-	    (r).hi = 0;                                                       \
-	}                                                                     \
-    } else {                                                              \
-	(r) = (a);                                                            \
-    }                                                                     \
-}
-
-#define jlong_L2I(i, l)		((i) = (l).lo)
-#define jlong_L2UI(ui, l)	((ui) = (l).lo)
-#define jlong_L2F(f, l)		{ double _d; jlong_L2D(_d, l); (f) = (float) _d; }
-
-#define jlong_L2D(d, l) {                                                 \
-    int32 _negative;                                                      \
-    jlong _absval;                                                        \
-                                                                          \
-    _negative = (l).hi >> 31;                                             \
-    if (_negative) {                                                      \
-	jlong_NEG(_absval, l);                                                \
-    } else {                                                              \
-	_absval = l;                                                          \
-    }                                                                     \
-    (d) = (double)_absval.hi * 4.294967296e9 + _absval.lo;                \
-    if (_negative)                                                        \
-	(d) = -(d);                                                           \
-}
-
-#define jlong_I2L(l, i)		((l).hi = (i) >> 31, (l).lo = (i))
-#define jlong_UI2L(l, ui)	((l).hi = 0, (l).lo = (ui))
-#define jlong_F2L(l, f)		{ double _d = (double) f; jlong_D2L(l, _d); }
-
-#define jlong_D2L(l, d) {                                                 \
-    int _negative;                                                        \
-    double _absval, _d_hi;                                                \
-    jlong _lo_d;                                                          \
-                                                                          \
-    _negative = ((d) < 0);                                                \
-    _absval = _negative ? -(d) : (d);                                     \
-                                                                          \
-    (l).hi = (juint)(_absval / 4.294967296e9);                            \
-    (l).lo = 0;                                                           \
-    jlong_L2D(_d_hi, l);                                                  \
-    _absval -= _d_hi;                                                     \
-    _lo_d.hi = 0;                                                         \
-    if (_absval < 0) {                                                    \
-	_lo_d.lo = (juint) -_absval;                                          \
-	jlong_SUB(l, l, _lo_d);                                               \
-    } else {                                                              \
-	_lo_d.lo = (juint) _absval;                                           \
-	jlong_ADD(l, l, _lo_d);                                               \
-    }                                                                     \
-                                                                          \
-    if (_negative)                                                        \
-	jlong_NEG(l, l);                                                      \
-}
-
-#endif /* !HAVE_LONG_LONG */
-
-/******************************************************************************/
-
-#ifdef HAVE_ALIGNED_LONGLONGS
-#define JRI_GET_INT64(_t,_addr) ( ((_t).x[0] = ((jint*)(_addr))[0]), \
-                              ((_t).x[1] = ((jint*)(_addr))[1]),      \
-                              (_t).l )
-#define JRI_SET_INT64(_t, _addr, _v) ( (_t).l = (_v),                \
-                                   ((jint*)(_addr))[0] = (_t).x[0], \
-                                   ((jint*)(_addr))[1] = (_t).x[1] )
-#else
-#define JRI_GET_INT64(_t,_addr) (*(jlong*)(_addr))
-#define JRI_SET_INT64(_t, _addr, _v) (*(jlong*)(_addr) = (_v))
-#endif
-
-/* If double's must be aligned on doubleword boundaries then define this */
-#ifdef HAVE_ALIGNED_DOUBLES
-#define JRI_GET_DOUBLE(_t,_addr) ( ((_t).x[0] = ((jint*)(_addr))[0]), \
-                               ((_t).x[1] = ((jint*)(_addr))[1]),      \
-                               (_t).d )
-#define JRI_SET_DOUBLE(_t, _addr, _v) ( (_t).d = (_v),                \
-                                    ((jint*)(_addr))[0] = (_t).x[0], \
-                                    ((jint*)(_addr))[1] = (_t).x[1] )
-#else
-#define JRI_GET_DOUBLE(_t,_addr) (*(jdouble*)(_addr))
-#define JRI_SET_DOUBLE(_t, _addr, _v) (*(jdouble*)(_addr) = (_v))
-#endif
-
-/******************************************************************************/
-#ifdef __cplusplus
-}
-#endif
-#endif /* JRI_MD_H */
-/******************************************************************************/
diff --git a/third_party/gecko/include/jritypes.h b/third_party/gecko/include/jritypes.h
deleted file mode 100644
index ddeb633..0000000
--- a/third_party/gecko/include/jritypes.h
+++ /dev/null
@@ -1,243 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: NPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is 
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or 
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the NPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the NPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/*******************************************************************************
- * Java Runtime Interface
- ******************************************************************************/
-
-#ifndef JRITYPES_H
-#define JRITYPES_H
-
-#include "jri_md.h"
-#include "jni.h"
-#include <stddef.h>
-#include <stdlib.h>
-#include <stdarg.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*******************************************************************************
- * Types
- ******************************************************************************/
-
-struct JRIEnvInterface;
-
-typedef void*		JRIRef;
-typedef void*		JRIGlobalRef;
-
-typedef jint		JRIFieldID;
-typedef jint		JRIMethodID;
-
-/* synonyms: */
-typedef JRIGlobalRef	jglobal;
-
-typedef union JRIValue {
-	jbool			z;
-	jbyte			b;
-	jchar			c;
-	jshort			s;
-	jint			i;
-	jlong			l;
-	jfloat			f;
-	jdouble			d;
-	jref			r;
-} JRIValue;
-
-typedef enum JRIBoolean {
-    JRIFalse		= 0,
-    JRITrue			= 1
-} JRIBoolean;
-
-typedef enum JRIConstant {
-	JRIUninitialized	= -1
-} JRIConstant;
-
-/* convenience types (these must be distinct struct types for c++ overloading): */
-#if 0	/* now in jni.h */
-typedef struct jbooleanArrayStruct*		jbooleanArray;
-typedef struct jbyteArrayStruct*		jbyteArray;
-typedef struct jcharArrayStruct*		jcharArray;
-typedef struct jshortArrayStruct*		jshortArray;
-typedef struct jintArrayStruct*			jintArray;
-typedef struct jlongArrayStruct*		jlongArray;
-typedef struct jfloatArrayStruct*		jfloatArray;
-typedef struct jdoubleArrayStruct*		jdoubleArray;
-typedef struct jobjectArrayStruct*		jobjectArray;
-#endif
-typedef struct jstringArrayStruct*		jstringArray;
-typedef struct jarrayArrayStruct*		jarrayArray;
-
-#define JRIConstructorMethodName	"<init>"
-
-/*******************************************************************************
- * Signature Construction Macros
- ******************************************************************************/
-
-/*
-** These macros can be used to construct signature strings. Hopefully their names
-** are a little easier to remember than the single character they correspond to.
-** For example, to specify the signature of the method:
-**
-**	public int read(byte b[], int off, int len);
-**
-** you could write something like this in C:
-**
-**	char* readSig = JRISigMethod(JRISigArray(JRISigByte)
-**								 JRISigInt
-**								 JRISigInt) JRISigInt;
-**
-** Of course, don't put commas between the types.
-*/
-#define JRISigArray(T)		"[" T
-#define JRISigByte			"B"
-#define JRISigChar			"C"
-#define JRISigClass(name)	"L" name ";"
-#define JRISigFloat			"F"
-#define JRISigDouble		"D"
-#define JRISigMethod(args)	"(" args ")"
-#define JRISigNoArgs		""
-#define JRISigInt			"I"
-#define JRISigLong			"J"
-#define JRISigShort			"S"
-#define JRISigVoid			"V"
-#define JRISigBoolean		"Z"
-
-/*******************************************************************************
- * Environments
- ******************************************************************************/
-
-extern JRI_PUBLIC_API(const struct JRIEnvInterface**)
-JRI_GetCurrentEnv(void);
-
-/*******************************************************************************
- * Specific Scalar Array Types
- ******************************************************************************/
-
-/*
-** The JRI Native Method Interface does not support boolean arrays. This
-** is to allow Java runtime implementations to optimize boolean array
-** storage. Using the ScalarArray operations on boolean arrays is bound
-** to fail, so convert any boolean arrays to byte arrays in Java before
-** passing them to a native method.
-*/
-
-#define JRI_NewByteArray(env, length, initialValues)	\
-	JRI_NewScalarArray(env, length, JRISigByte, (jbyte*)(initialValues))
-#define JRI_GetByteArrayLength(env, array)	\
-	JRI_GetScalarArrayLength(env, array)
-#define JRI_GetByteArrayElements(env, array)	\
-	JRI_GetScalarArrayElements(env, array)
-
-#define JRI_NewCharArray(env, length, initialValues)	\
-	JRI_NewScalarArray(env, ((length) * sizeof(jchar)), JRISigChar, (jbyte*)(initialValues))
-#define JRI_GetCharArrayLength(env, array)	\
-	JRI_GetScalarArrayLength(env, array)
-#define JRI_GetCharArrayElements(env, array)		   \
-	((jchar*)JRI_GetScalarArrayElements(env, array))
-
-#define JRI_NewShortArray(env, length, initialValues)	\
-	JRI_NewScalarArray(env, ((length) * sizeof(jshort)), JRISigShort, (jbyte*)(initialValues))
-#define JRI_GetShortArrayLength(env, array)	\
-	JRI_GetScalarArrayLength(env, array)
-#define JRI_GetShortArrayElements(env, array)		   \
-	((jshort*)JRI_GetScalarArrayElements(env, array))
-
-#define JRI_NewIntArray(env, length, initialValues)	\
-	JRI_NewScalarArray(env, ((length) * sizeof(jint)), JRISigInt, (jbyte*)(initialValues))
-#define JRI_GetIntArrayLength(env, array)	\
-	JRI_GetScalarArrayLength(env, array)
-#define JRI_GetIntArrayElements(env, array)		   \
-	((jint*)JRI_GetScalarArrayElements(env, array))
-
-#define JRI_NewLongArray(env, length, initialValues)	\
-	JRI_NewScalarArray(env, ((length) * sizeof(jlong)), JRISigLong, (jbyte*)(initialValues))
-#define JRI_GetLongArrayLength(env, array)	\
-	JRI_GetScalarArrayLength(env, array)
-#define JRI_GetLongArrayElements(env, array)		   \
-	((jlong*)JRI_GetScalarArrayElements(env, array))
-
-#define JRI_NewFloatArray(env, length, initialValues)	\
-	JRI_NewScalarArray(env, ((length) * sizeof(jfloat)), JRISigFloat, (jbyte*)(initialValues))
-#define JRI_GetFloatArrayLength(env, array)	\
-	JRI_GetScalarArrayLength(env, array)
-#define JRI_GetFloatArrayElements(env, array)		   \
-	((jfloat*)JRI_GetScalarArrayElements(env, array))
-
-#define JRI_NewDoubleArray(env, length, initialValues)	\
-	JRI_NewScalarArray(env, ((length) * sizeof(jdouble)), JRISigDouble, (jbyte*)(initialValues))
-#define JRI_GetDoubleArrayLength(env, array)	\
-	JRI_GetScalarArrayLength(env, array)
-#define JRI_GetDoubleArrayElements(env, array)		   \
-	((jdouble*)JRI_GetScalarArrayElements(env, array))
-
-/******************************************************************************/
-/*
-** JDK Stuff -- This stuff is still needed while we're using the JDK
-** dynamic linking strategy to call native methods.
-*/
-
-typedef union JRI_JDK_stack_item {
-    /* Non pointer items */
-    jint           i;
-    jfloat         f;
-    jint           o;
-    /* Pointer items */
-    void          *h;
-    void          *p;
-    unsigned char *addr;
-#ifdef IS_64
-    double         d;
-    long           l;		/* == 64bits! */
-#endif
-} JRI_JDK_stack_item;
-
-typedef union JRI_JDK_Java8Str {
-    jint x[2];
-    jdouble d;
-    jlong l;
-    void *p;
-    float f;
-} JRI_JDK_Java8;
-
-/******************************************************************************/
-#ifdef __cplusplus
-}
-#endif
-#endif /* JRITYPES_H */
-/******************************************************************************/
diff --git a/third_party/gecko/include/npapi.h b/third_party/gecko/include/npapi.h
deleted file mode 100644
index ad9795e..0000000
--- a/third_party/gecko/include/npapi.h
+++ /dev/null
@@ -1,726 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-
-/*
- *  npapi.h $Revision: 3.40 $
- *  Netscape client plug-in API spec
- */
-
-#ifndef _NPAPI_H_
-#define _NPAPI_H_
-
-#ifdef __OS2__
-#pragma pack(1)
-#endif
-
-#include "prtypes.h"
-/* Copied from xp_core.h */
-/* removed #ifdef for hpux defined in /usr/include/model.h */
-#ifndef XP_MAC
-#ifndef _INT16
-#define _INT16
-#endif
-#ifndef _INT32
-#define _INT32
-#endif
-#ifndef _UINT16
-#define _UINT16
-#endif
-#ifndef _UINT32
-#define _UINT32
-#endif
-#endif
-
-/* 
- * NO_NSPR_10_SUPPORT disables the inclusion 
- * of obsolete/protypes.h, whose int16, uint16, 
- * int32, and uint32 typedefs conflict with those 
- * in this file. 
- */ 
-#ifndef NO_NSPR_10_SUPPORT
-#define NO_NSPR_10_SUPPORT
-#endif
-#ifdef OJI
-#include "jri.h"                /* Java Runtime Interface */
-#endif
-
-#if defined (__OS2__ ) || defined (OS2)
-#	ifndef XP_OS2
-#		define XP_OS2 1
-#	endif /* XP_OS2 */
-#endif /* __OS2__ */
-
-#ifdef _WINDOWS
-#	include <windef.h>
-#	ifndef XP_WIN
-#		define XP_WIN 1
-#	endif /* XP_WIN */
-#endif /* _WINDOWS */
-
-#ifdef __MWERKS__
-#	define _declspec __declspec
-#	ifdef macintosh
-#		ifndef XP_MAC
-#			define XP_MAC 1
-#		endif /* XP_MAC */
-#	endif /* macintosh */
-#	ifdef __INTEL__
-#		undef NULL
-#		ifndef XP_WIN
-#			define XP_WIN 1
-#		endif /* XP_WIN */
-#	endif /* __INTEL__ */
-#endif /* __MWERKS__ */
-
-#if defined(XP_MAC) || defined(XP_MACOSX)
-	#include <Quickdraw.h>
-	#include <Events.h>
-#endif
-
-#if defined(XP_UNIX) 
-#	include <stdio.h>
-#	if defined(MOZ_X11)
-#		include <X11/Xlib.h>
-#		include <X11/Xutil.h>
-#	endif
-#endif
-
-/*----------------------------------------------------------------------*/
-/*                        Plugin Version Constants                      */
-/*----------------------------------------------------------------------*/
-
-#define NP_VERSION_MAJOR 0
-#define NP_VERSION_MINOR 16
-
-
-/* The OS/2 version of Netscape uses RC_DATA to define the
-   mime types, file extensions, etc that are required.
-   Use a vertical bar to separate types, end types with \0.
-   FileVersion and ProductVersion are 32bit ints, all other
-   entries are strings the MUST be terminated wwith a \0.
-
-AN EXAMPLE:
-
-RCDATA NP_INFO_ProductVersion { 1,0,0,1,}
-
-RCDATA NP_INFO_MIMEType    { "video/x-video|",
-                             "video/x-flick\0" }
-RCDATA NP_INFO_FileExtents { "avi|",
-                             "flc\0" }
-RCDATA NP_INFO_FileOpenName{ "MMOS2 video player(*.avi)|",
-                             "MMOS2 Flc/Fli player(*.flc)\0" }
-
-RCDATA NP_INFO_FileVersion       { 1,0,0,1 }
-RCDATA NP_INFO_CompanyName       { "Netscape Communications\0" }
-RCDATA NP_INFO_FileDescription   { "NPAVI32 Extension DLL\0"
-RCDATA NP_INFO_InternalName      { "NPAVI32\0" )
-RCDATA NP_INFO_LegalCopyright    { "Copyright Netscape Communications \251 1996\0"
-RCDATA NP_INFO_OriginalFilename  { "NVAPI32.DLL" }
-RCDATA NP_INFO_ProductName       { "NPAVI32 Dynamic Link Library\0" }
-
-*/
-
-
-/* RC_DATA types for version info - required */
-#define NP_INFO_ProductVersion      1
-#define NP_INFO_MIMEType            2
-#define NP_INFO_FileOpenName        3
-#define NP_INFO_FileExtents         4
-
-/* RC_DATA types for version info - used if found */
-#define NP_INFO_FileDescription     5
-#define NP_INFO_ProductName         6
-
-/* RC_DATA types for version info - optional */
-#define NP_INFO_CompanyName         7
-#define NP_INFO_FileVersion         8
-#define NP_INFO_InternalName        9
-#define NP_INFO_LegalCopyright      10
-#define NP_INFO_OriginalFilename    11
-
-#ifndef RC_INVOKED
-
-
-
-/*----------------------------------------------------------------------*/
-/*                       Definition of Basic Types                      */
-/*----------------------------------------------------------------------*/
-
-#ifndef _UINT16
-typedef unsigned short uint16;
-#endif
-
-#ifndef _UINT32
-#    if defined(__alpha) || defined(__amd64__) || defined(__x86_64__)
-typedef unsigned int uint32;
-#    else  /* __alpha */
-typedef unsigned long uint32;
-#    endif /* __alpha */
-#endif
-
-/*
- * AIX defines these in sys/inttypes.h included from sys/types.h
- */
-#ifndef AIX
-#ifndef _INT16
-typedef short int16;
-#endif
-
-#ifndef _INT32
-#    if defined(__alpha) || defined(__amd64__) || defined(__x86_64__)
-typedef int int32;
-#    else  /* __alpha */
-typedef long int32;
-#    endif /* __alpha */
-#endif
-#endif
-
-#ifndef FALSE
-#define FALSE (0)
-#endif
-#ifndef TRUE
-#define TRUE (1)
-#endif
-#ifndef NULL
-#define NULL (0L)
-#endif
-
-typedef unsigned char	NPBool;
-typedef int16			NPError;
-typedef int16			NPReason;
-typedef char*			NPMIMEType;
-
-
-
-/*----------------------------------------------------------------------*/
-/*                       Structures and definitions                     */
-/*----------------------------------------------------------------------*/
-
-#ifdef XP_MAC
-#pragma options align=mac68k
-#endif
-
-/*
- *  NPP is a plug-in's opaque instance handle
- */
-typedef struct _NPP
-{
-  void*	pdata;      /* plug-in private data */
-  void*	ndata;      /* netscape private data */
-} NPP_t;
-
-typedef NPP_t*  NPP;
-
-
-typedef struct _NPStream
-{
-  void*  pdata; /* plug-in private data */
-  void*  ndata; /* netscape private data */
-  const  char* url;
-  uint32 end;
-  uint32 lastmodified;
-  void*  notifyData;
-} NPStream;
-
-
-typedef struct _NPByteRange
-{
-  int32  offset; /* negative offset means from the end */
-  uint32 length;
-  struct _NPByteRange* next;
-} NPByteRange;
-
-
-typedef struct _NPSavedData
-{
-  int32	len;
-  void*	buf;
-} NPSavedData;
-
-
-typedef struct _NPRect
-{
-  uint16 top;
-  uint16 left;
-  uint16 bottom;
-  uint16 right;
-} NPRect;
-
-typedef struct _NPSize 
-{ 
-  int32 width; 
-  int32 height; 
-} NPSize; 
-
-#ifdef XP_UNIX
-/*
- * Unix specific structures and definitions
- */
-
-/*
- * Callback Structures.
- *
- * These are used to pass additional platform specific information.
- */
-enum {
-  NP_SETWINDOW = 1,
-  NP_PRINT
-};
-
-typedef struct
-{
-  int32 type;
-} NPAnyCallbackStruct;
-
-typedef struct
-{
-  int32        type;
-#ifdef MOZ_X11
-  Display*     display;
-  Visual*      visual;
-  Colormap     colormap;
-  unsigned int depth;
-#endif
-} NPSetWindowCallbackStruct;
-
-typedef struct
-{
-  int32 type;
-  FILE* fp;
-} NPPrintCallbackStruct;
-
-#endif /* XP_UNIX */
-
-
-/*
- *   The following masks are applied on certain platforms to NPNV and 
- *   NPPV selectors that pass around pointers to COM interfaces. Newer 
- *   compilers on some platforms may generate vtables that are not 
- *   compatible with older compilers. To prevent older plugins from 
- *   not understanding a new browser's ABI, these masks change the 
- *   values of those selectors on those platforms. To remain backwards
- *   compatible with differenet versions of the browser, plugins can 
- *   use these masks to dynamically determine and use the correct C++
- *   ABI that the browser is expecting. This does not apply to Windows 
- *   as Microsoft's COM ABI will likely not change.
- */
-
-#define NP_ABI_GCC3_MASK  0x10000000
-/*
- *   gcc 3.x generated vtables on UNIX and OSX are incompatible with 
- *   previous compilers.
- */
-#if (defined (XP_UNIX) && defined(__GNUC__) && (__GNUC__ >= 3))
-#define _NP_ABI_MIXIN_FOR_GCC3 NP_ABI_GCC3_MASK
-#else
-#define _NP_ABI_MIXIN_FOR_GCC3 0
-#endif
-
-
-#define NP_ABI_MACHO_MASK 0x01000000
-/*
- *   On OSX, the Mach-O executable format is significantly
- *   different than CFM. In addition to having a different
- *   C++ ABI, it also has has different C calling convention.
- *   You must use glue code when calling between CFM and
- *   Mach-O C functions. 
- */
-#if (defined(TARGET_RT_MAC_MACHO))
-#define _NP_ABI_MIXIN_FOR_MACHO NP_ABI_MACHO_MASK
-#else
-#define _NP_ABI_MIXIN_FOR_MACHO 0
-#endif
-
-
-#define NP_ABI_MASK (_NP_ABI_MIXIN_FOR_GCC3 | _NP_ABI_MIXIN_FOR_MACHO)
-
-/*
- * List of variable names for which NPP_GetValue shall be implemented
- */
-typedef enum {
-  NPPVpluginNameString = 1,
-  NPPVpluginDescriptionString,
-  NPPVpluginWindowBool,
-  NPPVpluginTransparentBool,
-  NPPVjavaClass,                /* Not implemented in Mozilla 1.0 */
-  NPPVpluginWindowSize,
-  NPPVpluginTimerInterval,
-
-  NPPVpluginScriptableInstance = (10 | NP_ABI_MASK),
-  NPPVpluginScriptableIID = 11,
-
-  /* Introduced in Mozilla 0.9.9 */
-  NPPVjavascriptPushCallerBool = 12,
-
-  /* Introduced in Mozilla 1.0 */
-  NPPVpluginKeepLibraryInMemory = 13,
-  NPPVpluginNeedsXEmbed         = 14,
-
-  /* Get the NPObject for scripting the plugin. Introduced in Firefox
-   * 1.0 (NPAPI minor version 14).
-   */
-  NPPVpluginScriptableNPObject  = 15,
-
-  /* Get the plugin value (as \0-terminated UTF-8 string data) for
-   * form submission if the plugin is part of a form. Use
-   * NPN_MemAlloc() to allocate memory for the string data. Introduced
-   * in Mozilla 1.8b2 (NPAPI minor version 15).
-   */
-  NPPVformValue = 16
-} NPPVariable;
-
-/*
- * List of variable names for which NPN_GetValue is implemented by Mozilla
- */
-typedef enum {
-  NPNVxDisplay = 1,
-  NPNVxtAppContext,
-  NPNVnetscapeWindow,
-  NPNVjavascriptEnabledBool,
-  NPNVasdEnabledBool,
-  NPNVisOfflineBool,
-
-  /* 10 and over are available on Mozilla builds starting with 0.9.4 */
-  NPNVserviceManager = (10 | NP_ABI_MASK),
-  NPNVDOMElement     = (11 | NP_ABI_MASK),   /* available in Mozilla 1.2 */
-  NPNVDOMWindow      = (12 | NP_ABI_MASK),
-  NPNVToolkit        = (13 | NP_ABI_MASK),
-  NPNVSupportsXEmbedBool = 14,
-
-  /* Get the NPObject wrapper for the browser window. */
-  NPNVWindowNPObject = 15,
-
-  /* Get the NPObject wrapper for the plugins DOM element. */
-  NPNVPluginElementNPObject = 16
-} NPNVariable;
-
-/*
- * The type of Tookkit the widgets use
- */
-typedef enum {
-  NPNVGtk12 = 1,
-  NPNVGtk2
-} NPNToolkitType;
-
-/*
- * The type of a NPWindow - it specifies the type of the data structure
- * returned in the window field.
- */
-typedef enum {
-  NPWindowTypeWindow = 1,
-  NPWindowTypeDrawable
-} NPWindowType;
-
-typedef struct _NPWindow
-{
-  void* window;  /* Platform specific window handle */
-                 /* OS/2: x - Position of bottom left corner  */
-                 /* OS/2: y - relative to visible netscape window */
-  int32 x;       /* Position of top left corner relative */
-  int32 y;       /* to a netscape page.					*/
-  uint32 width;  /* Maximum window size */
-  uint32 height;
-  NPRect clipRect; /* Clipping rectangle in port coordinates */
-                   /* Used by MAC only.			  */
-#if defined(XP_UNIX) && !defined(XP_MACOSX)
-  void * ws_info; /* Platform-dependent additonal data */
-#endif /* XP_UNIX */
-  NPWindowType type; /* Is this a window or a drawable? */
-} NPWindow;
-
-
-typedef struct _NPFullPrint
-{
-  NPBool pluginPrinted;/* Set TRUE if plugin handled fullscreen printing */
-  NPBool printOne;		 /* TRUE if plugin should print one copy to default printer */
-  void* platformPrint; /* Platform-specific printing info */
-} NPFullPrint;
-
-typedef struct _NPEmbedPrint
-{
-  NPWindow window;
-  void* platformPrint; /* Platform-specific printing info */
-} NPEmbedPrint;
-
-typedef struct _NPPrint
-{
-  uint16 mode;               /* NP_FULL or NP_EMBED */
-  union
-  {
-    NPFullPrint fullPrint;   /* if mode is NP_FULL */
-    NPEmbedPrint embedPrint; /* if mode is NP_EMBED */
-  } print;
-} NPPrint;
-
-#if defined(XP_MAC) || defined(XP_MACOSX)
-typedef EventRecord	NPEvent;
-#elif defined(XP_WIN)
-typedef struct _NPEvent
-{
-  uint16 event;
-  uint32 wParam;
-  uint32 lParam;
-} NPEvent;
-#elif defined(XP_OS2)
-typedef struct _NPEvent
-{
-  uint32 event;
-  uint32 wParam;
-  uint32 lParam;
-} NPEvent;
-#elif defined (XP_UNIX) && defined(MOZ_X11)
-typedef XEvent NPEvent;
-#else
-typedef void*			NPEvent;
-#endif /* XP_MAC */
-
-#if defined(XP_MAC) || defined(XP_MACOSX)
-typedef RgnHandle NPRegion;
-#elif defined(XP_WIN)
-typedef HRGN NPRegion;
-#elif defined(XP_UNIX) && defined(MOZ_X11)
-typedef Region NPRegion;
-#else
-typedef void *NPRegion;
-#endif /* XP_MAC */
-
-#if defined(XP_MAC) || defined(XP_MACOSX)
-/*
- *  Mac-specific structures and definitions.
- */
-
-typedef struct NP_Port
-{
-  CGrafPtr port; /* Grafport */
-  int32 portx;   /* position inside the topmost window */
-  int32 porty;
-} NP_Port;
-
-/*
- *  Non-standard event types that can be passed to HandleEvent
- */
-
-enum NPEventType {
-  NPEventType_GetFocusEvent = (osEvt + 16),
-  NPEventType_LoseFocusEvent,
-  NPEventType_AdjustCursorEvent,
-  NPEventType_MenuCommandEvent,
-  NPEventType_ClippingChangedEvent,
-  NPEventType_ScrollingBeginsEvent = 1000,
-  NPEventType_ScrollingEndsEvent
-};
-
-#ifdef OBSOLETE
-#define getFocusEvent     (osEvt + 16)
-#define loseFocusEvent    (osEvt + 17)
-#define adjustCursorEvent (osEvt + 18)
-#endif
-#endif /* XP_MAC */
-
-/*
- * Values for mode passed to NPP_New:
- */
-#define NP_EMBED 1
-#define NP_FULL  2
-
-/*
- * Values for stream type passed to NPP_NewStream:
- */
-#define NP_NORMAL     1
-#define NP_SEEK       2
-#define NP_ASFILE     3
-#define NP_ASFILEONLY 4
-
-#define NP_MAXREADY	(((unsigned)(~0)<<1)>>1)
-
-#ifdef XP_MAC
-#pragma options align=reset
-#endif
-
-
-/*----------------------------------------------------------------------*/
-/*		     Error and Reason Code definitions			*/
-/*----------------------------------------------------------------------*/
-
-/*
- * Values of type NPError:
- */
-#define NPERR_BASE                         0
-#define NPERR_NO_ERROR                    (NPERR_BASE + 0)
-#define NPERR_GENERIC_ERROR               (NPERR_BASE + 1)
-#define NPERR_INVALID_INSTANCE_ERROR      (NPERR_BASE + 2)
-#define NPERR_INVALID_FUNCTABLE_ERROR     (NPERR_BASE + 3)
-#define NPERR_MODULE_LOAD_FAILED_ERROR    (NPERR_BASE + 4)
-#define NPERR_OUT_OF_MEMORY_ERROR         (NPERR_BASE + 5)
-#define NPERR_INVALID_PLUGIN_ERROR        (NPERR_BASE + 6)
-#define NPERR_INVALID_PLUGIN_DIR_ERROR    (NPERR_BASE + 7)
-#define NPERR_INCOMPATIBLE_VERSION_ERROR  (NPERR_BASE + 8)
-#define NPERR_INVALID_PARAM               (NPERR_BASE + 9)
-#define NPERR_INVALID_URL                 (NPERR_BASE + 10)
-#define NPERR_FILE_NOT_FOUND              (NPERR_BASE + 11)
-#define NPERR_NO_DATA                     (NPERR_BASE + 12)
-#define NPERR_STREAM_NOT_SEEKABLE         (NPERR_BASE + 13)
-
-/*
- * Values of type NPReason:
- */
-#define NPRES_BASE          0
-#define NPRES_DONE         (NPRES_BASE + 0)
-#define NPRES_NETWORK_ERR  (NPRES_BASE + 1)
-#define NPRES_USER_BREAK   (NPRES_BASE + 2)
-
-/*
- * Don't use these obsolete error codes any more.
- */
-#define NP_NOERR  NP_NOERR_is_obsolete_use_NPERR_NO_ERROR
-#define NP_EINVAL NP_EINVAL_is_obsolete_use_NPERR_GENERIC_ERROR
-#define NP_EABORT NP_EABORT_is_obsolete_use_NPRES_USER_BREAK
-
-/*
- * Version feature information
- */
-#define NPVERS_HAS_STREAMOUTPUT      8
-#define NPVERS_HAS_NOTIFICATION      9
-#define NPVERS_HAS_LIVECONNECT       9
-#define NPVERS_WIN16_HAS_LIVECONNECT 9
-#define NPVERS_68K_HAS_LIVECONNECT   11
-#define NPVERS_HAS_WINDOWLESS        11
-#define NPVERS_HAS_XPCONNECT_SCRIPTING 13
-
-/*----------------------------------------------------------------------*/
-/*                        Function Prototypes                           */
-/*----------------------------------------------------------------------*/
-
-#if defined(_WINDOWS) && !defined(WIN32)
-#define NP_LOADDS  _loadds
-#else
-#if defined(__OS2__)
-#define NP_LOADDS _System
-#else
-#define NP_LOADDS
-#endif
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * NPP_* functions are provided by the plugin and called by the navigator.
- */
-
-#ifdef XP_UNIX
-char* NPP_GetMIMEDescription(void);
-#endif /* XP_UNIX */
-
-NPError NP_LOADDS NPP_Initialize(void);
-void    NP_LOADDS NPP_Shutdown(void);
-NPError NP_LOADDS NPP_New(NPMIMEType pluginType, NPP instance,
-                          uint16 mode, int16 argc, char* argn[],
-                          char* argv[], NPSavedData* saved);
-NPError NP_LOADDS NPP_Destroy(NPP instance, NPSavedData** save);
-NPError NP_LOADDS NPP_SetWindow(NPP instance, NPWindow* window);
-NPError NP_LOADDS NPP_NewStream(NPP instance, NPMIMEType type,
-                                NPStream* stream, NPBool seekable,
-                                uint16* stype);
-NPError NP_LOADDS NPP_DestroyStream(NPP instance, NPStream* stream,
-                                    NPReason reason);
-int32   NP_LOADDS NPP_WriteReady(NPP instance, NPStream* stream);
-int32   NP_LOADDS NPP_Write(NPP instance, NPStream* stream, int32 offset,
-                            int32 len, void* buffer);
-void    NP_LOADDS NPP_StreamAsFile(NPP instance, NPStream* stream,
-                                   const char* fname);
-void    NP_LOADDS NPP_Print(NPP instance, NPPrint* platformPrint);
-int16   NP_LOADDS NPP_HandleEvent(NPP instance, void* event);
-void    NP_LOADDS NPP_URLNotify(NPP instance, const char* url,
-                                NPReason reason, void* notifyData);
-#ifdef OJI
-jref    NP_LOADDS NPP_GetJavaClass(void);
-#endif
-NPError NP_LOADDS NPP_GetValue(NPP instance, NPPVariable variable, void *value);
-NPError NP_LOADDS NPP_SetValue(NPP instance, NPNVariable variable, void *value);
-
-/*
- * NPN_* functions are provided by the navigator and called by the plugin.
- */
-void    NP_LOADDS NPN_Version(int* plugin_major, int* plugin_minor,
-                              int* netscape_major, int* netscape_minor);
-NPError NP_LOADDS NPN_GetURLNotify(NPP instance, const char* url,
-                                   const char* target, void* notifyData);
-NPError NP_LOADDS NPN_GetURL(NPP instance, const char* url,
-                             const char* target);
-NPError NP_LOADDS NPN_PostURLNotify(NPP instance, const char* url,
-                                    const char* target, uint32 len,
-                                    const char* buf, NPBool file,
-                                    void* notifyData);
-NPError NP_LOADDS NPN_PostURL(NPP instance, const char* url,
-                              const char* target, uint32 len,
-                              const char* buf, NPBool file);
-NPError NP_LOADDS NPN_RequestRead(NPStream* stream, NPByteRange* rangeList);
-NPError NP_LOADDS NPN_NewStream(NPP instance, NPMIMEType type,
-                                const char* target, NPStream** stream);
-int32   NP_LOADDS NPN_Write(NPP instance, NPStream* stream, int32 len, void* buffer);
-NPError NP_LOADDS NPN_DestroyStream(NPP instance, NPStream* stream, NPReason reason);
-void    NP_LOADDS NPN_Status(NPP instance, const char* message);
-const char* NP_LOADDS	NPN_UserAgent(NPP instance);
-void*   NP_LOADDS NPN_MemAlloc(uint32 size);
-void    NP_LOADDS NPN_MemFree(void* ptr);
-uint32  NP_LOADDS NPN_MemFlush(uint32 size);
-void    NP_LOADDS NPN_ReloadPlugins(NPBool reloadPages);
-#ifdef OJI
-JRIEnv* NP_LOADDS NPN_GetJavaEnv(void);
-jref    NP_LOADDS NPN_GetJavaPeer(NPP instance);
-#endif
-NPError NP_LOADDS NPN_GetValue(NPP instance, NPNVariable variable, void *value);
-NPError NP_LOADDS NPN_SetValue(NPP instance, NPPVariable variable, void *value);
-void    NP_LOADDS NPN_InvalidateRect(NPP instance, NPRect *invalidRect);
-void    NP_LOADDS NPN_InvalidateRegion(NPP instance, NPRegion invalidRegion);
-void    NP_LOADDS NPN_ForceRedraw(NPP instance);
-void    NP_LOADDS NPN_PushPopupEnabledState(NPP instance, NPBool enabled);
-void    NP_LOADDS NPN_PopPopupEnabledState(NPP instance);
-
-#ifdef __cplusplus
-}  /* end extern "C" */
-#endif
-
-#endif /* RC_INVOKED */
-#ifdef __OS2__
-#pragma pack()
-#endif
-
-#endif /* _NPAPI_H_ */
diff --git a/third_party/gecko/include/npruntime.h b/third_party/gecko/include/npruntime.h
deleted file mode 100644
index 2d1dc02..0000000
--- a/third_party/gecko/include/npruntime.h
+++ /dev/null
@@ -1,397 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright © 2004, Apple Computer, Inc. and The Mozilla Foundation. 
- * All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- * 
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. Neither the names of Apple Computer, Inc. ("Apple") or The Mozilla
- * Foundation ("Mozilla") nor the names of their contributors may be used
- * to endorse or promote products derived from this software without
- * specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY APPLE, MOZILLA AND THEIR 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 APPLE, MOZILLA OR
- * THEIR 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.
- *
- * Revision 1 (March 4, 2004):
- * Initial proposal.
- *
- * Revision 2 (March 10, 2004):
- * All calls into script were made asynchronous.  Results are
- * provided via the NPScriptResultFunctionPtr callback.
- *
- * Revision 3 (March 10, 2004):
- * Corrected comments to not refer to class retain/release FunctionPtrs.
- *
- * Revision 4 (March 11, 2004):
- * Added additional convenience NPN_SetExceptionWithUTF8().
- * Changed NPHasPropertyFunctionPtr and NPHasMethodFunctionPtr to take NPClass
- * pointers instead of NPObject pointers.
- * Added NPIsValidIdentifier().
- *
- * Revision 5 (March 17, 2004):
- * Added context parameter to result callbacks from ScriptObject functions.
- *
- * Revision 6 (March 29, 2004):
- * Renamed functions implemented by user agent to NPN_*.  Removed _ from
- * type names.
- * Renamed "JavaScript" types to "Script".
- *
- * Revision 7 (April 21, 2004):
- * NPIdentifier becomes a void*, was int32_t
- * Remove NP_IsValidIdentifier, renamed NP_IdentifierFromUTF8 to NP_GetIdentifier
- * Added NPVariant and modified functions to use this new type.
- *
- * Revision 8 (July 9, 2004):
- * Updated to joint Apple-Mozilla license.
- *
- */
-#ifndef _NP_RUNTIME_H_
-#define _NP_RUNTIME_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "nptypes.h"
-
-/*
-    This API is used to facilitate binding code written in C to script
-    objects.  The API in this header does not assume the presence of a
-    user agent.  That is, it can be used to bind C code to scripting
-    environments outside of the context of a user agent.
-    
-    However, the normal use of the this API is in the context of a
-    scripting environment running in a browser or other user agent.
-    In particular it is used to support the extended Netscape
-    script-ability API for plugins (NP-SAP).  NP-SAP is an extension
-    of the Netscape plugin API.  As such we have adopted the use of
-    the "NP" prefix for this API.
-
-    The following NP{N|P}Variables were added to the Netscape plugin
-    API (in npapi.h):
-
-    NPNVWindowNPObject
-    NPNVPluginElementNPObject
-    NPPVpluginScriptableNPObject
-
-    These variables are exposed through NPN_GetValue() and
-    NPP_GetValue() (respectively) and are used to establish the
-    initial binding between the user agent and native code.  The DOM
-    objects in the user agent can be examined and manipulated using
-    the NPN_ functions that operate on NPObjects described in this
-    header.
-
-    To the extent possible the assumptions about the scripting
-    language used by the scripting environment have been minimized.
-*/
-
-#define NP_BEGIN_MACRO  do {
-#define NP_END_MACRO    } while (0)
-
-/*
-    Objects (non-primitive data) passed between 'C' and script is
-    always wrapped in an NPObject.  The 'interface' of an NPObject is
-    described by an NPClass.
-*/
-typedef struct NPObject NPObject;
-typedef struct NPClass NPClass;
-
-typedef char NPUTF8;
-typedef struct _NPString {
-    const NPUTF8 *utf8characters;
-    uint32_t utf8length;
-} NPString;
-
-typedef enum {
-    NPVariantType_Void,
-    NPVariantType_Null,
-    NPVariantType_Bool,
-    NPVariantType_Int32,
-    NPVariantType_Double,
-    NPVariantType_String,
-    NPVariantType_Object
-} NPVariantType;
-
-typedef struct _NPVariant {
-    NPVariantType type;
-    union {
-        bool boolValue;
-        uint32_t intValue;
-        double doubleValue;
-        NPString stringValue;
-        NPObject *objectValue;
-    } value;
-} NPVariant;
-
-/*
-    NPN_ReleaseVariantValue is called on all 'out' parameters
-    references.  Specifically it is to be called on variants that own
-    their value, as is the case with all non-const NPVariant*
-    arguments after a successful call to any methods (except this one)
-    in this API.
-
-    After calling NPN_ReleaseVariantValue, the type of the variant
-    will be NPVariantType_Void.
-*/
-void NPN_ReleaseVariantValue(NPVariant *variant);
-
-#define NPVARIANT_IS_VOID(_v)    ((_v).type == NPVariantType_Void)
-#define NPVARIANT_IS_NULL(_v)    ((_v).type == NPVariantType_Null)
-#define NPVARIANT_IS_BOOLEAN(_v) ((_v).type == NPVariantType_Bool)
-#define NPVARIANT_IS_INT32(_v)   ((_v).type == NPVariantType_Int32)
-#define NPVARIANT_IS_DOUBLE(_v)  ((_v).type == NPVariantType_Double)
-#define NPVARIANT_IS_STRING(_v)  ((_v).type == NPVariantType_String)
-#define NPVARIANT_IS_OBJECT(_v)  ((_v).type == NPVariantType_Object)
-
-#define NPVARIANT_TO_BOOLEAN(_v) ((_v).value.boolValue)
-#define NPVARIANT_TO_INT32(_v)   ((_v).value.intValue)
-#define NPVARIANT_TO_DOUBLE(_v)  ((_v).value.doubleValue)
-#define NPVARIANT_TO_STRING(_v)  ((_v).value.stringValue)
-#define NPVARIANT_TO_OBJECT(_v)  ((_v).value.objectValue)
-
-#define VOID_TO_NPVARIANT(_v)                                                 \
-NP_BEGIN_MACRO                                                                \
-    (_v).type = NPVariantType_Void;                                           \
-    (_v).value.objectValue = NULL;                                            \
-NP_END_MACRO
-
-#define NULL_TO_NPVARIANT(_v)                                                 \
-NP_BEGIN_MACRO                                                                \
-    (_v).type = NPVariantType_Null;                                           \
-    (_v).value.objectValue = NULL;                                            \
-NP_END_MACRO
-
-#define BOOLEAN_TO_NPVARIANT(_val, _v)                                        \
-NP_BEGIN_MACRO                                                                \
-    (_v).type = NPVariantType_Bool;                                           \
-    (_v).value.boolValue = !!(_val);                                          \
-NP_END_MACRO
-
-#define INT32_TO_NPVARIANT(_val, _v)                                          \
-NP_BEGIN_MACRO                                                                \
-    (_v).type = NPVariantType_Int32;                                          \
-    (_v).value.intValue = _val;                                               \
-NP_END_MACRO
-
-#define DOUBLE_TO_NPVARIANT(_val, _v)                                         \
-NP_BEGIN_MACRO                                                                \
-    (_v).type = NPVariantType_Double;                                         \
-    (_v).value.doubleValue = _val;                                            \
-NP_END_MACRO
-
-#define STRINGZ_TO_NPVARIANT(_val, _v)                                        \
-NP_BEGIN_MACRO                                                                \
-    (_v).type = NPVariantType_String;                                         \
-    NPString str = { _val, strlen(_val) };                                    \
-    (_v).value.stringValue = str;                                             \
-NP_END_MACRO
-
-#define STRINGN_TO_NPVARIANT(_val, _len, _v)                                  \
-NP_BEGIN_MACRO                                                                \
-    (_v).type = NPVariantType_String;                                         \
-    NPString str = { _val, _len };                                            \
-    (_v).value.stringValue = str;                                             \
-NP_END_MACRO
-
-#define OBJECT_TO_NPVARIANT(_val, _v)                                         \
-NP_BEGIN_MACRO                                                                \
-    (_v).type = NPVariantType_Object;                                         \
-    (_v).value.objectValue = _val;                                            \
-NP_END_MACRO
-
-
-/*
-	Type mappings (JavaScript types have been used for illustration
-    purposes):
-
-	JavaScript       to             C (NPVariant with type:)
-	undefined                       NPVariantType_Void
-	null                            NPVariantType_Null
-	Boolean                         NPVariantType_Bool
-	Number                          NPVariantType_Double or NPVariantType_Int32
-	String                          NPVariantType_String
-	Object                          NPVariantType_Object
-
-	C (NPVariant with type:)   to   JavaScript
-	NPVariantType_Void              undefined
-	NPVariantType_Null              null
-	NPVariantType_Bool              Boolean	
-	NPVariantType_Int32             Number
-	NPVariantType_Double            Number
-	NPVariantType_String            String
-	NPVariantType_Object            Object
-*/
-
-typedef void *NPIdentifier;
-
-/*
-    NPObjects have methods and properties.  Methods and properties are
-    identified with NPIdentifiers.  These identifiers may be reflected
-    in script.  NPIdentifiers can be either strings or integers, IOW,
-    methods and properties can be identified by either strings or
-    integers (i.e. foo["bar"] vs foo[1]). NPIdentifiers can be
-    compared using ==.  In case of any errors, the requested
-    NPIdentifier(s) will be NULL.
-*/
-NPIdentifier NPN_GetStringIdentifier(const NPUTF8 *name);
-void NPN_GetStringIdentifiers(const NPUTF8 **names, int32_t nameCount,
-                              NPIdentifier *identifiers);
-NPIdentifier NPN_GetIntIdentifier(int32_t intid);
-bool NPN_IdentifierIsString(NPIdentifier identifier);
-
-/*
-    The NPUTF8 returned from NPN_UTF8FromIdentifier SHOULD be freed.
-*/
-NPUTF8 *NPN_UTF8FromIdentifier(NPIdentifier identifier);
-
-/*
-    Get the integer represented by identifier. If identifier is not an
-    integer identifier, the behaviour is undefined.
-*/
-int32_t NPN_IntFromIdentifier(NPIdentifier identifier);
-
-/*
-    NPObject behavior is implemented using the following set of
-    callback functions.
-
-    The NPVariant *result argument of these functions (where
-    applicable) should be released using NPN_ReleaseVariantValue().
-*/
-typedef NPObject *(*NPAllocateFunctionPtr)(NPP npp, NPClass *aClass);
-typedef void (*NPDeallocateFunctionPtr)(NPObject *npobj);
-typedef void (*NPInvalidateFunctionPtr)(NPObject *npobj);
-typedef bool (*NPHasMethodFunctionPtr)(NPObject *npobj, NPIdentifier name);
-typedef bool (*NPInvokeFunctionPtr)(NPObject *npobj, NPIdentifier name,
-                                    const NPVariant *args, uint32_t argCount,
-                                    NPVariant *result);
-typedef bool (*NPInvokeDefaultFunctionPtr)(NPObject *npobj,
-                                           const NPVariant *args,
-                                           uint32_t argCount,
-                                           NPVariant *result);
-typedef bool (*NPHasPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name);
-typedef bool (*NPGetPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name,
-                                         NPVariant *result);
-typedef bool (*NPSetPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name,
-                                         const NPVariant *value);
-typedef bool (*NPRemovePropertyFunctionPtr)(NPObject *npobj,
-                                            NPIdentifier name);
-
-/*
-    NPObjects returned by create, retain, invoke, and getProperty pass
-    a reference count to the caller.  That is, the callee adds a
-    reference count which passes to the caller.  It is the caller's
-    responsibility to release the returned object.
-
-    NPInvokeFunctionPtr function may return 0 to indicate a void
-    result.
-
-    NPInvalidateFunctionPtr is called by the scripting environment
-    when the native code is shutdown.  Any attempt to message a
-    NPObject instance after the invalidate callback has been
-    called will result in undefined behavior, even if the native code
-    is still retaining those NPObject instances.  (The runtime
-    will typically return immediately, with 0 or NULL, from an attempt
-    to dispatch to a NPObject, but this behavior should not be
-    depended upon.)
-*/
-struct NPClass
-{
-    uint32_t structVersion;
-    NPAllocateFunctionPtr allocate;
-    NPDeallocateFunctionPtr deallocate;
-    NPInvalidateFunctionPtr invalidate;
-    NPHasMethodFunctionPtr hasMethod;
-    NPInvokeFunctionPtr invoke;
-    NPInvokeDefaultFunctionPtr invokeDefault;
-    NPHasPropertyFunctionPtr hasProperty;
-    NPGetPropertyFunctionPtr getProperty;
-    NPSetPropertyFunctionPtr setProperty;
-    NPRemovePropertyFunctionPtr removeProperty;
-};
-
-#define NP_CLASS_STRUCT_VERSION 1
-
-struct NPObject {
-    NPClass *_class;
-    uint32_t referenceCount;
-    /*
-     * Additional space may be allocated here by types of NPObjects
-     */
-};
-
-/*
-    If the class has an allocate function, NPN_CreateObject invokes
-    that function, otherwise a NPObject is allocated and
-    returned. This method will initialize the referenceCount member of
-    the NPObject to 1.
-*/
-NPObject *NPN_CreateObject(NPP npp, NPClass *aClass);
-
-/*
-    Increment the NPObject's reference count.
-*/
-NPObject *NPN_RetainObject(NPObject *npobj);
-
-/*
-    Decremented the NPObject's reference count.  If the reference
-    count goes to zero, the class's destroy function is invoke if
-    specified, otherwise the object is freed directly.
-*/
-void NPN_ReleaseObject(NPObject *npobj);
-
-/*
-    Functions to access script objects represented by NPObject.
-
-    Calls to script objects are synchronous.  If a function returns a
-    value, it will be supplied via the result NPVariant
-    argument. Successful calls will return true, false will be
-    returned in case of an error.
-    
-    Calls made from plugin code to script must be made from the thread
-    on which the plugin was initialized.
-*/
-
-bool NPN_Invoke(NPP npp, NPObject *npobj, NPIdentifier methodName,
-                const NPVariant *args, uint32_t argCount, NPVariant *result);
-bool NPN_InvokeDefault(NPP npp, NPObject *npobj, const NPVariant *args,
-                       uint32_t argCount, NPVariant *result);
-bool NPN_Evaluate(NPP npp, NPObject *npobj, NPString *script,
-                  NPVariant *result);
-bool NPN_GetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName,
-                     NPVariant *result);
-bool NPN_SetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName,
-                     const NPVariant *value);
-bool NPN_RemoveProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName);
-bool NPN_HasProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName);
-bool NPN_HasMethod(NPP npp, NPObject *npobj, NPIdentifier methodName);
-
-/*
-    NPN_SetException may be called to trigger a script exception upon
-    return from entry points into NPObjects.  Typical usage:
-
-    NPN_SetException (npobj, message);
-*/
-void NPN_SetException(NPObject *npobj, const NPUTF8 *message);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/third_party/gecko/include/nptypes.h b/third_party/gecko/include/nptypes.h
deleted file mode 100644
index a05d395..0000000
--- a/third_party/gecko/include/nptypes.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * mozilla.org.
- * Portions created by the Initial Developer are Copyright (C) 2004
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Johnny Stenback <jst@mozilla.org> (Original author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/*
- * Header file for ensuring that C99 types ([u]int32_t and bool) are
- * available.
- */
-
-#if defined(WIN32) || defined(OS2)
-  /*
-   * Win32 and OS/2 don't know C99, so define [u]int_32 here. The bool
-   * is predefined tho, both in C and C++.
-   */
-  typedef int int32_t;
-  typedef unsigned int uint32_t;
-#elif defined(_AIX) || defined(__sun) || defined(__osf__) || defined(IRIX) || defined(HPUX)
-  /*
-   * AIX and SunOS ship a inttypes.h header that defines [u]int32_t,
-   * but not bool for C.
-   */
-  #include <inttypes.h>
-
-  #ifndef __cplusplus
-    typedef int bool;
-  #endif
-#elif defined(bsdi) || defined(FREEBSD) || defined(OPENBSD)
-  /*
-   * BSD/OS, FreeBSD, and OpenBSD ship sys/types.h that define int32_t and 
-   * u_int32_t.
-   */
-  #include <sys/types.h>
-
-  /*
-   * BSD/OS ships no header that defines uint32_t, nor bool (for C)
-   * OpenBSD ships no header that defines uint32_t and using its bool macro is
-   * unsafe.
-   */
-  #if defined(bsdi) || defined(OPENBSD)
-  typedef u_int32_t uint32_t;
-
-  #if !defined(__cplusplus)
-    typedef int bool;
-  #endif
-  #else
-  /*
-   * FreeBSD defines uint32_t and bool.
-   */
-    #include <inttypes.h>
-    #include <stdbool.h>
-  #endif
-#elif defined(BEOS)
-  #include <inttypes.h>
-#else
-  /*
-   * For those that ship a standard C99 stdint.h header file, include
-   * it. Can't do the same for stdbool.h tho, since some systems ship
-   * with a stdbool.h file that doesn't compile!
-   */
-  #include <stdint.h>
-
-  #if !defined(__GNUC__) || (__GNUC__ > 2 || __GNUC_MINOR__ > 95)
-    #include <stdbool.h>
-  #else
-    /*
-     * GCC 2.91 can't deal with a typedef for bool, but a #define
-     * works.
-     */
-    #define bool int
-  #endif
-#endif
diff --git a/third_party/gecko/include/npupp.h b/third_party/gecko/include/npupp.h
deleted file mode 100644
index f7e8e5d..0000000
--- a/third_party/gecko/include/npupp.h
+++ /dev/null
@@ -1,1889 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-
-/*
- *  npupp.h $Revision: 3.20 $
- *  function call mecahnics needed by platform specific glue code.
- */
-
-
-#ifndef _NPUPP_H_
-#define _NPUPP_H_
-
-#if defined(__OS2__)
-#pragma pack(1)
-#endif
-
-#ifndef GENERATINGCFM
-#define GENERATINGCFM 0
-#endif
-
-#ifndef _NPAPI_H_
-#include "npapi.h"
-#endif
-
-#include "npruntime.h"
-
-#include "jri.h"
-
-/******************************************************************************************
-   plug-in function table macros
- 	        for each function in and out of the plugin API we define
-                    typedef NPP_FooUPP
-					#define NewNPP_FooProc
-					#define CallNPP_FooProc
-			for mac, define the UPP magic for PPC/68K calling
- *******************************************************************************************/
-
-
-/* NPP_Initialize */
-
-#define _NPUPP_USE_UPP_ (TARGET_RT_MAC_CFM && !TARGET_API_MAC_CARBON)
-
-#if _NPUPP_USE_UPP_
-typedef UniversalProcPtr NPP_InitializeUPP;
-
-enum {
-	uppNPP_InitializeProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(0))		
-		| RESULT_SIZE(SIZE_CODE(0))
-};
-
-#define NewNPP_InitializeProc(FUNC)		\
-		(NPP_InitializeUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPP_InitializeProcInfo, GetCurrentArchitecture())
-#define CallNPP_InitializeProc(FUNC)		\
-		(void)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPP_InitializeProcInfo)
-		
-#else
-
-typedef void (* NP_LOADDS NPP_InitializeUPP)(void);
-#define NewNPP_InitializeProc(FUNC)		\
-		((NPP_InitializeUPP) (FUNC))
-#define CallNPP_InitializeProc(FUNC)		\
-		(*(FUNC))()
-
-#endif
-
-
-/* NPP_Shutdown */
-
-#if _NPUPP_USE_UPP_
-typedef UniversalProcPtr NPP_ShutdownUPP;
-
-enum {
-	uppNPP_ShutdownProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(0))		
-		| RESULT_SIZE(SIZE_CODE(0))
-};
-
-#define NewNPP_ShutdownProc(FUNC)		\
-		(NPP_ShutdownUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPP_ShutdownProcInfo, GetCurrentArchitecture())
-#define CallNPP_ShutdownProc(FUNC)		\
-		(void)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPP_ShutdownProcInfo)
-		
-#else
-
-typedef void (* NP_LOADDS NPP_ShutdownUPP)(void);
-#define NewNPP_ShutdownProc(FUNC)		\
-		((NPP_ShutdownUPP) (FUNC))
-#define CallNPP_ShutdownProc(FUNC)		\
-		(*(FUNC))()
-
-#endif
-
-
-/* NPP_New */
-
-#if _NPUPP_USE_UPP_
-typedef UniversalProcPtr NPP_NewUPP;
-
-enum {
-	uppNPP_NewProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPMIMEType)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(uint16)))
-		| STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(int16)))
-		| STACK_ROUTINE_PARAMETER(5, SIZE_CODE(sizeof(char **)))
-		| STACK_ROUTINE_PARAMETER(6, SIZE_CODE(sizeof(char **)))
-		| STACK_ROUTINE_PARAMETER(7, SIZE_CODE(sizeof(NPSavedData *)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPError)))
-};
-
-#define NewNPP_NewProc(FUNC)		\
-		(NPP_NewUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPP_NewProcInfo, GetCurrentArchitecture())
-#define CallNPP_NewProc(FUNC, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7) \
-		(NPError)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPP_NewProcInfo, \
-								   (ARG1), (ARG2), (ARG3), (ARG4), (ARG5), (ARG6), (ARG7))
-#else
-
-typedef NPError	(* NP_LOADDS NPP_NewUPP)(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved);
-#define NewNPP_NewProc(FUNC)		\
-		((NPP_NewUPP) (FUNC))
-#define CallNPP_NewProc(FUNC, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3), (ARG4), (ARG5), (ARG6), (ARG7))
-
-#endif
-
-
-/* NPP_Destroy */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPP_DestroyUPP;
-enum {
-	uppNPP_DestroyProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPSavedData **)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPError)))
-};
-#define NewNPP_DestroyProc(FUNC)		\
-		(NPP_DestroyUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPP_DestroyProcInfo, GetCurrentArchitecture())
-#define CallNPP_DestroyProc(FUNC, ARG1, ARG2)		\
-		(NPError)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPP_DestroyProcInfo, (ARG1), (ARG2))
-#else
-
-typedef NPError	(* NP_LOADDS NPP_DestroyUPP)(NPP instance, NPSavedData** save);
-#define NewNPP_DestroyProc(FUNC)		\
-		((NPP_DestroyUPP) (FUNC))
-#define CallNPP_DestroyProc(FUNC, ARG1, ARG2)		\
-		(*(FUNC))((ARG1), (ARG2))
-
-#endif
-
-
-/* NPP_SetWindow */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPP_SetWindowUPP;
-enum {
-	uppNPP_SetWindowProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPWindow *)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPError)))
-};
-#define NewNPP_SetWindowProc(FUNC)		\
-		(NPP_SetWindowUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPP_SetWindowProcInfo, GetCurrentArchitecture())
-#define CallNPP_SetWindowProc(FUNC, ARG1, ARG2)		\
-		(NPError)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPP_SetWindowProcInfo, (ARG1), (ARG2))
-
-#else
-
-typedef NPError	(* NP_LOADDS NPP_SetWindowUPP)(NPP instance, NPWindow* window);
-#define NewNPP_SetWindowProc(FUNC)		\
-		((NPP_SetWindowUPP) (FUNC))
-#define CallNPP_SetWindowProc(FUNC, ARG1, ARG2)		\
-		(*(FUNC))((ARG1), (ARG2))
-
-#endif
-
-
-/* NPP_NewStream */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPP_NewStreamUPP;
-enum {
-	uppNPP_NewStreamProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPMIMEType)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(NPStream *)))
-		| STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(NPBool)))
-		| STACK_ROUTINE_PARAMETER(5, SIZE_CODE(sizeof(uint16 *)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPError)))
-};
-#define NewNPP_NewStreamProc(FUNC)		\
-		(NPP_NewStreamUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPP_NewStreamProcInfo, GetCurrentArchitecture())
-#define CallNPP_NewStreamProc(FUNC, ARG1, ARG2, ARG3, ARG4, ARG5)		\
-		(NPError)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPP_NewStreamProcInfo, (ARG1), (ARG2), (ARG3), (ARG4), (ARG5))
-#else
-
-typedef NPError	(* NP_LOADDS NPP_NewStreamUPP)(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype);
-#define NewNPP_NewStreamProc(FUNC)		\
-		((NPP_NewStreamUPP) (FUNC))
-#define CallNPP_NewStreamProc(FUNC, ARG1, ARG2, ARG3, ARG4, ARG5) \
-		(*(FUNC))((ARG1), (ARG2), (ARG3), (ARG4), (ARG5))
-#endif
-
-
-/* NPP_DestroyStream */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPP_DestroyStreamUPP;
-enum {
-	uppNPP_DestroyStreamProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPStream *)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(NPReason)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPError)))
-};
-#define NewNPP_DestroyStreamProc(FUNC)		\
-		(NPP_DestroyStreamUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPP_DestroyStreamProcInfo, GetCurrentArchitecture())
-#define CallNPP_DestroyStreamProc(FUNC,  NPParg, NPStreamPtr, NPReasonArg)		\
-		(NPError)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPP_DestroyStreamProcInfo, (NPParg), (NPStreamPtr), (NPReasonArg))
-
-#else
-
-typedef NPError	(* NP_LOADDS NPP_DestroyStreamUPP)(NPP instance, NPStream* stream, NPReason reason);
-#define NewNPP_DestroyStreamProc(FUNC)		\
-		((NPP_DestroyStreamUPP) (FUNC))
-#define CallNPP_DestroyStreamProc(FUNC,  NPParg, NPStreamPtr, NPReasonArg)		\
-		(*(FUNC))((NPParg), (NPStreamPtr), (NPReasonArg))
-
-#endif
-
-
-/* NPP_WriteReady */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPP_WriteReadyUPP;
-enum {
-	uppNPP_WriteReadyProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPStream *)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(int32)))
-};
-#define NewNPP_WriteReadyProc(FUNC)		\
-		(NPP_WriteReadyUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPP_WriteReadyProcInfo, GetCurrentArchitecture())
-#define CallNPP_WriteReadyProc(FUNC,  NPParg, NPStreamPtr)		\
-		(int32)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPP_WriteReadyProcInfo, (NPParg), (NPStreamPtr))
-
-#else
-
-typedef int32 (* NP_LOADDS NPP_WriteReadyUPP)(NPP instance, NPStream* stream);
-#define NewNPP_WriteReadyProc(FUNC)		\
-		((NPP_WriteReadyUPP) (FUNC))
-#define CallNPP_WriteReadyProc(FUNC,  NPParg, NPStreamPtr)		\
-		(*(FUNC))((NPParg), (NPStreamPtr))
-
-#endif
-
-
-/* NPP_Write */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPP_WriteUPP;
-enum {
-	uppNPP_WriteProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPStream *)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(int32)))
-		| STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(int32)))
-		| STACK_ROUTINE_PARAMETER(5, SIZE_CODE(sizeof(void*)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(int32)))
-};
-#define NewNPP_WriteProc(FUNC)		\
-		(NPP_WriteUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPP_WriteProcInfo, GetCurrentArchitecture())
-#define CallNPP_WriteProc(FUNC,  NPParg, NPStreamPtr, offsetArg, lenArg, bufferPtr)		\
-		(int32)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPP_WriteProcInfo, (NPParg), (NPStreamPtr), (offsetArg), (lenArg), (bufferPtr))
-
-#else
-
-typedef int32 (* NP_LOADDS NPP_WriteUPP)(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer);
-#define NewNPP_WriteProc(FUNC)		\
-		((NPP_WriteUPP) (FUNC))
-#define CallNPP_WriteProc(FUNC,  NPParg, NPStreamPtr, offsetArg, lenArg, bufferPtr)		\
-		(*(FUNC))((NPParg), (NPStreamPtr), (offsetArg), (lenArg), (bufferPtr))
-
-#endif
-
-
-/* NPP_StreamAsFile */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPP_StreamAsFileUPP;
-enum {
-	uppNPP_StreamAsFileProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPStream *)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(const char *)))
-		| RESULT_SIZE(SIZE_CODE(0))
-};
-#define NewNPP_StreamAsFileProc(FUNC)		\
-		(NPP_StreamAsFileUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPP_StreamAsFileProcInfo, GetCurrentArchitecture())
-#define CallNPP_StreamAsFileProc(FUNC, ARG1, ARG2, ARG3)		\
-		(void)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPP_StreamAsFileProcInfo, (ARG1), (ARG2), (ARG3))
-
-#else
-
-typedef void (* NP_LOADDS NPP_StreamAsFileUPP)(NPP instance, NPStream* stream, const char* fname);
-#define NewNPP_StreamAsFileProc(FUNC)		\
-		((NPP_StreamAsFileUPP) (FUNC))
-#define CallNPP_StreamAsFileProc(FUNC,  ARG1, ARG2, ARG3)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3))
-#endif
-
-
-/* NPP_Print */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPP_PrintUPP;
-enum {
-	uppNPP_PrintProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPPrint *)))
-		| RESULT_SIZE(SIZE_CODE(0))
-};
-#define NewNPP_PrintProc(FUNC)		\
-		(NPP_PrintUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPP_PrintProcInfo, GetCurrentArchitecture())
-#define CallNPP_PrintProc(FUNC,  NPParg, voidPtr)		\
-		(void)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPP_PrintProcInfo, (NPParg), (voidPtr))
-
-#else
-
-typedef void (* NP_LOADDS NPP_PrintUPP)(NPP instance, NPPrint* platformPrint);
-#define NewNPP_PrintProc(FUNC)		\
-		((NPP_PrintUPP) (FUNC))
-#define CallNPP_PrintProc(FUNC,  NPParg, NPPrintArg)		\
-		(*(FUNC))((NPParg), (NPPrintArg))
-
-#endif
-
-
-/* NPP_HandleEvent */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPP_HandleEventUPP;
-enum {
-	uppNPP_HandleEventProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(void *)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(int16)))
-};
-#define NewNPP_HandleEventProc(FUNC)		\
-		(NPP_HandleEventUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPP_HandleEventProcInfo, GetCurrentArchitecture())
-#define CallNPP_HandleEventProc(FUNC,  NPParg, voidPtr)		\
-		(int16)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPP_HandleEventProcInfo, (NPParg), (voidPtr))
-
-#else
-
-typedef int16 (* NP_LOADDS NPP_HandleEventUPP)(NPP instance, void* event);
-#define NewNPP_HandleEventProc(FUNC)		\
-		((NPP_HandleEventUPP) (FUNC))
-#define CallNPP_HandleEventProc(FUNC,  NPParg, voidPtr)		\
-		(*(FUNC))((NPParg), (voidPtr))
-
-#endif
-
-
-/* NPP_URLNotify */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPP_URLNotifyUPP;
-enum {
-	uppNPP_URLNotifyProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(const char*)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(NPReason)))
-		| STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(void*)))
-		| RESULT_SIZE(SIZE_CODE(SIZE_CODE(0)))
-};
-#define NewNPP_URLNotifyProc(FUNC)		\
-		(NPP_URLNotifyUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPP_URLNotifyProcInfo, GetCurrentArchitecture())
-#define CallNPP_URLNotifyProc(FUNC,  ARG1, ARG2, ARG3, ARG4)		\
-		(void)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPP_URLNotifyProcInfo, (ARG1), (ARG2), (ARG3), (ARG4))
-
-#else
-
-typedef void (* NP_LOADDS NPP_URLNotifyUPP)(NPP instance, const char* url, NPReason reason, void* notifyData);
-#define NewNPP_URLNotifyProc(FUNC)		\
-		((NPP_URLNotifyUPP) (FUNC))
-#define CallNPP_URLNotifyProc(FUNC,  ARG1, ARG2, ARG3, ARG4)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3), (ARG4))
-
-#endif
-
-
-/* NPP_GetValue */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPP_GetValueUPP;
-enum {
-	uppNPP_GetValueProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPPVariable)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(void *)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPError)))
-};
-#define NewNPP_GetValueProc(FUNC)		\
-		(NPP_GetValueUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPP_GetValueProcInfo, GetCurrentArchitecture())
-#define CallNPP_GetValueProc(FUNC, ARG1, ARG2, ARG3) \
-		(NPError)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPP_GetValueProcInfo, (ARG1), (ARG2), (ARG3))
-#else
-
-typedef NPError	(* NP_LOADDS NPP_GetValueUPP)(NPP instance, NPPVariable variable, void *ret_alue);
-#define NewNPP_GetValueProc(FUNC)		\
-		((NPP_GetValueUPP) (FUNC))
-#define CallNPP_GetValueProc(FUNC, ARG1, ARG2, ARG3)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3))
-#endif
-
-
-/* NPP_SetValue */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPP_SetValueUPP;
-enum {
-	uppNPP_SetValueProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPNVariable)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(void *)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPError)))
-};
-#define NewNPP_SetValueProc(FUNC)		\
-		(NPP_SetValueUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPP_SetValueProcInfo, GetCurrentArchitecture())
-#define CallNPP_SetValueProc(FUNC, ARG1, ARG2, ARG3) \
-		(NPError)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPP_SetValueProcInfo, (ARG1), (ARG2), (ARG3))
-#else
-
-typedef NPError	(* NP_LOADDS NPP_SetValueUPP)(NPP instance, NPNVariable variable, void *ret_alue);
-#define NewNPP_SetValueProc(FUNC)		\
-		((NPP_SetValueUPP) (FUNC))
-#define CallNPP_SetValueProc(FUNC, ARG1, ARG2, ARG3)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3))
-#endif
-
-
-/*
- *  Netscape entry points
- */
-
-
-/* NPN_GetValue */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_GetValueUPP;
-enum {
-	uppNPN_GetValueProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPNVariable)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(void *)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPError)))
-};
-#define NewNPN_GetValueProc(FUNC)		\
-		(NPN_GetValueUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_GetValueProcInfo, GetCurrentArchitecture())
-#define CallNPN_GetValueProc(FUNC, ARG1, ARG2, ARG3) \
-		(NPError)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_GetValueProcInfo, (ARG1), (ARG2), (ARG3))
-#else
-
-typedef NPError	(* NP_LOADDS NPN_GetValueUPP)(NPP instance, NPNVariable variable, void *ret_alue);
-#define NewNPN_GetValueProc(FUNC)		\
-		((NPN_GetValueUPP) (FUNC))
-#define CallNPN_GetValueProc(FUNC, ARG1, ARG2, ARG3)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3))
-#endif
-
-
-/* NPN_SetValue */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_SetValueUPP;
-enum {
-	uppNPN_SetValueProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPPVariable)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(void *)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPError)))
-};
-#define NewNPN_SetValueProc(FUNC)		\
-		(NPN_SetValueUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_SetValueProcInfo, GetCurrentArchitecture())
-#define CallNPN_SetValueProc(FUNC, ARG1, ARG2, ARG3) \
-		(NPError)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_SetValueProcInfo, (ARG1), (ARG2), (ARG3))
-#else
-
-typedef NPError	(* NP_LOADDS NPN_SetValueUPP)(NPP instance, NPPVariable variable, void *ret_alue);
-#define NewNPN_SetValueProc(FUNC)		\
-		((NPN_SetValueUPP) (FUNC))
-#define CallNPN_SetValueProc(FUNC, ARG1, ARG2, ARG3)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3))
-#endif
-
-
-/* NPN_GetUrlNotify */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_GetURLNotifyUPP;
-enum {
-	uppNPN_GetURLNotifyProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(const char*)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(const char*)))
-		| STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(void*)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPError)))
-};
-#define NewNPN_GetURLNotifyProc(FUNC)		\
-		(NPN_GetURLNotifyUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_GetURLNotifyProcInfo, GetCurrentArchitecture())
-#define CallNPN_GetURLNotifyProc(FUNC, ARG1, ARG2, ARG3, ARG4) \
-		(NPError)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_GetURLNotifyProcInfo, (ARG1), (ARG2), (ARG3), (ARG4))
-#else
-
-typedef NPError	(* NP_LOADDS NPN_GetURLNotifyUPP)(NPP instance, const char* url, const char* window, void* notifyData);
-#define NewNPN_GetURLNotifyProc(FUNC)		\
-		((NPN_GetURLNotifyUPP) (FUNC))
-#define CallNPN_GetURLNotifyProc(FUNC, ARG1, ARG2, ARG3, ARG4)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3), (ARG4))
-#endif
-
-
-/* NPN_PostUrlNotify */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_PostURLNotifyUPP;
-enum {
-	uppNPN_PostURLNotifyProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(const char*)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(const char*)))
-		| STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(uint32)))
-		| STACK_ROUTINE_PARAMETER(5, SIZE_CODE(sizeof(const char*)))
-		| STACK_ROUTINE_PARAMETER(6, SIZE_CODE(sizeof(NPBool)))
-		| STACK_ROUTINE_PARAMETER(7, SIZE_CODE(sizeof(void*)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPError)))
-};
-#define NewNPN_PostURLNotifyProc(FUNC)		\
-		(NPN_PostURLNotifyUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_PostURLNotifyProcInfo, GetCurrentArchitecture())
-#define CallNPN_PostURLNotifyProc(FUNC, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7) \
-		(NPError)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_PostURLNotifyProcInfo, (ARG1), (ARG2), (ARG3), (ARG4), (ARG5), (ARG6), (ARG7))
-#else
-
-typedef NPError (* NP_LOADDS NPN_PostURLNotifyUPP)(NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file, void* notifyData);
-#define NewNPN_PostURLNotifyProc(FUNC)		\
-		((NPN_PostURLNotifyUPP) (FUNC))
-#define CallNPN_PostURLNotifyProc(FUNC, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7) \
-		(*(FUNC))((ARG1), (ARG2), (ARG3), (ARG4), (ARG5), (ARG6), (ARG7))
-#endif
-
-
-/* NPN_GetUrl */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_GetURLUPP;
-enum {
-	uppNPN_GetURLProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(const char*)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(const char*)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPError)))
-};
-#define NewNPN_GetURLProc(FUNC)		\
-		(NPN_GetURLUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_GetURLProcInfo, GetCurrentArchitecture())
-#define CallNPN_GetURLProc(FUNC, ARG1, ARG2, ARG3) \
-		(NPError)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_GetURLProcInfo, (ARG1), (ARG2), (ARG3))
-#else
-
-typedef NPError	(* NP_LOADDS NPN_GetURLUPP)(NPP instance, const char* url, const char* window);
-#define NewNPN_GetURLProc(FUNC)		\
-		((NPN_GetURLUPP) (FUNC))
-#define CallNPN_GetURLProc(FUNC, ARG1, ARG2, ARG3)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3))
-#endif
-
-
-/* NPN_PostUrl */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_PostURLUPP;
-enum {
-	uppNPN_PostURLProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(const char*)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(const char*)))
-		| STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(uint32)))
-		| STACK_ROUTINE_PARAMETER(5, SIZE_CODE(sizeof(const char*)))
-		| STACK_ROUTINE_PARAMETER(6, SIZE_CODE(sizeof(NPBool)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPError)))
-};
-#define NewNPN_PostURLProc(FUNC)		\
-		(NPN_PostURLUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_PostURLProcInfo, GetCurrentArchitecture())
-#define CallNPN_PostURLProc(FUNC, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6) \
-		(NPError)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_PostURLProcInfo, (ARG1), (ARG2), (ARG3), (ARG4), (ARG5), (ARG6))
-#else
-
-typedef NPError (* NP_LOADDS NPN_PostURLUPP)(NPP instance, const char* url, const char* window, uint32 len, const char* buf, NPBool file);
-#define NewNPN_PostURLProc(FUNC)		\
-		((NPN_PostURLUPP) (FUNC))
-#define CallNPN_PostURLProc(FUNC, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6) \
-		(*(FUNC))((ARG1), (ARG2), (ARG3), (ARG4), (ARG5), (ARG6))
-#endif
-
-
-/* NPN_RequestRead */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_RequestReadUPP;
-enum {
-	uppNPN_RequestReadProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPStream *)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPByteRange *)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPError)))
-};
-#define NewNPN_RequestReadProc(FUNC)		\
-		(NPN_RequestReadUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_RequestReadProcInfo, GetCurrentArchitecture())
-#define CallNPN_RequestReadProc(FUNC,  stream, range)		\
-		(NPError)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_RequestReadProcInfo, (stream), (range))
-
-#else
-
-typedef NPError	(* NP_LOADDS NPN_RequestReadUPP)(NPStream* stream, NPByteRange* rangeList);
-#define NewNPN_RequestReadProc(FUNC)		\
-		((NPN_RequestReadUPP) (FUNC))
-#define CallNPN_RequestReadProc(FUNC, stream, range)		\
-		(*(FUNC))((stream), (range))
-
-#endif
-
-
-/* NPN_NewStream */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_NewStreamUPP;
-enum {
-	uppNPN_NewStreamProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPMIMEType)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(const char *)))
-		| STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(NPStream **)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPError)))
-};
-#define NewNPN_NewStreamProc(FUNC)		\
-		(NPN_NewStreamUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_NewStreamProcInfo, GetCurrentArchitecture())
-#define CallNPN_NewStreamProc(FUNC, npp, type, window, stream)		\
-		(NPError)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_NewStreamProcInfo, (npp), (type), (window), (stream))	
-
-#else
-
-typedef NPError	(* NP_LOADDS NPN_NewStreamUPP)(NPP instance, NPMIMEType type, const char* window, NPStream** stream);
-#define NewNPN_NewStreamProc(FUNC)		\
-		((NPN_NewStreamUPP) (FUNC))
-#define CallNPN_NewStreamProc(FUNC, npp, type, window, stream)		\
-		(*(FUNC))((npp), (type), (window), (stream))
-
-#endif
-
-
-/* NPN_Write */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_WriteUPP;
-enum {
-	uppNPN_WriteProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPStream *)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(int32)))
-		| STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(void*)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(int32)))
-};
-#define NewNPN_WriteProc(FUNC)		\
-		(NPN_WriteUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_WriteProcInfo, GetCurrentArchitecture())
-#define CallNPN_WriteProc(FUNC, npp, stream, len, buffer)		\
-		(int32)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_WriteProcInfo, (npp), (stream), (len), (buffer))	
-
-#else
-
-typedef int32 (* NP_LOADDS NPN_WriteUPP)(NPP instance, NPStream* stream, int32 len, void* buffer);
-#define NewNPN_WriteProc(FUNC)		\
-		((NPN_WriteUPP) (FUNC))
-#define CallNPN_WriteProc(FUNC, npp, stream, len, buffer)		\
-		(*(FUNC))((npp), (stream), (len), (buffer))
-
-#endif
-
-
-/* NPN_DestroyStream */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_DestroyStreamUPP;
-enum {
-	uppNPN_DestroyStreamProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP )))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPStream *)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(NPReason)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPError)))
-};
-#define NewNPN_DestroyStreamProc(FUNC)		\
-		(NPN_DestroyStreamUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_DestroyStreamProcInfo, GetCurrentArchitecture())
-#define CallNPN_DestroyStreamProc(FUNC, npp, stream, reason)		\
-		(NPError)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_DestroyStreamProcInfo, (npp), (stream), (reason))	
-
-#else
-
-typedef NPError (* NP_LOADDS NPN_DestroyStreamUPP)(NPP instance, NPStream* stream, NPReason reason);
-#define NewNPN_DestroyStreamProc(FUNC)		\
-		((NPN_DestroyStreamUPP) (FUNC))
-#define CallNPN_DestroyStreamProc(FUNC, npp, stream, reason)		\
-		(*(FUNC))((npp), (stream), (reason))
-
-#endif
-
-
-/* NPN_Status */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_StatusUPP;
-enum {
-	uppNPN_StatusProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(char *)))
-};
-
-#define NewNPN_StatusProc(FUNC)		\
-		(NPN_StatusUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_StatusProcInfo, GetCurrentArchitecture())
-#define CallNPN_StatusProc(FUNC, npp, msg)		\
-		(void)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_StatusProcInfo, (npp), (msg))	
-
-#else
-
-typedef void (* NP_LOADDS NPN_StatusUPP)(NPP instance, const char* message);
-#define NewNPN_StatusProc(FUNC)		\
-		((NPN_StatusUPP) (FUNC))
-#define CallNPN_StatusProc(FUNC, npp, msg)		\
-		(*(FUNC))((npp), (msg))	
-
-#endif
-
-
-/* NPN_UserAgent */
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_UserAgentUPP;
-enum {
-        uppNPN_UserAgentProcInfo = kThinkCStackBased
-                | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-                | RESULT_SIZE(SIZE_CODE(sizeof(const char *)))
-};
-
-#define NewNPN_UserAgentProc(FUNC)              \
-                (NPN_UserAgentUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_UserAgentProcInfo, GetCurrentArchitecture())
-#define CallNPN_UserAgentProc(FUNC, ARG1)               \
-                (const char*)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_UserAgentProcInfo, (ARG1))
-
-#else
-
-typedef const char*	(* NP_LOADDS NPN_UserAgentUPP)(NPP instance);
-#define NewNPN_UserAgentProc(FUNC)              \
-                ((NPN_UserAgentUPP) (FUNC))
-#define CallNPN_UserAgentProc(FUNC, ARG1)               \
-                (*(FUNC))((ARG1))
-
-#endif
-
-
-/* NPN_MemAlloc */
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_MemAllocUPP;
-enum {
-	uppNPN_MemAllocProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(uint32)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(void *)))
-};
-
-#define NewNPN_MemAllocProc(FUNC)		\
-		(NPN_MemAllocUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_MemAllocProcInfo, GetCurrentArchitecture())
-#define CallNPN_MemAllocProc(FUNC, ARG1)		\
-		(void*)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_MemAllocProcInfo, (ARG1))	
-
-#else
-
-typedef void* (* NP_LOADDS NPN_MemAllocUPP)(uint32 size);
-#define NewNPN_MemAllocProc(FUNC)		\
-		((NPN_MemAllocUPP) (FUNC))
-#define CallNPN_MemAllocProc(FUNC, ARG1)		\
-		(*(FUNC))((ARG1))	
-
-#endif
-
-
-/* NPN__MemFree */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_MemFreeUPP;
-enum {
-	uppNPN_MemFreeProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(void *)))
-};
-
-#define NewNPN_MemFreeProc(FUNC)		\
-		(NPN_MemFreeUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_MemFreeProcInfo, GetCurrentArchitecture())
-#define CallNPN_MemFreeProc(FUNC, ARG1)		\
-		(void)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_MemFreeProcInfo, (ARG1))
-
-#else
-
-typedef void (* NP_LOADDS NPN_MemFreeUPP)(void* ptr);
-#define NewNPN_MemFreeProc(FUNC)		\
-		((NPN_MemFreeUPP) (FUNC))
-#define CallNPN_MemFreeProc(FUNC, ARG1)		\
-		(*(FUNC))((ARG1))	
-
-#endif
-
-
-/* NPN_MemFlush */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_MemFlushUPP;
-enum {
-	uppNPN_MemFlushProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(uint32)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(uint32)))
-};
-
-#define NewNPN_MemFlushProc(FUNC)		\
-		(NPN_MemFlushUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_MemFlushProcInfo, GetCurrentArchitecture())
-#define CallNPN_MemFlushProc(FUNC, ARG1)		\
-		(uint32)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_MemFlushProcInfo, (ARG1))	
-
-#else
-
-typedef uint32 (* NP_LOADDS NPN_MemFlushUPP)(uint32 size);
-#define NewNPN_MemFlushProc(FUNC)		\
-		((NPN_MemFlushUPP) (FUNC))
-#define CallNPN_MemFlushProc(FUNC, ARG1)		\
-		(*(FUNC))((ARG1))	
-
-#endif
-
-
-
-/* NPN_ReloadPlugins */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_ReloadPluginsUPP;
-enum {
-	uppNPN_ReloadPluginsProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPBool)))
-		| RESULT_SIZE(SIZE_CODE(0))
-};
-
-#define NewNPN_ReloadPluginsProc(FUNC)		\
-		(NPN_ReloadPluginsUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_ReloadPluginsProcInfo, GetCurrentArchitecture())
-#define CallNPN_ReloadPluginsProc(FUNC, ARG1)		\
-		(void)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_ReloadPluginsProcInfo, (ARG1))	
-
-#else
-
-typedef void (* NP_LOADDS NPN_ReloadPluginsUPP)(NPBool reloadPages);
-#define NewNPN_ReloadPluginsProc(FUNC)		\
-		((NPN_ReloadPluginsUPP) (FUNC))
-#define CallNPN_ReloadPluginsProc(FUNC, ARG1)		\
-		(*(FUNC))((ARG1))	
-
-#endif
-
-/* NPN_GetJavaEnv */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_GetJavaEnvUPP;
-enum {
-	uppNPN_GetJavaEnvProcInfo = kThinkCStackBased
-		| RESULT_SIZE(SIZE_CODE(sizeof(JRIEnv*)))
-};
-
-#define NewNPN_GetJavaEnvProc(FUNC)		\
-		(NPN_GetJavaEnvUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_GetJavaEnvProcInfo, GetCurrentArchitecture())
-#define CallNPN_GetJavaEnvProc(FUNC)		\
-		(JRIEnv*)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_GetJavaEnvProcInfo)	
-
-#else
-typedef JRIEnv* (* NP_LOADDS NPN_GetJavaEnvUPP)(void);
-#define NewNPN_GetJavaEnvProc(FUNC)		\
-		((NPN_GetJavaEnvUPP) (FUNC))
-#define CallNPN_GetJavaEnvProc(FUNC)		\
-		(*(FUNC))()	
-
-#endif
-
-
-/* NPN_GetJavaPeer */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_GetJavaPeerUPP;
-enum {
-	uppNPN_GetJavaPeerProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(jref)))
-};
-
-#define NewNPN_GetJavaPeerProc(FUNC)		\
-		(NPN_GetJavaPeerUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_GetJavaPeerProcInfo, GetCurrentArchitecture())
-#define CallNPN_GetJavaPeerProc(FUNC, ARG1)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_GetJavaPeerProcInfo, (ARG1))	
-
-#else
-
-typedef jref (* NP_LOADDS NPN_GetJavaPeerUPP)(NPP instance);
-#define NewNPN_GetJavaPeerProc(FUNC)		\
-		((NPN_GetJavaPeerUPP) (FUNC))
-#define CallNPN_GetJavaPeerProc(FUNC, ARG1)		\
-		(*(FUNC))((ARG1))	
-
-#endif
-
-/* NPN_InvalidateRect */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_InvalidateRectUPP;
-enum {
-	uppNPN_InvalidateRectProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPRect *)))
-		| RESULT_SIZE(SIZE_CODE(0))
-};
-
-#define NewNPN_InvalidateRectProc(FUNC)		\
-		(NPN_InvalidateRectUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_InvalidateRectProcInfo, GetCurrentArchitecture())
-#define CallNPN_InvalidateRectProc(FUNC, ARG1, ARG2)		\
-		(void)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_InvalidateRectProcInfo, (ARG1), (ARG2))	
-
-#else
-
-typedef void (* NP_LOADDS NPN_InvalidateRectUPP)(NPP instance, NPRect *rect);
-#define NewNPN_InvalidateRectProc(FUNC)		\
-		((NPN_InvalidateRectUPP) (FUNC))
-#define CallNPN_InvalidateRectProc(FUNC, ARG1, ARG2)		\
-		(*(FUNC))((ARG1), (ARG2))	
-
-#endif
-
-
-/* NPN_InvalidateRegion */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_InvalidateRegionUPP;
-enum {
-	uppNPN_InvalidateRegionProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPRegion)))
-		| RESULT_SIZE(SIZE_CODE(0))
-};
-
-#define NewNPN_InvalidateRegionProc(FUNC)		\
-		(NPN_InvalidateRegionUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_InvalidateRegionProcInfo, GetCurrentArchitecture())
-#define CallNPN_InvalidateRegionProc(FUNC, ARG1, ARG2)		\
-		(void)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_InvalidateRegionProcInfo, (ARG1), (ARG2))	
-
-#else
-
-typedef void (* NP_LOADDS NPN_InvalidateRegionUPP)(NPP instance, NPRegion region);
-#define NewNPN_InvalidateRegionProc(FUNC)		\
-		((NPN_InvalidateRegionUPP) (FUNC))
-#define CallNPN_InvalidateRegionProc(FUNC, ARG1, ARG2)		\
-		(*(FUNC))((ARG1), (ARG2))	
-
-#endif
-
-/* NPN_ForceRedraw */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_ForceRedrawUPP;
-enum {
-	uppNPN_ForceRedrawProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(0)))
-};
-
-#define NewNPN_ForceRedrawProc(FUNC)		\
-		(NPN_ForceRedrawUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_ForceRedrawProcInfo, GetCurrentArchitecture())
-#define CallNPN_ForceRedrawProc(FUNC, ARG1)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_ForceRedrawProcInfo, (ARG1))	
-
-#else
-
-typedef void (* NP_LOADDS NPN_ForceRedrawUPP)(NPP instance);
-#define NewNPN_ForceRedrawProc(FUNC)		\
-		((NPN_ForceRedrawUPP) (FUNC))
-#define CallNPN_ForceRedrawProc(FUNC, ARG1)		\
-		(*(FUNC))((ARG1))	
-
-#endif
-
-/* NPN_GetStringIdentifier */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_GetStringIdentifierUPP;
-enum {
-	uppNPN_GetStringIdentifierProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(const NPUTF8*)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPIdentifier)))
-};
-
-#define NewNPN_GetStringIdentifierProc(FUNC)		\
-		(NPN_GetStringIdentifierUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_GetStringIdentifierProcInfo, GetCurrentArchitecture())
-#define CallNPN_GetStringIdentifierProc(FUNC, ARG1)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_GetStringIdentifierProcInfo, (ARG1))	
-
-#else
-
-typedef NPIdentifier (* NP_LOADDS NPN_GetStringIdentifierUPP)(const NPUTF8* name);
-#define NewNPN_GetStringIdentifierProc(FUNC)		\
-		((NPN_GetStringIdentifierUPP) (FUNC))
-#define CallNPN_GetStringIdentifierProc(FUNC, ARG1)		\
-		(*(FUNC))((ARG1))
-
-#endif
-
-/* NPN_GetStringIdentifiers */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_GetStringIdentifiersUPP;
-enum {
-	uppNPN_GetStringIdentifiersProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(const NPUTF8**)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(int32_t)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(NPIdentifier*)))
-        | RESULT_SIZE(SIZE_CODE(0))
-};
-
-#define NewNPN_GetStringIdentifiersProc(FUNC)		\
-		(NPN_GetStringIdentifiersUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_GetStringIdentifiersProcInfo, GetCurrentArchitecture())
-#define CallNPN_GetStringIdentifiersProc(FUNC, ARG1, ARG2, ARG3)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_GetStringIdentifiersProcInfo, (ARG1), (ARG2), (ARG3))	
-
-#else
-
-typedef void (* NP_LOADDS NPN_GetStringIdentifiersUPP)(const NPUTF8** names,
-                                                 int32_t nameCount,
-                                                 NPIdentifier* identifiers);
-#define NewNPN_GetStringIdentifiersProc(FUNC)		\
-		((NPN_GetStringIdentifiersUPP) (FUNC))
-#define CallNPN_GetStringIdentifiersProc(FUNC, ARG1, ARG2, ARG3)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3))
-
-#endif
-
-/* NPN_GetIntIdentifier */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_GetIntIdentifierUPP;
-enum {
-	uppNPN_GetIntIdentifierProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(int32_t)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPIdentifier)))
-};
-
-#define NewNPN_GetIntIdentifierProc(FUNC)		\
-		(NPN_GetIntIdentifierUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_GetIntIdentifierProcInfo, GetCurrentArchitecture())
-#define CallNPN_GetIntIdentifierProc(FUNC, ARG1)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_GetIntIdentifierProcInfo, (ARG1))	
-
-#else
-
-typedef NPIdentifier (* NP_LOADDS NPN_GetIntIdentifierUPP)(int32_t intid);
-#define NewNPN_GetIntIdentifierProc(FUNC)		\
-		((NPN_GetIntIdentifierUPP) (FUNC))
-#define CallNPN_GetIntIdentifierProc(FUNC, ARG1)		\
-		(*(FUNC))((ARG1))
-
-#endif
-
-/* NPN_IdentifierIsString */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_IdentifierIsStringUPP;
-enum {
-	uppNPN_IdentifierIsStringProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPIdentifier identifier)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(bool)))
-};
-
-#define NewNPN_IdentifierIsStringProc(FUNC)		\
-		(NPN_IdentifierIsStringUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_IdentifierIsStringProcInfo, GetCurrentArchitecture())
-#define CallNPN_IdentifierIsStringProc(FUNC, ARG1)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_IdentifierIsStringProcInfo, (ARG1))	
-
-#else
-
-typedef bool (* NP_LOADDS NPN_IdentifierIsStringUPP)(NPIdentifier identifier);
-#define NewNPN_IdentifierIsStringProc(FUNC)		\
-		((NPN_IdentifierIsStringUPP) (FUNC))
-#define CallNPN_IdentifierIsStringProc(FUNC, ARG1)		\
-		(*(FUNC))((ARG1))
-
-#endif
-
-/* NPN_UTF8FromIdentifier */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_UTF8FromIdentifierUPP;
-enum {
-	uppNPN_UTF8FromIdentifierProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPIdentifier)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPUTF8*)))
-};
-
-#define NewNPN_UTF8FromIdentifierProc(FUNC)		\
-		(NPN_UTF8FromIdentifierUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_UTF8FromIdentifierProcInfo, GetCurrentArchitecture())
-#define CallNPN_UTF8FromIdentifierProc(FUNC, ARG1)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_UTF8FromIdentifierProcInfo, (ARG1))	
-
-#else
-
-typedef NPUTF8* (* NP_LOADDS NPN_UTF8FromIdentifierUPP)(NPIdentifier identifier);
-#define NewNPN_UTF8FromIdentifierProc(FUNC)		\
-		((NPN_UTF8FromIdentifierUPP) (FUNC))
-#define CallNPN_UTF8FromIdentifierProc(FUNC, ARG1)		\
-		(*(FUNC))((ARG1))
-
-#endif
-
-/* NPN_IntFromIdentifier */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_IntFromIdentifierUPP;
-enum {
-	uppNPN_IntFromIdentifierProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPIdentifier)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(int32_t)))
-};
-
-#define NewNPN_IntFromIdentifierProc(FUNC)		\
-		(NPN_IntFromIdentifierUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_IntFromIdentifierProcInfo, GetCurrentArchitecture())
-#define CallNPN_IntFromIdentifierProc(FUNC, ARG1)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_IntFromIdentifierProcInfo, (ARG1))	
-
-#else
-
-typedef int32_t (* NP_LOADDS NPN_IntFromIdentifierUPP)(NPIdentifier identifier);
-#define NewNPN_IntFromIdentifierProc(FUNC)		\
-		((NPN_IntFromIdentifierUPP) (FUNC))
-#define CallNPN_IntFromIdentifierProc(FUNC, ARG1)		\
-		(*(FUNC))((ARG1))
-
-#endif
-
-/* NPN_CreateObject */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_CreateObjectUPP;
-enum {
-	uppNPN_CreateObjectProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPClass*)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPObject*)))
-};
-
-#define NewNPN_CreateObjectProc(FUNC)		\
-		(NPN_CreateObjectUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_CreateObjectProcInfo, GetCurrentArchitecture())
-#define CallNPN_CreateObjectProc(FUNC, ARG1, ARG2)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_CreateObjectProcInfo, (ARG1), (ARG2))	
-
-#else
-
-typedef NPObject* (* NP_LOADDS NPN_CreateObjectUPP)(NPP npp, NPClass *aClass);
-#define NewNPN_CreateObjectProc(FUNC)		\
-		((NPN_CreateObjectUPP) (FUNC))
-#define CallNPN_CreateObjectProc(FUNC, ARG1, ARG2)		\
-		(*(FUNC))((ARG1), (ARG2))
-
-#endif
-
-/* NPN_RetainObject */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_RetainObjectUPP;
-enum {
-	uppNPN_RetainObjectProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPObject*)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPObject*)))
-};
-
-#define NewNPN_RetainObjectProc(FUNC)		\
-		(NPN_RetainObjectUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_RetainObjectProcInfo, GetCurrentArchitecture())
-#define CallNPN_RetainObjectProc(FUNC, ARG1)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_RetainObjectProcInfo, (ARG1))	
-
-#else
-
-typedef NPObject* (* NP_LOADDS NPN_RetainObjectUPP)(NPObject *obj);
-#define NewNPN_RetainObjectProc(FUNC)		\
-		((NPN_RetainObjectUPP) (FUNC))
-#define CallNPN_RetainObjectProc(FUNC, ARG1)		\
-		(*(FUNC))((ARG1))
-
-#endif
-
-/* NPN_ReleaseObject */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_ReleaseObjectUPP;
-enum {
-	uppNPN_ReleaseObjectProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPObject*)))
-		| RESULT_SIZE(SIZE_CODE(0))
-};
-
-#define NewNPN_ReleaseObjectProc(FUNC)		\
-		(NPN_ReleaseObjectUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_ReleaseObjectProcInfo, GetCurrentArchitecture())
-#define CallNPN_ReleaseObjectProc(FUNC, ARG1)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_ReleaseObjectProcInfo, (ARG1))
-
-#else
-
-typedef void (* NP_LOADDS NPN_ReleaseObjectUPP)(NPObject *obj);
-#define NewNPN_ReleaseObjectProc(FUNC)		\
-		((NPN_ReleaseObjectUPP) (FUNC))
-#define CallNPN_ReleaseObjectProc(FUNC, ARG1)		\
-		(*(FUNC))((ARG1))
-
-#endif
-
-/* NPN_Invoke */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_InvokeUPP;
-enum {
-	uppNPN_InvokeProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPObject*)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(NPIdentifier)))
-		| STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(const NPVariant*)))
-		| STACK_ROUTINE_PARAMETER(5, SIZE_CODE(sizeof(uint32_t)))
-		| STACK_ROUTINE_PARAMETER(6, SIZE_CODE(sizeof(NPVariant*)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(bool)))
-};
-
-#define NewNPN_InvokeProc(FUNC)		\
-		(NPN_InvokeUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_InvokeProcInfo, GetCurrentArchitecture())
-#define CallNPN_InvokeProc(FUNC, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_InvokeProcInfo, (ARG1), (ARG2), (ARG3), (ARG4), (ARG5), (ARG6))
-
-#else
-
-typedef bool (* NP_LOADDS NPN_InvokeUPP)(NPP npp, NPObject* obj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result);
-#define NewNPN_InvokeProc(FUNC)		\
-		((NPN_InvokeUPP) (FUNC))
-#define CallNPN_InvokeProc(FUNC, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3), (ARG4), (ARG5), (ARG6))
-
-#endif
-
-/* NPN_InvokeDefault */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_InvokeDefaultUPP;
-enum {
-	uppNPN_InvokeDefaultProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPObject*)))
-		| STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(const NPVariant*)))
-		| STACK_ROUTINE_PARAMETER(5, SIZE_CODE(sizeof(uint32_t)))
-		| STACK_ROUTINE_PARAMETER(6, SIZE_CODE(sizeof(NPVariant*)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(bool)))
-};
-
-#define NewNPN_InvokeDefaultProc(FUNC)		\
-		(NPN_InvokeDefaultUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_InvokeDefaultProcInfo, GetCurrentArchitecture())
-#define CallNPN_InvokeDefaultProc(FUNC, ARG1, ARG2, ARG3, ARG4, ARG5)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_InvokeDefaultProcInfo, (ARG1), (ARG2), (ARG3), (ARG4), (ARG5))
-
-#else
-
-typedef bool (* NP_LOADDS NPN_InvokeDefaultUPP)(NPP npp, NPObject* obj, const NPVariant *args, uint32_t argCount, NPVariant *result);
-#define NewNPN_InvokeDefaultProc(FUNC)		\
-		((NPN_InvokeDefaultUPP) (FUNC))
-#define CallNPN_InvokeDefaultProc(FUNC, ARG1, ARG2, ARG3, ARG4, ARG5)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3), (ARG4), (ARG5))
-
-#endif
-
-/* NPN_Evaluate */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_EvaluateUPP;
-enum {
-	uppNPN_EvaluateProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPObject*)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(NPString*)))
-		| STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(NPVariant*)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(bool)))
-};
-
-#define NewNPN_EvaluateProc(FUNC)		\
-		(NPN_EvaluateUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_EvaluateProcInfo, GetCurrentArchitecture())
-#define CallNPN_EvaluateProc(FUNC, ARG1, ARG2, ARG3, ARG4)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_EvaluateProcInfo, (ARG1), (ARG2), (ARG3), (ARG4))
-
-#else
-
-typedef bool (* NP_LOADDS NPN_EvaluateUPP)(NPP npp, NPObject *obj, NPString *script, NPVariant *result);
-#define NewNPN_EvaluateProc(FUNC)		\
-		((NPN_EvaluateUPP) (FUNC))
-#define CallNPN_EvaluateProc(FUNC, ARG1, ARG2, ARG3, ARG4)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3), (ARG4))
-
-#endif
-
-/* NPN_GetProperty */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_GetPropertyUPP;
-enum {
-	uppNPN_GetPropertyProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPObject*)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(NPIdentifier)))
-		| STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(NPVariant*)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(bool)))
-};
-
-#define NewNPN_GetPropertyProc(FUNC)		\
-		(NPN_GetPropertyUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_GetPropertyProcInfo, GetCurrentArchitecture())
-#define CallNPN_GetPropertyProc(FUNC, ARG1, ARG2, ARG3, ARG4)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_GetPropertyProcInfo, (ARG1), (ARG2), (ARG3), (ARG4))
-
-#else
-
-typedef bool (* NP_LOADDS NPN_GetPropertyUPP)(NPP npp, NPObject *obj, NPIdentifier propertyName, NPVariant *result);
-#define NewNPN_GetPropertyProc(FUNC)		\
-		((NPN_GetPropertyUPP) (FUNC))
-#define CallNPN_GetPropertyProc(FUNC, ARG1, ARG2, ARG3, ARG4)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3), (ARG4))
-
-#endif
-
-/* NPN_SetProperty */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_SetPropertyUPP;
-enum {
-	uppNPN_SetPropertyProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPObject*)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(NPIdentifier)))
-		| STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(const NPVariant*)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(bool)))
-};
-
-#define NewNPN_SetPropertyProc(FUNC)		\
-		(NPN_SetPropertyUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_SetPropertyProcInfo, GetCurrentArchitecture())
-#define CallNPN_SetPropertyProc(FUNC, ARG1, ARG2, ARG3, ARG4)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_SetPropertyProcInfo, (ARG1), (ARG2), (ARG3), (ARG4))
-
-#else
-
-typedef bool (* NP_LOADDS NPN_SetPropertyUPP)(NPP npp, NPObject *obj, NPIdentifier propertyName, const NPVariant *value);
-#define NewNPN_SetPropertyProc(FUNC)		\
-		((NPN_SetPropertyUPP) (FUNC))
-#define CallNPN_SetPropertyProc(FUNC, ARG1, ARG2, ARG3, ARG4)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3), (ARG4))
-
-#endif
-
-/* NPN_RemoveProperty */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_RemovePropertyUPP;
-enum {
-	uppNPN_RemovePropertyProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPObject*)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(NPIdentifier)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(bool)))
-};
-
-#define NewNPN_RemovePropertyProc(FUNC)		\
-		(NPN_RemovePropertyUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_RemovePropertyProcInfo, GetCurrentArchitecture())
-#define CallNPN_RemovePropertyProc(FUNC, ARG1, ARG2, ARG3)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_RemovePropertyProcInfo, (ARG1), (ARG2), (ARG3))
-
-#else
-
-typedef bool (* NP_LOADDS NPN_RemovePropertyUPP)(NPP npp, NPObject *obj, NPIdentifier propertyName);
-#define NewNPN_RemovePropertyProc(FUNC)		\
-		((NPN_RemovePropertyUPP) (FUNC))
-#define CallNPN_RemovePropertyProc(FUNC, ARG1, ARG2, ARG3)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3))
-
-#endif
-
-/* NPN_HasProperty */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_HasPropertyUPP;
-enum {
-	uppNPN_HasPropertyProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPObject*)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(NPIdentifier)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(bool)))
-};
-
-#define NewNPN_HasPropertyProc(FUNC)		\
-		(NPN_HasPropertyUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_HasPropertyProcInfo, GetCurrentArchitecture())
-#define CallNPN_HasPropertyProc(FUNC, ARG1, ARG2, ARG3)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_HasPropertyProcInfo, (ARG1), (ARG2), (ARG3))
-
-#else
-
-typedef bool (* NP_LOADDS NPN_HasPropertyUPP)(NPP npp, NPObject *obj, NPIdentifier propertyName);
-#define NewNPN_HasPropertyProc(FUNC)		\
-		((NPN_HasPropertyUPP) (FUNC))
-#define CallNPN_HasPropertyProc(FUNC, ARG1, ARG2, ARG3)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3))
-
-#endif
-
-/* NPN_HasMethod */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_HasMethodUPP;
-enum {
-	uppNPN_HasMethodProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPObject*)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(NPIdentifier)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(bool)))
-};
-
-#define NewNPN_HasMethodProc(FUNC)		\
-		(NPN_HasMethodUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_HasMethodProcInfo, GetCurrentArchitecture())
-#define CallNPN_HasMethodProc(FUNC, ARG1, ARG2, ARG3)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_HasMethodProcInfo, (ARG1), (ARG2), (ARG3))
-
-#else
-
-typedef bool (* NP_LOADDS NPN_HasMethodUPP)(NPP npp, NPObject *obj, NPIdentifier propertyName);
-#define NewNPN_HasMethodProc(FUNC)		\
-		((NPN_HasMethodUPP) (FUNC))
-#define CallNPN_HasMethodProc(FUNC, ARG1, ARG2, ARG3)		\
-		(*(FUNC))((ARG1), (ARG2), (ARG3))
-
-#endif
-
-/* NPN_ReleaseVariantValue */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_ReleaseVariantValue;
-enum {
-	uppNPN_ReleaseVariantValueProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPVariant*)))
-		| RESULT_SIZE(SIZE_CODE(0))
-};
-
-#define NewNPN_ReleaseVariantValueProc(FUNC)		\
-		(NPN_ReleaseVariantValueUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_ReleaseVariantValueProcInfo, GetCurrentArchitecture())
-#define CallNPN_ReleaseVariantValueProc(FUNC, ARG1)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_ReleaseVariantValueProcInfo, (ARG1))	
-
-#else
-
-typedef void (* NP_LOADDS NPN_ReleaseVariantValueUPP)(NPVariant *variant);
-#define NewNPN_ReleaseVariantValueProc(FUNC)		\
-		((NPN_ReleaseVariantValueUPP) (FUNC))
-#define CallNPN_ReleaseVariantValueProc(FUNC, ARG1)		\
-		(*(FUNC))((ARG1))
-
-#endif
-
-/* NPN_SetException */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_SetExceptionUPP;
-enum {
-	uppNPN_SetExceptionProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPObject*)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(const NPUTF8*)))
-		| RESULT_SIZE(SIZE_CODE(0))
-};
-
-#define NewNPN_SetExceptionProc(FUNC)		\
-		(NPN_SetExceptionUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_SetExceptionProcInfo, GetCurrentArchitecture())
-#define CallNPN_SetExceptionProc(FUNC, ARG1, ARG2)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_SetExceptionProcInfo, (ARG1), (ARG2))	
-
-#else
-
-typedef void (* NP_LOADDS NPN_SetExceptionUPP)(NPObject *obj, const NPUTF8 *message);
-#define NewNPN_SetExceptionProc(FUNC)		\
-		((NPN_SetExceptionUPP) (FUNC))
-#define CallNPN_SetExceptionProc(FUNC, ARG1, ARG2)		\
-		(*(FUNC))((ARG1), (ARG2))	
-
-#endif
-
-/* NPN_PushPopupsEnabledStateUPP */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_PushPopupsEnabledStateUPP;
-enum {
-	uppNPN_PushPopupsEnabledStateProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-        | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPBool)))
-		| RESULT_SIZE(SIZE_CODE(0))
-};
-
-#define NewNPN_PushPopupsEnabledStateProc(FUNC)		\
-		(NPN_PushPopupsEnabledStateUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_PushPopupsEnabledStateProcInfo, GetCurrentArchitecture())
-#define CallNPN_PushPopupsEnabledStateProc(FUNC, ARG1, ARG2)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_PushPopupsEnabledStateProcInfo, (ARG1), (ARG2))
-
-#else
-
-typedef bool (* NP_LOADDS NPN_PushPopupsEnabledStateUPP)(NPP npp, NPBool enabled);
-#define NewNPN_PushPopupsEnabledStateProc(FUNC)		\
-		((NPN_PushPopupsEnabledStateUPP) (FUNC))
-#define CallNPN_PushPopupsEnabledStateProc(FUNC, ARG1, ARG2)		\
-		(*(FUNC))((ARG1), (ARG2))
-
-#endif
-
-/* NPN_PopPopupsEnabledState */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPN_PopPopupsEnabledStateUPP;
-enum {
-	uppNPN_PopPopupsEnabledStateProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPP)))
-		| RESULT_SIZE(SIZE_CODE(0))
-};
-
-#define NewNPN_PopPopupsEnabledStateProc(FUNC)		\
-		(NPN_PopPopupsEnabledStateUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPN_PopPopupsEnabledStateProcInfo, GetCurrentArchitecture())
-#define CallNPN_PopPopupsEnabledStateProc(FUNC, ARG1)		\
-		(jref)CallUniversalProc((UniversalProcPtr)(FUNC), uppNPN_PopPopupsEnabledStateProcInfo, (ARG1))
-
-#else
-
-typedef bool (* NP_LOADDS NPN_PopPopupsEnabledStateUPP)(NPP npp);
-#define NewNPN_PopPopupsEnabledStateProc(FUNC)		\
-		((NPN_PopPopupsEnabledStateUPP) (FUNC))
-#define CallNPN_PopPopupsEnabledStateProc(FUNC, ARG1)		\
-		(*(FUNC))((ARG1))
-
-#endif
-
-
-
-/******************************************************************************************
- * The actual plugin function table definitions
- *******************************************************************************************/
-
-#ifdef XP_MAC
-#if PRAGMA_STRUCT_ALIGN
-#pragma options align=mac68k
-#endif
-#endif
-
-typedef struct _NPPluginFuncs {
-    uint16 size;
-    uint16 version;
-    NPP_NewUPP newp;
-    NPP_DestroyUPP destroy;
-    NPP_SetWindowUPP setwindow;
-    NPP_NewStreamUPP newstream;
-    NPP_DestroyStreamUPP destroystream;
-    NPP_StreamAsFileUPP asfile;
-    NPP_WriteReadyUPP writeready;
-    NPP_WriteUPP write;
-    NPP_PrintUPP print;
-    NPP_HandleEventUPP event;
-    NPP_URLNotifyUPP urlnotify;
-    JRIGlobalRef javaClass;
-    NPP_GetValueUPP getvalue;
-    NPP_SetValueUPP setvalue;
-} NPPluginFuncs;
-
-typedef struct _NPNetscapeFuncs {
-    uint16 size;
-    uint16 version;
-    NPN_GetURLUPP geturl;
-    NPN_PostURLUPP posturl;
-    NPN_RequestReadUPP requestread;
-    NPN_NewStreamUPP newstream;
-    NPN_WriteUPP write;
-    NPN_DestroyStreamUPP destroystream;
-    NPN_StatusUPP status;
-    NPN_UserAgentUPP uagent;
-    NPN_MemAllocUPP memalloc;
-    NPN_MemFreeUPP memfree;
-    NPN_MemFlushUPP memflush;
-    NPN_ReloadPluginsUPP reloadplugins;
-    NPN_GetJavaEnvUPP getJavaEnv;
-    NPN_GetJavaPeerUPP getJavaPeer;
-    NPN_GetURLNotifyUPP geturlnotify;
-    NPN_PostURLNotifyUPP posturlnotify;
-    NPN_GetValueUPP getvalue;
-    NPN_SetValueUPP setvalue;
-    NPN_InvalidateRectUPP invalidaterect;
-    NPN_InvalidateRegionUPP invalidateregion;
-    NPN_ForceRedrawUPP forceredraw;
-    NPN_GetStringIdentifierUPP getstringidentifier;
-    NPN_GetStringIdentifiersUPP getstringidentifiers;
-    NPN_GetIntIdentifierUPP getintidentifier;
-    NPN_IdentifierIsStringUPP identifierisstring;
-    NPN_UTF8FromIdentifierUPP utf8fromidentifier;
-    NPN_IntFromIdentifierUPP intfromidentifier;
-    NPN_CreateObjectUPP createobject;
-    NPN_RetainObjectUPP retainobject;
-    NPN_ReleaseObjectUPP releaseobject;
-    NPN_InvokeUPP invoke;
-    NPN_InvokeDefaultUPP invokeDefault;
-    NPN_EvaluateUPP evaluate;
-    NPN_GetPropertyUPP getproperty;
-    NPN_SetPropertyUPP setproperty;
-    NPN_RemovePropertyUPP removeproperty;
-    NPN_HasPropertyUPP hasproperty;
-    NPN_HasMethodUPP hasmethod;
-    NPN_ReleaseVariantValueUPP releasevariantvalue;
-    NPN_SetExceptionUPP setexception;
-    NPN_PushPopupsEnabledStateUPP pushpopupsenabledstate;
-    NPN_PopPopupsEnabledStateUPP poppopupsenabledstate;
-} NPNetscapeFuncs;
-
-#ifdef XP_MAC
-#if PRAGMA_STRUCT_ALIGN
-#pragma options align=reset
-#endif
-#endif
-
-
-#if defined(XP_MAC) || defined(XP_MACOSX)
-/******************************************************************************************
- * Mac platform-specific plugin glue stuff
- *******************************************************************************************/
-
-/*
- * Main entry point of the plugin.
- * This routine will be called when the plugin is loaded. The function
- * tables are passed in and the plugin fills in the NPPluginFuncs table
- * and NPPShutdownUPP for Netscape's use.
- */
-
-#if _NPUPP_USE_UPP_
-
-typedef UniversalProcPtr NPP_MainEntryUPP;
-enum {
-	uppNPP_MainEntryProcInfo = kThinkCStackBased
-		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(NPNetscapeFuncs*)))
-		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(NPPluginFuncs*)))
-		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(NPP_ShutdownUPP*)))
-		| RESULT_SIZE(SIZE_CODE(sizeof(NPError)))
-};
-#define NewNPP_MainEntryProc(FUNC)		\
-		(NPP_MainEntryUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNPP_MainEntryProcInfo, GetCurrentArchitecture())
-#define CallNPP_MainEntryProc(FUNC,  netscapeFunc, pluginFunc, shutdownUPP)		\
-		CallUniversalProc((UniversalProcPtr)(FUNC), (ProcInfoType)uppNPP_MainEntryProcInfo, (netscapeFunc), (pluginFunc), (shutdownUPP))
-
-#else
-
-typedef NPError (* NP_LOADDS NPP_MainEntryUPP)(NPNetscapeFuncs*, NPPluginFuncs*, NPP_ShutdownUPP*);
-#define NewNPP_MainEntryProc(FUNC)		\
-		((NPP_MainEntryUPP) (FUNC))
-#define CallNPP_MainEntryProc(FUNC,  netscapeFunc, pluginFunc, shutdownUPP)		\
-		(*(FUNC))((netscapeFunc), (pluginFunc), (shutdownUPP))
-
-#endif
-
-
-/*
- * Mac version(s) of NP_GetMIMEDescription(const char *)
- * These can be called to retreive MIME information from the plugin dynamically
- *
- * Note: For compatibility with Quicktime, BPSupportedMIMEtypes is another way
- *       to get mime info from the plugin only on OSX and may not be supported 
- *       in furture version--use NP_GetMIMEDescription instead
- */
-
-enum
-{
- kBPSupportedMIMETypesStructVers_1    = 1
-};
-
-typedef struct _BPSupportedMIMETypes
-{
- SInt32    structVersion;      /* struct version */
- Handle    typeStrings;        /* STR# formated handle, allocated by plug-in */
- Handle    infoStrings;        /* STR# formated handle, allocated by plug-in */
-} BPSupportedMIMETypes;
-OSErr BP_GetSupportedMIMETypes(BPSupportedMIMETypes *mimeInfo, UInt32 flags);
-
-#if _NPUPP_USE_UPP_
-
-#define NP_GETMIMEDESCRIPTION_NAME "NP_GetMIMEDescriptionRD"
-typedef UniversalProcPtr NP_GetMIMEDescriptionUPP;
-enum {
-	uppNP_GetMIMEDescEntryProc = kThinkCStackBased
-		| RESULT_SIZE(SIZE_CODE(sizeof(const char *)))
-};
-#define NewNP_GetMIMEDescEntryProc(FUNC)		\
-		(NP_GetMIMEDescriptionUPP) NewRoutineDescriptor((ProcPtr)(FUNC), uppNP_GetMIMEDescEntryProc, GetCurrentArchitecture())
-#define CallNP_GetMIMEDescEntryProc(FUNC)		\
-		(const char *)CallUniversalProc((UniversalProcPtr)(FUNC), (ProcInfoType)uppNP_GetMIMEDescEntryProc)
-
-
-#else  /* !_NPUPP_USE_UPP_ */
-
- /* NP_GetMIMEDescription */
-#define NP_GETMIMEDESCRIPTION_NAME "NP_GetMIMEDescription"
-typedef const char* (* NP_LOADDS NP_GetMIMEDescriptionUPP)();
-#define NewNP_GetMIMEDescEntryProc(FUNC)		\
-		((NP_GetMIMEDescriptionUPP) (FUNC))
-#define CallNP_GetMIMEDescEntryProc(FUNC)		\
-		(*(FUNC))()
-/* BP_GetSupportedMIMETypes */
-typedef OSErr (* NP_LOADDS BP_GetSupportedMIMETypesUPP)(BPSupportedMIMETypes*, UInt32);
-#define NewBP_GetSupportedMIMETypesEntryProc(FUNC)		\
-		((BP_GetSupportedMIMETypesUPP) (FUNC))
-#define CallBP_GetMIMEDescEntryProc(FUNC,  mimeInfo, flags)		\
-		(*(FUNC))((mimeInfo), (flags))
-
-#endif
-#endif /* MAC */
-
-#if defined(_WINDOWS)
-#define OSCALL WINAPI
-#else
-#if defined(__OS2__)
-#define OSCALL _System
-#else
-#define OSCALL
-#endif
-#endif
-
-#if defined( _WINDOWS ) || defined (__OS2__)
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* plugin meta member functions */
-#if defined(__OS2__)
-
-typedef struct _NPPluginData {   /* Alternate OS2 Plugin interface */
-    char *pMimeTypes;
-    char *pFileExtents;
-    char *pFileOpenTemplate;
-    char *pProductName;
-    char *pProductDescription;
-    unsigned long dwProductVersionMS;
-    unsigned long dwProductVersionLS;
-} NPPluginData;
-
-NPError OSCALL NP_GetPluginData(NPPluginData * pPluginData);
-
-#endif
-
-NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* pFuncs);
-
-NPError OSCALL NP_Initialize(NPNetscapeFuncs* pFuncs);
-
-NPError OSCALL NP_Shutdown();
-
-char*	NP_GetMIMEDescription();
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _WINDOWS || __OS2__ */
-
-#if defined(__OS2__)
-#pragma pack()
-#endif
-
-#ifdef XP_UNIX
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* plugin meta member functions */
-
-char*	NP_GetMIMEDescription(void);
-NPError	NP_Initialize(NPNetscapeFuncs*, NPPluginFuncs*);
-NPError	NP_Shutdown(void);
-NPError NP_GetValue(void *future, NPPVariable aVariable, void *aValue);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* XP_UNIX */
-
-#endif /* _NPUPP_H_ */
diff --git a/third_party/gecko/include/obsolete/pralarm.h b/third_party/gecko/include/obsolete/pralarm.h
deleted file mode 100644
index 20476e4..0000000
--- a/third_party/gecko/include/obsolete/pralarm.h
+++ /dev/null
@@ -1,194 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Netscape Portable Runtime (NSPR).
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998-2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/*
-** File:		pralarm.h
-** Description:	API to periodic alarms.
-**
-**
-** Alarms are defined to invoke some client specified function at 
-** a time in the future. The notification may be a one time event
-** or repeated at a fixed interval. The interval at which the next
-** notification takes place may be modified by the client code only
-** during the respective notification.
-**
-** The notification is delivered on a thread that is part of the
-** alarm context (PRAlarm). The thread will inherit the priority
-** of the Alarm creator.
-**
-** Any number of periodic alarms (PRAlarmID) may be created within
-** the context of a single alarm (PRAlarm). The notifications will be
-** scheduled as close to the desired time as possible.
-**
-** Repeating periodic notifies are expected to run at a fixed rate.
-** That rate is expressed as some number of notifies per period where
-** the period is much larger than a PRIntervalTime (see prinrval.h).
-*/
-
-#if !defined(pralarm_h)
-#define pralarm_h
-
-#include "prtypes.h"
-#include "prinrval.h"
-
-
-PR_BEGIN_EXTERN_C
-
-/**********************************************************************/
-/************************* TYPES AND CONSTANTS ************************/
-/**********************************************************************/
-
-typedef struct PRAlarm PRAlarm;
-typedef struct PRAlarmID PRAlarmID;
-
-typedef PRBool (PR_CALLBACK *PRPeriodicAlarmFn)(
-    PRAlarmID *id, void *clientData, PRUint32 late);
-
-/**********************************************************************/
-/****************************** FUNCTIONS *****************************/
-/**********************************************************************/
-
-/***********************************************************************
-** FUNCTION:    PR_CreateAlarm
-** DESCRIPTION:
-**  Create an alarm context.
-** INPUTS:      void
-** OUTPUTS:     None
-** RETURN:      PRAlarm*
-**  
-** SIDE EFFECTS:
-**  This creates an alarm context, which is an object used for subsequent
-**  notification creations. It also creates a thread that will be used to
-** deliver the notifications that are expected to be defined. The client
-** is resposible for destroying the context when appropriate.
-** RESTRICTIONS:
-**  None. 
-** MEMORY:      The object (PRAlarm) and a thread to support notifications.
-** ALGORITHM:   N/A
-***********************************************************************/
-NSPR_API(PRAlarm*) PR_CreateAlarm(void);
-
-/***********************************************************************
-** FUNCTION:    PR_DestroyAlarm
-** DESCRIPTION:
-**  Destroys the context created by PR_CreateAlarm().
-** INPUTS:      PRAlarm*
-** OUTPUTS:     None
-** RETURN:      PRStatus
-**  
-** SIDE EFFECTS:
-**  This destroys the context that was created by PR_CreateAlarm().
-**  If there are any active alarms (PRAlarmID), they will be cancelled.
-**  Once that is done, the thread that was used to deliver the alarms
-**  will be joined. 
-** RESTRICTIONS:
-**  None. 
-** MEMORY:      N/A
-** ALGORITHM:   N/A
-***********************************************************************/
-NSPR_API(PRStatus) PR_DestroyAlarm(PRAlarm *alarm);
-
-/***********************************************************************
-** FUNCTION:    PR_SetAlarm
-** DESCRIPTION:
-**  Creates a periodic notifier that is to be delivered to a specified
-**  function at some fixed interval.
-** INPUTS:      PRAlarm *alarm              Parent alarm context
-**              PRIntervalTime period       Interval over which the notifies
-**                                          are delivered.
-**              PRUint32 rate               The rate within the interval that
-**                                          the notifies will be delivered.
-**              PRPeriodicAlarmFn function  Entry point where the notifies
-**                                          will be delivered.
-** OUTPUTS:     None
-** RETURN:      PRAlarmID*                  Handle to the notifier just created
-**                                          or NULL if the request failed.
-**  
-** SIDE EFFECTS:
-**  A periodic notifier is created. The notifications will be delivered
-**  by the alarm's internal thread at a fixed interval whose rate is the
-**  number of interrupts per interval specified. The first notification
-**  will be delivered as soon as possible, and they will continue until
-**  the notifier routine indicates that they should cease of the alarm
-**  context is destroyed (PR_DestroyAlarm).
-** RESTRICTIONS:
-**  None. 
-** MEMORY:      Memory for the notifier object.
-** ALGORITHM:   The rate at which notifications are delivered are stated
-**              to be "'rate' notifies per 'interval'". The exact time of
-**              the notification is computed based on a epoch established
-**              when the notifier was set. Each notification is delivered
-**              not ealier than the epoch plus the fixed rate times the
-**              notification sequence number. Such notifications have the
-**              potential to be late by not more than 'interval'/'rate'.
-**              The amount of lateness of one notification is taken into
-**              account on the next in an attempt to avoid long term slew.  
-***********************************************************************/
-NSPR_API(PRAlarmID*) PR_SetAlarm(
-    PRAlarm *alarm, PRIntervalTime period, PRUint32 rate,
-    PRPeriodicAlarmFn function, void *clientData);
-
-/***********************************************************************
-** FUNCTION:    PR_ResetAlarm
-** DESCRIPTION:
-**  Resets an existing alarm.
-** INPUTS:      PRAlarmID *id               Identify of the notifier.
-**              PRIntervalTime period       Interval over which the notifies
-**                                          are delivered.
-**              PRUint32 rate               The rate within the interval that
-**                                          the notifies will be delivered.
-** OUTPUTS:     None
-** RETURN:      PRStatus                    Indication of completion.
-**  
-** SIDE EFFECTS:
-**  An existing alarm may have its period and rate redefined. The
-**  additional side effect is that the notifier's epoch is recomputed.
-**  The first notification delivered by the newly refreshed alarm is
-**  defined to be 'interval'/'rate' from the time of the reset.
-** RESTRICTIONS:
-**  This function may only be called in the notifier for that alarm.
-** MEMORY:      N/A.
-** ALGORITHM:   See PR_SetAlarm().  
-***********************************************************************/
-NSPR_API(PRStatus) PR_ResetAlarm(
-	PRAlarmID *id, PRIntervalTime period, PRUint32 rate);
-
-PR_END_EXTERN_C
-
-#endif /* !defined(pralarm_h) */
-
-/* prinrval.h */
diff --git a/third_party/gecko/include/obsolete/probslet.h b/third_party/gecko/include/obsolete/probslet.h
deleted file mode 100644
index 374e696..0000000
--- a/third_party/gecko/include/obsolete/probslet.h
+++ /dev/null
@@ -1,175 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Netscape Portable Runtime (NSPR).
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998-2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/*
-** A collection of things thought to be obsolete
-*/
-
-#if defined(PROBSLET_H)
-#else
-#define PROBSLET_H
-
-#include "prio.h"
-
-PR_BEGIN_EXTERN_C
-
-/*
-** Yield the current thread.  The proper function to use in place of
-** PR_Yield() is PR_Sleep() with an argument of PR_INTERVAL_NO_WAIT.
-*/
-NSPR_API(PRStatus) PR_Yield(void);
-
-/************************************************************************/
-/************* The following definitions are for select *****************/
-/************************************************************************/
-
-/*
-** The following is obsolete and will be deleted in the next release!
-** These are provided for compatibility, but are GUARANTEED to be slow.
-**
-** Override PR_MAX_SELECT_DESC if you need more space in the select set.
-*/
-#ifndef PR_MAX_SELECT_DESC
-#define PR_MAX_SELECT_DESC 1024
-#endif
-typedef struct PR_fd_set {
-    PRUint32      hsize;
-    PRFileDesc   *harray[PR_MAX_SELECT_DESC];
-    PRUint32      nsize;
-    PRInt32       narray[PR_MAX_SELECT_DESC];
-} PR_fd_set;
-
-/*
-*************************************************************************
-** FUNCTION:    PR_Select
-** DESCRIPTION:
-**
-** The call returns as soon as I/O is ready on one or more of the underlying
-** file/socket descriptors or an exceptional condition is pending. A count of the 
-** number of ready descriptors is returned unless a timeout occurs in which case 
-** zero is returned.  On return, PR_Select replaces the given descriptor sets with 
-** subsets consisting of those descriptors that are ready for the requested condition.
-** The total number of ready descriptors in all the sets is the return value.
-**
-** INPUTS:
-**   PRInt32 num             
-**       This argument is unused but is provided for select(unix) interface
-**       compatability.  All input PR_fd_set arguments are self-describing
-**       with its own maximum number of elements in the set.
-**                               
-**   PR_fd_set *readfds
-**       A set describing the io descriptors for which ready for reading
-**       condition is of interest.  
-**                               
-**   PR_fd_set *writefds
-**       A set describing the io descriptors for which ready for writing
-**       condition is of interest.  
-**                               
-**   PR_fd_set *exceptfds
-**       A set describing the io descriptors for which exception pending
-**       condition is of interest.  
-**
-**   Any of the above readfds, writefds or exceptfds may be given as NULL 
-**   pointers if no descriptors are of interest for that particular condition.                          
-**   
-**   PRIntervalTime timeout  
-**       Amount of time the call will block waiting for I/O to become ready. 
-**       If this time expires without any I/O becoming ready, the result will
-**       be zero.
-**
-** OUTPUTS:    
-**   PR_fd_set *readfds
-**       A set describing the io descriptors which are ready for reading.
-**                               
-**   PR_fd_set *writefds
-**       A set describing the io descriptors which are ready for writing.
-**                               
-**   PR_fd_set *exceptfds
-**       A set describing the io descriptors which have pending exception.
-**
-** RETURN:PRInt32
-**   Number of io descriptors with asked for conditions or zero if the function
-**   timed out or -1 on failure.  The reason for the failure is obtained by 
-**   calling PR_GetError().
-** XXX can we implement this on windoze and mac?
-**************************************************************************
-*/
-NSPR_API(PRInt32) PR_Select(
-    PRInt32 num, PR_fd_set *readfds, PR_fd_set *writefds,
-    PR_fd_set *exceptfds, PRIntervalTime timeout);
-
-/* 
-** The following are not thread safe for two threads operating on them at the
-** same time.
-**
-** The following routines are provided for manipulating io descriptor sets.
-** PR_FD_ZERO(&fdset) initializes a descriptor set fdset to the null set.
-** PR_FD_SET(fd, &fdset) includes a particular file descriptor fd in fdset.
-** PR_FD_CLR(fd, &fdset) removes a file descriptor fd from fdset.  
-** PR_FD_ISSET(fd, &fdset) is nonzero if file descriptor fd is a member of 
-** fdset, zero otherwise.
-**
-** PR_FD_NSET(osfd, &fdset) includes a particular native file descriptor osfd
-** in fdset.
-** PR_FD_NCLR(osfd, &fdset) removes a native file descriptor osfd from fdset.  
-** PR_FD_NISSET(osfd, &fdset) is nonzero if native file descriptor osfd is a member of 
-** fdset, zero otherwise.
-*/
-
-NSPR_API(void)        PR_FD_ZERO(PR_fd_set *set);
-NSPR_API(void)        PR_FD_SET(PRFileDesc *fd, PR_fd_set *set);
-NSPR_API(void)        PR_FD_CLR(PRFileDesc *fd, PR_fd_set *set);
-NSPR_API(PRInt32)     PR_FD_ISSET(PRFileDesc *fd, PR_fd_set *set);
-NSPR_API(void)        PR_FD_NSET(PRInt32 osfd, PR_fd_set *set);
-NSPR_API(void)        PR_FD_NCLR(PRInt32 osfd, PR_fd_set *set);
-NSPR_API(PRInt32)     PR_FD_NISSET(PRInt32 osfd, PR_fd_set *set);
-
-#ifndef NO_NSPR_10_SUPPORT
-#ifdef XP_MAC
-#include <stat.h>
-#else
-#include <sys/stat.h>
-#endif
-
-NSPR_API(PRInt32) PR_Stat(const char *path, struct stat *buf);
-#endif /* NO_NSPR_10_SUPPORT */
-
-PR_END_EXTERN_C
-
-#endif /* defined(PROBSLET_H) */
-
-/* probslet.h */
diff --git a/third_party/gecko/include/obsolete/protypes.h b/third_party/gecko/include/obsolete/protypes.h
deleted file mode 100644
index c2a3c9b..0000000
--- a/third_party/gecko/include/obsolete/protypes.h
+++ /dev/null
@@ -1,254 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Netscape Portable Runtime (NSPR).
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998-2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/*
- * This header typedefs the old 'native' types to the new PR<type>s.
- * These definitions are scheduled to be eliminated at the earliest
- * possible time. The NSPR API is implemented and documented using
- * the new definitions.
- */
-
-#if !defined(PROTYPES_H)
-#define PROTYPES_H
-
-typedef PRUintn uintn;
-#ifndef _XP_Core_
-typedef PRIntn intn;
-#endif
-
-/*
- * It is trickier to define uint, int8, uint8, int16, uint16,
- * int32, uint32, int64, and uint64 because some of these int
- * types are defined by standard header files on some platforms.
- * Our strategy here is to include all such standard headers
- * first, and then define these int types only if they are not
- * defined by those standard headers.
- */
-
-/*
- * BeOS defines all the int types below in its standard header
- * file SupportDefs.h.
- */
-#ifdef XP_BEOS
-#include <support/SupportDefs.h>
-#endif
-
-/*
- * OpenVMS defines all the int types below in its standard
- * header files ints.h and types.h.
- */
-#ifdef VMS
-#include <ints.h>
-#include <types.h>
-#endif
-
-/*
- * SVR4 typedef of uint is commonly found on UNIX machines.
- *
- * On AIX 4.3, sys/inttypes.h (which is included by sys/types.h)
- * defines the types int8, int16, int32, and int64.
- */
-#ifdef XP_UNIX
-#include <sys/types.h>
-#endif
-
-/* model.h on HP-UX defines int8, int16, and int32. */
-#ifdef HPUX
-#include <model.h>
-#endif
-
-/*
- * uint
- */
-
-#if !defined(XP_BEOS) && !defined(VMS) \
-    && !defined(XP_UNIX) || defined(NTO)
-typedef PRUintn uint;
-#endif
-
-/*
- * uint64
- */
-
-#if !defined(XP_BEOS) && !defined(VMS)
-typedef PRUint64 uint64;
-#endif
-
-/*
- * uint32
- */
-
-#if !defined(XP_BEOS) && !defined(VMS)
-#if !defined(XP_MAC) && !defined(_WIN32) && !defined(XP_OS2) && !defined(NTO)
-typedef PRUint32 uint32;
-#else
-typedef unsigned long uint32;
-#endif
-#endif
-
-/*
- * uint16
- */
-
-#if !defined(XP_BEOS) && !defined(VMS)
-typedef PRUint16 uint16;
-#endif
-
-/*
- * uint8
- */
-
-#if !defined(XP_BEOS) && !defined(VMS)
-typedef PRUint8 uint8;
-#endif
-
-/*
- * int64
- */
-
-#if !defined(XP_BEOS) && !defined(VMS) \
-    && !defined(_PR_AIX_HAVE_BSD_INT_TYPES)
-typedef PRInt64 int64;
-#endif
-
-/*
- * int32
- */
-
-#if !defined(XP_BEOS) && !defined(VMS) \
-    && !defined(_PR_AIX_HAVE_BSD_INT_TYPES) \
-    && !defined(HPUX)
-#if !defined(WIN32) || !defined(_WINSOCK2API_)  /* defines its own "int32" */
-#if !defined(XP_MAC) && !defined(_WIN32) && !defined(XP_OS2) && !defined(NTO)
-typedef PRInt32 int32;
-#else
-typedef long int32;
-#endif
-#endif
-#endif
-
-/*
- * int16
- */
-
-#if !defined(XP_BEOS) && !defined(VMS) \
-    && !defined(_PR_AIX_HAVE_BSD_INT_TYPES) \
-    && !defined(HPUX)
-typedef PRInt16 int16;
-#endif
-
-/*
- * int8
- */
-
-#if !defined(XP_BEOS) && !defined(VMS) \
-    && !defined(_PR_AIX_HAVE_BSD_INT_TYPES) \
-    && !defined(HPUX)
-typedef PRInt8 int8;
-#endif
-
-typedef PRFloat64 float64;
-typedef PRUptrdiff uptrdiff_t;
-typedef PRUword uprword_t;
-typedef PRWord prword_t;
-
-
-/* Re: prbit.h */
-#define TEST_BIT	PR_TEST_BIT
-#define SET_BIT		PR_SET_BIT
-#define CLEAR_BIT	PR_CLEAR_BIT
-
-/* Re: prarena.h->plarena.h */
-#define PRArena PLArena
-#define PRArenaPool PLArenaPool
-#define PRArenaStats PLArenaStats
-#define PR_ARENA_ALIGN PL_ARENA_ALIGN
-#define PR_INIT_ARENA_POOL PL_INIT_ARENA_POOL
-#define PR_ARENA_ALLOCATE PL_ARENA_ALLOCATE
-#define PR_ARENA_GROW PL_ARENA_GROW
-#define PR_ARENA_MARK PL_ARENA_MARK
-#define PR_CLEAR_UNUSED PL_CLEAR_UNUSED
-#define PR_CLEAR_ARENA PL_CLEAR_ARENA
-#define PR_ARENA_RELEASE PL_ARENA_RELEASE
-#define PR_COUNT_ARENA PL_COUNT_ARENA
-#define PR_ARENA_DESTROY PL_ARENA_DESTROY
-#define PR_InitArenaPool PL_InitArenaPool
-#define PR_FreeArenaPool PL_FreeArenaPool
-#define PR_FinishArenaPool PL_FinishArenaPool
-#define PR_CompactArenaPool PL_CompactArenaPool
-#define PR_ArenaFinish PL_ArenaFinish
-#define PR_ArenaAllocate PL_ArenaAllocate
-#define PR_ArenaGrow PL_ArenaGrow
-#define PR_ArenaRelease PL_ArenaRelease
-#define PR_ArenaCountAllocation PL_ArenaCountAllocation
-#define PR_ArenaCountInplaceGrowth PL_ArenaCountInplaceGrowth
-#define PR_ArenaCountGrowth PL_ArenaCountGrowth
-#define PR_ArenaCountRelease PL_ArenaCountRelease
-#define PR_ArenaCountRetract PL_ArenaCountRetract
-
-/* Re: prhash.h->plhash.h */
-#define PRHashEntry PLHashEntry
-#define PRHashTable PLHashTable
-#define PRHashNumber PLHashNumber
-#define PRHashFunction PLHashFunction
-#define PRHashComparator PLHashComparator
-#define PRHashEnumerator PLHashEnumerator
-#define PRHashAllocOps PLHashAllocOps
-#define PR_NewHashTable PL_NewHashTable
-#define PR_HashTableDestroy PL_HashTableDestroy
-#define PR_HashTableRawLookup PL_HashTableRawLookup
-#define PR_HashTableRawAdd PL_HashTableRawAdd
-#define PR_HashTableRawRemove PL_HashTableRawRemove
-#define PR_HashTableAdd PL_HashTableAdd
-#define PR_HashTableRemove PL_HashTableRemove
-#define PR_HashTableEnumerateEntries PL_HashTableEnumerateEntries
-#define PR_HashTableLookup PL_HashTableLookup
-#define PR_HashTableDump PL_HashTableDump
-#define PR_HashString PL_HashString
-#define PR_CompareStrings PL_CompareStrings
-#define PR_CompareValues PL_CompareValues
-
-#if defined(XP_MAC)
-#ifndef TRUE				/* Mac standard is lower case true */
-	#define TRUE 1
-#endif
-#ifndef FALSE				/* Mac standard is lower case false */
-	#define FALSE 0
-#endif
-#endif
-
-#endif /* !defined(PROTYPES_H) */
diff --git a/third_party/gecko/include/obsolete/prsem.h b/third_party/gecko/include/obsolete/prsem.h
deleted file mode 100644
index fd0863a..0000000
--- a/third_party/gecko/include/obsolete/prsem.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Netscape Portable Runtime (NSPR).
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998-2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef prsem_h___
-#define prsem_h___
-
-/*
-** API for counting semaphores. Semaphores are counting synchronizing 
-** variables based on a lock and a condition variable.  They are lightweight 
-** contention control for a given count of resources.
-*/
-#include "prtypes.h"
-
-PR_BEGIN_EXTERN_C
-
-typedef struct PRSemaphore PRSemaphore;
-
-/*
-** Create a new semaphore object.
-*/
-NSPR_API(PRSemaphore*) PR_NewSem(PRUintn value);
-
-/*
-** Destroy the given semaphore object.
-**
-*/
-NSPR_API(void) PR_DestroySem(PRSemaphore *sem);
-
-/*
-** Wait on a Semaphore.
-** 
-** This routine allows a calling thread to wait or proceed depending upon the 
-** state of the semahore sem. The thread can proceed only if the counter value 
-** of the semaphore sem is currently greater than 0. If the value of semaphore 
-** sem is positive, it is decremented by one and the routine returns immediately 
-** allowing the calling thread to continue. If the value of semaphore sem is 0, 
-** the calling thread blocks awaiting the semaphore to be released by another 
-** thread.
-** 
-** This routine can return PR_PENDING_INTERRUPT if the waiting thread 
-** has been interrupted.
-*/
-NSPR_API(PRStatus) PR_WaitSem(PRSemaphore *sem);
-
-/*
-** This routine increments the counter value of the semaphore. If other threads 
-** are blocked for the semaphore, then the scheduler will determine which ONE 
-** thread will be unblocked.
-*/
-NSPR_API(void) PR_PostSem(PRSemaphore *sem);
-
-/*
-** Returns the value of the semaphore referenced by sem without affecting
-** the state of the semaphore.  The value represents the semaphore vaule
-F** at the time of the call, but may not be the actual value when the
-** caller inspects it.
-*/
-NSPR_API(PRUintn) PR_GetValueSem(PRSemaphore *sem);
-
-PR_END_EXTERN_C
-
-#endif /* prsem_h___ */
diff --git a/third_party/gecko/include/prcpucfg.h b/third_party/gecko/include/prcpucfg.h
deleted file mode 100644
index 310fff8..0000000
--- a/third_party/gecko/include/prcpucfg.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Netscape Portable Runtime (NSPR).
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998-2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef nspr_cpucfg___
-#define nspr_cpucfg___
-
-#ifndef XP_PC
-#define XP_PC
-#endif
-
-#ifndef WIN32
-#define WIN32
-#endif
-
-#ifndef WIN95
-#define WIN95
-#endif
-
-#define PR_AF_INET6 23  /* same as AF_INET6 */
-
-#if defined(_M_IX86) || defined(_X86_)
-
-#define IS_LITTLE_ENDIAN 1
-#undef  IS_BIG_ENDIAN
-
-#define PR_BYTES_PER_BYTE   1
-#define PR_BYTES_PER_SHORT  2
-#define PR_BYTES_PER_INT    4
-#define PR_BYTES_PER_INT64  8
-#define PR_BYTES_PER_LONG   4
-#define PR_BYTES_PER_FLOAT  4
-#define PR_BYTES_PER_WORD	4
-#define PR_BYTES_PER_DWORD	8
-#define PR_BYTES_PER_DOUBLE 8
-
-#define PR_BITS_PER_BYTE    8
-#define PR_BITS_PER_SHORT   16
-#define PR_BITS_PER_INT     32
-#define PR_BITS_PER_INT64   64
-#define PR_BITS_PER_LONG    32
-#define PR_BITS_PER_FLOAT   32
-#define PR_BITS_PER_WORD	32
-#define PR_BITS_PER_DWORD	64
-#define PR_BITS_PER_DOUBLE  64
-
-#define PR_BITS_PER_BYTE_LOG2   3
-#define PR_BITS_PER_SHORT_LOG2  4
-#define PR_BITS_PER_INT_LOG2    5
-#define PR_BITS_PER_INT64_LOG2  6
-#define PR_BITS_PER_LONG_LOG2   5
-#define PR_BITS_PER_FLOAT_LOG2  5
-#define PR_BITS_PER_WORD_LOG2	5
-#define PR_BITS_PER_DWORD_LOG2	6
-#define PR_BITS_PER_DOUBLE_LOG2 6
-
-#define PR_ALIGN_OF_SHORT   2
-#define PR_ALIGN_OF_INT     4
-#define PR_ALIGN_OF_LONG    4
-#define PR_ALIGN_OF_INT64   8
-#define PR_ALIGN_OF_FLOAT   4
-#define PR_ALIGN_OF_WORD	4
-#define PR_ALIGN_OF_DWORD	8
-#define PR_ALIGN_OF_DOUBLE  4
-#define PR_ALIGN_OF_POINTER 4
-
-#define PR_BYTES_PER_WORD_LOG2	2
-#define PR_BYTES_PER_DWORD_LOG2	2
-
-#elif defined(_ALPHA_)
-
-#define IS_LITTLE_ENDIAN 1
-#undef  IS_BIG_ENDIAN
-
-#define PR_BYTES_PER_BYTE   1
-#define PR_BYTES_PER_SHORT  2
-#define PR_BYTES_PER_INT    4
-#define PR_BYTES_PER_INT64  8
-#define PR_BYTES_PER_LONG   4
-#define PR_BYTES_PER_FLOAT  4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD   4
-#define PR_BYTES_PER_DWORD  8
-
-#define PR_BITS_PER_BYTE    8
-#define PR_BITS_PER_SHORT   16
-#define PR_BITS_PER_INT     32
-#define PR_BITS_PER_INT64   64
-#define PR_BITS_PER_LONG    32
-#define PR_BITS_PER_FLOAT   32
-#define PR_BITS_PER_DOUBLE  64
-#define PR_BITS_PER_WORD    32
-
-#define PR_BITS_PER_BYTE_LOG2   3
-#define PR_BITS_PER_SHORT_LOG2  4
-#define PR_BITS_PER_INT_LOG2    5
-#define PR_BITS_PER_INT64_LOG2  6
-#define PR_BITS_PER_LONG_LOG2   5
-#define PR_BITS_PER_FLOAT_LOG2  5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2   5
-
-#define PR_BYTES_PER_WORD_LOG2  2
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#define PR_ALIGN_OF_SHORT   2
-#define PR_ALIGN_OF_INT     4
-#define PR_ALIGN_OF_LONG    4
-#define PR_ALIGN_OF_INT64   8
-#define PR_ALIGN_OF_FLOAT   4
-#define PR_ALIGN_OF_DOUBLE  8
-#define PR_ALIGN_OF_POINTER 4
-
-#else /* defined(_M_IX86) || defined(_X86_) */
-
-#error unknown processor architecture
-
-#endif /* defined(_M_IX86) || defined(_X86_) */
-
-#define HAVE_LONG_LONG
-
-#ifndef NO_NSPR_10_SUPPORT
-
-#define BYTES_PER_BYTE      PR_BYTES_PER_BYTE
-#define BYTES_PER_SHORT     PR_BYTES_PER_SHORT
-#define BYTES_PER_INT       PR_BYTES_PER_INT
-#define BYTES_PER_INT64     PR_BYTES_PER_INT64
-#define BYTES_PER_LONG      PR_BYTES_PER_LONG
-#define BYTES_PER_FLOAT     PR_BYTES_PER_FLOAT
-#define BYTES_PER_DOUBLE    PR_BYTES_PER_DOUBLE
-#define BYTES_PER_WORD      PR_BYTES_PER_WORD
-#define BYTES_PER_DWORD     PR_BYTES_PER_DWORD
-
-#define BITS_PER_BYTE       PR_BITS_PER_BYTE
-#define BITS_PER_SHORT      PR_BITS_PER_SHORT
-#define BITS_PER_INT        PR_BITS_PER_INT
-#define BITS_PER_INT64      PR_BITS_PER_INT64
-#define BITS_PER_LONG       PR_BITS_PER_LONG
-#define BITS_PER_FLOAT      PR_BITS_PER_FLOAT
-#define BITS_PER_DOUBLE     PR_BITS_PER_DOUBLE
-#define BITS_PER_WORD       PR_BITS_PER_WORD
-
-#define BITS_PER_BYTE_LOG2  PR_BITS_PER_BYTE_LOG2
-#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
-#define BITS_PER_INT_LOG2   PR_BITS_PER_INT_LOG2
-#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
-#define BITS_PER_LONG_LOG2  PR_BITS_PER_LONG_LOG2
-#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
-#define BITS_PER_DOUBLE_LOG2    PR_BITS_PER_DOUBLE_LOG2
-#define BITS_PER_WORD_LOG2  PR_BITS_PER_WORD_LOG2
-
-#define ALIGN_OF_SHORT      PR_ALIGN_OF_SHORT
-#define ALIGN_OF_INT        PR_ALIGN_OF_INT
-#define ALIGN_OF_LONG       PR_ALIGN_OF_LONG
-#define ALIGN_OF_INT64      PR_ALIGN_OF_INT64
-#define ALIGN_OF_FLOAT      PR_ALIGN_OF_FLOAT
-#define ALIGN_OF_DOUBLE     PR_ALIGN_OF_DOUBLE
-#define ALIGN_OF_POINTER    PR_ALIGN_OF_POINTER
-#define ALIGN_OF_WORD       PR_ALIGN_OF_WORD
-
-#define BYTES_PER_WORD_LOG2		PR_BYTES_PER_WORD_LOG2
-#define BYTES_PER_DWORD_LOG2    PR_BYTES_PER_DWORD_LOG2
-#define WORDS_PER_DWORD_LOG2    PR_WORDS_PER_DWORD_LOG2
-
-#endif /* NO_NSPR_10_SUPPORT */
-
-#endif /* nspr_cpucfg___ */
diff --git a/third_party/gecko/include/prtypes.h b/third_party/gecko/include/prtypes.h
deleted file mode 100644
index 3eb1554..0000000
--- a/third_party/gecko/include/prtypes.h
+++ /dev/null
@@ -1,547 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Netscape Portable Runtime (NSPR).
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998-2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/*
-** File:                prtypes.h
-** Description: Definitions of NSPR's basic types
-**
-** Prototypes and macros used to make up for deficiencies that we have found
-** in ANSI environments.
-**
-** Since we do not wrap <stdlib.h> and all the other standard headers, authors
-** of portable code will not know in general that they need these definitions.
-** Instead of requiring these authors to find the dependent uses in their code
-** and take the following steps only in those C files, we take steps once here
-** for all C files.
-**/
-
-#ifndef prtypes_h___
-#define prtypes_h___
-
-#ifdef MDCPUCFG
-#include MDCPUCFG
-#else
-#include "prcpucfg.h"
-#endif
-
-#include <stddef.h>
-
-/***********************************************************************
-** MACROS:      PR_EXTERN
-**              PR_IMPLEMENT
-** DESCRIPTION:
-**      These are only for externally visible routines and globals.  For
-**      internal routines, just use "extern" for type checking and that
-**      will not export internal cross-file or forward-declared symbols.
-**      Define a macro for declaring procedures return types. We use this to
-**      deal with windoze specific type hackery for DLL definitions. Use
-**      PR_EXTERN when the prototype for the method is declared. Use
-**      PR_IMPLEMENT for the implementation of the method.
-**
-** Example:
-**   in dowhim.h
-**     PR_EXTERN( void ) DoWhatIMean( void );
-**   in dowhim.c
-**     PR_IMPLEMENT( void ) DoWhatIMean( void ) { return; }
-**
-**
-***********************************************************************/
-#if defined(WIN32)
-
-#define PR_EXPORT(__type) extern __declspec(dllexport) __type
-#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
-#define PR_IMPORT(__type) __declspec(dllimport) __type
-#define PR_IMPORT_DATA(__type) __declspec(dllimport) __type
-
-#define PR_EXTERN(__type) extern __declspec(dllexport) __type
-#define PR_IMPLEMENT(__type) __declspec(dllexport) __type
-#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type
-#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type
-
-#define PR_CALLBACK
-#define PR_CALLBACK_DECL
-#define PR_STATIC_CALLBACK(__x) static __x
-
-#elif defined(XP_BEOS)
-
-#define PR_EXPORT(__type) extern __declspec(dllexport) __type
-#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
-#define PR_IMPORT(__type) extern __declspec(dllexport) __type
-#define PR_IMPORT_DATA(__type) extern __declspec(dllexport) __type
-
-#define PR_EXTERN(__type) extern __declspec(dllexport) __type
-#define PR_IMPLEMENT(__type) __declspec(dllexport) __type
-#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type
-#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type
-
-#define PR_CALLBACK
-#define PR_CALLBACK_DECL
-#define PR_STATIC_CALLBACK(__x) static __x
-
-#elif defined(WIN16)
-
-#define PR_CALLBACK_DECL        __cdecl
-
-#if defined(_WINDLL)
-#define PR_EXPORT(__type) extern __type _cdecl _export _loadds
-#define PR_IMPORT(__type) extern __type _cdecl _export _loadds
-#define PR_EXPORT_DATA(__type) extern __type _export
-#define PR_IMPORT_DATA(__type) extern __type _export
-
-#define PR_EXTERN(__type) extern __type _cdecl _export _loadds
-#define PR_IMPLEMENT(__type) __type _cdecl _export _loadds
-#define PR_EXTERN_DATA(__type) extern __type _export
-#define PR_IMPLEMENT_DATA(__type) __type _export
-
-#define PR_CALLBACK             __cdecl __loadds
-#define PR_STATIC_CALLBACK(__x) static __x PR_CALLBACK
-
-#else /* this must be .EXE */
-#define PR_EXPORT(__type) extern __type _cdecl _export
-#define PR_IMPORT(__type) extern __type _cdecl _export
-#define PR_EXPORT_DATA(__type) extern __type _export
-#define PR_IMPORT_DATA(__type) extern __type _export
-
-#define PR_EXTERN(__type) extern __type _cdecl _export
-#define PR_IMPLEMENT(__type) __type _cdecl _export
-#define PR_EXTERN_DATA(__type) extern __type _export
-#define PR_IMPLEMENT_DATA(__type) __type _export
-
-#define PR_CALLBACK             __cdecl __loadds
-#define PR_STATIC_CALLBACK(__x) __x PR_CALLBACK
-#endif /* _WINDLL */
-
-#elif defined(XP_MAC)
-
-#define PR_EXPORT(__type) extern __declspec(export) __type
-#define PR_EXPORT_DATA(__type) extern __declspec(export) __type
-#define PR_IMPORT(__type) extern __declspec(export) __type
-#define PR_IMPORT_DATA(__type) extern __declspec(export) __type
-
-#define PR_EXTERN(__type) extern __declspec(export) __type
-#define PR_IMPLEMENT(__type) __declspec(export) __type
-#define PR_EXTERN_DATA(__type) extern __declspec(export) __type
-#define PR_IMPLEMENT_DATA(__type) __declspec(export) __type
-
-#define PR_CALLBACK
-#define PR_CALLBACK_DECL
-#define PR_STATIC_CALLBACK(__x) static __x
-
-#elif defined(XP_OS2_VACPP) 
-
-#define PR_EXPORT(__type) extern __type
-#define PR_EXPORT_DATA(__type) extern __type
-#define PR_IMPORT(__type) extern __type
-#define PR_IMPORT_DATA(__type) extern __type
-
-#define PR_EXTERN(__type) extern __type
-#define PR_IMPLEMENT(__type) __type
-#define PR_EXTERN_DATA(__type) extern __type
-#define PR_IMPLEMENT_DATA(__type) __type
-#define PR_CALLBACK _Optlink
-#define PR_CALLBACK_DECL
-#define PR_STATIC_CALLBACK(__x) static __x PR_CALLBACK
-
-#else /* Unix */
-
-#ifdef HAVE_VISIBILITY_PRAGMA
-#define PR_VISIBILITY_DEFAULT __attribute__((visibility("default")))
-#else
-#define PR_VISIBILITY_DEFAULT
-#endif
-
-#define PR_EXPORT(__type) extern PR_VISIBILITY_DEFAULT __type
-#define PR_EXPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
-#define PR_IMPORT(__type) extern PR_VISIBILITY_DEFAULT __type
-#define PR_IMPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
-
-#define PR_EXTERN(__type) extern PR_VISIBILITY_DEFAULT __type
-#define PR_IMPLEMENT(__type) PR_VISIBILITY_DEFAULT __type
-#define PR_EXTERN_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
-#define PR_IMPLEMENT_DATA(__type) PR_VISIBILITY_DEFAULT __type
-#define PR_CALLBACK
-#define PR_CALLBACK_DECL
-#define PR_STATIC_CALLBACK(__x) static __x
-
-#endif
-
-#if defined(_NSPR_BUILD_)
-#define NSPR_API(__type) PR_EXPORT(__type)
-#define NSPR_DATA_API(__type) PR_EXPORT_DATA(__type)
-#else
-#define NSPR_API(__type) PR_IMPORT(__type)
-#define NSPR_DATA_API(__type) PR_IMPORT_DATA(__type)
-#endif
-
-/***********************************************************************
-** MACROS:      PR_BEGIN_MACRO
-**              PR_END_MACRO
-** DESCRIPTION:
-**      Macro body brackets so that macros with compound statement definitions
-**      behave syntactically more like functions when called.
-***********************************************************************/
-#define PR_BEGIN_MACRO  do {
-#define PR_END_MACRO    } while (0)
-
-/***********************************************************************
-** MACROS:      PR_BEGIN_EXTERN_C
-**              PR_END_EXTERN_C
-** DESCRIPTION:
-**      Macro shorthands for conditional C++ extern block delimiters.
-***********************************************************************/
-#ifdef __cplusplus
-#define PR_BEGIN_EXTERN_C       extern "C" {
-#define PR_END_EXTERN_C         }
-#else
-#define PR_BEGIN_EXTERN_C
-#define PR_END_EXTERN_C
-#endif
-
-/***********************************************************************
-** MACROS:      PR_BIT
-**              PR_BITMASK
-** DESCRIPTION:
-** Bit masking macros.  XXX n must be <= 31 to be portable
-***********************************************************************/
-#define PR_BIT(n)       ((PRUint32)1 << (n))
-#define PR_BITMASK(n)   (PR_BIT(n) - 1)
-
-/***********************************************************************
-** MACROS:      PR_ROUNDUP
-**              PR_MIN
-**              PR_MAX
-**              PR_ABS
-** DESCRIPTION:
-**      Commonly used macros for operations on compatible types.
-***********************************************************************/
-#define PR_ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y))
-#define PR_MIN(x,y)     ((x)<(y)?(x):(y))
-#define PR_MAX(x,y)     ((x)>(y)?(x):(y))
-#define PR_ABS(x)       ((x)<0?-(x):(x))
-
-PR_BEGIN_EXTERN_C
-
-/************************************************************************
-** TYPES:       PRUint8
-**              PRInt8
-** DESCRIPTION:
-**  The int8 types are known to be 8 bits each. There is no type that
-**      is equivalent to a plain "char". 
-************************************************************************/
-#if PR_BYTES_PER_BYTE == 1
-typedef unsigned char PRUint8;
-/*
-** Some cfront-based C++ compilers do not like 'signed char' and
-** issue the warning message:
-**     warning: "signed" not implemented (ignored)
-** For these compilers, we have to define PRInt8 as plain 'char'.
-** Make sure that plain 'char' is indeed signed under these compilers.
-*/
-#if (defined(HPUX) && defined(__cplusplus) \
-        && !defined(__GNUC__) && __cplusplus < 199707L) \
-    || (defined(SCO) && defined(__cplusplus) \
-        && !defined(__GNUC__) && __cplusplus == 1L)
-typedef char PRInt8;
-#else
-typedef signed char PRInt8;
-#endif
-#else
-#error No suitable type for PRInt8/PRUint8
-#endif
-
-/************************************************************************
- * MACROS:      PR_INT8_MAX
- *              PR_INT8_MIN
- *              PR_UINT8_MAX
- * DESCRIPTION:
- *  The maximum and minimum values of a PRInt8 or PRUint8.
-************************************************************************/
-
-#define PR_INT8_MAX 127
-#define PR_INT8_MIN (-128)
-#define PR_UINT8_MAX 255U
-
-/************************************************************************
-** TYPES:       PRUint16
-**              PRInt16
-** DESCRIPTION:
-**  The int16 types are known to be 16 bits each. 
-************************************************************************/
-#if PR_BYTES_PER_SHORT == 2
-typedef unsigned short PRUint16;
-typedef short PRInt16;
-#else
-#error No suitable type for PRInt16/PRUint16
-#endif
-
-/************************************************************************
- * MACROS:      PR_INT16_MAX
- *              PR_INT16_MIN
- *              PR_UINT16_MAX
- * DESCRIPTION:
- *  The maximum and minimum values of a PRInt16 or PRUint16.
-************************************************************************/
-
-#define PR_INT16_MAX 32767
-#define PR_INT16_MIN (-32768)
-#define PR_UINT16_MAX 65535U
-
-/************************************************************************
-** TYPES:       PRUint32
-**              PRInt32
-** DESCRIPTION:
-**  The int32 types are known to be 32 bits each. 
-************************************************************************/
-#if PR_BYTES_PER_INT == 4
-typedef unsigned int PRUint32;
-typedef int PRInt32;
-#define PR_INT32(x)  x
-#define PR_UINT32(x) x ## U
-#elif PR_BYTES_PER_LONG == 4
-typedef unsigned long PRUint32;
-typedef long PRInt32;
-#define PR_INT32(x)  x ## L
-#define PR_UINT32(x) x ## UL
-#else
-#error No suitable type for PRInt32/PRUint32
-#endif
-
-/************************************************************************
- * MACROS:      PR_INT32_MAX
- *              PR_INT32_MIN
- *              PR_UINT32_MAX
- * DESCRIPTION:
- *  The maximum and minimum values of a PRInt32 or PRUint32.
-************************************************************************/
-
-#define PR_INT32_MAX PR_INT32(2147483647)
-#define PR_INT32_MIN (-PR_INT32_MAX - 1)
-#define PR_UINT32_MAX PR_UINT32(4294967295)
-
-/************************************************************************
-** TYPES:       PRUint64
-**              PRInt64
-** DESCRIPTION:
-**  The int64 types are known to be 64 bits each. Care must be used when
-**      declaring variables of type PRUint64 or PRInt64. Different hardware
-**      architectures and even different compilers have varying support for
-**      64 bit values. The only guaranteed portability requires the use of
-**      the LL_ macros (see prlong.h).
-************************************************************************/
-#ifdef HAVE_LONG_LONG
-#if PR_BYTES_PER_LONG == 8
-typedef long PRInt64;
-typedef unsigned long PRUint64;
-#elif defined(WIN16)
-typedef __int64 PRInt64;
-typedef unsigned __int64 PRUint64;
-#elif defined(WIN32) && !defined(__GNUC__)
-typedef __int64  PRInt64;
-typedef unsigned __int64 PRUint64;
-#else
-typedef long long PRInt64;
-typedef unsigned long long PRUint64;
-#endif /* PR_BYTES_PER_LONG == 8 */
-#else  /* !HAVE_LONG_LONG */
-typedef struct {
-#ifdef IS_LITTLE_ENDIAN
-    PRUint32 lo, hi;
-#else
-    PRUint32 hi, lo;
-#endif
-} PRInt64;
-typedef PRInt64 PRUint64;
-#endif /* !HAVE_LONG_LONG */
-
-/************************************************************************
-** TYPES:       PRUintn
-**              PRIntn
-** DESCRIPTION:
-**  The PRIntn types are most appropriate for automatic variables. They are
-**      guaranteed to be at least 16 bits, though various architectures may
-**      define them to be wider (e.g., 32 or even 64 bits). These types are
-**      never valid for fields of a structure. 
-************************************************************************/
-#if PR_BYTES_PER_INT >= 2
-typedef int PRIntn;
-typedef unsigned int PRUintn;
-#else
-#error 'sizeof(int)' not sufficient for platform use
-#endif
-
-/************************************************************************
-** TYPES:       PRFloat64
-** DESCRIPTION:
-**  NSPR's floating point type is always 64 bits. 
-************************************************************************/
-typedef double          PRFloat64;
-
-/************************************************************************
-** TYPES:       PRSize
-** DESCRIPTION:
-**  A type for representing the size of objects. 
-************************************************************************/
-typedef size_t PRSize;
-
-
-/************************************************************************
-** TYPES:       PROffset32, PROffset64
-** DESCRIPTION:
-**  A type for representing byte offsets from some location. 
-************************************************************************/
-typedef PRInt32 PROffset32;
-typedef PRInt64 PROffset64;
-
-/************************************************************************
-** TYPES:       PRPtrDiff
-** DESCRIPTION:
-**  A type for pointer difference. Variables of this type are suitable
-**      for storing a pointer or pointer subtraction. 
-************************************************************************/
-typedef ptrdiff_t PRPtrdiff;
-
-/************************************************************************
-** TYPES:       PRUptrdiff
-** DESCRIPTION:
-**  A type for pointer difference. Variables of this type are suitable
-**      for storing a pointer or pointer sutraction. 
-************************************************************************/
-typedef unsigned long PRUptrdiff;
-
-/************************************************************************
-** TYPES:       PRBool
-** DESCRIPTION:
-**  Use PRBool for variables and parameter types. Use PR_FALSE and PR_TRUE
-**      for clarity of target type in assignments and actual arguments. Use
-**      'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans
-**      just as you would C int-valued conditions. 
-************************************************************************/
-typedef PRIntn PRBool;
-#define PR_TRUE 1
-#define PR_FALSE 0
-
-/************************************************************************
-** TYPES:       PRPackedBool
-** DESCRIPTION:
-**  Use PRPackedBool within structs where bitfields are not desirable
-**      but minimum and consistant overhead matters.
-************************************************************************/
-typedef PRUint8 PRPackedBool;
-
-/*
-** Status code used by some routines that have a single point of failure or 
-** special status return.
-*/
-typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus;
-
-#ifdef MOZ_UNICODE
-/*
- * EXPERIMENTAL: This type may be removed in a future release.
- */
-#ifndef __PRUNICHAR__
-#define __PRUNICHAR__
-#if defined(WIN32) || defined(XP_MAC)
-typedef wchar_t PRUnichar;
-#else
-typedef PRUint16 PRUnichar;
-#endif
-#endif
-#endif /* MOZ_UNICODE */
-
-/*
-** WARNING: The undocumented data types PRWord and PRUword are
-** only used in the garbage collection and arena code.  Do not
-** use PRWord and PRUword in new code.
-**
-** A PRWord is an integer that is the same size as a void*.
-** It implements the notion of a "word" in the Java Virtual
-** Machine.  (See Sec. 3.4 "Words", The Java Virtual Machine
-** Specification, Addison-Wesley, September 1996.
-** http://java.sun.com/docs/books/vmspec/index.html.)
-*/
-typedef long PRWord;
-typedef unsigned long PRUword;
-
-#if defined(NO_NSPR_10_SUPPORT)
-#else
-/********* ???????????????? FIX ME       ??????????????????????????? *****/
-/********************** Some old definitions until pr=>ds transition is done ***/
-/********************** Also, we are still using NSPR 1.0. GC ******************/
-/*
-** Fundamental NSPR macros, used nearly everywhere.
-*/
-
-#define PR_PUBLIC_API		PR_IMPLEMENT
-
-/*
-** Macro body brackets so that macros with compound statement definitions
-** behave syntactically more like functions when called.
-*/
-#define NSPR_BEGIN_MACRO        do {
-#define NSPR_END_MACRO          } while (0)
-
-/*
-** Macro shorthands for conditional C++ extern block delimiters.
-*/
-#ifdef NSPR_BEGIN_EXTERN_C
-#undef NSPR_BEGIN_EXTERN_C
-#endif
-#ifdef NSPR_END_EXTERN_C
-#undef NSPR_END_EXTERN_C
-#endif
-
-#ifdef __cplusplus
-#define NSPR_BEGIN_EXTERN_C     extern "C" {
-#define NSPR_END_EXTERN_C       }
-#else
-#define NSPR_BEGIN_EXTERN_C
-#define NSPR_END_EXTERN_C
-#endif
-
-#ifdef XP_MAC
-#include "protypes.h"
-#else
-#include "obsolete/protypes.h"
-#endif
-
-/********* ????????????? End Fix me ?????????????????????????????? *****/
-#endif /* NO_NSPR_10_SUPPORT */
-
-PR_END_EXTERN_C
-
-#endif /* prtypes_h___ */
-
diff --git a/third_party/gecko/nsICookieService.idl b/third_party/gecko/nsICookieService.idl
deleted file mode 100644
index d0bddd4..0000000
--- a/third_party/gecko/nsICookieService.idl
+++ /dev/null
@@ -1,169 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsISupports.idl"
-
-interface nsIURI;
-interface nsIPrompt;
-interface nsIChannel;
-
-/**
- * nsICookieService
- *
- * Provides methods for setting and getting cookies in the context of a
- * page load.  See nsICookieManager for methods to manipulate the cookie
- * database directly.  This separation of interface is mainly historical.
- *
- * This service broadcasts the following notifications when the cookie
- * list is changed, or a cookie is rejected:
- *
- * topic  : "cookie-changed"
- *          broadcast whenever the cookie list changes in some way. there
- *          are four possible data strings for this notification; one
- *          notification will be broadcast for each change, and will involve
- *          a single cookie.
- * subject: an nsICookie2 interface pointer representing the cookie object
- *          that changed.
- * data   : "deleted"
- *          a cookie was deleted. the subject is the deleted cookie.
- *          "added"
- *          a cookie was added. the subject is the added cookie.
- *          "changed"
- *          a cookie was changed. the subject is the new cookie.
- *          "cleared"
- *          the entire cookie list was cleared. the subject is null.
- *
- * topic  : "cookie-rejected"
- *          broadcast whenever a cookie was rejected from being set as a
- *          result of user prefs.
- * subject: an nsIURI interface pointer representing the URI that attempted
- *          to set the cookie.
- * data   : none.
- */
-[scriptable, uuid(011C3190-1434-11d6-A618-0010A401EB10)]
-interface nsICookieService : nsISupports
-{
-  /*
-   * Get the complete cookie string associated with the URI.
-   *
-   * @param aURI
-   *        the URI of the document for which cookies are being queried.
-   * @param aChannel
-   *        the channel used to load the document.  this parameter may be null,
-   *        but it is strongly recommended that a non-null value be provided to
-   *        ensure that the cookie privacy preferences are honored.
-   *
-   * @return the resulting cookie string
-   */
-  string getCookieString(in nsIURI aURI, in nsIChannel aChannel);
-
-  /*
-   * Get the complete cookie string associated with the URI.
-   *
-   * XXX this function is redundant and will most likely be removed in a future
-   * revision of this interface.  GetCookieString will query the documentURI
-   * property off of nsIHttpChannelInternal if supported, so GetCookieString
-   * can be used in place of this method.
-   *
-   * @param aURI
-   *        the URI of the document for which cookies are being queried.
-   * @param aFirstURI
-   *        the URI that the user originally typed in or clicked on to initiate
-   *        the load of the document referenced by aURI.
-   * @param aChannel
-   *        the channel used to load the document.  this parameter may be null,
-   *        but it is strongly recommended that a non-null value be provided to
-   *        ensure that the cookie privacy preferences are honored.
-   *
-   * @return the resulting cookie string
-   */
-  string getCookieStringFromHttp(in nsIURI aURI, in nsIURI aFirstURI, in nsIChannel aChannel);
-
-  /*
-   * Set the cookie string associated with the URI.
-   *
-   * @param aURI
-   *        the URI of the document for which cookies are being set.
-   * @param aPrompt
-   *        the prompt to use for all user-level cookie notifications.
-   * @param aCookie
-   *        the cookie string to set.
-   * @param aChannel
-   *        the channel used to load the document.  this parameter may be null,
-   *        but it is strongly recommended that a non-null value be provided to
-   *        ensure that the cookie privacy preferences are honored.
-   *
-   * XXX should be able to allow null aPrompt, since nsIPrompt can be queryied
-   * from aChannel.
-   */
-  void setCookieString(in nsIURI aURI, in nsIPrompt aPrompt, in string aCookie, in nsIChannel aChannel);
-
-  /*
-   * Set the cookie string and expires associated with the URI.
-   *
-   * XXX this function is redundant and will most likely be removed in a future
-   * revision of this interface.  SetCookieString will query the documentURI
-   * property off of nsIHttpChannelInternal if supported, and SetCookieString
-   * could also query the Date header from the channel if aChannel supports
-   * nsIHttpChannel.
-   *
-   * @param aURI
-   *        the URI of the document for which cookies are being set.
-   * @param aFirstURI
-   *        the URI that the user originally typed in or clicked on to initiate
-   *        the load of the document referenced by aURI.
-   * @param aPrompt
-   *        the prompt to use for all user-level cookie notifications.
-   * @param aCookie
-   *        the cookie string to set.
-   * @param aServerTime
-   *        the expiry information of the cookie (the Date header from the HTTP
-   *        response).
-   * @param aChannel
-   *        the channel used to load the document.  this parameter may be null,
-   *        but it is strongly recommended that a non-null value be provided to
-   *        ensure that the cookie privacy preferences are honored.
-   */
-  void setCookieStringFromHttp(in nsIURI aURI, in nsIURI aFirstURI, in nsIPrompt aPrompt, in string aCookie, in string aServerTime, in nsIChannel aChannel);
-
-  /**
-   * This attribute really doesn't belong on this interface.  CVS blame will
-   * tell you why it is here.  It remains until we can find a better home for
-   * it.  Read the source if you want to know what it does :-(
-   */
-  readonly attribute boolean cookieIconIsVisible;
-};
diff --git a/third_party/gecko/nsIDOM3Document.idl b/third_party/gecko/nsIDOM3Document.idl
deleted file mode 100644
index 00a5d2e..0000000
--- a/third_party/gecko/nsIDOM3Document.idl
+++ /dev/null
@@ -1,76 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozila.org Code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2003
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Christopher A. Aillon <christopher@aillon.com> (Original Author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
- 
-#include "domstubs.idl"
-#include "nsIDOM3Node.idl"
-
-interface nsIDOMDOMConfiguration;
-
-/**
- * For more information on this interface, please see
- * http://www.w3.org/TR/DOM-Level-3-Core/
- */
-[scriptable, uuid(2e0e9ea1-72ab-4d9e-bdeb-ca64e1abeba4)]
-interface nsIDOM3Document : nsIDOM3Node
-{
-  // Introduced in DOM Level 3:
-  readonly attribute DOMString       actualEncoding;
-  // Introduced in DOM Level 3:
-  readonly attribute DOMString       xmlEncoding;
-  // Introduced in DOM Level 3:
-           attribute boolean         xmlStandalone;
-                                        // raises(DOMException) on setting
-  // Introduced in DOM Level 3:
-           attribute DOMString       xmlVersion;
-                                        // raises(DOMException) on setting
-  // Introduced in DOM Level 3:
-           attribute boolean         strictErrorChecking;
-  // Introduced in DOM Level 3:
-           attribute DOMString       documentURI;
-  // Introduced in DOM Level 3:
-  nsIDOMNode         adoptNode(in nsIDOMNode source)
-                                        raises(DOMException);
-  // Introduced in DOM Level 3:
-  readonly attribute nsIDOMDOMConfiguration domConfig;
-  // Introduced in DOM Level 3:
-  void               normalizeDocument();
-  // Introduced in DOM Level 3:
-  nsIDOMNode         renameNode(in nsIDOMNode node, 
-                                in DOMString namespaceURI, 
-                                in DOMString qualifiedName)
-                                        raises(DOMException);
-};
diff --git a/third_party/gecko/nsIDOM3Node.idl b/third_party/gecko/nsIDOM3Node.idl
deleted file mode 100644
index 9071ba9..0000000
--- a/third_party/gecko/nsIDOM3Node.idl
+++ /dev/null
@@ -1,88 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: NPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is 
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Johnny Stenback <jst@netscape.com> (original author)
- *   Christopher A. Aillon <christopher@aillon.com>
- *
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the NPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the NPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "domstubs.idl"
-
-interface nsIVariant;
-interface nsIDOMUserDataHandler;
-
-
-[scriptable, uuid(29fb2a18-1dd2-11b2-8dd9-a6fd5d5ad12f)]
-interface nsIDOM3Node : nsISupports
-{
-  // Introduced in DOM Level 3:
-  readonly attribute DOMString       baseURI;
-
-  // DocumentPosition
-  const unsigned short      DOCUMENT_POSITION_DISCONNECTED = 0x01;
-  const unsigned short      DOCUMENT_POSITION_PRECEDING    = 0x02;
-  const unsigned short      DOCUMENT_POSITION_FOLLOWING    = 0x04;
-  const unsigned short      DOCUMENT_POSITION_CONTAINS     = 0x08;
-  const unsigned short      DOCUMENT_POSITION_CONTAINED_BY = 0x10;
-  const unsigned short      DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;
-
-  // Introduced in DOM Level 3:
-  unsigned short     compareDocumentPosition(in nsIDOMNode other)
-                                        raises(DOMException);
-  // Introduced in DOM Level 3:
-           attribute DOMString       textContent;
-                                        // raises(DOMException) on setting
-                                        // raises(DOMException) on retrieval
-
-  // Introduced in DOM Level 3:
-  boolean            isSameNode(in nsIDOMNode other);
-  // Introduced in DOM Level 3:
-  DOMString          lookupPrefix(in DOMString namespaceURI);
-  // Introduced in DOM Level 3:
-  boolean            isDefaultNamespace(in DOMString namespaceURI);
-  // Introduced in DOM Level 3:
-  DOMString          lookupNamespaceURI(in DOMString prefix);
-  // Introduced in DOM Level 3:
-  boolean            isEqualNode(in nsIDOMNode arg);
-  // Introduced in DOM Level 3:
-  nsISupports        getFeature(in DOMString feature, 
-                                in DOMString version);
-  // Introduced in DOM Level 3:
-  nsIVariant         setUserData(in DOMString key, 
-                                 in nsIVariant data, 
-                                 in nsIDOMUserDataHandler handler);
-  // Introduced in DOM Level 3:
-  nsIVariant         getUserData(in DOMString key);
-};
diff --git a/third_party/gecko/nsIDOMElementCSSInlineStyle.idl b/third_party/gecko/nsIDOMElementCSSInlineStyle.idl
deleted file mode 100644
index ccfe935..0000000
--- a/third_party/gecko/nsIDOMElementCSSInlineStyle.idl
+++ /dev/null
@@ -1,46 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: NPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is 
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Johnny Stenback <jst@netscape.com> (original author)
- *
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the NPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the NPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "domstubs.idl"
-
-[scriptable, uuid(99715845-95fc-4a56-aa53-214b65c26e22)]
-interface nsIDOMElementCSSInlineStyle : nsISupports
-{
-  readonly attribute nsIDOMCSSStyleDeclaration  style;
-};
diff --git a/third_party/gecko/nsIDOMNSEventTarget.idl b/third_party/gecko/nsIDOMNSEventTarget.idl
deleted file mode 100644
index df5d66b..0000000
--- a/third_party/gecko/nsIDOMNSEventTarget.idl
+++ /dev/null
@@ -1,71 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * The Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2005
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Johnny Stenback <jst@mozilla.org> (original author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "domstubs.idl"
-
-/**
- * The nsIDOMNSEventTarget interface is an extension to the standard
- * nsIDOMEventTarget interface, implemented by all event targets in
- * the Document Object Model.
- */
-
-[scriptable, uuid(6cbbbf64-212f-4ef8-9ad4-7240dbb8d6ac)]
-interface nsIDOMNSEventTarget : nsISupports
-{
-  /**
-   * This method is the same as the addEventListener() method defined
-   * in nsIDOMEventTarget, but it takes one additional argument which
-   * lets callers control whether or not they want to receive
-   * untrusted events (synthetic events generated by untrusted code)
-   *
-   * @param   type See the type argument to the same method in
-   *               nsIDOMEventTarget.
-   * @param   listener See the listener argument to the same method in
-   *                   nsIDOMEventTarget.
-   * @param   useCapture See the listener argument to the same method in
-   *                     nsIDOMEventTarget.
-   * @param   wantsUntrusted If false, the listener will not receive any
-   *                         untrusted events (see above), if true, the
-   *                         listener will receive events whether or not
-   *                         they're trusted
-   */
-  void                     addEventListener(in DOMString type,
-                                            in nsIDOMEventListener listener,
-                                            in boolean useCapture,
-                                            in boolean wantsUntrusted);
-};
diff --git a/third_party/gecko/nsIDocumentLoader.idl b/third_party/gecko/nsIDocumentLoader.idl
deleted file mode 100644
index 39c3c4b..0000000
--- a/third_party/gecko/nsIDocumentLoader.idl
+++ /dev/null
@@ -1,76 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: NPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is 
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1999
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or 
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the NPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the NPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/* nsIDocumentLoader -->
-
-*/
-
-#include "nsISupports.idl"
-interface nsILoadGroup;
-interface nsIContentViewerContainer;
-interface nsIChannel;
-interface nsIURI;
-interface nsIWebProgress;
-interface nsIRequest;
-
-[scriptable, uuid(f43ba260-0737-11d2-beb9-00805f8a66dc)]
-interface nsIDocumentLoader : nsISupports
-{
-  void stop();
-  boolean isBusy();
-  void createDocumentLoader(out nsIDocumentLoader anInstance);
-
-  attribute nsISupports container;
-  
-  [noscript] void getContentViewerContainer(in nsISupports aDocumentID, out nsIContentViewerContainer aResult);
-  nsILoadGroup getLoadGroup();
-
-  void destroy();
-  // this should really be in a private interface as it is only
-  // called between a parent doc loader and it's child.
-  void clearParentDocLoader();
-  readonly attribute nsIChannel documentChannel;
-
-  void fireOnLocationChange(in nsIWebProgress aWebProgress,
-                            in nsIRequest aRequest,
-                            in nsIURI aUri);
-  void fireOnStatusChange(in nsIWebProgress aWebProgress,
-                          in nsIRequest aRequest,
-                          in nsresult aStatus,
-                          in wstring aMessage);
-};
-
diff --git a/third_party/gecko/nsIHttpProtocolHandler.idl b/third_party/gecko/nsIHttpProtocolHandler.idl
deleted file mode 100644
index 8b7f70c..0000000
--- a/third_party/gecko/nsIHttpProtocolHandler.idl
+++ /dev/null
@@ -1,143 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.mozilla.org/MPL/
- * 
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- * 
- * The Original Code is Mozilla.
- * 
- * The Initial Developer of the Original Code is Netscape
- * Communications.  Portions created by Netscape Communications are
- * Copyright (C) 2001 by Netscape Communications.  All
- * Rights Reserved.
- * 
- * Contributor(s): 
- *   Gagan Saksena <gagan@netscape.com> (original author)
- *   Darin Fisher <darin@netscape.com>
- */
-
-#include "nsIProxiedProtocolHandler.idl"
-
-[scriptable, uuid(122c91c0-2485-40ba-89c9-b895934921bc)]
-interface nsIHttpProtocolHandler : nsIProxiedProtocolHandler
-{
-    /**
-     * Get the HTTP advertised user agent string.
-     */
-    readonly attribute ACString userAgent;
-
-    /**
-     * Get the application name.
-	 *
-     * @return The name of this application (eg. "Mozilla").
-     */
-    readonly attribute ACString appName;
-
-    /**
-     * Get the application version string.
-     *
-     * @return The complete version (major and minor) string. (eg. "5.0")
-     */
-    readonly attribute ACString appVersion;
-
-    /**
-     * @return The vendor name.
-     */
-    attribute ACString vendor;
-
-    /**
-     * @return The vendor sub string.
-     */
-    attribute ACString vendorSub;
-    
-    /**
-     * @return The vendor comment.
-     */
-    attribute ACString vendorComment;
-
-    /**
-     * @return The product name.
-     */
-    attribute ACString product;
-
-    /**
-     * @return A product sub string.
-     */
-    attribute ACString productSub;
-
-    /**
-     * @return A product comment.
-     */
-    attribute ACString productComment;
-
-    /**
-     * Get the current platform.
-     *
-     * @return The platform this application is running on
-     *		   (eg. "Windows", "Macintosh", "X11")
-     */
-    readonly attribute ACString platform;
-
-    /**
-     * Get the current oscpu.
-     *
-     * @return The oscpu this application is running on
-     */
-    readonly attribute ACString oscpu;
-
-    /**
-     * Get the translation of the application. The value for language
-     * is usually a 2-letter code such as "en" and occasionally a 
-     * five-character code to indicate a language subtype, such as "zh_CN". 
-     */
-    attribute ACString language;
-
-    /**
-     * Get/Set the application comment misc portion.
-     */
-    attribute ACString misc;
-};
-
-%{C++
-/**
- * At initialization time, the HTTP handler will initialize each service
- * registered under this category:
- */
-#define NS_HTTP_STARTUP_CATEGORY "http-startup-category"
-
-/**
- * nsIObserver notification corresponding to startup category.  Services
- * registered under the startup category will receive this observer topic at
- * startup if they implement nsIObserver.  The "subject" of the notification
- * is the nsIHttpProtocolHandler instance.
- */
-#define NS_HTTP_STARTUP_TOPIC "http-startup"
-
-/**
- * Before an HTTP request is sent to the server, this observer topic is
- * notified.  The observer of this topic can then choose to set any additional
- * headers for this request before the request is actually sent to the server.
- * The "subject" of the notification is the nsIHttpChannel instance.
- */
-#define NS_HTTP_ON_MODIFY_REQUEST_TOPIC "http-on-modify-request"
-
-/**
- * After an HTTP server response is received, this observer topic is notified.
- * The observer of this topic can interrogate the response.  The "subject" of
- * the notification is the nsIHttpChannel instance.
- */
-#define NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC "http-on-examine-response"
-
-/**
- * The observer of this topic is notified after the received HTTP response
- * is merged with data from the browser cache.  This means that, among other
- * things, the Content-Type header will be set correctly.
- */
-#define NS_HTTP_ON_EXAMINE_MERGED_RESPONSE_TOPIC "http-on-examine-merged-response"
-%}
diff --git a/third_party/gecko/nsIPrefBranch2.idl b/third_party/gecko/nsIPrefBranch2.idl
deleted file mode 100644
index 01968b8..0000000
--- a/third_party/gecko/nsIPrefBranch2.idl
+++ /dev/null
@@ -1,114 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Communicator client code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Alec Flett <alecf@netscape.com>
- *   Brian Nesse <bnesse@netscape.com>
- *   Benjamin Smedberg <benjamin@smedbergs.us>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsIPrefBranch.idl"
-
-interface nsIObserver;
-
-/**
- * nsIPrefBranch2 allows clients to observe changes to pref values.
- *
- * @status FROZEN
- * @see nsIPrefBranch
- */
-[scriptable, uuid(74567534-eb94-4b1c-8f45-389643bfc555)]
-interface nsIPrefBranch2 : nsIPrefBranch
-{
-  /**
-   * Add a preference change observer. On preference changes, the following
-   * arguments will be passed to the nsIObserver.observe() method:
-   *   aSubject - The nsIPrefBranch object (this)
-   *   aTopic   - The string defined by NS_PREFBRANCH_PREFCHANGE_TOPIC_ID
-   *   aData    - The preference which has changed
-   *
-   * @param aDomain   The preference on which to listen for changes. This can
-   *                  be the name of an entire branch to observe.
-   *                  e.g. Holding the "root" prefbranch and calling
-   *                  addObserver("foo.bar.", ...) will observe changes to
-   *                  foo.bar.baz and foo.bar.bzip
-   * @param aObserver The object to be notified if the preference changes.
-   * @param aHoldWeak true  Hold a weak reference to |aObserver|. The object
-   *                        must implement the nsISupportsWeakReference
-   *                        interface or this will fail.
-   *                  false Hold a strong reference to |aObserver|.
-   *
-   * @note
-   * Registering as a preference observer can open an object to potential
-   * cyclical references which will cause memory leaks. These cycles generally
-   * occur because an object both registers itself as an observer (causing the
-   * branch to hold a reference to the observer) and holds a reference to the
-   * branch object for the purpose of getting/setting preference values. There
-   * are 3 approaches which have been implemented in an attempt to avoid these
-   * situations.
-   * 1) The nsPrefBranch object supports nsISupportsWeakReference. Any consumer
-   *    may hold a weak reference to it instead of a strong one.
-   * 2) The nsPrefBranch object listens for xpcom-shutdown and frees all of the
-   *    objects currently in its observer list. This insures that long lived
-   *    objects (services for example) will be freed correctly.
-   * 3) The observer can request to be held as a weak reference when it is
-   *    registered. This insures that shorter lived objects (say one tied to an
-   *    open window) will not fall into the cyclical reference trap.
-   *
-   * @see nsIObserver
-   * @see removeObserver
-   */
-  void addObserver(in string aDomain, in nsIObserver aObserver,
-                   in boolean aHoldWeak);
-
-  /**
-   * Remove a preference change observer.
-   *
-   * @param aDomain   The preference which is being observed for changes.
-   * @param aObserver An observer previously registered with addObserver().
-   *
-   * @see nsIObserver
-   * @see addObserver
-   */
-  void removeObserver(in string aDomain, in nsIObserver aObserver);
-};
-
-%{C++
-
-/**
- * Notification sent when a preference changes.
- */
-#define NS_PREFBRANCH_PREFCHANGE_TOPIC_ID "nsPref:changed"
-
-%}
diff --git a/third_party/gecko/nsIPropertyBag.idl b/third_party/gecko/nsIPropertyBag.idl
deleted file mode 100644
index 79c3f2f..0000000
--- a/third_party/gecko/nsIPropertyBag.idl
+++ /dev/null
@@ -1,63 +0,0 @@
-/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   John Bandhauer <jband@netscape.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/* nsIVariant based Property Bag support. */
-
-#include "nsISupports.idl"
-
-interface nsIVariant;
-interface nsISimpleEnumerator;
-
-[scriptable, uuid(bfcd37b0-a49f-11d5-910d-0010a4e73d9a)]
-interface nsIPropertyBag : nsISupports
-{
-    /**
-     * Get a nsISimpleEnumerator whose elements are nsIProperty objects.
-     */
-    readonly attribute nsISimpleEnumerator enumerator;
-
-    /**
-     * Get a property value for the given name.
-     * @throws NS_ERROR_FAILURE if a property with that name doesn't
-     * exist.
-     */
-    nsIVariant getProperty(in AString name);
-};
-
-
diff --git a/third_party/gecko/nsIPropertyBag2.idl b/third_party/gecko/nsIPropertyBag2.idl
deleted file mode 100644
index 19d0070..0000000
--- a/third_party/gecko/nsIPropertyBag2.idl
+++ /dev/null
@@ -1,64 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org property bag support.
- *
- * The Initial Developer of the Original Code is
- * Christian Biesinger <cbiesinger@web.de>.
- * Portions created by the Initial Developer are Copyright (C) 2005
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *    Darin Fisher <darin@meer.net>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/* nsIVariant based Property Bag support. */
-
-#include "nsIPropertyBag.idl"
-
-[scriptable, uuid(9bb35f13-0096-4a31-833a-acd97001132d)]
-interface nsIPropertyBag2 : nsIPropertyBag
-{
-  // Accessing a property as a different type may attempt conversion to the
-  // requested value
-
-  PRInt32     getPropertyAsInt32       (in AString prop);
-  PRUint32    getPropertyAsUint32      (in AString prop);
-  PRInt64     getPropertyAsInt64       (in AString prop);
-  PRUint64    getPropertyAsUint64      (in AString prop);
-  double      getPropertyAsDouble      (in AString prop);
-  AString     getPropertyAsAString     (in AString prop);
-  ACString    getPropertyAsACString    (in AString prop);
-  AUTF8String getPropertyAsAUTF8String (in AString prop);
-  boolean     getPropertyAsBool        (in AString prop);
-
-  /**
-   * This method returns null if the value exists, but is null.
-   */
-  void        getPropertyAsInterface   (in AString prop,
-                                        in nsIIDRef iid,
-                                        [iid_is(iid), retval] out nsQIResult result);
-};
diff --git a/third_party/gecko/nsIProtocolProxyFilter.idl b/third_party/gecko/nsIProtocolProxyFilter.idl
deleted file mode 100644
index a00dbfb..0000000
--- a/third_party/gecko/nsIProtocolProxyFilter.idl
+++ /dev/null
@@ -1,76 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is Google Inc.
- * Portions created by the Initial Developer are Copyright (C) 2005
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Darin Fisher <darin@meer.net>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsISupports.idl"
-
-interface nsIProtocolProxyService;
-interface nsIProxyInfo;
-interface nsIURI;
-
-/**
- * This interface is used to apply filters to the proxies selected for a given
- * URI.  Use nsIProtocolProxyService::registerFilter to hook up instances of
- * this interface.
- *
- * @status UNDER_REVIEW
- */
-[scriptable, uuid(f424abd3-32b4-456c-9f45-b7e3376cb0d1)]
-interface nsIProtocolProxyFilter : nsISupports
-{
-  /**
-   * This method is called to apply proxy filter rules for the given URI
-   * and proxy object (or list of proxy objects).
-   *
-   * @param aProxyService
-   *        A reference to the Protocol Proxy Service.  This is passed so that
-   *        implementations may easily access methods such as newProxyInfo.
-   * @param aURI
-   *        The URI for which these proxy settings apply.
-   * @param aProxy
-   *        The proxy (or list of proxies) that would be used by default for
-   *        the given URI.  This may be null.
-   *
-   * @return The proxy (or list of proxies) that should be used in place of
-   *         aProxy.  This can be just be aProxy if the filter chooses not to
-   *         modify the proxy.  It can also be null to indicate that a direct
-   *         connection should be used.  Use aProxyService.newProxyInfo to
-   *         construct nsIProxyInfo objects.
-   */
-  nsIProxyInfo applyFilter(in nsIProtocolProxyService aProxyService,
-                           in nsIURI aURI, in nsIProxyInfo aProxy);
-};
diff --git a/third_party/gecko/nsIProtocolProxyService.idl b/third_party/gecko/nsIProtocolProxyService.idl
deleted file mode 100644
index ae4514c..0000000
--- a/third_party/gecko/nsIProtocolProxyService.idl
+++ /dev/null
@@ -1,220 +0,0 @@
-/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* vim:set ts=4 sw=4 sts=4 et: */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Darin Fisher <darin@meer.net>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsISupports.idl"
-
-interface nsICancelable;
-interface nsIProtocolProxyCallback;
-interface nsIProtocolProxyFilter;
-interface nsIProxyInfo;
-interface nsIChannel;
-interface nsIURI;
-
-/**
- * nsIProtocolProxyService provides methods to access information about
- * various network proxies.
- *
- * @status UNDER_REVIEW
- */
-[scriptable, uuid(e38ab577-786e-4a7f-936b-7ae4c7d877b2)]
-interface nsIProtocolProxyService : nsISupports
-{
-    /**
-     * This flag may be passed to the resolve method to request that it fail
-     * instead of block the calling thread.  Proxy Auto Config (PAC) may
-     * perform a synchronous DNS query, which may not return immediately.  So,
-     * calling resolve without this flag may result in locking up the calling
-     * thread for a lengthy period of time.
-     *
-     * By passing this flag to resolve, one can failover to asyncResolve to
-     * avoid locking up the calling thread if a PAC query is required.
-     *
-     * When this flag is passed to resolve, resolve may throw the exception
-     * NS_BASE_STREAM_WOULD_BLOCK to indicate that it failed due to this flag
-     * being present.
-     */
-    const unsigned long RESOLVE_NON_BLOCKING = 1;
-    
-    /**
-     * This method returns a nsIProxyInfo instance that identifies a proxy to
-     * be used for loading the given URI.  Otherwise, this method returns null
-     * indicating that a direct connection should be used.
-     *
-     * @param aURI
-     *        The URI to test.
-     * @param aFlags
-     *        A bit-wise OR of the RESOLVE_ flags defined above.  Pass 0 to
-     *        specify the default behavior.
-     *
-     * NOTE: If this proxy is unavailable, getFailoverForProxy may be called
-     * to determine the correct secondary proxy to be used.
-     *
-     * NOTE: If the protocol handler for the given URI supports
-     * nsIProxiedProtocolHandler, then the nsIProxyInfo instance returned from
-     * resolve may be passed to the newProxiedChannel method to create a
-     * nsIChannel to the given URI that uses the specified proxy.
-     *
-     * NOTE: However, if the nsIProxyInfo type is "http", then it means that
-     * the given URI should be loaded using the HTTP protocol handler, which
-     * also supports nsIProxiedProtocolHandler.
-     *
-     * @see nsIProxiedProtocolHandler::newProxiedChannel 
-     */
-    nsIProxyInfo resolve(in nsIURI aURI, in unsigned long aFlags);
-
-    /**
-     * This method is an asychronous version of the resolve method.  Unlike
-     * resolve, this method is guaranteed not to block the calling thread
-     * waiting for DNS queries to complete.  This method is intended as a
-     * substitute for resolve when the result is not needed immediately.
-     *
-     * @param aURI
-     *        The URI to test.
-     * @param aFlags
-     *        A bit-wise OR of the RESOLVE_ flags defined above.  Pass 0 to
-     *        specify the default behavior.
-     * @param aCallback
-     *        The object to be notified when the result is available.
-     *
-     * @return An object that can be used to cancel the asychronous operation.
-     */
-    nsICancelable asyncResolve(in nsIURI aURI, in unsigned long aFlags,
-                               in nsIProtocolProxyCallback aCallback);
-
-    /**
-     * This method may be called to construct a nsIProxyInfo instance from
-     * the given parameters.  This method may be useful in conjunction with
-     * nsISocketTransportService::createTransport for creating, for example,
-     * a SOCKS connection.
-     *
-     * @param aType
-     *        The proxy type.  This is a string value that identifies the proxy
-     *        type.  Standard values include:
-     *          "http"   - specifies a HTTP proxy
-     *          "socks"  - specifies a SOCKS version 5 proxy
-     *          "socks4" - specifies a SOCKS version 4 proxy
-     *          "direct" - specifies a direct connection (useful for failover)
-     *        The type name is case-insensitive.  Other string values may be
-     *        possible.
-     * @param aHost
-     *        The proxy hostname or IP address.
-     * @param aPort
-     *        The proxy port.
-     * @param aFlags
-     *        Flags associated with this connection.  See nsIProxyInfo.idl
-     *        for currently defined flags.
-     * @param aFailoverTimeout
-     *        Specifies the length of time (in seconds) to ignore this proxy if
-     *        this proxy fails.  Pass PR_UINT32_MAX to specify the default
-     *        timeout value, causing nsIProxyInfo::failoverTimeout to be
-     *        assigned the default value.
-     * @param aFailoverProxy
-     *        Specifies the next proxy to try if this proxy fails.  This
-     *        parameter may be null.
-     */
-    nsIProxyInfo newProxyInfo(in ACString aType, in AUTF8String aHost,
-                              in long aPort, in unsigned long aFlags,
-                              in unsigned long aFailoverTimeout,
-                              in nsIProxyInfo aFailoverProxy);
-
-    /**
-     * If the proxy identified by aProxyInfo is unavailable for some reason,
-     * this method may be called to access an alternate proxy that may be used
-     * instead.  As a side-effect, this method may affect future result values
-     * from resolve/asyncResolve as well as from getFailoverForProxy.
-     *
-     * @param aProxyInfo
-     *        The proxy that was unavailable.
-     * @param aURI
-     *        The URI that was originally passed to resolve/asyncResolve.
-     * @param aReason
-     *        The error code corresponding to the proxy failure.  This value
-     *        may be used to tune the delay before this proxy is used again.
-     *
-     * @throw NS_ERROR_NOT_AVAILABLE if there is no alternate proxy available.
-     */
-    nsIProxyInfo getFailoverForProxy(in nsIProxyInfo aProxyInfo,
-                                     in nsIURI       aURI,
-                                     in nsresult     aReason);
-
-    /**
-     * This method may be used to register a proxy filter instance.  Each proxy
-     * filter is registered with an associated position that determines the
-     * order in which the filters are applied (starting from position 0).  When
-     * resolve/asyncResolve is called, it generates a list of proxies for the
-     * given URI, and then it applies the proxy filters.  The filters have the
-     * opportunity to modify the list of proxies.
-     *
-     * If two filters register for the same position, then the filters will be
-     * visited in the order in which they were registered.
-     *
-     * If the filter is already registered, then its position will be updated.
-     *
-     * After filters have been run, any disabled or disallowed proxies will be
-     * removed from the list.  A proxy is disabled if it had previously failed-
-     * over to another proxy (see getFailoverForProxy).  A proxy is disallowed,
-     * for example, if it is a HTTP proxy and the nsIProtocolHandler for the
-     * queried URI does not permit proxying via HTTP.
-     *
-     * If a nsIProtocolHandler disallows all proxying, then filters will never
-     * have a chance to intercept proxy requests for such URLs.
-     *
-     * @param aFilter
-     *        The nsIProtocolProxyFilter instance to be registered.
-     * @param aPosition
-     *        The position of the filter.
-     *
-     * NOTE: It is possible to construct filters that compete with one another
-     * in undesirable ways.  This API does not attempt to protect against such
-     * problems.  It is recommended that any extensions that choose to call
-     * this method make their position value configurable at runtime (perhaps
-     * via the preferences service).
-     */
-    void registerFilter(in nsIProtocolProxyFilter aFilter,
-                        in unsigned long aPosition);
-
-    /**
-     * This method may be used to unregister a proxy filter instance.  All
-     * filters will be automatically unregistered at XPCOM shutdown.
-     *
-     * @param aFilter
-     *        The nsIProtocolProxyFilter instance to be unregistered.
-     */
-    void unregisterFilter(in nsIProtocolProxyFilter aFilter);
-};
diff --git a/third_party/gecko/nsIProtocolProxyService0.idl b/third_party/gecko/nsIProtocolProxyService0.idl
deleted file mode 100644
index a045d07..0000000
--- a/third_party/gecko/nsIProtocolProxyService0.idl
+++ /dev/null
@@ -1,59 +0,0 @@
-/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: NPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is 
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or 
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the NPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the NPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/**
- * NOTE: This is the version of nsIProtocolProxyService from Mozilla 1.7 --Darin
- */
-
-#include "nsISupports.idl"
-#include "nsIURI.idl"
-
-interface nsIProxyInfo0;
-
-[scriptable, uuid(91c4fe40-76c2-433f-9324-ffdae12df4b4)]
-interface nsIProtocolProxyService0 : nsISupports
-{
-    readonly attribute PRBool proxyEnabled;
-
-    /** Given a uri, return a proxyInfo */
-    nsIProxyInfo0 examineForProxy(in nsIURI aURI);
-
-    /** Return a proxyInfo with the given data */
-    nsIProxyInfo0 newProxyInfo(in string type, in string host, in long port);
-
-    void configureFromPAC(in string url);
-};
diff --git a/third_party/gecko/nsIProxiedProtocolHandler.idl b/third_party/gecko/nsIProxiedProtocolHandler.idl
deleted file mode 100644
index 6d6e37f..0000000
--- a/third_party/gecko/nsIProxiedProtocolHandler.idl
+++ /dev/null
@@ -1,42 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.mozilla.org/MPL/
- * 
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- * 
- * The Original Code is Mozilla.
- * 
- * The Initial Developer of the Original Code is Netscape
- * Communications.  Portions created by Netscape Communications are
- * Copyright (C) 2001 by Netscape Communications.  All
- * Rights Reserved.
- * 
- * Contributor(s): Bradley Baetz <bbaetz@netscape.com> (original author)
- *
- */
-
-/**
- * NOTE: This is the version of nsIProxiedProtocolHandler from Mozilla 1.7 --Darin
- */
-
-#include "nsIProtocolHandler.idl"
-
-interface nsIChannel;
-interface nsIURI;
-interface nsIProxyInfo0;
-
-[scriptable, uuid(0a24fed4-1dd2-11b2-a75c-9f8b9a8f9ba7)]
-interface nsIProxiedProtocolHandler : nsIProtocolHandler
-{
-    /** Create a new channel with the given proxyInfo
-     *
-     */
-    nsIChannel newProxiedChannel(in nsIURI uri, in nsIProxyInfo0 proxyInfo);
-};
-
diff --git a/third_party/gecko/nsIProxyInfo.idl b/third_party/gecko/nsIProxyInfo.idl
deleted file mode 100644
index bbe07b8..0000000
--- a/third_party/gecko/nsIProxyInfo.idl
+++ /dev/null
@@ -1,104 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Bradley Baetz <bbaetz@netscape.com> (Original Developer)
- *   Malcolm Smith <malsmith@cs.rmit.edu.au>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsISupports.idl"
-
-/**
- * This interface identifies a proxy server.
- *
- * @status UNDER_REVIEW
- */
-[scriptable, uuid(3fe9308b-1608-4fa0-933c-c5ec2c6175fd)]
-interface nsIProxyInfo : nsISupports
-{
-  /**
-   * This attribute specifies the hostname of the proxy server.
-   */
-  readonly attribute AUTF8String host;
-
-  /**
-   * This attribute specifies the port number of the proxy server.
-   */
-  readonly attribute long port;
-
-  /**
-   * This attribute specifies the type of the proxy server as an ASCII string.
-   *
-   * Some special values for this attribute include (but are not limited to)
-   * the following:
-   *   "http"     HTTP proxy (or SSL CONNECT for HTTPS)
-   *   "socks"    SOCKS v5 proxy
-   *   "socks4"   SOCKS v4 proxy
-   *   "direct"   no proxy
-   */
-  readonly attribute ACString type; 
-
-  /**
-   * This attribute specifies flags that modify the proxy type.  The value of
-   * this attribute is the bit-wise combination of the Proxy Flags defined
-   * below.  Any undefined bits are reserved for future use.
-   */
-  readonly attribute unsigned long flags;
-
-  /**
-   * This attribute specifies the failover timeout in seconds for this proxy.
-   * If a nsIProxyInfo is reported as failed via nsIProtocolProxyService::
-   * getFailoverForProxy, then the failed proxy will not be used again for this
-   * many seconds.
-   */
-  readonly attribute unsigned long failoverTimeout;
-
-  /**
-   * This attribute specifies the proxy to failover to when this proxy fails.
-   */
-  attribute nsIProxyInfo failoverProxy;
-
-
-  /****************************************************************************
-   * The following "Proxy Flags" may be bit-wise combined to construct the flags
-   * attribute defined on this interface.
-   */
-
-  /**
-   * This flag is set if the proxy is to perform name resolution itself.  If
-   * this is the case, the hostname is used in some fashion, and we shouldn't
-   * do any form of DNS lookup ourselves.
-   */
-  const unsigned short TRANSPARENT_PROXY_RESOLVES_HOST = 1 << 0;
-};
diff --git a/third_party/gecko/nsIProxyInfo0.idl b/third_party/gecko/nsIProxyInfo0.idl
deleted file mode 100644
index ef0f45d..0000000
--- a/third_party/gecko/nsIProxyInfo0.idl
+++ /dev/null
@@ -1,67 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: NPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is 
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *                  Bradley Baetz <bbaetz@netscape.com> (Original Developer)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or 
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the NPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the NPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/**
- * NOTE: This is the version of nsIProxyInfo from Mozilla 1.7 --Darin
- */
-
-#include "nsISupports.idl"
-
-/* This interface is PRIVATE.
- * It is used as an opaque cookie, and this class may change at
- * any time without notice.
- *
- * @see nsIProtocolProxyService0::GetProxyInfo
- */
-
-native constCharPtr(const char*);
-
-[scriptable, uuid(b65d22b0-1dd1-11b2-8f95-920e5b7b56f0)]
-interface nsIProxyInfo0 : nsISupports
-{
-    [noscript, notxpcom] constCharPtr Host();
-    [noscript, notxpcom] PRInt32 Port();
-    [noscript, notxpcom] constCharPtr Type();
-
-    /**
-     * proxy info objects may be chained if several proxies could be treated
-     * equivalently.  this is used to support proxy failover.
-     */
-    readonly attribute nsIProxyInfo0 next;
-};
-
diff --git a/third_party/gecko/nsITimer.idl b/third_party/gecko/nsITimer.idl
deleted file mode 100644
index 8548938..0000000
--- a/third_party/gecko/nsITimer.idl
+++ /dev/null
@@ -1,196 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2002
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsISupports.idl"
-
-interface nsIObserver;
-
-%{C++
-/**
- * The signature of the timer callback function passed to initWithFuncCallback.
- * This is the function that will get called when the timer expires if the
- * timer is initialized via initWithFuncCallback.
- *
- * @param aTimer the timer which has expired
- * @param aClosure opaque parameter passed to initWithFuncCallback
- *
- * Implementers should return the following:
- *
- * @return NS_OK
- *
- */
-class nsITimer;
-typedef void (*nsTimerCallbackFunc) (nsITimer *aTimer, void *aClosure);
-%}
-
-native nsTimerCallbackFunc(nsTimerCallbackFunc);
-
-/**
- * The callback interface for timers.
- */
-interface nsITimer;
-
-[scriptable, uuid(a796816d-7d47-4348-9ab8-c7aeb3216a7d)]
-interface nsITimerCallback : nsISupports
-{
-  /**
-   * @param aTimer the timer which has expired
-   */
-  void notify(in nsITimer timer);
-};
-
-
-/**
- * nsITimer instances must be initialized by calling one of the "init" methods
- * documented below.  You may also re-initialize an existing instance with new
- * delay to avoid the overhead of destroying and creating a timer.  It is not
- * necessary to cancel the timer in that case.
- */
-[scriptable, uuid(436a83fa-b396-11d9-bcfa-00112478d626)]
-interface nsITimer : nsISupports
-{
-  /* Timer types */
-
-  /**
-   * Type of a timer that fires once only.
-   */
-  const short TYPE_ONE_SHOT           = 0;
-
-  /**
-   * After firing, a TYPE_REPEATING_SLACK timer is stopped and not restarted
-   * until its callback completes.  Specified timer period will be at least
-   * the time between when processing for last firing the callback completes
-   * and when the next firing occurs.
-   *
-   * This is the preferable repeating type for most situations.
-   */
-  const short TYPE_REPEATING_SLACK    = 1;
-  
-  /**
-   * An TYPE_REPEATING_PRECISE repeating timer aims to have constant period
-   * between firings.  The processing time for each timer callback should not
-   * influence the timer period.  However, if the processing for the last
-   * timer firing could not be completed until just before the next firing
-   * occurs, then you could have two timer notification routines being
-   * executed in quick succession.
-   */
-  const short TYPE_REPEATING_PRECISE  = 2;
-
-  /**
-   * Initialize a timer that will fire after the said delay.
-   * A user must keep a reference to this timer till it is 
-   * is no longer needed or has been cancelled.
-   *
-   * @param aObserver   the callback object that observes the 
-   *                    ``timer-callback'' topic with the subject being
-   *                    the timer itself when the timer fires:
-   *
-   *                    observe(nsISupports aSubject, => nsITimer
-   *                            string aTopic,        => ``timer-callback''
-   *                            wstring data          =>  null
-   *
-   * @param aDelay      delay in milliseconds for timer to fire
-   * @param aType       timer type per TYPE* consts defined above
-   */
-  void init(in nsIObserver aObserver, in unsigned long aDelay, 
-            in unsigned long aType);
-
-
-  /**
-   * Initialize a timer to fire after the given millisecond interval.
-   * This version takes a function to call and a closure to pass to
-   * that function.
-   *
-   * @param aFunc      The function to invoke
-   * @param aClosure   An opaque pointer to pass to that function
-   * @param aDelay     The millisecond interval
-   * @param aType      Timer type per TYPE* consts defined above
-   */
-  [noscript] void initWithFuncCallback(in nsTimerCallbackFunc aCallback,
-                                       in voidPtr aClosure,
-                                       in unsigned long aDelay, 
-                                       in unsigned long aType);
-
-  /**
-   * Initialize a timer to fire after the given millisecond interval.
-   * This version takes a function to call and a closure to pass to
-   * that function.
-   *
-   * @param aFunc      nsITimerCallback interface to call when timer expires
-   * @param aDelay     The millisecond interval
-   * @param aType      Timer type per TYPE* consts defined above
-   */
-  void initWithCallback(in nsITimerCallback aCallback,
-                        in unsigned long aDelay, 
-                        in unsigned long aType);
-
-  /**
-   * Cancel the timer.  This method works on all types, not just on repeating
-   * timers -- you might want to cancel a TYPE_ONE_SHOT timer, and even reuse
-   * it by re-initializing it (to avoid object destruction and creation costs
-   * by conserving one timer instance).
-   */
-  void cancel();
-  
-  /**
-   * The millisecond delay of the timeout
-   */
-  attribute unsigned long delay;
-  
-  /**
-   * The timer type : one shot or repeating
-   */  
-  attribute unsigned long type;
-
-  /**
-   * The opaque pointer pass to initWithFuncCallback.
-   */  
-  [noscript] readonly attribute voidPtr closure;
-
-  /**
-   * The nsITimerCallback object passed to initWithCallback.
-   */
-  readonly attribute nsITimerCallback callback;
-};
-
-%{C++
-#define NS_TIMER_CONTRACTID "@mozilla.org/timer;1"
-#define NS_TIMER_CALLBACK_TOPIC "timer-callback"
-%}
-
diff --git a/third_party/gecko/nsITimer0.idl b/third_party/gecko/nsITimer0.idl
deleted file mode 100644
index 4c042a9..0000000
--- a/third_party/gecko/nsITimer0.idl
+++ /dev/null
@@ -1,195 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2002
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/**
- * NOTE: This is the version of nsITimer from Mozilla 1.7 --Darin
- */
-
-#include "nsISupports.idl"
-
-interface nsIObserver;
-
-%{C++
-/**
- * The signature of the timer callback function passed to initWithCallback. This
- * is the function that will get called when the timer expires if the timer is 
- * initialized via initWithCallback.
- *
- * @param aTimer the timer which has expired
- * @param aClosure opaque parameter passed to initWithCallback
- *
- * Implementers should return the following:
- *
- * @return NS_OK
- *
- */
-class nsITimer0;
-typedef void (*nsTimerCallbackFunc0) (nsITimer0 *aTimer, void *aClosure);
-%}
-
-native nsTimerCallbackFunc0(nsTimerCallbackFunc0);
-
-/**
- * The callback interface for timers.
- */
-interface nsITimer0;
-
-[scriptable, uuid(a796816d-7d47-4348-9ab8-c7aeb3216a7d)]
-interface nsITimerCallback0 : nsISupports
-{
-  /**
-   * @param aTimer the timer which has expired
-   */
-  void notify(in nsITimer0 timer);
-};
-
-
-/**
- * nsITimer0 instances must be initialized by calling one of the "init" methods
- * documented below.  You may also re-initialize an existing instance with new
- * delay to avoid the overhead of destroying and creating a timer.  It is not
- * necessary to cancel the timer in that case.
- */
-[scriptable, uuid(29ee628e-a3ea-471f-965d-dc9f11d1c183)]
-interface nsITimer0 : nsISupports
-{
-  /* Timer types */
-
-  /**
-   * Type of a timer that fires once only.
-   */
-  const short TYPE_ONE_SHOT           = 0;
-
-  /**
-   * After firing, a TYPE_REPEATING_SLACK timer is stopped and not restarted
-   * until its callback completes.  Specified timer period will be at least
-   * the time between when processing for last firing the callback completes
-   * and when the next firing occurs.
-   *
-   * This is the preferable repeating type for most situations.
-   */
-  const short TYPE_REPEATING_SLACK    = 1;
-  
-  /**
-   * An TYPE_REPEATING_PRECISE repeating timer aims to have constant period
-   * between firings.  The processing time for each timer callback should not
-   * influence the timer period.  However, if the processing for the last
-   * timer firing could not be completed until just before the next firing
-   * occurs, then you could have two timer notification routines being
-   * executed in quick succession.
-   */
-  const short TYPE_REPEATING_PRECISE  = 2;
-
-  /**
-   * Initialize a timer that will fire after the said delay.
-   * A user must keep a reference to this timer till it is 
-   * is no longer needed or has been cancelled.
-   *
-   * @param aObserver   the callback object that observes the 
-   *                    ``timer-callback'' topic with the subject being
-   *                    the timer itself when the timer fires:
-   *
-   *                    observe(nsISupports aSubject, => nsITimer0
-   *                            string aTopic,        => ``timer-callback''
-   *                            wstring data          =>  null
-   *
-   * @param aDelay      delay in milliseconds for timer to fire
-   * @param aType       timer type per TYPE* consts defined above
-   */
-  void init(in nsIObserver aObserver, in unsigned long aDelay, 
-            in unsigned long aType);
-
-
-  /**
-   * Initialize a timer to fire after the given millisecond interval.
-   * This version takes a function to call and a closure to pass to
-   * that function.
-   *
-   * @param aFunc      The function to invoke
-   * @param aClosure   An opaque pointer to pass to that function
-   * @param aDelay     The millisecond interval
-   * @param aType      Timer type per TYPE* consts defined above
-   */
-  [noscript] void initWithFuncCallback(in nsTimerCallbackFunc0 aCallback,
-                                       in voidPtr aClosure,
-                                       in unsigned long aDelay, 
-                                       in unsigned long aType);
-
-  /**
-   * Initialize a timer to fire after the given millisecond interval.
-   * This version takes a function to call and a closure to pass to
-   * that function.
-   *
-   * @param aFunc      nsITimerCallback0 interface to call when timer expires
-   * @param aDelay     The millisecond interval
-   * @param aType      Timer type per TYPE* consts defined above
-   */
-  void initWithCallback(in nsITimerCallback0 aCallback,
-                        in unsigned long aDelay, 
-                        in unsigned long aType);
-
-  /**
-   * Cancel the timer.  This method works on all types, not just on repeating
-   * timers -- you might want to cancel a TYPE_ONE_SHOT timer, and even reuse
-   * it by re-initializing it (to avoid object destruction and creation costs
-   * by conserving one timer instance).
-   */
-  void cancel();
-  
-  /**
-   * The millisecond delay of the timeout
-   */
-  attribute unsigned long delay;
-  
-  /**
-   * The timer type : one shot or repeating
-   */  
-  attribute unsigned long type;
-
-  /**
-   * The opaque pointer pass to initWithCallback.
-   */  
-  [noscript] readonly attribute voidPtr closure;
-};
-
-%{C++
-#define NS_TIMER_CONTRACTID "@mozilla.org/timer;1"
-#define NS_TIMER_CALLBACK_TOPIC "timer-callback"
-%}
-
diff --git a/third_party/gecko/nsIWebNavigation.idl b/third_party/gecko/nsIWebNavigation.idl
deleted file mode 100644
index 1de75b2..0000000
--- a/third_party/gecko/nsIWebNavigation.idl
+++ /dev/null
@@ -1,206 +0,0 @@
-/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.mozilla.org/MPL/
- * 
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- * 
- * The Original Code is the Mozilla browser.
- * 
- * The Initial Developer of the Original Code is Netscape
- * Communications, Inc.  Portions created by Netscape are
- * Copyright (C) 1999, Mozilla.  All Rights Reserved.
- * 
- * Contributor(s):
- *   Travis Bogard <travis@netscape.com>
- */
-#include "nsISupports.idl"
-
-
-/**
- * The nsIWebNavigation interface defines an interface for navigating the web.
- * It provides methods and attributes to direct an object to navigate to a new
- * location, stop or restart an in process load or determine where the object,
- * has previously gone.   
- *
- * @status UNDER_REVIEW
- */
-
-interface nsIDOMDocument;
-interface nsIInputStream;
-interface nsISHistory;
-interface nsISHEntry;
-interface nsIURI;
-
-[scriptable, uuid(F5D9E7B0-D930-11d3-B057-00A024FFC08C)]
-interface nsIWebNavigation : nsISupports
-{
- /**
-  * Indicates if the object can go back.  If true this indicates that
-  * there is back session history available for navigation.
-  */
-  readonly attribute boolean canGoBack;
-
- /**
-  * Indicates if the object can go forward.  If true this indicates that
-  * there is forward session history available for navigation
-  */
-  readonly attribute boolean canGoForward;
-
- /**
-  * Tells the object to navigate to the previous session history item.  When
-  * a page is loaded from session history, all content is loaded from the
-  * cache (if available) and page state (such as form values, scroll position)
-  * is restored.
-  *
-  * @return NS_OK               - Backward navigation was successful.
-  *         NS_ERROR_UNEXPECTED - This call was unexpected at this time.  Most
-  *                               likely you can't go back right now.
-  */
-  void goBack();
-
- /**
-  * Tells the object to navigate to the next Forward session history item.
-  * When a page is loaded from session history, all content is loaded from
-  * the cache (if available) and page state (such as form values, scroll
-  * position) is restored.
-  *
-  * @return NS_OK               - Forward was successful.
-  *         NS_ERROR_UNEXPECTED - This call was unexpected at this time.  Most
-  *                               likely you can't go forward right now.
-  */
-  void goForward();
-
- /**
-  * Tells the object to navigate to the session history item at index.
-  *
-  * @return NS_OK -               GotoIndex was successful.
-  *         NS_ERROR_UNEXPECTED - This call was unexpected at this time.  Most
-  *                               likely you can't goto that index
-  */
-  void gotoIndex(in long index);
-
- /**
-  * Load flags for use with loadURI() and reload()
-  */
-  const unsigned long LOAD_FLAGS_MASK            = 0xffff;
-
- /**
-  * loadURI() specific flags
-  */
-
- /**
-  * Normal load flag.
-  */
-  const unsigned long LOAD_FLAGS_NONE            = 0x0000;
-
- /**
-  * Meta-refresh flag.  The cache is bypassed.  This type of load is
-  *                     usually the result of a meta-refresh tag, or a HTTP
-  *                     'refresh' header.
-  */
-  const unsigned long LOAD_FLAGS_IS_REFRESH      = 0x0010;
-
- /**
-  * Link-click flag. 
-  */
-  const unsigned long LOAD_FLAGS_IS_LINK         = 0x0020;
-
- /**
-  * Bypass history flag.
-  */
-  const unsigned long LOAD_FLAGS_BYPASS_HISTORY  = 0x0040;
-
- /**
-  * Replace history entry flag.
-  */
-  const unsigned long LOAD_FLAGS_REPLACE_HISTORY = 0x0080;
-
-  /* loadURI() & reload() specific flags */
-  const unsigned long LOAD_FLAGS_BYPASS_CACHE    = 0x0100; // Bypass the cache
-  const unsigned long LOAD_FLAGS_BYPASS_PROXY    = 0x0200; // Bypass the proxy
-  const unsigned long LOAD_FLAGS_CHARSET_CHANGE  = 0x0400; // Reload because of charset change, 
-
- /**
-  * Loads a given URI.  This will give priority to loading the requested URI
-  * in the object implementing	this interface.  If it can't be loaded here
-  * however, the URL dispatcher will go through its normal process of content
-  * loading.
-  *
-  * @param uri       - The URI string to load.
-  * @param loadFlags - Flags modifying load behaviour. Generally you will pass
-  *                    LOAD_FLAGS_NONE for this parameter.
-  * @param referrer  - The referring URI.  If this argument is NULL, the
-  *                    referring URI will be inferred internally.
-  * @param postData  - nsIInputStream containing POST data for the request.
-  */
-  void loadURI(in wstring uri,
-               in unsigned long loadFlags,
-               in nsIURI referrer,
-               in nsIInputStream postData,
-               in nsIInputStream headers);
-
- /**
-  * Tells the Object to reload the current page.
-  *
-  * @param reloadFlags - Flags modifying reload behaviour. Generally you will
-  *                      pass LOAD_FLAGS_NONE for this parameter.
-  */
-  void reload(in unsigned long reloadFlags);
-
- /**
-  * Stop() flags:
-  */
-
- /**
-  * Stop all network activity.  This includes both active network loads and
-  * pending meta-refreshes.
-  */
-  const unsigned long STOP_NETWORK = 0x01;
-
- /**
-  * Stop all content activity.  This includes animated images, plugins and
-  * pending Javascript timeouts.
-  */
-  const unsigned long STOP_CONTENT = 0x02;
-
- /**
-  * Stop all activity.
-  */
-  const unsigned long STOP_ALL = 0x03;
-
- /**
-  * Stops a load of a URI.
-  *
-  * @param stopFlags - Flags indicating the stop behavior.
-  */
-  void stop(in unsigned long stopFlags);
-
- /**
-  * Retrieves the current DOM document for the frame, or lazily creates a
-  * blank document if there is none. This attribute never returns null except
-  * for unexpected error situations.
-  */
-  readonly attribute nsIDOMDocument document;
-
- /**
-  * The currently loaded URI or null.
-  */
-  readonly attribute nsIURI currentURI;
-
- /**
-  * The referring URI.
-  */
-  readonly attribute nsIURI referringURI;
-
- /**
-  * The session history object used to store the session history for the
-  * session.
-  */
-  attribute nsISHistory sessionHistory;
-};
diff --git a/third_party/gecko/nsIWritablePropertyBag.idl b/third_party/gecko/nsIWritablePropertyBag.idl
deleted file mode 100644
index 83bdab0..0000000
--- a/third_party/gecko/nsIWritablePropertyBag.idl
+++ /dev/null
@@ -1,60 +0,0 @@
-/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org code.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   John Bandhauer <jband@netscape.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/* nsIVariant based writable Property Bag support. */
-
-#include "nsIPropertyBag.idl"
-
-[scriptable, uuid(96fc4671-eeb4-4823-9421-e50fb70ad353)]
-interface nsIWritablePropertyBag : nsIPropertyBag
-{
-    /**
-     * Set a property with the given name to the given value.  If
-     * a property already exists with the given name, it is
-     * overwritten.
-     */
-    void setProperty(in AString name, in nsIVariant value);
-
-    /**
-     * Delete a property with the given name.
-     * @throws NS_ERROR_FAILURE if a property with that name doesn't
-     * exist.
-     */
-    void deleteProperty(in AString name);
-};
diff --git a/third_party/gecko/nsIWritablePropertyBag2.idl b/third_party/gecko/nsIWritablePropertyBag2.idl
deleted file mode 100644
index 9968015..0000000
--- a/third_party/gecko/nsIWritablePropertyBag2.idl
+++ /dev/null
@@ -1,55 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is mozilla.org property bag support.
- *
- * The Initial Developer of the Original Code is
- * Christian Biesinger <cbiesinger@web.de>.
- * Portions created by the Initial Developer are Copyright (C) 2005
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *    Darin Fisher <darin@meer.net>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/* nsIVariant based Property Bag support. */
-
-#include "nsIPropertyBag2.idl"
-
-[scriptable, uuid(ee42c54a-19d3-472b-8bc3-76318d5ab5f4)]
-interface nsIWritablePropertyBag2 : nsIPropertyBag2
-{
-  void        setPropertyAsInt32       (in AString prop, in PRInt32 value);
-  void        setPropertyAsUint32      (in AString prop, in PRUint32 value);
-  void        setPropertyAsInt64       (in AString prop, in PRInt64 value);
-  void        setPropertyAsUint64      (in AString prop, in PRUint64 value);
-  void        setPropertyAsDouble      (in AString prop, in double value);
-  void        setPropertyAsAString     (in AString prop, in AString value);
-  void        setPropertyAsACString    (in AString prop, in ACString value);
-  void        setPropertyAsAUTF8String (in AString prop, in AUTF8String value);
-  void        setPropertyAsBool        (in AString prop, in boolean value);
-  void        setPropertyAsInterface   (in AString prop, in nsISupports value);
-};
diff --git a/third_party/gmock/CHANGES b/third_party/gmock/CHANGES
new file mode 100644
index 0000000..f703ac2
--- /dev/null
+++ b/third_party/gmock/CHANGES
@@ -0,0 +1,39 @@
+Changes for 1.4.0 (we skipped 1.2.* and 1.3.* to match the version of
+Google Test):
+
+ * Works in more environments: Symbian and minGW, Visual C++ 7.1.
+ * Lighter weight: comes with our own implementation of TR1 tuple (no
+   more dependency on Boost!).
+ * New feature: --gmock_catch_leaked_mocks for detecting leaked mocks.
+ * New feature: ACTION_TEMPLATE for defining templatized actions.
+ * New feature: the .After() clause for specifying expectation order.
+ * New feature: the .With() clause for for specifying inter-argument
+   constraints.
+ * New feature: actions ReturnArg<k>(), ReturnNew<T>(...), and
+   DeleteArg<k>().
+ * New feature: matchers Key(), Pair(), Args<...>(), AllArgs(), IsNull(),
+   and Contains().
+ * New feature: utility class MockFunction<F>, useful for checkpoints, etc.
+ * New feature: functions Value(x, m) and SafeMatcherCast<T>(m).
+ * New feature: copying a mock object is rejected at compile time.
+ * New feature: a script for fusing all Google Mock and Google Test
+   source files for easy deployment.
+ * Improved the Google Mock doctor to diagnose more diseases.
+ * Improved the Google Mock generator script.
+ * Compatibility fixes for Mac OS X and gcc.
+ * Bug fixes and implementation clean-ups.
+
+Changes for 1.1.0:
+
+ * New feature: ability to use Google Mock with any testing framework.
+ * New feature: macros for easily defining new matchers
+ * New feature: macros for easily defining new actions.
+ * New feature: more container matchers.
+ * New feature: actions for accessing function arguments and throwing
+   exceptions.
+ * Improved the Google Mock doctor script for diagnosing compiler errors.
+ * Bug fixes and implementation clean-ups.
+
+Changes for 1.0.0:
+
+ * Initial Open Source release of Google Mock
diff --git a/third_party/gmock/CONTRIBUTORS b/third_party/gmock/CONTRIBUTORS
new file mode 100644
index 0000000..9a8cc2e
--- /dev/null
+++ b/third_party/gmock/CONTRIBUTORS
@@ -0,0 +1,39 @@
+# This file contains a list of people who've made non-trivial
+# contribution to the Google C++ Mocking Framework project.  People
+# who commit code to the project are encouraged to add their names
+# here.  Please keep the list sorted by first names.
+
+Benoit Sigoure <tsuna@google.com>
+Bogdan Piloca <boo@google.com>
+Chandler Carruth <chandlerc@google.com>
+Dave MacLachlan <dmaclach@gmail.com>
+David Anderson <danderson@google.com>
+Dean Sturtevant
+Gene Volovich <gv@cite.com>
+Hal Burch <gmock@hburch.com>
+Jeffrey Yasskin <jyasskin@google.com>
+Jim Keller <jimkeller@google.com>
+Joe Walnes <joe@truemesh.com>
+Jon Wray <jwray@google.com>
+Keir Mierle <mierle@gmail.com>
+Keith Ray <keith.ray@gmail.com>
+Kostya Serebryany <kcc@google.com>
+Lev Makhlis
+Mario Tanev <radix@google.com>
+Mark Paskin
+Markus Heule <markus.heule@gmail.com>
+Matthew Simmons <simmonmt@acm.org>
+Mike Bland <mbland@google.com>
+Neal Norwitz <nnorwitz@gmail.com>
+Nermin Ozkiranartli <nermin@google.com>
+Owen Carlsen <ocarlsen@google.com>
+Paneendra Ba <paneendra@google.com>
+Paul Menage <menage@google.com>
+Piotr Kaminski <piotrk@google.com>
+Russ Rufer <russ@pentad.com>
+Sverre Sundsdal <sundsdal@gmail.com>
+Takeshi Yoshino <tyoshino@google.com>
+Vadim Berman <vadimb@google.com>
+Vlad Losev <vladl@google.com>
+Wolfgang Klier <wklier@google.com>
+Zhanyong Wan <wan@google.com>
diff --git a/third_party/gmock/COPYING b/third_party/gmock/COPYING
new file mode 100644
index 0000000..1941a11
--- /dev/null
+++ b/third_party/gmock/COPYING
@@ -0,0 +1,28 @@
+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.
diff --git a/third_party/gmock/README b/third_party/gmock/README
new file mode 100644
index 0000000..9cbda5d
--- /dev/null
+++ b/third_party/gmock/README
@@ -0,0 +1,328 @@
+Google C++ Mocking Framework
+============================
+http://code.google.com/p/googlemock/
+
+Overview
+--------
+Google's framework for writing and using C++ mock classes on Linux,
+Mac OS X, and Windows.  Inspired by jMock, EasyMock, and Hamcrest, and
+designed with C++'s specifics in mind, it can help you derive better
+designs of your system and write better tests.
+
+Google Mock:
+
+- provides a declarative syntax for defining mocks,
+- can easily define partial (hybrid) mocks, which are a cross of real
+  and mock objects,
+- handles functions of arbitrary types and overloaded functions,
+- comes with a rich set of matchers for validating function arguments,
+- uses an intuitive syntax for controlling the behavior of a mock,
+- does automatic verification of expectations (no record-and-replay
+  needed),
+- allows arbitrary (partial) ordering constraints on
+  function calls to be expressed,
+- lets a user extend it by defining new matchers and actions.
+- does not use exceptions, and
+- is easy to learn and use.
+
+Please see the project page above for more information as well as mailing lists
+for questions, discussions, and development. There is also an IRC channel on
+OFTC (irc.oftc.net) #gtest available. Please join us!
+
+Please note that code under scripts/generator/ is from the cppclean
+project (http://code.google.com/p/cppclean/) and under the Apache
+License, which is different from Google Mock's license.
+
+Requirements
+------------
+Google Mock is not a testing framework itself. Instead, it needs a
+testing framework for writing tests. It works with Google Test
+(http://code.google.com/p/googletest/) out of the box. You can use
+either the copy of Google Test that comes with Google Mock, or a
+compatible version you already have.  This version of Google Mock
+requires Google Test 1.4.0.
+
+You can also easily configure Google Mock to work with another testing
+framework of your choice; although it will still need Google Test as
+an internal dependency.  Please read
+http://code.google.com/p/googlemock/wiki/ForDummies#Using_Google_Mock_with_Any_Testing_Framework
+for how to do it.
+
+Google Mock depends on advanced C++ features and thus requires a more
+modern compiler.  The following are needed to use Google Mock:
+
+### Linux Requirements ###
+These are the base requirements to build and use Google Mock from a source
+package (as described below):
+  * GNU-compatible Make or "gmake"
+  * POSIX-standard shell
+  * POSIX(-2) Regular Expressions (regex.h)
+  * gcc 3.4 or newer.
+
+Furthermore, if you are building Google Mock from a VCS Checkout (also
+described below), there are further requirements:
+  * Automake version 1.9 or newer
+  * Autoconf version 2.59 or newer
+  * Libtool / Libtoolize
+  * Python version 2.3 or newer
+
+### Windows Requirements ###
+  * Microsoft Visual C++ 8.0 SP1 or newer
+
+### Mac OS X Requirements ###
+  * Mac OS X 10.4 Tiger or newer
+  * Developer Tools Installed
+
+Getting the Source
+------------------
+There are two primary ways of getting Google Mock's source code: you can
+download a source release in your preferred archive format, or directly check
+out the source from a Version Control System (VCS, we use Google Code's
+Subversion hosting). The VCS checkout requires a few extra steps and some extra
+software packages on your system, but lets you track development, and make
+patches to contribute much more easily, so we highly encourage it.
+
+### VCS Checkout: ###
+The first step is to select whether you want to check out the main line of
+development on Google Mock, or one of the released branches. The former will be
+much more active and have the latest features, but the latter provides much
+more stability and predictability. Choose whichever fits your needs best, and
+proceed with the following Subversion commands:
+
+  svn checkout http://googlemock.googlecode.com/svn/trunk/ gmock-svn
+
+or for a release version X.Y.*'s branch:
+
+  svn checkout http://googlemock.googlecode.com/svn/branches/release-X.Y/ \
+    gmock-X.Y-svn
+
+Next you will need to prepare the GNU Autotools build system, if you
+are using Linux or Mac OS X. Enter the target directory of the
+checkout command you used ('gmock-svn' or 'gmock-X.Y-svn' above) and
+proceed with the following command:
+
+  autoreconf -fvi
+
+Once you have completed this step, you are ready to build the library. Note
+that you should only need to complete this step once. The subsequent `make'
+invocations will automatically re-generate the bits of the build system that
+need to be changed.
+
+If your system uses older versions of the autotools, the above command will
+fail. You may need to explicitly specify a version to use. For instance, if you
+have both GNU Automake 1.4 and 1.9 installed and `automake' would invoke the
+1.4, use instead:
+
+  AUTOMAKE=automake-1.9 ACLOCAL=aclocal-1.9 autoreconf -fvi
+
+Make sure you're using the same version of automake and aclocal.
+
+### Source Package: ###
+Google Mock is also released in source packages which can be downloaded from
+its Google Code download page[1]. Several different archive formats are
+provided, but the only difference is the tools needed to extract their
+contents, and the size of the resulting file. Download whichever you are most
+comfortable with.
+
+  [1] Google Mock Downloads: http://code.google.com/p/googlemock/downloads/list
+
+Once downloaded expand the archive using whichever tools you prefer for that
+type. This will always result in a new directory with the name "gmock-X.Y.Z"
+which contains all of the source code. Here are some examples in Linux:
+
+  tar -xvzf gmock-X.Y.Z.tar.gz
+  tar -xvjf gmock-X.Y.Z.tar.bz2
+  unzip gmock-X.Y.Z.zip
+
+Choosing a TR1 Tuple Library
+----------------------------
+Google Mock uses the C++ Technical Report 1 (TR1) tuple library
+heavily.  Unfortunately TR1 tuple is not yet widely available with all
+compilers.  The good news is that Google Test 1.4.0+ implements a
+subset of TR1 tuple that's enough for Google Mock's need.  Google Mock
+will automatically use that implementation when the compiler doesn't
+provide TR1 tuple.
+
+Usually you don't need to care about which tuple library Google Test
+and Google Mock use.  However, if your project already uses TR1 tuple,
+you need to tell Google Test and Google Mock to use the same TR1 tuple
+library the rest of your project uses (this requirement is new in
+Google Test 1.4.0 and Google Mock 1.2.0, so you may need to take care
+of it when upgrading from an earlier version), or the two tuple
+implementations will clash.  To do that, add
+
+  -DGTEST_USE_OWN_TR1_TUPLE=0
+
+to the compiler flags while compiling Google Test, Google Mock, and
+your tests.
+
+If you want to use Boost's TR1 tuple library with Google Mock, please
+refer to the Boost website (http://www.boost.org/) for how to obtain
+it and set it up.
+
+Building the Source
+-------------------
+### Linux and Mac OS X (without Xcode) ###
+There are two primary options for building the source at this point: build it
+inside the source code tree, or in a separate directory. We recommend building
+in a separate directory as that tends to produce both more consistent results
+and be easier to clean up should anything go wrong, but both patterns are
+supported. The only hard restriction is that while the build directory can be
+a subdirectory of the source directory, the opposite is not possible and will
+result in errors. Once you have selected where you wish to build Google Mock,
+create the directory if necessary, and enter it. The following steps apply for
+either approach by simply substituting the shell variable SRCDIR with "." for
+building inside the source directory, and the relative path to the source
+directory otherwise.
+
+  ${SRCDIR}/configure  # Standard GNU configure script, --help for more info
+
+The default behavior of the configure script with respect to locating and using
+Google Test is to first search for a 'gtest-config' in the system path, and
+lacking this, build an internal copy of Google Test. You may optionally specify
+a custom Google Test you wish to build Google Mock against, provided it is
+a new enough version.
+
+  # Configure against an installation in '/opt' with '/opt/bin/gtest-config'.
+  ${SRCDIR}/configure --with-gtest=/opt
+
+This can also be used to specify a Google Test which hasn't yet been installed.
+However, it must have been configured and built as described in the Google Test
+README before you configure Google Mock. To enable this feature, simply pass
+the directory where you configured and built Google Test (which is not
+necessarily its source directory) to Google Mock's configure script.
+
+  # Configure against a build of Google Test in an arbitrary directory.
+  ${SRCDIR}/configure --with-gtest=../../my_gtest_build
+
+Finally, if you have a version of Google Test installed but for some reason
+wish to forcibly prevent it from being used, we provide a special option.
+Typically this is not needed as we fall back to the internal Google Test
+packaged with Google Mock if an installed version is either unavailable or too
+old to build Google Mock. When using the internally packaged Google Test, the
+user does *not* need to configure or build it, that is automatically handled by
+Google Mock's build system.
+
+  # Force the use of the internally packaged Google Test, despite
+  # 'gtest-config' being in your PATH.
+  ${SRCDIR}/configure --disable-external-gtest
+
+Once you have successfully configured Google Mock, the build steps are standard
+for GNU-style OSS packages.
+
+  make  # Standard makefile following GNU conventions
+  make check  # Builds and runs all tests - all should pass
+
+Other programs will only be able to use Google Mock's functionality if you
+install it in a location which they can access, in Linux this is typically
+under '/usr/local'. The following command will install all of the Google Mock
+libraries, public headers, and utilities necessary for other programs and
+libraries to leverage it. Note that if Google Mock was unable to find an
+external Google Test to build against, it will also install the internally
+packaged Google Test in order to allow the installed Google Mock to function
+properly. This Google Test install will be fully functional, and if installed
+will also be uninstalled by uninstalling Google Mock.
+
+  sudo make install  # Not necessary, but allows use by other programs
+
+Should you need to remove Google Mock from your system after having installed
+it, run the following command, and it will back out its changes.  However, note
+carefully that you must run this command on the *same* Google Mock build that
+you ran the install from, or the results are not predictable.  If you install
+Google Mock on your system, and are working from a VCS checkout, make sure you
+run this *before* updating your checkout of the source in order to uninstall
+the same version which you installed.
+
+  sudo make uninstall  # Must be run against the exact same build as "install"
+
+Your project can build against Google Mock and Google Test simply by leveraging
+the 'gmock-config' script. This script can be invoked directly out of the
+'scripts' subdirectory of the build tree, and it will be installed in the
+binary directory specified during the 'configure'. Here are some examples of
+its use, see 'gmock-config --help' for more detailed information.
+
+  gmock-config --min-version=1.0 || echo "Insufficient Google Mock version."
+
+  g++ $(gmock-config --cppflags --cxxflags) -o foo.o -c foo.cpp
+  g++ $(gmock-config --ldflags --libs) -o foo foo.o
+
+  # When using a built but not installed Google Mock:
+  g++ $(../../my_gmock_build/scripts/gmock-config ...) ...
+
+Note that when building your project against Google Mock, you are building
+against Google Test as well. There is no need to configure Google Test
+separately.
+
+### Windows ###
+The msvc/ directory contains VC++ 2005 projects for building Google
+Mock and selected tests.
+
+If you want to use a version of Google Test other then the one bundled with
+Google Mock, change the value of the GTestDir macro in gmock_config.vsprop
+to point to the new location.
+
+Open msvc/gmock.sln and build the library and tests. If you want to
+create your own project to use with Google Mock, you'll have to
+configure it to use the gmock_config propety sheet. For that:
+ * Open the Property Manager window (View | Other Windows | Property Manager)
+ * Right-click on your project and select "Add Existing Property Sheet..."
+ * Navigate to gmock_config.vsprops and select it.
+ * In Project Properties | Configuration Properties | General | Additional
+   Include Directories, type <path to Google Mock>/include.
+
+TODO(wan@google.com): update the .vsprops and .vcproj files such that the
+last step is unnecessary.
+
+### Using GNU Make ###
+The make/ directory contains a Makefile that you can use to build
+Google Mock on systems where GNU make is available (e.g. Linux and Mac
+OS X).  It doesn't try to build Google Mock's own tests.  Instead, it
+just builds the Google Mock libraries and some sample tests.  You can
+use it as a starting point for your own Makefile.
+
+If the default settings are correct for your environment, the
+following commands should succeed:
+
+  cd ${SRCDIR}/make
+  make
+  ./gmock_test
+
+If you see errors, try to tweak the contents of make/Makefile to make
+them go away.  There are instructions in make/Makefile on how to do
+it.
+
+### Using Your Own Build System ###
+If none of the build solutions we provide works for you, or if you
+prefer your own build system, you just need to compile
+${GTEST_SRCDIR}/src/gtest-all.cc (where GTEST_SRCDIR is the root of
+the Google Test source tree) and src/gmock-all.cc into a library and
+link your tests with it.  Assuming a Linux-like system and gcc,
+something like the following will do:
+
+  cd ${SRCDIR}
+  g++ -I. -I./include -I${GTEST_SRCDIR} -I${GTEST_SRCDIR}/include \
+    -c {GTEST_SRCDIR}/src/gtest-all.cc
+  g++ -I. -I./include -I${GTEST_SRCDIR} -I${GTEST_SRCDIR}/include \
+    -c src/gmock-all.cc
+  ar -rv libgmock.a gtest-all.o gmock-all.o
+  g++ -I. -I./include -I${GTEST_SRCDIR} -I${GTEST_SRCDIR}/include \
+    path/to/your_test.cc libgmock.a -o your_test
+
+Regenerating Source Files
+-------------------------
+Some of Google Mock's source files are generated from templates (not
+in the C++ sense) using a script.  A template file is named FOO.pump,
+where FOO is the name of the file it will generate.  For example, the
+file include/gmock/gmock-generated-actions.h.pump is used to generate
+gmock-generated-actions.h in the same directory.
+
+Normally you don't need to worry about regenerating the source files,
+unless you need to modify them (e.g. if you are working on a patch for
+Google Mock).  In that case, you should modify the corresponding .pump
+files instead and run the 'pump' script (for Pump is Useful for Meta
+Programming) to regenerate them.  We are still working on releasing
+the script and its documentation.  If you need it now, please email
+googlemock@googlegroups.com such that we know to make it happen
+sooner.
+
+Happy testing!
diff --git a/third_party/gmock/build.scons b/third_party/gmock/build.scons
new file mode 100644
index 0000000..d75284c
--- /dev/null
+++ b/third_party/gmock/build.scons
@@ -0,0 +1,47 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========================================================================
+
+Import('env')
+
+gmock_env = env.Clone()
+gmock_env.Append(
+    CPPPATH = [
+        '$MAIN_DIR/third_party/gmock',
+        '$MAIN_DIR/third_party/gmock/include',
+        '$MAIN_DIR/third_party/gtest/include',
+        ],
+    CCFLAGS = [
+        '/wd4061',  # enumerator is not explicitly handled by a case label
+        '/wd4242',  # conversion from type1 to type2, possible loss of data
+        '/wd4244',  # conversion from type1 to type2, possible loss of data
+        '/wd4628',  # digraphs not supported with -Ze
+        '/wd4640',  # construction of local static object is not thread-safe
+        ],
+)
+
+target_name = 'gmock'
+
+gmock_inputs = [ 'src/gmock-all.cc' ]
+
+if env.Bit('use_precompiled_headers'):
+    gmock_inputs += gmock_env.EnablePrecompile(target_name)
+
+gmock_env.ComponentLibrary(
+    lib_name=target_name,
+    source=gmock_inputs,
+)
+
diff --git a/third_party/gmock/include/gmock/gmock-actions.h b/third_party/gmock/include/gmock/gmock-actions.h
new file mode 100644
index 0000000..214b291
--- /dev/null
+++ b/third_party/gmock/include/gmock/gmock-actions.h
@@ -0,0 +1,981 @@
+// Copyright 2007, 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements some commonly used actions.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
+
+#include <algorithm>
+#include <string>
+
+#ifndef _WIN32_WCE
+#include <errno.h>
+#endif
+
+#include <gmock/gmock-printers.h>
+#include <gmock/internal/gmock-internal-utils.h>
+#include <gmock/internal/gmock-port.h>
+
+namespace testing {
+
+// To implement an action Foo, define:
+//   1. a class FooAction that implements the ActionInterface interface, and
+//   2. a factory function that creates an Action object from a
+//      const FooAction*.
+//
+// The two-level delegation design follows that of Matcher, providing
+// consistency for extension developers.  It also eases ownership
+// management as Action objects can now be copied like plain values.
+
+namespace internal {
+
+template <typename F>
+class MonomorphicDoDefaultActionImpl;
+
+template <typename F1, typename F2>
+class ActionAdaptor;
+
+// BuiltInDefaultValue<T>::Get() returns the "built-in" default
+// value for type T, which is NULL when T is a pointer type, 0 when T
+// is a numeric type, false when T is bool, or "" when T is string or
+// std::string.  For any other type T, this value is undefined and the
+// function will abort the process.
+template <typename T>
+class BuiltInDefaultValue {
+ public:
+  // This function returns true iff type T has a built-in default value.
+  static bool Exists() { return false; }
+  static T Get() {
+    Assert(false, __FILE__, __LINE__,
+           "Default action undefined for the function return type.");
+    return internal::Invalid<T>();
+    // The above statement will never be reached, but is required in
+    // order for this function to compile.
+  }
+};
+
+// This partial specialization says that we use the same built-in
+// default value for T and const T.
+template <typename T>
+class BuiltInDefaultValue<const T> {
+ public:
+  static bool Exists() { return BuiltInDefaultValue<T>::Exists(); }
+  static T Get() { return BuiltInDefaultValue<T>::Get(); }
+};
+
+// This partial specialization defines the default values for pointer
+// types.
+template <typename T>
+class BuiltInDefaultValue<T*> {
+ public:
+  static bool Exists() { return true; }
+  static T* Get() { return NULL; }
+};
+
+// The following specializations define the default values for
+// specific types we care about.
+#define GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(type, value) \
+  template <> \
+  class BuiltInDefaultValue<type> { \
+   public: \
+    static bool Exists() { return true; } \
+    static type Get() { return value; } \
+  }
+
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(void, );  // NOLINT
+#if GTEST_HAS_GLOBAL_STRING
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::string, "");
+#endif  // GTEST_HAS_GLOBAL_STRING
+#if GTEST_HAS_STD_STRING
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::std::string, "");
+#endif  // GTEST_HAS_STD_STRING
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(bool, false);
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned char, '\0');
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed char, '\0');
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(char, '\0');
+
+// There's no need for a default action for signed wchar_t, as that
+// type is the same as wchar_t for gcc, and invalid for MSVC.
+//
+// There's also no need for a default action for unsigned wchar_t, as
+// that type is the same as unsigned int for gcc, and invalid for
+// MSVC.
+#if GMOCK_WCHAR_T_IS_NATIVE_
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(wchar_t, 0U);  // NOLINT
+#endif
+
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned short, 0U);  // NOLINT
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed short, 0);     // NOLINT
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned int, 0U);
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed int, 0);
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned long, 0UL);  // NOLINT
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed long, 0L);     // NOLINT
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(UInt64, 0);
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(Int64, 0);
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(float, 0);
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(double, 0);
+
+#undef GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_
+
+}  // namespace internal
+
+// When an unexpected function call is encountered, Google Mock will
+// let it return a default value if the user has specified one for its
+// return type, or if the return type has a built-in default value;
+// otherwise Google Mock won't know what value to return and will have
+// to abort the process.
+//
+// The DefaultValue<T> class allows a user to specify the
+// default value for a type T that is both copyable and publicly
+// destructible (i.e. anything that can be used as a function return
+// type).  The usage is:
+//
+//   // Sets the default value for type T to be foo.
+//   DefaultValue<T>::Set(foo);
+template <typename T>
+class DefaultValue {
+ public:
+  // Sets the default value for type T; requires T to be
+  // copy-constructable and have a public destructor.
+  static void Set(T x) {
+    delete value_;
+    value_ = new T(x);
+  }
+
+  // Unsets the default value for type T.
+  static void Clear() {
+    delete value_;
+    value_ = NULL;
+  }
+
+  // Returns true iff the user has set the default value for type T.
+  static bool IsSet() { return value_ != NULL; }
+
+  // Returns true if T has a default return value set by the user or there
+  // exists a built-in default value.
+  static bool Exists() {
+    return IsSet() || internal::BuiltInDefaultValue<T>::Exists();
+  }
+
+  // Returns the default value for type T if the user has set one;
+  // otherwise returns the built-in default value if there is one;
+  // otherwise aborts the process.
+  static T Get() {
+    return value_ == NULL ?
+        internal::BuiltInDefaultValue<T>::Get() : *value_;
+  }
+ private:
+  static const T* value_;
+};
+
+// This partial specialization allows a user to set default values for
+// reference types.
+template <typename T>
+class DefaultValue<T&> {
+ public:
+  // Sets the default value for type T&.
+  static void Set(T& x) {  // NOLINT
+    address_ = &x;
+  }
+
+  // Unsets the default value for type T&.
+  static void Clear() {
+    address_ = NULL;
+  }
+
+  // Returns true iff the user has set the default value for type T&.
+  static bool IsSet() { return address_ != NULL; }
+
+  // Returns true if T has a default return value set by the user or there
+  // exists a built-in default value.
+  static bool Exists() {
+    return IsSet() || internal::BuiltInDefaultValue<T&>::Exists();
+  }
+
+  // Returns the default value for type T& if the user has set one;
+  // otherwise returns the built-in default value if there is one;
+  // otherwise aborts the process.
+  static T& Get() {
+    return address_ == NULL ?
+        internal::BuiltInDefaultValue<T&>::Get() : *address_;
+  }
+ private:
+  static T* address_;
+};
+
+// This specialization allows DefaultValue<void>::Get() to
+// compile.
+template <>
+class DefaultValue<void> {
+ public:
+  static bool Exists() { return true; }
+  static void Get() {}
+};
+
+// Points to the user-set default value for type T.
+template <typename T>
+const T* DefaultValue<T>::value_ = NULL;
+
+// Points to the user-set default value for type T&.
+template <typename T>
+T* DefaultValue<T&>::address_ = NULL;
+
+// Implement this interface to define an action for function type F.
+template <typename F>
+class ActionInterface {
+ public:
+  typedef typename internal::Function<F>::Result Result;
+  typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+  ActionInterface() : is_do_default_(false) {}
+
+  virtual ~ActionInterface() {}
+
+  // Performs the action.  This method is not const, as in general an
+  // action can have side effects and be stateful.  For example, a
+  // get-the-next-element-from-the-collection action will need to
+  // remember the current element.
+  virtual Result Perform(const ArgumentTuple& args) = 0;
+
+  // Returns true iff this is the DoDefault() action.
+  bool IsDoDefault() const { return is_do_default_; }
+ private:
+  template <typename Function>
+  friend class internal::MonomorphicDoDefaultActionImpl;
+
+  // This private constructor is reserved for implementing
+  // DoDefault(), the default action for a given mock function.
+  explicit ActionInterface(bool is_do_default)
+      : is_do_default_(is_do_default) {}
+
+  // True iff this action is DoDefault().
+  const bool is_do_default_;
+};
+
+// An Action<F> is a copyable and IMMUTABLE (except by assignment)
+// object that represents an action to be taken when a mock function
+// of type F is called.  The implementation of Action<T> is just a
+// linked_ptr to const ActionInterface<T>, so copying is fairly cheap.
+// Don't inherit from Action!
+//
+// You can view an object implementing ActionInterface<F> as a
+// concrete action (including its current state), and an Action<F>
+// object as a handle to it.
+template <typename F>
+class Action {
+ public:
+  typedef typename internal::Function<F>::Result Result;
+  typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+  // Constructs a null Action.  Needed for storing Action objects in
+  // STL containers.
+  Action() : impl_(NULL) {}
+
+  // Constructs an Action from its implementation.
+  explicit Action(ActionInterface<F>* impl) : impl_(impl) {}
+
+  // Copy constructor.
+  Action(const Action& action) : impl_(action.impl_) {}
+
+  // This constructor allows us to turn an Action<Func> object into an
+  // Action<F>, as long as F's arguments can be implicitly converted
+  // to Func's and Func's return type can be implicitly converted to
+  // F's.
+  template <typename Func>
+  explicit Action(const Action<Func>& action);
+
+  // Returns true iff this is the DoDefault() action.
+  bool IsDoDefault() const { return impl_->IsDoDefault(); }
+
+  // Performs the action.  Note that this method is const even though
+  // the corresponding method in ActionInterface is not.  The reason
+  // is that a const Action<F> means that it cannot be re-bound to
+  // another concrete action, not that the concrete action it binds to
+  // cannot change state.  (Think of the difference between a const
+  // pointer and a pointer to const.)
+  Result Perform(const ArgumentTuple& args) const {
+    return impl_->Perform(args);
+  }
+ private:
+  template <typename F1, typename F2>
+  friend class internal::ActionAdaptor;
+
+  internal::linked_ptr<ActionInterface<F> > impl_;
+};
+
+// The PolymorphicAction class template makes it easy to implement a
+// polymorphic action (i.e. an action that can be used in mock
+// functions of than one type, e.g. Return()).
+//
+// To define a polymorphic action, a user first provides a COPYABLE
+// implementation class that has a Perform() method template:
+//
+//   class FooAction {
+//    public:
+//     template <typename Result, typename ArgumentTuple>
+//     Result Perform(const ArgumentTuple& args) const {
+//       // Processes the arguments and returns a result, using
+//       // tr1::get<N>(args) to get the N-th (0-based) argument in the tuple.
+//     }
+//     ...
+//   };
+//
+// Then the user creates the polymorphic action using
+// MakePolymorphicAction(object) where object has type FooAction.  See
+// the definition of Return(void) and SetArgumentPointee<N>(value) for
+// complete examples.
+template <typename Impl>
+class PolymorphicAction {
+ public:
+  explicit PolymorphicAction(const Impl& impl) : impl_(impl) {}
+
+  template <typename F>
+  operator Action<F>() const {
+    return Action<F>(new MonomorphicImpl<F>(impl_));
+  }
+ private:
+  template <typename F>
+  class MonomorphicImpl : public ActionInterface<F> {
+   public:
+    typedef typename internal::Function<F>::Result Result;
+    typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+    explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {}
+
+    virtual Result Perform(const ArgumentTuple& args) {
+      return impl_.template Perform<Result>(args);
+    }
+
+   private:
+    Impl impl_;
+  };
+
+  Impl impl_;
+};
+
+// Creates an Action from its implementation and returns it.  The
+// created Action object owns the implementation.
+template <typename F>
+Action<F> MakeAction(ActionInterface<F>* impl) {
+  return Action<F>(impl);
+}
+
+// Creates a polymorphic action from its implementation.  This is
+// easier to use than the PolymorphicAction<Impl> constructor as it
+// doesn't require you to explicitly write the template argument, e.g.
+//
+//   MakePolymorphicAction(foo);
+// vs
+//   PolymorphicAction<TypeOfFoo>(foo);
+template <typename Impl>
+inline PolymorphicAction<Impl> MakePolymorphicAction(const Impl& impl) {
+  return PolymorphicAction<Impl>(impl);
+}
+
+namespace internal {
+
+// Allows an Action<F2> object to pose as an Action<F1>, as long as F2
+// and F1 are compatible.
+template <typename F1, typename F2>
+class ActionAdaptor : public ActionInterface<F1> {
+ public:
+  typedef typename internal::Function<F1>::Result Result;
+  typedef typename internal::Function<F1>::ArgumentTuple ArgumentTuple;
+
+  explicit ActionAdaptor(const Action<F2>& from) : impl_(from.impl_) {}
+
+  virtual Result Perform(const ArgumentTuple& args) {
+    return impl_->Perform(args);
+  }
+ private:
+  const internal::linked_ptr<ActionInterface<F2> > impl_;
+};
+
+// Implements the polymorphic Return(x) action, which can be used in
+// any function that returns the type of x, regardless of the argument
+// types.
+//
+// Note: The value passed into Return must be converted into
+// Function<F>::Result when this action is cast to Action<F> rather than
+// when that action is performed. This is important in scenarios like
+//
+// MOCK_METHOD1(Method, T(U));
+// ...
+// {
+//   Foo foo;
+//   X x(&foo);
+//   EXPECT_CALL(mock, Method(_)).WillOnce(Return(x));
+// }
+//
+// In the example above the variable x holds reference to foo which leaves
+// scope and gets destroyed.  If copying X just copies a reference to foo,
+// that copy will be left with a hanging reference.  If conversion to T
+// makes a copy of foo, the above code is safe. To support that scenario, we
+// need to make sure that the type conversion happens inside the EXPECT_CALL
+// statement, and conversion of the result of Return to Action<T(U)> is a
+// good place for that.
+//
+template <typename R>
+class ReturnAction {
+ public:
+  // Constructs a ReturnAction object from the value to be returned.
+  // 'value' is passed by value instead of by const reference in order
+  // to allow Return("string literal") to compile.
+  explicit ReturnAction(R value) : value_(value) {}
+
+  // This template type conversion operator allows Return(x) to be
+  // used in ANY function that returns x's type.
+  template <typename F>
+  operator Action<F>() const {
+    // Assert statement belongs here because this is the best place to verify
+    // conditions on F. It produces the clearest error messages
+    // in most compilers.
+    // Impl really belongs in this scope as a local class but can't
+    // because MSVC produces duplicate symbols in different translation units
+    // in this case. Until MS fixes that bug we put Impl into the class scope
+    // and put the typedef both here (for use in assert statement) and
+    // in the Impl class. But both definitions must be the same.
+    typedef typename Function<F>::Result Result;
+    GMOCK_COMPILE_ASSERT_(
+        !internal::is_reference<Result>::value,
+        use_ReturnRef_instead_of_Return_to_return_a_reference);
+    return Action<F>(new Impl<F>(value_));
+  }
+ private:
+  // Implements the Return(x) action for a particular function type F.
+  template <typename F>
+  class Impl : public ActionInterface<F> {
+   public:
+    typedef typename Function<F>::Result Result;
+    typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+
+    // The implicit cast is necessary when Result has more than one
+    // single-argument constructor (e.g. Result is std::vector<int>) and R
+    // has a type conversion operator template.  In that case, value_(value)
+    // won't compile as the compiler doesn't known which constructor of
+    // Result to call.  implicit_cast forces the compiler to convert R to
+    // Result without considering explicit constructors, thus resolving the
+    // ambiguity. value_ is then initialized using its copy constructor.
+    explicit Impl(R value)
+        : value_(::testing::internal::implicit_cast<Result>(value)) {}
+
+    virtual Result Perform(const ArgumentTuple&) { return value_; }
+
+   private:
+    GMOCK_COMPILE_ASSERT_(!internal::is_reference<Result>::value,
+                          Result_cannot_be_a_reference_type);
+    Result value_;
+  };
+
+  R value_;
+};
+
+// Implements the ReturnNull() action.
+class ReturnNullAction {
+ public:
+  // Allows ReturnNull() to be used in any pointer-returning function.
+  template <typename Result, typename ArgumentTuple>
+  static Result Perform(const ArgumentTuple&) {
+    GMOCK_COMPILE_ASSERT_(internal::is_pointer<Result>::value,
+                          ReturnNull_can_be_used_to_return_a_pointer_only);
+    return NULL;
+  }
+};
+
+// Implements the Return() action.
+class ReturnVoidAction {
+ public:
+  // Allows Return() to be used in any void-returning function.
+  template <typename Result, typename ArgumentTuple>
+  static void Perform(const ArgumentTuple&) {
+    CompileAssertTypesEqual<void, Result>();
+  }
+};
+
+// Implements the polymorphic ReturnRef(x) action, which can be used
+// in any function that returns a reference to the type of x,
+// regardless of the argument types.
+template <typename T>
+class ReturnRefAction {
+ public:
+  // Constructs a ReturnRefAction object from the reference to be returned.
+  explicit ReturnRefAction(T& ref) : ref_(ref) {}  // NOLINT
+
+  // This template type conversion operator allows ReturnRef(x) to be
+  // used in ANY function that returns a reference to x's type.
+  template <typename F>
+  operator Action<F>() const {
+    typedef typename Function<F>::Result Result;
+    // Asserts that the function return type is a reference.  This
+    // catches the user error of using ReturnRef(x) when Return(x)
+    // should be used, and generates some helpful error message.
+    GMOCK_COMPILE_ASSERT_(internal::is_reference<Result>::value,
+                          use_Return_instead_of_ReturnRef_to_return_a_value);
+    return Action<F>(new Impl<F>(ref_));
+  }
+ private:
+  // Implements the ReturnRef(x) action for a particular function type F.
+  template <typename F>
+  class Impl : public ActionInterface<F> {
+   public:
+    typedef typename Function<F>::Result Result;
+    typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+
+    explicit Impl(T& ref) : ref_(ref) {}  // NOLINT
+
+    virtual Result Perform(const ArgumentTuple&) {
+      return ref_;
+    }
+   private:
+    T& ref_;
+  };
+
+  T& ref_;
+};
+
+// Implements the DoDefault() action for a particular function type F.
+template <typename F>
+class MonomorphicDoDefaultActionImpl : public ActionInterface<F> {
+ public:
+  typedef typename Function<F>::Result Result;
+  typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+
+  MonomorphicDoDefaultActionImpl() : ActionInterface<F>(true) {}
+
+  // For technical reasons, DoDefault() cannot be used inside a
+  // composite action (e.g. DoAll(...)).  It can only be used at the
+  // top level in an EXPECT_CALL().  If this function is called, the
+  // user must be using DoDefault() inside a composite action, and we
+  // have to generate a run-time error.
+  virtual Result Perform(const ArgumentTuple&) {
+    Assert(false, __FILE__, __LINE__,
+           "You are using DoDefault() inside a composite action like "
+           "DoAll() or WithArgs().  This is not supported for technical "
+           "reasons.  Please instead spell out the default action, or "
+           "assign the default action to an Action variable and use "
+           "the variable in various places.");
+    return internal::Invalid<Result>();
+    // The above statement will never be reached, but is required in
+    // order for this function to compile.
+  }
+};
+
+// Implements the polymorphic DoDefault() action.
+class DoDefaultAction {
+ public:
+  // This template type conversion operator allows DoDefault() to be
+  // used in any function.
+  template <typename F>
+  operator Action<F>() const {
+    return Action<F>(new MonomorphicDoDefaultActionImpl<F>);
+  }
+};
+
+// Implements the Assign action to set a given pointer referent to a
+// particular value.
+template <typename T1, typename T2>
+class AssignAction {
+ public:
+  AssignAction(T1* ptr, T2 value) : ptr_(ptr), value_(value) {}
+
+  template <typename Result, typename ArgumentTuple>
+  void Perform(const ArgumentTuple& /* args */) const {
+    *ptr_ = value_;
+  }
+ private:
+  T1* const ptr_;
+  const T2 value_;
+};
+
+#if !GTEST_OS_WINDOWS_MOBILE
+
+// Implements the SetErrnoAndReturn action to simulate return from
+// various system calls and libc functions.
+template <typename T>
+class SetErrnoAndReturnAction {
+ public:
+  SetErrnoAndReturnAction(int errno_value, T result)
+      : errno_(errno_value),
+        result_(result) {}
+  template <typename Result, typename ArgumentTuple>
+  Result Perform(const ArgumentTuple& /* args */) const {
+    errno = errno_;
+    return result_;
+  }
+ private:
+  const int errno_;
+  const T result_;
+};
+
+#endif  // !GTEST_OS_WINDOWS_MOBILE
+
+// Implements the SetArgumentPointee<N>(x) action for any function
+// whose N-th argument (0-based) is a pointer to x's type.  The
+// template parameter kIsProto is true iff type A is ProtocolMessage,
+// proto2::Message, or a sub-class of those.
+template <size_t N, typename A, bool kIsProto>
+class SetArgumentPointeeAction {
+ public:
+  // Constructs an action that sets the variable pointed to by the
+  // N-th function argument to 'value'.
+  explicit SetArgumentPointeeAction(const A& value) : value_(value) {}
+
+  template <typename Result, typename ArgumentTuple>
+  void Perform(const ArgumentTuple& args) const {
+    CompileAssertTypesEqual<void, Result>();
+    *::std::tr1::get<N>(args) = value_;
+  }
+
+ private:
+  const A value_;
+};
+
+template <size_t N, typename Proto>
+class SetArgumentPointeeAction<N, Proto, true> {
+ public:
+  // Constructs an action that sets the variable pointed to by the
+  // N-th function argument to 'proto'.  Both ProtocolMessage and
+  // proto2::Message have the CopyFrom() method, so the same
+  // implementation works for both.
+  explicit SetArgumentPointeeAction(const Proto& proto) : proto_(new Proto) {
+    proto_->CopyFrom(proto);
+  }
+
+  template <typename Result, typename ArgumentTuple>
+  void Perform(const ArgumentTuple& args) const {
+    CompileAssertTypesEqual<void, Result>();
+    ::std::tr1::get<N>(args)->CopyFrom(*proto_);
+  }
+ private:
+  const internal::linked_ptr<Proto> proto_;
+};
+
+// Implements the InvokeWithoutArgs(f) action.  The template argument
+// FunctionImpl is the implementation type of f, which can be either a
+// function pointer or a functor.  InvokeWithoutArgs(f) can be used as an
+// Action<F> as long as f's type is compatible with F (i.e. f can be
+// assigned to a tr1::function<F>).
+template <typename FunctionImpl>
+class InvokeWithoutArgsAction {
+ public:
+  // The c'tor makes a copy of function_impl (either a function
+  // pointer or a functor).
+  explicit InvokeWithoutArgsAction(FunctionImpl function_impl)
+      : function_impl_(function_impl) {}
+
+  // Allows InvokeWithoutArgs(f) to be used as any action whose type is
+  // compatible with f.
+  template <typename Result, typename ArgumentTuple>
+  Result Perform(const ArgumentTuple&) { return function_impl_(); }
+ private:
+  FunctionImpl function_impl_;
+};
+
+// Implements the InvokeWithoutArgs(object_ptr, &Class::Method) action.
+template <class Class, typename MethodPtr>
+class InvokeMethodWithoutArgsAction {
+ public:
+  InvokeMethodWithoutArgsAction(Class* obj_ptr, MethodPtr method_ptr)
+      : obj_ptr_(obj_ptr), method_ptr_(method_ptr) {}
+
+  template <typename Result, typename ArgumentTuple>
+  Result Perform(const ArgumentTuple&) const {
+    return (obj_ptr_->*method_ptr_)();
+  }
+ private:
+  Class* const obj_ptr_;
+  const MethodPtr method_ptr_;
+};
+
+// Implements the IgnoreResult(action) action.
+template <typename A>
+class IgnoreResultAction {
+ public:
+  explicit IgnoreResultAction(const A& action) : action_(action) {}
+
+  template <typename F>
+  operator Action<F>() const {
+    // Assert statement belongs here because this is the best place to verify
+    // conditions on F. It produces the clearest error messages
+    // in most compilers.
+    // Impl really belongs in this scope as a local class but can't
+    // because MSVC produces duplicate symbols in different translation units
+    // in this case. Until MS fixes that bug we put Impl into the class scope
+    // and put the typedef both here (for use in assert statement) and
+    // in the Impl class. But both definitions must be the same.
+    typedef typename internal::Function<F>::Result Result;
+
+    // Asserts at compile time that F returns void.
+    CompileAssertTypesEqual<void, Result>();
+
+    return Action<F>(new Impl<F>(action_));
+  }
+ private:
+  template <typename F>
+  class Impl : public ActionInterface<F> {
+   public:
+    typedef typename internal::Function<F>::Result Result;
+    typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+    explicit Impl(const A& action) : action_(action) {}
+
+    virtual void Perform(const ArgumentTuple& args) {
+      // Performs the action and ignores its result.
+      action_.Perform(args);
+    }
+
+   private:
+    // Type OriginalFunction is the same as F except that its return
+    // type is IgnoredValue.
+    typedef typename internal::Function<F>::MakeResultIgnoredValue
+        OriginalFunction;
+
+    const Action<OriginalFunction> action_;
+  };
+
+  const A action_;
+};
+
+// A ReferenceWrapper<T> object represents a reference to type T,
+// which can be either const or not.  It can be explicitly converted
+// from, and implicitly converted to, a T&.  Unlike a reference,
+// ReferenceWrapper<T> can be copied and can survive template type
+// inference.  This is used to support by-reference arguments in the
+// InvokeArgument<N>(...) action.  The idea was from "reference
+// wrappers" in tr1, which we don't have in our source tree yet.
+template <typename T>
+class ReferenceWrapper {
+ public:
+  // Constructs a ReferenceWrapper<T> object from a T&.
+  explicit ReferenceWrapper(T& l_value) : pointer_(&l_value) {}  // NOLINT
+
+  // Allows a ReferenceWrapper<T> object to be implicitly converted to
+  // a T&.
+  operator T&() const { return *pointer_; }
+ private:
+  T* pointer_;
+};
+
+// Allows the expression ByRef(x) to be printed as a reference to x.
+template <typename T>
+void PrintTo(const ReferenceWrapper<T>& ref, ::std::ostream* os) {
+  T& value = ref;
+  UniversalPrinter<T&>::Print(value, os);
+}
+
+// Does two actions sequentially.  Used for implementing the DoAll(a1,
+// a2, ...) action.
+template <typename Action1, typename Action2>
+class DoBothAction {
+ public:
+  DoBothAction(Action1 action1, Action2 action2)
+      : action1_(action1), action2_(action2) {}
+
+  // This template type conversion operator allows DoAll(a1, ..., a_n)
+  // to be used in ANY function of compatible type.
+  template <typename F>
+  operator Action<F>() const {
+    return Action<F>(new Impl<F>(action1_, action2_));
+  }
+
+ private:
+  // Implements the DoAll(...) action for a particular function type F.
+  template <typename F>
+  class Impl : public ActionInterface<F> {
+   public:
+    typedef typename Function<F>::Result Result;
+    typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+    typedef typename Function<F>::MakeResultVoid VoidResult;
+
+    Impl(const Action<VoidResult>& action1, const Action<F>& action2)
+        : action1_(action1), action2_(action2) {}
+
+    virtual Result Perform(const ArgumentTuple& args) {
+      action1_.Perform(args);
+      return action2_.Perform(args);
+    }
+
+   private:
+    const Action<VoidResult> action1_;
+    const Action<F> action2_;
+  };
+
+  Action1 action1_;
+  Action2 action2_;
+};
+
+}  // namespace internal
+
+// An Unused object can be implicitly constructed from ANY value.
+// This is handy when defining actions that ignore some or all of the
+// mock function arguments.  For example, given
+//
+//   MOCK_METHOD3(Foo, double(const string& label, double x, double y));
+//   MOCK_METHOD3(Bar, double(int index, double x, double y));
+//
+// instead of
+//
+//   double DistanceToOriginWithLabel(const string& label, double x, double y) {
+//     return sqrt(x*x + y*y);
+//   }
+//   double DistanceToOriginWithIndex(int index, double x, double y) {
+//     return sqrt(x*x + y*y);
+//   }
+//   ...
+//   EXEPCT_CALL(mock, Foo("abc", _, _))
+//       .WillOnce(Invoke(DistanceToOriginWithLabel));
+//   EXEPCT_CALL(mock, Bar(5, _, _))
+//       .WillOnce(Invoke(DistanceToOriginWithIndex));
+//
+// you could write
+//
+//   // We can declare any uninteresting argument as Unused.
+//   double DistanceToOrigin(Unused, double x, double y) {
+//     return sqrt(x*x + y*y);
+//   }
+//   ...
+//   EXEPCT_CALL(mock, Foo("abc", _, _)).WillOnce(Invoke(DistanceToOrigin));
+//   EXEPCT_CALL(mock, Bar(5, _, _)).WillOnce(Invoke(DistanceToOrigin));
+typedef internal::IgnoredValue Unused;
+
+// This constructor allows us to turn an Action<From> object into an
+// Action<To>, as long as To's arguments can be implicitly converted
+// to From's and From's return type cann be implicitly converted to
+// To's.
+template <typename To>
+template <typename From>
+Action<To>::Action(const Action<From>& from)
+    : impl_(new internal::ActionAdaptor<To, From>(from)) {}
+
+// Creates an action that returns 'value'.  'value' is passed by value
+// instead of const reference - otherwise Return("string literal")
+// will trigger a compiler error about using array as initializer.
+template <typename R>
+internal::ReturnAction<R> Return(R value) {
+  return internal::ReturnAction<R>(value);
+}
+
+// Creates an action that returns NULL.
+inline PolymorphicAction<internal::ReturnNullAction> ReturnNull() {
+  return MakePolymorphicAction(internal::ReturnNullAction());
+}
+
+// Creates an action that returns from a void function.
+inline PolymorphicAction<internal::ReturnVoidAction> Return() {
+  return MakePolymorphicAction(internal::ReturnVoidAction());
+}
+
+// Creates an action that returns the reference to a variable.
+template <typename R>
+inline internal::ReturnRefAction<R> ReturnRef(R& x) {  // NOLINT
+  return internal::ReturnRefAction<R>(x);
+}
+
+// Creates an action that does the default action for the give mock function.
+inline internal::DoDefaultAction DoDefault() {
+  return internal::DoDefaultAction();
+}
+
+// Creates an action that sets the variable pointed by the N-th
+// (0-based) function argument to 'value'.
+template <size_t N, typename T>
+PolymorphicAction<
+  internal::SetArgumentPointeeAction<
+    N, T, internal::IsAProtocolMessage<T>::value> >
+SetArgumentPointee(const T& x) {
+  return MakePolymorphicAction(internal::SetArgumentPointeeAction<
+      N, T, internal::IsAProtocolMessage<T>::value>(x));
+}
+
+// Creates an action that sets a pointer referent to a given value.
+template <typename T1, typename T2>
+PolymorphicAction<internal::AssignAction<T1, T2> > Assign(T1* ptr, T2 val) {
+  return MakePolymorphicAction(internal::AssignAction<T1, T2>(ptr, val));
+}
+
+#if !GTEST_OS_WINDOWS_MOBILE
+
+// Creates an action that sets errno and returns the appropriate error.
+template <typename T>
+PolymorphicAction<internal::SetErrnoAndReturnAction<T> >
+SetErrnoAndReturn(int errval, T result) {
+  return MakePolymorphicAction(
+      internal::SetErrnoAndReturnAction<T>(errval, result));
+}
+
+#endif  // !GTEST_OS_WINDOWS_MOBILE
+
+// Various overloads for InvokeWithoutArgs().
+
+// Creates an action that invokes 'function_impl' with no argument.
+template <typename FunctionImpl>
+PolymorphicAction<internal::InvokeWithoutArgsAction<FunctionImpl> >
+InvokeWithoutArgs(FunctionImpl function_impl) {
+  return MakePolymorphicAction(
+      internal::InvokeWithoutArgsAction<FunctionImpl>(function_impl));
+}
+
+// Creates an action that invokes the given method on the given object
+// with no argument.
+template <class Class, typename MethodPtr>
+PolymorphicAction<internal::InvokeMethodWithoutArgsAction<Class, MethodPtr> >
+InvokeWithoutArgs(Class* obj_ptr, MethodPtr method_ptr) {
+  return MakePolymorphicAction(
+      internal::InvokeMethodWithoutArgsAction<Class, MethodPtr>(
+          obj_ptr, method_ptr));
+}
+
+// Creates an action that performs an_action and throws away its
+// result.  In other words, it changes the return type of an_action to
+// void.  an_action MUST NOT return void, or the code won't compile.
+template <typename A>
+inline internal::IgnoreResultAction<A> IgnoreResult(const A& an_action) {
+  return internal::IgnoreResultAction<A>(an_action);
+}
+
+// Creates a reference wrapper for the given L-value.  If necessary,
+// you can explicitly specify the type of the reference.  For example,
+// suppose 'derived' is an object of type Derived, ByRef(derived)
+// would wrap a Derived&.  If you want to wrap a const Base& instead,
+// where Base is a base class of Derived, just write:
+//
+//   ByRef<const Base>(derived)
+template <typename T>
+inline internal::ReferenceWrapper<T> ByRef(T& l_value) {  // NOLINT
+  return internal::ReferenceWrapper<T>(l_value);
+}
+
+}  // namespace testing
+
+#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
diff --git a/third_party/gmock/include/gmock/gmock-cardinalities.h b/third_party/gmock/include/gmock/gmock-cardinalities.h
new file mode 100644
index 0000000..ae4cb64
--- /dev/null
+++ b/third_party/gmock/include/gmock/gmock-cardinalities.h
@@ -0,0 +1,146 @@
+// Copyright 2007, 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements some commonly used cardinalities.  More
+// cardinalities can be defined by the user implementing the
+// CardinalityInterface interface if necessary.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
+
+#include <limits.h>
+#include <ostream>  // NOLINT
+#include <gmock/internal/gmock-port.h>
+#include <gtest/gtest.h>
+
+namespace testing {
+
+// To implement a cardinality Foo, define:
+//   1. a class FooCardinality that implements the
+//      CardinalityInterface interface, and
+//   2. a factory function that creates a Cardinality object from a
+//      const FooCardinality*.
+//
+// The two-level delegation design follows that of Matcher, providing
+// consistency for extension developers.  It also eases ownership
+// management as Cardinality objects can now be copied like plain values.
+
+// The implementation of a cardinality.
+class CardinalityInterface {
+ public:
+  virtual ~CardinalityInterface() {}
+
+  // Conservative estimate on the lower/upper bound of the number of
+  // calls allowed.
+  virtual int ConservativeLowerBound() const { return 0; }
+  virtual int ConservativeUpperBound() const { return INT_MAX; }
+
+  // Returns true iff call_count calls will satisfy this cardinality.
+  virtual bool IsSatisfiedByCallCount(int call_count) const = 0;
+
+  // Returns true iff call_count calls will saturate this cardinality.
+  virtual bool IsSaturatedByCallCount(int call_count) const = 0;
+
+  // Describes self to an ostream.
+  virtual void DescribeTo(::std::ostream* os) const = 0;
+};
+
+// A Cardinality is a copyable and IMMUTABLE (except by assignment)
+// object that specifies how many times a mock function is expected to
+// be called.  The implementation of Cardinality is just a linked_ptr
+// to const CardinalityInterface, so copying is fairly cheap.
+// Don't inherit from Cardinality!
+class Cardinality {
+ public:
+  // Constructs a null cardinality.  Needed for storing Cardinality
+  // objects in STL containers.
+  Cardinality() {}
+
+  // Constructs a Cardinality from its implementation.
+  explicit Cardinality(const CardinalityInterface* impl) : impl_(impl) {}
+
+  // Conservative estimate on the lower/upper bound of the number of
+  // calls allowed.
+  int ConservativeLowerBound() const { return impl_->ConservativeLowerBound(); }
+  int ConservativeUpperBound() const { return impl_->ConservativeUpperBound(); }
+
+  // Returns true iff call_count calls will satisfy this cardinality.
+  bool IsSatisfiedByCallCount(int call_count) const {
+    return impl_->IsSatisfiedByCallCount(call_count);
+  }
+
+  // Returns true iff call_count calls will saturate this cardinality.
+  bool IsSaturatedByCallCount(int call_count) const {
+    return impl_->IsSaturatedByCallCount(call_count);
+  }
+
+  // Returns true iff call_count calls will over-saturate this
+  // cardinality, i.e. exceed the maximum number of allowed calls.
+  bool IsOverSaturatedByCallCount(int call_count) const {
+    return impl_->IsSaturatedByCallCount(call_count) &&
+        !impl_->IsSatisfiedByCallCount(call_count);
+  }
+
+  // Describes self to an ostream
+  void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); }
+
+  // Describes the given actual call count to an ostream.
+  static void DescribeActualCallCountTo(int actual_call_count,
+                                        ::std::ostream* os);
+ private:
+  internal::linked_ptr<const CardinalityInterface> impl_;
+};
+
+// Creates a cardinality that allows at least n calls.
+Cardinality AtLeast(int n);
+
+// Creates a cardinality that allows at most n calls.
+Cardinality AtMost(int n);
+
+// Creates a cardinality that allows any number of calls.
+Cardinality AnyNumber();
+
+// Creates a cardinality that allows between min and max calls.
+Cardinality Between(int min, int max);
+
+// Creates a cardinality that allows exactly n calls.
+Cardinality Exactly(int n);
+
+// Creates a cardinality from its implementation.
+inline Cardinality MakeCardinality(const CardinalityInterface* c) {
+  return Cardinality(c);
+}
+
+}  // namespace testing
+
+#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
diff --git a/third_party/gmock/include/gmock/gmock-generated-actions.h b/third_party/gmock/include/gmock/gmock-generated-actions.h
new file mode 100644
index 0000000..143a99b
--- /dev/null
+++ b/third_party/gmock/include/gmock/gmock-generated-actions.h
@@ -0,0 +1,2355 @@
+// This file was GENERATED by a script.  DO NOT EDIT BY HAND!!!
+
+// Copyright 2007, 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements some commonly used variadic actions.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_
+
+#include <gmock/gmock-actions.h>
+#include <gmock/internal/gmock-port.h>
+
+namespace testing {
+namespace internal {
+
+// InvokeHelper<F> knows how to unpack an N-tuple and invoke an N-ary
+// function or method with the unpacked values, where F is a function
+// type that takes N arguments.
+template <typename Result, typename ArgumentTuple>
+class InvokeHelper;
+
+template <typename R>
+class InvokeHelper<R, ::std::tr1::tuple<> > {
+ public:
+  template <typename Function>
+  static R Invoke(Function function, const ::std::tr1::tuple<>&) {
+    return function();
+  }
+
+  template <class Class, typename MethodPtr>
+  static R InvokeMethod(Class* obj_ptr,
+                        MethodPtr method_ptr,
+                        const ::std::tr1::tuple<>&) {
+    return (obj_ptr->*method_ptr)();
+  }
+};
+
+template <typename R, typename A1>
+class InvokeHelper<R, ::std::tr1::tuple<A1> > {
+ public:
+  template <typename Function>
+  static R Invoke(Function function, const ::std::tr1::tuple<A1>& args) {
+    using ::std::tr1::get;
+    return function(get<0>(args));
+  }
+
+  template <class Class, typename MethodPtr>
+  static R InvokeMethod(Class* obj_ptr,
+                        MethodPtr method_ptr,
+                        const ::std::tr1::tuple<A1>& args) {
+    using ::std::tr1::get;
+    return (obj_ptr->*method_ptr)(get<0>(args));
+  }
+};
+
+template <typename R, typename A1, typename A2>
+class InvokeHelper<R, ::std::tr1::tuple<A1, A2> > {
+ public:
+  template <typename Function>
+  static R Invoke(Function function, const ::std::tr1::tuple<A1, A2>& args) {
+    using ::std::tr1::get;
+    return function(get<0>(args), get<1>(args));
+  }
+
+  template <class Class, typename MethodPtr>
+  static R InvokeMethod(Class* obj_ptr,
+                        MethodPtr method_ptr,
+                        const ::std::tr1::tuple<A1, A2>& args) {
+    using ::std::tr1::get;
+    return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args));
+  }
+};
+
+template <typename R, typename A1, typename A2, typename A3>
+class InvokeHelper<R, ::std::tr1::tuple<A1, A2, A3> > {
+ public:
+  template <typename Function>
+  static R Invoke(Function function, const ::std::tr1::tuple<A1, A2,
+      A3>& args) {
+    using ::std::tr1::get;
+    return function(get<0>(args), get<1>(args), get<2>(args));
+  }
+
+  template <class Class, typename MethodPtr>
+  static R InvokeMethod(Class* obj_ptr,
+                        MethodPtr method_ptr,
+                        const ::std::tr1::tuple<A1, A2, A3>& args) {
+    using ::std::tr1::get;
+    return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args));
+  }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+class InvokeHelper<R, ::std::tr1::tuple<A1, A2, A3, A4> > {
+ public:
+  template <typename Function>
+  static R Invoke(Function function, const ::std::tr1::tuple<A1, A2, A3,
+      A4>& args) {
+    using ::std::tr1::get;
+    return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args));
+  }
+
+  template <class Class, typename MethodPtr>
+  static R InvokeMethod(Class* obj_ptr,
+                        MethodPtr method_ptr,
+                        const ::std::tr1::tuple<A1, A2, A3, A4>& args) {
+    using ::std::tr1::get;
+    return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args),
+        get<3>(args));
+  }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5>
+class InvokeHelper<R, ::std::tr1::tuple<A1, A2, A3, A4, A5> > {
+ public:
+  template <typename Function>
+  static R Invoke(Function function, const ::std::tr1::tuple<A1, A2, A3, A4,
+      A5>& args) {
+    using ::std::tr1::get;
+    return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args),
+        get<4>(args));
+  }
+
+  template <class Class, typename MethodPtr>
+  static R InvokeMethod(Class* obj_ptr,
+                        MethodPtr method_ptr,
+                        const ::std::tr1::tuple<A1, A2, A3, A4, A5>& args) {
+    using ::std::tr1::get;
+    return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args),
+        get<3>(args), get<4>(args));
+  }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6>
+class InvokeHelper<R, ::std::tr1::tuple<A1, A2, A3, A4, A5, A6> > {
+ public:
+  template <typename Function>
+  static R Invoke(Function function, const ::std::tr1::tuple<A1, A2, A3, A4,
+      A5, A6>& args) {
+    using ::std::tr1::get;
+    return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args),
+        get<4>(args), get<5>(args));
+  }
+
+  template <class Class, typename MethodPtr>
+  static R InvokeMethod(Class* obj_ptr,
+                        MethodPtr method_ptr,
+                        const ::std::tr1::tuple<A1, A2, A3, A4, A5, A6>& args) {
+    using ::std::tr1::get;
+    return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args),
+        get<3>(args), get<4>(args), get<5>(args));
+  }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7>
+class InvokeHelper<R, ::std::tr1::tuple<A1, A2, A3, A4, A5, A6, A7> > {
+ public:
+  template <typename Function>
+  static R Invoke(Function function, const ::std::tr1::tuple<A1, A2, A3, A4,
+      A5, A6, A7>& args) {
+    using ::std::tr1::get;
+    return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args),
+        get<4>(args), get<5>(args), get<6>(args));
+  }
+
+  template <class Class, typename MethodPtr>
+  static R InvokeMethod(Class* obj_ptr,
+                        MethodPtr method_ptr,
+                        const ::std::tr1::tuple<A1, A2, A3, A4, A5, A6,
+                            A7>& args) {
+    using ::std::tr1::get;
+    return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args),
+        get<3>(args), get<4>(args), get<5>(args), get<6>(args));
+  }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8>
+class InvokeHelper<R, ::std::tr1::tuple<A1, A2, A3, A4, A5, A6, A7, A8> > {
+ public:
+  template <typename Function>
+  static R Invoke(Function function, const ::std::tr1::tuple<A1, A2, A3, A4,
+      A5, A6, A7, A8>& args) {
+    using ::std::tr1::get;
+    return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args),
+        get<4>(args), get<5>(args), get<6>(args), get<7>(args));
+  }
+
+  template <class Class, typename MethodPtr>
+  static R InvokeMethod(Class* obj_ptr,
+                        MethodPtr method_ptr,
+                        const ::std::tr1::tuple<A1, A2, A3, A4, A5, A6, A7,
+                            A8>& args) {
+    using ::std::tr1::get;
+    return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args),
+        get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args));
+  }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8, typename A9>
+class InvokeHelper<R, ::std::tr1::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9> > {
+ public:
+  template <typename Function>
+  static R Invoke(Function function, const ::std::tr1::tuple<A1, A2, A3, A4,
+      A5, A6, A7, A8, A9>& args) {
+    using ::std::tr1::get;
+    return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args),
+        get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args));
+  }
+
+  template <class Class, typename MethodPtr>
+  static R InvokeMethod(Class* obj_ptr,
+                        MethodPtr method_ptr,
+                        const ::std::tr1::tuple<A1, A2, A3, A4, A5, A6, A7, A8,
+                            A9>& args) {
+    using ::std::tr1::get;
+    return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args),
+        get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args),
+        get<8>(args));
+  }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8, typename A9,
+    typename A10>
+class InvokeHelper<R, ::std::tr1::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9,
+    A10> > {
+ public:
+  template <typename Function>
+  static R Invoke(Function function, const ::std::tr1::tuple<A1, A2, A3, A4,
+      A5, A6, A7, A8, A9, A10>& args) {
+    using ::std::tr1::get;
+    return function(get<0>(args), get<1>(args), get<2>(args), get<3>(args),
+        get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args),
+        get<9>(args));
+  }
+
+  template <class Class, typename MethodPtr>
+  static R InvokeMethod(Class* obj_ptr,
+                        MethodPtr method_ptr,
+                        const ::std::tr1::tuple<A1, A2, A3, A4, A5, A6, A7, A8,
+                            A9, A10>& args) {
+    using ::std::tr1::get;
+    return (obj_ptr->*method_ptr)(get<0>(args), get<1>(args), get<2>(args),
+        get<3>(args), get<4>(args), get<5>(args), get<6>(args), get<7>(args),
+        get<8>(args), get<9>(args));
+  }
+};
+
+// CallableHelper has static methods for invoking "callables",
+// i.e. function pointers and functors.  It uses overloading to
+// provide a uniform interface for invoking different kinds of
+// callables.  In particular, you can use:
+//
+//   CallableHelper<R>::Call(callable, a1, a2, ..., an)
+//
+// to invoke an n-ary callable, where R is its return type.  If an
+// argument, say a2, needs to be passed by reference, you should write
+// ByRef(a2) instead of a2 in the above expression.
+template <typename R>
+class CallableHelper {
+ public:
+  // Calls a nullary callable.
+  template <typename Function>
+  static R Call(Function function) { return function(); }
+
+  // Calls a unary callable.
+
+  // We deliberately pass a1 by value instead of const reference here
+  // in case it is a C-string literal.  If we had declared the
+  // parameter as 'const A1& a1' and write Call(function, "Hi"), the
+  // compiler would've thought A1 is 'char[3]', which causes trouble
+  // when you need to copy a value of type A1.  By declaring the
+  // parameter as 'A1 a1', the compiler will correctly infer that A1
+  // is 'const char*' when it sees Call(function, "Hi").
+  //
+  // Since this function is defined inline, the compiler can get rid
+  // of the copying of the arguments.  Therefore the performance won't
+  // be hurt.
+  template <typename Function, typename A1>
+  static R Call(Function function, A1 a1) { return function(a1); }
+
+  // Calls a binary callable.
+  template <typename Function, typename A1, typename A2>
+  static R Call(Function function, A1 a1, A2 a2) {
+    return function(a1, a2);
+  }
+
+  // Calls a ternary callable.
+  template <typename Function, typename A1, typename A2, typename A3>
+  static R Call(Function function, A1 a1, A2 a2, A3 a3) {
+    return function(a1, a2, a3);
+  }
+
+  // Calls a 4-ary callable.
+  template <typename Function, typename A1, typename A2, typename A3,
+      typename A4>
+  static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4) {
+    return function(a1, a2, a3, a4);
+  }
+
+  // Calls a 5-ary callable.
+  template <typename Function, typename A1, typename A2, typename A3,
+      typename A4, typename A5>
+  static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) {
+    return function(a1, a2, a3, a4, a5);
+  }
+
+  // Calls a 6-ary callable.
+  template <typename Function, typename A1, typename A2, typename A3,
+      typename A4, typename A5, typename A6>
+  static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) {
+    return function(a1, a2, a3, a4, a5, a6);
+  }
+
+  // Calls a 7-ary callable.
+  template <typename Function, typename A1, typename A2, typename A3,
+      typename A4, typename A5, typename A6, typename A7>
+  static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6,
+      A7 a7) {
+    return function(a1, a2, a3, a4, a5, a6, a7);
+  }
+
+  // Calls a 8-ary callable.
+  template <typename Function, typename A1, typename A2, typename A3,
+      typename A4, typename A5, typename A6, typename A7, typename A8>
+  static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6,
+      A7 a7, A8 a8) {
+    return function(a1, a2, a3, a4, a5, a6, a7, a8);
+  }
+
+  // Calls a 9-ary callable.
+  template <typename Function, typename A1, typename A2, typename A3,
+      typename A4, typename A5, typename A6, typename A7, typename A8,
+      typename A9>
+  static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6,
+      A7 a7, A8 a8, A9 a9) {
+    return function(a1, a2, a3, a4, a5, a6, a7, a8, a9);
+  }
+
+  // Calls a 10-ary callable.
+  template <typename Function, typename A1, typename A2, typename A3,
+      typename A4, typename A5, typename A6, typename A7, typename A8,
+      typename A9, typename A10>
+  static R Call(Function function, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6,
+      A7 a7, A8 a8, A9 a9, A10 a10) {
+    return function(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
+  }
+
+};  // class CallableHelper
+
+// An INTERNAL macro for extracting the type of a tuple field.  It's
+// subject to change without notice - DO NOT USE IN USER CODE!
+#define GMOCK_FIELD_(Tuple, N) \
+    typename ::std::tr1::tuple_element<N, Tuple>::type
+
+// SelectArgs<Result, ArgumentTuple, k1, k2, ..., k_n>::type is the
+// type of an n-ary function whose i-th (1-based) argument type is the
+// k{i}-th (0-based) field of ArgumentTuple, which must be a tuple
+// type, and whose return type is Result.  For example,
+//   SelectArgs<int, ::std::tr1::tuple<bool, char, double, long>, 0, 3>::type
+// is int(bool, long).
+//
+// SelectArgs<Result, ArgumentTuple, k1, k2, ..., k_n>::Select(args)
+// returns the selected fields (k1, k2, ..., k_n) of args as a tuple.
+// For example,
+//   SelectArgs<int, ::std::tr1::tuple<bool, char, double>, 2, 0>::Select(
+//       ::std::tr1::make_tuple(true, 'a', 2.5))
+// returns ::std::tr1::tuple (2.5, true).
+//
+// The numbers in list k1, k2, ..., k_n must be >= 0, where n can be
+// in the range [0, 10].  Duplicates are allowed and they don't have
+// to be in an ascending or descending order.
+
+template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
+    int k4, int k5, int k6, int k7, int k8, int k9, int k10>
+class SelectArgs {
+ public:
+  typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+      GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
+      GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5),
+      GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7),
+      GMOCK_FIELD_(ArgumentTuple, k8), GMOCK_FIELD_(ArgumentTuple, k9),
+      GMOCK_FIELD_(ArgumentTuple, k10));
+  typedef typename Function<type>::ArgumentTuple SelectedArgs;
+  static SelectedArgs Select(const ArgumentTuple& args) {
+    using ::std::tr1::get;
+    return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
+        get<k4>(args), get<k5>(args), get<k6>(args), get<k7>(args),
+        get<k8>(args), get<k9>(args), get<k10>(args));
+  }
+};
+
+template <typename Result, typename ArgumentTuple>
+class SelectArgs<Result, ArgumentTuple,
+                 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1> {
+ public:
+  typedef Result type();
+  typedef typename Function<type>::ArgumentTuple SelectedArgs;
+  static SelectedArgs Select(const ArgumentTuple& /* args */) {
+    using ::std::tr1::get;
+    return SelectedArgs();
+  }
+};
+
+template <typename Result, typename ArgumentTuple, int k1>
+class SelectArgs<Result, ArgumentTuple,
+                 k1, -1, -1, -1, -1, -1, -1, -1, -1, -1> {
+ public:
+  typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1));
+  typedef typename Function<type>::ArgumentTuple SelectedArgs;
+  static SelectedArgs Select(const ArgumentTuple& args) {
+    using ::std::tr1::get;
+    return SelectedArgs(get<k1>(args));
+  }
+};
+
+template <typename Result, typename ArgumentTuple, int k1, int k2>
+class SelectArgs<Result, ArgumentTuple,
+                 k1, k2, -1, -1, -1, -1, -1, -1, -1, -1> {
+ public:
+  typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+      GMOCK_FIELD_(ArgumentTuple, k2));
+  typedef typename Function<type>::ArgumentTuple SelectedArgs;
+  static SelectedArgs Select(const ArgumentTuple& args) {
+    using ::std::tr1::get;
+    return SelectedArgs(get<k1>(args), get<k2>(args));
+  }
+};
+
+template <typename Result, typename ArgumentTuple, int k1, int k2, int k3>
+class SelectArgs<Result, ArgumentTuple,
+                 k1, k2, k3, -1, -1, -1, -1, -1, -1, -1> {
+ public:
+  typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+      GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3));
+  typedef typename Function<type>::ArgumentTuple SelectedArgs;
+  static SelectedArgs Select(const ArgumentTuple& args) {
+    using ::std::tr1::get;
+    return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args));
+  }
+};
+
+template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
+    int k4>
+class SelectArgs<Result, ArgumentTuple,
+                 k1, k2, k3, k4, -1, -1, -1, -1, -1, -1> {
+ public:
+  typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+      GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
+      GMOCK_FIELD_(ArgumentTuple, k4));
+  typedef typename Function<type>::ArgumentTuple SelectedArgs;
+  static SelectedArgs Select(const ArgumentTuple& args) {
+    using ::std::tr1::get;
+    return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
+        get<k4>(args));
+  }
+};
+
+template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
+    int k4, int k5>
+class SelectArgs<Result, ArgumentTuple,
+                 k1, k2, k3, k4, k5, -1, -1, -1, -1, -1> {
+ public:
+  typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+      GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
+      GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5));
+  typedef typename Function<type>::ArgumentTuple SelectedArgs;
+  static SelectedArgs Select(const ArgumentTuple& args) {
+    using ::std::tr1::get;
+    return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
+        get<k4>(args), get<k5>(args));
+  }
+};
+
+template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
+    int k4, int k5, int k6>
+class SelectArgs<Result, ArgumentTuple,
+                 k1, k2, k3, k4, k5, k6, -1, -1, -1, -1> {
+ public:
+  typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+      GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
+      GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5),
+      GMOCK_FIELD_(ArgumentTuple, k6));
+  typedef typename Function<type>::ArgumentTuple SelectedArgs;
+  static SelectedArgs Select(const ArgumentTuple& args) {
+    using ::std::tr1::get;
+    return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
+        get<k4>(args), get<k5>(args), get<k6>(args));
+  }
+};
+
+template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
+    int k4, int k5, int k6, int k7>
+class SelectArgs<Result, ArgumentTuple,
+                 k1, k2, k3, k4, k5, k6, k7, -1, -1, -1> {
+ public:
+  typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+      GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
+      GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5),
+      GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7));
+  typedef typename Function<type>::ArgumentTuple SelectedArgs;
+  static SelectedArgs Select(const ArgumentTuple& args) {
+    using ::std::tr1::get;
+    return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
+        get<k4>(args), get<k5>(args), get<k6>(args), get<k7>(args));
+  }
+};
+
+template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
+    int k4, int k5, int k6, int k7, int k8>
+class SelectArgs<Result, ArgumentTuple,
+                 k1, k2, k3, k4, k5, k6, k7, k8, -1, -1> {
+ public:
+  typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+      GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
+      GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5),
+      GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7),
+      GMOCK_FIELD_(ArgumentTuple, k8));
+  typedef typename Function<type>::ArgumentTuple SelectedArgs;
+  static SelectedArgs Select(const ArgumentTuple& args) {
+    using ::std::tr1::get;
+    return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
+        get<k4>(args), get<k5>(args), get<k6>(args), get<k7>(args),
+        get<k8>(args));
+  }
+};
+
+template <typename Result, typename ArgumentTuple, int k1, int k2, int k3,
+    int k4, int k5, int k6, int k7, int k8, int k9>
+class SelectArgs<Result, ArgumentTuple,
+                 k1, k2, k3, k4, k5, k6, k7, k8, k9, -1> {
+ public:
+  typedef Result type(GMOCK_FIELD_(ArgumentTuple, k1),
+      GMOCK_FIELD_(ArgumentTuple, k2), GMOCK_FIELD_(ArgumentTuple, k3),
+      GMOCK_FIELD_(ArgumentTuple, k4), GMOCK_FIELD_(ArgumentTuple, k5),
+      GMOCK_FIELD_(ArgumentTuple, k6), GMOCK_FIELD_(ArgumentTuple, k7),
+      GMOCK_FIELD_(ArgumentTuple, k8), GMOCK_FIELD_(ArgumentTuple, k9));
+  typedef typename Function<type>::ArgumentTuple SelectedArgs;
+  static SelectedArgs Select(const ArgumentTuple& args) {
+    using ::std::tr1::get;
+    return SelectedArgs(get<k1>(args), get<k2>(args), get<k3>(args),
+        get<k4>(args), get<k5>(args), get<k6>(args), get<k7>(args),
+        get<k8>(args), get<k9>(args));
+  }
+};
+
+#undef GMOCK_FIELD_
+
+// Implements the WithArgs action.
+template <typename InnerAction, int k1 = -1, int k2 = -1, int k3 = -1,
+    int k4 = -1, int k5 = -1, int k6 = -1, int k7 = -1, int k8 = -1,
+    int k9 = -1, int k10 = -1>
+class WithArgsAction {
+ public:
+  explicit WithArgsAction(const InnerAction& action) : action_(action) {}
+
+  template <typename F>
+  operator Action<F>() const { return MakeAction(new Impl<F>(action_)); }
+
+ private:
+  template <typename F>
+  class Impl : public ActionInterface<F> {
+   public:
+    typedef typename Function<F>::Result Result;
+    typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+
+    explicit Impl(const InnerAction& action) : action_(action) {}
+
+    virtual Result Perform(const ArgumentTuple& args) {
+      return action_.Perform(SelectArgs<Result, ArgumentTuple, k1, k2, k3, k4,
+          k5, k6, k7, k8, k9, k10>::Select(args));
+    }
+
+   private:
+    typedef typename SelectArgs<Result, ArgumentTuple,
+        k1, k2, k3, k4, k5, k6, k7, k8, k9, k10>::type InnerFunctionType;
+
+    Action<InnerFunctionType> action_;
+  };
+
+  const InnerAction action_;
+};
+
+// A macro from the ACTION* family (defined later in this file)
+// defines an action that can be used in a mock function.  Typically,
+// these actions only care about a subset of the arguments of the mock
+// function.  For example, if such an action only uses the second
+// argument, it can be used in any mock function that takes >= 2
+// arguments where the type of the second argument is compatible.
+//
+// Therefore, the action implementation must be prepared to take more
+// arguments than it needs.  The ExcessiveArg type is used to
+// represent those excessive arguments.  In order to keep the compiler
+// error messages tractable, we define it in the testing namespace
+// instead of testing::internal.  However, this is an INTERNAL TYPE
+// and subject to change without notice, so a user MUST NOT USE THIS
+// TYPE DIRECTLY.
+struct ExcessiveArg {};
+
+// A helper class needed for implementing the ACTION* macros.
+template <typename Result, class Impl>
+class ActionHelper {
+ public:
+  static Result Perform(Impl* impl, const ::std::tr1::tuple<>& args) {
+    using ::std::tr1::get;
+    return impl->template gmock_PerformImpl<>(args, ExcessiveArg(),
+        ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+        ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+        ExcessiveArg());
+  }
+
+  template <typename A0>
+  static Result Perform(Impl* impl, const ::std::tr1::tuple<A0>& args) {
+    using ::std::tr1::get;
+    return impl->template gmock_PerformImpl<A0>(args, get<0>(args),
+        ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+        ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+        ExcessiveArg());
+  }
+
+  template <typename A0, typename A1>
+  static Result Perform(Impl* impl, const ::std::tr1::tuple<A0, A1>& args) {
+    using ::std::tr1::get;
+    return impl->template gmock_PerformImpl<A0, A1>(args, get<0>(args),
+        get<1>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+        ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+        ExcessiveArg());
+  }
+
+  template <typename A0, typename A1, typename A2>
+  static Result Perform(Impl* impl, const ::std::tr1::tuple<A0, A1, A2>& args) {
+    using ::std::tr1::get;
+    return impl->template gmock_PerformImpl<A0, A1, A2>(args, get<0>(args),
+        get<1>(args), get<2>(args), ExcessiveArg(), ExcessiveArg(),
+        ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+        ExcessiveArg());
+  }
+
+  template <typename A0, typename A1, typename A2, typename A3>
+  static Result Perform(Impl* impl, const ::std::tr1::tuple<A0, A1, A2,
+      A3>& args) {
+    using ::std::tr1::get;
+    return impl->template gmock_PerformImpl<A0, A1, A2, A3>(args, get<0>(args),
+        get<1>(args), get<2>(args), get<3>(args), ExcessiveArg(),
+        ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+        ExcessiveArg());
+  }
+
+  template <typename A0, typename A1, typename A2, typename A3, typename A4>
+  static Result Perform(Impl* impl, const ::std::tr1::tuple<A0, A1, A2, A3,
+      A4>& args) {
+    using ::std::tr1::get;
+    return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4>(args,
+        get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args),
+        ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+        ExcessiveArg());
+  }
+
+  template <typename A0, typename A1, typename A2, typename A3, typename A4,
+      typename A5>
+  static Result Perform(Impl* impl, const ::std::tr1::tuple<A0, A1, A2, A3, A4,
+      A5>& args) {
+    using ::std::tr1::get;
+    return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5>(args,
+        get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args),
+        get<5>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(),
+        ExcessiveArg());
+  }
+
+  template <typename A0, typename A1, typename A2, typename A3, typename A4,
+      typename A5, typename A6>
+  static Result Perform(Impl* impl, const ::std::tr1::tuple<A0, A1, A2, A3, A4,
+      A5, A6>& args) {
+    using ::std::tr1::get;
+    return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5, A6>(args,
+        get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args),
+        get<5>(args), get<6>(args), ExcessiveArg(), ExcessiveArg(),
+        ExcessiveArg());
+  }
+
+  template <typename A0, typename A1, typename A2, typename A3, typename A4,
+      typename A5, typename A6, typename A7>
+  static Result Perform(Impl* impl, const ::std::tr1::tuple<A0, A1, A2, A3, A4,
+      A5, A6, A7>& args) {
+    using ::std::tr1::get;
+    return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5, A6,
+        A7>(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args),
+        get<4>(args), get<5>(args), get<6>(args), get<7>(args), ExcessiveArg(),
+        ExcessiveArg());
+  }
+
+  template <typename A0, typename A1, typename A2, typename A3, typename A4,
+      typename A5, typename A6, typename A7, typename A8>
+  static Result Perform(Impl* impl, const ::std::tr1::tuple<A0, A1, A2, A3, A4,
+      A5, A6, A7, A8>& args) {
+    using ::std::tr1::get;
+    return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5, A6, A7,
+        A8>(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args),
+        get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args),
+        ExcessiveArg());
+  }
+
+  template <typename A0, typename A1, typename A2, typename A3, typename A4,
+      typename A5, typename A6, typename A7, typename A8, typename A9>
+  static Result Perform(Impl* impl, const ::std::tr1::tuple<A0, A1, A2, A3, A4,
+      A5, A6, A7, A8, A9>& args) {
+    using ::std::tr1::get;
+    return impl->template gmock_PerformImpl<A0, A1, A2, A3, A4, A5, A6, A7, A8,
+        A9>(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args),
+        get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args),
+        get<9>(args));
+  }
+};
+
+}  // namespace internal
+
+// Various overloads for Invoke().
+
+// WithArgs<N1, N2, ..., Nk>(an_action) creates an action that passes
+// the selected arguments of the mock function to an_action and
+// performs it.  It serves as an adaptor between actions with
+// different argument lists.  C++ doesn't support default arguments for
+// function templates, so we have to overload it.
+template <int k1, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1>
+WithArgs(const InnerAction& action) {
+  return internal::WithArgsAction<InnerAction, k1>(action);
+}
+
+template <int k1, int k2, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2>
+WithArgs(const InnerAction& action) {
+  return internal::WithArgsAction<InnerAction, k1, k2>(action);
+}
+
+template <int k1, int k2, int k3, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2, k3>
+WithArgs(const InnerAction& action) {
+  return internal::WithArgsAction<InnerAction, k1, k2, k3>(action);
+}
+
+template <int k1, int k2, int k3, int k4, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4>
+WithArgs(const InnerAction& action) {
+  return internal::WithArgsAction<InnerAction, k1, k2, k3, k4>(action);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5>
+WithArgs(const InnerAction& action) {
+  return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5>(action);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6>
+WithArgs(const InnerAction& action) {
+  return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6>(action);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, int k7,
+    typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7>
+WithArgs(const InnerAction& action) {
+  return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6,
+      k7>(action);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
+    typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7, k8>
+WithArgs(const InnerAction& action) {
+  return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7,
+      k8>(action);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
+    int k9, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7, k8, k9>
+WithArgs(const InnerAction& action) {
+  return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7, k8,
+      k9>(action);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
+    int k9, int k10, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7, k8,
+    k9, k10>
+WithArgs(const InnerAction& action) {
+  return internal::WithArgsAction<InnerAction, k1, k2, k3, k4, k5, k6, k7, k8,
+      k9, k10>(action);
+}
+
+// Creates an action that does actions a1, a2, ..., sequentially in
+// each invocation.
+template <typename Action1, typename Action2>
+inline internal::DoBothAction<Action1, Action2>
+DoAll(Action1 a1, Action2 a2) {
+  return internal::DoBothAction<Action1, Action2>(a1, a2);
+}
+
+template <typename Action1, typename Action2, typename Action3>
+inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
+    Action3> >
+DoAll(Action1 a1, Action2 a2, Action3 a3) {
+  return DoAll(a1, DoAll(a2, a3));
+}
+
+template <typename Action1, typename Action2, typename Action3,
+    typename Action4>
+inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
+    internal::DoBothAction<Action3, Action4> > >
+DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4) {
+  return DoAll(a1, DoAll(a2, a3, a4));
+}
+
+template <typename Action1, typename Action2, typename Action3,
+    typename Action4, typename Action5>
+inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
+    internal::DoBothAction<Action3, internal::DoBothAction<Action4,
+    Action5> > > >
+DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5) {
+  return DoAll(a1, DoAll(a2, a3, a4, a5));
+}
+
+template <typename Action1, typename Action2, typename Action3,
+    typename Action4, typename Action5, typename Action6>
+inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
+    internal::DoBothAction<Action3, internal::DoBothAction<Action4,
+    internal::DoBothAction<Action5, Action6> > > > >
+DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6) {
+  return DoAll(a1, DoAll(a2, a3, a4, a5, a6));
+}
+
+template <typename Action1, typename Action2, typename Action3,
+    typename Action4, typename Action5, typename Action6, typename Action7>
+inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
+    internal::DoBothAction<Action3, internal::DoBothAction<Action4,
+    internal::DoBothAction<Action5, internal::DoBothAction<Action6,
+    Action7> > > > > >
+DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
+    Action7 a7) {
+  return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7));
+}
+
+template <typename Action1, typename Action2, typename Action3,
+    typename Action4, typename Action5, typename Action6, typename Action7,
+    typename Action8>
+inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
+    internal::DoBothAction<Action3, internal::DoBothAction<Action4,
+    internal::DoBothAction<Action5, internal::DoBothAction<Action6,
+    internal::DoBothAction<Action7, Action8> > > > > > >
+DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
+    Action7 a7, Action8 a8) {
+  return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8));
+}
+
+template <typename Action1, typename Action2, typename Action3,
+    typename Action4, typename Action5, typename Action6, typename Action7,
+    typename Action8, typename Action9>
+inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
+    internal::DoBothAction<Action3, internal::DoBothAction<Action4,
+    internal::DoBothAction<Action5, internal::DoBothAction<Action6,
+    internal::DoBothAction<Action7, internal::DoBothAction<Action8,
+    Action9> > > > > > > >
+DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
+    Action7 a7, Action8 a8, Action9 a9) {
+  return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8, a9));
+}
+
+template <typename Action1, typename Action2, typename Action3,
+    typename Action4, typename Action5, typename Action6, typename Action7,
+    typename Action8, typename Action9, typename Action10>
+inline internal::DoBothAction<Action1, internal::DoBothAction<Action2,
+    internal::DoBothAction<Action3, internal::DoBothAction<Action4,
+    internal::DoBothAction<Action5, internal::DoBothAction<Action6,
+    internal::DoBothAction<Action7, internal::DoBothAction<Action8,
+    internal::DoBothAction<Action9, Action10> > > > > > > > >
+DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6,
+    Action7 a7, Action8 a8, Action9 a9, Action10 a10) {
+  return DoAll(a1, DoAll(a2, a3, a4, a5, a6, a7, a8, a9, a10));
+}
+
+}  // namespace testing
+
+// The ACTION* family of macros can be used in a namespace scope to
+// define custom actions easily.  The syntax:
+//
+//   ACTION(name) { statements; }
+//
+// will define an action with the given name that executes the
+// statements.  The value returned by the statements will be used as
+// the return value of the action.  Inside the statements, you can
+// refer to the K-th (0-based) argument of the mock function by
+// 'argK', and refer to its type by 'argK_type'.  For example:
+//
+//   ACTION(IncrementArg1) {
+//     arg1_type temp = arg1;
+//     return ++(*temp);
+//   }
+//
+// allows you to write
+//
+//   ...WillOnce(IncrementArg1());
+//
+// You can also refer to the entire argument tuple and its type by
+// 'args' and 'args_type', and refer to the mock function type and its
+// return type by 'function_type' and 'return_type'.
+//
+// Note that you don't need to specify the types of the mock function
+// arguments.  However rest assured that your code is still type-safe:
+// you'll get a compiler error if *arg1 doesn't support the ++
+// operator, or if the type of ++(*arg1) isn't compatible with the
+// mock function's return type, for example.
+//
+// Sometimes you'll want to parameterize the action.   For that you can use
+// another macro:
+//
+//   ACTION_P(name, param_name) { statements; }
+//
+// For example:
+//
+//   ACTION_P(Add, n) { return arg0 + n; }
+//
+// will allow you to write:
+//
+//   ...WillOnce(Add(5));
+//
+// Note that you don't need to provide the type of the parameter
+// either.  If you need to reference the type of a parameter named
+// 'foo', you can write 'foo_type'.  For example, in the body of
+// ACTION_P(Add, n) above, you can write 'n_type' to refer to the type
+// of 'n'.
+//
+// We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P10 to support
+// multi-parameter actions.
+//
+// For the purpose of typing, you can view
+//
+//   ACTION_Pk(Foo, p1, ..., pk) { ... }
+//
+// as shorthand for
+//
+//   template <typename p1_type, ..., typename pk_type>
+//   FooActionPk<p1_type, ..., pk_type> Foo(p1_type p1, ..., pk_type pk) { ... }
+//
+// In particular, you can provide the template type arguments
+// explicitly when invoking Foo(), as in Foo<long, bool>(5, false);
+// although usually you can rely on the compiler to infer the types
+// for you automatically.  You can assign the result of expression
+// Foo(p1, ..., pk) to a variable of type FooActionPk<p1_type, ...,
+// pk_type>.  This can be useful when composing actions.
+//
+// You can also overload actions with different numbers of parameters:
+//
+//   ACTION_P(Plus, a) { ... }
+//   ACTION_P2(Plus, a, b) { ... }
+//
+// While it's tempting to always use the ACTION* macros when defining
+// a new action, you should also consider implementing ActionInterface
+// or using MakePolymorphicAction() instead, especially if you need to
+// use the action a lot.  While these approaches require more work,
+// they give you more control on the types of the mock function
+// arguments and the action parameters, which in general leads to
+// better compiler error messages that pay off in the long run.  They
+// also allow overloading actions based on parameter types (as opposed
+// to just based on the number of parameters).
+//
+// CAVEAT:
+//
+// ACTION*() can only be used in a namespace scope.  The reason is
+// that C++ doesn't yet allow function-local types to be used to
+// instantiate templates.  The up-coming C++0x standard will fix this.
+// Once that's done, we'll consider supporting using ACTION*() inside
+// a function.
+//
+// MORE INFORMATION:
+//
+// To learn more about using these macros, please search for 'ACTION'
+// on http://code.google.com/p/googlemock/wiki/CookBook.
+
+// An internal macro needed for implementing ACTION*().
+#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_\
+    const args_type& args GTEST_ATTRIBUTE_UNUSED_,\
+    arg0_type arg0 GTEST_ATTRIBUTE_UNUSED_,\
+    arg1_type arg1 GTEST_ATTRIBUTE_UNUSED_,\
+    arg2_type arg2 GTEST_ATTRIBUTE_UNUSED_,\
+    arg3_type arg3 GTEST_ATTRIBUTE_UNUSED_,\
+    arg4_type arg4 GTEST_ATTRIBUTE_UNUSED_,\
+    arg5_type arg5 GTEST_ATTRIBUTE_UNUSED_,\
+    arg6_type arg6 GTEST_ATTRIBUTE_UNUSED_,\
+    arg7_type arg7 GTEST_ATTRIBUTE_UNUSED_,\
+    arg8_type arg8 GTEST_ATTRIBUTE_UNUSED_,\
+    arg9_type arg9 GTEST_ATTRIBUTE_UNUSED_
+
+// Sometimes you want to give an action explicit template parameters
+// that cannot be inferred from its value parameters.  ACTION() and
+// ACTION_P*() don't support that.  ACTION_TEMPLATE() remedies that
+// and can be viewed as an extension to ACTION() and ACTION_P*().
+//
+// The syntax:
+//
+//   ACTION_TEMPLATE(ActionName,
+//                   HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m),
+//                   AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; }
+//
+// defines an action template that takes m explicit template
+// parameters and n value parameters.  name_i is the name of the i-th
+// template parameter, and kind_i specifies whether it's a typename,
+// an integral constant, or a template.  p_i is the name of the i-th
+// value parameter.
+//
+// Example:
+//
+//   // DuplicateArg<k, T>(output) converts the k-th argument of the mock
+//   // function to type T and copies it to *output.
+//   ACTION_TEMPLATE(DuplicateArg,
+//                   HAS_2_TEMPLATE_PARAMS(int, k, typename, T),
+//                   AND_1_VALUE_PARAMS(output)) {
+//     *output = T(std::tr1::get<k>(args));
+//   }
+//   ...
+//     int n;
+//     EXPECT_CALL(mock, Foo(_, _))
+//         .WillOnce(DuplicateArg<1, unsigned char>(&n));
+//
+// To create an instance of an action template, write:
+//
+//   ActionName<t1, ..., t_m>(v1, ..., v_n)
+//
+// where the ts are the template arguments and the vs are the value
+// arguments.  The value argument types are inferred by the compiler.
+// If you want to explicitly specify the value argument types, you can
+// provide additional template arguments:
+//
+//   ActionName<t1, ..., t_m, u1, ..., u_k>(v1, ..., v_n)
+//
+// where u_i is the desired type of v_i.
+//
+// ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded on the
+// number of value parameters, but not on the number of template
+// parameters.  Without the restriction, the meaning of the following
+// is unclear:
+//
+//   OverloadedAction<int, bool>(x);
+//
+// Are we using a single-template-parameter action where 'bool' refers
+// to the type of x, or are we using a two-template-parameter action
+// where the compiler is asked to infer the type of x?
+//
+// Implementation notes:
+//
+// GMOCK_INTERNAL_*_HAS_m_TEMPLATE_PARAMS and
+// GMOCK_INTERNAL_*_AND_n_VALUE_PARAMS are internal macros for
+// implementing ACTION_TEMPLATE.  The main trick we use is to create
+// new macro invocations when expanding a macro.  For example, we have
+//
+//   #define ACTION_TEMPLATE(name, template_params, value_params)
+//       ... GMOCK_INTERNAL_DECL_##template_params ...
+//
+// which causes ACTION_TEMPLATE(..., HAS_1_TEMPLATE_PARAMS(typename, T), ...)
+// to expand to
+//
+//       ... GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(typename, T) ...
+//
+// Since GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS is a macro, the
+// preprocessor will continue to expand it to
+//
+//       ... typename T ...
+//
+// This technique conforms to the C++ standard and is portable.  It
+// allows us to implement action templates using O(N) code, where N is
+// the maximum number of template/value parameters supported.  Without
+// using it, we'd have to devote O(N^2) amount of code to implement all
+// combinations of m and n.
+
+// Declares the template parameters.
+#define GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(kind0, name0) kind0 name0
+#define GMOCK_INTERNAL_DECL_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \
+    name1) kind0 name0, kind1 name1
+#define GMOCK_INTERNAL_DECL_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2) kind0 name0, kind1 name1, kind2 name2
+#define GMOCK_INTERNAL_DECL_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3) kind0 name0, kind1 name1, kind2 name2, \
+    kind3 name3
+#define GMOCK_INTERNAL_DECL_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4) kind0 name0, kind1 name1, \
+    kind2 name2, kind3 name3, kind4 name4
+#define GMOCK_INTERNAL_DECL_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5) kind0 name0, \
+    kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5
+#define GMOCK_INTERNAL_DECL_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
+    name6) kind0 name0, kind1 name1, kind2 name2, kind3 name3, kind4 name4, \
+    kind5 name5, kind6 name6
+#define GMOCK_INTERNAL_DECL_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
+    kind7, name7) kind0 name0, kind1 name1, kind2 name2, kind3 name3, \
+    kind4 name4, kind5 name5, kind6 name6, kind7 name7
+#define GMOCK_INTERNAL_DECL_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
+    kind7, name7, kind8, name8) kind0 name0, kind1 name1, kind2 name2, \
+    kind3 name3, kind4 name4, kind5 name5, kind6 name6, kind7 name7, \
+    kind8 name8
+#define GMOCK_INTERNAL_DECL_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \
+    name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
+    name6, kind7, name7, kind8, name8, kind9, name9) kind0 name0, \
+    kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5, \
+    kind6 name6, kind7 name7, kind8 name8, kind9 name9
+
+// Lists the template parameters.
+#define GMOCK_INTERNAL_LIST_HAS_1_TEMPLATE_PARAMS(kind0, name0) name0
+#define GMOCK_INTERNAL_LIST_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \
+    name1) name0, name1
+#define GMOCK_INTERNAL_LIST_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2) name0, name1, name2
+#define GMOCK_INTERNAL_LIST_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3) name0, name1, name2, name3
+#define GMOCK_INTERNAL_LIST_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4) name0, name1, name2, name3, \
+    name4
+#define GMOCK_INTERNAL_LIST_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5) name0, name1, \
+    name2, name3, name4, name5
+#define GMOCK_INTERNAL_LIST_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
+    name6) name0, name1, name2, name3, name4, name5, name6
+#define GMOCK_INTERNAL_LIST_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
+    kind7, name7) name0, name1, name2, name3, name4, name5, name6, name7
+#define GMOCK_INTERNAL_LIST_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
+    kind7, name7, kind8, name8) name0, name1, name2, name3, name4, name5, \
+    name6, name7, name8
+#define GMOCK_INTERNAL_LIST_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \
+    name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
+    name6, kind7, name7, kind8, name8, kind9, name9) name0, name1, name2, \
+    name3, name4, name5, name6, name7, name8, name9
+
+// Declares the types of value parameters.
+#define GMOCK_INTERNAL_DECL_TYPE_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_DECL_TYPE_AND_1_VALUE_PARAMS(p0) , typename p0##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_2_VALUE_PARAMS(p0, p1) , \
+    typename p0##_type, typename p1##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , \
+    typename p0##_type, typename p1##_type, typename p2##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \
+    typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \
+    typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type, typename p4##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \
+    typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type, typename p4##_type, typename p5##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6) , typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type, typename p4##_type, typename p5##_type, \
+    typename p6##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7) , typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type, typename p4##_type, typename p5##_type, \
+    typename p6##_type, typename p7##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7, p8) , typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type, typename p4##_type, typename p5##_type, \
+    typename p6##_type, typename p7##_type, typename p8##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7, p8, p9) , typename p0##_type, typename p1##_type, \
+    typename p2##_type, typename p3##_type, typename p4##_type, \
+    typename p5##_type, typename p6##_type, typename p7##_type, \
+    typename p8##_type, typename p9##_type
+
+// Initializes the value parameters.
+#define GMOCK_INTERNAL_INIT_AND_0_VALUE_PARAMS()\
+    ()
+#define GMOCK_INTERNAL_INIT_AND_1_VALUE_PARAMS(p0)\
+    (p0##_type gmock_p0) : p0(gmock_p0)
+#define GMOCK_INTERNAL_INIT_AND_2_VALUE_PARAMS(p0, p1)\
+    (p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), p1(gmock_p1)
+#define GMOCK_INTERNAL_INIT_AND_3_VALUE_PARAMS(p0, p1, p2)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2)
+#define GMOCK_INTERNAL_INIT_AND_4_VALUE_PARAMS(p0, p1, p2, p3)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+        p3(gmock_p3)
+#define GMOCK_INTERNAL_INIT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), \
+        p2(gmock_p2), p3(gmock_p3), p4(gmock_p4)
+#define GMOCK_INTERNAL_INIT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4, \
+        p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+        p3(gmock_p3), p4(gmock_p4), p5(gmock_p5)
+#define GMOCK_INTERNAL_INIT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+        p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+        p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6)
+#define GMOCK_INTERNAL_INIT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+        p6##_type gmock_p6, p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), \
+        p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
+        p7(gmock_p7)
+#define GMOCK_INTERNAL_INIT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+        p6##_type gmock_p6, p7##_type gmock_p7, \
+        p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+        p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
+        p8(gmock_p8)
+#define GMOCK_INTERNAL_INIT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8, p9)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+        p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \
+        p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+        p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
+        p8(gmock_p8), p9(gmock_p9)
+
+// Declares the fields for storing the value parameters.
+#define GMOCK_INTERNAL_DEFN_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_DEFN_AND_1_VALUE_PARAMS(p0) p0##_type p0;
+#define GMOCK_INTERNAL_DEFN_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0; \
+    p1##_type p1;
+#define GMOCK_INTERNAL_DEFN_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0; \
+    p1##_type p1; p2##_type p2;
+#define GMOCK_INTERNAL_DEFN_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0; \
+    p1##_type p1; p2##_type p2; p3##_type p3;
+#define GMOCK_INTERNAL_DEFN_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \
+    p4) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4;
+#define GMOCK_INTERNAL_DEFN_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \
+    p5) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \
+    p5##_type p5;
+#define GMOCK_INTERNAL_DEFN_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \
+    p5##_type p5; p6##_type p6;
+#define GMOCK_INTERNAL_DEFN_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \
+    p5##_type p5; p6##_type p6; p7##_type p7;
+#define GMOCK_INTERNAL_DEFN_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \
+    p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8;
+#define GMOCK_INTERNAL_DEFN_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8, p9) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \
+    p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8; \
+    p9##_type p9;
+
+// Lists the value parameters.
+#define GMOCK_INTERNAL_LIST_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_LIST_AND_1_VALUE_PARAMS(p0) p0
+#define GMOCK_INTERNAL_LIST_AND_2_VALUE_PARAMS(p0, p1) p0, p1
+#define GMOCK_INTERNAL_LIST_AND_3_VALUE_PARAMS(p0, p1, p2) p0, p1, p2
+#define GMOCK_INTERNAL_LIST_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0, p1, p2, p3
+#define GMOCK_INTERNAL_LIST_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) p0, p1, \
+    p2, p3, p4
+#define GMOCK_INTERNAL_LIST_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) p0, \
+    p1, p2, p3, p4, p5
+#define GMOCK_INTERNAL_LIST_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6) p0, p1, p2, p3, p4, p5, p6
+#define GMOCK_INTERNAL_LIST_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7) p0, p1, p2, p3, p4, p5, p6, p7
+#define GMOCK_INTERNAL_LIST_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8) p0, p1, p2, p3, p4, p5, p6, p7, p8
+#define GMOCK_INTERNAL_LIST_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8, p9) p0, p1, p2, p3, p4, p5, p6, p7, p8, p9
+
+// Lists the value parameter types.
+#define GMOCK_INTERNAL_LIST_TYPE_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_LIST_TYPE_AND_1_VALUE_PARAMS(p0) , p0##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_2_VALUE_PARAMS(p0, p1) , p0##_type, \
+    p1##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , p0##_type, \
+    p1##_type, p2##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \
+    p0##_type, p1##_type, p2##_type, p3##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \
+    p0##_type, p1##_type, p2##_type, p3##_type, p4##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \
+    p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type, \
+    p6##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+    p5##_type, p6##_type, p7##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7, p8) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+    p5##_type, p6##_type, p7##_type, p8##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7, p8, p9) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+    p5##_type, p6##_type, p7##_type, p8##_type, p9##_type
+
+// Declares the value parameters.
+#define GMOCK_INTERNAL_DECL_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_DECL_AND_1_VALUE_PARAMS(p0) p0##_type p0
+#define GMOCK_INTERNAL_DECL_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0, \
+    p1##_type p1
+#define GMOCK_INTERNAL_DECL_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0, \
+    p1##_type p1, p2##_type p2
+#define GMOCK_INTERNAL_DECL_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0, \
+    p1##_type p1, p2##_type p2, p3##_type p3
+#define GMOCK_INTERNAL_DECL_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \
+    p4) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4
+#define GMOCK_INTERNAL_DECL_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \
+    p5) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \
+    p5##_type p5
+#define GMOCK_INTERNAL_DECL_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \
+    p5##_type p5, p6##_type p6
+#define GMOCK_INTERNAL_DECL_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \
+    p5##_type p5, p6##_type p6, p7##_type p7
+#define GMOCK_INTERNAL_DECL_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+    p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8
+#define GMOCK_INTERNAL_DECL_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8, p9) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+    p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \
+    p9##_type p9
+
+// The suffix of the class template implementing the action template.
+#define GMOCK_INTERNAL_COUNT_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_COUNT_AND_1_VALUE_PARAMS(p0) P
+#define GMOCK_INTERNAL_COUNT_AND_2_VALUE_PARAMS(p0, p1) P2
+#define GMOCK_INTERNAL_COUNT_AND_3_VALUE_PARAMS(p0, p1, p2) P3
+#define GMOCK_INTERNAL_COUNT_AND_4_VALUE_PARAMS(p0, p1, p2, p3) P4
+#define GMOCK_INTERNAL_COUNT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) P5
+#define GMOCK_INTERNAL_COUNT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) P6
+#define GMOCK_INTERNAL_COUNT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6) P7
+#define GMOCK_INTERNAL_COUNT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7) P8
+#define GMOCK_INTERNAL_COUNT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8) P9
+#define GMOCK_INTERNAL_COUNT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8, p9) P10
+
+// The name of the class template implementing the action template.
+#define GMOCK_ACTION_CLASS_(name, value_params)\
+    GMOCK_CONCAT_TOKEN_(name##Action, GMOCK_INTERNAL_COUNT_##value_params)
+
+#define ACTION_TEMPLATE(name, template_params, value_params)\
+  template <GMOCK_INTERNAL_DECL_##template_params\
+            GMOCK_INTERNAL_DECL_TYPE_##value_params>\
+  class GMOCK_ACTION_CLASS_(name, value_params) {\
+   public:\
+    GMOCK_ACTION_CLASS_(name, value_params)\
+        GMOCK_INTERNAL_INIT_##value_params {}\
+    template <typename F>\
+    class gmock_Impl : public ::testing::ActionInterface<F> {\
+     public:\
+      typedef F function_type;\
+      typedef typename ::testing::internal::Function<F>::Result return_type;\
+      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+          args_type;\
+      explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {}\
+      virtual return_type Perform(const args_type& args) {\
+        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+            Perform(this, args);\
+      }\
+      template <typename arg0_type, typename arg1_type, typename arg2_type, \
+          typename arg3_type, typename arg4_type, typename arg5_type, \
+          typename arg6_type, typename arg7_type, typename arg8_type, \
+          typename arg9_type>\
+      return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+          arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+          arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+          arg9_type arg9) const;\
+      GMOCK_INTERNAL_DEFN_##value_params\
+    };\
+    template <typename F> operator ::testing::Action<F>() const {\
+      return ::testing::Action<F>(\
+          new gmock_Impl<F>(GMOCK_INTERNAL_LIST_##value_params));\
+    }\
+    GMOCK_INTERNAL_DEFN_##value_params\
+  };\
+  template <GMOCK_INTERNAL_DECL_##template_params\
+            GMOCK_INTERNAL_DECL_TYPE_##value_params>\
+  inline GMOCK_ACTION_CLASS_(name, value_params)<\
+      GMOCK_INTERNAL_LIST_##template_params\
+      GMOCK_INTERNAL_LIST_TYPE_##value_params> name(\
+          GMOCK_INTERNAL_DECL_##value_params) {\
+    return GMOCK_ACTION_CLASS_(name, value_params)<\
+        GMOCK_INTERNAL_LIST_##template_params\
+        GMOCK_INTERNAL_LIST_TYPE_##value_params>(\
+            GMOCK_INTERNAL_LIST_##value_params);\
+  }\
+  template <GMOCK_INTERNAL_DECL_##template_params\
+            GMOCK_INTERNAL_DECL_TYPE_##value_params>\
+  template <typename F>\
+  template <typename arg0_type, typename arg1_type, typename arg2_type,\
+      typename arg3_type, typename arg4_type, typename arg5_type,\
+      typename arg6_type, typename arg7_type, typename arg8_type,\
+      typename arg9_type>\
+  typename ::testing::internal::Function<F>::Result\
+      GMOCK_ACTION_CLASS_(name, value_params)<\
+          GMOCK_INTERNAL_LIST_##template_params\
+          GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl<F>::\
+              gmock_PerformImpl(\
+          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION(name)\
+  class name##Action {\
+   public:\
+    name##Action() {}\
+    template <typename F>\
+    class gmock_Impl : public ::testing::ActionInterface<F> {\
+     public:\
+      typedef F function_type;\
+      typedef typename ::testing::internal::Function<F>::Result return_type;\
+      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+          args_type;\
+      gmock_Impl() {}\
+      virtual return_type Perform(const args_type& args) {\
+        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+            Perform(this, args);\
+      }\
+      template <typename arg0_type, typename arg1_type, typename arg2_type, \
+          typename arg3_type, typename arg4_type, typename arg5_type, \
+          typename arg6_type, typename arg7_type, typename arg8_type, \
+          typename arg9_type>\
+      return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+          arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+          arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+          arg9_type arg9) const;\
+    };\
+    template <typename F> operator ::testing::Action<F>() const {\
+      return ::testing::Action<F>(new gmock_Impl<F>());\
+    }\
+  };\
+  inline name##Action name() {\
+    return name##Action();\
+  }\
+  template <typename F>\
+  template <typename arg0_type, typename arg1_type, typename arg2_type, \
+      typename arg3_type, typename arg4_type, typename arg5_type, \
+      typename arg6_type, typename arg7_type, typename arg8_type, \
+      typename arg9_type>\
+  typename ::testing::internal::Function<F>::Result\
+      name##Action::gmock_Impl<F>::gmock_PerformImpl(\
+          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P(name, p0)\
+  template <typename p0##_type>\
+  class name##ActionP {\
+   public:\
+    name##ActionP(p0##_type gmock_p0) : p0(gmock_p0) {}\
+    template <typename F>\
+    class gmock_Impl : public ::testing::ActionInterface<F> {\
+     public:\
+      typedef F function_type;\
+      typedef typename ::testing::internal::Function<F>::Result return_type;\
+      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+          args_type;\
+      explicit gmock_Impl(p0##_type gmock_p0) : p0(gmock_p0) {}\
+      virtual return_type Perform(const args_type& args) {\
+        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+            Perform(this, args);\
+      }\
+      template <typename arg0_type, typename arg1_type, typename arg2_type, \
+          typename arg3_type, typename arg4_type, typename arg5_type, \
+          typename arg6_type, typename arg7_type, typename arg8_type, \
+          typename arg9_type>\
+      return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+          arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+          arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+          arg9_type arg9) const;\
+      p0##_type p0;\
+    };\
+    template <typename F> operator ::testing::Action<F>() const {\
+      return ::testing::Action<F>(new gmock_Impl<F>(p0));\
+    }\
+    p0##_type p0;\
+  };\
+  template <typename p0##_type>\
+  inline name##ActionP<p0##_type> name(p0##_type p0) {\
+    return name##ActionP<p0##_type>(p0);\
+  }\
+  template <typename p0##_type>\
+  template <typename F>\
+  template <typename arg0_type, typename arg1_type, typename arg2_type, \
+      typename arg3_type, typename arg4_type, typename arg5_type, \
+      typename arg6_type, typename arg7_type, typename arg8_type, \
+      typename arg9_type>\
+  typename ::testing::internal::Function<F>::Result\
+      name##ActionP<p0##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P2(name, p0, p1)\
+  template <typename p0##_type, typename p1##_type>\
+  class name##ActionP2 {\
+   public:\
+    name##ActionP2(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \
+        p1(gmock_p1) {}\
+    template <typename F>\
+    class gmock_Impl : public ::testing::ActionInterface<F> {\
+     public:\
+      typedef F function_type;\
+      typedef typename ::testing::internal::Function<F>::Result return_type;\
+      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+          args_type;\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \
+          p1(gmock_p1) {}\
+      virtual return_type Perform(const args_type& args) {\
+        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+            Perform(this, args);\
+      }\
+      template <typename arg0_type, typename arg1_type, typename arg2_type, \
+          typename arg3_type, typename arg4_type, typename arg5_type, \
+          typename arg6_type, typename arg7_type, typename arg8_type, \
+          typename arg9_type>\
+      return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+          arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+          arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+          arg9_type arg9) const;\
+      p0##_type p0;\
+      p1##_type p1;\
+    };\
+    template <typename F> operator ::testing::Action<F>() const {\
+      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+  };\
+  template <typename p0##_type, typename p1##_type>\
+  inline name##ActionP2<p0##_type, p1##_type> name(p0##_type p0, \
+      p1##_type p1) {\
+    return name##ActionP2<p0##_type, p1##_type>(p0, p1);\
+  }\
+  template <typename p0##_type, typename p1##_type>\
+  template <typename F>\
+  template <typename arg0_type, typename arg1_type, typename arg2_type, \
+      typename arg3_type, typename arg4_type, typename arg5_type, \
+      typename arg6_type, typename arg7_type, typename arg8_type, \
+      typename arg9_type>\
+  typename ::testing::internal::Function<F>::Result\
+      name##ActionP2<p0##_type, p1##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P3(name, p0, p1, p2)\
+  template <typename p0##_type, typename p1##_type, typename p2##_type>\
+  class name##ActionP3 {\
+   public:\
+    name##ActionP3(p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {}\
+    template <typename F>\
+    class gmock_Impl : public ::testing::ActionInterface<F> {\
+     public:\
+      typedef F function_type;\
+      typedef typename ::testing::internal::Function<F>::Result return_type;\
+      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+          args_type;\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, \
+          p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {}\
+      virtual return_type Perform(const args_type& args) {\
+        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+            Perform(this, args);\
+      }\
+      template <typename arg0_type, typename arg1_type, typename arg2_type, \
+          typename arg3_type, typename arg4_type, typename arg5_type, \
+          typename arg6_type, typename arg7_type, typename arg8_type, \
+          typename arg9_type>\
+      return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+          arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+          arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+          arg9_type arg9) const;\
+      p0##_type p0;\
+      p1##_type p1;\
+      p2##_type p2;\
+    };\
+    template <typename F> operator ::testing::Action<F>() const {\
+      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+    p2##_type p2;\
+  };\
+  template <typename p0##_type, typename p1##_type, typename p2##_type>\
+  inline name##ActionP3<p0##_type, p1##_type, p2##_type> name(p0##_type p0, \
+      p1##_type p1, p2##_type p2) {\
+    return name##ActionP3<p0##_type, p1##_type, p2##_type>(p0, p1, p2);\
+  }\
+  template <typename p0##_type, typename p1##_type, typename p2##_type>\
+  template <typename F>\
+  template <typename arg0_type, typename arg1_type, typename arg2_type, \
+      typename arg3_type, typename arg4_type, typename arg5_type, \
+      typename arg6_type, typename arg7_type, typename arg8_type, \
+      typename arg9_type>\
+  typename ::testing::internal::Function<F>::Result\
+      name##ActionP3<p0##_type, p1##_type, \
+          p2##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P4(name, p0, p1, p2, p3)\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type>\
+  class name##ActionP4 {\
+   public:\
+    name##ActionP4(p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2, p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), \
+        p2(gmock_p2), p3(gmock_p3) {}\
+    template <typename F>\
+    class gmock_Impl : public ::testing::ActionInterface<F> {\
+     public:\
+      typedef F function_type;\
+      typedef typename ::testing::internal::Function<F>::Result return_type;\
+      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+          args_type;\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+          p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+          p3(gmock_p3) {}\
+      virtual return_type Perform(const args_type& args) {\
+        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+            Perform(this, args);\
+      }\
+      template <typename arg0_type, typename arg1_type, typename arg2_type, \
+          typename arg3_type, typename arg4_type, typename arg5_type, \
+          typename arg6_type, typename arg7_type, typename arg8_type, \
+          typename arg9_type>\
+      return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+          arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+          arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+          arg9_type arg9) const;\
+      p0##_type p0;\
+      p1##_type p1;\
+      p2##_type p2;\
+      p3##_type p3;\
+    };\
+    template <typename F> operator ::testing::Action<F>() const {\
+      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+    p2##_type p2;\
+    p3##_type p3;\
+  };\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type>\
+  inline name##ActionP4<p0##_type, p1##_type, p2##_type, \
+      p3##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, \
+      p3##_type p3) {\
+    return name##ActionP4<p0##_type, p1##_type, p2##_type, p3##_type>(p0, p1, \
+        p2, p3);\
+  }\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type>\
+  template <typename F>\
+  template <typename arg0_type, typename arg1_type, typename arg2_type, \
+      typename arg3_type, typename arg4_type, typename arg5_type, \
+      typename arg6_type, typename arg7_type, typename arg8_type, \
+      typename arg9_type>\
+  typename ::testing::internal::Function<F>::Result\
+      name##ActionP4<p0##_type, p1##_type, p2##_type, \
+          p3##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P5(name, p0, p1, p2, p3, p4)\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type>\
+  class name##ActionP5 {\
+   public:\
+    name##ActionP5(p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2, p3##_type gmock_p3, \
+        p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+        p3(gmock_p3), p4(gmock_p4) {}\
+    template <typename F>\
+    class gmock_Impl : public ::testing::ActionInterface<F> {\
+     public:\
+      typedef F function_type;\
+      typedef typename ::testing::internal::Function<F>::Result return_type;\
+      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+          args_type;\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+          p3##_type gmock_p3, p4##_type gmock_p4) : p0(gmock_p0), \
+          p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), p4(gmock_p4) {}\
+      virtual return_type Perform(const args_type& args) {\
+        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+            Perform(this, args);\
+      }\
+      template <typename arg0_type, typename arg1_type, typename arg2_type, \
+          typename arg3_type, typename arg4_type, typename arg5_type, \
+          typename arg6_type, typename arg7_type, typename arg8_type, \
+          typename arg9_type>\
+      return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+          arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+          arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+          arg9_type arg9) const;\
+      p0##_type p0;\
+      p1##_type p1;\
+      p2##_type p2;\
+      p3##_type p3;\
+      p4##_type p4;\
+    };\
+    template <typename F> operator ::testing::Action<F>() const {\
+      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+    p2##_type p2;\
+    p3##_type p3;\
+    p4##_type p4;\
+  };\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type>\
+  inline name##ActionP5<p0##_type, p1##_type, p2##_type, p3##_type, \
+      p4##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+      p4##_type p4) {\
+    return name##ActionP5<p0##_type, p1##_type, p2##_type, p3##_type, \
+        p4##_type>(p0, p1, p2, p3, p4);\
+  }\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type>\
+  template <typename F>\
+  template <typename arg0_type, typename arg1_type, typename arg2_type, \
+      typename arg3_type, typename arg4_type, typename arg5_type, \
+      typename arg6_type, typename arg7_type, typename arg8_type, \
+      typename arg9_type>\
+  typename ::testing::internal::Function<F>::Result\
+      name##ActionP5<p0##_type, p1##_type, p2##_type, p3##_type, \
+          p4##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P6(name, p0, p1, p2, p3, p4, p5)\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type>\
+  class name##ActionP6 {\
+   public:\
+    name##ActionP6(p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+        p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+        p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {}\
+    template <typename F>\
+    class gmock_Impl : public ::testing::ActionInterface<F> {\
+     public:\
+      typedef F function_type;\
+      typedef typename ::testing::internal::Function<F>::Result return_type;\
+      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+          args_type;\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+          p3##_type gmock_p3, p4##_type gmock_p4, \
+          p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+          p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {}\
+      virtual return_type Perform(const args_type& args) {\
+        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+            Perform(this, args);\
+      }\
+      template <typename arg0_type, typename arg1_type, typename arg2_type, \
+          typename arg3_type, typename arg4_type, typename arg5_type, \
+          typename arg6_type, typename arg7_type, typename arg8_type, \
+          typename arg9_type>\
+      return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+          arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+          arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+          arg9_type arg9) const;\
+      p0##_type p0;\
+      p1##_type p1;\
+      p2##_type p2;\
+      p3##_type p3;\
+      p4##_type p4;\
+      p5##_type p5;\
+    };\
+    template <typename F> operator ::testing::Action<F>() const {\
+      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4, p5));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+    p2##_type p2;\
+    p3##_type p3;\
+    p4##_type p4;\
+    p5##_type p5;\
+  };\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type>\
+  inline name##ActionP6<p0##_type, p1##_type, p2##_type, p3##_type, \
+      p4##_type, p5##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, \
+      p3##_type p3, p4##_type p4, p5##_type p5) {\
+    return name##ActionP6<p0##_type, p1##_type, p2##_type, p3##_type, \
+        p4##_type, p5##_type>(p0, p1, p2, p3, p4, p5);\
+  }\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type>\
+  template <typename F>\
+  template <typename arg0_type, typename arg1_type, typename arg2_type, \
+      typename arg3_type, typename arg4_type, typename arg5_type, \
+      typename arg6_type, typename arg7_type, typename arg8_type, \
+      typename arg9_type>\
+  typename ::testing::internal::Function<F>::Result\
+      name##ActionP6<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+          p5##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P7(name, p0, p1, p2, p3, p4, p5, p6)\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type>\
+  class name##ActionP7 {\
+   public:\
+    name##ActionP7(p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+        p5##_type gmock_p5, p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), \
+        p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), \
+        p6(gmock_p6) {}\
+    template <typename F>\
+    class gmock_Impl : public ::testing::ActionInterface<F> {\
+     public:\
+      typedef F function_type;\
+      typedef typename ::testing::internal::Function<F>::Result return_type;\
+      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+          args_type;\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+          p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+          p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6) {}\
+      virtual return_type Perform(const args_type& args) {\
+        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+            Perform(this, args);\
+      }\
+      template <typename arg0_type, typename arg1_type, typename arg2_type, \
+          typename arg3_type, typename arg4_type, typename arg5_type, \
+          typename arg6_type, typename arg7_type, typename arg8_type, \
+          typename arg9_type>\
+      return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+          arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+          arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+          arg9_type arg9) const;\
+      p0##_type p0;\
+      p1##_type p1;\
+      p2##_type p2;\
+      p3##_type p3;\
+      p4##_type p4;\
+      p5##_type p5;\
+      p6##_type p6;\
+    };\
+    template <typename F> operator ::testing::Action<F>() const {\
+      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4, p5, \
+          p6));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+    p2##_type p2;\
+    p3##_type p3;\
+    p4##_type p4;\
+    p5##_type p5;\
+    p6##_type p6;\
+  };\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type>\
+  inline name##ActionP7<p0##_type, p1##_type, p2##_type, p3##_type, \
+      p4##_type, p5##_type, p6##_type> name(p0##_type p0, p1##_type p1, \
+      p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \
+      p6##_type p6) {\
+    return name##ActionP7<p0##_type, p1##_type, p2##_type, p3##_type, \
+        p4##_type, p5##_type, p6##_type>(p0, p1, p2, p3, p4, p5, p6);\
+  }\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type>\
+  template <typename F>\
+  template <typename arg0_type, typename arg1_type, typename arg2_type, \
+      typename arg3_type, typename arg4_type, typename arg5_type, \
+      typename arg6_type, typename arg7_type, typename arg8_type, \
+      typename arg9_type>\
+  typename ::testing::internal::Function<F>::Result\
+      name##ActionP7<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+          p5##_type, p6##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P8(name, p0, p1, p2, p3, p4, p5, p6, p7)\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type>\
+  class name##ActionP8 {\
+   public:\
+    name##ActionP8(p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+        p5##_type gmock_p5, p6##_type gmock_p6, \
+        p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+        p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
+        p7(gmock_p7) {}\
+    template <typename F>\
+    class gmock_Impl : public ::testing::ActionInterface<F> {\
+     public:\
+      typedef F function_type;\
+      typedef typename ::testing::internal::Function<F>::Result return_type;\
+      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+          args_type;\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+          p6##_type gmock_p6, p7##_type gmock_p7) : p0(gmock_p0), \
+          p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), \
+          p5(gmock_p5), p6(gmock_p6), p7(gmock_p7) {}\
+      virtual return_type Perform(const args_type& args) {\
+        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+            Perform(this, args);\
+      }\
+      template <typename arg0_type, typename arg1_type, typename arg2_type, \
+          typename arg3_type, typename arg4_type, typename arg5_type, \
+          typename arg6_type, typename arg7_type, typename arg8_type, \
+          typename arg9_type>\
+      return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+          arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+          arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+          arg9_type arg9) const;\
+      p0##_type p0;\
+      p1##_type p1;\
+      p2##_type p2;\
+      p3##_type p3;\
+      p4##_type p4;\
+      p5##_type p5;\
+      p6##_type p6;\
+      p7##_type p7;\
+    };\
+    template <typename F> operator ::testing::Action<F>() const {\
+      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4, p5, \
+          p6, p7));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+    p2##_type p2;\
+    p3##_type p3;\
+    p4##_type p4;\
+    p5##_type p5;\
+    p6##_type p6;\
+    p7##_type p7;\
+  };\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type>\
+  inline name##ActionP8<p0##_type, p1##_type, p2##_type, p3##_type, \
+      p4##_type, p5##_type, p6##_type, p7##_type> name(p0##_type p0, \
+      p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \
+      p6##_type p6, p7##_type p7) {\
+    return name##ActionP8<p0##_type, p1##_type, p2##_type, p3##_type, \
+        p4##_type, p5##_type, p6##_type, p7##_type>(p0, p1, p2, p3, p4, p5, \
+        p6, p7);\
+  }\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type>\
+  template <typename F>\
+  template <typename arg0_type, typename arg1_type, typename arg2_type, \
+      typename arg3_type, typename arg4_type, typename arg5_type, \
+      typename arg6_type, typename arg7_type, typename arg8_type, \
+      typename arg9_type>\
+  typename ::testing::internal::Function<F>::Result\
+      name##ActionP8<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+          p5##_type, p6##_type, \
+          p7##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8)\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type, typename p8##_type>\
+  class name##ActionP9 {\
+   public:\
+    name##ActionP9(p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+        p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \
+        p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+        p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
+        p8(gmock_p8) {}\
+    template <typename F>\
+    class gmock_Impl : public ::testing::ActionInterface<F> {\
+     public:\
+      typedef F function_type;\
+      typedef typename ::testing::internal::Function<F>::Result return_type;\
+      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+          args_type;\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+          p6##_type gmock_p6, p7##_type gmock_p7, \
+          p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+          p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
+          p7(gmock_p7), p8(gmock_p8) {}\
+      virtual return_type Perform(const args_type& args) {\
+        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+            Perform(this, args);\
+      }\
+      template <typename arg0_type, typename arg1_type, typename arg2_type, \
+          typename arg3_type, typename arg4_type, typename arg5_type, \
+          typename arg6_type, typename arg7_type, typename arg8_type, \
+          typename arg9_type>\
+      return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+          arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+          arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+          arg9_type arg9) const;\
+      p0##_type p0;\
+      p1##_type p1;\
+      p2##_type p2;\
+      p3##_type p3;\
+      p4##_type p4;\
+      p5##_type p5;\
+      p6##_type p6;\
+      p7##_type p7;\
+      p8##_type p8;\
+    };\
+    template <typename F> operator ::testing::Action<F>() const {\
+      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4, p5, \
+          p6, p7, p8));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+    p2##_type p2;\
+    p3##_type p3;\
+    p4##_type p4;\
+    p5##_type p5;\
+    p6##_type p6;\
+    p7##_type p7;\
+    p8##_type p8;\
+  };\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type, typename p8##_type>\
+  inline name##ActionP9<p0##_type, p1##_type, p2##_type, p3##_type, \
+      p4##_type, p5##_type, p6##_type, p7##_type, \
+      p8##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+      p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, \
+      p8##_type p8) {\
+    return name##ActionP9<p0##_type, p1##_type, p2##_type, p3##_type, \
+        p4##_type, p5##_type, p6##_type, p7##_type, p8##_type>(p0, p1, p2, \
+        p3, p4, p5, p6, p7, p8);\
+  }\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type, typename p8##_type>\
+  template <typename F>\
+  template <typename arg0_type, typename arg1_type, typename arg2_type, \
+      typename arg3_type, typename arg4_type, typename arg5_type, \
+      typename arg6_type, typename arg7_type, typename arg8_type, \
+      typename arg9_type>\
+  typename ::testing::internal::Function<F>::Result\
+      name##ActionP9<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+          p5##_type, p6##_type, p7##_type, \
+          p8##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type, typename p8##_type, \
+      typename p9##_type>\
+  class name##ActionP10 {\
+   public:\
+    name##ActionP10(p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+        p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \
+        p8##_type gmock_p8, p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), \
+        p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
+        p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {}\
+    template <typename F>\
+    class gmock_Impl : public ::testing::ActionInterface<F> {\
+     public:\
+      typedef F function_type;\
+      typedef typename ::testing::internal::Function<F>::Result return_type;\
+      typedef typename ::testing::internal::Function<F>::ArgumentTuple\
+          args_type;\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+          p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \
+          p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+          p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
+          p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {}\
+      virtual return_type Perform(const args_type& args) {\
+        return ::testing::internal::ActionHelper<return_type, gmock_Impl>::\
+            Perform(this, args);\
+      }\
+      template <typename arg0_type, typename arg1_type, typename arg2_type, \
+          typename arg3_type, typename arg4_type, typename arg5_type, \
+          typename arg6_type, typename arg7_type, typename arg8_type, \
+          typename arg9_type>\
+      return_type gmock_PerformImpl(const args_type& args, arg0_type arg0, \
+          arg1_type arg1, arg2_type arg2, arg3_type arg3, arg4_type arg4, \
+          arg5_type arg5, arg6_type arg6, arg7_type arg7, arg8_type arg8, \
+          arg9_type arg9) const;\
+      p0##_type p0;\
+      p1##_type p1;\
+      p2##_type p2;\
+      p3##_type p3;\
+      p4##_type p4;\
+      p5##_type p5;\
+      p6##_type p6;\
+      p7##_type p7;\
+      p8##_type p8;\
+      p9##_type p9;\
+    };\
+    template <typename F> operator ::testing::Action<F>() const {\
+      return ::testing::Action<F>(new gmock_Impl<F>(p0, p1, p2, p3, p4, p5, \
+          p6, p7, p8, p9));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+    p2##_type p2;\
+    p3##_type p3;\
+    p4##_type p4;\
+    p5##_type p5;\
+    p6##_type p6;\
+    p7##_type p7;\
+    p8##_type p8;\
+    p9##_type p9;\
+  };\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type, typename p8##_type, \
+      typename p9##_type>\
+  inline name##ActionP10<p0##_type, p1##_type, p2##_type, p3##_type, \
+      p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, \
+      p9##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+      p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \
+      p9##_type p9) {\
+    return name##ActionP10<p0##_type, p1##_type, p2##_type, p3##_type, \
+        p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, p9##_type>(p0, \
+        p1, p2, p3, p4, p5, p6, p7, p8, p9);\
+  }\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type, typename p8##_type, \
+      typename p9##_type>\
+  template <typename F>\
+  template <typename arg0_type, typename arg1_type, typename arg2_type, \
+      typename arg3_type, typename arg4_type, typename arg5_type, \
+      typename arg6_type, typename arg7_type, typename arg8_type, \
+      typename arg9_type>\
+  typename ::testing::internal::Function<F>::Result\
+      name##ActionP10<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+          p5##_type, p6##_type, p7##_type, p8##_type, \
+          p9##_type>::gmock_Impl<F>::gmock_PerformImpl(\
+          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+// TODO(wan@google.com): move the following to a different .h file
+// such that we don't have to run 'pump' every time the code is
+// updated.
+namespace testing {
+
+// Various overloads for InvokeArgument<N>().
+//
+// The InvokeArgument<N>(a1, a2, ..., a_k) action invokes the N-th
+// (0-based) argument, which must be a k-ary callable, of the mock
+// function, with arguments a1, a2, ..., a_k.
+//
+// Notes:
+//
+//   1. The arguments are passed by value by default.  If you need to
+//   pass an argument by reference, wrap it inside ByRef().  For
+//   example,
+//
+//     InvokeArgument<1>(5, string("Hello"), ByRef(foo))
+//
+//   passes 5 and string("Hello") by value, and passes foo by
+//   reference.
+//
+//   2. If the callable takes an argument by reference but ByRef() is
+//   not used, it will receive the reference to a copy of the value,
+//   instead of the original value.  For example, when the 0-th
+//   argument of the mock function takes a const string&, the action
+//
+//     InvokeArgument<0>(string("Hello"))
+//
+//   makes a copy of the temporary string("Hello") object and passes a
+//   reference of the copy, instead of the original temporary object,
+//   to the callable.  This makes it easy for a user to define an
+//   InvokeArgument action from temporary values and have it performed
+//   later.
+
+ACTION_TEMPLATE(InvokeArgument,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_0_VALUE_PARAMS()) {
+  return internal::CallableHelper<return_type>::Call(
+      ::std::tr1::get<k>(args));
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_1_VALUE_PARAMS(p0)) {
+  return internal::CallableHelper<return_type>::Call(
+      ::std::tr1::get<k>(args), p0);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_2_VALUE_PARAMS(p0, p1)) {
+  return internal::CallableHelper<return_type>::Call(
+      ::std::tr1::get<k>(args), p0, p1);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_3_VALUE_PARAMS(p0, p1, p2)) {
+  return internal::CallableHelper<return_type>::Call(
+      ::std::tr1::get<k>(args), p0, p1, p2);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_4_VALUE_PARAMS(p0, p1, p2, p3)) {
+  return internal::CallableHelper<return_type>::Call(
+      ::std::tr1::get<k>(args), p0, p1, p2, p3);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) {
+  return internal::CallableHelper<return_type>::Call(
+      ::std::tr1::get<k>(args), p0, p1, p2, p3, p4);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) {
+  return internal::CallableHelper<return_type>::Call(
+      ::std::tr1::get<k>(args), p0, p1, p2, p3, p4, p5);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) {
+  return internal::CallableHelper<return_type>::Call(
+      ::std::tr1::get<k>(args), p0, p1, p2, p3, p4, p5, p6);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)) {
+  return internal::CallableHelper<return_type>::Call(
+      ::std::tr1::get<k>(args), p0, p1, p2, p3, p4, p5, p6, p7);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8)) {
+  return internal::CallableHelper<return_type>::Call(
+      ::std::tr1::get<k>(args), p0, p1, p2, p3, p4, p5, p6, p7, p8);
+}
+
+ACTION_TEMPLATE(InvokeArgument,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) {
+  return internal::CallableHelper<return_type>::Call(
+      ::std::tr1::get<k>(args), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
+}
+
+// Various overloads for ReturnNew<T>().
+//
+// The ReturnNew<T>(a1, a2, ..., a_k) action returns a pointer to a new
+// instance of type T, constructed on the heap with constructor arguments
+// a1, a2, ..., and a_k. The caller assumes ownership of the returned value.
+ACTION_TEMPLATE(ReturnNew,
+                HAS_1_TEMPLATE_PARAMS(typename, T),
+                AND_0_VALUE_PARAMS()) {
+  return new T();
+}
+
+ACTION_TEMPLATE(ReturnNew,
+                HAS_1_TEMPLATE_PARAMS(typename, T),
+                AND_1_VALUE_PARAMS(p0)) {
+  return new T(p0);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+                HAS_1_TEMPLATE_PARAMS(typename, T),
+                AND_2_VALUE_PARAMS(p0, p1)) {
+  return new T(p0, p1);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+                HAS_1_TEMPLATE_PARAMS(typename, T),
+                AND_3_VALUE_PARAMS(p0, p1, p2)) {
+  return new T(p0, p1, p2);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+                HAS_1_TEMPLATE_PARAMS(typename, T),
+                AND_4_VALUE_PARAMS(p0, p1, p2, p3)) {
+  return new T(p0, p1, p2, p3);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+                HAS_1_TEMPLATE_PARAMS(typename, T),
+                AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)) {
+  return new T(p0, p1, p2, p3, p4);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+                HAS_1_TEMPLATE_PARAMS(typename, T),
+                AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)) {
+  return new T(p0, p1, p2, p3, p4, p5);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+                HAS_1_TEMPLATE_PARAMS(typename, T),
+                AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)) {
+  return new T(p0, p1, p2, p3, p4, p5, p6);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+                HAS_1_TEMPLATE_PARAMS(typename, T),
+                AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)) {
+  return new T(p0, p1, p2, p3, p4, p5, p6, p7);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+                HAS_1_TEMPLATE_PARAMS(typename, T),
+                AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8)) {
+  return new T(p0, p1, p2, p3, p4, p5, p6, p7, p8);
+}
+
+ACTION_TEMPLATE(ReturnNew,
+                HAS_1_TEMPLATE_PARAMS(typename, T),
+                AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9)) {
+  return new T(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
+}
+
+}  // namespace testing
+
+#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_ACTIONS_H_
diff --git a/third_party/gmock/include/gmock/gmock-generated-function-mockers.h b/third_party/gmock/include/gmock/gmock-generated-function-mockers.h
new file mode 100644
index 0000000..9f3a956
--- /dev/null
+++ b/third_party/gmock/include/gmock/gmock-generated-function-mockers.h
@@ -0,0 +1,869 @@
+// This file was GENERATED by a script.  DO NOT EDIT BY HAND!!!
+
+// Copyright 2007, 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements function mockers of various arities.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_
+
+#include <gmock/gmock-spec-builders.h>
+#include <gmock/internal/gmock-internal-utils.h>
+
+namespace testing {
+namespace internal {
+
+template <typename F>
+class FunctionMockerBase;
+
+// Note: class FunctionMocker really belongs to the ::testing
+// namespace.  However if we define it in ::testing, MSVC will
+// complain when classes in ::testing::internal declare it as a
+// friend class template.  To workaround this compiler bug, we define
+// FunctionMocker in ::testing::internal and import it into ::testing.
+template <typename F>
+class FunctionMocker;
+
+template <typename R>
+class FunctionMocker<R()> : public
+    internal::FunctionMockerBase<R()> {
+ public:
+  typedef R F();
+  typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+  MockSpec<F>& With() {
+    return this->current_spec();
+  }
+
+  R Invoke() {
+    // Even though gcc and MSVC don't enforce it, 'this->' is required
+    // by the C++ standard [14.6.4] here, as the base class type is
+    // dependent on the template argument (and thus shouldn't be
+    // looked into when resolving InvokeWith).
+    return this->InvokeWith(ArgumentTuple());
+  }
+};
+
+template <typename R, typename A1>
+class FunctionMocker<R(A1)> : public
+    internal::FunctionMockerBase<R(A1)> {
+ public:
+  typedef R F(A1);
+  typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+  MockSpec<F>& With(const Matcher<A1>& m1) {
+    this->current_spec().SetMatchers(::std::tr1::make_tuple(m1));
+    return this->current_spec();
+  }
+
+  R Invoke(A1 a1) {
+    // Even though gcc and MSVC don't enforce it, 'this->' is required
+    // by the C++ standard [14.6.4] here, as the base class type is
+    // dependent on the template argument (and thus shouldn't be
+    // looked into when resolving InvokeWith).
+    return this->InvokeWith(ArgumentTuple(a1));
+  }
+};
+
+template <typename R, typename A1, typename A2>
+class FunctionMocker<R(A1, A2)> : public
+    internal::FunctionMockerBase<R(A1, A2)> {
+ public:
+  typedef R F(A1, A2);
+  typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+  MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2) {
+    this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2));
+    return this->current_spec();
+  }
+
+  R Invoke(A1 a1, A2 a2) {
+    // Even though gcc and MSVC don't enforce it, 'this->' is required
+    // by the C++ standard [14.6.4] here, as the base class type is
+    // dependent on the template argument (and thus shouldn't be
+    // looked into when resolving InvokeWith).
+    return this->InvokeWith(ArgumentTuple(a1, a2));
+  }
+};
+
+template <typename R, typename A1, typename A2, typename A3>
+class FunctionMocker<R(A1, A2, A3)> : public
+    internal::FunctionMockerBase<R(A1, A2, A3)> {
+ public:
+  typedef R F(A1, A2, A3);
+  typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+  MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
+      const Matcher<A3>& m3) {
+    this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2, m3));
+    return this->current_spec();
+  }
+
+  R Invoke(A1 a1, A2 a2, A3 a3) {
+    // Even though gcc and MSVC don't enforce it, 'this->' is required
+    // by the C++ standard [14.6.4] here, as the base class type is
+    // dependent on the template argument (and thus shouldn't be
+    // looked into when resolving InvokeWith).
+    return this->InvokeWith(ArgumentTuple(a1, a2, a3));
+  }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+class FunctionMocker<R(A1, A2, A3, A4)> : public
+    internal::FunctionMockerBase<R(A1, A2, A3, A4)> {
+ public:
+  typedef R F(A1, A2, A3, A4);
+  typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+  MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
+      const Matcher<A3>& m3, const Matcher<A4>& m4) {
+    this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2, m3, m4));
+    return this->current_spec();
+  }
+
+  R Invoke(A1 a1, A2 a2, A3 a3, A4 a4) {
+    // Even though gcc and MSVC don't enforce it, 'this->' is required
+    // by the C++ standard [14.6.4] here, as the base class type is
+    // dependent on the template argument (and thus shouldn't be
+    // looked into when resolving InvokeWith).
+    return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4));
+  }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5>
+class FunctionMocker<R(A1, A2, A3, A4, A5)> : public
+    internal::FunctionMockerBase<R(A1, A2, A3, A4, A5)> {
+ public:
+  typedef R F(A1, A2, A3, A4, A5);
+  typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+  MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
+      const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5) {
+    this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2, m3, m4,
+        m5));
+    return this->current_spec();
+  }
+
+  R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) {
+    // Even though gcc and MSVC don't enforce it, 'this->' is required
+    // by the C++ standard [14.6.4] here, as the base class type is
+    // dependent on the template argument (and thus shouldn't be
+    // looked into when resolving InvokeWith).
+    return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5));
+  }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6>
+class FunctionMocker<R(A1, A2, A3, A4, A5, A6)> : public
+    internal::FunctionMockerBase<R(A1, A2, A3, A4, A5, A6)> {
+ public:
+  typedef R F(A1, A2, A3, A4, A5, A6);
+  typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+  MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
+      const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5,
+      const Matcher<A6>& m6) {
+    this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2, m3, m4, m5,
+        m6));
+    return this->current_spec();
+  }
+
+  R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) {
+    // Even though gcc and MSVC don't enforce it, 'this->' is required
+    // by the C++ standard [14.6.4] here, as the base class type is
+    // dependent on the template argument (and thus shouldn't be
+    // looked into when resolving InvokeWith).
+    return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6));
+  }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7>
+class FunctionMocker<R(A1, A2, A3, A4, A5, A6, A7)> : public
+    internal::FunctionMockerBase<R(A1, A2, A3, A4, A5, A6, A7)> {
+ public:
+  typedef R F(A1, A2, A3, A4, A5, A6, A7);
+  typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+  MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
+      const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5,
+      const Matcher<A6>& m6, const Matcher<A7>& m7) {
+    this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2, m3, m4, m5,
+        m6, m7));
+    return this->current_spec();
+  }
+
+  R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) {
+    // Even though gcc and MSVC don't enforce it, 'this->' is required
+    // by the C++ standard [14.6.4] here, as the base class type is
+    // dependent on the template argument (and thus shouldn't be
+    // looked into when resolving InvokeWith).
+    return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7));
+  }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8>
+class FunctionMocker<R(A1, A2, A3, A4, A5, A6, A7, A8)> : public
+    internal::FunctionMockerBase<R(A1, A2, A3, A4, A5, A6, A7, A8)> {
+ public:
+  typedef R F(A1, A2, A3, A4, A5, A6, A7, A8);
+  typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+  MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
+      const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5,
+      const Matcher<A6>& m6, const Matcher<A7>& m7, const Matcher<A8>& m8) {
+    this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2, m3, m4, m5,
+        m6, m7, m8));
+    return this->current_spec();
+  }
+
+  R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) {
+    // Even though gcc and MSVC don't enforce it, 'this->' is required
+    // by the C++ standard [14.6.4] here, as the base class type is
+    // dependent on the template argument (and thus shouldn't be
+    // looked into when resolving InvokeWith).
+    return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7, a8));
+  }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8, typename A9>
+class FunctionMocker<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)> : public
+    internal::FunctionMockerBase<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)> {
+ public:
+  typedef R F(A1, A2, A3, A4, A5, A6, A7, A8, A9);
+  typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+  MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
+      const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5,
+      const Matcher<A6>& m6, const Matcher<A7>& m7, const Matcher<A8>& m8,
+      const Matcher<A9>& m9) {
+    this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2, m3, m4, m5,
+        m6, m7, m8, m9));
+    return this->current_spec();
+  }
+
+  R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) {
+    // Even though gcc and MSVC don't enforce it, 'this->' is required
+    // by the C++ standard [14.6.4] here, as the base class type is
+    // dependent on the template argument (and thus shouldn't be
+    // looked into when resolving InvokeWith).
+    return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7, a8, a9));
+  }
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8, typename A9,
+    typename A10>
+class FunctionMocker<R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)> : public
+    internal::FunctionMockerBase<R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)> {
+ public:
+  typedef R F(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
+  typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+  MockSpec<F>& With(const Matcher<A1>& m1, const Matcher<A2>& m2,
+      const Matcher<A3>& m3, const Matcher<A4>& m4, const Matcher<A5>& m5,
+      const Matcher<A6>& m6, const Matcher<A7>& m7, const Matcher<A8>& m8,
+      const Matcher<A9>& m9, const Matcher<A10>& m10) {
+    this->current_spec().SetMatchers(::std::tr1::make_tuple(m1, m2, m3, m4, m5,
+        m6, m7, m8, m9, m10));
+    return this->current_spec();
+  }
+
+  R Invoke(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9,
+      A10 a10) {
+    // Even though gcc and MSVC don't enforce it, 'this->' is required
+    // by the C++ standard [14.6.4] here, as the base class type is
+    // dependent on the template argument (and thus shouldn't be
+    // looked into when resolving InvokeWith).
+    return this->InvokeWith(ArgumentTuple(a1, a2, a3, a4, a5, a6, a7, a8, a9,
+        a10));
+  }
+};
+
+}  // namespace internal
+
+// The style guide prohibits "using" statements in a namespace scope
+// inside a header file.  However, the FunctionMocker class template
+// is meant to be defined in the ::testing namespace.  The following
+// line is just a trick for working around a bug in MSVC 8.0, which
+// cannot handle it if we define FunctionMocker in ::testing.
+using internal::FunctionMocker;
+
+// The result type of function type F.
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_RESULT_(tn, F) tn ::testing::internal::Function<F>::Result
+
+// The type of argument N of function type F.
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_ARG_(tn, F, N) tn ::testing::internal::Function<F>::Argument##N
+
+// The matcher type for argument N of function type F.
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_MATCHER_(tn, F, N) const ::testing::Matcher<GMOCK_ARG_(tn, F, N)>&
+
+// The variable for mocking the given method.
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_MOCKER_(arity, constness, Method) \
+    GMOCK_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD0_(tn, constness, ct, Method, F) \
+  GMOCK_RESULT_(tn, F) ct Method() constness { \
+    GMOCK_COMPILE_ASSERT_(::std::tr1::tuple_size< \
+        tn ::testing::internal::Function<F>::ArgumentTuple>::value == 0, \
+        this_method_does_not_take_0_arguments); \
+    GMOCK_MOCKER_(0, constness, Method).SetOwnerAndName(this, #Method); \
+    return GMOCK_MOCKER_(0, constness, Method).Invoke(); \
+  } \
+  ::testing::MockSpec<F>& \
+      gmock_##Method() constness { \
+    return GMOCK_MOCKER_(0, constness, Method).RegisterOwner(this).With(); \
+  } \
+  mutable ::testing::FunctionMocker<F> GMOCK_MOCKER_(0, constness, Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD1_(tn, constness, ct, Method, F) \
+  GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1) constness { \
+    GMOCK_COMPILE_ASSERT_(::std::tr1::tuple_size< \
+        tn ::testing::internal::Function<F>::ArgumentTuple>::value == 1, \
+        this_method_does_not_take_1_argument); \
+    GMOCK_MOCKER_(1, constness, Method).SetOwnerAndName(this, #Method); \
+    return GMOCK_MOCKER_(1, constness, Method).Invoke(gmock_a1); \
+  } \
+  ::testing::MockSpec<F>& \
+      gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1) constness { \
+    return GMOCK_MOCKER_(1, constness, \
+        Method).RegisterOwner(this).With(gmock_a1); \
+  } \
+  mutable ::testing::FunctionMocker<F> GMOCK_MOCKER_(1, constness, Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD2_(tn, constness, ct, Method, F) \
+  GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \
+                                 GMOCK_ARG_(tn, F, 2) gmock_a2) constness { \
+    GMOCK_COMPILE_ASSERT_(::std::tr1::tuple_size< \
+        tn ::testing::internal::Function<F>::ArgumentTuple>::value == 2, \
+        this_method_does_not_take_2_arguments); \
+    GMOCK_MOCKER_(2, constness, Method).SetOwnerAndName(this, #Method); \
+    return GMOCK_MOCKER_(2, constness, Method).Invoke(gmock_a1, gmock_a2); \
+  } \
+  ::testing::MockSpec<F>& \
+      gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \
+                     GMOCK_MATCHER_(tn, F, 2) gmock_a2) constness { \
+    return GMOCK_MOCKER_(2, constness, \
+        Method).RegisterOwner(this).With(gmock_a1, gmock_a2); \
+  } \
+  mutable ::testing::FunctionMocker<F> GMOCK_MOCKER_(2, constness, Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD3_(tn, constness, ct, Method, F) \
+  GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \
+                                 GMOCK_ARG_(tn, F, 2) gmock_a2, \
+                                 GMOCK_ARG_(tn, F, 3) gmock_a3) constness { \
+    GMOCK_COMPILE_ASSERT_(::std::tr1::tuple_size< \
+        tn ::testing::internal::Function<F>::ArgumentTuple>::value == 3, \
+        this_method_does_not_take_3_arguments); \
+    GMOCK_MOCKER_(3, constness, Method).SetOwnerAndName(this, #Method); \
+    return GMOCK_MOCKER_(3, constness, Method).Invoke(gmock_a1, gmock_a2, \
+        gmock_a3); \
+  } \
+  ::testing::MockSpec<F>& \
+      gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \
+                     GMOCK_MATCHER_(tn, F, 2) gmock_a2, \
+                     GMOCK_MATCHER_(tn, F, 3) gmock_a3) constness { \
+    return GMOCK_MOCKER_(3, constness, \
+        Method).RegisterOwner(this).With(gmock_a1, gmock_a2, gmock_a3); \
+  } \
+  mutable ::testing::FunctionMocker<F> GMOCK_MOCKER_(3, constness, Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD4_(tn, constness, ct, Method, F) \
+  GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \
+                                 GMOCK_ARG_(tn, F, 2) gmock_a2, \
+                                 GMOCK_ARG_(tn, F, 3) gmock_a3, \
+                                 GMOCK_ARG_(tn, F, 4) gmock_a4) constness { \
+    GMOCK_COMPILE_ASSERT_(::std::tr1::tuple_size< \
+        tn ::testing::internal::Function<F>::ArgumentTuple>::value == 4, \
+        this_method_does_not_take_4_arguments); \
+    GMOCK_MOCKER_(4, constness, Method).SetOwnerAndName(this, #Method); \
+    return GMOCK_MOCKER_(4, constness, Method).Invoke(gmock_a1, gmock_a2, \
+        gmock_a3, gmock_a4); \
+  } \
+  ::testing::MockSpec<F>& \
+      gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \
+                     GMOCK_MATCHER_(tn, F, 2) gmock_a2, \
+                     GMOCK_MATCHER_(tn, F, 3) gmock_a3, \
+                     GMOCK_MATCHER_(tn, F, 4) gmock_a4) constness { \
+    return GMOCK_MOCKER_(4, constness, \
+        Method).RegisterOwner(this).With(gmock_a1, gmock_a2, gmock_a3, \
+        gmock_a4); \
+  } \
+  mutable ::testing::FunctionMocker<F> GMOCK_MOCKER_(4, constness, Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD5_(tn, constness, ct, Method, F) \
+  GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \
+                                 GMOCK_ARG_(tn, F, 2) gmock_a2, \
+                                 GMOCK_ARG_(tn, F, 3) gmock_a3, \
+                                 GMOCK_ARG_(tn, F, 4) gmock_a4, \
+                                 GMOCK_ARG_(tn, F, 5) gmock_a5) constness { \
+    GMOCK_COMPILE_ASSERT_(::std::tr1::tuple_size< \
+        tn ::testing::internal::Function<F>::ArgumentTuple>::value == 5, \
+        this_method_does_not_take_5_arguments); \
+    GMOCK_MOCKER_(5, constness, Method).SetOwnerAndName(this, #Method); \
+    return GMOCK_MOCKER_(5, constness, Method).Invoke(gmock_a1, gmock_a2, \
+        gmock_a3, gmock_a4, gmock_a5); \
+  } \
+  ::testing::MockSpec<F>& \
+      gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \
+                     GMOCK_MATCHER_(tn, F, 2) gmock_a2, \
+                     GMOCK_MATCHER_(tn, F, 3) gmock_a3, \
+                     GMOCK_MATCHER_(tn, F, 4) gmock_a4, \
+                     GMOCK_MATCHER_(tn, F, 5) gmock_a5) constness { \
+    return GMOCK_MOCKER_(5, constness, \
+        Method).RegisterOwner(this).With(gmock_a1, gmock_a2, gmock_a3, \
+        gmock_a4, gmock_a5); \
+  } \
+  mutable ::testing::FunctionMocker<F> GMOCK_MOCKER_(5, constness, Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD6_(tn, constness, ct, Method, F) \
+  GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \
+                                 GMOCK_ARG_(tn, F, 2) gmock_a2, \
+                                 GMOCK_ARG_(tn, F, 3) gmock_a3, \
+                                 GMOCK_ARG_(tn, F, 4) gmock_a4, \
+                                 GMOCK_ARG_(tn, F, 5) gmock_a5, \
+                                 GMOCK_ARG_(tn, F, 6) gmock_a6) constness { \
+    GMOCK_COMPILE_ASSERT_(::std::tr1::tuple_size< \
+        tn ::testing::internal::Function<F>::ArgumentTuple>::value == 6, \
+        this_method_does_not_take_6_arguments); \
+    GMOCK_MOCKER_(6, constness, Method).SetOwnerAndName(this, #Method); \
+    return GMOCK_MOCKER_(6, constness, Method).Invoke(gmock_a1, gmock_a2, \
+        gmock_a3, gmock_a4, gmock_a5, gmock_a6); \
+  } \
+  ::testing::MockSpec<F>& \
+      gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \
+                     GMOCK_MATCHER_(tn, F, 2) gmock_a2, \
+                     GMOCK_MATCHER_(tn, F, 3) gmock_a3, \
+                     GMOCK_MATCHER_(tn, F, 4) gmock_a4, \
+                     GMOCK_MATCHER_(tn, F, 5) gmock_a5, \
+                     GMOCK_MATCHER_(tn, F, 6) gmock_a6) constness { \
+    return GMOCK_MOCKER_(6, constness, \
+        Method).RegisterOwner(this).With(gmock_a1, gmock_a2, gmock_a3, \
+        gmock_a4, gmock_a5, gmock_a6); \
+  } \
+  mutable ::testing::FunctionMocker<F> GMOCK_MOCKER_(6, constness, Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD7_(tn, constness, ct, Method, F) \
+  GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \
+                                 GMOCK_ARG_(tn, F, 2) gmock_a2, \
+                                 GMOCK_ARG_(tn, F, 3) gmock_a3, \
+                                 GMOCK_ARG_(tn, F, 4) gmock_a4, \
+                                 GMOCK_ARG_(tn, F, 5) gmock_a5, \
+                                 GMOCK_ARG_(tn, F, 6) gmock_a6, \
+                                 GMOCK_ARG_(tn, F, 7) gmock_a7) constness { \
+    GMOCK_COMPILE_ASSERT_(::std::tr1::tuple_size< \
+        tn ::testing::internal::Function<F>::ArgumentTuple>::value == 7, \
+        this_method_does_not_take_7_arguments); \
+    GMOCK_MOCKER_(7, constness, Method).SetOwnerAndName(this, #Method); \
+    return GMOCK_MOCKER_(7, constness, Method).Invoke(gmock_a1, gmock_a2, \
+        gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7); \
+  } \
+  ::testing::MockSpec<F>& \
+      gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \
+                     GMOCK_MATCHER_(tn, F, 2) gmock_a2, \
+                     GMOCK_MATCHER_(tn, F, 3) gmock_a3, \
+                     GMOCK_MATCHER_(tn, F, 4) gmock_a4, \
+                     GMOCK_MATCHER_(tn, F, 5) gmock_a5, \
+                     GMOCK_MATCHER_(tn, F, 6) gmock_a6, \
+                     GMOCK_MATCHER_(tn, F, 7) gmock_a7) constness { \
+    return GMOCK_MOCKER_(7, constness, \
+        Method).RegisterOwner(this).With(gmock_a1, gmock_a2, gmock_a3, \
+        gmock_a4, gmock_a5, gmock_a6, gmock_a7); \
+  } \
+  mutable ::testing::FunctionMocker<F> GMOCK_MOCKER_(7, constness, Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD8_(tn, constness, ct, Method, F) \
+  GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \
+                                 GMOCK_ARG_(tn, F, 2) gmock_a2, \
+                                 GMOCK_ARG_(tn, F, 3) gmock_a3, \
+                                 GMOCK_ARG_(tn, F, 4) gmock_a4, \
+                                 GMOCK_ARG_(tn, F, 5) gmock_a5, \
+                                 GMOCK_ARG_(tn, F, 6) gmock_a6, \
+                                 GMOCK_ARG_(tn, F, 7) gmock_a7, \
+                                 GMOCK_ARG_(tn, F, 8) gmock_a8) constness { \
+    GMOCK_COMPILE_ASSERT_(::std::tr1::tuple_size< \
+        tn ::testing::internal::Function<F>::ArgumentTuple>::value == 8, \
+        this_method_does_not_take_8_arguments); \
+    GMOCK_MOCKER_(8, constness, Method).SetOwnerAndName(this, #Method); \
+    return GMOCK_MOCKER_(8, constness, Method).Invoke(gmock_a1, gmock_a2, \
+        gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8); \
+  } \
+  ::testing::MockSpec<F>& \
+      gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \
+                     GMOCK_MATCHER_(tn, F, 2) gmock_a2, \
+                     GMOCK_MATCHER_(tn, F, 3) gmock_a3, \
+                     GMOCK_MATCHER_(tn, F, 4) gmock_a4, \
+                     GMOCK_MATCHER_(tn, F, 5) gmock_a5, \
+                     GMOCK_MATCHER_(tn, F, 6) gmock_a6, \
+                     GMOCK_MATCHER_(tn, F, 7) gmock_a7, \
+                     GMOCK_MATCHER_(tn, F, 8) gmock_a8) constness { \
+    return GMOCK_MOCKER_(8, constness, \
+        Method).RegisterOwner(this).With(gmock_a1, gmock_a2, gmock_a3, \
+        gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8); \
+  } \
+  mutable ::testing::FunctionMocker<F> GMOCK_MOCKER_(8, constness, Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD9_(tn, constness, ct, Method, F) \
+  GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \
+                                 GMOCK_ARG_(tn, F, 2) gmock_a2, \
+                                 GMOCK_ARG_(tn, F, 3) gmock_a3, \
+                                 GMOCK_ARG_(tn, F, 4) gmock_a4, \
+                                 GMOCK_ARG_(tn, F, 5) gmock_a5, \
+                                 GMOCK_ARG_(tn, F, 6) gmock_a6, \
+                                 GMOCK_ARG_(tn, F, 7) gmock_a7, \
+                                 GMOCK_ARG_(tn, F, 8) gmock_a8, \
+                                 GMOCK_ARG_(tn, F, 9) gmock_a9) constness { \
+    GMOCK_COMPILE_ASSERT_(::std::tr1::tuple_size< \
+        tn ::testing::internal::Function<F>::ArgumentTuple>::value == 9, \
+        this_method_does_not_take_9_arguments); \
+    GMOCK_MOCKER_(9, constness, Method).SetOwnerAndName(this, #Method); \
+    return GMOCK_MOCKER_(9, constness, Method).Invoke(gmock_a1, gmock_a2, \
+        gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, \
+        gmock_a9); \
+  } \
+  ::testing::MockSpec<F>& \
+      gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \
+                     GMOCK_MATCHER_(tn, F, 2) gmock_a2, \
+                     GMOCK_MATCHER_(tn, F, 3) gmock_a3, \
+                     GMOCK_MATCHER_(tn, F, 4) gmock_a4, \
+                     GMOCK_MATCHER_(tn, F, 5) gmock_a5, \
+                     GMOCK_MATCHER_(tn, F, 6) gmock_a6, \
+                     GMOCK_MATCHER_(tn, F, 7) gmock_a7, \
+                     GMOCK_MATCHER_(tn, F, 8) gmock_a8, \
+                     GMOCK_MATCHER_(tn, F, 9) gmock_a9) constness { \
+    return GMOCK_MOCKER_(9, constness, \
+        Method).RegisterOwner(this).With(gmock_a1, gmock_a2, gmock_a3, \
+        gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, gmock_a9); \
+  } \
+  mutable ::testing::FunctionMocker<F> GMOCK_MOCKER_(9, constness, Method)
+
+// INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
+#define GMOCK_METHOD10_(tn, constness, ct, Method, F) \
+  GMOCK_RESULT_(tn, F) ct Method(GMOCK_ARG_(tn, F, 1) gmock_a1, \
+                                 GMOCK_ARG_(tn, F, 2) gmock_a2, \
+                                 GMOCK_ARG_(tn, F, 3) gmock_a3, \
+                                 GMOCK_ARG_(tn, F, 4) gmock_a4, \
+                                 GMOCK_ARG_(tn, F, 5) gmock_a5, \
+                                 GMOCK_ARG_(tn, F, 6) gmock_a6, \
+                                 GMOCK_ARG_(tn, F, 7) gmock_a7, \
+                                 GMOCK_ARG_(tn, F, 8) gmock_a8, \
+                                 GMOCK_ARG_(tn, F, 9) gmock_a9, \
+                                 GMOCK_ARG_(tn, F, 10) gmock_a10) constness { \
+    GMOCK_COMPILE_ASSERT_(::std::tr1::tuple_size< \
+        tn ::testing::internal::Function<F>::ArgumentTuple>::value == 10, \
+        this_method_does_not_take_10_arguments); \
+    GMOCK_MOCKER_(10, constness, Method).SetOwnerAndName(this, #Method); \
+    return GMOCK_MOCKER_(10, constness, Method).Invoke(gmock_a1, gmock_a2, \
+        gmock_a3, gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, gmock_a9, \
+        gmock_a10); \
+  } \
+  ::testing::MockSpec<F>& \
+      gmock_##Method(GMOCK_MATCHER_(tn, F, 1) gmock_a1, \
+                     GMOCK_MATCHER_(tn, F, 2) gmock_a2, \
+                     GMOCK_MATCHER_(tn, F, 3) gmock_a3, \
+                     GMOCK_MATCHER_(tn, F, 4) gmock_a4, \
+                     GMOCK_MATCHER_(tn, F, 5) gmock_a5, \
+                     GMOCK_MATCHER_(tn, F, 6) gmock_a6, \
+                     GMOCK_MATCHER_(tn, F, 7) gmock_a7, \
+                     GMOCK_MATCHER_(tn, F, 8) gmock_a8, \
+                     GMOCK_MATCHER_(tn, F, 9) gmock_a9, \
+                     GMOCK_MATCHER_(tn, F, 10) gmock_a10) constness { \
+    return GMOCK_MOCKER_(10, constness, \
+        Method).RegisterOwner(this).With(gmock_a1, gmock_a2, gmock_a3, \
+        gmock_a4, gmock_a5, gmock_a6, gmock_a7, gmock_a8, gmock_a9, \
+        gmock_a10); \
+  } \
+  mutable ::testing::FunctionMocker<F> GMOCK_MOCKER_(10, constness, Method)
+
+#define MOCK_METHOD0(m, F) GMOCK_METHOD0_(, , , m, F)
+#define MOCK_METHOD1(m, F) GMOCK_METHOD1_(, , , m, F)
+#define MOCK_METHOD2(m, F) GMOCK_METHOD2_(, , , m, F)
+#define MOCK_METHOD3(m, F) GMOCK_METHOD3_(, , , m, F)
+#define MOCK_METHOD4(m, F) GMOCK_METHOD4_(, , , m, F)
+#define MOCK_METHOD5(m, F) GMOCK_METHOD5_(, , , m, F)
+#define MOCK_METHOD6(m, F) GMOCK_METHOD6_(, , , m, F)
+#define MOCK_METHOD7(m, F) GMOCK_METHOD7_(, , , m, F)
+#define MOCK_METHOD8(m, F) GMOCK_METHOD8_(, , , m, F)
+#define MOCK_METHOD9(m, F) GMOCK_METHOD9_(, , , m, F)
+#define MOCK_METHOD10(m, F) GMOCK_METHOD10_(, , , m, F)
+
+#define MOCK_CONST_METHOD0(m, F) GMOCK_METHOD0_(, const, , m, F)
+#define MOCK_CONST_METHOD1(m, F) GMOCK_METHOD1_(, const, , m, F)
+#define MOCK_CONST_METHOD2(m, F) GMOCK_METHOD2_(, const, , m, F)
+#define MOCK_CONST_METHOD3(m, F) GMOCK_METHOD3_(, const, , m, F)
+#define MOCK_CONST_METHOD4(m, F) GMOCK_METHOD4_(, const, , m, F)
+#define MOCK_CONST_METHOD5(m, F) GMOCK_METHOD5_(, const, , m, F)
+#define MOCK_CONST_METHOD6(m, F) GMOCK_METHOD6_(, const, , m, F)
+#define MOCK_CONST_METHOD7(m, F) GMOCK_METHOD7_(, const, , m, F)
+#define MOCK_CONST_METHOD8(m, F) GMOCK_METHOD8_(, const, , m, F)
+#define MOCK_CONST_METHOD9(m, F) GMOCK_METHOD9_(, const, , m, F)
+#define MOCK_CONST_METHOD10(m, F) GMOCK_METHOD10_(, const, , m, F)
+
+#define MOCK_METHOD0_T(m, F) GMOCK_METHOD0_(typename, , , m, F)
+#define MOCK_METHOD1_T(m, F) GMOCK_METHOD1_(typename, , , m, F)
+#define MOCK_METHOD2_T(m, F) GMOCK_METHOD2_(typename, , , m, F)
+#define MOCK_METHOD3_T(m, F) GMOCK_METHOD3_(typename, , , m, F)
+#define MOCK_METHOD4_T(m, F) GMOCK_METHOD4_(typename, , , m, F)
+#define MOCK_METHOD5_T(m, F) GMOCK_METHOD5_(typename, , , m, F)
+#define MOCK_METHOD6_T(m, F) GMOCK_METHOD6_(typename, , , m, F)
+#define MOCK_METHOD7_T(m, F) GMOCK_METHOD7_(typename, , , m, F)
+#define MOCK_METHOD8_T(m, F) GMOCK_METHOD8_(typename, , , m, F)
+#define MOCK_METHOD9_T(m, F) GMOCK_METHOD9_(typename, , , m, F)
+#define MOCK_METHOD10_T(m, F) GMOCK_METHOD10_(typename, , , m, F)
+
+#define MOCK_CONST_METHOD0_T(m, F) GMOCK_METHOD0_(typename, const, , m, F)
+#define MOCK_CONST_METHOD1_T(m, F) GMOCK_METHOD1_(typename, const, , m, F)
+#define MOCK_CONST_METHOD2_T(m, F) GMOCK_METHOD2_(typename, const, , m, F)
+#define MOCK_CONST_METHOD3_T(m, F) GMOCK_METHOD3_(typename, const, , m, F)
+#define MOCK_CONST_METHOD4_T(m, F) GMOCK_METHOD4_(typename, const, , m, F)
+#define MOCK_CONST_METHOD5_T(m, F) GMOCK_METHOD5_(typename, const, , m, F)
+#define MOCK_CONST_METHOD6_T(m, F) GMOCK_METHOD6_(typename, const, , m, F)
+#define MOCK_CONST_METHOD7_T(m, F) GMOCK_METHOD7_(typename, const, , m, F)
+#define MOCK_CONST_METHOD8_T(m, F) GMOCK_METHOD8_(typename, const, , m, F)
+#define MOCK_CONST_METHOD9_T(m, F) GMOCK_METHOD9_(typename, const, , m, F)
+#define MOCK_CONST_METHOD10_T(m, F) GMOCK_METHOD10_(typename, const, , m, F)
+
+#define MOCK_METHOD0_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD0_(, , ct, m, F)
+#define MOCK_METHOD1_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD1_(, , ct, m, F)
+#define MOCK_METHOD2_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD2_(, , ct, m, F)
+#define MOCK_METHOD3_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD3_(, , ct, m, F)
+#define MOCK_METHOD4_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD4_(, , ct, m, F)
+#define MOCK_METHOD5_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD5_(, , ct, m, F)
+#define MOCK_METHOD6_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD6_(, , ct, m, F)
+#define MOCK_METHOD7_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD7_(, , ct, m, F)
+#define MOCK_METHOD8_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD8_(, , ct, m, F)
+#define MOCK_METHOD9_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD9_(, , ct, m, F)
+#define MOCK_METHOD10_WITH_CALLTYPE(ct, m, F) GMOCK_METHOD10_(, , ct, m, F)
+
+#define MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD0_(, const, ct, m, F)
+#define MOCK_CONST_METHOD1_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD1_(, const, ct, m, F)
+#define MOCK_CONST_METHOD2_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD2_(, const, ct, m, F)
+#define MOCK_CONST_METHOD3_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD3_(, const, ct, m, F)
+#define MOCK_CONST_METHOD4_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD4_(, const, ct, m, F)
+#define MOCK_CONST_METHOD5_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD5_(, const, ct, m, F)
+#define MOCK_CONST_METHOD6_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD6_(, const, ct, m, F)
+#define MOCK_CONST_METHOD7_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD7_(, const, ct, m, F)
+#define MOCK_CONST_METHOD8_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD8_(, const, ct, m, F)
+#define MOCK_CONST_METHOD9_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD9_(, const, ct, m, F)
+#define MOCK_CONST_METHOD10_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD10_(, const, ct, m, F)
+
+#define MOCK_METHOD0_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD0_(typename, , ct, m, F)
+#define MOCK_METHOD1_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD1_(typename, , ct, m, F)
+#define MOCK_METHOD2_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD2_(typename, , ct, m, F)
+#define MOCK_METHOD3_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD3_(typename, , ct, m, F)
+#define MOCK_METHOD4_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD4_(typename, , ct, m, F)
+#define MOCK_METHOD5_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD5_(typename, , ct, m, F)
+#define MOCK_METHOD6_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD6_(typename, , ct, m, F)
+#define MOCK_METHOD7_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD7_(typename, , ct, m, F)
+#define MOCK_METHOD8_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD8_(typename, , ct, m, F)
+#define MOCK_METHOD9_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD9_(typename, , ct, m, F)
+#define MOCK_METHOD10_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD10_(typename, , ct, m, F)
+
+#define MOCK_CONST_METHOD0_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD0_(typename, const, ct, m, F)
+#define MOCK_CONST_METHOD1_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD1_(typename, const, ct, m, F)
+#define MOCK_CONST_METHOD2_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD2_(typename, const, ct, m, F)
+#define MOCK_CONST_METHOD3_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD3_(typename, const, ct, m, F)
+#define MOCK_CONST_METHOD4_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD4_(typename, const, ct, m, F)
+#define MOCK_CONST_METHOD5_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD5_(typename, const, ct, m, F)
+#define MOCK_CONST_METHOD6_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD6_(typename, const, ct, m, F)
+#define MOCK_CONST_METHOD7_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD7_(typename, const, ct, m, F)
+#define MOCK_CONST_METHOD8_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD8_(typename, const, ct, m, F)
+#define MOCK_CONST_METHOD9_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD9_(typename, const, ct, m, F)
+#define MOCK_CONST_METHOD10_T_WITH_CALLTYPE(ct, m, F) \
+    GMOCK_METHOD10_(typename, const, ct, m, F)
+
+// A MockFunction<F> class has one mock method whose type is F.  It is
+// useful when you just want your test code to emit some messages and
+// have Google Mock verify the right messages are sent (and perhaps at
+// the right times).  For example, if you are exercising code:
+//
+//   Foo(1);
+//   Foo(2);
+//   Foo(3);
+//
+// and want to verify that Foo(1) and Foo(3) both invoke
+// mock.Bar("a"), but Foo(2) doesn't invoke anything, you can write:
+//
+// TEST(FooTest, InvokesBarCorrectly) {
+//   MyMock mock;
+//   MockFunction<void(string check_point_name)> check;
+//   {
+//     InSequence s;
+//
+//     EXPECT_CALL(mock, Bar("a"));
+//     EXPECT_CALL(check, Call("1"));
+//     EXPECT_CALL(check, Call("2"));
+//     EXPECT_CALL(mock, Bar("a"));
+//   }
+//   Foo(1);
+//   check.Call("1");
+//   Foo(2);
+//   check.Call("2");
+//   Foo(3);
+// }
+//
+// The expectation spec says that the first Bar("a") must happen
+// before check point "1", the second Bar("a") must happen after check
+// point "2", and nothing should happen between the two check
+// points. The explicit check points make it easy to tell which
+// Bar("a") is called by which call to Foo().
+template <typename F>
+class MockFunction;
+
+template <typename R>
+class MockFunction<R()> {
+ public:
+  MOCK_METHOD0_T(Call, R());
+};
+
+template <typename R, typename A0>
+class MockFunction<R(A0)> {
+ public:
+  MOCK_METHOD1_T(Call, R(A0));
+};
+
+template <typename R, typename A0, typename A1>
+class MockFunction<R(A0, A1)> {
+ public:
+  MOCK_METHOD2_T(Call, R(A0, A1));
+};
+
+template <typename R, typename A0, typename A1, typename A2>
+class MockFunction<R(A0, A1, A2)> {
+ public:
+  MOCK_METHOD3_T(Call, R(A0, A1, A2));
+};
+
+template <typename R, typename A0, typename A1, typename A2, typename A3>
+class MockFunction<R(A0, A1, A2, A3)> {
+ public:
+  MOCK_METHOD4_T(Call, R(A0, A1, A2, A3));
+};
+
+template <typename R, typename A0, typename A1, typename A2, typename A3,
+    typename A4>
+class MockFunction<R(A0, A1, A2, A3, A4)> {
+ public:
+  MOCK_METHOD5_T(Call, R(A0, A1, A2, A3, A4));
+};
+
+template <typename R, typename A0, typename A1, typename A2, typename A3,
+    typename A4, typename A5>
+class MockFunction<R(A0, A1, A2, A3, A4, A5)> {
+ public:
+  MOCK_METHOD6_T(Call, R(A0, A1, A2, A3, A4, A5));
+};
+
+template <typename R, typename A0, typename A1, typename A2, typename A3,
+    typename A4, typename A5, typename A6>
+class MockFunction<R(A0, A1, A2, A3, A4, A5, A6)> {
+ public:
+  MOCK_METHOD7_T(Call, R(A0, A1, A2, A3, A4, A5, A6));
+};
+
+template <typename R, typename A0, typename A1, typename A2, typename A3,
+    typename A4, typename A5, typename A6, typename A7>
+class MockFunction<R(A0, A1, A2, A3, A4, A5, A6, A7)> {
+ public:
+  MOCK_METHOD8_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7));
+};
+
+template <typename R, typename A0, typename A1, typename A2, typename A3,
+    typename A4, typename A5, typename A6, typename A7, typename A8>
+class MockFunction<R(A0, A1, A2, A3, A4, A5, A6, A7, A8)> {
+ public:
+  MOCK_METHOD9_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7, A8));
+};
+
+template <typename R, typename A0, typename A1, typename A2, typename A3,
+    typename A4, typename A5, typename A6, typename A7, typename A8,
+    typename A9>
+class MockFunction<R(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9)> {
+ public:
+  MOCK_METHOD10_T(Call, R(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9));
+};
+
+}  // namespace testing
+
+#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_FUNCTION_MOCKERS_H_
diff --git a/third_party/gmock/include/gmock/gmock-generated-matchers.h b/third_party/gmock/include/gmock/gmock-generated-matchers.h
new file mode 100644
index 0000000..a59e457
--- /dev/null
+++ b/third_party/gmock/include/gmock/gmock-generated-matchers.h
@@ -0,0 +1,1700 @@
+// This file was GENERATED by a script.  DO NOT EDIT BY HAND!!!
+
+// 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.
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements some commonly used variadic matchers.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_
+
+#include <sstream>
+#include <string>
+#include <vector>
+#include <gmock/gmock-matchers.h>
+#include <gmock/gmock-printers.h>
+
+namespace testing {
+namespace internal {
+
+// The type of the i-th (0-based) field of Tuple.
+#define GMOCK_FIELD_TYPE_(Tuple, i) \
+    typename ::std::tr1::tuple_element<i, Tuple>::type
+
+// TupleFields<Tuple, k0, ..., kn> is for selecting fields from a
+// tuple of type Tuple.  It has two members:
+//
+//   type: a tuple type whose i-th field is the ki-th field of Tuple.
+//   GetSelectedFields(t): returns fields k0, ..., and kn of t as a tuple.
+//
+// For example, in class TupleFields<tuple<bool, char, int>, 2, 0>, we have:
+//
+//   type is tuple<int, bool>, and
+//   GetSelectedFields(make_tuple(true, 'a', 42)) is (42, true).
+
+template <class Tuple, int k0 = -1, int k1 = -1, int k2 = -1, int k3 = -1,
+    int k4 = -1, int k5 = -1, int k6 = -1, int k7 = -1, int k8 = -1,
+    int k9 = -1>
+class TupleFields;
+
+// This generic version is used when there are 10 selectors.
+template <class Tuple, int k0, int k1, int k2, int k3, int k4, int k5, int k6,
+    int k7, int k8, int k9>
+class TupleFields {
+ public:
+  typedef ::std::tr1::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+      GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
+      GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4),
+      GMOCK_FIELD_TYPE_(Tuple, k5), GMOCK_FIELD_TYPE_(Tuple, k6),
+      GMOCK_FIELD_TYPE_(Tuple, k7), GMOCK_FIELD_TYPE_(Tuple, k8),
+      GMOCK_FIELD_TYPE_(Tuple, k9)> type;
+  static type GetSelectedFields(const Tuple& t) {
+    using ::std::tr1::get;
+    return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t),
+        get<k5>(t), get<k6>(t), get<k7>(t), get<k8>(t), get<k9>(t));
+  }
+};
+
+// The following specialization is used for 0 ~ 9 selectors.
+
+template <class Tuple>
+class TupleFields<Tuple, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1> {
+ public:
+  typedef ::std::tr1::tuple<> type;
+  static type GetSelectedFields(const Tuple& t) {
+    using ::std::tr1::get;
+    return type();
+  }
+};
+
+template <class Tuple, int k0>
+class TupleFields<Tuple, k0, -1, -1, -1, -1, -1, -1, -1, -1, -1> {
+ public:
+  typedef ::std::tr1::tuple<GMOCK_FIELD_TYPE_(Tuple, k0)> type;
+  static type GetSelectedFields(const Tuple& t) {
+    using ::std::tr1::get;
+    return type(get<k0>(t));
+  }
+};
+
+template <class Tuple, int k0, int k1>
+class TupleFields<Tuple, k0, k1, -1, -1, -1, -1, -1, -1, -1, -1> {
+ public:
+  typedef ::std::tr1::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+      GMOCK_FIELD_TYPE_(Tuple, k1)> type;
+  static type GetSelectedFields(const Tuple& t) {
+    using ::std::tr1::get;
+    return type(get<k0>(t), get<k1>(t));
+  }
+};
+
+template <class Tuple, int k0, int k1, int k2>
+class TupleFields<Tuple, k0, k1, k2, -1, -1, -1, -1, -1, -1, -1> {
+ public:
+  typedef ::std::tr1::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+      GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2)> type;
+  static type GetSelectedFields(const Tuple& t) {
+    using ::std::tr1::get;
+    return type(get<k0>(t), get<k1>(t), get<k2>(t));
+  }
+};
+
+template <class Tuple, int k0, int k1, int k2, int k3>
+class TupleFields<Tuple, k0, k1, k2, k3, -1, -1, -1, -1, -1, -1> {
+ public:
+  typedef ::std::tr1::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+      GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
+      GMOCK_FIELD_TYPE_(Tuple, k3)> type;
+  static type GetSelectedFields(const Tuple& t) {
+    using ::std::tr1::get;
+    return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t));
+  }
+};
+
+template <class Tuple, int k0, int k1, int k2, int k3, int k4>
+class TupleFields<Tuple, k0, k1, k2, k3, k4, -1, -1, -1, -1, -1> {
+ public:
+  typedef ::std::tr1::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+      GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
+      GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4)> type;
+  static type GetSelectedFields(const Tuple& t) {
+    using ::std::tr1::get;
+    return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t));
+  }
+};
+
+template <class Tuple, int k0, int k1, int k2, int k3, int k4, int k5>
+class TupleFields<Tuple, k0, k1, k2, k3, k4, k5, -1, -1, -1, -1> {
+ public:
+  typedef ::std::tr1::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+      GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
+      GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4),
+      GMOCK_FIELD_TYPE_(Tuple, k5)> type;
+  static type GetSelectedFields(const Tuple& t) {
+    using ::std::tr1::get;
+    return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t),
+        get<k5>(t));
+  }
+};
+
+template <class Tuple, int k0, int k1, int k2, int k3, int k4, int k5, int k6>
+class TupleFields<Tuple, k0, k1, k2, k3, k4, k5, k6, -1, -1, -1> {
+ public:
+  typedef ::std::tr1::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+      GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
+      GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4),
+      GMOCK_FIELD_TYPE_(Tuple, k5), GMOCK_FIELD_TYPE_(Tuple, k6)> type;
+  static type GetSelectedFields(const Tuple& t) {
+    using ::std::tr1::get;
+    return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t),
+        get<k5>(t), get<k6>(t));
+  }
+};
+
+template <class Tuple, int k0, int k1, int k2, int k3, int k4, int k5, int k6,
+    int k7>
+class TupleFields<Tuple, k0, k1, k2, k3, k4, k5, k6, k7, -1, -1> {
+ public:
+  typedef ::std::tr1::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+      GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
+      GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4),
+      GMOCK_FIELD_TYPE_(Tuple, k5), GMOCK_FIELD_TYPE_(Tuple, k6),
+      GMOCK_FIELD_TYPE_(Tuple, k7)> type;
+  static type GetSelectedFields(const Tuple& t) {
+    using ::std::tr1::get;
+    return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t),
+        get<k5>(t), get<k6>(t), get<k7>(t));
+  }
+};
+
+template <class Tuple, int k0, int k1, int k2, int k3, int k4, int k5, int k6,
+    int k7, int k8>
+class TupleFields<Tuple, k0, k1, k2, k3, k4, k5, k6, k7, k8, -1> {
+ public:
+  typedef ::std::tr1::tuple<GMOCK_FIELD_TYPE_(Tuple, k0),
+      GMOCK_FIELD_TYPE_(Tuple, k1), GMOCK_FIELD_TYPE_(Tuple, k2),
+      GMOCK_FIELD_TYPE_(Tuple, k3), GMOCK_FIELD_TYPE_(Tuple, k4),
+      GMOCK_FIELD_TYPE_(Tuple, k5), GMOCK_FIELD_TYPE_(Tuple, k6),
+      GMOCK_FIELD_TYPE_(Tuple, k7), GMOCK_FIELD_TYPE_(Tuple, k8)> type;
+  static type GetSelectedFields(const Tuple& t) {
+    using ::std::tr1::get;
+    return type(get<k0>(t), get<k1>(t), get<k2>(t), get<k3>(t), get<k4>(t),
+        get<k5>(t), get<k6>(t), get<k7>(t), get<k8>(t));
+  }
+};
+
+#undef GMOCK_FIELD_TYPE_
+
+// Implements the Args() matcher.
+template <class ArgsTuple, int k0 = -1, int k1 = -1, int k2 = -1, int k3 = -1,
+    int k4 = -1, int k5 = -1, int k6 = -1, int k7 = -1, int k8 = -1,
+    int k9 = -1>
+class ArgsMatcherImpl : public MatcherInterface<ArgsTuple> {
+ public:
+  // ArgsTuple may have top-level const or reference modifiers.
+  typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(ArgsTuple)) RawArgsTuple;
+  typedef typename internal::TupleFields<RawArgsTuple, k0, k1, k2, k3, k4, k5,
+      k6, k7, k8, k9>::type SelectedArgs;
+  typedef Matcher<const SelectedArgs&> MonomorphicInnerMatcher;
+
+  template <typename InnerMatcher>
+  explicit ArgsMatcherImpl(const InnerMatcher& inner_matcher)
+      : inner_matcher_(SafeMatcherCast<const SelectedArgs&>(inner_matcher)) {}
+
+  virtual bool Matches(ArgsTuple args) const {
+    return inner_matcher_.Matches(GetSelectedArgs(args));
+  }
+
+  virtual void DescribeTo(::std::ostream* os) const {
+    PrintIndices(os);
+    inner_matcher_.DescribeTo(os);
+  }
+
+  virtual void DescribeNegationTo(::std::ostream* os) const {
+    PrintIndices(os);
+    inner_matcher_.DescribeNegationTo(os);
+  }
+
+  virtual void ExplainMatchResultTo(ArgsTuple args,
+                                    ::std::ostream* os) const {
+    inner_matcher_.ExplainMatchResultTo(GetSelectedArgs(args), os);
+  }
+
+ private:
+  static SelectedArgs GetSelectedArgs(ArgsTuple args) {
+    return TupleFields<RawArgsTuple, k0, k1, k2, k3, k4, k5, k6, k7, k8,
+        k9>::GetSelectedFields(args);
+  }
+
+  // Prints the indices of the selected fields.
+  static void PrintIndices(::std::ostream* os) {
+    *os << "are a tuple whose fields (";
+    const int indices[10] = { k0, k1, k2, k3, k4, k5, k6, k7, k8, k9 };
+    for (int i = 0; i < 10; i++) {
+      if (indices[i] < 0)
+        break;
+
+      if (i >= 1)
+        *os << ", ";
+
+      *os << "#" << indices[i];
+    }
+    *os << ") ";
+  }
+
+  const MonomorphicInnerMatcher inner_matcher_;
+};
+
+template <class InnerMatcher, int k0 = -1, int k1 = -1, int k2 = -1,
+    int k3 = -1, int k4 = -1, int k5 = -1, int k6 = -1, int k7 = -1,
+    int k8 = -1, int k9 = -1>
+class ArgsMatcher {
+ public:
+  explicit ArgsMatcher(const InnerMatcher& inner_matcher)
+      : inner_matcher_(inner_matcher) {}
+
+  template <typename ArgsTuple>
+  operator Matcher<ArgsTuple>() const {
+    return MakeMatcher(new ArgsMatcherImpl<ArgsTuple, k0, k1, k2, k3, k4, k5,
+        k6, k7, k8, k9>(inner_matcher_));
+  }
+
+  const InnerMatcher inner_matcher_;
+};
+
+// Implements ElementsAre() of 1-10 arguments.
+
+template <typename T1>
+class ElementsAreMatcher1 {
+ public:
+  explicit ElementsAreMatcher1(const T1& e1) : e1_(e1) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container))
+        RawContainer;
+    typedef typename internal::StlContainerView<RawContainer>::type::value_type
+        Element;
+
+    // Nokia's Symbian Compiler has a nasty bug where the object put
+    // in a one-element local array is not destructed when the array
+    // goes out of scope.  This leads to obvious badness as we've
+    // added the linked_ptr in it to our other linked_ptrs list.
+    // Hence we implement ElementsAreMatcher1 specially to avoid using
+    // a local array.
+    const Matcher<const Element&> matcher =
+        MatcherCast<const Element&>(e1_);
+    return MakeMatcher(new ElementsAreMatcherImpl<Container>(&matcher, 1));
+  }
+
+ private:
+  const T1& e1_;
+};
+
+template <typename T1, typename T2>
+class ElementsAreMatcher2 {
+ public:
+  ElementsAreMatcher2(const T1& e1, const T2& e2) : e1_(e1), e2_(e2) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container))
+        RawContainer;
+    typedef typename internal::StlContainerView<RawContainer>::type::value_type
+        Element;
+
+    const Matcher<const Element&> matchers[] = {
+      MatcherCast<const Element&>(e1_),
+      MatcherCast<const Element&>(e2_),
+    };
+
+    return MakeMatcher(new ElementsAreMatcherImpl<Container>(matchers, 2));
+  }
+
+ private:
+  const T1& e1_;
+  const T2& e2_;
+};
+
+template <typename T1, typename T2, typename T3>
+class ElementsAreMatcher3 {
+ public:
+  ElementsAreMatcher3(const T1& e1, const T2& e2, const T3& e3) : e1_(e1),
+      e2_(e2), e3_(e3) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container))
+        RawContainer;
+    typedef typename internal::StlContainerView<RawContainer>::type::value_type
+        Element;
+
+    const Matcher<const Element&> matchers[] = {
+      MatcherCast<const Element&>(e1_),
+      MatcherCast<const Element&>(e2_),
+      MatcherCast<const Element&>(e3_),
+    };
+
+    return MakeMatcher(new ElementsAreMatcherImpl<Container>(matchers, 3));
+  }
+
+ private:
+  const T1& e1_;
+  const T2& e2_;
+  const T3& e3_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4>
+class ElementsAreMatcher4 {
+ public:
+  ElementsAreMatcher4(const T1& e1, const T2& e2, const T3& e3,
+      const T4& e4) : e1_(e1), e2_(e2), e3_(e3), e4_(e4) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container))
+        RawContainer;
+    typedef typename internal::StlContainerView<RawContainer>::type::value_type
+        Element;
+
+    const Matcher<const Element&> matchers[] = {
+      MatcherCast<const Element&>(e1_),
+      MatcherCast<const Element&>(e2_),
+      MatcherCast<const Element&>(e3_),
+      MatcherCast<const Element&>(e4_),
+    };
+
+    return MakeMatcher(new ElementsAreMatcherImpl<Container>(matchers, 4));
+  }
+
+ private:
+  const T1& e1_;
+  const T2& e2_;
+  const T3& e3_;
+  const T4& e4_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+class ElementsAreMatcher5 {
+ public:
+  ElementsAreMatcher5(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+      const T5& e5) : e1_(e1), e2_(e2), e3_(e3), e4_(e4), e5_(e5) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container))
+        RawContainer;
+    typedef typename internal::StlContainerView<RawContainer>::type::value_type
+        Element;
+
+    const Matcher<const Element&> matchers[] = {
+      MatcherCast<const Element&>(e1_),
+      MatcherCast<const Element&>(e2_),
+      MatcherCast<const Element&>(e3_),
+      MatcherCast<const Element&>(e4_),
+      MatcherCast<const Element&>(e5_),
+    };
+
+    return MakeMatcher(new ElementsAreMatcherImpl<Container>(matchers, 5));
+  }
+
+ private:
+  const T1& e1_;
+  const T2& e2_;
+  const T3& e3_;
+  const T4& e4_;
+  const T5& e5_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6>
+class ElementsAreMatcher6 {
+ public:
+  ElementsAreMatcher6(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+      const T5& e5, const T6& e6) : e1_(e1), e2_(e2), e3_(e3), e4_(e4),
+      e5_(e5), e6_(e6) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container))
+        RawContainer;
+    typedef typename internal::StlContainerView<RawContainer>::type::value_type
+        Element;
+
+    const Matcher<const Element&> matchers[] = {
+      MatcherCast<const Element&>(e1_),
+      MatcherCast<const Element&>(e2_),
+      MatcherCast<const Element&>(e3_),
+      MatcherCast<const Element&>(e4_),
+      MatcherCast<const Element&>(e5_),
+      MatcherCast<const Element&>(e6_),
+    };
+
+    return MakeMatcher(new ElementsAreMatcherImpl<Container>(matchers, 6));
+  }
+
+ private:
+  const T1& e1_;
+  const T2& e2_;
+  const T3& e3_;
+  const T4& e4_;
+  const T5& e5_;
+  const T6& e6_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7>
+class ElementsAreMatcher7 {
+ public:
+  ElementsAreMatcher7(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+      const T5& e5, const T6& e6, const T7& e7) : e1_(e1), e2_(e2), e3_(e3),
+      e4_(e4), e5_(e5), e6_(e6), e7_(e7) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container))
+        RawContainer;
+    typedef typename internal::StlContainerView<RawContainer>::type::value_type
+        Element;
+
+    const Matcher<const Element&> matchers[] = {
+      MatcherCast<const Element&>(e1_),
+      MatcherCast<const Element&>(e2_),
+      MatcherCast<const Element&>(e3_),
+      MatcherCast<const Element&>(e4_),
+      MatcherCast<const Element&>(e5_),
+      MatcherCast<const Element&>(e6_),
+      MatcherCast<const Element&>(e7_),
+    };
+
+    return MakeMatcher(new ElementsAreMatcherImpl<Container>(matchers, 7));
+  }
+
+ private:
+  const T1& e1_;
+  const T2& e2_;
+  const T3& e3_;
+  const T4& e4_;
+  const T5& e5_;
+  const T6& e6_;
+  const T7& e7_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8>
+class ElementsAreMatcher8 {
+ public:
+  ElementsAreMatcher8(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+      const T5& e5, const T6& e6, const T7& e7, const T8& e8) : e1_(e1),
+      e2_(e2), e3_(e3), e4_(e4), e5_(e5), e6_(e6), e7_(e7), e8_(e8) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container))
+        RawContainer;
+    typedef typename internal::StlContainerView<RawContainer>::type::value_type
+        Element;
+
+    const Matcher<const Element&> matchers[] = {
+      MatcherCast<const Element&>(e1_),
+      MatcherCast<const Element&>(e2_),
+      MatcherCast<const Element&>(e3_),
+      MatcherCast<const Element&>(e4_),
+      MatcherCast<const Element&>(e5_),
+      MatcherCast<const Element&>(e6_),
+      MatcherCast<const Element&>(e7_),
+      MatcherCast<const Element&>(e8_),
+    };
+
+    return MakeMatcher(new ElementsAreMatcherImpl<Container>(matchers, 8));
+  }
+
+ private:
+  const T1& e1_;
+  const T2& e2_;
+  const T3& e3_;
+  const T4& e4_;
+  const T5& e5_;
+  const T6& e6_;
+  const T7& e7_;
+  const T8& e8_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9>
+class ElementsAreMatcher9 {
+ public:
+  ElementsAreMatcher9(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+      const T5& e5, const T6& e6, const T7& e7, const T8& e8,
+      const T9& e9) : e1_(e1), e2_(e2), e3_(e3), e4_(e4), e5_(e5), e6_(e6),
+      e7_(e7), e8_(e8), e9_(e9) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container))
+        RawContainer;
+    typedef typename internal::StlContainerView<RawContainer>::type::value_type
+        Element;
+
+    const Matcher<const Element&> matchers[] = {
+      MatcherCast<const Element&>(e1_),
+      MatcherCast<const Element&>(e2_),
+      MatcherCast<const Element&>(e3_),
+      MatcherCast<const Element&>(e4_),
+      MatcherCast<const Element&>(e5_),
+      MatcherCast<const Element&>(e6_),
+      MatcherCast<const Element&>(e7_),
+      MatcherCast<const Element&>(e8_),
+      MatcherCast<const Element&>(e9_),
+    };
+
+    return MakeMatcher(new ElementsAreMatcherImpl<Container>(matchers, 9));
+  }
+
+ private:
+  const T1& e1_;
+  const T2& e2_;
+  const T3& e3_;
+  const T4& e4_;
+  const T5& e5_;
+  const T6& e6_;
+  const T7& e7_;
+  const T8& e8_;
+  const T9& e9_;
+};
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10>
+class ElementsAreMatcher10 {
+ public:
+  ElementsAreMatcher10(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+      const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9,
+      const T10& e10) : e1_(e1), e2_(e2), e3_(e3), e4_(e4), e5_(e5), e6_(e6),
+      e7_(e7), e8_(e8), e9_(e9), e10_(e10) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container))
+        RawContainer;
+    typedef typename internal::StlContainerView<RawContainer>::type::value_type
+        Element;
+
+    const Matcher<const Element&> matchers[] = {
+      MatcherCast<const Element&>(e1_),
+      MatcherCast<const Element&>(e2_),
+      MatcherCast<const Element&>(e3_),
+      MatcherCast<const Element&>(e4_),
+      MatcherCast<const Element&>(e5_),
+      MatcherCast<const Element&>(e6_),
+      MatcherCast<const Element&>(e7_),
+      MatcherCast<const Element&>(e8_),
+      MatcherCast<const Element&>(e9_),
+      MatcherCast<const Element&>(e10_),
+    };
+
+    return MakeMatcher(new ElementsAreMatcherImpl<Container>(matchers, 10));
+  }
+
+ private:
+  const T1& e1_;
+  const T2& e2_;
+  const T3& e3_;
+  const T4& e4_;
+  const T5& e5_;
+  const T6& e6_;
+  const T7& e7_;
+  const T8& e8_;
+  const T9& e9_;
+  const T10& e10_;
+};
+
+}  // namespace internal
+
+// Args<N1, N2, ..., Nk>(a_matcher) matches a tuple if the selected
+// fields of it matches a_matcher.  C++ doesn't support default
+// arguments for function templates, so we have to overload it.
+template <typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher>
+Args(const InnerMatcher& matcher) {
+  return internal::ArgsMatcher<InnerMatcher>(matcher);
+}
+
+template <int k1, typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1>
+Args(const InnerMatcher& matcher) {
+  return internal::ArgsMatcher<InnerMatcher, k1>(matcher);
+}
+
+template <int k1, int k2, typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2>
+Args(const InnerMatcher& matcher) {
+  return internal::ArgsMatcher<InnerMatcher, k1, k2>(matcher);
+}
+
+template <int k1, int k2, int k3, typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3>
+Args(const InnerMatcher& matcher) {
+  return internal::ArgsMatcher<InnerMatcher, k1, k2, k3>(matcher);
+}
+
+template <int k1, int k2, int k3, int k4, typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4>
+Args(const InnerMatcher& matcher) {
+  return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4>(matcher);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5>
+Args(const InnerMatcher& matcher) {
+  return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5>(matcher);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6>
+Args(const InnerMatcher& matcher) {
+  return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6>(matcher);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, int k7,
+    typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7>
+Args(const InnerMatcher& matcher) {
+  return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6,
+      k7>(matcher);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
+    typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7, k8>
+Args(const InnerMatcher& matcher) {
+  return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7,
+      k8>(matcher);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
+    int k9, typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7, k8, k9>
+Args(const InnerMatcher& matcher) {
+  return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7, k8,
+      k9>(matcher);
+}
+
+template <int k1, int k2, int k3, int k4, int k5, int k6, int k7, int k8,
+    int k9, int k10, typename InnerMatcher>
+inline internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7, k8, k9,
+    k10>
+Args(const InnerMatcher& matcher) {
+  return internal::ArgsMatcher<InnerMatcher, k1, k2, k3, k4, k5, k6, k7, k8,
+      k9, k10>(matcher);
+}
+
+// ElementsAre(e0, e1, ..., e_n) matches an STL-style container with
+// (n + 1) elements, where the i-th element in the container must
+// match the i-th argument in the list.  Each argument of
+// ElementsAre() can be either a value or a matcher.  We support up to
+// 10 arguments.
+//
+// NOTE: Since ElementsAre() cares about the order of the elements, it
+// must not be used with containers whose elements's order is
+// undefined (e.g. hash_map).
+
+inline internal::ElementsAreMatcher0 ElementsAre() {
+  return internal::ElementsAreMatcher0();
+}
+
+template <typename T1>
+inline internal::ElementsAreMatcher1<T1> ElementsAre(const T1& e1) {
+  return internal::ElementsAreMatcher1<T1>(e1);
+}
+
+template <typename T1, typename T2>
+inline internal::ElementsAreMatcher2<T1, T2> ElementsAre(const T1& e1,
+    const T2& e2) {
+  return internal::ElementsAreMatcher2<T1, T2>(e1, e2);
+}
+
+template <typename T1, typename T2, typename T3>
+inline internal::ElementsAreMatcher3<T1, T2, T3> ElementsAre(const T1& e1,
+    const T2& e2, const T3& e3) {
+  return internal::ElementsAreMatcher3<T1, T2, T3>(e1, e2, e3);
+}
+
+template <typename T1, typename T2, typename T3, typename T4>
+inline internal::ElementsAreMatcher4<T1, T2, T3, T4> ElementsAre(const T1& e1,
+    const T2& e2, const T3& e3, const T4& e4) {
+  return internal::ElementsAreMatcher4<T1, T2, T3, T4>(e1, e2, e3, e4);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+inline internal::ElementsAreMatcher5<T1, T2, T3, T4,
+    T5> ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+    const T5& e5) {
+  return internal::ElementsAreMatcher5<T1, T2, T3, T4, T5>(e1, e2, e3, e4, e5);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6>
+inline internal::ElementsAreMatcher6<T1, T2, T3, T4, T5,
+    T6> ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+    const T5& e5, const T6& e6) {
+  return internal::ElementsAreMatcher6<T1, T2, T3, T4, T5, T6>(e1, e2, e3, e4,
+      e5, e6);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7>
+inline internal::ElementsAreMatcher7<T1, T2, T3, T4, T5, T6,
+    T7> ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+    const T5& e5, const T6& e6, const T7& e7) {
+  return internal::ElementsAreMatcher7<T1, T2, T3, T4, T5, T6, T7>(e1, e2, e3,
+      e4, e5, e6, e7);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8>
+inline internal::ElementsAreMatcher8<T1, T2, T3, T4, T5, T6, T7,
+    T8> ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+    const T5& e5, const T6& e6, const T7& e7, const T8& e8) {
+  return internal::ElementsAreMatcher8<T1, T2, T3, T4, T5, T6, T7, T8>(e1, e2,
+      e3, e4, e5, e6, e7, e8);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9>
+inline internal::ElementsAreMatcher9<T1, T2, T3, T4, T5, T6, T7, T8,
+    T9> ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+    const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9) {
+  return internal::ElementsAreMatcher9<T1, T2, T3, T4, T5, T6, T7, T8, T9>(e1,
+      e2, e3, e4, e5, e6, e7, e8, e9);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+    typename T6, typename T7, typename T8, typename T9, typename T10>
+inline internal::ElementsAreMatcher10<T1, T2, T3, T4, T5, T6, T7, T8, T9,
+    T10> ElementsAre(const T1& e1, const T2& e2, const T3& e3, const T4& e4,
+    const T5& e5, const T6& e6, const T7& e7, const T8& e8, const T9& e9,
+    const T10& e10) {
+  return internal::ElementsAreMatcher10<T1, T2, T3, T4, T5, T6, T7, T8, T9,
+      T10>(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
+}
+
+// ElementsAreArray(array) and ElementAreArray(array, count) are like
+// ElementsAre(), except that they take an array of values or
+// matchers.  The former form infers the size of 'array', which must
+// be a static C-style array.  In the latter form, 'array' can either
+// be a static array or a pointer to a dynamically created array.
+
+template <typename T>
+inline internal::ElementsAreArrayMatcher<T> ElementsAreArray(
+    const T* first, size_t count) {
+  return internal::ElementsAreArrayMatcher<T>(first, count);
+}
+
+template <typename T, size_t N>
+inline internal::ElementsAreArrayMatcher<T>
+ElementsAreArray(const T (&array)[N]) {
+  return internal::ElementsAreArrayMatcher<T>(array, N);
+}
+
+}  // namespace testing
+
+// The MATCHER* family of macros can be used in a namespace scope to
+// define custom matchers easily.  The syntax:
+//
+//   MATCHER(name, description_string) { statements; }
+//
+// will define a matcher with the given name that executes the
+// statements, which must return a bool to indicate if the match
+// succeeds.  Inside the statements, you can refer to the value being
+// matched by 'arg', and refer to its type by 'arg_type'.
+//
+// The description string documents what the matcher does, and is used
+// to generate the failure message when the match fails.  Since a
+// MATCHER() is usually defined in a header file shared by multiple
+// C++ source files, we require the description to be a C-string
+// literal to avoid possible side effects.  It can be empty, in which
+// case we'll use the sequence of words in the matcher name as the
+// description.
+//
+// For example:
+//
+//   MATCHER(IsEven, "") { return (arg % 2) == 0; }
+//
+// allows you to write
+//
+//   // Expects mock_foo.Bar(n) to be called where n is even.
+//   EXPECT_CALL(mock_foo, Bar(IsEven()));
+//
+// or,
+//
+//   // Verifies that the value of some_expression is even.
+//   EXPECT_THAT(some_expression, IsEven());
+//
+// If the above assertion fails, it will print something like:
+//
+//   Value of: some_expression
+//   Expected: is even
+//     Actual: 7
+//
+// where the description "is even" is automatically calculated from the
+// matcher name IsEven.
+//
+// Note that the type of the value being matched (arg_type) is
+// determined by the context in which you use the matcher and is
+// supplied to you by the compiler, so you don't need to worry about
+// declaring it (nor can you).  This allows the matcher to be
+// polymorphic.  For example, IsEven() can be used to match any type
+// where the value of "(arg % 2) == 0" can be implicitly converted to
+// a bool.  In the "Bar(IsEven())" example above, if method Bar()
+// takes an int, 'arg_type' will be int; if it takes an unsigned long,
+// 'arg_type' will be unsigned long; and so on.
+//
+// Sometimes you'll want to parameterize the matcher.  For that you
+// can use another macro:
+//
+//   MATCHER_P(name, param_name, description_string) { statements; }
+//
+// For example:
+//
+//   MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; }
+//
+// will allow you to write:
+//
+//   EXPECT_THAT(Blah("a"), HasAbsoluteValue(n));
+//
+// which may lead to this message (assuming n is 10):
+//
+//   Value of: Blah("a")
+//   Expected: has absolute value 10
+//     Actual: -9
+//
+// Note that both the matcher description and its parameter are
+// printed, making the message human-friendly.
+//
+// In the matcher definition body, you can write 'foo_type' to
+// reference the type of a parameter named 'foo'.  For example, in the
+// body of MATCHER_P(HasAbsoluteValue, value) above, you can write
+// 'value_type' to refer to the type of 'value'.
+//
+// We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P10 to
+// support multi-parameter matchers.
+//
+// When defining a parameterized matcher, you can use Python-style
+// interpolations in the description string to refer to the parameter
+// values.  We support the following syntax currently:
+//
+//   %%       a single '%' character
+//   %(*)s    all parameters of the matcher printed as a tuple
+//   %(foo)s  value of the matcher parameter named 'foo'
+//
+// For example,
+//
+//   MATCHER_P2(InClosedRange, low, hi, "is in range [%(low)s, %(hi)s]") {
+//     return low <= arg && arg <= hi;
+//   }
+//   ...
+//   EXPECT_THAT(3, InClosedRange(4, 6));
+//
+// would generate a failure that contains the message:
+//
+//   Expected: is in range [4, 6]
+//
+// If you specify "" as the description, the failure message will
+// contain the sequence of words in the matcher name followed by the
+// parameter values printed as a tuple.  For example,
+//
+//   MATCHER_P2(InClosedRange, low, hi, "") { ... }
+//   ...
+//   EXPECT_THAT(3, InClosedRange(4, 6));
+//
+// would generate a failure that contains the text:
+//
+//   Expected: in closed range (4, 6)
+//
+// For the purpose of typing, you can view
+//
+//   MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... }
+//
+// as shorthand for
+//
+//   template <typename p1_type, ..., typename pk_type>
+//   FooMatcherPk<p1_type, ..., pk_type>
+//   Foo(p1_type p1, ..., pk_type pk) { ... }
+//
+// When you write Foo(v1, ..., vk), the compiler infers the types of
+// the parameters v1, ..., and vk for you.  If you are not happy with
+// the result of the type inference, you can specify the types by
+// explicitly instantiating the template, as in Foo<long, bool>(5,
+// false).  As said earlier, you don't get to (or need to) specify
+// 'arg_type' as that's determined by the context in which the matcher
+// is used.  You can assign the result of expression Foo(p1, ..., pk)
+// to a variable of type FooMatcherPk<p1_type, ..., pk_type>.  This
+// can be useful when composing matchers.
+//
+// While you can instantiate a matcher template with reference types,
+// passing the parameters by pointer usually makes your code more
+// readable.  If, however, you still want to pass a parameter by
+// reference, be aware that in the failure message generated by the
+// matcher you will see the value of the referenced object but not its
+// address.
+//
+// You can overload matchers with different numbers of parameters:
+//
+//   MATCHER_P(Blah, a, description_string1) { ... }
+//   MATCHER_P2(Blah, a, b, description_string2) { ... }
+//
+// While it's tempting to always use the MATCHER* macros when defining
+// a new matcher, you should also consider implementing
+// MatcherInterface or using MakePolymorphicMatcher() instead,
+// especially if you need to use the matcher a lot.  While these
+// approaches require more work, they give you more control on the
+// types of the value being matched and the matcher parameters, which
+// in general leads to better compiler error messages that pay off in
+// the long run.  They also allow overloading matchers based on
+// parameter types (as opposed to just based on the number of
+// parameters).
+//
+// CAVEAT:
+//
+// MATCHER*() can only be used in a namespace scope.  The reason is
+// that C++ doesn't yet allow function-local types to be used to
+// instantiate templates.  The up-coming C++0x standard will fix this.
+// Once that's done, we'll consider supporting using MATCHER*() inside
+// a function.
+//
+// MORE INFORMATION:
+//
+// To learn more about using these macros, please search for 'MATCHER'
+// on http://code.google.com/p/googlemock/wiki/CookBook.
+
+#define MATCHER(name, description)\
+  class name##Matcher {\
+   public:\
+    template <typename arg_type>\
+    class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+     public:\
+      gmock_Impl(const ::testing::internal::Interpolations& gmock_interp)\
+           : gmock_interp_(gmock_interp) {}\
+      virtual bool Matches(arg_type arg) const;\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<>());\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
+      }\
+      const ::testing::internal::Interpolations gmock_interp_;\
+    };\
+    template <typename arg_type>\
+    operator ::testing::Matcher<arg_type>() const {\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(gmock_interp_));\
+    }\
+    name##Matcher() {\
+      const char* gmock_param_names[] = { NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
+    }\
+    ::testing::internal::Interpolations gmock_interp_;\
+  };\
+  inline name##Matcher name() {\
+    return name##Matcher();\
+  }\
+  template <typename arg_type>\
+  bool name##Matcher::\
+      gmock_Impl<arg_type>::Matches(arg_type arg) const
+
+#define MATCHER_P(name, p0, description)\
+  template <typename p0##_type>\
+  class name##MatcherP {\
+   public:\
+    template <typename arg_type>\
+    class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+     public:\
+      explicit gmock_Impl(p0##_type gmock_p0, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), gmock_interp_(gmock_interp) {}\
+      virtual bool Matches(arg_type arg) const;\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type>(p0));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
+      }\
+      p0##_type p0;\
+      const ::testing::internal::Interpolations gmock_interp_;\
+    };\
+    template <typename arg_type>\
+    operator ::testing::Matcher<arg_type>() const {\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, gmock_interp_));\
+    }\
+    name##MatcherP(p0##_type gmock_p0) : p0(gmock_p0) {\
+      const char* gmock_param_names[] = { #p0, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
+    }\
+    p0##_type p0;\
+    ::testing::internal::Interpolations gmock_interp_;\
+  };\
+  template <typename p0##_type>\
+  inline name##MatcherP<p0##_type> name(p0##_type p0) {\
+    return name##MatcherP<p0##_type>(p0);\
+  }\
+  template <typename p0##_type>\
+  template <typename arg_type>\
+  bool name##MatcherP<p0##_type>::\
+      gmock_Impl<arg_type>::Matches(arg_type arg) const
+
+#define MATCHER_P2(name, p0, p1, description)\
+  template <typename p0##_type, typename p1##_type>\
+  class name##MatcherP2 {\
+   public:\
+    template <typename arg_type>\
+    class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+     public:\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), gmock_interp_(gmock_interp) {}\
+      virtual bool Matches(arg_type arg) const;\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type>(p0, p1));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
+      }\
+      p0##_type p0;\
+      p1##_type p1;\
+      const ::testing::internal::Interpolations gmock_interp_;\
+    };\
+    template <typename arg_type>\
+    operator ::testing::Matcher<arg_type>() const {\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, gmock_interp_));\
+    }\
+    name##MatcherP2(p0##_type gmock_p0, p1##_type gmock_p1) : p0(gmock_p0), \
+        p1(gmock_p1) {\
+      const char* gmock_param_names[] = { #p0, #p1, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+    ::testing::internal::Interpolations gmock_interp_;\
+  };\
+  template <typename p0##_type, typename p1##_type>\
+  inline name##MatcherP2<p0##_type, p1##_type> name(p0##_type p0, \
+      p1##_type p1) {\
+    return name##MatcherP2<p0##_type, p1##_type>(p0, p1);\
+  }\
+  template <typename p0##_type, typename p1##_type>\
+  template <typename arg_type>\
+  bool name##MatcherP2<p0##_type, p1##_type>::\
+      gmock_Impl<arg_type>::Matches(arg_type arg) const
+
+#define MATCHER_P3(name, p0, p1, p2, description)\
+  template <typename p0##_type, typename p1##_type, typename p2##_type>\
+  class name##MatcherP3 {\
+   public:\
+    template <typename arg_type>\
+    class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+     public:\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+               gmock_interp_(gmock_interp) {}\
+      virtual bool Matches(arg_type arg) const;\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type, p2##_type>(p0, p1, \
+                    p2));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
+      }\
+      p0##_type p0;\
+      p1##_type p1;\
+      p2##_type p2;\
+      const ::testing::internal::Interpolations gmock_interp_;\
+    };\
+    template <typename arg_type>\
+    operator ::testing::Matcher<arg_type>() const {\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, p2, gmock_interp_));\
+    }\
+    name##MatcherP3(p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2) {\
+      const char* gmock_param_names[] = { #p0, #p1, #p2, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+    p2##_type p2;\
+    ::testing::internal::Interpolations gmock_interp_;\
+  };\
+  template <typename p0##_type, typename p1##_type, typename p2##_type>\
+  inline name##MatcherP3<p0##_type, p1##_type, p2##_type> name(p0##_type p0, \
+      p1##_type p1, p2##_type p2) {\
+    return name##MatcherP3<p0##_type, p1##_type, p2##_type>(p0, p1, p2);\
+  }\
+  template <typename p0##_type, typename p1##_type, typename p2##_type>\
+  template <typename arg_type>\
+  bool name##MatcherP3<p0##_type, p1##_type, p2##_type>::\
+      gmock_Impl<arg_type>::Matches(arg_type arg) const
+
+#define MATCHER_P4(name, p0, p1, p2, p3, description)\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type>\
+  class name##MatcherP4 {\
+   public:\
+    template <typename arg_type>\
+    class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+     public:\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+          p3##_type gmock_p3, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+               gmock_interp_(gmock_interp) {}\
+      virtual bool Matches(arg_type arg) const;\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type, p2##_type, \
+                    p3##_type>(p0, p1, p2, p3));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
+      }\
+      p0##_type p0;\
+      p1##_type p1;\
+      p2##_type p2;\
+      p3##_type p3;\
+      const ::testing::internal::Interpolations gmock_interp_;\
+    };\
+    template <typename arg_type>\
+    operator ::testing::Matcher<arg_type>() const {\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, p2, p3, gmock_interp_));\
+    }\
+    name##MatcherP4(p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2, p3##_type gmock_p3) : p0(gmock_p0), p1(gmock_p1), \
+        p2(gmock_p2), p3(gmock_p3) {\
+      const char* gmock_param_names[] = { #p0, #p1, #p2, #p3, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+    p2##_type p2;\
+    p3##_type p3;\
+    ::testing::internal::Interpolations gmock_interp_;\
+  };\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type>\
+  inline name##MatcherP4<p0##_type, p1##_type, p2##_type, \
+      p3##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, \
+      p3##_type p3) {\
+    return name##MatcherP4<p0##_type, p1##_type, p2##_type, p3##_type>(p0, \
+        p1, p2, p3);\
+  }\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type>\
+  template <typename arg_type>\
+  bool name##MatcherP4<p0##_type, p1##_type, p2##_type, p3##_type>::\
+      gmock_Impl<arg_type>::Matches(arg_type arg) const
+
+#define MATCHER_P5(name, p0, p1, p2, p3, p4, description)\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type>\
+  class name##MatcherP5 {\
+   public:\
+    template <typename arg_type>\
+    class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+     public:\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+          p3##_type gmock_p3, p4##_type gmock_p4, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+               p4(gmock_p4), gmock_interp_(gmock_interp) {}\
+      virtual bool Matches(arg_type arg) const;\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+                    p4##_type>(p0, p1, p2, p3, p4));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
+      }\
+      p0##_type p0;\
+      p1##_type p1;\
+      p2##_type p2;\
+      p3##_type p3;\
+      p4##_type p4;\
+      const ::testing::internal::Interpolations gmock_interp_;\
+    };\
+    template <typename arg_type>\
+    operator ::testing::Matcher<arg_type>() const {\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, gmock_interp_));\
+    }\
+    name##MatcherP5(p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2, p3##_type gmock_p3, \
+        p4##_type gmock_p4) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+        p3(gmock_p3), p4(gmock_p4) {\
+      const char* gmock_param_names[] = { #p0, #p1, #p2, #p3, #p4, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+    p2##_type p2;\
+    p3##_type p3;\
+    p4##_type p4;\
+    ::testing::internal::Interpolations gmock_interp_;\
+  };\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type>\
+  inline name##MatcherP5<p0##_type, p1##_type, p2##_type, p3##_type, \
+      p4##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+      p4##_type p4) {\
+    return name##MatcherP5<p0##_type, p1##_type, p2##_type, p3##_type, \
+        p4##_type>(p0, p1, p2, p3, p4);\
+  }\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type>\
+  template <typename arg_type>\
+  bool name##MatcherP5<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type>::\
+      gmock_Impl<arg_type>::Matches(arg_type arg) const
+
+#define MATCHER_P6(name, p0, p1, p2, p3, p4, p5, description)\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type>\
+  class name##MatcherP6 {\
+   public:\
+    template <typename arg_type>\
+    class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+     public:\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+               p4(gmock_p4), p5(gmock_p5), gmock_interp_(gmock_interp) {}\
+      virtual bool Matches(arg_type arg) const;\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+                    p4##_type, p5##_type>(p0, p1, p2, p3, p4, p5));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
+      }\
+      p0##_type p0;\
+      p1##_type p1;\
+      p2##_type p2;\
+      p3##_type p3;\
+      p4##_type p4;\
+      p5##_type p5;\
+      const ::testing::internal::Interpolations gmock_interp_;\
+    };\
+    template <typename arg_type>\
+    operator ::testing::Matcher<arg_type>() const {\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, gmock_interp_));\
+    }\
+    name##MatcherP6(p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+        p5##_type gmock_p5) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+        p3(gmock_p3), p4(gmock_p4), p5(gmock_p5) {\
+      const char* gmock_param_names[] = { #p0, #p1, #p2, #p3, #p4, #p5, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+    p2##_type p2;\
+    p3##_type p3;\
+    p4##_type p4;\
+    p5##_type p5;\
+    ::testing::internal::Interpolations gmock_interp_;\
+  };\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type>\
+  inline name##MatcherP6<p0##_type, p1##_type, p2##_type, p3##_type, \
+      p4##_type, p5##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, \
+      p3##_type p3, p4##_type p4, p5##_type p5) {\
+    return name##MatcherP6<p0##_type, p1##_type, p2##_type, p3##_type, \
+        p4##_type, p5##_type>(p0, p1, p2, p3, p4, p5);\
+  }\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type>\
+  template <typename arg_type>\
+  bool name##MatcherP6<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+      p5##_type>::\
+      gmock_Impl<arg_type>::Matches(arg_type arg) const
+
+#define MATCHER_P7(name, p0, p1, p2, p3, p4, p5, p6, description)\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type>\
+  class name##MatcherP7 {\
+   public:\
+    template <typename arg_type>\
+    class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+     public:\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+          p6##_type gmock_p6, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+               p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
+               gmock_interp_(gmock_interp) {}\
+      virtual bool Matches(arg_type arg) const;\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+                    p4##_type, p5##_type, p6##_type>(p0, p1, p2, p3, p4, p5, \
+                    p6));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
+      }\
+      p0##_type p0;\
+      p1##_type p1;\
+      p2##_type p2;\
+      p3##_type p3;\
+      p4##_type p4;\
+      p5##_type p5;\
+      p6##_type p6;\
+      const ::testing::internal::Interpolations gmock_interp_;\
+    };\
+    template <typename arg_type>\
+    operator ::testing::Matcher<arg_type>() const {\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, p6, gmock_interp_));\
+    }\
+    name##MatcherP7(p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+        p5##_type gmock_p5, p6##_type gmock_p6) : p0(gmock_p0), p1(gmock_p1), \
+        p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), \
+        p6(gmock_p6) {\
+      const char* gmock_param_names[] = { #p0, #p1, #p2, #p3, #p4, #p5, #p6, \
+          NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+    p2##_type p2;\
+    p3##_type p3;\
+    p4##_type p4;\
+    p5##_type p5;\
+    p6##_type p6;\
+    ::testing::internal::Interpolations gmock_interp_;\
+  };\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type>\
+  inline name##MatcherP7<p0##_type, p1##_type, p2##_type, p3##_type, \
+      p4##_type, p5##_type, p6##_type> name(p0##_type p0, p1##_type p1, \
+      p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \
+      p6##_type p6) {\
+    return name##MatcherP7<p0##_type, p1##_type, p2##_type, p3##_type, \
+        p4##_type, p5##_type, p6##_type>(p0, p1, p2, p3, p4, p5, p6);\
+  }\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type>\
+  template <typename arg_type>\
+  bool name##MatcherP7<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+      p5##_type, p6##_type>::\
+      gmock_Impl<arg_type>::Matches(arg_type arg) const
+
+#define MATCHER_P8(name, p0, p1, p2, p3, p4, p5, p6, p7, description)\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type>\
+  class name##MatcherP8 {\
+   public:\
+    template <typename arg_type>\
+    class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+     public:\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+          p6##_type gmock_p6, p7##_type gmock_p7, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+               p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
+               gmock_interp_(gmock_interp) {}\
+      virtual bool Matches(arg_type arg) const;\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+                    p4##_type, p5##_type, p6##_type, p7##_type>(p0, p1, p2, \
+                    p3, p4, p5, p6, p7));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
+      }\
+      p0##_type p0;\
+      p1##_type p1;\
+      p2##_type p2;\
+      p3##_type p3;\
+      p4##_type p4;\
+      p5##_type p5;\
+      p6##_type p6;\
+      p7##_type p7;\
+      const ::testing::internal::Interpolations gmock_interp_;\
+    };\
+    template <typename arg_type>\
+    operator ::testing::Matcher<arg_type>() const {\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, p6, p7, \
+              gmock_interp_));\
+    }\
+    name##MatcherP8(p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+        p5##_type gmock_p5, p6##_type gmock_p6, \
+        p7##_type gmock_p7) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+        p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
+        p7(gmock_p7) {\
+      const char* gmock_param_names[] = { #p0, #p1, #p2, #p3, #p4, #p5, #p6, \
+          #p7, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+    p2##_type p2;\
+    p3##_type p3;\
+    p4##_type p4;\
+    p5##_type p5;\
+    p6##_type p6;\
+    p7##_type p7;\
+    ::testing::internal::Interpolations gmock_interp_;\
+  };\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type>\
+  inline name##MatcherP8<p0##_type, p1##_type, p2##_type, p3##_type, \
+      p4##_type, p5##_type, p6##_type, p7##_type> name(p0##_type p0, \
+      p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, p5##_type p5, \
+      p6##_type p6, p7##_type p7) {\
+    return name##MatcherP8<p0##_type, p1##_type, p2##_type, p3##_type, \
+        p4##_type, p5##_type, p6##_type, p7##_type>(p0, p1, p2, p3, p4, p5, \
+        p6, p7);\
+  }\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type>\
+  template <typename arg_type>\
+  bool name##MatcherP8<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+      p5##_type, p6##_type, p7##_type>::\
+      gmock_Impl<arg_type>::Matches(arg_type arg) const
+
+#define MATCHER_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, description)\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type, typename p8##_type>\
+  class name##MatcherP9 {\
+   public:\
+    template <typename arg_type>\
+    class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+     public:\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+          p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+               p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
+               p8(gmock_p8), gmock_interp_(gmock_interp) {}\
+      virtual bool Matches(arg_type arg) const;\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+                    p4##_type, p5##_type, p6##_type, p7##_type, \
+                    p8##_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
+      }\
+      p0##_type p0;\
+      p1##_type p1;\
+      p2##_type p2;\
+      p3##_type p3;\
+      p4##_type p4;\
+      p5##_type p5;\
+      p6##_type p6;\
+      p7##_type p7;\
+      p8##_type p8;\
+      const ::testing::internal::Interpolations gmock_interp_;\
+    };\
+    template <typename arg_type>\
+    operator ::testing::Matcher<arg_type>() const {\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8, \
+              gmock_interp_));\
+    }\
+    name##MatcherP9(p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+        p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \
+        p8##_type gmock_p8) : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), \
+        p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
+        p8(gmock_p8) {\
+      const char* gmock_param_names[] = { #p0, #p1, #p2, #p3, #p4, #p5, #p6, \
+          #p7, #p8, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+    p2##_type p2;\
+    p3##_type p3;\
+    p4##_type p4;\
+    p5##_type p5;\
+    p6##_type p6;\
+    p7##_type p7;\
+    p8##_type p8;\
+    ::testing::internal::Interpolations gmock_interp_;\
+  };\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type, typename p8##_type>\
+  inline name##MatcherP9<p0##_type, p1##_type, p2##_type, p3##_type, \
+      p4##_type, p5##_type, p6##_type, p7##_type, \
+      p8##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+      p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, \
+      p8##_type p8) {\
+    return name##MatcherP9<p0##_type, p1##_type, p2##_type, p3##_type, \
+        p4##_type, p5##_type, p6##_type, p7##_type, p8##_type>(p0, p1, p2, \
+        p3, p4, p5, p6, p7, p8);\
+  }\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type, typename p8##_type>\
+  template <typename arg_type>\
+  bool name##MatcherP9<p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+      p5##_type, p6##_type, p7##_type, p8##_type>::\
+      gmock_Impl<arg_type>::Matches(arg_type arg) const
+
+#define MATCHER_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, description)\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type, typename p8##_type, \
+      typename p9##_type>\
+  class name##MatcherP10 {\
+   public:\
+    template <typename arg_type>\
+    class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\
+     public:\
+      gmock_Impl(p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+          p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+          p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \
+          p9##_type gmock_p9, \
+          const ::testing::internal::Interpolations& gmock_interp)\
+           : p0(gmock_p0), p1(gmock_p1), p2(gmock_p2), p3(gmock_p3), \
+               p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), p7(gmock_p7), \
+               p8(gmock_p8), p9(gmock_p9), gmock_interp_(gmock_interp) {}\
+      virtual bool Matches(arg_type arg) const;\
+      virtual void DescribeTo(::std::ostream* gmock_os) const {\
+        const ::testing::internal::Strings& gmock_printed_params = \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\
+                ::std::tr1::tuple<p0##_type, p1##_type, p2##_type, p3##_type, \
+                    p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, \
+                    p9##_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9));\
+        *gmock_os << ::testing::internal::FormatMatcherDescription(\
+                     #name, description, gmock_interp_, gmock_printed_params);\
+      }\
+      p0##_type p0;\
+      p1##_type p1;\
+      p2##_type p2;\
+      p3##_type p3;\
+      p4##_type p4;\
+      p5##_type p5;\
+      p6##_type p6;\
+      p7##_type p7;\
+      p8##_type p8;\
+      p9##_type p9;\
+      const ::testing::internal::Interpolations gmock_interp_;\
+    };\
+    template <typename arg_type>\
+    operator ::testing::Matcher<arg_type>() const {\
+      return ::testing::Matcher<arg_type>(\
+          new gmock_Impl<arg_type>(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, \
+              gmock_interp_));\
+    }\
+    name##MatcherP10(p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2, p3##_type gmock_p3, p4##_type gmock_p4, \
+        p5##_type gmock_p5, p6##_type gmock_p6, p7##_type gmock_p7, \
+        p8##_type gmock_p8, p9##_type gmock_p9) : p0(gmock_p0), p1(gmock_p1), \
+        p2(gmock_p2), p3(gmock_p3), p4(gmock_p4), p5(gmock_p5), p6(gmock_p6), \
+        p7(gmock_p7), p8(gmock_p8), p9(gmock_p9) {\
+      const char* gmock_param_names[] = { #p0, #p1, #p2, #p3, #p4, #p5, #p6, \
+          #p7, #p8, #p9, NULL };\
+      gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\
+          gmock_param_names, ("" description ""));\
+    }\
+    p0##_type p0;\
+    p1##_type p1;\
+    p2##_type p2;\
+    p3##_type p3;\
+    p4##_type p4;\
+    p5##_type p5;\
+    p6##_type p6;\
+    p7##_type p7;\
+    p8##_type p8;\
+    p9##_type p9;\
+    ::testing::internal::Interpolations gmock_interp_;\
+  };\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type, typename p8##_type, \
+      typename p9##_type>\
+  inline name##MatcherP10<p0##_type, p1##_type, p2##_type, p3##_type, \
+      p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, \
+      p9##_type> name(p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+      p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \
+      p9##_type p9) {\
+    return name##MatcherP10<p0##_type, p1##_type, p2##_type, p3##_type, \
+        p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, p9##_type>(p0, \
+        p1, p2, p3, p4, p5, p6, p7, p8, p9);\
+  }\
+  template <typename p0##_type, typename p1##_type, typename p2##_type, \
+      typename p3##_type, typename p4##_type, typename p5##_type, \
+      typename p6##_type, typename p7##_type, typename p8##_type, \
+      typename p9##_type>\
+  template <typename arg_type>\
+  bool name##MatcherP10<p0##_type, p1##_type, p2##_type, p3##_type, \
+      p4##_type, p5##_type, p6##_type, p7##_type, p8##_type, p9##_type>::\
+      gmock_Impl<arg_type>::Matches(arg_type arg) const
+
+#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_
diff --git a/third_party/gmock/include/gmock/gmock-generated-nice-strict.h b/third_party/gmock/include/gmock/gmock-generated-nice-strict.h
new file mode 100644
index 0000000..fc9a81b
--- /dev/null
+++ b/third_party/gmock/include/gmock/gmock-generated-nice-strict.h
@@ -0,0 +1,268 @@
+// This file was GENERATED by a script.  DO NOT EDIT BY HAND!!!
+
+// 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Implements class templates NiceMock and StrictMock.
+//
+// Given a mock class MockFoo that is created using Google Mock,
+// NiceMock<MockFoo> is a subclass of MockFoo that allows
+// uninteresting calls (i.e. calls to mock methods that have no
+// EXPECT_CALL specs), and StrictMock<MockFoo> is a subclass of
+// MockFoo that treats all uninteresting calls as errors.
+//
+// NiceMock and StrictMock "inherits" the constructors of their
+// respective base class, with up-to 10 arguments.  Therefore you can
+// write NiceMock<MockFoo>(5, "a") to construct a nice mock where
+// MockFoo has a constructor that accepts (int, const char*), for
+// example.
+//
+// A known limitation is that NiceMock<MockFoo> and
+// StrictMock<MockFoo> only works for mock methods defined using the
+// MOCK_METHOD* family of macros DIRECTLY in the MockFoo class.  If a
+// mock method is defined in a base class of MockFoo, the "nice" or
+// "strict" modifier may not affect it, depending on the compiler.  In
+// particular, nesting NiceMock and StrictMock is NOT supported.
+//
+// Another known limitation is that the constructors of the base mock
+// cannot have arguments passed by non-const reference, which are
+// banned by the Google C++ style guide anyway.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_
+
+#include <gmock/gmock-spec-builders.h>
+#include <gmock/internal/gmock-port.h>
+
+namespace testing {
+
+template <class MockClass>
+class NiceMock : public MockClass {
+ public:
+  // We don't factor out the constructor body to a common method, as
+  // we have to avoid a possible clash with members of MockClass.
+  NiceMock() {
+    ::testing::Mock::AllowUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  // C++ doesn't (yet) allow inheritance of constructors, so we have
+  // to define it for each arity.
+  template <typename A1>
+  explicit NiceMock(const A1& a1) : MockClass(a1) {
+    ::testing::Mock::AllowUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+  template <typename A1, typename A2>
+  NiceMock(const A1& a1, const A2& a2) : MockClass(a1, a2) {
+    ::testing::Mock::AllowUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  template <typename A1, typename A2, typename A3>
+  NiceMock(const A1& a1, const A2& a2, const A3& a3) : MockClass(a1, a2, a3) {
+    ::testing::Mock::AllowUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  template <typename A1, typename A2, typename A3, typename A4>
+  NiceMock(const A1& a1, const A2& a2, const A3& a3,
+      const A4& a4) : MockClass(a1, a2, a3, a4) {
+    ::testing::Mock::AllowUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  template <typename A1, typename A2, typename A3, typename A4, typename A5>
+  NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+      const A5& a5) : MockClass(a1, a2, a3, a4, a5) {
+    ::testing::Mock::AllowUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  template <typename A1, typename A2, typename A3, typename A4, typename A5,
+      typename A6>
+  NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+      const A5& a5, const A6& a6) : MockClass(a1, a2, a3, a4, a5, a6) {
+    ::testing::Mock::AllowUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  template <typename A1, typename A2, typename A3, typename A4, typename A5,
+      typename A6, typename A7>
+  NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+      const A5& a5, const A6& a6, const A7& a7) : MockClass(a1, a2, a3, a4, a5,
+      a6, a7) {
+    ::testing::Mock::AllowUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  template <typename A1, typename A2, typename A3, typename A4, typename A5,
+      typename A6, typename A7, typename A8>
+  NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+      const A5& a5, const A6& a6, const A7& a7, const A8& a8) : MockClass(a1,
+      a2, a3, a4, a5, a6, a7, a8) {
+    ::testing::Mock::AllowUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  template <typename A1, typename A2, typename A3, typename A4, typename A5,
+      typename A6, typename A7, typename A8, typename A9>
+  NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+      const A5& a5, const A6& a6, const A7& a7, const A8& a8,
+      const A9& a9) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9) {
+    ::testing::Mock::AllowUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  template <typename A1, typename A2, typename A3, typename A4, typename A5,
+      typename A6, typename A7, typename A8, typename A9, typename A10>
+  NiceMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+      const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9,
+      const A10& a10) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {
+    ::testing::Mock::AllowUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  virtual ~NiceMock() {
+    ::testing::Mock::UnregisterCallReaction(
+        internal::implicit_cast<MockClass*>(this));
+  }
+};
+
+template <class MockClass>
+class StrictMock : public MockClass {
+ public:
+  // We don't factor out the constructor body to a common method, as
+  // we have to avoid a possible clash with members of MockClass.
+  StrictMock() {
+    ::testing::Mock::FailUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  template <typename A1>
+  explicit StrictMock(const A1& a1) : MockClass(a1) {
+    ::testing::Mock::FailUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+  template <typename A1, typename A2>
+  StrictMock(const A1& a1, const A2& a2) : MockClass(a1, a2) {
+    ::testing::Mock::FailUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  template <typename A1, typename A2, typename A3>
+  StrictMock(const A1& a1, const A2& a2, const A3& a3) : MockClass(a1, a2, a3) {
+    ::testing::Mock::FailUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  template <typename A1, typename A2, typename A3, typename A4>
+  StrictMock(const A1& a1, const A2& a2, const A3& a3,
+      const A4& a4) : MockClass(a1, a2, a3, a4) {
+    ::testing::Mock::FailUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  template <typename A1, typename A2, typename A3, typename A4, typename A5>
+  StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+      const A5& a5) : MockClass(a1, a2, a3, a4, a5) {
+    ::testing::Mock::FailUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  template <typename A1, typename A2, typename A3, typename A4, typename A5,
+      typename A6>
+  StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+      const A5& a5, const A6& a6) : MockClass(a1, a2, a3, a4, a5, a6) {
+    ::testing::Mock::FailUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  template <typename A1, typename A2, typename A3, typename A4, typename A5,
+      typename A6, typename A7>
+  StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+      const A5& a5, const A6& a6, const A7& a7) : MockClass(a1, a2, a3, a4, a5,
+      a6, a7) {
+    ::testing::Mock::FailUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  template <typename A1, typename A2, typename A3, typename A4, typename A5,
+      typename A6, typename A7, typename A8>
+  StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+      const A5& a5, const A6& a6, const A7& a7, const A8& a8) : MockClass(a1,
+      a2, a3, a4, a5, a6, a7, a8) {
+    ::testing::Mock::FailUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  template <typename A1, typename A2, typename A3, typename A4, typename A5,
+      typename A6, typename A7, typename A8, typename A9>
+  StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+      const A5& a5, const A6& a6, const A7& a7, const A8& a8,
+      const A9& a9) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9) {
+    ::testing::Mock::FailUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  template <typename A1, typename A2, typename A3, typename A4, typename A5,
+      typename A6, typename A7, typename A8, typename A9, typename A10>
+  StrictMock(const A1& a1, const A2& a2, const A3& a3, const A4& a4,
+      const A5& a5, const A6& a6, const A7& a7, const A8& a8, const A9& a9,
+      const A10& a10) : MockClass(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {
+    ::testing::Mock::FailUninterestingCalls(
+        internal::implicit_cast<MockClass*>(this));
+  }
+
+  virtual ~StrictMock() {
+    ::testing::Mock::UnregisterCallReaction(
+        internal::implicit_cast<MockClass*>(this));
+  }
+};
+
+// The following specializations catch some (relatively more common)
+// user errors of nesting nice and strict mocks.  They do NOT catch
+// all possible errors.
+
+// These specializations are declared but not defined, as NiceMock and
+// StrictMock cannot be nested.
+template <typename MockClass>
+class NiceMock<NiceMock<MockClass> >;
+template <typename MockClass>
+class NiceMock<StrictMock<MockClass> >;
+template <typename MockClass>
+class StrictMock<NiceMock<MockClass> >;
+template <typename MockClass>
+class StrictMock<StrictMock<MockClass> >;
+
+}  // namespace testing
+
+#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_NICE_STRICT_H_
diff --git a/third_party/gmock/include/gmock/gmock-matchers.h b/third_party/gmock/include/gmock/gmock-matchers.h
new file mode 100644
index 0000000..deb0946
--- /dev/null
+++ b/third_party/gmock/include/gmock/gmock-matchers.h
@@ -0,0 +1,2799 @@
+// Copyright 2007, 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements some commonly used argument matchers.  More
+// matchers can be defined by the user implementing the
+// MatcherInterface<T> interface if necessary.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
+
+#include <algorithm>
+#include <limits>
+#include <ostream>  // NOLINT
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <gmock/gmock-printers.h>
+#include <gmock/internal/gmock-internal-utils.h>
+#include <gmock/internal/gmock-port.h>
+#include <gtest/gtest.h>
+
+namespace testing {
+
+// To implement a matcher Foo for type T, define:
+//   1. a class FooMatcherImpl that implements the
+//      MatcherInterface<T> interface, and
+//   2. a factory function that creates a Matcher<T> object from a
+//      FooMatcherImpl*.
+//
+// The two-level delegation design makes it possible to allow a user
+// to write "v" instead of "Eq(v)" where a Matcher is expected, which
+// is impossible if we pass matchers by pointers.  It also eases
+// ownership management as Matcher objects can now be copied like
+// plain values.
+
+// The implementation of a matcher.
+template <typename T>
+class MatcherInterface {
+ public:
+  virtual ~MatcherInterface() {}
+
+  // Returns true iff the matcher matches x.
+  virtual bool Matches(T x) const = 0;
+
+  // Describes this matcher to an ostream.
+  virtual void DescribeTo(::std::ostream* os) const = 0;
+
+  // Describes the negation of this matcher to an ostream.  For
+  // example, if the description of this matcher is "is greater than
+  // 7", the negated description could be "is not greater than 7".
+  // You are not required to override this when implementing
+  // MatcherInterface, but it is highly advised so that your matcher
+  // can produce good error messages.
+  virtual void DescribeNegationTo(::std::ostream* os) const {
+    *os << "not (";
+    DescribeTo(os);
+    *os << ")";
+  }
+
+  // Explains why x matches, or doesn't match, the matcher.  Override
+  // this to provide any additional information that helps a user
+  // understand the match result.
+  virtual void ExplainMatchResultTo(T /* x */, ::std::ostream* /* os */) const {
+    // By default, nothing more needs to be explained, as Google Mock
+    // has already printed the value of x when this function is
+    // called.
+  }
+};
+
+namespace internal {
+
+// An internal class for implementing Matcher<T>, which will derive
+// from it.  We put functionalities common to all Matcher<T>
+// specializations here to avoid code duplication.
+template <typename T>
+class MatcherBase {
+ public:
+  // Returns true iff this matcher matches x.
+  bool Matches(T x) const { return impl_->Matches(x); }
+
+  // Describes this matcher to an ostream.
+  void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); }
+
+  // Describes the negation of this matcher to an ostream.
+  void DescribeNegationTo(::std::ostream* os) const {
+    impl_->DescribeNegationTo(os);
+  }
+
+  // Explains why x matches, or doesn't match, the matcher.
+  void ExplainMatchResultTo(T x, ::std::ostream* os) const {
+    impl_->ExplainMatchResultTo(x, os);
+  }
+ protected:
+  MatcherBase() {}
+
+  // Constructs a matcher from its implementation.
+  explicit MatcherBase(const MatcherInterface<T>* impl)
+      : impl_(impl) {}
+
+  virtual ~MatcherBase() {}
+ private:
+  // shared_ptr (util/gtl/shared_ptr.h) and linked_ptr have similar
+  // interfaces.  The former dynamically allocates a chunk of memory
+  // to hold the reference count, while the latter tracks all
+  // references using a circular linked list without allocating
+  // memory.  It has been observed that linked_ptr performs better in
+  // typical scenarios.  However, shared_ptr can out-perform
+  // linked_ptr when there are many more uses of the copy constructor
+  // than the default constructor.
+  //
+  // If performance becomes a problem, we should see if using
+  // shared_ptr helps.
+  ::testing::internal::linked_ptr<const MatcherInterface<T> > impl_;
+};
+
+// The default implementation of ExplainMatchResultTo() for
+// polymorphic matchers.
+template <typename PolymorphicMatcherImpl, typename T>
+inline void ExplainMatchResultTo(const PolymorphicMatcherImpl& /* impl */,
+                                 const T& /* x */,
+                                 ::std::ostream* /* os */) {
+  // By default, nothing more needs to be said, as Google Mock already
+  // prints the value of x elsewhere.
+}
+
+}  // namespace internal
+
+// A Matcher<T> is a copyable and IMMUTABLE (except by assignment)
+// object that can check whether a value of type T matches.  The
+// implementation of Matcher<T> is just a linked_ptr to const
+// MatcherInterface<T>, so copying is fairly cheap.  Don't inherit
+// from Matcher!
+template <typename T>
+class Matcher : public internal::MatcherBase<T> {
+ public:
+  // Constructs a null matcher.  Needed for storing Matcher objects in
+  // STL containers.
+  Matcher() {}
+
+  // Constructs a matcher from its implementation.
+  explicit Matcher(const MatcherInterface<T>* impl)
+      : internal::MatcherBase<T>(impl) {}
+
+  // Implicit constructor here allows people to write
+  // EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes
+  Matcher(T value);  // NOLINT
+};
+
+// The following two specializations allow the user to write str
+// instead of Eq(str) and "foo" instead of Eq("foo") when a string
+// matcher is expected.
+template <>
+class Matcher<const internal::string&>
+    : public internal::MatcherBase<const internal::string&> {
+ public:
+  Matcher() {}
+
+  explicit Matcher(const MatcherInterface<const internal::string&>* impl)
+      : internal::MatcherBase<const internal::string&>(impl) {}
+
+  // Allows the user to write str instead of Eq(str) sometimes, where
+  // str is a string object.
+  Matcher(const internal::string& s);  // NOLINT
+
+  // Allows the user to write "foo" instead of Eq("foo") sometimes.
+  Matcher(const char* s);  // NOLINT
+};
+
+template <>
+class Matcher<internal::string>
+    : public internal::MatcherBase<internal::string> {
+ public:
+  Matcher() {}
+
+  explicit Matcher(const MatcherInterface<internal::string>* impl)
+      : internal::MatcherBase<internal::string>(impl) {}
+
+  // Allows the user to write str instead of Eq(str) sometimes, where
+  // str is a string object.
+  Matcher(const internal::string& s);  // NOLINT
+
+  // Allows the user to write "foo" instead of Eq("foo") sometimes.
+  Matcher(const char* s);  // NOLINT
+};
+
+// The PolymorphicMatcher class template makes it easy to implement a
+// polymorphic matcher (i.e. a matcher that can match values of more
+// than one type, e.g. Eq(n) and NotNull()).
+//
+// To define a polymorphic matcher, a user first provides a Impl class
+// that has a Matches() method, a DescribeTo() method, and a
+// DescribeNegationTo() method.  The Matches() method is usually a
+// method template (such that it works with multiple types).  Then the
+// user creates the polymorphic matcher using
+// MakePolymorphicMatcher().  To provide additional explanation to the
+// match result, define a FREE function (or function template)
+//
+//   void ExplainMatchResultTo(const Impl& matcher, const Value& value,
+//                             ::std::ostream* os);
+//
+// in the SAME NAME SPACE where Impl is defined.  See the definition
+// of NotNull() for a complete example.
+template <class Impl>
+class PolymorphicMatcher {
+ public:
+  explicit PolymorphicMatcher(const Impl& impl) : impl_(impl) {}
+
+  // Returns a mutable reference to the underlying matcher
+  // implementation object.
+  Impl& mutable_impl() { return impl_; }
+
+  // Returns an immutable reference to the underlying matcher
+  // implementation object.
+  const Impl& impl() const { return impl_; }
+
+  template <typename T>
+  operator Matcher<T>() const {
+    return Matcher<T>(new MonomorphicImpl<T>(impl_));
+  }
+ private:
+  template <typename T>
+  class MonomorphicImpl : public MatcherInterface<T> {
+   public:
+    explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {}
+
+    virtual bool Matches(T x) const { return impl_.Matches(x); }
+
+    virtual void DescribeTo(::std::ostream* os) const {
+      impl_.DescribeTo(os);
+    }
+
+    virtual void DescribeNegationTo(::std::ostream* os) const {
+      impl_.DescribeNegationTo(os);
+    }
+
+    virtual void ExplainMatchResultTo(T x, ::std::ostream* os) const {
+      using ::testing::internal::ExplainMatchResultTo;
+
+      // C++ uses Argument-Dependent Look-up (aka Koenig Look-up) to
+      // resolve the call to ExplainMatchResultTo() here.  This
+      // means that if there's a ExplainMatchResultTo() function
+      // defined in the name space where class Impl is defined, it
+      // will be picked by the compiler as the better match.
+      // Otherwise the default implementation of it in
+      // ::testing::internal will be picked.
+      //
+      // This look-up rule lets a writer of a polymorphic matcher
+      // customize the behavior of ExplainMatchResultTo() when he
+      // cares to.  Nothing needs to be done by the writer if he
+      // doesn't need to customize it.
+      ExplainMatchResultTo(impl_, x, os);
+    }
+
+   private:
+    const Impl impl_;
+  };
+
+  Impl impl_;
+};
+
+// Creates a matcher from its implementation.  This is easier to use
+// than the Matcher<T> constructor as it doesn't require you to
+// explicitly write the template argument, e.g.
+//
+//   MakeMatcher(foo);
+// vs
+//   Matcher<const string&>(foo);
+template <typename T>
+inline Matcher<T> MakeMatcher(const MatcherInterface<T>* impl) {
+  return Matcher<T>(impl);
+};
+
+// Creates a polymorphic matcher from its implementation.  This is
+// easier to use than the PolymorphicMatcher<Impl> constructor as it
+// doesn't require you to explicitly write the template argument, e.g.
+//
+//   MakePolymorphicMatcher(foo);
+// vs
+//   PolymorphicMatcher<TypeOfFoo>(foo);
+template <class Impl>
+inline PolymorphicMatcher<Impl> MakePolymorphicMatcher(const Impl& impl) {
+  return PolymorphicMatcher<Impl>(impl);
+}
+
+// In order to be safe and clear, casting between different matcher
+// types is done explicitly via MatcherCast<T>(m), which takes a
+// matcher m and returns a Matcher<T>.  It compiles only when T can be
+// statically converted to the argument type of m.
+template <typename T, typename M>
+Matcher<T> MatcherCast(M m);
+
+// Implements SafeMatcherCast().
+//
+// We use an intermediate class to do the actual safe casting as Nokia's
+// Symbian compiler cannot decide between
+// template <T, M> ... (M) and
+// template <T, U> ... (const Matcher<U>&)
+// for function templates but can for member function templates.
+template <typename T>
+class SafeMatcherCastImpl {
+ public:
+  // This overload handles polymorphic matchers only since monomorphic
+  // matchers are handled by the next one.
+  template <typename M>
+  static inline Matcher<T> Cast(M polymorphic_matcher) {
+    return Matcher<T>(polymorphic_matcher);
+  }
+
+  // This overload handles monomorphic matchers.
+  //
+  // In general, if type T can be implicitly converted to type U, we can
+  // safely convert a Matcher<U> to a Matcher<T> (i.e. Matcher is
+  // contravariant): just keep a copy of the original Matcher<U>, convert the
+  // argument from type T to U, and then pass it to the underlying Matcher<U>.
+  // The only exception is when U is a reference and T is not, as the
+  // underlying Matcher<U> may be interested in the argument's address, which
+  // is not preserved in the conversion from T to U.
+  template <typename U>
+  static inline Matcher<T> Cast(const Matcher<U>& matcher) {
+    // Enforce that T can be implicitly converted to U.
+    GMOCK_COMPILE_ASSERT_((internal::ImplicitlyConvertible<T, U>::value),
+                          T_must_be_implicitly_convertible_to_U);
+    // Enforce that we are not converting a non-reference type T to a reference
+    // type U.
+    GMOCK_COMPILE_ASSERT_(
+        internal::is_reference<T>::value || !internal::is_reference<U>::value,
+        cannot_convert_non_referentce_arg_to_reference);
+    // In case both T and U are arithmetic types, enforce that the
+    // conversion is not lossy.
+    typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(T)) RawT;
+    typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(U)) RawU;
+    const bool kTIsOther = GMOCK_KIND_OF_(RawT) == internal::kOther;
+    const bool kUIsOther = GMOCK_KIND_OF_(RawU) == internal::kOther;
+    GMOCK_COMPILE_ASSERT_(
+        kTIsOther || kUIsOther ||
+        (internal::LosslessArithmeticConvertible<RawT, RawU>::value),
+        conversion_of_arithmetic_types_must_be_lossless);
+    return MatcherCast<T>(matcher);
+  }
+};
+
+template <typename T, typename M>
+inline Matcher<T> SafeMatcherCast(const M& polymorphic_matcher) {
+  return SafeMatcherCastImpl<T>::Cast(polymorphic_matcher);
+}
+
+// A<T>() returns a matcher that matches any value of type T.
+template <typename T>
+Matcher<T> A();
+
+// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION
+// and MUST NOT BE USED IN USER CODE!!!
+namespace internal {
+
+// Appends the explanation on the result of matcher.Matches(value) to
+// os iff the explanation is not empty.
+template <typename T>
+void ExplainMatchResultAsNeededTo(const Matcher<T>& matcher, T value,
+                                  ::std::ostream* os) {
+  ::std::stringstream reason;
+  matcher.ExplainMatchResultTo(value, &reason);
+  const internal::string s = reason.str();
+  if (s != "") {
+    *os << " (" << s << ")";
+  }
+}
+
+// An internal helper class for doing compile-time loop on a tuple's
+// fields.
+template <size_t N>
+class TuplePrefix {
+ public:
+  // TuplePrefix<N>::Matches(matcher_tuple, value_tuple) returns true
+  // iff the first N fields of matcher_tuple matches the first N
+  // fields of value_tuple, respectively.
+  template <typename MatcherTuple, typename ValueTuple>
+  static bool Matches(const MatcherTuple& matcher_tuple,
+                      const ValueTuple& value_tuple) {
+    using ::std::tr1::get;
+    return TuplePrefix<N - 1>::Matches(matcher_tuple, value_tuple)
+        && get<N - 1>(matcher_tuple).Matches(get<N - 1>(value_tuple));
+  }
+
+  // TuplePrefix<N>::DescribeMatchFailuresTo(matchers, values, os)
+  // describes failures in matching the first N fields of matchers
+  // against the first N fields of values.  If there is no failure,
+  // nothing will be streamed to os.
+  template <typename MatcherTuple, typename ValueTuple>
+  static void DescribeMatchFailuresTo(const MatcherTuple& matchers,
+                                      const ValueTuple& values,
+                                      ::std::ostream* os) {
+    using ::std::tr1::tuple_element;
+    using ::std::tr1::get;
+
+    // First, describes failures in the first N - 1 fields.
+    TuplePrefix<N - 1>::DescribeMatchFailuresTo(matchers, values, os);
+
+    // Then describes the failure (if any) in the (N - 1)-th (0-based)
+    // field.
+    typename tuple_element<N - 1, MatcherTuple>::type matcher =
+        get<N - 1>(matchers);
+    typedef typename tuple_element<N - 1, ValueTuple>::type Value;
+    Value value = get<N - 1>(values);
+    if (!matcher.Matches(value)) {
+      // TODO(wan): include in the message the name of the parameter
+      // as used in MOCK_METHOD*() when possible.
+      *os << "  Expected arg #" << N - 1 << ": ";
+      get<N - 1>(matchers).DescribeTo(os);
+      *os << "\n           Actual: ";
+      // We remove the reference in type Value to prevent the
+      // universal printer from printing the address of value, which
+      // isn't interesting to the user most of the time.  The
+      // matcher's ExplainMatchResultTo() method handles the case when
+      // the address is interesting.
+      internal::UniversalPrinter<GMOCK_REMOVE_REFERENCE_(Value)>::
+          Print(value, os);
+      ExplainMatchResultAsNeededTo<Value>(matcher, value, os);
+      *os << "\n";
+    }
+  }
+};
+
+// The base case.
+template <>
+class TuplePrefix<0> {
+ public:
+  template <typename MatcherTuple, typename ValueTuple>
+  static bool Matches(const MatcherTuple& /* matcher_tuple */,
+                      const ValueTuple& /* value_tuple */) {
+    return true;
+  }
+
+  template <typename MatcherTuple, typename ValueTuple>
+  static void DescribeMatchFailuresTo(const MatcherTuple& /* matchers */,
+                                      const ValueTuple& /* values */,
+                                      ::std::ostream* /* os */) {}
+};
+
+// TupleMatches(matcher_tuple, value_tuple) returns true iff all
+// matchers in matcher_tuple match the corresponding fields in
+// value_tuple.  It is a compiler error if matcher_tuple and
+// value_tuple have different number of fields or incompatible field
+// types.
+template <typename MatcherTuple, typename ValueTuple>
+bool TupleMatches(const MatcherTuple& matcher_tuple,
+                  const ValueTuple& value_tuple) {
+  using ::std::tr1::tuple_size;
+  // Makes sure that matcher_tuple and value_tuple have the same
+  // number of fields.
+  GMOCK_COMPILE_ASSERT_(tuple_size<MatcherTuple>::value ==
+                        tuple_size<ValueTuple>::value,
+                        matcher_and_value_have_different_numbers_of_fields);
+  return TuplePrefix<tuple_size<ValueTuple>::value>::
+      Matches(matcher_tuple, value_tuple);
+}
+
+// Describes failures in matching matchers against values.  If there
+// is no failure, nothing will be streamed to os.
+template <typename MatcherTuple, typename ValueTuple>
+void DescribeMatchFailureTupleTo(const MatcherTuple& matchers,
+                                 const ValueTuple& values,
+                                 ::std::ostream* os) {
+  using ::std::tr1::tuple_size;
+  TuplePrefix<tuple_size<MatcherTuple>::value>::DescribeMatchFailuresTo(
+      matchers, values, os);
+}
+
+// The MatcherCastImpl class template is a helper for implementing
+// MatcherCast().  We need this helper in order to partially
+// specialize the implementation of MatcherCast() (C++ allows
+// class/struct templates to be partially specialized, but not
+// function templates.).
+
+// This general version is used when MatcherCast()'s argument is a
+// polymorphic matcher (i.e. something that can be converted to a
+// Matcher but is not one yet; for example, Eq(value)).
+template <typename T, typename M>
+class MatcherCastImpl {
+ public:
+  static Matcher<T> Cast(M polymorphic_matcher) {
+    return Matcher<T>(polymorphic_matcher);
+  }
+};
+
+// This more specialized version is used when MatcherCast()'s argument
+// is already a Matcher.  This only compiles when type T can be
+// statically converted to type U.
+template <typename T, typename U>
+class MatcherCastImpl<T, Matcher<U> > {
+ public:
+  static Matcher<T> Cast(const Matcher<U>& source_matcher) {
+    return Matcher<T>(new Impl(source_matcher));
+  }
+ private:
+  class Impl : public MatcherInterface<T> {
+   public:
+    explicit Impl(const Matcher<U>& source_matcher)
+        : source_matcher_(source_matcher) {}
+
+    // We delegate the matching logic to the source matcher.
+    virtual bool Matches(T x) const {
+      return source_matcher_.Matches(static_cast<U>(x));
+    }
+
+    virtual void DescribeTo(::std::ostream* os) const {
+      source_matcher_.DescribeTo(os);
+    }
+
+    virtual void DescribeNegationTo(::std::ostream* os) const {
+      source_matcher_.DescribeNegationTo(os);
+    }
+
+    virtual void ExplainMatchResultTo(T x, ::std::ostream* os) const {
+      source_matcher_.ExplainMatchResultTo(static_cast<U>(x), os);
+    }
+   private:
+    const Matcher<U> source_matcher_;
+  };
+};
+
+// This even more specialized version is used for efficiently casting
+// a matcher to its own type.
+template <typename T>
+class MatcherCastImpl<T, Matcher<T> > {
+ public:
+  static Matcher<T> Cast(const Matcher<T>& matcher) { return matcher; }
+};
+
+// Implements A<T>().
+template <typename T>
+class AnyMatcherImpl : public MatcherInterface<T> {
+ public:
+  virtual bool Matches(T /* x */) const { return true; }
+  virtual void DescribeTo(::std::ostream* os) const { *os << "is anything"; }
+  virtual void DescribeNegationTo(::std::ostream* os) const {
+    // This is mostly for completeness' safe, as it's not very useful
+    // to write Not(A<bool>()).  However we cannot completely rule out
+    // such a possibility, and it doesn't hurt to be prepared.
+    *os << "never matches";
+  }
+};
+
+// Implements _, a matcher that matches any value of any
+// type.  This is a polymorphic matcher, so we need a template type
+// conversion operator to make it appearing as a Matcher<T> for any
+// type T.
+class AnythingMatcher {
+ public:
+  template <typename T>
+  operator Matcher<T>() const { return A<T>(); }
+};
+
+// Implements a matcher that compares a given value with a
+// pre-supplied value using one of the ==, <=, <, etc, operators.  The
+// two values being compared don't have to have the same type.
+//
+// The matcher defined here is polymorphic (for example, Eq(5) can be
+// used to match an int, a short, a double, etc).  Therefore we use
+// a template type conversion operator in the implementation.
+//
+// We define this as a macro in order to eliminate duplicated source
+// code.
+//
+// The following template definition assumes that the Rhs parameter is
+// a "bare" type (i.e. neither 'const T' nor 'T&').
+#define GMOCK_IMPLEMENT_COMPARISON_MATCHER_(name, op, relation) \
+  template <typename Rhs> class name##Matcher { \
+   public: \
+    explicit name##Matcher(const Rhs& rhs) : rhs_(rhs) {} \
+    template <typename Lhs> \
+    operator Matcher<Lhs>() const { \
+      return MakeMatcher(new Impl<Lhs>(rhs_)); \
+    } \
+   private: \
+    template <typename Lhs> \
+    class Impl : public MatcherInterface<Lhs> { \
+     public: \
+      explicit Impl(const Rhs& rhs) : rhs_(rhs) {} \
+      virtual bool Matches(Lhs lhs) const { return lhs op rhs_; } \
+      virtual void DescribeTo(::std::ostream* os) const { \
+        *os << "is " relation  " "; \
+        UniversalPrinter<Rhs>::Print(rhs_, os); \
+      } \
+      virtual void DescribeNegationTo(::std::ostream* os) const { \
+        *os << "is not " relation  " "; \
+        UniversalPrinter<Rhs>::Print(rhs_, os); \
+      } \
+     private: \
+      Rhs rhs_; \
+    }; \
+    Rhs rhs_; \
+  }
+
+// Implements Eq(v), Ge(v), Gt(v), Le(v), Lt(v), and Ne(v)
+// respectively.
+GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Eq, ==, "equal to");
+GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Ge, >=, "greater than or equal to");
+GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Gt, >, "greater than");
+GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Le, <=, "less than or equal to");
+GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Lt, <, "less than");
+GMOCK_IMPLEMENT_COMPARISON_MATCHER_(Ne, !=, "not equal to");
+
+#undef GMOCK_IMPLEMENT_COMPARISON_MATCHER_
+
+// Implements the polymorphic IsNull() matcher, which matches any raw or smart
+// pointer that is NULL.
+class IsNullMatcher {
+ public:
+  template <typename Pointer>
+  bool Matches(const Pointer& p) const { return GetRawPointer(p) == NULL; }
+
+  void DescribeTo(::std::ostream* os) const { *os << "is NULL"; }
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "is not NULL";
+  }
+};
+
+// Implements the polymorphic NotNull() matcher, which matches any raw or smart
+// pointer that is not NULL.
+class NotNullMatcher {
+ public:
+  template <typename Pointer>
+  bool Matches(const Pointer& p) const { return GetRawPointer(p) != NULL; }
+
+  void DescribeTo(::std::ostream* os) const { *os << "is not NULL"; }
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "is NULL";
+  }
+};
+
+// Ref(variable) matches any argument that is a reference to
+// 'variable'.  This matcher is polymorphic as it can match any
+// super type of the type of 'variable'.
+//
+// The RefMatcher template class implements Ref(variable).  It can
+// only be instantiated with a reference type.  This prevents a user
+// from mistakenly using Ref(x) to match a non-reference function
+// argument.  For example, the following will righteously cause a
+// compiler error:
+//
+//   int n;
+//   Matcher<int> m1 = Ref(n);   // This won't compile.
+//   Matcher<int&> m2 = Ref(n);  // This will compile.
+template <typename T>
+class RefMatcher;
+
+template <typename T>
+class RefMatcher<T&> {
+  // Google Mock is a generic framework and thus needs to support
+  // mocking any function types, including those that take non-const
+  // reference arguments.  Therefore the template parameter T (and
+  // Super below) can be instantiated to either a const type or a
+  // non-const type.
+ public:
+  // RefMatcher() takes a T& instead of const T&, as we want the
+  // compiler to catch using Ref(const_value) as a matcher for a
+  // non-const reference.
+  explicit RefMatcher(T& x) : object_(x) {}  // NOLINT
+
+  template <typename Super>
+  operator Matcher<Super&>() const {
+    // By passing object_ (type T&) to Impl(), which expects a Super&,
+    // we make sure that Super is a super type of T.  In particular,
+    // this catches using Ref(const_value) as a matcher for a
+    // non-const reference, as you cannot implicitly convert a const
+    // reference to a non-const reference.
+    return MakeMatcher(new Impl<Super>(object_));
+  }
+ private:
+  template <typename Super>
+  class Impl : public MatcherInterface<Super&> {
+   public:
+    explicit Impl(Super& x) : object_(x) {}  // NOLINT
+
+    // Matches() takes a Super& (as opposed to const Super&) in
+    // order to match the interface MatcherInterface<Super&>.
+    virtual bool Matches(Super& x) const { return &x == &object_; }  // NOLINT
+
+    virtual void DescribeTo(::std::ostream* os) const {
+      *os << "references the variable ";
+      UniversalPrinter<Super&>::Print(object_, os);
+    }
+
+    virtual void DescribeNegationTo(::std::ostream* os) const {
+      *os << "does not reference the variable ";
+      UniversalPrinter<Super&>::Print(object_, os);
+    }
+
+    virtual void ExplainMatchResultTo(Super& x,  // NOLINT
+                                      ::std::ostream* os) const {
+      *os << "is located @" << static_cast<const void*>(&x);
+    }
+   private:
+    const Super& object_;
+  };
+
+  T& object_;
+};
+
+// Polymorphic helper functions for narrow and wide string matchers.
+inline bool CaseInsensitiveCStringEquals(const char* lhs, const char* rhs) {
+  return String::CaseInsensitiveCStringEquals(lhs, rhs);
+}
+
+inline bool CaseInsensitiveCStringEquals(const wchar_t* lhs,
+                                         const wchar_t* rhs) {
+  return String::CaseInsensitiveWideCStringEquals(lhs, rhs);
+}
+
+// String comparison for narrow or wide strings that can have embedded NUL
+// characters.
+template <typename StringType>
+bool CaseInsensitiveStringEquals(const StringType& s1,
+                                 const StringType& s2) {
+  // Are the heads equal?
+  if (!CaseInsensitiveCStringEquals(s1.c_str(), s2.c_str())) {
+    return false;
+  }
+
+  // Skip the equal heads.
+  const typename StringType::value_type nul = 0;
+  const size_t i1 = s1.find(nul), i2 = s2.find(nul);
+
+  // Are we at the end of either s1 or s2?
+  if (i1 == StringType::npos || i2 == StringType::npos) {
+    return i1 == i2;
+  }
+
+  // Are the tails equal?
+  return CaseInsensitiveStringEquals(s1.substr(i1 + 1), s2.substr(i2 + 1));
+}
+
+// String matchers.
+
+// Implements equality-based string matchers like StrEq, StrCaseNe, and etc.
+template <typename StringType>
+class StrEqualityMatcher {
+ public:
+  typedef typename StringType::const_pointer ConstCharPointer;
+
+  StrEqualityMatcher(const StringType& str, bool expect_eq,
+                     bool case_sensitive)
+      : string_(str), expect_eq_(expect_eq), case_sensitive_(case_sensitive) {}
+
+  // When expect_eq_ is true, returns true iff s is equal to string_;
+  // otherwise returns true iff s is not equal to string_.
+  bool Matches(ConstCharPointer s) const {
+    if (s == NULL) {
+      return !expect_eq_;
+    }
+    return Matches(StringType(s));
+  }
+
+  bool Matches(const StringType& s) const {
+    const bool eq = case_sensitive_ ? s == string_ :
+        CaseInsensitiveStringEquals(s, string_);
+    return expect_eq_ == eq;
+  }
+
+  void DescribeTo(::std::ostream* os) const {
+    DescribeToHelper(expect_eq_, os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const {
+    DescribeToHelper(!expect_eq_, os);
+  }
+ private:
+  void DescribeToHelper(bool expect_eq, ::std::ostream* os) const {
+    *os << "is ";
+    if (!expect_eq) {
+      *os << "not ";
+    }
+    *os << "equal to ";
+    if (!case_sensitive_) {
+      *os << "(ignoring case) ";
+    }
+    UniversalPrinter<StringType>::Print(string_, os);
+  }
+
+  const StringType string_;
+  const bool expect_eq_;
+  const bool case_sensitive_;
+};
+
+// Implements the polymorphic HasSubstr(substring) matcher, which
+// can be used as a Matcher<T> as long as T can be converted to a
+// string.
+template <typename StringType>
+class HasSubstrMatcher {
+ public:
+  typedef typename StringType::const_pointer ConstCharPointer;
+
+  explicit HasSubstrMatcher(const StringType& substring)
+      : substring_(substring) {}
+
+  // These overloaded methods allow HasSubstr(substring) to be used as a
+  // Matcher<T> as long as T can be converted to string.  Returns true
+  // iff s contains substring_ as a substring.
+  bool Matches(ConstCharPointer s) const {
+    return s != NULL && Matches(StringType(s));
+  }
+
+  bool Matches(const StringType& s) const {
+    return s.find(substring_) != StringType::npos;
+  }
+
+  // Describes what this matcher matches.
+  void DescribeTo(::std::ostream* os) const {
+    *os << "has substring ";
+    UniversalPrinter<StringType>::Print(substring_, os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "has no substring ";
+    UniversalPrinter<StringType>::Print(substring_, os);
+  }
+ private:
+  const StringType substring_;
+};
+
+// Implements the polymorphic StartsWith(substring) matcher, which
+// can be used as a Matcher<T> as long as T can be converted to a
+// string.
+template <typename StringType>
+class StartsWithMatcher {
+ public:
+  typedef typename StringType::const_pointer ConstCharPointer;
+
+  explicit StartsWithMatcher(const StringType& prefix) : prefix_(prefix) {
+  }
+
+  // These overloaded methods allow StartsWith(prefix) to be used as a
+  // Matcher<T> as long as T can be converted to string.  Returns true
+  // iff s starts with prefix_.
+  bool Matches(ConstCharPointer s) const {
+    return s != NULL && Matches(StringType(s));
+  }
+
+  bool Matches(const StringType& s) const {
+    return s.length() >= prefix_.length() &&
+        s.substr(0, prefix_.length()) == prefix_;
+  }
+
+  void DescribeTo(::std::ostream* os) const {
+    *os << "starts with ";
+    UniversalPrinter<StringType>::Print(prefix_, os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "doesn't start with ";
+    UniversalPrinter<StringType>::Print(prefix_, os);
+  }
+ private:
+  const StringType prefix_;
+};
+
+// Implements the polymorphic EndsWith(substring) matcher, which
+// can be used as a Matcher<T> as long as T can be converted to a
+// string.
+template <typename StringType>
+class EndsWithMatcher {
+ public:
+  typedef typename StringType::const_pointer ConstCharPointer;
+
+  explicit EndsWithMatcher(const StringType& suffix) : suffix_(suffix) {}
+
+  // These overloaded methods allow EndsWith(suffix) to be used as a
+  // Matcher<T> as long as T can be converted to string.  Returns true
+  // iff s ends with suffix_.
+  bool Matches(ConstCharPointer s) const {
+    return s != NULL && Matches(StringType(s));
+  }
+
+  bool Matches(const StringType& s) const {
+    return s.length() >= suffix_.length() &&
+        s.substr(s.length() - suffix_.length()) == suffix_;
+  }
+
+  void DescribeTo(::std::ostream* os) const {
+    *os << "ends with ";
+    UniversalPrinter<StringType>::Print(suffix_, os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "doesn't end with ";
+    UniversalPrinter<StringType>::Print(suffix_, os);
+  }
+ private:
+  const StringType suffix_;
+};
+
+#if GMOCK_HAS_REGEX
+
+// Implements polymorphic matchers MatchesRegex(regex) and
+// ContainsRegex(regex), which can be used as a Matcher<T> as long as
+// T can be converted to a string.
+class MatchesRegexMatcher {
+ public:
+  MatchesRegexMatcher(const RE* regex, bool full_match)
+      : regex_(regex), full_match_(full_match) {}
+
+  // These overloaded methods allow MatchesRegex(regex) to be used as
+  // a Matcher<T> as long as T can be converted to string.  Returns
+  // true iff s matches regular expression regex.  When full_match_ is
+  // true, a full match is done; otherwise a partial match is done.
+  bool Matches(const char* s) const {
+    return s != NULL && Matches(internal::string(s));
+  }
+
+  bool Matches(const internal::string& s) const {
+    return full_match_ ? RE::FullMatch(s, *regex_) :
+        RE::PartialMatch(s, *regex_);
+  }
+
+  void DescribeTo(::std::ostream* os) const {
+    *os << (full_match_ ? "matches" : "contains")
+        << " regular expression ";
+    UniversalPrinter<internal::string>::Print(regex_->pattern(), os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "doesn't " << (full_match_ ? "match" : "contain")
+        << " regular expression ";
+    UniversalPrinter<internal::string>::Print(regex_->pattern(), os);
+  }
+ private:
+  const internal::linked_ptr<const RE> regex_;
+  const bool full_match_;
+};
+
+#endif  // GMOCK_HAS_REGEX
+
+// Implements a matcher that compares the two fields of a 2-tuple
+// using one of the ==, <=, <, etc, operators.  The two fields being
+// compared don't have to have the same type.
+//
+// The matcher defined here is polymorphic (for example, Eq() can be
+// used to match a tuple<int, short>, a tuple<const long&, double>,
+// etc).  Therefore we use a template type conversion operator in the
+// implementation.
+//
+// We define this as a macro in order to eliminate duplicated source
+// code.
+#define GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(name, op) \
+  class name##2Matcher { \
+   public: \
+    template <typename T1, typename T2> \
+    operator Matcher<const ::std::tr1::tuple<T1, T2>&>() const { \
+      return MakeMatcher(new Impl<T1, T2>); \
+    } \
+   private: \
+    template <typename T1, typename T2> \
+    class Impl : public MatcherInterface<const ::std::tr1::tuple<T1, T2>&> { \
+     public: \
+      virtual bool Matches(const ::std::tr1::tuple<T1, T2>& args) const { \
+        return ::std::tr1::get<0>(args) op ::std::tr1::get<1>(args); \
+      } \
+      virtual void DescribeTo(::std::ostream* os) const { \
+        *os << "are a pair (x, y) where x " #op " y"; \
+      } \
+      virtual void DescribeNegationTo(::std::ostream* os) const { \
+        *os << "are a pair (x, y) where x " #op " y is false"; \
+      } \
+    }; \
+  }
+
+// Implements Eq(), Ge(), Gt(), Le(), Lt(), and Ne() respectively.
+GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(Eq, ==);
+GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(Ge, >=);
+GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(Gt, >);
+GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(Le, <=);
+GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(Lt, <);
+GMOCK_IMPLEMENT_COMPARISON2_MATCHER_(Ne, !=);
+
+#undef GMOCK_IMPLEMENT_COMPARISON2_MATCHER_
+
+// Implements the Not(...) matcher for a particular argument type T.
+// We do not nest it inside the NotMatcher class template, as that
+// will prevent different instantiations of NotMatcher from sharing
+// the same NotMatcherImpl<T> class.
+template <typename T>
+class NotMatcherImpl : public MatcherInterface<T> {
+ public:
+  explicit NotMatcherImpl(const Matcher<T>& matcher)
+      : matcher_(matcher) {}
+
+  virtual bool Matches(T x) const {
+    return !matcher_.Matches(x);
+  }
+
+  virtual void DescribeTo(::std::ostream* os) const {
+    matcher_.DescribeNegationTo(os);
+  }
+
+  virtual void DescribeNegationTo(::std::ostream* os) const {
+    matcher_.DescribeTo(os);
+  }
+
+  virtual void ExplainMatchResultTo(T x, ::std::ostream* os) const {
+    matcher_.ExplainMatchResultTo(x, os);
+  }
+ private:
+  const Matcher<T> matcher_;
+};
+
+// Implements the Not(m) matcher, which matches a value that doesn't
+// match matcher m.
+template <typename InnerMatcher>
+class NotMatcher {
+ public:
+  explicit NotMatcher(InnerMatcher matcher) : matcher_(matcher) {}
+
+  // This template type conversion operator allows Not(m) to be used
+  // to match any type m can match.
+  template <typename T>
+  operator Matcher<T>() const {
+    return Matcher<T>(new NotMatcherImpl<T>(SafeMatcherCast<T>(matcher_)));
+  }
+ private:
+  InnerMatcher matcher_;
+};
+
+// Implements the AllOf(m1, m2) matcher for a particular argument type
+// T. We do not nest it inside the BothOfMatcher class template, as
+// that will prevent different instantiations of BothOfMatcher from
+// sharing the same BothOfMatcherImpl<T> class.
+template <typename T>
+class BothOfMatcherImpl : public MatcherInterface<T> {
+ public:
+  BothOfMatcherImpl(const Matcher<T>& matcher1, const Matcher<T>& matcher2)
+      : matcher1_(matcher1), matcher2_(matcher2) {}
+
+  virtual bool Matches(T x) const {
+    return matcher1_.Matches(x) && matcher2_.Matches(x);
+  }
+
+  virtual void DescribeTo(::std::ostream* os) const {
+    *os << "(";
+    matcher1_.DescribeTo(os);
+    *os << ") and (";
+    matcher2_.DescribeTo(os);
+    *os << ")";
+  }
+
+  virtual void DescribeNegationTo(::std::ostream* os) const {
+    *os << "not ";
+    DescribeTo(os);
+  }
+
+  virtual void ExplainMatchResultTo(T x, ::std::ostream* os) const {
+    if (Matches(x)) {
+      // When both matcher1_ and matcher2_ match x, we need to
+      // explain why *both* of them match.
+      ::std::stringstream ss1;
+      matcher1_.ExplainMatchResultTo(x, &ss1);
+      const internal::string s1 = ss1.str();
+
+      ::std::stringstream ss2;
+      matcher2_.ExplainMatchResultTo(x, &ss2);
+      const internal::string s2 = ss2.str();
+
+      if (s1 == "") {
+        *os << s2;
+      } else {
+        *os << s1;
+        if (s2 != "") {
+          *os << "; " << s2;
+        }
+      }
+    } else {
+      // Otherwise we only need to explain why *one* of them fails
+      // to match.
+      if (!matcher1_.Matches(x)) {
+        matcher1_.ExplainMatchResultTo(x, os);
+      } else {
+        matcher2_.ExplainMatchResultTo(x, os);
+      }
+    }
+  }
+ private:
+  const Matcher<T> matcher1_;
+  const Matcher<T> matcher2_;
+};
+
+// Used for implementing the AllOf(m_1, ..., m_n) matcher, which
+// matches a value that matches all of the matchers m_1, ..., and m_n.
+template <typename Matcher1, typename Matcher2>
+class BothOfMatcher {
+ public:
+  BothOfMatcher(Matcher1 matcher1, Matcher2 matcher2)
+      : matcher1_(matcher1), matcher2_(matcher2) {}
+
+  // This template type conversion operator allows a
+  // BothOfMatcher<Matcher1, Matcher2> object to match any type that
+  // both Matcher1 and Matcher2 can match.
+  template <typename T>
+  operator Matcher<T>() const {
+    return Matcher<T>(new BothOfMatcherImpl<T>(SafeMatcherCast<T>(matcher1_),
+                                               SafeMatcherCast<T>(matcher2_)));
+  }
+ private:
+  Matcher1 matcher1_;
+  Matcher2 matcher2_;
+};
+
+// Implements the AnyOf(m1, m2) matcher for a particular argument type
+// T.  We do not nest it inside the AnyOfMatcher class template, as
+// that will prevent different instantiations of AnyOfMatcher from
+// sharing the same EitherOfMatcherImpl<T> class.
+template <typename T>
+class EitherOfMatcherImpl : public MatcherInterface<T> {
+ public:
+  EitherOfMatcherImpl(const Matcher<T>& matcher1, const Matcher<T>& matcher2)
+      : matcher1_(matcher1), matcher2_(matcher2) {}
+
+  virtual bool Matches(T x) const {
+    return matcher1_.Matches(x) || matcher2_.Matches(x);
+  }
+
+  virtual void DescribeTo(::std::ostream* os) const {
+    *os << "(";
+    matcher1_.DescribeTo(os);
+    *os << ") or (";
+    matcher2_.DescribeTo(os);
+    *os << ")";
+  }
+
+  virtual void DescribeNegationTo(::std::ostream* os) const {
+    *os << "not ";
+    DescribeTo(os);
+  }
+
+  virtual void ExplainMatchResultTo(T x, ::std::ostream* os) const {
+    if (Matches(x)) {
+      // If either matcher1_ or matcher2_ matches x, we just need
+      // to explain why *one* of them matches.
+      if (matcher1_.Matches(x)) {
+        matcher1_.ExplainMatchResultTo(x, os);
+      } else {
+        matcher2_.ExplainMatchResultTo(x, os);
+      }
+    } else {
+      // Otherwise we need to explain why *neither* matches.
+      ::std::stringstream ss1;
+      matcher1_.ExplainMatchResultTo(x, &ss1);
+      const internal::string s1 = ss1.str();
+
+      ::std::stringstream ss2;
+      matcher2_.ExplainMatchResultTo(x, &ss2);
+      const internal::string s2 = ss2.str();
+
+      if (s1 == "") {
+        *os << s2;
+      } else {
+        *os << s1;
+        if (s2 != "") {
+          *os << "; " << s2;
+        }
+      }
+    }
+  }
+ private:
+  const Matcher<T> matcher1_;
+  const Matcher<T> matcher2_;
+};
+
+// Used for implementing the AnyOf(m_1, ..., m_n) matcher, which
+// matches a value that matches at least one of the matchers m_1, ...,
+// and m_n.
+template <typename Matcher1, typename Matcher2>
+class EitherOfMatcher {
+ public:
+  EitherOfMatcher(Matcher1 matcher1, Matcher2 matcher2)
+      : matcher1_(matcher1), matcher2_(matcher2) {}
+
+  // This template type conversion operator allows a
+  // EitherOfMatcher<Matcher1, Matcher2> object to match any type that
+  // both Matcher1 and Matcher2 can match.
+  template <typename T>
+  operator Matcher<T>() const {
+    return Matcher<T>(new EitherOfMatcherImpl<T>(
+        SafeMatcherCast<T>(matcher1_), SafeMatcherCast<T>(matcher2_)));
+  }
+ private:
+  Matcher1 matcher1_;
+  Matcher2 matcher2_;
+};
+
+// Used for implementing Truly(pred), which turns a predicate into a
+// matcher.
+template <typename Predicate>
+class TrulyMatcher {
+ public:
+  explicit TrulyMatcher(Predicate pred) : predicate_(pred) {}
+
+  // This method template allows Truly(pred) to be used as a matcher
+  // for type T where T is the argument type of predicate 'pred'.  The
+  // argument is passed by reference as the predicate may be
+  // interested in the address of the argument.
+  template <typename T>
+  bool Matches(T& x) const {  // NOLINT
+#if GTEST_OS_WINDOWS
+    // MSVC warns about converting a value into bool (warning 4800).
+#pragma warning(push)          // Saves the current warning state.
+#pragma warning(disable:4800)  // Temporarily disables warning 4800.
+#endif  // GTEST_OS_WINDOWS
+    return predicate_(x);
+#if GTEST_OS_WINDOWS
+#pragma warning(pop)           // Restores the warning state.
+#endif  // GTEST_OS_WINDOWS
+  }
+
+  void DescribeTo(::std::ostream* os) const {
+    *os << "satisfies the given predicate";
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "doesn't satisfy the given predicate";
+  }
+ private:
+  Predicate predicate_;
+};
+
+// Used for implementing Matches(matcher), which turns a matcher into
+// a predicate.
+template <typename M>
+class MatcherAsPredicate {
+ public:
+  explicit MatcherAsPredicate(M matcher) : matcher_(matcher) {}
+
+  // This template operator() allows Matches(m) to be used as a
+  // predicate on type T where m is a matcher on type T.
+  //
+  // The argument x is passed by reference instead of by value, as
+  // some matcher may be interested in its address (e.g. as in
+  // Matches(Ref(n))(x)).
+  template <typename T>
+  bool operator()(const T& x) const {
+    // We let matcher_ commit to a particular type here instead of
+    // when the MatcherAsPredicate object was constructed.  This
+    // allows us to write Matches(m) where m is a polymorphic matcher
+    // (e.g. Eq(5)).
+    //
+    // If we write Matcher<T>(matcher_).Matches(x) here, it won't
+    // compile when matcher_ has type Matcher<const T&>; if we write
+    // Matcher<const T&>(matcher_).Matches(x) here, it won't compile
+    // when matcher_ has type Matcher<T>; if we just write
+    // matcher_.Matches(x), it won't compile when matcher_ is
+    // polymorphic, e.g. Eq(5).
+    //
+    // MatcherCast<const T&>() is necessary for making the code work
+    // in all of the above situations.
+    return MatcherCast<const T&>(matcher_).Matches(x);
+  }
+ private:
+  M matcher_;
+};
+
+// For implementing ASSERT_THAT() and EXPECT_THAT().  The template
+// argument M must be a type that can be converted to a matcher.
+template <typename M>
+class PredicateFormatterFromMatcher {
+ public:
+  explicit PredicateFormatterFromMatcher(const M& m) : matcher_(m) {}
+
+  // This template () operator allows a PredicateFormatterFromMatcher
+  // object to act as a predicate-formatter suitable for using with
+  // Google Test's EXPECT_PRED_FORMAT1() macro.
+  template <typename T>
+  AssertionResult operator()(const char* value_text, const T& x) const {
+    // We convert matcher_ to a Matcher<const T&> *now* instead of
+    // when the PredicateFormatterFromMatcher object was constructed,
+    // as matcher_ may be polymorphic (e.g. NotNull()) and we won't
+    // know which type to instantiate it to until we actually see the
+    // type of x here.
+    //
+    // We write MatcherCast<const T&>(matcher_) instead of
+    // Matcher<const T&>(matcher_), as the latter won't compile when
+    // matcher_ has type Matcher<T> (e.g. An<int>()).
+    const Matcher<const T&> matcher = MatcherCast<const T&>(matcher_);
+    if (matcher.Matches(x)) {
+      return AssertionSuccess();
+    } else {
+      ::std::stringstream ss;
+      ss << "Value of: " << value_text << "\n"
+         << "Expected: ";
+      matcher.DescribeTo(&ss);
+      ss << "\n  Actual: ";
+      UniversalPrinter<T>::Print(x, &ss);
+      ExplainMatchResultAsNeededTo<const T&>(matcher, x, &ss);
+      return AssertionFailure(Message() << ss.str());
+    }
+  }
+ private:
+  const M matcher_;
+};
+
+// A helper function for converting a matcher to a predicate-formatter
+// without the user needing to explicitly write the type.  This is
+// used for implementing ASSERT_THAT() and EXPECT_THAT().
+template <typename M>
+inline PredicateFormatterFromMatcher<M>
+MakePredicateFormatterFromMatcher(const M& matcher) {
+  return PredicateFormatterFromMatcher<M>(matcher);
+}
+
+// Implements the polymorphic floating point equality matcher, which
+// matches two float values using ULP-based approximation.  The
+// template is meant to be instantiated with FloatType being either
+// float or double.
+template <typename FloatType>
+class FloatingEqMatcher {
+ public:
+  // Constructor for FloatingEqMatcher.
+  // The matcher's input will be compared with rhs.  The matcher treats two
+  // NANs as equal if nan_eq_nan is true.  Otherwise, under IEEE standards,
+  // equality comparisons between NANs will always return false.
+  FloatingEqMatcher(FloatType rhs, bool nan_eq_nan) :
+    rhs_(rhs), nan_eq_nan_(nan_eq_nan) {}
+
+  // Implements floating point equality matcher as a Matcher<T>.
+  template <typename T>
+  class Impl : public MatcherInterface<T> {
+   public:
+    Impl(FloatType rhs, bool nan_eq_nan) :
+      rhs_(rhs), nan_eq_nan_(nan_eq_nan) {}
+
+    virtual bool Matches(T value) const {
+      const FloatingPoint<FloatType> lhs(value), rhs(rhs_);
+
+      // Compares NaNs first, if nan_eq_nan_ is true.
+      if (nan_eq_nan_ && lhs.is_nan()) {
+        return rhs.is_nan();
+      }
+
+      return lhs.AlmostEquals(rhs);
+    }
+
+    virtual void DescribeTo(::std::ostream* os) const {
+      // os->precision() returns the previously set precision, which we
+      // store to restore the ostream to its original configuration
+      // after outputting.
+      const ::std::streamsize old_precision = os->precision(
+          ::std::numeric_limits<FloatType>::digits10 + 2);
+      if (FloatingPoint<FloatType>(rhs_).is_nan()) {
+        if (nan_eq_nan_) {
+          *os << "is NaN";
+        } else {
+          *os << "never matches";
+        }
+      } else {
+        *os << "is approximately " << rhs_;
+      }
+      os->precision(old_precision);
+    }
+
+    virtual void DescribeNegationTo(::std::ostream* os) const {
+      // As before, get original precision.
+      const ::std::streamsize old_precision = os->precision(
+          ::std::numeric_limits<FloatType>::digits10 + 2);
+      if (FloatingPoint<FloatType>(rhs_).is_nan()) {
+        if (nan_eq_nan_) {
+          *os << "is not NaN";
+        } else {
+          *os << "is anything";
+        }
+      } else {
+        *os << "is not approximately " << rhs_;
+      }
+      // Restore original precision.
+      os->precision(old_precision);
+    }
+
+   private:
+    const FloatType rhs_;
+    const bool nan_eq_nan_;
+  };
+
+  // The following 3 type conversion operators allow FloatEq(rhs) and
+  // NanSensitiveFloatEq(rhs) to be used as a Matcher<float>, a
+  // Matcher<const float&>, or a Matcher<float&>, but nothing else.
+  // (While Google's C++ coding style doesn't allow arguments passed
+  // by non-const reference, we may see them in code not conforming to
+  // the style.  Therefore Google Mock needs to support them.)
+  operator Matcher<FloatType>() const {
+    return MakeMatcher(new Impl<FloatType>(rhs_, nan_eq_nan_));
+  }
+
+  operator Matcher<const FloatType&>() const {
+    return MakeMatcher(new Impl<const FloatType&>(rhs_, nan_eq_nan_));
+  }
+
+  operator Matcher<FloatType&>() const {
+    return MakeMatcher(new Impl<FloatType&>(rhs_, nan_eq_nan_));
+  }
+ private:
+  const FloatType rhs_;
+  const bool nan_eq_nan_;
+};
+
+// Implements the Pointee(m) matcher for matching a pointer whose
+// pointee matches matcher m.  The pointer can be either raw or smart.
+template <typename InnerMatcher>
+class PointeeMatcher {
+ public:
+  explicit PointeeMatcher(const InnerMatcher& matcher) : matcher_(matcher) {}
+
+  // This type conversion operator template allows Pointee(m) to be
+  // used as a matcher for any pointer type whose pointee type is
+  // compatible with the inner matcher, where type Pointer can be
+  // either a raw pointer or a smart pointer.
+  //
+  // The reason we do this instead of relying on
+  // MakePolymorphicMatcher() is that the latter is not flexible
+  // enough for implementing the DescribeTo() method of Pointee().
+  template <typename Pointer>
+  operator Matcher<Pointer>() const {
+    return MakeMatcher(new Impl<Pointer>(matcher_));
+  }
+ private:
+  // The monomorphic implementation that works for a particular pointer type.
+  template <typename Pointer>
+  class Impl : public MatcherInterface<Pointer> {
+   public:
+    typedef typename PointeeOf<GMOCK_REMOVE_CONST_(  // NOLINT
+        GMOCK_REMOVE_REFERENCE_(Pointer))>::type Pointee;
+
+    explicit Impl(const InnerMatcher& matcher)
+        : matcher_(MatcherCast<const Pointee&>(matcher)) {}
+
+    virtual bool Matches(Pointer p) const {
+      return GetRawPointer(p) != NULL && matcher_.Matches(*p);
+    }
+
+    virtual void DescribeTo(::std::ostream* os) const {
+      *os << "points to a value that ";
+      matcher_.DescribeTo(os);
+    }
+
+    virtual void DescribeNegationTo(::std::ostream* os) const {
+      *os << "does not point to a value that ";
+      matcher_.DescribeTo(os);
+    }
+
+    virtual void ExplainMatchResultTo(Pointer pointer,
+                                      ::std::ostream* os) const {
+      if (GetRawPointer(pointer) == NULL)
+        return;
+
+      ::std::stringstream ss;
+      matcher_.ExplainMatchResultTo(*pointer, &ss);
+      const internal::string s = ss.str();
+      if (s != "") {
+        *os << "points to a value that " << s;
+      }
+    }
+   private:
+    const Matcher<const Pointee&> matcher_;
+  };
+
+  const InnerMatcher matcher_;
+};
+
+// Implements the Field() matcher for matching a field (i.e. member
+// variable) of an object.
+template <typename Class, typename FieldType>
+class FieldMatcher {
+ public:
+  FieldMatcher(FieldType Class::*field,
+               const Matcher<const FieldType&>& matcher)
+      : field_(field), matcher_(matcher) {}
+
+  // Returns true iff the inner matcher matches obj.field.
+  bool Matches(const Class& obj) const {
+    return matcher_.Matches(obj.*field_);
+  }
+
+  // Returns true iff the inner matcher matches obj->field.
+  bool Matches(const Class* p) const {
+    return (p != NULL) && matcher_.Matches(p->*field_);
+  }
+
+  void DescribeTo(::std::ostream* os) const {
+    *os << "the given field ";
+    matcher_.DescribeTo(os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "the given field ";
+    matcher_.DescribeNegationTo(os);
+  }
+
+  // The first argument of ExplainMatchResultTo() is needed to help
+  // Symbian's C++ compiler choose which overload to use.  Its type is
+  // true_type iff the Field() matcher is used to match a pointer.
+  void ExplainMatchResultTo(false_type /* is_not_pointer */, const Class& obj,
+                            ::std::ostream* os) const {
+    ::std::stringstream ss;
+    matcher_.ExplainMatchResultTo(obj.*field_, &ss);
+    const internal::string s = ss.str();
+    if (s != "") {
+      *os << "the given field " << s;
+    }
+  }
+
+  void ExplainMatchResultTo(true_type /* is_pointer */, const Class* p,
+                            ::std::ostream* os) const {
+    if (p != NULL) {
+      // Since *p has a field, it must be a class/struct/union type
+      // and thus cannot be a pointer.  Therefore we pass false_type()
+      // as the first argument.
+      ExplainMatchResultTo(false_type(), *p, os);
+    }
+  }
+ private:
+  const FieldType Class::*field_;
+  const Matcher<const FieldType&> matcher_;
+};
+
+// Explains the result of matching an object or pointer against a field matcher.
+template <typename Class, typename FieldType, typename T>
+void ExplainMatchResultTo(const FieldMatcher<Class, FieldType>& matcher,
+                          const T& value, ::std::ostream* os) {
+  matcher.ExplainMatchResultTo(
+      typename ::testing::internal::is_pointer<T>::type(), value, os);
+}
+
+// Implements the Property() matcher for matching a property
+// (i.e. return value of a getter method) of an object.
+template <typename Class, typename PropertyType>
+class PropertyMatcher {
+ public:
+  // The property may have a reference type, so 'const PropertyType&'
+  // may cause double references and fail to compile.  That's why we
+  // need GMOCK_REFERENCE_TO_CONST, which works regardless of
+  // PropertyType being a reference or not.
+  typedef GMOCK_REFERENCE_TO_CONST_(PropertyType) RefToConstProperty;
+
+  PropertyMatcher(PropertyType (Class::*property)() const,
+                  const Matcher<RefToConstProperty>& matcher)
+      : property_(property), matcher_(matcher) {}
+
+  // Returns true iff obj.property() matches the inner matcher.
+  bool Matches(const Class& obj) const {
+    return matcher_.Matches((obj.*property_)());
+  }
+
+  // Returns true iff p->property() matches the inner matcher.
+  bool Matches(const Class* p) const {
+    return (p != NULL) && matcher_.Matches((p->*property_)());
+  }
+
+  void DescribeTo(::std::ostream* os) const {
+    *os << "the given property ";
+    matcher_.DescribeTo(os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "the given property ";
+    matcher_.DescribeNegationTo(os);
+  }
+
+  // The first argument of ExplainMatchResultTo() is needed to help
+  // Symbian's C++ compiler choose which overload to use.  Its type is
+  // true_type iff the Property() matcher is used to match a pointer.
+  void ExplainMatchResultTo(false_type /* is_not_pointer */, const Class& obj,
+                            ::std::ostream* os) const {
+    ::std::stringstream ss;
+    matcher_.ExplainMatchResultTo((obj.*property_)(), &ss);
+    const internal::string s = ss.str();
+    if (s != "") {
+      *os << "the given property " << s;
+    }
+  }
+
+  void ExplainMatchResultTo(true_type /* is_pointer */, const Class* p,
+                            ::std::ostream* os) const {
+    if (p != NULL) {
+      // Since *p has a property method, it must be a
+      // class/struct/union type and thus cannot be a pointer.
+      // Therefore we pass false_type() as the first argument.
+      ExplainMatchResultTo(false_type(), *p, os);
+    }
+  }
+ private:
+  PropertyType (Class::*property_)() const;
+  const Matcher<RefToConstProperty> matcher_;
+};
+
+// Explains the result of matching an object or pointer against a
+// property matcher.
+template <typename Class, typename PropertyType, typename T>
+void ExplainMatchResultTo(const PropertyMatcher<Class, PropertyType>& matcher,
+                          const T& value, ::std::ostream* os) {
+  matcher.ExplainMatchResultTo(
+      typename ::testing::internal::is_pointer<T>::type(), value, os);
+}
+
+// Type traits specifying various features of different functors for ResultOf.
+// The default template specifies features for functor objects.
+// Functor classes have to typedef argument_type and result_type
+// to be compatible with ResultOf.
+template <typename Functor>
+struct CallableTraits {
+  typedef typename Functor::result_type ResultType;
+  typedef Functor StorageType;
+
+  static void CheckIsValid(Functor functor) {}
+  template <typename T>
+  static ResultType Invoke(Functor f, T arg) { return f(arg); }
+};
+
+// Specialization for function pointers.
+template <typename ArgType, typename ResType>
+struct CallableTraits<ResType(*)(ArgType)> {
+  typedef ResType ResultType;
+  typedef ResType(*StorageType)(ArgType);
+
+  static void CheckIsValid(ResType(*f)(ArgType)) {
+    GTEST_CHECK_(f != NULL)
+        << "NULL function pointer is passed into ResultOf().";
+  }
+  template <typename T>
+  static ResType Invoke(ResType(*f)(ArgType), T arg) {
+    return (*f)(arg);
+  }
+};
+
+// Implements the ResultOf() matcher for matching a return value of a
+// unary function of an object.
+template <typename Callable>
+class ResultOfMatcher {
+ public:
+  typedef typename CallableTraits<Callable>::ResultType ResultType;
+
+  ResultOfMatcher(Callable callable, const Matcher<ResultType>& matcher)
+      : callable_(callable), matcher_(matcher) {
+    CallableTraits<Callable>::CheckIsValid(callable_);
+  }
+
+  template <typename T>
+  operator Matcher<T>() const {
+    return Matcher<T>(new Impl<T>(callable_, matcher_));
+  }
+
+ private:
+  typedef typename CallableTraits<Callable>::StorageType CallableStorageType;
+
+  template <typename T>
+  class Impl : public MatcherInterface<T> {
+   public:
+    Impl(CallableStorageType callable, const Matcher<ResultType>& matcher)
+        : callable_(callable), matcher_(matcher) {}
+    // Returns true iff callable_(obj) matches the inner matcher.
+    // The calling syntax is different for different types of callables
+    // so we abstract it in CallableTraits<Callable>::Invoke().
+    virtual bool Matches(T obj) const {
+      return matcher_.Matches(
+          CallableTraits<Callable>::template Invoke<T>(callable_, obj));
+    }
+
+    virtual void DescribeTo(::std::ostream* os) const {
+      *os << "result of the given callable ";
+      matcher_.DescribeTo(os);
+    }
+
+    virtual void DescribeNegationTo(::std::ostream* os) const {
+      *os << "result of the given callable ";
+      matcher_.DescribeNegationTo(os);
+    }
+
+    virtual void ExplainMatchResultTo(T obj, ::std::ostream* os) const {
+      ::std::stringstream ss;
+      matcher_.ExplainMatchResultTo(
+          CallableTraits<Callable>::template Invoke<T>(callable_, obj),
+          &ss);
+      const internal::string s = ss.str();
+      if (s != "")
+        *os << "result of the given callable " << s;
+    }
+   private:
+    // Functors often define operator() as non-const method even though
+    // they are actualy stateless. But we need to use them even when
+    // 'this' is a const pointer. It's the user's responsibility not to
+    // use stateful callables with ResultOf(), which does't guarantee
+    // how many times the callable will be invoked.
+    mutable CallableStorageType callable_;
+    const Matcher<ResultType> matcher_;
+  };  // class Impl
+
+  const CallableStorageType callable_;
+  const Matcher<ResultType> matcher_;
+};
+
+// Explains the result of matching a value against a functor matcher.
+template <typename T, typename Callable>
+void ExplainMatchResultTo(const ResultOfMatcher<Callable>& matcher,
+                          T obj, ::std::ostream* os) {
+  matcher.ExplainMatchResultTo(obj, os);
+}
+
+// Implements an equality matcher for any STL-style container whose elements
+// support ==. This matcher is like Eq(), but its failure explanations provide
+// more detailed information that is useful when the container is used as a set.
+// The failure message reports elements that are in one of the operands but not
+// the other. The failure messages do not report duplicate or out-of-order
+// elements in the containers (which don't properly matter to sets, but can
+// occur if the containers are vectors or lists, for example).
+//
+// Uses the container's const_iterator, value_type, operator ==,
+// begin(), and end().
+template <typename Container>
+class ContainerEqMatcher {
+ public:
+  typedef internal::StlContainerView<Container> View;
+  typedef typename View::type StlContainer;
+  typedef typename View::const_reference StlContainerReference;
+
+  // We make a copy of rhs in case the elements in it are modified
+  // after this matcher is created.
+  explicit ContainerEqMatcher(const Container& rhs) : rhs_(View::Copy(rhs)) {
+    // Makes sure the user doesn't instantiate this class template
+    // with a const or reference type.
+    testing::StaticAssertTypeEq<Container,
+        GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container))>();
+  }
+
+  template <typename LhsContainer>
+  bool Matches(const LhsContainer& lhs) const {
+    // GMOCK_REMOVE_CONST_() is needed to work around an MSVC 8.0 bug
+    // that causes LhsContainer to be a const type sometimes.
+    typedef internal::StlContainerView<GMOCK_REMOVE_CONST_(LhsContainer)>
+        LhsView;
+    StlContainerReference lhs_stl_container = LhsView::ConstReference(lhs);
+    return lhs_stl_container == rhs_;
+  }
+  void DescribeTo(::std::ostream* os) const {
+    *os << "equals ";
+    UniversalPrinter<StlContainer>::Print(rhs_, os);
+  }
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "does not equal ";
+    UniversalPrinter<StlContainer>::Print(rhs_, os);
+  }
+
+  template <typename LhsContainer>
+  void ExplainMatchResultTo(const LhsContainer& lhs,
+                            ::std::ostream* os) const {
+    // GMOCK_REMOVE_CONST_() is needed to work around an MSVC 8.0 bug
+    // that causes LhsContainer to be a const type sometimes.
+    typedef internal::StlContainerView<GMOCK_REMOVE_CONST_(LhsContainer)>
+        LhsView;
+    typedef typename LhsView::type LhsStlContainer;
+    StlContainerReference lhs_stl_container = LhsView::ConstReference(lhs);
+
+    // Something is different. Check for missing values first.
+    bool printed_header = false;
+    for (typename LhsStlContainer::const_iterator it =
+             lhs_stl_container.begin();
+         it != lhs_stl_container.end(); ++it) {
+      if (internal::ArrayAwareFind(rhs_.begin(), rhs_.end(), *it) ==
+          rhs_.end()) {
+        if (printed_header) {
+          *os << ", ";
+        } else {
+          *os << "Only in actual: ";
+          printed_header = true;
+        }
+        UniversalPrinter<typename LhsStlContainer::value_type>::Print(*it, os);
+      }
+    }
+
+    // Now check for extra values.
+    bool printed_header2 = false;
+    for (typename StlContainer::const_iterator it = rhs_.begin();
+         it != rhs_.end(); ++it) {
+      if (internal::ArrayAwareFind(
+              lhs_stl_container.begin(), lhs_stl_container.end(), *it) ==
+          lhs_stl_container.end()) {
+        if (printed_header2) {
+          *os << ", ";
+        } else {
+          *os << (printed_header ? "; not" : "Not") << " in actual: ";
+          printed_header2 = true;
+        }
+        UniversalPrinter<typename StlContainer::value_type>::Print(*it, os);
+      }
+    }
+  }
+ private:
+  const StlContainer rhs_;
+};
+
+template <typename LhsContainer, typename Container>
+void ExplainMatchResultTo(const ContainerEqMatcher<Container>& matcher,
+                          const LhsContainer& lhs,
+                          ::std::ostream* os) {
+  matcher.ExplainMatchResultTo(lhs, os);
+}
+
+// Implements Contains(element_matcher) for the given argument type Container.
+template <typename Container>
+class ContainsMatcherImpl : public MatcherInterface<Container> {
+ public:
+  typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer;
+  typedef StlContainerView<RawContainer> View;
+  typedef typename View::type StlContainer;
+  typedef typename View::const_reference StlContainerReference;
+  typedef typename StlContainer::value_type Element;
+
+  template <typename InnerMatcher>
+  explicit ContainsMatcherImpl(InnerMatcher inner_matcher)
+      : inner_matcher_(
+          testing::SafeMatcherCast<const Element&>(inner_matcher)) {}
+
+  // Returns true iff 'container' matches.
+  virtual bool Matches(Container container) const {
+    StlContainerReference stl_container = View::ConstReference(container);
+    for (typename StlContainer::const_iterator it = stl_container.begin();
+         it != stl_container.end(); ++it) {
+      if (inner_matcher_.Matches(*it))
+        return true;
+    }
+    return false;
+  }
+
+  // Describes what this matcher does.
+  virtual void DescribeTo(::std::ostream* os) const {
+    *os << "contains at least one element that ";
+    inner_matcher_.DescribeTo(os);
+  }
+
+  // Describes what the negation of this matcher does.
+  virtual void DescribeNegationTo(::std::ostream* os) const {
+    *os << "doesn't contain any element that ";
+    inner_matcher_.DescribeTo(os);
+  }
+
+  // Explains why 'container' matches, or doesn't match, this matcher.
+  virtual void ExplainMatchResultTo(Container container,
+                                    ::std::ostream* os) const {
+    StlContainerReference stl_container = View::ConstReference(container);
+
+    // We need to explain which (if any) element matches inner_matcher_.
+    typename StlContainer::const_iterator it = stl_container.begin();
+    for (size_t i = 0; it != stl_container.end(); ++it, ++i) {
+      if (inner_matcher_.Matches(*it)) {
+        *os << "element " << i << " matches";
+        return;
+      }
+    }
+  }
+
+ private:
+  const Matcher<const Element&> inner_matcher_;
+};
+
+// Implements polymorphic Contains(element_matcher).
+template <typename M>
+class ContainsMatcher {
+ public:
+  explicit ContainsMatcher(M m) : inner_matcher_(m) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    return MakeMatcher(new ContainsMatcherImpl<Container>(inner_matcher_));
+  }
+
+ private:
+  const M inner_matcher_;
+};
+
+// Implements Key(inner_matcher) for the given argument pair type.
+// Key(inner_matcher) matches an std::pair whose 'first' field matches
+// inner_matcher.  For example, Contains(Key(Ge(5))) can be used to match an
+// std::map that contains at least one element whose key is >= 5.
+template <typename PairType>
+class KeyMatcherImpl : public MatcherInterface<PairType> {
+ public:
+  typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(PairType)) RawPairType;
+  typedef typename RawPairType::first_type KeyType;
+
+  template <typename InnerMatcher>
+  explicit KeyMatcherImpl(InnerMatcher inner_matcher)
+      : inner_matcher_(
+          testing::SafeMatcherCast<const KeyType&>(inner_matcher)) {
+  }
+
+  // Returns true iff 'key_value.first' (the key) matches the inner matcher.
+  virtual bool Matches(PairType key_value) const {
+    return inner_matcher_.Matches(key_value.first);
+  }
+
+  // Describes what this matcher does.
+  virtual void DescribeTo(::std::ostream* os) const {
+    *os << "has a key that ";
+    inner_matcher_.DescribeTo(os);
+  }
+
+  // Describes what the negation of this matcher does.
+  virtual void DescribeNegationTo(::std::ostream* os) const {
+    *os << "doesn't have a key that ";
+    inner_matcher_.DescribeTo(os);
+  }
+
+  // Explains why 'key_value' matches, or doesn't match, this matcher.
+  virtual void ExplainMatchResultTo(PairType key_value,
+                                    ::std::ostream* os) const {
+    inner_matcher_.ExplainMatchResultTo(key_value.first, os);
+  }
+
+ private:
+  const Matcher<const KeyType&> inner_matcher_;
+};
+
+// Implements polymorphic Key(matcher_for_key).
+template <typename M>
+class KeyMatcher {
+ public:
+  explicit KeyMatcher(M m) : matcher_for_key_(m) {}
+
+  template <typename PairType>
+  operator Matcher<PairType>() const {
+    return MakeMatcher(new KeyMatcherImpl<PairType>(matcher_for_key_));
+  }
+
+ private:
+  const M matcher_for_key_;
+};
+
+// Implements Pair(first_matcher, second_matcher) for the given argument pair
+// type with its two matchers. See Pair() function below.
+template <typename PairType>
+class PairMatcherImpl : public MatcherInterface<PairType> {
+ public:
+  typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(PairType)) RawPairType;
+  typedef typename RawPairType::first_type FirstType;
+  typedef typename RawPairType::second_type SecondType;
+
+  template <typename FirstMatcher, typename SecondMatcher>
+  PairMatcherImpl(FirstMatcher first_matcher, SecondMatcher second_matcher)
+      : first_matcher_(
+            testing::SafeMatcherCast<const FirstType&>(first_matcher)),
+        second_matcher_(
+            testing::SafeMatcherCast<const SecondType&>(second_matcher)) {
+  }
+
+  // Returns true iff 'a_pair.first' matches first_matcher and 'a_pair.second'
+  // matches second_matcher.
+  virtual bool Matches(PairType a_pair) const {
+    return first_matcher_.Matches(a_pair.first) &&
+           second_matcher_.Matches(a_pair.second);
+  }
+
+  // Describes what this matcher does.
+  virtual void DescribeTo(::std::ostream* os) const {
+    *os << "has a first field that ";
+    first_matcher_.DescribeTo(os);
+    *os << ", and has a second field that ";
+    second_matcher_.DescribeTo(os);
+  }
+
+  // Describes what the negation of this matcher does.
+  virtual void DescribeNegationTo(::std::ostream* os) const {
+    *os << "has a first field that ";
+    first_matcher_.DescribeNegationTo(os);
+    *os << ", or has a second field that ";
+    second_matcher_.DescribeNegationTo(os);
+  }
+
+  // Explains why 'a_pair' matches, or doesn't match, this matcher.
+  virtual void ExplainMatchResultTo(PairType a_pair,
+                                    ::std::ostream* os) const {
+    ::std::stringstream ss1;
+    first_matcher_.ExplainMatchResultTo(a_pair.first, &ss1);
+    internal::string s1 = ss1.str();
+    if (s1 != "") {
+       s1 = "the first field " + s1;
+    }
+
+    ::std::stringstream ss2;
+    second_matcher_.ExplainMatchResultTo(a_pair.second, &ss2);
+    internal::string s2 = ss2.str();
+    if (s2 != "") {
+       s2 = "the second field " + s2;
+    }
+
+    *os << s1;
+    if (s1 != "" && s2 != "") {
+       *os << ", and ";
+    }
+    *os << s2;
+  }
+
+ private:
+  const Matcher<const FirstType&> first_matcher_;
+  const Matcher<const SecondType&> second_matcher_;
+};
+
+// Implements polymorphic Pair(first_matcher, second_matcher).
+template <typename FirstMatcher, typename SecondMatcher>
+class PairMatcher {
+ public:
+  PairMatcher(FirstMatcher first_matcher, SecondMatcher second_matcher)
+      : first_matcher_(first_matcher), second_matcher_(second_matcher) {}
+
+  template <typename PairType>
+  operator Matcher<PairType> () const {
+    return MakeMatcher(
+        new PairMatcherImpl<PairType>(
+            first_matcher_, second_matcher_));
+  }
+
+ private:
+  const FirstMatcher first_matcher_;
+  const SecondMatcher second_matcher_;
+};
+
+// Implements ElementsAre() and ElementsAreArray().
+template <typename Container>
+class ElementsAreMatcherImpl : public MatcherInterface<Container> {
+ public:
+  typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer;
+  typedef internal::StlContainerView<RawContainer> View;
+  typedef typename View::type StlContainer;
+  typedef typename View::const_reference StlContainerReference;
+  typedef typename StlContainer::value_type Element;
+
+  // Constructs the matcher from a sequence of element values or
+  // element matchers.
+  template <typename InputIter>
+  ElementsAreMatcherImpl(InputIter first, size_t count) {
+    matchers_.reserve(count);
+    InputIter it = first;
+    for (size_t i = 0; i != count; ++i, ++it) {
+      matchers_.push_back(MatcherCast<const Element&>(*it));
+    }
+  }
+
+  // Returns true iff 'container' matches.
+  virtual bool Matches(Container container) const {
+    StlContainerReference stl_container = View::ConstReference(container);
+    if (stl_container.size() != count())
+      return false;
+
+    typename StlContainer::const_iterator it = stl_container.begin();
+    for (size_t i = 0; i != count();  ++it, ++i) {
+      if (!matchers_[i].Matches(*it))
+        return false;
+    }
+
+    return true;
+  }
+
+  // Describes what this matcher does.
+  virtual void DescribeTo(::std::ostream* os) const {
+    if (count() == 0) {
+      *os << "is empty";
+    } else if (count() == 1) {
+      *os << "has 1 element that ";
+      matchers_[0].DescribeTo(os);
+    } else {
+      *os << "has " << Elements(count()) << " where\n";
+      for (size_t i = 0; i != count(); ++i) {
+        *os << "element " << i << " ";
+        matchers_[i].DescribeTo(os);
+        if (i + 1 < count()) {
+          *os << ",\n";
+        }
+      }
+    }
+  }
+
+  // Describes what the negation of this matcher does.
+  virtual void DescribeNegationTo(::std::ostream* os) const {
+    if (count() == 0) {
+      *os << "is not empty";
+      return;
+    }
+
+    *os << "does not have " << Elements(count()) << ", or\n";
+    for (size_t i = 0; i != count(); ++i) {
+      *os << "element " << i << " ";
+      matchers_[i].DescribeNegationTo(os);
+      if (i + 1 < count()) {
+        *os << ", or\n";
+      }
+    }
+  }
+
+  // Explains why 'container' matches, or doesn't match, this matcher.
+  virtual void ExplainMatchResultTo(Container container,
+                                    ::std::ostream* os) const {
+    StlContainerReference stl_container = View::ConstReference(container);
+    if (Matches(container)) {
+      // We need to explain why *each* element matches (the obvious
+      // ones can be skipped).
+
+      bool reason_printed = false;
+      typename StlContainer::const_iterator it = stl_container.begin();
+      for (size_t i = 0; i != count(); ++it, ++i) {
+        ::std::stringstream ss;
+        matchers_[i].ExplainMatchResultTo(*it, &ss);
+
+        const string s = ss.str();
+        if (!s.empty()) {
+          if (reason_printed) {
+            *os << ",\n";
+          }
+          *os << "element " << i << " " << s;
+          reason_printed = true;
+        }
+      }
+    } else {
+      // We need to explain why the container doesn't match.
+      const size_t actual_count = stl_container.size();
+      if (actual_count != count()) {
+        // The element count doesn't match.  If the container is
+        // empty, there's no need to explain anything as Google Mock
+        // already prints the empty container.  Otherwise we just need
+        // to show how many elements there actually are.
+        if (actual_count != 0) {
+          *os << "has " << Elements(actual_count);
+        }
+        return;
+      }
+
+      // The container has the right size but at least one element
+      // doesn't match expectation.  We need to find this element and
+      // explain why it doesn't match.
+      typename StlContainer::const_iterator it = stl_container.begin();
+      for (size_t i = 0; i != count(); ++it, ++i) {
+        if (matchers_[i].Matches(*it)) {
+          continue;
+        }
+
+        *os << "element " << i << " doesn't match";
+
+        ::std::stringstream ss;
+        matchers_[i].ExplainMatchResultTo(*it, &ss);
+        const string s = ss.str();
+        if (!s.empty()) {
+          *os << " (" << s << ")";
+        }
+        return;
+      }
+    }
+  }
+
+ private:
+  static Message Elements(size_t count) {
+    return Message() << count << (count == 1 ? " element" : " elements");
+  }
+
+  size_t count() const { return matchers_.size(); }
+  std::vector<Matcher<const Element&> > matchers_;
+};
+
+// Implements ElementsAre() of 0 arguments.
+class ElementsAreMatcher0 {
+ public:
+  ElementsAreMatcher0() {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container))
+        RawContainer;
+    typedef typename internal::StlContainerView<RawContainer>::type::value_type
+        Element;
+
+    const Matcher<const Element&>* const matchers = NULL;
+    return MakeMatcher(new ElementsAreMatcherImpl<Container>(matchers, 0));
+  }
+};
+
+// Implements ElementsAreArray().
+template <typename T>
+class ElementsAreArrayMatcher {
+ public:
+  ElementsAreArrayMatcher(const T* first, size_t count) :
+      first_(first), count_(count) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container))
+        RawContainer;
+    typedef typename internal::StlContainerView<RawContainer>::type::value_type
+        Element;
+
+    return MakeMatcher(new ElementsAreMatcherImpl<Container>(first_, count_));
+  }
+
+ private:
+  const T* const first_;
+  const size_t count_;
+};
+
+// Constants denoting interpolations in a matcher description string.
+const int kTupleInterpolation = -1;    // "%(*)s"
+const int kPercentInterpolation = -2;  // "%%"
+const int kInvalidInterpolation = -3;  // "%" followed by invalid text
+
+// Records the location and content of an interpolation.
+struct Interpolation {
+  Interpolation(const char* start, const char* end, int param)
+      : start_pos(start), end_pos(end), param_index(param) {}
+
+  // Points to the start of the interpolation (the '%' character).
+  const char* start_pos;
+  // Points to the first character after the interpolation.
+  const char* end_pos;
+  // 0-based index of the interpolated matcher parameter;
+  // kTupleInterpolation for "%(*)s"; kPercentInterpolation for "%%".
+  int param_index;
+};
+
+typedef ::std::vector<Interpolation> Interpolations;
+
+// Parses a matcher description string and returns a vector of
+// interpolations that appear in the string; generates non-fatal
+// failures iff 'description' is an invalid matcher description.
+// 'param_names' is a NULL-terminated array of parameter names in the
+// order they appear in the MATCHER_P*() parameter list.
+Interpolations ValidateMatcherDescription(
+    const char* param_names[], const char* description);
+
+// Returns the actual matcher description, given the matcher name,
+// user-supplied description template string, interpolations in the
+// string, and the printed values of the matcher parameters.
+string FormatMatcherDescription(
+    const char* matcher_name, const char* description,
+    const Interpolations& interp, const Strings& param_values);
+
+}  // namespace internal
+
+// Implements MatcherCast().
+template <typename T, typename M>
+inline Matcher<T> MatcherCast(M matcher) {
+  return internal::MatcherCastImpl<T, M>::Cast(matcher);
+}
+
+// _ is a matcher that matches anything of any type.
+//
+// This definition is fine as:
+//
+//   1. The C++ standard permits using the name _ in a namespace that
+//      is not the global namespace or ::std.
+//   2. The AnythingMatcher class has no data member or constructor,
+//      so it's OK to create global variables of this type.
+//   3. c-style has approved of using _ in this case.
+const internal::AnythingMatcher _ = {};
+// Creates a matcher that matches any value of the given type T.
+template <typename T>
+inline Matcher<T> A() { return MakeMatcher(new internal::AnyMatcherImpl<T>()); }
+
+// Creates a matcher that matches any value of the given type T.
+template <typename T>
+inline Matcher<T> An() { return A<T>(); }
+
+// Creates a polymorphic matcher that matches anything equal to x.
+// Note: if the parameter of Eq() were declared as const T&, Eq("foo")
+// wouldn't compile.
+template <typename T>
+inline internal::EqMatcher<T> Eq(T x) { return internal::EqMatcher<T>(x); }
+
+// Constructs a Matcher<T> from a 'value' of type T.  The constructed
+// matcher matches any value that's equal to 'value'.
+template <typename T>
+Matcher<T>::Matcher(T value) { *this = Eq(value); }
+
+// Creates a monomorphic matcher that matches anything with type Lhs
+// and equal to rhs.  A user may need to use this instead of Eq(...)
+// in order to resolve an overloading ambiguity.
+//
+// TypedEq<T>(x) is just a convenient short-hand for Matcher<T>(Eq(x))
+// or Matcher<T>(x), but more readable than the latter.
+//
+// We could define similar monomorphic matchers for other comparison
+// operations (e.g. TypedLt, TypedGe, and etc), but decided not to do
+// it yet as those are used much less than Eq() in practice.  A user
+// can always write Matcher<T>(Lt(5)) to be explicit about the type,
+// for example.
+template <typename Lhs, typename Rhs>
+inline Matcher<Lhs> TypedEq(const Rhs& rhs) { return Eq(rhs); }
+
+// Creates a polymorphic matcher that matches anything >= x.
+template <typename Rhs>
+inline internal::GeMatcher<Rhs> Ge(Rhs x) {
+  return internal::GeMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches anything > x.
+template <typename Rhs>
+inline internal::GtMatcher<Rhs> Gt(Rhs x) {
+  return internal::GtMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches anything <= x.
+template <typename Rhs>
+inline internal::LeMatcher<Rhs> Le(Rhs x) {
+  return internal::LeMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches anything < x.
+template <typename Rhs>
+inline internal::LtMatcher<Rhs> Lt(Rhs x) {
+  return internal::LtMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches anything != x.
+template <typename Rhs>
+inline internal::NeMatcher<Rhs> Ne(Rhs x) {
+  return internal::NeMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches any NULL pointer.
+inline PolymorphicMatcher<internal::IsNullMatcher > IsNull() {
+  return MakePolymorphicMatcher(internal::IsNullMatcher());
+}
+
+// Creates a polymorphic matcher that matches any non-NULL pointer.
+// This is convenient as Not(NULL) doesn't compile (the compiler
+// thinks that that expression is comparing a pointer with an integer).
+inline PolymorphicMatcher<internal::NotNullMatcher > NotNull() {
+  return MakePolymorphicMatcher(internal::NotNullMatcher());
+}
+
+// Creates a polymorphic matcher that matches any argument that
+// references variable x.
+template <typename T>
+inline internal::RefMatcher<T&> Ref(T& x) {  // NOLINT
+  return internal::RefMatcher<T&>(x);
+}
+
+// Creates a matcher that matches any double argument approximately
+// equal to rhs, where two NANs are considered unequal.
+inline internal::FloatingEqMatcher<double> DoubleEq(double rhs) {
+  return internal::FloatingEqMatcher<double>(rhs, false);
+}
+
+// Creates a matcher that matches any double argument approximately
+// equal to rhs, including NaN values when rhs is NaN.
+inline internal::FloatingEqMatcher<double> NanSensitiveDoubleEq(double rhs) {
+  return internal::FloatingEqMatcher<double>(rhs, true);
+}
+
+// Creates a matcher that matches any float argument approximately
+// equal to rhs, where two NANs are considered unequal.
+inline internal::FloatingEqMatcher<float> FloatEq(float rhs) {
+  return internal::FloatingEqMatcher<float>(rhs, false);
+}
+
+// Creates a matcher that matches any double argument approximately
+// equal to rhs, including NaN values when rhs is NaN.
+inline internal::FloatingEqMatcher<float> NanSensitiveFloatEq(float rhs) {
+  return internal::FloatingEqMatcher<float>(rhs, true);
+}
+
+// Creates a matcher that matches a pointer (raw or smart) that points
+// to a value that matches inner_matcher.
+template <typename InnerMatcher>
+inline internal::PointeeMatcher<InnerMatcher> Pointee(
+    const InnerMatcher& inner_matcher) {
+  return internal::PointeeMatcher<InnerMatcher>(inner_matcher);
+}
+
+// Creates a matcher that matches an object whose given field matches
+// 'matcher'.  For example,
+//   Field(&Foo::number, Ge(5))
+// matches a Foo object x iff x.number >= 5.
+template <typename Class, typename FieldType, typename FieldMatcher>
+inline PolymorphicMatcher<
+  internal::FieldMatcher<Class, FieldType> > Field(
+    FieldType Class::*field, const FieldMatcher& matcher) {
+  return MakePolymorphicMatcher(
+      internal::FieldMatcher<Class, FieldType>(
+          field, MatcherCast<const FieldType&>(matcher)));
+  // The call to MatcherCast() is required for supporting inner
+  // matchers of compatible types.  For example, it allows
+  //   Field(&Foo::bar, m)
+  // to compile where bar is an int32 and m is a matcher for int64.
+}
+
+// Creates a matcher that matches an object whose given property
+// matches 'matcher'.  For example,
+//   Property(&Foo::str, StartsWith("hi"))
+// matches a Foo object x iff x.str() starts with "hi".
+template <typename Class, typename PropertyType, typename PropertyMatcher>
+inline PolymorphicMatcher<
+  internal::PropertyMatcher<Class, PropertyType> > Property(
+    PropertyType (Class::*property)() const, const PropertyMatcher& matcher) {
+  return MakePolymorphicMatcher(
+      internal::PropertyMatcher<Class, PropertyType>(
+          property,
+          MatcherCast<GMOCK_REFERENCE_TO_CONST_(PropertyType)>(matcher)));
+  // The call to MatcherCast() is required for supporting inner
+  // matchers of compatible types.  For example, it allows
+  //   Property(&Foo::bar, m)
+  // to compile where bar() returns an int32 and m is a matcher for int64.
+}
+
+// Creates a matcher that matches an object iff the result of applying
+// a callable to x matches 'matcher'.
+// For example,
+//   ResultOf(f, StartsWith("hi"))
+// matches a Foo object x iff f(x) starts with "hi".
+// callable parameter can be a function, function pointer, or a functor.
+// Callable has to satisfy the following conditions:
+//   * It is required to keep no state affecting the results of
+//     the calls on it and make no assumptions about how many calls
+//     will be made. Any state it keeps must be protected from the
+//     concurrent access.
+//   * If it is a function object, it has to define type result_type.
+//     We recommend deriving your functor classes from std::unary_function.
+template <typename Callable, typename ResultOfMatcher>
+internal::ResultOfMatcher<Callable> ResultOf(
+    Callable callable, const ResultOfMatcher& matcher) {
+  return internal::ResultOfMatcher<Callable>(
+          callable,
+          MatcherCast<typename internal::CallableTraits<Callable>::ResultType>(
+              matcher));
+  // The call to MatcherCast() is required for supporting inner
+  // matchers of compatible types.  For example, it allows
+  //   ResultOf(Function, m)
+  // to compile where Function() returns an int32 and m is a matcher for int64.
+}
+
+// String matchers.
+
+// Matches a string equal to str.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::string> >
+    StrEq(const internal::string& str) {
+  return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::string>(
+      str, true, true));
+}
+
+// Matches a string not equal to str.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::string> >
+    StrNe(const internal::string& str) {
+  return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::string>(
+      str, false, true));
+}
+
+// Matches a string equal to str, ignoring case.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::string> >
+    StrCaseEq(const internal::string& str) {
+  return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::string>(
+      str, true, false));
+}
+
+// Matches a string not equal to str, ignoring case.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::string> >
+    StrCaseNe(const internal::string& str) {
+  return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::string>(
+      str, false, false));
+}
+
+// Creates a matcher that matches any string, std::string, or C string
+// that contains the given substring.
+inline PolymorphicMatcher<internal::HasSubstrMatcher<internal::string> >
+    HasSubstr(const internal::string& substring) {
+  return MakePolymorphicMatcher(internal::HasSubstrMatcher<internal::string>(
+      substring));
+}
+
+// Matches a string that starts with 'prefix' (case-sensitive).
+inline PolymorphicMatcher<internal::StartsWithMatcher<internal::string> >
+    StartsWith(const internal::string& prefix) {
+  return MakePolymorphicMatcher(internal::StartsWithMatcher<internal::string>(
+      prefix));
+}
+
+// Matches a string that ends with 'suffix' (case-sensitive).
+inline PolymorphicMatcher<internal::EndsWithMatcher<internal::string> >
+    EndsWith(const internal::string& suffix) {
+  return MakePolymorphicMatcher(internal::EndsWithMatcher<internal::string>(
+      suffix));
+}
+
+#ifdef GMOCK_HAS_REGEX
+
+// Matches a string that fully matches regular expression 'regex'.
+// The matcher takes ownership of 'regex'.
+inline PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
+    const internal::RE* regex) {
+  return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, true));
+}
+inline PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
+    const internal::string& regex) {
+  return MatchesRegex(new internal::RE(regex));
+}
+
+// Matches a string that contains regular expression 'regex'.
+// The matcher takes ownership of 'regex'.
+inline PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
+    const internal::RE* regex) {
+  return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, false));
+}
+inline PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
+    const internal::string& regex) {
+  return ContainsRegex(new internal::RE(regex));
+}
+
+#endif  // GMOCK_HAS_REGEX
+
+#if GTEST_HAS_GLOBAL_WSTRING || GTEST_HAS_STD_WSTRING
+// Wide string matchers.
+
+// Matches a string equal to str.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::wstring> >
+    StrEq(const internal::wstring& str) {
+  return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::wstring>(
+      str, true, true));
+}
+
+// Matches a string not equal to str.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::wstring> >
+    StrNe(const internal::wstring& str) {
+  return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::wstring>(
+      str, false, true));
+}
+
+// Matches a string equal to str, ignoring case.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::wstring> >
+    StrCaseEq(const internal::wstring& str) {
+  return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::wstring>(
+      str, true, false));
+}
+
+// Matches a string not equal to str, ignoring case.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<internal::wstring> >
+    StrCaseNe(const internal::wstring& str) {
+  return MakePolymorphicMatcher(internal::StrEqualityMatcher<internal::wstring>(
+      str, false, false));
+}
+
+// Creates a matcher that matches any wstring, std::wstring, or C wide string
+// that contains the given substring.
+inline PolymorphicMatcher<internal::HasSubstrMatcher<internal::wstring> >
+    HasSubstr(const internal::wstring& substring) {
+  return MakePolymorphicMatcher(internal::HasSubstrMatcher<internal::wstring>(
+      substring));
+}
+
+// Matches a string that starts with 'prefix' (case-sensitive).
+inline PolymorphicMatcher<internal::StartsWithMatcher<internal::wstring> >
+    StartsWith(const internal::wstring& prefix) {
+  return MakePolymorphicMatcher(internal::StartsWithMatcher<internal::wstring>(
+      prefix));
+}
+
+// Matches a string that ends with 'suffix' (case-sensitive).
+inline PolymorphicMatcher<internal::EndsWithMatcher<internal::wstring> >
+    EndsWith(const internal::wstring& suffix) {
+  return MakePolymorphicMatcher(internal::EndsWithMatcher<internal::wstring>(
+      suffix));
+}
+
+#endif  // GTEST_HAS_GLOBAL_WSTRING || GTEST_HAS_STD_WSTRING
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field == the second field.
+inline internal::Eq2Matcher Eq() { return internal::Eq2Matcher(); }
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field >= the second field.
+inline internal::Ge2Matcher Ge() { return internal::Ge2Matcher(); }
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field > the second field.
+inline internal::Gt2Matcher Gt() { return internal::Gt2Matcher(); }
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field <= the second field.
+inline internal::Le2Matcher Le() { return internal::Le2Matcher(); }
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field < the second field.
+inline internal::Lt2Matcher Lt() { return internal::Lt2Matcher(); }
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field != the second field.
+inline internal::Ne2Matcher Ne() { return internal::Ne2Matcher(); }
+
+// Creates a matcher that matches any value of type T that m doesn't
+// match.
+template <typename InnerMatcher>
+inline internal::NotMatcher<InnerMatcher> Not(InnerMatcher m) {
+  return internal::NotMatcher<InnerMatcher>(m);
+}
+
+// Creates a matcher that matches any value that matches all of the
+// given matchers.
+//
+// For now we only support up to 5 matchers.  Support for more
+// matchers can be added as needed, or the user can use nested
+// AllOf()s.
+template <typename Matcher1, typename Matcher2>
+inline internal::BothOfMatcher<Matcher1, Matcher2>
+AllOf(Matcher1 m1, Matcher2 m2) {
+  return internal::BothOfMatcher<Matcher1, Matcher2>(m1, m2);
+}
+
+template <typename Matcher1, typename Matcher2, typename Matcher3>
+inline internal::BothOfMatcher<Matcher1,
+           internal::BothOfMatcher<Matcher2, Matcher3> >
+AllOf(Matcher1 m1, Matcher2 m2, Matcher3 m3) {
+  return AllOf(m1, AllOf(m2, m3));
+}
+
+template <typename Matcher1, typename Matcher2, typename Matcher3,
+          typename Matcher4>
+inline internal::BothOfMatcher<Matcher1,
+           internal::BothOfMatcher<Matcher2,
+               internal::BothOfMatcher<Matcher3, Matcher4> > >
+AllOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4) {
+  return AllOf(m1, AllOf(m2, m3, m4));
+}
+
+template <typename Matcher1, typename Matcher2, typename Matcher3,
+          typename Matcher4, typename Matcher5>
+inline internal::BothOfMatcher<Matcher1,
+           internal::BothOfMatcher<Matcher2,
+               internal::BothOfMatcher<Matcher3,
+                   internal::BothOfMatcher<Matcher4, Matcher5> > > >
+AllOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4, Matcher5 m5) {
+  return AllOf(m1, AllOf(m2, m3, m4, m5));
+}
+
+// Creates a matcher that matches any value that matches at least one
+// of the given matchers.
+//
+// For now we only support up to 5 matchers.  Support for more
+// matchers can be added as needed, or the user can use nested
+// AnyOf()s.
+template <typename Matcher1, typename Matcher2>
+inline internal::EitherOfMatcher<Matcher1, Matcher2>
+AnyOf(Matcher1 m1, Matcher2 m2) {
+  return internal::EitherOfMatcher<Matcher1, Matcher2>(m1, m2);
+}
+
+template <typename Matcher1, typename Matcher2, typename Matcher3>
+inline internal::EitherOfMatcher<Matcher1,
+           internal::EitherOfMatcher<Matcher2, Matcher3> >
+AnyOf(Matcher1 m1, Matcher2 m2, Matcher3 m3) {
+  return AnyOf(m1, AnyOf(m2, m3));
+}
+
+template <typename Matcher1, typename Matcher2, typename Matcher3,
+          typename Matcher4>
+inline internal::EitherOfMatcher<Matcher1,
+           internal::EitherOfMatcher<Matcher2,
+               internal::EitherOfMatcher<Matcher3, Matcher4> > >
+AnyOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4) {
+  return AnyOf(m1, AnyOf(m2, m3, m4));
+}
+
+template <typename Matcher1, typename Matcher2, typename Matcher3,
+          typename Matcher4, typename Matcher5>
+inline internal::EitherOfMatcher<Matcher1,
+           internal::EitherOfMatcher<Matcher2,
+               internal::EitherOfMatcher<Matcher3,
+                   internal::EitherOfMatcher<Matcher4, Matcher5> > > >
+AnyOf(Matcher1 m1, Matcher2 m2, Matcher3 m3, Matcher4 m4, Matcher5 m5) {
+  return AnyOf(m1, AnyOf(m2, m3, m4, m5));
+}
+
+// Returns a matcher that matches anything that satisfies the given
+// predicate.  The predicate can be any unary function or functor
+// whose return type can be implicitly converted to bool.
+template <typename Predicate>
+inline PolymorphicMatcher<internal::TrulyMatcher<Predicate> >
+Truly(Predicate pred) {
+  return MakePolymorphicMatcher(internal::TrulyMatcher<Predicate>(pred));
+}
+
+// Returns a matcher that matches an equal container.
+// This matcher behaves like Eq(), but in the event of mismatch lists the
+// values that are included in one container but not the other. (Duplicate
+// values and order differences are not explained.)
+template <typename Container>
+inline PolymorphicMatcher<internal::ContainerEqMatcher<
+                            GMOCK_REMOVE_CONST_(Container)> >
+    ContainerEq(const Container& rhs) {
+  // This following line is for working around a bug in MSVC 8.0,
+  // which causes Container to be a const type sometimes.
+  typedef GMOCK_REMOVE_CONST_(Container) RawContainer;
+  return MakePolymorphicMatcher(internal::ContainerEqMatcher<RawContainer>(rhs));
+}
+
+// Matches an STL-style container or a native array that contains at
+// least one element matching the given value or matcher.
+//
+// Examples:
+//   ::std::set<int> page_ids;
+//   page_ids.insert(3);
+//   page_ids.insert(1);
+//   EXPECT_THAT(page_ids, Contains(1));
+//   EXPECT_THAT(page_ids, Contains(Gt(2)));
+//   EXPECT_THAT(page_ids, Not(Contains(4)));
+//
+//   ::std::map<int, size_t> page_lengths;
+//   page_lengths[1] = 100;
+//   EXPECT_THAT(page_lengths,
+//               Contains(::std::pair<const int, size_t>(1, 100)));
+//
+//   const char* user_ids[] = { "joe", "mike", "tom" };
+//   EXPECT_THAT(user_ids, Contains(Eq(::std::string("tom"))));
+template <typename M>
+inline internal::ContainsMatcher<M> Contains(M matcher) {
+  return internal::ContainsMatcher<M>(matcher);
+}
+
+// Key(inner_matcher) matches an std::pair whose 'first' field matches
+// inner_matcher.  For example, Contains(Key(Ge(5))) can be used to match an
+// std::map that contains at least one element whose key is >= 5.
+template <typename M>
+inline internal::KeyMatcher<M> Key(M inner_matcher) {
+  return internal::KeyMatcher<M>(inner_matcher);
+}
+
+// Pair(first_matcher, second_matcher) matches a std::pair whose 'first' field
+// matches first_matcher and whose 'second' field matches second_matcher.  For
+// example, EXPECT_THAT(map_type, ElementsAre(Pair(Ge(5), "foo"))) can be used
+// to match a std::map<int, string> that contains exactly one element whose key
+// is >= 5 and whose value equals "foo".
+template <typename FirstMatcher, typename SecondMatcher>
+inline internal::PairMatcher<FirstMatcher, SecondMatcher>
+Pair(FirstMatcher first_matcher, SecondMatcher second_matcher) {
+  return internal::PairMatcher<FirstMatcher, SecondMatcher>(
+      first_matcher, second_matcher);
+}
+
+// Returns a predicate that is satisfied by anything that matches the
+// given matcher.
+template <typename M>
+inline internal::MatcherAsPredicate<M> Matches(M matcher) {
+  return internal::MatcherAsPredicate<M>(matcher);
+}
+
+// Returns true iff the value matches the matcher.
+template <typename T, typename M>
+inline bool Value(const T& value, M matcher) {
+  return testing::Matches(matcher)(value);
+}
+
+// AllArgs(m) is a synonym of m.  This is useful in
+//
+//   EXPECT_CALL(foo, Bar(_, _)).With(AllArgs(Eq()));
+//
+// which is easier to read than
+//
+//   EXPECT_CALL(foo, Bar(_, _)).With(Eq());
+template <typename InnerMatcher>
+inline InnerMatcher AllArgs(const InnerMatcher& matcher) { return matcher; }
+
+// These macros allow using matchers to check values in Google Test
+// tests.  ASSERT_THAT(value, matcher) and EXPECT_THAT(value, matcher)
+// succeed iff the value matches the matcher.  If the assertion fails,
+// the value and the description of the matcher will be printed.
+#define ASSERT_THAT(value, matcher) ASSERT_PRED_FORMAT1(\
+    ::testing::internal::MakePredicateFormatterFromMatcher(matcher), value)
+#define EXPECT_THAT(value, matcher) EXPECT_PRED_FORMAT1(\
+    ::testing::internal::MakePredicateFormatterFromMatcher(matcher), value)
+
+}  // namespace testing
+
+#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
diff --git a/third_party/gmock/include/gmock/gmock-more-actions.h b/third_party/gmock/include/gmock/gmock-more-actions.h
new file mode 100644
index 0000000..c6fa6bb
--- /dev/null
+++ b/third_party/gmock/include/gmock/gmock-more-actions.h
@@ -0,0 +1,190 @@
+// Copyright 2007, 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements some actions that depend on gmock-generated-actions.h.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_
+
+#include <gmock/gmock-generated-actions.h>
+
+namespace testing {
+namespace internal {
+
+// Implements the Invoke(f) action.  The template argument
+// FunctionImpl is the implementation type of f, which can be either a
+// function pointer or a functor.  Invoke(f) can be used as an
+// Action<F> as long as f's type is compatible with F (i.e. f can be
+// assigned to a tr1::function<F>).
+template <typename FunctionImpl>
+class InvokeAction {
+ public:
+  // The c'tor makes a copy of function_impl (either a function
+  // pointer or a functor).
+  explicit InvokeAction(FunctionImpl function_impl)
+      : function_impl_(function_impl) {}
+
+  template <typename Result, typename ArgumentTuple>
+  Result Perform(const ArgumentTuple& args) {
+    return InvokeHelper<Result, ArgumentTuple>::Invoke(function_impl_, args);
+  }
+ private:
+  FunctionImpl function_impl_;
+};
+
+// Implements the Invoke(object_ptr, &Class::Method) action.
+template <class Class, typename MethodPtr>
+class InvokeMethodAction {
+ public:
+  InvokeMethodAction(Class* obj_ptr, MethodPtr method_ptr)
+      : obj_ptr_(obj_ptr), method_ptr_(method_ptr) {}
+
+  template <typename Result, typename ArgumentTuple>
+  Result Perform(const ArgumentTuple& args) const {
+    return InvokeHelper<Result, ArgumentTuple>::InvokeMethod(
+        obj_ptr_, method_ptr_, args);
+  }
+ private:
+  Class* const obj_ptr_;
+  const MethodPtr method_ptr_;
+};
+
+}  // namespace internal
+
+// Various overloads for Invoke().
+
+// Creates an action that invokes 'function_impl' with the mock
+// function's arguments.
+template <typename FunctionImpl>
+PolymorphicAction<internal::InvokeAction<FunctionImpl> > Invoke(
+    FunctionImpl function_impl) {
+  return MakePolymorphicAction(
+      internal::InvokeAction<FunctionImpl>(function_impl));
+}
+
+// Creates an action that invokes the given method on the given object
+// with the mock function's arguments.
+template <class Class, typename MethodPtr>
+PolymorphicAction<internal::InvokeMethodAction<Class, MethodPtr> > Invoke(
+    Class* obj_ptr, MethodPtr method_ptr) {
+  return MakePolymorphicAction(
+      internal::InvokeMethodAction<Class, MethodPtr>(obj_ptr, method_ptr));
+}
+
+// WithoutArgs(inner_action) can be used in a mock function with a
+// non-empty argument list to perform inner_action, which takes no
+// argument.  In other words, it adapts an action accepting no
+// argument to one that accepts (and ignores) arguments.
+template <typename InnerAction>
+inline internal::WithArgsAction<InnerAction>
+WithoutArgs(const InnerAction& action) {
+  return internal::WithArgsAction<InnerAction>(action);
+}
+
+// WithArg<k>(an_action) creates an action that passes the k-th
+// (0-based) argument of the mock function to an_action and performs
+// it.  It adapts an action accepting one argument to one that accepts
+// multiple arguments.  For convenience, we also provide
+// WithArgs<k>(an_action) (defined below) as a synonym.
+template <int k, typename InnerAction>
+inline internal::WithArgsAction<InnerAction, k>
+WithArg(const InnerAction& action) {
+  return internal::WithArgsAction<InnerAction, k>(action);
+}
+
+// Action ReturnArg<k>() returns the k-th argument of the mock function.
+ACTION_TEMPLATE(ReturnArg,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_0_VALUE_PARAMS()) {
+  return std::tr1::get<k>(args);
+}
+
+// Action SaveArg<k>(pointer) saves the k-th (0-based) argument of the
+// mock function to *pointer.
+ACTION_TEMPLATE(SaveArg,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_1_VALUE_PARAMS(pointer)) {
+  *pointer = ::std::tr1::get<k>(args);
+}
+
+// Action SetArgReferee<k>(value) assigns 'value' to the variable
+// referenced by the k-th (0-based) argument of the mock function.
+ACTION_TEMPLATE(SetArgReferee,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_1_VALUE_PARAMS(value)) {
+  typedef typename ::std::tr1::tuple_element<k, args_type>::type argk_type;
+  // Ensures that argument #k is a reference.  If you get a compiler
+  // error on the next line, you are using SetArgReferee<k>(value) in
+  // a mock function whose k-th (0-based) argument is not a reference.
+  GMOCK_COMPILE_ASSERT_(internal::is_reference<argk_type>::value,
+                        SetArgReferee_must_be_used_with_a_reference_argument);
+  ::std::tr1::get<k>(args) = value;
+}
+
+// Action SetArrayArgument<k>(first, last) copies the elements in
+// source range [first, last) to the array pointed to by the k-th
+// (0-based) argument, which can be either a pointer or an
+// iterator. The action does not take ownership of the elements in the
+// source range.
+ACTION_TEMPLATE(SetArrayArgument,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_2_VALUE_PARAMS(first, last)) {
+  // Microsoft compiler deprecates ::std::copy, so we want to suppress warning
+  // 4996 (Function call with parameters that may be unsafe) there.
+#ifdef _MSC_VER
+#pragma warning(push)          // Saves the current warning state.
+#pragma warning(disable:4996)  // Temporarily disables warning 4996.
+#endif
+  ::std::copy(first, last, ::std::tr1::get<k>(args));
+#ifdef _MSC_VER
+#pragma warning(pop)           // Restores the warning state.
+#endif
+}
+
+// Action DeleteArg<k>() deletes the k-th (0-based) argument of the mock
+// function.
+ACTION_TEMPLATE(DeleteArg,
+                HAS_1_TEMPLATE_PARAMS(int, k),
+                AND_0_VALUE_PARAMS()) {
+  delete ::std::tr1::get<k>(args);
+}
+
+// Action Throw(exception) can be used in a mock function of any type
+// to throw the given exception.  Any copyable value can be thrown.
+#if GTEST_HAS_EXCEPTIONS
+ACTION_P(Throw, exception) { throw exception; }
+#endif  // GTEST_HAS_EXCEPTIONS
+
+}  // namespace testing
+
+#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_
diff --git a/third_party/gmock/include/gmock/gmock-printers.h b/third_party/gmock/include/gmock/gmock-printers.h
new file mode 100644
index 0000000..9f024d0
--- /dev/null
+++ b/third_party/gmock/include/gmock/gmock-printers.h
@@ -0,0 +1,747 @@
+// Copyright 2007, 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements a universal value printer that can print a
+// value of any type T:
+//
+//   void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr);
+//
+// A user can teach this function how to print a class type T by
+// defining either operator<<() or PrintTo() in the namespace that
+// defines T.  More specifically, the FIRST defined function in the
+// following list will be used (assuming T is defined in namespace
+// foo):
+//
+//   1. foo::PrintTo(const T&, ostream*)
+//   2. operator<<(ostream&, const T&) defined in either foo or the
+//      global namespace.
+//
+// If none of the above is defined, it will print the debug string of
+// the value if it is a protocol buffer, or print the raw bytes in the
+// value otherwise.
+//
+// To aid debugging: when T is a reference type, the address of the
+// value is also printed; when T is a (const) char pointer, both the
+// pointer value and the NUL-terminated string it points to are
+// printed.
+//
+// We also provide some convenient wrappers:
+//
+//   // Prints a value as the given type to a string.
+//   string ::testing::internal::UniversalPrinter<T>::PrintToString(value);
+//
+//   // Prints a value tersely: for a reference type, the referenced
+//   // value (but not the address) is printed; for a (const) char
+//   // pointer, the NUL-terminated string (but not the pointer) is
+//   // printed.
+//   void ::testing::internal::UniversalTersePrint(const T& value, ostream*);
+//
+//   // Prints value using the type inferred by the compiler.  The difference
+//   // from UniversalTersePrint() is that this function prints both the
+//   // pointer and the NUL-terminated string for a (const) char pointer.
+//   void ::testing::internal::UniversalPrint(const T& value, ostream*);
+//
+//   // Prints the fields of a tuple tersely to a string vector, one
+//   // element for each field.
+//   std::vector<string> UniversalTersePrintTupleFieldsToStrings(
+//       const Tuple& value);
+//
+// Known limitation:
+//
+// The print primitives print the elements of an STL-style container
+// using the compiler-inferred type of *iter where iter is a
+// const_iterator of the container.  When const_iterator is an input
+// iterator but not a forward iterator, this inferred type may not
+// match value_type, and the print output may be incorrect.  In
+// practice, this is rarely a problem as for most containers
+// const_iterator is a forward iterator.  We'll fix this if there's an
+// actual need for it.  Note that this fix cannot rely on value_type
+// being defined as many user-defined container types don't have
+// value_type.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_PRINTERS_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_PRINTERS_H_
+
+#include <ostream>  // NOLINT
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <gmock/internal/gmock-internal-utils.h>
+#include <gmock/internal/gmock-port.h>
+#include <gtest/gtest.h>
+
+namespace testing {
+
+// Definitions in the 'internal' and 'internal2' name spaces are
+// subject to change without notice.  DO NOT USE THEM IN USER CODE!
+namespace internal2 {
+
+// Prints the given number of bytes in the given object to the given
+// ostream.
+void PrintBytesInObjectTo(const unsigned char* obj_bytes,
+                          size_t count,
+                          ::std::ostream* os);
+
+// TypeWithoutFormatter<T, kIsProto>::PrintValue(value, os) is called
+// by the universal printer to print a value of type T when neither
+// operator<< nor PrintTo() is defined for type T.  When T is
+// ProtocolMessage, proto2::Message, or a subclass of those, kIsProto
+// will be true and the short debug string of the protocol message
+// value will be printed; otherwise kIsProto will be false and the
+// bytes in the value will be printed.
+template <typename T, bool kIsProto>
+class TypeWithoutFormatter {
+ public:
+  static void PrintValue(const T& value, ::std::ostream* os) {
+    PrintBytesInObjectTo(reinterpret_cast<const unsigned char*>(&value),
+                         sizeof(value), os);
+  }
+};
+
+// We print a protobuf using its ShortDebugString() when the string
+// doesn't exceed this many characters; otherwise we print it using
+// DebugString() for better readability.
+const size_t kProtobufOneLinerMaxLength = 50;
+
+template <typename T>
+class TypeWithoutFormatter<T, true> {
+ public:
+  static void PrintValue(const T& value, ::std::ostream* os) {
+    const ::testing::internal::string short_str = value.ShortDebugString();
+    const ::testing::internal::string pretty_str =
+        short_str.length() <= kProtobufOneLinerMaxLength ?
+        short_str : ("\n" + value.DebugString());
+    ::std::operator<<(*os, "<" + pretty_str + ">");
+  }
+};
+
+// Prints the given value to the given ostream.  If the value is a
+// protocol message, its short debug string is printed; otherwise the
+// bytes in the value are printed.  This is what
+// UniversalPrinter<T>::Print() does when it knows nothing about type
+// T and T has no << operator.
+//
+// A user can override this behavior for a class type Foo by defining
+// a << operator in the namespace where Foo is defined.
+//
+// We put this operator in namespace 'internal2' instead of 'internal'
+// to simplify the implementation, as much code in 'internal' needs to
+// use << in STL, which would conflict with our own << were it defined
+// in 'internal'.
+//
+// Note that this operator<< takes a generic std::basic_ostream<Char,
+// CharTraits> type instead of the more restricted std::ostream.  If
+// we define it to take an std::ostream instead, we'll get an
+// "ambiguous overloads" compiler error when trying to print a type
+// Foo that supports streaming to std::basic_ostream<Char,
+// CharTraits>, as the compiler cannot tell whether
+// operator<<(std::ostream&, const T&) or
+// operator<<(std::basic_stream<Char, CharTraits>, const Foo&) is more
+// specific.
+template <typename Char, typename CharTraits, typename T>
+::std::basic_ostream<Char, CharTraits>& operator<<(
+    ::std::basic_ostream<Char, CharTraits>& os, const T& x) {
+  TypeWithoutFormatter<T, ::testing::internal::IsAProtocolMessage<T>::value>::
+      PrintValue(x, &os);
+  return os;
+}
+
+}  // namespace internal2
+}  // namespace testing
+
+// This namespace MUST NOT BE NESTED IN ::testing, or the name look-up
+// magic needed for implementing UniversalPrinter won't work.
+namespace testing_internal {
+
+// Used to print a value that is not an STL-style container when the
+// user doesn't define PrintTo() for it.
+template <typename T>
+void DefaultPrintNonContainerTo(const T& value, ::std::ostream* os) {
+  // With the following statement, during unqualified name lookup,
+  // testing::internal2::operator<< appears as if it was declared in
+  // the nearest enclosing namespace that contains both
+  // ::testing_internal and ::testing::internal2, i.e. the global
+  // namespace.  For more details, refer to the C++ Standard section
+  // 7.3.4-1 [namespace.udir].  This allows us to fall back onto
+  // testing::internal2::operator<< in case T doesn't come with a <<
+  // operator.
+  //
+  // We cannot write 'using ::testing::internal2::operator<<;', which
+  // gcc 3.3 fails to compile due to a compiler bug.
+  using namespace ::testing::internal2;  // NOLINT
+
+  // Assuming T is defined in namespace foo, in the next statement,
+  // the compiler will consider all of:
+  //
+  //   1. foo::operator<< (thanks to Koenig look-up),
+  //   2. ::operator<< (as the current namespace is enclosed in ::),
+  //   3. testing::internal2::operator<< (thanks to the using statement above).
+  //
+  // The operator<< whose type matches T best will be picked.
+  //
+  // We deliberately allow #2 to be a candidate, as sometimes it's
+  // impossible to define #1 (e.g. when foo is ::std, defining
+  // anything in it is undefined behavior unless you are a compiler
+  // vendor.).
+  *os << value;
+}
+
+}  // namespace testing_internal
+
+namespace testing {
+namespace internal {
+
+// UniversalPrinter<T>::Print(value, ostream_ptr) prints the given
+// value to the given ostream.  The caller must ensure that
+// 'ostream_ptr' is not NULL, or the behavior is undefined.
+//
+// We define UniversalPrinter as a class template (as opposed to a
+// function template), as we need to partially specialize it for
+// reference types, which cannot be done with function templates.
+template <typename T>
+class UniversalPrinter;
+
+template <typename T>
+void UniversalPrint(const T& value, ::std::ostream* os);
+
+// Used to print an STL-style container when the user doesn't define
+// a PrintTo() for it.
+template <typename C>
+void DefaultPrintTo(IsContainer /* dummy */,
+                    false_type /* is not a pointer */,
+                    const C& container, ::std::ostream* os) {
+  const size_t kMaxCount = 32;  // The maximum number of elements to print.
+  *os << '{';
+  size_t count = 0;
+  for (typename C::const_iterator it = container.begin();
+       it != container.end(); ++it, ++count) {
+    if (count > 0) {
+      *os << ',';
+      if (count == kMaxCount) {  // Enough has been printed.
+        *os << " ...";
+        break;
+      }
+    }
+    *os << ' ';
+    // We cannot call PrintTo(*it, os) here as PrintTo() doesn't
+    // handle *it being a native array.
+    internal::UniversalPrint(*it, os);
+  }
+
+  if (count > 0) {
+    *os << ' ';
+  }
+  *os << '}';
+}
+
+// Used to print a pointer that is neither a char pointer nor a member
+// pointer, when the user doesn't define PrintTo() for it.  (A member
+// variable pointer or member function pointer doesn't really point to
+// a location in the address space.  Their representation is
+// implementation-defined.  Therefore they will be printed as raw
+// bytes.)
+template <typename T>
+void DefaultPrintTo(IsNotContainer /* dummy */,
+                    true_type /* is a pointer */,
+                    T* p, ::std::ostream* os) {
+  if (p == NULL) {
+    *os << "NULL";
+  } else {
+    // We want to print p as a const void*.  However, we cannot cast
+    // it to const void* directly, even using reinterpret_cast, as
+    // earlier versions of gcc (e.g. 3.4.5) cannot compile the cast
+    // when p is a function pointer.  Casting to UInt64 first solves
+    // the problem.
+    *os << reinterpret_cast<const void*>(reinterpret_cast<internal::UInt64>(p));
+  }
+}
+
+// Used to print a non-container, non-pointer value when the user
+// doesn't define PrintTo() for it.
+template <typename T>
+void DefaultPrintTo(IsNotContainer /* dummy */,
+                    false_type /* is not a pointer */,
+                    const T& value, ::std::ostream* os) {
+  ::testing_internal::DefaultPrintNonContainerTo(value, os);
+}
+
+// Prints the given value using the << operator if it has one;
+// otherwise prints the bytes in it.  This is what
+// UniversalPrinter<T>::Print() does when PrintTo() is not specialized
+// or overloaded for type T.
+//
+// A user can override this behavior for a class type Foo by defining
+// an overload of PrintTo() in the namespace where Foo is defined.  We
+// give the user this option as sometimes defining a << operator for
+// Foo is not desirable (e.g. the coding style may prevent doing it,
+// or there is already a << operator but it doesn't do what the user
+// wants).
+template <typename T>
+void PrintTo(const T& value, ::std::ostream* os) {
+  // DefaultPrintTo() is overloaded.  The type of its first two
+  // arguments determine which version will be picked.  If T is an
+  // STL-style container, the version for container will be called; if
+  // T is a pointer, the pointer version will be called; otherwise the
+  // generic version will be called.
+  //
+  // Note that we check for container types here, prior to we check
+  // for protocol message types in our operator<<.  The rationale is:
+  //
+  // For protocol messages, we want to give people a chance to
+  // override Google Mock's format by defining a PrintTo() or
+  // operator<<.  For STL containers, other formats can be
+  // incompatible with Google Mock's format for the container
+  // elements; therefore we check for container types here to ensure
+  // that our format is used.
+  //
+  // The second argument of DefaultPrintTo() is needed to bypass a bug
+  // in Symbian's C++ compiler that prevents it from picking the right
+  // overload between:
+  //
+  //   PrintTo(const T& x, ...);
+  //   PrintTo(T* x, ...);
+  DefaultPrintTo(IsContainerTest<T>(0), is_pointer<T>(), value, os);
+}
+
+// The following list of PrintTo() overloads tells
+// UniversalPrinter<T>::Print() how to print standard types (built-in
+// types, strings, plain arrays, and pointers).
+
+// Overloads for various char types.
+void PrintCharTo(char c, int char_code, ::std::ostream* os);
+inline void PrintTo(unsigned char c, ::std::ostream* os) {
+  PrintCharTo(c, c, os);
+}
+inline void PrintTo(signed char c, ::std::ostream* os) {
+  PrintCharTo(c, c, os);
+}
+inline void PrintTo(char c, ::std::ostream* os) {
+  // When printing a plain char, we always treat it as unsigned.  This
+  // way, the output won't be affected by whether the compiler thinks
+  // char is signed or not.
+  PrintTo(static_cast<unsigned char>(c), os);
+}
+
+// Overloads for other simple built-in types.
+inline void PrintTo(bool x, ::std::ostream* os) {
+  *os << (x ? "true" : "false");
+}
+
+// Overload for wchar_t type.
+// Prints a wchar_t as a symbol if it is printable or as its internal
+// code otherwise and also as its decimal code (except for L'\0').
+// The L'\0' char is printed as "L'\\0'". The decimal code is printed
+// as signed integer when wchar_t is implemented by the compiler
+// as a signed type and is printed as an unsigned integer when wchar_t
+// is implemented as an unsigned type.
+void PrintTo(wchar_t wc, ::std::ostream* os);
+
+// Overloads for C strings.
+void PrintTo(const char* s, ::std::ostream* os);
+inline void PrintTo(char* s, ::std::ostream* os) {
+  PrintTo(implicit_cast<const char*>(s), os);
+}
+
+// MSVC can be configured to define wchar_t as a typedef of unsigned
+// short.  It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native
+// type.  When wchar_t is a typedef, defining an overload for const
+// wchar_t* would cause unsigned short* be printed as a wide string,
+// possibly causing invalid memory accesses.
+#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
+// Overloads for wide C strings
+void PrintTo(const wchar_t* s, ::std::ostream* os);
+inline void PrintTo(wchar_t* s, ::std::ostream* os) {
+  PrintTo(implicit_cast<const wchar_t*>(s), os);
+}
+#endif
+
+// Overload for C arrays.  Multi-dimensional arrays are printed
+// properly.
+
+// Prints the given number of elements in an array, without printing
+// the curly braces.
+template <typename T>
+void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) {
+  UniversalPrinter<T>::Print(a[0], os);
+  for (size_t i = 1; i != count; i++) {
+    *os << ", ";
+    UniversalPrinter<T>::Print(a[i], os);
+  }
+}
+
+// Overloads for ::string and ::std::string.
+#if GTEST_HAS_GLOBAL_STRING
+void PrintStringTo(const ::string&s, ::std::ostream* os);
+inline void PrintTo(const ::string& s, ::std::ostream* os) {
+  PrintStringTo(s, os);
+}
+#endif  // GTEST_HAS_GLOBAL_STRING
+
+#if GTEST_HAS_STD_STRING
+void PrintStringTo(const ::std::string&s, ::std::ostream* os);
+inline void PrintTo(const ::std::string& s, ::std::ostream* os) {
+  PrintStringTo(s, os);
+}
+#endif  // GTEST_HAS_STD_STRING
+
+// Overloads for ::wstring and ::std::wstring.
+#if GTEST_HAS_GLOBAL_WSTRING
+void PrintWideStringTo(const ::wstring&s, ::std::ostream* os);
+inline void PrintTo(const ::wstring& s, ::std::ostream* os) {
+  PrintWideStringTo(s, os);
+}
+#endif  // GTEST_HAS_GLOBAL_WSTRING
+
+#if GTEST_HAS_STD_WSTRING
+void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os);
+inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) {
+  PrintWideStringTo(s, os);
+}
+#endif  // GTEST_HAS_STD_WSTRING
+
+// Overload for ::std::tr1::tuple.  Needed for printing function
+// arguments, which are packed as tuples.
+
+// Helper function for printing a tuple.  T must be instantiated with
+// a tuple type.
+template <typename T>
+void PrintTupleTo(const T& t, ::std::ostream* os);
+
+// Overloaded PrintTo() for tuples of various arities.  We support
+// tuples of up-to 10 fields.  The following implementation works
+// regardless of whether tr1::tuple is implemented using the
+// non-standard variadic template feature or not.
+
+inline void PrintTo(const ::std::tr1::tuple<>& t, ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1>
+void PrintTo(const ::std::tr1::tuple<T1>& t, ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2>
+void PrintTo(const ::std::tr1::tuple<T1, T2>& t, ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3>& t, ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4>& t, ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5>& t,
+             ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+          typename T6>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6>& t,
+             ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+          typename T6, typename T7>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7>& t,
+             ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+          typename T6, typename T7, typename T8>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8>& t,
+             ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+          typename T6, typename T7, typename T8, typename T9>
+void PrintTo(const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9>& t,
+             ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+template <typename T1, typename T2, typename T3, typename T4, typename T5,
+          typename T6, typename T7, typename T8, typename T9, typename T10>
+void PrintTo(
+    const ::std::tr1::tuple<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>& t,
+    ::std::ostream* os) {
+  PrintTupleTo(t, os);
+}
+
+// Overload for std::pair.
+template <typename T1, typename T2>
+void PrintTo(const ::std::pair<T1, T2>& value, ::std::ostream* os) {
+  *os << '(';
+  UniversalPrinter<T1>::Print(value.first, os);
+  *os << ", ";
+  UniversalPrinter<T2>::Print(value.second, os);
+  *os << ')';
+}
+
+// Implements printing a non-reference type T by letting the compiler
+// pick the right overload of PrintTo() for T.
+template <typename T>
+class UniversalPrinter {
+ public:
+  // MSVC warns about adding const to a function type, so we want to
+  // disable the warning.
+#ifdef _MSC_VER
+#pragma warning(push)          // Saves the current warning state.
+#pragma warning(disable:4180)  // Temporarily disables warning 4180.
+#endif  // _MSC_VER
+
+  // Note: we deliberately don't call this PrintTo(), as that name
+  // conflicts with ::testing::internal::PrintTo in the body of the
+  // function.
+  static void Print(const T& value, ::std::ostream* os) {
+    // By default, ::testing::internal::PrintTo() is used for printing
+    // the value.
+    //
+    // Thanks to Koenig look-up, if T is a class and has its own
+    // PrintTo() function defined in its namespace, that function will
+    // be visible here.  Since it is more specific than the generic ones
+    // in ::testing::internal, it will be picked by the compiler in the
+    // following statement - exactly what we want.
+    PrintTo(value, os);
+  }
+
+  // A convenient wrapper for Print() that returns the print-out as a
+  // string.
+  static string PrintToString(const T& value) {
+    ::std::stringstream ss;
+    Print(value, &ss);
+    return ss.str();
+  }
+
+#ifdef _MSC_VER
+#pragma warning(pop)           // Restores the warning state.
+#endif  // _MSC_VER
+};
+
+// UniversalPrintArray(begin, len, os) prints an array of 'len'
+// elements, starting at address 'begin'.
+template <typename T>
+void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) {
+  if (len == 0) {
+    *os << "{}";
+  } else {
+    *os << "{ ";
+    const size_t kThreshold = 18;
+    const size_t kChunkSize = 8;
+    // If the array has more than kThreshold elements, we'll have to
+    // omit some details by printing only the first and the last
+    // kChunkSize elements.
+    // TODO(wan@google.com): let the user control the threshold using a flag.
+    if (len <= kThreshold) {
+      PrintRawArrayTo(begin, len, os);
+    } else {
+      PrintRawArrayTo(begin, kChunkSize, os);
+      *os << ", ..., ";
+      PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os);
+    }
+    *os << " }";
+  }
+}
+// This overload prints a (const) char array compactly.
+void UniversalPrintArray(const char* begin, size_t len, ::std::ostream* os);
+
+// Prints an array of 'len' elements, starting at address 'begin', to a string.
+template <typename T>
+string UniversalPrintArrayToString(const T* begin, size_t len) {
+  ::std::stringstream ss;
+  UniversalPrintArray(begin, len, &ss);
+  return ss.str();
+}
+
+// Implements printing an array type T[N].
+template <typename T, size_t N>
+class UniversalPrinter<T[N]> {
+ public:
+  // Prints the given array, omitting some elements when there are too
+  // many.
+  static void Print(const T (&a)[N], ::std::ostream* os) {
+    UniversalPrintArray(a, N, os);
+  }
+
+  // A convenient wrapper for Print() that returns the print-out as a
+  // string.
+  static string PrintToString(const T (&a)[N]) {
+    return UniversalPrintArrayToString(a, N);
+  }
+};
+
+// Implements printing a reference type T&.
+template <typename T>
+class UniversalPrinter<T&> {
+ public:
+  // MSVC warns about adding const to a function type, so we want to
+  // disable the warning.
+#ifdef _MSC_VER
+#pragma warning(push)          // Saves the current warning state.
+#pragma warning(disable:4180)  // Temporarily disables warning 4180.
+#endif  // _MSC_VER
+
+  static void Print(const T& value, ::std::ostream* os) {
+    // Prints the address of the value.  We use reinterpret_cast here
+    // as static_cast doesn't compile when T is a function type.
+    *os << "@" << reinterpret_cast<const void*>(&value) << " ";
+
+    // Then prints the value itself.
+    UniversalPrinter<T>::Print(value, os);
+  }
+
+  // A convenient wrapper for Print() that returns the print-out as a
+  // string.
+  static string PrintToString(const T& value) {
+    ::std::stringstream ss;
+    Print(value, &ss);
+    return ss.str();
+  }
+
+#ifdef _MSC_VER
+#pragma warning(pop)           // Restores the warning state.
+#endif  // _MSC_VER
+};
+
+// Prints a value tersely: for a reference type, the referenced value
+// (but not the address) is printed; for a (const) char pointer, the
+// NUL-terminated string (but not the pointer) is printed.
+template <typename T>
+void UniversalTersePrint(const T& value, ::std::ostream* os) {
+  UniversalPrinter<T>::Print(value, os);
+}
+inline void UniversalTersePrint(const char* str, ::std::ostream* os) {
+  if (str == NULL) {
+    *os << "NULL";
+  } else {
+    UniversalPrinter<string>::Print(string(str), os);
+  }
+}
+inline void UniversalTersePrint(char* str, ::std::ostream* os) {
+  UniversalTersePrint(static_cast<const char*>(str), os);
+}
+
+// Prints a value using the type inferred by the compiler.  The
+// difference between this and UniversalTersePrint() is that for a
+// (const) char pointer, this prints both the pointer and the
+// NUL-terminated string.
+template <typename T>
+void UniversalPrint(const T& value, ::std::ostream* os) {
+  UniversalPrinter<T>::Print(value, os);
+}
+
+typedef ::std::vector<string> Strings;
+
+// This helper template allows PrintTo() for tuples and
+// UniversalTersePrintTupleFieldsToStrings() to be defined by
+// induction on the number of tuple fields.  The idea is that
+// TuplePrefixPrinter<N>::PrintPrefixTo(t, os) prints the first N
+// fields in tuple t, and can be defined in terms of
+// TuplePrefixPrinter<N - 1>.
+
+// The inductive case.
+template <size_t N>
+struct TuplePrefixPrinter {
+  // Prints the first N fields of a tuple.
+  template <typename Tuple>
+  static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
+    TuplePrefixPrinter<N - 1>::PrintPrefixTo(t, os);
+    *os << ", ";
+    UniversalPrinter<typename ::std::tr1::tuple_element<N - 1, Tuple>::type>
+        ::Print(::std::tr1::get<N - 1>(t), os);
+  }
+
+  // Tersely prints the first N fields of a tuple to a string vector,
+  // one element for each field.
+  template <typename Tuple>
+  static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) {
+    TuplePrefixPrinter<N - 1>::TersePrintPrefixToStrings(t, strings);
+    ::std::stringstream ss;
+    UniversalTersePrint(::std::tr1::get<N - 1>(t), &ss);
+    strings->push_back(ss.str());
+  }
+};
+
+// Base cases.
+template <>
+struct TuplePrefixPrinter<0> {
+  template <typename Tuple>
+  static void PrintPrefixTo(const Tuple&, ::std::ostream*) {}
+
+  template <typename Tuple>
+  static void TersePrintPrefixToStrings(const Tuple&, Strings*) {}
+};
+template <>
+template <typename Tuple>
+void TuplePrefixPrinter<1>::PrintPrefixTo(const Tuple& t, ::std::ostream* os) {
+  UniversalPrinter<typename ::std::tr1::tuple_element<0, Tuple>::type>::
+      Print(::std::tr1::get<0>(t), os);
+}
+
+// Helper function for printing a tuple.  T must be instantiated with
+// a tuple type.
+template <typename T>
+void PrintTupleTo(const T& t, ::std::ostream* os) {
+  *os << "(";
+  TuplePrefixPrinter< ::std::tr1::tuple_size<T>::value>::
+      PrintPrefixTo(t, os);
+  *os << ")";
+}
+
+// Prints the fields of a tuple tersely to a string vector, one
+// element for each field.  See the comment before
+// UniversalTersePrint() for how we define "tersely".
+template <typename Tuple>
+Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) {
+  Strings result;
+  TuplePrefixPrinter< ::std::tr1::tuple_size<Tuple>::value>::
+      TersePrintPrefixToStrings(value, &result);
+  return result;
+}
+
+}  // namespace internal
+}  // namespace testing
+
+#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_PRINTERS_H_
diff --git a/third_party/gmock/include/gmock/gmock-spec-builders.h b/third_party/gmock/include/gmock/gmock-spec-builders.h
new file mode 100644
index 0000000..9cb549a
--- /dev/null
+++ b/third_party/gmock/include/gmock/gmock-spec-builders.h
@@ -0,0 +1,1840 @@
+// Copyright 2007, 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements the ON_CALL() and EXPECT_CALL() macros.
+//
+// A user can use the ON_CALL() macro to specify the default action of
+// a mock method.  The syntax is:
+//
+//   ON_CALL(mock_object, Method(argument-matchers))
+//       .With(multi-argument-matcher)
+//       .WillByDefault(action);
+//
+//  where the .With() clause is optional.
+//
+// A user can use the EXPECT_CALL() macro to specify an expectation on
+// a mock method.  The syntax is:
+//
+//   EXPECT_CALL(mock_object, Method(argument-matchers))
+//       .With(multi-argument-matchers)
+//       .Times(cardinality)
+//       .InSequence(sequences)
+//       .After(expectations)
+//       .WillOnce(action)
+//       .WillRepeatedly(action)
+//       .RetiresOnSaturation();
+//
+// where all clauses are optional, and .InSequence()/.After()/
+// .WillOnce() can appear any number of times.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
+
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <gmock/gmock-actions.h>
+#include <gmock/gmock-cardinalities.h>
+#include <gmock/gmock-matchers.h>
+#include <gmock/gmock-printers.h>
+#include <gmock/internal/gmock-internal-utils.h>
+#include <gmock/internal/gmock-port.h>
+#include <gtest/gtest.h>
+
+namespace testing {
+
+// An abstract handle of an expectation.
+class Expectation;
+
+// A set of expectation handles.
+class ExpectationSet;
+
+// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION
+// and MUST NOT BE USED IN USER CODE!!!
+namespace internal {
+
+// Implements a mock function.
+template <typename F> class FunctionMocker;
+
+// Base class for expectations.
+class ExpectationBase;
+
+// Implements an expectation.
+template <typename F> class TypedExpectation;
+
+// Helper class for testing the Expectation class template.
+class ExpectationTester;
+
+// Base class for function mockers.
+template <typename F> class FunctionMockerBase;
+
+// Protects the mock object registry (in class Mock), all function
+// mockers, and all expectations.
+//
+// The reason we don't use more fine-grained protection is: when a
+// mock function Foo() is called, it needs to consult its expectations
+// to see which one should be picked.  If another thread is allowed to
+// call a mock function (either Foo() or a different one) at the same
+// time, it could affect the "retired" attributes of Foo()'s
+// expectations when InSequence() is used, and thus affect which
+// expectation gets picked.  Therefore, we sequence all mock function
+// calls to ensure the integrity of the mock objects' states.
+extern Mutex g_gmock_mutex;
+
+// Abstract base class of FunctionMockerBase.  This is the
+// type-agnostic part of the function mocker interface.  Its pure
+// virtual methods are implemented by FunctionMockerBase.
+class UntypedFunctionMockerBase {
+ public:
+  virtual ~UntypedFunctionMockerBase() {}
+
+  // Verifies that all expectations on this mock function have been
+  // satisfied.  Reports one or more Google Test non-fatal failures
+  // and returns false if not.
+  // L >= g_gmock_mutex
+  virtual bool VerifyAndClearExpectationsLocked() = 0;
+
+  // Clears the ON_CALL()s set on this mock function.
+  // L >= g_gmock_mutex
+  virtual void ClearDefaultActionsLocked() = 0;
+};  // class UntypedFunctionMockerBase
+
+// This template class implements a default action spec (i.e. an
+// ON_CALL() statement).
+template <typename F>
+class DefaultActionSpec {
+ public:
+  typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+  typedef typename Function<F>::ArgumentMatcherTuple ArgumentMatcherTuple;
+
+  // Constructs a DefaultActionSpec object from the information inside
+  // the parenthesis of an ON_CALL() statement.
+  DefaultActionSpec(const char* file, int line,
+                    const ArgumentMatcherTuple& matchers)
+      : file_(file),
+        line_(line),
+        matchers_(matchers),
+        // By default, extra_matcher_ should match anything.  However,
+        // we cannot initialize it with _ as that triggers a compiler
+        // bug in Symbian's C++ compiler (cannot decide between two
+        // overloaded constructors of Matcher<const ArgumentTuple&>).
+        extra_matcher_(A<const ArgumentTuple&>()),
+        last_clause_(kNone) {
+  }
+
+  // Where in the source file was the default action spec defined?
+  const char* file() const { return file_; }
+  int line() const { return line_; }
+
+  // Implements the .With() clause.
+  DefaultActionSpec& With(const Matcher<const ArgumentTuple&>& m) {
+    // Makes sure this is called at most once.
+    ExpectSpecProperty(last_clause_ < kWith,
+                       ".With() cannot appear "
+                       "more than once in an ON_CALL().");
+    last_clause_ = kWith;
+
+    extra_matcher_ = m;
+    return *this;
+  }
+
+  // Implements the .WillByDefault() clause.
+  DefaultActionSpec& WillByDefault(const Action<F>& action) {
+    ExpectSpecProperty(last_clause_ < kWillByDefault,
+                       ".WillByDefault() must appear "
+                       "exactly once in an ON_CALL().");
+    last_clause_ = kWillByDefault;
+
+    ExpectSpecProperty(!action.IsDoDefault(),
+                       "DoDefault() cannot be used in ON_CALL().");
+    action_ = action;
+    return *this;
+  }
+
+  // Returns true iff the given arguments match the matchers.
+  bool Matches(const ArgumentTuple& args) const {
+    return TupleMatches(matchers_, args) && extra_matcher_.Matches(args);
+  }
+
+  // Returns the action specified by the user.
+  const Action<F>& GetAction() const {
+    AssertSpecProperty(last_clause_ == kWillByDefault,
+                       ".WillByDefault() must appear exactly "
+                       "once in an ON_CALL().");
+    return action_;
+  }
+ private:
+  // Gives each clause in the ON_CALL() statement a name.
+  enum Clause {
+    // Do not change the order of the enum members!  The run-time
+    // syntax checking relies on it.
+    kNone,
+    kWith,
+    kWillByDefault,
+  };
+
+  // Asserts that the ON_CALL() statement has a certain property.
+  void AssertSpecProperty(bool property, const string& failure_message) const {
+    Assert(property, file_, line_, failure_message);
+  }
+
+  // Expects that the ON_CALL() statement has a certain property.
+  void ExpectSpecProperty(bool property, const string& failure_message) const {
+    Expect(property, file_, line_, failure_message);
+  }
+
+  // The information in statement
+  //
+  //   ON_CALL(mock_object, Method(matchers))
+  //       .With(multi-argument-matcher)
+  //       .WillByDefault(action);
+  //
+  // is recorded in the data members like this:
+  //
+  //   source file that contains the statement => file_
+  //   line number of the statement            => line_
+  //   matchers                                => matchers_
+  //   multi-argument-matcher                  => extra_matcher_
+  //   action                                  => action_
+  const char* file_;
+  int line_;
+  ArgumentMatcherTuple matchers_;
+  Matcher<const ArgumentTuple&> extra_matcher_;
+  Action<F> action_;
+
+  // The last clause in the ON_CALL() statement as seen so far.
+  // Initially kNone and changes as the statement is parsed.
+  Clause last_clause_;
+};  // class DefaultActionSpec
+
+// Possible reactions on uninteresting calls.  TODO(wan@google.com):
+// rename the enum values to the kFoo style.
+enum CallReaction {
+  ALLOW,
+  WARN,
+  FAIL,
+};
+
+}  // namespace internal
+
+// Utilities for manipulating mock objects.
+class Mock {
+ public:
+  // The following public methods can be called concurrently.
+
+  // Tells Google Mock to ignore mock_obj when checking for leaked
+  // mock objects.
+  static void AllowLeak(const void* mock_obj);
+
+  // Verifies and clears all expectations on the given mock object.
+  // If the expectations aren't satisfied, generates one or more
+  // Google Test non-fatal failures and returns false.
+  static bool VerifyAndClearExpectations(void* mock_obj);
+
+  // Verifies all expectations on the given mock object and clears its
+  // default actions and expectations.  Returns true iff the
+  // verification was successful.
+  static bool VerifyAndClear(void* mock_obj);
+ private:
+  // Needed for a function mocker to register itself (so that we know
+  // how to clear a mock object).
+  template <typename F>
+  friend class internal::FunctionMockerBase;
+
+  template <typename M>
+  friend class NiceMock;
+
+  template <typename M>
+  friend class StrictMock;
+
+  // Tells Google Mock to allow uninteresting calls on the given mock
+  // object.
+  // L < g_gmock_mutex
+  static void AllowUninterestingCalls(const void* mock_obj);
+
+  // Tells Google Mock to warn the user about uninteresting calls on
+  // the given mock object.
+  // L < g_gmock_mutex
+  static void WarnUninterestingCalls(const void* mock_obj);
+
+  // Tells Google Mock to fail uninteresting calls on the given mock
+  // object.
+  // L < g_gmock_mutex
+  static void FailUninterestingCalls(const void* mock_obj);
+
+  // Tells Google Mock the given mock object is being destroyed and
+  // its entry in the call-reaction table should be removed.
+  // L < g_gmock_mutex
+  static void UnregisterCallReaction(const void* mock_obj);
+
+  // Returns the reaction Google Mock will have on uninteresting calls
+  // made on the given mock object.
+  // L < g_gmock_mutex
+  static internal::CallReaction GetReactionOnUninterestingCalls(
+      const void* mock_obj);
+
+  // Verifies that all expectations on the given mock object have been
+  // satisfied.  Reports one or more Google Test non-fatal failures
+  // and returns false if not.
+  // L >= g_gmock_mutex
+  static bool VerifyAndClearExpectationsLocked(void* mock_obj);
+
+  // Clears all ON_CALL()s set on the given mock object.
+  // L >= g_gmock_mutex
+  static void ClearDefaultActionsLocked(void* mock_obj);
+
+  // Registers a mock object and a mock method it owns.
+  // L < g_gmock_mutex
+  static void Register(const void* mock_obj,
+                       internal::UntypedFunctionMockerBase* mocker);
+
+  // Tells Google Mock where in the source code mock_obj is used in an
+  // ON_CALL or EXPECT_CALL.  In case mock_obj is leaked, this
+  // information helps the user identify which object it is.
+  // L < g_gmock_mutex
+  static void RegisterUseByOnCallOrExpectCall(
+      const void* mock_obj, const char* file, int line);
+
+  // Unregisters a mock method; removes the owning mock object from
+  // the registry when the last mock method associated with it has
+  // been unregistered.  This is called only in the destructor of
+  // FunctionMockerBase.
+  // L >= g_gmock_mutex
+  static void UnregisterLocked(internal::UntypedFunctionMockerBase* mocker);
+};  // class Mock
+
+// An abstract handle of an expectation.  Useful in the .After()
+// clause of EXPECT_CALL() for setting the (partial) order of
+// expectations.  The syntax:
+//
+//   Expectation e1 = EXPECT_CALL(...)...;
+//   EXPECT_CALL(...).After(e1)...;
+//
+// sets two expectations where the latter can only be matched after
+// the former has been satisfied.
+//
+// Notes:
+//   - This class is copyable and has value semantics.
+//   - Constness is shallow: a const Expectation object itself cannot
+//     be modified, but the mutable methods of the ExpectationBase
+//     object it references can be called via expectation_base().
+//   - The constructors and destructor are defined out-of-line because
+//     the Symbian WINSCW compiler wants to otherwise instantiate them
+//     when it sees this class definition, at which point it doesn't have
+//     ExpectationBase available yet, leading to incorrect destruction
+//     in the linked_ptr (or compilation errors if using a checking
+//     linked_ptr).
+class Expectation {
+ public:
+  // Constructs a null object that doesn't reference any expectation.
+  Expectation();
+
+  ~Expectation();
+
+  // This single-argument ctor must not be explicit, in order to support the
+  //   Expectation e = EXPECT_CALL(...);
+  // syntax.
+  //
+  // A TypedExpectation object stores its pre-requisites as
+  // Expectation objects, and needs to call the non-const Retire()
+  // method on the ExpectationBase objects they reference.  Therefore
+  // Expectation must receive a *non-const* reference to the
+  // ExpectationBase object.
+  Expectation(internal::ExpectationBase& exp);  // NOLINT
+
+  // The compiler-generated copy ctor and operator= work exactly as
+  // intended, so we don't need to define our own.
+
+  // Returns true iff rhs references the same expectation as this object does.
+  bool operator==(const Expectation& rhs) const {
+    return expectation_base_ == rhs.expectation_base_;
+  }
+
+  bool operator!=(const Expectation& rhs) const { return !(*this == rhs); }
+
+ private:
+  friend class ExpectationSet;
+  friend class Sequence;
+  friend class ::testing::internal::ExpectationBase;
+
+  template <typename F>
+  friend class ::testing::internal::FunctionMockerBase;
+
+  template <typename F>
+  friend class ::testing::internal::TypedExpectation;
+
+  // This comparator is needed for putting Expectation objects into a set.
+  class Less {
+   public:
+    bool operator()(const Expectation& lhs, const Expectation& rhs) const {
+      return lhs.expectation_base_.get() < rhs.expectation_base_.get();
+    }
+  };
+
+  typedef ::std::set<Expectation, Less> Set;
+
+  Expectation(
+      const internal::linked_ptr<internal::ExpectationBase>& expectation_base);
+
+  // Returns the expectation this object references.
+  const internal::linked_ptr<internal::ExpectationBase>&
+  expectation_base() const {
+    return expectation_base_;
+  }
+
+  // A linked_ptr that co-owns the expectation this handle references.
+  internal::linked_ptr<internal::ExpectationBase> expectation_base_;
+};
+
+// A set of expectation handles.  Useful in the .After() clause of
+// EXPECT_CALL() for setting the (partial) order of expectations.  The
+// syntax:
+//
+//   ExpectationSet es;
+//   es += EXPECT_CALL(...)...;
+//   es += EXPECT_CALL(...)...;
+//   EXPECT_CALL(...).After(es)...;
+//
+// sets three expectations where the last one can only be matched
+// after the first two have both been satisfied.
+//
+// This class is copyable and has value semantics.
+class ExpectationSet {
+ public:
+  // A bidirectional iterator that can read a const element in the set.
+  typedef Expectation::Set::const_iterator const_iterator;
+
+  // An object stored in the set.  This is an alias of Expectation.
+  typedef Expectation::Set::value_type value_type;
+
+  // Constructs an empty set.
+  ExpectationSet() {}
+
+  // This single-argument ctor must not be explicit, in order to support the
+  //   ExpectationSet es = EXPECT_CALL(...);
+  // syntax.
+  ExpectationSet(internal::ExpectationBase& exp) {  // NOLINT
+    *this += Expectation(exp);
+  }
+
+  // This single-argument ctor implements implicit conversion from
+  // Expectation and thus must not be explicit.  This allows either an
+  // Expectation or an ExpectationSet to be used in .After().
+  ExpectationSet(const Expectation& e) {  // NOLINT
+    *this += e;
+  }
+
+  // The compiler-generator ctor and operator= works exactly as
+  // intended, so we don't need to define our own.
+
+  // Returns true iff rhs contains the same set of Expectation objects
+  // as this does.
+  bool operator==(const ExpectationSet& rhs) const {
+    return expectations_ == rhs.expectations_;
+  }
+
+  bool operator!=(const ExpectationSet& rhs) const { return !(*this == rhs); }
+
+  // Implements the syntax
+  //   expectation_set += EXPECT_CALL(...);
+  ExpectationSet& operator+=(const Expectation& e) {
+    expectations_.insert(e);
+    return *this;
+  }
+
+  int size() const { return static_cast<int>(expectations_.size()); }
+
+  const_iterator begin() const { return expectations_.begin(); }
+  const_iterator end() const { return expectations_.end(); }
+
+ private:
+  Expectation::Set expectations_;
+};
+
+
+// Sequence objects are used by a user to specify the relative order
+// in which the expectations should match.  They are copyable (we rely
+// on the compiler-defined copy constructor and assignment operator).
+class Sequence {
+ public:
+  // Constructs an empty sequence.
+  Sequence() : last_expectation_(new Expectation) {}
+
+  // Adds an expectation to this sequence.  The caller must ensure
+  // that no other thread is accessing this Sequence object.
+  void AddExpectation(const Expectation& expectation) const;
+
+ private:
+  // The last expectation in this sequence.  We use a linked_ptr here
+  // because Sequence objects are copyable and we want the copies to
+  // be aliases.  The linked_ptr allows the copies to co-own and share
+  // the same Expectation object.
+  internal::linked_ptr<Expectation> last_expectation_;
+};  // class Sequence
+
+// An object of this type causes all EXPECT_CALL() statements
+// encountered in its scope to be put in an anonymous sequence.  The
+// work is done in the constructor and destructor.  You should only
+// create an InSequence object on the stack.
+//
+// The sole purpose for this class is to support easy definition of
+// sequential expectations, e.g.
+//
+//   {
+//     InSequence dummy;  // The name of the object doesn't matter.
+//
+//     // The following expectations must match in the order they appear.
+//     EXPECT_CALL(a, Bar())...;
+//     EXPECT_CALL(a, Baz())...;
+//     ...
+//     EXPECT_CALL(b, Xyz())...;
+//   }
+//
+// You can create InSequence objects in multiple threads, as long as
+// they are used to affect different mock objects.  The idea is that
+// each thread can create and set up its own mocks as if it's the only
+// thread.  However, for clarity of your tests we recommend you to set
+// up mocks in the main thread unless you have a good reason not to do
+// so.
+class InSequence {
+ public:
+  InSequence();
+  ~InSequence();
+ private:
+  bool sequence_created_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(InSequence);  // NOLINT
+} GMOCK_ATTRIBUTE_UNUSED_;
+
+namespace internal {
+
+// Points to the implicit sequence introduced by a living InSequence
+// object (if any) in the current thread or NULL.
+extern ThreadLocal<Sequence*> g_gmock_implicit_sequence;
+
+// Base class for implementing expectations.
+//
+// There are two reasons for having a type-agnostic base class for
+// Expectation:
+//
+//   1. We need to store collections of expectations of different
+//   types (e.g. all pre-requisites of a particular expectation, all
+//   expectations in a sequence).  Therefore these expectation objects
+//   must share a common base class.
+//
+//   2. We can avoid binary code bloat by moving methods not depending
+//   on the template argument of Expectation to the base class.
+//
+// This class is internal and mustn't be used by user code directly.
+class ExpectationBase {
+ public:
+  // source_text is the EXPECT_CALL(...) source that created this Expectation.
+  ExpectationBase(const char* file, int line, const string& source_text);
+
+  virtual ~ExpectationBase();
+
+  // Where in the source file was the expectation spec defined?
+  const char* file() const { return file_; }
+  int line() const { return line_; }
+  const char* source_text() const { return source_text_.c_str(); }
+  // Returns the cardinality specified in the expectation spec.
+  const Cardinality& cardinality() const { return cardinality_; }
+
+  // Describes the source file location of this expectation.
+  void DescribeLocationTo(::std::ostream* os) const {
+    *os << file() << ":" << line() << ": ";
+  }
+
+  // Describes how many times a function call matching this
+  // expectation has occurred.
+  // L >= g_gmock_mutex
+  virtual void DescribeCallCountTo(::std::ostream* os) const = 0;
+ protected:
+  friend class ::testing::Expectation;
+
+  enum Clause {
+    // Don't change the order of the enum members!
+    kNone,
+    kWith,
+    kTimes,
+    kInSequence,
+    kAfter,
+    kWillOnce,
+    kWillRepeatedly,
+    kRetiresOnSaturation,
+  };
+
+  // Returns an Expectation object that references and co-owns this
+  // expectation.
+  virtual Expectation GetHandle() = 0;
+
+  // Asserts that the EXPECT_CALL() statement has the given property.
+  void AssertSpecProperty(bool property, const string& failure_message) const {
+    Assert(property, file_, line_, failure_message);
+  }
+
+  // Expects that the EXPECT_CALL() statement has the given property.
+  void ExpectSpecProperty(bool property, const string& failure_message) const {
+    Expect(property, file_, line_, failure_message);
+  }
+
+  // Explicitly specifies the cardinality of this expectation.  Used
+  // by the subclasses to implement the .Times() clause.
+  void SpecifyCardinality(const Cardinality& cardinality);
+
+  // Returns true iff the user specified the cardinality explicitly
+  // using a .Times().
+  bool cardinality_specified() const { return cardinality_specified_; }
+
+  // Sets the cardinality of this expectation spec.
+  void set_cardinality(const Cardinality& cardinality) {
+    cardinality_ = cardinality;
+  }
+
+  // The following group of methods should only be called after the
+  // EXPECT_CALL() statement, and only when g_gmock_mutex is held by
+  // the current thread.
+
+  // Retires all pre-requisites of this expectation.
+  // L >= g_gmock_mutex
+  void RetireAllPreRequisites();
+
+  // Returns true iff this expectation is retired.
+  // L >= g_gmock_mutex
+  bool is_retired() const {
+    g_gmock_mutex.AssertHeld();
+    return retired_;
+  }
+
+  // Retires this expectation.
+  // L >= g_gmock_mutex
+  void Retire() {
+    g_gmock_mutex.AssertHeld();
+    retired_ = true;
+  }
+
+  // Returns true iff this expectation is satisfied.
+  // L >= g_gmock_mutex
+  bool IsSatisfied() const {
+    g_gmock_mutex.AssertHeld();
+    return cardinality().IsSatisfiedByCallCount(call_count_);
+  }
+
+  // Returns true iff this expectation is saturated.
+  // L >= g_gmock_mutex
+  bool IsSaturated() const {
+    g_gmock_mutex.AssertHeld();
+    return cardinality().IsSaturatedByCallCount(call_count_);
+  }
+
+  // Returns true iff this expectation is over-saturated.
+  // L >= g_gmock_mutex
+  bool IsOverSaturated() const {
+    g_gmock_mutex.AssertHeld();
+    return cardinality().IsOverSaturatedByCallCount(call_count_);
+  }
+
+  // Returns true iff all pre-requisites of this expectation are satisfied.
+  // L >= g_gmock_mutex
+  bool AllPrerequisitesAreSatisfied() const;
+
+  // Adds unsatisfied pre-requisites of this expectation to 'result'.
+  // L >= g_gmock_mutex
+  void FindUnsatisfiedPrerequisites(ExpectationSet* result) const;
+
+  // Returns the number this expectation has been invoked.
+  // L >= g_gmock_mutex
+  int call_count() const {
+    g_gmock_mutex.AssertHeld();
+    return call_count_;
+  }
+
+  // Increments the number this expectation has been invoked.
+  // L >= g_gmock_mutex
+  void IncrementCallCount() {
+    g_gmock_mutex.AssertHeld();
+    call_count_++;
+  }
+
+ private:
+  friend class ::testing::Sequence;
+  friend class ::testing::internal::ExpectationTester;
+
+  template <typename Function>
+  friend class TypedExpectation;
+
+  // This group of fields are part of the spec and won't change after
+  // an EXPECT_CALL() statement finishes.
+  const char* file_;          // The file that contains the expectation.
+  int line_;                  // The line number of the expectation.
+  const string source_text_;  // The EXPECT_CALL(...) source text.
+  // True iff the cardinality is specified explicitly.
+  bool cardinality_specified_;
+  Cardinality cardinality_;            // The cardinality of the expectation.
+  // The immediate pre-requisites (i.e. expectations that must be
+  // satisfied before this expectation can be matched) of this
+  // expectation.  We use linked_ptr in the set because we want an
+  // Expectation object to be co-owned by its FunctionMocker and its
+  // successors.  This allows multiple mock objects to be deleted at
+  // different times.
+  ExpectationSet immediate_prerequisites_;
+
+  // This group of fields are the current state of the expectation,
+  // and can change as the mock function is called.
+  int call_count_;  // How many times this expectation has been invoked.
+  bool retired_;    // True iff this expectation has retired.
+};  // class ExpectationBase
+
+// Impements an expectation for the given function type.
+template <typename F>
+class TypedExpectation : public ExpectationBase {
+ public:
+  typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+  typedef typename Function<F>::ArgumentMatcherTuple ArgumentMatcherTuple;
+  typedef typename Function<F>::Result Result;
+
+  TypedExpectation(FunctionMockerBase<F>* owner,
+                   const char* file, int line, const string& source_text,
+                   const ArgumentMatcherTuple& m)
+      : ExpectationBase(file, line, source_text),
+        owner_(owner),
+        matchers_(m),
+        extra_matcher_specified_(false),
+        // By default, extra_matcher_ should match anything.  However,
+        // we cannot initialize it with _ as that triggers a compiler
+        // bug in Symbian's C++ compiler (cannot decide between two
+        // overloaded constructors of Matcher<const ArgumentTuple&>).
+        extra_matcher_(A<const ArgumentTuple&>()),
+        repeated_action_specified_(false),
+        repeated_action_(DoDefault()),
+        retires_on_saturation_(false),
+        last_clause_(kNone),
+        action_count_checked_(false) {}
+
+  virtual ~TypedExpectation() {
+    // Check the validity of the action count if it hasn't been done
+    // yet (for example, if the expectation was never used).
+    CheckActionCountIfNotDone();
+  }
+
+  // Implements the .With() clause.
+  TypedExpectation& With(const Matcher<const ArgumentTuple&>& m) {
+    if (last_clause_ == kWith) {
+      ExpectSpecProperty(false,
+                         ".With() cannot appear "
+                         "more than once in an EXPECT_CALL().");
+    } else {
+      ExpectSpecProperty(last_clause_ < kWith,
+                         ".With() must be the first "
+                         "clause in an EXPECT_CALL().");
+    }
+    last_clause_ = kWith;
+
+    extra_matcher_ = m;
+    extra_matcher_specified_ = true;
+    return *this;
+  }
+
+  // Implements the .Times() clause.
+  TypedExpectation& Times(const Cardinality& cardinality) {
+    if (last_clause_ ==kTimes) {
+      ExpectSpecProperty(false,
+                         ".Times() cannot appear "
+                         "more than once in an EXPECT_CALL().");
+    } else {
+      ExpectSpecProperty(last_clause_ < kTimes,
+                         ".Times() cannot appear after "
+                         ".InSequence(), .WillOnce(), .WillRepeatedly(), "
+                         "or .RetiresOnSaturation().");
+    }
+    last_clause_ = kTimes;
+
+    ExpectationBase::SpecifyCardinality(cardinality);
+    return *this;
+  }
+
+  // Implements the .Times() clause.
+  TypedExpectation& Times(int n) {
+    return Times(Exactly(n));
+  }
+
+  // Implements the .InSequence() clause.
+  TypedExpectation& InSequence(const Sequence& s) {
+    ExpectSpecProperty(last_clause_ <= kInSequence,
+                       ".InSequence() cannot appear after .After(),"
+                       " .WillOnce(), .WillRepeatedly(), or "
+                       ".RetiresOnSaturation().");
+    last_clause_ = kInSequence;
+
+    s.AddExpectation(GetHandle());
+    return *this;
+  }
+  TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2) {
+    return InSequence(s1).InSequence(s2);
+  }
+  TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2,
+                               const Sequence& s3) {
+    return InSequence(s1, s2).InSequence(s3);
+  }
+  TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2,
+                               const Sequence& s3, const Sequence& s4) {
+    return InSequence(s1, s2, s3).InSequence(s4);
+  }
+  TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2,
+                               const Sequence& s3, const Sequence& s4,
+                               const Sequence& s5) {
+    return InSequence(s1, s2, s3, s4).InSequence(s5);
+  }
+
+  // Implements that .After() clause.
+  TypedExpectation& After(const ExpectationSet& s) {
+    ExpectSpecProperty(last_clause_ <= kAfter,
+                       ".After() cannot appear after .WillOnce(),"
+                       " .WillRepeatedly(), or "
+                       ".RetiresOnSaturation().");
+    last_clause_ = kAfter;
+
+    for (ExpectationSet::const_iterator it = s.begin(); it != s.end(); ++it) {
+      immediate_prerequisites_ += *it;
+    }
+    return *this;
+  }
+  TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2) {
+    return After(s1).After(s2);
+  }
+  TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2,
+                          const ExpectationSet& s3) {
+    return After(s1, s2).After(s3);
+  }
+  TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2,
+                          const ExpectationSet& s3, const ExpectationSet& s4) {
+    return After(s1, s2, s3).After(s4);
+  }
+  TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2,
+                          const ExpectationSet& s3, const ExpectationSet& s4,
+                          const ExpectationSet& s5) {
+    return After(s1, s2, s3, s4).After(s5);
+  }
+
+  // Implements the .WillOnce() clause.
+  TypedExpectation& WillOnce(const Action<F>& action) {
+    ExpectSpecProperty(last_clause_ <= kWillOnce,
+                       ".WillOnce() cannot appear after "
+                       ".WillRepeatedly() or .RetiresOnSaturation().");
+    last_clause_ = kWillOnce;
+
+    actions_.push_back(action);
+    if (!cardinality_specified()) {
+      set_cardinality(Exactly(static_cast<int>(actions_.size())));
+    }
+    return *this;
+  }
+
+  // Implements the .WillRepeatedly() clause.
+  TypedExpectation& WillRepeatedly(const Action<F>& action) {
+    if (last_clause_ == kWillRepeatedly) {
+      ExpectSpecProperty(false,
+                         ".WillRepeatedly() cannot appear "
+                         "more than once in an EXPECT_CALL().");
+    } else {
+      ExpectSpecProperty(last_clause_ < kWillRepeatedly,
+                         ".WillRepeatedly() cannot appear "
+                         "after .RetiresOnSaturation().");
+    }
+    last_clause_ = kWillRepeatedly;
+    repeated_action_specified_ = true;
+
+    repeated_action_ = action;
+    if (!cardinality_specified()) {
+      set_cardinality(AtLeast(static_cast<int>(actions_.size())));
+    }
+
+    // Now that no more action clauses can be specified, we check
+    // whether their count makes sense.
+    CheckActionCountIfNotDone();
+    return *this;
+  }
+
+  // Implements the .RetiresOnSaturation() clause.
+  TypedExpectation& RetiresOnSaturation() {
+    ExpectSpecProperty(last_clause_ < kRetiresOnSaturation,
+                       ".RetiresOnSaturation() cannot appear "
+                       "more than once.");
+    last_clause_ = kRetiresOnSaturation;
+    retires_on_saturation_ = true;
+
+    // Now that no more action clauses can be specified, we check
+    // whether their count makes sense.
+    CheckActionCountIfNotDone();
+    return *this;
+  }
+
+  // Returns the matchers for the arguments as specified inside the
+  // EXPECT_CALL() macro.
+  const ArgumentMatcherTuple& matchers() const {
+    return matchers_;
+  }
+
+  // Returns the matcher specified by the .With() clause.
+  const Matcher<const ArgumentTuple&>& extra_matcher() const {
+    return extra_matcher_;
+  }
+
+  // Returns the sequence of actions specified by the .WillOnce() clause.
+  const std::vector<Action<F> >& actions() const { return actions_; }
+
+  // Returns the action specified by the .WillRepeatedly() clause.
+  const Action<F>& repeated_action() const { return repeated_action_; }
+
+  // Returns true iff the .RetiresOnSaturation() clause was specified.
+  bool retires_on_saturation() const { return retires_on_saturation_; }
+
+  // Describes how many times a function call matching this
+  // expectation has occurred (implements
+  // ExpectationBase::DescribeCallCountTo()).
+  // L >= g_gmock_mutex
+  virtual void DescribeCallCountTo(::std::ostream* os) const {
+    g_gmock_mutex.AssertHeld();
+
+    // Describes how many times the function is expected to be called.
+    *os << "         Expected: to be ";
+    cardinality().DescribeTo(os);
+    *os << "\n           Actual: ";
+    Cardinality::DescribeActualCallCountTo(call_count(), os);
+
+    // Describes the state of the expectation (e.g. is it satisfied?
+    // is it active?).
+    *os << " - " << (IsOverSaturated() ? "over-saturated" :
+                     IsSaturated() ? "saturated" :
+                     IsSatisfied() ? "satisfied" : "unsatisfied")
+        << " and "
+        << (is_retired() ? "retired" : "active");
+  }
+
+  void MaybeDescribeExtraMatcherTo(::std::ostream* os) {
+    if (extra_matcher_specified_) {
+      *os << "    Expected args: ";
+      extra_matcher_.DescribeTo(os);
+      *os << "\n";
+    }
+  }
+
+ private:
+  template <typename Function>
+  friend class FunctionMockerBase;
+
+  // Returns an Expectation object that references and co-owns this
+  // expectation.
+  virtual Expectation GetHandle() {
+    return owner_->GetHandleOf(this);
+  }
+
+  // The following methods will be called only after the EXPECT_CALL()
+  // statement finishes and when the current thread holds
+  // g_gmock_mutex.
+
+  // Returns true iff this expectation matches the given arguments.
+  // L >= g_gmock_mutex
+  bool Matches(const ArgumentTuple& args) const {
+    g_gmock_mutex.AssertHeld();
+    return TupleMatches(matchers_, args) && extra_matcher_.Matches(args);
+  }
+
+  // Returns true iff this expectation should handle the given arguments.
+  // L >= g_gmock_mutex
+  bool ShouldHandleArguments(const ArgumentTuple& args) const {
+    g_gmock_mutex.AssertHeld();
+
+    // In case the action count wasn't checked when the expectation
+    // was defined (e.g. if this expectation has no WillRepeatedly()
+    // or RetiresOnSaturation() clause), we check it when the
+    // expectation is used for the first time.
+    CheckActionCountIfNotDone();
+    return !is_retired() && AllPrerequisitesAreSatisfied() && Matches(args);
+  }
+
+  // Describes the result of matching the arguments against this
+  // expectation to the given ostream.
+  // L >= g_gmock_mutex
+  void DescribeMatchResultTo(const ArgumentTuple& args,
+                             ::std::ostream* os) const {
+    g_gmock_mutex.AssertHeld();
+
+    if (is_retired()) {
+      *os << "         Expected: the expectation is active\n"
+          << "           Actual: it is retired\n";
+    } else if (!Matches(args)) {
+      if (!TupleMatches(matchers_, args)) {
+        DescribeMatchFailureTupleTo(matchers_, args, os);
+      }
+      if (!extra_matcher_.Matches(args)) {
+        *os << "    Expected args: ";
+        extra_matcher_.DescribeTo(os);
+        *os << "\n           Actual: don't match";
+
+        internal::ExplainMatchResultAsNeededTo<const ArgumentTuple&>(
+            extra_matcher_, args, os);
+        *os << "\n";
+      }
+    } else if (!AllPrerequisitesAreSatisfied()) {
+      *os << "         Expected: all pre-requisites are satisfied\n"
+          << "           Actual: the following immediate pre-requisites "
+          << "are not satisfied:\n";
+      ExpectationSet unsatisfied_prereqs;
+      FindUnsatisfiedPrerequisites(&unsatisfied_prereqs);
+      int i = 0;
+      for (ExpectationSet::const_iterator it = unsatisfied_prereqs.begin();
+           it != unsatisfied_prereqs.end(); ++it) {
+        it->expectation_base()->DescribeLocationTo(os);
+        *os << "pre-requisite #" << i++ << "\n";
+      }
+      *os << "                   (end of pre-requisites)\n";
+    } else {
+      // This line is here just for completeness' sake.  It will never
+      // be executed as currently the DescribeMatchResultTo() function
+      // is called only when the mock function call does NOT match the
+      // expectation.
+      *os << "The call matches the expectation.\n";
+    }
+  }
+
+  // Returns the action that should be taken for the current invocation.
+  // L >= g_gmock_mutex
+  const Action<F>& GetCurrentAction(const FunctionMockerBase<F>* mocker,
+                                    const ArgumentTuple& args) const {
+    g_gmock_mutex.AssertHeld();
+    const int count = call_count();
+    Assert(count >= 1, __FILE__, __LINE__,
+           "call_count() is <= 0 when GetCurrentAction() is "
+           "called - this should never happen.");
+
+    const int action_count = static_cast<int>(actions().size());
+    if (action_count > 0 && !repeated_action_specified_ &&
+        count > action_count) {
+      // If there is at least one WillOnce() and no WillRepeatedly(),
+      // we warn the user when the WillOnce() clauses ran out.
+      ::std::stringstream ss;
+      DescribeLocationTo(&ss);
+      ss << "Actions ran out in " << source_text() << "...\n"
+         << "Called " << count << " times, but only "
+         << action_count << " WillOnce()"
+         << (action_count == 1 ? " is" : "s are") << " specified - ";
+      mocker->DescribeDefaultActionTo(args, &ss);
+      Log(WARNING, ss.str(), 1);
+    }
+
+    return count <= action_count ? actions()[count - 1] : repeated_action();
+  }
+
+  // Given the arguments of a mock function call, if the call will
+  // over-saturate this expectation, returns the default action;
+  // otherwise, returns the next action in this expectation.  Also
+  // describes *what* happened to 'what', and explains *why* Google
+  // Mock does it to 'why'.  This method is not const as it calls
+  // IncrementCallCount().
+  // L >= g_gmock_mutex
+  Action<F> GetActionForArguments(const FunctionMockerBase<F>* mocker,
+                                  const ArgumentTuple& args,
+                                  ::std::ostream* what,
+                                  ::std::ostream* why) {
+    g_gmock_mutex.AssertHeld();
+    if (IsSaturated()) {
+      // We have an excessive call.
+      IncrementCallCount();
+      *what << "Mock function called more times than expected - ";
+      mocker->DescribeDefaultActionTo(args, what);
+      DescribeCallCountTo(why);
+
+      // TODO(wan): allow the user to control whether unexpected calls
+      // should fail immediately or continue using a flag
+      // --gmock_unexpected_calls_are_fatal.
+      return DoDefault();
+    }
+
+    IncrementCallCount();
+    RetireAllPreRequisites();
+
+    if (retires_on_saturation() && IsSaturated()) {
+      Retire();
+    }
+
+    // Must be done after IncrementCount()!
+    *what << "Mock function call matches " << source_text() <<"...\n";
+    return GetCurrentAction(mocker, args);
+  }
+
+  // Checks the action count (i.e. the number of WillOnce() and
+  // WillRepeatedly() clauses) against the cardinality if this hasn't
+  // been done before.  Prints a warning if there are too many or too
+  // few actions.
+  // L < mutex_
+  void CheckActionCountIfNotDone() const {
+    bool should_check = false;
+    {
+      MutexLock l(&mutex_);
+      if (!action_count_checked_) {
+        action_count_checked_ = true;
+        should_check = true;
+      }
+    }
+
+    if (should_check) {
+      if (!cardinality_specified_) {
+        // The cardinality was inferred - no need to check the action
+        // count against it.
+        return;
+      }
+
+      // The cardinality was explicitly specified.
+      const int action_count = static_cast<int>(actions_.size());
+      const int upper_bound = cardinality().ConservativeUpperBound();
+      const int lower_bound = cardinality().ConservativeLowerBound();
+      bool too_many;  // True if there are too many actions, or false
+                      // if there are too few.
+      if (action_count > upper_bound ||
+          (action_count == upper_bound && repeated_action_specified_)) {
+        too_many = true;
+      } else if (0 < action_count && action_count < lower_bound &&
+                 !repeated_action_specified_) {
+        too_many = false;
+      } else {
+        return;
+      }
+
+      ::std::stringstream ss;
+      DescribeLocationTo(&ss);
+      ss << "Too " << (too_many ? "many" : "few")
+         << " actions specified in " << source_text() << "...\n"
+         << "Expected to be ";
+      cardinality().DescribeTo(&ss);
+      ss << ", but has " << (too_many ? "" : "only ")
+         << action_count << " WillOnce()"
+         << (action_count == 1 ? "" : "s");
+      if (repeated_action_specified_) {
+        ss << " and a WillRepeatedly()";
+      }
+      ss << ".";
+      Log(WARNING, ss.str(), -1);  // -1 means "don't print stack trace".
+    }
+  }
+
+  // All the fields below won't change once the EXPECT_CALL()
+  // statement finishes.
+  FunctionMockerBase<F>* const owner_;
+  ArgumentMatcherTuple matchers_;
+  bool extra_matcher_specified_;
+  Matcher<const ArgumentTuple&> extra_matcher_;
+  std::vector<Action<F> > actions_;
+  bool repeated_action_specified_;  // True if a WillRepeatedly() was specified.
+  Action<F> repeated_action_;
+  bool retires_on_saturation_;
+  Clause last_clause_;
+  mutable bool action_count_checked_;  // Under mutex_.
+  mutable Mutex mutex_;  // Protects action_count_checked_.
+};  // class TypedExpectation
+
+// A MockSpec object is used by ON_CALL() or EXPECT_CALL() for
+// specifying the default behavior of, or expectation on, a mock
+// function.
+
+// Note: class MockSpec really belongs to the ::testing namespace.
+// However if we define it in ::testing, MSVC will complain when
+// classes in ::testing::internal declare it as a friend class
+// template.  To workaround this compiler bug, we define MockSpec in
+// ::testing::internal and import it into ::testing.
+
+template <typename F>
+class MockSpec {
+ public:
+  typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+  typedef typename internal::Function<F>::ArgumentMatcherTuple
+      ArgumentMatcherTuple;
+
+  // Constructs a MockSpec object, given the function mocker object
+  // that the spec is associated with.
+  explicit MockSpec(internal::FunctionMockerBase<F>* function_mocker)
+      : function_mocker_(function_mocker) {}
+
+  // Adds a new default action spec to the function mocker and returns
+  // the newly created spec.
+  internal::DefaultActionSpec<F>& InternalDefaultActionSetAt(
+      const char* file, int line, const char* obj, const char* call) {
+    LogWithLocation(internal::INFO, file, line,
+        string("ON_CALL(") + obj + ", " + call + ") invoked");
+    return function_mocker_->AddNewDefaultActionSpec(file, line, matchers_);
+  }
+
+  // Adds a new expectation spec to the function mocker and returns
+  // the newly created spec.
+  internal::TypedExpectation<F>& InternalExpectedAt(
+      const char* file, int line, const char* obj, const char* call) {
+    const string source_text(string("EXPECT_CALL(") + obj + ", " + call + ")");
+    LogWithLocation(internal::INFO, file, line, source_text + " invoked");
+    return function_mocker_->AddNewExpectation(
+        file, line, source_text, matchers_);
+  }
+
+ private:
+  template <typename Function>
+  friend class internal::FunctionMocker;
+
+  void SetMatchers(const ArgumentMatcherTuple& matchers) {
+    matchers_ = matchers;
+  }
+
+  // Logs a message including file and line number information.
+  void LogWithLocation(testing::internal::LogSeverity severity,
+                       const char* file, int line,
+                       const string& message) {
+    ::std::ostringstream s;
+    s << file << ":" << line << ": " << message << ::std::endl;
+    Log(severity, s.str(), 0);
+  }
+
+  // The function mocker that owns this spec.
+  internal::FunctionMockerBase<F>* const function_mocker_;
+  // The argument matchers specified in the spec.
+  ArgumentMatcherTuple matchers_;
+};  // class MockSpec
+
+// MSVC warns about using 'this' in base member initializer list, so
+// we need to temporarily disable the warning.  We have to do it for
+// the entire class to suppress the warning, even though it's about
+// the constructor only.
+
+#ifdef _MSC_VER
+#pragma warning(push)          // Saves the current warning state.
+#pragma warning(disable:4355)  // Temporarily disables warning 4355.
+#endif  // _MSV_VER
+
+// C++ treats the void type specially.  For example, you cannot define
+// a void-typed variable or pass a void value to a function.
+// ActionResultHolder<T> holds a value of type T, where T must be a
+// copyable type or void (T doesn't need to be default-constructable).
+// It hides the syntactic difference between void and other types, and
+// is used to unify the code for invoking both void-returning and
+// non-void-returning mock functions.  This generic definition is used
+// when T is not void.
+template <typename T>
+class ActionResultHolder {
+ public:
+  explicit ActionResultHolder(T value) : value_(value) {}
+
+  // The compiler-generated copy constructor and assignment operator
+  // are exactly what we need, so we don't need to define them.
+
+  T value() const { return value_; }
+
+  // Prints the held value as an action's result to os.
+  void PrintAsActionResult(::std::ostream* os) const {
+    *os << "\n          Returns: ";
+    UniversalPrinter<T>::Print(value_, os);
+  }
+
+  // Performs the given mock function's default action and returns the
+  // result in a ActionResultHolder.
+  template <typename Function, typename Arguments>
+  static ActionResultHolder PerformDefaultAction(
+      const FunctionMockerBase<Function>* func_mocker,
+      const Arguments& args,
+      const string& call_description) {
+    return ActionResultHolder(
+        func_mocker->PerformDefaultAction(args, call_description));
+  }
+
+  // Performs the given action and returns the result in a
+  // ActionResultHolder.
+  template <typename Function, typename Arguments>
+  static ActionResultHolder PerformAction(const Action<Function>& action,
+                                          const Arguments& args) {
+    return ActionResultHolder(action.Perform(args));
+  }
+
+ private:
+  T value_;
+};
+
+// Specialization for T = void.
+template <>
+class ActionResultHolder<void> {
+ public:
+  ActionResultHolder() {}
+  void value() const {}
+  void PrintAsActionResult(::std::ostream* /* os */) const {}
+
+  template <typename Function, typename Arguments>
+  static ActionResultHolder PerformDefaultAction(
+      const FunctionMockerBase<Function>* func_mocker,
+      const Arguments& args,
+      const string& call_description) {
+    func_mocker->PerformDefaultAction(args, call_description);
+    return ActionResultHolder();
+  }
+
+  template <typename Function, typename Arguments>
+  static ActionResultHolder PerformAction(const Action<Function>& action,
+                                          const Arguments& args) {
+    action.Perform(args);
+    return ActionResultHolder();
+  }
+};
+
+// The base of the function mocker class for the given function type.
+// We put the methods in this class instead of its child to avoid code
+// bloat.
+template <typename F>
+class FunctionMockerBase : public UntypedFunctionMockerBase {
+ public:
+  typedef typename Function<F>::Result Result;
+  typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+  typedef typename Function<F>::ArgumentMatcherTuple ArgumentMatcherTuple;
+
+  FunctionMockerBase() : mock_obj_(NULL), name_(""), current_spec_(this) {}
+
+  // The destructor verifies that all expectations on this mock
+  // function have been satisfied.  If not, it will report Google Test
+  // non-fatal failures for the violations.
+  // L < g_gmock_mutex
+  virtual ~FunctionMockerBase() {
+    MutexLock l(&g_gmock_mutex);
+    VerifyAndClearExpectationsLocked();
+    Mock::UnregisterLocked(this);
+  }
+
+  // Returns the ON_CALL spec that matches this mock function with the
+  // given arguments; returns NULL if no matching ON_CALL is found.
+  // L = *
+  const DefaultActionSpec<F>* FindDefaultActionSpec(
+      const ArgumentTuple& args) const {
+    for (typename std::vector<DefaultActionSpec<F> >::const_reverse_iterator it
+             = default_actions_.rbegin();
+         it != default_actions_.rend(); ++it) {
+      const DefaultActionSpec<F>& spec = *it;
+      if (spec.Matches(args))
+        return &spec;
+    }
+
+    return NULL;
+  }
+
+  // Performs the default action of this mock function on the given arguments
+  // and returns the result. Asserts with a helpful call descrption if there is
+  // no valid return value. This method doesn't depend on the mutable state of
+  // this object, and thus can be called concurrently without locking.
+  // L = *
+  Result PerformDefaultAction(const ArgumentTuple& args,
+                              const string& call_description) const {
+    const DefaultActionSpec<F>* const spec = FindDefaultActionSpec(args);
+    if (spec != NULL) {
+      return spec->GetAction().Perform(args);
+    }
+    Assert(DefaultValue<Result>::Exists(), "", -1,
+           call_description + "\n    The mock function has no default action "
+           "set, and its return type has no default value set.");
+    return DefaultValue<Result>::Get();
+  }
+
+  // Registers this function mocker and the mock object owning it;
+  // returns a reference to the function mocker object.  This is only
+  // called by the ON_CALL() and EXPECT_CALL() macros.
+  // L < g_gmock_mutex
+  FunctionMocker<F>& RegisterOwner(const void* mock_obj) {
+    {
+      MutexLock l(&g_gmock_mutex);
+      mock_obj_ = mock_obj;
+    }
+    Mock::Register(mock_obj, this);
+    return *::testing::internal::down_cast<FunctionMocker<F>*>(this);
+  }
+
+  // The following two functions are from UntypedFunctionMockerBase.
+
+  // Verifies that all expectations on this mock function have been
+  // satisfied.  Reports one or more Google Test non-fatal failures
+  // and returns false if not.
+  // L >= g_gmock_mutex
+  virtual bool VerifyAndClearExpectationsLocked();
+
+  // Clears the ON_CALL()s set on this mock function.
+  // L >= g_gmock_mutex
+  virtual void ClearDefaultActionsLocked() {
+    g_gmock_mutex.AssertHeld();
+    default_actions_.clear();
+  }
+
+  // Sets the name of the function being mocked.  Will be called upon
+  // each invocation of this mock function.
+  // L < g_gmock_mutex
+  void SetOwnerAndName(const void* mock_obj, const char* name) {
+    // We protect name_ under g_gmock_mutex in case this mock function
+    // is called from two threads concurrently.
+    MutexLock l(&g_gmock_mutex);
+    mock_obj_ = mock_obj;
+    name_ = name;
+  }
+
+  // Returns the address of the mock object this method belongs to.
+  // Must be called after SetOwnerAndName() has been called.
+  // L < g_gmock_mutex
+  const void* MockObject() const {
+    const void* mock_obj;
+    {
+      // We protect mock_obj_ under g_gmock_mutex in case this mock
+      // function is called from two threads concurrently.
+      MutexLock l(&g_gmock_mutex);
+      mock_obj = mock_obj_;
+    }
+    return mock_obj;
+  }
+
+  // Returns the name of the function being mocked.  Must be called
+  // after SetOwnerAndName() has been called.
+  // L < g_gmock_mutex
+  const char* Name() const {
+    const char* name;
+    {
+      // We protect name_ under g_gmock_mutex in case this mock
+      // function is called from two threads concurrently.
+      MutexLock l(&g_gmock_mutex);
+      name = name_;
+    }
+    return name;
+  }
+ protected:
+  template <typename Function>
+  friend class MockSpec;
+
+  // Returns the result of invoking this mock function with the given
+  // arguments.  This function can be safely called from multiple
+  // threads concurrently.
+  // L < g_gmock_mutex
+  Result InvokeWith(const ArgumentTuple& args);
+
+  // Adds and returns a default action spec for this mock function.
+  // L < g_gmock_mutex
+  DefaultActionSpec<F>& AddNewDefaultActionSpec(
+      const char* file, int line,
+      const ArgumentMatcherTuple& m) {
+    Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line);
+    default_actions_.push_back(DefaultActionSpec<F>(file, line, m));
+    return default_actions_.back();
+  }
+
+  // Adds and returns an expectation spec for this mock function.
+  // L < g_gmock_mutex
+  TypedExpectation<F>& AddNewExpectation(
+      const char* file,
+      int line,
+      const string& source_text,
+      const ArgumentMatcherTuple& m) {
+    Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line);
+    const linked_ptr<TypedExpectation<F> > expectation(
+        new TypedExpectation<F>(this, file, line, source_text, m));
+    expectations_.push_back(expectation);
+
+    // Adds this expectation into the implicit sequence if there is one.
+    Sequence* const implicit_sequence = g_gmock_implicit_sequence.get();
+    if (implicit_sequence != NULL) {
+      implicit_sequence->AddExpectation(Expectation(expectation));
+    }
+
+    return *expectation;
+  }
+
+  // The current spec (either default action spec or expectation spec)
+  // being described on this function mocker.
+  MockSpec<F>& current_spec() { return current_spec_; }
+ private:
+  template <typename Func> friend class TypedExpectation;
+
+  typedef std::vector<internal::linked_ptr<TypedExpectation<F> > >
+  TypedExpectations;
+
+  // Returns an Expectation object that references and co-owns exp,
+  // which must be an expectation on this mock function.
+  Expectation GetHandleOf(TypedExpectation<F>* exp) {
+    for (typename TypedExpectations::const_iterator it = expectations_.begin();
+         it != expectations_.end(); ++it) {
+      if (it->get() == exp) {
+        return Expectation(*it);
+      }
+    }
+
+    Assert(false, __FILE__, __LINE__, "Cannot find expectation.");
+    return Expectation();
+    // The above statement is just to make the code compile, and will
+    // never be executed.
+  }
+
+  // Some utilities needed for implementing InvokeWith().
+
+  // Describes what default action will be performed for the given
+  // arguments.
+  // L = *
+  void DescribeDefaultActionTo(const ArgumentTuple& args,
+                               ::std::ostream* os) const {
+    const DefaultActionSpec<F>* const spec = FindDefaultActionSpec(args);
+
+    if (spec == NULL) {
+      *os << (internal::type_equals<Result, void>::value ?
+              "returning directly.\n" :
+              "returning default value.\n");
+    } else {
+      *os << "taking default action specified at:\n"
+          << spec->file() << ":" << spec->line() << ":\n";
+    }
+  }
+
+  // Writes a message that the call is uninteresting (i.e. neither
+  // explicitly expected nor explicitly unexpected) to the given
+  // ostream.
+  // L < g_gmock_mutex
+  void DescribeUninterestingCall(const ArgumentTuple& args,
+                                 ::std::ostream* os) const {
+    *os << "Uninteresting mock function call - ";
+    DescribeDefaultActionTo(args, os);
+    *os << "    Function call: " << Name();
+    UniversalPrinter<ArgumentTuple>::Print(args, os);
+  }
+
+  // Critical section: We must find the matching expectation and the
+  // corresponding action that needs to be taken in an ATOMIC
+  // transaction.  Otherwise another thread may call this mock
+  // method in the middle and mess up the state.
+  //
+  // However, performing the action has to be left out of the critical
+  // section.  The reason is that we have no control on what the
+  // action does (it can invoke an arbitrary user function or even a
+  // mock function) and excessive locking could cause a dead lock.
+  // L < g_gmock_mutex
+  bool FindMatchingExpectationAndAction(
+      const ArgumentTuple& args, TypedExpectation<F>** exp, Action<F>* action,
+      bool* is_excessive, ::std::ostream* what, ::std::ostream* why) {
+    MutexLock l(&g_gmock_mutex);
+    *exp = this->FindMatchingExpectationLocked(args);
+    if (*exp == NULL) {  // A match wasn't found.
+      *action = DoDefault();
+      this->FormatUnexpectedCallMessageLocked(args, what, why);
+      return false;
+    }
+
+    // This line must be done before calling GetActionForArguments(),
+    // which will increment the call count for *exp and thus affect
+    // its saturation status.
+    *is_excessive = (*exp)->IsSaturated();
+    *action = (*exp)->GetActionForArguments(this, args, what, why);
+    return true;
+  }
+
+  // Returns the expectation that matches the arguments, or NULL if no
+  // expectation matches them.
+  // L >= g_gmock_mutex
+  TypedExpectation<F>* FindMatchingExpectationLocked(
+      const ArgumentTuple& args) const {
+    g_gmock_mutex.AssertHeld();
+    for (typename TypedExpectations::const_reverse_iterator it =
+             expectations_.rbegin();
+         it != expectations_.rend(); ++it) {
+      TypedExpectation<F>* const exp = it->get();
+      if (exp->ShouldHandleArguments(args)) {
+        return exp;
+      }
+    }
+    return NULL;
+  }
+
+  // Returns a message that the arguments don't match any expectation.
+  // L >= g_gmock_mutex
+  void FormatUnexpectedCallMessageLocked(const ArgumentTuple& args,
+                                         ::std::ostream* os,
+                                         ::std::ostream* why) const {
+    g_gmock_mutex.AssertHeld();
+    *os << "\nUnexpected mock function call - ";
+    DescribeDefaultActionTo(args, os);
+    PrintTriedExpectationsLocked(args, why);
+  }
+
+  // Prints a list of expectations that have been tried against the
+  // current mock function call.
+  // L >= g_gmock_mutex
+  void PrintTriedExpectationsLocked(const ArgumentTuple& args,
+                                    ::std::ostream* why) const {
+    g_gmock_mutex.AssertHeld();
+    const int count = static_cast<int>(expectations_.size());
+    *why << "Google Mock tried the following " << count << " "
+         << (count == 1 ? "expectation, but it didn't match" :
+             "expectations, but none matched")
+         << ":\n";
+    for (int i = 0; i < count; i++) {
+      *why << "\n";
+      expectations_[i]->DescribeLocationTo(why);
+      if (count > 1) {
+        *why << "tried expectation #" << i << ": ";
+      }
+      *why << expectations_[i]->source_text() << "...\n";
+      expectations_[i]->DescribeMatchResultTo(args, why);
+      expectations_[i]->DescribeCallCountTo(why);
+    }
+  }
+
+  // Address of the mock object this mock method belongs to.  Only
+  // valid after this mock method has been called or
+  // ON_CALL/EXPECT_CALL has been invoked on it.
+  const void* mock_obj_;  // Protected by g_gmock_mutex.
+
+  // Name of the function being mocked.  Only valid after this mock
+  // method has been called.
+  const char* name_;  // Protected by g_gmock_mutex.
+
+  // The current spec (either default action spec or expectation spec)
+  // being described on this function mocker.
+  MockSpec<F> current_spec_;
+
+  // All default action specs for this function mocker.
+  std::vector<DefaultActionSpec<F> > default_actions_;
+  // All expectations for this function mocker.
+  TypedExpectations expectations_;
+
+  // There is no generally useful and implementable semantics of
+  // copying a mock object, so copying a mock is usually a user error.
+  // Thus we disallow copying function mockers.  If the user really
+  // wants to copy a mock object, he should implement his own copy
+  // operation, for example:
+  //
+  //   class MockFoo : public Foo {
+  //    public:
+  //     // Defines a copy constructor explicitly.
+  //     MockFoo(const MockFoo& src) {}
+  //     ...
+  //   };
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(FunctionMockerBase);
+};  // class FunctionMockerBase
+
+#ifdef _MSC_VER
+#pragma warning(pop)  // Restores the warning state.
+#endif  // _MSV_VER
+
+// Implements methods of FunctionMockerBase.
+
+// Verifies that all expectations on this mock function have been
+// satisfied.  Reports one or more Google Test non-fatal failures and
+// returns false if not.
+// L >= g_gmock_mutex
+template <typename F>
+bool FunctionMockerBase<F>::VerifyAndClearExpectationsLocked() {
+  g_gmock_mutex.AssertHeld();
+  bool expectations_met = true;
+  for (typename TypedExpectations::const_iterator it = expectations_.begin();
+       it != expectations_.end(); ++it) {
+    TypedExpectation<F>* const exp = it->get();
+
+    if (exp->IsOverSaturated()) {
+      // There was an upper-bound violation.  Since the error was
+      // already reported when it occurred, there is no need to do
+      // anything here.
+      expectations_met = false;
+    } else if (!exp->IsSatisfied()) {
+      expectations_met = false;
+      ::std::stringstream ss;
+      ss  << "Actual function call count doesn't match "
+          << exp->source_text() << "...\n";
+      // No need to show the source file location of the expectation
+      // in the description, as the Expect() call that follows already
+      // takes care of it.
+      exp->MaybeDescribeExtraMatcherTo(&ss);
+      exp->DescribeCallCountTo(&ss);
+      Expect(false, exp->file(), exp->line(), ss.str());
+    }
+  }
+  expectations_.clear();
+  return expectations_met;
+}
+
+// Reports an uninteresting call (whose description is in msg) in the
+// manner specified by 'reaction'.
+void ReportUninterestingCall(CallReaction reaction, const string& msg);
+
+// Calculates the result of invoking this mock function with the given
+// arguments, prints it, and returns it.
+// L < g_gmock_mutex
+template <typename F>
+typename Function<F>::Result FunctionMockerBase<F>::InvokeWith(
+    const typename Function<F>::ArgumentTuple& args) {
+  typedef ActionResultHolder<Result> ResultHolder;
+
+  if (expectations_.size() == 0) {
+    // No expectation is set on this mock method - we have an
+    // uninteresting call.
+
+    // We must get Google Mock's reaction on uninteresting calls
+    // made on this mock object BEFORE performing the action,
+    // because the action may DELETE the mock object and make the
+    // following expression meaningless.
+    const CallReaction reaction =
+        Mock::GetReactionOnUninterestingCalls(MockObject());
+
+    // True iff we need to print this call's arguments and return
+    // value.  This definition must be kept in sync with
+    // the behavior of ReportUninterestingCall().
+    const bool need_to_report_uninteresting_call =
+        // If the user allows this uninteresting call, we print it
+        // only when he wants informational messages.
+        reaction == ALLOW ? LogIsVisible(INFO) :
+        // If the user wants this to be a warning, we print it only
+        // when he wants to see warnings.
+        reaction == WARN ? LogIsVisible(WARNING) :
+        // Otherwise, the user wants this to be an error, and we
+        // should always print detailed information in the error.
+        true;
+
+    if (!need_to_report_uninteresting_call) {
+      // Perform the action without printing the call information.
+      return PerformDefaultAction(args, "");
+    }
+
+    // Warns about the uninteresting call.
+    ::std::stringstream ss;
+    DescribeUninterestingCall(args, &ss);
+
+    // Calculates the function result.
+    const ResultHolder result =
+        ResultHolder::PerformDefaultAction(this, args, ss.str());
+
+    // Prints the function result.
+    result.PrintAsActionResult(&ss);
+
+    ReportUninterestingCall(reaction, ss.str());
+    return result.value();
+  }
+
+  bool is_excessive = false;
+  ::std::stringstream ss;
+  ::std::stringstream why;
+  ::std::stringstream loc;
+  Action<F> action;
+  TypedExpectation<F>* exp;
+
+  // The FindMatchingExpectationAndAction() function acquires and
+  // releases g_gmock_mutex.
+  const bool found = FindMatchingExpectationAndAction(
+      args, &exp, &action, &is_excessive, &ss, &why);
+
+  // True iff we need to print the call's arguments and return value.
+  // This definition must be kept in sync with the uses of Expect()
+  // and Log() in this function.
+  const bool need_to_report_call = !found || is_excessive || LogIsVisible(INFO);
+  if (!need_to_report_call) {
+    // Perform the action without printing the call information.
+    return action.IsDoDefault() ? PerformDefaultAction(args, "") :
+        action.Perform(args);
+  }
+
+  ss << "    Function call: " << Name();
+  UniversalPrinter<ArgumentTuple>::Print(args, &ss);
+
+  // In case the action deletes a piece of the expectation, we
+  // generate the message beforehand.
+  if (found && !is_excessive) {
+    exp->DescribeLocationTo(&loc);
+  }
+
+  const ResultHolder result = action.IsDoDefault() ?
+      ResultHolder::PerformDefaultAction(this, args, ss.str()) :
+      ResultHolder::PerformAction(action, args);
+  result.PrintAsActionResult(&ss);
+  ss << "\n" << why.str();
+
+  if (!found) {
+    // No expectation matches this call - reports a failure.
+    Expect(false, NULL, -1, ss.str());
+  } else if (is_excessive) {
+    // We had an upper-bound violation and the failure message is in ss.
+    Expect(false, exp->file(), exp->line(), ss.str());
+  } else {
+    // We had an expected call and the matching expectation is
+    // described in ss.
+    Log(INFO, loc.str() + ss.str(), 2);
+  }
+  return result.value();
+}
+
+}  // namespace internal
+
+// The style guide prohibits "using" statements in a namespace scope
+// inside a header file.  However, the MockSpec class template is
+// meant to be defined in the ::testing namespace.  The following line
+// is just a trick for working around a bug in MSVC 8.0, which cannot
+// handle it if we define MockSpec in ::testing.
+using internal::MockSpec;
+
+// Const(x) is a convenient function for obtaining a const reference
+// to x.  This is useful for setting expectations on an overloaded
+// const mock method, e.g.
+//
+//   class MockFoo : public FooInterface {
+//    public:
+//     MOCK_METHOD0(Bar, int());
+//     MOCK_CONST_METHOD0(Bar, int&());
+//   };
+//
+//   MockFoo foo;
+//   // Expects a call to non-const MockFoo::Bar().
+//   EXPECT_CALL(foo, Bar());
+//   // Expects a call to const MockFoo::Bar().
+//   EXPECT_CALL(Const(foo), Bar());
+template <typename T>
+inline const T& Const(const T& x) { return x; }
+
+// Constructs an Expectation object that references and co-owns exp.
+inline Expectation::Expectation(internal::ExpectationBase& exp)  // NOLINT
+    : expectation_base_(exp.GetHandle().expectation_base()) {}
+
+}  // namespace testing
+
+// A separate macro is required to avoid compile errors when the name
+// of the method used in call is a result of macro expansion.
+// See CompilesWithMethodNameExpandedFromMacro tests in
+// internal/gmock-spec-builders_test.cc for more details.
+#define GMOCK_ON_CALL_IMPL_(obj, call) \
+    ((obj).gmock_##call).InternalDefaultActionSetAt(__FILE__, __LINE__, \
+                                                    #obj, #call)
+#define ON_CALL(obj, call) GMOCK_ON_CALL_IMPL_(obj, call)
+
+#define GMOCK_EXPECT_CALL_IMPL_(obj, call) \
+    ((obj).gmock_##call).InternalExpectedAt(__FILE__, __LINE__, #obj, #call)
+#define EXPECT_CALL(obj, call) GMOCK_EXPECT_CALL_IMPL_(obj, call)
+
+#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
diff --git a/third_party/gmock/include/gmock/gmock.h b/third_party/gmock/include/gmock/gmock.h
new file mode 100644
index 0000000..daf5288
--- /dev/null
+++ b/third_party/gmock/include/gmock/gmock.h
@@ -0,0 +1,94 @@
+// Copyright 2007, 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This is the main header file a user should include.
+
+#ifndef GMOCK_INCLUDE_GMOCK_GMOCK_H_
+#define GMOCK_INCLUDE_GMOCK_GMOCK_H_
+
+// This file implements the following syntax:
+//
+//   ON_CALL(mock_object.Method(...))
+//     .With(...) ?
+//     .WillByDefault(...);
+//
+// where With() is optional and WillByDefault() must appear exactly
+// once.
+//
+//   EXPECT_CALL(mock_object.Method(...))
+//     .With(...) ?
+//     .Times(...) ?
+//     .InSequence(...) *
+//     .WillOnce(...) *
+//     .WillRepeatedly(...) ?
+//     .RetiresOnSaturation() ? ;
+//
+// where all clauses are optional and WillOnce() can be repeated.
+
+#include <gmock/gmock-actions.h>
+#include <gmock/gmock-cardinalities.h>
+#include <gmock/gmock-generated-actions.h>
+#include <gmock/gmock-generated-function-mockers.h>
+#include <gmock/gmock-generated-matchers.h>
+#include <gmock/gmock-more-actions.h>
+#include <gmock/gmock-generated-nice-strict.h>
+#include <gmock/gmock-matchers.h>
+#include <gmock/gmock-printers.h>
+#include <gmock/internal/gmock-internal-utils.h>
+
+namespace testing {
+
+// Declares Google Mock flags that we want a user to use programmatically.
+GMOCK_DECLARE_bool_(catch_leaked_mocks);
+GMOCK_DECLARE_string_(verbose);
+
+// Initializes Google Mock.  This must be called before running the
+// tests.  In particular, it parses the command line for the flags
+// that Google Mock recognizes.  Whenever a Google Mock flag is seen,
+// it is removed from argv, and *argc is decremented.
+//
+// No value is returned.  Instead, the Google Mock flag variables are
+// updated.
+//
+// Since Google Test is needed for Google Mock to work, this function
+// also initializes Google Test and parses its flags, if that hasn't
+// been done.
+void InitGoogleMock(int* argc, char** argv);
+
+// This overloaded version can be used in Windows programs compiled in
+// UNICODE mode.
+void InitGoogleMock(int* argc, wchar_t** argv);
+
+}  // namespace testing
+
+#endif  // GMOCK_INCLUDE_GMOCK_GMOCK_H_
diff --git a/third_party/gmock/include/gmock/internal/gmock-generated-internal-utils.h b/third_party/gmock/include/gmock/internal/gmock-generated-internal-utils.h
new file mode 100644
index 0000000..6386b05
--- /dev/null
+++ b/third_party/gmock/include/gmock/internal/gmock-generated-internal-utils.h
@@ -0,0 +1,277 @@
+// This file was GENERATED by a script.  DO NOT EDIT BY HAND!!!
+
+// Copyright 2007, 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file contains template meta-programming utility classes needed
+// for implementing Google Mock.
+
+#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_
+#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_
+
+#include <gmock/internal/gmock-port.h>
+
+namespace testing {
+
+template <typename T>
+class Matcher;
+
+namespace internal {
+
+// An IgnoredValue object can be implicitly constructed from ANY value.
+// This is used in implementing the IgnoreResult(a) action.
+class IgnoredValue {
+ public:
+  // This constructor template allows any value to be implicitly
+  // converted to IgnoredValue.  The object has no data member and
+  // doesn't try to remember anything about the argument.  We
+  // deliberately omit the 'explicit' keyword in order to allow the
+  // conversion to be implicit.
+  template <typename T>
+  IgnoredValue(const T&) {}
+};
+
+// MatcherTuple<T>::type is a tuple type where each field is a Matcher
+// for the corresponding field in tuple type T.
+template <typename Tuple>
+struct MatcherTuple;
+
+template <>
+struct MatcherTuple< ::std::tr1::tuple<> > {
+  typedef ::std::tr1::tuple< > type;
+};
+
+template <typename A1>
+struct MatcherTuple< ::std::tr1::tuple<A1> > {
+  typedef ::std::tr1::tuple<Matcher<A1> > type;
+};
+
+template <typename A1, typename A2>
+struct MatcherTuple< ::std::tr1::tuple<A1, A2> > {
+  typedef ::std::tr1::tuple<Matcher<A1>, Matcher<A2> > type;
+};
+
+template <typename A1, typename A2, typename A3>
+struct MatcherTuple< ::std::tr1::tuple<A1, A2, A3> > {
+  typedef ::std::tr1::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3> > type;
+};
+
+template <typename A1, typename A2, typename A3, typename A4>
+struct MatcherTuple< ::std::tr1::tuple<A1, A2, A3, A4> > {
+  typedef ::std::tr1::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>,
+      Matcher<A4> > type;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5>
+struct MatcherTuple< ::std::tr1::tuple<A1, A2, A3, A4, A5> > {
+  typedef ::std::tr1::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
+      Matcher<A5> > type;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+    typename A6>
+struct MatcherTuple< ::std::tr1::tuple<A1, A2, A3, A4, A5, A6> > {
+  typedef ::std::tr1::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
+      Matcher<A5>, Matcher<A6> > type;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+    typename A6, typename A7>
+struct MatcherTuple< ::std::tr1::tuple<A1, A2, A3, A4, A5, A6, A7> > {
+  typedef ::std::tr1::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
+      Matcher<A5>, Matcher<A6>, Matcher<A7> > type;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+    typename A6, typename A7, typename A8>
+struct MatcherTuple< ::std::tr1::tuple<A1, A2, A3, A4, A5, A6, A7, A8> > {
+  typedef ::std::tr1::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
+      Matcher<A5>, Matcher<A6>, Matcher<A7>, Matcher<A8> > type;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+    typename A6, typename A7, typename A8, typename A9>
+struct MatcherTuple< ::std::tr1::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9> > {
+  typedef ::std::tr1::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
+      Matcher<A5>, Matcher<A6>, Matcher<A7>, Matcher<A8>, Matcher<A9> > type;
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+    typename A6, typename A7, typename A8, typename A9, typename A10>
+struct MatcherTuple< ::std::tr1::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9,
+    A10> > {
+  typedef ::std::tr1::tuple<Matcher<A1>, Matcher<A2>, Matcher<A3>, Matcher<A4>,
+      Matcher<A5>, Matcher<A6>, Matcher<A7>, Matcher<A8>, Matcher<A9>,
+      Matcher<A10> > type;
+};
+
+// Template struct Function<F>, where F must be a function type, contains
+// the following typedefs:
+//
+//   Result:               the function's return type.
+//   ArgumentN:            the type of the N-th argument, where N starts with 1.
+//   ArgumentTuple:        the tuple type consisting of all parameters of F.
+//   ArgumentMatcherTuple: the tuple type consisting of Matchers for all
+//                         parameters of F.
+//   MakeResultVoid:       the function type obtained by substituting void
+//                         for the return type of F.
+//   MakeResultIgnoredValue:
+//                         the function type obtained by substituting Something
+//                         for the return type of F.
+template <typename F>
+struct Function;
+
+template <typename R>
+struct Function<R()> {
+  typedef R Result;
+  typedef ::std::tr1::tuple<> ArgumentTuple;
+  typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+  typedef void MakeResultVoid();
+  typedef IgnoredValue MakeResultIgnoredValue();
+};
+
+template <typename R, typename A1>
+struct Function<R(A1)>
+    : Function<R()> {
+  typedef A1 Argument1;
+  typedef ::std::tr1::tuple<A1> ArgumentTuple;
+  typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+  typedef void MakeResultVoid(A1);
+  typedef IgnoredValue MakeResultIgnoredValue(A1);
+};
+
+template <typename R, typename A1, typename A2>
+struct Function<R(A1, A2)>
+    : Function<R(A1)> {
+  typedef A2 Argument2;
+  typedef ::std::tr1::tuple<A1, A2> ArgumentTuple;
+  typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+  typedef void MakeResultVoid(A1, A2);
+  typedef IgnoredValue MakeResultIgnoredValue(A1, A2);
+};
+
+template <typename R, typename A1, typename A2, typename A3>
+struct Function<R(A1, A2, A3)>
+    : Function<R(A1, A2)> {
+  typedef A3 Argument3;
+  typedef ::std::tr1::tuple<A1, A2, A3> ArgumentTuple;
+  typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+  typedef void MakeResultVoid(A1, A2, A3);
+  typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4>
+struct Function<R(A1, A2, A3, A4)>
+    : Function<R(A1, A2, A3)> {
+  typedef A4 Argument4;
+  typedef ::std::tr1::tuple<A1, A2, A3, A4> ArgumentTuple;
+  typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+  typedef void MakeResultVoid(A1, A2, A3, A4);
+  typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5>
+struct Function<R(A1, A2, A3, A4, A5)>
+    : Function<R(A1, A2, A3, A4)> {
+  typedef A5 Argument5;
+  typedef ::std::tr1::tuple<A1, A2, A3, A4, A5> ArgumentTuple;
+  typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+  typedef void MakeResultVoid(A1, A2, A3, A4, A5);
+  typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6>
+struct Function<R(A1, A2, A3, A4, A5, A6)>
+    : Function<R(A1, A2, A3, A4, A5)> {
+  typedef A6 Argument6;
+  typedef ::std::tr1::tuple<A1, A2, A3, A4, A5, A6> ArgumentTuple;
+  typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+  typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6);
+  typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7>
+struct Function<R(A1, A2, A3, A4, A5, A6, A7)>
+    : Function<R(A1, A2, A3, A4, A5, A6)> {
+  typedef A7 Argument7;
+  typedef ::std::tr1::tuple<A1, A2, A3, A4, A5, A6, A7> ArgumentTuple;
+  typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+  typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7);
+  typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8>
+struct Function<R(A1, A2, A3, A4, A5, A6, A7, A8)>
+    : Function<R(A1, A2, A3, A4, A5, A6, A7)> {
+  typedef A8 Argument8;
+  typedef ::std::tr1::tuple<A1, A2, A3, A4, A5, A6, A7, A8> ArgumentTuple;
+  typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+  typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7, A8);
+  typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7, A8);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8, typename A9>
+struct Function<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)>
+    : Function<R(A1, A2, A3, A4, A5, A6, A7, A8)> {
+  typedef A9 Argument9;
+  typedef ::std::tr1::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9> ArgumentTuple;
+  typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+  typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7, A8, A9);
+  typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7, A8,
+      A9);
+};
+
+template <typename R, typename A1, typename A2, typename A3, typename A4,
+    typename A5, typename A6, typename A7, typename A8, typename A9,
+    typename A10>
+struct Function<R(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10)>
+    : Function<R(A1, A2, A3, A4, A5, A6, A7, A8, A9)> {
+  typedef A10 Argument10;
+  typedef ::std::tr1::tuple<A1, A2, A3, A4, A5, A6, A7, A8, A9,
+      A10> ArgumentTuple;
+  typedef typename MatcherTuple<ArgumentTuple>::type ArgumentMatcherTuple;
+  typedef void MakeResultVoid(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
+  typedef IgnoredValue MakeResultIgnoredValue(A1, A2, A3, A4, A5, A6, A7, A8,
+      A9, A10);
+};
+
+}  // namespace internal
+
+}  // namespace testing
+
+#endif  // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_GENERATED_INTERNAL_UTILS_H_
diff --git a/third_party/gmock/include/gmock/internal/gmock-internal-utils.h b/third_party/gmock/include/gmock/internal/gmock-internal-utils.h
new file mode 100644
index 0000000..7b17335
--- /dev/null
+++ b/third_party/gmock/include/gmock/internal/gmock-internal-utils.h
@@ -0,0 +1,763 @@
+// Copyright 2007, 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file defines some utilities useful for implementing Google
+// Mock.  They are subject to change without notice, so please DO NOT
+// USE THEM IN USER CODE.
+
+#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
+#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
+
+#include <stdio.h>
+#include <ostream>  // NOLINT
+#include <string>
+
+#include <gmock/internal/gmock-generated-internal-utils.h>
+#include <gmock/internal/gmock-port.h>
+#include <gtest/gtest.h>
+
+// Concatenates two pre-processor symbols; works for concatenating
+// built-in macros like __FILE__ and __LINE__.
+#define GMOCK_CONCAT_TOKEN_IMPL_(foo, bar) foo##bar
+#define GMOCK_CONCAT_TOKEN_(foo, bar) GMOCK_CONCAT_TOKEN_IMPL_(foo, bar)
+
+#ifdef __GNUC__
+#define GMOCK_ATTRIBUTE_UNUSED_ __attribute__ ((unused))
+#else
+#define GMOCK_ATTRIBUTE_UNUSED_
+#endif  // __GNUC__
+
+class ProtocolMessage;
+namespace proto2 { class Message; }
+
+namespace testing {
+namespace internal {
+
+// Converts an identifier name to a space-separated list of lower-case
+// words.  Each maximum substring of the form [A-Za-z][a-z]*|\d+ is
+// treated as one word.  For example, both "FooBar123" and
+// "foo_bar_123" are converted to "foo bar 123".
+string ConvertIdentifierNameToWords(const char* id_name);
+
+// Defining a variable of type CompileAssertTypesEqual<T1, T2> will cause a
+// compiler error iff T1 and T2 are different types.
+template <typename T1, typename T2>
+struct CompileAssertTypesEqual;
+
+template <typename T>
+struct CompileAssertTypesEqual<T, T> {
+};
+
+// Removes the reference from a type if it is a reference type,
+// otherwise leaves it unchanged.  This is the same as
+// tr1::remove_reference, which is not widely available yet.
+template <typename T>
+struct RemoveReference { typedef T type; };  // NOLINT
+template <typename T>
+struct RemoveReference<T&> { typedef T type; };  // NOLINT
+
+// A handy wrapper around RemoveReference that works when the argument
+// T depends on template parameters.
+#define GMOCK_REMOVE_REFERENCE_(T) \
+    typename ::testing::internal::RemoveReference<T>::type
+
+// Removes const from a type if it is a const type, otherwise leaves
+// it unchanged.  This is the same as tr1::remove_const, which is not
+// widely available yet.
+template <typename T>
+struct RemoveConst { typedef T type; };  // NOLINT
+template <typename T>
+struct RemoveConst<const T> { typedef T type; };  // NOLINT
+
+// MSVC 8.0 has a bug which causes the above definition to fail to
+// remove the const in 'const int[3]'.  The following specialization
+// works around the bug.  However, it causes trouble with gcc and thus
+// needs to be conditionally compiled.
+#ifdef _MSC_VER
+template <typename T, size_t N>
+struct RemoveConst<T[N]> {
+  typedef typename RemoveConst<T>::type type[N];
+};
+#endif  // _MSC_VER
+
+// A handy wrapper around RemoveConst that works when the argument
+// T depends on template parameters.
+#define GMOCK_REMOVE_CONST_(T) \
+    typename ::testing::internal::RemoveConst<T>::type
+
+// Adds reference to a type if it is not a reference type,
+// otherwise leaves it unchanged.  This is the same as
+// tr1::add_reference, which is not widely available yet.
+template <typename T>
+struct AddReference { typedef T& type; };  // NOLINT
+template <typename T>
+struct AddReference<T&> { typedef T& type; };  // NOLINT
+
+// A handy wrapper around AddReference that works when the argument T
+// depends on template parameters.
+#define GMOCK_ADD_REFERENCE_(T) \
+    typename ::testing::internal::AddReference<T>::type
+
+// Adds a reference to const on top of T as necessary.  For example,
+// it transforms
+//
+//   char         ==> const char&
+//   const char   ==> const char&
+//   char&        ==> const char&
+//   const char&  ==> const char&
+//
+// The argument T must depend on some template parameters.
+#define GMOCK_REFERENCE_TO_CONST_(T) \
+    GMOCK_ADD_REFERENCE_(const GMOCK_REMOVE_REFERENCE_(T))
+
+// PointeeOf<Pointer>::type is the type of a value pointed to by a
+// Pointer, which can be either a smart pointer or a raw pointer.  The
+// following default implementation is for the case where Pointer is a
+// smart pointer.
+template <typename Pointer>
+struct PointeeOf {
+  // Smart pointer classes define type element_type as the type of
+  // their pointees.
+  typedef typename Pointer::element_type type;
+};
+// This specialization is for the raw pointer case.
+template <typename T>
+struct PointeeOf<T*> { typedef T type; };  // NOLINT
+
+// GetRawPointer(p) returns the raw pointer underlying p when p is a
+// smart pointer, or returns p itself when p is already a raw pointer.
+// The following default implementation is for the smart pointer case.
+template <typename Pointer>
+inline typename Pointer::element_type* GetRawPointer(const Pointer& p) {
+  return p.get();
+}
+// This overloaded version is for the raw pointer case.
+template <typename Element>
+inline Element* GetRawPointer(Element* p) { return p; }
+
+// This comparator allows linked_ptr to be stored in sets.
+template <typename T>
+struct LinkedPtrLessThan {
+  bool operator()(const ::testing::internal::linked_ptr<T>& lhs,
+                  const ::testing::internal::linked_ptr<T>& rhs) const {
+    return lhs.get() < rhs.get();
+  }
+};
+
+// ImplicitlyConvertible<From, To>::value is a compile-time bool
+// constant that's true iff type From can be implicitly converted to
+// type To.
+template <typename From, typename To>
+class ImplicitlyConvertible {
+ private:
+  // We need the following helper functions only for their types.
+  // They have no implementations.
+
+  // MakeFrom() is an expression whose type is From.  We cannot simply
+  // use From(), as the type From may not have a public default
+  // constructor.
+  static From MakeFrom();
+
+  // These two functions are overloaded.  Given an expression
+  // Helper(x), the compiler will pick the first version if x can be
+  // implicitly converted to type To; otherwise it will pick the
+  // second version.
+  //
+  // The first version returns a value of size 1, and the second
+  // version returns a value of size 2.  Therefore, by checking the
+  // size of Helper(x), which can be done at compile time, we can tell
+  // which version of Helper() is used, and hence whether x can be
+  // implicitly converted to type To.
+  static char Helper(To);
+  static char (&Helper(...))[2];  // NOLINT
+
+  // We have to put the 'public' section after the 'private' section,
+  // or MSVC refuses to compile the code.
+ public:
+  // MSVC warns about implicitly converting from double to int for
+  // possible loss of data, so we need to temporarily disable the
+  // warning.
+#ifdef _MSC_VER
+#pragma warning(push)          // Saves the current warning state.
+#pragma warning(disable:4244)  // Temporarily disables warning 4244.
+  static const bool value =
+      sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1;
+#pragma warning(pop)           // Restores the warning state.
+#else
+  static const bool value =
+      sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1;
+#endif  // _MSV_VER
+};
+template <typename From, typename To>
+const bool ImplicitlyConvertible<From, To>::value;
+
+// Symbian compilation can be done with wchar_t being either a native
+// type or a typedef.  Using Google Mock with OpenC without wchar_t
+// should require the definition of _STLP_NO_WCHAR_T.
+//
+// MSVC treats wchar_t as a native type usually, but treats it as the
+// same as unsigned short when the compiler option /Zc:wchar_t- is
+// specified.  It defines _NATIVE_WCHAR_T_DEFINED symbol when wchar_t
+// is a native type.
+#if (GTEST_OS_SYMBIAN && defined(_STLP_NO_WCHAR_T)) || \
+    (defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED))
+// wchar_t is a typedef.
+#else
+#define GMOCK_WCHAR_T_IS_NATIVE_ 1
+#endif
+
+// signed wchar_t and unsigned wchar_t are NOT in the C++ standard.
+// Using them is a bad practice and not portable.  So DON'T use them.
+//
+// Still, Google Mock is designed to work even if the user uses signed
+// wchar_t or unsigned wchar_t (obviously, assuming the compiler
+// supports them).
+//
+// To gcc,
+//   wchar_t == signed wchar_t != unsigned wchar_t == unsigned int
+#ifdef __GNUC__
+#define GMOCK_HAS_SIGNED_WCHAR_T_ 1  // signed/unsigned wchar_t are valid types.
+#endif
+
+// In what follows, we use the term "kind" to indicate whether a type
+// is bool, an integer type (excluding bool), a floating-point type,
+// or none of them.  This categorization is useful for determining
+// when a matcher argument type can be safely converted to another
+// type in the implementation of SafeMatcherCast.
+enum TypeKind {
+  kBool, kInteger, kFloatingPoint, kOther
+};
+
+// KindOf<T>::value is the kind of type T.
+template <typename T> struct KindOf {
+  enum { value = kOther };  // The default kind.
+};
+
+// This macro declares that the kind of 'type' is 'kind'.
+#define GMOCK_DECLARE_KIND_(type, kind) \
+  template <> struct KindOf<type> { enum { value = kind }; }
+
+GMOCK_DECLARE_KIND_(bool, kBool);
+
+// All standard integer types.
+GMOCK_DECLARE_KIND_(char, kInteger);
+GMOCK_DECLARE_KIND_(signed char, kInteger);
+GMOCK_DECLARE_KIND_(unsigned char, kInteger);
+GMOCK_DECLARE_KIND_(short, kInteger);  // NOLINT
+GMOCK_DECLARE_KIND_(unsigned short, kInteger);  // NOLINT
+GMOCK_DECLARE_KIND_(int, kInteger);
+GMOCK_DECLARE_KIND_(unsigned int, kInteger);
+GMOCK_DECLARE_KIND_(long, kInteger);  // NOLINT
+GMOCK_DECLARE_KIND_(unsigned long, kInteger);  // NOLINT
+
+#if GMOCK_WCHAR_T_IS_NATIVE_
+GMOCK_DECLARE_KIND_(wchar_t, kInteger);
+#endif
+
+// Non-standard integer types.
+GMOCK_DECLARE_KIND_(Int64, kInteger);
+GMOCK_DECLARE_KIND_(UInt64, kInteger);
+
+// All standard floating-point types.
+GMOCK_DECLARE_KIND_(float, kFloatingPoint);
+GMOCK_DECLARE_KIND_(double, kFloatingPoint);
+GMOCK_DECLARE_KIND_(long double, kFloatingPoint);
+
+#undef GMOCK_DECLARE_KIND_
+
+// Evaluates to the kind of 'type'.
+#define GMOCK_KIND_OF_(type) \
+  static_cast< ::testing::internal::TypeKind>( \
+      ::testing::internal::KindOf<type>::value)
+
+// Evaluates to true iff integer type T is signed.
+#define GMOCK_IS_SIGNED_(T) (static_cast<T>(-1) < 0)
+
+// LosslessArithmeticConvertibleImpl<kFromKind, From, kToKind, To>::value
+// is true iff arithmetic type From can be losslessly converted to
+// arithmetic type To.
+//
+// It's the user's responsibility to ensure that both From and To are
+// raw (i.e. has no CV modifier, is not a pointer, and is not a
+// reference) built-in arithmetic types, kFromKind is the kind of
+// From, and kToKind is the kind of To; the value is
+// implementation-defined when the above pre-condition is violated.
+template <TypeKind kFromKind, typename From, TypeKind kToKind, typename To>
+struct LosslessArithmeticConvertibleImpl : public false_type {};
+
+// Converting bool to bool is lossless.
+template <>
+struct LosslessArithmeticConvertibleImpl<kBool, bool, kBool, bool>
+    : public true_type {};  // NOLINT
+
+// Converting bool to any integer type is lossless.
+template <typename To>
+struct LosslessArithmeticConvertibleImpl<kBool, bool, kInteger, To>
+    : public true_type {};  // NOLINT
+
+// Converting bool to any floating-point type is lossless.
+template <typename To>
+struct LosslessArithmeticConvertibleImpl<kBool, bool, kFloatingPoint, To>
+    : public true_type {};  // NOLINT
+
+// Converting an integer to bool is lossy.
+template <typename From>
+struct LosslessArithmeticConvertibleImpl<kInteger, From, kBool, bool>
+    : public false_type {};  // NOLINT
+
+// Converting an integer to another non-bool integer is lossless iff
+// the target type's range encloses the source type's range.
+template <typename From, typename To>
+struct LosslessArithmeticConvertibleImpl<kInteger, From, kInteger, To>
+    : public bool_constant<
+      // When converting from a smaller size to a larger size, we are
+      // fine as long as we are not converting from signed to unsigned.
+      ((sizeof(From) < sizeof(To)) &&
+       (!GMOCK_IS_SIGNED_(From) || GMOCK_IS_SIGNED_(To))) ||
+      // When converting between the same size, the signedness must match.
+      ((sizeof(From) == sizeof(To)) &&
+       (GMOCK_IS_SIGNED_(From) == GMOCK_IS_SIGNED_(To)))> {};  // NOLINT
+
+#undef GMOCK_IS_SIGNED_
+
+// Converting an integer to a floating-point type may be lossy, since
+// the format of a floating-point number is implementation-defined.
+template <typename From, typename To>
+struct LosslessArithmeticConvertibleImpl<kInteger, From, kFloatingPoint, To>
+    : public false_type {};  // NOLINT
+
+// Converting a floating-point to bool is lossy.
+template <typename From>
+struct LosslessArithmeticConvertibleImpl<kFloatingPoint, From, kBool, bool>
+    : public false_type {};  // NOLINT
+
+// Converting a floating-point to an integer is lossy.
+template <typename From, typename To>
+struct LosslessArithmeticConvertibleImpl<kFloatingPoint, From, kInteger, To>
+    : public false_type {};  // NOLINT
+
+// Converting a floating-point to another floating-point is lossless
+// iff the target type is at least as big as the source type.
+template <typename From, typename To>
+struct LosslessArithmeticConvertibleImpl<
+  kFloatingPoint, From, kFloatingPoint, To>
+    : public bool_constant<sizeof(From) <= sizeof(To)> {};  // NOLINT
+
+// LosslessArithmeticConvertible<From, To>::value is true iff arithmetic
+// type From can be losslessly converted to arithmetic type To.
+//
+// It's the user's responsibility to ensure that both From and To are
+// raw (i.e. has no CV modifier, is not a pointer, and is not a
+// reference) built-in arithmetic types; the value is
+// implementation-defined when the above pre-condition is violated.
+template <typename From, typename To>
+struct LosslessArithmeticConvertible
+    : public LosslessArithmeticConvertibleImpl<
+  GMOCK_KIND_OF_(From), From, GMOCK_KIND_OF_(To), To> {};  // NOLINT
+
+// IsAProtocolMessage<T>::value is a compile-time bool constant that's
+// true iff T is type ProtocolMessage, proto2::Message, or a subclass
+// of those.
+template <typename T>
+struct IsAProtocolMessage
+    : public bool_constant<
+  ImplicitlyConvertible<const T*, const ::ProtocolMessage*>::value ||
+  ImplicitlyConvertible<const T*, const ::proto2::Message*>::value> {
+};
+
+// When the compiler sees expression IsContainerTest<C>(0), the first
+// overload of IsContainerTest will be picked if C is an STL-style
+// container class (since C::const_iterator* is a valid type and 0 can
+// be converted to it), while the second overload will be picked
+// otherwise (since C::const_iterator will be an invalid type in this
+// case).  Therefore, we can determine whether C is a container class
+// by checking the type of IsContainerTest<C>(0).  The value of the
+// expression is insignificant.
+typedef int IsContainer;
+template <class C>
+IsContainer IsContainerTest(typename C::const_iterator*) { return 0; }
+
+typedef char IsNotContainer;
+template <class C>
+IsNotContainer IsContainerTest(...) { return '\0'; }
+
+// This interface knows how to report a Google Mock failure (either
+// non-fatal or fatal).
+class FailureReporterInterface {
+ public:
+  // The type of a failure (either non-fatal or fatal).
+  enum FailureType {
+    NONFATAL, FATAL
+  };
+
+  virtual ~FailureReporterInterface() {}
+
+  // Reports a failure that occurred at the given source file location.
+  virtual void ReportFailure(FailureType type, const char* file, int line,
+                             const string& message) = 0;
+};
+
+// Returns the failure reporter used by Google Mock.
+FailureReporterInterface* GetFailureReporter();
+
+// Asserts that condition is true; aborts the process with the given
+// message if condition is false.  We cannot use LOG(FATAL) or CHECK()
+// as Google Mock might be used to mock the log sink itself.  We
+// inline this function to prevent it from showing up in the stack
+// trace.
+inline void Assert(bool condition, const char* file, int line,
+                   const string& msg) {
+  if (!condition) {
+    GetFailureReporter()->ReportFailure(FailureReporterInterface::FATAL,
+                                        file, line, msg);
+  }
+}
+inline void Assert(bool condition, const char* file, int line) {
+  Assert(condition, file, line, "Assertion failed.");
+}
+
+// Verifies that condition is true; generates a non-fatal failure if
+// condition is false.
+inline void Expect(bool condition, const char* file, int line,
+                   const string& msg) {
+  if (!condition) {
+    GetFailureReporter()->ReportFailure(FailureReporterInterface::NONFATAL,
+                                        file, line, msg);
+  }
+}
+inline void Expect(bool condition, const char* file, int line) {
+  Expect(condition, file, line, "Expectation failed.");
+}
+
+// Severity level of a log.
+enum LogSeverity {
+  INFO = 0,
+  WARNING = 1,
+};
+
+// Valid values for the --gmock_verbose flag.
+
+// All logs (informational and warnings) are printed.
+const char kInfoVerbosity[] = "info";
+// Only warnings are printed.
+const char kWarningVerbosity[] = "warning";
+// No logs are printed.
+const char kErrorVerbosity[] = "error";
+
+// Returns true iff a log with the given severity is visible according
+// to the --gmock_verbose flag.
+bool LogIsVisible(LogSeverity severity);
+
+// Prints the given message to stdout iff 'severity' >= the level
+// specified by the --gmock_verbose flag.  If stack_frames_to_skip >=
+// 0, also prints the stack trace excluding the top
+// stack_frames_to_skip frames.  In opt mode, any positive
+// stack_frames_to_skip is treated as 0, since we don't know which
+// function calls will be inlined by the compiler and need to be
+// conservative.
+void Log(LogSeverity severity, const string& message, int stack_frames_to_skip);
+
+// TODO(wan@google.com): group all type utilities together.
+
+// Type traits.
+
+// is_reference<T>::value is non-zero iff T is a reference type.
+template <typename T> struct is_reference : public false_type {};
+template <typename T> struct is_reference<T&> : public true_type {};
+
+// type_equals<T1, T2>::value is non-zero iff T1 and T2 are the same type.
+template <typename T1, typename T2> struct type_equals : public false_type {};
+template <typename T> struct type_equals<T, T> : public true_type {};
+
+// remove_reference<T>::type removes the reference from type T, if any.
+template <typename T> struct remove_reference { typedef T type; };  // NOLINT
+template <typename T> struct remove_reference<T&> { typedef T type; }; // NOLINT
+
+// Invalid<T>() returns an invalid value of type T.  This is useful
+// when a value of type T is needed for compilation, but the statement
+// will not really be executed (or we don't care if the statement
+// crashes).
+template <typename T>
+inline T Invalid() {
+  return *static_cast<typename remove_reference<T>::type*>(NULL);
+}
+template <>
+inline void Invalid<void>() {}
+
+// Utilities for native arrays.
+
+// ArrayEq() compares two k-dimensional native arrays using the
+// elements' operator==, where k can be any integer >= 0.  When k is
+// 0, ArrayEq() degenerates into comparing a single pair of values.
+
+template <typename T, typename U>
+bool ArrayEq(const T* lhs, size_t size, const U* rhs);
+
+// This generic version is used when k is 0.
+template <typename T, typename U>
+inline bool ArrayEq(const T& lhs, const U& rhs) { return lhs == rhs; }
+
+// This overload is used when k >= 1.
+template <typename T, typename U, size_t N>
+inline bool ArrayEq(const T(&lhs)[N], const U(&rhs)[N]) {
+  return internal::ArrayEq(lhs, N, rhs);
+}
+
+// This helper reduces code bloat.  If we instead put its logic inside
+// the previous ArrayEq() function, arrays with different sizes would
+// lead to different copies of the template code.
+template <typename T, typename U>
+bool ArrayEq(const T* lhs, size_t size, const U* rhs) {
+  for (size_t i = 0; i != size; i++) {
+    if (!internal::ArrayEq(lhs[i], rhs[i]))
+      return false;
+  }
+  return true;
+}
+
+// Finds the first element in the iterator range [begin, end) that
+// equals elem.  Element may be a native array type itself.
+template <typename Iter, typename Element>
+Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) {
+  for (Iter it = begin; it != end; ++it) {
+    if (internal::ArrayEq(*it, elem))
+      return it;
+  }
+  return end;
+}
+
+// CopyArray() copies a k-dimensional native array using the elements'
+// operator=, where k can be any integer >= 0.  When k is 0,
+// CopyArray() degenerates into copying a single value.
+
+template <typename T, typename U>
+void CopyArray(const T* from, size_t size, U* to);
+
+// This generic version is used when k is 0.
+template <typename T, typename U>
+inline void CopyArray(const T& from, U* to) { *to = from; }
+
+// This overload is used when k >= 1.
+template <typename T, typename U, size_t N>
+inline void CopyArray(const T(&from)[N], U(*to)[N]) {
+  internal::CopyArray(from, N, *to);
+}
+
+// This helper reduces code bloat.  If we instead put its logic inside
+// the previous CopyArray() function, arrays with different sizes
+// would lead to different copies of the template code.
+template <typename T, typename U>
+void CopyArray(const T* from, size_t size, U* to) {
+  for (size_t i = 0; i != size; i++) {
+    internal::CopyArray(from[i], to + i);
+  }
+}
+
+// The relation between an NativeArray object (see below) and the
+// native array it represents.
+enum RelationToSource {
+  kReference,  // The NativeArray references the native array.
+  kCopy        // The NativeArray makes a copy of the native array and
+               // owns the copy.
+};
+
+// Adapts a native array to a read-only STL-style container.  Instead
+// of the complete STL container concept, this adaptor only implements
+// members useful for Google Mock's container matchers.  New members
+// should be added as needed.  To simplify the implementation, we only
+// support Element being a raw type (i.e. having no top-level const or
+// reference modifier).  It's the client's responsibility to satisfy
+// this requirement.  Element can be an array type itself (hence
+// multi-dimensional arrays are supported).
+template <typename Element>
+class NativeArray {
+ public:
+  // STL-style container typedefs.
+  typedef Element value_type;
+  typedef const Element* const_iterator;
+
+  // Constructs from a native array.
+  NativeArray(const Element* array, size_t count, RelationToSource relation) {
+    Init(array, count, relation);
+  }
+
+  // Copy constructor.
+  NativeArray(const NativeArray& rhs) {
+    Init(rhs.array_, rhs.size_, rhs.relation_to_source_);
+  }
+
+  ~NativeArray() {
+    // Ensures that the user doesn't instantiate NativeArray with a
+    // const or reference type.
+    testing::StaticAssertTypeEq<Element,
+        GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Element))>();
+    if (relation_to_source_ == kCopy)
+      delete[] array_;
+  }
+
+  // STL-style container methods.
+  size_t size() const { return size_; }
+  const_iterator begin() const { return array_; }
+  const_iterator end() const { return array_ + size_; }
+  bool operator==(const NativeArray& rhs) const {
+    return size() == rhs.size() &&
+        ArrayEq(begin(), size(), rhs.begin());
+  }
+
+ private:
+  // Not implemented as we don't want to support assignment.
+  void operator=(const NativeArray& rhs);
+
+  // Initializes this object; makes a copy of the input array if
+  // 'relation' is kCopy.
+  void Init(const Element* array, size_t size, RelationToSource relation) {
+    if (relation == kReference) {
+      array_ = array;
+    } else {
+      Element* const copy = new Element[size];
+      CopyArray(array, size, copy);
+      array_ = copy;
+    }
+    size_ = size;
+    relation_to_source_ = relation;
+  }
+
+  const Element* array_;
+  size_t size_;
+  RelationToSource relation_to_source_;
+};
+
+// Given a raw type (i.e. having no top-level reference or const
+// modifier) RawContainer that's either an STL-style container or a
+// native array, class StlContainerView<RawContainer> has the
+// following members:
+//
+//   - type is a type that provides an STL-style container view to
+//     (i.e. implements the STL container concept for) RawContainer;
+//   - const_reference is a type that provides a reference to a const
+//     RawContainer;
+//   - ConstReference(raw_container) returns a const reference to an STL-style
+//     container view to raw_container, which is a RawContainer.
+//   - Copy(raw_container) returns an STL-style container view of a
+//     copy of raw_container, which is a RawContainer.
+//
+// This generic version is used when RawContainer itself is already an
+// STL-style container.
+template <class RawContainer>
+class StlContainerView {
+ public:
+  typedef RawContainer type;
+  typedef const type& const_reference;
+
+  static const_reference ConstReference(const RawContainer& container) {
+    // Ensures that RawContainer is not a const type.
+    testing::StaticAssertTypeEq<RawContainer,
+        GMOCK_REMOVE_CONST_(RawContainer)>();
+    return container;
+  }
+  static type Copy(const RawContainer& container) { return container; }
+};
+
+// This specialization is used when RawContainer is a native array type.
+template <typename Element, size_t N>
+class StlContainerView<Element[N]> {
+ public:
+  typedef GMOCK_REMOVE_CONST_(Element) RawElement;
+  typedef internal::NativeArray<RawElement> type;
+  // NativeArray<T> can represent a native array either by value or by
+  // reference (selected by a constructor argument), so 'const type'
+  // can be used to reference a const native array.  We cannot
+  // 'typedef const type& const_reference' here, as that would mean
+  // ConstReference() has to return a reference to a local variable.
+  typedef const type const_reference;
+
+  static const_reference ConstReference(const Element (&array)[N]) {
+    // Ensures that Element is not a const type.
+    testing::StaticAssertTypeEq<Element, RawElement>();
+#if GTEST_OS_SYMBIAN
+    // The Nokia Symbian compiler confuses itself in template instantiation
+    // for this call without the cast to Element*:
+    // function call '[testing::internal::NativeArray<char *>].NativeArray(
+    //     {lval} const char *[4], long, testing::internal::RelationToSource)'
+    //     does not match
+    // 'testing::internal::NativeArray<char *>::NativeArray(
+    //     char *const *, unsigned int, testing::internal::RelationToSource)'
+    // (instantiating: 'testing::internal::ContainsMatcherImpl
+    //     <const char * (&)[4]>::Matches(const char * (&)[4]) const')
+    // (instantiating: 'testing::internal::StlContainerView<char *[4]>::
+    //     ConstReference(const char * (&)[4])')
+    // (and though the N parameter type is mismatched in the above explicit
+    // conversion of it doesn't help - only the conversion of the array).
+    return type(const_cast<Element*>(&array[0]), N, kReference);
+#else
+    return type(array, N, kReference);
+#endif  // GTEST_OS_SYMBIAN
+  }
+  static type Copy(const Element (&array)[N]) {
+#if GTEST_OS_SYMBIAN
+    return type(const_cast<Element*>(&array[0]), N, kCopy);
+#else
+    return type(array, N, kCopy);
+#endif  // GTEST_OS_SYMBIAN
+  }
+};
+
+// This specialization is used when RawContainer is a native array
+// represented as a (pointer, size) tuple.
+template <typename ElementPointer, typename Size>
+class StlContainerView< ::std::tr1::tuple<ElementPointer, Size> > {
+ public:
+  typedef GMOCK_REMOVE_CONST_(
+      typename internal::PointeeOf<ElementPointer>::type) RawElement;
+  typedef internal::NativeArray<RawElement> type;
+  typedef const type const_reference;
+
+  static const_reference ConstReference(
+      const ::std::tr1::tuple<ElementPointer, Size>& array) {
+    using ::std::tr1::get;
+    return type(get<0>(array), get<1>(array), kReference);
+  }
+  static type Copy(const ::std::tr1::tuple<ElementPointer, Size>& array) {
+    using ::std::tr1::get;
+    return type(get<0>(array), get<1>(array), kCopy);
+  }
+};
+
+// The following specialization prevents the user from instantiating
+// StlContainer with a reference type.
+template <typename T> class StlContainerView<T&>;
+
+}  // namespace internal
+}  // namespace testing
+
+#endif  // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
diff --git a/third_party/gmock/include/gmock/internal/gmock-port.h b/third_party/gmock/include/gmock/internal/gmock-port.h
new file mode 100644
index 0000000..649f838
--- /dev/null
+++ b/third_party/gmock/include/gmock/internal/gmock-port.h
@@ -0,0 +1,235 @@
+// 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.
+//
+// Author: vadimb@google.com (Vadim Berman)
+//
+// Low-level types and utilities for porting Google Mock to various
+// platforms.  They are subject to change without notice.  DO NOT USE
+// THEM IN USER CODE.
+
+#ifndef GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_
+#define GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_
+
+#include <assert.h>
+#include <stdlib.h>
+#include <iostream>
+
+// Most of the types needed for porting Google Mock are also required
+// for Google Test and are defined in gtest-port.h.
+#include <gtest/internal/gtest-linked_ptr.h>
+#include <gtest/internal/gtest-port.h>
+
+// To avoid conditional compilation everywhere, we make it
+// gmock-port.h's responsibility to #include the header implementing
+// tr1/tuple.  gmock-port.h does this via gtest-port.h, which is
+// guaranteed to pull in the tuple header.
+
+#if GTEST_OS_LINUX
+
+// On some platforms, <regex.h> needs someone to define size_t, and
+// won't compile otherwise.  We can #include it here as we already
+// included <stdlib.h>, which is guaranteed to define size_t through
+// <stddef.h>.
+#include <regex.h>  // NOLINT
+
+// Defines this iff Google Mock uses the enhanced POSIX regular
+// expression syntax.  This is public as it affects how a user uses
+// regular expression matchers.
+#define GMOCK_USES_POSIX_RE 1
+
+#endif  // GTEST_OS_LINUX
+
+#if defined(GMOCK_USES_PCRE) || defined(GMOCK_USES_POSIX_RE)
+// Defines this iff regular expression matchers are supported.  This
+// is public as it tells a user whether he can use regular expression
+// matchers.
+#define GMOCK_HAS_REGEX 1
+#endif  // defined(GMOCK_USES_PCRE) || defined(GMOCK_USES_POSIX_RE)
+
+namespace testing {
+namespace internal {
+
+// For MS Visual C++, check the compiler version. At least VS 2003 is
+// required to compile Google Mock.
+#if defined(_MSC_VER) && _MSC_VER < 1310
+#error "At least Visual C++ 2003 (7.1) is required to compile Google Mock."
+#endif
+
+// Use implicit_cast as a safe version of static_cast for upcasting in
+// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a
+// const Foo*).  When you use implicit_cast, the compiler checks that
+// the cast is safe.  Such explicit implicit_casts are necessary in
+// surprisingly many situations where C++ demands an exact type match
+// instead of an argument type convertable to a target type.
+//
+// The syntax for using implicit_cast is the same as for static_cast:
+//
+//   implicit_cast<ToType>(expr)
+//
+// implicit_cast would have been part of the C++ standard library,
+// but the proposal was submitted too late.  It will probably make
+// its way into the language in the future.
+template<typename To>
+inline To implicit_cast(To x) { return x; }
+
+// When you upcast (that is, cast a pointer from type Foo to type
+// SuperclassOfFoo), it's fine to use implicit_cast<>, since upcasts
+// always succeed.  When you downcast (that is, cast a pointer from
+// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because
+// how do you know the pointer is really of type SubclassOfFoo?  It
+// could be a bare Foo, or of type DifferentSubclassOfFoo.  Thus,
+// when you downcast, you should use this macro.  In debug mode, we
+// use dynamic_cast<> to double-check the downcast is legal (we die
+// if it's not).  In normal mode, we do the efficient static_cast<>
+// instead.  Thus, it's important to test in debug mode to make sure
+// the cast is legal!
+//    This is the only place in the code we should use dynamic_cast<>.
+// In particular, you SHOULDN'T be using dynamic_cast<> in order to
+// do RTTI (eg code like this:
+//    if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo);
+//    if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo);
+// You should design the code some other way not to need this.
+template<typename To, typename From>  // use like this: down_cast<T*>(foo);
+inline To down_cast(From* f) {  // so we only accept pointers
+  // Ensures that To is a sub-type of From *.  This test is here only
+  // for compile-time type checking, and has no overhead in an
+  // optimized build at run-time, as it will be optimized away
+  // completely.
+  if (false) {
+    const To to = NULL;
+    ::testing::internal::implicit_cast<From*>(to);
+  }
+
+#if GTEST_HAS_RTTI
+  assert(f == NULL || dynamic_cast<To>(f) != NULL);  // RTTI: debug mode only!
+#endif
+  return static_cast<To>(f);
+}
+
+// The GMOCK_COMPILE_ASSERT_ macro can be used to verify that a compile time
+// expression is true. For example, you could use it to verify the
+// size of a static array:
+//
+//   GMOCK_COMPILE_ASSERT_(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES,
+//                         content_type_names_incorrect_size);
+//
+// or to make sure a struct is smaller than a certain size:
+//
+//   GMOCK_COMPILE_ASSERT_(sizeof(foo) < 128, foo_too_large);
+//
+// The second argument to the macro is the name of the variable. If
+// the expression is false, most compilers will issue a warning/error
+// containing the name of the variable.
+
+template <bool>
+struct CompileAssert {
+};
+
+#define GMOCK_COMPILE_ASSERT_(expr, msg) \
+  typedef ::testing::internal::CompileAssert<(bool(expr))> \
+      msg[bool(expr) ? 1 : -1]
+
+// Implementation details of GMOCK_COMPILE_ASSERT_:
+//
+// - GMOCK_COMPILE_ASSERT_ works by defining an array type that has -1
+//   elements (and thus is invalid) when the expression is false.
+//
+// - The simpler definition
+//
+//    #define GMOCK_COMPILE_ASSERT_(expr, msg) typedef char msg[(expr) ? 1 : -1]
+//
+//   does not work, as gcc supports variable-length arrays whose sizes
+//   are determined at run-time (this is gcc's extension and not part
+//   of the C++ standard).  As a result, gcc fails to reject the
+//   following code with the simple definition:
+//
+//     int foo;
+//     GMOCK_COMPILE_ASSERT_(foo, msg); // not supposed to compile as foo is
+//                                      // not a compile-time constant.
+//
+// - By using the type CompileAssert<(bool(expr))>, we ensures that
+//   expr is a compile-time constant.  (Template arguments must be
+//   determined at compile-time.)
+//
+// - The outter parentheses in CompileAssert<(bool(expr))> are necessary
+//   to work around a bug in gcc 3.4.4 and 4.0.1.  If we had written
+//
+//     CompileAssert<bool(expr)>
+//
+//   instead, these compilers will refuse to compile
+//
+//     GMOCK_COMPILE_ASSERT_(5 > 0, some_message);
+//
+//   (They seem to think the ">" in "5 > 0" marks the end of the
+//   template argument list.)
+//
+// - The array size is (bool(expr) ? 1 : -1), instead of simply
+//
+//     ((expr) ? 1 : -1).
+//
+//   This is to avoid running into a bug in MS VC 7.1, which
+//   causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
+
+#if GTEST_HAS_GLOBAL_STRING
+typedef ::string string;
+#elif GTEST_HAS_STD_STRING
+typedef ::std::string string;
+#else
+#error "Google Mock requires ::std::string to compile."
+#endif  // GTEST_HAS_GLOBAL_STRING
+
+#if GTEST_HAS_GLOBAL_WSTRING
+typedef ::wstring wstring;
+#elif GTEST_HAS_STD_WSTRING
+typedef ::std::wstring wstring;
+#endif  // GTEST_HAS_GLOBAL_WSTRING
+
+}  // namespace internal
+}  // namespace testing
+
+// Macro for referencing flags.  This is public as we want the user to
+// use this syntax to reference Google Mock flags.
+#define GMOCK_FLAG(name) FLAGS_gmock_##name
+
+// Macros for declaring flags.
+#define GMOCK_DECLARE_bool_(name) extern bool GMOCK_FLAG(name)
+#define GMOCK_DECLARE_int32_(name) \
+    extern ::testing::internal::Int32 GMOCK_FLAG(name)
+#define GMOCK_DECLARE_string_(name) \
+    extern ::testing::internal::String GMOCK_FLAG(name)
+
+// Macros for defining flags.
+#define GMOCK_DEFINE_bool_(name, default_val, doc) \
+    bool GMOCK_FLAG(name) = (default_val)
+#define GMOCK_DEFINE_int32_(name, default_val, doc) \
+    ::testing::internal::Int32 GMOCK_FLAG(name) = (default_val)
+#define GMOCK_DEFINE_string_(name, default_val, doc) \
+    ::testing::internal::String GMOCK_FLAG(name) = (default_val)
+
+#endif  // GMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_
diff --git a/third_party/gmock/scripts/generator/COPYING b/third_party/gmock/scripts/generator/COPYING
new file mode 100644
index 0000000..87ea063
--- /dev/null
+++ b/third_party/gmock/scripts/generator/COPYING
@@ -0,0 +1,203 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [2007] Neal Norwitz
+   Portions Copyright [2007] Google Inc.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/third_party/gmock/scripts/generator/README b/third_party/gmock/scripts/generator/README
new file mode 100644
index 0000000..d6f9597
--- /dev/null
+++ b/third_party/gmock/scripts/generator/README
@@ -0,0 +1,35 @@
+
+The Google Mock class generator is an application that is part of cppclean.
+For more information about cppclean, see the README.cppclean file or
+visit http://code.google.com/p/cppclean/
+
+cppclean requires Python 2.3.5 or later.  If you don't have Python installed
+on your system, you will also need to install it.  You can download Python
+from:  http://www.python.org/download/releases/
+
+To use the Google Mock class generator, you need to call it
+on the command line passing the header file and class for which you want
+to generate a Google Mock class.
+
+Make sure to install the scripts somewhere in your path.  Then you can
+run the program.
+
+  gmock_gen.py header-file.h [ClassName]...
+
+If no ClassNames are specified, all classes in the file are emitted.
+
+To change the indentation from the default of 2, set INDENT in
+the environment.  For example to use an indent of 4 spaces:
+
+INDENT=4 gmock_gen.py header-file.h ClassName
+
+This version was made from SVN revision 281 in the cppclean repository.
+
+Known Limitations
+-----------------
+Not all code will be generated properly.  For example, when mocking templated
+classes, the template information is lost.  You will need to add the template
+information manually.
+
+Not all permutations of using multiple pointers/references will be rendered
+properly.  These will also have to be fixed manually.
diff --git a/third_party/gmock/scripts/generator/README.cppclean b/third_party/gmock/scripts/generator/README.cppclean
new file mode 100644
index 0000000..65431b6
--- /dev/null
+++ b/third_party/gmock/scripts/generator/README.cppclean
@@ -0,0 +1,115 @@
+Goal:
+-----
+  CppClean attempts to find problems in C++ source that slow development
+  in large code bases, for example various forms of unused code.
+  Unused code can be unused functions, methods, data members, types, etc
+  to unnecessary #include directives.  Unnecessary #includes can cause
+  considerable extra compiles increasing the edit-compile-run cycle.
+
+  The project home page is:   http://code.google.com/p/cppclean/
+
+
+Features:
+---------
+ * Find and print C++ language constructs: classes, methods, functions, etc.
+ * Find classes with virtual methods, no virtual destructor, and no bases
+ * Find global/static data that are potential problems when using threads
+ * Unnecessary forward class declarations
+ * Unnecessary function declarations
+ * Undeclared function definitions
+ * (planned) Find unnecessary header files #included
+   - No direct reference to anything in the header
+   - Header is unnecessary if classes were forward declared instead
+ * (planned) Source files that reference headers not directly #included,
+   ie, files that rely on a transitive #include from another header
+ * (planned) Unused members (private, protected, & public) methods and data
+ * (planned) Store AST in a SQL database so relationships can be queried
+
+AST is Abstract Syntax Tree, a representation of parsed source code.
+http://en.wikipedia.org/wiki/Abstract_syntax_tree
+
+
+System Requirements:
+--------------------
+ * Python 2.4 or later (2.3 probably works too)
+ * Works on Windows (untested), Mac OS X, and Unix
+
+
+How to Run:
+-----------
+  For all examples, it is assumed that cppclean resides in a directory called
+  /cppclean.
+
+  To print warnings for classes with virtual methods, no virtual destructor and
+  no base classes:
+
+      /cppclean/run.sh nonvirtual_dtors.py file1.h file2.h file3.cc ...
+
+  To print all the functions defined in header file(s):
+
+      /cppclean/run.sh functions.py file1.h file2.h ...
+
+  All the commands take multiple files on the command line.  Other programs
+  include: find_warnings, headers, methods, and types.  Some other programs
+  are available, but used primarily for debugging.
+
+  run.sh is a simple wrapper that sets PYTHONPATH to /cppclean and then
+  runs the program in /cppclean/cpp/PROGRAM.py.  There is currently
+  no equivalent for Windows.  Contributions for a run.bat file
+  would be greatly appreciated.
+
+
+How to Configure:
+-----------------
+  You can add a siteheaders.py file in /cppclean/cpp to configure where
+  to look for other headers (typically -I options passed to a compiler).
+  Currently two values are supported:  _TRANSITIVE and GetIncludeDirs.
+  _TRANSITIVE should be set to a boolean value (True or False) indicating
+  whether to transitively process all header files.  The default is False.
+
+  GetIncludeDirs is a function that takes a single argument and returns
+  a sequence of directories to include.  This can be a generator or
+  return a static list.
+
+      def GetIncludeDirs(filename):
+          return ['/some/path/with/other/headers']
+
+      # Here is a more complicated example.
+      def GetIncludeDirs(filename):
+          yield '/path1'
+          yield os.path.join('/path2', os.path.dirname(filename))
+          yield '/path3'
+
+
+How to Test:
+------------
+  For all examples, it is assumed that cppclean resides in a directory called
+  /cppclean.  The tests require
+
+  cd /cppclean
+  make test
+  # To generate expected results after a change:
+  make expected
+
+
+Current Status:
+---------------
+  The parser works pretty well for header files, parsing about 99% of Google's
+  header files.  Anything which inspects structure of C++ source files should
+  work reasonably well.  Function bodies are not transformed to an AST,
+  but left as tokens.  Much work is still needed on finding unused header files
+  and storing an AST in a database.
+
+
+Non-goals:
+----------
+ * Parsing all valid C++ source
+ * Handling invalid C++ source gracefully
+ * Compiling to machine code (or anything beyond an AST)
+
+
+Contact:
+--------
+  If you used cppclean, I would love to hear about your experiences
+  cppclean@googlegroups.com.  Even if you don't use cppclean, I'd like to
+  hear from you.  :-)  (You can contact me directly at:  nnorwitz@gmail.com)
diff --git a/third_party/gmock/scripts/generator/cpp/__init__.py b/third_party/gmock/scripts/generator/cpp/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/gmock/scripts/generator/cpp/__init__.py
diff --git a/third_party/gmock/scripts/generator/cpp/ast.py b/third_party/gmock/scripts/generator/cpp/ast.py
new file mode 100644
index 0000000..47dc9a0
--- /dev/null
+++ b/third_party/gmock/scripts/generator/cpp/ast.py
@@ -0,0 +1,1717 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Neal Norwitz
+# Portions Copyright 2007 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Generate an Abstract Syntax Tree (AST) for C++."""
+
+__author__ = 'nnorwitz@google.com (Neal Norwitz)'
+
+
+# TODO:
+#  * Tokens should never be exported, need to convert to Nodes
+#    (return types, parameters, etc.)
+#  * Handle static class data for templatized classes
+#  * Handle casts (both C++ and C-style)
+#  * Handle conditions and loops (if/else, switch, for, while/do)
+#
+# TODO much, much later:
+#  * Handle #define
+#  * exceptions
+
+
+try:
+    # Python 3.x
+    import builtins
+except ImportError:
+    # Python 2.x
+    import __builtin__ as builtins
+
+import sys
+import traceback
+
+from cpp import keywords
+from cpp import tokenize
+from cpp import utils
+
+
+if not hasattr(builtins, 'reversed'):
+    # Support Python 2.3 and earlier.
+    def reversed(seq):
+        for i in range(len(seq)-1, -1, -1):
+            yield seq[i]
+
+if not hasattr(builtins, 'next'):
+    # Support Python 2.5 and earlier.
+    def next(obj):
+        return obj.next()
+
+
+VISIBILITY_PUBLIC, VISIBILITY_PROTECTED, VISIBILITY_PRIVATE = range(3)
+
+FUNCTION_NONE = 0x00
+FUNCTION_CONST = 0x01
+FUNCTION_VIRTUAL = 0x02
+FUNCTION_PURE_VIRTUAL = 0x04
+FUNCTION_CTOR = 0x08
+FUNCTION_DTOR = 0x10
+FUNCTION_ATTRIBUTE = 0x20
+FUNCTION_UNKNOWN_ANNOTATION = 0x40
+FUNCTION_THROW = 0x80
+
+"""
+These are currently unused.  Should really handle these properly at some point.
+
+TYPE_MODIFIER_INLINE   = 0x010000
+TYPE_MODIFIER_EXTERN   = 0x020000
+TYPE_MODIFIER_STATIC   = 0x040000
+TYPE_MODIFIER_CONST    = 0x080000
+TYPE_MODIFIER_REGISTER = 0x100000
+TYPE_MODIFIER_VOLATILE = 0x200000
+TYPE_MODIFIER_MUTABLE  = 0x400000
+
+TYPE_MODIFIER_MAP = {
+    'inline': TYPE_MODIFIER_INLINE,
+    'extern': TYPE_MODIFIER_EXTERN,
+    'static': TYPE_MODIFIER_STATIC,
+    'const': TYPE_MODIFIER_CONST,
+    'register': TYPE_MODIFIER_REGISTER,
+    'volatile': TYPE_MODIFIER_VOLATILE,
+    'mutable': TYPE_MODIFIER_MUTABLE,
+    }
+"""
+
+_INTERNAL_TOKEN = 'internal'
+_NAMESPACE_POP = 'ns-pop'
+
+
+# TODO(nnorwitz): use this as a singleton for templated_types, etc
+# where we don't want to create a new empty dict each time.  It is also const.
+class _NullDict(object):
+    __contains__ = lambda self: False
+    keys = values = items = iterkeys = itervalues = iteritems = lambda self: ()
+
+
+# TODO(nnorwitz): move AST nodes into a separate module.
+class Node(object):
+    """Base AST node."""
+
+    def __init__(self, start, end):
+        self.start = start
+        self.end = end
+
+    def IsDeclaration(self):
+        """Returns bool if this node is a declaration."""
+        return False
+
+    def IsDefinition(self):
+        """Returns bool if this node is a definition."""
+        return False
+
+    def IsExportable(self):
+        """Returns bool if this node exportable from a header file."""
+        return False
+
+    def Requires(self, node):
+        """Does this AST node require the definition of the node passed in?"""
+        return False
+
+    def XXX__str__(self):
+        return self._StringHelper(self.__class__.__name__, '')
+
+    def _StringHelper(self, name, suffix):
+        if not utils.DEBUG:
+            return '%s(%s)' % (name, suffix)
+        return '%s(%d, %d, %s)' % (name, self.start, self.end, suffix)
+
+    def __repr__(self):
+        return str(self)
+
+
+class Define(Node):
+    def __init__(self, start, end, name, definition):
+        Node.__init__(self, start, end)
+        self.name = name
+        self.definition = definition
+
+    def __str__(self):
+        value = '%s %s' % (self.name, self.definition)
+        return self._StringHelper(self.__class__.__name__, value)
+
+
+class Include(Node):
+    def __init__(self, start, end, filename, system):
+        Node.__init__(self, start, end)
+        self.filename = filename
+        self.system = system
+
+    def __str__(self):
+        fmt = '"%s"'
+        if self.system:
+            fmt = '<%s>'
+        return self._StringHelper(self.__class__.__name__, fmt % self.filename)
+
+
+class Goto(Node):
+    def __init__(self, start, end, label):
+        Node.__init__(self, start, end)
+        self.label = label
+
+    def __str__(self):
+        return self._StringHelper(self.__class__.__name__, str(self.label))
+
+
+class Expr(Node):
+    def __init__(self, start, end, expr):
+        Node.__init__(self, start, end)
+        self.expr = expr
+
+    def Requires(self, node):
+        # TODO(nnorwitz): impl.
+        return False
+
+    def __str__(self):
+        return self._StringHelper(self.__class__.__name__, str(self.expr))
+
+
+class Return(Expr):
+    pass
+
+
+class Delete(Expr):
+    pass
+
+
+class Friend(Expr):
+    def __init__(self, start, end, expr, namespace):
+        Expr.__init__(self, start, end, expr)
+        self.namespace = namespace[:]
+
+
+class Using(Node):
+    def __init__(self, start, end, names):
+        Node.__init__(self, start, end)
+        self.names = names
+
+    def __str__(self):
+        return self._StringHelper(self.__class__.__name__, str(self.names))
+
+
+class Parameter(Node):
+    def __init__(self, start, end, name, parameter_type, default):
+        Node.__init__(self, start, end)
+        self.name = name
+        self.type = parameter_type
+        self.default = default
+
+    def Requires(self, node):
+        # TODO(nnorwitz): handle namespaces, etc.
+        return self.type.name == node.name
+
+    def __str__(self):
+        name = str(self.type)
+        suffix = '%s %s' % (name, self.name)
+        if self.default:
+            suffix += ' = ' + ''.join([d.name for d in self.default])
+        return self._StringHelper(self.__class__.__name__, suffix)
+
+
+class _GenericDeclaration(Node):
+    def __init__(self, start, end, name, namespace):
+        Node.__init__(self, start, end)
+        self.name = name
+        self.namespace = namespace[:]
+
+    def FullName(self):
+        prefix = ''
+        if self.namespace and self.namespace[-1]:
+            prefix = '::'.join(self.namespace) + '::'
+        return prefix + self.name
+
+    def _TypeStringHelper(self, suffix):
+        if self.namespace:
+            names = [n or '<anonymous>' for n in self.namespace]
+            suffix += ' in ' + '::'.join(names)
+        return self._StringHelper(self.__class__.__name__, suffix)
+
+
+# TODO(nnorwitz): merge with Parameter in some way?
+class VariableDeclaration(_GenericDeclaration):
+    def __init__(self, start, end, name, var_type, initial_value, namespace):
+        _GenericDeclaration.__init__(self, start, end, name, namespace)
+        self.type = var_type
+        self.initial_value = initial_value
+
+    def Requires(self, node):
+        # TODO(nnorwitz): handle namespaces, etc.
+        return self.type.name == node.name
+
+    def ToString(self):
+        """Return a string that tries to reconstitute the variable decl."""
+        suffix = '%s %s' % (self.type, self.name)
+        if self.initial_value:
+            suffix += ' = ' + self.initial_value
+        return suffix
+
+    def __str__(self):
+        return self._StringHelper(self.__class__.__name__, self.ToString())
+
+
+class Typedef(_GenericDeclaration):
+    def __init__(self, start, end, name, alias, namespace):
+        _GenericDeclaration.__init__(self, start, end, name, namespace)
+        self.alias = alias
+
+    def IsDefinition(self):
+        return True
+
+    def IsExportable(self):
+        return True
+
+    def Requires(self, node):
+        # TODO(nnorwitz): handle namespaces, etc.
+        name = node.name
+        for token in self.alias:
+            if token is not None and name == token.name:
+                return True
+        return False
+
+    def __str__(self):
+        suffix = '%s, %s' % (self.name, self.alias)
+        return self._TypeStringHelper(suffix)
+
+
+class _NestedType(_GenericDeclaration):
+    def __init__(self, start, end, name, fields, namespace):
+        _GenericDeclaration.__init__(self, start, end, name, namespace)
+        self.fields = fields
+
+    def IsDefinition(self):
+        return True
+
+    def IsExportable(self):
+        return True
+
+    def __str__(self):
+        suffix = '%s, {%s}' % (self.name, self.fields)
+        return self._TypeStringHelper(suffix)
+
+
+class Union(_NestedType):
+    pass
+
+
+class Enum(_NestedType):
+    pass
+
+
+class Class(_GenericDeclaration):
+    def __init__(self, start, end, name, bases, templated_types, body, namespace):
+        _GenericDeclaration.__init__(self, start, end, name, namespace)
+        self.bases = bases
+        self.body = body
+        self.templated_types = templated_types
+
+    def IsDeclaration(self):
+        return self.bases is None and self.body is None
+
+    def IsDefinition(self):
+        return not self.IsDeclaration()
+
+    def IsExportable(self):
+        return not self.IsDeclaration()
+
+    def Requires(self, node):
+        # TODO(nnorwitz): handle namespaces, etc.
+        if self.bases:
+            for token_list in self.bases:
+                # TODO(nnorwitz): bases are tokens, do name comparision.
+                for token in token_list:
+                    if token.name == node.name:
+                        return True
+        # TODO(nnorwitz): search in body too.
+        return False
+
+    def __str__(self):
+        name = self.name
+        if self.templated_types:
+            name += '<%s>' % self.templated_types
+        suffix = '%s, %s, %s' % (name, self.bases, self.body)
+        return self._TypeStringHelper(suffix)
+
+
+class Struct(Class):
+    pass
+
+
+class Function(_GenericDeclaration):
+    def __init__(self, start, end, name, return_type, parameters,
+                 modifiers, templated_types, body, namespace):
+        _GenericDeclaration.__init__(self, start, end, name, namespace)
+        converter = TypeConverter(namespace)
+        self.return_type = converter.CreateReturnType(return_type)
+        self.parameters = converter.ToParameters(parameters)
+        self.modifiers = modifiers
+        self.body = body
+        self.templated_types = templated_types
+
+    def IsDeclaration(self):
+        return self.body is None
+
+    def IsDefinition(self):
+        return self.body is not None
+
+    def IsExportable(self):
+        if self.return_type and 'static' in self.return_type.modifiers:
+            return False
+        return None not in self.namespace
+
+    def Requires(self, node):
+        if self.parameters:
+            # TODO(nnorwitz): parameters are tokens, do name comparision.
+            for p in self.parameters:
+                if p.name == node.name:
+                    return True
+        # TODO(nnorwitz): search in body too.
+        return False
+
+    def __str__(self):
+        # TODO(nnorwitz): add templated_types.
+        suffix = ('%s %s(%s), 0x%02x, %s' %
+                  (self.return_type, self.name, self.parameters,
+                   self.modifiers, self.body))
+        return self._TypeStringHelper(suffix)
+
+
+class Method(Function):
+    def __init__(self, start, end, name, in_class, return_type, parameters,
+                 modifiers, templated_types, body, namespace):
+        Function.__init__(self, start, end, name, return_type, parameters,
+                          modifiers, templated_types, body, namespace)
+        # TODO(nnorwitz): in_class could also be a namespace which can
+        # mess up finding functions properly.
+        self.in_class = in_class
+
+
+class Type(_GenericDeclaration):
+    """Type used for any variable (eg class, primitive, struct, etc)."""
+
+    def __init__(self, start, end, name, templated_types, modifiers,
+                 reference, pointer, array):
+        """
+        Args:
+          name: str name of main type
+          templated_types: [Class (Type?)] template type info between <>
+          modifiers: [str] type modifiers (keywords) eg, const, mutable, etc.
+          reference, pointer, array: bools
+        """
+        _GenericDeclaration.__init__(self, start, end, name, [])
+        self.templated_types = templated_types
+        if not name and modifiers:
+            self.name = modifiers.pop()
+        self.modifiers = modifiers
+        self.reference = reference
+        self.pointer = pointer
+        self.array = array
+
+    def __str__(self):
+        prefix = ''
+        if self.modifiers:
+            prefix = ' '.join(self.modifiers) + ' '
+        name = str(self.name)
+        if self.templated_types:
+            name += '<%s>' % self.templated_types
+        suffix = prefix + name
+        if self.reference:
+            suffix += '&'
+        if self.pointer:
+            suffix += '*'
+        if self.array:
+            suffix += '[]'
+        return self._TypeStringHelper(suffix)
+
+    # By definition, Is* are always False.  A Type can only exist in
+    # some sort of variable declaration, parameter, or return value.
+    def IsDeclaration(self):
+        return False
+
+    def IsDefinition(self):
+        return False
+
+    def IsExportable(self):
+        return False
+
+
+class TypeConverter(object):
+
+    def __init__(self, namespace_stack):
+        self.namespace_stack = namespace_stack
+
+    def _GetTemplateEnd(self, tokens, start):
+        count = 1
+        end = start
+        while 1:
+            token = tokens[end]
+            end += 1
+            if token.name == '<':
+                count += 1
+            elif token.name == '>':
+                count -= 1
+                if count == 0:
+                    break
+        return tokens[start:end-1], end
+
+    def ToType(self, tokens):
+        """Convert [Token,...] to [Class(...), ] useful for base classes.
+        For example, code like class Foo : public Bar<x, y> { ... };
+        the "Bar<x, y>" portion gets converted to an AST.
+
+        Returns:
+          [Class(...), ...]
+        """
+        result = []
+        name_tokens = []
+        reference = pointer = array = False
+
+        def AddType(templated_types):
+            # Partition tokens into name and modifier tokens.
+            names = []
+            modifiers = []
+            for t in name_tokens:
+                if keywords.IsKeyword(t.name):
+                    modifiers.append(t.name)
+                else:
+                    names.append(t.name)
+            name = ''.join(names)
+            result.append(Type(name_tokens[0].start, name_tokens[-1].end,
+                               name, templated_types, modifiers,
+                               reference, pointer, array))
+            del name_tokens[:]
+
+        i = 0
+        end = len(tokens)
+        while i < end:
+            token = tokens[i]
+            if token.name == '<':
+                new_tokens, new_end = self._GetTemplateEnd(tokens, i+1)
+                AddType(self.ToType(new_tokens))
+                # If there is a comma after the template, we need to consume
+                # that here otherwise it becomes part of the name.
+                i = new_end
+                reference = pointer = array = False
+            elif token.name == ',':
+                AddType([])
+                reference = pointer = array = False
+            elif token.name == '*':
+                pointer = True
+            elif token.name == '&':
+                reference = True
+            elif token.name == '[':
+               pointer = True
+            elif token.name == ']':
+                pass
+            else:
+                name_tokens.append(token)
+            i += 1
+
+        if name_tokens:
+            # No '<' in the tokens, just a simple name and no template.
+            AddType([])
+        return result
+
+    def DeclarationToParts(self, parts, needs_name_removed):
+        name = None
+        default = []
+        if needs_name_removed:
+            # Handle default (initial) values properly.
+            for i, t in enumerate(parts):
+                if t.name == '=':
+                    default = parts[i+1:]
+                    name = parts[i-1].name
+                    if name == ']' and parts[i-2].name == '[':
+                        name = parts[i-3].name
+                        i -= 1
+                    parts = parts[:i-1]
+                    break
+            else:
+                if parts[-1].token_type == tokenize.NAME:
+                    name = parts.pop().name
+                else:
+                    # TODO(nnorwitz): this is a hack that happens for code like
+                    # Register(Foo<T>); where it thinks this is a function call
+                    # but it's actually a declaration.
+                    name = '???'
+        modifiers = []
+        type_name = []
+        other_tokens = []
+        templated_types = []
+        i = 0
+        end = len(parts)
+        while i < end:
+            p = parts[i]
+            if keywords.IsKeyword(p.name):
+                modifiers.append(p.name)
+            elif p.name == '<':
+                templated_tokens, new_end = self._GetTemplateEnd(parts, i+1)
+                templated_types = self.ToType(templated_tokens)
+                i = new_end - 1
+                # Don't add a spurious :: to data members being initialized.
+                next_index = i + 1
+                if next_index < end and parts[next_index].name == '::':
+                    i += 1
+            elif p.name in ('[', ']', '='):
+                # These are handled elsewhere.
+                other_tokens.append(p)
+            elif p.name not in ('*', '&', '>'):
+                # Ensure that names have a space between them.
+                if (type_name and type_name[-1].token_type == tokenize.NAME and
+                    p.token_type == tokenize.NAME):
+                    type_name.append(tokenize.Token(tokenize.SYNTAX, ' ', 0, 0))
+                type_name.append(p)
+            else:
+                other_tokens.append(p)
+            i += 1
+        type_name = ''.join([t.name for t in type_name])
+        return name, type_name, templated_types, modifiers, default, other_tokens
+
+    def ToParameters(self, tokens):
+        if not tokens:
+            return []
+
+        result = []
+        name = type_name = ''
+        type_modifiers = []
+        pointer = reference = array = False
+        first_token = None
+        default = []
+
+        def AddParameter():
+            if default:
+                del default[0]  # Remove flag.
+            end = type_modifiers[-1].end
+            parts = self.DeclarationToParts(type_modifiers, True)
+            (name, type_name, templated_types, modifiers,
+             unused_default, unused_other_tokens) = parts
+            parameter_type = Type(first_token.start, first_token.end,
+                                  type_name, templated_types, modifiers,
+                                  reference, pointer, array)
+            p = Parameter(first_token.start, end, name,
+                          parameter_type, default)
+            result.append(p)
+
+        template_count = 0
+        for s in tokens:
+            if not first_token:
+                first_token = s
+            if s.name == '<':
+                template_count += 1
+            elif s.name == '>':
+                template_count -= 1
+            if template_count > 0:
+                type_modifiers.append(s)
+                continue
+
+            if s.name == ',':
+                AddParameter()
+                name = type_name = ''
+                type_modifiers = []
+                pointer = reference = array = False
+                first_token = None
+                default = []
+            elif s.name == '*':
+                pointer = True
+            elif s.name == '&':
+                reference = True
+            elif s.name == '[':
+                array = True
+            elif s.name == ']':
+                pass  # Just don't add to type_modifiers.
+            elif s.name == '=':
+                # Got a default value.  Add any value (None) as a flag.
+                default.append(None)
+            elif default:
+                default.append(s)
+            else:
+                type_modifiers.append(s)
+        AddParameter()
+        return result
+
+    def CreateReturnType(self, return_type_seq):
+        if not return_type_seq:
+            return None
+        start = return_type_seq[0].start
+        end = return_type_seq[-1].end
+        _, name, templated_types, modifiers, default, other_tokens = \
+           self.DeclarationToParts(return_type_seq, False)
+        names = [n.name for n in other_tokens]
+        reference = '&' in names
+        pointer = '*' in names
+        array = '[' in names
+        return Type(start, end, name, templated_types, modifiers,
+                    reference, pointer, array)
+
+    def GetTemplateIndices(self, names):
+        # names is a list of strings.
+        start = names.index('<')
+        end = len(names) - 1
+        while end > 0:
+            if names[end] == '>':
+                break
+            end -= 1
+        return start, end+1
+
+class AstBuilder(object):
+    def __init__(self, token_stream, filename, in_class='', visibility=None,
+                 namespace_stack=[]):
+        self.tokens = token_stream
+        self.filename = filename
+        # TODO(nnorwitz): use a better data structure (deque) for the queue.
+        # Switching directions of the "queue" improved perf by about 25%.
+        # Using a deque should be even better since we access from both sides.
+        self.token_queue = []
+        self.namespace_stack = namespace_stack[:]
+        self.in_class = in_class
+        if in_class is None:
+            self.in_class_name_only = None
+        else:
+            self.in_class_name_only = in_class.split('::')[-1]
+        self.visibility = visibility
+        self.in_function = False
+        self.current_token = None
+        # Keep the state whether we are currently handling a typedef or not.
+        self._handling_typedef = False
+
+        self.converter = TypeConverter(self.namespace_stack)
+
+    def HandleError(self, msg, token):
+        printable_queue = list(reversed(self.token_queue[-20:]))
+        sys.stderr.write('Got %s in %s @ %s %s\n' %
+                         (msg, self.filename, token, printable_queue))
+
+    def Generate(self):
+        while 1:
+            token = self._GetNextToken()
+            if not token:
+                break
+
+            # Get the next token.
+            self.current_token = token
+
+            # Dispatch on the next token type.
+            if token.token_type == _INTERNAL_TOKEN:
+                if token.name == _NAMESPACE_POP:
+                    self.namespace_stack.pop()
+                continue
+
+            try:
+                result = self._GenerateOne(token)
+                if result is not None:
+                    yield result
+            except:
+                self.HandleError('exception', token)
+                raise
+
+    def _CreateVariable(self, pos_token, name, type_name, type_modifiers,
+                        ref_pointer_name_seq, templated_types, value=None):
+        reference = '&' in ref_pointer_name_seq
+        pointer = '*' in ref_pointer_name_seq
+        array = '[' in ref_pointer_name_seq
+        var_type = Type(pos_token.start, pos_token.end, type_name,
+                        templated_types, type_modifiers,
+                        reference, pointer, array)
+        return VariableDeclaration(pos_token.start, pos_token.end,
+                                   name, var_type, value, self.namespace_stack)
+
+    def _GenerateOne(self, token):
+        if token.token_type == tokenize.NAME:
+            if (keywords.IsKeyword(token.name) and
+                not keywords.IsBuiltinType(token.name)):
+                method = getattr(self, 'handle_' + token.name)
+                return method()
+            elif token.name == self.in_class_name_only:
+                # The token name is the same as the class, must be a ctor if
+                # there is a paren.  Otherwise, it's the return type.
+                # Peek ahead to get the next token to figure out which.
+                next = self._GetNextToken()
+                self._AddBackToken(next)
+                if next.token_type == tokenize.SYNTAX and next.name == '(':
+                    return self._GetMethod([token], FUNCTION_CTOR, None, True)
+                # Fall through--handle like any other method.
+
+            # Handle data or function declaration/definition.
+            syntax = tokenize.SYNTAX
+            temp_tokens, last_token = \
+                self._GetVarTokensUpTo(syntax, '(', ';', '{', '[')
+            temp_tokens.insert(0, token)
+            if last_token.name == '(':
+                # If there is an assignment before the paren,
+                # this is an expression, not a method.
+                expr = bool([e for e in temp_tokens if e.name == '='])
+                if expr:
+                    new_temp = self._GetTokensUpTo(tokenize.SYNTAX, ';')
+                    temp_tokens.append(last_token)
+                    temp_tokens.extend(new_temp)
+                    last_token = tokenize.Token(tokenize.SYNTAX, ';', 0, 0)
+
+            if last_token.name == '[':
+                # Handle array, this isn't a method, unless it's an operator.
+                # TODO(nnorwitz): keep the size somewhere.
+                # unused_size = self._GetTokensUpTo(tokenize.SYNTAX, ']')
+                temp_tokens.append(last_token)
+                if temp_tokens[-2].name == 'operator':
+                    temp_tokens.append(self._GetNextToken())
+                else:
+                    temp_tokens2, last_token = \
+                        self._GetVarTokensUpTo(tokenize.SYNTAX, ';')
+                    temp_tokens.extend(temp_tokens2)
+
+            if last_token.name == ';':
+                # Handle data, this isn't a method.
+                parts = self.converter.DeclarationToParts(temp_tokens, True)
+                (name, type_name, templated_types, modifiers, default,
+                 unused_other_tokens) = parts
+
+                t0 = temp_tokens[0]
+                names = [t.name for t in temp_tokens]
+                if templated_types:
+                    start, end = self.converter.GetTemplateIndices(names)
+                    names = names[:start] + names[end:]
+                default = ''.join([t.name for t in default])
+                return self._CreateVariable(t0, name, type_name, modifiers,
+                                            names, templated_types, default)
+            if last_token.name == '{':
+                self._AddBackTokens(temp_tokens[1:])
+                self._AddBackToken(last_token)
+                method_name = temp_tokens[0].name
+                method = getattr(self, 'handle_' + method_name, None)
+                if not method:
+                    # Must be declaring a variable.
+                    # TODO(nnorwitz): handle the declaration.
+                    return None
+                return method()
+            return self._GetMethod(temp_tokens, 0, None, False)
+        elif token.token_type == tokenize.SYNTAX:
+            if token.name == '~' and self.in_class:
+                # Must be a dtor (probably not in method body).
+                token = self._GetNextToken()
+                # self.in_class can contain A::Name, but the dtor will only
+                # be Name.  Make sure to compare against the right value.
+                if (token.token_type == tokenize.NAME and
+                    token.name == self.in_class_name_only):
+                    return self._GetMethod([token], FUNCTION_DTOR, None, True)
+            # TODO(nnorwitz): handle a lot more syntax.
+        elif token.token_type == tokenize.PREPROCESSOR:
+            # TODO(nnorwitz): handle more preprocessor directives.
+            # token starts with a #, so remove it and strip whitespace.
+            name = token.name[1:].lstrip()
+            if name.startswith('include'):
+                # Remove "include".
+                name = name[7:].strip()
+                assert name
+                # Handle #include \<newline> "header-on-second-line.h".
+                if name.startswith('\\'):
+                    name = name[1:].strip()
+                assert name[0] in '<"', token
+                assert name[-1] in '>"', token
+                system = name[0] == '<'
+                filename = name[1:-1]
+                return Include(token.start, token.end, filename, system)
+            if name.startswith('define'):
+                # Remove "define".
+                name = name[6:].strip()
+                assert name
+                value = ''
+                for i, c in enumerate(name):
+                    if c.isspace():
+                        value = name[i:].lstrip()
+                        name = name[:i]
+                        break
+                return Define(token.start, token.end, name, value)
+            if name.startswith('if') and name[2:3].isspace():
+                condition = name[3:].strip()
+                if condition.startswith('0') or condition.startswith('(0)'):
+                    self._SkipIf0Blocks()
+        return None
+
+    def _GetTokensUpTo(self, expected_token_type, expected_token):
+        return self._GetVarTokensUpTo(expected_token_type, expected_token)[0]
+
+    def _GetVarTokensUpTo(self, expected_token_type, *expected_tokens):
+        last_token = self._GetNextToken()
+        tokens = []
+        while (last_token.token_type != expected_token_type or
+               last_token.name not in expected_tokens):
+            tokens.append(last_token)
+            last_token = self._GetNextToken()
+        return tokens, last_token
+
+    # TODO(nnorwitz): remove _IgnoreUpTo() it shouldn't be necesary.
+    def _IgnoreUpTo(self, token_type, token):
+        unused_tokens = self._GetTokensUpTo(token_type, token)
+
+    def _SkipIf0Blocks(self):
+        count = 1
+        while 1:
+            token = self._GetNextToken()
+            if token.token_type != tokenize.PREPROCESSOR:
+                continue
+
+            name = token.name[1:].lstrip()
+            if name.startswith('endif'):
+                count -= 1
+                if count == 0:
+                    break
+            elif name.startswith('if'):
+                count += 1
+
+    def _GetMatchingChar(self, open_paren, close_paren, GetNextToken=None):
+        if GetNextToken is None:
+            GetNextToken = self._GetNextToken
+        # Assumes the current token is open_paren and we will consume
+        # and return up to the close_paren.
+        count = 1
+        token = GetNextToken()
+        while 1:
+            if token.token_type == tokenize.SYNTAX:
+                if token.name == open_paren:
+                    count += 1
+                elif token.name == close_paren:
+                    count -= 1
+                    if count == 0:
+                        break
+            yield token
+            token = GetNextToken()
+        yield token
+
+    def _GetParameters(self):
+        return self._GetMatchingChar('(', ')')
+
+    def GetScope(self):
+        return self._GetMatchingChar('{', '}')
+
+    def _GetNextToken(self):
+        if self.token_queue:
+            return self.token_queue.pop()
+        return next(self.tokens)
+
+    def _AddBackToken(self, token):
+        if token.whence == tokenize.WHENCE_STREAM:
+            token.whence = tokenize.WHENCE_QUEUE
+            self.token_queue.insert(0, token)
+        else:
+            assert token.whence == tokenize.WHENCE_QUEUE, token
+            self.token_queue.append(token)
+
+    def _AddBackTokens(self, tokens):
+        if tokens:
+            if tokens[-1].whence == tokenize.WHENCE_STREAM:
+                for token in tokens:
+                    token.whence = tokenize.WHENCE_QUEUE
+                self.token_queue[:0] = reversed(tokens)
+            else:
+                assert tokens[-1].whence == tokenize.WHENCE_QUEUE, tokens
+                self.token_queue.extend(reversed(tokens))
+
+    def GetName(self, seq=None):
+        """Returns ([tokens], next_token_info)."""
+        GetNextToken = self._GetNextToken
+        if seq is not None:
+            it = iter(seq)
+            GetNextToken = lambda: next(it)
+        next_token = GetNextToken()
+        tokens = []
+        last_token_was_name = False
+        while (next_token.token_type == tokenize.NAME or
+               (next_token.token_type == tokenize.SYNTAX and
+                next_token.name in ('::', '<'))):
+            # Two NAMEs in a row means the identifier should terminate.
+            # It's probably some sort of variable declaration.
+            if last_token_was_name and next_token.token_type == tokenize.NAME:
+                break
+            last_token_was_name = next_token.token_type == tokenize.NAME
+            tokens.append(next_token)
+            # Handle templated names.
+            if next_token.name == '<':
+                tokens.extend(self._GetMatchingChar('<', '>', GetNextToken))
+                last_token_was_name = True
+            next_token = GetNextToken()
+        return tokens, next_token
+
+    def GetMethod(self, modifiers, templated_types):
+        return_type_and_name = self._GetTokensUpTo(tokenize.SYNTAX, '(')
+        assert len(return_type_and_name) >= 1
+        return self._GetMethod(return_type_and_name, modifiers, templated_types,
+                               False)
+
+    def _GetMethod(self, return_type_and_name, modifiers, templated_types,
+                   get_paren):
+        template_portion = None
+        if get_paren:
+            token = self._GetNextToken()
+            assert token.token_type == tokenize.SYNTAX, token
+            if token.name == '<':
+                # Handle templatized dtors.
+                template_portion = [token]
+                template_portion.extend(self._GetMatchingChar('<', '>'))
+                token = self._GetNextToken()
+            assert token.token_type == tokenize.SYNTAX, token
+            assert token.name == '(', token
+
+        name = return_type_and_name.pop()
+        # Handle templatized ctors.
+        if name.name == '>':
+            index = 1
+            while return_type_and_name[index].name != '<':
+                index += 1
+            template_portion = return_type_and_name[index:] + [name]
+            del return_type_and_name[index:]
+            name = return_type_and_name.pop()
+        elif name.name == ']':
+            rt = return_type_and_name
+            assert rt[-1].name == '[', return_type_and_name
+            assert rt[-2].name == 'operator', return_type_and_name
+            name_seq = return_type_and_name[-2:]
+            del return_type_and_name[-2:]
+            name = tokenize.Token(tokenize.NAME, 'operator[]',
+                                  name_seq[0].start, name.end)
+            # Get the open paren so _GetParameters() below works.
+            unused_open_paren = self._GetNextToken()
+
+        # TODO(nnorwitz): store template_portion.
+        return_type = return_type_and_name
+        indices = name
+        if return_type:
+            indices = return_type[0]
+
+        # Force ctor for templatized ctors.
+        if name.name == self.in_class and not modifiers:
+            modifiers |= FUNCTION_CTOR
+        parameters = list(self._GetParameters())
+        del parameters[-1]              # Remove trailing ')'.
+
+        # Handling operator() is especially weird.
+        if name.name == 'operator' and not parameters:
+            token = self._GetNextToken()
+            assert token.name == '(', token
+            parameters = list(self._GetParameters())
+            del parameters[-1]          # Remove trailing ')'.
+
+        token = self._GetNextToken()
+        while token.token_type == tokenize.NAME:
+            modifier_token = token
+            token = self._GetNextToken()
+            if modifier_token.name == 'const':
+                modifiers |= FUNCTION_CONST
+            elif modifier_token.name == '__attribute__':
+                # TODO(nnorwitz): handle more __attribute__ details.
+                modifiers |= FUNCTION_ATTRIBUTE
+                assert token.name == '(', token
+                # Consume everything between the (parens).
+                unused_tokens = list(self._GetMatchingChar('(', ')'))
+                token = self._GetNextToken()
+            elif modifier_token.name == 'throw':
+                modifiers |= FUNCTION_THROW
+                assert token.name == '(', token
+                # Consume everything between the (parens).
+                unused_tokens = list(self._GetMatchingChar('(', ')'))
+                token = self._GetNextToken()
+            elif modifier_token.name == modifier_token.name.upper():
+                # HACK(nnorwitz):  assume that all upper-case names
+                # are some macro we aren't expanding.
+                modifiers |= FUNCTION_UNKNOWN_ANNOTATION
+            else:
+                self.HandleError('unexpected token', modifier_token)
+
+        assert token.token_type == tokenize.SYNTAX, token
+        # Handle ctor initializers.
+        if token.name == ':':
+            # TODO(nnorwitz): anything else to handle for initializer list?
+            while token.name != ';' and token.name != '{':
+                token = self._GetNextToken()
+
+        # Handle pointer to functions that are really data but look
+        # like method declarations.
+        if token.name == '(':
+            if parameters[0].name == '*':
+                # name contains the return type.
+                name = parameters.pop()
+                # parameters contains the name of the data.
+                modifiers = [p.name for p in parameters]
+                # Already at the ( to open the parameter list.
+                function_parameters = list(self._GetMatchingChar('(', ')'))
+                del function_parameters[-1]  # Remove trailing ')'.
+                # TODO(nnorwitz): store the function_parameters.
+                token = self._GetNextToken()
+                assert token.token_type == tokenize.SYNTAX, token
+                assert token.name == ';', token
+                return self._CreateVariable(indices, name.name, indices.name,
+                                            modifiers, '', None)
+            # At this point, we got something like:
+            #  return_type (type::*name_)(params);
+            # This is a data member called name_ that is a function pointer.
+            # With this code: void (sq_type::*field_)(string&);
+            # We get: name=void return_type=[] parameters=sq_type ... field_
+            # TODO(nnorwitz): is return_type always empty?
+            # TODO(nnorwitz): this isn't even close to being correct.
+            # Just put in something so we don't crash and can move on.
+            real_name = parameters[-1]
+            modifiers = [p.name for p in self._GetParameters()]
+            del modifiers[-1]           # Remove trailing ')'.
+            return self._CreateVariable(indices, real_name.name, indices.name,
+                                        modifiers, '', None)
+
+        if token.name == '{':
+            body = list(self.GetScope())
+            del body[-1]                # Remove trailing '}'.
+        else:
+            body = None
+            if token.name == '=':
+                token = self._GetNextToken()
+                assert token.token_type == tokenize.CONSTANT, token
+                assert token.name == '0', token
+                modifiers |= FUNCTION_PURE_VIRTUAL
+                token = self._GetNextToken()
+
+            if token.name == '[':
+                # TODO(nnorwitz): store tokens and improve parsing.
+                # template <typename T, size_t N> char (&ASH(T (&seq)[N]))[N];
+                tokens = list(self._GetMatchingChar('[', ']'))
+                token = self._GetNextToken()
+
+            assert token.name == ';', (token, return_type_and_name, parameters)
+
+        # Looks like we got a method, not a function.
+        if len(return_type) > 2 and return_type[-1].name == '::':
+            return_type, in_class = \
+                         self._GetReturnTypeAndClassName(return_type)
+            return Method(indices.start, indices.end, name.name, in_class,
+                          return_type, parameters, modifiers, templated_types,
+                          body, self.namespace_stack)
+        return Function(indices.start, indices.end, name.name, return_type,
+                        parameters, modifiers, templated_types, body,
+                        self.namespace_stack)
+
+    def _GetReturnTypeAndClassName(self, token_seq):
+        # Splitting the return type from the class name in a method
+        # can be tricky.  For example, Return::Type::Is::Hard::To::Find().
+        # Where is the return type and where is the class name?
+        # The heuristic used is to pull the last name as the class name.
+        # This includes all the templated type info.
+        # TODO(nnorwitz): if there is only One name like in the
+        # example above, punt and assume the last bit is the class name.
+
+        # Ignore a :: prefix, if exists so we can find the first real name.
+        i = 0
+        if token_seq[0].name == '::':
+            i = 1
+        # Ignore a :: suffix, if exists.
+        end = len(token_seq) - 1
+        if token_seq[end-1].name == '::':
+            end -= 1
+
+        # Make a copy of the sequence so we can append a sentinel
+        # value. This is required for GetName will has to have some
+        # terminating condition beyond the last name.
+        seq_copy = token_seq[i:end]
+        seq_copy.append(tokenize.Token(tokenize.SYNTAX, '', 0, 0))
+        names = []
+        while i < end:
+            # Iterate through the sequence parsing out each name.
+            new_name, next = self.GetName(seq_copy[i:])
+            assert new_name, 'Got empty new_name, next=%s' % next
+            # We got a pointer or ref.  Add it to the name.
+            if next and next.token_type == tokenize.SYNTAX:
+                new_name.append(next)
+            names.append(new_name)
+            i += len(new_name)
+
+        # Now that we have the names, it's time to undo what we did.
+
+        # Remove the sentinel value.
+        names[-1].pop()
+        # Flatten the token sequence for the return type.
+        return_type = [e for seq in names[:-1] for e in seq]
+        # The class name is the last name.
+        class_name = names[-1]
+        return return_type, class_name
+
+    def handle_bool(self):
+        pass
+
+    def handle_char(self):
+        pass
+
+    def handle_int(self):
+        pass
+
+    def handle_long(self):
+        pass
+
+    def handle_short(self):
+        pass
+
+    def handle_double(self):
+        pass
+
+    def handle_float(self):
+        pass
+
+    def handle_void(self):
+        pass
+
+    def handle_wchar_t(self):
+        pass
+
+    def handle_unsigned(self):
+        pass
+
+    def handle_signed(self):
+        pass
+
+    def _GetNestedType(self, ctor):
+        name = None
+        name_tokens, token = self.GetName()
+        if name_tokens:
+            name = ''.join([t.name for t in name_tokens])
+
+        # Handle forward declarations.
+        if token.token_type == tokenize.SYNTAX and token.name == ';':
+            return ctor(token.start, token.end, name, None,
+                        self.namespace_stack)
+
+        if token.token_type == tokenize.NAME and self._handling_typedef:
+            self._AddBackToken(token)
+            return ctor(token.start, token.end, name, None,
+                        self.namespace_stack)
+
+        # Must be the type declaration.
+        fields = list(self._GetMatchingChar('{', '}'))
+        del fields[-1]                  # Remove trailing '}'.
+        if token.token_type == tokenize.SYNTAX and token.name == '{':
+            next = self._GetNextToken()
+            new_type = ctor(token.start, token.end, name, fields,
+                            self.namespace_stack)
+            # A name means this is an anonymous type and the name
+            # is the variable declaration.
+            if next.token_type != tokenize.NAME:
+                return new_type
+            name = new_type
+            token = next
+
+        # Must be variable declaration using the type prefixed with keyword.
+        assert token.token_type == tokenize.NAME, token
+        return self._CreateVariable(token, token.name, name, [], '', None)
+
+    def handle_struct(self):
+        # Special case the handling typedef/aliasing of structs here.
+        # It would be a pain to handle in the class code.
+        name_tokens, var_token = self.GetName()
+        if name_tokens:
+            next_token = self._GetNextToken()
+            is_syntax = (var_token.token_type == tokenize.SYNTAX and
+                         var_token.name[0] in '*&')
+            is_variable = (var_token.token_type == tokenize.NAME and
+                           next_token.name == ';')
+            variable = var_token
+            if is_syntax and not is_variable:
+                variable = next_token
+                temp = self._GetNextToken()
+                if temp.token_type == tokenize.SYNTAX and temp.name == '(':
+                    # Handle methods declared to return a struct.
+                    t0 = name_tokens[0]
+                    struct = tokenize.Token(tokenize.NAME, 'struct',
+                                            t0.start-7, t0.start-2)
+                    type_and_name = [struct]
+                    type_and_name.extend(name_tokens)
+                    type_and_name.extend((var_token, next_token))
+                    return self._GetMethod(type_and_name, 0, None, False)
+                assert temp.name == ';', (temp, name_tokens, var_token)
+            if is_syntax or (is_variable and not self._handling_typedef):
+                modifiers = ['struct']
+                type_name = ''.join([t.name for t in name_tokens])
+                position = name_tokens[0]
+                return self._CreateVariable(position, variable.name, type_name,
+                                            modifiers, var_token.name, None)
+            name_tokens.extend((var_token, next_token))
+            self._AddBackTokens(name_tokens)
+        else:
+            self._AddBackToken(var_token)
+        return self._GetClass(Struct, VISIBILITY_PUBLIC, None)
+
+    def handle_union(self):
+        return self._GetNestedType(Union)
+
+    def handle_enum(self):
+        return self._GetNestedType(Enum)
+
+    def handle_auto(self):
+        # TODO(nnorwitz): warn about using auto?  Probably not since it
+        # will be reclaimed and useful for C++0x.
+        pass
+
+    def handle_register(self):
+        pass
+
+    def handle_const(self):
+        pass
+
+    def handle_inline(self):
+        pass
+
+    def handle_extern(self):
+        pass
+
+    def handle_static(self):
+        pass
+
+    def handle_virtual(self):
+        # What follows must be a method.
+        token = token2 = self._GetNextToken()
+        if token.name == 'inline':
+            # HACK(nnorwitz): handle inline dtors by ignoring 'inline'.
+            token2 = self._GetNextToken()
+        if token2.token_type == tokenize.SYNTAX and token2.name == '~':
+            return self.GetMethod(FUNCTION_VIRTUAL + FUNCTION_DTOR, None)
+        assert token.token_type == tokenize.NAME or token.name == '::', token
+        return_type_and_name = self._GetTokensUpTo(tokenize.SYNTAX, '(')
+        return_type_and_name.insert(0, token)
+        if token2 is not token:
+            return_type_and_name.insert(1, token2)
+        return self._GetMethod(return_type_and_name, FUNCTION_VIRTUAL,
+                               None, False)
+
+    def handle_volatile(self):
+        pass
+
+    def handle_mutable(self):
+        pass
+
+    def handle_public(self):
+        assert self.in_class
+        self.visibility = VISIBILITY_PUBLIC
+
+    def handle_protected(self):
+        assert self.in_class
+        self.visibility = VISIBILITY_PROTECTED
+
+    def handle_private(self):
+        assert self.in_class
+        self.visibility = VISIBILITY_PRIVATE
+
+    def handle_friend(self):
+        tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';')
+        assert tokens
+        t0 = tokens[0]
+        return Friend(t0.start, t0.end, tokens, self.namespace_stack)
+
+    def handle_static_cast(self):
+        pass
+
+    def handle_const_cast(self):
+        pass
+
+    def handle_dynamic_cast(self):
+        pass
+
+    def handle_reinterpret_cast(self):
+        pass
+
+    def handle_new(self):
+        pass
+
+    def handle_delete(self):
+        tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';')
+        assert tokens
+        return Delete(tokens[0].start, tokens[0].end, tokens)
+
+    def handle_typedef(self):
+        token = self._GetNextToken()
+        if (token.token_type == tokenize.NAME and
+            keywords.IsKeyword(token.name)):
+            # Token must be struct/enum/union/class.
+            method = getattr(self, 'handle_' + token.name)
+            self._handling_typedef = True
+            tokens = [method()]
+            self._handling_typedef = False
+        else:
+            tokens = [token]
+
+        # Get the remainder of the typedef up to the semi-colon.
+        tokens.extend(self._GetTokensUpTo(tokenize.SYNTAX, ';'))
+
+        # TODO(nnorwitz): clean all this up.
+        assert tokens
+        name = tokens.pop()
+        indices = name
+        if tokens:
+            indices = tokens[0]
+        if not indices:
+            indices = token
+        if name.name == ')':
+            # HACK(nnorwitz): Handle pointers to functions "properly".
+            if (len(tokens) >= 4 and
+                tokens[1].name == '(' and tokens[2].name == '*'):
+                tokens.append(name)
+                name = tokens[3]
+        elif name.name == ']':
+            # HACK(nnorwitz): Handle arrays properly.
+            if len(tokens) >= 2:
+                tokens.append(name)
+                name = tokens[1]
+        new_type = tokens
+        if tokens and isinstance(tokens[0], tokenize.Token):
+            new_type = self.converter.ToType(tokens)[0]
+        return Typedef(indices.start, indices.end, name.name,
+                       new_type, self.namespace_stack)
+
+    def handle_typeid(self):
+        pass  # Not needed yet.
+
+    def handle_typename(self):
+        pass  # Not needed yet.
+
+    def _GetTemplatedTypes(self):
+        result = {}
+        tokens = list(self._GetMatchingChar('<', '>'))
+        len_tokens = len(tokens) - 1    # Ignore trailing '>'.
+        i = 0
+        while i < len_tokens:
+            key = tokens[i].name
+            i += 1
+            if keywords.IsKeyword(key) or key == ',':
+                continue
+            type_name = default = None
+            if i < len_tokens:
+                i += 1
+                if tokens[i-1].name == '=':
+                    assert i < len_tokens, '%s %s' % (i, tokens)
+                    default, unused_next_token = self.GetName(tokens[i:])
+                    i += len(default)
+                else:
+                    if tokens[i-1].name != ',':
+                        # We got something like: Type variable.
+                        # Re-adjust the key (variable) and type_name (Type).
+                        key = tokens[i-1].name
+                        type_name = tokens[i-2]
+
+            result[key] = (type_name, default)
+        return result
+
+    def handle_template(self):
+        token = self._GetNextToken()
+        assert token.token_type == tokenize.SYNTAX, token
+        assert token.name == '<', token
+        templated_types = self._GetTemplatedTypes()
+        # TODO(nnorwitz): for now, just ignore the template params.
+        token = self._GetNextToken()
+        if token.token_type == tokenize.NAME:
+            if token.name == 'class':
+                return self._GetClass(Class, VISIBILITY_PRIVATE, templated_types)
+            elif token.name == 'struct':
+                return self._GetClass(Struct, VISIBILITY_PUBLIC, templated_types)
+            elif token.name == 'friend':
+                return self.handle_friend()
+        self._AddBackToken(token)
+        tokens, last = self._GetVarTokensUpTo(tokenize.SYNTAX, '(', ';')
+        tokens.append(last)
+        self._AddBackTokens(tokens)
+        if last.name == '(':
+            return self.GetMethod(FUNCTION_NONE, templated_types)
+        # Must be a variable definition.
+        return None
+
+    def handle_true(self):
+        pass  # Nothing to do.
+
+    def handle_false(self):
+        pass  # Nothing to do.
+
+    def handle_asm(self):
+        pass  # Not needed yet.
+
+    def handle_class(self):
+        return self._GetClass(Class, VISIBILITY_PRIVATE, None)
+
+    def _GetBases(self):
+        # Get base classes.
+        bases = []
+        while 1:
+            token = self._GetNextToken()
+            assert token.token_type == tokenize.NAME, token
+            # TODO(nnorwitz): store kind of inheritance...maybe.
+            if token.name not in ('public', 'protected', 'private'):
+                # If inheritance type is not specified, it is private.
+                # Just put the token back so we can form a name.
+                # TODO(nnorwitz): it would be good to warn about this.
+                self._AddBackToken(token)
+            else:
+                # Check for virtual inheritance.
+                token = self._GetNextToken()
+                if token.name != 'virtual':
+                    self._AddBackToken(token)
+                else:
+                    # TODO(nnorwitz): store that we got virtual for this base.
+                    pass
+            base, next_token = self.GetName()
+            bases_ast = self.converter.ToType(base)
+            assert len(bases_ast) == 1, bases_ast
+            bases.append(bases_ast[0])
+            assert next_token.token_type == tokenize.SYNTAX, next_token
+            if next_token.name == '{':
+                token = next_token
+                break
+            # Support multiple inheritance.
+            assert next_token.name == ',', next_token
+        return bases, token
+
+    def _GetClass(self, class_type, visibility, templated_types):
+        class_name = None
+        class_token = self._GetNextToken()
+        if class_token.token_type != tokenize.NAME:
+            assert class_token.token_type == tokenize.SYNTAX, class_token
+            token = class_token
+        else:
+            self._AddBackToken(class_token)
+            name_tokens, token = self.GetName()
+            class_name = ''.join([t.name for t in name_tokens])
+        bases = None
+        if token.token_type == tokenize.SYNTAX:
+            if token.name == ';':
+                # Forward declaration.
+                return class_type(class_token.start, class_token.end,
+                                  class_name, None, templated_types, None,
+                                  self.namespace_stack)
+            if token.name in '*&':
+                # Inline forward declaration.  Could be method or data.
+                name_token = self._GetNextToken()
+                next_token = self._GetNextToken()
+                if next_token.name == ';':
+                    # Handle data
+                    modifiers = ['class']
+                    return self._CreateVariable(class_token, name_token.name,
+                                                class_name,
+                                                modifiers, token.name, None)
+                else:
+                    # Assume this is a method.
+                    tokens = (class_token, token, name_token, next_token)
+                    self._AddBackTokens(tokens)
+                    return self.GetMethod(FUNCTION_NONE, None)
+            if token.name == ':':
+                bases, token = self._GetBases()
+
+        body = None
+        if token.token_type == tokenize.SYNTAX and token.name == '{':
+            assert token.token_type == tokenize.SYNTAX, token
+            assert token.name == '{', token
+
+            ast = AstBuilder(self.GetScope(), self.filename, class_name,
+                             visibility, self.namespace_stack)
+            body = list(ast.Generate())
+
+            if not self._handling_typedef:
+                token = self._GetNextToken()
+                if token.token_type != tokenize.NAME:
+                    assert token.token_type == tokenize.SYNTAX, token
+                    assert token.name == ';', token
+                else:
+                    new_class = class_type(class_token.start, class_token.end,
+                                           class_name, bases, None,
+                                           body, self.namespace_stack)
+
+                    modifiers = []
+                    return self._CreateVariable(class_token,
+                                                token.name, new_class,
+                                                modifiers, token.name, None)
+        else:
+            if not self._handling_typedef:
+                self.HandleError('non-typedef token', token)
+            self._AddBackToken(token)
+
+        return class_type(class_token.start, class_token.end, class_name,
+                          bases, None, body, self.namespace_stack)
+
+    def handle_namespace(self):
+        token = self._GetNextToken()
+        # Support anonymous namespaces.
+        name = None
+        if token.token_type == tokenize.NAME:
+            name = token.name
+            token = self._GetNextToken()
+        self.namespace_stack.append(name)
+        assert token.token_type == tokenize.SYNTAX, token
+        # Create an internal token that denotes when the namespace is complete.
+        internal_token = tokenize.Token(_INTERNAL_TOKEN, _NAMESPACE_POP,
+                                        None, None)
+        internal_token.whence = token.whence
+        if token.name == '=':
+            # TODO(nnorwitz): handle aliasing namespaces.
+            name, next_token = self.GetName()
+            assert next_token.name == ';', next_token
+            self._AddBackToken(internal_token)
+        else:
+            assert token.name == '{', token
+            tokens = list(self.GetScope())
+            # Replace the trailing } with the internal namespace pop token.
+            tokens[-1] = internal_token
+            # Handle namespace with nothing in it.
+            self._AddBackTokens(tokens)
+        return None
+
+    def handle_using(self):
+        tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';')
+        assert tokens
+        return Using(tokens[0].start, tokens[0].end, tokens)
+
+    def handle_explicit(self):
+        assert self.in_class
+        # Nothing much to do.
+        # TODO(nnorwitz): maybe verify the method name == class name.
+        # This must be a ctor.
+        return self.GetMethod(FUNCTION_CTOR, None)
+
+    def handle_this(self):
+        pass  # Nothing to do.
+
+    def handle_operator(self):
+        # Pull off the next token(s?) and make that part of the method name.
+        pass
+
+    def handle_sizeof(self):
+        pass
+
+    def handle_case(self):
+        pass
+
+    def handle_switch(self):
+        pass
+
+    def handle_default(self):
+        token = self._GetNextToken()
+        assert token.token_type == tokenize.SYNTAX
+        assert token.name == ':'
+
+    def handle_if(self):
+        pass
+
+    def handle_else(self):
+        pass
+
+    def handle_return(self):
+        tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';')
+        if not tokens:
+            return Return(self.current_token.start, self.current_token.end, None)
+        return Return(tokens[0].start, tokens[0].end, tokens)
+
+    def handle_goto(self):
+        tokens = self._GetTokensUpTo(tokenize.SYNTAX, ';')
+        assert len(tokens) == 1, str(tokens)
+        return Goto(tokens[0].start, tokens[0].end, tokens[0].name)
+
+    def handle_try(self):
+        pass  # Not needed yet.
+
+    def handle_catch(self):
+        pass  # Not needed yet.
+
+    def handle_throw(self):
+        pass  # Not needed yet.
+
+    def handle_while(self):
+        pass
+
+    def handle_do(self):
+        pass
+
+    def handle_for(self):
+        pass
+
+    def handle_break(self):
+        self._IgnoreUpTo(tokenize.SYNTAX, ';')
+
+    def handle_continue(self):
+        self._IgnoreUpTo(tokenize.SYNTAX, ';')
+
+
+def BuilderFromSource(source, filename):
+    """Utility method that returns an AstBuilder from source code.
+
+    Args:
+      source: 'C++ source code'
+      filename: 'file1'
+
+    Returns:
+      AstBuilder
+    """
+    return AstBuilder(tokenize.GetTokens(source), filename)
+
+
+def PrintIndentifiers(filename, should_print):
+    """Prints all identifiers for a C++ source file.
+
+    Args:
+      filename: 'file1'
+      should_print: predicate with signature: bool Function(token)
+    """
+    source = utils.ReadFile(filename, False)
+    if source is None:
+        sys.stderr.write('Unable to find: %s\n' % filename)
+        return
+
+    #print('Processing %s' % actual_filename)
+    builder = BuilderFromSource(source, filename)
+    try:
+        for node in builder.Generate():
+            if should_print(node):
+                print(node.name)
+    except KeyboardInterrupt:
+        return
+    except:
+        pass
+
+
+def PrintAllIndentifiers(filenames, should_print):
+    """Prints all identifiers for each C++ source file in filenames.
+
+    Args:
+      filenames: ['file1', 'file2', ...]
+      should_print: predicate with signature: bool Function(token)
+    """
+    for path in filenames:
+        PrintIndentifiers(path, should_print)
+
+
+def main(argv):
+    for filename in argv[1:]:
+        source = utils.ReadFile(filename)
+        if source is None:
+            continue
+
+        print('Processing %s' % filename)
+        builder = BuilderFromSource(source, filename)
+        try:
+            entire_ast = filter(None, builder.Generate())
+        except KeyboardInterrupt:
+            return
+        except:
+            # Already printed a warning, print the traceback and continue.
+            traceback.print_exc()
+        else:
+            if utils.DEBUG:
+                for ast in entire_ast:
+                    print(ast)
+
+
+if __name__ == '__main__':
+    main(sys.argv)
diff --git a/third_party/gmock/scripts/generator/cpp/gmock_class.py b/third_party/gmock/scripts/generator/cpp/gmock_class.py
new file mode 100644
index 0000000..3ad0bcd
--- /dev/null
+++ b/third_party/gmock/scripts/generator/cpp/gmock_class.py
@@ -0,0 +1,178 @@
+#!/usr/bin/env python
+#
+# Copyright 2008 Google Inc.  All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Generate Google Mock classes from base classes.
+
+This program will read in a C++ source file and output the Google Mock
+classes for the specified classes.  If no class is specified, all
+classes in the source file are emitted.
+
+Usage:
+  gmock_class.py header-file.h [ClassName]...
+
+Output is sent to stdout.
+"""
+
+__author__ = 'nnorwitz@google.com (Neal Norwitz)'
+
+
+import os
+import re
+import sets
+import sys
+
+from cpp import ast
+from cpp import utils
+
+_VERSION = (1, 0, 1)  # The version of this script.
+# How many spaces to indent.  Can set me with the INDENT environment variable.
+_INDENT = 2
+
+
+def _GenerateMethods(output_lines, source, class_node):
+  function_type = ast.FUNCTION_VIRTUAL | ast.FUNCTION_PURE_VIRTUAL
+  ctor_or_dtor = ast.FUNCTION_CTOR | ast.FUNCTION_DTOR
+
+  for node in class_node.body:
+    # We only care about virtual functions.
+    if (isinstance(node, ast.Function) and
+        node.modifiers & function_type and
+        not node.modifiers & ctor_or_dtor):
+      # Pick out all the elements we need from the original function.
+      const = ''
+      if node.modifiers & ast.FUNCTION_CONST:
+        const = 'CONST_'
+      return_type = 'void'
+      if node.return_type:
+        # Add modifiers like 'const'.
+        modifiers = ''
+        if node.return_type.modifiers:
+          modifiers = ' '.join(node.return_type.modifiers) + ' '
+        return_type = modifiers + node.return_type.name
+        if node.return_type.pointer:
+          return_type += '*'
+        if node.return_type.reference:
+          return_type += '&'
+      prefix = 'MOCK_%sMETHOD%d' % (const, len(node.parameters))
+      args = ''
+      if node.parameters:
+        # Get the full text of the parameters from the start
+        # of the first parameter to the end of the last parameter.
+        start = node.parameters[0].start
+        end = node.parameters[-1].end
+        # Remove // comments.
+        args_strings = re.sub(r'//.*', '', source[start:end])
+        # Condense multiple spaces and eliminate newlines putting the
+        # parameters together on a single line.  Ensure there is a
+        # space in an argument which is split by a newline without
+        # intervening whitespace, e.g.: int\nBar
+        args = re.sub('  +', ' ', args_strings.replace('\n', ' '))
+
+      # Create the prototype.
+      indent = ' ' * _INDENT
+      line = ('%s%s(%s,\n%s%s(%s));' %
+              (indent, prefix, node.name, indent*3, return_type, args))
+      output_lines.append(line)
+
+
+def _GenerateMocks(filename, source, ast_list, desired_class_names):
+  processed_class_names = sets.Set()
+  lines = []
+  for node in ast_list:
+    if (isinstance(node, ast.Class) and node.body and
+        # desired_class_names being None means that all classes are selected.
+        (not desired_class_names or node.name in desired_class_names)):
+      class_name = node.name
+      processed_class_names.add(class_name)
+      class_node = node
+      # Add namespace before the class.
+      if class_node.namespace:
+        lines.extend(['namespace %s {' % n for n in class_node.namespace])  # }
+        lines.append('')
+
+      # Add the class prolog.
+      lines.append('class Mock%s : public %s {' % (class_name, class_name))  # }
+      lines.append('%spublic:' % (' ' * (_INDENT // 2)))
+
+      # Add all the methods.
+      _GenerateMethods(lines, source, class_node)
+
+      # Close the class.
+      if lines:
+        # If there are no virtual methods, no need for a public label.
+        if len(lines) == 2:
+          del lines[-1]
+
+        # Only close the class if there really is a class.
+        lines.append('};')
+        lines.append('')  # Add an extra newline.
+
+      # Close the namespace.
+      if class_node.namespace:
+        for i in range(len(class_node.namespace)-1, -1, -1):
+          lines.append('}  // namespace %s' % class_node.namespace[i])
+        lines.append('')  # Add an extra newline.
+
+  if desired_class_names:
+    missing_class_name_list = list(desired_class_names - processed_class_names)
+    if missing_class_name_list:
+      missing_class_name_list.sort()
+      sys.stderr.write('Class(es) not found in %s: %s\n' %
+                       (filename, ', '.join(missing_class_name_list)))
+  elif not processed_class_names:
+    sys.stderr.write('No class found in %s\n' % filename)
+
+  return lines
+
+
+def main(argv=sys.argv):
+  if len(argv) < 2:
+    sys.stderr.write('Google Mock Class Generator v%s\n\n' %
+                     '.'.join(map(str, _VERSION)))
+    sys.stderr.write(__doc__)
+    return 1
+
+  global _INDENT
+  try:
+    _INDENT = int(os.environ['INDENT'])
+  except KeyError:
+    pass
+  except:
+    sys.stderr.write('Unable to use indent of %s\n' % os.environ.get('INDENT'))
+
+  filename = argv[1]
+  desired_class_names = None  # None means all classes in the source file.
+  if len(argv) >= 3:
+    desired_class_names = sets.Set(argv[2:])
+  source = utils.ReadFile(filename)
+  if source is None:
+    return 1
+
+  builder = ast.BuilderFromSource(source, filename)
+  try:
+    entire_ast = filter(None, builder.Generate())
+  except KeyboardInterrupt:
+    return
+  except:
+    # An error message was already printed since we couldn't parse.
+    pass
+  else:
+    lines = _GenerateMocks(filename, source, entire_ast, desired_class_names)
+    sys.stdout.write('\n'.join(lines))
+
+
+if __name__ == '__main__':
+  main(sys.argv)
diff --git a/third_party/gmock/scripts/generator/cpp/gmock_class_test.py b/third_party/gmock/scripts/generator/cpp/gmock_class_test.py
new file mode 100644
index 0000000..ae00800
--- /dev/null
+++ b/third_party/gmock/scripts/generator/cpp/gmock_class_test.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+#
+# Copyright 2009 Neal Norwitz All Rights Reserved.
+# Portions Copyright 2009 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Tests for gmock.scripts.generator.cpp.gmock_class."""
+
+__author__ = 'nnorwitz@google.com (Neal Norwitz)'
+
+
+import os
+import sys
+import unittest
+
+# Allow the cpp imports below to work when run as a standalone script.
+sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
+
+from cpp import ast
+from cpp import gmock_class
+
+
+class TestCase(unittest.TestCase):
+  """Helper class that adds assert methods."""
+
+  def assertEqualIgnoreLeadingWhitespace(self, expected_lines, lines):
+    """Specialized assert that ignores the indent level."""
+    stripped_lines = '\n'.join([s.lstrip() for s in lines.split('\n')])
+    self.assertEqual(expected_lines, stripped_lines)
+
+
+class GenerateMethodsTest(TestCase):
+
+  def GenerateMethodSource(self, cpp_source):
+    """Helper method to convert C++ source to gMock output source lines."""
+    method_source_lines = []
+    # <test> is a pseudo-filename, it is not read or written.
+    builder = ast.BuilderFromSource(cpp_source, '<test>')
+    ast_list = list(builder.Generate())
+    gmock_class._GenerateMethods(method_source_lines, cpp_source, ast_list[0])
+    return ''.join(method_source_lines)
+
+  def testStrangeNewlineInParameter(self):
+    source = """
+class Foo {
+ public:
+  virtual void Bar(int
+a) = 0;
+};
+"""
+    self.assertEqualIgnoreLeadingWhitespace(
+        'MOCK_METHOD1(Bar,\nvoid(int a));',
+        self.GenerateMethodSource(source))
+
+  def testDoubleSlashCommentsInParameterListAreRemoved(self):
+    source = """
+class Foo {
+ public:
+  virtual void Bar(int a,  // inline comments should be elided.
+                   int b   // inline comments should be elided.
+                   ) const = 0;
+};
+"""
+    self.assertEqualIgnoreLeadingWhitespace(
+        'MOCK_CONST_METHOD2(Bar,\nvoid(int a, int b));',
+        self.GenerateMethodSource(source))
+
+  def testCStyleCommentsInParameterListAreNotRemoved(self):
+    # NOTE(nnorwitz): I'm not sure if it's the best behavior to keep these
+    # comments.  Also note that C style comments after the last parameter
+    # are still elided.
+    source = """
+class Foo {
+ public:
+  virtual const string& Bar(int /* keeper */, int b);
+};
+"""
+    self.assertEqualIgnoreLeadingWhitespace(
+        'MOCK_METHOD2(Bar,\nconst string&(int /* keeper */, int b));',
+        self.GenerateMethodSource(source))
+
+
+class GenerateMocksTest(TestCase):
+
+  def GenerateMocks(self, cpp_source):
+    """Helper method to convert C++ source to complete gMock output source."""
+    # <test> is a pseudo-filename, it is not read or written.
+    filename = '<test>'
+    builder = ast.BuilderFromSource(cpp_source, filename)
+    ast_list = list(builder.Generate())
+    lines = gmock_class._GenerateMocks(filename, cpp_source, ast_list, None)
+    return '\n'.join(lines)
+
+  def testNamespaces(self):
+    source = """
+namespace Foo {
+namespace Bar { class Forward; }
+namespace Baz {
+
+class Test {
+ public:
+  virtual void Foo();
+};
+
+}  // namespace Baz
+}  // namespace Foo
+"""
+    expected = """\
+namespace Foo {
+namespace Baz {
+
+class MockTest : public Test {
+public:
+MOCK_METHOD0(Foo,
+void());
+};
+
+}  // namespace Baz
+}  // namespace Foo
+"""
+    self.assertEqualIgnoreLeadingWhitespace(
+        expected, self.GenerateMocks(source))
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/third_party/gmock/scripts/generator/cpp/keywords.py b/third_party/gmock/scripts/generator/cpp/keywords.py
new file mode 100644
index 0000000..f694450
--- /dev/null
+++ b/third_party/gmock/scripts/generator/cpp/keywords.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Neal Norwitz
+# Portions Copyright 2007 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""C++ keywords and helper utilities for determining keywords."""
+
+__author__ = 'nnorwitz@google.com (Neal Norwitz)'
+
+
+try:
+    # Python 3.x
+    import builtins
+except ImportError:
+    # Python 2.x
+    import __builtin__ as builtins
+
+
+if not hasattr(builtins, 'set'):
+    # Nominal support for Python 2.3.
+    from sets import Set as set
+
+
+TYPES = set('bool char int long short double float void wchar_t unsigned signed'.split())
+TYPE_MODIFIERS = set('auto register const inline extern static virtual volatile mutable'.split())
+ACCESS = set('public protected private friend'.split())
+
+CASTS = set('static_cast const_cast dynamic_cast reinterpret_cast'.split())
+
+OTHERS = set('true false asm class namespace using explicit this operator sizeof'.split())
+OTHER_TYPES = set('new delete typedef struct union enum typeid typename template'.split())
+
+CONTROL = set('case switch default if else return goto'.split())
+EXCEPTION = set('try catch throw'.split())
+LOOP = set('while do for break continue'.split())
+
+ALL = TYPES | TYPE_MODIFIERS | ACCESS | CASTS | OTHERS | OTHER_TYPES | CONTROL | EXCEPTION | LOOP
+
+
+def IsKeyword(token):
+    return token in ALL
+
+def IsBuiltinType(token):
+    if token in ('virtual', 'inline'):
+        # These only apply to methods, they can't be types by themselves.
+        return False
+    return token in TYPES or token in TYPE_MODIFIERS
diff --git a/third_party/gmock/scripts/generator/cpp/tokenize.py b/third_party/gmock/scripts/generator/cpp/tokenize.py
new file mode 100644
index 0000000..28c3345
--- /dev/null
+++ b/third_party/gmock/scripts/generator/cpp/tokenize.py
@@ -0,0 +1,287 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Neal Norwitz
+# Portions Copyright 2007 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Tokenize C++ source code."""
+
+__author__ = 'nnorwitz@google.com (Neal Norwitz)'
+
+
+try:
+    # Python 3.x
+    import builtins
+except ImportError:
+    # Python 2.x
+    import __builtin__ as builtins
+
+
+import sys
+
+from cpp import utils
+
+
+if not hasattr(builtins, 'set'):
+    # Nominal support for Python 2.3.
+    from sets import Set as set
+
+
+# Add $ as a valid identifier char since so much code uses it.
+_letters = 'abcdefghijklmnopqrstuvwxyz'
+VALID_IDENTIFIER_CHARS = set(_letters + _letters.upper() + '_0123456789$')
+HEX_DIGITS = set('0123456789abcdefABCDEF')
+INT_OR_FLOAT_DIGITS = set('01234567890eE-+')
+
+
+# C++0x string preffixes.
+_STR_PREFIXES = set(('R', 'u8', 'u8R', 'u', 'uR', 'U', 'UR', 'L', 'LR'))
+
+
+# Token types.
+UNKNOWN = 'UNKNOWN'
+SYNTAX = 'SYNTAX'
+CONSTANT = 'CONSTANT'
+NAME = 'NAME'
+PREPROCESSOR = 'PREPROCESSOR'
+
+# Where the token originated from.  This can be used for backtracking.
+# It is always set to WHENCE_STREAM in this code.
+WHENCE_STREAM, WHENCE_QUEUE = range(2)
+
+
+class Token(object):
+    """Data container to represent a C++ token.
+
+    Tokens can be identifiers, syntax char(s), constants, or
+    pre-processor directives.
+
+    start contains the index of the first char of the token in the source
+    end contains the index of the last char of the token in the source
+    """
+
+    def __init__(self, token_type, name, start, end):
+        self.token_type = token_type
+        self.name = name
+        self.start = start
+        self.end = end
+        self.whence = WHENCE_STREAM
+
+    def __str__(self):
+        if not utils.DEBUG:
+            return 'Token(%r)' % self.name
+        return 'Token(%r, %s, %s)' % (self.name, self.start, self.end)
+
+    __repr__ = __str__
+
+
+def _GetString(source, start, i):
+    i = source.find('"', i+1)
+    while source[i-1] == '\\':
+        # Count the trailing backslashes.
+        backslash_count = 1
+        j = i - 2
+        while source[j] == '\\':
+            backslash_count += 1
+            j -= 1
+        # When trailing backslashes are even, they escape each other.
+        if (backslash_count % 2) == 0:
+            break
+        i = source.find('"', i+1)
+    return i + 1
+
+
+def _GetChar(source, start, i):
+    # NOTE(nnorwitz): may not be quite correct, should be good enough.
+    i = source.find("'", i+1)
+    while source[i-1] == '\\':
+        # Need to special case '\\'.
+        if (i - 2) > start and source[i-2] == '\\':
+            break
+        i = source.find("'", i+1)
+    # Try to handle unterminated single quotes (in a #if 0 block).
+    if i < 0:
+        i = start
+    return i + 1
+
+
+def GetTokens(source):
+    """Returns a sequence of Tokens.
+
+    Args:
+      source: string of C++ source code.
+
+    Yields:
+      Token that represents the next token in the source.
+    """
+    # Cache various valid character sets for speed.
+    valid_identifier_chars = VALID_IDENTIFIER_CHARS
+    hex_digits = HEX_DIGITS
+    int_or_float_digits = INT_OR_FLOAT_DIGITS
+    int_or_float_digits2 = int_or_float_digits | set('.')
+
+    # Only ignore errors while in a #if 0 block.
+    ignore_errors = False
+    count_ifs = 0
+
+    i = 0
+    end = len(source)
+    while i < end:
+        # Skip whitespace.
+        while i < end and source[i].isspace():
+            i += 1
+        if i >= end:
+            return
+
+        token_type = UNKNOWN
+        start = i
+        c = source[i]
+        if c.isalpha() or c == '_':              # Find a string token.
+            token_type = NAME
+            while source[i] in valid_identifier_chars:
+                i += 1
+            # String and character constants can look like a name if
+            # they are something like L"".
+            if (source[i] == "'" and (i - start) == 1 and
+                source[start:i] in 'uUL'):
+                # u, U, and L are valid C++0x character preffixes.
+                token_type = CONSTANT
+                i = _GetChar(source, start, i)
+            elif source[i] == "'" and source[start:i] in _STR_PREFIXES:
+                token_type = CONSTANT
+                i = _GetString(source, start, i)
+        elif c == '/' and source[i+1] == '/':    # Find // comments.
+            i = source.find('\n', i)
+            if i == -1:  # Handle EOF.
+                i = end
+            continue
+        elif c == '/' and source[i+1] == '*':    # Find /* comments. */
+            i = source.find('*/', i) + 2
+            continue
+        elif c in ':+-<>&|*=':                   # : or :: (plus other chars).
+            token_type = SYNTAX
+            i += 1
+            new_ch = source[i]
+            if new_ch == c:
+                i += 1
+            elif c == '-' and new_ch == '>':
+                i += 1
+            elif new_ch == '=':
+                i += 1
+        elif c in '()[]{}~!?^%;/.,':             # Handle single char tokens.
+            token_type = SYNTAX
+            i += 1
+            if c == '.' and source[i].isdigit():
+                token_type = CONSTANT
+                i += 1
+                while source[i] in int_or_float_digits:
+                    i += 1
+                # Handle float suffixes.
+                for suffix in ('l', 'f'):
+                    if suffix == source[i:i+1].lower():
+                        i += 1
+                        break
+        elif c.isdigit():                        # Find integer.
+            token_type = CONSTANT
+            if c == '0' and source[i+1] in 'xX':
+                # Handle hex digits.
+                i += 2
+                while source[i] in hex_digits:
+                    i += 1
+            else:
+                while source[i] in int_or_float_digits2:
+                    i += 1
+            # Handle integer (and float) suffixes.
+            for suffix in ('ull', 'll', 'ul', 'l', 'f', 'u'):
+                size = len(suffix)
+                if suffix == source[i:i+size].lower():
+                    i += size
+                    break
+        elif c == '"':                           # Find string.
+            token_type = CONSTANT
+            i = _GetString(source, start, i)
+        elif c == "'":                           # Find char.
+            token_type = CONSTANT
+            i = _GetChar(source, start, i)
+        elif c == '#':                           # Find pre-processor command.
+            token_type = PREPROCESSOR
+            got_if = source[i:i+3] == '#if' and source[i+3:i+4].isspace()
+            if got_if:
+                count_ifs += 1
+            elif source[i:i+6] == '#endif':
+                count_ifs -= 1
+                if count_ifs == 0:
+                    ignore_errors = False
+
+            # TODO(nnorwitz): handle preprocessor statements (\ continuations).
+            while 1:
+                i1 = source.find('\n', i)
+                i2 = source.find('//', i)
+                i3 = source.find('/*', i)
+                i4 = source.find('"', i)
+                # NOTE(nnorwitz): doesn't handle comments in #define macros.
+                # Get the first important symbol (newline, comment, EOF/end).
+                i = min([x for x in (i1, i2, i3, i4, end) if x != -1])
+
+                # Handle #include "dir//foo.h" properly.
+                if source[i] == '"':
+                    i = source.find('"', i+1) + 1
+                    assert i > 0
+                    continue
+                # Keep going if end of the line and the line ends with \.
+                if not (i == i1 and source[i-1] == '\\'):
+                    if got_if:
+                        condition = source[start+4:i].lstrip()
+                        if (condition.startswith('0') or
+                            condition.startswith('(0)')):
+                            ignore_errors = True
+                    break
+                i += 1
+        elif c == '\\':                          # Handle \ in code.
+            # This is different from the pre-processor \ handling.
+            i += 1
+            continue
+        elif ignore_errors:
+            # The tokenizer seems to be in pretty good shape.  This
+            # raise is conditionally disabled so that bogus code
+            # in an #if 0 block can be handled.  Since we will ignore
+            # it anyways, this is probably fine.  So disable the
+            # exception and  return the bogus char.
+            i += 1
+        else:
+            sys.stderr.write('Got invalid token in %s @ %d token:%s: %r\n' %
+                             ('?', i, c, source[i-10:i+10]))
+            raise RuntimeError('unexpected token')
+
+        if i <= 0:
+            print('Invalid index, exiting now.')
+            return
+        yield Token(token_type, source[start:i], start, i)
+
+
+if __name__ == '__main__':
+    def main(argv):
+        """Driver mostly for testing purposes."""
+        for filename in argv[1:]:
+            source = utils.ReadFile(filename)
+            if source is None:
+                continue
+
+            for token in GetTokens(source):
+                print('%-12s: %s' % (token.token_type, token.name))
+                # print('\r%6.2f%%' % (100.0 * index / token.end),)
+            sys.stdout.write('\n')
+
+
+    main(sys.argv)
diff --git a/third_party/gmock/scripts/generator/cpp/utils.py b/third_party/gmock/scripts/generator/cpp/utils.py
new file mode 100644
index 0000000..eab36ee
--- /dev/null
+++ b/third_party/gmock/scripts/generator/cpp/utils.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Neal Norwitz
+# Portions Copyright 2007 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Generic utilities for C++ parsing."""
+
+__author__ = 'nnorwitz@google.com (Neal Norwitz)'
+
+
+import sys
+
+
+# Set to True to see the start/end token indices.
+DEBUG = True
+
+
+def ReadFile(filename, print_error=True):
+    """Returns the contents of a file."""
+    try:
+        fp = open(filename)
+        try:
+            return fp.read()
+        finally:
+            fp.close()
+    except IOError:
+        if print_error:
+            print('Error reading %s: %s' % (filename, sys.exc_info()[1]))
+        return None
diff --git a/third_party/gmock/scripts/generator/gmock_gen.py b/third_party/gmock/scripts/generator/gmock_gen.py
new file mode 100644
index 0000000..8cc0d13
--- /dev/null
+++ b/third_party/gmock/scripts/generator/gmock_gen.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+#
+# Copyright 2008 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Driver for starting up Google Mock class generator."""
+
+__author__ = 'nnorwitz@google.com (Neal Norwitz)'
+
+import os
+import sys
+
+if __name__ == '__main__':
+  # Add the directory of this script to the path so we can import gmock_class.
+  sys.path.append(os.path.dirname(__file__))
+
+  from cpp import gmock_class
+  # Fix the docstring in case they require the usage.
+  gmock_class.__doc__ = gmock_class.__doc__.replace('gmock_class.py', __file__)
+  gmock_class.main()
diff --git a/third_party/gmock/scripts/gmock_doctor.py b/third_party/gmock/scripts/gmock_doctor.py
new file mode 100644
index 0000000..bc814ad
--- /dev/null
+++ b/third_party/gmock/scripts/gmock_doctor.py
@@ -0,0 +1,519 @@
+#!/usr/bin/env python
+#
+# 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.
+
+"""Converts gcc errors in code using Google Mock to plain English."""
+
+__author__ = 'wan@google.com (Zhanyong Wan)'
+
+import re
+import sys
+
+_VERSION = '1.0.3'
+
+_COMMON_GMOCK_SYMBOLS = [
+    # Matchers
+    '_',
+    'A',
+    'AddressSatisfies',
+    'AllOf',
+    'An',
+    'AnyOf',
+    'ContainerEq',
+    'Contains',
+    'ContainsRegex',
+    'DoubleEq',
+    'ElementsAre',
+    'ElementsAreArray',
+    'EndsWith',
+    'Eq',
+    'Field',
+    'FloatEq',
+    'Ge',
+    'Gt',
+    'HasSubstr',
+    'IsInitializedProto',
+    'Le',
+    'Lt',
+    'MatcherCast',
+    'Matches',
+    'MatchesRegex',
+    'NanSensitiveDoubleEq',
+    'NanSensitiveFloatEq',
+    'Ne',
+    'Not',
+    'NotNull',
+    'Pointee',
+    'Property',
+    'Ref',
+    'ResultOf',
+    'SafeMatcherCast',
+    'StartsWith',
+    'StrCaseEq',
+    'StrCaseNe',
+    'StrEq',
+    'StrNe',
+    'Truly',
+    'TypedEq',
+    'Value',
+
+    # Actions
+    'Assign',
+    'ByRef',
+    'DeleteArg',
+    'DoAll',
+    'DoDefault',
+    'IgnoreResult',
+    'Invoke',
+    'InvokeArgument',
+    'InvokeWithoutArgs',
+    'Return',
+    'ReturnNew',
+    'ReturnNull',
+    'ReturnRef',
+    'SaveArg',
+    'SetArgReferee',
+    'SetArgumentPointee',
+    'SetArrayArgument',
+    'SetErrnoAndReturn',
+    'Throw',
+    'WithArg',
+    'WithArgs',
+    'WithoutArgs',
+
+    # Cardinalities
+    'AnyNumber',
+    'AtLeast',
+    'AtMost',
+    'Between',
+    'Exactly',
+
+    # Sequences
+    'InSequence',
+    'Sequence',
+
+    # Misc
+    'DefaultValue',
+    'Mock',
+    ]
+
+# Regex for matching source file path and line number in gcc's errors.
+_FILE_LINE_RE = r'(?P<file>.*):(?P<line>\d+):\s+'
+
+
+def _FindAllMatches(regex, s):
+  """Generates all matches of regex in string s."""
+
+  r = re.compile(regex)
+  return r.finditer(s)
+
+
+def _GenericDiagnoser(short_name, long_name, regex, diagnosis, msg):
+  """Diagnoses the given disease by pattern matching.
+
+  Args:
+    short_name: Short name of the disease.
+    long_name:  Long name of the disease.
+    regex:      Regex for matching the symptoms.
+    diagnosis:  Pattern for formatting the diagnosis.
+    msg:        Gcc's error messages.
+  Yields:
+    Tuples of the form
+      (short name of disease, long name of disease, diagnosis).
+  """
+
+  diagnosis = '%(file)s:%(line)s:' + diagnosis
+  for m in _FindAllMatches(regex, msg):
+    yield (short_name, long_name, diagnosis % m.groupdict())
+
+
+def _NeedToReturnReferenceDiagnoser(msg):
+  """Diagnoses the NRR disease, given the error messages by gcc."""
+
+  regex = (r'In member function \'testing::internal::ReturnAction<R>.*\n'
+           + _FILE_LINE_RE + r'instantiated from here\n'
+           r'.*gmock-actions\.h.*error: creating array with negative size')
+  diagnosis = """
+You are using an Return() action in a function that returns a reference.
+Please use ReturnRef() instead."""
+  return _GenericDiagnoser('NRR', 'Need to Return Reference',
+                           regex, diagnosis, msg)
+
+
+def _NeedToReturnSomethingDiagnoser(msg):
+  """Diagnoses the NRS disease, given the error messages by gcc."""
+
+  regex = (_FILE_LINE_RE +
+           r'(instantiated from here\n.'
+           r'*gmock.*actions\.h.*error: void value not ignored)'
+           r'|(error: control reaches end of non-void function)')
+  diagnosis = """
+You are using an action that returns void, but it needs to return
+*something*.  Please tell it *what* to return.  Perhaps you can use
+the pattern DoAll(some_action, Return(some_value))?"""
+  return _GenericDiagnoser('NRS', 'Need to Return Something',
+                           regex, diagnosis, msg)
+
+
+def _NeedToReturnNothingDiagnoser(msg):
+  """Diagnoses the NRN disease, given the error messages by gcc."""
+
+  regex = (_FILE_LINE_RE + r'instantiated from here\n'
+           r'.*gmock-actions\.h.*error: instantiation of '
+           r'\'testing::internal::ReturnAction<R>::Impl<F>::value_\' '
+           r'as type \'void\'')
+  diagnosis = """
+You are using an action that returns *something*, but it needs to return
+void.  Please use a void-returning action instead.
+
+All actions but the last in DoAll(...) must return void.  Perhaps you need
+to re-arrange the order of actions in a DoAll(), if you are using one?"""
+  return _GenericDiagnoser('NRN', 'Need to Return Nothing',
+                           regex, diagnosis, msg)
+
+
+def _IncompleteByReferenceArgumentDiagnoser(msg):
+  """Diagnoses the IBRA disease, given the error messages by gcc."""
+
+  regex = (_FILE_LINE_RE + r'instantiated from here\n'
+           r'.*gmock-printers\.h.*error: invalid application of '
+           r'\'sizeof\' to incomplete type \'(?P<type>.*)\'')
+  diagnosis = """
+In order to mock this function, Google Mock needs to see the definition
+of type "%(type)s" - declaration alone is not enough.  Either #include
+the header that defines it, or change the argument to be passed
+by pointer."""
+  return _GenericDiagnoser('IBRA', 'Incomplete By-Reference Argument Type',
+                           regex, diagnosis, msg)
+
+
+def _OverloadedFunctionMatcherDiagnoser(msg):
+  """Diagnoses the OFM disease, given the error messages by gcc."""
+
+  regex = (_FILE_LINE_RE + r'error: no matching function for '
+           r'call to \'Truly\(<unresolved overloaded function type>\)')
+  diagnosis = """
+The argument you gave to Truly() is an overloaded function.  Please tell
+gcc which overloaded version you want to use.
+
+For example, if you want to use the version whose signature is
+  bool Foo(int n);
+you should write
+  Truly(static_cast<bool (*)(int n)>(Foo))"""
+  return _GenericDiagnoser('OFM', 'Overloaded Function Matcher',
+                           regex, diagnosis, msg)
+
+
+def _OverloadedFunctionActionDiagnoser(msg):
+  """Diagnoses the OFA disease, given the error messages by gcc."""
+
+  regex = (_FILE_LINE_RE + r'error: no matching function for call to \'Invoke\('
+           r'<unresolved overloaded function type>')
+  diagnosis = """
+You are passing an overloaded function to Invoke().  Please tell gcc
+which overloaded version you want to use.
+
+For example, if you want to use the version whose signature is
+  bool MyFunction(int n, double x);
+you should write something like
+  Invoke(static_cast<bool (*)(int n, double x)>(MyFunction))"""
+  return _GenericDiagnoser('OFA', 'Overloaded Function Action',
+                           regex, diagnosis, msg)
+
+
+def _OverloadedMethodActionDiagnoser1(msg):
+  """Diagnoses the OMA disease, given the error messages by gcc."""
+
+  regex = (_FILE_LINE_RE + r'error: '
+           r'.*no matching function for call to \'Invoke\(.*, '
+           r'unresolved overloaded function type>')
+  diagnosis = """
+The second argument you gave to Invoke() is an overloaded method.  Please
+tell gcc which overloaded version you want to use.
+
+For example, if you want to use the version whose signature is
+  class Foo {
+    ...
+    bool Bar(int n, double x);
+  };
+you should write something like
+  Invoke(foo, static_cast<bool (Foo::*)(int n, double x)>(&Foo::Bar))"""
+  return _GenericDiagnoser('OMA', 'Overloaded Method Action',
+                           regex, diagnosis, msg)
+
+
+def _MockObjectPointerDiagnoser(msg):
+  """Diagnoses the MOP disease, given the error messages by gcc."""
+
+  regex = (_FILE_LINE_RE + r'error: request for member '
+           r'\'gmock_(?P<method>.+)\' in \'(?P<mock_object>.+)\', '
+           r'which is of non-class type \'(.*::)*(?P<class_name>.+)\*\'')
+  diagnosis = """
+The first argument to ON_CALL() and EXPECT_CALL() must be a mock *object*,
+not a *pointer* to it.  Please write '*(%(mock_object)s)' instead of
+'%(mock_object)s' as your first argument.
+
+For example, given the mock class:
+
+  class %(class_name)s : public ... {
+    ...
+    MOCK_METHOD0(%(method)s, ...);
+  };
+
+and the following mock instance:
+
+  %(class_name)s* mock_ptr = ...
+
+you should use the EXPECT_CALL like this:
+
+  EXPECT_CALL(*mock_ptr, %(method)s(...));"""
+  return _GenericDiagnoser('MOP', 'Mock Object Pointer',
+                           regex, diagnosis, msg)
+
+
+def _OverloadedMethodActionDiagnoser2(msg):
+  """Diagnoses the OMA disease, given the error messages by gcc."""
+
+  regex = (_FILE_LINE_RE + r'error: no matching function for '
+           r'call to \'Invoke\(.+, <unresolved overloaded function type>\)')
+  diagnosis = """
+The second argument you gave to Invoke() is an overloaded method.  Please
+tell gcc which overloaded version you want to use.
+
+For example, if you want to use the version whose signature is
+  class Foo {
+    ...
+    bool Bar(int n, double x);
+  };
+you should write something like
+  Invoke(foo, static_cast<bool (Foo::*)(int n, double x)>(&Foo::Bar))"""
+  return _GenericDiagnoser('OMA', 'Overloaded Method Action',
+                           regex, diagnosis, msg)
+
+
+def _NeedToUseSymbolDiagnoser(msg):
+  """Diagnoses the NUS disease, given the error messages by gcc."""
+
+  regex = (_FILE_LINE_RE + r'error: \'(?P<symbol>.+)\' '
+           r'(was not declared in this scope|has not been declared)')
+  diagnosis = """
+'%(symbol)s' is defined by Google Mock in the testing namespace.
+Did you forget to write
+  using testing::%(symbol)s;
+?"""
+  for m in _FindAllMatches(regex, msg):
+    symbol = m.groupdict()['symbol']
+    if symbol in _COMMON_GMOCK_SYMBOLS:
+      yield ('NUS', 'Need to Use Symbol', diagnosis % m.groupdict())
+
+
+def _NeedToUseReturnNullDiagnoser(msg):
+  """Diagnoses the NRNULL disease, given the error messages by gcc."""
+
+  regex = ('instantiated from \'testing::internal::ReturnAction<R>'
+           '::operator testing::Action<Func>\(\) const.*\n' +
+           _FILE_LINE_RE + r'instantiated from here\n'
+           r'.*error: no matching function for call to \'implicit_cast\('
+           r'long int&\)')
+  diagnosis = """
+You are probably calling Return(NULL) and the compiler isn't sure how to turn
+NULL into the right type. Use ReturnNull() instead.
+Note: the line number may be off; please fix all instances of Return(NULL)."""
+  return _GenericDiagnoser('NRNULL', 'Need to use ReturnNull',
+                           regex, diagnosis, msg)
+
+
+_TTB_DIAGNOSIS = """
+In a mock class template, types or typedefs defined in the base class
+template are *not* automatically visible.  This is how C++ works.  Before
+you can use a type or typedef named %(type)s defined in base class Base<T>, you
+need to make it visible.  One way to do it is:
+
+  typedef typename Base<T>::%(type)s %(type)s;"""
+
+
+def _TypeInTemplatedBaseDiagnoser1(msg):
+  """Diagnoses the TTB disease, given the error messages by gcc.
+
+  This version works when the type is used as the mock function's return
+  type.
+  """
+
+  gcc_4_3_1_regex = (
+      r'In member function \'int .*\n' + _FILE_LINE_RE +
+      r'error: a function call cannot appear in a constant-expression')
+  gcc_4_4_0_regex = (
+      r'error: a function call cannot appear in a constant-expression'
+      + _FILE_LINE_RE + r'error: template argument 1 is invalid\n')
+  diagnosis = _TTB_DIAGNOSIS % {'type': 'Foo'}
+  return (list(_GenericDiagnoser('TTB', 'Type in Template Base',
+                                gcc_4_3_1_regex, diagnosis, msg)) +
+          list(_GenericDiagnoser('TTB', 'Type in Template Base',
+                                 gcc_4_4_0_regex, diagnosis, msg)))
+
+
+def _TypeInTemplatedBaseDiagnoser2(msg):
+  """Diagnoses the TTB disease, given the error messages by gcc.
+
+  This version works when the type is used as the mock function's sole
+  parameter type.
+  """
+
+  regex = (_FILE_LINE_RE +
+           r'error: \'(?P<type>.+)\' was not declared in this scope\n'
+           r'.*error: template argument 1 is invalid\n')
+  return _GenericDiagnoser('TTB', 'Type in Template Base',
+                           regex, _TTB_DIAGNOSIS, msg)
+
+
+def _TypeInTemplatedBaseDiagnoser3(msg):
+  """Diagnoses the TTB disease, given the error messages by gcc.
+
+  This version works when the type is used as a parameter of a mock
+  function that has multiple parameters.
+  """
+
+  regex = (r'error: expected `;\' before \'::\' token\n'
+           + _FILE_LINE_RE +
+           r'error: \'(?P<type>.+)\' was not declared in this scope\n'
+           r'.*error: template argument 1 is invalid\n'
+           r'.*error: \'.+\' was not declared in this scope')
+  return _GenericDiagnoser('TTB', 'Type in Template Base',
+                           regex, _TTB_DIAGNOSIS, msg)
+
+
+def _WrongMockMethodMacroDiagnoser(msg):
+  """Diagnoses the WMM disease, given the error messages by gcc."""
+
+  regex = (_FILE_LINE_RE +
+           r'.*this_method_does_not_take_(?P<wrong_args>\d+)_argument.*\n'
+           r'.*\n'
+           r'.*candidates are.*FunctionMocker<[^>]+A(?P<args>\d+)\)>')
+  diagnosis = """
+You are using MOCK_METHOD%(wrong_args)s to define a mock method that has
+%(args)s arguments. Use MOCK_METHOD%(args)s (or MOCK_CONST_METHOD%(args)s,
+MOCK_METHOD%(args)s_T, MOCK_CONST_METHOD%(args)s_T as appropriate) instead."""
+  return _GenericDiagnoser('WMM', 'Wrong MOCK_METHODn Macro',
+                           regex, diagnosis, msg)
+
+
+def _WrongParenPositionDiagnoser(msg):
+  """Diagnoses the WPP disease, given the error messages by gcc."""
+
+  regex = (_FILE_LINE_RE +
+           r'error:.*testing::internal::MockSpec<.* has no member named \''
+           r'(?P<method>\w+)\'')
+  diagnosis = """
+The closing parenthesis of ON_CALL or EXPECT_CALL should be *before*
+".%(method)s".  For example, you should write:
+  EXPECT_CALL(my_mock, Foo(_)).%(method)s(...);
+instead of:
+  EXPECT_CALL(my_mock, Foo(_).%(method)s(...));"""
+  return _GenericDiagnoser('WPP', 'Wrong Parenthesis Position',
+                           regex, diagnosis, msg)
+
+
+_DIAGNOSERS = [
+    _IncompleteByReferenceArgumentDiagnoser,
+    _MockObjectPointerDiagnoser,
+    _NeedToReturnNothingDiagnoser,
+    _NeedToReturnReferenceDiagnoser,
+    _NeedToReturnSomethingDiagnoser,
+    _NeedToUseReturnNullDiagnoser,
+    _NeedToUseSymbolDiagnoser,
+    _OverloadedFunctionActionDiagnoser,
+    _OverloadedFunctionMatcherDiagnoser,
+    _OverloadedMethodActionDiagnoser1,
+    _OverloadedMethodActionDiagnoser2,
+    _TypeInTemplatedBaseDiagnoser1,
+    _TypeInTemplatedBaseDiagnoser2,
+    _TypeInTemplatedBaseDiagnoser3,
+    _WrongMockMethodMacroDiagnoser,
+    _WrongParenPositionDiagnoser,
+    ]
+
+
+def Diagnose(msg):
+  """Generates all possible diagnoses given the gcc error message."""
+
+  diagnoses = []
+  for diagnoser in _DIAGNOSERS:
+    for diag in diagnoser(msg):
+      diagnosis = '[%s - %s]\n%s' % diag
+      if not diagnosis in diagnoses:
+        diagnoses.append(diagnosis)
+  return diagnoses
+
+
+def main():
+  print ('Google Mock Doctor v%s - '
+         'diagnoses problems in code using Google Mock.' % _VERSION)
+
+  if sys.stdin.isatty():
+    print ('Please copy and paste the compiler errors here.  Press c-D when '
+           'you are done:')
+  else:
+    print 'Waiting for compiler errors on stdin . . .'
+
+  msg = sys.stdin.read().strip()
+  diagnoses = Diagnose(msg)
+  count = len(diagnoses)
+  if not count:
+    print '\nGcc complained:'
+    print '8<------------------------------------------------------------'
+    print msg
+    print '------------------------------------------------------------>8'
+    print """
+Uh-oh, I'm not smart enough to figure out what the problem is. :-(
+However...
+If you send your source code and gcc's error messages to
+googlemock@googlegroups.com, you can be helped and I can get smarter --
+win-win for us!"""
+  else:
+    print '------------------------------------------------------------'
+    print 'Your code appears to have the following',
+    if count > 1:
+      print '%s diseases:' % (count,)
+    else:
+      print 'disease:'
+    i = 0
+    for d in diagnoses:
+      i += 1
+      if count > 1:
+        print '\n#%s:' % (i,)
+      print d
+    print """
+How did I do?  If you think I'm wrong or unhelpful, please send your
+source code and gcc's error messages to googlemock@googlegroups.com.  Then
+you can be helped and I can get smarter -- I promise I won't be upset!"""
+
+
+if __name__ == '__main__':
+  main()
diff --git a/third_party/gmock/src/gmock-all.cc b/third_party/gmock/src/gmock-all.cc
new file mode 100644
index 0000000..c9223fc
--- /dev/null
+++ b/third_party/gmock/src/gmock-all.cc
@@ -0,0 +1,48 @@
+// 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+//
+// Google C++ Mocking Framework (Google Mock)
+//
+// This file #includes all Google Mock implementation .cc files.  The
+// purpose is to allow a user to build Google Mock by compiling this
+// file alone.
+
+// This line ensures that gmock.h can be compiled on its own, even
+// when it's fused.
+#include <gmock/gmock.h>
+
+// The following lines pull in the real gmock *.cc files.
+#include "src/gmock-cardinalities.cc"
+#include "src/gmock-internal-utils.cc"
+#include "src/gmock-matchers.cc"
+#include "src/gmock-printers.cc"
+#include "src/gmock-spec-builders.cc"
+#include "src/gmock.cc"
diff --git a/third_party/gmock/src/gmock-cardinalities.cc b/third_party/gmock/src/gmock-cardinalities.cc
new file mode 100644
index 0000000..07eed46
--- /dev/null
+++ b/third_party/gmock/src/gmock-cardinalities.cc
@@ -0,0 +1,155 @@
+// Copyright 2007, 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements cardinalities.
+
+#include <gmock/gmock-cardinalities.h>
+
+#include <limits.h>
+#include <ostream>  // NOLINT
+#include <sstream>
+#include <string>
+#include <gmock/internal/gmock-internal-utils.h>
+#include <gtest/gtest.h>
+
+namespace testing {
+
+namespace {
+
+// Implements the Between(m, n) cardinality.
+class BetweenCardinalityImpl : public CardinalityInterface {
+ public:
+  BetweenCardinalityImpl(int min, int max)
+      : min_(min >= 0 ? min : 0),
+        max_(max >= min_ ? max : min_) {
+    std::stringstream ss;
+    if (min < 0) {
+      ss << "The invocation lower bound must be >= 0, "
+         << "but is actually " << min << ".";
+      internal::Expect(false, __FILE__, __LINE__, ss.str());
+    } else if (max < 0) {
+      ss << "The invocation upper bound must be >= 0, "
+         << "but is actually " << max << ".";
+      internal::Expect(false, __FILE__, __LINE__, ss.str());
+    } else if (min > max) {
+      ss << "The invocation upper bound (" << max
+         << ") must be >= the invocation lower bound (" << min
+         << ").";
+      internal::Expect(false, __FILE__, __LINE__, ss.str());
+    }
+  }
+
+  // Conservative estimate on the lower/upper bound of the number of
+  // calls allowed.
+  virtual int ConservativeLowerBound() const { return min_; }
+  virtual int ConservativeUpperBound() const { return max_; }
+
+  virtual bool IsSatisfiedByCallCount(int call_count) const {
+    return min_ <= call_count && call_count <= max_ ;
+  }
+
+  virtual bool IsSaturatedByCallCount(int call_count) const {
+    return call_count >= max_;
+  }
+
+  virtual void DescribeTo(::std::ostream* os) const;
+ private:
+  const int min_;
+  const int max_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(BetweenCardinalityImpl);
+};
+
+// Formats "n times" in a human-friendly way.
+inline internal::string FormatTimes(int n) {
+  if (n == 1) {
+    return "once";
+  } else if (n == 2) {
+    return "twice";
+  } else {
+    std::stringstream ss;
+    ss << n << " times";
+    return ss.str();
+  }
+}
+
+// Describes the Between(m, n) cardinality in human-friendly text.
+void BetweenCardinalityImpl::DescribeTo(::std::ostream* os) const {
+  if (min_ == 0) {
+    if (max_ == 0) {
+      *os << "never called";
+    } else if (max_ == INT_MAX) {
+      *os << "called any number of times";
+    } else {
+      *os << "called at most " << FormatTimes(max_);
+    }
+  } else if (min_ == max_) {
+    *os << "called " << FormatTimes(min_);
+  } else if (max_ == INT_MAX) {
+    *os << "called at least " << FormatTimes(min_);
+  } else {
+    // 0 < min_ < max_ < INT_MAX
+    *os << "called between " << min_ << " and " << max_ << " times";
+  }
+}
+
+}  // Unnamed namespace
+
+// Describes the given call count to an ostream.
+void Cardinality::DescribeActualCallCountTo(int actual_call_count,
+                                            ::std::ostream* os) {
+  if (actual_call_count > 0) {
+    *os << "called " << FormatTimes(actual_call_count);
+  } else {
+    *os << "never called";
+  }
+}
+
+// Creates a cardinality that allows at least n calls.
+Cardinality AtLeast(int n) { return Between(n, INT_MAX); }
+
+// Creates a cardinality that allows at most n calls.
+Cardinality AtMost(int n) { return Between(0, n); }
+
+// Creates a cardinality that allows any number of calls.
+Cardinality AnyNumber() { return AtLeast(0); }
+
+// Creates a cardinality that allows between min and max calls.
+Cardinality Between(int min, int max) {
+  return Cardinality(new BetweenCardinalityImpl(min, max));
+}
+
+// Creates a cardinality that allows exactly n calls.
+Cardinality Exactly(int n) { return Between(n, n); }
+
+}  // namespace testing
diff --git a/third_party/gmock/src/gmock-internal-utils.cc b/third_party/gmock/src/gmock-internal-utils.cc
new file mode 100644
index 0000000..196ec74
--- /dev/null
+++ b/third_party/gmock/src/gmock-internal-utils.cc
@@ -0,0 +1,173 @@
+// Copyright 2007, 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file defines some utilities useful for implementing Google
+// Mock.  They are subject to change without notice, so please DO NOT
+// USE THEM IN USER CODE.
+
+#include <gmock/internal/gmock-internal-utils.h>
+
+#include <ctype.h>
+#include <ostream>  // NOLINT
+#include <string>
+#include <gmock/gmock.h>
+#include <gmock/internal/gmock-port.h>
+#include <gtest/gtest.h>
+
+namespace testing {
+namespace internal {
+
+// Converts an identifier name to a space-separated list of lower-case
+// words.  Each maximum substring of the form [A-Za-z][a-z]*|\d+ is
+// treated as one word.  For example, both "FooBar123" and
+// "foo_bar_123" are converted to "foo bar 123".
+string ConvertIdentifierNameToWords(const char* id_name) {
+  string result;
+  char prev_char = '\0';
+  for (const char* p = id_name; *p != '\0'; prev_char = *(p++)) {
+    // We don't care about the current locale as the input is
+    // guaranteed to be a valid C++ identifier name.
+    const bool starts_new_word = isupper(*p) ||
+        (!isalpha(prev_char) && islower(*p)) ||
+        (!isdigit(prev_char) && isdigit(*p));
+
+    if (isalnum(*p)) {
+      if (starts_new_word && result != "")
+        result += ' ';
+      result += tolower(*p);
+    }
+  }
+  return result;
+}
+
+// This class reports Google Mock failures as Google Test failures.  A
+// user can define another class in a similar fashion if he intends to
+// use Google Mock with a testing framework other than Google Test.
+class GoogleTestFailureReporter : public FailureReporterInterface {
+ public:
+  virtual void ReportFailure(FailureType type, const char* file, int line,
+                             const string& message) {
+    AssertHelper(type == FATAL ?
+                 TestPartResult::kFatalFailure :
+                 TestPartResult::kNonFatalFailure,
+                 file,
+                 line,
+                 message.c_str()) = Message();
+    if (type == FATAL) {
+      posix::Abort();
+    }
+  }
+};
+
+// Returns the global failure reporter.  Will create a
+// GoogleTestFailureReporter and return it the first time called.
+FailureReporterInterface* GetFailureReporter() {
+  // Points to the global failure reporter used by Google Mock.  gcc
+  // guarantees that the following use of failure_reporter is
+  // thread-safe.  We may need to add additional synchronization to
+  // protect failure_reporter if we port Google Mock to other
+  // compilers.
+  static FailureReporterInterface* const failure_reporter =
+      new GoogleTestFailureReporter();
+  return failure_reporter;
+}
+
+// Protects global resources (stdout in particular) used by Log().
+static Mutex g_log_mutex(Mutex::NO_CONSTRUCTOR_NEEDED_FOR_STATIC_MUTEX);
+
+// Returns true iff a log with the given severity is visible according
+// to the --gmock_verbose flag.
+bool LogIsVisible(LogSeverity severity) {
+  if (GMOCK_FLAG(verbose) == kInfoVerbosity) {
+    // Always show the log if --gmock_verbose=info.
+    return true;
+  } else if (GMOCK_FLAG(verbose) == kErrorVerbosity) {
+    // Always hide it if --gmock_verbose=error.
+    return false;
+  } else {
+    // If --gmock_verbose is neither "info" nor "error", we treat it
+    // as "warning" (its default value).
+    return severity == WARNING;
+  }
+}
+
+// Prints the given message to stdout iff 'severity' >= the level
+// specified by the --gmock_verbose flag.  If stack_frames_to_skip >=
+// 0, also prints the stack trace excluding the top
+// stack_frames_to_skip frames.  In opt mode, any positive
+// stack_frames_to_skip is treated as 0, since we don't know which
+// function calls will be inlined by the compiler and need to be
+// conservative.
+void Log(LogSeverity severity, const string& message,
+         int stack_frames_to_skip) {
+  if (!LogIsVisible(severity))
+    return;
+
+  // Ensures that logs from different threads don't interleave.
+  MutexLock l(&g_log_mutex);
+
+  // "using ::std::cout;" doesn't work with Symbian's STLport, where cout is a
+  // macro.
+
+  if (severity == WARNING) {
+    // Prints a GMOCK WARNING marker to make the warnings easily searchable.
+    std::cout << "\nGMOCK WARNING:";
+  }
+  // Pre-pends a new-line to message if it doesn't start with one.
+  if (message.empty() || message[0] != '\n') {
+    std::cout << "\n";
+  }
+  std::cout << message;
+  if (stack_frames_to_skip >= 0) {
+#ifdef NDEBUG
+    // In opt mode, we have to be conservative and skip no stack frame.
+    const int actual_to_skip = 0;
+#else
+    // In dbg mode, we can do what the caller tell us to do (plus one
+    // for skipping this function's stack frame).
+    const int actual_to_skip = stack_frames_to_skip + 1;
+#endif  // NDEBUG
+
+    // Appends a new-line to message if it doesn't end with one.
+    if (!message.empty() && *message.rbegin() != '\n') {
+      std::cout << "\n";
+    }
+    std::cout << "Stack trace:\n"
+         << ::testing::internal::GetCurrentOsStackTraceExceptTop(
+             ::testing::UnitTest::GetInstance(), actual_to_skip);
+  }
+  std::cout << ::std::flush;
+}
+
+}  // namespace internal
+}  // namespace testing
diff --git a/third_party/gmock/src/gmock-matchers.cc b/third_party/gmock/src/gmock-matchers.cc
new file mode 100644
index 0000000..0abca70
--- /dev/null
+++ b/third_party/gmock/src/gmock-matchers.cc
@@ -0,0 +1,190 @@
+// Copyright 2007, 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements Matcher<const string&>, Matcher<string>, and
+// utilities for defining matchers.
+
+#include <gmock/gmock-matchers.h>
+#include <gmock/gmock-generated-matchers.h>
+
+#include <string.h>
+#include <sstream>
+#include <string>
+
+namespace testing {
+
+// Constructs a matcher that matches a const string& whose value is
+// equal to s.
+Matcher<const internal::string&>::Matcher(const internal::string& s) {
+  *this = Eq(s);
+}
+
+// Constructs a matcher that matches a const string& whose value is
+// equal to s.
+Matcher<const internal::string&>::Matcher(const char* s) {
+  *this = Eq(internal::string(s));
+}
+
+// Constructs a matcher that matches a string whose value is equal to s.
+Matcher<internal::string>::Matcher(const internal::string& s) { *this = Eq(s); }
+
+// Constructs a matcher that matches a string whose value is equal to s.
+Matcher<internal::string>::Matcher(const char* s) {
+  *this = Eq(internal::string(s));
+}
+
+namespace internal {
+
+// Utilities for validating and formatting description strings in the
+// MATCHER*() macros.
+
+// Returns the 0-based index of the given parameter in the
+// NULL-terminated parameter array; if the parameter is "*", returns
+// kTupleInterpolation; if it's not found in the list, returns
+// kInvalidInterpolation.
+int GetParamIndex(const char* param_names[], const string& param_name) {
+  if (param_name == "*")
+    return kTupleInterpolation;
+
+  for (int i = 0; param_names[i] != NULL; i++) {
+    if (param_name == param_names[i])
+      return i;
+  }
+  return kInvalidInterpolation;
+}
+
+// Helper function used by ValidateMatcherDescription() to format
+// error messages.
+string FormatMatcherDescriptionSyntaxError(const char* description,
+                                           const char* error_pos) {
+  ::std::stringstream ss;
+  ss << "Syntax error at index " << (error_pos - description)
+     << " in matcher description \"" << description << "\": ";
+  return ss.str();
+}
+
+// Parses a matcher description string and returns a vector of
+// interpolations that appear in the string; generates non-fatal
+// failures iff 'description' is an invalid matcher description.
+// 'param_names' is a NULL-terminated array of parameter names in the
+// order they appear in the MATCHER_P*() parameter list.
+Interpolations ValidateMatcherDescription(
+    const char* param_names[], const char* description) {
+  Interpolations interps;
+  for (const char* p = description; *p != '\0';) {
+    if (SkipPrefix("%%", &p)) {
+      interps.push_back(Interpolation(p - 2, p, kPercentInterpolation));
+    } else if (SkipPrefix("%(", &p)) {
+      const char* const q = strstr(p, ")s");
+      if (q == NULL) {
+        // TODO(wan@google.com): change the source file location in
+        // the failure to point to where the MATCHER*() macro is used.
+        ADD_FAILURE() << FormatMatcherDescriptionSyntaxError(description, p - 2)
+                      << "an interpolation must end with \")s\", "
+                      << "but \"" << (p - 2) << "\" does not.";
+      } else {
+        const string param_name(p, q);
+        const int param_index = GetParamIndex(param_names, param_name);
+        if (param_index == kInvalidInterpolation) {
+          ADD_FAILURE() << FormatMatcherDescriptionSyntaxError(description, p)
+                        << "\"" << param_name
+                        << "\" is an invalid parameter name.";
+        } else {
+          interps.push_back(Interpolation(p - 2, q + 2, param_index));
+          p = q + 2;
+        }
+      }
+    } else {
+      EXPECT_NE(*p, '%') << FormatMatcherDescriptionSyntaxError(description, p)
+                         << "use \"%%\" instead of \"%\" to print \"%\".";
+      ++p;
+    }
+  }
+  return interps;
+}
+
+// Joins a vector of strings as if they are fields of a tuple; returns
+// the joined string.
+string JoinAsTuple(const Strings& fields) {
+  switch (fields.size()) {
+    case 0:
+      return "";
+    case 1:
+      return fields[0];
+    default:
+      string result = "(" + fields[0];
+      for (size_t i = 1; i < fields.size(); i++) {
+        result += ", ";
+        result += fields[i];
+      }
+      result += ")";
+      return result;
+  }
+}
+
+// Returns the actual matcher description, given the matcher name,
+// user-supplied description template string, interpolations in the
+// string, and the printed values of the matcher parameters.
+string FormatMatcherDescription(
+    const char* matcher_name, const char* description,
+    const Interpolations& interp, const Strings& param_values) {
+  string result;
+  if (*description == '\0') {
+    // When the user supplies an empty description, we calculate one
+    // from the matcher name.
+    result = ConvertIdentifierNameToWords(matcher_name);
+    if (param_values.size() >= 1)
+      result += " " + JoinAsTuple(param_values);
+  } else {
+    // The end position of the last interpolation.
+    const char* last_interp_end = description;
+    for (size_t i = 0; i < interp.size(); i++) {
+      result.append(last_interp_end, interp[i].start_pos);
+      const int param_index = interp[i].param_index;
+      if (param_index == kTupleInterpolation) {
+        result += JoinAsTuple(param_values);
+      } else if (param_index == kPercentInterpolation) {
+        result += '%';
+      } else if (param_index != kInvalidInterpolation) {
+        result += param_values[param_index];
+      }
+      last_interp_end = interp[i].end_pos;
+    }
+    result += last_interp_end;
+  }
+
+  return result;
+}
+
+}  // namespace internal
+}  // namespace testing
diff --git a/third_party/gmock/src/gmock-printers.cc b/third_party/gmock/src/gmock-printers.cc
new file mode 100644
index 0000000..8efba78
--- /dev/null
+++ b/third_party/gmock/src/gmock-printers.cc
@@ -0,0 +1,320 @@
+// Copyright 2007, 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements a universal value printer that can print a
+// value of any type T:
+//
+//   void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr);
+//
+// It uses the << operator when possible, and prints the bytes in the
+// object otherwise.  A user can override its behavior for a class
+// type Foo by defining either operator<<(::std::ostream&, const Foo&)
+// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that
+// defines Foo.
+
+#include <gmock/gmock-printers.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <ostream>  // NOLINT
+#include <string>
+#include <gmock/internal/gmock-port.h>
+
+namespace testing {
+
+namespace {
+
+using ::std::ostream;
+
+#if GTEST_OS_WINDOWS_MOBILE  // Windows CE does not define _snprintf_s.
+#define snprintf _snprintf
+#elif _MSC_VER >= 1400  // VC 8.0 and later deprecate snprintf and _snprintf.
+#define snprintf _snprintf_s
+#elif _MSC_VER
+#define snprintf _snprintf
+#endif  // GTEST_OS_WINDOWS_MOBILE
+
+// Prints a segment of bytes in the given object.
+void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start,
+                                size_t count, ostream* os) {
+  char text[5] = "";
+  for (size_t i = 0; i != count; i++) {
+    const size_t j = start + i;
+    if (i != 0) {
+      // Organizes the bytes into groups of 2 for easy parsing by
+      // human.
+      if ((j % 2) == 0) {
+        *os << " ";
+      }
+    }
+    snprintf(text, sizeof(text), "%02X", obj_bytes[j]);
+    *os << text;
+  }
+}
+
+// Prints the bytes in the given value to the given ostream.
+void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count,
+                              ostream* os) {
+  // Tells the user how big the object is.
+  *os << count << "-byte object <";
+
+  const size_t kThreshold = 132;
+  const size_t kChunkSize = 64;
+  // If the object size is bigger than kThreshold, we'll have to omit
+  // some details by printing only the first and the last kChunkSize
+  // bytes.
+  // TODO(wan): let the user control the threshold using a flag.
+  if (count < kThreshold) {
+    PrintByteSegmentInObjectTo(obj_bytes, 0, count, os);
+  } else {
+    PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os);
+    *os << " ... ";
+    // Rounds up to 2-byte boundary.
+    const size_t resume_pos = (count - kChunkSize + 1)/2*2;
+    PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os);
+  }
+  *os << ">";
+}
+
+}  // namespace
+
+namespace internal2 {
+
+// Delegates to PrintBytesInObjectToImpl() to print the bytes in the
+// given object.  The delegation simplifies the implementation, which
+// uses the << operator and thus is easier done outside of the
+// ::testing::internal namespace, which contains a << operator that
+// sometimes conflicts with the one in STL.
+void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count,
+                          ostream* os) {
+  PrintBytesInObjectToImpl(obj_bytes, count, os);
+}
+
+}  // namespace internal2
+
+namespace internal {
+
+// Prints a wide char as a char literal without the quotes, escaping it
+// when necessary.
+static void PrintAsWideCharLiteralTo(wchar_t c, ostream* os) {
+  switch (c) {
+    case L'\0':
+      *os << "\\0";
+      break;
+    case L'\'':
+      *os << "\\'";
+      break;
+    case L'\?':
+      *os << "\\?";
+      break;
+    case L'\\':
+      *os << "\\\\";
+      break;
+    case L'\a':
+      *os << "\\a";
+      break;
+    case L'\b':
+      *os << "\\b";
+      break;
+    case L'\f':
+      *os << "\\f";
+      break;
+    case L'\n':
+      *os << "\\n";
+      break;
+    case L'\r':
+      *os << "\\r";
+      break;
+    case L'\t':
+      *os << "\\t";
+      break;
+    case L'\v':
+      *os << "\\v";
+      break;
+    default:
+      // Checks whether c is printable or not. Printable characters are in
+      // the range [0x20,0x7E].
+      // We test the value of c directly instead of calling isprint(), as
+      // isprint() is buggy on Windows mobile.
+      if (0x20 <= c && c <= 0x7E) {
+        *os << static_cast<char>(c);
+      } else {
+        // Buffer size enough for the maximum number of digits and \0.
+        char text[2 * sizeof(unsigned long) + 1] = "";
+        snprintf(text, sizeof(text), "%lX", static_cast<unsigned long>(c));
+        *os << "\\x" << text;
+      }
+  }
+}
+
+// Prints a char as if it's part of a string literal, escaping it when
+// necessary.
+static void PrintAsWideStringLiteralTo(wchar_t c, ostream* os) {
+  switch (c) {
+    case L'\'':
+      *os << "'";
+      break;
+    case L'"':
+      *os << "\\\"";
+      break;
+    default:
+      PrintAsWideCharLiteralTo(c, os);
+  }
+}
+
+// Prints a char as a char literal without the quotes, escaping it
+// when necessary.
+static void PrintAsCharLiteralTo(char c, ostream* os) {
+  PrintAsWideCharLiteralTo(static_cast<unsigned char>(c), os);
+}
+
+// Prints a char as if it's part of a string literal, escaping it when
+// necessary.
+static void PrintAsStringLiteralTo(char c, ostream* os) {
+  PrintAsWideStringLiteralTo(static_cast<unsigned char>(c), os);
+}
+
+// Prints a char and its code.  The '\0' char is printed as "'\\0'",
+// other unprintable characters are also properly escaped using the
+// standard C++ escape sequence.
+void PrintCharTo(char c, int char_code, ostream* os) {
+  *os << "'";
+  PrintAsCharLiteralTo(c, os);
+  *os << "'";
+  if (c != '\0')
+    *os << " (" << char_code << ")";
+}
+
+// Prints a wchar_t as a symbol if it is printable or as its internal
+// code otherwise and also as its decimal code (except for L'\0').
+// The L'\0' char is printed as "L'\\0'". The decimal code is printed
+// as signed integer when wchar_t is implemented by the compiler
+// as a signed type and is printed as an unsigned integer when wchar_t
+// is implemented as an unsigned type.
+void PrintTo(wchar_t wc, ostream* os) {
+  *os << "L'";
+  PrintAsWideCharLiteralTo(wc, os);
+  *os << "'";
+  if (wc != L'\0') {
+    // Type Int64 is used because it provides more storage than wchar_t thus
+    // when the compiler converts signed or unsigned implementation of wchar_t
+    // to Int64 it fills higher bits with either zeros or the sign bit
+    // passing it to operator <<() as either signed or unsigned integer.
+    *os << " (" << static_cast<Int64>(wc) << ")";
+  }
+}
+
+// Prints the given array of characters to the ostream.
+// The array starts at *begin, the length is len, it may include '\0' characters
+// and may not be null-terminated.
+static void PrintCharsAsStringTo(const char* begin, size_t len, ostream* os) {
+  *os << "\"";
+  for (size_t index = 0; index < len; ++index) {
+    PrintAsStringLiteralTo(begin[index], os);
+  }
+  *os << "\"";
+}
+
+// Prints a (const) char array of 'len' elements, starting at address 'begin'.
+void UniversalPrintArray(const char* begin, size_t len, ostream* os) {
+  PrintCharsAsStringTo(begin, len, os);
+}
+
+// Prints the given array of wide characters to the ostream.
+// The array starts at *begin, the length is len, it may include L'\0'
+// characters and may not be null-terminated.
+static void PrintWideCharsAsStringTo(const wchar_t* begin, size_t len,
+                                     ostream* os) {
+  *os << "L\"";
+  for (size_t index = 0; index < len; ++index) {
+    PrintAsWideStringLiteralTo(begin[index], os);
+  }
+  *os << "\"";
+}
+
+// Prints the given C string to the ostream.
+void PrintTo(const char* s, ostream* os) {
+  if (s == NULL) {
+    *os << "NULL";
+  } else {
+    *os << implicit_cast<const void*>(s) << " pointing to ";
+    PrintCharsAsStringTo(s, strlen(s), os);
+  }
+}
+
+// MSVC compiler can be configured to define whar_t as a typedef
+// of unsigned short. Defining an overload for const wchar_t* in that case
+// would cause pointers to unsigned shorts be printed as wide strings,
+// possibly accessing more memory than intended and causing invalid
+// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when
+// wchar_t is implemented as a native type.
+#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
+// Prints the given wide C string to the ostream.
+void PrintTo(const wchar_t* s, ostream* os) {
+  if (s == NULL) {
+    *os << "NULL";
+  } else {
+    *os << implicit_cast<const void*>(s) << " pointing to ";
+    PrintWideCharsAsStringTo(s, wcslen(s), os);
+  }
+}
+#endif  // wchar_t is native
+
+// Prints a ::string object.
+#if GTEST_HAS_GLOBAL_STRING
+void PrintStringTo(const ::string& s, ostream* os) {
+  PrintCharsAsStringTo(s.data(), s.size(), os);
+}
+#endif  // GTEST_HAS_GLOBAL_STRING
+
+#if GTEST_HAS_STD_STRING
+void PrintStringTo(const ::std::string& s, ostream* os) {
+  PrintCharsAsStringTo(s.data(), s.size(), os);
+}
+#endif  // GTEST_HAS_STD_STRING
+
+// Prints a ::wstring object.
+#if GTEST_HAS_GLOBAL_WSTRING
+void PrintWideStringTo(const ::wstring& s, ostream* os) {
+  PrintWideCharsAsStringTo(s.data(), s.size(), os);
+}
+#endif  // GTEST_HAS_GLOBAL_WSTRING
+
+#if GTEST_HAS_STD_WSTRING
+void PrintWideStringTo(const ::std::wstring& s, ostream* os) {
+  PrintWideCharsAsStringTo(s.data(), s.size(), os);
+}
+#endif  // GTEST_HAS_STD_WSTRING
+
+}  // namespace internal
+
+}  // namespace testing
diff --git a/third_party/gmock/src/gmock-spec-builders.cc b/third_party/gmock/src/gmock-spec-builders.cc
new file mode 100644
index 0000000..edd60fe
--- /dev/null
+++ b/third_party/gmock/src/gmock-spec-builders.cc
@@ -0,0 +1,465 @@
+// Copyright 2007, 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements the spec builder syntax (ON_CALL and
+// EXPECT_CALL).
+
+#include <gmock/gmock-spec-builders.h>
+
+#include <stdlib.h>
+#include <iostream>  // NOLINT
+#include <map>
+#include <set>
+#include <string>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#if GTEST_OS_CYGWIN || GTEST_OS_LINUX || GTEST_OS_MAC
+#include <unistd.h>  // NOLINT
+#endif
+
+namespace testing {
+namespace internal {
+
+// Protects the mock object registry (in class Mock), all function
+// mockers, and all expectations.
+Mutex g_gmock_mutex(Mutex::NO_CONSTRUCTOR_NEEDED_FOR_STATIC_MUTEX);
+
+// Constructs an ExpectationBase object.
+ExpectationBase::ExpectationBase(const char* file,
+                                 int line,
+                                 const string& source_text)
+    : file_(file),
+      line_(line),
+      source_text_(source_text),
+      cardinality_specified_(false),
+      cardinality_(Exactly(1)),
+      call_count_(0),
+      retired_(false) {
+}
+
+// Destructs an ExpectationBase object.
+ExpectationBase::~ExpectationBase() {}
+
+// Explicitly specifies the cardinality of this expectation.  Used by
+// the subclasses to implement the .Times() clause.
+void ExpectationBase::SpecifyCardinality(const Cardinality& cardinality) {
+  cardinality_specified_ = true;
+  cardinality_ = cardinality;
+}
+
+// Retires all pre-requisites of this expectation.
+void ExpectationBase::RetireAllPreRequisites() {
+  if (is_retired()) {
+    // We can take this short-cut as we never retire an expectation
+    // until we have retired all its pre-requisites.
+    return;
+  }
+
+  for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin();
+       it != immediate_prerequisites_.end(); ++it) {
+    ExpectationBase* const prerequisite = it->expectation_base().get();
+    if (!prerequisite->is_retired()) {
+      prerequisite->RetireAllPreRequisites();
+      prerequisite->Retire();
+    }
+  }
+}
+
+// Returns true iff all pre-requisites of this expectation have been
+// satisfied.
+// L >= g_gmock_mutex
+bool ExpectationBase::AllPrerequisitesAreSatisfied() const {
+  g_gmock_mutex.AssertHeld();
+  for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin();
+       it != immediate_prerequisites_.end(); ++it) {
+    if (!(it->expectation_base()->IsSatisfied()) ||
+        !(it->expectation_base()->AllPrerequisitesAreSatisfied()))
+      return false;
+  }
+  return true;
+}
+
+// Adds unsatisfied pre-requisites of this expectation to 'result'.
+// L >= g_gmock_mutex
+void ExpectationBase::FindUnsatisfiedPrerequisites(
+    ExpectationSet* result) const {
+  g_gmock_mutex.AssertHeld();
+  for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin();
+       it != immediate_prerequisites_.end(); ++it) {
+    if (it->expectation_base()->IsSatisfied()) {
+      // If *it is satisfied and has a call count of 0, some of its
+      // pre-requisites may not be satisfied yet.
+      if (it->expectation_base()->call_count_ == 0) {
+        it->expectation_base()->FindUnsatisfiedPrerequisites(result);
+      }
+    } else {
+      // Now that we know *it is unsatisfied, we are not so interested
+      // in whether its pre-requisites are satisfied.  Therefore we
+      // don't recursively call FindUnsatisfiedPrerequisites() here.
+      *result += *it;
+    }
+  }
+}
+
+// Points to the implicit sequence introduced by a living InSequence
+// object (if any) in the current thread or NULL.
+ThreadLocal<Sequence*> g_gmock_implicit_sequence;
+
+// Reports an uninteresting call (whose description is in msg) in the
+// manner specified by 'reaction'.
+void ReportUninterestingCall(CallReaction reaction, const string& msg) {
+  switch (reaction) {
+    case ALLOW:
+      Log(INFO, msg, 3);
+      break;
+    case WARN:
+      Log(WARNING, msg, 3);
+      break;
+    default:  // FAIL
+      Expect(false, NULL, -1, msg);
+  }
+}
+
+}  // namespace internal
+
+// Class Mock.
+
+namespace {
+
+typedef std::set<internal::UntypedFunctionMockerBase*> FunctionMockers;
+
+// The current state of a mock object.  Such information is needed for
+// detecting leaked mock objects and explicitly verifying a mock's
+// expectations.
+struct MockObjectState {
+  MockObjectState()
+      : first_used_file(NULL), first_used_line(-1), leakable(false) {}
+
+  // Where in the source file an ON_CALL or EXPECT_CALL is first
+  // invoked on this mock object.
+  const char* first_used_file;
+  int first_used_line;
+  ::std::string first_used_test_case;
+  ::std::string first_used_test;
+  bool leakable;  // true iff it's OK to leak the object.
+  FunctionMockers function_mockers;  // All registered methods of the object.
+};
+
+// A global registry holding the state of all mock objects that are
+// alive.  A mock object is added to this registry the first time
+// Mock::AllowLeak(), ON_CALL(), or EXPECT_CALL() is called on it.  It
+// is removed from the registry in the mock object's destructor.
+class MockObjectRegistry {
+ public:
+  // Maps a mock object (identified by its address) to its state.
+  typedef std::map<const void*, MockObjectState> StateMap;
+
+  // This destructor will be called when a program exits, after all
+  // tests in it have been run.  By then, there should be no mock
+  // object alive.  Therefore we report any living object as test
+  // failure, unless the user explicitly asked us to ignore it.
+  ~MockObjectRegistry() {
+
+    // "using ::std::cout;" doesn't work with Symbian's STLport, where cout is
+    // a macro.
+
+    if (!GMOCK_FLAG(catch_leaked_mocks))
+      return;
+
+    int leaked_count = 0;
+    for (StateMap::const_iterator it = states_.begin(); it != states_.end();
+         ++it) {
+      if (it->second.leakable)  // The user said it's fine to leak this object.
+        continue;
+
+      // TODO(wan@google.com): Print the type of the leaked object.
+      // This can help the user identify the leaked object.
+      std::cout << "\n";
+      const MockObjectState& state = it->second;
+      std::cout << internal::FormatFileLocation(state.first_used_file,
+                                                state.first_used_line);
+      std::cout << " ERROR: this mock object";
+      if (state.first_used_test != "") {
+        std::cout << " (used in test " << state.first_used_test_case << "."
+             << state.first_used_test << ")";
+      }
+      std::cout << " should be deleted but never is. Its address is @"
+           << it->first << ".";
+      leaked_count++;
+    }
+    if (leaked_count > 0) {
+      std::cout << "\nERROR: " << leaked_count
+           << " leaked mock " << (leaked_count == 1 ? "object" : "objects")
+           << " found at program exit.\n";
+      std::cout.flush();
+      ::std::cerr.flush();
+      // RUN_ALL_TESTS() has already returned when this destructor is
+      // called.  Therefore we cannot use the normal Google Test
+      // failure reporting mechanism.
+      _exit(1);  // We cannot call exit() as it is not reentrant and
+                 // may already have been called.
+    }
+  }
+
+  StateMap& states() { return states_; }
+ private:
+  StateMap states_;
+};
+
+// Protected by g_gmock_mutex.
+MockObjectRegistry g_mock_object_registry;
+
+// Maps a mock object to the reaction Google Mock should have when an
+// uninteresting method is called.  Protected by g_gmock_mutex.
+std::map<const void*, internal::CallReaction> g_uninteresting_call_reaction;
+
+// Sets the reaction Google Mock should have when an uninteresting
+// method of the given mock object is called.
+// L < g_gmock_mutex
+void SetReactionOnUninterestingCalls(const void* mock_obj,
+                                     internal::CallReaction reaction) {
+  internal::MutexLock l(&internal::g_gmock_mutex);
+  g_uninteresting_call_reaction[mock_obj] = reaction;
+}
+
+}  // namespace
+
+// Tells Google Mock to allow uninteresting calls on the given mock
+// object.
+// L < g_gmock_mutex
+void Mock::AllowUninterestingCalls(const void* mock_obj) {
+  SetReactionOnUninterestingCalls(mock_obj, internal::ALLOW);
+}
+
+// Tells Google Mock to warn the user about uninteresting calls on the
+// given mock object.
+// L < g_gmock_mutex
+void Mock::WarnUninterestingCalls(const void* mock_obj) {
+  SetReactionOnUninterestingCalls(mock_obj, internal::WARN);
+}
+
+// Tells Google Mock to fail uninteresting calls on the given mock
+// object.
+// L < g_gmock_mutex
+void Mock::FailUninterestingCalls(const void* mock_obj) {
+  SetReactionOnUninterestingCalls(mock_obj, internal::FAIL);
+}
+
+// Tells Google Mock the given mock object is being destroyed and its
+// entry in the call-reaction table should be removed.
+// L < g_gmock_mutex
+void Mock::UnregisterCallReaction(const void* mock_obj) {
+  internal::MutexLock l(&internal::g_gmock_mutex);
+  g_uninteresting_call_reaction.erase(mock_obj);
+}
+
+// Returns the reaction Google Mock will have on uninteresting calls
+// made on the given mock object.
+// L < g_gmock_mutex
+internal::CallReaction Mock::GetReactionOnUninterestingCalls(
+    const void* mock_obj) {
+  internal::MutexLock l(&internal::g_gmock_mutex);
+  return (g_uninteresting_call_reaction.count(mock_obj) == 0) ?
+      internal::WARN : g_uninteresting_call_reaction[mock_obj];
+}
+
+// Tells Google Mock to ignore mock_obj when checking for leaked mock
+// objects.
+// L < g_gmock_mutex
+void Mock::AllowLeak(const void* mock_obj) {
+  internal::MutexLock l(&internal::g_gmock_mutex);
+  g_mock_object_registry.states()[mock_obj].leakable = true;
+}
+
+// Verifies and clears all expectations on the given mock object.  If
+// the expectations aren't satisfied, generates one or more Google
+// Test non-fatal failures and returns false.
+// L < g_gmock_mutex
+bool Mock::VerifyAndClearExpectations(void* mock_obj) {
+  internal::MutexLock l(&internal::g_gmock_mutex);
+  return VerifyAndClearExpectationsLocked(mock_obj);
+}
+
+// Verifies all expectations on the given mock object and clears its
+// default actions and expectations.  Returns true iff the
+// verification was successful.
+// L < g_gmock_mutex
+bool Mock::VerifyAndClear(void* mock_obj) {
+  internal::MutexLock l(&internal::g_gmock_mutex);
+  ClearDefaultActionsLocked(mock_obj);
+  return VerifyAndClearExpectationsLocked(mock_obj);
+}
+
+// Verifies and clears all expectations on the given mock object.  If
+// the expectations aren't satisfied, generates one or more Google
+// Test non-fatal failures and returns false.
+// L >= g_gmock_mutex
+bool Mock::VerifyAndClearExpectationsLocked(void* mock_obj) {
+  internal::g_gmock_mutex.AssertHeld();
+  if (g_mock_object_registry.states().count(mock_obj) == 0) {
+    // No EXPECT_CALL() was set on the given mock object.
+    return true;
+  }
+
+  // Verifies and clears the expectations on each mock method in the
+  // given mock object.
+  bool expectations_met = true;
+  FunctionMockers& mockers =
+      g_mock_object_registry.states()[mock_obj].function_mockers;
+  for (FunctionMockers::const_iterator it = mockers.begin();
+       it != mockers.end(); ++it) {
+    if (!(*it)->VerifyAndClearExpectationsLocked()) {
+      expectations_met = false;
+    }
+  }
+
+  // We don't clear the content of mockers, as they may still be
+  // needed by ClearDefaultActionsLocked().
+  return expectations_met;
+}
+
+// Registers a mock object and a mock method it owns.
+// L < g_gmock_mutex
+void Mock::Register(const void* mock_obj,
+                    internal::UntypedFunctionMockerBase* mocker) {
+  internal::MutexLock l(&internal::g_gmock_mutex);
+  g_mock_object_registry.states()[mock_obj].function_mockers.insert(mocker);
+}
+
+// Tells Google Mock where in the source code mock_obj is used in an
+// ON_CALL or EXPECT_CALL.  In case mock_obj is leaked, this
+// information helps the user identify which object it is.
+// L < g_gmock_mutex
+void Mock::RegisterUseByOnCallOrExpectCall(
+    const void* mock_obj, const char* file, int line) {
+  internal::MutexLock l(&internal::g_gmock_mutex);
+  MockObjectState& state = g_mock_object_registry.states()[mock_obj];
+  if (state.first_used_file == NULL) {
+    state.first_used_file = file;
+    state.first_used_line = line;
+    const TestInfo* const test_info =
+        UnitTest::GetInstance()->current_test_info();
+    if (test_info != NULL) {
+      // TODO(wan@google.com): record the test case name when the
+      // ON_CALL or EXPECT_CALL is invoked from SetUpTestCase() or
+      // TearDownTestCase().
+      state.first_used_test_case = test_info->test_case_name();
+      state.first_used_test = test_info->name();
+    }
+  }
+}
+
+// Unregisters a mock method; removes the owning mock object from the
+// registry when the last mock method associated with it has been
+// unregistered.  This is called only in the destructor of
+// FunctionMockerBase.
+// L >= g_gmock_mutex
+void Mock::UnregisterLocked(internal::UntypedFunctionMockerBase* mocker) {
+  internal::g_gmock_mutex.AssertHeld();
+  for (MockObjectRegistry::StateMap::iterator it =
+           g_mock_object_registry.states().begin();
+       it != g_mock_object_registry.states().end(); ++it) {
+    FunctionMockers& mockers = it->second.function_mockers;
+    if (mockers.erase(mocker) > 0) {
+      // mocker was in mockers and has been just removed.
+      if (mockers.empty()) {
+        g_mock_object_registry.states().erase(it);
+      }
+      return;
+    }
+  }
+}
+
+// Clears all ON_CALL()s set on the given mock object.
+// L >= g_gmock_mutex
+void Mock::ClearDefaultActionsLocked(void* mock_obj) {
+  internal::g_gmock_mutex.AssertHeld();
+
+  if (g_mock_object_registry.states().count(mock_obj) == 0) {
+    // No ON_CALL() was set on the given mock object.
+    return;
+  }
+
+  // Clears the default actions for each mock method in the given mock
+  // object.
+  FunctionMockers& mockers =
+      g_mock_object_registry.states()[mock_obj].function_mockers;
+  for (FunctionMockers::const_iterator it = mockers.begin();
+       it != mockers.end(); ++it) {
+    (*it)->ClearDefaultActionsLocked();
+  }
+
+  // We don't clear the content of mockers, as they may still be
+  // needed by VerifyAndClearExpectationsLocked().
+}
+
+Expectation::Expectation() {}
+
+Expectation::Expectation(
+    const internal::linked_ptr<internal::ExpectationBase>& expectation_base)
+    : expectation_base_(expectation_base) {}
+
+Expectation::~Expectation() {}
+
+// Adds an expectation to a sequence.
+void Sequence::AddExpectation(const Expectation& expectation) const {
+  if (*last_expectation_ != expectation) {
+    if (last_expectation_->expectation_base() != NULL) {
+      expectation.expectation_base()->immediate_prerequisites_
+          += *last_expectation_;
+    }
+    *last_expectation_ = expectation;
+  }
+}
+
+// Creates the implicit sequence if there isn't one.
+InSequence::InSequence() {
+  if (internal::g_gmock_implicit_sequence.get() == NULL) {
+    internal::g_gmock_implicit_sequence.set(new Sequence);
+    sequence_created_ = true;
+  } else {
+    sequence_created_ = false;
+  }
+}
+
+// Deletes the implicit sequence if it was created by the constructor
+// of this object.
+InSequence::~InSequence() {
+  if (sequence_created_) {
+    delete internal::g_gmock_implicit_sequence.get();
+    internal::g_gmock_implicit_sequence.set(NULL);
+  }
+}
+
+}  // namespace testing
diff --git a/third_party/gmock/src/gmock.cc b/third_party/gmock/src/gmock.cc
new file mode 100644
index 0000000..f487265
--- /dev/null
+++ b/third_party/gmock/src/gmock.cc
@@ -0,0 +1,182 @@
+// 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+#include <gmock/gmock.h>
+#include <gmock/internal/gmock-port.h>
+
+namespace testing {
+
+// TODO(wan@google.com): support using environment variables to
+// control the flag values, like what Google Test does.
+
+GMOCK_DEFINE_bool_(catch_leaked_mocks, true,
+                   "true iff Google Mock should report leaked mock objects "
+                   "as failures.");
+
+GMOCK_DEFINE_string_(verbose, internal::kWarningVerbosity,
+                     "Controls how verbose Google Mock's output is."
+                     "  Valid values:\n"
+                     "  info    - prints all messages.\n"
+                     "  warning - prints warnings and errors.\n"
+                     "  error   - prints errors only.");
+
+namespace internal {
+
+// Parses a string as a command line flag.  The string should have the
+// format "--gmock_flag=value".  When def_optional is true, the
+// "=value" part can be omitted.
+//
+// Returns the value of the flag, or NULL if the parsing failed.
+static const char* ParseGoogleMockFlagValue(const char* str,
+                                            const char* flag,
+                                            bool def_optional) {
+  // str and flag must not be NULL.
+  if (str == NULL || flag == NULL) return NULL;
+
+  // The flag must start with "--gmock_".
+  const String flag_str = String::Format("--gmock_%s", flag);
+  const size_t flag_len = flag_str.length();
+  if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL;
+
+  // Skips the flag name.
+  const char* flag_end = str + flag_len;
+
+  // When def_optional is true, it's OK to not have a "=value" part.
+  if (def_optional && (flag_end[0] == '\0')) {
+    return flag_end;
+  }
+
+  // If def_optional is true and there are more characters after the
+  // flag name, or if def_optional is false, there must be a '=' after
+  // the flag name.
+  if (flag_end[0] != '=') return NULL;
+
+  // Returns the string after "=".
+  return flag_end + 1;
+}
+
+// Parses a string for a Google Mock bool flag, in the form of
+// "--gmock_flag=value".
+//
+// On success, stores the value of the flag in *value, and returns
+// true.  On failure, returns false without changing *value.
+static bool ParseGoogleMockBoolFlag(const char* str, const char* flag,
+                                    bool* value) {
+  // Gets the value of the flag as a string.
+  const char* const value_str = ParseGoogleMockFlagValue(str, flag, true);
+
+  // Aborts if the parsing failed.
+  if (value_str == NULL) return false;
+
+  // Converts the string value to a bool.
+  *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F');
+  return true;
+}
+
+// Parses a string for a Google Mock string flag, in the form of
+// "--gmock_flag=value".
+//
+// On success, stores the value of the flag in *value, and returns
+// true.  On failure, returns false without changing *value.
+static bool ParseGoogleMockStringFlag(const char* str, const char* flag,
+                                      String* value) {
+  // Gets the value of the flag as a string.
+  const char* const value_str = ParseGoogleMockFlagValue(str, flag, false);
+
+  // Aborts if the parsing failed.
+  if (value_str == NULL) return false;
+
+  // Sets *value to the value of the flag.
+  *value = value_str;
+  return true;
+}
+
+// The internal implementation of InitGoogleMock().
+//
+// The type parameter CharType can be instantiated to either char or
+// wchar_t.
+template <typename CharType>
+void InitGoogleMockImpl(int* argc, CharType** argv) {
+  // Makes sure Google Test is initialized.  InitGoogleTest() is
+  // idempotent, so it's fine if the user has already called it.
+  InitGoogleTest(argc, argv);
+  if (*argc <= 0) return;
+
+  for (int i = 1; i != *argc; i++) {
+    const String arg_string = StreamableToString(argv[i]);
+    const char* const arg = arg_string.c_str();
+
+    // Do we see a Google Mock flag?
+    if (ParseGoogleMockBoolFlag(arg, "catch_leaked_mocks",
+                                &GMOCK_FLAG(catch_leaked_mocks)) ||
+        ParseGoogleMockStringFlag(arg, "verbose", &GMOCK_FLAG(verbose))) {
+      // Yes.  Shift the remainder of the argv list left by one.  Note
+      // that argv has (*argc + 1) elements, the last one always being
+      // NULL.  The following loop moves the trailing NULL element as
+      // well.
+      for (int j = i; j != *argc; j++) {
+        argv[j] = argv[j + 1];
+      }
+
+      // Decrements the argument count.
+      (*argc)--;
+
+      // We also need to decrement the iterator as we just removed
+      // an element.
+      i--;
+    }
+  }
+}
+
+}  // namespace internal
+
+// Initializes Google Mock.  This must be called before running the
+// tests.  In particular, it parses a command line for the flags that
+// Google Mock recognizes.  Whenever a Google Mock flag is seen, it is
+// removed from argv, and *argc is decremented.
+//
+// No value is returned.  Instead, the Google Mock flag variables are
+// updated.
+//
+// Since Google Test is needed for Google Mock to work, this function
+// also initializes Google Test and parses its flags, if that hasn't
+// been done.
+void InitGoogleMock(int* argc, char** argv) {
+  internal::InitGoogleMockImpl(argc, argv);
+}
+
+// This overloaded version can be used in Windows programs compiled in
+// UNICODE mode.
+void InitGoogleMock(int* argc, wchar_t** argv) {
+  internal::InitGoogleMockImpl(argc, argv);
+}
+
+}  // namespace testing
diff --git a/third_party/gmock/src/gmock_main.cc b/third_party/gmock/src/gmock_main.cc
new file mode 100644
index 0000000..0a3071b
--- /dev/null
+++ b/third_party/gmock/src/gmock_main.cc
@@ -0,0 +1,54 @@
+// 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.
+//
+// Author: wan@google.com (Zhanyong Wan)
+
+#include <iostream>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+// MS C++ compiler/linker has a bug on Windows (not on Windows CE), which
+// causes a link error when _tmain is defined in a static library and UNICODE
+// is enabled. For this reason instead of _tmain, main function is used on
+// Windows. See the following link to track the current status of this bug:
+// http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=394464  // NOLINT
+#if GTEST_OS_WINDOWS_MOBILE
+#include <tchar.h>  // NOLINT
+
+int _tmain(int argc, TCHAR** argv) {
+#else
+int main(int argc, char** argv) {
+#endif  // GTEST_OS_WINDOWS_MOBILE
+  std::cout << "Running main() from gmock_main.cc\n";
+  // Since Google Mock depends on Google Test, InitGoogleMock() is
+  // also responsible for initializing Google Test.  Therefore there's
+  // no need for calling testing::InitGoogleTest() separately.
+  testing::InitGoogleMock(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/third_party/lzma/build.scons b/third_party/lzma/build.scons
index 5cd142d..1eb3845 100644
--- a/third_party/lzma/build.scons
+++ b/third_party/lzma/build.scons
@@ -17,9 +17,4 @@
 
 Import('env')
 
-env.ComponentLibrary(
-    lib_name='lzma',
-    source='LzmaStateDecode.c',
-)
-
-
+env.BuildSConscript('v4_65')
diff --git a/third_party/lzma/setup_env.bat b/third_party/lzma/setup_env.bat
deleted file mode 100644
index 8260e2d..0000000
--- a/third_party/lzma/setup_env.bat
+++ /dev/null
@@ -1,8 +0,0 @@
-:: This script must not rely on any external tools or PATH values.
-@echo OFF
-
-:: Let advanced users checkout the tools in just one P4 enlistment
-if "%SETUP_ENV_LZMA_4_43%"=="done" goto :EOF
-set  SETUP_ENV_LZMA_4_43=done
-
-set PATH=%PATH%;%~dp0
diff --git a/third_party/lzma/LzmaStateDecode.c b/third_party/lzma/v4_43/LzmaStateDecode.c
similarity index 100%
rename from third_party/lzma/LzmaStateDecode.c
rename to third_party/lzma/v4_43/LzmaStateDecode.c
diff --git a/third_party/lzma/LzmaStateDecode.h b/third_party/lzma/v4_43/LzmaStateDecode.h
similarity index 100%
rename from third_party/lzma/LzmaStateDecode.h
rename to third_party/lzma/v4_43/LzmaStateDecode.h
diff --git a/third_party/lzma/LzmaTypes.h b/third_party/lzma/v4_43/LzmaTypes.h
similarity index 100%
rename from third_party/lzma/LzmaTypes.h
rename to third_party/lzma/v4_43/LzmaTypes.h
diff --git a/third_party/lzma/v4_43/build.scons b/third_party/lzma/v4_43/build.scons
new file mode 100644
index 0000000..5cd142d
--- /dev/null
+++ b/third_party/lzma/v4_43/build.scons
@@ -0,0 +1,25 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========================================================================
+
+Import('env')
+
+env.ComponentLibrary(
+    lib_name='lzma',
+    source='LzmaStateDecode.c',
+)
+
+
diff --git a/third_party/lzma/lzma.exe b/third_party/lzma/v4_43/lzma.exe
similarity index 100%
rename from third_party/lzma/lzma.exe
rename to third_party/lzma/v4_43/lzma.exe
Binary files differ
diff --git a/third_party/lzma/v4_65/build.scons b/third_party/lzma/v4_65/build.scons
new file mode 100644
index 0000000..eb3a64b
--- /dev/null
+++ b/third_party/lzma/v4_65/build.scons
@@ -0,0 +1,30 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2009 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========================================================================
+
+Import('env')
+
+local_env = env.Clone()
+local_env.FilterOut(CCFLAGS=['/RTC1'])
+
+local_env.ComponentLibrary(
+    lib_name='lzma',
+    source=[
+        'files/C/Bcj2.c',
+        'files/C/Bra86.c',
+        'files/C/LzmaDec.c',
+    ],
+)
diff --git a/third_party/lzma/v4_65/files/7zC.txt b/third_party/lzma/v4_65/files/7zC.txt
new file mode 100644
index 0000000..5d5d06d
--- /dev/null
+++ b/third_party/lzma/v4_65/files/7zC.txt
@@ -0,0 +1,194 @@
+7z ANSI-C Decoder 4.62
+----------------------
+
+7z ANSI-C provides 7z/LZMA decoding.
+7z ANSI-C version is simplified version ported from C++ code.
+
+LZMA is default and general compression method of 7z format
+in 7-Zip compression program (www.7-zip.org). LZMA provides high 
+compression ratio and very fast decompression.
+
+
+LICENSE
+-------
+
+7z ANSI-C Decoder is part of the LZMA SDK.
+LZMA SDK is written and placed in the public domain by Igor Pavlov.
+
+Files
+---------------------
+
+7zDecode.*   - Low level 7z decoding
+7zExtract.*  - High level 7z decoding
+7zHeader.*   - .7z format constants
+7zIn.*       - .7z archive opening
+7zItem.*     - .7z structures
+7zMain.c     - Test application
+
+
+How To Use
+----------
+
+You must download 7-Zip program from www.7-zip.org.
+
+You can create .7z archive with 7z.exe or 7za.exe:
+
+  7za.exe a archive.7z *.htm -r -mx -m0fb=255
+
+If you have big number of files in archive, and you need fast extracting, 
+you can use partly-solid archives:
+  
+  7za.exe a archive.7z *.htm -ms=512K -r -mx -m0fb=255 -m0d=512K
+
+In that example 7-Zip will use 512KB solid blocks. So it needs to decompress only 
+512KB for extracting one file from such archive.
+
+
+Limitations of current version of 7z ANSI-C Decoder
+---------------------------------------------------
+
+ - It reads only "FileName", "Size", "LastWriteTime" and "CRC" information for each file in archive.
+ - It supports only LZMA and Copy (no compression) methods with BCJ or BCJ2 filters.
+ - It converts original UTF-16 Unicode file names to UTF-8 Unicode file names.
+ 
+These limitations will be fixed in future versions.
+
+
+Using 7z ANSI-C Decoder Test application:
+-----------------------------------------
+
+Usage: 7zDec <command> <archive_name>
+
+<Command>:
+  e: Extract files from archive
+  l: List contents of archive
+  t: Test integrity of archive
+
+Example: 
+
+  7zDec l archive.7z
+
+lists contents of archive.7z
+
+  7zDec e archive.7z
+
+extracts files from archive.7z to current folder.
+
+
+How to use .7z Decoder
+----------------------
+
+Memory allocation
+~~~~~~~~~~~~~~~~~
+
+7z Decoder uses two memory pools:
+1) Temporary pool
+2) Main pool
+Such scheme can allow you to avoid fragmentation of allocated blocks.
+
+
+Steps for using 7z decoder
+--------------------------
+
+Use code at 7zMain.c as example.
+
+1) Declare variables:
+  inStream                 /* implements ILookInStream interface */
+  CSzArEx db;              /* 7z archive database structure */
+  ISzAlloc allocImp;       /* memory functions for main pool */
+  ISzAlloc allocTempImp;   /* memory functions for temporary pool */
+
+2) call CrcGenerateTable(); function to initialize CRC structures.
+
+3) call SzArEx_Init(&db); function to initialize db structures.
+
+4) call SzArEx_Open(&db, inStream, &allocMain, &allocTemp) to open archive
+
+This function opens archive "inStream" and reads headers to "db".
+All items in "db" will be allocated with "allocMain" functions.
+SzArEx_Open function allocates and frees temporary structures by "allocTemp" functions.
+
+5) List items or Extract items
+
+  Listing code:
+  ~~~~~~~~~~~~~
+    {
+      UInt32 i;
+      for (i = 0; i < db.db.NumFiles; i++)
+      {
+        CFileItem *f = db.db.Files + i;
+        printf("%10d  %s\n", (int)f->Size, f->Name);
+      }
+    }
+
+  Extracting code:
+  ~~~~~~~~~~~~~~~~
+
+  SZ_RESULT SzAr_Extract(
+    CArchiveDatabaseEx *db,
+    ILookInStream *inStream, 
+    UInt32 fileIndex,         /* index of file */
+    UInt32 *blockIndex,       /* index of solid block */
+    Byte **outBuffer,         /* pointer to pointer to output buffer (allocated with allocMain) */
+    size_t *outBufferSize,    /* buffer size for output buffer */
+    size_t *offset,           /* offset of stream for required file in *outBuffer */
+    size_t *outSizeProcessed, /* size of file in *outBuffer */
+    ISzAlloc *allocMain,
+    ISzAlloc *allocTemp);
+
+  If you need to decompress more than one file, you can send these values from previous call:
+    blockIndex, 
+    outBuffer, 
+    outBufferSize,
+  You can consider "outBuffer" as cache of solid block. If your archive is solid, 
+  it will increase decompression speed.
+
+  After decompressing you must free "outBuffer":
+  allocImp.Free(outBuffer);
+
+6) call SzArEx_Free(&db, allocImp.Free) to free allocated items in "db".
+
+
+
+
+Memory requirements for .7z decoding 
+------------------------------------
+
+Memory usage for Archive opening:
+  - Temporary pool:
+     - Memory for uncompressed .7z headers
+     - some other temporary blocks
+  - Main pool:
+     - Memory for database: 
+       Estimated size of one file structures in solid archive:
+         - Size (4 or 8 Bytes)
+         - CRC32 (4 bytes)
+         - LastWriteTime (8 bytes)
+         - Some file information (4 bytes)
+         - File Name (variable length) + pointer + allocation structures
+
+Memory usage for archive Decompressing:
+  - Temporary pool:
+     - Memory for LZMA decompressing structures
+  - Main pool:
+     - Memory for decompressed solid block
+     - Memory for temprorary buffers, if BCJ2 fileter is used. Usually these 
+       temprorary buffers can be about 15% of solid block size. 
+  
+
+7z Decoder doesn't allocate memory for compressed blocks. 
+Instead of this, you must allocate buffer with desired 
+size before calling 7z Decoder. Use 7zMain.c as example.
+
+
+Defines
+-------
+
+_SZ_ALLOC_DEBUG   - define it if you want to debug alloc/free operations to stderr.
+
+
+---
+
+http://www.7-zip.org
+http://www.7-zip.org/sdk.html
+http://www.7-zip.org/support.html
diff --git a/third_party/lzma/v4_65/files/7zFormat.txt b/third_party/lzma/v4_65/files/7zFormat.txt
new file mode 100644
index 0000000..e1cf738
--- /dev/null
+++ b/third_party/lzma/v4_65/files/7zFormat.txt
@@ -0,0 +1,471 @@
+7z Format description (2.30 Beta 25)
+-----------------------------------
+
+This file contains description of 7z archive format. 
+7z archive can contain files compressed with any method.
+See "Methods.txt" for description for defined compressing methods.
+
+
+Format structure Overview
+-------------------------
+
+Some fields can be optional.
+
+Archive structure
+~~~~~~~~~~~~~~~~~  
+SignatureHeader
+[PackedStreams]
+[PackedStreamsForHeaders]
+[
+  Header 
+  or 
+  {
+    Packed Header
+    HeaderInfo
+  }
+]
+
+
+
+Header structure
+~~~~~~~~~~~~~~~~  
+{
+  ArchiveProperties
+  AdditionalStreams
+  {
+    PackInfo
+    {
+      PackPos
+      NumPackStreams
+      Sizes[NumPackStreams]
+      CRCs[NumPackStreams]
+    }
+    CodersInfo
+    {
+      NumFolders
+      Folders[NumFolders]
+      {
+        NumCoders
+        CodersInfo[NumCoders]
+        {
+          ID
+          NumInStreams;
+          NumOutStreams;
+          PropertiesSize
+          Properties[PropertiesSize]
+        }
+        NumBindPairs
+        BindPairsInfo[NumBindPairs]
+        {
+          InIndex;
+          OutIndex;
+        }
+        PackedIndices
+      }
+      UnPackSize[Folders][Folders.NumOutstreams]
+      CRCs[NumFolders]
+    }
+    SubStreamsInfo
+    {
+      NumUnPackStreamsInFolders[NumFolders];
+      UnPackSizes[]
+      CRCs[]
+    }
+  }
+  MainStreamsInfo
+  {
+    (Same as in AdditionalStreams)
+  }
+  FilesInfo
+  {
+    NumFiles
+    Properties[]
+    {
+      ID
+      Size
+      Data
+    }
+  }
+}
+
+HeaderInfo structure
+~~~~~~~~~~~~~~~~~~~~
+{
+  (Same as in AdditionalStreams)
+}
+
+
+
+Notes about Notation and encoding
+---------------------------------
+
+7z uses little endian encoding.
+
+7z archive format has optional headers that are marked as
+[]
+Header
+[]
+
+REAL_UINT64 means real UINT64.
+
+UINT64 means real UINT64 encoded with the following scheme:
+
+  Size of encoding sequence depends from first byte:
+  First_Byte  Extra_Bytes        Value
+  (binary)   
+  0xxxxxxx               : ( xxxxxxx           )
+  10xxxxxx    BYTE y[1]  : (  xxxxxx << (8 * 1)) + y
+  110xxxxx    BYTE y[2]  : (   xxxxx << (8 * 2)) + y
+  ...
+  1111110x    BYTE y[6]  : (       x << (8 * 6)) + y
+  11111110    BYTE y[7]  :                         y
+  11111111    BYTE y[8]  :                         y
+
+
+
+Property IDs
+------------
+
+0x00 = kEnd,
+
+0x01 = kHeader,
+
+0x02 = kArchiveProperties,
+    
+0x03 = kAdditionalStreamsInfo,
+0x04 = kMainStreamsInfo,
+0x05 = kFilesInfo,
+    
+0x06 = kPackInfo,
+0x07 = kUnPackInfo,
+0x08 = kSubStreamsInfo,
+
+0x09 = kSize,
+0x0A = kCRC,
+
+0x0B = kFolder,
+
+0x0C = kCodersUnPackSize,
+0x0D = kNumUnPackStream,
+
+0x0E = kEmptyStream,
+0x0F = kEmptyFile,
+0x10 = kAnti,
+
+0x11 = kName,
+0x12 = kCreationTime,
+0x13 = kLastAccessTime,
+0x14 = kLastWriteTime,
+0x15 = kWinAttributes,
+0x16 = kComment,
+
+0x17 = kEncodedHeader,
+
+
+7z format headers
+-----------------
+
+SignatureHeader
+~~~~~~~~~~~~~~~
+  BYTE kSignature[6] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C};
+
+  ArchiveVersion
+  {
+    BYTE Major;   // now = 0
+    BYTE Minor;   // now = 2
+  };
+
+  UINT32 StartHeaderCRC;
+
+  StartHeader
+  {
+    REAL_UINT64 NextHeaderOffset
+    REAL_UINT64 NextHeaderSize
+    UINT32 NextHeaderCRC
+  }
+
+
+...........................
+
+
+ArchiveProperties
+~~~~~~~~~~~~~~~~~
+BYTE NID::kArchiveProperties (0x02)
+for (;;)
+{
+  BYTE PropertyType;
+  if (aType == 0)
+    break;
+  UINT64 PropertySize;
+  BYTE PropertyData[PropertySize];
+}
+
+
+Digests (NumStreams)
+~~~~~~~~~~~~~~~~~~~~~
+  BYTE AllAreDefined
+  if (AllAreDefined == 0)
+  {
+    for(NumStreams)
+      BIT Defined
+  }
+  UINT32 CRCs[NumDefined]
+
+
+PackInfo
+~~~~~~~~~~~~
+  BYTE NID::kPackInfo  (0x06)
+  UINT64 PackPos
+  UINT64 NumPackStreams
+
+  []
+  BYTE NID::kSize    (0x09)
+  UINT64 PackSizes[NumPackStreams]
+  []
+
+  []
+  BYTE NID::kCRC      (0x0A)
+  PackStreamDigests[NumPackStreams]
+  []
+
+  BYTE NID::kEnd
+
+
+Folder
+~~~~~~
+  UINT64 NumCoders;
+  for (NumCoders)
+  {
+    BYTE 
+    {
+      0:3 DecompressionMethod.IDSize
+      4:
+        0 - IsSimple
+        1 - Is not simple
+      5:
+        0 - No Attributes
+        1 - There Are Attributes
+      7:
+        0 - Last Method in Alternative_Method_List
+        1 - There are more alternative methods
+    } 
+    BYTE DecompressionMethod.ID[DecompressionMethod.IDSize]
+    if (!IsSimple)
+    {
+      UINT64 NumInStreams;
+      UINT64 NumOutStreams;
+    }
+    if (DecompressionMethod[0] != 0)
+    {
+      UINT64 PropertiesSize
+      BYTE Properties[PropertiesSize]
+    }
+  }
+    
+  NumBindPairs = NumOutStreamsTotal - 1;
+
+  for (NumBindPairs)
+  {
+    UINT64 InIndex;
+    UINT64 OutIndex;
+  }
+
+  NumPackedStreams = NumInStreamsTotal - NumBindPairs;
+  if (NumPackedStreams > 1)
+    for(NumPackedStreams)
+    {
+      UINT64 Index;
+    };
+
+
+
+
+Coders Info
+~~~~~~~~~~~
+
+  BYTE NID::kUnPackInfo  (0x07)
+
+
+  BYTE NID::kFolder  (0x0B)
+  UINT64 NumFolders
+  BYTE External
+  switch(External)
+  {
+    case 0:
+      Folders[NumFolders]
+    case 1:
+      UINT64 DataStreamIndex
+  }
+
+
+  BYTE ID::kCodersUnPackSize  (0x0C)
+  for(Folders)
+    for(Folder.NumOutStreams)
+     UINT64 UnPackSize;
+
+
+  []
+  BYTE NID::kCRC   (0x0A)
+  UnPackDigests[NumFolders]
+  []
+
+  
+
+  BYTE NID::kEnd
+
+
+
+SubStreams Info
+~~~~~~~~~~~~~~
+  BYTE NID::kSubStreamsInfo; (0x08)
+
+  []
+  BYTE NID::kNumUnPackStream; (0x0D)
+  UINT64 NumUnPackStreamsInFolders[NumFolders];
+  []
+
+
+  []
+  BYTE NID::kSize  (0x09)
+  UINT64 UnPackSizes[]
+  []
+
+
+  []
+  BYTE NID::kCRC  (0x0A)
+  Digests[Number of streams with unknown CRC]
+  []
+
+  
+  BYTE NID::kEnd
+
+
+Streams Info
+~~~~~~~~~~~~
+
+  []
+  PackInfo
+  []
+
+
+  []
+  CodersInfo
+  []
+
+
+  []
+  SubStreamsInfo
+  []
+
+  BYTE NID::kEnd
+
+
+FilesInfo
+~~~~~~~~~
+  BYTE NID::kFilesInfo;  (0x05)
+  UINT64 NumFiles
+
+  for (;;)
+  {
+    BYTE PropertyType;
+    if (aType == 0)
+      break;
+
+    UINT64 Size;
+
+    switch(PropertyType)
+    {
+      kEmptyStream:   (0x0E)
+        for(NumFiles)
+          BIT IsEmptyStream
+
+      kEmptyFile:     (0x0F)
+        for(EmptyStreams)
+          BIT IsEmptyFile
+
+      kAnti:          (0x10)
+        for(EmptyStreams)
+          BIT IsAntiFile
+      
+      case kCreationTime:   (0x12)
+      case kLastAccessTime: (0x13)
+      case kLastWriteTime:  (0x14)
+        BYTE AllAreDefined
+        if (AllAreDefined == 0)
+        {
+          for(NumFiles)
+            BIT TimeDefined
+        }
+        BYTE External;
+        if(External != 0)
+          UINT64 DataIndex
+        []
+        for(Definded Items)
+          UINT32 Time
+        []
+      
+      kNames:     (0x11)
+        BYTE External;
+        if(External != 0)
+          UINT64 DataIndex
+        []
+        for(Files)
+        {
+          wchar_t Names[NameSize];
+          wchar_t 0;
+        }
+        []
+
+      kAttributes:  (0x15)
+        BYTE AllAreDefined
+        if (AllAreDefined == 0)
+        {
+          for(NumFiles)
+            BIT AttributesAreDefined
+        }
+        BYTE External;
+        if(External != 0)
+          UINT64 DataIndex
+        []
+        for(Definded Attributes)
+          UINT32 Attributes
+        []
+    }
+  }
+
+
+Header
+~~~~~~
+  BYTE NID::kHeader (0x01)
+
+  []
+  ArchiveProperties
+  []
+
+  []
+  BYTE NID::kAdditionalStreamsInfo; (0x03)
+  StreamsInfo
+  []
+
+  []
+  BYTE NID::kMainStreamsInfo;    (0x04)
+  StreamsInfo
+  []
+
+  []
+  FilesInfo
+  []
+
+  BYTE NID::kEnd
+
+
+HeaderInfo
+~~~~~~~~~~
+  []
+  BYTE NID::kEncodedHeader; (0x17)
+  StreamsInfo for Encoded Header
+  []
+
+
+---
+End of document
diff --git a/third_party/lzma/v4_65/files/C/7zBuf.c b/third_party/lzma/v4_65/files/C/7zBuf.c
new file mode 100644
index 0000000..14e7f4e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/7zBuf.c
@@ -0,0 +1,36 @@
+/* 7zBuf.c -- Byte Buffer
+2008-03-28
+Igor Pavlov
+Public domain */
+
+#include "7zBuf.h"
+
+void Buf_Init(CBuf *p)
+{
+  p->data = 0;
+  p->size = 0;
+}
+
+int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc)
+{
+  p->size = 0;
+  if (size == 0)
+  {
+    p->data = 0;
+    return 1;
+  }
+  p->data = (Byte *)alloc->Alloc(alloc, size);
+  if (p->data != 0)
+  {
+    p->size = size;
+    return 1;
+  }
+  return 0;
+}
+
+void Buf_Free(CBuf *p, ISzAlloc *alloc)
+{
+  alloc->Free(alloc, p->data);
+  p->data = 0;
+  p->size = 0;
+}
diff --git a/third_party/lzma/v4_65/files/C/7zBuf.h b/third_party/lzma/v4_65/files/C/7zBuf.h
new file mode 100644
index 0000000..c5bd718
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/7zBuf.h
@@ -0,0 +1,31 @@
+/* 7zBuf.h -- Byte Buffer
+2008-10-04 : Igor Pavlov : Public domain */
+
+#ifndef __7Z_BUF_H
+#define __7Z_BUF_H
+
+#include "Types.h"
+
+typedef struct
+{
+  Byte *data;
+  size_t size;
+} CBuf;
+
+void Buf_Init(CBuf *p);
+int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc);
+void Buf_Free(CBuf *p, ISzAlloc *alloc);
+
+typedef struct
+{
+  Byte *data;
+  size_t size;
+  size_t pos;
+} CDynBuf;
+
+void DynBuf_Construct(CDynBuf *p);
+void DynBuf_SeekToBeg(CDynBuf *p);
+int DynBuf_Write(CDynBuf *p, const Byte *buf, size_t size, ISzAlloc *alloc);
+void DynBuf_Free(CDynBuf *p, ISzAlloc *alloc);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/7zBuf2.c b/third_party/lzma/v4_65/files/C/7zBuf2.c
new file mode 100644
index 0000000..8d17e0d
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/7zBuf2.c
@@ -0,0 +1,45 @@
+/* 7zBuf2.c -- Byte Buffer
+2008-10-04 : Igor Pavlov : Public domain */
+
+#include <string.h>
+#include "7zBuf.h"
+
+void DynBuf_Construct(CDynBuf *p)
+{
+  p->data = 0;
+  p->size = 0;
+  p->pos = 0;
+}
+
+void DynBuf_SeekToBeg(CDynBuf *p)
+{
+  p->pos = 0;
+}
+
+int DynBuf_Write(CDynBuf *p, const Byte *buf, size_t size, ISzAlloc *alloc)
+{
+  if (size > p->size - p->pos)
+  {
+    size_t newSize = p->pos + size;
+    Byte *data;
+    newSize += newSize / 4;
+    data = (Byte *)alloc->Alloc(alloc, newSize);
+    if (data == 0)
+      return 0;
+    p->size = newSize;
+    memcpy(data, p->data, p->pos);
+    alloc->Free(alloc, p->data);
+    p->data = data;
+  }
+  memcpy(p->data + p->pos, buf, size);
+  p->pos += size;
+  return 1;
+}
+
+void DynBuf_Free(CDynBuf *p, ISzAlloc *alloc)
+{
+  alloc->Free(alloc, p->data);
+  p->data = 0;
+  p->size = 0;
+  p->pos = 0;
+}
diff --git a/third_party/lzma/v4_65/files/C/7zCrc.c b/third_party/lzma/v4_65/files/C/7zCrc.c
new file mode 100644
index 0000000..71962b2
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/7zCrc.c
@@ -0,0 +1,35 @@
+/* 7zCrc.c -- CRC32 calculation
+2008-08-05
+Igor Pavlov
+Public domain */
+
+#include "7zCrc.h"
+
+#define kCrcPoly 0xEDB88320
+UInt32 g_CrcTable[256];
+
+void MY_FAST_CALL CrcGenerateTable(void)
+{
+  UInt32 i;
+  for (i = 0; i < 256; i++)
+  {
+    UInt32 r = i;
+    int j;
+    for (j = 0; j < 8; j++)
+      r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1));
+    g_CrcTable[i] = r;
+  }
+}
+
+UInt32 MY_FAST_CALL CrcUpdate(UInt32 v, const void *data, size_t size)
+{
+  const Byte *p = (const Byte *)data;
+  for (; size > 0 ; size--, p++)
+    v = CRC_UPDATE_BYTE(v, *p);
+  return v;
+}
+
+UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size)
+{
+  return CrcUpdate(CRC_INIT_VAL, data, size) ^ 0xFFFFFFFF;
+}
diff --git a/third_party/lzma/v4_65/files/C/7zCrc.h b/third_party/lzma/v4_65/files/C/7zCrc.h
new file mode 100644
index 0000000..00dc29c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/7zCrc.h
@@ -0,0 +1,24 @@
+/* 7zCrc.h -- CRC32 calculation
+2008-03-13
+Igor Pavlov
+Public domain */
+
+#ifndef __7Z_CRC_H
+#define __7Z_CRC_H
+
+#include <stddef.h>
+
+#include "Types.h"
+
+extern UInt32 g_CrcTable[];
+
+void MY_FAST_CALL CrcGenerateTable(void);
+
+#define CRC_INIT_VAL 0xFFFFFFFF
+#define CRC_GET_DIGEST(crc) ((crc) ^ 0xFFFFFFFF)
+#define CRC_UPDATE_BYTE(crc, b) (g_CrcTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8))
+
+UInt32 MY_FAST_CALL CrcUpdate(UInt32 crc, const void *data, size_t size);
+UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/7zFile.c b/third_party/lzma/v4_65/files/C/7zFile.c
new file mode 100644
index 0000000..9a44c59
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/7zFile.c
@@ -0,0 +1,263 @@
+/* 7zFile.c -- File IO
+2008-11-22 : Igor Pavlov : Public domain */
+
+#include "7zFile.h"
+
+#ifndef USE_WINDOWS_FILE
+
+#include <errno.h>
+
+#endif
+
+#ifdef USE_WINDOWS_FILE
+
+/*
+   ReadFile and WriteFile functions in Windows have BUG:
+   If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1)
+   from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES
+   (Insufficient system resources exist to complete the requested service).
+   Probably in some version of Windows there are problems with other sizes:
+   for 32 MB (maybe also for 16 MB).
+   And message can be "Network connection was lost"
+*/
+
+#define kChunkSizeMax (1 << 22)
+
+#endif
+
+void File_Construct(CSzFile *p)
+{
+  #ifdef USE_WINDOWS_FILE
+  p->handle = INVALID_HANDLE_VALUE;
+  #else
+  p->file = NULL;
+  #endif
+}
+
+static WRes File_Open(CSzFile *p, const char *name, int writeMode)
+{
+  #ifdef USE_WINDOWS_FILE
+  p->handle = CreateFileA(name,
+      writeMode ? GENERIC_WRITE : GENERIC_READ,
+      FILE_SHARE_READ, NULL,
+      writeMode ? CREATE_ALWAYS : OPEN_EXISTING,
+      FILE_ATTRIBUTE_NORMAL, NULL);
+  return (p->handle != INVALID_HANDLE_VALUE) ? 0 : GetLastError();
+  #else
+  p->file = fopen(name, writeMode ? "wb+" : "rb");
+  return (p->file != 0) ? 0 : errno;
+  #endif
+}
+
+WRes InFile_Open(CSzFile *p, const char *name) { return File_Open(p, name, 0); }
+WRes OutFile_Open(CSzFile *p, const char *name) { return File_Open(p, name, 1); }
+
+WRes File_Close(CSzFile *p)
+{
+  #ifdef USE_WINDOWS_FILE
+  if (p->handle != INVALID_HANDLE_VALUE)
+  {
+    if (!CloseHandle(p->handle))
+      return GetLastError();
+    p->handle = INVALID_HANDLE_VALUE;
+  }
+  #else
+  if (p->file != NULL)
+  {
+    int res = fclose(p->file);
+    if (res != 0)
+      return res;
+    p->file = NULL;
+  }
+  #endif
+  return 0;
+}
+
+WRes File_Read(CSzFile *p, void *data, size_t *size)
+{
+  size_t originalSize = *size;
+  if (originalSize == 0)
+    return 0;
+
+  #ifdef USE_WINDOWS_FILE
+
+  *size = 0;
+  do
+  {
+    DWORD curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : (DWORD)originalSize;
+    DWORD processed = 0;
+    BOOL res = ReadFile(p->handle, data, curSize, &processed, NULL);
+    data = (void *)((Byte *)data + processed);
+    originalSize -= processed;
+    *size += processed;
+    if (!res)
+      return GetLastError();
+    if (processed == 0)
+      break;
+  }
+  while (originalSize > 0);
+  return 0;
+
+  #else
+  
+  *size = fread(data, 1, originalSize, p->file);
+  if (*size == originalSize)
+    return 0;
+  return ferror(p->file);
+  
+  #endif
+}
+
+WRes File_Write(CSzFile *p, const void *data, size_t *size)
+{
+  size_t originalSize = *size;
+  if (originalSize == 0)
+    return 0;
+  
+  #ifdef USE_WINDOWS_FILE
+
+  *size = 0;
+  do
+  {
+    DWORD curSize = (originalSize > kChunkSizeMax) ? kChunkSizeMax : (DWORD)originalSize;
+    DWORD processed = 0;
+    BOOL res = WriteFile(p->handle, data, curSize, &processed, NULL);
+    data = (void *)((Byte *)data + processed);
+    originalSize -= processed;
+    *size += processed;
+    if (!res)
+      return GetLastError();
+    if (processed == 0)
+      break;
+  }
+  while (originalSize > 0);
+  return 0;
+
+  #else
+
+  *size = fwrite(data, 1, originalSize, p->file);
+  if (*size == originalSize)
+    return 0;
+  return ferror(p->file);
+  
+  #endif
+}
+
+WRes File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin)
+{
+  #ifdef USE_WINDOWS_FILE
+
+  LARGE_INTEGER value;
+  DWORD moveMethod;
+  value.LowPart = (DWORD)*pos;
+  value.HighPart = (LONG)((UInt64)*pos >> 16 >> 16); /* for case when UInt64 is 32-bit only */
+  switch (origin)
+  {
+    case SZ_SEEK_SET: moveMethod = FILE_BEGIN; break;
+    case SZ_SEEK_CUR: moveMethod = FILE_CURRENT; break;
+    case SZ_SEEK_END: moveMethod = FILE_END; break;
+    default: return ERROR_INVALID_PARAMETER;
+  }
+  value.LowPart = SetFilePointer(p->handle, value.LowPart, &value.HighPart, moveMethod);
+  if (value.LowPart == 0xFFFFFFFF)
+  {
+    WRes res = GetLastError();
+    if (res != NO_ERROR)
+      return res;
+  }
+  *pos = ((Int64)value.HighPart << 32) | value.LowPart;
+  return 0;
+
+  #else
+  
+  int moveMethod;
+  int res;
+  switch (origin)
+  {
+    case SZ_SEEK_SET: moveMethod = SEEK_SET; break;
+    case SZ_SEEK_CUR: moveMethod = SEEK_CUR; break;
+    case SZ_SEEK_END: moveMethod = SEEK_END; break;
+    default: return 1;
+  }
+  res = fseek(p->file, (long)*pos, moveMethod);
+  *pos = ftell(p->file);
+  return res;
+  
+  #endif
+}
+
+WRes File_GetLength(CSzFile *p, UInt64 *length)
+{
+  #ifdef USE_WINDOWS_FILE
+  
+  DWORD sizeHigh;
+  DWORD sizeLow = GetFileSize(p->handle, &sizeHigh);
+  if (sizeLow == 0xFFFFFFFF)
+  {
+    DWORD res = GetLastError();
+    if (res != NO_ERROR)
+      return res;
+  }
+  *length = (((UInt64)sizeHigh) << 32) + sizeLow;
+  return 0;
+  
+  #else
+  
+  long pos = ftell(p->file);
+  int res = fseek(p->file, 0, SEEK_END);
+  *length = ftell(p->file);
+  fseek(p->file, pos, SEEK_SET);
+  return res;
+  
+  #endif
+}
+
+
+/* ---------- FileSeqInStream ---------- */
+
+static SRes FileSeqInStream_Read(void *pp, void *buf, size_t *size)
+{
+  CFileSeqInStream *p = (CFileSeqInStream *)pp;
+  return File_Read(&p->file, buf, size) == 0 ? SZ_OK : SZ_ERROR_READ;
+}
+
+void FileSeqInStream_CreateVTable(CFileSeqInStream *p)
+{
+  p->s.Read = FileSeqInStream_Read;
+}
+
+
+/* ---------- FileInStream ---------- */
+
+static SRes FileInStream_Read(void *pp, void *buf, size_t *size)
+{
+  CFileInStream *p = (CFileInStream *)pp;
+  return (File_Read(&p->file, buf, size) == 0) ? SZ_OK : SZ_ERROR_READ;
+}
+
+static SRes FileInStream_Seek(void *pp, Int64 *pos, ESzSeek origin)
+{
+  CFileInStream *p = (CFileInStream *)pp;
+  return File_Seek(&p->file, pos, origin);
+}
+
+void FileInStream_CreateVTable(CFileInStream *p)
+{
+  p->s.Read = FileInStream_Read;
+  p->s.Seek = FileInStream_Seek;
+}
+
+
+/* ---------- FileOutStream ---------- */
+
+static size_t FileOutStream_Write(void *pp, const void *data, size_t size)
+{
+  CFileOutStream *p = (CFileOutStream *)pp;
+  File_Write(&p->file, data, &size);
+  return size;
+}
+
+void FileOutStream_CreateVTable(CFileOutStream *p)
+{
+  p->s.Write = FileOutStream_Write;
+}
diff --git a/third_party/lzma/v4_65/files/C/7zFile.h b/third_party/lzma/v4_65/files/C/7zFile.h
new file mode 100644
index 0000000..fbef683
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/7zFile.h
@@ -0,0 +1,74 @@
+/* 7zFile.h -- File IO
+2008-11-22 : Igor Pavlov : Public domain */
+
+#ifndef __7Z_FILE_H
+#define __7Z_FILE_H
+
+#ifdef _WIN32
+#define USE_WINDOWS_FILE
+#endif
+
+#ifdef USE_WINDOWS_FILE
+#include <windows.h>
+#else
+#include <stdio.h>
+#endif
+
+#include "Types.h"
+
+
+/* ---------- File ---------- */
+
+typedef struct
+{
+  #ifdef USE_WINDOWS_FILE
+  HANDLE handle;
+  #else
+  FILE *file;
+  #endif
+} CSzFile;
+
+void File_Construct(CSzFile *p);
+WRes InFile_Open(CSzFile *p, const char *name);
+WRes OutFile_Open(CSzFile *p, const char *name);
+WRes File_Close(CSzFile *p);
+
+/* reads max(*size, remain file's size) bytes */
+WRes File_Read(CSzFile *p, void *data, size_t *size);
+
+/* writes *size bytes */
+WRes File_Write(CSzFile *p, const void *data, size_t *size);
+
+WRes File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin);
+WRes File_GetLength(CSzFile *p, UInt64 *length);
+
+
+/* ---------- FileInStream ---------- */
+
+typedef struct
+{
+  ISeqInStream s;
+  CSzFile file;
+} CFileSeqInStream;
+
+void FileSeqInStream_CreateVTable(CFileSeqInStream *p);
+
+
+typedef struct
+{
+  ISeekInStream s;
+  CSzFile file;
+} CFileInStream;
+
+void FileInStream_CreateVTable(CFileInStream *p);
+
+
+typedef struct
+{
+  ISeqOutStream s;
+  CSzFile file;
+} CFileOutStream;
+
+void FileOutStream_CreateVTable(CFileOutStream *p);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/7zStream.c b/third_party/lzma/v4_65/files/C/7zStream.c
new file mode 100644
index 0000000..86232aa
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/7zStream.c
@@ -0,0 +1,169 @@
+/* 7zStream.c -- 7z Stream functions
+2008-11-23 : Igor Pavlov : Public domain */
+
+#include <string.h>
+
+#include "Types.h"
+
+SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType)
+{
+  while (size != 0)
+  {
+    size_t processed = size;
+    RINOK(stream->Read(stream, buf, &processed));
+    if (processed == 0)
+      return errorType;
+    buf = (void *)((Byte *)buf + processed);
+    size -= processed;
+  }
+  return SZ_OK;
+}
+
+SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size)
+{
+  return SeqInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF);
+}
+
+SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf)
+{
+  size_t processed = 1;
+  RINOK(stream->Read(stream, buf, &processed));
+  return (processed == 1) ? SZ_OK : SZ_ERROR_INPUT_EOF;
+}
+
+SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset)
+{
+  Int64 t = offset;
+  return stream->Seek(stream, &t, SZ_SEEK_SET);
+}
+
+SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size)
+{
+  void *lookBuf;
+  if (*size == 0)
+    return SZ_OK;
+  RINOK(stream->Look(stream, &lookBuf, size));
+  memcpy(buf, lookBuf, *size);
+  return stream->Skip(stream, *size);
+}
+
+SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType)
+{
+  while (size != 0)
+  {
+    size_t processed = size;
+    RINOK(stream->Read(stream, buf, &processed));
+    if (processed == 0)
+      return errorType;
+    buf = (void *)((Byte *)buf + processed);
+    size -= processed;
+  }
+  return SZ_OK;
+}
+
+SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size)
+{
+  return LookInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF);
+}
+
+static SRes LookToRead_Look_Lookahead(void *pp, void **buf, size_t *size)
+{
+  SRes res = SZ_OK;
+  CLookToRead *p = (CLookToRead *)pp;
+  size_t size2 = p->size - p->pos;
+  if (size2 == 0 && *size > 0)
+  {
+    p->pos = 0;
+    size2 = LookToRead_BUF_SIZE;
+    res = p->realStream->Read(p->realStream, p->buf, &size2);
+    p->size = size2;
+  }
+  if (size2 < *size)
+    *size = size2;
+  *buf = p->buf + p->pos;
+  return res;
+}
+
+static SRes LookToRead_Look_Exact(void *pp, void **buf, size_t *size)
+{
+  SRes res = SZ_OK;
+  CLookToRead *p = (CLookToRead *)pp;
+  size_t size2 = p->size - p->pos;
+  if (size2 == 0 && *size > 0)
+  {
+    p->pos = 0;
+    if (*size > LookToRead_BUF_SIZE)
+      *size = LookToRead_BUF_SIZE;
+    res = p->realStream->Read(p->realStream, p->buf, size);
+    size2 = p->size = *size;
+  }
+  if (size2 < *size)
+    *size = size2;
+  *buf = p->buf + p->pos;
+  return res;
+}
+
+static SRes LookToRead_Skip(void *pp, size_t offset)
+{
+  CLookToRead *p = (CLookToRead *)pp;
+  p->pos += offset;
+  return SZ_OK;
+}
+
+static SRes LookToRead_Read(void *pp, void *buf, size_t *size)
+{
+  CLookToRead *p = (CLookToRead *)pp;
+  size_t rem = p->size - p->pos;
+  if (rem == 0)
+    return p->realStream->Read(p->realStream, buf, size);
+  if (rem > *size)
+    rem = *size;
+  memcpy(buf, p->buf + p->pos, rem);
+  p->pos += rem;
+  *size = rem;
+  return SZ_OK;
+}
+
+static SRes LookToRead_Seek(void *pp, Int64 *pos, ESzSeek origin)
+{
+  CLookToRead *p = (CLookToRead *)pp;
+  p->pos = p->size = 0;
+  return p->realStream->Seek(p->realStream, pos, origin);
+}
+
+void LookToRead_CreateVTable(CLookToRead *p, int lookahead)
+{
+  p->s.Look = lookahead ?
+      LookToRead_Look_Lookahead :
+      LookToRead_Look_Exact;
+  p->s.Skip = LookToRead_Skip;
+  p->s.Read = LookToRead_Read;
+  p->s.Seek = LookToRead_Seek;
+}
+
+void LookToRead_Init(CLookToRead *p)
+{
+  p->pos = p->size = 0;
+}
+
+static SRes SecToLook_Read(void *pp, void *buf, size_t *size)
+{
+  CSecToLook *p = (CSecToLook *)pp;
+  return LookInStream_LookRead(p->realStream, buf, size);
+}
+
+void SecToLook_CreateVTable(CSecToLook *p)
+{
+  p->s.Read = SecToLook_Read;
+}
+
+static SRes SecToRead_Read(void *pp, void *buf, size_t *size)
+{
+  CSecToRead *p = (CSecToRead *)pp;
+  return p->realStream->Read(p->realStream, buf, size);
+}
+
+void SecToRead_CreateVTable(CSecToRead *p)
+{
+  p->s.Read = SecToRead_Read;
+}
diff --git a/third_party/lzma/v4_65/files/C/7zVersion.h b/third_party/lzma/v4_65/files/C/7zVersion.h
new file mode 100644
index 0000000..b7eb235
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/7zVersion.h
@@ -0,0 +1,7 @@
+#define MY_VER_MAJOR 4
+#define MY_VER_MINOR 65
+#define MY_VER_BUILD 0
+#define MY_VERSION "4.65"
+#define MY_DATE "2009-02-03"
+#define MY_COPYRIGHT ": Igor Pavlov : Public domain"
+#define MY_VERSION_COPYRIGHT_DATE MY_VERSION " " MY_COPYRIGHT " : " MY_DATE
diff --git a/third_party/lzma/v4_65/files/C/Alloc.c b/third_party/lzma/v4_65/files/C/Alloc.c
new file mode 100644
index 0000000..358a7b5
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Alloc.c
@@ -0,0 +1,127 @@
+/* Alloc.c -- Memory allocation functions
+2008-09-24
+Igor Pavlov
+Public domain */
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+#include <stdlib.h>
+
+#include "Alloc.h"
+
+/* #define _SZ_ALLOC_DEBUG */
+
+/* use _SZ_ALLOC_DEBUG to debug alloc/free operations */
+#ifdef _SZ_ALLOC_DEBUG
+#include <stdio.h>
+int g_allocCount = 0;
+int g_allocCountMid = 0;
+int g_allocCountBig = 0;
+#endif
+
+void *MyAlloc(size_t size)
+{
+  if (size == 0)
+    return 0;
+  #ifdef _SZ_ALLOC_DEBUG
+  {
+    void *p = malloc(size);
+    fprintf(stderr, "\nAlloc %10d bytes, count = %10d,  addr = %8X", size, g_allocCount++, (unsigned)p);
+    return p;
+  }
+  #else
+  return malloc(size);
+  #endif
+}
+
+void MyFree(void *address)
+{
+  #ifdef _SZ_ALLOC_DEBUG
+  if (address != 0)
+    fprintf(stderr, "\nFree; count = %10d,  addr = %8X", --g_allocCount, (unsigned)address);
+  #endif
+  free(address);
+}
+
+#ifdef _WIN32
+
+void *MidAlloc(size_t size)
+{
+  if (size == 0)
+    return 0;
+  #ifdef _SZ_ALLOC_DEBUG
+  fprintf(stderr, "\nAlloc_Mid %10d bytes;  count = %10d", size, g_allocCountMid++);
+  #endif
+  return VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE);
+}
+
+void MidFree(void *address)
+{
+  #ifdef _SZ_ALLOC_DEBUG
+  if (address != 0)
+    fprintf(stderr, "\nFree_Mid; count = %10d", --g_allocCountMid);
+  #endif
+  if (address == 0)
+    return;
+  VirtualFree(address, 0, MEM_RELEASE);
+}
+
+#ifndef MEM_LARGE_PAGES
+#undef _7ZIP_LARGE_PAGES
+#endif
+
+#ifdef _7ZIP_LARGE_PAGES
+SIZE_T g_LargePageSize = 0;
+typedef SIZE_T (WINAPI *GetLargePageMinimumP)();
+#endif
+
+void SetLargePageSize()
+{
+  #ifdef _7ZIP_LARGE_PAGES
+  SIZE_T size = 0;
+  GetLargePageMinimumP largePageMinimum = (GetLargePageMinimumP)
+        GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetLargePageMinimum");
+  if (largePageMinimum == 0)
+    return;
+  size = largePageMinimum();
+  if (size == 0 || (size & (size - 1)) != 0)
+    return;
+  g_LargePageSize = size;
+  #endif
+}
+
+
+void *BigAlloc(size_t size)
+{
+  if (size == 0)
+    return 0;
+  #ifdef _SZ_ALLOC_DEBUG
+  fprintf(stderr, "\nAlloc_Big %10d bytes;  count = %10d", size, g_allocCountBig++);
+  #endif
+  
+  #ifdef _7ZIP_LARGE_PAGES
+  if (g_LargePageSize != 0 && g_LargePageSize <= (1 << 30) && size >= (1 << 18))
+  {
+    void *res = VirtualAlloc(0, (size + g_LargePageSize - 1) & (~(g_LargePageSize - 1)),
+        MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
+    if (res != 0)
+      return res;
+  }
+  #endif
+  return VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE);
+}
+
+void BigFree(void *address)
+{
+  #ifdef _SZ_ALLOC_DEBUG
+  if (address != 0)
+    fprintf(stderr, "\nFree_Big; count = %10d", --g_allocCountBig);
+  #endif
+  
+  if (address == 0)
+    return;
+  VirtualFree(address, 0, MEM_RELEASE);
+}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/Alloc.h b/third_party/lzma/v4_65/files/C/Alloc.h
new file mode 100644
index 0000000..ff0669c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Alloc.h
@@ -0,0 +1,32 @@
+/* Alloc.h -- Memory allocation functions
+2008-03-13
+Igor Pavlov
+Public domain */
+
+#ifndef __COMMON_ALLOC_H
+#define __COMMON_ALLOC_H
+
+#include <stddef.h>
+
+void *MyAlloc(size_t size);
+void MyFree(void *address);
+
+#ifdef _WIN32
+
+void SetLargePageSize();
+
+void *MidAlloc(size_t size);
+void MidFree(void *address);
+void *BigAlloc(size_t size);
+void BigFree(void *address);
+
+#else
+
+#define MidAlloc(size) MyAlloc(size)
+#define MidFree(address) MyFree(address)
+#define BigAlloc(size) MyAlloc(size)
+#define BigFree(address) MyFree(address)
+
+#endif
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/Archive/7z/7z.dsp b/third_party/lzma/v4_65/files/C/Archive/7z/7z.dsp
new file mode 100644
index 0000000..9626b03
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Archive/7z/7z.dsp
@@ -0,0 +1,199 @@
+# Microsoft Developer Studio Project File - Name="7z" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=7z - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "7z.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "7z.mak" CFG="7z - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "7z - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "7z - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "7z - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FAs /YX /FD /c
+# ADD BASE RSC /l 0x419 /d "NDEBUG"
+# ADD RSC /l 0x419 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386 /out:"Release/7zDec.exe" /opt:NOWIN98
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF  "$(CFG)" == "7z - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W4 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "_SZ_ALLOC_DEBUG2" /D "_SZ_NO_INT_64_A" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x419 /d "_DEBUG"
+# ADD RSC /l 0x419 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /out:"Debug/7zDec.exe" /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "7z - Win32 Release"
+# Name "7z - Win32 Debug"
+# Begin Group "Common"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\7zBuf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\7zBuf.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\7zCrc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\7zCrc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\7zFile.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\7zFile.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\7zStream.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Bcj2.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Bcj2.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Bra.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Bra86.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\LzmaDec.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\LzmaDec.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Types.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\7zAlloc.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\7zAlloc.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\7zDecode.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\7zDecode.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\7zExtract.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\7zExtract.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\7zHeader.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\7zHeader.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\7zIn.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\7zIn.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\7zItem.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\7zItem.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\7zMain.c
+# End Source File
+# End Target
+# End Project
diff --git a/third_party/lzma/v4_65/files/C/Archive/7z/7z.dsw b/third_party/lzma/v4_65/files/C/Archive/7z/7z.dsw
new file mode 100644
index 0000000..848d13c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Archive/7z/7z.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "7z"=.\7z.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/third_party/lzma/v4_65/files/C/Archive/7z/7zAlloc.c b/third_party/lzma/v4_65/files/C/Archive/7z/7zAlloc.c
new file mode 100644
index 0000000..4bfaf42
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Archive/7z/7zAlloc.c
@@ -0,0 +1,77 @@
+/* 7zAlloc.c -- Allocation functions
+2008-10-04 : Igor Pavlov : Public domain */
+
+#include <stdlib.h>
+#include "7zAlloc.h"
+
+/* #define _SZ_ALLOC_DEBUG */
+/* use _SZ_ALLOC_DEBUG to debug alloc/free operations */
+
+#ifdef _SZ_ALLOC_DEBUG
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include <stdio.h>
+int g_allocCount = 0;
+int g_allocCountTemp = 0;
+
+#endif
+
+void *SzAlloc(void *p, size_t size)
+{
+  p = p;
+  if (size == 0)
+    return 0;
+  #ifdef _SZ_ALLOC_DEBUG
+  fprintf(stderr, "\nAlloc %10d bytes; count = %10d", size, g_allocCount);
+  g_allocCount++;
+  #endif
+  return malloc(size);
+}
+
+void SzFree(void *p, void *address)
+{
+  p = p;
+  #ifdef _SZ_ALLOC_DEBUG
+  if (address != 0)
+  {
+    g_allocCount--;
+    fprintf(stderr, "\nFree; count = %10d", g_allocCount);
+  }
+  #endif
+  free(address);
+}
+
+void *SzAllocTemp(void *p, size_t size)
+{
+  p = p;
+  if (size == 0)
+    return 0;
+  #ifdef _SZ_ALLOC_DEBUG
+  fprintf(stderr, "\nAlloc_temp %10d bytes;  count = %10d", size, g_allocCountTemp);
+  g_allocCountTemp++;
+  #ifdef _WIN32
+  return HeapAlloc(GetProcessHeap(), 0, size);
+  #endif
+  #endif
+  return malloc(size);
+}
+
+void SzFreeTemp(void *p, void *address)
+{
+  p = p;
+  #ifdef _SZ_ALLOC_DEBUG
+  if (address != 0)
+  {
+    g_allocCountTemp--;
+    fprintf(stderr, "\nFree_temp; count = %10d", g_allocCountTemp);
+  }
+  #ifdef _WIN32
+  HeapFree(GetProcessHeap(), 0, address);
+  return;
+  #endif
+  #endif
+  free(address);
+}
diff --git a/third_party/lzma/v4_65/files/C/Archive/7z/7zAlloc.h b/third_party/lzma/v4_65/files/C/Archive/7z/7zAlloc.h
new file mode 100644
index 0000000..e752ef1
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Archive/7z/7zAlloc.h
@@ -0,0 +1,15 @@
+/* 7zAlloc.h -- Allocation functions
+2008-10-04 : Igor Pavlov : Public domain */
+
+#ifndef __7Z_ALLOC_H
+#define __7Z_ALLOC_H
+
+#include <stddef.h>
+
+void *SzAlloc(void *p, size_t size);
+void SzFree(void *p, void *address);
+
+void *SzAllocTemp(void *p, size_t size);
+void SzFreeTemp(void *p, void *address);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/Archive/7z/7zDecode.c b/third_party/lzma/v4_65/files/C/Archive/7z/7zDecode.c
new file mode 100644
index 0000000..02526f0
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Archive/7z/7zDecode.c
@@ -0,0 +1,254 @@
+/* 7zDecode.c -- Decoding from 7z folder
+2008-11-23 : Igor Pavlov : Public domain */
+
+#include <string.h>
+
+#include "../../Bcj2.h"
+#include "../../Bra.h"
+#include "../../LzmaDec.h"
+#include "7zDecode.h"
+
+#define k_Copy 0
+#define k_LZMA 0x30101
+#define k_BCJ 0x03030103
+#define k_BCJ2 0x0303011B
+
+static SRes SzDecodeLzma(CSzCoderInfo *coder, UInt64 inSize, ILookInStream *inStream,
+    Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain)
+{
+  CLzmaDec state;
+  SRes res = SZ_OK;
+
+  LzmaDec_Construct(&state);
+  RINOK(LzmaDec_AllocateProbs(&state, coder->Props.data, (unsigned)coder->Props.size, allocMain));
+  state.dic = outBuffer;
+  state.dicBufSize = outSize;
+  LzmaDec_Init(&state);
+
+  for (;;)
+  {
+    Byte *inBuf = NULL;
+    size_t lookahead = (1 << 18);
+    if (lookahead > inSize)
+      lookahead = (size_t)inSize;
+    res = inStream->Look((void *)inStream, (void **)&inBuf, &lookahead);
+    if (res != SZ_OK)
+      break;
+
+    {
+      SizeT inProcessed = (SizeT)lookahead, dicPos = state.dicPos;
+      ELzmaStatus status;
+      res = LzmaDec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status);
+      lookahead -= inProcessed;
+      inSize -= inProcessed;
+      if (res != SZ_OK)
+        break;
+      if (state.dicPos == state.dicBufSize || (inProcessed == 0 && dicPos == state.dicPos))
+      {
+        if (state.dicBufSize != outSize || lookahead != 0 ||
+            (status != LZMA_STATUS_FINISHED_WITH_MARK &&
+             status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK))
+          res = SZ_ERROR_DATA;
+        break;
+      }
+      res = inStream->Skip((void *)inStream, inProcessed);
+      if (res != SZ_OK)
+        break;
+    }
+  }
+
+  LzmaDec_FreeProbs(&state, allocMain);
+  return res;
+}
+
+static SRes SzDecodeCopy(UInt64 inSize, ILookInStream *inStream, Byte *outBuffer)
+{
+  while (inSize > 0)
+  {
+    void *inBuf;
+    size_t curSize = (1 << 18);
+    if (curSize > inSize)
+      curSize = (size_t)inSize;
+    RINOK(inStream->Look((void *)inStream, (void **)&inBuf, &curSize));
+    if (curSize == 0)
+      return SZ_ERROR_INPUT_EOF;
+    memcpy(outBuffer, inBuf, curSize);
+    outBuffer += curSize;
+    inSize -= curSize;
+    RINOK(inStream->Skip((void *)inStream, curSize));
+  }
+  return SZ_OK;
+}
+
+#define IS_UNSUPPORTED_METHOD(m) ((m) != k_Copy && (m) != k_LZMA)
+#define IS_UNSUPPORTED_CODER(c) (IS_UNSUPPORTED_METHOD(c.MethodID) || c.NumInStreams != 1 || c.NumOutStreams != 1)
+#define IS_NO_BCJ(c) (c.MethodID != k_BCJ || c.NumInStreams != 1 || c.NumOutStreams != 1)
+#define IS_NO_BCJ2(c) (c.MethodID != k_BCJ2 || c.NumInStreams != 4 || c.NumOutStreams != 1)
+
+SRes CheckSupportedFolder(const CSzFolder *f)
+{
+  if (f->NumCoders < 1 || f->NumCoders > 4)
+    return SZ_ERROR_UNSUPPORTED;
+  if (IS_UNSUPPORTED_CODER(f->Coders[0]))
+    return SZ_ERROR_UNSUPPORTED;
+  if (f->NumCoders == 1)
+  {
+    if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBindPairs != 0)
+      return SZ_ERROR_UNSUPPORTED;
+    return SZ_OK;
+  }
+  if (f->NumCoders == 2)
+  {
+    if (IS_NO_BCJ(f->Coders[1]) ||
+        f->NumPackStreams != 1 || f->PackStreams[0] != 0 ||
+        f->NumBindPairs != 1 ||
+        f->BindPairs[0].InIndex != 1 || f->BindPairs[0].OutIndex != 0)
+      return SZ_ERROR_UNSUPPORTED;
+    return SZ_OK;
+  }
+  if (f->NumCoders == 4)
+  {
+    if (IS_UNSUPPORTED_CODER(f->Coders[1]) ||
+        IS_UNSUPPORTED_CODER(f->Coders[2]) ||
+        IS_NO_BCJ2(f->Coders[3]))
+      return SZ_ERROR_UNSUPPORTED;
+    if (f->NumPackStreams != 4 ||
+        f->PackStreams[0] != 2 ||
+        f->PackStreams[1] != 6 ||
+        f->PackStreams[2] != 1 ||
+        f->PackStreams[3] != 0 ||
+        f->NumBindPairs != 3 ||
+        f->BindPairs[0].InIndex != 5 || f->BindPairs[0].OutIndex != 0 ||
+        f->BindPairs[1].InIndex != 4 || f->BindPairs[1].OutIndex != 1 ||
+        f->BindPairs[2].InIndex != 3 || f->BindPairs[2].OutIndex != 2)
+      return SZ_ERROR_UNSUPPORTED;
+    return SZ_OK;
+  }
+  return SZ_ERROR_UNSUPPORTED;
+}
+
+UInt64 GetSum(const UInt64 *values, UInt32 index)
+{
+  UInt64 sum = 0;
+  UInt32 i;
+  for (i = 0; i < index; i++)
+    sum += values[i];
+  return sum;
+}
+
+SRes SzDecode2(const UInt64 *packSizes, const CSzFolder *folder,
+    ILookInStream *inStream, UInt64 startPos,
+    Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain,
+    Byte *tempBuf[])
+{
+  UInt32 ci;
+  SizeT tempSizes[3] = { 0, 0, 0};
+  SizeT tempSize3 = 0;
+  Byte *tempBuf3 = 0;
+
+  RINOK(CheckSupportedFolder(folder));
+
+  for (ci = 0; ci < folder->NumCoders; ci++)
+  {
+    CSzCoderInfo *coder = &folder->Coders[ci];
+
+    if (coder->MethodID == k_Copy || coder->MethodID == k_LZMA)
+    {
+      UInt32 si = 0;
+      UInt64 offset;
+      UInt64 inSize;
+      Byte *outBufCur = outBuffer;
+      SizeT outSizeCur = outSize;
+      if (folder->NumCoders == 4)
+      {
+        UInt32 indices[] = { 3, 2, 0 };
+        UInt64 unpackSize = folder->UnpackSizes[ci];
+        si = indices[ci];
+        if (ci < 2)
+        {
+          Byte *temp;
+          outSizeCur = (SizeT)unpackSize;
+          if (outSizeCur != unpackSize)
+            return SZ_ERROR_MEM;
+          temp = (Byte *)IAlloc_Alloc(allocMain, outSizeCur);
+          if (temp == 0 && outSizeCur != 0)
+            return SZ_ERROR_MEM;
+          outBufCur = tempBuf[1 - ci] = temp;
+          tempSizes[1 - ci] = outSizeCur;
+        }
+        else if (ci == 2)
+        {
+          if (unpackSize > outSize) /* check it */
+            return SZ_ERROR_PARAM;
+          tempBuf3 = outBufCur = outBuffer + (outSize - (size_t)unpackSize);
+          tempSize3 = outSizeCur = (SizeT)unpackSize;
+        }
+        else
+          return SZ_ERROR_UNSUPPORTED;
+      }
+      offset = GetSum(packSizes, si);
+      inSize = packSizes[si];
+      RINOK(LookInStream_SeekTo(inStream, startPos + offset));
+
+      if (coder->MethodID == k_Copy)
+      {
+        if (inSize != outSizeCur) /* check it */
+          return SZ_ERROR_DATA;
+        RINOK(SzDecodeCopy(inSize, inStream, outBufCur));
+      }
+      else
+      {
+        RINOK(SzDecodeLzma(coder, inSize, inStream, outBufCur, outSizeCur, allocMain));
+      }
+    }
+    else if (coder->MethodID == k_BCJ)
+    {
+      UInt32 state;
+      if (ci != 1)
+        return SZ_ERROR_UNSUPPORTED;
+      x86_Convert_Init(state);
+      x86_Convert(outBuffer, outSize, 0, &state, 0);
+    }
+    else if (coder->MethodID == k_BCJ2)
+    {
+      UInt64 offset = GetSum(packSizes, 1);
+      UInt64 s3Size = packSizes[1];
+      SRes res;
+      if (ci != 3)
+        return SZ_ERROR_UNSUPPORTED;
+      RINOK(LookInStream_SeekTo(inStream, startPos + offset));
+      tempSizes[2] = (SizeT)s3Size;
+      if (tempSizes[2] != s3Size)
+        return SZ_ERROR_MEM;
+      tempBuf[2] = (Byte *)IAlloc_Alloc(allocMain, tempSizes[2]);
+      if (tempBuf[2] == 0 && tempSizes[2] != 0)
+        return SZ_ERROR_MEM;
+      res = SzDecodeCopy(s3Size, inStream, tempBuf[2]);
+      RINOK(res)
+
+      res = Bcj2_Decode(
+          tempBuf3, tempSize3,
+          tempBuf[0], tempSizes[0],
+          tempBuf[1], tempSizes[1],
+          tempBuf[2], tempSizes[2],
+          outBuffer, outSize);
+      RINOK(res)
+    }
+    else
+      return SZ_ERROR_UNSUPPORTED;
+  }
+  return SZ_OK;
+}
+
+SRes SzDecode(const UInt64 *packSizes, const CSzFolder *folder,
+    ILookInStream *inStream, UInt64 startPos,
+    Byte *outBuffer, size_t outSize, ISzAlloc *allocMain)
+{
+  Byte *tempBuf[3] = { 0, 0, 0};
+  int i;
+  SRes res = SzDecode2(packSizes, folder, inStream, startPos,
+      outBuffer, (SizeT)outSize, allocMain, tempBuf);
+  for (i = 0; i < 3; i++)
+    IAlloc_Free(allocMain, tempBuf[i]);
+  return res;
+}
diff --git a/third_party/lzma/v4_65/files/C/Archive/7z/7zDecode.h b/third_party/lzma/v4_65/files/C/Archive/7z/7zDecode.h
new file mode 100644
index 0000000..e19fe38
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Archive/7z/7zDecode.h
@@ -0,0 +1,13 @@
+/* 7zDecode.h -- Decoding from 7z folder
+2008-11-23 : Igor Pavlov : Public domain */
+
+#ifndef __7Z_DECODE_H
+#define __7Z_DECODE_H
+
+#include "7zItem.h"
+
+SRes SzDecode(const UInt64 *packSizes, const CSzFolder *folder,
+    ILookInStream *stream, UInt64 startPos,
+    Byte *outBuffer, size_t outSize, ISzAlloc *allocMain);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/Archive/7z/7zExtract.c b/third_party/lzma/v4_65/files/C/Archive/7z/7zExtract.c
new file mode 100644
index 0000000..ff79802
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Archive/7z/7zExtract.c
@@ -0,0 +1,93 @@
+/* 7zExtract.c -- Extracting from 7z archive
+2008-11-23 : Igor Pavlov : Public domain */
+
+#include "../../7zCrc.h"
+#include "7zDecode.h"
+#include "7zExtract.h"
+
+SRes SzAr_Extract(
+    const CSzArEx *p,
+    ILookInStream *inStream,
+    UInt32 fileIndex,
+    UInt32 *blockIndex,
+    Byte **outBuffer,
+    size_t *outBufferSize,
+    size_t *offset,
+    size_t *outSizeProcessed,
+    ISzAlloc *allocMain,
+    ISzAlloc *allocTemp)
+{
+  UInt32 folderIndex = p->FileIndexToFolderIndexMap[fileIndex];
+  SRes res = SZ_OK;
+  *offset = 0;
+  *outSizeProcessed = 0;
+  if (folderIndex == (UInt32)-1)
+  {
+    IAlloc_Free(allocMain, *outBuffer);
+    *blockIndex = folderIndex;
+    *outBuffer = 0;
+    *outBufferSize = 0;
+    return SZ_OK;
+  }
+
+  if (*outBuffer == 0 || *blockIndex != folderIndex)
+  {
+    CSzFolder *folder = p->db.Folders + folderIndex;
+    UInt64 unpackSizeSpec = SzFolder_GetUnpackSize(folder);
+    size_t unpackSize = (size_t)unpackSizeSpec;
+    UInt64 startOffset = SzArEx_GetFolderStreamPos(p, folderIndex, 0);
+
+    if (unpackSize != unpackSizeSpec)
+      return SZ_ERROR_MEM;
+    *blockIndex = folderIndex;
+    IAlloc_Free(allocMain, *outBuffer);
+    *outBuffer = 0;
+    
+    RINOK(LookInStream_SeekTo(inStream, startOffset));
+    
+    if (res == SZ_OK)
+    {
+      *outBufferSize = unpackSize;
+      if (unpackSize != 0)
+      {
+        *outBuffer = (Byte *)IAlloc_Alloc(allocMain, unpackSize);
+        if (*outBuffer == 0)
+          res = SZ_ERROR_MEM;
+      }
+      if (res == SZ_OK)
+      {
+        res = SzDecode(p->db.PackSizes +
+          p->FolderStartPackStreamIndex[folderIndex], folder,
+          inStream, startOffset,
+          *outBuffer, unpackSize, allocTemp);
+        if (res == SZ_OK)
+        {
+          if (folder->UnpackCRCDefined)
+          {
+            if (CrcCalc(*outBuffer, unpackSize) != folder->UnpackCRC)
+              res = SZ_ERROR_CRC;
+          }
+        }
+      }
+    }
+  }
+  if (res == SZ_OK)
+  {
+    UInt32 i;
+    CSzFileItem *fileItem = p->db.Files + fileIndex;
+    *offset = 0;
+    for (i = p->FolderStartFileIndex[folderIndex]; i < fileIndex; i++)
+      *offset += (UInt32)p->db.Files[i].Size;
+    *outSizeProcessed = (size_t)fileItem->Size;
+    if (*offset + *outSizeProcessed > *outBufferSize)
+      return SZ_ERROR_FAIL;
+    {
+      if (fileItem->FileCRCDefined)
+      {
+        if (CrcCalc(*outBuffer + *offset, *outSizeProcessed) != fileItem->FileCRC)
+          res = SZ_ERROR_CRC;
+      }
+    }
+  }
+  return res;
+}
diff --git a/third_party/lzma/v4_65/files/C/Archive/7z/7zExtract.h b/third_party/lzma/v4_65/files/C/Archive/7z/7zExtract.h
new file mode 100644
index 0000000..5f78415
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Archive/7z/7zExtract.h
@@ -0,0 +1,41 @@
+/* 7zExtract.h -- Extracting from 7z archive
+2008-11-23 : Igor Pavlov : Public domain */
+
+#ifndef __7Z_EXTRACT_H
+#define __7Z_EXTRACT_H
+
+#include "7zIn.h"
+
+/*
+  SzExtract extracts file from archive
+
+  *outBuffer must be 0 before first call for each new archive.
+
+  Extracting cache:
+    If you need to decompress more than one file, you can send
+    these values from previous call:
+      *blockIndex,
+      *outBuffer,
+      *outBufferSize
+    You can consider "*outBuffer" as cache of solid block. If your archive is solid,
+    it will increase decompression speed.
+  
+    If you use external function, you can declare these 3 cache variables
+    (blockIndex, outBuffer, outBufferSize) as static in that external function.
+    
+    Free *outBuffer and set *outBuffer to 0, if you want to flush cache.
+*/
+
+SRes SzAr_Extract(
+    const CSzArEx *db,
+    ILookInStream *inStream,
+    UInt32 fileIndex,         /* index of file */
+    UInt32 *blockIndex,       /* index of solid block */
+    Byte **outBuffer,         /* pointer to pointer to output buffer (allocated with allocMain) */
+    size_t *outBufferSize,    /* buffer size for output buffer */
+    size_t *offset,           /* offset of stream for required file in *outBuffer */
+    size_t *outSizeProcessed, /* size of file in *outBuffer */
+    ISzAlloc *allocMain,
+    ISzAlloc *allocTemp);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/Archive/7z/7zHeader.c b/third_party/lzma/v4_65/files/C/Archive/7z/7zHeader.c
new file mode 100644
index 0000000..e48faa4
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Archive/7z/7zHeader.c
@@ -0,0 +1,6 @@
+/*  7zHeader.c -- 7z Headers
+2008-10-04 : Igor Pavlov : Public domain */
+
+#include "7zHeader.h"
+
+Byte k7zSignature[k7zSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C};
diff --git a/third_party/lzma/v4_65/files/C/Archive/7z/7zHeader.h b/third_party/lzma/v4_65/files/C/Archive/7z/7zHeader.h
new file mode 100644
index 0000000..9941b6f
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Archive/7z/7zHeader.h
@@ -0,0 +1,57 @@
+/* 7zHeader.h -- 7z Headers
+2008-10-04 : Igor Pavlov : Public domain */
+
+#ifndef __7Z_HEADER_H
+#define __7Z_HEADER_H
+
+#include "../../Types.h"
+
+#define k7zSignatureSize 6
+extern Byte k7zSignature[k7zSignatureSize];
+
+#define k7zMajorVersion 0
+
+#define k7zStartHeaderSize 0x20
+
+enum EIdEnum
+{
+  k7zIdEnd,
+    
+  k7zIdHeader,
+    
+  k7zIdArchiveProperties,
+    
+  k7zIdAdditionalStreamsInfo,
+  k7zIdMainStreamsInfo,
+  k7zIdFilesInfo,
+  
+  k7zIdPackInfo,
+  k7zIdUnpackInfo,
+  k7zIdSubStreamsInfo,
+  
+  k7zIdSize,
+  k7zIdCRC,
+  
+  k7zIdFolder,
+  
+  k7zIdCodersUnpackSize,
+  k7zIdNumUnpackStream,
+  
+  k7zIdEmptyStream,
+  k7zIdEmptyFile,
+  k7zIdAnti,
+  
+  k7zIdName,
+  k7zIdCTime,
+  k7zIdATime,
+  k7zIdMTime,
+  k7zIdWinAttributes,
+  k7zIdComment,
+  
+  k7zIdEncodedHeader,
+  
+  k7zIdStartPos,
+  k7zIdDummy
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/Archive/7z/7zIn.c b/third_party/lzma/v4_65/files/C/Archive/7z/7zIn.c
new file mode 100644
index 0000000..f6143f8
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Archive/7z/7zIn.c
@@ -0,0 +1,1204 @@
+/* 7zIn.c -- 7z Input functions
+2008-12-31 : Igor Pavlov : Public domain */
+
+#include "../../7zCrc.h"
+#include "../../CpuArch.h"
+
+#include "7zDecode.h"
+#include "7zIn.h"
+
+#define RINOM(x) { if ((x) == 0) return SZ_ERROR_MEM; }
+
+#define NUM_FOLDER_CODERS_MAX 32
+#define NUM_CODER_STREAMS_MAX 32
+
+void SzArEx_Init(CSzArEx *p)
+{
+  SzAr_Init(&p->db);
+  p->FolderStartPackStreamIndex = 0;
+  p->PackStreamStartPositions = 0;
+  p->FolderStartFileIndex = 0;
+  p->FileIndexToFolderIndexMap = 0;
+}
+
+void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc)
+{
+  IAlloc_Free(alloc, p->FolderStartPackStreamIndex);
+  IAlloc_Free(alloc, p->PackStreamStartPositions);
+  IAlloc_Free(alloc, p->FolderStartFileIndex);
+  IAlloc_Free(alloc, p->FileIndexToFolderIndexMap);
+  SzAr_Free(&p->db, alloc);
+  SzArEx_Init(p);
+}
+
+/*
+UInt64 GetFolderPackStreamSize(int folderIndex, int streamIndex) const
+{
+  return PackSizes[FolderStartPackStreamIndex[folderIndex] + streamIndex];
+}
+
+UInt64 GetFilePackSize(int fileIndex) const
+{
+  int folderIndex = FileIndexToFolderIndexMap[fileIndex];
+  if (folderIndex >= 0)
+  {
+    const CSzFolder &folderInfo = Folders[folderIndex];
+    if (FolderStartFileIndex[folderIndex] == fileIndex)
+    return GetFolderFullPackSize(folderIndex);
+  }
+  return 0;
+}
+*/
+
+#define MY_ALLOC(T, p, size, alloc) { if ((size) == 0) p = 0; else \
+  if ((p = (T *)IAlloc_Alloc(alloc, (size) * sizeof(T))) == 0) return SZ_ERROR_MEM; }
+
+static SRes SzArEx_Fill(CSzArEx *p, ISzAlloc *alloc)
+{
+  UInt32 startPos = 0;
+  UInt64 startPosSize = 0;
+  UInt32 i;
+  UInt32 folderIndex = 0;
+  UInt32 indexInFolder = 0;
+  MY_ALLOC(UInt32, p->FolderStartPackStreamIndex, p->db.NumFolders, alloc);
+  for (i = 0; i < p->db.NumFolders; i++)
+  {
+    p->FolderStartPackStreamIndex[i] = startPos;
+    startPos += p->db.Folders[i].NumPackStreams;
+  }
+
+  MY_ALLOC(UInt64, p->PackStreamStartPositions, p->db.NumPackStreams, alloc);
+
+  for (i = 0; i < p->db.NumPackStreams; i++)
+  {
+    p->PackStreamStartPositions[i] = startPosSize;
+    startPosSize += p->db.PackSizes[i];
+  }
+
+  MY_ALLOC(UInt32, p->FolderStartFileIndex, p->db.NumFolders, alloc);
+  MY_ALLOC(UInt32, p->FileIndexToFolderIndexMap, p->db.NumFiles, alloc);
+
+  for (i = 0; i < p->db.NumFiles; i++)
+  {
+    CSzFileItem *file = p->db.Files + i;
+    int emptyStream = !file->HasStream;
+    if (emptyStream && indexInFolder == 0)
+    {
+      p->FileIndexToFolderIndexMap[i] = (UInt32)-1;
+      continue;
+    }
+    if (indexInFolder == 0)
+    {
+      /*
+      v3.13 incorrectly worked with empty folders
+      v4.07: Loop for skipping empty folders
+      */
+      for (;;)
+      {
+        if (folderIndex >= p->db.NumFolders)
+          return SZ_ERROR_ARCHIVE;
+        p->FolderStartFileIndex[folderIndex] = i;
+        if (p->db.Folders[folderIndex].NumUnpackStreams != 0)
+          break;
+        folderIndex++;
+      }
+    }
+    p->FileIndexToFolderIndexMap[i] = folderIndex;
+    if (emptyStream)
+      continue;
+    indexInFolder++;
+    if (indexInFolder >= p->db.Folders[folderIndex].NumUnpackStreams)
+    {
+      folderIndex++;
+      indexInFolder = 0;
+    }
+  }
+  return SZ_OK;
+}
+
+
+UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder)
+{
+  return p->dataPos +
+    p->PackStreamStartPositions[p->FolderStartPackStreamIndex[folderIndex] + indexInFolder];
+}
+
+int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize)
+{
+  UInt32 packStreamIndex = p->FolderStartPackStreamIndex[folderIndex];
+  CSzFolder *folder = p->db.Folders + folderIndex;
+  UInt64 size = 0;
+  UInt32 i;
+  for (i = 0; i < folder->NumPackStreams; i++)
+  {
+    UInt64 t = size + p->db.PackSizes[packStreamIndex + i];
+    if (t < size) /* check it */
+      return SZ_ERROR_FAIL;
+    size = t;
+  }
+  *resSize = size;
+  return SZ_OK;
+}
+
+
+/*
+SRes SzReadTime(const CObjectVector<CBuf> &dataVector,
+    CObjectVector<CSzFileItem> &files, UInt64 type)
+{
+  CBoolVector boolVector;
+  RINOK(ReadBoolVector2(files.Size(), boolVector))
+
+  CStreamSwitch streamSwitch;
+  RINOK(streamSwitch.Set(this, &dataVector));
+
+  for (int i = 0; i < files.Size(); i++)
+  {
+    CSzFileItem &file = files[i];
+    CArchiveFileTime fileTime;
+    bool defined = boolVector[i];
+    if (defined)
+    {
+      UInt32 low, high;
+      RINOK(SzReadUInt32(low));
+      RINOK(SzReadUInt32(high));
+      fileTime.dwLowDateTime = low;
+      fileTime.dwHighDateTime = high;
+    }
+    switch(type)
+    {
+      case k7zIdCTime: file.IsCTimeDefined = defined; if (defined) file.CTime = fileTime; break;
+      case k7zIdATime: file.IsATimeDefined = defined; if (defined) file.ATime = fileTime; break;
+      case k7zIdMTime: file.IsMTimeDefined = defined; if (defined) file.MTime = fileTime; break;
+    }
+  }
+  return SZ_OK;
+}
+*/
+
+static int TestSignatureCandidate(Byte *testBytes)
+{
+  size_t i;
+  for (i = 0; i < k7zSignatureSize; i++)
+    if (testBytes[i] != k7zSignature[i])
+      return 0;
+  return 1;
+}
+
+typedef struct _CSzState
+{
+  Byte *Data;
+  size_t Size;
+}CSzData;
+
+static SRes SzReadByte(CSzData *sd, Byte *b)
+{
+  if (sd->Size == 0)
+    return SZ_ERROR_ARCHIVE;
+  sd->Size--;
+  *b = *sd->Data++;
+  return SZ_OK;
+}
+
+static SRes SzReadBytes(CSzData *sd, Byte *data, size_t size)
+{
+  size_t i;
+  for (i = 0; i < size; i++)
+  {
+    RINOK(SzReadByte(sd, data + i));
+  }
+  return SZ_OK;
+}
+
+static SRes SzReadUInt32(CSzData *sd, UInt32 *value)
+{
+  int i;
+  *value = 0;
+  for (i = 0; i < 4; i++)
+  {
+    Byte b;
+    RINOK(SzReadByte(sd, &b));
+    *value |= ((UInt32)(b) << (8 * i));
+  }
+  return SZ_OK;
+}
+
+static SRes SzReadNumber(CSzData *sd, UInt64 *value)
+{
+  Byte firstByte;
+  Byte mask = 0x80;
+  int i;
+  RINOK(SzReadByte(sd, &firstByte));
+  *value = 0;
+  for (i = 0; i < 8; i++)
+  {
+    Byte b;
+    if ((firstByte & mask) == 0)
+    {
+      UInt64 highPart = firstByte & (mask - 1);
+      *value += (highPart << (8 * i));
+      return SZ_OK;
+    }
+    RINOK(SzReadByte(sd, &b));
+    *value |= ((UInt64)b << (8 * i));
+    mask >>= 1;
+  }
+  return SZ_OK;
+}
+
+static SRes SzReadNumber32(CSzData *sd, UInt32 *value)
+{
+  UInt64 value64;
+  RINOK(SzReadNumber(sd, &value64));
+  if (value64 >= 0x80000000)
+    return SZ_ERROR_UNSUPPORTED;
+  if (value64 >= ((UInt64)(1) << ((sizeof(size_t) - 1) * 8 + 2)))
+    return SZ_ERROR_UNSUPPORTED;
+  *value = (UInt32)value64;
+  return SZ_OK;
+}
+
+static SRes SzReadID(CSzData *sd, UInt64 *value)
+{
+  return SzReadNumber(sd, value);
+}
+
+static SRes SzSkeepDataSize(CSzData *sd, UInt64 size)
+{
+  if (size > sd->Size)
+    return SZ_ERROR_ARCHIVE;
+  sd->Size -= (size_t)size;
+  sd->Data += (size_t)size;
+  return SZ_OK;
+}
+
+static SRes SzSkeepData(CSzData *sd)
+{
+  UInt64 size;
+  RINOK(SzReadNumber(sd, &size));
+  return SzSkeepDataSize(sd, size);
+}
+
+static SRes SzReadArchiveProperties(CSzData *sd)
+{
+  for (;;)
+  {
+    UInt64 type;
+    RINOK(SzReadID(sd, &type));
+    if (type == k7zIdEnd)
+      break;
+    SzSkeepData(sd);
+  }
+  return SZ_OK;
+}
+
+static SRes SzWaitAttribute(CSzData *sd, UInt64 attribute)
+{
+  for (;;)
+  {
+    UInt64 type;
+    RINOK(SzReadID(sd, &type));
+    if (type == attribute)
+      return SZ_OK;
+    if (type == k7zIdEnd)
+      return SZ_ERROR_ARCHIVE;
+    RINOK(SzSkeepData(sd));
+  }
+}
+
+static SRes SzReadBoolVector(CSzData *sd, size_t numItems, Byte **v, ISzAlloc *alloc)
+{
+  Byte b = 0;
+  Byte mask = 0;
+  size_t i;
+  MY_ALLOC(Byte, *v, numItems, alloc);
+  for (i = 0; i < numItems; i++)
+  {
+    if (mask == 0)
+    {
+      RINOK(SzReadByte(sd, &b));
+      mask = 0x80;
+    }
+    (*v)[i] = (Byte)(((b & mask) != 0) ? 1 : 0);
+    mask >>= 1;
+  }
+  return SZ_OK;
+}
+
+static SRes SzReadBoolVector2(CSzData *sd, size_t numItems, Byte **v, ISzAlloc *alloc)
+{
+  Byte allAreDefined;
+  size_t i;
+  RINOK(SzReadByte(sd, &allAreDefined));
+  if (allAreDefined == 0)
+    return SzReadBoolVector(sd, numItems, v, alloc);
+  MY_ALLOC(Byte, *v, numItems, alloc);
+  for (i = 0; i < numItems; i++)
+    (*v)[i] = 1;
+  return SZ_OK;
+}
+
+static SRes SzReadHashDigests(
+    CSzData *sd,
+    size_t numItems,
+    Byte **digestsDefined,
+    UInt32 **digests,
+    ISzAlloc *alloc)
+{
+  size_t i;
+  RINOK(SzReadBoolVector2(sd, numItems, digestsDefined, alloc));
+  MY_ALLOC(UInt32, *digests, numItems, alloc);
+  for (i = 0; i < numItems; i++)
+    if ((*digestsDefined)[i])
+    {
+      RINOK(SzReadUInt32(sd, (*digests) + i));
+    }
+  return SZ_OK;
+}
+
+static SRes SzReadPackInfo(
+    CSzData *sd,
+    UInt64 *dataOffset,
+    UInt32 *numPackStreams,
+    UInt64 **packSizes,
+    Byte **packCRCsDefined,
+    UInt32 **packCRCs,
+    ISzAlloc *alloc)
+{
+  UInt32 i;
+  RINOK(SzReadNumber(sd, dataOffset));
+  RINOK(SzReadNumber32(sd, numPackStreams));
+
+  RINOK(SzWaitAttribute(sd, k7zIdSize));
+
+  MY_ALLOC(UInt64, *packSizes, (size_t)*numPackStreams, alloc);
+
+  for (i = 0; i < *numPackStreams; i++)
+  {
+    RINOK(SzReadNumber(sd, (*packSizes) + i));
+  }
+
+  for (;;)
+  {
+    UInt64 type;
+    RINOK(SzReadID(sd, &type));
+    if (type == k7zIdEnd)
+      break;
+    if (type == k7zIdCRC)
+    {
+      RINOK(SzReadHashDigests(sd, (size_t)*numPackStreams, packCRCsDefined, packCRCs, alloc));
+      continue;
+    }
+    RINOK(SzSkeepData(sd));
+  }
+  if (*packCRCsDefined == 0)
+  {
+    MY_ALLOC(Byte, *packCRCsDefined, (size_t)*numPackStreams, alloc);
+    MY_ALLOC(UInt32, *packCRCs, (size_t)*numPackStreams, alloc);
+    for (i = 0; i < *numPackStreams; i++)
+    {
+      (*packCRCsDefined)[i] = 0;
+      (*packCRCs)[i] = 0;
+    }
+  }
+  return SZ_OK;
+}
+
+static SRes SzReadSwitch(CSzData *sd)
+{
+  Byte external;
+  RINOK(SzReadByte(sd, &external));
+  return (external == 0) ? SZ_OK: SZ_ERROR_UNSUPPORTED;
+}
+
+static SRes SzGetNextFolderItem(CSzData *sd, CSzFolder *folder, ISzAlloc *alloc)
+{
+  UInt32 numCoders, numBindPairs, numPackStreams, i;
+  UInt32 numInStreams = 0, numOutStreams = 0;
+  
+  RINOK(SzReadNumber32(sd, &numCoders));
+  if (numCoders > NUM_FOLDER_CODERS_MAX)
+    return SZ_ERROR_UNSUPPORTED;
+  folder->NumCoders = numCoders;
+  
+  MY_ALLOC(CSzCoderInfo, folder->Coders, (size_t)numCoders, alloc);
+
+  for (i = 0; i < numCoders; i++)
+    SzCoderInfo_Init(folder->Coders + i);
+
+  for (i = 0; i < numCoders; i++)
+  {
+    Byte mainByte;
+    CSzCoderInfo *coder = folder->Coders + i;
+    {
+      unsigned idSize, j;
+      Byte longID[15];
+      RINOK(SzReadByte(sd, &mainByte));
+      idSize = (unsigned)(mainByte & 0xF);
+      RINOK(SzReadBytes(sd, longID, idSize));
+      if (idSize > sizeof(coder->MethodID))
+        return SZ_ERROR_UNSUPPORTED;
+      coder->MethodID = 0;
+      for (j = 0; j < idSize; j++)
+        coder->MethodID |= (UInt64)longID[idSize - 1 - j] << (8 * j);
+
+      if ((mainByte & 0x10) != 0)
+      {
+        RINOK(SzReadNumber32(sd, &coder->NumInStreams));
+        RINOK(SzReadNumber32(sd, &coder->NumOutStreams));
+        if (coder->NumInStreams > NUM_CODER_STREAMS_MAX ||
+            coder->NumOutStreams > NUM_CODER_STREAMS_MAX)
+          return SZ_ERROR_UNSUPPORTED;
+      }
+      else
+      {
+        coder->NumInStreams = 1;
+        coder->NumOutStreams = 1;
+      }
+      if ((mainByte & 0x20) != 0)
+      {
+        UInt64 propertiesSize = 0;
+        RINOK(SzReadNumber(sd, &propertiesSize));
+        if (!Buf_Create(&coder->Props, (size_t)propertiesSize, alloc))
+          return SZ_ERROR_MEM;
+        RINOK(SzReadBytes(sd, coder->Props.data, (size_t)propertiesSize));
+      }
+    }
+    while ((mainByte & 0x80) != 0)
+    {
+      RINOK(SzReadByte(sd, &mainByte));
+      RINOK(SzSkeepDataSize(sd, (mainByte & 0xF)));
+      if ((mainByte & 0x10) != 0)
+      {
+        UInt32 n;
+        RINOK(SzReadNumber32(sd, &n));
+        RINOK(SzReadNumber32(sd, &n));
+      }
+      if ((mainByte & 0x20) != 0)
+      {
+        UInt64 propertiesSize = 0;
+        RINOK(SzReadNumber(sd, &propertiesSize));
+        RINOK(SzSkeepDataSize(sd, propertiesSize));
+      }
+    }
+    numInStreams += coder->NumInStreams;
+    numOutStreams += coder->NumOutStreams;
+  }
+
+  if (numOutStreams == 0)
+    return SZ_ERROR_UNSUPPORTED;
+
+  folder->NumBindPairs = numBindPairs = numOutStreams - 1;
+  MY_ALLOC(CBindPair, folder->BindPairs, (size_t)numBindPairs, alloc);
+
+  for (i = 0; i < numBindPairs; i++)
+  {
+    CBindPair *bp = folder->BindPairs + i;
+    RINOK(SzReadNumber32(sd, &bp->InIndex));
+    RINOK(SzReadNumber32(sd, &bp->OutIndex));
+  }
+
+  if (numInStreams < numBindPairs)
+    return SZ_ERROR_UNSUPPORTED;
+
+  folder->NumPackStreams = numPackStreams = numInStreams - numBindPairs;
+  MY_ALLOC(UInt32, folder->PackStreams, (size_t)numPackStreams, alloc);
+
+  if (numPackStreams == 1)
+  {
+    for (i = 0; i < numInStreams ; i++)
+      if (SzFolder_FindBindPairForInStream(folder, i) < 0)
+        break;
+    if (i == numInStreams)
+      return SZ_ERROR_UNSUPPORTED;
+    folder->PackStreams[0] = i;
+  }
+  else
+    for (i = 0; i < numPackStreams; i++)
+    {
+      RINOK(SzReadNumber32(sd, folder->PackStreams + i));
+    }
+  return SZ_OK;
+}
+
+static SRes SzReadUnpackInfo(
+    CSzData *sd,
+    UInt32 *numFolders,
+    CSzFolder **folders,  /* for alloc */
+    ISzAlloc *alloc,
+    ISzAlloc *allocTemp)
+{
+  UInt32 i;
+  RINOK(SzWaitAttribute(sd, k7zIdFolder));
+  RINOK(SzReadNumber32(sd, numFolders));
+  {
+    RINOK(SzReadSwitch(sd));
+
+    MY_ALLOC(CSzFolder, *folders, (size_t)*numFolders, alloc);
+
+    for (i = 0; i < *numFolders; i++)
+      SzFolder_Init((*folders) + i);
+
+    for (i = 0; i < *numFolders; i++)
+    {
+      RINOK(SzGetNextFolderItem(sd, (*folders) + i, alloc));
+    }
+  }
+
+  RINOK(SzWaitAttribute(sd, k7zIdCodersUnpackSize));
+
+  for (i = 0; i < *numFolders; i++)
+  {
+    UInt32 j;
+    CSzFolder *folder = (*folders) + i;
+    UInt32 numOutStreams = SzFolder_GetNumOutStreams(folder);
+
+    MY_ALLOC(UInt64, folder->UnpackSizes, (size_t)numOutStreams, alloc);
+
+    for (j = 0; j < numOutStreams; j++)
+    {
+      RINOK(SzReadNumber(sd, folder->UnpackSizes + j));
+    }
+  }
+
+  for (;;)
+  {
+    UInt64 type;
+    RINOK(SzReadID(sd, &type));
+    if (type == k7zIdEnd)
+      return SZ_OK;
+    if (type == k7zIdCRC)
+    {
+      SRes res;
+      Byte *crcsDefined = 0;
+      UInt32 *crcs = 0;
+      res = SzReadHashDigests(sd, *numFolders, &crcsDefined, &crcs, allocTemp);
+      if (res == SZ_OK)
+      {
+        for (i = 0; i < *numFolders; i++)
+        {
+          CSzFolder *folder = (*folders) + i;
+          folder->UnpackCRCDefined = crcsDefined[i];
+          folder->UnpackCRC = crcs[i];
+        }
+      }
+      IAlloc_Free(allocTemp, crcs);
+      IAlloc_Free(allocTemp, crcsDefined);
+      RINOK(res);
+      continue;
+    }
+    RINOK(SzSkeepData(sd));
+  }
+}
+
+static SRes SzReadSubStreamsInfo(
+    CSzData *sd,
+    UInt32 numFolders,
+    CSzFolder *folders,
+    UInt32 *numUnpackStreams,
+    UInt64 **unpackSizes,
+    Byte **digestsDefined,
+    UInt32 **digests,
+    ISzAlloc *allocTemp)
+{
+  UInt64 type = 0;
+  UInt32 i;
+  UInt32 si = 0;
+  UInt32 numDigests = 0;
+
+  for (i = 0; i < numFolders; i++)
+    folders[i].NumUnpackStreams = 1;
+  *numUnpackStreams = numFolders;
+
+  for (;;)
+  {
+    RINOK(SzReadID(sd, &type));
+    if (type == k7zIdNumUnpackStream)
+    {
+      *numUnpackStreams = 0;
+      for (i = 0; i < numFolders; i++)
+      {
+        UInt32 numStreams;
+        RINOK(SzReadNumber32(sd, &numStreams));
+        folders[i].NumUnpackStreams = numStreams;
+        *numUnpackStreams += numStreams;
+      }
+      continue;
+    }
+    if (type == k7zIdCRC || type == k7zIdSize)
+      break;
+    if (type == k7zIdEnd)
+      break;
+    RINOK(SzSkeepData(sd));
+  }
+
+  if (*numUnpackStreams == 0)
+  {
+    *unpackSizes = 0;
+    *digestsDefined = 0;
+    *digests = 0;
+  }
+  else
+  {
+    *unpackSizes = (UInt64 *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(UInt64));
+    RINOM(*unpackSizes);
+    *digestsDefined = (Byte *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(Byte));
+    RINOM(*digestsDefined);
+    *digests = (UInt32 *)IAlloc_Alloc(allocTemp, (size_t)*numUnpackStreams * sizeof(UInt32));
+    RINOM(*digests);
+  }
+
+  for (i = 0; i < numFolders; i++)
+  {
+    /*
+    v3.13 incorrectly worked with empty folders
+    v4.07: we check that folder is empty
+    */
+    UInt64 sum = 0;
+    UInt32 j;
+    UInt32 numSubstreams = folders[i].NumUnpackStreams;
+    if (numSubstreams == 0)
+      continue;
+    if (type == k7zIdSize)
+    for (j = 1; j < numSubstreams; j++)
+    {
+      UInt64 size;
+      RINOK(SzReadNumber(sd, &size));
+      (*unpackSizes)[si++] = size;
+      sum += size;
+    }
+    (*unpackSizes)[si++] = SzFolder_GetUnpackSize(folders + i) - sum;
+  }
+  if (type == k7zIdSize)
+  {
+    RINOK(SzReadID(sd, &type));
+  }
+
+  for (i = 0; i < *numUnpackStreams; i++)
+  {
+    (*digestsDefined)[i] = 0;
+    (*digests)[i] = 0;
+  }
+
+
+  for (i = 0; i < numFolders; i++)
+  {
+    UInt32 numSubstreams = folders[i].NumUnpackStreams;
+    if (numSubstreams != 1 || !folders[i].UnpackCRCDefined)
+      numDigests += numSubstreams;
+  }
+
+ 
+  si = 0;
+  for (;;)
+  {
+    if (type == k7zIdCRC)
+    {
+      int digestIndex = 0;
+      Byte *digestsDefined2 = 0;
+      UInt32 *digests2 = 0;
+      SRes res = SzReadHashDigests(sd, numDigests, &digestsDefined2, &digests2, allocTemp);
+      if (res == SZ_OK)
+      {
+        for (i = 0; i < numFolders; i++)
+        {
+          CSzFolder *folder = folders + i;
+          UInt32 numSubstreams = folder->NumUnpackStreams;
+          if (numSubstreams == 1 && folder->UnpackCRCDefined)
+          {
+            (*digestsDefined)[si] = 1;
+            (*digests)[si] = folder->UnpackCRC;
+            si++;
+          }
+          else
+          {
+            UInt32 j;
+            for (j = 0; j < numSubstreams; j++, digestIndex++)
+            {
+              (*digestsDefined)[si] = digestsDefined2[digestIndex];
+              (*digests)[si] = digests2[digestIndex];
+              si++;
+            }
+          }
+        }
+      }
+      IAlloc_Free(allocTemp, digestsDefined2);
+      IAlloc_Free(allocTemp, digests2);
+      RINOK(res);
+    }
+    else if (type == k7zIdEnd)
+      return SZ_OK;
+    else
+    {
+      RINOK(SzSkeepData(sd));
+    }
+    RINOK(SzReadID(sd, &type));
+  }
+}
+
+
+static SRes SzReadStreamsInfo(
+    CSzData *sd,
+    UInt64 *dataOffset,
+    CSzAr *p,
+    UInt32 *numUnpackStreams,
+    UInt64 **unpackSizes, /* allocTemp */
+    Byte **digestsDefined,   /* allocTemp */
+    UInt32 **digests,        /* allocTemp */
+    ISzAlloc *alloc,
+    ISzAlloc *allocTemp)
+{
+  for (;;)
+  {
+    UInt64 type;
+    RINOK(SzReadID(sd, &type));
+    if ((UInt64)(int)type != type)
+      return SZ_ERROR_UNSUPPORTED;
+    switch((int)type)
+    {
+      case k7zIdEnd:
+        return SZ_OK;
+      case k7zIdPackInfo:
+      {
+        RINOK(SzReadPackInfo(sd, dataOffset, &p->NumPackStreams,
+            &p->PackSizes, &p->PackCRCsDefined, &p->PackCRCs, alloc));
+        break;
+      }
+      case k7zIdUnpackInfo:
+      {
+        RINOK(SzReadUnpackInfo(sd, &p->NumFolders, &p->Folders, alloc, allocTemp));
+        break;
+      }
+      case k7zIdSubStreamsInfo:
+      {
+        RINOK(SzReadSubStreamsInfo(sd, p->NumFolders, p->Folders,
+            numUnpackStreams, unpackSizes, digestsDefined, digests, allocTemp));
+        break;
+      }
+      default:
+        return SZ_ERROR_UNSUPPORTED;
+    }
+  }
+}
+
+Byte kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+static SRes SzReadFileNames(CSzData *sd, UInt32 numFiles, CSzFileItem *files, ISzAlloc *alloc)
+{
+  UInt32 i;
+  for (i = 0; i < numFiles; i++)
+  {
+    UInt32 len = 0;
+    UInt32 pos = 0;
+    CSzFileItem *file = files + i;
+    while (pos + 2 <= sd->Size)
+    {
+      int numAdds;
+      UInt32 value = (UInt32)(sd->Data[pos] | (((UInt32)sd->Data[pos + 1]) << 8));
+      pos += 2;
+      len++;
+      if (value == 0)
+        break;
+      if (value < 0x80)
+        continue;
+      if (value >= 0xD800 && value < 0xE000)
+      {
+        UInt32 c2;
+        if (value >= 0xDC00)
+          return SZ_ERROR_ARCHIVE;
+        if (pos + 2 > sd->Size)
+          return SZ_ERROR_ARCHIVE;
+        c2 = (UInt32)(sd->Data[pos] | (((UInt32)sd->Data[pos + 1]) << 8));
+        pos += 2;
+        if (c2 < 0xDC00 || c2 >= 0xE000)
+          return SZ_ERROR_ARCHIVE;
+        value = ((value - 0xD800) << 10) | (c2 - 0xDC00);
+      }
+      for (numAdds = 1; numAdds < 5; numAdds++)
+        if (value < (((UInt32)1) << (numAdds * 5 + 6)))
+          break;
+      len += numAdds;
+    }
+
+    MY_ALLOC(char, file->Name, (size_t)len, alloc);
+
+    len = 0;
+    while (2 <= sd->Size)
+    {
+      int numAdds;
+      UInt32 value = (UInt32)(sd->Data[0] | (((UInt32)sd->Data[1]) << 8));
+      SzSkeepDataSize(sd, 2);
+      if (value < 0x80)
+      {
+        file->Name[len++] = (char)value;
+        if (value == 0)
+          break;
+        continue;
+      }
+      if (value >= 0xD800 && value < 0xE000)
+      {
+        UInt32 c2 = (UInt32)(sd->Data[0] | (((UInt32)sd->Data[1]) << 8));
+        SzSkeepDataSize(sd, 2);
+        value = ((value - 0xD800) << 10) | (c2 - 0xDC00);
+      }
+      for (numAdds = 1; numAdds < 5; numAdds++)
+        if (value < (((UInt32)1) << (numAdds * 5 + 6)))
+          break;
+      file->Name[len++] = (char)(kUtf8Limits[numAdds - 1] + (value >> (6 * numAdds)));
+      do
+      {
+        numAdds--;
+        file->Name[len++] = (char)(0x80 + ((value >> (6 * numAdds)) & 0x3F));
+      }
+      while (numAdds > 0);
+
+      len += numAdds;
+    }
+  }
+  return SZ_OK;
+}
+
+static SRes SzReadHeader2(
+    CSzArEx *p,   /* allocMain */
+    CSzData *sd,
+    UInt64 **unpackSizes,  /* allocTemp */
+    Byte **digestsDefined,    /* allocTemp */
+    UInt32 **digests,         /* allocTemp */
+    Byte **emptyStreamVector, /* allocTemp */
+    Byte **emptyFileVector,   /* allocTemp */
+    Byte **lwtVector,         /* allocTemp */
+    ISzAlloc *allocMain,
+    ISzAlloc *allocTemp)
+{
+  UInt64 type;
+  UInt32 numUnpackStreams = 0;
+  UInt32 numFiles = 0;
+  CSzFileItem *files = 0;
+  UInt32 numEmptyStreams = 0;
+  UInt32 i;
+
+  RINOK(SzReadID(sd, &type));
+
+  if (type == k7zIdArchiveProperties)
+  {
+    RINOK(SzReadArchiveProperties(sd));
+    RINOK(SzReadID(sd, &type));
+  }
+ 
+ 
+  if (type == k7zIdMainStreamsInfo)
+  {
+    RINOK(SzReadStreamsInfo(sd,
+        &p->dataPos,
+        &p->db,
+        &numUnpackStreams,
+        unpackSizes,
+        digestsDefined,
+        digests, allocMain, allocTemp));
+    p->dataPos += p->startPosAfterHeader;
+    RINOK(SzReadID(sd, &type));
+  }
+
+  if (type == k7zIdEnd)
+    return SZ_OK;
+  if (type != k7zIdFilesInfo)
+    return SZ_ERROR_ARCHIVE;
+  
+  RINOK(SzReadNumber32(sd, &numFiles));
+  p->db.NumFiles = numFiles;
+
+  MY_ALLOC(CSzFileItem, files, (size_t)numFiles, allocMain);
+
+  p->db.Files = files;
+  for (i = 0; i < numFiles; i++)
+    SzFile_Init(files + i);
+
+  for (;;)
+  {
+    UInt64 type;
+    UInt64 size;
+    RINOK(SzReadID(sd, &type));
+    if (type == k7zIdEnd)
+      break;
+    RINOK(SzReadNumber(sd, &size));
+
+    if ((UInt64)(int)type != type)
+    {
+      RINOK(SzSkeepDataSize(sd, size));
+    }
+    else
+    switch((int)type)
+    {
+      case k7zIdName:
+      {
+        RINOK(SzReadSwitch(sd));
+        RINOK(SzReadFileNames(sd, numFiles, files, allocMain))
+        break;
+      }
+      case k7zIdEmptyStream:
+      {
+        RINOK(SzReadBoolVector(sd, numFiles, emptyStreamVector, allocTemp));
+        numEmptyStreams = 0;
+        for (i = 0; i < numFiles; i++)
+          if ((*emptyStreamVector)[i])
+            numEmptyStreams++;
+        break;
+      }
+      case k7zIdEmptyFile:
+      {
+        RINOK(SzReadBoolVector(sd, numEmptyStreams, emptyFileVector, allocTemp));
+        break;
+      }
+      case k7zIdMTime:
+      {
+        RINOK(SzReadBoolVector2(sd, numFiles, lwtVector, allocTemp));
+        RINOK(SzReadSwitch(sd));
+        for (i = 0; i < numFiles; i++)
+        {
+          CSzFileItem *f = &files[i];
+          Byte defined = (*lwtVector)[i];
+          f->MTimeDefined = defined;
+          f->MTime.Low = f->MTime.High = 0;
+          if (defined)
+          {
+            RINOK(SzReadUInt32(sd, &f->MTime.Low));
+            RINOK(SzReadUInt32(sd, &f->MTime.High));
+          }
+        }
+        break;
+      }
+      default:
+      {
+        RINOK(SzSkeepDataSize(sd, size));
+      }
+    }
+  }
+
+  {
+    UInt32 emptyFileIndex = 0;
+    UInt32 sizeIndex = 0;
+    for (i = 0; i < numFiles; i++)
+    {
+      CSzFileItem *file = files + i;
+      file->IsAnti = 0;
+      if (*emptyStreamVector == 0)
+        file->HasStream = 1;
+      else
+        file->HasStream = (Byte)((*emptyStreamVector)[i] ? 0 : 1);
+      if (file->HasStream)
+      {
+        file->IsDir = 0;
+        file->Size = (*unpackSizes)[sizeIndex];
+        file->FileCRC = (*digests)[sizeIndex];
+        file->FileCRCDefined = (Byte)(*digestsDefined)[sizeIndex];
+        sizeIndex++;
+      }
+      else
+      {
+        if (*emptyFileVector == 0)
+          file->IsDir = 1;
+        else
+          file->IsDir = (Byte)((*emptyFileVector)[emptyFileIndex] ? 0 : 1);
+        emptyFileIndex++;
+        file->Size = 0;
+        file->FileCRCDefined = 0;
+      }
+    }
+  }
+  return SzArEx_Fill(p, allocMain);
+}
+
+static SRes SzReadHeader(
+    CSzArEx *p,
+    CSzData *sd,
+    ISzAlloc *allocMain,
+    ISzAlloc *allocTemp)
+{
+  UInt64 *unpackSizes = 0;
+  Byte *digestsDefined = 0;
+  UInt32 *digests = 0;
+  Byte *emptyStreamVector = 0;
+  Byte *emptyFileVector = 0;
+  Byte *lwtVector = 0;
+  SRes res = SzReadHeader2(p, sd,
+      &unpackSizes, &digestsDefined, &digests,
+      &emptyStreamVector, &emptyFileVector, &lwtVector,
+      allocMain, allocTemp);
+  IAlloc_Free(allocTemp, unpackSizes);
+  IAlloc_Free(allocTemp, digestsDefined);
+  IAlloc_Free(allocTemp, digests);
+  IAlloc_Free(allocTemp, emptyStreamVector);
+  IAlloc_Free(allocTemp, emptyFileVector);
+  IAlloc_Free(allocTemp, lwtVector);
+  return res;
+}
+
+static SRes SzReadAndDecodePackedStreams2(
+    ILookInStream *inStream,
+    CSzData *sd,
+    CBuf *outBuffer,
+    UInt64 baseOffset,
+    CSzAr *p,
+    UInt64 **unpackSizes,
+    Byte **digestsDefined,
+    UInt32 **digests,
+    ISzAlloc *allocTemp)
+{
+
+  UInt32 numUnpackStreams = 0;
+  UInt64 dataStartPos;
+  CSzFolder *folder;
+  UInt64 unpackSize;
+  SRes res;
+
+  RINOK(SzReadStreamsInfo(sd, &dataStartPos, p,
+      &numUnpackStreams,  unpackSizes, digestsDefined, digests,
+      allocTemp, allocTemp));
+  
+  dataStartPos += baseOffset;
+  if (p->NumFolders != 1)
+    return SZ_ERROR_ARCHIVE;
+
+  folder = p->Folders;
+  unpackSize = SzFolder_GetUnpackSize(folder);
+  
+  RINOK(LookInStream_SeekTo(inStream, dataStartPos));
+
+  if (!Buf_Create(outBuffer, (size_t)unpackSize, allocTemp))
+    return SZ_ERROR_MEM;
+  
+  res = SzDecode(p->PackSizes, folder,
+          inStream, dataStartPos,
+          outBuffer->data, (size_t)unpackSize, allocTemp);
+  RINOK(res);
+  if (folder->UnpackCRCDefined)
+    if (CrcCalc(outBuffer->data, (size_t)unpackSize) != folder->UnpackCRC)
+      return SZ_ERROR_CRC;
+  return SZ_OK;
+}
+
+static SRes SzReadAndDecodePackedStreams(
+    ILookInStream *inStream,
+    CSzData *sd,
+    CBuf *outBuffer,
+    UInt64 baseOffset,
+    ISzAlloc *allocTemp)
+{
+  CSzAr p;
+  UInt64 *unpackSizes = 0;
+  Byte *digestsDefined = 0;
+  UInt32 *digests = 0;
+  SRes res;
+  SzAr_Init(&p);
+  res = SzReadAndDecodePackedStreams2(inStream, sd, outBuffer, baseOffset,
+    &p, &unpackSizes, &digestsDefined, &digests,
+    allocTemp);
+  SzAr_Free(&p, allocTemp);
+  IAlloc_Free(allocTemp, unpackSizes);
+  IAlloc_Free(allocTemp, digestsDefined);
+  IAlloc_Free(allocTemp, digests);
+  return res;
+}
+
+static SRes SzArEx_Open2(
+    CSzArEx *p,
+    ILookInStream *inStream,
+    ISzAlloc *allocMain,
+    ISzAlloc *allocTemp)
+{
+  Byte header[k7zStartHeaderSize];
+  UInt64 nextHeaderOffset, nextHeaderSize;
+  size_t nextHeaderSizeT;
+  UInt32 nextHeaderCRC;
+  CBuf buffer;
+  SRes res;
+
+  RINOK(LookInStream_Read2(inStream, header, k7zStartHeaderSize, SZ_ERROR_NO_ARCHIVE));
+
+  if (!TestSignatureCandidate(header))
+    return SZ_ERROR_NO_ARCHIVE;
+  if (header[6] != k7zMajorVersion)
+    return SZ_ERROR_UNSUPPORTED;
+
+  nextHeaderOffset = GetUi64(header + 12);
+  nextHeaderSize = GetUi64(header + 20);
+  nextHeaderCRC = GetUi32(header + 28);
+
+  p->startPosAfterHeader = k7zStartHeaderSize;
+  
+  if (CrcCalc(header + 12, 20) != GetUi32(header + 8))
+    return SZ_ERROR_CRC;
+
+  nextHeaderSizeT = (size_t)nextHeaderSize;
+  if (nextHeaderSizeT != nextHeaderSize)
+    return SZ_ERROR_MEM;
+  if (nextHeaderSizeT == 0)
+    return SZ_OK;
+  if (nextHeaderOffset > nextHeaderOffset + nextHeaderSize ||
+      nextHeaderOffset > nextHeaderOffset + nextHeaderSize + k7zStartHeaderSize)
+    return SZ_ERROR_NO_ARCHIVE;
+
+  {
+    Int64 pos = 0;
+    RINOK(inStream->Seek(inStream, &pos, SZ_SEEK_END));
+    if ((UInt64)pos < nextHeaderOffset ||
+        (UInt64)pos < k7zStartHeaderSize + nextHeaderOffset ||
+        (UInt64)pos < k7zStartHeaderSize + nextHeaderOffset + nextHeaderSize)
+      return SZ_ERROR_INPUT_EOF;
+  }
+
+  RINOK(LookInStream_SeekTo(inStream, k7zStartHeaderSize + nextHeaderOffset));
+
+  if (!Buf_Create(&buffer, nextHeaderSizeT, allocTemp))
+    return SZ_ERROR_MEM;
+
+  res = LookInStream_Read(inStream, buffer.data, nextHeaderSizeT);
+  if (res == SZ_OK)
+  {
+    res = SZ_ERROR_ARCHIVE;
+    if (CrcCalc(buffer.data, nextHeaderSizeT) == nextHeaderCRC)
+    {
+      CSzData sd;
+      UInt64 type;
+      sd.Data = buffer.data;
+      sd.Size = buffer.size;
+      res = SzReadID(&sd, &type);
+      if (res == SZ_OK)
+      {
+        if (type == k7zIdEncodedHeader)
+        {
+          CBuf outBuffer;
+          Buf_Init(&outBuffer);
+          res = SzReadAndDecodePackedStreams(inStream, &sd, &outBuffer, p->startPosAfterHeader, allocTemp);
+          if (res != SZ_OK)
+            Buf_Free(&outBuffer, allocTemp);
+          else
+          {
+            Buf_Free(&buffer, allocTemp);
+            buffer.data = outBuffer.data;
+            buffer.size = outBuffer.size;
+            sd.Data = buffer.data;
+            sd.Size = buffer.size;
+            res = SzReadID(&sd, &type);
+          }
+        }
+      }
+      if (res == SZ_OK)
+      {
+        if (type == k7zIdHeader)
+          res = SzReadHeader(p, &sd, allocMain, allocTemp);
+        else
+          res = SZ_ERROR_UNSUPPORTED;
+      }
+    }
+  }
+  Buf_Free(&buffer, allocTemp);
+  return res;
+}
+
+SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp)
+{
+  SRes res = SzArEx_Open2(p, inStream, allocMain, allocTemp);
+  if (res != SZ_OK)
+    SzArEx_Free(p, allocMain);
+  return res;
+}
diff --git a/third_party/lzma/v4_65/files/C/Archive/7z/7zIn.h b/third_party/lzma/v4_65/files/C/Archive/7z/7zIn.h
new file mode 100644
index 0000000..c8430a7
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Archive/7z/7zIn.h
@@ -0,0 +1,41 @@
+/* 7zIn.h -- 7z Input functions
+2008-11-23 : Igor Pavlov : Public domain */
+
+#ifndef __7Z_IN_H
+#define __7Z_IN_H
+
+#include "7zHeader.h"
+#include "7zItem.h"
+
+typedef struct
+{
+  CSzAr db;
+  
+  UInt64 startPosAfterHeader;
+  UInt64 dataPos;
+
+  UInt32 *FolderStartPackStreamIndex;
+  UInt64 *PackStreamStartPositions;
+  UInt32 *FolderStartFileIndex;
+  UInt32 *FileIndexToFolderIndexMap;
+} CSzArEx;
+
+void SzArEx_Init(CSzArEx *p);
+void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc);
+UInt64 SzArEx_GetFolderStreamPos(const CSzArEx *p, UInt32 folderIndex, UInt32 indexInFolder);
+int SzArEx_GetFolderFullPackSize(const CSzArEx *p, UInt32 folderIndex, UInt64 *resSize);
+
+/*
+Errors:
+SZ_ERROR_NO_ARCHIVE
+SZ_ERROR_ARCHIVE
+SZ_ERROR_UNSUPPORTED
+SZ_ERROR_MEM
+SZ_ERROR_CRC
+SZ_ERROR_INPUT_EOF
+SZ_ERROR_FAIL
+*/
+
+SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, ISzAlloc *allocMain, ISzAlloc *allocTemp);
+ 
+#endif
diff --git a/third_party/lzma/v4_65/files/C/Archive/7z/7zItem.c b/third_party/lzma/v4_65/files/C/Archive/7z/7zItem.c
new file mode 100644
index 0000000..db44571
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Archive/7z/7zItem.c
@@ -0,0 +1,127 @@
+/* 7zItem.c -- 7z Items
+2008-10-04 : Igor Pavlov : Public domain */
+
+#include "7zItem.h"
+
+void SzCoderInfo_Init(CSzCoderInfo *p)
+{
+  Buf_Init(&p->Props);
+}
+
+void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc)
+{
+  Buf_Free(&p->Props, alloc);
+  SzCoderInfo_Init(p);
+}
+
+void SzFolder_Init(CSzFolder *p)
+{
+  p->Coders = 0;
+  p->BindPairs = 0;
+  p->PackStreams = 0;
+  p->UnpackSizes = 0;
+  p->NumCoders = 0;
+  p->NumBindPairs = 0;
+  p->NumPackStreams = 0;
+  p->UnpackCRCDefined = 0;
+  p->UnpackCRC = 0;
+  p->NumUnpackStreams = 0;
+}
+
+void SzFolder_Free(CSzFolder *p, ISzAlloc *alloc)
+{
+  UInt32 i;
+  if (p->Coders)
+    for (i = 0; i < p->NumCoders; i++)
+      SzCoderInfo_Free(&p->Coders[i], alloc);
+  IAlloc_Free(alloc, p->Coders);
+  IAlloc_Free(alloc, p->BindPairs);
+  IAlloc_Free(alloc, p->PackStreams);
+  IAlloc_Free(alloc, p->UnpackSizes);
+  SzFolder_Init(p);
+}
+
+UInt32 SzFolder_GetNumOutStreams(CSzFolder *p)
+{
+  UInt32 result = 0;
+  UInt32 i;
+  for (i = 0; i < p->NumCoders; i++)
+    result += p->Coders[i].NumOutStreams;
+  return result;
+}
+
+int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex)
+{
+  UInt32 i;
+  for (i = 0; i < p->NumBindPairs; i++)
+    if (p->BindPairs[i].InIndex == inStreamIndex)
+      return i;
+  return -1;
+}
+
+
+int SzFolder_FindBindPairForOutStream(CSzFolder *p, UInt32 outStreamIndex)
+{
+  UInt32 i;
+  for (i = 0; i < p->NumBindPairs; i++)
+    if (p->BindPairs[i].OutIndex == outStreamIndex)
+      return i;
+  return -1;
+}
+
+UInt64 SzFolder_GetUnpackSize(CSzFolder *p)
+{
+  int i = (int)SzFolder_GetNumOutStreams(p);
+  if (i == 0)
+    return 0;
+  for (i--; i >= 0; i--)
+    if (SzFolder_FindBindPairForOutStream(p, i) < 0)
+      return p->UnpackSizes[i];
+  /* throw 1; */
+  return 0;
+}
+
+void SzFile_Init(CSzFileItem *p)
+{
+  p->HasStream = 1;
+  p->IsDir = 0;
+  p->IsAnti = 0;
+  p->FileCRCDefined = 0;
+  p->MTimeDefined = 0;
+  p->Name = 0;
+}
+
+static void SzFile_Free(CSzFileItem *p, ISzAlloc *alloc)
+{
+  IAlloc_Free(alloc, p->Name);
+  SzFile_Init(p);
+}
+
+void SzAr_Init(CSzAr *p)
+{
+  p->PackSizes = 0;
+  p->PackCRCsDefined = 0;
+  p->PackCRCs = 0;
+  p->Folders = 0;
+  p->Files = 0;
+  p->NumPackStreams = 0;
+  p->NumFolders = 0;
+  p->NumFiles = 0;
+}
+
+void SzAr_Free(CSzAr *p, ISzAlloc *alloc)
+{
+  UInt32 i;
+  if (p->Folders)
+    for (i = 0; i < p->NumFolders; i++)
+      SzFolder_Free(&p->Folders[i], alloc);
+  if (p->Files)
+    for (i = 0; i < p->NumFiles; i++)
+      SzFile_Free(&p->Files[i], alloc);
+  IAlloc_Free(alloc, p->PackSizes);
+  IAlloc_Free(alloc, p->PackCRCsDefined);
+  IAlloc_Free(alloc, p->PackCRCs);
+  IAlloc_Free(alloc, p->Folders);
+  IAlloc_Free(alloc, p->Files);
+  SzAr_Init(p);
+}
diff --git a/third_party/lzma/v4_65/files/C/Archive/7z/7zItem.h b/third_party/lzma/v4_65/files/C/Archive/7z/7zItem.h
new file mode 100644
index 0000000..9f1366c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Archive/7z/7zItem.h
@@ -0,0 +1,84 @@
+/* 7zItem.h -- 7z Items
+2008-10-04 : Igor Pavlov : Public domain */
+
+#ifndef __7Z_ITEM_H
+#define __7Z_ITEM_H
+
+#include "../../7zBuf.h"
+
+typedef struct
+{
+  UInt32 NumInStreams;
+  UInt32 NumOutStreams;
+  UInt64 MethodID;
+  CBuf Props;
+} CSzCoderInfo;
+
+void SzCoderInfo_Init(CSzCoderInfo *p);
+void SzCoderInfo_Free(CSzCoderInfo *p, ISzAlloc *alloc);
+
+typedef struct
+{
+  UInt32 InIndex;
+  UInt32 OutIndex;
+} CBindPair;
+
+typedef struct
+{
+  CSzCoderInfo *Coders;
+  CBindPair *BindPairs;
+  UInt32 *PackStreams;
+  UInt64 *UnpackSizes;
+  UInt32 NumCoders;
+  UInt32 NumBindPairs;
+  UInt32 NumPackStreams;
+  int UnpackCRCDefined;
+  UInt32 UnpackCRC;
+
+  UInt32 NumUnpackStreams;
+} CSzFolder;
+
+void SzFolder_Init(CSzFolder *p);
+UInt64 SzFolder_GetUnpackSize(CSzFolder *p);
+int SzFolder_FindBindPairForInStream(CSzFolder *p, UInt32 inStreamIndex);
+UInt32 SzFolder_GetNumOutStreams(CSzFolder *p);
+UInt64 SzFolder_GetUnpackSize(CSzFolder *p);
+
+typedef struct
+{
+  UInt32 Low;
+  UInt32 High;
+} CNtfsFileTime;
+
+typedef struct
+{
+  CNtfsFileTime MTime;
+  UInt64 Size;
+  char *Name;
+  UInt32 FileCRC;
+
+  Byte HasStream;
+  Byte IsDir;
+  Byte IsAnti;
+  Byte FileCRCDefined;
+  Byte MTimeDefined;
+} CSzFileItem;
+
+void SzFile_Init(CSzFileItem *p);
+
+typedef struct
+{
+  UInt64 *PackSizes;
+  Byte *PackCRCsDefined;
+  UInt32 *PackCRCs;
+  CSzFolder *Folders;
+  CSzFileItem *Files;
+  UInt32 NumPackStreams;
+  UInt32 NumFolders;
+  UInt32 NumFiles;
+} CSzAr;
+
+void SzAr_Init(CSzAr *p);
+void SzAr_Free(CSzAr *p, ISzAlloc *alloc);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/Archive/7z/7zMain.c b/third_party/lzma/v4_65/files/C/Archive/7z/7zMain.c
new file mode 100644
index 0000000..0c20e8c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Archive/7z/7zMain.c
@@ -0,0 +1,262 @@
+/* 7zMain.c - Test application for 7z Decoder
+2008-11-23 : Igor Pavlov : Public domain */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../../7zCrc.h"
+#include "../../7zFile.h"
+#include "../../7zVersion.h"
+
+#include "7zAlloc.h"
+#include "7zExtract.h"
+#include "7zIn.h"
+
+static void ConvertNumberToString(UInt64 value, char *s)
+{
+  char temp[32];
+  int pos = 0;
+  do
+  {
+    temp[pos++] = (char)('0' + (int)(value % 10));
+    value /= 10;
+  }
+  while (value != 0);
+  do
+    *s++ = temp[--pos];
+  while (pos > 0);
+  *s = '\0';
+}
+
+#define PERIOD_4 (4 * 365 + 1)
+#define PERIOD_100 (PERIOD_4 * 25 - 1)
+#define PERIOD_400 (PERIOD_100 * 4 + 1)
+
+static void ConvertFileTimeToString(CNtfsFileTime *ft, char *s)
+{
+  unsigned year, mon, day, hour, min, sec;
+  UInt64 v64 = ft->Low | ((UInt64)ft->High << 32);
+  Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+  unsigned temp;
+  UInt32 v;
+  v64 /= 10000000;
+  sec = (unsigned)(v64 % 60);
+  v64 /= 60;
+  min = (unsigned)(v64 % 60);
+  v64 /= 60;
+  hour = (unsigned)(v64 % 24);
+  v64 /= 24;
+
+  v = (UInt32)v64;
+
+  year = (unsigned)(1601 + v / PERIOD_400 * 400);
+  v %= PERIOD_400;
+
+  temp = (unsigned)(v / PERIOD_100);
+  if (temp == 4)
+    temp = 3;
+  year += temp * 100;
+  v -= temp * PERIOD_100;
+
+  temp = v / PERIOD_4;
+  if (temp == 25)
+    temp = 24;
+  year += temp * 4;
+  v -= temp * PERIOD_4;
+
+  temp = v / 365;
+  if (temp == 4)
+    temp = 3;
+  year += temp;
+  v -= temp * 365;
+
+  if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
+    ms[1] = 29;
+  for (mon = 1; mon <= 12; mon++)
+  {
+    unsigned s = ms[mon - 1];
+    if (v < s)
+      break;
+    v -= s;
+  }
+  day = (unsigned)v + 1;
+  sprintf(s, "%04d-%02d-%02d %02d:%02d:%02d", year, mon, day, hour, min, sec);
+}
+
+void PrintError(char *sz)
+{
+  printf("\nERROR: %s\n", sz);
+}
+
+int MY_CDECL main(int numargs, char *args[])
+{
+  CFileInStream archiveStream;
+  CLookToRead lookStream;
+  CSzArEx db;
+  SRes res;
+  ISzAlloc allocImp;
+  ISzAlloc allocTempImp;
+
+  printf("\n7z ANSI-C Decoder " MY_VERSION_COPYRIGHT_DATE "\n");
+  if (numargs == 1)
+  {
+    printf(
+      "\nUsage: 7zDec <command> <archive_name>\n\n"
+      "<Commands>\n"
+      "  e: Extract files from archive\n"
+      "  l: List contents of archive\n"
+      "  t: Test integrity of archive\n");
+    return 0;
+  }
+  if (numargs < 3)
+  {
+    PrintError("incorrect command");
+    return 1;
+  }
+
+  if (InFile_Open(&archiveStream.file, args[2]))
+  {
+    PrintError("can not open input file");
+    return 1;
+  }
+
+  
+  FileInStream_CreateVTable(&archiveStream);
+  LookToRead_CreateVTable(&lookStream, False);
+  
+  lookStream.realStream = &archiveStream.s;
+  LookToRead_Init(&lookStream);
+
+  allocImp.Alloc = SzAlloc;
+  allocImp.Free = SzFree;
+
+  allocTempImp.Alloc = SzAllocTemp;
+  allocTempImp.Free = SzFreeTemp;
+
+  CrcGenerateTable();
+
+  SzArEx_Init(&db);
+  res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp);
+  if (res == SZ_OK)
+  {
+    char *command = args[1];
+    int listCommand = 0, testCommand = 0, extractCommand = 0;
+    if (strcmp(command, "l") == 0) listCommand = 1;
+    else if (strcmp(command, "t") == 0) testCommand = 1;
+    else if (strcmp(command, "e") == 0) extractCommand = 1;
+
+    if (listCommand)
+    {
+      UInt32 i;
+      for (i = 0; i < db.db.NumFiles; i++)
+      {
+        CSzFileItem *f = db.db.Files + i;
+        char s[32], t[32];
+        ConvertNumberToString(f->Size, s);
+        if (f->MTimeDefined)
+          ConvertFileTimeToString(&f->MTime, t);
+        else
+          strcpy(t, "                   ");
+
+        printf("%s %10s  %s\n", t, s, f->Name);
+      }
+    }
+    else if (testCommand || extractCommand)
+    {
+      UInt32 i;
+
+      /*
+      if you need cache, use these 3 variables.
+      if you use external function, you can make these variable as static.
+      */
+      UInt32 blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */
+      Byte *outBuffer = 0; /* it must be 0 before first call for each new archive. */
+      size_t outBufferSize = 0;  /* it can have any value before first call (if outBuffer = 0) */
+
+      printf("\n");
+      for (i = 0; i < db.db.NumFiles; i++)
+      {
+        size_t offset;
+        size_t outSizeProcessed;
+        CSzFileItem *f = db.db.Files + i;
+        if (f->IsDir)
+          printf("Directory ");
+        else
+          printf(testCommand ?
+            "Testing   ":
+            "Extracting");
+        printf(" %s", f->Name);
+        if (f->IsDir)
+        {
+          printf("\n");
+          continue;
+        }
+        res = SzAr_Extract(&db, &lookStream.s, i,
+            &blockIndex, &outBuffer, &outBufferSize,
+            &offset, &outSizeProcessed,
+            &allocImp, &allocTempImp);
+        if (res != SZ_OK)
+          break;
+        if (!testCommand)
+        {
+          CSzFile outFile;
+          size_t processedSize;
+          char *fileName = f->Name;
+          size_t nameLen = strlen(f->Name);
+          for (; nameLen > 0; nameLen--)
+            if (f->Name[nameLen - 1] == '/')
+            {
+              fileName = f->Name + nameLen;
+              break;
+            }
+            
+          if (OutFile_Open(&outFile, fileName))
+          {
+            PrintError("can not open output file");
+            res = SZ_ERROR_FAIL;
+            break;
+          }
+          processedSize = outSizeProcessed;
+          if (File_Write(&outFile, outBuffer + offset, &processedSize) != 0 ||
+              processedSize != outSizeProcessed)
+          {
+            PrintError("can not write output file");
+            res = SZ_ERROR_FAIL;
+            break;
+          }
+          if (File_Close(&outFile))
+          {
+            PrintError("can not close output file");
+            res = SZ_ERROR_FAIL;
+            break;
+          }
+        }
+        printf("\n");
+      }
+      IAlloc_Free(&allocImp, outBuffer);
+    }
+    else
+    {
+      PrintError("incorrect command");
+      res = SZ_ERROR_FAIL;
+    }
+  }
+  SzArEx_Free(&db, &allocImp);
+
+  File_Close(&archiveStream.file);
+  if (res == SZ_OK)
+  {
+    printf("\nEverything is Ok\n");
+    return 0;
+  }
+  if (res == SZ_ERROR_UNSUPPORTED)
+    PrintError("decoder doesn't support this archive");
+  else if (res == SZ_ERROR_MEM)
+    PrintError("can not allocate memory");
+  else if (res == SZ_ERROR_CRC)
+    PrintError("CRC error");
+  else
+    printf("\nERROR #%d\n", res);
+  return 1;
+}
diff --git a/third_party/lzma/v4_65/files/C/Archive/7z/makefile b/third_party/lzma/v4_65/files/C/Archive/7z/makefile
new file mode 100644
index 0000000..c7bb05b
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Archive/7z/makefile
@@ -0,0 +1,33 @@
+MY_STATIC_LINK=1
+
+PROG = 7zDec.exe
+
+C_OBJS = \
+  $O\7zBuf.obj \
+  $O\7zBuf2.obj \
+  $O\7zCrc.obj \
+  $O\LzmaDec.obj \
+  $O\Bra86.obj \
+  $O\Bcj2.obj \
+  $O\7zFile.obj \
+  $O\7zStream.obj \
+
+7Z_OBJS = \
+  $O\7zAlloc.obj \
+  $O\7zDecode.obj \
+  $O\7zExtract.obj \
+  $O\7zHeader.obj \
+  $O\7zIn.obj \
+  $O\7zItem.obj \
+  $O\7zMain.obj \
+
+OBJS = \
+  $(7Z_OBJS) \
+  $(C_OBJS) \
+
+!include "../../../CPP/Build.mak"
+
+$(7Z_OBJS): $(*B).c
+	$(COMPL_O1)
+$(C_OBJS): ../../$(*B).c
+	$(COMPL_O2)
diff --git a/third_party/lzma/v4_65/files/C/Archive/7z/makefile.gcc b/third_party/lzma/v4_65/files/C/Archive/7z/makefile.gcc
new file mode 100644
index 0000000..2203dfc
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Archive/7z/makefile.gcc
@@ -0,0 +1,61 @@
+PROG = 7zDec
+CXX = g++
+LIB =
+RM = rm -f
+CFLAGS = -c -O2 -Wall
+
+OBJS = 7zAlloc.o 7zBuf.o 7zBuf2.o 7zCrc.o 7zDecode.o 7zExtract.o 7zHeader.o 7zIn.o 7zItem.o 7zMain.o LzmaDec.o Bra86.o Bcj2.o 7zFile.o 7zStream.o
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+	$(CXX) -o $(PROG) $(LDFLAGS) $(OBJS) $(LIB)
+
+7zAlloc.o: 7zAlloc.c
+	$(CXX) $(CFLAGS) 7zAlloc.c
+
+7zBuf.o: ../../7zBuf.c
+	$(CXX) $(CFLAGS) ../../7zBuf.c
+
+7zBuf2.o: ../../7zBuf2.c
+	$(CXX) $(CFLAGS) ../../7zBuf2.c
+
+7zCrc.o: ../../7zCrc.c
+	$(CXX) $(CFLAGS) ../../7zCrc.c
+
+7zDecode.o: 7zDecode.c
+	$(CXX) $(CFLAGS) 7zDecode.c
+
+7zExtract.o: 7zExtract.c
+	$(CXX) $(CFLAGS) 7zExtract.c
+
+7zHeader.o: 7zHeader.c
+	$(CXX) $(CFLAGS) 7zHeader.c
+
+7zIn.o: 7zIn.c
+	$(CXX) $(CFLAGS) 7zIn.c
+
+7zItem.o: 7zItem.c
+	$(CXX) $(CFLAGS) 7zItem.c
+
+7zMain.o: 7zMain.c
+	$(CXX) $(CFLAGS) 7zMain.c
+
+LzmaDec.o: ../../LzmaDec.c
+	$(CXX) $(CFLAGS) ../../LzmaDec.c
+
+Bra86.o: ../../Bra86.c
+	$(CXX) $(CFLAGS) ../../Bra86.c
+
+Bcj2.o: ../../Bcj2.c
+	$(CXX) $(CFLAGS) ../../Bcj2.c
+
+7zFile.o: ../../7zFile.c
+	$(CXX) $(CFLAGS) ../../7zFile.c
+
+7zStream.o: ../../7zStream.c
+	$(CXX) $(CFLAGS) ../../7zStream.c
+
+clean:
+	-$(RM) $(PROG) $(OBJS)
+
diff --git a/third_party/lzma/v4_65/files/C/Bcj2.c b/third_party/lzma/v4_65/files/C/Bcj2.c
new file mode 100644
index 0000000..20199ce
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Bcj2.c
@@ -0,0 +1,132 @@
+/* Bcj2.c -- Converter for x86 code (BCJ2)
+2008-10-04 : Igor Pavlov : Public domain */
+
+#include "Bcj2.h"
+
+#ifdef _LZMA_PROB32
+#define CProb UInt32
+#else
+#define CProb UInt16
+#endif
+
+#define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80)
+#define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1))
+
+#define kNumTopBits 24
+#define kTopValue ((UInt32)1 << kNumTopBits)
+
+#define kNumBitModelTotalBits 11
+#define kBitModelTotal (1 << kNumBitModelTotalBits)
+#define kNumMoveBits 5
+
+#define RC_READ_BYTE (*buffer++)
+#define RC_TEST { if (buffer == bufferLim) return SZ_ERROR_DATA; }
+#define RC_INIT2 code = 0; range = 0xFFFFFFFF; \
+  { int i; for (i = 0; i < 5; i++) { RC_TEST; code = (code << 8) | RC_READ_BYTE; }}
+
+#define NORMALIZE if (range < kTopValue) { RC_TEST; range <<= 8; code = (code << 8) | RC_READ_BYTE; }
+
+#define IF_BIT_0(p) ttt = *(p); bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
+#define UPDATE_0(p) range = bound; *(p) = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); NORMALIZE;
+#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CProb)(ttt - (ttt >> kNumMoveBits)); NORMALIZE;
+
+int Bcj2_Decode(
+    const Byte *buf0, SizeT size0,
+    const Byte *buf1, SizeT size1,
+    const Byte *buf2, SizeT size2,
+    const Byte *buf3, SizeT size3,
+    Byte *outBuf, SizeT outSize)
+{
+  CProb p[256 + 2];
+  SizeT inPos = 0, outPos = 0;
+
+  const Byte *buffer, *bufferLim;
+  UInt32 range, code;
+  Byte prevByte = 0;
+
+  unsigned int i;
+  for (i = 0; i < sizeof(p) / sizeof(p[0]); i++)
+    p[i] = kBitModelTotal >> 1;
+
+  buffer = buf3;
+  bufferLim = buffer + size3;
+  RC_INIT2
+
+  if (outSize == 0)
+    return SZ_OK;
+
+  for (;;)
+  {
+    Byte b;
+    CProb *prob;
+    UInt32 bound;
+    UInt32 ttt;
+
+    SizeT limit = size0 - inPos;
+    if (outSize - outPos < limit)
+      limit = outSize - outPos;
+    while (limit != 0)
+    {
+      Byte b = buf0[inPos];
+      outBuf[outPos++] = b;
+      if (IsJ(prevByte, b))
+        break;
+      inPos++;
+      prevByte = b;
+      limit--;
+    }
+
+    if (limit == 0 || outPos == outSize)
+      break;
+
+    b = buf0[inPos++];
+
+    if (b == 0xE8)
+      prob = p + prevByte;
+    else if (b == 0xE9)
+      prob = p + 256;
+    else
+      prob = p + 257;
+
+    IF_BIT_0(prob)
+    {
+      UPDATE_0(prob)
+      prevByte = b;
+    }
+    else
+    {
+      UInt32 dest;
+      const Byte *v;
+      UPDATE_1(prob)
+      if (b == 0xE8)
+      {
+        v = buf1;
+        if (size1 < 4)
+          return SZ_ERROR_DATA;
+        buf1 += 4;
+        size1 -= 4;
+      }
+      else
+      {
+        v = buf2;
+        if (size2 < 4)
+          return SZ_ERROR_DATA;
+        buf2 += 4;
+        size2 -= 4;
+      }
+      dest = (((UInt32)v[0] << 24) | ((UInt32)v[1] << 16) |
+          ((UInt32)v[2] << 8) | ((UInt32)v[3])) - ((UInt32)outPos + 4);
+      outBuf[outPos++] = (Byte)dest;
+      if (outPos == outSize)
+        break;
+      outBuf[outPos++] = (Byte)(dest >> 8);
+      if (outPos == outSize)
+        break;
+      outBuf[outPos++] = (Byte)(dest >> 16);
+      if (outPos == outSize)
+        break;
+      outBuf[outPos++] = prevByte = (Byte)(dest >> 24);
+    }
+  }
+  return (outPos == outSize) ? SZ_OK : SZ_ERROR_DATA;
+}
diff --git a/third_party/lzma/v4_65/files/C/Bcj2.h b/third_party/lzma/v4_65/files/C/Bcj2.h
new file mode 100644
index 0000000..32d450b
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Bcj2.h
@@ -0,0 +1,30 @@
+/* Bcj2.h -- Converter for x86 code (BCJ2)
+2008-10-04 : Igor Pavlov : Public domain */
+
+#ifndef __BCJ2_H
+#define __BCJ2_H
+
+#include "Types.h"
+
+/*
+Conditions:
+  outSize <= FullOutputSize,
+  where FullOutputSize is full size of output stream of x86_2 filter.
+
+If buf0 overlaps outBuf, there are two required conditions:
+  1) (buf0 >= outBuf)
+  2) (buf0 + size0 >= outBuf + FullOutputSize).
+
+Returns:
+  SZ_OK
+  SZ_ERROR_DATA - Data error
+*/
+
+int Bcj2_Decode(
+    const Byte *buf0, SizeT size0,
+    const Byte *buf1, SizeT size1,
+    const Byte *buf2, SizeT size2,
+    const Byte *buf3, SizeT size3,
+    Byte *outBuf, SizeT outSize);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/Bra.c b/third_party/lzma/v4_65/files/C/Bra.c
new file mode 100644
index 0000000..5e54695
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Bra.c
@@ -0,0 +1,133 @@
+/* Bra.c -- Converters for RISC code
+2008-10-04 : Igor Pavlov : Public domain */
+
+#include "Bra.h"
+
+SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding)
+{
+  SizeT i;
+  if (size < 4)
+    return 0;
+  size -= 4;
+  ip += 8;
+  for (i = 0; i <= size; i += 4)
+  {
+    if (data[i + 3] == 0xEB)
+    {
+      UInt32 dest;
+      UInt32 src = ((UInt32)data[i + 2] << 16) | ((UInt32)data[i + 1] << 8) | (data[i + 0]);
+      src <<= 2;
+      if (encoding)
+        dest = ip + (UInt32)i + src;
+      else
+        dest = src - (ip + (UInt32)i);
+      dest >>= 2;
+      data[i + 2] = (Byte)(dest >> 16);
+      data[i + 1] = (Byte)(dest >> 8);
+      data[i + 0] = (Byte)dest;
+    }
+  }
+  return i;
+}
+
+SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding)
+{
+  SizeT i;
+  if (size < 4)
+    return 0;
+  size -= 4;
+  ip += 4;
+  for (i = 0; i <= size; i += 2)
+  {
+    if ((data[i + 1] & 0xF8) == 0xF0 &&
+        (data[i + 3] & 0xF8) == 0xF8)
+    {
+      UInt32 dest;
+      UInt32 src =
+        (((UInt32)data[i + 1] & 0x7) << 19) |
+        ((UInt32)data[i + 0] << 11) |
+        (((UInt32)data[i + 3] & 0x7) << 8) |
+        (data[i + 2]);
+      
+      src <<= 1;
+      if (encoding)
+        dest = ip + (UInt32)i + src;
+      else
+        dest = src - (ip + (UInt32)i);
+      dest >>= 1;
+      
+      data[i + 1] = (Byte)(0xF0 | ((dest >> 19) & 0x7));
+      data[i + 0] = (Byte)(dest >> 11);
+      data[i + 3] = (Byte)(0xF8 | ((dest >> 8) & 0x7));
+      data[i + 2] = (Byte)dest;
+      i += 2;
+    }
+  }
+  return i;
+}
+
+SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding)
+{
+  SizeT i;
+  if (size < 4)
+    return 0;
+  size -= 4;
+  for (i = 0; i <= size; i += 4)
+  {
+    if ((data[i] >> 2) == 0x12 && (data[i + 3] & 3) == 1)
+    {
+      UInt32 src = ((UInt32)(data[i + 0] & 3) << 24) |
+        ((UInt32)data[i + 1] << 16) |
+        ((UInt32)data[i + 2] << 8) |
+        ((UInt32)data[i + 3] & (~3));
+      
+      UInt32 dest;
+      if (encoding)
+        dest = ip + (UInt32)i + src;
+      else
+        dest = src - (ip + (UInt32)i);
+      data[i + 0] = (Byte)(0x48 | ((dest >> 24) &  0x3));
+      data[i + 1] = (Byte)(dest >> 16);
+      data[i + 2] = (Byte)(dest >> 8);
+      data[i + 3] &= 0x3;
+      data[i + 3] |= dest;
+    }
+  }
+  return i;
+}
+
+SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding)
+{
+  UInt32 i;
+  if (size < 4)
+    return 0;
+  size -= 4;
+  for (i = 0; i <= size; i += 4)
+  {
+    if (data[i] == 0x40 && (data[i + 1] & 0xC0) == 0x00 ||
+        data[i] == 0x7F && (data[i + 1] & 0xC0) == 0xC0)
+    {
+      UInt32 src =
+        ((UInt32)data[i + 0] << 24) |
+        ((UInt32)data[i + 1] << 16) |
+        ((UInt32)data[i + 2] << 8) |
+        ((UInt32)data[i + 3]);
+      UInt32 dest;
+      
+      src <<= 2;
+      if (encoding)
+        dest = ip + i + src;
+      else
+        dest = src - (ip + i);
+      dest >>= 2;
+      
+      dest = (((0 - ((dest >> 22) & 1)) << 22) & 0x3FFFFFFF) | (dest & 0x3FFFFF) | 0x40000000;
+
+      data[i + 0] = (Byte)(dest >> 24);
+      data[i + 1] = (Byte)(dest >> 16);
+      data[i + 2] = (Byte)(dest >> 8);
+      data[i + 3] = (Byte)dest;
+    }
+  }
+  return i;
+}
diff --git a/third_party/lzma/v4_65/files/C/Bra.h b/third_party/lzma/v4_65/files/C/Bra.h
new file mode 100644
index 0000000..45e231e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Bra.h
@@ -0,0 +1,60 @@
+/* Bra.h -- Branch converters for executables
+2008-10-04 : Igor Pavlov : Public domain */
+
+#ifndef __BRA_H
+#define __BRA_H
+
+#include "Types.h"
+
+/*
+These functions convert relative addresses to absolute addresses
+in CALL instructions to increase the compression ratio.
+  
+  In:
+    data     - data buffer
+    size     - size of data
+    ip       - current virtual Instruction Pinter (IP) value
+    state    - state variable for x86 converter
+    encoding - 0 (for decoding), 1 (for encoding)
+  
+  Out:
+    state    - state variable for x86 converter
+
+  Returns:
+    The number of processed bytes. If you call these functions with multiple calls,
+    you must start next call with first byte after block of processed bytes.
+  
+  Type   Endian  Alignment  LookAhead
+  
+  x86    little      1          4
+  ARMT   little      2          2
+  ARM    little      4          0
+  PPC     big        4          0
+  SPARC   big        4          0
+  IA64   little     16          0
+
+  size must be >= Alignment + LookAhead, if it's not last block.
+  If (size < Alignment + LookAhead), converter returns 0.
+
+  Example:
+
+    UInt32 ip = 0;
+    for ()
+    {
+      ; size must be >= Alignment + LookAhead, if it's not last block
+      SizeT processed = Convert(data, size, ip, 1);
+      data += processed;
+      size -= processed;
+      ip += processed;
+    }
+*/
+
+#define x86_Convert_Init(state) { state = 0; }
+SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding);
+SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding);
+SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding);
+SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding);
+SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding);
+SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/Bra86.c b/third_party/lzma/v4_65/files/C/Bra86.c
new file mode 100644
index 0000000..1ee0e70
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Bra86.c
@@ -0,0 +1,85 @@
+/* Bra86.c -- Converter for x86 code (BCJ)
+2008-10-04 : Igor Pavlov : Public domain */
+
+#include "Bra.h"
+
+#define Test86MSByte(b) ((b) == 0 || (b) == 0xFF)
+
+const Byte kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0};
+const Byte kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3};
+
+SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding)
+{
+  SizeT bufferPos = 0, prevPosT;
+  UInt32 prevMask = *state & 0x7;
+  if (size < 5)
+    return 0;
+  ip += 5;
+  prevPosT = (SizeT)0 - 1;
+
+  for (;;)
+  {
+    Byte *p = data + bufferPos;
+    Byte *limit = data + size - 4;
+    for (; p < limit; p++)
+      if ((*p & 0xFE) == 0xE8)
+        break;
+    bufferPos = (SizeT)(p - data);
+    if (p >= limit)
+      break;
+    prevPosT = bufferPos - prevPosT;
+    if (prevPosT > 3)
+      prevMask = 0;
+    else
+    {
+      prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7;
+      if (prevMask != 0)
+      {
+        Byte b = p[4 - kMaskToBitNumber[prevMask]];
+        if (!kMaskToAllowedStatus[prevMask] || Test86MSByte(b))
+        {
+          prevPosT = bufferPos;
+          prevMask = ((prevMask << 1) & 0x7) | 1;
+          bufferPos++;
+          continue;
+        }
+      }
+    }
+    prevPosT = bufferPos;
+
+    if (Test86MSByte(p[4]))
+    {
+      UInt32 src = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]);
+      UInt32 dest;
+      for (;;)
+      {
+        Byte b;
+        int index;
+        if (encoding)
+          dest = (ip + (UInt32)bufferPos) + src;
+        else
+          dest = src - (ip + (UInt32)bufferPos);
+        if (prevMask == 0)
+          break;
+        index = kMaskToBitNumber[prevMask] * 8;
+        b = (Byte)(dest >> (24 - index));
+        if (!Test86MSByte(b))
+          break;
+        src = dest ^ ((1 << (32 - index)) - 1);
+      }
+      p[4] = (Byte)(~(((dest >> 24) & 1) - 1));
+      p[3] = (Byte)(dest >> 16);
+      p[2] = (Byte)(dest >> 8);
+      p[1] = (Byte)dest;
+      bufferPos += 5;
+    }
+    else
+    {
+      prevMask = ((prevMask << 1) & 0x7) | 1;
+      bufferPos++;
+    }
+  }
+  prevPosT = bufferPos - prevPosT;
+  *state = ((prevPosT > 3) ? 0 : ((prevMask << ((int)prevPosT - 1)) & 0x7));
+  return bufferPos;
+}
diff --git a/third_party/lzma/v4_65/files/C/BraIA64.c b/third_party/lzma/v4_65/files/C/BraIA64.c
new file mode 100644
index 0000000..0b4ee85
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/BraIA64.c
@@ -0,0 +1,67 @@
+/* BraIA64.c -- Converter for IA-64 code
+2008-10-04 : Igor Pavlov : Public domain */
+
+#include "Bra.h"
+
+static const Byte kBranchTable[32] =
+{
+  0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0,
+  4, 4, 6, 6, 0, 0, 7, 7,
+  4, 4, 0, 0, 4, 4, 0, 0
+};
+
+SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding)
+{
+  SizeT i;
+  if (size < 16)
+    return 0;
+  size -= 16;
+  for (i = 0; i <= size; i += 16)
+  {
+    UInt32 instrTemplate = data[i] & 0x1F;
+    UInt32 mask = kBranchTable[instrTemplate];
+    UInt32 bitPos = 5;
+    int slot;
+    for (slot = 0; slot < 3; slot++, bitPos += 41)
+    {
+      UInt32 bytePos, bitRes;
+      UInt64 instruction, instNorm;
+      int j;
+      if (((mask >> slot) & 1) == 0)
+        continue;
+      bytePos = (bitPos >> 3);
+      bitRes = bitPos & 0x7;
+      instruction = 0;
+      for (j = 0; j < 6; j++)
+        instruction += (UInt64)data[i + j + bytePos] << (8 * j);
+
+      instNorm = instruction >> bitRes;
+      if (((instNorm >> 37) & 0xF) == 0x5 && ((instNorm >> 9) & 0x7) == 0)
+      {
+        UInt32 src = (UInt32)((instNorm >> 13) & 0xFFFFF);
+        UInt32 dest;
+        src |= ((UInt32)(instNorm >> 36) & 1) << 20;
+        
+        src <<= 4;
+        
+        if (encoding)
+          dest = ip + (UInt32)i + src;
+        else
+          dest = src - (ip + (UInt32)i);
+        
+        dest >>= 4;
+        
+        instNorm &= ~((UInt64)(0x8FFFFF) << 13);
+        instNorm |= ((UInt64)(dest & 0xFFFFF) << 13);
+        instNorm |= ((UInt64)(dest & 0x100000) << (36 - 20));
+        
+        instruction &= (1 << bitRes) - 1;
+        instruction |= (instNorm << bitRes);
+        for (j = 0; j < 6; j++)
+          data[i + j + bytePos] = (Byte)(instruction >> (8 * j));
+      }
+    }
+  }
+  return i;
+}
diff --git a/third_party/lzma/v4_65/files/C/CpuArch.h b/third_party/lzma/v4_65/files/C/CpuArch.h
new file mode 100644
index 0000000..7384b0c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/CpuArch.h
@@ -0,0 +1,69 @@
+/* CpuArch.h
+2008-08-05
+Igor Pavlov
+Public domain */
+
+#ifndef __CPUARCH_H
+#define __CPUARCH_H
+
+/*
+LITTLE_ENDIAN_UNALIGN means:
+  1) CPU is LITTLE_ENDIAN
+  2) it's allowed to make unaligned memory accesses
+if LITTLE_ENDIAN_UNALIGN is not defined, it means that we don't know
+about these properties of platform.
+*/
+
+#if defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) || defined(__i386__) || defined(__x86_64__)
+#define LITTLE_ENDIAN_UNALIGN
+#endif
+
+#ifdef LITTLE_ENDIAN_UNALIGN
+
+#define GetUi16(p) (*(const UInt16 *)(p))
+#define GetUi32(p) (*(const UInt32 *)(p))
+#define GetUi64(p) (*(const UInt64 *)(p))
+#define SetUi32(p, d) *(UInt32 *)(p) = (d);
+
+#else
+
+#define GetUi16(p) (((const Byte *)(p))[0] | ((UInt16)((const Byte *)(p))[1] << 8))
+
+#define GetUi32(p) ( \
+             ((const Byte *)(p))[0]        | \
+    ((UInt32)((const Byte *)(p))[1] <<  8) | \
+    ((UInt32)((const Byte *)(p))[2] << 16) | \
+    ((UInt32)((const Byte *)(p))[3] << 24))
+
+#define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32))
+
+#define SetUi32(p, d) { UInt32 _x_ = (d); \
+    ((Byte *)(p))[0] = (Byte)_x_; \
+    ((Byte *)(p))[1] = (Byte)(_x_ >> 8); \
+    ((Byte *)(p))[2] = (Byte)(_x_ >> 16); \
+    ((Byte *)(p))[3] = (Byte)(_x_ >> 24); }
+
+#endif
+
+#if defined(LITTLE_ENDIAN_UNALIGN) && defined(_WIN64) && (_MSC_VER >= 1300)
+
+#pragma intrinsic(_byteswap_ulong)
+#pragma intrinsic(_byteswap_uint64)
+#define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p))
+#define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p))
+
+#else
+
+#define GetBe32(p) ( \
+    ((UInt32)((const Byte *)(p))[0] << 24) | \
+    ((UInt32)((const Byte *)(p))[1] << 16) | \
+    ((UInt32)((const Byte *)(p))[2] <<  8) | \
+             ((const Byte *)(p))[3] )
+
+#define GetBe64(p) (((UInt64)GetBe32(p) << 32) | GetBe32(((const Byte *)(p)) + 4))
+
+#endif
+
+#define GetBe16(p) (((UInt16)((const Byte *)(p))[0] << 8) | ((const Byte *)(p))[1])
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/LzFind.c b/third_party/lzma/v4_65/files/C/LzFind.c
new file mode 100644
index 0000000..34f4f09
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzFind.c
@@ -0,0 +1,751 @@
+/* LzFind.c -- Match finder for LZ algorithms
+2008-10-04 : Igor Pavlov : Public domain */
+
+#include <string.h>
+
+#include "LzFind.h"
+#include "LzHash.h"
+
+#define kEmptyHashValue 0
+#define kMaxValForNormalize ((UInt32)0xFFFFFFFF)
+#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */
+#define kNormalizeMask (~(kNormalizeStepMin - 1))
+#define kMaxHistorySize ((UInt32)3 << 30)
+
+#define kStartMaxLen 3
+
+static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc)
+{
+  if (!p->directInput)
+  {
+    alloc->Free(alloc, p->bufferBase);
+    p->bufferBase = 0;
+  }
+}
+
+/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */
+
+static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc)
+{
+  UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv;
+  if (p->directInput)
+  {
+    p->blockSize = blockSize;
+    return 1;
+  }
+  if (p->bufferBase == 0 || p->blockSize != blockSize)
+  {
+    LzInWindow_Free(p, alloc);
+    p->blockSize = blockSize;
+    p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize);
+  }
+  return (p->bufferBase != 0);
+}
+
+Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; }
+Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; }
+
+UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; }
+
+void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue)
+{
+  p->posLimit -= subValue;
+  p->pos -= subValue;
+  p->streamPos -= subValue;
+}
+
+static void MatchFinder_ReadBlock(CMatchFinder *p)
+{
+  if (p->streamEndWasReached || p->result != SZ_OK)
+    return;
+  for (;;)
+  {
+    Byte *dest = p->buffer + (p->streamPos - p->pos);
+    size_t size = (p->bufferBase + p->blockSize - dest);
+    if (size == 0)
+      return;
+    p->result = p->stream->Read(p->stream, dest, &size);
+    if (p->result != SZ_OK)
+      return;
+    if (size == 0)
+    {
+      p->streamEndWasReached = 1;
+      return;
+    }
+    p->streamPos += (UInt32)size;
+    if (p->streamPos - p->pos > p->keepSizeAfter)
+      return;
+  }
+}
+
+void MatchFinder_MoveBlock(CMatchFinder *p)
+{
+  memmove(p->bufferBase,
+    p->buffer - p->keepSizeBefore,
+    (size_t)(p->streamPos - p->pos + p->keepSizeBefore));
+  p->buffer = p->bufferBase + p->keepSizeBefore;
+}
+
+int MatchFinder_NeedMove(CMatchFinder *p)
+{
+  /* if (p->streamEndWasReached) return 0; */
+  return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter);
+}
+
+void MatchFinder_ReadIfRequired(CMatchFinder *p)
+{
+  if (p->streamEndWasReached)
+    return;
+  if (p->keepSizeAfter >= p->streamPos - p->pos)
+    MatchFinder_ReadBlock(p);
+}
+
+static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p)
+{
+  if (MatchFinder_NeedMove(p))
+    MatchFinder_MoveBlock(p);
+  MatchFinder_ReadBlock(p);
+}
+
+static void MatchFinder_SetDefaultSettings(CMatchFinder *p)
+{
+  p->cutValue = 32;
+  p->btMode = 1;
+  p->numHashBytes = 4;
+  /* p->skipModeBits = 0; */
+  p->directInput = 0;
+  p->bigHash = 0;
+}
+
+#define kCrcPoly 0xEDB88320
+
+void MatchFinder_Construct(CMatchFinder *p)
+{
+  UInt32 i;
+  p->bufferBase = 0;
+  p->directInput = 0;
+  p->hash = 0;
+  MatchFinder_SetDefaultSettings(p);
+
+  for (i = 0; i < 256; i++)
+  {
+    UInt32 r = i;
+    int j;
+    for (j = 0; j < 8; j++)
+      r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1));
+    p->crc[i] = r;
+  }
+}
+
+static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc)
+{
+  alloc->Free(alloc, p->hash);
+  p->hash = 0;
+}
+
+void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc)
+{
+  MatchFinder_FreeThisClassMemory(p, alloc);
+  LzInWindow_Free(p, alloc);
+}
+
+static CLzRef* AllocRefs(UInt32 num, ISzAlloc *alloc)
+{
+  size_t sizeInBytes = (size_t)num * sizeof(CLzRef);
+  if (sizeInBytes / sizeof(CLzRef) != num)
+    return 0;
+  return (CLzRef *)alloc->Alloc(alloc, sizeInBytes);
+}
+
+int MatchFinder_Create(CMatchFinder *p, UInt32 historySize,
+    UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter,
+    ISzAlloc *alloc)
+{
+  UInt32 sizeReserv;
+  if (historySize > kMaxHistorySize)
+  {
+    MatchFinder_Free(p, alloc);
+    return 0;
+  }
+  sizeReserv = historySize >> 1;
+  if (historySize > ((UInt32)2 << 30))
+    sizeReserv = historySize >> 2;
+  sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19);
+
+  p->keepSizeBefore = historySize + keepAddBufferBefore + 1;
+  p->keepSizeAfter = matchMaxLen + keepAddBufferAfter;
+  /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */
+  if (LzInWindow_Create(p, sizeReserv, alloc))
+  {
+    UInt32 newCyclicBufferSize = (historySize /* >> p->skipModeBits */) + 1;
+    UInt32 hs;
+    p->matchMaxLen = matchMaxLen;
+    {
+      p->fixedHashSize = 0;
+      if (p->numHashBytes == 2)
+        hs = (1 << 16) - 1;
+      else
+      {
+        hs = historySize - 1;
+        hs |= (hs >> 1);
+        hs |= (hs >> 2);
+        hs |= (hs >> 4);
+        hs |= (hs >> 8);
+        hs >>= 1;
+        /* hs >>= p->skipModeBits; */
+        hs |= 0xFFFF; /* don't change it! It's required for Deflate */
+        if (hs > (1 << 24))
+        {
+          if (p->numHashBytes == 3)
+            hs = (1 << 24) - 1;
+          else
+            hs >>= 1;
+        }
+      }
+      p->hashMask = hs;
+      hs++;
+      if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size;
+      if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size;
+      if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size;
+      hs += p->fixedHashSize;
+    }
+
+    {
+      UInt32 prevSize = p->hashSizeSum + p->numSons;
+      UInt32 newSize;
+      p->historySize = historySize;
+      p->hashSizeSum = hs;
+      p->cyclicBufferSize = newCyclicBufferSize;
+      p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize);
+      newSize = p->hashSizeSum + p->numSons;
+      if (p->hash != 0 && prevSize == newSize)
+        return 1;
+      MatchFinder_FreeThisClassMemory(p, alloc);
+      p->hash = AllocRefs(newSize, alloc);
+      if (p->hash != 0)
+      {
+        p->son = p->hash + p->hashSizeSum;
+        return 1;
+      }
+    }
+  }
+  MatchFinder_Free(p, alloc);
+  return 0;
+}
+
+static void MatchFinder_SetLimits(CMatchFinder *p)
+{
+  UInt32 limit = kMaxValForNormalize - p->pos;
+  UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos;
+  if (limit2 < limit)
+    limit = limit2;
+  limit2 = p->streamPos - p->pos;
+  if (limit2 <= p->keepSizeAfter)
+  {
+    if (limit2 > 0)
+      limit2 = 1;
+  }
+  else
+    limit2 -= p->keepSizeAfter;
+  if (limit2 < limit)
+    limit = limit2;
+  {
+    UInt32 lenLimit = p->streamPos - p->pos;
+    if (lenLimit > p->matchMaxLen)
+      lenLimit = p->matchMaxLen;
+    p->lenLimit = lenLimit;
+  }
+  p->posLimit = p->pos + limit;
+}
+
+void MatchFinder_Init(CMatchFinder *p)
+{
+  UInt32 i;
+  for (i = 0; i < p->hashSizeSum; i++)
+    p->hash[i] = kEmptyHashValue;
+  p->cyclicBufferPos = 0;
+  p->buffer = p->bufferBase;
+  p->pos = p->streamPos = p->cyclicBufferSize;
+  p->result = SZ_OK;
+  p->streamEndWasReached = 0;
+  MatchFinder_ReadBlock(p);
+  MatchFinder_SetLimits(p);
+}
+
+static UInt32 MatchFinder_GetSubValue(CMatchFinder *p)
+{
+  return (p->pos - p->historySize - 1) & kNormalizeMask;
+}
+
+void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems)
+{
+  UInt32 i;
+  for (i = 0; i < numItems; i++)
+  {
+    UInt32 value = items[i];
+    if (value <= subValue)
+      value = kEmptyHashValue;
+    else
+      value -= subValue;
+    items[i] = value;
+  }
+}
+
+static void MatchFinder_Normalize(CMatchFinder *p)
+{
+  UInt32 subValue = MatchFinder_GetSubValue(p);
+  MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons);
+  MatchFinder_ReduceOffsets(p, subValue);
+}
+
+static void MatchFinder_CheckLimits(CMatchFinder *p)
+{
+  if (p->pos == kMaxValForNormalize)
+    MatchFinder_Normalize(p);
+  if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos)
+    MatchFinder_CheckAndMoveAndRead(p);
+  if (p->cyclicBufferPos == p->cyclicBufferSize)
+    p->cyclicBufferPos = 0;
+  MatchFinder_SetLimits(p);
+}
+
+static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
+    UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue,
+    UInt32 *distances, UInt32 maxLen)
+{
+  son[_cyclicBufferPos] = curMatch;
+  for (;;)
+  {
+    UInt32 delta = pos - curMatch;
+    if (cutValue-- == 0 || delta >= _cyclicBufferSize)
+      return distances;
+    {
+      const Byte *pb = cur - delta;
+      curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)];
+      if (pb[maxLen] == cur[maxLen] && *pb == *cur)
+      {
+        UInt32 len = 0;
+        while (++len != lenLimit)
+          if (pb[len] != cur[len])
+            break;
+        if (maxLen < len)
+        {
+          *distances++ = maxLen = len;
+          *distances++ = delta - 1;
+          if (len == lenLimit)
+            return distances;
+        }
+      }
+    }
+  }
+}
+
+UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
+    UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue,
+    UInt32 *distances, UInt32 maxLen)
+{
+  CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1;
+  CLzRef *ptr1 = son + (_cyclicBufferPos << 1);
+  UInt32 len0 = 0, len1 = 0;
+  for (;;)
+  {
+    UInt32 delta = pos - curMatch;
+    if (cutValue-- == 0 || delta >= _cyclicBufferSize)
+    {
+      *ptr0 = *ptr1 = kEmptyHashValue;
+      return distances;
+    }
+    {
+      CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1);
+      const Byte *pb = cur - delta;
+      UInt32 len = (len0 < len1 ? len0 : len1);
+      if (pb[len] == cur[len])
+      {
+        if (++len != lenLimit && pb[len] == cur[len])
+          while (++len != lenLimit)
+            if (pb[len] != cur[len])
+              break;
+        if (maxLen < len)
+        {
+          *distances++ = maxLen = len;
+          *distances++ = delta - 1;
+          if (len == lenLimit)
+          {
+            *ptr1 = pair[0];
+            *ptr0 = pair[1];
+            return distances;
+          }
+        }
+      }
+      if (pb[len] < cur[len])
+      {
+        *ptr1 = curMatch;
+        ptr1 = pair + 1;
+        curMatch = *ptr1;
+        len1 = len;
+      }
+      else
+      {
+        *ptr0 = curMatch;
+        ptr0 = pair;
+        curMatch = *ptr0;
+        len0 = len;
+      }
+    }
+  }
+}
+
+static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
+    UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue)
+{
+  CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1;
+  CLzRef *ptr1 = son + (_cyclicBufferPos << 1);
+  UInt32 len0 = 0, len1 = 0;
+  for (;;)
+  {
+    UInt32 delta = pos - curMatch;
+    if (cutValue-- == 0 || delta >= _cyclicBufferSize)
+    {
+      *ptr0 = *ptr1 = kEmptyHashValue;
+      return;
+    }
+    {
+      CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1);
+      const Byte *pb = cur - delta;
+      UInt32 len = (len0 < len1 ? len0 : len1);
+      if (pb[len] == cur[len])
+      {
+        while (++len != lenLimit)
+          if (pb[len] != cur[len])
+            break;
+        {
+          if (len == lenLimit)
+          {
+            *ptr1 = pair[0];
+            *ptr0 = pair[1];
+            return;
+          }
+        }
+      }
+      if (pb[len] < cur[len])
+      {
+        *ptr1 = curMatch;
+        ptr1 = pair + 1;
+        curMatch = *ptr1;
+        len1 = len;
+      }
+      else
+      {
+        *ptr0 = curMatch;
+        ptr0 = pair;
+        curMatch = *ptr0;
+        len0 = len;
+      }
+    }
+  }
+}
+
+#define MOVE_POS \
+  ++p->cyclicBufferPos; \
+  p->buffer++; \
+  if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p);
+
+#define MOVE_POS_RET MOVE_POS return offset;
+
+static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; }
+
+#define GET_MATCHES_HEADER2(minLen, ret_op) \
+  UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \
+  lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \
+  cur = p->buffer;
+
+#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0)
+#define SKIP_HEADER(minLen)        GET_MATCHES_HEADER2(minLen, continue)
+
+#define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue
+
+#define GET_MATCHES_FOOTER(offset, maxLen) \
+  offset = (UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \
+  distances + offset, maxLen) - distances); MOVE_POS_RET;
+
+#define SKIP_FOOTER \
+  SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS;
+
+static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+{
+  UInt32 offset;
+  GET_MATCHES_HEADER(2)
+  HASH2_CALC;
+  curMatch = p->hash[hashValue];
+  p->hash[hashValue] = p->pos;
+  offset = 0;
+  GET_MATCHES_FOOTER(offset, 1)
+}
+
+UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+{
+  UInt32 offset;
+  GET_MATCHES_HEADER(3)
+  HASH_ZIP_CALC;
+  curMatch = p->hash[hashValue];
+  p->hash[hashValue] = p->pos;
+  offset = 0;
+  GET_MATCHES_FOOTER(offset, 2)
+}
+
+static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+{
+  UInt32 hash2Value, delta2, maxLen, offset;
+  GET_MATCHES_HEADER(3)
+
+  HASH3_CALC;
+
+  delta2 = p->pos - p->hash[hash2Value];
+  curMatch = p->hash[kFix3HashSize + hashValue];
+  
+  p->hash[hash2Value] =
+  p->hash[kFix3HashSize + hashValue] = p->pos;
+
+
+  maxLen = 2;
+  offset = 0;
+  if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur)
+  {
+    for (; maxLen != lenLimit; maxLen++)
+      if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen])
+        break;
+    distances[0] = maxLen;
+    distances[1] = delta2 - 1;
+    offset = 2;
+    if (maxLen == lenLimit)
+    {
+      SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p));
+      MOVE_POS_RET;
+    }
+  }
+  GET_MATCHES_FOOTER(offset, maxLen)
+}
+
+static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+{
+  UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset;
+  GET_MATCHES_HEADER(4)
+
+  HASH4_CALC;
+
+  delta2 = p->pos - p->hash[                hash2Value];
+  delta3 = p->pos - p->hash[kFix3HashSize + hash3Value];
+  curMatch = p->hash[kFix4HashSize + hashValue];
+  
+  p->hash[                hash2Value] =
+  p->hash[kFix3HashSize + hash3Value] =
+  p->hash[kFix4HashSize + hashValue] = p->pos;
+
+  maxLen = 1;
+  offset = 0;
+  if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur)
+  {
+    distances[0] = maxLen = 2;
+    distances[1] = delta2 - 1;
+    offset = 2;
+  }
+  if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur)
+  {
+    maxLen = 3;
+    distances[offset + 1] = delta3 - 1;
+    offset += 2;
+    delta2 = delta3;
+  }
+  if (offset != 0)
+  {
+    for (; maxLen != lenLimit; maxLen++)
+      if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen])
+        break;
+    distances[offset - 2] = maxLen;
+    if (maxLen == lenLimit)
+    {
+      SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p));
+      MOVE_POS_RET;
+    }
+  }
+  if (maxLen < 3)
+    maxLen = 3;
+  GET_MATCHES_FOOTER(offset, maxLen)
+}
+
+static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+{
+  UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset;
+  GET_MATCHES_HEADER(4)
+
+  HASH4_CALC;
+
+  delta2 = p->pos - p->hash[                hash2Value];
+  delta3 = p->pos - p->hash[kFix3HashSize + hash3Value];
+  curMatch = p->hash[kFix4HashSize + hashValue];
+
+  p->hash[                hash2Value] =
+  p->hash[kFix3HashSize + hash3Value] =
+  p->hash[kFix4HashSize + hashValue] = p->pos;
+
+  maxLen = 1;
+  offset = 0;
+  if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur)
+  {
+    distances[0] = maxLen = 2;
+    distances[1] = delta2 - 1;
+    offset = 2;
+  }
+  if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur)
+  {
+    maxLen = 3;
+    distances[offset + 1] = delta3 - 1;
+    offset += 2;
+    delta2 = delta3;
+  }
+  if (offset != 0)
+  {
+    for (; maxLen != lenLimit; maxLen++)
+      if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen])
+        break;
+    distances[offset - 2] = maxLen;
+    if (maxLen == lenLimit)
+    {
+      p->son[p->cyclicBufferPos] = curMatch;
+      MOVE_POS_RET;
+    }
+  }
+  if (maxLen < 3)
+    maxLen = 3;
+  offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p),
+    distances + offset, maxLen) - (distances));
+  MOVE_POS_RET
+}
+
+UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
+{
+  UInt32 offset;
+  GET_MATCHES_HEADER(3)
+  HASH_ZIP_CALC;
+  curMatch = p->hash[hashValue];
+  p->hash[hashValue] = p->pos;
+  offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p),
+    distances, 2) - (distances));
+  MOVE_POS_RET
+}
+
+static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+{
+  do
+  {
+    SKIP_HEADER(2)
+    HASH2_CALC;
+    curMatch = p->hash[hashValue];
+    p->hash[hashValue] = p->pos;
+    SKIP_FOOTER
+  }
+  while (--num != 0);
+}
+
+void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+{
+  do
+  {
+    SKIP_HEADER(3)
+    HASH_ZIP_CALC;
+    curMatch = p->hash[hashValue];
+    p->hash[hashValue] = p->pos;
+    SKIP_FOOTER
+  }
+  while (--num != 0);
+}
+
+static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+{
+  do
+  {
+    UInt32 hash2Value;
+    SKIP_HEADER(3)
+    HASH3_CALC;
+    curMatch = p->hash[kFix3HashSize + hashValue];
+    p->hash[hash2Value] =
+    p->hash[kFix3HashSize + hashValue] = p->pos;
+    SKIP_FOOTER
+  }
+  while (--num != 0);
+}
+
+static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+{
+  do
+  {
+    UInt32 hash2Value, hash3Value;
+    SKIP_HEADER(4)
+    HASH4_CALC;
+    curMatch = p->hash[kFix4HashSize + hashValue];
+    p->hash[                hash2Value] =
+    p->hash[kFix3HashSize + hash3Value] = p->pos;
+    p->hash[kFix4HashSize + hashValue] = p->pos;
+    SKIP_FOOTER
+  }
+  while (--num != 0);
+}
+
+static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+{
+  do
+  {
+    UInt32 hash2Value, hash3Value;
+    SKIP_HEADER(4)
+    HASH4_CALC;
+    curMatch = p->hash[kFix4HashSize + hashValue];
+    p->hash[                hash2Value] =
+    p->hash[kFix3HashSize + hash3Value] =
+    p->hash[kFix4HashSize + hashValue] = p->pos;
+    p->son[p->cyclicBufferPos] = curMatch;
+    MOVE_POS
+  }
+  while (--num != 0);
+}
+
+void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
+{
+  do
+  {
+    SKIP_HEADER(3)
+    HASH_ZIP_CALC;
+    curMatch = p->hash[hashValue];
+    p->hash[hashValue] = p->pos;
+    p->son[p->cyclicBufferPos] = curMatch;
+    MOVE_POS
+  }
+  while (--num != 0);
+}
+
+void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable)
+{
+  vTable->Init = (Mf_Init_Func)MatchFinder_Init;
+  vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte;
+  vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes;
+  vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos;
+  if (!p->btMode)
+  {
+    vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches;
+    vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip;
+  }
+  else if (p->numHashBytes == 2)
+  {
+    vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches;
+    vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip;
+  }
+  else if (p->numHashBytes == 3)
+  {
+    vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches;
+    vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip;
+  }
+  else
+  {
+    vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches;
+    vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip;
+  }
+}
diff --git a/third_party/lzma/v4_65/files/C/LzFind.h b/third_party/lzma/v4_65/files/C/LzFind.h
new file mode 100644
index 0000000..5b9cebf
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzFind.h
@@ -0,0 +1,107 @@
+/* LzFind.h -- Match finder for LZ algorithms
+2008-10-04 : Igor Pavlov : Public domain */
+
+#ifndef __LZFIND_H
+#define __LZFIND_H
+
+#include "Types.h"
+
+typedef UInt32 CLzRef;
+
+typedef struct _CMatchFinder
+{
+  Byte *buffer;
+  UInt32 pos;
+  UInt32 posLimit;
+  UInt32 streamPos;
+  UInt32 lenLimit;
+
+  UInt32 cyclicBufferPos;
+  UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */
+
+  UInt32 matchMaxLen;
+  CLzRef *hash;
+  CLzRef *son;
+  UInt32 hashMask;
+  UInt32 cutValue;
+
+  Byte *bufferBase;
+  ISeqInStream *stream;
+  int streamEndWasReached;
+
+  UInt32 blockSize;
+  UInt32 keepSizeBefore;
+  UInt32 keepSizeAfter;
+
+  UInt32 numHashBytes;
+  int directInput;
+  int btMode;
+  /* int skipModeBits; */
+  int bigHash;
+  UInt32 historySize;
+  UInt32 fixedHashSize;
+  UInt32 hashSizeSum;
+  UInt32 numSons;
+  SRes result;
+  UInt32 crc[256];
+} CMatchFinder;
+
+#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer)
+#define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)])
+
+#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos)
+
+int MatchFinder_NeedMove(CMatchFinder *p);
+Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p);
+void MatchFinder_MoveBlock(CMatchFinder *p);
+void MatchFinder_ReadIfRequired(CMatchFinder *p);
+
+void MatchFinder_Construct(CMatchFinder *p);
+
+/* Conditions:
+     historySize <= 3 GB
+     keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB
+*/
+int MatchFinder_Create(CMatchFinder *p, UInt32 historySize,
+    UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter,
+    ISzAlloc *alloc);
+void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc);
+void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems);
+void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue);
+
+UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son,
+    UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue,
+    UInt32 *distances, UInt32 maxLen);
+
+/*
+Conditions:
+  Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func.
+  Mf_GetPointerToCurrentPos_Func's result must be used only before any other function
+*/
+
+typedef void (*Mf_Init_Func)(void *object);
+typedef Byte (*Mf_GetIndexByte_Func)(void *object, Int32 index);
+typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object);
+typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object);
+typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances);
+typedef void (*Mf_Skip_Func)(void *object, UInt32);
+
+typedef struct _IMatchFinder
+{
+  Mf_Init_Func Init;
+  Mf_GetIndexByte_Func GetIndexByte;
+  Mf_GetNumAvailableBytes_Func GetNumAvailableBytes;
+  Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos;
+  Mf_GetMatches_Func GetMatches;
+  Mf_Skip_Func Skip;
+} IMatchFinder;
+
+void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable);
+
+void MatchFinder_Init(CMatchFinder *p);
+UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
+UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
+void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
+void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/LzFindMt.c b/third_party/lzma/v4_65/files/C/LzFindMt.c
new file mode 100644
index 0000000..b49cd76
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzFindMt.c
@@ -0,0 +1,793 @@
+/* LzFindMt.c -- multithreaded Match finder for LZ algorithms
+2008-10-04 : Igor Pavlov : Public domain */
+
+#include "LzHash.h"
+
+#include "LzFindMt.h"
+
+void MtSync_Construct(CMtSync *p)
+{
+  p->wasCreated = False;
+  p->csWasInitialized = False;
+  p->csWasEntered = False;
+  Thread_Construct(&p->thread);
+  Event_Construct(&p->canStart);
+  Event_Construct(&p->wasStarted);
+  Event_Construct(&p->wasStopped);
+  Semaphore_Construct(&p->freeSemaphore);
+  Semaphore_Construct(&p->filledSemaphore);
+}
+
+void MtSync_GetNextBlock(CMtSync *p)
+{
+  if (p->needStart)
+  {
+    p->numProcessedBlocks = 1;
+    p->needStart = False;
+    p->stopWriting = False;
+    p->exit = False;
+    Event_Reset(&p->wasStarted);
+    Event_Reset(&p->wasStopped);
+
+    Event_Set(&p->canStart);
+    Event_Wait(&p->wasStarted);
+  }
+  else
+  {
+    CriticalSection_Leave(&p->cs);
+    p->csWasEntered = False;
+    p->numProcessedBlocks++;
+    Semaphore_Release1(&p->freeSemaphore);
+  }
+  Semaphore_Wait(&p->filledSemaphore);
+  CriticalSection_Enter(&p->cs);
+  p->csWasEntered = True;
+}
+
+/* MtSync_StopWriting must be called if Writing was started */
+
+void MtSync_StopWriting(CMtSync *p)
+{
+  UInt32 myNumBlocks = p->numProcessedBlocks;
+  if (!Thread_WasCreated(&p->thread) || p->needStart)
+    return;
+  p->stopWriting = True;
+  if (p->csWasEntered)
+  {
+    CriticalSection_Leave(&p->cs);
+    p->csWasEntered = False;
+  }
+  Semaphore_Release1(&p->freeSemaphore);
+ 
+  Event_Wait(&p->wasStopped);
+
+  while (myNumBlocks++ != p->numProcessedBlocks)
+  {
+    Semaphore_Wait(&p->filledSemaphore);
+    Semaphore_Release1(&p->freeSemaphore);
+  }
+  p->needStart = True;
+}
+
+void MtSync_Destruct(CMtSync *p)
+{
+  if (Thread_WasCreated(&p->thread))
+  {
+    MtSync_StopWriting(p);
+    p->exit = True;
+    if (p->needStart)
+      Event_Set(&p->canStart);
+    Thread_Wait(&p->thread);
+    Thread_Close(&p->thread);
+  }
+  if (p->csWasInitialized)
+  {
+    CriticalSection_Delete(&p->cs);
+    p->csWasInitialized = False;
+  }
+
+  Event_Close(&p->canStart);
+  Event_Close(&p->wasStarted);
+  Event_Close(&p->wasStopped);
+  Semaphore_Close(&p->freeSemaphore);
+  Semaphore_Close(&p->filledSemaphore);
+
+  p->wasCreated = False;
+}
+
+#define RINOK_THREAD(x) { if ((x) != 0) return SZ_ERROR_THREAD; }
+
+static SRes MtSync_Create2(CMtSync *p, unsigned (MY_STD_CALL *startAddress)(void *), void *obj, UInt32 numBlocks)
+{
+  if (p->wasCreated)
+    return SZ_OK;
+
+  RINOK_THREAD(CriticalSection_Init(&p->cs));
+  p->csWasInitialized = True;
+
+  RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->canStart));
+  RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->wasStarted));
+  RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->wasStopped));
+  
+  RINOK_THREAD(Semaphore_Create(&p->freeSemaphore, numBlocks, numBlocks));
+  RINOK_THREAD(Semaphore_Create(&p->filledSemaphore, 0, numBlocks));
+
+  p->needStart = True;
+  
+  RINOK_THREAD(Thread_Create(&p->thread, startAddress, obj));
+  p->wasCreated = True;
+  return SZ_OK;
+}
+
+static SRes MtSync_Create(CMtSync *p, unsigned (MY_STD_CALL *startAddress)(void *), void *obj, UInt32 numBlocks)
+{
+  SRes res = MtSync_Create2(p, startAddress, obj, numBlocks);
+  if (res != SZ_OK)
+    MtSync_Destruct(p);
+  return res;
+}
+
+void MtSync_Init(CMtSync *p) { p->needStart = True; }
+
+#define kMtMaxValForNormalize 0xFFFFFFFF
+
+#define DEF_GetHeads2(name, v, action) \
+static void GetHeads ## name(const Byte *p, UInt32 pos, \
+UInt32 *hash, UInt32 hashMask, UInt32 *heads, UInt32 numHeads, const UInt32 *crc) \
+{ action; for (; numHeads != 0; numHeads--) { \
+const UInt32 value = (v); p++; *heads++ = pos - hash[value]; hash[value] = pos++;  } }
+
+#define DEF_GetHeads(name, v) DEF_GetHeads2(name, v, ;)
+
+DEF_GetHeads2(2,  (p[0] | ((UInt32)p[1] << 8)), hashMask = hashMask; crc = crc; )
+DEF_GetHeads(3,  (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8)) & hashMask)
+DEF_GetHeads(4,  (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ (crc[p[3]] << 5)) & hashMask)
+DEF_GetHeads(4b, (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ ((UInt32)p[3] << 16)) & hashMask)
+DEF_GetHeads(5,  (crc[p[0]] ^ p[1] ^ ((UInt32)p[2] << 8) ^ (crc[p[3]] << 5) ^ (crc[p[4]] << 3)) & hashMask)
+
+void HashThreadFunc(CMatchFinderMt *mt)
+{
+  CMtSync *p = &mt->hashSync;
+  for (;;)
+  {
+    UInt32 numProcessedBlocks = 0;
+    Event_Wait(&p->canStart);
+    Event_Set(&p->wasStarted);
+    for (;;)
+    {
+      if (p->exit)
+        return;
+      if (p->stopWriting)
+      {
+        p->numProcessedBlocks = numProcessedBlocks;
+        Event_Set(&p->wasStopped);
+        break;
+      }
+
+      {
+        CMatchFinder *mf = mt->MatchFinder;
+        if (MatchFinder_NeedMove(mf))
+        {
+          CriticalSection_Enter(&mt->btSync.cs);
+          CriticalSection_Enter(&mt->hashSync.cs);
+          {
+            const Byte *beforePtr = MatchFinder_GetPointerToCurrentPos(mf);
+            const Byte *afterPtr;
+            MatchFinder_MoveBlock(mf);
+            afterPtr = MatchFinder_GetPointerToCurrentPos(mf);
+            mt->pointerToCurPos -= beforePtr - afterPtr;
+            mt->buffer -= beforePtr - afterPtr;
+          }
+          CriticalSection_Leave(&mt->btSync.cs);
+          CriticalSection_Leave(&mt->hashSync.cs);
+          continue;
+        }
+
+        Semaphore_Wait(&p->freeSemaphore);
+
+        MatchFinder_ReadIfRequired(mf);
+        if (mf->pos > (kMtMaxValForNormalize - kMtHashBlockSize))
+        {
+          UInt32 subValue = (mf->pos - mf->historySize - 1);
+          MatchFinder_ReduceOffsets(mf, subValue);
+          MatchFinder_Normalize3(subValue, mf->hash + mf->fixedHashSize, mf->hashMask + 1);
+        }
+        {
+          UInt32 *heads = mt->hashBuf + ((numProcessedBlocks++) & kMtHashNumBlocksMask) * kMtHashBlockSize;
+          UInt32 num = mf->streamPos - mf->pos;
+          heads[0] = 2;
+          heads[1] = num;
+          if (num >= mf->numHashBytes)
+          {
+            num = num - mf->numHashBytes + 1;
+            if (num > kMtHashBlockSize - 2)
+              num = kMtHashBlockSize - 2;
+            mt->GetHeadsFunc(mf->buffer, mf->pos, mf->hash + mf->fixedHashSize, mf->hashMask, heads + 2, num, mf->crc);
+            heads[0] += num;
+          }
+          mf->pos += num;
+          mf->buffer += num;
+        }
+      }
+
+      Semaphore_Release1(&p->filledSemaphore);
+    }
+  }
+}
+
+void MatchFinderMt_GetNextBlock_Hash(CMatchFinderMt *p)
+{
+  MtSync_GetNextBlock(&p->hashSync);
+  p->hashBufPosLimit = p->hashBufPos = ((p->hashSync.numProcessedBlocks - 1) & kMtHashNumBlocksMask) * kMtHashBlockSize;
+  p->hashBufPosLimit += p->hashBuf[p->hashBufPos++];
+  p->hashNumAvail = p->hashBuf[p->hashBufPos++];
+}
+
+#define kEmptyHashValue 0
+
+/* #define MFMT_GM_INLINE */
+
+#ifdef MFMT_GM_INLINE
+
+#define NO_INLINE MY_FAST_CALL
+
+Int32 NO_INLINE GetMatchesSpecN(UInt32 lenLimit, UInt32 pos, const Byte *cur, CLzRef *son,
+    UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue,
+    UInt32 *_distances, UInt32 _maxLen, const UInt32 *hash, Int32 limit, UInt32 size, UInt32 *posRes)
+{
+  do
+  {
+  UInt32 *distances = _distances + 1;
+  UInt32 curMatch = pos - *hash++;
+
+  CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1;
+  CLzRef *ptr1 = son + (_cyclicBufferPos << 1);
+  UInt32 len0 = 0, len1 = 0;
+  UInt32 cutValue = _cutValue;
+  UInt32 maxLen = _maxLen;
+  for (;;)
+  {
+    UInt32 delta = pos - curMatch;
+    if (cutValue-- == 0 || delta >= _cyclicBufferSize)
+    {
+      *ptr0 = *ptr1 = kEmptyHashValue;
+      break;
+    }
+    {
+      CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1);
+      const Byte *pb = cur - delta;
+      UInt32 len = (len0 < len1 ? len0 : len1);
+      if (pb[len] == cur[len])
+      {
+        if (++len != lenLimit && pb[len] == cur[len])
+          while (++len != lenLimit)
+            if (pb[len] != cur[len])
+              break;
+        if (maxLen < len)
+        {
+          *distances++ = maxLen = len;
+          *distances++ = delta - 1;
+          if (len == lenLimit)
+          {
+            *ptr1 = pair[0];
+            *ptr0 = pair[1];
+            break;
+          }
+        }
+      }
+      if (pb[len] < cur[len])
+      {
+        *ptr1 = curMatch;
+        ptr1 = pair + 1;
+        curMatch = *ptr1;
+        len1 = len;
+      }
+      else
+      {
+        *ptr0 = curMatch;
+        ptr0 = pair;
+        curMatch = *ptr0;
+        len0 = len;
+      }
+    }
+  }
+  pos++;
+  _cyclicBufferPos++;
+  cur++;
+  {
+    UInt32 num = (UInt32)(distances - _distances);
+    *_distances = num - 1;
+    _distances += num;
+    limit -= num;
+  }
+  }
+  while (limit > 0 && --size != 0);
+  *posRes = pos;
+  return limit;
+}
+
+#endif
+
+void BtGetMatches(CMatchFinderMt *p, UInt32 *distances)
+{
+  UInt32 numProcessed = 0;
+  UInt32 curPos = 2;
+  UInt32 limit = kMtBtBlockSize - (p->matchMaxLen * 2);
+  distances[1] = p->hashNumAvail;
+  while (curPos < limit)
+  {
+    if (p->hashBufPos == p->hashBufPosLimit)
+    {
+      MatchFinderMt_GetNextBlock_Hash(p);
+      distances[1] = numProcessed + p->hashNumAvail;
+      if (p->hashNumAvail >= p->numHashBytes)
+        continue;
+      for (; p->hashNumAvail != 0; p->hashNumAvail--)
+        distances[curPos++] = 0;
+      break;
+    }
+    {
+      UInt32 size = p->hashBufPosLimit - p->hashBufPos;
+      UInt32 lenLimit = p->matchMaxLen;
+      UInt32 pos = p->pos;
+      UInt32 cyclicBufferPos = p->cyclicBufferPos;
+      if (lenLimit >= p->hashNumAvail)
+        lenLimit = p->hashNumAvail;
+      {
+        UInt32 size2 = p->hashNumAvail - lenLimit + 1;
+        if (size2 < size)
+          size = size2;
+        size2 = p->cyclicBufferSize - cyclicBufferPos;
+        if (size2 < size)
+          size = size2;
+      }
+      #ifndef MFMT_GM_INLINE
+      while (curPos < limit && size-- != 0)
+      {
+        UInt32 *startDistances = distances + curPos;
+        UInt32 num = (UInt32)(GetMatchesSpec1(lenLimit, pos - p->hashBuf[p->hashBufPos++],
+          pos, p->buffer, p->son, cyclicBufferPos, p->cyclicBufferSize, p->cutValue,
+          startDistances + 1, p->numHashBytes - 1) - startDistances);
+        *startDistances = num - 1;
+        curPos += num;
+        cyclicBufferPos++;
+        pos++;
+        p->buffer++;
+      }
+      #else
+      {
+        UInt32 posRes;
+        curPos = limit - GetMatchesSpecN(lenLimit, pos, p->buffer, p->son, cyclicBufferPos, p->cyclicBufferSize, p->cutValue,
+          distances + curPos, p->numHashBytes - 1, p->hashBuf + p->hashBufPos, (Int32)(limit - curPos) , size, &posRes);
+        p->hashBufPos += posRes - pos;
+        cyclicBufferPos += posRes - pos;
+        p->buffer += posRes - pos;
+        pos = posRes;
+      }
+      #endif
+
+      numProcessed += pos - p->pos;
+      p->hashNumAvail -= pos - p->pos;
+      p->pos = pos;
+      if (cyclicBufferPos == p->cyclicBufferSize)
+        cyclicBufferPos = 0;
+      p->cyclicBufferPos = cyclicBufferPos;
+    }
+  }
+  distances[0] = curPos;
+}
+
+void BtFillBlock(CMatchFinderMt *p, UInt32 globalBlockIndex)
+{
+  CMtSync *sync = &p->hashSync;
+  if (!sync->needStart)
+  {
+    CriticalSection_Enter(&sync->cs);
+    sync->csWasEntered = True;
+  }
+  
+  BtGetMatches(p, p->btBuf + (globalBlockIndex & kMtBtNumBlocksMask) * kMtBtBlockSize);
+
+  if (p->pos > kMtMaxValForNormalize - kMtBtBlockSize)
+  {
+    UInt32 subValue = p->pos - p->cyclicBufferSize;
+    MatchFinder_Normalize3(subValue, p->son, p->cyclicBufferSize * 2);
+    p->pos -= subValue;
+  }
+
+  if (!sync->needStart)
+  {
+    CriticalSection_Leave(&sync->cs);
+    sync->csWasEntered = False;
+  }
+}
+
+void BtThreadFunc(CMatchFinderMt *mt)
+{
+  CMtSync *p = &mt->btSync;
+  for (;;)
+  {
+    UInt32 blockIndex = 0;
+    Event_Wait(&p->canStart);
+    Event_Set(&p->wasStarted);
+    for (;;)
+    {
+      if (p->exit)
+        return;
+      if (p->stopWriting)
+      {
+        p->numProcessedBlocks = blockIndex;
+        MtSync_StopWriting(&mt->hashSync);
+        Event_Set(&p->wasStopped);
+        break;
+      }
+      Semaphore_Wait(&p->freeSemaphore);
+      BtFillBlock(mt, blockIndex++);
+      Semaphore_Release1(&p->filledSemaphore);
+    }
+  }
+}
+
+void MatchFinderMt_Construct(CMatchFinderMt *p)
+{
+  p->hashBuf = 0;
+  MtSync_Construct(&p->hashSync);
+  MtSync_Construct(&p->btSync);
+}
+
+void MatchFinderMt_FreeMem(CMatchFinderMt *p, ISzAlloc *alloc)
+{
+  alloc->Free(alloc, p->hashBuf);
+  p->hashBuf = 0;
+}
+
+void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAlloc *alloc)
+{
+  MtSync_Destruct(&p->hashSync);
+  MtSync_Destruct(&p->btSync);
+  MatchFinderMt_FreeMem(p, alloc);
+}
+
+#define kHashBufferSize (kMtHashBlockSize * kMtHashNumBlocks)
+#define kBtBufferSize (kMtBtBlockSize * kMtBtNumBlocks)
+
+static unsigned MY_STD_CALL HashThreadFunc2(void *p) { HashThreadFunc((CMatchFinderMt *)p);  return 0; }
+static unsigned MY_STD_CALL BtThreadFunc2(void *p)
+{
+  Byte allocaDummy[0x180];
+  int i = 0;
+  for (i = 0; i < 16; i++)
+    allocaDummy[i] = (Byte)i;
+  BtThreadFunc((CMatchFinderMt *)p);
+  return 0;
+}
+
+SRes MatchFinderMt_Create(CMatchFinderMt *p, UInt32 historySize, UInt32 keepAddBufferBefore,
+    UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc)
+{
+  CMatchFinder *mf = p->MatchFinder;
+  p->historySize = historySize;
+  if (kMtBtBlockSize <= matchMaxLen * 4)
+    return SZ_ERROR_PARAM;
+  if (p->hashBuf == 0)
+  {
+    p->hashBuf = (UInt32 *)alloc->Alloc(alloc, (kHashBufferSize + kBtBufferSize) * sizeof(UInt32));
+    if (p->hashBuf == 0)
+      return SZ_ERROR_MEM;
+    p->btBuf = p->hashBuf + kHashBufferSize;
+  }
+  keepAddBufferBefore += (kHashBufferSize + kBtBufferSize);
+  keepAddBufferAfter += kMtHashBlockSize;
+  if (!MatchFinder_Create(mf, historySize, keepAddBufferBefore, matchMaxLen, keepAddBufferAfter, alloc))
+    return SZ_ERROR_MEM;
+
+  RINOK(MtSync_Create(&p->hashSync, HashThreadFunc2, p, kMtHashNumBlocks));
+  RINOK(MtSync_Create(&p->btSync, BtThreadFunc2, p, kMtBtNumBlocks));
+  return SZ_OK;
+}
+
+/* Call it after ReleaseStream / SetStream */
+void MatchFinderMt_Init(CMatchFinderMt *p)
+{
+  CMatchFinder *mf = p->MatchFinder;
+  p->btBufPos = p->btBufPosLimit = 0;
+  p->hashBufPos = p->hashBufPosLimit = 0;
+  MatchFinder_Init(mf);
+  p->pointerToCurPos = MatchFinder_GetPointerToCurrentPos(mf);
+  p->btNumAvailBytes = 0;
+  p->lzPos = p->historySize + 1;
+
+  p->hash = mf->hash;
+  p->fixedHashSize = mf->fixedHashSize;
+  p->crc = mf->crc;
+
+  p->son = mf->son;
+  p->matchMaxLen = mf->matchMaxLen;
+  p->numHashBytes = mf->numHashBytes;
+  p->pos = mf->pos;
+  p->buffer = mf->buffer;
+  p->cyclicBufferPos = mf->cyclicBufferPos;
+  p->cyclicBufferSize = mf->cyclicBufferSize;
+  p->cutValue = mf->cutValue;
+}
+
+/* ReleaseStream is required to finish multithreading */
+void MatchFinderMt_ReleaseStream(CMatchFinderMt *p)
+{
+  MtSync_StopWriting(&p->btSync);
+  /* p->MatchFinder->ReleaseStream(); */
+}
+
+void MatchFinderMt_Normalize(CMatchFinderMt *p)
+{
+  MatchFinder_Normalize3(p->lzPos - p->historySize - 1, p->hash, p->fixedHashSize);
+  p->lzPos = p->historySize + 1;
+}
+
+void MatchFinderMt_GetNextBlock_Bt(CMatchFinderMt *p)
+{
+  UInt32 blockIndex;
+  MtSync_GetNextBlock(&p->btSync);
+  blockIndex = ((p->btSync.numProcessedBlocks - 1) & kMtBtNumBlocksMask);
+  p->btBufPosLimit = p->btBufPos = blockIndex * kMtBtBlockSize;
+  p->btBufPosLimit += p->btBuf[p->btBufPos++];
+  p->btNumAvailBytes = p->btBuf[p->btBufPos++];
+  if (p->lzPos >= kMtMaxValForNormalize - kMtBtBlockSize)
+    MatchFinderMt_Normalize(p);
+}
+
+const Byte * MatchFinderMt_GetPointerToCurrentPos(CMatchFinderMt *p)
+{
+  return p->pointerToCurPos;
+}
+
+#define GET_NEXT_BLOCK_IF_REQUIRED if (p->btBufPos == p->btBufPosLimit) MatchFinderMt_GetNextBlock_Bt(p);
+
+UInt32 MatchFinderMt_GetNumAvailableBytes(CMatchFinderMt *p)
+{
+  GET_NEXT_BLOCK_IF_REQUIRED;
+  return p->btNumAvailBytes;
+}
+
+Byte MatchFinderMt_GetIndexByte(CMatchFinderMt *p, Int32 index)
+{
+  return p->pointerToCurPos[index];
+}
+
+UInt32 * MixMatches2(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances)
+{
+  UInt32 hash2Value, curMatch2;
+  UInt32 *hash = p->hash;
+  const Byte *cur = p->pointerToCurPos;
+  UInt32 lzPos = p->lzPos;
+  MT_HASH2_CALC
+      
+  curMatch2 = hash[hash2Value];
+  hash[hash2Value] = lzPos;
+
+  if (curMatch2 >= matchMinPos)
+    if (cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0])
+    {
+      *distances++ = 2;
+      *distances++ = lzPos - curMatch2 - 1;
+    }
+  return distances;
+}
+
+UInt32 * MixMatches3(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances)
+{
+  UInt32 hash2Value, hash3Value, curMatch2, curMatch3;
+  UInt32 *hash = p->hash;
+  const Byte *cur = p->pointerToCurPos;
+  UInt32 lzPos = p->lzPos;
+  MT_HASH3_CALC
+
+  curMatch2 = hash[                hash2Value];
+  curMatch3 = hash[kFix3HashSize + hash3Value];
+  
+  hash[                hash2Value] =
+  hash[kFix3HashSize + hash3Value] =
+    lzPos;
+
+  if (curMatch2 >= matchMinPos && cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0])
+  {
+    distances[1] = lzPos - curMatch2 - 1;
+    if (cur[(ptrdiff_t)curMatch2 - lzPos + 2] == cur[2])
+    {
+      distances[0] = 3;
+      return distances + 2;
+    }
+    distances[0] = 2;
+    distances += 2;
+  }
+  if (curMatch3 >= matchMinPos && cur[(ptrdiff_t)curMatch3 - lzPos] == cur[0])
+  {
+    *distances++ = 3;
+    *distances++ = lzPos - curMatch3 - 1;
+  }
+  return distances;
+}
+
+/*
+UInt32 *MixMatches4(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances)
+{
+  UInt32 hash2Value, hash3Value, hash4Value, curMatch2, curMatch3, curMatch4;
+  UInt32 *hash = p->hash;
+  const Byte *cur = p->pointerToCurPos;
+  UInt32 lzPos = p->lzPos;
+  MT_HASH4_CALC
+      
+  curMatch2 = hash[                hash2Value];
+  curMatch3 = hash[kFix3HashSize + hash3Value];
+  curMatch4 = hash[kFix4HashSize + hash4Value];
+  
+  hash[                hash2Value] =
+  hash[kFix3HashSize + hash3Value] =
+  hash[kFix4HashSize + hash4Value] =
+    lzPos;
+
+  if (curMatch2 >= matchMinPos && cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0])
+  {
+    distances[1] = lzPos - curMatch2 - 1;
+    if (cur[(ptrdiff_t)curMatch2 - lzPos + 2] == cur[2])
+    {
+      distances[0] =  (cur[(ptrdiff_t)curMatch2 - lzPos + 3] == cur[3]) ? 4 : 3;
+      return distances + 2;
+    }
+    distances[0] = 2;
+    distances += 2;
+  }
+  if (curMatch3 >= matchMinPos && cur[(ptrdiff_t)curMatch3 - lzPos] == cur[0])
+  {
+    distances[1] = lzPos - curMatch3 - 1;
+    if (cur[(ptrdiff_t)curMatch3 - lzPos + 3] == cur[3])
+    {
+      distances[0] = 4;
+      return distances + 2;
+    }
+    distances[0] = 3;
+    distances += 2;
+  }
+
+  if (curMatch4 >= matchMinPos)
+    if (
+      cur[(ptrdiff_t)curMatch4 - lzPos] == cur[0] &&
+      cur[(ptrdiff_t)curMatch4 - lzPos + 3] == cur[3]
+      )
+    {
+      *distances++ = 4;
+      *distances++ = lzPos - curMatch4 - 1;
+    }
+  return distances;
+}
+*/
+
+#define INCREASE_LZ_POS p->lzPos++; p->pointerToCurPos++;
+
+UInt32 MatchFinderMt2_GetMatches(CMatchFinderMt *p, UInt32 *distances)
+{
+  const UInt32 *btBuf = p->btBuf + p->btBufPos;
+  UInt32 len = *btBuf++;
+  p->btBufPos += 1 + len;
+  p->btNumAvailBytes--;
+  {
+    UInt32 i;
+    for (i = 0; i < len; i += 2)
+    {
+      *distances++ = *btBuf++;
+      *distances++ = *btBuf++;
+    }
+  }
+  INCREASE_LZ_POS
+  return len;
+}
+
+UInt32 MatchFinderMt_GetMatches(CMatchFinderMt *p, UInt32 *distances)
+{
+  const UInt32 *btBuf = p->btBuf + p->btBufPos;
+  UInt32 len = *btBuf++;
+  p->btBufPos += 1 + len;
+
+  if (len == 0)
+  {
+    if (p->btNumAvailBytes-- >= 4)
+      len = (UInt32)(p->MixMatchesFunc(p, p->lzPos - p->historySize, distances) - (distances));
+  }
+  else
+  {
+    /* Condition: there are matches in btBuf with length < p->numHashBytes */
+    UInt32 *distances2;
+    p->btNumAvailBytes--;
+    distances2 = p->MixMatchesFunc(p, p->lzPos - btBuf[1], distances);
+    do
+    {
+      *distances2++ = *btBuf++;
+      *distances2++ = *btBuf++;
+    }
+    while ((len -= 2) != 0);
+    len  = (UInt32)(distances2 - (distances));
+  }
+  INCREASE_LZ_POS
+  return len;
+}
+
+#define SKIP_HEADER2  do { GET_NEXT_BLOCK_IF_REQUIRED
+#define SKIP_HEADER(n) SKIP_HEADER2 if (p->btNumAvailBytes-- >= (n)) { const Byte *cur = p->pointerToCurPos; UInt32 *hash = p->hash;
+#define SKIP_FOOTER } INCREASE_LZ_POS p->btBufPos += p->btBuf[p->btBufPos] + 1; } while (--num != 0);
+
+void MatchFinderMt0_Skip(CMatchFinderMt *p, UInt32 num)
+{
+  SKIP_HEADER2 { p->btNumAvailBytes--;
+  SKIP_FOOTER
+}
+
+void MatchFinderMt2_Skip(CMatchFinderMt *p, UInt32 num)
+{
+  SKIP_HEADER(2)
+      UInt32 hash2Value;
+      MT_HASH2_CALC
+      hash[hash2Value] = p->lzPos;
+  SKIP_FOOTER
+}
+
+void MatchFinderMt3_Skip(CMatchFinderMt *p, UInt32 num)
+{
+  SKIP_HEADER(3)
+      UInt32 hash2Value, hash3Value;
+      MT_HASH3_CALC
+      hash[kFix3HashSize + hash3Value] =
+      hash[                hash2Value] =
+        p->lzPos;
+  SKIP_FOOTER
+}
+
+/*
+void MatchFinderMt4_Skip(CMatchFinderMt *p, UInt32 num)
+{
+  SKIP_HEADER(4)
+      UInt32 hash2Value, hash3Value, hash4Value;
+      MT_HASH4_CALC
+      hash[kFix4HashSize + hash4Value] =
+      hash[kFix3HashSize + hash3Value] =
+      hash[                hash2Value] =
+        p->lzPos;
+  SKIP_FOOTER
+}
+*/
+
+void MatchFinderMt_CreateVTable(CMatchFinderMt *p, IMatchFinder *vTable)
+{
+  vTable->Init = (Mf_Init_Func)MatchFinderMt_Init;
+  vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinderMt_GetIndexByte;
+  vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinderMt_GetNumAvailableBytes;
+  vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinderMt_GetPointerToCurrentPos;
+  vTable->GetMatches = (Mf_GetMatches_Func)MatchFinderMt_GetMatches;
+  switch(p->MatchFinder->numHashBytes)
+  {
+    case 2:
+      p->GetHeadsFunc = GetHeads2;
+      p->MixMatchesFunc = (Mf_Mix_Matches)0;
+      vTable->Skip = (Mf_Skip_Func)MatchFinderMt0_Skip;
+      vTable->GetMatches = (Mf_GetMatches_Func)MatchFinderMt2_GetMatches;
+      break;
+    case 3:
+      p->GetHeadsFunc = GetHeads3;
+      p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches2;
+      vTable->Skip = (Mf_Skip_Func)MatchFinderMt2_Skip;
+      break;
+    default:
+    /* case 4: */
+      p->GetHeadsFunc = p->MatchFinder->bigHash ? GetHeads4b : GetHeads4;
+      /* p->GetHeadsFunc = GetHeads4; */
+      p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches3;
+      vTable->Skip = (Mf_Skip_Func)MatchFinderMt3_Skip;
+      break;
+    /*
+    default:
+      p->GetHeadsFunc = GetHeads5;
+      p->MixMatchesFunc = (Mf_Mix_Matches)MixMatches4;
+      vTable->Skip = (Mf_Skip_Func)MatchFinderMt4_Skip;
+      break;
+    */
+  }
+}
diff --git a/third_party/lzma/v4_65/files/C/LzFindMt.h b/third_party/lzma/v4_65/files/C/LzFindMt.h
new file mode 100644
index 0000000..2c7e462
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzFindMt.h
@@ -0,0 +1,97 @@
+/* LzFindMt.h -- multithreaded Match finder for LZ algorithms
+2008-10-04 : Igor Pavlov : Public domain */
+
+#ifndef __LZFINDMT_H
+#define __LZFINDMT_H
+
+#include "Threads.h"
+#include "LzFind.h"
+
+#define kMtHashBlockSize (1 << 13)
+#define kMtHashNumBlocks (1 << 3)
+#define kMtHashNumBlocksMask (kMtHashNumBlocks - 1)
+
+#define kMtBtBlockSize (1 << 14)
+#define kMtBtNumBlocks (1 << 6)
+#define kMtBtNumBlocksMask (kMtBtNumBlocks - 1)
+
+typedef struct _CMtSync
+{
+  Bool wasCreated;
+  Bool needStart;
+  Bool exit;
+  Bool stopWriting;
+
+  CThread thread;
+  CAutoResetEvent canStart;
+  CAutoResetEvent wasStarted;
+  CAutoResetEvent wasStopped;
+  CSemaphore freeSemaphore;
+  CSemaphore filledSemaphore;
+  Bool csWasInitialized;
+  Bool csWasEntered;
+  CCriticalSection cs;
+  UInt32 numProcessedBlocks;
+} CMtSync;
+
+typedef UInt32 * (*Mf_Mix_Matches)(void *p, UInt32 matchMinPos, UInt32 *distances);
+
+/* kMtCacheLineDummy must be >= size_of_CPU_cache_line */
+#define kMtCacheLineDummy 128
+
+typedef void (*Mf_GetHeads)(const Byte *buffer, UInt32 pos,
+  UInt32 *hash, UInt32 hashMask, UInt32 *heads, UInt32 numHeads, const UInt32 *crc);
+
+typedef struct _CMatchFinderMt
+{
+  /* LZ */
+  const Byte *pointerToCurPos;
+  UInt32 *btBuf;
+  UInt32 btBufPos;
+  UInt32 btBufPosLimit;
+  UInt32 lzPos;
+  UInt32 btNumAvailBytes;
+
+  UInt32 *hash;
+  UInt32 fixedHashSize;
+  UInt32 historySize;
+  const UInt32 *crc;
+
+  Mf_Mix_Matches MixMatchesFunc;
+  
+  /* LZ + BT */
+  CMtSync btSync;
+  Byte btDummy[kMtCacheLineDummy];
+
+  /* BT */
+  UInt32 *hashBuf;
+  UInt32 hashBufPos;
+  UInt32 hashBufPosLimit;
+  UInt32 hashNumAvail;
+
+  CLzRef *son;
+  UInt32 matchMaxLen;
+  UInt32 numHashBytes;
+  UInt32 pos;
+  Byte *buffer;
+  UInt32 cyclicBufferPos;
+  UInt32 cyclicBufferSize; /* it must be historySize + 1 */
+  UInt32 cutValue;
+
+  /* BT + Hash */
+  CMtSync hashSync;
+  /* Byte hashDummy[kMtCacheLineDummy]; */
+  
+  /* Hash */
+  Mf_GetHeads GetHeadsFunc;
+  CMatchFinder *MatchFinder;
+} CMatchFinderMt;
+
+void MatchFinderMt_Construct(CMatchFinderMt *p);
+void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAlloc *alloc);
+SRes MatchFinderMt_Create(CMatchFinderMt *p, UInt32 historySize, UInt32 keepAddBufferBefore,
+    UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc);
+void MatchFinderMt_CreateVTable(CMatchFinderMt *p, IMatchFinder *vTable);
+void MatchFinderMt_ReleaseStream(CMatchFinderMt *p);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/LzHash.h b/third_party/lzma/v4_65/files/C/LzHash.h
new file mode 100644
index 0000000..9f4173e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzHash.h
@@ -0,0 +1,54 @@
+/* LzHash.h -- HASH functions for LZ algorithms
+2008-10-04 : Igor Pavlov : Public domain */
+
+#ifndef __LZHASH_H
+#define __LZHASH_H
+
+#define kHash2Size (1 << 10)
+#define kHash3Size (1 << 16)
+#define kHash4Size (1 << 20)
+
+#define kFix3HashSize (kHash2Size)
+#define kFix4HashSize (kHash2Size + kHash3Size)
+#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size)
+
+#define HASH2_CALC hashValue = cur[0] | ((UInt32)cur[1] << 8);
+
+#define HASH3_CALC { \
+  UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
+  hash2Value = temp & (kHash2Size - 1); \
+  hashValue = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; }
+
+#define HASH4_CALC { \
+  UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
+  hash2Value = temp & (kHash2Size - 1); \
+  hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \
+  hashValue = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; }
+
+#define HASH5_CALC { \
+  UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
+  hash2Value = temp & (kHash2Size - 1); \
+  hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \
+  hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \
+  hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \
+  hash4Value &= (kHash4Size - 1); }
+
+/* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */
+#define HASH_ZIP_CALC hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF;
+
+
+#define MT_HASH2_CALC \
+  hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1);
+
+#define MT_HASH3_CALC { \
+  UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
+  hash2Value = temp & (kHash2Size - 1); \
+  hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); }
+
+#define MT_HASH4_CALC { \
+  UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
+  hash2Value = temp & (kHash2Size - 1); \
+  hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \
+  hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); }
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/LzmaDec.c b/third_party/lzma/v4_65/files/C/LzmaDec.c
new file mode 100644
index 0000000..d87eb19
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaDec.c
@@ -0,0 +1,1007 @@
+/* LzmaDec.c -- LZMA Decoder
+2008-11-06 : Igor Pavlov : Public domain */
+
+#include "LzmaDec.h"
+
+#include <string.h>
+
+#define kNumTopBits 24
+#define kTopValue ((UInt32)1 << kNumTopBits)
+
+#define kNumBitModelTotalBits 11
+#define kBitModelTotal (1 << kNumBitModelTotalBits)
+#define kNumMoveBits 5
+
+#define RC_INIT_SIZE 5
+
+#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); }
+
+#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
+#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits));
+#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits));
+#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \
+  { UPDATE_0(p); i = (i + i); A0; } else \
+  { UPDATE_1(p); i = (i + i) + 1; A1; }
+#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;)
+
+#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); }
+#define TREE_DECODE(probs, limit, i) \
+  { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; }
+
+/* #define _LZMA_SIZE_OPT */
+
+#ifdef _LZMA_SIZE_OPT
+#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i)
+#else
+#define TREE_6_DECODE(probs, i) \
+  { i = 1; \
+  TREE_GET_BIT(probs, i); \
+  TREE_GET_BIT(probs, i); \
+  TREE_GET_BIT(probs, i); \
+  TREE_GET_BIT(probs, i); \
+  TREE_GET_BIT(probs, i); \
+  TREE_GET_BIT(probs, i); \
+  i -= 0x40; }
+#endif
+
+#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); }
+
+#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
+#define UPDATE_0_CHECK range = bound;
+#define UPDATE_1_CHECK range -= bound; code -= bound;
+#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \
+  { UPDATE_0_CHECK; i = (i + i); A0; } else \
+  { UPDATE_1_CHECK; i = (i + i) + 1; A1; }
+#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;)
+#define TREE_DECODE_CHECK(probs, limit, i) \
+  { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; }
+
+
+#define kNumPosBitsMax 4
+#define kNumPosStatesMax (1 << kNumPosBitsMax)
+
+#define kLenNumLowBits 3
+#define kLenNumLowSymbols (1 << kLenNumLowBits)
+#define kLenNumMidBits 3
+#define kLenNumMidSymbols (1 << kLenNumMidBits)
+#define kLenNumHighBits 8
+#define kLenNumHighSymbols (1 << kLenNumHighBits)
+
+#define LenChoice 0
+#define LenChoice2 (LenChoice + 1)
+#define LenLow (LenChoice2 + 1)
+#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
+#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
+#define kNumLenProbs (LenHigh + kLenNumHighSymbols)
+
+
+#define kNumStates 12
+#define kNumLitStates 7
+
+#define kStartPosModelIndex 4
+#define kEndPosModelIndex 14
+#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
+
+#define kNumPosSlotBits 6
+#define kNumLenToPosStates 4
+
+#define kNumAlignBits 4
+#define kAlignTableSize (1 << kNumAlignBits)
+
+#define kMatchMinLen 2
+#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols)
+
+#define IsMatch 0
+#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
+#define IsRepG0 (IsRep + kNumStates)
+#define IsRepG1 (IsRepG0 + kNumStates)
+#define IsRepG2 (IsRepG1 + kNumStates)
+#define IsRep0Long (IsRepG2 + kNumStates)
+#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
+#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
+#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
+#define LenCoder (Align + kAlignTableSize)
+#define RepLenCoder (LenCoder + kNumLenProbs)
+#define Literal (RepLenCoder + kNumLenProbs)
+
+#define LZMA_BASE_SIZE 1846
+#define LZMA_LIT_SIZE 768
+
+#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp)))
+
+#if Literal != LZMA_BASE_SIZE
+StopCompilingDueBUG
+#endif
+
+static const Byte kLiteralNextStates[kNumStates * 2] =
+{
+  0, 0, 0, 0, 1, 2, 3,  4,  5,  6,  4,  5,
+  7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10
+};
+
+#define LZMA_DIC_MIN (1 << 12)
+
+/* First LZMA-symbol is always decoded.
+And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization
+Out:
+  Result:
+    SZ_OK - OK
+    SZ_ERROR_DATA - Error
+  p->remainLen:
+    < kMatchSpecLenStart : normal remain
+    = kMatchSpecLenStart : finished
+    = kMatchSpecLenStart + 1 : Flush marker
+    = kMatchSpecLenStart + 2 : State Init Marker
+*/
+
+static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit)
+{
+  CLzmaProb *probs = p->probs;
+
+  unsigned state = p->state;
+  UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3];
+  unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1;
+  unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1;
+  unsigned lc = p->prop.lc;
+
+  Byte *dic = p->dic;
+  SizeT dicBufSize = p->dicBufSize;
+  SizeT dicPos = p->dicPos;
+  
+  UInt32 processedPos = p->processedPos;
+  UInt32 checkDicSize = p->checkDicSize;
+  unsigned len = 0;
+
+  const Byte *buf = p->buf;
+  UInt32 range = p->range;
+  UInt32 code = p->code;
+
+  do
+  {
+    CLzmaProb *prob;
+    UInt32 bound;
+    unsigned ttt;
+    unsigned posState = processedPos & pbMask;
+
+    prob = probs + IsMatch + (state << kNumPosBitsMax) + posState;
+    IF_BIT_0(prob)
+    {
+      unsigned symbol;
+      UPDATE_0(prob);
+      prob = probs + Literal;
+      if (checkDicSize != 0 || processedPos != 0)
+        prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) +
+        (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc))));
+
+      if (state < kNumLitStates)
+      {
+        symbol = 1;
+        do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100);
+      }
+      else
+      {
+        unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)];
+        unsigned offs = 0x100;
+        symbol = 1;
+        do
+        {
+          unsigned bit;
+          CLzmaProb *probLit;
+          matchByte <<= 1;
+          bit = (matchByte & offs);
+          probLit = prob + offs + bit + symbol;
+          GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit)
+        }
+        while (symbol < 0x100);
+      }
+      dic[dicPos++] = (Byte)symbol;
+      processedPos++;
+
+      state = kLiteralNextStates[state];
+      /* if (state < 4) state = 0; else if (state < 10) state -= 3; else state -= 6; */
+      continue;
+    }
+    else
+    {
+      UPDATE_1(prob);
+      prob = probs + IsRep + state;
+      IF_BIT_0(prob)
+      {
+        UPDATE_0(prob);
+        state += kNumStates;
+        prob = probs + LenCoder;
+      }
+      else
+      {
+        UPDATE_1(prob);
+        if (checkDicSize == 0 && processedPos == 0)
+          return SZ_ERROR_DATA;
+        prob = probs + IsRepG0 + state;
+        IF_BIT_0(prob)
+        {
+          UPDATE_0(prob);
+          prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState;
+          IF_BIT_0(prob)
+          {
+            UPDATE_0(prob);
+            dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)];
+            dicPos++;
+            processedPos++;
+            state = state < kNumLitStates ? 9 : 11;
+            continue;
+          }
+          UPDATE_1(prob);
+        }
+        else
+        {
+          UInt32 distance;
+          UPDATE_1(prob);
+          prob = probs + IsRepG1 + state;
+          IF_BIT_0(prob)
+          {
+            UPDATE_0(prob);
+            distance = rep1;
+          }
+          else
+          {
+            UPDATE_1(prob);
+            prob = probs + IsRepG2 + state;
+            IF_BIT_0(prob)
+            {
+              UPDATE_0(prob);
+              distance = rep2;
+            }
+            else
+            {
+              UPDATE_1(prob);
+              distance = rep3;
+              rep3 = rep2;
+            }
+            rep2 = rep1;
+          }
+          rep1 = rep0;
+          rep0 = distance;
+        }
+        state = state < kNumLitStates ? 8 : 11;
+        prob = probs + RepLenCoder;
+      }
+      {
+        unsigned limit, offset;
+        CLzmaProb *probLen = prob + LenChoice;
+        IF_BIT_0(probLen)
+        {
+          UPDATE_0(probLen);
+          probLen = prob + LenLow + (posState << kLenNumLowBits);
+          offset = 0;
+          limit = (1 << kLenNumLowBits);
+        }
+        else
+        {
+          UPDATE_1(probLen);
+          probLen = prob + LenChoice2;
+          IF_BIT_0(probLen)
+          {
+            UPDATE_0(probLen);
+            probLen = prob + LenMid + (posState << kLenNumMidBits);
+            offset = kLenNumLowSymbols;
+            limit = (1 << kLenNumMidBits);
+          }
+          else
+          {
+            UPDATE_1(probLen);
+            probLen = prob + LenHigh;
+            offset = kLenNumLowSymbols + kLenNumMidSymbols;
+            limit = (1 << kLenNumHighBits);
+          }
+        }
+        TREE_DECODE(probLen, limit, len);
+        len += offset;
+      }
+
+      if (state >= kNumStates)
+      {
+        UInt32 distance;
+        prob = probs + PosSlot +
+            ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits);
+        TREE_6_DECODE(prob, distance);
+        if (distance >= kStartPosModelIndex)
+        {
+          unsigned posSlot = (unsigned)distance;
+          int numDirectBits = (int)(((distance >> 1) - 1));
+          distance = (2 | (distance & 1));
+          if (posSlot < kEndPosModelIndex)
+          {
+            distance <<= numDirectBits;
+            prob = probs + SpecPos + distance - posSlot - 1;
+            {
+              UInt32 mask = 1;
+              unsigned i = 1;
+              do
+              {
+                GET_BIT2(prob + i, i, ; , distance |= mask);
+                mask <<= 1;
+              }
+              while (--numDirectBits != 0);
+            }
+          }
+          else
+          {
+            numDirectBits -= kNumAlignBits;
+            do
+            {
+              NORMALIZE
+              range >>= 1;
+              
+              {
+                UInt32 t;
+                code -= range;
+                t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */
+                distance = (distance << 1) + (t + 1);
+                code += range & t;
+              }
+              /*
+              distance <<= 1;
+              if (code >= range)
+              {
+                code -= range;
+                distance |= 1;
+              }
+              */
+            }
+            while (--numDirectBits != 0);
+            prob = probs + Align;
+            distance <<= kNumAlignBits;
+            {
+              unsigned i = 1;
+              GET_BIT2(prob + i, i, ; , distance |= 1);
+              GET_BIT2(prob + i, i, ; , distance |= 2);
+              GET_BIT2(prob + i, i, ; , distance |= 4);
+              GET_BIT2(prob + i, i, ; , distance |= 8);
+            }
+            if (distance == (UInt32)0xFFFFFFFF)
+            {
+              len += kMatchSpecLenStart;
+              state -= kNumStates;
+              break;
+            }
+          }
+        }
+        rep3 = rep2;
+        rep2 = rep1;
+        rep1 = rep0;
+        rep0 = distance + 1;
+        if (checkDicSize == 0)
+        {
+          if (distance >= processedPos)
+            return SZ_ERROR_DATA;
+        }
+        else if (distance >= checkDicSize)
+          return SZ_ERROR_DATA;
+        state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3;
+        /* state = kLiteralNextStates[state]; */
+      }
+
+      len += kMatchMinLen;
+
+      if (limit == dicPos)
+        return SZ_ERROR_DATA;
+      {
+        SizeT rem = limit - dicPos;
+        unsigned curLen = ((rem < len) ? (unsigned)rem : len);
+        SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0);
+
+        processedPos += curLen;
+
+        len -= curLen;
+        if (pos + curLen <= dicBufSize)
+        {
+          Byte *dest = dic + dicPos;
+          ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos;
+          const Byte *lim = dest + curLen;
+          dicPos += curLen;
+          do
+            *(dest) = (Byte)*(dest + src);
+          while (++dest != lim);
+        }
+        else
+        {
+          do
+          {
+            dic[dicPos++] = dic[pos];
+            if (++pos == dicBufSize)
+              pos = 0;
+          }
+          while (--curLen != 0);
+        }
+      }
+    }
+  }
+  while (dicPos < limit && buf < bufLimit);
+  NORMALIZE;
+  p->buf = buf;
+  p->range = range;
+  p->code = code;
+  p->remainLen = len;
+  p->dicPos = dicPos;
+  p->processedPos = processedPos;
+  p->reps[0] = rep0;
+  p->reps[1] = rep1;
+  p->reps[2] = rep2;
+  p->reps[3] = rep3;
+  p->state = state;
+
+  return SZ_OK;
+}
+
+static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit)
+{
+  if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart)
+  {
+    Byte *dic = p->dic;
+    SizeT dicPos = p->dicPos;
+    SizeT dicBufSize = p->dicBufSize;
+    unsigned len = p->remainLen;
+    UInt32 rep0 = p->reps[0];
+    if (limit - dicPos < len)
+      len = (unsigned)(limit - dicPos);
+
+    if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len)
+      p->checkDicSize = p->prop.dicSize;
+
+    p->processedPos += len;
+    p->remainLen -= len;
+    while (len-- != 0)
+    {
+      dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)];
+      dicPos++;
+    }
+    p->dicPos = dicPos;
+  }
+}
+
+static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit)
+{
+  do
+  {
+    SizeT limit2 = limit;
+    if (p->checkDicSize == 0)
+    {
+      UInt32 rem = p->prop.dicSize - p->processedPos;
+      if (limit - p->dicPos > rem)
+        limit2 = p->dicPos + rem;
+    }
+    RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit));
+    if (p->processedPos >= p->prop.dicSize)
+      p->checkDicSize = p->prop.dicSize;
+    LzmaDec_WriteRem(p, limit);
+  }
+  while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart);
+
+  if (p->remainLen > kMatchSpecLenStart)
+  {
+    p->remainLen = kMatchSpecLenStart;
+  }
+  return 0;
+}
+
+typedef enum
+{
+  DUMMY_ERROR, /* unexpected end of input stream */
+  DUMMY_LIT,
+  DUMMY_MATCH,
+  DUMMY_REP
+} ELzmaDummy;
+
+static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize)
+{
+  UInt32 range = p->range;
+  UInt32 code = p->code;
+  const Byte *bufLimit = buf + inSize;
+  CLzmaProb *probs = p->probs;
+  unsigned state = p->state;
+  ELzmaDummy res;
+
+  {
+    CLzmaProb *prob;
+    UInt32 bound;
+    unsigned ttt;
+    unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1);
+
+    prob = probs + IsMatch + (state << kNumPosBitsMax) + posState;
+    IF_BIT_0_CHECK(prob)
+    {
+      UPDATE_0_CHECK
+
+      /* if (bufLimit - buf >= 7) return DUMMY_LIT; */
+
+      prob = probs + Literal;
+      if (p->checkDicSize != 0 || p->processedPos != 0)
+        prob += (LZMA_LIT_SIZE *
+          ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) +
+          (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc))));
+
+      if (state < kNumLitStates)
+      {
+        unsigned symbol = 1;
+        do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100);
+      }
+      else
+      {
+        unsigned matchByte = p->dic[p->dicPos - p->reps[0] +
+            ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)];
+        unsigned offs = 0x100;
+        unsigned symbol = 1;
+        do
+        {
+          unsigned bit;
+          CLzmaProb *probLit;
+          matchByte <<= 1;
+          bit = (matchByte & offs);
+          probLit = prob + offs + bit + symbol;
+          GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit)
+        }
+        while (symbol < 0x100);
+      }
+      res = DUMMY_LIT;
+    }
+    else
+    {
+      unsigned len;
+      UPDATE_1_CHECK;
+
+      prob = probs + IsRep + state;
+      IF_BIT_0_CHECK(prob)
+      {
+        UPDATE_0_CHECK;
+        state = 0;
+        prob = probs + LenCoder;
+        res = DUMMY_MATCH;
+      }
+      else
+      {
+        UPDATE_1_CHECK;
+        res = DUMMY_REP;
+        prob = probs + IsRepG0 + state;
+        IF_BIT_0_CHECK(prob)
+        {
+          UPDATE_0_CHECK;
+          prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState;
+          IF_BIT_0_CHECK(prob)
+          {
+            UPDATE_0_CHECK;
+            NORMALIZE_CHECK;
+            return DUMMY_REP;
+          }
+          else
+          {
+            UPDATE_1_CHECK;
+          }
+        }
+        else
+        {
+          UPDATE_1_CHECK;
+          prob = probs + IsRepG1 + state;
+          IF_BIT_0_CHECK(prob)
+          {
+            UPDATE_0_CHECK;
+          }
+          else
+          {
+            UPDATE_1_CHECK;
+            prob = probs + IsRepG2 + state;
+            IF_BIT_0_CHECK(prob)
+            {
+              UPDATE_0_CHECK;
+            }
+            else
+            {
+              UPDATE_1_CHECK;
+            }
+          }
+        }
+        state = kNumStates;
+        prob = probs + RepLenCoder;
+      }
+      {
+        unsigned limit, offset;
+        CLzmaProb *probLen = prob + LenChoice;
+        IF_BIT_0_CHECK(probLen)
+        {
+          UPDATE_0_CHECK;
+          probLen = prob + LenLow + (posState << kLenNumLowBits);
+          offset = 0;
+          limit = 1 << kLenNumLowBits;
+        }
+        else
+        {
+          UPDATE_1_CHECK;
+          probLen = prob + LenChoice2;
+          IF_BIT_0_CHECK(probLen)
+          {
+            UPDATE_0_CHECK;
+            probLen = prob + LenMid + (posState << kLenNumMidBits);
+            offset = kLenNumLowSymbols;
+            limit = 1 << kLenNumMidBits;
+          }
+          else
+          {
+            UPDATE_1_CHECK;
+            probLen = prob + LenHigh;
+            offset = kLenNumLowSymbols + kLenNumMidSymbols;
+            limit = 1 << kLenNumHighBits;
+          }
+        }
+        TREE_DECODE_CHECK(probLen, limit, len);
+        len += offset;
+      }
+
+      if (state < 4)
+      {
+        unsigned posSlot;
+        prob = probs + PosSlot +
+            ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) <<
+            kNumPosSlotBits);
+        TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot);
+        if (posSlot >= kStartPosModelIndex)
+        {
+          int numDirectBits = ((posSlot >> 1) - 1);
+
+          /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */
+
+          if (posSlot < kEndPosModelIndex)
+          {
+            prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1;
+          }
+          else
+          {
+            numDirectBits -= kNumAlignBits;
+            do
+            {
+              NORMALIZE_CHECK
+              range >>= 1;
+              code -= range & (((code - range) >> 31) - 1);
+              /* if (code >= range) code -= range; */
+            }
+            while (--numDirectBits != 0);
+            prob = probs + Align;
+            numDirectBits = kNumAlignBits;
+          }
+          {
+            unsigned i = 1;
+            do
+            {
+              GET_BIT_CHECK(prob + i, i);
+            }
+            while (--numDirectBits != 0);
+          }
+        }
+      }
+    }
+  }
+  NORMALIZE_CHECK;
+  return res;
+}
+
+
+static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data)
+{
+  p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]);
+  p->range = 0xFFFFFFFF;
+  p->needFlush = 0;
+}
+
+void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState)
+{
+  p->needFlush = 1;
+  p->remainLen = 0;
+  p->tempBufSize = 0;
+
+  if (initDic)
+  {
+    p->processedPos = 0;
+    p->checkDicSize = 0;
+    p->needInitState = 1;
+  }
+  if (initState)
+    p->needInitState = 1;
+}
+
+void LzmaDec_Init(CLzmaDec *p)
+{
+  p->dicPos = 0;
+  LzmaDec_InitDicAndState(p, True, True);
+}
+
+static void LzmaDec_InitStateReal(CLzmaDec *p)
+{
+  UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp));
+  UInt32 i;
+  CLzmaProb *probs = p->probs;
+  for (i = 0; i < numProbs; i++)
+    probs[i] = kBitModelTotal >> 1;
+  p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1;
+  p->state = 0;
+  p->needInitState = 0;
+}
+
+SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen,
+    ELzmaFinishMode finishMode, ELzmaStatus *status)
+{
+  SizeT inSize = *srcLen;
+  (*srcLen) = 0;
+  LzmaDec_WriteRem(p, dicLimit);
+  
+  *status = LZMA_STATUS_NOT_SPECIFIED;
+
+  while (p->remainLen != kMatchSpecLenStart)
+  {
+      int checkEndMarkNow;
+
+      if (p->needFlush != 0)
+      {
+        for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--)
+          p->tempBuf[p->tempBufSize++] = *src++;
+        if (p->tempBufSize < RC_INIT_SIZE)
+        {
+          *status = LZMA_STATUS_NEEDS_MORE_INPUT;
+          return SZ_OK;
+        }
+        if (p->tempBuf[0] != 0)
+          return SZ_ERROR_DATA;
+
+        LzmaDec_InitRc(p, p->tempBuf);
+        p->tempBufSize = 0;
+      }
+
+      checkEndMarkNow = 0;
+      if (p->dicPos >= dicLimit)
+      {
+        if (p->remainLen == 0 && p->code == 0)
+        {
+          *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK;
+          return SZ_OK;
+        }
+        if (finishMode == LZMA_FINISH_ANY)
+        {
+          *status = LZMA_STATUS_NOT_FINISHED;
+          return SZ_OK;
+        }
+        if (p->remainLen != 0)
+        {
+          *status = LZMA_STATUS_NOT_FINISHED;
+          return SZ_ERROR_DATA;
+        }
+        checkEndMarkNow = 1;
+      }
+
+      if (p->needInitState)
+        LzmaDec_InitStateReal(p);
+  
+      if (p->tempBufSize == 0)
+      {
+        SizeT processed;
+        const Byte *bufLimit;
+        if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow)
+        {
+          int dummyRes = LzmaDec_TryDummy(p, src, inSize);
+          if (dummyRes == DUMMY_ERROR)
+          {
+            memcpy(p->tempBuf, src, inSize);
+            p->tempBufSize = (unsigned)inSize;
+            (*srcLen) += inSize;
+            *status = LZMA_STATUS_NEEDS_MORE_INPUT;
+            return SZ_OK;
+          }
+          if (checkEndMarkNow && dummyRes != DUMMY_MATCH)
+          {
+            *status = LZMA_STATUS_NOT_FINISHED;
+            return SZ_ERROR_DATA;
+          }
+          bufLimit = src;
+        }
+        else
+          bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX;
+        p->buf = src;
+        if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0)
+          return SZ_ERROR_DATA;
+        processed = (SizeT)(p->buf - src);
+        (*srcLen) += processed;
+        src += processed;
+        inSize -= processed;
+      }
+      else
+      {
+        unsigned rem = p->tempBufSize, lookAhead = 0;
+        while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize)
+          p->tempBuf[rem++] = src[lookAhead++];
+        p->tempBufSize = rem;
+        if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow)
+        {
+          int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem);
+          if (dummyRes == DUMMY_ERROR)
+          {
+            (*srcLen) += lookAhead;
+            *status = LZMA_STATUS_NEEDS_MORE_INPUT;
+            return SZ_OK;
+          }
+          if (checkEndMarkNow && dummyRes != DUMMY_MATCH)
+          {
+            *status = LZMA_STATUS_NOT_FINISHED;
+            return SZ_ERROR_DATA;
+          }
+        }
+        p->buf = p->tempBuf;
+        if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0)
+          return SZ_ERROR_DATA;
+        lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf));
+        (*srcLen) += lookAhead;
+        src += lookAhead;
+        inSize -= lookAhead;
+        p->tempBufSize = 0;
+      }
+  }
+  if (p->code == 0)
+    *status = LZMA_STATUS_FINISHED_WITH_MARK;
+  return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA;
+}
+
+SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status)
+{
+  SizeT outSize = *destLen;
+  SizeT inSize = *srcLen;
+  *srcLen = *destLen = 0;
+  for (;;)
+  {
+    SizeT inSizeCur = inSize, outSizeCur, dicPos;
+    ELzmaFinishMode curFinishMode;
+    SRes res;
+    if (p->dicPos == p->dicBufSize)
+      p->dicPos = 0;
+    dicPos = p->dicPos;
+    if (outSize > p->dicBufSize - dicPos)
+    {
+      outSizeCur = p->dicBufSize;
+      curFinishMode = LZMA_FINISH_ANY;
+    }
+    else
+    {
+      outSizeCur = dicPos + outSize;
+      curFinishMode = finishMode;
+    }
+
+    res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status);
+    src += inSizeCur;
+    inSize -= inSizeCur;
+    *srcLen += inSizeCur;
+    outSizeCur = p->dicPos - dicPos;
+    memcpy(dest, p->dic + dicPos, outSizeCur);
+    dest += outSizeCur;
+    outSize -= outSizeCur;
+    *destLen += outSizeCur;
+    if (res != 0)
+      return res;
+    if (outSizeCur == 0 || outSize == 0)
+      return SZ_OK;
+  }
+}
+
+void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc)
+{
+  alloc->Free(alloc, p->probs);
+  p->probs = 0;
+}
+
+static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc)
+{
+  alloc->Free(alloc, p->dic);
+  p->dic = 0;
+}
+
+void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc)
+{
+  LzmaDec_FreeProbs(p, alloc);
+  LzmaDec_FreeDict(p, alloc);
+}
+
+SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size)
+{
+  UInt32 dicSize;
+  Byte d;
+  
+  if (size < LZMA_PROPS_SIZE)
+    return SZ_ERROR_UNSUPPORTED;
+  else
+    dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24);
+ 
+  if (dicSize < LZMA_DIC_MIN)
+    dicSize = LZMA_DIC_MIN;
+  p->dicSize = dicSize;
+
+  d = data[0];
+  if (d >= (9 * 5 * 5))
+    return SZ_ERROR_UNSUPPORTED;
+
+  p->lc = d % 9;
+  d /= 9;
+  p->pb = d / 5;
+  p->lp = d % 5;
+
+  return SZ_OK;
+}
+
+static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc)
+{
+  UInt32 numProbs = LzmaProps_GetNumProbs(propNew);
+  if (p->probs == 0 || numProbs != p->numProbs)
+  {
+    LzmaDec_FreeProbs(p, alloc);
+    p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb));
+    p->numProbs = numProbs;
+    if (p->probs == 0)
+      return SZ_ERROR_MEM;
+  }
+  return SZ_OK;
+}
+
+SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
+{
+  CLzmaProps propNew;
+  RINOK(LzmaProps_Decode(&propNew, props, propsSize));
+  RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
+  p->prop = propNew;
+  return SZ_OK;
+}
+
+SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
+{
+  CLzmaProps propNew;
+  SizeT dicBufSize;
+  RINOK(LzmaProps_Decode(&propNew, props, propsSize));
+  RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
+  dicBufSize = propNew.dicSize;
+  if (p->dic == 0 || dicBufSize != p->dicBufSize)
+  {
+    LzmaDec_FreeDict(p, alloc);
+    p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize);
+    if (p->dic == 0)
+    {
+      LzmaDec_FreeProbs(p, alloc);
+      return SZ_ERROR_MEM;
+    }
+  }
+  p->dicBufSize = dicBufSize;
+  p->prop = propNew;
+  return SZ_OK;
+}
+
+SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
+    const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
+    ELzmaStatus *status, ISzAlloc *alloc)
+{
+  CLzmaDec p;
+  SRes res;
+  SizeT inSize = *srcLen;
+  SizeT outSize = *destLen;
+  *srcLen = *destLen = 0;
+  if (inSize < RC_INIT_SIZE)
+    return SZ_ERROR_INPUT_EOF;
+
+  LzmaDec_Construct(&p);
+  res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc);
+  if (res != 0)
+    return res;
+  p.dic = dest;
+  p.dicBufSize = outSize;
+
+  LzmaDec_Init(&p);
+  
+  *srcLen = inSize;
+  res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status);
+
+  if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT)
+    res = SZ_ERROR_INPUT_EOF;
+
+  (*destLen) = p.dicPos;
+  LzmaDec_FreeProbs(&p, alloc);
+  return res;
+}
diff --git a/third_party/lzma/v4_65/files/C/LzmaDec.h b/third_party/lzma/v4_65/files/C/LzmaDec.h
new file mode 100644
index 0000000..98cdbe9
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaDec.h
@@ -0,0 +1,223 @@
+/* LzmaDec.h -- LZMA Decoder
+2008-10-04 : Igor Pavlov : Public domain */
+
+#ifndef __LZMADEC_H
+#define __LZMADEC_H
+
+#include "Types.h"
+
+/* #define _LZMA_PROB32 */
+/* _LZMA_PROB32 can increase the speed on some CPUs,
+   but memory usage for CLzmaDec::probs will be doubled in that case */
+
+#ifdef _LZMA_PROB32
+#define CLzmaProb UInt32
+#else
+#define CLzmaProb UInt16
+#endif
+
+
+/* ---------- LZMA Properties ---------- */
+
+#define LZMA_PROPS_SIZE 5
+
+typedef struct _CLzmaProps
+{
+  unsigned lc, lp, pb;
+  UInt32 dicSize;
+} CLzmaProps;
+
+/* LzmaProps_Decode - decodes properties
+Returns:
+  SZ_OK
+  SZ_ERROR_UNSUPPORTED - Unsupported properties
+*/
+
+SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size);
+
+
+/* ---------- LZMA Decoder state ---------- */
+
+/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case.
+   Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */
+
+#define LZMA_REQUIRED_INPUT_MAX 20
+
+typedef struct
+{
+  CLzmaProps prop;
+  CLzmaProb *probs;
+  Byte *dic;
+  const Byte *buf;
+  UInt32 range, code;
+  SizeT dicPos;
+  SizeT dicBufSize;
+  UInt32 processedPos;
+  UInt32 checkDicSize;
+  unsigned state;
+  UInt32 reps[4];
+  unsigned remainLen;
+  int needFlush;
+  int needInitState;
+  UInt32 numProbs;
+  unsigned tempBufSize;
+  Byte tempBuf[LZMA_REQUIRED_INPUT_MAX];
+} CLzmaDec;
+
+#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; }
+
+void LzmaDec_Init(CLzmaDec *p);
+
+/* There are two types of LZMA streams:
+     0) Stream with end mark. That end mark adds about 6 bytes to compressed size.
+     1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */
+
+typedef enum
+{
+  LZMA_FINISH_ANY,   /* finish at any point */
+  LZMA_FINISH_END    /* block must be finished at the end */
+} ELzmaFinishMode;
+
+/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!!
+
+   You must use LZMA_FINISH_END, when you know that current output buffer
+   covers last bytes of block. In other cases you must use LZMA_FINISH_ANY.
+
+   If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK,
+   and output value of destLen will be less than output buffer size limit.
+   You can check status result also.
+
+   You can use multiple checks to test data integrity after full decompression:
+     1) Check Result and "status" variable.
+     2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize.
+     3) Check that output(srcLen) = compressedSize, if you know real compressedSize.
+        You must use correct finish mode in that case. */
+
+typedef enum
+{
+  LZMA_STATUS_NOT_SPECIFIED,               /* use main error code instead */
+  LZMA_STATUS_FINISHED_WITH_MARK,          /* stream was finished with end mark. */
+  LZMA_STATUS_NOT_FINISHED,                /* stream was not finished */
+  LZMA_STATUS_NEEDS_MORE_INPUT,            /* you must provide more input bytes */
+  LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK  /* there is probability that stream was finished without end mark */
+} ELzmaStatus;
+
+/* ELzmaStatus is used only as output value for function call */
+
+
+/* ---------- Interfaces ---------- */
+
+/* There are 3 levels of interfaces:
+     1) Dictionary Interface
+     2) Buffer Interface
+     3) One Call Interface
+   You can select any of these interfaces, but don't mix functions from different
+   groups for same object. */
+
+
+/* There are two variants to allocate state for Dictionary Interface:
+     1) LzmaDec_Allocate / LzmaDec_Free
+     2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs
+   You can use variant 2, if you set dictionary buffer manually.
+   For Buffer Interface you must always use variant 1.
+
+LzmaDec_Allocate* can return:
+  SZ_OK
+  SZ_ERROR_MEM         - Memory allocation error
+  SZ_ERROR_UNSUPPORTED - Unsupported properties
+*/
+   
+SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc);
+void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc);
+
+SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc);
+void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc);
+
+/* ---------- Dictionary Interface ---------- */
+
+/* You can use it, if you want to eliminate the overhead for data copying from
+   dictionary to some other external buffer.
+   You must work with CLzmaDec variables directly in this interface.
+
+   STEPS:
+     LzmaDec_Constr()
+     LzmaDec_Allocate()
+     for (each new stream)
+     {
+       LzmaDec_Init()
+       while (it needs more decompression)
+       {
+         LzmaDec_DecodeToDic()
+         use data from CLzmaDec::dic and update CLzmaDec::dicPos
+       }
+     }
+     LzmaDec_Free()
+*/
+
+/* LzmaDec_DecodeToDic
+   
+   The decoding to internal dictionary buffer (CLzmaDec::dic).
+   You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!!
+
+finishMode:
+  It has meaning only if the decoding reaches output limit (dicLimit).
+  LZMA_FINISH_ANY - Decode just dicLimit bytes.
+  LZMA_FINISH_END - Stream must be finished after dicLimit.
+
+Returns:
+  SZ_OK
+    status:
+      LZMA_STATUS_FINISHED_WITH_MARK
+      LZMA_STATUS_NOT_FINISHED
+      LZMA_STATUS_NEEDS_MORE_INPUT
+      LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
+  SZ_ERROR_DATA - Data error
+*/
+
+SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit,
+    const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
+
+
+/* ---------- Buffer Interface ---------- */
+
+/* It's zlib-like interface.
+   See LzmaDec_DecodeToDic description for information about STEPS and return results,
+   but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need
+   to work with CLzmaDec variables manually.
+
+finishMode:
+  It has meaning only if the decoding reaches output limit (*destLen).
+  LZMA_FINISH_ANY - Decode just destLen bytes.
+  LZMA_FINISH_END - Stream must be finished after (*destLen).
+*/
+
+SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen,
+    const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
+
+
+/* ---------- One Call Interface ---------- */
+
+/* LzmaDecode
+
+finishMode:
+  It has meaning only if the decoding reaches output limit (*destLen).
+  LZMA_FINISH_ANY - Decode just destLen bytes.
+  LZMA_FINISH_END - Stream must be finished after (*destLen).
+
+Returns:
+  SZ_OK
+    status:
+      LZMA_STATUS_FINISHED_WITH_MARK
+      LZMA_STATUS_NOT_FINISHED
+      LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
+  SZ_ERROR_DATA - Data error
+  SZ_ERROR_MEM  - Memory allocation error
+  SZ_ERROR_UNSUPPORTED - Unsupported properties
+  SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src).
+*/
+
+SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
+    const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
+    ELzmaStatus *status, ISzAlloc *alloc);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/LzmaEnc.c b/third_party/lzma/v4_65/files/C/LzmaEnc.c
new file mode 100644
index 0000000..9196c43
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaEnc.c
@@ -0,0 +1,2281 @@
+/* LzmaEnc.c -- LZMA Encoder
+2009-02-02 : Igor Pavlov : Public domain */
+
+#include <string.h>
+
+/* #define SHOW_STAT */
+/* #define SHOW_STAT2 */
+
+#if defined(SHOW_STAT) || defined(SHOW_STAT2)
+#include <stdio.h>
+#endif
+
+#include "LzmaEnc.h"
+
+#include "LzFind.h"
+#ifdef COMPRESS_MF_MT
+#include "LzFindMt.h"
+#endif
+
+#ifdef SHOW_STAT
+static int ttt = 0;
+#endif
+
+#define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1)
+
+#define kBlockSize (9 << 10)
+#define kUnpackBlockSize (1 << 18)
+#define kMatchArraySize (1 << 21)
+#define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX)
+
+#define kNumMaxDirectBits (31)
+
+#define kNumTopBits 24
+#define kTopValue ((UInt32)1 << kNumTopBits)
+
+#define kNumBitModelTotalBits 11
+#define kBitModelTotal (1 << kNumBitModelTotalBits)
+#define kNumMoveBits 5
+#define kProbInitValue (kBitModelTotal >> 1)
+
+#define kNumMoveReducingBits 4
+#define kNumBitPriceShiftBits 4
+#define kBitPrice (1 << kNumBitPriceShiftBits)
+
+void LzmaEncProps_Init(CLzmaEncProps *p)
+{
+  p->level = 5;
+  p->dictSize = p->mc = 0;
+  p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1;
+  p->writeEndMark = 0;
+}
+
+void LzmaEncProps_Normalize(CLzmaEncProps *p)
+{
+  int level = p->level;
+  if (level < 0) level = 5;
+  p->level = level;
+  if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26)));
+  if (p->lc < 0) p->lc = 3;
+  if (p->lp < 0) p->lp = 0;
+  if (p->pb < 0) p->pb = 2;
+  if (p->algo < 0) p->algo = (level < 5 ? 0 : 1);
+  if (p->fb < 0) p->fb = (level < 7 ? 32 : 64);
+  if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1);
+  if (p->numHashBytes < 0) p->numHashBytes = 4;
+  if (p->mc == 0)  p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1);
+  if (p->numThreads < 0)
+    p->numThreads =
+      #ifdef COMPRESS_MF_MT
+      ((p->btMode && p->algo) ? 2 : 1);
+      #else
+      1;
+      #endif
+}
+
+UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2)
+{
+  CLzmaEncProps props = *props2;
+  LzmaEncProps_Normalize(&props);
+  return props.dictSize;
+}
+
+/* #define LZMA_LOG_BSR */
+/* Define it for Intel's CPU */
+
+
+#ifdef LZMA_LOG_BSR
+
+#define kDicLogSizeMaxCompress 30
+
+#define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); }
+
+UInt32 GetPosSlot1(UInt32 pos)
+{
+  UInt32 res;
+  BSR2_RET(pos, res);
+  return res;
+}
+#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); }
+#define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); }
+
+#else
+
+#define kNumLogBits (9 + (int)sizeof(size_t) / 2)
+#define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7)
+
+void LzmaEnc_FastPosInit(Byte *g_FastPos)
+{
+  int c = 2, slotFast;
+  g_FastPos[0] = 0;
+  g_FastPos[1] = 1;
+  
+  for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++)
+  {
+    UInt32 k = (1 << ((slotFast >> 1) - 1));
+    UInt32 j;
+    for (j = 0; j < k; j++, c++)
+      g_FastPos[c] = (Byte)slotFast;
+  }
+}
+
+#define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \
+  (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \
+  res = p->g_FastPos[pos >> i] + (i * 2); }
+/*
+#define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \
+  p->g_FastPos[pos >> 6] + 12 : \
+  p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; }
+*/
+
+#define GetPosSlot1(pos) p->g_FastPos[pos]
+#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); }
+#define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); }
+
+#endif
+
+
+#define LZMA_NUM_REPS 4
+
+typedef unsigned CState;
+
+typedef struct _COptimal
+{
+  UInt32 price;
+
+  CState state;
+  int prev1IsChar;
+  int prev2;
+
+  UInt32 posPrev2;
+  UInt32 backPrev2;
+
+  UInt32 posPrev;
+  UInt32 backPrev;
+  UInt32 backs[LZMA_NUM_REPS];
+} COptimal;
+
+#define kNumOpts (1 << 12)
+
+#define kNumLenToPosStates 4
+#define kNumPosSlotBits 6
+#define kDicLogSizeMin 0
+#define kDicLogSizeMax 32
+#define kDistTableSizeMax (kDicLogSizeMax * 2)
+
+
+#define kNumAlignBits 4
+#define kAlignTableSize (1 << kNumAlignBits)
+#define kAlignMask (kAlignTableSize - 1)
+
+#define kStartPosModelIndex 4
+#define kEndPosModelIndex 14
+#define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex)
+
+#define kNumFullDistances (1 << (kEndPosModelIndex / 2))
+
+#ifdef _LZMA_PROB32
+#define CLzmaProb UInt32
+#else
+#define CLzmaProb UInt16
+#endif
+
+#define LZMA_PB_MAX 4
+#define LZMA_LC_MAX 8
+#define LZMA_LP_MAX 4
+
+#define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX)
+
+
+#define kLenNumLowBits 3
+#define kLenNumLowSymbols (1 << kLenNumLowBits)
+#define kLenNumMidBits 3
+#define kLenNumMidSymbols (1 << kLenNumMidBits)
+#define kLenNumHighBits 8
+#define kLenNumHighSymbols (1 << kLenNumHighBits)
+
+#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols)
+
+#define LZMA_MATCH_LEN_MIN 2
+#define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1)
+
+#define kNumStates 12
+
+typedef struct
+{
+  CLzmaProb choice;
+  CLzmaProb choice2;
+  CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits];
+  CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits];
+  CLzmaProb high[kLenNumHighSymbols];
+} CLenEnc;
+
+typedef struct
+{
+  CLenEnc p;
+  UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal];
+  UInt32 tableSize;
+  UInt32 counters[LZMA_NUM_PB_STATES_MAX];
+} CLenPriceEnc;
+
+typedef struct _CRangeEnc
+{
+  UInt32 range;
+  Byte cache;
+  UInt64 low;
+  UInt64 cacheSize;
+  Byte *buf;
+  Byte *bufLim;
+  Byte *bufBase;
+  ISeqOutStream *outStream;
+  UInt64 processed;
+  SRes res;
+} CRangeEnc;
+
+typedef struct _CSeqInStreamBuf
+{
+  ISeqInStream funcTable;
+  const Byte *data;
+  SizeT rem;
+} CSeqInStreamBuf;
+
+static SRes MyRead(void *pp, void *data, size_t *size)
+{
+  size_t curSize = *size;
+  CSeqInStreamBuf *p = (CSeqInStreamBuf *)pp;
+  if (p->rem < curSize)
+    curSize = p->rem;
+  memcpy(data, p->data, curSize);
+  p->rem -= curSize;
+  p->data += curSize;
+  *size = curSize;
+  return SZ_OK;
+}
+
+typedef struct
+{
+  CLzmaProb *litProbs;
+
+  CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX];
+  CLzmaProb isRep[kNumStates];
+  CLzmaProb isRepG0[kNumStates];
+  CLzmaProb isRepG1[kNumStates];
+  CLzmaProb isRepG2[kNumStates];
+  CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX];
+
+  CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits];
+  CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex];
+  CLzmaProb posAlignEncoder[1 << kNumAlignBits];
+  
+  CLenPriceEnc lenEnc;
+  CLenPriceEnc repLenEnc;
+
+  UInt32 reps[LZMA_NUM_REPS];
+  UInt32 state;
+} CSaveState;
+
+typedef struct _CLzmaEnc
+{
+  IMatchFinder matchFinder;
+  void *matchFinderObj;
+
+  #ifdef COMPRESS_MF_MT
+  Bool mtMode;
+  CMatchFinderMt matchFinderMt;
+  #endif
+
+  CMatchFinder matchFinderBase;
+
+  #ifdef COMPRESS_MF_MT
+  Byte pad[128];
+  #endif
+  
+  UInt32 optimumEndIndex;
+  UInt32 optimumCurrentIndex;
+
+  UInt32 longestMatchLength;
+  UInt32 numPairs;
+  UInt32 numAvail;
+  COptimal opt[kNumOpts];
+  
+  #ifndef LZMA_LOG_BSR
+  Byte g_FastPos[1 << kNumLogBits];
+  #endif
+
+  UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits];
+  UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1];
+  UInt32 numFastBytes;
+  UInt32 additionalOffset;
+  UInt32 reps[LZMA_NUM_REPS];
+  UInt32 state;
+
+  UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax];
+  UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances];
+  UInt32 alignPrices[kAlignTableSize];
+  UInt32 alignPriceCount;
+
+  UInt32 distTableSize;
+
+  unsigned lc, lp, pb;
+  unsigned lpMask, pbMask;
+
+  CLzmaProb *litProbs;
+
+  CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX];
+  CLzmaProb isRep[kNumStates];
+  CLzmaProb isRepG0[kNumStates];
+  CLzmaProb isRepG1[kNumStates];
+  CLzmaProb isRepG2[kNumStates];
+  CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX];
+
+  CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits];
+  CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex];
+  CLzmaProb posAlignEncoder[1 << kNumAlignBits];
+  
+  CLenPriceEnc lenEnc;
+  CLenPriceEnc repLenEnc;
+
+  unsigned lclp;
+
+  Bool fastMode;
+  
+  CRangeEnc rc;
+
+  Bool writeEndMark;
+  UInt64 nowPos64;
+  UInt32 matchPriceCount;
+  Bool finished;
+  Bool multiThread;
+
+  SRes result;
+  UInt32 dictSize;
+  UInt32 matchFinderCycles;
+
+  ISeqInStream *inStream;
+  CSeqInStreamBuf seqBufInStream;
+
+  CSaveState saveState;
+} CLzmaEnc;
+
+void LzmaEnc_SaveState(CLzmaEncHandle pp)
+{
+  CLzmaEnc *p = (CLzmaEnc *)pp;
+  CSaveState *dest = &p->saveState;
+  int i;
+  dest->lenEnc = p->lenEnc;
+  dest->repLenEnc = p->repLenEnc;
+  dest->state = p->state;
+
+  for (i = 0; i < kNumStates; i++)
+  {
+    memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i]));
+    memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i]));
+  }
+  for (i = 0; i < kNumLenToPosStates; i++)
+    memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i]));
+  memcpy(dest->isRep, p->isRep, sizeof(p->isRep));
+  memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0));
+  memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1));
+  memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2));
+  memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders));
+  memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder));
+  memcpy(dest->reps, p->reps, sizeof(p->reps));
+  memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb));
+}
+
+void LzmaEnc_RestoreState(CLzmaEncHandle pp)
+{
+  CLzmaEnc *dest = (CLzmaEnc *)pp;
+  const CSaveState *p = &dest->saveState;
+  int i;
+  dest->lenEnc = p->lenEnc;
+  dest->repLenEnc = p->repLenEnc;
+  dest->state = p->state;
+
+  for (i = 0; i < kNumStates; i++)
+  {
+    memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i]));
+    memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i]));
+  }
+  for (i = 0; i < kNumLenToPosStates; i++)
+    memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i]));
+  memcpy(dest->isRep, p->isRep, sizeof(p->isRep));
+  memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0));
+  memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1));
+  memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2));
+  memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders));
+  memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder));
+  memcpy(dest->reps, p->reps, sizeof(p->reps));
+  memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb));
+}
+
+SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2)
+{
+  CLzmaEnc *p = (CLzmaEnc *)pp;
+  CLzmaEncProps props = *props2;
+  LzmaEncProps_Normalize(&props);
+
+  if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX ||
+      props.dictSize > (1 << kDicLogSizeMaxCompress) || props.dictSize > (1 << 30))
+    return SZ_ERROR_PARAM;
+  p->dictSize = props.dictSize;
+  p->matchFinderCycles = props.mc;
+  {
+    unsigned fb = props.fb;
+    if (fb < 5)
+      fb = 5;
+    if (fb > LZMA_MATCH_LEN_MAX)
+      fb = LZMA_MATCH_LEN_MAX;
+    p->numFastBytes = fb;
+  }
+  p->lc = props.lc;
+  p->lp = props.lp;
+  p->pb = props.pb;
+  p->fastMode = (props.algo == 0);
+  p->matchFinderBase.btMode = props.btMode;
+  {
+    UInt32 numHashBytes = 4;
+    if (props.btMode)
+    {
+      if (props.numHashBytes < 2)
+        numHashBytes = 2;
+      else if (props.numHashBytes < 4)
+        numHashBytes = props.numHashBytes;
+    }
+    p->matchFinderBase.numHashBytes = numHashBytes;
+  }
+
+  p->matchFinderBase.cutValue = props.mc;
+
+  p->writeEndMark = props.writeEndMark;
+
+  #ifdef COMPRESS_MF_MT
+  /*
+  if (newMultiThread != _multiThread)
+  {
+    ReleaseMatchFinder();
+    _multiThread = newMultiThread;
+  }
+  */
+  p->multiThread = (props.numThreads > 1);
+  #endif
+
+  return SZ_OK;
+}
+
+static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4,  5,  6,   4, 5};
+static const int kMatchNextStates[kNumStates]   = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10};
+static const int kRepNextStates[kNumStates]     = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11};
+static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11};
+
+#define IsCharState(s) ((s) < 7)
+
+#define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1)
+
+#define kInfinityPrice (1 << 30)
+
+static void RangeEnc_Construct(CRangeEnc *p)
+{
+  p->outStream = 0;
+  p->bufBase = 0;
+}
+
+#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize)
+
+#define RC_BUF_SIZE (1 << 16)
+static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc)
+{
+  if (p->bufBase == 0)
+  {
+    p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE);
+    if (p->bufBase == 0)
+      return 0;
+    p->bufLim = p->bufBase + RC_BUF_SIZE;
+  }
+  return 1;
+}
+
+static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc)
+{
+  alloc->Free(alloc, p->bufBase);
+  p->bufBase = 0;
+}
+
+static void RangeEnc_Init(CRangeEnc *p)
+{
+  /* Stream.Init(); */
+  p->low = 0;
+  p->range = 0xFFFFFFFF;
+  p->cacheSize = 1;
+  p->cache = 0;
+
+  p->buf = p->bufBase;
+
+  p->processed = 0;
+  p->res = SZ_OK;
+}
+
+static void RangeEnc_FlushStream(CRangeEnc *p)
+{
+  size_t num;
+  if (p->res != SZ_OK)
+    return;
+  num = p->buf - p->bufBase;
+  if (num != p->outStream->Write(p->outStream, p->bufBase, num))
+    p->res = SZ_ERROR_WRITE;
+  p->processed += num;
+  p->buf = p->bufBase;
+}
+
+static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p)
+{
+  if ((UInt32)p->low < (UInt32)0xFF000000 || (int)(p->low >> 32) != 0)
+  {
+    Byte temp = p->cache;
+    do
+    {
+      Byte *buf = p->buf;
+      *buf++ = (Byte)(temp + (Byte)(p->low >> 32));
+      p->buf = buf;
+      if (buf == p->bufLim)
+        RangeEnc_FlushStream(p);
+      temp = 0xFF;
+    }
+    while (--p->cacheSize != 0);
+    p->cache = (Byte)((UInt32)p->low >> 24);
+  }
+  p->cacheSize++;
+  p->low = (UInt32)p->low << 8;
+}
+
+static void RangeEnc_FlushData(CRangeEnc *p)
+{
+  int i;
+  for (i = 0; i < 5; i++)
+    RangeEnc_ShiftLow(p);
+}
+
+static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, int numBits)
+{
+  do
+  {
+    p->range >>= 1;
+    p->low += p->range & (0 - ((value >> --numBits) & 1));
+    if (p->range < kTopValue)
+    {
+      p->range <<= 8;
+      RangeEnc_ShiftLow(p);
+    }
+  }
+  while (numBits != 0);
+}
+
+static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, UInt32 symbol)
+{
+  UInt32 ttt = *prob;
+  UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt;
+  if (symbol == 0)
+  {
+    p->range = newBound;
+    ttt += (kBitModelTotal - ttt) >> kNumMoveBits;
+  }
+  else
+  {
+    p->low += newBound;
+    p->range -= newBound;
+    ttt -= ttt >> kNumMoveBits;
+  }
+  *prob = (CLzmaProb)ttt;
+  if (p->range < kTopValue)
+  {
+    p->range <<= 8;
+    RangeEnc_ShiftLow(p);
+  }
+}
+
+static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol)
+{
+  symbol |= 0x100;
+  do
+  {
+    RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1);
+    symbol <<= 1;
+  }
+  while (symbol < 0x10000);
+}
+
+static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, UInt32 matchByte)
+{
+  UInt32 offs = 0x100;
+  symbol |= 0x100;
+  do
+  {
+    matchByte <<= 1;
+    RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1);
+    symbol <<= 1;
+    offs &= ~(matchByte ^ symbol);
+  }
+  while (symbol < 0x10000);
+}
+
+void LzmaEnc_InitPriceTables(UInt32 *ProbPrices)
+{
+  UInt32 i;
+  for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits))
+  {
+    const int kCyclesBits = kNumBitPriceShiftBits;
+    UInt32 w = i;
+    UInt32 bitCount = 0;
+    int j;
+    for (j = 0; j < kCyclesBits; j++)
+    {
+      w = w * w;
+      bitCount <<= 1;
+      while (w >= ((UInt32)1 << 16))
+      {
+        w >>= 1;
+        bitCount++;
+      }
+    }
+    ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount);
+  }
+}
+
+
+#define GET_PRICE(prob, symbol) \
+  p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits];
+
+#define GET_PRICEa(prob, symbol) \
+  ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits];
+
+#define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits]
+#define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits]
+
+#define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits]
+#define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits]
+
+static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 *ProbPrices)
+{
+  UInt32 price = 0;
+  symbol |= 0x100;
+  do
+  {
+    price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1);
+    symbol <<= 1;
+  }
+  while (symbol < 0x10000);
+  return price;
+}
+
+static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, UInt32 *ProbPrices)
+{
+  UInt32 price = 0;
+  UInt32 offs = 0x100;
+  symbol |= 0x100;
+  do
+  {
+    matchByte <<= 1;
+    price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1);
+    symbol <<= 1;
+    offs &= ~(matchByte ^ symbol);
+  }
+  while (symbol < 0x10000);
+  return price;
+}
+
+
+static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol)
+{
+  UInt32 m = 1;
+  int i;
+  for (i = numBitLevels; i != 0;)
+  {
+    UInt32 bit;
+    i--;
+    bit = (symbol >> i) & 1;
+    RangeEnc_EncodeBit(rc, probs + m, bit);
+    m = (m << 1) | bit;
+  }
+}
+
+static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol)
+{
+  UInt32 m = 1;
+  int i;
+  for (i = 0; i < numBitLevels; i++)
+  {
+    UInt32 bit = symbol & 1;
+    RangeEnc_EncodeBit(rc, probs + m, bit);
+    m = (m << 1) | bit;
+    symbol >>= 1;
+  }
+}
+
+static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices)
+{
+  UInt32 price = 0;
+  symbol |= (1 << numBitLevels);
+  while (symbol != 1)
+  {
+    price += GET_PRICEa(probs[symbol >> 1], symbol & 1);
+    symbol >>= 1;
+  }
+  return price;
+}
+
+static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices)
+{
+  UInt32 price = 0;
+  UInt32 m = 1;
+  int i;
+  for (i = numBitLevels; i != 0; i--)
+  {
+    UInt32 bit = symbol & 1;
+    symbol >>= 1;
+    price += GET_PRICEa(probs[m], bit);
+    m = (m << 1) | bit;
+  }
+  return price;
+}
+
+
+static void LenEnc_Init(CLenEnc *p)
+{
+  unsigned i;
+  p->choice = p->choice2 = kProbInitValue;
+  for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++)
+    p->low[i] = kProbInitValue;
+  for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++)
+    p->mid[i] = kProbInitValue;
+  for (i = 0; i < kLenNumHighSymbols; i++)
+    p->high[i] = kProbInitValue;
+}
+
+static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState)
+{
+  if (symbol < kLenNumLowSymbols)
+  {
+    RangeEnc_EncodeBit(rc, &p->choice, 0);
+    RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol);
+  }
+  else
+  {
+    RangeEnc_EncodeBit(rc, &p->choice, 1);
+    if (symbol < kLenNumLowSymbols + kLenNumMidSymbols)
+    {
+      RangeEnc_EncodeBit(rc, &p->choice2, 0);
+      RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols);
+    }
+    else
+    {
+      RangeEnc_EncodeBit(rc, &p->choice2, 1);
+      RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols);
+    }
+  }
+}
+
+static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, UInt32 *ProbPrices)
+{
+  UInt32 a0 = GET_PRICE_0a(p->choice);
+  UInt32 a1 = GET_PRICE_1a(p->choice);
+  UInt32 b0 = a1 + GET_PRICE_0a(p->choice2);
+  UInt32 b1 = a1 + GET_PRICE_1a(p->choice2);
+  UInt32 i = 0;
+  for (i = 0; i < kLenNumLowSymbols; i++)
+  {
+    if (i >= numSymbols)
+      return;
+    prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices);
+  }
+  for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++)
+  {
+    if (i >= numSymbols)
+      return;
+    prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices);
+  }
+  for (; i < numSymbols; i++)
+    prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices);
+}
+
+static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, UInt32 *ProbPrices)
+{
+  LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices);
+  p->counters[posState] = p->tableSize;
+}
+
+static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, UInt32 *ProbPrices)
+{
+  UInt32 posState;
+  for (posState = 0; posState < numPosStates; posState++)
+    LenPriceEnc_UpdateTable(p, posState, ProbPrices);
+}
+
+static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, UInt32 *ProbPrices)
+{
+  LenEnc_Encode(&p->p, rc, symbol, posState);
+  if (updatePrice)
+    if (--p->counters[posState] == 0)
+      LenPriceEnc_UpdateTable(p, posState, ProbPrices);
+}
+
+
+
+
+static void MovePos(CLzmaEnc *p, UInt32 num)
+{
+  #ifdef SHOW_STAT
+  ttt += num;
+  printf("\n MovePos %d", num);
+  #endif
+  if (num != 0)
+  {
+    p->additionalOffset += num;
+    p->matchFinder.Skip(p->matchFinderObj, num);
+  }
+}
+
+static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes)
+{
+  UInt32 lenRes = 0, numPairs;
+  p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj);
+  numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches);
+  #ifdef SHOW_STAT
+  printf("\n i = %d numPairs = %d    ", ttt, numPairs / 2);
+  ttt++;
+  {
+    UInt32 i;
+    for (i = 0; i < numPairs; i += 2)
+      printf("%2d %6d   | ", p->matches[i], p->matches[i + 1]);
+  }
+  #endif
+  if (numPairs > 0)
+  {
+    lenRes = p->matches[numPairs - 2];
+    if (lenRes == p->numFastBytes)
+    {
+      const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
+      UInt32 distance = p->matches[numPairs - 1] + 1;
+      UInt32 numAvail = p->numAvail;
+      if (numAvail > LZMA_MATCH_LEN_MAX)
+        numAvail = LZMA_MATCH_LEN_MAX;
+      {
+        const Byte *pby2 = pby - distance;
+        for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++);
+      }
+    }
+  }
+  p->additionalOffset++;
+  *numDistancePairsRes = numPairs;
+  return lenRes;
+}
+
+
+#define MakeAsChar(p) (p)->backPrev = (UInt32)(-1); (p)->prev1IsChar = False;
+#define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False;
+#define IsShortRep(p) ((p)->backPrev == 0)
+
+static UInt32 GetRepLen1Price(CLzmaEnc *p, UInt32 state, UInt32 posState)
+{
+  return
+    GET_PRICE_0(p->isRepG0[state]) +
+    GET_PRICE_0(p->isRep0Long[state][posState]);
+}
+
+static UInt32 GetPureRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 state, UInt32 posState)
+{
+  UInt32 price;
+  if (repIndex == 0)
+  {
+    price = GET_PRICE_0(p->isRepG0[state]);
+    price += GET_PRICE_1(p->isRep0Long[state][posState]);
+  }
+  else
+  {
+    price = GET_PRICE_1(p->isRepG0[state]);
+    if (repIndex == 1)
+      price += GET_PRICE_0(p->isRepG1[state]);
+    else
+    {
+      price += GET_PRICE_1(p->isRepG1[state]);
+      price += GET_PRICE(p->isRepG2[state], repIndex - 2);
+    }
+  }
+  return price;
+}
+
+static UInt32 GetRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 len, UInt32 state, UInt32 posState)
+{
+  return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] +
+    GetPureRepPrice(p, repIndex, state, posState);
+}
+
+static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur)
+{
+  UInt32 posMem = p->opt[cur].posPrev;
+  UInt32 backMem = p->opt[cur].backPrev;
+  p->optimumEndIndex = cur;
+  do
+  {
+    if (p->opt[cur].prev1IsChar)
+    {
+      MakeAsChar(&p->opt[posMem])
+      p->opt[posMem].posPrev = posMem - 1;
+      if (p->opt[cur].prev2)
+      {
+        p->opt[posMem - 1].prev1IsChar = False;
+        p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2;
+        p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2;
+      }
+    }
+    {
+      UInt32 posPrev = posMem;
+      UInt32 backCur = backMem;
+      
+      backMem = p->opt[posPrev].backPrev;
+      posMem = p->opt[posPrev].posPrev;
+      
+      p->opt[posPrev].backPrev = backCur;
+      p->opt[posPrev].posPrev = cur;
+      cur = posPrev;
+    }
+  }
+  while (cur != 0);
+  *backRes = p->opt[0].backPrev;
+  p->optimumCurrentIndex  = p->opt[0].posPrev;
+  return p->optimumCurrentIndex;
+}
+
+#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300)
+
+static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes)
+{
+  UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur;
+  UInt32 matchPrice, repMatchPrice, normalMatchPrice;
+  UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS];
+  UInt32 *matches;
+  const Byte *data;
+  Byte curByte, matchByte;
+  if (p->optimumEndIndex != p->optimumCurrentIndex)
+  {
+    const COptimal *opt = &p->opt[p->optimumCurrentIndex];
+    UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex;
+    *backRes = opt->backPrev;
+    p->optimumCurrentIndex = opt->posPrev;
+    return lenRes;
+  }
+  p->optimumCurrentIndex = p->optimumEndIndex = 0;
+  
+  if (p->additionalOffset == 0)
+    mainLen = ReadMatchDistances(p, &numPairs);
+  else
+  {
+    mainLen = p->longestMatchLength;
+    numPairs = p->numPairs;
+  }
+
+  numAvail = p->numAvail;
+  if (numAvail < 2)
+  {
+    *backRes = (UInt32)(-1);
+    return 1;
+  }
+  if (numAvail > LZMA_MATCH_LEN_MAX)
+    numAvail = LZMA_MATCH_LEN_MAX;
+
+  data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
+  repMaxIndex = 0;
+  for (i = 0; i < LZMA_NUM_REPS; i++)
+  {
+    UInt32 lenTest;
+    const Byte *data2;
+    reps[i] = p->reps[i];
+    data2 = data - (reps[i] + 1);
+    if (data[0] != data2[0] || data[1] != data2[1])
+    {
+      repLens[i] = 0;
+      continue;
+    }
+    for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++);
+    repLens[i] = lenTest;
+    if (lenTest > repLens[repMaxIndex])
+      repMaxIndex = i;
+  }
+  if (repLens[repMaxIndex] >= p->numFastBytes)
+  {
+    UInt32 lenRes;
+    *backRes = repMaxIndex;
+    lenRes = repLens[repMaxIndex];
+    MovePos(p, lenRes - 1);
+    return lenRes;
+  }
+
+  matches = p->matches;
+  if (mainLen >= p->numFastBytes)
+  {
+    *backRes = matches[numPairs - 1] + LZMA_NUM_REPS;
+    MovePos(p, mainLen - 1);
+    return mainLen;
+  }
+  curByte = *data;
+  matchByte = *(data - (reps[0] + 1));
+
+  if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2)
+  {
+    *backRes = (UInt32)-1;
+    return 1;
+  }
+
+  p->opt[0].state = (CState)p->state;
+
+  posState = (position & p->pbMask);
+
+  {
+    const CLzmaProb *probs = LIT_PROBS(position, *(data - 1));
+    p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) +
+        (!IsCharState(p->state) ?
+          LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) :
+          LitEnc_GetPrice(probs, curByte, p->ProbPrices));
+  }
+
+  MakeAsChar(&p->opt[1]);
+
+  matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]);
+  repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]);
+
+  if (matchByte == curByte)
+  {
+    UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState);
+    if (shortRepPrice < p->opt[1].price)
+    {
+      p->opt[1].price = shortRepPrice;
+      MakeAsShortRep(&p->opt[1]);
+    }
+  }
+  lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]);
+
+  if (lenEnd < 2)
+  {
+    *backRes = p->opt[1].backPrev;
+    return 1;
+  }
+
+  p->opt[1].posPrev = 0;
+  for (i = 0; i < LZMA_NUM_REPS; i++)
+    p->opt[0].backs[i] = reps[i];
+
+  len = lenEnd;
+  do
+    p->opt[len--].price = kInfinityPrice;
+  while (len >= 2);
+
+  for (i = 0; i < LZMA_NUM_REPS; i++)
+  {
+    UInt32 repLen = repLens[i];
+    UInt32 price;
+    if (repLen < 2)
+      continue;
+    price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState);
+    do
+    {
+      UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2];
+      COptimal *opt = &p->opt[repLen];
+      if (curAndLenPrice < opt->price)
+      {
+        opt->price = curAndLenPrice;
+        opt->posPrev = 0;
+        opt->backPrev = i;
+        opt->prev1IsChar = False;
+      }
+    }
+    while (--repLen >= 2);
+  }
+
+  normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]);
+
+  len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2);
+  if (len <= mainLen)
+  {
+    UInt32 offs = 0;
+    while (len > matches[offs])
+      offs += 2;
+    for (; ; len++)
+    {
+      COptimal *opt;
+      UInt32 distance = matches[offs + 1];
+
+      UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN];
+      UInt32 lenToPosState = GetLenToPosState(len);
+      if (distance < kNumFullDistances)
+        curAndLenPrice += p->distancesPrices[lenToPosState][distance];
+      else
+      {
+        UInt32 slot;
+        GetPosSlot2(distance, slot);
+        curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot];
+      }
+      opt = &p->opt[len];
+      if (curAndLenPrice < opt->price)
+      {
+        opt->price = curAndLenPrice;
+        opt->posPrev = 0;
+        opt->backPrev = distance + LZMA_NUM_REPS;
+        opt->prev1IsChar = False;
+      }
+      if (len == matches[offs])
+      {
+        offs += 2;
+        if (offs == numPairs)
+          break;
+      }
+    }
+  }
+
+  cur = 0;
+
+    #ifdef SHOW_STAT2
+    if (position >= 0)
+    {
+      unsigned i;
+      printf("\n pos = %4X", position);
+      for (i = cur; i <= lenEnd; i++)
+      printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price);
+    }
+    #endif
+
+  for (;;)
+  {
+    UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen;
+    UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice;
+    Bool nextIsChar;
+    Byte curByte, matchByte;
+    const Byte *data;
+    COptimal *curOpt;
+    COptimal *nextOpt;
+
+    cur++;
+    if (cur == lenEnd)
+      return Backward(p, backRes, cur);
+
+    newLen = ReadMatchDistances(p, &numPairs);
+    if (newLen >= p->numFastBytes)
+    {
+      p->numPairs = numPairs;
+      p->longestMatchLength = newLen;
+      return Backward(p, backRes, cur);
+    }
+    position++;
+    curOpt = &p->opt[cur];
+    posPrev = curOpt->posPrev;
+    if (curOpt->prev1IsChar)
+    {
+      posPrev--;
+      if (curOpt->prev2)
+      {
+        state = p->opt[curOpt->posPrev2].state;
+        if (curOpt->backPrev2 < LZMA_NUM_REPS)
+          state = kRepNextStates[state];
+        else
+          state = kMatchNextStates[state];
+      }
+      else
+        state = p->opt[posPrev].state;
+      state = kLiteralNextStates[state];
+    }
+    else
+      state = p->opt[posPrev].state;
+    if (posPrev == cur - 1)
+    {
+      if (IsShortRep(curOpt))
+        state = kShortRepNextStates[state];
+      else
+        state = kLiteralNextStates[state];
+    }
+    else
+    {
+      UInt32 pos;
+      const COptimal *prevOpt;
+      if (curOpt->prev1IsChar && curOpt->prev2)
+      {
+        posPrev = curOpt->posPrev2;
+        pos = curOpt->backPrev2;
+        state = kRepNextStates[state];
+      }
+      else
+      {
+        pos = curOpt->backPrev;
+        if (pos < LZMA_NUM_REPS)
+          state = kRepNextStates[state];
+        else
+          state = kMatchNextStates[state];
+      }
+      prevOpt = &p->opt[posPrev];
+      if (pos < LZMA_NUM_REPS)
+      {
+        UInt32 i;
+        reps[0] = prevOpt->backs[pos];
+        for (i = 1; i <= pos; i++)
+          reps[i] = prevOpt->backs[i - 1];
+        for (; i < LZMA_NUM_REPS; i++)
+          reps[i] = prevOpt->backs[i];
+      }
+      else
+      {
+        UInt32 i;
+        reps[0] = (pos - LZMA_NUM_REPS);
+        for (i = 1; i < LZMA_NUM_REPS; i++)
+          reps[i] = prevOpt->backs[i - 1];
+      }
+    }
+    curOpt->state = (CState)state;
+
+    curOpt->backs[0] = reps[0];
+    curOpt->backs[1] = reps[1];
+    curOpt->backs[2] = reps[2];
+    curOpt->backs[3] = reps[3];
+
+    curPrice = curOpt->price;
+    nextIsChar = False;
+    data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
+    curByte = *data;
+    matchByte = *(data - (reps[0] + 1));
+
+    posState = (position & p->pbMask);
+
+    curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]);
+    {
+      const CLzmaProb *probs = LIT_PROBS(position, *(data - 1));
+      curAnd1Price +=
+        (!IsCharState(state) ?
+          LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) :
+          LitEnc_GetPrice(probs, curByte, p->ProbPrices));
+    }
+
+    nextOpt = &p->opt[cur + 1];
+
+    if (curAnd1Price < nextOpt->price)
+    {
+      nextOpt->price = curAnd1Price;
+      nextOpt->posPrev = cur;
+      MakeAsChar(nextOpt);
+      nextIsChar = True;
+    }
+
+    matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]);
+    repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]);
+    
+    if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0))
+    {
+      UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState);
+      if (shortRepPrice <= nextOpt->price)
+      {
+        nextOpt->price = shortRepPrice;
+        nextOpt->posPrev = cur;
+        MakeAsShortRep(nextOpt);
+        nextIsChar = True;
+      }
+    }
+    numAvailFull = p->numAvail;
+    {
+      UInt32 temp = kNumOpts - 1 - cur;
+      if (temp < numAvailFull)
+        numAvailFull = temp;
+    }
+
+    if (numAvailFull < 2)
+      continue;
+    numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes);
+
+    if (!nextIsChar && matchByte != curByte) /* speed optimization */
+    {
+      /* try Literal + rep0 */
+      UInt32 temp;
+      UInt32 lenTest2;
+      const Byte *data2 = data - (reps[0] + 1);
+      UInt32 limit = p->numFastBytes + 1;
+      if (limit > numAvailFull)
+        limit = numAvailFull;
+
+      for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++);
+      lenTest2 = temp - 1;
+      if (lenTest2 >= 2)
+      {
+        UInt32 state2 = kLiteralNextStates[state];
+        UInt32 posStateNext = (position + 1) & p->pbMask;
+        UInt32 nextRepMatchPrice = curAnd1Price +
+            GET_PRICE_1(p->isMatch[state2][posStateNext]) +
+            GET_PRICE_1(p->isRep[state2]);
+        /* for (; lenTest2 >= 2; lenTest2--) */
+        {
+          UInt32 curAndLenPrice;
+          COptimal *opt;
+          UInt32 offset = cur + 1 + lenTest2;
+          while (lenEnd < offset)
+            p->opt[++lenEnd].price = kInfinityPrice;
+          curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext);
+          opt = &p->opt[offset];
+          if (curAndLenPrice < opt->price)
+          {
+            opt->price = curAndLenPrice;
+            opt->posPrev = cur + 1;
+            opt->backPrev = 0;
+            opt->prev1IsChar = True;
+            opt->prev2 = False;
+          }
+        }
+      }
+    }
+    
+    startLen = 2; /* speed optimization */
+    {
+    UInt32 repIndex;
+    for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++)
+    {
+      UInt32 lenTest;
+      UInt32 lenTestTemp;
+      UInt32 price;
+      const Byte *data2 = data - (reps[repIndex] + 1);
+      if (data[0] != data2[0] || data[1] != data2[1])
+        continue;
+      for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++);
+      while (lenEnd < cur + lenTest)
+        p->opt[++lenEnd].price = kInfinityPrice;
+      lenTestTemp = lenTest;
+      price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState);
+      do
+      {
+        UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2];
+        COptimal *opt = &p->opt[cur + lenTest];
+        if (curAndLenPrice < opt->price)
+        {
+          opt->price = curAndLenPrice;
+          opt->posPrev = cur;
+          opt->backPrev = repIndex;
+          opt->prev1IsChar = False;
+        }
+      }
+      while (--lenTest >= 2);
+      lenTest = lenTestTemp;
+      
+      if (repIndex == 0)
+        startLen = lenTest + 1;
+        
+      /* if (_maxMode) */
+        {
+          UInt32 lenTest2 = lenTest + 1;
+          UInt32 limit = lenTest2 + p->numFastBytes;
+          UInt32 nextRepMatchPrice;
+          if (limit > numAvailFull)
+            limit = numAvailFull;
+          for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++);
+          lenTest2 -= lenTest + 1;
+          if (lenTest2 >= 2)
+          {
+            UInt32 state2 = kRepNextStates[state];
+            UInt32 posStateNext = (position + lenTest) & p->pbMask;
+            UInt32 curAndLenCharPrice =
+                price + p->repLenEnc.prices[posState][lenTest - 2] +
+                GET_PRICE_0(p->isMatch[state2][posStateNext]) +
+                LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]),
+                    data[lenTest], data2[lenTest], p->ProbPrices);
+            state2 = kLiteralNextStates[state2];
+            posStateNext = (position + lenTest + 1) & p->pbMask;
+            nextRepMatchPrice = curAndLenCharPrice +
+                GET_PRICE_1(p->isMatch[state2][posStateNext]) +
+                GET_PRICE_1(p->isRep[state2]);
+            
+            /* for (; lenTest2 >= 2; lenTest2--) */
+            {
+              UInt32 curAndLenPrice;
+              COptimal *opt;
+              UInt32 offset = cur + lenTest + 1 + lenTest2;
+              while (lenEnd < offset)
+                p->opt[++lenEnd].price = kInfinityPrice;
+              curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext);
+              opt = &p->opt[offset];
+              if (curAndLenPrice < opt->price)
+              {
+                opt->price = curAndLenPrice;
+                opt->posPrev = cur + lenTest + 1;
+                opt->backPrev = 0;
+                opt->prev1IsChar = True;
+                opt->prev2 = True;
+                opt->posPrev2 = cur;
+                opt->backPrev2 = repIndex;
+              }
+            }
+          }
+        }
+    }
+    }
+    /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */
+    if (newLen > numAvail)
+    {
+      newLen = numAvail;
+      for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2);
+      matches[numPairs] = newLen;
+      numPairs += 2;
+    }
+    if (newLen >= startLen)
+    {
+      UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]);
+      UInt32 offs, curBack, posSlot;
+      UInt32 lenTest;
+      while (lenEnd < cur + newLen)
+        p->opt[++lenEnd].price = kInfinityPrice;
+
+      offs = 0;
+      while (startLen > matches[offs])
+        offs += 2;
+      curBack = matches[offs + 1];
+      GetPosSlot2(curBack, posSlot);
+      for (lenTest = /*2*/ startLen; ; lenTest++)
+      {
+        UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN];
+        UInt32 lenToPosState = GetLenToPosState(lenTest);
+        COptimal *opt;
+        if (curBack < kNumFullDistances)
+          curAndLenPrice += p->distancesPrices[lenToPosState][curBack];
+        else
+          curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask];
+        
+        opt = &p->opt[cur + lenTest];
+        if (curAndLenPrice < opt->price)
+        {
+          opt->price = curAndLenPrice;
+          opt->posPrev = cur;
+          opt->backPrev = curBack + LZMA_NUM_REPS;
+          opt->prev1IsChar = False;
+        }
+
+        if (/*_maxMode && */lenTest == matches[offs])
+        {
+          /* Try Match + Literal + Rep0 */
+          const Byte *data2 = data - (curBack + 1);
+          UInt32 lenTest2 = lenTest + 1;
+          UInt32 limit = lenTest2 + p->numFastBytes;
+          UInt32 nextRepMatchPrice;
+          if (limit > numAvailFull)
+            limit = numAvailFull;
+          for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++);
+          lenTest2 -= lenTest + 1;
+          if (lenTest2 >= 2)
+          {
+            UInt32 state2 = kMatchNextStates[state];
+            UInt32 posStateNext = (position + lenTest) & p->pbMask;
+            UInt32 curAndLenCharPrice = curAndLenPrice +
+                GET_PRICE_0(p->isMatch[state2][posStateNext]) +
+                LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]),
+                    data[lenTest], data2[lenTest], p->ProbPrices);
+            state2 = kLiteralNextStates[state2];
+            posStateNext = (posStateNext + 1) & p->pbMask;
+            nextRepMatchPrice = curAndLenCharPrice +
+                GET_PRICE_1(p->isMatch[state2][posStateNext]) +
+                GET_PRICE_1(p->isRep[state2]);
+            
+            /* for (; lenTest2 >= 2; lenTest2--) */
+            {
+              UInt32 offset = cur + lenTest + 1 + lenTest2;
+              UInt32 curAndLenPrice;
+              COptimal *opt;
+              while (lenEnd < offset)
+                p->opt[++lenEnd].price = kInfinityPrice;
+              curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext);
+              opt = &p->opt[offset];
+              if (curAndLenPrice < opt->price)
+              {
+                opt->price = curAndLenPrice;
+                opt->posPrev = cur + lenTest + 1;
+                opt->backPrev = 0;
+                opt->prev1IsChar = True;
+                opt->prev2 = True;
+                opt->posPrev2 = cur;
+                opt->backPrev2 = curBack + LZMA_NUM_REPS;
+              }
+            }
+          }
+          offs += 2;
+          if (offs == numPairs)
+            break;
+          curBack = matches[offs + 1];
+          if (curBack >= kNumFullDistances)
+            GetPosSlot2(curBack, posSlot);
+        }
+      }
+    }
+  }
+}
+
+#define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist))
+
+static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes)
+{
+  UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i;
+  const Byte *data;
+  const UInt32 *matches;
+
+  if (p->additionalOffset == 0)
+    mainLen = ReadMatchDistances(p, &numPairs);
+  else
+  {
+    mainLen = p->longestMatchLength;
+    numPairs = p->numPairs;
+  }
+
+  numAvail = p->numAvail;
+  *backRes = (UInt32)-1;
+  if (numAvail < 2)
+    return 1;
+  if (numAvail > LZMA_MATCH_LEN_MAX)
+    numAvail = LZMA_MATCH_LEN_MAX;
+  data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
+
+  repLen = repIndex = 0;
+  for (i = 0; i < LZMA_NUM_REPS; i++)
+  {
+    UInt32 len;
+    const Byte *data2 = data - (p->reps[i] + 1);
+    if (data[0] != data2[0] || data[1] != data2[1])
+      continue;
+    for (len = 2; len < numAvail && data[len] == data2[len]; len++);
+    if (len >= p->numFastBytes)
+    {
+      *backRes = i;
+      MovePos(p, len - 1);
+      return len;
+    }
+    if (len > repLen)
+    {
+      repIndex = i;
+      repLen = len;
+    }
+  }
+
+  matches = p->matches;
+  if (mainLen >= p->numFastBytes)
+  {
+    *backRes = matches[numPairs - 1] + LZMA_NUM_REPS;
+    MovePos(p, mainLen - 1);
+    return mainLen;
+  }
+
+  mainDist = 0; /* for GCC */
+  if (mainLen >= 2)
+  {
+    mainDist = matches[numPairs - 1];
+    while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1)
+    {
+      if (!ChangePair(matches[numPairs - 3], mainDist))
+        break;
+      numPairs -= 2;
+      mainLen = matches[numPairs - 2];
+      mainDist = matches[numPairs - 1];
+    }
+    if (mainLen == 2 && mainDist >= 0x80)
+      mainLen = 1;
+  }
+
+  if (repLen >= 2 && (
+        (repLen + 1 >= mainLen) ||
+        (repLen + 2 >= mainLen && mainDist >= (1 << 9)) ||
+        (repLen + 3 >= mainLen && mainDist >= (1 << 15))))
+  {
+    *backRes = repIndex;
+    MovePos(p, repLen - 1);
+    return repLen;
+  }
+  
+  if (mainLen < 2 || numAvail <= 2)
+    return 1;
+
+  p->longestMatchLength = ReadMatchDistances(p, &p->numPairs);
+  if (p->longestMatchLength >= 2)
+  {
+    UInt32 newDistance = matches[p->numPairs - 1];
+    if ((p->longestMatchLength >= mainLen && newDistance < mainDist) ||
+        (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) ||
+        (p->longestMatchLength > mainLen + 1) ||
+        (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist)))
+      return 1;
+  }
+  
+  data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
+  for (i = 0; i < LZMA_NUM_REPS; i++)
+  {
+    UInt32 len, limit;
+    const Byte *data2 = data - (p->reps[i] + 1);
+    if (data[0] != data2[0] || data[1] != data2[1])
+      continue;
+    limit = mainLen - 1;
+    for (len = 2; len < limit && data[len] == data2[len]; len++);
+    if (len >= limit)
+      return 1;
+  }
+  *backRes = mainDist + LZMA_NUM_REPS;
+  MovePos(p, mainLen - 2);
+  return mainLen;
+}
+
+static void WriteEndMarker(CLzmaEnc *p, UInt32 posState)
+{
+  UInt32 len;
+  RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1);
+  RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0);
+  p->state = kMatchNextStates[p->state];
+  len = LZMA_MATCH_LEN_MIN;
+  LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices);
+  RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1);
+  RangeEnc_EncodeDirectBits(&p->rc, (((UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits);
+  RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask);
+}
+
+static SRes CheckErrors(CLzmaEnc *p)
+{
+  if (p->result != SZ_OK)
+    return p->result;
+  if (p->rc.res != SZ_OK)
+    p->result = SZ_ERROR_WRITE;
+  if (p->matchFinderBase.result != SZ_OK)
+    p->result = SZ_ERROR_READ;
+  if (p->result != SZ_OK)
+    p->finished = True;
+  return p->result;
+}
+
+static SRes Flush(CLzmaEnc *p, UInt32 nowPos)
+{
+  /* ReleaseMFStream(); */
+  p->finished = True;
+  if (p->writeEndMark)
+    WriteEndMarker(p, nowPos & p->pbMask);
+  RangeEnc_FlushData(&p->rc);
+  RangeEnc_FlushStream(&p->rc);
+  return CheckErrors(p);
+}
+
+static void FillAlignPrices(CLzmaEnc *p)
+{
+  UInt32 i;
+  for (i = 0; i < kAlignTableSize; i++)
+    p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices);
+  p->alignPriceCount = 0;
+}
+
+static void FillDistancesPrices(CLzmaEnc *p)
+{
+  UInt32 tempPrices[kNumFullDistances];
+  UInt32 i, lenToPosState;
+  for (i = kStartPosModelIndex; i < kNumFullDistances; i++)
+  {
+    UInt32 posSlot = GetPosSlot1(i);
+    UInt32 footerBits = ((posSlot >> 1) - 1);
+    UInt32 base = ((2 | (posSlot & 1)) << footerBits);
+    tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices);
+  }
+
+  for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++)
+  {
+    UInt32 posSlot;
+    const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState];
+    UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState];
+    for (posSlot = 0; posSlot < p->distTableSize; posSlot++)
+      posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices);
+    for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++)
+      posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits);
+
+    {
+      UInt32 *distancesPrices = p->distancesPrices[lenToPosState];
+      UInt32 i;
+      for (i = 0; i < kStartPosModelIndex; i++)
+        distancesPrices[i] = posSlotPrices[i];
+      for (; i < kNumFullDistances; i++)
+        distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i];
+    }
+  }
+  p->matchPriceCount = 0;
+}
+
+void LzmaEnc_Construct(CLzmaEnc *p)
+{
+  RangeEnc_Construct(&p->rc);
+  MatchFinder_Construct(&p->matchFinderBase);
+  #ifdef COMPRESS_MF_MT
+  MatchFinderMt_Construct(&p->matchFinderMt);
+  p->matchFinderMt.MatchFinder = &p->matchFinderBase;
+  #endif
+
+  {
+    CLzmaEncProps props;
+    LzmaEncProps_Init(&props);
+    LzmaEnc_SetProps(p, &props);
+  }
+
+  #ifndef LZMA_LOG_BSR
+  LzmaEnc_FastPosInit(p->g_FastPos);
+  #endif
+
+  LzmaEnc_InitPriceTables(p->ProbPrices);
+  p->litProbs = 0;
+  p->saveState.litProbs = 0;
+}
+
+CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc)
+{
+  void *p;
+  p = alloc->Alloc(alloc, sizeof(CLzmaEnc));
+  if (p != 0)
+    LzmaEnc_Construct((CLzmaEnc *)p);
+  return p;
+}
+
+void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc)
+{
+  alloc->Free(alloc, p->litProbs);
+  alloc->Free(alloc, p->saveState.litProbs);
+  p->litProbs = 0;
+  p->saveState.litProbs = 0;
+}
+
+void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig)
+{
+  #ifdef COMPRESS_MF_MT
+  MatchFinderMt_Destruct(&p->matchFinderMt, allocBig);
+  #endif
+  MatchFinder_Free(&p->matchFinderBase, allocBig);
+  LzmaEnc_FreeLits(p, alloc);
+  RangeEnc_Free(&p->rc, alloc);
+}
+
+void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig)
+{
+  LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig);
+  alloc->Free(alloc, p);
+}
+
+static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize, UInt32 maxUnpackSize)
+{
+  UInt32 nowPos32, startPos32;
+  if (p->inStream != 0)
+  {
+    p->matchFinderBase.stream = p->inStream;
+    p->matchFinder.Init(p->matchFinderObj);
+    p->inStream = 0;
+  }
+
+  if (p->finished)
+    return p->result;
+  RINOK(CheckErrors(p));
+
+  nowPos32 = (UInt32)p->nowPos64;
+  startPos32 = nowPos32;
+
+  if (p->nowPos64 == 0)
+  {
+    UInt32 numPairs;
+    Byte curByte;
+    if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0)
+      return Flush(p, nowPos32);
+    ReadMatchDistances(p, &numPairs);
+    RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0);
+    p->state = kLiteralNextStates[p->state];
+    curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset);
+    LitEnc_Encode(&p->rc, p->litProbs, curByte);
+    p->additionalOffset--;
+    nowPos32++;
+  }
+
+  if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0)
+  for (;;)
+  {
+    UInt32 pos, len, posState;
+
+    if (p->fastMode)
+      len = GetOptimumFast(p, &pos);
+    else
+      len = GetOptimum(p, nowPos32, &pos);
+
+    #ifdef SHOW_STAT2
+    printf("\n pos = %4X,   len = %d   pos = %d", nowPos32, len, pos);
+    #endif
+
+    posState = nowPos32 & p->pbMask;
+    if (len == 1 && pos == (UInt32)-1)
+    {
+      Byte curByte;
+      CLzmaProb *probs;
+      const Byte *data;
+
+      RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0);
+      data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset;
+      curByte = *data;
+      probs = LIT_PROBS(nowPos32, *(data - 1));
+      if (IsCharState(p->state))
+        LitEnc_Encode(&p->rc, probs, curByte);
+      else
+        LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1));
+      p->state = kLiteralNextStates[p->state];
+    }
+    else
+    {
+      RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1);
+      if (pos < LZMA_NUM_REPS)
+      {
+        RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1);
+        if (pos == 0)
+        {
+          RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0);
+          RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1));
+        }
+        else
+        {
+          UInt32 distance = p->reps[pos];
+          RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1);
+          if (pos == 1)
+            RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0);
+          else
+          {
+            RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1);
+            RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2);
+            if (pos == 3)
+              p->reps[3] = p->reps[2];
+            p->reps[2] = p->reps[1];
+          }
+          p->reps[1] = p->reps[0];
+          p->reps[0] = distance;
+        }
+        if (len == 1)
+          p->state = kShortRepNextStates[p->state];
+        else
+        {
+          LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices);
+          p->state = kRepNextStates[p->state];
+        }
+      }
+      else
+      {
+        UInt32 posSlot;
+        RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0);
+        p->state = kMatchNextStates[p->state];
+        LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices);
+        pos -= LZMA_NUM_REPS;
+        GetPosSlot(pos, posSlot);
+        RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot);
+        
+        if (posSlot >= kStartPosModelIndex)
+        {
+          UInt32 footerBits = ((posSlot >> 1) - 1);
+          UInt32 base = ((2 | (posSlot & 1)) << footerBits);
+          UInt32 posReduced = pos - base;
+
+          if (posSlot < kEndPosModelIndex)
+            RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced);
+          else
+          {
+            RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits);
+            RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask);
+            p->alignPriceCount++;
+          }
+        }
+        p->reps[3] = p->reps[2];
+        p->reps[2] = p->reps[1];
+        p->reps[1] = p->reps[0];
+        p->reps[0] = pos;
+        p->matchPriceCount++;
+      }
+    }
+    p->additionalOffset -= len;
+    nowPos32 += len;
+    if (p->additionalOffset == 0)
+    {
+      UInt32 processed;
+      if (!p->fastMode)
+      {
+        if (p->matchPriceCount >= (1 << 7))
+          FillDistancesPrices(p);
+        if (p->alignPriceCount >= kAlignTableSize)
+          FillAlignPrices(p);
+      }
+      if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0)
+        break;
+      processed = nowPos32 - startPos32;
+      if (useLimits)
+      {
+        if (processed + kNumOpts + 300 >= maxUnpackSize ||
+            RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize)
+          break;
+      }
+      else if (processed >= (1 << 15))
+      {
+        p->nowPos64 += nowPos32 - startPos32;
+        return CheckErrors(p);
+      }
+    }
+  }
+  p->nowPos64 += nowPos32 - startPos32;
+  return Flush(p, nowPos32);
+}
+
+#define kBigHashDicLimit ((UInt32)1 << 24)
+
+static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig)
+{
+  UInt32 beforeSize = kNumOpts;
+  Bool btMode;
+  if (!RangeEnc_Alloc(&p->rc, alloc))
+    return SZ_ERROR_MEM;
+  btMode = (p->matchFinderBase.btMode != 0);
+  #ifdef COMPRESS_MF_MT
+  p->mtMode = (p->multiThread && !p->fastMode && btMode);
+  #endif
+
+  {
+    unsigned lclp = p->lc + p->lp;
+    if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp)
+    {
+      LzmaEnc_FreeLits(p, alloc);
+      p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb));
+      p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb));
+      if (p->litProbs == 0 || p->saveState.litProbs == 0)
+      {
+        LzmaEnc_FreeLits(p, alloc);
+        return SZ_ERROR_MEM;
+      }
+      p->lclp = lclp;
+    }
+  }
+
+  p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit);
+
+  if (beforeSize + p->dictSize < keepWindowSize)
+    beforeSize = keepWindowSize - p->dictSize;
+
+  #ifdef COMPRESS_MF_MT
+  if (p->mtMode)
+  {
+    RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig));
+    p->matchFinderObj = &p->matchFinderMt;
+    MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder);
+  }
+  else
+  #endif
+  {
+    if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig))
+      return SZ_ERROR_MEM;
+    p->matchFinderObj = &p->matchFinderBase;
+    MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder);
+  }
+  return SZ_OK;
+}
+
+void LzmaEnc_Init(CLzmaEnc *p)
+{
+  UInt32 i;
+  p->state = 0;
+  for (i = 0 ; i < LZMA_NUM_REPS; i++)
+    p->reps[i] = 0;
+
+  RangeEnc_Init(&p->rc);
+
+
+  for (i = 0; i < kNumStates; i++)
+  {
+    UInt32 j;
+    for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++)
+    {
+      p->isMatch[i][j] = kProbInitValue;
+      p->isRep0Long[i][j] = kProbInitValue;
+    }
+    p->isRep[i] = kProbInitValue;
+    p->isRepG0[i] = kProbInitValue;
+    p->isRepG1[i] = kProbInitValue;
+    p->isRepG2[i] = kProbInitValue;
+  }
+
+  {
+    UInt32 num = 0x300 << (p->lp + p->lc);
+    for (i = 0; i < num; i++)
+      p->litProbs[i] = kProbInitValue;
+  }
+
+  {
+    for (i = 0; i < kNumLenToPosStates; i++)
+    {
+      CLzmaProb *probs = p->posSlotEncoder[i];
+      UInt32 j;
+      for (j = 0; j < (1 << kNumPosSlotBits); j++)
+        probs[j] = kProbInitValue;
+    }
+  }
+  {
+    for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++)
+      p->posEncoders[i] = kProbInitValue;
+  }
+
+  LenEnc_Init(&p->lenEnc.p);
+  LenEnc_Init(&p->repLenEnc.p);
+
+  for (i = 0; i < (1 << kNumAlignBits); i++)
+    p->posAlignEncoder[i] = kProbInitValue;
+
+  p->optimumEndIndex = 0;
+  p->optimumCurrentIndex = 0;
+  p->additionalOffset = 0;
+
+  p->pbMask = (1 << p->pb) - 1;
+  p->lpMask = (1 << p->lp) - 1;
+}
+
+void LzmaEnc_InitPrices(CLzmaEnc *p)
+{
+  if (!p->fastMode)
+  {
+    FillDistancesPrices(p);
+    FillAlignPrices(p);
+  }
+
+  p->lenEnc.tableSize =
+  p->repLenEnc.tableSize =
+      p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN;
+  LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices);
+  LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices);
+}
+
+static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig)
+{
+  UInt32 i;
+  for (i = 0; i < (UInt32)kDicLogSizeMaxCompress; i++)
+    if (p->dictSize <= ((UInt32)1 << i))
+      break;
+  p->distTableSize = i * 2;
+
+  p->finished = False;
+  p->result = SZ_OK;
+  RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig));
+  LzmaEnc_Init(p);
+  LzmaEnc_InitPrices(p);
+  p->nowPos64 = 0;
+  return SZ_OK;
+}
+
+static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqInStream *inStream, ISeqOutStream *outStream,
+    ISzAlloc *alloc, ISzAlloc *allocBig)
+{
+  CLzmaEnc *p = (CLzmaEnc *)pp;
+  p->inStream = inStream;
+  p->rc.outStream = outStream;
+  return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig);
+}
+
+SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp,
+    ISeqInStream *inStream, UInt32 keepWindowSize,
+    ISzAlloc *alloc, ISzAlloc *allocBig)
+{
+  CLzmaEnc *p = (CLzmaEnc *)pp;
+  p->inStream = inStream;
+  return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig);
+}
+
+static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen)
+{
+  p->seqBufInStream.funcTable.Read = MyRead;
+  p->seqBufInStream.data = src;
+  p->seqBufInStream.rem = srcLen;
+}
+
+SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen,
+    UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig)
+{
+  CLzmaEnc *p = (CLzmaEnc *)pp;
+  LzmaEnc_SetInputBuf(p, src, srcLen);
+  p->inStream = &p->seqBufInStream.funcTable;
+  return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig);
+}
+
+void LzmaEnc_Finish(CLzmaEncHandle pp)
+{
+  #ifdef COMPRESS_MF_MT
+  CLzmaEnc *p = (CLzmaEnc *)pp;
+  if (p->mtMode)
+    MatchFinderMt_ReleaseStream(&p->matchFinderMt);
+  #else
+  pp = pp;
+  #endif
+}
+
+typedef struct _CSeqOutStreamBuf
+{
+  ISeqOutStream funcTable;
+  Byte *data;
+  SizeT rem;
+  Bool overflow;
+} CSeqOutStreamBuf;
+
+static size_t MyWrite(void *pp, const void *data, size_t size)
+{
+  CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp;
+  if (p->rem < size)
+  {
+    size = p->rem;
+    p->overflow = True;
+  }
+  memcpy(p->data, data, size);
+  p->rem -= size;
+  p->data += size;
+  return size;
+}
+
+
+UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp)
+{
+  const CLzmaEnc *p = (CLzmaEnc *)pp;
+  return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj);
+}
+
+const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp)
+{
+  const CLzmaEnc *p = (CLzmaEnc *)pp;
+  return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset;
+}
+
+SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit,
+    Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize)
+{
+  CLzmaEnc *p = (CLzmaEnc *)pp;
+  UInt64 nowPos64;
+  SRes res;
+  CSeqOutStreamBuf outStream;
+
+  outStream.funcTable.Write = MyWrite;
+  outStream.data = dest;
+  outStream.rem = *destLen;
+  outStream.overflow = False;
+
+  p->writeEndMark = False;
+  p->finished = False;
+  p->result = SZ_OK;
+
+  if (reInit)
+    LzmaEnc_Init(p);
+  LzmaEnc_InitPrices(p);
+  nowPos64 = p->nowPos64;
+  RangeEnc_Init(&p->rc);
+  p->rc.outStream = &outStream.funcTable;
+
+  res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize);
+  
+  *unpackSize = (UInt32)(p->nowPos64 - nowPos64);
+  *destLen -= outStream.rem;
+  if (outStream.overflow)
+    return SZ_ERROR_OUTPUT_EOF;
+
+  return res;
+}
+
+SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress,
+    ISzAlloc *alloc, ISzAlloc *allocBig)
+{
+  CLzmaEnc *p = (CLzmaEnc *)pp;
+  SRes res = SZ_OK;
+
+  #ifdef COMPRESS_MF_MT
+  Byte allocaDummy[0x300];
+  int i = 0;
+  for (i = 0; i < 16; i++)
+    allocaDummy[i] = (Byte)i;
+  #endif
+
+  RINOK(LzmaEnc_Prepare(pp, inStream, outStream, alloc, allocBig));
+
+  for (;;)
+  {
+    res = LzmaEnc_CodeOneBlock(p, False, 0, 0);
+    if (res != SZ_OK || p->finished != 0)
+      break;
+    if (progress != 0)
+    {
+      res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc));
+      if (res != SZ_OK)
+      {
+        res = SZ_ERROR_PROGRESS;
+        break;
+      }
+    }
+  }
+  LzmaEnc_Finish(pp);
+  return res;
+}
+
+SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size)
+{
+  CLzmaEnc *p = (CLzmaEnc *)pp;
+  int i;
+  UInt32 dictSize = p->dictSize;
+  if (*size < LZMA_PROPS_SIZE)
+    return SZ_ERROR_PARAM;
+  *size = LZMA_PROPS_SIZE;
+  props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc);
+
+  for (i = 11; i <= 30; i++)
+  {
+    if (dictSize <= ((UInt32)2 << i))
+    {
+      dictSize = (2 << i);
+      break;
+    }
+    if (dictSize <= ((UInt32)3 << i))
+    {
+      dictSize = (3 << i);
+      break;
+    }
+  }
+
+  for (i = 0; i < 4; i++)
+    props[1 + i] = (Byte)(dictSize >> (8 * i));
+  return SZ_OK;
+}
+
+SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
+    int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig)
+{
+  SRes res;
+  CLzmaEnc *p = (CLzmaEnc *)pp;
+
+  CSeqOutStreamBuf outStream;
+
+  LzmaEnc_SetInputBuf(p, src, srcLen);
+
+  outStream.funcTable.Write = MyWrite;
+  outStream.data = dest;
+  outStream.rem = *destLen;
+  outStream.overflow = False;
+
+  p->writeEndMark = writeEndMark;
+  res = LzmaEnc_Encode(pp, &outStream.funcTable, &p->seqBufInStream.funcTable,
+      progress, alloc, allocBig);
+
+  *destLen -= outStream.rem;
+  if (outStream.overflow)
+    return SZ_ERROR_OUTPUT_EOF;
+  return res;
+}
+
+SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
+    const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark,
+    ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig)
+{
+  CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc);
+  SRes res;
+  if (p == 0)
+    return SZ_ERROR_MEM;
+
+  res = LzmaEnc_SetProps(p, props);
+  if (res == SZ_OK)
+  {
+    res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize);
+    if (res == SZ_OK)
+      res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen,
+          writeEndMark, progress, alloc, allocBig);
+  }
+
+  LzmaEnc_Destroy(p, alloc, allocBig);
+  return res;
+}
diff --git a/third_party/lzma/v4_65/files/C/LzmaEnc.h b/third_party/lzma/v4_65/files/C/LzmaEnc.h
new file mode 100644
index 0000000..bfbc7d2
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaEnc.h
@@ -0,0 +1,72 @@
+/*  LzmaEnc.h -- LZMA Encoder
+2008-10-04 : Igor Pavlov : Public domain */
+
+#ifndef __LZMAENC_H
+#define __LZMAENC_H
+
+#include "Types.h"
+
+#define LZMA_PROPS_SIZE 5
+
+typedef struct _CLzmaEncProps
+{
+  int level;       /*  0 <= level <= 9 */
+  UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version
+                      (1 << 12) <= dictSize <= (1 << 30) for 64-bit version
+                       default = (1 << 24) */
+  int lc;          /* 0 <= lc <= 8, default = 3 */
+  int lp;          /* 0 <= lp <= 4, default = 0 */
+  int pb;          /* 0 <= pb <= 4, default = 2 */
+  int algo;        /* 0 - fast, 1 - normal, default = 1 */
+  int fb;          /* 5 <= fb <= 273, default = 32 */
+  int btMode;      /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */
+  int numHashBytes; /* 2, 3 or 4, default = 4 */
+  UInt32 mc;        /* 1 <= mc <= (1 << 30), default = 32 */
+  unsigned writeEndMark;  /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */
+  int numThreads;  /* 1 or 2, default = 2 */
+} CLzmaEncProps;
+
+void LzmaEncProps_Init(CLzmaEncProps *p);
+void LzmaEncProps_Normalize(CLzmaEncProps *p);
+UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2);
+
+
+/* ---------- CLzmaEncHandle Interface ---------- */
+
+/* LzmaEnc_* functions can return the following exit codes:
+Returns:
+  SZ_OK           - OK
+  SZ_ERROR_MEM    - Memory allocation error
+  SZ_ERROR_PARAM  - Incorrect paramater in props
+  SZ_ERROR_WRITE  - Write callback error.
+  SZ_ERROR_PROGRESS - some break from progress callback
+  SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version)
+*/
+
+typedef void * CLzmaEncHandle;
+
+CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc);
+void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig);
+SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props);
+SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size);
+SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream,
+    ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
+SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
+    int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
+
+/* ---------- One Call Interface ---------- */
+
+/* LzmaEncode
+Return code:
+  SZ_OK               - OK
+  SZ_ERROR_MEM        - Memory allocation error
+  SZ_ERROR_PARAM      - Incorrect paramater
+  SZ_ERROR_OUTPUT_EOF - output buffer overflow
+  SZ_ERROR_THREAD     - errors in multithreading functions (only for Mt version)
+*/
+
+SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
+    const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark,
+    ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/LzmaLib.c b/third_party/lzma/v4_65/files/C/LzmaLib.c
new file mode 100644
index 0000000..02a5118
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaLib.c
@@ -0,0 +1,46 @@
+/* LzmaLib.c -- LZMA library wrapper
+2008-08-05
+Igor Pavlov
+Public domain */
+
+#include "LzmaEnc.h"
+#include "LzmaDec.h"
+#include "Alloc.h"
+#include "LzmaLib.h"
+
+static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); }
+static void SzFree(void *p, void *address) { p = p; MyFree(address); }
+static ISzAlloc g_Alloc = { SzAlloc, SzFree };
+
+MY_STDAPI LzmaCompress(unsigned char *dest, size_t  *destLen, const unsigned char *src, size_t  srcLen,
+  unsigned char *outProps, size_t *outPropsSize,
+  int level, /* 0 <= level <= 9, default = 5 */
+  unsigned dictSize, /* use (1 << N) or (3 << N). 4 KB < dictSize <= 128 MB */
+  int lc, /* 0 <= lc <= 8, default = 3  */
+  int lp, /* 0 <= lp <= 4, default = 0  */
+  int pb, /* 0 <= pb <= 4, default = 2  */
+  int fb,  /* 5 <= fb <= 273, default = 32 */
+  int numThreads /* 1 or 2, default = 2 */
+)
+{
+  CLzmaEncProps props;
+  LzmaEncProps_Init(&props);
+  props.level = level;
+  props.dictSize = dictSize;
+  props.lc = lc;
+  props.lp = lp;
+  props.pb = pb;
+  props.fb = fb;
+  props.numThreads = numThreads;
+
+  return LzmaEncode(dest, destLen, src, srcLen, &props, outProps, outPropsSize, 0,
+      NULL, &g_Alloc, &g_Alloc);
+}
+
+
+MY_STDAPI LzmaUncompress(unsigned char *dest, size_t  *destLen, const unsigned char *src, size_t  *srcLen,
+  const unsigned char *props, size_t propsSize)
+{
+  ELzmaStatus status;
+  return LzmaDecode(dest, destLen, src, srcLen, props, (unsigned)propsSize, LZMA_FINISH_ANY, &status, &g_Alloc);
+}
diff --git a/third_party/lzma/v4_65/files/C/LzmaLib.h b/third_party/lzma/v4_65/files/C/LzmaLib.h
new file mode 100644
index 0000000..5c9eeec
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaLib.h
@@ -0,0 +1,135 @@
+/* LzmaLib.h -- LZMA library interface
+2008-08-05
+Igor Pavlov
+Public domain */
+
+#ifndef __LZMALIB_H
+#define __LZMALIB_H
+
+#include "Types.h"
+
+#ifdef __cplusplus
+  #define MY_EXTERN_C extern "C"
+#else
+  #define MY_EXTERN_C extern
+#endif
+
+#define MY_STDAPI MY_EXTERN_C int MY_STD_CALL
+
+#define LZMA_PROPS_SIZE 5
+
+/*
+RAM requirements for LZMA:
+  for compression:   (dictSize * 11.5 + 6 MB) + state_size
+  for decompression: dictSize + state_size
+    state_size = (4 + (1.5 << (lc + lp))) KB
+    by default (lc=3, lp=0), state_size = 16 KB.
+
+LZMA properties (5 bytes) format
+    Offset Size  Description
+      0     1    lc, lp and pb in encoded form.
+      1     4    dictSize (little endian).
+*/
+
+/*
+LzmaCompress
+------------
+
+outPropsSize -
+     In:  the pointer to the size of outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5.
+     Out: the pointer to the size of written properties in outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5.
+
+  LZMA Encoder will use defult values for any parameter, if it is
+  -1  for any from: level, loc, lp, pb, fb, numThreads
+   0  for dictSize
+  
+level - compression level: 0 <= level <= 9;
+
+  level dictSize algo  fb
+    0:    16 KB   0    32
+    1:    64 KB   0    32
+    2:   256 KB   0    32
+    3:     1 MB   0    32
+    4:     4 MB   0    32
+    5:    16 MB   1    32
+    6:    32 MB   1    32
+    7+:   64 MB   1    64
+ 
+  The default value for "level" is 5.
+
+  algo = 0 means fast method
+  algo = 1 means normal method
+
+dictSize - The dictionary size in bytes. The maximum value is
+        128 MB = (1 << 27) bytes for 32-bit version
+          1 GB = (1 << 30) bytes for 64-bit version
+     The default value is 16 MB = (1 << 24) bytes.
+     It's recommended to use the dictionary that is larger than 4 KB and
+     that can be calculated as (1 << N) or (3 << N) sizes.
+
+lc - The number of literal context bits (high bits of previous literal).
+     It can be in the range from 0 to 8. The default value is 3.
+     Sometimes lc=4 gives the gain for big files.
+
+lp - The number of literal pos bits (low bits of current position for literals).
+     It can be in the range from 0 to 4. The default value is 0.
+     The lp switch is intended for periodical data when the period is equal to 2^lp.
+     For example, for 32-bit (4 bytes) periodical data you can use lp=2. Often it's
+     better to set lc=0, if you change lp switch.
+
+pb - The number of pos bits (low bits of current position).
+     It can be in the range from 0 to 4. The default value is 2.
+     The pb switch is intended for periodical data when the period is equal 2^pb.
+
+fb - Word size (the number of fast bytes).
+     It can be in the range from 5 to 273. The default value is 32.
+     Usually, a big number gives a little bit better compression ratio and
+     slower compression process.
+
+numThreads - The number of thereads. 1 or 2. The default value is 2.
+     Fast mode (algo = 0) can use only 1 thread.
+
+Out:
+  destLen  - processed output size
+Returns:
+  SZ_OK               - OK
+  SZ_ERROR_MEM        - Memory allocation error
+  SZ_ERROR_PARAM      - Incorrect paramater
+  SZ_ERROR_OUTPUT_EOF - output buffer overflow
+  SZ_ERROR_THREAD     - errors in multithreading functions (only for Mt version)
+*/
+
+MY_STDAPI LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen,
+  unsigned char *outProps, size_t *outPropsSize, /* *outPropsSize must be = 5 */
+  int level,      /* 0 <= level <= 9, default = 5 */
+  unsigned dictSize,  /* default = (1 << 24) */
+  int lc,        /* 0 <= lc <= 8, default = 3  */
+  int lp,        /* 0 <= lp <= 4, default = 0  */
+  int pb,        /* 0 <= pb <= 4, default = 2  */
+  int fb,        /* 5 <= fb <= 273, default = 32 */
+  int numThreads /* 1 or 2, default = 2 */
+  );
+
+/*
+LzmaUncompress
+--------------
+In:
+  dest     - output data
+  destLen  - output data size
+  src      - input data
+  srcLen   - input data size
+Out:
+  destLen  - processed output size
+  srcLen   - processed input size
+Returns:
+  SZ_OK                - OK
+  SZ_ERROR_DATA        - Data error
+  SZ_ERROR_MEM         - Memory allocation arror
+  SZ_ERROR_UNSUPPORTED - Unsupported properties
+  SZ_ERROR_INPUT_EOF   - it needs more bytes in input buffer (src)
+*/
+
+MY_STDAPI LzmaUncompress(unsigned char *dest, size_t *destLen, const unsigned char *src, SizeT *srcLen,
+  const unsigned char *props, size_t propsSize);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/LzmaLib/LzmaLib.def b/third_party/lzma/v4_65/files/C/LzmaLib/LzmaLib.def
new file mode 100644
index 0000000..8bc6add
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaLib/LzmaLib.def
@@ -0,0 +1,4 @@
+EXPORTS
+  LzmaCompress
+  LzmaUncompress
+
diff --git a/third_party/lzma/v4_65/files/C/LzmaLib/LzmaLib.dsp b/third_party/lzma/v4_65/files/C/LzmaLib/LzmaLib.dsp
new file mode 100644
index 0000000..3ba6d25
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaLib/LzmaLib.dsp
@@ -0,0 +1,178 @@
+# Microsoft Developer Studio Project File - Name="LzmaLib" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=LzmaLib - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "LzmaLib.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "LzmaLib.mak" CFG="LzmaLib - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "LzmaLib - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "LzmaLib - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "LzmaLib - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LZMALIB_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /Gr /MT /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LZMALIB_EXPORTS" /D "COMPRESS_MF_MT" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x419 /d "NDEBUG"
+# ADD RSC /l 0x419 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"C:\Util\LZMA.dll" /opt:NOWIN98
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF  "$(CFG)" == "LzmaLib - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LZMALIB_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /ZI /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LZMALIB_EXPORTS" /D "COMPRESS_MF_MT" /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x419 /d "_DEBUG"
+# ADD RSC /l 0x419 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"C:\Util\LZMA.dll" /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "LzmaLib - Win32 Release"
+# Name "LzmaLib - Win32 Debug"
+# Begin Group "Spec"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\LzmaLib.def
+# End Source File
+# Begin Source File
+
+SOURCE=.\LzmaLibExports.c
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\Alloc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\Alloc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\IStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzFind.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzFind.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzFindMt.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzFindMt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzHash.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzmaDec.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzmaDec.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzmaEnc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzmaEnc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzmaLib.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzmaLib.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# Begin Source File
+
+SOURCE=..\Threads.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\Threads.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Types.h
+# End Source File
+# End Target
+# End Project
diff --git a/third_party/lzma/v4_65/files/C/LzmaLib/LzmaLib.dsw b/third_party/lzma/v4_65/files/C/LzmaLib/LzmaLib.dsw
new file mode 100644
index 0000000..6faf333
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaLib/LzmaLib.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "LzmaLib"=.\LzmaLib.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/third_party/lzma/v4_65/files/C/LzmaLib/LzmaLibExports.c b/third_party/lzma/v4_65/files/C/LzmaLib/LzmaLibExports.c
new file mode 100644
index 0000000..845545d
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaLib/LzmaLibExports.c
@@ -0,0 +1,12 @@
+/* LzmaLibExports.c -- LZMA library DLL Entry point
+2008-10-04 : Igor Pavlov : Public domain */
+
+#include <windows.h>
+
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
+{
+  hInstance = hInstance;
+  dwReason = dwReason;
+  lpReserved = lpReserved;
+  return TRUE;
+}
diff --git a/third_party/lzma/v4_65/files/C/LzmaLib/makefile b/third_party/lzma/v4_65/files/C/LzmaLib/makefile
new file mode 100644
index 0000000..1e6b40c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaLib/makefile
@@ -0,0 +1,37 @@
+MY_STATIC_LINK=1
+SLIB = sLZMA.lib
+PROG = LZMA.dll
+SLIBPATH = $O\$(SLIB)
+
+DEF_FILE = LzmaLib.def
+CFLAGS = $(CFLAGS) \
+  -DCOMPRESS_MF_MT \
+
+LIBS = $(LIBS) oleaut32.lib
+
+LIB_OBJS = \
+  $O\LzmaLibExports.obj \
+
+C_OBJS = \
+  $O\Alloc.obj \
+  $O\LzFind.obj \
+  $O\LzFindMt.obj \
+  $O\LzmaDec.obj \
+  $O\LzmaEnc.obj \
+  $O\LzmaLib.obj \
+  $O\Threads.obj \
+
+OBJS = \
+  $(LIB_OBJS) \
+  $(C_OBJS) \
+  $O\resource.res
+
+!include "../../CPP/Build.mak"
+
+$(SLIBPATH): $O $(OBJS)
+	lib -out:$(SLIBPATH) $(OBJS) $(LIBS)
+
+$(LIB_OBJS): $(*B).c
+	$(COMPL_O2)
+$(C_OBJS): ../$(*B).c
+	$(COMPL_O2)
diff --git a/third_party/lzma/v4_65/files/C/LzmaLib/resource.rc b/third_party/lzma/v4_65/files/C/LzmaLib/resource.rc
new file mode 100644
index 0000000..1e48916
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaLib/resource.rc
@@ -0,0 +1,4 @@
+#include "../../CPP/7zip/MyVersionInfo.rc"
+
+MY_VERSION_INFO_DLL("LZMA library", "LZMA")
+
diff --git a/third_party/lzma/v4_65/files/C/LzmaUtil/Lzma86Dec.c b/third_party/lzma/v4_65/files/C/LzmaUtil/Lzma86Dec.c
new file mode 100644
index 0000000..b801dd1
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaUtil/Lzma86Dec.c
@@ -0,0 +1,61 @@
+/* Lzma86Dec.c -- LZMA + x86 (BCJ) Filter Decoder
+2008-04-07
+Igor Pavlov
+Public domain */
+
+#include "Lzma86Dec.h"
+
+#include "../Alloc.h"
+#include "../Bra.h"
+#include "../LzmaDec.h"
+
+#define LZMA86_SIZE_OFFSET (1 + LZMA_PROPS_SIZE)
+#define LZMA86_HEADER_SIZE (LZMA86_SIZE_OFFSET + 8)
+
+static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); }
+static void SzFree(void *p, void *address) { p = p; MyFree(address); }
+static ISzAlloc g_Alloc = { SzAlloc, SzFree };
+
+SRes Lzma86_GetUnpackSize(const Byte *src, SizeT srcLen, UInt64 *unpackSize)
+{
+  unsigned i;
+  if (srcLen < LZMA86_HEADER_SIZE)
+    return SZ_ERROR_INPUT_EOF;
+  *unpackSize = 0;
+  for (i = 0; i < sizeof(UInt64); i++)
+    *unpackSize += ((UInt64)src[LZMA86_SIZE_OFFSET + i]) << (8 * i);
+  return SZ_OK;
+}
+
+SRes Lzma86_Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen)
+{
+  SRes res;
+  int useFilter;
+  SizeT inSizePure;
+  ELzmaStatus status;
+
+  if (*srcLen < LZMA86_HEADER_SIZE)
+    return SZ_ERROR_INPUT_EOF;
+
+  useFilter = src[0];
+
+  if (useFilter > 1)
+  {
+    *destLen = 0;
+    return SZ_ERROR_UNSUPPORTED;
+  }
+
+  inSizePure = *srcLen - LZMA86_HEADER_SIZE;
+  res = LzmaDecode(dest, destLen, src + LZMA86_HEADER_SIZE, &inSizePure,
+      src + 1, LZMA_PROPS_SIZE, LZMA_FINISH_ANY, &status, &g_Alloc);
+  *srcLen = inSizePure + LZMA86_HEADER_SIZE;
+  if (res != SZ_OK)
+    return res;
+  if (useFilter == 1)
+  {
+    UInt32 x86State;
+    x86_Convert_Init(x86State);
+    x86_Convert(dest, *destLen, 0, &x86State, 0);
+  }
+  return SZ_OK;
+}
diff --git a/third_party/lzma/v4_65/files/C/LzmaUtil/Lzma86Dec.h b/third_party/lzma/v4_65/files/C/LzmaUtil/Lzma86Dec.h
new file mode 100644
index 0000000..f711821
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaUtil/Lzma86Dec.h
@@ -0,0 +1,45 @@
+/* Lzma86Dec.h -- LZMA + x86 (BCJ) Filter Decoder
+2008-08-05
+Igor Pavlov
+Public domain */
+
+#ifndef __LZMA86DEC_H
+#define __LZMA86DEC_H
+
+#include "../Types.h"
+
+/*
+Lzma86_GetUnpackSize:
+  In:
+    src      - input data
+    srcLen   - input data size
+  Out:
+    unpackSize - size of uncompressed stream
+  Return code:
+    SZ_OK               - OK
+    SZ_ERROR_INPUT_EOF  - Error in headers
+*/
+
+SRes Lzma86_GetUnpackSize(const Byte *src, SizeT srcLen, UInt64 *unpackSize);
+
+/*
+Lzma86_Decode:
+  In:
+    dest     - output data
+    destLen  - output data size
+    src      - input data
+    srcLen   - input data size
+  Out:
+    destLen  - processed output size
+    srcLen   - processed input size
+  Return code:
+    SZ_OK           - OK
+    SZ_ERROR_DATA  - Data error
+    SZ_ERROR_MEM   - Memory allocation error
+    SZ_ERROR_UNSUPPORTED - unsupported file
+    SZ_ERROR_INPUT_EOF - it needs more bytes in input buffer
+*/
+
+SRes Lzma86_Decode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/LzmaUtil/Lzma86Enc.c b/third_party/lzma/v4_65/files/C/LzmaUtil/Lzma86Enc.c
new file mode 100644
index 0000000..efc81ea
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaUtil/Lzma86Enc.c
@@ -0,0 +1,113 @@
+/* Lzma86Enc.c -- LZMA + x86 (BCJ) Filter Encoder
+2008-08-05
+Igor Pavlov
+Public domain */
+
+#include <string.h>
+
+#include "Lzma86Enc.h"
+
+#include "../Alloc.h"
+#include "../Bra.h"
+#include "../LzmaEnc.h"
+
+#define SZE_OUT_OVERFLOW SZE_DATA_ERROR
+
+static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); }
+static void SzFree(void *p, void *address) { p = p; MyFree(address); }
+static ISzAlloc g_Alloc = { SzAlloc, SzFree };
+
+#define LZMA86_SIZE_OFFSET (1 + LZMA_PROPS_SIZE)
+#define LZMA86_HEADER_SIZE (LZMA86_SIZE_OFFSET + 8)
+
+int Lzma86_Encode(Byte *dest, size_t *destLen, const Byte *src, size_t srcLen,
+    int level, UInt32 dictSize, int filterMode)
+{
+  size_t outSize2 = *destLen;
+  Byte *filteredStream;
+  Bool useFilter;
+  int mainResult = SZ_ERROR_OUTPUT_EOF;
+  CLzmaEncProps props;
+  LzmaEncProps_Init(&props);
+  props.level = level;
+  props.dictSize = dictSize;
+  
+  *destLen = 0;
+  if (outSize2 < LZMA86_HEADER_SIZE)
+    return SZ_ERROR_OUTPUT_EOF;
+
+  {
+    int i;
+    UInt64 t = srcLen;
+    for (i = 0; i < 8; i++, t >>= 8)
+      dest[LZMA86_SIZE_OFFSET + i] = (Byte)t;
+  }
+
+  filteredStream = 0;
+  useFilter = (filterMode != SZ_FILTER_NO);
+  if (useFilter)
+  {
+    if (srcLen != 0)
+    {
+      filteredStream = (Byte *)MyAlloc(srcLen);
+      if (filteredStream == 0)
+        return SZ_ERROR_MEM;
+      memcpy(filteredStream, src, srcLen);
+    }
+    {
+      UInt32 x86State;
+      x86_Convert_Init(x86State);
+      x86_Convert(filteredStream, srcLen, 0, &x86State, 1);
+    }
+  }
+
+  {
+    size_t minSize = 0;
+    Bool bestIsFiltered = False;
+
+    /* passes for SZ_FILTER_AUTO:
+        0 - BCJ + LZMA
+        1 - LZMA
+        2 - BCJ + LZMA agaian, if pass 0 (BCJ + LZMA) is better.
+    */
+    int numPasses = (filterMode == SZ_FILTER_AUTO) ? 3 : 1;
+
+    int i;
+    for (i = 0; i < numPasses; i++)
+    {
+      size_t outSizeProcessed = outSize2 - LZMA86_HEADER_SIZE;
+      size_t outPropsSize = 5;
+      SRes curRes;
+      Bool curModeIsFiltered = (numPasses > 1 && i == numPasses - 1);
+      if (curModeIsFiltered && !bestIsFiltered)
+        break;
+      if (useFilter && i == 0)
+        curModeIsFiltered = True;
+      
+      curRes = LzmaEncode(dest + LZMA86_HEADER_SIZE, &outSizeProcessed,
+          curModeIsFiltered ? filteredStream : src, srcLen,
+          &props, dest + 1, &outPropsSize, 0,
+          NULL, &g_Alloc, &g_Alloc);
+      
+      if (curRes != SZ_ERROR_OUTPUT_EOF)
+      {
+        if (curRes != SZ_OK)
+        {
+          mainResult = curRes;
+          break;
+        }
+        if (outSizeProcessed <= minSize || mainResult != SZ_OK)
+        {
+          minSize = outSizeProcessed;
+          bestIsFiltered = curModeIsFiltered;
+          mainResult = SZ_OK;
+        }
+      }
+    }
+    dest[0] = (bestIsFiltered ? 1 : 0);
+    *destLen = LZMA86_HEADER_SIZE + minSize;
+  }
+  if (useFilter)
+    MyFree(filteredStream);
+  return mainResult;
+}
diff --git a/third_party/lzma/v4_65/files/C/LzmaUtil/Lzma86Enc.h b/third_party/lzma/v4_65/files/C/LzmaUtil/Lzma86Enc.h
new file mode 100644
index 0000000..10be1cd
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaUtil/Lzma86Enc.h
@@ -0,0 +1,72 @@
+/* Lzma86Enc.h -- LZMA + x86 (BCJ) Filter Encoder
+2008-08-05
+Igor Pavlov
+Public domain */
+
+#ifndef __LZMA86ENC_H
+#define __LZMA86ENC_H
+
+#include "../Types.h"
+
+/*
+It's an example for LZMA + x86 Filter use.
+You can use .lzma86 extension, if you write that stream to file.
+.lzma86 header adds one additional byte to standard .lzma header.
+.lzma86 header (14 bytes):
+  Offset Size  Description
+    0     1    = 0 - no filter,
+               = 1 - x86 filter
+    1     1    lc, lp and pb in encoded form
+    2     4    dictSize (little endian)
+    6     8    uncompressed size (little endian)
+
+
+Lzma86_Encode
+-------------
+level - compression level: 0 <= level <= 9, the default value for "level" is 5.
+
+
+dictSize - The dictionary size in bytes. The maximum value is
+        128 MB = (1 << 27) bytes for 32-bit version
+          1 GB = (1 << 30) bytes for 64-bit version
+     The default value is 16 MB = (1 << 24) bytes, for level = 5.
+     It's recommended to use the dictionary that is larger than 4 KB and
+     that can be calculated as (1 << N) or (3 << N) sizes.
+     For better compression ratio dictSize must be >= inSize.
+
+filterMode:
+    SZ_FILTER_NO   - no Filter
+    SZ_FILTER_YES  - x86 Filter
+    SZ_FILTER_AUTO - it tries both alternatives to select best.
+              Encoder will use 2 or 3 passes:
+              2 passes when FILTER_NO provides better compression.
+              3 passes when FILTER_YES provides better compression.
+
+Lzma86Encode allocates Data with MyAlloc functions.
+RAM Requirements for compressing:
+  RamSize = dictionarySize * 11.5 + 6MB + FilterBlockSize
+      filterMode     FilterBlockSize
+     SZ_FILTER_NO         0
+     SZ_FILTER_YES      inSize
+     SZ_FILTER_AUTO     inSize
+
+
+Return code:
+  SZ_OK               - OK
+  SZ_ERROR_MEM        - Memory allocation error
+  SZ_ERROR_PARAM      - Incorrect paramater
+  SZ_ERROR_OUTPUT_EOF - output buffer overflow
+  SZ_ERROR_THREAD     - errors in multithreading functions (only for Mt version)
+*/
+
+enum ESzFilterMode
+{
+  SZ_FILTER_NO,
+  SZ_FILTER_YES,
+  SZ_FILTER_AUTO
+};
+
+SRes Lzma86_Encode(Byte *dest, size_t *destLen, const Byte *src, size_t srcLen,
+    int level, UInt32 dictSize, int filterMode);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/C/LzmaUtil/LzmaUtil.c b/third_party/lzma/v4_65/files/C/LzmaUtil/LzmaUtil.c
new file mode 100644
index 0000000..016d7b0
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaUtil/LzmaUtil.c
@@ -0,0 +1,254 @@
+/* LzmaUtil.c -- Test application for LZMA compression
+2008-11-23 : Igor Pavlov : Public domain */
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../Alloc.h"
+#include "../7zFile.h"
+#include "../7zVersion.h"
+#include "../LzmaDec.h"
+#include "../LzmaEnc.h"
+
+const char *kCantReadMessage = "Can not read input file";
+const char *kCantWriteMessage = "Can not write output file";
+const char *kCantAllocateMessage = "Can not allocate memory";
+const char *kDataErrorMessage = "Data error";
+
+static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); }
+static void SzFree(void *p, void *address) { p = p; MyFree(address); }
+static ISzAlloc g_Alloc = { SzAlloc, SzFree };
+
+void PrintHelp(char *buffer)
+{
+  strcat(buffer, "\nLZMA Utility " MY_VERSION_COPYRIGHT_DATE "\n"
+      "\nUsage:  lzma <e|d> inputFile outputFile\n"
+             "  e: encode file\n"
+             "  d: decode file\n");
+}
+
+int PrintError(char *buffer, const char *message)
+{
+  strcat(buffer, "\nError: ");
+  strcat(buffer, message);
+  strcat(buffer, "\n");
+  return 1;
+}
+
+int PrintErrorNumber(char *buffer, SRes val)
+{
+  sprintf(buffer + strlen(buffer), "\nError code: %x\n", (unsigned)val);
+  return 1;
+}
+
+int PrintUserError(char *buffer)
+{
+  return PrintError(buffer, "Incorrect command");
+}
+
+#define IN_BUF_SIZE (1 << 16)
+#define OUT_BUF_SIZE (1 << 16)
+
+static SRes Decode2(CLzmaDec *state, ISeqOutStream *outStream, ISeqInStream *inStream,
+    UInt64 unpackSize)
+{
+  int thereIsSize = (unpackSize != (UInt64)(Int64)-1);
+  Byte inBuf[IN_BUF_SIZE];
+  Byte outBuf[OUT_BUF_SIZE];
+  size_t inPos = 0, inSize = 0, outPos = 0;
+  LzmaDec_Init(state);
+  for (;;)
+  {
+    if (inPos == inSize)
+    {
+      inSize = IN_BUF_SIZE;
+      RINOK(inStream->Read(inStream, inBuf, &inSize));
+      inPos = 0;
+    }
+    {
+      SRes res;
+      SizeT inProcessed = inSize - inPos;
+      SizeT outProcessed = OUT_BUF_SIZE - outPos;
+      ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
+      ELzmaStatus status;
+      if (thereIsSize && outProcessed > unpackSize)
+      {
+        outProcessed = (SizeT)unpackSize;
+        finishMode = LZMA_FINISH_END;
+      }
+      
+      res = LzmaDec_DecodeToBuf(state, outBuf + outPos, &outProcessed,
+        inBuf + inPos, &inProcessed, finishMode, &status);
+      inPos += inProcessed;
+      outPos += outProcessed;
+      unpackSize -= outProcessed;
+      
+      if (outStream)
+        if (outStream->Write(outStream, outBuf, outPos) != outPos)
+          return SZ_ERROR_WRITE;
+        
+      outPos = 0;
+      
+      if (res != SZ_OK || thereIsSize && unpackSize == 0)
+        return res;
+      
+      if (inProcessed == 0 && outProcessed == 0)
+      {
+        if (thereIsSize || status != LZMA_STATUS_FINISHED_WITH_MARK)
+          return SZ_ERROR_DATA;
+        return res;
+      }
+    }
+  }
+}
+
+static SRes Decode(ISeqOutStream *outStream, ISeqInStream *inStream)
+{
+  UInt64 unpackSize;
+  int i;
+  SRes res = 0;
+
+  CLzmaDec state;
+
+  /* header: 5 bytes of LZMA properties and 8 bytes of uncompressed size */
+  unsigned char header[LZMA_PROPS_SIZE + 8];
+
+  /* Read and parse header */
+
+  RINOK(SeqInStream_Read(inStream, header, sizeof(header)));
+
+  unpackSize = 0;
+  for (i = 0; i < 8; i++)
+    unpackSize += (UInt64)header[LZMA_PROPS_SIZE + i] << (i * 8);
+
+  LzmaDec_Construct(&state);
+  RINOK(LzmaDec_Allocate(&state, header, LZMA_PROPS_SIZE, &g_Alloc));
+  res = Decode2(&state, outStream, inStream, unpackSize);
+  LzmaDec_Free(&state, &g_Alloc);
+  return res;
+}
+
+static SRes Encode(ISeqOutStream *outStream, ISeqInStream *inStream, UInt64 fileSize, char *rs)
+{
+  CLzmaEncHandle enc;
+  SRes res;
+  CLzmaEncProps props;
+
+  rs = rs;
+
+  enc = LzmaEnc_Create(&g_Alloc);
+  if (enc == 0)
+    return SZ_ERROR_MEM;
+
+  LzmaEncProps_Init(&props);
+  res = LzmaEnc_SetProps(enc, &props);
+
+  if (res == SZ_OK)
+  {
+    Byte header[LZMA_PROPS_SIZE + 8];
+    size_t headerSize = LZMA_PROPS_SIZE;
+    int i;
+
+    res = LzmaEnc_WriteProperties(enc, header, &headerSize);
+    for (i = 0; i < 8; i++)
+      header[headerSize++] = (Byte)(fileSize >> (8 * i));
+    if (outStream->Write(outStream, header, headerSize) != headerSize)
+      res = SZ_ERROR_WRITE;
+    else
+    {
+      if (res == SZ_OK)
+        res = LzmaEnc_Encode(enc, outStream, inStream, NULL, &g_Alloc, &g_Alloc);
+    }
+  }
+  LzmaEnc_Destroy(enc, &g_Alloc, &g_Alloc);
+  return res;
+}
+
+int main2(int numArgs, const char *args[], char *rs)
+{
+  CFileSeqInStream inStream;
+  CFileOutStream outStream;
+  char c;
+  int res;
+  int encodeMode;
+  Bool useOutFile = False;
+
+  FileSeqInStream_CreateVTable(&inStream);
+  File_Construct(&inStream.file);
+
+  FileOutStream_CreateVTable(&outStream);
+  File_Construct(&outStream.file);
+
+  if (numArgs == 1)
+  {
+    PrintHelp(rs);
+    return 0;
+  }
+
+  if (numArgs < 3 || numArgs > 4 || strlen(args[1]) != 1)
+    return PrintUserError(rs);
+
+  c = args[1][0];
+  encodeMode = (c == 'e' || c == 'E');
+  if (!encodeMode && c != 'd' && c != 'D')
+    return PrintUserError(rs);
+
+  {
+    size_t t4 = sizeof(UInt32);
+    size_t t8 = sizeof(UInt64);
+    if (t4 != 4 || t8 != 8)
+      return PrintError(rs, "Incorrect UInt32 or UInt64");
+  }
+
+  if (InFile_Open(&inStream.file, args[2]) != 0)
+    return PrintError(rs, "Can not open input file");
+
+  if (numArgs > 3)
+  {
+    useOutFile = True;
+    if (OutFile_Open(&outStream.file, args[3]) != 0)
+      return PrintError(rs, "Can not open output file");
+  }
+  else if (encodeMode)
+    PrintUserError(rs);
+
+  if (encodeMode)
+  {
+    UInt64 fileSize;
+    File_GetLength(&inStream.file, &fileSize);
+    res = Encode(&outStream.s, &inStream.s, fileSize, rs);
+  }
+  else
+  {
+    res = Decode(&outStream.s, useOutFile ? &inStream.s : NULL);
+  }
+
+  if (useOutFile)
+    File_Close(&outStream.file);
+  File_Close(&inStream.file);
+
+  if (res != SZ_OK)
+  {
+    if (res == SZ_ERROR_MEM)
+      return PrintError(rs, kCantAllocateMessage);
+    else if (res == SZ_ERROR_DATA)
+      return PrintError(rs, kDataErrorMessage);
+    else if (res == SZ_ERROR_WRITE)
+      return PrintError(rs, kCantWriteMessage);
+    else if (res == SZ_ERROR_READ)
+      return PrintError(rs, kCantReadMessage);
+    return PrintErrorNumber(rs, res);
+  }
+  return 0;
+}
+
+int MY_CDECL main(int numArgs, const char *args[])
+{
+  char rs[800] = { 0 };
+  int res = main2(numArgs, args, rs);
+  printf(rs);
+  return res;
+}
diff --git a/third_party/lzma/v4_65/files/C/LzmaUtil/LzmaUtil.dsp b/third_party/lzma/v4_65/files/C/LzmaUtil/LzmaUtil.dsp
new file mode 100644
index 0000000..faac2e6
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaUtil/LzmaUtil.dsp
@@ -0,0 +1,168 @@
+# Microsoft Developer Studio Project File - Name="LzmaUtil" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=LzmaUtil - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "LzmaUtil.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "LzmaUtil.mak" CFG="LzmaUtil - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "LzmaUtil - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "LzmaUtil - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "LzmaUtil - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x419 /d "NDEBUG"
+# ADD RSC /l 0x419 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386 /out:"c:\util\lzmac.exe"
+
+!ELSEIF  "$(CFG)" == "LzmaUtil - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x419 /d "_DEBUG"
+# ADD RSC /l 0x419 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /out:"c:\util\lzmac.exe" /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "LzmaUtil - Win32 Release"
+# Name "LzmaUtil - Win32 Debug"
+# Begin Source File
+
+SOURCE=..\7zFile.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\7zFile.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\7zStream.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\7zVersion.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Alloc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\Alloc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\CpuArch.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzFind.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzFind.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzFindMt.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzFindMt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzHash.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzmaDec.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzmaDec.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzmaEnc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzmaEnc.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LzmaUtil.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\Threads.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\Threads.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Types.h
+# End Source File
+# End Target
+# End Project
diff --git a/third_party/lzma/v4_65/files/C/LzmaUtil/LzmaUtil.dsw b/third_party/lzma/v4_65/files/C/LzmaUtil/LzmaUtil.dsw
new file mode 100644
index 0000000..c52eaf6
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaUtil/LzmaUtil.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "LzmaUtil"=.\LzmaUtil.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/third_party/lzma/v4_65/files/C/LzmaUtil/makefile b/third_party/lzma/v4_65/files/C/LzmaUtil/makefile
new file mode 100644
index 0000000..fbb98b8
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaUtil/makefile
@@ -0,0 +1,29 @@
+MY_STATIC_LINK=1
+PROG = LZMAc.exe
+
+CFLAGS = $(CFLAGS) \
+  -DCOMPRESS_MF_MT \
+
+LIB_OBJS = \
+  $O\LzmaUtil.obj \
+
+C_OBJS = \
+  $O\Alloc.obj \
+  $O\LzFind.obj \
+  $O\LzFindMt.obj \
+  $O\LzmaDec.obj \
+  $O\LzmaEnc.obj \
+  $O\7zFile.obj \
+  $O\7zStream.obj \
+  $O\Threads.obj \
+
+OBJS = \
+  $(LIB_OBJS) \
+  $(C_OBJS) \
+
+!include "../../CPP/Build.mak"
+
+$(LIB_OBJS): $(*B).c
+	$(COMPL_O2)
+$(C_OBJS): ../$(*B).c
+	$(COMPL_O2)
diff --git a/third_party/lzma/v4_65/files/C/LzmaUtil/makefile.gcc b/third_party/lzma/v4_65/files/C/LzmaUtil/makefile.gcc
new file mode 100644
index 0000000..9fcdead
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/LzmaUtil/makefile.gcc
@@ -0,0 +1,44 @@
+PROG = lzma
+CXX = g++
+LIB =
+RM = rm -f
+CFLAGS = -c -O2 -Wall
+
+OBJS = \
+  LzmaUtil.o \
+  Alloc.o \
+  LzFind.o \
+  LzmaDec.o \
+  LzmaEnc.o \
+  7zFile.o \
+  7zStream.o \
+
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+	$(CXX) -o $(PROG) $(LDFLAGS) $(OBJS) $(LIB) $(LIB2)
+
+LzmaUtil.o: LzmaUtil.c
+	$(CXX) $(CFLAGS) LzmaUtil.c
+
+Alloc.o: ../Alloc.c
+	$(CXX) $(CFLAGS) ../Alloc.c
+
+LzFind.o: ../LzFind.c
+	$(CXX) $(CFLAGS) ../LzFind.c
+
+LzmaDec.o: ../LzmaDec.c
+	$(CXX) $(CFLAGS) ../LzmaDec.c
+
+LzmaEnc.o: ../LzmaEnc.c
+	$(CXX) $(CFLAGS) ../LzmaEnc.c
+
+7zFile.o: ../7zFile.c
+	$(CXX) $(CFLAGS) ../7zFile.c
+
+7zStream.o: ../7zStream.c
+	$(CXX) $(CFLAGS) ../7zStream.c
+
+clean:
+	-$(RM) $(PROG) $(OBJS)
diff --git a/third_party/lzma/v4_65/files/C/Threads.c b/third_party/lzma/v4_65/files/C/Threads.c
new file mode 100644
index 0000000..4fdd69b
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Threads.c
@@ -0,0 +1,109 @@
+/* Threads.c -- multithreading library
+2008-08-05
+Igor Pavlov
+Public domain */
+
+#include "Threads.h"
+#include <process.h>
+
+static WRes GetError()
+{
+  DWORD res = GetLastError();
+  return (res) ? (WRes)(res) : 1;
+}
+
+WRes HandleToWRes(HANDLE h) { return (h != 0) ? 0 : GetError(); }
+WRes BOOLToWRes(BOOL v) { return v ? 0 : GetError(); }
+
+static WRes MyCloseHandle(HANDLE *h)
+{
+  if (*h != NULL)
+    if (!CloseHandle(*h))
+      return GetError();
+  *h = NULL;
+  return 0;
+}
+
+WRes Thread_Create(CThread *thread, THREAD_FUNC_RET_TYPE (THREAD_FUNC_CALL_TYPE *startAddress)(void *), LPVOID parameter)
+{
+  unsigned threadId; /* Windows Me/98/95: threadId parameter may not be NULL in _beginthreadex/CreateThread functions */
+  thread->handle =
+    /* CreateThread(0, 0, startAddress, parameter, 0, &threadId); */
+    (HANDLE)_beginthreadex(NULL, 0, startAddress, parameter, 0, &threadId);
+    /* maybe we must use errno here, but probably GetLastError() is also OK. */
+  return HandleToWRes(thread->handle);
+}
+
+WRes WaitObject(HANDLE h)
+{
+  return (WRes)WaitForSingleObject(h, INFINITE);
+}
+
+WRes Thread_Wait(CThread *thread)
+{
+  if (thread->handle == NULL)
+    return 1;
+  return WaitObject(thread->handle);
+}
+
+WRes Thread_Close(CThread *thread)
+{
+  return MyCloseHandle(&thread->handle);
+}
+
+WRes Event_Create(CEvent *p, BOOL manualReset, int initialSignaled)
+{
+  p->handle = CreateEvent(NULL, manualReset, (initialSignaled ? TRUE : FALSE), NULL);
+  return HandleToWRes(p->handle);
+}
+
+WRes ManualResetEvent_Create(CManualResetEvent *p, int initialSignaled)
+  { return Event_Create(p, TRUE, initialSignaled); }
+WRes ManualResetEvent_CreateNotSignaled(CManualResetEvent *p)
+  { return ManualResetEvent_Create(p, 0); }
+
+WRes AutoResetEvent_Create(CAutoResetEvent *p, int initialSignaled)
+  { return Event_Create(p, FALSE, initialSignaled); }
+WRes AutoResetEvent_CreateNotSignaled(CAutoResetEvent *p)
+  { return AutoResetEvent_Create(p, 0); }
+
+WRes Event_Set(CEvent *p) { return BOOLToWRes(SetEvent(p->handle)); }
+WRes Event_Reset(CEvent *p) { return BOOLToWRes(ResetEvent(p->handle)); }
+WRes Event_Wait(CEvent *p) { return WaitObject(p->handle); }
+WRes Event_Close(CEvent *p) { return MyCloseHandle(&p->handle); }
+
+
+WRes Semaphore_Create(CSemaphore *p, UInt32 initiallyCount, UInt32 maxCount)
+{
+  p->handle = CreateSemaphore(NULL, (LONG)initiallyCount, (LONG)maxCount, NULL);
+  return HandleToWRes(p->handle);
+}
+
+WRes Semaphore_Release(CSemaphore *p, LONG releaseCount, LONG *previousCount)
+{
+  return BOOLToWRes(ReleaseSemaphore(p->handle, releaseCount, previousCount));
+}
+WRes Semaphore_ReleaseN(CSemaphore *p, UInt32 releaseCount)
+{
+  return Semaphore_Release(p, (LONG)releaseCount, NULL);
+}
+WRes Semaphore_Release1(CSemaphore *p)
+{
+  return Semaphore_ReleaseN(p, 1);
+}
+
+WRes Semaphore_Wait(CSemaphore *p) { return WaitObject(p->handle); }
+WRes Semaphore_Close(CSemaphore *p) { return MyCloseHandle(&p->handle); }
+
+WRes CriticalSection_Init(CCriticalSection *p)
+{
+  /* InitializeCriticalSection can raise only STATUS_NO_MEMORY exception */
+  __try
+  {
+    InitializeCriticalSection(p);
+    /* InitializeCriticalSectionAndSpinCount(p, 0); */
+  }
+  __except (EXCEPTION_EXECUTE_HANDLER) { return 1; }
+  return 0;
+}
+
diff --git a/third_party/lzma/v4_65/files/C/Threads.h b/third_party/lzma/v4_65/files/C/Threads.h
new file mode 100644
index 0000000..a823e57
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Threads.h
@@ -0,0 +1,68 @@
+/* Threads.h -- multithreading library
+2008-11-22 : Igor Pavlov : Public domain */
+
+#ifndef __7Z_THRESDS_H
+#define __7Z_THRESDS_H
+
+#include "Types.h"
+
+typedef struct _CThread
+{
+  HANDLE handle;
+} CThread;
+
+#define Thread_Construct(thread) (thread)->handle = NULL
+#define Thread_WasCreated(thread) ((thread)->handle != NULL)
+ 
+typedef unsigned THREAD_FUNC_RET_TYPE;
+#define THREAD_FUNC_CALL_TYPE MY_STD_CALL
+#define THREAD_FUNC_DECL THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE
+
+WRes Thread_Create(CThread *thread, THREAD_FUNC_RET_TYPE (THREAD_FUNC_CALL_TYPE *startAddress)(void *), LPVOID parameter);
+WRes Thread_Wait(CThread *thread);
+WRes Thread_Close(CThread *thread);
+
+typedef struct _CEvent
+{
+  HANDLE handle;
+} CEvent;
+
+typedef CEvent CAutoResetEvent;
+typedef CEvent CManualResetEvent;
+
+#define Event_Construct(event) (event)->handle = NULL
+#define Event_IsCreated(event) ((event)->handle != NULL)
+
+WRes ManualResetEvent_Create(CManualResetEvent *event, int initialSignaled);
+WRes ManualResetEvent_CreateNotSignaled(CManualResetEvent *event);
+WRes AutoResetEvent_Create(CAutoResetEvent *event, int initialSignaled);
+WRes AutoResetEvent_CreateNotSignaled(CAutoResetEvent *event);
+WRes Event_Set(CEvent *event);
+WRes Event_Reset(CEvent *event);
+WRes Event_Wait(CEvent *event);
+WRes Event_Close(CEvent *event);
+
+
+typedef struct _CSemaphore
+{
+  HANDLE handle;
+} CSemaphore;
+
+#define Semaphore_Construct(p) (p)->handle = NULL
+
+WRes Semaphore_Create(CSemaphore *p, UInt32 initiallyCount, UInt32 maxCount);
+WRes Semaphore_ReleaseN(CSemaphore *p, UInt32 num);
+WRes Semaphore_Release1(CSemaphore *p);
+WRes Semaphore_Wait(CSemaphore *p);
+WRes Semaphore_Close(CSemaphore *p);
+
+
+typedef CRITICAL_SECTION CCriticalSection;
+
+WRes CriticalSection_Init(CCriticalSection *p);
+#define CriticalSection_Delete(p) DeleteCriticalSection(p)
+#define CriticalSection_Enter(p) EnterCriticalSection(p)
+#define CriticalSection_Leave(p) LeaveCriticalSection(p)
+
+#endif
+
diff --git a/third_party/lzma/v4_65/files/C/Types.h b/third_party/lzma/v4_65/files/C/Types.h
new file mode 100644
index 0000000..1af5cfc
--- /dev/null
+++ b/third_party/lzma/v4_65/files/C/Types.h
@@ -0,0 +1,208 @@
+/* Types.h -- Basic types
+2008-11-23 : Igor Pavlov : Public domain */
+
+#ifndef __7Z_TYPES_H
+#define __7Z_TYPES_H
+
+#include <stddef.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#define SZ_OK 0
+
+#define SZ_ERROR_DATA 1
+#define SZ_ERROR_MEM 2
+#define SZ_ERROR_CRC 3
+#define SZ_ERROR_UNSUPPORTED 4
+#define SZ_ERROR_PARAM 5
+#define SZ_ERROR_INPUT_EOF 6
+#define SZ_ERROR_OUTPUT_EOF 7
+#define SZ_ERROR_READ 8
+#define SZ_ERROR_WRITE 9
+#define SZ_ERROR_PROGRESS 10
+#define SZ_ERROR_FAIL 11
+#define SZ_ERROR_THREAD 12
+
+#define SZ_ERROR_ARCHIVE 16
+#define SZ_ERROR_NO_ARCHIVE 17
+
+typedef int SRes;
+
+#ifdef _WIN32
+typedef DWORD WRes;
+#else
+typedef int WRes;
+#endif
+
+#ifndef RINOK
+#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; }
+#endif
+
+typedef unsigned char Byte;
+typedef short Int16;
+typedef unsigned short UInt16;
+
+#ifdef _LZMA_UINT32_IS_ULONG
+typedef long Int32;
+typedef unsigned long UInt32;
+#else
+typedef int Int32;
+typedef unsigned int UInt32;
+#endif
+
+#ifdef _SZ_NO_INT_64
+
+/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers.
+   NOTES: Some code will work incorrectly in that case! */
+
+typedef long Int64;
+typedef unsigned long UInt64;
+
+#else
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+typedef __int64 Int64;
+typedef unsigned __int64 UInt64;
+#else
+typedef long long int Int64;
+typedef unsigned long long int UInt64;
+#endif
+
+#endif
+
+#ifdef _LZMA_NO_SYSTEM_SIZE_T
+typedef UInt32 SizeT;
+#else
+typedef size_t SizeT;
+#endif
+
+typedef int Bool;
+#define True 1
+#define False 0
+
+
+#ifdef _MSC_VER
+
+#if _MSC_VER >= 1300
+#define MY_NO_INLINE __declspec(noinline)
+#else
+#define MY_NO_INLINE
+#endif
+
+#define MY_CDECL __cdecl
+#define MY_STD_CALL __stdcall
+#define MY_FAST_CALL MY_NO_INLINE __fastcall
+
+#else
+
+#define MY_CDECL
+#define MY_STD_CALL
+#define MY_FAST_CALL
+
+#endif
+
+
+/* The following interfaces use first parameter as pointer to structure */
+
+typedef struct
+{
+  SRes (*Read)(void *p, void *buf, size_t *size);
+    /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
+       (output(*size) < input(*size)) is allowed */
+} ISeqInStream;
+
+/* it can return SZ_ERROR_INPUT_EOF */
+SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size);
+SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType);
+SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf);
+
+typedef struct
+{
+  size_t (*Write)(void *p, const void *buf, size_t size);
+    /* Returns: result - the number of actually written bytes.
+       (result < size) means error */
+} ISeqOutStream;
+
+typedef enum
+{
+  SZ_SEEK_SET = 0,
+  SZ_SEEK_CUR = 1,
+  SZ_SEEK_END = 2
+} ESzSeek;
+
+typedef struct
+{
+  SRes (*Read)(void *p, void *buf, size_t *size);  /* same as ISeqInStream::Read */
+  SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin);
+} ISeekInStream;
+
+typedef struct
+{
+  SRes (*Look)(void *p, void **buf, size_t *size);
+    /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
+       (output(*size) > input(*size)) is not allowed
+       (output(*size) < input(*size)) is allowed */
+  SRes (*Skip)(void *p, size_t offset);
+    /* offset must be <= output(*size) of Look */
+
+  SRes (*Read)(void *p, void *buf, size_t *size);
+    /* reads directly (without buffer). It's same as ISeqInStream::Read */
+  SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin);
+} ILookInStream;
+
+SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size);
+SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset);
+
+/* reads via ILookInStream::Read */
+SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType);
+SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size);
+
+#define LookToRead_BUF_SIZE (1 << 14)
+
+typedef struct
+{
+  ILookInStream s;
+  ISeekInStream *realStream;
+  size_t pos;
+  size_t size;
+  Byte buf[LookToRead_BUF_SIZE];
+} CLookToRead;
+
+void LookToRead_CreateVTable(CLookToRead *p, int lookahead);
+void LookToRead_Init(CLookToRead *p);
+
+typedef struct
+{
+  ISeqInStream s;
+  ILookInStream *realStream;
+} CSecToLook;
+
+void SecToLook_CreateVTable(CSecToLook *p);
+
+typedef struct
+{
+  ISeqInStream s;
+  ILookInStream *realStream;
+} CSecToRead;
+
+void SecToRead_CreateVTable(CSecToRead *p);
+
+typedef struct
+{
+  SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize);
+    /* Returns: result. (result != SZ_OK) means break.
+       Value (UInt64)(Int64)-1 for size means unknown value. */
+} ICompressProgress;
+
+typedef struct
+{
+  void *(*Alloc)(void *p, size_t size);
+  void (*Free)(void *p, void *address); /* address can be 0 */
+} ISzAlloc;
+
+#define IAlloc_Alloc(p, size) (p)->Alloc((p), size)
+#define IAlloc_Free(p, a) (p)->Free((p), a)
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zCompressionMode.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zCompressionMode.cpp
new file mode 100644
index 0000000..6774fc4
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zCompressionMode.cpp
@@ -0,0 +1,3 @@
+// CompressionMethod.cpp
+
+#include "StdAfx.h"
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zCompressionMode.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zCompressionMode.h
new file mode 100644
index 0000000..a7cf999
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zCompressionMode.h
@@ -0,0 +1,50 @@
+// 7zCompressionMode.h
+
+#ifndef __7Z_COMPRESSION_MODE_H
+#define __7Z_COMPRESSION_MODE_H
+
+#include "../../../Common/MyString.h"
+
+#include "../../../Windows/PropVariant.h"
+
+#include "../../Common/MethodProps.h"
+
+namespace NArchive {
+namespace N7z {
+
+struct CMethodFull: public CMethod
+{
+  UInt32 NumInStreams;
+  UInt32 NumOutStreams;
+  bool IsSimpleCoder() const { return (NumInStreams == 1) && (NumOutStreams == 1); }
+};
+
+struct CBind
+{
+  UInt32 InCoder;
+  UInt32 InStream;
+  UInt32 OutCoder;
+  UInt32 OutStream;
+};
+
+struct CCompressionMethodMode
+{
+  CObjectVector<CMethodFull> Methods;
+  CRecordVector<CBind> Binds;
+  #ifdef COMPRESS_MT
+  UInt32 NumThreads;
+  #endif
+  bool PasswordIsDefined;
+  UString Password;
+
+  bool IsEmpty() const { return (Methods.IsEmpty() && !PasswordIsDefined); }
+  CCompressionMethodMode(): PasswordIsDefined(false)
+      #ifdef COMPRESS_MT
+      , NumThreads(1)
+      #endif
+  {}
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zDecode.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zDecode.cpp
new file mode 100644
index 0000000..2d25ff2
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zDecode.cpp
@@ -0,0 +1,332 @@
+// 7zDecode.cpp
+
+#include "StdAfx.h"
+
+#include "../../Common/LimitedStreams.h"
+#include "../../Common/LockedStream.h"
+#include "../../Common/ProgressUtils.h"
+#include "../../Common/StreamObjects.h"
+
+#include "7zDecode.h"
+
+namespace NArchive {
+namespace N7z {
+
+static void ConvertFolderItemInfoToBindInfo(const CFolder &folder,
+    CBindInfoEx &bindInfo)
+{
+  bindInfo.Clear();
+  int i;
+  for (i = 0; i < folder.BindPairs.Size(); i++)
+  {
+    NCoderMixer::CBindPair bindPair;
+    bindPair.InIndex = (UInt32)folder.BindPairs[i].InIndex;
+    bindPair.OutIndex = (UInt32)folder.BindPairs[i].OutIndex;
+    bindInfo.BindPairs.Add(bindPair);
+  }
+  UInt32 outStreamIndex = 0;
+  for (i = 0; i < folder.Coders.Size(); i++)
+  {
+    NCoderMixer::CCoderStreamsInfo coderStreamsInfo;
+    const CCoderInfo &coderInfo = folder.Coders[i];
+    coderStreamsInfo.NumInStreams = (UInt32)coderInfo.NumInStreams;
+    coderStreamsInfo.NumOutStreams = (UInt32)coderInfo.NumOutStreams;
+    bindInfo.Coders.Add(coderStreamsInfo);
+    bindInfo.CoderMethodIDs.Add(coderInfo.MethodID);
+    for (UInt32 j = 0; j < coderStreamsInfo.NumOutStreams; j++, outStreamIndex++)
+      if (folder.FindBindPairForOutStream(outStreamIndex) < 0)
+        bindInfo.OutStreams.Add(outStreamIndex);
+  }
+  for (i = 0; i < folder.PackStreams.Size(); i++)
+    bindInfo.InStreams.Add((UInt32)folder.PackStreams[i]);
+}
+
+static bool AreCodersEqual(const NCoderMixer::CCoderStreamsInfo &a1,
+    const NCoderMixer::CCoderStreamsInfo &a2)
+{
+  return (a1.NumInStreams == a2.NumInStreams) &&
+    (a1.NumOutStreams == a2.NumOutStreams);
+}
+
+static bool AreBindPairsEqual(const NCoderMixer::CBindPair &a1, const NCoderMixer::CBindPair &a2)
+{
+  return (a1.InIndex == a2.InIndex) &&
+    (a1.OutIndex == a2.OutIndex);
+}
+
+static bool AreBindInfoExEqual(const CBindInfoEx &a1, const CBindInfoEx &a2)
+{
+  if (a1.Coders.Size() != a2.Coders.Size())
+    return false;
+  int i;
+  for (i = 0; i < a1.Coders.Size(); i++)
+    if (!AreCodersEqual(a1.Coders[i], a2.Coders[i]))
+      return false;
+  if (a1.BindPairs.Size() != a2.BindPairs.Size())
+    return false;
+  for (i = 0; i < a1.BindPairs.Size(); i++)
+    if (!AreBindPairsEqual(a1.BindPairs[i], a2.BindPairs[i]))
+      return false;
+  for (i = 0; i < a1.CoderMethodIDs.Size(); i++)
+    if (a1.CoderMethodIDs[i] != a2.CoderMethodIDs[i])
+      return false;
+  if (a1.InStreams.Size() != a2.InStreams.Size())
+    return false;
+  if (a1.OutStreams.Size() != a2.OutStreams.Size())
+    return false;
+  return true;
+}
+
+CDecoder::CDecoder(bool multiThread)
+{
+  #ifndef _ST_MODE
+  multiThread = true;
+  #endif
+  _multiThread = multiThread;
+  _bindInfoExPrevIsDefined = false;
+}
+
+HRESULT CDecoder::Decode(
+    DECL_EXTERNAL_CODECS_LOC_VARS
+    IInStream *inStream,
+    UInt64 startPos,
+    const UInt64 *packSizes,
+    const CFolder &folderInfo,
+    ISequentialOutStream *outStream,
+    ICompressProgressInfo *compressProgress
+    #ifndef _NO_CRYPTO
+    , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
+    #endif
+    #ifdef COMPRESS_MT
+    , bool mtMode, UInt32 numThreads
+    #endif
+    )
+{
+  if (!folderInfo.CheckStructure())
+    return E_NOTIMPL;
+  #ifndef _NO_CRYPTO
+  passwordIsDefined = false;
+  #endif
+  CObjectVector< CMyComPtr<ISequentialInStream> > inStreams;
+  
+  CLockedInStream lockedInStream;
+  lockedInStream.Init(inStream);
+  
+  for (int j = 0; j < folderInfo.PackStreams.Size(); j++)
+  {
+    CLockedSequentialInStreamImp *lockedStreamImpSpec = new
+        CLockedSequentialInStreamImp;
+    CMyComPtr<ISequentialInStream> lockedStreamImp = lockedStreamImpSpec;
+    lockedStreamImpSpec->Init(&lockedInStream, startPos);
+    startPos += packSizes[j];
+    
+    CLimitedSequentialInStream *streamSpec = new
+        CLimitedSequentialInStream;
+    CMyComPtr<ISequentialInStream> inStream = streamSpec;
+    streamSpec->SetStream(lockedStreamImp);
+    streamSpec->Init(packSizes[j]);
+    inStreams.Add(inStream);
+  }
+  
+  int numCoders = folderInfo.Coders.Size();
+  
+  CBindInfoEx bindInfo;
+  ConvertFolderItemInfoToBindInfo(folderInfo, bindInfo);
+  bool createNewCoders;
+  if (!_bindInfoExPrevIsDefined)
+    createNewCoders = true;
+  else
+    createNewCoders = !AreBindInfoExEqual(bindInfo, _bindInfoExPrev);
+  if (createNewCoders)
+  {
+    int i;
+    _decoders.Clear();
+    // _decoders2.Clear();
+    
+    _mixerCoder.Release();
+
+    if (_multiThread)
+    {
+      _mixerCoderMTSpec = new NCoderMixer::CCoderMixer2MT;
+      _mixerCoder = _mixerCoderMTSpec;
+      _mixerCoderCommon = _mixerCoderMTSpec;
+    }
+    else
+    {
+      #ifdef _ST_MODE
+      _mixerCoderSTSpec = new NCoderMixer::CCoderMixer2ST;
+      _mixerCoder = _mixerCoderSTSpec;
+      _mixerCoderCommon = _mixerCoderSTSpec;
+      #endif
+    }
+    RINOK(_mixerCoderCommon->SetBindInfo(bindInfo));
+    
+    for (i = 0; i < numCoders; i++)
+    {
+      const CCoderInfo &coderInfo = folderInfo.Coders[i];
+
+  
+      CMyComPtr<ICompressCoder> decoder;
+      CMyComPtr<ICompressCoder2> decoder2;
+      RINOK(CreateCoder(
+          EXTERNAL_CODECS_LOC_VARS
+          coderInfo.MethodID, decoder, decoder2, false));
+      CMyComPtr<IUnknown> decoderUnknown;
+      if (coderInfo.IsSimpleCoder())
+      {
+        if (decoder == 0)
+          return E_NOTIMPL;
+
+        decoderUnknown = (IUnknown *)decoder;
+        
+        if (_multiThread)
+          _mixerCoderMTSpec->AddCoder(decoder);
+        #ifdef _ST_MODE
+        else
+          _mixerCoderSTSpec->AddCoder(decoder, false);
+        #endif
+      }
+      else
+      {
+        if (decoder2 == 0)
+          return E_NOTIMPL;
+        decoderUnknown = (IUnknown *)decoder2;
+        if (_multiThread)
+          _mixerCoderMTSpec->AddCoder2(decoder2);
+        #ifdef _ST_MODE
+        else
+          _mixerCoderSTSpec->AddCoder2(decoder2, false);
+        #endif
+      }
+      _decoders.Add(decoderUnknown);
+      #ifdef EXTERNAL_CODECS
+      CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
+      decoderUnknown.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
+      if (setCompressCodecsInfo)
+      {
+        RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecsInfo));
+      }
+      #endif
+    }
+    _bindInfoExPrev = bindInfo;
+    _bindInfoExPrevIsDefined = true;
+  }
+  int i;
+  _mixerCoderCommon->ReInit();
+  
+  UInt32 packStreamIndex = 0, unpackStreamIndex = 0;
+  UInt32 coderIndex = 0;
+  // UInt32 coder2Index = 0;
+  
+  for (i = 0; i < numCoders; i++)
+  {
+    const CCoderInfo &coderInfo = folderInfo.Coders[i];
+    CMyComPtr<IUnknown> &decoder = _decoders[coderIndex];
+    
+    {
+      CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties;
+      decoder.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecoderProperties);
+      if (setDecoderProperties)
+      {
+        const CByteBuffer &props = coderInfo.Props;
+        size_t size = props.GetCapacity();
+        if (size > 0xFFFFFFFF)
+          return E_NOTIMPL;
+        if (size > 0)
+        {
+          RINOK(setDecoderProperties->SetDecoderProperties2((const Byte *)props, (UInt32)size));
+        }
+      }
+    }
+
+    #ifdef COMPRESS_MT
+    if (mtMode)
+    {
+      CMyComPtr<ICompressSetCoderMt> setCoderMt;
+      decoder.QueryInterface(IID_ICompressSetCoderMt, &setCoderMt);
+      if (setCoderMt)
+      {
+        RINOK(setCoderMt->SetNumberOfThreads(numThreads));
+      }
+    }
+    #endif
+
+    #ifndef _NO_CRYPTO
+    {
+      CMyComPtr<ICryptoSetPassword> cryptoSetPassword;
+      decoder.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword);
+      if (cryptoSetPassword)
+      {
+        if (getTextPassword == 0)
+          return E_FAIL;
+        CMyComBSTR passwordBSTR;
+        RINOK(getTextPassword->CryptoGetTextPassword(&passwordBSTR));
+        CByteBuffer buffer;
+        passwordIsDefined = true;
+        const UString password(passwordBSTR);
+        const UInt32 sizeInBytes = password.Length() * 2;
+        buffer.SetCapacity(sizeInBytes);
+        for (int i = 0; i < password.Length(); i++)
+        {
+          wchar_t c = password[i];
+          ((Byte *)buffer)[i * 2] = (Byte)c;
+          ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8);
+        }
+        RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, sizeInBytes));
+      }
+    }
+    #endif
+
+    coderIndex++;
+    
+    UInt32 numInStreams = (UInt32)coderInfo.NumInStreams;
+    UInt32 numOutStreams = (UInt32)coderInfo.NumOutStreams;
+    CRecordVector<const UInt64 *> packSizesPointers;
+    CRecordVector<const UInt64 *> unpackSizesPointers;
+    packSizesPointers.Reserve(numInStreams);
+    unpackSizesPointers.Reserve(numOutStreams);
+    UInt32 j;
+    for (j = 0; j < numOutStreams; j++, unpackStreamIndex++)
+      unpackSizesPointers.Add(&folderInfo.UnpackSizes[unpackStreamIndex]);
+    
+    for (j = 0; j < numInStreams; j++, packStreamIndex++)
+    {
+      int bindPairIndex = folderInfo.FindBindPairForInStream(packStreamIndex);
+      if (bindPairIndex >= 0)
+        packSizesPointers.Add(
+        &folderInfo.UnpackSizes[(UInt32)folderInfo.BindPairs[bindPairIndex].OutIndex]);
+      else
+      {
+        int index = folderInfo.FindPackStreamArrayIndex(packStreamIndex);
+        if (index < 0)
+          return E_FAIL;
+        packSizesPointers.Add(&packSizes[index]);
+      }
+    }
+    
+    _mixerCoderCommon->SetCoderInfo(i,
+        &packSizesPointers.Front(),
+        &unpackSizesPointers.Front());
+  }
+  UInt32 mainCoder, temp;
+  bindInfo.FindOutStream(bindInfo.OutStreams[0], mainCoder, temp);
+
+  if (_multiThread)
+    _mixerCoderMTSpec->SetProgressCoderIndex(mainCoder);
+  /*
+  else
+    _mixerCoderSTSpec->SetProgressCoderIndex(mainCoder);;
+  */
+  
+  if (numCoders == 0)
+    return 0;
+  CRecordVector<ISequentialInStream *> inStreamPointers;
+  inStreamPointers.Reserve(inStreams.Size());
+  for (i = 0; i < inStreams.Size(); i++)
+    inStreamPointers.Add(inStreams[i]);
+  ISequentialOutStream *outStreamPointer = outStream;
+  return _mixerCoder->Code(&inStreamPointers.Front(), NULL,
+    inStreams.Size(), &outStreamPointer, NULL, 1, compressProgress);
+}
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zDecode.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zDecode.h
new file mode 100644
index 0000000..1057a52
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zDecode.h
@@ -0,0 +1,68 @@
+// 7zDecode.h
+
+#ifndef __7Z_DECODE_H
+#define __7Z_DECODE_H
+
+#include "../../IStream.h"
+#include "../../IPassword.h"
+
+#include "../Common/CoderMixer2.h"
+#include "../Common/CoderMixer2MT.h"
+#ifdef _ST_MODE
+#include "../Common/CoderMixer2ST.h"
+#endif
+
+#include "../../Common/CreateCoder.h"
+
+#include "7zItem.h"
+
+namespace NArchive {
+namespace N7z {
+
+struct CBindInfoEx: public NCoderMixer::CBindInfo
+{
+  CRecordVector<CMethodId> CoderMethodIDs;
+  void Clear()
+  {
+    CBindInfo::Clear();
+    CoderMethodIDs.Clear();
+  }
+};
+
+class CDecoder
+{
+  bool _bindInfoExPrevIsDefined;
+  CBindInfoEx _bindInfoExPrev;
+  
+  bool _multiThread;
+  #ifdef _ST_MODE
+  NCoderMixer::CCoderMixer2ST *_mixerCoderSTSpec;
+  #endif
+  NCoderMixer::CCoderMixer2MT *_mixerCoderMTSpec;
+  NCoderMixer::CCoderMixer2 *_mixerCoderCommon;
+  
+  CMyComPtr<ICompressCoder2> _mixerCoder;
+  CObjectVector<CMyComPtr<IUnknown> > _decoders;
+  // CObjectVector<CMyComPtr<ICompressCoder2> > _decoders2;
+public:
+  CDecoder(bool multiThread);
+  HRESULT Decode(
+      DECL_EXTERNAL_CODECS_LOC_VARS
+      IInStream *inStream,
+      UInt64 startPos,
+      const UInt64 *packSizes,
+      const CFolder &folder,
+      ISequentialOutStream *outStream,
+      ICompressProgressInfo *compressProgress
+      #ifndef _NO_CRYPTO
+      , ICryptoGetTextPassword *getTextPasswordSpec, bool &passwordIsDefined
+      #endif
+      #ifdef COMPRESS_MT
+      , bool mtMode, UInt32 numThreads
+      #endif
+      );
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zEncode.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zEncode.cpp
new file mode 100644
index 0000000..0180384
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zEncode.cpp
@@ -0,0 +1,453 @@
+// Encode.cpp
+
+#include "StdAfx.h"
+
+#include "7zEncode.h"
+#include "7zSpecStream.h"
+
+#include "../../IPassword.h"
+#include "../../Common/ProgressUtils.h"
+#include "../../Common/LimitedStreams.h"
+#include "../../Common/InOutTempBuffer.h"
+#include "../../Common/StreamObjects.h"
+#include "../../Common/CreateCoder.h"
+#include "../../Common/FilterCoder.h"
+
+static const UInt64 k_AES = 0x06F10701;
+static const UInt64 k_BCJ  = 0x03030103;
+static const UInt64 k_BCJ2 = 0x0303011B;
+
+namespace NArchive {
+namespace N7z {
+
+static void ConvertBindInfoToFolderItemInfo(const NCoderMixer::CBindInfo &bindInfo,
+    const CRecordVector<CMethodId> decompressionMethods,
+    CFolder &folder)
+{
+  folder.Coders.Clear();
+  // bindInfo.CoderMethodIDs.Clear();
+  // folder.OutStreams.Clear();
+  folder.PackStreams.Clear();
+  folder.BindPairs.Clear();
+  int i;
+  for (i = 0; i < bindInfo.BindPairs.Size(); i++)
+  {
+    CBindPair bindPair;
+    bindPair.InIndex = bindInfo.BindPairs[i].InIndex;
+    bindPair.OutIndex = bindInfo.BindPairs[i].OutIndex;
+    folder.BindPairs.Add(bindPair);
+  }
+  for (i = 0; i < bindInfo.Coders.Size(); i++)
+  {
+    CCoderInfo coderInfo;
+    const NCoderMixer::CCoderStreamsInfo &coderStreamsInfo = bindInfo.Coders[i];
+    coderInfo.NumInStreams = coderStreamsInfo.NumInStreams;
+    coderInfo.NumOutStreams = coderStreamsInfo.NumOutStreams;
+    coderInfo.MethodID = decompressionMethods[i];
+    folder.Coders.Add(coderInfo);
+  }
+  for (i = 0; i < bindInfo.InStreams.Size(); i++)
+    folder.PackStreams.Add(bindInfo.InStreams[i]);
+}
+
+HRESULT CEncoder::CreateMixerCoder(
+    DECL_EXTERNAL_CODECS_LOC_VARS
+    const UInt64 *inSizeForReduce)
+{
+  _mixerCoderSpec = new NCoderMixer::CCoderMixer2MT;
+  _mixerCoder = _mixerCoderSpec;
+  RINOK(_mixerCoderSpec->SetBindInfo(_bindInfo));
+  for (int i = 0; i < _options.Methods.Size(); i++)
+  {
+    const CMethodFull &methodFull = _options.Methods[i];
+    _codersInfo.Add(CCoderInfo());
+    CCoderInfo &encodingInfo = _codersInfo.Back();
+    encodingInfo.MethodID = methodFull.Id;
+    CMyComPtr<ICompressCoder> encoder;
+    CMyComPtr<ICompressCoder2> encoder2;
+    
+
+    RINOK(CreateCoder(
+        EXTERNAL_CODECS_LOC_VARS
+        methodFull.Id, encoder, encoder2, true));
+
+    if (!encoder && !encoder2)
+      return E_FAIL;
+
+    CMyComPtr<IUnknown> encoderCommon = encoder ? (IUnknown *)encoder : (IUnknown *)encoder2;
+   
+    #ifdef COMPRESS_MT
+    {
+      CMyComPtr<ICompressSetCoderMt> setCoderMt;
+      encoderCommon.QueryInterface(IID_ICompressSetCoderMt, &setCoderMt);
+      if (setCoderMt)
+      {
+        RINOK(setCoderMt->SetNumberOfThreads(_options.NumThreads));
+      }
+    }
+    #endif
+        
+
+    RINOK(SetMethodProperties(methodFull, inSizeForReduce, encoderCommon));
+
+    /*
+    CMyComPtr<ICryptoResetSalt> resetSalt;
+    encoderCommon.QueryInterface(IID_ICryptoResetSalt, (void **)&resetSalt);
+    if (resetSalt != NULL)
+    {
+      resetSalt->ResetSalt();
+    }
+    */
+
+    #ifdef EXTERNAL_CODECS
+    CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
+    encoderCommon.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
+    if (setCompressCodecsInfo)
+    {
+      RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecsInfo));
+    }
+    #endif
+    
+    CMyComPtr<ICryptoSetPassword> cryptoSetPassword;
+    encoderCommon.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword);
+
+    if (cryptoSetPassword)
+    {
+      CByteBuffer buffer;
+      const UInt32 sizeInBytes = _options.Password.Length() * 2;
+      buffer.SetCapacity(sizeInBytes);
+      for (int i = 0; i < _options.Password.Length(); i++)
+      {
+        wchar_t c = _options.Password[i];
+        ((Byte *)buffer)[i * 2] = (Byte)c;
+        ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8);
+      }
+      RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, sizeInBytes));
+    }
+
+    if (encoder)
+      _mixerCoderSpec->AddCoder(encoder);
+    else
+      _mixerCoderSpec->AddCoder2(encoder2);
+  }
+  return S_OK;
+}
+
+HRESULT CEncoder::Encode(
+    DECL_EXTERNAL_CODECS_LOC_VARS
+    ISequentialInStream *inStream,
+    const UInt64 *inStreamSize, const UInt64 *inSizeForReduce,
+    CFolder &folderItem,
+    ISequentialOutStream *outStream,
+    CRecordVector<UInt64> &packSizes,
+    ICompressProgressInfo *compressProgress)
+{
+  RINOK(EncoderConstr());
+
+  if (_mixerCoderSpec == NULL)
+  {
+    RINOK(CreateMixerCoder(EXTERNAL_CODECS_LOC_VARS inSizeForReduce));
+  }
+  _mixerCoderSpec->ReInit();
+  // _mixerCoderSpec->SetCoderInfo(0, NULL, NULL, progress);
+
+  CObjectVector<CInOutTempBuffer> inOutTempBuffers;
+  CObjectVector<CSequentialOutTempBufferImp *> tempBufferSpecs;
+  CObjectVector<CMyComPtr<ISequentialOutStream> > tempBuffers;
+  int numMethods = _bindInfo.Coders.Size();
+  int i;
+  for (i = 1; i < _bindInfo.OutStreams.Size(); i++)
+  {
+    inOutTempBuffers.Add(CInOutTempBuffer());
+    inOutTempBuffers.Back().Create();
+    inOutTempBuffers.Back().InitWriting();
+  }
+  for (i = 1; i < _bindInfo.OutStreams.Size(); i++)
+  {
+    CSequentialOutTempBufferImp *tempBufferSpec =
+        new CSequentialOutTempBufferImp;
+    CMyComPtr<ISequentialOutStream> tempBuffer = tempBufferSpec;
+    tempBufferSpec->Init(&inOutTempBuffers[i - 1]);
+    tempBuffers.Add(tempBuffer);
+    tempBufferSpecs.Add(tempBufferSpec);
+  }
+
+  for (i = 0; i < numMethods; i++)
+    _mixerCoderSpec->SetCoderInfo(i, NULL, NULL);
+
+  if (_bindInfo.InStreams.IsEmpty())
+    return E_FAIL;
+  UInt32 mainCoderIndex, mainStreamIndex;
+  _bindInfo.FindInStream(_bindInfo.InStreams[0], mainCoderIndex, mainStreamIndex);
+  
+  if (inStreamSize != NULL)
+  {
+    CRecordVector<const UInt64 *> sizePointers;
+    for (UInt32 i = 0; i < _bindInfo.Coders[mainCoderIndex].NumInStreams; i++)
+      if (i == mainStreamIndex)
+        sizePointers.Add(inStreamSize);
+      else
+        sizePointers.Add(NULL);
+    _mixerCoderSpec->SetCoderInfo(mainCoderIndex, &sizePointers.Front(), NULL);
+  }
+
+  
+  // UInt64 outStreamStartPos;
+  // RINOK(stream->Seek(0, STREAM_SEEK_CUR, &outStreamStartPos));
+  
+  CSequentialInStreamSizeCount2 *inStreamSizeCountSpec =
+      new CSequentialInStreamSizeCount2;
+  CMyComPtr<ISequentialInStream> inStreamSizeCount = inStreamSizeCountSpec;
+  CSequentialOutStreamSizeCount *outStreamSizeCountSpec =
+      new CSequentialOutStreamSizeCount;
+  CMyComPtr<ISequentialOutStream> outStreamSizeCount = outStreamSizeCountSpec;
+
+  inStreamSizeCountSpec->Init(inStream);
+  outStreamSizeCountSpec->SetStream(outStream);
+  outStreamSizeCountSpec->Init();
+
+  CRecordVector<ISequentialInStream *> inStreamPointers;
+  CRecordVector<ISequentialOutStream *> outStreamPointers;
+  inStreamPointers.Add(inStreamSizeCount);
+  outStreamPointers.Add(outStreamSizeCount);
+  for (i = 1; i < _bindInfo.OutStreams.Size(); i++)
+    outStreamPointers.Add(tempBuffers[i - 1]);
+
+  for (i = 0; i < _codersInfo.Size(); i++)
+  {
+    CCoderInfo &encodingInfo = _codersInfo[i];
+    
+    CMyComPtr<ICryptoResetInitVector> resetInitVector;
+    _mixerCoderSpec->_coders[i].QueryInterface(IID_ICryptoResetInitVector, (void **)&resetInitVector);
+    if (resetInitVector != NULL)
+    {
+      resetInitVector->ResetInitVector();
+    }
+
+    CMyComPtr<ICompressWriteCoderProperties> writeCoderProperties;
+    _mixerCoderSpec->_coders[i].QueryInterface(IID_ICompressWriteCoderProperties, (void **)&writeCoderProperties);
+    if (writeCoderProperties != NULL)
+    {
+      CSequentialOutStreamImp *outStreamSpec = new CSequentialOutStreamImp;
+      CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
+      outStreamSpec->Init();
+      writeCoderProperties->WriteCoderProperties(outStream);
+      size_t size = outStreamSpec->GetSize();
+      encodingInfo.Props.SetCapacity(size);
+      memmove(encodingInfo.Props, outStreamSpec->GetBuffer(), size);
+    }
+  }
+
+  UInt32 progressIndex = mainCoderIndex;
+
+  for (i = 0; i < _codersInfo.Size(); i++)
+  {
+    const CCoderInfo &e = _codersInfo[i];
+    if ((e.MethodID == k_BCJ || e.MethodID == k_BCJ2) && i + 1 < _codersInfo.Size())
+      progressIndex = i + 1;
+  }
+
+  _mixerCoderSpec->SetProgressCoderIndex(progressIndex);
+  
+  RINOK(_mixerCoder->Code(&inStreamPointers.Front(), NULL, 1,
+    &outStreamPointers.Front(), NULL, outStreamPointers.Size(), compressProgress));
+  
+  ConvertBindInfoToFolderItemInfo(_decompressBindInfo, _decompressionMethods,
+      folderItem);
+  
+  packSizes.Add(outStreamSizeCountSpec->GetSize());
+  
+  for (i = 1; i < _bindInfo.OutStreams.Size(); i++)
+  {
+    CInOutTempBuffer &inOutTempBuffer = inOutTempBuffers[i - 1];
+    inOutTempBuffer.FlushWrite();
+    inOutTempBuffer.InitReading();
+    inOutTempBuffer.WriteToStream(outStream);
+    packSizes.Add(inOutTempBuffer.GetDataSize());
+  }
+  
+  for (i = 0; i < (int)_bindReverseConverter->NumSrcInStreams; i++)
+  {
+    int binder = _bindInfo.FindBinderForInStream(
+        _bindReverseConverter->DestOutToSrcInMap[i]);
+    UInt64 streamSize;
+    if (binder < 0)
+      streamSize = inStreamSizeCountSpec->GetSize();
+    else
+      streamSize = _mixerCoderSpec->GetWriteProcessedSize(binder);
+    folderItem.UnpackSizes.Add(streamSize);
+  }
+  for (i = numMethods - 1; i >= 0; i--)
+    folderItem.Coders[numMethods - 1 - i].Props = _codersInfo[i].Props;
+  return S_OK;
+}
+
+
+CEncoder::CEncoder(const CCompressionMethodMode &options):
+  _bindReverseConverter(0),
+  _constructed(false)
+{
+  if (options.IsEmpty())
+    throw 1;
+
+  _options = options;
+  _mixerCoderSpec = NULL;
+}
+
+HRESULT CEncoder::EncoderConstr()
+{
+  if (_constructed)
+    return S_OK;
+  if (_options.Methods.IsEmpty())
+  {
+    // it has only password method;
+    if (!_options.PasswordIsDefined)
+      throw 1;
+    if (!_options.Binds.IsEmpty())
+      throw 1;
+    NCoderMixer::CCoderStreamsInfo coderStreamsInfo;
+    CMethodFull method;
+    
+    method.NumInStreams = 1;
+    method.NumOutStreams = 1;
+    coderStreamsInfo.NumInStreams = 1;
+    coderStreamsInfo.NumOutStreams = 1;
+    method.Id = k_AES;
+    
+    _options.Methods.Add(method);
+    _bindInfo.Coders.Add(coderStreamsInfo);
+  
+    _bindInfo.InStreams.Add(0);
+    _bindInfo.OutStreams.Add(0);
+  }
+  else
+  {
+
+  UInt32 numInStreams = 0, numOutStreams = 0;
+  int i;
+  for (i = 0; i < _options.Methods.Size(); i++)
+  {
+    const CMethodFull &methodFull = _options.Methods[i];
+    NCoderMixer::CCoderStreamsInfo coderStreamsInfo;
+    coderStreamsInfo.NumInStreams = methodFull.NumOutStreams;
+    coderStreamsInfo.NumOutStreams = methodFull.NumInStreams;
+    if (_options.Binds.IsEmpty())
+    {
+      if (i < _options.Methods.Size() - 1)
+      {
+        NCoderMixer::CBindPair bindPair;
+        bindPair.InIndex = numInStreams + coderStreamsInfo.NumInStreams;
+        bindPair.OutIndex = numOutStreams;
+        _bindInfo.BindPairs.Add(bindPair);
+      }
+      else
+        _bindInfo.OutStreams.Insert(0, numOutStreams);
+      for (UInt32 j = 1; j < coderStreamsInfo.NumOutStreams; j++)
+        _bindInfo.OutStreams.Add(numOutStreams + j);
+    }
+    
+    numInStreams += coderStreamsInfo.NumInStreams;
+    numOutStreams += coderStreamsInfo.NumOutStreams;
+
+    _bindInfo.Coders.Add(coderStreamsInfo);
+  }
+
+  if (!_options.Binds.IsEmpty())
+  {
+    for (i = 0; i < _options.Binds.Size(); i++)
+    {
+      NCoderMixer::CBindPair bindPair;
+      const CBind &bind = _options.Binds[i];
+      bindPair.InIndex = _bindInfo.GetCoderInStreamIndex(bind.InCoder) + bind.InStream;
+      bindPair.OutIndex = _bindInfo.GetCoderOutStreamIndex(bind.OutCoder) + bind.OutStream;
+      _bindInfo.BindPairs.Add(bindPair);
+    }
+    for (i = 0; i < (int)numOutStreams; i++)
+      if (_bindInfo.FindBinderForOutStream(i) == -1)
+        _bindInfo.OutStreams.Add(i);
+  }
+
+  for (i = 0; i < (int)numInStreams; i++)
+    if (_bindInfo.FindBinderForInStream(i) == -1)
+      _bindInfo.InStreams.Add(i);
+
+  if (_bindInfo.InStreams.IsEmpty())
+    throw 1; // this is error
+
+  // Make main stream first in list
+  int inIndex = _bindInfo.InStreams[0];
+  for (;;)
+  {
+    UInt32 coderIndex, coderStreamIndex;
+    _bindInfo.FindInStream(inIndex, coderIndex, coderStreamIndex);
+    UInt32 outIndex = _bindInfo.GetCoderOutStreamIndex(coderIndex);
+    int binder = _bindInfo.FindBinderForOutStream(outIndex);
+    if (binder >= 0)
+    {
+      inIndex = _bindInfo.BindPairs[binder].InIndex;
+      continue;
+    }
+    for (i = 0; i < _bindInfo.OutStreams.Size(); i++)
+      if (_bindInfo.OutStreams[i] == outIndex)
+      {
+        _bindInfo.OutStreams.Delete(i);
+        _bindInfo.OutStreams.Insert(0, outIndex);
+        break;
+      }
+    break;
+  }
+
+  if (_options.PasswordIsDefined)
+  {
+    int numCryptoStreams = _bindInfo.OutStreams.Size();
+
+    for (i = 0; i < numCryptoStreams; i++)
+    {
+      NCoderMixer::CBindPair bindPair;
+      bindPair.InIndex = numInStreams + i;
+      bindPair.OutIndex = _bindInfo.OutStreams[i];
+      _bindInfo.BindPairs.Add(bindPair);
+    }
+    _bindInfo.OutStreams.Clear();
+
+    /*
+    if (numCryptoStreams == 0)
+      numCryptoStreams = 1;
+    */
+
+    for (i = 0; i < numCryptoStreams; i++)
+    {
+      NCoderMixer::CCoderStreamsInfo coderStreamsInfo;
+      CMethodFull method;
+      method.NumInStreams = 1;
+      method.NumOutStreams = 1;
+      coderStreamsInfo.NumInStreams = method.NumOutStreams;
+      coderStreamsInfo.NumOutStreams = method.NumInStreams;
+      method.Id = k_AES;
+
+      _options.Methods.Add(method);
+      _bindInfo.Coders.Add(coderStreamsInfo);
+      _bindInfo.OutStreams.Add(numOutStreams + i);
+    }
+  }
+
+  }
+
+  for (int i = _options.Methods.Size() - 1; i >= 0; i--)
+  {
+    const CMethodFull &methodFull = _options.Methods[i];
+    _decompressionMethods.Add(methodFull.Id);
+  }
+
+  _bindReverseConverter = new NCoderMixer::CBindReverseConverter(_bindInfo);
+  _bindReverseConverter->CreateReverseBindInfo(_decompressBindInfo);
+  _constructed = true;
+  return S_OK;
+}
+
+CEncoder::~CEncoder()
+{
+  delete _bindReverseConverter;
+}
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zEncode.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zEncode.h
new file mode 100644
index 0000000..4909a6e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zEncode.h
@@ -0,0 +1,55 @@
+// 7zEncode.h
+
+#ifndef __7Z_ENCODE_H
+#define __7Z_ENCODE_H
+
+// #include "../../Common/StreamObjects.h"
+
+#include "7zCompressionMode.h"
+
+#include "../Common/CoderMixer2.h"
+#include "../Common/CoderMixer2MT.h"
+#ifdef _ST_MODE
+#include "../Common/CoderMixer2ST.h"
+#endif
+#include "7zItem.h"
+
+#include "../../Common/CreateCoder.h"
+
+namespace NArchive {
+namespace N7z {
+
+class CEncoder
+{
+  NCoderMixer::CCoderMixer2MT *_mixerCoderSpec;
+  CMyComPtr<ICompressCoder2> _mixerCoder;
+
+  CObjectVector<CCoderInfo> _codersInfo;
+
+  CCompressionMethodMode _options;
+  NCoderMixer::CBindInfo _bindInfo;
+  NCoderMixer::CBindInfo _decompressBindInfo;
+  NCoderMixer::CBindReverseConverter *_bindReverseConverter;
+  CRecordVector<CMethodId> _decompressionMethods;
+
+  HRESULT CreateMixerCoder(DECL_EXTERNAL_CODECS_LOC_VARS
+      const UInt64 *inSizeForReduce);
+
+  bool _constructed;
+public:
+  CEncoder(const CCompressionMethodMode &options);
+  ~CEncoder();
+  HRESULT EncoderConstr();
+  HRESULT Encode(
+      DECL_EXTERNAL_CODECS_LOC_VARS
+      ISequentialInStream *inStream,
+      const UInt64 *inStreamSize, const UInt64 *inSizeForReduce,
+      CFolder &folderItem,
+      ISequentialOutStream *outStream,
+      CRecordVector<UInt64> &packSizes,
+      ICompressProgressInfo *compressProgress);
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zExtract.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zExtract.cpp
new file mode 100644
index 0000000..06e9ef9
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zExtract.cpp
@@ -0,0 +1,273 @@
+// 7zExtract.cpp
+
+#include "StdAfx.h"
+
+#include "7zHandler.h"
+#include "7zFolderOutStream.h"
+#include "7zDecode.h"
+// #include "7z1Decode.h"
+
+#include "../../../Common/ComTry.h"
+#include "../../Common/StreamObjects.h"
+#include "../../Common/ProgressUtils.h"
+#include "../../Common/LimitedStreams.h"
+
+namespace NArchive {
+namespace N7z {
+
+struct CExtractFolderInfo
+{
+  #ifdef _7Z_VOL
+  int VolumeIndex;
+  #endif
+  CNum FileIndex;
+  CNum FolderIndex;
+  CBoolVector ExtractStatuses;
+  UInt64 UnpackSize;
+  CExtractFolderInfo(
+    #ifdef _7Z_VOL
+    int volumeIndex,
+    #endif
+    CNum fileIndex, CNum folderIndex):
+    #ifdef _7Z_VOL
+    VolumeIndex(volumeIndex),
+    #endif
+    FileIndex(fileIndex),
+    FolderIndex(folderIndex),
+    UnpackSize(0)
+  {
+    if (fileIndex != kNumNoIndex)
+    {
+      ExtractStatuses.Reserve(1);
+      ExtractStatuses.Add(true);
+    }
+  };
+};
+
+STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
+    Int32 testModeSpec, IArchiveExtractCallback *extractCallbackSpec)
+{
+  COM_TRY_BEGIN
+  bool testMode = (testModeSpec != 0);
+  CMyComPtr<IArchiveExtractCallback> extractCallback = extractCallbackSpec;
+  UInt64 importantTotalUnpacked = 0;
+
+  bool allFilesMode = (numItems == UInt32(-1));
+  if (allFilesMode)
+    numItems =
+    #ifdef _7Z_VOL
+    _refs.Size();
+    #else
+    _db.Files.Size();
+    #endif
+
+  if(numItems == 0)
+    return S_OK;
+
+  /*
+  if(_volumes.Size() != 1)
+    return E_FAIL;
+  const CVolume &volume = _volumes.Front();
+  const CArchiveDatabaseEx &_db = volume.Database;
+  IInStream *_inStream = volume.Stream;
+  */
+  
+  CObjectVector<CExtractFolderInfo> extractFolderInfoVector;
+  for(UInt32 ii = 0; ii < numItems; ii++)
+  {
+    // UInt32 fileIndex = allFilesMode ? indexIndex : indices[indexIndex];
+    UInt32 ref2Index = allFilesMode ? ii : indices[ii];
+    // const CRef2 &ref2 = _refs[ref2Index];
+
+    // for(UInt32 ri = 0; ri < ref2.Refs.Size(); ri++)
+    {
+      #ifdef _7Z_VOL
+      // const CRef &ref = ref2.Refs[ri];
+      const CRef &ref = _refs[ref2Index];
+
+      int volumeIndex = ref.VolumeIndex;
+      const CVolume &volume = _volumes[volumeIndex];
+      const CArchiveDatabaseEx &db = volume.Database;
+      UInt32 fileIndex = ref.ItemIndex;
+      #else
+      const CArchiveDatabaseEx &db = _db;
+      UInt32 fileIndex = ref2Index;
+      #endif
+
+      CNum folderIndex = db.FileIndexToFolderIndexMap[fileIndex];
+      if (folderIndex == kNumNoIndex)
+      {
+        extractFolderInfoVector.Add(CExtractFolderInfo(
+            #ifdef _7Z_VOL
+            volumeIndex,
+            #endif
+            fileIndex, kNumNoIndex));
+        continue;
+      }
+      if (extractFolderInfoVector.IsEmpty() ||
+        folderIndex != extractFolderInfoVector.Back().FolderIndex
+        #ifdef _7Z_VOL
+        || volumeIndex != extractFolderInfoVector.Back().VolumeIndex
+        #endif
+        )
+      {
+        extractFolderInfoVector.Add(CExtractFolderInfo(
+            #ifdef _7Z_VOL
+            volumeIndex,
+            #endif
+            kNumNoIndex, folderIndex));
+        const CFolder &folderInfo = db.Folders[folderIndex];
+        UInt64 unpackSize = folderInfo.GetUnpackSize();
+        importantTotalUnpacked += unpackSize;
+        extractFolderInfoVector.Back().UnpackSize = unpackSize;
+      }
+      
+      CExtractFolderInfo &efi = extractFolderInfoVector.Back();
+      
+      // const CFolderInfo &folderInfo = m_dam_Folders[folderIndex];
+      CNum startIndex = db.FolderStartFileIndex[folderIndex];
+      for (CNum index = efi.ExtractStatuses.Size();
+          index <= fileIndex - startIndex; index++)
+      {
+        // UInt64 unpackSize = _db.Files[startIndex + index].UnpackSize;
+        // Count partial_folder_size
+        // efi.UnpackSize += unpackSize;
+        // importantTotalUnpacked += unpackSize;
+        efi.ExtractStatuses.Add(index == fileIndex - startIndex);
+      }
+    }
+  }
+
+  extractCallback->SetTotal(importantTotalUnpacked);
+
+  CDecoder decoder(
+    #ifdef _ST_MODE
+    false
+    #else
+    true
+    #endif
+    );
+  // CDecoder1 decoder;
+
+  UInt64 currentTotalPacked = 0;
+  UInt64 currentTotalUnpacked = 0;
+  UInt64 totalFolderUnpacked;
+  UInt64 totalFolderPacked;
+
+  CLocalProgress *lps = new CLocalProgress;
+  CMyComPtr<ICompressProgressInfo> progress = lps;
+  lps->Init(extractCallback, false);
+
+  for(int i = 0; i < extractFolderInfoVector.Size(); i++,
+      currentTotalUnpacked += totalFolderUnpacked,
+      currentTotalPacked += totalFolderPacked)
+  {
+    lps->OutSize = currentTotalUnpacked;
+    lps->InSize = currentTotalPacked;
+    RINOK(lps->SetCur());
+    
+    const CExtractFolderInfo &efi = extractFolderInfoVector[i];
+    totalFolderUnpacked = efi.UnpackSize;
+
+    totalFolderPacked = 0;
+
+    CFolderOutStream *folderOutStream = new CFolderOutStream;
+    CMyComPtr<ISequentialOutStream> outStream(folderOutStream);
+
+    #ifdef _7Z_VOL
+    const CVolume &volume = _volumes[efi.VolumeIndex];
+    const CArchiveDatabaseEx &db = volume.Database;
+    #else
+    const CArchiveDatabaseEx &db = _db;
+    #endif
+
+    CNum startIndex;
+    if (efi.FileIndex != kNumNoIndex)
+      startIndex = efi.FileIndex;
+    else
+      startIndex = db.FolderStartFileIndex[efi.FolderIndex];
+
+
+    HRESULT result = folderOutStream->Init(&db,
+        #ifdef _7Z_VOL
+        volume.StartRef2Index,
+        #else
+        0,
+        #endif
+        startIndex,
+        &efi.ExtractStatuses, extractCallback, testMode, _crcSize != 0);
+
+    RINOK(result);
+
+    if (efi.FileIndex != kNumNoIndex)
+      continue;
+
+    CNum folderIndex = efi.FolderIndex;
+    const CFolder &folderInfo = db.Folders[folderIndex];
+
+    totalFolderPacked = _db.GetFolderFullPackSize(folderIndex);
+
+    CNum packStreamIndex = db.FolderStartPackStreamIndex[folderIndex];
+    UInt64 folderStartPackPos = db.GetFolderStreamPos(folderIndex, 0);
+
+    #ifndef _NO_CRYPTO
+    CMyComPtr<ICryptoGetTextPassword> getTextPassword;
+    if (extractCallback)
+      extractCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword);
+    #endif
+
+    try
+    {
+      #ifndef _NO_CRYPTO
+      bool passwordIsDefined;
+      #endif
+
+      HRESULT result = decoder.Decode(
+          EXTERNAL_CODECS_VARS
+          #ifdef _7Z_VOL
+          volume.Stream,
+          #else
+          _inStream,
+          #endif
+          folderStartPackPos,
+          &db.PackSizes[packStreamIndex],
+          folderInfo,
+          outStream,
+          progress
+          #ifndef _NO_CRYPTO
+          , getTextPassword, passwordIsDefined
+          #endif
+          #ifdef COMPRESS_MT
+          , true, _numThreads
+          #endif
+          );
+
+      if (result == S_FALSE)
+      {
+        RINOK(folderOutStream->FlushCorrupted(NArchive::NExtract::NOperationResult::kDataError));
+        continue;
+      }
+      if (result == E_NOTIMPL)
+      {
+        RINOK(folderOutStream->FlushCorrupted(NArchive::NExtract::NOperationResult::kUnSupportedMethod));
+        continue;
+      }
+      if (result != S_OK)
+        return result;
+      if (folderOutStream->WasWritingFinished() != S_OK)
+      {
+        RINOK(folderOutStream->FlushCorrupted(NArchive::NExtract::NOperationResult::kDataError));
+        continue;
+      }
+    }
+    catch(...)
+    {
+      RINOK(folderOutStream->FlushCorrupted(NArchive::NExtract::NOperationResult::kDataError));
+      continue;
+    }
+  }
+  return S_OK;
+  COM_TRY_END
+}
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zFolderInStream.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zFolderInStream.cpp
new file mode 100644
index 0000000..b029ae1
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zFolderInStream.cpp
@@ -0,0 +1,130 @@
+// 7zFolderInStream.cpp
+
+#include "StdAfx.h"
+
+#include "7zFolderInStream.h"
+
+namespace NArchive {
+namespace N7z {
+
+CFolderInStream::CFolderInStream()
+{
+  _inStreamWithHashSpec = new CSequentialInStreamWithCRC;
+  _inStreamWithHash = _inStreamWithHashSpec;
+}
+
+void CFolderInStream::Init(IArchiveUpdateCallback *updateCallback,
+    const UInt32 *fileIndices, UInt32 numFiles)
+{
+  _updateCallback = updateCallback;
+  _numFiles = numFiles;
+  _fileIndex = 0;
+  _fileIndices = fileIndices;
+  Processed.Clear();
+  CRCs.Clear();
+  Sizes.Clear();
+  _fileIsOpen = false;
+  _currentSizeIsDefined = false;
+}
+
+HRESULT CFolderInStream::OpenStream()
+{
+  _filePos = 0;
+  while (_fileIndex < _numFiles)
+  {
+    _currentSizeIsDefined = false;
+    CMyComPtr<ISequentialInStream> stream;
+    HRESULT result = _updateCallback->GetStream(_fileIndices[_fileIndex], &stream);
+    if (result != S_OK && result != S_FALSE)
+      return result;
+    _fileIndex++;
+    _inStreamWithHashSpec->SetStream(stream);
+    _inStreamWithHashSpec->Init();
+    if (!stream)
+    {
+      RINOK(_updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK));
+      Sizes.Add(0);
+      Processed.Add(result == S_OK);
+      AddDigest();
+      continue;
+    }
+    CMyComPtr<IStreamGetSize> streamGetSize;
+    if (stream.QueryInterface(IID_IStreamGetSize, &streamGetSize) == S_OK)
+    {
+      if(streamGetSize)
+      {
+        _currentSizeIsDefined = true;
+        RINOK(streamGetSize->GetSize(&_currentSize));
+      }
+    }
+
+    _fileIsOpen = true;
+    return S_OK;
+  }
+  return S_OK;
+}
+
+void CFolderInStream::AddDigest()
+{
+  CRCs.Add(_inStreamWithHashSpec->GetCRC());
+}
+
+HRESULT CFolderInStream::CloseStream()
+{
+  RINOK(_updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK));
+  _inStreamWithHashSpec->ReleaseStream();
+  _fileIsOpen = false;
+  Processed.Add(true);
+  Sizes.Add(_filePos);
+  AddDigest();
+  return S_OK;
+}
+
+STDMETHODIMP CFolderInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
+{
+  UInt32 realProcessedSize = 0;
+  while ((_fileIndex < _numFiles || _fileIsOpen) && size > 0)
+  {
+    if (_fileIsOpen)
+    {
+      UInt32 localProcessedSize;
+      RINOK(_inStreamWithHash->Read(
+          ((Byte *)data) + realProcessedSize, size, &localProcessedSize));
+      if (localProcessedSize == 0)
+      {
+        RINOK(CloseStream());
+        continue;
+      }
+      realProcessedSize += localProcessedSize;
+      _filePos += localProcessedSize;
+      size -= localProcessedSize;
+      break;
+    }
+    else
+    {
+      RINOK(OpenStream());
+    }
+  }
+  if (processedSize != 0)
+    *processedSize = realProcessedSize;
+  return S_OK;
+}
+
+STDMETHODIMP CFolderInStream::GetSubStreamSize(UInt64 subStream, UInt64 *value)
+{
+  *value = 0;
+  int subStreamIndex = (int)subStream;
+  if (subStreamIndex < 0 || subStream > Sizes.Size())
+    return E_FAIL;
+  if (subStreamIndex < Sizes.Size())
+  {
+    *value= Sizes[subStreamIndex];
+    return S_OK;
+  }
+  if (!_currentSizeIsDefined)
+    return S_FALSE;
+  *value = _currentSize;
+  return S_OK;
+}
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zFolderInStream.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zFolderInStream.h
new file mode 100644
index 0000000..68e2b27
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zFolderInStream.h
@@ -0,0 +1,66 @@
+// 7z/FolderInStream.h
+
+#ifndef __7Z_FOLDERINSTREAM_H
+#define __7Z_FOLDERINSTREAM_H
+
+#include "7zItem.h"
+#include "7zHeader.h"
+
+#include "../IArchive.h"
+#include "../Common/InStreamWithCRC.h"
+#include "../../IStream.h"
+#include "../../ICoder.h"
+
+namespace NArchive {
+namespace N7z {
+
+class CFolderInStream:
+  public ISequentialInStream,
+  public ICompressGetSubStreamSize,
+  public CMyUnknownImp
+{
+public:
+
+  MY_UNKNOWN_IMP1(ICompressGetSubStreamSize)
+
+  CFolderInStream();
+
+  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
+
+  STDMETHOD(GetSubStreamSize)(UInt64 subStream, UInt64 *value);
+private:
+  CSequentialInStreamWithCRC *_inStreamWithHashSpec;
+  CMyComPtr<ISequentialInStream> _inStreamWithHash;
+  CMyComPtr<IArchiveUpdateCallback> _updateCallback;
+
+  bool _currentSizeIsDefined;
+  UInt64 _currentSize;
+
+  bool _fileIsOpen;
+  UInt64 _filePos;
+
+  const UInt32 *_fileIndices;
+  UInt32 _numFiles;
+  UInt32 _fileIndex;
+
+  HRESULT OpenStream();
+  HRESULT CloseStream();
+  void AddDigest();
+public:
+  void Init(IArchiveUpdateCallback *updateCallback,
+      const UInt32 *fileIndices, UInt32 numFiles);
+  CRecordVector<bool> Processed;
+  CRecordVector<UInt32> CRCs;
+  CRecordVector<UInt64> Sizes;
+  UInt64 GetFullSize() const
+  {
+    UInt64 size = 0;
+    for (int i = 0; i < Sizes.Size(); i++)
+      size += Sizes[i];
+    return size;
+  }
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zFolderOutStream.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zFolderOutStream.cpp
new file mode 100644
index 0000000..61b938d
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zFolderOutStream.cpp
@@ -0,0 +1,164 @@
+// 7zFolderOutStream.cpp
+
+#include "StdAfx.h"
+
+#include "7zFolderOutStream.h"
+
+namespace NArchive {
+namespace N7z {
+
+CFolderOutStream::CFolderOutStream()
+{
+  _outStreamWithHashSpec = new COutStreamWithCRC;
+  _outStreamWithHash = _outStreamWithHashSpec;
+}
+
+HRESULT CFolderOutStream::Init(
+    const CArchiveDatabaseEx *archiveDatabase,
+    UInt32 ref2Offset,
+    UInt32 startIndex,
+    const CBoolVector *extractStatuses,
+    IArchiveExtractCallback *extractCallback,
+    bool testMode,
+    bool checkCrc)
+{
+  _archiveDatabase = archiveDatabase;
+  _ref2Offset = ref2Offset;
+  _startIndex = startIndex;
+
+  _extractStatuses = extractStatuses;
+  _extractCallback = extractCallback;
+  _testMode = testMode;
+
+  _checkCrc = checkCrc;
+
+  _currentIndex = 0;
+  _fileIsOpen = false;
+  return WriteEmptyFiles();
+}
+
+HRESULT CFolderOutStream::OpenFile()
+{
+  Int32 askMode;
+  if((*_extractStatuses)[_currentIndex])
+    askMode = _testMode ?
+        NArchive::NExtract::NAskMode::kTest :
+        NArchive::NExtract::NAskMode::kExtract;
+  else
+    askMode = NArchive::NExtract::NAskMode::kSkip;
+  CMyComPtr<ISequentialOutStream> realOutStream;
+
+  UInt32 index = _startIndex + _currentIndex;
+  RINOK(_extractCallback->GetStream(_ref2Offset + index, &realOutStream, askMode));
+
+  _outStreamWithHashSpec->SetStream(realOutStream);
+  _outStreamWithHashSpec->Init(_checkCrc);
+  if (askMode == NArchive::NExtract::NAskMode::kExtract &&
+      (!realOutStream))
+  {
+    const CFileItem &fi = _archiveDatabase->Files[index];
+    if (!_archiveDatabase->IsItemAnti(index) && !fi.IsDir)
+      askMode = NArchive::NExtract::NAskMode::kSkip;
+  }
+  return _extractCallback->PrepareOperation(askMode);
+}
+
+HRESULT CFolderOutStream::WriteEmptyFiles()
+{
+  for(;_currentIndex < _extractStatuses->Size(); _currentIndex++)
+  {
+    UInt32 index = _startIndex + _currentIndex;
+    const CFileItem &fi = _archiveDatabase->Files[index];
+    if (!_archiveDatabase->IsItemAnti(index) && !fi.IsDir && fi.Size != 0)
+      return S_OK;
+    RINOK(OpenFile());
+    RINOK(_extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kOK));
+    _outStreamWithHashSpec->ReleaseStream();
+  }
+  return S_OK;
+}
+
+STDMETHODIMP CFolderOutStream::Write(const void *data,
+    UInt32 size, UInt32 *processedSize)
+{
+  UInt32 realProcessedSize = 0;
+  while(_currentIndex < _extractStatuses->Size())
+  {
+    if (_fileIsOpen)
+    {
+      UInt32 index = _startIndex + _currentIndex;
+      const CFileItem &fi = _archiveDatabase->Files[index];
+      UInt64 fileSize = fi.Size;
+      
+      UInt32 numBytesToWrite = (UInt32)MyMin(fileSize - _filePos,
+          UInt64(size - realProcessedSize));
+      
+      UInt32 processedSizeLocal;
+      RINOK(_outStreamWithHash->Write((const Byte *)data + realProcessedSize,
+            numBytesToWrite, &processedSizeLocal));
+
+      _filePos += processedSizeLocal;
+      realProcessedSize += processedSizeLocal;
+      if (_filePos == fileSize)
+      {
+        bool digestsAreEqual;
+        if (fi.CrcDefined && _checkCrc)
+          digestsAreEqual = fi.Crc == _outStreamWithHashSpec->GetCRC();
+        else
+          digestsAreEqual = true;
+
+        RINOK(_extractCallback->SetOperationResult(
+            digestsAreEqual ?
+            NArchive::NExtract::NOperationResult::kOK :
+            NArchive::NExtract::NOperationResult::kCRCError));
+        _outStreamWithHashSpec->ReleaseStream();
+        _fileIsOpen = false;
+        _currentIndex++;
+      }
+      if (realProcessedSize == size)
+      {
+        if (processedSize != NULL)
+          *processedSize = realProcessedSize;
+        return WriteEmptyFiles();
+      }
+    }
+    else
+    {
+      RINOK(OpenFile());
+      _fileIsOpen = true;
+      _filePos = 0;
+    }
+  }
+  if (processedSize != NULL)
+    *processedSize = size;
+  return S_OK;
+}
+
+HRESULT CFolderOutStream::FlushCorrupted(Int32 resultEOperationResult)
+{
+  while(_currentIndex < _extractStatuses->Size())
+  {
+    if (_fileIsOpen)
+    {
+      RINOK(_extractCallback->SetOperationResult(resultEOperationResult));
+      _outStreamWithHashSpec->ReleaseStream();
+      _fileIsOpen = false;
+      _currentIndex++;
+    }
+    else
+    {
+      RINOK(OpenFile());
+      _fileIsOpen = true;
+    }
+  }
+  return S_OK;
+}
+
+HRESULT CFolderOutStream::WasWritingFinished()
+{
+  if (_currentIndex == _extractStatuses->Size())
+    return S_OK;
+  return E_FAIL;
+}
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zFolderOutStream.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zFolderOutStream.h
new file mode 100644
index 0000000..f54fba3
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zFolderOutStream.h
@@ -0,0 +1,60 @@
+// 7zFolderOutStream.h
+
+#ifndef __7Z_FOLDEROUTSTREAM_H
+#define __7Z_FOLDEROUTSTREAM_H
+
+#include "7zIn.h"
+
+#include "../../IStream.h"
+#include "../IArchive.h"
+#include "../Common/OutStreamWithCRC.h"
+
+namespace NArchive {
+namespace N7z {
+
+class CFolderOutStream:
+  public ISequentialOutStream,
+  public CMyUnknownImp
+{
+public:
+  MY_UNKNOWN_IMP
+  
+  CFolderOutStream();
+
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+private:
+
+  COutStreamWithCRC *_outStreamWithHashSpec;
+  CMyComPtr<ISequentialOutStream> _outStreamWithHash;
+  const CArchiveDatabaseEx *_archiveDatabase;
+  const CBoolVector *_extractStatuses;
+  UInt32 _startIndex;
+  UInt32 _ref2Offset;
+  int _currentIndex;
+  // UInt64 _currentDataPos;
+  CMyComPtr<IArchiveExtractCallback> _extractCallback;
+  bool _testMode;
+
+  bool _fileIsOpen;
+
+  bool _checkCrc;
+  UInt64 _filePos;
+
+  HRESULT OpenFile();
+  HRESULT WriteEmptyFiles();
+public:
+  HRESULT Init(
+      const CArchiveDatabaseEx *archiveDatabase,
+      UInt32 ref2Offset,
+      UInt32 startIndex,
+      const CBoolVector *extractStatuses,
+      IArchiveExtractCallback *extractCallback,
+      bool testMode,
+      bool checkCrc);
+  HRESULT FlushCorrupted(Int32 resultEOperationResult);
+  HRESULT WasWritingFinished();
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zHandler.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zHandler.cpp
new file mode 100644
index 0000000..44131fa
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zHandler.cpp
@@ -0,0 +1,503 @@
+// 7zHandler.cpp
+
+#include "StdAfx.h"
+
+extern "C"
+{
+  #include "../../../../C/CpuArch.h"
+}
+
+#include "../../../Common/ComTry.h"
+#include "../../../Common/IntToString.h"
+
+#ifdef COMPRESS_MT
+#include "../../../Windows/System.h"
+#endif
+
+#include "../Common/ItemNameUtils.h"
+
+#include "7zHandler.h"
+#include "7zProperties.h"
+
+#ifdef __7Z_SET_PROPERTIES
+#ifdef EXTRACT_ONLY
+#include "../Common/ParseProperties.h"
+#endif
+#endif
+
+using namespace NWindows;
+
+extern UString ConvertMethodIdToString(UInt64 id);
+
+namespace NArchive {
+namespace N7z {
+
+CHandler::CHandler()
+{
+  _crcSize = 4;
+
+  #ifndef _NO_CRYPTO
+  _passwordIsDefined = false;
+  #endif
+
+  #ifdef EXTRACT_ONLY
+  #ifdef COMPRESS_MT
+  _numThreads = NSystem::GetNumberOfProcessors();
+  #endif
+  #else
+  Init();
+  #endif
+}
+
+STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
+{
+  *numItems = _db.Files.Size();
+  return S_OK;
+}
+
+#ifdef _SFX
+
+IMP_IInArchive_ArcProps_NO
+
+STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 * /* numProperties */)
+{
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP CHandler::GetPropertyInfo(UInt32 /* index */,
+      BSTR * /* name */, PROPID * /* propID */, VARTYPE * /* varType */)
+{
+  return E_NOTIMPL;
+}
+
+
+#else
+
+STATPROPSTG kArcProps[] =
+{
+  { NULL, kpidMethod, VT_BSTR},
+  { NULL, kpidSolid, VT_BOOL},
+  { NULL, kpidNumBlocks, VT_UI4},
+  { NULL, kpidPhySize, VT_UI8},
+  { NULL, kpidHeadersSize, VT_UI8},
+  { NULL, kpidOffset, VT_UI8}
+};
+
+STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
+{
+  COM_TRY_BEGIN
+  NCOM::CPropVariant prop;
+  switch(propID)
+  {
+    case kpidMethod:
+    {
+      UString resString;
+      CRecordVector<UInt64> ids;
+      int i;
+      for (i = 0; i < _db.Folders.Size(); i++)
+      {
+        const CFolder &f = _db.Folders[i];
+        for (int j = f.Coders.Size() - 1; j >= 0; j--)
+          ids.AddToUniqueSorted(f.Coders[j].MethodID);
+      }
+
+      for (i = 0; i < ids.Size(); i++)
+      {
+        UInt64 id = ids[i];
+        UString methodName;
+        /* bool methodIsKnown = */ FindMethod(EXTERNAL_CODECS_VARS id, methodName);
+        if (methodName.IsEmpty())
+          methodName = ConvertMethodIdToString(id);
+        if (!resString.IsEmpty())
+          resString += L' ';
+        resString += methodName;
+      }
+      prop = resString;
+      break;
+    }
+    case kpidSolid: prop = _db.IsSolid(); break;
+    case kpidNumBlocks: prop = (UInt32)_db.Folders.Size(); break;
+    case kpidHeadersSize:  prop = _db.HeadersSize; break;
+    case kpidPhySize:  prop = _db.PhySize; break;
+    case kpidOffset: if (_db.ArchiveInfo.StartPosition != 0) prop = _db.ArchiveInfo.StartPosition; break;
+  }
+  prop.Detach(value);
+  return S_OK;
+  COM_TRY_END
+}
+
+IMP_IInArchive_ArcProps
+
+#endif
+
+static void SetPropFromUInt64Def(CUInt64DefVector &v, int index, NCOM::CPropVariant &prop)
+{
+  UInt64 value;
+  if (v.GetItem(index, value))
+  {
+    FILETIME ft;
+    ft.dwLowDateTime = (DWORD)value;
+    ft.dwHighDateTime = (DWORD)(value >> 32);
+    prop = ft;
+  }
+}
+
+#ifndef _SFX
+
+static UString ConvertUInt32ToString(UInt32 value)
+{
+  wchar_t buffer[32];
+  ConvertUInt64ToString(value, buffer);
+  return buffer;
+}
+
+static UString GetStringForSizeValue(UInt32 value)
+{
+  for (int i = 31; i >= 0; i--)
+    if ((UInt32(1) << i) == value)
+      return ConvertUInt32ToString(i);
+  UString result;
+  if (value % (1 << 20) == 0)
+  {
+    result += ConvertUInt32ToString(value >> 20);
+    result += L"m";
+  }
+  else if (value % (1 << 10) == 0)
+  {
+    result += ConvertUInt32ToString(value >> 10);
+    result += L"k";
+  }
+  else
+  {
+    result += ConvertUInt32ToString(value);
+    result += L"b";
+  }
+  return result;
+}
+
+static const UInt64 k_Copy = 0x0;
+static const UInt64 k_LZMA  = 0x030101;
+static const UInt64 k_PPMD  = 0x030401;
+
+static wchar_t GetHex(Byte value)
+{
+  return (wchar_t)((value < 10) ? (L'0' + value) : (L'A' + (value - 10)));
+}
+static inline UString GetHex2(Byte value)
+{
+  UString result;
+  result += GetHex((Byte)(value >> 4));
+  result += GetHex((Byte)(value & 0xF));
+  return result;
+}
+
+#endif
+
+static const UInt64 k_AES  = 0x06F10701;
+
+bool CHandler::IsEncrypted(UInt32 index2) const
+{
+  CNum folderIndex = _db.FileIndexToFolderIndexMap[index2];
+  if (folderIndex != kNumNoIndex)
+  {
+    const CFolder &folderInfo = _db.Folders[folderIndex];
+    for (int i = folderInfo.Coders.Size() - 1; i >= 0; i--)
+      if (folderInfo.Coders[i].MethodID == k_AES)
+        return true;
+  }
+  return false;
+}
+
+STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID,  PROPVARIANT *value)
+{
+  COM_TRY_BEGIN
+  NCOM::CPropVariant prop;
+  
+  /*
+  const CRef2 &ref2 = _refs[index];
+  if (ref2.Refs.IsEmpty())
+    return E_FAIL;
+  const CRef &ref = ref2.Refs.Front();
+  */
+  
+  const CFileItem &item = _db.Files[index];
+  UInt32 index2 = index;
+
+  switch(propID)
+  {
+    case kpidPath:
+      if (!item.Name.IsEmpty())
+        prop = NItemName::GetOSName(item.Name);
+      break;
+    case kpidIsDir:  prop = item.IsDir; break;
+    case kpidSize:
+    {
+      prop = item.Size;
+      // prop = ref2.Size;
+      break;
+    }
+    case kpidPackSize:
+    {
+      // prop = ref2.PackSize;
+      {
+        CNum folderIndex = _db.FileIndexToFolderIndexMap[index2];
+        if (folderIndex != kNumNoIndex)
+        {
+          if (_db.FolderStartFileIndex[folderIndex] == (CNum)index2)
+            prop = _db.GetFolderFullPackSize(folderIndex);
+          /*
+          else
+            prop = (UInt64)0;
+          */
+        }
+        else
+          prop = (UInt64)0;
+      }
+      break;
+    }
+    case kpidPosition:  { UInt64 v; if (_db.StartPos.GetItem(index2, v)) prop = v; break; }
+    case kpidCTime:  SetPropFromUInt64Def(_db.CTime, index2, prop); break;
+    case kpidATime:  SetPropFromUInt64Def(_db.ATime, index2, prop); break;
+    case kpidMTime:  SetPropFromUInt64Def(_db.MTime, index2, prop); break;
+    case kpidAttrib:  if (item.AttribDefined) prop = item.Attrib; break;
+    case kpidCRC:  if (item.CrcDefined) prop = item.Crc; break;
+    case kpidEncrypted:  prop = IsEncrypted(index2); break;
+    case kpidIsAnti:  prop = _db.IsItemAnti(index2); break;
+    #ifndef _SFX
+    case kpidMethod:
+      {
+        CNum folderIndex = _db.FileIndexToFolderIndexMap[index2];
+        if (folderIndex != kNumNoIndex)
+        {
+          const CFolder &folderInfo = _db.Folders[folderIndex];
+          UString methodsString;
+          for (int i = folderInfo.Coders.Size() - 1; i >= 0; i--)
+          {
+            const CCoderInfo &coderInfo = folderInfo.Coders[i];
+            if (!methodsString.IsEmpty())
+              methodsString += L' ';
+
+            {
+              UString methodName;
+              bool methodIsKnown = FindMethod(
+                  EXTERNAL_CODECS_VARS
+                  coderInfo.MethodID, methodName);
+
+              if (methodIsKnown)
+              {
+                methodsString += methodName;
+                if (coderInfo.MethodID == k_LZMA)
+                {
+                  if (coderInfo.Props.GetCapacity() >= 5)
+                  {
+                    methodsString += L":";
+                    UInt32 dicSize = GetUi32((const Byte *)coderInfo.Props + 1);
+                    methodsString += GetStringForSizeValue(dicSize);
+                  }
+                }
+                else if (coderInfo.MethodID == k_PPMD)
+                {
+                  if (coderInfo.Props.GetCapacity() >= 5)
+                  {
+                    Byte order = *(const Byte *)coderInfo.Props;
+                    methodsString += L":o";
+                    methodsString += ConvertUInt32ToString(order);
+                    methodsString += L":mem";
+                    UInt32 dicSize = GetUi32((const Byte *)coderInfo.Props + 1);
+                    methodsString += GetStringForSizeValue(dicSize);
+                  }
+                }
+                else if (coderInfo.MethodID == k_AES)
+                {
+                  if (coderInfo.Props.GetCapacity() >= 1)
+                  {
+                    methodsString += L":";
+                    const Byte *data = (const Byte *)coderInfo.Props;
+                    Byte firstByte = *data++;
+                    UInt32 numCyclesPower = firstByte & 0x3F;
+                    methodsString += ConvertUInt32ToString(numCyclesPower);
+                    /*
+                    if ((firstByte & 0xC0) != 0)
+                    {
+                      methodsString += L":";
+                      return S_OK;
+                      UInt32 saltSize = (firstByte >> 7) & 1;
+                      UInt32 ivSize = (firstByte >> 6) & 1;
+                      if (coderInfo.Props.GetCapacity() >= 2)
+                      {
+                        Byte secondByte = *data++;
+                        saltSize += (secondByte >> 4);
+                        ivSize += (secondByte & 0x0F);
+                      }
+                    }
+                    */
+                  }
+                }
+                else
+                {
+                  if (coderInfo.Props.GetCapacity() > 0)
+                  {
+                    methodsString += L":[";
+                    for (size_t bi = 0; bi < coderInfo.Props.GetCapacity(); bi++)
+                    {
+                      if (bi > 5 && bi + 1 < coderInfo.Props.GetCapacity())
+                      {
+                        methodsString += L"..";
+                        break;
+                      }
+                      else
+                        methodsString += GetHex2(coderInfo.Props[bi]);
+                    }
+                    methodsString += L"]";
+                  }
+                }
+              }
+              else
+              {
+                methodsString += ConvertMethodIdToString(coderInfo.MethodID);
+              }
+            }
+          }
+          prop = methodsString;
+        }
+      }
+      break;
+    case kpidBlock:
+      {
+        CNum folderIndex = _db.FileIndexToFolderIndexMap[index2];
+        if (folderIndex != kNumNoIndex)
+          prop = (UInt32)folderIndex;
+      }
+      break;
+    case kpidPackedSize0:
+    case kpidPackedSize1:
+    case kpidPackedSize2:
+    case kpidPackedSize3:
+    case kpidPackedSize4:
+      {
+        CNum folderIndex = _db.FileIndexToFolderIndexMap[index2];
+        if (folderIndex != kNumNoIndex)
+        {
+          const CFolder &folderInfo = _db.Folders[folderIndex];
+          if (_db.FolderStartFileIndex[folderIndex] == (CNum)index2 &&
+              folderInfo.PackStreams.Size() > (int)(propID - kpidPackedSize0))
+          {
+            prop = _db.GetFolderPackStreamSize(folderIndex, propID - kpidPackedSize0);
+          }
+          else
+            prop = (UInt64)0;
+        }
+        else
+          prop = (UInt64)0;
+      }
+      break;
+    #endif
+  }
+  prop.Detach(value);
+  return S_OK;
+  COM_TRY_END
+}
+
+STDMETHODIMP CHandler::Open(IInStream *stream,
+    const UInt64 *maxCheckStartPosition,
+    IArchiveOpenCallback *openArchiveCallback)
+{
+  COM_TRY_BEGIN
+  Close();
+  #ifndef _SFX
+  _fileInfoPopIDs.Clear();
+  #endif
+  try
+  {
+    CMyComPtr<IArchiveOpenCallback> openArchiveCallbackTemp = openArchiveCallback;
+
+    #ifndef _NO_CRYPTO
+    CMyComPtr<ICryptoGetTextPassword> getTextPassword;
+    if (openArchiveCallback)
+    {
+      openArchiveCallbackTemp.QueryInterface(
+          IID_ICryptoGetTextPassword, &getTextPassword);
+    }
+    #endif
+    CInArchive archive;
+    RINOK(archive.Open(stream, maxCheckStartPosition));
+    #ifndef _NO_CRYPTO
+    _passwordIsDefined = false;
+    UString password;
+    #endif
+    HRESULT result = archive.ReadDatabase(
+      EXTERNAL_CODECS_VARS
+      _db
+      #ifndef _NO_CRYPTO
+      , getTextPassword, _passwordIsDefined
+      #endif
+      );
+    RINOK(result);
+    _db.Fill();
+    _inStream = stream;
+  }
+  catch(...)
+  {
+    Close();
+    return S_FALSE;
+  }
+  // _inStream = stream;
+  #ifndef _SFX
+  FillPopIDs();
+  #endif
+  return S_OK;
+  COM_TRY_END
+}
+
+STDMETHODIMP CHandler::Close()
+{
+  COM_TRY_BEGIN
+  _inStream.Release();
+  _db.Clear();
+  return S_OK;
+  COM_TRY_END
+}
+
+#ifdef __7Z_SET_PROPERTIES
+#ifdef EXTRACT_ONLY
+
+STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties)
+{
+  COM_TRY_BEGIN
+  #ifdef COMPRESS_MT
+  const UInt32 numProcessors = NSystem::GetNumberOfProcessors();
+  _numThreads = numProcessors;
+  #endif
+
+  for (int i = 0; i < numProperties; i++)
+  {
+    UString name = names[i];
+    name.MakeUpper();
+    if (name.IsEmpty())
+      return E_INVALIDARG;
+    const PROPVARIANT &value = values[i];
+    UInt32 number;
+    int index = ParseStringToUInt32(name, number);
+    if (index == 0)
+    {
+      if(name.Left(2).CompareNoCase(L"MT") == 0)
+      {
+        #ifdef COMPRESS_MT
+        RINOK(ParseMtProp(name.Mid(2), value, numProcessors, _numThreads));
+        #endif
+        continue;
+      }
+      else
+        return E_INVALIDARG;
+    }
+  }
+  return S_OK;
+  COM_TRY_END
+}
+
+#endif
+#endif
+
+IMPL_ISetCompressCodecsInfo
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zHandler.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zHandler.h
new file mode 100644
index 0000000..9adf846
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zHandler.h
@@ -0,0 +1,121 @@
+// 7z/Handler.h
+
+#ifndef __7Z_HANDLER_H
+#define __7Z_HANDLER_H
+
+#include "../../ICoder.h"
+#include "../IArchive.h"
+#include "7zIn.h"
+
+#include "7zCompressionMode.h"
+
+#include "../../Common/CreateCoder.h"
+
+#ifndef EXTRACT_ONLY
+#include "../Common/HandlerOut.h"
+#endif
+
+namespace NArchive {
+namespace N7z {
+
+#ifndef __7Z_SET_PROPERTIES
+
+#ifdef EXTRACT_ONLY
+#ifdef COMPRESS_MT
+#define __7Z_SET_PROPERTIES
+#endif
+#else
+#define __7Z_SET_PROPERTIES
+#endif
+
+#endif
+
+
+class CHandler:
+  #ifndef EXTRACT_ONLY
+  public NArchive::COutHandler,
+  #endif
+  public IInArchive,
+  #ifdef __7Z_SET_PROPERTIES
+  public ISetProperties,
+  #endif
+  #ifndef EXTRACT_ONLY
+  public IOutArchive,
+  #endif
+  PUBLIC_ISetCompressCodecsInfo
+  public CMyUnknownImp
+{
+public:
+  MY_QUERYINTERFACE_BEGIN2(IInArchive)
+  #ifdef __7Z_SET_PROPERTIES
+  MY_QUERYINTERFACE_ENTRY(ISetProperties)
+  #endif
+  #ifndef EXTRACT_ONLY
+  MY_QUERYINTERFACE_ENTRY(IOutArchive)
+  #endif
+  QUERY_ENTRY_ISetCompressCodecsInfo
+  MY_QUERYINTERFACE_END
+  MY_ADDREF_RELEASE
+
+  INTERFACE_IInArchive(;)
+
+  #ifdef __7Z_SET_PROPERTIES
+  STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties);
+  #endif
+
+  #ifndef EXTRACT_ONLY
+  INTERFACE_IOutArchive(;)
+  #endif
+
+  DECL_ISetCompressCodecsInfo
+
+  CHandler();
+
+private:
+  CMyComPtr<IInStream> _inStream;
+  NArchive::N7z::CArchiveDatabaseEx _db;
+  #ifndef _NO_CRYPTO
+  bool _passwordIsDefined;
+  #endif
+
+  #ifdef EXTRACT_ONLY
+  
+  #ifdef COMPRESS_MT
+  UInt32 _numThreads;
+  #endif
+
+  UInt32 _crcSize;
+
+  #else
+  
+  CRecordVector<CBind> _binds;
+
+  HRESULT SetPassword(CCompressionMethodMode &methodMode, IArchiveUpdateCallback *updateCallback);
+
+  HRESULT SetCompressionMethod(CCompressionMethodMode &method,
+      CObjectVector<COneMethodInfo> &methodsInfo
+      #ifdef COMPRESS_MT
+      , UInt32 numThreads
+      #endif
+      );
+
+  HRESULT SetCompressionMethod(
+      CCompressionMethodMode &method,
+      CCompressionMethodMode &headerMethod);
+
+  #endif
+
+  bool IsEncrypted(UInt32 index2) const;
+  #ifndef _SFX
+
+  CRecordVector<UInt64> _fileInfoPopIDs;
+  void FillPopIDs();
+
+  #endif
+
+  DECL_EXTERNAL_CODECS_VARS
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zHandlerOut.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zHandlerOut.cpp
new file mode 100644
index 0000000..3e2591d
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zHandlerOut.cpp
@@ -0,0 +1,477 @@
+// 7zHandlerOut.cpp
+
+#include "StdAfx.h"
+
+#include "../../../Windows/PropVariant.h"
+
+#include "../../../Common/ComTry.h"
+#include "../../../Common/StringToInt.h"
+
+#include "../../ICoder.h"
+
+#include "../Common/ItemNameUtils.h"
+#include "../Common/ParseProperties.h"
+
+#include "7zHandler.h"
+#include "7zOut.h"
+#include "7zUpdate.h"
+
+using namespace NWindows;
+
+namespace NArchive {
+namespace N7z {
+
+static const wchar_t *kLZMAMethodName = L"LZMA";
+static const wchar_t *kCopyMethod = L"Copy";
+static const wchar_t *kDefaultMethodName = kLZMAMethodName;
+
+static const UInt32 kLzmaAlgorithmX5 = 1;
+static const wchar_t *kLzmaMatchFinderForHeaders = L"BT2";
+static const UInt32 kDictionaryForHeaders = 1 << 20;
+static const UInt32 kNumFastBytesForHeaders = 273;
+static const UInt32 kAlgorithmForHeaders = kLzmaAlgorithmX5;
+
+static inline bool IsCopyMethod(const UString &methodName)
+  { return (methodName.CompareNoCase(kCopyMethod) == 0); }
+
+STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
+{
+  *type = NFileTimeType::kWindows;
+  return S_OK;
+}
+
+HRESULT CHandler::SetPassword(CCompressionMethodMode &methodMode,
+    IArchiveUpdateCallback *updateCallback)
+{
+  CMyComPtr<ICryptoGetTextPassword2> getTextPassword;
+  if (!getTextPassword)
+  {
+    CMyComPtr<IArchiveUpdateCallback> udateCallback2(updateCallback);
+    udateCallback2.QueryInterface(IID_ICryptoGetTextPassword2, &getTextPassword);
+  }
+  
+  if (getTextPassword)
+  {
+    CMyComBSTR password;
+    Int32 passwordIsDefined;
+    RINOK(getTextPassword->CryptoGetTextPassword2(
+        &passwordIsDefined, &password));
+    methodMode.PasswordIsDefined = IntToBool(passwordIsDefined);
+    if (methodMode.PasswordIsDefined)
+      methodMode.Password = password;
+  }
+  else
+    methodMode.PasswordIsDefined = false;
+  return S_OK;
+}
+
+HRESULT CHandler::SetCompressionMethod(
+    CCompressionMethodMode &methodMode,
+    CCompressionMethodMode &headerMethod)
+{
+  HRESULT res = SetCompressionMethod(methodMode, _methods
+  #ifdef COMPRESS_MT
+  , _numThreads
+  #endif
+  );
+  RINOK(res);
+  methodMode.Binds = _binds;
+
+  if (_compressHeaders)
+  {
+    // headerMethod.Methods.Add(methodMode.Methods.Back());
+
+    CObjectVector<COneMethodInfo> headerMethodInfoVector;
+    COneMethodInfo oneMethodInfo;
+    oneMethodInfo.MethodName = kLZMAMethodName;
+    {
+      CProp prop;
+      prop.Id = NCoderPropID::kMatchFinder;
+      prop.Value = kLzmaMatchFinderForHeaders;
+      oneMethodInfo.Props.Add(prop);
+    }
+    {
+      CProp prop;
+      prop.Id = NCoderPropID::kAlgorithm;
+      prop.Value = kAlgorithmForHeaders;
+      oneMethodInfo.Props.Add(prop);
+    }
+    {
+      CProp prop;
+      prop.Id = NCoderPropID::kNumFastBytes;
+      prop.Value = (UInt32)kNumFastBytesForHeaders;
+      oneMethodInfo.Props.Add(prop);
+    }
+    {
+      CProp prop;
+      prop.Id = NCoderPropID::kDictionarySize;
+      prop.Value = (UInt32)kDictionaryForHeaders;
+      oneMethodInfo.Props.Add(prop);
+    }
+    headerMethodInfoVector.Add(oneMethodInfo);
+    HRESULT res = SetCompressionMethod(headerMethod, headerMethodInfoVector
+      #ifdef COMPRESS_MT
+      ,1
+      #endif
+    );
+    RINOK(res);
+  }
+  return S_OK;
+}
+
+HRESULT CHandler::SetCompressionMethod(
+    CCompressionMethodMode &methodMode,
+    CObjectVector<COneMethodInfo> &methodsInfo
+    #ifdef COMPRESS_MT
+    , UInt32 numThreads
+    #endif
+    )
+{
+  UInt32 level = _level;
+  
+  if (methodsInfo.IsEmpty())
+  {
+    COneMethodInfo oneMethodInfo;
+    oneMethodInfo.MethodName = ((level == 0) ? kCopyMethod : kDefaultMethodName);
+    methodsInfo.Add(oneMethodInfo);
+  }
+
+  bool needSolid = false;
+  for(int i = 0; i < methodsInfo.Size(); i++)
+  {
+    COneMethodInfo &oneMethodInfo = methodsInfo[i];
+    SetCompressionMethod2(oneMethodInfo
+      #ifdef COMPRESS_MT
+      , numThreads
+      #endif
+      );
+
+    if (!IsCopyMethod(oneMethodInfo.MethodName))
+      needSolid = true;
+
+    CMethodFull methodFull;
+
+    if (!FindMethod(
+        EXTERNAL_CODECS_VARS
+        oneMethodInfo.MethodName, methodFull.Id, methodFull.NumInStreams, methodFull.NumOutStreams))
+      return E_INVALIDARG;
+    methodFull.Props = oneMethodInfo.Props;
+    methodMode.Methods.Add(methodFull);
+
+    if (!_numSolidBytesDefined)
+    {
+      for (int j = 0; j < methodFull.Props.Size(); j++)
+      {
+        const CProp &prop = methodFull.Props[j];
+        if ((prop.Id == NCoderPropID::kDictionarySize ||
+             prop.Id == NCoderPropID::kUsedMemorySize) && prop.Value.vt == VT_UI4)
+        {
+          _numSolidBytes = ((UInt64)prop.Value.ulVal) << 7;
+          const UInt64 kMinSize = (1 << 24);
+          if (_numSolidBytes < kMinSize)
+            _numSolidBytes = kMinSize;
+          _numSolidBytesDefined = true;
+          break;
+        }
+      }
+    }
+  }
+
+  if (!needSolid && !_numSolidBytesDefined)
+  {
+    _numSolidBytesDefined = true;
+    _numSolidBytes  = 0;
+  }
+  return S_OK;
+}
+
+static HRESULT GetTime(IArchiveUpdateCallback *updateCallback, int index, bool writeTime, PROPID propID, UInt64 &ft, bool &ftDefined)
+{
+  ft = 0;
+  ftDefined = false;
+  if (!writeTime)
+    return S_OK;
+  NCOM::CPropVariant prop;
+  RINOK(updateCallback->GetProperty(index, propID, &prop));
+  if (prop.vt == VT_FILETIME)
+  {
+    ft = prop.filetime.dwLowDateTime | ((UInt64)prop.filetime.dwHighDateTime << 32);
+    ftDefined = true;
+  }
+  else if (prop.vt != VT_EMPTY)
+    return E_INVALIDARG;
+  return S_OK;
+}
+
+STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
+    IArchiveUpdateCallback *updateCallback)
+{
+  COM_TRY_BEGIN
+
+  const CArchiveDatabaseEx *db = 0;
+  #ifdef _7Z_VOL
+  if(_volumes.Size() > 1)
+    return E_FAIL;
+  const CVolume *volume = 0;
+  if (_volumes.Size() == 1)
+  {
+    volume = &_volumes.Front();
+    db = &volume->Database;
+  }
+  #else
+  if (_inStream != 0)
+    db = &_db;
+  #endif
+
+  CObjectVector<CUpdateItem> updateItems;
+  
+  for (UInt32 i = 0; i < numItems; i++)
+  {
+    Int32 newData;
+    Int32 newProperties;
+    UInt32 indexInArchive;
+    if (!updateCallback)
+      return E_FAIL;
+    RINOK(updateCallback->GetUpdateItemInfo(i, &newData, &newProperties, &indexInArchive));
+    CUpdateItem ui;
+    ui.NewProperties = IntToBool(newProperties);
+    ui.NewData = IntToBool(newData);
+    ui.IndexInArchive = indexInArchive;
+    ui.IndexInClient = i;
+    ui.IsAnti = false;
+    ui.Size = 0;
+
+    if (ui.IndexInArchive != -1)
+    {
+      const CFileItem &fi = db->Files[ui.IndexInArchive];
+      ui.Name = fi.Name;
+      ui.IsDir = fi.IsDir;
+      ui.Size = fi.Size;
+      ui.IsAnti = db->IsItemAnti(ui.IndexInArchive);
+      
+      ui.CTimeDefined = db->CTime.GetItem(ui.IndexInArchive, ui.CTime);
+      ui.ATimeDefined = db->ATime.GetItem(ui.IndexInArchive, ui.ATime);
+      ui.MTimeDefined = db->MTime.GetItem(ui.IndexInArchive, ui.MTime);
+    }
+
+    if (ui.NewProperties)
+    {
+      bool nameIsDefined;
+      bool folderStatusIsDefined;
+      {
+        NCOM::CPropVariant prop;
+        RINOK(updateCallback->GetProperty(i, kpidAttrib, &prop));
+        if (prop.vt == VT_EMPTY)
+          ui.AttribDefined = false;
+        else if (prop.vt != VT_UI4)
+          return E_INVALIDARG;
+        else
+        {
+          ui.Attrib = prop.ulVal;
+          ui.AttribDefined = true;
+        }
+      }
+      
+      // we need MTime to sort files.
+      RINOK(GetTime(updateCallback, i, WriteCTime, kpidCTime, ui.CTime, ui.CTimeDefined));
+      RINOK(GetTime(updateCallback, i, WriteATime, kpidATime, ui.ATime, ui.ATimeDefined));
+      RINOK(GetTime(updateCallback, i, true,       kpidMTime, ui.MTime, ui.MTimeDefined));
+
+      {
+        NCOM::CPropVariant prop;
+        RINOK(updateCallback->GetProperty(i, kpidPath, &prop));
+        if (prop.vt == VT_EMPTY)
+          nameIsDefined = false;
+        else if (prop.vt != VT_BSTR)
+          return E_INVALIDARG;
+        else
+        {
+          ui.Name = NItemName::MakeLegalName(prop.bstrVal);
+          nameIsDefined = true;
+        }
+      }
+      {
+        NCOM::CPropVariant prop;
+        RINOK(updateCallback->GetProperty(i, kpidIsDir, &prop));
+        if (prop.vt == VT_EMPTY)
+          folderStatusIsDefined = false;
+        else if (prop.vt != VT_BOOL)
+          return E_INVALIDARG;
+        else
+        {
+          ui.IsDir = (prop.boolVal != VARIANT_FALSE);
+          folderStatusIsDefined = true;
+        }
+      }
+
+      {
+        NCOM::CPropVariant prop;
+        RINOK(updateCallback->GetProperty(i, kpidIsAnti, &prop));
+        if (prop.vt == VT_EMPTY)
+          ui.IsAnti = false;
+        else if (prop.vt != VT_BOOL)
+          return E_INVALIDARG;
+        else
+          ui.IsAnti = (prop.boolVal != VARIANT_FALSE);
+      }
+
+      if (ui.IsAnti)
+      {
+        ui.AttribDefined = false;
+
+        ui.CTimeDefined = false;
+        ui.ATimeDefined = false;
+        ui.MTimeDefined = false;
+        
+        ui.Size = 0;
+      }
+
+      if (!folderStatusIsDefined && ui.AttribDefined)
+        ui.SetDirStatusFromAttrib();
+    }
+
+    if (ui.NewData)
+    {
+      NCOM::CPropVariant prop;
+      RINOK(updateCallback->GetProperty(i, kpidSize, &prop));
+      if (prop.vt != VT_UI8)
+        return E_INVALIDARG;
+      ui.Size = (UInt64)prop.uhVal.QuadPart;
+      if (ui.Size != 0 && ui.IsAnti)
+        return E_INVALIDARG;
+    }
+    updateItems.Add(ui);
+  }
+
+  CCompressionMethodMode methodMode, headerMethod;
+  RINOK(SetCompressionMethod(methodMode, headerMethod));
+  #ifdef COMPRESS_MT
+  methodMode.NumThreads = _numThreads;
+  headerMethod.NumThreads = 1;
+  #endif
+
+  RINOK(SetPassword(methodMode, updateCallback));
+
+  bool compressMainHeader = _compressHeaders;  // check it
+
+  bool encryptHeaders = false;
+
+  if (methodMode.PasswordIsDefined)
+  {
+    if (_encryptHeadersSpecified)
+      encryptHeaders = _encryptHeaders;
+    #ifndef _NO_CRYPTO
+    else
+      encryptHeaders = _passwordIsDefined;
+    #endif
+    compressMainHeader = true;
+    if(encryptHeaders)
+      RINOK(SetPassword(headerMethod, updateCallback));
+  }
+
+  if (numItems < 2)
+    compressMainHeader = false;
+
+  CUpdateOptions options;
+  options.Method = &methodMode;
+  options.HeaderMethod = (_compressHeaders || encryptHeaders) ? &headerMethod : 0;
+  options.UseFilters = _level != 0 && _autoFilter;
+  options.MaxFilter = _level >= 8;
+
+  options.HeaderOptions.CompressMainHeader = compressMainHeader;
+  options.HeaderOptions.WriteCTime = WriteCTime;
+  options.HeaderOptions.WriteATime = WriteATime;
+  options.HeaderOptions.WriteMTime = WriteMTime;
+  
+  options.NumSolidFiles = _numSolidFiles;
+  options.NumSolidBytes = _numSolidBytes;
+  options.SolidExtension = _solidExtension;
+  options.RemoveSfxBlock = _removeSfxBlock;
+  options.VolumeMode = _volumeMode;
+
+  COutArchive archive;
+  CArchiveDatabase newDatabase;
+  HRESULT res = Update(
+      EXTERNAL_CODECS_VARS
+      #ifdef _7Z_VOL
+      volume ? volume->Stream: 0,
+      volume ? db : 0,
+      #else
+      _inStream,
+      db,
+      #endif
+      updateItems,
+      archive, newDatabase, outStream, updateCallback, options);
+
+  RINOK(res);
+
+  updateItems.ClearAndFree();
+
+  return archive.WriteDatabase(EXTERNAL_CODECS_VARS
+      newDatabase, options.HeaderMethod, options.HeaderOptions);
+
+  COM_TRY_END
+}
+
+static HRESULT GetBindInfoPart(UString &srcString, UInt32 &coder, UInt32 &stream)
+{
+  stream = 0;
+  int index = ParseStringToUInt32(srcString, coder);
+  if (index == 0)
+    return E_INVALIDARG;
+  srcString.Delete(0, index);
+  if (srcString[0] == 'S')
+  {
+    srcString.Delete(0);
+    int index = ParseStringToUInt32(srcString, stream);
+    if (index == 0)
+      return E_INVALIDARG;
+    srcString.Delete(0, index);
+  }
+  return S_OK;
+}
+
+static HRESULT GetBindInfo(UString &srcString, CBind &bind)
+{
+  RINOK(GetBindInfoPart(srcString, bind.OutCoder, bind.OutStream));
+  if (srcString[0] != ':')
+    return E_INVALIDARG;
+  srcString.Delete(0);
+  RINOK(GetBindInfoPart(srcString, bind.InCoder, bind.InStream));
+  if (!srcString.IsEmpty())
+    return E_INVALIDARG;
+  return S_OK;
+}
+
+STDMETHODIMP CHandler::SetProperties(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties)
+{
+  COM_TRY_BEGIN
+  _binds.Clear();
+  BeforeSetProperty();
+
+  for (int i = 0; i < numProperties; i++)
+  {
+    UString name = names[i];
+    name.MakeUpper();
+    if (name.IsEmpty())
+      return E_INVALIDARG;
+
+    const PROPVARIANT &value = values[i];
+
+    if (name[0] == 'B')
+    {
+      name.Delete(0);
+      CBind bind;
+      RINOK(GetBindInfo(name, bind));
+      _binds.Add(bind);
+      continue;
+    }
+
+    RINOK(SetProperty(name, value));
+  }
+
+  return S_OK;
+  COM_TRY_END
+}
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zHeader.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zHeader.cpp
new file mode 100644
index 0000000..f232a23
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zHeader.cpp
@@ -0,0 +1,27 @@
+// 7z/Header.cpp
+
+#include "StdAfx.h"
+#include "7zHeader.h"
+
+namespace NArchive {
+namespace N7z {
+
+Byte kSignature[kSignatureSize] = {'7' + 1, 'z', 0xBC, 0xAF, 0x27, 0x1C};
+#ifdef _7Z_VOL
+Byte kFinishSignature[kSignatureSize] = {'7' + 1, 'z', 0xBC, 0xAF, 0x27, 0x1C + 1};
+#endif
+
+class SignatureInitializer
+{
+public:
+  SignatureInitializer()
+  {
+    kSignature[0]--;
+    #ifdef _7Z_VOL
+    kFinishSignature[0]--;
+    #endif
+  };
+} g_SignatureInitializer;
+
+}}
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zHeader.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zHeader.h
new file mode 100644
index 0000000..30622b9
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zHeader.h
@@ -0,0 +1,97 @@
+// 7z/7zHeader.h
+
+#ifndef __7Z_HEADER_H
+#define __7Z_HEADER_H
+
+#include "../../../Common/Types.h"
+
+namespace NArchive {
+namespace N7z {
+
+const int kSignatureSize = 6;
+extern Byte kSignature[kSignatureSize];
+
+// #define _7Z_VOL
+// 7z-MultiVolume is not finished yet.
+// It can work already, but I still do not like some
+// things of that new multivolume format.
+// So please keep it commented.
+
+#ifdef _7Z_VOL
+extern Byte kFinishSignature[kSignatureSize];
+#endif
+
+struct CArchiveVersion
+{
+  Byte Major;
+  Byte Minor;
+};
+
+const Byte kMajorVersion = 0;
+
+struct CStartHeader
+{
+  UInt64 NextHeaderOffset;
+  UInt64 NextHeaderSize;
+  UInt32 NextHeaderCRC;
+};
+
+const UInt32 kStartHeaderSize = 20;
+
+#ifdef _7Z_VOL
+struct CFinishHeader: public CStartHeader
+{
+  UInt64 ArchiveStartOffset;  // data offset from end if that struct
+  UInt64 AdditionalStartBlockSize; // start  signature & start header size
+};
+
+const UInt32 kFinishHeaderSize = kStartHeaderSize + 16;
+#endif
+
+namespace NID
+{
+  enum EEnum
+  {
+    kEnd,
+
+    kHeader,
+
+    kArchiveProperties,
+    
+    kAdditionalStreamsInfo,
+    kMainStreamsInfo,
+    kFilesInfo,
+    
+    kPackInfo,
+    kUnpackInfo,
+    kSubStreamsInfo,
+
+    kSize,
+    kCRC,
+
+    kFolder,
+
+    kCodersUnpackSize,
+    kNumUnpackStream,
+
+    kEmptyStream,
+    kEmptyFile,
+    kAnti,
+
+    kName,
+    kCTime,
+    kATime,
+    kMTime,
+    kWinAttributes,
+    kComment,
+
+    kEncodedHeader,
+
+    kStartPos,
+    kDummy
+  };
+}
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zIn.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zIn.cpp
new file mode 100644
index 0000000..ab78eea
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zIn.cpp
@@ -0,0 +1,1260 @@
+// 7zIn.cpp
+
+#include "StdAfx.h"
+
+extern "C"
+{
+  #include "../../../../C/7zCrc.h"
+  #include "../../../../C/CpuArch.h"
+}
+
+#include "../../Common/StreamObjects.h"
+#include "../../Common/StreamUtils.h"
+
+#include "7zDecode.h"
+#include "7zIn.h"
+
+#define Get16(p) GetUi16(p)
+#define Get32(p) GetUi32(p)
+#define Get64(p) GetUi64(p)
+
+// define FORMAT_7Z_RECOVERY if you want to recover multivolume archives with empty StartHeader
+#ifndef _SFX
+#define FORMAT_7Z_RECOVERY
+#endif
+
+namespace NArchive {
+namespace N7z {
+
+static void BoolVector_Fill_False(CBoolVector &v, int size)
+{
+  v.Clear();
+  v.Reserve(size);
+  for (int i = 0; i < size; i++)
+    v.Add(false);
+}
+
+static bool BoolVector_GetAndSet(CBoolVector &v, UInt32 index)
+{
+  if (index >= (UInt32)v.Size())
+    return true;
+  bool res = v[index];
+  v[index] = true;
+  return res;
+}
+
+bool CFolder::CheckStructure() const
+{
+  const int kNumCodersMax = sizeof(UInt32) * 8; // don't change it
+  const int kMaskSize = sizeof(UInt32) * 8; // it must be >= kNumCodersMax
+  const int kNumBindsMax = 32;
+
+  if (Coders.Size() > kNumCodersMax || BindPairs.Size() > kNumBindsMax)
+    return false;
+
+  {
+    CBoolVector v;
+    BoolVector_Fill_False(v, BindPairs.Size() + PackStreams.Size());
+    
+    int i;
+    for (i = 0; i < BindPairs.Size(); i++)
+      if (BoolVector_GetAndSet(v, BindPairs[i].InIndex))
+        return false;
+    for (i = 0; i < PackStreams.Size(); i++)
+      if (BoolVector_GetAndSet(v, PackStreams[i]))
+        return false;
+    
+    BoolVector_Fill_False(v, UnpackSizes.Size());
+    for (i = 0; i < BindPairs.Size(); i++)
+      if (BoolVector_GetAndSet(v, BindPairs[i].OutIndex))
+        return false;
+  }
+  
+  UInt32 mask[kMaskSize];
+  int i;
+  for (i = 0; i < kMaskSize; i++)
+    mask[i] = 0;
+
+  {
+    CIntVector inStreamToCoder, outStreamToCoder;
+    for (i = 0; i < Coders.Size(); i++)
+    {
+      CNum j;
+      const CCoderInfo &coder = Coders[i];
+      for (j = 0; j < coder.NumInStreams; j++)
+        inStreamToCoder.Add(i);
+      for (j = 0; j < coder.NumOutStreams; j++)
+        outStreamToCoder.Add(i);
+    }
+    
+    for (i = 0; i < BindPairs.Size(); i++)
+    {
+      const CBindPair &bp = BindPairs[i];
+      mask[inStreamToCoder[bp.InIndex]] |= (1 << outStreamToCoder[bp.OutIndex]);
+    }
+  }
+  
+  for (i = 0; i < kMaskSize; i++)
+    for (int j = 0; j < kMaskSize; j++)
+      if (((1 << j) & mask[i]) != 0)
+        mask[i] |= mask[j];
+
+  for (i = 0; i < kMaskSize; i++)
+    if (((1 << i) & mask[i]) != 0)
+      return false;
+
+  return true;
+}
+
+class CInArchiveException {};
+
+static void ThrowException() { throw CInArchiveException(); }
+static inline void ThrowEndOfData()   { ThrowException(); }
+static inline void ThrowUnsupported() { ThrowException(); }
+static inline void ThrowIncorrect()   { ThrowException(); }
+static inline void ThrowUnsupportedVersion() { ThrowException(); }
+
+/*
+class CInArchiveException
+{
+public:
+  enum CCauseType
+  {
+    kUnsupportedVersion = 0,
+    kUnsupported,
+    kIncorrect,
+    kEndOfData,
+  } Cause;
+  CInArchiveException(CCauseType cause): Cause(cause) {};
+};
+
+static void ThrowException(CInArchiveException::CCauseType c) { throw CInArchiveException(c); }
+static void ThrowEndOfData()   { ThrowException(CInArchiveException::kEndOfData); }
+static void ThrowUnsupported() { ThrowException(CInArchiveException::kUnsupported); }
+static void ThrowIncorrect()   { ThrowException(CInArchiveException::kIncorrect); }
+static void ThrowUnsupportedVersion() { ThrowException(CInArchiveException::kUnsupportedVersion); }
+*/
+
+class CStreamSwitch
+{
+  CInArchive *_archive;
+  bool _needRemove;
+public:
+  CStreamSwitch(): _needRemove(false) {}
+  ~CStreamSwitch() { Remove(); }
+  void Remove();
+  void Set(CInArchive *archive, const Byte *data, size_t size);
+  void Set(CInArchive *archive, const CByteBuffer &byteBuffer);
+  void Set(CInArchive *archive, const CObjectVector<CByteBuffer> *dataVector);
+};
+
+void CStreamSwitch::Remove()
+{
+  if (_needRemove)
+  {
+    _archive->DeleteByteStream();
+    _needRemove = false;
+  }
+}
+
+void CStreamSwitch::Set(CInArchive *archive, const Byte *data, size_t size)
+{
+  Remove();
+  _archive = archive;
+  _archive->AddByteStream(data, size);
+  _needRemove = true;
+}
+
+void CStreamSwitch::Set(CInArchive *archive, const CByteBuffer &byteBuffer)
+{
+  Set(archive, byteBuffer, byteBuffer.GetCapacity());
+}
+
+void CStreamSwitch::Set(CInArchive *archive, const CObjectVector<CByteBuffer> *dataVector)
+{
+  Remove();
+  Byte external = archive->ReadByte();
+  if (external != 0)
+  {
+    int dataIndex = (int)archive->ReadNum();
+    if (dataIndex < 0 || dataIndex >= dataVector->Size())
+      ThrowIncorrect();
+    Set(archive, (*dataVector)[dataIndex]);
+  }
+}
+
+Byte CInByte2::ReadByte()
+{
+  if (_pos >= _size)
+    ThrowEndOfData();
+  return _buffer[_pos++];
+}
+
+void CInByte2::ReadBytes(Byte *data, size_t size)
+{
+  if (size > _size - _pos)
+    ThrowEndOfData();
+  for (size_t i = 0; i < size; i++)
+    data[i] = _buffer[_pos++];
+}
+
+void CInByte2::SkeepData(UInt64 size)
+{
+  if (size > _size - _pos)
+    ThrowEndOfData();
+  _pos += (size_t)size;
+}
+
+void CInByte2::SkeepData()
+{
+  SkeepData(ReadNumber());
+}
+
+UInt64 CInByte2::ReadNumber()
+{
+  if (_pos >= _size)
+    ThrowEndOfData();
+  Byte firstByte = _buffer[_pos++];
+  Byte mask = 0x80;
+  UInt64 value = 0;
+  for (int i = 0; i < 8; i++)
+  {
+    if ((firstByte & mask) == 0)
+    {
+      UInt64 highPart = firstByte & (mask - 1);
+      value += (highPart << (i * 8));
+      return value;
+    }
+    if (_pos >= _size)
+      ThrowEndOfData();
+    value |= ((UInt64)_buffer[_pos++] << (8 * i));
+    mask >>= 1;
+  }
+  return value;
+}
+
+CNum CInByte2::ReadNum()
+{
+  UInt64 value = ReadNumber();
+  if (value > kNumMax)
+    ThrowUnsupported();
+  return (CNum)value;
+}
+
+UInt32 CInByte2::ReadUInt32()
+{
+  if (_pos + 4 > _size)
+    ThrowEndOfData();
+  UInt32 res = Get32(_buffer + _pos);
+  _pos += 4;
+  return res;
+}
+
+UInt64 CInByte2::ReadUInt64()
+{
+  if (_pos + 8 > _size)
+    ThrowEndOfData();
+  UInt64 res = Get64(_buffer + _pos);
+  _pos += 8;
+  return res;
+}
+
+void CInByte2::ReadString(UString &s)
+{
+  const Byte *buf = _buffer + _pos;
+  size_t rem = (_size - _pos) / 2 * 2;
+  {
+    size_t i;
+    for (i = 0; i < rem; i += 2)
+      if (buf[i] == 0 && buf[i + 1] == 0)
+        break;
+    if (i == rem)
+      ThrowEndOfData();
+    rem = i;
+  }
+  int len = (int)(rem / 2);
+  if (len < 0 || (size_t)len * 2 != rem)
+    ThrowUnsupported();
+  wchar_t *p = s.GetBuffer(len);
+  int i;
+  for (i = 0; i < len; i++, buf += 2)
+    p[i] = (wchar_t)Get16(buf);
+  s.ReleaseBuffer(len);
+  _pos += rem + 2;
+}
+
+static inline bool TestSignatureCandidate(const Byte *p)
+{
+  for (int i = 0; i < kSignatureSize; i++)
+    if (p[i] != kSignature[i])
+      return false;
+  return (p[0x1A] == 0 && p[0x1B] == 0);
+}
+
+HRESULT CInArchive::FindAndReadSignature(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
+{
+  RINOK(ReadStream_FALSE(stream, _header, kHeaderSize));
+
+  if (TestSignatureCandidate(_header))
+    return S_OK;
+
+  CByteBuffer byteBuffer;
+  const UInt32 kBufferSize = (1 << 16);
+  byteBuffer.SetCapacity(kBufferSize);
+  Byte *buffer = byteBuffer;
+  UInt32 numPrevBytes = kHeaderSize - 1;
+  memcpy(buffer, _header + 1, numPrevBytes);
+  UInt64 curTestPos = _arhiveBeginStreamPosition + 1;
+  for (;;)
+  {
+    if (searchHeaderSizeLimit != NULL)
+      if (curTestPos - _arhiveBeginStreamPosition > *searchHeaderSizeLimit)
+        break;
+    do
+    {
+      UInt32 numReadBytes = kBufferSize - numPrevBytes;
+      UInt32 processedSize;
+      RINOK(stream->Read(buffer + numPrevBytes, numReadBytes, &processedSize));
+      numPrevBytes += processedSize;
+      if (processedSize == 0)
+        return S_FALSE;
+    }
+    while (numPrevBytes < kHeaderSize);
+    UInt32 numTests = numPrevBytes - kHeaderSize + 1;
+    for (UInt32 pos = 0; pos < numTests; pos++)
+    {
+      for (; buffer[pos] != '7' && pos < numTests; pos++);
+      if (pos == numTests)
+        break;
+      if (TestSignatureCandidate(buffer + pos))
+      {
+        memcpy(_header, buffer + pos, kHeaderSize);
+        curTestPos += pos;
+        _arhiveBeginStreamPosition = curTestPos;
+        return stream->Seek(curTestPos + kHeaderSize, STREAM_SEEK_SET, NULL);
+      }
+    }
+    curTestPos += numTests;
+    numPrevBytes -= numTests;
+    memmove(buffer, buffer + numTests, numPrevBytes);
+  }
+  return S_FALSE;
+}
+
+// S_FALSE means that file is not archive
+HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
+{
+  HeadersSize = 0;
+  Close();
+  RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_arhiveBeginStreamPosition))
+  RINOK(FindAndReadSignature(stream, searchHeaderSizeLimit));
+  _stream = stream;
+  return S_OK;
+}
+  
+void CInArchive::Close()
+{
+  _stream.Release();
+}
+
+void CInArchive::ReadArchiveProperties(CInArchiveInfo & /* archiveInfo */)
+{
+  for (;;)
+  {
+    if (ReadID() == NID::kEnd)
+      break;
+    SkeepData();
+  }
+}
+
+void CInArchive::GetNextFolderItem(CFolder &folder)
+{
+  CNum numCoders = ReadNum();
+
+  folder.Coders.Clear();
+  folder.Coders.Reserve((int)numCoders);
+  CNum numInStreams = 0;
+  CNum numOutStreams = 0;
+  CNum i;
+  for (i = 0; i < numCoders; i++)
+  {
+    folder.Coders.Add(CCoderInfo());
+    CCoderInfo &coder = folder.Coders.Back();
+
+    {
+      Byte mainByte = ReadByte();
+      int idSize = (mainByte & 0xF);
+      Byte longID[15];
+      ReadBytes(longID, idSize);
+      if (idSize > 8)
+        ThrowUnsupported();
+      UInt64 id = 0;
+      for (int j = 0; j < idSize; j++)
+        id |= (UInt64)longID[idSize - 1 - j] << (8 * j);
+      coder.MethodID = id;
+
+      if ((mainByte & 0x10) != 0)
+      {
+        coder.NumInStreams = ReadNum();
+        coder.NumOutStreams = ReadNum();
+      }
+      else
+      {
+        coder.NumInStreams = 1;
+        coder.NumOutStreams = 1;
+      }
+      if ((mainByte & 0x20) != 0)
+      {
+        CNum propsSize = ReadNum();
+        coder.Props.SetCapacity((size_t)propsSize);
+        ReadBytes((Byte *)coder.Props, (size_t)propsSize);
+      }
+      if ((mainByte & 0x80) != 0)
+        ThrowUnsupported();
+    }
+    numInStreams += coder.NumInStreams;
+    numOutStreams += coder.NumOutStreams;
+  }
+
+  CNum numBindPairs = numOutStreams - 1;
+  folder.BindPairs.Clear();
+  folder.BindPairs.Reserve(numBindPairs);
+  for (i = 0; i < numBindPairs; i++)
+  {
+    CBindPair bp;
+    bp.InIndex = ReadNum();
+    bp.OutIndex = ReadNum();
+    folder.BindPairs.Add(bp);
+  }
+
+  if (numInStreams < numBindPairs)
+    ThrowUnsupported();
+  CNum numPackStreams = numInStreams - numBindPairs;
+  folder.PackStreams.Reserve(numPackStreams);
+  if (numPackStreams == 1)
+  {
+    for (i = 0; i < numInStreams; i++)
+      if (folder.FindBindPairForInStream(i) < 0)
+      {
+        folder.PackStreams.Add(i);
+        break;
+      }
+    if (folder.PackStreams.Size() != 1)
+      ThrowUnsupported();
+  }
+  else
+    for (i = 0; i < numPackStreams; i++)
+      folder.PackStreams.Add(ReadNum());
+}
+
+void CInArchive::WaitAttribute(UInt64 attribute)
+{
+  for (;;)
+  {
+    UInt64 type = ReadID();
+    if (type == attribute)
+      return;
+    if (type == NID::kEnd)
+      ThrowIncorrect();
+    SkeepData();
+  }
+}
+
+void CInArchive::ReadHashDigests(int numItems,
+    CBoolVector &digestsDefined,
+    CRecordVector<UInt32> &digests)
+{
+  ReadBoolVector2(numItems, digestsDefined);
+  digests.Clear();
+  digests.Reserve(numItems);
+  for (int i = 0; i < numItems; i++)
+  {
+    UInt32 crc = 0;
+    if (digestsDefined[i])
+      crc = ReadUInt32();
+    digests.Add(crc);
+  }
+}
+
+void CInArchive::ReadPackInfo(
+    UInt64 &dataOffset,
+    CRecordVector<UInt64> &packSizes,
+    CBoolVector &packCRCsDefined,
+    CRecordVector<UInt32> &packCRCs)
+{
+  dataOffset = ReadNumber();
+  CNum numPackStreams = ReadNum();
+
+  WaitAttribute(NID::kSize);
+  packSizes.Clear();
+  packSizes.Reserve(numPackStreams);
+  for (CNum i = 0; i < numPackStreams; i++)
+    packSizes.Add(ReadNumber());
+
+  UInt64 type;
+  for (;;)
+  {
+    type = ReadID();
+    if (type == NID::kEnd)
+      break;
+    if (type == NID::kCRC)
+    {
+      ReadHashDigests(numPackStreams, packCRCsDefined, packCRCs);
+      continue;
+    }
+    SkeepData();
+  }
+  if (packCRCsDefined.IsEmpty())
+  {
+    BoolVector_Fill_False(packCRCsDefined, numPackStreams);
+    packCRCs.Reserve(numPackStreams);
+    packCRCs.Clear();
+    for (CNum i = 0; i < numPackStreams; i++)
+      packCRCs.Add(0);
+  }
+}
+
+void CInArchive::ReadUnpackInfo(
+    const CObjectVector<CByteBuffer> *dataVector,
+    CObjectVector<CFolder> &folders)
+{
+  WaitAttribute(NID::kFolder);
+  CNum numFolders = ReadNum();
+
+  {
+    CStreamSwitch streamSwitch;
+    streamSwitch.Set(this, dataVector);
+    folders.Clear();
+    folders.Reserve(numFolders);
+    for (CNum i = 0; i < numFolders; i++)
+    {
+      folders.Add(CFolder());
+      GetNextFolderItem(folders.Back());
+    }
+  }
+
+  WaitAttribute(NID::kCodersUnpackSize);
+
+  CNum i;
+  for (i = 0; i < numFolders; i++)
+  {
+    CFolder &folder = folders[i];
+    CNum numOutStreams = folder.GetNumOutStreams();
+    folder.UnpackSizes.Reserve(numOutStreams);
+    for (CNum j = 0; j < numOutStreams; j++)
+      folder.UnpackSizes.Add(ReadNumber());
+  }
+
+  for (;;)
+  {
+    UInt64 type = ReadID();
+    if (type == NID::kEnd)
+      return;
+    if (type == NID::kCRC)
+    {
+      CBoolVector crcsDefined;
+      CRecordVector<UInt32> crcs;
+      ReadHashDigests(numFolders, crcsDefined, crcs);
+      for (i = 0; i < numFolders; i++)
+      {
+        CFolder &folder = folders[i];
+        folder.UnpackCRCDefined = crcsDefined[i];
+        folder.UnpackCRC = crcs[i];
+      }
+      continue;
+    }
+    SkeepData();
+  }
+}
+
+void CInArchive::ReadSubStreamsInfo(
+    const CObjectVector<CFolder> &folders,
+    CRecordVector<CNum> &numUnpackStreamsInFolders,
+    CRecordVector<UInt64> &unpackSizes,
+    CBoolVector &digestsDefined,
+    CRecordVector<UInt32> &digests)
+{
+  numUnpackStreamsInFolders.Clear();
+  numUnpackStreamsInFolders.Reserve(folders.Size());
+  UInt64 type;
+  for (;;)
+  {
+    type = ReadID();
+    if (type == NID::kNumUnpackStream)
+    {
+      for (int i = 0; i < folders.Size(); i++)
+        numUnpackStreamsInFolders.Add(ReadNum());
+      continue;
+    }
+    if (type == NID::kCRC || type == NID::kSize)
+      break;
+    if (type == NID::kEnd)
+      break;
+    SkeepData();
+  }
+
+  if (numUnpackStreamsInFolders.IsEmpty())
+    for (int i = 0; i < folders.Size(); i++)
+      numUnpackStreamsInFolders.Add(1);
+
+  int i;
+  for (i = 0; i < numUnpackStreamsInFolders.Size(); i++)
+  {
+    // v3.13 incorrectly worked with empty folders
+    // v4.07: we check that folder is empty
+    CNum numSubstreams = numUnpackStreamsInFolders[i];
+    if (numSubstreams == 0)
+      continue;
+    UInt64 sum = 0;
+    for (CNum j = 1; j < numSubstreams; j++)
+      if (type == NID::kSize)
+      {
+        UInt64 size = ReadNumber();
+        unpackSizes.Add(size);
+        sum += size;
+      }
+    unpackSizes.Add(folders[i].GetUnpackSize() - sum);
+  }
+  if (type == NID::kSize)
+    type = ReadID();
+
+  int numDigests = 0;
+  int numDigestsTotal = 0;
+  for (i = 0; i < folders.Size(); i++)
+  {
+    CNum numSubstreams = numUnpackStreamsInFolders[i];
+    if (numSubstreams != 1 || !folders[i].UnpackCRCDefined)
+      numDigests += numSubstreams;
+    numDigestsTotal += numSubstreams;
+  }
+
+  for (;;)
+  {
+    if (type == NID::kCRC)
+    {
+      CBoolVector digestsDefined2;
+      CRecordVector<UInt32> digests2;
+      ReadHashDigests(numDigests, digestsDefined2, digests2);
+      int digestIndex = 0;
+      for (i = 0; i < folders.Size(); i++)
+      {
+        CNum numSubstreams = numUnpackStreamsInFolders[i];
+        const CFolder &folder = folders[i];
+        if (numSubstreams == 1 && folder.UnpackCRCDefined)
+        {
+          digestsDefined.Add(true);
+          digests.Add(folder.UnpackCRC);
+        }
+        else
+          for (CNum j = 0; j < numSubstreams; j++, digestIndex++)
+          {
+            digestsDefined.Add(digestsDefined2[digestIndex]);
+            digests.Add(digests2[digestIndex]);
+          }
+      }
+    }
+    else if (type == NID::kEnd)
+    {
+      if (digestsDefined.IsEmpty())
+      {
+        BoolVector_Fill_False(digestsDefined, numDigestsTotal);
+        digests.Clear();
+        for (int i = 0; i < numDigestsTotal; i++)
+          digests.Add(0);
+      }
+      return;
+    }
+    else
+      SkeepData();
+    type = ReadID();
+  }
+}
+
+void CInArchive::ReadStreamsInfo(
+    const CObjectVector<CByteBuffer> *dataVector,
+    UInt64 &dataOffset,
+    CRecordVector<UInt64> &packSizes,
+    CBoolVector &packCRCsDefined,
+    CRecordVector<UInt32> &packCRCs,
+    CObjectVector<CFolder> &folders,
+    CRecordVector<CNum> &numUnpackStreamsInFolders,
+    CRecordVector<UInt64> &unpackSizes,
+    CBoolVector &digestsDefined,
+    CRecordVector<UInt32> &digests)
+{
+  for (;;)
+  {
+    UInt64 type = ReadID();
+    if (type > ((UInt32)1 << 30))
+      ThrowIncorrect();
+    switch((UInt32)type)
+    {
+      case NID::kEnd:
+        return;
+      case NID::kPackInfo:
+      {
+        ReadPackInfo(dataOffset, packSizes, packCRCsDefined, packCRCs);
+        break;
+      }
+      case NID::kUnpackInfo:
+      {
+        ReadUnpackInfo(dataVector, folders);
+        break;
+      }
+      case NID::kSubStreamsInfo:
+      {
+        ReadSubStreamsInfo(folders, numUnpackStreamsInFolders,
+            unpackSizes, digestsDefined, digests);
+        break;
+      }
+      default:
+        ThrowIncorrect();
+    }
+  }
+}
+
+void CInArchive::ReadBoolVector(int numItems, CBoolVector &v)
+{
+  v.Clear();
+  v.Reserve(numItems);
+  Byte b = 0;
+  Byte mask = 0;
+  for (int i = 0; i < numItems; i++)
+  {
+    if (mask == 0)
+    {
+      b = ReadByte();
+      mask = 0x80;
+    }
+    v.Add((b & mask) != 0);
+    mask >>= 1;
+  }
+}
+
+void CInArchive::ReadBoolVector2(int numItems, CBoolVector &v)
+{
+  Byte allAreDefined = ReadByte();
+  if (allAreDefined == 0)
+  {
+    ReadBoolVector(numItems, v);
+    return;
+  }
+  v.Clear();
+  v.Reserve(numItems);
+  for (int i = 0; i < numItems; i++)
+    v.Add(true);
+}
+
+void CInArchive::ReadUInt64DefVector(const CObjectVector<CByteBuffer> &dataVector,
+    CUInt64DefVector &v, int numFiles)
+{
+  ReadBoolVector2(numFiles, v.Defined);
+
+  CStreamSwitch streamSwitch;
+  streamSwitch.Set(this, &dataVector);
+  v.Values.Reserve(numFiles);
+
+  for (int i = 0; i < numFiles; i++)
+  {
+    UInt64 t = 0;
+    if (v.Defined[i])
+      t = ReadUInt64();
+    v.Values.Add(t);
+  }
+}
+
+HRESULT CInArchive::ReadAndDecodePackedStreams(
+    DECL_EXTERNAL_CODECS_LOC_VARS
+    UInt64 baseOffset,
+    UInt64 &dataOffset, CObjectVector<CByteBuffer> &dataVector
+    #ifndef _NO_CRYPTO
+    , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
+    #endif
+    )
+{
+  CRecordVector<UInt64> packSizes;
+  CBoolVector packCRCsDefined;
+  CRecordVector<UInt32> packCRCs;
+  CObjectVector<CFolder> folders;
+  
+  CRecordVector<CNum> numUnpackStreamsInFolders;
+  CRecordVector<UInt64> unpackSizes;
+  CBoolVector digestsDefined;
+  CRecordVector<UInt32> digests;
+  
+  ReadStreamsInfo(NULL,
+    dataOffset,
+    packSizes,
+    packCRCsDefined,
+    packCRCs,
+    folders,
+    numUnpackStreamsInFolders,
+    unpackSizes,
+    digestsDefined,
+    digests);
+  
+  // db.ArchiveInfo.DataStartPosition2 += db.ArchiveInfo.StartPositionAfterHeader;
+  
+  CNum packIndex = 0;
+  CDecoder decoder(
+    #ifdef _ST_MODE
+    false
+    #else
+    true
+    #endif
+    );
+  UInt64 dataStartPos = baseOffset + dataOffset;
+  for (int i = 0; i < folders.Size(); i++)
+  {
+    const CFolder &folder = folders[i];
+    dataVector.Add(CByteBuffer());
+    CByteBuffer &data = dataVector.Back();
+    UInt64 unpackSize64 = folder.GetUnpackSize();
+    size_t unpackSize = (size_t)unpackSize64;
+    if (unpackSize != unpackSize64)
+      ThrowUnsupported();
+    data.SetCapacity(unpackSize);
+    
+    CSequentialOutStreamImp2 *outStreamSpec = new CSequentialOutStreamImp2;
+    CMyComPtr<ISequentialOutStream> outStream = outStreamSpec;
+    outStreamSpec->Init(data, unpackSize);
+    
+    HRESULT result = decoder.Decode(
+      EXTERNAL_CODECS_LOC_VARS
+      _stream, dataStartPos,
+      &packSizes[packIndex], folder, outStream, NULL
+      #ifndef _NO_CRYPTO
+      , getTextPassword, passwordIsDefined
+      #endif
+      #ifdef COMPRESS_MT
+      , false, 1
+      #endif
+      );
+    RINOK(result);
+    
+    if (folder.UnpackCRCDefined)
+      if (CrcCalc(data, unpackSize) != folder.UnpackCRC)
+        ThrowIncorrect();
+    for (int j = 0; j < folder.PackStreams.Size(); j++)
+    {
+      UInt64 packSize = packSizes[packIndex++];
+      dataStartPos += packSize;
+      HeadersSize += packSize;
+    }
+  }
+  return S_OK;
+}
+
+HRESULT CInArchive::ReadHeader(
+    DECL_EXTERNAL_CODECS_LOC_VARS
+    CArchiveDatabaseEx &db
+    #ifndef _NO_CRYPTO
+    , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
+    #endif
+    )
+{
+  UInt64 type = ReadID();
+
+  if (type == NID::kArchiveProperties)
+  {
+    ReadArchiveProperties(db.ArchiveInfo);
+    type = ReadID();
+  }
+ 
+  CObjectVector<CByteBuffer> dataVector;
+  
+  if (type == NID::kAdditionalStreamsInfo)
+  {
+    HRESULT result = ReadAndDecodePackedStreams(
+        EXTERNAL_CODECS_LOC_VARS
+        db.ArchiveInfo.StartPositionAfterHeader,
+        db.ArchiveInfo.DataStartPosition2,
+        dataVector
+        #ifndef _NO_CRYPTO
+        , getTextPassword, passwordIsDefined
+        #endif
+        );
+    RINOK(result);
+    db.ArchiveInfo.DataStartPosition2 += db.ArchiveInfo.StartPositionAfterHeader;
+    type = ReadID();
+  }
+
+  CRecordVector<UInt64> unpackSizes;
+  CBoolVector digestsDefined;
+  CRecordVector<UInt32> digests;
+  
+  if (type == NID::kMainStreamsInfo)
+  {
+    ReadStreamsInfo(&dataVector,
+        db.ArchiveInfo.DataStartPosition,
+        db.PackSizes,
+        db.PackCRCsDefined,
+        db.PackCRCs,
+        db.Folders,
+        db.NumUnpackStreamsVector,
+        unpackSizes,
+        digestsDefined,
+        digests);
+    db.ArchiveInfo.DataStartPosition += db.ArchiveInfo.StartPositionAfterHeader;
+    type = ReadID();
+  }
+  else
+  {
+    for (int i = 0; i < db.Folders.Size(); i++)
+    {
+      db.NumUnpackStreamsVector.Add(1);
+      CFolder &folder = db.Folders[i];
+      unpackSizes.Add(folder.GetUnpackSize());
+      digestsDefined.Add(folder.UnpackCRCDefined);
+      digests.Add(folder.UnpackCRC);
+    }
+  }
+
+  db.Files.Clear();
+
+  if (type == NID::kEnd)
+    return S_OK;
+  if (type != NID::kFilesInfo)
+    ThrowIncorrect();
+  
+  CNum numFiles = ReadNum();
+  db.Files.Reserve(numFiles);
+  CNum i;
+  for (i = 0; i < numFiles; i++)
+    db.Files.Add(CFileItem());
+
+  db.ArchiveInfo.FileInfoPopIDs.Add(NID::kSize);
+  if (!db.PackSizes.IsEmpty())
+    db.ArchiveInfo.FileInfoPopIDs.Add(NID::kPackInfo);
+  if (numFiles > 0  && !digests.IsEmpty())
+    db.ArchiveInfo.FileInfoPopIDs.Add(NID::kCRC);
+
+  CBoolVector emptyStreamVector;
+  BoolVector_Fill_False(emptyStreamVector, (int)numFiles);
+  CBoolVector emptyFileVector;
+  CBoolVector antiFileVector;
+  CNum numEmptyStreams = 0;
+
+  for (;;)
+  {
+    UInt64 type = ReadID();
+    if (type == NID::kEnd)
+      break;
+    UInt64 size = ReadNumber();
+    size_t ppp = _inByteBack->_pos;
+    bool addPropIdToList = true;
+    bool isKnownType = true;
+    if (type > ((UInt32)1 << 30))
+      isKnownType = false;
+    else switch((UInt32)type)
+    {
+      case NID::kName:
+      {
+        CStreamSwitch streamSwitch;
+        streamSwitch.Set(this, &dataVector);
+        for (int i = 0; i < db.Files.Size(); i++)
+          _inByteBack->ReadString(db.Files[i].Name);
+        break;
+      }
+      case NID::kWinAttributes:
+      {
+        CBoolVector boolVector;
+        ReadBoolVector2(db.Files.Size(), boolVector);
+        CStreamSwitch streamSwitch;
+        streamSwitch.Set(this, &dataVector);
+        for (i = 0; i < numFiles; i++)
+        {
+          CFileItem &file = db.Files[i];
+          file.AttribDefined = boolVector[i];
+          if (file.AttribDefined)
+            file.Attrib = ReadUInt32();
+        }
+        break;
+      }
+      case NID::kEmptyStream:
+      {
+        ReadBoolVector(numFiles, emptyStreamVector);
+        for (i = 0; i < (CNum)emptyStreamVector.Size(); i++)
+          if (emptyStreamVector[i])
+            numEmptyStreams++;
+
+        BoolVector_Fill_False(emptyFileVector, numEmptyStreams);
+        BoolVector_Fill_False(antiFileVector, numEmptyStreams);
+
+        break;
+      }
+      case NID::kEmptyFile:  ReadBoolVector(numEmptyStreams, emptyFileVector); break;
+      case NID::kAnti:  ReadBoolVector(numEmptyStreams, antiFileVector); break;
+      case NID::kStartPos:  ReadUInt64DefVector(dataVector, db.StartPos, (int)numFiles); break;
+      case NID::kCTime:  ReadUInt64DefVector(dataVector, db.CTime, (int)numFiles); break;
+      case NID::kATime:  ReadUInt64DefVector(dataVector, db.ATime, (int)numFiles); break;
+      case NID::kMTime:  ReadUInt64DefVector(dataVector, db.MTime, (int)numFiles); break;
+      case NID::kDummy:
+      {
+        for (UInt64 j = 0; j < size; j++)
+          if (ReadByte() != 0)
+            ThrowIncorrect();
+        addPropIdToList = false;
+        break;
+      }
+      default:
+        addPropIdToList = isKnownType = false;
+    }
+    if (isKnownType)
+    {
+      if(addPropIdToList)
+        db.ArchiveInfo.FileInfoPopIDs.Add(type);
+    }
+    else
+      SkeepData(size);
+    bool checkRecordsSize = (db.ArchiveInfo.Version.Major > 0 ||
+        db.ArchiveInfo.Version.Minor > 2);
+    if (checkRecordsSize && _inByteBack->_pos - ppp != size)
+      ThrowIncorrect();
+  }
+
+  CNum emptyFileIndex = 0;
+  CNum sizeIndex = 0;
+
+  CNum numAntiItems = 0;
+  for (i = 0; i < numEmptyStreams; i++)
+    if (antiFileVector[i])
+      numAntiItems++;
+    
+  for (i = 0; i < numFiles; i++)
+  {
+    CFileItem &file = db.Files[i];
+    bool isAnti;
+    file.HasStream = !emptyStreamVector[i];
+    if (file.HasStream)
+    {
+      file.IsDir = false;
+      isAnti = false;
+      file.Size = unpackSizes[sizeIndex];
+      file.Crc = digests[sizeIndex];
+      file.CrcDefined = digestsDefined[sizeIndex];
+      sizeIndex++;
+    }
+    else
+    {
+      file.IsDir = !emptyFileVector[emptyFileIndex];
+      isAnti = antiFileVector[emptyFileIndex];
+      emptyFileIndex++;
+      file.Size = 0;
+      file.CrcDefined = false;
+    }
+    if (numAntiItems != 0)
+      db.IsAnti.Add(isAnti);
+  }
+  return S_OK;
+}
+
+
+void CArchiveDatabaseEx::FillFolderStartPackStream()
+{
+  FolderStartPackStreamIndex.Clear();
+  FolderStartPackStreamIndex.Reserve(Folders.Size());
+  CNum startPos = 0;
+  for (int i = 0; i < Folders.Size(); i++)
+  {
+    FolderStartPackStreamIndex.Add(startPos);
+    startPos += (CNum)Folders[i].PackStreams.Size();
+  }
+}
+
+void CArchiveDatabaseEx::FillStartPos()
+{
+  PackStreamStartPositions.Clear();
+  PackStreamStartPositions.Reserve(PackSizes.Size());
+  UInt64 startPos = 0;
+  for (int i = 0; i < PackSizes.Size(); i++)
+  {
+    PackStreamStartPositions.Add(startPos);
+    startPos += PackSizes[i];
+  }
+}
+
+void CArchiveDatabaseEx::FillFolderStartFileIndex()
+{
+  FolderStartFileIndex.Clear();
+  FolderStartFileIndex.Reserve(Folders.Size());
+  FileIndexToFolderIndexMap.Clear();
+  FileIndexToFolderIndexMap.Reserve(Files.Size());
+  
+  int folderIndex = 0;
+  CNum indexInFolder = 0;
+  for (int i = 0; i < Files.Size(); i++)
+  {
+    const CFileItem &file = Files[i];
+    bool emptyStream = !file.HasStream;
+    if (emptyStream && indexInFolder == 0)
+    {
+      FileIndexToFolderIndexMap.Add(kNumNoIndex);
+      continue;
+    }
+    if (indexInFolder == 0)
+    {
+      // v3.13 incorrectly worked with empty folders
+      // v4.07: Loop for skipping empty folders
+      for (;;)
+      {
+        if (folderIndex >= Folders.Size())
+          ThrowIncorrect();
+        FolderStartFileIndex.Add(i); // check it
+        if (NumUnpackStreamsVector[folderIndex] != 0)
+          break;
+        folderIndex++;
+      }
+    }
+    FileIndexToFolderIndexMap.Add(folderIndex);
+    if (emptyStream)
+      continue;
+    indexInFolder++;
+    if (indexInFolder >= NumUnpackStreamsVector[folderIndex])
+    {
+      folderIndex++;
+      indexInFolder = 0;
+    }
+  }
+}
+
+HRESULT CInArchive::ReadDatabase2(
+    DECL_EXTERNAL_CODECS_LOC_VARS
+    CArchiveDatabaseEx &db
+    #ifndef _NO_CRYPTO
+    , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
+    #endif
+    )
+{
+  db.Clear();
+  db.ArchiveInfo.StartPosition = _arhiveBeginStreamPosition;
+
+  db.ArchiveInfo.Version.Major = _header[6];
+  db.ArchiveInfo.Version.Minor = _header[7];
+
+  if (db.ArchiveInfo.Version.Major != kMajorVersion)
+    ThrowUnsupportedVersion();
+
+  UInt32 crcFromArchive = Get32(_header + 8);
+  UInt64 nextHeaderOffset = Get64(_header + 0xC);
+  UInt64 nextHeaderSize = Get64(_header + 0x14);
+  UInt32 nextHeaderCRC = Get32(_header + 0x1C);
+  UInt32 crc = CrcCalc(_header + 0xC, 20);
+
+  #ifdef FORMAT_7Z_RECOVERY
+  if (crcFromArchive == 0 && nextHeaderOffset == 0 && nextHeaderSize == 0 && nextHeaderCRC == 0)
+  {
+    UInt64 cur, cur2;
+    RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &cur));
+    const int kCheckSize = 500;
+    Byte buf[kCheckSize];
+    RINOK(_stream->Seek(0, STREAM_SEEK_END, &cur2));
+    int checkSize = kCheckSize;
+    if (cur2 - cur < kCheckSize)
+      checkSize = (int)(cur2 - cur);
+    RINOK(_stream->Seek(-checkSize, STREAM_SEEK_END, &cur2));
+    
+    RINOK(ReadStream_FALSE(_stream, buf, (size_t)checkSize));
+
+    int i;
+    for (i = (int)checkSize - 2; i >= 0; i--)
+      if (buf[i] == 0x17 && buf[i + 1] == 0x6 || buf[i] == 0x01 && buf[i + 1] == 0x04)
+        break;
+    if (i < 0)
+      return S_FALSE;
+    nextHeaderSize = checkSize - i;
+    nextHeaderOffset = cur2 - cur + i;
+    nextHeaderCRC = CrcCalc(buf + i, (size_t)nextHeaderSize);
+    RINOK(_stream->Seek(cur, STREAM_SEEK_SET, NULL));
+  }
+  #endif
+
+  #ifdef FORMAT_7Z_RECOVERY
+  crcFromArchive = crc;
+  #endif
+
+  db.ArchiveInfo.StartPositionAfterHeader = _arhiveBeginStreamPosition + kHeaderSize;
+
+  if (crc != crcFromArchive)
+    ThrowIncorrect();
+
+  if (nextHeaderSize == 0)
+    return S_OK;
+
+  if (nextHeaderSize > (UInt64)0xFFFFFFFF)
+    return S_FALSE;
+
+  RINOK(_stream->Seek(nextHeaderOffset, STREAM_SEEK_CUR, NULL));
+
+  CByteBuffer buffer2;
+  buffer2.SetCapacity((size_t)nextHeaderSize);
+
+  RINOK(ReadStream_FALSE(_stream, buffer2, (size_t)nextHeaderSize));
+  HeadersSize += kHeaderSize + nextHeaderSize;
+  db.PhySize = kHeaderSize + nextHeaderOffset + nextHeaderSize;
+
+  if (CrcCalc(buffer2, (UInt32)nextHeaderSize) != nextHeaderCRC)
+    ThrowIncorrect();
+  
+  CStreamSwitch streamSwitch;
+  streamSwitch.Set(this, buffer2);
+  
+  CObjectVector<CByteBuffer> dataVector;
+  
+  UInt64 type = ReadID();
+  if (type != NID::kHeader)
+  {
+    if (type != NID::kEncodedHeader)
+      ThrowIncorrect();
+    HRESULT result = ReadAndDecodePackedStreams(
+        EXTERNAL_CODECS_LOC_VARS
+        db.ArchiveInfo.StartPositionAfterHeader,
+        db.ArchiveInfo.DataStartPosition2,
+        dataVector
+        #ifndef _NO_CRYPTO
+        , getTextPassword, passwordIsDefined
+        #endif
+        );
+    RINOK(result);
+    if (dataVector.Size() == 0)
+      return S_OK;
+    if (dataVector.Size() > 1)
+      ThrowIncorrect();
+    streamSwitch.Remove();
+    streamSwitch.Set(this, dataVector.Front());
+    if (ReadID() != NID::kHeader)
+      ThrowIncorrect();
+  }
+
+  db.HeadersSize = HeadersSize;
+
+  return ReadHeader(
+    EXTERNAL_CODECS_LOC_VARS
+    db
+    #ifndef _NO_CRYPTO
+    , getTextPassword, passwordIsDefined
+    #endif
+    );
+}
+
+HRESULT CInArchive::ReadDatabase(
+    DECL_EXTERNAL_CODECS_LOC_VARS
+    CArchiveDatabaseEx &db
+    #ifndef _NO_CRYPTO
+    , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
+    #endif
+    )
+{
+  try
+  {
+    return ReadDatabase2(
+      EXTERNAL_CODECS_LOC_VARS db
+      #ifndef _NO_CRYPTO
+      , getTextPassword, passwordIsDefined
+      #endif
+      );
+  }
+  catch(CInArchiveException &) { return S_FALSE; }
+}
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zIn.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zIn.h
new file mode 100644
index 0000000..09c5110
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zIn.h
@@ -0,0 +1,245 @@
+// 7zIn.h
+
+#ifndef __7Z_IN_H
+#define __7Z_IN_H
+
+#include "../../../Common/MyCom.h"
+
+#include "../../IPassword.h"
+#include "../../IStream.h"
+
+#include "../../Common/CreateCoder.h"
+#include "../../Common/InBuffer.h"
+
+#include "7zItem.h"
+ 
+namespace NArchive {
+namespace N7z {
+  
+struct CInArchiveInfo
+{
+  CArchiveVersion Version;
+  UInt64 StartPosition;
+  UInt64 StartPositionAfterHeader;
+  UInt64 DataStartPosition;
+  UInt64 DataStartPosition2;
+  CRecordVector<UInt64> FileInfoPopIDs;
+  void Clear()
+  {
+    FileInfoPopIDs.Clear();
+  }
+};
+
+struct CArchiveDatabaseEx: public CArchiveDatabase
+{
+  CInArchiveInfo ArchiveInfo;
+  CRecordVector<UInt64> PackStreamStartPositions;
+  CRecordVector<CNum> FolderStartPackStreamIndex;
+  CRecordVector<CNum> FolderStartFileIndex;
+  CRecordVector<CNum> FileIndexToFolderIndexMap;
+
+  UInt64 HeadersSize;
+  UInt64 PhySize;
+
+  void Clear()
+  {
+    CArchiveDatabase::Clear();
+    ArchiveInfo.Clear();
+    PackStreamStartPositions.Clear();
+    FolderStartPackStreamIndex.Clear();
+    FolderStartFileIndex.Clear();
+    FileIndexToFolderIndexMap.Clear();
+
+    HeadersSize = 0;
+    PhySize = 0;
+  }
+
+  void FillFolderStartPackStream();
+  void FillStartPos();
+  void FillFolderStartFileIndex();
+
+  void Fill()
+  {
+    FillFolderStartPackStream();
+    FillStartPos();
+    FillFolderStartFileIndex();
+  }
+  
+  UInt64 GetFolderStreamPos(int folderIndex, int indexInFolder) const
+  {
+    return ArchiveInfo.DataStartPosition +
+        PackStreamStartPositions[FolderStartPackStreamIndex[folderIndex] + indexInFolder];
+  }
+  
+  UInt64 GetFolderFullPackSize(int folderIndex) const
+  {
+    CNum packStreamIndex = FolderStartPackStreamIndex[folderIndex];
+    const CFolder &folder = Folders[folderIndex];
+    UInt64 size = 0;
+    for (int i = 0; i < folder.PackStreams.Size(); i++)
+      size += PackSizes[packStreamIndex + i];
+    return size;
+  }
+  
+  UInt64 GetFolderPackStreamSize(int folderIndex, int streamIndex) const
+  {
+    return PackSizes[FolderStartPackStreamIndex[folderIndex] + streamIndex];
+  }
+
+  UInt64 GetFilePackSize(CNum fileIndex) const
+  {
+    CNum folderIndex = FileIndexToFolderIndexMap[fileIndex];
+    if (folderIndex != kNumNoIndex)
+      if (FolderStartFileIndex[folderIndex] == fileIndex)
+        return GetFolderFullPackSize(folderIndex);
+    return 0;
+  }
+};
+
+class CInByte2
+{
+  const Byte *_buffer;
+  size_t _size;
+public:
+  size_t _pos;
+  void Init(const Byte *buffer, size_t size)
+  {
+    _buffer = buffer;
+    _size = size;
+    _pos = 0;
+  }
+  Byte ReadByte();
+  void ReadBytes(Byte *data, size_t size);
+  void SkeepData(UInt64 size);
+  void SkeepData();
+  UInt64 ReadNumber();
+  CNum ReadNum();
+  UInt32 ReadUInt32();
+  UInt64 ReadUInt64();
+  void ReadString(UString &s);
+};
+
+class CStreamSwitch;
+
+const UInt32 kHeaderSize = 32;
+
+class CInArchive
+{
+  friend class CStreamSwitch;
+
+  CMyComPtr<IInStream> _stream;
+
+  CObjectVector<CInByte2> _inByteVector;
+  CInByte2 *_inByteBack;
+ 
+  UInt64 _arhiveBeginStreamPosition;
+
+  Byte _header[kHeaderSize];
+
+  UInt64 HeadersSize;
+
+  void AddByteStream(const Byte *buffer, size_t size)
+  {
+    _inByteVector.Add(CInByte2());
+    _inByteBack = &_inByteVector.Back();
+    _inByteBack->Init(buffer, size);
+  }
+  
+  void DeleteByteStream()
+  {
+    _inByteVector.DeleteBack();
+    if (!_inByteVector.IsEmpty())
+      _inByteBack = &_inByteVector.Back();
+  }
+
+private:
+  HRESULT FindAndReadSignature(IInStream *stream, const UInt64 *searchHeaderSizeLimit);
+  
+  void ReadBytes(Byte *data, size_t size) { _inByteBack->ReadBytes(data, size); }
+  Byte ReadByte() { return _inByteBack->ReadByte(); }
+  UInt64 ReadNumber() { return _inByteBack->ReadNumber(); }
+  CNum ReadNum() { return _inByteBack->ReadNum(); }
+  UInt64 ReadID() { return _inByteBack->ReadNumber(); }
+  UInt32 ReadUInt32() { return _inByteBack->ReadUInt32(); }
+  UInt64 ReadUInt64() { return _inByteBack->ReadUInt64(); }
+  void SkeepData(UInt64 size) { _inByteBack->SkeepData(size); }
+  void SkeepData() { _inByteBack->SkeepData(); }
+  void WaitAttribute(UInt64 attribute);
+
+  void ReadArchiveProperties(CInArchiveInfo &archiveInfo);
+  void GetNextFolderItem(CFolder &itemInfo);
+  void ReadHashDigests(int numItems,
+      CBoolVector &digestsDefined, CRecordVector<UInt32> &digests);
+  
+  void ReadPackInfo(
+      UInt64 &dataOffset,
+      CRecordVector<UInt64> &packSizes,
+      CBoolVector &packCRCsDefined,
+      CRecordVector<UInt32> &packCRCs);
+  
+  void ReadUnpackInfo(
+      const CObjectVector<CByteBuffer> *dataVector,
+      CObjectVector<CFolder> &folders);
+  
+  void ReadSubStreamsInfo(
+      const CObjectVector<CFolder> &folders,
+      CRecordVector<CNum> &numUnpackStreamsInFolders,
+      CRecordVector<UInt64> &unpackSizes,
+      CBoolVector &digestsDefined,
+      CRecordVector<UInt32> &digests);
+
+  void ReadStreamsInfo(
+      const CObjectVector<CByteBuffer> *dataVector,
+      UInt64 &dataOffset,
+      CRecordVector<UInt64> &packSizes,
+      CBoolVector &packCRCsDefined,
+      CRecordVector<UInt32> &packCRCs,
+      CObjectVector<CFolder> &folders,
+      CRecordVector<CNum> &numUnpackStreamsInFolders,
+      CRecordVector<UInt64> &unpackSizes,
+      CBoolVector &digestsDefined,
+      CRecordVector<UInt32> &digests);
+
+
+  void ReadBoolVector(int numItems, CBoolVector &v);
+  void ReadBoolVector2(int numItems, CBoolVector &v);
+  void ReadUInt64DefVector(const CObjectVector<CByteBuffer> &dataVector,
+      CUInt64DefVector &v, int numFiles);
+  HRESULT ReadAndDecodePackedStreams(
+      DECL_EXTERNAL_CODECS_LOC_VARS
+      UInt64 baseOffset, UInt64 &dataOffset,
+      CObjectVector<CByteBuffer> &dataVector
+      #ifndef _NO_CRYPTO
+      , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
+      #endif
+      );
+  HRESULT ReadHeader(
+      DECL_EXTERNAL_CODECS_LOC_VARS
+      CArchiveDatabaseEx &db
+      #ifndef _NO_CRYPTO
+      ,ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
+      #endif
+      );
+  HRESULT ReadDatabase2(
+      DECL_EXTERNAL_CODECS_LOC_VARS
+      CArchiveDatabaseEx &db
+      #ifndef _NO_CRYPTO
+      ,ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
+      #endif
+      );
+public:
+  HRESULT Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit); // S_FALSE means is not archive
+  void Close();
+
+  HRESULT ReadDatabase(
+      DECL_EXTERNAL_CODECS_LOC_VARS
+      CArchiveDatabaseEx &db
+      #ifndef _NO_CRYPTO
+      ,ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
+      #endif
+      );
+};
+  
+}}
+  
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zItem.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zItem.h
new file mode 100644
index 0000000..1c3068b
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zItem.h
@@ -0,0 +1,258 @@
+// 7zItem.h
+
+#ifndef __7Z_ITEM_H
+#define __7Z_ITEM_H
+
+#include "../../../Common/Buffer.h"
+#include "../../../Common/MyString.h"
+
+#include "../../Common/MethodId.h"
+
+#include "7zHeader.h"
+
+namespace NArchive {
+namespace N7z {
+
+typedef UInt32 CNum;
+const CNum kNumMax     = 0x7FFFFFFF;
+const CNum kNumNoIndex = 0xFFFFFFFF;
+
+struct CCoderInfo
+{
+  CMethodId MethodID;
+  CByteBuffer Props;
+  CNum NumInStreams;
+  CNum NumOutStreams;
+  bool IsSimpleCoder() const { return (NumInStreams == 1) && (NumOutStreams == 1); }
+};
+
+struct CBindPair
+{
+  CNum InIndex;
+  CNum OutIndex;
+};
+
+struct CFolder
+{
+  CObjectVector<CCoderInfo> Coders;
+  CRecordVector<CBindPair> BindPairs;
+  CRecordVector<CNum> PackStreams;
+  CRecordVector<UInt64> UnpackSizes;
+  UInt32 UnpackCRC;
+  bool UnpackCRCDefined;
+
+  CFolder(): UnpackCRCDefined(false) {}
+
+  UInt64 GetUnpackSize() const // test it
+  {
+    if (UnpackSizes.IsEmpty())
+      return 0;
+    for (int i = UnpackSizes.Size() - 1; i >= 0; i--)
+      if (FindBindPairForOutStream(i) < 0)
+        return UnpackSizes[i];
+    throw 1;
+  }
+
+  CNum GetNumOutStreams() const
+  {
+    CNum result = 0;
+    for (int i = 0; i < Coders.Size(); i++)
+      result += Coders[i].NumOutStreams;
+    return result;
+  }
+
+  int FindBindPairForInStream(CNum inStreamIndex) const
+  {
+    for(int i = 0; i < BindPairs.Size(); i++)
+      if (BindPairs[i].InIndex == inStreamIndex)
+        return i;
+    return -1;
+  }
+  int FindBindPairForOutStream(CNum outStreamIndex) const
+  {
+    for(int i = 0; i < BindPairs.Size(); i++)
+      if (BindPairs[i].OutIndex == outStreamIndex)
+        return i;
+    return -1;
+  }
+  int FindPackStreamArrayIndex(CNum inStreamIndex) const
+  {
+    for(int i = 0; i < PackStreams.Size(); i++)
+      if (PackStreams[i] == inStreamIndex)
+        return i;
+    return -1;
+  }
+
+  bool CheckStructure() const;
+};
+
+struct CUInt64DefVector
+{
+  CRecordVector<UInt64> Values;
+  CRecordVector<bool> Defined;
+  
+  void Clear()
+  {
+    Values.Clear();
+    Defined.Clear();
+  }
+  
+  void ReserveDown()
+  {
+    Values.ReserveDown();
+    Values.ReserveDown();
+  }
+
+  bool GetItem(int index, UInt64 &value) const
+  {
+    if (index < Defined.Size() && Defined[index])
+    {
+      value = Values[index];
+      return true;
+    }
+    value = 0;
+    return false;
+  }
+  
+  void SetItem(int index, bool defined, UInt64 value)
+  {
+    while (index >= Defined.Size())
+      Defined.Add(false);
+    Defined[index] = defined;
+    if (!defined)
+      return;
+    while (index >= Values.Size())
+      Values.Add(0);
+    Values[index] = value;
+  }
+
+  bool CheckSize(int size) const { return Defined.Size() == size || Defined.Size() == 0; }
+};
+
+struct CFileItem
+{
+  UInt64 Size;
+  UInt32 Attrib;
+  UInt32 Crc;
+  UString Name;
+
+  bool HasStream; // Test it !!! it means that there is
+                  // stream in some folder. It can be empty stream
+  bool IsDir;
+  bool CrcDefined;
+  bool AttribDefined;
+
+  CFileItem():
+    HasStream(true),
+    IsDir(false),
+    CrcDefined(false),
+    AttribDefined(false)
+      {}
+  void SetAttrib(UInt32 attrib)
+  {
+    AttribDefined = true;
+    Attrib = attrib;
+  }
+};
+
+struct CFileItem2
+{
+  UInt64 CTime;
+  UInt64 ATime;
+  UInt64 MTime;
+  UInt64 StartPos;
+  bool CTimeDefined;
+  bool ATimeDefined;
+  bool MTimeDefined;
+  bool StartPosDefined;
+  bool IsAnti;
+};
+
+struct CArchiveDatabase
+{
+  CRecordVector<UInt64> PackSizes;
+  CRecordVector<bool> PackCRCsDefined;
+  CRecordVector<UInt32> PackCRCs;
+  CObjectVector<CFolder> Folders;
+  CRecordVector<CNum> NumUnpackStreamsVector;
+  CObjectVector<CFileItem> Files;
+
+  CUInt64DefVector CTime;
+  CUInt64DefVector ATime;
+  CUInt64DefVector MTime;
+  CUInt64DefVector StartPos;
+  CRecordVector<bool> IsAnti;
+
+  void Clear()
+  {
+    PackSizes.Clear();
+    PackCRCsDefined.Clear();
+    PackCRCs.Clear();
+    Folders.Clear();
+    NumUnpackStreamsVector.Clear();
+    Files.Clear();
+    CTime.Clear();
+    ATime.Clear();
+    MTime.Clear();
+    StartPos.Clear();
+    IsAnti.Clear();
+  }
+
+  void ReserveDown()
+  {
+    PackSizes.ReserveDown();
+    PackCRCsDefined.ReserveDown();
+    PackCRCs.ReserveDown();
+    Folders.ReserveDown();
+    NumUnpackStreamsVector.ReserveDown();
+    Files.ReserveDown();
+    CTime.ReserveDown();
+    ATime.ReserveDown();
+    MTime.ReserveDown();
+    StartPos.ReserveDown();
+    IsAnti.ReserveDown();
+  }
+
+  bool IsEmpty() const
+  {
+    return (PackSizes.IsEmpty() &&
+      PackCRCsDefined.IsEmpty() &&
+      PackCRCs.IsEmpty() &&
+      Folders.IsEmpty() &&
+      NumUnpackStreamsVector.IsEmpty() &&
+      Files.IsEmpty());
+  }
+
+  bool CheckNumFiles() const
+  {
+    int size = Files.Size();
+    return (
+      CTime.CheckSize(size) &&
+      ATime.CheckSize(size) &&
+      MTime.CheckSize(size) &&
+      StartPos.CheckSize(size) &&
+      (size == IsAnti.Size() || IsAnti.Size() == 0));
+  }
+
+  bool IsSolid() const
+  {
+    for (int i = 0; i < NumUnpackStreamsVector.Size(); i++)
+      if (NumUnpackStreamsVector[i] > 1)
+        return true;
+    return false;
+  }
+  bool IsItemAnti(int index) const { return (index < IsAnti.Size() && IsAnti[index]); }
+  void SetItemAnti(int index, bool isAnti)
+  {
+    while (index >= IsAnti.Size())
+      IsAnti.Add(false);
+    IsAnti[index] = isAnti;
+  }
+
+  void GetFile(int index, CFileItem &file, CFileItem2 &file2) const;
+  void AddFile(const CFileItem &file, const CFileItem2 &file2);
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zOut.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zOut.cpp
new file mode 100644
index 0000000..7ac3c18
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zOut.cpp
@@ -0,0 +1,876 @@
+// 7zOut.cpp
+
+#include "StdAfx.h"
+
+#include "../../../Common/AutoPtr.h"
+#include "../../Common/StreamObjects.h"
+
+#include "7zOut.h"
+
+extern "C"
+{
+#include "../../../../C/7zCrc.h"
+}
+
+static HRESULT WriteBytes(ISequentialOutStream *stream, const void *data, size_t size)
+{
+  while (size > 0)
+  {
+    UInt32 curSize = (UInt32)MyMin(size, (size_t)0xFFFFFFFF);
+    UInt32 processedSize;
+    RINOK(stream->Write(data, curSize, &processedSize));
+    if (processedSize == 0)
+      return E_FAIL;
+    data = (const void *)((const Byte *)data + processedSize);
+    size -= processedSize;
+  }
+  return S_OK;
+}
+
+namespace NArchive {
+namespace N7z {
+
+HRESULT COutArchive::WriteDirect(const void *data, UInt32 size)
+{
+  return ::WriteBytes(SeqStream, data, size);
+}
+
+HRESULT COutArchive::WriteSignature()
+{
+  Byte buf[8];
+  memcpy(buf, kSignature, kSignatureSize);
+  buf[kSignatureSize] = kMajorVersion;
+  buf[kSignatureSize + 1] = 3;
+  return WriteDirect(buf, 8);
+}
+
+#ifdef _7Z_VOL
+HRESULT COutArchive::WriteFinishSignature()
+{
+  RINOK(WriteDirect(kFinishSignature, kSignatureSize));
+  CArchiveVersion av;
+  av.Major = kMajorVersion;
+  av.Minor = 2;
+  RINOK(WriteDirectByte(av.Major));
+  return WriteDirectByte(av.Minor);
+}
+#endif
+
+static void SetUInt32(Byte *p, UInt32 d)
+{
+  for (int i = 0; i < 4; i++, d >>= 8)
+    p[i] = (Byte)d;
+}
+
+static void SetUInt64(Byte *p, UInt64 d)
+{
+  for (int i = 0; i < 8; i++, d >>= 8)
+    p[i] = (Byte)d;
+}
+
+HRESULT COutArchive::WriteStartHeader(const CStartHeader &h)
+{
+  Byte buf[24];
+  SetUInt64(buf + 4, h.NextHeaderOffset);
+  SetUInt64(buf + 12, h.NextHeaderSize);
+  SetUInt32(buf + 20, h.NextHeaderCRC);
+  SetUInt32(buf, CrcCalc(buf + 4, 20));
+  return WriteDirect(buf, 24);
+}
+
+#ifdef _7Z_VOL
+HRESULT COutArchive::WriteFinishHeader(const CFinishHeader &h)
+{
+  CCRC crc;
+  crc.UpdateUInt64(h.NextHeaderOffset);
+  crc.UpdateUInt64(h.NextHeaderSize);
+  crc.UpdateUInt32(h.NextHeaderCRC);
+  crc.UpdateUInt64(h.ArchiveStartOffset);
+  crc.UpdateUInt64(h.AdditionalStartBlockSize);
+  RINOK(WriteDirectUInt32(crc.GetDigest()));
+  RINOK(WriteDirectUInt64(h.NextHeaderOffset));
+  RINOK(WriteDirectUInt64(h.NextHeaderSize));
+  RINOK(WriteDirectUInt32(h.NextHeaderCRC));
+  RINOK(WriteDirectUInt64(h.ArchiveStartOffset));
+  return WriteDirectUInt64(h.AdditionalStartBlockSize);
+}
+#endif
+
+HRESULT COutArchive::Create(ISequentialOutStream *stream, bool endMarker)
+{
+  Close();
+  #ifdef _7Z_VOL
+  // endMarker = false;
+  _endMarker = endMarker;
+  #endif
+  SeqStream = stream;
+  if (!endMarker)
+  {
+    SeqStream.QueryInterface(IID_IOutStream, &Stream);
+    if (!Stream)
+    {
+      return E_NOTIMPL;
+      // endMarker = true;
+    }
+  }
+  #ifdef _7Z_VOL
+  if (endMarker)
+  {
+    /*
+    CStartHeader sh;
+    sh.NextHeaderOffset = (UInt32)(Int32)-1;
+    sh.NextHeaderSize = (UInt32)(Int32)-1;
+    sh.NextHeaderCRC = 0;
+    WriteStartHeader(sh);
+    */
+  }
+  else
+  #endif
+  {
+    if (!Stream)
+      return E_FAIL;
+    RINOK(WriteSignature());
+    RINOK(Stream->Seek(0, STREAM_SEEK_CUR, &_prefixHeaderPos));
+  }
+  return S_OK;
+}
+
+void COutArchive::Close()
+{
+  SeqStream.Release();
+  Stream.Release();
+}
+
+HRESULT COutArchive::SkeepPrefixArchiveHeader()
+{
+  #ifdef _7Z_VOL
+  if (_endMarker)
+    return S_OK;
+  #endif
+  return Stream->Seek(24, STREAM_SEEK_CUR, NULL);
+}
+
+UInt64 COutArchive::GetPos() const
+{
+  if (_countMode)
+    return _countSize;
+  if (_writeToStream)
+    return _outByte.GetProcessedSize();
+  return _outByte2.GetPos();
+}
+
+void COutArchive::WriteBytes(const void *data, size_t size)
+{
+  if (_countMode)
+    _countSize += size;
+  else if (_writeToStream)
+  {
+    _outByte.WriteBytes(data, size);
+    _crc = CrcUpdate(_crc, data, size);
+  }
+  else
+    _outByte2.WriteBytes(data, size);
+}
+
+void COutArchive::WriteByte(Byte b)
+{
+  if (_countMode)
+    _countSize++;
+  else if (_writeToStream)
+  {
+    _outByte.WriteByte(b);
+    _crc = CRC_UPDATE_BYTE(_crc, b);
+  }
+  else
+    _outByte2.WriteByte(b);
+}
+
+void COutArchive::WriteUInt32(UInt32 value)
+{
+  for (int i = 0; i < 4; i++)
+  {
+    WriteByte((Byte)value);
+    value >>= 8;
+  }
+}
+
+void COutArchive::WriteUInt64(UInt64 value)
+{
+  for (int i = 0; i < 8; i++)
+  {
+    WriteByte((Byte)value);
+    value >>= 8;
+  }
+}
+
+void COutArchive::WriteNumber(UInt64 value)
+{
+  Byte firstByte = 0;
+  Byte mask = 0x80;
+  int i;
+  for (i = 0; i < 8; i++)
+  {
+    if (value < ((UInt64(1) << ( 7  * (i + 1)))))
+    {
+      firstByte |= Byte(value >> (8 * i));
+      break;
+    }
+    firstByte |= mask;
+    mask >>= 1;
+  }
+  WriteByte(firstByte);
+  for (;i > 0; i--)
+  {
+    WriteByte((Byte)value);
+    value >>= 8;
+  }
+}
+
+static UInt32 GetBigNumberSize(UInt64 value)
+{
+  int i;
+  for (i = 1; i < 9; i++)
+    if (value < (((UInt64)1 << (i * 7))))
+      break;
+  return i;
+}
+
+#ifdef _7Z_VOL
+UInt32 COutArchive::GetVolHeadersSize(UInt64 dataSize, int nameLength, bool props)
+{
+  UInt32 result = GetBigNumberSize(dataSize) * 2 + 41;
+  if (nameLength != 0)
+  {
+    nameLength = (nameLength + 1) * 2;
+    result += nameLength + GetBigNumberSize(nameLength) + 2;
+  }
+  if (props)
+  {
+    result += 20;
+  }
+  if (result >= 128)
+    result++;
+  result += kSignatureSize + 2 + kFinishHeaderSize;
+  return result;
+}
+
+UInt64 COutArchive::GetVolPureSize(UInt64 volSize, int nameLength, bool props)
+{
+  UInt32 headersSizeBase = COutArchive::GetVolHeadersSize(1, nameLength, props);
+  int testSize;
+  if (volSize > headersSizeBase)
+    testSize = volSize - headersSizeBase;
+  else
+    testSize = 1;
+  UInt32 headersSize = COutArchive::GetVolHeadersSize(testSize, nameLength, props);
+  UInt64 pureSize = 1;
+  if (volSize > headersSize)
+    pureSize = volSize - headersSize;
+  return pureSize;
+}
+#endif
+
+void COutArchive::WriteFolder(const CFolder &folder)
+{
+  WriteNumber(folder.Coders.Size());
+  int i;
+  for (i = 0; i < folder.Coders.Size(); i++)
+  {
+    const CCoderInfo &coder = folder.Coders[i];
+    {
+      size_t propsSize = coder.Props.GetCapacity();
+      
+      UInt64 id = coder.MethodID;
+      int idSize;
+      for (idSize = 1; idSize < sizeof(id); idSize++)
+        if ((id >> (8 * idSize)) == 0)
+          break;
+      BYTE longID[15];
+      for (int t = idSize - 1; t >= 0 ; t--, id >>= 8)
+        longID[t] = (Byte)(id & 0xFF);
+      Byte b;
+      b = (Byte)(idSize & 0xF);
+      bool isComplex = !coder.IsSimpleCoder();
+      b |= (isComplex ? 0x10 : 0);
+      b |= ((propsSize != 0) ? 0x20 : 0 );
+      WriteByte(b);
+      WriteBytes(longID, idSize);
+      if (isComplex)
+      {
+        WriteNumber(coder.NumInStreams);
+        WriteNumber(coder.NumOutStreams);
+      }
+      if (propsSize == 0)
+        continue;
+      WriteNumber(propsSize);
+      WriteBytes(coder.Props, propsSize);
+    }
+  }
+  for (i = 0; i < folder.BindPairs.Size(); i++)
+  {
+    const CBindPair &bindPair = folder.BindPairs[i];
+    WriteNumber(bindPair.InIndex);
+    WriteNumber(bindPair.OutIndex);
+  }
+  if (folder.PackStreams.Size() > 1)
+    for (i = 0; i < folder.PackStreams.Size(); i++)
+    {
+      WriteNumber(folder.PackStreams[i]);
+    }
+}
+
+void COutArchive::WriteBoolVector(const CBoolVector &boolVector)
+{
+  Byte b = 0;
+  Byte mask = 0x80;
+  for (int i = 0; i < boolVector.Size(); i++)
+  {
+    if (boolVector[i])
+      b |= mask;
+    mask >>= 1;
+    if (mask == 0)
+    {
+      WriteByte(b);
+      mask = 0x80;
+      b = 0;
+    }
+  }
+  if (mask != 0x80)
+    WriteByte(b);
+}
+
+
+void COutArchive::WriteHashDigests(
+    const CRecordVector<bool> &digestsDefined,
+    const CRecordVector<UInt32> &digests)
+{
+  int numDefined = 0;
+  int i;
+  for (i = 0; i < digestsDefined.Size(); i++)
+    if (digestsDefined[i])
+      numDefined++;
+  if (numDefined == 0)
+    return;
+
+  WriteByte(NID::kCRC);
+  if (numDefined == digestsDefined.Size())
+    WriteByte(1);
+  else
+  {
+    WriteByte(0);
+    WriteBoolVector(digestsDefined);
+  }
+  for (i = 0; i < digests.Size(); i++)
+    if (digestsDefined[i])
+      WriteUInt32(digests[i]);
+}
+
+void COutArchive::WritePackInfo(
+    UInt64 dataOffset,
+    const CRecordVector<UInt64> &packSizes,
+    const CRecordVector<bool> &packCRCsDefined,
+    const CRecordVector<UInt32> &packCRCs)
+{
+  if (packSizes.IsEmpty())
+    return;
+  WriteByte(NID::kPackInfo);
+  WriteNumber(dataOffset);
+  WriteNumber(packSizes.Size());
+  WriteByte(NID::kSize);
+  for (int i = 0; i < packSizes.Size(); i++)
+    WriteNumber(packSizes[i]);
+
+  WriteHashDigests(packCRCsDefined, packCRCs);
+  
+  WriteByte(NID::kEnd);
+}
+
+void COutArchive::WriteUnpackInfo(const CObjectVector<CFolder> &folders)
+{
+  if (folders.IsEmpty())
+    return;
+
+  WriteByte(NID::kUnpackInfo);
+
+  WriteByte(NID::kFolder);
+  WriteNumber(folders.Size());
+  {
+    WriteByte(0);
+    for (int i = 0; i < folders.Size(); i++)
+      WriteFolder(folders[i]);
+  }
+  
+  WriteByte(NID::kCodersUnpackSize);
+  int i;
+  for (i = 0; i < folders.Size(); i++)
+  {
+    const CFolder &folder = folders[i];
+    for (int j = 0; j < folder.UnpackSizes.Size(); j++)
+      WriteNumber(folder.UnpackSizes[j]);
+  }
+
+  CRecordVector<bool> unpackCRCsDefined;
+  CRecordVector<UInt32> unpackCRCs;
+  for (i = 0; i < folders.Size(); i++)
+  {
+    const CFolder &folder = folders[i];
+    unpackCRCsDefined.Add(folder.UnpackCRCDefined);
+    unpackCRCs.Add(folder.UnpackCRC);
+  }
+  WriteHashDigests(unpackCRCsDefined, unpackCRCs);
+
+  WriteByte(NID::kEnd);
+}
+
+void COutArchive::WriteSubStreamsInfo(
+    const CObjectVector<CFolder> &folders,
+    const CRecordVector<CNum> &numUnpackStreamsInFolders,
+    const CRecordVector<UInt64> &unpackSizes,
+    const CRecordVector<bool> &digestsDefined,
+    const CRecordVector<UInt32> &digests)
+{
+  WriteByte(NID::kSubStreamsInfo);
+
+  int i;
+  for (i = 0; i < numUnpackStreamsInFolders.Size(); i++)
+  {
+    if (numUnpackStreamsInFolders[i] != 1)
+    {
+      WriteByte(NID::kNumUnpackStream);
+      for (i = 0; i < numUnpackStreamsInFolders.Size(); i++)
+        WriteNumber(numUnpackStreamsInFolders[i]);
+      break;
+    }
+  }
+ 
+
+  bool needFlag = true;
+  CNum index = 0;
+  for (i = 0; i < numUnpackStreamsInFolders.Size(); i++)
+    for (CNum j = 0; j < numUnpackStreamsInFolders[i]; j++)
+    {
+      if (j + 1 != numUnpackStreamsInFolders[i])
+      {
+        if (needFlag)
+          WriteByte(NID::kSize);
+        needFlag = false;
+        WriteNumber(unpackSizes[index]);
+      }
+      index++;
+    }
+
+  CRecordVector<bool> digestsDefined2;
+  CRecordVector<UInt32> digests2;
+
+  int digestIndex = 0;
+  for (i = 0; i < folders.Size(); i++)
+  {
+    int numSubStreams = (int)numUnpackStreamsInFolders[i];
+    if (numSubStreams == 1 && folders[i].UnpackCRCDefined)
+      digestIndex++;
+    else
+      for (int j = 0; j < numSubStreams; j++, digestIndex++)
+      {
+        digestsDefined2.Add(digestsDefined[digestIndex]);
+        digests2.Add(digests[digestIndex]);
+      }
+  }
+  WriteHashDigests(digestsDefined2, digests2);
+  WriteByte(NID::kEnd);
+}
+
+void COutArchive::SkipAlign(unsigned /* pos */, unsigned /* alignSize */)
+{
+  return;
+}
+
+/*
+7-Zip 4.50 - 4.58 contain BUG, so they do not support .7z archives with Unknown field.
+
+void COutArchive::SkipAlign(unsigned pos, unsigned alignSize)
+{
+  pos += (unsigned)GetPos();
+  pos &= (alignSize - 1);
+  if (pos == 0)
+    return;
+  unsigned skip = alignSize - pos;
+  if (skip < 2)
+    skip += alignSize;
+  skip -= 2;
+  WriteByte(NID::kDummy);
+  WriteByte((Byte)skip);
+  for (unsigned i = 0; i < skip; i++)
+    WriteByte(0);
+}
+*/
+
+void COutArchive::WriteAlignedBoolHeader(const CBoolVector &v, int numDefined, Byte type, unsigned itemSize)
+{
+  const UInt64 bvSize = (numDefined == v.Size()) ? 0 : (v.Size() + 7) / 8;
+  const UInt64 dataSize = (UInt64)numDefined * itemSize + bvSize + 2;
+  SkipAlign(3 + (unsigned)bvSize + (unsigned)GetBigNumberSize(dataSize), itemSize);
+
+  WriteByte(type);
+  WriteNumber(dataSize);
+  if (numDefined == v.Size())
+    WriteByte(1);
+  else
+  {
+    WriteByte(0);
+    WriteBoolVector(v);
+  }
+  WriteByte(0);
+}
+
+void COutArchive::WriteUInt64DefVector(const CUInt64DefVector &v, Byte type)
+{
+  int numDefined = 0;
+
+  int i;
+  for (i = 0; i < v.Defined.Size(); i++)
+    if (v.Defined[i])
+      numDefined++;
+
+  if (numDefined == 0)
+    return;
+
+  WriteAlignedBoolHeader(v.Defined, numDefined, type, 8);
+  
+  for (i = 0; i < v.Defined.Size(); i++)
+    if (v.Defined[i])
+      WriteUInt64(v.Values[i]);
+}
+
+HRESULT COutArchive::EncodeStream(
+    DECL_EXTERNAL_CODECS_LOC_VARS
+    CEncoder &encoder, const Byte *data, size_t dataSize,
+    CRecordVector<UInt64> &packSizes, CObjectVector<CFolder> &folders)
+{
+  CSequentialInStreamImp *streamSpec = new CSequentialInStreamImp;
+  CMyComPtr<ISequentialInStream> stream = streamSpec;
+  streamSpec->Init(data, dataSize);
+  CFolder folderItem;
+  folderItem.UnpackCRCDefined = true;
+  folderItem.UnpackCRC = CrcCalc(data, dataSize);
+  UInt64 dataSize64 = dataSize;
+  RINOK(encoder.Encode(
+      EXTERNAL_CODECS_LOC_VARS
+      stream, NULL, &dataSize64, folderItem, SeqStream, packSizes, NULL))
+  folders.Add(folderItem);
+  return S_OK;
+}
+
+HRESULT COutArchive::EncodeStream(
+    DECL_EXTERNAL_CODECS_LOC_VARS
+    CEncoder &encoder, const CByteBuffer &data,
+    CRecordVector<UInt64> &packSizes, CObjectVector<CFolder> &folders)
+{
+  return EncodeStream(
+      EXTERNAL_CODECS_LOC_VARS
+      encoder, data, data.GetCapacity(), packSizes, folders);
+}
+
+void COutArchive::WriteHeader(
+    const CArchiveDatabase &db,
+    const CHeaderOptions &headerOptions,
+    UInt64 &headerOffset)
+{
+  int i;
+  
+  UInt64 packedSize = 0;
+  for (i = 0; i < db.PackSizes.Size(); i++)
+    packedSize += db.PackSizes[i];
+
+  headerOffset = packedSize;
+
+  WriteByte(NID::kHeader);
+
+  // Archive Properties
+
+  if (db.Folders.Size() > 0)
+  {
+    WriteByte(NID::kMainStreamsInfo);
+    WritePackInfo(0, db.PackSizes,
+        db.PackCRCsDefined,
+        db.PackCRCs);
+
+    WriteUnpackInfo(db.Folders);
+
+    CRecordVector<UInt64> unpackSizes;
+    CRecordVector<bool> digestsDefined;
+    CRecordVector<UInt32> digests;
+    for (i = 0; i < db.Files.Size(); i++)
+    {
+      const CFileItem &file = db.Files[i];
+      if (!file.HasStream)
+        continue;
+      unpackSizes.Add(file.Size);
+      digestsDefined.Add(file.CrcDefined);
+      digests.Add(file.Crc);
+    }
+
+    WriteSubStreamsInfo(
+        db.Folders,
+        db.NumUnpackStreamsVector,
+        unpackSizes,
+        digestsDefined,
+        digests);
+    WriteByte(NID::kEnd);
+  }
+
+  if (db.Files.IsEmpty())
+  {
+    WriteByte(NID::kEnd);
+    return;
+  }
+
+  WriteByte(NID::kFilesInfo);
+  WriteNumber(db.Files.Size());
+
+  {
+  /* ---------- Empty Streams ---------- */
+  CBoolVector emptyStreamVector;
+  emptyStreamVector.Reserve(db.Files.Size());
+  int numEmptyStreams = 0;
+  for (i = 0; i < db.Files.Size(); i++)
+    if (db.Files[i].HasStream)
+      emptyStreamVector.Add(false);
+    else
+    {
+      emptyStreamVector.Add(true);
+      numEmptyStreams++;
+    }
+  if (numEmptyStreams > 0)
+  {
+    WriteByte(NID::kEmptyStream);
+    WriteNumber((emptyStreamVector.Size() + 7) / 8);
+    WriteBoolVector(emptyStreamVector);
+
+    CBoolVector emptyFileVector, antiVector;
+    emptyFileVector.Reserve(numEmptyStreams);
+    antiVector.Reserve(numEmptyStreams);
+    CNum numEmptyFiles = 0, numAntiItems = 0;
+    for (i = 0; i < db.Files.Size(); i++)
+    {
+      const CFileItem &file = db.Files[i];
+      if (!file.HasStream)
+      {
+        emptyFileVector.Add(!file.IsDir);
+        if (!file.IsDir)
+          numEmptyFiles++;
+        bool isAnti = db.IsItemAnti(i);
+        antiVector.Add(isAnti);
+        if (isAnti)
+          numAntiItems++;
+      }
+    }
+
+    if (numEmptyFiles > 0)
+    {
+      WriteByte(NID::kEmptyFile);
+      WriteNumber((emptyFileVector.Size() + 7) / 8);
+      WriteBoolVector(emptyFileVector);
+    }
+
+    if (numAntiItems > 0)
+    {
+      WriteByte(NID::kAnti);
+      WriteNumber((antiVector.Size() + 7) / 8);
+      WriteBoolVector(antiVector);
+    }
+  }
+  }
+
+
+  {
+    /* ---------- Names ---------- */
+    
+    int numDefined = 0;
+    size_t namesDataSize = 0;
+    for (int i = 0; i < db.Files.Size(); i++)
+    {
+      const UString &name = db.Files[i].Name;
+      if (!name.IsEmpty())
+        numDefined++;
+      namesDataSize += (name.Length() + 1) * 2;
+    }
+    
+    if (numDefined > 0)
+    {
+      namesDataSize++;
+      SkipAlign(2 + GetBigNumberSize(namesDataSize), 2);
+
+      WriteByte(NID::kName);
+      WriteNumber(namesDataSize);
+      WriteByte(0);
+      for (int i = 0; i < db.Files.Size(); i++)
+      {
+        const UString &name = db.Files[i].Name;
+        for (int t = 0; t <= name.Length(); t++)
+        {
+          wchar_t c = name[t];
+          WriteByte((Byte)c);
+          WriteByte((Byte)(c >> 8));
+        }
+      }
+    }
+  }
+
+  if (headerOptions.WriteCTime) WriteUInt64DefVector(db.CTime, NID::kCTime);
+  if (headerOptions.WriteATime) WriteUInt64DefVector(db.ATime, NID::kATime);
+  if (headerOptions.WriteMTime) WriteUInt64DefVector(db.MTime, NID::kMTime);
+  WriteUInt64DefVector(db.StartPos, NID::kStartPos);
+  
+  {
+    /* ---------- Write Attrib ---------- */
+    CBoolVector boolVector;
+    boolVector.Reserve(db.Files.Size());
+    int numDefined = 0;
+    for (i = 0; i < db.Files.Size(); i++)
+    {
+      bool defined = db.Files[i].AttribDefined;
+      boolVector.Add(defined);
+      if (defined)
+        numDefined++;
+    }
+    if (numDefined > 0)
+    {
+      WriteAlignedBoolHeader(boolVector, numDefined, NID::kWinAttributes, 4);
+      for (i = 0; i < db.Files.Size(); i++)
+      {
+        const CFileItem &file = db.Files[i];
+        if (file.AttribDefined)
+          WriteUInt32(file.Attrib);
+      }
+    }
+  }
+
+  WriteByte(NID::kEnd); // for files
+  WriteByte(NID::kEnd); // for headers
+}
+
+HRESULT COutArchive::WriteDatabase(
+    DECL_EXTERNAL_CODECS_LOC_VARS
+    const CArchiveDatabase &db,
+    const CCompressionMethodMode *options,
+    const CHeaderOptions &headerOptions)
+{
+  if (!db.CheckNumFiles())
+    return E_FAIL;
+
+  UInt64 headerOffset;
+  UInt32 headerCRC;
+  UInt64 headerSize;
+  if (db.IsEmpty())
+  {
+    headerSize = 0;
+    headerOffset = 0;
+    headerCRC = CrcCalc(0, 0);
+  }
+  else
+  {
+    bool encodeHeaders = false;
+    if (options != 0)
+      if (options->IsEmpty())
+        options = 0;
+    if (options != 0)
+      if (options->PasswordIsDefined || headerOptions.CompressMainHeader)
+        encodeHeaders = true;
+
+    _outByte.SetStream(SeqStream);
+    _outByte.Init();
+    _crc = CRC_INIT_VAL;
+    _countMode = encodeHeaders;
+    _writeToStream = true;
+    _countSize = 0;
+    WriteHeader(db, headerOptions, headerOffset);
+
+    if (encodeHeaders)
+    {
+      CByteBuffer buf;
+      buf.SetCapacity(_countSize);
+      _outByte2.Init((Byte *)buf, _countSize);
+      
+      _countMode = false;
+      _writeToStream = false;
+      WriteHeader(db, headerOptions, headerOffset);
+      
+      if (_countSize != _outByte2.GetPos())
+        return E_FAIL;
+
+      CCompressionMethodMode encryptOptions;
+      encryptOptions.PasswordIsDefined = options->PasswordIsDefined;
+      encryptOptions.Password = options->Password;
+      CEncoder encoder(headerOptions.CompressMainHeader ? *options : encryptOptions);
+      CRecordVector<UInt64> packSizes;
+      CObjectVector<CFolder> folders;
+      RINOK(EncodeStream(
+          EXTERNAL_CODECS_LOC_VARS
+          encoder, (const Byte *)buf,
+          _countSize, packSizes, folders));
+
+      _writeToStream = true;
+      
+      if (folders.Size() == 0)
+        throw 1;
+
+      WriteID(NID::kEncodedHeader);
+      WritePackInfo(headerOffset, packSizes,
+        CRecordVector<bool>(), CRecordVector<UInt32>());
+      WriteUnpackInfo(folders);
+      WriteByte(NID::kEnd);
+      for (int i = 0; i < packSizes.Size(); i++)
+        headerOffset += packSizes[i];
+    }
+    RINOK(_outByte.Flush());
+    headerCRC = CRC_GET_DIGEST(_crc);
+    headerSize = _outByte.GetProcessedSize();
+  }
+  #ifdef _7Z_VOL
+  if (_endMarker)
+  {
+    CFinishHeader h;
+    h.NextHeaderSize = headerSize;
+    h.NextHeaderCRC = headerCRC;
+    h.NextHeaderOffset =
+        UInt64(0) - (headerSize +
+        4 + kFinishHeaderSize);
+    h.ArchiveStartOffset = h.NextHeaderOffset - headerOffset;
+    h.AdditionalStartBlockSize = 0;
+    RINOK(WriteFinishHeader(h));
+    return WriteFinishSignature();
+  }
+  else
+  #endif
+  {
+    CStartHeader h;
+    h.NextHeaderSize = headerSize;
+    h.NextHeaderCRC = headerCRC;
+    h.NextHeaderOffset = headerOffset;
+    RINOK(Stream->Seek(_prefixHeaderPos, STREAM_SEEK_SET, NULL));
+    return WriteStartHeader(h);
+  }
+}
+
+void CArchiveDatabase::GetFile(int index, CFileItem &file, CFileItem2 &file2) const
+{
+  file = Files[index];
+  file2.CTimeDefined = CTime.GetItem(index, file2.CTime);
+  file2.ATimeDefined = ATime.GetItem(index, file2.ATime);
+  file2.MTimeDefined = MTime.GetItem(index, file2.MTime);
+  file2.StartPosDefined = StartPos.GetItem(index, file2.StartPos);
+  file2.IsAnti = IsItemAnti(index);
+}
+
+void CArchiveDatabase::AddFile(const CFileItem &file, const CFileItem2 &file2)
+{
+  int index = Files.Size();
+  CTime.SetItem(index, file2.CTimeDefined, file2.CTime);
+  ATime.SetItem(index, file2.ATimeDefined, file2.ATime);
+  MTime.SetItem(index, file2.MTimeDefined, file2.MTime);
+  StartPos.SetItem(index, file2.StartPosDefined, file2.StartPos);
+  SetItemAnti(index, file2.IsAnti);
+  Files.Add(file);
+}
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zOut.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zOut.h
new file mode 100644
index 0000000..bdd84ab
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zOut.h
@@ -0,0 +1,156 @@
+// 7z/Out.h
+
+#ifndef __7Z_OUT_H
+#define __7Z_OUT_H
+
+#include "7zHeader.h"
+#include "7zItem.h"
+#include "7zCompressionMode.h"
+#include "7zEncode.h"
+
+#include "../../Common/OutBuffer.h"
+
+namespace NArchive {
+namespace N7z {
+
+class CWriteBufferLoc
+{
+  Byte *_data;
+  size_t _size;
+  size_t _pos;
+public:
+  CWriteBufferLoc(): _size(0), _pos(0) {}
+  void Init(Byte *data, size_t size)
+  {
+    _data = data;
+    _size = size;
+    _pos = 0;
+  }
+  void WriteBytes(const void *data, size_t size)
+  {
+    if (size > _size - _pos)
+      throw 1;
+    memcpy(_data + _pos, data, size);
+    _pos += size;
+  }
+  void WriteByte(Byte b)
+  {
+    if (_size == _pos)
+      throw 1;
+    _data[_pos++] = b;
+  }
+  size_t GetPos() const { return _pos; }
+};
+
+struct CHeaderOptions
+{
+  bool CompressMainHeader;
+  bool WriteCTime;
+  bool WriteATime;
+  bool WriteMTime;
+
+  CHeaderOptions():
+      CompressMainHeader(true),
+      WriteCTime(false),
+      WriteATime(false),
+      WriteMTime(true)
+      {}
+};
+
+class COutArchive
+{
+  UInt64 _prefixHeaderPos;
+
+  HRESULT WriteDirect(const void *data, UInt32 size);
+  
+  UInt64 GetPos() const;
+  void WriteBytes(const void *data, size_t size);
+  void WriteBytes(const CByteBuffer &data) { WriteBytes(data, data.GetCapacity()); }
+  void WriteByte(Byte b);
+  void WriteUInt32(UInt32 value);
+  void WriteUInt64(UInt64 value);
+  void WriteNumber(UInt64 value);
+  void WriteID(UInt64 value) { WriteNumber(value); }
+
+  void WriteFolder(const CFolder &folder);
+  HRESULT WriteFileHeader(const CFileItem &itemInfo);
+  void WriteBoolVector(const CBoolVector &boolVector);
+  void WriteHashDigests(
+      const CRecordVector<bool> &digestsDefined,
+      const CRecordVector<UInt32> &hashDigests);
+
+  void WritePackInfo(
+      UInt64 dataOffset,
+      const CRecordVector<UInt64> &packSizes,
+      const CRecordVector<bool> &packCRCsDefined,
+      const CRecordVector<UInt32> &packCRCs);
+
+  void WriteUnpackInfo(const CObjectVector<CFolder> &folders);
+
+  void WriteSubStreamsInfo(
+      const CObjectVector<CFolder> &folders,
+      const CRecordVector<CNum> &numUnpackStreamsInFolders,
+      const CRecordVector<UInt64> &unpackSizes,
+      const CRecordVector<bool> &digestsDefined,
+      const CRecordVector<UInt32> &hashDigests);
+
+  void SkipAlign(unsigned pos, unsigned alignSize);
+  void WriteAlignedBoolHeader(const CBoolVector &v, int numDefined, Byte type, unsigned itemSize);
+  void WriteUInt64DefVector(const CUInt64DefVector &v, Byte type);
+
+  HRESULT EncodeStream(
+      DECL_EXTERNAL_CODECS_LOC_VARS
+      CEncoder &encoder, const Byte *data, size_t dataSize,
+      CRecordVector<UInt64> &packSizes, CObjectVector<CFolder> &folders);
+  HRESULT EncodeStream(
+      DECL_EXTERNAL_CODECS_LOC_VARS
+      CEncoder &encoder, const CByteBuffer &data,
+      CRecordVector<UInt64> &packSizes, CObjectVector<CFolder> &folders);
+  void WriteHeader(
+      const CArchiveDatabase &db,
+      const CHeaderOptions &headerOptions,
+      UInt64 &headerOffset);
+  
+  bool _countMode;
+  bool _writeToStream;
+  size_t _countSize;
+  UInt32 _crc;
+  COutBuffer _outByte;
+  CWriteBufferLoc _outByte2;
+
+  #ifdef _7Z_VOL
+  bool _endMarker;
+  #endif
+
+  HRESULT WriteSignature();
+  #ifdef _7Z_VOL
+  HRESULT WriteFinishSignature();
+  #endif
+  HRESULT WriteStartHeader(const CStartHeader &h);
+  #ifdef _7Z_VOL
+  HRESULT WriteFinishHeader(const CFinishHeader &h);
+  #endif
+  CMyComPtr<IOutStream> Stream;
+public:
+
+  COutArchive() { _outByte.Create(1 << 16); }
+  CMyComPtr<ISequentialOutStream> SeqStream;
+  HRESULT Create(ISequentialOutStream *stream, bool endMarker);
+  void Close();
+  HRESULT SkeepPrefixArchiveHeader();
+  HRESULT WriteDatabase(
+      DECL_EXTERNAL_CODECS_LOC_VARS
+      const CArchiveDatabase &db,
+      const CCompressionMethodMode *options,
+      const CHeaderOptions &headerOptions);
+
+  #ifdef _7Z_VOL
+  static UInt32 GetVolHeadersSize(UInt64 dataSize, int nameLength = 0, bool props = false);
+  static UInt64 GetVolPureSize(UInt64 volSize, int nameLength = 0, bool props = false);
+  #endif
+
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zProperties.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zProperties.cpp
new file mode 100644
index 0000000..66d9078
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zProperties.cpp
@@ -0,0 +1,163 @@
+// 7zProperties.cpp
+
+#include "StdAfx.h"
+
+#include "7zProperties.h"
+#include "7zHeader.h"
+#include "7zHandler.h"
+
+// #define _MULTI_PACK
+
+namespace NArchive {
+namespace N7z {
+
+struct CPropMap
+{
+  UInt64 FilePropID;
+  STATPROPSTG StatPROPSTG;
+};
+
+CPropMap kPropMap[] =
+{
+  { NID::kName, NULL, kpidPath, VT_BSTR},
+  { NID::kSize, NULL, kpidSize, VT_UI8},
+  { NID::kPackInfo, NULL, kpidPackSize, VT_UI8},
+  
+  #ifdef _MULTI_PACK
+  { 100, L"Pack0", kpidPackedSize0, VT_UI8},
+  { 101, L"Pack1", kpidPackedSize1, VT_UI8},
+  { 102, L"Pack2", kpidPackedSize2, VT_UI8},
+  { 103, L"Pack3", kpidPackedSize3, VT_UI8},
+  { 104, L"Pack4", kpidPackedSize4, VT_UI8},
+  #endif
+
+  { NID::kCTime, NULL, kpidCTime, VT_FILETIME},
+  { NID::kMTime, NULL, kpidMTime, VT_FILETIME},
+  { NID::kATime, NULL, kpidATime, VT_FILETIME},
+  { NID::kWinAttributes, NULL, kpidAttrib, VT_UI4},
+  { NID::kStartPos, NULL, kpidPosition, VT_UI4},
+
+  { NID::kCRC, NULL, kpidCRC, VT_UI4},
+  
+  { NID::kAnti, NULL, kpidIsAnti, VT_BOOL},
+
+  #ifndef _SFX
+  { 97, NULL, kpidEncrypted, VT_BOOL},
+  { 98, NULL, kpidMethod, VT_BSTR},
+  { 99, NULL, kpidBlock, VT_UI4}
+  #endif
+};
+
+static const int kPropMapSize = sizeof(kPropMap) / sizeof(kPropMap[0]);
+
+static int FindPropInMap(UInt64 filePropID)
+{
+  for (int i = 0; i < kPropMapSize; i++)
+    if (kPropMap[i].FilePropID == filePropID)
+      return i;
+  return -1;
+}
+
+static void CopyOneItem(CRecordVector<UInt64> &src,
+    CRecordVector<UInt64> &dest, UInt32 item)
+{
+  for (int i = 0; i < src.Size(); i++)
+    if (src[i] == item)
+    {
+      dest.Add(item);
+      src.Delete(i);
+      return;
+    }
+}
+
+static void RemoveOneItem(CRecordVector<UInt64> &src, UInt32 item)
+{
+  for (int i = 0; i < src.Size(); i++)
+    if (src[i] == item)
+    {
+      src.Delete(i);
+      return;
+    }
+}
+
+static void InsertToHead(CRecordVector<UInt64> &dest, UInt32 item)
+{
+  for (int i = 0; i < dest.Size(); i++)
+    if (dest[i] == item)
+    {
+      dest.Delete(i);
+      break;
+    }
+  dest.Insert(0, item);
+}
+
+void CHandler::FillPopIDs()
+{
+  _fileInfoPopIDs.Clear();
+
+  #ifdef _7Z_VOL
+  if(_volumes.Size() < 1)
+    return;
+  const CVolume &volume = _volumes.Front();
+  const CArchiveDatabaseEx &_db = volume.Database;
+  #endif
+
+  CRecordVector<UInt64> fileInfoPopIDs = _db.ArchiveInfo.FileInfoPopIDs;
+
+  RemoveOneItem(fileInfoPopIDs, NID::kEmptyStream);
+  RemoveOneItem(fileInfoPopIDs, NID::kEmptyFile);
+
+  CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kName);
+  CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kAnti);
+  CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kSize);
+  CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kPackInfo);
+  CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kCTime);
+  CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kMTime);
+  CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kATime);
+  CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kWinAttributes);
+  CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kCRC);
+  CopyOneItem(fileInfoPopIDs, _fileInfoPopIDs, NID::kComment);
+  _fileInfoPopIDs += fileInfoPopIDs;
+ 
+  #ifndef _SFX
+  _fileInfoPopIDs.Add(97);
+  _fileInfoPopIDs.Add(98);
+  _fileInfoPopIDs.Add(99);
+  #endif
+  #ifdef _MULTI_PACK
+  _fileInfoPopIDs.Add(100);
+  _fileInfoPopIDs.Add(101);
+  _fileInfoPopIDs.Add(102);
+  _fileInfoPopIDs.Add(103);
+  _fileInfoPopIDs.Add(104);
+  #endif
+
+  #ifndef _SFX
+  InsertToHead(_fileInfoPopIDs, NID::kMTime);
+  InsertToHead(_fileInfoPopIDs, NID::kPackInfo);
+  InsertToHead(_fileInfoPopIDs, NID::kSize);
+  InsertToHead(_fileInfoPopIDs, NID::kName);
+  #endif
+}
+
+STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProperties)
+{
+  *numProperties = _fileInfoPopIDs.Size();
+  return S_OK;
+}
+
+STDMETHODIMP CHandler::GetPropertyInfo(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType)
+{
+  if ((int)index >= _fileInfoPopIDs.Size())
+    return E_INVALIDARG;
+  int indexInMap = FindPropInMap(_fileInfoPopIDs[index]);
+  if (indexInMap == -1)
+    return E_INVALIDARG;
+  const STATPROPSTG &srcItem = kPropMap[indexInMap].StatPROPSTG;
+  *propID = srcItem.propid;
+  *varType = srcItem.vt;
+  *name = 0;
+  return S_OK;
+}
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zProperties.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zProperties.h
new file mode 100644
index 0000000..6618179
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zProperties.h
@@ -0,0 +1,22 @@
+// 7zProperties.h
+
+#ifndef __7Z_PROPERTIES_H
+#define __7Z_PROPERTIES_H
+
+#include "../../PropID.h"
+
+namespace NArchive {
+namespace N7z {
+
+enum
+{
+  kpidPackedSize0 = kpidUserDefined,
+  kpidPackedSize1,
+  kpidPackedSize2,
+  kpidPackedSize3,
+  kpidPackedSize4
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zRegister.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zRegister.cpp
new file mode 100644
index 0000000..e18c4d7
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zRegister.cpp
@@ -0,0 +1,18 @@
+// 7zRegister.cpp
+
+#include "StdAfx.h"
+
+#include "../../Common/RegisterArc.h"
+
+#include "7zHandler.h"
+static IInArchive *CreateArc() { return new NArchive::N7z::CHandler;  }
+#ifndef EXTRACT_ONLY
+static IOutArchive *CreateArcOut() { return new NArchive::N7z::CHandler;  }
+#else
+#define CreateArcOut 0
+#endif
+
+static CArcInfo g_ArcInfo =
+  { L"7z", L"7z", 0, 7, {'7' + 1 , 'z', 0xBC, 0xAF, 0x27, 0x1C}, 6, false, CreateArc, CreateArcOut };
+
+REGISTER_ARC_DEC_SIG(7z)
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zSpecStream.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zSpecStream.cpp
new file mode 100644
index 0000000..0696963
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zSpecStream.cpp
@@ -0,0 +1,24 @@
+// 7zSpecStream.cpp
+
+#include "StdAfx.h"
+
+#include "7zSpecStream.h"
+
+STDMETHODIMP CSequentialInStreamSizeCount2::Read(void *data, UInt32 size, UInt32 *processedSize)
+{
+  UInt32 realProcessedSize;
+  HRESULT result = _stream->Read(data, size, &realProcessedSize);
+  _size += realProcessedSize;
+  if (processedSize != 0)
+    *processedSize = realProcessedSize;
+  return result;
+}
+
+STDMETHODIMP CSequentialInStreamSizeCount2::GetSubStreamSize(
+    UInt64 subStream, UInt64 *value)
+{
+  if (_getSubStreamSize == NULL)
+    return E_NOTIMPL;
+  return  _getSubStreamSize->GetSubStreamSize(subStream, value);
+}
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zSpecStream.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zSpecStream.h
new file mode 100644
index 0000000..2e26efd
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zSpecStream.h
@@ -0,0 +1,35 @@
+// 7zSpecStream.h
+
+#ifndef __7Z_SPEC_STREAM_H
+#define __7Z_SPEC_STREAM_H
+
+#include "../../IStream.h"
+#include "../../ICoder.h"
+#include "../../../Common/MyCom.h"
+
+class CSequentialInStreamSizeCount2:
+  public ISequentialInStream,
+  public ICompressGetSubStreamSize,
+  public CMyUnknownImp
+{
+  CMyComPtr<ISequentialInStream> _stream;
+  CMyComPtr<ICompressGetSubStreamSize> _getSubStreamSize;
+  UInt64 _size;
+public:
+  void Init(ISequentialInStream *stream)
+  {
+    _stream = stream;
+    _getSubStreamSize = 0;
+    _stream.QueryInterface(IID_ICompressGetSubStreamSize, &_getSubStreamSize);
+    _size = 0;
+  }
+  UInt64 GetSize() const { return _size; }
+
+  MY_UNKNOWN_IMP1(ICompressGetSubStreamSize)
+
+  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
+
+  STDMETHOD(GetSubStreamSize)(UInt64 subStream, UInt64 *value);
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zUpdate.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zUpdate.cpp
new file mode 100644
index 0000000..0285b18
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zUpdate.cpp
@@ -0,0 +1,820 @@
+// 7zUpdate.cpp
+
+#include "StdAfx.h"
+
+#include "../../Common/LimitedStreams.h"
+#include "../../Common/ProgressUtils.h"
+
+#include "../../Compress/CopyCoder.h"
+
+#include "../Common/ItemNameUtils.h"
+
+#include "7zEncode.h"
+#include "7zFolderInStream.h"
+#include "7zHandler.h"
+#include "7zOut.h"
+#include "7zUpdate.h"
+
+namespace NArchive {
+namespace N7z {
+
+static const wchar_t *kMatchFinderForBCJ2_LZMA = L"BT2";
+static const UInt32 kDictionaryForBCJ2_LZMA = 1 << 20;
+static const UInt32 kAlgorithmForBCJ2_LZMA = 1;
+static const UInt32 kNumFastBytesForBCJ2_LZMA = 64;
+
+static HRESULT WriteRange(IInStream *inStream, ISequentialOutStream *outStream,
+    UInt64 position, UInt64 size, ICompressProgressInfo *progress)
+{
+  RINOK(inStream->Seek(position, STREAM_SEEK_SET, 0));
+  CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
+  CMyComPtr<CLimitedSequentialInStream> inStreamLimited(streamSpec);
+  streamSpec->SetStream(inStream);
+  streamSpec->Init(size);
+
+  NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
+  CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
+  RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress));
+  return (copyCoderSpec->TotalSize == size ? S_OK : E_FAIL);
+}
+
+static int GetReverseSlashPos(const UString &name)
+{
+  int slashPos = name.ReverseFind(L'/');
+  #ifdef _WIN32
+  int slash1Pos = name.ReverseFind(L'\\');
+  slashPos = MyMax(slashPos, slash1Pos);
+  #endif
+  return slashPos;
+}
+
+int CUpdateItem::GetExtensionPos() const
+{
+  int slashPos = GetReverseSlashPos(Name);
+  int dotPos = Name.ReverseFind(L'.');
+  if (dotPos < 0 || (dotPos < slashPos && slashPos >= 0))
+    return Name.Length();
+  return dotPos + 1;
+}
+
+UString CUpdateItem::GetExtension() const
+{
+  return Name.Mid(GetExtensionPos());
+}
+
+#define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; }
+
+static int CompareBuffers(const CByteBuffer &a1, const CByteBuffer &a2)
+{
+  size_t c1 = a1.GetCapacity();
+  size_t c2 = a2.GetCapacity();
+  RINOZ(MyCompare(c1, c2));
+  for (size_t i = 0; i < c1; i++)
+    RINOZ(MyCompare(a1[i], a2[i]));
+  return 0;
+}
+
+static int CompareCoders(const CCoderInfo &c1, const CCoderInfo &c2)
+{
+  RINOZ(MyCompare(c1.NumInStreams, c2.NumInStreams));
+  RINOZ(MyCompare(c1.NumOutStreams, c2.NumOutStreams));
+  RINOZ(MyCompare(c1.MethodID, c2.MethodID));
+  return CompareBuffers(c1.Props, c2.Props);
+}
+
+static int CompareBindPairs(const CBindPair &b1, const CBindPair &b2)
+{
+  RINOZ(MyCompare(b1.InIndex, b2.InIndex));
+  return MyCompare(b1.OutIndex, b2.OutIndex);
+}
+
+static int CompareFolders(const CFolder &f1, const CFolder &f2)
+{
+  int s1 = f1.Coders.Size();
+  int s2 = f2.Coders.Size();
+  RINOZ(MyCompare(s1, s2));
+  int i;
+  for (i = 0; i < s1; i++)
+    RINOZ(CompareCoders(f1.Coders[i], f2.Coders[i]));
+  s1 = f1.BindPairs.Size();
+  s2 = f2.BindPairs.Size();
+  RINOZ(MyCompare(s1, s2));
+  for (i = 0; i < s1; i++)
+    RINOZ(CompareBindPairs(f1.BindPairs[i], f2.BindPairs[i]));
+  return 0;
+}
+
+static int CompareFiles(const CFileItem &f1, const CFileItem &f2)
+{
+  return MyStringCompareNoCase(f1.Name, f2.Name);
+}
+
+static int CompareFolderRefs(const int *p1, const int *p2, void *param)
+{
+  int i1 = *p1;
+  int i2 = *p2;
+  const CArchiveDatabaseEx &db = *(const CArchiveDatabaseEx *)param;
+  RINOZ(CompareFolders(
+      db.Folders[i1],
+      db.Folders[i2]));
+  RINOZ(MyCompare(
+      db.NumUnpackStreamsVector[i1],
+      db.NumUnpackStreamsVector[i2]));
+  if (db.NumUnpackStreamsVector[i1] == 0)
+    return 0;
+  return CompareFiles(
+      db.Files[db.FolderStartFileIndex[i1]],
+      db.Files[db.FolderStartFileIndex[i2]]);
+}
+
+////////////////////////////////////////////////////////////
+
+static int CompareEmptyItems(const int *p1, const int *p2, void *param)
+{
+  const CObjectVector<CUpdateItem> &updateItems = *(const CObjectVector<CUpdateItem> *)param;
+  const CUpdateItem &u1 = updateItems[*p1];
+  const CUpdateItem &u2 = updateItems[*p2];
+  if (u1.IsDir != u2.IsDir)
+    return (u1.IsDir) ? 1 : -1;
+  if (u1.IsDir)
+  {
+    if (u1.IsAnti != u2.IsAnti)
+      return (u1.IsAnti ? 1 : -1);
+    int n = MyStringCompareNoCase(u1.Name, u2.Name);
+    return -n;
+  }
+  if (u1.IsAnti != u2.IsAnti)
+    return (u1.IsAnti ? 1 : -1);
+  return MyStringCompareNoCase(u1.Name, u2.Name);
+}
+
+static const char *g_Exts =
+  " lzma 7z ace arc arj bz bz2 deb lzo lzx gz pak rpm sit tgz tbz tbz2 tgz cab ha lha lzh rar zoo"
+  " zip jar ear war msi"
+  " 3gp avi mov mpeg mpg mpe wmv"
+  " aac ape fla flac la mp3 m4a mp4 ofr ogg pac ra rm rka shn swa tta wv wma wav"
+  " swf "
+  " chm hxi hxs"
+  " gif jpeg jpg jp2 png tiff  bmp ico psd psp"
+  " awg ps eps cgm dxf svg vrml wmf emf ai md"
+  " cad dwg pps key sxi"
+  " max 3ds"
+  " iso bin nrg mdf img pdi tar cpio xpi"
+  " vfd vhd vud vmc vsv"
+  " vmdk dsk nvram vmem vmsd vmsn vmss vmtm"
+  " inl inc idl acf asa h hpp hxx c cpp cxx rc java cs pas bas vb cls ctl frm dlg def"
+  " f77 f f90 f95"
+  " asm sql manifest dep "
+  " mak clw csproj vcproj sln dsp dsw "
+  " class "
+  " bat cmd"
+  " xml xsd xsl xslt hxk hxc htm html xhtml xht mht mhtml htw asp aspx css cgi jsp shtml"
+  " awk sed hta js php php3 php4 php5 phptml pl pm py pyo rb sh tcl vbs"
+  " text txt tex ans asc srt reg ini doc docx mcw dot rtf hlp xls xlr xlt xlw ppt pdf"
+  " sxc sxd sxi sxg sxw stc sti stw stm odt ott odg otg odp otp ods ots odf"
+  " abw afp cwk lwp wpd wps wpt wrf wri"
+  " abf afm bdf fon mgf otf pcf pfa snf ttf"
+  " dbf mdb nsf ntf wdb db fdb gdb"
+  " exe dll ocx vbx sfx sys tlb awx com obj lib out o so "
+  " pdb pch idb ncb opt";
+
+int GetExtIndex(const char *ext)
+{
+  int extIndex = 1;
+  const char *p = g_Exts;
+  for (;;)
+  {
+    char c = *p++;
+    if (c == 0)
+      return extIndex;
+    if (c == ' ')
+      continue;
+    int pos = 0;
+    for (;;)
+    {
+      char c2 = ext[pos++];
+      if (c2 == 0 && (c == 0 || c == ' '))
+        return extIndex;
+      if (c != c2)
+        break;
+      c = *p++;
+    }
+    extIndex++;
+    for (;;)
+    {
+      if (c == 0)
+        return extIndex;
+      if (c == ' ')
+        break;
+      c = *p++;
+    }
+  }
+}
+
+struct CRefItem
+{
+  const CUpdateItem *UpdateItem;
+  UInt32 Index;
+  UInt32 ExtensionPos;
+  UInt32 NamePos;
+  int ExtensionIndex;
+  CRefItem(UInt32 index, const CUpdateItem &ui, bool sortByType):
+    UpdateItem(&ui),
+    Index(index),
+    ExtensionPos(0),
+    NamePos(0),
+    ExtensionIndex(0)
+  {
+    if (sortByType)
+    {
+      int slashPos = GetReverseSlashPos(ui.Name);
+      NamePos = ((slashPos >= 0) ? (slashPos + 1) : 0);
+      int dotPos = ui.Name.ReverseFind(L'.');
+      if (dotPos < 0 || (dotPos < slashPos && slashPos >= 0))
+        ExtensionPos = ui.Name.Length();
+      else
+      {
+        ExtensionPos = dotPos + 1;
+        UString us = ui.Name.Mid(ExtensionPos);
+        if (!us.IsEmpty())
+        {
+          us.MakeLower();
+          int i;
+          AString s;
+          for (i = 0; i < us.Length(); i++)
+          {
+            wchar_t c = us[i];
+            if (c >= 0x80)
+              break;
+            s += (char)c;
+          }
+          if (i == us.Length())
+            ExtensionIndex = GetExtIndex(s);
+          else
+            ExtensionIndex = 0;
+        }
+      }
+    }
+  }
+};
+
+static int CompareUpdateItems(const CRefItem *p1, const CRefItem *p2, void *param)
+{
+  const CRefItem &a1 = *p1;
+  const CRefItem &a2 = *p2;
+  const CUpdateItem &u1 = *a1.UpdateItem;
+  const CUpdateItem &u2 = *a2.UpdateItem;
+  int n;
+  if (u1.IsDir != u2.IsDir)
+    return (u1.IsDir) ? 1 : -1;
+  if (u1.IsDir)
+  {
+    if (u1.IsAnti != u2.IsAnti)
+      return (u1.IsAnti ? 1 : -1);
+    n = MyStringCompareNoCase(u1.Name, u2.Name);
+    return -n;
+  }
+  bool sortByType = *(bool *)param;
+  if (sortByType)
+  {
+    RINOZ(MyCompare(a1.ExtensionIndex, a2.ExtensionIndex))
+    RINOZ(MyStringCompareNoCase(u1.Name + a1.ExtensionPos, u2.Name + a2.ExtensionPos));
+    RINOZ(MyStringCompareNoCase(u1.Name + a1.NamePos, u2.Name + a2.NamePos));
+    if (!u1.MTimeDefined && u2.MTimeDefined) return 1;
+    if (u1.MTimeDefined && !u2.MTimeDefined) return -1;
+    if (u1.MTimeDefined && u2.MTimeDefined) RINOZ(MyCompare(u1.MTime, u2.MTime));
+    RINOZ(MyCompare(u1.Size, u2.Size))
+  }
+  return MyStringCompareNoCase(u1.Name, u2.Name);
+}
+
+struct CSolidGroup
+{
+  CCompressionMethodMode Method;
+  CRecordVector<UInt32> Indices;
+};
+
+static wchar_t *g_ExeExts[] =
+{
+  L"dll",
+  L"exe",
+  L"ocx",
+  L"sfx",
+  L"sys"
+};
+
+static bool IsExeFile(const UString &ext)
+{
+  for (int i = 0; i < sizeof(g_ExeExts) / sizeof(g_ExeExts[0]); i++)
+    if (ext.CompareNoCase(g_ExeExts[i]) == 0)
+      return true;
+  return false;
+}
+
+static const UInt64 k_LZMA  = 0x030101;
+static const UInt64 k_BCJ   = 0x03030103;
+static const UInt64 k_BCJ2  = 0x0303011B;
+
+static bool GetMethodFull(UInt64 methodID,
+    UInt32 numInStreams, CMethodFull &methodResult)
+{
+  methodResult.Id = methodID;
+  methodResult.NumInStreams = numInStreams;
+  methodResult.NumOutStreams = 1;
+  return true;
+}
+
+static bool MakeExeMethod(const CCompressionMethodMode &method,
+    bool bcj2Filter, CCompressionMethodMode &exeMethod)
+{
+  exeMethod = method;
+  if (bcj2Filter)
+  {
+    CMethodFull methodFull;
+    if (!GetMethodFull(k_BCJ2, 4, methodFull))
+      return false;
+    exeMethod.Methods.Insert(0, methodFull);
+    if (!GetMethodFull(k_LZMA, 1, methodFull))
+      return false;
+    {
+      CProp prop;
+      prop.Id = NCoderPropID::kAlgorithm;
+      prop.Value = kAlgorithmForBCJ2_LZMA;
+      methodFull.Props.Add(prop);
+    }
+    {
+      CProp prop;
+      prop.Id = NCoderPropID::kMatchFinder;
+      prop.Value = kMatchFinderForBCJ2_LZMA;
+      methodFull.Props.Add(prop);
+    }
+    {
+      CProp prop;
+      prop.Id = NCoderPropID::kDictionarySize;
+      prop.Value = kDictionaryForBCJ2_LZMA;
+      methodFull.Props.Add(prop);
+    }
+    {
+      CProp prop;
+      prop.Id = NCoderPropID::kNumFastBytes;
+      prop.Value = kNumFastBytesForBCJ2_LZMA;
+      methodFull.Props.Add(prop);
+    }
+
+    exeMethod.Methods.Add(methodFull);
+    exeMethod.Methods.Add(methodFull);
+    CBind bind;
+
+    bind.OutCoder = 0;
+    bind.InStream = 0;
+
+    bind.InCoder = 1;
+    bind.OutStream = 0;
+    exeMethod.Binds.Add(bind);
+
+    bind.InCoder = 2;
+    bind.OutStream = 1;
+    exeMethod.Binds.Add(bind);
+
+    bind.InCoder = 3;
+    bind.OutStream = 2;
+    exeMethod.Binds.Add(bind);
+  }
+  else
+  {
+    CMethodFull methodFull;
+    if (!GetMethodFull(k_BCJ, 1, methodFull))
+      return false;
+    exeMethod.Methods.Insert(0, methodFull);
+    CBind bind;
+    bind.OutCoder = 0;
+    bind.InStream = 0;
+    bind.InCoder = 1;
+    bind.OutStream = 0;
+    exeMethod.Binds.Add(bind);
+  }
+  return true;
+}
+
+static void SplitFilesToGroups(
+    const CCompressionMethodMode &method,
+    bool useFilters, bool maxFilter,
+    const CObjectVector<CUpdateItem> &updateItems,
+    CObjectVector<CSolidGroup> &groups)
+{
+  if (method.Methods.Size() != 1 || method.Binds.Size() != 0)
+    useFilters = false;
+  groups.Clear();
+  groups.Add(CSolidGroup());
+  groups.Add(CSolidGroup());
+  CSolidGroup &generalGroup = groups[0];
+  CSolidGroup &exeGroup = groups[1];
+  generalGroup.Method = method;
+  int i;
+  for (i = 0; i < updateItems.Size(); i++)
+  {
+    const CUpdateItem &ui = updateItems[i];
+    if (!ui.NewData)
+      continue;
+    if (!ui.HasStream())
+      continue;
+    if (useFilters)
+    {
+      const UString name = ui.Name;
+      int dotPos = name.ReverseFind(L'.');
+      if (dotPos >= 0)
+      {
+        UString ext = name.Mid(dotPos + 1);
+        if (IsExeFile(ext))
+        {
+          exeGroup.Indices.Add(i);
+          continue;
+        }
+      }
+    }
+    generalGroup.Indices.Add(i);
+  }
+  if (exeGroup.Indices.Size() > 0)
+    if (!MakeExeMethod(method, maxFilter, exeGroup.Method))
+      exeGroup.Method = method;
+  for (i = 0; i < groups.Size();)
+    if (groups[i].Indices.Size() == 0)
+      groups.Delete(i);
+    else
+      i++;
+}
+
+static void FromUpdateItemToFileItem(const CUpdateItem &ui,
+    CFileItem &file, CFileItem2 &file2)
+{
+  file.Name = NItemName::MakeLegalName(ui.Name);
+  if (ui.AttribDefined)
+    file.SetAttrib(ui.Attrib);
+  
+  file2.CTime = ui.CTime;  file2.CTimeDefined = ui.CTimeDefined;
+  file2.ATime = ui.ATime;  file2.ATimeDefined = ui.ATimeDefined;
+  file2.MTime = ui.MTime;  file2.MTimeDefined = ui.MTimeDefined;
+  file2.IsAnti = ui.IsAnti;
+  file2.StartPosDefined = false;
+
+  file.Size = ui.Size;
+  file.IsDir = ui.IsDir;
+  file.HasStream = ui.HasStream();
+}
+
+static HRESULT Update2(
+    DECL_EXTERNAL_CODECS_LOC_VARS
+    IInStream *inStream,
+    const CArchiveDatabaseEx *db,
+    const CObjectVector<CUpdateItem> &updateItems,
+    COutArchive &archive,
+    CArchiveDatabase &newDatabase,
+    ISequentialOutStream *seqOutStream,
+    IArchiveUpdateCallback *updateCallback,
+    const CUpdateOptions &options)
+{
+  UInt64 numSolidFiles = options.NumSolidFiles;
+  if (numSolidFiles == 0)
+    numSolidFiles = 1;
+  /*
+  CMyComPtr<IOutStream> outStream;
+  RINOK(seqOutStream->QueryInterface(IID_IOutStream, (void **)&outStream));
+  if (!outStream)
+    return E_NOTIMPL;
+  */
+
+  UInt64 startBlockSize = db != 0 ? db->ArchiveInfo.StartPosition: 0;
+  if (startBlockSize > 0 && !options.RemoveSfxBlock)
+  {
+    RINOK(WriteRange(inStream, seqOutStream, 0, startBlockSize, NULL));
+  }
+
+  CRecordVector<int> fileIndexToUpdateIndexMap;
+  if (db != 0)
+  {
+    fileIndexToUpdateIndexMap.Reserve(db->Files.Size());
+    for (int i = 0; i < db->Files.Size(); i++)
+      fileIndexToUpdateIndexMap.Add(-1);
+  }
+  int i;
+  for(i = 0; i < updateItems.Size(); i++)
+  {
+    int index = updateItems[i].IndexInArchive;
+    if (index != -1)
+      fileIndexToUpdateIndexMap[index] = i;
+  }
+
+  CRecordVector<int> folderRefs;
+  if (db != 0)
+  {
+    for(i = 0; i < db->Folders.Size(); i++)
+    {
+      CNum indexInFolder = 0;
+      CNum numCopyItems = 0;
+      CNum numUnpackStreams = db->NumUnpackStreamsVector[i];
+      for (CNum fileIndex = db->FolderStartFileIndex[i];
+      indexInFolder < numUnpackStreams; fileIndex++)
+      {
+        if (db->Files[fileIndex].HasStream)
+        {
+          indexInFolder++;
+          int updateIndex = fileIndexToUpdateIndexMap[fileIndex];
+          if (updateIndex >= 0)
+            if (!updateItems[updateIndex].NewData)
+              numCopyItems++;
+        }
+      }
+      if (numCopyItems != numUnpackStreams && numCopyItems != 0)
+        return E_NOTIMPL; // It needs repacking !!!
+      if (numCopyItems > 0)
+        folderRefs.Add(i);
+    }
+    folderRefs.Sort(CompareFolderRefs, (void *)db);
+  }
+
+  ////////////////////////////
+
+  RINOK(archive.Create(seqOutStream, false));
+  RINOK(archive.SkeepPrefixArchiveHeader());
+  UInt64 complexity = 0;
+  for(i = 0; i < folderRefs.Size(); i++)
+    complexity += db->GetFolderFullPackSize(folderRefs[i]);
+  UInt64 inSizeForReduce = 0;
+  for(i = 0; i < updateItems.Size(); i++)
+  {
+    const CUpdateItem &ui = updateItems[i];
+    if (ui.NewData)
+    {
+      complexity += ui.Size;
+      if (numSolidFiles == 1)
+      {
+        if (ui.Size > inSizeForReduce)
+          inSizeForReduce = ui.Size;
+      }
+      else
+        inSizeForReduce += ui.Size;
+    }
+  }
+  RINOK(updateCallback->SetTotal(complexity));
+  complexity = 0;
+  RINOK(updateCallback->SetCompleted(&complexity));
+
+
+  CLocalProgress *lps = new CLocalProgress;
+  CMyComPtr<ICompressProgressInfo> progress = lps;
+  lps->Init(updateCallback, true);
+
+  /////////////////////////////////////////
+  // Write Copy Items
+
+  for(i = 0; i < folderRefs.Size(); i++)
+  {
+    int folderIndex = folderRefs[i];
+    
+    lps->ProgressOffset = complexity;
+    UInt64 packSize = db->GetFolderFullPackSize(folderIndex);
+    RINOK(WriteRange(inStream, archive.SeqStream,
+        db->GetFolderStreamPos(folderIndex, 0), packSize, progress));
+    complexity += packSize;
+    
+    const CFolder &folder = db->Folders[folderIndex];
+    CNum startIndex = db->FolderStartPackStreamIndex[folderIndex];
+    for (int j = 0; j < folder.PackStreams.Size(); j++)
+    {
+      newDatabase.PackSizes.Add(db->PackSizes[startIndex + j]);
+      // newDatabase.PackCRCsDefined.Add(db.PackCRCsDefined[startIndex + j]);
+      // newDatabase.PackCRCs.Add(db.PackCRCs[startIndex + j]);
+    }
+    newDatabase.Folders.Add(folder);
+
+    CNum numUnpackStreams = db->NumUnpackStreamsVector[folderIndex];
+    newDatabase.NumUnpackStreamsVector.Add(numUnpackStreams);
+
+    CNum indexInFolder = 0;
+    for (CNum fi = db->FolderStartFileIndex[folderIndex];
+        indexInFolder < numUnpackStreams; fi++)
+    {
+      CFileItem file;
+      CFileItem2 file2;
+      db->GetFile(fi, file, file2);
+      if (file.HasStream)
+      {
+        indexInFolder++;
+        int updateIndex = fileIndexToUpdateIndexMap[fi];
+        if (updateIndex >= 0)
+        {
+          const CUpdateItem &ui = updateItems[updateIndex];
+          if (ui.NewProperties)
+          {
+            CFileItem uf;
+            FromUpdateItemToFileItem(ui, uf, file2);
+            uf.Size = file.Size;
+            uf.Crc = file.Crc;
+            uf.CrcDefined = file.CrcDefined;
+            uf.HasStream = file.HasStream;
+            file = uf;
+          }
+        }
+        newDatabase.AddFile(file, file2);
+      }
+    }
+  }
+
+  folderRefs.ClearAndFree();
+  fileIndexToUpdateIndexMap.ClearAndFree();
+
+  /////////////////////////////////////////
+  // Compress New Files
+
+  CObjectVector<CSolidGroup> groups;
+  SplitFilesToGroups(*options.Method, options.UseFilters, options.MaxFilter,
+      updateItems, groups);
+
+  const UInt32 kMinReduceSize = (1 << 16);
+  if (inSizeForReduce < kMinReduceSize)
+    inSizeForReduce = kMinReduceSize;
+
+  for (int groupIndex = 0; groupIndex < groups.Size(); groupIndex++)
+  {
+    const CSolidGroup &group = groups[groupIndex];
+    int numFiles = group.Indices.Size();
+    if (numFiles == 0)
+      continue;
+    CRecordVector<CRefItem> refItems;
+    refItems.Reserve(numFiles);
+    bool sortByType = (numSolidFiles > 1);
+    for (i = 0; i < numFiles; i++)
+      refItems.Add(CRefItem(group.Indices[i], updateItems[group.Indices[i]], sortByType));
+    refItems.Sort(CompareUpdateItems, (void *)&sortByType);
+    
+    CRecordVector<UInt32> indices;
+    indices.Reserve(numFiles);
+
+    for (i = 0; i < numFiles; i++)
+    {
+      UInt32 index = refItems[i].Index;
+      indices.Add(index);
+      /*
+      const CUpdateItem &ui = updateItems[index];
+      CFileItem file;
+      if (ui.NewProperties)
+        FromUpdateItemToFileItem(ui, file);
+      else
+        file = db.Files[ui.IndexInArchive];
+      if (file.IsAnti || file.IsDir)
+        return E_FAIL;
+      newDatabase.Files.Add(file);
+      */
+    }
+    
+    CEncoder encoder(group.Method);
+
+    for (i = 0; i < numFiles;)
+    {
+      UInt64 totalSize = 0;
+      int numSubFiles;
+      UString prevExtension;
+      for (numSubFiles = 0; i + numSubFiles < numFiles &&
+          numSubFiles < numSolidFiles; numSubFiles++)
+      {
+        const CUpdateItem &ui = updateItems[indices[i + numSubFiles]];
+        totalSize += ui.Size;
+        if (totalSize > options.NumSolidBytes)
+          break;
+        if (options.SolidExtension)
+        {
+          UString ext = ui.GetExtension();
+          if (numSubFiles == 0)
+            prevExtension = ext;
+          else
+            if (ext.CompareNoCase(prevExtension) != 0)
+              break;
+        }
+      }
+      if (numSubFiles < 1)
+        numSubFiles = 1;
+
+      CFolderInStream *inStreamSpec = new CFolderInStream;
+      CMyComPtr<ISequentialInStream> solidInStream(inStreamSpec);
+      inStreamSpec->Init(updateCallback, &indices[i], numSubFiles);
+      
+      CFolder folderItem;
+
+      int startPackIndex = newDatabase.PackSizes.Size();
+      RINOK(encoder.Encode(
+          EXTERNAL_CODECS_LOC_VARS
+          solidInStream, NULL, &inSizeForReduce, folderItem,
+          archive.SeqStream, newDatabase.PackSizes, progress));
+
+      for (; startPackIndex < newDatabase.PackSizes.Size(); startPackIndex++)
+        lps->OutSize += newDatabase.PackSizes[startPackIndex];
+
+      lps->InSize += folderItem.GetUnpackSize();
+      // for()
+      // newDatabase.PackCRCsDefined.Add(false);
+      // newDatabase.PackCRCs.Add(0);
+      
+      newDatabase.Folders.Add(folderItem);
+      
+      CNum numUnpackStreams = 0;
+      for (int subIndex = 0; subIndex < numSubFiles; subIndex++)
+      {
+        const CUpdateItem &ui = updateItems[indices[i + subIndex]];
+        CFileItem file;
+        CFileItem2 file2;
+        if (ui.NewProperties)
+          FromUpdateItemToFileItem(ui, file, file2);
+        else
+          db->GetFile(ui.IndexInArchive, file, file2);
+        if (file2.IsAnti || file.IsDir)
+          return E_FAIL;
+        
+        /*
+        CFileItem &file = newDatabase.Files[
+              startFileIndexInDatabase + i + subIndex];
+        */
+        if (!inStreamSpec->Processed[subIndex])
+        {
+          continue;
+          // file.Name += L".locked";
+        }
+
+        file.Crc = inStreamSpec->CRCs[subIndex];
+        file.Size = inStreamSpec->Sizes[subIndex];
+        if (file.Size != 0)
+        {
+          file.CrcDefined = true;
+          file.HasStream = true;
+          numUnpackStreams++;
+        }
+        else
+        {
+          file.CrcDefined = false;
+          file.HasStream = false;
+        }
+        newDatabase.AddFile(file, file2);
+      }
+      // numUnpackStreams = 0 is very bad case for locked files
+      // v3.13 doesn't understand it.
+      newDatabase.NumUnpackStreamsVector.Add(numUnpackStreams);
+      i += numSubFiles;
+    }
+  }
+
+  groups.ClearAndFree();
+
+  {
+    /////////////////////////////////////////
+    // Write Empty Files & Folders
+    
+    CRecordVector<int> emptyRefs;
+    for(i = 0; i < updateItems.Size(); i++)
+    {
+      const CUpdateItem &ui = updateItems[i];
+      if (ui.NewData)
+      {
+        if (ui.HasStream())
+          continue;
+      }
+      else
+        if (ui.IndexInArchive != -1)
+          if (db->Files[ui.IndexInArchive].HasStream)
+            continue;
+      emptyRefs.Add(i);
+    }
+    emptyRefs.Sort(CompareEmptyItems, (void *)&updateItems);
+    for (i = 0; i < emptyRefs.Size(); i++)
+    {
+      const CUpdateItem &ui = updateItems[emptyRefs[i]];
+      CFileItem file;
+      CFileItem2 file2;
+      if (ui.NewProperties)
+        FromUpdateItemToFileItem(ui, file, file2);
+      else
+        db->GetFile(ui.IndexInArchive, file, file2);
+      newDatabase.AddFile(file, file2);
+    }
+  }
+    
+  newDatabase.ReserveDown();
+  return S_OK;
+}
+
+HRESULT Update(
+    DECL_EXTERNAL_CODECS_LOC_VARS
+    IInStream *inStream,
+    const CArchiveDatabaseEx *db,
+    const CObjectVector<CUpdateItem> &updateItems,
+    COutArchive &archive,
+    CArchiveDatabase &newDatabase,
+    ISequentialOutStream *seqOutStream,
+    IArchiveUpdateCallback *updateCallback,
+    const CUpdateOptions &options)
+{
+  return Update2(
+        EXTERNAL_CODECS_LOC_VARS
+        inStream, db, updateItems,
+        archive, newDatabase, seqOutStream, updateCallback, options);
+}
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zUpdate.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zUpdate.h
new file mode 100644
index 0000000..bcc7cef
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/7zUpdate.h
@@ -0,0 +1,84 @@
+// 7zUpdate.h
+
+#ifndef __7Z_UPDATE_H
+#define __7Z_UPDATE_H
+
+#include "7zIn.h"
+#include "7zOut.h"
+#include "7zCompressionMode.h"
+
+#include "../IArchive.h"
+
+namespace NArchive {
+namespace N7z {
+
+struct CUpdateItem
+{
+  int IndexInArchive;
+  int IndexInClient;
+  
+  UInt64 CTime;
+  UInt64 ATime;
+  UInt64 MTime;
+
+  UInt64 Size;
+  UString Name;
+
+  UInt32 Attrib;
+  
+  bool NewData;
+  bool NewProperties;
+
+  bool IsAnti;
+  bool IsDir;
+
+  bool AttribDefined;
+  bool CTimeDefined;
+  bool ATimeDefined;
+  bool MTimeDefined;
+
+  bool HasStream() const { return !IsDir && !IsAnti && Size != 0; }
+
+  CUpdateItem():
+      IsAnti(false),
+      IsDir(false),
+      AttribDefined(false),
+      CTimeDefined(false),
+      ATimeDefined(false),
+      MTimeDefined(false)
+      {}
+  void SetDirStatusFromAttrib() { IsDir = ((Attrib & FILE_ATTRIBUTE_DIRECTORY) != 0); };
+
+  int GetExtensionPos() const;
+  UString GetExtension() const;
+};
+
+struct CUpdateOptions
+{
+  const CCompressionMethodMode *Method;
+  const CCompressionMethodMode *HeaderMethod;
+  bool UseFilters;
+  bool MaxFilter;
+
+  CHeaderOptions HeaderOptions;
+
+  UInt64 NumSolidFiles;
+  UInt64 NumSolidBytes;
+  bool SolidExtension;
+  bool RemoveSfxBlock;
+  bool VolumeMode;
+};
+
+HRESULT Update(
+    DECL_EXTERNAL_CODECS_LOC_VARS
+    IInStream *inStream,
+    const CArchiveDatabaseEx *db,
+    const CObjectVector<CUpdateItem> &updateItems,
+    COutArchive &archive,
+    CArchiveDatabase &newDatabase,
+    ISequentialOutStream *seqOutStream,
+    IArchiveUpdateCallback *updateCallback,
+    const CUpdateOptions &options);
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/StdAfx.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/StdAfx.cpp
new file mode 100644
index 0000000..d0feea8
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/StdAfx.cpp
@@ -0,0 +1,3 @@
+// StdAfx.cpp
+
+#include "StdAfx.h"
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/StdAfx.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/StdAfx.h
new file mode 100644
index 0000000..2e4be10
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/7z/StdAfx.h
@@ -0,0 +1,9 @@
+// StdAfx.h
+
+#ifndef __STDAFX_H
+#define __STDAFX_H
+
+#include "../../../Common/MyWindows.h"
+#include "../../../Common/NewHandler.h"
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Archive.def b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Archive.def
new file mode 100644
index 0000000..55b530b
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Archive.def
@@ -0,0 +1,6 @@
+EXPORTS
+  CreateObject PRIVATE
+  GetHandlerProperty PRIVATE
+  GetNumberOfFormats PRIVATE
+  GetHandlerProperty2 PRIVATE
+  CreateObject PRIVATE
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Archive2.def b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Archive2.def
new file mode 100644
index 0000000..885d39d
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Archive2.def
@@ -0,0 +1,9 @@
+EXPORTS
+  CreateObject PRIVATE
+  GetHandlerProperty PRIVATE
+  GetNumberOfFormats PRIVATE
+  GetHandlerProperty2 PRIVATE
+  CreateObject PRIVATE
+  GetNumberOfMethods PRIVATE
+  GetMethodProperty PRIVATE
+  SetLargePageMode PRIVATE
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/ArchiveExports.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/ArchiveExports.cpp
new file mode 100644
index 0000000..ae388d6
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/ArchiveExports.cpp
@@ -0,0 +1,130 @@
+// ArchiveExports.cpp
+
+#include "StdAfx.h"
+
+#include "../../Common/ComTry.h"
+#include "../../Common/Types.h"
+#include "../../Windows/PropVariant.h"
+#include "../Common/RegisterArc.h"
+
+#include "IArchive.h"
+#include "../ICoder.h"
+#include "../IPassword.h"
+
+static const unsigned int kNumArcsMax = 32;
+static unsigned int g_NumArcs = 0;
+static const CArcInfo *g_Arcs[kNumArcsMax];
+void RegisterArc(const CArcInfo *arcInfo)
+{
+  if (g_NumArcs < kNumArcsMax)
+    g_Arcs[g_NumArcs++] = arcInfo;
+}
+
+DEFINE_GUID(CLSID_CArchiveHandler,
+0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00);
+
+#define CLS_ARC_ID_ITEM(cls) ((cls).Data4[5])
+
+static inline HRESULT SetPropString(const char *s, unsigned int size, PROPVARIANT *value)
+{
+  if ((value->bstrVal = ::SysAllocStringByteLen(s, size)) != 0)
+    value->vt = VT_BSTR;
+  return S_OK;
+}
+
+static inline HRESULT SetPropGUID(const GUID &guid, PROPVARIANT *value)
+{
+  return SetPropString((const char *)&guid, sizeof(GUID), value);
+}
+
+int FindFormatCalssId(const GUID *clsID)
+{
+  GUID cls = *clsID;
+  CLS_ARC_ID_ITEM(cls) = 0;
+  if (cls != CLSID_CArchiveHandler)
+    return -1;
+  Byte id = CLS_ARC_ID_ITEM(*clsID);
+  for (unsigned i = 0; i < g_NumArcs; i++)
+    if (g_Arcs[i]->ClassId == id)
+      return (int)i;
+  return -1;
+}
+
+STDAPI CreateArchiver(const GUID *clsid, const GUID *iid, void **outObject)
+{
+  COM_TRY_BEGIN
+  {
+    int needIn = (*iid == IID_IInArchive);
+    int needOut = (*iid == IID_IOutArchive);
+    if (!needIn && !needOut)
+      return E_NOINTERFACE;
+    int formatIndex = FindFormatCalssId(clsid);
+    if (formatIndex < 0)
+      return CLASS_E_CLASSNOTAVAILABLE;
+    
+    const CArcInfo &arc = *g_Arcs[formatIndex];
+    if (needIn)
+    {
+      *outObject = arc.CreateInArchive();
+      ((IInArchive *)*outObject)->AddRef();
+    }
+    else
+    {
+      if (!arc.CreateOutArchive)
+        return CLASS_E_CLASSNOTAVAILABLE;
+      *outObject = arc.CreateOutArchive();
+      ((IOutArchive *)*outObject)->AddRef();
+    }
+  }
+  COM_TRY_END
+  return S_OK;
+}
+
+STDAPI GetHandlerProperty2(UInt32 formatIndex, PROPID propID, PROPVARIANT *value)
+{
+  if (formatIndex >= g_NumArcs)
+    return E_INVALIDARG;
+  const CArcInfo &arc = *g_Arcs[formatIndex];
+  NWindows::NCOM::CPropVariant prop;
+  switch(propID)
+  {
+    case NArchive::kName:
+      prop = arc.Name;
+      break;
+    case NArchive::kClassID:
+    {
+      GUID clsId = CLSID_CArchiveHandler;
+      CLS_ARC_ID_ITEM(clsId) = arc.ClassId;
+      return SetPropGUID(clsId, value);
+    }
+    case NArchive::kExtension:
+      if (arc.Ext != 0)
+        prop = arc.Ext;
+      break;
+    case NArchive::kAddExtension:
+      if (arc.AddExt != 0)
+        prop = arc.AddExt;
+      break;
+    case NArchive::kUpdate:
+      prop = (bool)(arc.CreateOutArchive != 0);
+      break;
+    case NArchive::kKeepName:
+      prop = arc.KeepName;
+      break;
+    case NArchive::kStartSignature:
+      return SetPropString((const char *)arc.Signature, arc.SignatureSize, value);
+  }
+  prop.Detach(value);
+  return S_OK;
+}
+
+STDAPI GetHandlerProperty(PROPID propID, PROPVARIANT *value)
+{
+  return GetHandlerProperty2(0, propID, value);
+}
+
+STDAPI GetNumberOfFormats(UINT32 *numFormats)
+{
+  *numFormats = g_NumArcs;
+  return S_OK;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CoderMixer2.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CoderMixer2.cpp
new file mode 100644
index 0000000..aed94f9
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CoderMixer2.cpp
@@ -0,0 +1,121 @@
+// CoderMixer2.cpp
+
+#include "StdAfx.h"
+
+#include "CoderMixer2.h"
+
+namespace NCoderMixer {
+
+CBindReverseConverter::CBindReverseConverter(const CBindInfo &srcBindInfo):
+  _srcBindInfo(srcBindInfo)
+{
+  srcBindInfo.GetNumStreams(NumSrcInStreams, _numSrcOutStreams);
+
+  UInt32  j;
+  for (j = 0; j < NumSrcInStreams; j++)
+  {
+    _srcInToDestOutMap.Add(0);
+    DestOutToSrcInMap.Add(0);
+  }
+  for (j = 0; j < _numSrcOutStreams; j++)
+  {
+    _srcOutToDestInMap.Add(0);
+    _destInToSrcOutMap.Add(0);
+  }
+
+  UInt32 destInOffset = 0;
+  UInt32 destOutOffset = 0;
+  UInt32 srcInOffset = NumSrcInStreams;
+  UInt32 srcOutOffset = _numSrcOutStreams;
+
+  for (int i = srcBindInfo.Coders.Size() - 1; i >= 0; i--)
+  {
+    const CCoderStreamsInfo &srcCoderInfo = srcBindInfo.Coders[i];
+
+    srcInOffset -= srcCoderInfo.NumInStreams;
+    srcOutOffset -= srcCoderInfo.NumOutStreams;
+    
+    UInt32 j;
+    for (j = 0; j < srcCoderInfo.NumInStreams; j++, destOutOffset++)
+    {
+      UInt32 index = srcInOffset + j;
+      _srcInToDestOutMap[index] = destOutOffset;
+      DestOutToSrcInMap[destOutOffset] = index;
+    }
+    for (j = 0; j < srcCoderInfo.NumOutStreams; j++, destInOffset++)
+    {
+      UInt32 index = srcOutOffset + j;
+      _srcOutToDestInMap[index] = destInOffset;
+      _destInToSrcOutMap[destInOffset] = index;
+    }
+  }
+}
+
+void CBindReverseConverter::CreateReverseBindInfo(CBindInfo &destBindInfo)
+{
+  destBindInfo.Coders.Clear();
+  destBindInfo.BindPairs.Clear();
+  destBindInfo.InStreams.Clear();
+  destBindInfo.OutStreams.Clear();
+
+  int i;
+  for (i = _srcBindInfo.Coders.Size() - 1; i >= 0; i--)
+  {
+    const CCoderStreamsInfo &srcCoderInfo = _srcBindInfo.Coders[i];
+    CCoderStreamsInfo destCoderInfo;
+    destCoderInfo.NumInStreams = srcCoderInfo.NumOutStreams;
+    destCoderInfo.NumOutStreams = srcCoderInfo.NumInStreams;
+    destBindInfo.Coders.Add(destCoderInfo);
+  }
+  for (i = _srcBindInfo.BindPairs.Size() - 1; i >= 0; i--)
+  {
+    const CBindPair &srcBindPair = _srcBindInfo.BindPairs[i];
+    CBindPair destBindPair;
+    destBindPair.InIndex = _srcOutToDestInMap[srcBindPair.OutIndex];
+    destBindPair.OutIndex = _srcInToDestOutMap[srcBindPair.InIndex];
+    destBindInfo.BindPairs.Add(destBindPair);
+  }
+  for (i = 0; i < _srcBindInfo.InStreams.Size(); i++)
+    destBindInfo.OutStreams.Add(_srcInToDestOutMap[_srcBindInfo.InStreams[i]]);
+  for (i = 0; i < _srcBindInfo.OutStreams.Size(); i++)
+    destBindInfo.InStreams.Add(_srcOutToDestInMap[_srcBindInfo.OutStreams[i]]);
+}
+
+CCoderInfo2::CCoderInfo2(UInt32 numInStreams, UInt32 numOutStreams):
+    NumInStreams(numInStreams),
+    NumOutStreams(numOutStreams)
+{
+  InSizes.Reserve(NumInStreams);
+  InSizePointers.Reserve(NumInStreams);
+  OutSizePointers.Reserve(NumOutStreams);
+  OutSizePointers.Reserve(NumOutStreams);
+}
+
+static void SetSizes(const UInt64 **srcSizes, CRecordVector<UInt64> &sizes,
+    CRecordVector<const UInt64 *> &sizePointers, UInt32 numItems)
+{
+  sizes.Clear();
+  sizePointers.Clear();
+  for(UInt32 i = 0; i < numItems; i++)
+  {
+    if (srcSizes == 0 || srcSizes[i] == NULL)
+    {
+      sizes.Add(0);
+      sizePointers.Add(NULL);
+    }
+    else
+    {
+      sizes.Add(*srcSizes[i]);
+      sizePointers.Add(&sizes.Back());
+    }
+  }
+}
+
+void CCoderInfo2::SetCoderInfo(const UInt64 **inSizes,
+      const UInt64 **outSizes)
+{
+  SetSizes(inSizes, InSizes, InSizePointers, NumInStreams);
+  SetSizes(outSizes, OutSizes, OutSizePointers, NumOutStreams);
+}
+
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CoderMixer2.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CoderMixer2.h
new file mode 100644
index 0000000..a03722d
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CoderMixer2.h
@@ -0,0 +1,174 @@
+// CoderMixer2.h
+
+#ifndef __CODER_MIXER2_H
+#define __CODER_MIXER2_H
+
+#include "../../../Common/MyVector.h"
+#include "../../../Common/Types.h"
+#include "../../../Common/MyCom.h"
+#include "../../ICoder.h"
+
+namespace NCoderMixer {
+
+struct CBindPair
+{
+  UInt32 InIndex;
+  UInt32 OutIndex;
+};
+
+struct CCoderStreamsInfo
+{
+  UInt32 NumInStreams;
+  UInt32 NumOutStreams;
+};
+
+struct CBindInfo
+{
+  CRecordVector<CCoderStreamsInfo> Coders;
+  CRecordVector<CBindPair> BindPairs;
+  CRecordVector<UInt32> InStreams;
+  CRecordVector<UInt32> OutStreams;
+
+  void Clear()
+  {
+    Coders.Clear();
+    BindPairs.Clear();
+    InStreams.Clear();
+    OutStreams.Clear();
+  }
+
+  /*
+  UInt32 GetCoderStartOutStream(UInt32 coderIndex) const
+  {
+    UInt32 numOutStreams = 0;
+    for (UInt32 i = 0; i < coderIndex; i++)
+      numOutStreams += Coders[i].NumOutStreams;
+    return numOutStreams;
+  }
+  */
+
+
+  void GetNumStreams(UInt32 &numInStreams, UInt32 &numOutStreams) const
+  {
+    numInStreams = 0;
+    numOutStreams = 0;
+    for (int i = 0; i < Coders.Size(); i++)
+    {
+      const CCoderStreamsInfo &coderStreamsInfo = Coders[i];
+      numInStreams += coderStreamsInfo.NumInStreams;
+      numOutStreams += coderStreamsInfo.NumOutStreams;
+    }
+  }
+
+  int FindBinderForInStream(UInt32 inStream) const
+  {
+    for (int i = 0; i < BindPairs.Size(); i++)
+      if (BindPairs[i].InIndex == inStream)
+        return i;
+    return -1;
+  }
+  int FindBinderForOutStream(UInt32 outStream) const
+  {
+    for (int i = 0; i < BindPairs.Size(); i++)
+      if (BindPairs[i].OutIndex == outStream)
+        return i;
+    return -1;
+  }
+
+  UInt32 GetCoderInStreamIndex(UInt32 coderIndex) const
+  {
+    UInt32 streamIndex = 0;
+    for (UInt32 i = 0; i < coderIndex; i++)
+      streamIndex += Coders[i].NumInStreams;
+    return streamIndex;
+  }
+
+  UInt32 GetCoderOutStreamIndex(UInt32 coderIndex) const
+  {
+    UInt32 streamIndex = 0;
+    for (UInt32 i = 0; i < coderIndex; i++)
+      streamIndex += Coders[i].NumOutStreams;
+    return streamIndex;
+  }
+
+
+  void FindInStream(UInt32 streamIndex, UInt32 &coderIndex,
+      UInt32 &coderStreamIndex) const
+  {
+    for (coderIndex = 0; coderIndex < (UInt32)Coders.Size(); coderIndex++)
+    {
+      UInt32 curSize = Coders[coderIndex].NumInStreams;
+      if (streamIndex < curSize)
+      {
+        coderStreamIndex = streamIndex;
+        return;
+      }
+      streamIndex -= curSize;
+    }
+    throw 1;
+  }
+  void FindOutStream(UInt32 streamIndex, UInt32 &coderIndex,
+      UInt32 &coderStreamIndex) const
+  {
+    for (coderIndex = 0; coderIndex < (UInt32)Coders.Size(); coderIndex++)
+    {
+      UInt32 curSize = Coders[coderIndex].NumOutStreams;
+      if (streamIndex < curSize)
+      {
+        coderStreamIndex = streamIndex;
+        return;
+      }
+      streamIndex -= curSize;
+    }
+    throw 1;
+  }
+};
+
+class CBindReverseConverter
+{
+  UInt32 _numSrcOutStreams;
+  NCoderMixer::CBindInfo _srcBindInfo;
+  CRecordVector<UInt32> _srcInToDestOutMap;
+  CRecordVector<UInt32> _srcOutToDestInMap;
+  CRecordVector<UInt32> _destInToSrcOutMap;
+public:
+  UInt32 NumSrcInStreams;
+  CRecordVector<UInt32> DestOutToSrcInMap;
+
+  CBindReverseConverter(const NCoderMixer::CBindInfo &srcBindInfo);
+  void CreateReverseBindInfo(NCoderMixer::CBindInfo &destBindInfo);
+};
+
+struct CCoderInfo2
+{
+  CMyComPtr<ICompressCoder> Coder;
+  CMyComPtr<ICompressCoder2> Coder2;
+  UInt32 NumInStreams;
+  UInt32 NumOutStreams;
+
+  CRecordVector<UInt64> InSizes;
+  CRecordVector<UInt64> OutSizes;
+  CRecordVector<const UInt64 *> InSizePointers;
+  CRecordVector<const UInt64 *> OutSizePointers;
+
+  CCoderInfo2(UInt32 numInStreams, UInt32 numOutStreams);
+  void SetCoderInfo(const UInt64 **inSizes, const UInt64 **outSizes);
+
+  HRESULT QueryInterface(REFGUID iid, void** pp) const
+  {
+    IUnknown *p = Coder ? (IUnknown *)Coder : (IUnknown *)Coder2;
+    return p->QueryInterface(iid, pp);
+  }
+};
+
+class CCoderMixer2
+{
+public:
+  virtual HRESULT SetBindInfo(const CBindInfo &bindInfo) = 0;
+  virtual void ReInit() = 0;
+  virtual void SetCoderInfo(UInt32 coderIndex, const UInt64 **inSizes, const UInt64 **outSizes) = 0;
+};
+
+}
+#endif
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CoderMixer2MT.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CoderMixer2MT.cpp
new file mode 100644
index 0000000..1265dfc
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CoderMixer2MT.cpp
@@ -0,0 +1,230 @@
+// CoderMixer2MT.cpp
+
+#include "StdAfx.h"
+
+#include "CoderMixer2MT.h"
+
+namespace NCoderMixer {
+
+CCoder2::CCoder2(UInt32 numInStreams, UInt32 numOutStreams):
+    CCoderInfo2(numInStreams, numOutStreams)
+{
+  InStreams.Reserve(NumInStreams);
+  InStreamPointers.Reserve(NumInStreams);
+  OutStreams.Reserve(NumOutStreams);
+  OutStreamPointers.Reserve(NumOutStreams);
+}
+
+void CCoder2::Execute() { Code(NULL); }
+
+void CCoder2::Code(ICompressProgressInfo *progress)
+{
+  InStreamPointers.Clear();
+  OutStreamPointers.Clear();
+  UInt32 i;
+  for (i = 0; i < NumInStreams; i++)
+  {
+    if (InSizePointers[i] != NULL)
+      InSizePointers[i] = &InSizes[i];
+    InStreamPointers.Add((ISequentialInStream *)InStreams[i]);
+  }
+  for (i = 0; i < NumOutStreams; i++)
+  {
+    if (OutSizePointers[i] != NULL)
+      OutSizePointers[i] = &OutSizes[i];
+    OutStreamPointers.Add((ISequentialOutStream *)OutStreams[i]);
+  }
+  if (Coder)
+    Result = Coder->Code(InStreamPointers[0], OutStreamPointers[0],
+        InSizePointers[0], OutSizePointers[0], progress);
+  else
+    Result = Coder2->Code(&InStreamPointers.Front(), &InSizePointers.Front(), NumInStreams,
+      &OutStreamPointers.Front(), &OutSizePointers.Front(), NumOutStreams, progress);
+  {
+    int i;
+    for (i = 0; i < InStreams.Size(); i++)
+      InStreams[i].Release();
+    for (i = 0; i < OutStreams.Size(); i++)
+      OutStreams[i].Release();
+  }
+}
+
+static void SetSizes(const UInt64 **srcSizes, CRecordVector<UInt64> &sizes,
+    CRecordVector<const UInt64 *> &sizePointers, UInt32 numItems)
+{
+  sizes.Clear();
+  sizePointers.Clear();
+  for(UInt32 i = 0; i < numItems; i++)
+  {
+    if (srcSizes == 0 || srcSizes[i] == NULL)
+    {
+      sizes.Add(0);
+      sizePointers.Add(NULL);
+    }
+    else
+    {
+      sizes.Add(*srcSizes[i]);
+      sizePointers.Add(&sizes.Back());
+    }
+  }
+}
+
+
+void CCoder2::SetCoderInfo(const UInt64 **inSizes, const UInt64 **outSizes)
+{
+  SetSizes(inSizes, InSizes, InSizePointers, NumInStreams);
+  SetSizes(outSizes, OutSizes, OutSizePointers, NumOutStreams);
+}
+
+//////////////////////////////////////
+// CCoderMixer2MT
+
+HRESULT CCoderMixer2MT::SetBindInfo(const CBindInfo &bindInfo)
+{
+  _bindInfo = bindInfo;
+  _streamBinders.Clear();
+  for(int i = 0; i < _bindInfo.BindPairs.Size(); i++)
+  {
+    _streamBinders.Add(CStreamBinder());
+    RINOK(_streamBinders.Back().CreateEvents());
+  }
+  return S_OK;
+}
+
+void CCoderMixer2MT::AddCoderCommon()
+{
+  const CCoderStreamsInfo &c = _bindInfo.Coders[_coders.Size()];
+  CCoder2 threadCoderInfo(c.NumInStreams, c.NumOutStreams);
+  _coders.Add(threadCoderInfo);
+}
+
+void CCoderMixer2MT::AddCoder(ICompressCoder *coder)
+{
+  AddCoderCommon();
+  _coders.Back().Coder = coder;
+}
+
+void CCoderMixer2MT::AddCoder2(ICompressCoder2 *coder)
+{
+  AddCoderCommon();
+  _coders.Back().Coder2 = coder;
+}
+
+
+void CCoderMixer2MT::ReInit()
+{
+  for(int i = 0; i < _streamBinders.Size(); i++)
+    _streamBinders[i].ReInit();
+}
+
+
+HRESULT CCoderMixer2MT::Init(ISequentialInStream **inStreams, ISequentialOutStream **outStreams)
+{
+  /*
+  if (_coders.Size() != _bindInfo.Coders.Size())
+    throw 0;
+  */
+  int i;
+  for(i = 0; i < _coders.Size(); i++)
+  {
+    CCoder2 &coderInfo = _coders[i];
+    const CCoderStreamsInfo &coderStreamsInfo = _bindInfo.Coders[i];
+    coderInfo.InStreams.Clear();
+    UInt32 j;
+    for(j = 0; j < coderStreamsInfo.NumInStreams; j++)
+      coderInfo.InStreams.Add(NULL);
+    coderInfo.OutStreams.Clear();
+    for(j = 0; j < coderStreamsInfo.NumOutStreams; j++)
+      coderInfo.OutStreams.Add(NULL);
+  }
+
+  for(i = 0; i < _bindInfo.BindPairs.Size(); i++)
+  {
+    const CBindPair &bindPair = _bindInfo.BindPairs[i];
+    UInt32 inCoderIndex, inCoderStreamIndex;
+    UInt32 outCoderIndex, outCoderStreamIndex;
+    _bindInfo.FindInStream(bindPair.InIndex, inCoderIndex, inCoderStreamIndex);
+    _bindInfo.FindOutStream(bindPair.OutIndex, outCoderIndex, outCoderStreamIndex);
+
+    _streamBinders[i].CreateStreams(
+        &_coders[inCoderIndex].InStreams[inCoderStreamIndex],
+        &_coders[outCoderIndex].OutStreams[outCoderStreamIndex]);
+  }
+
+  for(i = 0; i < _bindInfo.InStreams.Size(); i++)
+  {
+    UInt32 inCoderIndex, inCoderStreamIndex;
+    _bindInfo.FindInStream(_bindInfo.InStreams[i], inCoderIndex, inCoderStreamIndex);
+    _coders[inCoderIndex].InStreams[inCoderStreamIndex] = inStreams[i];
+  }
+  
+  for(i = 0; i < _bindInfo.OutStreams.Size(); i++)
+  {
+    UInt32 outCoderIndex, outCoderStreamIndex;
+    _bindInfo.FindOutStream(_bindInfo.OutStreams[i], outCoderIndex, outCoderStreamIndex);
+    _coders[outCoderIndex].OutStreams[outCoderStreamIndex] = outStreams[i];
+  }
+  return S_OK;
+}
+
+HRESULT CCoderMixer2MT::ReturnIfError(HRESULT code)
+{
+  for (int i = 0; i < _coders.Size(); i++)
+    if (_coders[i].Result == code)
+      return code;
+  return S_OK;
+}
+
+STDMETHODIMP CCoderMixer2MT::Code(ISequentialInStream **inStreams,
+      const UInt64 ** /* inSizes */,
+      UInt32 numInStreams,
+      ISequentialOutStream **outStreams,
+      const UInt64 ** /* outSizes */,
+      UInt32 numOutStreams,
+      ICompressProgressInfo *progress)
+{
+  if (numInStreams != (UInt32)_bindInfo.InStreams.Size() ||
+      numOutStreams != (UInt32)_bindInfo.OutStreams.Size())
+    return E_INVALIDARG;
+
+  Init(inStreams, outStreams);
+
+  int i;
+  for (i = 0; i < _coders.Size(); i++)
+    if (i != _progressCoderIndex)
+    {
+      RINOK(_coders[i].Create());
+    }
+
+  for (i = 0; i < _coders.Size(); i++)
+    if (i != _progressCoderIndex)
+      _coders[i].Start();
+
+  _coders[_progressCoderIndex].Code(progress);
+
+  for (i = 0; i < _coders.Size(); i++)
+    if (i != _progressCoderIndex)
+      _coders[i].WaitFinish();
+
+  RINOK(ReturnIfError(E_ABORT));
+  RINOK(ReturnIfError(E_OUTOFMEMORY));
+
+  for (i = 0; i < _coders.Size(); i++)
+  {
+    HRESULT result = _coders[i].Result;
+    if (result != S_OK && result != E_FAIL && result != S_FALSE)
+      return result;
+  }
+
+  RINOK(ReturnIfError(S_FALSE));
+
+  for (i = 0; i < _coders.Size(); i++)
+  {
+    HRESULT result = _coders[i].Result;
+    if (result != S_OK)
+      return result;
+  }
+  return S_OK;
+}
+
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CoderMixer2MT.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CoderMixer2MT.h
new file mode 100644
index 0000000..d1c7f4d
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CoderMixer2MT.h
@@ -0,0 +1,80 @@
+// CoderMixer2MT.h
+
+#ifndef __CODER_MIXER2_MT_H
+#define __CODER_MIXER2_MT_H
+
+#include "CoderMixer2.h"
+#include "../../../Common/MyCom.h"
+#include "../../Common/StreamBinder.h"
+#include "../../Common/VirtThread.h"
+
+namespace NCoderMixer {
+
+struct CCoder2: public CCoderInfo2, public CVirtThread
+{
+  HRESULT Result;
+  CObjectVector< CMyComPtr<ISequentialInStream> > InStreams;
+  CObjectVector< CMyComPtr<ISequentialOutStream> > OutStreams;
+  CRecordVector<ISequentialInStream*> InStreamPointers;
+  CRecordVector<ISequentialOutStream*> OutStreamPointers;
+
+  CCoder2(UInt32 numInStreams, UInt32 numOutStreams);
+  void SetCoderInfo(const UInt64 **inSizes, const UInt64 **outSizes);
+  virtual void Execute();
+  void Code(ICompressProgressInfo *progress);
+};
+
+
+/*
+  SetBindInfo()
+  for each coder
+    AddCoder[2]()
+  SetProgressIndex(UInt32 coderIndex);
+ 
+  for each file
+  {
+    ReInit()
+    for each coder
+      SetCoderInfo
+    Code
+  }
+*/
+
+class CCoderMixer2MT:
+  public ICompressCoder2,
+  public CCoderMixer2,
+  public CMyUnknownImp
+{
+  CBindInfo _bindInfo;
+  CObjectVector<CStreamBinder> _streamBinders;
+  int _progressCoderIndex;
+
+  void AddCoderCommon();
+  HRESULT Init(ISequentialInStream **inStreams, ISequentialOutStream **outStreams);
+  HRESULT ReturnIfError(HRESULT code);
+public:
+  CObjectVector<CCoder2> _coders;
+  MY_UNKNOWN_IMP
+
+  STDMETHOD(Code)(ISequentialInStream **inStreams,
+      const UInt64 **inSizes,
+      UInt32 numInStreams,
+      ISequentialOutStream **outStreams,
+      const UInt64 **outSizes,
+      UInt32 numOutStreams,
+      ICompressProgressInfo *progress);
+
+  HRESULT SetBindInfo(const CBindInfo &bindInfo);
+  void AddCoder(ICompressCoder *coder);
+  void AddCoder2(ICompressCoder2 *coder);
+  void SetProgressCoderIndex(int coderIndex) {  _progressCoderIndex = coderIndex; }
+
+  void ReInit();
+  void SetCoderInfo(UInt32 coderIndex, const UInt64 **inSizes, const UInt64 **outSizes)
+    {  _coders[coderIndex].SetCoderInfo(inSizes, outSizes); }
+  UInt64 GetWriteProcessedSize(UInt32 binderIndex) const
+    {  return _streamBinders[binderIndex].ProcessedSize; }
+};
+
+}
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CrossThreadProgress.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CrossThreadProgress.cpp
new file mode 100644
index 0000000..a974b54
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CrossThreadProgress.cpp
@@ -0,0 +1,15 @@
+// CrossThreadProgress.cpp
+
+#include "StdAfx.h"
+
+#include "CrossThreadProgress.h"
+
+STDMETHODIMP CCrossThreadProgress::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
+{
+  InSize = inSize;
+  OutSize = outSize;
+  ProgressEvent.Set();
+  WaitEvent.Lock();
+  return Result;
+}
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CrossThreadProgress.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CrossThreadProgress.h
new file mode 100644
index 0000000..7e0b105
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/CrossThreadProgress.h
@@ -0,0 +1,37 @@
+// CrossThreadProgress.h
+
+#ifndef __CROSSTHREADPROGRESS_H
+#define __CROSSTHREADPROGRESS_H
+
+#include "../../ICoder.h"
+#include "../../../Windows/Synchronization.h"
+#include "../../../Common/MyCom.h"
+
+class CCrossThreadProgress:
+  public ICompressProgressInfo,
+  public CMyUnknownImp
+{
+public:
+  const UInt64 *InSize;
+  const UInt64 *OutSize;
+  HRESULT Result;
+  NWindows::NSynchronization::CAutoResetEvent ProgressEvent;
+  NWindows::NSynchronization::CAutoResetEvent WaitEvent;
+
+  HRes Create()
+  {
+    RINOK(ProgressEvent.CreateIfNotCreated());
+    return WaitEvent.CreateIfNotCreated();
+  }
+  void Init()
+  {
+    ProgressEvent.Reset();
+    WaitEvent.Reset();
+  }
+
+  MY_UNKNOWN_IMP
+
+  STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/DummyOutStream.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/DummyOutStream.cpp
new file mode 100644
index 0000000..54bcfec
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/DummyOutStream.cpp
@@ -0,0 +1,22 @@
+// DummyOutStream.cpp
+
+#include "StdAfx.h"
+
+#include "DummyOutStream.h"
+
+STDMETHODIMP CDummyOutStream::Write(const void *data,  UInt32 size, UInt32 *processedSize)
+{
+  UInt32 realProcessedSize;
+  HRESULT result;
+  if(!_stream)
+  {
+    realProcessedSize = size;
+    result = S_OK;
+  }
+  else
+    result = _stream->Write(data, size, &realProcessedSize);
+  _size += realProcessedSize;
+  if(processedSize != NULL)
+    *processedSize = realProcessedSize;
+  return result;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/DummyOutStream.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/DummyOutStream.h
new file mode 100644
index 0000000..13d5b62
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/DummyOutStream.h
@@ -0,0 +1,24 @@
+// DummyOutStream.h
+
+#ifndef __DUMMYOUTSTREAM_H
+#define __DUMMYOUTSTREAM_H
+
+#include "../../IStream.h"
+#include "Common/MyCom.h"
+
+class CDummyOutStream:
+  public ISequentialOutStream,
+  public CMyUnknownImp
+{
+  CMyComPtr<ISequentialOutStream> _stream;
+  UInt64 _size;
+public:
+  void SetStream(ISequentialOutStream *outStream) { _stream = outStream; }
+  void ReleaseStream() { _stream.Release(); }
+  void Init() { _size = 0; }
+  MY_UNKNOWN_IMP
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+  UInt64 GetSize() const { return _size; }
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/HandlerOut.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/HandlerOut.cpp
new file mode 100644
index 0000000..76739e9
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/HandlerOut.cpp
@@ -0,0 +1,620 @@
+// HandlerOut.cpp
+
+#include "StdAfx.h"
+
+#include "../../../Common/StringToInt.h"
+
+#include "../../../Windows/PropVariant.h"
+
+#ifdef COMPRESS_MT
+#include "../../../Windows/System.h"
+#endif
+
+#include "../../ICoder.h"
+
+#include "../Common/ParseProperties.h"
+
+#include "HandlerOut.h"
+
+using namespace NWindows;
+
+namespace NArchive {
+
+static const wchar_t *kCopyMethod = L"Copy";
+static const wchar_t *kLZMAMethodName = L"LZMA";
+static const wchar_t *kLZMA2MethodName = L"LZMA2";
+static const wchar_t *kBZip2MethodName = L"BZip2";
+static const wchar_t *kPpmdMethodName = L"PPMd";
+static const wchar_t *kDeflateMethodName = L"Deflate";
+static const wchar_t *kDeflate64MethodName = L"Deflate64";
+
+static const wchar_t *kLzmaMatchFinderX1 = L"HC4";
+static const wchar_t *kLzmaMatchFinderX5 = L"BT4";
+
+static const UInt32 kLzmaAlgoX1 = 0;
+static const UInt32 kLzmaAlgoX5 = 1;
+
+static const UInt32 kLzmaDicSizeX1 = 1 << 16;
+static const UInt32 kLzmaDicSizeX3 = 1 << 20;
+static const UInt32 kLzmaDicSizeX5 = 1 << 24;
+static const UInt32 kLzmaDicSizeX7 = 1 << 25;
+static const UInt32 kLzmaDicSizeX9 = 1 << 26;
+
+static const UInt32 kLzmaFastBytesX1 = 32;
+static const UInt32 kLzmaFastBytesX7 = 64;
+
+static const UInt32 kPpmdMemSizeX1 = (1 << 22);
+static const UInt32 kPpmdMemSizeX5 = (1 << 24);
+static const UInt32 kPpmdMemSizeX7 = (1 << 26);
+static const UInt32 kPpmdMemSizeX9 = (192 << 20);
+
+static const UInt32 kPpmdOrderX1 = 4;
+static const UInt32 kPpmdOrderX5 = 6;
+static const UInt32 kPpmdOrderX7 = 16;
+static const UInt32 kPpmdOrderX9 = 32;
+
+static const UInt32 kDeflateAlgoX1 = 0;
+static const UInt32 kDeflateAlgoX5 = 1;
+
+static const UInt32 kDeflateFastBytesX1 = 32;
+static const UInt32 kDeflateFastBytesX7 = 64;
+static const UInt32 kDeflateFastBytesX9 = 128;
+
+static const UInt32 kDeflatePassesX1 = 1;
+static const UInt32 kDeflatePassesX7 = 3;
+static const UInt32 kDeflatePassesX9 = 10;
+
+static const UInt32 kBZip2NumPassesX1 = 1;
+static const UInt32 kBZip2NumPassesX7 = 2;
+static const UInt32 kBZip2NumPassesX9 = 7;
+
+static const UInt32 kBZip2DicSizeX1 = 100000;
+static const UInt32 kBZip2DicSizeX3 = 500000;
+static const UInt32 kBZip2DicSizeX5 = 900000;
+
+static const wchar_t *kDefaultMethodName = kLZMAMethodName;
+
+static const wchar_t *kLzmaMatchFinderForHeaders = L"BT2";
+static const UInt32 kDictionaryForHeaders = 1 << 20;
+static const UInt32 kNumFastBytesForHeaders = 273;
+static const UInt32 kAlgorithmForHeaders = kLzmaAlgoX5;
+
+static bool AreEqual(const UString &methodName, const wchar_t *s)
+  { return (methodName.CompareNoCase(s) == 0); }
+
+static inline bool IsLZMAMethod(const UString &methodName)
+{
+  return
+    AreEqual(methodName, kLZMAMethodName) ||
+    AreEqual(methodName, kLZMA2MethodName);
+}
+
+static inline bool IsBZip2Method(const UString &methodName)
+  { return AreEqual(methodName, kBZip2MethodName); }
+
+static inline bool IsPpmdMethod(const UString &methodName)
+  { return AreEqual(methodName, kPpmdMethodName); }
+
+static inline bool IsDeflateMethod(const UString &methodName)
+{
+  return
+    AreEqual(methodName, kDeflateMethodName) ||
+    AreEqual(methodName, kDeflate64MethodName);
+}
+
+struct CNameToPropID
+{
+  PROPID PropID;
+  VARTYPE VarType;
+  const wchar_t *Name;
+};
+
+CNameToPropID g_NameToPropID[] =
+{
+  { NCoderPropID::kOrder, VT_UI4, L"O" },
+  { NCoderPropID::kPosStateBits, VT_UI4, L"PB" },
+  { NCoderPropID::kLitContextBits, VT_UI4, L"LC" },
+  { NCoderPropID::kLitPosBits, VT_UI4, L"LP" },
+  { NCoderPropID::kEndMarker, VT_BOOL, L"eos" },
+
+  { NCoderPropID::kNumPasses, VT_UI4, L"Pass" },
+  { NCoderPropID::kNumFastBytes, VT_UI4, L"fb" },
+  { NCoderPropID::kMatchFinderCycles, VT_UI4, L"mc" },
+  { NCoderPropID::kAlgorithm, VT_UI4, L"a" },
+  { NCoderPropID::kMatchFinder, VT_BSTR, L"mf" },
+  { NCoderPropID::kNumThreads, VT_UI4, L"mt" }
+};
+
+static bool ConvertProperty(PROPVARIANT srcProp, VARTYPE varType, NCOM::CPropVariant &destProp)
+{
+  if (varType == srcProp.vt)
+  {
+    destProp = srcProp;
+    return true;
+  }
+  if (varType == VT_UI1)
+  {
+    if (srcProp.vt == VT_UI4)
+    {
+      UInt32 value = srcProp.ulVal;
+      if (value > 0xFF)
+        return false;
+      destProp = (Byte)value;
+      return true;
+    }
+  }
+  else if (varType == VT_BOOL)
+  {
+    bool res;
+    if (SetBoolProperty(res, srcProp) != S_OK)
+      return false;
+    destProp = res;
+    return true;
+  }
+  return false;
+}
+    
+static int FindPropIdFromStringName(const UString &name)
+{
+  for (int i = 0; i < sizeof(g_NameToPropID) / sizeof(g_NameToPropID[0]); i++)
+    if (name.CompareNoCase(g_NameToPropID[i].Name) == 0)
+      return i;
+  return -1;
+}
+
+static void SetOneMethodProp(COneMethodInfo &oneMethodInfo, PROPID propID,
+    const NWindows::NCOM::CPropVariant &value)
+{
+  for (int j = 0; j < oneMethodInfo.Props.Size(); j++)
+    if (oneMethodInfo.Props[j].Id == propID)
+      return;
+  CProp prop;
+  prop.Id = propID;
+  prop.Value = value;
+  oneMethodInfo.Props.Add(prop);
+}
+
+void COutHandler::SetCompressionMethod2(COneMethodInfo &oneMethodInfo
+    #ifdef COMPRESS_MT
+    , UInt32 numThreads
+    #endif
+    )
+{
+  UInt32 level = _level;
+  if (oneMethodInfo.MethodName.IsEmpty())
+    oneMethodInfo.MethodName = kDefaultMethodName;
+  
+  if (IsLZMAMethod(oneMethodInfo.MethodName))
+  {
+    UInt32 dicSize =
+      (level >= 9 ? kLzmaDicSizeX9 :
+      (level >= 7 ? kLzmaDicSizeX7 :
+      (level >= 5 ? kLzmaDicSizeX5 :
+      (level >= 3 ? kLzmaDicSizeX3 :
+                    kLzmaDicSizeX1))));
+    
+    UInt32 algo =
+      (level >= 5 ? kLzmaAlgoX5 :
+                    kLzmaAlgoX1);
+    
+    UInt32 fastBytes =
+      (level >= 7 ? kLzmaFastBytesX7 :
+                    kLzmaFastBytesX1);
+    
+    const wchar_t *matchFinder =
+      (level >= 5 ? kLzmaMatchFinderX5 :
+                    kLzmaMatchFinderX1);
+    
+    SetOneMethodProp(oneMethodInfo, NCoderPropID::kDictionarySize, dicSize);
+    SetOneMethodProp(oneMethodInfo, NCoderPropID::kAlgorithm, algo);
+    SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumFastBytes, fastBytes);
+    SetOneMethodProp(oneMethodInfo, NCoderPropID::kMatchFinder, matchFinder);
+    #ifdef COMPRESS_MT
+    SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumThreads, numThreads);
+    #endif
+  }
+  else if (IsDeflateMethod(oneMethodInfo.MethodName))
+  {
+    UInt32 fastBytes =
+      (level >= 9 ? kDeflateFastBytesX9 :
+      (level >= 7 ? kDeflateFastBytesX7 :
+                    kDeflateFastBytesX1));
+    
+    UInt32 numPasses =
+      (level >= 9 ? kDeflatePassesX9 :
+      (level >= 7 ? kDeflatePassesX7 :
+                    kDeflatePassesX1));
+    
+    UInt32 algo =
+      (level >= 5 ? kDeflateAlgoX5 :
+                    kDeflateAlgoX1);
+    
+    SetOneMethodProp(oneMethodInfo, NCoderPropID::kAlgorithm, algo);
+    SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumFastBytes, fastBytes);
+    SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumPasses, numPasses);
+  }
+  else if (IsBZip2Method(oneMethodInfo.MethodName))
+  {
+    UInt32 numPasses =
+      (level >= 9 ? kBZip2NumPassesX9 :
+      (level >= 7 ? kBZip2NumPassesX7 :
+                    kBZip2NumPassesX1));
+    
+    UInt32 dicSize =
+      (level >= 5 ? kBZip2DicSizeX5 :
+      (level >= 3 ? kBZip2DicSizeX3 :
+                    kBZip2DicSizeX1));
+    
+    SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumPasses, numPasses);
+    SetOneMethodProp(oneMethodInfo, NCoderPropID::kDictionarySize, dicSize);
+    #ifdef COMPRESS_MT
+    SetOneMethodProp(oneMethodInfo, NCoderPropID::kNumThreads, numThreads);
+    #endif
+  }
+  else if (IsPpmdMethod(oneMethodInfo.MethodName))
+  {
+    UInt32 useMemSize =
+      (level >= 9 ? kPpmdMemSizeX9 :
+      (level >= 7 ? kPpmdMemSizeX7 :
+      (level >= 5 ? kPpmdMemSizeX5 :
+                    kPpmdMemSizeX1)));
+    
+    UInt32 order =
+      (level >= 9 ? kPpmdOrderX9 :
+      (level >= 7 ? kPpmdOrderX7 :
+      (level >= 5 ? kPpmdOrderX5 :
+                    kPpmdOrderX1)));
+    
+    SetOneMethodProp(oneMethodInfo, NCoderPropID::kUsedMemorySize, useMemSize);
+    SetOneMethodProp(oneMethodInfo, NCoderPropID::kOrder, order);
+  }
+}
+
+static void SplitParams(const UString &srcString, UStringVector &subStrings)
+{
+  subStrings.Clear();
+  UString name;
+  int len = srcString.Length();
+  if (len == 0)
+    return;
+  for (int i = 0; i < len; i++)
+  {
+    wchar_t c = srcString[i];
+    if (c == L':')
+    {
+      subStrings.Add(name);
+      name.Empty();
+    }
+    else
+      name += c;
+  }
+  subStrings.Add(name);
+}
+
+static void SplitParam(const UString &param, UString &name, UString &value)
+{
+  int eqPos = param.Find(L'=');
+  if (eqPos >= 0)
+  {
+    name = param.Left(eqPos);
+    value = param.Mid(eqPos + 1);
+    return;
+  }
+  for(int i = 0; i < param.Length(); i++)
+  {
+    wchar_t c = param[i];
+    if (c >= L'0' && c <= L'9')
+    {
+      name = param.Left(i);
+      value = param.Mid(i);
+      return;
+    }
+  }
+  name = param;
+}
+
+HRESULT COutHandler::SetParam(COneMethodInfo &oneMethodInfo, const UString &name, const UString &value)
+{
+  CProp prop;
+  if (name.CompareNoCase(L"D") == 0 ||
+      name.CompareNoCase(L"MEM") == 0)
+  {
+    UInt32 dicSize;
+    RINOK(ParsePropDictionaryValue(value, dicSize));
+    prop.Id = (name.CompareNoCase(L"D") == 0) ?
+        NCoderPropID::kDictionarySize :
+        NCoderPropID::kUsedMemorySize;
+    prop.Value = dicSize;
+  }
+  else
+  {
+    int index = FindPropIdFromStringName(name);
+    if (index < 0)
+      return E_INVALIDARG;
+    
+    const CNameToPropID &nameToPropID = g_NameToPropID[index];
+    prop.Id = nameToPropID.PropID;
+    
+    NCOM::CPropVariant propValue;
+    
+    if (nameToPropID.VarType == VT_BSTR)
+      propValue = value;
+    else if (nameToPropID.VarType == VT_BOOL)
+    {
+      bool res;
+      if (!StringToBool(value, res))
+        return E_INVALIDARG;
+      propValue = res;
+    }
+    else
+    {
+      UInt32 number;
+      if (ParseStringToUInt32(value, number) == value.Length())
+        propValue = number;
+      else
+        propValue = value;
+    }
+    
+    if (!ConvertProperty(propValue, nameToPropID.VarType, prop.Value))
+      return E_INVALIDARG;
+  }
+  oneMethodInfo.Props.Add(prop);
+  return S_OK;
+}
+
+HRESULT COutHandler::SetParams(COneMethodInfo &oneMethodInfo, const UString &srcString)
+{
+  UStringVector params;
+  SplitParams(srcString, params);
+  if (params.Size() > 0)
+    oneMethodInfo.MethodName = params[0];
+  for (int i = 1; i < params.Size(); i++)
+  {
+    const UString &param = params[i];
+    UString name, value;
+    SplitParam(param, name, value);
+    RINOK(SetParam(oneMethodInfo, name, value));
+  }
+  return S_OK;
+}
+
+HRESULT COutHandler::SetSolidSettings(const UString &s)
+{
+  UString s2 = s;
+  s2.MakeUpper();
+  for (int i = 0; i < s2.Length();)
+  {
+    const wchar_t *start = ((const wchar_t *)s2) + i;
+    const wchar_t *end;
+    UInt64 v = ConvertStringToUInt64(start, &end);
+    if (start == end)
+    {
+      if (s2[i++] != 'E')
+        return E_INVALIDARG;
+      _solidExtension = true;
+      continue;
+    }
+    i += (int)(end - start);
+    if (i == s2.Length())
+      return E_INVALIDARG;
+    wchar_t c = s2[i++];
+    switch(c)
+    {
+      case 'F':
+        if (v < 1)
+          v = 1;
+        _numSolidFiles = v;
+        break;
+      case 'B':
+        _numSolidBytes = v;
+        _numSolidBytesDefined = true;
+        break;
+      case 'K':
+        _numSolidBytes = (v << 10);
+        _numSolidBytesDefined = true;
+        break;
+      case 'M':
+        _numSolidBytes = (v << 20);
+        _numSolidBytesDefined = true;
+        break;
+      case 'G':
+        _numSolidBytes = (v << 30);
+        _numSolidBytesDefined = true;
+        break;
+      default:
+        return E_INVALIDARG;
+    }
+  }
+  return S_OK;
+}
+
+HRESULT COutHandler::SetSolidSettings(const PROPVARIANT &value)
+{
+  bool isSolid;
+  switch(value.vt)
+  {
+    case VT_EMPTY:
+      isSolid = true;
+      break;
+    case VT_BOOL:
+      isSolid = (value.boolVal != VARIANT_FALSE);
+      break;
+    case VT_BSTR:
+      if (StringToBool(value.bstrVal, isSolid))
+        break;
+      return SetSolidSettings(value.bstrVal);
+    default:
+      return E_INVALIDARG;
+  }
+  if (isSolid)
+    InitSolid();
+  else
+    _numSolidFiles = 1;
+  return S_OK;
+}
+
+void COutHandler::Init()
+{
+  _removeSfxBlock = false;
+  _compressHeaders = true;
+  _encryptHeadersSpecified = false;
+  _encryptHeaders = false;
+  
+  WriteCTime = false;
+  WriteATime = false;
+  WriteMTime = true;
+  
+  #ifdef COMPRESS_MT
+  _numThreads = NWindows::NSystem::GetNumberOfProcessors();
+  #endif
+  
+  _level = 5;
+  _autoFilter = true;
+  _volumeMode = false;
+  _crcSize = 4;
+  InitSolid();
+}
+
+void COutHandler::BeforeSetProperty()
+{
+  Init();
+  #ifdef COMPRESS_MT
+  numProcessors = NSystem::GetNumberOfProcessors();
+  #endif
+
+  mainDicSize = 0xFFFFFFFF;
+  mainDicMethodIndex = 0xFFFFFFFF;
+  minNumber = 0;
+  _crcSize = 4;
+}
+
+HRESULT COutHandler::SetProperty(const wchar_t *nameSpec, const PROPVARIANT &value)
+{
+  UString name = nameSpec;
+  name.MakeUpper();
+  if (name.IsEmpty())
+    return E_INVALIDARG;
+  
+  if (name[0] == 'X')
+  {
+    name.Delete(0);
+    _level = 9;
+    return ParsePropValue(name, value, _level);
+  }
+  
+  if (name[0] == L'S')
+  {
+    name.Delete(0);
+    if (name.IsEmpty())
+      return SetSolidSettings(value);
+    if (value.vt != VT_EMPTY)
+      return E_INVALIDARG;
+    return SetSolidSettings(name);
+  }
+  
+  if (name == L"CRC")
+  {
+    _crcSize = 4;
+    name.Delete(0, 3);
+    return ParsePropValue(name, value, _crcSize);
+  }
+  
+  UInt32 number;
+  int index = ParseStringToUInt32(name, number);
+  UString realName = name.Mid(index);
+  if (index == 0)
+  {
+    if(name.Left(2).CompareNoCase(L"MT") == 0)
+    {
+      #ifdef COMPRESS_MT
+      RINOK(ParseMtProp(name.Mid(2), value, numProcessors, _numThreads));
+      #endif
+      return S_OK;
+    }
+    if (name.CompareNoCase(L"RSFX") == 0)  return SetBoolProperty(_removeSfxBlock, value);
+    if (name.CompareNoCase(L"F") == 0) return SetBoolProperty(_autoFilter, value);
+    if (name.CompareNoCase(L"HC") == 0) return SetBoolProperty(_compressHeaders, value);
+    if (name.CompareNoCase(L"HCF") == 0)
+    {
+      bool compressHeadersFull = true;
+      RINOK(SetBoolProperty(compressHeadersFull, value));
+      if (!compressHeadersFull)
+        return E_INVALIDARG;
+      return S_OK;
+    }
+    if (name.CompareNoCase(L"HE") == 0)
+    {
+      RINOK(SetBoolProperty(_encryptHeaders, value));
+      _encryptHeadersSpecified = true;
+      return S_OK;
+    }
+    if (name.CompareNoCase(L"TC") == 0) return SetBoolProperty(WriteCTime, value);
+    if (name.CompareNoCase(L"TA") == 0) return SetBoolProperty(WriteATime, value);
+    if (name.CompareNoCase(L"TM") == 0) return SetBoolProperty(WriteMTime, value);
+    if (name.CompareNoCase(L"V") == 0) return SetBoolProperty(_volumeMode, value);
+    number = 0;
+  }
+  if (number > 10000)
+    return E_FAIL;
+  if (number < minNumber)
+    return E_INVALIDARG;
+  number -= minNumber;
+  for(int j = _methods.Size(); j <= (int)number; j++)
+  {
+    COneMethodInfo oneMethodInfo;
+    _methods.Add(oneMethodInfo);
+  }
+  
+  COneMethodInfo &oneMethodInfo = _methods[number];
+  
+  if (realName.Length() == 0)
+  {
+    if (value.vt != VT_BSTR)
+      return E_INVALIDARG;
+    
+    RINOK(SetParams(oneMethodInfo, value.bstrVal));
+  }
+  else
+  {
+    CProp prop;
+    if (realName.Left(1).CompareNoCase(L"D") == 0)
+    {
+      UInt32 dicSize;
+      RINOK(ParsePropDictionaryValue(realName.Mid(1), value, dicSize));
+      prop.Id = NCoderPropID::kDictionarySize;
+      prop.Value = dicSize;
+      if (number <= mainDicMethodIndex)
+        mainDicSize = dicSize;
+    }
+    else if (realName.Left(1).CompareNoCase(L"C") == 0)
+    {
+      UInt32 blockSize;
+      RINOK(ParsePropDictionaryValue(realName.Mid(1), value, blockSize));
+      prop.Id = NCoderPropID::kBlockSize;
+      prop.Value = blockSize;
+    }
+    else if (realName.Left(3).CompareNoCase(L"MEM") == 0)
+    {
+      UInt32 dicSize;
+      RINOK(ParsePropDictionaryValue(realName.Mid(3), value, dicSize));
+      prop.Id = NCoderPropID::kUsedMemorySize;
+      prop.Value = dicSize;
+      if (number <= mainDicMethodIndex)
+        mainDicSize = dicSize;
+    }
+    else
+    {
+      int index = FindPropIdFromStringName(realName);
+      if (index < 0)
+        return E_INVALIDARG;
+      const CNameToPropID &nameToPropID = g_NameToPropID[index];
+      prop.Id = nameToPropID.PropID;
+      if (!ConvertProperty(value, nameToPropID.VarType, prop.Value))
+        return E_INVALIDARG;
+    }
+    oneMethodInfo.Props.Add(prop);
+  }
+  return S_OK;
+}
+
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/HandlerOut.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/HandlerOut.h
new file mode 100644
index 0000000..89c81c1
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/HandlerOut.h
@@ -0,0 +1,85 @@
+// HandlerOut.h
+
+#ifndef __HANDLER_OUT_H
+#define __HANDLER_OUT_H
+
+#include "../../../Common/MyString.h"
+#include "../../Common/MethodProps.h"
+
+namespace NArchive {
+
+struct COneMethodInfo
+{
+  CObjectVector<CProp> Props;
+  UString MethodName;
+};
+
+class COutHandler
+{
+public:
+  HRESULT SetProperty(const wchar_t *name, const PROPVARIANT &value);
+  
+  HRESULT SetSolidSettings(const UString &s);
+  HRESULT SetSolidSettings(const PROPVARIANT &value);
+
+  #ifdef COMPRESS_MT
+  UInt32 _numThreads;
+  #endif
+
+  UInt32 _crcSize;
+
+  CObjectVector<COneMethodInfo> _methods;
+  bool _removeSfxBlock;
+  
+  UInt64 _numSolidFiles;
+  UInt64 _numSolidBytes;
+  bool _numSolidBytesDefined;
+  bool _solidExtension;
+
+  bool _compressHeaders;
+  bool _encryptHeadersSpecified;
+  bool _encryptHeaders;
+
+  bool WriteCTime;
+  bool WriteATime;
+  bool WriteMTime;
+
+  bool _autoFilter;
+  UInt32 _level;
+
+  bool _volumeMode;
+
+  HRESULT SetParam(COneMethodInfo &oneMethodInfo, const UString &name, const UString &value);
+  HRESULT SetParams(COneMethodInfo &oneMethodInfo, const UString &srcString);
+
+  void SetCompressionMethod2(COneMethodInfo &oneMethodInfo
+      #ifdef COMPRESS_MT
+      , UInt32 numThreads
+      #endif
+      );
+
+  void InitSolidFiles() { _numSolidFiles = (UInt64)(Int64)(-1); }
+  void InitSolidSize()  { _numSolidBytes = (UInt64)(Int64)(-1); }
+  void InitSolid()
+  {
+    InitSolidFiles();
+    InitSolidSize();
+    _solidExtension = false;
+    _numSolidBytesDefined = false;
+  }
+
+  void Init();
+
+  COutHandler() { Init(); }
+
+  void BeforeSetProperty();
+
+  UInt32 minNumber;
+  UInt32 numProcessors;
+  UInt32 mainDicSize;
+  UInt32 mainDicMethodIndex;
+};
+
+}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/InStreamWithCRC.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/InStreamWithCRC.cpp
new file mode 100644
index 0000000..1d9e555
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/InStreamWithCRC.cpp
@@ -0,0 +1,40 @@
+// InStreamWithCRC.cpp
+
+#include "StdAfx.h"
+
+#include "InStreamWithCRC.h"
+
+STDMETHODIMP CSequentialInStreamWithCRC::Read(void *data, UInt32 size, UInt32 *processedSize)
+{
+  UInt32 realProcessedSize;
+  HRESULT result = _stream->Read(data, size, &realProcessedSize);
+  _size += realProcessedSize;
+  if (size > 0 && realProcessedSize == 0)
+    _wasFinished = true;
+  _crc = CrcUpdate(_crc, data, realProcessedSize);
+  if(processedSize != NULL)
+    *processedSize = realProcessedSize;
+  return result;
+}
+
+STDMETHODIMP CInStreamWithCRC::Read(void *data, UInt32 size, UInt32 *processedSize)
+{
+  UInt32 realProcessedSize;
+  HRESULT result = _stream->Read(data, size, &realProcessedSize);
+  if (size > 0 && realProcessedSize == 0)
+    _wasFinished = true;
+  _size += realProcessedSize;
+  _crc = CrcUpdate(_crc, data, realProcessedSize);
+  if(processedSize != NULL)
+    *processedSize = realProcessedSize;
+  return result;
+}
+
+STDMETHODIMP CInStreamWithCRC::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
+{
+  if (seekOrigin != STREAM_SEEK_SET || offset != 0)
+    return E_FAIL;
+  _size = 0;
+  _crc = CRC_INIT_VAL;
+  return _stream->Seek(offset, seekOrigin, newPosition);
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/InStreamWithCRC.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/InStreamWithCRC.h
new file mode 100644
index 0000000..c5ada6f
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/InStreamWithCRC.h
@@ -0,0 +1,69 @@
+// InStreamWithCRC.h
+
+#ifndef __INSTREAMWITHCRC_H
+#define __INSTREAMWITHCRC_H
+
+#include "../../../Common/MyCom.h"
+#include "../../IStream.h"
+
+extern "C"
+{
+#include "../../../../C/7zCrc.h"
+}
+
+class CSequentialInStreamWithCRC:
+  public ISequentialInStream,
+  public CMyUnknownImp
+{
+public:
+  MY_UNKNOWN_IMP
+
+  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
+private:
+  CMyComPtr<ISequentialInStream> _stream;
+  UInt64 _size;
+  UInt32 _crc;
+  bool _wasFinished;
+public:
+  void SetStream(ISequentialInStream *stream) { _stream = stream;  }
+  void Init()
+  {
+    _size = 0;
+    _wasFinished = false;
+    _crc = CRC_INIT_VAL;
+  }
+  void ReleaseStream() { _stream.Release(); }
+  UInt32 GetCRC() const { return CRC_GET_DIGEST(_crc); }
+  UInt64 GetSize() const { return _size; }
+  bool WasFinished() const { return _wasFinished; }
+};
+
+class CInStreamWithCRC:
+  public IInStream,
+  public CMyUnknownImp
+{
+public:
+  MY_UNKNOWN_IMP1(IInStream)
+
+  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
+  STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
+private:
+  CMyComPtr<IInStream> _stream;
+  UInt64 _size;
+  UInt32 _crc;
+  bool _wasFinished;
+public:
+  void SetStream(IInStream *stream) { _stream = stream;  }
+  void Init()
+  {
+    _size = 0;
+    _wasFinished = false;
+    _crc = CRC_INIT_VAL;
+  }
+  void ReleaseStream() { _stream.Release(); }
+  UInt32 GetCRC() const { return CRC_GET_DIGEST(_crc); }
+  UInt64 GetSize() const { return _size; }
+  bool WasFinished() const { return _wasFinished; }
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/ItemNameUtils.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/ItemNameUtils.cpp
new file mode 100644
index 0000000..6dfaf98
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/ItemNameUtils.cpp
@@ -0,0 +1,59 @@
+// Archive/Common/ItemNameUtils.cpp
+
+#include "StdAfx.h"
+
+#include "ItemNameUtils.h"
+
+namespace NArchive {
+namespace NItemName {
+
+static const wchar_t kOSDirDelimiter = WCHAR_PATH_SEPARATOR;
+static const wchar_t kDirDelimiter = L'/';
+
+UString MakeLegalName(const UString &name)
+{
+  UString zipName = name;
+  zipName.Replace(kOSDirDelimiter, kDirDelimiter);
+  return zipName;
+}
+
+UString GetOSName(const UString &name)
+{
+  UString newName = name;
+  newName.Replace(kDirDelimiter, kOSDirDelimiter);
+  return newName;
+}
+
+UString GetOSName2(const UString &name)
+{
+  if (name.IsEmpty())
+    return UString();
+  UString newName = GetOSName(name);
+  if (newName[newName.Length() - 1] == kOSDirDelimiter)
+    newName.Delete(newName.Length() - 1);
+  return newName;
+}
+
+bool HasTailSlash(const AString &name, UINT codePage)
+{
+  if (name.IsEmpty())
+    return false;
+  LPCSTR prev =
+  #ifdef _WIN32
+    CharPrevExA((WORD)codePage, name, &name[name.Length()], 0);
+  #else
+    (LPCSTR)(name) + (name.Length() - 1);
+  #endif
+  return (*prev == '/');
+}
+
+#ifndef _WIN32
+UString WinNameToOSName(const UString &name)
+{
+  UString newName = name;
+  newName.Replace(L'\\', kOSDirDelimiter);
+  return newName;
+}
+#endif
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/ItemNameUtils.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/ItemNameUtils.h
new file mode 100644
index 0000000..5eafacb
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/ItemNameUtils.h
@@ -0,0 +1,24 @@
+// Archive/Common/ItemNameUtils.h
+
+#ifndef __ARCHIVE_ITEMNAMEUTILS_H
+#define __ARCHIVE_ITEMNAMEUTILS_H
+
+#include "../../../Common/MyString.h"
+
+namespace NArchive {
+namespace NItemName {
+
+  UString MakeLegalName(const UString &name);
+  UString GetOSName(const UString &name);
+  UString GetOSName2(const UString &name);
+  bool HasTailSlash(const AString &name, UINT codePage);
+
+  #ifdef _WIN32
+  inline UString WinNameToOSName(const UString &name)  { return name; }
+  #else
+  UString WinNameToOSName(const UString &name);
+  #endif
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/MultiStream.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/MultiStream.cpp
new file mode 100644
index 0000000..cf7dc05
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/MultiStream.cpp
@@ -0,0 +1,201 @@
+// MultiStream.cpp
+
+#include "StdAfx.h"
+
+#include "MultiStream.h"
+
+STDMETHODIMP CMultiStream::Read(void *data, UInt32 size, UInt32 *processedSize)
+{
+  if(processedSize != NULL)
+    *processedSize = 0;
+  while(_streamIndex < Streams.Size() && size > 0)
+  {
+    CSubStreamInfo &s = Streams[_streamIndex];
+    if (_pos == s.Size)
+    {
+      _streamIndex++;
+      _pos = 0;
+      continue;
+    }
+    RINOK(s.Stream->Seek(s.Pos + _pos, STREAM_SEEK_SET, 0));
+    UInt32 sizeToRead = UInt32(MyMin((UInt64)size, s.Size - _pos));
+    UInt32 realProcessed;
+    HRESULT result = s.Stream->Read(data, sizeToRead, &realProcessed);
+    data = (void *)((Byte *)data + realProcessed);
+    size -= realProcessed;
+    if(processedSize != NULL)
+      *processedSize += realProcessed;
+    _pos += realProcessed;
+    _seekPos += realProcessed;
+    RINOK(result);
+    break;
+  }
+  return S_OK;
+}
+  
+STDMETHODIMP CMultiStream::Seek(Int64 offset, UInt32 seekOrigin,
+    UInt64 *newPosition)
+{
+  UInt64 newPos;
+  switch(seekOrigin)
+  {
+    case STREAM_SEEK_SET:
+      newPos = offset;
+      break;
+    case STREAM_SEEK_CUR:
+      newPos = _seekPos + offset;
+      break;
+    case STREAM_SEEK_END:
+      newPos = _totalLength + offset;
+      break;
+    default:
+      return STG_E_INVALIDFUNCTION;
+  }
+  _seekPos = 0;
+  for (_streamIndex = 0; _streamIndex < Streams.Size(); _streamIndex++)
+  {
+    UInt64 size = Streams[_streamIndex].Size;
+    if (newPos < _seekPos + size)
+    {
+      _pos = newPos - _seekPos;
+      _seekPos += _pos;
+      if (newPosition != 0)
+        *newPosition = newPos;
+      return S_OK;
+    }
+    _seekPos += size;
+  }
+  if (newPos == _seekPos)
+  {
+    if (newPosition != 0)
+      *newPosition = newPos;
+    return S_OK;
+  }
+  return E_FAIL;
+}
+
+
+/*
+class COutVolumeStream:
+  public ISequentialOutStream,
+  public CMyUnknownImp
+{
+  int _volIndex;
+  UInt64 _volSize;
+  UInt64 _curPos;
+  CMyComPtr<ISequentialOutStream> _volumeStream;
+  COutArchive _archive;
+  CCRC _crc;
+
+public:
+  MY_UNKNOWN_IMP
+
+  CFileItem _file;
+  CUpdateOptions _options;
+  CMyComPtr<IArchiveUpdateCallback2> VolumeCallback;
+  void Init(IArchiveUpdateCallback2 *volumeCallback,
+      const UString &name)
+  {
+    _file.Name = name;
+    _file.IsStartPosDefined = true;
+    _file.StartPos = 0;
+    
+    VolumeCallback = volumeCallback;
+    _volIndex = 0;
+    _volSize = 0;
+  }
+  
+  HRESULT Flush();
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+};
+
+HRESULT COutVolumeStream::Flush()
+{
+  if (_volumeStream)
+  {
+    _file.UnPackSize = _curPos;
+    _file.FileCRC = _crc.GetDigest();
+    RINOK(WriteVolumeHeader(_archive, _file, _options));
+    _archive.Close();
+    _volumeStream.Release();
+    _file.StartPos += _file.UnPackSize;
+  }
+  return S_OK;
+}
+*/
+
+/*
+STDMETHODIMP COutMultiStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
+{
+  if(processedSize != NULL)
+    *processedSize = 0;
+  while(size > 0)
+  {
+    if (_streamIndex >= Streams.Size())
+    {
+      CSubStreamInfo subStream;
+      RINOK(VolumeCallback->GetVolumeSize(Streams.Size(), &subStream.Size));
+      RINOK(VolumeCallback->GetVolumeStream(Streams.Size(), &subStream.Stream));
+      subStream.Pos = 0;
+      Streams.Add(subStream);
+      continue;
+    }
+    CSubStreamInfo &subStream = Streams[_streamIndex];
+    if (_offsetPos >= subStream.Size)
+    {
+      _offsetPos -= subStream.Size;
+      _streamIndex++;
+      continue;
+    }
+    if (_offsetPos != subStream.Pos)
+    {
+      CMyComPtr<IOutStream> outStream;
+      RINOK(subStream.Stream.QueryInterface(IID_IOutStream, &outStream));
+      RINOK(outStream->Seek(_offsetPos, STREAM_SEEK_SET, NULL));
+      subStream.Pos = _offsetPos;
+    }
+
+    UInt32 curSize = (UInt32)MyMin((UInt64)size, subStream.Size - subStream.Pos);
+    UInt32 realProcessed;
+    RINOK(subStream.Stream->Write(data, curSize, &realProcessed));
+    data = (void *)((Byte *)data + realProcessed);
+    size -= realProcessed;
+    subStream.Pos += realProcessed;
+    _offsetPos += realProcessed;
+    _absPos += realProcessed;
+    if (_absPos > _length)
+      _length = _absPos;
+    if(processedSize != NULL)
+      *processedSize += realProcessed;
+    if (subStream.Pos == subStream.Size)
+    {
+      _streamIndex++;
+      _offsetPos = 0;
+    }
+    if (realProcessed != curSize && realProcessed == 0)
+      return E_FAIL;
+  }
+  return S_OK;
+}
+
+STDMETHODIMP COutMultiStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
+{
+  if(seekOrigin >= 3)
+    return STG_E_INVALIDFUNCTION;
+  switch(seekOrigin)
+  {
+    case STREAM_SEEK_SET:
+      _absPos = offset;
+      break;
+    case STREAM_SEEK_CUR:
+      _absPos += offset;
+      break;
+    case STREAM_SEEK_END:
+      _absPos = _length + offset;
+      break;
+  }
+  _offsetPos = _absPos;
+  _streamIndex = 0;
+  return S_OK;
+}
+*/
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/MultiStream.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/MultiStream.h
new file mode 100644
index 0000000..137c940
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/MultiStream.h
@@ -0,0 +1,76 @@
+// MultiStream.h
+
+#ifndef __MULTISTREAM_H
+#define __MULTISTREAM_H
+
+#include "../../../Common/MyCom.h"
+#include "../../../Common/MyVector.h"
+#include "../../Archive/IArchive.h"
+
+class CMultiStream:
+  public IInStream,
+  public CMyUnknownImp
+{
+  int _streamIndex;
+  UInt64 _pos;
+  UInt64 _seekPos;
+  UInt64 _totalLength;
+public:
+  struct CSubStreamInfo
+  {
+    CMyComPtr<IInStream> Stream;
+    UInt64 Pos;
+    UInt64 Size;
+  };
+  CObjectVector<CSubStreamInfo> Streams;
+  void Init()
+  {
+    _streamIndex = 0;
+    _pos = 0;
+    _seekPos = 0;
+    _totalLength = 0;
+    for (int i = 0; i < Streams.Size(); i++)
+      _totalLength += Streams[i].Size;
+  }
+
+  MY_UNKNOWN_IMP1(IInStream)
+
+  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
+  STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
+};
+
+/*
+class COutMultiStream:
+  public IOutStream,
+  public CMyUnknownImp
+{
+  int _streamIndex; // required stream
+  UInt64 _offsetPos; // offset from start of _streamIndex index
+  UInt64 _absPos;
+  UInt64 _length;
+
+  struct CSubStreamInfo
+  {
+    CMyComPtr<ISequentialOutStream> Stream;
+    UInt64 Size;
+    UInt64 Pos;
+ };
+  CObjectVector<CSubStreamInfo> Streams;
+public:
+  CMyComPtr<IArchiveUpdateCallback2> VolumeCallback;
+  void Init()
+  {
+    _streamIndex = 0;
+    _offsetPos = 0;
+    _absPos = 0;
+    _length = 0;
+  }
+
+  MY_UNKNOWN_IMP1(IOutStream)
+
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+  STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
+};
+*/
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp
new file mode 100644
index 0000000..2ab2da6
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/OutStreamWithCRC.cpp
@@ -0,0 +1,24 @@
+// OutStreamWithCRC.cpp
+
+#include "StdAfx.h"
+
+#include "OutStreamWithCRC.h"
+
+STDMETHODIMP COutStreamWithCRC::Write(const void *data, UInt32 size, UInt32 *processedSize)
+{
+  UInt32 realProcessedSize;
+  HRESULT result;
+  if(!_stream)
+  {
+    realProcessedSize = size;
+    result = S_OK;
+  }
+  else
+    result = _stream->Write(data, size, &realProcessedSize);
+  if (_calculate)
+    _crc = CrcUpdate(_crc, data, realProcessedSize);
+  _size += realProcessedSize;
+  if(processedSize != NULL)
+    *processedSize = realProcessedSize;
+  return result;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/OutStreamWithCRC.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/OutStreamWithCRC.h
new file mode 100644
index 0000000..a034ee9
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/OutStreamWithCRC.h
@@ -0,0 +1,38 @@
+// OutStreamWithCRC.h
+
+#ifndef __OUT_STREAM_WITH_CRC_H
+#define __OUT_STREAM_WITH_CRC_H
+
+#include "../../../Common/MyCom.h"
+#include "../../IStream.h"
+
+extern "C"
+{
+#include "../../../../C/7zCrc.h"
+}
+
+class COutStreamWithCRC:
+  public ISequentialOutStream,
+  public CMyUnknownImp
+{
+  CMyComPtr<ISequentialOutStream> _stream;
+  UInt64 _size;
+  UInt32 _crc;
+  bool _calculate;
+public:
+  MY_UNKNOWN_IMP
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+  void SetStream(ISequentialOutStream *stream) { _stream = stream; }
+  void ReleaseStream() { _stream.Release(); }
+  void Init(bool calculate = true)
+  {
+    _size = 0;
+    _calculate = calculate;
+    _crc = CRC_INIT_VAL;
+  }
+  void InitCRC() { _crc = CRC_INIT_VAL; }
+  UInt64 GetSize() const { return _size; }
+  UInt32 GetCRC() const { return CRC_GET_DIGEST(_crc); }
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/ParseProperties.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/ParseProperties.cpp
new file mode 100644
index 0000000..5cd849e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/ParseProperties.cpp
@@ -0,0 +1,177 @@
+// ParseProperties.cpp
+
+#include "StdAfx.h"
+
+#include "ParseProperties.h"
+
+#include "Common/StringToInt.h"
+#include "Common/MyCom.h"
+
+HRESULT ParsePropValue(const UString &name, const PROPVARIANT &prop, UInt32 &resValue)
+{
+  if (prop.vt == VT_UI4)
+  {
+    if (!name.IsEmpty())
+      return E_INVALIDARG;
+    resValue = prop.ulVal;
+  }
+  else if (prop.vt == VT_EMPTY)
+  {
+    if(!name.IsEmpty())
+    {
+      const wchar_t *start = name;
+      const wchar_t *end;
+      UInt64 v = ConvertStringToUInt64(start, &end);
+      if (end - start != name.Length())
+        return E_INVALIDARG;
+      resValue = (UInt32)v;
+    }
+  }
+  else
+    return E_INVALIDARG;
+  return S_OK;
+}
+
+static const int kLogarithmicSizeLimit = 32;
+static const wchar_t kByteSymbol = L'B';
+static const wchar_t kKiloByteSymbol = L'K';
+static const wchar_t kMegaByteSymbol = L'M';
+
+HRESULT ParsePropDictionaryValue(const UString &srcStringSpec, UInt32 &dicSize)
+{
+  UString srcString = srcStringSpec;
+  srcString.MakeUpper();
+
+  const wchar_t *start = srcString;
+  const wchar_t *end;
+  UInt64 number = ConvertStringToUInt64(start, &end);
+  int numDigits = (int)(end - start);
+  if (numDigits == 0 || srcString.Length() > numDigits + 1)
+    return E_INVALIDARG;
+  if (srcString.Length() == numDigits)
+  {
+    if (number >= kLogarithmicSizeLimit)
+      return E_INVALIDARG;
+    dicSize = (UInt32)1 << (int)number;
+    return S_OK;
+  }
+  switch (srcString[numDigits])
+  {
+    case kByteSymbol:
+      if (number >= ((UInt64)1 << kLogarithmicSizeLimit))
+        return E_INVALIDARG;
+      dicSize = (UInt32)number;
+      break;
+    case kKiloByteSymbol:
+      if (number >= ((UInt64)1 << (kLogarithmicSizeLimit - 10)))
+        return E_INVALIDARG;
+      dicSize = (UInt32)(number << 10);
+      break;
+    case kMegaByteSymbol:
+      if (number >= ((UInt64)1 << (kLogarithmicSizeLimit - 20)))
+        return E_INVALIDARG;
+      dicSize = (UInt32)(number << 20);
+      break;
+    default:
+      return E_INVALIDARG;
+  }
+  return S_OK;
+}
+
+HRESULT ParsePropDictionaryValue(const UString &name, const PROPVARIANT &prop, UInt32 &resValue)
+{
+  if (name.IsEmpty())
+  {
+    if (prop.vt == VT_UI4)
+    {
+      UInt32 logDicSize = prop.ulVal;
+      if (logDicSize >= 32)
+        return E_INVALIDARG;
+      resValue = (UInt32)1 << logDicSize;
+      return S_OK;
+    }
+    if (prop.vt == VT_BSTR)
+      return ParsePropDictionaryValue(prop.bstrVal, resValue);
+    return E_INVALIDARG;
+  }
+  return ParsePropDictionaryValue(name, resValue);
+}
+
+bool StringToBool(const UString &s, bool &res)
+{
+  if (s.IsEmpty() || s.CompareNoCase(L"ON") == 0 || s.Compare(L"+") == 0)
+  {
+    res = true;
+    return true;
+  }
+  if (s.CompareNoCase(L"OFF") == 0 || s.Compare(L"-") == 0)
+  {
+    res = false;
+    return true;
+  }
+  return false;
+}
+
+HRESULT SetBoolProperty(bool &dest, const PROPVARIANT &value)
+{
+  switch(value.vt)
+  {
+    case VT_EMPTY:
+      dest = true;
+      return S_OK;
+    case VT_BOOL:
+      dest = (value.boolVal != VARIANT_FALSE);
+      return S_OK;
+    /*
+    case VT_UI4:
+      dest = (value.ulVal != 0);
+      break;
+    */
+    case VT_BSTR:
+      return StringToBool(value.bstrVal, dest) ?  S_OK : E_INVALIDARG;
+  }
+  return E_INVALIDARG;
+}
+
+int ParseStringToUInt32(const UString &srcString, UInt32 &number)
+{
+  const wchar_t *start = srcString;
+  const wchar_t *end;
+  UInt64 number64 = ConvertStringToUInt64(start, &end);
+  if (number64 > 0xFFFFFFFF)
+  {
+    number = 0;
+    return 0;
+  }
+  number = (UInt32)number64;
+  return (int)(end - start);
+}
+
+HRESULT ParseMtProp(const UString &name, const PROPVARIANT &prop, UInt32 defaultNumThreads, UInt32 &numThreads)
+{
+  if (name.IsEmpty())
+  {
+    switch(prop.vt)
+    {
+      case VT_UI4:
+        numThreads = prop.ulVal;
+        break;
+      default:
+      {
+        bool val;
+        RINOK(SetBoolProperty(val, prop));
+        numThreads = (val ? defaultNumThreads : 1);
+        break;
+      }
+    }
+  }
+  else
+  {
+    UInt32 number;
+    int index = ParseStringToUInt32(name, number);
+    if (index != name.Length())
+      return E_INVALIDARG;
+    numThreads = number;
+  }
+  return S_OK;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/ParseProperties.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/ParseProperties.h
new file mode 100644
index 0000000..6f80f63
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/ParseProperties.h
@@ -0,0 +1,18 @@
+// ParseProperties.h
+
+#ifndef __PARSEPROPERTIES_H
+#define __PARSEPROPERTIES_H
+
+#include "Common/MyString.h"
+#include "Common/Types.h"
+
+HRESULT ParsePropValue(const UString &name, const PROPVARIANT &prop, UInt32 &resValue);
+HRESULT ParsePropDictionaryValue(const UString &srcStringSpec, UInt32 &dicSize);
+HRESULT ParsePropDictionaryValue(const UString &name, const PROPVARIANT &prop, UInt32 &resValue);
+
+bool StringToBool(const UString &s, bool &res);
+HRESULT SetBoolProperty(bool &dest, const PROPVARIANT &value);
+int ParseStringToUInt32(const UString &srcString, UInt32 &number);
+HRESULT ParseMtProp(const UString &name, const PROPVARIANT &prop, UInt32 defaultNumThreads, UInt32 &numThreads);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/StdAfx.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/StdAfx.h
new file mode 100644
index 0000000..2e4be10
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Common/StdAfx.h
@@ -0,0 +1,9 @@
+// StdAfx.h
+
+#ifndef __STDAFX_H
+#define __STDAFX_H
+
+#include "../../../Common/MyWindows.h"
+#include "../../../Common/NewHandler.h"
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/DllExports2.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/DllExports2.cpp
new file mode 100644
index 0000000..545fcbd
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/DllExports2.cpp
@@ -0,0 +1,82 @@
+// DLLExports.cpp
+
+#include "StdAfx.h"
+
+#include "../../Common/MyInitGuid.h"
+#include "../../Common/ComTry.h"
+#include "../../Common/Types.h"
+#include "../../Windows/PropVariant.h"
+#if defined(_WIN32) && defined(_7ZIP_LARGE_PAGES)
+extern "C"
+{
+#include "../../../C/Alloc.h"
+}
+#endif
+
+#include "IArchive.h"
+#include "../ICoder.h"
+#include "../IPassword.h"
+
+HINSTANCE g_hInstance;
+#ifndef _UNICODE
+#ifdef _WIN32
+bool g_IsNT = false;
+static bool IsItWindowsNT()
+{
+  OSVERSIONINFO versionInfo;
+  versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
+  if (!::GetVersionEx(&versionInfo))
+    return false;
+  return (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT);
+}
+#endif
+#endif
+
+extern "C"
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
+{
+  if (dwReason == DLL_PROCESS_ATTACH)
+  {
+    g_hInstance = hInstance;
+    #ifndef _UNICODE
+    #ifdef _WIN32
+    g_IsNT = IsItWindowsNT();
+    #endif
+    #endif
+  }
+  return TRUE;
+}
+
+DEFINE_GUID(CLSID_CArchiveHandler,
+0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00);
+
+static const UInt16 kDecodeId = 0x2790;
+
+DEFINE_GUID(CLSID_CCodec,
+0x23170F69, 0x40C1, kDecodeId, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+
+STDAPI CreateCoder(const GUID *clsid, const GUID *iid, void **outObject);
+STDAPI CreateArchiver(const GUID *classID, const GUID *iid, void **outObject);
+
+STDAPI CreateObject(const GUID *clsid, const GUID *iid, void **outObject)
+{
+  // COM_TRY_BEGIN
+  *outObject = 0;
+  if (*iid == IID_ICompressCoder || *iid == IID_ICompressCoder2 || *iid == IID_ICompressFilter)
+  {
+    return CreateCoder(clsid, iid, outObject);
+  }
+  else
+  {
+    return CreateArchiver(clsid, iid, outObject);
+  }
+  // COM_TRY_END
+}
+
+STDAPI SetLargePageMode()
+{
+  #if defined(_WIN32) && defined(_7ZIP_LARGE_PAGES)
+  SetLargePageSize();
+  #endif
+  return S_OK;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/IArchive.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/IArchive.h
new file mode 100644
index 0000000..88d6c40
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/IArchive.h
@@ -0,0 +1,228 @@
+// IArchive.h
+
+#ifndef __IARCHIVE_H
+#define __IARCHIVE_H
+
+#include "../IStream.h"
+#include "../IProgress.h"
+#include "../PropID.h"
+
+#define ARCHIVE_INTERFACE_SUB(i, base, x) DECL_INTERFACE_SUB(i, base, 6, x)
+#define ARCHIVE_INTERFACE(i, x) ARCHIVE_INTERFACE_SUB(i, IUnknown, x)
+
+namespace NFileTimeType
+{
+  enum EEnum
+  {
+    kWindows,
+    kUnix,
+    kDOS
+  };
+}
+
+namespace NArchive
+{
+  enum
+  {
+    kName = 0,
+    kClassID,
+    kExtension,
+    kAddExtension,
+    kUpdate,
+    kKeepName,
+    kStartSignature,
+    kFinishSignature,
+    kAssociate
+  };
+
+  namespace NExtract
+  {
+    namespace NAskMode
+    {
+      enum
+      {
+        kExtract = 0,
+        kTest,
+        kSkip
+      };
+    }
+    namespace NOperationResult
+    {
+      enum
+      {
+        kOK = 0,
+        kUnSupportedMethod,
+        kDataError,
+        kCRCError
+      };
+    }
+  }
+  namespace NUpdate
+  {
+    namespace NOperationResult
+    {
+      enum
+      {
+        kOK = 0,
+        kError
+      };
+    }
+  }
+}
+
+#define INTERFACE_IArchiveOpenCallback(x) \
+  STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes) x; \
+  STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes) x; \
+
+ARCHIVE_INTERFACE(IArchiveOpenCallback, 0x10)
+{
+  INTERFACE_IArchiveOpenCallback(PURE);
+};
+
+
+#define INTERFACE_IArchiveExtractCallback(x) \
+  INTERFACE_IProgress(x) \
+  /* GetStream OUT: S_OK - OK, S_FALSE - skeep this file */ \
+  STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream,  Int32 askExtractMode) x; \
+  STDMETHOD(PrepareOperation)(Int32 askExtractMode) x; \
+  STDMETHOD(SetOperationResult)(Int32 resultEOperationResult) x; \
+
+ARCHIVE_INTERFACE_SUB(IArchiveExtractCallback, IProgress, 0x20)
+{
+  INTERFACE_IArchiveExtractCallback(PURE)
+};
+
+
+#define INTERFACE_IArchiveOpenVolumeCallback(x) \
+  STDMETHOD(GetProperty)(PROPID propID, PROPVARIANT *value) x; \
+  STDMETHOD(GetStream)(const wchar_t *name, IInStream **inStream) x; \
+
+ARCHIVE_INTERFACE(IArchiveOpenVolumeCallback, 0x30)
+{
+  INTERFACE_IArchiveOpenVolumeCallback(PURE);
+};
+
+
+ARCHIVE_INTERFACE(IInArchiveGetStream, 0x40)
+{
+  STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream) PURE;
+};
+
+
+ARCHIVE_INTERFACE(IArchiveOpenSetSubArchiveName, 0x50)
+{
+  STDMETHOD(SetSubArchiveName)(const wchar_t *name) PURE;
+};
+
+
+/*
+IInArchive::Extract:
+  indices must be sorted
+  numItems = 0xFFFFFFFF means "all files"
+  testMode != 0 means "test files without writing to outStream"
+*/
+
+#define INTERFACE_IInArchive(x) \
+  STDMETHOD(Open)(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *openArchiveCallback) x; \
+  STDMETHOD(Close)() x; \
+  STDMETHOD(GetNumberOfItems)(UInt32 *numItems) x; \
+  STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value) x; \
+  STDMETHOD(Extract)(const UInt32* indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) x; \
+  STDMETHOD(GetArchiveProperty)(PROPID propID, PROPVARIANT *value) x; \
+  STDMETHOD(GetNumberOfProperties)(UInt32 *numProperties) x; \
+  STDMETHOD(GetPropertyInfo)(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) x; \
+  STDMETHOD(GetNumberOfArchiveProperties)(UInt32 *numProperties) x; \
+  STDMETHOD(GetArchivePropertyInfo)(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) x;
+
+ARCHIVE_INTERFACE(IInArchive, 0x60)
+{
+  INTERFACE_IInArchive(PURE)
+};
+
+
+#define INTERFACE_IArchiveUpdateCallback(x) \
+  INTERFACE_IProgress(x); \
+  STDMETHOD(GetUpdateItemInfo)(UInt32 index,  \
+      Int32 *newData, /*1 - new data, 0 - old data */ \
+      Int32 *newProperties, /* 1 - new properties, 0 - old properties */ \
+      UInt32 *indexInArchive /* -1 if there is no in archive, or if doesn't matter */ \
+      )  x; \
+  STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value) x; \
+  STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **inStream) x; \
+  STDMETHOD(SetOperationResult)(Int32 operationResult) x; \
+
+ARCHIVE_INTERFACE_SUB(IArchiveUpdateCallback, IProgress, 0x80)
+{
+  INTERFACE_IArchiveUpdateCallback(PURE);
+};
+
+#define INTERFACE_IArchiveUpdateCallback2(x) \
+  INTERFACE_IArchiveUpdateCallback(x) \
+  STDMETHOD(GetVolumeSize)(UInt32 index, UInt64 *size) x; \
+  STDMETHOD(GetVolumeStream)(UInt32 index, ISequentialOutStream **volumeStream) x; \
+
+ARCHIVE_INTERFACE_SUB(IArchiveUpdateCallback2, IArchiveUpdateCallback, 0x82)
+{
+  INTERFACE_IArchiveUpdateCallback2(PURE);
+};
+
+
+#define INTERFACE_IOutArchive(x) \
+  STDMETHOD(UpdateItems)(ISequentialOutStream *outStream, UInt32 numItems, IArchiveUpdateCallback *updateCallback) x; \
+  STDMETHOD(GetFileTimeType)(UInt32 *type) x;
+
+ARCHIVE_INTERFACE(IOutArchive, 0xA0)
+{
+  INTERFACE_IOutArchive(PURE)
+};
+
+
+ARCHIVE_INTERFACE(ISetProperties, 0x03)
+{
+  STDMETHOD(SetProperties)(const wchar_t **names, const PROPVARIANT *values, Int32 numProperties) PURE;
+};
+
+
+#define IMP_IInArchive_GetProp(k) \
+  (UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) \
+    { if(index >= sizeof(k) / sizeof(k[0])) return E_INVALIDARG; \
+    const STATPROPSTG &srcItem = k[index]; \
+    *propID = srcItem.propid; *varType = srcItem.vt; *name = 0; return S_OK; } \
+
+#define IMP_IInArchive_GetProp_WITH_NAME(k) \
+  (UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) \
+    { if(index >= sizeof(k) / sizeof(k[0])) return E_INVALIDARG; \
+    const STATPROPSTG &srcItem = k[index]; \
+    *propID = srcItem.propid; *varType = srcItem.vt; \
+    if (srcItem.lpwstrName == 0) *name = 0; else *name = ::SysAllocString(srcItem.lpwstrName); return S_OK; } \
+
+#define IMP_IInArchive_Props \
+  STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProperties) \
+    { *numProperties = sizeof(kProps) / sizeof(kProps[0]); return S_OK; } \
+  STDMETHODIMP CHandler::GetPropertyInfo IMP_IInArchive_GetProp(kProps)
+
+#define IMP_IInArchive_Props_WITH_NAME \
+  STDMETHODIMP CHandler::GetNumberOfProperties(UInt32 *numProperties) \
+    { *numProperties = sizeof(kProps) / sizeof(kProps[0]); return S_OK; } \
+  STDMETHODIMP CHandler::GetPropertyInfo IMP_IInArchive_GetProp_WITH_NAME(kProps)
+
+
+#define IMP_IInArchive_ArcProps \
+  STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProperties) \
+    { *numProperties = sizeof(kArcProps) / sizeof(kArcProps[0]); return S_OK; } \
+  STDMETHODIMP CHandler::GetArchivePropertyInfo IMP_IInArchive_GetProp(kArcProps)
+
+#define IMP_IInArchive_ArcProps_WITH_NAME \
+  STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProperties) \
+    { *numProperties = sizeof(kArcProps) / sizeof(kArcProps[0]); return S_OK; } \
+  STDMETHODIMP CHandler::GetArchivePropertyInfo IMP_IInArchive_GetProp_WITH_NAME(kArcProps)
+
+#define IMP_IInArchive_ArcProps_NO \
+  STDMETHODIMP CHandler::GetNumberOfArchiveProperties(UInt32 *numProperties) \
+    { *numProperties = 0; return S_OK; } \
+  STDMETHODIMP CHandler::GetArchivePropertyInfo(UInt32, BSTR *, PROPID *, VARTYPE *) \
+    { return E_NOTIMPL; } \
+  STDMETHODIMP CHandler::GetArchiveProperty(PROPID, PROPVARIANT *value) \
+    { value->vt = VT_EMPTY; return S_OK; }
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Icons/7z.ico b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Icons/7z.ico
new file mode 100644
index 0000000..319753a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Icons/7z.ico
Binary files differ
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaArcRegister.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaArcRegister.cpp
new file mode 100644
index 0000000..bbeb177
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaArcRegister.cpp
@@ -0,0 +1,14 @@
+// LzmaArcRegister.cpp
+
+#include "StdAfx.h"
+
+#include "../../Common/RegisterArc.h"
+
+#include "LzmaHandler.h"
+
+static IInArchive *CreateArc() { return new NArchive::NLzma::CHandler;  }
+
+static CArcInfo g_ArcInfo =
+  { L"Lzma", L"lzma lzma86", 0, 0xA, {0 }, 0, true, CreateArc, NULL };
+
+REGISTER_ARC(Lzma)
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaFiltersDecode.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaFiltersDecode.cpp
new file mode 100644
index 0000000..26c1092
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaFiltersDecode.cpp
@@ -0,0 +1,86 @@
+// LzmaFiltersDecode.cpp
+
+#include "StdAfx.h"
+
+#include "LzmaFiltersDecode.h"
+
+namespace NArchive {
+namespace NLzma {
+
+static const UInt64 k_LZMA = 0x030101;
+static const UInt64 k_BCJ = 0x03030103;
+  
+HRESULT CDecoder::Code(
+    DECL_EXTERNAL_CODECS_LOC_VARS
+    const CHeader &block,
+    ISequentialInStream *inStream, ISequentialOutStream *outStream,
+    UInt64 *inProcessedSize, ICompressProgressInfo *progress)
+{
+  *inProcessedSize = (UInt64)(Int64)-1;
+
+  if (block.FilterMethod > 1)
+    return E_NOTIMPL;
+
+  if (!_lzmaDecoder)
+  {
+    RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS k_LZMA, _lzmaDecoder, false));
+    if (_lzmaDecoder == 0)
+      return E_NOTIMPL;
+  }
+
+  {
+    CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties;
+    _lzmaDecoder.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecoderProperties);
+    if (!setDecoderProperties)
+      return E_NOTIMPL;
+    RINOK(setDecoderProperties->SetDecoderProperties2(block.LzmaProps, 5));
+  }
+
+  bool filteredMode = (block.FilterMethod == 1);
+
+  CMyComPtr<ICompressSetOutStream> setOutStream;
+
+  if (filteredMode)
+  {
+    if (!_bcjStream)
+    {
+      CMyComPtr<ICompressCoder> coder;
+      RINOK(CreateCoder(EXTERNAL_CODECS_LOC_VARS k_BCJ, coder, false));
+      if (!coder)
+        return E_NOTIMPL;
+      coder.QueryInterface(IID_ISequentialOutStream, &_bcjStream);
+      if (!_bcjStream)
+        return E_NOTIMPL;
+    }
+
+    _bcjStream.QueryInterface(IID_ICompressSetOutStream, &setOutStream);
+    if (!setOutStream)
+      return E_NOTIMPL;
+    RINOK(setOutStream->SetOutStream(outStream));
+    outStream = _bcjStream;
+  }
+
+  const UInt64 *unpackSize = block.HasUnpackSize() ? &block.UnpackSize : NULL;
+  RINOK(_lzmaDecoder->Code(inStream, outStream, NULL, unpackSize, progress));
+
+  if (filteredMode)
+  {
+    CMyComPtr<IOutStreamFlush> flush;
+    _bcjStream.QueryInterface(IID_IOutStreamFlush, &flush);
+    if (flush)
+    {
+      RINOK(flush->Flush());
+    }
+    RINOK(setOutStream->ReleaseOutStream());
+  }
+
+  CMyComPtr<ICompressGetInStreamProcessedSize> getInStreamProcessedSize;
+  _lzmaDecoder.QueryInterface(IID_ICompressGetInStreamProcessedSize, &getInStreamProcessedSize);
+  if (getInStreamProcessedSize)
+  {
+    RINOK(getInStreamProcessedSize->GetInStreamProcessedSize(inProcessedSize));
+  }
+  return S_OK;
+}
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaFiltersDecode.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaFiltersDecode.h
new file mode 100644
index 0000000..36de496
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaFiltersDecode.h
@@ -0,0 +1,26 @@
+// LzmaFiltersDecode.h
+
+#ifndef __LZMA_FILTERS_DECODE_H
+#define __LZMA_FILTERS_DECODE_H
+
+#include "../../Common/CreateCoder.h"
+
+#include "LzmaItem.h"
+
+namespace NArchive {
+namespace NLzma {
+
+class CDecoder
+{
+  CMyComPtr<ICompressCoder> _lzmaDecoder;
+  CMyComPtr<ISequentialOutStream> _bcjStream;
+public:
+  HRESULT Code(DECL_EXTERNAL_CODECS_LOC_VARS
+      const CHeader &block,
+      ISequentialInStream *inStream, ISequentialOutStream *outStream,
+      UInt64 *inProcessedSize, ICompressProgressInfo *progress);
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaHandler.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaHandler.cpp
new file mode 100644
index 0000000..c434595
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaHandler.cpp
@@ -0,0 +1,243 @@
+// LzmaHandler.cpp
+
+#include "StdAfx.h"
+
+#include "LzmaHandler.h"
+
+#include "Common/Defs.h"
+#include "Common/StringConvert.h"
+#include "Common/ComTry.h"
+#include "Common/IntToString.h"
+
+#include "Windows/PropVariant.h"
+
+#include "../../Common/ProgressUtils.h"
+#include "../../Common/StreamUtils.h"
+#include "../Common/DummyOutStream.h"
+
+#include "LzmaFiltersDecode.h"
+
+namespace NArchive {
+namespace NLzma {
+
+STATPROPSTG kProps[] =
+{
+  { NULL, kpidSize, VT_UI8},
+  { NULL, kpidPackSize, VT_UI8},
+  { NULL, kpidMethod, VT_UI1}
+};
+
+IMP_IInArchive_Props
+IMP_IInArchive_ArcProps_NO
+
+STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
+{
+  *numItems = 1;
+  return S_OK;
+}
+
+static void ConvertUInt32ToString(UInt32 value, wchar_t *s)
+{
+  ConvertUInt64ToString(value, s + MyStringLen(s));
+}
+
+static void DictSizeToString(UInt32 value, wchar_t *s)
+{
+  for (int i = 0; i <= 31; i++)
+    if ((UInt32(1) << i) == value)
+    {
+      ConvertUInt32ToString(i, s);
+      return;
+    }
+  wchar_t c = L'b';
+  if ((value & ((1 << 20) - 1)) == 0)
+  {
+    value >>= 20;
+    c = L'm';
+  }
+  else if ((value & ((1 << 10) - 1)) == 0)
+  {
+    value >>= 10;
+    c = L'k';
+  }
+  ConvertUInt32ToString(value, s);
+  int p = MyStringLen(s);
+  s[p++] = c;
+  s[p++] = L'\0';
+}
+
+static void MyStrCat(wchar_t *d, const wchar_t *s)
+{
+  MyStringCopy(d + MyStringLen(d), s);
+}
+
+STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID,  PROPVARIANT *value)
+{
+  if (index != 0)
+    return E_INVALIDARG;
+  NWindows::NCOM::CPropVariant propVariant;
+  switch(propID)
+  {
+    case kpidSize:
+      if (m_StreamInfo.HasUnpackSize())
+        propVariant = (UInt64)m_StreamInfo.UnpackSize;
+      break;
+    case kpidPackSize:
+      propVariant = (UInt64)m_PackSize;
+      break;
+    case kpidMethod:
+    {
+      wchar_t s[64];
+      s[0] = '\0';
+      if (m_StreamInfo.IsThereFilter)
+      {
+        const wchar_t *f;
+        if (m_StreamInfo.FilterMethod == 0)
+          f = L"Copy";
+        else if (m_StreamInfo.FilterMethod == 1)
+          f = L"BCJ";
+        else
+          f = L"Unknown";
+        MyStrCat(s, f);
+        MyStrCat(s, L" ");
+      }
+      MyStrCat(s, L"LZMA:");
+      DictSizeToString(m_StreamInfo.GetDicSize(), s);
+      propVariant = s;
+      break;
+    }
+  }
+  propVariant.Detach(value);
+  return S_OK;
+}
+
+STDMETHODIMP CHandler::Open(IInStream *inStream,
+    const UInt64 * /* maxCheckStartPosition */,
+    IArchiveOpenCallback * /* openArchiveCallback */)
+{
+  {
+    RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &m_StreamStartPosition));
+
+    HRESULT res = ReadStreamHeader(inStream, m_StreamInfo);
+    if (res != S_OK)
+      return S_FALSE;
+    
+    Byte b;
+    RINOK(ReadStream_FALSE(inStream, &b, 1));
+    if (b != 0)
+      return S_FALSE;
+
+    UInt64 endPos;
+    RINOK(inStream->Seek(0, STREAM_SEEK_END, &endPos));
+    m_PackSize = endPos - m_StreamStartPosition - m_StreamInfo.GetHeaderSize();
+
+    m_Stream = inStream;
+  }
+  return S_OK;
+}
+
+STDMETHODIMP CHandler::Close()
+{
+  m_Stream.Release();
+  return S_OK;
+}
+
+
+STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
+    Int32 _aTestMode, IArchiveExtractCallback *extractCallback)
+{
+  COM_TRY_BEGIN
+  bool allFilesMode = (numItems == UInt32(-1));
+  if (!allFilesMode)
+  {
+    if (numItems == 0)
+      return S_OK;
+    if (numItems != 1)
+      return E_INVALIDARG;
+    if (indices[0] != 0)
+      return E_INVALIDARG;
+  }
+
+  bool testMode = (_aTestMode != 0);
+
+  RINOK(extractCallback->SetTotal(m_PackSize));
+    
+  UInt64 currentTotalPacked = 0;
+
+  CDummyOutStream *outStreamSpec = new CDummyOutStream;
+  CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
+
+  {
+    CMyComPtr<ISequentialOutStream> realOutStream;
+    Int32 askMode = testMode ?
+        NArchive::NExtract::NAskMode::kTest :
+        NArchive::NExtract::NAskMode::kExtract;
+    
+    RINOK(extractCallback->GetStream(0, &realOutStream, askMode));
+
+    outStreamSpec->SetStream(realOutStream);
+    outStreamSpec->Init();
+    if(!testMode && !realOutStream)
+      return S_OK;
+    extractCallback->PrepareOperation(askMode);
+  }
+  
+  CLocalProgress *lps = new CLocalProgress;
+  CMyComPtr<ICompressProgressInfo> progress = lps;
+  lps->Init(extractCallback, true);
+
+  CDecoder decoder;
+  RINOK(m_Stream->Seek(m_StreamStartPosition, STREAM_SEEK_SET, NULL));
+  UInt64 streamPos = m_StreamStartPosition;
+  Int32 opRes = NArchive::NExtract::NOperationResult::kOK;
+  bool firstItem = true;
+  for (;;)
+  {
+    CHeader st;
+    HRESULT result = ReadStreamHeader(m_Stream, st);
+    if (result != S_OK)
+    {
+      if (firstItem)
+        return E_FAIL;
+      break;
+    }
+    firstItem = false;
+
+    lps->OutSize = outStreamSpec->GetSize();
+    lps->InSize = currentTotalPacked;
+    RINOK(lps->SetCur());
+    
+    streamPos += st.GetHeaderSize();
+    UInt64 packProcessed;
+
+    {
+      result = decoder.Code(
+          EXTERNAL_CODECS_VARS
+          st, m_Stream, outStream, &packProcessed, progress);
+      if (result == E_NOTIMPL)
+      {
+        opRes = NArchive::NExtract::NOperationResult::kUnSupportedMethod;
+        break;
+      }
+      if (result == S_FALSE)
+      {
+        opRes = NArchive::NExtract::NOperationResult::kDataError;
+        break;
+      }
+      RINOK(result);
+    }
+
+    if (packProcessed == (UInt64)(Int64)-1)
+      break;
+    RINOK(m_Stream->Seek(streamPos + packProcessed, STREAM_SEEK_SET, NULL));
+    currentTotalPacked += packProcessed;
+    streamPos += packProcessed;
+  }
+  outStream.Release();
+  return extractCallback->SetOperationResult(opRes);
+  COM_TRY_END
+}
+
+IMPL_ISetCompressCodecsInfo
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaHandler.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaHandler.h
new file mode 100644
index 0000000..e8fdced
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaHandler.h
@@ -0,0 +1,69 @@
+// Lzma/Handler.h
+
+#ifndef __GZIP_HANDLER_H
+#define __GZIP_HANDLER_H
+
+#include "Common/MyCom.h"
+
+#include "../IArchive.h"
+#include "../../Common/CreateCoder.h"
+
+#include "LzmaIn.h"
+
+namespace NArchive {
+namespace NLzma {
+
+// const UInt64 k_LZMA = 0x030101;
+
+class CHandler:
+  public IInArchive,
+  PUBLIC_ISetCompressCodecsInfo
+  public CMyUnknownImp
+{
+public:
+  MY_QUERYINTERFACE_BEGIN
+  MY_QUERYINTERFACE_ENTRY(IInArchive)
+  QUERY_ENTRY_ISetCompressCodecsInfo
+  MY_QUERYINTERFACE_END
+  MY_ADDREF_RELEASE
+
+  STDMETHOD(Open)(IInStream *inStream,
+      const UInt64 *maxCheckStartPosition,
+      IArchiveOpenCallback *openArchiveCallback);
+  STDMETHOD(Close)();
+  
+  STDMETHOD(GetNumberOfItems)(UInt32 *numItems);
+  STDMETHOD(GetProperty)(UInt32 index, PROPID propID,  PROPVARIANT *value);
+  STDMETHOD(Extract)(const UInt32* indices, UInt32 numItems,
+      Int32 testMode, IArchiveExtractCallback *extractCallback);
+
+  STDMETHOD(GetArchiveProperty)(PROPID propID, PROPVARIANT *value);
+
+  STDMETHOD(GetNumberOfProperties)(UInt32 *numProperties);
+  STDMETHOD(GetPropertyInfo)(UInt32 index,
+      BSTR *name, PROPID *propID, VARTYPE *varType);
+
+  STDMETHOD(GetNumberOfArchiveProperties)(UInt32 *numProperties);
+  STDMETHOD(GetArchivePropertyInfo)(UInt32 index,
+      BSTR *name, PROPID *propID, VARTYPE *varType);
+
+  UString GetMethodString();
+public:
+  CHandler() {  }
+
+private:
+  CHeader m_StreamInfo;
+  UInt64 m_StreamStartPosition;
+  UInt64 m_PackSize;
+
+  CMyComPtr<IInStream> m_Stream;
+
+  DECL_EXTERNAL_CODECS_VARS
+
+  DECL_ISetCompressCodecsInfo
+
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaIn.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaIn.cpp
new file mode 100644
index 0000000..342b01e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaIn.cpp
@@ -0,0 +1,56 @@
+// Archive/LzmaIn.cpp
+
+#include "StdAfx.h"
+
+#include "LzmaIn.h"
+
+#include "../../Common/StreamUtils.h"
+
+namespace NArchive {
+namespace NLzma {
+ 
+static bool CheckDictSize(const Byte *p)
+{
+  UInt32 dicSize = GetUi32(p);
+  int i;
+  for (i = 1; i <= 30; i++)
+    if (dicSize == ((UInt32)2 << i) || dicSize == ((UInt32)3 << i))
+      return true;
+  return false;
+}
+
+HRESULT ReadStreamHeader(ISequentialInStream *inStream, CHeader &block)
+{
+  Byte sig[5 + 9];
+  RINOK(ReadStream_FALSE(inStream, sig, 5 + 8));
+
+  const Byte kMaxProp0Val = 5 * 5 * 9 - 1;
+  if (sig[0] > kMaxProp0Val)
+    return S_FALSE;
+
+  for (int i = 0; i < 5; i++)
+    block.LzmaProps[i] = sig[i];
+  
+  block.IsThereFilter = false;
+  block.FilterMethod = 0;
+
+  if (!CheckDictSize(sig + 1))
+  {
+    if (sig[0] > 1 || sig[1] > kMaxProp0Val)
+      return S_FALSE;
+    block.IsThereFilter = true;
+    block.FilterMethod = sig[0];
+    for (int i = 0; i < 5; i++)
+      block.LzmaProps[i] = sig[i + 1];
+    if (!CheckDictSize(block.LzmaProps + 1))
+      return S_FALSE;
+    RINOK(ReadStream_FALSE(inStream, sig + 5 + 8, 1));
+  }
+  UInt32 unpOffset = 5 + (block.IsThereFilter ? 1 : 0);
+  block.UnpackSize = GetUi64(sig + unpOffset);
+  if (block.HasUnpackSize() && block.UnpackSize >= ((UInt64)1 << 56))
+    return S_FALSE;
+  return S_OK;
+}
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaIn.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaIn.h
new file mode 100644
index 0000000..6f237f2
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaIn.h
@@ -0,0 +1,16 @@
+// Archive/LzmaIn.h
+
+#ifndef __ARCHIVE_LZMA_IN_H
+#define __ARCHIVE_LZMA_IN_H
+
+#include "LzmaItem.h"
+#include "../../IStream.h"
+
+namespace NArchive {
+namespace NLzma {
+
+HRESULT ReadStreamHeader(ISequentialInStream *inStream, CHeader &st);
+
+}}
+  
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaItem.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaItem.h
new file mode 100644
index 0000000..8fcae21
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/LzmaItem.h
@@ -0,0 +1,27 @@
+// Archive/LzmaItem.h
+
+#ifndef __ARCHIVE_LZMA_ITEM_H
+#define __ARCHIVE_LZMA_ITEM_H
+
+#include "Common/Types.h"
+
+#include "../../../../C/CpuArch.h"
+
+namespace NArchive {
+namespace NLzma {
+
+struct CHeader
+{
+  UInt64 UnpackSize;
+  bool IsThereFilter;
+  Byte FilterMethod;
+  Byte LzmaProps[5];
+
+  UInt32 GetDicSize() const { return GetUi32(LzmaProps + 1); }
+  bool HasUnpackSize() const { return (UnpackSize != (UInt64)(Int64)-1);  }
+  unsigned GetHeaderSize() const { return 5 + 8 + (IsThereFilter ? 1 : 0); }
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/StdAfx.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/StdAfx.h
new file mode 100644
index 0000000..e7fb698
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Lzma/StdAfx.h
@@ -0,0 +1,8 @@
+// StdAfx.h
+
+#ifndef __STDAFX_H
+#define __STDAFX_H
+
+#include "../../../Common/MyWindows.h"
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Split/SplitHandler.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Split/SplitHandler.cpp
new file mode 100644
index 0000000..61b7f4a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Split/SplitHandler.cpp
@@ -0,0 +1,357 @@
+// SplitHandler.cpp
+
+#include "StdAfx.h"
+
+#include "Common/ComTry.h"
+#include "Common/Defs.h"
+#include "Common/NewHandler.h"
+#include "Common/StringConvert.h"
+
+#include "Windows/PropVariant.h"
+#include "Windows/Time.h"
+
+#include "../../Common/ProgressUtils.h"
+
+#include "../../Compress/CopyCoder.h"
+
+#include "../Common/ItemNameUtils.h"
+#include "../Common/MultiStream.h"
+
+#include "SplitHandler.h"
+
+using namespace NWindows;
+using namespace NTime;
+
+namespace NArchive {
+namespace NSplit {
+
+STATPROPSTG kProps[] =
+{
+  { NULL, kpidPath, VT_BSTR},
+  { NULL, kpidSize, VT_UI8},
+  { NULL, kpidPackSize, VT_UI8},
+};
+
+IMP_IInArchive_Props
+IMP_IInArchive_ArcProps_NO
+
+class CSeqName
+{
+public:
+  UString _unchangedPart;
+  UString _changedPart;
+  bool _splitStyle;
+  UString GetNextName()
+  {
+    UString newName;
+    if (_splitStyle)
+    {
+      int i;
+      int numLetters = _changedPart.Length();
+      for (i = numLetters - 1; i >= 0; i--)
+      {
+        wchar_t c = _changedPart[i];
+        if (c == 'z')
+        {
+          c = 'a';
+          newName = c + newName;
+          continue;
+        }
+        else if (c == 'Z')
+        {
+          c = 'A';
+          newName = c + newName;
+          continue;
+        }
+        c++;
+        if ((c == 'z' || c == 'Z') && i == 0)
+        {
+          _unchangedPart += c;
+          wchar_t newChar = (c == 'z') ? L'a' : L'A';
+          newName.Empty();
+          numLetters++;
+          for (int k = 0; k < numLetters; k++)
+            newName += newChar;
+          break;
+        }
+        newName = c + newName;
+        i--;
+        for (; i >= 0; i--)
+          newName = _changedPart[i] + newName;
+        break;
+      }
+    }
+    else
+    {
+      int i;
+      int numLetters = _changedPart.Length();
+      for (i = numLetters - 1; i >= 0; i--)
+      {
+        wchar_t c = _changedPart[i];
+        if (c == L'9')
+        {
+          c = L'0';
+          newName = c + newName;
+          if (i == 0)
+            newName = UString(L'1') + newName;
+          continue;
+        }
+        c++;
+        newName = c + newName;
+        i--;
+        for (; i >= 0; i--)
+          newName = _changedPart[i] + newName;
+        break;
+      }
+    }
+    _changedPart = newName;
+    return _unchangedPart + _changedPart;
+  }
+};
+
+STDMETHODIMP CHandler::Open(IInStream *stream,
+    const UInt64 * /* maxCheckStartPosition */,
+    IArchiveOpenCallback *openArchiveCallback)
+{
+  COM_TRY_BEGIN
+  Close();
+  if (openArchiveCallback == 0)
+    return S_FALSE;
+  // try
+  {
+    CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
+    CMyComPtr<IArchiveOpenCallback> openArchiveCallbackWrap = openArchiveCallback;
+    if (openArchiveCallbackWrap.QueryInterface(IID_IArchiveOpenVolumeCallback,
+        &openVolumeCallback) != S_OK)
+      return S_FALSE;
+    
+    {
+      NCOM::CPropVariant prop;
+      RINOK(openVolumeCallback->GetProperty(kpidName, &prop));
+      if (prop.vt != VT_BSTR)
+        return S_FALSE;
+      _name = prop.bstrVal;
+    }
+    
+    int dotPos = _name.ReverseFind('.');
+    UString prefix, ext;
+    if (dotPos >= 0)
+    {
+      prefix = _name.Left(dotPos + 1);
+      ext = _name.Mid(dotPos + 1);
+    }
+    else
+      ext = _name;
+    UString extBig = ext;
+    extBig.MakeUpper();
+
+    CSeqName seqName;
+
+    int numLetters = 2;
+    bool splitStyle = false;
+    if (extBig.Right(2) == L"AA")
+    {
+      splitStyle = true;
+      while (numLetters < extBig.Length())
+      {
+        if (extBig[extBig.Length() - numLetters - 1] != 'A')
+          break;
+        numLetters++;
+      }
+    }
+    else if (ext.Right(2) == L"01")
+    {
+      while (numLetters < extBig.Length())
+      {
+        if (extBig[extBig.Length() - numLetters - 1] != '0')
+          break;
+        numLetters++;
+      }
+      if (numLetters != ext.Length())
+        return S_FALSE;
+    }
+    else
+      return S_FALSE;
+
+    _streams.Add(stream);
+
+    seqName._unchangedPart = prefix + ext.Left(extBig.Length() - numLetters);
+    seqName._changedPart = ext.Right(numLetters);
+    seqName._splitStyle = splitStyle;
+
+    if (prefix.Length() < 1)
+      _subName = L"file";
+    else
+      _subName = prefix.Left(prefix.Length() - 1);
+
+    _totalSize = 0;
+    UInt64 size;
+    {
+      NCOM::CPropVariant prop;
+      RINOK(openVolumeCallback->GetProperty(kpidSize, &prop));
+      if (prop.vt != VT_UI8)
+        return E_INVALIDARG;
+      size = prop.uhVal.QuadPart;
+    }
+    _totalSize += size;
+    _sizes.Add(size);
+    
+    if (openArchiveCallback != NULL)
+    {
+      UInt64 numFiles = _streams.Size();
+      RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL));
+    }
+
+    for (;;)
+    {
+      UString fullName = seqName.GetNextName();
+      CMyComPtr<IInStream> nextStream;
+      HRESULT result = openVolumeCallback->GetStream(fullName, &nextStream);
+      if (result == S_FALSE)
+        break;
+      if (result != S_OK)
+        return result;
+      if (!stream)
+        break;
+      {
+        NCOM::CPropVariant prop;
+        RINOK(openVolumeCallback->GetProperty(kpidSize, &prop));
+        if (prop.vt != VT_UI8)
+          return E_INVALIDARG;
+        size = prop.uhVal.QuadPart;
+      }
+      _totalSize += size;
+      _sizes.Add(size);
+      _streams.Add(nextStream);
+      if (openArchiveCallback != NULL)
+      {
+        UInt64 numFiles = _streams.Size();
+        RINOK(openArchiveCallback->SetCompleted(&numFiles, NULL));
+      }
+    }
+  }
+  /*
+  catch(...)
+  {
+    return S_FALSE;
+  }
+  */
+  return S_OK;
+  COM_TRY_END
+}
+
+STDMETHODIMP CHandler::Close()
+{
+  _sizes.Clear();
+  _streams.Clear();
+  return S_OK;
+}
+
+STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)
+{
+  *numItems = _streams.IsEmpty() ? 0 : 1;
+  return S_OK;
+}
+
+STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)
+{
+  NWindows::NCOM::CPropVariant prop;
+  switch(propID)
+  {
+    case kpidPath:
+      prop = _subName;
+      break;
+    case kpidSize:
+    case kpidPackSize:
+      prop = _totalSize;
+      break;
+  }
+  prop.Detach(value);
+  return S_OK;
+}
+
+STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
+    Int32 _aTestMode, IArchiveExtractCallback *_anExtractCallback)
+{
+  COM_TRY_BEGIN
+
+  if (numItems != UInt32(-1))
+  {
+    if (numItems != 1)
+      return E_INVALIDARG;
+    if (indices[0] != 0)
+      return E_INVALIDARG;
+  }
+  bool testMode = (_aTestMode != 0);
+  CMyComPtr<IArchiveExtractCallback> extractCallback = _anExtractCallback;
+  extractCallback->SetTotal(_totalSize);
+  
+  /*
+  CMyComPtr<IArchiveVolumeExtractCallback> volumeExtractCallback;
+  if (extractCallback.QueryInterface(&volumeExtractCallback) != S_OK)
+    return E_FAIL;
+  */
+
+  UInt64 currentTotalSize = 0;
+  UInt64 currentItemSize;
+
+  RINOK(extractCallback->SetCompleted(&currentTotalSize));
+  CMyComPtr<ISequentialOutStream> realOutStream;
+  Int32 askMode;
+  askMode = testMode ? NArchive::NExtract::NAskMode::kTest :
+  NArchive::NExtract::NAskMode::kExtract;
+  Int32 index = 0;
+  RINOK(extractCallback->GetStream(index, &realOutStream, askMode));
+  
+  RINOK(extractCallback->PrepareOperation(askMode));
+  if (testMode)
+  {
+    RINOK(extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kOK));
+    return S_OK;
+  }
+  
+  if (!testMode && (!realOutStream))
+    return S_OK;
+
+  NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder;
+  CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
+
+  CLocalProgress *lps = new CLocalProgress;
+  CMyComPtr<ICompressProgressInfo> progress = lps;
+  lps->Init(extractCallback, false);
+
+  for (int i = 0; i < _streams.Size(); i++, currentTotalSize += currentItemSize)
+  {
+    lps->InSize = lps->OutSize = currentTotalSize;
+    RINOK(lps->SetCur());
+    IInStream *inStream = _streams[i];
+    RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
+    RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress));
+    currentItemSize = copyCoderSpec->TotalSize;
+  }
+  realOutStream.Release();
+  return extractCallback->SetOperationResult(NArchive::NExtract::NOperationResult::kOK);
+  COM_TRY_END
+}
+
+STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
+{
+  if (index != 0)
+    return E_INVALIDARG;
+  *stream = 0;
+  CMultiStream *streamSpec = new CMultiStream;
+  CMyComPtr<ISequentialInStream> streamTemp = streamSpec;
+  for (int i = 0; i < _streams.Size(); i++)
+  {
+    CMultiStream::CSubStreamInfo subStreamInfo;
+    subStreamInfo.Stream = _streams[i];
+    subStreamInfo.Pos = 0;
+    subStreamInfo.Size = _sizes[i];
+    streamSpec->Streams.Add(subStreamInfo);
+  }
+  streamSpec->Init();
+  *stream = streamTemp.Detach();
+  return S_OK;
+}
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Split/SplitHandler.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Split/SplitHandler.h
new file mode 100644
index 0000000..9e02076
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Split/SplitHandler.h
@@ -0,0 +1,37 @@
+// Split/Handler.h
+
+#ifndef __SPLIT_HANDLER_H
+#define __SPLIT_HANDLER_H
+
+#include "Common/MyCom.h"
+#include "Common/MyString.h"
+#include "../IArchive.h"
+
+namespace NArchive {
+namespace NSplit {
+
+class CHandler:
+  public IInArchive,
+  public IInArchiveGetStream,
+  // public IOutArchive,
+  public CMyUnknownImp
+{
+public:
+  MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream)
+
+  INTERFACE_IInArchive(;)
+
+  STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);
+
+private:
+  UString _subName;
+  UString _name;
+  CObjectVector<CMyComPtr<IInStream> > _streams;
+  CRecordVector<UInt64> _sizes;
+
+  UInt64 _totalSize;
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Split/SplitHandlerOut.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Split/SplitHandlerOut.cpp
new file mode 100644
index 0000000..6edf86f
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Split/SplitHandlerOut.cpp
@@ -0,0 +1,102 @@
+// Split/OutHandler.cpp
+
+#include "StdAfx.h"
+
+#include "SplitHandler.h"
+#include "../../../Windows/PropVariant.h"
+#include "../../../Common/ComTry.h"
+#include "../../../Common/StringToInt.h"
+
+using namespace NWindows;
+
+namespace NArchive {
+namespace NSplit {
+
+/*
+STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type)
+{
+  *type = NFileTimeType::kWindows;
+  return S_OK;
+}
+
+STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numItems,
+    IArchiveUpdateCallback *updateCallback)
+{
+  COM_TRY_BEGIN
+
+  if (numItems != 1)
+    return E_INVALIDARG;
+
+  UInt64 volumeSize = 0;
+
+  CMyComPtr<IArchiveUpdateCallback2> callback2;
+  updateCallback->QueryInterface(IID_IArchiveUpdateCallback2,
+      (void **)&callback2);
+
+  RINOK(callback2->GetVolumeSize(0, &volumeSize));
+
+  Int32 newData;
+  Int32 newProperties;
+  UInt32 indexInArchive;
+  if (!updateCallback)
+    return E_FAIL;
+
+  UInt32 fileIndex = 0;
+  RINOK(updateCallback->GetUpdateItemInfo(fileIndex,
+    &newData, &newProperties, &indexInArchive));
+
+  if (newProperties != 0)
+  {
+    {
+      NCOM::CPropVariant prop;
+      RINOK(updateCallback->GetProperty(fileIndex, kpidIsFolder, &prop));
+      if (prop.vt == VT_EMPTY)
+      {
+      }
+      else if (prop.vt != VT_BOOL)
+        return E_INVALIDARG;
+      else
+      {
+        if (prop.boolVal != VARIANT_FALSE)
+          return E_INVALIDARG;
+      }
+    }
+    {
+      NCOM::CPropVariant prop;
+      RINOK(updateCallback->GetProperty(fileIndex, kpidIsAnti, &prop));
+      if (prop.vt == VT_EMPTY)
+      {
+      }
+      else if (prop.vt != VT_BOOL)
+        return E_INVALIDARG;
+      else
+      {
+        if (prop.boolVal != VARIANT_FALSE)
+          return E_INVALIDARG;
+      }
+    }
+  }
+  UInt64 newSize;
+  bool thereIsCopyData = false;
+  if (newData != 0)
+  {
+    NCOM::CPropVariant prop;
+    RINOK(updateCallback->GetProperty(fileIndex, kpidSize, &prop));
+    if (prop.vt != VT_UI8)
+      return E_INVALIDARG;
+    newSize = prop.uhVal.QuadPart;
+  }
+  else
+    thereIsCopyData = true;
+
+  UInt64 pos = 0;
+  while(pos < newSize)
+  {
+
+  }
+  return S_OK;
+  COM_TRY_END
+}
+*/
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Split/SplitRegister.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Split/SplitRegister.cpp
new file mode 100644
index 0000000..56ddba2
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Split/SplitRegister.cpp
@@ -0,0 +1,20 @@
+// SplitRegister.cpp
+
+#include "StdAfx.h"
+
+#include "../../Common/RegisterArc.h"
+
+#include "SplitHandler.h"
+static IInArchive *CreateArc() { return new NArchive::NSplit::CHandler;  }
+/*
+#ifndef EXTRACT_ONLY
+static IOutArchive *CreateArcOut() { return new NArchive::NSplit::CHandler;  }
+#else
+#define CreateArcOut 0
+#endif
+*/
+
+static CArcInfo g_ArcInfo =
+{ L"Split", L"001", 0, 0xEA, { 0 }, 0, false, CreateArc, 0 };
+
+REGISTER_ARC(Split)
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/Split/StdAfx.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Split/StdAfx.h
new file mode 100644
index 0000000..e7fb698
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/Split/StdAfx.h
@@ -0,0 +1,8 @@
+// StdAfx.h
+
+#ifndef __STDAFX_H
+#define __STDAFX_H
+
+#include "../../../Common/MyWindows.h"
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Archive/StdAfx.h b/third_party/lzma/v4_65/files/CPP/7zip/Archive/StdAfx.h
new file mode 100644
index 0000000..ef555ec
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Archive/StdAfx.h
@@ -0,0 +1,9 @@
+// StdAfx.h
+
+#ifndef __STDAFX_H
+#define __STDAFX_H
+
+#include "../../Common/MyWindows.h"
+#include "../../Common/NewHandler.h"
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/Alone.dsp b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/Alone.dsp
new file mode 100644
index 0000000..b435649
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/Alone.dsp
@@ -0,0 +1,1361 @@
+# Microsoft Developer Studio Project File - Name="Alone" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=Alone - Win32 DebugU
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "Alone.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "Alone.mak" CFG="Alone - Win32 DebugU"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "Alone - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "Alone - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE "Alone - Win32 ReleaseU" (based on "Win32 (x86) Console Application")
+!MESSAGE "Alone - Win32 DebugU" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "Alone - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /Gz /MT /W3 /GX /O1 /I "..\..\..\\" /D "NDEBUG" /D "_MBCS" /D "WIN32" /D "_CONSOLE" /D "COMPRESS_MF_MT" /D "COMPRESS_MT" /D "_NO_CRYPTO" /D "BREAK_HANDLER" /D "BENCH_MT" /Yu"StdAfx.h" /FD /c
+# ADD BASE RSC /l 0x419 /d "NDEBUG"
+# ADD RSC /l 0x419 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386 /out:"c:\UTIL\7zr.exe" /opt:NOWIN98
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF  "$(CFG)" == "Alone - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /Gz /MDd /W3 /Gm /GX /ZI /Od /I "..\..\..\\" /D "_DEBUG" /D "_MBCS" /D "WIN32" /D "_CONSOLE" /D "COMPRESS_MF_MT" /D "COMPRESS_MT" /D "_NO_CRYPTO" /D "BREAK_HANDLER" /D "BENCH_MT" /Yu"StdAfx.h" /FD /GZ /c
+# ADD BASE RSC /l 0x419 /d "_DEBUG"
+# ADD RSC /l 0x419 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /out:"c:\UTIL\7zr.exe" /pdbtype:sept
+
+!ELSEIF  "$(CFG)" == "Alone - Win32 ReleaseU"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "ReleaseU"
+# PROP BASE Intermediate_Dir "ReleaseU"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "ReleaseU"
+# PROP Intermediate_Dir "ReleaseU"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "EXCLUDE_COM" /D "NO_REGISTRY" /D "FORMAT_7Z" /D "FORMAT_BZIP2" /D "FORMAT_ZIP" /D "FORMAT_TAR" /D "FORMAT_GZIP" /D "COMPRESS_LZMA" /D "COMPRESS_BCJ_X86" /D "COMPRESS_BCJ2" /D "COMPRESS_COPY" /D "COMPRESS_MF_PAT" /D "COMPRESS_MF_BT" /D "COMPRESS_PPMD" /D "COMPRESS_DEFLATE" /D "COMPRESS_IMPLODE" /D "COMPRESS_BZIP2" /D "CRYPTO_ZIP" /Yu"StdAfx.h" /FD /c
+# ADD CPP /nologo /Gz /MD /W3 /GX /O1 /I "..\..\..\\" /D "NDEBUG" /D "UNICODE" /D "_UNICODE" /D "WIN32" /D "_CONSOLE" /D "COMPRESS_MF_MT" /D "COMPRESS_MT" /D "_NO_CRYPTO" /D "BREAK_HANDLER" /D "BENCH_MT" /Yu"StdAfx.h" /FD /c
+# ADD BASE RSC /l 0x419 /d "NDEBUG"
+# ADD RSC /l 0x419 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386 /out:"c:\UTIL\7za.exe" /opt:NOWIN98
+# SUBTRACT BASE LINK32 /pdb:none
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386 /out:"c:\UTIL\7zr.exe" /opt:NOWIN98
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF  "$(CFG)" == "Alone - Win32 DebugU"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "DebugU"
+# PROP BASE Intermediate_Dir "DebugU"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "DebugU"
+# PROP Intermediate_Dir "DebugU"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "EXCLUDE_COM" /D "NO_REGISTRY" /D "FORMAT_7Z" /D "FORMAT_BZIP2" /D "FORMAT_ZIP" /D "FORMAT_TAR" /D "FORMAT_GZIP" /D "COMPRESS_LZMA" /D "COMPRESS_BCJ_X86" /D "COMPRESS_BCJ2" /D "COMPRESS_COPY" /D "COMPRESS_MF_PAT" /D "COMPRESS_MF_BT" /D "COMPRESS_PPMD" /D "COMPRESS_DEFLATE" /D "COMPRESS_IMPLODE" /D "COMPRESS_BZIP2" /D "CRYPTO_ZIP" /D "_MBCS" /Yu"StdAfx.h" /FD /GZ /c
+# ADD CPP /nologo /Gz /W4 /Gm /GX /ZI /Od /I "..\..\..\\" /D "_DEBUG" /D "_UNICODE" /D "UNICODE" /D "WIN32" /D "_CONSOLE" /D "COMPRESS_MF_MT" /D "COMPRESS_MT" /D "_NO_CRYPTO" /D "BREAK_HANDLER" /D "BENCH_MT" /Yu"StdAfx.h" /FD /GZ /c
+# ADD BASE RSC /l 0x419 /d "_DEBUG"
+# ADD RSC /l 0x419 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /out:"c:\UTIL\7za.exe" /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /out:"c:\UTIL\7zr.exe" /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "Alone - Win32 Release"
+# Name "Alone - Win32 Debug"
+# Name "Alone - Win32 ReleaseU"
+# Name "Alone - Win32 DebugU"
+# Begin Group "Console"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\UI\Console\ArError.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Console\CompressionMode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Console\ConsoleClose.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Console\ConsoleClose.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Console\ExtractCallbackConsole.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Console\ExtractCallbackConsole.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Console\List.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Console\List.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Console\Main.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Console\MainAr.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Console\OpenCallbackConsole.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Console\OpenCallbackConsole.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Console\PercentPrinter.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Console\PercentPrinter.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Console\UpdateCallbackConsole.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Console\UpdateCallbackConsole.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Console\UserInputUtils.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Console\UserInputUtils.h
+# End Source File
+# End Group
+# Begin Group "Spec"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.cpp
+# ADD CPP /Yc"StdAfx.h"
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.h
+# End Source File
+# End Group
+# Begin Group "Common"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\..\Common\AutoPtr.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\Buffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\CommandLineParser.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\CommandLineParser.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\ComTry.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\CRC.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\Defs.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\DynamicBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\IntToString.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\IntToString.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\ListFileUtils.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\ListFileUtils.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyCom.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyException.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyGuidDef.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyInitGuid.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyString.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyString.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyUnknown.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyVector.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyVector.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyWindows.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyWindows.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\NewHandler.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\NewHandler.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\Random.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\Random.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\StdInStream.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\StdInStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\StdOutStream.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\StdOutStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\StringConvert.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\StringConvert.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\StringToInt.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\StringToInt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\Types.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\UTFConvert.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\UTFConvert.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\Wildcard.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\Wildcard.h
+# End Source File
+# End Group
+# Begin Group "Windows"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\..\Windows\Defs.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\Device.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\DLL.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\DLL.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\Error.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\Error.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileDir.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileDir.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileFind.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileFind.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileIO.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileIO.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileName.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileName.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\Handle.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\MemoryLock.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\MemoryLock.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\PropVariant.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\PropVariant.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\PropVariantConversions.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\PropVariantConversions.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\Synchronization.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\Synchronization.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\System.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\System.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\Thread.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\Time.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\Time.h
+# End Source File
+# End Group
+# Begin Group "7zip Common"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\Common\CreateCoder.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\CreateCoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\CrossThreadProgress.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\CrossThreadProgress.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\FilePathAutoRename.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\FilePathAutoRename.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\FileStreams.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\FileStreams.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\FilterCoder.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\FilterCoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\InBuffer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\InBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\InOutTempBuffer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\InOutTempBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\LimitedStreams.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\LimitedStreams.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\LockedStream.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\LockedStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\MethodId.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\MethodId.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\MethodProps.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\MethodProps.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\OffsetStream.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\OffsetStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\OutBuffer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\OutBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\ProgressMt.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\ProgressMt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\ProgressUtils.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\ProgressUtils.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\RegisterArc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\RegisterCodec.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\StreamBinder.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\StreamBinder.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\StreamObjects.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\StreamObjects.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\StreamUtils.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\StreamUtils.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\VirtThread.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\VirtThread.h
+# End Source File
+# End Group
+# Begin Group "Compress"
+
+# PROP Default_Filter ""
+# Begin Group "LZMA_Alone"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\Compress\LZMA_Alone\LzmaBench.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\LZMA_Alone\LzmaBench.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\LZMA_Alone\LzmaBenchCon.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\LZMA_Alone\LzmaBenchCon.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\Compress\Bcj2Coder.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\Bcj2Coder.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\Bcj2Register.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\BcjCoder.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\BcjCoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\BcjRegister.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\BranchCoder.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\BranchCoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\BranchMisc.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\BranchMisc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\BranchRegister.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\ByteSwap.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\ByteSwap.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\ByteSwapRegister.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\CopyCoder.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\CopyCoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\CopyRegister.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\LzmaDecoder.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\LzmaDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\LzmaEncoder.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\LzmaEncoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\LzmaRegister.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\RangeCoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\RangeCoderBit.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\RangeCoderBitTree.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Compress\RangeCoderOpt.h
+# End Source File
+# End Group
+# Begin Group "Archive"
+
+# PROP Default_Filter ""
+# Begin Group "7z"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zCompressionMode.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zCompressionMode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zDecode.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zDecode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zEncode.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zEncode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zExtract.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zFolderInStream.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zFolderInStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zFolderOutStream.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zFolderOutStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zHandler.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zHandler.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zHandlerOut.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zHeader.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zHeader.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zIn.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zIn.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zItem.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zOut.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zOut.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zProperties.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zProperties.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zRegister.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zSpecStream.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zSpecStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zUpdate.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\7z\7zUpdate.h
+# End Source File
+# End Group
+# Begin Group "Archive Common"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\CoderMixer2.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\CoderMixer2.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\CoderMixer2MT.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\CoderMixer2MT.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\DummyOutStream.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\DummyOutStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\HandlerOut.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\HandlerOut.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\InStreamWithCRC.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\InStreamWithCRC.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\ItemNameUtils.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\ItemNameUtils.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\MultiStream.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\MultiStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\OutStreamWithCRC.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\OutStreamWithCRC.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\ParseProperties.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Common\ParseProperties.h
+# End Source File
+# End Group
+# Begin Group "split"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\Archive\Split\SplitHandler.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Split\SplitHandler.h
+# End Source File
+# End Group
+# Begin Group "LZM"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\Archive\Lzma\LzmaArcRegister.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Lzma\LzmaFiltersDecode.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Lzma\LzmaFiltersDecode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Lzma\LzmaHandler.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Lzma\LzmaHandler.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Lzma\LzmaIn.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Lzma\LzmaIn.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Archive\Lzma\LzmaItem.h
+# End Source File
+# End Group
+# End Group
+# Begin Group "UI Common"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\UI\Common\ArchiveCommandLine.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\ArchiveCommandLine.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\ArchiveExtractCallback.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\ArchiveExtractCallback.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\ArchiveOpenCallback.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\ArchiveOpenCallback.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\DefaultName.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\DefaultName.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\EnumDirItems.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\EnumDirItems.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\Extract.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\Extract.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\ExtractingFilePath.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\ExtractingFilePath.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\LoadCodecs.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\LoadCodecs.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\OpenArchive.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\OpenArchive.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\Property.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\PropIDUtils.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\PropIDUtils.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\SetProperties.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\SetProperties.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\SortUtils.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\SortUtils.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\TempFiles.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\TempFiles.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\Update.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\Update.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\UpdateAction.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\UpdateAction.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\UpdateCallback.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\UpdateCallback.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\UpdatePair.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\UpdatePair.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\UpdateProduce.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\UpdateProduce.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\WorkDir.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\UI\Common\WorkDir.h
+# End Source File
+# End Group
+# Begin Group "7-zip"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\ICoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\IMyUnknown.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\IPassword.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\IProgress.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\IStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\PropID.h
+# End Source File
+# End Group
+# Begin Group "C"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\..\..\C\7zCrc.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\7zCrc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\Alloc.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\Alloc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\Bra.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\Bra.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\Bra86.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\BraIA64.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\IStream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzFind.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzFind.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzFindMt.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzFindMt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\Compress\Lz\LzHash.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzHash.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzmaDec.c
+
+!IF  "$(CFG)" == "Alone - Win32 Release"
+
+# ADD CPP /O2
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Alone - Win32 Debug"
+
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Alone - Win32 ReleaseU"
+
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ELSEIF  "$(CFG)" == "Alone - Win32 DebugU"
+
+# SUBTRACT CPP /YX /Yc /Yu
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzmaDec.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzmaEnc.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzmaEnc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\Threads.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\Threads.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\Types.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/Alone.dsw b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/Alone.dsw
new file mode 100644
index 0000000..65eca43
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/Alone.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Alone"=.\Alone.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/StdAfx.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/StdAfx.cpp
new file mode 100644
index 0000000..d0feea8
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/StdAfx.cpp
@@ -0,0 +1,3 @@
+// StdAfx.cpp
+
+#include "StdAfx.h"
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/StdAfx.h b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/StdAfx.h
new file mode 100644
index 0000000..2e4be10
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/StdAfx.h
@@ -0,0 +1,9 @@
+// StdAfx.h
+
+#ifndef __STDAFX_H
+#define __STDAFX_H
+
+#include "../../../Common/MyWindows.h"
+#include "../../../Common/NewHandler.h"
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/makefile b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/makefile
new file mode 100644
index 0000000..d255af7
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/makefile
@@ -0,0 +1,212 @@
+PROG = 7za.exe
+LIBS = $(LIBS) user32.lib oleaut32.lib Advapi32.lib
+
+CFLAGS = $(CFLAGS) -I ../../../ \
+  -D_NO_CRYPTO \
+  -DWIN_LONG_PATH \
+  -DCOMPRESS_MT \
+  -DCOMPRESS_MF_MT \
+  -D_NO_CRYPTO \
+  -DBREAK_HANDLER \
+  -DBENCH_MT \
+
+
+CONSOLE_OBJS = \
+  $O\ConsoleClose.obj \
+  $O\ExtractCallbackConsole.obj \
+  $O\List.obj \
+  $O\Main.obj \
+  $O\MainAr.obj \
+  $O\OpenCallbackConsole.obj \
+  $O\PercentPrinter.obj \
+  $O\UpdateCallbackConsole.obj \
+  $O\UserInputUtils.obj \
+
+COMMON_OBJS = \
+  $O\CommandLineParser.obj \
+  $O\CRC.obj \
+  $O\IntToString.obj \
+  $O\ListFileUtils.obj \
+  $O\NewHandler.obj \
+  $O\StdInStream.obj \
+  $O\StdOutStream.obj \
+  $O\MyString.obj \
+  $O\StringConvert.obj \
+  $O\StringToInt.obj \
+  $O\UTFConvert.obj \
+  $O\MyVector.obj \
+  $O\Wildcard.obj \
+
+WIN_OBJS = \
+  $O\DLL.obj \
+  $O\Error.obj \
+  $O\FileDir.obj \
+  $O\FileFind.obj \
+  $O\FileIO.obj \
+  $O\FileName.obj \
+  $O\MemoryLock.obj \
+  $O\PropVariant.obj \
+  $O\PropVariantConversions.obj \
+  $O\Synchronization.obj \
+  $O\System.obj \
+  $O\Time.obj \
+
+7ZIP_COMMON_OBJS = \
+  $O\CreateCoder.obj \
+  $O\FilePathAutoRename.obj \
+  $O\FileStreams.obj \
+  $O\InBuffer.obj \
+  $O\InOutTempBuffer.obj \
+  $O\FilterCoder.obj \
+  $O\LimitedStreams.obj \
+  $O\LockedStream.obj \
+  $O\MethodId.obj \
+  $O\MethodProps.obj \
+  $O\OffsetStream.obj \
+  $O\OutBuffer.obj \
+  $O\ProgressUtils.obj \
+  $O\StreamBinder.obj \
+  $O\StreamObjects.obj \
+  $O\StreamUtils.obj \
+  $O\VirtThread.obj \
+
+UI_COMMON_OBJS = \
+  $O\ArchiveCommandLine.obj \
+  $O\ArchiveExtractCallback.obj \
+  $O\ArchiveOpenCallback.obj \
+  $O\DefaultName.obj \
+  $O\EnumDirItems.obj \
+  $O\Extract.obj \
+  $O\ExtractingFilePath.obj \
+  $O\LoadCodecs.obj \
+  $O\OpenArchive.obj \
+  $O\PropIDUtils.obj \
+  $O\SetProperties.obj \
+  $O\SortUtils.obj \
+  $O\TempFiles.obj \
+  $O\Update.obj \
+  $O\UpdateAction.obj \
+  $O\UpdateCallback.obj \
+  $O\UpdatePair.obj \
+  $O\UpdateProduce.obj \
+  $O\WorkDir.obj \
+
+AR_COMMON_OBJS = \
+  $O\CoderMixer2.obj \
+  $O\CoderMixer2MT.obj \
+  $O\CrossThreadProgress.obj \
+  $O\DummyOutStream.obj \
+  $O\HandlerOut.obj \
+  $O\InStreamWithCRC.obj \
+  $O\ItemNameUtils.obj \
+  $O\MultiStream.obj \
+  $O\OutStreamWithCRC.obj \
+  $O\ParseProperties.obj \
+
+
+7Z_OBJS = \
+  $O\7zCompressionMode.obj \
+  $O\7zDecode.obj \
+  $O\7zEncode.obj \
+  $O\7zExtract.obj \
+  $O\7zFolderInStream.obj \
+  $O\7zFolderOutStream.obj \
+  $O\7zHandler.obj \
+  $O\7zHandlerOut.obj \
+  $O\7zHeader.obj \
+  $O\7zIn.obj \
+  $O\7zOut.obj \
+  $O\7zProperties.obj \
+  $O\7zRegister.obj \
+  $O\7zSpecStream.obj \
+  $O\7zUpdate.obj \
+
+LZM_OBJS = \
+  $O\LzmaArcRegister.obj \
+  $O\LzmaFiltersDecode.obj \
+  $O\LzmaHandler.obj \
+  $O\LzmaIn.obj \
+
+SPLIT_OBJS = \
+  $O\SplitHandler.obj \
+  $O\SplitHandlerOut.obj \
+  $O\SplitRegister.obj \
+
+COMPRESS_OBJS = \
+  $O\Bcj2Coder.obj \
+  $O\Bcj2Register.obj \
+  $O\BcjCoder.obj \
+  $O\BcjRegister.obj \
+  $O\BranchCoder.obj \
+  $O\BranchMisc.obj \
+  $O\BranchRegister.obj \
+  $O\ByteSwap.obj \
+  $O\ByteSwapRegister.obj \
+  $O\CopyCoder.obj \
+  $O\CopyRegister.obj \
+  $O\LzmaDecoder.obj \
+  $O\LzmaEncoder.obj \
+  $O\LzmaRegister.obj \
+
+LZMA_BENCH_OBJS = \
+  $O\LzmaBench.obj \
+  $O\LzmaBenchCon.obj \
+
+C_OBJS = \
+  $O\7zCrc.obj \
+  $O\Bra.obj \
+  $O\Bra86.obj \
+  $O\BraIA64.obj \
+  $O\Alloc.obj \
+  $O\LzmaDec.obj \
+  $O\LzmaEnc.obj \
+  $O\LzFind.obj \
+  $O\LzFindMt.obj \
+  $O\Threads.obj \
+
+OBJS = \
+  $O\StdAfx.obj \
+  $(CONSOLE_OBJS) \
+  $(COMMON_OBJS) \
+  $(WIN_OBJS) \
+  $(7ZIP_COMMON_OBJS) \
+  $(UI_COMMON_OBJS) \
+  $(AR_COMMON_OBJS) \
+  $(7Z_OBJS) \
+  $(LZM_OBJS) \
+  $(SPLIT_OBJS) \
+  $(COMPRESS_OBJS) \
+  $(LZMA_BENCH_OBJS) \
+  $(C_OBJS) \
+  $(CRC_OBJS) \
+  $O\resource.res
+
+
+!include "../../../Build.mak"
+
+$(CONSOLE_OBJS): ../../UI/Console/$(*B).cpp
+	$(COMPL)
+
+$(COMMON_OBJS): ../../../Common/$(*B).cpp
+	$(COMPL)
+$(WIN_OBJS): ../../../Windows/$(*B).cpp
+	$(COMPL)
+$(7ZIP_COMMON_OBJS): ../../Common/$(*B).cpp
+	$(COMPL)
+$(UI_COMMON_OBJS): ../../UI/Common/$(*B).cpp
+	$(COMPL)
+$(AR_COMMON_OBJS): ../../Archive/Common/$(*B).cpp
+	$(COMPL)
+
+$(7Z_OBJS): ../../Archive/7z/$(*B).cpp
+	$(COMPL)
+$(LZM_OBJS): ../../Archive/Lzma/$(*B).cpp
+	$(COMPL)
+$(SPLIT_OBJS): ../../Archive/Split/$(*B).cpp
+	$(COMPL)
+$(COMPRESS_OBJS): ../../Compress/$(*B).cpp
+	$(COMPL_O2)
+$(LZMA_BENCH_OBJS): ../../Compress/LZMA_Alone/$(*B).cpp
+	$(COMPL)
+$(C_OBJS): ../../../../C/$(*B).c
+	$(COMPL_O2)
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/resource.rc b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/resource.rc
new file mode 100644
index 0000000..fc9063c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Alone7z/resource.rc
@@ -0,0 +1,3 @@
+#include "../../MyVersionInfo.rc"
+
+MY_VERSION_INFO_APP("7-Zip Standalone Console", "7za")
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zExtractR/StdAfx.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zExtractR/StdAfx.cpp
new file mode 100644
index 0000000..d0feea8
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zExtractR/StdAfx.cpp
@@ -0,0 +1,3 @@
+// StdAfx.cpp
+
+#include "StdAfx.h"
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zExtractR/StdAfx.h b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zExtractR/StdAfx.h
new file mode 100644
index 0000000..2e4be10
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zExtractR/StdAfx.h
@@ -0,0 +1,9 @@
+// StdAfx.h
+
+#ifndef __STDAFX_H
+#define __STDAFX_H
+
+#include "../../../Common/MyWindows.h"
+#include "../../../Common/NewHandler.h"
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zExtractR/makefile b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zExtractR/makefile
new file mode 100644
index 0000000..f544083
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zExtractR/makefile
@@ -0,0 +1,128 @@
+PROG = 7zxr.dll
+DEF_FILE = ../../Archive/Archive2.def
+LIBS = $(LIBS) user32.lib oleaut32.lib
+CFLAGS = $(CFLAGS) -I ../../../ \
+  -DEXTRACT_ONLY \
+  -DCOMPRESS_MT \
+  -D_NO_CRYPTO
+
+COMMON_OBJS = \
+  $O\CRC.obj \
+  $O\IntToString.obj \
+  $O\NewHandler.obj \
+  $O\MyString.obj \
+  $O\StringConvert.obj \
+  $O\StringToInt.obj \
+  $O\MyVector.obj \
+  $O\Wildcard.obj \
+
+WIN_OBJS = \
+  $O\FileDir.obj \
+  $O\FileFind.obj \
+  $O\FileIO.obj \
+  $O\PropVariant.obj \
+  $O\Synchronization.obj \
+  $O\System.obj \
+
+7ZIP_COMMON_OBJS = \
+  $O\CreateCoder.obj \
+  $O\InBuffer.obj \
+  $O\InOutTempBuffer.obj \
+  $O\FilterCoder.obj \
+  $O\LimitedStreams.obj \
+  $O\LockedStream.obj \
+  $O\MethodId.obj \
+  $O\MethodProps.obj \
+  $O\OutBuffer.obj \
+  $O\ProgressUtils.obj \
+  $O\StreamBinder.obj \
+  $O\StreamObjects.obj \
+  $O\StreamUtils.obj \
+  $O\VirtThread.obj \
+
+AR_OBJS = \
+  $O\ArchiveExports.obj \
+  $O\DllExports2.obj \
+
+AR_COMMON_OBJS = \
+  $O\CoderMixer2.obj \
+  $O\CoderMixer2MT.obj \
+  $O\CrossThreadProgress.obj \
+  $O\HandlerOut.obj \
+  $O\ItemNameUtils.obj \
+  $O\OutStreamWithCRC.obj \
+  $O\ParseProperties.obj \
+
+
+7Z_OBJS = \
+  $O\7zCompressionMode.obj \
+  $O\7zDecode.obj \
+  $O\7zExtract.obj \
+  $O\7zFolderOutStream.obj \
+  $O\7zHandler.obj \
+  $O\7zHeader.obj \
+  $O\7zIn.obj \
+  $O\7zProperties.obj \
+  $O\7zRegister.obj \
+
+
+COMPRESS_OBJS = \
+  $O\CodecExports.obj \
+  $O\Bcj2Coder.obj \
+  $O\Bcj2Register.obj \
+  $O\BcjCoder.obj \
+  $O\BcjRegister.obj \
+  $O\BranchCoder.obj \
+  $O\BranchMisc.obj \
+  $O\BranchRegister.obj \
+  $O\ByteSwap.obj \
+  $O\ByteSwapRegister.obj \
+  $O\CopyCoder.obj \
+  $O\CopyRegister.obj \
+  $O\LzmaDecoder.obj \
+  $O\LzmaRegister.obj \
+
+C_OBJS = \
+  $O\7zCrc.obj \
+  $O\Alloc.obj \
+  $O\Bra.obj \
+  $O\Bra86.obj \
+  $O\BraIA64.obj \
+  $O\LzmaDec.obj \
+  $O\Threads.obj \
+
+OBJS = \
+  $O\StdAfx.obj \
+  $(CONSOLE_OBJS) \
+  $(COMMON_OBJS) \
+  $(WIN_OBJS) \
+  $(7ZIP_COMMON_OBJS) \
+  $(AR_OBJS) \
+  $(AR_COMMON_OBJS) \
+  $(7Z_OBJS) \
+  $(COMPRESS_OBJS) \
+  $(C_OBJS) \
+  $O\resource.res
+
+
+!include "../../../Build.mak"
+
+$(COMMON_OBJS): ../../../Common/$(*B).cpp
+	$(COMPL)
+$(WIN_OBJS): ../../../Windows/$(*B).cpp
+	$(COMPL)
+$(7ZIP_COMMON_OBJS): ../../Common/$(*B).cpp
+	$(COMPL)
+$(AR_OBJS): ../../Archive/$(*B).cpp
+	$(COMPL)
+$(AR_COMMON_OBJS): ../../Archive/Common/$(*B).cpp
+	$(COMPL)
+
+$(7Z_OBJS): ../../Archive/7z/$(*B).cpp
+	$(COMPL)
+
+$(COMPRESS_OBJS): ../../Compress/$(*B).cpp
+	$(COMPL_O2)
+
+$(C_OBJS): ../../../../C/$(*B).c
+	$(COMPL_O2)
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zExtractR/resource.rc b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zExtractR/resource.rc
new file mode 100644
index 0000000..eb63698
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zExtractR/resource.rc
@@ -0,0 +1,5 @@
+#include "../../MyVersionInfo.rc"
+
+MY_VERSION_INFO_DLL("7z Standalone Extracting Plugin", "7zxr")
+
+101  ICON  "../../Archive/Icons/7z.ico"
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zR/StdAfx.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zR/StdAfx.cpp
new file mode 100644
index 0000000..d0feea8
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zR/StdAfx.cpp
@@ -0,0 +1,3 @@
+// StdAfx.cpp
+
+#include "StdAfx.h"
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zR/StdAfx.h b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zR/StdAfx.h
new file mode 100644
index 0000000..2e4be10
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zR/StdAfx.h
@@ -0,0 +1,9 @@
+// StdAfx.h
+
+#ifndef __STDAFX_H
+#define __STDAFX_H
+
+#include "../../../Common/MyWindows.h"
+#include "../../../Common/NewHandler.h"
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zR/makefile b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zR/makefile
new file mode 100644
index 0000000..b103f2a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zR/makefile
@@ -0,0 +1,139 @@
+PROG = 7zra.dll
+DEF_FILE = ../../Archive/Archive2.def
+LIBS = $(LIBS) user32.lib oleaut32.lib
+CFLAGS = $(CFLAGS) -I ../../../ \
+  -DCOMPRESS_MT \
+  -DCOMPRESS_MF_MT \
+  -D_NO_CRYPTO
+
+COMMON_OBJS = \
+  $O\CRC.obj \
+  $O\IntToString.obj \
+  $O\NewHandler.obj \
+  $O\MyString.obj \
+  $O\StringConvert.obj \
+  $O\StringToInt.obj \
+  $O\MyVector.obj \
+  $O\Wildcard.obj \
+
+WIN_OBJS = \
+  $O\FileDir.obj \
+  $O\FileFind.obj \
+  $O\FileIO.obj \
+  $O\PropVariant.obj \
+  $O\Synchronization.obj \
+  $O\System.obj \
+
+7ZIP_COMMON_OBJS = \
+  $O\CreateCoder.obj \
+  $O\InBuffer.obj \
+  $O\InOutTempBuffer.obj \
+  $O\FilterCoder.obj \
+  $O\LimitedStreams.obj \
+  $O\LockedStream.obj \
+  $O\MethodId.obj \
+  $O\MethodProps.obj \
+  $O\OutBuffer.obj \
+  $O\ProgressUtils.obj \
+  $O\StreamBinder.obj \
+  $O\StreamObjects.obj \
+  $O\StreamUtils.obj \
+  $O\VirtThread.obj \
+
+AR_OBJS = \
+  $O\ArchiveExports.obj \
+  $O\DllExports2.obj \
+
+AR_COMMON_OBJS = \
+  $O\CoderMixer2.obj \
+  $O\CoderMixer2MT.obj \
+  $O\CrossThreadProgress.obj \
+  $O\HandlerOut.obj \
+  $O\InStreamWithCRC.obj \
+  $O\ItemNameUtils.obj \
+  $O\OutStreamWithCRC.obj \
+  $O\ParseProperties.obj \
+
+
+7Z_OBJS = \
+  $O\7zCompressionMode.obj \
+  $O\7zDecode.obj \
+  $O\7zEncode.obj \
+  $O\7zExtract.obj \
+  $O\7zFolderInStream.obj \
+  $O\7zFolderOutStream.obj \
+  $O\7zHandler.obj \
+  $O\7zHandlerOut.obj \
+  $O\7zHeader.obj \
+  $O\7zIn.obj \
+  $O\7zOut.obj \
+  $O\7zProperties.obj \
+  $O\7zSpecStream.obj \
+  $O\7zUpdate.obj \
+  $O\7zRegister.obj \
+
+
+COMPRESS_OBJS = \
+  $O\CodecExports.obj \
+  $O\Bcj2Coder.obj \
+  $O\Bcj2Register.obj \
+  $O\BcjCoder.obj \
+  $O\BcjRegister.obj \
+  $O\BranchCoder.obj \
+  $O\BranchMisc.obj \
+  $O\BranchRegister.obj \
+  $O\ByteSwap.obj \
+  $O\ByteSwapRegister.obj \
+  $O\CopyCoder.obj \
+  $O\CopyRegister.obj \
+  $O\LzmaDecoder.obj \
+  $O\LzmaEncoder.obj \
+  $O\LzmaRegister.obj \
+
+C_OBJS = \
+  $O\7zCrc.obj \
+  $O\Alloc.obj \
+  $O\Bra.obj \
+  $O\Bra86.obj \
+  $O\BraIA64.obj \
+  $O\LzFind.obj \
+  $O\LzFindMt.obj \
+  $O\LzmaDec.obj \
+  $O\LzmaEnc.obj \
+  $O\Threads.obj \
+
+OBJS = \
+  $O\StdAfx.obj \
+  $(CONSOLE_OBJS) \
+  $(COMMON_OBJS) \
+  $(WIN_OBJS) \
+  $(7ZIP_COMMON_OBJS) \
+  $(AR_OBJS) \
+  $(AR_COMMON_OBJS) \
+  $(7Z_OBJS) \
+  $(COMPRESS_OBJS) \
+  $(C_OBJS) \
+  $O\resource.res
+
+
+!include "../../../Build.mak"
+
+$(COMMON_OBJS): ../../../Common/$(*B).cpp
+	$(COMPL)
+$(WIN_OBJS): ../../../Windows/$(*B).cpp
+	$(COMPL)
+$(7ZIP_COMMON_OBJS): ../../Common/$(*B).cpp
+	$(COMPL)
+$(AR_OBJS): ../../Archive/$(*B).cpp
+	$(COMPL)
+$(AR_COMMON_OBJS): ../../Archive/Common/$(*B).cpp
+	$(COMPL)
+
+$(7Z_OBJS): ../../Archive/7z/$(*B).cpp
+	$(COMPL)
+
+$(COMPRESS_OBJS): ../../Compress/$(*B).cpp
+	$(COMPL_O2)
+
+$(C_OBJS): ../../../../C/$(*B).c
+	$(COMPL_O2)
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zR/resource.rc b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zR/resource.rc
new file mode 100644
index 0000000..f9c1768
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Bundles/Format7zR/resource.rc
@@ -0,0 +1,5 @@
+#include "../../MyVersionInfo.rc"
+
+MY_VERSION_INFO_DLL("7z Standalone Plugin", "7zr")
+
+101  ICON  "../../Archive/Icons/7z.ico"
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/CreateCoder.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Common/CreateCoder.cpp
new file mode 100644
index 0000000..2742495
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/CreateCoder.cpp
@@ -0,0 +1,292 @@
+// CreateCoder.cpp
+
+#include "StdAfx.h"
+
+#include "CreateCoder.h"
+
+#include "../../Windows/PropVariant.h"
+#include "../../Windows/Defs.h"
+#include "FilterCoder.h"
+#include "RegisterCodec.h"
+
+static const unsigned int kNumCodecsMax = 64;
+unsigned int g_NumCodecs = 0;
+const CCodecInfo *g_Codecs[kNumCodecsMax];
+void RegisterCodec(const CCodecInfo *codecInfo)
+{
+  if (g_NumCodecs < kNumCodecsMax)
+    g_Codecs[g_NumCodecs++] = codecInfo;
+}
+
+#ifdef EXTERNAL_CODECS
+static HRESULT ReadNumberOfStreams(ICompressCodecsInfo *codecsInfo, UInt32 index, PROPID propID, UInt32 &res)
+{
+  NWindows::NCOM::CPropVariant prop;
+  RINOK(codecsInfo->GetProperty(index, propID, &prop));
+  if (prop.vt == VT_EMPTY)
+    res = 1;
+  else if (prop.vt == VT_UI4)
+    res = prop.ulVal;
+  else
+    return E_INVALIDARG;
+  return S_OK;
+}
+
+static HRESULT ReadIsAssignedProp(ICompressCodecsInfo *codecsInfo, UInt32 index, PROPID propID, bool &res)
+{
+  NWindows::NCOM::CPropVariant prop;
+  RINOK(codecsInfo->GetProperty(index, propID, &prop));
+  if (prop.vt == VT_EMPTY)
+    res = true;
+  else if (prop.vt == VT_BOOL)
+    res = VARIANT_BOOLToBool(prop.boolVal);
+  else
+    return E_INVALIDARG;
+  return S_OK;
+}
+
+HRESULT LoadExternalCodecs(ICompressCodecsInfo *codecsInfo, CObjectVector<CCodecInfoEx> &externalCodecs)
+{
+  UInt32 num;
+  RINOK(codecsInfo->GetNumberOfMethods(&num));
+  for (UInt32 i = 0; i < num; i++)
+  {
+    CCodecInfoEx info;
+    NWindows::NCOM::CPropVariant prop;
+    RINOK(codecsInfo->GetProperty(i, NMethodPropID::kID, &prop));
+    // if (prop.vt != VT_BSTR)
+    // info.Id.IDSize = (Byte)SysStringByteLen(prop.bstrVal);
+    // memmove(info.Id.ID, prop.bstrVal, info.Id.IDSize);
+    if (prop.vt != VT_UI8)
+    {
+      continue; // old Interface
+      // return E_INVALIDARG;
+    }
+    info.Id = prop.uhVal.QuadPart;
+    prop.Clear();
+    
+    RINOK(codecsInfo->GetProperty(i, NMethodPropID::kName, &prop));
+    if (prop.vt == VT_BSTR)
+      info.Name = prop.bstrVal;
+    else if (prop.vt != VT_EMPTY)
+      return E_INVALIDARG;;
+    
+    RINOK(ReadNumberOfStreams(codecsInfo, i, NMethodPropID::kInStreams, info.NumInStreams));
+    RINOK(ReadNumberOfStreams(codecsInfo, i, NMethodPropID::kOutStreams, info.NumOutStreams));
+    RINOK(ReadIsAssignedProp(codecsInfo, i, NMethodPropID::kEncoderIsAssigned, info.EncoderIsAssigned));
+    RINOK(ReadIsAssignedProp(codecsInfo, i, NMethodPropID::kDecoderIsAssigned, info.DecoderIsAssigned));
+    
+    externalCodecs.Add(info);
+  }
+  return S_OK;
+}
+
+#endif
+
+bool FindMethod(
+  #ifdef EXTERNAL_CODECS
+  ICompressCodecsInfo * /* codecsInfo */, const CObjectVector<CCodecInfoEx> *externalCodecs,
+  #endif
+  const UString &name,
+  CMethodId &methodId, UInt32 &numInStreams, UInt32 &numOutStreams)
+{
+  UInt32 i;
+  for (i = 0; i < g_NumCodecs; i++)
+  {
+    const CCodecInfo &codec = *g_Codecs[i];
+    if (name.CompareNoCase(codec.Name) == 0)
+    {
+      methodId = codec.Id;
+      numInStreams = codec.NumInStreams;
+      numOutStreams = 1;
+      return true;
+    }
+  }
+  #ifdef EXTERNAL_CODECS
+  if (externalCodecs)
+    for (i = 0; i < (UInt32)externalCodecs->Size(); i++)
+    {
+      const CCodecInfoEx &codec = (*externalCodecs)[i];
+      if (codec.Name.CompareNoCase(name) == 0)
+      {
+        methodId = codec.Id;
+        numInStreams = codec.NumInStreams;
+        numOutStreams = codec.NumOutStreams;
+        return true;
+      }
+    }
+  #endif
+  return false;
+}
+
+bool FindMethod(
+  #ifdef EXTERNAL_CODECS
+  ICompressCodecsInfo * /* codecsInfo */, const CObjectVector<CCodecInfoEx> *externalCodecs,
+  #endif
+  CMethodId methodId, UString &name)
+{
+  UInt32 i;
+  for (i = 0; i < g_NumCodecs; i++)
+  {
+    const CCodecInfo &codec = *g_Codecs[i];
+    if (methodId == codec.Id)
+    {
+      name = codec.Name;
+      return true;
+    }
+  }
+  #ifdef EXTERNAL_CODECS
+  if (externalCodecs)
+    for (i = 0; i < (UInt32)externalCodecs->Size(); i++)
+    {
+      const CCodecInfoEx &codec = (*externalCodecs)[i];
+      if (methodId == codec.Id)
+      {
+        name = codec.Name;
+        return true;
+      }
+    }
+  #endif
+  return false;
+}
+
+HRESULT CreateCoder(
+  DECL_EXTERNAL_CODECS_LOC_VARS
+  CMethodId methodId,
+  CMyComPtr<ICompressFilter> &filter,
+  CMyComPtr<ICompressCoder> &coder,
+  CMyComPtr<ICompressCoder2> &coder2,
+  bool encode, bool onlyCoder)
+{
+  bool created = false;
+  UInt32 i;
+  for (i = 0; i < g_NumCodecs; i++)
+  {
+    const CCodecInfo &codec = *g_Codecs[i];
+    if (codec.Id == methodId)
+    {
+      if (encode)
+      {
+        if (codec.CreateEncoder)
+        {
+          void *p = codec.CreateEncoder();
+          if (codec.IsFilter) filter = (ICompressFilter *)p;
+          else if (codec.NumInStreams == 1) coder = (ICompressCoder *)p;
+          else coder2 = (ICompressCoder2 *)p;
+          created = (p != 0);
+          break;
+        }
+      }
+      else
+        if (codec.CreateDecoder)
+        {
+          void *p = codec.CreateDecoder();
+          if (codec.IsFilter) filter = (ICompressFilter *)p;
+          else if (codec.NumInStreams == 1) coder = (ICompressCoder *)p;
+          else coder2 = (ICompressCoder2 *)p;
+          created = (p != 0);
+          break;
+        }
+    }
+  }
+
+  #ifdef EXTERNAL_CODECS
+  if (!created && externalCodecs)
+    for (i = 0; i < (UInt32)externalCodecs->Size(); i++)
+    {
+      const CCodecInfoEx &codec = (*externalCodecs)[i];
+      if (codec.Id == methodId)
+      {
+        if (encode)
+        {
+          if (codec.EncoderIsAssigned)
+          {
+            if (codec.IsSimpleCodec())
+            {
+              HRESULT result = codecsInfo->CreateEncoder(i, &IID_ICompressCoder, (void **)&coder);
+              if (result != S_OK && result != E_NOINTERFACE && result != CLASS_E_CLASSNOTAVAILABLE)
+                return result;
+              if (!coder)
+              {
+                RINOK(codecsInfo->CreateEncoder(i, &IID_ICompressFilter, (void **)&filter));
+              }
+            }
+            else
+            {
+              RINOK(codecsInfo->CreateEncoder(i, &IID_ICompressCoder2, (void **)&coder2));
+            }
+            break;
+          }
+        }
+        else
+          if (codec.DecoderIsAssigned)
+          {
+            if (codec.IsSimpleCodec())
+            {
+              HRESULT result = codecsInfo->CreateDecoder(i, &IID_ICompressCoder, (void **)&coder);
+              if (result != S_OK && result != E_NOINTERFACE && result != CLASS_E_CLASSNOTAVAILABLE)
+                return result;
+              if (!coder)
+              {
+                RINOK(codecsInfo->CreateDecoder(i, &IID_ICompressFilter, (void **)&filter));
+              }
+            }
+            else
+            {
+              RINOK(codecsInfo->CreateDecoder(i, &IID_ICompressCoder2, (void **)&coder2));
+            }
+            break;
+          }
+      }
+    }
+  #endif
+
+  if (onlyCoder && filter)
+  {
+    CFilterCoder *coderSpec = new CFilterCoder;
+    coder = coderSpec;
+    coderSpec->Filter = filter;
+  }
+  return S_OK;
+}
+
+HRESULT CreateCoder(
+  DECL_EXTERNAL_CODECS_LOC_VARS
+  CMethodId methodId,
+  CMyComPtr<ICompressCoder> &coder,
+  CMyComPtr<ICompressCoder2> &coder2,
+  bool encode)
+{
+  CMyComPtr<ICompressFilter> filter;
+  return CreateCoder(
+    EXTERNAL_CODECS_LOC_VARS
+    methodId,
+    filter, coder, coder2, encode, true);
+}
+
+HRESULT CreateCoder(
+  DECL_EXTERNAL_CODECS_LOC_VARS
+  CMethodId methodId,
+  CMyComPtr<ICompressCoder> &coder, bool encode)
+{
+  CMyComPtr<ICompressFilter> filter;
+  CMyComPtr<ICompressCoder2> coder2;
+  return CreateCoder(
+    EXTERNAL_CODECS_LOC_VARS
+    methodId,
+    coder, coder2, encode);
+}
+
+HRESULT CreateFilter(
+  DECL_EXTERNAL_CODECS_LOC_VARS
+  CMethodId methodId,
+  CMyComPtr<ICompressFilter> &filter,
+  bool encode)
+{
+  CMyComPtr<ICompressCoder> coder;
+  CMyComPtr<ICompressCoder2> coder2;
+  return CreateCoder(
+    EXTERNAL_CODECS_LOC_VARS
+    methodId,
+    filter, coder, coder2, encode, false);
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/CreateCoder.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/CreateCoder.h
new file mode 100644
index 0000000..1fcc134
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/CreateCoder.h
@@ -0,0 +1,98 @@
+// CreateCoder.h
+
+#ifndef __CREATECODER_H
+#define __CREATECODER_H
+
+#include "Common/MyCom.h"
+#include "Common/MyString.h"
+#include "../ICoder.h"
+
+#include "MethodId.h"
+
+#ifdef EXTERNAL_CODECS
+
+struct CCodecInfoEx
+{
+  UString Name;
+  CMethodId Id;
+  UInt32 NumInStreams;
+  UInt32 NumOutStreams;
+  bool EncoderIsAssigned;
+  bool DecoderIsAssigned;
+  bool IsSimpleCodec() const { return NumOutStreams == 1 && NumInStreams == 1; }
+  CCodecInfoEx(): EncoderIsAssigned(false), DecoderIsAssigned(false) {}
+};
+
+HRESULT LoadExternalCodecs(ICompressCodecsInfo *codecsInfo, CObjectVector<CCodecInfoEx> &externalCodecs);
+
+#define PUBLIC_ISetCompressCodecsInfo public ISetCompressCodecsInfo,
+#define QUERY_ENTRY_ISetCompressCodecsInfo MY_QUERYINTERFACE_ENTRY(ISetCompressCodecsInfo)
+#define DECL_ISetCompressCodecsInfo STDMETHOD(SetCompressCodecsInfo)(ICompressCodecsInfo *compressCodecsInfo);
+#define IMPL_ISetCompressCodecsInfo2(x) \
+STDMETHODIMP x::SetCompressCodecsInfo(ICompressCodecsInfo *compressCodecsInfo) { \
+  COM_TRY_BEGIN _codecsInfo = compressCodecsInfo;  return LoadExternalCodecs(_codecsInfo, _externalCodecs); COM_TRY_END }
+#define IMPL_ISetCompressCodecsInfo IMPL_ISetCompressCodecsInfo2(CHandler)
+
+#define EXTERNAL_CODECS_VARS2 _codecsInfo, &_externalCodecs
+
+#define DECL_EXTERNAL_CODECS_VARS CMyComPtr<ICompressCodecsInfo> _codecsInfo; CObjectVector<CCodecInfoEx> _externalCodecs;
+#define EXTERNAL_CODECS_VARS EXTERNAL_CODECS_VARS2,
+
+#define DECL_EXTERNAL_CODECS_LOC_VARS2 ICompressCodecsInfo *codecsInfo, const CObjectVector<CCodecInfoEx> *externalCodecs
+#define EXTERNAL_CODECS_LOC_VARS2 codecsInfo, externalCodecs
+
+#define DECL_EXTERNAL_CODECS_LOC_VARS DECL_EXTERNAL_CODECS_LOC_VARS2,
+#define EXTERNAL_CODECS_LOC_VARS EXTERNAL_CODECS_LOC_VARS2,
+
+#else
+
+#define PUBLIC_ISetCompressCodecsInfo
+#define QUERY_ENTRY_ISetCompressCodecsInfo
+#define DECL_ISetCompressCodecsInfo
+#define IMPL_ISetCompressCodecsInfo
+#define EXTERNAL_CODECS_VARS2
+#define DECL_EXTERNAL_CODECS_VARS
+#define EXTERNAL_CODECS_VARS EXTERNAL_CODECS_VARS2
+#define DECL_EXTERNAL_CODECS_LOC_VARS2
+#define EXTERNAL_CODECS_LOC_VARS2
+#define DECL_EXTERNAL_CODECS_LOC_VARS
+#define EXTERNAL_CODECS_LOC_VARS
+
+#endif
+
+bool FindMethod(
+  DECL_EXTERNAL_CODECS_LOC_VARS
+  const UString &name, CMethodId &methodId, UInt32 &numInStreams, UInt32 &numOutStreams);
+
+bool FindMethod(
+  DECL_EXTERNAL_CODECS_LOC_VARS
+  CMethodId methodId, UString &name);
+
+
+HRESULT CreateCoder(
+  DECL_EXTERNAL_CODECS_LOC_VARS
+  CMethodId methodId,
+  CMyComPtr<ICompressFilter> &filter,
+  CMyComPtr<ICompressCoder> &coder,
+  CMyComPtr<ICompressCoder2> &coder2,
+  bool encode, bool onlyCoder);
+
+HRESULT CreateCoder(
+  DECL_EXTERNAL_CODECS_LOC_VARS
+  CMethodId methodId,
+  CMyComPtr<ICompressCoder> &coder,
+  CMyComPtr<ICompressCoder2> &coder2,
+  bool encode);
+
+HRESULT CreateCoder(
+  DECL_EXTERNAL_CODECS_LOC_VARS
+  CMethodId methodId,
+  CMyComPtr<ICompressCoder> &coder, bool encode);
+
+HRESULT CreateFilter(
+  DECL_EXTERNAL_CODECS_LOC_VARS
+  CMethodId methodId,
+  CMyComPtr<ICompressFilter> &filter,
+  bool encode);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/FilePathAutoRename.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Common/FilePathAutoRename.cpp
new file mode 100644
index 0000000..bf69fc7
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/FilePathAutoRename.cpp
@@ -0,0 +1,57 @@
+// FilePathAutoRename.cpp
+
+#include "StdAfx.h"
+#include "FilePathAutoRename.h"
+
+#include "Common/Defs.h"
+#include "Common/IntToString.h"
+
+#include "Windows/FileName.h"
+#include "Windows/FileFind.h"
+
+using namespace NWindows;
+
+static bool MakeAutoName(const UString &name,
+    const UString &extension, int value, UString &path)
+{
+  wchar_t number[32];
+  ConvertUInt64ToString(value, number);
+  path = name;
+  path += number;
+  path += extension;
+  return NFile::NFind::DoesFileExist(path);
+}
+
+bool AutoRenamePath(UString &fullProcessedPath)
+{
+  UString path;
+  int dotPos = fullProcessedPath.ReverseFind(L'.');
+
+  int slashPos = fullProcessedPath.ReverseFind(L'/');
+  #ifdef _WIN32
+  int slash1Pos = fullProcessedPath.ReverseFind(L'\\');
+  slashPos = MyMax(slashPos, slash1Pos);
+  #endif
+
+  UString name, extension;
+  if (dotPos > slashPos &&  dotPos > 0)
+  {
+    name = fullProcessedPath.Left(dotPos);
+    extension = fullProcessedPath.Mid(dotPos);
+  }
+  else
+    name = fullProcessedPath;
+  name += L'_';
+  int indexLeft = 1, indexRight = (1 << 30);
+  while (indexLeft != indexRight)
+  {
+    int indexMid = (indexLeft + indexRight) / 2;
+    if (MakeAutoName(name, extension, indexMid, path))
+      indexLeft = indexMid + 1;
+    else
+      indexRight = indexMid;
+  }
+  if (MakeAutoName(name, extension, indexRight, fullProcessedPath))
+    return false;
+  return true;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/FilePathAutoRename.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/FilePathAutoRename.h
new file mode 100644
index 0000000..3ef87f4
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/FilePathAutoRename.h
@@ -0,0 +1,10 @@
+// Util/FilePathAutoRename.h
+
+#ifndef __FILEPATHAUTORENAME_H
+#define __FILEPATHAUTORENAME_H
+
+#include "Common/MyString.h"
+
+bool AutoRenamePath(UString &fullProcessedPath);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/FileStreams.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Common/FileStreams.cpp
new file mode 100644
index 0000000..eb7ad3a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/FileStreams.cpp
@@ -0,0 +1,261 @@
+// FileStreams.cpp
+
+#include "StdAfx.h"
+
+#ifndef _WIN32
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#endif
+
+#include "FileStreams.h"
+
+static inline HRESULT ConvertBoolToHRESULT(bool result)
+{
+  #ifdef _WIN32
+  if (result)
+    return S_OK;
+  DWORD lastError = ::GetLastError();
+  if (lastError == 0)
+    return E_FAIL;
+  return HRESULT_FROM_WIN32(lastError);
+  #else
+  return result ? S_OK: E_FAIL;
+  #endif
+}
+
+bool CInFileStream::Open(LPCTSTR fileName)
+{
+  return File.Open(fileName);
+}
+
+#ifdef USE_WIN_FILE
+#ifndef _UNICODE
+bool CInFileStream::Open(LPCWSTR fileName)
+{
+  return File.Open(fileName);
+}
+#endif
+#endif
+
+bool CInFileStream::OpenShared(LPCTSTR fileName, bool shareForWrite)
+{
+  return File.OpenShared(fileName, shareForWrite);
+}
+
+#ifdef USE_WIN_FILE
+#ifndef _UNICODE
+bool CInFileStream::OpenShared(LPCWSTR fileName, bool shareForWrite)
+{
+  return File.OpenShared(fileName, shareForWrite);
+}
+#endif
+#endif
+
+STDMETHODIMP CInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize)
+{
+  #ifdef USE_WIN_FILE
+  
+  UInt32 realProcessedSize;
+  bool result = File.ReadPart(data, size, realProcessedSize);
+  if(processedSize != NULL)
+    *processedSize = realProcessedSize;
+  return ConvertBoolToHRESULT(result);
+  
+  #else
+  
+  if(processedSize != NULL)
+    *processedSize = 0;
+  ssize_t res = File.Read(data, (size_t)size);
+  if (res == -1)
+    return E_FAIL;
+  if(processedSize != NULL)
+    *processedSize = (UInt32)res;
+  return S_OK;
+
+  #endif
+}
+
+#ifndef _WIN32_WCE
+STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize)
+{
+  #ifdef _WIN32
+  UInt32 realProcessedSize;
+  BOOL res = ::ReadFile(GetStdHandle(STD_INPUT_HANDLE),
+      data, size, (DWORD *)&realProcessedSize, NULL);
+  if(processedSize != NULL)
+    *processedSize = realProcessedSize;
+  if (res == FALSE && GetLastError() == ERROR_BROKEN_PIPE)
+    return S_OK;
+  return ConvertBoolToHRESULT(res != FALSE);
+  
+  #else
+
+  if(processedSize != NULL)
+    *processedSize = 0;
+  ssize_t res;
+  do
+  {
+    res = read(0, data, (size_t)size);
+  }
+  while (res < 0 && (errno == EINTR));
+  if (res == -1)
+    return E_FAIL;
+  if(processedSize != NULL)
+    *processedSize = (UInt32)res;
+  return S_OK;
+  
+  #endif
+}
+  
+#endif
+
+STDMETHODIMP CInFileStream::Seek(Int64 offset, UInt32 seekOrigin,
+    UInt64 *newPosition)
+{
+  if(seekOrigin >= 3)
+    return STG_E_INVALIDFUNCTION;
+
+  #ifdef USE_WIN_FILE
+
+  UInt64 realNewPosition;
+  bool result = File.Seek(offset, seekOrigin, realNewPosition);
+  if(newPosition != NULL)
+    *newPosition = realNewPosition;
+  return ConvertBoolToHRESULT(result);
+  
+  #else
+  
+  off_t res = File.Seek(offset, seekOrigin);
+  if (res == -1)
+    return E_FAIL;
+  if(newPosition != NULL)
+    *newPosition = (UInt64)res;
+  return S_OK;
+  
+  #endif
+}
+
+STDMETHODIMP CInFileStream::GetSize(UInt64 *size)
+{
+  return ConvertBoolToHRESULT(File.GetLength(*size));
+}
+
+
+//////////////////////////
+// COutFileStream
+
+HRESULT COutFileStream::Close()
+{
+  return ConvertBoolToHRESULT(File.Close());
+}
+
+STDMETHODIMP COutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
+{
+  #ifdef USE_WIN_FILE
+
+  UInt32 realProcessedSize;
+  bool result = File.WritePart(data, size, realProcessedSize);
+  ProcessedSize += realProcessedSize;
+  if(processedSize != NULL)
+    *processedSize = realProcessedSize;
+  return ConvertBoolToHRESULT(result);
+  
+  #else
+  
+  if(processedSize != NULL)
+    *processedSize = 0;
+  ssize_t res = File.Write(data, (size_t)size);
+  if (res == -1)
+    return E_FAIL;
+  if(processedSize != NULL)
+    *processedSize = (UInt32)res;
+  ProcessedSize += res;
+  return S_OK;
+  
+  #endif
+}
+  
+STDMETHODIMP COutFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
+{
+  if(seekOrigin >= 3)
+    return STG_E_INVALIDFUNCTION;
+  #ifdef USE_WIN_FILE
+
+  UInt64 realNewPosition;
+  bool result = File.Seek(offset, seekOrigin, realNewPosition);
+  if(newPosition != NULL)
+    *newPosition = realNewPosition;
+  return ConvertBoolToHRESULT(result);
+  
+  #else
+  
+  off_t res = File.Seek(offset, seekOrigin);
+  if (res == -1)
+    return E_FAIL;
+  if(newPosition != NULL)
+    *newPosition = (UInt64)res;
+  return S_OK;
+  
+  #endif
+}
+
+STDMETHODIMP COutFileStream::SetSize(Int64 newSize)
+{
+  #ifdef USE_WIN_FILE
+  UInt64 currentPos;
+  if(!File.Seek(0, FILE_CURRENT, currentPos))
+    return E_FAIL;
+  bool result = File.SetLength(newSize);
+  UInt64 currentPos2;
+  result = result && File.Seek(currentPos, currentPos2);
+  return result ? S_OK : E_FAIL;
+  #else
+  return E_FAIL;
+  #endif
+}
+
+#ifndef _WIN32_WCE
+STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
+{
+  if(processedSize != NULL)
+    *processedSize = 0;
+
+  #ifdef _WIN32
+  UInt32 realProcessedSize;
+  BOOL res = TRUE;
+  if (size > 0)
+  {
+    // Seems that Windows doesn't like big amounts writing to stdout.
+    // So we limit portions by 32KB.
+    UInt32 sizeTemp = (1 << 15);
+    if (sizeTemp > size)
+      sizeTemp = size;
+    res = ::WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
+        data, sizeTemp, (DWORD *)&realProcessedSize, NULL);
+    size -= realProcessedSize;
+    data = (const void *)((const Byte *)data + realProcessedSize);
+    if(processedSize != NULL)
+      *processedSize += realProcessedSize;
+  }
+  return ConvertBoolToHRESULT(res != FALSE);
+
+  #else
+  
+  ssize_t res;
+  do
+  {
+    res = write(1, data, (size_t)size);
+  }
+  while (res < 0 && (errno == EINTR));
+  if (res == -1)
+    return E_FAIL;
+  if(processedSize != NULL)
+    *processedSize = (UInt32)res;
+  return S_OK;
+  
+  return S_OK;
+  #endif
+}
+  
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/FileStreams.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/FileStreams.h
new file mode 100644
index 0000000..361c219
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/FileStreams.h
@@ -0,0 +1,140 @@
+// FileStreams.h
+
+#ifndef __FILESTREAMS_H
+#define __FILESTREAMS_H
+
+#ifdef _WIN32
+#define USE_WIN_FILE
+#endif
+
+#ifdef USE_WIN_FILE
+#include "../../Windows/FileIO.h"
+#else
+#include "../../Common/C_FileIO.h"
+#endif
+
+#include "../IStream.h"
+#include "../../Common/MyCom.h"
+
+class CInFileStream:
+  public IInStream,
+  public IStreamGetSize,
+  public CMyUnknownImp
+{
+public:
+  #ifdef USE_WIN_FILE
+  NWindows::NFile::NIO::CInFile File;
+  #else
+  NC::NFile::NIO::CInFile File;
+  #endif
+  CInFileStream() {}
+  virtual ~CInFileStream() {}
+
+  bool Open(LPCTSTR fileName);
+  #ifdef USE_WIN_FILE
+  #ifndef _UNICODE
+  bool Open(LPCWSTR fileName);
+  #endif
+  #endif
+
+  bool OpenShared(LPCTSTR fileName, bool shareForWrite);
+  #ifdef USE_WIN_FILE
+  #ifndef _UNICODE
+  bool OpenShared(LPCWSTR fileName, bool shareForWrite);
+  #endif
+  #endif
+
+  MY_UNKNOWN_IMP2(IInStream, IStreamGetSize)
+
+  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
+  STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
+
+  STDMETHOD(GetSize)(UInt64 *size);
+};
+
+#ifndef _WIN32_WCE
+class CStdInFileStream:
+  public ISequentialInStream,
+  public CMyUnknownImp
+{
+public:
+  // HANDLE File;
+  // CStdInFileStream() File(INVALID_HANDLE_VALUE): {}
+  // void Open() { File = GetStdHandle(STD_INPUT_HANDLE); };
+  MY_UNKNOWN_IMP
+
+  virtual ~CStdInFileStream() {}
+  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
+};
+#endif
+
+class COutFileStream:
+  public IOutStream,
+  public CMyUnknownImp
+{
+  #ifdef USE_WIN_FILE
+  NWindows::NFile::NIO::COutFile File;
+  #else
+  NC::NFile::NIO::COutFile File;
+  #endif
+public:
+  virtual ~COutFileStream() {}
+  bool Create(LPCTSTR fileName, bool createAlways)
+  {
+    ProcessedSize = 0;
+    return File.Create(fileName, createAlways);
+  }
+  bool Open(LPCTSTR fileName, DWORD creationDisposition)
+  {
+    ProcessedSize = 0;
+    return File.Open(fileName, creationDisposition);
+  }
+  #ifdef USE_WIN_FILE
+  #ifndef _UNICODE
+  bool Create(LPCWSTR fileName, bool createAlways)
+  {
+    ProcessedSize = 0;
+    return File.Create(fileName, createAlways);
+  }
+  bool Open(LPCWSTR fileName, DWORD creationDisposition)
+  {
+    ProcessedSize = 0;
+    return File.Open(fileName, creationDisposition);
+  }
+  #endif
+  #endif
+
+  HRESULT Close();
+  
+  UInt64 ProcessedSize;
+
+  #ifdef USE_WIN_FILE
+  bool SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime)
+  {
+    return File.SetTime(cTime, aTime, mTime);
+  }
+  bool SetMTime(const FILETIME *mTime) {  return File.SetMTime(mTime); }
+  #endif
+
+
+  MY_UNKNOWN_IMP1(IOutStream)
+
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+  STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
+  STDMETHOD(SetSize)(Int64 newSize);
+};
+
+#ifndef _WIN32_WCE
+class CStdOutFileStream:
+  public ISequentialOutStream,
+  public CMyUnknownImp
+{
+public:
+  MY_UNKNOWN_IMP
+
+  virtual ~CStdOutFileStream() {}
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+};
+#endif
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/FilterCoder.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Common/FilterCoder.cpp
new file mode 100644
index 0000000..6b34260
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/FilterCoder.cpp
@@ -0,0 +1,256 @@
+// FilterCoder.cpp
+
+#include "StdAfx.h"
+
+#include "FilterCoder.h"
+extern "C"
+{
+#include "../../../C/Alloc.h"
+}
+#include "../../Common/Defs.h"
+#include "StreamUtils.h"
+
+static const UInt32 kBufferSize = 1 << 17;
+
+CFilterCoder::CFilterCoder()
+{
+  _buffer = (Byte *)::MidAlloc(kBufferSize);
+}
+
+CFilterCoder::~CFilterCoder()
+{
+  ::MidFree(_buffer);
+}
+
+HRESULT CFilterCoder::WriteWithLimit(ISequentialOutStream *outStream, UInt32 size)
+{
+  if (_outSizeIsDefined)
+  {
+    UInt64 remSize = _outSize - _nowPos64;
+    if (size > remSize)
+      size = (UInt32)remSize;
+  }
+  RINOK(WriteStream(outStream, _buffer, size));
+  _nowPos64 += size;
+  return S_OK;
+}
+
+
+STDMETHODIMP CFilterCoder::Code(ISequentialInStream *inStream,
+      ISequentialOutStream *outStream, const UInt64 * /* inSize */, const UInt64 *outSize,
+      ICompressProgressInfo *progress)
+{
+  RINOK(Init());
+  UInt32 bufferPos = 0;
+  _outSizeIsDefined = (outSize != 0);
+  if (_outSizeIsDefined)
+    _outSize = *outSize;
+
+  while(NeedMore())
+  {
+    size_t processedSize = kBufferSize - bufferPos;
+    
+    // Change it: It can be optimized using ReadPart
+    RINOK(ReadStream(inStream, _buffer + bufferPos, &processedSize));
+    
+    UInt32 endPos = bufferPos + (UInt32)processedSize;
+
+    bufferPos = Filter->Filter(_buffer, endPos);
+    if (bufferPos > endPos)
+    {
+      for (; endPos< bufferPos; endPos++)
+        _buffer[endPos] = 0;
+      bufferPos = Filter->Filter(_buffer, endPos);
+    }
+
+    if (bufferPos == 0)
+    {
+      if (endPos > 0)
+        return WriteWithLimit(outStream, endPos);
+      return S_OK;
+    }
+    RINOK(WriteWithLimit(outStream, bufferPos));
+    if (progress != NULL)
+    {
+      RINOK(progress->SetRatioInfo(&_nowPos64, &_nowPos64));
+    }
+    UInt32 i = 0;
+    while(bufferPos < endPos)
+      _buffer[i++] = _buffer[bufferPos++];
+    bufferPos = i;
+  }
+  return S_OK;
+}
+
+// #ifdef _ST_MODE
+STDMETHODIMP CFilterCoder::SetOutStream(ISequentialOutStream *outStream)
+{
+  _bufferPos = 0;
+  _outStream = outStream;
+  return Init();
+}
+
+STDMETHODIMP CFilterCoder::ReleaseOutStream()
+{
+  _outStream.Release();
+  return S_OK;
+};
+
+
+STDMETHODIMP CFilterCoder::Write(const void *data, UInt32 size, UInt32 *processedSize)
+{
+  UInt32 processedSizeTotal = 0;
+  while(size > 0)
+  {
+    UInt32 sizeMax = kBufferSize - _bufferPos;
+    UInt32 sizeTemp = size;
+    if (sizeTemp > sizeMax)
+      sizeTemp = sizeMax;
+    memmove(_buffer + _bufferPos, data, sizeTemp);
+    size -= sizeTemp;
+    processedSizeTotal += sizeTemp;
+    data = (const Byte *)data + sizeTemp;
+    UInt32 endPos = _bufferPos + sizeTemp;
+    _bufferPos = Filter->Filter(_buffer, endPos);
+    if (_bufferPos == 0)
+    {
+      _bufferPos = endPos;
+      break;
+    }
+    if (_bufferPos > endPos)
+    {
+      if (size != 0)
+        return E_FAIL;
+      break;
+    }
+    RINOK(WriteWithLimit(_outStream, _bufferPos));
+    UInt32 i = 0;
+    while(_bufferPos < endPos)
+      _buffer[i++] = _buffer[_bufferPos++];
+    _bufferPos = i;
+  }
+  if (processedSize != NULL)
+    *processedSize = processedSizeTotal;
+  return S_OK;
+}
+
+STDMETHODIMP CFilterCoder::Flush()
+{
+  if (_bufferPos != 0)
+  {
+    UInt32 endPos = Filter->Filter(_buffer, _bufferPos);
+    if (endPos > _bufferPos)
+    {
+      for (; _bufferPos < endPos; _bufferPos++)
+        _buffer[_bufferPos] = 0;
+      if (Filter->Filter(_buffer, endPos) != endPos)
+        return E_FAIL;
+    }
+    RINOK(WriteStream(_outStream, _buffer, _bufferPos));
+    _bufferPos = 0;
+  }
+  CMyComPtr<IOutStreamFlush> flush;
+  _outStream.QueryInterface(IID_IOutStreamFlush, &flush);
+  if (flush)
+    return  flush->Flush();
+  return S_OK;
+}
+
+
+STDMETHODIMP CFilterCoder::SetInStream(ISequentialInStream *inStream)
+{
+  _convertedPosBegin = _convertedPosEnd = _bufferPos = 0;
+  _inStream = inStream;
+  return Init();
+}
+
+STDMETHODIMP CFilterCoder::ReleaseInStream()
+{
+  _inStream.Release();
+  return S_OK;
+};
+
+STDMETHODIMP CFilterCoder::Read(void *data, UInt32 size, UInt32 *processedSize)
+{
+  UInt32 processedSizeTotal = 0;
+  while(size > 0)
+  {
+    if (_convertedPosBegin != _convertedPosEnd)
+    {
+      UInt32 sizeTemp = MyMin(size, _convertedPosEnd - _convertedPosBegin);
+      memmove(data, _buffer + _convertedPosBegin, sizeTemp);
+      _convertedPosBegin += sizeTemp;
+      data = (void *)((Byte *)data + sizeTemp);
+      size -= sizeTemp;
+      processedSizeTotal += sizeTemp;
+      break;
+    }
+    int i;
+    for (i = 0; _convertedPosEnd + i < _bufferPos; i++)
+      _buffer[i] = _buffer[i + _convertedPosEnd];
+    _bufferPos = i;
+    _convertedPosBegin = _convertedPosEnd = 0;
+    size_t processedSizeTemp = kBufferSize - _bufferPos;
+    RINOK(ReadStream(_inStream, _buffer + _bufferPos, &processedSizeTemp));
+    _bufferPos = _bufferPos + (UInt32)processedSizeTemp;
+    _convertedPosEnd = Filter->Filter(_buffer, _bufferPos);
+    if (_convertedPosEnd == 0)
+    {
+      if (_bufferPos == 0)
+        break;
+      else
+      {
+        _convertedPosEnd = _bufferPos; // check it
+        continue;
+      }
+    }
+    if (_convertedPosEnd > _bufferPos)
+    {
+      for (; _bufferPos < _convertedPosEnd; _bufferPos++)
+        _buffer[_bufferPos] = 0;
+      _convertedPosEnd = Filter->Filter(_buffer, _bufferPos);
+    }
+  }
+  if (processedSize != NULL)
+    *processedSize = processedSizeTotal;
+  return S_OK;
+}
+
+// #endif // _ST_MODE
+
+#ifndef _NO_CRYPTO
+STDMETHODIMP CFilterCoder::CryptoSetPassword(const Byte *data, UInt32 size)
+{
+  return _setPassword->CryptoSetPassword(data, size);
+}
+#endif
+
+#ifndef EXTRACT_ONLY
+STDMETHODIMP CFilterCoder::SetCoderProperties(const PROPID *propIDs,
+      const PROPVARIANT *properties, UInt32 numProperties)
+{
+  return _SetCoderProperties->SetCoderProperties(propIDs, properties, numProperties);
+}
+
+STDMETHODIMP CFilterCoder::WriteCoderProperties(ISequentialOutStream *outStream)
+{
+  return _writeCoderProperties->WriteCoderProperties(outStream);
+}
+
+/*
+STDMETHODIMP CFilterCoder::ResetSalt()
+{
+  return _CryptoResetSalt->ResetSalt();
+}
+*/
+
+STDMETHODIMP CFilterCoder::ResetInitVector()
+{
+  return _CryptoResetInitVector->ResetInitVector();
+}
+#endif
+
+STDMETHODIMP CFilterCoder::SetDecoderProperties2(const Byte *data, UInt32 size)
+{
+  return _setDecoderProperties->SetDecoderProperties2(data, size);
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/FilterCoder.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/FilterCoder.h
new file mode 100644
index 0000000..a0ec08c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/FilterCoder.h
@@ -0,0 +1,143 @@
+// FilterCoder.h
+
+#ifndef __FILTERCODER_H
+#define __FILTERCODER_H
+
+#include "../../Common/MyCom.h"
+#include "../ICoder.h"
+#include "../IPassword.h"
+
+#define MY_QUERYINTERFACE_ENTRY_AG(i, sub0, sub) if (iid == IID_ ## i) \
+{ if (!sub) RINOK(sub0->QueryInterface(IID_ ## i, (void **)&sub)) \
+*outObject = (void *)(i *)this; AddRef(); return S_OK; }
+
+class CFilterCoder:
+  public ICompressCoder,
+  // #ifdef _ST_MODE
+  public ICompressSetInStream,
+  public ISequentialInStream,
+  public ICompressSetOutStream,
+  public ISequentialOutStream,
+  public IOutStreamFlush,
+  // #endif
+
+  #ifndef _NO_CRYPTO
+  public ICryptoSetPassword,
+  #endif
+  #ifndef EXTRACT_ONLY
+  public ICompressSetCoderProperties,
+  public ICompressWriteCoderProperties,
+  // public ICryptoResetSalt,
+  public ICryptoResetInitVector,
+  #endif
+  public ICompressSetDecoderProperties2,
+  public CMyUnknownImp
+{
+protected:
+  Byte *_buffer;
+  // #ifdef _ST_MODE
+  CMyComPtr<ISequentialInStream> _inStream;
+  CMyComPtr<ISequentialOutStream> _outStream;
+  UInt32 _bufferPos;
+  UInt32 _convertedPosBegin;
+  UInt32 _convertedPosEnd;
+  // #endif
+  bool _outSizeIsDefined;
+  UInt64 _outSize;
+  UInt64 _nowPos64;
+
+  HRESULT Init()
+  {
+    _nowPos64 = 0;
+    _outSizeIsDefined = false;
+    return Filter->Init();
+  }
+
+  CMyComPtr<ICryptoSetPassword> _setPassword;
+  #ifndef EXTRACT_ONLY
+  CMyComPtr<ICompressSetCoderProperties> _SetCoderProperties;
+  CMyComPtr<ICompressWriteCoderProperties> _writeCoderProperties;
+  // CMyComPtr<ICryptoResetSalt> _CryptoResetSalt;
+  CMyComPtr<ICryptoResetInitVector> _CryptoResetInitVector;
+  #endif
+  CMyComPtr<ICompressSetDecoderProperties2> _setDecoderProperties;
+public:
+  CMyComPtr<ICompressFilter> Filter;
+
+  CFilterCoder();
+  ~CFilterCoder();
+  HRESULT WriteWithLimit(ISequentialOutStream *outStream, UInt32 size);
+  bool NeedMore() const
+    { return (!_outSizeIsDefined || (_nowPos64 < _outSize)); }
+
+public:
+  MY_QUERYINTERFACE_BEGIN
+    MY_QUERYINTERFACE_ENTRY(ICompressCoder)
+    // #ifdef _ST_MODE
+    MY_QUERYINTERFACE_ENTRY(ICompressSetInStream)
+    MY_QUERYINTERFACE_ENTRY(ISequentialInStream)
+
+    MY_QUERYINTERFACE_ENTRY(ICompressSetOutStream)
+    MY_QUERYINTERFACE_ENTRY(ISequentialOutStream)
+    MY_QUERYINTERFACE_ENTRY(IOutStreamFlush)
+    // #endif
+
+    #ifndef _NO_CRYPTO
+    MY_QUERYINTERFACE_ENTRY_AG(ICryptoSetPassword, Filter, _setPassword)
+    #endif
+
+    #ifndef EXTRACT_ONLY
+    MY_QUERYINTERFACE_ENTRY_AG(ICompressSetCoderProperties, Filter, _SetCoderProperties)
+    MY_QUERYINTERFACE_ENTRY_AG(ICompressWriteCoderProperties, Filter, _writeCoderProperties)
+    // MY_QUERYINTERFACE_ENTRY_AG(ICryptoResetSalt, Filter, _CryptoResetSalt)
+    MY_QUERYINTERFACE_ENTRY_AG(ICryptoResetInitVector, Filter, _CryptoResetInitVector)
+    #endif
+
+    MY_QUERYINTERFACE_ENTRY_AG(ICompressSetDecoderProperties2, Filter, _setDecoderProperties)
+  MY_QUERYINTERFACE_END
+  MY_ADDREF_RELEASE
+  STDMETHOD(Code)(ISequentialInStream *inStream,
+      ISequentialOutStream *outStream, const UInt64 *inSize, const UInt64 *outSize,
+      ICompressProgressInfo *progress);
+  // #ifdef _ST_MODE
+  STDMETHOD(ReleaseInStream)();
+  STDMETHOD(SetInStream)(ISequentialInStream *inStream);
+  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); \
+  STDMETHOD(SetOutStream)(ISequentialOutStream *outStream);
+  STDMETHOD(ReleaseOutStream)();
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+  STDMETHOD(Flush)();
+  // #endif
+
+  #ifndef _NO_CRYPTO
+  STDMETHOD(CryptoSetPassword)(const Byte *data, UInt32 size);
+  #endif
+  #ifndef EXTRACT_ONLY
+  STDMETHOD(SetCoderProperties)(const PROPID *propIDs,
+      const PROPVARIANT *properties, UInt32 numProperties);
+  STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStream);
+  // STDMETHOD(ResetSalt)();
+  STDMETHOD(ResetInitVector)();
+  #endif
+  STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size);
+};
+
+// #ifdef _ST_MODE
+class CInStreamReleaser
+{
+public:
+  CFilterCoder *FilterCoder;
+  CInStreamReleaser(): FilterCoder(0) {}
+  ~CInStreamReleaser() { if (FilterCoder) FilterCoder->ReleaseInStream(); }
+};
+
+class COutStreamReleaser
+{
+public:
+  CFilterCoder *FilterCoder;
+  COutStreamReleaser(): FilterCoder(0) {}
+  ~COutStreamReleaser() { if (FilterCoder) FilterCoder->ReleaseOutStream(); }
+};
+// #endif
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/InBuffer.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Common/InBuffer.cpp
new file mode 100644
index 0000000..29e3ea1
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/InBuffer.cpp
@@ -0,0 +1,83 @@
+// InBuffer.cpp
+
+#include "StdAfx.h"
+
+#include "InBuffer.h"
+
+extern "C"
+{
+  #include "../../../C/Alloc.h"
+}
+
+CInBuffer::CInBuffer():
+  _buffer(0),
+  _bufferLimit(0),
+  _bufferBase(0),
+  _stream(0),
+  _bufferSize(0)
+{}
+
+bool CInBuffer::Create(UInt32 bufferSize)
+{
+  const UInt32 kMinBlockSize = 1;
+  if (bufferSize < kMinBlockSize)
+    bufferSize = kMinBlockSize;
+  if (_bufferBase != 0 && _bufferSize == bufferSize)
+    return true;
+  Free();
+  _bufferSize = bufferSize;
+  _bufferBase = (Byte *)::MidAlloc(bufferSize);
+  return (_bufferBase != 0);
+}
+
+void CInBuffer::Free()
+{
+  ::MidFree(_bufferBase);
+  _bufferBase = 0;
+}
+
+void CInBuffer::SetStream(ISequentialInStream *stream)
+{
+  _stream = stream;
+}
+
+void CInBuffer::Init()
+{
+  _processedSize = 0;
+  _buffer = _bufferBase;
+  _bufferLimit = _buffer;
+  _wasFinished = false;
+  #ifdef _NO_EXCEPTIONS
+  ErrorCode = S_OK;
+  #endif
+}
+
+bool CInBuffer::ReadBlock()
+{
+  #ifdef _NO_EXCEPTIONS
+  if (ErrorCode != S_OK)
+    return false;
+  #endif
+  if (_wasFinished)
+    return false;
+  _processedSize += (_buffer - _bufferBase);
+  UInt32 numProcessedBytes;
+  HRESULT result = _stream->Read(_bufferBase, _bufferSize, &numProcessedBytes);
+  #ifdef _NO_EXCEPTIONS
+  ErrorCode = result;
+  #else
+  if (result != S_OK)
+    throw CInBufferException(result);
+  #endif
+  _buffer = _bufferBase;
+  _bufferLimit = _buffer + numProcessedBytes;
+  _wasFinished = (numProcessedBytes == 0);
+  return (!_wasFinished);
+}
+
+Byte CInBuffer::ReadBlock2()
+{
+  if(!ReadBlock())
+    return 0xFF;
+  return *_buffer++;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/InBuffer.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/InBuffer.h
new file mode 100644
index 0000000..75625bf
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/InBuffer.h
@@ -0,0 +1,81 @@
+// InBuffer.h
+
+#ifndef __INBUFFER_H
+#define __INBUFFER_H
+
+#include "../IStream.h"
+#include "../../Common/MyCom.h"
+#include "../../Common/MyException.h"
+
+#ifndef _NO_EXCEPTIONS
+struct CInBufferException: public CSystemException
+{
+  CInBufferException(HRESULT errorCode): CSystemException(errorCode) {}
+};
+#endif
+
+class CInBuffer
+{
+  Byte *_buffer;
+  Byte *_bufferLimit;
+  Byte *_bufferBase;
+  CMyComPtr<ISequentialInStream> _stream;
+  UInt64 _processedSize;
+  UInt32 _bufferSize;
+  bool _wasFinished;
+
+  bool ReadBlock();
+  Byte ReadBlock2();
+
+public:
+  #ifdef _NO_EXCEPTIONS
+  HRESULT ErrorCode;
+  #endif
+
+  CInBuffer();
+  ~CInBuffer() { Free(); }
+
+  bool Create(UInt32 bufferSize);
+  void Free();
+  
+  void SetStream(ISequentialInStream *stream);
+  void Init();
+  void ReleaseStream() { _stream.Release(); }
+
+  bool ReadByte(Byte &b)
+  {
+    if (_buffer >= _bufferLimit)
+      if (!ReadBlock())
+        return false;
+    b = *_buffer++;
+    return true;
+  }
+  Byte ReadByte()
+  {
+    if (_buffer >= _bufferLimit)
+      return ReadBlock2();
+    return *_buffer++;
+  }
+  UInt32 ReadBytes(Byte *buf, UInt32 size)
+  {
+    if ((UInt32)(_bufferLimit - _buffer) >= size)
+    {
+      for (UInt32 i = 0; i < size; i++)
+        buf[i] = _buffer[i];
+      _buffer += size;
+      return size;
+    }
+    for (UInt32 i = 0; i < size; i++)
+    {
+      if (_buffer >= _bufferLimit)
+        if (!ReadBlock())
+          return i;
+      buf[i] = *_buffer++;
+    }
+    return size;
+  }
+  UInt64 GetProcessedSize() const { return _processedSize + (_buffer - _bufferBase); }
+  bool WasFinished() const { return _wasFinished; }
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/InOutTempBuffer.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Common/InOutTempBuffer.cpp
new file mode 100644
index 0000000..4cc0b2f
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/InOutTempBuffer.cpp
@@ -0,0 +1,122 @@
+// InOutTempBuffer.cpp
+
+#include "StdAfx.h"
+
+#include "InOutTempBuffer.h"
+#include "../../Common/Defs.h"
+// #include "Windows/Defs.h"
+
+#include "StreamUtils.h"
+
+using namespace NWindows;
+using namespace NFile;
+using namespace NDirectory;
+
+static UInt32 kTmpBufferMemorySize = (1 << 20);
+
+static LPCTSTR kTempFilePrefixString = TEXT("iot");
+
+CInOutTempBuffer::CInOutTempBuffer():
+  _buffer(NULL)
+{
+}
+
+void CInOutTempBuffer::Create()
+{
+  _buffer = new Byte[kTmpBufferMemorySize];
+}
+
+CInOutTempBuffer::~CInOutTempBuffer()
+{
+  delete []_buffer;
+}
+void CInOutTempBuffer::InitWriting()
+{
+  _bufferPosition = 0;
+  _tmpFileCreated = false;
+  _fileSize = 0;
+}
+
+bool CInOutTempBuffer::WriteToFile(const void *data, UInt32 size)
+{
+  if (size == 0)
+    return true;
+  if(!_tmpFileCreated)
+  {
+    CSysString tempDirPath;
+    if(!MyGetTempPath(tempDirPath))
+      return false;
+    if (_tempFile.Create(tempDirPath, kTempFilePrefixString, _tmpFileName) == 0)
+      return false;
+    // _outFile.SetOpenCreationDispositionCreateAlways();
+    if(!_outFile.Create(_tmpFileName, true))
+      return false;
+    _tmpFileCreated = true;
+  }
+  UInt32 processedSize;
+  if(!_outFile.Write(data, size, processedSize))
+    return false;
+  _fileSize += processedSize;
+  return (processedSize == size);
+}
+
+bool CInOutTempBuffer::FlushWrite()
+{
+  return _outFile.Close();
+}
+
+bool CInOutTempBuffer::Write(const void *data, UInt32 size)
+{
+  if(_bufferPosition < kTmpBufferMemorySize)
+  {
+    UInt32 curSize = MyMin(kTmpBufferMemorySize - _bufferPosition, size);
+    memmove(_buffer + _bufferPosition, (const Byte *)data, curSize);
+    _bufferPosition += curSize;
+    size -= curSize;
+    data = ((const Byte *)data) + curSize;
+    _fileSize += curSize;
+  }
+  return WriteToFile(data, size);
+}
+
+bool CInOutTempBuffer::InitReading()
+{
+  _currentPositionInBuffer = 0;
+  if(_tmpFileCreated)
+    return _inFile.Open(_tmpFileName);
+  return true;
+}
+
+HRESULT CInOutTempBuffer::WriteToStream(ISequentialOutStream *stream)
+{
+  if (_currentPositionInBuffer < _bufferPosition)
+  {
+    UInt32 sizeToWrite = _bufferPosition - _currentPositionInBuffer;
+    RINOK(WriteStream(stream, _buffer + _currentPositionInBuffer, sizeToWrite));
+    _currentPositionInBuffer += sizeToWrite;
+  }
+  if (!_tmpFileCreated)
+    return true;
+  for (;;)
+  {
+    UInt32 localProcessedSize;
+    if (!_inFile.ReadPart(_buffer, kTmpBufferMemorySize, localProcessedSize))
+      return E_FAIL;
+    if (localProcessedSize == 0)
+      return S_OK;
+    RINOK(WriteStream(stream, _buffer, localProcessedSize));
+  }
+}
+
+STDMETHODIMP CSequentialOutTempBufferImp::Write(const void *data, UInt32 size, UInt32 *processedSize)
+{
+  if (!_buffer->Write(data, size))
+  {
+    if (processedSize != NULL)
+      *processedSize = 0;
+    return E_FAIL;
+  }
+  if (processedSize != NULL)
+    *processedSize = size;
+  return S_OK;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/InOutTempBuffer.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/InOutTempBuffer.h
new file mode 100644
index 0000000..e1e0e97
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/InOutTempBuffer.h
@@ -0,0 +1,55 @@
+// Util/InOutTempBuffer.h
+
+#ifndef __IN_OUT_TEMP_BUFFER_H
+#define __IN_OUT_TEMP_BUFFER_H
+
+#include "../../Windows/FileIO.h"
+#include "../../Windows/FileDir.h"
+#include "../../Common/MyCom.h"
+
+#include "../IStream.h"
+
+class CInOutTempBuffer
+{
+  NWindows::NFile::NDirectory::CTempFile _tempFile;
+  NWindows::NFile::NIO::COutFile _outFile;
+  NWindows::NFile::NIO::CInFile _inFile;
+  Byte *_buffer;
+  UInt32 _bufferPosition;
+  UInt32 _currentPositionInBuffer;
+  CSysString _tmpFileName;
+  bool _tmpFileCreated;
+
+  UInt64 _fileSize;
+
+  bool WriteToFile(const void *data, UInt32 size);
+public:
+  CInOutTempBuffer();
+  ~CInOutTempBuffer();
+  void Create();
+
+  void InitWriting();
+  bool Write(const void *data, UInt32 size);
+  UInt64 GetDataSize() const { return _fileSize; }
+  bool FlushWrite();
+  bool InitReading();
+  HRESULT WriteToStream(ISequentialOutStream *stream);
+};
+
+class CSequentialOutTempBufferImp:
+  public ISequentialOutStream,
+  public CMyUnknownImp
+{
+  CInOutTempBuffer *_buffer;
+public:
+  // CSequentialOutStreamImp(): _size(0) {}
+  // UInt32 _size;
+  void Init(CInOutTempBuffer *buffer)  { _buffer = buffer; }
+  // UInt32 GetSize() const { return _size; }
+
+  MY_UNKNOWN_IMP
+
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/LimitedStreams.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Common/LimitedStreams.cpp
new file mode 100644
index 0000000..490aa37
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/LimitedStreams.cpp
@@ -0,0 +1,45 @@
+// LimitedStreams.cpp
+
+#include "StdAfx.h"
+
+#include "LimitedStreams.h"
+#include "../../Common/Defs.h"
+
+STDMETHODIMP CLimitedSequentialInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
+{
+  UInt32 realProcessedSize = 0;
+  UInt32 sizeToRead = (UInt32)MyMin((_size - _pos), (UInt64)size);
+  HRESULT result = S_OK;
+  if (sizeToRead > 0)
+  {
+    result = _stream->Read(data, sizeToRead, &realProcessedSize);
+    _pos += realProcessedSize;
+    if (realProcessedSize == 0)
+      _wasFinished = true;
+  }
+  if(processedSize != NULL)
+    *processedSize = realProcessedSize;
+  return result;
+}
+
+STDMETHODIMP CLimitedSequentialOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
+{
+  HRESULT result = S_OK;
+  if (processedSize != NULL)
+    *processedSize = 0;
+  if (size > _size)
+  {
+    size = (UInt32)_size;
+    if (size == 0)
+    {
+      _overflow = true;
+      return E_FAIL;
+    }
+  }
+  if (_stream)
+    result = _stream->Write(data, size, &size);
+  _size -= size;
+  if (processedSize != NULL)
+    *processedSize = size;
+  return result;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/LimitedStreams.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/LimitedStreams.h
new file mode 100644
index 0000000..9bfdc8e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/LimitedStreams.h
@@ -0,0 +1,54 @@
+// LimitedStreams.h
+
+#ifndef __LIMITEDSTREAMS_H
+#define __LIMITEDSTREAMS_H
+
+#include "../../Common/MyCom.h"
+#include "../IStream.h"
+
+class CLimitedSequentialInStream:
+  public ISequentialInStream,
+  public CMyUnknownImp
+{
+  CMyComPtr<ISequentialInStream> _stream;
+  UInt64 _size;
+  UInt64 _pos;
+  bool _wasFinished;
+public:
+  void SetStream(ISequentialInStream *stream) { _stream = stream; }
+  void Init(UInt64 streamSize)
+  {
+    _size = streamSize;
+    _pos = 0;
+    _wasFinished = false;
+  }
+ 
+  MY_UNKNOWN_IMP
+
+  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
+  UInt64 GetSize() const { return _pos; }
+  bool WasFinished() const { return _wasFinished; }
+};
+
+class CLimitedSequentialOutStream:
+  public ISequentialOutStream,
+  public CMyUnknownImp
+{
+  CMyComPtr<ISequentialOutStream> _stream;
+  UInt64 _size;
+  bool _overflow;
+public:
+  MY_UNKNOWN_IMP
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+  void SetStream(ISequentialOutStream *stream) { _stream = stream; }
+  void ReleaseStream() { _stream.Release(); }
+  void Init(UInt64 size)
+  {
+    _size = size;
+    _overflow = false;
+  }
+  bool IsFinishedOK() const { return (_size == 0 && !_overflow); }
+  UInt64 GetRem() const { return _size; }
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/LockedStream.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Common/LockedStream.cpp
new file mode 100644
index 0000000..f05601c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/LockedStream.cpp
@@ -0,0 +1,23 @@
+// LockedStream.cpp
+
+#include "StdAfx.h"
+
+#include "LockedStream.h"
+
+HRESULT CLockedInStream::Read(UInt64 startPos, void *data, UInt32 size,
+  UInt32 *processedSize)
+{
+  NWindows::NSynchronization::CCriticalSectionLock lock(_criticalSection);
+  RINOK(_stream->Seek(startPos, STREAM_SEEK_SET, NULL));
+  return _stream->Read(data, size, processedSize);
+}
+
+STDMETHODIMP CLockedSequentialInStreamImp::Read(void *data, UInt32 size, UInt32 *processedSize)
+{
+  UInt32 realProcessedSize = 0;
+  HRESULT result = _lockedInStream->Read(_pos, data, size, &realProcessedSize);
+  _pos += realProcessedSize;
+  if (processedSize != NULL)
+    *processedSize = realProcessedSize;
+  return result;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/LockedStream.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/LockedStream.h
new file mode 100644
index 0000000..486e422
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/LockedStream.h
@@ -0,0 +1,38 @@
+// LockedStream.h
+
+#ifndef __LOCKEDSTREAM_H
+#define __LOCKEDSTREAM_H
+
+#include "../../Windows/Synchronization.h"
+#include "../../Common/MyCom.h"
+#include "../IStream.h"
+
+class CLockedInStream
+{
+  CMyComPtr<IInStream> _stream;
+  NWindows::NSynchronization::CCriticalSection _criticalSection;
+public:
+  void Init(IInStream *stream)
+    { _stream = stream; }
+  HRESULT Read(UInt64 startPos, void *data, UInt32 size, UInt32 *processedSize);
+};
+
+class CLockedSequentialInStreamImp:
+  public ISequentialInStream,
+  public CMyUnknownImp
+{
+  CLockedInStream *_lockedInStream;
+  UInt64 _pos;
+public:
+  void Init(CLockedInStream *lockedInStream, UInt64 startPos)
+  {
+    _lockedInStream = lockedInStream;
+    _pos = startPos;
+  }
+
+  MY_UNKNOWN_IMP
+
+  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/MethodId.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Common/MethodId.cpp
new file mode 100644
index 0000000..b797b68
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/MethodId.cpp
@@ -0,0 +1,27 @@
+// MethodId.cpp
+
+#include "StdAfx.h"
+
+#include "MethodId.h"
+#include "../../Common/MyString.h"
+
+static inline wchar_t GetHex(Byte value)
+{
+  return (wchar_t)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
+}
+
+UString ConvertMethodIdToString(UInt64 id)
+{
+  wchar_t s[32];
+  int len = 32;
+  s[--len] = 0;
+  do
+  {
+    s[--len] = GetHex((Byte)id & 0xF);
+    id >>= 4;
+    s[--len] = GetHex((Byte)id & 0xF);
+    id >>= 4;
+  }
+  while (id != 0);
+  return s + len;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/MethodId.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/MethodId.h
new file mode 100644
index 0000000..54ebc9f
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/MethodId.h
@@ -0,0 +1,10 @@
+// MethodId.h
+
+#ifndef __7Z_METHOD_ID_H
+#define __7Z_METHOD_ID_H
+
+#include "../../Common/Types.h"
+
+typedef UInt64 CMethodId;
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/MethodProps.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Common/MethodProps.cpp
new file mode 100644
index 0000000..86adc6b
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/MethodProps.cpp
@@ -0,0 +1,99 @@
+// MethodProps.cpp
+
+#include "StdAfx.h"
+
+#include "../../Common/MyCom.h"
+
+#include "../ICoder.h"
+
+#include "MethodProps.h"
+
+static UInt64 k_LZMA = 0x030101;
+// static UInt64 k_LZMA2 = 0x030102;
+
+HRESULT SetMethodProperties(const CMethod &method, const UInt64 *inSizeForReduce, IUnknown *coder)
+{
+  bool tryReduce = false;
+  UInt32 reducedDictionarySize = 1 << 10;
+  if (inSizeForReduce != 0 && (method.Id == k_LZMA /* || methodFull.MethodID == k_LZMA2 */))
+  {
+    for (;;)
+    {
+      const UInt32 step = (reducedDictionarySize >> 1);
+      if (reducedDictionarySize >= *inSizeForReduce)
+      {
+        tryReduce = true;
+        break;
+      }
+      reducedDictionarySize += step;
+      if (reducedDictionarySize >= *inSizeForReduce)
+      {
+        tryReduce = true;
+        break;
+      }
+      if (reducedDictionarySize >= ((UInt32)3 << 30))
+        break;
+      reducedDictionarySize += step;
+    }
+  }
+
+  {
+    int numProps = method.Props.Size();
+    CMyComPtr<ICompressSetCoderProperties> setCoderProperties;
+    coder->QueryInterface(IID_ICompressSetCoderProperties, (void **)&setCoderProperties);
+    if (setCoderProperties == NULL)
+    {
+      if (numProps != 0)
+        return E_INVALIDARG;
+    }
+    else
+    {
+      CRecordVector<PROPID> propIDs;
+      NWindows::NCOM::CPropVariant *values = new NWindows::NCOM::CPropVariant[numProps];
+      HRESULT res = S_OK;
+      try
+      {
+        for (int i = 0; i < numProps; i++)
+        {
+          const CProp &prop = method.Props[i];
+          propIDs.Add(prop.Id);
+          NWindows::NCOM::CPropVariant &value = values[i];
+          value = prop.Value;
+          // if (tryReduce && prop.Id == NCoderPropID::kDictionarySize && value.vt == VT_UI4 && reducedDictionarySize < value.ulVal)
+          if (tryReduce)
+            if (prop.Id == NCoderPropID::kDictionarySize)
+              if (value.vt == VT_UI4)
+                if (reducedDictionarySize < value.ulVal)
+            value.ulVal = reducedDictionarySize;
+        }
+        CMyComPtr<ICompressSetCoderProperties> setCoderProperties;
+        coder->QueryInterface(IID_ICompressSetCoderProperties, (void **)&setCoderProperties);
+        res = setCoderProperties->SetCoderProperties(&propIDs.Front(), values, numProps);
+      }
+      catch(...)
+      {
+        delete []values;
+        throw;
+      }
+      delete []values;
+      RINOK(res);
+    }
+  }
+ 
+  /*
+  CMyComPtr<ICompressWriteCoderProperties> writeCoderProperties;
+  coder->QueryInterface(IID_ICompressWriteCoderProperties, (void **)&writeCoderProperties);
+  if (writeCoderProperties != NULL)
+  {
+    CSequentialOutStreamImp *outStreamSpec = new CSequentialOutStreamImp;
+    CMyComPtr<ISequentialOutStream> outStream(outStreamSpec);
+    outStreamSpec->Init();
+    RINOK(writeCoderProperties->WriteCoderProperties(outStream));
+    size_t size = outStreamSpec->GetSize();
+    filterProps.SetCapacity(size);
+    memmove(filterProps, outStreamSpec->GetBuffer(), size);
+  }
+  */
+  return S_OK;
+}
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/MethodProps.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/MethodProps.h
new file mode 100644
index 0000000..c0761c5
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/MethodProps.h
@@ -0,0 +1,41 @@
+// MethodProps.h
+
+#ifndef __7Z_METHOD_PROPS_H
+#define __7Z_METHOD_PROPS_H
+
+#include "../../Common/MyVector.h"
+
+#include "../../Windows/PropVariant.h"
+
+#include "MethodId.h"
+
+struct CProp
+{
+  PROPID Id;
+  NWindows::NCOM::CPropVariant Value;
+};
+
+struct CMethod
+{
+  CMethodId Id;
+  CObjectVector<CProp> Props;
+};
+
+struct CMethodsMode
+{
+  CObjectVector<CMethod> Methods;
+  #ifdef COMPRESS_MT
+  UInt32 NumThreads;
+  #endif
+
+  CMethodsMode()
+      #ifdef COMPRESS_MT
+      : NumThreads(1)
+      #endif
+  {}
+  bool IsEmpty() const { return Methods.IsEmpty() ; }
+};
+
+HRESULT SetMethodProperties(const CMethod &method, const UInt64 *inSizeForReduce, IUnknown *coder);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/OffsetStream.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Common/OffsetStream.cpp
new file mode 100644
index 0000000..24fcf16
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/OffsetStream.cpp
@@ -0,0 +1,35 @@
+// OffsetStream.cpp
+
+#include "StdAfx.h"
+
+#include "Common/Defs.h"
+#include "OffsetStream.h"
+
+HRESULT COffsetOutStream::Init(IOutStream *stream, UInt64 offset)
+{
+  _offset = offset;
+  _stream = stream;
+  return _stream->Seek(offset, STREAM_SEEK_SET, NULL);
+}
+
+STDMETHODIMP COffsetOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
+{
+  return _stream->Write(data, size, processedSize);
+}
+
+STDMETHODIMP COffsetOutStream::Seek(Int64 offset, UInt32 seekOrigin,
+    UInt64 *newPosition)
+{
+  UInt64 absoluteNewPosition;
+  if (seekOrigin == STREAM_SEEK_SET)
+    offset += _offset;
+  HRESULT result = _stream->Seek(offset, seekOrigin, &absoluteNewPosition);
+  if (newPosition != NULL)
+    *newPosition = absoluteNewPosition - _offset;
+  return result;
+}
+
+STDMETHODIMP COffsetOutStream::SetSize(Int64 newSize)
+{
+  return _stream->SetSize(_offset + newSize);
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/OffsetStream.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/OffsetStream.h
new file mode 100644
index 0000000..5a69404
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/OffsetStream.h
@@ -0,0 +1,25 @@
+// OffsetStream.h
+
+#ifndef __OFFSETSTREAM_H
+#define __OFFSETSTREAM_H
+
+#include "Common/MyCom.h"
+#include "../IStream.h"
+
+class COffsetOutStream:
+  public IOutStream,
+  public CMyUnknownImp
+{
+  UInt64 _offset;
+  CMyComPtr<IOutStream> _stream;
+public:
+  HRESULT Init(IOutStream *stream, UInt64 offset);
+  
+  MY_UNKNOWN_IMP
+
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+  STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
+  STDMETHOD(SetSize)(Int64 newSize);
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/OutBuffer.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Common/OutBuffer.cpp
new file mode 100644
index 0000000..998525c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/OutBuffer.cpp
@@ -0,0 +1,119 @@
+// OutByte.cpp
+
+#include "StdAfx.h"
+
+#include "OutBuffer.h"
+
+extern "C"
+{
+  #include "../../../C/Alloc.h"
+}
+
+bool COutBuffer::Create(UInt32 bufferSize)
+{
+  const UInt32 kMinBlockSize = 1;
+  if (bufferSize < kMinBlockSize)
+    bufferSize = kMinBlockSize;
+  if (_buffer != 0 && _bufferSize == bufferSize)
+    return true;
+  Free();
+  _bufferSize = bufferSize;
+  _buffer = (Byte *)::MidAlloc(bufferSize);
+  return (_buffer != 0);
+}
+
+void COutBuffer::Free()
+{
+  ::MidFree(_buffer);
+  _buffer = 0;
+}
+
+void COutBuffer::SetStream(ISequentialOutStream *stream)
+{
+  _stream = stream;
+}
+
+void COutBuffer::Init()
+{
+  _streamPos = 0;
+  _limitPos = _bufferSize;
+  _pos = 0;
+  _processedSize = 0;
+  _overDict = false;
+  #ifdef _NO_EXCEPTIONS
+  ErrorCode = S_OK;
+  #endif
+}
+
+UInt64 COutBuffer::GetProcessedSize() const
+{
+  UInt64 res = _processedSize + _pos - _streamPos;
+  if (_streamPos > _pos)
+    res += _bufferSize;
+  return res;
+}
+
+
+HRESULT COutBuffer::FlushPart()
+{
+  // _streamPos < _bufferSize
+  UInt32 size = (_streamPos >= _pos) ? (_bufferSize - _streamPos) : (_pos - _streamPos);
+  HRESULT result = S_OK;
+  #ifdef _NO_EXCEPTIONS
+  result = ErrorCode;
+  #endif
+  if (_buffer2 != 0)
+  {
+    memmove(_buffer2, _buffer + _streamPos, size);
+    _buffer2 += size;
+  }
+
+  if (_stream != 0
+      #ifdef _NO_EXCEPTIONS
+      && (ErrorCode == S_OK)
+      #endif
+     )
+  {
+    UInt32 processedSize = 0;
+    result = _stream->Write(_buffer + _streamPos, size, &processedSize);
+    size = processedSize;
+  }
+  _streamPos += size;
+  if (_streamPos == _bufferSize)
+    _streamPos = 0;
+  if (_pos == _bufferSize)
+  {
+    _overDict = true;
+    _pos = 0;
+  }
+  _limitPos = (_streamPos > _pos) ? _streamPos : _bufferSize;
+  _processedSize += size;
+  return result;
+}
+
+HRESULT COutBuffer::Flush()
+{
+  #ifdef _NO_EXCEPTIONS
+  if (ErrorCode != S_OK)
+    return ErrorCode;
+  #endif
+
+  while(_streamPos != _pos)
+  {
+    HRESULT result = FlushPart();
+    if (result != S_OK)
+      return result;
+  }
+  return S_OK;
+}
+
+void COutBuffer::FlushWithCheck()
+{
+  HRESULT result = Flush();
+  #ifdef _NO_EXCEPTIONS
+  ErrorCode = result;
+  #else
+  if (result != S_OK)
+    throw COutBufferException(result);
+  #endif
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/OutBuffer.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/OutBuffer.h
new file mode 100644
index 0000000..62e77ca
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/OutBuffer.h
@@ -0,0 +1,64 @@
+// OutBuffer.h
+
+#ifndef __OUTBUFFER_H
+#define __OUTBUFFER_H
+
+#include "../IStream.h"
+#include "../../Common/MyCom.h"
+#include "../../Common/MyException.h"
+
+#ifndef _NO_EXCEPTIONS
+struct COutBufferException: public CSystemException
+{
+  COutBufferException(HRESULT errorCode): CSystemException(errorCode) {}
+};
+#endif
+
+class COutBuffer
+{
+protected:
+  Byte *_buffer;
+  UInt32 _pos;
+  UInt32 _limitPos;
+  UInt32 _streamPos;
+  UInt32 _bufferSize;
+  CMyComPtr<ISequentialOutStream> _stream;
+  UInt64 _processedSize;
+  Byte  *_buffer2;
+  bool _overDict;
+
+  HRESULT FlushPart();
+public:
+  #ifdef _NO_EXCEPTIONS
+  HRESULT ErrorCode;
+  #endif
+
+  COutBuffer(): _buffer(0), _pos(0), _stream(0), _buffer2(0) {}
+  ~COutBuffer() { Free(); }
+  
+  bool Create(UInt32 bufferSize);
+  void Free();
+
+  void SetMemStream(Byte *buffer) { _buffer2 = buffer; }
+  void SetStream(ISequentialOutStream *stream);
+  void Init();
+  HRESULT Flush();
+  void FlushWithCheck();
+  void ReleaseStream() {  _stream.Release(); }
+
+  void WriteByte(Byte b)
+  {
+    _buffer[_pos++] = b;
+    if(_pos == _limitPos)
+      FlushWithCheck();
+  }
+  void WriteBytes(const void *data, size_t size)
+  {
+    for (size_t i = 0; i < size; i++)
+      WriteByte(((const Byte *)data)[i]);
+  }
+
+  UInt64 GetProcessedSize() const;
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/ProgressUtils.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Common/ProgressUtils.cpp
new file mode 100644
index 0000000..f24ff6b
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/ProgressUtils.cpp
@@ -0,0 +1,42 @@
+// ProgressUtils.h
+
+#include "StdAfx.h"
+
+#include "ProgressUtils.h"
+
+CLocalProgress::CLocalProgress()
+{
+  ProgressOffset = InSize = OutSize = 0;
+  SendRatio = SendProgress = true;
+}
+
+void CLocalProgress::Init(IProgress *progress, bool inSizeIsMain)
+{
+  _ratioProgress.Release();
+  _progress = progress;
+  _progress.QueryInterface(IID_ICompressProgressInfo, &_ratioProgress);
+  _inSizeIsMain = inSizeIsMain;
+}
+
+STDMETHODIMP CLocalProgress::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
+{
+  UInt64 inSizeNew = InSize, outSizeNew = OutSize;
+  if (inSize)
+    inSizeNew += (*inSize);
+  if (outSize)
+    outSizeNew += (*outSize);
+  if (SendRatio && _ratioProgress)
+  {
+    RINOK(_ratioProgress->SetRatioInfo(&inSizeNew, &outSizeNew));
+  }
+  inSizeNew += ProgressOffset;
+  outSizeNew += ProgressOffset;
+  if (SendProgress)
+    return _progress->SetCompleted(_inSizeIsMain ? &inSizeNew : &outSizeNew);
+  return S_OK;
+}
+
+HRESULT CLocalProgress::SetCur()
+{
+  return SetRatioInfo(NULL, NULL);
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/ProgressUtils.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/ProgressUtils.h
new file mode 100644
index 0000000..bae5395
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/ProgressUtils.h
@@ -0,0 +1,34 @@
+// ProgressUtils.h
+
+#ifndef __PROGRESSUTILS_H
+#define __PROGRESSUTILS_H
+
+#include "../../Common/MyCom.h"
+
+#include "../ICoder.h"
+#include "../IProgress.h"
+
+class CLocalProgress:
+  public ICompressProgressInfo,
+  public CMyUnknownImp
+{
+  CMyComPtr<IProgress> _progress;
+  CMyComPtr<ICompressProgressInfo> _ratioProgress;
+  bool _inSizeIsMain;
+public:
+  UInt64 ProgressOffset;
+  UInt64 InSize;
+  UInt64 OutSize;
+  bool SendRatio;
+  bool SendProgress;
+
+  CLocalProgress();
+  void Init(IProgress *progress, bool inSizeIsMain);
+  HRESULT SetCur();
+
+  MY_UNKNOWN_IMP
+
+  STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/RegisterArc.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/RegisterArc.h
new file mode 100644
index 0000000..ba5ed7c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/RegisterArc.h
@@ -0,0 +1,36 @@
+// RegisterArc.h
+
+#ifndef __REGISTERARC_H
+#define __REGISTERARC_H
+
+#include "../Archive/IArchive.h"
+
+typedef IInArchive * (*CreateInArchiveP)();
+typedef IOutArchive * (*CreateOutArchiveP)();
+
+struct CArcInfo
+{
+  const wchar_t *Name;
+  const wchar_t *Ext;
+  const wchar_t *AddExt;
+  Byte ClassId;
+  Byte Signature[16];
+  int SignatureSize;
+  bool KeepName;
+  CreateInArchiveP CreateInArchive;
+  CreateOutArchiveP CreateOutArchive;
+};
+
+void RegisterArc(const CArcInfo *arcInfo);
+
+#define REGISTER_ARC_NAME(x) CRegister ## x
+
+#define REGISTER_ARC_DEC_SIG(x) struct REGISTER_ARC_NAME(x) { \
+    REGISTER_ARC_NAME(x)() { g_ArcInfo.Signature[0]--; RegisterArc(&g_ArcInfo); }}; \
+    static REGISTER_ARC_NAME(x) g_RegisterArc;
+
+#define REGISTER_ARC(x) struct REGISTER_ARC_NAME(x) { \
+    REGISTER_ARC_NAME(x)() { RegisterArc(&g_ArcInfo); }}; \
+    static REGISTER_ARC_NAME(x) g_RegisterArc;
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/RegisterCodec.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/RegisterCodec.h
new file mode 100644
index 0000000..786b4a4
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/RegisterCodec.h
@@ -0,0 +1,33 @@
+// RegisterCodec.h
+
+#ifndef __REGISTERCODEC_H
+#define __REGISTERCODEC_H
+
+#include "../Common/MethodId.h"
+
+typedef void * (*CreateCodecP)();
+struct CCodecInfo
+{
+  CreateCodecP CreateDecoder;
+  CreateCodecP CreateEncoder;
+  CMethodId Id;
+  const wchar_t *Name;
+  UInt32 NumInStreams;
+  bool IsFilter;
+};
+
+void RegisterCodec(const CCodecInfo *codecInfo);
+
+#define REGISTER_CODEC_NAME(x) CRegisterCodec ## x
+
+#define REGISTER_CODEC(x) struct REGISTER_CODEC_NAME(x) { \
+    REGISTER_CODEC_NAME(x)() { RegisterCodec(&g_CodecInfo); }}; \
+    static REGISTER_CODEC_NAME(x) g_RegisterCodec;
+
+#define REGISTER_CODECS_NAME(x) CRegisterCodecs ## x
+#define REGISTER_CODECS(x) struct REGISTER_CODECS_NAME(x) { \
+    REGISTER_CODECS_NAME(x)() { for (int i = 0; i < sizeof(g_CodecsInfo) / sizeof(g_CodecsInfo[0]); i++) \
+    RegisterCodec(&g_CodecsInfo[i]); }}; \
+    static REGISTER_CODECS_NAME(x) g_RegisterCodecs;
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/StdAfx.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/StdAfx.h
new file mode 100644
index 0000000..ef555ec
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/StdAfx.h
@@ -0,0 +1,9 @@
+// StdAfx.h
+
+#ifndef __STDAFX_H
+#define __STDAFX_H
+
+#include "../../Common/MyWindows.h"
+#include "../../Common/NewHandler.h"
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamBinder.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamBinder.cpp
new file mode 100644
index 0000000..03f8862
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamBinder.cpp
@@ -0,0 +1,150 @@
+// StreamBinder.cpp
+
+#include "StdAfx.h"
+
+#include "StreamBinder.h"
+#include "../../Common/Defs.h"
+#include "../../Common/MyCom.h"
+
+using namespace NWindows;
+using namespace NSynchronization;
+
+class CSequentialInStreamForBinder:
+  public ISequentialInStream,
+  public CMyUnknownImp
+{
+public:
+  MY_UNKNOWN_IMP
+
+  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
+private:
+  CStreamBinder *m_StreamBinder;
+public:
+  ~CSequentialInStreamForBinder() { m_StreamBinder->CloseRead(); }
+  void SetBinder(CStreamBinder *streamBinder) { m_StreamBinder = streamBinder; }
+};
+
+STDMETHODIMP CSequentialInStreamForBinder::Read(void *data, UInt32 size, UInt32 *processedSize)
+  { return m_StreamBinder->Read(data, size, processedSize); }
+
+class CSequentialOutStreamForBinder:
+  public ISequentialOutStream,
+  public CMyUnknownImp
+{
+public:
+  MY_UNKNOWN_IMP
+
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+
+private:
+  CStreamBinder *m_StreamBinder;
+public:
+  ~CSequentialOutStreamForBinder() {  m_StreamBinder->CloseWrite(); }
+  void SetBinder(CStreamBinder *streamBinder) { m_StreamBinder = streamBinder; }
+};
+
+STDMETHODIMP CSequentialOutStreamForBinder::Write(const void *data, UInt32 size, UInt32 *processedSize)
+  { return m_StreamBinder->Write(data, size, processedSize); }
+
+
+//////////////////////////
+// CStreamBinder
+// (_thereAreBytesToReadEvent && _bufferSize == 0) means that stream is finished.
+
+HRes CStreamBinder::CreateEvents()
+{
+  RINOK(_allBytesAreWritenEvent.Create(true));
+  RINOK(_thereAreBytesToReadEvent.Create());
+  return _readStreamIsClosedEvent.Create();
+}
+
+void CStreamBinder::ReInit()
+{
+  _thereAreBytesToReadEvent.Reset();
+  _readStreamIsClosedEvent.Reset();
+  ProcessedSize = 0;
+}
+
+
+  
+void CStreamBinder::CreateStreams(ISequentialInStream **inStream,
+      ISequentialOutStream **outStream)
+{
+  CSequentialInStreamForBinder *inStreamSpec = new
+      CSequentialInStreamForBinder;
+  CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
+  inStreamSpec->SetBinder(this);
+  *inStream = inStreamLoc.Detach();
+
+  CSequentialOutStreamForBinder *outStreamSpec = new
+      CSequentialOutStreamForBinder;
+  CMyComPtr<ISequentialOutStream> outStreamLoc(outStreamSpec);
+  outStreamSpec->SetBinder(this);
+  *outStream = outStreamLoc.Detach();
+
+  _buffer = NULL;
+  _bufferSize= 0;
+  ProcessedSize = 0;
+}
+
+HRESULT CStreamBinder::Read(void *data, UInt32 size, UInt32 *processedSize)
+{
+  UInt32 sizeToRead = size;
+  if (size > 0)
+  {
+    RINOK(_thereAreBytesToReadEvent.Lock());
+    sizeToRead = MyMin(_bufferSize, size);
+    if (_bufferSize > 0)
+    {
+      memcpy(data, _buffer, sizeToRead);
+      _buffer = ((const Byte *)_buffer) + sizeToRead;
+      _bufferSize -= sizeToRead;
+      if (_bufferSize == 0)
+      {
+        _thereAreBytesToReadEvent.Reset();
+        _allBytesAreWritenEvent.Set();
+      }
+    }
+  }
+  if (processedSize != NULL)
+    *processedSize = sizeToRead;
+  ProcessedSize += sizeToRead;
+  return S_OK;
+}
+
+void CStreamBinder::CloseRead()
+{
+  _readStreamIsClosedEvent.Set();
+}
+
+HRESULT CStreamBinder::Write(const void *data, UInt32 size, UInt32 *processedSize)
+{
+  if (size > 0)
+  {
+    _buffer = data;
+    _bufferSize = size;
+    _allBytesAreWritenEvent.Reset();
+    _thereAreBytesToReadEvent.Set();
+
+    HANDLE events[2];
+    events[0] = _allBytesAreWritenEvent;
+    events[1] = _readStreamIsClosedEvent;
+    DWORD waitResult = ::WaitForMultipleObjects(2, events, FALSE, INFINITE);
+    if (waitResult != WAIT_OBJECT_0 + 0)
+    {
+      // ReadingWasClosed = true;
+      return S_FALSE;
+    }
+    // if(!_allBytesAreWritenEvent.Lock())
+    //   return E_FAIL;
+  }
+  if (processedSize != NULL)
+    *processedSize = size;
+  return S_OK;
+}
+
+void CStreamBinder::CloseWrite()
+{
+  // _bufferSize must be = 0
+  _thereAreBytesToReadEvent.Set();
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamBinder.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamBinder.h
new file mode 100644
index 0000000..48f68e6
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamBinder.h
@@ -0,0 +1,32 @@
+// StreamBinder.h
+
+#ifndef __STREAMBINDER_H
+#define __STREAMBINDER_H
+
+#include "../IStream.h"
+#include "../../Windows/Synchronization.h"
+
+class CStreamBinder
+{
+  NWindows::NSynchronization::CManualResetEvent _allBytesAreWritenEvent;
+  NWindows::NSynchronization::CManualResetEvent _thereAreBytesToReadEvent;
+  NWindows::NSynchronization::CManualResetEvent _readStreamIsClosedEvent;
+  UInt32 _bufferSize;
+  const void *_buffer;
+public:
+  // bool ReadingWasClosed;
+  UInt64 ProcessedSize;
+  CStreamBinder() {}
+  HRes CreateEvents();
+
+  void CreateStreams(ISequentialInStream **inStream,
+      ISequentialOutStream **outStream);
+  HRESULT Read(void *data, UInt32 size, UInt32 *processedSize);
+  void CloseRead();
+
+  HRESULT Write(const void *data, UInt32 size, UInt32 *processedSize);
+  void CloseWrite();
+  void ReInit();
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamObjects.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamObjects.cpp
new file mode 100644
index 0000000..e043e56
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamObjects.cpp
@@ -0,0 +1,68 @@
+// StreamObjects.cpp
+
+#include "StdAfx.h"
+
+#include "StreamObjects.h"
+#include "../../Common/Defs.h"
+
+
+STDMETHODIMP CSequentialInStreamImp::Read(void *data, UInt32 size, UInt32 *processedSize)
+{
+  size_t rem = _size - _pos;
+  if (size < rem)
+    rem = (size_t)size;
+  memcpy(data, _dataPointer + _pos, rem);
+  _pos += rem;
+  if (processedSize != NULL)
+    *processedSize = (UInt32)rem;
+  return S_OK;
+}
+
+
+void CWriteBuffer::Write(const void *data, size_t size)
+{
+  size_t newCapacity = _size + size;
+  _buffer.EnsureCapacity(newCapacity);
+  memcpy(_buffer + _size, data, size);
+  _size += size;
+}
+
+STDMETHODIMP CSequentialOutStreamImp::Write(const void *data, UInt32 size, UInt32 *processedSize)
+{
+  _writeBuffer.Write(data, (size_t)size);
+  if(processedSize != NULL)
+    *processedSize = size;
+  return S_OK;
+}
+
+STDMETHODIMP CSequentialOutStreamImp2::Write(const void *data, UInt32 size, UInt32 *processedSize)
+{
+  size_t rem = _size - _pos;
+  if (size < rem)
+    rem = (size_t)size;
+  memcpy(_buffer + _pos, data, rem);
+  _pos += rem;
+  if (processedSize != NULL)
+    *processedSize = (UInt32)rem;
+  return (rem == size ? S_OK : E_FAIL);
+}
+
+STDMETHODIMP CSequentialInStreamSizeCount::Read(void *data, UInt32 size, UInt32 *processedSize)
+{
+  UInt32 realProcessedSize;
+  HRESULT result = _stream->Read(data, size, &realProcessedSize);
+  _size += realProcessedSize;
+  if (processedSize != 0)
+    *processedSize = realProcessedSize;
+  return result;
+}
+
+STDMETHODIMP CSequentialOutStreamSizeCount::Write(const void *data, UInt32 size, UInt32 *processedSize)
+{
+  UInt32 realProcessedSize;
+  HRESULT result = _stream->Write(data, size, &realProcessedSize);
+  _size += realProcessedSize;
+  if (processedSize != 0)
+    *processedSize = realProcessedSize;
+  return result;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamObjects.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamObjects.h
new file mode 100644
index 0000000..a21089c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamObjects.h
@@ -0,0 +1,117 @@
+// StreamObjects.h
+
+#ifndef __STREAMOBJECTS_H
+#define __STREAMOBJECTS_H
+
+#include "../../Common/DynamicBuffer.h"
+#include "../../Common/MyCom.h"
+#include "../IStream.h"
+
+class CSequentialInStreamImp:
+  public ISequentialInStream,
+  public CMyUnknownImp
+{
+  const Byte *_dataPointer;
+  size_t _size;
+  size_t _pos;
+
+public:
+  void Init(const Byte *dataPointer, size_t size)
+  {
+    _dataPointer = dataPointer;
+    _size = size;
+    _pos = 0;
+  }
+
+  MY_UNKNOWN_IMP
+
+  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
+};
+
+
+class CWriteBuffer
+{
+  CByteDynamicBuffer _buffer;
+  size_t _size;
+public:
+  CWriteBuffer(): _size(0) {}
+  void Init() { _size = 0;  }
+  void Write(const void *data, size_t size);
+  size_t GetSize() const { return _size; }
+  const CByteDynamicBuffer& GetBuffer() const { return _buffer; }
+};
+
+class CSequentialOutStreamImp:
+  public ISequentialOutStream,
+  public CMyUnknownImp
+{
+  CWriteBuffer _writeBuffer;
+public:
+  void Init() { _writeBuffer.Init(); }
+  size_t GetSize() const { return _writeBuffer.GetSize(); }
+  const CByteDynamicBuffer& GetBuffer() const { return _writeBuffer.GetBuffer(); }
+
+  MY_UNKNOWN_IMP
+
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+};
+
+class CSequentialOutStreamImp2:
+  public ISequentialOutStream,
+  public CMyUnknownImp
+{
+  Byte *_buffer;
+  size_t _size;
+  size_t _pos;
+public:
+
+  void Init(Byte *buffer, size_t size)
+  {
+    _buffer = buffer;
+    _pos = 0;
+    _size = size;
+  }
+
+  size_t GetPos() const { return _pos; }
+
+  MY_UNKNOWN_IMP
+
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+};
+
+class CSequentialInStreamSizeCount:
+  public ISequentialInStream,
+  public CMyUnknownImp
+{
+  CMyComPtr<ISequentialInStream> _stream;
+  UInt64 _size;
+public:
+  void Init(ISequentialInStream *stream)
+  {
+    _stream = stream;
+    _size = 0;
+  }
+  UInt64 GetSize() const { return _size; }
+
+  MY_UNKNOWN_IMP
+
+  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
+};
+
+class CSequentialOutStreamSizeCount:
+  public ISequentialOutStream,
+  public CMyUnknownImp
+{
+  CMyComPtr<ISequentialOutStream> _stream;
+  UInt64 _size;
+public:
+  void SetStream(ISequentialOutStream *stream) { _stream = stream; }
+  void Init() { _size = 0; }
+  UInt64 GetSize() const { return _size; }
+
+  MY_UNKNOWN_IMP
+
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamUtils.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamUtils.cpp
new file mode 100644
index 0000000..049e4aa
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamUtils.cpp
@@ -0,0 +1,56 @@
+// StreamUtils.cpp
+
+#include "StdAfx.h"
+
+#include "StreamUtils.h"
+
+static const UInt32 kBlockSize = ((UInt32)1 << 31);
+
+HRESULT ReadStream(ISequentialInStream *stream, void *data, size_t *processedSize)
+{
+  size_t size = *processedSize;
+  *processedSize = 0;
+  while (size != 0)
+  {
+    UInt32 curSize = (size < kBlockSize) ? (UInt32)size : kBlockSize;
+    UInt32 processedSizeLoc;
+    HRESULT res = stream->Read(data, curSize, &processedSizeLoc);
+    *processedSize += processedSizeLoc;
+    data = (void *)((Byte *)data + processedSizeLoc);
+    size -= processedSizeLoc;
+    RINOK(res);
+    if (processedSizeLoc == 0)
+      return S_OK;
+  }
+  return S_OK;
+}
+
+HRESULT ReadStream_FALSE(ISequentialInStream *stream, void *data, size_t size)
+{
+  size_t processedSize = size;
+  RINOK(ReadStream(stream, data, &processedSize));
+  return (size == processedSize) ? S_OK : S_FALSE;
+}
+
+HRESULT ReadStream_FAIL(ISequentialInStream *stream, void *data, size_t size)
+{
+  size_t processedSize = size;
+  RINOK(ReadStream(stream, data, &processedSize));
+  return (size == processedSize) ? S_OK : E_FAIL;
+}
+
+HRESULT WriteStream(ISequentialOutStream *stream, const void *data, size_t size)
+{
+  while (size != 0)
+  {
+    UInt32 curSize = (size < kBlockSize) ? (UInt32)size : kBlockSize;
+    UInt32 processedSizeLoc;
+    HRESULT res = stream->Write(data, curSize, &processedSizeLoc);
+    data = (const void *)((const Byte *)data + processedSizeLoc);
+    size -= processedSizeLoc;
+    RINOK(res);
+    if (processedSizeLoc == 0)
+      return E_FAIL;
+  }
+  return S_OK;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamUtils.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamUtils.h
new file mode 100644
index 0000000..f1cfd18
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/StreamUtils.h
@@ -0,0 +1,13 @@
+// StreamUtils.h
+
+#ifndef __STREAMUTILS_H
+#define __STREAMUTILS_H
+
+#include "../IStream.h"
+
+HRESULT ReadStream(ISequentialInStream *stream, void *data, size_t *size);
+HRESULT ReadStream_FALSE(ISequentialInStream *stream, void *data, size_t size);
+HRESULT ReadStream_FAIL(ISequentialInStream *stream, void *data, size_t size);
+HRESULT WriteStream(ISequentialOutStream *stream, const void *data, size_t size);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/VirtThread.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Common/VirtThread.cpp
new file mode 100644
index 0000000..f12581a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/VirtThread.cpp
@@ -0,0 +1,45 @@
+// VirtThread.cpp
+
+#include "StdAfx.h"
+
+#include "VirtThread.h"
+
+static THREAD_FUNC_DECL CoderThread(void *p)
+{
+  for (;;)
+  {
+    CVirtThread *t = (CVirtThread *)p;
+    t->StartEvent.Lock();
+    if (t->ExitEvent)
+      return 0;
+    t->Execute();
+    t->FinishedEvent.Set();
+  }
+}
+
+WRes CVirtThread::Create()
+{
+  RINOK(StartEvent.CreateIfNotCreated());
+  RINOK(FinishedEvent.CreateIfNotCreated());
+  StartEvent.Reset();
+  FinishedEvent.Reset();
+  ExitEvent = false;
+  if (Thread.IsCreated())
+    return S_OK;
+  return Thread.Create(CoderThread, this);
+}
+
+void CVirtThread::Start()
+{
+  ExitEvent = false;
+  StartEvent.Set();
+}
+
+CVirtThread::~CVirtThread()
+{
+  ExitEvent = true;
+  if (StartEvent.IsCreated())
+    StartEvent.Set();
+  Thread.Wait();
+}
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Common/VirtThread.h b/third_party/lzma/v4_65/files/CPP/7zip/Common/VirtThread.h
new file mode 100644
index 0000000..f14a1f2
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Common/VirtThread.h
@@ -0,0 +1,23 @@
+// VirtThread.h
+
+#ifndef __VIRTTHREAD_H
+#define __VIRTTHREAD_H
+
+#include "../../Windows/Synchronization.h"
+#include "../../Windows/Thread.h"
+
+struct CVirtThread
+{
+  NWindows::NSynchronization::CAutoResetEvent StartEvent;
+  NWindows::NSynchronization::CAutoResetEvent FinishedEvent;
+  NWindows::CThread Thread;
+  bool ExitEvent;
+
+  ~CVirtThread();
+  WRes Create();
+  void Start();
+  void WaitFinish() { FinishedEvent.Lock(); }
+  virtual void Execute() = 0;
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/Bcj2Coder.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/Bcj2Coder.cpp
new file mode 100644
index 0000000..e1356ab
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/Bcj2Coder.cpp
@@ -0,0 +1,393 @@
+// Bcj2Coder.cpp
+
+#include "StdAfx.h"
+
+extern "C"
+{
+#include "../../../C/Alloc.h"
+}
+
+#include "Bcj2Coder.h"
+
+namespace NCompress {
+namespace NBcj2 {
+
+inline bool IsJcc(Byte b0, Byte b1) { return (b0 == 0x0F && (b1 & 0xF0) == 0x80); }
+inline bool IsJ(Byte b0, Byte b1) { return ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1)); }
+inline unsigned GetIndex(Byte b0, Byte b1) { return ((b1 == 0xE8) ? b0 : ((b1 == 0xE9) ? 256 : 257)); }
+
+#ifndef EXTRACT_ONLY
+
+static const int kBufferSize = 1 << 17;
+
+static bool inline Test86MSByte(Byte b)
+{
+  return (b == 0 || b == 0xFF);
+}
+
+bool CEncoder::Create()
+{
+  if (!_mainStream.Create(1 << 16))
+    return false;
+  if (!_callStream.Create(1 << 20))
+    return false;
+  if (!_jumpStream.Create(1 << 20))
+    return false;
+  if (!_rangeEncoder.Create(1 << 20))
+    return false;
+  if (_buffer == 0)
+  {
+    _buffer = (Byte *)MidAlloc(kBufferSize);
+    if (_buffer == 0)
+      return false;
+  }
+  return true;
+}
+
+CEncoder::~CEncoder()
+{
+  ::MidFree(_buffer);
+}
+
+HRESULT CEncoder::Flush()
+{
+  RINOK(_mainStream.Flush());
+  RINOK(_callStream.Flush());
+  RINOK(_jumpStream.Flush());
+  _rangeEncoder.FlushData();
+  return _rangeEncoder.FlushStream();
+}
+
+const UInt32 kDefaultLimit = (1 << 24);
+
+HRESULT CEncoder::CodeReal(ISequentialInStream **inStreams,
+      const UInt64 **inSizes,
+      UInt32 numInStreams,
+      ISequentialOutStream **outStreams,
+      const UInt64 ** /* outSizes */,
+      UInt32 numOutStreams,
+      ICompressProgressInfo *progress)
+{
+  if (numInStreams != 1 || numOutStreams != 4)
+    return E_INVALIDARG;
+
+  if (!Create())
+    return E_OUTOFMEMORY;
+
+  bool sizeIsDefined = false;
+  UInt64 inSize = 0;
+  if (inSizes != NULL)
+    if (inSizes[0] != NULL)
+    {
+      inSize = *inSizes[0];
+      if (inSize <= kDefaultLimit)
+        sizeIsDefined = true;
+    }
+
+  ISequentialInStream *inStream = inStreams[0];
+
+  _mainStream.SetStream(outStreams[0]);
+  _mainStream.Init();
+  _callStream.SetStream(outStreams[1]);
+  _callStream.Init();
+  _jumpStream.SetStream(outStreams[2]);
+  _jumpStream.Init();
+  _rangeEncoder.SetStream(outStreams[3]);
+  _rangeEncoder.Init();
+  for (int i = 0; i < 256 + 2; i++)
+    _statusEncoder[i].Init();
+  CCoderReleaser releaser(this);
+
+  CMyComPtr<ICompressGetSubStreamSize> getSubStreamSize;
+  {
+    inStream->QueryInterface(IID_ICompressGetSubStreamSize, (void **)&getSubStreamSize);
+  }
+
+  UInt32 nowPos = 0;
+  UInt64 nowPos64 = 0;
+  UInt32 bufferPos = 0;
+
+  Byte prevByte = 0;
+
+  UInt64 subStreamIndex = 0;
+  UInt64 subStreamStartPos  = 0;
+  UInt64 subStreamEndPos = 0;
+
+  for (;;)
+  {
+    UInt32 processedSize = 0;
+    for (;;)
+    {
+      UInt32 size = kBufferSize - (bufferPos + processedSize);
+      UInt32 processedSizeLoc;
+      if (size == 0)
+        break;
+      RINOK(inStream->Read(_buffer + bufferPos + processedSize, size, &processedSizeLoc));
+      if (processedSizeLoc == 0)
+        break;
+      processedSize += processedSizeLoc;
+    }
+    UInt32 endPos = bufferPos + processedSize;
+    
+    if (endPos < 5)
+    {
+      // change it
+      for (bufferPos = 0; bufferPos < endPos; bufferPos++)
+      {
+        Byte b = _buffer[bufferPos];
+        _mainStream.WriteByte(b);
+        UInt32 index;
+        if (b == 0xE8)
+          index = prevByte;
+        else if (b == 0xE9)
+          index = 256;
+        else if (IsJcc(prevByte, b))
+          index = 257;
+        else
+        {
+          prevByte = b;
+          continue;
+        }
+        _statusEncoder[index].Encode(&_rangeEncoder, 0);
+        prevByte = b;
+      }
+      return Flush();
+    }
+
+    bufferPos = 0;
+
+    UInt32 limit = endPos - 5;
+    while(bufferPos <= limit)
+    {
+      Byte b = _buffer[bufferPos];
+      _mainStream.WriteByte(b);
+      if (!IsJ(prevByte, b))
+      {
+        bufferPos++;
+        prevByte = b;
+        continue;
+      }
+      Byte nextByte = _buffer[bufferPos + 4];
+      UInt32 src =
+        (UInt32(nextByte) << 24) |
+        (UInt32(_buffer[bufferPos + 3]) << 16) |
+        (UInt32(_buffer[bufferPos + 2]) << 8) |
+        (_buffer[bufferPos + 1]);
+      UInt32 dest = (nowPos + bufferPos + 5) + src;
+      // if (Test86MSByte(nextByte))
+      bool convert;
+      if (getSubStreamSize != NULL)
+      {
+        UInt64 currentPos = (nowPos64 + bufferPos);
+        while (subStreamEndPos < currentPos)
+        {
+          UInt64 subStreamSize;
+          HRESULT result = getSubStreamSize->GetSubStreamSize(subStreamIndex, &subStreamSize);
+          if (result == S_OK)
+          {
+            subStreamStartPos = subStreamEndPos;
+            subStreamEndPos += subStreamSize;
+            subStreamIndex++;
+          }
+          else if (result == S_FALSE || result == E_NOTIMPL)
+          {
+            getSubStreamSize.Release();
+            subStreamStartPos = 0;
+            subStreamEndPos = subStreamStartPos - 1;
+          }
+          else
+            return result;
+        }
+        if (getSubStreamSize == NULL)
+        {
+          if (sizeIsDefined)
+            convert = (dest < inSize);
+          else
+            convert = Test86MSByte(nextByte);
+        }
+        else if (subStreamEndPos - subStreamStartPos > kDefaultLimit)
+          convert = Test86MSByte(nextByte);
+        else
+        {
+          UInt64 dest64 = (currentPos + 5) + Int64(Int32(src));
+          convert = (dest64 >= subStreamStartPos && dest64 < subStreamEndPos);
+        }
+      }
+      else if (sizeIsDefined)
+        convert = (dest < inSize);
+      else
+        convert = Test86MSByte(nextByte);
+      unsigned index = GetIndex(prevByte, b);
+      if (convert)
+      {
+        _statusEncoder[index].Encode(&_rangeEncoder, 1);
+        bufferPos += 5;
+        COutBuffer &s = (b == 0xE8) ? _callStream : _jumpStream;
+        for (int i = 24; i >= 0; i -= 8)
+          s.WriteByte((Byte)(dest >> i));
+        prevByte = nextByte;
+      }
+      else
+      {
+        _statusEncoder[index].Encode(&_rangeEncoder, 0);
+        bufferPos++;
+        prevByte = b;
+      }
+    }
+    nowPos += bufferPos;
+    nowPos64 += bufferPos;
+
+    if (progress != NULL)
+    {
+      /*
+      const UInt64 compressedSize =
+        _mainStream.GetProcessedSize() +
+        _callStream.GetProcessedSize() +
+        _jumpStream.GetProcessedSize() +
+        _rangeEncoder.GetProcessedSize();
+      */
+      RINOK(progress->SetRatioInfo(&nowPos64, NULL));
+    }
+ 
+    UInt32 i = 0;
+    while(bufferPos < endPos)
+      _buffer[i++] = _buffer[bufferPos++];
+    bufferPos = i;
+  }
+}
+
+STDMETHODIMP CEncoder::Code(ISequentialInStream **inStreams,
+      const UInt64 **inSizes,
+      UInt32 numInStreams,
+      ISequentialOutStream **outStreams,
+      const UInt64 **outSizes,
+      UInt32 numOutStreams,
+      ICompressProgressInfo *progress)
+{
+  try
+  {
+    return CodeReal(inStreams, inSizes, numInStreams,
+      outStreams, outSizes,numOutStreams, progress);
+  }
+  catch(const COutBufferException &e) { return e.ErrorCode; }
+  catch(...) { return S_FALSE; }
+}
+
+#endif
+
+HRESULT CDecoder::CodeReal(ISequentialInStream **inStreams,
+      const UInt64 ** /* inSizes */,
+      UInt32 numInStreams,
+      ISequentialOutStream **outStreams,
+      const UInt64 ** /* outSizes */,
+      UInt32 numOutStreams,
+      ICompressProgressInfo *progress)
+{
+  if (numInStreams != 4 || numOutStreams != 1)
+    return E_INVALIDARG;
+
+  if (!_mainInStream.Create(1 << 16))
+    return E_OUTOFMEMORY;
+  if (!_callStream.Create(1 << 20))
+    return E_OUTOFMEMORY;
+  if (!_jumpStream.Create(1 << 16))
+    return E_OUTOFMEMORY;
+  if (!_rangeDecoder.Create(1 << 20))
+    return E_OUTOFMEMORY;
+  if (!_outStream.Create(1 << 16))
+    return E_OUTOFMEMORY;
+
+  _mainInStream.SetStream(inStreams[0]);
+  _callStream.SetStream(inStreams[1]);
+  _jumpStream.SetStream(inStreams[2]);
+  _rangeDecoder.SetStream(inStreams[3]);
+  _outStream.SetStream(outStreams[0]);
+
+  _mainInStream.Init();
+  _callStream.Init();
+  _jumpStream.Init();
+  _rangeDecoder.Init();
+  _outStream.Init();
+
+  for (int i = 0; i < 256 + 2; i++)
+    _statusDecoder[i].Init();
+
+  CCoderReleaser releaser(this);
+
+  Byte prevByte = 0;
+  UInt32 processedBytes = 0;
+  for (;;)
+  {
+    if (processedBytes >= (1 << 20) && progress != NULL)
+    {
+      /*
+      const UInt64 compressedSize =
+        _mainInStream.GetProcessedSize() +
+        _callStream.GetProcessedSize() +
+        _jumpStream.GetProcessedSize() +
+        _rangeDecoder.GetProcessedSize();
+      */
+      const UInt64 nowPos64 = _outStream.GetProcessedSize();
+      RINOK(progress->SetRatioInfo(NULL, &nowPos64));
+      processedBytes = 0;
+    }
+    UInt32 i;
+    Byte b = 0;
+    const UInt32 kBurstSize = (1 << 18);
+    for (i = 0; i < kBurstSize; i++)
+    {
+      if (!_mainInStream.ReadByte(b))
+        return Flush();
+      _outStream.WriteByte(b);
+      if (IsJ(prevByte, b))
+        break;
+      prevByte = b;
+    }
+    processedBytes += i;
+    if (i == kBurstSize)
+      continue;
+    unsigned index = GetIndex(prevByte, b);
+    if (_statusDecoder[index].Decode(&_rangeDecoder) == 1)
+    {
+      UInt32 src = 0;
+      CInBuffer &s = (b == 0xE8) ? _callStream : _jumpStream;
+      for (int i = 0; i < 4; i++)
+      {
+        Byte b0;
+        if(!s.ReadByte(b0))
+          return S_FALSE;
+        src <<= 8;
+        src |= ((UInt32)b0);
+      }
+      UInt32 dest = src - (UInt32(_outStream.GetProcessedSize()) + 4) ;
+      _outStream.WriteByte((Byte)(dest));
+      _outStream.WriteByte((Byte)(dest >> 8));
+      _outStream.WriteByte((Byte)(dest >> 16));
+      _outStream.WriteByte((Byte)(dest >> 24));
+      prevByte = (Byte)(dest >> 24);
+      processedBytes += 4;
+    }
+    else
+      prevByte = b;
+  }
+}
+
+STDMETHODIMP CDecoder::Code(ISequentialInStream **inStreams,
+      const UInt64 **inSizes,
+      UInt32 numInStreams,
+      ISequentialOutStream **outStreams,
+      const UInt64 **outSizes,
+      UInt32 numOutStreams,
+      ICompressProgressInfo *progress)
+{
+  try
+  {
+    return CodeReal(inStreams, inSizes, numInStreams,
+        outStreams, outSizes,numOutStreams, progress);
+  }
+  catch(const CInBufferException &e) { return e.ErrorCode; }
+  catch(const COutBufferException &e) { return e.ErrorCode; }
+  catch(...) { return S_FALSE; }
+}
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/Bcj2Coder.h b/third_party/lzma/v4_65/files/CPP/7zip/Compress/Bcj2Coder.h
new file mode 100644
index 0000000..b8b1e7a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/Bcj2Coder.h
@@ -0,0 +1,125 @@
+// Bcj2Coder.h
+
+#ifndef __COMPRESS_BCJ2_CODER_H
+#define __COMPRESS_BCJ2_CODER_H
+
+#include "../../Common/MyCom.h"
+
+#include "../ICoder.h"
+
+#include "RangeCoderBit.h"
+
+namespace NCompress {
+namespace NBcj2 {
+
+const int kNumMoveBits = 5;
+
+#ifndef EXTRACT_ONLY
+
+class CEncoder:
+  public ICompressCoder2,
+  public CMyUnknownImp
+{
+  Byte *_buffer;
+public:
+  CEncoder(): _buffer(0) {};
+  ~CEncoder();
+  bool Create();
+
+  COutBuffer _mainStream;
+  COutBuffer _callStream;
+  COutBuffer _jumpStream;
+  NCompress::NRangeCoder::CEncoder _rangeEncoder;
+  NCompress::NRangeCoder::CBitEncoder<kNumMoveBits> _statusEncoder[256 + 2];
+
+  HRESULT Flush();
+  void ReleaseStreams()
+  {
+    _mainStream.ReleaseStream();
+    _callStream.ReleaseStream();
+    _jumpStream.ReleaseStream();
+    _rangeEncoder.ReleaseStream();
+  }
+
+  class CCoderReleaser
+  {
+    CEncoder *_coder;
+  public:
+    CCoderReleaser(CEncoder *coder): _coder(coder) {}
+    ~CCoderReleaser() {  _coder->ReleaseStreams(); }
+  };
+
+public:
+
+  MY_UNKNOWN_IMP
+
+  HRESULT CodeReal(ISequentialInStream **inStreams,
+      const UInt64 **inSizes,
+      UInt32 numInStreams,
+      ISequentialOutStream **outStreams,
+      const UInt64 **outSizes,
+      UInt32 numOutStreams,
+      ICompressProgressInfo *progress);
+  STDMETHOD(Code)(ISequentialInStream **inStreams,
+      const UInt64 **inSizes,
+      UInt32 numInStreams,
+      ISequentialOutStream **outStreams,
+      const UInt64 **outSizes,
+      UInt32 numOutStreams,
+      ICompressProgressInfo *progress);
+};
+
+#endif
+
+class CDecoder:
+  public ICompressCoder2,
+  public CMyUnknownImp
+{
+public:
+  CInBuffer _mainInStream;
+  CInBuffer _callStream;
+  CInBuffer _jumpStream;
+  NCompress::NRangeCoder::CDecoder _rangeDecoder;
+  NCompress::NRangeCoder::CBitDecoder<kNumMoveBits> _statusDecoder[256 + 2];
+
+  COutBuffer _outStream;
+
+  void ReleaseStreams()
+  {
+    _mainInStream.ReleaseStream();
+    _callStream.ReleaseStream();
+    _jumpStream.ReleaseStream();
+    _rangeDecoder.ReleaseStream();
+    _outStream.ReleaseStream();
+  }
+
+  HRESULT Flush() { return _outStream.Flush(); }
+  class CCoderReleaser
+  {
+    CDecoder *_coder;
+  public:
+    CCoderReleaser(CDecoder *coder): _coder(coder) {}
+    ~CCoderReleaser()  { _coder->ReleaseStreams(); }
+  };
+
+public:
+  MY_UNKNOWN_IMP
+  HRESULT CodeReal(ISequentialInStream **inStreams,
+      const UInt64 **inSizes,
+      UInt32 numInStreams,
+      ISequentialOutStream **outStreams,
+      const UInt64 **outSizes,
+      UInt32 numOutStreams,
+      ICompressProgressInfo *progress);
+  STDMETHOD(Code)(ISequentialInStream **inStreams,
+      const UInt64 **inSizes,
+      UInt32 numInStreams,
+      ISequentialOutStream **outStreams,
+      const UInt64 **outSizes,
+      UInt32 numOutStreams,
+      ICompressProgressInfo *progress);
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/Bcj2Register.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/Bcj2Register.cpp
new file mode 100644
index 0000000..8eb1e73
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/Bcj2Register.cpp
@@ -0,0 +1,19 @@
+// Bcj2Register.cpp
+
+#include "StdAfx.h"
+
+#include "../Common/RegisterCodec.h"
+
+#include "Bcj2Coder.h"
+
+static void *CreateCodec() { return (void *)(ICompressCoder2 *)(new NCompress::NBcj2::CDecoder()); }
+#ifndef EXTRACT_ONLY
+static void *CreateCodecOut() { return (void *)(ICompressCoder2 *)(new NCompress::NBcj2::CEncoder());  }
+#else
+#define CreateCodecOut 0
+#endif
+
+static CCodecInfo g_CodecInfo =
+  { CreateCodec, CreateCodecOut, 0x0303011B, L"BCJ2", 4, false };
+
+REGISTER_CODEC(BCJ2)
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/BcjCoder.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/BcjCoder.cpp
new file mode 100644
index 0000000..0e34ef4
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/BcjCoder.cpp
@@ -0,0 +1,15 @@
+// BcjCoder.cpp
+
+#include "StdAfx.h"
+
+#include "BcjCoder.h"
+
+UInt32 CBCJ_x86_Encoder::SubFilter(Byte *data, UInt32 size)
+{
+  return (UInt32)::x86_Convert(data, size, _bufferPos, &_prevMask, 1);
+}
+
+UInt32 CBCJ_x86_Decoder::SubFilter(Byte *data, UInt32 size)
+{
+  return (UInt32)::x86_Convert(data, size, _bufferPos, &_prevMask, 0);
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/BcjCoder.h b/third_party/lzma/v4_65/files/CPP/7zip/Compress/BcjCoder.h
new file mode 100644
index 0000000..612bb54
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/BcjCoder.h
@@ -0,0 +1,22 @@
+// BcjCoder.h
+
+#ifndef __COMPRESS_BCJ_CODER_H
+#define __COMPRESS_BCJ_CODER_H
+
+extern "C"
+{
+#include "../../../C/Bra.h"
+}
+
+#include "BranchCoder.h"
+
+struct CBranch86
+{
+  UInt32 _prevMask;
+  void x86Init() { x86_Convert_Init(_prevMask); }
+};
+
+MyClassB(BCJ_x86, 0x01, 3, CBranch86 ,
+    virtual void SubInit() { x86Init(); })
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/BcjRegister.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/BcjRegister.cpp
new file mode 100644
index 0000000..648ad8e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/BcjRegister.cpp
@@ -0,0 +1,19 @@
+// BcjRegister.cpp
+
+#include "StdAfx.h"
+
+#include "../Common/RegisterCodec.h"
+
+#include "BcjCoder.h"
+
+static void *CreateCodec() { return (void *)(ICompressFilter *)(new CBCJ_x86_Decoder()); }
+#ifndef EXTRACT_ONLY
+static void *CreateCodecOut() { return (void *)(ICompressFilter *)(new CBCJ_x86_Encoder());  }
+#else
+#define CreateCodecOut 0
+#endif
+
+static CCodecInfo g_CodecInfo =
+  { CreateCodec, CreateCodecOut, 0x03030103, L"BCJ", 1, true };
+
+REGISTER_CODEC(BCJ)
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/BranchCoder.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/BranchCoder.cpp
new file mode 100644
index 0000000..4317095
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/BranchCoder.cpp
@@ -0,0 +1,19 @@
+// BranchCoder.cpp
+
+#include "StdAfx.h"
+
+#include "BranchCoder.h"
+
+STDMETHODIMP CBranchConverter::Init()
+{
+  _bufferPos = 0;
+  SubInit();
+  return S_OK;
+}
+
+STDMETHODIMP_(UInt32) CBranchConverter::Filter(Byte *data, UInt32 size)
+{
+  UInt32 processedSize = SubFilter(data, size);
+  _bufferPos += processedSize;
+  return processedSize;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/BranchCoder.h b/third_party/lzma/v4_65/files/CPP/7zip/Compress/BranchCoder.h
new file mode 100644
index 0000000..0e3a5c4
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/BranchCoder.h
@@ -0,0 +1,44 @@
+// BranchCoder.h
+
+#ifndef __COMPRESS_BRANCH_CODER_H
+#define __COMPRESS_BRANCH_CODER_H
+
+#include "../../Common/MyCom.h"
+
+#include "../ICoder.h"
+
+class CBranchConverter:
+  public ICompressFilter,
+  public CMyUnknownImp
+{
+protected:
+  UInt32 _bufferPos;
+  virtual void SubInit() {}
+  virtual UInt32 SubFilter(Byte *data, UInt32 size) = 0;
+public:
+  MY_UNKNOWN_IMP;
+  STDMETHOD(Init)();
+  STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size);
+};
+
+#define MyClassEncoderA(Name) class C ## Name: public CBranchConverter \
+  { public: UInt32 SubFilter(Byte *data, UInt32 size); };
+
+#define MyClassDecoderA(Name) class C ## Name: public CBranchConverter \
+  { public: UInt32 SubFilter(Byte *data, UInt32 size); };
+
+#define MyClassEncoderB(Name, ADD_ITEMS, ADD_INIT) class C ## Name: public CBranchConverter, public ADD_ITEMS \
+  { public: UInt32 SubFilter(Byte *data, UInt32 size); ADD_INIT};
+
+#define MyClassDecoderB(Name, ADD_ITEMS, ADD_INIT) class C ## Name: public CBranchConverter, public ADD_ITEMS \
+  { public: UInt32 SubFilter(Byte *data, UInt32 size); ADD_INIT};
+
+#define MyClassA(Name, id, subId)  \
+MyClassEncoderA(Name ## _Encoder) \
+MyClassDecoderA(Name ## _Decoder)
+
+#define MyClassB(Name, id, subId, ADD_ITEMS, ADD_INIT)  \
+MyClassEncoderB(Name ## _Encoder, ADD_ITEMS, ADD_INIT) \
+MyClassDecoderB(Name ## _Decoder, ADD_ITEMS, ADD_INIT)
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/BranchMisc.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/BranchMisc.cpp
new file mode 100644
index 0000000..5a19c44
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/BranchMisc.cpp
@@ -0,0 +1,40 @@
+// BranchMisc.cpp
+
+#include "StdAfx.h"
+
+extern "C"
+{
+#include "../../../C/Bra.h"
+}
+
+#include "BranchMisc.h"
+
+UInt32 CBC_ARM_Encoder::SubFilter(Byte *data, UInt32 size)
+  { return (UInt32)::ARM_Convert(data, size, _bufferPos, 1); }
+
+UInt32 CBC_ARM_Decoder::SubFilter(Byte *data, UInt32 size)
+  { return (UInt32)::ARM_Convert(data, size, _bufferPos, 0); }
+
+UInt32 CBC_ARMT_Encoder::SubFilter(Byte *data, UInt32 size)
+  { return (UInt32)::ARMT_Convert(data, size, _bufferPos, 1); }
+
+UInt32 CBC_ARMT_Decoder::SubFilter(Byte *data, UInt32 size)
+  { return (UInt32)::ARMT_Convert(data, size, _bufferPos, 0); }
+
+UInt32 CBC_PPC_Encoder::SubFilter(Byte *data, UInt32 size)
+  { return (UInt32)::PPC_Convert(data, size, _bufferPos, 1); }
+
+UInt32 CBC_PPC_Decoder::SubFilter(Byte *data, UInt32 size)
+  { return (UInt32)::PPC_Convert(data, size, _bufferPos, 0); }
+
+UInt32 CBC_SPARC_Encoder::SubFilter(Byte *data, UInt32 size)
+  { return (UInt32)::SPARC_Convert(data, size, _bufferPos, 1); }
+
+UInt32 CBC_SPARC_Decoder::SubFilter(Byte *data, UInt32 size)
+  { return (UInt32)::SPARC_Convert(data, size, _bufferPos, 0); }
+
+UInt32 CBC_IA64_Encoder::SubFilter(Byte *data, UInt32 size)
+  { return (UInt32)::IA64_Convert(data, size, _bufferPos, 1); }
+
+UInt32 CBC_IA64_Decoder::SubFilter(Byte *data, UInt32 size)
+  {  return (UInt32)::IA64_Convert(data, size, _bufferPos, 0); }
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/BranchMisc.h b/third_party/lzma/v4_65/files/CPP/7zip/Compress/BranchMisc.h
new file mode 100644
index 0000000..81198b2
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/BranchMisc.h
@@ -0,0 +1,14 @@
+// BranchMisc.h
+
+#ifndef __COMPRESS_BRANCH_MISC_H
+#define __COMPRESS_BRANCH_MISC_H
+
+#include "BranchCoder.h"
+
+MyClassA(BC_ARM,   0x05, 1)
+MyClassA(BC_ARMT,  0x07, 1)
+MyClassA(BC_PPC,   0x02, 5)
+MyClassA(BC_SPARC, 0x08, 5)
+MyClassA(BC_IA64,  0x04, 1)
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/BranchRegister.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/BranchRegister.cpp
new file mode 100644
index 0000000..380828c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/BranchRegister.cpp
@@ -0,0 +1,30 @@
+// BranchRegister.cpp
+
+#include "StdAfx.h"
+
+#include "../Common/RegisterCodec.h"
+
+#include "BranchMisc.h"
+
+#define CREATE_CODEC(x) \
+  static void *CreateCodec ## x() { return (void *)(ICompressFilter *)(new C ## x ## _Decoder); } \
+  static void *CreateCodec ## x ## Out() { return (void *)(ICompressFilter *)(new C ## x ## _Encoder); }
+
+CREATE_CODEC(BC_PPC)
+CREATE_CODEC(BC_IA64)
+CREATE_CODEC(BC_ARM)
+CREATE_CODEC(BC_ARMT)
+CREATE_CODEC(BC_SPARC)
+
+#define METHOD_ITEM(x, id1, id2, name) { CreateCodec ## x, CreateCodec ## x ## Out, 0x03030000 + (id1 * 256) + id2, name, 1, true  }
+
+static CCodecInfo g_CodecsInfo[] =
+{
+  METHOD_ITEM(BC_PPC,   0x02, 0x05, L"PPC"),
+  METHOD_ITEM(BC_IA64,  0x04, 1, L"IA64"),
+  METHOD_ITEM(BC_ARM,   0x05, 1, L"ARM"),
+  METHOD_ITEM(BC_ARMT,  0x07, 1, L"ARMT"),
+  METHOD_ITEM(BC_SPARC, 0x08, 0x05, L"SPARC")
+};
+
+REGISTER_CODECS(Branch)
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/ByteSwap.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/ByteSwap.cpp
new file mode 100644
index 0000000..3f252f2
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/ByteSwap.cpp
@@ -0,0 +1,38 @@
+// ByteSwap.cpp
+
+#include "StdAfx.h"
+
+#include "ByteSwap.h"
+
+STDMETHODIMP CByteSwap2::Init() { return S_OK; }
+
+STDMETHODIMP_(UInt32) CByteSwap2::Filter(Byte *data, UInt32 size)
+{
+  const UInt32 kStep = 2;
+  UInt32 i;
+  for (i = 0; i + kStep <= size; i += kStep)
+  {
+    Byte b = data[i];
+    data[i] = data[i + 1];
+    data[i + 1] = b;
+  }
+  return i;
+}
+
+STDMETHODIMP CByteSwap4::Init() { return S_OK; }
+
+STDMETHODIMP_(UInt32) CByteSwap4::Filter(Byte *data, UInt32 size)
+{
+  const UInt32 kStep = 4;
+  UInt32 i;
+  for (i = 0; i + kStep <= size; i += kStep)
+  {
+    Byte b0 = data[i];
+    Byte b1 = data[i + 1];
+    data[i] = data[i + 3];
+    data[i + 1] = data[i + 2];
+    data[i + 2] = b1;
+    data[i + 3] = b0;
+  }
+  return i;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/ByteSwap.h b/third_party/lzma/v4_65/files/CPP/7zip/Compress/ByteSwap.h
new file mode 100644
index 0000000..f13bec8
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/ByteSwap.h
@@ -0,0 +1,30 @@
+// ByteSwap.h
+
+#ifndef __COMPRESS_BYTE_SWAP_H
+#define __COMPRESS_BYTE_SWAP_H
+
+#include "../../Common/MyCom.h"
+
+#include "../ICoder.h"
+
+class CByteSwap2:
+  public ICompressFilter,
+  public CMyUnknownImp
+{
+public:
+  MY_UNKNOWN_IMP
+  STDMETHOD(Init)();
+  STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size);
+};
+
+class CByteSwap4:
+  public ICompressFilter,
+  public CMyUnknownImp
+{
+public:
+  MY_UNKNOWN_IMP
+  STDMETHOD(Init)();
+  STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size);
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/ByteSwapRegister.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/ByteSwapRegister.cpp
new file mode 100644
index 0000000..e3405ca
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/ByteSwapRegister.cpp
@@ -0,0 +1,18 @@
+// ByteSwapRegister.cpp
+
+#include "StdAfx.h"
+
+#include "../Common/RegisterCodec.h"
+
+#include "ByteSwap.h"
+
+static void *CreateCodec2() { return (void *)(ICompressFilter *)(new CByteSwap2); }
+static void *CreateCodec4() { return (void *)(ICompressFilter *)(new CByteSwap4); }
+
+static CCodecInfo g_CodecsInfo[] =
+{
+  { CreateCodec2, CreateCodec4, 0x020302, L"Swap2", 1, true },
+  { CreateCodec4, CreateCodec4, 0x020304, L"Swap4", 1, true }
+};
+
+REGISTER_CODECS(ByteSwap)
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/CodecExports.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/CodecExports.cpp
new file mode 100644
index 0000000..cd7a209
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/CodecExports.cpp
@@ -0,0 +1,157 @@
+// CodecExports.cpp
+
+#include "StdAfx.h"
+
+#include "../../Common/ComTry.h"
+#include "../../Windows/PropVariant.h"
+#include "../Common/RegisterCodec.h"
+#include "../ICoder.h"
+
+extern unsigned int g_NumCodecs;
+extern const CCodecInfo *g_Codecs[];
+
+static const UInt16 kDecodeId = 0x2790;
+
+DEFINE_GUID(CLSID_CCodec,
+0x23170F69, 0x40C1, kDecodeId, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+
+static inline HRESULT SetPropString(const char *s, unsigned int size, PROPVARIANT *value)
+{
+  if ((value->bstrVal = ::SysAllocStringByteLen(s, size)) != 0)
+    value->vt = VT_BSTR;
+  return S_OK;
+}
+
+static inline HRESULT SetPropGUID(const GUID &guid, PROPVARIANT *value)
+{
+  return SetPropString((const char *)&guid, sizeof(GUID), value);
+}
+
+static HRESULT SetClassID(CMethodId id, bool encode, PROPVARIANT *value)
+{
+  GUID clsId = CLSID_CCodec;
+  for (int i = 0; i < sizeof(id); i++, id >>= 8)
+    clsId.Data4[i] = (Byte)(id & 0xFF);
+  if (encode)
+    clsId.Data3++;
+  return SetPropGUID(clsId, value);
+}
+
+static HRESULT FindCodecClassId(const GUID *clsID, UInt32 isCoder2, bool isFilter, bool &encode, int &index)
+{
+  index = -1;
+  if (clsID->Data1 != CLSID_CCodec.Data1 ||
+      clsID->Data2 != CLSID_CCodec.Data2 ||
+      (clsID->Data3 & ~1) != kDecodeId)
+    return S_OK;
+  encode = (clsID->Data3 != kDecodeId);
+  UInt64 id = 0;
+  for (int j = 0; j < 8; j++)
+    id |= ((UInt64)clsID->Data4[j]) << (8 * j);
+  for (unsigned i = 0; i < g_NumCodecs; i++)
+  {
+    const CCodecInfo &codec = *g_Codecs[i];
+    if (id != codec.Id || encode && !codec.CreateEncoder || !encode && !codec.CreateDecoder)
+      continue;
+    if (!isFilter && codec.IsFilter || isFilter && !codec.IsFilter ||
+        codec.NumInStreams != 1 && !isCoder2 || codec.NumInStreams == 1 && isCoder2)
+      return E_NOINTERFACE;
+    index = i;
+    return S_OK;
+  }
+  return S_OK;
+}
+
+STDAPI CreateCoder2(bool encode, UInt32 index, const GUID *iid, void **outObject)
+{
+  COM_TRY_BEGIN
+  *outObject = 0;
+  bool isCoder = (*iid == IID_ICompressCoder) != 0;
+  bool isCoder2 = (*iid == IID_ICompressCoder2) != 0;
+  bool isFilter = (*iid == IID_ICompressFilter) != 0;
+  const CCodecInfo &codec = *g_Codecs[index];
+  if (!isFilter && codec.IsFilter || isFilter && !codec.IsFilter ||
+      codec.NumInStreams != 1 && !isCoder2 || codec.NumInStreams == 1 && isCoder2)
+    return E_NOINTERFACE;
+  if (encode)
+  {
+    if (!codec.CreateEncoder)
+      return CLASS_E_CLASSNOTAVAILABLE;
+    *outObject = codec.CreateEncoder();
+  }
+  else
+  {
+    if (!codec.CreateDecoder)
+      return CLASS_E_CLASSNOTAVAILABLE;
+    *outObject = codec.CreateDecoder();
+  }
+  if (isCoder)
+    ((ICompressCoder *)*outObject)->AddRef();
+  else if (isCoder2)
+    ((ICompressCoder2 *)*outObject)->AddRef();
+  else
+    ((ICompressFilter *)*outObject)->AddRef();
+  return S_OK;
+  COM_TRY_END
+}
+
+STDAPI CreateCoder(const GUID *clsid, const GUID *iid, void **outObject)
+{
+  *outObject = 0;
+  bool isCoder = (*iid == IID_ICompressCoder) != 0;
+  bool isCoder2 = (*iid == IID_ICompressCoder2) != 0;
+  bool isFilter = (*iid == IID_ICompressFilter) != 0;
+  if (!isCoder && !isCoder2 && !isFilter)
+    return E_NOINTERFACE;
+  bool encode;
+  int codecIndex;
+  HRESULT res = FindCodecClassId(clsid, isCoder2, isFilter, encode, codecIndex);
+  if (res != S_OK)
+    return res;
+  if (codecIndex < 0)
+    return CLASS_E_CLASSNOTAVAILABLE;
+  return CreateCoder2(encode, codecIndex, iid, outObject);
+}
+
+STDAPI GetMethodProperty(UInt32 codecIndex, PROPID propID, PROPVARIANT *value)
+{
+  ::VariantClear((VARIANTARG *)value);
+  const CCodecInfo &codec = *g_Codecs[codecIndex];
+  switch(propID)
+  {
+    case NMethodPropID::kID:
+    {
+      value->uhVal.QuadPart = (UInt64)codec.Id;
+      value->vt = VT_UI8;
+      break;
+    }
+    case NMethodPropID::kName:
+      if ((value->bstrVal = ::SysAllocString(codec.Name)) != 0)
+        value->vt = VT_BSTR;
+      break;
+    case NMethodPropID::kDecoder:
+      if (codec.CreateDecoder)
+        return SetClassID(codec.Id, false, value);
+      break;
+    case NMethodPropID::kEncoder:
+      if (codec.CreateEncoder)
+        return SetClassID(codec.Id, true, value);
+      break;
+    case NMethodPropID::kInStreams:
+    {
+      if (codec.NumInStreams != 1)
+      {
+        value->vt = VT_UI4;
+        value->ulVal = (ULONG)codec.NumInStreams;
+      }
+      break;
+    }
+  }
+  return S_OK;
+}
+
+STDAPI GetNumberOfMethods(UINT32 *numCodecs)
+{
+  *numCodecs = g_NumCodecs;
+  return S_OK;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/CopyCoder.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/CopyCoder.cpp
new file mode 100644
index 0000000..899ffa3
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/CopyCoder.cpp
@@ -0,0 +1,62 @@
+// Compress/CopyCoder.cpp
+
+#include "StdAfx.h"
+
+extern "C"
+{
+#include "../../../C/Alloc.h"
+}
+
+#include "../Common/StreamUtils.h"
+
+#include "CopyCoder.h"
+
+namespace NCompress {
+
+static const UInt32 kBufferSize = 1 << 17;
+
+CCopyCoder::~CCopyCoder()
+{
+  ::MidFree(_buffer);
+}
+
+STDMETHODIMP CCopyCoder::Code(ISequentialInStream *inStream,
+    ISequentialOutStream *outStream,
+    const UInt64 * /* inSize */, const UInt64 *outSize,
+    ICompressProgressInfo *progress)
+{
+  if (_buffer == 0)
+  {
+    _buffer = (Byte *)::MidAlloc(kBufferSize);
+    if (_buffer == 0)
+      return E_OUTOFMEMORY;
+  }
+
+  TotalSize = 0;
+  for (;;)
+  {
+    UInt32 realProcessedSize;
+    UInt32 size = kBufferSize;
+    if (outSize != 0)
+      if (size > *outSize - TotalSize)
+        size = (UInt32)(*outSize - TotalSize);
+    RINOK(inStream->Read(_buffer, size, &realProcessedSize));
+    if (realProcessedSize == 0)
+      break;
+    RINOK(WriteStream(outStream, _buffer, realProcessedSize));
+    TotalSize += realProcessedSize;
+    if (progress != NULL)
+    {
+      RINOK(progress->SetRatioInfo(&TotalSize, &TotalSize));
+    }
+  }
+  return S_OK;
+}
+
+STDMETHODIMP CCopyCoder::GetInStreamProcessedSize(UInt64 *value)
+{
+  *value = TotalSize;
+  return S_OK;
+}
+
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/CopyCoder.h b/third_party/lzma/v4_65/files/CPP/7zip/Compress/CopyCoder.h
new file mode 100644
index 0000000..c573678
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/CopyCoder.h
@@ -0,0 +1,32 @@
+// Compress/CopyCoder.h
+
+#ifndef __COMPRESS_COPY_CODER_H
+#define __COMPRESS_COPY_CODER_H
+
+#include "../../Common/MyCom.h"
+
+#include "../ICoder.h"
+
+namespace NCompress {
+
+class CCopyCoder:
+  public ICompressCoder,
+  public ICompressGetInStreamProcessedSize,
+  public CMyUnknownImp
+{
+  Byte *_buffer;
+public:
+  UInt64 TotalSize;
+  CCopyCoder(): TotalSize(0) , _buffer(0) {};
+  ~CCopyCoder();
+
+  MY_UNKNOWN_IMP1(ICompressGetInStreamProcessedSize)
+
+  STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream,
+      const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress);
+  STDMETHOD(GetInStreamProcessedSize)(UInt64 *value);
+};
+
+}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/CopyRegister.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/CopyRegister.cpp
new file mode 100644
index 0000000..efb9b9e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/CopyRegister.cpp
@@ -0,0 +1,14 @@
+// CopyRegister.cpp
+
+#include "StdAfx.h"
+
+#include "../Common/RegisterCodec.h"
+
+#include "CopyCoder.h"
+
+static void *CreateCodec() { return (void *)(ICompressCoder *)(new NCompress::CCopyCoder); }
+
+static CCodecInfo g_CodecInfo =
+{ CreateCodec, CreateCodec, 0x00, L"Copy", 1, false };
+
+REGISTER_CODEC(Copy)
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/AloneLZMA.dsp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/AloneLZMA.dsp
new file mode 100644
index 0000000..76fa7b8
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/AloneLZMA.dsp
@@ -0,0 +1,462 @@
+# Microsoft Developer Studio Project File - Name="AloneLZMA" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=AloneLZMA - Win32 DebugU
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "AloneLZMA.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "AloneLZMA.mak" CFG="AloneLZMA - Win32 DebugU"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "AloneLZMA - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "AloneLZMA - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE "AloneLZMA - Win32 ReleaseU" (based on "Win32 (x86) Console Application")
+!MESSAGE "AloneLZMA - Win32 DebugU" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "AloneLZMA - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\..\..\\" /D "NDEBUG" /D "_MBCS" /D "WIN32" /D "_CONSOLE" /D "COMPRESS_MF_MT" /D "BENCH_MT" /FAcs /Yu"StdAfx.h" /FD /c
+# ADD BASE RSC /l 0x419 /d "NDEBUG"
+# ADD RSC /l 0x419 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386 /out:"c:\UTIL\lzma.exe" /opt:NOWIN98
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF  "$(CFG)" == "AloneLZMA - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\..\\" /D "_DEBUG" /D "_MBCS" /D "WIN32" /D "_CONSOLE" /D "COMPRESS_MF_MT" /D "BENCH_MT" /Yu"StdAfx.h" /FD /GZ /c
+# ADD BASE RSC /l 0x419 /d "_DEBUG"
+# ADD RSC /l 0x419 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /out:"c:\UTIL\lzma.exe" /pdbtype:sept
+
+!ELSEIF  "$(CFG)" == "AloneLZMA - Win32 ReleaseU"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "ReleaseU"
+# PROP BASE Intermediate_Dir "ReleaseU"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "ReleaseU"
+# PROP Intermediate_Dir "ReleaseU"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "EXCLUDE_COM" /D "NO_REGISTRY" /D "FORMAT_7Z" /D "FORMAT_BZIP2" /D "FORMAT_ZIP" /D "FORMAT_TAR" /D "FORMAT_GZIP" /D "COMPRESS_LZMA" /D "COMPRESS_BCJ_X86" /D "COMPRESS_BCJ2" /D "COMPRESS_COPY" /D "COMPRESS_MF_PAT" /D "COMPRESS_MF_BT" /D "COMPRESS_PPMD" /D "COMPRESS_DEFLATE" /D "COMPRESS_IMPLODE" /D "COMPRESS_BZIP2" /D "CRYPTO_ZIP" /Yu"StdAfx.h" /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\..\\" /D "NDEBUG" /D "UNICODE" /D "_UNICODE" /D "WIN32" /D "_CONSOLE" /D "COMPRESS_MF_MT" /D "BENCH_MT" /Yu"StdAfx.h" /FD /c
+# ADD BASE RSC /l 0x419 /d "NDEBUG"
+# ADD RSC /l 0x419 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386 /out:"c:\UTIL\7za2.exe" /opt:NOWIN98
+# SUBTRACT BASE LINK32 /pdb:none
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386 /out:"c:\UTIL\lzma.exe" /opt:NOWIN98
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF  "$(CFG)" == "AloneLZMA - Win32 DebugU"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "DebugU"
+# PROP BASE Intermediate_Dir "DebugU"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "DebugU"
+# PROP Intermediate_Dir "DebugU"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "EXCLUDE_COM" /D "NO_REGISTRY" /D "FORMAT_7Z" /D "FORMAT_BZIP2" /D "FORMAT_ZIP" /D "FORMAT_TAR" /D "FORMAT_GZIP" /D "COMPRESS_LZMA" /D "COMPRESS_BCJ_X86" /D "COMPRESS_BCJ2" /D "COMPRESS_COPY" /D "COMPRESS_MF_PAT" /D "COMPRESS_MF_BT" /D "COMPRESS_PPMD" /D "COMPRESS_DEFLATE" /D "COMPRESS_IMPLODE" /D "COMPRESS_BZIP2" /D "CRYPTO_ZIP" /D "_MBCS" /Yu"StdAfx.h" /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\\" /D "_DEBUG" /D "_UNICODE" /D "UNICODE" /D "WIN32" /D "_CONSOLE" /D "COMPRESS_MF_MT" /D "BENCH_MT" /Yu"StdAfx.h" /FD /GZ /c
+# ADD BASE RSC /l 0x419 /d "_DEBUG"
+# ADD RSC /l 0x419 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /out:"c:\UTIL\7za2.exe" /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /out:"c:\UTIL\lzma.exe" /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "AloneLZMA - Win32 Release"
+# Name "AloneLZMA - Win32 Debug"
+# Name "AloneLZMA - Win32 ReleaseU"
+# Name "AloneLZMA - Win32 DebugU"
+# Begin Group "Spec"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\StdAfx.cpp
+# ADD CPP /Yc"StdAfx.h"
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.h
+# End Source File
+# End Group
+# Begin Group "Compress"
+
+# PROP Default_Filter ""
+# Begin Group "LZMA"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\LzmaDecoder.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzmaDecoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzmaEncoder.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\LzmaEncoder.h
+# End Source File
+# End Group
+# End Group
+# Begin Group "Windows"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileIO.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileIO.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\Synchronization.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\Synchronization.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\System.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\System.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\Thread.h
+# End Source File
+# End Group
+# Begin Group "Common"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\..\Common\CommandLineParser.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\CommandLineParser.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\CRC.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\Defs.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\Defs.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\IntToString.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\IntToString.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyCom.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyString.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyString.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyVector.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyVector.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyWindows.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\NewHandler.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\NewHandler.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\StringConvert.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\StringConvert.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\StringToInt.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\StringToInt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\Types.h
+# End Source File
+# End Group
+# Begin Group "7zip Common"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\Common\FileStreams.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\FileStreams.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\InBuffer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\InBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\OutBuffer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\OutBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\StreamUtils.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\StreamUtils.h
+# End Source File
+# End Group
+# Begin Group "C"
+
+# PROP Default_Filter ""
+# Begin Group "LzmaUtil"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzmaUtil\Lzma86Dec.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzmaUtil\Lzma86Dec.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzmaUtil\Lzma86Enc.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzmaUtil\Lzma86Enc.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\..\..\C\7zCrc.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\7zCrc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\Alloc.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\Alloc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\Bra.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\Bra86.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzFind.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzFind.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzFindMt.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzFindMt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzHash.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzmaDec.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzmaDec.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzmaEnc.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\LzmaEnc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\Threads.c
+# SUBTRACT CPP /YX /Yc /Yu
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\Threads.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\C\Types.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\ICoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LzmaAlone.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\LzmaBench.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\LzmaBench.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LzmaBenchCon.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\LzmaBenchCon.h
+# End Source File
+# End Target
+# End Project
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/AloneLZMA.dsw b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/AloneLZMA.dsw
new file mode 100644
index 0000000..d7482d8
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/AloneLZMA.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "AloneLZMA"=.\AloneLZMA.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/LzmaAlone.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/LzmaAlone.cpp
new file mode 100644
index 0000000..4044c86
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/LzmaAlone.cpp
@@ -0,0 +1,534 @@
+// LzmaAlone.cpp
+
+#include "StdAfx.h"
+
+#include "../../../Common/MyWindows.h"
+#include "../../../Common/MyInitGuid.h"
+
+#include <stdio.h>
+
+#if defined(_WIN32) || defined(OS2) || defined(MSDOS)
+#include <fcntl.h>
+#include <io.h>
+#define MY_SET_BINARY_MODE(file) _setmode(_fileno(file), O_BINARY)
+#else
+#define MY_SET_BINARY_MODE(file)
+#endif
+
+#include "../../../Common/CommandLineParser.h"
+#include "../../../Common/StringConvert.h"
+#include "../../../Common/StringToInt.h"
+
+#include "../../Common/FileStreams.h"
+#include "../../Common/StreamUtils.h"
+
+#include "../LzmaDecoder.h"
+#include "../LzmaEncoder.h"
+
+#include "LzmaBenchCon.h"
+
+#ifdef COMPRESS_MF_MT
+#include "../../../Windows/System.h"
+#endif
+
+extern "C"
+{
+  #include "../../../../C/7zVersion.h"
+  #include "../../../../C/Alloc.h"
+  #include "../../../../C/LzmaUtil/Lzma86Dec.h"
+  #include "../../../../C/LzmaUtil/Lzma86Enc.h"
+}
+
+using namespace NCommandLineParser;
+
+#ifdef _WIN32
+bool g_IsNT = false;
+static inline bool IsItWindowsNT()
+{
+  OSVERSIONINFO versionInfo;
+  versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
+  if (!::GetVersionEx(&versionInfo))
+    return false;
+  return (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT);
+}
+#endif
+
+static const char *kCantAllocate = "Can not allocate memory";
+static const char *kReadError = "Read error";
+static const char *kWriteError = "Write error";
+
+namespace NKey {
+enum Enum
+{
+  kHelp1 = 0,
+  kHelp2,
+  kAlgo,
+  kDict,
+  kFb,
+  kMc,
+  kLc,
+  kLp,
+  kPb,
+  kMatchFinder,
+  kMultiThread,
+  kEOS,
+  kStdIn,
+  kStdOut,
+  kFilter86
+};
+}
+
+static const CSwitchForm kSwitchForms[] =
+{
+  { L"?",  NSwitchType::kSimple, false },
+  { L"H",  NSwitchType::kSimple, false },
+  { L"A", NSwitchType::kUnLimitedPostString, false, 1 },
+  { L"D", NSwitchType::kUnLimitedPostString, false, 1 },
+  { L"FB", NSwitchType::kUnLimitedPostString, false, 1 },
+  { L"MC", NSwitchType::kUnLimitedPostString, false, 1 },
+  { L"LC", NSwitchType::kUnLimitedPostString, false, 1 },
+  { L"LP", NSwitchType::kUnLimitedPostString, false, 1 },
+  { L"PB", NSwitchType::kUnLimitedPostString, false, 1 },
+  { L"MF", NSwitchType::kUnLimitedPostString, false, 1 },
+  { L"MT", NSwitchType::kUnLimitedPostString, false, 0 },
+  { L"EOS", NSwitchType::kSimple, false },
+  { L"SI",  NSwitchType::kSimple, false },
+  { L"SO",  NSwitchType::kSimple, false },
+  { L"F86",  NSwitchType::kPostChar, false, 0, 0, L"+" }
+};
+
+static const int kNumSwitches = sizeof(kSwitchForms) / sizeof(kSwitchForms[0]);
+
+static void PrintHelp()
+{
+  fprintf(stderr, "\nUsage:  LZMA <e|d> inputFile outputFile [<switches>...]\n"
+             "  e: encode file\n"
+             "  d: decode file\n"
+             "  b: Benchmark\n"
+    "<Switches>\n"
+    "  -a{N}:  set compression mode - [0, 1], default: 1 (max)\n"
+    "  -d{N}:  set dictionary size - [12, 30], default: 23 (8MB)\n"
+    "  -fb{N}: set number of fast bytes - [5, 273], default: 128\n"
+    "  -mc{N}: set number of cycles for match finder\n"
+    "  -lc{N}: set number of literal context bits - [0, 8], default: 3\n"
+    "  -lp{N}: set number of literal pos bits - [0, 4], default: 0\n"
+    "  -pb{N}: set number of pos bits - [0, 4], default: 2\n"
+    "  -mf{MF_ID}: set Match Finder: [bt2, bt3, bt4, hc4], default: bt4\n"
+    "  -mt{N}: set number of CPU threads\n"
+    "  -eos:   write End Of Stream marker\n"
+    "  -si:    read data from stdin\n"
+    "  -so:    write data to stdout\n"
+    );
+}
+
+static void PrintHelpAndExit(const char *s)
+{
+  fprintf(stderr, "\nError: %s\n\n", s);
+  PrintHelp();
+  throw -1;
+}
+
+static void IncorrectCommand()
+{
+  PrintHelpAndExit("Incorrect command");
+}
+
+static void WriteArgumentsToStringList(int numArguments, const char *arguments[],
+    UStringVector &strings)
+{
+  for(int i = 1; i < numArguments; i++)
+    strings.Add(MultiByteToUnicodeString(arguments[i]));
+}
+
+static bool GetNumber(const wchar_t *s, UInt32 &value)
+{
+  value = 0;
+  if (MyStringLen(s) == 0)
+    return false;
+  const wchar_t *end;
+  UInt64 res = ConvertStringToUInt64(s, &end);
+  if (*end != L'\0')
+    return false;
+  if (res > 0xFFFFFFFF)
+    return false;
+  value = UInt32(res);
+  return true;
+}
+
+static void ParseUInt32(const CParser &parser, int index, UInt32 &res)
+{
+  if (parser[index].ThereIs)
+    if (!GetNumber(parser[index].PostStrings[0], res))
+      IncorrectCommand();
+}
+
+int main2(int n, const char *args[])
+{
+  #ifdef _WIN32
+  g_IsNT = IsItWindowsNT();
+  #endif
+
+  fprintf(stderr, "\nLZMA " MY_VERSION_COPYRIGHT_DATE "\n");
+
+  if (n == 1)
+  {
+    PrintHelp();
+    return 0;
+  }
+
+  bool unsupportedTypes = (sizeof(Byte) != 1 || sizeof(UInt32) < 4 || sizeof(UInt64) < 4);
+  if (unsupportedTypes)
+  {
+    fprintf(stderr, "Unsupported base types. Edit Common/Types.h and recompile");
+    return 1;
+  }
+
+  UStringVector commandStrings;
+  WriteArgumentsToStringList(n, args, commandStrings);
+  CParser parser(kNumSwitches);
+  try
+  {
+    parser.ParseStrings(kSwitchForms, commandStrings);
+  }
+  catch(...)
+  {
+    IncorrectCommand();
+  }
+
+  if(parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs)
+  {
+    PrintHelp();
+    return 0;
+  }
+  const UStringVector &nonSwitchStrings = parser.NonSwitchStrings;
+
+  int paramIndex = 0;
+  if (paramIndex >= nonSwitchStrings.Size())
+    IncorrectCommand();
+  const UString &command = nonSwitchStrings[paramIndex++];
+
+  bool dictDefined = false;
+  UInt32 dict = (UInt32)-1;
+  if(parser[NKey::kDict].ThereIs)
+  {
+    UInt32 dicLog;
+    if (!GetNumber(parser[NKey::kDict].PostStrings[0], dicLog))
+      IncorrectCommand();
+    dict = 1 << dicLog;
+    dictDefined = true;
+  }
+  UString mf = L"BT4";
+  if (parser[NKey::kMatchFinder].ThereIs)
+    mf = parser[NKey::kMatchFinder].PostStrings[0];
+
+  UInt32 numThreads = (UInt32)-1;
+
+  #ifdef COMPRESS_MF_MT
+  if (parser[NKey::kMultiThread].ThereIs)
+  {
+    UInt32 numCPUs = NWindows::NSystem::GetNumberOfProcessors();
+    const UString &s = parser[NKey::kMultiThread].PostStrings[0];
+    if (s.IsEmpty())
+      numThreads = numCPUs;
+    else
+      if (!GetNumber(s, numThreads))
+        IncorrectCommand();
+  }
+  #endif
+
+  if (command.CompareNoCase(L"b") == 0)
+  {
+    const UInt32 kNumDefaultItereations = 1;
+    UInt32 numIterations = kNumDefaultItereations;
+    {
+      if (paramIndex < nonSwitchStrings.Size())
+        if (!GetNumber(nonSwitchStrings[paramIndex++], numIterations))
+          numIterations = kNumDefaultItereations;
+    }
+    return LzmaBenchCon(stderr, numIterations, numThreads, dict);
+  }
+
+  if (numThreads == (UInt32)-1)
+    numThreads = 1;
+
+  bool encodeMode = false;
+  if (command.CompareNoCase(L"e") == 0)
+    encodeMode = true;
+  else if (command.CompareNoCase(L"d") == 0)
+    encodeMode = false;
+  else
+    IncorrectCommand();
+
+  bool stdInMode = parser[NKey::kStdIn].ThereIs;
+  bool stdOutMode = parser[NKey::kStdOut].ThereIs;
+
+  CMyComPtr<ISequentialInStream> inStream;
+  CInFileStream *inStreamSpec = 0;
+  if (stdInMode)
+  {
+    inStream = new CStdInFileStream;
+    MY_SET_BINARY_MODE(stdin);
+  }
+  else
+  {
+    if (paramIndex >= nonSwitchStrings.Size())
+      IncorrectCommand();
+    const UString &inputName = nonSwitchStrings[paramIndex++];
+    inStreamSpec = new CInFileStream;
+    inStream = inStreamSpec;
+    if (!inStreamSpec->Open(GetSystemString(inputName)))
+    {
+      fprintf(stderr, "\nError: can not open input file %s\n",
+          (const char *)GetOemString(inputName));
+      return 1;
+    }
+  }
+
+  CMyComPtr<ISequentialOutStream> outStream;
+  COutFileStream *outStreamSpec = NULL;
+  if (stdOutMode)
+  {
+    outStream = new CStdOutFileStream;
+    MY_SET_BINARY_MODE(stdout);
+  }
+  else
+  {
+    if (paramIndex >= nonSwitchStrings.Size())
+      IncorrectCommand();
+    const UString &outputName = nonSwitchStrings[paramIndex++];
+    outStreamSpec = new COutFileStream;
+    outStream = outStreamSpec;
+    if (!outStreamSpec->Create(GetSystemString(outputName), true))
+    {
+      fprintf(stderr, "\nError: can not open output file %s\n",
+        (const char *)GetOemString(outputName));
+      return 1;
+    }
+  }
+
+  if (parser[NKey::kFilter86].ThereIs)
+  {
+    // -f86 switch is for x86 filtered mode: BCJ + LZMA.
+    if (parser[NKey::kEOS].ThereIs || stdInMode)
+      throw "Can not use stdin in this mode";
+    UInt64 fileSize;
+    inStreamSpec->File.GetLength(fileSize);
+    if (fileSize > 0xF0000000)
+      throw "File is too big";
+    size_t inSize = (size_t)fileSize;
+    Byte *inBuffer = 0;
+    if (inSize != 0)
+    {
+      inBuffer = (Byte *)MyAlloc((size_t)inSize);
+      if (inBuffer == 0)
+        throw kCantAllocate;
+    }
+    
+    if (ReadStream_FAIL(inStream, inBuffer, inSize) != S_OK)
+      throw "Can not read";
+
+    Byte *outBuffer = 0;
+    size_t outSize;
+    if (encodeMode)
+    {
+      // we allocate 105% of original size for output buffer
+      outSize = (size_t)fileSize / 20 * 21 + (1 << 16);
+      if (outSize != 0)
+      {
+        outBuffer = (Byte *)MyAlloc((size_t)outSize);
+        if (outBuffer == 0)
+          throw kCantAllocate;
+      }
+      if (!dictDefined)
+        dict = 1 << 23;
+      int res = Lzma86_Encode(outBuffer, &outSize, inBuffer, inSize,
+          5, dict, parser[NKey::kFilter86].PostCharIndex == 0 ? SZ_FILTER_YES : SZ_FILTER_AUTO);
+      if (res != 0)
+      {
+        fprintf(stderr, "\nEncoder error = %d\n", (int)res);
+        return 1;
+      }
+    }
+    else
+    {
+      UInt64 outSize64;
+      if (Lzma86_GetUnpackSize(inBuffer, inSize, &outSize64) != 0)
+        throw "data error";
+      outSize = (size_t)outSize64;
+      if (outSize != outSize64)
+        throw "too big";
+      if (outSize != 0)
+      {
+        outBuffer = (Byte *)MyAlloc(outSize);
+        if (outBuffer == 0)
+          throw kCantAllocate;
+      }
+      int res = Lzma86_Decode(outBuffer, &outSize, inBuffer, &inSize);
+      if (inSize != (size_t)fileSize)
+        throw "incorrect processed size";
+      if (res != 0)
+        throw "LzmaDecoder error";
+    }
+    if (WriteStream(outStream, outBuffer, outSize) != S_OK)
+      throw kWriteError;
+    MyFree(outBuffer);
+    MyFree(inBuffer);
+    return 0;
+  }
+
+
+  UInt64 fileSize;
+  if (encodeMode)
+  {
+    NCompress::NLzma::CEncoder *encoderSpec = new NCompress::NLzma::CEncoder;
+    CMyComPtr<ICompressCoder> encoder = encoderSpec;
+
+    if (!dictDefined)
+      dict = 1 << 23;
+
+    UInt32 pb = 2;
+    UInt32 lc = 3; // = 0; for 32-bit data
+    UInt32 lp = 0; // = 2; for 32-bit data
+    UInt32 algo = 1;
+    UInt32 fb = 128;
+    UInt32 mc = 16 + fb / 2;
+    bool mcDefined = false;
+
+    bool eos = parser[NKey::kEOS].ThereIs || stdInMode;
+ 
+    ParseUInt32(parser, NKey::kAlgo, algo);
+    ParseUInt32(parser, NKey::kFb, fb);
+    ParseUInt32(parser, NKey::kLc, lc);
+    ParseUInt32(parser, NKey::kLp, lp);
+    ParseUInt32(parser, NKey::kPb, pb);
+
+    mcDefined = parser[NKey::kMc].ThereIs;
+    if (mcDefined)
+      if (!GetNumber(parser[NKey::kMc].PostStrings[0], mc))
+        IncorrectCommand();
+    
+    PROPID propIDs[] =
+    {
+      NCoderPropID::kDictionarySize,
+      NCoderPropID::kPosStateBits,
+      NCoderPropID::kLitContextBits,
+      NCoderPropID::kLitPosBits,
+      NCoderPropID::kAlgorithm,
+      NCoderPropID::kNumFastBytes,
+      NCoderPropID::kMatchFinder,
+      NCoderPropID::kEndMarker,
+      NCoderPropID::kNumThreads,
+      NCoderPropID::kMatchFinderCycles,
+    };
+    const int kNumPropsMax = sizeof(propIDs) / sizeof(propIDs[0]);
+
+    PROPVARIANT props[kNumPropsMax];
+    for (int p = 0; p < 6; p++)
+      props[p].vt = VT_UI4;
+
+    props[0].ulVal = (UInt32)dict;
+    props[1].ulVal = (UInt32)pb;
+    props[2].ulVal = (UInt32)lc;
+    props[3].ulVal = (UInt32)lp;
+    props[4].ulVal = (UInt32)algo;
+    props[5].ulVal = (UInt32)fb;
+
+    props[6].vt = VT_BSTR;
+    props[6].bstrVal = (BSTR)(const wchar_t *)mf;
+
+    props[7].vt = VT_BOOL;
+    props[7].boolVal = eos ? VARIANT_TRUE : VARIANT_FALSE;
+
+    props[8].vt = VT_UI4;
+    props[8].ulVal = (UInt32)numThreads;
+
+    // it must be last in property list
+    props[9].vt = VT_UI4;
+    props[9].ulVal = (UInt32)mc;
+
+    int numProps = kNumPropsMax;
+    if (!mcDefined)
+      numProps--;
+
+    if (encoderSpec->SetCoderProperties(propIDs, props, numProps) != S_OK)
+      IncorrectCommand();
+    encoderSpec->WriteCoderProperties(outStream);
+
+    if (eos || stdInMode)
+      fileSize = (UInt64)(Int64)-1;
+    else
+      inStreamSpec->File.GetLength(fileSize);
+
+    for (int i = 0; i < 8; i++)
+    {
+      Byte b = Byte(fileSize >> (8 * i));
+      if (outStream->Write(&b, 1, 0) != S_OK)
+      {
+        fprintf(stderr, kWriteError);
+        return 1;
+      }
+    }
+    HRESULT result = encoder->Code(inStream, outStream, 0, 0, 0);
+    if (result == E_OUTOFMEMORY)
+    {
+      fprintf(stderr, "\nError: Can not allocate memory\n");
+      return 1;
+    }
+    else if (result != S_OK)
+    {
+      fprintf(stderr, "\nEncoder error = %X\n", (unsigned int)result);
+      return 1;
+    }
+  }
+  else
+  {
+    NCompress::NLzma::CDecoder *decoderSpec = new NCompress::NLzma::CDecoder;
+    CMyComPtr<ICompressCoder> decoder = decoderSpec;
+    decoderSpec->FinishStream = true;
+    const UInt32 kPropertiesSize = 5;
+    Byte header[kPropertiesSize + 8];
+    if (ReadStream_FALSE(inStream, header, kPropertiesSize + 8) != S_OK)
+    {
+      fprintf(stderr, kReadError);
+      return 1;
+    }
+    if (decoderSpec->SetDecoderProperties2(header, kPropertiesSize) != S_OK)
+    {
+      fprintf(stderr, "SetDecoderProperties error");
+      return 1;
+    }
+    fileSize = 0;
+    for (int i = 0; i < 8; i++)
+      fileSize |= ((UInt64)header[kPropertiesSize + i]) << (8 * i);
+
+    if (decoder->Code(inStream, outStream, 0, (fileSize == (UInt64)(Int64)-1) ? 0 : &fileSize, 0) != S_OK)
+    {
+      fprintf(stderr, "Decoder error");
+      return 1;
+    }
+  }
+  if (outStreamSpec != NULL)
+  {
+    if (outStreamSpec->Close() != S_OK)
+    {
+      fprintf(stderr, "File closing error");
+      return 1;
+    }
+  }
+  return 0;
+}
+
+int MY_CDECL main(int n, const char *args[])
+{
+  try { return main2(n, args); }
+  catch(const char *s)
+  {
+    fprintf(stderr, "\nError: %s\n", s);
+    return 1;
+  }
+  catch(...)
+  {
+    fprintf(stderr, "\nError\n");
+    return 1;
+  }
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/LzmaBench.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/LzmaBench.cpp
new file mode 100644
index 0000000..2ad9a57
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/LzmaBench.cpp
@@ -0,0 +1,1021 @@
+// LzmaBench.cpp
+
+#include "StdAfx.h"
+
+#include "LzmaBench.h"
+
+#ifndef _WIN32
+#define USE_POSIX_TIME
+#define USE_POSIX_TIME2
+#endif
+
+#ifdef USE_POSIX_TIME
+#include <time.h>
+#ifdef USE_POSIX_TIME2
+#include <sys/time.h>
+#endif
+#endif
+
+#ifdef _WIN32
+#define USE_ALLOCA
+#endif
+
+#ifdef USE_ALLOCA
+#ifdef _WIN32
+#include <malloc.h>
+#else
+#include <stdlib.h>
+#endif
+#endif
+
+extern "C"
+{
+#include "../../../../C/7zCrc.h"
+#include "../../../../C/Alloc.h"
+}
+
+#include "../../../Common/MyCom.h"
+
+#ifdef BENCH_MT
+#include "../../../Windows/Synchronization.h"
+#include "../../../Windows/Thread.h"
+#endif
+
+#ifdef EXTERNAL_LZMA
+#include "../../../Windows/PropVariant.h"
+#include "../../ICoder.h"
+#else
+#include "../LzmaDecoder.h"
+#include "../LzmaEncoder.h"
+#endif
+
+static const UInt32 kUncompressMinBlockSize = 1 << 26;
+static const UInt32 kAdditionalSize = (1 << 16);
+static const UInt32 kCompressedAdditionalSize = (1 << 10);
+static const UInt32 kMaxLzmaPropSize = 5;
+
+class CBaseRandomGenerator
+{
+  UInt32 A1;
+  UInt32 A2;
+public:
+  CBaseRandomGenerator() { Init(); }
+  void Init() { A1 = 362436069; A2 = 521288629;}
+  UInt32 GetRnd()
+  {
+    return
+      ((A1 = 36969 * (A1 & 0xffff) + (A1 >> 16)) << 16) +
+      ((A2 = 18000 * (A2 & 0xffff) + (A2 >> 16)) );
+  }
+};
+
+class CBenchBuffer
+{
+public:
+  size_t BufferSize;
+  Byte *Buffer;
+  CBenchBuffer(): Buffer(0) {}
+  virtual ~CBenchBuffer() { Free(); }
+  void Free()
+  {
+    ::MidFree(Buffer);
+    Buffer = 0;
+  }
+  bool Alloc(size_t bufferSize)
+  {
+    if (Buffer != 0 && BufferSize == bufferSize)
+      return true;
+    Free();
+    Buffer = (Byte *)::MidAlloc(bufferSize);
+    BufferSize = bufferSize;
+    return (Buffer != 0);
+  }
+};
+
+class CBenchRandomGenerator: public CBenchBuffer
+{
+  CBaseRandomGenerator *RG;
+public:
+  void Set(CBaseRandomGenerator *rg) { RG = rg; }
+  UInt32 GetVal(UInt32 &res, int numBits)
+  {
+    UInt32 val = res & (((UInt32)1 << numBits) - 1);
+    res >>= numBits;
+    return val;
+  }
+  UInt32 GetLen(UInt32 &res)
+  {
+    UInt32 len = GetVal(res, 2);
+    return GetVal(res, 1 + len);
+  }
+  void Generate()
+  {
+    UInt32 pos = 0;
+    UInt32 rep0 = 1;
+    while (pos < BufferSize)
+    {
+      UInt32 res = RG->GetRnd();
+      res >>= 1;
+      if (GetVal(res, 1) == 0 || pos < 1024)
+        Buffer[pos++] = (Byte)(res & 0xFF);
+      else
+      {
+        UInt32 len;
+        len = 1 + GetLen(res);
+        if (GetVal(res, 3) != 0)
+        {
+          len += GetLen(res);
+          do
+          {
+            UInt32 ppp = GetVal(res, 5) + 6;
+            res = RG->GetRnd();
+            if (ppp > 30)
+              continue;
+            rep0 = /* (1 << ppp) +*/  GetVal(res, ppp);
+            res = RG->GetRnd();
+          }
+          while (rep0 >= pos);
+          rep0++;
+        }
+
+        for (UInt32 i = 0; i < len && pos < BufferSize; i++, pos++)
+          Buffer[pos] = Buffer[pos - rep0];
+      }
+    }
+  }
+};
+
+
+class CBenchmarkInStream:
+  public ISequentialInStream,
+  public CMyUnknownImp
+{
+  const Byte *Data;
+  size_t Pos;
+  size_t Size;
+public:
+  MY_UNKNOWN_IMP
+  void Init(const Byte *data, size_t size)
+  {
+    Data = data;
+    Size = size;
+    Pos = 0;
+  }
+  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
+};
+
+STDMETHODIMP CBenchmarkInStream::Read(void *data, UInt32 size, UInt32 *processedSize)
+{
+  size_t remain = Size - Pos;
+  UInt32 kMaxBlockSize = (1 << 20);
+  if (size > kMaxBlockSize)
+    size = kMaxBlockSize;
+  if (size > remain)
+    size = (UInt32)remain;
+  for (UInt32 i = 0; i < size; i++)
+    ((Byte *)data)[i] = Data[Pos + i];
+  Pos += size;
+  if(processedSize != NULL)
+    *processedSize = size;
+  return S_OK;
+}
+  
+class CBenchmarkOutStream:
+  public ISequentialOutStream,
+  public CBenchBuffer,
+  public CMyUnknownImp
+{
+  // bool _overflow;
+public:
+  UInt32 Pos;
+  // CBenchmarkOutStream(): _overflow(false) {}
+  void Init()
+  {
+    // _overflow = false;
+    Pos = 0;
+  }
+  MY_UNKNOWN_IMP
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+};
+
+STDMETHODIMP CBenchmarkOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
+{
+  size_t curSize = BufferSize - Pos;
+  if (curSize > size)
+    curSize = size;
+  memcpy(Buffer + Pos, data, curSize);
+  Pos += (UInt32)curSize;
+  if(processedSize != NULL)
+    *processedSize = (UInt32)curSize;
+  if (curSize != size)
+  {
+    // _overflow = true;
+    return E_FAIL;
+  }
+  return S_OK;
+}
+  
+class CCrcOutStream:
+  public ISequentialOutStream,
+  public CMyUnknownImp
+{
+public:
+  UInt32 Crc;
+  MY_UNKNOWN_IMP
+  void Init() { Crc = CRC_INIT_VAL; }
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+};
+
+STDMETHODIMP CCrcOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
+{
+  Crc = CrcUpdate(Crc, data, size);
+  if (processedSize != NULL)
+    *processedSize = size;
+  return S_OK;
+}
+  
+static UInt64 GetTimeCount()
+{
+  #ifdef USE_POSIX_TIME
+  #ifdef USE_POSIX_TIME2
+  timeval v;
+  if (gettimeofday(&v, 0) == 0)
+    return (UInt64)(v.tv_sec) * 1000000 + v.tv_usec;
+  return (UInt64)time(NULL) * 1000000;
+  #else
+  return time(NULL);
+  #endif
+  #else
+  /*
+  LARGE_INTEGER value;
+  if (::QueryPerformanceCounter(&value))
+    return value.QuadPart;
+  */
+  return GetTickCount();
+  #endif
+}
+
+static UInt64 GetFreq()
+{
+  #ifdef USE_POSIX_TIME
+  #ifdef USE_POSIX_TIME2
+  return 1000000;
+  #else
+  return 1;
+  #endif
+  #else
+  /*
+  LARGE_INTEGER value;
+  if (::QueryPerformanceFrequency(&value))
+    return value.QuadPart;
+  */
+  return 1000;
+  #endif
+}
+
+#ifndef USE_POSIX_TIME
+static inline UInt64 GetTime64(const FILETIME &t) { return ((UInt64)t.dwHighDateTime << 32) | t.dwLowDateTime; }
+#endif
+static UInt64 GetUserTime()
+{
+  #ifdef USE_POSIX_TIME
+  return clock();
+  #else
+  FILETIME creationTime, exitTime, kernelTime, userTime;
+  if (::GetProcessTimes(::GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime) != 0)
+    return GetTime64(userTime) + GetTime64(kernelTime);
+  return (UInt64)GetTickCount() * 10000;
+  #endif
+}
+
+static UInt64 GetUserFreq()
+{
+  #ifdef USE_POSIX_TIME
+  return CLOCKS_PER_SEC;
+  #else
+  return 10000000;
+  #endif
+}
+
+class CBenchProgressStatus
+{
+  #ifdef BENCH_MT
+  NWindows::NSynchronization::CCriticalSection CS;
+  #endif
+public:
+  HRESULT Res;
+  bool EncodeMode;
+  void SetResult(HRESULT res)
+  {
+    #ifdef BENCH_MT
+    NWindows::NSynchronization::CCriticalSectionLock lock(CS);
+    #endif
+    Res = res;
+  }
+  HRESULT GetResult()
+  {
+    #ifdef BENCH_MT
+    NWindows::NSynchronization::CCriticalSectionLock lock(CS);
+    #endif
+    return Res;
+  }
+};
+
+class CBenchProgressInfo:
+  public ICompressProgressInfo,
+  public CMyUnknownImp
+{
+public:
+  CBenchProgressStatus *Status;
+  CBenchInfo BenchInfo;
+  HRESULT Res;
+  IBenchCallback *callback;
+  CBenchProgressInfo(): callback(0) {}
+  MY_UNKNOWN_IMP
+  STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
+};
+
+void SetStartTime(CBenchInfo &bi)
+{
+  bi.GlobalFreq = GetFreq();
+  bi.UserFreq = GetUserFreq();
+  bi.GlobalTime = ::GetTimeCount();
+  bi.UserTime = ::GetUserTime();
+}
+
+void SetFinishTime(const CBenchInfo &biStart, CBenchInfo &dest)
+{
+  dest.GlobalFreq = GetFreq();
+  dest.UserFreq = GetUserFreq();
+  dest.GlobalTime = ::GetTimeCount() - biStart.GlobalTime;
+  dest.UserTime = ::GetUserTime() - biStart.UserTime;
+}
+
+STDMETHODIMP CBenchProgressInfo::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
+{
+  HRESULT res = Status->GetResult();
+  if (res != S_OK)
+    return res;
+  if (!callback)
+    return res;
+  CBenchInfo info = BenchInfo;
+  SetFinishTime(BenchInfo, info);
+  if (Status->EncodeMode)
+  {
+    info.UnpackSize = *inSize;
+    info.PackSize = *outSize;
+    res = callback->SetEncodeResult(info, false);
+  }
+  else
+  {
+    info.PackSize = BenchInfo.PackSize + *inSize;
+    info.UnpackSize = BenchInfo.UnpackSize + *outSize;
+    res = callback->SetDecodeResult(info, false);
+  }
+  if (res != S_OK)
+    Status->SetResult(res);
+  return res;
+}
+
+static const int kSubBits = 8;
+
+static UInt32 GetLogSize(UInt32 size)
+{
+  for (int i = kSubBits; i < 32; i++)
+    for (UInt32 j = 0; j < (1 << kSubBits); j++)
+      if (size <= (((UInt32)1) << i) + (j << (i - kSubBits)))
+        return (i << kSubBits) + j;
+  return (32 << kSubBits);
+}
+
+static void NormalizeVals(UInt64 &v1, UInt64 &v2)
+{
+  while (v1 > 1000000)
+  {
+    v1 >>= 1;
+    v2 >>= 1;
+  }
+}
+
+UInt64 GetUsage(const CBenchInfo &info)
+{
+  UInt64 userTime = info.UserTime;
+  UInt64 userFreq = info.UserFreq;
+  UInt64 globalTime = info.GlobalTime;
+  UInt64 globalFreq = info.GlobalFreq;
+  NormalizeVals(userTime, userFreq);
+  NormalizeVals(globalFreq, globalTime);
+  if (userFreq == 0)
+    userFreq = 1;
+  if (globalTime == 0)
+    globalTime = 1;
+  return userTime * globalFreq * 1000000 / userFreq / globalTime;
+}
+
+UInt64 GetRatingPerUsage(const CBenchInfo &info, UInt64 rating)
+{
+  UInt64 userTime = info.UserTime;
+  UInt64 userFreq = info.UserFreq;
+  UInt64 globalTime = info.GlobalTime;
+  UInt64 globalFreq = info.GlobalFreq;
+  NormalizeVals(userFreq, userTime);
+  NormalizeVals(globalTime, globalFreq);
+  if (globalFreq == 0)
+    globalFreq = 1;
+  if (userTime == 0)
+    userTime = 1;
+  return userFreq * globalTime / globalFreq *  rating / userTime;
+}
+
+static UInt64 MyMultDiv64(UInt64 value, UInt64 elapsedTime, UInt64 freq)
+{
+  UInt64 elTime = elapsedTime;
+  NormalizeVals(freq, elTime);
+  if (elTime == 0)
+    elTime = 1;
+  return value * freq / elTime;
+}
+
+UInt64 GetCompressRating(UInt32 dictionarySize, UInt64 elapsedTime, UInt64 freq, UInt64 size)
+{
+  UInt64 t = GetLogSize(dictionarySize) - (kBenchMinDicLogSize << kSubBits);
+  UInt64 numCommandsForOne = 870 + ((t * t * 5) >> (2 * kSubBits));
+  UInt64 numCommands = (UInt64)(size) * numCommandsForOne;
+  return MyMultDiv64(numCommands, elapsedTime, freq);
+}
+
+UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 freq, UInt64 outSize, UInt64 inSize, UInt32 numIterations)
+{
+  UInt64 numCommands = (inSize * 200 + outSize * 4) * numIterations;
+  return MyMultDiv64(numCommands, elapsedTime, freq);
+}
+
+#ifdef EXTERNAL_LZMA
+typedef UInt32 (WINAPI * CreateObjectPointer)(const GUID *clsID,
+    const GUID *interfaceID, void **outObject);
+#endif
+
+struct CEncoderInfo;
+
+struct CEncoderInfo
+{
+  #ifdef BENCH_MT
+  NWindows::CThread thread[2];
+  #endif
+  CMyComPtr<ICompressCoder> encoder;
+  CBenchProgressInfo *progressInfoSpec[2];
+  CMyComPtr<ICompressProgressInfo> progressInfo[2];
+  UInt32 NumIterations;
+  #ifdef USE_ALLOCA
+  size_t AllocaSize;
+  #endif
+
+  struct CDecoderInfo
+  {
+    CEncoderInfo *Encoder;
+    UInt32 DecoderIndex;
+    #ifdef USE_ALLOCA
+    size_t AllocaSize;
+    #endif
+    bool CallbackMode;
+  };
+  CDecoderInfo decodersInfo[2];
+
+  CMyComPtr<ICompressCoder> decoders[2];
+  HRESULT Results[2];
+  CBenchmarkOutStream *outStreamSpec;
+  CMyComPtr<ISequentialOutStream> outStream;
+  IBenchCallback *callback;
+  UInt32 crc;
+  UInt32 kBufferSize;
+  UInt32 compressedSize;
+  CBenchRandomGenerator rg;
+  CBenchmarkOutStream *propStreamSpec;
+  CMyComPtr<ISequentialOutStream> propStream;
+  HRESULT Init(UInt32 dictionarySize, UInt32 numThreads, CBaseRandomGenerator *rg);
+  HRESULT Encode();
+  HRESULT Decode(UInt32 decoderIndex);
+
+  CEncoderInfo(): outStreamSpec(0), callback(0), propStreamSpec(0) {}
+
+  #ifdef BENCH_MT
+  static THREAD_FUNC_DECL EncodeThreadFunction(void *param)
+  {
+    CEncoderInfo *encoder = (CEncoderInfo *)param;
+    #ifdef USE_ALLOCA
+    alloca(encoder->AllocaSize);
+    #endif
+    HRESULT res = encoder->Encode();
+    encoder->Results[0] = res;
+    if (res != S_OK)
+      encoder->progressInfoSpec[0]->Status->SetResult(res);
+
+    return 0;
+  }
+  static THREAD_FUNC_DECL DecodeThreadFunction(void *param)
+  {
+    CDecoderInfo *decoder = (CDecoderInfo *)param;
+    #ifdef USE_ALLOCA
+    alloca(decoder->AllocaSize);
+    #endif
+    CEncoderInfo *encoder = decoder->Encoder;
+    encoder->Results[decoder->DecoderIndex] = encoder->Decode(decoder->DecoderIndex);
+    return 0;
+  }
+
+  HRESULT CreateEncoderThread()
+  {
+    return thread[0].Create(EncodeThreadFunction, this);
+  }
+
+  HRESULT CreateDecoderThread(int index, bool callbackMode
+      #ifdef USE_ALLOCA
+      , size_t allocaSize
+      #endif
+      )
+  {
+    CDecoderInfo &decoder = decodersInfo[index];
+    decoder.DecoderIndex = index;
+    decoder.Encoder = this;
+    #ifdef USE_ALLOCA
+    decoder.AllocaSize = allocaSize;
+    #endif
+    decoder.CallbackMode = callbackMode;
+    return thread[index].Create(DecodeThreadFunction, &decoder);
+  }
+  #endif
+};
+
+HRESULT CEncoderInfo::Init(UInt32 dictionarySize, UInt32 numThreads, CBaseRandomGenerator *rgLoc)
+{
+  rg.Set(rgLoc);
+  kBufferSize = dictionarySize + kAdditionalSize;
+  UInt32 kCompressedBufferSize = (kBufferSize / 2) + kCompressedAdditionalSize;
+  if (!rg.Alloc(kBufferSize))
+    return E_OUTOFMEMORY;
+  rg.Generate();
+  crc = CrcCalc(rg.Buffer, rg.BufferSize);
+
+  outStreamSpec = new CBenchmarkOutStream;
+  if (!outStreamSpec->Alloc(kCompressedBufferSize))
+    return E_OUTOFMEMORY;
+
+  outStream = outStreamSpec;
+
+  propStreamSpec = 0;
+  if (!propStream)
+  {
+    propStreamSpec = new CBenchmarkOutStream;
+    propStream = propStreamSpec;
+  }
+  if (!propStreamSpec->Alloc(kMaxLzmaPropSize))
+    return E_OUTOFMEMORY;
+  propStreamSpec->Init();
+  
+  PROPID propIDs[] =
+  {
+    NCoderPropID::kDictionarySize,
+    NCoderPropID::kMultiThread
+  };
+  const int kNumProps = sizeof(propIDs) / sizeof(propIDs[0]);
+  PROPVARIANT properties[kNumProps];
+  properties[0].vt = VT_UI4;
+  properties[0].ulVal = (UInt32)dictionarySize;
+
+  properties[1].vt = VT_BOOL;
+  properties[1].boolVal = (numThreads > 1) ? VARIANT_TRUE : VARIANT_FALSE;
+
+  {
+    CMyComPtr<ICompressSetCoderProperties> setCoderProperties;
+    RINOK(encoder.QueryInterface(IID_ICompressSetCoderProperties, &setCoderProperties));
+    if (!setCoderProperties)
+      return E_FAIL;
+    RINOK(setCoderProperties->SetCoderProperties(propIDs, properties, kNumProps));
+
+    CMyComPtr<ICompressWriteCoderProperties> writeCoderProperties;
+    encoder.QueryInterface(IID_ICompressWriteCoderProperties, &writeCoderProperties);
+    if (writeCoderProperties)
+    {
+      RINOK(writeCoderProperties->WriteCoderProperties(propStream));
+    }
+  }
+  return S_OK;
+}
+
+HRESULT CEncoderInfo::Encode()
+{
+  CBenchmarkInStream *inStreamSpec = new CBenchmarkInStream;
+  CMyComPtr<ISequentialInStream> inStream = inStreamSpec;
+  inStreamSpec->Init(rg.Buffer, rg.BufferSize);
+  outStreamSpec->Init();
+
+  RINOK(encoder->Code(inStream, outStream, 0, 0, progressInfo[0]));
+  compressedSize = outStreamSpec->Pos;
+  encoder.Release();
+  return S_OK;
+}
+
+HRESULT CEncoderInfo::Decode(UInt32 decoderIndex)
+{
+  CBenchmarkInStream *inStreamSpec = new CBenchmarkInStream;
+  CMyComPtr<ISequentialInStream> inStream = inStreamSpec;
+  CMyComPtr<ICompressCoder> &decoder = decoders[decoderIndex];
+
+  CMyComPtr<ICompressSetDecoderProperties2> compressSetDecoderProperties;
+  decoder.QueryInterface(IID_ICompressSetDecoderProperties2, &compressSetDecoderProperties);
+  if (!compressSetDecoderProperties)
+    return E_FAIL;
+
+  CCrcOutStream *crcOutStreamSpec = new CCrcOutStream;
+  CMyComPtr<ISequentialOutStream> crcOutStream = crcOutStreamSpec;
+    
+  CBenchProgressInfo *pi = progressInfoSpec[decoderIndex];
+  pi->BenchInfo.UnpackSize = 0;
+  pi->BenchInfo.PackSize = 0;
+
+  for (UInt32 j = 0; j < NumIterations; j++)
+  {
+    inStreamSpec->Init(outStreamSpec->Buffer, compressedSize);
+    crcOutStreamSpec->Init();
+    
+    RINOK(compressSetDecoderProperties->SetDecoderProperties2(propStreamSpec->Buffer, propStreamSpec->Pos));
+    UInt64 outSize = kBufferSize;
+    RINOK(decoder->Code(inStream, crcOutStream, 0, &outSize, progressInfo[decoderIndex]));
+    if (CRC_GET_DIGEST(crcOutStreamSpec->Crc) != crc)
+      return S_FALSE;
+    pi->BenchInfo.UnpackSize += kBufferSize;
+    pi->BenchInfo.PackSize += compressedSize;
+  }
+  decoder.Release();
+  return S_OK;
+}
+
+static const UInt32 kNumThreadsMax = (1 << 16);
+
+struct CBenchEncoders
+{
+  CEncoderInfo *encoders;
+  CBenchEncoders(UInt32 num): encoders(0) { encoders = new CEncoderInfo[num]; }
+  ~CBenchEncoders() { delete []encoders; }
+};
+
+HRESULT LzmaBench(
+  #ifdef EXTERNAL_LZMA
+  CCodecs *codecs,
+  #endif
+  UInt32 numThreads, UInt32 dictionarySize, IBenchCallback *callback)
+{
+  UInt32 numEncoderThreads =
+    #ifdef BENCH_MT
+    (numThreads > 1 ? numThreads / 2 : 1);
+    #else
+    1;
+    #endif
+  UInt32 numSubDecoderThreads =
+    #ifdef BENCH_MT
+    (numThreads > 1 ? 2 : 1);
+    #else
+    1;
+    #endif
+  if (dictionarySize < (1 << kBenchMinDicLogSize) || numThreads < 1 || numEncoderThreads > kNumThreadsMax)
+  {
+    return E_INVALIDARG;
+  }
+
+  CBenchEncoders encodersSpec(numEncoderThreads);
+  CEncoderInfo *encoders = encodersSpec.encoders;
+
+  #ifdef EXTERNAL_LZMA
+  UString name = L"LZMA";
+  #endif
+
+  UInt32 i;
+  for (i = 0; i < numEncoderThreads; i++)
+  {
+    CEncoderInfo &encoder = encoders[i];
+    encoder.callback = (i == 0) ? callback : 0;
+
+    #ifdef EXTERNAL_LZMA
+    RINOK(codecs->CreateCoder(name, true, encoder.encoder));
+    #else
+    encoder.encoder = new NCompress::NLzma::CEncoder;
+    #endif
+    for (UInt32 j = 0; j < numSubDecoderThreads; j++)
+    {
+      #ifdef EXTERNAL_LZMA
+      RINOK(codecs->CreateCoder(name, false, encoder.decoders[j]));
+      #else
+      encoder.decoders[j] = new NCompress::NLzma::CDecoder;
+      #endif
+    }
+  }
+
+  CBaseRandomGenerator rg;
+  rg.Init();
+  for (i = 0; i < numEncoderThreads; i++)
+  {
+    RINOK(encoders[i].Init(dictionarySize, numThreads, &rg));
+  }
+
+  CBenchProgressStatus status;
+  status.Res = S_OK;
+  status.EncodeMode = true;
+
+  for (i = 0; i < numEncoderThreads; i++)
+  {
+    CEncoderInfo &encoder = encoders[i];
+    for (int j = 0; j < 2; j++)
+    {
+      encoder.progressInfo[j] = encoder.progressInfoSpec[j] = new CBenchProgressInfo;
+      encoder.progressInfoSpec[j]->Status = &status;
+    }
+    if (i == 0)
+    {
+      encoder.progressInfoSpec[0]->callback = callback;
+      encoder.progressInfoSpec[0]->BenchInfo.NumIterations = numEncoderThreads;
+      SetStartTime(encoder.progressInfoSpec[0]->BenchInfo);
+    }
+
+    #ifdef BENCH_MT
+    if (numEncoderThreads > 1)
+    {
+      #ifdef USE_ALLOCA
+      encoder.AllocaSize = (i * 16 * 21) & 0x7FF;
+      #endif
+      RINOK(encoder.CreateEncoderThread())
+    }
+    else
+    #endif
+    {
+      RINOK(encoder.Encode());
+    }
+  }
+  #ifdef BENCH_MT
+  if (numEncoderThreads > 1)
+    for (i = 0; i < numEncoderThreads; i++)
+      encoders[i].thread[0].Wait();
+  #endif
+
+  RINOK(status.Res);
+
+  CBenchInfo info;
+
+  SetFinishTime(encoders[0].progressInfoSpec[0]->BenchInfo, info);
+  info.UnpackSize = 0;
+  info.PackSize = 0;
+  info.NumIterations = 1; // progressInfoSpec->NumIterations;
+  for (i = 0; i < numEncoderThreads; i++)
+  {
+    CEncoderInfo &encoder = encoders[i];
+    info.UnpackSize += encoder.kBufferSize;
+    info.PackSize += encoder.compressedSize;
+  }
+  RINOK(callback->SetEncodeResult(info, true));
+
+
+  status.Res = S_OK;
+  status.EncodeMode = false;
+
+  UInt32 numDecoderThreads = numEncoderThreads * numSubDecoderThreads;
+  for (i = 0; i < numEncoderThreads; i++)
+  {
+    CEncoderInfo &encoder = encoders[i];
+    encoder.NumIterations = 2 + kUncompressMinBlockSize / encoder.kBufferSize;
+
+    if (i == 0)
+    {
+      encoder.progressInfoSpec[0]->callback = callback;
+      encoder.progressInfoSpec[0]->BenchInfo.NumIterations = numDecoderThreads;
+      SetStartTime(encoder.progressInfoSpec[0]->BenchInfo);
+    }
+
+    #ifdef BENCH_MT
+    if (numDecoderThreads > 1)
+    {
+      for (UInt32 j = 0; j < numSubDecoderThreads; j++)
+      {
+        HRESULT res = encoder.CreateDecoderThread(j, (i == 0 && j == 0)
+            #ifdef USE_ALLOCA
+            , ((i * numSubDecoderThreads + j) * 16 * 21) & 0x7FF
+            #endif
+            );
+        RINOK(res);
+      }
+    }
+    else
+    #endif
+    {
+      RINOK(encoder.Decode(0));
+    }
+  }
+  #ifdef BENCH_MT
+  HRESULT res = S_OK;
+  if (numDecoderThreads > 1)
+    for (i = 0; i < numEncoderThreads; i++)
+      for (UInt32 j = 0; j < numSubDecoderThreads; j++)
+      {
+        CEncoderInfo &encoder = encoders[i];
+        encoder.thread[j].Wait();
+        if (encoder.Results[j] != S_OK)
+          res = encoder.Results[j];
+      }
+  RINOK(res);
+  #endif
+  RINOK(status.Res);
+  SetFinishTime(encoders[0].progressInfoSpec[0]->BenchInfo, info);
+  info.UnpackSize = 0;
+  info.PackSize = 0;
+  info.NumIterations = numSubDecoderThreads * encoders[0].NumIterations;
+  for (i = 0; i < numEncoderThreads; i++)
+  {
+    CEncoderInfo &encoder = encoders[i];
+    info.UnpackSize += encoder.kBufferSize;
+    info.PackSize += encoder.compressedSize;
+  }
+  RINOK(callback->SetDecodeResult(info, false));
+  RINOK(callback->SetDecodeResult(info, true));
+  return S_OK;
+}
+
+
+inline UInt64 GetLZMAUsage(bool multiThread, UInt32 dictionary)
+{
+  UInt32 hs = dictionary - 1;
+  hs |= (hs >> 1);
+  hs |= (hs >> 2);
+  hs |= (hs >> 4);
+  hs |= (hs >> 8);
+  hs >>= 1;
+  hs |= 0xFFFF;
+  if (hs > (1 << 24))
+    hs >>= 1;
+  hs++;
+  return ((hs + (1 << 16)) + (UInt64)dictionary * 2) * 4 + (UInt64)dictionary * 3 / 2 +
+      (1 << 20) + (multiThread ? (6 << 20) : 0);
+}
+
+UInt64 GetBenchMemoryUsage(UInt32 numThreads, UInt32 dictionary)
+{
+  const UInt32 kBufferSize = dictionary;
+  const UInt32 kCompressedBufferSize = (kBufferSize / 2);
+  UInt32 numSubThreads = (numThreads > 1) ? 2 : 1;
+  UInt32 numBigThreads = numThreads / numSubThreads;
+  return (kBufferSize + kCompressedBufferSize +
+    GetLZMAUsage((numThreads > 1), dictionary) + (2 << 20)) * numBigThreads;
+}
+
+static bool CrcBig(const void *data, UInt32 size, UInt32 numCycles, UInt32 crcBase)
+{
+  for (UInt32 i = 0; i < numCycles; i++)
+    if (CrcCalc(data, size) != crcBase)
+      return false;
+  return true;
+}
+
+#ifdef BENCH_MT
+struct CCrcInfo
+{
+  NWindows::CThread Thread;
+  const Byte *Data;
+  UInt32 Size;
+  UInt32 NumCycles;
+  UInt32 Crc;
+  bool Res;
+  void Wait()
+  {
+    Thread.Wait();
+    Thread.Close();
+  }
+};
+
+static THREAD_FUNC_DECL CrcThreadFunction(void *param)
+{
+  CCrcInfo *p = (CCrcInfo *)param;
+  p->Res = CrcBig(p->Data, p->Size, p->NumCycles, p->Crc);
+  return 0;
+}
+
+struct CCrcThreads
+{
+  UInt32 NumThreads;
+  CCrcInfo *Items;
+  CCrcThreads(): Items(0), NumThreads(0) {}
+  void WaitAll()
+  {
+    for (UInt32 i = 0; i < NumThreads; i++)
+      Items[i].Wait();
+    NumThreads = 0;
+  }
+  ~CCrcThreads()
+  {
+    WaitAll();
+    delete []Items;
+  }
+};
+#endif
+
+static UInt32 CrcCalc1(const Byte *buf, UInt32 size)
+{
+  UInt32 crc = CRC_INIT_VAL;;
+  for (UInt32 i = 0; i < size; i++)
+    crc = CRC_UPDATE_BYTE(crc, buf[i]);
+  return CRC_GET_DIGEST(crc);
+}
+
+static void RandGen(Byte *buf, UInt32 size, CBaseRandomGenerator &RG)
+{
+  for (UInt32 i = 0; i < size; i++)
+    buf[i] = (Byte)RG.GetRnd();
+}
+
+static UInt32 RandGenCrc(Byte *buf, UInt32 size, CBaseRandomGenerator &RG)
+{
+  RandGen(buf, size, RG);
+  return CrcCalc1(buf, size);
+}
+
+bool CrcInternalTest()
+{
+  CBenchBuffer buffer;
+  const UInt32 kBufferSize0 = (1 << 8);
+  const UInt32 kBufferSize1 = (1 << 10);
+  const UInt32 kCheckSize = (1 << 5);
+  if (!buffer.Alloc(kBufferSize0 + kBufferSize1))
+    return false;
+  Byte *buf = buffer.Buffer;
+  UInt32 i;
+  for (i = 0; i < kBufferSize0; i++)
+    buf[i] = (Byte)i;
+  UInt32 crc1 = CrcCalc1(buf, kBufferSize0);
+  if (crc1 != 0x29058C73)
+    return false;
+  CBaseRandomGenerator RG;
+  RandGen(buf + kBufferSize0, kBufferSize1, RG);
+  for (i = 0; i < kBufferSize0 + kBufferSize1 - kCheckSize; i++)
+    for (UInt32 j = 0; j < kCheckSize; j++)
+      if (CrcCalc1(buf + i, j) != CrcCalc(buf + i, j))
+        return false;
+  return true;
+}
+
+HRESULT CrcBench(UInt32 numThreads, UInt32 bufferSize, UInt64 &speed)
+{
+  if (numThreads == 0)
+    numThreads = 1;
+
+  CBenchBuffer buffer;
+  size_t totalSize = (size_t)bufferSize * numThreads;
+  if (totalSize / numThreads != bufferSize)
+    return E_OUTOFMEMORY;
+  if (!buffer.Alloc(totalSize))
+    return E_OUTOFMEMORY;
+
+  Byte *buf = buffer.Buffer;
+  CBaseRandomGenerator RG;
+  UInt32 numCycles = ((UInt32)1 << 30) / ((bufferSize >> 2) + 1) + 1;
+
+  UInt64 timeVal;
+  #ifdef BENCH_MT
+  CCrcThreads threads;
+  if (numThreads > 1)
+  {
+    threads.Items = new CCrcInfo[numThreads];
+    UInt32 i;
+    for (i = 0; i < numThreads; i++)
+    {
+      CCrcInfo &info = threads.Items[i];
+      Byte *data = buf + (size_t)bufferSize * i;
+      info.Data = data;
+      info.NumCycles = numCycles;
+      info.Size = bufferSize;
+      info.Crc = RandGenCrc(data, bufferSize, RG);
+    }
+    timeVal = GetTimeCount();
+    for (i = 0; i < numThreads; i++)
+    {
+      CCrcInfo &info = threads.Items[i];
+      RINOK(info.Thread.Create(CrcThreadFunction, &info));
+      threads.NumThreads++;
+    }
+    threads.WaitAll();
+    for (i = 0; i < numThreads; i++)
+      if (!threads.Items[i].Res)
+        return S_FALSE;
+  }
+  else
+  #endif
+  {
+    UInt32 crc = RandGenCrc(buf, bufferSize, RG);
+    timeVal = GetTimeCount();
+    if (!CrcBig(buf, bufferSize, numCycles, crc))
+      return S_FALSE;
+  }
+  timeVal = GetTimeCount() - timeVal;
+  if (timeVal == 0)
+    timeVal = 1;
+
+  UInt64 size = (UInt64)numCycles * totalSize;
+  speed = MyMultDiv64(size, timeVal, GetFreq());
+  return S_OK;
+}
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/LzmaBench.h b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/LzmaBench.h
new file mode 100644
index 0000000..f8aeb6b
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/LzmaBench.h
@@ -0,0 +1,48 @@
+// LzmaBench.h
+
+#ifndef __LZMABENCH_H
+#define __LZMABENCH_H
+
+#include <stdio.h>
+#include "../../../Common/Types.h"
+#ifdef EXTERNAL_LZMA
+#include "../../UI/Common/LoadCodecs.h"
+#endif
+
+struct CBenchInfo
+{
+  UInt64 GlobalTime;
+  UInt64 GlobalFreq;
+  UInt64 UserTime;
+  UInt64 UserFreq;
+  UInt64 UnpackSize;
+  UInt64 PackSize;
+  UInt32 NumIterations;
+  CBenchInfo(): NumIterations(0) {}
+};
+
+struct IBenchCallback
+{
+  virtual HRESULT SetEncodeResult(const CBenchInfo &info, bool final) = 0;
+  virtual HRESULT SetDecodeResult(const CBenchInfo &info, bool final) = 0;
+};
+
+UInt64 GetUsage(const CBenchInfo &benchOnfo);
+UInt64 GetRatingPerUsage(const CBenchInfo &info, UInt64 rating);
+UInt64 GetCompressRating(UInt32 dictionarySize, UInt64 elapsedTime, UInt64 freq, UInt64 size);
+UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 freq, UInt64 outSize, UInt64 inSize, UInt32 numIterations);
+
+HRESULT LzmaBench(
+  #ifdef EXTERNAL_LZMA
+  CCodecs *codecs,
+  #endif
+  UInt32 numThreads, UInt32 dictionarySize, IBenchCallback *callback);
+
+const int kBenchMinDicLogSize = 18;
+
+UInt64 GetBenchMemoryUsage(UInt32 numThreads, UInt32 dictionary);
+
+bool CrcInternalTest();
+HRESULT CrcBench(UInt32 numThreads, UInt32 bufferSize, UInt64 &speed);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/LzmaBenchCon.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/LzmaBenchCon.cpp
new file mode 100644
index 0000000..b1c455e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/LzmaBenchCon.cpp
@@ -0,0 +1,311 @@
+// LzmaBenchCon.cpp
+
+#include "StdAfx.h"
+
+#include <stdio.h>
+
+#include "LzmaBench.h"
+#include "LzmaBenchCon.h"
+#include "../../../Common/IntToString.h"
+
+#if defined(BENCH_MT) || defined(_WIN32)
+#include "../../../Windows/System.h"
+#endif
+
+#ifdef BREAK_HANDLER
+#include "../../UI/Console/ConsoleClose.h"
+#endif
+#include "../../../Common/MyCom.h"
+
+struct CTotalBenchRes
+{
+  UInt64 NumIterations;
+  UInt64 Rating;
+  UInt64 Usage;
+  UInt64 RPU;
+  void Init() { NumIterations = 0; Rating = 0; Usage = 0; RPU = 0; }
+  void Normalize()
+  {
+    if (NumIterations == 0)
+      return;
+    Rating /= NumIterations;
+    Usage /= NumIterations;
+    RPU /= NumIterations;
+    NumIterations = 1;
+  }
+  void SetMid(const CTotalBenchRes &r1, const CTotalBenchRes &r2)
+  {
+    Rating = (r1.Rating + r2.Rating) / 2;
+    Usage = (r1.Usage + r2.Usage) / 2;
+    RPU = (r1.RPU + r2.RPU) / 2;
+    NumIterations = (r1.NumIterations + r2.NumIterations) / 2;
+  }
+};
+
+struct CBenchCallback: public IBenchCallback
+{
+  CTotalBenchRes EncodeRes;
+  CTotalBenchRes DecodeRes;
+  FILE *f;
+  void Init() { EncodeRes.Init(); DecodeRes.Init(); }
+  void Normalize() { EncodeRes.Normalize(); DecodeRes.Normalize(); }
+  UInt32 dictionarySize;
+  HRESULT SetEncodeResult(const CBenchInfo &info, bool final);
+  HRESULT SetDecodeResult(const CBenchInfo &info, bool final);
+};
+
+static void NormalizeVals(UInt64 &v1, UInt64 &v2)
+{
+  while (v1 > 1000000)
+  {
+    v1 >>= 1;
+    v2 >>= 1;
+  }
+}
+
+static UInt64 MyMultDiv64(UInt64 value, UInt64 elapsedTime, UInt64 freq)
+{
+  UInt64 elTime = elapsedTime;
+  NormalizeVals(freq, elTime);
+  if (elTime == 0)
+    elTime = 1;
+  return value * freq / elTime;
+}
+
+static void PrintNumber(FILE *f, UInt64 value, int size)
+{
+  char s[32];
+  ConvertUInt64ToString(value, s);
+  fprintf(f, " ");
+  for (int len = (int)strlen(s); len < size; len++)
+    fprintf(f, " ");
+  fprintf(f, "%s", s);
+}
+
+static void PrintRating(FILE *f, UInt64 rating)
+{
+  PrintNumber(f, rating / 1000000, 6);
+}
+
+static void PrintResults(FILE *f, UInt64 usage, UInt64 rpu, UInt64 rating)
+{
+  PrintNumber(f, (usage + 5000) / 10000, 5);
+  PrintRating(f, rpu);
+  PrintRating(f, rating);
+}
+
+
+static void PrintResults(FILE *f, const CBenchInfo &info, UInt64 rating, CTotalBenchRes &res)
+{
+  UInt64 speed = MyMultDiv64(info.UnpackSize, info.GlobalTime, info.GlobalFreq);
+  PrintNumber(f, speed / 1024, 7);
+  UInt64 usage = GetUsage(info);
+  UInt64 rpu = GetRatingPerUsage(info, rating);
+  PrintResults(f, usage, rpu, rating);
+  res.NumIterations++;
+  res.RPU += rpu;
+  res.Rating += rating;
+  res.Usage += usage;
+}
+
+static void PrintTotals(FILE *f, const CTotalBenchRes &res)
+{
+  fprintf(f, "       ");
+  PrintResults(f, res.Usage, res.RPU, res.Rating);
+}
+
+
+HRESULT CBenchCallback::SetEncodeResult(const CBenchInfo &info, bool final)
+{
+  #ifdef BREAK_HANDLER
+  if (NConsoleClose::TestBreakSignal())
+    return E_ABORT;
+  #endif
+
+  if (final)
+  {
+    UInt64 rating = GetCompressRating(dictionarySize, info.GlobalTime, info.GlobalFreq, info.UnpackSize);
+    PrintResults(f, info, rating, EncodeRes);
+  }
+  return S_OK;
+}
+
+static const char *kSep = "  | ";
+
+
+HRESULT CBenchCallback::SetDecodeResult(const CBenchInfo &info, bool final)
+{
+  #ifdef BREAK_HANDLER
+  if (NConsoleClose::TestBreakSignal())
+    return E_ABORT;
+  #endif
+  if (final)
+  {
+    UInt64 rating = GetDecompressRating(info.GlobalTime, info.GlobalFreq, info.UnpackSize, info.PackSize, info.NumIterations);
+    fprintf(f, kSep);
+    CBenchInfo info2 = info;
+    info2.UnpackSize *= info2.NumIterations;
+    info2.PackSize *= info2.NumIterations;
+    info2.NumIterations = 1;
+    PrintResults(f, info2, rating, DecodeRes);
+  }
+  return S_OK;
+}
+
+static void PrintRequirements(FILE *f, const char *sizeString, UInt64 size, const char *threadsString, UInt32 numThreads)
+{
+  fprintf(f, "\nRAM %s ", sizeString);
+  PrintNumber(f, (size >> 20), 5);
+  fprintf(f, " MB,  # %s %3d", threadsString, (unsigned int)numThreads);
+}
+
+HRESULT LzmaBenchCon(
+  #ifdef EXTERNAL_LZMA
+  CCodecs *codecs,
+  #endif
+  FILE *f, UInt32 numIterations, UInt32 numThreads, UInt32 dictionary)
+{
+  if (!CrcInternalTest())
+    return S_FALSE;
+  #ifdef BENCH_MT
+  UInt64 ramSize = NWindows::NSystem::GetRamSize();  //
+  UInt32 numCPUs = NWindows::NSystem::GetNumberOfProcessors();
+  PrintRequirements(f, "size: ", ramSize, "CPU hardware threads:", numCPUs);
+  if (numThreads == (UInt32)-1)
+    numThreads = numCPUs;
+  if (numThreads > 1)
+    numThreads &= ~1;
+  if (dictionary == (UInt32)-1)
+  {
+    int dicSizeLog;
+    for (dicSizeLog = 25; dicSizeLog > kBenchMinDicLogSize; dicSizeLog--)
+      if (GetBenchMemoryUsage(numThreads, ((UInt32)1 << dicSizeLog)) + (8 << 20) <= ramSize)
+        break;
+    dictionary = (1 << dicSizeLog);
+  }
+  #else
+  if (dictionary == (UInt32)-1)
+    dictionary = (1 << 22);
+  numThreads = 1;
+  #endif
+
+  PrintRequirements(f, "usage:", GetBenchMemoryUsage(numThreads, dictionary), "Benchmark threads:   ", numThreads);
+
+  CBenchCallback callback;
+  callback.Init();
+  callback.f = f;
+  
+  fprintf(f, "\n\nDict        Compressing          |        Decompressing\n   ");
+  int j;
+  for (j = 0; j < 2; j++)
+  {
+    fprintf(f, "   Speed Usage    R/U Rating");
+    if (j == 0)
+      fprintf(f, kSep);
+  }
+  fprintf(f, "\n   ");
+  for (j = 0; j < 2; j++)
+  {
+    fprintf(f, "    KB/s     %%   MIPS   MIPS");
+    if (j == 0)
+      fprintf(f, kSep);
+  }
+  fprintf(f, "\n\n");
+  for (UInt32 i = 0; i < numIterations; i++)
+  {
+    const int kStartDicLog = 22;
+    int pow = (dictionary < ((UInt32)1 << kStartDicLog)) ? kBenchMinDicLogSize : kStartDicLog;
+    while (((UInt32)1 << pow) > dictionary)
+      pow--;
+    for (; ((UInt32)1 << pow) <= dictionary; pow++)
+    {
+      fprintf(f, "%2d:", pow);
+      callback.dictionarySize = (UInt32)1 << pow;
+      HRESULT res = LzmaBench(
+        #ifdef EXTERNAL_LZMA
+        codecs,
+        #endif
+        numThreads, callback.dictionarySize, &callback);
+      fprintf(f, "\n");
+      RINOK(res);
+    }
+  }
+  callback.Normalize();
+  fprintf(f, "----------------------------------------------------------------\nAvr:");
+  PrintTotals(f, callback.EncodeRes);
+  fprintf(f, "     ");
+  PrintTotals(f, callback.DecodeRes);
+  fprintf(f, "\nTot:");
+  CTotalBenchRes midRes;
+  midRes.SetMid(callback.EncodeRes, callback.DecodeRes);
+  PrintTotals(f, midRes);
+  fprintf(f, "\n");
+  return S_OK;
+}
+
+struct CTempValues
+{
+  UInt64 *Values;
+  CTempValues(UInt32 num) { Values = new UInt64[num]; }
+  ~CTempValues() { delete []Values; }
+};
+
+HRESULT CrcBenchCon(FILE *f, UInt32 numIterations, UInt32 numThreads, UInt32 dictionary)
+{
+  if (!CrcInternalTest())
+    return S_FALSE;
+
+  #ifdef BENCH_MT
+  UInt64 ramSize = NWindows::NSystem::GetRamSize();
+  UInt32 numCPUs = NWindows::NSystem::GetNumberOfProcessors();
+  PrintRequirements(f, "size: ", ramSize, "CPU hardware threads:", numCPUs);
+  if (numThreads == (UInt32)-1)
+    numThreads = numCPUs;
+  #else
+  numThreads = 1;
+  #endif
+  if (dictionary == (UInt32)-1)
+    dictionary = (1 << 24);
+
+  CTempValues speedTotals(numThreads);
+  fprintf(f, "\n\nSize");
+  for (UInt32 ti = 0; ti < numThreads; ti++)
+  {
+    fprintf(f, " %5d", ti + 1);
+    speedTotals.Values[ti] = 0;
+  }
+  fprintf(f, "\n\n");
+
+  UInt64 numSteps = 0;
+  for (UInt32 i = 0; i < numIterations; i++)
+  {
+    for (int pow = 10; pow < 32; pow++)
+    {
+      UInt32 bufSize = (UInt32)1 << pow;
+      if (bufSize > dictionary)
+        break;
+      fprintf(f, "%2d: ", pow);
+      UInt64 speed;
+      for (UInt32 ti = 0; ti < numThreads; ti++)
+      {
+        #ifdef BREAK_HANDLER
+        if (NConsoleClose::TestBreakSignal())
+          return E_ABORT;
+        #endif
+        RINOK(CrcBench(ti + 1, bufSize, speed));
+        PrintNumber(f, (speed >> 20), 5);
+        speedTotals.Values[ti] += speed;
+      }
+      fprintf(f, "\n");
+      numSteps++;
+    }
+  }
+  if (numSteps != 0)
+  {
+    fprintf(f, "\nAvg:");
+    for (UInt32 ti = 0; ti < numThreads; ti++)
+      PrintNumber(f, ((speedTotals.Values[ti] / numSteps) >> 20), 5);
+    fprintf(f, "\n");
+  }
+  return S_OK;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/LzmaBenchCon.h b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/LzmaBenchCon.h
new file mode 100644
index 0000000..ea8539d
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/LzmaBenchCon.h
@@ -0,0 +1,20 @@
+// LzmaBenchCon.h
+
+#ifndef __LZMABENCHCON_H
+#define __LZMABENCHCON_H
+
+#include <stdio.h>
+#include "../../../Common/Types.h"
+#ifdef EXTERNAL_LZMA
+#include "../../UI/Common/LoadCodecs.h"
+#endif
+HRESULT LzmaBenchCon(
+  #ifdef EXTERNAL_LZMA
+  CCodecs *codecs,
+  #endif
+  FILE *f, UInt32 numIterations, UInt32 numThreads, UInt32 dictionary);
+
+HRESULT CrcBenchCon(FILE *f, UInt32 numIterations, UInt32 numThreads, UInt32 dictionary);
+
+#endif
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/StdAfx.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/StdAfx.cpp
new file mode 100644
index 0000000..d0feea8
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/StdAfx.cpp
@@ -0,0 +1,3 @@
+// StdAfx.cpp
+
+#include "StdAfx.h"
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/StdAfx.h b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/StdAfx.h
new file mode 100644
index 0000000..e7fb698
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/StdAfx.h
@@ -0,0 +1,8 @@
+// StdAfx.h
+
+#ifndef __STDAFX_H
+#define __STDAFX_H
+
+#include "../../../Common/MyWindows.h"
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/makefile b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/makefile
new file mode 100644
index 0000000..1c4baa0
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/makefile
@@ -0,0 +1,79 @@
+PROG = lzma.exe
+LIBS = $(LIBS) user32.lib
+CFLAGS = $(CFLAGS) \
+  -DCOMPRESS_MF_MT \
+  -DBENCH_MT \
+
+LZMA_OBJS = \
+  $O\LzmaAlone.obj \
+  $O\LzmaBench.obj \
+  $O\LzmaBenchCon.obj \
+
+LZMA_OPT_OBJS = \
+  $O\LzmaDecoder.obj \
+  $O\LzmaEncoder.obj \
+
+COMMON_OBJS = \
+  $O\CommandLineParser.obj \
+  $O\CRC.obj \
+  $O\IntToString.obj \
+  $O\MyString.obj \
+  $O\StringConvert.obj \
+  $O\StringToInt.obj \
+  $O\MyVector.obj
+
+WIN_OBJS = \
+  $O\System.obj
+
+7ZIP_COMMON_OBJS = \
+  $O\InBuffer.obj \
+  $O\OutBuffer.obj \
+  $O\StreamUtils.obj \
+
+C_OBJS = \
+  $O\7zCrc.obj \
+  $O\Alloc.obj \
+  $O\Bra86.obj \
+  $O\LzFind.obj \
+  $O\LzFindMt.obj \
+  $O\LzmaDec.obj \
+  $O\LzmaEnc.obj \
+  $O\Threads.obj \
+
+C_LZMAUTIL_OBJS = \
+  $O\Lzma86Dec.obj \
+  $O\Lzma86Enc.obj \
+
+OBJS = \
+  $O\StdAfx.obj \
+  $(LZMA_OBJS) \
+  $(LZMA_OPT_OBJS) \
+  $(COMMON_OBJS) \
+  $(WIN_OBJS) \
+  $(7ZIP_COMMON_OBJS) \
+  $(C_OBJS) \
+  $(C_LZMAUTIL_OBJS) \
+  $O\FileStreams.obj \
+  $O\FileIO.obj \
+
+!include "../../../Build.mak"
+
+
+$(LZMA_OBJS): $(*B).cpp
+	$(COMPL)
+$(LZMA_OPT_OBJS): ../$(*B).cpp
+	$(COMPL_O2)
+$(COMMON_OBJS): ../../../Common/$(*B).cpp
+	$(COMPL)
+$(WIN_OBJS): ../../../Windows/$(*B).cpp
+	$(COMPL)
+$(7ZIP_COMMON_OBJS): ../../Common/$(*B).cpp
+	$(COMPL)
+$O\FileStreams.obj: ../../Common/FileStreams.cpp
+	$(COMPL)
+$O\FileIO.obj: ../../../Windows/FileIO.cpp
+	$(COMPL)
+$(C_OBJS): ../../../../C/$(*B).c
+	$(COMPL_O2)
+$(C_LZMAUTIL_OBJS): ../../../../C/LzmaUtil/$(*B).c
+	$(COMPL_O2)
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/makefile.gcc b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/makefile.gcc
new file mode 100644
index 0000000..8d53437
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LZMA_Alone/makefile.gcc
@@ -0,0 +1,135 @@
+PROG = lzma
+CXX = g++ -O2 -Wall
+CXX_C = gcc -O2 -Wall
+LIB = -lm
+RM = rm -f
+CFLAGS = -c
+
+ifdef SystemDrive
+IS_MINGW = 1
+endif
+
+ifdef IS_MINGW
+FILE_IO =FileIO
+FILE_IO_2 =Windows/$(FILE_IO)
+LIB2 = -luuid
+else
+FILE_IO =C_FileIO
+FILE_IO_2 =Common/$(FILE_IO)
+endif
+
+OBJS = \
+  LzmaAlone.o \
+  LzmaBench.o \
+  LzmaBenchCon.o \
+  LzmaDecoder.o \
+  LzmaEncoder.o \
+  InBuffer.o \
+  OutBuffer.o \
+  FileStreams.o \
+  StreamUtils.o \
+  $(FILE_IO).o \
+  CommandLineParser.o \
+  CRC.o \
+  IntToString.o \
+  MyString.o \
+  StringConvert.o \
+  StringToInt.o \
+  MyVector.o \
+  7zCrc.o \
+  Alloc.o \
+  Bra86.o \
+  LzFind.o \
+  LzmaDec.o \
+  LzmaEnc.o \
+  Lzma86Dec.o \
+  Lzma86Enc.o \
+
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+	$(CXX) -o $(PROG) $(LDFLAGS) $(OBJS) $(LIB) $(LIB2)
+
+LzmaAlone.o: LzmaAlone.cpp
+	$(CXX) $(CFLAGS) LzmaAlone.cpp
+
+LzmaBench.o: LzmaBench.cpp
+	$(CXX) $(CFLAGS) LzmaBench.cpp
+
+LzmaBenchCon.o: LzmaBenchCon.cpp
+	$(CXX) $(CFLAGS) LzmaBenchCon.cpp
+
+LzmaDecoder.o: ../LzmaDecoder.cpp
+	$(CXX) $(CFLAGS) ../LzmaDecoder.cpp
+
+LzmaEncoder.o: ../LzmaEncoder.cpp
+	$(CXX) $(CFLAGS) ../LzmaEncoder.cpp
+
+InBuffer.o: ../../Common/InBuffer.cpp
+	$(CXX) $(CFLAGS) ../../Common/InBuffer.cpp
+
+OutBuffer.o: ../../Common/OutBuffer.cpp
+	$(CXX) $(CFLAGS) ../../Common/OutBuffer.cpp
+
+FileStreams.o: ../../Common/FileStreams.cpp
+	$(CXX) $(CFLAGS) ../../Common/FileStreams.cpp
+
+StreamUtils.o: ../../Common/StreamUtils.cpp
+	$(CXX) $(CFLAGS) ../../Common/StreamUtils.cpp
+
+$(FILE_IO).o: ../../../$(FILE_IO_2).cpp
+	$(CXX) $(CFLAGS) ../../../$(FILE_IO_2).cpp
+
+
+CommandLineParser.o: ../../../Common/CommandLineParser.cpp
+	$(CXX) $(CFLAGS) ../../../Common/CommandLineParser.cpp
+
+CRC.o: ../../../Common/CRC.cpp
+	$(CXX) $(CFLAGS) ../../../Common/CRC.cpp
+
+MyWindows.o: ../../../Common/MyWindows.cpp
+	$(CXX) $(CFLAGS) ../../../Common/MyWindows.cpp
+
+IntToString.o: ../../../Common/IntToString.cpp
+	$(CXX) $(CFLAGS) ../../../Common/IntToString.cpp
+
+MyString.o: ../../../Common/MyString.cpp
+	$(CXX) $(CFLAGS) ../../../Common/MyString.cpp
+
+StringConvert.o: ../../../Common/StringConvert.cpp
+	$(CXX) $(CFLAGS) ../../../Common/StringConvert.cpp
+
+StringToInt.o: ../../../Common/StringToInt.cpp
+	$(CXX) $(CFLAGS) ../../../Common/StringToInt.cpp
+
+MyVector.o: ../../../Common/MyVector.cpp
+	$(CXX) $(CFLAGS) ../../../Common/MyVector.cpp
+
+7zCrc.o: ../../../../C/7zCrc.c
+	$(CXX_C) $(CFLAGS) ../../../../C/7zCrc.c
+
+Alloc.o: ../../../../C/Alloc.c
+	$(CXX_C) $(CFLAGS) ../../../../C/Alloc.c
+
+Bra86.o: ../../../../C/Bra86.c
+	$(CXX_C) $(CFLAGS) ../../../../C/Bra86.c
+
+LzFind.o: ../../../../C/LzFind.c
+	$(CXX_C) $(CFLAGS) ../../../../C/LzFind.c
+
+LzmaDec.o: ../../../../C/LzmaDec.c
+	$(CXX_C) $(CFLAGS) ../../../../C/LzmaDec.c
+
+LzmaEnc.o: ../../../../C/LzmaEnc.c
+	$(CXX_C) $(CFLAGS) ../../../../C/LzmaEnc.c
+
+Lzma86Dec.o: ../../../../C/LzmaUtil/Lzma86Dec.c
+	$(CXX_C) $(CFLAGS) ../../../../C/LzmaUtil/Lzma86Dec.c
+
+Lzma86Enc.o: ../../../../C/LzmaUtil/Lzma86Enc.c
+	$(CXX_C) $(CFLAGS) ../../../../C/LzmaUtil/Lzma86Enc.c
+
+clean:
+	-$(RM) $(PROG) $(OBJS)
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/LzmaDecoder.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LzmaDecoder.cpp
new file mode 100644
index 0000000..5bfffe9
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LzmaDecoder.cpp
@@ -0,0 +1,190 @@
+// LzmaDecoder.cpp
+
+#include "StdAfx.h"
+
+extern "C"
+{
+#include "../../../C/Alloc.h"
+}
+
+#include "../Common/StreamUtils.h"
+
+#include "LzmaDecoder.h"
+
+static HRESULT SResToHRESULT(SRes res)
+{
+  switch(res)
+  {
+    case SZ_OK: return S_OK;
+    case SZ_ERROR_MEM: return E_OUTOFMEMORY;
+    case SZ_ERROR_PARAM: return E_INVALIDARG;
+    case SZ_ERROR_UNSUPPORTED: return E_NOTIMPL;
+    // case SZ_ERROR_PROGRESS: return E_ABORT;
+    case SZ_ERROR_DATA: return S_FALSE;
+  }
+  return E_FAIL;
+}
+
+namespace NCompress {
+namespace NLzma {
+
+static const UInt32 kInBufSize = 1 << 20;
+
+CDecoder::CDecoder(): _inBuf(0), _outSizeDefined(false), FinishStream(false)
+{
+  LzmaDec_Construct(&_state);
+}
+
+static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); }
+static void SzFree(void *p, void *address) { p = p; MyFree(address); }
+static ISzAlloc g_Alloc = { SzAlloc, SzFree };
+
+CDecoder::~CDecoder()
+{
+  LzmaDec_Free(&_state, &g_Alloc);
+  MyFree(_inBuf);
+}
+
+STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *prop, UInt32 size)
+{
+  RINOK(SResToHRESULT(LzmaDec_Allocate(&_state, prop, size, &g_Alloc)));
+
+  if (_inBuf == 0)
+  {
+    _inBuf = (Byte *)MyAlloc(kInBufSize);
+    if (_inBuf == 0)
+      return E_OUTOFMEMORY;
+  }
+
+  return S_OK;
+}
+
+STDMETHODIMP CDecoder::GetInStreamProcessedSize(UInt64 *value) { *value = _inSizeProcessed; return S_OK; }
+STDMETHODIMP CDecoder::SetInStream(ISequentialInStream *inStream) { _inStream = inStream; return S_OK; }
+STDMETHODIMP CDecoder::ReleaseInStream() { _inStream.Release(); return S_OK; }
+
+STDMETHODIMP CDecoder::SetOutStreamSize(const UInt64 *outSize)
+{
+  _outSizeDefined = (outSize != NULL);
+  if (_outSizeDefined)
+    _outSize = *outSize;
+
+  LzmaDec_Init(&_state);
+  
+  _inPos = _inSize = 0;
+  _inSizeProcessed = _outSizeProcessed = 0;
+  return S_OK;
+}
+
+STDMETHODIMP CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
+    const UInt64 * /* inSize */, const UInt64 *outSize, ICompressProgressInfo *progress)
+{
+  if (_inBuf == 0)
+    return S_FALSE;
+  SetOutStreamSize(outSize);
+
+  for (;;)
+  {
+    if (_inPos == _inSize)
+    {
+      _inPos = _inSize = 0;
+      RINOK(inStream->Read(_inBuf, kInBufSize, &_inSize));
+    }
+
+    SizeT dicPos = _state.dicPos;
+    SizeT curSize = _state.dicBufSize - dicPos;
+    const UInt32 kStepSize = ((UInt32)1 << 22);
+    if (curSize > kStepSize)
+      curSize = (SizeT)kStepSize;
+    
+    ELzmaFinishMode finishMode = LZMA_FINISH_ANY;
+    if (_outSizeDefined)
+    {
+      const UInt64 rem = _outSize - _outSizeProcessed;
+      if (rem < curSize)
+      {
+        curSize = (SizeT)rem;
+        if (FinishStream)
+          finishMode = LZMA_FINISH_END;
+      }
+    }
+
+    SizeT inSizeProcessed = _inSize - _inPos;
+    ELzmaStatus status;
+    SRes res = LzmaDec_DecodeToDic(&_state, dicPos + curSize, _inBuf + _inPos, &inSizeProcessed, finishMode, &status);
+
+    _inPos += (UInt32)inSizeProcessed;
+    _inSizeProcessed += inSizeProcessed;
+    SizeT outSizeProcessed = _state.dicPos - dicPos;
+    _outSizeProcessed += outSizeProcessed;
+
+    bool finished = (inSizeProcessed == 0 && outSizeProcessed == 0);
+    bool stopDecoding = (_outSizeDefined && _outSizeProcessed >= _outSize);
+
+    if (res != 0 || _state.dicPos == _state.dicBufSize || finished || stopDecoding)
+    {
+      HRESULT res2 = WriteStream(outStream, _state.dic, _state.dicPos);
+      if (res != 0)
+        return S_FALSE;
+      RINOK(res2);
+      if (stopDecoding)
+        return S_OK;
+      if (finished)
+        return (status == LZMA_STATUS_FINISHED_WITH_MARK ? S_OK : S_FALSE);
+    }
+    if (_state.dicPos == _state.dicBufSize)
+      _state.dicPos = 0;
+
+    if (progress != NULL)
+    {
+      RINOK(progress->SetRatioInfo(&_inSizeProcessed, &_outSizeProcessed));
+    }
+  }
+}
+
+#ifndef NO_READ_FROM_CODER
+
+STDMETHODIMP CDecoder::Read(void *data, UInt32 size, UInt32 *processedSize)
+{
+  if (processedSize)
+    *processedSize = 0;
+  do
+  {
+    if (_inPos == _inSize)
+    {
+      _inPos = _inSize = 0;
+      RINOK(_inStream->Read(_inBuf, kInBufSize, &_inSize));
+    }
+    {
+      SizeT inProcessed = _inSize - _inPos;
+
+      if (_outSizeDefined)
+      {
+        const UInt64 rem = _outSize - _outSizeProcessed;
+        if (rem < size)
+          size = (UInt32)rem;
+      }
+
+      SizeT outProcessed = size;
+      ELzmaStatus status;
+      SRes res = LzmaDec_DecodeToBuf(&_state, (Byte *)data, &outProcessed,
+          _inBuf + _inPos, &inProcessed, LZMA_FINISH_ANY, &status);
+      _inPos += (UInt32)inProcessed;
+      _inSizeProcessed += inProcessed;
+      _outSizeProcessed += outProcessed;
+      size -= (UInt32)outProcessed;
+      data = (Byte *)data + outProcessed;
+      if (processedSize)
+        *processedSize += (UInt32)outProcessed;
+      RINOK(SResToHRESULT(res));
+      if (inProcessed == 0 && outProcessed == 0)
+        return S_OK;
+    }
+  }
+  while (size != 0);
+  return S_OK;
+}
+
+#endif
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/LzmaDecoder.h b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LzmaDecoder.h
new file mode 100644
index 0000000..e132d5c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LzmaDecoder.h
@@ -0,0 +1,73 @@
+// LzmaDecoder.h
+
+#ifndef __LZMA_DECODER_H
+#define __LZMA_DECODER_H
+
+extern "C"
+{
+#include "../../../C/LzmaDec.h"
+}
+
+#include "../../Common/MyCom.h"
+#include "../ICoder.h"
+
+namespace NCompress {
+namespace NLzma {
+
+class CDecoder:
+  public ICompressCoder,
+  public ICompressSetDecoderProperties2,
+  public ICompressGetInStreamProcessedSize,
+  #ifndef NO_READ_FROM_CODER
+  public ICompressSetInStream,
+  public ICompressSetOutStreamSize,
+  public ISequentialInStream,
+  #endif
+  public CMyUnknownImp
+{
+  CMyComPtr<ISequentialInStream> _inStream;
+  Byte *_inBuf;
+  UInt32 _inPos;
+  UInt32 _inSize;
+  CLzmaDec _state;
+  bool _outSizeDefined;
+  UInt64 _outSize;
+  UInt64 _inSizeProcessed;
+  UInt64 _outSizeProcessed;
+public:
+
+  #ifndef NO_READ_FROM_CODER
+  MY_UNKNOWN_IMP5(
+      ICompressSetDecoderProperties2,
+      ICompressGetInStreamProcessedSize,
+      ICompressSetInStream,
+      ICompressSetOutStreamSize,
+      ISequentialInStream)
+  #else
+  MY_UNKNOWN_IMP2(
+      ICompressSetDecoderProperties2,
+      ICompressGetInStreamProcessedSize)
+  #endif
+
+  STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream,
+      const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress);
+  STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size);
+  STDMETHOD(GetInStreamProcessedSize)(UInt64 *value);
+  STDMETHOD(SetInStream)(ISequentialInStream *inStream);
+  STDMETHOD(ReleaseInStream)();
+  STDMETHOD(SetOutStreamSize)(const UInt64 *outSize);
+
+  #ifndef NO_READ_FROM_CODER
+  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
+  #endif
+
+  bool FinishStream;
+
+  CDecoder();
+  virtual ~CDecoder();
+
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/LzmaEncoder.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LzmaEncoder.cpp
new file mode 100644
index 0000000..986e1ed
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LzmaEncoder.cpp
@@ -0,0 +1,211 @@
+// LzmaEncoder.cpp
+
+#include "StdAfx.h"
+
+extern "C"
+{
+#include "../../../C/Alloc.h"
+}
+
+#include "../Common/StreamUtils.h"
+
+#include "LzmaEncoder.h"
+
+static HRESULT SResToHRESULT(SRes res)
+{
+  switch(res)
+  {
+    case SZ_OK: return S_OK;
+    case SZ_ERROR_MEM: return E_OUTOFMEMORY;
+    case SZ_ERROR_PARAM: return E_INVALIDARG;
+    // case SZ_ERROR_THREAD: return E_FAIL;
+  }
+  return E_FAIL;
+}
+
+namespace NCompress {
+namespace NLzma {
+
+static const UInt32 kStreamStepSize = (UInt32)1 << 31;
+
+static SRes MyRead(void *object, void *data, size_t *size)
+{
+  UInt32 curSize = ((*size < kStreamStepSize) ? (UInt32)*size : kStreamStepSize);
+  HRESULT res = ((CSeqInStream *)object)->RealStream->Read(data, curSize, &curSize);
+  *size = curSize;
+  return (SRes)res;
+}
+
+static size_t MyWrite(void *object, const void *data, size_t size)
+{
+  CSeqOutStream *p = (CSeqOutStream *)object;
+  p->Res = WriteStream(p->RealStream, data, size);
+  if (p->Res != 0)
+    return 0;
+  return size;
+}
+
+static void *SzBigAlloc(void *, size_t size) { return BigAlloc(size); }
+static void SzBigFree(void *, void *address) { BigFree(address); }
+static ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree };
+
+static void *SzAlloc(void *, size_t size) { return MyAlloc(size); }
+static void SzFree(void *, void *address) { MyFree(address); }
+static ISzAlloc g_Alloc = { SzAlloc, SzFree };
+
+CEncoder::CEncoder()
+{
+  _seqInStream.SeqInStream.Read = MyRead;
+  _seqOutStream.SeqOutStream.Write = MyWrite;
+  _encoder = 0;
+  _encoder = LzmaEnc_Create(&g_Alloc);
+  if (_encoder == 0)
+    throw 1;
+}
+
+CEncoder::~CEncoder()
+{
+  if (_encoder != 0)
+    LzmaEnc_Destroy(_encoder, &g_Alloc, &g_BigAlloc);
+}
+
+inline wchar_t GetUpperChar(wchar_t c)
+{
+  if (c >= 'a' && c <= 'z')
+    c -= 0x20;
+  return c;
+}
+
+static int ParseMatchFinder(const wchar_t *s, int *btMode, int *numHashBytes)
+{
+  wchar_t c = GetUpperChar(*s++);
+  if (c == L'H')
+  {
+    if (GetUpperChar(*s++) != L'C')
+      return 0;
+    int numHashBytesLoc = (int)(*s++ - L'0');
+    if (numHashBytesLoc < 4 || numHashBytesLoc > 4)
+      return 0;
+    if (*s++ != 0)
+      return 0;
+    *btMode = 0;
+    *numHashBytes = numHashBytesLoc;
+    return 1;
+  }
+  if (c != L'B')
+    return 0;
+
+  if (GetUpperChar(*s++) != L'T')
+    return 0;
+  int numHashBytesLoc = (int)(*s++ - L'0');
+  if (numHashBytesLoc < 2 || numHashBytesLoc > 4)
+    return 0;
+  c = GetUpperChar(*s++);
+  if (c != L'\0')
+    return 0;
+  *btMode = 1;
+  *numHashBytes = numHashBytesLoc;
+  return 1;
+}
+
+STDMETHODIMP CEncoder::SetCoderProperties(const PROPID *propIDs,
+    const PROPVARIANT *coderProps, UInt32 numProps)
+{
+  CLzmaEncProps props;
+  LzmaEncProps_Init(&props);
+
+  for (UInt32 i = 0; i < numProps; i++)
+  {
+    const PROPVARIANT &prop = coderProps[i];
+    switch (propIDs[i])
+    {
+      case NCoderPropID::kNumFastBytes:
+        if (prop.vt != VT_UI4) return E_INVALIDARG; props.fb = prop.ulVal; break;
+      case NCoderPropID::kMatchFinderCycles:
+        if (prop.vt != VT_UI4) return E_INVALIDARG; props.mc = prop.ulVal; break;
+      case NCoderPropID::kAlgorithm:
+        if (prop.vt != VT_UI4) return E_INVALIDARG; props.algo = prop.ulVal; break;
+      case NCoderPropID::kDictionarySize:
+        if (prop.vt != VT_UI4) return E_INVALIDARG; props.dictSize = prop.ulVal; break;
+      case NCoderPropID::kPosStateBits:
+        if (prop.vt != VT_UI4) return E_INVALIDARG; props.pb = prop.ulVal; break;
+      case NCoderPropID::kLitPosBits:
+        if (prop.vt != VT_UI4) return E_INVALIDARG; props.lp = prop.ulVal; break;
+      case NCoderPropID::kLitContextBits:
+        if (prop.vt != VT_UI4) return E_INVALIDARG; props.lc = prop.ulVal; break;
+      case NCoderPropID::kNumThreads:
+        if (prop.vt != VT_UI4) return E_INVALIDARG; props.numThreads = prop.ulVal; break;
+      case NCoderPropID::kMultiThread:
+        if (prop.vt != VT_BOOL) return E_INVALIDARG; props.numThreads = ((prop.boolVal == VARIANT_TRUE) ? 2 : 1); break;
+      case NCoderPropID::kEndMarker:
+        if (prop.vt != VT_BOOL) return E_INVALIDARG; props.writeEndMark = (prop.boolVal == VARIANT_TRUE); break;
+      case NCoderPropID::kMatchFinder:
+        if (prop.vt != VT_BSTR) return E_INVALIDARG;
+        if (!ParseMatchFinder(prop.bstrVal, &props.btMode, &props.numHashBytes /* , &_matchFinderBase.skipModeBits */))
+          return E_INVALIDARG; break;
+      default:
+        return E_INVALIDARG;
+    }
+  }
+  return SResToHRESULT(LzmaEnc_SetProps(_encoder, &props));
+}
+
+STDMETHODIMP CEncoder::WriteCoderProperties(ISequentialOutStream *outStream)
+{
+  Byte props[LZMA_PROPS_SIZE];
+  size_t size = LZMA_PROPS_SIZE;
+  RINOK(LzmaEnc_WriteProperties(_encoder, props, &size));
+  return WriteStream(outStream, props, size);
+}
+
+STDMETHODIMP CEncoder::SetOutStream(ISequentialOutStream *outStream)
+{
+  _seqOutStream.RealStream = outStream;
+  _seqOutStream.Res = S_OK;
+  return S_OK;
+}
+
+STDMETHODIMP CEncoder::ReleaseOutStream()
+{
+  _seqOutStream.RealStream.Release();
+  return S_OK;
+}
+
+typedef struct _CCompressProgressImp
+{
+  ICompressProgress p;
+  ICompressProgressInfo *Progress;
+  HRESULT Res;
+} CCompressProgressImp;
+
+#define PROGRESS_UNKNOWN_VALUE ((UInt64)(Int64)-1)
+
+#define CONVERT_PR_VAL(x) (x == PROGRESS_UNKNOWN_VALUE ? NULL : &x)
+
+SRes CompressProgress(void *pp, UInt64 inSize, UInt64 outSize)
+{
+  CCompressProgressImp *p = (CCompressProgressImp *)pp;
+  p->Res = p->Progress->SetRatioInfo(CONVERT_PR_VAL(inSize), CONVERT_PR_VAL(outSize));
+  return (SRes)p->Res;
+}
+
+STDMETHODIMP CEncoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream,
+    const UInt64 * /* inSize */, const UInt64 * /* outSize */, ICompressProgressInfo *progress)
+{
+  CCompressProgressImp progressImp;
+  progressImp.p.Progress = CompressProgress;
+  progressImp.Progress = progress;
+  progressImp.Res = SZ_OK;
+
+  _seqInStream.RealStream = inStream;
+  SetOutStream(outStream);
+  SRes res = LzmaEnc_Encode(_encoder, &_seqOutStream.SeqOutStream, &_seqInStream.SeqInStream, progress ? &progressImp.p : NULL, &g_Alloc, &g_BigAlloc);
+  ReleaseOutStream();
+  if (res == SZ_ERROR_WRITE && _seqOutStream.Res != S_OK)
+    return _seqOutStream.Res;
+  if (res == SZ_ERROR_PROGRESS && progressImp.Res != S_OK)
+    return progressImp.Res;
+  return SResToHRESULT(res);
+}
+  
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/LzmaEncoder.h b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LzmaEncoder.h
new file mode 100644
index 0000000..4205d2b
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LzmaEncoder.h
@@ -0,0 +1,64 @@
+// LzmaEncoder.h
+
+#ifndef __LZMA_ENCODER_H
+#define __LZMA_ENCODER_H
+
+extern "C"
+{
+#include "../../../C/LzmaEnc.h"
+}
+
+#include "../../Common/MyCom.h"
+
+#include "../ICoder.h"
+
+namespace NCompress {
+namespace NLzma {
+
+struct CSeqInStream
+{
+  ISeqInStream SeqInStream;
+  ISequentialInStream *RealStream;
+};
+
+struct CSeqOutStream
+{
+  ISeqOutStream SeqOutStream;
+  CMyComPtr<ISequentialOutStream> RealStream;
+  HRESULT Res;
+};
+
+class CEncoder :
+  public ICompressCoder,
+  public ICompressSetOutStream,
+  public ICompressSetCoderProperties,
+  public ICompressWriteCoderProperties,
+  public CMyUnknownImp
+{
+  CLzmaEncHandle _encoder;
+ 
+  CSeqInStream _seqInStream;
+  CSeqOutStream _seqOutStream;
+
+public:
+  CEncoder();
+
+  MY_UNKNOWN_IMP3(
+      ICompressSetOutStream,
+      ICompressSetCoderProperties,
+      ICompressWriteCoderProperties
+      )
+    
+  STDMETHOD(Code)(ISequentialInStream *inStream, ISequentialOutStream *outStream,
+      const UInt64 *inSize, const UInt64 *outSize, ICompressProgressInfo *progress);
+  STDMETHOD(SetCoderProperties)(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps);
+  STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStream);
+  STDMETHOD(SetOutStream)(ISequentialOutStream *outStream);
+  STDMETHOD(ReleaseOutStream)();
+
+  virtual ~CEncoder();
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/LzmaRegister.cpp b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LzmaRegister.cpp
new file mode 100644
index 0000000..96ed0ba
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/LzmaRegister.cpp
@@ -0,0 +1,20 @@
+// LzmaRegister.cpp
+
+#include "StdAfx.h"
+
+#include "../Common/RegisterCodec.h"
+
+#include "LzmaDecoder.h"
+
+static void *CreateCodec() { return (void *)(ICompressCoder *)(new NCompress::NLzma::CDecoder); }
+#ifndef EXTRACT_ONLY
+#include "LzmaEncoder.h"
+static void *CreateCodecOut() { return (void *)(ICompressCoder *)(new NCompress::NLzma::CEncoder);  }
+#else
+#define CreateCodecOut 0
+#endif
+
+static CCodecInfo g_CodecInfo =
+  { CreateCodec, CreateCodecOut, 0x030101, L"LZMA", 1, false };
+
+REGISTER_CODEC(LZMA)
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/RangeCoder.h b/third_party/lzma/v4_65/files/CPP/7zip/Compress/RangeCoder.h
new file mode 100644
index 0000000..1e14ac1
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/RangeCoder.h
@@ -0,0 +1,204 @@
+// Compress/RangeCoder.h
+
+#ifndef __COMPRESS_RANGE_CODER_H
+#define __COMPRESS_RANGE_CODER_H
+
+#include "../Common/InBuffer.h"
+#include "../Common/OutBuffer.h"
+
+namespace NCompress {
+namespace NRangeCoder {
+
+const int kNumTopBits = 24;
+const UInt32 kTopValue = (1 << kNumTopBits);
+
+class CEncoder
+{
+  UInt32 _cacheSize;
+  Byte _cache;
+public:
+  UInt64 Low;
+  UInt32 Range;
+  COutBuffer Stream;
+  bool Create(UInt32 bufferSize) { return Stream.Create(bufferSize); }
+
+  void SetStream(ISequentialOutStream *stream) { Stream.SetStream(stream); }
+  void Init()
+  {
+    Stream.Init();
+    Low = 0;
+    Range = 0xFFFFFFFF;
+    _cacheSize = 1;
+    _cache = 0;
+  }
+
+  void FlushData()
+  {
+    // Low += 1;
+    for(int i = 0; i < 5; i++)
+      ShiftLow();
+  }
+
+  HRESULT FlushStream() { return Stream.Flush();  }
+
+  void ReleaseStream() { Stream.ReleaseStream(); }
+
+  void Encode(UInt32 start, UInt32 size, UInt32 total)
+  {
+    Low += start * (Range /= total);
+    Range *= size;
+    while (Range < kTopValue)
+    {
+      Range <<= 8;
+      ShiftLow();
+    }
+  }
+
+  void ShiftLow()
+  {
+    if ((UInt32)Low < (UInt32)0xFF000000 || (int)(Low >> 32) != 0)
+    {
+      Byte temp = _cache;
+      do
+      {
+        Stream.WriteByte((Byte)(temp + (Byte)(Low >> 32)));
+        temp = 0xFF;
+      }
+      while(--_cacheSize != 0);
+      _cache = (Byte)((UInt32)Low >> 24);
+    }
+    _cacheSize++;
+    Low = (UInt32)Low << 8;
+  }
+  
+  void EncodeDirectBits(UInt32 value, int numBits)
+  {
+    for (numBits--; numBits >= 0; numBits--)
+    {
+      Range >>= 1;
+      Low += Range & (0 - ((value >> numBits) & 1));
+      if (Range < kTopValue)
+      {
+        Range <<= 8;
+        ShiftLow();
+      }
+    }
+  }
+
+  void EncodeBit(UInt32 size0, UInt32 numTotalBits, UInt32 symbol)
+  {
+    UInt32 newBound = (Range >> numTotalBits) * size0;
+    if (symbol == 0)
+      Range = newBound;
+    else
+    {
+      Low += newBound;
+      Range -= newBound;
+    }
+    while (Range < kTopValue)
+    {
+      Range <<= 8;
+      ShiftLow();
+    }
+  }
+
+  UInt64 GetProcessedSize() {  return Stream.GetProcessedSize() + _cacheSize + 4; }
+};
+
+class CDecoder
+{
+public:
+  CInBuffer Stream;
+  UInt32 Range;
+  UInt32 Code;
+  bool Create(UInt32 bufferSize) { return Stream.Create(bufferSize); }
+
+  void Normalize()
+  {
+    while (Range < kTopValue)
+    {
+      Code = (Code << 8) | Stream.ReadByte();
+      Range <<= 8;
+    }
+  }
+  
+  void SetStream(ISequentialInStream *stream) { Stream.SetStream(stream); }
+  void Init()
+  {
+    Stream.Init();
+    Code = 0;
+    Range = 0xFFFFFFFF;
+    for(int i = 0; i < 5; i++)
+      Code = (Code << 8) | Stream.ReadByte();
+  }
+
+  void ReleaseStream() { Stream.ReleaseStream(); }
+
+  UInt32 GetThreshold(UInt32 total)
+  {
+    return (Code) / ( Range /= total);
+  }
+
+  void Decode(UInt32 start, UInt32 size)
+  {
+    Code -= start * Range;
+    Range *= size;
+    Normalize();
+  }
+
+  UInt32 DecodeDirectBits(int numTotalBits)
+  {
+    UInt32 range = Range;
+    UInt32 code = Code;
+    UInt32 result = 0;
+    for (int i = numTotalBits; i != 0; i--)
+    {
+      range >>= 1;
+      /*
+      result <<= 1;
+      if (code >= range)
+      {
+        code -= range;
+        result |= 1;
+      }
+      */
+      UInt32 t = (code - range) >> 31;
+      code -= range & (t - 1);
+      result = (result << 1) | (1 - t);
+
+      if (range < kTopValue)
+      {
+        code = (code << 8) | Stream.ReadByte();
+        range <<= 8;
+      }
+    }
+    Range = range;
+    Code = code;
+    return result;
+  }
+
+  UInt32 DecodeBit(UInt32 size0, UInt32 numTotalBits)
+  {
+    UInt32 newBound = (Range >> numTotalBits) * size0;
+    UInt32 symbol;
+    if (Code < newBound)
+    {
+      symbol = 0;
+      Range = newBound;
+    }
+    else
+    {
+      symbol = 1;
+      Code -= newBound;
+      Range -= newBound;
+    }
+    Normalize();
+    return symbol;
+  }
+
+  UInt64 GetProcessedSize() {return Stream.GetProcessedSize(); }
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/RangeCoderBit.h b/third_party/lzma/v4_65/files/CPP/7zip/Compress/RangeCoderBit.h
new file mode 100644
index 0000000..566cdd4
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/RangeCoderBit.h
@@ -0,0 +1,113 @@
+// Compress/RangeCoderBit.h
+
+#ifndef __COMPRESS_RANGE_CODER_BIT_H
+#define __COMPRESS_RANGE_CODER_BIT_H
+
+#include "RangeCoder.h"
+
+namespace NCompress {
+namespace NRangeCoder {
+
+const int kNumBitModelTotalBits  = 11;
+const UInt32 kBitModelTotal = (1 << kNumBitModelTotalBits);
+
+const int kNumMoveReducingBits = 4;
+
+const int kNumBitPriceShiftBits = 4;
+const UInt32 kBitPrice = 1 << kNumBitPriceShiftBits;
+
+extern UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits];
+
+template <int numMoveBits>
+class CBitModel
+{
+public:
+  UInt32 Prob;
+  void UpdateModel(UInt32 symbol)
+  {
+    /*
+    Prob -= (Prob + ((symbol - 1) & ((1 << numMoveBits) - 1))) >> numMoveBits;
+    Prob += (1 - symbol) << (kNumBitModelTotalBits - numMoveBits);
+    */
+    if (symbol == 0)
+      Prob += (kBitModelTotal - Prob) >> numMoveBits;
+    else
+      Prob -= (Prob) >> numMoveBits;
+  }
+public:
+  void Init() { Prob = kBitModelTotal / 2; }
+};
+
+template <int numMoveBits>
+class CBitEncoder: public CBitModel<numMoveBits>
+{
+public:
+  void Encode(CEncoder *encoder, UInt32 symbol)
+  {
+    /*
+    encoder->EncodeBit(this->Prob, kNumBitModelTotalBits, symbol);
+    this->UpdateModel(symbol);
+    */
+    UInt32 newBound = (encoder->Range >> kNumBitModelTotalBits) * this->Prob;
+    if (symbol == 0)
+    {
+      encoder->Range = newBound;
+      this->Prob += (kBitModelTotal - this->Prob) >> numMoveBits;
+    }
+    else
+    {
+      encoder->Low += newBound;
+      encoder->Range -= newBound;
+      this->Prob -= (this->Prob) >> numMoveBits;
+    }
+    if (encoder->Range < kTopValue)
+    {
+      encoder->Range <<= 8;
+      encoder->ShiftLow();
+    }
+  }
+  UInt32 GetPrice(UInt32 symbol) const
+  {
+    return ProbPrices[(this->Prob ^ ((-(int)symbol)) & (kBitModelTotal - 1)) >> kNumMoveReducingBits];
+  }
+  UInt32 GetPrice0() const { return ProbPrices[this->Prob >> kNumMoveReducingBits]; }
+  UInt32 GetPrice1() const { return ProbPrices[(this->Prob ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits]; }
+};
+
+
+template <int numMoveBits>
+class CBitDecoder: public CBitModel<numMoveBits>
+{
+public:
+  UInt32 Decode(CDecoder *decoder)
+  {
+    UInt32 newBound = (decoder->Range >> kNumBitModelTotalBits) * this->Prob;
+    if (decoder->Code < newBound)
+    {
+      decoder->Range = newBound;
+      this->Prob += (kBitModelTotal - this->Prob) >> numMoveBits;
+      if (decoder->Range < kTopValue)
+      {
+        decoder->Code = (decoder->Code << 8) | decoder->Stream.ReadByte();
+        decoder->Range <<= 8;
+      }
+      return 0;
+    }
+    else
+    {
+      decoder->Range -= newBound;
+      decoder->Code -= newBound;
+      this->Prob -= (this->Prob) >> numMoveBits;
+      if (decoder->Range < kTopValue)
+      {
+        decoder->Code = (decoder->Code << 8) | decoder->Stream.ReadByte();
+        decoder->Range <<= 8;
+      }
+      return 1;
+    }
+  }
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/Compress/StdAfx.h b/third_party/lzma/v4_65/files/CPP/7zip/Compress/StdAfx.h
new file mode 100644
index 0000000..99a8aa4
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/Compress/StdAfx.h
@@ -0,0 +1,8 @@
+// StdAfx.h
+
+#ifndef __STDAFX_H
+#define __STDAFX_H
+
+#include "../../Common/MyWindows.h"
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/ICoder.h b/third_party/lzma/v4_65/files/CPP/7zip/ICoder.h
new file mode 100644
index 0000000..262dfdb
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/ICoder.h
@@ -0,0 +1,186 @@
+// ICoder.h
+
+#ifndef __ICODER_H
+#define __ICODER_H
+
+#include "IStream.h"
+
+#define CODER_INTERFACE(i, x) DECL_INTERFACE(i, 4, x)
+
+CODER_INTERFACE(ICompressProgressInfo, 0x04)
+{
+  STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize) PURE;
+};
+
+CODER_INTERFACE(ICompressCoder, 0x05)
+{
+  STDMETHOD(Code)(ISequentialInStream *inStream,
+      ISequentialOutStream *outStream,
+      const UInt64 *inSize,
+      const UInt64 *outSize,
+      ICompressProgressInfo *progress) PURE;
+};
+
+CODER_INTERFACE(ICompressCoder2, 0x18)
+{
+  STDMETHOD(Code)(ISequentialInStream **inStreams,
+      const UInt64 **inSizes,
+      UInt32 numInStreams,
+      ISequentialOutStream **outStreams,
+      const UInt64 **outSizes,
+      UInt32 numOutStreams,
+      ICompressProgressInfo *progress) PURE;
+};
+
+namespace NCoderPropID
+{
+  enum EEnum
+  {
+    kDictionarySize = 0x400,
+    kUsedMemorySize,
+    kOrder,
+    kBlockSize,
+    kPosStateBits = 0x440,
+    kLitContextBits,
+    kLitPosBits,
+    kNumFastBytes = 0x450,
+    kMatchFinder,
+    kMatchFinderCycles,
+    kNumPasses = 0x460,
+    kAlgorithm = 0x470,
+    kMultiThread = 0x480,
+    kNumThreads,
+    kEndMarker = 0x490
+  };
+}
+
+CODER_INTERFACE(ICompressSetCoderProperties, 0x20)
+{
+  STDMETHOD(SetCoderProperties)(const PROPID *propIDs,
+      const PROPVARIANT *properties, UInt32 numProperties) PURE;
+};
+
+/*
+CODER_INTERFACE(ICompressSetCoderProperties, 0x21)
+{
+  STDMETHOD(SetDecoderProperties)(ISequentialInStream *inStream) PURE;
+};
+*/
+
+CODER_INTERFACE(ICompressSetDecoderProperties2, 0x22)
+{
+  STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size) PURE;
+};
+
+CODER_INTERFACE(ICompressWriteCoderProperties, 0x23)
+{
+  STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStreams) PURE;
+};
+
+CODER_INTERFACE(ICompressGetInStreamProcessedSize, 0x24)
+{
+  STDMETHOD(GetInStreamProcessedSize)(UInt64 *value) PURE;
+};
+
+CODER_INTERFACE(ICompressSetCoderMt, 0x25)
+{
+  STDMETHOD(SetNumberOfThreads)(UInt32 numThreads) PURE;
+};
+
+CODER_INTERFACE(ICompressGetSubStreamSize, 0x30)
+{
+  STDMETHOD(GetSubStreamSize)(UInt64 subStream, UInt64 *value) PURE;
+};
+
+CODER_INTERFACE(ICompressSetInStream, 0x31)
+{
+  STDMETHOD(SetInStream)(ISequentialInStream *inStream) PURE;
+  STDMETHOD(ReleaseInStream)() PURE;
+};
+
+CODER_INTERFACE(ICompressSetOutStream, 0x32)
+{
+  STDMETHOD(SetOutStream)(ISequentialOutStream *outStream) PURE;
+  STDMETHOD(ReleaseOutStream)() PURE;
+};
+
+CODER_INTERFACE(ICompressSetInStreamSize, 0x33)
+{
+  STDMETHOD(SetInStreamSize)(const UInt64 *inSize) PURE;
+};
+
+CODER_INTERFACE(ICompressSetOutStreamSize, 0x34)
+{
+  STDMETHOD(SetOutStreamSize)(const UInt64 *outSize) PURE;
+};
+
+CODER_INTERFACE(ICompressFilter, 0x40)
+{
+  STDMETHOD(Init)() PURE;
+  STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size) PURE;
+  // Filter return outSize (UInt32)
+  // if (outSize <= size): Filter have converted outSize bytes
+  // if (outSize > size): Filter have not converted anything.
+  //      and it needs at least outSize bytes to convert one block
+  //      (it's for crypto block algorithms).
+};
+
+CODER_INTERFACE(ICompressCodecsInfo, 0x60)
+{
+  STDMETHOD(GetNumberOfMethods)(UInt32 *numMethods) PURE;
+  STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value) PURE;
+  STDMETHOD(CreateDecoder)(UInt32 index, const GUID *iid, void **coder) PURE;
+  STDMETHOD(CreateEncoder)(UInt32 index, const GUID *iid, void **coder) PURE;
+};
+CODER_INTERFACE(ISetCompressCodecsInfo, 0x61)
+{
+  STDMETHOD(SetCompressCodecsInfo)(ICompressCodecsInfo *compressCodecsInfo) PURE;
+};
+
+CODER_INTERFACE(ICryptoProperties, 0x80)
+{
+  STDMETHOD(SetKey)(const Byte *data, UInt32 size) PURE;
+  STDMETHOD(SetInitVector)(const Byte *data, UInt32 size) PURE;
+};
+
+/*
+CODER_INTERFACE(ICryptoResetSalt, 0x88)
+{
+  STDMETHOD(ResetSalt)() PURE;
+};
+*/
+
+CODER_INTERFACE(ICryptoResetInitVector, 0x8C)
+{
+  STDMETHOD(ResetInitVector)() PURE;
+};
+
+CODER_INTERFACE(ICryptoSetPassword, 0x90)
+{
+  STDMETHOD(CryptoSetPassword)(const Byte *data, UInt32 size) PURE;
+};
+
+CODER_INTERFACE(ICryptoSetCRC, 0xA0)
+{
+  STDMETHOD(CryptoSetCRC)(UInt32 crc) PURE;
+};
+
+//////////////////////
+// It's for DLL file
+namespace NMethodPropID
+{
+  enum EEnum
+  {
+    kID,
+    kName,
+    kDecoder,
+    kEncoder,
+    kInStreams,
+    kOutStreams,
+    kDescription,
+    kDecoderIsAssigned,
+    kEncoderIsAssigned
+  };
+}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/IDecl.h b/third_party/lzma/v4_65/files/CPP/7zip/IDecl.h
new file mode 100644
index 0000000..8316eb3
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/IDecl.h
@@ -0,0 +1,15 @@
+// IDecl.h
+
+#ifndef __IDECL_H
+#define __IDECL_H
+
+#include "../Common/MyUnknown.h"
+
+#define DECL_INTERFACE_SUB(i, base, groupId, subId) \
+DEFINE_GUID(IID_ ## i, \
+0x23170F69, 0x40C1, 0x278A, 0, 0, 0, (groupId), 0, (subId), 0, 0); \
+struct i: public base
+
+#define DECL_INTERFACE(i, groupId, subId) DECL_INTERFACE_SUB(i, IUnknown, groupId, subId)
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/IPassword.h b/third_party/lzma/v4_65/files/CPP/7zip/IPassword.h
new file mode 100644
index 0000000..3ca7b09
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/IPassword.h
@@ -0,0 +1,24 @@
+// IPassword.h
+
+#ifndef __IPASSWORD_H
+#define __IPASSWORD_H
+
+#include "../Common/MyUnknown.h"
+#include "../Common/Types.h"
+
+#include "IDecl.h"
+
+#define PASSWORD_INTERFACE(i, x) DECL_INTERFACE(i, 5, x)
+
+PASSWORD_INTERFACE(ICryptoGetTextPassword, 0x10)
+{
+  STDMETHOD(CryptoGetTextPassword)(BSTR *password) PURE;
+};
+
+PASSWORD_INTERFACE(ICryptoGetTextPassword2, 0x11)
+{
+  STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password) PURE;
+};
+
+#endif
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/IProgress.h b/third_party/lzma/v4_65/files/CPP/7zip/IProgress.h
new file mode 100644
index 0000000..d6093f1
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/IProgress.h
@@ -0,0 +1,33 @@
+// Interface/IProgress.h
+
+#ifndef __IPROGRESS_H
+#define __IPROGRESS_H
+
+#include "../Common/MyUnknown.h"
+#include "../Common/Types.h"
+
+#include "IDecl.h"
+
+#define INTERFACE_IProgress(x) \
+  STDMETHOD(SetTotal)(UInt64 total) x; \
+  STDMETHOD(SetCompleted)(const UInt64 *completeValue) x; \
+
+DECL_INTERFACE(IProgress, 0, 5)
+{
+  INTERFACE_IProgress(PURE)
+};
+
+/*
+// {23170F69-40C1-278A-0000-000000050002}
+DEFINE_GUID(IID_IProgress2,
+0x23170F69, 0x40C1, 0x278A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x02);
+MIDL_INTERFACE("23170F69-40C1-278A-0000-000000050002")
+IProgress2: public IUnknown
+{
+public:
+  STDMETHOD(SetTotal)(const UInt64 *total) PURE;
+  STDMETHOD(SetCompleted)(const UInt64 *completeValue) PURE;
+};
+*/
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/IStream.h b/third_party/lzma/v4_65/files/CPP/7zip/IStream.h
new file mode 100644
index 0000000..a177a9c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/IStream.h
@@ -0,0 +1,58 @@
+// IStream.h
+
+#ifndef __ISTREAM_H
+#define __ISTREAM_H
+
+#include "../Common/MyUnknown.h"
+#include "../Common/Types.h"
+
+#include "IDecl.h"
+
+#define STREAM_INTERFACE_SUB(i, base, x) DECL_INTERFACE_SUB(i, base, 3, x)
+#define STREAM_INTERFACE(i, x) STREAM_INTERFACE_SUB(i, IUnknown, x)
+
+STREAM_INTERFACE(ISequentialInStream, 0x01)
+{
+  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize) PURE;
+  /*
+  Out: if size != 0, return_value = S_OK and (*processedSize == 0),
+    then there are no more bytes in stream.
+  if (size > 0) && there are bytes in stream,
+  this function must read at least 1 byte.
+  This function is allowed to read less than number of remaining bytes in stream.
+  You must call Read function in loop, if you need exact amount of data
+  */
+};
+
+STREAM_INTERFACE(ISequentialOutStream, 0x02)
+{
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize) PURE;
+  /*
+  if (size > 0) this function must write at least 1 byte.
+  This function is allowed to write less than "size".
+  You must call Write function in loop, if you need to write exact amount of data
+  */
+};
+
+STREAM_INTERFACE_SUB(IInStream, ISequentialInStream, 0x03)
+{
+  STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) PURE;
+};
+
+STREAM_INTERFACE_SUB(IOutStream, ISequentialOutStream, 0x04)
+{
+  STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) PURE;
+  STDMETHOD(SetSize)(Int64 newSize) PURE;
+};
+
+STREAM_INTERFACE(IStreamGetSize, 0x06)
+{
+  STDMETHOD(GetSize)(UInt64 *size) PURE;
+};
+
+STREAM_INTERFACE(IOutStreamFlush, 0x07)
+{
+  STDMETHOD(Flush)() PURE;
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/MyVersion.h b/third_party/lzma/v4_65/files/CPP/7zip/MyVersion.h
new file mode 100644
index 0000000..68058ec
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/MyVersion.h
@@ -0,0 +1,8 @@
+#define MY_VER_MAJOR 4
+#define MY_VER_MINOR 65
+#define MY_VER_BUILD 0
+#define MY_VERSION "4.65"
+#define MY_7ZIP_VERSION "7-Zip 4.65"
+#define MY_DATE "2009-02-03"
+#define MY_COPYRIGHT "Copyright (c) 1999-2009 Igor Pavlov"
+#define MY_VERSION_COPYRIGHT_DATE MY_VERSION "  " MY_COPYRIGHT "  " MY_DATE
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/MyVersionInfo.rc b/third_party/lzma/v4_65/files/CPP/7zip/MyVersionInfo.rc
new file mode 100644
index 0000000..c3712b1
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/MyVersionInfo.rc
@@ -0,0 +1,45 @@
+#include <WinVer.h>
+#include "MyVersion.h"
+
+#define MY_VER MY_VER_MAJOR,MY_VER_MINOR,MY_VER_BUILD,0
+
+#ifdef DEBUG
+#define DBG_FL VS_FF_DEBUG
+#else
+#define DBG_FL 0
+#endif
+
+#define MY_VERSION_INFO(fileType, descr, intName, origName)  \
+LANGUAGE 9, 1 \
+1 VERSIONINFO \
+  FILEVERSION MY_VER \
+  PRODUCTVERSION MY_VER \
+  FILEFLAGSMASK VS_FFI_FILEFLAGSMASK \
+  FILEFLAGS DBG_FL \
+  FILEOS VOS_NT_WINDOWS32 \
+  FILETYPE fileType \
+  FILESUBTYPE 0x0L \
+BEGIN \
+    BLOCK "StringFileInfo" \
+    BEGIN  \
+        BLOCK "040904b0" \
+        BEGIN \
+            VALUE "CompanyName", "Igor Pavlov" \
+            VALUE "FileDescription", descr \
+            VALUE "FileVersion", MY_VERSION  \
+            VALUE "InternalName", intName \
+            VALUE "LegalCopyright", MY_COPYRIGHT \
+            VALUE "OriginalFilename", origName \
+            VALUE "ProductName", "7-Zip" \
+            VALUE "ProductVersion", MY_VERSION \
+        END \
+    END \
+    BLOCK "VarFileInfo" \
+    BEGIN \
+        VALUE "Translation", 0x409, 1200 \
+    END \
+END
+
+#define MY_VERSION_INFO_APP(descr, intName) MY_VERSION_INFO(VFT_APP, descr, intName, intName ".exe")
+
+#define MY_VERSION_INFO_DLL(descr, intName) MY_VERSION_INFO(VFT_DLL, descr, intName, intName ".dll")
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/PropID.h b/third_party/lzma/v4_65/files/CPP/7zip/PropID.h
new file mode 100644
index 0000000..9de487e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/PropID.h
@@ -0,0 +1,69 @@
+// PropID.h
+
+#ifndef __7ZIP_PROPID_H
+#define __7ZIP_PROPID_H
+
+enum
+{
+  kpidNoProperty = 0,
+  
+  kpidHandlerItemIndex = 2,
+  kpidPath,
+  kpidName,
+  kpidExtension,
+  kpidIsDir,
+  kpidSize,
+  kpidPackSize,
+  kpidAttrib,
+  kpidCTime,
+  kpidATime,
+  kpidMTime,
+  kpidSolid,
+  kpidCommented,
+  kpidEncrypted,
+  kpidSplitBefore,
+  kpidSplitAfter,
+  kpidDictionarySize,
+  kpidCRC,
+  kpidType,
+  kpidIsAnti,
+  kpidMethod,
+  kpidHostOS,
+  kpidFileSystem,
+  kpidUser,
+  kpidGroup,
+  kpidBlock,
+  kpidComment,
+  kpidPosition,
+  kpidPrefix,
+  kpidNumSubDirs,
+  kpidNumSubFiles,
+  kpidUnpackVer,
+  kpidVolume,
+  kpidIsVolume,
+  kpidOffset,
+  kpidLinks,
+  kpidNumBlocks,
+  kpidNumVolumes,
+  kpidTimeType,
+  kpidBit64,
+  kpidBigEndian,
+  kpidCpu,
+  kpidPhySize,
+  kpidHeadersSize,
+  kpidChecksum,
+  kpidCharacts,
+  kpidVa,
+
+  kpidTotalSize = 0x1100,
+  kpidFreeSpace,
+  kpidClusterSize,
+  kpidVolumeName,
+
+  kpidLocalName = 0x1200,
+  kpidProvider,
+
+  kpidUserDefined = 0x10000
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/Client7z.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/Client7z.cpp
new file mode 100644
index 0000000..a63144a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/Client7z.cpp
@@ -0,0 +1,853 @@
+// Client7z.cpp
+
+#include "StdAfx.h"
+
+#include "Common/IntToString.h"
+#include "Common/MyInitGuid.h"
+#include "Common/StringConvert.h"
+
+#include "Windows/DLL.h"
+#include "Windows/FileDir.h"
+#include "Windows/FileFind.h"
+#include "Windows/FileName.h"
+#include "Windows/PropVariant.h"
+#include "Windows/PropVariantConversions.h"
+
+#include "../../Common/FileStreams.h"
+
+#include "../../Archive/IArchive.h"
+
+#include "../../IPassword.h"
+#include "../../MyVersion.h"
+
+// use another CLSIDs, if you want to support other formats (zip, rar, ...).
+// {23170F69-40C1-278A-1000-000110070000}
+DEFINE_GUID(CLSID_CFormat7z,
+  0x23170F69, 0x40C1, 0x278A, 0x10, 0x00, 0x00, 0x01, 0x10, 0x07, 0x00, 0x00);
+
+using namespace NWindows;
+
+#define kDllName "7z.dll"
+
+static const char *kCopyrightString = MY_7ZIP_VERSION
+" ("  kDllName " client) "
+MY_COPYRIGHT " " MY_DATE;
+
+static const char *kHelpString =
+"Usage: Client7z.exe [a | l | x ] archive.7z [fileName ...]\n"
+"Examples:\n"
+"  Client7z.exe a archive.7z f1.txt f2.txt  : compress two files to archive.7z\n"
+"  Client7z.exe l archive.7z   : List contents of archive.7z\n"
+"  Client7z.exe x archive.7z   : eXtract files from archive.7z\n";
+
+
+typedef UINT32 (WINAPI * CreateObjectFunc)(
+    const GUID *clsID,
+    const GUID *interfaceID,
+    void **outObject);
+
+#ifdef _WIN32
+#ifndef _UNICODE
+bool g_IsNT = false;
+static inline bool IsItWindowsNT()
+{
+  OSVERSIONINFO versionInfo;
+  versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
+  if (!::GetVersionEx(&versionInfo))
+    return false;
+  return (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT);
+}
+#endif
+#endif
+
+void PrintString(const UString &s)
+{
+  printf("%s", (LPCSTR)GetOemString(s));
+}
+
+void PrintString(const AString &s)
+{
+  printf("%s", (LPCSTR)s);
+}
+
+void PrintNewLine()
+{
+  PrintString("\n");
+}
+
+void PrintStringLn(const AString &s)
+{
+  PrintString(s);
+  PrintNewLine();
+}
+
+void PrintError(const AString &s)
+{
+  PrintNewLine();
+  PrintString(s);
+  PrintNewLine();
+}
+
+static HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result)
+{
+  NCOM::CPropVariant prop;
+  RINOK(archive->GetProperty(index, propID, &prop));
+  if (prop.vt == VT_BOOL)
+    result = VARIANT_BOOLToBool(prop.boolVal);
+  else if (prop.vt == VT_EMPTY)
+    result = false;
+  else
+    return E_FAIL;
+  return S_OK;
+}
+
+static HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result)
+{
+  return IsArchiveItemProp(archive, index, kpidIsDir, result);
+}
+
+
+static const wchar_t *kEmptyFileAlias = L"[Content]";
+
+
+//////////////////////////////////////////////////////////////
+// Archive Open callback class
+
+
+class CArchiveOpenCallback:
+  public IArchiveOpenCallback,
+  public ICryptoGetTextPassword,
+  public CMyUnknownImp
+{
+public:
+  MY_UNKNOWN_IMP1(ICryptoGetTextPassword)
+
+  STDMETHOD(SetTotal)(const UInt64 *files, const UInt64 *bytes);
+  STDMETHOD(SetCompleted)(const UInt64 *files, const UInt64 *bytes);
+
+  STDMETHOD(CryptoGetTextPassword)(BSTR *password);
+
+  bool PasswordIsDefined;
+  UString Password;
+
+  CArchiveOpenCallback() : PasswordIsDefined(false) {}
+};
+
+STDMETHODIMP CArchiveOpenCallback::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */)
+{
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveOpenCallback::SetCompleted(const UInt64 * /* files */, const UInt64 * /* bytes */)
+{
+  return S_OK;
+}
+  
+STDMETHODIMP CArchiveOpenCallback::CryptoGetTextPassword(BSTR *password)
+{
+  if (!PasswordIsDefined)
+  {
+    // You can ask real password here from user
+    // Password = GetPassword(OutStream);
+    // PasswordIsDefined = true;
+    PrintError("Password is not defined");
+    return E_ABORT;
+  }
+  return StringToBstr(Password, password);
+}
+
+
+//////////////////////////////////////////////////////////////
+// Archive Extracting callback class
+
+static const wchar_t *kCantDeleteOutputFile = L"ERROR: Can not delete output file ";
+
+static const char *kTestingString    =  "Testing     ";
+static const char *kExtractingString =  "Extracting  ";
+static const char *kSkippingString   =  "Skipping    ";
+
+static const char *kUnsupportedMethod = "Unsupported Method";
+static const char *kCRCFailed = "CRC Failed";
+static const char *kDataError = "Data Error";
+static const char *kUnknownError = "Unknown Error";
+
+class CArchiveExtractCallback:
+  public IArchiveExtractCallback,
+  public ICryptoGetTextPassword,
+  public CMyUnknownImp
+{
+public:
+  MY_UNKNOWN_IMP1(ICryptoGetTextPassword)
+
+  // IProgress
+  STDMETHOD(SetTotal)(UInt64 size);
+  STDMETHOD(SetCompleted)(const UInt64 *completeValue);
+
+  // IArchiveExtractCallback
+  STDMETHOD(GetStream)(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode);
+  STDMETHOD(PrepareOperation)(Int32 askExtractMode);
+  STDMETHOD(SetOperationResult)(Int32 resultEOperationResult);
+
+  // ICryptoGetTextPassword
+  STDMETHOD(CryptoGetTextPassword)(BSTR *aPassword);
+
+private:
+  CMyComPtr<IInArchive> _archiveHandler;
+  UString _directoryPath;  // Output directory
+  UString _filePath;       // name inside arcvhive
+  UString _diskFilePath;   // full path to file on disk
+  bool _extractMode;
+  struct CProcessedFileInfo
+  {
+    FILETIME MTime;
+    UInt32 Attrib;
+    bool isDir;
+    bool AttribDefined;
+    bool MTimeDefined;
+  } _processedFileInfo;
+
+  COutFileStream *_outFileStreamSpec;
+  CMyComPtr<ISequentialOutStream> _outFileStream;
+
+public:
+  void Init(IInArchive *archiveHandler, const UString &directoryPath);
+
+  UInt64 NumErrors;
+  bool PasswordIsDefined;
+  UString Password;
+
+  CArchiveExtractCallback() : PasswordIsDefined(false) {}
+};
+
+void CArchiveExtractCallback::Init(IInArchive *archiveHandler, const UString &directoryPath)
+{
+  NumErrors = 0;
+  _archiveHandler = archiveHandler;
+  _directoryPath = directoryPath;
+  NFile::NName::NormalizeDirPathPrefix(_directoryPath);
+}
+
+STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 /* size */)
+{
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 * /* completeValue */)
+{
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index,
+    ISequentialOutStream **outStream, Int32 askExtractMode)
+{
+  *outStream = 0;
+  _outFileStream.Release();
+
+  {
+    // Get Name
+    NCOM::CPropVariant prop;
+    RINOK(_archiveHandler->GetProperty(index, kpidPath, &prop));
+    
+    UString fullPath;
+    if (prop.vt == VT_EMPTY)
+      fullPath = kEmptyFileAlias;
+    else
+    {
+      if (prop.vt != VT_BSTR)
+        return E_FAIL;
+      fullPath = prop.bstrVal;
+    }
+    _filePath = fullPath;
+  }
+
+  if (askExtractMode != NArchive::NExtract::NAskMode::kExtract)
+    return S_OK;
+
+  {
+    // Get Attrib
+    NCOM::CPropVariant prop;
+    RINOK(_archiveHandler->GetProperty(index, kpidAttrib, &prop));
+    if (prop.vt == VT_EMPTY)
+    {
+      _processedFileInfo.Attrib = 0;
+      _processedFileInfo.AttribDefined = false;
+    }
+    else
+    {
+      if (prop.vt != VT_UI4)
+        return E_FAIL;
+      _processedFileInfo.Attrib = prop.ulVal;
+      _processedFileInfo.AttribDefined = true;
+    }
+  }
+
+  RINOK(IsArchiveItemFolder(_archiveHandler, index, _processedFileInfo.isDir));
+
+  {
+    // Get Modified Time
+    NCOM::CPropVariant prop;
+    RINOK(_archiveHandler->GetProperty(index, kpidMTime, &prop));
+    _processedFileInfo.MTimeDefined = false;
+    switch(prop.vt)
+    {
+      case VT_EMPTY:
+        // _processedFileInfo.MTime = _utcMTimeDefault;
+        break;
+      case VT_FILETIME:
+        _processedFileInfo.MTime = prop.filetime;
+        _processedFileInfo.MTimeDefined = true;
+        break;
+      default:
+        return E_FAIL;
+    }
+
+  }
+  {
+    // Get Size
+    NCOM::CPropVariant prop;
+    RINOK(_archiveHandler->GetProperty(index, kpidSize, &prop));
+    bool newFileSizeDefined = (prop.vt != VT_EMPTY);
+    UInt64 newFileSize;
+    if (newFileSizeDefined)
+      newFileSize = ConvertPropVariantToUInt64(prop);
+  }
+
+  
+  {
+    // Create folders for file
+    int slashPos = _filePath.ReverseFind(WCHAR_PATH_SEPARATOR);
+    if (slashPos >= 0)
+      NFile::NDirectory::CreateComplexDirectory(_directoryPath + _filePath.Left(slashPos));
+  }
+
+  UString fullProcessedPath = _directoryPath + _filePath;
+  _diskFilePath = fullProcessedPath;
+
+  if (_processedFileInfo.isDir)
+  {
+    NFile::NDirectory::CreateComplexDirectory(fullProcessedPath);
+  }
+  else
+  {
+    NFile::NFind::CFileInfoW fi;
+    if (NFile::NFind::FindFile(fullProcessedPath, fi))
+    {
+      if (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath))
+      {
+        PrintString(UString(kCantDeleteOutputFile) + fullProcessedPath);
+        return E_ABORT;
+      }
+    }
+    
+    _outFileStreamSpec = new COutFileStream;
+    CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec);
+    if (!_outFileStreamSpec->Open(fullProcessedPath, CREATE_ALWAYS))
+    {
+      PrintString((UString)L"can not open output file " + fullProcessedPath);
+      return E_ABORT;
+    }
+    _outFileStream = outStreamLoc;
+    *outStream = outStreamLoc.Detach();
+  }
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
+{
+  _extractMode = false;
+  switch (askExtractMode)
+  {
+    case NArchive::NExtract::NAskMode::kExtract:  _extractMode = true; break;
+  };
+  switch (askExtractMode)
+  {
+    case NArchive::NExtract::NAskMode::kExtract:  PrintString(kExtractingString); break;
+    case NArchive::NExtract::NAskMode::kTest:  PrintString(kTestingString); break;
+    case NArchive::NExtract::NAskMode::kSkip:  PrintString(kSkippingString); break;
+  };
+  PrintString(_filePath);
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult)
+{
+  switch(operationResult)
+  {
+    case NArchive::NExtract::NOperationResult::kOK:
+      break;
+    default:
+    {
+      NumErrors++;
+      PrintString("     ");
+      switch(operationResult)
+      {
+        case NArchive::NExtract::NOperationResult::kUnSupportedMethod:
+          PrintString(kUnsupportedMethod);
+          break;
+        case NArchive::NExtract::NOperationResult::kCRCError:
+          PrintString(kCRCFailed);
+          break;
+        case NArchive::NExtract::NOperationResult::kDataError:
+          PrintString(kDataError);
+          break;
+        default:
+          PrintString(kUnknownError);
+      }
+    }
+  }
+
+  if (_outFileStream != NULL)
+  {
+    if (_processedFileInfo.MTimeDefined)
+      _outFileStreamSpec->SetMTime(&_processedFileInfo.MTime);
+    RINOK(_outFileStreamSpec->Close());
+  }
+  _outFileStream.Release();
+  if (_extractMode && _processedFileInfo.AttribDefined)
+    NFile::NDirectory::MySetFileAttributes(_diskFilePath, _processedFileInfo.Attrib);
+  PrintNewLine();
+  return S_OK;
+}
+
+
+STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
+{
+  if (!PasswordIsDefined)
+  {
+    // You can ask real password here from user
+    // Password = GetPassword(OutStream);
+    // PasswordIsDefined = true;
+    PrintError("Password is not defined");
+    return E_ABORT;
+  }
+  return StringToBstr(Password, password);
+}
+
+
+
+//////////////////////////////////////////////////////////////
+// Archive Creating callback class
+
+struct CDirItem
+{
+  UInt64 Size;
+  FILETIME CTime;
+  FILETIME ATime;
+  FILETIME MTime;
+  UString Name;
+  UString FullPath;
+  UInt32 Attrib;
+
+  bool isDir() const { return (Attrib & FILE_ATTRIBUTE_DIRECTORY) != 0 ; }
+};
+
+class CArchiveUpdateCallback:
+  public IArchiveUpdateCallback2,
+  public ICryptoGetTextPassword2,
+  public CMyUnknownImp
+{
+public:
+  MY_UNKNOWN_IMP2(IArchiveUpdateCallback2, ICryptoGetTextPassword2)
+
+  // IProgress
+  STDMETHOD(SetTotal)(UInt64 size);
+  STDMETHOD(SetCompleted)(const UInt64 *completeValue);
+
+  // IUpdateCallback2
+  STDMETHOD(EnumProperties)(IEnumSTATPROPSTG **enumerator);
+  STDMETHOD(GetUpdateItemInfo)(UInt32 index,
+      Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive);
+  STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value);
+  STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **inStream);
+  STDMETHOD(SetOperationResult)(Int32 operationResult);
+  STDMETHOD(GetVolumeSize)(UInt32 index, UInt64 *size);
+  STDMETHOD(GetVolumeStream)(UInt32 index, ISequentialOutStream **volumeStream);
+
+  STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password);
+
+public:
+  CRecordVector<UInt64> VolumesSizes;
+  UString VolName;
+  UString VolExt;
+
+  UString DirPrefix;
+  const CObjectVector<CDirItem> *DirItems;
+
+  bool PasswordIsDefined;
+  UString Password;
+  bool AskPassword;
+
+  bool m_NeedBeClosed;
+
+  UStringVector FailedFiles;
+  CRecordVector<HRESULT> FailedCodes;
+
+  CArchiveUpdateCallback(): PasswordIsDefined(false), AskPassword(false), DirItems(0) {};
+
+  ~CArchiveUpdateCallback() { Finilize(); }
+  HRESULT Finilize();
+
+  void Init(const CObjectVector<CDirItem> *dirItems)
+  {
+    DirItems = dirItems;
+    m_NeedBeClosed = false;
+    FailedFiles.Clear();
+    FailedCodes.Clear();
+  }
+};
+
+STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 /* size */)
+{
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 * /* completeValue */)
+{
+  return S_OK;
+}
+
+
+STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG ** /* enumerator */)
+{
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 /* index */,
+      Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive)
+{
+  if (newData != NULL)
+    *newData = BoolToInt(true);
+  if (newProperties != NULL)
+    *newProperties = BoolToInt(true);
+  if (indexInArchive != NULL)
+    *indexInArchive = (UInt32)-1;
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
+{
+  NWindows::NCOM::CPropVariant prop;
+  
+  if (propID == kpidIsAnti)
+  {
+    prop = false;
+    prop.Detach(value);
+    return S_OK;
+  }
+
+  {
+    const CDirItem &dirItem = (*DirItems)[index];
+    switch(propID)
+    {
+      case kpidPath:  prop = dirItem.Name; break;
+      case kpidIsDir:  prop = dirItem.isDir(); break;
+      case kpidSize:  prop = dirItem.Size; break;
+      case kpidAttrib:  prop = dirItem.Attrib; break;
+      case kpidCTime:  prop = dirItem.CTime; break;
+      case kpidATime:  prop = dirItem.ATime; break;
+      case kpidMTime:  prop = dirItem.MTime; break;
+    }
+  }
+  prop.Detach(value);
+  return S_OK;
+}
+
+HRESULT CArchiveUpdateCallback::Finilize()
+{
+  if (m_NeedBeClosed)
+  {
+    PrintNewLine();
+    m_NeedBeClosed = false;
+  }
+  return S_OK;
+}
+
+static void GetStream2(const wchar_t *name)
+{
+  PrintString("Compressing  ");
+  if (name[0] == 0)
+    name = kEmptyFileAlias;
+  PrintString(name);
+}
+
+STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream)
+{
+  RINOK(Finilize());
+
+  const CDirItem &dirItem = (*DirItems)[index];
+  GetStream2(dirItem.Name);
+ 
+  if (dirItem.isDir())
+    return S_OK;
+
+  {
+    CInFileStream *inStreamSpec = new CInFileStream;
+    CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
+    UString path = DirPrefix + dirItem.FullPath;
+    if (!inStreamSpec->Open(path))
+    {
+      DWORD sysError = ::GetLastError();
+      FailedCodes.Add(sysError);
+      FailedFiles.Add(path);
+      // if (systemError == ERROR_SHARING_VIOLATION)
+      {
+        PrintNewLine();
+        PrintError("WARNING: can't open file");
+        // PrintString(NError::MyFormatMessageW(systemError));
+        return S_FALSE;
+      }
+      // return sysError;
+    }
+    *inStream = inStreamLoc.Detach();
+  }
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 /* operationResult */)
+{
+  m_NeedBeClosed = true;
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size)
+{
+  if (VolumesSizes.Size() == 0)
+    return S_FALSE;
+  if (index >= (UInt32)VolumesSizes.Size())
+    index = VolumesSizes.Size() - 1;
+  *size = VolumesSizes[index];
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream)
+{
+  wchar_t temp[32];
+  ConvertUInt64ToString(index + 1, temp);
+  UString res = temp;
+  while (res.Length() < 2)
+    res = UString(L'0') + res;
+  UString fileName = VolName;
+  fileName += L'.';
+  fileName += res;
+  fileName += VolExt;
+  COutFileStream *streamSpec = new COutFileStream;
+  CMyComPtr<ISequentialOutStream> streamLoc(streamSpec);
+  if (!streamSpec->Create(fileName, false))
+    return ::GetLastError();
+  *volumeStream = streamLoc.Detach();
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)
+{
+  if (!PasswordIsDefined)
+  {
+    if (AskPassword)
+    {
+      // You can ask real password here from user
+      // Password = GetPassword(OutStream);
+      // PasswordIsDefined = true;
+      PrintError("Password is not defined");
+      return E_ABORT;
+    }
+  }
+  *passwordIsDefined = BoolToInt(PasswordIsDefined);
+  return StringToBstr(Password, password);
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+// Main function
+
+int MY_CDECL main(int argc, char* argv[])
+{
+  #ifdef _WIN32
+  #ifndef _UNICODE
+  g_IsNT = IsItWindowsNT();
+  #endif
+  #endif
+
+  PrintStringLn(kCopyrightString);
+
+  if (argc < 3)
+  {
+    PrintStringLn(kHelpString);
+    return 1;
+  }
+  NWindows::NDLL::CLibrary library;
+  if (!library.Load(TEXT(kDllName)))
+  {
+    PrintError("Can not load library");
+    return 1;
+  }
+  CreateObjectFunc createObjectFunc = (CreateObjectFunc)library.GetProcAddress("CreateObject");
+  if (createObjectFunc == 0)
+  {
+    PrintError("Can not get CreateObject");
+    return 1;
+  }
+
+  AString command = argv[1];
+  command.MakeLower();
+  UString archiveName = GetUnicodeString(argv[2], CP_OEMCP);
+  if (command.Compare("a") == 0)
+  {
+    // create archive command
+    if (argc < 4)
+    {
+      PrintStringLn(kHelpString);
+      return 1;
+    }
+    CObjectVector<CDirItem> dirItems;
+    int i;
+    for (i = 3; i < argc; i++)
+    {
+      CDirItem di;
+      UString name = GetUnicodeString(argv[i], CP_OEMCP);
+      
+      NFile::NFind::CFileInfoW fi;
+      if (!NFile::NFind::FindFile(name, fi))
+      {
+        PrintString(UString(L"Can't find file") + name);
+        return 1;
+      }
+
+      di.Attrib = fi.Attrib;
+      di.Size = fi.Size;
+      di.CTime = fi.CTime;
+      di.ATime = fi.ATime;
+      di.MTime = fi.MTime;
+      di.Name = name;
+      di.FullPath = name;
+      dirItems.Add(di);
+    }
+    COutFileStream *outFileStreamSpec = new COutFileStream;
+    CMyComPtr<IOutStream> outFileStream = outFileStreamSpec;
+    if (!outFileStreamSpec->Create(archiveName, false))
+    {
+      PrintError("can't create archive file");
+      return 1;
+    }
+
+    CMyComPtr<IOutArchive> outArchive;
+    if (createObjectFunc(&CLSID_CFormat7z, &IID_IOutArchive, (void **)&outArchive) != S_OK)
+    {
+      PrintError("Can not get class object");
+      return 1;
+    }
+
+    CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
+    CMyComPtr<IArchiveUpdateCallback2> updateCallback(updateCallbackSpec);
+    updateCallbackSpec->Init(&dirItems);
+    // updateCallbackSpec->PasswordIsDefined = true;
+    // updateCallbackSpec->Password = L"1";
+
+    HRESULT result = outArchive->UpdateItems(outFileStream, dirItems.Size(), updateCallback);
+    updateCallbackSpec->Finilize();
+    if (result != S_OK)
+    {
+      PrintError("Update Error");
+      return 1;
+    }
+    for (i = 0; i < updateCallbackSpec->FailedFiles.Size(); i++)
+    {
+      PrintNewLine();
+      PrintString((UString)L"Error for file: " + updateCallbackSpec->FailedFiles[i]);
+    }
+    if (updateCallbackSpec->FailedFiles.Size() != 0)
+      return 1;
+  }
+  else
+  {
+    if (argc != 3)
+    {
+      PrintStringLn(kHelpString);
+      return 1;
+    }
+
+    bool listCommand;
+    if (command.Compare("l") == 0)
+      listCommand = true;
+    else if (command.Compare("x") == 0)
+      listCommand = false;
+    else
+    {
+      PrintError("incorrect command");
+      return 1;
+    }
+  
+    CMyComPtr<IInArchive> archive;
+    if (createObjectFunc(&CLSID_CFormat7z, &IID_IInArchive, (void **)&archive) != S_OK)
+    {
+      PrintError("Can not get class object");
+      return 1;
+    }
+    
+    CInFileStream *fileSpec = new CInFileStream;
+    CMyComPtr<IInStream> file = fileSpec;
+    
+    if (!fileSpec->Open(archiveName))
+    {
+      PrintError("Can not open archive file");
+      return 1;
+    }
+
+    {
+      CArchiveOpenCallback *openCallbackSpec = new CArchiveOpenCallback;
+      CMyComPtr<IArchiveOpenCallback> openCallback(openCallbackSpec);
+      openCallbackSpec->PasswordIsDefined = false;
+      // openCallbackSpec->PasswordIsDefined = true;
+      // openCallbackSpec->Password = L"1";
+      
+      if (archive->Open(file, 0, openCallback) != S_OK)
+      {
+        PrintError("Can not open archive");
+        return 1;
+      }
+    }
+    
+    if (listCommand)
+    {
+      // List command
+      UInt32 numItems = 0;
+      archive->GetNumberOfItems(&numItems);
+      for (UInt32 i = 0; i < numItems; i++)
+      {
+        {
+          // Get uncompressed size of file
+          NWindows::NCOM::CPropVariant prop;
+          archive->GetProperty(i, kpidSize, &prop);
+          UString s = ConvertPropVariantToString(prop);
+          PrintString(s);
+          PrintString("  ");
+        }
+        {
+          // Get name of file
+          NWindows::NCOM::CPropVariant prop;
+          archive->GetProperty(i, kpidPath, &prop);
+          UString s = ConvertPropVariantToString(prop);
+          PrintString(s);
+        }
+        PrintString("\n");
+      }
+    }
+    else
+    {
+      // Extract command
+      CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback;
+      CMyComPtr<IArchiveExtractCallback> extractCallback(extractCallbackSpec);
+      extractCallbackSpec->Init(archive, L""); // second parameter is output folder path
+      extractCallbackSpec->PasswordIsDefined = false;
+      // extractCallbackSpec->PasswordIsDefined = true;
+      // extractCallbackSpec->Password = L"1";
+      HRESULT result = archive->Extract(NULL, (UInt32)(Int32)(-1), false, extractCallback);
+      if (result != S_OK)
+      {
+        PrintError("Extract Error");
+        return 1;
+      }
+    }
+  }
+  return 0;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/Client7z.dsp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/Client7z.dsp
new file mode 100644
index 0000000..542e285
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/Client7z.dsp
@@ -0,0 +1,226 @@
+# Microsoft Developer Studio Project File - Name="Client7z" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=Client7z - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "Client7z.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "Client7z.mak" CFG="Client7z - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "Client7z - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "Client7z - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "Client7z - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\..\\" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c
+# ADD BASE RSC /l 0x419 /d "NDEBUG"
+# ADD RSC /l 0x419 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /machine:I386
+
+!ELSEIF  "$(CFG)" == "Client7z - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\\" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c
+# ADD BASE RSC /l 0x419 /d "_DEBUG"
+# ADD RSC /l 0x419 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.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 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "Client7z - Win32 Release"
+# Name "Client7z - Win32 Debug"
+# Begin Group "Spec"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\StdAfx.cpp
+# ADD CPP /Yc"stdafx.h"
+# End Source File
+# Begin Source File
+
+SOURCE=.\StdAfx.h
+# End Source File
+# End Group
+# Begin Group "Windows"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\..\Windows\DLL.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\DLL.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileDir.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileDir.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileFind.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileFind.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileIO.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileIO.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileName.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\FileName.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\PropVariant.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\PropVariant.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\PropVariantConversions.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Windows\PropVariantConversions.h
+# End Source File
+# End Group
+# Begin Group "Common"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\..\Common\IntToString.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\IntToString.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyString.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyString.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyVector.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\MyVector.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\NewHandler.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\NewHandler.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\StringConvert.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\StringConvert.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\Wildcard.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\Common\Wildcard.h
+# End Source File
+# End Group
+# Begin Group "7zip Common"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\Common\FileStreams.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\Common\FileStreams.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\Client7z.cpp
+# End Source File
+# End Target
+# End Project
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/Client7z.dsw b/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/Client7z.dsw
new file mode 100644
index 0000000..598a6d3
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/Client7z.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Client7z"=.\Client7z.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/StdAfx.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/StdAfx.cpp
new file mode 100644
index 0000000..d0feea8
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/StdAfx.cpp
@@ -0,0 +1,3 @@
+// StdAfx.cpp
+
+#include "StdAfx.h"
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/StdAfx.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/StdAfx.h
new file mode 100644
index 0000000..b23436e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/StdAfx.h
@@ -0,0 +1,9 @@
+// StdAfx.h
+
+#ifndef __STDAFX_H
+#define __STDAFX_H
+
+#include <windows.h>
+#include <stdio.h>
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/makefile b/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/makefile
new file mode 100644
index 0000000..226c36a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Client7z/makefile
@@ -0,0 +1,45 @@
+PROG = 7z.exe
+LIBS = $(LIBS) user32.lib oleaut32.lib advapi32.lib
+CFLAGS = $(CFLAGS) -I ../../../
+
+CONSOLE_OBJS = \
+  $O\Client7z.obj \
+
+COMMON_OBJS = \
+  $O\IntToString.obj \
+  $O\NewHandler.obj \
+  $O\MyString.obj \
+  $O\StringConvert.obj \
+  $O\StringToInt.obj \
+  $O\MyVector.obj \
+  $O\Wildcard.obj \
+
+WIN_OBJS = \
+  $O\DLL.obj \
+  $O\FileDir.obj \
+  $O\FileFind.obj \
+  $O\FileIO.obj \
+  $O\FileName.obj \
+  $O\PropVariant.obj \
+  $O\PropVariantConversions.obj \
+
+7ZIP_COMMON_OBJS = \
+  $O\FileStreams.obj \
+
+OBJS = \
+  $O\StdAfx.obj \
+  $(CONSOLE_OBJS) \
+  $(COMMON_OBJS) \
+  $(WIN_OBJS) \
+  $(7ZIP_COMMON_OBJS) \
+
+!include "../../../Build.mak"
+
+$(CONSOLE_OBJS): $(*B).cpp
+	$(COMPL)
+$(COMMON_OBJS): ../../../Common/$(*B).cpp
+	$(COMPL)
+$(WIN_OBJS): ../../../Windows/$(*B).cpp
+	$(COMPL)
+$(7ZIP_COMMON_OBJS): ../../Common/$(*B).cpp
+	$(COMPL)
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveCommandLine.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveCommandLine.cpp
new file mode 100644
index 0000000..46e8a2a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveCommandLine.cpp
@@ -0,0 +1,1011 @@
+// ArchiveCommandLine.cpp
+
+#include "StdAfx.h"
+
+#ifdef _WIN32
+#include <io.h>
+#endif
+#include <stdio.h>
+
+#include "Common/ListFileUtils.h"
+#include "Common/StringConvert.h"
+#include "Common/StringToInt.h"
+
+#include "Windows/FileName.h"
+#include "Windows/FileDir.h"
+#ifdef _WIN32
+#include "Windows/FileMapping.h"
+#include "Windows/Synchronization.h"
+#endif
+
+#include "ArchiveCommandLine.h"
+#include "UpdateAction.h"
+#include "Update.h"
+#include "SortUtils.h"
+#include "EnumDirItems.h"
+
+extern bool g_CaseSensitive;
+
+#if _MSC_VER >= 1400
+#define MY_isatty_fileno(x) _isatty(_fileno(x))
+#else
+#define MY_isatty_fileno(x) isatty(fileno(x))
+#endif
+
+#define MY_IS_TERMINAL(x) (MY_isatty_fileno(x) != 0);
+
+using namespace NCommandLineParser;
+using namespace NWindows;
+using namespace NFile;
+
+namespace NKey {
+enum Enum
+{
+  kHelp1 = 0,
+  kHelp2,
+  kHelp3,
+  kDisableHeaders,
+  kDisablePercents,
+  kArchiveType,
+  kYes,
+  #ifndef _NO_CRYPTO
+  kPassword,
+  #endif
+  kProperty,
+  kOutputDir,
+  kWorkingDir,
+  kInclude,
+  kExclude,
+  kArInclude,
+  kArExclude,
+  kNoArName,
+  kUpdate,
+  kVolume,
+  kRecursed,
+  kSfx,
+  kStdIn,
+  kStdOut,
+  kOverwrite,
+  kEmail,
+  kShowDialog,
+  kLargePages,
+  kCharSet,
+  kTechMode,
+  kShareForWrite,
+  kCaseSensitive
+};
+
+}
+
+
+static const wchar_t kRecursedIDChar = 'R';
+static const wchar_t *kRecursedPostCharSet = L"0-";
+
+namespace NRecursedPostCharIndex {
+  enum EEnum
+  {
+    kWildCardRecursionOnly = 0,
+    kNoRecursion = 1
+  };
+}
+
+static const char kImmediateNameID = '!';
+static const char kMapNameID = '#';
+static const char kFileListID = '@';
+
+static const char kSomeCludePostStringMinSize = 2; // at least <@|!><N>ame must be
+static const char kSomeCludeAfterRecursedPostStringMinSize = 2; // at least <@|!><N>ame must be
+
+static const wchar_t *kOverwritePostCharSet = L"asut";
+
+NExtract::NOverwriteMode::EEnum k_OverwriteModes[] =
+{
+  NExtract::NOverwriteMode::kWithoutPrompt,
+  NExtract::NOverwriteMode::kSkipExisting,
+  NExtract::NOverwriteMode::kAutoRename,
+  NExtract::NOverwriteMode::kAutoRenameExisting
+};
+
+static const CSwitchForm kSwitchForms[] =
+  {
+    { L"?",  NSwitchType::kSimple, false },
+    { L"H",  NSwitchType::kSimple, false },
+    { L"-HELP",  NSwitchType::kSimple, false },
+    { L"BA", NSwitchType::kSimple, false },
+    { L"BD", NSwitchType::kSimple, false },
+    { L"T",  NSwitchType::kUnLimitedPostString, false, 1 },
+    { L"Y",  NSwitchType::kSimple, false },
+    #ifndef _NO_CRYPTO
+    { L"P",  NSwitchType::kUnLimitedPostString, false, 0 },
+    #endif
+    { L"M",  NSwitchType::kUnLimitedPostString, true, 1 },
+    { L"O",  NSwitchType::kUnLimitedPostString, false, 1 },
+    { L"W",  NSwitchType::kUnLimitedPostString, false, 0 },
+    { L"I",  NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize},
+    { L"X",  NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize},
+    { L"AI", NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize},
+    { L"AX", NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize},
+    { L"AN", NSwitchType::kSimple, false },
+    { L"U",  NSwitchType::kUnLimitedPostString, true, 1},
+    { L"V",  NSwitchType::kUnLimitedPostString, true, 1},
+    { L"R",  NSwitchType::kPostChar, false, 0, 0, kRecursedPostCharSet },
+    { L"SFX", NSwitchType::kUnLimitedPostString, false, 0 },
+    { L"SI", NSwitchType::kUnLimitedPostString, false, 0 },
+    { L"SO", NSwitchType::kSimple, false, 0 },
+    { L"AO", NSwitchType::kPostChar, false, 1, 1, kOverwritePostCharSet},
+    { L"SEML", NSwitchType::kUnLimitedPostString, false, 0},
+    { L"AD",  NSwitchType::kSimple, false },
+    { L"SLP", NSwitchType::kUnLimitedPostString, false, 0},
+    { L"SCS", NSwitchType::kUnLimitedPostString, false, 0},
+    { L"SLT", NSwitchType::kSimple, false },
+    { L"SSW", NSwitchType::kSimple, false },
+    { L"SSC", NSwitchType::kPostChar, false, 0, 0, L"-" }
+  };
+
+static const CCommandForm g_CommandForms[] =
+{
+  { L"A", false },
+  { L"U", false },
+  { L"D", false },
+  { L"T", false },
+  { L"E", false },
+  { L"X", false },
+  { L"L", false },
+  { L"B", false },
+  { L"I", false }
+};
+
+static const int kNumCommandForms = sizeof(g_CommandForms) /  sizeof(g_CommandForms[0]);
+
+static const wchar_t *kUniversalWildcard = L"*";
+static const int kMinNonSwitchWords = 1;
+static const int kCommandIndex = 0;
+
+// ---------------------------
+// exception messages
+
+static const char *kUserErrorMessage  = "Incorrect command line";
+static const char *kIncorrectListFile = "Incorrect item in listfile.\nCheck charset encoding and -scs switch.";
+static const char *kIncorrectWildCardInListFile = "Incorrect wildcard in listfile";
+static const char *kIncorrectWildCardInCommandLine  = "Incorrect wildcard in command line";
+static const char *kTerminalOutError = "I won't write compressed data to a terminal";
+static const char *kSameTerminalError = "I won't write data and program's messages to same terminal";
+
+static void ThrowException(const char *errorMessage)
+{
+  throw CArchiveCommandLineException(errorMessage);
+};
+
+static void ThrowUserErrorException()
+{
+  ThrowException(kUserErrorMessage);
+};
+
+// ---------------------------
+
+bool CArchiveCommand::IsFromExtractGroup() const
+{
+  switch(CommandType)
+  {
+    case NCommandType::kTest:
+    case NCommandType::kExtract:
+    case NCommandType::kFullExtract:
+      return true;
+    default:
+      return false;
+  }
+}
+
+NExtract::NPathMode::EEnum CArchiveCommand::GetPathMode() const
+{
+  switch(CommandType)
+  {
+    case NCommandType::kTest:
+    case NCommandType::kFullExtract:
+      return NExtract::NPathMode::kFullPathnames;
+    default:
+      return NExtract::NPathMode::kNoPathnames;
+  }
+}
+
+bool CArchiveCommand::IsFromUpdateGroup() const
+{
+  return (CommandType == NCommandType::kAdd ||
+    CommandType == NCommandType::kUpdate ||
+    CommandType == NCommandType::kDelete);
+}
+
+static NRecursedType::EEnum GetRecursedTypeFromIndex(int index)
+{
+  switch (index)
+  {
+    case NRecursedPostCharIndex::kWildCardRecursionOnly:
+      return NRecursedType::kWildCardOnlyRecursed;
+    case NRecursedPostCharIndex::kNoRecursion:
+      return NRecursedType::kNonRecursed;
+    default:
+      return NRecursedType::kRecursed;
+  }
+}
+
+static bool ParseArchiveCommand(const UString &commandString, CArchiveCommand &command)
+{
+  UString commandStringUpper = commandString;
+  commandStringUpper.MakeUpper();
+  UString postString;
+  int commandIndex = ParseCommand(kNumCommandForms, g_CommandForms, commandStringUpper,
+      postString) ;
+  if (commandIndex < 0)
+    return false;
+  command.CommandType = (NCommandType::EEnum)commandIndex;
+  return true;
+}
+
+// ------------------------------------------------------------------
+// filenames functions
+
+static bool AddNameToCensor(NWildcard::CCensor &wildcardCensor,
+    const UString &name, bool include, NRecursedType::EEnum type)
+{
+  bool isWildCard = DoesNameContainWildCard(name);
+  bool recursed = false;
+
+  switch (type)
+  {
+    case NRecursedType::kWildCardOnlyRecursed:
+      recursed = isWildCard;
+      break;
+    case NRecursedType::kRecursed:
+      recursed = true;
+      break;
+    case NRecursedType::kNonRecursed:
+      recursed = false;
+      break;
+  }
+  wildcardCensor.AddItem(include, name, recursed);
+  return true;
+}
+
+static void AddToCensorFromListFile(NWildcard::CCensor &wildcardCensor,
+    LPCWSTR fileName, bool include, NRecursedType::EEnum type, UINT codePage)
+{
+  UStringVector names;
+  if (!ReadNamesFromListFile(fileName, names, codePage))
+    throw kIncorrectListFile;
+  for (int i = 0; i < names.Size(); i++)
+    if (!AddNameToCensor(wildcardCensor, names[i], include, type))
+      throw kIncorrectWildCardInListFile;
+}
+
+static void AddCommandLineWildCardToCensr(NWildcard::CCensor &wildcardCensor,
+    const UString &name, bool include, NRecursedType::EEnum recursedType)
+{
+  if (!AddNameToCensor(wildcardCensor, name, include, recursedType))
+    throw kIncorrectWildCardInCommandLine;
+}
+
+static void AddToCensorFromNonSwitchesStrings(
+    int startIndex,
+    NWildcard::CCensor &wildcardCensor,
+    const UStringVector &nonSwitchStrings, NRecursedType::EEnum type,
+    bool thereAreSwitchIncludes, UINT codePage)
+{
+  if (nonSwitchStrings.Size() == startIndex && (!thereAreSwitchIncludes))
+    AddCommandLineWildCardToCensr(wildcardCensor, kUniversalWildcard, true, type);
+  for (int i = startIndex; i < nonSwitchStrings.Size(); i++)
+  {
+    const UString &s = nonSwitchStrings[i];
+    if (s[0] == kFileListID)
+      AddToCensorFromListFile(wildcardCensor, s.Mid(1), true, type, codePage);
+    else
+      AddCommandLineWildCardToCensr(wildcardCensor, s, true, type);
+  }
+}
+
+#ifdef _WIN32
+static void ParseMapWithPaths(NWildcard::CCensor &wildcardCensor,
+    const UString &switchParam, bool include,
+    NRecursedType::EEnum commonRecursedType)
+{
+  int splitPos = switchParam.Find(L':');
+  if (splitPos < 0)
+    ThrowUserErrorException();
+  UString mappingName = switchParam.Left(splitPos);
+  
+  UString switchParam2 = switchParam.Mid(splitPos + 1);
+  splitPos = switchParam2.Find(L':');
+  if (splitPos < 0)
+    ThrowUserErrorException();
+  
+  UString mappingSize = switchParam2.Left(splitPos);
+  UString eventName = switchParam2.Mid(splitPos + 1);
+  
+  UInt64 dataSize64 = ConvertStringToUInt64(mappingSize, NULL);
+  UInt32 dataSize = (UInt32)dataSize64;
+  {
+    CFileMapping fileMapping;
+    if (!fileMapping.Open(FILE_MAP_READ, false, GetSystemString(mappingName)))
+      ThrowException("Can not open mapping");
+    LPVOID data = fileMapping.MapViewOfFile(FILE_MAP_READ, 0, dataSize);
+    if (data == NULL)
+      ThrowException("MapViewOfFile error");
+    try
+    {
+      const wchar_t *curData = (const wchar_t *)data;
+      if (*curData != 0)
+        ThrowException("Incorrect mapping data");
+      UInt32 numChars = dataSize / sizeof(wchar_t);
+      UString name;
+      for (UInt32 i = 1; i < numChars; i++)
+      {
+        wchar_t c = curData[i];
+        if (c == L'\0')
+        {
+          AddCommandLineWildCardToCensr(wildcardCensor,
+              name, include, commonRecursedType);
+          name.Empty();
+        }
+        else
+          name += c;
+      }
+      if (!name.IsEmpty())
+        ThrowException("data error");
+    }
+    catch(...)
+    {
+      UnmapViewOfFile(data);
+      throw;
+    }
+    UnmapViewOfFile(data);
+  }
+  
+  {
+    NSynchronization::CManualResetEvent event;
+    if (event.Open(EVENT_MODIFY_STATE, false, GetSystemString(eventName)) == S_OK)
+      event.Set();
+  }
+}
+#endif
+
+static void AddSwitchWildCardsToCensor(NWildcard::CCensor &wildcardCensor,
+    const UStringVector &strings, bool include,
+    NRecursedType::EEnum commonRecursedType, UINT codePage)
+{
+  for (int i = 0; i < strings.Size(); i++)
+  {
+    const UString &name = strings[i];
+    NRecursedType::EEnum recursedType;
+    int pos = 0;
+    if (name.Length() < kSomeCludePostStringMinSize)
+      ThrowUserErrorException();
+    if (::MyCharUpper(name[pos]) == kRecursedIDChar)
+    {
+      pos++;
+      int index = UString(kRecursedPostCharSet).Find(name[pos]);
+      recursedType = GetRecursedTypeFromIndex(index);
+      if (index >= 0)
+        pos++;
+    }
+    else
+      recursedType = commonRecursedType;
+    if (name.Length() < pos + kSomeCludeAfterRecursedPostStringMinSize)
+      ThrowUserErrorException();
+    UString tail = name.Mid(pos + 1);
+    if (name[pos] == kImmediateNameID)
+      AddCommandLineWildCardToCensr(wildcardCensor, tail, include, recursedType);
+    else if (name[pos] == kFileListID)
+      AddToCensorFromListFile(wildcardCensor, tail, include, recursedType, codePage);
+    #ifdef _WIN32
+    else if (name[pos] == kMapNameID)
+      ParseMapWithPaths(wildcardCensor, tail, include, recursedType);
+    #endif
+    else
+      ThrowUserErrorException();
+  }
+}
+
+#ifdef _WIN32
+
+// This code converts all short file names to long file names.
+
+static void ConvertToLongName(const UString &prefix, UString &name)
+{
+  if (name.IsEmpty() || DoesNameContainWildCard(name))
+    return;
+  NFind::CFileInfoW fileInfo;
+  if (NFind::FindFile(prefix + name, fileInfo))
+    name = fileInfo.Name;
+}
+
+static void ConvertToLongNames(const UString &prefix, CObjectVector<NWildcard::CItem> &items)
+{
+  for (int i = 0; i < items.Size(); i++)
+  {
+    NWildcard::CItem &item = items[i];
+    if (item.Recursive || item.PathParts.Size() != 1)
+      continue;
+    ConvertToLongName(prefix, item.PathParts.Front());
+  }
+}
+
+static void ConvertToLongNames(const UString &prefix, NWildcard::CCensorNode &node)
+{
+  ConvertToLongNames(prefix, node.IncludeItems);
+  ConvertToLongNames(prefix, node.ExcludeItems);
+  int i;
+  for (i = 0; i < node.SubNodes.Size(); i++)
+    ConvertToLongName(prefix, node.SubNodes[i].Name);
+  // mix folders with same name
+  for (i = 0; i < node.SubNodes.Size(); i++)
+  {
+    NWildcard::CCensorNode &nextNode1 = node.SubNodes[i];
+    for (int j = i + 1; j < node.SubNodes.Size();)
+    {
+      const NWildcard::CCensorNode &nextNode2 = node.SubNodes[j];
+      if (nextNode1.Name.CompareNoCase(nextNode2.Name) == 0)
+      {
+        nextNode1.IncludeItems += nextNode2.IncludeItems;
+        nextNode1.ExcludeItems += nextNode2.ExcludeItems;
+        node.SubNodes.Delete(j);
+      }
+      else
+        j++;
+    }
+  }
+  for (i = 0; i < node.SubNodes.Size(); i++)
+  {
+    NWildcard::CCensorNode &nextNode = node.SubNodes[i];
+    ConvertToLongNames(prefix + nextNode.Name + wchar_t(NFile::NName::kDirDelimiter), nextNode);
+  }
+}
+
+static void ConvertToLongNames(NWildcard::CCensor &censor)
+{
+  for (int i = 0; i < censor.Pairs.Size(); i++)
+  {
+    NWildcard::CPair &pair = censor.Pairs[i];
+    ConvertToLongNames(pair.Prefix, pair.Head);
+  }
+}
+
+#endif
+
+static NUpdateArchive::NPairAction::EEnum GetUpdatePairActionType(int i)
+{
+  switch(i)
+  {
+    case NUpdateArchive::NPairAction::kIgnore: return NUpdateArchive::NPairAction::kIgnore;
+    case NUpdateArchive::NPairAction::kCopy: return NUpdateArchive::NPairAction::kCopy;
+    case NUpdateArchive::NPairAction::kCompress: return NUpdateArchive::NPairAction::kCompress;
+    case NUpdateArchive::NPairAction::kCompressAsAnti: return NUpdateArchive::NPairAction::kCompressAsAnti;
+  }
+  throw 98111603;
+}
+
+const UString kUpdatePairStateIDSet = L"PQRXYZW";
+const int kUpdatePairStateNotSupportedActions[] = {2, 2, 1, -1, -1, -1, -1};
+
+const UString kUpdatePairActionIDSet = L"0123"; //Ignore, Copy, Compress, Create Anti
+
+const wchar_t *kUpdateIgnoreItselfPostStringID = L"-";
+const wchar_t kUpdateNewArchivePostCharID = '!';
+
+
+static bool ParseUpdateCommandString2(const UString &command,
+    NUpdateArchive::CActionSet &actionSet, UString &postString)
+{
+  for (int i = 0; i < command.Length();)
+  {
+    wchar_t c = MyCharUpper(command[i]);
+    int statePos = kUpdatePairStateIDSet.Find(c);
+    if (statePos < 0)
+    {
+      postString = command.Mid(i);
+      return true;
+    }
+    i++;
+    if (i >= command.Length())
+      return false;
+    int actionPos = kUpdatePairActionIDSet.Find(::MyCharUpper(command[i]));
+    if (actionPos < 0)
+      return false;
+    actionSet.StateActions[statePos] = GetUpdatePairActionType(actionPos);
+    if (kUpdatePairStateNotSupportedActions[statePos] == actionPos)
+      return false;
+    i++;
+  }
+  postString.Empty();
+  return true;
+}
+
+static void ParseUpdateCommandString(CUpdateOptions &options,
+    const UStringVector &updatePostStrings,
+    const NUpdateArchive::CActionSet &defaultActionSet)
+{
+  for (int i = 0; i < updatePostStrings.Size(); i++)
+  {
+    const UString &updateString = updatePostStrings[i];
+    if (updateString.CompareNoCase(kUpdateIgnoreItselfPostStringID) == 0)
+    {
+      if (options.UpdateArchiveItself)
+      {
+        options.UpdateArchiveItself = false;
+        options.Commands.Delete(0);
+      }
+    }
+    else
+    {
+      NUpdateArchive::CActionSet actionSet = defaultActionSet;
+
+      UString postString;
+      if (!ParseUpdateCommandString2(updateString, actionSet, postString))
+        ThrowUserErrorException();
+      if (postString.IsEmpty())
+      {
+        if (options.UpdateArchiveItself)
+          options.Commands[0].ActionSet = actionSet;
+      }
+      else
+      {
+        if (MyCharUpper(postString[0]) != kUpdateNewArchivePostCharID)
+          ThrowUserErrorException();
+        CUpdateArchiveCommand uc;
+        UString archivePath = postString.Mid(1);
+        if (archivePath.IsEmpty())
+          ThrowUserErrorException();
+        uc.UserArchivePath = archivePath;
+        uc.ActionSet = actionSet;
+        options.Commands.Add(uc);
+      }
+    }
+  }
+}
+
+static const char kByteSymbol = 'B';
+static const char kKiloSymbol = 'K';
+static const char kMegaSymbol = 'M';
+static const char kGigaSymbol = 'G';
+
+static bool ParseComplexSize(const UString &src, UInt64 &result)
+{
+  UString s = src;
+  s.MakeUpper();
+
+  const wchar_t *start = s;
+  const wchar_t *end;
+  UInt64 number = ConvertStringToUInt64(start, &end);
+  int numDigits = (int)(end - start);
+  if (numDigits == 0 || s.Length() > numDigits + 1)
+    return false;
+  if (s.Length() == numDigits)
+  {
+    result = number;
+    return true;
+  }
+  int numBits;
+  switch (s[numDigits])
+  {
+    case kByteSymbol:
+      result = number;
+      return true;
+    case kKiloSymbol:
+      numBits = 10;
+      break;
+    case kMegaSymbol:
+      numBits = 20;
+      break;
+    case kGigaSymbol:
+      numBits = 30;
+      break;
+    default:
+      return false;
+  }
+  if (number >= ((UInt64)1 << (64 - numBits)))
+    return false;
+  result = number << numBits;
+  return true;
+}
+
+static void SetAddCommandOptions(
+    NCommandType::EEnum commandType,
+    const CParser &parser,
+    CUpdateOptions &options)
+{
+  NUpdateArchive::CActionSet defaultActionSet;
+  switch(commandType)
+  {
+    case NCommandType::kAdd:
+      defaultActionSet = NUpdateArchive::kAddActionSet;
+      break;
+    case NCommandType::kDelete:
+      defaultActionSet = NUpdateArchive::kDeleteActionSet;
+      break;
+    default:
+      defaultActionSet = NUpdateArchive::kUpdateActionSet;
+  }
+  
+  options.UpdateArchiveItself = true;
+  
+  options.Commands.Clear();
+  CUpdateArchiveCommand updateMainCommand;
+  updateMainCommand.ActionSet = defaultActionSet;
+  options.Commands.Add(updateMainCommand);
+  if (parser[NKey::kUpdate].ThereIs)
+    ParseUpdateCommandString(options, parser[NKey::kUpdate].PostStrings,
+        defaultActionSet);
+  if (parser[NKey::kWorkingDir].ThereIs)
+  {
+    const UString &postString = parser[NKey::kWorkingDir].PostStrings[0];
+    if (postString.IsEmpty())
+      NDirectory::MyGetTempPath(options.WorkingDir);
+    else
+      options.WorkingDir = postString;
+  }
+  options.SfxMode = parser[NKey::kSfx].ThereIs;
+  if (options.SfxMode)
+    options.SfxModule = parser[NKey::kSfx].PostStrings[0];
+
+  if (parser[NKey::kVolume].ThereIs)
+  {
+    const UStringVector &sv = parser[NKey::kVolume].PostStrings;
+    for (int i = 0; i < sv.Size(); i++)
+    {
+      UInt64 size;
+      if (!ParseComplexSize(sv[i], size))
+        ThrowException("Incorrect volume size");
+      options.VolumesSizes.Add(size);
+    }
+  }
+}
+
+static void SetMethodOptions(const CParser &parser, CObjectVector<CProperty> &properties)
+{
+  if (parser[NKey::kProperty].ThereIs)
+  {
+    // options.MethodMode.Properties.Clear();
+    for (int i = 0; i < parser[NKey::kProperty].PostStrings.Size(); i++)
+    {
+      CProperty property;
+      const UString &postString = parser[NKey::kProperty].PostStrings[i];
+      int index = postString.Find(L'=');
+      if (index < 0)
+        property.Name = postString;
+      else
+      {
+        property.Name = postString.Left(index);
+        property.Value = postString.Mid(index + 1);
+      }
+      properties.Add(property);
+    }
+  }
+}
+
+CArchiveCommandLineParser::CArchiveCommandLineParser():
+  parser(sizeof(kSwitchForms) / sizeof(kSwitchForms[0])) {}
+
+void CArchiveCommandLineParser::Parse1(const UStringVector &commandStrings,
+    CArchiveCommandLineOptions &options)
+{
+  try
+  {
+    parser.ParseStrings(kSwitchForms, commandStrings);
+  }
+  catch(...)
+  {
+    ThrowUserErrorException();
+  }
+
+  options.IsInTerminal = MY_IS_TERMINAL(stdin);
+  options.IsStdOutTerminal = MY_IS_TERMINAL(stdout);
+  options.IsStdErrTerminal = MY_IS_TERMINAL(stderr);
+  options.StdOutMode = parser[NKey::kStdOut].ThereIs;
+  options.EnableHeaders = !parser[NKey::kDisableHeaders].ThereIs;
+  options.HelpMode = parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs  || parser[NKey::kHelp3].ThereIs;
+
+  #ifdef _WIN32
+  options.LargePages = false;
+  if (parser[NKey::kLargePages].ThereIs)
+  {
+    const UString &postString = parser[NKey::kLargePages].PostStrings.Front();
+    if (postString.IsEmpty())
+      options.LargePages = true;
+  }
+  #endif
+}
+
+struct CCodePagePair
+{
+  const wchar_t *Name;
+  UINT CodePage;
+};
+
+static CCodePagePair g_CodePagePairs[] =
+{
+  { L"UTF-8", CP_UTF8 },
+  { L"WIN",   CP_ACP },
+  { L"DOS",   CP_OEMCP }
+};
+
+static const int kNumCodePages = sizeof(g_CodePagePairs) / sizeof(g_CodePagePairs[0]);
+
+static bool ConvertStringToUInt32(const wchar_t *s, UInt32 &v)
+{
+  const wchar_t *end;
+  UInt64 number = ConvertStringToUInt64(s, &end);
+  if (*end != 0)
+    return false;
+  if (number > (UInt32)0xFFFFFFFF)
+    return false;
+  v = (UInt32)number;
+  return true;
+}
+
+void CArchiveCommandLineParser::Parse2(CArchiveCommandLineOptions &options)
+{
+  const UStringVector &nonSwitchStrings = parser.NonSwitchStrings;
+  int numNonSwitchStrings = nonSwitchStrings.Size();
+  if (numNonSwitchStrings < kMinNonSwitchWords)
+    ThrowUserErrorException();
+
+  if (!ParseArchiveCommand(nonSwitchStrings[kCommandIndex], options.Command))
+    ThrowUserErrorException();
+
+  options.TechMode = parser[NKey::kTechMode].ThereIs;
+
+  if (parser[NKey::kCaseSensitive].ThereIs)
+    g_CaseSensitive = (parser[NKey::kCaseSensitive].PostCharIndex < 0);
+
+  NRecursedType::EEnum recursedType;
+  if (parser[NKey::kRecursed].ThereIs)
+    recursedType = GetRecursedTypeFromIndex(parser[NKey::kRecursed].PostCharIndex);
+  else
+    recursedType = NRecursedType::kNonRecursed;
+
+  UINT codePage = CP_UTF8;
+  if (parser[NKey::kCharSet].ThereIs)
+  {
+    UString name = parser[NKey::kCharSet].PostStrings.Front();
+    name.MakeUpper();
+    int i;
+    for (i = 0; i < kNumCodePages; i++)
+    {
+      const CCodePagePair &pair = g_CodePagePairs[i];
+      if (name.Compare(pair.Name) == 0)
+      {
+        codePage = pair.CodePage;
+        break;
+      }
+    }
+    if (i >= kNumCodePages)
+      ThrowUserErrorException();
+  }
+
+  bool thereAreSwitchIncludes = false;
+  if (parser[NKey::kInclude].ThereIs)
+  {
+    thereAreSwitchIncludes = true;
+    AddSwitchWildCardsToCensor(options.WildcardCensor,
+        parser[NKey::kInclude].PostStrings, true, recursedType, codePage);
+  }
+  if (parser[NKey::kExclude].ThereIs)
+    AddSwitchWildCardsToCensor(options.WildcardCensor,
+        parser[NKey::kExclude].PostStrings, false, recursedType, codePage);
+ 
+  int curCommandIndex = kCommandIndex + 1;
+  bool thereIsArchiveName = !parser[NKey::kNoArName].ThereIs &&
+      options.Command.CommandType != NCommandType::kBenchmark &&
+      options.Command.CommandType != NCommandType::kInfo;
+  if (thereIsArchiveName)
+  {
+    if (curCommandIndex >= numNonSwitchStrings)
+      ThrowUserErrorException();
+    options.ArchiveName = nonSwitchStrings[curCommandIndex++];
+  }
+
+  AddToCensorFromNonSwitchesStrings(
+      curCommandIndex, options.WildcardCensor,
+      nonSwitchStrings, recursedType, thereAreSwitchIncludes, codePage);
+
+  options.YesToAll = parser[NKey::kYes].ThereIs;
+
+  bool isExtractGroupCommand = options.Command.IsFromExtractGroup();
+
+  #ifndef _NO_CRYPTO
+  options.PasswordEnabled = parser[NKey::kPassword].ThereIs;
+  if (options.PasswordEnabled)
+    options.Password = parser[NKey::kPassword].PostStrings[0];
+  #endif
+
+  options.StdInMode = parser[NKey::kStdIn].ThereIs;
+  options.ShowDialog = parser[NKey::kShowDialog].ThereIs;
+
+  if (parser[NKey::kArchiveType].ThereIs)
+    options.ArcType = parser[NKey::kArchiveType].PostStrings[0];
+
+  if (isExtractGroupCommand || options.Command.CommandType == NCommandType::kList)
+  {
+    if (options.StdInMode)
+      ThrowException("Reading archives from stdin is not implemented");
+    if (!options.WildcardCensor.AllAreRelative())
+      ThrowException("Cannot use absolute pathnames for this command");
+
+    NWildcard::CCensor archiveWildcardCensor;
+
+    if (parser[NKey::kArInclude].ThereIs)
+    {
+      AddSwitchWildCardsToCensor(archiveWildcardCensor,
+        parser[NKey::kArInclude].PostStrings, true, NRecursedType::kNonRecursed, codePage);
+    }
+    if (parser[NKey::kArExclude].ThereIs)
+      AddSwitchWildCardsToCensor(archiveWildcardCensor,
+      parser[NKey::kArExclude].PostStrings, false, NRecursedType::kNonRecursed, codePage);
+
+    if (thereIsArchiveName)
+      AddCommandLineWildCardToCensr(archiveWildcardCensor, options.ArchiveName, true, NRecursedType::kNonRecursed);
+
+    #ifdef _WIN32
+    ConvertToLongNames(archiveWildcardCensor);
+    #endif
+
+    archiveWildcardCensor.ExtendExclude();
+
+    UStringVector archivePaths;
+
+    {
+      CDirItems dirItems;
+      {
+        UStringVector errorPaths;
+        CRecordVector<DWORD> errorCodes;
+        HRESULT res = EnumerateItems(archiveWildcardCensor, dirItems, NULL, errorPaths, errorCodes);
+        if (res != S_OK || errorPaths.Size() > 0)
+          throw "cannot find archive";
+      }
+      for (int i = 0; i < dirItems.Items.Size(); i++)
+      {
+        const CDirItem &dirItem = dirItems.Items[i];
+        if (!dirItem.IsDir())
+          archivePaths.Add(dirItems.GetPhyPath(i));
+      }
+    }
+
+    if (archivePaths.Size() == 0)
+      throw "there is no such archive";
+
+    UStringVector archivePathsFull;
+
+    int i;
+    for (i = 0; i < archivePaths.Size(); i++)
+    {
+      UString fullPath;
+      NFile::NDirectory::MyGetFullPathName(archivePaths[i], fullPath);
+      archivePathsFull.Add(fullPath);
+    }
+    CIntVector indices;
+    SortFileNames(archivePathsFull, indices);
+    options.ArchivePathsSorted.Reserve(indices.Size());
+    options.ArchivePathsFullSorted.Reserve(indices.Size());
+    for (i = 0; i < indices.Size(); i++)
+    {
+      options.ArchivePathsSorted.Add(archivePaths[indices[i]]);
+      options.ArchivePathsFullSorted.Add(archivePathsFull[indices[i]]);
+    }
+
+    if (isExtractGroupCommand)
+    {
+      SetMethodOptions(parser, options.ExtractProperties);
+      if (options.StdOutMode && options.IsStdOutTerminal && options.IsStdErrTerminal)
+        throw kSameTerminalError;
+      if (parser[NKey::kOutputDir].ThereIs)
+      {
+        options.OutputDir = parser[NKey::kOutputDir].PostStrings[0];
+        NFile::NName::NormalizeDirPathPrefix(options.OutputDir);
+      }
+
+      options.OverwriteMode = NExtract::NOverwriteMode::kAskBefore;
+      if (parser[NKey::kOverwrite].ThereIs)
+        options.OverwriteMode =
+            k_OverwriteModes[parser[NKey::kOverwrite].PostCharIndex];
+      else if (options.YesToAll)
+        options.OverwriteMode = NExtract::NOverwriteMode::kWithoutPrompt;
+    }
+  }
+  else if (options.Command.IsFromUpdateGroup())
+  {
+    CUpdateOptions &updateOptions = options.UpdateOptions;
+
+    SetAddCommandOptions(options.Command.CommandType, parser, updateOptions);
+    
+    SetMethodOptions(parser, updateOptions.MethodMode.Properties);
+
+    if (parser[NKey::kShareForWrite].ThereIs)
+      updateOptions.OpenShareForWrite = true;
+
+    options.EnablePercents = !parser[NKey::kDisablePercents].ThereIs;
+
+    if (options.EnablePercents)
+    {
+      if ((options.StdOutMode && !options.IsStdErrTerminal) ||
+         (!options.StdOutMode && !options.IsStdOutTerminal))
+        options.EnablePercents = false;
+    }
+
+    updateOptions.EMailMode = parser[NKey::kEmail].ThereIs;
+    if (updateOptions.EMailMode)
+    {
+      updateOptions.EMailAddress = parser[NKey::kEmail].PostStrings.Front();
+      if (updateOptions.EMailAddress.Length() > 0)
+        if (updateOptions.EMailAddress[0] == L'.')
+        {
+          updateOptions.EMailRemoveAfter = true;
+          updateOptions.EMailAddress.Delete(0);
+        }
+    }
+
+    updateOptions.StdOutMode = options.StdOutMode;
+    updateOptions.StdInMode = options.StdInMode;
+
+    if (updateOptions.StdOutMode && updateOptions.EMailMode)
+      throw "stdout mode and email mode cannot be combined";
+    if (updateOptions.StdOutMode && options.IsStdOutTerminal)
+      throw kTerminalOutError;
+    if (updateOptions.StdInMode)
+      updateOptions.StdInFileName = parser[NKey::kStdIn].PostStrings.Front();
+
+    #ifdef _WIN32
+    ConvertToLongNames(options.WildcardCensor);
+    #endif
+  }
+  else if (options.Command.CommandType == NCommandType::kBenchmark)
+  {
+    options.NumThreads = (UInt32)-1;
+    options.DictionarySize = (UInt32)-1;
+    options.NumIterations = 1;
+    if (curCommandIndex < numNonSwitchStrings)
+    {
+      if (!ConvertStringToUInt32(nonSwitchStrings[curCommandIndex++], options.NumIterations))
+        ThrowUserErrorException();
+    }
+    for (int i = 0; i < parser[NKey::kProperty].PostStrings.Size(); i++)
+    {
+      UString postString = parser[NKey::kProperty].PostStrings[i];
+      postString.MakeUpper();
+      if (postString.Length() < 2)
+        ThrowUserErrorException();
+      if (postString[0] == 'D')
+      {
+        int pos = 1;
+        if (postString[pos] == '=')
+          pos++;
+        UInt32 logSize;
+        if (!ConvertStringToUInt32((const wchar_t *)postString + pos, logSize))
+          ThrowUserErrorException();
+        if (logSize > 31)
+          ThrowUserErrorException();
+        options.DictionarySize = 1 << logSize;
+      }
+      else if (postString[0] == 'M' && postString[1] == 'T' )
+      {
+        int pos = 2;
+        if (postString[pos] == '=')
+          pos++;
+        if (postString[pos] != 0)
+          if (!ConvertStringToUInt32((const wchar_t *)postString + pos, options.NumThreads))
+            ThrowUserErrorException();
+      }
+      else if (postString[0] == 'M' && postString[1] == '=' )
+      {
+        int pos = 2;
+        if (postString[pos] != 0)
+          options.Method = postString.Mid(2);
+      }
+      else
+        ThrowUserErrorException();
+    }
+  }
+  else if (options.Command.CommandType == NCommandType::kInfo)
+  {
+  }
+  else
+    ThrowUserErrorException();
+  options.WildcardCensor.ExtendExclude();
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveCommandLine.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveCommandLine.h
new file mode 100644
index 0000000..9c37f33
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveCommandLine.h
@@ -0,0 +1,106 @@
+// ArchiveCommandLine.h
+
+#ifndef __ARCHIVECOMMANDLINE_H
+#define __ARCHIVECOMMANDLINE_H
+
+#include "Common/Wildcard.h"
+#include "Common/CommandLineParser.h"
+
+#include "Extract.h"
+#include "Update.h"
+
+struct CArchiveCommandLineException: public AString
+{
+  CArchiveCommandLineException(const char *errorMessage): AString(errorMessage) {}
+};
+
+namespace NCommandType { enum EEnum
+{
+  kAdd = 0,
+  kUpdate,
+  kDelete,
+  kTest,
+  kExtract,
+  kFullExtract,
+  kList,
+  kBenchmark,
+  kInfo
+};}
+
+namespace NRecursedType { enum EEnum
+{
+  kRecursed,
+  kWildCardOnlyRecursed,
+  kNonRecursed
+};}
+
+struct CArchiveCommand
+{
+  NCommandType::EEnum CommandType;
+  bool IsFromExtractGroup() const;
+  bool IsFromUpdateGroup() const;
+  bool IsTestMode() const { return CommandType == NCommandType::kTest; }
+  NExtract::NPathMode::EEnum GetPathMode() const;
+};
+
+struct CArchiveCommandLineOptions
+{
+  bool HelpMode;
+
+  #ifdef _WIN32
+  bool LargePages;
+  #endif
+
+  bool IsInTerminal;
+  bool IsStdOutTerminal;
+  bool IsStdErrTerminal;
+  bool StdInMode;
+  bool StdOutMode;
+  bool EnableHeaders;
+
+  bool YesToAll;
+  bool ShowDialog;
+  // NWildcard::CCensor ArchiveWildcardCensor;
+  NWildcard::CCensor WildcardCensor;
+
+  CArchiveCommand Command;
+  UString ArchiveName;
+
+  #ifndef _NO_CRYPTO
+  bool PasswordEnabled;
+  UString Password;
+  #endif
+
+  bool TechMode;
+  // Extract
+  bool AppendName;
+  UString OutputDir;
+  NExtract::NOverwriteMode::EEnum OverwriteMode;
+  UStringVector ArchivePathsSorted;
+  UStringVector ArchivePathsFullSorted;
+  CObjectVector<CProperty> ExtractProperties;
+
+  CUpdateOptions UpdateOptions;
+  UString ArcType;
+  bool EnablePercents;
+
+  // Benchmark
+  UInt32 NumIterations;
+  UInt32 NumThreads;
+  UInt32 DictionarySize;
+  UString Method;
+
+
+  CArchiveCommandLineOptions(): StdInMode(false), StdOutMode(false) {};
+};
+
+class CArchiveCommandLineParser
+{
+  NCommandLineParser::CParser parser;
+public:
+  CArchiveCommandLineParser();
+  void Parse1(const UStringVector &commandStrings, CArchiveCommandLineOptions &options);
+  void Parse2(CArchiveCommandLineOptions &options);
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
new file mode 100644
index 0000000..5af5286
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp
@@ -0,0 +1,476 @@
+// ArchiveExtractCallback.cpp
+
+#include "StdAfx.h"
+
+#include "ArchiveExtractCallback.h"
+
+#include "Common/Wildcard.h"
+#include "Common/StringConvert.h"
+#include "Common/ComTry.h"
+
+#include "Windows/FileDir.h"
+#include "Windows/FileFind.h"
+#include "Windows/Time.h"
+#include "Windows/Defs.h"
+#include "Windows/PropVariant.h"
+
+#include "Windows/PropVariantConversions.h"
+
+#include "../../Common/FilePathAutoRename.h"
+
+#include "../Common/ExtractingFilePath.h"
+#include "OpenArchive.h"
+
+using namespace NWindows;
+
+static const wchar_t *kCantAutoRename = L"ERROR: Can not create file with auto name";
+static const wchar_t *kCantRenameFile = L"ERROR: Can not rename existing file ";
+static const wchar_t *kCantDeleteOutputFile = L"ERROR: Can not delete output file ";
+
+
+void CArchiveExtractCallback::Init(
+    IInArchive *archiveHandler,
+    IFolderArchiveExtractCallback *extractCallback2,
+    bool stdOutMode,
+    const UString &directoryPath,
+    const UStringVector &removePathParts,
+    const UString &itemDefaultName,
+    const FILETIME &utcMTimeDefault,
+    UInt32 attributesDefault,
+    UInt64 packSize)
+{
+  _stdOutMode = stdOutMode;
+  _numErrors = 0;
+  _unpTotal = 1;
+  _packTotal = packSize;
+
+  _extractCallback2 = extractCallback2;
+  _compressProgress.Release();
+  _extractCallback2.QueryInterface(IID_ICompressProgressInfo, &_compressProgress);
+
+  LocalProgressSpec->Init(extractCallback2, true);
+  LocalProgressSpec->SendProgress = false;
+
+  _itemDefaultName = itemDefaultName;
+  _utcMTimeDefault = utcMTimeDefault;
+  _attributesDefault = attributesDefault;
+  _removePathParts = removePathParts;
+  _archiveHandler = archiveHandler;
+  _directoryPath = directoryPath;
+  NFile::NName::NormalizeDirPathPrefix(_directoryPath);
+}
+
+STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 size)
+{
+  COM_TRY_BEGIN
+  _unpTotal = size;
+  if (!_multiArchives && _extractCallback2)
+    return _extractCallback2->SetTotal(size);
+  return S_OK;
+  COM_TRY_END
+}
+
+static void NormalizeVals(UInt64 &v1, UInt64 &v2)
+{
+  const UInt64 kMax = (UInt64)1 << 31;
+  while (v1 > kMax)
+  {
+    v1 >>= 1;
+    v2 >>= 1;
+  }
+}
+
+static UInt64 MyMultDiv64(UInt64 unpCur, UInt64 unpTotal, UInt64 packTotal)
+{
+  NormalizeVals(packTotal, unpTotal);
+  NormalizeVals(unpCur, unpTotal);
+  if (unpTotal == 0)
+    unpTotal = 1;
+  return unpCur * packTotal / unpTotal;
+}
+
+STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue)
+{
+  COM_TRY_BEGIN
+  if (!_extractCallback2)
+    return S_OK;
+
+  if (_multiArchives)
+  {
+    if (completeValue != NULL)
+    {
+      UInt64 packCur = LocalProgressSpec->InSize + MyMultDiv64(*completeValue, _unpTotal, _packTotal);
+      return _extractCallback2->SetCompleted(&packCur);
+    }
+  }
+  return _extractCallback2->SetCompleted(completeValue);
+  COM_TRY_END
+}
+
+STDMETHODIMP CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
+{
+  COM_TRY_BEGIN
+  return _localProgress->SetRatioInfo(inSize, outSize);
+  COM_TRY_END
+}
+
+void CArchiveExtractCallback::CreateComplexDirectory(const UStringVector &dirPathParts, UString &fullPath)
+{
+  fullPath = _directoryPath;
+  for(int i = 0; i < dirPathParts.Size(); i++)
+  {
+    if (i > 0)
+      fullPath += wchar_t(NFile::NName::kDirDelimiter);
+    fullPath += dirPathParts[i];
+    NFile::NDirectory::MyCreateDirectory(fullPath);
+  }
+}
+
+static UString MakePathNameFromParts(const UStringVector &parts)
+{
+  UString result;
+  for(int i = 0; i < parts.Size(); i++)
+  {
+    if(i != 0)
+      result += wchar_t(NFile::NName::kDirDelimiter);
+    result += parts[i];
+  }
+  return result;
+}
+
+
+HRESULT CArchiveExtractCallback::GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined)
+{
+  filetimeIsDefined = false;
+  NCOM::CPropVariant prop;
+  RINOK(_archiveHandler->GetProperty(index, propID, &prop));
+  if (prop.vt == VT_FILETIME)
+  {
+    filetime = prop.filetime;
+    filetimeIsDefined = (filetime.dwHighDateTime != 0 || filetime.dwLowDateTime != 0);
+  }
+  else if (prop.vt != VT_EMPTY)
+    return E_FAIL;
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode)
+{
+  COM_TRY_BEGIN
+  *outStream = 0;
+  _outFileStream.Release();
+
+  _encrypted = false;
+  _isSplit = false;
+  _curSize = 0;
+
+  UString fullPath;
+
+  RINOK(GetArchiveItemPath(_archiveHandler, index, _itemDefaultName, fullPath));
+  RINOK(IsArchiveItemFolder(_archiveHandler, index, _processedFileInfo.IsDir));
+
+  _filePath = fullPath;
+
+  {
+    NCOM::CPropVariant prop;
+    RINOK(_archiveHandler->GetProperty(index, kpidPosition, &prop));
+    if (prop.vt != VT_EMPTY)
+    {
+      if (prop.vt != VT_UI8)
+        return E_FAIL;
+      _position = prop.uhVal.QuadPart;
+      _isSplit = true;
+    }
+  }
+    
+  RINOK(IsArchiveItemProp(_archiveHandler, index, kpidEncrypted, _encrypted));
+
+  bool newFileSizeDefined;
+  UInt64 newFileSize;
+  {
+    NCOM::CPropVariant prop;
+    RINOK(_archiveHandler->GetProperty(index, kpidSize, &prop));
+    newFileSizeDefined = (prop.vt != VT_EMPTY);
+    if (newFileSizeDefined)
+    {
+      newFileSize = ConvertPropVariantToUInt64(prop);
+      _curSize = newFileSize;
+    }
+  }
+
+  if(askExtractMode == NArchive::NExtract::NAskMode::kExtract)
+  {
+    if (_stdOutMode)
+    {
+      CMyComPtr<ISequentialOutStream> outStreamLoc = new CStdOutFileStream;
+      *outStream = outStreamLoc.Detach();
+      return S_OK;
+    }
+
+    {
+      NCOM::CPropVariant prop;
+      RINOK(_archiveHandler->GetProperty(index, kpidAttrib, &prop));
+      if (prop.vt == VT_EMPTY)
+      {
+        _processedFileInfo.Attributes = _attributesDefault;
+        _processedFileInfo.AttributesAreDefined = false;
+      }
+      else
+      {
+        if (prop.vt != VT_UI4)
+          return E_FAIL;
+        _processedFileInfo.Attributes = prop.ulVal;
+        _processedFileInfo.AttributesAreDefined = true;
+      }
+    }
+
+    RINOK(GetTime(index, kpidCTime, _processedFileInfo.CTime, _processedFileInfo.CTimeDefined));
+    RINOK(GetTime(index, kpidATime, _processedFileInfo.ATime, _processedFileInfo.ATimeDefined));
+    RINOK(GetTime(index, kpidMTime, _processedFileInfo.MTime, _processedFileInfo.MTimeDefined));
+
+    bool isAnti = false;
+    RINOK(IsArchiveItemProp(_archiveHandler, index, kpidIsAnti, isAnti));
+
+    UStringVector pathParts;
+    SplitPathToParts(fullPath, pathParts);
+    
+    if(pathParts.IsEmpty())
+      return E_FAIL;
+    int numRemovePathParts = 0;
+    switch(_pathMode)
+    {
+      case NExtract::NPathMode::kFullPathnames:
+        break;
+      case NExtract::NPathMode::kCurrentPathnames:
+      {
+        numRemovePathParts = _removePathParts.Size();
+        if (pathParts.Size() <= numRemovePathParts)
+          return E_FAIL;
+        for (int i = 0; i < numRemovePathParts; i++)
+          if (_removePathParts[i].CompareNoCase(pathParts[i]) != 0)
+            return E_FAIL;
+        break;
+      }
+      case NExtract::NPathMode::kNoPathnames:
+      {
+        numRemovePathParts = pathParts.Size() - 1;
+        break;
+      }
+    }
+    pathParts.Delete(0, numRemovePathParts);
+    MakeCorrectPath(pathParts);
+    UString processedPath = MakePathNameFromParts(pathParts);
+    if (!isAnti)
+    {
+      if (!_processedFileInfo.IsDir)
+      {
+        if (!pathParts.IsEmpty())
+          pathParts.DeleteBack();
+      }
+    
+      if (!pathParts.IsEmpty())
+      {
+        UString fullPathNew;
+        CreateComplexDirectory(pathParts, fullPathNew);
+        if (_processedFileInfo.IsDir)
+          NFile::NDirectory::SetDirTime(fullPathNew,
+            (WriteCTime && _processedFileInfo.CTimeDefined) ? &_processedFileInfo.CTime : NULL,
+            (WriteATime && _processedFileInfo.ATimeDefined) ? &_processedFileInfo.ATime : NULL,
+            (WriteMTime && _processedFileInfo.MTimeDefined) ? &_processedFileInfo.MTime : &_utcMTimeDefault);
+      }
+    }
+
+
+    UString fullProcessedPath = _directoryPath + processedPath;
+
+    if(_processedFileInfo.IsDir)
+    {
+      _diskFilePath = fullProcessedPath;
+      if (isAnti)
+        NFile::NDirectory::MyRemoveDirectory(_diskFilePath);
+      return S_OK;
+    }
+
+    if (!_isSplit)
+    {
+    NFile::NFind::CFileInfoW fileInfo;
+    if(NFile::NFind::FindFile(fullProcessedPath, fileInfo))
+    {
+      switch(_overwriteMode)
+      {
+        case NExtract::NOverwriteMode::kSkipExisting:
+          return S_OK;
+        case NExtract::NOverwriteMode::kAskBefore:
+        {
+          Int32 overwiteResult;
+          RINOK(_extractCallback2->AskOverwrite(
+              fullProcessedPath, &fileInfo.MTime, &fileInfo.Size, fullPath,
+              _processedFileInfo.MTimeDefined ? &_processedFileInfo.MTime : NULL,
+              newFileSizeDefined ? &newFileSize : NULL,
+              &overwiteResult))
+
+          switch(overwiteResult)
+          {
+            case NOverwriteAnswer::kCancel:
+              return E_ABORT;
+            case NOverwriteAnswer::kNo:
+              return S_OK;
+            case NOverwriteAnswer::kNoToAll:
+              _overwriteMode = NExtract::NOverwriteMode::kSkipExisting;
+              return S_OK;
+            case NOverwriteAnswer::kYesToAll:
+              _overwriteMode = NExtract::NOverwriteMode::kWithoutPrompt;
+              break;
+            case NOverwriteAnswer::kYes:
+              break;
+            case NOverwriteAnswer::kAutoRename:
+              _overwriteMode = NExtract::NOverwriteMode::kAutoRename;
+              break;
+            default:
+              return E_FAIL;
+          }
+        }
+      }
+      if (_overwriteMode == NExtract::NOverwriteMode::kAutoRename)
+      {
+        if (!AutoRenamePath(fullProcessedPath))
+        {
+          UString message = UString(kCantAutoRename) + fullProcessedPath;
+          RINOK(_extractCallback2->MessageError(message));
+          return E_FAIL;
+        }
+      }
+      else if (_overwriteMode == NExtract::NOverwriteMode::kAutoRenameExisting)
+      {
+        UString existPath = fullProcessedPath;
+        if (!AutoRenamePath(existPath))
+        {
+          UString message = kCantAutoRename + fullProcessedPath;
+          RINOK(_extractCallback2->MessageError(message));
+          return E_FAIL;
+        }
+        if(!NFile::NDirectory::MyMoveFile(fullProcessedPath, existPath))
+        {
+          UString message = UString(kCantRenameFile) + fullProcessedPath;
+          RINOK(_extractCallback2->MessageError(message));
+          return E_FAIL;
+        }
+      }
+      else
+        if (!NFile::NDirectory::DeleteFileAlways(fullProcessedPath))
+        {
+          UString message = UString(kCantDeleteOutputFile) +  fullProcessedPath;
+          RINOK(_extractCallback2->MessageError(message));
+          return S_OK;
+          // return E_FAIL;
+        }
+    }
+    }
+    if (!isAnti)
+    {
+      _outFileStreamSpec = new COutFileStream;
+      CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec);
+      if (!_outFileStreamSpec->Open(fullProcessedPath, _isSplit ? OPEN_ALWAYS: CREATE_ALWAYS))
+      {
+        // if (::GetLastError() != ERROR_FILE_EXISTS || !isSplit)
+        {
+          UString message = L"can not open output file " + fullProcessedPath;
+          RINOK(_extractCallback2->MessageError(message));
+          return S_OK;
+        }
+      }
+      if (_isSplit)
+      {
+        RINOK(_outFileStreamSpec->Seek(_position, STREAM_SEEK_SET, NULL));
+      }
+      _outFileStream = outStreamLoc;
+      *outStream = outStreamLoc.Detach();
+    }
+    _diskFilePath = fullProcessedPath;
+  }
+  else
+  {
+    *outStream = NULL;
+  }
+  return S_OK;
+  COM_TRY_END
+}
+
+STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
+{
+  COM_TRY_BEGIN
+  _extractMode = false;
+  switch (askExtractMode)
+  {
+    case NArchive::NExtract::NAskMode::kExtract:
+      _extractMode = true;
+  };
+  return _extractCallback2->PrepareOperation(_filePath, _processedFileInfo.IsDir,
+      askExtractMode, _isSplit ? &_position: 0);
+  COM_TRY_END
+}
+
+STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult)
+{
+  COM_TRY_BEGIN
+  switch(operationResult)
+  {
+    case NArchive::NExtract::NOperationResult::kOK:
+    case NArchive::NExtract::NOperationResult::kUnSupportedMethod:
+    case NArchive::NExtract::NOperationResult::kCRCError:
+    case NArchive::NExtract::NOperationResult::kDataError:
+      break;
+    default:
+      _outFileStream.Release();
+      return E_FAIL;
+  }
+  if (_outFileStream != NULL)
+  {
+    _outFileStreamSpec->SetTime(
+        (WriteCTime && _processedFileInfo.CTimeDefined) ? &_processedFileInfo.CTime : NULL,
+        (WriteATime && _processedFileInfo.ATimeDefined) ? &_processedFileInfo.ATime : NULL,
+        (WriteMTime && _processedFileInfo.MTimeDefined) ? &_processedFileInfo.MTime : &_utcMTimeDefault);
+    _curSize = _outFileStreamSpec->ProcessedSize;
+    RINOK(_outFileStreamSpec->Close());
+    _outFileStream.Release();
+  }
+  UnpackSize += _curSize;
+  if (_processedFileInfo.IsDir)
+    NumFolders++;
+  else
+    NumFiles++;
+
+  if (_extractMode && _processedFileInfo.AttributesAreDefined)
+    NFile::NDirectory::MySetFileAttributes(_diskFilePath, _processedFileInfo.Attributes);
+  RINOK(_extractCallback2->SetOperationResult(operationResult, _encrypted));
+  return S_OK;
+  COM_TRY_END
+}
+
+/*
+STDMETHODIMP CArchiveExtractCallback::GetInStream(
+    const wchar_t *name, ISequentialInStream **inStream)
+{
+  COM_TRY_BEGIN
+  CInFileStream *inFile = new CInFileStream;
+  CMyComPtr<ISequentialInStream> inStreamTemp = inFile;
+  if (!inFile->Open(_srcDirectoryPrefix + name))
+    return ::GetLastError();
+  *inStream = inStreamTemp.Detach();
+  return S_OK;
+  COM_TRY_END
+}
+*/
+
+STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
+{
+  COM_TRY_BEGIN
+  if (!_cryptoGetTextPassword)
+  {
+    RINOK(_extractCallback2.QueryInterface(IID_ICryptoGetTextPassword,
+        &_cryptoGetTextPassword));
+  }
+  return _cryptoGetTextPassword->CryptoGetTextPassword(password);
+  COM_TRY_END
+}
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveExtractCallback.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveExtractCallback.h
new file mode 100644
index 0000000..e895c54
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveExtractCallback.h
@@ -0,0 +1,133 @@
+// ArchiveExtractCallback.h
+
+#ifndef __ARCHIVEEXTRACTCALLBACK_H
+#define __ARCHIVEEXTRACTCALLBACK_H
+
+#include "../../Archive/IArchive.h"
+#include "IFileExtractCallback.h"
+
+#include "Common/MyString.h"
+#include "Common/MyCom.h"
+
+#include "../../Common/FileStreams.h"
+#include "../../Common/ProgressUtils.h"
+#include "../../IPassword.h"
+
+#include "ExtractMode.h"
+
+class CArchiveExtractCallback:
+  public IArchiveExtractCallback,
+  // public IArchiveVolumeExtractCallback,
+  public ICryptoGetTextPassword,
+  public ICompressProgressInfo,
+  public CMyUnknownImp
+{
+public:
+  MY_UNKNOWN_IMP2(ICryptoGetTextPassword, ICompressProgressInfo)
+  // COM_INTERFACE_ENTRY(IArchiveVolumeExtractCallback)
+
+  INTERFACE_IArchiveExtractCallback(;)
+
+  STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
+
+  // IArchiveVolumeExtractCallback
+  // STDMETHOD(GetInStream)(const wchar_t *name, ISequentialInStream **inStream);
+
+  // ICryptoGetTextPassword
+  STDMETHOD(CryptoGetTextPassword)(BSTR *aPassword);
+
+private:
+  CMyComPtr<IInArchive> _archiveHandler;
+  CMyComPtr<IFolderArchiveExtractCallback> _extractCallback2;
+  CMyComPtr<ICompressProgressInfo> _compressProgress;
+  CMyComPtr<ICryptoGetTextPassword> _cryptoGetTextPassword;
+  UString _directoryPath;
+  NExtract::NPathMode::EEnum _pathMode;
+  NExtract::NOverwriteMode::EEnum _overwriteMode;
+
+  UString _filePath;
+  UInt64 _position;
+  bool _isSplit;
+
+  UString _diskFilePath;
+
+  bool _extractMode;
+
+  bool WriteCTime;
+  bool WriteATime;
+  bool WriteMTime;
+
+  bool _encrypted;
+
+  struct CProcessedFileInfo
+  {
+    FILETIME CTime;
+    FILETIME ATime;
+    FILETIME MTime;
+    UInt32 Attributes;
+  
+    bool CTimeDefined;
+    bool ATimeDefined;
+    bool MTimeDefined;
+
+    bool IsDir;
+    bool AttributesAreDefined;
+  } _processedFileInfo;
+
+  UInt64 _curSize;
+  COutFileStream *_outFileStreamSpec;
+  CMyComPtr<ISequentialOutStream> _outFileStream;
+  UStringVector _removePathParts;
+
+  UString _itemDefaultName;
+  FILETIME _utcMTimeDefault;
+  UInt32 _attributesDefault;
+  bool _stdOutMode;
+
+  void CreateComplexDirectory(const UStringVector &dirPathParts, UString &fullPath);
+  HRESULT GetTime(int index, PROPID propID, FILETIME &filetime, bool &filetimeIsDefined);
+public:
+  CArchiveExtractCallback():
+      WriteCTime(true),
+      WriteATime(true),
+      WriteMTime(true),
+      _multiArchives(false)
+  {
+    LocalProgressSpec = new CLocalProgress();
+    _localProgress = LocalProgressSpec;
+  }
+
+  CLocalProgress *LocalProgressSpec;
+  CMyComPtr<ICompressProgressInfo> _localProgress;
+  UInt64 _packTotal;
+  UInt64 _unpTotal;
+
+  bool _multiArchives;
+  UInt64 NumFolders;
+  UInt64 NumFiles;
+  UInt64 UnpackSize;
+  
+  void InitForMulti(bool multiArchives,
+      NExtract::NPathMode::EEnum pathMode,
+      NExtract::NOverwriteMode::EEnum overwriteMode)
+  {
+    _multiArchives = multiArchives; NumFolders = NumFiles = UnpackSize = 0;
+    _pathMode = pathMode;
+    _overwriteMode = overwriteMode;
+  }
+
+  void Init(
+      IInArchive *archiveHandler,
+      IFolderArchiveExtractCallback *extractCallback2,
+      bool stdOutMode,
+      const UString &directoryPath,
+      const UStringVector &removePathParts,
+      const UString &itemDefaultName,
+      const FILETIME &utcMTimeDefault,
+      UInt32 attributesDefault,
+      UInt64 packSize);
+
+  UInt64 _numErrors;
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveName.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveName.cpp
new file mode 100644
index 0000000..9b9a4fe
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveName.cpp
@@ -0,0 +1,46 @@
+// ArchiveName.cpp
+
+#include "StdAfx.h"
+
+#include "Windows/FileFind.h"
+#include "Windows/FileDir.h"
+
+using namespace NWindows;
+
+UString CreateArchiveName(const UString &srcName, bool fromPrev, bool keepName)
+{
+  UString resultName = L"Archive";
+  if (fromPrev)
+  {
+    UString dirPrefix;
+    if (NFile::NDirectory::GetOnlyDirPrefix(srcName, dirPrefix))
+    {
+      if (dirPrefix.Length() > 0)
+        if (dirPrefix[dirPrefix.Length() - 1] == WCHAR_PATH_SEPARATOR)
+        {
+          dirPrefix.Delete(dirPrefix.Length() - 1);
+          NFile::NFind::CFileInfoW fileInfo;
+          if (NFile::NFind::FindFile(dirPrefix, fileInfo))
+            resultName = fileInfo.Name;
+        }
+    }
+  }
+  else
+  {
+    NFile::NFind::CFileInfoW fileInfo;
+    if (!NFile::NFind::FindFile(srcName, fileInfo))
+      return resultName;
+    resultName = fileInfo.Name;
+    if (!fileInfo.IsDir() && !keepName)
+    {
+      int dotPos = resultName.ReverseFind('.');
+      if (dotPos > 0)
+      {
+        UString archiveName2 = resultName.Left(dotPos);
+        if (archiveName2.ReverseFind('.') < 0)
+          resultName = archiveName2;
+      }
+    }
+  }
+  return resultName;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveName.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveName.h
new file mode 100644
index 0000000..9513fb2
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveName.h
@@ -0,0 +1,10 @@
+// ArchiveName.h
+
+#ifndef __ARCHIVENAME_H
+#define __ARCHIVENAME_H
+
+#include "Common/MyString.h"
+
+UString CreateArchiveName(const UString &srcName, bool fromPrev, bool keepName);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp
new file mode 100644
index 0000000..8f289d1
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveOpenCallback.cpp
@@ -0,0 +1,129 @@
+// ArchiveOpenCallback.cpp
+
+#include "StdAfx.h"
+
+#include "ArchiveOpenCallback.h"
+
+#include "Common/StringConvert.h"
+#include "Common/ComTry.h"
+#include "Windows/PropVariant.h"
+
+#include "../../Common/FileStreams.h"
+
+using namespace NWindows;
+
+STDMETHODIMP COpenCallbackImp::SetTotal(const UInt64 *files, const UInt64 *bytes)
+{
+  COM_TRY_BEGIN
+  if (ReOpenCallback)
+    return ReOpenCallback->SetTotal(files, bytes);
+  if (!Callback)
+    return S_OK;
+  return Callback->Open_SetTotal(files, bytes);
+  COM_TRY_END
+}
+
+STDMETHODIMP COpenCallbackImp::SetCompleted(const UInt64 *files, const UInt64 *bytes)
+{
+  COM_TRY_BEGIN
+  if (ReOpenCallback)
+    return ReOpenCallback->SetCompleted(files, bytes);
+  if (!Callback)
+    return S_OK;
+  return Callback->Open_SetCompleted(files, bytes);
+  COM_TRY_END
+}
+  
+STDMETHODIMP COpenCallbackImp::GetProperty(PROPID propID, PROPVARIANT *value)
+{
+  COM_TRY_BEGIN
+  NCOM::CPropVariant prop;
+  if (_subArchiveMode)
+    switch(propID)
+    {
+      case kpidName: prop = _subArchiveName; break;
+    }
+  else
+    switch(propID)
+    {
+      case kpidName:  prop = _fileInfo.Name; break;
+      case kpidIsDir:  prop = _fileInfo.IsDir(); break;
+      case kpidSize:  prop = _fileInfo.Size; break;
+      case kpidAttrib:  prop = (UInt32)_fileInfo.Attrib; break;
+      case kpidCTime:  prop = _fileInfo.CTime; break;
+      case kpidATime:  prop = _fileInfo.ATime; break;
+      case kpidMTime:  prop = _fileInfo.MTime; break;
+    }
+  prop.Detach(value);
+  return S_OK;
+  COM_TRY_END
+}
+
+int COpenCallbackImp::FindName(const UString &name)
+{
+  for (int i = 0; i < FileNames.Size(); i++)
+    if (name.CompareNoCase(FileNames[i]) == 0)
+      return i;
+  return -1;
+}
+
+struct CInFileStreamVol: public CInFileStream
+{
+  UString Name;
+  COpenCallbackImp *OpenCallbackImp;
+  CMyComPtr<IArchiveOpenCallback> OpenCallbackRef;
+  ~CInFileStreamVol()
+  {
+    int index = OpenCallbackImp->FindName(Name);
+    if (index >= 0)
+      OpenCallbackImp->FileNames.Delete(index);
+  }
+};
+
+STDMETHODIMP COpenCallbackImp::GetStream(const wchar_t *name, IInStream **inStream)
+{
+  COM_TRY_BEGIN
+  if (_subArchiveMode)
+    return S_FALSE;
+  if (Callback)
+  {
+    RINOK(Callback->Open_CheckBreak());
+  }
+  *inStream = NULL;
+  UString fullPath = _folderPrefix + name;
+  if (!NFile::NFind::FindFile(fullPath, _fileInfo))
+    return S_FALSE;
+  if (_fileInfo.IsDir())
+    return S_FALSE;
+  CInFileStreamVol *inFile = new CInFileStreamVol;
+  CMyComPtr<IInStream> inStreamTemp = inFile;
+  if (!inFile->Open(fullPath))
+    return ::GetLastError();
+  *inStream = inStreamTemp.Detach();
+  inFile->Name = name;
+  inFile->OpenCallbackImp = this;
+  inFile->OpenCallbackRef = this;
+  FileNames.Add(name);
+  TotalSize += _fileInfo.Size;
+  return S_OK;
+  COM_TRY_END
+}
+
+#ifndef _NO_CRYPTO
+STDMETHODIMP COpenCallbackImp::CryptoGetTextPassword(BSTR *password)
+{
+  COM_TRY_BEGIN
+  if (ReOpenCallback)
+  {
+    CMyComPtr<ICryptoGetTextPassword> getTextPassword;
+    ReOpenCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword);
+    if (getTextPassword)
+      return getTextPassword->CryptoGetTextPassword(password);
+  }
+  if (!Callback)
+    return E_NOTIMPL;
+  return Callback->Open_CryptoGetTextPassword(password);
+  COM_TRY_END
+}
+#endif
+  
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveOpenCallback.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveOpenCallback.h
new file mode 100644
index 0000000..ca44597
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ArchiveOpenCallback.h
@@ -0,0 +1,101 @@
+// ArchiveOpenCallback.h
+
+#ifndef __ARCHIVE_OPEN_CALLBACK_H
+#define __ARCHIVE_OPEN_CALLBACK_H
+
+#include "Common/MyString.h"
+#include "Common/MyCom.h"
+#include "Windows/FileFind.h"
+
+#ifndef _NO_CRYPTO
+#include "../../IPassword.h"
+#endif
+#include "../../Archive/IArchive.h"
+
+#ifdef _NO_CRYPTO
+
+#define INTERFACE_IOpenCallbackUI_Crypto(x)
+
+#else
+
+#define INTERFACE_IOpenCallbackUI_Crypto(x) \
+  virtual HRESULT Open_CryptoGetTextPassword(BSTR *password) x; \
+  virtual HRESULT Open_GetPasswordIfAny(UString &password) x; \
+  virtual bool Open_WasPasswordAsked() x; \
+  virtual void Open_ClearPasswordWasAskedFlag() x; \
+  
+#endif
+
+#define INTERFACE_IOpenCallbackUI(x) \
+  virtual HRESULT Open_CheckBreak() x; \
+  virtual HRESULT Open_SetTotal(const UInt64 *files, const UInt64 *bytes) x; \
+  virtual HRESULT Open_SetCompleted(const UInt64 *files, const UInt64 *bytes) x; \
+  INTERFACE_IOpenCallbackUI_Crypto(x)
+
+struct IOpenCallbackUI
+{
+  INTERFACE_IOpenCallbackUI(=0)
+};
+
+class COpenCallbackImp:
+  public IArchiveOpenCallback,
+  public IArchiveOpenVolumeCallback,
+  public IArchiveOpenSetSubArchiveName,
+  #ifndef _NO_CRYPTO
+  public ICryptoGetTextPassword,
+  #endif
+  public CMyUnknownImp
+{
+public:
+  #ifndef _NO_CRYPTO
+  MY_UNKNOWN_IMP3(
+      IArchiveOpenVolumeCallback,
+      ICryptoGetTextPassword,
+      IArchiveOpenSetSubArchiveName
+      )
+  #else
+  MY_UNKNOWN_IMP2(
+      IArchiveOpenVolumeCallback,
+      IArchiveOpenSetSubArchiveName
+      )
+  #endif
+
+  INTERFACE_IArchiveOpenCallback(;)
+  INTERFACE_IArchiveOpenVolumeCallback(;)
+
+  #ifndef _NO_CRYPTO
+  STDMETHOD(CryptoGetTextPassword)(BSTR *password);
+  #endif
+
+  STDMETHOD(SetSubArchiveName(const wchar_t *name))
+  {
+    _subArchiveMode = true;
+    _subArchiveName = name;
+    return  S_OK;
+  }
+
+private:
+  UString _folderPrefix;
+  NWindows::NFile::NFind::CFileInfoW _fileInfo;
+  bool _subArchiveMode;
+  UString _subArchiveName;
+public:
+  UStringVector FileNames;
+  IOpenCallbackUI *Callback;
+  CMyComPtr<IArchiveOpenCallback> ReOpenCallback;
+  UInt64 TotalSize;
+
+  COpenCallbackImp(): Callback(NULL) {}
+  void Init(const UString &folderPrefix,  const UString &fileName)
+  {
+    _folderPrefix = folderPrefix;
+    if (!NWindows::NFile::NFind::FindFile(_folderPrefix + fileName, _fileInfo))
+      throw 1;
+    FileNames.Clear();
+    _subArchiveMode = false;
+    TotalSize = 0;
+  }
+  int FindName(const UString &name);
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/DefaultName.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/DefaultName.cpp
new file mode 100644
index 0000000..4335e27
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/DefaultName.cpp
@@ -0,0 +1,35 @@
+// DefaultName.cpp
+
+#include "StdAfx.h"
+
+#include "DefaultName.h"
+
+static UString GetDefaultName3(const UString &fileName,
+    const UString &extension, const UString &addSubExtension)
+{
+  int extLength = extension.Length();
+  int fileNameLength = fileName.Length();
+  if (fileNameLength > extLength + 1)
+  {
+    int dotPos = fileNameLength - (extLength + 1);
+    if (fileName[dotPos] == '.')
+      if (extension.CompareNoCase(fileName.Mid(dotPos + 1)) == 0)
+        return fileName.Left(dotPos) + addSubExtension;
+  }
+  int dotPos = fileName.ReverseFind(L'.');
+  if (dotPos > 0)
+    return fileName.Left(dotPos) + addSubExtension;
+
+  if (addSubExtension.IsEmpty())
+    return fileName + L"~";
+  else
+    return fileName + addSubExtension;
+}
+
+UString GetDefaultName2(const UString &fileName,
+    const UString &extension, const UString &addSubExtension)
+{
+  UString name = GetDefaultName3(fileName, extension, addSubExtension);
+  name.TrimRight();
+  return name;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/DefaultName.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/DefaultName.h
new file mode 100644
index 0000000..9764ff8
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/DefaultName.h
@@ -0,0 +1,11 @@
+// DefaultName.h
+
+#ifndef __DEFAULTNAME_H
+#define __DEFAULTNAME_H
+
+#include "Common/MyString.h"
+
+UString GetDefaultName2(const UString &fileName,
+    const UString &extension, const UString &addSubExtension);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/DirItem.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/DirItem.h
new file mode 100644
index 0000000..0f28948
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/DirItem.h
@@ -0,0 +1,68 @@
+// DirItem.h
+
+#ifndef __DIR_ITEM_H
+#define __DIR_ITEM_H
+
+#include "Common/MyString.h"
+#include "Common/Types.h"
+#include "../../Archive/IArchive.h"
+
+struct CDirItem
+{
+  UInt64 Size;
+  FILETIME CTime;
+  FILETIME ATime;
+  FILETIME MTime;
+  UString Name;
+  UInt32 Attrib;
+  int PhyParent;
+  int LogParent;
+  
+  CDirItem(): PhyParent(-1), LogParent(-1) {}
+  bool IsDir() const { return (Attrib & FILE_ATTRIBUTE_DIRECTORY) != 0 ; }
+};
+
+class CDirItems
+{
+  UStringVector Prefixes;
+  CIntVector PhyParents;
+  CIntVector LogParents;
+
+  UString GetPrefixesPath(const CIntVector &parents, int index, const UString &name) const;
+public:
+  CObjectVector<CDirItem> Items;
+
+  int GetNumFolders() const { return Prefixes.Size(); }
+  UString GetPhyPath(int index) const;
+  UString GetLogPath(int index) const;
+
+  int AddPrefix(int phyParent, int logParent, const UString &prefix);
+  void DeleteLastPrefix();
+
+  void EnumerateDirectory(int phyParent, int logParent, const UString &phyPrefix,
+    UStringVector &errorPaths, CRecordVector<DWORD> &errorCodes);
+
+  void EnumerateDirItems2(
+    const UString &phyPrefix,
+    const UString &logPrefix,
+    const UStringVector &filePaths,
+    UStringVector &errorPaths, CRecordVector<DWORD> &errorCodes);
+
+  void ReserveDown();
+};
+
+struct CArcItem
+{
+  UInt64 Size;
+  FILETIME MTime;
+  UString Name;
+  bool IsDir;
+  bool SizeDefined;
+  bool Censored;
+  UInt32 IndexInServer;
+  int TimeType;
+  
+  CArcItem(): IsDir(false), SizeDefined(false), Censored(false), TimeType(-1) {}
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/EnumDirItems.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/EnumDirItems.cpp
new file mode 100644
index 0000000..b83edf2
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/EnumDirItems.cpp
@@ -0,0 +1,367 @@
+// EnumDirItems.cpp
+
+#include "StdAfx.h"
+
+#include <stdio.h>
+
+#include "Common/StringConvert.h"
+#include "Common/Wildcard.h"
+#include "Common/MyCom.h"
+
+#include "EnumDirItems.h"
+
+using namespace NWindows;
+using namespace NFile;
+using namespace NName;
+
+void AddDirFileInfo(int phyParent, int logParent,
+    const NFind::CFileInfoW &fi, CObjectVector<CDirItem> &dirItems)
+{
+  CDirItem di;
+  di.Size = fi.Size;
+  di.CTime = fi.CTime;
+  di.ATime = fi.ATime;
+  di.MTime = fi.MTime;
+  di.Attrib = fi.Attrib;
+  di.PhyParent = phyParent;
+  di.LogParent = logParent;
+  di.Name = fi.Name;
+  dirItems.Add(di);
+}
+
+UString CDirItems::GetPrefixesPath(const CIntVector &parents, int index, const UString &name) const
+{
+  UString path;
+  int len = name.Length();
+  int i;
+  for (i = index; i >= 0; i = parents[i])
+    len += Prefixes[i].Length();
+  int totalLen = len;
+  wchar_t *p = path.GetBuffer(len);
+  p[len] = 0;
+  len -= name.Length();
+  memcpy(p + len, (const wchar_t *)name, name.Length() * sizeof(wchar_t));
+  for (i = index; i >= 0; i = parents[i])
+  {
+    const UString &s = Prefixes[i];
+    len -= s.Length();
+    memcpy(p + len, (const wchar_t *)s, s.Length() * sizeof(wchar_t));
+  }
+  path.ReleaseBuffer(totalLen);
+  return path;
+}
+
+UString CDirItems::GetPhyPath(int index) const
+{
+  const CDirItem &di = Items[index];
+  return GetPrefixesPath(PhyParents, di.PhyParent, di.Name);
+}
+
+UString CDirItems::GetLogPath(int index) const
+{
+  const CDirItem &di = Items[index];
+  return GetPrefixesPath(LogParents, di.LogParent, di.Name);
+}
+
+void CDirItems::ReserveDown()
+{
+  Prefixes.ReserveDown();
+  PhyParents.ReserveDown();
+  LogParents.ReserveDown();
+  Items.ReserveDown();
+}
+
+int CDirItems::AddPrefix(int phyParent, int logParent, const UString &prefix)
+{
+  PhyParents.Add(phyParent);
+  LogParents.Add(logParent);
+  return Prefixes.Add(prefix);
+}
+
+void CDirItems::DeleteLastPrefix()
+{
+  PhyParents.DeleteBack();
+  LogParents.DeleteBack();
+  Prefixes.DeleteBack();
+}
+
+void CDirItems::EnumerateDirectory(int phyParent, int logParent, const UString &phyPrefix,
+    UStringVector &errorPaths, CRecordVector<DWORD> &errorCodes)
+{
+  NFind::CEnumeratorW enumerator(phyPrefix + (wchar_t)kAnyStringWildcard);
+  for (;;)
+  {
+    NFind::CFileInfoW fi;
+    bool found;
+    if (!enumerator.Next(fi, found))
+    {
+      errorCodes.Add(::GetLastError());
+      errorPaths.Add(phyPrefix);
+      return;
+    }
+    if (!found)
+      break;
+    AddDirFileInfo(phyParent, logParent, fi, Items);
+    if (fi.IsDir())
+    {
+      const UString name2 = fi.Name + (wchar_t)kDirDelimiter;
+      int parent = AddPrefix(phyParent, logParent, name2);
+      EnumerateDirectory(parent, parent, phyPrefix + name2, errorPaths, errorCodes);
+    }
+  }
+}
+
+void CDirItems::EnumerateDirItems2(const UString &phyPrefix, const UString &logPrefix,
+    const UStringVector &filePaths, UStringVector &errorPaths, CRecordVector<DWORD> &errorCodes)
+{
+  int phyParent = phyPrefix.IsEmpty() ? -1 : AddPrefix(-1, -1, phyPrefix);
+  int logParent = logPrefix.IsEmpty() ? -1 : AddPrefix(-1, -1, logPrefix);
+
+  for (int i = 0; i < filePaths.Size(); i++)
+  {
+    const UString &filePath = filePaths[i];
+    NFind::CFileInfoW fi;
+    const UString phyPath = phyPrefix + filePath;
+    if (!NFind::FindFile(phyPath, fi))
+    {
+      errorCodes.Add(::GetLastError());
+      errorPaths.Add(phyPath);
+      continue;
+    }
+    int delimiter = filePath.ReverseFind((wchar_t)kDirDelimiter);
+    UString phyPrefixCur;
+    int phyParentCur = phyParent;
+    if (delimiter >= 0)
+    {
+      phyPrefixCur = filePath.Left(delimiter + 1);
+      phyParentCur = AddPrefix(phyParent, logParent, phyPrefixCur);
+    }
+    AddDirFileInfo(phyParentCur, logParent, fi, Items);
+    if (fi.IsDir())
+    {
+      const UString name2 = fi.Name + (wchar_t)kDirDelimiter;
+      int parent = AddPrefix(phyParentCur, logParent, name2);
+      EnumerateDirectory(parent, parent, phyPrefix + phyPrefixCur + name2, errorPaths, errorCodes);
+    }
+  }
+  ReserveDown();
+}
+
+static HRESULT EnumerateDirItems(const NWildcard::CCensorNode &curNode,
+    int phyParent, int logParent, const UString &phyPrefix,
+    const UStringVector &addArchivePrefix,
+    CDirItems &dirItems,
+    bool enterToSubFolders,
+    IEnumDirItemCallback *callback,
+    UStringVector &errorPaths,
+    CRecordVector<DWORD> &errorCodes);
+
+static HRESULT EnumerateDirItems_Spec(const NWildcard::CCensorNode &curNode,
+    int phyParent, int logParent, const UString &curFolderName,
+    const UString &phyPrefix,
+    const UStringVector &addArchivePrefix,
+    CDirItems &dirItems,
+    bool enterToSubFolders,
+    IEnumDirItemCallback *callback,
+    UStringVector &errorPaths,
+    CRecordVector<DWORD> &errorCodes)
+  
+{
+  const UString name2 = curFolderName + (wchar_t)kDirDelimiter;
+  int parent = dirItems.AddPrefix(phyParent, logParent, name2);
+  int numItems = dirItems.Items.Size();
+  HRESULT res = EnumerateDirItems(curNode, parent, parent, phyPrefix + name2,
+    addArchivePrefix, dirItems, enterToSubFolders, callback, errorPaths, errorCodes);
+  if (numItems == dirItems.Items.Size())
+    dirItems.DeleteLastPrefix();
+  return res;
+}
+
+
+static HRESULT EnumerateDirItems(const NWildcard::CCensorNode &curNode,
+    int phyParent, int logParent, const UString &phyPrefix,
+    const UStringVector &addArchivePrefix,  // prefix from curNode
+    CDirItems &dirItems,
+    bool enterToSubFolders,
+    IEnumDirItemCallback *callback,
+    UStringVector &errorPaths,
+    CRecordVector<DWORD> &errorCodes)
+{
+  if (!enterToSubFolders)
+    if (curNode.NeedCheckSubDirs())
+      enterToSubFolders = true;
+  if (callback)
+    RINOK(callback->ScanProgress(dirItems.GetNumFolders(), dirItems.Items.Size(), phyPrefix));
+
+  // try direct_names case at first
+  if (addArchivePrefix.IsEmpty() && !enterToSubFolders)
+  {
+    // check that all names are direct
+    int i;
+    for (i = 0; i < curNode.IncludeItems.Size(); i++)
+    {
+      const NWildcard::CItem &item = curNode.IncludeItems[i];
+      if (item.Recursive || item.PathParts.Size() != 1)
+        break;
+      const UString &name = item.PathParts.Front();
+      if (name.IsEmpty() || DoesNameContainWildCard(name))
+        break;
+    }
+    if (i == curNode.IncludeItems.Size())
+    {
+      // all names are direct (no wildcards)
+      // so we don't need file_system's dir enumerator
+      CRecordVector<bool> needEnterVector;
+      for (i = 0; i < curNode.IncludeItems.Size(); i++)
+      {
+        const NWildcard::CItem &item = curNode.IncludeItems[i];
+        const UString &name = item.PathParts.Front();
+        const UString fullPath = phyPrefix + name;
+        NFind::CFileInfoW fi;
+        if (!NFind::FindFile(fullPath, fi))
+        {
+          errorCodes.Add(::GetLastError());
+          errorPaths.Add(fullPath);
+          continue;
+        }
+        bool isDir = fi.IsDir();
+        if (isDir && !item.ForDir || !isDir && !item.ForFile)
+        {
+          errorCodes.Add((DWORD)E_FAIL);
+          errorPaths.Add(fullPath);
+          continue;
+        }
+        {
+          UStringVector pathParts;
+          pathParts.Add(fi.Name);
+          if (curNode.CheckPathToRoot(false, pathParts, !isDir))
+            continue;
+        }
+        AddDirFileInfo(phyParent, logParent, fi, dirItems.Items);
+        if (!isDir)
+          continue;
+        
+        UStringVector addArchivePrefixNew;
+        const NWildcard::CCensorNode *nextNode = 0;
+        int index = curNode.FindSubNode(name);
+        if (index >= 0)
+        {
+          for (int t = needEnterVector.Size(); t <= index; t++)
+            needEnterVector.Add(true);
+          needEnterVector[index] = false;
+          nextNode = &curNode.SubNodes[index];
+        }
+        else
+        {
+          nextNode = &curNode;
+          addArchivePrefixNew.Add(name); // don't change it to fi.Name. It's for shortnames support
+        }
+
+        RINOK(EnumerateDirItems_Spec(*nextNode, phyParent, logParent, fi.Name, phyPrefix,
+            addArchivePrefixNew, dirItems, true, callback, errorPaths, errorCodes));
+      }
+      for (i = 0; i < curNode.SubNodes.Size(); i++)
+      {
+        if (i < needEnterVector.Size())
+          if (!needEnterVector[i])
+            continue;
+        const NWildcard::CCensorNode &nextNode = curNode.SubNodes[i];
+        const UString fullPath = phyPrefix + nextNode.Name;
+        NFind::CFileInfoW fi;
+        if (!NFind::FindFile(fullPath, fi))
+        {
+          if (!nextNode.AreThereIncludeItems())
+            continue;
+          errorCodes.Add(::GetLastError());
+          errorPaths.Add(fullPath);
+          continue;
+        }
+        if (!fi.IsDir())
+        {
+          errorCodes.Add((DWORD)E_FAIL);
+          errorPaths.Add(fullPath);
+          continue;
+        }
+
+        RINOK(EnumerateDirItems_Spec(nextNode, phyParent, logParent, fi.Name, phyPrefix,
+            UStringVector(), dirItems, false, callback, errorPaths, errorCodes));
+      }
+      return S_OK;
+    }
+  }
+
+
+  NFind::CEnumeratorW enumerator(phyPrefix + wchar_t(kAnyStringWildcard));
+  for (int ttt = 0; ; ttt++)
+  {
+    NFind::CFileInfoW fi;
+    bool found;
+    if (!enumerator.Next(fi, found))
+    {
+      errorCodes.Add(::GetLastError());
+      errorPaths.Add(phyPrefix);
+      break;
+    }
+    if (!found)
+      break;
+
+    if (callback && (ttt & 0xFF) == 0xFF)
+      RINOK(callback->ScanProgress(dirItems.GetNumFolders(), dirItems.Items.Size(), phyPrefix));
+    const UString &name = fi.Name;
+    bool enterToSubFolders2 = enterToSubFolders;
+    UStringVector addArchivePrefixNew = addArchivePrefix;
+    addArchivePrefixNew.Add(name);
+    {
+      UStringVector addArchivePrefixNewTemp(addArchivePrefixNew);
+      if (curNode.CheckPathToRoot(false, addArchivePrefixNewTemp, !fi.IsDir()))
+        continue;
+    }
+    if (curNode.CheckPathToRoot(true, addArchivePrefixNew, !fi.IsDir()))
+    {
+      AddDirFileInfo(phyParent, logParent, fi, dirItems.Items);
+      if (fi.IsDir())
+        enterToSubFolders2 = true;
+    }
+    if (!fi.IsDir())
+      continue;
+
+    const NWildcard::CCensorNode *nextNode = 0;
+    if (addArchivePrefix.IsEmpty())
+    {
+      int index = curNode.FindSubNode(name);
+      if (index >= 0)
+        nextNode = &curNode.SubNodes[index];
+    }
+    if (!enterToSubFolders2 && nextNode == 0)
+      continue;
+
+    addArchivePrefixNew = addArchivePrefix;
+    if (nextNode == 0)
+    {
+      nextNode = &curNode;
+      addArchivePrefixNew.Add(name);
+    }
+
+    RINOK(EnumerateDirItems_Spec(*nextNode, phyParent, logParent, name, phyPrefix,
+        addArchivePrefixNew, dirItems, enterToSubFolders2, callback, errorPaths, errorCodes));
+  }
+  return S_OK;
+}
+
+HRESULT EnumerateItems(
+    const NWildcard::CCensor &censor,
+    CDirItems &dirItems,
+    IEnumDirItemCallback *callback,
+    UStringVector &errorPaths,
+    CRecordVector<DWORD> &errorCodes)
+{
+  for (int i = 0; i < censor.Pairs.Size(); i++)
+  {
+    const NWildcard::CPair &pair = censor.Pairs[i];
+    int phyParent = pair.Prefix.IsEmpty() ? -1 : dirItems.AddPrefix(-1, -1, pair.Prefix);
+    RINOK(EnumerateDirItems(pair.Head, phyParent, -1, pair.Prefix, UStringVector(), dirItems, false,
+        callback, errorPaths, errorCodes));
+  }
+  dirItems.ReserveDown();
+  return S_OK;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/EnumDirItems.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/EnumDirItems.h
new file mode 100644
index 0000000..d0ce950
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/EnumDirItems.h
@@ -0,0 +1,25 @@
+// EnumDirItems.h
+
+#ifndef __ENUM_DIR_ITEMS_H
+#define __ENUM_DIR_ITEMS_H
+
+#include "Common/Wildcard.h"
+#include "Windows/FileFind.h"
+#include "DirItem.h"
+
+void AddDirFileInfo(int phyParent, int logParent,
+    const NWindows::NFile::NFind::CFileInfoW &fi, CObjectVector<CDirItem> &dirItems);
+
+struct IEnumDirItemCallback
+{
+  virtual HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, const wchar_t *path) = 0;
+};
+
+HRESULT EnumerateItems(
+    const NWildcard::CCensor &censor,
+    CDirItems &dirItems,
+    IEnumDirItemCallback *callback,
+    UStringVector &errorPaths,
+    CRecordVector<DWORD> &errorCodes);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ExitCode.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ExitCode.h
new file mode 100644
index 0000000..b6d7d4d
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ExitCode.h
@@ -0,0 +1,27 @@
+// ExitCode.h
+
+#ifndef __EXIT_CODE_H
+#define __EXIT_CODE_H
+
+namespace NExitCode {
+
+enum EEnum {
+
+  kSuccess       = 0,     // Successful operation
+  kWarning       = 1,     // Non fatal error(s) occurred
+  kFatalError    = 2,     // A fatal error occurred
+  // kCRCError      = 3,     // A CRC error occurred when unpacking
+  // kLockedArchive = 4,     // Attempt to modify an archive previously locked
+  // kWriteError    = 5,     // Write to disk error
+  // kOpenError     = 6,     // Open file error
+  kUserError     = 7,     // Command line option error
+  kMemoryError   = 8,     // Not enough memory for operation
+  // kCreateFileError = 9,     // Create file error
+  
+  kUserBreak     = 255   // User stopped the process
+
+};
+
+}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/Extract.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/Extract.cpp
new file mode 100644
index 0000000..3ac497f
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/Extract.cpp
@@ -0,0 +1,218 @@
+// Extract.cpp
+
+#include "StdAfx.h"
+
+#include "Extract.h"
+
+#include "Windows/Defs.h"
+#include "Windows/FileDir.h"
+
+#include "OpenArchive.h"
+#include "SetProperties.h"
+
+using namespace NWindows;
+
+static HRESULT DecompressArchive(
+    IInArchive *archive,
+    UInt64 packSize,
+    const NWildcard::CCensorNode &wildcardCensor,
+    const CExtractOptions &options,
+    IExtractCallbackUI *callback,
+    CArchiveExtractCallback *extractCallbackSpec,
+    UString &errorMessage)
+{
+  CRecordVector<UInt32> realIndices;
+  UInt32 numItems;
+  RINOK(archive->GetNumberOfItems(&numItems));
+
+  for(UInt32 i = 0; i < numItems; i++)
+  {
+    UString filePath;
+    RINOK(GetArchiveItemPath(archive, i, options.DefaultItemName, filePath));
+    bool isFolder;
+    RINOK(IsArchiveItemFolder(archive, i, isFolder));
+    if (!wildcardCensor.CheckPath(filePath, !isFolder))
+      continue;
+    realIndices.Add(i);
+  }
+  if (realIndices.Size() == 0)
+  {
+    callback->ThereAreNoFiles();
+    return S_OK;
+  }
+
+  UStringVector removePathParts;
+
+  UString outDir = options.OutputDir;
+  outDir.Replace(L"*", options.DefaultItemName);
+  #ifdef _WIN32
+  outDir.TrimRight();
+  #endif
+
+  if(!outDir.IsEmpty())
+    if(!NFile::NDirectory::CreateComplexDirectory(outDir))
+    {
+      HRESULT res = ::GetLastError();
+      if (res == S_OK)
+        res = E_FAIL;
+      errorMessage = ((UString)L"Can not create output directory ") + outDir;
+      return res;
+    }
+
+  extractCallbackSpec->Init(
+      archive,
+      callback,
+      options.StdOutMode,
+      outDir,
+      removePathParts,
+      options.DefaultItemName,
+      options.ArchiveFileInfo.MTime,
+      options.ArchiveFileInfo.Attrib,
+      packSize);
+
+  #ifdef COMPRESS_MT
+  RINOK(SetProperties(archive, options.Properties));
+  #endif
+
+  HRESULT result = archive->Extract(&realIndices.Front(),
+    realIndices.Size(), options.TestMode? 1: 0, extractCallbackSpec);
+
+  return callback->ExtractResult(result);
+}
+
+HRESULT DecompressArchives(
+    CCodecs *codecs, const CIntVector &formatIndices,
+    UStringVector &archivePaths, UStringVector &archivePathsFull,
+    const NWildcard::CCensorNode &wildcardCensor,
+    const CExtractOptions &optionsSpec,
+    IOpenCallbackUI *openCallback,
+    IExtractCallbackUI *extractCallback,
+    UString &errorMessage,
+    CDecompressStat &stat)
+{
+  stat.Clear();
+  CExtractOptions options = optionsSpec;
+  int i;
+  UInt64 totalPackSize = 0;
+  CRecordVector<UInt64> archiveSizes;
+  for (i = 0; i < archivePaths.Size(); i++)
+  {
+    const UString &archivePath = archivePaths[i];
+    NFile::NFind::CFileInfoW fi;
+    if (!NFile::NFind::FindFile(archivePath, fi))
+      throw "there is no such archive";
+    if (fi.IsDir())
+      throw "can't decompress folder";
+    archiveSizes.Add(fi.Size);
+    totalPackSize += fi.Size;
+  }
+  CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback;
+  CMyComPtr<IArchiveExtractCallback> ec(extractCallbackSpec);
+  bool multi = (archivePaths.Size() > 1);
+  extractCallbackSpec->InitForMulti(multi, options.PathMode, options.OverwriteMode);
+  if (multi)
+  {
+    RINOK(extractCallback->SetTotal(totalPackSize));
+  }
+  for (i = 0; i < archivePaths.Size(); i++)
+  {
+    const UString &archivePath = archivePaths[i];
+    NFile::NFind::CFileInfoW fi;
+    if (!NFile::NFind::FindFile(archivePath, fi))
+      throw "there is no such archive";
+
+    if (fi.IsDir())
+      throw "there is no such archive";
+
+    options.ArchiveFileInfo = fi;
+
+    #ifndef _NO_CRYPTO
+    openCallback->Open_ClearPasswordWasAskedFlag();
+    #endif
+
+    RINOK(extractCallback->BeforeOpen(archivePath));
+    CArchiveLink archiveLink;
+
+    CIntVector formatIndices2 = formatIndices;
+    #ifndef _SFX
+    if (formatIndices.IsEmpty())
+    {
+      int pos = archivePath.ReverseFind(L'.');
+      if (pos >= 0)
+      {
+        UString s = archivePath.Mid(pos + 1);
+        int index = codecs->FindFormatForExtension(s);
+        if (index >= 0 && s == L"001")
+        {
+          s = archivePath.Left(pos);
+          pos = s.ReverseFind(L'.');
+          if (pos >= 0)
+          {
+            int index2 = codecs->FindFormatForExtension(s.Mid(pos + 1));
+            if (index2 >= 0 && s.CompareNoCase(L"rar") != 0)
+            {
+              formatIndices2.Add(index2);
+              formatIndices2.Add(index);
+            }
+          }
+        }
+      }
+    }
+    #endif
+    HRESULT result = MyOpenArchive(codecs, formatIndices2, archivePath, archiveLink, openCallback);
+    if (result == E_ABORT)
+      return result;
+
+    bool crypted = false;
+    #ifndef _NO_CRYPTO
+    crypted = openCallback->Open_WasPasswordAsked();
+    #endif
+
+    RINOK(extractCallback->OpenResult(archivePath, result, crypted));
+    if (result != S_OK)
+      continue;
+
+    for (int v = 0; v < archiveLink.VolumePaths.Size(); v++)
+    {
+      int index = archivePathsFull.FindInSorted(archiveLink.VolumePaths[v]);
+      if (index >= 0 && index > i)
+      {
+        archivePaths.Delete(index);
+        archivePathsFull.Delete(index);
+        totalPackSize -= archiveSizes[index];
+        archiveSizes.Delete(index);
+      }
+    }
+    if (archiveLink.VolumePaths.Size() != 0)
+    {
+      totalPackSize += archiveLink.VolumesSize;
+      RINOK(extractCallback->SetTotal(totalPackSize));
+    }
+
+    #ifndef _NO_CRYPTO
+    UString password;
+    RINOK(openCallback->Open_GetPasswordIfAny(password));
+    if (!password.IsEmpty())
+    {
+      RINOK(extractCallback->SetPassword(password));
+    }
+    #endif
+
+    options.DefaultItemName = archiveLink.GetDefaultItemName();
+    RINOK(DecompressArchive(
+        archiveLink.GetArchive(),
+        fi.Size + archiveLink.VolumesSize,
+        wildcardCensor, options, extractCallback, extractCallbackSpec, errorMessage));
+    extractCallbackSpec->LocalProgressSpec->InSize += fi.Size +
+        archiveLink.VolumesSize;
+    extractCallbackSpec->LocalProgressSpec->OutSize = extractCallbackSpec->UnpackSize;
+    if (!errorMessage.IsEmpty())
+      return E_FAIL;
+  }
+  stat.NumFolders = extractCallbackSpec->NumFolders;
+  stat.NumFiles = extractCallbackSpec->NumFiles;
+  stat.UnpackSize = extractCallbackSpec->UnpackSize;
+  stat.NumArchives = archivePaths.Size();
+  stat.PackSize = extractCallbackSpec->LocalProgressSpec->InSize;
+  return S_OK;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/Extract.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/Extract.h
new file mode 100644
index 0000000..37add18
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/Extract.h
@@ -0,0 +1,77 @@
+// Extract.h
+
+#ifndef __EXTRACT_H
+#define __EXTRACT_H
+
+#include "Common/Wildcard.h"
+#include "Windows/FileFind.h"
+
+#include "../../Archive/IArchive.h"
+
+#include "ArchiveExtractCallback.h"
+#include "ArchiveOpenCallback.h"
+#include "ExtractMode.h"
+#include "Property.h"
+
+#include "../Common/LoadCodecs.h"
+
+class CExtractOptions
+{
+public:
+  bool StdOutMode;
+  bool TestMode;
+  NExtract::NPathMode::EEnum PathMode;
+
+  UString OutputDir;
+  bool YesToAll;
+  UString DefaultItemName;
+  NWindows::NFile::NFind::CFileInfoW ArchiveFileInfo;
+  
+  // bool ShowDialog;
+  // bool PasswordEnabled;
+  // UString Password;
+  #ifdef COMPRESS_MT
+  CObjectVector<CProperty> Properties;
+  #endif
+
+  NExtract::NOverwriteMode::EEnum OverwriteMode;
+
+  #ifdef EXTERNAL_CODECS
+  CCodecs *Codecs;
+  #endif
+
+  CExtractOptions():
+      StdOutMode(false),
+      YesToAll(false),
+      TestMode(false),
+      PathMode(NExtract::NPathMode::kFullPathnames),
+      OverwriteMode(NExtract::NOverwriteMode::kAskBefore)
+      {}
+
+  /*
+    bool FullPathMode() const { return (ExtractMode == NExtractMode::kTest) ||
+    (ExtractMode == NExtractMode::kFullPath); }
+  */
+};
+
+struct CDecompressStat
+{
+  UInt64 NumArchives;
+  UInt64 UnpackSize;
+  UInt64 PackSize;
+  UInt64 NumFolders;
+  UInt64 NumFiles;
+  void Clear() { NumArchives = PackSize = UnpackSize = NumFolders = NumFiles = 0; }
+};
+
+HRESULT DecompressArchives(
+    CCodecs *codecs, const CIntVector &formatIndices,
+    UStringVector &archivePaths, UStringVector &archivePathsFull,
+    const NWildcard::CCensorNode &wildcardCensor,
+    const CExtractOptions &options,
+    IOpenCallbackUI *openCallback,
+    IExtractCallbackUI *extractCallback,
+    UString &errorMessage,
+    CDecompressStat &stat);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ExtractMode.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ExtractMode.h
new file mode 100644
index 0000000..b448fb3
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ExtractMode.h
@@ -0,0 +1,31 @@
+// ExtractMode.h
+
+#ifndef __EXTRACT_MODE_H
+#define __EXTRACT_MODE_H
+
+namespace NExtract {
+  
+  namespace NPathMode
+  {
+    enum EEnum
+    {
+      kFullPathnames,
+      kCurrentPathnames,
+      kNoPathnames
+    };
+  }
+  
+  namespace NOverwriteMode
+  {
+    enum EEnum
+    {
+      kAskBefore,
+      kWithoutPrompt,
+      kSkipExisting,
+      kAutoRename,
+      kAutoRenameExisting
+    };
+  }
+}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ExtractingFilePath.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ExtractingFilePath.cpp
new file mode 100644
index 0000000..5de388d
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ExtractingFilePath.cpp
@@ -0,0 +1,97 @@
+// ExtractingFilePath.cpp
+
+#include "StdAfx.h"
+#include "ExtractingFilePath.h"
+
+static UString ReplaceIncorrectChars(const UString &s)
+{
+  #ifdef _WIN32
+  UString res;
+  for (int i = 0; i < s.Length(); i++)
+  {
+    wchar_t c = s[i];
+    if (c < 0x20 || c == '*' || c == '?' || c == '<' || c == '>'  || c == '|' || c == ':' || c == '"')
+      c = '_';
+    res += c;
+  }
+  res.TrimRight();
+  return res;
+  #else
+  return s;
+  #endif
+}
+
+#ifdef _WIN32
+static const wchar_t *g_ReservedNames[] =
+{
+  L"CON", L"PRN", L"AUX", L"NUL"
+};
+
+static bool CheckTail(const UString &name, int len)
+{
+  int dotPos = name.Find(L'.');
+  if (dotPos < 0)
+    dotPos = name.Length();
+  UString s = name.Left(dotPos);
+  s.TrimRight();
+  return (s.Length() != len);
+}
+
+static bool CheckNameNum(const UString &name, const wchar_t *reservedName)
+{
+  int len = MyStringLen(reservedName);
+  if (name.Length() <= len)
+    return true;
+  if (name.Left(len).CompareNoCase(reservedName) != 0)
+    return true;
+  wchar_t c = name[len];
+  if (c < L'0' || c > L'9')
+    return true;
+  return CheckTail(name, len + 1);
+}
+
+static bool IsSupportedName(const UString &name)
+{
+  for (int i = 0; i < sizeof(g_ReservedNames) / sizeof(g_ReservedNames[0]); i++)
+  {
+    const wchar_t *reservedName = g_ReservedNames[i];
+    int len = MyStringLen(reservedName);
+    if (name.Length() < len)
+      continue;
+    if (name.Left(len).CompareNoCase(reservedName) != 0)
+      continue;
+    if (!CheckTail(name, len))
+      return false;
+  }
+  if (!CheckNameNum(name, L"COM"))
+    return false;
+  return CheckNameNum(name, L"LPT");
+}
+#endif
+
+static UString GetCorrectFileName(const UString &path)
+{
+  if (path == L".." || path == L".")
+    return UString();
+  return ReplaceIncorrectChars(path);
+}
+
+void MakeCorrectPath(UStringVector &pathParts)
+{
+  for (int i = 0; i < pathParts.Size();)
+  {
+    UString &s = pathParts[i];
+    s = GetCorrectFileName(s);
+    if (s.IsEmpty())
+      pathParts.Delete(i);
+    else
+    {
+      #ifdef _WIN32
+      if (!IsSupportedName(s))
+        s = (UString)L"_" + s;
+      #endif
+      i++;
+    }
+  }
+}
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ExtractingFilePath.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ExtractingFilePath.h
new file mode 100644
index 0000000..a86a6a9
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ExtractingFilePath.h
@@ -0,0 +1,10 @@
+// ExtractingFilePath.h
+
+#ifndef __EXTRACTINGFILEPATH_H
+#define __EXTRACTINGFILEPATH_H
+
+#include "Common/MyString.h"
+
+void MakeCorrectPath(UStringVector &pathParts);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/IFileExtractCallback.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/IFileExtractCallback.h
new file mode 100644
index 0000000..e8dcdce
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/IFileExtractCallback.h
@@ -0,0 +1,46 @@
+// IFileExtractCallback.h
+
+#ifndef __IFILEEXTRACTCALLBACK_H
+#define __IFILEEXTRACTCALLBACK_H
+
+#include "Common/MyString.h"
+#include "../../IDecl.h"
+
+namespace NOverwriteAnswer
+{
+  enum EEnum
+  {
+    kYes,
+    kYesToAll,
+    kNo,
+    kNoToAll,
+    kAutoRename,
+    kCancel
+  };
+}
+
+DECL_INTERFACE_SUB(IFolderArchiveExtractCallback, IProgress, 0x01, 0x07)
+{
+public:
+  STDMETHOD(AskOverwrite)(
+      const wchar_t *existName, const FILETIME *existTime, const UInt64 *existSize,
+      const wchar_t *newName, const FILETIME *newTime, const UInt64 *newSize,
+      Int32 *answer) PURE;
+  STDMETHOD(PrepareOperation)(const wchar_t *name, bool isFolder, Int32 askExtractMode, const UInt64 *position) PURE;
+  STDMETHOD(MessageError)(const wchar_t *message) PURE;
+  STDMETHOD(SetOperationResult)(Int32 operationResult, bool encrypted) PURE;
+};
+
+struct IExtractCallbackUI: IFolderArchiveExtractCallback
+{
+  virtual HRESULT BeforeOpen(const wchar_t *name) = 0;
+  virtual HRESULT OpenResult(const wchar_t *name, HRESULT result, bool encrypted) = 0;
+  virtual HRESULT ThereAreNoFiles() = 0;
+  virtual HRESULT ExtractResult(HRESULT result) = 0;
+
+  #ifndef _NO_CRYPTO
+  virtual HRESULT SetPassword(const UString &password) = 0;
+  #endif
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/LoadCodecs.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/LoadCodecs.cpp
new file mode 100644
index 0000000..4b5639e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/LoadCodecs.cpp
@@ -0,0 +1,674 @@
+// LoadCodecs.cpp
+
+#include "StdAfx.h"
+
+#include "LoadCodecs.h"
+
+#include "../../../Common/MyCom.h"
+#ifdef NEW_FOLDER_INTERFACE
+#include "../../../Common/StringToInt.h"
+#endif
+#include "../../../Windows/PropVariant.h"
+
+#include "../../ICoder.h"
+#include "../../Common/RegisterArc.h"
+
+#ifdef EXTERNAL_CODECS
+#include "../../../Windows/FileFind.h"
+#include "../../../Windows/DLL.h"
+#ifdef NEW_FOLDER_INTERFACE
+#include "../../../Windows/ResourceString.h"
+static const UINT kIconTypesResId = 100;
+#endif
+
+#ifdef _WIN32
+#include "Windows/Registry.h"
+#endif
+
+using namespace NWindows;
+using namespace NFile;
+
+#ifdef _WIN32
+extern HINSTANCE g_hInstance;
+#endif
+
+static CSysString GetLibraryFolderPrefix()
+{
+  #ifdef _WIN32
+  TCHAR fullPath[MAX_PATH + 1];
+  ::GetModuleFileName(g_hInstance, fullPath, MAX_PATH);
+  CSysString path = fullPath;
+  int pos = path.ReverseFind(TEXT(CHAR_PATH_SEPARATOR));
+  return path.Left(pos + 1);
+  #else
+  return CSysString(); // FIX IT
+  #endif
+}
+
+#define kCodecsFolderName TEXT("Codecs")
+#define kFormatsFolderName TEXT("Formats")
+static const TCHAR *kMainDll = TEXT("7z.dll");
+
+#ifdef _WIN32
+static LPCTSTR kRegistryPath = TEXT("Software") TEXT(STRING_PATH_SEPARATOR) TEXT("7-zip");
+static LPCTSTR kProgramPathValue = TEXT("Path");
+static bool ReadPathFromRegistry(HKEY baseKey, CSysString &path)
+{
+  NRegistry::CKey key;
+  if(key.Open(baseKey, kRegistryPath, KEY_READ) == ERROR_SUCCESS)
+    if (key.QueryValue(kProgramPathValue, path) == ERROR_SUCCESS)
+    {
+      NName::NormalizeDirPathPrefix(path);
+      return true;
+    }
+  return false;
+}
+
+#endif
+
+CSysString GetBaseFolderPrefixFromRegistry()
+{
+  CSysString moduleFolderPrefix = GetLibraryFolderPrefix();
+  NFind::CFileInfo fi;
+  if (NFind::FindFile(moduleFolderPrefix + kMainDll, fi))
+    if (!fi.IsDir())
+      return moduleFolderPrefix;
+  if (NFind::FindFile(moduleFolderPrefix + kCodecsFolderName, fi))
+    if (fi.IsDir())
+      return moduleFolderPrefix;
+  if (NFind::FindFile(moduleFolderPrefix + kFormatsFolderName, fi))
+    if (fi.IsDir())
+      return moduleFolderPrefix;
+  #ifdef _WIN32
+  CSysString path;
+  if (ReadPathFromRegistry(HKEY_CURRENT_USER, path))
+    return path;
+  if (ReadPathFromRegistry(HKEY_LOCAL_MACHINE, path))
+    return path;
+  #endif
+  return moduleFolderPrefix;
+}
+
+typedef UInt32 (WINAPI *GetNumberOfMethodsFunc)(UInt32 *numMethods);
+typedef UInt32 (WINAPI *GetNumberOfFormatsFunc)(UInt32 *numFormats);
+typedef UInt32 (WINAPI *GetHandlerPropertyFunc)(PROPID propID, PROPVARIANT *value);
+typedef UInt32 (WINAPI *GetHandlerPropertyFunc2)(UInt32 index, PROPID propID, PROPVARIANT *value);
+typedef UInt32 (WINAPI *CreateObjectFunc)(const GUID *clsID, const GUID *iid, void **outObject);
+typedef UInt32 (WINAPI *SetLargePageModeFunc)();
+
+
+static HRESULT GetCoderClass(GetMethodPropertyFunc getMethodProperty, UInt32 index,
+    PROPID propId, CLSID &clsId, bool &isAssigned)
+{
+  NWindows::NCOM::CPropVariant prop;
+  isAssigned = false;
+  RINOK(getMethodProperty(index, propId, &prop));
+  if (prop.vt == VT_BSTR)
+  {
+    isAssigned = true;
+    clsId = *(const GUID *)prop.bstrVal;
+  }
+  else if (prop.vt != VT_EMPTY)
+    return E_FAIL;
+  return S_OK;
+}
+
+HRESULT CCodecs::LoadCodecs()
+{
+  CCodecLib &lib = Libs.Back();
+  lib.GetMethodProperty = (GetMethodPropertyFunc)lib.Lib.GetProcAddress("GetMethodProperty");
+  if (lib.GetMethodProperty == NULL)
+    return S_OK;
+
+  UInt32 numMethods = 1;
+  GetNumberOfMethodsFunc getNumberOfMethodsFunc = (GetNumberOfMethodsFunc)lib.Lib.GetProcAddress("GetNumberOfMethods");
+  if (getNumberOfMethodsFunc != NULL)
+  {
+    RINOK(getNumberOfMethodsFunc(&numMethods));
+  }
+
+  for(UInt32 i = 0; i < numMethods; i++)
+  {
+    CDllCodecInfo info;
+    info.LibIndex = Libs.Size() - 1;
+    info.CodecIndex = i;
+
+    RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kEncoder, info.Encoder, info.EncoderIsAssigned));
+    RINOK(GetCoderClass(lib.GetMethodProperty, i, NMethodPropID::kDecoder, info.Decoder, info.DecoderIsAssigned));
+
+    Codecs.Add(info);
+  }
+  return S_OK;
+}
+
+static HRESULT ReadProp(
+    GetHandlerPropertyFunc getProp,
+    GetHandlerPropertyFunc2 getProp2,
+    UInt32 index, PROPID propID, NCOM::CPropVariant &prop)
+{
+  if (getProp2)
+    return getProp2(index, propID, &prop);;
+  return getProp(propID, &prop);
+}
+
+static HRESULT ReadBoolProp(
+    GetHandlerPropertyFunc getProp,
+    GetHandlerPropertyFunc2 getProp2,
+    UInt32 index, PROPID propID, bool &res)
+{
+  NCOM::CPropVariant prop;
+  RINOK(ReadProp(getProp, getProp2, index, propID, prop));
+  if (prop.vt == VT_BOOL)
+    res = VARIANT_BOOLToBool(prop.boolVal);
+  else if (prop.vt != VT_EMPTY)
+    return E_FAIL;
+  return S_OK;
+}
+
+static HRESULT ReadStringProp(
+    GetHandlerPropertyFunc getProp,
+    GetHandlerPropertyFunc2 getProp2,
+    UInt32 index, PROPID propID, UString &res)
+{
+  NCOM::CPropVariant prop;
+  RINOK(ReadProp(getProp, getProp2, index, propID, prop));
+  if (prop.vt == VT_BSTR)
+    res = prop.bstrVal;
+  else if (prop.vt != VT_EMPTY)
+    return E_FAIL;
+  return S_OK;
+}
+
+#endif
+
+static const unsigned int kNumArcsMax = 32;
+static unsigned int g_NumArcs = 0;
+static const CArcInfo *g_Arcs[kNumArcsMax];
+void RegisterArc(const CArcInfo *arcInfo)
+{
+  if (g_NumArcs < kNumArcsMax)
+    g_Arcs[g_NumArcs++] = arcInfo;
+}
+
+static void SplitString(const UString &srcString, UStringVector &destStrings)
+{
+  destStrings.Clear();
+  UString s;
+  int len = srcString.Length();
+  if (len == 0)
+    return;
+  for (int i = 0; i < len; i++)
+  {
+    wchar_t c = srcString[i];
+    if (c == L' ')
+    {
+      if (!s.IsEmpty())
+      {
+        destStrings.Add(s);
+        s.Empty();
+      }
+    }
+    else
+      s += c;
+  }
+  if (!s.IsEmpty())
+    destStrings.Add(s);
+}
+
+void CArcInfoEx::AddExts(const wchar_t* ext, const wchar_t* addExt)
+{
+  UStringVector exts, addExts;
+  SplitString(ext, exts);
+  if (addExt != 0)
+    SplitString(addExt, addExts);
+  for (int i = 0; i < exts.Size(); i++)
+  {
+    CArcExtInfo extInfo;
+    extInfo.Ext = exts[i];
+    if (i < addExts.Size())
+    {
+      extInfo.AddExt = addExts[i];
+      if (extInfo.AddExt == L"*")
+        extInfo.AddExt.Empty();
+    }
+    Exts.Add(extInfo);
+  }
+}
+
+#ifdef EXTERNAL_CODECS
+
+HRESULT CCodecs::LoadFormats()
+{
+  const NDLL::CLibrary &lib = Libs.Back().Lib;
+  GetHandlerPropertyFunc getProp = 0;
+  GetHandlerPropertyFunc2 getProp2 = (GetHandlerPropertyFunc2)
+      lib.GetProcAddress("GetHandlerProperty2");
+  if (getProp2 == NULL)
+  {
+    getProp = (GetHandlerPropertyFunc)
+        lib.GetProcAddress("GetHandlerProperty");
+    if (getProp == NULL)
+      return S_OK;
+  }
+
+  UInt32 numFormats = 1;
+  GetNumberOfFormatsFunc getNumberOfFormats = (GetNumberOfFormatsFunc)
+    lib.GetProcAddress("GetNumberOfFormats");
+  if (getNumberOfFormats != NULL)
+  {
+    RINOK(getNumberOfFormats(&numFormats));
+  }
+  if (getProp2 == NULL)
+    numFormats = 1;
+
+  for(UInt32 i = 0; i < numFormats; i++)
+  {
+    CArcInfoEx item;
+    item.LibIndex = Libs.Size() - 1;
+    item.FormatIndex = i;
+
+    RINOK(ReadStringProp(getProp, getProp2, i, NArchive::kName, item.Name));
+
+    NCOM::CPropVariant prop;
+    if (ReadProp(getProp, getProp2, i, NArchive::kClassID, prop) != S_OK)
+      continue;
+    if (prop.vt != VT_BSTR)
+      continue;
+    item.ClassID = *(const GUID *)prop.bstrVal;
+    prop.Clear();
+
+    UString ext, addExt;
+    RINOK(ReadStringProp(getProp, getProp2, i, NArchive::kExtension, ext));
+    RINOK(ReadStringProp(getProp, getProp2, i, NArchive::kAddExtension, addExt));
+    item.AddExts(ext, addExt);
+
+    ReadBoolProp(getProp, getProp2, i, NArchive::kUpdate, item.UpdateEnabled);
+    if (item.UpdateEnabled)
+      ReadBoolProp(getProp, getProp2, i, NArchive::kKeepName, item.KeepName);
+    
+    if (ReadProp(getProp, getProp2, i, NArchive::kStartSignature, prop) == S_OK)
+      if (prop.vt == VT_BSTR)
+      {
+        UINT len = ::SysStringByteLen(prop.bstrVal);
+        item.StartSignature.SetCapacity(len);
+        memmove(item.StartSignature, prop.bstrVal, len);
+      }
+    Formats.Add(item);
+  }
+  return S_OK;
+}
+
+#ifdef NEW_FOLDER_INTERFACE
+void CCodecLib::LoadIcons()
+{
+  UString iconTypes = MyLoadStringW((HMODULE)Lib, kIconTypesResId);
+  UStringVector pairs;
+  SplitString(iconTypes, pairs);
+  for (int i = 0; i < pairs.Size(); i++)
+  {
+    const UString &s = pairs[i];
+    int pos = s.Find(L':');
+    if (pos < 0)
+      continue;
+    CIconPair iconPair;
+    const wchar_t *end;
+    UString num = s.Mid(pos + 1);
+    iconPair.IconIndex = (UInt32)ConvertStringToUInt64(num, &end);
+    if (*end != L'\0')
+      continue;
+    iconPair.Ext = s.Left(pos);
+    IconPairs.Add(iconPair);
+  }
+}
+
+int CCodecLib::FindIconIndex(const UString &ext) const
+{
+  for (int i = 0; i < IconPairs.Size(); i++)
+  {
+    const CIconPair &pair = IconPairs[i];
+    if (ext.CompareNoCase(pair.Ext) == 0)
+      return pair.IconIndex;
+  }
+  return -1;
+}
+#endif
+
+#ifdef _7ZIP_LARGE_PAGES
+extern "C"
+{
+  extern SIZE_T g_LargePageSize;
+}
+#endif
+
+HRESULT CCodecs::LoadDll(const CSysString &dllPath)
+{
+  {
+    NDLL::CLibrary library;
+    if (!library.LoadEx(dllPath, LOAD_LIBRARY_AS_DATAFILE))
+      return S_OK;
+  }
+  Libs.Add(CCodecLib());
+  CCodecLib &lib = Libs.Back();
+  #ifdef NEW_FOLDER_INTERFACE
+  lib.Path = dllPath;
+  #endif
+  bool used = false;
+  HRESULT res = S_OK;
+  if (lib.Lib.Load(dllPath))
+  {
+    #ifdef NEW_FOLDER_INTERFACE
+    lib.LoadIcons();
+    #endif
+
+    #ifdef _7ZIP_LARGE_PAGES
+    if (g_LargePageSize != 0)
+    {
+      SetLargePageModeFunc setLargePageMode = (SetLargePageModeFunc)lib.Lib.GetProcAddress("SetLargePageMode");
+      if (setLargePageMode != 0)
+        setLargePageMode();
+    }
+    #endif
+
+    lib.CreateObject = (CreateObjectFunc)lib.Lib.GetProcAddress("CreateObject");
+    if (lib.CreateObject != 0)
+    {
+      int startSize = Codecs.Size();
+      res = LoadCodecs();
+      used = (Codecs.Size() != startSize);
+      if (res == S_OK)
+      {
+        startSize = Formats.Size();
+        res = LoadFormats();
+        used = used || (Formats.Size() != startSize);
+      }
+    }
+  }
+  if (!used)
+    Libs.DeleteBack();
+  return res;
+}
+
+HRESULT CCodecs::LoadDllsFromFolder(const CSysString &folderPrefix)
+{
+  NFile::NFind::CEnumerator enumerator(folderPrefix + CSysString(TEXT("*")));
+  NFile::NFind::CFileInfo fi;
+  while (enumerator.Next(fi))
+  {
+    if (fi.IsDir())
+      continue;
+    RINOK(LoadDll(folderPrefix + fi.Name));
+  }
+  return S_OK;
+}
+
+#endif
+
+#ifndef _SFX
+static inline void SetBuffer(CByteBuffer &bb, const Byte *data, int size)
+{
+  bb.SetCapacity(size);
+  memmove((Byte *)bb, data, size);
+}
+#endif
+
+HRESULT CCodecs::Load()
+{
+  Formats.Clear();
+  #ifdef EXTERNAL_CODECS
+  Codecs.Clear();
+  #endif
+  for (UInt32 i = 0; i < g_NumArcs; i++)
+  {
+    const CArcInfo &arc = *g_Arcs[i];
+    CArcInfoEx item;
+    item.Name = arc.Name;
+    item.CreateInArchive = arc.CreateInArchive;
+    item.CreateOutArchive = arc.CreateOutArchive;
+    item.AddExts(arc.Ext, arc.AddExt);
+    item.UpdateEnabled = (arc.CreateOutArchive != 0);
+    item.KeepName = arc.KeepName;
+
+    #ifndef _SFX
+    SetBuffer(item.StartSignature, arc.Signature, arc.SignatureSize);
+    #endif
+    Formats.Add(item);
+  }
+  #ifdef EXTERNAL_CODECS
+  const CSysString baseFolder = GetBaseFolderPrefixFromRegistry();
+  RINOK(LoadDll(baseFolder + kMainDll));
+  RINOK(LoadDllsFromFolder(baseFolder + kCodecsFolderName TEXT(STRING_PATH_SEPARATOR)));
+  RINOK(LoadDllsFromFolder(baseFolder + kFormatsFolderName TEXT(STRING_PATH_SEPARATOR)));
+  #endif
+  return S_OK;
+}
+
+#ifndef _SFX
+
+int CCodecs::FindFormatForArchiveName(const UString &arcPath) const
+{
+  int slashPos1 = arcPath.ReverseFind(WCHAR_PATH_SEPARATOR);
+  int slashPos2 = arcPath.ReverseFind(L'.');
+  int dotPos = arcPath.ReverseFind(L'.');
+  if (dotPos < 0 || dotPos < slashPos1 || dotPos < slashPos2)
+    return -1;
+  UString ext = arcPath.Mid(dotPos + 1);
+  for (int i = 0; i < Formats.Size(); i++)
+  {
+    const CArcInfoEx &arc = Formats[i];
+    if (!arc.UpdateEnabled)
+      continue;
+    // if (arc.FindExtension(ext) >= 0)
+    UString mainExt = arc.GetMainExt();
+    if (!mainExt.IsEmpty() && ext.CompareNoCase(mainExt) == 0)
+      return i;
+  }
+  return -1;
+}
+
+int CCodecs::FindFormatForExtension(const UString &ext) const
+{
+  if (ext.IsEmpty())
+    return -1;
+  for (int i = 0; i < Formats.Size(); i++)
+    if (Formats[i].FindExtension(ext) >= 0)
+      return i;
+  return -1;
+}
+
+int CCodecs::FindFormatForArchiveType(const UString &arcType) const
+{
+  for (int i = 0; i < Formats.Size(); i++)
+    if (Formats[i].Name.CompareNoCase(arcType) == 0)
+      return i;
+  return -1;
+}
+
+bool CCodecs::FindFormatForArchiveType(const UString &arcType, CIntVector &formatIndices) const
+{
+  formatIndices.Clear();
+  for (int pos = 0; pos < arcType.Length();)
+  {
+    int pos2 = arcType.Find('.', pos);
+    if (pos2 < 0)
+      pos2 = arcType.Length();
+    const UString name = arcType.Mid(pos, pos2 - pos);
+    int index = FindFormatForArchiveType(name);
+    if (index < 0 && name != L"*")
+    {
+      formatIndices.Clear();
+      return false;
+    }
+    formatIndices.Add(index);
+    pos = pos2 + 1;
+  }
+  return true;
+}
+
+#endif
+
+#ifdef EXTERNAL_CODECS
+
+#ifdef EXPORT_CODECS
+extern unsigned int g_NumCodecs;
+STDAPI CreateCoder2(bool encode, UInt32 index, const GUID *iid, void **outObject);
+STDAPI GetMethodProperty(UInt32 codecIndex, PROPID propID, PROPVARIANT *value);
+// STDAPI GetNumberOfMethods(UInt32 *numCodecs);
+#endif
+
+STDMETHODIMP CCodecs::GetNumberOfMethods(UInt32 *numMethods)
+{
+  *numMethods =
+      #ifdef EXPORT_CODECS
+      g_NumCodecs +
+      #endif
+      Codecs.Size();
+  return S_OK;
+}
+
+STDMETHODIMP CCodecs::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
+{
+  #ifdef EXPORT_CODECS
+  if (index < g_NumCodecs)
+    return GetMethodProperty(index, propID, value);
+  #endif
+
+  const CDllCodecInfo &ci = Codecs[index
+      #ifdef EXPORT_CODECS
+      - g_NumCodecs
+      #endif
+      ];
+
+  if (propID == NMethodPropID::kDecoderIsAssigned)
+  {
+    NWindows::NCOM::CPropVariant propVariant;
+    propVariant = ci.DecoderIsAssigned;
+    propVariant.Detach(value);
+    return S_OK;
+  }
+  if (propID == NMethodPropID::kEncoderIsAssigned)
+  {
+    NWindows::NCOM::CPropVariant propVariant;
+    propVariant = ci.EncoderIsAssigned;
+    propVariant.Detach(value);
+    return S_OK;
+  }
+  return Libs[ci.LibIndex].GetMethodProperty(ci.CodecIndex, propID, value);
+}
+
+STDMETHODIMP CCodecs::CreateDecoder(UInt32 index, const GUID *iid, void **coder)
+{
+  #ifdef EXPORT_CODECS
+  if (index < g_NumCodecs)
+    return CreateCoder2(false, index, iid, coder);
+  #endif
+  const CDllCodecInfo &ci = Codecs[index
+      #ifdef EXPORT_CODECS
+      - g_NumCodecs
+      #endif
+      ];
+  if (ci.DecoderIsAssigned)
+    return Libs[ci.LibIndex].CreateObject(&ci.Decoder, iid, (void **)coder);
+  return S_OK;
+}
+
+STDMETHODIMP CCodecs::CreateEncoder(UInt32 index, const GUID *iid, void **coder)
+{
+  #ifdef EXPORT_CODECS
+  if (index < g_NumCodecs)
+    return CreateCoder2(true, index, iid, coder);
+  #endif
+  const CDllCodecInfo &ci = Codecs[index
+      #ifdef EXPORT_CODECS
+      - g_NumCodecs
+      #endif
+      ];
+  if (ci.EncoderIsAssigned)
+    return Libs[ci.LibIndex].CreateObject(&ci.Encoder, iid, (void **)coder);
+  return S_OK;
+}
+
+HRESULT CCodecs::CreateCoder(const UString &name, bool encode, CMyComPtr<ICompressCoder> &coder) const
+{
+  for (int i = 0; i < Codecs.Size(); i++)
+  {
+    const CDllCodecInfo &codec = Codecs[i];
+    if (encode && !codec.EncoderIsAssigned || !encode && !codec.DecoderIsAssigned)
+      continue;
+    const CCodecLib &lib = Libs[codec.LibIndex];
+    UString res;
+    NWindows::NCOM::CPropVariant prop;
+    RINOK(lib.GetMethodProperty(codec.CodecIndex, NMethodPropID::kName, &prop));
+    if (prop.vt == VT_BSTR)
+      res = prop.bstrVal;
+    else if (prop.vt != VT_EMPTY)
+      continue;
+    if (name.CompareNoCase(res) == 0)
+      return lib.CreateObject(encode ? &codec.Encoder : &codec.Decoder, &IID_ICompressCoder, (void **)&coder);
+  }
+  return CLASS_E_CLASSNOTAVAILABLE;
+}
+
+int CCodecs::GetCodecLibIndex(UInt32 index)
+{
+  #ifdef EXPORT_CODECS
+  if (index < g_NumCodecs)
+    return -1;
+  #endif
+  #ifdef EXTERNAL_CODECS
+  const CDllCodecInfo &ci = Codecs[index
+      #ifdef EXPORT_CODECS
+      - g_NumCodecs
+      #endif
+      ];
+  return ci.LibIndex;
+  #else
+  return -1;
+  #endif
+}
+
+bool CCodecs::GetCodecEncoderIsAssigned(UInt32 index)
+{
+  #ifdef EXPORT_CODECS
+  if (index < g_NumCodecs)
+  {
+    NWindows::NCOM::CPropVariant prop;
+    if (GetProperty(index, NMethodPropID::kEncoder, &prop) == S_OK)
+      if (prop.vt != VT_EMPTY)
+        return true;
+    return false;
+  }
+  #endif
+  #ifdef EXTERNAL_CODECS
+  const CDllCodecInfo &ci = Codecs[index
+      #ifdef EXPORT_CODECS
+      - g_NumCodecs
+      #endif
+      ];
+  return ci.EncoderIsAssigned;
+  #else
+  return false;
+  #endif
+}
+
+HRESULT CCodecs::GetCodecId(UInt32 index, UInt64 &id)
+{
+  UString s;
+  NWindows::NCOM::CPropVariant prop;
+  RINOK(GetProperty(index, NMethodPropID::kID, &prop));
+  if (prop.vt != VT_UI8)
+    return E_INVALIDARG;
+  id = prop.uhVal.QuadPart;
+  return S_OK;
+}
+
+UString CCodecs::GetCodecName(UInt32 index)
+{
+  UString s;
+  NWindows::NCOM::CPropVariant prop;
+  if (GetProperty(index, NMethodPropID::kName, &prop) == S_OK)
+    if (prop.vt == VT_BSTR)
+      s = prop.bstrVal;
+  return s;
+}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/LoadCodecs.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/LoadCodecs.h
new file mode 100644
index 0000000..71de2ff
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/LoadCodecs.h
@@ -0,0 +1,220 @@
+// LoadCodecs.h
+
+#ifndef __LOADCODECS_H
+#define __LOADCODECS_H
+
+#include "../../../Common/Types.h"
+#include "../../../Common/MyCom.h"
+#include "../../../Common/MyString.h"
+#include "../../../Common/Buffer.h"
+#include "../../ICoder.h"
+
+#ifdef EXTERNAL_CODECS
+#include "../../../Windows/DLL.h"
+#endif
+
+struct CDllCodecInfo
+{
+  CLSID Encoder;
+  CLSID Decoder;
+  bool EncoderIsAssigned;
+  bool DecoderIsAssigned;
+  int LibIndex;
+  UInt32 CodecIndex;
+};
+
+#include "../../Archive/IArchive.h"
+
+typedef IInArchive * (*CreateInArchiveP)();
+typedef IOutArchive * (*CreateOutArchiveP)();
+
+struct CArcExtInfo
+{
+  UString Ext;
+  UString AddExt;
+  CArcExtInfo() {}
+  CArcExtInfo(const UString &ext): Ext(ext) {}
+  CArcExtInfo(const UString &ext, const UString &addExt): Ext(ext), AddExt(addExt) {}
+};
+
+
+struct CArcInfoEx
+{
+  #ifdef EXTERNAL_CODECS
+  int LibIndex;
+  UInt32 FormatIndex;
+  CLSID ClassID;
+  #endif
+  bool UpdateEnabled;
+  CreateInArchiveP CreateInArchive;
+  CreateOutArchiveP CreateOutArchive;
+  UString Name;
+  CObjectVector<CArcExtInfo> Exts;
+  #ifndef _SFX
+  CByteBuffer StartSignature;
+  // CByteBuffer FinishSignature;
+  #ifdef NEW_FOLDER_INTERFACE
+  UStringVector AssociateExts;
+  #endif
+  #endif
+  bool KeepName;
+  UString GetMainExt() const
+  {
+    if (Exts.IsEmpty())
+      return UString();
+    return Exts[0].Ext;
+  }
+  int FindExtension(const UString &ext) const
+  {
+    for (int i = 0; i < Exts.Size(); i++)
+      if (ext.CompareNoCase(Exts[i].Ext) == 0)
+        return i;
+    return -1;
+  }
+  UString GetAllExtensions() const
+  {
+    UString s;
+    for (int i = 0; i < Exts.Size(); i++)
+    {
+      if (i > 0)
+        s += ' ';
+      s += Exts[i].Ext;
+    }
+    return s;
+  }
+
+  void AddExts(const wchar_t* ext, const wchar_t* addExt);
+
+  CArcInfoEx():
+    #ifdef EXTERNAL_CODECS
+    LibIndex(-1),
+    #endif
+    UpdateEnabled(false),
+    CreateInArchive(0), CreateOutArchive(0),
+    KeepName(false)
+    #ifndef _SFX
+    #endif
+  {}
+};
+
+#ifdef EXTERNAL_CODECS
+typedef UInt32 (WINAPI *GetMethodPropertyFunc)(UInt32 index, PROPID propID, PROPVARIANT *value);
+typedef UInt32 (WINAPI *CreateObjectFunc)(const GUID *clsID, const GUID *interfaceID, void **outObject);
+
+
+struct CCodecLib
+{
+  NWindows::NDLL::CLibrary Lib;
+  GetMethodPropertyFunc GetMethodProperty;
+  CreateObjectFunc CreateObject;
+  #ifdef NEW_FOLDER_INTERFACE
+  struct CIconPair
+  {
+    UString Ext;
+    UInt32 IconIndex;
+  };
+  CSysString Path;
+  CObjectVector<CIconPair> IconPairs;
+  void LoadIcons();
+  int FindIconIndex(const UString &ext) const;
+  #endif
+  CCodecLib(): GetMethodProperty(0) {}
+};
+#endif
+
+class CCodecs:
+  #ifdef EXTERNAL_CODECS
+  public ICompressCodecsInfo,
+  #else
+  public IUnknown,
+  #endif
+  public CMyUnknownImp
+{
+public:
+  #ifdef EXTERNAL_CODECS
+  CObjectVector<CCodecLib> Libs;
+  CObjectVector<CDllCodecInfo> Codecs;
+  HRESULT LoadCodecs();
+  HRESULT LoadFormats();
+  HRESULT LoadDll(const CSysString &path);
+  HRESULT LoadDllsFromFolder(const CSysString &folderPrefix);
+
+  HRESULT CreateArchiveHandler(const CArcInfoEx &ai, void **archive, bool outHandler) const
+  {
+    return Libs[ai.LibIndex].CreateObject(&ai.ClassID, outHandler ? &IID_IOutArchive : &IID_IInArchive, (void **)archive);
+  }
+  #endif
+
+public:
+  CObjectVector<CArcInfoEx> Formats;
+  HRESULT Load();
+  
+  #ifndef _SFX
+  int FindFormatForArchiveName(const UString &arcPath) const;
+  int FindFormatForExtension(const UString &ext) const;
+  int FindFormatForArchiveType(const UString &arcType) const;
+  bool FindFormatForArchiveType(const UString &arcType, CIntVector &formatIndices) const;
+  #endif
+
+  MY_UNKNOWN_IMP
+
+  #ifdef EXTERNAL_CODECS
+  STDMETHOD(GetNumberOfMethods)(UInt32 *numMethods);
+  STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value);
+  STDMETHOD(CreateDecoder)(UInt32 index, const GUID *interfaceID, void **coder);
+  STDMETHOD(CreateEncoder)(UInt32 index, const GUID *interfaceID, void **coder);
+  #endif
+
+  int GetCodecLibIndex(UInt32 index);
+  bool GetCodecEncoderIsAssigned(UInt32 index);
+  HRESULT GetCodecId(UInt32 index, UInt64 &id);
+  UString GetCodecName(UInt32 index);
+
+  HRESULT CreateInArchive(int formatIndex, CMyComPtr<IInArchive> &archive) const
+  {
+    const CArcInfoEx &ai = Formats[formatIndex];
+    #ifdef EXTERNAL_CODECS
+    if (ai.LibIndex < 0)
+    #endif
+    {
+      archive = ai.CreateInArchive();
+      return S_OK;
+    }
+    #ifdef EXTERNAL_CODECS
+    return CreateArchiveHandler(ai, (void **)&archive, false);
+    #endif
+  }
+  HRESULT CreateOutArchive(int formatIndex, CMyComPtr<IOutArchive> &archive) const
+  {
+    const CArcInfoEx &ai = Formats[formatIndex];
+    #ifdef EXTERNAL_CODECS
+    if (ai.LibIndex < 0)
+    #endif
+    {
+      archive = ai.CreateOutArchive();
+      return S_OK;
+    }
+    #ifdef EXTERNAL_CODECS
+    return CreateArchiveHandler(ai, (void **)&archive, true);
+    #endif
+  }
+  int FindOutFormatFromName(const UString &name) const
+  {
+    for (int i = 0; i < Formats.Size(); i++)
+    {
+      const CArcInfoEx &arc = Formats[i];
+      if (!arc.UpdateEnabled)
+        continue;
+      if (arc.Name.CompareNoCase(name) == 0)
+        return i;
+    }
+    return -1;
+  }
+
+  #ifdef EXTERNAL_CODECS
+  HRESULT CreateCoder(const UString &name, bool encode, CMyComPtr<ICompressCoder> &coder) const;
+  #endif
+
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/OpenArchive.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/OpenArchive.cpp
new file mode 100644
index 0000000..364ddff
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/OpenArchive.cpp
@@ -0,0 +1,550 @@
+// OpenArchive.cpp
+
+#include "StdAfx.h"
+
+#include "OpenArchive.h"
+
+#include "Common/Wildcard.h"
+
+#include "Windows/FileName.h"
+#include "Windows/FileDir.h"
+#include "Windows/Defs.h"
+#include "Windows/PropVariant.h"
+
+#include "../../Common/FileStreams.h"
+#include "../../Common/StreamUtils.h"
+
+#include "Common/StringConvert.h"
+
+#include "DefaultName.h"
+
+using namespace NWindows;
+
+HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, UString &result)
+{
+  NCOM::CPropVariant prop;
+  RINOK(archive->GetProperty(index, kpidPath, &prop));
+  if(prop.vt == VT_BSTR)
+    result = prop.bstrVal;
+  else if (prop.vt == VT_EMPTY)
+    result.Empty();
+  else
+    return E_FAIL;
+  return S_OK;
+}
+
+HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, const UString &defaultName, UString &result)
+{
+  RINOK(GetArchiveItemPath(archive, index, result));
+  if (result.IsEmpty())
+  {
+    result = defaultName;
+    NCOM::CPropVariant prop;
+    RINOK(archive->GetProperty(index, kpidExtension, &prop));
+    if (prop.vt == VT_BSTR)
+    {
+      result += L'.';
+      result += prop.bstrVal;
+    }
+    else if (prop.vt != VT_EMPTY)
+      return E_FAIL;
+  }
+  return S_OK;
+}
+
+HRESULT GetArchiveItemFileTime(IInArchive *archive, UInt32 index,
+    const FILETIME &defaultFileTime, FILETIME &fileTime)
+{
+  NCOM::CPropVariant prop;
+  RINOK(archive->GetProperty(index, kpidMTime, &prop));
+  if (prop.vt == VT_FILETIME)
+    fileTime = prop.filetime;
+  else if (prop.vt == VT_EMPTY)
+    fileTime = defaultFileTime;
+  else
+    return E_FAIL;
+  return S_OK;
+}
+
+HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result)
+{
+  NCOM::CPropVariant prop;
+  RINOK(archive->GetProperty(index, propID, &prop));
+  if(prop.vt == VT_BOOL)
+    result = VARIANT_BOOLToBool(prop.boolVal);
+  else if (prop.vt == VT_EMPTY)
+    result = false;
+  else
+    return E_FAIL;
+  return S_OK;
+}
+
+HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result)
+{
+  return IsArchiveItemProp(archive, index, kpidIsDir, result);
+}
+
+HRESULT IsArchiveItemAnti(IInArchive *archive, UInt32 index, bool &result)
+{
+  return IsArchiveItemProp(archive, index, kpidIsAnti, result);
+}
+
+// Static-SFX (for Linux) can be big.
+const UInt64 kMaxCheckStartPosition = 1 << 22;
+
+HRESULT ReOpenArchive(IInArchive *archive, const UString &fileName, IArchiveOpenCallback *openArchiveCallback)
+{
+  CInFileStream *inStreamSpec = new CInFileStream;
+  CMyComPtr<IInStream> inStream(inStreamSpec);
+  inStreamSpec->Open(fileName);
+  return archive->Open(inStream, &kMaxCheckStartPosition, openArchiveCallback);
+}
+
+#ifndef _SFX
+static inline bool TestSignature(const Byte *p1, const Byte *p2, size_t size)
+{
+  for (size_t i = 0; i < size; i++)
+    if (p1[i] != p2[i])
+      return false;
+  return true;
+}
+#endif
+
+HRESULT OpenArchive(
+    CCodecs *codecs,
+    int arcTypeIndex,
+    IInStream *inStream,
+    const UString &fileName,
+    IInArchive **archiveResult,
+    int &formatIndex,
+    UString &defaultItemName,
+    IArchiveOpenCallback *openArchiveCallback)
+{
+  *archiveResult = NULL;
+  UString extension;
+  {
+    int dotPos = fileName.ReverseFind(L'.');
+    if (dotPos >= 0)
+      extension = fileName.Mid(dotPos + 1);
+  }
+  CIntVector orderIndices;
+  if (arcTypeIndex >= 0)
+    orderIndices.Add(arcTypeIndex);
+  else
+  {
+
+  int i;
+  int numFinded = 0;
+  for (i = 0; i < codecs->Formats.Size(); i++)
+    if (codecs->Formats[i].FindExtension(extension) >= 0)
+      orderIndices.Insert(numFinded++, i);
+    else
+      orderIndices.Add(i);
+  
+  #ifndef _SFX
+  if (numFinded != 1)
+  {
+    CIntVector orderIndices2;
+    CByteBuffer byteBuffer;
+    const size_t kBufferSize = (1 << 21);
+    byteBuffer.SetCapacity(kBufferSize);
+    RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
+    size_t processedSize = kBufferSize;
+    RINOK(ReadStream(inStream, byteBuffer, &processedSize));
+    if (processedSize == 0)
+      return S_FALSE;
+
+    const Byte *buf = byteBuffer;
+    Byte hash[1 << 16];
+    memset(hash, 0xFF, 1 << 16);
+    Byte prevs[256];
+    if (orderIndices.Size() > 255)
+      return S_FALSE;
+    int i;
+    for (i = 0; i < orderIndices.Size(); i++)
+    {
+      const CArcInfoEx &ai = codecs->Formats[orderIndices[i]];
+      const CByteBuffer &sig = ai.StartSignature;
+      if (sig.GetCapacity() < 2)
+        continue;
+      UInt32 v = sig[0] | ((UInt32)sig[1] << 8);
+      prevs[i] = hash[v];
+      hash[v] = (Byte)i;
+    }
+
+    processedSize--;
+    for (UInt32 pos = 0; pos < processedSize; pos++)
+    {
+      for (; pos < processedSize && hash[buf[pos] | ((UInt32)buf[pos + 1] << 8)] == 0xFF; pos++);
+      if (pos == processedSize)
+        break;
+      UInt32 v = buf[pos] | ((UInt32)buf[pos + 1] << 8);
+      Byte *ptr = &hash[v];
+      int i = *ptr;
+      do
+      {
+        int index = orderIndices[i];
+        const CArcInfoEx &ai = codecs->Formats[index];
+        const CByteBuffer &sig = ai.StartSignature;
+        if (sig.GetCapacity() != 0 && pos + sig.GetCapacity() <= processedSize + 1)
+          if (TestSignature(buf + pos, sig, sig.GetCapacity()))
+          {
+            orderIndices2.Add(index);
+            orderIndices[i] = 0xFF;
+            *ptr = prevs[i];
+          }
+        ptr = &prevs[i];
+        i = *ptr;
+      }
+      while (i != 0xFF);
+    }
+    
+    for (i = 0; i < orderIndices.Size(); i++)
+    {
+      int val = orderIndices[i];
+      if (val != 0xFF)
+        orderIndices2.Add(val);
+    }
+    orderIndices = orderIndices2;
+
+    if (orderIndices.Size() >= 2)
+    {
+      int isoIndex = codecs->FindFormatForArchiveType(L"iso");
+      int udfIndex = codecs->FindFormatForArchiveType(L"udf");
+      int iIso = -1;
+      int iUdf = -1;
+      for (int i = 0; i < orderIndices.Size(); i++)
+      {
+        if (orderIndices[i] == isoIndex) iIso = i;
+        if (orderIndices[i] == udfIndex) iUdf = i;
+      }
+      if (iUdf == iIso + 1)
+      {
+        orderIndices[iUdf] = isoIndex;
+        orderIndices[iIso] = udfIndex;
+      }
+    }
+  }
+  else if (extension == L"000" || extension == L"001")
+  {
+    CByteBuffer byteBuffer;
+    const size_t kBufferSize = (1 << 10);
+    byteBuffer.SetCapacity(kBufferSize);
+    Byte *buffer = byteBuffer;
+    RINOK(inStream->Seek(0, STREAM_SEEK_SET, NULL));
+    size_t processedSize = kBufferSize;
+    RINOK(ReadStream(inStream, buffer, &processedSize));
+    if (processedSize >= 16)
+    {
+      Byte kRarHeader[] = {0x52 , 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00};
+      if (TestSignature(buffer, kRarHeader, 7) && buffer[9] == 0x73 && (buffer[10] & 1) != 0)
+      {
+        for (int i = 0; i < orderIndices.Size(); i++)
+        {
+          int index = orderIndices[i];
+          const CArcInfoEx &ai = codecs->Formats[index];
+          if (ai.Name.CompareNoCase(L"rar") != 0)
+            continue;
+          orderIndices.Delete(i--);
+          orderIndices.Insert(0, index);
+          break;
+        }
+      }
+    }
+  }
+  #endif
+  }
+
+  for(int i = 0; i < orderIndices.Size(); i++)
+  {
+    inStream->Seek(0, STREAM_SEEK_SET, NULL);
+
+    CMyComPtr<IInArchive> archive;
+
+    formatIndex = orderIndices[i];
+    RINOK(codecs->CreateInArchive(formatIndex, archive));
+    if (!archive)
+      continue;
+
+    #ifdef EXTERNAL_CODECS
+    {
+      CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
+      archive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
+      if (setCompressCodecsInfo)
+      {
+        RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs));
+      }
+    }
+    #endif
+
+    HRESULT result = archive->Open(inStream, &kMaxCheckStartPosition, openArchiveCallback);
+    if (result == S_FALSE)
+      continue;
+    RINOK(result);
+    *archiveResult = archive.Detach();
+    const CArcInfoEx &format = codecs->Formats[formatIndex];
+    if (format.Exts.Size() == 0)
+    {
+      defaultItemName = GetDefaultName2(fileName, L"", L"");
+    }
+    else
+    {
+      int subExtIndex = format.FindExtension(extension);
+      if (subExtIndex < 0)
+        subExtIndex = 0;
+      defaultItemName = GetDefaultName2(fileName,
+          format.Exts[subExtIndex].Ext,
+          format.Exts[subExtIndex].AddExt);
+    }
+    return S_OK;
+  }
+  return S_FALSE;
+}
+
+HRESULT OpenArchive(
+    CCodecs *codecs,
+    int arcTypeIndex,
+    const UString &filePath,
+    IInArchive **archiveResult,
+    int &formatIndex,
+    UString &defaultItemName,
+    IArchiveOpenCallback *openArchiveCallback)
+{
+  CInFileStream *inStreamSpec = new CInFileStream;
+  CMyComPtr<IInStream> inStream(inStreamSpec);
+  if (!inStreamSpec->Open(filePath))
+    return GetLastError();
+  return OpenArchive(codecs, arcTypeIndex, inStream, ExtractFileNameFromPath(filePath),
+    archiveResult, formatIndex,
+    defaultItemName, openArchiveCallback);
+}
+
+static void MakeDefaultName(UString &name)
+{
+  int dotPos = name.ReverseFind(L'.');
+  if (dotPos < 0)
+    return;
+  UString ext = name.Mid(dotPos + 1);
+  if (ext.IsEmpty())
+    return;
+  for (int pos = 0; pos < ext.Length(); pos++)
+    if (ext[pos] < L'0' || ext[pos] > L'9')
+      return;
+  name = name.Left(dotPos);
+}
+
+HRESULT OpenArchive(
+    CCodecs *codecs,
+    const CIntVector &formatIndices,
+    const UString &fileName,
+    IInArchive **archive0,
+    IInArchive **archive1,
+    int &formatIndex0,
+    int &formatIndex1,
+    UString &defaultItemName0,
+    UString &defaultItemName1,
+    IArchiveOpenCallback *openArchiveCallback)
+{
+  if (formatIndices.Size() >= 3)
+    return E_NOTIMPL;
+  
+  int arcTypeIndex = -1;
+  if (formatIndices.Size() >= 1)
+    arcTypeIndex = formatIndices[formatIndices.Size() - 1];
+  
+  HRESULT result = OpenArchive(codecs, arcTypeIndex, fileName,
+    archive0, formatIndex0, defaultItemName0, openArchiveCallback);
+  RINOK(result);
+
+  if (formatIndices.Size() == 1)
+    return S_OK;
+  arcTypeIndex = -1;
+  if (formatIndices.Size() >= 2)
+    arcTypeIndex = formatIndices[formatIndices.Size() - 2];
+
+  HRESULT resSpec = (formatIndices.Size() == 0 ? S_OK : E_NOTIMPL);
+
+  CMyComPtr<IInArchiveGetStream> getStream;
+  result = (*archive0)->QueryInterface(IID_IInArchiveGetStream, (void **)&getStream);
+  if (result != S_OK || !getStream)
+    return resSpec;
+
+  CMyComPtr<ISequentialInStream> subSeqStream;
+  result = getStream->GetStream(0, &subSeqStream);
+  if (result != S_OK || !subSeqStream)
+    return resSpec;
+
+  CMyComPtr<IInStream> subStream;
+  result = subSeqStream.QueryInterface(IID_IInStream, &subStream);
+  if (result != S_OK || !subStream)
+    return resSpec;
+
+  UInt32 numItems;
+  RINOK((*archive0)->GetNumberOfItems(&numItems));
+  if (numItems < 1)
+    return resSpec;
+
+  UString subPath;
+  RINOK(GetArchiveItemPath(*archive0, 0, subPath))
+  if (subPath.IsEmpty())
+  {
+    MakeDefaultName(defaultItemName0);
+    subPath = defaultItemName0;
+    const CArcInfoEx &format = codecs->Formats[formatIndex0];
+    if (format.Name.CompareNoCase(L"7z") == 0)
+    {
+      if (subPath.Right(3).CompareNoCase(L".7z") != 0)
+        subPath += L".7z";
+    }
+  }
+  else
+    subPath = ExtractFileNameFromPath(subPath);
+
+  CMyComPtr<IArchiveOpenSetSubArchiveName> setSubArchiveName;
+  openArchiveCallback->QueryInterface(IID_IArchiveOpenSetSubArchiveName, (void **)&setSubArchiveName);
+  if (setSubArchiveName)
+    setSubArchiveName->SetSubArchiveName(subPath);
+
+  result = OpenArchive(codecs, arcTypeIndex, subStream, subPath,
+      archive1, formatIndex1, defaultItemName1, openArchiveCallback);
+  resSpec = (formatIndices.Size() == 0 ? S_OK : S_FALSE);
+  if (result != S_OK)
+    return resSpec;
+  return S_OK;
+}
+
+static void SetCallback(const UString &archiveName,
+    IOpenCallbackUI *openCallbackUI,
+    IArchiveOpenCallback *reOpenCallback,
+    CMyComPtr<IArchiveOpenCallback> &openCallback)
+{
+  COpenCallbackImp *openCallbackSpec = new COpenCallbackImp;
+  openCallback = openCallbackSpec;
+  openCallbackSpec->Callback = openCallbackUI;
+  openCallbackSpec->ReOpenCallback = reOpenCallback;
+
+  UString fullName;
+  int fileNamePartStartIndex;
+  NFile::NDirectory::MyGetFullPathName(archiveName, fullName, fileNamePartStartIndex);
+  openCallbackSpec->Init(
+      fullName.Left(fileNamePartStartIndex),
+      fullName.Mid(fileNamePartStartIndex));
+}
+
+HRESULT MyOpenArchive(
+    CCodecs *codecs,
+    int arcTypeIndex,
+    const UString &archiveName,
+    IInArchive **archive, UString &defaultItemName, IOpenCallbackUI *openCallbackUI)
+{
+  CMyComPtr<IArchiveOpenCallback> openCallback;
+  SetCallback(archiveName, openCallbackUI, NULL, openCallback);
+  int formatInfo;
+  return OpenArchive(codecs, arcTypeIndex, archiveName, archive, formatInfo, defaultItemName, openCallback);
+}
+
+HRESULT MyOpenArchive(
+    CCodecs *codecs,
+    const CIntVector &formatIndices,
+    const UString &archiveName,
+    IInArchive **archive0,
+    IInArchive **archive1,
+    UString &defaultItemName0,
+    UString &defaultItemName1,
+    UStringVector &volumePaths,
+    UInt64 &volumesSize,
+    IOpenCallbackUI *openCallbackUI)
+{
+  volumesSize = 0;
+  COpenCallbackImp *openCallbackSpec = new COpenCallbackImp;
+  CMyComPtr<IArchiveOpenCallback> openCallback = openCallbackSpec;
+  openCallbackSpec->Callback = openCallbackUI;
+
+  UString fullName;
+  int fileNamePartStartIndex;
+  NFile::NDirectory::MyGetFullPathName(archiveName, fullName, fileNamePartStartIndex);
+  UString prefix = fullName.Left(fileNamePartStartIndex);
+  UString name = fullName.Mid(fileNamePartStartIndex);
+  openCallbackSpec->Init(prefix, name);
+
+  int formatIndex0, formatIndex1;
+  RINOK(OpenArchive(codecs, formatIndices, archiveName,
+      archive0,
+      archive1,
+      formatIndex0,
+      formatIndex1,
+      defaultItemName0,
+      defaultItemName1,
+      openCallback));
+  volumePaths.Add(prefix + name);
+  for (int i = 0; i < openCallbackSpec->FileNames.Size(); i++)
+    volumePaths.Add(prefix + openCallbackSpec->FileNames[i]);
+  volumesSize = openCallbackSpec->TotalSize;
+  return S_OK;
+}
+
+HRESULT CArchiveLink::Close()
+{
+  if (Archive1 != 0)
+    RINOK(Archive1->Close());
+  if (Archive0 != 0)
+    RINOK(Archive0->Close());
+  IsOpen = false;
+  return S_OK;
+}
+
+void CArchiveLink::Release()
+{
+  IsOpen = false;
+  Archive1.Release();
+  Archive0.Release();
+}
+
+HRESULT OpenArchive(
+    CCodecs *codecs,
+    const CIntVector &formatIndices,
+    const UString &archiveName,
+    CArchiveLink &archiveLink,
+    IArchiveOpenCallback *openCallback)
+{
+  HRESULT res = OpenArchive(codecs, formatIndices, archiveName,
+    &archiveLink.Archive0, &archiveLink.Archive1,
+    archiveLink.FormatIndex0, archiveLink.FormatIndex1,
+    archiveLink.DefaultItemName0, archiveLink.DefaultItemName1,
+    openCallback);
+  archiveLink.IsOpen = (res == S_OK);
+  return res;
+}
+
+HRESULT MyOpenArchive(CCodecs *codecs,
+    const CIntVector &formatIndices,
+    const UString &archiveName,
+    CArchiveLink &archiveLink,
+    IOpenCallbackUI *openCallbackUI)
+{
+  HRESULT res = MyOpenArchive(codecs, formatIndices, archiveName,
+    &archiveLink.Archive0, &archiveLink.Archive1,
+    archiveLink.DefaultItemName0, archiveLink.DefaultItemName1,
+    archiveLink.VolumePaths,
+    archiveLink.VolumesSize,
+    openCallbackUI);
+  archiveLink.IsOpen = (res == S_OK);
+  return res;
+}
+
+HRESULT ReOpenArchive(CCodecs *codecs, CArchiveLink &archiveLink, const UString &fileName,
+    IArchiveOpenCallback *openCallback)
+{
+  if (archiveLink.GetNumLevels() > 1)
+    return E_NOTIMPL;
+
+  if (archiveLink.GetNumLevels() == 0)
+    return MyOpenArchive(codecs, CIntVector(), fileName, archiveLink, 0);
+
+  CMyComPtr<IArchiveOpenCallback> openCallbackNew;
+  SetCallback(fileName, NULL, openCallback, openCallbackNew);
+
+  HRESULT res = ReOpenArchive(archiveLink.GetArchive(), fileName, openCallbackNew);
+  archiveLink.IsOpen = (res == S_OK);
+  return res;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/OpenArchive.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/OpenArchive.h
new file mode 100644
index 0000000..f1b4fda
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/OpenArchive.h
@@ -0,0 +1,117 @@
+// OpenArchive.h
+
+#ifndef __OPENARCHIVE_H
+#define __OPENARCHIVE_H
+
+#include "Common/MyString.h"
+#include "Windows/FileFind.h"
+
+#include "../../Archive/IArchive.h"
+#include "LoadCodecs.h"
+#include "ArchiveOpenCallback.h"
+
+HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, UString &result);
+HRESULT GetArchiveItemPath(IInArchive *archive, UInt32 index, const UString &defaultName, UString &result);
+HRESULT GetArchiveItemFileTime(IInArchive *archive, UInt32 index,
+    const FILETIME &defaultFileTime, FILETIME &fileTime);
+HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result);
+HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result);
+HRESULT IsArchiveItemAnti(IInArchive *archive, UInt32 index, bool &result);
+
+struct ISetSubArchiveName
+{
+  virtual void SetSubArchiveName(const wchar_t *name) = 0;
+};
+
+HRESULT OpenArchive(
+    CCodecs *codecs,
+    int arcTypeIndex,
+    IInStream *inStream,
+    const UString &fileName,
+    IInArchive **archiveResult,
+    int &formatIndex,
+    UString &defaultItemName,
+    IArchiveOpenCallback *openArchiveCallback);
+
+HRESULT OpenArchive(
+    CCodecs *codecs,
+    int arcTypeIndex,
+    const UString &filePath,
+    IInArchive **archive,
+    int &formatIndex,
+    UString &defaultItemName,
+    IArchiveOpenCallback *openArchiveCallback);
+
+HRESULT OpenArchive(
+    CCodecs *codecs,
+    const CIntVector &formatIndices,
+    const UString &filePath,
+    IInArchive **archive0,
+    IInArchive **archive1,
+    int &formatIndex0,
+    int &formatIndex1,
+    UString &defaultItemName0,
+    UString &defaultItemName1,
+    IArchiveOpenCallback *openArchiveCallback);
+
+
+HRESULT ReOpenArchive(IInArchive *archive, const UString &fileName, IArchiveOpenCallback *openArchiveCallback);
+
+struct CArchiveLink
+{
+  CMyComPtr<IInArchive> Archive0;
+  CMyComPtr<IInArchive> Archive1;
+  UString DefaultItemName0;
+  UString DefaultItemName1;
+
+  int FormatIndex0;
+  int FormatIndex1;
+  
+  UStringVector VolumePaths;
+
+  bool IsOpen;
+  UInt64 VolumesSize;
+
+  int GetNumLevels() const
+  {
+    int result = 0;
+    if (Archive0)
+    {
+      result++;
+      if (Archive1)
+        result++;
+    }
+    return result;
+  }
+
+  CArchiveLink(): IsOpen(false), VolumesSize(0) {};
+
+  IInArchive *GetArchive() { return Archive1 != 0 ? Archive1: Archive0; }
+  UString GetDefaultItemName()  { return Archive1 != 0 ? DefaultItemName1: DefaultItemName0; }
+  int GetArchiverIndex() const { return Archive1 != 0 ? FormatIndex1: FormatIndex0; }
+  HRESULT Close();
+  void Release();
+};
+
+HRESULT OpenArchive(
+    CCodecs *codecs,
+    const CIntVector &formatIndices,
+    const UString &archiveName,
+    CArchiveLink &archiveLink,
+    IArchiveOpenCallback *openCallback);
+
+HRESULT MyOpenArchive(
+    CCodecs *codecs,
+    const CIntVector &formatIndices,
+    const UString &archiveName,
+    CArchiveLink &archiveLink,
+    IOpenCallbackUI *openCallbackUI);
+
+HRESULT ReOpenArchive(
+    CCodecs *codecs,
+    CArchiveLink &archiveLink,
+    const UString &fileName,
+    IArchiveOpenCallback *openCallback);
+
+#endif
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/PropIDUtils.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/PropIDUtils.cpp
new file mode 100644
index 0000000..bf11ea1
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/PropIDUtils.cpp
@@ -0,0 +1,89 @@
+// PropIDUtils.cpp
+
+#include "StdAfx.h"
+
+#include "PropIDUtils.h"
+
+#include "Common/IntToString.h"
+#include "Common/StringConvert.h"
+
+#include "Windows/FileFind.h"
+#include "Windows/PropVariantConversions.h"
+
+#include "../../PropID.h"
+
+using namespace NWindows;
+
+static UString ConvertUInt32ToString(UInt32 value)
+{
+  wchar_t buffer[32];
+  ConvertUInt64ToString(value, buffer);
+  return buffer;
+}
+
+static void ConvertUInt32ToHex(UInt32 value, wchar_t *s)
+{
+  for (int i = 0; i < 8; i++)
+  {
+    int t = value & 0xF;
+    value >>= 4;
+    s[7 - i] = (wchar_t)((t < 10) ? (L'0' + t) : (L'A' + (t - 10)));
+  }
+  s[8] = L'\0';
+}
+
+UString ConvertPropertyToString(const PROPVARIANT &propVariant, PROPID propID, bool full)
+{
+  switch(propID)
+  {
+    case kpidCTime:
+    case kpidATime:
+    case kpidMTime:
+    {
+      if (propVariant.vt != VT_FILETIME)
+        return UString(); // It is error;
+      FILETIME localFileTime;
+      if (propVariant.filetime.dwHighDateTime == 0 &&
+          propVariant.filetime.dwLowDateTime == 0)
+        return UString();
+      if (!::FileTimeToLocalFileTime(&propVariant.filetime, &localFileTime))
+        return UString(); // It is error;
+      return ConvertFileTimeToString(localFileTime, true, full);
+    }
+    case kpidCRC:
+    {
+      if(propVariant.vt != VT_UI4)
+        break;
+      wchar_t temp[12];
+      ConvertUInt32ToHex(propVariant.ulVal, temp);
+      return temp;
+    }
+    case kpidAttrib:
+    {
+      if(propVariant.vt != VT_UI4)
+        break;
+      UString result;
+      UInt32 attributes = propVariant.ulVal;
+      if (NFile::NFind::NAttributes::IsReadOnly(attributes)) result += L'R';
+      if (NFile::NFind::NAttributes::IsHidden(attributes)) result += L'H';
+      if (NFile::NFind::NAttributes::IsSystem(attributes)) result += L'S';
+      if (NFile::NFind::NAttributes::IsDir(attributes)) result += L'D';
+      if (NFile::NFind::NAttributes::IsArchived(attributes)) result += L'A';
+      if (NFile::NFind::NAttributes::IsCompressed(attributes)) result += L'C';
+      if (NFile::NFind::NAttributes::IsEncrypted(attributes)) result += L'E';
+      return result;
+    }
+    case kpidDictionarySize:
+    {
+      if(propVariant.vt != VT_UI4)
+        break;
+      UInt32 size = propVariant.ulVal;
+      if (size % (1 << 20) == 0)
+        return ConvertUInt32ToString(size >> 20) + L"MB";
+      if (size % (1 << 10) == 0)
+        return ConvertUInt32ToString(size >> 10) + L"KB";
+      return ConvertUInt32ToString(size);
+    }
+  }
+  return ConvertPropVariantToString(propVariant);
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/PropIDUtils.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/PropIDUtils.h
new file mode 100644
index 0000000..1d82097
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/PropIDUtils.h
@@ -0,0 +1,10 @@
+// PropIDUtils.h
+
+#ifndef __PROPIDUTILS_H
+#define __PROPIDUTILS_H
+
+#include "Common/MyString.h"
+
+UString ConvertPropertyToString(const PROPVARIANT &propVariant, PROPID propID, bool full = true);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/Property.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/Property.h
new file mode 100644
index 0000000..9fd340c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/Property.h
@@ -0,0 +1,14 @@
+// Property.h
+
+#ifndef __PROPERTY_H
+#define __PROPERTY_H
+
+#include "Common/MyString.h"
+
+struct CProperty
+{
+  UString Name;
+  UString Value;
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/SetProperties.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/SetProperties.cpp
new file mode 100644
index 0000000..4827f2a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/SetProperties.cpp
@@ -0,0 +1,79 @@
+// SetProperties.cpp
+
+#include "StdAfx.h"
+
+#include "SetProperties.h"
+
+#include "Windows/PropVariant.h"
+#include "Common/MyString.h"
+#include "Common/StringToInt.h"
+#include "Common/MyCom.h"
+
+#include "../../Archive/IArchive.h"
+
+using namespace NWindows;
+using namespace NCOM;
+
+static void ParseNumberString(const UString &s, NCOM::CPropVariant &prop)
+{
+  const wchar_t *endPtr;
+  UInt64 result = ConvertStringToUInt64(s, &endPtr);
+  if (endPtr - (const wchar_t *)s != s.Length())
+    prop = s;
+  else if (result <= 0xFFFFFFFF)
+    prop = (UInt32)result;
+  else
+    prop = result;
+}
+
+HRESULT SetProperties(IUnknown *unknown, const CObjectVector<CProperty> &properties)
+{
+  if (properties.IsEmpty())
+    return S_OK;
+  CMyComPtr<ISetProperties> setProperties;
+  unknown->QueryInterface(IID_ISetProperties, (void **)&setProperties);
+  if (!setProperties)
+    return S_OK;
+
+  UStringVector realNames;
+  CPropVariant *values = new CPropVariant[properties.Size()];
+  try
+  {
+    int i;
+    for(i = 0; i < properties.Size(); i++)
+    {
+      const CProperty &property = properties[i];
+      NCOM::CPropVariant propVariant;
+      UString name = property.Name;
+      if (property.Value.IsEmpty())
+      {
+        if (!name.IsEmpty())
+        {
+          wchar_t c = name[name.Length() - 1];
+          if (c == L'-')
+            propVariant = false;
+          else if (c == L'+')
+            propVariant = true;
+          if (propVariant.vt != VT_EMPTY)
+            name = name.Left(name.Length() - 1);
+        }
+      }
+      else
+        ParseNumberString(property.Value, propVariant);
+      realNames.Add(name);
+      values[i] = propVariant;
+    }
+    CRecordVector<const wchar_t *> names;
+    for(i = 0; i < realNames.Size(); i++)
+      names.Add((const wchar_t *)realNames[i]);
+    
+    RINOK(setProperties->SetProperties(&names.Front(), values, names.Size()));
+  }
+  catch(...)
+  {
+    delete []values;
+    throw;
+  }
+  delete []values;
+  return S_OK;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/SetProperties.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/SetProperties.h
new file mode 100644
index 0000000..892f1a2
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/SetProperties.h
@@ -0,0 +1,10 @@
+// SetProperties.h
+
+#ifndef __SETPROPERTIES_H
+#define __SETPROPERTIES_H
+
+#include "Property.h"
+
+HRESULT SetProperties(IUnknown *unknown, const CObjectVector<CProperty> &properties);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/SortUtils.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/SortUtils.cpp
new file mode 100644
index 0000000..061e777
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/SortUtils.cpp
@@ -0,0 +1,22 @@
+// SortUtils.cpp
+
+#include "StdAfx.h"
+
+#include "SortUtils.h"
+#include "Common/Wildcard.h"
+
+static int CompareStrings(const int *p1, const int *p2, void *param)
+{
+  const UStringVector &strings = *(const UStringVector *)param;
+  return CompareFileNames(strings[*p1], strings[*p2]);
+}
+
+void SortFileNames(const UStringVector &strings, CIntVector &indices)
+{
+  indices.Clear();
+  int numItems = strings.Size();
+  indices.Reserve(numItems);
+  for(int i = 0; i < numItems; i++)
+    indices.Add(i);
+  indices.Sort(CompareStrings, (void *)&strings);
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/SortUtils.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/SortUtils.h
new file mode 100644
index 0000000..e152246
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/SortUtils.h
@@ -0,0 +1,10 @@
+// SortUtils.h
+
+#ifndef __SORTUTLS_H
+#define __SORTUTLS_H
+
+#include "Common/MyString.h"
+
+void SortFileNames(const UStringVector &strings, CIntVector &indices);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/StdAfx.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/StdAfx.h
new file mode 100644
index 0000000..9a8e7d2
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/StdAfx.h
@@ -0,0 +1,9 @@
+// stdafx.h
+
+#ifndef __STDAFX_H
+#define __STDAFX_H
+
+#include "../../../Common/MyWindows.h"
+#include "../../../Common/NewHandler.h"
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/TempFiles.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/TempFiles.cpp
new file mode 100644
index 0000000..eeaec18
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/TempFiles.cpp
@@ -0,0 +1,22 @@
+// TempFiles.cpp
+
+#include "StdAfx.h"
+
+#include "TempFiles.h"
+
+#include "Windows/FileDir.h"
+#include "Windows/FileIO.h"
+
+using namespace NWindows;
+using namespace NFile;
+
+void CTempFiles::Clear()
+{
+  while(!Paths.IsEmpty())
+  {
+    NDirectory::DeleteFileAlways((LPCWSTR)Paths.Back());
+    Paths.DeleteBack();
+  }
+}
+
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/TempFiles.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/TempFiles.h
new file mode 100644
index 0000000..eb474a7
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/TempFiles.h
@@ -0,0 +1,16 @@
+// TempFiles.h
+
+#ifndef __TEMPFILES_H
+#define __TEMPFILES_H
+
+#include "Common/MyString.h"
+
+class CTempFiles
+{
+  void Clear();
+public:
+  UStringVector Paths;
+  ~CTempFiles() { Clear(); }
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/Update.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/Update.cpp
new file mode 100644
index 0000000..d8fee28
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/Update.cpp
@@ -0,0 +1,911 @@
+// Update.cpp
+
+#include "StdAfx.h"
+
+#include "Update.h"
+
+#include "Common/IntToString.h"
+#include "Common/StringConvert.h"
+
+#ifdef _WIN32
+#include "Windows/DLL.h"
+#endif
+
+#include "Windows/FileDir.h"
+#include "Windows/FileFind.h"
+#include "Windows/FileName.h"
+#include "Windows/PropVariant.h"
+#include "Windows/PropVariantConversions.h"
+#include "Windows/Time.h"
+
+#include "../../Common/FileStreams.h"
+
+#include "../../Compress/CopyCoder.h"
+
+#include "../Common/DirItem.h"
+#include "../Common/EnumDirItems.h"
+#include "../Common/OpenArchive.h"
+#include "../Common/UpdateProduce.h"
+
+#include "EnumDirItems.h"
+#include "SetProperties.h"
+#include "TempFiles.h"
+#include "UpdateCallback.h"
+
+static const char *kUpdateIsNotSupoorted =
+  "update operations are not supported for this archive";
+
+using namespace NWindows;
+using namespace NCOM;
+using namespace NFile;
+using namespace NName;
+
+static const wchar_t *kTempFolderPrefix = L"7zE";
+
+using namespace NUpdateArchive;
+
+static HRESULT CopyBlock(ISequentialInStream *inStream, ISequentialOutStream *outStream)
+{
+  CMyComPtr<ICompressCoder> copyCoder = new NCompress::CCopyCoder;
+  return copyCoder->Code(inStream, outStream, NULL, NULL, NULL);
+}
+
+class COutMultiVolStream:
+  public IOutStream,
+  public CMyUnknownImp
+{
+  int _streamIndex; // required stream
+  UInt64 _offsetPos; // offset from start of _streamIndex index
+  UInt64 _absPos;
+  UInt64 _length;
+
+  struct CSubStreamInfo
+  {
+    COutFileStream *StreamSpec;
+    CMyComPtr<IOutStream> Stream;
+    UString Name;
+    UInt64 Pos;
+    UInt64 RealSize;
+  };
+  CObjectVector<CSubStreamInfo> Streams;
+public:
+  // CMyComPtr<IArchiveUpdateCallback2> VolumeCallback;
+  CRecordVector<UInt64> Sizes;
+  UString Prefix;
+  CTempFiles *TempFiles;
+
+  void Init()
+  {
+    _streamIndex = 0;
+    _offsetPos = 0;
+    _absPos = 0;
+    _length = 0;
+  }
+
+  HRESULT Close();
+
+  MY_UNKNOWN_IMP1(IOutStream)
+
+  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
+  STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);
+  STDMETHOD(SetSize)(Int64 newSize);
+};
+
+// static NSynchronization::CCriticalSection g_TempPathsCS;
+
+HRESULT COutMultiVolStream::Close()
+{
+  HRESULT res = S_OK;
+  for (int i = 0; i < Streams.Size(); i++)
+  {
+    CSubStreamInfo &s = Streams[i];
+    if (s.StreamSpec)
+    {
+      HRESULT res2 = s.StreamSpec->Close();
+      if (res2 != S_OK)
+        res = res2;
+    }
+  }
+  return res;
+}
+
+STDMETHODIMP COutMultiVolStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
+{
+  if(processedSize != NULL)
+    *processedSize = 0;
+  while(size > 0)
+  {
+    if (_streamIndex >= Streams.Size())
+    {
+      CSubStreamInfo subStream;
+
+      wchar_t temp[32];
+      ConvertUInt64ToString(_streamIndex + 1, temp);
+      UString res = temp;
+      while (res.Length() < 3)
+        res = UString(L'0') + res;
+      UString name = Prefix + res;
+      subStream.StreamSpec = new COutFileStream;
+      subStream.Stream = subStream.StreamSpec;
+      if(!subStream.StreamSpec->Create(name, false))
+        return ::GetLastError();
+      {
+        // NSynchronization::CCriticalSectionLock lock(g_TempPathsCS);
+        TempFiles->Paths.Add(name);
+      }
+
+      subStream.Pos = 0;
+      subStream.RealSize = 0;
+      subStream.Name = name;
+      Streams.Add(subStream);
+      continue;
+    }
+    CSubStreamInfo &subStream = Streams[_streamIndex];
+
+    int index = _streamIndex;
+    if (index >= Sizes.Size())
+      index = Sizes.Size() - 1;
+    UInt64 volSize = Sizes[index];
+
+    if (_offsetPos >= volSize)
+    {
+      _offsetPos -= volSize;
+      _streamIndex++;
+      continue;
+    }
+    if (_offsetPos != subStream.Pos)
+    {
+      // CMyComPtr<IOutStream> outStream;
+      // RINOK(subStream.Stream.QueryInterface(IID_IOutStream, &outStream));
+      RINOK(subStream.Stream->Seek(_offsetPos, STREAM_SEEK_SET, NULL));
+      subStream.Pos = _offsetPos;
+    }
+
+    UInt32 curSize = (UInt32)MyMin((UInt64)size, volSize - subStream.Pos);
+    UInt32 realProcessed;
+    RINOK(subStream.Stream->Write(data, curSize, &realProcessed));
+    data = (void *)((Byte *)data + realProcessed);
+    size -= realProcessed;
+    subStream.Pos += realProcessed;
+    _offsetPos += realProcessed;
+    _absPos += realProcessed;
+    if (_absPos > _length)
+      _length = _absPos;
+    if (_offsetPos > subStream.RealSize)
+      subStream.RealSize = _offsetPos;
+    if(processedSize != NULL)
+      *processedSize += realProcessed;
+    if (subStream.Pos == volSize)
+    {
+      _streamIndex++;
+      _offsetPos = 0;
+    }
+    if (realProcessed == 0 && curSize != 0)
+      return E_FAIL;
+    break;
+  }
+  return S_OK;
+}
+
+STDMETHODIMP COutMultiVolStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
+{
+  if(seekOrigin >= 3)
+    return STG_E_INVALIDFUNCTION;
+  switch(seekOrigin)
+  {
+    case STREAM_SEEK_SET:
+      _absPos = offset;
+      break;
+    case STREAM_SEEK_CUR:
+      _absPos += offset;
+      break;
+    case STREAM_SEEK_END:
+      _absPos = _length + offset;
+      break;
+  }
+  _offsetPos = _absPos;
+  if (newPosition != NULL)
+    *newPosition = _absPos;
+  _streamIndex = 0;
+  return S_OK;
+}
+
+STDMETHODIMP COutMultiVolStream::SetSize(Int64 newSize)
+{
+  if (newSize < 0)
+    return E_INVALIDARG;
+  int i = 0;
+  while (i < Streams.Size())
+  {
+    CSubStreamInfo &subStream = Streams[i++];
+    if ((UInt64)newSize < subStream.RealSize)
+    {
+      RINOK(subStream.Stream->SetSize(newSize));
+      subStream.RealSize = newSize;
+      break;
+    }
+    newSize -= subStream.RealSize;
+  }
+  while (i < Streams.Size())
+  {
+    {
+      CSubStreamInfo &subStream = Streams.Back();
+      subStream.Stream.Release();
+      NDirectory::DeleteFileAlways(subStream.Name);
+    }
+    Streams.DeleteBack();
+  }
+  _offsetPos = _absPos;
+  _streamIndex = 0;
+  _length = newSize;
+  return S_OK;
+}
+
+static const wchar_t *kDefaultArchiveType = L"7z";
+static const wchar_t *kSFXExtension =
+  #ifdef _WIN32
+    L"exe";
+  #else
+    L"";
+  #endif
+
+bool CUpdateOptions::Init(const CCodecs *codecs, const CIntVector &formatIndices, const UString &arcPath)
+{
+  if (formatIndices.Size() > 1)
+    return false;
+  int arcTypeIndex = -1;
+  if (formatIndices.Size() != 0)
+    arcTypeIndex = formatIndices[0];
+  if (arcTypeIndex >= 0)
+    MethodMode.FormatIndex = arcTypeIndex;
+  else
+  {
+    MethodMode.FormatIndex = codecs->FindFormatForArchiveName(arcPath);
+    if (MethodMode.FormatIndex < 0)
+      MethodMode.FormatIndex = codecs->FindFormatForArchiveType(kDefaultArchiveType);
+  }
+  if (MethodMode.FormatIndex < 0)
+    return false;
+  const CArcInfoEx &arcInfo = codecs->Formats[MethodMode.FormatIndex];
+  if (!arcInfo.UpdateEnabled)
+    return false;
+  UString typeExt = arcInfo.GetMainExt();
+  UString ext = typeExt;
+  if (SfxMode)
+    ext = kSFXExtension;
+  ArchivePath.BaseExtension = ext;
+  ArchivePath.VolExtension = typeExt;
+  ArchivePath.ParseFromPath(arcPath);
+  for (int i = 0; i < Commands.Size(); i++)
+  {
+    CUpdateArchiveCommand &uc = Commands[i];
+    uc.ArchivePath.BaseExtension = ext;
+    uc.ArchivePath.VolExtension = typeExt;
+    uc.ArchivePath.ParseFromPath(uc.UserArchivePath);
+  }
+  return true;
+}
+
+/*
+struct CUpdateProduceCallbackImp: public IUpdateProduceCallback
+{
+  const CObjectVector<CArcItem> *_arcItems;
+  IUpdateCallbackUI *_callback;
+  
+  CUpdateProduceCallbackImp(const CObjectVector<CArcItem> *a, 
+      IUpdateCallbackUI *callback): _arcItems(a), _callback(callback) {}
+  virtual HRESULT ShowDeleteFile(int arcIndex);
+};
+
+HRESULT CUpdateProduceCallbackImp::ShowDeleteFile(int arcIndex)
+{
+  return _callback->ShowDeleteFile((*_arcItems)[arcIndex].Name);
+}
+*/
+
+static HRESULT Compress(
+    CCodecs *codecs,
+    const CActionSet &actionSet,
+    IInArchive *archive,
+    const CCompressionMethodMode &compressionMethod,
+    CArchivePath &archivePath,
+    const CObjectVector<CArcItem> &arcItems,
+    bool shareForWrite,
+    bool stdInMode,
+    /* const UString & stdInFileName, */
+    bool stdOutMode,
+    const CDirItems &dirItems,
+    bool sfxMode,
+    const UString &sfxModule,
+    const CRecordVector<UInt64> &volumesSizes,
+    CTempFiles &tempFiles,
+    CUpdateErrorInfo &errorInfo,
+    IUpdateCallbackUI *callback)
+{
+  CMyComPtr<IOutArchive> outArchive;
+  if(archive != NULL)
+  {
+    CMyComPtr<IInArchive> archive2 = archive;
+    HRESULT result = archive2.QueryInterface(IID_IOutArchive, &outArchive);
+    if(result != S_OK)
+      throw kUpdateIsNotSupoorted;
+  }
+  else
+  {
+    RINOK(codecs->CreateOutArchive(compressionMethod.FormatIndex, outArchive));
+
+    #ifdef EXTERNAL_CODECS
+    {
+      CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
+      outArchive.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
+      if (setCompressCodecsInfo)
+      {
+        RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecs));
+      }
+    }
+    #endif
+  }
+  if (outArchive == 0)
+    throw kUpdateIsNotSupoorted;
+  
+  NFileTimeType::EEnum fileTimeType;
+  UInt32 value;
+  RINOK(outArchive->GetFileTimeType(&value));
+
+  switch(value)
+  {
+    case NFileTimeType::kWindows:
+    case NFileTimeType::kUnix:
+    case NFileTimeType::kDOS:
+      fileTimeType = (NFileTimeType::EEnum)value;
+      break;
+    default:
+      return E_FAIL;
+  }
+
+  CRecordVector<CUpdatePair2> updatePairs2;
+
+  {
+    CRecordVector<CUpdatePair> updatePairs;
+    GetUpdatePairInfoList(dirItems, arcItems, fileTimeType, updatePairs); // must be done only once!!!
+    // CUpdateProduceCallbackImp upCallback(&arcItems, callback);
+    UpdateProduce(updatePairs, actionSet, updatePairs2, NULL /* &upCallback */);
+  }
+
+  UInt32 numFiles = 0;
+  for (int i = 0; i < updatePairs2.Size(); i++)
+    if (updatePairs2[i].NewData)
+      numFiles++;
+  
+  RINOK(callback->SetNumFiles(numFiles));
+
+  
+  CArchiveUpdateCallback *updateCallbackSpec = new CArchiveUpdateCallback;
+  CMyComPtr<IArchiveUpdateCallback> updateCallback(updateCallbackSpec);
+  
+  updateCallbackSpec->ShareForWrite = shareForWrite;
+  updateCallbackSpec->StdInMode = stdInMode;
+  updateCallbackSpec->Callback = callback;
+  updateCallbackSpec->DirItems = &dirItems;
+  updateCallbackSpec->ArcItems = &arcItems;
+  updateCallbackSpec->UpdatePairs = &updatePairs2;
+
+  CMyComPtr<ISequentialOutStream> outStream;
+
+  const UString &archiveName = archivePath.GetFinalPath();
+  if (!stdOutMode)
+  {
+    UString resultPath;
+    int pos;
+    if(!NFile::NDirectory::MyGetFullPathName(archiveName, resultPath, pos))
+      throw 1417161;
+    NFile::NDirectory::CreateComplexDirectory(resultPath.Left(pos));
+  }
+
+  COutFileStream *outStreamSpec = NULL;
+  COutMultiVolStream *volStreamSpec = NULL;
+
+  if (volumesSizes.Size() == 0)
+  {
+    if (stdOutMode)
+      outStream = new CStdOutFileStream;
+    else
+    {
+      outStreamSpec = new COutFileStream;
+      outStream = outStreamSpec;
+      bool isOK = false;
+      UString realPath;
+      for (int i = 0; i < (1 << 16); i++)
+      {
+        if (archivePath.Temp)
+        {
+          if (i > 0)
+          {
+            wchar_t s[32];
+            ConvertUInt64ToString(i, s);
+            archivePath.TempPostfix = s;
+          }
+          realPath = archivePath.GetTempPath();
+        }
+        else
+          realPath = archivePath.GetFinalPath();
+        if (outStreamSpec->Create(realPath, false))
+        {
+          tempFiles.Paths.Add(realPath);
+          isOK = true;
+          break;
+        }
+        if (::GetLastError() != ERROR_FILE_EXISTS)
+          break;
+        if (!archivePath.Temp)
+          break;
+      }
+      if (!isOK)
+      {
+        errorInfo.SystemError = ::GetLastError();
+        errorInfo.FileName = realPath;
+        errorInfo.Message = L"Can not open file";
+        return E_FAIL;
+      }
+    }
+  }
+  else
+  {
+    if (stdOutMode)
+      return E_FAIL;
+    volStreamSpec = new COutMultiVolStream;
+    outStream = volStreamSpec;
+    volStreamSpec->Sizes = volumesSizes;
+    volStreamSpec->Prefix = archivePath.GetFinalPath() + UString(L".");
+    volStreamSpec->TempFiles = &tempFiles;
+    volStreamSpec->Init();
+
+    /*
+    updateCallbackSpec->VolumesSizes = volumesSizes;
+    updateCallbackSpec->VolName = archivePath.Prefix + archivePath.Name;
+    if (!archivePath.VolExtension.IsEmpty())
+      updateCallbackSpec->VolExt = UString(L'.') + archivePath.VolExtension;
+    */
+  }
+
+  RINOK(SetProperties(outArchive, compressionMethod.Properties));
+
+  if (sfxMode)
+  {
+    CInFileStream *sfxStreamSpec = new CInFileStream;
+    CMyComPtr<IInStream> sfxStream(sfxStreamSpec);
+    if (!sfxStreamSpec->Open(sfxModule))
+    {
+      errorInfo.SystemError = ::GetLastError();
+      errorInfo.Message = L"Can't open sfx module";
+      errorInfo.FileName = sfxModule;
+      return E_FAIL;
+    }
+
+    CMyComPtr<ISequentialOutStream> sfxOutStream;
+    COutFileStream *outStreamSpec = NULL;
+    if (volumesSizes.Size() == 0)
+      sfxOutStream = outStream;
+    else
+    {
+      outStreamSpec = new COutFileStream;
+      sfxOutStream = outStreamSpec;
+      UString realPath = archivePath.GetFinalPath();
+      if (!outStreamSpec->Create(realPath, false))
+      {
+        errorInfo.SystemError = ::GetLastError();
+        errorInfo.FileName = realPath;
+        errorInfo.Message = L"Can not open file";
+        return E_FAIL;
+      }
+    }
+    RINOK(CopyBlock(sfxStream, sfxOutStream));
+    if (outStreamSpec)
+    {
+      RINOK(outStreamSpec->Close());
+    }
+  }
+
+  HRESULT result = outArchive->UpdateItems(outStream, updatePairs2.Size(), updateCallback);
+  callback->Finilize();
+  RINOK(result);
+  if (outStreamSpec)
+    result = outStreamSpec->Close();
+  else if (volStreamSpec)
+    result = volStreamSpec->Close();
+  return result;
+}
+
+HRESULT EnumerateInArchiveItems(const NWildcard::CCensor &censor,
+    IInArchive *archive,
+    const UString &defaultItemName,
+    const NWindows::NFile::NFind::CFileInfoW &archiveFileInfo,
+    CObjectVector<CArcItem> &arcItems)
+{
+  arcItems.Clear();
+  UInt32 numItems;
+  RINOK(archive->GetNumberOfItems(&numItems));
+  arcItems.Reserve(numItems);
+  for (UInt32 i = 0; i < numItems; i++)
+  {
+    CArcItem ai;
+
+    RINOK(GetArchiveItemPath(archive, i, ai.Name));
+    // check it: defaultItemName !!!
+    if (ai.Name.IsEmpty())
+      ai.Name = defaultItemName;
+    RINOK(IsArchiveItemFolder(archive, i, ai.IsDir));
+    ai.Censored = censor.CheckPath(ai.Name, !ai.IsDir);
+    RINOK(GetArchiveItemFileTime(archive, i, archiveFileInfo.MTime, ai.MTime));
+
+    {
+      CPropVariant prop;
+      RINOK(archive->GetProperty(i, kpidSize, &prop));
+      ai.SizeDefined = (prop.vt != VT_EMPTY);
+      if (ai.SizeDefined)
+        ai.Size = ConvertPropVariantToUInt64(prop);
+    }
+
+    {
+      CPropVariant prop;
+      RINOK(archive->GetProperty(i, kpidTimeType, &prop));
+      if (prop.vt == VT_UI4)
+      {
+        ai.TimeType = (int)(NFileTimeType::EEnum)prop.ulVal;
+        switch(ai.TimeType)
+        {
+          case NFileTimeType::kWindows:
+          case NFileTimeType::kUnix:
+          case NFileTimeType::kDOS:
+            break;
+          default:
+            return E_FAIL;
+        }
+      }
+    }
+
+    ai.IndexInServer = i;
+    arcItems.Add(ai);
+  }
+  return S_OK;
+}
+
+
+static HRESULT UpdateWithItemLists(
+    CCodecs *codecs,
+    CUpdateOptions &options,
+    IInArchive *archive,
+    const CObjectVector<CArcItem> &arcItems,
+    CDirItems &dirItems,
+    CTempFiles &tempFiles,
+    CUpdateErrorInfo &errorInfo,
+    IUpdateCallbackUI2 *callback)
+{
+  for(int i = 0; i < options.Commands.Size(); i++)
+  {
+    CUpdateArchiveCommand &command = options.Commands[i];
+    if (options.StdOutMode)
+    {
+      RINOK(callback->StartArchive(0, archive != 0));
+    }
+    else
+    {
+      RINOK(callback->StartArchive(command.ArchivePath.GetFinalPath(),
+          i == 0 && options.UpdateArchiveItself && archive != 0));
+    }
+
+    RINOK(Compress(
+        codecs,
+        command.ActionSet, archive,
+        options.MethodMode,
+        command.ArchivePath,
+        arcItems,
+        options.OpenShareForWrite,
+        options.StdInMode,
+        /* options.StdInFileName, */
+        options.StdOutMode,
+        dirItems,
+        options.SfxMode, options.SfxModule,
+        options.VolumesSizes,
+        tempFiles,
+        errorInfo, callback));
+
+    RINOK(callback->FinishArchive());
+  }
+  return S_OK;
+}
+
+#ifdef _WIN32
+class CCurrentDirRestorer
+{
+  UString m_CurrentDirectory;
+public:
+  CCurrentDirRestorer()
+    { NFile::NDirectory::MyGetCurrentDirectory(m_CurrentDirectory); }
+  ~CCurrentDirRestorer()
+    { RestoreDirectory();}
+  bool RestoreDirectory()
+    { return BOOLToBool(NFile::NDirectory::MySetCurrentDirectory(m_CurrentDirectory)); }
+};
+#endif
+
+struct CEnumDirItemUpdateCallback: public IEnumDirItemCallback
+{
+  IUpdateCallbackUI2 *Callback;
+  HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, const wchar_t *path)
+  {
+    return Callback->ScanProgress(numFolders, numFiles, path);
+  }
+};
+
+#ifdef _WIN32
+typedef ULONG (FAR PASCAL MY_MAPISENDDOCUMENTS)(
+  ULONG_PTR ulUIParam,
+  LPSTR lpszDelimChar,
+  LPSTR lpszFilePaths,
+  LPSTR lpszFileNames,
+  ULONG ulReserved
+);
+typedef MY_MAPISENDDOCUMENTS FAR *MY_LPMAPISENDDOCUMENTS;
+#endif
+
+HRESULT UpdateArchive(
+    CCodecs *codecs,
+    const NWildcard::CCensor &censor,
+    CUpdateOptions &options,
+    CUpdateErrorInfo &errorInfo,
+    IOpenCallbackUI *openCallback,
+    IUpdateCallbackUI2 *callback)
+{
+  if (options.StdOutMode && options.EMailMode)
+    return E_FAIL;
+
+  if (options.VolumesSizes.Size() > 0 && (options.EMailMode || options.SfxMode))
+    return E_NOTIMPL;
+
+  if (options.SfxMode)
+  {
+    CProperty property;
+    property.Name = L"rsfx";
+    property.Value = L"on";
+    options.MethodMode.Properties.Add(property);
+    if (options.SfxModule.IsEmpty())
+    {
+      errorInfo.Message = L"sfx file is not specified";
+      return E_FAIL;
+    }
+    UString name = options.SfxModule;
+    if (!NDirectory::MySearchPath(NULL, name, NULL, options.SfxModule))
+    {
+      errorInfo.Message = L"can't find specified sfx module";
+      return E_FAIL;
+    }
+  }
+
+  const UString archiveName = options.ArchivePath.GetFinalPath();
+
+  UString defaultItemName;
+  NFind::CFileInfoW archiveFileInfo;
+
+  CArchiveLink archiveLink;
+  IInArchive *archive = 0;
+  if (NFind::FindFile(archiveName, archiveFileInfo))
+  {
+    if (archiveFileInfo.IsDir())
+      throw "there is no such archive";
+    if (options.VolumesSizes.Size() > 0)
+      return E_NOTIMPL;
+    CIntVector formatIndices;
+    if (options.MethodMode.FormatIndex >= 0)
+      formatIndices.Add(options.MethodMode.FormatIndex);
+    HRESULT result = MyOpenArchive(codecs, formatIndices, archiveName, archiveLink, openCallback);
+    if (result == E_ABORT)
+      return result;
+    RINOK(callback->OpenResult(archiveName, result));
+    RINOK(result);
+    if (archiveLink.VolumePaths.Size() > 1)
+    {
+      errorInfo.SystemError = (DWORD)E_NOTIMPL;
+      errorInfo.Message = L"Updating for multivolume archives is not implemented";
+      return E_NOTIMPL;
+    }
+    archive = archiveLink.GetArchive();
+    defaultItemName = archiveLink.GetDefaultItemName();
+  }
+  else
+  {
+    /*
+    if (archiveType.IsEmpty())
+      throw "type of archive is not specified";
+    */
+  }
+
+  CDirItems dirItems;
+  if (options.StdInMode)
+  {
+    CDirItem di;
+    di.Name = options.StdInFileName;
+    di.Size = (UInt64)(Int64)-1;
+    di.Attrib = 0;
+    NTime::GetCurUtcFileTime(di.MTime);
+    di.CTime = di.ATime = di.MTime;
+    dirItems.Items.Add(di);
+  }
+  else
+  {
+    bool needScanning = false;
+    for(int i = 0; i < options.Commands.Size(); i++)
+      if (options.Commands[i].ActionSet.NeedScanning())
+        needScanning = true;
+    if (needScanning)
+    {
+      CEnumDirItemUpdateCallback enumCallback;
+      enumCallback.Callback = callback;
+      RINOK(callback->StartScanning());
+      UStringVector errorPaths;
+      CRecordVector<DWORD> errorCodes;
+      HRESULT res = EnumerateItems(censor, dirItems, &enumCallback, errorPaths, errorCodes);
+      for (int i = 0; i < errorPaths.Size(); i++)
+      {
+        RINOK(callback->CanNotFindError(errorPaths[i], errorCodes[i]));
+      }
+      if (res != S_OK)
+      {
+        if (res != E_ABORT)
+          errorInfo.Message = L"Scanning error";
+        // errorInfo.FileName = errorPath;
+        return res;
+      }
+      RINOK(callback->FinishScanning());
+    }
+  }
+
+  UString tempDirPrefix;
+  bool usesTempDir = false;
+  
+  #ifdef _WIN32
+  NDirectory::CTempDirectoryW tempDirectory;
+  if (options.EMailMode && options.EMailRemoveAfter)
+  {
+    tempDirectory.Create(kTempFolderPrefix);
+    tempDirPrefix = tempDirectory.GetPath();
+    NormalizeDirPathPrefix(tempDirPrefix);
+    usesTempDir = true;
+  }
+  #endif
+
+  CTempFiles tempFiles;
+
+  bool createTempFile = false;
+  if(!options.StdOutMode && options.UpdateArchiveItself)
+  {
+    CArchivePath &ap = options.Commands[0].ArchivePath;
+    ap = options.ArchivePath;
+    // if ((archive != 0 && !usesTempDir) || !options.WorkingDir.IsEmpty())
+    if ((archive != 0 || !options.WorkingDir.IsEmpty()) && !usesTempDir && options.VolumesSizes.Size() == 0)
+    {
+      createTempFile = true;
+      ap.Temp = true;
+      if (!options.WorkingDir.IsEmpty())
+      {
+        ap.TempPrefix = options.WorkingDir;
+        NormalizeDirPathPrefix(ap.TempPrefix);
+      }
+    }
+  }
+
+  for(int i = 0; i < options.Commands.Size(); i++)
+  {
+    CArchivePath &ap = options.Commands[i].ArchivePath;
+    if (usesTempDir)
+    {
+      // Check it
+      ap.Prefix = tempDirPrefix;
+      // ap.Temp = true;
+      // ap.TempPrefix = tempDirPrefix;
+    }
+    if (i > 0 || !createTempFile)
+    {
+      const UString &path = ap.GetFinalPath();
+      if (NFind::DoesFileExist(path))
+      {
+        errorInfo.SystemError = 0;
+        errorInfo.Message = L"File already exists";
+        errorInfo.FileName = path;
+        return E_FAIL;
+      }
+    }
+  }
+
+  CObjectVector<CArcItem> arcItems;
+  if (archive != NULL)
+  {
+    RINOK(EnumerateInArchiveItems(censor,
+        archive, defaultItemName, archiveFileInfo, arcItems));
+  }
+
+  RINOK(UpdateWithItemLists(codecs, options, archive, arcItems, dirItems,
+      tempFiles, errorInfo, callback));
+
+  if (archive != NULL)
+  {
+    RINOK(archiveLink.Close());
+    archiveLink.Release();
+  }
+
+  tempFiles.Paths.Clear();
+  if(createTempFile)
+  {
+    try
+    {
+      CArchivePath &ap = options.Commands[0].ArchivePath;
+      const UString &tempPath = ap.GetTempPath();
+      if (archive != NULL)
+        if (!NDirectory::DeleteFileAlways(archiveName))
+        {
+          errorInfo.SystemError = ::GetLastError();
+          errorInfo.Message = L"delete file error";
+          errorInfo.FileName = archiveName;
+          return E_FAIL;
+        }
+      if (!NDirectory::MyMoveFile(tempPath, archiveName))
+      {
+        errorInfo.SystemError = ::GetLastError();
+        errorInfo.Message = L"move file error";
+        errorInfo.FileName = tempPath;
+        errorInfo.FileName2 = archiveName;
+        return E_FAIL;
+      }
+    }
+    catch(...)
+    {
+      throw;
+    }
+  }
+
+  #ifdef _WIN32
+  if (options.EMailMode)
+  {
+    NDLL::CLibrary mapiLib;
+    if (!mapiLib.Load(TEXT("Mapi32.dll")))
+    {
+      errorInfo.SystemError = ::GetLastError();
+      errorInfo.Message = L"can not load Mapi32.dll";
+      return E_FAIL;
+    }
+    MY_LPMAPISENDDOCUMENTS fnSend = (MY_LPMAPISENDDOCUMENTS)
+        mapiLib.GetProcAddress("MAPISendDocuments");
+    if (fnSend == 0)
+    {
+      errorInfo.SystemError = ::GetLastError();
+      errorInfo.Message = L"can not find MAPISendDocuments function";
+      return E_FAIL;
+    }
+    UStringVector fullPaths;
+    int i;
+    for(i = 0; i < options.Commands.Size(); i++)
+    {
+      CArchivePath &ap = options.Commands[i].ArchivePath;
+      UString arcPath;
+      if(!NFile::NDirectory::MyGetFullPathName(ap.GetFinalPath(), arcPath))
+      {
+        errorInfo.SystemError = ::GetLastError();
+        return E_FAIL;
+      }
+      fullPaths.Add(arcPath);
+    }
+    CCurrentDirRestorer curDirRestorer;
+    for(i = 0; i < fullPaths.Size(); i++)
+    {
+      UString arcPath = fullPaths[i];
+      UString fileName = ExtractFileNameFromPath(arcPath);
+      AString path = GetAnsiString(arcPath);
+      AString name = GetAnsiString(fileName);
+      // Warning!!! MAPISendDocuments function changes Current directory
+      fnSend(0, ";", (LPSTR)(LPCSTR)path, (LPSTR)(LPCSTR)name, 0);
+    }
+  }
+  #endif
+  return S_OK;
+}
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/Update.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/Update.h
new file mode 100644
index 0000000..46547bf
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/Update.h
@@ -0,0 +1,166 @@
+// Update.h
+
+#ifndef __UPDATE_H
+#define __UPDATE_H
+
+#include "Common/Wildcard.h"
+#include "Windows/FileFind.h"
+#include "../../Archive/IArchive.h"
+
+#include "UpdateAction.h"
+#include "ArchiveOpenCallback.h"
+#include "UpdateCallback.h"
+#include "Property.h"
+#include "LoadCodecs.h"
+
+struct CArchivePath
+{
+  UString Prefix;   // path(folder) prefix including slash
+  UString Name; // base name
+  UString BaseExtension; // archive type extension or "exe" extension
+  UString VolExtension;  // archive type extension for volumes
+
+  bool Temp;
+  UString TempPrefix;  // path(folder) for temp location
+  UString TempPostfix;
+
+  CArchivePath(): Temp(false) {};
+  
+  void ParseFromPath(const UString &path)
+  {
+    SplitPathToParts(path, Prefix, Name);
+    if (Name.IsEmpty())
+      return;
+    int dotPos = Name.ReverseFind(L'.');
+    if (dotPos <= 0)
+      return;
+    if (dotPos == Name.Length() - 1)
+    {
+      Name = Name.Left(dotPos);
+      BaseExtension.Empty();
+      return;
+    }
+    if (BaseExtension.CompareNoCase(Name.Mid(dotPos + 1)) == 0)
+    {
+      BaseExtension = Name.Mid(dotPos + 1);
+      Name = Name.Left(dotPos);
+    }
+    else
+      BaseExtension.Empty();
+  }
+
+  UString GetPathWithoutExt() const
+  {
+    return Prefix + Name;
+  }
+
+  UString GetFinalPath() const
+  {
+    UString path = GetPathWithoutExt();
+    if (!BaseExtension.IsEmpty())
+      path += UString(L'.') + BaseExtension;
+    return path;
+  }
+
+  
+  UString GetTempPath() const
+  {
+    UString path = TempPrefix + Name;
+    if (!BaseExtension.IsEmpty())
+      path += UString(L'.') + BaseExtension;
+    path += L".tmp";
+    path += TempPostfix;
+    return path;
+  }
+};
+
+struct CUpdateArchiveCommand
+{
+  UString UserArchivePath;
+  CArchivePath ArchivePath;
+  NUpdateArchive::CActionSet ActionSet;
+};
+
+struct CCompressionMethodMode
+{
+  int FormatIndex;
+  CObjectVector<CProperty> Properties;
+  CCompressionMethodMode(): FormatIndex(-1) {}
+};
+
+struct CUpdateOptions
+{
+  CCompressionMethodMode MethodMode;
+
+  CObjectVector<CUpdateArchiveCommand> Commands;
+  bool UpdateArchiveItself;
+  CArchivePath ArchivePath;
+  
+  bool SfxMode;
+  UString SfxModule;
+  
+  bool OpenShareForWrite;
+
+  bool StdInMode;
+  UString StdInFileName;
+  bool StdOutMode;
+  
+  bool EMailMode;
+  bool EMailRemoveAfter;
+  UString EMailAddress;
+
+  UString WorkingDir;
+
+  bool Init(const CCodecs *codecs, const CIntVector &formatIndices, const UString &arcPath);
+
+  CUpdateOptions():
+    UpdateArchiveItself(true),
+    SfxMode(false),
+    StdInMode(false),
+    StdOutMode(false),
+    EMailMode(false),
+    EMailRemoveAfter(false),
+    OpenShareForWrite(false)
+      {};
+  CRecordVector<UInt64> VolumesSizes;
+};
+
+struct CErrorInfo
+{
+  DWORD SystemError;
+  UString FileName;
+  UString FileName2;
+  UString Message;
+  // UStringVector ErrorPaths;
+  // CRecordVector<DWORD> ErrorCodes;
+  CErrorInfo(): SystemError(0) {};
+};
+
+struct CUpdateErrorInfo: public CErrorInfo
+{
+};
+
+#define INTERFACE_IUpdateCallbackUI2(x) \
+  INTERFACE_IUpdateCallbackUI(x) \
+  virtual HRESULT OpenResult(const wchar_t *name, HRESULT result) x; \
+  virtual HRESULT StartScanning() x; \
+  virtual HRESULT ScanProgress(UInt64 numFolders, UInt64 numFiles, const wchar_t *path) x; \
+  virtual HRESULT CanNotFindError(const wchar_t *name, DWORD systemError) x; \
+  virtual HRESULT FinishScanning() x; \
+  virtual HRESULT StartArchive(const wchar_t *name, bool updating) x; \
+  virtual HRESULT FinishArchive() x; \
+
+struct IUpdateCallbackUI2: public IUpdateCallbackUI
+{
+  INTERFACE_IUpdateCallbackUI2(=0)
+};
+
+HRESULT UpdateArchive(
+    CCodecs *codecs,
+    const NWildcard::CCensor &censor,
+    CUpdateOptions &options,
+    CUpdateErrorInfo &errorInfo,
+    IOpenCallbackUI *openCallback,
+    IUpdateCallbackUI2 *callback);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateAction.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateAction.cpp
new file mode 100644
index 0000000..845384f
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateAction.cpp
@@ -0,0 +1,64 @@
+// UpdateAction.cpp
+
+#include "StdAfx.h"
+
+#include "UpdateAction.h"
+
+namespace NUpdateArchive {
+
+const CActionSet kAddActionSet =
+{
+  NPairAction::kCopy,
+  NPairAction::kCopy,
+  NPairAction::kCompress,
+  NPairAction::kCompress,
+  NPairAction::kCompress,
+  NPairAction::kCompress,
+  NPairAction::kCompress
+};
+
+const CActionSet kUpdateActionSet =
+{
+  NPairAction::kCopy,
+  NPairAction::kCopy,
+  NPairAction::kCompress,
+  NPairAction::kCopy,
+  NPairAction::kCompress,
+  NPairAction::kCopy,
+  NPairAction::kCompress
+};
+
+const CActionSet kFreshActionSet =
+{
+  NPairAction::kCopy,
+  NPairAction::kCopy,
+  NPairAction::kIgnore,
+  NPairAction::kCopy,
+  NPairAction::kCompress,
+  NPairAction::kCopy,
+  NPairAction::kCompress
+};
+
+const CActionSet kSynchronizeActionSet =
+{
+  NPairAction::kCopy,
+  NPairAction::kIgnore,
+  NPairAction::kCompress,
+  NPairAction::kCopy,
+  NPairAction::kCompress,
+  NPairAction::kCopy,
+  NPairAction::kCompress,
+};
+
+const CActionSet kDeleteActionSet =
+{
+  NPairAction::kCopy,
+  NPairAction::kIgnore,
+  NPairAction::kIgnore,
+  NPairAction::kIgnore,
+  NPairAction::kIgnore,
+  NPairAction::kIgnore,
+  NPairAction::kIgnore
+};
+
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateAction.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateAction.h
new file mode 100644
index 0000000..7da5ff2
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateAction.h
@@ -0,0 +1,57 @@
+// UpdateAction.h
+
+#ifndef __UPDATE_ACTION_H
+#define __UPDATE_ACTION_H
+
+namespace NUpdateArchive {
+
+  namespace NPairState
+  {
+    const int kNumValues = 7;
+    enum EEnum
+    {
+      kNotMasked = 0,
+      kOnlyInArchive,
+      kOnlyOnDisk,
+      kNewInArchive,
+      kOldInArchive,
+      kSameFiles,
+      kUnknowNewerFiles
+    };
+  }
+  namespace NPairAction
+  {
+    enum EEnum
+    {
+      kIgnore = 0,
+      kCopy,
+      kCompress,
+      kCompressAsAnti
+    };
+  }
+  struct CActionSet
+  {
+    NPairAction::EEnum StateActions[NPairState::kNumValues];
+    bool NeedScanning() const
+    {
+      int i;
+      for (i = 0; i < NPairState::kNumValues; i++)
+        if (StateActions[i] == NPairAction::kCompress)
+          return true;
+      for (i = 1; i < NPairState::kNumValues; i++)
+        if (StateActions[i] != NPairAction::kIgnore)
+          return true;
+      return false;
+    }
+  };
+  extern const CActionSet kAddActionSet;
+  extern const CActionSet kUpdateActionSet;
+  extern const CActionSet kFreshActionSet;
+  extern const CActionSet kSynchronizeActionSet;
+  extern const CActionSet kDeleteActionSet;
+};
+
+
+#endif
+
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateCallback.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateCallback.cpp
new file mode 100644
index 0000000..dcca2a1
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateCallback.cpp
@@ -0,0 +1,242 @@
+// UpdateCallback.cpp
+
+#include "StdAfx.h"
+
+#include "UpdateCallback.h"
+
+#include "Common/StringConvert.h"
+#include "Common/IntToString.h"
+#include "Common/Defs.h"
+#include "Common/ComTry.h"
+
+#include "Windows/PropVariant.h"
+
+#include "../../Common/FileStreams.h"
+
+using namespace NWindows;
+
+CArchiveUpdateCallback::CArchiveUpdateCallback():
+  Callback(0),
+  ShareForWrite(false),
+  StdInMode(false),
+  DirItems(0),
+  ArcItems(0),
+  UpdatePairs(0),
+  NewNames(0)
+  {}
+
+
+STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 size)
+{
+  COM_TRY_BEGIN
+  return Callback->SetTotal(size);
+  COM_TRY_END
+}
+
+STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 *completeValue)
+{
+  COM_TRY_BEGIN
+  return Callback->SetCompleted(completeValue);
+  COM_TRY_END
+}
+
+STDMETHODIMP CArchiveUpdateCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)
+{
+  COM_TRY_BEGIN
+  return Callback->SetRatioInfo(inSize, outSize);
+  COM_TRY_END
+}
+
+
+/*
+STATPROPSTG kProperties[] =
+{
+  { NULL, kpidPath, VT_BSTR},
+  { NULL, kpidIsDir, VT_BOOL},
+  { NULL, kpidSize, VT_UI8},
+  { NULL, kpidCTime, VT_FILETIME},
+  { NULL, kpidATime, VT_FILETIME},
+  { NULL, kpidMTime, VT_FILETIME},
+  { NULL, kpidAttrib, VT_UI4},
+  { NULL, kpidIsAnti, VT_BOOL}
+};
+
+STDMETHODIMP CArchiveUpdateCallback::EnumProperties(IEnumSTATPROPSTG **)
+{
+  return CStatPropEnumerator::CreateEnumerator(kProperties, sizeof(kProperties) / sizeof(kProperties[0]), enumerator);
+}
+*/
+
+STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 index,
+      Int32 *newData, Int32 *newProps, UInt32 *indexInArchive)
+{
+  COM_TRY_BEGIN
+  RINOK(Callback->CheckBreak());
+  const CUpdatePair2 &up = (*UpdatePairs)[index];
+  if (newData != NULL) *newData = BoolToInt(up.NewData);
+  if (newProps != NULL) *newProps = BoolToInt(up.NewProps);
+  if (indexInArchive != NULL)
+  {
+    *indexInArchive = (UInt32)-1;
+    if (up.ExistInArchive())
+      *indexInArchive = (ArcItems == 0) ? up.ArcIndex : (*ArcItems)[up.ArcIndex].IndexInServer;
+  }
+  return S_OK;
+  COM_TRY_END
+}
+
+STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
+{
+  COM_TRY_BEGIN
+  const CUpdatePair2 &up = (*UpdatePairs)[index];
+  NWindows::NCOM::CPropVariant prop;
+  
+  if (propID == kpidIsAnti)
+  {
+    prop = up.IsAnti;
+    prop.Detach(value);
+    return S_OK;
+  }
+
+  if (up.IsAnti)
+  {
+    switch(propID)
+    {
+      case kpidIsDir:
+      case kpidPath:
+        break;
+      case kpidSize:
+        prop = (UInt64)0;
+        prop.Detach(value);
+        return S_OK;
+      default:
+        prop.Detach(value);
+        return S_OK;
+    }
+  }
+  
+  if (up.ExistOnDisk())
+  {
+    const CDirItem &di = DirItems->Items[up.DirIndex];
+    switch(propID)
+    {
+      case kpidPath:  prop = DirItems->GetLogPath(up.DirIndex); break;
+      case kpidIsDir:  prop = di.IsDir(); break;
+      case kpidSize:  prop = di.Size; break;
+      case kpidAttrib:  prop = di.Attrib; break;
+      case kpidCTime:  prop = di.CTime; break;
+      case kpidATime:  prop = di.ATime; break;
+      case kpidMTime:  prop = di.MTime; break;
+    }
+  }
+  else
+  {
+    if (propID == kpidPath)
+    {
+      if (up.NewNameIndex >= 0)
+      {
+        prop = (*NewNames)[up.NewNameIndex];
+        prop.Detach(value);
+        return S_OK;
+      }
+    }
+    if (up.ExistInArchive() && Archive)
+    {
+      UInt32 indexInArchive;
+      if (ArcItems == 0)
+        indexInArchive = up.ArcIndex;
+      else
+        indexInArchive = (*ArcItems)[up.ArcIndex].IndexInServer;
+      return Archive->GetProperty(indexInArchive, propID, value);
+    }
+  }
+  prop.Detach(value);
+  return S_OK;
+  COM_TRY_END
+}
+
+STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream)
+{
+  COM_TRY_BEGIN
+  const CUpdatePair2 &up = (*UpdatePairs)[index];
+  if (!up.NewData)
+    return E_FAIL;
+  
+  RINOK(Callback->CheckBreak());
+  RINOK(Callback->Finilize());
+
+  if (up.IsAnti)
+  {
+    return Callback->GetStream((*ArcItems)[up.ArcIndex].Name, true);
+  }
+  const CDirItem &di = DirItems->Items[up.DirIndex];
+  RINOK(Callback->GetStream(DirItems->GetLogPath(up.DirIndex), false));
+ 
+  if (di.IsDir())
+    return S_OK;
+
+  if (StdInMode)
+  {
+    CStdInFileStream *inStreamSpec = new CStdInFileStream;
+    CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
+    *inStream = inStreamLoc.Detach();
+  }
+  else
+  {
+    CInFileStream *inStreamSpec = new CInFileStream;
+    CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
+    const UString path = DirItems->GetPhyPath(up.DirIndex);
+    if (!inStreamSpec->OpenShared(path, ShareForWrite))
+    {
+      return Callback->OpenFileError(path, ::GetLastError());
+    }
+    *inStream = inStreamLoc.Detach();
+  }
+  return S_OK;
+  COM_TRY_END
+}
+
+STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 operationResult)
+{
+  COM_TRY_BEGIN
+  return Callback->SetOperationResult(operationResult);
+  COM_TRY_END
+}
+
+STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size)
+{
+  if (VolumesSizes.Size() == 0)
+    return S_FALSE;
+  if (index >= (UInt32)VolumesSizes.Size())
+    index = VolumesSizes.Size() - 1;
+  *size = VolumesSizes[index];
+  return S_OK;
+}
+
+STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream)
+{
+  COM_TRY_BEGIN
+  wchar_t temp[32];
+  ConvertUInt64ToString(index + 1, temp);
+  UString res = temp;
+  while (res.Length() < 2)
+    res = UString(L'0') + res;
+  UString fileName = VolName;
+  fileName += L'.';
+  fileName += res;
+  fileName += VolExt;
+  COutFileStream *streamSpec = new COutFileStream;
+  CMyComPtr<ISequentialOutStream> streamLoc(streamSpec);
+  if (!streamSpec->Create(fileName, false))
+    return ::GetLastError();
+  *volumeStream = streamLoc.Detach();
+  return S_OK;
+  COM_TRY_END
+}
+
+STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)
+{
+  COM_TRY_BEGIN
+  return Callback->CryptoGetTextPassword2(passwordIsDefined, password);
+  COM_TRY_END
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateCallback.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateCallback.h
new file mode 100644
index 0000000..2a814b3
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateCallback.h
@@ -0,0 +1,70 @@
+// UpdateCallback.h
+
+#ifndef __UPDATECALLBACK_H
+#define __UPDATECALLBACK_H
+
+#include "Common/MyCom.h"
+#include "Common/MyString.h"
+
+#include "../../IPassword.h"
+#include "../../ICoder.h"
+
+#include "../Common/UpdatePair.h"
+#include "../Common/UpdateProduce.h"
+
+#define INTERFACE_IUpdateCallbackUI(x) \
+  virtual HRESULT SetTotal(UInt64 size) x; \
+  virtual HRESULT SetCompleted(const UInt64 *completeValue) x; \
+  virtual HRESULT SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize) x; \
+  virtual HRESULT CheckBreak() x; \
+  virtual HRESULT Finilize() x; \
+  virtual HRESULT SetNumFiles(UInt64 numFiles) x; \
+  virtual HRESULT GetStream(const wchar_t *name, bool isAnti) x; \
+  virtual HRESULT OpenFileError(const wchar_t *name, DWORD systemError) x; \
+  virtual HRESULT SetOperationResult(Int32 operationResult) x; \
+  virtual HRESULT CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password) x; \
+  // virtual HRESULT ShowDeleteFile(const wchar_t *name) x; \
+  // virtual HRESULT CloseProgress() { return S_OK; };
+
+struct IUpdateCallbackUI
+{
+  INTERFACE_IUpdateCallbackUI(=0)
+};
+
+class CArchiveUpdateCallback:
+  public IArchiveUpdateCallback2,
+  public ICryptoGetTextPassword2,
+  public ICompressProgressInfo,
+  public CMyUnknownImp
+{
+public:
+  MY_UNKNOWN_IMP3(
+      IArchiveUpdateCallback2,
+      ICryptoGetTextPassword2,
+      ICompressProgressInfo)
+
+  STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
+
+  INTERFACE_IArchiveUpdateCallback2(;)
+
+  STDMETHOD(CryptoGetTextPassword2)(Int32 *passwordIsDefined, BSTR *password);
+
+public:
+  CRecordVector<UInt64> VolumesSizes;
+  UString VolName;
+  UString VolExt;
+
+  IUpdateCallbackUI *Callback;
+
+  bool ShareForWrite;
+  bool StdInMode;
+  const CDirItems *DirItems;
+  const CObjectVector<CArcItem> *ArcItems;
+  const CRecordVector<CUpdatePair2> *UpdatePairs;
+  const UStringVector *NewNames;
+  CMyComPtr<IInArchive> Archive;
+
+  CArchiveUpdateCallback();
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdatePair.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdatePair.cpp
new file mode 100644
index 0000000..f6727cb
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdatePair.cpp
@@ -0,0 +1,161 @@
+// UpdatePair.cpp
+
+#include "StdAfx.h"
+
+#include <time.h>
+
+#include "Common/Defs.h"
+#include "Common/Wildcard.h"
+#include "Windows/Time.h"
+
+#include "UpdatePair.h"
+#include "SortUtils.h"
+
+using namespace NWindows;
+using namespace NTime;
+
+static int MyCompareTime(NFileTimeType::EEnum fileTimeType, const FILETIME &time1, const FILETIME &time2)
+{
+  switch(fileTimeType)
+  {
+    case NFileTimeType::kWindows:
+      return ::CompareFileTime(&time1, &time2);
+    case NFileTimeType::kUnix:
+      {
+        UInt32 unixTime1, unixTime2;
+        FileTimeToUnixTime(time1, unixTime1);
+        FileTimeToUnixTime(time2, unixTime2);
+        return MyCompare(unixTime1, unixTime2);
+      }
+    case NFileTimeType::kDOS:
+      {
+        UInt32 dosTime1, dosTime2;
+        FileTimeToDosTime(time1, dosTime1);
+        FileTimeToDosTime(time2, dosTime2);
+        return MyCompare(dosTime1, dosTime2);
+      }
+  }
+  throw 4191618;
+}
+
+static const wchar_t *kDuplicateFileNameMessage = L"Duplicate filename:";
+static const wchar_t *kNotCensoredCollisionMessaged = L"Internal file name collision (file on disk, file in archive):";
+
+static void ThrowError(const UString &message, const UString &s1, const UString &s2)
+{
+  UString m = message;
+  m += L'\n';
+  m += s1;
+  m += L'\n';
+  m += s2;
+  throw m;
+}
+
+static void TestDuplicateString(const UStringVector &strings, const CIntVector &indices)
+{
+  for(int i = 0; i + 1 < indices.Size(); i++)
+    if (CompareFileNames(strings[indices[i]], strings[indices[i + 1]]) == 0)
+      ThrowError(kDuplicateFileNameMessage, strings[indices[i]], strings[indices[i + 1]]);
+}
+
+void GetUpdatePairInfoList(
+    const CDirItems &dirItems,
+    const CObjectVector<CArcItem> &arcItems,
+    NFileTimeType::EEnum fileTimeType,
+    CRecordVector<CUpdatePair> &updatePairs)
+{
+  CIntVector dirIndices, arcIndices;
+  
+  int numDirItems = dirItems.Items.Size();
+  int numArcItems = arcItems.Size();
+  
+  
+  {
+    UStringVector arcNames;
+    arcNames.Reserve(numArcItems);
+    for (int i = 0; i < numArcItems; i++)
+      arcNames.Add(arcItems[i].Name);
+    SortFileNames(arcNames, arcIndices);
+    TestDuplicateString(arcNames, arcIndices);
+  }
+
+  UStringVector dirNames;
+  {
+    dirNames.Reserve(numDirItems);
+    for (int i = 0; i < numDirItems; i++)
+      dirNames.Add(dirItems.GetLogPath(i));
+    SortFileNames(dirNames, dirIndices);
+    TestDuplicateString(dirNames, dirIndices);
+  }
+  
+  int dirIndex = 0, arcIndex = 0;
+  while (dirIndex < numDirItems && arcIndex < numArcItems)
+  {
+    CUpdatePair pair;
+    int dirIndex2 = dirIndices[dirIndex];
+    int arcIndex2 = arcIndices[arcIndex];
+    const CDirItem &di = dirItems.Items[dirIndex2];
+    const CArcItem &ai = arcItems[arcIndex2];
+    int compareResult = CompareFileNames(dirNames[dirIndex2], ai.Name);
+    if (compareResult < 0)
+    {
+      pair.State = NUpdateArchive::NPairState::kOnlyOnDisk;
+      pair.DirIndex = dirIndex2;
+      dirIndex++;
+    }
+    else if (compareResult > 0)
+    {
+      pair.State = ai.Censored ?
+          NUpdateArchive::NPairState::kOnlyInArchive:
+          NUpdateArchive::NPairState::kNotMasked;
+      pair.ArcIndex = arcIndex2;
+      arcIndex++;
+    }
+    else
+    {
+      if (!ai.Censored)
+        ThrowError(kNotCensoredCollisionMessaged, dirNames[dirIndex2], ai.Name);
+      pair.DirIndex = dirIndex2;
+      pair.ArcIndex = arcIndex2;
+      switch (MyCompareTime(
+          ai.TimeType != - 1 ? (NFileTimeType::EEnum)ai.TimeType : fileTimeType,
+          di.MTime, ai.MTime))
+      {
+        case -1: pair.State = NUpdateArchive::NPairState::kNewInArchive; break;
+        case 1:  pair.State = NUpdateArchive::NPairState::kOldInArchive; break;
+        default:
+          if (ai.SizeDefined)
+            if (di.Size != ai.Size)
+              pair.State = NUpdateArchive::NPairState::kUnknowNewerFiles;
+            else
+              pair.State = NUpdateArchive::NPairState::kSameFiles;
+          else
+              pair.State = NUpdateArchive::NPairState::kUnknowNewerFiles;
+      }
+      dirIndex++;
+      arcIndex++;
+    }
+    updatePairs.Add(pair);
+  }
+
+  for (; dirIndex < numDirItems; dirIndex++)
+  {
+    CUpdatePair pair;
+    pair.State = NUpdateArchive::NPairState::kOnlyOnDisk;
+    pair.DirIndex = dirIndices[dirIndex];
+    updatePairs.Add(pair);
+  }
+  
+  for (; arcIndex < numArcItems; arcIndex++)
+  {
+    CUpdatePair pair;
+    int arcIndex2 = arcIndices[arcIndex];
+    pair.State = arcItems[arcIndex2].Censored ?
+        NUpdateArchive::NPairState::kOnlyInArchive:
+        NUpdateArchive::NPairState::kNotMasked;
+    pair.ArcIndex = arcIndex2;
+    updatePairs.Add(pair);
+  }
+
+  updatePairs.ReserveDown();
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdatePair.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdatePair.h
new file mode 100644
index 0000000..3a33264
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdatePair.h
@@ -0,0 +1,25 @@
+// UpdatePair.h
+
+#ifndef __UPDATE_PAIR_H
+#define __UPDATE_PAIR_H
+
+#include "DirItem.h"
+#include "UpdateAction.h"
+
+#include "../../Archive/IArchive.h"
+
+struct CUpdatePair
+{
+  NUpdateArchive::NPairState::EEnum State;
+  int ArcIndex;
+  int DirIndex;
+  CUpdatePair(): ArcIndex(-1), DirIndex(-1) {}
+};
+
+void GetUpdatePairInfoList(
+    const CDirItems &dirItems,
+    const CObjectVector<CArcItem> &arcItems,
+    NFileTimeType::EEnum fileTimeType,
+    CRecordVector<CUpdatePair> &updatePairs);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateProduce.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateProduce.cpp
new file mode 100644
index 0000000..c21db3b
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateProduce.cpp
@@ -0,0 +1,58 @@
+// UpdateProduce.cpp
+
+#include "StdAfx.h"
+
+#include "UpdateProduce.h"
+
+using namespace NUpdateArchive;
+
+static const char *kUpdateActionSetCollision = "Internal collision in update action set";
+
+void UpdateProduce(
+    const CRecordVector<CUpdatePair> &updatePairs,
+    const CActionSet &actionSet,
+    CRecordVector<CUpdatePair2> &operationChain,
+    IUpdateProduceCallback *callback)
+{
+  for (int i = 0; i < updatePairs.Size(); i++)
+  {
+    const CUpdatePair &pair = updatePairs[i];
+
+    CUpdatePair2 up2;
+    up2.IsAnti = false;
+    up2.DirIndex = pair.DirIndex;
+    up2.ArcIndex = pair.ArcIndex;
+    up2.NewData = up2.NewProps = true;
+    
+    switch(actionSet.StateActions[pair.State])
+    {
+      case NPairAction::kIgnore:
+        /*
+        if (pair.State != NPairState::kOnlyOnDisk)
+          IgnoreArchiveItem(m_ArchiveItems[pair.ArcIndex]);
+        // cout << "deleting";
+        */
+        if (callback)
+          callback->ShowDeleteFile(pair.ArcIndex);
+        continue;
+
+      case NPairAction::kCopy:
+        if (pair.State == NPairState::kOnlyOnDisk)
+          throw kUpdateActionSetCollision;
+        up2.NewData = up2.NewProps = false;
+        break;
+      
+      case NPairAction::kCompress:
+        if (pair.State == NPairState::kOnlyInArchive ||
+            pair.State == NPairState::kNotMasked)
+          throw kUpdateActionSetCollision;
+        break;
+      
+      case NPairAction::kCompressAsAnti:
+        up2.IsAnti = true;
+        break;
+    }
+    operationChain.Add(up2);
+  }
+  operationChain.ReserveDown();
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateProduce.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateProduce.h
new file mode 100644
index 0000000..e18648c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/UpdateProduce.h
@@ -0,0 +1,35 @@
+// UpdateProduce.h
+
+#ifndef __UPDATE_PRODUCE_H
+#define __UPDATE_PRODUCE_H
+
+#include "UpdatePair.h"
+
+struct CUpdatePair2
+{
+  bool NewData;
+  bool NewProps;
+  bool IsAnti;
+  
+  int DirIndex;
+  int ArcIndex;
+  int NewNameIndex;
+
+  bool ExistOnDisk() const { return DirIndex != -1; }
+  bool ExistInArchive() const { return ArcIndex != -1; }
+
+  CUpdatePair2(): IsAnti(false), DirIndex(-1), ArcIndex(-1), NewNameIndex(-1) {}
+};
+
+struct IUpdateProduceCallback
+{
+  virtual HRESULT ShowDeleteFile(int arcIndex) = 0;
+};
+
+void UpdateProduce(
+    const CRecordVector<CUpdatePair> &updatePairs,
+    const NUpdateArchive::CActionSet &actionSet,
+    CRecordVector<CUpdatePair2> &operationChain,
+    IUpdateProduceCallback *callback);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/WorkDir.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/WorkDir.cpp
new file mode 100644
index 0000000..e97275b
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/WorkDir.cpp
@@ -0,0 +1,64 @@
+// WorkDir.cpp
+
+#include "StdAfx.h"
+
+#include "WorkDir.h"
+
+#include "Common/StringConvert.h"
+#include "Common/Wildcard.h"
+
+#include "Windows/FileName.h"
+#include "Windows/FileDir.h"
+
+static inline UINT GetCurrentCodePage()
+  { return ::AreFileApisANSI() ? CP_ACP : CP_OEMCP; }
+
+using namespace NWindows;
+using namespace NFile;
+using namespace NName;
+
+UString GetWorkDir(const NWorkDir::CInfo &workDirInfo, const UString &path)
+{
+  NWorkDir::NMode::EEnum mode = workDirInfo.Mode;
+  if (workDirInfo.ForRemovableOnly)
+  {
+    mode = NWorkDir::NMode::kCurrent;
+    UString prefix = path.Left(3);
+    if (prefix[1] == L':' && prefix[2] == L'\\')
+    {
+      UINT driveType = GetDriveType(GetSystemString(prefix, GetCurrentCodePage()));
+      if (driveType == DRIVE_CDROM || driveType == DRIVE_REMOVABLE)
+        mode = workDirInfo.Mode;
+    }
+    /*
+    CParsedPath parsedPath;
+    parsedPath.ParsePath(archiveName);
+    UINT driveType = GetDriveType(parsedPath.Prefix);
+    if ((driveType != DRIVE_CDROM) && (driveType != DRIVE_REMOVABLE))
+      mode = NZipSettings::NWorkDir::NMode::kCurrent;
+    */
+  }
+  switch(mode)
+  {
+    case NWorkDir::NMode::kCurrent:
+    {
+      return ExtractDirPrefixFromPath(path);
+    }
+    case NWorkDir::NMode::kSpecified:
+    {
+      UString tempDir = workDirInfo.Path;
+      NormalizeDirPathPrefix(tempDir);
+      return tempDir;
+    }
+    default:
+    {
+      UString tempDir;
+      if(!NFile::NDirectory::MyGetTempPath(tempDir))
+        throw 141717;
+      return tempDir;
+    }
+  }
+}
+
+
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/WorkDir.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/WorkDir.h
new file mode 100644
index 0000000..0643d67
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/WorkDir.h
@@ -0,0 +1,10 @@
+// WorkDir.h
+
+#ifndef __WORKDIR_H
+#define __WORKDIR_H
+
+#include "ZipRegistry.h"
+
+UString GetWorkDir(const NWorkDir::CInfo &workDirInfo, const UString &path);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ZipRegistry.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ZipRegistry.h
new file mode 100644
index 0000000..30be8d8
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Common/ZipRegistry.h
@@ -0,0 +1,98 @@
+// ZipRegistry.h
+
+#ifndef __ZIPREGISTRY_H
+#define __ZIPREGISTRY_H
+
+#include "Common/MyString.h"
+#include "Common/Types.h"
+#include "ExtractMode.h"
+
+namespace NExtract
+{
+  struct CInfo
+  {
+    NPathMode::EEnum PathMode;
+    NOverwriteMode::EEnum OverwriteMode;
+    UStringVector Paths;
+    bool ShowPassword;
+  };
+}
+
+namespace NCompression {
+  
+  struct CFormatOptions
+  {
+    CSysString FormatID;
+    UString Options;
+    UString Method;
+    UString EncryptionMethod;
+    UInt32 Level;
+    UInt32 Dictionary;
+    UInt32 Order;
+    UInt32 BlockLogSize;
+    UInt32 NumThreads;
+    void ResetForLevelChange()
+    {
+      BlockLogSize = NumThreads = Level = Dictionary = Order = UInt32(-1);
+      Method.Empty();
+      // EncryptionMethod.Empty();
+      // Options.Empty();
+    }
+    CFormatOptions() { ResetForLevelChange(); }
+  };
+
+  struct CInfo
+  {
+    UStringVector HistoryArchives;
+    UInt32 Level;
+    UString ArchiveType;
+
+    CObjectVector<CFormatOptions> FormatOptionsVector;
+
+    bool ShowPassword;
+    bool EncryptHeaders;
+  };
+}
+
+namespace NWorkDir{
+  
+  namespace NMode
+  {
+    enum EEnum
+    {
+      kSystem,
+      kCurrent,
+      kSpecified
+    };
+  }
+  struct CInfo
+  {
+    NMode::EEnum Mode;
+    UString Path;
+    bool ForRemovableOnly;
+    void SetForRemovableOnlyDefault() { ForRemovableOnly = true; }
+    void SetDefault()
+    {
+      Mode = NMode::kSystem;
+      Path.Empty();
+      SetForRemovableOnlyDefault();
+    }
+  };
+}
+
+void SaveExtractionInfo(const NExtract::CInfo &info);
+void ReadExtractionInfo(NExtract::CInfo &info);
+
+void SaveCompressionInfo(const NCompression::CInfo &info);
+void ReadCompressionInfo(NCompression::CInfo &info);
+
+void SaveWorkDirInfo(const NWorkDir::CInfo &info);
+void ReadWorkDirInfo(NWorkDir::CInfo &info);
+
+void SaveCascadedMenu(bool enabled);
+bool ReadCascadedMenu();
+
+void SaveContextMenuStatus(UInt32 value);
+bool ReadContextMenuStatus(UInt32 &value);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/ConsoleClose.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/ConsoleClose.cpp
new file mode 100644
index 0000000..d18b39e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/ConsoleClose.cpp
@@ -0,0 +1,63 @@
+// ConsoleClose.cpp
+
+#include "StdAfx.h"
+
+#include "ConsoleClose.h"
+
+static int g_BreakCounter = 0;
+static const int kBreakAbortThreshold = 2;
+
+namespace NConsoleClose {
+
+static BOOL WINAPI HandlerRoutine(DWORD ctrlType)
+{
+  if (ctrlType == CTRL_LOGOFF_EVENT)
+  {
+    // printf("\nCTRL_LOGOFF_EVENT\n");
+    return TRUE;
+  }
+
+  g_BreakCounter++;
+  if (g_BreakCounter < kBreakAbortThreshold)
+    return TRUE;
+  return FALSE;
+  /*
+  switch(ctrlType)
+  {
+    case CTRL_C_EVENT:
+    case CTRL_BREAK_EVENT:
+      if (g_BreakCounter < kBreakAbortThreshold)
+      return TRUE;
+  }
+  return FALSE;
+  */
+}
+
+bool TestBreakSignal()
+{
+  /*
+  if (g_BreakCounter > 0)
+    return true;
+  */
+  return (g_BreakCounter > 0);
+}
+
+void CheckCtrlBreak()
+{
+  if (TestBreakSignal())
+    throw CCtrlBreakException();
+}
+
+CCtrlHandlerSetter::CCtrlHandlerSetter()
+{
+  if(!SetConsoleCtrlHandler(HandlerRoutine, TRUE))
+    throw "SetConsoleCtrlHandler fails";
+}
+
+CCtrlHandlerSetter::~CCtrlHandlerSetter()
+{
+  if(!SetConsoleCtrlHandler(HandlerRoutine, FALSE))
+    throw "SetConsoleCtrlHandler fails";
+}
+
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/ConsoleClose.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/ConsoleClose.h
new file mode 100644
index 0000000..9019c4c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/ConsoleClose.h
@@ -0,0 +1,24 @@
+// ConsoleCloseUtils.h
+
+#ifndef __CONSOLECLOSEUTILS_H
+#define __CONSOLECLOSEUTILS_H
+
+namespace NConsoleClose {
+
+bool TestBreakSignal();
+
+class CCtrlHandlerSetter
+{
+public:
+  CCtrlHandlerSetter();
+  virtual ~CCtrlHandlerSetter();
+};
+
+class CCtrlBreakException
+{};
+
+void CheckCtrlBreak();
+
+}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/ExtractCallbackConsole.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/ExtractCallbackConsole.cpp
new file mode 100644
index 0000000..af65739
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/ExtractCallbackConsole.cpp
@@ -0,0 +1,228 @@
+// ExtractCallbackConsole.h
+
+#include "StdAfx.h"
+
+#include "ExtractCallbackConsole.h"
+#include "UserInputUtils.h"
+#include "ConsoleClose.h"
+
+#include "Common/Wildcard.h"
+
+#include "Windows/FileDir.h"
+#include "Windows/FileFind.h"
+#include "Windows/Time.h"
+#include "Windows/Defs.h"
+#include "Windows/PropVariant.h"
+#include "Windows/Error.h"
+#include "Windows/PropVariantConversions.h"
+
+#include "../../Common/FilePathAutoRename.h"
+
+#include "../Common/ExtractingFilePath.h"
+
+using namespace NWindows;
+using namespace NFile;
+using namespace NDirectory;
+
+static const char *kTestString    =  "Testing     ";
+static const char *kExtractString =  "Extracting  ";
+static const char *kSkipString   =  "Skipping    ";
+
+// static const char *kCantAutoRename = "can not create file with auto name\n";
+// static const char *kCantRenameFile = "can not rename existing file\n";
+// static const char *kCantDeleteOutputFile = "can not delete output file ";
+static const char *kError = "ERROR: ";
+static const char *kMemoryExceptionMessage = "Can't allocate required memory!";
+
+static const char *kProcessing = "Processing archive: ";
+static const char *kEverythingIsOk = "Everything is Ok";
+static const char *kNoFiles = "No files to process";
+
+static const char *kUnsupportedMethod = "Unsupported Method";
+static const char *kCrcFailed = "CRC Failed";
+static const char *kCrcFailedEncrypted = "CRC Failed in encrypted file. Wrong password?";
+static const char *kDataError = "Data Error";
+static const char *kDataErrorEncrypted = "Data Error in encrypted file. Wrong password?";
+static const char *kUnknownError = "Unknown Error";
+
+STDMETHODIMP CExtractCallbackConsole::SetTotal(UInt64)
+{
+  if (NConsoleClose::TestBreakSignal())
+    return E_ABORT;
+  return S_OK;
+}
+
+STDMETHODIMP CExtractCallbackConsole::SetCompleted(const UInt64 *)
+{
+  if (NConsoleClose::TestBreakSignal())
+    return E_ABORT;
+  return S_OK;
+}
+
+STDMETHODIMP CExtractCallbackConsole::AskOverwrite(
+    const wchar_t *existName, const FILETIME *, const UInt64 *,
+    const wchar_t *newName, const FILETIME *, const UInt64 *,
+    Int32 *answer)
+{
+  (*OutStream) << "file " << existName <<
+    "\nalready exists. Overwrite with " << endl;
+  (*OutStream) << newName;
+  
+  NUserAnswerMode::EEnum overwriteAnswer = ScanUserYesNoAllQuit(OutStream);
+  
+  switch(overwriteAnswer)
+  {
+    case NUserAnswerMode::kQuit:  return E_ABORT;
+    case NUserAnswerMode::kNo:     *answer = NOverwriteAnswer::kNo; break;
+    case NUserAnswerMode::kNoAll:  *answer = NOverwriteAnswer::kNoToAll; break;
+    case NUserAnswerMode::kYesAll: *answer = NOverwriteAnswer::kYesToAll; break;
+    case NUserAnswerMode::kYes:    *answer = NOverwriteAnswer::kYes; break;
+    case NUserAnswerMode::kAutoRenameAll: *answer = NOverwriteAnswer::kAutoRename; break;
+    default: return E_FAIL;
+  }
+  return S_OK;
+}
+
+STDMETHODIMP CExtractCallbackConsole::PrepareOperation(const wchar_t *name, bool /* isFolder */, Int32 askExtractMode, const UInt64 *position)
+{
+  switch (askExtractMode)
+  {
+    case NArchive::NExtract::NAskMode::kExtract: (*OutStream) << kExtractString; break;
+    case NArchive::NExtract::NAskMode::kTest:    (*OutStream) << kTestString; break;
+    case NArchive::NExtract::NAskMode::kSkip:    (*OutStream) << kSkipString; break;
+  };
+  (*OutStream) << name;
+  if (position != 0)
+    (*OutStream) << " <" << *position << ">";
+  return S_OK;
+}
+
+STDMETHODIMP CExtractCallbackConsole::MessageError(const wchar_t *message)
+{
+  (*OutStream) << message << endl;
+  NumFileErrorsInCurrentArchive++;
+  NumFileErrors++;
+  return S_OK;
+}
+
+STDMETHODIMP CExtractCallbackConsole::SetOperationResult(Int32 operationResult, bool encrypted)
+{
+  switch(operationResult)
+  {
+    case NArchive::NExtract::NOperationResult::kOK:
+      break;
+    default:
+    {
+      NumFileErrorsInCurrentArchive++;
+      NumFileErrors++;
+      (*OutStream) << "     ";
+      switch(operationResult)
+      {
+        case NArchive::NExtract::NOperationResult::kUnSupportedMethod:
+          (*OutStream) << kUnsupportedMethod;
+          break;
+        case NArchive::NExtract::NOperationResult::kCRCError:
+          (*OutStream) << (encrypted ? kCrcFailedEncrypted: kCrcFailed);
+          break;
+        case NArchive::NExtract::NOperationResult::kDataError:
+          (*OutStream) << (encrypted ? kDataErrorEncrypted : kDataError);
+          break;
+        default:
+          (*OutStream) << kUnknownError;
+      }
+    }
+  }
+  (*OutStream) << endl;
+  return S_OK;
+}
+
+#ifndef _NO_CRYPTO
+
+HRESULT CExtractCallbackConsole::SetPassword(const UString &password)
+{
+  PasswordIsDefined = true;
+  Password = password;
+  return S_OK;
+}
+
+STDMETHODIMP CExtractCallbackConsole::CryptoGetTextPassword(BSTR *password)
+{
+  if (!PasswordIsDefined)
+  {
+    Password = GetPassword(OutStream);
+    PasswordIsDefined = true;
+  }
+  return StringToBstr(Password, password);
+}
+
+#endif
+
+HRESULT CExtractCallbackConsole::BeforeOpen(const wchar_t *name)
+{
+  NumArchives++;
+  NumFileErrorsInCurrentArchive = 0;
+  (*OutStream) << endl << kProcessing << name << endl;
+  return S_OK;
+}
+
+HRESULT CExtractCallbackConsole::OpenResult(const wchar_t * /* name */, HRESULT result, bool encrypted)
+{
+  (*OutStream) << endl;
+  if (result != S_OK)
+  {
+    (*OutStream) << "Error: ";
+    if (result == S_FALSE)
+    {
+      (*OutStream) << (encrypted ?
+        "Can not open encrypted archive. Wrong password?" :
+        "Can not open file as archive");
+    }
+    else
+    {
+      if (result == E_OUTOFMEMORY)
+        (*OutStream) << "Can't allocate required memory";
+      else
+        (*OutStream) << NError::MyFormatMessage(result);
+    }
+    (*OutStream) << endl;
+    NumArchiveErrors++;
+  }
+  return S_OK;
+}
+  
+HRESULT CExtractCallbackConsole::ThereAreNoFiles()
+{
+  (*OutStream) << endl << kNoFiles << endl;
+  return S_OK;
+}
+
+HRESULT CExtractCallbackConsole::ExtractResult(HRESULT result)
+{
+  if (result == S_OK)
+  {
+    (*OutStream) << endl;
+    if (NumFileErrorsInCurrentArchive == 0)
+      (*OutStream) << kEverythingIsOk << endl;
+    else
+    {
+      NumArchiveErrors++;
+      (*OutStream) << "Sub items Errors: " << NumFileErrorsInCurrentArchive << endl;
+    }
+  }
+  if (result == S_OK)
+    return result;
+  NumArchiveErrors++;
+  if (result == E_ABORT || result == ERROR_DISK_FULL)
+    return result;
+  (*OutStream) << endl << kError;
+  if (result == E_OUTOFMEMORY)
+    (*OutStream) << kMemoryExceptionMessage;
+  else
+  {
+    UString message;
+    NError::MyFormatMessage(result, message);
+    (*OutStream) << message;
+  }
+  (*OutStream) << endl;
+  return S_OK;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/ExtractCallbackConsole.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/ExtractCallbackConsole.h
new file mode 100644
index 0000000..e42ca6f
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/ExtractCallbackConsole.h
@@ -0,0 +1,73 @@
+// ExtractCallbackConsole.h
+
+#ifndef __EXTRACTCALLBACKCONSOLE_H
+#define __EXTRACTCALLBACKCONSOLE_H
+
+#include "Common/MyString.h"
+#include "Common/StdOutStream.h"
+#include "../../Common/FileStreams.h"
+#include "../../IPassword.h"
+#include "../../Archive/IArchive.h"
+#include "../Common/ArchiveExtractCallback.h"
+
+class CExtractCallbackConsole:
+  public IExtractCallbackUI,
+  #ifndef _NO_CRYPTO
+  public ICryptoGetTextPassword,
+  #endif
+  public CMyUnknownImp
+{
+public:
+  MY_QUERYINTERFACE_BEGIN2(IFolderArchiveExtractCallback)
+  #ifndef _NO_CRYPTO
+  MY_QUERYINTERFACE_ENTRY(ICryptoGetTextPassword)
+  #endif
+  MY_QUERYINTERFACE_END
+  MY_ADDREF_RELEASE
+
+  STDMETHOD(SetTotal)(UInt64 total);
+  STDMETHOD(SetCompleted)(const UInt64 *completeValue);
+
+  // IFolderArchiveExtractCallback
+  STDMETHOD(AskOverwrite)(
+      const wchar_t *existName, const FILETIME *existTime, const UInt64 *existSize,
+      const wchar_t *newName, const FILETIME *newTime, const UInt64 *newSize,
+      Int32 *answer);
+  STDMETHOD (PrepareOperation)(const wchar_t *name, bool isFolder, Int32 askExtractMode, const UInt64 *position);
+
+  STDMETHOD(MessageError)(const wchar_t *message);
+  STDMETHOD(SetOperationResult)(Int32 operationResult, bool encrypted);
+
+  HRESULT BeforeOpen(const wchar_t *name);
+  HRESULT OpenResult(const wchar_t *name, HRESULT result, bool encrypted);
+  HRESULT ThereAreNoFiles();
+  HRESULT ExtractResult(HRESULT result);
+
+ 
+  #ifndef _NO_CRYPTO
+  HRESULT SetPassword(const UString &password);
+  STDMETHOD(CryptoGetTextPassword)(BSTR *password);
+
+  bool PasswordIsDefined;
+  UString Password;
+
+  #endif
+  
+  UInt64 NumArchives;
+  UInt64 NumArchiveErrors;
+  UInt64 NumFileErrors;
+  UInt64 NumFileErrorsInCurrentArchive;
+
+  CStdOutStream *OutStream;
+
+  void Init()
+  {
+    NumArchives = 0;
+    NumArchiveErrors = 0;
+    NumFileErrors = 0;
+    NumFileErrorsInCurrentArchive = 0;
+  }
+
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/List.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/List.cpp
new file mode 100644
index 0000000..82a08f2
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/List.cpp
@@ -0,0 +1,596 @@
+// List.cpp
+
+#include "StdAfx.h"
+
+#include "List.h"
+#include "ConsoleClose.h"
+
+#include "Common/StringConvert.h"
+#include "Common/StdOutStream.h"
+#include "Common/IntToString.h"
+#include "Common/MyCom.h"
+
+#include "Windows/PropVariant.h"
+#include "Windows/Defs.h"
+#include "Windows/PropVariantConversions.h"
+#include "Windows/FileDir.h"
+#include "Windows/Error.h"
+
+#include "../../Archive/IArchive.h"
+
+#include "../Common/PropIDUtils.h"
+#include "../Common/OpenArchive.h"
+
+#include "OpenCallbackConsole.h"
+
+using namespace NWindows;
+
+struct CPropIdToName
+{
+  PROPID PropID;
+  const wchar_t *Name;
+};
+
+static CPropIdToName kPropIdToName[] =
+{
+  { kpidPath, L"Path" },
+  { kpidName, L"Name" },
+  { kpidIsDir, L"Folder" },
+  { kpidSize, L"Size" },
+  { kpidPackSize, L"Packed Size" },
+  { kpidAttrib, L"Attributes" },
+  { kpidCTime, L"Created" },
+  { kpidATime, L"Accessed" },
+  { kpidMTime, L"Modified" },
+  { kpidSolid, L"Solid" },
+  { kpidCommented, L"Commented" },
+  { kpidEncrypted, L"Encrypted" },
+  { kpidSplitBefore, L"Split Before" },
+  { kpidSplitAfter, L"Split After" },
+  { kpidDictionarySize, L"Dictionary Size" },
+  { kpidCRC, L"CRC" },
+  { kpidType, L"Type" },
+  { kpidIsAnti, L"Anti" },
+  { kpidMethod, L"Method" },
+  { kpidHostOS, L"Host OS" },
+  { kpidFileSystem, L"File System" },
+  { kpidUser, L"User" },
+  { kpidGroup, L"Group" },
+  { kpidBlock, L"Block" },
+  { kpidComment, L"Comment" },
+  { kpidPosition, L"Position" },
+  { kpidPrefix, L"Prefix" },
+  { kpidNumSubDirs, L"Folders" },
+  { kpidNumSubFiles, L"Files" },
+  { kpidUnpackVer, L"Version" },
+  { kpidVolume, L"Volume" },
+  { kpidIsVolume, L"Multivolume" },
+  { kpidOffset, L"Offset" },
+  { kpidLinks, L"Links" },
+  { kpidNumBlocks, L"Blocks" },
+  { kpidNumVolumes, L"Volumes" },
+
+  { kpidBit64, L"64-bit" },
+  { kpidBigEndian, L"Big-endian" },
+  { kpidCpu, L"CPU" },
+  { kpidPhySize, L"Physical Size" },
+  { kpidHeadersSize, L"Headers Size" },
+  { kpidChecksum, L"Checksum" },
+  { kpidCharacts, L"Characteristics" },
+  { kpidVa, L"Virtual Address" },
+  { kpidFreeSpace, L"Free Space" },
+  { kpidClusterSize, L"Cluster Size" }
+};
+
+static const char kEmptyAttribChar = '.';
+
+static const char *kListing = "Listing archive: ";
+static const wchar_t *kFilesMessage = L"files";
+static const wchar_t *kDirsMessage = L"folders";
+
+static void GetAttribString(DWORD wa, bool isDir, char *s)
+{
+  s[0] = ((wa & FILE_ATTRIBUTE_DIRECTORY) != 0 || isDir) ? 'D' : kEmptyAttribChar;
+  s[1] = ((wa & FILE_ATTRIBUTE_READONLY) != 0) ? 'R': kEmptyAttribChar;
+  s[2] = ((wa & FILE_ATTRIBUTE_HIDDEN) != 0) ? 'H': kEmptyAttribChar;
+  s[3] = ((wa & FILE_ATTRIBUTE_SYSTEM) != 0) ? 'S': kEmptyAttribChar;
+  s[4] = ((wa & FILE_ATTRIBUTE_ARCHIVE) != 0) ? 'A': kEmptyAttribChar;
+  s[5] = '\0';
+}
+
+enum EAdjustment
+{
+  kLeft,
+  kCenter,
+  kRight
+};
+
+struct CFieldInfo
+{
+  PROPID PropID;
+  UString Name;
+  EAdjustment TitleAdjustment;
+  EAdjustment TextAdjustment;
+  int PrefixSpacesWidth;
+  int Width;
+};
+
+struct CFieldInfoInit
+{
+  PROPID PropID;
+  const wchar_t *Name;
+  EAdjustment TitleAdjustment;
+  EAdjustment TextAdjustment;
+  int PrefixSpacesWidth;
+  int Width;
+};
+
+CFieldInfoInit kStandardFieldTable[] =
+{
+  { kpidMTime, L"   Date      Time", kLeft, kLeft, 0, 19 },
+  { kpidAttrib, L"Attr", kRight, kCenter, 1, 5 },
+  { kpidSize, L"Size", kRight, kRight, 1, 12 },
+  { kpidPackSize, L"Compressed", kRight, kRight, 1, 12 },
+  { kpidPath, L"Name", kLeft, kLeft, 2, 24 }
+};
+
+void PrintSpaces(int numSpaces)
+{
+  for (int i = 0; i < numSpaces; i++)
+    g_StdOut << ' ';
+}
+
+void PrintString(EAdjustment adjustment, int width, const UString &textString)
+{
+  const int numSpaces = width - textString.Length();
+  int numLeftSpaces = 0;
+  switch (adjustment)
+  {
+    case kLeft:
+      numLeftSpaces = 0;
+      break;
+    case kCenter:
+      numLeftSpaces = numSpaces / 2;
+      break;
+    case kRight:
+      numLeftSpaces = numSpaces;
+      break;
+  }
+  PrintSpaces(numLeftSpaces);
+  g_StdOut << textString;
+  PrintSpaces(numSpaces - numLeftSpaces);
+}
+
+class CFieldPrinter
+{
+  CObjectVector<CFieldInfo> _fields;
+public:
+  void Clear() { _fields.Clear(); }
+  void Init(const CFieldInfoInit *standardFieldTable, int numItems);
+  HRESULT Init(IInArchive *archive);
+  void PrintTitle();
+  void PrintTitleLines();
+  HRESULT PrintItemInfo(IInArchive *archive,
+      const UString &defaultItemName,
+      UInt32 index,
+      bool techMode);
+  HRESULT PrintSummaryInfo(UInt64 numFiles, UInt64 numDirs,
+      const UInt64 *size, const UInt64 *compressedSize);
+};
+
+void CFieldPrinter::Init(const CFieldInfoInit *standardFieldTable, int numItems)
+{
+  Clear();
+  for (int i = 0; i < numItems; i++)
+  {
+    CFieldInfo fieldInfo;
+    const CFieldInfoInit &fieldInfoInit = standardFieldTable[i];
+    fieldInfo.PropID = fieldInfoInit.PropID;
+    fieldInfo.Name = fieldInfoInit.Name;
+    fieldInfo.TitleAdjustment = fieldInfoInit.TitleAdjustment;
+    fieldInfo.TextAdjustment = fieldInfoInit.TextAdjustment;
+    fieldInfo.PrefixSpacesWidth = fieldInfoInit.PrefixSpacesWidth;
+    fieldInfo.Width = fieldInfoInit.Width;
+    _fields.Add(fieldInfo);
+  }
+}
+
+static UString GetPropName(PROPID propID, BSTR name)
+{
+  for (int i = 0; i < sizeof(kPropIdToName) / sizeof(kPropIdToName[0]); i++)
+  {
+    const CPropIdToName &propIdToName = kPropIdToName[i];
+    if (propIdToName.PropID == propID)
+      return propIdToName.Name;
+  }
+  if (name)
+    return name;
+  wchar_t s[32];
+  ConvertUInt64ToString(propID, s);
+  return s;
+}
+
+HRESULT CFieldPrinter::Init(IInArchive *archive)
+{
+  Clear();
+  UInt32 numProps;
+  RINOK(archive->GetNumberOfProperties(&numProps));
+  for (UInt32 i = 0; i < numProps; i++)
+  {
+    CMyComBSTR name;
+    PROPID propID;
+    VARTYPE vt;
+    RINOK(archive->GetPropertyInfo(i, &name, &propID, &vt));
+    CFieldInfo fieldInfo;
+    fieldInfo.PropID = propID;
+    fieldInfo.Name = GetPropName(propID, name);
+    _fields.Add(fieldInfo);
+  }
+  return S_OK;
+}
+
+void CFieldPrinter::PrintTitle()
+{
+  for (int i = 0; i < _fields.Size(); i++)
+  {
+    const CFieldInfo &fieldInfo = _fields[i];
+    PrintSpaces(fieldInfo.PrefixSpacesWidth);
+    PrintString(fieldInfo.TitleAdjustment,
+      ((fieldInfo.PropID == kpidPath) ? 0: fieldInfo.Width), fieldInfo.Name);
+  }
+}
+
+void CFieldPrinter::PrintTitleLines()
+{
+  for (int i = 0; i < _fields.Size(); i++)
+  {
+    const CFieldInfo &fieldInfo = _fields[i];
+    PrintSpaces(fieldInfo.PrefixSpacesWidth);
+    for (int i = 0; i < fieldInfo.Width; i++)
+      g_StdOut << '-';
+  }
+}
+
+
+BOOL IsFileTimeZero(CONST FILETIME *lpFileTime)
+{
+  return (lpFileTime->dwLowDateTime == 0) && (lpFileTime->dwHighDateTime == 0);
+}
+
+static const char *kEmptyTimeString = "                   ";
+void PrintTime(const NCOM::CPropVariant &prop)
+{
+  if (prop.vt != VT_FILETIME)
+    throw "incorrect item";
+  if (IsFileTimeZero(&prop.filetime))
+    g_StdOut << kEmptyTimeString;
+  else
+  {
+    FILETIME localFileTime;
+    if (!FileTimeToLocalFileTime(&prop.filetime, &localFileTime))
+      throw "FileTimeToLocalFileTime error";
+    char s[32];
+    if (ConvertFileTimeToString(localFileTime, s, true, true))
+      g_StdOut << s;
+    else
+      g_StdOut << kEmptyTimeString;
+  }
+}
+
+HRESULT CFieldPrinter::PrintItemInfo(IInArchive *archive,
+    const UString &defaultItemName,
+    UInt32 index,
+    bool techMode)
+{
+  /*
+  if (techMode)
+  {
+    g_StdOut << "Index = ";
+    g_StdOut << (UInt64)index;
+    g_StdOut << endl;
+  }
+  */
+  for (int i = 0; i < _fields.Size(); i++)
+  {
+    const CFieldInfo &fieldInfo = _fields[i];
+    if (!techMode)
+      PrintSpaces(fieldInfo.PrefixSpacesWidth);
+
+    NCOM::CPropVariant prop;
+    if (fieldInfo.PropID == kpidPath)
+    {
+      UString s;
+      RINOK(GetArchiveItemPath(archive, index, defaultItemName, s));
+      prop = s;
+    }
+    else
+    {
+      RINOK(archive->GetProperty(index, fieldInfo.PropID, &prop));
+    }
+    if (techMode)
+    {
+      g_StdOut << fieldInfo.Name << " = ";
+    }
+    int width = (fieldInfo.PropID == kpidPath) ? 0: fieldInfo.Width;
+    if (prop.vt == VT_EMPTY)
+    {
+      switch(fieldInfo.PropID)
+      {
+        case kpidPath:  prop = defaultItemName; break;
+        default:
+          if (techMode)
+            g_StdOut << endl;
+          else
+            PrintSpaces(width);
+          continue;
+      }
+    }
+    if (fieldInfo.PropID == kpidMTime)
+    {
+      PrintTime(prop);
+    }
+    else if (fieldInfo.PropID == kpidAttrib)
+    {
+      if (prop.vt != VT_UI4)
+        throw "incorrect item";
+      UInt32 attributes = prop.ulVal;
+      bool isFolder;
+      RINOK(IsArchiveItemFolder(archive, index, isFolder));
+      char s[8];
+      GetAttribString(attributes, isFolder, s);
+      g_StdOut << s;
+    }
+    else if (prop.vt == VT_BSTR)
+    {
+      if (techMode)
+        g_StdOut << prop.bstrVal;
+      else
+        PrintString(fieldInfo.TextAdjustment, width, prop.bstrVal);
+    }
+    else
+    {
+      UString s = ConvertPropertyToString(prop, fieldInfo.PropID);
+      s.Replace(wchar_t(0xA), L' ');
+      s.Replace(wchar_t(0xD), L' ');
+
+      if (techMode)
+        g_StdOut << s;
+      else
+        PrintString(fieldInfo.TextAdjustment, width, s);
+    }
+    if (techMode)
+      g_StdOut << endl;
+  }
+  return S_OK;
+}
+
+void PrintNumberString(EAdjustment adjustment, int width, const UInt64 *value)
+{
+  wchar_t textString[32] = { 0 };
+  if (value != NULL)
+    ConvertUInt64ToString(*value, textString);
+  PrintString(adjustment, width, textString);
+}
+
+
+HRESULT CFieldPrinter::PrintSummaryInfo(UInt64 numFiles, UInt64 numDirs,
+    const UInt64 *size, const UInt64 *compressedSize)
+{
+  for (int i = 0; i < _fields.Size(); i++)
+  {
+    const CFieldInfo &fieldInfo = _fields[i];
+    PrintSpaces(fieldInfo.PrefixSpacesWidth);
+    NCOM::CPropVariant prop;
+    if (fieldInfo.PropID == kpidSize)
+      PrintNumberString(fieldInfo.TextAdjustment, fieldInfo.Width, size);
+    else if (fieldInfo.PropID == kpidPackSize)
+      PrintNumberString(fieldInfo.TextAdjustment, fieldInfo.Width, compressedSize);
+    else if (fieldInfo.PropID == kpidPath)
+    {
+      wchar_t textString[32];
+      ConvertUInt64ToString(numFiles, textString);
+      UString temp = textString;
+      temp += L" ";
+      temp += kFilesMessage;
+      temp += L", ";
+      ConvertUInt64ToString(numDirs, textString);
+      temp += textString;
+      temp += L" ";
+      temp += kDirsMessage;
+      PrintString(fieldInfo.TextAdjustment, 0, temp);
+    }
+    else
+      PrintString(fieldInfo.TextAdjustment, fieldInfo.Width, L"");
+  }
+  return S_OK;
+}
+
+bool GetUInt64Value(IInArchive *archive, UInt32 index, PROPID propID, UInt64 &value)
+{
+  NCOM::CPropVariant prop;
+  if (archive->GetProperty(index, propID, &prop) != S_OK)
+    throw "GetPropertyValue error";
+  if (prop.vt == VT_EMPTY)
+    return false;
+  value = ConvertPropVariantToUInt64(prop);
+  return true;
+}
+
+HRESULT ListArchives(CCodecs *codecs, const CIntVector &formatIndices,
+    UStringVector &archivePaths, UStringVector &archivePathsFull,
+    const NWildcard::CCensorNode &wildcardCensor,
+    bool enableHeaders, bool techMode,
+    #ifndef _NO_CRYPTO
+    bool &passwordEnabled, UString &password,
+    #endif
+    UInt64 &numErrors)
+{
+  numErrors = 0;
+  CFieldPrinter fieldPrinter;
+  if (!techMode)
+    fieldPrinter.Init(kStandardFieldTable, sizeof(kStandardFieldTable) / sizeof(kStandardFieldTable[0]));
+
+  UInt64 numFiles2 = 0, numDirs2 = 0, totalPackSize2 = 0, totalUnPackSize2 = 0;
+  UInt64 *totalPackSizePointer2 = 0, *totalUnPackSizePointer2 = 0;
+  for (int i = 0; i < archivePaths.Size(); i++)
+  {
+    const UString &archiveName = archivePaths[i];
+    NFile::NFind::CFileInfoW fi;
+    if (!NFile::NFind::FindFile(archiveName, fi) || fi.IsDir())
+    {
+      g_StdOut << endl << "Error: " << archiveName << " is not file" << endl;
+      numErrors++;
+      continue;
+    }
+
+    CArchiveLink archiveLink;
+
+    COpenCallbackConsole openCallback;
+    openCallback.OutStream = &g_StdOut;
+
+    #ifndef _NO_CRYPTO
+
+    openCallback.PasswordIsDefined = passwordEnabled;
+    openCallback.Password = password;
+
+    #endif
+
+    HRESULT result = MyOpenArchive(codecs, formatIndices, archiveName, archiveLink, &openCallback);
+    if (result != S_OK)
+    {
+      if (result == E_ABORT)
+        return result;
+      g_StdOut << endl << "Error: " << archiveName << ": ";
+      if (result == S_FALSE)
+        g_StdOut << "is not supported archive";
+      else if (result == E_OUTOFMEMORY)
+        g_StdOut << "Can't allocate required memory";
+      else
+        g_StdOut << NError::MyFormatMessage(result);
+      g_StdOut << endl;
+      numErrors++;
+      continue;
+    }
+
+    for (int v = 0; v < archiveLink.VolumePaths.Size(); v++)
+    {
+      int index = archivePathsFull.FindInSorted(archiveLink.VolumePaths[v]);
+      if (index >= 0 && index > i)
+      {
+        archivePaths.Delete(index);
+        archivePathsFull.Delete(index);
+      }
+    }
+
+    IInArchive *archive = archiveLink.GetArchive();
+    const UString defaultItemName = archiveLink.GetDefaultItemName();
+
+    if (enableHeaders)
+    {
+      g_StdOut << endl << kListing << archiveName << endl << endl;
+
+      UInt32 numProps;
+      if (archive->GetNumberOfArchiveProperties(&numProps) == S_OK)
+      {
+        for (UInt32 i = 0; i < numProps; i++)
+        {
+          CMyComBSTR name;
+          PROPID propID;
+          VARTYPE vt;
+          if (archive->GetArchivePropertyInfo(i, &name, &propID, &vt) != S_OK)
+            continue;
+          NCOM::CPropVariant prop;
+          if (archive->GetArchiveProperty(propID, &prop) != S_OK)
+            continue;
+          UString s = ConvertPropertyToString(prop, propID);
+          if (!s.IsEmpty())
+            g_StdOut << GetPropName(propID, name) << " = " << s << endl;
+        }
+      }
+      if (techMode)
+        g_StdOut << "----------\n";
+      if (numProps > 0)
+        g_StdOut << endl;
+    }
+
+    if (enableHeaders && !techMode)
+    {
+      fieldPrinter.PrintTitle();
+      g_StdOut << endl;
+      fieldPrinter.PrintTitleLines();
+      g_StdOut << endl;
+    }
+
+    if (techMode)
+    {
+      RINOK(fieldPrinter.Init(archive));
+    }
+    UInt64 numFiles = 0, numDirs = 0, totalPackSize = 0, totalUnPackSize = 0;
+    UInt64 *totalPackSizePointer = 0, *totalUnPackSizePointer = 0;
+    UInt32 numItems;
+    RINOK(archive->GetNumberOfItems(&numItems));
+    for(UInt32 i = 0; i < numItems; i++)
+    {
+      if (NConsoleClose::TestBreakSignal())
+        return E_ABORT;
+
+      UString filePath;
+      RINOK(GetArchiveItemPath(archive, i, defaultItemName, filePath));
+
+      bool isFolder;
+      RINOK(IsArchiveItemFolder(archive, i, isFolder));
+      if (!wildcardCensor.CheckPath(filePath, !isFolder))
+        continue;
+      
+      fieldPrinter.PrintItemInfo(archive, defaultItemName, i, techMode);
+      
+      UInt64 packSize, unpackSize;
+      if (!GetUInt64Value(archive, i, kpidSize, unpackSize))
+        unpackSize = 0;
+      else
+        totalUnPackSizePointer = &totalUnPackSize;
+      if (!GetUInt64Value(archive, i, kpidPackSize, packSize))
+        packSize = 0;
+      else
+        totalPackSizePointer = &totalPackSize;
+      
+      g_StdOut << endl;
+
+      if (isFolder)
+        numDirs++;
+      else
+        numFiles++;
+      totalPackSize += packSize;
+      totalUnPackSize += unpackSize;
+    }
+    if (enableHeaders && !techMode)
+    {
+      fieldPrinter.PrintTitleLines();
+      g_StdOut << endl;
+      fieldPrinter.PrintSummaryInfo(numFiles, numDirs, totalUnPackSizePointer, totalPackSizePointer);
+      g_StdOut << endl;
+    }
+    if (totalPackSizePointer != 0)
+    {
+      totalPackSizePointer2 = &totalPackSize2;
+      totalPackSize2 += totalPackSize;
+    }
+    if (totalUnPackSizePointer != 0)
+    {
+      totalUnPackSizePointer2 = &totalUnPackSize2;
+      totalUnPackSize2 += totalUnPackSize;
+    }
+    numFiles2 += numFiles;
+    numDirs2 += numDirs;
+  }
+  if (enableHeaders && !techMode && archivePaths.Size() > 1)
+  {
+    g_StdOut << endl;
+    fieldPrinter.PrintTitleLines();
+    g_StdOut << endl;
+    fieldPrinter.PrintSummaryInfo(numFiles2, numDirs2, totalUnPackSizePointer2, totalPackSizePointer2);
+    g_StdOut << endl;
+    g_StdOut << "Archives: " << archivePaths.Size() << endl;
+  }
+  return S_OK;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/List.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/List.h
new file mode 100644
index 0000000..bb4287e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/List.h
@@ -0,0 +1,19 @@
+// List.h
+
+#ifndef __LIST_H
+#define __LIST_H
+
+#include "Common/Wildcard.h"
+#include "../Common/LoadCodecs.h"
+
+HRESULT ListArchives(CCodecs *codecs, const CIntVector &formatIndices,
+    UStringVector &archivePaths, UStringVector &archivePathsFull,
+    const NWildcard::CCensorNode &wildcardCensor,
+    bool enableHeaders, bool techMode,
+    #ifndef _NO_CRYPTO
+    bool &passwordEnabled, UString &password,
+    #endif
+    UInt64 &errors);
+
+#endif
+
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/Main.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/Main.cpp
new file mode 100644
index 0000000..87d48e9
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/Main.cpp
@@ -0,0 +1,596 @@
+// Main.cpp
+
+#include "StdAfx.h"
+
+#include "Common/MyInitGuid.h"
+
+#include "Common/CommandLineParser.h"
+#include "Common/MyException.h"
+#include "Common/IntToString.h"
+#include "Common/StdOutStream.h"
+#include "Common/StringConvert.h"
+#include "Common/StringToInt.h"
+
+#include "Windows/FileDir.h"
+#include "Windows/FileName.h"
+#include "Windows/Defs.h"
+#include "Windows/Error.h"
+#ifdef _WIN32
+#include "Windows/MemoryLock.h"
+#endif
+
+#include "../../IPassword.h"
+#include "../../ICoder.h"
+#include "../Common/UpdateAction.h"
+#include "../Common/Update.h"
+#include "../Common/Extract.h"
+#include "../Common/ArchiveCommandLine.h"
+#include "../Common/ExitCode.h"
+#ifdef EXTERNAL_CODECS
+#include "../Common/LoadCodecs.h"
+#endif
+
+#include "../../Compress/LZMA_Alone/LzmaBenchCon.h"
+
+#include "List.h"
+#include "OpenCallbackConsole.h"
+#include "ExtractCallbackConsole.h"
+#include "UpdateCallbackConsole.h"
+
+#include "../../MyVersion.h"
+
+#if defined( _WIN32) && defined( _7ZIP_LARGE_PAGES)
+extern "C"
+{
+#include "../../../../C/Alloc.h"
+}
+#endif
+
+using namespace NWindows;
+using namespace NFile;
+using namespace NCommandLineParser;
+
+HINSTANCE g_hInstance = 0;
+extern CStdOutStream *g_StdStream;
+
+static const char *kCopyrightString = "\n7-Zip"
+#ifndef EXTERNAL_CODECS
+" (A)"
+#endif
+
+#ifdef _WIN64
+" [64]"
+#endif
+
+" " MY_VERSION_COPYRIGHT_DATE "\n";
+
+static const char *kHelpString =
+    "\nUsage: 7z"
+#ifdef _NO_CRYPTO
+    "r"
+#else
+#ifndef EXTERNAL_CODECS
+    "a"
+#endif
+#endif
+    " <command> [<switches>...] <archive_name> [<file_names>...]\n"
+    "       [<@listfiles...>]\n"
+    "\n"
+    "<Commands>\n"
+    "  a: Add files to archive\n"
+    "  b: Benchmark\n"
+    "  d: Delete files from archive\n"
+    "  e: Extract files from archive (without using directory names)\n"
+    "  l: List contents of archive\n"
+//    "  l[a|t][f]: List contents of archive\n"
+//    "    a - with Additional fields\n"
+//    "    t - with all fields\n"
+//    "    f - with Full pathnames\n"
+    "  t: Test integrity of archive\n"
+    "  u: Update files to archive\n"
+    "  x: eXtract files with full paths\n"
+    "<Switches>\n"
+    "  -ai[r[-|0]]{@listfile|!wildcard}: Include archives\n"
+    "  -ax[r[-|0]]{@listfile|!wildcard}: eXclude archives\n"
+    "  -bd: Disable percentage indicator\n"
+    "  -i[r[-|0]]{@listfile|!wildcard}: Include filenames\n"
+    "  -m{Parameters}: set compression Method\n"
+    "  -o{Directory}: set Output directory\n"
+    #ifndef _NO_CRYPTO
+    "  -p{Password}: set Password\n"
+    #endif
+    "  -r[-|0]: Recurse subdirectories\n"
+    "  -scs{UTF-8 | WIN | DOS}: set charset for list files\n"
+    "  -sfx[{name}]: Create SFX archive\n"
+    "  -si[{name}]: read data from stdin\n"
+    "  -slt: show technical information for l (List) command\n"
+    "  -so: write data to stdout\n"
+    "  -ssc[-]: set sensitive case mode\n"
+    "  -ssw: compress shared files\n"
+    "  -t{Type}: Set type of archive\n"
+    "  -v{Size}[b|k|m|g]: Create volumes\n"
+    "  -u[-][p#][q#][r#][x#][y#][z#][!newArchiveName]: Update options\n"
+    "  -w[{path}]: assign Work directory. Empty path means a temporary directory\n"
+    "  -x[r[-|0]]]{@listfile|!wildcard}: eXclude filenames\n"
+    "  -y: assume Yes on all queries\n";
+
+// ---------------------------
+// exception messages
+
+static const char *kEverythingIsOk = "Everything is Ok";
+static const char *kUserErrorMessage  = "Incorrect command line"; // NExitCode::kUserError
+static const char *kNoFormats = "7-Zip cannot find the code that works with archives.";
+
+static const wchar_t *kDefaultSfxModule = L"7zCon.sfx";
+
+static void ShowMessageAndThrowException(CStdOutStream &s, LPCSTR message, NExitCode::EEnum code)
+{
+  s << message << endl;
+  throw code;
+}
+
+static void PrintHelpAndExit(CStdOutStream &s) // yyy
+{
+  s << kHelpString;
+  ShowMessageAndThrowException(s, kUserErrorMessage, NExitCode::kUserError);
+}
+
+#ifndef _WIN32
+static void GetArguments(int numArguments, const char *arguments[], UStringVector &parts)
+{
+  parts.Clear();
+  for(int i = 0; i < numArguments; i++)
+  {
+    UString s = MultiByteToUnicodeString(arguments[i]);
+    parts.Add(s);
+  }
+}
+#endif
+
+static void ShowCopyrightAndHelp(CStdOutStream &s, bool needHelp)
+{
+  s << kCopyrightString;
+  // s << "# CPUs: " << (UInt64)NWindows::NSystem::GetNumberOfProcessors() << "\n";
+  if (needHelp)
+    s << kHelpString;
+}
+
+#ifdef EXTERNAL_CODECS
+static void PrintString(CStdOutStream &stdStream, const AString &s, int size)
+{
+  int len = s.Length();
+  stdStream << s;
+  for (int i = len; i < size; i++)
+    stdStream << ' ';
+}
+#endif
+
+static void PrintString(CStdOutStream &stdStream, const UString &s, int size)
+{
+  int len = s.Length();
+  stdStream << s;
+  for (int i = len; i < size; i++)
+    stdStream << ' ';
+}
+
+static inline char GetHex(Byte value)
+{
+  return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10)));
+}
+
+const char *kUnsupportedArcTypeMessage = "Unsupported archive type";
+
+int Main2(
+  #ifndef _WIN32
+  int numArguments, const char *arguments[]
+  #endif
+)
+{
+  #ifdef _WIN32
+  SetFileApisToOEM();
+  #endif
+  
+  UStringVector commandStrings;
+  #ifdef _WIN32
+  NCommandLineParser::SplitCommandLine(GetCommandLineW(), commandStrings);
+  #else
+  GetArguments(numArguments, arguments, commandStrings);
+  #endif
+
+  if(commandStrings.Size() == 1)
+  {
+    ShowCopyrightAndHelp(g_StdOut, true);
+    return 0;
+  }
+  commandStrings.Delete(0);
+
+  CArchiveCommandLineOptions options;
+
+  CArchiveCommandLineParser parser;
+
+  parser.Parse1(commandStrings, options);
+
+  if(options.HelpMode)
+  {
+    ShowCopyrightAndHelp(g_StdOut, true);
+    return 0;
+  }
+
+  #if defined(_WIN32) && defined(_7ZIP_LARGE_PAGES)
+  if (options.LargePages)
+  {
+    SetLargePageSize();
+    NSecurity::EnableLockMemoryPrivilege();
+  }
+  #endif
+
+  CStdOutStream &stdStream = options.StdOutMode ? g_StdErr : g_StdOut;
+  g_StdStream = &stdStream;
+
+  if (options.EnableHeaders)
+    ShowCopyrightAndHelp(stdStream, false);
+
+  parser.Parse2(options);
+
+  CCodecs *codecs = new CCodecs;
+  CMyComPtr<
+    #ifdef EXTERNAL_CODECS
+    ICompressCodecsInfo
+    #else
+    IUnknown
+    #endif
+    > compressCodecsInfo = codecs;
+  HRESULT result = codecs->Load();
+  if (result != S_OK)
+    throw CSystemException(result);
+
+  bool isExtractGroupCommand = options.Command.IsFromExtractGroup();
+
+  if (codecs->Formats.Size() == 0 &&
+        (isExtractGroupCommand ||
+        options.Command.CommandType == NCommandType::kList ||
+        options.Command.IsFromUpdateGroup()))
+    throw kNoFormats;
+
+  CIntVector formatIndices;
+  if (!codecs->FindFormatForArchiveType(options.ArcType, formatIndices))
+    throw kUnsupportedArcTypeMessage;
+
+  if (options.Command.CommandType == NCommandType::kInfo)
+  {
+    stdStream << endl << "Formats:" << endl;
+    int i;
+    for (i = 0; i < codecs->Formats.Size(); i++)
+    {
+      const CArcInfoEx &arc = codecs->Formats[i];
+      #ifdef EXTERNAL_CODECS
+      if (arc.LibIndex >= 0)
+      {
+        char s[32];
+        ConvertUInt64ToString(arc.LibIndex, s);
+        PrintString(stdStream, s, 2);
+      }
+      else
+      #endif
+        stdStream << "  ";
+      stdStream << ' ';
+      stdStream << (char)(arc.UpdateEnabled ? 'C' : ' ');
+      stdStream << (char)(arc.KeepName ? 'K' : ' ');
+      stdStream << "  ";
+      PrintString(stdStream, arc.Name, 6);
+      stdStream << "  ";
+      UString s;
+      for (int t = 0; t < arc.Exts.Size(); t++)
+      {
+        const CArcExtInfo &ext = arc.Exts[t];
+        s += ext.Ext;
+        if (!ext.AddExt.IsEmpty())
+        {
+          s += L" (";
+          s += ext.AddExt;
+          s += L')';
+        }
+        s += L' ';
+      }
+      PrintString(stdStream, s, 14);
+      stdStream << "  ";
+      const CByteBuffer &sig = arc.StartSignature;
+      for (size_t j = 0; j < sig.GetCapacity(); j++)
+      {
+        Byte b = sig[j];
+        if (b > 0x20 && b < 0x80)
+        {
+          stdStream << (char)b;
+        }
+        else
+        {
+          stdStream << GetHex((Byte)((b >> 4) & 0xF));
+          stdStream << GetHex((Byte)(b & 0xF));
+        }
+        stdStream << ' ';
+      }
+      stdStream << endl;
+    }
+    stdStream << endl << "Codecs:" << endl;
+
+    #ifdef EXTERNAL_CODECS
+    UInt32 numMethods;
+    if (codecs->GetNumberOfMethods(&numMethods) == S_OK)
+    for (UInt32 j = 0; j < numMethods; j++)
+    {
+      int libIndex = codecs->GetCodecLibIndex(j);
+      if (libIndex >= 0)
+      {
+        char s[32];
+        ConvertUInt64ToString(libIndex, s);
+        PrintString(stdStream, s, 2);
+      }
+      else
+        stdStream << "  ";
+      stdStream << ' ';
+      stdStream << (char)(codecs->GetCodecEncoderIsAssigned(j) ? 'C' : ' ');
+      UInt64 id;
+      stdStream << "  ";
+      HRESULT res = codecs->GetCodecId(j, id);
+      if (res != S_OK)
+        id = (UInt64)(Int64)-1;
+      char s[32];
+      ConvertUInt64ToString(id, s, 16);
+      PrintString(stdStream, s, 8);
+      stdStream << "  ";
+      PrintString(stdStream, codecs->GetCodecName(j), 11);
+      stdStream << endl;
+      /*
+      if (res != S_OK)
+        throw "incorrect Codec ID";
+      */
+    }
+    #endif
+    return S_OK;
+  }
+  else if (options.Command.CommandType == NCommandType::kBenchmark)
+  {
+    if (options.Method.CompareNoCase(L"CRC") == 0)
+    {
+      HRESULT res = CrcBenchCon((FILE *)stdStream, options.NumIterations, options.NumThreads, options.DictionarySize);
+      if (res != S_OK)
+      {
+        if (res == S_FALSE)
+        {
+          stdStream << "\nCRC Error\n";
+          return NExitCode::kFatalError;
+        }
+        throw CSystemException(res);
+      }
+    }
+    else
+    {
+      HRESULT res = LzmaBenchCon(
+        #ifdef EXTERNAL_LZMA
+        codecs,
+        #endif
+        (FILE *)stdStream, options.NumIterations, options.NumThreads, options.DictionarySize);
+      if (res != S_OK)
+      {
+        if (res == S_FALSE)
+        {
+          stdStream << "\nDecoding Error\n";
+          return NExitCode::kFatalError;
+        }
+        throw CSystemException(res);
+      }
+    }
+  }
+  else if (isExtractGroupCommand || options.Command.CommandType == NCommandType::kList)
+  {
+    if(isExtractGroupCommand)
+    {
+      CExtractCallbackConsole *ecs = new CExtractCallbackConsole;
+      CMyComPtr<IFolderArchiveExtractCallback> extractCallback = ecs;
+
+      ecs->OutStream = &stdStream;
+
+      #ifndef _NO_CRYPTO
+      ecs->PasswordIsDefined = options.PasswordEnabled;
+      ecs->Password = options.Password;
+      #endif
+
+      ecs->Init();
+
+      COpenCallbackConsole openCallback;
+      openCallback.OutStream = &stdStream;
+
+      #ifndef _NO_CRYPTO
+      openCallback.PasswordIsDefined = options.PasswordEnabled;
+      openCallback.Password = options.Password;
+      #endif
+
+      CExtractOptions eo;
+      eo.StdOutMode = options.StdOutMode;
+      eo.PathMode = options.Command.GetPathMode();
+      eo.TestMode = options.Command.IsTestMode();
+      eo.OverwriteMode = options.OverwriteMode;
+      eo.OutputDir = options.OutputDir;
+      eo.YesToAll = options.YesToAll;
+      #ifdef COMPRESS_MT
+      eo.Properties = options.ExtractProperties;
+      #endif
+      UString errorMessage;
+      CDecompressStat stat;
+      HRESULT result = DecompressArchives(
+          codecs,
+          formatIndices,
+          options.ArchivePathsSorted,
+          options.ArchivePathsFullSorted,
+          options.WildcardCensor.Pairs.Front().Head,
+          eo, &openCallback, ecs, errorMessage, stat);
+      if (!errorMessage.IsEmpty())
+      {
+        stdStream << endl << "Error: " << errorMessage;
+        if (result == S_OK)
+          result = E_FAIL;
+      }
+
+      stdStream << endl;
+      if (ecs->NumArchives > 1)
+        stdStream << "Archives: " << ecs->NumArchives << endl;
+      if (ecs->NumArchiveErrors != 0 || ecs->NumFileErrors != 0)
+      {
+        if (ecs->NumArchives > 1)
+        {
+          stdStream << endl;
+          if (ecs->NumArchiveErrors != 0)
+            stdStream << "Archive Errors: " << ecs->NumArchiveErrors << endl;
+          if (ecs->NumFileErrors != 0)
+            stdStream << "Sub items Errors: " << ecs->NumFileErrors << endl;
+        }
+        if (result != S_OK)
+          throw CSystemException(result);
+        return NExitCode::kFatalError;
+      }
+      if (result != S_OK)
+        throw CSystemException(result);
+      if (stat.NumFolders != 0)
+        stdStream << "Folders: " << stat.NumFolders << endl;
+      if (stat.NumFiles != 1 || stat.NumFolders != 0)
+          stdStream << "Files: " << stat.NumFiles << endl;
+      stdStream
+           << "Size:       " << stat.UnpackSize << endl
+           << "Compressed: " << stat.PackSize << endl;
+    }
+    else
+    {
+      UInt64 numErrors = 0;
+      HRESULT result = ListArchives(
+          codecs,
+          formatIndices,
+          options.ArchivePathsSorted,
+          options.ArchivePathsFullSorted,
+          options.WildcardCensor.Pairs.Front().Head,
+          options.EnableHeaders,
+          options.TechMode,
+          #ifndef _NO_CRYPTO
+          options.PasswordEnabled,
+          options.Password,
+          #endif
+          numErrors);
+      if (numErrors > 0)
+      {
+        g_StdOut << endl << "Errors: " << numErrors;
+        return NExitCode::kFatalError;
+      }
+      if (result != S_OK)
+        throw CSystemException(result);
+    }
+  }
+  else if(options.Command.IsFromUpdateGroup())
+  {
+    UString workingDir;
+
+    CUpdateOptions &uo = options.UpdateOptions;
+    if (uo.SfxMode && uo.SfxModule.IsEmpty())
+      uo.SfxModule = kDefaultSfxModule;
+
+    COpenCallbackConsole openCallback;
+    openCallback.OutStream = &stdStream;
+
+    #ifndef _NO_CRYPTO
+    bool passwordIsDefined =
+        options.PasswordEnabled && !options.Password.IsEmpty();
+    openCallback.PasswordIsDefined = passwordIsDefined;
+    openCallback.Password = options.Password;
+    #endif
+
+    CUpdateCallbackConsole callback;
+    callback.EnablePercents = options.EnablePercents;
+
+    #ifndef _NO_CRYPTO
+    callback.PasswordIsDefined = passwordIsDefined;
+    callback.AskPassword = options.PasswordEnabled && options.Password.IsEmpty();
+    callback.Password = options.Password;
+    #endif
+    callback.StdOutMode = uo.StdOutMode;
+    callback.Init(&stdStream);
+
+    CUpdateErrorInfo errorInfo;
+
+    if (!uo.Init(codecs, formatIndices, options.ArchiveName))
+      throw kUnsupportedArcTypeMessage;
+    HRESULT result = UpdateArchive(codecs,
+        options.WildcardCensor, uo,
+        errorInfo, &openCallback, &callback);
+
+    int exitCode = NExitCode::kSuccess;
+    if (callback.CantFindFiles.Size() > 0)
+    {
+      stdStream << endl;
+      stdStream << "WARNINGS for files:" << endl << endl;
+      int numErrors = callback.CantFindFiles.Size();
+      for (int i = 0; i < numErrors; i++)
+      {
+        stdStream << callback.CantFindFiles[i] << " : ";
+        stdStream << NError::MyFormatMessageW(callback.CantFindCodes[i]) << endl;
+      }
+      stdStream << "----------------" << endl;
+      stdStream << "WARNING: Cannot find " << numErrors << " file";
+      if (numErrors > 1)
+        stdStream << "s";
+      stdStream << endl;
+      exitCode = NExitCode::kWarning;
+    }
+
+    if (result != S_OK)
+    {
+      UString message;
+      if (!errorInfo.Message.IsEmpty())
+      {
+        message += errorInfo.Message;
+        message += L"\n";
+      }
+      if (!errorInfo.FileName.IsEmpty())
+      {
+        message += errorInfo.FileName;
+        message += L"\n";
+      }
+      if (!errorInfo.FileName2.IsEmpty())
+      {
+        message += errorInfo.FileName2;
+        message += L"\n";
+      }
+      if (errorInfo.SystemError != 0)
+      {
+        message += NError::MyFormatMessageW(errorInfo.SystemError);
+        message += L"\n";
+      }
+      if (!message.IsEmpty())
+        stdStream << L"\nError:\n" << message;
+      throw CSystemException(result);
+    }
+    int numErrors = callback.FailedFiles.Size();
+    if (numErrors == 0)
+    {
+      if (callback.CantFindFiles.Size() == 0)
+        stdStream << kEverythingIsOk << endl;
+    }
+    else
+    {
+      stdStream << endl;
+      stdStream << "WARNINGS for files:" << endl << endl;
+      for (int i = 0; i < numErrors; i++)
+      {
+        stdStream << callback.FailedFiles[i] << " : ";
+        stdStream << NError::MyFormatMessageW(callback.FailedCodes[i]) << endl;
+      }
+      stdStream << "----------------" << endl;
+      stdStream << "WARNING: Cannot open " << numErrors << " file";
+      if (numErrors > 1)
+        stdStream << "s";
+      stdStream << endl;
+      exitCode = NExitCode::kWarning;
+    }
+    return exitCode;
+  }
+  else
+    PrintHelpAndExit(stdStream);
+  return 0;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/MainAr.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/MainAr.cpp
new file mode 100644
index 0000000..02918d7
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/MainAr.cpp
@@ -0,0 +1,157 @@
+// MainAr.cpp
+
+#include "StdAfx.h"
+
+// #include <locale.h>
+
+#include "Windows/Error.h"
+
+#include "Common/StdOutStream.h"
+#include "Common/NewHandler.h"
+#include "Common/MyException.h"
+#include "Common/StringConvert.h"
+
+#include "../Common/ExitCode.h"
+#include "../Common/ArchiveCommandLine.h"
+#include "ConsoleClose.h"
+
+using namespace NWindows;
+
+CStdOutStream *g_StdStream = 0;
+
+#ifdef _WIN32
+#ifndef _UNICODE
+bool g_IsNT = false;
+#endif
+#if !defined(_UNICODE) || !defined(_WIN64)
+static inline bool IsItWindowsNT()
+{
+  OSVERSIONINFO versionInfo;
+  versionInfo.dwOSVersionInfoSize = sizeof(versionInfo);
+  if (!::GetVersionEx(&versionInfo))
+    return false;
+  return (versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT);
+}
+#endif
+#endif
+
+extern int Main2(
+  #ifndef _WIN32
+  int numArguments, const char *arguments[]
+  #endif
+);
+
+static const char *kExceptionErrorMessage = "\n\nError:\n";
+static const char *kUserBreak  = "\nBreak signaled\n";
+
+static const char *kMemoryExceptionMessage = "\n\nERROR: Can't allocate required memory!\n";
+static const char *kUnknownExceptionMessage = "\n\nUnknown Error\n";
+static const char *kInternalExceptionMessage = "\n\nInternal Error #";
+
+int MY_CDECL main
+(
+#ifndef _WIN32
+int numArguments, const char *arguments[]
+#endif
+)
+{
+  g_StdStream = &g_StdOut;
+  #ifdef _WIN32
+  
+  #ifdef _UNICODE
+  #ifndef _WIN64
+  if (!IsItWindowsNT())
+  {
+    (*g_StdStream) << "This program requires Windows NT/2000/2003/2008/XP/Vista";
+    return NExitCode::kFatalError;
+  }
+  #endif
+  #else
+  g_IsNT = IsItWindowsNT();
+  #endif
+  
+  #endif
+
+  // setlocale(LC_COLLATE, ".OCP");
+  NConsoleClose::CCtrlHandlerSetter ctrlHandlerSetter;
+  int res = 0;
+  try
+  {
+    res = Main2(
+#ifndef _WIN32
+      numArguments, arguments
+#endif
+    );
+  }
+  catch(const CNewException &)
+  {
+    (*g_StdStream) << kMemoryExceptionMessage;
+    return (NExitCode::kMemoryError);
+  }
+  catch(const NConsoleClose::CCtrlBreakException &)
+  {
+    (*g_StdStream) << endl << kUserBreak;
+    return (NExitCode::kUserBreak);
+  }
+  catch(const CArchiveCommandLineException &e)
+  {
+    (*g_StdStream) << kExceptionErrorMessage << e << endl;
+    return (NExitCode::kUserError);
+  }
+  catch(const CSystemException &systemError)
+  {
+    if (systemError.ErrorCode == E_OUTOFMEMORY)
+    {
+      (*g_StdStream) << kMemoryExceptionMessage;
+      return (NExitCode::kMemoryError);
+    }
+    if (systemError.ErrorCode == E_ABORT)
+    {
+      (*g_StdStream) << endl << kUserBreak;
+      return (NExitCode::kUserBreak);
+    }
+    UString message;
+    NError::MyFormatMessage(systemError.ErrorCode, message);
+    (*g_StdStream) << endl << endl << "System error:" << endl <<
+        message << endl;
+    return (NExitCode::kFatalError);
+  }
+  catch(NExitCode::EEnum &exitCode)
+  {
+    (*g_StdStream) << kInternalExceptionMessage << exitCode << endl;
+    return (exitCode);
+  }
+  /*
+  catch(const NExitCode::CMultipleErrors &multipleErrors)
+  {
+    (*g_StdStream) << endl << multipleErrors.NumErrors << " errors" << endl;
+    return (NExitCode::kFatalError);
+  }
+  */
+  catch(const UString &s)
+  {
+    (*g_StdStream) << kExceptionErrorMessage << s << endl;
+    return (NExitCode::kFatalError);
+  }
+  catch(const AString &s)
+  {
+    (*g_StdStream) << kExceptionErrorMessage << s << endl;
+    return (NExitCode::kFatalError);
+  }
+  catch(const char *s)
+  {
+    (*g_StdStream) << kExceptionErrorMessage << s << endl;
+    return (NExitCode::kFatalError);
+  }
+  catch(int t)
+  {
+    (*g_StdStream) << kInternalExceptionMessage << t << endl;
+    return (NExitCode::kFatalError);
+  }
+  catch(...)
+  {
+    (*g_StdStream) << kUnknownExceptionMessage;
+    return (NExitCode::kFatalError);
+  }
+  return  res;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/OpenCallbackConsole.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/OpenCallbackConsole.cpp
new file mode 100644
index 0000000..7dba2ad
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/OpenCallbackConsole.cpp
@@ -0,0 +1,58 @@
+// OpenCallbackConsole.cpp
+
+#include "StdAfx.h"
+
+#include "OpenCallbackConsole.h"
+
+#include "ConsoleClose.h"
+#include "UserInputUtils.h"
+
+HRESULT COpenCallbackConsole::Open_CheckBreak()
+{
+  if (NConsoleClose::TestBreakSignal())
+    return E_ABORT;
+  return S_OK;
+}
+
+HRESULT COpenCallbackConsole::Open_SetTotal(const UInt64 *, const UInt64 *)
+{
+  return Open_CheckBreak();
+}
+
+HRESULT COpenCallbackConsole::Open_SetCompleted(const UInt64 *, const UInt64 *)
+{
+  return Open_CheckBreak();
+}
+ 
+#ifndef _NO_CRYPTO
+
+HRESULT COpenCallbackConsole::Open_CryptoGetTextPassword(BSTR *password)
+{
+  PasswordWasAsked = true;
+  RINOK(Open_CheckBreak());
+  if (!PasswordIsDefined)
+  {
+    Password = GetPassword(OutStream);
+    PasswordIsDefined = true;
+  }
+  return StringToBstr(Password, password);
+}
+
+HRESULT COpenCallbackConsole::Open_GetPasswordIfAny(UString &password)
+{
+  if (PasswordIsDefined)
+    password = Password;
+  return S_OK;
+}
+
+bool COpenCallbackConsole::Open_WasPasswordAsked()
+{
+  return PasswordWasAsked;
+}
+
+void COpenCallbackConsole::Open_ClearPasswordWasAskedFlag()
+{
+  PasswordWasAsked = false;
+}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/OpenCallbackConsole.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/OpenCallbackConsole.h
new file mode 100644
index 0000000..c002e6a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/OpenCallbackConsole.h
@@ -0,0 +1,24 @@
+// OpenCallbackConsole.h
+
+#ifndef __OPENCALLBACKCONSOLE_H
+#define __OPENCALLBACKCONSOLE_H
+
+#include "Common/StdOutStream.h"
+#include "../Common/ArchiveOpenCallback.h"
+
+class COpenCallbackConsole: public IOpenCallbackUI
+{
+public:
+  INTERFACE_IOpenCallbackUI(;)
+  
+  CStdOutStream *OutStream;
+
+  #ifndef _NO_CRYPTO
+  bool PasswordIsDefined;
+  bool PasswordWasAsked;
+  UString Password;
+  COpenCallbackConsole(): PasswordIsDefined(false), PasswordWasAsked(false) {}
+  #endif
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/PercentPrinter.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/PercentPrinter.cpp
new file mode 100644
index 0000000..28452b1
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/PercentPrinter.cpp
@@ -0,0 +1,90 @@
+// PercentPrinter.cpp
+
+#include "StdAfx.h"
+
+#include "Common/IntToString.h"
+#include "Common/MyString.h"
+
+#include "PercentPrinter.h"
+
+const int kPaddingSize = 2;
+const int kPercentsSize = 4;
+const int kMaxExtraSize = kPaddingSize + 32 + kPercentsSize;
+
+static void ClearPrev(char *p, int num)
+{
+  int i;
+  for (i = 0; i < num; i++) *p++ = '\b';
+  for (i = 0; i < num; i++) *p++ = ' ';
+  for (i = 0; i < num; i++) *p++ = '\b';
+  *p = '\0';
+}
+
+void CPercentPrinter::ClosePrint()
+{
+  if (m_NumExtraChars == 0)
+    return;
+  char s[kMaxExtraSize * 3 + 1];
+  ClearPrev(s, m_NumExtraChars);
+  (*OutStream) << s;
+  m_NumExtraChars = 0;
+}
+
+void CPercentPrinter::PrintString(const char *s)
+{
+  ClosePrint();
+  (*OutStream) << s;
+}
+
+void CPercentPrinter::PrintString(const wchar_t *s)
+{
+  ClosePrint();
+  (*OutStream) << s;
+}
+
+void CPercentPrinter::PrintNewLine()
+{
+  ClosePrint();
+  (*OutStream) << "\n";
+}
+
+void CPercentPrinter::RePrintRatio()
+{
+  char s[32];
+  ConvertUInt64ToString(((m_Total == 0) ? 0 : (m_CurValue * 100 / m_Total)), s);
+  int size = (int)strlen(s);
+  s[size++] = '%';
+  s[size] = '\0';
+
+  int extraSize = kPaddingSize + MyMax(size, kPercentsSize);
+  if (extraSize < m_NumExtraChars)
+    extraSize = m_NumExtraChars;
+
+  char fullString[kMaxExtraSize * 3];
+  char *p = fullString;
+  int i;
+  if (m_NumExtraChars == 0)
+  {
+    for (i = 0; i < extraSize; i++)
+      *p++ = ' ';
+    m_NumExtraChars = extraSize;
+  }
+
+  for (i = 0; i < m_NumExtraChars; i++)
+    *p++ = '\b';
+  m_NumExtraChars = extraSize;
+  for (; size < m_NumExtraChars; size++)
+    *p++ = ' ';
+  MyStringCopy(p, s);
+  (*OutStream) << fullString;
+  OutStream->Flush();
+  m_PrevValue = m_CurValue;
+}
+
+void CPercentPrinter::PrintRatio()
+{
+  if (m_CurValue < m_PrevValue + m_MinStepSize &&
+      m_CurValue + m_MinStepSize > m_PrevValue && m_NumExtraChars != 0)
+    return;
+  RePrintRatio();
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/PercentPrinter.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/PercentPrinter.h
new file mode 100644
index 0000000..97f2e6a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/PercentPrinter.h
@@ -0,0 +1,31 @@
+// PercentPrinter.h
+
+#ifndef __PERCENTPRINTER_H
+#define __PERCENTPRINTER_H
+
+#include "Common/Types.h"
+#include "Common/StdOutStream.h"
+
+class CPercentPrinter
+{
+  UInt64 m_MinStepSize;
+  UInt64 m_PrevValue;
+  UInt64 m_CurValue;
+  UInt64 m_Total;
+  int m_NumExtraChars;
+public:
+  CStdOutStream *OutStream;
+
+  CPercentPrinter(UInt64 minStepSize = 1): m_MinStepSize(minStepSize),
+      m_PrevValue(0), m_CurValue(0), m_Total(1), m_NumExtraChars(0) {}
+  void SetTotal(UInt64 total) { m_Total = total; m_PrevValue = 0; }
+  void SetRatio(UInt64 doneValue) { m_CurValue = doneValue; }
+  void PrintString(const char *s);
+  void PrintString(const wchar_t *s);
+  void PrintNewLine();
+  void ClosePrint();
+  void RePrintRatio();
+  void PrintRatio();
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/StdAfx.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/StdAfx.cpp
new file mode 100644
index 0000000..d0feea8
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/StdAfx.cpp
@@ -0,0 +1,3 @@
+// StdAfx.cpp
+
+#include "StdAfx.h"
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/StdAfx.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/StdAfx.h
new file mode 100644
index 0000000..2e4be10
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/StdAfx.h
@@ -0,0 +1,9 @@
+// StdAfx.h
+
+#ifndef __STDAFX_H
+#define __STDAFX_H
+
+#include "../../../Common/MyWindows.h"
+#include "../../../Common/NewHandler.h"
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/UpdateCallbackConsole.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/UpdateCallbackConsole.cpp
new file mode 100644
index 0000000..bf22d34
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/UpdateCallbackConsole.cpp
@@ -0,0 +1,238 @@
+// UpdateCallbackConsole.cpp
+
+#include "StdAfx.h"
+
+#include "UpdateCallbackConsole.h"
+
+#include "Windows/Error.h"
+#ifdef COMPRESS_MT
+#include "Windows/Synchronization.h"
+#endif
+
+#include "ConsoleClose.h"
+#include "UserInputUtils.h"
+
+using namespace NWindows;
+
+#ifdef COMPRESS_MT
+static NSynchronization::CCriticalSection g_CriticalSection;
+#define MT_LOCK NSynchronization::CCriticalSectionLock lock(g_CriticalSection);
+#else
+#define MT_LOCK
+#endif
+
+static const wchar_t *kEmptyFileAlias = L"[Content]";
+
+static const char *kCreatingArchiveMessage = "Creating archive ";
+static const char *kUpdatingArchiveMessage = "Updating archive ";
+static const char *kScanningMessage = "Scanning";
+
+
+HRESULT CUpdateCallbackConsole::OpenResult(const wchar_t *name, HRESULT result)
+{
+  (*OutStream) << endl;
+  if (result != S_OK)
+    (*OutStream) << "Error: " << name << " is not supported archive" << endl;
+  return S_OK;
+}
+
+HRESULT CUpdateCallbackConsole::StartScanning()
+{
+  (*OutStream) << kScanningMessage;
+  return S_OK;
+}
+
+HRESULT CUpdateCallbackConsole::ScanProgress(UInt64 /* numFolders */, UInt64 /* numFiles */, const wchar_t * /* path */)
+{
+  return CheckBreak();
+}
+
+HRESULT CUpdateCallbackConsole::CanNotFindError(const wchar_t *name, DWORD systemError)
+{
+  CantFindFiles.Add(name);
+  CantFindCodes.Add(systemError);
+  // m_PercentPrinter.ClosePrint();
+  if (!m_WarningsMode)
+  {
+    (*OutStream) << endl << endl;
+    m_PercentPrinter.PrintNewLine();
+    m_WarningsMode = true;
+  }
+  m_PercentPrinter.PrintString(name);
+  m_PercentPrinter.PrintString(":  WARNING: ");
+  m_PercentPrinter.PrintString(NError::MyFormatMessageW(systemError));
+  m_PercentPrinter.PrintNewLine();
+  return S_OK;
+}
+
+HRESULT CUpdateCallbackConsole::FinishScanning()
+{
+  (*OutStream) << endl << endl;
+  return S_OK;
+}
+
+HRESULT CUpdateCallbackConsole::StartArchive(const wchar_t *name, bool updating)
+{
+  if(updating)
+    (*OutStream) << kUpdatingArchiveMessage;
+  else
+    (*OutStream) << kCreatingArchiveMessage;
+  if (name != 0)
+    (*OutStream) << name;
+  else
+    (*OutStream) << "StdOut";
+  (*OutStream) << endl << endl;
+  return S_OK;
+}
+
+HRESULT CUpdateCallbackConsole::FinishArchive()
+{
+  (*OutStream) << endl;
+  return S_OK;
+}
+
+HRESULT CUpdateCallbackConsole::CheckBreak()
+{
+  if (NConsoleClose::TestBreakSignal())
+    return E_ABORT;
+  return S_OK;
+}
+
+HRESULT CUpdateCallbackConsole::Finilize()
+{
+  MT_LOCK
+  if (m_NeedBeClosed)
+  {
+    if (EnablePercents)
+    {
+      m_PercentPrinter.ClosePrint();
+    }
+    if (!StdOutMode && m_NeedNewLine)
+    {
+      m_PercentPrinter.PrintNewLine();
+      m_NeedNewLine = false;
+    }
+    m_NeedBeClosed = false;
+  }
+  return S_OK;
+}
+
+HRESULT CUpdateCallbackConsole::SetNumFiles(UInt64 /* numFiles */)
+{
+  return S_OK;
+}
+
+HRESULT CUpdateCallbackConsole::SetTotal(UInt64 size)
+{
+  MT_LOCK
+  if (EnablePercents)
+    m_PercentPrinter.SetTotal(size);
+  return S_OK;
+}
+
+HRESULT CUpdateCallbackConsole::SetCompleted(const UInt64 *completeValue)
+{
+  MT_LOCK
+  if (completeValue != NULL)
+  {
+    if (EnablePercents)
+    {
+      m_PercentPrinter.SetRatio(*completeValue);
+      m_PercentPrinter.PrintRatio();
+      m_NeedBeClosed = true;
+    }
+  }
+  if (NConsoleClose::TestBreakSignal())
+    return E_ABORT;
+  return S_OK;
+}
+
+HRESULT CUpdateCallbackConsole::SetRatioInfo(const UInt64 * /* inSize */, const UInt64 * /* outSize */)
+{
+  if (NConsoleClose::TestBreakSignal())
+    return E_ABORT;
+  return S_OK;
+}
+
+HRESULT CUpdateCallbackConsole::GetStream(const wchar_t *name, bool isAnti)
+{
+  MT_LOCK
+  if (StdOutMode)
+    return S_OK;
+  if(isAnti)
+    m_PercentPrinter.PrintString("Anti item    ");
+  else
+    m_PercentPrinter.PrintString("Compressing  ");
+  if (name[0] == 0)
+    name = kEmptyFileAlias;
+  m_PercentPrinter.PrintString(name);
+  if (EnablePercents)
+    m_PercentPrinter.RePrintRatio();
+  return S_OK;
+}
+
+HRESULT CUpdateCallbackConsole::OpenFileError(const wchar_t *name, DWORD systemError)
+{
+  MT_LOCK
+  FailedCodes.Add(systemError);
+  FailedFiles.Add(name);
+  // if (systemError == ERROR_SHARING_VIOLATION)
+  {
+    m_PercentPrinter.ClosePrint();
+    m_PercentPrinter.PrintNewLine();
+    m_PercentPrinter.PrintString("WARNING: ");
+    m_PercentPrinter.PrintString(NError::MyFormatMessageW(systemError));
+    return S_FALSE;
+  }
+  // return systemError;
+}
+
+HRESULT CUpdateCallbackConsole::SetOperationResult(Int32 )
+{
+  m_NeedBeClosed = true;
+  m_NeedNewLine = true;
+  return S_OK;
+}
+
+HRESULT CUpdateCallbackConsole::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)
+{
+  #ifdef _NO_CRYPTO
+
+  *passwordIsDefined = false;
+  return StringToBstr(L"", password);
+  
+  #else
+  
+  if (!PasswordIsDefined)
+  {
+    if (AskPassword)
+    {
+      Password = GetPassword(OutStream);
+      PasswordIsDefined = true;
+    }
+  }
+  *passwordIsDefined = BoolToInt(PasswordIsDefined);
+  return StringToBstr(Password, password);
+  
+  #endif
+  
+}
+
+/*
+HRESULT CUpdateCallbackConsole::ShowDeleteFile(const wchar_t *name)
+{
+  // MT_LOCK
+  if (StdOutMode)
+    return S_OK;
+  RINOK(Finilize());
+  m_PercentPrinter.PrintString("Deleting  ");
+  if (name[0] == 0)
+    name = kEmptyFileAlias;
+  m_PercentPrinter.PrintString(name);
+  if (EnablePercents)
+    m_PercentPrinter.RePrintRatio();
+  m_NeedBeClosed = true;
+  m_NeedNewLine = true;
+  return S_OK;
+}
+*/
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/UpdateCallbackConsole.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/UpdateCallbackConsole.h
new file mode 100644
index 0000000..da8d8cc
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/UpdateCallbackConsole.h
@@ -0,0 +1,61 @@
+// UpdateCallbackConsole.h
+
+#ifndef __UPDATECALLBACKCONSOLE_H
+#define __UPDATECALLBACKCONSOLE_H
+
+#include "Common/MyString.h"
+#include "Common/StdOutStream.h"
+#include "PercentPrinter.h"
+#include "../Common/Update.h"
+
+class CUpdateCallbackConsole: public IUpdateCallbackUI2
+{
+  CPercentPrinter m_PercentPrinter;
+  bool m_NeedBeClosed;
+  bool m_NeedNewLine;
+
+  bool m_WarningsMode;
+
+  CStdOutStream *OutStream;
+public:
+  bool EnablePercents;
+  bool StdOutMode;
+
+  #ifndef _NO_CRYPTO
+  bool PasswordIsDefined;
+  UString Password;
+  bool AskPassword;
+  #endif
+
+  CUpdateCallbackConsole():
+      m_PercentPrinter(1 << 16),
+      #ifndef _NO_CRYPTO
+      PasswordIsDefined(false),
+      AskPassword(false),
+      #endif
+      StdOutMode(false),
+      EnablePercents(true),
+      m_WarningsMode(false)
+      {}
+  
+  ~CUpdateCallbackConsole() { Finilize(); }
+  void Init(CStdOutStream *outStream)
+  {
+    m_NeedBeClosed = false;
+    m_NeedNewLine = false;
+    FailedFiles.Clear();
+    FailedCodes.Clear();
+    OutStream = outStream;
+    m_PercentPrinter.OutStream = outStream;
+  }
+
+  INTERFACE_IUpdateCallbackUI2(;)
+
+  UStringVector FailedFiles;
+  CRecordVector<HRESULT> FailedCodes;
+
+  UStringVector CantFindFiles;
+  CRecordVector<HRESULT> CantFindCodes;
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/UserInputUtils.cpp b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/UserInputUtils.cpp
new file mode 100644
index 0000000..b85cd59
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/UserInputUtils.cpp
@@ -0,0 +1,56 @@
+// UserInputUtils.cpp
+
+#include "StdAfx.h"
+
+#include "Common/StdInStream.h"
+#include "Common/StringConvert.h"
+
+#include "UserInputUtils.h"
+
+static const char kYes = 'Y';
+static const char kNo = 'N';
+static const char kYesAll = 'A';
+static const char kNoAll = 'S';
+static const char kAutoRenameAll = 'U';
+static const char kQuit = 'Q';
+
+static const char *kFirstQuestionMessage = "?\n";
+static const char *kHelpQuestionMessage =
+  "(Y)es / (N)o / (A)lways / (S)kip all / A(u)to rename all / (Q)uit? ";
+
+// return true if pressed Quite;
+
+NUserAnswerMode::EEnum ScanUserYesNoAllQuit(CStdOutStream *outStream)
+{
+  (*outStream) << kFirstQuestionMessage;
+  for(;;)
+  {
+    (*outStream) << kHelpQuestionMessage;
+    AString scannedString = g_StdIn.ScanStringUntilNewLine();
+    scannedString.Trim();
+    if(!scannedString.IsEmpty())
+      switch(::MyCharUpper(scannedString[0]))
+      {
+        case kYes:
+          return NUserAnswerMode::kYes;
+        case kNo:
+          return NUserAnswerMode::kNo;
+        case kYesAll:
+          return NUserAnswerMode::kYesAll;
+        case kNoAll:
+          return NUserAnswerMode::kNoAll;
+        case kAutoRenameAll:
+          return NUserAnswerMode::kAutoRenameAll;
+        case kQuit:
+          return NUserAnswerMode::kQuit;
+      }
+  }
+}
+
+UString GetPassword(CStdOutStream *outStream)
+{
+  (*outStream) << "\nEnter password:";
+  outStream->Flush();
+  AString oemPassword = g_StdIn.ScanStringUntilNewLine();
+  return MultiByteToUnicodeString(oemPassword, CP_OEMCP);
+}
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/UserInputUtils.h b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/UserInputUtils.h
new file mode 100644
index 0000000..8b5232b
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/UserInputUtils.h
@@ -0,0 +1,24 @@
+// UserInputUtils.h
+
+#ifndef __USERINPUTUTILS_H
+#define __USERINPUTUTILS_H
+
+#include "Common/StdOutStream.h"
+
+namespace NUserAnswerMode {
+
+enum EEnum
+{
+  kYes,
+  kNo,
+  kYesAll,
+  kNoAll,
+  kAutoRenameAll,
+  kQuit
+};
+}
+
+NUserAnswerMode::EEnum ScanUserYesNoAllQuit(CStdOutStream *outStream);
+UString GetPassword(CStdOutStream *outStream);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/makefile b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/makefile
new file mode 100644
index 0000000..01682e9
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/7zip/UI/Console/makefile
@@ -0,0 +1,120 @@
+PROG = 7z.exe
+LIBS = $(LIBS) user32.lib oleaut32.lib advapi32.lib
+CFLAGS = $(CFLAGS) -I ../../../  \
+  -DCOMPRESS_MT \
+  -DWIN_LONG_PATH \
+  -DEXTERNAL_LZMA \
+  -DEXTERNAL_CODECS \
+  -DBREAK_HANDLER \
+  -DBENCH_MT \
+  -D_7ZIP_LARGE_PAGES \
+
+CONSOLE_OBJS = \
+  $O\ConsoleClose.obj \
+  $O\ExtractCallbackConsole.obj \
+  $O\List.obj \
+  $O\Main.obj \
+  $O\MainAr.obj \
+  $O\OpenCallbackConsole.obj \
+  $O\PercentPrinter.obj \
+  $O\UpdateCallbackConsole.obj \
+  $O\UserInputUtils.obj \
+
+COMMON_OBJS = \
+  $O\CommandLineParser.obj \
+  $O\CRC.obj \
+  $O\IntToString.obj \
+  $O\ListFileUtils.obj \
+  $O\NewHandler.obj \
+  $O\StdInStream.obj \
+  $O\StdOutStream.obj \
+  $O\MyString.obj \
+  $O\StringConvert.obj \
+  $O\StringToInt.obj \
+  $O\UTFConvert.obj \
+  $O\MyVector.obj \
+  $O\Wildcard.obj \
+
+WIN_OBJS = \
+  $O\DLL.obj \
+  $O\Error.obj \
+  $O\FileDir.obj \
+  $O\FileFind.obj \
+  $O\FileIO.obj \
+  $O\FileName.obj \
+  $O\MemoryLock.obj \
+  $O\PropVariant.obj \
+  $O\PropVariantConversions.obj \
+  $O\Registry.obj \
+  $O\System.obj \
+  $O\Time.obj \
+
+7ZIP_COMMON_OBJS = \
+  $O\FilePathAutoRename.obj \
+  $O\FileStreams.obj \
+  $O\ProgressUtils.obj \
+  $O\StreamUtils.obj \
+
+UI_COMMON_OBJS = \
+  $O\ArchiveCommandLine.obj \
+  $O\ArchiveExtractCallback.obj \
+  $O\ArchiveOpenCallback.obj \
+  $O\DefaultName.obj \
+  $O\EnumDirItems.obj \
+  $O\Extract.obj \
+  $O\ExtractingFilePath.obj \
+  $O\LoadCodecs.obj \
+  $O\OpenArchive.obj \
+  $O\PropIDUtils.obj \
+  $O\SetProperties.obj \
+  $O\SortUtils.obj \
+  $O\TempFiles.obj \
+  $O\Update.obj \
+  $O\UpdateAction.obj \
+  $O\UpdateCallback.obj \
+  $O\UpdatePair.obj \
+  $O\UpdateProduce.obj \
+  $O\WorkDir.obj \
+
+LZMA_BENCH_OBJS = \
+  $O\LzmaBench.obj \
+  $O\LzmaBenchCon.obj \
+
+C_OBJS = \
+  $O\Alloc.obj \
+  $O\Threads.obj \
+
+!include "../../Crc2.mak"
+
+OBJS = \
+  $O\StdAfx.obj \
+  $(CONSOLE_OBJS) \
+  $(COMMON_OBJS) \
+  $(WIN_OBJS) \
+  $(7ZIP_COMMON_OBJS) \
+  $(UI_COMMON_OBJS) \
+  $O\CopyCoder.obj \
+  $(LZMA_BENCH_OBJS) \
+  $(C_OBJS) \
+  $(CRC_OBJS) \
+  $O\resource.res
+
+!include "../../../Build.mak"
+
+$(CONSOLE_OBJS): $(*B).cpp
+	$(COMPL)
+$(COMMON_OBJS): ../../../Common/$(*B).cpp
+	$(COMPL)
+$(WIN_OBJS): ../../../Windows/$(*B).cpp
+	$(COMPL)
+$(7ZIP_COMMON_OBJS): ../../Common/$(*B).cpp
+	$(COMPL)
+$(UI_COMMON_OBJS): ../Common/$(*B).cpp
+	$(COMPL)
+$O\CopyCoder.obj: ../../Compress/$(*B).cpp
+	$(COMPL)
+$(LZMA_BENCH_OBJS): ../../Compress/LZMA_Alone/$(*B).cpp
+	$(COMPL)
+$(C_OBJS): ../../../../C/$(*B).c
+	$(COMPL_O2)
+!include "../../Crc.mak"
diff --git a/third_party/lzma/v4_65/files/CPP/Build.mak b/third_party/lzma/v4_65/files/CPP/Build.mak
new file mode 100644
index 0000000..9d4ac52
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Build.mak
@@ -0,0 +1,71 @@
+!IFDEF CPU
+!IFNDEF NO_BUFFEROVERFLOWU
+LIBS = $(LIBS) bufferoverflowU.lib
+!ENDIF
+!ENDIF
+
+
+!IFNDEF O
+!IFDEF CPU
+O=$(CPU)
+!ELSE
+O=O
+!ENDIF
+!ENDIF
+
+!IF "$(CPU)" != "IA64"
+!IF "$(CPU)" != "AMD64"
+MY_ML = ml
+!ELSE
+MY_ML = ml64
+!ENDIF
+!ENDIF
+
+COMPL_ASM = $(MY_ML) -c -Fo$O/ $**
+
+CFLAGS = $(CFLAGS) -nologo -c -Fo$O/ -WX -EHsc -Gr -Gy -GR-
+
+!IFDEF MY_STATIC_LINK
+!IFNDEF MY_SINGLE_THREAD
+CFLAGS = $(CFLAGS) -MT
+!ENDIF
+!ELSE
+CFLAGS = $(CFLAGS) -MD
+!ENDIF
+
+!IFDEF NEW_COMPILER
+CFLAGS = $(CFLAGS) -W4 -GS- -Zc:forScope
+!ELSE
+CFLAGS = $(CFLAGS) -W3
+!ENDIF
+
+CFLAGS_O1 = $(CFLAGS) -O1
+CFLAGS_O2 = $(CFLAGS) -O2
+
+LFLAGS = $(LFLAGS) -nologo -OPT:NOWIN98 -OPT:REF -OPT:ICF
+
+!IFDEF DEF_FILE
+LFLAGS = $(LFLAGS) -DLL -DEF:$(DEF_FILE)
+!ENDIF
+
+PROGPATH = $O\$(PROG)
+
+COMPL_O1   = $(CPP) $(CFLAGS_O1) $**
+COMPL_O2   = $(CPP) $(CFLAGS_O2) $**
+COMPL_PCH  = $(CPP) $(CFLAGS_O1) -Yc"StdAfx.h" -Fp$O/a.pch $**
+COMPL      = $(CPP) $(CFLAGS_O1) -Yu"StdAfx.h" -Fp$O/a.pch $**
+
+all: $(PROGPATH)
+
+clean:
+	-del /Q $(PROGPATH) $O\*.exe $O\*.dll $O\*.obj $O\*.lib $O\*.exp $O\*.res $O\*.pch
+
+$O:
+	if not exist "$O" mkdir "$O"
+
+$(PROGPATH): $O $(OBJS) $(DEF_FILE)
+	link $(LFLAGS) -out:$(PROGPATH) $(OBJS) $(LIBS)
+$O\resource.res: $(*B).rc
+	rc -fo$@ $**
+$O\StdAfx.obj: $(*B).cpp
+	$(COMPL_PCH)
diff --git a/third_party/lzma/v4_65/files/CPP/Common/AutoPtr.h b/third_party/lzma/v4_65/files/CPP/Common/AutoPtr.h
new file mode 100644
index 0000000..006d315
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/AutoPtr.h
@@ -0,0 +1,35 @@
+// Common/AutoPtr.h
+
+#ifndef __COMMON_AUTOPTR_H
+#define __COMMON_AUTOPTR_H
+
+template<class T> class CMyAutoPtr
+{
+  T *_p;
+public:
+  CMyAutoPtr(T *p = 0) : _p(p) {}
+  CMyAutoPtr(CMyAutoPtr<T>& p): _p(p.release()) {}
+  CMyAutoPtr<T>& operator=(CMyAutoPtr<T>& p)
+  {
+    reset(p.release());
+    return (*this);
+  }
+  ~CMyAutoPtr() { delete _p; }
+  T& operator*() const { return *_p; }
+  // T* operator->() const { return (&**this); }
+  T* get() const { return _p; }
+  T* release()
+  {
+    T *tmp = _p;
+    _p = 0;
+    return tmp;
+  }
+  void reset(T* p = 0)
+  {
+    if (p != _p)
+      delete _p;
+    _p = p;
+  }
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/Buffer.h b/third_party/lzma/v4_65/files/CPP/Common/Buffer.h
new file mode 100644
index 0000000..b6960fa
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/Buffer.h
@@ -0,0 +1,77 @@
+// Common/Buffer.h
+
+#ifndef __COMMON_BUFFER_H
+#define __COMMON_BUFFER_H
+
+#include "Defs.h"
+
+template <class T> class CBuffer
+{
+protected:
+  size_t _capacity;
+  T *_items;
+public:
+  void Free()
+  {
+    delete []_items;
+    _items = 0;
+    _capacity = 0;
+  }
+  CBuffer(): _capacity(0), _items(0) {};
+  CBuffer(const CBuffer &buffer): _capacity(0), _items(0) { *this = buffer; }
+  CBuffer(size_t size): _items(0),  _capacity(0) {  SetCapacity(size); }
+  virtual ~CBuffer() { delete []_items; }
+  operator T *() { return _items; };
+  operator const T *() const { return _items; };
+  size_t GetCapacity() const { return  _capacity; }
+  void SetCapacity(size_t newCapacity)
+  {
+    if (newCapacity == _capacity)
+      return;
+    T *newBuffer;
+    if (newCapacity > 0)
+    {
+      newBuffer = new T[newCapacity];
+      if (_capacity > 0)
+        memmove(newBuffer, _items, MyMin(_capacity, newCapacity) * sizeof(T));
+    }
+    else
+      newBuffer = 0;
+    delete []_items;
+    _items = newBuffer;
+    _capacity = newCapacity;
+  }
+  CBuffer& operator=(const CBuffer &buffer)
+  {
+    Free();
+    if (buffer._capacity > 0)
+    {
+      SetCapacity(buffer._capacity);
+      memmove(_items, buffer._items, buffer._capacity * sizeof(T));
+    }
+    return *this;
+  }
+};
+
+template <class T>
+bool operator==(const CBuffer<T>& b1, const CBuffer<T>& b2)
+{
+  if (b1.GetCapacity() != b2.GetCapacity())
+    return false;
+  for (size_t i = 0; i < b1.GetCapacity(); i++)
+    if (b1[i] != b2[i])
+      return false;
+  return true;
+}
+
+template <class T>
+bool operator!=(const CBuffer<T>& b1, const CBuffer<T>& b2)
+{
+  return !(b1 == b2);
+}
+
+typedef CBuffer<char> CCharBuffer;
+typedef CBuffer<wchar_t> CWCharBuffer;
+typedef CBuffer<unsigned char> CByteBuffer;
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/CRC.cpp b/third_party/lzma/v4_65/files/CPP/Common/CRC.cpp
new file mode 100644
index 0000000..a1e54ec
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/CRC.cpp
@@ -0,0 +1,10 @@
+// Common/CRC.cpp
+
+#include "StdAfx.h"
+
+extern "C"
+{
+#include "../../C/7zCrc.h"
+}
+
+struct CCRCTableInit { CCRCTableInit() { CrcGenerateTable(); } } g_CRCTableInit;
diff --git a/third_party/lzma/v4_65/files/CPP/Common/C_FileIO.cpp b/third_party/lzma/v4_65/files/CPP/Common/C_FileIO.cpp
new file mode 100644
index 0000000..b4893d6
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/C_FileIO.cpp
@@ -0,0 +1,88 @@
+// Common/C_FileIO.h
+
+#include "C_FileIO.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+namespace NC {
+namespace NFile {
+namespace NIO {
+
+bool CFileBase::OpenBinary(const char *name, int flags)
+{
+  #ifdef O_BINARY
+  flags |= O_BINARY;
+  #endif
+  Close();
+  _handle = ::open(name, flags, 0666);
+  return _handle != -1;
+}
+
+bool CFileBase::Close()
+{
+  if (_handle == -1)
+    return true;
+  if (close(_handle) != 0)
+    return false;
+  _handle = -1;
+  return true;
+}
+
+bool CFileBase::GetLength(UInt64 &length) const
+{
+  off_t curPos = Seek(0, SEEK_CUR);
+  off_t lengthTemp = Seek(0, SEEK_END);
+  Seek(curPos, SEEK_SET);
+  length = (UInt64)lengthTemp;
+  return true;
+}
+
+off_t CFileBase::Seek(off_t distanceToMove, int moveMethod) const
+{
+  return ::lseek(_handle, distanceToMove, moveMethod);
+}
+
+/////////////////////////
+// CInFile
+
+bool CInFile::Open(const char *name)
+{
+  return CFileBase::OpenBinary(name, O_RDONLY);
+}
+
+bool CInFile::OpenShared(const char *name, bool)
+{
+  return Open(name);
+}
+
+ssize_t CInFile::Read(void *data, size_t size)
+{
+  return read(_handle, data, size);
+}
+
+/////////////////////////
+// COutFile
+
+bool COutFile::Create(const char *name, bool createAlways)
+{
+  if (createAlways)
+  {
+    Close();
+    _handle = ::creat(name, 0666);
+    return _handle != -1;
+  }
+  return OpenBinary(name, O_CREAT | O_EXCL | O_WRONLY);
+}
+
+bool COutFile::Open(const char *name, DWORD creationDisposition)
+{
+  return Create(name, false);
+}
+
+ssize_t COutFile::Write(const void *data, size_t size)
+{
+  return write(_handle, data, size);
+}
+
+}}}
diff --git a/third_party/lzma/v4_65/files/CPP/Common/C_FileIO.h b/third_party/lzma/v4_65/files/CPP/Common/C_FileIO.h
new file mode 100644
index 0000000..27aa568
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/C_FileIO.h
@@ -0,0 +1,47 @@
+// Common/C_FileIO.h
+
+#ifndef __COMMON_C_FILEIO_H
+#define __COMMON_C_FILEIO_H
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "Types.h"
+#include "MyWindows.h"
+
+namespace NC {
+namespace NFile {
+namespace NIO {
+
+class CFileBase
+{
+protected:
+  int _handle;
+  bool OpenBinary(const char *name, int flags);
+public:
+  CFileBase(): _handle(-1) {};
+  ~CFileBase() { Close(); }
+  bool Close();
+  bool GetLength(UInt64 &length) const;
+  off_t Seek(off_t distanceToMove, int moveMethod) const;
+};
+
+class CInFile: public CFileBase
+{
+public:
+  bool Open(const char *name);
+  bool OpenShared(const char *name, bool shareForWrite);
+  ssize_t Read(void *data, size_t size);
+};
+
+class COutFile: public CFileBase
+{
+public:
+  bool Create(const char *name, bool createAlways);
+  bool Open(const char *name, DWORD creationDisposition);
+  ssize_t Write(const void *data, size_t size);
+};
+
+}}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/ComTry.h b/third_party/lzma/v4_65/files/CPP/Common/ComTry.h
new file mode 100644
index 0000000..5153362
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/ComTry.h
@@ -0,0 +1,17 @@
+// ComTry.h
+
+#ifndef __COM_TRY_H
+#define __COM_TRY_H
+
+#include "MyWindows.h"
+// #include "Exception.h"
+// #include "NewHandler.h"
+
+#define COM_TRY_BEGIN try {
+#define COM_TRY_END } catch(...) { return E_OUTOFMEMORY; }
+  
+  // catch(const CNewException &) { return E_OUTOFMEMORY; }\
+  // catch(const CSystemException &e) { return e.ErrorCode; }\
+  // catch(...) { return E_FAIL; }
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/CommandLineParser.cpp b/third_party/lzma/v4_65/files/CPP/Common/CommandLineParser.cpp
new file mode 100644
index 0000000..6de5e63
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/CommandLineParser.cpp
@@ -0,0 +1,232 @@
+// CommandLineParser.cpp
+
+#include "StdAfx.h"
+
+#include "CommandLineParser.h"
+
+namespace NCommandLineParser {
+
+void SplitCommandLine(const UString &src, UString &dest1, UString &dest2)
+{
+  dest1.Empty();
+  dest2.Empty();
+  bool quoteMode = false;
+  int i;
+  for (i = 0; i < src.Length(); i++)
+  {
+    wchar_t c = src[i];
+    if (c == L'\"')
+      quoteMode = !quoteMode;
+    else if (c == L' ' && !quoteMode)
+    {
+      i++;
+      break;
+    }
+    else
+      dest1 += c;
+  }
+  dest2 = src.Mid(i);
+}
+
+void SplitCommandLine(const UString &s, UStringVector &parts)
+{
+  UString sTemp = s;
+  sTemp.Trim();
+  parts.Clear();
+  for (;;)
+  {
+    UString s1, s2;
+    SplitCommandLine(sTemp, s1, s2);
+    // s1.Trim();
+    // s2.Trim();
+    if (!s1.IsEmpty())
+      parts.Add(s1);
+    if (s2.IsEmpty())
+      break;
+    sTemp = s2;
+  }
+}
+
+
+static const wchar_t kSwitchID1 = '-';
+// static const wchar_t kSwitchID2 = '/';
+
+static const wchar_t kSwitchMinus = '-';
+static const wchar_t *kStopSwitchParsing = L"--";
+
+static bool IsItSwitchChar(wchar_t c)
+{
+  return (c == kSwitchID1 /*|| c == kSwitchID2 */);
+}
+
+CParser::CParser(int numSwitches):
+  _numSwitches(numSwitches)
+{
+  _switches = new CSwitchResult[_numSwitches];
+}
+
+CParser::~CParser()
+{
+  delete []_switches;
+}
+
+void CParser::ParseStrings(const CSwitchForm *switchForms,
+  const UStringVector &commandStrings)
+{
+  int numCommandStrings = commandStrings.Size();
+  bool stopSwitch = false;
+  for (int i = 0; i < numCommandStrings; i++)
+  {
+    const UString &s = commandStrings[i];
+    if (stopSwitch)
+      NonSwitchStrings.Add(s);
+    else
+      if (s == kStopSwitchParsing)
+        stopSwitch = true;
+      else
+        if (!ParseString(s, switchForms))
+          NonSwitchStrings.Add(s);
+  }
+}
+
+// if string contains switch then function updates switch structures
+// out: (string is a switch)
+bool CParser::ParseString(const UString &s, const CSwitchForm *switchForms)
+{
+  int len = s.Length();
+  if (len == 0)
+    return false;
+  int pos = 0;
+  if (!IsItSwitchChar(s[pos]))
+    return false;
+  while (pos < len)
+  {
+    if (IsItSwitchChar(s[pos]))
+      pos++;
+    const int kNoLen = -1;
+    int matchedSwitchIndex = 0; // GCC Warning
+    int maxLen = kNoLen;
+    for (int switchIndex = 0; switchIndex < _numSwitches; switchIndex++)
+    {
+      int switchLen = MyStringLen(switchForms[switchIndex].IDString);
+      if (switchLen <= maxLen || pos + switchLen > len)
+        continue;
+
+      UString temp = s + pos;
+      temp = temp.Left(switchLen);
+      if (temp.CompareNoCase(switchForms[switchIndex].IDString) == 0)
+      // if (_strnicmp(switchForms[switchIndex].IDString, LPCSTR(s) + pos, switchLen) == 0)
+      {
+        matchedSwitchIndex = switchIndex;
+        maxLen = switchLen;
+      }
+    }
+    if (maxLen == kNoLen)
+      throw "maxLen == kNoLen";
+    CSwitchResult &matchedSwitch = _switches[matchedSwitchIndex];
+    const CSwitchForm &switchForm = switchForms[matchedSwitchIndex];
+    if ((!switchForm.Multi) && matchedSwitch.ThereIs)
+      throw "switch must be single";
+    matchedSwitch.ThereIs = true;
+    pos += maxLen;
+    int tailSize = len - pos;
+    NSwitchType::EEnum type = switchForm.Type;
+    switch(type)
+    {
+      case NSwitchType::kPostMinus:
+        {
+          if (tailSize == 0)
+            matchedSwitch.WithMinus = false;
+          else
+          {
+            matchedSwitch.WithMinus = (s[pos] == kSwitchMinus);
+            if (matchedSwitch.WithMinus)
+              pos++;
+          }
+          break;
+        }
+      case NSwitchType::kPostChar:
+        {
+          if (tailSize < switchForm.MinLen)
+            throw "switch is not full";
+          UString set = switchForm.PostCharSet;
+          const int kEmptyCharValue = -1;
+          if (tailSize == 0)
+            matchedSwitch.PostCharIndex = kEmptyCharValue;
+          else
+          {
+            int index = set.Find(s[pos]);
+            if (index < 0)
+              matchedSwitch.PostCharIndex =  kEmptyCharValue;
+            else
+            {
+              matchedSwitch.PostCharIndex = index;
+              pos++;
+            }
+          }
+          break;
+        }
+      case NSwitchType::kLimitedPostString:
+      case NSwitchType::kUnLimitedPostString:
+        {
+          int minLen = switchForm.MinLen;
+          if (tailSize < minLen)
+            throw "switch is not full";
+          if (type == NSwitchType::kUnLimitedPostString)
+          {
+            matchedSwitch.PostStrings.Add(s.Mid(pos));
+            return true;
+          }
+          int maxLen = switchForm.MaxLen;
+          UString stringSwitch = s.Mid(pos, minLen);
+          pos += minLen;
+          for (int i = minLen; i < maxLen && pos < len; i++, pos++)
+          {
+            wchar_t c = s[pos];
+            if (IsItSwitchChar(c))
+              break;
+            stringSwitch += c;
+          }
+          matchedSwitch.PostStrings.Add(stringSwitch);
+          break;
+        }
+      case NSwitchType::kSimple:
+          break;
+    }
+  }
+  return true;
+}
+
+const CSwitchResult& CParser::operator[](size_t index) const
+{
+  return _switches[index];
+}
+
+/////////////////////////////////
+// Command parsing procedures
+
+int ParseCommand(int numCommandForms, const CCommandForm *commandForms,
+    const UString &commandString, UString &postString)
+{
+  for (int i = 0; i < numCommandForms; i++)
+  {
+    const UString id = commandForms[i].IDString;
+    if (commandForms[i].PostStringMode)
+    {
+      if (commandString.Find(id) == 0)
+      {
+        postString = commandString.Mid(id.Length());
+        return i;
+      }
+    }
+    else
+      if (commandString == id)
+      {
+        postString.Empty();
+        return i;
+      }
+  }
+  return -1;
+}
+   
+}
diff --git a/third_party/lzma/v4_65/files/CPP/Common/CommandLineParser.h b/third_party/lzma/v4_65/files/CPP/Common/CommandLineParser.h
new file mode 100644
index 0000000..6c7226a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/CommandLineParser.h
@@ -0,0 +1,72 @@
+// Common/CommandLineParser.h
+
+#ifndef __COMMON_COMMANDLINEPARSER_H
+#define __COMMON_COMMANDLINEPARSER_H
+
+#include "MyString.h"
+
+namespace NCommandLineParser {
+
+void SplitCommandLine(const UString &src, UString &dest1, UString &dest2);
+void SplitCommandLine(const UString &s, UStringVector &parts);
+
+namespace NSwitchType {
+  enum EEnum
+  {
+    kSimple,
+    kPostMinus,
+    kLimitedPostString,
+    kUnLimitedPostString,
+    kPostChar
+  };
+}
+
+struct CSwitchForm
+{
+  const wchar_t *IDString;
+  NSwitchType::EEnum Type;
+  bool Multi;
+  int MinLen;
+  int MaxLen;
+  const wchar_t *PostCharSet;
+};
+
+struct CSwitchResult
+{
+  bool ThereIs;
+  bool WithMinus;
+  UStringVector PostStrings;
+  int PostCharIndex;
+  CSwitchResult(): ThereIs(false) {};
+};
+  
+class CParser
+{
+  int _numSwitches;
+  CSwitchResult *_switches;
+  bool ParseString(const UString &s, const CSwitchForm *switchForms);
+public:
+  UStringVector NonSwitchStrings;
+  CParser(int numSwitches);
+  ~CParser();
+  void ParseStrings(const CSwitchForm *switchForms,
+    const UStringVector &commandStrings);
+  const CSwitchResult& operator[](size_t index) const;
+};
+
+/////////////////////////////////
+// Command parsing procedures
+
+struct CCommandForm
+{
+  wchar_t *IDString;
+  bool PostStringMode;
+};
+
+// Returns: Index of form and postString; -1, if there is no match
+int ParseCommand(int numCommandForms, const CCommandForm *commandForms,
+    const UString &commandString, UString &postString);
+
+}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/Defs.h b/third_party/lzma/v4_65/files/CPP/Common/Defs.h
new file mode 100644
index 0000000..dad3ae8
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/Defs.h
@@ -0,0 +1,20 @@
+// Common/Defs.h
+
+#ifndef __COMMON_DEFS_H
+#define __COMMON_DEFS_H
+
+template <class T> inline T MyMin(T a, T b)
+  {  return a < b ? a : b; }
+template <class T> inline T MyMax(T a, T b)
+  {  return a > b ? a : b; }
+
+template <class T> inline int MyCompare(T a, T b)
+  {  return a < b ? -1 : (a == b ? 0 : 1); }
+
+inline int BoolToInt(bool value)
+  { return (value ? 1: 0); }
+
+inline bool IntToBool(int value)
+  { return (value != 0); }
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/DynamicBuffer.h b/third_party/lzma/v4_65/files/CPP/Common/DynamicBuffer.h
new file mode 100644
index 0000000..fd33cec
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/DynamicBuffer.h
@@ -0,0 +1,47 @@
+// Common/DynamicBuffer.h
+
+#ifndef __COMMON_DYNAMICBUFFER_H
+#define __COMMON_DYNAMICBUFFER_H
+
+#include "Buffer.h"
+
+template <class T> class CDynamicBuffer: public CBuffer<T>
+{
+  void GrowLength(size_t size)
+  {
+    size_t delta;
+    if (this->_capacity > 64)
+      delta = this->_capacity / 4;
+    else if (this->_capacity > 8)
+      delta = 16;
+    else
+      delta = 4;
+    delta = MyMax(delta, size);
+    SetCapacity(this->_capacity + delta);
+  }
+public:
+  CDynamicBuffer(): CBuffer<T>() {};
+  CDynamicBuffer(const CDynamicBuffer &buffer): CBuffer<T>(buffer) {};
+  CDynamicBuffer(size_t size): CBuffer<T>(size) {};
+  CDynamicBuffer& operator=(const CDynamicBuffer &buffer)
+  {
+    this->Free();
+    if (buffer._capacity > 0)
+    {
+      SetCapacity(buffer._capacity);
+      memmove(this->_items, buffer._items, buffer._capacity * sizeof(T));
+    }
+    return *this;
+  }
+  void EnsureCapacity(size_t capacity)
+  {
+    if (this->_capacity < capacity)
+      GrowLength(capacity - this->_capacity);
+  }
+};
+
+typedef CDynamicBuffer<char> CCharDynamicBuffer;
+typedef CDynamicBuffer<wchar_t> CWCharDynamicBuffer;
+typedef CDynamicBuffer<unsigned char> CByteDynamicBuffer;
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/IntToString.cpp b/third_party/lzma/v4_65/files/CPP/Common/IntToString.cpp
new file mode 100644
index 0000000..beda203
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/IntToString.cpp
@@ -0,0 +1,63 @@
+// Common/IntToString.cpp
+
+#include "StdAfx.h"
+
+#include "IntToString.h"
+
+void ConvertUInt64ToString(UInt64 value, char *s, UInt32 base)
+{
+  if (base < 2 || base > 36)
+  {
+    *s = '\0';
+    return;
+  }
+  char temp[72];
+  int pos = 0;
+  do
+  {
+    int delta = (int)(value % base);
+    temp[pos++] = (char)((delta < 10) ? ('0' + delta) : ('a' + (delta - 10)));
+    value /= base;
+  }
+  while (value != 0);
+  do
+    *s++ = temp[--pos];
+  while (pos > 0);
+  *s = '\0';
+}
+
+void ConvertUInt64ToString(UInt64 value, wchar_t *s)
+{
+  wchar_t temp[32];
+  int pos = 0;
+  do
+  {
+    temp[pos++] = (wchar_t)(L'0' + (int)(value % 10));
+    value /= 10;
+  }
+  while (value != 0);
+  do
+    *s++ = temp[--pos];
+  while (pos > 0);
+  *s = L'\0';
+}
+
+void ConvertInt64ToString(Int64 value, char *s)
+{
+  if (value < 0)
+  {
+    *s++ = '-';
+    value = -value;
+  }
+  ConvertUInt64ToString(value, s);
+}
+
+void ConvertInt64ToString(Int64 value, wchar_t *s)
+{
+  if (value < 0)
+  {
+    *s++ = L'-';
+    value = -value;
+  }
+  ConvertUInt64ToString(value, s);
+}
diff --git a/third_party/lzma/v4_65/files/CPP/Common/IntToString.h b/third_party/lzma/v4_65/files/CPP/Common/IntToString.h
new file mode 100644
index 0000000..cf86090
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/IntToString.h
@@ -0,0 +1,15 @@
+// Common/IntToString.h
+
+#ifndef __COMMON_INTTOSTRING_H
+#define __COMMON_INTTOSTRING_H
+
+#include <stddef.h>
+#include "Types.h"
+
+void ConvertUInt64ToString(UInt64 value, char *s, UInt32 base = 10);
+void ConvertUInt64ToString(UInt64 value, wchar_t *s);
+
+void ConvertInt64ToString(Int64 value, char *s);
+void ConvertInt64ToString(Int64 value, wchar_t *s);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/ListFileUtils.cpp b/third_party/lzma/v4_65/files/CPP/Common/ListFileUtils.cpp
new file mode 100644
index 0000000..c1c682a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/ListFileUtils.cpp
@@ -0,0 +1,75 @@
+// Common/ListFileUtils.cpp
+
+#include "StdAfx.h"
+
+#include "MyWindows.h"
+#include "../Windows/FileIO.h"
+
+#include "ListFileUtils.h"
+#include "StringConvert.h"
+#include "UTFConvert.h"
+
+static const char kQuoteChar     = '\"';
+static void RemoveQuote(UString &s)
+{
+  if (s.Length() >= 2)
+    if (s[0] == kQuoteChar && s[s.Length() - 1] == kQuoteChar)
+      s = s.Mid(1, s.Length() - 2);
+}
+
+bool ReadNamesFromListFile(LPCWSTR fileName, UStringVector &resultStrings, UINT codePage)
+{
+  NWindows::NFile::NIO::CInFile file;
+  if (!file.Open(fileName))
+    return false;
+  UInt64 length;
+  if (!file.GetLength(length))
+    return false;
+  if (length > ((UInt32)1 << 31))
+    return false;
+  AString s;
+  char *p = s.GetBuffer((int)length + 1);
+  UInt32 processed;
+  if (!file.Read(p, (UInt32)length, processed))
+    return false;
+  p[(UInt32)length] = 0;
+  s.ReleaseBuffer();
+  file.Close();
+
+  UString u;
+  #ifdef CP_UTF8
+  if (codePage == CP_UTF8)
+  {
+    if (!ConvertUTF8ToUnicode(s, u))
+      return false;
+  }
+  else
+  #endif
+    u = MultiByteToUnicodeString(s, codePage);
+  if (!u.IsEmpty())
+  {
+    if (u[0] == 0xFEFF)
+      u.Delete(0);
+  }
+
+  UString t;
+  for (int i = 0; i < u.Length(); i++)
+  {
+    wchar_t c = u[i];
+    if (c == L'\n' || c == 0xD)
+    {
+      t.Trim();
+      RemoveQuote(t);
+      if (!t.IsEmpty())
+        resultStrings.Add(t);
+      t.Empty();
+    }
+    else
+      t += c;
+  }
+  t.Trim();
+  RemoveQuote(t);
+  if (!t.IsEmpty())
+    resultStrings.Add(t);
+  return true;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/Common/ListFileUtils.h b/third_party/lzma/v4_65/files/CPP/Common/ListFileUtils.h
new file mode 100644
index 0000000..c58a8bd
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/ListFileUtils.h
@@ -0,0 +1,11 @@
+// Common/ListFileUtils.h
+
+#ifndef __COMMON_LISTFILEUTILS_H
+#define __COMMON_LISTFILEUTILS_H
+
+#include "MyString.h"
+#include "Types.h"
+
+bool ReadNamesFromListFile(LPCWSTR fileName, UStringVector &strings, UINT codePage = CP_OEMCP);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/MyCom.h b/third_party/lzma/v4_65/files/CPP/Common/MyCom.h
new file mode 100644
index 0000000..2f00c25
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/MyCom.h
@@ -0,0 +1,225 @@
+// MyCom.h
+
+#ifndef __MYCOM_H
+#define __MYCOM_H
+
+#include "MyWindows.h"
+
+#ifndef RINOK
+#define RINOK(x) { HRESULT __result_ = (x); if (__result_ != S_OK) return __result_; }
+#endif
+
+template <class T>
+class CMyComPtr
+{
+  T* _p;
+public:
+  // typedef T _PtrClass;
+  CMyComPtr() { _p = NULL;}
+  CMyComPtr(T* p) {if ((_p = p) != NULL) p->AddRef(); }
+  CMyComPtr(const CMyComPtr<T>& lp)
+  {
+    if ((_p = lp._p) != NULL)
+      _p->AddRef();
+  }
+  ~CMyComPtr() { if (_p) _p->Release(); }
+  void Release() { if (_p) { _p->Release(); _p = NULL; } }
+  operator T*() const {  return (T*)_p;  }
+  // T& operator*() const {  return *_p; }
+  T** operator&() { return &_p; }
+  T* operator->() const { return _p; }
+  T* operator=(T* p)
+  {
+    if (p != 0)
+      p->AddRef();
+    if (_p)
+      _p->Release();
+    _p = p;
+    return p;
+  }
+  T* operator=(const CMyComPtr<T>& lp) { return (*this = lp._p); }
+  bool operator!() const { return (_p == NULL); }
+  // bool operator==(T* pT) const {  return _p == pT; }
+  // Compare two objects for equivalence
+  void Attach(T* p2)
+  {
+    Release();
+    _p = p2;
+  }
+  T* Detach()
+  {
+    T* pt = _p;
+    _p = NULL;
+    return pt;
+  }
+  #ifdef _WIN32
+  HRESULT CoCreateInstance(REFCLSID rclsid, REFIID iid, LPUNKNOWN pUnkOuter = NULL, DWORD dwClsContext = CLSCTX_ALL)
+  {
+    return ::CoCreateInstance(rclsid, pUnkOuter, dwClsContext, iid, (void**)&_p);
+  }
+  #endif
+  /*
+  HRESULT CoCreateInstance(LPCOLESTR szProgID, LPUNKNOWN pUnkOuter = NULL, DWORD dwClsContext = CLSCTX_ALL)
+  {
+    CLSID clsid;
+    HRESULT hr = CLSIDFromProgID(szProgID, &clsid);
+    ATLASSERT(_p == NULL);
+    if (SUCCEEDED(hr))
+      hr = ::CoCreateInstance(clsid, pUnkOuter, dwClsContext, __uuidof(T), (void**)&_p);
+    return hr;
+  }
+  */
+  template <class Q>
+  HRESULT QueryInterface(REFGUID iid, Q** pp) const
+  {
+    return _p->QueryInterface(iid, (void**)pp);
+  }
+};
+
+//////////////////////////////////////////////////////////
+
+inline HRESULT StringToBstr(LPCOLESTR src, BSTR *bstr)
+{
+  *bstr = ::SysAllocString(src);
+  return (*bstr != 0) ? S_OK : E_OUTOFMEMORY;
+}
+
+class CMyComBSTR
+{
+public:
+  BSTR m_str;
+  CMyComBSTR(): m_str(NULL) {}
+  CMyComBSTR(LPCOLESTR src) { m_str = ::SysAllocString(src); }
+  // CMyComBSTR(int nSize) { m_str = ::SysAllocStringLen(NULL, nSize); }
+  // CMyComBSTR(int nSize, LPCOLESTR sz) { m_str = ::SysAllocStringLen(sz, nSize);  }
+  CMyComBSTR(const CMyComBSTR& src) { m_str = src.MyCopy(); }
+  /*
+  CMyComBSTR(REFGUID src)
+  {
+    LPOLESTR szGuid;
+    StringFromCLSID(src, &szGuid);
+    m_str = ::SysAllocString(szGuid);
+    CoTaskMemFree(szGuid);
+  }
+  */
+  ~CMyComBSTR() { ::SysFreeString(m_str); }
+  CMyComBSTR& operator=(const CMyComBSTR& src)
+  {
+    if (m_str != src.m_str)
+    {
+      if (m_str)
+        ::SysFreeString(m_str);
+      m_str = src.MyCopy();
+    }
+    return *this;
+  }
+  CMyComBSTR& operator=(LPCOLESTR src)
+  {
+    ::SysFreeString(m_str);
+    m_str = ::SysAllocString(src);
+    return *this;
+  }
+  unsigned int Length() const { return ::SysStringLen(m_str); }
+  operator BSTR() const { return m_str; }
+  BSTR* operator&() { return &m_str; }
+  BSTR MyCopy() const
+  {
+    int byteLen = ::SysStringByteLen(m_str);
+    BSTR res = ::SysAllocStringByteLen(NULL, byteLen);
+    memcpy(res, m_str, byteLen);
+    return res;
+  }
+  /*
+  void Attach(BSTR src) { m_str = src; }
+  BSTR Detach()
+  {
+    BSTR s = m_str;
+    m_str = NULL;
+    return s;
+  }
+  */
+  void Empty()
+  {
+    ::SysFreeString(m_str);
+    m_str = NULL;
+  }
+  bool operator!() const {  return (m_str == NULL); }
+};
+
+//////////////////////////////////////////////////////////
+
+class CMyUnknownImp
+{
+public:
+  ULONG __m_RefCount;
+  CMyUnknownImp(): __m_RefCount(0) {}
+};
+
+#define MY_QUERYINTERFACE_BEGIN STDMETHOD(QueryInterface) \
+    (REFGUID iid, void **outObject) {
+
+#define MY_QUERYINTERFACE_ENTRY(i) if (iid == IID_ ## i) \
+    { *outObject = (void *)(i *)this; AddRef(); return S_OK; }
+
+#define MY_QUERYINTERFACE_ENTRY_UNKNOWN(i) if (iid == IID_IUnknown) \
+    { *outObject = (void *)(IUnknown *)(i *)this; AddRef(); return S_OK; }
+
+#define MY_QUERYINTERFACE_BEGIN2(i) MY_QUERYINTERFACE_BEGIN \
+    MY_QUERYINTERFACE_ENTRY_UNKNOWN(i) \
+    MY_QUERYINTERFACE_ENTRY(i)
+
+#define MY_QUERYINTERFACE_END return E_NOINTERFACE; }
+
+#define MY_ADDREF_RELEASE \
+STDMETHOD_(ULONG, AddRef)() { return ++__m_RefCount; } \
+STDMETHOD_(ULONG, Release)() { if (--__m_RefCount != 0)  \
+  return __m_RefCount; delete this; return 0; }
+
+#define MY_UNKNOWN_IMP_SPEC(i) \
+  MY_QUERYINTERFACE_BEGIN \
+  i \
+  MY_QUERYINTERFACE_END \
+  MY_ADDREF_RELEASE
+
+
+#define MY_UNKNOWN_IMP MY_QUERYINTERFACE_BEGIN \
+  MY_QUERYINTERFACE_ENTRY_UNKNOWN(IUnknown) \
+  MY_QUERYINTERFACE_END \
+  MY_ADDREF_RELEASE
+
+#define MY_UNKNOWN_IMP1(i) MY_UNKNOWN_IMP_SPEC( \
+  MY_QUERYINTERFACE_ENTRY_UNKNOWN(i) \
+  MY_QUERYINTERFACE_ENTRY(i) \
+  )
+
+#define MY_UNKNOWN_IMP2(i1, i2) MY_UNKNOWN_IMP_SPEC( \
+  MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \
+  MY_QUERYINTERFACE_ENTRY(i1) \
+  MY_QUERYINTERFACE_ENTRY(i2) \
+  )
+
+#define MY_UNKNOWN_IMP3(i1, i2, i3) MY_UNKNOWN_IMP_SPEC( \
+  MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \
+  MY_QUERYINTERFACE_ENTRY(i1) \
+  MY_QUERYINTERFACE_ENTRY(i2) \
+  MY_QUERYINTERFACE_ENTRY(i3) \
+  )
+
+#define MY_UNKNOWN_IMP4(i1, i2, i3, i4) MY_UNKNOWN_IMP_SPEC( \
+  MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \
+  MY_QUERYINTERFACE_ENTRY(i1) \
+  MY_QUERYINTERFACE_ENTRY(i2) \
+  MY_QUERYINTERFACE_ENTRY(i3) \
+  MY_QUERYINTERFACE_ENTRY(i4) \
+  )
+
+#define MY_UNKNOWN_IMP5(i1, i2, i3, i4, i5) MY_UNKNOWN_IMP_SPEC( \
+  MY_QUERYINTERFACE_ENTRY_UNKNOWN(i1) \
+  MY_QUERYINTERFACE_ENTRY(i1) \
+  MY_QUERYINTERFACE_ENTRY(i2) \
+  MY_QUERYINTERFACE_ENTRY(i3) \
+  MY_QUERYINTERFACE_ENTRY(i4) \
+  MY_QUERYINTERFACE_ENTRY(i5) \
+  )
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/MyException.h b/third_party/lzma/v4_65/files/CPP/Common/MyException.h
new file mode 100644
index 0000000..f0ad111
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/MyException.h
@@ -0,0 +1,14 @@
+// Common/Exception.h
+
+#ifndef __COMMON_EXCEPTION_H
+#define __COMMON_EXCEPTION_H
+
+#include "MyWindows.h"
+
+struct CSystemException
+{
+  HRESULT ErrorCode;
+  CSystemException(HRESULT errorCode): ErrorCode(errorCode) {}
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/MyGuidDef.h b/third_party/lzma/v4_65/files/CPP/Common/MyGuidDef.h
new file mode 100644
index 0000000..7cfaba0
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/MyGuidDef.h
@@ -0,0 +1,54 @@
+// Common/MyGuidDef.h
+
+#ifndef GUID_DEFINED
+#define GUID_DEFINED
+
+#include "Types.h"
+
+typedef struct {
+  UInt32 Data1;
+  UInt16 Data2;
+  UInt16 Data3;
+  unsigned char Data4[8];
+} GUID;
+
+#ifdef __cplusplus
+#define REFGUID const GUID &
+#else
+#define REFGUID const GUID *
+#endif
+
+#define REFCLSID REFGUID
+#define REFIID REFGUID
+
+#ifdef __cplusplus
+inline int operator==(REFGUID g1, REFGUID g2)
+{
+  for (int i = 0; i < (int)sizeof(g1); i++)
+    if (((unsigned char *)&g1)[i] != ((unsigned char *)&g2)[i])
+      return 0;
+  return 1;
+}
+inline int operator!=(REFGUID g1, REFGUID g2) { return !(g1 == g2); }
+#endif
+
+#ifdef __cplusplus
+  #define MY_EXTERN_C extern "C"
+#else
+  #define MY_EXTERN_C extern
+#endif
+
+#endif // GUID_DEFINED
+
+
+#ifdef DEFINE_GUID
+#undef DEFINE_GUID
+#endif
+
+#ifdef INITGUID
+  #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
+    MY_EXTERN_C const GUID name = { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }
+#else
+  #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
+    MY_EXTERN_C const GUID name
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/MyInitGuid.h b/third_party/lzma/v4_65/files/CPP/Common/MyInitGuid.h
new file mode 100644
index 0000000..4fc1556
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/MyInitGuid.h
@@ -0,0 +1,15 @@
+// Common/MyInitGuid.h
+
+#ifndef __COMMON_MYINITGUID_H
+#define __COMMON_MYINITGUID_H
+
+#ifdef _WIN32
+#include <initguid.h>
+#else
+#define INITGUID
+#include "MyGuidDef.h"
+DEFINE_GUID(IID_IUnknown,
+0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
+#endif
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/MyString.cpp b/third_party/lzma/v4_65/files/CPP/Common/MyString.cpp
new file mode 100644
index 0000000..2c02e82
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/MyString.cpp
@@ -0,0 +1,200 @@
+// Common/MyString.cpp
+
+#include "StdAfx.h"
+
+#ifndef _WIN32
+#include <ctype.h>
+#endif
+
+#ifndef _UNICODE
+#include "StringConvert.h"
+#endif
+
+#include "MyString.h"
+
+
+#ifdef _WIN32
+
+#ifndef _UNICODE
+
+wchar_t MyCharUpper(wchar_t c)
+{
+  if (c == 0)
+    return 0;
+  wchar_t *res = CharUpperW((LPWSTR)(UINT_PTR)(unsigned int)c);
+  if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
+    return (wchar_t)(unsigned int)(UINT_PTR)res;
+  const int kBufferSize = 4;
+  char s[kBufferSize + 1];
+  int numChars = ::WideCharToMultiByte(CP_ACP, 0, &c, 1, s, kBufferSize, 0, 0);
+  if (numChars == 0 || numChars > kBufferSize)
+    return c;
+  s[numChars] = 0;
+  ::CharUpperA(s);
+  ::MultiByteToWideChar(CP_ACP, 0, s, numChars, &c, 1);
+  return c;
+}
+
+wchar_t MyCharLower(wchar_t c)
+{
+  if (c == 0)
+    return 0;
+  wchar_t *res = CharLowerW((LPWSTR)(UINT_PTR)(unsigned int)c);
+  if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
+    return (wchar_t)(unsigned int)(UINT_PTR)res;
+  const int kBufferSize = 4;
+  char s[kBufferSize + 1];
+  int numChars = ::WideCharToMultiByte(CP_ACP, 0, &c, 1, s, kBufferSize, 0, 0);
+  if (numChars == 0 || numChars > kBufferSize)
+    return c;
+  s[numChars] = 0;
+  ::CharLowerA(s);
+  ::MultiByteToWideChar(CP_ACP, 0, s, numChars, &c, 1);
+  return c;
+}
+
+wchar_t * MyStringUpper(wchar_t *s)
+{
+  if (s == 0)
+    return 0;
+  wchar_t *res = CharUpperW(s);
+  if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
+    return res;
+  AString a = UnicodeStringToMultiByte(s);
+  a.MakeUpper();
+  return MyStringCopy(s, (const wchar_t *)MultiByteToUnicodeString(a));
+}
+
+wchar_t * MyStringLower(wchar_t *s)
+{
+  if (s == 0)
+    return 0;
+  wchar_t *res = CharLowerW(s);
+  if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
+    return res;
+  AString a = UnicodeStringToMultiByte(s);
+  a.MakeLower();
+  return MyStringCopy(s, (const wchar_t *)MultiByteToUnicodeString(a));
+}
+
+#endif
+
+/*
+inline int ConvertCompareResult(int r) { return r - 2; }
+
+int MyStringCollate(const wchar_t *s1, const wchar_t *s2)
+{
+  int res = CompareStringW(
+        LOCALE_USER_DEFAULT, SORT_STRINGSORT, s1, -1, s2, -1);
+  #ifdef _UNICODE
+  return ConvertCompareResult(res);
+  #else
+  if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
+    return ConvertCompareResult(res);
+  return MyStringCollate(UnicodeStringToMultiByte(s1),
+        UnicodeStringToMultiByte(s2));
+  #endif
+}
+
+#ifndef _WIN32_WCE
+int MyStringCollate(const char *s1, const char *s2)
+{
+  return ConvertCompareResult(CompareStringA(
+    LOCALE_USER_DEFAULT, SORT_STRINGSORT, s1, -1, s2, -1));
+}
+
+int MyStringCollateNoCase(const char *s1, const char *s2)
+{
+  return ConvertCompareResult(CompareStringA(
+    LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, s1, -1, s2, -1));
+}
+#endif
+
+int MyStringCollateNoCase(const wchar_t *s1, const wchar_t *s2)
+{
+  int res = CompareStringW(
+        LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT, s1, -1, s2, -1);
+  #ifdef _UNICODE
+  return ConvertCompareResult(res);
+  #else
+  if (res != 0 || ::GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
+    return ConvertCompareResult(res);
+  return MyStringCollateNoCase(UnicodeStringToMultiByte(s1),
+      UnicodeStringToMultiByte(s2));
+  #endif
+}
+*/
+
+#else
+
+wchar_t MyCharUpper(wchar_t c)
+{
+  return toupper(c);
+}
+
+/*
+int MyStringCollateNoCase(const wchar_t *s1, const wchar_t *s2)
+{
+  for (;;)
+  {
+    wchar_t c1 = *s1++;
+    wchar_t c2 = *s2++;
+    wchar_t u1 = MyCharUpper(c1);
+    wchar_t u2 = MyCharUpper(c2);
+
+    if (u1 < u2) return -1;
+    if (u1 > u2) return 1;
+    if (u1 == 0) return 0;
+  }
+}
+*/
+
+#endif
+
+int MyStringCompare(const char *s1, const char *s2)
+{
+  for (;;)
+  {
+    unsigned char c1 = (unsigned char)*s1++;
+    unsigned char c2 = (unsigned char)*s2++;
+    if (c1 < c2) return -1;
+    if (c1 > c2) return 1;
+    if (c1 == 0) return 0;
+  }
+}
+
+int MyStringCompare(const wchar_t *s1, const wchar_t *s2)
+{
+  for (;;)
+  {
+    wchar_t c1 = *s1++;
+    wchar_t c2 = *s2++;
+    if (c1 < c2) return -1;
+    if (c1 > c2) return 1;
+    if (c1 == 0) return 0;
+  }
+}
+
+int MyStringCompareNoCase(const wchar_t *s1, const wchar_t *s2)
+{
+  for (;;)
+  {
+    wchar_t c1 = *s1++;
+    wchar_t c2 = *s2++;
+    if (c1 != c2)
+    {
+      wchar_t u1 = MyCharUpper(c1);
+      wchar_t u2 = MyCharUpper(c2);
+      if (u1 < u2) return -1;
+      if (u1 > u2) return 1;
+    }
+    if (c1 == 0) return 0;
+  }
+}
+
+/*
+int MyStringCompareNoCase(const char *s1, const char *s2)
+{
+  return MyStringCompareNoCase(MultiByteToUnicodeString(s1), MultiByteToUnicodeString(s2));
+}
+*/
diff --git a/third_party/lzma/v4_65/files/CPP/Common/MyString.h b/third_party/lzma/v4_65/files/CPP/Common/MyString.h
new file mode 100644
index 0000000..bae239d
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/MyString.h
@@ -0,0 +1,628 @@
+// Common/String.h
+
+#ifndef __COMMON_STRING_H
+#define __COMMON_STRING_H
+
+#include <string.h>
+// #include <wchar.h>
+
+#include "MyVector.h"
+
+#ifdef _WIN32
+#include "MyWindows.h"
+#endif
+
+template <class T>
+inline int MyStringLen(const T *s)
+{
+  int i;
+  for (i = 0; s[i] != '\0'; i++);
+  return i;
+}
+
+template <class T>
+inline T * MyStringCopy(T *dest, const T *src)
+{
+  T *destStart = dest;
+  while ((*dest++ = *src++) != 0);
+  return destStart;
+}
+
+inline wchar_t* MyStringGetNextCharPointer(wchar_t *p)
+  { return (p + 1); }
+inline const wchar_t* MyStringGetNextCharPointer(const wchar_t *p)
+  { return (p + 1); }
+inline wchar_t* MyStringGetPrevCharPointer(const wchar_t *, wchar_t *p)
+  { return (p - 1); }
+inline const wchar_t* MyStringGetPrevCharPointer(const wchar_t *, const wchar_t *p)
+  { return (p - 1); }
+
+#ifdef _WIN32
+
+inline char* MyStringGetNextCharPointer(char *p)
+  { return CharNextA(p); }
+inline const char* MyStringGetNextCharPointer(const char *p)
+  { return CharNextA(p); }
+
+inline char* MyStringGetPrevCharPointer(char *base, char *p)
+  { return CharPrevA(base, p); }
+inline const char* MyStringGetPrevCharPointer(const char *base, const char *p)
+  { return CharPrevA(base, p); }
+
+inline char MyCharUpper(char c)
+  { return (char)(unsigned int)(UINT_PTR)CharUpperA((LPSTR)(UINT_PTR)(unsigned int)(unsigned char)c); }
+#ifdef _UNICODE
+inline wchar_t MyCharUpper(wchar_t c)
+  { return (wchar_t)(unsigned int)(UINT_PTR)CharUpperW((LPWSTR)(UINT_PTR)(unsigned int)c); }
+#else
+wchar_t MyCharUpper(wchar_t c);
+#endif
+
+inline char MyCharLower(char c)
+  { return (char)(unsigned int)(UINT_PTR)CharLowerA((LPSTR)(UINT_PTR)(unsigned int)(unsigned char)c); }
+#ifdef _UNICODE
+inline wchar_t MyCharLower(wchar_t c)
+  { return (wchar_t)(unsigned int)(UINT_PTR)CharLowerW((LPWSTR)(UINT_PTR)(unsigned int)c); }
+#else
+wchar_t MyCharLower(wchar_t c);
+#endif
+
+inline char * MyStringUpper(char *s) { return CharUpperA(s); }
+#ifdef _UNICODE
+inline wchar_t * MyStringUpper(wchar_t *s) { return CharUpperW(s); }
+#else
+wchar_t * MyStringUpper(wchar_t *s);
+#endif
+
+inline char * MyStringLower(char *s) { return CharLowerA(s); }
+#ifdef _UNICODE
+inline wchar_t * MyStringLower(wchar_t *s) { return CharLowerW(s); }
+#else
+wchar_t * MyStringLower(wchar_t *s);
+#endif
+
+#else // Standard-C
+wchar_t MyCharUpper(wchar_t c);
+#endif
+
+//////////////////////////////////////
+// Compare
+
+/*
+#ifndef _WIN32_WCE
+int MyStringCollate(const char *s1, const char *s2);
+int MyStringCollateNoCase(const char *s1, const char *s2);
+#endif
+int MyStringCollate(const wchar_t *s1, const wchar_t *s2);
+int MyStringCollateNoCase(const wchar_t *s1, const wchar_t *s2);
+*/
+
+int MyStringCompare(const char *s1, const char  *s2);
+int MyStringCompare(const wchar_t *s1, const wchar_t *s2);
+
+// int MyStringCompareNoCase(const char *s1, const char  *s2);
+int MyStringCompareNoCase(const wchar_t *s1, const wchar_t *s2);
+
+template <class T>
+class CStringBase
+{
+  void TrimLeftWithCharSet(const CStringBase &charSet)
+  {
+    const T *p = _chars;
+    while (charSet.Find(*p) >= 0 && (*p != 0))
+      p = GetNextCharPointer(p);
+    Delete(0, (int)(p - _chars));
+  }
+  void TrimRightWithCharSet(const CStringBase &charSet)
+  {
+    const T *p = _chars;
+    const T *pLast = NULL;
+    while (*p != 0)
+    {
+      if (charSet.Find(*p) >= 0)
+      {
+        if (pLast == NULL)
+          pLast = p;
+      }
+      else
+        pLast = NULL;
+      p = GetNextCharPointer(p);
+    }
+    if (pLast != NULL)
+    {
+      int i = (int)(pLast - _chars);
+      Delete(i, _length - i);
+    }
+
+  }
+  void MoveItems(int destIndex, int srcIndex)
+  {
+    memmove(_chars + destIndex, _chars + srcIndex,
+        sizeof(T) * (_length - srcIndex + 1));
+  }
+  
+  void InsertSpace(int &index, int size)
+  {
+    CorrectIndex(index);
+    GrowLength(size);
+    MoveItems(index + size, index);
+  }
+
+  static T *GetNextCharPointer(T *p)
+    { return MyStringGetNextCharPointer(p); }
+  static const T *GetNextCharPointer(const T *p)
+    { return MyStringGetNextCharPointer(p); }
+  static T *GetPrevCharPointer(T *base, T *p)
+    { return MyStringGetPrevCharPointer(base, p); }
+  static const T *GetPrevCharPointer(const T *base, const T *p)
+    { return MyStringGetPrevCharPointer(base, p); }
+protected:
+  T *_chars;
+  int _length;
+  int _capacity;
+  
+  void SetCapacity(int newCapacity)
+  {
+    int realCapacity = newCapacity + 1;
+    if (realCapacity == _capacity)
+      return;
+    /*
+    const int kMaxStringSize = 0x20000000;
+    #ifndef _WIN32_WCE
+    if (newCapacity > kMaxStringSize || newCapacity < _length)
+      throw 1052337;
+    #endif
+    */
+    T *newBuffer = new T[realCapacity];
+    if (_capacity > 0)
+    {
+      for (int i = 0; i < _length; i++)
+        newBuffer[i] = _chars[i];
+      delete []_chars;
+    }
+    _chars = newBuffer;
+    _chars[_length] = 0;
+    _capacity = realCapacity;
+  }
+
+  void GrowLength(int n)
+  {
+    int freeSize = _capacity - _length - 1;
+    if (n <= freeSize)
+      return;
+    int delta;
+    if (_capacity > 64)
+      delta = _capacity / 2;
+    else if (_capacity > 8)
+      delta = 16;
+    else
+      delta = 4;
+    if (freeSize + delta < n)
+      delta = n - freeSize;
+    SetCapacity(_capacity + delta);
+  }
+
+  void CorrectIndex(int &index) const
+  {
+    if (index > _length)
+      index = _length;
+  }
+
+public:
+  CStringBase(): _chars(0), _length(0), _capacity(0) { SetCapacity(3); }
+  CStringBase(T c):  _chars(0), _length(0), _capacity(0)
+  {
+    SetCapacity(1);
+    _chars[0] = c;
+    _chars[1] = 0;
+    _length = 1;
+  }
+  CStringBase(const T *chars): _chars(0), _length(0), _capacity(0)
+  {
+    int length = MyStringLen(chars);
+    SetCapacity(length);
+    MyStringCopy(_chars, chars); // can be optimized by memove()
+    _length = length;
+  }
+  CStringBase(const CStringBase &s):  _chars(0), _length(0), _capacity(0)
+  {
+    SetCapacity(s._length);
+    MyStringCopy(_chars, s._chars);
+    _length = s._length;
+  }
+  ~CStringBase() {  delete []_chars; }
+
+  operator const T*() const { return _chars;}
+
+  // The minimum size of the character buffer in characters.
+  // This value does not include space for a null terminator.
+  T* GetBuffer(int minBufLength)
+  {
+    if (minBufLength >= _capacity)
+      SetCapacity(minBufLength);
+    return _chars;
+  }
+  void ReleaseBuffer() { ReleaseBuffer(MyStringLen(_chars)); }
+  void ReleaseBuffer(int newLength)
+  {
+    /*
+    #ifndef _WIN32_WCE
+    if (newLength >= _capacity)
+      throw 282217;
+    #endif
+    */
+    _chars[newLength] = 0;
+    _length = newLength;
+  }
+
+  CStringBase& operator=(T c)
+  {
+    Empty();
+    SetCapacity(1);
+    _chars[0] = c;
+    _chars[1] = 0;
+    _length = 1;
+    return *this;
+  }
+  CStringBase& operator=(const T *chars)
+  {
+    Empty();
+    int length = MyStringLen(chars);
+    SetCapacity(length);
+    MyStringCopy(_chars, chars);
+    _length = length;
+    return *this;
+  }
+  CStringBase& operator=(const CStringBase& s)
+  {
+    if (&s == this)
+      return *this;
+    Empty();
+    SetCapacity(s._length);
+    MyStringCopy(_chars, s._chars);
+    _length = s._length;
+    return *this;
+  }
+  
+  CStringBase& operator+=(T c)
+  {
+    GrowLength(1);
+    _chars[_length] = c;
+    _chars[++_length] = 0;
+    return *this;
+  }
+  CStringBase& operator+=(const T *s)
+  {
+    int len = MyStringLen(s);
+    GrowLength(len);
+    MyStringCopy(_chars + _length, s);
+    _length += len;
+    return *this;
+  }
+  CStringBase& operator+=(const CStringBase &s)
+  {
+    GrowLength(s._length);
+    MyStringCopy(_chars + _length, s._chars);
+    _length += s._length;
+    return *this;
+  }
+  void Empty()
+  {
+    _length = 0;
+    _chars[0] = 0;
+  }
+  int Length() const { return _length; }
+  bool IsEmpty() const { return (_length == 0); }
+
+  CStringBase Mid(int startIndex) const
+    { return Mid(startIndex, _length - startIndex); }
+  CStringBase Mid(int startIndex, int count ) const
+  {
+    if (startIndex + count > _length)
+      count = _length - startIndex;
+    
+    if (startIndex == 0 && startIndex + count == _length)
+      return *this;
+    
+    CStringBase<T> result;
+    result.SetCapacity(count);
+    // MyStringNCopy(result._chars, _chars + startIndex, count);
+    for (int i = 0; i < count; i++)
+      result._chars[i] = _chars[startIndex + i];
+    result._chars[count] = 0;
+    result._length = count;
+    return result;
+  }
+  CStringBase Left(int count) const
+    { return Mid(0, count); }
+  CStringBase Right(int count) const
+  {
+    if (count > _length)
+      count = _length;
+    return Mid(_length - count, count);
+  }
+
+  void MakeUpper()
+    { MyStringUpper(_chars); }
+  void MakeLower()
+    { MyStringLower(_chars); }
+
+  int Compare(const CStringBase& s) const
+    { return MyStringCompare(_chars, s._chars); }
+
+  int Compare(const T *s) const
+    { return MyStringCompare(_chars, s); }
+
+  int CompareNoCase(const CStringBase& s) const
+    { return MyStringCompareNoCase(_chars, s._chars); }
+
+  int CompareNoCase(const T *s) const
+    { return MyStringCompareNoCase(_chars, s); }
+
+  /*
+  int Collate(const CStringBase& s) const
+    { return MyStringCollate(_chars, s._chars); }
+  int CollateNoCase(const CStringBase& s) const
+    { return MyStringCollateNoCase(_chars, s._chars); }
+  */
+
+  int Find(T c) const { return Find(c, 0); }
+  int Find(T c, int startIndex) const
+  {
+    T *p = _chars + startIndex;
+    for (;;)
+    {
+      if (*p == c)
+        return (int)(p - _chars);
+      if (*p == 0)
+        return -1;
+      p = GetNextCharPointer(p);
+    }
+  }
+  int Find(const CStringBase &s) const { return Find(s, 0); }
+  int Find(const CStringBase &s, int startIndex) const
+  {
+    if (s.IsEmpty())
+      return startIndex;
+    for (; startIndex < _length; startIndex++)
+    {
+      int j;
+      for (j = 0; j < s._length && startIndex + j < _length; j++)
+        if (_chars[startIndex+j] != s._chars[j])
+          break;
+      if (j == s._length)
+        return startIndex;
+    }
+    return -1;
+  }
+  int ReverseFind(T c) const
+  {
+    if (_length == 0)
+      return -1;
+    T *p = _chars + _length - 1;
+    for (;;)
+    {
+      if (*p == c)
+        return (int)(p - _chars);
+      if (p == _chars)
+        return -1;
+      p = GetPrevCharPointer(_chars, p);
+    }
+  }
+  int FindOneOf(const CStringBase &s) const
+  {
+    for (int i = 0; i < _length; i++)
+      if (s.Find(_chars[i]) >= 0)
+        return i;
+      return -1;
+  }
+
+  void TrimLeft(T c)
+  {
+    const T *p = _chars;
+    while (c == *p)
+      p = GetNextCharPointer(p);
+    Delete(0, p - _chars);
+  }
+  private:
+  CStringBase GetTrimDefaultCharSet()
+  {
+    CStringBase<T> charSet;
+    charSet += (T)' ';
+    charSet += (T)'\n';
+    charSet += (T)'\t';
+    return charSet;
+  }
+  public:
+
+  void TrimLeft()
+  {
+    TrimLeftWithCharSet(GetTrimDefaultCharSet());
+  }
+  void TrimRight()
+  {
+    TrimRightWithCharSet(GetTrimDefaultCharSet());
+  }
+  void TrimRight(T c)
+  {
+    const T *p = _chars;
+    const T *pLast = NULL;
+    while (*p != 0)
+    {
+      if (*p == c)
+      {
+        if (pLast == NULL)
+          pLast = p;
+      }
+      else
+        pLast = NULL;
+      p = GetNextCharPointer(p);
+    }
+    if (pLast != NULL)
+    {
+      int i = pLast - _chars;
+      Delete(i, _length - i);
+    }
+  }
+  void Trim()
+  {
+    TrimRight();
+    TrimLeft();
+  }
+
+  int Insert(int index, T c)
+  {
+    InsertSpace(index, 1);
+    _chars[index] = c;
+    _length++;
+    return _length;
+  }
+  int Insert(int index, const CStringBase &s)
+  {
+    CorrectIndex(index);
+    if (s.IsEmpty())
+      return _length;
+    int numInsertChars = s.Length();
+    InsertSpace(index, numInsertChars);
+    for (int i = 0; i < numInsertChars; i++)
+      _chars[index + i] = s[i];
+    _length += numInsertChars;
+    return _length;
+  }
+
+  // !!!!!!!!!!!!!!! test it if newChar = '\0'
+  int Replace(T oldChar, T newChar)
+  {
+    if (oldChar == newChar)
+      return 0;
+    int number  = 0;
+    int pos  = 0;
+    while (pos < Length())
+    {
+      pos = Find(oldChar, pos);
+      if (pos < 0)
+        break;
+      _chars[pos] = newChar;
+      pos++;
+      number++;
+    }
+    return number;
+  }
+  int Replace(const CStringBase &oldString, const CStringBase &newString)
+  {
+    if (oldString.IsEmpty())
+      return 0;
+    if (oldString == newString)
+      return 0;
+    int oldStringLength = oldString.Length();
+    int newStringLength = newString.Length();
+    int number  = 0;
+    int pos  = 0;
+    while (pos < _length)
+    {
+      pos = Find(oldString, pos);
+      if (pos < 0)
+        break;
+      Delete(pos, oldStringLength);
+      Insert(pos, newString);
+      pos += newStringLength;
+      number++;
+    }
+    return number;
+  }
+  int Delete(int index, int count = 1 )
+  {
+    if (index + count > _length)
+      count = _length - index;
+    if (count > 0)
+    {
+      MoveItems(index, index + count);
+      _length -= count;
+    }
+    return _length;
+  }
+};
+
+template <class T>
+CStringBase<T> operator+(const CStringBase<T>& s1, const CStringBase<T>& s2)
+{
+  CStringBase<T> result(s1);
+  result += s2;
+  return result;
+}
+
+template <class T>
+CStringBase<T> operator+(const CStringBase<T>& s, T c)
+{
+  CStringBase<T> result(s);
+  result += c;
+  return result;
+}
+
+template <class T>
+CStringBase<T> operator+(T c, const CStringBase<T>& s)
+{
+  CStringBase<T> result(c);
+  result += s;
+  return result;
+}
+
+template <class T>
+CStringBase<T> operator+(const CStringBase<T>& s, const T * chars)
+{
+  CStringBase<T> result(s);
+  result += chars;
+  return result;
+}
+
+template <class T>
+CStringBase<T> operator+(const T * chars, const CStringBase<T>& s)
+{
+  CStringBase<T> result(chars);
+  result += s;
+  return result;
+}
+
+template <class T>
+bool operator==(const CStringBase<T>& s1, const CStringBase<T>& s2)
+  { return (s1.Compare(s2) == 0); }
+
+template <class T>
+bool operator<(const CStringBase<T>& s1, const CStringBase<T>& s2)
+  { return (s1.Compare(s2) < 0); }
+
+template <class T>
+bool operator==(const T *s1, const CStringBase<T>& s2)
+  { return (s2.Compare(s1) == 0); }
+
+template <class T>
+bool operator==(const CStringBase<T>& s1, const T *s2)
+  { return (s1.Compare(s2) == 0); }
+
+template <class T>
+bool operator!=(const CStringBase<T>& s1, const CStringBase<T>& s2)
+  { return (s1.Compare(s2) != 0); }
+
+template <class T>
+bool operator!=(const T *s1, const CStringBase<T>& s2)
+  { return (s2.Compare(s1) != 0); }
+
+template <class T>
+bool operator!=(const CStringBase<T>& s1, const T *s2)
+  { return (s1.Compare(s2) != 0); }
+
+typedef CStringBase<char> AString;
+typedef CStringBase<wchar_t> UString;
+
+typedef CObjectVector<AString> AStringVector;
+typedef CObjectVector<UString> UStringVector;
+
+#ifdef _UNICODE
+  typedef UString CSysString;
+#else
+  typedef AString CSysString;
+#endif
+
+typedef CObjectVector<CSysString> CSysStringVector;
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/MyUnknown.h b/third_party/lzma/v4_65/files/CPP/Common/MyUnknown.h
new file mode 100644
index 0000000..136145a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/MyUnknown.h
@@ -0,0 +1,24 @@
+// MyUnknown.h
+
+#ifndef __MYUNKNOWN_H
+#define __MYUNKNOWN_H
+
+#ifdef _WIN32
+
+#ifdef _WIN32_WCE
+#if (_WIN32_WCE > 300)
+#include <basetyps.h>
+#else
+#define MIDL_INTERFACE(x) struct
+#endif
+#else
+#include <basetyps.h>
+#endif
+
+#include <unknwn.h>
+
+#else
+#include "MyWindows.h"
+#endif
+  
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/MyVector.cpp b/third_party/lzma/v4_65/files/CPP/Common/MyVector.cpp
new file mode 100644
index 0000000..88d9993
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/MyVector.cpp
@@ -0,0 +1,87 @@
+// Common/MyVector.cpp
+
+#include "StdAfx.h"
+
+#include <string.h>
+
+#include "MyVector.h"
+
+CBaseRecordVector::~CBaseRecordVector() { ClearAndFree(); }
+
+void CBaseRecordVector::ClearAndFree()
+{
+  Clear();
+  delete []((unsigned char *)_items);
+  _capacity = 0;
+  _size = 0;
+  _items = 0;
+}
+
+void CBaseRecordVector::Clear() { DeleteFrom(0); }
+void CBaseRecordVector::DeleteBack() { Delete(_size - 1); }
+void CBaseRecordVector::DeleteFrom(int index) { Delete(index, _size - index); }
+
+void CBaseRecordVector::ReserveOnePosition()
+{
+  if (_size != _capacity)
+    return;
+  int delta = 1;
+  if (_capacity >= 64)
+    delta = _capacity / 4;
+  else if (_capacity >= 8)
+    delta = 8;
+  Reserve(_capacity + delta);
+}
+
+void CBaseRecordVector::Reserve(int newCapacity)
+{
+  // if (newCapacity <= _capacity)
+  if (newCapacity == _capacity)
+    return;
+  if ((unsigned)newCapacity >= ((unsigned)1 << (sizeof(unsigned) * 8 - 1)))
+    throw 1052353;
+  size_t newSize = (size_t)(unsigned)newCapacity * _itemSize;
+  if (newSize / _itemSize != (size_t)(unsigned)newCapacity)
+    throw 1052354;
+  unsigned char *p = NULL;
+  if (newSize > 0)
+  {
+    p = new unsigned char[newSize];
+    if (p == 0)
+      throw 1052355;
+    int numRecordsToMove = (_size < newCapacity ? _size : newCapacity);
+    memcpy(p, _items, _itemSize * numRecordsToMove);
+  }
+  delete [](unsigned char *)_items;
+  _items = p;
+  _capacity = newCapacity;
+}
+
+void CBaseRecordVector::ReserveDown()
+{
+  Reserve(_size);
+}
+
+void CBaseRecordVector::MoveItems(int destIndex, int srcIndex)
+{
+  memmove(((unsigned char *)_items) + destIndex * _itemSize,
+    ((unsigned char  *)_items) + srcIndex * _itemSize,
+    _itemSize * (_size - srcIndex));
+}
+
+void CBaseRecordVector::InsertOneItem(int index)
+{
+  ReserveOnePosition();
+  MoveItems(index + 1, index);
+  _size++;
+}
+
+void CBaseRecordVector::Delete(int index, int num)
+{
+  TestIndexAndCorrectNum(index, num);
+  if (num > 0)
+  {
+    MoveItems(index, index + num);
+    _size -= num;
+  }
+}
diff --git a/third_party/lzma/v4_65/files/CPP/Common/MyVector.h b/third_party/lzma/v4_65/files/CPP/Common/MyVector.h
new file mode 100644
index 0000000..079e8ae
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/MyVector.h
@@ -0,0 +1,250 @@
+// Common/Vector.h
+
+#ifndef __COMMON_VECTOR_H
+#define __COMMON_VECTOR_H
+
+#include "Defs.h"
+
+class CBaseRecordVector
+{
+  void MoveItems(int destIndex, int srcIndex);
+protected:
+  int _capacity;
+  int _size;
+  void *_items;
+  size_t _itemSize;
+  
+  void ReserveOnePosition();
+  void InsertOneItem(int index);
+  void TestIndexAndCorrectNum(int index, int &num) const
+    { if (index + num > _size) num = _size - index; }
+public:
+  CBaseRecordVector(size_t itemSize): _capacity(0), _size(0), _items(0), _itemSize(itemSize) {}
+  virtual ~CBaseRecordVector();
+  void ClearAndFree();
+  int Size() const { return _size; }
+  bool IsEmpty() const { return (_size == 0); }
+  void Reserve(int newCapacity);
+  void ReserveDown();
+  virtual void Delete(int index, int num = 1);
+  void Clear();
+  void DeleteFrom(int index);
+  void DeleteBack();
+};
+
+template <class T>
+class CRecordVector: public CBaseRecordVector
+{
+public:
+  CRecordVector(): CBaseRecordVector(sizeof(T)){};
+  CRecordVector(const CRecordVector &v): CBaseRecordVector(sizeof(T)) { *this = v; }
+  CRecordVector& operator=(const CRecordVector &v)
+  {
+    Clear();
+    return (*this += v);
+  }
+  CRecordVector& operator+=(const CRecordVector &v)
+  {
+    int size = v.Size();
+    Reserve(Size() + size);
+    for (int i = 0; i < size; i++)
+      Add(v[i]);
+    return *this;
+  }
+  int Add(T item)
+  {
+    ReserveOnePosition();
+    ((T *)_items)[_size] = item;
+    return _size++;
+  }
+  void Insert(int index, T item)
+  {
+    InsertOneItem(index);
+    ((T *)_items)[index] = item;
+  }
+  // T* GetPointer() const { return (T*)_items; }
+  // operator const T *() const { return _items; };
+  const T& operator[](int index) const { return ((T *)_items)[index]; }
+  T& operator[](int index) { return ((T *)_items)[index]; }
+  const T& Front() const { return operator[](0); }
+  T& Front() { return operator[](0); }
+  const T& Back() const { return operator[](_size - 1); }
+  T& Back() { return operator[](_size - 1); }
+
+  void Swap(int i, int j)
+  {
+    T temp = operator[](i);
+    operator[](i) = operator[](j);
+    operator[](j) = temp;
+  }
+
+  int FindInSorted(const T& item) const
+  {
+    int left = 0, right = Size();
+    while (left != right)
+    {
+      int mid = (left + right) / 2;
+      const T& midValue = (*this)[mid];
+      if (item == midValue)
+        return mid;
+      if (item < midValue)
+        right = mid;
+      else
+        left = mid + 1;
+    }
+    return -1;
+  }
+
+  int AddToUniqueSorted(const T& item)
+  {
+    int left = 0, right = Size();
+    while (left != right)
+    {
+      int mid = (left + right) / 2;
+      const T& midValue = (*this)[mid];
+      if (item == midValue)
+        return mid;
+      if (item < midValue)
+        right = mid;
+      else
+        left = mid + 1;
+    }
+    Insert(right, item);
+    return right;
+  }
+
+  static void SortRefDown(T* p, int k, int size, int (*compare)(const T*, const T*, void *), void *param)
+  {
+    T temp = p[k];
+    for (;;)
+    {
+      int s = (k << 1);
+      if (s > size)
+        break;
+      if (s < size && compare(p + s + 1, p + s, param) > 0)
+        s++;
+      if (compare(&temp, p + s, param) >= 0)
+        break;
+      p[k] = p[s];
+      k = s;
+    }
+    p[k] = temp;
+  }
+
+  void Sort(int (*compare)(const T*, const T*, void *), void *param)
+  {
+    int size = _size;
+    if (size <= 1)
+      return;
+    T* p = (&Front()) - 1;
+    {
+      int i = size / 2;
+      do
+        SortRefDown(p, i, size, compare, param);
+      while (--i != 0);
+    }
+    do
+    {
+      T temp = p[size];
+      p[size--] = p[1];
+      p[1] = temp;
+      SortRefDown(p, 1, size, compare, param);
+    }
+    while (size > 1);
+  }
+};
+
+typedef CRecordVector<int> CIntVector;
+typedef CRecordVector<unsigned int> CUIntVector;
+typedef CRecordVector<bool> CBoolVector;
+typedef CRecordVector<unsigned char> CByteVector;
+typedef CRecordVector<void *> CPointerVector;
+
+template <class T>
+class CObjectVector: public CPointerVector
+{
+public:
+  CObjectVector() {};
+  ~CObjectVector() { Clear(); };
+  CObjectVector(const CObjectVector &v) { *this = v; }
+  CObjectVector& operator=(const CObjectVector &v)
+  {
+    Clear();
+    return (*this += v);
+  }
+  CObjectVector& operator+=(const CObjectVector &v)
+  {
+    int size = v.Size();
+    Reserve(Size() + size);
+    for (int i = 0; i < size; i++)
+      Add(v[i]);
+    return *this;
+  }
+  const T& operator[](int index) const { return *((T *)CPointerVector::operator[](index)); }
+  T& operator[](int index) { return *((T *)CPointerVector::operator[](index)); }
+  T& Front() { return operator[](0); }
+  const T& Front() const { return operator[](0); }
+  T& Back() { return operator[](_size - 1); }
+  const T& Back() const { return operator[](_size - 1); }
+  int Add(const T& item) { return CPointerVector::Add(new T(item)); }
+  void Insert(int index, const T& item) { CPointerVector::Insert(index, new T(item)); }
+  virtual void Delete(int index, int num = 1)
+  {
+    TestIndexAndCorrectNum(index, num);
+    for (int i = 0; i < num; i++)
+      delete (T *)(((void **)_items)[index + i]);
+    CPointerVector::Delete(index, num);
+  }
+  int Find(const T& item) const
+  {
+    for (int i = 0; i < Size(); i++)
+      if (item == (*this)[i])
+        return i;
+    return -1;
+  }
+  int FindInSorted(const T& item) const
+  {
+    int left = 0, right = Size();
+    while (left != right)
+    {
+      int mid = (left + right) / 2;
+      const T& midValue = (*this)[mid];
+      if (item == midValue)
+        return mid;
+      if (item < midValue)
+        right = mid;
+      else
+        left = mid + 1;
+    }
+    return -1;
+  }
+  int AddToSorted(const T& item)
+  {
+    int left = 0, right = Size();
+    while (left != right)
+    {
+      int mid = (left + right) / 2;
+      const T& midValue = (*this)[mid];
+      if (item == midValue)
+      {
+        right = mid + 1;
+        break;
+      }
+      if (item < midValue)
+        right = mid;
+      else
+        left = mid + 1;
+    }
+    Insert(right, item);
+    return right;
+  }
+
+  void Sort(int (*compare)(void *const *, void *const *, void *), void *param)
+    { CPointerVector::Sort(compare, param); }
+
+  static int CompareObjectItems(void *const *a1, void *const *a2, void * /* param */)
+    { return MyCompare(*(*((const T **)a1)), *(*((const T **)a2))); }
+  void Sort() { CPointerVector::Sort(CompareObjectItems, 0); }
+};
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/MyWindows.h b/third_party/lzma/v4_65/files/CPP/Common/MyWindows.h
new file mode 100644
index 0000000..2861ba0
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/MyWindows.h
@@ -0,0 +1,214 @@
+// MyWindows.h
+
+#ifndef __MYWINDOWS_H
+#define __MYWINDOWS_H
+
+#ifdef _WIN32
+
+#include <windows.h>
+
+#define CHAR_PATH_SEPARATOR '\\'
+#define WCHAR_PATH_SEPARATOR L'\\'
+#define STRING_PATH_SEPARATOR "\\"
+#define WSTRING_PATH_SEPARATOR L"\\"
+
+#else
+
+#define CHAR_PATH_SEPARATOR '/'
+#define WCHAR_PATH_SEPARATOR L'/'
+#define STRING_PATH_SEPARATOR "/"
+#define WSTRING_PATH_SEPARATOR L"/"
+
+#include <stddef.h> // for wchar_t
+#include <string.h>
+
+#include "MyGuidDef.h"
+
+typedef char CHAR;
+typedef unsigned char UCHAR;
+
+#undef BYTE
+typedef unsigned char BYTE;
+
+typedef short SHORT;
+typedef unsigned short USHORT;
+
+#undef WORD
+typedef unsigned short WORD;
+typedef short VARIANT_BOOL;
+
+typedef int INT;
+typedef Int32 INT32;
+typedef unsigned int UINT;
+typedef UInt32 UINT32;
+typedef INT32 LONG;   // LONG, ULONG and DWORD must be 32-bit
+typedef UINT32 ULONG;
+
+#undef DWORD
+typedef UINT32 DWORD;
+
+typedef Int64 LONGLONG;
+typedef UInt64 ULONGLONG;
+
+typedef struct LARGE_INTEGER { LONGLONG QuadPart; }LARGE_INTEGER;
+typedef struct _ULARGE_INTEGER { ULONGLONG QuadPart;} ULARGE_INTEGER;
+
+typedef const CHAR *LPCSTR;
+typedef CHAR TCHAR;
+typedef const TCHAR *LPCTSTR;
+typedef wchar_t WCHAR;
+typedef WCHAR OLECHAR;
+typedef const WCHAR *LPCWSTR;
+typedef OLECHAR *BSTR;
+typedef const OLECHAR *LPCOLESTR;
+typedef OLECHAR *LPOLESTR;
+
+typedef struct _FILETIME
+{
+  DWORD dwLowDateTime;
+  DWORD dwHighDateTime;
+}FILETIME;
+
+#define HRESULT LONG
+#define FAILED(Status) ((HRESULT)(Status)<0)
+typedef ULONG PROPID;
+typedef LONG SCODE;
+
+#define S_OK    ((HRESULT)0x00000000L)
+#define S_FALSE ((HRESULT)0x00000001L)
+#define E_NOTIMPL ((HRESULT)0x80004001L)
+#define E_NOINTERFACE ((HRESULT)0x80004002L)
+#define E_ABORT ((HRESULT)0x80004004L)
+#define E_FAIL ((HRESULT)0x80004005L)
+#define STG_E_INVALIDFUNCTION ((HRESULT)0x80030001L)
+#define E_OUTOFMEMORY ((HRESULT)0x8007000EL)
+#define E_INVALIDARG ((HRESULT)0x80070057L)
+
+#ifdef _MSC_VER
+#define STDMETHODCALLTYPE __stdcall
+#else
+#define STDMETHODCALLTYPE
+#endif
+
+#define STDMETHOD_(t, f) virtual t STDMETHODCALLTYPE f
+#define STDMETHOD(f) STDMETHOD_(HRESULT, f)
+#define STDMETHODIMP_(type) type STDMETHODCALLTYPE
+#define STDMETHODIMP STDMETHODIMP_(HRESULT)
+
+#define PURE = 0
+
+#define MIDL_INTERFACE(x) struct
+
+#ifdef __cplusplus
+
+DEFINE_GUID(IID_IUnknown,
+0x00000000, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
+struct IUnknown
+{
+  STDMETHOD(QueryInterface) (REFIID iid, void **outObject) PURE;
+  STDMETHOD_(ULONG, AddRef)() PURE;
+  STDMETHOD_(ULONG, Release)() PURE;
+  #ifndef _WIN32
+  virtual ~IUnknown() {}
+  #endif
+};
+
+typedef IUnknown *LPUNKNOWN;
+
+#endif
+
+#define VARIANT_TRUE ((VARIANT_BOOL)-1)
+#define VARIANT_FALSE ((VARIANT_BOOL)0)
+
+enum VARENUM
+{
+  VT_EMPTY = 0,
+  VT_NULL = 1,
+  VT_I2 = 2,
+  VT_I4 = 3,
+  VT_R4 = 4,
+  VT_R8 = 5,
+  VT_CY = 6,
+  VT_DATE = 7,
+  VT_BSTR = 8,
+  VT_DISPATCH = 9,
+  VT_ERROR = 10,
+  VT_BOOL = 11,
+  VT_VARIANT = 12,
+  VT_UNKNOWN = 13,
+  VT_DECIMAL = 14,
+  VT_I1 = 16,
+  VT_UI1 = 17,
+  VT_UI2 = 18,
+  VT_UI4 = 19,
+  VT_I8 = 20,
+  VT_UI8 = 21,
+  VT_INT = 22,
+  VT_UINT = 23,
+  VT_VOID = 24,
+  VT_HRESULT = 25,
+  VT_FILETIME = 64
+};
+
+typedef unsigned short VARTYPE;
+typedef WORD PROPVAR_PAD1;
+typedef WORD PROPVAR_PAD2;
+typedef WORD PROPVAR_PAD3;
+
+#ifdef __cplusplus
+
+typedef struct tagPROPVARIANT
+{
+  VARTYPE vt;
+  PROPVAR_PAD1 wReserved1;
+  PROPVAR_PAD2 wReserved2;
+  PROPVAR_PAD3 wReserved3;
+  union
+  {
+    CHAR cVal;
+    UCHAR bVal;
+    SHORT iVal;
+    USHORT uiVal;
+    LONG lVal;
+    ULONG ulVal;
+    INT intVal;
+    UINT uintVal;
+    LARGE_INTEGER hVal;
+    ULARGE_INTEGER uhVal;
+    VARIANT_BOOL boolVal;
+    SCODE scode;
+    FILETIME filetime;
+    BSTR bstrVal;
+  };
+} PROPVARIANT;
+
+typedef PROPVARIANT tagVARIANT;
+typedef tagVARIANT VARIANT;
+typedef VARIANT VARIANTARG;
+
+MY_EXTERN_C HRESULT VariantClear(VARIANTARG *prop);
+MY_EXTERN_C HRESULT VariantCopy(VARIANTARG *dest, VARIANTARG *src);
+
+#endif
+
+MY_EXTERN_C BSTR SysAllocStringByteLen(LPCSTR psz, UINT len);
+MY_EXTERN_C BSTR SysAllocString(const OLECHAR *sz);
+MY_EXTERN_C void SysFreeString(BSTR bstr);
+MY_EXTERN_C UINT SysStringByteLen(BSTR bstr);
+MY_EXTERN_C UINT SysStringLen(BSTR bstr);
+
+MY_EXTERN_C DWORD GetLastError();
+MY_EXTERN_C LONG CompareFileTime(const FILETIME* ft1, const FILETIME* ft2);
+
+#define CP_ACP    0
+#define CP_OEMCP  1
+
+typedef enum tagSTREAM_SEEK
+{
+  STREAM_SEEK_SET = 0,
+  STREAM_SEEK_CUR = 1,
+  STREAM_SEEK_END = 2
+} STREAM_SEEK;
+
+#endif
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/NewHandler.cpp b/third_party/lzma/v4_65/files/CPP/Common/NewHandler.cpp
new file mode 100644
index 0000000..aad6e7d
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/NewHandler.cpp
@@ -0,0 +1,116 @@
+// NewHandler.cpp
+ 
+#include "StdAfx.h"
+
+#include <stdlib.h>
+
+#include "NewHandler.h"
+
+// #define DEBUG_MEMORY_LEAK
+
+#ifndef DEBUG_MEMORY_LEAK
+
+#ifdef _WIN32
+void *
+#ifdef _MSC_VER
+__cdecl
+#endif
+operator new(size_t size)
+{
+  // void *p = ::HeapAlloc(::GetProcessHeap(), 0, size);
+  void *p = ::malloc(size);
+  if (p == 0)
+    throw CNewException();
+  return p;
+}
+
+void
+#ifdef _MSC_VER
+__cdecl
+#endif
+operator delete(void *p) throw()
+{
+  /*
+  if (p == 0)
+    return;
+  ::HeapFree(::GetProcessHeap(), 0, p);
+  */
+  ::free(p);
+}
+#endif
+
+#else
+
+#pragma init_seg(lib)
+const int kDebugSize = 1000000;
+static void *a[kDebugSize];
+static int index = 0;
+
+static int numAllocs = 0;
+void * __cdecl operator new(size_t size)
+{
+  numAllocs++;
+  void *p = HeapAlloc(GetProcessHeap(), 0, size);
+  if (index == 40)
+  {
+    int t = 1;
+  }
+  if (index < kDebugSize)
+  {
+    a[index] = p;
+    index++;
+  }
+  if (p == 0)
+    throw CNewException();
+  printf("Alloc %6d, size = %8d\n", numAllocs, size);
+  return p;
+}
+
+class CC
+{
+public:
+  CC()
+  {
+    for (int i = 0; i < kDebugSize; i++)
+      a[i] = 0;
+  }
+  ~CC()
+  {
+    for (int i = 0; i < kDebugSize; i++)
+      if (a[i] != 0)
+        return;
+  }
+} g_CC;
+
+
+void __cdecl operator delete(void *p)
+{
+  if (p == 0)
+    return;
+  /*
+  for (int i = 0; i < index; i++)
+    if (a[i] == p)
+      a[i] = 0;
+  */
+  HeapFree(GetProcessHeap(), 0, p);
+  numAllocs--;
+  printf("Free %d\n", numAllocs);
+}
+
+#endif
+
+/*
+int MemErrorVC(size_t)
+{
+  throw CNewException();
+  // return 1;
+}
+CNewHandlerSetter::CNewHandlerSetter()
+{
+  // MemErrorOldVCFunction = _set_new_handler(MemErrorVC);
+}
+CNewHandlerSetter::~CNewHandlerSetter()
+{
+  // _set_new_handler(MemErrorOldVCFunction);
+}
+*/
diff --git a/third_party/lzma/v4_65/files/CPP/Common/NewHandler.h b/third_party/lzma/v4_65/files/CPP/Common/NewHandler.h
new file mode 100644
index 0000000..215ba05
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/NewHandler.h
@@ -0,0 +1,16 @@
+// Common/NewHandler.h
+
+#ifndef __COMMON_NEWHANDLER_H
+#define __COMMON_NEWHANDLER_H
+
+class CNewException {};
+
+#ifdef _WIN32
+void
+#ifdef _MSC_VER
+__cdecl
+#endif
+operator delete(void *p) throw();
+#endif
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/StdAfx.h b/third_party/lzma/v4_65/files/CPP/Common/StdAfx.h
new file mode 100644
index 0000000..b8ba1d5
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/StdAfx.h
@@ -0,0 +1,9 @@
+// StdAfx.h
+
+#ifndef __STDAFX_H
+#define __STDAFX_H
+
+// #include "MyWindows.h"
+#include "NewHandler.h"
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/StdInStream.cpp b/third_party/lzma/v4_65/files/CPP/Common/StdInStream.cpp
new file mode 100644
index 0000000..b3d0092
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/StdInStream.cpp
@@ -0,0 +1,84 @@
+// Common/StdInStream.cpp
+
+#include "StdAfx.h"
+
+#include <tchar.h>
+#include "StdInStream.h"
+
+#ifdef _MSC_VER
+// "was declared deprecated" disabling
+#pragma warning(disable : 4996 )
+#endif
+
+static const char kIllegalChar = '\0';
+static const char kNewLineChar = '\n';
+
+static const char *kEOFMessage = "Unexpected end of input stream";
+static const char *kReadErrorMessage  ="Error reading input stream";
+static const char *kIllegalCharMessage = "Illegal character in input stream";
+
+static LPCTSTR kFileOpenMode = TEXT("r");
+
+CStdInStream g_StdIn(stdin);
+
+bool CStdInStream::Open(LPCTSTR fileName)
+{
+  Close();
+  _stream = _tfopen(fileName, kFileOpenMode);
+  _streamIsOpen = (_stream != 0);
+  return _streamIsOpen;
+}
+
+bool CStdInStream::Close()
+{
+  if (!_streamIsOpen)
+    return true;
+  _streamIsOpen = (fclose(_stream) != 0);
+  return !_streamIsOpen;
+}
+
+CStdInStream::~CStdInStream()
+{
+  Close();
+}
+
+AString CStdInStream::ScanStringUntilNewLine()
+{
+  AString s;
+  for (;;)
+  {
+    int intChar = GetChar();
+    if (intChar == EOF)
+      throw kEOFMessage;
+    char c = char(intChar);
+    if (c == kIllegalChar)
+      throw kIllegalCharMessage;
+    if (c == kNewLineChar)
+      break;
+    s += c;
+  }
+  return s;
+}
+
+void CStdInStream::ReadToString(AString &resultString)
+{
+  resultString.Empty();
+  int c;
+  while ((c = GetChar()) != EOF)
+    resultString += char(c);
+}
+
+bool CStdInStream::Eof()
+{
+  return (feof(_stream) != 0);
+}
+
+int CStdInStream::GetChar()
+{
+  int c = fgetc(_stream); // getc() doesn't work in BeOS?
+  if (c == EOF && !Eof())
+    throw kReadErrorMessage;
+  return c;
+}
+
+
diff --git a/third_party/lzma/v4_65/files/CPP/Common/StdInStream.h b/third_party/lzma/v4_65/files/CPP/Common/StdInStream.h
new file mode 100644
index 0000000..4b08c7c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/StdInStream.h
@@ -0,0 +1,31 @@
+// Common/StdInStream.h
+
+#ifndef __COMMON_STDINSTREAM_H
+#define __COMMON_STDINSTREAM_H
+
+#include <stdio.h>
+
+#include "MyString.h"
+#include "Types.h"
+
+class CStdInStream
+{
+  bool _streamIsOpen;
+  FILE *_stream;
+public:
+  CStdInStream(): _streamIsOpen(false) {};
+  CStdInStream(FILE *stream): _streamIsOpen(false), _stream(stream) {};
+  ~CStdInStream();
+  bool Open(LPCTSTR fileName);
+  bool Close();
+
+  AString ScanStringUntilNewLine();
+  void ReadToString(AString &resultString);
+
+  bool Eof();
+  int GetChar();
+};
+
+extern CStdInStream g_StdIn;
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/StdOutStream.cpp b/third_party/lzma/v4_65/files/CPP/Common/StdOutStream.cpp
new file mode 100644
index 0000000..b93e255
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/StdOutStream.cpp
@@ -0,0 +1,93 @@
+// Common/StdOutStream.cpp
+
+#include "StdAfx.h"
+
+#include <tchar.h>
+
+#include "StdOutStream.h"
+#include "IntToString.h"
+#include "StringConvert.h"
+
+#ifdef _MSC_VER
+// "was declared deprecated" disabling
+#pragma warning(disable : 4996 )
+#endif
+
+static const char kNewLineChar =  '\n';
+
+static const char *kFileOpenMode = "wt";
+
+CStdOutStream  g_StdOut(stdout);
+CStdOutStream  g_StdErr(stderr);
+
+bool CStdOutStream::Open(const char *fileName)
+{
+  Close();
+  _stream = fopen(fileName, kFileOpenMode);
+  _streamIsOpen = (_stream != 0);
+  return _streamIsOpen;
+}
+
+bool CStdOutStream::Close()
+{
+  if (!_streamIsOpen)
+    return true;
+  if (fclose(_stream) != 0)
+    return false;
+  _stream = 0;
+  _streamIsOpen = false;
+  return true;
+}
+
+bool CStdOutStream::Flush()
+{
+  return (fflush(_stream) == 0);
+}
+
+CStdOutStream::~CStdOutStream ()
+{
+  Close();
+}
+
+CStdOutStream & CStdOutStream::operator<<(CStdOutStream & (*aFunction)(CStdOutStream  &))
+{
+  (*aFunction)(*this);
+  return *this;
+}
+
+CStdOutStream & endl(CStdOutStream & outStream)
+{
+  return outStream << kNewLineChar;
+}
+
+CStdOutStream & CStdOutStream::operator<<(const char *string)
+{
+  fputs(string, _stream);
+  return *this;
+}
+
+CStdOutStream & CStdOutStream::operator<<(const wchar_t *string)
+{
+  *this << (const char *)UnicodeStringToMultiByte(string, CP_OEMCP);
+  return *this;
+}
+
+CStdOutStream & CStdOutStream::operator<<(char c)
+{
+  fputc(c, _stream);
+  return *this;
+}
+
+CStdOutStream & CStdOutStream::operator<<(int number)
+{
+  char textString[32];
+  ConvertInt64ToString(number, textString);
+  return operator<<(textString);
+}
+
+CStdOutStream & CStdOutStream::operator<<(UInt64 number)
+{
+  char textString[32];
+  ConvertUInt64ToString(number, textString);
+  return operator<<(textString);
+}
diff --git a/third_party/lzma/v4_65/files/CPP/Common/StdOutStream.h b/third_party/lzma/v4_65/files/CPP/Common/StdOutStream.h
new file mode 100644
index 0000000..b0b2c61
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/StdOutStream.h
@@ -0,0 +1,35 @@
+// Common/StdOutStream.h
+
+#ifndef __COMMON_STDOUTSTREAM_H
+#define __COMMON_STDOUTSTREAM_H
+
+#include <stdio.h>
+
+#include "Types.h"
+
+class CStdOutStream
+{
+  bool _streamIsOpen;
+  FILE *_stream;
+public:
+  CStdOutStream (): _streamIsOpen(false), _stream(0) {};
+  CStdOutStream (FILE *stream): _streamIsOpen(false), _stream(stream) {};
+  ~CStdOutStream ();
+  operator FILE *() { return _stream; }
+  bool Open(const char *fileName);
+  bool Close();
+  bool Flush();
+  CStdOutStream & operator<<(CStdOutStream & (* aFunction)(CStdOutStream  &));
+  CStdOutStream & operator<<(const char *string);
+  CStdOutStream & operator<<(const wchar_t *string);
+  CStdOutStream & operator<<(char c);
+  CStdOutStream & operator<<(int number);
+  CStdOutStream & operator<<(UInt64 number);
+};
+
+CStdOutStream & endl(CStdOutStream & outStream);
+
+extern CStdOutStream g_StdOut;
+extern CStdOutStream g_StdErr;
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/StringConvert.cpp b/third_party/lzma/v4_65/files/CPP/Common/StringConvert.cpp
new file mode 100644
index 0000000..9bd47de
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/StringConvert.cpp
@@ -0,0 +1,102 @@
+// Common/StringConvert.cpp
+
+#include "StdAfx.h"
+
+#include "StringConvert.h"
+
+#ifndef _WIN32
+#include <stdlib.h>
+#endif
+
+#ifdef _WIN32
+UString MultiByteToUnicodeString(const AString &srcString, UINT codePage)
+{
+  UString resultString;
+  if (!srcString.IsEmpty())
+  {
+    int numChars = MultiByteToWideChar(codePage, 0, srcString,
+      srcString.Length(), resultString.GetBuffer(srcString.Length()),
+      srcString.Length() + 1);
+    #ifndef _WIN32_WCE
+    if (numChars == 0)
+      throw 282228;
+    #endif
+    resultString.ReleaseBuffer(numChars);
+  }
+  return resultString;
+}
+
+AString UnicodeStringToMultiByte(const UString &s, UINT codePage, char defaultChar, bool &defaultCharWasUsed)
+{
+  AString dest;
+  defaultCharWasUsed = false;
+  if (!s.IsEmpty())
+  {
+    int numRequiredBytes = s.Length() * 2;
+    BOOL defUsed;
+    int numChars = WideCharToMultiByte(codePage, 0, s, s.Length(),
+        dest.GetBuffer(numRequiredBytes), numRequiredBytes + 1,
+        &defaultChar, &defUsed);
+    defaultCharWasUsed = (defUsed != FALSE);
+    #ifndef _WIN32_WCE
+    if (numChars == 0)
+      throw 282229;
+    #endif
+    dest.ReleaseBuffer(numChars);
+  }
+  return dest;
+}
+
+AString UnicodeStringToMultiByte(const UString &srcString, UINT codePage)
+{
+  bool defaultCharWasUsed;
+  return UnicodeStringToMultiByte(srcString, codePage, '_', defaultCharWasUsed);
+}
+
+#ifndef _WIN32_WCE
+AString SystemStringToOemString(const CSysString &srcString)
+{
+  AString result;
+  CharToOem(srcString, result.GetBuffer(srcString.Length() * 2));
+  result.ReleaseBuffer();
+  return result;
+}
+#endif
+
+#else
+
+UString MultiByteToUnicodeString(const AString &srcString, UINT codePage)
+{
+  UString resultString;
+  for (int i = 0; i < srcString.Length(); i++)
+    resultString += wchar_t(srcString[i]);
+  /*
+  if (!srcString.IsEmpty())
+  {
+    int numChars = mbstowcs(resultString.GetBuffer(srcString.Length()), srcString, srcString.Length() + 1);
+    if (numChars < 0) throw "Your environment does not support UNICODE";
+    resultString.ReleaseBuffer(numChars);
+  }
+  */
+  return resultString;
+}
+
+AString UnicodeStringToMultiByte(const UString &srcString, UINT codePage)
+{
+  AString resultString;
+  for (int i = 0; i < srcString.Length(); i++)
+    resultString += char(srcString[i]);
+  /*
+  if (!srcString.IsEmpty())
+  {
+    int numRequiredBytes = srcString.Length() * 6 + 1;
+    int numChars = wcstombs(resultString.GetBuffer(numRequiredBytes), srcString, numRequiredBytes);
+    if (numChars < 0) throw "Your environment does not support UNICODE";
+    resultString.ReleaseBuffer(numChars);
+  }
+  */
+  return resultString;
+}
+
+#endif
+
diff --git a/third_party/lzma/v4_65/files/CPP/Common/StringConvert.h b/third_party/lzma/v4_65/files/CPP/Common/StringConvert.h
new file mode 100644
index 0000000..0c37eb0
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/StringConvert.h
@@ -0,0 +1,73 @@
+// Common/StringConvert.h
+
+#ifndef __COMMON_STRINGCONVERT_H
+#define __COMMON_STRINGCONVERT_H
+
+#include "MyWindows.h"
+#include "MyString.h"
+#include "Types.h"
+
+UString MultiByteToUnicodeString(const AString &srcString, UINT codePage = CP_ACP);
+AString UnicodeStringToMultiByte(const UString &srcString, UINT codePage, char defaultChar, bool &defaultCharWasUsed);
+AString UnicodeStringToMultiByte(const UString &srcString, UINT codePage = CP_ACP);
+
+
+inline const wchar_t* GetUnicodeString(const wchar_t* unicodeString)
+  { return unicodeString; }
+inline const UString& GetUnicodeString(const UString &unicodeString)
+  { return unicodeString; }
+inline UString GetUnicodeString(const AString &ansiString)
+  { return MultiByteToUnicodeString(ansiString); }
+inline UString GetUnicodeString(const AString &multiByteString, UINT codePage)
+  { return MultiByteToUnicodeString(multiByteString, codePage); }
+inline const wchar_t* GetUnicodeString(const wchar_t* unicodeString, UINT)
+  { return unicodeString; }
+inline const UString& GetUnicodeString(const UString &unicodeString, UINT)
+  { return unicodeString; }
+
+inline const char* GetAnsiString(const char* ansiString)
+  { return ansiString; }
+inline const AString& GetAnsiString(const AString &ansiString)
+  { return ansiString; }
+inline AString GetAnsiString(const UString &unicodeString)
+  { return UnicodeStringToMultiByte(unicodeString); }
+
+inline const char* GetOemString(const char* oemString)
+  { return oemString; }
+inline const AString& GetOemString(const AString &oemString)
+  { return oemString; }
+inline AString GetOemString(const UString &unicodeString)
+  { return UnicodeStringToMultiByte(unicodeString, CP_OEMCP); }
+
+
+#ifdef _UNICODE
+  inline const wchar_t* GetSystemString(const wchar_t* unicodeString)
+    { return unicodeString;}
+  inline const UString& GetSystemString(const UString &unicodeString)
+    { return unicodeString;}
+  inline const wchar_t* GetSystemString(const wchar_t* unicodeString, UINT /* codePage */)
+    { return unicodeString;}
+  inline const UString& GetSystemString(const UString &unicodeString, UINT /* codePage */)
+    { return unicodeString;}
+  inline UString GetSystemString(const AString &multiByteString, UINT codePage)
+    { return MultiByteToUnicodeString(multiByteString, codePage);}
+  inline UString GetSystemString(const AString &multiByteString)
+    { return MultiByteToUnicodeString(multiByteString);}
+#else
+  inline const char* GetSystemString(const char *ansiString)
+    { return ansiString; }
+  inline const AString& GetSystemString(const AString &multiByteString, UINT)
+    { return multiByteString; }
+  inline const char * GetSystemString(const char *multiByteString, UINT)
+    { return multiByteString; }
+  inline AString GetSystemString(const UString &unicodeString)
+    { return UnicodeStringToMultiByte(unicodeString); }
+  inline AString GetSystemString(const UString &unicodeString, UINT codePage)
+    { return UnicodeStringToMultiByte(unicodeString, codePage); }
+#endif
+
+#ifndef _WIN32_WCE
+AString SystemStringToOemString(const CSysString &srcString);
+#endif
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/StringToInt.cpp b/third_party/lzma/v4_65/files/CPP/Common/StringToInt.cpp
new file mode 100644
index 0000000..9473766
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/StringToInt.cpp
@@ -0,0 +1,90 @@
+// Common/StringToInt.cpp
+
+#include "StdAfx.h"
+
+#include "StringToInt.h"
+
+UInt64 ConvertStringToUInt64(const char *s, const char **end)
+{
+  UInt64 result = 0;
+  for (;;)
+  {
+    char c = *s;
+    if (c < '0' || c > '9')
+    {
+      if (end != NULL)
+        *end = s;
+      return result;
+    }
+    result *= 10;
+    result += (c - '0');
+    s++;
+  }
+}
+
+UInt64 ConvertOctStringToUInt64(const char *s, const char **end)
+{
+  UInt64 result = 0;
+  for (;;)
+  {
+    char c = *s;
+    if (c < '0' || c > '7')
+    {
+      if (end != NULL)
+        *end = s;
+      return result;
+    }
+    result <<= 3;
+    result += (c - '0');
+    s++;
+  }
+}
+
+UInt64 ConvertHexStringToUInt64(const char *s, const char **end)
+{
+  UInt64 result = 0;
+  for (;;)
+  {
+    char c = *s;
+    UInt32 v;
+    if (c >= '0' && c <= '9') v = (c - '0');
+    else if (c >= 'A' && c <= 'F') v = 10 + (c - 'A');
+    else if (c >= 'a' && c <= 'f') v = 10 + (c - 'a');
+    else
+    {
+      if (end != NULL)
+        *end = s;
+      return result;
+    }
+    result <<= 4;
+    result |= v;
+    s++;
+  }
+}
+
+
+UInt64 ConvertStringToUInt64(const wchar_t *s, const wchar_t **end)
+{
+  UInt64 result = 0;
+  for (;;)
+  {
+    wchar_t c = *s;
+    if (c < '0' || c > '9')
+    {
+      if (end != NULL)
+        *end = s;
+      return result;
+    }
+    result *= 10;
+    result += (c - '0');
+    s++;
+  }
+}
+
+
+Int64 ConvertStringToInt64(const char *s, const char **end)
+{
+  if (*s == '-')
+    return -(Int64)ConvertStringToUInt64(s + 1, end);
+  return ConvertStringToUInt64(s, end);
+}
diff --git a/third_party/lzma/v4_65/files/CPP/Common/StringToInt.h b/third_party/lzma/v4_65/files/CPP/Common/StringToInt.h
new file mode 100644
index 0000000..c0d860e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/StringToInt.h
@@ -0,0 +1,18 @@
+// Common/StringToInt.h
+
+#ifndef __COMMON_STRINGTOINT_H
+#define __COMMON_STRINGTOINT_H
+
+#include <string.h>
+#include "Types.h"
+
+UInt64 ConvertStringToUInt64(const char *s, const char **end);
+UInt64 ConvertOctStringToUInt64(const char *s, const char **end);
+UInt64 ConvertHexStringToUInt64(const char *s, const char **end);
+UInt64 ConvertStringToUInt64(const wchar_t *s, const wchar_t **end);
+
+Int64 ConvertStringToInt64(const char *s, const char **end);
+
+#endif
+
+
diff --git a/third_party/lzma/v4_65/files/CPP/Common/Types.h b/third_party/lzma/v4_65/files/CPP/Common/Types.h
new file mode 100644
index 0000000..4bb8541
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/Types.h
@@ -0,0 +1,14 @@
+// Common/Types.h
+
+#ifndef __COMMON_TYPES_H
+#define __COMMON_TYPES_H
+
+extern "C"
+{
+#include "../../C/Types.h"
+}
+
+typedef int HRes;
+
+#endif
+
diff --git a/third_party/lzma/v4_65/files/CPP/Common/UTFConvert.cpp b/third_party/lzma/v4_65/files/CPP/Common/UTFConvert.cpp
new file mode 100644
index 0000000..9d1fd00
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/UTFConvert.cpp
@@ -0,0 +1,145 @@
+// UTFConvert.cpp
+
+#include "StdAfx.h"
+
+#include "UTFConvert.h"
+#include "Types.h"
+
+static const Byte kUtf8Limits[5] = { 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+static Bool Utf8_To_Utf16(wchar_t *dest, size_t *destLen, const char *src, size_t srcLen)
+{
+  size_t destPos = 0, srcPos = 0;
+  for (;;)
+  {
+    Byte c;
+    int numAdds;
+    if (srcPos == srcLen)
+    {
+      *destLen = destPos;
+      return True;
+    }
+    c = (Byte)src[srcPos++];
+
+    if (c < 0x80)
+    {
+      if (dest)
+        dest[destPos] = (wchar_t)c;
+      destPos++;
+      continue;
+    }
+    if (c < 0xC0)
+      break;
+    for (numAdds = 1; numAdds < 5; numAdds++)
+      if (c < kUtf8Limits[numAdds])
+        break;
+    UInt32 value = (c - kUtf8Limits[numAdds - 1]);
+
+    do
+    {
+      Byte c2;
+      if (srcPos == srcLen)
+        break;
+      c2 = (Byte)src[srcPos++];
+      if (c2 < 0x80 || c2 >= 0xC0)
+        break;
+      value <<= 6;
+      value |= (c2 - 0x80);
+    }
+    while (--numAdds != 0);
+    
+    if (value < 0x10000)
+    {
+      if (dest)
+        dest[destPos] = (wchar_t)value;
+      destPos++;
+    }
+    else
+    {
+      value -= 0x10000;
+      if (value >= 0x100000)
+        break;
+      if (dest)
+      {
+        dest[destPos + 0] = (wchar_t)(0xD800 + (value >> 10));
+        dest[destPos + 1] = (wchar_t)(0xDC00 + (value & 0x3FF));
+      }
+      destPos += 2;
+    }
+  }
+  *destLen = destPos;
+  return False;
+}
+
+static Bool Utf16_To_Utf8(char *dest, size_t *destLen, const wchar_t *src, size_t srcLen)
+{
+  size_t destPos = 0, srcPos = 0;
+  for (;;)
+  {
+    unsigned numAdds;
+    UInt32 value;
+    if (srcPos == srcLen)
+    {
+      *destLen = destPos;
+      return True;
+    }
+    value = src[srcPos++];
+    if (value < 0x80)
+    {
+      if (dest)
+        dest[destPos] = (char)value;
+      destPos++;
+      continue;
+    }
+    if (value >= 0xD800 && value < 0xE000)
+    {
+      UInt32 c2;
+      if (value >= 0xDC00 || srcPos == srcLen)
+        break;
+      c2 = src[srcPos++];
+      if (c2 < 0xDC00 || c2 >= 0xE000)
+        break;
+      value = ((value - 0xD800) << 10) | (c2 - 0xDC00);
+    }
+    for (numAdds = 1; numAdds < 5; numAdds++)
+      if (value < (((UInt32)1) << (numAdds * 5 + 6)))
+        break;
+    if (dest)
+      dest[destPos] = (char)(kUtf8Limits[numAdds - 1] + (value >> (6 * numAdds)));
+    destPos++;
+    do
+    {
+      numAdds--;
+      if (dest)
+        dest[destPos] = (char)(0x80 + ((value >> (6 * numAdds)) & 0x3F));
+      destPos++;
+    }
+    while (numAdds != 0);
+  }
+  *destLen = destPos;
+  return False;
+}
+
+bool ConvertUTF8ToUnicode(const AString &src, UString &dest)
+{
+  dest.Empty();
+  size_t destLen = 0;
+  Utf8_To_Utf16(NULL, &destLen, src, src.Length());
+  wchar_t *p = dest.GetBuffer((int)destLen);
+  Bool res = Utf8_To_Utf16(p, &destLen, src, src.Length());
+  p[destLen] = 0;
+  dest.ReleaseBuffer();
+  return res ? true : false;
+}
+
+bool ConvertUnicodeToUTF8(const UString &src, AString &dest)
+{
+  dest.Empty();
+  size_t destLen = 0;
+  Utf16_To_Utf8(NULL, &destLen, src, src.Length());
+  char *p = dest.GetBuffer((int)destLen);
+  Bool res = Utf16_To_Utf8(p, &destLen, src, src.Length());
+  p[destLen] = 0;
+  dest.ReleaseBuffer();
+  return res ? true : false;
+}
diff --git a/third_party/lzma/v4_65/files/CPP/Common/UTFConvert.h b/third_party/lzma/v4_65/files/CPP/Common/UTFConvert.h
new file mode 100644
index 0000000..2a14600
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/UTFConvert.h
@@ -0,0 +1,11 @@
+// Common/UTFConvert.h
+
+#ifndef __COMMON_UTFCONVERT_H
+#define __COMMON_UTFCONVERT_H
+
+#include "MyString.h"
+
+bool ConvertUTF8ToUnicode(const AString &utfString, UString &resultString);
+bool ConvertUnicodeToUTF8(const UString &unicodeString, AString &resultString);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Common/Wildcard.cpp b/third_party/lzma/v4_65/files/CPP/Common/Wildcard.cpp
new file mode 100644
index 0000000..141c5dd
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/Wildcard.cpp
@@ -0,0 +1,458 @@
+// Common/Wildcard.cpp
+
+#include "StdAfx.h"
+
+#include "Wildcard.h"
+
+bool g_CaseSensitive =
+  #ifdef _WIN32
+    false;
+  #else
+    true;
+  #endif
+
+static const wchar_t kAnyCharsChar = L'*';
+static const wchar_t kAnyCharChar = L'?';
+
+#ifdef _WIN32
+static const wchar_t kDirDelimiter1 = L'\\';
+#endif
+static const wchar_t kDirDelimiter2 = L'/';
+
+static const UString kWildCardCharSet = L"?*";
+
+static const UString kIllegalWildCardFileNameChars=
+  L"\x1\x2\x3\x4\x5\x6\x7\x8\x9\xA\xB\xC\xD\xE\xF"
+  L"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
+  L"\"/:<>\\|";
+
+
+static inline bool IsCharDirLimiter(wchar_t c)
+{
+  return (
+    #ifdef _WIN32
+    c == kDirDelimiter1 ||
+    #endif
+    c == kDirDelimiter2);
+}
+
+int CompareFileNames(const UString &s1, const UString &s2)
+{
+  if (g_CaseSensitive)
+    return s1.Compare(s2);
+  return s1.CompareNoCase(s2);
+}
+
+// -----------------------------------------
+// this function compares name with mask
+// ? - any char
+// * - any char or empty
+
+static bool EnhancedMaskTest(const wchar_t *mask, const wchar_t *name)
+{
+  for (;;)
+  {
+    wchar_t m = *mask;
+    wchar_t c = *name;
+    if (m == 0)
+      return (c == 0);
+    if (m == kAnyCharsChar)
+    {
+      if (EnhancedMaskTest(mask + 1, name))
+        return true;
+      if (c == 0)
+        return false;
+    }
+    else
+    {
+      if (m == kAnyCharChar)
+      {
+        if (c == 0)
+          return false;
+      }
+      else if (m != c)
+        if (g_CaseSensitive || MyCharUpper(m) != MyCharUpper(c))
+          return false;
+      mask++;
+    }
+    name++;
+  }
+}
+
+// --------------------------------------------------
+// Splits path to strings
+
+void SplitPathToParts(const UString &path, UStringVector &pathParts)
+{
+  pathParts.Clear();
+  UString name;
+  int len = path.Length();
+  if (len == 0)
+    return;
+  for (int i = 0; i < len; i++)
+  {
+    wchar_t c = path[i];
+    if (IsCharDirLimiter(c))
+    {
+      pathParts.Add(name);
+      name.Empty();
+    }
+    else
+      name += c;
+  }
+  pathParts.Add(name);
+}
+
+void SplitPathToParts(const UString &path, UString &dirPrefix, UString &name)
+{
+  int i;
+  for (i = path.Length() - 1; i >= 0; i--)
+    if (IsCharDirLimiter(path[i]))
+      break;
+  dirPrefix = path.Left(i + 1);
+  name = path.Mid(i + 1);
+}
+
+UString ExtractDirPrefixFromPath(const UString &path)
+{
+  int i;
+  for (i = path.Length() - 1; i >= 0; i--)
+    if (IsCharDirLimiter(path[i]))
+      break;
+  return path.Left(i + 1);
+}
+
+UString ExtractFileNameFromPath(const UString &path)
+{
+  int i;
+  for (i = path.Length() - 1; i >= 0; i--)
+    if (IsCharDirLimiter(path[i]))
+      break;
+  return path.Mid(i + 1);
+}
+
+
+bool CompareWildCardWithName(const UString &mask, const UString &name)
+{
+  return EnhancedMaskTest(mask, name);
+}
+
+bool DoesNameContainWildCard(const UString &path)
+{
+  return (path.FindOneOf(kWildCardCharSet) >= 0);
+}
+
+
+// ----------------------------------------------------------'
+// NWildcard
+
+namespace NWildcard {
+
+
+/*
+M = MaskParts.Size();
+N = TestNameParts.Size();
+
+                           File                          Dir
+ForFile     req   M<=N  [N-M, N)                          -
+         nonreq   M=N   [0, M)                            -
+ 
+ForDir      req   M<N   [0, M) ... [N-M-1, N-1)  same as ForBoth-File
+         nonreq         [0, M)                   same as ForBoth-File
+
+ForBoth     req   m<=N  [0, M) ... [N-M, N)      same as ForBoth-File
+         nonreq         [0, M)                   same as ForBoth-File
+
+*/
+
+bool CItem::CheckPath(const UStringVector &pathParts, bool isFile) const
+{
+  if (!isFile && !ForDir)
+    return false;
+  int delta = (int)pathParts.Size() - (int)PathParts.Size();
+  if (delta < 0)
+    return false;
+  int start = 0;
+  int finish = 0;
+  if (isFile)
+  {
+    if (!ForDir && !Recursive && delta !=0)
+      return false;
+    if (!ForFile && delta == 0)
+      return false;
+    if (!ForDir && Recursive)
+      start = delta;
+  }
+  if (Recursive)
+  {
+    finish = delta;
+    if (isFile && !ForFile)
+      finish = delta - 1;
+  }
+  for (int d = start; d <= finish; d++)
+  {
+    int i;
+    for (i = 0; i < PathParts.Size(); i++)
+      if (!CompareWildCardWithName(PathParts[i], pathParts[i + d]))
+        break;
+    if (i == PathParts.Size())
+      return true;
+  }
+  return false;
+}
+
+int CCensorNode::FindSubNode(const UString &name) const
+{
+  for (int i = 0; i < SubNodes.Size(); i++)
+    if (CompareFileNames(SubNodes[i].Name, name) == 0)
+      return i;
+  return -1;
+}
+
+void CCensorNode::AddItemSimple(bool include, CItem &item)
+{
+  if (include)
+    IncludeItems.Add(item);
+  else
+    ExcludeItems.Add(item);
+}
+
+void CCensorNode::AddItem(bool include, CItem &item)
+{
+  if (item.PathParts.Size() <= 1)
+  {
+    AddItemSimple(include, item);
+    return;
+  }
+  const UString &front = item.PathParts.Front();
+  if (DoesNameContainWildCard(front))
+  {
+    AddItemSimple(include, item);
+    return;
+  }
+  int index = FindSubNode(front);
+  if (index < 0)
+    index = SubNodes.Add(CCensorNode(front, this));
+  item.PathParts.Delete(0);
+  SubNodes[index].AddItem(include, item);
+}
+
+void CCensorNode::AddItem(bool include, const UString &path, bool recursive, bool forFile, bool forDir)
+{
+  CItem item;
+  SplitPathToParts(path, item.PathParts);
+  item.Recursive = recursive;
+  item.ForFile = forFile;
+  item.ForDir = forDir;
+  AddItem(include, item);
+}
+
+bool CCensorNode::NeedCheckSubDirs() const
+{
+  for (int i = 0; i < IncludeItems.Size(); i++)
+  {
+    const CItem &item = IncludeItems[i];
+    if (item.Recursive || item.PathParts.Size() > 1)
+      return true;
+  }
+  return false;
+}
+
+bool CCensorNode::AreThereIncludeItems() const
+{
+  if (IncludeItems.Size() > 0)
+    return true;
+  for (int i = 0; i < SubNodes.Size(); i++)
+    if (SubNodes[i].AreThereIncludeItems())
+      return true;
+  return false;
+}
+
+bool CCensorNode::CheckPathCurrent(bool include, const UStringVector &pathParts, bool isFile) const
+{
+  const CObjectVector<CItem> &items = include ? IncludeItems : ExcludeItems;
+  for (int i = 0; i < items.Size(); i++)
+    if (items[i].CheckPath(pathParts, isFile))
+      return true;
+  return false;
+}
+
+bool CCensorNode::CheckPath(UStringVector &pathParts, bool isFile, bool &include) const
+{
+  if (CheckPathCurrent(false, pathParts, isFile))
+  {
+    include = false;
+    return true;
+  }
+  include = true;
+  bool finded = CheckPathCurrent(true, pathParts, isFile);
+  if (pathParts.Size() == 1)
+    return finded;
+  int index = FindSubNode(pathParts.Front());
+  if (index >= 0)
+  {
+    UStringVector pathParts2 = pathParts;
+    pathParts2.Delete(0);
+    if (SubNodes[index].CheckPath(pathParts2, isFile, include))
+      return true;
+  }
+  return finded;
+}
+
+bool CCensorNode::CheckPath(const UString &path, bool isFile, bool &include) const
+{
+  UStringVector pathParts;
+  SplitPathToParts(path, pathParts);
+  return CheckPath(pathParts, isFile, include);
+}
+
+bool CCensorNode::CheckPath(const UString &path, bool isFile) const
+{
+  bool include;
+  if (CheckPath(path, isFile, include))
+    return include;
+  return false;
+}
+
+bool CCensorNode::CheckPathToRoot(bool include, UStringVector &pathParts, bool isFile) const
+{
+  if (CheckPathCurrent(include, pathParts, isFile))
+    return true;
+  if (Parent == 0)
+    return false;
+  pathParts.Insert(0, Name);
+  return Parent->CheckPathToRoot(include, pathParts, isFile);
+}
+
+/*
+bool CCensorNode::CheckPathToRoot(bool include, const UString &path, bool isFile) const
+{
+  UStringVector pathParts;
+  SplitPathToParts(path, pathParts);
+  return CheckPathToRoot(include, pathParts, isFile);
+}
+*/
+
+void CCensorNode::AddItem2(bool include, const UString &path, bool recursive)
+{
+  if (path.IsEmpty())
+    return;
+  bool forFile = true;
+  bool forFolder = true;
+  UString path2 = path;
+  if (IsCharDirLimiter(path[path.Length() - 1]))
+  {
+    path2.Delete(path.Length() - 1);
+    forFile = false;
+  }
+  AddItem(include, path2, recursive, forFile, forFolder);
+}
+
+void CCensorNode::ExtendExclude(const CCensorNode &fromNodes)
+{
+  ExcludeItems += fromNodes.ExcludeItems;
+  for (int i = 0; i < fromNodes.SubNodes.Size(); i++)
+  {
+    const CCensorNode &node = fromNodes.SubNodes[i];
+    int subNodeIndex = FindSubNode(node.Name);
+    if (subNodeIndex < 0)
+      subNodeIndex = SubNodes.Add(CCensorNode(node.Name, this));
+    SubNodes[subNodeIndex].ExtendExclude(node);
+  }
+}
+
+int CCensor::FindPrefix(const UString &prefix) const
+{
+  for (int i = 0; i < Pairs.Size(); i++)
+    if (CompareFileNames(Pairs[i].Prefix, prefix) == 0)
+      return i;
+  return -1;
+}
+
+void CCensor::AddItem(bool include, const UString &path, bool recursive)
+{
+  UStringVector pathParts;
+  SplitPathToParts(path, pathParts);
+  bool forFile = true;
+  if (pathParts.Back().IsEmpty())
+  {
+    forFile = false;
+    pathParts.DeleteBack();
+  }
+  const UString &front = pathParts.Front();
+  bool isAbs = false;
+  if (front.IsEmpty())
+    isAbs = true;
+  else if (front.Length() == 2 && front[1] == L':')
+    isAbs = true;
+  else
+  {
+    for (int i = 0; i < pathParts.Size(); i++)
+    {
+      const UString &part = pathParts[i];
+      if (part == L".." || part == L".")
+      {
+        isAbs = true;
+        break;
+      }
+    }
+  }
+  int numAbsParts = 0;
+  if (isAbs)
+    if (pathParts.Size() > 1)
+      numAbsParts = pathParts.Size() - 1;
+    else
+      numAbsParts = 1;
+  UString prefix;
+  for (int i = 0; i < numAbsParts; i++)
+  {
+    const UString &front = pathParts.Front();
+    if (DoesNameContainWildCard(front))
+      break;
+    prefix += front;
+    prefix += WCHAR_PATH_SEPARATOR;
+    pathParts.Delete(0);
+  }
+  int index = FindPrefix(prefix);
+  if (index < 0)
+    index = Pairs.Add(CPair(prefix));
+
+  CItem item;
+  item.PathParts = pathParts;
+  item.ForDir = true;
+  item.ForFile = forFile;
+  item.Recursive = recursive;
+  Pairs[index].Head.AddItem(include, item);
+}
+
+bool CCensor::CheckPath(const UString &path, bool isFile) const
+{
+  bool finded = false;
+  for (int i = 0; i < Pairs.Size(); i++)
+  {
+    bool include;
+    if (Pairs[i].Head.CheckPath(path, isFile, include))
+    {
+      if (!include)
+        return false;
+      finded = true;
+    }
+  }
+  return finded;
+}
+
+void CCensor::ExtendExclude()
+{
+  int i;
+  for (i = 0; i < Pairs.Size(); i++)
+    if (Pairs[i].Prefix.IsEmpty())
+      break;
+  if (i == Pairs.Size())
+    return;
+  int index = i;
+  for (i = 0; i < Pairs.Size(); i++)
+    if (index != i)
+      Pairs[i].Head.ExtendExclude(Pairs[index].Head);
+}
+
+}
diff --git a/third_party/lzma/v4_65/files/CPP/Common/Wildcard.h b/third_party/lzma/v4_65/files/CPP/Common/Wildcard.h
new file mode 100644
index 0000000..6d4cbce
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Common/Wildcard.h
@@ -0,0 +1,80 @@
+// Common/Wildcard.h
+
+#ifndef __COMMON_WILDCARD_H
+#define __COMMON_WILDCARD_H
+
+#include "MyString.h"
+
+int CompareFileNames(const UString &s1, const UString &s2);
+
+void SplitPathToParts(const UString &path, UStringVector &pathParts);
+void SplitPathToParts(const UString &path, UString &dirPrefix, UString &name);
+UString ExtractDirPrefixFromPath(const UString &path);
+UString ExtractFileNameFromPath(const UString &path);
+bool DoesNameContainWildCard(const UString &path);
+bool CompareWildCardWithName(const UString &mask, const UString &name);
+
+namespace NWildcard {
+
+struct CItem
+{
+  UStringVector PathParts;
+  bool Recursive;
+  bool ForFile;
+  bool ForDir;
+  bool CheckPath(const UStringVector &pathParts, bool isFile) const;
+};
+
+class CCensorNode
+{
+  CCensorNode *Parent;
+  bool CheckPathCurrent(bool include, const UStringVector &pathParts, bool isFile) const;
+  void AddItemSimple(bool include, CItem &item);
+  bool CheckPath(UStringVector &pathParts, bool isFile, bool &include) const;
+public:
+  CCensorNode(): Parent(0) { };
+  CCensorNode(const UString &name, CCensorNode *parent): Name(name), Parent(parent) { };
+  UString Name;
+  CObjectVector<CCensorNode> SubNodes;
+  CObjectVector<CItem> IncludeItems;
+  CObjectVector<CItem> ExcludeItems;
+
+  int FindSubNode(const UString &path) const;
+
+  void AddItem(bool include, CItem &item);
+  void AddItem(bool include, const UString &path, bool recursive, bool forFile, bool forDir);
+  void AddItem2(bool include, const UString &path, bool recursive);
+
+  bool NeedCheckSubDirs() const;
+  bool AreThereIncludeItems() const;
+
+  bool CheckPath(const UString &path, bool isFile, bool &include) const;
+  bool CheckPath(const UString &path, bool isFile) const;
+
+  bool CheckPathToRoot(bool include, UStringVector &pathParts, bool isFile) const;
+  // bool CheckPathToRoot(const UString &path, bool isFile, bool include) const;
+  void ExtendExclude(const CCensorNode &fromNodes);
+};
+
+struct CPair
+{
+  UString Prefix;
+  CCensorNode Head;
+  CPair(const UString &prefix): Prefix(prefix) { };
+};
+
+class CCensor
+{
+  int FindPrefix(const UString &prefix) const;
+public:
+  CObjectVector<CPair> Pairs;
+  bool AllAreRelative() const
+    { return (Pairs.Size() == 1 && Pairs.Front().Prefix.IsEmpty()); }
+  void AddItem(bool include, const UString &path, bool recursive);
+  bool CheckPath(const UString &path, bool isFile) const;
+  void ExtendExclude();
+};
+
+}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/DLL.cpp b/third_party/lzma/v4_65/files/CPP/Windows/DLL.cpp
new file mode 100644
index 0000000..8a95a25
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/DLL.cpp
@@ -0,0 +1,115 @@
+// Windows/DLL.cpp
+
+#include "StdAfx.h"
+
+#include "DLL.h"
+#include "Defs.h"
+#ifndef _UNICODE
+#include "../Common/StringConvert.h"
+#endif
+
+#ifndef _UNICODE
+extern bool g_IsNT;
+#endif
+
+namespace NWindows {
+namespace NDLL {
+
+CLibrary::~CLibrary()
+{
+  Free();
+}
+
+bool CLibrary::Free()
+{
+  if (_module == 0)
+    return true;
+  // MessageBox(0, TEXT(""), TEXT("Free"), 0);
+  // Sleep(5000);
+  if (!::FreeLibrary(_module))
+    return false;
+  _module = 0;
+  return true;
+}
+
+bool CLibrary::LoadOperations(HMODULE newModule)
+{
+  if (newModule == NULL)
+    return false;
+  if (!Free())
+    return false;
+  _module = newModule;
+  return true;
+}
+
+bool CLibrary::LoadEx(LPCTSTR fileName, DWORD flags)
+{
+  // MessageBox(0, fileName, TEXT("LoadEx"), 0);
+  return LoadOperations(::LoadLibraryEx(fileName, NULL, flags));
+}
+
+bool CLibrary::Load(LPCTSTR fileName)
+{
+  // MessageBox(0, fileName, TEXT("Load"), 0);
+  // Sleep(5000);
+  // OutputDebugString(fileName);
+  // OutputDebugString(TEXT("\n"));
+  return LoadOperations(::LoadLibrary(fileName));
+}
+
+#ifndef _UNICODE
+static inline UINT GetCurrentCodePage() { return ::AreFileApisANSI() ? CP_ACP : CP_OEMCP; }
+CSysString GetSysPath(LPCWSTR sysPath)
+  { return UnicodeStringToMultiByte(sysPath, GetCurrentCodePage()); }
+
+bool CLibrary::LoadEx(LPCWSTR fileName, DWORD flags)
+{
+  if (g_IsNT)
+    return LoadOperations(::LoadLibraryExW(fileName, NULL, flags));
+  return LoadEx(GetSysPath(fileName), flags);
+}
+bool CLibrary::Load(LPCWSTR fileName)
+{
+  if (g_IsNT)
+    return LoadOperations(::LoadLibraryW(fileName));
+  return Load(GetSysPath(fileName));
+}
+#endif
+
+bool MyGetModuleFileName(HMODULE hModule, CSysString &result)
+{
+  result.Empty();
+  TCHAR fullPath[MAX_PATH + 2];
+  DWORD size = ::GetModuleFileName(hModule, fullPath, MAX_PATH + 1);
+  if (size <= MAX_PATH && size != 0)
+  {
+    result = fullPath;
+    return true;
+  }
+  return false;
+}
+
+#ifndef _UNICODE
+bool MyGetModuleFileName(HMODULE hModule, UString &result)
+{
+  result.Empty();
+  if (g_IsNT)
+  {
+    wchar_t fullPath[MAX_PATH + 2];
+    DWORD size = ::GetModuleFileNameW(hModule, fullPath, MAX_PATH + 1);
+    if (size <= MAX_PATH && size != 0)
+    {
+      result = fullPath;
+      return true;
+    }
+    return false;
+  }
+  CSysString resultSys;
+  if (!MyGetModuleFileName(hModule, resultSys))
+    return false;
+  result = MultiByteToUnicodeString(resultSys, GetCurrentCodePage());
+  return true;
+}
+#endif
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/DLL.h b/third_party/lzma/v4_65/files/CPP/Windows/DLL.h
new file mode 100644
index 0000000..4c2ffa2
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/DLL.h
@@ -0,0 +1,54 @@
+// Windows/DLL.h
+
+#ifndef __WINDOWS_DLL_H
+#define __WINDOWS_DLL_H
+
+#include "../Common/MyString.h"
+
+namespace NWindows {
+namespace NDLL {
+
+class CLibrary
+{
+  bool LoadOperations(HMODULE newModule);
+protected:
+  HMODULE _module;
+public:
+  operator HMODULE() const { return _module; }
+  HMODULE* operator&() { return &_module; }
+
+  CLibrary():_module(NULL) {};
+  ~CLibrary();
+  void Attach(HMODULE m)
+  {
+    Free();
+    _module = m;
+  }
+  HMODULE Detach()
+  {
+    HMODULE m = _module;
+    _module = NULL;
+    return m;
+  }
+
+  // operator HMODULE() const { return _module; };
+  bool IsLoaded() const { return (_module != NULL); };
+  bool Free();
+  bool LoadEx(LPCTSTR fileName, DWORD flags = LOAD_LIBRARY_AS_DATAFILE);
+  bool Load(LPCTSTR fileName);
+  #ifndef _UNICODE
+  bool LoadEx(LPCWSTR fileName, DWORD flags = LOAD_LIBRARY_AS_DATAFILE);
+  bool Load(LPCWSTR fileName);
+  #endif
+  FARPROC GetProcAddress(LPCSTR procName) const
+    { return ::GetProcAddress(_module, procName); }
+};
+
+bool MyGetModuleFileName(HMODULE hModule, CSysString &result);
+#ifndef _UNICODE
+bool MyGetModuleFileName(HMODULE hModule, UString &result);
+#endif
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/Defs.h b/third_party/lzma/v4_65/files/CPP/Windows/Defs.h
new file mode 100644
index 0000000..898be8d
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/Defs.h
@@ -0,0 +1,23 @@
+// Windows/Defs.h
+
+#ifndef __WINDOWS_DEFS_H
+#define __WINDOWS_DEFS_H
+
+inline bool BOOLToBool(BOOL value)
+  { return (value != FALSE); }
+
+#ifdef _WIN32
+inline bool LRESULTToBool(LRESULT value)
+  { return (value != FALSE); }
+#endif
+
+inline BOOL BoolToBOOL(bool value)
+  { return (value ? TRUE: FALSE); }
+
+inline VARIANT_BOOL BoolToVARIANT_BOOL(bool value)
+  { return (value ? VARIANT_TRUE: VARIANT_FALSE); }
+
+inline bool VARIANT_BOOLToBool(VARIANT_BOOL value)
+  { return (value != VARIANT_FALSE); }
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/Error.cpp b/third_party/lzma/v4_65/files/CPP/Windows/Error.cpp
new file mode 100644
index 0000000..7b18c29
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/Error.cpp
@@ -0,0 +1,50 @@
+// Windows/Error.h
+
+#include "StdAfx.h"
+
+#include "Windows/Error.h"
+#ifndef _UNICODE
+#include "Common/StringConvert.h"
+#endif
+
+#ifndef _UNICODE
+extern bool g_IsNT;
+#endif
+
+namespace NWindows {
+namespace NError {
+
+bool MyFormatMessage(DWORD messageID, CSysString &message)
+{
+  LPVOID msgBuf;
+  if (::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+      FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+      NULL,messageID, 0, (LPTSTR) &msgBuf,0, NULL) == 0)
+    return false;
+  message = (LPCTSTR)msgBuf;
+  ::LocalFree(msgBuf);
+  return true;
+}
+
+#ifndef _UNICODE
+bool MyFormatMessage(DWORD messageID, UString &message)
+{
+  if (g_IsNT)
+  {
+    LPVOID msgBuf;
+    if (::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+        NULL, messageID, 0, (LPWSTR) &msgBuf, 0, NULL) == 0)
+      return false;
+    message = (LPCWSTR)msgBuf;
+    ::LocalFree(msgBuf);
+    return true;
+  }
+  CSysString messageSys;
+  bool result = MyFormatMessage(messageID, messageSys);
+  message = GetUnicodeString(messageSys);
+  return result;
+}
+#endif
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/Error.h b/third_party/lzma/v4_65/files/CPP/Windows/Error.h
new file mode 100644
index 0000000..05b5cd0
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/Error.h
@@ -0,0 +1,33 @@
+// Windows/Error.h
+
+#ifndef __WINDOWS_ERROR_H
+#define __WINDOWS_ERROR_H
+
+#include "Common/MyString.h"
+
+namespace NWindows {
+namespace NError {
+
+bool MyFormatMessage(DWORD messageID, CSysString &message);
+inline CSysString MyFormatMessage(DWORD messageID)
+{
+  CSysString message;
+  MyFormatMessage(messageID, message);
+  return message;
+}
+#ifdef _UNICODE
+inline UString MyFormatMessageW(DWORD messageID)
+  { return MyFormatMessage(messageID); }
+#else
+bool MyFormatMessage(DWORD messageID, UString &message);
+inline UString MyFormatMessageW(DWORD messageID)
+{
+  UString message;
+  MyFormatMessage(messageID, message);
+  return message;
+}
+#endif
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/FileDir.cpp b/third_party/lzma/v4_65/files/CPP/Windows/FileDir.cpp
new file mode 100644
index 0000000..4224d6f
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/FileDir.cpp
@@ -0,0 +1,841 @@
+// Windows/FileDir.cpp
+
+#include "StdAfx.h"
+
+#include "FileDir.h"
+#include "FileName.h"
+#include "FileFind.h"
+#include "Defs.h"
+#ifndef _UNICODE
+#include "../Common/StringConvert.h"
+#endif
+
+#ifndef _UNICODE
+extern bool g_IsNT;
+#endif
+
+namespace NWindows {
+namespace NFile {
+
+#if defined(WIN_LONG_PATH) && defined(_UNICODE)
+#define WIN_LONG_PATH2
+#endif
+
+// SetCurrentDirectory doesn't support \\?\ prefix
+
+#ifdef WIN_LONG_PATH
+bool GetLongPathBase(LPCWSTR fileName, UString &res);
+bool GetLongPath(LPCWSTR fileName, UString &res);
+#endif
+
+namespace NDirectory {
+
+#ifndef _UNICODE
+static inline UINT GetCurrentCodePage() { return ::AreFileApisANSI() ? CP_ACP : CP_OEMCP; }
+static UString GetUnicodePath(const CSysString &sysPath)
+  { return MultiByteToUnicodeString(sysPath, GetCurrentCodePage()); }
+static CSysString GetSysPath(LPCWSTR sysPath)
+  { return UnicodeStringToMultiByte(sysPath, GetCurrentCodePage()); }
+#endif
+
+bool MyGetWindowsDirectory(CSysString &path)
+{
+  UINT needLength = ::GetWindowsDirectory(path.GetBuffer(MAX_PATH + 1), MAX_PATH + 1);
+  path.ReleaseBuffer();
+  return (needLength > 0 && needLength <= MAX_PATH);
+}
+
+bool MyGetSystemDirectory(CSysString &path)
+{
+  UINT needLength = ::GetSystemDirectory(path.GetBuffer(MAX_PATH + 1), MAX_PATH + 1);
+  path.ReleaseBuffer();
+  return (needLength > 0 && needLength <= MAX_PATH);
+}
+
+#ifndef _UNICODE
+bool MyGetWindowsDirectory(UString &path)
+{
+  if (g_IsNT)
+  {
+    UINT needLength = ::GetWindowsDirectoryW(path.GetBuffer(MAX_PATH + 1), MAX_PATH + 1);
+    path.ReleaseBuffer();
+    return (needLength > 0 && needLength <= MAX_PATH);
+  }
+  CSysString sysPath;
+  if (!MyGetWindowsDirectory(sysPath))
+    return false;
+  path = GetUnicodePath(sysPath);
+  return true;
+}
+
+bool MyGetSystemDirectory(UString &path)
+{
+  if (g_IsNT)
+  {
+    UINT needLength = ::GetSystemDirectoryW(path.GetBuffer(MAX_PATH + 1), MAX_PATH + 1);
+    path.ReleaseBuffer();
+    return (needLength > 0 && needLength <= MAX_PATH);
+  }
+  CSysString sysPath;
+  if (!MyGetSystemDirectory(sysPath))
+    return false;
+  path = GetUnicodePath(sysPath);
+  return true;
+}
+#endif
+
+bool SetDirTime(LPCWSTR fileName, const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime)
+{
+  #ifndef _UNICODE
+  if (!g_IsNT)
+  {
+    ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+    return false;
+  }
+  #endif
+  HANDLE hDir = ::CreateFileW(fileName, GENERIC_WRITE,
+      FILE_SHARE_READ | FILE_SHARE_WRITE,
+      NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+  #ifdef WIN_LONG_PATH
+  if (hDir == INVALID_HANDLE_VALUE)
+  {
+    UString longPath;
+    if (GetLongPath(fileName, longPath))
+      hDir = ::CreateFileW(longPath, GENERIC_WRITE,
+        FILE_SHARE_READ | FILE_SHARE_WRITE,
+        NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+  }
+  #endif
+
+  bool res = false;
+  if (hDir != INVALID_HANDLE_VALUE)
+  {
+    res = BOOLToBool(::SetFileTime(hDir, cTime, aTime, mTime));
+    ::CloseHandle(hDir);
+  }
+  return res;
+}
+
+bool MySetFileAttributes(LPCTSTR fileName, DWORD fileAttributes)
+{
+  if (::SetFileAttributes(fileName, fileAttributes))
+    return true;
+  #ifdef WIN_LONG_PATH2
+  UString longPath;
+  if (GetLongPath(fileName, longPath))
+    return BOOLToBool(::SetFileAttributesW(longPath, fileAttributes));
+  #endif
+  return false;
+}
+
+bool MyRemoveDirectory(LPCTSTR pathName)
+{
+  if (::RemoveDirectory(pathName))
+    return true;
+  #ifdef WIN_LONG_PATH2
+  UString longPath;
+  if (GetLongPath(pathName, longPath))
+    return BOOLToBool(::RemoveDirectoryW(longPath));
+  #endif
+  return false;
+}
+
+#ifdef WIN_LONG_PATH
+bool GetLongPaths(LPCWSTR s1, LPCWSTR s2, UString &d1, UString &d2)
+{
+  if (!GetLongPathBase(s1, d1) || !GetLongPathBase(s2, d2))
+    return false;
+  if (d1.IsEmpty() && d2.IsEmpty()) return false;
+  if (d1.IsEmpty()) d1 = s1;
+  if (d2.IsEmpty()) d2 = s2;
+  return true;
+}
+#endif
+
+bool MyMoveFile(LPCTSTR existFileName, LPCTSTR newFileName)
+{
+  if (::MoveFile(existFileName, newFileName))
+    return true;
+  #ifdef WIN_LONG_PATH2
+  UString d1, d2;
+  if (GetLongPaths(existFileName, newFileName, d1, d2))
+    return BOOLToBool(::MoveFileW(d1, d2));
+  #endif
+  return false;
+}
+
+#ifndef _UNICODE
+bool MySetFileAttributes(LPCWSTR fileName, DWORD fileAttributes)
+{
+  if (!g_IsNT)
+    return MySetFileAttributes(GetSysPath(fileName), fileAttributes);
+  if (::SetFileAttributesW(fileName, fileAttributes))
+    return true;
+  #ifdef WIN_LONG_PATH
+  UString longPath;
+  if (GetLongPath(fileName, longPath))
+    return BOOLToBool(::SetFileAttributesW(longPath, fileAttributes));
+  #endif
+  return false;
+}
+
+
+bool MyRemoveDirectory(LPCWSTR pathName)
+{
+  if (!g_IsNT)
+    return MyRemoveDirectory(GetSysPath(pathName));
+  if (::RemoveDirectoryW(pathName))
+    return true;
+  #ifdef WIN_LONG_PATH
+  UString longPath;
+  if (GetLongPath(pathName, longPath))
+    return BOOLToBool(::RemoveDirectoryW(longPath));
+  #endif
+  return false;
+}
+
+bool MyMoveFile(LPCWSTR existFileName, LPCWSTR newFileName)
+{
+  if (!g_IsNT)
+    return MyMoveFile(GetSysPath(existFileName), GetSysPath(newFileName));
+  if (::MoveFileW(existFileName, newFileName))
+    return true;
+  #ifdef WIN_LONG_PATH
+  UString d1, d2;
+  if (GetLongPaths(existFileName, newFileName, d1, d2))
+    return BOOLToBool(::MoveFileW(d1, d2));
+  #endif
+  return false;
+}
+#endif
+
+bool MyCreateDirectory(LPCTSTR pathName)
+{
+  if (::CreateDirectory(pathName, NULL))
+    return true;
+  #ifdef WIN_LONG_PATH2
+  if (::GetLastError() != ERROR_ALREADY_EXISTS)
+  {
+    UString longPath;
+    if (GetLongPath(pathName, longPath))
+      return BOOLToBool(::CreateDirectoryW(longPath, NULL));
+  }
+  #endif
+  return false;
+}
+
+#ifndef _UNICODE
+bool MyCreateDirectory(LPCWSTR pathName)
+{
+  if (!g_IsNT)
+    return MyCreateDirectory(GetSysPath(pathName));
+  if (::CreateDirectoryW(pathName, NULL))
+    return true;
+  #ifdef WIN_LONG_PATH
+  if (::GetLastError() != ERROR_ALREADY_EXISTS)
+  {
+    UString longPath;
+    if (GetLongPath(pathName, longPath))
+      return BOOLToBool(::CreateDirectoryW(longPath, NULL));
+  }
+  #endif
+  return false;
+}
+#endif
+
+/*
+bool CreateComplexDirectory(LPCTSTR pathName)
+{
+  NName::CParsedPath path;
+  path.ParsePath(pathName);
+  CSysString fullPath = path.Prefix;
+  DWORD errorCode = ERROR_SUCCESS;
+  for (int i = 0; i < path.PathParts.Size(); i++)
+  {
+    const CSysString &string = path.PathParts[i];
+    if (string.IsEmpty())
+    {
+      if (i != path.PathParts.Size() - 1)
+        return false;
+      return true;
+    }
+    fullPath += path.PathParts[i];
+    if (!MyCreateDirectory(fullPath))
+    {
+      DWORD errorCode = GetLastError();
+      if (errorCode != ERROR_ALREADY_EXISTS)
+        return false;
+    }
+    fullPath += NName::kDirDelimiter;
+  }
+  return true;
+}
+*/
+
+bool CreateComplexDirectory(LPCTSTR _aPathName)
+{
+  CSysString pathName = _aPathName;
+  int pos = pathName.ReverseFind(TEXT(CHAR_PATH_SEPARATOR));
+  if (pos > 0 && pos == pathName.Length() - 1)
+  {
+    if (pathName.Length() == 3 && pathName[1] == ':')
+      return true; // Disk folder;
+    pathName.Delete(pos);
+  }
+  CSysString pathName2 = pathName;
+  pos = pathName.Length();
+  for (;;)
+  {
+    if (MyCreateDirectory(pathName))
+      break;
+    if (::GetLastError() == ERROR_ALREADY_EXISTS)
+    {
+      NFind::CFileInfo fileInfo;
+      if (!NFind::FindFile(pathName, fileInfo)) // For network folders
+        return true;
+      if (!fileInfo.IsDir())
+        return false;
+      break;
+    }
+    pos = pathName.ReverseFind(TEXT(CHAR_PATH_SEPARATOR));
+    if (pos < 0 || pos == 0)
+      return false;
+    if (pathName[pos - 1] == ':')
+      return false;
+    pathName = pathName.Left(pos);
+  }
+  pathName = pathName2;
+  while (pos < pathName.Length())
+  {
+    pos = pathName.Find(TEXT(CHAR_PATH_SEPARATOR), pos + 1);
+    if (pos < 0)
+      pos = pathName.Length();
+    if (!MyCreateDirectory(pathName.Left(pos)))
+      return false;
+  }
+  return true;
+}
+
+#ifndef _UNICODE
+
+bool CreateComplexDirectory(LPCWSTR _aPathName)
+{
+  UString pathName = _aPathName;
+  int pos = pathName.ReverseFind(WCHAR_PATH_SEPARATOR);
+  if (pos > 0 && pos == pathName.Length() - 1)
+  {
+    if (pathName.Length() == 3 && pathName[1] == L':')
+      return true; // Disk folder;
+    pathName.Delete(pos);
+  }
+  UString pathName2 = pathName;
+  pos = pathName.Length();
+  for (;;)
+  {
+    if (MyCreateDirectory(pathName))
+      break;
+    if (::GetLastError() == ERROR_ALREADY_EXISTS)
+    {
+      NFind::CFileInfoW fileInfo;
+      if (!NFind::FindFile(pathName, fileInfo)) // For network folders
+        return true;
+      if (!fileInfo.IsDir())
+        return false;
+      break;
+    }
+    pos = pathName.ReverseFind(WCHAR_PATH_SEPARATOR);
+    if (pos < 0 || pos == 0)
+      return false;
+    if (pathName[pos - 1] == L':')
+      return false;
+    pathName = pathName.Left(pos);
+  }
+  pathName = pathName2;
+  while (pos < pathName.Length())
+  {
+    pos = pathName.Find(WCHAR_PATH_SEPARATOR, pos + 1);
+    if (pos < 0)
+      pos = pathName.Length();
+    if (!MyCreateDirectory(pathName.Left(pos)))
+      return false;
+  }
+  return true;
+}
+
+#endif
+
+bool DeleteFileAlways(LPCTSTR name)
+{
+  if (!MySetFileAttributes(name, 0))
+    return false;
+  if (::DeleteFile(name))
+    return true;
+  #ifdef WIN_LONG_PATH2
+  UString longPath;
+  if (GetLongPath(name, longPath))
+    return BOOLToBool(::DeleteFileW(longPath));
+  #endif
+  return false;
+}
+
+#ifndef _UNICODE
+bool DeleteFileAlways(LPCWSTR name)
+{
+  if (!g_IsNT)
+    return DeleteFileAlways(GetSysPath(name));
+  if (!MySetFileAttributes(name, 0))
+    return false;
+  if (::DeleteFileW(name))
+    return true;
+  #ifdef WIN_LONG_PATH
+  UString longPath;
+  if (GetLongPath(name, longPath))
+    return BOOLToBool(::DeleteFileW(longPath));
+  #endif
+  return false;
+}
+#endif
+
+static bool RemoveDirectorySubItems2(const CSysString pathPrefix, const NFind::CFileInfo &fileInfo)
+{
+  if (fileInfo.IsDir())
+    return RemoveDirectoryWithSubItems(pathPrefix + fileInfo.Name);
+  return DeleteFileAlways(pathPrefix + fileInfo.Name);
+}
+
+bool RemoveDirectoryWithSubItems(const CSysString &path)
+{
+  NFind::CFileInfo fileInfo;
+  CSysString pathPrefix = path + NName::kDirDelimiter;
+  {
+    NFind::CEnumerator enumerator(pathPrefix + TCHAR(NName::kAnyStringWildcard));
+    while (enumerator.Next(fileInfo))
+      if (!RemoveDirectorySubItems2(pathPrefix, fileInfo))
+        return false;
+  }
+  if (!MySetFileAttributes(path, 0))
+    return false;
+  return MyRemoveDirectory(path);
+}
+
+#ifndef _UNICODE
+static bool RemoveDirectorySubItems2(const UString pathPrefix, const NFind::CFileInfoW &fileInfo)
+{
+  if (fileInfo.IsDir())
+    return RemoveDirectoryWithSubItems(pathPrefix + fileInfo.Name);
+  return DeleteFileAlways(pathPrefix + fileInfo.Name);
+}
+bool RemoveDirectoryWithSubItems(const UString &path)
+{
+  NFind::CFileInfoW fileInfo;
+  UString pathPrefix = path + UString(NName::kDirDelimiter);
+  {
+    NFind::CEnumeratorW enumerator(pathPrefix + UString(NName::kAnyStringWildcard));
+    while (enumerator.Next(fileInfo))
+      if (!RemoveDirectorySubItems2(pathPrefix, fileInfo))
+        return false;
+  }
+  if (!MySetFileAttributes(path, 0))
+    return false;
+  return MyRemoveDirectory(path);
+}
+#endif
+
+#ifndef _WIN32_WCE
+
+bool MyGetShortPathName(LPCTSTR longPath, CSysString &shortPath)
+{
+  DWORD needLength = ::GetShortPathName(longPath, shortPath.GetBuffer(MAX_PATH + 1), MAX_PATH + 1);
+  shortPath.ReleaseBuffer();
+  return (needLength > 0 && needLength < MAX_PATH);
+}
+
+bool MyGetFullPathName(LPCTSTR fileName, CSysString &resultPath, int &fileNamePartStartIndex)
+{
+  resultPath.Empty();
+  LPTSTR fileNamePointer = 0;
+  LPTSTR buffer = resultPath.GetBuffer(MAX_PATH);
+  DWORD needLength = ::GetFullPathName(fileName, MAX_PATH + 1, buffer, &fileNamePointer);
+  resultPath.ReleaseBuffer();
+  if (needLength == 0)
+    return false;
+  if (needLength >= MAX_PATH)
+  {
+    #ifdef WIN_LONG_PATH2
+    needLength++;
+    buffer = resultPath.GetBuffer(needLength + 1);
+    DWORD needLength2 = ::GetFullPathNameW(fileName, needLength, buffer, &fileNamePointer);
+    resultPath.ReleaseBuffer();
+    if (needLength2 == 0 || needLength2 > needLength)
+    #endif
+      return false;
+  }
+  if (fileNamePointer == 0)
+    fileNamePartStartIndex = lstrlen(fileName);
+  else
+    fileNamePartStartIndex = (int)(fileNamePointer - buffer);
+  return true;
+}
+
+#ifndef _UNICODE
+bool MyGetFullPathName(LPCWSTR fileName, UString &resultPath, int &fileNamePartStartIndex)
+{
+  resultPath.Empty();
+  if (g_IsNT)
+  {
+    LPWSTR fileNamePointer = 0;
+    LPWSTR buffer = resultPath.GetBuffer(MAX_PATH);
+    DWORD needLength = ::GetFullPathNameW(fileName, MAX_PATH + 1, buffer, &fileNamePointer);
+    resultPath.ReleaseBuffer();
+    if (needLength == 0)
+      return false;
+    if (needLength >= MAX_PATH)
+    {
+      #ifdef WIN_LONG_PATH
+      needLength++;
+      buffer = resultPath.GetBuffer(needLength + 1);
+      DWORD needLength2 = ::GetFullPathNameW(fileName, needLength, buffer, &fileNamePointer);
+      resultPath.ReleaseBuffer();
+      if (needLength2 == 0 || needLength2 > needLength)
+      #endif
+        return false;
+    }
+    if (fileNamePointer == 0)
+      fileNamePartStartIndex = MyStringLen(fileName);
+    else
+      fileNamePartStartIndex = (int)(fileNamePointer - buffer);
+  }
+  else
+  {
+    CSysString sysPath;
+    if (!MyGetFullPathName(GetSysPath(fileName), sysPath, fileNamePartStartIndex))
+      return false;
+    UString resultPath1 = GetUnicodePath(sysPath.Left(fileNamePartStartIndex));
+    UString resultPath2 = GetUnicodePath(sysPath.Mid(fileNamePartStartIndex));
+    fileNamePartStartIndex = resultPath1.Length();
+    resultPath = resultPath1 + resultPath2;
+  }
+  return true;
+}
+#endif
+
+
+bool MyGetFullPathName(LPCTSTR fileName, CSysString &path)
+{
+  int index;
+  return MyGetFullPathName(fileName, path, index);
+}
+
+#ifndef _UNICODE
+bool MyGetFullPathName(LPCWSTR fileName, UString &path)
+{
+  int index;
+  return MyGetFullPathName(fileName, path, index);
+}
+#endif
+
+bool GetOnlyName(LPCTSTR fileName, CSysString &resultName)
+{
+  int index;
+  if (!MyGetFullPathName(fileName, resultName, index))
+    return false;
+  resultName = resultName.Mid(index);
+  return true;
+}
+
+#ifndef _UNICODE
+bool GetOnlyName(LPCWSTR fileName, UString &resultName)
+{
+  int index;
+  if (!MyGetFullPathName(fileName, resultName, index))
+    return false;
+  resultName = resultName.Mid(index);
+  return true;
+}
+#endif
+
+bool GetOnlyDirPrefix(LPCTSTR fileName, CSysString &resultName)
+{
+  int index;
+  if (!MyGetFullPathName(fileName, resultName, index))
+    return false;
+  resultName = resultName.Left(index);
+  return true;
+}
+
+#ifndef _UNICODE
+bool GetOnlyDirPrefix(LPCWSTR fileName, UString &resultName)
+{
+  int index;
+  if (!MyGetFullPathName(fileName, resultName, index))
+    return false;
+  resultName = resultName.Left(index);
+  return true;
+}
+#endif
+
+bool MyGetCurrentDirectory(CSysString &path)
+{
+  DWORD needLength = ::GetCurrentDirectory(MAX_PATH + 1, path.GetBuffer(MAX_PATH + 1));
+  path.ReleaseBuffer();
+  return (needLength > 0 && needLength <= MAX_PATH);
+}
+
+#ifndef _UNICODE
+bool MySetCurrentDirectory(LPCWSTR path)
+{
+  if (g_IsNT)
+    return BOOLToBool(::SetCurrentDirectoryW(path));
+  return MySetCurrentDirectory(GetSysPath(path));
+}
+bool MyGetCurrentDirectory(UString &path)
+{
+  if (g_IsNT)
+  {
+    DWORD needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, path.GetBuffer(MAX_PATH + 1));
+    path.ReleaseBuffer();
+    return (needLength > 0 && needLength <= MAX_PATH);
+  }
+  CSysString sysPath;
+  if (!MyGetCurrentDirectory(sysPath))
+    return false;
+  path = GetUnicodePath(sysPath);
+  return true;
+}
+#endif
+#endif
+
+bool MySearchPath(LPCTSTR path, LPCTSTR fileName, LPCTSTR extension,
+  CSysString &resultPath, UINT32 &filePart)
+{
+  LPTSTR filePartPointer;
+  DWORD value = ::SearchPath(path, fileName, extension,
+    MAX_PATH, resultPath.GetBuffer(MAX_PATH + 1), &filePartPointer);
+  filePart = (UINT32)(filePartPointer - (LPCTSTR)resultPath);
+  resultPath.ReleaseBuffer();
+  return (value > 0 && value <= MAX_PATH);
+}
+
+#ifndef _UNICODE
+bool MySearchPath(LPCWSTR path, LPCWSTR fileName, LPCWSTR extension,
+  UString &resultPath, UINT32 &filePart)
+{
+  if (g_IsNT)
+  {
+    LPWSTR filePartPointer = 0;
+    DWORD value = ::SearchPathW(path, fileName, extension,
+        MAX_PATH, resultPath.GetBuffer(MAX_PATH + 1), &filePartPointer);
+    filePart = (UINT32)(filePartPointer - (LPCWSTR)resultPath);
+    resultPath.ReleaseBuffer();
+    return (value > 0 && value <= MAX_PATH);
+  }
+  
+  CSysString sysPath;
+  if (!MySearchPath(
+      path != 0 ? (LPCTSTR)GetSysPath(path): 0,
+      fileName != 0 ? (LPCTSTR)GetSysPath(fileName): 0,
+      extension != 0 ? (LPCTSTR)GetSysPath(extension): 0,
+      sysPath, filePart))
+    return false;
+  UString resultPath1 = GetUnicodePath(sysPath.Left(filePart));
+  UString resultPath2 = GetUnicodePath(sysPath.Mid(filePart));
+  filePart = resultPath1.Length();
+  resultPath = resultPath1 + resultPath2;
+  return true;
+}
+#endif
+
+bool MyGetTempPath(CSysString &path)
+{
+  DWORD needLength = ::GetTempPath(MAX_PATH + 1, path.GetBuffer(MAX_PATH + 1));
+  path.ReleaseBuffer();
+  return (needLength > 0 && needLength <= MAX_PATH);
+}
+
+#ifndef _UNICODE
+bool MyGetTempPath(UString &path)
+{
+  path.Empty();
+  if (g_IsNT)
+  {
+    DWORD needLength = ::GetTempPathW(MAX_PATH + 1, path.GetBuffer(MAX_PATH + 1));
+    path.ReleaseBuffer();
+    return (needLength > 0 && needLength <= MAX_PATH);
+  }
+  CSysString sysPath;
+  if (!MyGetTempPath(sysPath))
+    return false;
+  path = GetUnicodePath(sysPath);
+  return true;
+}
+#endif
+
+UINT MyGetTempFileName(LPCTSTR dirPath, LPCTSTR prefix, CSysString &path)
+{
+  UINT number = ::GetTempFileName(dirPath, prefix, 0, path.GetBuffer(MAX_PATH + 1));
+  path.ReleaseBuffer();
+  return number;
+}
+
+#ifndef _UNICODE
+UINT MyGetTempFileName(LPCWSTR dirPath, LPCWSTR prefix, UString &path)
+{
+  if (g_IsNT)
+  {
+    UINT number = ::GetTempFileNameW(dirPath, prefix, 0, path.GetBuffer(MAX_PATH));
+    path.ReleaseBuffer();
+    return number;
+  }
+  CSysString sysPath;
+  UINT number = MyGetTempFileName(
+      dirPath ? (LPCTSTR)GetSysPath(dirPath): 0,
+      prefix ? (LPCTSTR)GetSysPath(prefix): 0,
+      sysPath);
+  path = GetUnicodePath(sysPath);
+  return number;
+}
+#endif
+
+UINT CTempFile::Create(LPCTSTR dirPath, LPCTSTR prefix, CSysString &resultPath)
+{
+  Remove();
+  UINT number = MyGetTempFileName(dirPath, prefix, resultPath);
+  if (number != 0)
+  {
+    _fileName = resultPath;
+    _mustBeDeleted = true;
+  }
+  return number;
+}
+
+bool CTempFile::Create(LPCTSTR prefix, CSysString &resultPath)
+{
+  CSysString tempPath;
+  if (!MyGetTempPath(tempPath))
+    return false;
+  if (Create(tempPath, prefix, resultPath) != 0)
+    return true;
+  if (!MyGetWindowsDirectory(tempPath))
+    return false;
+  return (Create(tempPath, prefix, resultPath) != 0);
+}
+
+bool CTempFile::Remove()
+{
+  if (!_mustBeDeleted)
+    return true;
+  _mustBeDeleted = !DeleteFileAlways(_fileName);
+  return !_mustBeDeleted;
+}
+
+#ifndef _UNICODE
+
+UINT CTempFileW::Create(LPCWSTR dirPath, LPCWSTR prefix, UString &resultPath)
+{
+  Remove();
+  UINT number = MyGetTempFileName(dirPath, prefix, resultPath);
+  if (number != 0)
+  {
+    _fileName = resultPath;
+    _mustBeDeleted = true;
+  }
+  return number;
+}
+
+bool CTempFileW::Create(LPCWSTR prefix, UString &resultPath)
+{
+  UString tempPath;
+  if (!MyGetTempPath(tempPath))
+    return false;
+  if (Create(tempPath, prefix, resultPath) != 0)
+    return true;
+  if (!MyGetWindowsDirectory(tempPath))
+    return false;
+  return (Create(tempPath, prefix, resultPath) != 0);
+}
+
+bool CTempFileW::Remove()
+{
+  if (!_mustBeDeleted)
+    return true;
+  _mustBeDeleted = !DeleteFileAlways(_fileName);
+  return !_mustBeDeleted;
+}
+
+#endif
+
+bool CreateTempDirectory(LPCTSTR prefix, CSysString &dirName)
+{
+  /*
+  CSysString prefix = tempPath + prefixChars;
+  CRandom random;
+  random.Init();
+  */
+  for (;;)
+  {
+    CTempFile tempFile;
+    if (!tempFile.Create(prefix, dirName))
+      return false;
+    if (!::DeleteFile(dirName))
+      return false;
+    /*
+    UINT32 randomNumber = random.Generate();
+    TCHAR randomNumberString[32];
+    _stprintf(randomNumberString, _T("%04X"), randomNumber);
+    dirName = prefix + randomNumberString;
+    */
+    if (NFind::DoesFileExist(dirName))
+      continue;
+    if (MyCreateDirectory(dirName))
+      return true;
+    if (::GetLastError() != ERROR_ALREADY_EXISTS)
+      return false;
+  }
+}
+
+bool CTempDirectory::Create(LPCTSTR prefix)
+{
+  Remove();
+  return (_mustBeDeleted = CreateTempDirectory(prefix, _tempDir));
+}
+
+#ifndef _UNICODE
+
+bool CreateTempDirectory(LPCWSTR prefix, UString &dirName)
+{
+  /*
+  CSysString prefix = tempPath + prefixChars;
+  CRandom random;
+  random.Init();
+  */
+  for (;;)
+  {
+    CTempFileW tempFile;
+    if (!tempFile.Create(prefix, dirName))
+      return false;
+    if (!DeleteFileAlways(dirName))
+      return false;
+    /*
+    UINT32 randomNumber = random.Generate();
+    TCHAR randomNumberString[32];
+    _stprintf(randomNumberString, _T("%04X"), randomNumber);
+    dirName = prefix + randomNumberString;
+    */
+    if (NFind::DoesFileExist(dirName))
+      continue;
+    if (MyCreateDirectory(dirName))
+      return true;
+    if (::GetLastError() != ERROR_ALREADY_EXISTS)
+      return false;
+  }
+}
+
+bool CTempDirectoryW::Create(LPCWSTR prefix)
+{
+  Remove();
+  return (_mustBeDeleted = CreateTempDirectory(prefix, _tempDir));
+}
+
+#endif
+
+}}}
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/FileDir.h b/third_party/lzma/v4_65/files/CPP/Windows/FileDir.h
new file mode 100644
index 0000000..279ccdc
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/FileDir.h
@@ -0,0 +1,178 @@
+// Windows/FileDir.h
+
+#ifndef __WINDOWS_FILEDIR_H
+#define __WINDOWS_FILEDIR_H
+
+#include "../Common/MyString.h"
+#include "Defs.h"
+
+namespace NWindows {
+namespace NFile {
+namespace NDirectory {
+
+#ifdef WIN_LONG_PATH
+bool GetLongPaths(LPCWSTR s1, LPCWSTR s2, UString &d1, UString &d2);
+#endif
+
+bool MyGetWindowsDirectory(CSysString &path);
+bool MyGetSystemDirectory(CSysString &path);
+#ifndef _UNICODE
+bool MyGetWindowsDirectory(UString &path);
+bool MyGetSystemDirectory(UString &path);
+#endif
+
+bool SetDirTime(LPCWSTR fileName, const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime);
+
+bool MySetFileAttributes(LPCTSTR fileName, DWORD fileAttributes);
+bool MyMoveFile(LPCTSTR existFileName, LPCTSTR newFileName);
+bool MyRemoveDirectory(LPCTSTR pathName);
+bool MyCreateDirectory(LPCTSTR pathName);
+bool CreateComplexDirectory(LPCTSTR pathName);
+bool DeleteFileAlways(LPCTSTR name);
+bool RemoveDirectoryWithSubItems(const CSysString &path);
+
+#ifndef _UNICODE
+bool MySetFileAttributes(LPCWSTR fileName, DWORD fileAttributes);
+bool MyMoveFile(LPCWSTR existFileName, LPCWSTR newFileName);
+bool MyRemoveDirectory(LPCWSTR pathName);
+bool MyCreateDirectory(LPCWSTR pathName);
+bool CreateComplexDirectory(LPCWSTR pathName);
+bool DeleteFileAlways(LPCWSTR name);
+bool RemoveDirectoryWithSubItems(const UString &path);
+#endif
+
+#ifndef _WIN32_WCE
+bool MyGetShortPathName(LPCTSTR longPath, CSysString &shortPath);
+
+bool MyGetFullPathName(LPCTSTR fileName, CSysString &resultPath,
+    int &fileNamePartStartIndex);
+bool MyGetFullPathName(LPCTSTR fileName, CSysString &resultPath);
+bool GetOnlyName(LPCTSTR fileName, CSysString &resultName);
+bool GetOnlyDirPrefix(LPCTSTR fileName, CSysString &resultName);
+#ifndef _UNICODE
+bool MyGetFullPathName(LPCWSTR fileName, UString &resultPath,
+    int &fileNamePartStartIndex);
+bool MyGetFullPathName(LPCWSTR fileName, UString &resultPath);
+bool GetOnlyName(LPCWSTR fileName, UString &resultName);
+bool GetOnlyDirPrefix(LPCWSTR fileName, UString &resultName);
+#endif
+
+inline bool MySetCurrentDirectory(LPCTSTR path)
+  { return BOOLToBool(::SetCurrentDirectory(path)); }
+bool MyGetCurrentDirectory(CSysString &resultPath);
+#ifndef _UNICODE
+bool MySetCurrentDirectory(LPCWSTR path);
+bool MyGetCurrentDirectory(UString &resultPath);
+#endif
+#endif
+
+bool MySearchPath(LPCTSTR path, LPCTSTR fileName, LPCTSTR extension,
+  CSysString &resultPath, UINT32 &filePart);
+#ifndef _UNICODE
+bool MySearchPath(LPCWSTR path, LPCWSTR fileName, LPCWSTR extension,
+  UString &resultPath, UINT32 &filePart);
+#endif
+
+inline bool MySearchPath(LPCTSTR path, LPCTSTR fileName, LPCTSTR extension,
+  CSysString &resultPath)
+{
+  UINT32 value;
+  return MySearchPath(path, fileName, extension, resultPath, value);
+}
+
+#ifndef _UNICODE
+inline bool MySearchPath(LPCWSTR path, LPCWSTR fileName, LPCWSTR extension,
+  UString &resultPath)
+{
+  UINT32 value;
+  return MySearchPath(path, fileName, extension, resultPath, value);
+}
+#endif
+
+bool MyGetTempPath(CSysString &resultPath);
+#ifndef _UNICODE
+bool MyGetTempPath(UString &resultPath);
+#endif
+
+UINT MyGetTempFileName(LPCTSTR dirPath, LPCTSTR prefix, CSysString &resultPath);
+#ifndef _UNICODE
+UINT MyGetTempFileName(LPCWSTR dirPath, LPCWSTR prefix, UString &resultPath);
+#endif
+
+class CTempFile
+{
+  bool _mustBeDeleted;
+  CSysString _fileName;
+public:
+  CTempFile(): _mustBeDeleted(false) {}
+  ~CTempFile() { Remove(); }
+  void DisableDeleting() { _mustBeDeleted = false; }
+  UINT Create(LPCTSTR dirPath, LPCTSTR prefix, CSysString &resultPath);
+  bool Create(LPCTSTR prefix, CSysString &resultPath);
+  bool Remove();
+};
+
+#ifdef _UNICODE
+typedef CTempFile CTempFileW;
+#else
+class CTempFileW
+{
+  bool _mustBeDeleted;
+  UString _fileName;
+public:
+  CTempFileW(): _mustBeDeleted(false) {}
+  ~CTempFileW() { Remove(); }
+  void DisableDeleting() { _mustBeDeleted = false; }
+  UINT Create(LPCWSTR dirPath, LPCWSTR prefix, UString &resultPath);
+  bool Create(LPCWSTR prefix, UString &resultPath);
+  bool Remove();
+};
+#endif
+
+bool CreateTempDirectory(LPCTSTR prefixChars, CSysString &dirName);
+
+class CTempDirectory
+{
+  bool _mustBeDeleted;
+  CSysString _tempDir;
+public:
+  const CSysString &GetPath() const { return _tempDir; }
+  CTempDirectory(): _mustBeDeleted(false) {}
+  ~CTempDirectory() { Remove();  }
+  bool Create(LPCTSTR prefix) ;
+  bool Remove()
+  {
+    if (!_mustBeDeleted)
+      return true;
+    _mustBeDeleted = !RemoveDirectoryWithSubItems(_tempDir);
+    return (!_mustBeDeleted);
+  }
+  void DisableDeleting() { _mustBeDeleted = false; }
+};
+
+#ifdef _UNICODE
+typedef CTempDirectory CTempDirectoryW;
+#else
+class CTempDirectoryW
+{
+  bool _mustBeDeleted;
+  UString _tempDir;
+public:
+  const UString &GetPath() const { return _tempDir; }
+  CTempDirectoryW(): _mustBeDeleted(false) {}
+  ~CTempDirectoryW() { Remove();  }
+  bool Create(LPCWSTR prefix) ;
+  bool Remove()
+  {
+    if (!_mustBeDeleted)
+      return true;
+    _mustBeDeleted = !RemoveDirectoryWithSubItems(_tempDir);
+    return (!_mustBeDeleted);
+  }
+  void DisableDeleting() { _mustBeDeleted = false; }
+};
+#endif
+
+}}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/FileFind.cpp b/third_party/lzma/v4_65/files/CPP/Windows/FileFind.cpp
new file mode 100644
index 0000000..f33ec96
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/FileFind.cpp
@@ -0,0 +1,402 @@
+// Windows/FileFind.cpp
+
+#include "StdAfx.h"
+
+#include "FileFind.h"
+#ifndef _UNICODE
+#include "../Common/StringConvert.h"
+#endif
+
+#ifndef _UNICODE
+extern bool g_IsNT;
+#endif
+
+namespace NWindows {
+namespace NFile {
+
+#if defined(WIN_LONG_PATH) && defined(_UNICODE)
+#define WIN_LONG_PATH2
+#endif
+
+bool GetLongPath(LPCWSTR fileName, UString &res);
+
+namespace NFind {
+
+static const TCHAR kDot = TEXT('.');
+
+bool CFileInfo::IsDots() const
+{
+  if (!IsDir() || Name.IsEmpty())
+    return false;
+  if (Name[0] != kDot)
+    return false;
+  return Name.Length() == 1 || (Name[1] == kDot && Name.Length() == 2);
+}
+
+#ifndef _UNICODE
+bool CFileInfoW::IsDots() const
+{
+  if (!IsDir() || Name.IsEmpty())
+    return false;
+  if (Name[0] != kDot)
+    return false;
+  return Name.Length() == 1 || (Name[1] == kDot && Name.Length() == 2);
+}
+#endif
+
+static void ConvertWIN32_FIND_DATA_To_FileInfo(const WIN32_FIND_DATA &fd, CFileInfo &fi)
+{
+  fi.Attrib = fd.dwFileAttributes;
+  fi.CTime = fd.ftCreationTime;
+  fi.ATime = fd.ftLastAccessTime;
+  fi.MTime = fd.ftLastWriteTime;
+  fi.Size  = (((UInt64)fd.nFileSizeHigh) << 32) + fd.nFileSizeLow;
+  fi.Name = fd.cFileName;
+  #ifndef _WIN32_WCE
+  fi.ReparseTag = fd.dwReserved0;
+  #else
+  fi.ObjectID = fd.dwOID;
+  #endif
+}
+
+#ifndef _UNICODE
+
+static inline UINT GetCurrentCodePage() { return ::AreFileApisANSI() ? CP_ACP : CP_OEMCP; }
+
+static void ConvertWIN32_FIND_DATA_To_FileInfo(const WIN32_FIND_DATAW &fd, CFileInfoW &fi)
+{
+  fi.Attrib = fd.dwFileAttributes;
+  fi.CTime = fd.ftCreationTime;
+  fi.ATime = fd.ftLastAccessTime;
+  fi.MTime = fd.ftLastWriteTime;
+  fi.Size  = (((UInt64)fd.nFileSizeHigh) << 32) + fd.nFileSizeLow;
+  fi.Name = fd.cFileName;
+  #ifndef _WIN32_WCE
+  fi.ReparseTag = fd.dwReserved0;
+  #else
+  fi.ObjectID = fd.dwOID;
+  #endif
+}
+
+static void ConvertWIN32_FIND_DATA_To_FileInfo(const WIN32_FIND_DATA &fd, CFileInfoW &fi)
+{
+  fi.Attrib = fd.dwFileAttributes;
+  fi.CTime = fd.ftCreationTime;
+  fi.ATime = fd.ftLastAccessTime;
+  fi.MTime = fd.ftLastWriteTime;
+  fi.Size  = (((UInt64)fd.nFileSizeHigh) << 32) + fd.nFileSizeLow;
+  fi.Name = GetUnicodeString(fd.cFileName, GetCurrentCodePage());
+  #ifndef _WIN32_WCE
+  fi.ReparseTag = fd.dwReserved0;
+  #else
+  fi.ObjectID = fd.dwOID;
+  #endif
+}
+#endif
+  
+////////////////////////////////
+// CFindFile
+
+bool CFindFile::Close()
+{
+  if (_handle == INVALID_HANDLE_VALUE)
+    return true;
+  if (!::FindClose(_handle))
+    return false;
+  _handle = INVALID_HANDLE_VALUE;
+  return true;
+}
+
+          
+bool CFindFile::FindFirst(LPCTSTR wildcard, CFileInfo &fileInfo)
+{
+  if (!Close())
+    return false;
+  WIN32_FIND_DATA fd;
+  _handle = ::FindFirstFile(wildcard, &fd);
+  #ifdef WIN_LONG_PATH2
+  if (_handle == INVALID_HANDLE_VALUE)
+  {
+    UString longPath;
+    if (GetLongPath(wildcard, longPath))
+      _handle = ::FindFirstFileW(longPath, &fd);
+  }
+  #endif
+  if (_handle == INVALID_HANDLE_VALUE)
+    return false;
+  ConvertWIN32_FIND_DATA_To_FileInfo(fd, fileInfo);
+  return true;
+}
+
+#ifndef _UNICODE
+bool CFindFile::FindFirst(LPCWSTR wildcard, CFileInfoW &fileInfo)
+{
+  if (!Close())
+    return false;
+  if (g_IsNT)
+  {
+    WIN32_FIND_DATAW fd;
+    _handle = ::FindFirstFileW(wildcard, &fd);
+    #ifdef WIN_LONG_PATH
+    if (_handle == INVALID_HANDLE_VALUE)
+    {
+      UString longPath;
+      if (GetLongPath(wildcard, longPath))
+        _handle = ::FindFirstFileW(longPath, &fd);
+    }
+    #endif
+    if (_handle != INVALID_HANDLE_VALUE)
+      ConvertWIN32_FIND_DATA_To_FileInfo(fd, fileInfo);
+  }
+  else
+  {
+    WIN32_FIND_DATAA fd;
+    _handle = ::FindFirstFileA(UnicodeStringToMultiByte(wildcard,
+        GetCurrentCodePage()), &fd);
+    if (_handle != INVALID_HANDLE_VALUE)
+      ConvertWIN32_FIND_DATA_To_FileInfo(fd, fileInfo);
+  }
+  return (_handle != INVALID_HANDLE_VALUE);
+}
+#endif
+
+bool CFindFile::FindNext(CFileInfo &fileInfo)
+{
+  WIN32_FIND_DATA fd;
+  bool result = BOOLToBool(::FindNextFile(_handle, &fd));
+  if (result)
+    ConvertWIN32_FIND_DATA_To_FileInfo(fd, fileInfo);
+  return result;
+}
+
+#ifndef _UNICODE
+bool CFindFile::FindNext(CFileInfoW &fileInfo)
+{
+  if (g_IsNT)
+  {
+    WIN32_FIND_DATAW fd;
+    if (!::FindNextFileW(_handle, &fd))
+      return false;
+    ConvertWIN32_FIND_DATA_To_FileInfo(fd, fileInfo);
+  }
+  else
+  {
+    WIN32_FIND_DATAA fd;
+    if (!::FindNextFileA(_handle, &fd))
+      return false;
+    ConvertWIN32_FIND_DATA_To_FileInfo(fd, fileInfo);
+  }
+  return true;
+}
+#endif
+
+bool FindFile(LPCTSTR wildcard, CFileInfo &fileInfo)
+{
+  CFindFile finder;
+  return finder.FindFirst(wildcard, fileInfo);
+}
+
+#ifndef _UNICODE
+bool FindFile(LPCWSTR wildcard, CFileInfoW &fileInfo)
+{
+  CFindFile finder;
+  return finder.FindFirst(wildcard, fileInfo);
+}
+#endif
+
+bool DoesFileExist(LPCTSTR name)
+{
+  CFileInfo fileInfo;
+  return FindFile(name, fileInfo);
+}
+
+#ifndef _UNICODE
+bool DoesFileExist(LPCWSTR name)
+{
+  CFileInfoW fileInfo;
+  return FindFile(name, fileInfo);
+}
+#endif
+
+/////////////////////////////////////
+// CEnumerator
+
+bool CEnumerator::NextAny(CFileInfo &fileInfo)
+{
+  if (_findFile.IsHandleAllocated())
+    return _findFile.FindNext(fileInfo);
+  else
+    return _findFile.FindFirst(_wildcard, fileInfo);
+}
+
+bool CEnumerator::Next(CFileInfo &fileInfo)
+{
+  for (;;)
+  {
+    if (!NextAny(fileInfo))
+      return false;
+    if (!fileInfo.IsDots())
+      return true;
+  }
+}
+
+bool CEnumerator::Next(CFileInfo &fileInfo, bool &found)
+{
+  if (Next(fileInfo))
+  {
+    found = true;
+    return true;
+  }
+  found = false;
+  return (::GetLastError() == ERROR_NO_MORE_FILES);
+}
+
+#ifndef _UNICODE
+bool CEnumeratorW::NextAny(CFileInfoW &fileInfo)
+{
+  if (_findFile.IsHandleAllocated())
+    return _findFile.FindNext(fileInfo);
+  else
+    return _findFile.FindFirst(_wildcard, fileInfo);
+}
+
+bool CEnumeratorW::Next(CFileInfoW &fileInfo)
+{
+  for (;;)
+  {
+    if (!NextAny(fileInfo))
+      return false;
+    if (!fileInfo.IsDots())
+      return true;
+  }
+}
+
+bool CEnumeratorW::Next(CFileInfoW &fileInfo, bool &found)
+{
+  if (Next(fileInfo))
+  {
+    found = true;
+    return true;
+  }
+  found = false;
+  return (::GetLastError() == ERROR_NO_MORE_FILES);
+}
+
+#endif
+
+////////////////////////////////
+// CFindChangeNotification
+// FindFirstChangeNotification can return 0. MSDN doesn't tell about it.
+
+bool CFindChangeNotification::Close()
+{
+  if (!IsHandleAllocated())
+    return true;
+  if (!::FindCloseChangeNotification(_handle))
+    return false;
+  _handle = INVALID_HANDLE_VALUE;
+  return true;
+}
+           
+HANDLE CFindChangeNotification::FindFirst(LPCTSTR pathName, bool watchSubtree, DWORD notifyFilter)
+{
+  _handle = ::FindFirstChangeNotification(pathName, BoolToBOOL(watchSubtree), notifyFilter);
+  #ifdef WIN_LONG_PATH2
+  if (!IsHandleAllocated())
+  {
+    UString longPath;
+    if (GetLongPath(pathName, longPath))
+      _handle = ::FindFirstChangeNotificationW(longPath, BoolToBOOL(watchSubtree), notifyFilter);
+  }
+  #endif
+  return _handle;
+}
+
+#ifndef _UNICODE
+HANDLE CFindChangeNotification::FindFirst(LPCWSTR pathName, bool watchSubtree, DWORD notifyFilter)
+{
+  if (!g_IsNT)
+    return FindFirst(UnicodeStringToMultiByte(pathName, GetCurrentCodePage()), watchSubtree, notifyFilter);
+  _handle = ::FindFirstChangeNotificationW(pathName, BoolToBOOL(watchSubtree), notifyFilter);
+  #ifdef WIN_LONG_PATH
+  if (!IsHandleAllocated())
+  {
+    UString longPath;
+    if (GetLongPath(pathName, longPath))
+      _handle = ::FindFirstChangeNotificationW(longPath, BoolToBOOL(watchSubtree), notifyFilter);
+  }
+  #endif
+  return _handle;
+}
+#endif
+
+#ifndef _WIN32_WCE
+bool MyGetLogicalDriveStrings(CSysStringVector &driveStrings)
+{
+  driveStrings.Clear();
+  UINT32 size = GetLogicalDriveStrings(0, NULL);
+  if (size == 0)
+    return false;
+  CSysString buffer;
+  UINT32 newSize = GetLogicalDriveStrings(size, buffer.GetBuffer(size));
+  if (newSize == 0)
+    return false;
+  if (newSize > size)
+    return false;
+  CSysString string;
+  for (UINT32 i = 0; i < newSize; i++)
+  {
+    TCHAR c = buffer[i];
+    if (c == TEXT('\0'))
+    {
+      driveStrings.Add(string);
+      string.Empty();
+    }
+    else
+      string += c;
+  }
+  if (!string.IsEmpty())
+    return false;
+  return true;
+}
+
+#ifndef _UNICODE
+bool MyGetLogicalDriveStrings(UStringVector &driveStrings)
+{
+  driveStrings.Clear();
+  if (g_IsNT)
+  {
+    UINT32 size = GetLogicalDriveStringsW(0, NULL);
+    if (size == 0)
+      return false;
+    UString buffer;
+    UINT32 newSize = GetLogicalDriveStringsW(size, buffer.GetBuffer(size));
+    if (newSize == 0)
+      return false;
+    if (newSize > size)
+      return false;
+    UString string;
+    for (UINT32 i = 0; i < newSize; i++)
+    {
+      WCHAR c = buffer[i];
+      if (c == L'\0')
+      {
+        driveStrings.Add(string);
+        string.Empty();
+      }
+      else
+        string += c;
+    }
+    return string.IsEmpty();
+  }
+  CSysStringVector driveStringsA;
+  bool res = MyGetLogicalDriveStrings(driveStringsA);
+  for (int i = 0; i < driveStringsA.Size(); i++)
+    driveStrings.Add(GetUnicodeString(driveStringsA[i]));
+  return res;
+}
+#endif
+
+#endif
+
+}}}
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/FileFind.h b/third_party/lzma/v4_65/files/CPP/Windows/FileFind.h
new file mode 100644
index 0000000..09b9924
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/FileFind.h
@@ -0,0 +1,153 @@
+// Windows/FileFind.h
+
+#ifndef __WINDOWS_FILEFIND_H
+#define __WINDOWS_FILEFIND_H
+
+#include "../Common/MyString.h"
+#include "../Common/Types.h"
+#include "FileName.h"
+#include "Defs.h"
+
+namespace NWindows {
+namespace NFile {
+namespace NFind {
+
+namespace NAttributes
+{
+  inline bool IsReadOnly(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_READONLY) != 0; }
+  inline bool IsHidden(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_HIDDEN) != 0; }
+  inline bool IsSystem(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_SYSTEM) != 0; }
+  inline bool IsDir(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0; }
+  inline bool IsArchived(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_ARCHIVE) != 0; }
+  inline bool IsCompressed(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_COMPRESSED) != 0; }
+  inline bool IsEncrypted(DWORD attrib) { return (attrib & FILE_ATTRIBUTE_ENCRYPTED) != 0; }
+}
+
+class CFileInfoBase
+{
+  bool MatchesMask(UINT32 mask) const { return ((Attrib & mask) != 0); }
+public:
+  UInt64 Size;
+  FILETIME CTime;
+  FILETIME ATime;
+  FILETIME MTime;
+  DWORD Attrib;
+  
+  #ifndef _WIN32_WCE
+  UINT32 ReparseTag;
+  #else
+  DWORD ObjectID;
+  #endif
+
+  bool IsArchived() const { return MatchesMask(FILE_ATTRIBUTE_ARCHIVE); }
+  bool IsCompressed() const { return MatchesMask(FILE_ATTRIBUTE_COMPRESSED); }
+  bool IsDir() const { return MatchesMask(FILE_ATTRIBUTE_DIRECTORY); }
+  bool IsEncrypted() const { return MatchesMask(FILE_ATTRIBUTE_ENCRYPTED); }
+  bool IsHidden() const { return MatchesMask(FILE_ATTRIBUTE_HIDDEN); }
+  bool IsNormal() const { return MatchesMask(FILE_ATTRIBUTE_NORMAL); }
+  bool IsOffline() const { return MatchesMask(FILE_ATTRIBUTE_OFFLINE); }
+  bool IsReadOnly() const { return MatchesMask(FILE_ATTRIBUTE_READONLY); }
+  bool HasReparsePoint() const { return MatchesMask(FILE_ATTRIBUTE_REPARSE_POINT); }
+  bool IsSparse() const { return MatchesMask(FILE_ATTRIBUTE_SPARSE_FILE); }
+  bool IsSystem() const { return MatchesMask(FILE_ATTRIBUTE_SYSTEM); }
+  bool IsTemporary() const { return MatchesMask(FILE_ATTRIBUTE_TEMPORARY); }
+};
+
+class CFileInfo: public CFileInfoBase
+{
+public:
+  CSysString Name;
+  bool IsDots() const;
+};
+
+#ifdef _UNICODE
+typedef CFileInfo CFileInfoW;
+#else
+class CFileInfoW: public CFileInfoBase
+{
+public:
+  UString Name;
+  bool IsDots() const;
+};
+#endif
+
+class CFindFile
+{
+  friend class CEnumerator;
+  HANDLE _handle;
+public:
+  bool IsHandleAllocated() const { return _handle != INVALID_HANDLE_VALUE; }
+  CFindFile(): _handle(INVALID_HANDLE_VALUE) {}
+  ~CFindFile() {  Close(); }
+  bool FindFirst(LPCTSTR wildcard, CFileInfo &fileInfo);
+  bool FindNext(CFileInfo &fileInfo);
+  #ifndef _UNICODE
+  bool FindFirst(LPCWSTR wildcard, CFileInfoW &fileInfo);
+  bool FindNext(CFileInfoW &fileInfo);
+  #endif
+  bool Close();
+};
+
+bool FindFile(LPCTSTR wildcard, CFileInfo &fileInfo);
+
+bool DoesFileExist(LPCTSTR name);
+#ifndef _UNICODE
+bool FindFile(LPCWSTR wildcard, CFileInfoW &fileInfo);
+bool DoesFileExist(LPCWSTR name);
+#endif
+
+class CEnumerator
+{
+  CFindFile _findFile;
+  CSysString _wildcard;
+  bool NextAny(CFileInfo &fileInfo);
+public:
+  CEnumerator(): _wildcard(NName::kAnyStringWildcard) {}
+  CEnumerator(const CSysString &wildcard): _wildcard(wildcard) {}
+  bool Next(CFileInfo &fileInfo);
+  bool Next(CFileInfo &fileInfo, bool &found);
+};
+
+#ifdef _UNICODE
+typedef CEnumerator CEnumeratorW;
+#else
+class CEnumeratorW
+{
+  CFindFile _findFile;
+  UString _wildcard;
+  bool NextAny(CFileInfoW &fileInfo);
+public:
+  CEnumeratorW(): _wildcard(NName::kAnyStringWildcard) {}
+  CEnumeratorW(const UString &wildcard): _wildcard(wildcard) {}
+  bool Next(CFileInfoW &fileInfo);
+  bool Next(CFileInfoW &fileInfo, bool &found);
+};
+#endif
+
+class CFindChangeNotification
+{
+  HANDLE _handle;
+public:
+  operator HANDLE () { return _handle; }
+  bool IsHandleAllocated() const { return _handle != INVALID_HANDLE_VALUE && _handle != 0; }
+  CFindChangeNotification(): _handle(INVALID_HANDLE_VALUE) {}
+  ~CFindChangeNotification() { Close(); }
+  bool Close();
+  HANDLE FindFirst(LPCTSTR pathName, bool watchSubtree, DWORD notifyFilter);
+  #ifndef _UNICODE
+  HANDLE FindFirst(LPCWSTR pathName, bool watchSubtree, DWORD notifyFilter);
+  #endif
+  bool FindNext() { return BOOLToBool(::FindNextChangeNotification(_handle)); }
+};
+
+#ifndef _WIN32_WCE
+bool MyGetLogicalDriveStrings(CSysStringVector &driveStrings);
+#ifndef _UNICODE
+bool MyGetLogicalDriveStrings(UStringVector &driveStrings);
+#endif
+#endif
+
+}}}
+
+#endif
+
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/FileIO.cpp b/third_party/lzma/v4_65/files/CPP/Windows/FileIO.cpp
new file mode 100644
index 0000000..5646d0a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/FileIO.cpp
@@ -0,0 +1,317 @@
+// Windows/FileIO.cpp
+
+#include "StdAfx.h"
+
+#include "FileIO.h"
+#include "Defs.h"
+#ifdef WIN_LONG_PATH
+#include "../Common/MyString.h"
+#endif
+#ifndef _UNICODE
+#include "../Common/StringConvert.h"
+#endif
+
+#ifndef _UNICODE
+extern bool g_IsNT;
+#endif
+
+namespace NWindows {
+namespace NFile {
+
+#if defined(WIN_LONG_PATH) && defined(_UNICODE)
+#define WIN_LONG_PATH2
+#endif
+
+#ifdef WIN_LONG_PATH
+bool GetLongPathBase(LPCWSTR s, UString &res)
+{
+  res.Empty();
+  int len = MyStringLen(s);
+  wchar_t c = s[0];
+  if (len < 1 || c == L'\\' || c == L'.' && (len == 1 || len == 2 && s[1] == L'.'))
+    return true;
+  UString curDir;
+  bool isAbs = false;
+  if (len > 3)
+    isAbs = (s[1] == L':' && s[2] == L'\\' && (c >= L'a' && c <= L'z' || c >= L'A' && c <= L'Z'));
+
+  if (!isAbs)
+    {
+      DWORD needLength = ::GetCurrentDirectoryW(MAX_PATH + 1, curDir.GetBuffer(MAX_PATH + 1));
+      curDir.ReleaseBuffer();
+      if (needLength == 0 || needLength > MAX_PATH)
+        return false;
+      if (curDir[curDir.Length() - 1] != L'\\')
+        curDir += L'\\';
+    }
+  res = UString(L"\\\\?\\") + curDir + s;
+  return true;
+}
+
+bool GetLongPath(LPCWSTR path, UString &longPath)
+{
+  if (GetLongPathBase(path, longPath))
+    return !longPath.IsEmpty();
+  return false;
+}
+#endif
+
+namespace NIO {
+
+CFileBase::~CFileBase() { Close(); }
+
+bool CFileBase::Create(LPCTSTR fileName, DWORD desiredAccess,
+    DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
+{
+  if (!Close())
+    return false;
+  _handle = ::CreateFile(fileName, desiredAccess, shareMode,
+      (LPSECURITY_ATTRIBUTES)NULL, creationDisposition,
+      flagsAndAttributes, (HANDLE)NULL);
+  #ifdef WIN_LONG_PATH2
+  if (_handle == INVALID_HANDLE_VALUE)
+  {
+    UString longPath;
+    if (GetLongPath(fileName, longPath))
+      _handle = ::CreateFileW(longPath, desiredAccess, shareMode,
+        (LPSECURITY_ATTRIBUTES)NULL, creationDisposition,
+        flagsAndAttributes, (HANDLE)NULL);
+  }
+  #endif
+  return (_handle != INVALID_HANDLE_VALUE);
+}
+
+#ifndef _UNICODE
+bool CFileBase::Create(LPCWSTR fileName, DWORD desiredAccess,
+    DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
+{
+  if (!g_IsNT)
+    return Create(UnicodeStringToMultiByte(fileName, ::AreFileApisANSI() ? CP_ACP : CP_OEMCP),
+      desiredAccess, shareMode, creationDisposition, flagsAndAttributes);
+  if (!Close())
+    return false;
+  _handle = ::CreateFileW(fileName, desiredAccess, shareMode,
+    (LPSECURITY_ATTRIBUTES)NULL, creationDisposition,
+    flagsAndAttributes, (HANDLE)NULL);
+  #ifdef WIN_LONG_PATH
+  if (_handle == INVALID_HANDLE_VALUE)
+  {
+    UString longPath;
+    if (GetLongPath(fileName, longPath))
+      _handle = ::CreateFileW(longPath, desiredAccess, shareMode,
+        (LPSECURITY_ATTRIBUTES)NULL, creationDisposition,
+        flagsAndAttributes, (HANDLE)NULL);
+  }
+  #endif
+  return (_handle != INVALID_HANDLE_VALUE);
+}
+#endif
+
+bool CFileBase::Close()
+{
+  if (_handle == INVALID_HANDLE_VALUE)
+    return true;
+  if (!::CloseHandle(_handle))
+    return false;
+  _handle = INVALID_HANDLE_VALUE;
+  return true;
+}
+
+bool CFileBase::GetPosition(UInt64 &position) const
+{
+  return Seek(0, FILE_CURRENT, position);
+}
+
+bool CFileBase::GetLength(UInt64 &length) const
+{
+  DWORD sizeHigh;
+  DWORD sizeLow = ::GetFileSize(_handle, &sizeHigh);
+  if (sizeLow == 0xFFFFFFFF)
+    if (::GetLastError() != NO_ERROR)
+      return false;
+  length = (((UInt64)sizeHigh) << 32) + sizeLow;
+  return true;
+}
+
+bool CFileBase::Seek(Int64 distanceToMove, DWORD moveMethod, UInt64 &newPosition) const
+{
+  LARGE_INTEGER value;
+  value.QuadPart = distanceToMove;
+  value.LowPart = ::SetFilePointer(_handle, value.LowPart, &value.HighPart, moveMethod);
+  if (value.LowPart == 0xFFFFFFFF)
+    if (::GetLastError() != NO_ERROR)
+      return false;
+  newPosition = value.QuadPart;
+  return true;
+}
+
+bool CFileBase::Seek(UInt64 position, UInt64 &newPosition)
+{
+  return Seek(position, FILE_BEGIN, newPosition);
+}
+
+bool CFileBase::SeekToBegin()
+{
+  UInt64 newPosition;
+  return Seek(0, newPosition);
+}
+
+bool CFileBase::SeekToEnd(UInt64 &newPosition)
+{
+  return Seek(0, FILE_END, newPosition);
+}
+
+bool CFileBase::GetFileInformation(CByHandleFileInfo &fileInfo) const
+{
+  BY_HANDLE_FILE_INFORMATION winFileInfo;
+  if (!::GetFileInformationByHandle(_handle, &winFileInfo))
+    return false;
+  fileInfo.Attributes = winFileInfo.dwFileAttributes;
+  fileInfo.CTime = winFileInfo.ftCreationTime;
+  fileInfo.ATime = winFileInfo.ftLastAccessTime;
+  fileInfo.MTime = winFileInfo.ftLastWriteTime;
+  fileInfo.VolumeSerialNumber = winFileInfo.dwFileAttributes;
+  fileInfo.Size = (((UInt64)winFileInfo.nFileSizeHigh) << 32) +  winFileInfo.nFileSizeLow;
+  fileInfo.NumberOfLinks = winFileInfo.nNumberOfLinks;
+  fileInfo.FileIndex = (((UInt64)winFileInfo.nFileIndexHigh) << 32) + winFileInfo.nFileIndexLow;
+  return true;
+}
+
+/////////////////////////
+// CInFile
+
+bool CInFile::Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
+  { return Create(fileName, GENERIC_READ, shareMode, creationDisposition, flagsAndAttributes); }
+
+bool CInFile::OpenShared(LPCTSTR fileName, bool shareForWrite)
+{ return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); }
+
+bool CInFile::Open(LPCTSTR fileName)
+  { return OpenShared(fileName, false); }
+
+#ifndef _UNICODE
+bool CInFile::Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
+  { return Create(fileName, GENERIC_READ, shareMode, creationDisposition, flagsAndAttributes); }
+
+bool CInFile::OpenShared(LPCWSTR fileName, bool shareForWrite)
+{ return Open(fileName, FILE_SHARE_READ | (shareForWrite ? FILE_SHARE_WRITE : 0), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL); }
+
+bool CInFile::Open(LPCWSTR fileName)
+  { return OpenShared(fileName, false); }
+#endif
+
+// ReadFile and WriteFile functions in Windows have BUG:
+// If you Read or Write 64MB or more (probably min_failure_size = 64MB - 32KB + 1)
+// from/to Network file, it returns ERROR_NO_SYSTEM_RESOURCES
+// (Insufficient system resources exist to complete the requested service).
+
+// Probably in some version of Windows there are problems with other sizes:
+// for 32 MB (maybe also for 16 MB).
+// And message can be "Network connection was lost"
+
+static UInt32 kChunkSizeMax = (1 << 22);
+
+bool CInFile::ReadPart(void *data, UInt32 size, UInt32 &processedSize)
+{
+  if (size > kChunkSizeMax)
+    size = kChunkSizeMax;
+  DWORD processedLoc = 0;
+  bool res = BOOLToBool(::ReadFile(_handle, data, size, &processedLoc, NULL));
+  processedSize = (UInt32)processedLoc;
+  return res;
+}
+
+bool CInFile::Read(void *data, UInt32 size, UInt32 &processedSize)
+{
+  processedSize = 0;
+  do
+  {
+    UInt32 processedLoc = 0;
+    bool res = ReadPart(data, size, processedLoc);
+    processedSize += processedLoc;
+    if (!res)
+      return false;
+    if (processedLoc == 0)
+      return true;
+    data = (void *)((unsigned char *)data + processedLoc);
+    size -= processedLoc;
+  }
+  while (size > 0);
+  return true;
+}
+
+/////////////////////////
+// COutFile
+
+bool COutFile::Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
+  { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode, creationDisposition, flagsAndAttributes); }
+
+static inline DWORD GetCreationDisposition(bool createAlways)
+  { return createAlways? CREATE_ALWAYS: CREATE_NEW; }
+
+bool COutFile::Open(LPCTSTR fileName, DWORD creationDisposition)
+  { return Open(fileName, FILE_SHARE_READ, creationDisposition, FILE_ATTRIBUTE_NORMAL); }
+
+bool COutFile::Create(LPCTSTR fileName, bool createAlways)
+  { return Open(fileName, GetCreationDisposition(createAlways)); }
+
+#ifndef _UNICODE
+
+bool COutFile::Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes)
+  { return CFileBase::Create(fileName, GENERIC_WRITE, shareMode,      creationDisposition, flagsAndAttributes); }
+
+bool COutFile::Open(LPCWSTR fileName, DWORD creationDisposition)
+  { return Open(fileName, FILE_SHARE_READ,  creationDisposition, FILE_ATTRIBUTE_NORMAL); }
+
+bool COutFile::Create(LPCWSTR fileName, bool createAlways)
+  { return Open(fileName, GetCreationDisposition(createAlways)); }
+
+#endif
+
+bool COutFile::SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime)
+  { return BOOLToBool(::SetFileTime(_handle, cTime, aTime, mTime)); }
+
+bool COutFile::SetMTime(const FILETIME *mTime) {  return SetTime(NULL, NULL, mTime); }
+
+bool COutFile::WritePart(const void *data, UInt32 size, UInt32 &processedSize)
+{
+  if (size > kChunkSizeMax)
+    size = kChunkSizeMax;
+  DWORD processedLoc = 0;
+  bool res = BOOLToBool(::WriteFile(_handle, data, size, &processedLoc, NULL));
+  processedSize = (UInt32)processedLoc;
+  return res;
+}
+
+bool COutFile::Write(const void *data, UInt32 size, UInt32 &processedSize)
+{
+  processedSize = 0;
+  do
+  {
+    UInt32 processedLoc = 0;
+    bool res = WritePart(data, size, processedLoc);
+    processedSize += processedLoc;
+    if (!res)
+      return false;
+    if (processedLoc == 0)
+      return true;
+    data = (const void *)((const unsigned char *)data + processedLoc);
+    size -= processedLoc;
+  }
+  while (size > 0);
+  return true;
+}
+
+bool COutFile::SetEndOfFile() { return BOOLToBool(::SetEndOfFile(_handle)); }
+
+bool COutFile::SetLength(UInt64 length)
+{
+  UInt64 newPosition;
+  if (!Seek(length, newPosition))
+    return false;
+  if (newPosition != length)
+    return false;
+  return SetEndOfFile();
+}
+
+}}}
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/FileIO.h b/third_party/lzma/v4_65/files/CPP/Windows/FileIO.h
new file mode 100644
index 0000000..f1e600e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/FileIO.h
@@ -0,0 +1,99 @@
+// Windows/FileIO.h
+
+#ifndef __WINDOWS_FILEIO_H
+#define __WINDOWS_FILEIO_H
+
+#include "../Common/Types.h"
+
+namespace NWindows {
+namespace NFile {
+namespace NIO {
+
+struct CByHandleFileInfo
+{
+  DWORD    Attributes;
+  FILETIME CTime;
+  FILETIME ATime;
+  FILETIME MTime;
+  DWORD    VolumeSerialNumber;
+  UInt64   Size;
+  DWORD    NumberOfLinks;
+  UInt64   FileIndex;
+};
+
+class CFileBase
+{
+protected:
+  HANDLE _handle;
+  bool Create(LPCTSTR fileName, DWORD desiredAccess,
+      DWORD shareMode, DWORD creationDisposition,  DWORD flagsAndAttributes);
+  #ifndef _UNICODE
+  bool Create(LPCWSTR fileName, DWORD desiredAccess,
+      DWORD shareMode, DWORD creationDisposition,  DWORD flagsAndAttributes);
+  #endif
+
+public:
+  CFileBase(): _handle(INVALID_HANDLE_VALUE){};
+  ~CFileBase();
+
+  bool Close();
+
+  bool GetPosition(UInt64 &position) const;
+  bool GetLength(UInt64 &length) const;
+
+  bool Seek(Int64 distanceToMove, DWORD moveMethod, UInt64 &newPosition) const;
+  bool Seek(UInt64 position, UInt64 &newPosition);
+  bool SeekToBegin();
+  bool SeekToEnd(UInt64 &newPosition);
+  
+  bool GetFileInformation(CByHandleFileInfo &fileInfo) const;
+};
+
+class CInFile: public CFileBase
+{
+public:
+  bool Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes);
+  bool OpenShared(LPCTSTR fileName, bool shareForWrite);
+  bool Open(LPCTSTR fileName);
+  #ifndef _UNICODE
+  bool Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes);
+  bool OpenShared(LPCWSTR fileName, bool shareForWrite);
+  bool Open(LPCWSTR fileName);
+  #endif
+  bool ReadPart(void *data, UInt32 size, UInt32 &processedSize);
+  bool Read(void *data, UInt32 size, UInt32 &processedSize);
+};
+
+class COutFile: public CFileBase
+{
+  // DWORD m_CreationDisposition;
+public:
+  // COutFile(): m_CreationDisposition(CREATE_NEW){};
+  bool Open(LPCTSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes);
+  bool Open(LPCTSTR fileName, DWORD creationDisposition);
+  bool Create(LPCTSTR fileName, bool createAlways);
+
+  #ifndef _UNICODE
+  bool Open(LPCWSTR fileName, DWORD shareMode, DWORD creationDisposition, DWORD flagsAndAttributes);
+  bool Open(LPCWSTR fileName, DWORD creationDisposition);
+  bool Create(LPCWSTR fileName, bool createAlways);
+  #endif
+
+  /*
+  void SetOpenCreationDisposition(DWORD creationDisposition)
+    { m_CreationDisposition = creationDisposition; }
+  void SetOpenCreationDispositionCreateAlways()
+    { m_CreationDisposition = CREATE_ALWAYS; }
+  */
+
+  bool SetTime(const FILETIME *cTime, const FILETIME *aTime, const FILETIME *mTime);
+  bool SetMTime(const FILETIME *mTime);
+  bool WritePart(const void *data, UInt32 size, UInt32 &processedSize);
+  bool Write(const void *data, UInt32 size, UInt32 &processedSize);
+  bool SetEndOfFile();
+  bool SetLength(UInt64 length);
+};
+
+}}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/FileMapping.cpp b/third_party/lzma/v4_65/files/CPP/Windows/FileMapping.cpp
new file mode 100644
index 0000000..d884afb
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/FileMapping.cpp
@@ -0,0 +1,14 @@
+// Windows/FileMapping.cpp
+
+#include "StdAfx.h"
+
+#include "Windows/FileMapping.h"
+
+namespace NWindows {
+namespace NFile {
+namespace NMapping {
+
+
+
+
+}}}
\ No newline at end of file
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/FileMapping.h b/third_party/lzma/v4_65/files/CPP/Windows/FileMapping.h
new file mode 100644
index 0000000..25f5a51
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/FileMapping.h
@@ -0,0 +1,50 @@
+// Windows/FileMapping.h
+
+#ifndef __WINDOWS_FILEMAPPING_H
+#define __WINDOWS_FILEMAPPING_H
+
+#include "Windows/Handle.h"
+#include "Windows/Defs.h"
+
+namespace NWindows {
+// namespace NFile {
+// namespace NMapping {
+
+class CFileMapping: public CHandle
+{
+public:
+  bool Create(HANDLE file, LPSECURITY_ATTRIBUTES attributes,
+    DWORD protect, UINT64 maximumSize, LPCTSTR name)
+  {
+    _handle = ::CreateFileMapping(file, attributes,
+      protect, DWORD(maximumSize >> 32), DWORD(maximumSize), name);
+    return (_handle != NULL);
+  }
+
+  bool Open(DWORD desiredAccess, bool inheritHandle, LPCTSTR name)
+  {
+    _handle = ::OpenFileMapping(desiredAccess, BoolToBOOL(inheritHandle), name);
+    return (_handle != NULL);
+  }
+
+  LPVOID MapViewOfFile(DWORD desiredAccess, UINT64 fileOffset,
+      SIZE_T numberOfBytesToMap)
+  {
+    return ::MapViewOfFile(_handle, desiredAccess,
+        DWORD(fileOffset >> 32), DWORD(fileOffset), numberOfBytesToMap);
+  }
+
+  LPVOID MapViewOfFileEx(DWORD desiredAccess, UINT64 fileOffset,
+      SIZE_T numberOfBytesToMap, LPVOID baseAddress)
+  {
+    return ::MapViewOfFileEx(_handle, desiredAccess,
+      DWORD(fileOffset >> 32), DWORD(fileOffset),
+      numberOfBytesToMap, baseAddress);
+  }
+  
+
+};
+
+}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/FileName.cpp b/third_party/lzma/v4_65/files/CPP/Windows/FileName.cpp
new file mode 100644
index 0000000..8443a4a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/FileName.cpp
@@ -0,0 +1,50 @@
+// Windows/FileName.cpp
+
+#include "StdAfx.h"
+
+#include "Windows/FileName.h"
+#include "Common/Wildcard.h"
+
+namespace NWindows {
+namespace NFile {
+namespace NName {
+
+void NormalizeDirPathPrefix(CSysString &dirPath)
+{
+  if (dirPath.IsEmpty())
+    return;
+  if (dirPath.ReverseFind(kDirDelimiter) != dirPath.Length() - 1)
+    dirPath += kDirDelimiter;
+}
+
+#ifndef _UNICODE
+void NormalizeDirPathPrefix(UString &dirPath)
+{
+  if (dirPath.IsEmpty())
+    return;
+  if (dirPath.ReverseFind(wchar_t(kDirDelimiter)) != dirPath.Length() - 1)
+    dirPath += wchar_t(kDirDelimiter);
+}
+#endif
+
+const wchar_t kExtensionDelimiter = L'.';
+
+void SplitNameToPureNameAndExtension(const UString &fullName,
+    UString &pureName, UString &extensionDelimiter, UString &extension)
+{
+  int index = fullName.ReverseFind(kExtensionDelimiter);
+  if (index < 0)
+  {
+    pureName = fullName;
+    extensionDelimiter.Empty();
+    extension.Empty();
+  }
+  else
+  {
+    pureName = fullName.Left(index);
+    extensionDelimiter = kExtensionDelimiter;
+    extension = fullName.Mid(index + 1);
+  }
+}
+
+}}}
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/FileName.h b/third_party/lzma/v4_65/files/CPP/Windows/FileName.h
new file mode 100644
index 0000000..20786e0
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/FileName.h
@@ -0,0 +1,25 @@
+// Windows/FileName.h
+
+#ifndef __WINDOWS_FILENAME_H
+#define __WINDOWS_FILENAME_H
+
+#include "../Common/MyString.h"
+
+namespace NWindows {
+namespace NFile {
+namespace NName {
+
+const TCHAR kDirDelimiter = CHAR_PATH_SEPARATOR;
+const TCHAR kAnyStringWildcard = '*';
+
+void NormalizeDirPathPrefix(CSysString &dirPath); // ensures that it ended with '\\'
+#ifndef _UNICODE
+void NormalizeDirPathPrefix(UString &dirPath); // ensures that it ended with '\\'
+#endif
+
+void SplitNameToPureNameAndExtension(const UString &fullName,
+    UString &pureName, UString &extensionDelimiter, UString &extension);
+
+}}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/Handle.h b/third_party/lzma/v4_65/files/CPP/Windows/Handle.h
new file mode 100644
index 0000000..0791b4a
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/Handle.h
@@ -0,0 +1,37 @@
+// Windows/Handle.h
+
+#ifndef __WINDOWS_HANDLE_H
+#define __WINDOWS_HANDLE_H
+
+namespace NWindows {
+
+class CHandle
+{
+protected:
+  HANDLE _handle;
+public:
+  operator HANDLE() { return _handle; }
+  CHandle(): _handle(NULL) {}
+  ~CHandle() { Close(); }
+  bool Close()
+  {
+    if (_handle == NULL)
+      return true;
+    if (!::CloseHandle(_handle))
+      return false;
+    _handle = NULL;
+    return true;
+  }
+  void Attach(HANDLE handle)
+    { _handle = handle; }
+  HANDLE Detach()
+  {
+    HANDLE handle = _handle;
+    _handle = NULL;
+    return handle;
+  }
+};
+
+}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/MemoryLock.cpp b/third_party/lzma/v4_65/files/CPP/Windows/MemoryLock.cpp
new file mode 100644
index 0000000..284c832
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/MemoryLock.cpp
@@ -0,0 +1,78 @@
+// Common/MemoryLock.cpp
+
+#include "StdAfx.h"
+
+namespace NWindows {
+namespace NSecurity {
+
+#ifndef _UNICODE
+typedef BOOL (WINAPI * OpenProcessTokenP)(HANDLE ProcessHandle, DWORD DesiredAccess, PHANDLE TokenHandle);
+typedef BOOL (WINAPI * LookupPrivilegeValueP)(LPCTSTR lpSystemName, LPCTSTR lpName, PLUID  lpLuid);
+typedef BOOL (WINAPI * AdjustTokenPrivilegesP)(HANDLE TokenHandle, BOOL DisableAllPrivileges,
+    PTOKEN_PRIVILEGES NewState, DWORD BufferLength, PTOKEN_PRIVILEGES PreviousState,PDWORD ReturnLength);
+#endif
+
+#ifdef _UNICODE
+bool EnableLockMemoryPrivilege(
+#else
+static bool EnableLockMemoryPrivilege2(HMODULE hModule,
+#endif
+bool enable)
+{
+  #ifndef _UNICODE
+  if (hModule == NULL)
+    return false;
+  OpenProcessTokenP openProcessToken = (OpenProcessTokenP)GetProcAddress(hModule, "OpenProcessToken");
+  LookupPrivilegeValueP lookupPrivilegeValue = (LookupPrivilegeValueP)GetProcAddress(hModule, "LookupPrivilegeValueA" );
+  AdjustTokenPrivilegesP adjustTokenPrivileges = (AdjustTokenPrivilegesP)GetProcAddress(hModule, "AdjustTokenPrivileges");
+  if (openProcessToken == NULL || adjustTokenPrivileges == NULL || lookupPrivilegeValue == NULL)
+    return false;
+  #endif
+
+  HANDLE token;
+  if (!
+    #ifdef _UNICODE
+    ::OpenProcessToken
+    #else
+    openProcessToken
+    #endif
+    (::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))
+    return false;
+  TOKEN_PRIVILEGES tp;
+  bool res = false;
+  if (
+    #ifdef _UNICODE
+    ::LookupPrivilegeValue
+    #else
+    lookupPrivilegeValue
+    #endif
+    (NULL, SE_LOCK_MEMORY_NAME, &(tp.Privileges[0].Luid)))
+  {
+    tp.PrivilegeCount = 1;
+    tp.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED: 0;
+    if (
+      #ifdef _UNICODE
+      ::AdjustTokenPrivileges
+      #else
+      adjustTokenPrivileges
+      #endif
+      (token, FALSE, &tp, 0, NULL, NULL))
+      res = (GetLastError() == ERROR_SUCCESS);
+  }
+  ::CloseHandle(token);
+  return res;
+}
+
+#ifndef _UNICODE
+bool EnableLockMemoryPrivilege(bool enable)
+{
+  HMODULE hModule = LoadLibrary(TEXT("Advapi32.dll"));
+  if (hModule == NULL)
+    return false;
+  bool res = EnableLockMemoryPrivilege2(hModule, enable);
+  ::FreeLibrary(hModule);
+  return res;
+}
+#endif
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/MemoryLock.h b/third_party/lzma/v4_65/files/CPP/Windows/MemoryLock.h
new file mode 100644
index 0000000..321024b
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/MemoryLock.h
@@ -0,0 +1,13 @@
+// Windows/MemoryLock.h
+
+#ifndef __WINDOWS_MEMORYLOCK_H
+#define __WINDOWS_MEMORYLOCK_H
+
+namespace NWindows {
+namespace NSecurity {
+
+bool EnableLockMemoryPrivilege(bool enable = true);
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/PropVariant.cpp b/third_party/lzma/v4_65/files/CPP/Windows/PropVariant.cpp
new file mode 100644
index 0000000..ae2e6cd
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/PropVariant.cpp
@@ -0,0 +1,312 @@
+// Windows/PropVariant.cpp
+
+#include "StdAfx.h"
+
+#include "PropVariant.h"
+
+#include "../Common/Defs.h"
+
+namespace NWindows {
+namespace NCOM {
+
+CPropVariant::CPropVariant(const PROPVARIANT& varSrc)
+{
+  vt = VT_EMPTY;
+  InternalCopy(&varSrc);
+}
+
+CPropVariant::CPropVariant(const CPropVariant& varSrc)
+{
+  vt = VT_EMPTY;
+  InternalCopy(&varSrc);
+}
+
+CPropVariant::CPropVariant(BSTR bstrSrc)
+{
+  vt = VT_EMPTY;
+  *this = bstrSrc;
+}
+
+CPropVariant::CPropVariant(LPCOLESTR lpszSrc)
+{
+  vt = VT_EMPTY;
+  *this = lpszSrc;
+}
+
+CPropVariant& CPropVariant::operator=(const CPropVariant& varSrc)
+{
+  InternalCopy(&varSrc);
+  return *this;
+}
+CPropVariant& CPropVariant::operator=(const PROPVARIANT& varSrc)
+{
+  InternalCopy(&varSrc);
+  return *this;
+}
+
+CPropVariant& CPropVariant::operator=(BSTR bstrSrc)
+{
+  *this = (LPCOLESTR)bstrSrc;
+  return *this;
+}
+
+CPropVariant& CPropVariant::operator=(LPCOLESTR lpszSrc)
+{
+  InternalClear();
+  vt = VT_BSTR;
+  wReserved1 = 0;
+  bstrVal = ::SysAllocString(lpszSrc);
+  if (bstrVal == NULL && lpszSrc != NULL)
+  {
+    vt = VT_ERROR;
+    scode = E_OUTOFMEMORY;
+  }
+  return *this;
+}
+
+
+CPropVariant& CPropVariant::operator=(bool bSrc)
+{
+  if (vt != VT_BOOL)
+  {
+    InternalClear();
+    vt = VT_BOOL;
+  }
+  boolVal = bSrc ? VARIANT_TRUE : VARIANT_FALSE;
+  return *this;
+}
+
+CPropVariant& CPropVariant::operator=(UInt32 value)
+{
+  if (vt != VT_UI4)
+  {
+    InternalClear();
+    vt = VT_UI4;
+  }
+  ulVal = value;
+  return *this;
+}
+
+CPropVariant& CPropVariant::operator=(UInt64 value)
+{
+  if (vt != VT_UI8)
+  {
+    InternalClear();
+    vt = VT_UI8;
+  }
+  uhVal.QuadPart = value;
+  return *this;
+}
+
+CPropVariant& CPropVariant::operator=(const FILETIME &value)
+{
+  if (vt != VT_FILETIME)
+  {
+    InternalClear();
+    vt = VT_FILETIME;
+  }
+  filetime = value;
+  return *this;
+}
+
+CPropVariant& CPropVariant::operator=(Int32 value)
+{
+  if (vt != VT_I4)
+  {
+    InternalClear();
+    vt = VT_I4;
+  }
+  lVal = value;
+  
+  return *this;
+}
+
+CPropVariant& CPropVariant::operator=(Byte value)
+{
+  if (vt != VT_UI1)
+  {
+    InternalClear();
+    vt = VT_UI1;
+  }
+  bVal = value;
+  return *this;
+}
+
+CPropVariant& CPropVariant::operator=(Int16 value)
+{
+  if (vt != VT_I2)
+  {
+    InternalClear();
+    vt = VT_I2;
+  }
+  iVal = value;
+  return *this;
+}
+
+/*
+CPropVariant& CPropVariant::operator=(LONG value)
+{
+  if (vt != VT_I4)
+  {
+    InternalClear();
+    vt = VT_I4;
+  }
+  lVal = value;
+  return *this;
+}
+*/
+
+static HRESULT MyPropVariantClear(PROPVARIANT *propVariant)
+{
+  switch(propVariant->vt)
+  {
+    case VT_UI1:
+    case VT_I1:
+    case VT_I2:
+    case VT_UI2:
+    case VT_BOOL:
+    case VT_I4:
+    case VT_UI4:
+    case VT_R4:
+    case VT_INT:
+    case VT_UINT:
+    case VT_ERROR:
+    case VT_FILETIME:
+    case VT_UI8:
+    case VT_R8:
+    case VT_CY:
+    case VT_DATE:
+      propVariant->vt = VT_EMPTY;
+      propVariant->wReserved1 = 0;
+      return S_OK;
+  }
+  return ::VariantClear((VARIANTARG *)propVariant);
+}
+
+HRESULT CPropVariant::Clear()
+{
+  return MyPropVariantClear(this);
+}
+
+HRESULT CPropVariant::Copy(const PROPVARIANT* pSrc)
+{
+  ::VariantClear((tagVARIANT *)this);
+  switch(pSrc->vt)
+  {
+    case VT_UI1:
+    case VT_I1:
+    case VT_I2:
+    case VT_UI2:
+    case VT_BOOL:
+    case VT_I4:
+    case VT_UI4:
+    case VT_R4:
+    case VT_INT:
+    case VT_UINT:
+    case VT_ERROR:
+    case VT_FILETIME:
+    case VT_UI8:
+    case VT_R8:
+    case VT_CY:
+    case VT_DATE:
+      memmove((PROPVARIANT*)this, pSrc, sizeof(PROPVARIANT));
+      return S_OK;
+  }
+  return ::VariantCopy((tagVARIANT *)this, (tagVARIANT *)(pSrc));
+}
+
+
+HRESULT CPropVariant::Attach(PROPVARIANT* pSrc)
+{
+  HRESULT hr = Clear();
+  if (FAILED(hr))
+    return hr;
+  memcpy(this, pSrc, sizeof(PROPVARIANT));
+  pSrc->vt = VT_EMPTY;
+  return S_OK;
+}
+
+HRESULT CPropVariant::Detach(PROPVARIANT* pDest)
+{
+  HRESULT hr = MyPropVariantClear(pDest);
+  if (FAILED(hr))
+    return hr;
+  memcpy(pDest, this, sizeof(PROPVARIANT));
+  vt = VT_EMPTY;
+  return S_OK;
+}
+
+HRESULT CPropVariant::InternalClear()
+{
+  HRESULT hr = Clear();
+  if (FAILED(hr))
+  {
+    vt = VT_ERROR;
+    scode = hr;
+  }
+  return hr;
+}
+
+void CPropVariant::InternalCopy(const PROPVARIANT* pSrc)
+{
+  HRESULT hr = Copy(pSrc);
+  if (FAILED(hr))
+  {
+    vt = VT_ERROR;
+    scode = hr;
+  }
+}
+
+int CPropVariant::Compare(const CPropVariant &a)
+{
+  if (vt != a.vt)
+    return 0; // it's mean some bug
+  switch (vt)
+  {
+    case VT_EMPTY:
+      return 0;
+    
+    /*
+    case VT_I1:
+      return MyCompare(cVal, a.cVal);
+    */
+    case VT_UI1:
+      return MyCompare(bVal, a.bVal);
+
+    case VT_I2:
+      return MyCompare(iVal, a.iVal);
+    case VT_UI2:
+      return MyCompare(uiVal, a.uiVal);
+    
+    case VT_I4:
+      return MyCompare(lVal, a.lVal);
+    /*
+    case VT_INT:
+      return MyCompare(intVal, a.intVal);
+    */
+    case VT_UI4:
+      return MyCompare(ulVal, a.ulVal);
+    /*
+    case VT_UINT:
+      return MyCompare(uintVal, a.uintVal);
+    */
+    case VT_I8:
+      return MyCompare(hVal.QuadPart, a.hVal.QuadPart);
+    case VT_UI8:
+      return MyCompare(uhVal.QuadPart, a.uhVal.QuadPart);
+
+    case VT_BOOL:
+      return -MyCompare(boolVal, a.boolVal);
+
+    case VT_FILETIME:
+      return ::CompareFileTime(&filetime, &a.filetime);
+    case VT_BSTR:
+      return 0; // Not implemented
+      // return MyCompare(aPropVarint.cVal);
+
+    default:
+      return 0;
+  }
+}
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/PropVariant.h b/third_party/lzma/v4_65/files/CPP/Windows/PropVariant.h
new file mode 100644
index 0000000..d44215f
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/PropVariant.h
@@ -0,0 +1,57 @@
+// Windows/PropVariant.h
+
+#ifndef __WINDOWS_PROPVARIANT_H
+#define __WINDOWS_PROPVARIANT_H
+
+#include "../Common/MyWindows.h"
+#include "../Common/Types.h"
+
+namespace NWindows {
+namespace NCOM {
+
+class CPropVariant : public tagPROPVARIANT
+{
+public:
+  CPropVariant() { vt = VT_EMPTY; wReserved1 = 0; }
+  ~CPropVariant() { Clear(); }
+  CPropVariant(const PROPVARIANT& varSrc);
+  CPropVariant(const CPropVariant& varSrc);
+  CPropVariant(BSTR bstrSrc);
+  CPropVariant(LPCOLESTR lpszSrc);
+  CPropVariant(bool bSrc) { vt = VT_BOOL; wReserved1 = 0; boolVal = (bSrc ? VARIANT_TRUE : VARIANT_FALSE); };
+  CPropVariant(UInt32 value) { vt = VT_UI4; wReserved1 = 0; ulVal = value; }
+  CPropVariant(UInt64 value) { vt = VT_UI8; wReserved1 = 0; uhVal = *(ULARGE_INTEGER*)&value; }
+  CPropVariant(const FILETIME &value) { vt = VT_FILETIME; wReserved1 = 0; filetime = value; }
+  CPropVariant(Int32 value) { vt = VT_I4; wReserved1 = 0; lVal = value; }
+  CPropVariant(Byte value) { vt = VT_UI1; wReserved1 = 0; bVal = value; }
+  CPropVariant(Int16 value) { vt = VT_I2; wReserved1 = 0; iVal = value; }
+  // CPropVariant(LONG value, VARTYPE vtSrc = VT_I4) { vt = vtSrc; lVal = value; }
+
+  CPropVariant& operator=(const CPropVariant& varSrc);
+  CPropVariant& operator=(const PROPVARIANT& varSrc);
+  CPropVariant& operator=(BSTR bstrSrc);
+  CPropVariant& operator=(LPCOLESTR lpszSrc);
+  CPropVariant& operator=(bool bSrc);
+  CPropVariant& operator=(UInt32 value);
+  CPropVariant& operator=(UInt64 value);
+  CPropVariant& operator=(const FILETIME &value);
+
+  CPropVariant& operator=(Int32 value);
+  CPropVariant& operator=(Byte value);
+  CPropVariant& operator=(Int16 value);
+  // CPropVariant& operator=(LONG  value);
+
+  HRESULT Clear();
+  HRESULT Copy(const PROPVARIANT* pSrc);
+  HRESULT Attach(PROPVARIANT* pSrc);
+  HRESULT Detach(PROPVARIANT* pDest);
+
+  HRESULT InternalClear();
+  void InternalCopy(const PROPVARIANT* pSrc);
+
+  int Compare(const CPropVariant &a1);
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/PropVariantConversions.cpp b/third_party/lzma/v4_65/files/CPP/Windows/PropVariantConversions.cpp
new file mode 100644
index 0000000..df8abfb
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/PropVariantConversions.cpp
@@ -0,0 +1,115 @@
+// PropVariantConversions.cpp
+
+#include "StdAfx.h"
+
+#include "PropVariantConversions.h"
+
+#include "Windows/Defs.h"
+
+#include "Common/StringConvert.h"
+#include "Common/IntToString.h"
+
+static UString ConvertUInt64ToString(UInt64 value)
+{
+  wchar_t buffer[32];
+  ConvertUInt64ToString(value, buffer);
+  return buffer;
+}
+
+static UString ConvertInt64ToString(Int64 value)
+{
+  wchar_t buffer[32];
+  ConvertInt64ToString(value, buffer);
+  return buffer;
+}
+
+static char *UIntToStringSpec(char c, UInt32 value, char *s, int numPos)
+{
+  if (c != 0)
+    *s++ = c;
+  char temp[16];
+  int pos = 0;
+  do
+  {
+    temp[pos++] = (char)('0' + value % 10);
+    value /= 10;
+  }
+  while (value != 0);
+  int i;
+  for (i = 0; i < numPos - pos; i++)
+    *s++ = '0';
+  do
+    *s++ = temp[--pos];
+  while (pos > 0);
+  *s = '\0';
+  return s;
+}
+
+bool ConvertFileTimeToString(const FILETIME &ft, char *s, bool includeTime, bool includeSeconds)
+{
+  s[0] = '\0';
+  SYSTEMTIME st;
+  if (!BOOLToBool(FileTimeToSystemTime(&ft, &st)))
+    return false;
+  s = UIntToStringSpec(0, st.wYear, s, 4);
+  s = UIntToStringSpec('-', st.wMonth, s, 2);
+  s = UIntToStringSpec('-', st.wDay, s, 2);
+  if (includeTime)
+  {
+    s = UIntToStringSpec(' ', st.wHour, s, 2);
+    s = UIntToStringSpec(':', st.wMinute, s, 2);
+    if (includeSeconds)
+      UIntToStringSpec(':', st.wSecond, s, 2);
+  }
+  return true;
+}
+
+UString ConvertFileTimeToString(const FILETIME &fileTime, bool includeTime, bool includeSeconds)
+{
+  char s[32];
+  ConvertFileTimeToString(fileTime, s,  includeTime, includeSeconds);
+  return GetUnicodeString(s);
+}
+ 
+
+UString ConvertPropVariantToString(const PROPVARIANT &prop)
+{
+  switch (prop.vt)
+  {
+    case VT_EMPTY: return UString();
+    case VT_BSTR: return prop.bstrVal;
+    case VT_UI1: return ConvertUInt64ToString(prop.bVal);
+    case VT_UI2: return ConvertUInt64ToString(prop.uiVal);
+    case VT_UI4: return ConvertUInt64ToString(prop.ulVal);
+    case VT_UI8: return ConvertUInt64ToString(prop.uhVal.QuadPart);
+    case VT_FILETIME: return ConvertFileTimeToString(prop.filetime, true, true);
+    // case VT_I1: return ConvertInt64ToString(prop.cVal);
+    case VT_I2: return ConvertInt64ToString(prop.iVal);
+    case VT_I4: return ConvertInt64ToString(prop.lVal);
+    case VT_I8: return ConvertInt64ToString(prop.hVal.QuadPart);
+    case VT_BOOL: return VARIANT_BOOLToBool(prop.boolVal) ? L"+" : L"-";
+    default:
+      #ifndef _WIN32_WCE
+      throw 150245;
+      #else
+      return UString();
+      #endif
+  }
+}
+
+UInt64 ConvertPropVariantToUInt64(const PROPVARIANT &prop)
+{
+  switch (prop.vt)
+  {
+    case VT_UI1: return prop.bVal;
+    case VT_UI2: return prop.uiVal;
+    case VT_UI4: return prop.ulVal;
+    case VT_UI8: return (UInt64)prop.uhVal.QuadPart;
+    default:
+      #ifndef _WIN32_WCE
+      throw 151199;
+      #else
+      return 0;
+      #endif
+  }
+}
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/PropVariantConversions.h b/third_party/lzma/v4_65/files/CPP/Windows/PropVariantConversions.h
new file mode 100644
index 0000000..68ad961
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/PropVariantConversions.h
@@ -0,0 +1,14 @@
+// Windows/PropVariantConversions.h
+
+#ifndef __PROPVARIANTCONVERSIONS_H
+#define __PROPVARIANTCONVERSIONS_H
+
+#include "Common/Types.h"
+#include "Common/MyString.h"
+
+bool ConvertFileTimeToString(const FILETIME &ft, char *s, bool includeTime = true, bool includeSeconds = true);
+UString ConvertFileTimeToString(const FILETIME &ft, bool includeTime = true, bool includeSeconds = true);
+UString ConvertPropVariantToString(const PROPVARIANT &propVariant);
+UInt64 ConvertPropVariantToUInt64(const PROPVARIANT &propVariant);
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/StdAfx.h b/third_party/lzma/v4_65/files/CPP/Windows/StdAfx.h
new file mode 100644
index 0000000..8b383c5
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/StdAfx.h
@@ -0,0 +1,9 @@
+// StdAfx.h
+
+#ifndef __STDAFX_H
+#define __STDAFX_H
+
+#include "../Common/MyWindows.h"
+#include "../Common/NewHandler.h"
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/Synchronization.cpp b/third_party/lzma/v4_65/files/CPP/Windows/Synchronization.cpp
new file mode 100644
index 0000000..5f86d1e
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/Synchronization.cpp
@@ -0,0 +1,10 @@
+// Windows/Synchronization.cpp
+
+#include "StdAfx.h"
+
+#include "Synchronization.h"
+
+namespace NWindows {
+namespace NSynchronization {
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/Synchronization.h b/third_party/lzma/v4_65/files/CPP/Windows/Synchronization.h
new file mode 100644
index 0000000..8d34ae9
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/Synchronization.h
@@ -0,0 +1,168 @@
+// Windows/Synchronization.h
+
+#ifndef __WINDOWS_SYNCHRONIZATION_H
+#define __WINDOWS_SYNCHRONIZATION_H
+
+#include "Defs.h"
+
+extern "C"
+{
+#include "../../C/Threads.h"
+}
+
+#ifdef _WIN32
+#include "Handle.h"
+#endif
+
+namespace NWindows {
+namespace NSynchronization {
+
+class CBaseEvent
+{
+protected:
+  ::CEvent _object;
+public:
+  bool IsCreated() { return Event_IsCreated(&_object) != 0; }
+  operator HANDLE() { return _object.handle; }
+  CBaseEvent() { Event_Construct(&_object); }
+  ~CBaseEvent() { Close(); }
+  WRes Close() { return Event_Close(&_object); }
+  #ifdef _WIN32
+  WRes Create(bool manualReset, bool initiallyOwn, LPCTSTR name = NULL,
+      LPSECURITY_ATTRIBUTES securityAttributes = NULL)
+  {
+    _object.handle = ::CreateEvent(securityAttributes, BoolToBOOL(manualReset),
+        BoolToBOOL(initiallyOwn), name);
+    if (_object.handle != 0)
+      return 0;
+    return ::GetLastError();
+  }
+  WRes Open(DWORD desiredAccess, bool inheritHandle, LPCTSTR name)
+  {
+    _object.handle = ::OpenEvent(desiredAccess, BoolToBOOL(inheritHandle), name);
+    if (_object.handle != 0)
+      return 0;
+    return ::GetLastError();
+  }
+  #endif
+
+  WRes Set() { return Event_Set(&_object); }
+  // bool Pulse() { return BOOLToBool(::PulseEvent(_handle)); }
+  WRes Reset() { return Event_Reset(&_object); }
+  WRes Lock() { return Event_Wait(&_object); }
+};
+
+class CManualResetEvent: public CBaseEvent
+{
+public:
+  WRes Create(bool initiallyOwn = false)
+  {
+    return ManualResetEvent_Create(&_object, initiallyOwn ? 1: 0);
+  }
+  WRes CreateIfNotCreated()
+  {
+    if (IsCreated())
+      return 0;
+    return ManualResetEvent_CreateNotSignaled(&_object);
+  }
+  #ifdef _WIN32
+  WRes CreateWithName(bool initiallyOwn, LPCTSTR name)
+  {
+    return CBaseEvent::Create(true, initiallyOwn, name);
+  }
+  #endif
+};
+
+class CAutoResetEvent: public CBaseEvent
+{
+public:
+  WRes Create()
+  {
+    return AutoResetEvent_CreateNotSignaled(&_object);
+  }
+  WRes CreateIfNotCreated()
+  {
+    if (IsCreated())
+      return 0;
+    return AutoResetEvent_CreateNotSignaled(&_object);
+  }
+};
+
+#ifdef _WIN32
+class CObject: public CHandle
+{
+public:
+  WRes Lock(DWORD timeoutInterval = INFINITE)
+    { return (::WaitForSingleObject(_handle, timeoutInterval) == WAIT_OBJECT_0 ? 0 : ::GetLastError()); }
+};
+class CMutex: public CObject
+{
+public:
+  WRes Create(bool initiallyOwn, LPCTSTR name = NULL,
+      LPSECURITY_ATTRIBUTES securityAttributes = NULL)
+  {
+    _handle = ::CreateMutex(securityAttributes, BoolToBOOL(initiallyOwn), name);
+    if (_handle != 0)
+      return 0;
+    return ::GetLastError();
+  }
+  WRes Open(DWORD desiredAccess, bool inheritHandle, LPCTSTR name)
+  {
+    _handle = ::OpenMutex(desiredAccess, BoolToBOOL(inheritHandle), name);
+    if (_handle != 0)
+      return 0;
+    return ::GetLastError();
+  }
+  WRes Release()
+  {
+    return ::ReleaseMutex(_handle) ? 0 : ::GetLastError();
+  }
+};
+class CMutexLock
+{
+  CMutex *_object;
+public:
+  CMutexLock(CMutex &object): _object(&object) { _object->Lock(); }
+  ~CMutexLock() { _object->Release(); }
+};
+#endif
+
+class CSemaphore
+{
+  ::CSemaphore _object;
+public:
+  CSemaphore() { Semaphore_Construct(&_object); }
+  ~CSemaphore() { Close(); }
+  WRes Close() {  return Semaphore_Close(&_object); }
+  operator HANDLE() { return _object.handle; }
+  WRes Create(UInt32 initiallyCount, UInt32 maxCount)
+  {
+    return Semaphore_Create(&_object, initiallyCount, maxCount);
+  }
+  WRes Release() { return Semaphore_Release1(&_object); }
+  WRes Release(UInt32 releaseCount) { return Semaphore_ReleaseN(&_object, releaseCount); }
+  WRes Lock() { return Semaphore_Wait(&_object); }
+};
+
+class CCriticalSection
+{
+  ::CCriticalSection _object;
+public:
+  CCriticalSection() { CriticalSection_Init(&_object); }
+  ~CCriticalSection() { CriticalSection_Delete(&_object); }
+  void Enter() { CriticalSection_Enter(&_object); }
+  void Leave() { CriticalSection_Leave(&_object); }
+};
+
+class CCriticalSectionLock
+{
+  CCriticalSection *_object;
+  void Unlock()  { _object->Leave(); }
+public:
+  CCriticalSectionLock(CCriticalSection &object): _object(&object) {_object->Enter(); }
+  ~CCriticalSectionLock() { Unlock(); }
+};
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/System.cpp b/third_party/lzma/v4_65/files/CPP/Windows/System.cpp
new file mode 100644
index 0000000..8e4069c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/System.cpp
@@ -0,0 +1,64 @@
+// Windows/System.cpp
+
+#include "StdAfx.h"
+
+#include "System.h"
+
+namespace NWindows {
+namespace NSystem {
+
+UInt32 GetNumberOfProcessors()
+{
+  SYSTEM_INFO systemInfo;
+  GetSystemInfo(&systemInfo);
+  return (UInt32)systemInfo.dwNumberOfProcessors;
+}
+
+#if !defined(_WIN64) && defined(__GNUC__)
+
+typedef struct _MY_MEMORYSTATUSEX {
+  DWORD dwLength;
+  DWORD dwMemoryLoad;
+  DWORDLONG ullTotalPhys;
+  DWORDLONG ullAvailPhys;
+  DWORDLONG ullTotalPageFile;
+  DWORDLONG ullAvailPageFile;
+  DWORDLONG ullTotalVirtual;
+  DWORDLONG ullAvailVirtual;
+  DWORDLONG ullAvailExtendedVirtual;
+} MY_MEMORYSTATUSEX, *MY_LPMEMORYSTATUSEX;
+
+#else
+
+#define MY_MEMORYSTATUSEX MEMORYSTATUSEX
+#define MY_LPMEMORYSTATUSEX LPMEMORYSTATUSEX
+
+#endif
+
+typedef BOOL (WINAPI *GlobalMemoryStatusExP)(MY_LPMEMORYSTATUSEX lpBuffer);
+
+UInt64 GetRamSize()
+{
+  MY_MEMORYSTATUSEX stat;
+  stat.dwLength = sizeof(stat);
+  #ifdef _WIN64
+  if (!::GlobalMemoryStatusEx(&stat))
+    return 0;
+  return stat.ullTotalPhys;
+  #else
+  GlobalMemoryStatusExP globalMemoryStatusEx = (GlobalMemoryStatusExP)
+        ::GetProcAddress(::GetModuleHandle(TEXT("kernel32.dll")),
+        "GlobalMemoryStatusEx");
+  if (globalMemoryStatusEx != 0)
+    if (globalMemoryStatusEx(&stat))
+      return stat.ullTotalPhys;
+  {
+    MEMORYSTATUS stat;
+    stat.dwLength = sizeof(stat);
+    GlobalMemoryStatus(&stat);
+    return stat.dwTotalPhys;
+  }
+  #endif
+}
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/System.h b/third_party/lzma/v4_65/files/CPP/Windows/System.h
new file mode 100644
index 0000000..e006715
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/System.h
@@ -0,0 +1,16 @@
+// Windows/System.h
+
+#ifndef __WINDOWS_SYSTEM_H
+#define __WINDOWS_SYSTEM_H
+
+#include "../Common/Types.h"
+
+namespace NWindows {
+namespace NSystem {
+
+UInt32 GetNumberOfProcessors();
+UInt64 GetRamSize();
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/Thread.h b/third_party/lzma/v4_65/files/CPP/Windows/Thread.h
new file mode 100644
index 0000000..39104f4
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/Thread.h
@@ -0,0 +1,41 @@
+// Windows/Thread.h
+
+#ifndef __WINDOWS_THREAD_H
+#define __WINDOWS_THREAD_H
+
+#include "Defs.h"
+
+extern "C"
+{
+#include "../../C/Threads.h"
+}
+
+namespace NWindows {
+
+class CThread
+{
+  ::CThread thread;
+public:
+  CThread() { Thread_Construct(&thread); }
+  ~CThread() { Close(); }
+  bool IsCreated() { return Thread_WasCreated(&thread) != 0; }
+  WRes Close()  { return Thread_Close(&thread); }
+  WRes Create(THREAD_FUNC_RET_TYPE (THREAD_FUNC_CALL_TYPE *startAddress)(void *), LPVOID parameter)
+    { return Thread_Create(&thread, startAddress, parameter); }
+  WRes Wait() { return Thread_Wait(&thread); }
+  
+  #ifdef _WIN32
+  operator HANDLE() { return thread.handle; }
+  void Attach(HANDLE handle) { thread.handle = handle; }
+  HANDLE Detach() { HANDLE h = thread.handle; thread.handle = NULL; return h; }
+  DWORD Resume() { return ::ResumeThread(thread.handle); }
+  DWORD Suspend() { return ::SuspendThread(thread.handle); }
+  bool Terminate(DWORD exitCode) { return BOOLToBool(::TerminateThread(thread.handle, exitCode)); }
+  int GetPriority() { return ::GetThreadPriority(thread.handle); }
+  bool SetPriority(int priority) { return BOOLToBool(::SetThreadPriority(thread.handle, priority)); }
+  #endif
+};
+
+}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/Time.cpp b/third_party/lzma/v4_65/files/CPP/Windows/Time.cpp
new file mode 100644
index 0000000..810dcbe
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/Time.cpp
@@ -0,0 +1,86 @@
+// Windows/Time.cpp
+
+#include "StdAfx.h"
+
+#include "Time.h"
+#include "Windows/Defs.h"
+
+namespace NWindows {
+namespace NTime {
+
+bool DosTimeToFileTime(UInt32 dosTime, FILETIME &fileTime)
+{
+  return BOOLToBool(::DosDateTimeToFileTime((UInt16)(dosTime >> 16), (UInt16)(dosTime & 0xFFFF), &fileTime));
+}
+
+static const UInt32 kHighDosTime = 0xFF9FBF7D;
+static const UInt32 kLowDosTime = 0x210000;
+
+bool FileTimeToDosTime(const FILETIME &fileTime, UInt32 &dosTime)
+{
+  WORD datePart, timePart;
+  if (!::FileTimeToDosDateTime(&fileTime, &datePart, &timePart))
+  {
+    dosTime = (fileTime.dwHighDateTime >= 0x01C00000) ? kHighDosTime : kLowDosTime;
+    return false;
+  }
+  dosTime = (((UInt32)datePart) << 16) + timePart;
+  return true;
+}
+
+static const UInt32 kNumTimeQuantumsInSecond = 10000000;
+static const UInt64 kUnixTimeStartValue = ((UInt64)kNumTimeQuantumsInSecond) * 60 * 60 * 24 * 134774;
+
+void UnixTimeToFileTime(UInt32 unixTime, FILETIME &fileTime)
+{
+  UInt64 v = kUnixTimeStartValue + ((UInt64)unixTime) * kNumTimeQuantumsInSecond;
+  fileTime.dwLowDateTime = (DWORD)v;
+  fileTime.dwHighDateTime = (DWORD)(v >> 32);
+}
+
+bool FileTimeToUnixTime(const FILETIME &fileTime, UInt32 &unixTime)
+{
+  UInt64 winTime = (((UInt64)fileTime.dwHighDateTime) << 32) + fileTime.dwLowDateTime;
+  if (winTime < kUnixTimeStartValue)
+  {
+    unixTime = 0;
+    return false;
+  }
+  winTime = (winTime - kUnixTimeStartValue) / kNumTimeQuantumsInSecond;
+  if (winTime > 0xFFFFFFFF)
+  {
+    unixTime = 0xFFFFFFFF;
+    return false;
+  }
+  unixTime = (UInt32)winTime;
+  return true;
+}
+
+bool GetSecondsSince1601(unsigned year, unsigned month, unsigned day,
+  unsigned hour, unsigned min, unsigned sec, UInt64 &resSeconds)
+{
+  resSeconds = 0;
+  if (year < 1601 || year >= 10000 || month < 1 || month > 12 ||
+      day < 1 || day > 31 || hour > 23 || min > 59 || sec > 59)
+    return false;
+  UInt32 numYears = year - 1601;
+  UInt32 numDays = numYears * 365 + numYears / 4 - numYears / 100 + numYears / 400;
+  Byte ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+  if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
+    ms[1] = 29;
+  month--;
+  for (unsigned i = 0; i < month; i++)
+    numDays += ms[i];
+  numDays += day - 1;
+  resSeconds = ((UInt64)(numDays * 24 + hour) * 60 + min) * 60 + sec;
+  return true;
+}
+
+void GetCurUtcFileTime(FILETIME &ft)
+{
+  SYSTEMTIME st;
+  GetSystemTime(&st);
+  SystemTimeToFileTime(&st, &ft);
+}
+
+}}
diff --git a/third_party/lzma/v4_65/files/CPP/Windows/Time.h b/third_party/lzma/v4_65/files/CPP/Windows/Time.h
new file mode 100644
index 0000000..6f510b2
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CPP/Windows/Time.h
@@ -0,0 +1,21 @@
+// Windows/Time.h
+
+#ifndef __WINDOWS_TIME_H
+#define __WINDOWS_TIME_H
+
+#include "Common/Types.h"
+
+namespace NWindows {
+namespace NTime {
+
+bool DosTimeToFileTime(UInt32 dosTime, FILETIME &fileTime);
+bool FileTimeToDosTime(const FILETIME &fileTime, UInt32 &dosTime);
+void UnixTimeToFileTime(UInt32 unixTime, FILETIME &fileTime);
+bool FileTimeToUnixTime(const FILETIME &fileTime, UInt32 &unixTime);
+bool GetSecondsSince1601(unsigned year, unsigned month, unsigned day,
+  unsigned hour, unsigned min, unsigned sec, UInt64 &resSeconds);
+void GetCurUtcFileTime(FILETIME &ft);
+
+}}
+
+#endif
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Common/CRC.cs b/third_party/lzma/v4_65/files/CS/7zip/Common/CRC.cs
new file mode 100644
index 0000000..82cc857
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Common/CRC.cs
@@ -0,0 +1,55 @@
+// Common/CRC.cs
+
+namespace SevenZip
+{
+	class CRC
+	{
+		public static readonly uint[] Table;
+
+		static CRC()
+		{
+			Table = new uint[256];
+			const uint kPoly = 0xEDB88320;
+			for (uint i = 0; i < 256; i++)
+			{
+				uint r = i;
+				for (int j = 0; j < 8; j++)
+					if ((r & 1) != 0)
+						r = (r >> 1) ^ kPoly;
+					else
+						r >>= 1;
+				Table[i] = r;
+			}
+		}
+
+		uint _value = 0xFFFFFFFF;
+
+		public void Init() { _value = 0xFFFFFFFF; }
+
+		public void UpdateByte(byte b)
+		{
+			_value = Table[(((byte)(_value)) ^ b)] ^ (_value >> 8);
+		}
+
+		public void Update(byte[] data, uint offset, uint size)
+		{
+			for (uint i = 0; i < size; i++)
+				_value = Table[(((byte)(_value)) ^ data[offset + i])] ^ (_value >> 8);
+		}
+
+		public uint GetDigest() { return _value ^ 0xFFFFFFFF; }
+
+		static uint CalculateDigest(byte[] data, uint offset, uint size)
+		{
+			CRC crc = new CRC();
+			// crc.Init();
+			crc.Update(data, offset, size);
+			return crc.GetDigest();
+		}
+
+		static bool VerifyDigest(uint digest, byte[] data, uint offset, uint size)
+		{
+			return (CalculateDigest(data, offset, size) == digest);
+		}
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Common/CommandLineParser.cs b/third_party/lzma/v4_65/files/CS/7zip/Common/CommandLineParser.cs
new file mode 100644
index 0000000..8eabf59
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Common/CommandLineParser.cs
@@ -0,0 +1,274 @@
+// CommandLineParser.cs
+
+using System;
+using System.Collections;
+
+namespace SevenZip.CommandLineParser
+{
+	public enum SwitchType
+	{
+		Simple,
+		PostMinus,
+		LimitedPostString,
+		UnLimitedPostString,
+		PostChar
+	}
+
+	public class SwitchForm
+	{
+		public string IDString;
+		public SwitchType Type;
+		public bool Multi;
+		public int MinLen;
+		public int MaxLen;
+		public string PostCharSet;
+
+		public SwitchForm(string idString, SwitchType type, bool multi,
+			int minLen, int maxLen, string postCharSet)
+		{
+			IDString = idString;
+			Type = type;
+			Multi = multi;
+			MinLen = minLen;
+			MaxLen = maxLen;
+			PostCharSet = postCharSet;
+		}
+		public SwitchForm(string idString, SwitchType type, bool multi, int minLen):
+			this(idString, type, multi, minLen, 0, "")
+		{
+		}
+		public SwitchForm(string idString, SwitchType type, bool multi):
+			this(idString, type, multi, 0)
+		{
+		}
+	}
+
+	public class SwitchResult
+	{
+		public bool ThereIs;
+		public bool WithMinus;
+		public ArrayList PostStrings = new ArrayList();
+		public int PostCharIndex;
+		public SwitchResult()
+		{
+			ThereIs = false;
+		}
+	}
+
+	public class Parser
+	{
+		public ArrayList NonSwitchStrings = new ArrayList();
+		SwitchResult[] _switches;
+
+		public Parser(int numSwitches)
+		{
+			_switches = new SwitchResult[numSwitches];
+			for (int i = 0; i < numSwitches; i++)
+				_switches[i] = new SwitchResult();
+		}
+
+		bool ParseString(string srcString, SwitchForm[] switchForms)
+		{
+			int len = srcString.Length;
+			if (len == 0)
+				return false;
+			int pos = 0;
+			if (!IsItSwitchChar(srcString[pos]))
+				return false;
+			while (pos < len)
+			{
+				if (IsItSwitchChar(srcString[pos]))
+					pos++;
+				const int kNoLen = -1;
+				int matchedSwitchIndex = 0;
+				int maxLen = kNoLen;
+				for (int switchIndex = 0; switchIndex < _switches.Length; switchIndex++)
+				{
+					int switchLen = switchForms[switchIndex].IDString.Length;
+					if (switchLen <= maxLen || pos + switchLen > len)
+						continue;
+					if (String.Compare(switchForms[switchIndex].IDString, 0,
+							srcString, pos, switchLen, true) == 0)
+					{
+						matchedSwitchIndex = switchIndex;
+						maxLen = switchLen;
+					}
+				}
+				if (maxLen == kNoLen)
+					throw new Exception("maxLen == kNoLen");
+				SwitchResult matchedSwitch = _switches[matchedSwitchIndex];
+				SwitchForm switchForm = switchForms[matchedSwitchIndex];
+				if ((!switchForm.Multi) && matchedSwitch.ThereIs)
+					throw new Exception("switch must be single");
+				matchedSwitch.ThereIs = true;
+				pos += maxLen;
+				int tailSize = len - pos;
+				SwitchType type = switchForm.Type;
+				switch (type)
+				{
+					case SwitchType.PostMinus:
+						{
+							if (tailSize == 0)
+								matchedSwitch.WithMinus = false;
+							else
+							{
+								matchedSwitch.WithMinus = (srcString[pos] == kSwitchMinus);
+								if (matchedSwitch.WithMinus)
+									pos++;
+							}
+							break;
+						}
+					case SwitchType.PostChar:
+						{
+							if (tailSize < switchForm.MinLen)
+								throw new Exception("switch is not full");
+							string charSet = switchForm.PostCharSet;
+							const int kEmptyCharValue = -1;
+							if (tailSize == 0)
+								matchedSwitch.PostCharIndex = kEmptyCharValue;
+							else
+							{
+								int index = charSet.IndexOf(srcString[pos]);
+								if (index < 0)
+									matchedSwitch.PostCharIndex = kEmptyCharValue;
+								else
+								{
+									matchedSwitch.PostCharIndex = index;
+									pos++;
+								}
+							}
+							break;
+						}
+					case SwitchType.LimitedPostString:
+					case SwitchType.UnLimitedPostString:
+						{
+							int minLen = switchForm.MinLen;
+							if (tailSize < minLen)
+								throw new Exception("switch is not full");
+							if (type == SwitchType.UnLimitedPostString)
+							{
+								matchedSwitch.PostStrings.Add(srcString.Substring(pos));
+								return true;
+							}
+							String stringSwitch = srcString.Substring(pos, minLen);
+							pos += minLen;
+							for (int i = minLen; i < switchForm.MaxLen && pos < len; i++, pos++)
+							{
+								char c = srcString[pos];
+								if (IsItSwitchChar(c))
+									break;
+								stringSwitch += c;
+							}
+							matchedSwitch.PostStrings.Add(stringSwitch);
+							break;
+						}
+				}
+			}
+			return true;
+
+		}
+
+		public void ParseStrings(SwitchForm[] switchForms, string[] commandStrings)
+		{
+			int numCommandStrings = commandStrings.Length;
+			bool stopSwitch = false;
+			for (int i = 0; i < numCommandStrings; i++)
+			{
+				string s = commandStrings[i];
+				if (stopSwitch)
+					NonSwitchStrings.Add(s);
+				else
+					if (s == kStopSwitchParsing)
+					stopSwitch = true;
+				else
+					if (!ParseString(s, switchForms))
+					NonSwitchStrings.Add(s);
+			}
+		}
+
+		public SwitchResult this[int index] { get { return _switches[index]; } }
+
+		public static int ParseCommand(CommandForm[] commandForms, string commandString,
+			out string postString)
+		{
+			for (int i = 0; i < commandForms.Length; i++)
+			{
+				string id = commandForms[i].IDString;
+				if (commandForms[i].PostStringMode)
+				{
+					if (commandString.IndexOf(id) == 0)
+					{
+						postString = commandString.Substring(id.Length);
+						return i;
+					}
+				}
+				else
+					if (commandString == id)
+				{
+					postString = "";
+					return i;
+				}
+			}
+			postString = "";
+			return -1;
+		}
+
+		static bool ParseSubCharsCommand(int numForms, CommandSubCharsSet[] forms,
+			string commandString, ArrayList indices)
+		{
+			indices.Clear();
+			int numUsedChars = 0;
+			for (int i = 0; i < numForms; i++)
+			{
+				CommandSubCharsSet charsSet = forms[i];
+				int currentIndex = -1;
+				int len = charsSet.Chars.Length;
+				for (int j = 0; j < len; j++)
+				{
+					char c = charsSet.Chars[j];
+					int newIndex = commandString.IndexOf(c);
+					if (newIndex >= 0)
+					{
+						if (currentIndex >= 0)
+							return false;
+						if (commandString.IndexOf(c, newIndex + 1) >= 0)
+							return false;
+						currentIndex = j;
+						numUsedChars++;
+					}
+				}
+				if (currentIndex == -1 && !charsSet.EmptyAllowed)
+					return false;
+				indices.Add(currentIndex);
+			}
+			return (numUsedChars == commandString.Length);
+		}
+		const char kSwitchID1 = '-';
+		const char kSwitchID2 = '/';
+
+		const char kSwitchMinus = '-';
+		const string kStopSwitchParsing = "--";
+
+		static bool IsItSwitchChar(char c)
+		{
+			return (c == kSwitchID1 || c == kSwitchID2);
+		}
+	}
+
+	public class CommandForm
+	{
+		public string IDString = "";
+		public bool PostStringMode = false;
+		public CommandForm(string idString, bool postStringMode)
+		{
+			IDString = idString;
+			PostStringMode = postStringMode;
+		}
+	}
+
+	class CommandSubCharsSet
+	{
+		public string Chars = "";
+		public bool EmptyAllowed = false;
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Common/InBuffer.cs b/third_party/lzma/v4_65/files/CS/7zip/Common/InBuffer.cs
new file mode 100644
index 0000000..7c51f0b
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Common/InBuffer.cs
@@ -0,0 +1,72 @@
+// InBuffer.cs
+
+namespace SevenZip.Buffer
+{
+	public class InBuffer
+	{
+		byte[] m_Buffer;
+		uint m_Pos;
+		uint m_Limit;
+		uint m_BufferSize;
+		System.IO.Stream m_Stream;
+		bool m_StreamWasExhausted;
+		ulong m_ProcessedSize;
+
+		public InBuffer(uint bufferSize)
+		{
+			m_Buffer = new byte[bufferSize];
+			m_BufferSize = bufferSize;
+		}
+
+		public void Init(System.IO.Stream stream)
+		{
+			m_Stream = stream;
+			m_ProcessedSize = 0;
+			m_Limit = 0;
+			m_Pos = 0;
+			m_StreamWasExhausted = false;
+		}
+
+		public bool ReadBlock()
+		{
+			if (m_StreamWasExhausted)
+				return false;
+			m_ProcessedSize += m_Pos;
+			int aNumProcessedBytes = m_Stream.Read(m_Buffer, 0, (int)m_BufferSize);
+			m_Pos = 0;
+			m_Limit = (uint)aNumProcessedBytes;
+			m_StreamWasExhausted = (aNumProcessedBytes == 0);
+			return (!m_StreamWasExhausted);
+		}
+
+
+		public void ReleaseStream()
+		{
+			// m_Stream.Close(); 
+			m_Stream = null;
+		}
+
+		public bool ReadByte(byte b) // check it
+		{
+			if (m_Pos >= m_Limit)
+				if (!ReadBlock())
+					return false;
+			b = m_Buffer[m_Pos++];
+			return true;
+		}
+
+		public byte ReadByte()
+		{
+			// return (byte)m_Stream.ReadByte();
+			if (m_Pos >= m_Limit)
+				if (!ReadBlock())
+					return 0xFF;
+			return m_Buffer[m_Pos++];
+		}
+
+		public ulong GetProcessedSize()
+		{
+			return m_ProcessedSize + m_Pos;
+		}
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Common/OutBuffer.cs b/third_party/lzma/v4_65/files/CS/7zip/Common/OutBuffer.cs
new file mode 100644
index 0000000..2da16e1
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Common/OutBuffer.cs
@@ -0,0 +1,47 @@
+// OutBuffer.cs
+
+namespace SevenZip.Buffer
+{
+	public class OutBuffer
+	{
+		byte[] m_Buffer;
+		uint m_Pos;
+		uint m_BufferSize;
+		System.IO.Stream m_Stream;
+		ulong m_ProcessedSize;
+
+		public OutBuffer(uint bufferSize)
+		{
+			m_Buffer = new byte[bufferSize];
+			m_BufferSize = bufferSize;
+		}
+
+		public void SetStream(System.IO.Stream stream) { m_Stream = stream; }
+		public void FlushStream() { m_Stream.Flush(); }
+		public void CloseStream() { m_Stream.Close(); }
+		public void ReleaseStream() { m_Stream = null; }
+
+		public void Init()
+		{
+			m_ProcessedSize = 0;
+			m_Pos = 0;
+		}
+
+		public void WriteByte(byte b)
+		{
+			m_Buffer[m_Pos++] = b;
+			if (m_Pos >= m_BufferSize)
+				FlushData();
+		}
+
+		public void FlushData()
+		{
+			if (m_Pos == 0)
+				return;
+			m_Stream.Write(m_Buffer, 0, (int)m_Pos);
+			m_Pos = 0;
+		}
+
+		public ulong GetProcessedSize() { return m_ProcessedSize + m_Pos; }
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Compress/LZ/IMatchFinder.cs b/third_party/lzma/v4_65/files/CS/7zip/Compress/LZ/IMatchFinder.cs
new file mode 100644
index 0000000..10ca2b3
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Compress/LZ/IMatchFinder.cs
@@ -0,0 +1,24 @@
+// IMatchFinder.cs
+
+using System;
+
+namespace SevenZip.Compression.LZ
+{
+	interface IInWindowStream
+	{
+		void SetStream(System.IO.Stream inStream);
+		void Init();
+		void ReleaseStream();
+		Byte GetIndexByte(Int32 index);
+		UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit);
+		UInt32 GetNumAvailableBytes();
+	}
+
+	interface IMatchFinder : IInWindowStream
+	{
+		void Create(UInt32 historySize, UInt32 keepAddBufferBefore,
+				UInt32 matchMaxLen, UInt32 keepAddBufferAfter);
+		UInt32 GetMatches(UInt32[] distances);
+		void Skip(UInt32 num);
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Compress/LZ/LzBinTree.cs b/third_party/lzma/v4_65/files/CS/7zip/Compress/LZ/LzBinTree.cs
new file mode 100644
index 0000000..c1c006b
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Compress/LZ/LzBinTree.cs
@@ -0,0 +1,367 @@
+// LzBinTree.cs
+
+using System;
+
+namespace SevenZip.Compression.LZ
+{
+	public class BinTree : InWindow, IMatchFinder
+	{
+		UInt32 _cyclicBufferPos;
+		UInt32 _cyclicBufferSize = 0;
+		UInt32 _matchMaxLen;
+
+		UInt32[] _son;
+		UInt32[] _hash;
+
+		UInt32 _cutValue = 0xFF;
+		UInt32 _hashMask;
+		UInt32 _hashSizeSum = 0;
+
+		bool HASH_ARRAY = true;
+
+		const UInt32 kHash2Size = 1 << 10;
+		const UInt32 kHash3Size = 1 << 16;
+		const UInt32 kBT2HashSize = 1 << 16;
+		const UInt32 kStartMaxLen = 1;
+		const UInt32 kHash3Offset = kHash2Size;
+		const UInt32 kEmptyHashValue = 0;
+		const UInt32 kMaxValForNormalize = ((UInt32)1 << 31) - 1;
+	
+		UInt32 kNumHashDirectBytes = 0;
+		UInt32 kMinMatchCheck = 4;
+		UInt32 kFixHashSize = kHash2Size + kHash3Size;
+		
+		public void SetType(int numHashBytes)
+		{
+			HASH_ARRAY = (numHashBytes > 2);
+			if (HASH_ARRAY)
+			{
+				kNumHashDirectBytes = 0;
+				kMinMatchCheck = 4;
+				kFixHashSize = kHash2Size + kHash3Size;
+			}
+			else
+			{
+				kNumHashDirectBytes = 2;
+				kMinMatchCheck = 2 + 1;
+				kFixHashSize = 0;
+			}
+		}
+
+		public new void SetStream(System.IO.Stream stream) { base.SetStream(stream); }
+		public new void ReleaseStream() { base.ReleaseStream(); }
+		
+		public new void Init()
+		{
+			base.Init();
+			for (UInt32 i = 0; i < _hashSizeSum; i++)
+				_hash[i] = kEmptyHashValue;
+			_cyclicBufferPos = 0;
+			ReduceOffsets(-1);
+		}
+
+		public new void MovePos()
+		{
+			if (++_cyclicBufferPos >= _cyclicBufferSize)
+				_cyclicBufferPos = 0;
+			base.MovePos();
+			if (_pos == kMaxValForNormalize)
+				Normalize();
+		}
+
+		public new Byte GetIndexByte(Int32 index) { return base.GetIndexByte(index); }
+
+		public new UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit)
+		{ return base.GetMatchLen(index, distance, limit); }
+
+		public new UInt32 GetNumAvailableBytes() { return base.GetNumAvailableBytes(); }
+
+		public void Create(UInt32 historySize, UInt32 keepAddBufferBefore,
+				UInt32 matchMaxLen, UInt32 keepAddBufferAfter)
+		{
+			if (historySize > kMaxValForNormalize - 256)
+				throw new Exception();
+			_cutValue = 16 + (matchMaxLen >> 1);
+				
+			UInt32 windowReservSize = (historySize + keepAddBufferBefore +
+					matchMaxLen + keepAddBufferAfter) / 2 + 256;
+
+			base.Create(historySize + keepAddBufferBefore, matchMaxLen + keepAddBufferAfter, windowReservSize);
+
+			_matchMaxLen = matchMaxLen;
+
+			UInt32 cyclicBufferSize = historySize + 1;
+			if (_cyclicBufferSize != cyclicBufferSize)
+				_son = new UInt32[(_cyclicBufferSize = cyclicBufferSize) * 2];
+
+			UInt32 hs = kBT2HashSize;
+
+			if (HASH_ARRAY)
+			{
+				hs = historySize - 1;
+				hs |= (hs >> 1);
+				hs |= (hs >> 2);
+				hs |= (hs >> 4);
+				hs |= (hs >> 8);
+				hs >>= 1;
+				hs |= 0xFFFF;
+				if (hs > (1 << 24))
+					hs >>= 1;
+				_hashMask = hs;
+				hs++;
+				hs += kFixHashSize;
+			}
+			if (hs != _hashSizeSum)
+				_hash = new UInt32[_hashSizeSum = hs];
+		}
+
+		public UInt32 GetMatches(UInt32[] distances)
+		{
+			UInt32 lenLimit;
+			if (_pos + _matchMaxLen <= _streamPos)
+				lenLimit = _matchMaxLen;
+			else
+			{
+				lenLimit = _streamPos - _pos;
+				if (lenLimit < kMinMatchCheck)
+				{
+					MovePos();
+					return 0;
+				}
+			}
+
+			UInt32 offset = 0;
+			UInt32 matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0;
+			UInt32 cur = _bufferOffset + _pos;
+			UInt32 maxLen = kStartMaxLen; // to avoid items for len < hashSize;
+			UInt32 hashValue, hash2Value = 0, hash3Value = 0;
+
+			if (HASH_ARRAY)
+			{
+				UInt32 temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1];
+				hash2Value = temp & (kHash2Size - 1);
+				temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8);
+				hash3Value = temp & (kHash3Size - 1);
+				hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask;
+			}
+			else
+				hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8);
+
+			UInt32 curMatch = _hash[kFixHashSize + hashValue];
+			if (HASH_ARRAY)
+			{
+				UInt32 curMatch2 = _hash[hash2Value];
+				UInt32 curMatch3 = _hash[kHash3Offset + hash3Value];
+				_hash[hash2Value] = _pos;
+				_hash[kHash3Offset + hash3Value] = _pos;
+				if (curMatch2 > matchMinPos)
+					if (_bufferBase[_bufferOffset + curMatch2] == _bufferBase[cur])
+					{
+						distances[offset++] = maxLen = 2;
+						distances[offset++] = _pos - curMatch2 - 1;
+					}
+				if (curMatch3 > matchMinPos)
+					if (_bufferBase[_bufferOffset + curMatch3] == _bufferBase[cur])
+					{
+						if (curMatch3 == curMatch2)
+							offset -= 2;
+						distances[offset++] = maxLen = 3;
+						distances[offset++] = _pos - curMatch3 - 1;
+						curMatch2 = curMatch3;
+					}
+				if (offset != 0 && curMatch2 == curMatch)
+				{
+					offset -= 2;
+					maxLen = kStartMaxLen;
+				}
+			}
+
+			_hash[kFixHashSize + hashValue] = _pos;
+
+			UInt32 ptr0 = (_cyclicBufferPos << 1) + 1;
+			UInt32 ptr1 = (_cyclicBufferPos << 1);
+
+			UInt32 len0, len1;
+			len0 = len1 = kNumHashDirectBytes;
+			
+			if (kNumHashDirectBytes != 0)
+			{
+				if (curMatch > matchMinPos)
+				{
+					if (_bufferBase[_bufferOffset + curMatch + kNumHashDirectBytes] !=
+							_bufferBase[cur + kNumHashDirectBytes])
+					{
+						distances[offset++] = maxLen = kNumHashDirectBytes;
+						distances[offset++] = _pos - curMatch - 1;
+					}
+				}
+			}
+			
+			UInt32 count = _cutValue;
+			
+			while(true)
+			{
+				if(curMatch <= matchMinPos || count-- == 0)
+				{
+					_son[ptr0] = _son[ptr1] = kEmptyHashValue;
+					break;
+				}
+				UInt32 delta = _pos - curMatch;
+				UInt32 cyclicPos = ((delta <= _cyclicBufferPos) ?
+							(_cyclicBufferPos - delta) :
+							(_cyclicBufferPos - delta + _cyclicBufferSize)) << 1;
+
+				UInt32 pby1 = _bufferOffset + curMatch;
+				UInt32 len = Math.Min(len0, len1);
+				if (_bufferBase[pby1 + len] == _bufferBase[cur + len])
+				{
+					while(++len != lenLimit)
+						if (_bufferBase[pby1 + len] != _bufferBase[cur + len])
+							break;
+					if (maxLen < len)
+					{
+						distances[offset++] = maxLen = len;
+						distances[offset++] = delta - 1;
+						if (len == lenLimit)
+						{
+							_son[ptr1] = _son[cyclicPos];
+							_son[ptr0] = _son[cyclicPos + 1];
+							break;
+						}
+					}
+				}
+				if (_bufferBase[pby1 + len] < _bufferBase[cur + len])
+				{
+					_son[ptr1] = curMatch;
+					ptr1 = cyclicPos + 1;
+					curMatch = _son[ptr1];
+					len1 = len;
+				}
+				else
+				{
+					_son[ptr0] = curMatch;
+					ptr0 = cyclicPos;
+					curMatch = _son[ptr0];
+					len0 = len;
+				}
+			}
+			MovePos();
+			return offset;
+		}
+
+		public void Skip(UInt32 num)
+		{
+			do
+			{
+				UInt32 lenLimit;
+				if (_pos + _matchMaxLen <= _streamPos)
+					lenLimit = _matchMaxLen;
+				else
+				{
+					lenLimit = _streamPos - _pos;
+					if (lenLimit < kMinMatchCheck)
+					{
+						MovePos();
+						continue;
+					}
+				}
+
+				UInt32 matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0;
+				UInt32 cur = _bufferOffset + _pos;
+
+				UInt32 hashValue;
+
+				if (HASH_ARRAY)
+				{
+					UInt32 temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1];
+					UInt32 hash2Value = temp & (kHash2Size - 1);
+					_hash[hash2Value] = _pos;
+					temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8);
+					UInt32 hash3Value = temp & (kHash3Size - 1);
+					_hash[kHash3Offset + hash3Value] = _pos;
+					hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask;
+				}
+				else
+					hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8);
+
+				UInt32 curMatch = _hash[kFixHashSize + hashValue];
+				_hash[kFixHashSize + hashValue] = _pos;
+
+				UInt32 ptr0 = (_cyclicBufferPos << 1) + 1;
+				UInt32 ptr1 = (_cyclicBufferPos << 1);
+
+				UInt32 len0, len1;
+				len0 = len1 = kNumHashDirectBytes;
+
+				UInt32 count = _cutValue;
+				while (true)
+				{
+					if (curMatch <= matchMinPos || count-- == 0)
+					{
+						_son[ptr0] = _son[ptr1] = kEmptyHashValue;
+						break;
+					}
+
+					UInt32 delta = _pos - curMatch;
+					UInt32 cyclicPos = ((delta <= _cyclicBufferPos) ?
+								(_cyclicBufferPos - delta) :
+								(_cyclicBufferPos - delta + _cyclicBufferSize)) << 1;
+
+					UInt32 pby1 = _bufferOffset + curMatch;
+					UInt32 len = Math.Min(len0, len1);
+					if (_bufferBase[pby1 + len] == _bufferBase[cur + len])
+					{
+						while (++len != lenLimit)
+							if (_bufferBase[pby1 + len] != _bufferBase[cur + len])
+								break;
+						if (len == lenLimit)
+						{
+							_son[ptr1] = _son[cyclicPos];
+							_son[ptr0] = _son[cyclicPos + 1];
+							break;
+						}
+					}
+					if (_bufferBase[pby1 + len] < _bufferBase[cur + len])
+					{
+						_son[ptr1] = curMatch;
+						ptr1 = cyclicPos + 1;
+						curMatch = _son[ptr1];
+						len1 = len;
+					}
+					else
+					{
+						_son[ptr0] = curMatch;
+						ptr0 = cyclicPos;
+						curMatch = _son[ptr0];
+						len0 = len;
+					}
+				}
+				MovePos();
+			}
+			while (--num != 0);
+		}
+
+		void NormalizeLinks(UInt32[] items, UInt32 numItems, UInt32 subValue)
+		{
+			for (UInt32 i = 0; i < numItems; i++)
+			{
+				UInt32 value = items[i];
+				if (value <= subValue)
+					value = kEmptyHashValue;
+				else
+					value -= subValue;
+				items[i] = value;
+			}
+		}
+
+		void Normalize()
+		{
+			UInt32 subValue = _pos - _cyclicBufferSize;
+			NormalizeLinks(_son, _cyclicBufferSize * 2, subValue);
+			NormalizeLinks(_hash, _hashSizeSum, subValue);
+			ReduceOffsets((Int32)subValue);
+		}
+
+		public void SetCutValue(UInt32 cutValue) { _cutValue = cutValue; }
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Compress/LZ/LzInWindow.cs b/third_party/lzma/v4_65/files/CS/7zip/Compress/LZ/LzInWindow.cs
new file mode 100644
index 0000000..52d23ce
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Compress/LZ/LzInWindow.cs
@@ -0,0 +1,132 @@
+// LzInWindow.cs
+
+using System;
+
+namespace SevenZip.Compression.LZ
+{
+	public class InWindow
+	{
+		public Byte[] _bufferBase = null; // pointer to buffer with data
+		System.IO.Stream _stream;
+		UInt32 _posLimit; // offset (from _buffer) of first byte when new block reading must be done
+		bool _streamEndWasReached; // if (true) then _streamPos shows real end of stream
+
+		UInt32 _pointerToLastSafePosition;
+
+		public UInt32 _bufferOffset;
+
+		public UInt32 _blockSize; // Size of Allocated memory block
+		public UInt32 _pos; // offset (from _buffer) of curent byte
+		UInt32 _keepSizeBefore; // how many BYTEs must be kept in buffer before _pos
+		UInt32 _keepSizeAfter; // how many BYTEs must be kept buffer after _pos
+		public UInt32 _streamPos; // offset (from _buffer) of first not read byte from Stream
+
+		public void MoveBlock()
+		{
+			UInt32 offset = (UInt32)(_bufferOffset) + _pos - _keepSizeBefore;
+			// we need one additional byte, since MovePos moves on 1 byte.
+			if (offset > 0)
+				offset--;
+			
+			UInt32 numBytes = (UInt32)(_bufferOffset) + _streamPos - offset;
+
+			// check negative offset ????
+			for (UInt32 i = 0; i < numBytes; i++)
+				_bufferBase[i] = _bufferBase[offset + i];
+			_bufferOffset -= offset;
+		}
+
+		public virtual void ReadBlock()
+		{
+			if (_streamEndWasReached)
+				return;
+			while (true)
+			{
+				int size = (int)((0 - _bufferOffset) + _blockSize - _streamPos);
+				if (size == 0)
+					return;
+				int numReadBytes = _stream.Read(_bufferBase, (int)(_bufferOffset + _streamPos), size);
+				if (numReadBytes == 0)
+				{
+					_posLimit = _streamPos;
+					UInt32 pointerToPostion = _bufferOffset + _posLimit;
+					if (pointerToPostion > _pointerToLastSafePosition)
+						_posLimit = (UInt32)(_pointerToLastSafePosition - _bufferOffset);
+
+					_streamEndWasReached = true;
+					return;
+				}
+				_streamPos += (UInt32)numReadBytes;
+				if (_streamPos >= _pos + _keepSizeAfter)
+					_posLimit = _streamPos - _keepSizeAfter;
+			}
+		}
+
+		void Free() { _bufferBase = null; }
+
+		public void Create(UInt32 keepSizeBefore, UInt32 keepSizeAfter, UInt32 keepSizeReserv)
+		{
+			_keepSizeBefore = keepSizeBefore;
+			_keepSizeAfter = keepSizeAfter;
+			UInt32 blockSize = keepSizeBefore + keepSizeAfter + keepSizeReserv;
+			if (_bufferBase == null || _blockSize != blockSize)
+			{
+				Free();
+				_blockSize = blockSize;
+				_bufferBase = new Byte[_blockSize];
+			}
+			_pointerToLastSafePosition = _blockSize - keepSizeAfter;
+		}
+
+		public void SetStream(System.IO.Stream stream) { _stream = stream; }
+		public void ReleaseStream() { _stream = null; }
+
+		public void Init()
+		{
+			_bufferOffset = 0;
+			_pos = 0;
+			_streamPos = 0;
+			_streamEndWasReached = false;
+			ReadBlock();
+		}
+
+		public void MovePos()
+		{
+			_pos++;
+			if (_pos > _posLimit)
+			{
+				UInt32 pointerToPostion = _bufferOffset + _pos;
+				if (pointerToPostion > _pointerToLastSafePosition)
+					MoveBlock();
+				ReadBlock();
+			}
+		}
+
+		public Byte GetIndexByte(Int32 index) { return _bufferBase[_bufferOffset + _pos + index]; }
+
+		// index + limit have not to exceed _keepSizeAfter;
+		public UInt32 GetMatchLen(Int32 index, UInt32 distance, UInt32 limit)
+		{
+			if (_streamEndWasReached)
+				if ((_pos + index) + limit > _streamPos)
+					limit = _streamPos - (UInt32)(_pos + index);
+			distance++;
+			// Byte *pby = _buffer + (size_t)_pos + index;
+			UInt32 pby = _bufferOffset + _pos + (UInt32)index;
+
+			UInt32 i;
+			for (i = 0; i < limit && _bufferBase[pby + i] == _bufferBase[pby + i - distance]; i++);
+			return i;
+		}
+
+		public UInt32 GetNumAvailableBytes() { return _streamPos - _pos; }
+
+		public void ReduceOffsets(Int32 subValue)
+		{
+			_bufferOffset += (UInt32)subValue;
+			_posLimit -= (UInt32)subValue;
+			_pos -= (UInt32)subValue;
+			_streamPos -= (UInt32)subValue;
+		}
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Compress/LZ/LzOutWindow.cs b/third_party/lzma/v4_65/files/CS/7zip/Compress/LZ/LzOutWindow.cs
new file mode 100644
index 0000000..c998584
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Compress/LZ/LzOutWindow.cs
@@ -0,0 +1,110 @@
+// LzOutWindow.cs
+
+namespace SevenZip.Compression.LZ
+{
+	public class OutWindow
+	{
+		byte[] _buffer = null;
+		uint _pos;
+		uint _windowSize = 0;
+		uint _streamPos;
+		System.IO.Stream _stream;
+
+		public uint TrainSize = 0;
+
+		public void Create(uint windowSize)
+		{
+			if (_windowSize != windowSize)
+			{
+				// System.GC.Collect();
+				_buffer = new byte[windowSize];
+			}
+			_windowSize = windowSize;
+			_pos = 0;
+			_streamPos = 0;
+		}
+
+		public void Init(System.IO.Stream stream, bool solid)
+		{
+			ReleaseStream();
+			_stream = stream;
+			if (!solid)
+			{
+				_streamPos = 0;
+				_pos = 0;
+				TrainSize = 0;
+			}
+		}
+	
+		public bool Train(System.IO.Stream stream)
+		{
+			long len = stream.Length;
+			uint size = (len < _windowSize) ? (uint)len : _windowSize;
+			TrainSize = size;
+			stream.Position = len - size;
+			_streamPos = _pos = 0;
+			while (size > 0)
+			{
+				uint curSize = _windowSize - _pos;
+				if (size < curSize)
+					curSize = size;
+				int numReadBytes = stream.Read(_buffer, (int)_pos, (int)curSize);
+				if (numReadBytes == 0)
+					return false;
+				size -= (uint)numReadBytes;
+				_pos += (uint)numReadBytes;
+				_streamPos += (uint)numReadBytes;
+				if (_pos == _windowSize)
+					_streamPos = _pos = 0;
+			}
+			return true;
+		}
+
+		public void ReleaseStream()
+		{
+			Flush();
+			_stream = null;
+		}
+
+		public void Flush()
+		{
+			uint size = _pos - _streamPos;
+			if (size == 0)
+				return;
+			_stream.Write(_buffer, (int)_streamPos, (int)size);
+			if (_pos >= _windowSize)
+				_pos = 0;
+			_streamPos = _pos;
+		}
+
+		public void CopyBlock(uint distance, uint len)
+		{
+			uint pos = _pos - distance - 1;
+			if (pos >= _windowSize)
+				pos += _windowSize;
+			for (; len > 0; len--)
+			{
+				if (pos >= _windowSize)
+					pos = 0;
+				_buffer[_pos++] = _buffer[pos++];
+				if (_pos >= _windowSize)
+					Flush();
+			}
+		}
+
+		public void PutByte(byte b)
+		{
+			_buffer[_pos++] = b;
+			if (_pos >= _windowSize)
+				Flush();
+		}
+
+		public byte GetByte(uint distance)
+		{
+			uint pos = _pos - distance - 1;
+			if (pos >= _windowSize)
+				pos += _windowSize;
+			return _buffer[pos];
+		}
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Compress/LZMA/LzmaBase.cs b/third_party/lzma/v4_65/files/CS/7zip/Compress/LZMA/LzmaBase.cs
new file mode 100644
index 0000000..c7bca86
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Compress/LZMA/LzmaBase.cs
@@ -0,0 +1,76 @@
+// LzmaBase.cs
+
+namespace SevenZip.Compression.LZMA
+{
+	internal abstract class Base
+	{
+		public const uint kNumRepDistances = 4;
+		public const uint kNumStates = 12;
+
+		// static byte []kLiteralNextStates  = {0, 0, 0, 0, 1, 2, 3, 4,  5,  6,   4, 5};
+		// static byte []kMatchNextStates    = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10};
+		// static byte []kRepNextStates      = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11};
+		// static byte []kShortRepNextStates = {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11};
+
+		public struct State
+		{
+			public uint Index;
+			public void Init() { Index = 0; }
+			public void UpdateChar()
+			{
+				if (Index < 4) Index = 0;
+				else if (Index < 10) Index -= 3;
+				else Index -= 6;
+			}
+			public void UpdateMatch() { Index = (uint)(Index < 7 ? 7 : 10); }
+			public void UpdateRep() { Index = (uint)(Index < 7 ? 8 : 11); }
+			public void UpdateShortRep() { Index = (uint)(Index < 7 ? 9 : 11); }
+			public bool IsCharState() { return Index < 7; }
+		}
+
+		public const int kNumPosSlotBits = 6;
+		public const int kDicLogSizeMin = 0;
+		// public const int kDicLogSizeMax = 30;
+		// public const uint kDistTableSizeMax = kDicLogSizeMax * 2;
+
+		public const int kNumLenToPosStatesBits = 2; // it's for speed optimization
+		public const uint kNumLenToPosStates = 1 << kNumLenToPosStatesBits;
+
+		public const uint kMatchMinLen = 2;
+
+		public static uint GetLenToPosState(uint len)
+		{
+			len -= kMatchMinLen;
+			if (len < kNumLenToPosStates)
+				return len;
+			return (uint)(kNumLenToPosStates - 1);
+		}
+
+		public const int kNumAlignBits = 4;
+		public const uint kAlignTableSize = 1 << kNumAlignBits;
+		public const uint kAlignMask = (kAlignTableSize - 1);
+
+		public const uint kStartPosModelIndex = 4;
+		public const uint kEndPosModelIndex = 14;
+		public const uint kNumPosModels = kEndPosModelIndex - kStartPosModelIndex;
+
+		public const uint kNumFullDistances = 1 << ((int)kEndPosModelIndex / 2);
+
+		public const uint kNumLitPosStatesBitsEncodingMax = 4;
+		public const uint kNumLitContextBitsMax = 8;
+
+		public const int kNumPosStatesBitsMax = 4;
+		public const uint kNumPosStatesMax = (1 << kNumPosStatesBitsMax);
+		public const int kNumPosStatesBitsEncodingMax = 4;
+		public const uint kNumPosStatesEncodingMax = (1 << kNumPosStatesBitsEncodingMax);
+
+		public const int kNumLowLenBits = 3;
+		public const int kNumMidLenBits = 3;
+		public const int kNumHighLenBits = 8;
+		public const uint kNumLowLenSymbols = 1 << kNumLowLenBits;
+		public const uint kNumMidLenSymbols = 1 << kNumMidLenBits;
+		public const uint kNumLenSymbols = kNumLowLenSymbols + kNumMidLenSymbols +
+				(1 << kNumHighLenBits);
+		public const uint kMatchMaxLen = kMatchMinLen + kNumLenSymbols - 1;
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Compress/LZMA/LzmaDecoder.cs b/third_party/lzma/v4_65/files/CS/7zip/Compress/LZMA/LzmaDecoder.cs
new file mode 100644
index 0000000..a9be39f
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Compress/LZMA/LzmaDecoder.cs
@@ -0,0 +1,398 @@
+// LzmaDecoder.cs
+
+using System;
+
+namespace SevenZip.Compression.LZMA
+{
+	using RangeCoder;
+
+	public class Decoder : ICoder, ISetDecoderProperties // ,System.IO.Stream
+	{
+		class LenDecoder
+		{
+			BitDecoder m_Choice = new BitDecoder();
+			BitDecoder m_Choice2 = new BitDecoder();
+			BitTreeDecoder[] m_LowCoder = new BitTreeDecoder[Base.kNumPosStatesMax];
+			BitTreeDecoder[] m_MidCoder = new BitTreeDecoder[Base.kNumPosStatesMax];
+			BitTreeDecoder m_HighCoder = new BitTreeDecoder(Base.kNumHighLenBits);
+			uint m_NumPosStates = 0;
+
+			public void Create(uint numPosStates)
+			{
+				for (uint posState = m_NumPosStates; posState < numPosStates; posState++)
+				{
+					m_LowCoder[posState] = new BitTreeDecoder(Base.kNumLowLenBits);
+					m_MidCoder[posState] = new BitTreeDecoder(Base.kNumMidLenBits);
+				}
+				m_NumPosStates = numPosStates;
+			}
+
+			public void Init()
+			{
+				m_Choice.Init();
+				for (uint posState = 0; posState < m_NumPosStates; posState++)
+				{
+					m_LowCoder[posState].Init();
+					m_MidCoder[posState].Init();
+				}
+				m_Choice2.Init();
+				m_HighCoder.Init();
+			}
+
+			public uint Decode(RangeCoder.Decoder rangeDecoder, uint posState)
+			{
+				if (m_Choice.Decode(rangeDecoder) == 0)
+					return m_LowCoder[posState].Decode(rangeDecoder);
+				else
+				{
+					uint symbol = Base.kNumLowLenSymbols;
+					if (m_Choice2.Decode(rangeDecoder) == 0)
+						symbol += m_MidCoder[posState].Decode(rangeDecoder);
+					else
+					{
+						symbol += Base.kNumMidLenSymbols;
+						symbol += m_HighCoder.Decode(rangeDecoder);
+					}
+					return symbol;
+				}
+			}
+		}
+
+		class LiteralDecoder
+		{
+			struct Decoder2
+			{
+				BitDecoder[] m_Decoders;
+				public void Create() { m_Decoders = new BitDecoder[0x300]; }
+				public void Init() { for (int i = 0; i < 0x300; i++) m_Decoders[i].Init(); }
+
+				public byte DecodeNormal(RangeCoder.Decoder rangeDecoder)
+				{
+					uint symbol = 1;
+					do
+						symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder);
+					while (symbol < 0x100);
+					return (byte)symbol;
+				}
+
+				public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, byte matchByte)
+				{
+					uint symbol = 1;
+					do
+					{
+						uint matchBit = (uint)(matchByte >> 7) & 1;
+						matchByte <<= 1;
+						uint bit = m_Decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder);
+						symbol = (symbol << 1) | bit;
+						if (matchBit != bit)
+						{
+							while (symbol < 0x100)
+								symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder);
+							break;
+						}
+					}
+					while (symbol < 0x100);
+					return (byte)symbol;
+				}
+			}
+
+			Decoder2[] m_Coders;
+			int m_NumPrevBits;
+			int m_NumPosBits;
+			uint m_PosMask;
+
+			public void Create(int numPosBits, int numPrevBits)
+			{
+				if (m_Coders != null && m_NumPrevBits == numPrevBits &&
+					m_NumPosBits == numPosBits)
+					return;
+				m_NumPosBits = numPosBits;
+				m_PosMask = ((uint)1 << numPosBits) - 1;
+				m_NumPrevBits = numPrevBits;
+				uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits);
+				m_Coders = new Decoder2[numStates];
+				for (uint i = 0; i < numStates; i++)
+					m_Coders[i].Create();
+			}
+
+			public void Init()
+			{
+				uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits);
+				for (uint i = 0; i < numStates; i++)
+					m_Coders[i].Init();
+			}
+
+			uint GetState(uint pos, byte prevByte)
+			{ return ((pos & m_PosMask) << m_NumPrevBits) + (uint)(prevByte >> (8 - m_NumPrevBits)); }
+
+			public byte DecodeNormal(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte)
+			{ return m_Coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder); }
+
+			public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte, byte matchByte)
+			{ return m_Coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte); }
+		};
+
+		LZ.OutWindow m_OutWindow = new LZ.OutWindow();
+		RangeCoder.Decoder m_RangeDecoder = new RangeCoder.Decoder();
+
+		BitDecoder[] m_IsMatchDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax];
+		BitDecoder[] m_IsRepDecoders = new BitDecoder[Base.kNumStates];
+		BitDecoder[] m_IsRepG0Decoders = new BitDecoder[Base.kNumStates];
+		BitDecoder[] m_IsRepG1Decoders = new BitDecoder[Base.kNumStates];
+		BitDecoder[] m_IsRepG2Decoders = new BitDecoder[Base.kNumStates];
+		BitDecoder[] m_IsRep0LongDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax];
+
+		BitTreeDecoder[] m_PosSlotDecoder = new BitTreeDecoder[Base.kNumLenToPosStates];
+		BitDecoder[] m_PosDecoders = new BitDecoder[Base.kNumFullDistances - Base.kEndPosModelIndex];
+
+		BitTreeDecoder m_PosAlignDecoder = new BitTreeDecoder(Base.kNumAlignBits);
+
+		LenDecoder m_LenDecoder = new LenDecoder();
+		LenDecoder m_RepLenDecoder = new LenDecoder();
+
+		LiteralDecoder m_LiteralDecoder = new LiteralDecoder();
+
+		uint m_DictionarySize;
+		uint m_DictionarySizeCheck;
+
+		uint m_PosStateMask;
+
+		public Decoder()
+		{
+			m_DictionarySize = 0xFFFFFFFF;
+			for (int i = 0; i < Base.kNumLenToPosStates; i++)
+				m_PosSlotDecoder[i] = new BitTreeDecoder(Base.kNumPosSlotBits);
+		}
+
+		void SetDictionarySize(uint dictionarySize)
+		{
+			if (m_DictionarySize != dictionarySize)
+			{
+				m_DictionarySize = dictionarySize;
+				m_DictionarySizeCheck = Math.Max(m_DictionarySize, 1);
+				uint blockSize = Math.Max(m_DictionarySizeCheck, (1 << 12));
+				m_OutWindow.Create(blockSize);
+			}
+		}
+
+		void SetLiteralProperties(int lp, int lc)
+		{
+			if (lp > 8)
+				throw new InvalidParamException();
+			if (lc > 8)
+				throw new InvalidParamException();
+			m_LiteralDecoder.Create(lp, lc);
+		}
+
+		void SetPosBitsProperties(int pb)
+		{
+			if (pb > Base.kNumPosStatesBitsMax)
+				throw new InvalidParamException();
+			uint numPosStates = (uint)1 << pb;
+			m_LenDecoder.Create(numPosStates);
+			m_RepLenDecoder.Create(numPosStates);
+			m_PosStateMask = numPosStates - 1;
+		}
+
+		bool _solid = false;
+		void Init(System.IO.Stream inStream, System.IO.Stream outStream)
+		{
+			m_RangeDecoder.Init(inStream);
+			m_OutWindow.Init(outStream, _solid);
+
+			uint i;
+			for (i = 0; i < Base.kNumStates; i++)
+			{
+				for (uint j = 0; j <= m_PosStateMask; j++)
+				{
+					uint index = (i << Base.kNumPosStatesBitsMax) + j;
+					m_IsMatchDecoders[index].Init();
+					m_IsRep0LongDecoders[index].Init();
+				}
+				m_IsRepDecoders[i].Init();
+				m_IsRepG0Decoders[i].Init();
+				m_IsRepG1Decoders[i].Init();
+				m_IsRepG2Decoders[i].Init();
+			}
+
+			m_LiteralDecoder.Init();
+			for (i = 0; i < Base.kNumLenToPosStates; i++)
+				m_PosSlotDecoder[i].Init();
+			// m_PosSpecDecoder.Init();
+			for (i = 0; i < Base.kNumFullDistances - Base.kEndPosModelIndex; i++)
+				m_PosDecoders[i].Init();
+
+			m_LenDecoder.Init();
+			m_RepLenDecoder.Init();
+			m_PosAlignDecoder.Init();
+		}
+
+		public void Code(System.IO.Stream inStream, System.IO.Stream outStream,
+			Int64 inSize, Int64 outSize, ICodeProgress progress)
+		{
+			Init(inStream, outStream);
+
+			Base.State state = new Base.State();
+			state.Init();
+			uint rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0;
+
+			UInt64 nowPos64 = 0;
+			UInt64 outSize64 = (UInt64)outSize;
+			if (nowPos64 < outSize64)
+			{
+				if (m_IsMatchDecoders[state.Index << Base.kNumPosStatesBitsMax].Decode(m_RangeDecoder) != 0)
+					throw new DataErrorException();
+				state.UpdateChar();
+				byte b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, 0, 0);
+				m_OutWindow.PutByte(b);
+				nowPos64++;
+			}
+			while (nowPos64 < outSize64)
+			{
+				// UInt64 next = Math.Min(nowPos64 + (1 << 18), outSize64);
+					// while(nowPos64 < next)
+				{
+					uint posState = (uint)nowPos64 & m_PosStateMask;
+					if (m_IsMatchDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode(m_RangeDecoder) == 0)
+					{
+						byte b;
+						byte prevByte = m_OutWindow.GetByte(0);
+						if (!state.IsCharState())
+							b = m_LiteralDecoder.DecodeWithMatchByte(m_RangeDecoder,
+								(uint)nowPos64, prevByte, m_OutWindow.GetByte(rep0));
+						else
+							b = m_LiteralDecoder.DecodeNormal(m_RangeDecoder, (uint)nowPos64, prevByte);
+						m_OutWindow.PutByte(b);
+						state.UpdateChar();
+						nowPos64++;
+					}
+					else
+					{
+						uint len;
+						if (m_IsRepDecoders[state.Index].Decode(m_RangeDecoder) == 1)
+						{
+							if (m_IsRepG0Decoders[state.Index].Decode(m_RangeDecoder) == 0)
+							{
+								if (m_IsRep0LongDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode(m_RangeDecoder) == 0)
+								{
+									state.UpdateShortRep();
+									m_OutWindow.PutByte(m_OutWindow.GetByte(rep0));
+									nowPos64++;
+									continue;
+								}
+							}
+							else
+							{
+								UInt32 distance;
+								if (m_IsRepG1Decoders[state.Index].Decode(m_RangeDecoder) == 0)
+								{
+									distance = rep1;
+								}
+								else
+								{
+									if (m_IsRepG2Decoders[state.Index].Decode(m_RangeDecoder) == 0)
+										distance = rep2;
+									else
+									{
+										distance = rep3;
+										rep3 = rep2;
+									}
+									rep2 = rep1;
+								}
+								rep1 = rep0;
+								rep0 = distance;
+							}
+							len = m_RepLenDecoder.Decode(m_RangeDecoder, posState) + Base.kMatchMinLen;
+							state.UpdateRep();
+						}
+						else
+						{
+							rep3 = rep2;
+							rep2 = rep1;
+							rep1 = rep0;
+							len = Base.kMatchMinLen + m_LenDecoder.Decode(m_RangeDecoder, posState);
+							state.UpdateMatch();
+							uint posSlot = m_PosSlotDecoder[Base.GetLenToPosState(len)].Decode(m_RangeDecoder);
+							if (posSlot >= Base.kStartPosModelIndex)
+							{
+								int numDirectBits = (int)((posSlot >> 1) - 1);
+								rep0 = ((2 | (posSlot & 1)) << numDirectBits);
+								if (posSlot < Base.kEndPosModelIndex)
+									rep0 += BitTreeDecoder.ReverseDecode(m_PosDecoders,
+											rep0 - posSlot - 1, m_RangeDecoder, numDirectBits);
+								else
+								{
+									rep0 += (m_RangeDecoder.DecodeDirectBits(
+										numDirectBits - Base.kNumAlignBits) << Base.kNumAlignBits);
+									rep0 += m_PosAlignDecoder.ReverseDecode(m_RangeDecoder);
+								}
+							}
+							else
+								rep0 = posSlot;
+						}
+						if (rep0 >= m_OutWindow.TrainSize + nowPos64 || rep0 >= m_DictionarySizeCheck)
+						{
+							if (rep0 == 0xFFFFFFFF)
+								break;
+							throw new DataErrorException();
+						}
+						m_OutWindow.CopyBlock(rep0, len);
+						nowPos64 += len;
+					}
+				}
+			}
+			m_OutWindow.Flush();
+			m_OutWindow.ReleaseStream();
+			m_RangeDecoder.ReleaseStream();
+		}
+
+		public void SetDecoderProperties(byte[] properties)
+		{
+			if (properties.Length < 5)
+				throw new InvalidParamException();
+			int lc = properties[0] % 9;
+			int remainder = properties[0] / 9;
+			int lp = remainder % 5;
+			int pb = remainder / 5;
+			if (pb > Base.kNumPosStatesBitsMax)
+				throw new InvalidParamException();
+			UInt32 dictionarySize = 0;
+			for (int i = 0; i < 4; i++)
+				dictionarySize += ((UInt32)(properties[1 + i])) << (i * 8);
+			SetDictionarySize(dictionarySize);
+			SetLiteralProperties(lp, lc);
+			SetPosBitsProperties(pb);
+		}
+
+		public bool Train(System.IO.Stream stream)
+		{
+			_solid = true;
+			return m_OutWindow.Train(stream);
+		}
+
+		/*
+		public override bool CanRead { get { return true; }}
+		public override bool CanWrite { get { return true; }}
+		public override bool CanSeek { get { return true; }}
+		public override long Length { get { return 0; }}
+		public override long Position
+		{
+			get { return 0;	}
+			set { }
+		}
+		public override void Flush() { }
+		public override int Read(byte[] buffer, int offset, int count) 
+		{
+			return 0;
+		}
+		public override void Write(byte[] buffer, int offset, int count)
+		{
+		}
+		public override long Seek(long offset, System.IO.SeekOrigin origin)
+		{
+			return 0;
+		}
+		public override void SetLength(long value) {}
+		*/
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Compress/LZMA/LzmaEncoder.cs b/third_party/lzma/v4_65/files/CS/7zip/Compress/LZMA/LzmaEncoder.cs
new file mode 100644
index 0000000..a8d6723
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Compress/LZMA/LzmaEncoder.cs
@@ -0,0 +1,1480 @@
+// LzmaEncoder.cs
+
+using System;
+
+namespace SevenZip.Compression.LZMA
+{
+	using RangeCoder;
+
+	public class Encoder : ICoder, ISetCoderProperties, IWriteCoderProperties
+	{
+		enum EMatchFinderType
+		{
+			BT2,
+			BT4,
+		};
+
+		const UInt32 kIfinityPrice = 0xFFFFFFF;
+
+		static Byte[] g_FastPos = new Byte[1 << 11];
+
+		static Encoder()
+		{
+			const Byte kFastSlots = 22;
+			int c = 2;
+			g_FastPos[0] = 0;
+			g_FastPos[1] = 1;
+			for (Byte slotFast = 2; slotFast < kFastSlots; slotFast++)
+			{
+				UInt32 k = ((UInt32)1 << ((slotFast >> 1) - 1));
+				for (UInt32 j = 0; j < k; j++, c++)
+					g_FastPos[c] = slotFast;
+			}
+		}
+
+		static UInt32 GetPosSlot(UInt32 pos)
+		{
+			if (pos < (1 << 11))
+				return g_FastPos[pos];
+			if (pos < (1 << 21))
+				return (UInt32)(g_FastPos[pos >> 10] + 20);
+			return (UInt32)(g_FastPos[pos >> 20] + 40);
+		}
+
+		static UInt32 GetPosSlot2(UInt32 pos)
+		{
+			if (pos < (1 << 17))
+				return (UInt32)(g_FastPos[pos >> 6] + 12);
+			if (pos < (1 << 27))
+				return (UInt32)(g_FastPos[pos >> 16] + 32);
+			return (UInt32)(g_FastPos[pos >> 26] + 52);
+		}
+
+		Base.State _state = new Base.State();
+		Byte _previousByte;
+		UInt32[] _repDistances = new UInt32[Base.kNumRepDistances];
+
+		void BaseInit()
+		{
+			_state.Init();
+			_previousByte = 0;
+			for (UInt32 i = 0; i < Base.kNumRepDistances; i++)
+				_repDistances[i] = 0;
+		}
+
+		const int kDefaultDictionaryLogSize = 22;
+		const UInt32 kNumFastBytesDefault = 0x20;
+
+		class LiteralEncoder
+		{
+			public struct Encoder2
+			{
+				BitEncoder[] m_Encoders;
+
+				public void Create() { m_Encoders = new BitEncoder[0x300]; }
+
+				public void Init() { for (int i = 0; i < 0x300; i++) m_Encoders[i].Init(); }
+
+				public void Encode(RangeCoder.Encoder rangeEncoder, byte symbol)
+				{
+					uint context = 1;
+					for (int i = 7; i >= 0; i--)
+					{
+						uint bit = (uint)((symbol >> i) & 1);
+						m_Encoders[context].Encode(rangeEncoder, bit);
+						context = (context << 1) | bit;
+					}
+				}
+
+				public void EncodeMatched(RangeCoder.Encoder rangeEncoder, byte matchByte, byte symbol)
+				{
+					uint context = 1;
+					bool same = true;
+					for (int i = 7; i >= 0; i--)
+					{
+						uint bit = (uint)((symbol >> i) & 1);
+						uint state = context;
+						if (same)
+						{
+							uint matchBit = (uint)((matchByte >> i) & 1);
+							state += ((1 + matchBit) << 8);
+							same = (matchBit == bit);
+						}
+						m_Encoders[state].Encode(rangeEncoder, bit);
+						context = (context << 1) | bit;
+					}
+				}
+
+				public uint GetPrice(bool matchMode, byte matchByte, byte symbol)
+				{
+					uint price = 0;
+					uint context = 1;
+					int i = 7;
+					if (matchMode)
+					{
+						for (; i >= 0; i--)
+						{
+							uint matchBit = (uint)(matchByte >> i) & 1;
+							uint bit = (uint)(symbol >> i) & 1;
+							price += m_Encoders[((1 + matchBit) << 8) + context].GetPrice(bit);
+							context = (context << 1) | bit;
+							if (matchBit != bit)
+							{
+								i--;
+								break;
+							}
+						}
+					}
+					for (; i >= 0; i--)
+					{
+						uint bit = (uint)(symbol >> i) & 1;
+						price += m_Encoders[context].GetPrice(bit);
+						context = (context << 1) | bit;
+					}
+					return price;
+				}
+			}
+
+			Encoder2[] m_Coders;
+			int m_NumPrevBits;
+			int m_NumPosBits;
+			uint m_PosMask;
+
+			public void Create(int numPosBits, int numPrevBits)
+			{
+				if (m_Coders != null && m_NumPrevBits == numPrevBits && m_NumPosBits == numPosBits)
+					return;
+				m_NumPosBits = numPosBits;
+				m_PosMask = ((uint)1 << numPosBits) - 1;
+				m_NumPrevBits = numPrevBits;
+				uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits);
+				m_Coders = new Encoder2[numStates];
+				for (uint i = 0; i < numStates; i++)
+					m_Coders[i].Create();
+			}
+
+			public void Init()
+			{
+				uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits);
+				for (uint i = 0; i < numStates; i++)
+					m_Coders[i].Init();
+			}
+
+			public Encoder2 GetSubCoder(UInt32 pos, Byte prevByte)
+			{ return m_Coders[((pos & m_PosMask) << m_NumPrevBits) + (uint)(prevByte >> (8 - m_NumPrevBits))]; }
+		}
+
+		class LenEncoder
+		{
+			RangeCoder.BitEncoder _choice = new RangeCoder.BitEncoder();
+			RangeCoder.BitEncoder _choice2 = new RangeCoder.BitEncoder();
+			RangeCoder.BitTreeEncoder[] _lowCoder = new RangeCoder.BitTreeEncoder[Base.kNumPosStatesEncodingMax];
+			RangeCoder.BitTreeEncoder[] _midCoder = new RangeCoder.BitTreeEncoder[Base.kNumPosStatesEncodingMax];
+			RangeCoder.BitTreeEncoder _highCoder = new RangeCoder.BitTreeEncoder(Base.kNumHighLenBits);
+
+			public LenEncoder()
+			{
+				for (UInt32 posState = 0; posState < Base.kNumPosStatesEncodingMax; posState++)
+				{
+					_lowCoder[posState] = new RangeCoder.BitTreeEncoder(Base.kNumLowLenBits);
+					_midCoder[posState] = new RangeCoder.BitTreeEncoder(Base.kNumMidLenBits);
+				}
+			}
+
+			public void Init(UInt32 numPosStates)
+			{
+				_choice.Init();
+				_choice2.Init();
+				for (UInt32 posState = 0; posState < numPosStates; posState++)
+				{
+					_lowCoder[posState].Init();
+					_midCoder[posState].Init();
+				}
+				_highCoder.Init();
+			}
+
+			public void Encode(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState)
+			{
+				if (symbol < Base.kNumLowLenSymbols)
+				{
+					_choice.Encode(rangeEncoder, 0);
+					_lowCoder[posState].Encode(rangeEncoder, symbol);
+				}
+				else
+				{
+					symbol -= Base.kNumLowLenSymbols;
+					_choice.Encode(rangeEncoder, 1);
+					if (symbol < Base.kNumMidLenSymbols)
+					{
+						_choice2.Encode(rangeEncoder, 0);
+						_midCoder[posState].Encode(rangeEncoder, symbol);
+					}
+					else
+					{
+						_choice2.Encode(rangeEncoder, 1);
+						_highCoder.Encode(rangeEncoder, symbol - Base.kNumMidLenSymbols);
+					}
+				}
+			}
+
+			public void SetPrices(UInt32 posState, UInt32 numSymbols, UInt32[] prices, UInt32 st)
+			{
+				UInt32 a0 = _choice.GetPrice0();
+				UInt32 a1 = _choice.GetPrice1();
+				UInt32 b0 = a1 + _choice2.GetPrice0();
+				UInt32 b1 = a1 + _choice2.GetPrice1();
+				UInt32 i = 0;
+				for (i = 0; i < Base.kNumLowLenSymbols; i++)
+				{
+					if (i >= numSymbols)
+						return;
+					prices[st + i] = a0 + _lowCoder[posState].GetPrice(i);
+				}
+				for (; i < Base.kNumLowLenSymbols + Base.kNumMidLenSymbols; i++)
+				{
+					if (i >= numSymbols)
+						return;
+					prices[st + i] = b0 + _midCoder[posState].GetPrice(i - Base.kNumLowLenSymbols);
+				}
+				for (; i < numSymbols; i++)
+					prices[st + i] = b1 + _highCoder.GetPrice(i - Base.kNumLowLenSymbols - Base.kNumMidLenSymbols);
+			}
+		};
+
+		const UInt32 kNumLenSpecSymbols = Base.kNumLowLenSymbols + Base.kNumMidLenSymbols;
+
+		class LenPriceTableEncoder : LenEncoder
+		{
+			UInt32[] _prices = new UInt32[Base.kNumLenSymbols << Base.kNumPosStatesBitsEncodingMax];
+			UInt32 _tableSize;
+			UInt32[] _counters = new UInt32[Base.kNumPosStatesEncodingMax];
+
+			public void SetTableSize(UInt32 tableSize) { _tableSize = tableSize; }
+
+			public UInt32 GetPrice(UInt32 symbol, UInt32 posState)
+			{
+				return _prices[posState * Base.kNumLenSymbols + symbol];
+			}
+
+			void UpdateTable(UInt32 posState)
+			{
+				SetPrices(posState, _tableSize, _prices, posState * Base.kNumLenSymbols);
+				_counters[posState] = _tableSize;
+			}
+
+			public void UpdateTables(UInt32 numPosStates)
+			{
+				for (UInt32 posState = 0; posState < numPosStates; posState++)
+					UpdateTable(posState);
+			}
+
+			public new void Encode(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState)
+			{
+				base.Encode(rangeEncoder, symbol, posState);
+				if (--_counters[posState] == 0)
+					UpdateTable(posState);
+			}
+		}
+
+		const UInt32 kNumOpts = 1 << 12;
+		class Optimal
+		{
+			public Base.State State;
+
+			public bool Prev1IsChar;
+			public bool Prev2;
+
+			public UInt32 PosPrev2;
+			public UInt32 BackPrev2;
+
+			public UInt32 Price;
+			public UInt32 PosPrev;
+			public UInt32 BackPrev;
+
+			public UInt32 Backs0;
+			public UInt32 Backs1;
+			public UInt32 Backs2;
+			public UInt32 Backs3;
+
+			public void MakeAsChar() { BackPrev = 0xFFFFFFFF; Prev1IsChar = false; }
+			public void MakeAsShortRep() { BackPrev = 0; ; Prev1IsChar = false; }
+			public bool IsShortRep() { return (BackPrev == 0); }
+		};
+		Optimal[] _optimum = new Optimal[kNumOpts];
+		LZ.IMatchFinder _matchFinder = null;
+		RangeCoder.Encoder _rangeEncoder = new RangeCoder.Encoder();
+
+		RangeCoder.BitEncoder[] _isMatch = new RangeCoder.BitEncoder[Base.kNumStates << Base.kNumPosStatesBitsMax];
+		RangeCoder.BitEncoder[] _isRep = new RangeCoder.BitEncoder[Base.kNumStates];
+		RangeCoder.BitEncoder[] _isRepG0 = new RangeCoder.BitEncoder[Base.kNumStates];
+		RangeCoder.BitEncoder[] _isRepG1 = new RangeCoder.BitEncoder[Base.kNumStates];
+		RangeCoder.BitEncoder[] _isRepG2 = new RangeCoder.BitEncoder[Base.kNumStates];
+		RangeCoder.BitEncoder[] _isRep0Long = new RangeCoder.BitEncoder[Base.kNumStates << Base.kNumPosStatesBitsMax];
+
+		RangeCoder.BitTreeEncoder[] _posSlotEncoder = new RangeCoder.BitTreeEncoder[Base.kNumLenToPosStates];
+		
+		RangeCoder.BitEncoder[] _posEncoders = new RangeCoder.BitEncoder[Base.kNumFullDistances - Base.kEndPosModelIndex];
+		RangeCoder.BitTreeEncoder _posAlignEncoder = new RangeCoder.BitTreeEncoder(Base.kNumAlignBits);
+
+		LenPriceTableEncoder _lenEncoder = new LenPriceTableEncoder();
+		LenPriceTableEncoder _repMatchLenEncoder = new LenPriceTableEncoder();
+
+		LiteralEncoder _literalEncoder = new LiteralEncoder();
+
+		UInt32[] _matchDistances = new UInt32[Base.kMatchMaxLen * 2 + 2];
+		
+		UInt32 _numFastBytes = kNumFastBytesDefault;
+		UInt32 _longestMatchLength;
+		UInt32 _numDistancePairs;
+
+		UInt32 _additionalOffset;
+
+		UInt32 _optimumEndIndex;
+		UInt32 _optimumCurrentIndex;
+
+		bool _longestMatchWasFound;
+
+		UInt32[] _posSlotPrices = new UInt32[1 << (Base.kNumPosSlotBits + Base.kNumLenToPosStatesBits)];
+		UInt32[] _distancesPrices = new UInt32[Base.kNumFullDistances << Base.kNumLenToPosStatesBits];
+		UInt32[] _alignPrices = new UInt32[Base.kAlignTableSize];
+		UInt32 _alignPriceCount;
+
+		UInt32 _distTableSize = (kDefaultDictionaryLogSize * 2);
+
+		int _posStateBits = 2;
+		UInt32 _posStateMask = (4 - 1);
+		int _numLiteralPosStateBits = 0;
+		int _numLiteralContextBits = 3;
+
+		UInt32 _dictionarySize = (1 << kDefaultDictionaryLogSize);
+		UInt32 _dictionarySizePrev = 0xFFFFFFFF;
+		UInt32 _numFastBytesPrev = 0xFFFFFFFF;
+
+		Int64 nowPos64;
+		bool _finished;
+		System.IO.Stream _inStream;
+
+		EMatchFinderType _matchFinderType = EMatchFinderType.BT4;
+		bool _writeEndMark = false;
+		
+		bool _needReleaseMFStream;
+
+		void Create()
+		{
+			if (_matchFinder == null)
+			{
+				LZ.BinTree bt = new LZ.BinTree();
+				int numHashBytes = 4;
+				if (_matchFinderType == EMatchFinderType.BT2)
+					numHashBytes = 2;
+				bt.SetType(numHashBytes);
+				_matchFinder = bt;
+			}
+			_literalEncoder.Create(_numLiteralPosStateBits, _numLiteralContextBits);
+
+			if (_dictionarySize == _dictionarySizePrev && _numFastBytesPrev == _numFastBytes)
+				return;
+			_matchFinder.Create(_dictionarySize, kNumOpts, _numFastBytes, Base.kMatchMaxLen + 1);
+			_dictionarySizePrev = _dictionarySize;
+			_numFastBytesPrev = _numFastBytes;
+		}
+
+		public Encoder()
+		{
+			for (int i = 0; i < kNumOpts; i++)
+				_optimum[i] = new Optimal();
+			for (int i = 0; i < Base.kNumLenToPosStates; i++)
+				_posSlotEncoder[i] = new RangeCoder.BitTreeEncoder(Base.kNumPosSlotBits);
+		}
+
+		void SetWriteEndMarkerMode(bool writeEndMarker)
+		{
+			_writeEndMark = writeEndMarker;
+		}
+
+		void Init()
+		{
+			BaseInit();
+			_rangeEncoder.Init();
+
+			uint i;
+			for (i = 0; i < Base.kNumStates; i++)
+			{
+				for (uint j = 0; j <= _posStateMask; j++)
+				{
+					uint complexState = (i << Base.kNumPosStatesBitsMax) + j;
+					_isMatch[complexState].Init();
+					_isRep0Long[complexState].Init();
+				}
+				_isRep[i].Init();
+				_isRepG0[i].Init();
+				_isRepG1[i].Init();
+				_isRepG2[i].Init();
+			}
+			_literalEncoder.Init();
+			for (i = 0; i < Base.kNumLenToPosStates; i++)
+				_posSlotEncoder[i].Init();
+			for (i = 0; i < Base.kNumFullDistances - Base.kEndPosModelIndex; i++)
+				_posEncoders[i].Init();
+
+			_lenEncoder.Init((UInt32)1 << _posStateBits);
+			_repMatchLenEncoder.Init((UInt32)1 << _posStateBits);
+
+			_posAlignEncoder.Init();
+
+			_longestMatchWasFound = false;
+			_optimumEndIndex = 0;
+			_optimumCurrentIndex = 0;
+			_additionalOffset = 0;
+		}
+
+		void ReadMatchDistances(out UInt32 lenRes, out UInt32 numDistancePairs)
+		{
+			lenRes = 0;
+			numDistancePairs = _matchFinder.GetMatches(_matchDistances);
+			if (numDistancePairs > 0)
+			{
+				lenRes = _matchDistances[numDistancePairs - 2];
+				if (lenRes == _numFastBytes)
+					lenRes += _matchFinder.GetMatchLen((int)lenRes - 1, _matchDistances[numDistancePairs - 1],
+						Base.kMatchMaxLen - lenRes);
+			}
+			_additionalOffset++;
+		}
+
+
+		void MovePos(UInt32 num)
+		{
+			if (num > 0)
+			{
+				_matchFinder.Skip(num);
+				_additionalOffset += num;
+			}
+		}
+
+		UInt32 GetRepLen1Price(Base.State state, UInt32 posState)
+		{
+			return _isRepG0[state.Index].GetPrice0() +
+					_isRep0Long[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0();
+		}
+
+		UInt32 GetPureRepPrice(UInt32 repIndex, Base.State state, UInt32 posState)
+		{
+			UInt32 price;
+			if (repIndex == 0)
+			{
+				price = _isRepG0[state.Index].GetPrice0();
+				price += _isRep0Long[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1();
+			}
+			else
+			{
+				price = _isRepG0[state.Index].GetPrice1();
+				if (repIndex == 1)
+					price += _isRepG1[state.Index].GetPrice0();
+				else
+				{
+					price += _isRepG1[state.Index].GetPrice1();
+					price += _isRepG2[state.Index].GetPrice(repIndex - 2);
+				}
+			}
+			return price;
+		}
+
+		UInt32 GetRepPrice(UInt32 repIndex, UInt32 len, Base.State state, UInt32 posState)
+		{
+			UInt32 price = _repMatchLenEncoder.GetPrice(len - Base.kMatchMinLen, posState);
+			return price + GetPureRepPrice(repIndex, state, posState);
+		}
+	
+		UInt32 GetPosLenPrice(UInt32 pos, UInt32 len, UInt32 posState)
+		{
+			UInt32 price;
+			UInt32 lenToPosState = Base.GetLenToPosState(len);
+			if (pos < Base.kNumFullDistances)
+				price = _distancesPrices[(lenToPosState * Base.kNumFullDistances) + pos];
+			else
+				price = _posSlotPrices[(lenToPosState << Base.kNumPosSlotBits) + GetPosSlot2(pos)] +
+					_alignPrices[pos & Base.kAlignMask];
+			return price + _lenEncoder.GetPrice(len - Base.kMatchMinLen, posState);
+		}
+
+		UInt32 Backward(out UInt32 backRes, UInt32 cur)
+		{
+			_optimumEndIndex = cur;
+			UInt32 posMem = _optimum[cur].PosPrev;
+			UInt32 backMem = _optimum[cur].BackPrev;
+			do
+			{
+				if (_optimum[cur].Prev1IsChar)
+				{
+					_optimum[posMem].MakeAsChar();
+					_optimum[posMem].PosPrev = posMem - 1;
+					if (_optimum[cur].Prev2)
+					{
+						_optimum[posMem - 1].Prev1IsChar = false;
+						_optimum[posMem - 1].PosPrev = _optimum[cur].PosPrev2;
+						_optimum[posMem - 1].BackPrev = _optimum[cur].BackPrev2;
+					}
+				}
+				UInt32 posPrev = posMem;
+				UInt32 backCur = backMem;
+
+				backMem = _optimum[posPrev].BackPrev;
+				posMem = _optimum[posPrev].PosPrev;
+
+				_optimum[posPrev].BackPrev = backCur;
+				_optimum[posPrev].PosPrev = cur;
+				cur = posPrev;
+			}
+			while (cur > 0);
+			backRes = _optimum[0].BackPrev;
+			_optimumCurrentIndex = _optimum[0].PosPrev;
+			return _optimumCurrentIndex;
+		}
+
+		UInt32[] reps = new UInt32[Base.kNumRepDistances];
+		UInt32[] repLens = new UInt32[Base.kNumRepDistances];
+
+
+		UInt32 GetOptimum(UInt32 position, out UInt32 backRes)
+		{
+			if (_optimumEndIndex != _optimumCurrentIndex)
+			{
+				UInt32 lenRes = _optimum[_optimumCurrentIndex].PosPrev - _optimumCurrentIndex;
+				backRes = _optimum[_optimumCurrentIndex].BackPrev;
+				_optimumCurrentIndex = _optimum[_optimumCurrentIndex].PosPrev;
+				return lenRes;
+			}
+			_optimumCurrentIndex = _optimumEndIndex = 0;
+
+			UInt32 lenMain, numDistancePairs;
+			if (!_longestMatchWasFound)
+			{
+				ReadMatchDistances(out lenMain, out numDistancePairs);
+			}
+			else
+			{
+				lenMain = _longestMatchLength;
+				numDistancePairs = _numDistancePairs;
+				_longestMatchWasFound = false;
+			}
+
+			UInt32 numAvailableBytes = _matchFinder.GetNumAvailableBytes() + 1;
+			if (numAvailableBytes < 2)
+			{
+				backRes = 0xFFFFFFFF;
+				return 1;
+			}
+			if (numAvailableBytes > Base.kMatchMaxLen)
+				numAvailableBytes = Base.kMatchMaxLen;
+
+			UInt32 repMaxIndex = 0;
+			UInt32 i;			
+			for (i = 0; i < Base.kNumRepDistances; i++)
+			{
+				reps[i] = _repDistances[i];
+				repLens[i] = _matchFinder.GetMatchLen(0 - 1, reps[i], Base.kMatchMaxLen);
+				if (repLens[i] > repLens[repMaxIndex])
+					repMaxIndex = i;
+			}
+			if (repLens[repMaxIndex] >= _numFastBytes)
+			{
+				backRes = repMaxIndex;
+				UInt32 lenRes = repLens[repMaxIndex];
+				MovePos(lenRes - 1);
+				return lenRes;
+			}
+
+			if (lenMain >= _numFastBytes)
+			{
+				backRes = _matchDistances[numDistancePairs - 1] + Base.kNumRepDistances;
+				MovePos(lenMain - 1);
+				return lenMain;
+			}
+			
+			Byte currentByte = _matchFinder.GetIndexByte(0 - 1);
+			Byte matchByte = _matchFinder.GetIndexByte((Int32)(0 - _repDistances[0] - 1 - 1));
+
+			if (lenMain < 2 && currentByte != matchByte && repLens[repMaxIndex] < 2)
+			{
+				backRes = (UInt32)0xFFFFFFFF;
+				return 1;
+			}
+
+			_optimum[0].State = _state;
+
+			UInt32 posState = (position & _posStateMask);
+
+			_optimum[1].Price = _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0() +
+					_literalEncoder.GetSubCoder(position, _previousByte).GetPrice(!_state.IsCharState(), matchByte, currentByte);
+			_optimum[1].MakeAsChar();
+
+			UInt32 matchPrice = _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1();
+			UInt32 repMatchPrice = matchPrice + _isRep[_state.Index].GetPrice1();
+
+			if (matchByte == currentByte)
+			{
+				UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(_state, posState);
+				if (shortRepPrice < _optimum[1].Price)
+				{
+					_optimum[1].Price = shortRepPrice;
+					_optimum[1].MakeAsShortRep();
+				}
+			}
+
+			UInt32 lenEnd = ((lenMain >= repLens[repMaxIndex]) ? lenMain : repLens[repMaxIndex]);
+
+			if(lenEnd < 2)
+			{
+				backRes = _optimum[1].BackPrev;
+				return 1;
+			}
+			
+			_optimum[1].PosPrev = 0;
+
+			_optimum[0].Backs0 = reps[0];
+			_optimum[0].Backs1 = reps[1];
+			_optimum[0].Backs2 = reps[2];
+			_optimum[0].Backs3 = reps[3];
+
+			UInt32 len = lenEnd;
+			do
+				_optimum[len--].Price = kIfinityPrice;
+			while (len >= 2);
+
+			for (i = 0; i < Base.kNumRepDistances; i++)
+			{
+				UInt32 repLen = repLens[i];
+				if (repLen < 2)
+					continue;
+				UInt32 price = repMatchPrice + GetPureRepPrice(i, _state, posState);
+				do
+				{
+					UInt32 curAndLenPrice = price + _repMatchLenEncoder.GetPrice(repLen - 2, posState);
+					Optimal optimum = _optimum[repLen];
+					if (curAndLenPrice < optimum.Price)
+					{
+						optimum.Price = curAndLenPrice;
+						optimum.PosPrev = 0;
+						optimum.BackPrev = i;
+						optimum.Prev1IsChar = false;
+					}
+				}
+				while (--repLen >= 2);
+			}
+
+			UInt32 normalMatchPrice = matchPrice + _isRep[_state.Index].GetPrice0();
+			
+			len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2);
+			if (len <= lenMain)
+			{
+				UInt32 offs = 0;
+				while (len > _matchDistances[offs])
+					offs += 2;
+				for (; ; len++)
+				{
+					UInt32 distance = _matchDistances[offs + 1];
+					UInt32 curAndLenPrice = normalMatchPrice + GetPosLenPrice(distance, len, posState);
+					Optimal optimum = _optimum[len];
+					if (curAndLenPrice < optimum.Price)
+					{
+						optimum.Price = curAndLenPrice;
+						optimum.PosPrev = 0;
+						optimum.BackPrev = distance + Base.kNumRepDistances;
+						optimum.Prev1IsChar = false;
+					}
+					if (len == _matchDistances[offs])
+					{
+						offs += 2;
+						if (offs == numDistancePairs)
+							break;
+					}
+				}
+			}
+
+			UInt32 cur = 0;
+
+			while (true)
+			{
+				cur++;
+				if (cur == lenEnd)
+					return Backward(out backRes, cur);
+				UInt32 newLen;
+				ReadMatchDistances(out newLen, out numDistancePairs);
+				if (newLen >= _numFastBytes)
+				{
+					_numDistancePairs = numDistancePairs;
+					_longestMatchLength = newLen;
+					_longestMatchWasFound = true;
+					return Backward(out backRes, cur);
+				}
+				position++;
+				UInt32 posPrev = _optimum[cur].PosPrev;
+				Base.State state;
+				if (_optimum[cur].Prev1IsChar)
+				{
+					posPrev--;
+					if (_optimum[cur].Prev2)
+					{
+						state = _optimum[_optimum[cur].PosPrev2].State;
+						if (_optimum[cur].BackPrev2 < Base.kNumRepDistances)
+							state.UpdateRep();
+						else
+							state.UpdateMatch();
+					}
+					else
+						state = _optimum[posPrev].State;
+					state.UpdateChar();
+				}
+				else
+					state = _optimum[posPrev].State;
+				if (posPrev == cur - 1)
+				{
+					if (_optimum[cur].IsShortRep())
+						state.UpdateShortRep();
+					else
+						state.UpdateChar();
+				}
+				else
+				{
+					UInt32 pos;
+					if (_optimum[cur].Prev1IsChar && _optimum[cur].Prev2)
+					{
+						posPrev = _optimum[cur].PosPrev2;
+						pos = _optimum[cur].BackPrev2;
+						state.UpdateRep();
+					}
+					else
+					{
+						pos = _optimum[cur].BackPrev;
+						if (pos < Base.kNumRepDistances)
+							state.UpdateRep();
+						else
+							state.UpdateMatch();
+					}
+					Optimal opt = _optimum[posPrev];
+					if (pos < Base.kNumRepDistances)
+					{
+						if (pos == 0)
+						{
+							reps[0] = opt.Backs0;
+							reps[1] = opt.Backs1;
+							reps[2] = opt.Backs2;
+							reps[3] = opt.Backs3;
+						}
+						else if (pos == 1)
+						{
+							reps[0] = opt.Backs1;
+							reps[1] = opt.Backs0;
+							reps[2] = opt.Backs2;
+							reps[3] = opt.Backs3;
+						}
+						else if (pos == 2)
+						{
+							reps[0] = opt.Backs2;
+							reps[1] = opt.Backs0;
+							reps[2] = opt.Backs1;
+							reps[3] = opt.Backs3;
+						}
+						else
+						{
+							reps[0] = opt.Backs3;
+							reps[1] = opt.Backs0;
+							reps[2] = opt.Backs1;
+							reps[3] = opt.Backs2;
+						}
+					}
+					else
+					{
+						reps[0] = (pos - Base.kNumRepDistances);
+						reps[1] = opt.Backs0;
+						reps[2] = opt.Backs1;
+						reps[3] = opt.Backs2;
+					}
+				}
+				_optimum[cur].State = state;
+				_optimum[cur].Backs0 = reps[0];
+				_optimum[cur].Backs1 = reps[1];
+				_optimum[cur].Backs2 = reps[2];
+				_optimum[cur].Backs3 = reps[3];
+				UInt32 curPrice = _optimum[cur].Price;
+
+				currentByte = _matchFinder.GetIndexByte(0 - 1);
+				matchByte = _matchFinder.GetIndexByte((Int32)(0 - reps[0] - 1 - 1));
+
+				posState = (position & _posStateMask);
+
+				UInt32 curAnd1Price = curPrice +
+					_isMatch[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0() +
+					_literalEncoder.GetSubCoder(position, _matchFinder.GetIndexByte(0 - 2)).
+					GetPrice(!state.IsCharState(), matchByte, currentByte);
+
+				Optimal nextOptimum = _optimum[cur + 1];
+
+				bool nextIsChar = false;
+				if (curAnd1Price < nextOptimum.Price)
+				{
+					nextOptimum.Price = curAnd1Price;
+					nextOptimum.PosPrev = cur;
+					nextOptimum.MakeAsChar();
+					nextIsChar = true;
+				}
+
+				matchPrice = curPrice + _isMatch[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1();
+				repMatchPrice = matchPrice + _isRep[state.Index].GetPrice1();
+
+				if (matchByte == currentByte &&
+					!(nextOptimum.PosPrev < cur && nextOptimum.BackPrev == 0))
+				{
+					UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(state, posState);
+					if (shortRepPrice <= nextOptimum.Price)
+					{
+						nextOptimum.Price = shortRepPrice;
+						nextOptimum.PosPrev = cur;
+						nextOptimum.MakeAsShortRep();
+						nextIsChar = true;
+					}
+				}
+
+				UInt32 numAvailableBytesFull = _matchFinder.GetNumAvailableBytes() + 1;
+				numAvailableBytesFull = Math.Min(kNumOpts - 1 - cur, numAvailableBytesFull);
+				numAvailableBytes = numAvailableBytesFull;
+
+				if (numAvailableBytes < 2)
+					continue;
+				if (numAvailableBytes > _numFastBytes)
+					numAvailableBytes = _numFastBytes;
+				if (!nextIsChar && matchByte != currentByte)
+				{
+					// try Literal + rep0
+					UInt32 t = Math.Min(numAvailableBytesFull - 1, _numFastBytes);
+					UInt32 lenTest2 = _matchFinder.GetMatchLen(0, reps[0], t);
+					if (lenTest2 >= 2)
+					{
+						Base.State state2 = state;
+						state2.UpdateChar();
+						UInt32 posStateNext = (position + 1) & _posStateMask;
+						UInt32 nextRepMatchPrice = curAnd1Price +
+							_isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1() +
+							_isRep[state2.Index].GetPrice1();
+						{
+							UInt32 offset = cur + 1 + lenTest2;
+							while (lenEnd < offset)
+								_optimum[++lenEnd].Price = kIfinityPrice;
+							UInt32 curAndLenPrice = nextRepMatchPrice + GetRepPrice(
+								0, lenTest2, state2, posStateNext);
+							Optimal optimum = _optimum[offset];
+							if (curAndLenPrice < optimum.Price)
+							{
+								optimum.Price = curAndLenPrice;
+								optimum.PosPrev = cur + 1;
+								optimum.BackPrev = 0;
+								optimum.Prev1IsChar = true;
+								optimum.Prev2 = false;
+							}
+						}
+					}
+				}
+
+				UInt32 startLen = 2; // speed optimization 
+
+				for (UInt32 repIndex = 0; repIndex < Base.kNumRepDistances; repIndex++)
+				{
+					UInt32 lenTest = _matchFinder.GetMatchLen(0 - 1, reps[repIndex], numAvailableBytes);
+					if (lenTest < 2)
+						continue;
+					UInt32 lenTestTemp = lenTest;
+					do
+					{
+						while (lenEnd < cur + lenTest)
+							_optimum[++lenEnd].Price = kIfinityPrice;
+						UInt32 curAndLenPrice = repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState);
+						Optimal optimum = _optimum[cur + lenTest];
+						if (curAndLenPrice < optimum.Price)
+						{
+							optimum.Price = curAndLenPrice;
+							optimum.PosPrev = cur;
+							optimum.BackPrev = repIndex;
+							optimum.Prev1IsChar = false;
+						}
+					}
+					while(--lenTest >= 2);
+					lenTest = lenTestTemp;
+
+					if (repIndex == 0)
+						startLen = lenTest + 1;
+
+					// if (_maxMode)
+					if (lenTest < numAvailableBytesFull)
+					{
+						UInt32 t = Math.Min(numAvailableBytesFull - 1 - lenTest, _numFastBytes);
+						UInt32 lenTest2 = _matchFinder.GetMatchLen((Int32)lenTest, reps[repIndex], t);
+						if (lenTest2 >= 2)
+						{
+							Base.State state2 = state;
+							state2.UpdateRep();
+							UInt32 posStateNext = (position + lenTest) & _posStateMask;
+							UInt32 curAndLenCharPrice = 
+									repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState) + 
+									_isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice0() +
+									_literalEncoder.GetSubCoder(position + lenTest, 
+									_matchFinder.GetIndexByte((Int32)lenTest - 1 - 1)).GetPrice(true,
+									_matchFinder.GetIndexByte((Int32)((Int32)lenTest - 1 - (Int32)(reps[repIndex] + 1))), 
+									_matchFinder.GetIndexByte((Int32)lenTest - 1));
+							state2.UpdateChar();
+							posStateNext = (position + lenTest + 1) & _posStateMask;
+							UInt32 nextMatchPrice = curAndLenCharPrice + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1();
+							UInt32 nextRepMatchPrice = nextMatchPrice + _isRep[state2.Index].GetPrice1();
+							
+							// for(; lenTest2 >= 2; lenTest2--)
+							{
+								UInt32 offset = lenTest + 1 + lenTest2;
+								while(lenEnd < cur + offset)
+									_optimum[++lenEnd].Price = kIfinityPrice;
+								UInt32 curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext);
+								Optimal optimum = _optimum[cur + offset];
+								if (curAndLenPrice < optimum.Price) 
+								{
+									optimum.Price = curAndLenPrice;
+									optimum.PosPrev = cur + lenTest + 1;
+									optimum.BackPrev = 0;
+									optimum.Prev1IsChar = true;
+									optimum.Prev2 = true;
+									optimum.PosPrev2 = cur;
+									optimum.BackPrev2 = repIndex;
+								}
+							}
+						}
+					}
+				}
+
+				if (newLen > numAvailableBytes)
+				{
+					newLen = numAvailableBytes;
+					for (numDistancePairs = 0; newLen > _matchDistances[numDistancePairs]; numDistancePairs += 2) ;
+					_matchDistances[numDistancePairs] = newLen;
+					numDistancePairs += 2;
+				}
+				if (newLen >= startLen)
+				{
+					normalMatchPrice = matchPrice + _isRep[state.Index].GetPrice0();
+					while (lenEnd < cur + newLen)
+						_optimum[++lenEnd].Price = kIfinityPrice;
+
+					UInt32 offs = 0;
+					while (startLen > _matchDistances[offs])
+						offs += 2;
+
+					for (UInt32 lenTest = startLen; ; lenTest++)
+					{
+						UInt32 curBack = _matchDistances[offs + 1];
+						UInt32 curAndLenPrice = normalMatchPrice + GetPosLenPrice(curBack, lenTest, posState);
+						Optimal optimum = _optimum[cur + lenTest];
+						if (curAndLenPrice < optimum.Price)
+						{
+							optimum.Price = curAndLenPrice;
+							optimum.PosPrev = cur;
+							optimum.BackPrev = curBack + Base.kNumRepDistances;
+							optimum.Prev1IsChar = false;
+						}
+
+						if (lenTest == _matchDistances[offs])
+						{
+							if (lenTest < numAvailableBytesFull)
+							{
+								UInt32 t = Math.Min(numAvailableBytesFull - 1 - lenTest, _numFastBytes);
+								UInt32 lenTest2 = _matchFinder.GetMatchLen((Int32)lenTest, curBack, t);
+								if (lenTest2 >= 2)
+								{
+									Base.State state2 = state;
+									state2.UpdateMatch();
+									UInt32 posStateNext = (position + lenTest) & _posStateMask;
+									UInt32 curAndLenCharPrice = curAndLenPrice +
+										_isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice0() +
+										_literalEncoder.GetSubCoder(position + lenTest,
+										_matchFinder.GetIndexByte((Int32)lenTest - 1 - 1)).
+										GetPrice(true,
+										_matchFinder.GetIndexByte((Int32)lenTest - (Int32)(curBack + 1) - 1),
+										_matchFinder.GetIndexByte((Int32)lenTest - 1));
+									state2.UpdateChar();
+									posStateNext = (position + lenTest + 1) & _posStateMask;
+									UInt32 nextMatchPrice = curAndLenCharPrice + _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice1();
+									UInt32 nextRepMatchPrice = nextMatchPrice + _isRep[state2.Index].GetPrice1();
+
+									UInt32 offset = lenTest + 1 + lenTest2;
+									while (lenEnd < cur + offset)
+										_optimum[++lenEnd].Price = kIfinityPrice;
+									curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext);
+									optimum = _optimum[cur + offset];
+									if (curAndLenPrice < optimum.Price)
+									{
+										optimum.Price = curAndLenPrice;
+										optimum.PosPrev = cur + lenTest + 1;
+										optimum.BackPrev = 0;
+										optimum.Prev1IsChar = true;
+										optimum.Prev2 = true;
+										optimum.PosPrev2 = cur;
+										optimum.BackPrev2 = curBack + Base.kNumRepDistances;
+									}
+								}
+							}
+							offs += 2;
+							if (offs == numDistancePairs)
+								break;
+						}
+					}
+				}
+			}
+		}
+
+		bool ChangePair(UInt32 smallDist, UInt32 bigDist)
+		{
+			const int kDif = 7;
+			return (smallDist < ((UInt32)(1) << (32 - kDif)) && bigDist >= (smallDist << kDif));
+		}
+
+		void WriteEndMarker(UInt32 posState)
+		{
+			if (!_writeEndMark)
+				return;
+
+			_isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].Encode(_rangeEncoder, 1);
+			_isRep[_state.Index].Encode(_rangeEncoder, 0);
+			_state.UpdateMatch();
+			UInt32 len = Base.kMatchMinLen;
+			_lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState);
+			UInt32 posSlot = (1 << Base.kNumPosSlotBits) - 1;
+			UInt32 lenToPosState = Base.GetLenToPosState(len);
+			_posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot);
+			int footerBits = 30;
+			UInt32 posReduced = (((UInt32)1) << footerBits) - 1;
+			_rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits);
+			_posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask);
+		}
+
+		void Flush(UInt32 nowPos)
+		{
+			ReleaseMFStream();
+			WriteEndMarker(nowPos & _posStateMask);
+			_rangeEncoder.FlushData();
+			_rangeEncoder.FlushStream();
+		}
+
+		public void CodeOneBlock(out Int64 inSize, out Int64 outSize, out bool finished)
+		{
+			inSize = 0;
+			outSize = 0;
+			finished = true;
+
+			if (_inStream != null)
+			{
+				_matchFinder.SetStream(_inStream);
+				_matchFinder.Init();
+				_needReleaseMFStream = true;
+				_inStream = null;
+				if (_trainSize > 0)
+					_matchFinder.Skip(_trainSize);
+			}
+
+			if (_finished)
+				return;
+			_finished = true;
+
+
+			Int64 progressPosValuePrev = nowPos64;
+			if (nowPos64 == 0)
+			{
+				if (_matchFinder.GetNumAvailableBytes() == 0)
+				{
+					Flush((UInt32)nowPos64);
+					return;
+				}
+				UInt32 len, numDistancePairs; // it's not used
+				ReadMatchDistances(out len, out numDistancePairs);
+				UInt32 posState = (UInt32)(nowPos64) & _posStateMask;
+				_isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].Encode(_rangeEncoder, 0);
+				_state.UpdateChar();
+				Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset));
+				_literalEncoder.GetSubCoder((UInt32)(nowPos64), _previousByte).Encode(_rangeEncoder, curByte);
+				_previousByte = curByte;
+				_additionalOffset--;
+				nowPos64++;
+			}
+			if (_matchFinder.GetNumAvailableBytes() == 0)
+			{
+				Flush((UInt32)nowPos64);
+				return;
+			}
+			while (true)
+			{
+				UInt32 pos;
+				UInt32 len = GetOptimum((UInt32)nowPos64, out pos);
+				
+				UInt32 posState = ((UInt32)nowPos64) & _posStateMask;
+				UInt32 complexState = (_state.Index << Base.kNumPosStatesBitsMax) + posState;
+				if (len == 1 && pos == 0xFFFFFFFF)
+				{
+					_isMatch[complexState].Encode(_rangeEncoder, 0);
+					Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset));
+					LiteralEncoder.Encoder2 subCoder = _literalEncoder.GetSubCoder((UInt32)nowPos64, _previousByte);
+					if (!_state.IsCharState())
+					{
+						Byte matchByte = _matchFinder.GetIndexByte((Int32)(0 - _repDistances[0] - 1 - _additionalOffset));
+						subCoder.EncodeMatched(_rangeEncoder, matchByte, curByte);
+					}
+					else
+						subCoder.Encode(_rangeEncoder, curByte);
+					_previousByte = curByte;
+					_state.UpdateChar();
+				}
+				else
+				{
+					_isMatch[complexState].Encode(_rangeEncoder, 1);
+					if (pos < Base.kNumRepDistances)
+					{
+						_isRep[_state.Index].Encode(_rangeEncoder, 1);
+						if (pos == 0)
+						{
+							_isRepG0[_state.Index].Encode(_rangeEncoder, 0);
+							if (len == 1)
+								_isRep0Long[complexState].Encode(_rangeEncoder, 0);
+							else
+								_isRep0Long[complexState].Encode(_rangeEncoder, 1);
+						}
+						else
+						{
+							_isRepG0[_state.Index].Encode(_rangeEncoder, 1);
+							if (pos == 1)
+								_isRepG1[_state.Index].Encode(_rangeEncoder, 0);
+							else
+							{
+								_isRepG1[_state.Index].Encode(_rangeEncoder, 1);
+								_isRepG2[_state.Index].Encode(_rangeEncoder, pos - 2);
+							}
+						}
+						if (len == 1)
+							_state.UpdateShortRep();
+						else
+						{
+							_repMatchLenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState);
+							_state.UpdateRep();
+						}
+						UInt32 distance = _repDistances[pos];
+						if (pos != 0)
+						{
+							for (UInt32 i = pos; i >= 1; i--)
+								_repDistances[i] = _repDistances[i - 1];
+							_repDistances[0] = distance;
+						}
+					}
+					else
+					{
+						_isRep[_state.Index].Encode(_rangeEncoder, 0);
+						_state.UpdateMatch();
+						_lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState);
+						pos -= Base.kNumRepDistances;
+						UInt32 posSlot = GetPosSlot(pos);
+						UInt32 lenToPosState = Base.GetLenToPosState(len);
+						_posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot);
+
+						if (posSlot >= Base.kStartPosModelIndex)
+						{
+							int footerBits = (int)((posSlot >> 1) - 1);
+							UInt32 baseVal = ((2 | (posSlot & 1)) << footerBits);
+							UInt32 posReduced = pos - baseVal;
+
+							if (posSlot < Base.kEndPosModelIndex)
+								RangeCoder.BitTreeEncoder.ReverseEncode(_posEncoders,
+										baseVal - posSlot - 1, _rangeEncoder, footerBits, posReduced);
+							else
+							{
+								_rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits);
+								_posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask);
+								_alignPriceCount++;
+							}
+						}
+						UInt32 distance = pos;
+						for (UInt32 i = Base.kNumRepDistances - 1; i >= 1; i--)
+							_repDistances[i] = _repDistances[i - 1];
+						_repDistances[0] = distance;
+						_matchPriceCount++;
+					}
+					_previousByte = _matchFinder.GetIndexByte((Int32)(len - 1 - _additionalOffset));
+				}
+				_additionalOffset -= len;
+				nowPos64 += len;
+				if (_additionalOffset == 0)
+				{
+					// if (!_fastMode)
+					if (_matchPriceCount >= (1 << 7))
+						FillDistancesPrices();
+					if (_alignPriceCount >= Base.kAlignTableSize)
+						FillAlignPrices();
+					inSize = nowPos64;
+					outSize = _rangeEncoder.GetProcessedSizeAdd();
+					if (_matchFinder.GetNumAvailableBytes() == 0)
+					{
+						Flush((UInt32)nowPos64);
+						return;
+					}
+
+					if (nowPos64 - progressPosValuePrev >= (1 << 12))
+					{
+						_finished = false;
+						finished = false;
+						return;
+					}
+				}
+			}
+		}
+
+		void ReleaseMFStream()
+		{
+			if (_matchFinder != null && _needReleaseMFStream)
+			{
+				_matchFinder.ReleaseStream();
+				_needReleaseMFStream = false;
+			}
+		}
+
+		void SetOutStream(System.IO.Stream outStream) { _rangeEncoder.SetStream(outStream); }
+		void ReleaseOutStream() { _rangeEncoder.ReleaseStream(); }
+
+		void ReleaseStreams()
+		{
+			ReleaseMFStream();
+			ReleaseOutStream();
+		}
+
+		void SetStreams(System.IO.Stream inStream, System.IO.Stream outStream,
+				Int64 inSize, Int64 outSize)
+		{
+			_inStream = inStream;
+			_finished = false;
+			Create();
+			SetOutStream(outStream);
+			Init();
+
+			// if (!_fastMode)
+			{
+				FillDistancesPrices();
+				FillAlignPrices();
+			}
+
+			_lenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen);
+			_lenEncoder.UpdateTables((UInt32)1 << _posStateBits);
+			_repMatchLenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen);
+			_repMatchLenEncoder.UpdateTables((UInt32)1 << _posStateBits);
+
+			nowPos64 = 0;
+		}
+
+
+		public void Code(System.IO.Stream inStream, System.IO.Stream outStream,
+			Int64 inSize, Int64 outSize, ICodeProgress progress)
+		{
+			_needReleaseMFStream = false;
+			try
+			{
+				SetStreams(inStream, outStream, inSize, outSize);
+				while (true)
+				{
+					Int64 processedInSize;
+					Int64 processedOutSize;
+					bool finished;
+					CodeOneBlock(out processedInSize, out processedOutSize, out finished);
+					if (finished)
+						return;
+					if (progress != null)
+					{
+						progress.SetProgress(processedInSize, processedOutSize);
+					}
+				}
+			}
+			finally
+			{
+				ReleaseStreams();
+			}
+		}
+
+		const int kPropSize = 5;
+		Byte[] properties = new Byte[kPropSize];
+
+		public void WriteCoderProperties(System.IO.Stream outStream)
+		{
+			properties[0] = (Byte)((_posStateBits * 5 + _numLiteralPosStateBits) * 9 + _numLiteralContextBits);
+			for (int i = 0; i < 4; i++)
+				properties[1 + i] = (Byte)(_dictionarySize >> (8 * i));
+			outStream.Write(properties, 0, kPropSize);
+		}
+		
+		UInt32[] tempPrices = new UInt32[Base.kNumFullDistances];
+		UInt32 _matchPriceCount;
+
+		void FillDistancesPrices()
+		{
+			for (UInt32 i = Base.kStartPosModelIndex; i < Base.kNumFullDistances; i++)
+			{ 
+				UInt32 posSlot = GetPosSlot(i);
+				int footerBits = (int)((posSlot >> 1) - 1);
+				UInt32 baseVal = ((2 | (posSlot & 1)) << footerBits);
+				tempPrices[i] = BitTreeEncoder.ReverseGetPrice(_posEncoders, 
+					baseVal - posSlot - 1, footerBits, i - baseVal);
+			}
+
+			for (UInt32 lenToPosState = 0; lenToPosState < Base.kNumLenToPosStates; lenToPosState++)
+			{
+				UInt32 posSlot;
+				RangeCoder.BitTreeEncoder encoder = _posSlotEncoder[lenToPosState];
+			
+				UInt32 st = (lenToPosState << Base.kNumPosSlotBits);
+				for (posSlot = 0; posSlot < _distTableSize; posSlot++)
+					_posSlotPrices[st + posSlot] = encoder.GetPrice(posSlot);
+				for (posSlot = Base.kEndPosModelIndex; posSlot < _distTableSize; posSlot++)
+					_posSlotPrices[st + posSlot] += ((((posSlot >> 1) - 1) - Base.kNumAlignBits) << RangeCoder.BitEncoder.kNumBitPriceShiftBits);
+
+				UInt32 st2 = lenToPosState * Base.kNumFullDistances;
+				UInt32 i;
+				for (i = 0; i < Base.kStartPosModelIndex; i++)
+					_distancesPrices[st2 + i] = _posSlotPrices[st + i];
+				for (; i < Base.kNumFullDistances; i++)
+					_distancesPrices[st2 + i] = _posSlotPrices[st + GetPosSlot(i)] + tempPrices[i];
+			}
+			_matchPriceCount = 0;
+		}
+
+		void FillAlignPrices()
+		{
+			for (UInt32 i = 0; i < Base.kAlignTableSize; i++)
+				_alignPrices[i] = _posAlignEncoder.ReverseGetPrice(i);
+			_alignPriceCount = 0;
+		}
+
+
+		static string[] kMatchFinderIDs = 
+		{
+			"BT2",
+			"BT4",
+		};
+
+		static int FindMatchFinder(string s)
+		{
+			for (int m = 0; m < kMatchFinderIDs.Length; m++)
+				if (s == kMatchFinderIDs[m])
+					return m;
+			return -1;
+		}
+	
+		public void SetCoderProperties(CoderPropID[] propIDs, object[] properties)
+		{
+			for (UInt32 i = 0; i < properties.Length; i++)
+			{
+				object prop = properties[i];
+				switch (propIDs[i])
+				{
+					case CoderPropID.NumFastBytes:
+					{
+						if (!(prop is Int32))
+							throw new InvalidParamException();
+						Int32 numFastBytes = (Int32)prop;
+						if (numFastBytes < 5 || numFastBytes > Base.kMatchMaxLen)
+							throw new InvalidParamException();
+						_numFastBytes = (UInt32)numFastBytes;
+						break;
+					}
+					case CoderPropID.Algorithm:
+					{
+						/*
+						if (!(prop is Int32))
+							throw new InvalidParamException();
+						Int32 maximize = (Int32)prop;
+						_fastMode = (maximize == 0);
+						_maxMode = (maximize >= 2);
+						*/
+						break;
+					}
+					case CoderPropID.MatchFinder:
+					{
+						if (!(prop is String))
+							throw new InvalidParamException();
+						EMatchFinderType matchFinderIndexPrev = _matchFinderType;
+						int m = FindMatchFinder(((string)prop).ToUpper());
+						if (m < 0)
+							throw new InvalidParamException();
+						_matchFinderType = (EMatchFinderType)m;
+						if (_matchFinder != null && matchFinderIndexPrev != _matchFinderType)
+							{
+							_dictionarySizePrev = 0xFFFFFFFF;
+							_matchFinder = null;
+							}
+						break;
+					}
+					case CoderPropID.DictionarySize:
+					{
+						const int kDicLogSizeMaxCompress = 30;
+						if (!(prop is Int32))
+							throw new InvalidParamException(); ;
+						Int32 dictionarySize = (Int32)prop;
+						if (dictionarySize < (UInt32)(1 << Base.kDicLogSizeMin) ||
+							dictionarySize > (UInt32)(1 << kDicLogSizeMaxCompress))
+							throw new InvalidParamException();
+						_dictionarySize = (UInt32)dictionarySize;
+						int dicLogSize;
+						for (dicLogSize = 0; dicLogSize < (UInt32)kDicLogSizeMaxCompress; dicLogSize++)
+							if (dictionarySize <= ((UInt32)(1) << dicLogSize))
+								break;
+						_distTableSize = (UInt32)dicLogSize * 2;
+						break;
+					}
+					case CoderPropID.PosStateBits:
+					{
+						if (!(prop is Int32))
+							throw new InvalidParamException();
+						Int32 v = (Int32)prop;
+						if (v < 0 || v > (UInt32)Base.kNumPosStatesBitsEncodingMax)
+							throw new InvalidParamException();
+						_posStateBits = (int)v;
+						_posStateMask = (((UInt32)1) << (int)_posStateBits) - 1;
+						break;
+					}
+					case CoderPropID.LitPosBits:
+					{
+						if (!(prop is Int32))
+							throw new InvalidParamException();
+						Int32 v = (Int32)prop;
+						if (v < 0 || v > (UInt32)Base.kNumLitPosStatesBitsEncodingMax)
+							throw new InvalidParamException();
+						_numLiteralPosStateBits = (int)v;
+						break;
+					}
+					case CoderPropID.LitContextBits:
+					{
+						if (!(prop is Int32))
+							throw new InvalidParamException();
+						Int32 v = (Int32)prop;
+						if (v < 0 || v > (UInt32)Base.kNumLitContextBitsMax)
+							throw new InvalidParamException(); ;
+						_numLiteralContextBits = (int)v;
+						break;
+					}
+					case CoderPropID.EndMarker:
+					{
+						if (!(prop is Boolean))
+							throw new InvalidParamException();
+						SetWriteEndMarkerMode((Boolean)prop);
+						break;
+					}
+					default:
+						throw new InvalidParamException();
+				}
+			}
+		}
+
+		uint _trainSize = 0;
+		public void SetTrainSize(uint trainSize)
+		{
+			_trainSize = trainSize;
+		}
+		
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/LzmaAlone.cs b/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/LzmaAlone.cs
new file mode 100644
index 0000000..7e22450
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/LzmaAlone.cs
@@ -0,0 +1,364 @@
+using System;
+using System.IO;
+namespace SevenZip
+{
+	using CommandLineParser;
+	
+	public class CDoubleStream: Stream
+	{
+		public System.IO.Stream s1;
+		public System.IO.Stream s2;
+		public int fileIndex;
+		public long skipSize;
+		
+		public override bool CanRead { get { return true; }}
+		public override bool CanWrite { get { return false; }}
+		public override bool CanSeek { get { return false; }}
+		public override long Length { get { return s1.Length + s2.Length - skipSize; } }
+		public override long Position
+		{
+			get { return 0;	}
+			set { }
+		}
+		public override void Flush() { }
+		public override int Read(byte[] buffer, int offset, int count) 
+		{
+			int numTotal = 0;
+			while (count > 0)
+			{
+				if (fileIndex == 0)
+				{
+					int num = s1.Read(buffer, offset, count);
+					offset += num;
+					count -= num;
+					numTotal += num;
+					if (num == 0)
+						fileIndex++;
+				}
+				if (fileIndex == 1)
+				{
+					numTotal += s2.Read(buffer, offset, count);
+					return numTotal;
+				}
+			}
+			return numTotal;
+		}
+		public override void Write(byte[] buffer, int offset, int count)
+		{
+			throw (new Exception("can't Write"));
+		}
+		public override long Seek(long offset, System.IO.SeekOrigin origin)
+		{
+			throw (new Exception("can't Seek"));
+		}
+		public override void SetLength(long value)
+		{
+			throw (new Exception("can't SetLength"));
+		}
+	}
+	
+	class LzmaAlone
+	{
+		enum Key
+		{
+			Help1 = 0,
+			Help2,
+			Mode,
+			Dictionary,
+			FastBytes,
+			LitContext,
+			LitPos,
+			PosBits,
+			MatchFinder,
+			EOS,
+			StdIn,
+			StdOut,
+			Train
+		};
+
+		static void PrintHelp()
+		{
+			System.Console.WriteLine("\nUsage:  LZMA <e|d> [<switches>...] inputFile outputFile\n" +
+				"  e: encode file\n" +
+				"  d: decode file\n" +
+				"  b: Benchmark\n" +
+				"<Switches>\n" +
+				// "  -a{N}:  set compression mode - [0, 1], default: 1 (max)\n" +
+				"  -d{N}:  set dictionary - [0, 29], default: 23 (8MB)\n" +
+				"  -fb{N}: set number of fast bytes - [5, 273], default: 128\n" +
+				"  -lc{N}: set number of literal context bits - [0, 8], default: 3\n" +
+				"  -lp{N}: set number of literal pos bits - [0, 4], default: 0\n" +
+				"  -pb{N}: set number of pos bits - [0, 4], default: 2\n" +
+				"  -mf{MF_ID}: set Match Finder: [bt2, bt4], default: bt4\n" +
+				"  -eos:   write End Of Stream marker\n"
+				// + "  -si:    read data from stdin\n"
+				// + "  -so:    write data to stdout\n"
+				);
+		}
+
+		static bool GetNumber(string s, out Int32 v)
+		{
+			v = 0;
+			for (int i = 0; i < s.Length; i++)
+			{
+				char c = s[i];
+				if (c < '0' || c > '9')
+					return false;
+				v *= 10;
+				v += (Int32)(c - '0');
+			}
+			return true;
+		}
+
+		static int IncorrectCommand()
+		{
+			throw (new Exception("Command line error"));
+			// System.Console.WriteLine("\nCommand line error\n");
+			// return 1;
+		}
+		static int Main2(string[] args)
+		{
+			System.Console.WriteLine("\nLZMA# 4.61  2008-11-23\n");
+
+			if (args.Length == 0)
+			{
+				PrintHelp();
+				return 0;
+			}
+
+			SwitchForm[] kSwitchForms = new SwitchForm[13];
+			int sw = 0;
+			kSwitchForms[sw++] = new SwitchForm("?", SwitchType.Simple, false);
+			kSwitchForms[sw++] = new SwitchForm("H", SwitchType.Simple, false);
+			kSwitchForms[sw++] = new SwitchForm("A", SwitchType.UnLimitedPostString, false, 1);
+			kSwitchForms[sw++] = new SwitchForm("D", SwitchType.UnLimitedPostString, false, 1);
+			kSwitchForms[sw++] = new SwitchForm("FB", SwitchType.UnLimitedPostString, false, 1);
+			kSwitchForms[sw++] = new SwitchForm("LC", SwitchType.UnLimitedPostString, false, 1);
+			kSwitchForms[sw++] = new SwitchForm("LP", SwitchType.UnLimitedPostString, false, 1);
+			kSwitchForms[sw++] = new SwitchForm("PB", SwitchType.UnLimitedPostString, false, 1);
+			kSwitchForms[sw++] = new SwitchForm("MF", SwitchType.UnLimitedPostString, false, 1);
+			kSwitchForms[sw++] = new SwitchForm("EOS", SwitchType.Simple, false);
+			kSwitchForms[sw++] = new SwitchForm("SI", SwitchType.Simple, false);
+			kSwitchForms[sw++] = new SwitchForm("SO", SwitchType.Simple, false);
+			kSwitchForms[sw++] = new SwitchForm("T", SwitchType.UnLimitedPostString, false, 1);
+
+
+			Parser parser = new Parser(sw);
+			try
+			{
+				parser.ParseStrings(kSwitchForms, args);
+			}
+			catch
+			{
+				return IncorrectCommand();
+			}
+
+			if (parser[(int)Key.Help1].ThereIs || parser[(int)Key.Help2].ThereIs)
+			{
+				PrintHelp();
+				return 0;
+			}
+
+			System.Collections.ArrayList nonSwitchStrings = parser.NonSwitchStrings;
+
+			int paramIndex = 0;
+			if (paramIndex >= nonSwitchStrings.Count)
+				return IncorrectCommand();
+			string command = (string)nonSwitchStrings[paramIndex++];
+			command = command.ToLower();
+
+			bool dictionaryIsDefined = false;
+			Int32 dictionary = 1 << 21;
+			if (parser[(int)Key.Dictionary].ThereIs)
+			{
+				Int32 dicLog;
+				if (!GetNumber((string)parser[(int)Key.Dictionary].PostStrings[0], out dicLog))
+					IncorrectCommand();
+				dictionary = (Int32)1 << dicLog;
+				dictionaryIsDefined = true;
+			}
+			string mf = "bt4";
+			if (parser[(int)Key.MatchFinder].ThereIs)
+				mf = (string)parser[(int)Key.MatchFinder].PostStrings[0];
+			mf = mf.ToLower();
+
+			if (command == "b")
+			{
+				const Int32 kNumDefaultItereations = 10;
+				Int32 numIterations = kNumDefaultItereations;
+				if (paramIndex < nonSwitchStrings.Count)
+					if (!GetNumber((string)nonSwitchStrings[paramIndex++], out numIterations))
+						numIterations = kNumDefaultItereations;
+				return LzmaBench.LzmaBenchmark(numIterations, (UInt32)dictionary);
+			}
+
+			string train = "";
+			if (parser[(int)Key.Train].ThereIs)
+				train = (string)parser[(int)Key.Train].PostStrings[0];
+
+			bool encodeMode = false;
+			if (command == "e")
+				encodeMode = true;
+			else if (command == "d")
+				encodeMode = false;
+			else
+				IncorrectCommand();
+
+			bool stdInMode = parser[(int)Key.StdIn].ThereIs;
+			bool stdOutMode = parser[(int)Key.StdOut].ThereIs;
+
+			Stream inStream = null;
+			if (stdInMode)
+			{
+				throw (new Exception("Not implemeted"));
+			}
+			else
+			{
+				if (paramIndex >= nonSwitchStrings.Count)
+					IncorrectCommand();
+				string inputName = (string)nonSwitchStrings[paramIndex++];
+				inStream = new FileStream(inputName, FileMode.Open, FileAccess.Read);
+			}
+
+			FileStream outStream = null;
+			if (stdOutMode)
+			{
+				throw (new Exception("Not implemeted"));
+			}
+			else
+			{
+				if (paramIndex >= nonSwitchStrings.Count)
+					IncorrectCommand();
+				string outputName = (string)nonSwitchStrings[paramIndex++];
+				outStream = new FileStream(outputName, FileMode.Create, FileAccess.Write);
+			}
+
+			FileStream trainStream = null;
+			if (train.Length != 0)
+				trainStream = new FileStream(train, FileMode.Open, FileAccess.Read);
+
+			if (encodeMode)
+			{
+				if (!dictionaryIsDefined)
+					dictionary = 1 << 23;
+
+				Int32 posStateBits = 2;
+				Int32 litContextBits = 3; // for normal files
+				// UInt32 litContextBits = 0; // for 32-bit data
+				Int32 litPosBits = 0;
+				// UInt32 litPosBits = 2; // for 32-bit data
+				Int32 algorithm = 2;
+				Int32 numFastBytes = 128;
+
+				bool eos = parser[(int)Key.EOS].ThereIs || stdInMode;
+
+				if (parser[(int)Key.Mode].ThereIs)
+					if (!GetNumber((string)parser[(int)Key.Mode].PostStrings[0], out algorithm))
+						IncorrectCommand();
+
+				if (parser[(int)Key.FastBytes].ThereIs)
+					if (!GetNumber((string)parser[(int)Key.FastBytes].PostStrings[0], out numFastBytes))
+						IncorrectCommand();
+				if (parser[(int)Key.LitContext].ThereIs)
+					if (!GetNumber((string)parser[(int)Key.LitContext].PostStrings[0], out litContextBits))
+						IncorrectCommand();
+				if (parser[(int)Key.LitPos].ThereIs)
+					if (!GetNumber((string)parser[(int)Key.LitPos].PostStrings[0], out litPosBits))
+						IncorrectCommand();
+				if (parser[(int)Key.PosBits].ThereIs)
+					if (!GetNumber((string)parser[(int)Key.PosBits].PostStrings[0], out posStateBits))
+						IncorrectCommand();
+
+				CoderPropID[] propIDs = 
+				{
+					CoderPropID.DictionarySize,
+					CoderPropID.PosStateBits,
+					CoderPropID.LitContextBits,
+					CoderPropID.LitPosBits,
+					CoderPropID.Algorithm,
+					CoderPropID.NumFastBytes,
+					CoderPropID.MatchFinder,
+					CoderPropID.EndMarker
+				};
+				object[] properties = 
+				{
+					(Int32)(dictionary),
+					(Int32)(posStateBits),
+					(Int32)(litContextBits),
+					(Int32)(litPosBits),
+					(Int32)(algorithm),
+					(Int32)(numFastBytes),
+					mf,
+					eos
+				};
+
+				Compression.LZMA.Encoder encoder = new Compression.LZMA.Encoder();
+				encoder.SetCoderProperties(propIDs, properties);
+				encoder.WriteCoderProperties(outStream);
+				Int64 fileSize;
+				if (eos || stdInMode)
+					fileSize = -1;
+				else
+					fileSize = inStream.Length;
+				for (int i = 0; i < 8; i++)
+					outStream.WriteByte((Byte)(fileSize >> (8 * i)));
+				if (trainStream != null)
+				{
+					CDoubleStream doubleStream = new CDoubleStream();
+					doubleStream.s1 = trainStream;
+					doubleStream.s2 = inStream;
+					doubleStream.fileIndex = 0;
+					inStream = doubleStream;
+					long trainFileSize = trainStream.Length;
+					doubleStream.skipSize = 0;
+					if (trainFileSize > dictionary)
+						doubleStream.skipSize = trainFileSize - dictionary;
+					trainStream.Seek(doubleStream.skipSize, SeekOrigin.Begin);
+					encoder.SetTrainSize((uint)(trainFileSize - doubleStream.skipSize));
+				}
+				encoder.Code(inStream, outStream, -1, -1, null);
+			}
+			else if (command == "d")
+			{
+				byte[] properties = new byte[5];
+				if (inStream.Read(properties, 0, 5) != 5)
+					throw (new Exception("input .lzma is too short"));
+				Compression.LZMA.Decoder decoder = new Compression.LZMA.Decoder();
+				decoder.SetDecoderProperties(properties);
+				if (trainStream != null)
+				{
+					if (!decoder.Train(trainStream))
+						throw (new Exception("can't train"));
+				}
+				long outSize = 0;
+				for (int i = 0; i < 8; i++)
+				{
+					int v = inStream.ReadByte();
+					if (v < 0)
+						throw (new Exception("Can't Read 1"));
+					outSize |= ((long)(byte)v) << (8 * i);
+				}
+				long compressedSize = inStream.Length - inStream.Position;
+				decoder.Code(inStream, outStream, compressedSize, outSize, null);
+			}
+			else
+				throw (new Exception("Command Error"));
+			return 0;
+		}
+
+		[STAThread]
+		static int Main(string[] args)
+		{
+			try
+			{
+				return Main2(args);
+			}
+			catch (Exception e)
+			{
+				Console.WriteLine("{0} Caught exception #1.", e);
+				// throw e;
+				return 1;
+			}
+		}
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/LzmaAlone.csproj b/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/LzmaAlone.csproj
new file mode 100644
index 0000000..6d87b61
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/LzmaAlone.csproj
@@ -0,0 +1,90 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>8.0.50727</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{CE33DF18-F9C8-4D6F-9057-DBB4DB96E973}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <RootNamespace>LzmaAlone</RootNamespace>
+    <AssemblyName>Lzma#</AssemblyName>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>.\bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugSymbols>false</DebugSymbols>
+    <Optimize>true</Optimize>
+    <OutputPath>.\bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <PlatformTarget>AnyCPU</PlatformTarget>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="..\..\Common\CommandLineParser.cs">
+      <Link>Common\CommandLineParser.cs</Link>
+    </Compile>
+    <Compile Include="..\..\Common\CRC.cs">
+      <Link>Common\CRC.cs</Link>
+    </Compile>
+    <Compile Include="..\..\ICoder.cs">
+      <Link>ICoder.cs</Link>
+    </Compile>
+    <Compile Include="..\LZ\IMatchFinder.cs">
+      <Link>LZ\IMatchFinder.cs</Link>
+    </Compile>
+    <Compile Include="..\LZ\LzBinTree.cs">
+      <Link>LZ\LzBinTree.cs</Link>
+    </Compile>
+    <Compile Include="..\LZ\LzInWindow.cs">
+      <Link>LZ\LzInWindow.cs</Link>
+    </Compile>
+    <Compile Include="..\LZ\LzOutWindow.cs">
+      <Link>LZ\LzOutWindow.cs</Link>
+    </Compile>
+    <Compile Include="..\LZMA\LzmaBase.cs">
+      <Link>LZMA\LzmaBase.cs</Link>
+    </Compile>
+    <Compile Include="..\LZMA\LzmaDecoder.cs">
+      <Link>LZMA\LzmaDecoder.cs</Link>
+    </Compile>
+    <Compile Include="..\LZMA\LzmaEncoder.cs">
+      <Link>LZMA\LzmaEncoder.cs</Link>
+    </Compile>
+    <Compile Include="..\RangeCoder\RangeCoder.cs">
+      <Link>RangeCoder\RangeCoder.cs</Link>
+    </Compile>
+    <Compile Include="..\RangeCoder\RangeCoderBit.cs">
+      <Link>RangeCoder\RangeCoderBit.cs</Link>
+    </Compile>
+    <Compile Include="..\RangeCoder\RangeCoderBitTree.cs">
+      <Link>RangeCoder\RangeCoderBitTree.cs</Link>
+    </Compile>
+    <Compile Include="LzmaAlone.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="LzmaBench.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Properties\Settings.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Settings.settings</DependentUpon>
+    </Compile>
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.cs</LastGenOutput>
+    </None>
+    <AppDesigner Include="Properties\" />
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" />
+</Project>
\ No newline at end of file
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/LzmaAlone.sln b/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/LzmaAlone.sln
new file mode 100644
index 0000000..376cd27
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/LzmaAlone.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual C# Express 2005
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LzmaAlone", "LzmaAlone.csproj", "{CE33DF18-F9C8-4D6F-9057-DBB4DB96E973}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{CE33DF18-F9C8-4D6F-9057-DBB4DB96E973}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{CE33DF18-F9C8-4D6F-9057-DBB4DB96E973}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{CE33DF18-F9C8-4D6F-9057-DBB4DB96E973}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{CE33DF18-F9C8-4D6F-9057-DBB4DB96E973}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/LzmaBench.cs b/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/LzmaBench.cs
new file mode 100644
index 0000000..f7b6bd0
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/LzmaBench.cs
@@ -0,0 +1,340 @@
+// LzmaBench.cs
+
+using System;
+using System.IO;
+
+namespace SevenZip
+{
+	/// <summary>
+	/// LZMA Benchmark
+	/// </summary>
+	internal abstract class LzmaBench
+	{
+		const UInt32 kAdditionalSize = (6 << 20);
+		const UInt32 kCompressedAdditionalSize = (1 << 10);
+		const UInt32 kMaxLzmaPropSize = 10;
+
+		class CRandomGenerator
+		{
+			UInt32 A1;
+			UInt32 A2;
+			public CRandomGenerator() { Init(); }
+			public void Init() { A1 = 362436069; A2 = 521288629; }
+			public UInt32 GetRnd()
+			{
+				return
+					((A1 = 36969 * (A1 & 0xffff) + (A1 >> 16)) << 16) ^
+					((A2 = 18000 * (A2 & 0xffff) + (A2 >> 16)));
+			}
+		};
+
+		class CBitRandomGenerator
+		{
+			CRandomGenerator RG = new CRandomGenerator();
+			UInt32 Value;
+			int NumBits;
+			public void Init()
+			{
+				Value = 0;
+				NumBits = 0;
+			}
+			public UInt32 GetRnd(int numBits)
+			{
+				UInt32 result;
+				if (NumBits > numBits)
+				{
+					result = Value & (((UInt32)1 << numBits) - 1);
+					Value >>= numBits;
+					NumBits -= numBits;
+					return result;
+				}
+				numBits -= NumBits;
+				result = (Value << numBits);
+				Value = RG.GetRnd();
+				result |= Value & (((UInt32)1 << numBits) - 1);
+				Value >>= numBits;
+				NumBits = 32 - numBits;
+				return result;
+			}
+		};
+
+		class CBenchRandomGenerator
+		{
+			CBitRandomGenerator RG = new CBitRandomGenerator();
+			UInt32 Pos;
+			UInt32 Rep0;
+			
+			public UInt32 BufferSize;
+			public Byte[] Buffer = null;
+
+			public CBenchRandomGenerator() { }
+
+			public void Set(UInt32 bufferSize)
+			{
+				Buffer = new Byte[bufferSize];
+				Pos = 0;
+				BufferSize = bufferSize;
+			}
+			UInt32 GetRndBit() { return RG.GetRnd(1); }
+			UInt32 GetLogRandBits(int numBits)
+			{
+				UInt32 len = RG.GetRnd(numBits);
+				return RG.GetRnd((int)len);
+			}
+			UInt32 GetOffset()
+			{
+				if (GetRndBit() == 0)
+					return GetLogRandBits(4);
+				return (GetLogRandBits(4) << 10) | RG.GetRnd(10);
+			}
+			UInt32 GetLen1() { return RG.GetRnd(1 + (int)RG.GetRnd(2)); }
+			UInt32 GetLen2() { return RG.GetRnd(2 + (int)RG.GetRnd(2)); }
+			public void Generate()
+			{
+				RG.Init();
+				Rep0 = 1;
+				while (Pos < BufferSize)
+				{
+					if (GetRndBit() == 0 || Pos < 1)
+						Buffer[Pos++] = (Byte)RG.GetRnd(8);
+					else
+					{
+						UInt32 len;
+						if (RG.GetRnd(3) == 0)
+							len = 1 + GetLen1();
+						else
+						{
+							do
+								Rep0 = GetOffset();
+							while (Rep0 >= Pos);
+							Rep0++;
+							len = 2 + GetLen2();
+						}
+						for (UInt32 i = 0; i < len && Pos < BufferSize; i++, Pos++)
+							Buffer[Pos] = Buffer[Pos - Rep0];
+					}
+				}
+			}
+		};
+
+		class CrcOutStream : System.IO.Stream
+		{
+			public CRC CRC = new CRC();
+			public void Init() { CRC.Init(); }
+			public UInt32 GetDigest() { return CRC.GetDigest(); }
+
+			public override bool CanRead { get { return false; } }
+			public override bool CanSeek { get { return false; } }
+			public override bool CanWrite { get { return true; } }
+			public override Int64 Length { get { return 0; } }
+			public override Int64 Position { get { return 0; } set { } }
+			public override void Flush() { }
+			public override long Seek(long offset, SeekOrigin origin) { return 0; }
+			public override void SetLength(long value) { }
+			public override int Read(byte[] buffer, int offset, int count) { return 0; }
+
+			public override void WriteByte(byte b)
+			{
+				CRC.UpdateByte(b);
+			}
+			public override void Write(byte[] buffer, int offset, int count)
+			{
+				CRC.Update(buffer, (uint)offset, (uint)count);
+			}
+		};
+
+		class CProgressInfo : ICodeProgress
+		{
+			public Int64 ApprovedStart;
+			public Int64 InSize;
+			public System.DateTime Time;
+			public void Init() { InSize = 0; }
+			public void SetProgress(Int64 inSize, Int64 outSize)
+			{
+				if (inSize >= ApprovedStart && InSize == 0)
+				{
+					Time = DateTime.UtcNow;
+					InSize = inSize;
+				}
+			}
+		}
+		const int kSubBits = 8;
+
+		static UInt32 GetLogSize(UInt32 size)
+		{
+			for (int i = kSubBits; i < 32; i++)
+				for (UInt32 j = 0; j < (1 << kSubBits); j++)
+					if (size <= (((UInt32)1) << i) + (j << (i - kSubBits)))
+						return (UInt32)(i << kSubBits) + j;
+			return (32 << kSubBits);
+		}
+
+		static UInt64 MyMultDiv64(UInt64 value, UInt64 elapsedTime)
+		{
+			UInt64 freq = TimeSpan.TicksPerSecond;
+			UInt64 elTime = elapsedTime;
+			while (freq > 1000000)
+			{
+				freq >>= 1;
+				elTime >>= 1;
+			}
+			if (elTime == 0)
+				elTime = 1;
+			return value * freq / elTime;
+		}
+
+		static UInt64 GetCompressRating(UInt32 dictionarySize, UInt64 elapsedTime, UInt64 size)
+		{
+			UInt64 t = GetLogSize(dictionarySize) - (18 << kSubBits);
+			UInt64 numCommandsForOne = 1060 + ((t * t * 10) >> (2 * kSubBits));
+			UInt64 numCommands = (UInt64)(size) * numCommandsForOne;
+			return MyMultDiv64(numCommands, elapsedTime);
+		}
+
+		static UInt64 GetDecompressRating(UInt64 elapsedTime, UInt64 outSize, UInt64 inSize)
+		{
+			UInt64 numCommands = inSize * 220 + outSize * 20;
+			return MyMultDiv64(numCommands, elapsedTime);
+		}
+
+		static UInt64 GetTotalRating(
+			UInt32 dictionarySize,
+			UInt64 elapsedTimeEn, UInt64 sizeEn,
+			UInt64 elapsedTimeDe,
+			UInt64 inSizeDe, UInt64 outSizeDe)
+		{
+			return (GetCompressRating(dictionarySize, elapsedTimeEn, sizeEn) +
+				GetDecompressRating(elapsedTimeDe, inSizeDe, outSizeDe)) / 2;
+		}
+
+		static void PrintValue(UInt64 v)
+		{
+			string s = v.ToString();
+			for (int i = 0; i + s.Length < 6; i++)
+				System.Console.Write(" ");
+			System.Console.Write(s);
+		}
+
+		static void PrintRating(UInt64 rating)
+		{
+			PrintValue(rating / 1000000);
+			System.Console.Write(" MIPS");
+		}
+
+		static void PrintResults(
+			UInt32 dictionarySize,
+			UInt64 elapsedTime,
+			UInt64 size,
+			bool decompressMode, UInt64 secondSize)
+		{
+			UInt64 speed = MyMultDiv64(size, elapsedTime);
+			PrintValue(speed / 1024);
+			System.Console.Write(" KB/s  ");
+			UInt64 rating;
+			if (decompressMode)
+				rating = GetDecompressRating(elapsedTime, size, secondSize);
+			else
+				rating = GetCompressRating(dictionarySize, elapsedTime, size);
+			PrintRating(rating);
+		}
+
+		static public int LzmaBenchmark(Int32 numIterations, UInt32 dictionarySize)
+		{
+			if (numIterations <= 0)
+				return 0;
+			if (dictionarySize < (1 << 18))
+			{
+				System.Console.WriteLine("\nError: dictionary size for benchmark must be >= 19 (512 KB)");
+				return 1;
+			}
+			System.Console.Write("\n       Compressing                Decompressing\n\n");
+
+			Compression.LZMA.Encoder encoder = new Compression.LZMA.Encoder();
+			Compression.LZMA.Decoder decoder = new Compression.LZMA.Decoder();
+
+
+			CoderPropID[] propIDs = 
+			{ 
+				CoderPropID.DictionarySize,
+			};
+			object[] properties = 
+			{
+				(Int32)(dictionarySize),
+			};
+
+			UInt32 kBufferSize = dictionarySize + kAdditionalSize;
+			UInt32 kCompressedBufferSize = (kBufferSize / 2) + kCompressedAdditionalSize;
+
+			encoder.SetCoderProperties(propIDs, properties);
+			System.IO.MemoryStream propStream = new System.IO.MemoryStream();
+			encoder.WriteCoderProperties(propStream);
+			byte[] propArray = propStream.ToArray();
+
+			CBenchRandomGenerator rg = new CBenchRandomGenerator();
+
+			rg.Set(kBufferSize);
+			rg.Generate();
+			CRC crc = new CRC();
+			crc.Init();
+			crc.Update(rg.Buffer, 0, rg.BufferSize);
+
+			CProgressInfo progressInfo = new CProgressInfo();
+			progressInfo.ApprovedStart = dictionarySize;
+
+			UInt64 totalBenchSize = 0;
+			UInt64 totalEncodeTime = 0;
+			UInt64 totalDecodeTime = 0;
+			UInt64 totalCompressedSize = 0;
+
+			MemoryStream inStream = new MemoryStream(rg.Buffer, 0, (int)rg.BufferSize);
+			MemoryStream compressedStream = new MemoryStream((int)kCompressedBufferSize);
+			CrcOutStream crcOutStream = new CrcOutStream();
+			for (Int32 i = 0; i < numIterations; i++)
+			{
+				progressInfo.Init();
+				inStream.Seek(0, SeekOrigin.Begin);
+				compressedStream.Seek(0, SeekOrigin.Begin);
+				encoder.Code(inStream, compressedStream, -1, -1, progressInfo);
+				TimeSpan sp2 = DateTime.UtcNow - progressInfo.Time;
+				UInt64 encodeTime = (UInt64)sp2.Ticks;
+
+				long compressedSize = compressedStream.Position;
+				if (progressInfo.InSize == 0)
+					throw (new Exception("Internal ERROR 1282"));
+
+				UInt64 decodeTime = 0;
+				for (int j = 0; j < 2; j++)
+				{
+					compressedStream.Seek(0, SeekOrigin.Begin);
+					crcOutStream.Init();
+
+					decoder.SetDecoderProperties(propArray);
+					UInt64 outSize = kBufferSize;
+					System.DateTime startTime = DateTime.UtcNow;
+					decoder.Code(compressedStream, crcOutStream, 0, (Int64)outSize, null);
+					TimeSpan sp = (DateTime.UtcNow - startTime);
+					decodeTime = (ulong)sp.Ticks;
+					if (crcOutStream.GetDigest() != crc.GetDigest())
+						throw (new Exception("CRC Error"));
+				}
+				UInt64 benchSize = kBufferSize - (UInt64)progressInfo.InSize;
+				PrintResults(dictionarySize, encodeTime, benchSize, false, 0);
+				System.Console.Write("     ");
+				PrintResults(dictionarySize, decodeTime, kBufferSize, true, (ulong)compressedSize);
+				System.Console.WriteLine();
+
+				totalBenchSize += benchSize;
+				totalEncodeTime += encodeTime;
+				totalDecodeTime += decodeTime;
+				totalCompressedSize += (ulong)compressedSize;
+			}
+			System.Console.WriteLine("---------------------------------------------------");
+			PrintResults(dictionarySize, totalEncodeTime, totalBenchSize, false, 0);
+			System.Console.Write("     ");
+			PrintResults(dictionarySize, totalDecodeTime,
+					kBufferSize * (UInt64)numIterations, true, totalCompressedSize);
+			System.Console.WriteLine("    Average");
+			return 0;
+		}
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/Properties/AssemblyInfo.cs b/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..9614884
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/Properties/AssemblyInfo.cs
@@ -0,0 +1,29 @@
+#region Using directives
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+#endregion
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("LZMA#")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Igor Pavlov")]
+[assembly: AssemblyProduct("LZMA# SDK")]
+[assembly: AssemblyCopyright("Copyright @ Igor Pavlov 1999-2004")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers 
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("4.12.*")]
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/Properties/Resources.cs b/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/Properties/Resources.cs
new file mode 100644
index 0000000..1170cf1
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/Properties/Resources.cs
@@ -0,0 +1,70 @@
+//------------------------------------------------------------------------------
+// <autogenerated>
+//     This code was generated by a tool.
+//     Runtime Version:2.0.40607.42
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </autogenerated>
+//------------------------------------------------------------------------------
+
+namespace LzmaAlone.Properties
+{
+	using System;
+	using System.IO;
+	using System.Resources;
+
+	/// <summary>
+	///    A strongly-typed resource class, for looking up localized strings, etc.
+	/// </summary>
+	// This class was auto-generated by the Strongly Typed Resource Builder
+	// class via a tool like ResGen or Visual Studio.NET.
+	// To add or remove a member, edit your .ResX file then rerun ResGen
+	// with the /str option, or rebuild your VS project.
+	class Resources
+	{
+
+		private static System.Resources.ResourceManager _resMgr;
+
+		private static System.Globalization.CultureInfo _resCulture;
+
+		/*FamANDAssem*/
+		internal Resources()
+		{
+		}
+
+		/// <summary>
+		///    Returns the cached ResourceManager instance used by this class.
+		/// </summary>
+		[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+		public static System.Resources.ResourceManager ResourceManager
+		{
+			get
+			{
+				if ((_resMgr == null))
+				{
+					System.Resources.ResourceManager temp = new System.Resources.ResourceManager("Resources", typeof(Resources).Assembly);
+					_resMgr = temp;
+				}
+				return _resMgr;
+			}
+		}
+
+		/// <summary>
+		///    Overrides the current thread's CurrentUICulture property for all
+		///    resource lookups using this strongly typed resource class.
+		/// </summary>
+		[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+		public static System.Globalization.CultureInfo Culture
+		{
+			get
+			{
+				return _resCulture;
+			}
+			set
+			{
+				_resCulture = value;
+			}
+		}
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/Properties/Settings.cs b/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/Properties/Settings.cs
new file mode 100644
index 0000000..ccfed77
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Compress/LzmaAlone/Properties/Settings.cs
@@ -0,0 +1,42 @@
+//------------------------------------------------------------------------------
+// <autogenerated>
+//     This code was generated by a tool.
+//     Runtime Version:2.0.40607.42
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </autogenerated>
+//------------------------------------------------------------------------------
+
+namespace LzmaAlone.Properties
+{
+	public partial class Settings : System.Configuration.ApplicationSettingsBase
+	{
+		private static Settings m_Value;
+
+		private static object m_SyncObject = new object();
+
+		public static Settings Value
+		{
+			get
+			{
+				if ((Settings.m_Value == null))
+				{
+					System.Threading.Monitor.Enter(Settings.m_SyncObject);
+					if ((Settings.m_Value == null))
+					{
+						try
+						{
+							Settings.m_Value = new Settings();
+						}
+						finally
+						{
+							System.Threading.Monitor.Exit(Settings.m_SyncObject);
+						}
+					}
+				}
+				return Settings.m_Value;
+			}
+		}
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Compress/RangeCoder/RangeCoder.cs b/third_party/lzma/v4_65/files/CS/7zip/Compress/RangeCoder/RangeCoder.cs
new file mode 100644
index 0000000..949c6bb
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Compress/RangeCoder/RangeCoder.cs
@@ -0,0 +1,234 @@
+using System;
+
+namespace SevenZip.Compression.RangeCoder
+{
+	class Encoder
+	{
+		public const uint kTopValue = (1 << 24);
+
+		System.IO.Stream Stream;
+
+		public UInt64 Low;
+		public uint Range;
+		uint _cacheSize;
+		byte _cache;
+
+		long StartPosition;
+
+		public void SetStream(System.IO.Stream stream)
+		{
+			Stream = stream;
+		}
+
+		public void ReleaseStream()
+		{
+			Stream = null;
+		}
+
+		public void Init()
+		{
+			StartPosition = Stream.Position;
+
+			Low = 0;
+			Range = 0xFFFFFFFF;
+			_cacheSize = 1;
+			_cache = 0;
+		}
+
+		public void FlushData()
+		{
+			for (int i = 0; i < 5; i++)
+				ShiftLow();
+		}
+
+		public void FlushStream()
+		{
+			Stream.Flush();
+		}
+
+		public void CloseStream()
+		{
+			Stream.Close();
+		}
+
+		public void Encode(uint start, uint size, uint total)
+		{
+			Low += start * (Range /= total);
+			Range *= size;
+			while (Range < kTopValue)
+			{
+				Range <<= 8;
+				ShiftLow();
+			}
+		}
+
+		public void ShiftLow()
+		{
+			if ((uint)Low < (uint)0xFF000000 || (uint)(Low >> 32) == 1)
+			{
+				byte temp = _cache;
+				do
+				{
+					Stream.WriteByte((byte)(temp + (Low >> 32)));
+					temp = 0xFF;
+				}
+				while (--_cacheSize != 0);
+				_cache = (byte)(((uint)Low) >> 24);
+			}
+			_cacheSize++;
+			Low = ((uint)Low) << 8;
+		}
+
+		public void EncodeDirectBits(uint v, int numTotalBits)
+		{
+			for (int i = numTotalBits - 1; i >= 0; i--)
+			{
+				Range >>= 1;
+				if (((v >> i) & 1) == 1)
+					Low += Range;
+				if (Range < kTopValue)
+				{
+					Range <<= 8;
+					ShiftLow();
+				}
+			}
+		}
+
+		public void EncodeBit(uint size0, int numTotalBits, uint symbol)
+		{
+			uint newBound = (Range >> numTotalBits) * size0;
+			if (symbol == 0)
+				Range = newBound;
+			else
+			{
+				Low += newBound;
+				Range -= newBound;
+			}
+			while (Range < kTopValue)
+			{
+				Range <<= 8;
+				ShiftLow();
+			}
+		}
+
+		public long GetProcessedSizeAdd()
+		{
+			return _cacheSize +
+				Stream.Position - StartPosition + 4;
+			// (long)Stream.GetProcessedSize();
+		}
+	}
+
+	class Decoder
+	{
+		public const uint kTopValue = (1 << 24);
+		public uint Range;
+		public uint Code;
+		// public Buffer.InBuffer Stream = new Buffer.InBuffer(1 << 16);
+		public System.IO.Stream Stream;
+
+		public void Init(System.IO.Stream stream)
+		{
+			// Stream.Init(stream);
+			Stream = stream;
+
+			Code = 0;
+			Range = 0xFFFFFFFF;
+			for (int i = 0; i < 5; i++)
+				Code = (Code << 8) | (byte)Stream.ReadByte();
+		}
+
+		public void ReleaseStream()
+		{
+			// Stream.ReleaseStream();
+			Stream = null;
+		}
+
+		public void CloseStream()
+		{
+			Stream.Close();
+		}
+
+		public void Normalize()
+		{
+			while (Range < kTopValue)
+			{
+				Code = (Code << 8) | (byte)Stream.ReadByte();
+				Range <<= 8;
+			}
+		}
+
+		public void Normalize2()
+		{
+			if (Range < kTopValue)
+			{
+				Code = (Code << 8) | (byte)Stream.ReadByte();
+				Range <<= 8;
+			}
+		}
+
+		public uint GetThreshold(uint total)
+		{
+			return Code / (Range /= total);
+		}
+
+		public void Decode(uint start, uint size, uint total)
+		{
+			Code -= start * Range;
+			Range *= size;
+			Normalize();
+		}
+
+		public uint DecodeDirectBits(int numTotalBits)
+		{
+			uint range = Range;
+			uint code = Code;
+			uint result = 0;
+			for (int i = numTotalBits; i > 0; i--)
+			{
+				range >>= 1;
+				/*
+				result <<= 1;
+				if (code >= range)
+				{
+					code -= range;
+					result |= 1;
+				}
+				*/
+				uint t = (code - range) >> 31;
+				code -= range & (t - 1);
+				result = (result << 1) | (1 - t);
+
+				if (range < kTopValue)
+				{
+					code = (code << 8) | (byte)Stream.ReadByte();
+					range <<= 8;
+				}
+			}
+			Range = range;
+			Code = code;
+			return result;
+		}
+
+		public uint DecodeBit(uint size0, int numTotalBits)
+		{
+			uint newBound = (Range >> numTotalBits) * size0;
+			uint symbol;
+			if (Code < newBound)
+			{
+				symbol = 0;
+				Range = newBound;
+			}
+			else
+			{
+				symbol = 1;
+				Code -= newBound;
+				Range -= newBound;
+			}
+			Normalize();
+			return symbol;
+		}
+
+		// ulong GetProcessedSize() {return Stream.GetProcessedSize(); }
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Compress/RangeCoder/RangeCoderBit.cs b/third_party/lzma/v4_65/files/CS/7zip/Compress/RangeCoder/RangeCoderBit.cs
new file mode 100644
index 0000000..4f0346d
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Compress/RangeCoder/RangeCoderBit.cs
@@ -0,0 +1,117 @@
+using System;
+
+namespace SevenZip.Compression.RangeCoder
+{
+	struct BitEncoder
+	{
+		public const int kNumBitModelTotalBits = 11;
+		public const uint kBitModelTotal = (1 << kNumBitModelTotalBits);
+		const int kNumMoveBits = 5;
+		const int kNumMoveReducingBits = 2;
+		public const int kNumBitPriceShiftBits = 6;
+
+		uint Prob;
+
+		public void Init() { Prob = kBitModelTotal >> 1; }
+
+		public void UpdateModel(uint symbol)
+		{
+			if (symbol == 0)
+				Prob += (kBitModelTotal - Prob) >> kNumMoveBits;
+			else
+				Prob -= (Prob) >> kNumMoveBits;
+		}
+
+		public void Encode(Encoder encoder, uint symbol)
+		{
+			// encoder.EncodeBit(Prob, kNumBitModelTotalBits, symbol);
+			// UpdateModel(symbol);
+			uint newBound = (encoder.Range >> kNumBitModelTotalBits) * Prob;
+			if (symbol == 0)
+			{
+				encoder.Range = newBound;
+				Prob += (kBitModelTotal - Prob) >> kNumMoveBits;
+			}
+			else
+			{
+				encoder.Low += newBound;
+				encoder.Range -= newBound;
+				Prob -= (Prob) >> kNumMoveBits;
+			}
+			if (encoder.Range < Encoder.kTopValue)
+			{
+				encoder.Range <<= 8;
+				encoder.ShiftLow();
+			}
+		}
+
+		private static UInt32[] ProbPrices = new UInt32[kBitModelTotal >> kNumMoveReducingBits];
+
+		static BitEncoder()
+		{
+			const int kNumBits = (kNumBitModelTotalBits - kNumMoveReducingBits);
+			for (int i = kNumBits - 1; i >= 0; i--)
+			{
+				UInt32 start = (UInt32)1 << (kNumBits - i - 1);
+				UInt32 end = (UInt32)1 << (kNumBits - i);
+				for (UInt32 j = start; j < end; j++)
+					ProbPrices[j] = ((UInt32)i << kNumBitPriceShiftBits) +
+						(((end - j) << kNumBitPriceShiftBits) >> (kNumBits - i - 1));
+			}
+		}
+
+		public uint GetPrice(uint symbol)
+		{
+			return ProbPrices[(((Prob - symbol) ^ ((-(int)symbol))) & (kBitModelTotal - 1)) >> kNumMoveReducingBits];
+		}
+	  public uint GetPrice0() { return ProbPrices[Prob >> kNumMoveReducingBits]; }
+		public uint GetPrice1() { return ProbPrices[(kBitModelTotal - Prob) >> kNumMoveReducingBits]; }
+	}
+
+	struct BitDecoder
+	{
+		public const int kNumBitModelTotalBits = 11;
+		public const uint kBitModelTotal = (1 << kNumBitModelTotalBits);
+		const int kNumMoveBits = 5;
+
+		uint Prob;
+
+		public void UpdateModel(int numMoveBits, uint symbol)
+		{
+			if (symbol == 0)
+				Prob += (kBitModelTotal - Prob) >> numMoveBits;
+			else
+				Prob -= (Prob) >> numMoveBits;
+		}
+
+		public void Init() { Prob = kBitModelTotal >> 1; }
+
+		public uint Decode(RangeCoder.Decoder rangeDecoder)
+		{
+			uint newBound = (uint)(rangeDecoder.Range >> kNumBitModelTotalBits) * (uint)Prob;
+			if (rangeDecoder.Code < newBound)
+			{
+				rangeDecoder.Range = newBound;
+				Prob += (kBitModelTotal - Prob) >> kNumMoveBits;
+				if (rangeDecoder.Range < Decoder.kTopValue)
+				{
+					rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte();
+					rangeDecoder.Range <<= 8;
+				}
+				return 0;
+			}
+			else
+			{
+				rangeDecoder.Range -= newBound;
+				rangeDecoder.Code -= newBound;
+				Prob -= (Prob) >> kNumMoveBits;
+				if (rangeDecoder.Range < Decoder.kTopValue)
+				{
+					rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte();
+					rangeDecoder.Range <<= 8;
+				}
+				return 1;
+			}
+		}
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/Compress/RangeCoder/RangeCoderBitTree.cs b/third_party/lzma/v4_65/files/CS/7zip/Compress/RangeCoder/RangeCoderBitTree.cs
new file mode 100644
index 0000000..4b4506f
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/Compress/RangeCoder/RangeCoderBitTree.cs
@@ -0,0 +1,157 @@
+using System;
+
+namespace SevenZip.Compression.RangeCoder
+{
+	struct BitTreeEncoder
+	{
+		BitEncoder[] Models;
+		int NumBitLevels;
+
+		public BitTreeEncoder(int numBitLevels)
+		{
+			NumBitLevels = numBitLevels;
+			Models = new BitEncoder[1 << numBitLevels];
+		}
+
+		public void Init()
+		{
+			for (uint i = 1; i < (1 << NumBitLevels); i++)
+				Models[i].Init();
+		}
+
+		public void Encode(Encoder rangeEncoder, UInt32 symbol)
+		{
+			UInt32 m = 1;
+			for (int bitIndex = NumBitLevels; bitIndex > 0; )
+			{
+				bitIndex--;
+				UInt32 bit = (symbol >> bitIndex) & 1;
+				Models[m].Encode(rangeEncoder, bit);
+				m = (m << 1) | bit;
+			}
+		}
+
+		public void ReverseEncode(Encoder rangeEncoder, UInt32 symbol)
+		{
+			UInt32 m = 1;
+			for (UInt32 i = 0; i < NumBitLevels; i++)
+			{
+				UInt32 bit = symbol & 1;
+				Models[m].Encode(rangeEncoder, bit);
+				m = (m << 1) | bit;
+				symbol >>= 1;
+			}
+		}
+
+		public UInt32 GetPrice(UInt32 symbol)
+		{
+			UInt32 price = 0;
+			UInt32 m = 1;
+			for (int bitIndex = NumBitLevels; bitIndex > 0; )
+			{
+				bitIndex--;
+				UInt32 bit = (symbol >> bitIndex) & 1;
+				price += Models[m].GetPrice(bit);
+				m = (m << 1) + bit;
+			}
+			return price;
+		}
+
+		public UInt32 ReverseGetPrice(UInt32 symbol)
+		{
+			UInt32 price = 0;
+			UInt32 m = 1;
+			for (int i = NumBitLevels; i > 0; i--)
+			{
+				UInt32 bit = symbol & 1;
+				symbol >>= 1;
+				price += Models[m].GetPrice(bit);
+				m = (m << 1) | bit;
+			}
+			return price;
+		}
+
+		public static UInt32 ReverseGetPrice(BitEncoder[] Models, UInt32 startIndex,
+			int NumBitLevels, UInt32 symbol)
+		{
+			UInt32 price = 0;
+			UInt32 m = 1;
+			for (int i = NumBitLevels; i > 0; i--)
+			{
+				UInt32 bit = symbol & 1;
+				symbol >>= 1;
+				price += Models[startIndex + m].GetPrice(bit);
+				m = (m << 1) | bit;
+			}
+			return price;
+		}
+
+		public static void ReverseEncode(BitEncoder[] Models, UInt32 startIndex,
+			Encoder rangeEncoder, int NumBitLevels, UInt32 symbol)
+		{
+			UInt32 m = 1;
+			for (int i = 0; i < NumBitLevels; i++)
+			{
+				UInt32 bit = symbol & 1;
+				Models[startIndex + m].Encode(rangeEncoder, bit);
+				m = (m << 1) | bit;
+				symbol >>= 1;
+			}
+		}
+	}
+
+	struct BitTreeDecoder
+	{
+		BitDecoder[] Models;
+		int NumBitLevels;
+
+		public BitTreeDecoder(int numBitLevels)
+		{
+			NumBitLevels = numBitLevels;
+			Models = new BitDecoder[1 << numBitLevels];
+		}
+
+		public void Init()
+		{
+			for (uint i = 1; i < (1 << NumBitLevels); i++)
+				Models[i].Init();
+		}
+
+		public uint Decode(RangeCoder.Decoder rangeDecoder)
+		{
+			uint m = 1;
+			for (int bitIndex = NumBitLevels; bitIndex > 0; bitIndex--)
+				m = (m << 1) + Models[m].Decode(rangeDecoder);
+			return m - ((uint)1 << NumBitLevels);
+		}
+
+		public uint ReverseDecode(RangeCoder.Decoder rangeDecoder)
+		{
+			uint m = 1;
+			uint symbol = 0;
+			for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++)
+			{
+				uint bit = Models[m].Decode(rangeDecoder);
+				m <<= 1;
+				m += bit;
+				symbol |= (bit << bitIndex);
+			}
+			return symbol;
+		}
+
+		public static uint ReverseDecode(BitDecoder[] Models, UInt32 startIndex,
+			RangeCoder.Decoder rangeDecoder, int NumBitLevels)
+		{
+			uint m = 1;
+			uint symbol = 0;
+			for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++)
+			{
+				uint bit = Models[startIndex + m].Decode(rangeDecoder);
+				m <<= 1;
+				m += bit;
+				symbol |= (bit << bitIndex);
+			}
+			return symbol;
+		}
+	}
+}
diff --git a/third_party/lzma/v4_65/files/CS/7zip/ICoder.cs b/third_party/lzma/v4_65/files/CS/7zip/ICoder.cs
new file mode 100644
index 0000000..85bf531
--- /dev/null
+++ b/third_party/lzma/v4_65/files/CS/7zip/ICoder.cs
@@ -0,0 +1,145 @@
+// ICoder.h
+
+using System;
+
+namespace SevenZip
+{
+	/// <summary>
+	/// The exception that is thrown when an error in input stream occurs during decoding.
+	/// </summary>
+	class DataErrorException : ApplicationException
+	{
+		public DataErrorException(): base("Data Error") { }
+	}
+
+	/// <summary>
+	/// The exception that is thrown when the value of an argument is outside the allowable range.
+	/// </summary>
+	class InvalidParamException : ApplicationException
+	{
+		public InvalidParamException(): base("Invalid Parameter") { }
+	}
+
+	public interface ICodeProgress
+	{
+		/// <summary>
+		/// Callback progress.
+		/// </summary>
+		/// <param name="inSize">
+		/// input size. -1 if unknown.
+		/// </param>
+		/// <param name="outSize">
+		/// output size. -1 if unknown.
+		/// </param>
+		void SetProgress(Int64 inSize, Int64 outSize);
+	};
+
+	public interface ICoder
+	{
+		/// <summary>
+		/// Codes streams.
+		/// </summary>
+		/// <param name="inStream">
+		/// input Stream.
+		/// </param>
+		/// <param name="outStream">
+		/// output Stream.
+		/// </param>
+		/// <param name="inSize">
+		/// input Size. -1 if unknown.
+		/// </param>
+		/// <param name="outSize">
+		/// output Size. -1 if unknown.
+		/// </param>
+		/// <param name="progress">
+		/// callback progress reference.
+		/// </param>
+		/// <exception cref="SevenZip.DataErrorException">
+		/// if input stream is not valid
+		/// </exception>
+		void Code(System.IO.Stream inStream, System.IO.Stream outStream,
+			Int64 inSize, Int64 outSize, ICodeProgress progress);
+	};
+
+	/*
+	public interface ICoder2
+	{
+		 void Code(ISequentialInStream []inStreams,
+				const UInt64 []inSizes, 
+				ISequentialOutStream []outStreams, 
+				UInt64 []outSizes,
+				ICodeProgress progress);
+	};
+  */
+
+	/// <summary>
+	/// Provides the fields that represent properties idenitifiers for compressing.
+	/// </summary>
+	public enum CoderPropID
+	{
+		/// <summary>
+		/// Specifies size of dictionary.
+		/// </summary>
+		DictionarySize = 0x400,
+		/// <summary>
+		/// Specifies size of memory for PPM*.
+		/// </summary>
+		UsedMemorySize,
+		/// <summary>
+		/// Specifies order for PPM methods.
+		/// </summary>
+		Order,
+		/// <summary>
+		/// Specifies number of postion state bits for LZMA (0 <= x <= 4).
+		/// </summary>
+		PosStateBits = 0x440,
+		/// <summary>
+		/// Specifies number of literal context bits for LZMA (0 <= x <= 8).
+		/// </summary>
+		LitContextBits,
+		/// <summary>
+		/// Specifies number of literal position bits for LZMA (0 <= x <= 4).
+		/// </summary>
+		LitPosBits,
+		/// <summary>
+		/// Specifies number of fast bytes for LZ*.
+		/// </summary>
+		NumFastBytes = 0x450,
+		/// <summary>
+		/// Specifies match finder. LZMA: "BT2", "BT4" or "BT4B".
+		/// </summary>
+		MatchFinder,
+		/// <summary>
+		/// Specifies number of passes.
+		/// </summary>
+		NumPasses = 0x460,
+		/// <summary>
+		/// Specifies number of algorithm.
+		/// </summary>
+		Algorithm = 0x470,
+		/// <summary>
+		/// Specifies multithread mode.
+		/// </summary>
+		MultiThread = 0x480,
+		/// <summary>
+		/// Specifies mode with end marker.
+		/// </summary>
+		EndMarker = 0x490
+	};
+
+
+	public interface ISetCoderProperties
+	{
+		void SetCoderProperties(CoderPropID[] propIDs, object[] properties);
+	};
+
+	public interface IWriteCoderProperties
+	{
+		void WriteCoderProperties(System.IO.Stream outStream);
+	}
+
+	public interface ISetDecoderProperties
+	{
+		void SetDecoderProperties(byte[] properties);
+	}
+}
diff --git a/third_party/lzma/v4_65/files/Java/SevenZip/CRC.java b/third_party/lzma/v4_65/files/Java/SevenZip/CRC.java
new file mode 100644
index 0000000..c55e33c
--- /dev/null
+++ b/third_party/lzma/v4_65/files/Java/SevenZip/CRC.java
@@ -0,0 +1,52 @@
+// SevenZip/CRC.java
+
+package SevenZip;
+
+public class CRC
+{
+	static public int[] Table = new int[256];
+	
+	static
+	{
+		for (int i = 0; i < 256; i++)
+		{
+			int r = i;
+			for (int j = 0; j < 8; j++)
+				if ((r & 1) != 0)
+					r = (r >>> 1) ^ 0xEDB88320;
+				else
+					r >>>= 1;
+			Table[i] = r;
+		}
+	}
+	
+	int _value = -1;
+	
+	public void Init()
+	{
+		_value = -1;
+	}
+	
+	public void Update(byte[] data, int offset, int size)
+	{
+		for (int i = 0; i < size; i++)
+			_value = Table[(_value ^ data[offset + i]) & 0xFF] ^ (_value >>> 8);
+	}
+	
+	public void Update(byte[] data)
+	{
+		int size = data.length;
+		for (int i = 0; i < size; i++)
+			_value = Table[(_value ^ data[i]) & 0xFF] ^ (_value >>> 8);
+	}
+	
+	public void UpdateByte(int b)
+	{
+		_value = Table[(_value ^ b) & 0xFF] ^ (_value >>> 8);
+	}
+	
+	public int GetDigest()
+	{
+		return _value ^ (-1);
+	}
+}
diff --git a/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZ/BinTree.java b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZ/BinTree.java
new file mode 100644
index 0000000..e2074e9
--- /dev/null
+++ b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZ/BinTree.java
@@ -0,0 +1,382 @@
+// LZ.BinTree
+
+package SevenZip.Compression.LZ;
+import java.io.IOException;
+
+
+public class BinTree extends InWindow
+{
+	int _cyclicBufferPos;
+	int _cyclicBufferSize = 0;
+	int _matchMaxLen;
+	
+	int[] _son;
+	int[] _hash;
+	
+	int _cutValue = 0xFF;
+	int _hashMask;
+	int _hashSizeSum = 0;
+	
+	boolean HASH_ARRAY = true;
+
+	static final int kHash2Size = 1 << 10;
+	static final int kHash3Size = 1 << 16;
+	static final int kBT2HashSize = 1 << 16;
+	static final int kStartMaxLen = 1;
+	static final int kHash3Offset = kHash2Size;
+	static final int kEmptyHashValue = 0;
+	static final int kMaxValForNormalize = (1 << 30) - 1;
+	
+	int kNumHashDirectBytes = 0;
+	int kMinMatchCheck = 4;
+	int kFixHashSize = kHash2Size + kHash3Size;
+
+	public void SetType(int numHashBytes)
+	{
+		HASH_ARRAY = (numHashBytes > 2);
+		if (HASH_ARRAY)
+		{
+			kNumHashDirectBytes = 0;
+			kMinMatchCheck = 4;
+			kFixHashSize = kHash2Size + kHash3Size;
+		}
+		else
+		{
+			kNumHashDirectBytes = 2;
+			kMinMatchCheck = 2 + 1;
+			kFixHashSize = 0;
+		}
+	}
+	
+
+	
+
+	public void Init() throws IOException
+	{
+		super.Init();
+		for (int i = 0; i < _hashSizeSum; i++)
+			_hash[i] = kEmptyHashValue;
+		_cyclicBufferPos = 0;
+		ReduceOffsets(-1);
+	}
+	
+	public void MovePos() throws IOException
+	{
+		if (++_cyclicBufferPos >= _cyclicBufferSize)
+			_cyclicBufferPos = 0;
+		super.MovePos();
+		if (_pos == kMaxValForNormalize)
+			Normalize();
+	}
+	
+
+	
+	
+	
+	
+	
+	
+	public boolean Create(int historySize, int keepAddBufferBefore,
+			int matchMaxLen, int keepAddBufferAfter)
+	{
+		if (historySize > kMaxValForNormalize - 256)
+			return false;
+		_cutValue = 16 + (matchMaxLen >> 1);
+
+		int windowReservSize = (historySize + keepAddBufferBefore +
+				matchMaxLen + keepAddBufferAfter) / 2 + 256;
+		
+		super.Create(historySize + keepAddBufferBefore, matchMaxLen + keepAddBufferAfter, windowReservSize);
+		
+		_matchMaxLen = matchMaxLen;
+
+		int cyclicBufferSize = historySize + 1;
+		if (_cyclicBufferSize != cyclicBufferSize)
+			_son = new int[(_cyclicBufferSize = cyclicBufferSize) * 2];
+
+		int hs = kBT2HashSize;
+
+		if (HASH_ARRAY)
+		{
+			hs = historySize - 1;
+			hs |= (hs >> 1);
+			hs |= (hs >> 2);
+			hs |= (hs >> 4);
+			hs |= (hs >> 8);
+			hs >>= 1;
+			hs |= 0xFFFF;
+			if (hs > (1 << 24))
+				hs >>= 1;
+			_hashMask = hs;
+			hs++;
+			hs += kFixHashSize;
+		}
+		if (hs != _hashSizeSum)
+			_hash = new int [_hashSizeSum = hs];
+		return true;
+	}
+	public int GetMatches(int[] distances) throws IOException
+	{
+		int lenLimit;
+		if (_pos + _matchMaxLen <= _streamPos)
+			lenLimit = _matchMaxLen;
+		else
+		{
+			lenLimit = _streamPos - _pos;
+			if (lenLimit < kMinMatchCheck)
+			{
+				MovePos();
+				return 0;
+			}
+		}
+
+		int offset = 0;
+		int matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0;
+		int cur = _bufferOffset + _pos;
+		int maxLen = kStartMaxLen; // to avoid items for len < hashSize;
+		int hashValue, hash2Value = 0, hash3Value = 0;
+		
+		if (HASH_ARRAY)
+		{
+			int temp = CrcTable[_bufferBase[cur] & 0xFF] ^ (_bufferBase[cur + 1] & 0xFF);
+			hash2Value = temp & (kHash2Size - 1);
+			temp ^= ((int)(_bufferBase[cur + 2] & 0xFF) << 8);
+			hash3Value = temp & (kHash3Size - 1);
+			hashValue = (temp ^ (CrcTable[_bufferBase[cur + 3] & 0xFF] << 5)) & _hashMask;
+		}
+		else
+			hashValue = ((_bufferBase[cur] & 0xFF) ^ ((int)(_bufferBase[cur + 1] & 0xFF) << 8));
+
+		int curMatch = _hash[kFixHashSize + hashValue];
+		if (HASH_ARRAY)
+		{
+			int curMatch2 = _hash[hash2Value];
+			int curMatch3 = _hash[kHash3Offset + hash3Value];
+			_hash[hash2Value] = _pos;
+			_hash[kHash3Offset + hash3Value] = _pos;
+			if (curMatch2 > matchMinPos)
+				if (_bufferBase[_bufferOffset + curMatch2] == _bufferBase[cur])
+				{
+					distances[offset++] = maxLen = 2;
+					distances[offset++] = _pos - curMatch2 - 1;
+				}
+			if (curMatch3 > matchMinPos)
+				if (_bufferBase[_bufferOffset + curMatch3] == _bufferBase[cur])
+				{
+					if (curMatch3 == curMatch2)
+						offset -= 2;
+					distances[offset++] = maxLen = 3;
+					distances[offset++] = _pos - curMatch3 - 1;
+					curMatch2 = curMatch3;
+				}
+			if (offset != 0 && curMatch2 == curMatch)
+			{
+				offset -= 2;
+				maxLen = kStartMaxLen;
+			}
+		}
+
+		_hash[kFixHashSize + hashValue] = _pos;
+
+		int ptr0 = (_cyclicBufferPos << 1) + 1;
+		int ptr1 = (_cyclicBufferPos << 1);
+
+		int len0, len1;
+		len0 = len1 = kNumHashDirectBytes;
+
+		if (kNumHashDirectBytes != 0)
+		{
+			if (curMatch > matchMinPos)
+			{
+				if (_bufferBase[_bufferOffset + curMatch + kNumHashDirectBytes] !=
+						_bufferBase[cur + kNumHashDirectBytes])
+				{
+					distances[offset++] = maxLen = kNumHashDirectBytes;
+					distances[offset++] = _pos - curMatch - 1;
+				}
+			}
+		}
+
+		int count = _cutValue;
+
+		while (true)
+		{
+			if (curMatch <= matchMinPos || count-- == 0)
+			{
+				_son[ptr0] = _son[ptr1] = kEmptyHashValue;
+				break;
+			}
+			int delta = _pos - curMatch;
+			int cyclicPos = ((delta <= _cyclicBufferPos) ?
+				(_cyclicBufferPos - delta) :
+				(_cyclicBufferPos - delta + _cyclicBufferSize)) << 1;
+
+			int pby1 = _bufferOffset + curMatch;
+			int len = Math.min(len0, len1);
+			if (_bufferBase[pby1 + len] == _bufferBase[cur + len])
+			{
+				while(++len != lenLimit)
+					if (_bufferBase[pby1 + len] != _bufferBase[cur + len])
+						break;
+				if (maxLen < len)
+				{
+					distances[offset++] = maxLen = len;
+					distances[offset++] = delta - 1;
+					if (len == lenLimit)
+					{
+						_son[ptr1] = _son[cyclicPos];
+						_son[ptr0] = _son[cyclicPos + 1];
+						break;
+					}
+				}
+			}
+			if ((_bufferBase[pby1 + len] & 0xFF) < (_bufferBase[cur + len] & 0xFF))
+			{
+				_son[ptr1] = curMatch;
+				ptr1 = cyclicPos + 1;
+				curMatch = _son[ptr1];
+				len1 = len;
+			}
+			else
+			{
+				_son[ptr0] = curMatch;
+				ptr0 = cyclicPos;
+				curMatch = _son[ptr0];
+				len0 = len;
+			}
+		}
+		MovePos();
+		return offset;
+	}
+
+	public void Skip(int num) throws IOException
+	{
+		do
+		{
+			int lenLimit;
+			if (_pos + _matchMaxLen <= _streamPos)
+			lenLimit = _matchMaxLen;
+			else
+			{
+				lenLimit = _streamPos - _pos;
+				if (lenLimit < kMinMatchCheck)
+				{
+					MovePos();
+					continue;
+				}
+			}
+
+			int matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0;
+			int cur = _bufferOffset + _pos;
+			
+			int hashValue;
+
+			if (HASH_ARRAY)
+			{
+				int temp = CrcTable[_bufferBase[cur] & 0xFF] ^ (_bufferBase[cur + 1] & 0xFF);
+				int hash2Value = temp & (kHash2Size - 1);
+				_hash[hash2Value] = _pos;
+				temp ^= ((int)(_bufferBase[cur + 2] & 0xFF) << 8);
+				int hash3Value = temp & (kHash3Size - 1);
+				_hash[kHash3Offset + hash3Value] = _pos;
+				hashValue = (temp ^ (CrcTable[_bufferBase[cur + 3] & 0xFF] << 5)) & _hashMask;
+			}
+			else
+				hashValue = ((_bufferBase[cur] & 0xFF) ^ ((int)(_bufferBase[cur + 1] & 0xFF) << 8));
+
+			int curMatch = _hash[kFixHashSize + hashValue];
+			_hash[kFixHashSize + hashValue] = _pos;
+
+			int ptr0 = (_cyclicBufferPos << 1) + 1;
+			int ptr1 = (_cyclicBufferPos << 1);
+
+			int len0, len1;
+			len0 = len1 = kNumHashDirectBytes;
+
+			int count = _cutValue;
+			while (true)
+			{
+				if (curMatch <= matchMinPos || count-- == 0)
+				{
+					_son[ptr0] = _son[ptr1] = kEmptyHashValue;
+					break;
+				}
+
+				int delta = _pos - curMatch;
+				int cyclicPos = ((delta <= _cyclicBufferPos) ?
+					(_cyclicBufferPos - delta) :
+					(_cyclicBufferPos - delta + _cyclicBufferSize)) << 1;
+
+				int pby1 = _bufferOffset + curMatch;
+				int len = Math.min(len0, len1);
+				if (_bufferBase[pby1 + len] == _bufferBase[cur + len])
+				{
+					while (++len != lenLimit)
+						if (_bufferBase[pby1 + len] != _bufferBase[cur + len])
+							break;
+					if (len == lenLimit)
+					{
+						_son[ptr1] = _son[cyclicPos];
+						_son[ptr0] = _son[cyclicPos + 1];
+						break;
+					}
+				}
+				if ((_bufferBase[pby1 + len] & 0xFF) < (_bufferBase[cur + len] & 0xFF))
+				{
+					_son[ptr1] = curMatch;
+					ptr1 = cyclicPos + 1;
+					curMatch = _son[ptr1];
+					len1 = len;
+				}
+				else
+				{
+					_son[ptr0] = curMatch;
+					ptr0 = cyclicPos;
+					curMatch = _son[ptr0];
+					len0 = len;
+				}
+			}
+			MovePos();
+		}
+		while (--num != 0);
+	}
+	
+	void NormalizeLinks(int[] items, int numItems, int subValue)
+	{
+		for (int i = 0; i < numItems; i++)
+		{
+			int value = items[i];
+			if (value <= subValue)
+				value = kEmptyHashValue;
+			else
+				value -= subValue;
+			items[i] = value;
+		}
+	}
+	
+	void Normalize()
+	{
+		int subValue = _pos - _cyclicBufferSize;
+		NormalizeLinks(_son, _cyclicBufferSize * 2, subValue);
+		NormalizeLinks(_hash, _hashSizeSum, subValue);
+		ReduceOffsets(subValue);
+	}
+	
+	public void SetCutValue(int cutValue) { _cutValue = cutValue; }
+
+	private static final int[] CrcTable = new int[256];
+
+	static
+	{
+		for (int i = 0; i < 256; i++)
+		{
+			int r = i;
+			for (int j = 0; j < 8; j++)
+				if ((r & 1) != 0)
+					r = (r >>> 1) ^ 0xEDB88320;
+				else
+					r >>>= 1;
+			CrcTable[i] = r;
+		}
+	}
+}
diff --git a/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZ/InWindow.java b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZ/InWindow.java
new file mode 100644
index 0000000..c9efe60
--- /dev/null
+++ b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZ/InWindow.java
@@ -0,0 +1,131 @@
+// LZ.InWindow
+
+package SevenZip.Compression.LZ;
+
+import java.io.IOException;
+
+public class InWindow
+{
+	public byte[] _bufferBase; // pointer to buffer with data
+	java.io.InputStream _stream;
+	int _posLimit;  // offset (from _buffer) of first byte when new block reading must be done
+	boolean _streamEndWasReached; // if (true) then _streamPos shows real end of stream
+	
+	int _pointerToLastSafePosition;
+	
+	public int _bufferOffset;
+	
+	public int _blockSize;  // Size of Allocated memory block
+	public int _pos;             // offset (from _buffer) of curent byte
+	int _keepSizeBefore;  // how many BYTEs must be kept in buffer before _pos
+	int _keepSizeAfter;   // how many BYTEs must be kept buffer after _pos
+	public int _streamPos;   // offset (from _buffer) of first not read byte from Stream
+	
+	public void MoveBlock()
+	{
+		int offset = _bufferOffset + _pos - _keepSizeBefore;
+		// we need one additional byte, since MovePos moves on 1 byte.
+		if (offset > 0)
+			offset--;
+
+		int numBytes = _bufferOffset + _streamPos - offset;
+		
+		// check negative offset ????
+		for (int i = 0; i < numBytes; i++)
+			_bufferBase[i] = _bufferBase[offset + i];
+		_bufferOffset -= offset;
+	}
+	
+	public void ReadBlock() throws IOException
+	{
+		if (_streamEndWasReached)
+			return;
+		while (true)
+		{
+			int size = (0 - _bufferOffset) + _blockSize - _streamPos;
+			if (size == 0)
+				return;
+			int numReadBytes = _stream.read(_bufferBase, _bufferOffset + _streamPos, size);
+			if (numReadBytes == -1)
+			{
+				_posLimit = _streamPos;
+				int pointerToPostion = _bufferOffset + _posLimit;
+				if (pointerToPostion > _pointerToLastSafePosition)
+					_posLimit = _pointerToLastSafePosition - _bufferOffset;
+				
+				_streamEndWasReached = true;
+				return;
+			}
+			_streamPos += numReadBytes;
+			if (_streamPos >= _pos + _keepSizeAfter)
+				_posLimit = _streamPos - _keepSizeAfter;
+		}
+	}
+	
+	void Free() { _bufferBase = null; }
+	
+	public void Create(int keepSizeBefore, int keepSizeAfter, int keepSizeReserv)
+	{
+		_keepSizeBefore = keepSizeBefore;
+		_keepSizeAfter = keepSizeAfter;
+		int blockSize = keepSizeBefore + keepSizeAfter + keepSizeReserv;
+		if (_bufferBase == null || _blockSize != blockSize)
+		{
+			Free();
+			_blockSize = blockSize;
+			_bufferBase = new byte[_blockSize];
+		}
+		_pointerToLastSafePosition = _blockSize - keepSizeAfter;
+	}
+	
+	public void SetStream(java.io.InputStream stream) { _stream = stream; 	}
+	public void ReleaseStream() { _stream = null; }
+
+	public void Init() throws IOException
+	{
+		_bufferOffset = 0;
+		_pos = 0;
+		_streamPos = 0;
+		_streamEndWasReached = false;
+		ReadBlock();
+	}
+	
+	public void MovePos() throws IOException
+	{
+		_pos++;
+		if (_pos > _posLimit)
+		{
+			int pointerToPostion = _bufferOffset + _pos;
+			if (pointerToPostion > _pointerToLastSafePosition)
+				MoveBlock();
+			ReadBlock();
+		}
+	}
+	
+	public byte GetIndexByte(int index)	{ return _bufferBase[_bufferOffset + _pos + index]; }
+	
+	// index + limit have not to exceed _keepSizeAfter;
+	public int GetMatchLen(int index, int distance, int limit)
+	{
+		if (_streamEndWasReached)
+			if ((_pos + index) + limit > _streamPos)
+				limit = _streamPos - (_pos + index);
+		distance++;
+		// Byte *pby = _buffer + (size_t)_pos + index;
+		int pby = _bufferOffset + _pos + index;
+		
+		int i;
+		for (i = 0; i < limit && _bufferBase[pby + i] == _bufferBase[pby + i - distance]; i++);
+		return i;
+	}
+	
+	public int GetNumAvailableBytes()	{ return _streamPos - _pos; }
+	
+	public void ReduceOffsets(int subValue)
+	{
+		_bufferOffset += subValue;
+		_posLimit -= subValue;
+		_pos -= subValue;
+		_streamPos -= subValue;
+	}
+}
diff --git a/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZ/OutWindow.java b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZ/OutWindow.java
new file mode 100644
index 0000000..2fd2832
--- /dev/null
+++ b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZ/OutWindow.java
@@ -0,0 +1,85 @@
+// LZ.OutWindow
+
+package SevenZip.Compression.LZ;
+
+import java.io.IOException;
+
+public class OutWindow
+{
+	byte[] _buffer;
+	int _pos;
+	int _windowSize = 0;
+	int _streamPos;
+	java.io.OutputStream _stream;
+	
+	public void Create(int windowSize)
+	{
+		if (_buffer == null || _windowSize != windowSize)
+			_buffer = new byte[windowSize];
+		_windowSize = windowSize;
+		_pos = 0;
+		_streamPos = 0;
+	}
+	
+	public void SetStream(java.io.OutputStream stream) throws IOException
+	{
+		ReleaseStream();
+		_stream = stream;
+	}
+	
+	public void ReleaseStream() throws IOException
+	{
+		Flush();
+		_stream = null;
+	}
+	
+	public void Init(boolean solid)
+	{
+		if (!solid)
+		{
+			_streamPos = 0;
+			_pos = 0;
+		}
+	}
+	
+	public void Flush() throws IOException
+	{
+		int size = _pos - _streamPos;
+		if (size == 0)
+			return;
+		_stream.write(_buffer, _streamPos, size);
+		if (_pos >= _windowSize)
+			_pos = 0;
+		_streamPos = _pos;
+	}
+	
+	public void CopyBlock(int distance, int len) throws IOException
+	{
+		int pos = _pos - distance - 1;
+		if (pos < 0)
+			pos += _windowSize;
+		for (; len != 0; len--)
+		{
+			if (pos >= _windowSize)
+				pos = 0;
+			_buffer[_pos++] = _buffer[pos++];
+			if (_pos >= _windowSize)
+				Flush();
+		}
+	}
+	
+	public void PutByte(byte b) throws IOException
+	{
+		_buffer[_pos++] = b;
+		if (_pos >= _windowSize)
+			Flush();
+	}
+	
+	public byte GetByte(int distance)
+	{
+		int pos = _pos - distance - 1;
+		if (pos < 0)
+			pos += _windowSize;
+		return _buffer[pos];
+	}
+}
diff --git a/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZMA/Base.java b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZMA/Base.java
new file mode 100644
index 0000000..b4f2fb5
--- /dev/null
+++ b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZMA/Base.java
@@ -0,0 +1,88 @@
+// Base.java
+
+package SevenZip.Compression.LZMA;
+
+public class Base
+{
+	public static final int kNumRepDistances = 4;
+	public static final int kNumStates = 12;
+	
+	public static final int StateInit()
+	{
+		return 0;
+	}
+	
+	public static final int StateUpdateChar(int index)
+	{
+		if (index < 4) 
+			return 0;
+		if (index < 10) 
+			return index - 3;
+		return index - 6;
+	}
+	
+	public static final int StateUpdateMatch(int index)
+	{
+		return (index < 7 ? 7 : 10); 
+	}
+
+	public static final int StateUpdateRep(int index)
+	{ 
+		return (index < 7 ? 8 : 11); 
+	}
+	
+	public static final int StateUpdateShortRep(int index)
+	{ 
+		return (index < 7 ? 9 : 11); 
+	}
+
+	public static final boolean StateIsCharState(int index)
+	{ 
+		return index < 7; 
+	}
+	
+	public static final int kNumPosSlotBits = 6;
+	public static final int kDicLogSizeMin = 0;
+	// public static final int kDicLogSizeMax = 28;
+	// public static final int kDistTableSizeMax = kDicLogSizeMax * 2;
+	
+	public static final int kNumLenToPosStatesBits = 2; // it's for speed optimization
+	public static final int kNumLenToPosStates = 1 << kNumLenToPosStatesBits;
+	
+	public static final int kMatchMinLen = 2;
+	
+	public static final int GetLenToPosState(int len)
+	{
+		len -= kMatchMinLen;
+		if (len < kNumLenToPosStates)
+			return len;
+		return (int)(kNumLenToPosStates - 1);
+	}
+	
+	public static final int kNumAlignBits = 4;
+	public static final int kAlignTableSize = 1 << kNumAlignBits;
+	public static final int kAlignMask = (kAlignTableSize - 1);
+	
+	public static final int kStartPosModelIndex = 4;
+	public static final int kEndPosModelIndex = 14;
+	public static final int kNumPosModels = kEndPosModelIndex - kStartPosModelIndex;
+	
+	public static final  int kNumFullDistances = 1 << (kEndPosModelIndex / 2);
+	
+	public static final  int kNumLitPosStatesBitsEncodingMax = 4;
+	public static final  int kNumLitContextBitsMax = 8;
+	
+	public static final  int kNumPosStatesBitsMax = 4;
+	public static final  int kNumPosStatesMax = (1 << kNumPosStatesBitsMax);
+	public static final  int kNumPosStatesBitsEncodingMax = 4;
+	public static final  int kNumPosStatesEncodingMax = (1 << kNumPosStatesBitsEncodingMax);
+	
+	public static final  int kNumLowLenBits = 3;
+	public static final  int kNumMidLenBits = 3;
+	public static final  int kNumHighLenBits = 8;
+	public static final  int kNumLowLenSymbols = 1 << kNumLowLenBits;
+	public static final  int kNumMidLenSymbols = 1 << kNumMidLenBits;
+	public static final  int kNumLenSymbols = kNumLowLenSymbols + kNumMidLenSymbols +
+			(1 << kNumHighLenBits);
+	public static final  int kMatchMaxLen = kMatchMinLen + kNumLenSymbols - 1;
+}
diff --git a/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZMA/Decoder.java b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZMA/Decoder.java
new file mode 100644
index 0000000..16ee249
--- /dev/null
+++ b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZMA/Decoder.java
@@ -0,0 +1,329 @@
+package SevenZip.Compression.LZMA;
+
+import SevenZip.Compression.RangeCoder.BitTreeDecoder;
+import SevenZip.Compression.LZMA.Base;
+import SevenZip.Compression.LZ.OutWindow;
+import java.io.IOException;
+
+public class Decoder
+{
+	class LenDecoder
+	{
+		short[] m_Choice = new short[2];
+		BitTreeDecoder[] m_LowCoder = new BitTreeDecoder[Base.kNumPosStatesMax];
+		BitTreeDecoder[] m_MidCoder = new BitTreeDecoder[Base.kNumPosStatesMax];
+		BitTreeDecoder m_HighCoder = new BitTreeDecoder(Base.kNumHighLenBits);
+		int m_NumPosStates = 0;
+		
+		public void Create(int numPosStates)
+		{
+			for (; m_NumPosStates < numPosStates; m_NumPosStates++)
+			{
+				m_LowCoder[m_NumPosStates] = new BitTreeDecoder(Base.kNumLowLenBits);
+				m_MidCoder[m_NumPosStates] = new BitTreeDecoder(Base.kNumMidLenBits);
+			}
+		}
+		
+		public void Init()
+		{
+			SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_Choice);
+			for (int posState = 0; posState < m_NumPosStates; posState++)
+			{
+				m_LowCoder[posState].Init();
+				m_MidCoder[posState].Init();
+			}
+			m_HighCoder.Init();
+		}
+		
+		public int Decode(SevenZip.Compression.RangeCoder.Decoder rangeDecoder, int posState) throws IOException
+		{
+			if (rangeDecoder.DecodeBit(m_Choice, 0) == 0)
+				return m_LowCoder[posState].Decode(rangeDecoder);
+			int symbol = Base.kNumLowLenSymbols;
+			if (rangeDecoder.DecodeBit(m_Choice, 1) == 0)
+				symbol += m_MidCoder[posState].Decode(rangeDecoder);
+			else
+				symbol += Base.kNumMidLenSymbols + m_HighCoder.Decode(rangeDecoder);
+			return symbol;
+		}
+	}
+	
+	class LiteralDecoder
+	{
+		class Decoder2
+		{
+			short[] m_Decoders = new short[0x300];
+			
+			public void Init()
+			{
+				SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_Decoders);
+			}
+			
+			public byte DecodeNormal(SevenZip.Compression.RangeCoder.Decoder rangeDecoder) throws IOException
+			{
+				int symbol = 1;
+				do
+					symbol = (symbol << 1) | rangeDecoder.DecodeBit(m_Decoders, symbol);
+				while (symbol < 0x100);
+				return (byte)symbol;
+			}
+			
+			public byte DecodeWithMatchByte(SevenZip.Compression.RangeCoder.Decoder rangeDecoder, byte matchByte) throws IOException
+			{
+				int symbol = 1;
+				do
+				{
+					int matchBit = (matchByte >> 7) & 1;
+					matchByte <<= 1;
+					int bit = rangeDecoder.DecodeBit(m_Decoders, ((1 + matchBit) << 8) + symbol);
+					symbol = (symbol << 1) | bit;
+					if (matchBit != bit)
+					{
+						while (symbol < 0x100)
+							symbol = (symbol << 1) | rangeDecoder.DecodeBit(m_Decoders, symbol);
+						break;
+					}
+				}
+				while (symbol < 0x100);
+				return (byte)symbol;
+			}
+		}
+		
+		Decoder2[] m_Coders;
+		int m_NumPrevBits;
+		int m_NumPosBits;
+		int m_PosMask;
+		
+		public void Create(int numPosBits, int numPrevBits)
+		{
+			if (m_Coders != null && m_NumPrevBits == numPrevBits && m_NumPosBits == numPosBits)
+				return;
+			m_NumPosBits = numPosBits;
+			m_PosMask = (1 << numPosBits) - 1;
+			m_NumPrevBits = numPrevBits;
+			int numStates = 1 << (m_NumPrevBits + m_NumPosBits);
+			m_Coders = new Decoder2[numStates];
+			for (int i = 0; i < numStates; i++)
+				m_Coders[i] = new Decoder2();
+		}
+		
+		public void Init()
+		{
+			int numStates = 1 << (m_NumPrevBits + m_NumPosBits);
+			for (int i = 0; i < numStates; i++)
+				m_Coders[i].Init();
+		}
+		
+		Decoder2 GetDecoder(int pos, byte prevByte)
+		{
+			return m_Coders[((pos & m_PosMask) << m_NumPrevBits) + ((prevByte & 0xFF) >>> (8 - m_NumPrevBits))];
+		}
+	}
+	
+	OutWindow m_OutWindow = new OutWindow();
+	SevenZip.Compression.RangeCoder.Decoder m_RangeDecoder = new SevenZip.Compression.RangeCoder.Decoder();
+	
+	short[] m_IsMatchDecoders = new short[Base.kNumStates << Base.kNumPosStatesBitsMax];
+	short[] m_IsRepDecoders = new short[Base.kNumStates];
+	short[] m_IsRepG0Decoders = new short[Base.kNumStates];
+	short[] m_IsRepG1Decoders = new short[Base.kNumStates];
+	short[] m_IsRepG2Decoders = new short[Base.kNumStates];
+	short[] m_IsRep0LongDecoders = new short[Base.kNumStates << Base.kNumPosStatesBitsMax];
+	
+	BitTreeDecoder[] m_PosSlotDecoder = new BitTreeDecoder[Base.kNumLenToPosStates];
+	short[] m_PosDecoders = new short[Base.kNumFullDistances - Base.kEndPosModelIndex];
+	
+	BitTreeDecoder m_PosAlignDecoder = new BitTreeDecoder(Base.kNumAlignBits);
+	
+	LenDecoder m_LenDecoder = new LenDecoder();
+	LenDecoder m_RepLenDecoder = new LenDecoder();
+	
+	LiteralDecoder m_LiteralDecoder = new LiteralDecoder();
+	
+	int m_DictionarySize = -1;
+	int m_DictionarySizeCheck =  -1;
+	
+	int m_PosStateMask;
+	
+	public Decoder()
+	{
+		for (int i = 0; i < Base.kNumLenToPosStates; i++)
+			m_PosSlotDecoder[i] = new BitTreeDecoder(Base.kNumPosSlotBits);
+	}
+	
+	boolean SetDictionarySize(int dictionarySize)
+	{
+		if (dictionarySize < 0)
+			return false;
+		if (m_DictionarySize != dictionarySize)
+		{
+			m_DictionarySize = dictionarySize;
+			m_DictionarySizeCheck = Math.max(m_DictionarySize, 1);
+			m_OutWindow.Create(Math.max(m_DictionarySizeCheck, (1 << 12)));
+		}
+		return true;
+	}
+	
+	boolean SetLcLpPb(int lc, int lp, int pb)
+	{
+		if (lc > Base.kNumLitContextBitsMax || lp > 4 || pb > Base.kNumPosStatesBitsMax)
+			return false;
+		m_LiteralDecoder.Create(lp, lc);
+		int numPosStates = 1 << pb;
+		m_LenDecoder.Create(numPosStates);
+		m_RepLenDecoder.Create(numPosStates);
+		m_PosStateMask = numPosStates - 1;
+		return true;
+	}
+	
+	void Init() throws IOException
+	{
+		m_OutWindow.Init(false);
+		
+		SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsMatchDecoders);
+		SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsRep0LongDecoders);
+		SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsRepDecoders);
+		SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsRepG0Decoders);
+		SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsRepG1Decoders);
+		SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_IsRepG2Decoders);
+		SevenZip.Compression.RangeCoder.Decoder.InitBitModels(m_PosDecoders);
+		
+		m_LiteralDecoder.Init();
+		int i;
+		for (i = 0; i < Base.kNumLenToPosStates; i++)
+			m_PosSlotDecoder[i].Init();
+		m_LenDecoder.Init();
+		m_RepLenDecoder.Init();
+		m_PosAlignDecoder.Init();
+		m_RangeDecoder.Init();
+	}
+	
+	public boolean Code(java.io.InputStream inStream, java.io.OutputStream outStream,
+			long outSize) throws IOException
+	{
+		m_RangeDecoder.SetStream(inStream);
+		m_OutWindow.SetStream(outStream);
+		Init();
+		
+		int state = Base.StateInit();
+		int rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0;
+		
+		long nowPos64 = 0;
+		byte prevByte = 0;
+		while (outSize < 0 || nowPos64 < outSize)
+		{
+			int posState = (int)nowPos64 & m_PosStateMask;
+			if (m_RangeDecoder.DecodeBit(m_IsMatchDecoders, (state << Base.kNumPosStatesBitsMax) + posState) == 0)
+			{
+				LiteralDecoder.Decoder2 decoder2 = m_LiteralDecoder.GetDecoder((int)nowPos64, prevByte);
+				if (!Base.StateIsCharState(state))
+					prevByte = decoder2.DecodeWithMatchByte(m_RangeDecoder, m_OutWindow.GetByte(rep0));
+				else
+					prevByte = decoder2.DecodeNormal(m_RangeDecoder);
+				m_OutWindow.PutByte(prevByte);
+				state = Base.StateUpdateChar(state);
+				nowPos64++;
+			}
+			else
+			{
+				int len;
+				if (m_RangeDecoder.DecodeBit(m_IsRepDecoders, state) == 1)
+				{
+					len = 0;
+					if (m_RangeDecoder.DecodeBit(m_IsRepG0Decoders, state) == 0)
+					{
+						if (m_RangeDecoder.DecodeBit(m_IsRep0LongDecoders, (state << Base.kNumPosStatesBitsMax) + posState) == 0)
+						{
+							state = Base.StateUpdateShortRep(state);
+							len = 1;
+						}
+					}
+					else
+					{
+						int distance;
+						if (m_RangeDecoder.DecodeBit(m_IsRepG1Decoders, state) == 0)
+							distance = rep1;
+						else
+						{
+							if (m_RangeDecoder.DecodeBit(m_IsRepG2Decoders, state) == 0)
+								distance = rep2;
+							else
+							{
+								distance = rep3;
+								rep3 = rep2;
+							}
+							rep2 = rep1;
+						}
+						rep1 = rep0;
+						rep0 = distance;
+					}
+					if (len == 0)
+					{
+						len = m_RepLenDecoder.Decode(m_RangeDecoder, posState) + Base.kMatchMinLen;
+						state = Base.StateUpdateRep(state);
+					}
+				}
+				else
+				{
+					rep3 = rep2;
+					rep2 = rep1;
+					rep1 = rep0;
+					len = Base.kMatchMinLen + m_LenDecoder.Decode(m_RangeDecoder, posState);
+					state = Base.StateUpdateMatch(state);
+					int posSlot = m_PosSlotDecoder[Base.GetLenToPosState(len)].Decode(m_RangeDecoder);
+					if (posSlot >= Base.kStartPosModelIndex)
+					{
+						int numDirectBits = (posSlot >> 1) - 1;
+						rep0 = ((2 | (posSlot & 1)) << numDirectBits);
+						if (posSlot < Base.kEndPosModelIndex)
+							rep0 += BitTreeDecoder.ReverseDecode(m_PosDecoders,
+									rep0 - posSlot - 1, m_RangeDecoder, numDirectBits);
+						else
+						{
+							rep0 += (m_RangeDecoder.DecodeDirectBits(
+									numDirectBits - Base.kNumAlignBits) << Base.kNumAlignBits);
+							rep0 += m_PosAlignDecoder.ReverseDecode(m_RangeDecoder);
+							if (rep0 < 0)
+							{
+								if (rep0 == -1)
+									break;
+								return false;
+							}
+						}
+					}
+					else
+						rep0 = posSlot;
+				}
+				if (rep0 >= nowPos64 || rep0 >= m_DictionarySizeCheck)
+				{
+					// m_OutWindow.Flush();
+					return false;
+				}
+				m_OutWindow.CopyBlock(rep0, len);
+				nowPos64 += len;
+				prevByte = m_OutWindow.GetByte(0);
+			}
+		}
+		m_OutWindow.Flush();
+		m_OutWindow.ReleaseStream();
+		m_RangeDecoder.ReleaseStream();
+		return true;
+	}
+	
+	public boolean SetDecoderProperties(byte[] properties)
+	{
+		if (properties.length < 5)
+			return false;
+		int val = properties[0] & 0xFF;
+		int lc = val % 9;
+		int remainder = val / 9;
+		int lp = remainder % 5;
+		int pb = remainder / 5;
+		int dictionarySize = 0;
+		for (int i = 0; i < 4; i++)
+			dictionarySize += ((int)(properties[1 + i]) & 0xFF) << (i * 8);
+		if (!SetLcLpPb(lc, lp, pb))
+			return false;
+		return SetDictionarySize(dictionarySize);
+	}
+}
diff --git a/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZMA/Encoder.java b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZMA/Encoder.java
new file mode 100644
index 0000000..e421810
--- /dev/null
+++ b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/LZMA/Encoder.java
@@ -0,0 +1,1416 @@
+package SevenZip.Compression.LZMA;
+
+import SevenZip.Compression.RangeCoder.BitTreeEncoder;
+import SevenZip.Compression.LZMA.Base;
+import SevenZip.Compression.LZ.BinTree;
+import SevenZip.ICodeProgress;
+import java.io.IOException;
+
+public class Encoder
+{
+	public static final int EMatchFinderTypeBT2 = 0;
+	public static final int EMatchFinderTypeBT4 = 1;
+
+
+
+
+	static final int kIfinityPrice = 0xFFFFFFF;
+
+	static byte[] g_FastPos = new byte[1 << 11];
+
+	static
+	{
+		int kFastSlots = 22;
+		int c = 2;
+		g_FastPos[0] = 0;
+		g_FastPos[1] = 1;
+		for (int slotFast = 2; slotFast < kFastSlots; slotFast++)
+		{
+			int k = (1 << ((slotFast >> 1) - 1));
+			for (int j = 0; j < k; j++, c++)
+				g_FastPos[c] = (byte)slotFast;
+		}
+	}
+
+	static int GetPosSlot(int pos)
+	{
+		if (pos < (1 << 11))
+			return g_FastPos[pos];
+		if (pos < (1 << 21))
+			return (g_FastPos[pos >> 10] + 20);
+		return (g_FastPos[pos >> 20] + 40);
+	}
+
+	static int GetPosSlot2(int pos)
+	{
+		if (pos < (1 << 17))
+			return (g_FastPos[pos >> 6] + 12);
+		if (pos < (1 << 27))
+			return (g_FastPos[pos >> 16] + 32);
+		return (g_FastPos[pos >> 26] + 52);
+	}
+
+	int _state = Base.StateInit();
+	byte _previousByte;
+	int[] _repDistances = new int[Base.kNumRepDistances];
+
+	void BaseInit()
+	{
+		_state = Base.StateInit();
+		_previousByte = 0;
+		for (int i = 0; i < Base.kNumRepDistances; i++)
+			_repDistances[i] = 0;
+	}
+
+	static final int kDefaultDictionaryLogSize = 22;
+	static final int kNumFastBytesDefault = 0x20;
+
+	class LiteralEncoder
+	{
+		class Encoder2
+		{
+			short[] m_Encoders = new short[0x300];
+
+			public void Init() { SevenZip.Compression.RangeCoder.Encoder.InitBitModels(m_Encoders); }
+
+
+
+			public void Encode(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, byte symbol) throws IOException
+			{
+				int context = 1;
+				for (int i = 7; i >= 0; i--)
+				{
+					int bit = ((symbol >> i) & 1);
+					rangeEncoder.Encode(m_Encoders, context, bit);
+					context = (context << 1) | bit;
+				}
+			}
+
+			public void EncodeMatched(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, byte matchByte, byte symbol) throws IOException
+			{
+				int context = 1;
+				boolean same = true;
+				for (int i = 7; i >= 0; i--)
+				{
+					int bit = ((symbol >> i) & 1);
+					int state = context;
+					if (same)
+					{
+						int matchBit = ((matchByte >> i) & 1);
+						state += ((1 + matchBit) << 8);
+						same = (matchBit == bit);
+					}
+					rangeEncoder.Encode(m_Encoders, state, bit);
+					context = (context << 1) | bit;
+				}
+			}
+
+			public int GetPrice(boolean matchMode, byte matchByte, byte symbol)
+			{
+				int price = 0;
+				int context = 1;
+				int i = 7;
+				if (matchMode)
+				{
+					for (; i >= 0; i--)
+					{
+						int matchBit = (matchByte >> i) & 1;
+						int bit = (symbol >> i) & 1;
+						price += SevenZip.Compression.RangeCoder.Encoder.GetPrice(m_Encoders[((1 + matchBit) << 8) + context], bit);
+						context = (context << 1) | bit;
+						if (matchBit != bit)
+						{
+							i--;
+							break;
+						}
+					}
+				}
+				for (; i >= 0; i--)
+				{
+					int bit = (symbol >> i) & 1;
+					price += SevenZip.Compression.RangeCoder.Encoder.GetPrice(m_Encoders[context], bit);
+					context = (context << 1) | bit;
+				}
+				return price;
+			}
+		}
+
+		Encoder2[] m_Coders;
+		int m_NumPrevBits;
+		int m_NumPosBits;
+		int m_PosMask;
+
+		public void Create(int numPosBits, int numPrevBits)
+		{
+			if (m_Coders != null && m_NumPrevBits == numPrevBits && m_NumPosBits == numPosBits)
+				return;
+			m_NumPosBits = numPosBits;
+			m_PosMask = (1 << numPosBits) - 1;
+			m_NumPrevBits = numPrevBits;
+			int numStates = 1 << (m_NumPrevBits + m_NumPosBits);
+			m_Coders = new Encoder2[numStates];
+			for (int i = 0; i < numStates; i++)
+				m_Coders[i] = new Encoder2();
+		}
+
+		public void Init()
+		{
+			int numStates = 1 << (m_NumPrevBits + m_NumPosBits);
+			for (int i = 0; i < numStates; i++)
+				m_Coders[i].Init();
+		}
+
+		public Encoder2 GetSubCoder(int pos, byte prevByte)
+		{ return m_Coders[((pos & m_PosMask) << m_NumPrevBits) + ((prevByte & 0xFF) >>> (8 - m_NumPrevBits))]; }
+	}
+
+	class LenEncoder
+	{
+		short[] _choice = new short[2];
+		BitTreeEncoder[] _lowCoder = new BitTreeEncoder[Base.kNumPosStatesEncodingMax];
+		BitTreeEncoder[] _midCoder = new BitTreeEncoder[Base.kNumPosStatesEncodingMax];
+		BitTreeEncoder _highCoder = new BitTreeEncoder(Base.kNumHighLenBits);
+
+
+		public LenEncoder()
+		{
+			for (int posState = 0; posState < Base.kNumPosStatesEncodingMax; posState++)
+			{
+				_lowCoder[posState] = new BitTreeEncoder(Base.kNumLowLenBits);
+				_midCoder[posState] = new BitTreeEncoder(Base.kNumMidLenBits);
+			}
+		}
+
+		public void Init(int numPosStates)
+		{
+			SevenZip.Compression.RangeCoder.Encoder.InitBitModels(_choice);
+
+			for (int posState = 0; posState < numPosStates; posState++)
+			{
+				_lowCoder[posState].Init();
+				_midCoder[posState].Init();
+			}
+			_highCoder.Init();
+		}
+
+		public void Encode(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, int symbol, int posState) throws IOException
+		{
+			if (symbol < Base.kNumLowLenSymbols)
+			{
+				rangeEncoder.Encode(_choice, 0, 0);
+				_lowCoder[posState].Encode(rangeEncoder, symbol);
+			}
+			else
+			{
+				symbol -= Base.kNumLowLenSymbols;
+				rangeEncoder.Encode(_choice, 0, 1);
+				if (symbol < Base.kNumMidLenSymbols)
+				{
+					rangeEncoder.Encode(_choice, 1, 0);
+					_midCoder[posState].Encode(rangeEncoder, symbol);
+				}
+				else
+				{
+					rangeEncoder.Encode(_choice, 1, 1);
+					_highCoder.Encode(rangeEncoder, symbol - Base.kNumMidLenSymbols);
+				}
+			}
+		}
+
+		public void SetPrices(int posState, int numSymbols, int[] prices, int st)
+		{
+			int a0 = SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_choice[0]);
+			int a1 = SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_choice[0]);
+			int b0 = a1 + SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_choice[1]);
+			int b1 = a1 + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_choice[1]);
+			int i = 0;
+			for (i = 0; i < Base.kNumLowLenSymbols; i++)
+			{
+				if (i >= numSymbols)
+					return;
+				prices[st + i] = a0 + _lowCoder[posState].GetPrice(i);
+			}
+			for (; i < Base.kNumLowLenSymbols + Base.kNumMidLenSymbols; i++)
+			{
+				if (i >= numSymbols)
+					return;
+				prices[st + i] = b0 + _midCoder[posState].GetPrice(i - Base.kNumLowLenSymbols);
+			}
+			for (; i < numSymbols; i++)
+				prices[st + i] = b1 + _highCoder.GetPrice(i - Base.kNumLowLenSymbols - Base.kNumMidLenSymbols);
+		}
+	};
+
+	public static final int kNumLenSpecSymbols = Base.kNumLowLenSymbols + Base.kNumMidLenSymbols;
+
+	class LenPriceTableEncoder extends LenEncoder
+	{
+		int[] _prices = new int[Base.kNumLenSymbols<<Base.kNumPosStatesBitsEncodingMax];
+		int _tableSize;
+		int[] _counters = new int[Base.kNumPosStatesEncodingMax];
+
+		public void SetTableSize(int tableSize) { _tableSize = tableSize; }
+
+		public int GetPrice(int symbol, int posState)
+		{
+			return _prices[posState * Base.kNumLenSymbols + symbol];
+		}
+
+		void UpdateTable(int posState)
+		{
+			SetPrices(posState, _tableSize, _prices, posState * Base.kNumLenSymbols);
+			_counters[posState] = _tableSize;
+		}
+
+		public void UpdateTables(int numPosStates)
+		{
+			for (int posState = 0; posState < numPosStates; posState++)
+				UpdateTable(posState);
+		}
+
+		public void Encode(SevenZip.Compression.RangeCoder.Encoder rangeEncoder, int symbol, int posState) throws IOException
+		{
+			super.Encode(rangeEncoder, symbol, posState);
+			if (--_counters[posState] == 0)
+				UpdateTable(posState);
+		}
+	}
+
+	static final int kNumOpts = 1 << 12;
+	class Optimal
+	{
+		public int State;
+
+		public boolean Prev1IsChar;
+		public boolean Prev2;
+
+		public int PosPrev2;
+		public int BackPrev2;
+
+		public int Price;
+		public int PosPrev;
+		public int BackPrev;
+
+		public int Backs0;
+		public int Backs1;
+		public int Backs2;
+		public int Backs3;
+
+		public void MakeAsChar() { BackPrev = -1; Prev1IsChar = false; }
+		public void MakeAsShortRep() { BackPrev = 0; ; Prev1IsChar = false; }
+		public boolean IsShortRep() { return (BackPrev == 0); }
+	};
+	Optimal[] _optimum = new Optimal[kNumOpts];
+	SevenZip.Compression.LZ.BinTree _matchFinder = null;
+	SevenZip.Compression.RangeCoder.Encoder _rangeEncoder = new SevenZip.Compression.RangeCoder.Encoder();
+
+	short[] _isMatch = new short[Base.kNumStates<<Base.kNumPosStatesBitsMax];
+	short[] _isRep = new short[Base.kNumStates];
+	short[] _isRepG0 = new short[Base.kNumStates];
+	short[] _isRepG1 = new short[Base.kNumStates];
+	short[] _isRepG2 = new short[Base.kNumStates];
+	short[] _isRep0Long = new short[Base.kNumStates<<Base.kNumPosStatesBitsMax];
+
+	BitTreeEncoder[] _posSlotEncoder = new BitTreeEncoder[Base.kNumLenToPosStates]; // kNumPosSlotBits
+
+	short[] _posEncoders = new short[Base.kNumFullDistances-Base.kEndPosModelIndex];
+	BitTreeEncoder _posAlignEncoder = new BitTreeEncoder(Base.kNumAlignBits);
+
+	LenPriceTableEncoder _lenEncoder = new LenPriceTableEncoder();
+	LenPriceTableEncoder _repMatchLenEncoder = new LenPriceTableEncoder();
+
+	LiteralEncoder _literalEncoder = new LiteralEncoder();
+
+	int[] _matchDistances = new int[Base.kMatchMaxLen*2+2];
+
+	int _numFastBytes = kNumFastBytesDefault;
+	int _longestMatchLength;
+	int _numDistancePairs;
+
+	int _additionalOffset;
+
+	int _optimumEndIndex;
+	int _optimumCurrentIndex;
+
+	boolean _longestMatchWasFound;
+
+	int[] _posSlotPrices = new int[1<<(Base.kNumPosSlotBits+Base.kNumLenToPosStatesBits)];
+	int[] _distancesPrices = new int[Base.kNumFullDistances<<Base.kNumLenToPosStatesBits];
+	int[] _alignPrices = new int[Base.kAlignTableSize];
+	int _alignPriceCount;
+
+	int _distTableSize = (kDefaultDictionaryLogSize * 2);
+
+	int _posStateBits = 2;
+	int _posStateMask = (4 - 1);
+	int _numLiteralPosStateBits = 0;
+	int _numLiteralContextBits = 3;
+
+	int _dictionarySize = (1 << kDefaultDictionaryLogSize);
+	int _dictionarySizePrev = -1;
+	int _numFastBytesPrev = -1;
+
+	long nowPos64;
+	boolean _finished;
+	java.io.InputStream _inStream;
+
+	int _matchFinderType = EMatchFinderTypeBT4;
+	boolean _writeEndMark = false;
+
+	boolean _needReleaseMFStream = false;
+
+	void Create()
+	{
+		if (_matchFinder == null)
+		{
+			SevenZip.Compression.LZ.BinTree bt = new SevenZip.Compression.LZ.BinTree();
+			int numHashBytes = 4;
+			if (_matchFinderType == EMatchFinderTypeBT2)
+				numHashBytes = 2;
+			bt.SetType(numHashBytes);
+			_matchFinder = bt;
+		}
+		_literalEncoder.Create(_numLiteralPosStateBits, _numLiteralContextBits);
+
+		if (_dictionarySize == _dictionarySizePrev && _numFastBytesPrev == _numFastBytes)
+			return;
+		_matchFinder.Create(_dictionarySize, kNumOpts, _numFastBytes, Base.kMatchMaxLen + 1);
+		_dictionarySizePrev = _dictionarySize;
+		_numFastBytesPrev = _numFastBytes;
+	}
+
+	public Encoder()
+	{
+		for (int i = 0; i < kNumOpts; i++)
+			_optimum[i] = new Optimal();
+		for (int i = 0; i < Base.kNumLenToPosStates; i++)
+			_posSlotEncoder[i] = new BitTreeEncoder(Base.kNumPosSlotBits);
+	}
+
+	void SetWriteEndMarkerMode(boolean writeEndMarker)
+	{
+		_writeEndMark = writeEndMarker;
+	}
+
+	void Init()
+	{
+		BaseInit();
+		_rangeEncoder.Init();
+
+		SevenZip.Compression.RangeCoder.Encoder.InitBitModels(_isMatch);
+		SevenZip.Compression.RangeCoder.Encoder.InitBitModels(_isRep0Long);
+		SevenZip.Compression.RangeCoder.Encoder.InitBitModels(_isRep);
+		SevenZip.Compression.RangeCoder.Encoder.InitBitModels(_isRepG0);
+		SevenZip.Compression.RangeCoder.Encoder.InitBitModels(_isRepG1);
+		SevenZip.Compression.RangeCoder.Encoder.InitBitModels(_isRepG2);
+		SevenZip.Compression.RangeCoder.Encoder.InitBitModels(_posEncoders);
+
+
+
+
+
+
+
+		_literalEncoder.Init();
+		for (int i = 0; i < Base.kNumLenToPosStates; i++)
+			_posSlotEncoder[i].Init();
+
+
+
+		_lenEncoder.Init(1 << _posStateBits);
+		_repMatchLenEncoder.Init(1 << _posStateBits);
+
+		_posAlignEncoder.Init();
+
+		_longestMatchWasFound = false;
+		_optimumEndIndex = 0;
+		_optimumCurrentIndex = 0;
+		_additionalOffset = 0;
+	}
+
+	int ReadMatchDistances() throws java.io.IOException
+	{
+		int lenRes = 0;
+		_numDistancePairs = _matchFinder.GetMatches(_matchDistances);
+		if (_numDistancePairs > 0)
+		{
+			lenRes = _matchDistances[_numDistancePairs - 2];
+			if (lenRes == _numFastBytes)
+				lenRes += _matchFinder.GetMatchLen((int)lenRes - 1, _matchDistances[_numDistancePairs - 1],
+					Base.kMatchMaxLen - lenRes);
+		}
+		_additionalOffset++;
+		return lenRes;
+	}
+
+	void MovePos(int num) throws java.io.IOException
+	{
+		if (num > 0)
+		{
+			_matchFinder.Skip(num);
+			_additionalOffset += num;
+		}
+	}
+
+	int GetRepLen1Price(int state, int posState)
+	{
+		return SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isRepG0[state]) +
+				SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isRep0Long[(state << Base.kNumPosStatesBitsMax) + posState]);
+	}
+
+	int GetPureRepPrice(int repIndex, int state, int posState)
+	{
+		int price;
+		if (repIndex == 0)
+		{
+			price = SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isRepG0[state]);
+			price += SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isRep0Long[(state << Base.kNumPosStatesBitsMax) + posState]);
+		}
+		else
+		{
+			price = SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isRepG0[state]);
+			if (repIndex == 1)
+				price += SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isRepG1[state]);
+			else
+			{
+				price += SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isRepG1[state]);
+				price += SevenZip.Compression.RangeCoder.Encoder.GetPrice(_isRepG2[state], repIndex - 2);
+			}
+		}
+		return price;
+	}
+
+	int GetRepPrice(int repIndex, int len, int state, int posState)
+	{
+		int price = _repMatchLenEncoder.GetPrice(len - Base.kMatchMinLen, posState);
+		return price + GetPureRepPrice(repIndex, state, posState);
+	}
+
+	int GetPosLenPrice(int pos, int len, int posState)
+	{
+		int price;
+		int lenToPosState = Base.GetLenToPosState(len);
+		if (pos < Base.kNumFullDistances)
+			price = _distancesPrices[(lenToPosState * Base.kNumFullDistances) + pos];
+		else
+			price = _posSlotPrices[(lenToPosState << Base.kNumPosSlotBits) + GetPosSlot2(pos)] +
+				_alignPrices[pos & Base.kAlignMask];
+		return price + _lenEncoder.GetPrice(len - Base.kMatchMinLen, posState);
+	}
+
+	int Backward(int cur)
+	{
+		_optimumEndIndex = cur;
+		int posMem = _optimum[cur].PosPrev;
+		int backMem = _optimum[cur].BackPrev;
+		do
+		{
+			if (_optimum[cur].Prev1IsChar)
+			{
+				_optimum[posMem].MakeAsChar();
+				_optimum[posMem].PosPrev = posMem - 1;
+				if (_optimum[cur].Prev2)
+				{
+					_optimum[posMem - 1].Prev1IsChar = false;
+					_optimum[posMem - 1].PosPrev = _optimum[cur].PosPrev2;
+					_optimum[posMem - 1].BackPrev = _optimum[cur].BackPrev2;
+				}
+			}
+			int posPrev = posMem;
+			int backCur = backMem;
+
+			backMem = _optimum[posPrev].BackPrev;
+			posMem = _optimum[posPrev].PosPrev;
+
+			_optimum[posPrev].BackPrev = backCur;
+			_optimum[posPrev].PosPrev = cur;
+			cur = posPrev;
+		}
+		while (cur > 0);
+		backRes = _optimum[0].BackPrev;
+		_optimumCurrentIndex = _optimum[0].PosPrev;
+		return _optimumCurrentIndex;
+	}
+
+	int[] reps = new int[Base.kNumRepDistances];
+	int[] repLens = new int[Base.kNumRepDistances];
+	int backRes;
+
+	int GetOptimum(int position) throws IOException
+	{
+		if (_optimumEndIndex != _optimumCurrentIndex)
+		{
+			int lenRes = _optimum[_optimumCurrentIndex].PosPrev - _optimumCurrentIndex;
+			backRes = _optimum[_optimumCurrentIndex].BackPrev;
+			_optimumCurrentIndex = _optimum[_optimumCurrentIndex].PosPrev;
+			return lenRes;
+		}
+		_optimumCurrentIndex = _optimumEndIndex = 0;
+
+		int lenMain, numDistancePairs;
+		if (!_longestMatchWasFound)
+		{
+			lenMain = ReadMatchDistances();
+		}
+		else
+		{
+			lenMain = _longestMatchLength;
+			_longestMatchWasFound = false;
+		}
+		numDistancePairs = _numDistancePairs;
+
+		int numAvailableBytes = _matchFinder.GetNumAvailableBytes() + 1;
+		if (numAvailableBytes < 2)
+		{
+			backRes = -1;
+			return 1;
+		}
+		if (numAvailableBytes > Base.kMatchMaxLen)
+			numAvailableBytes = Base.kMatchMaxLen;
+
+		int repMaxIndex = 0;
+		int i;
+		for (i = 0; i < Base.kNumRepDistances; i++)
+		{
+			reps[i] = _repDistances[i];
+			repLens[i] = _matchFinder.GetMatchLen(0 - 1, reps[i], Base.kMatchMaxLen);
+			if (repLens[i] > repLens[repMaxIndex])
+				repMaxIndex = i;
+		}
+		if (repLens[repMaxIndex] >= _numFastBytes)
+		{
+			backRes = repMaxIndex;
+			int lenRes = repLens[repMaxIndex];
+			MovePos(lenRes - 1);
+			return lenRes;
+		}
+
+		if (lenMain >= _numFastBytes)
+		{
+			backRes = _matchDistances[numDistancePairs - 1] + Base.kNumRepDistances;
+			MovePos(lenMain - 1);
+			return lenMain;
+		}
+
+		byte currentByte = _matchFinder.GetIndexByte(0 - 1);
+		byte matchByte = _matchFinder.GetIndexByte(0 - _repDistances[0] - 1 - 1);
+
+		if (lenMain < 2 && currentByte != matchByte && repLens[repMaxIndex] < 2)
+		{
+			backRes = -1;
+			return 1;
+		}
+
+		_optimum[0].State = _state;
+
+		int posState = (position & _posStateMask);
+
+		_optimum[1].Price = SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isMatch[(_state << Base.kNumPosStatesBitsMax) + posState]) +
+				_literalEncoder.GetSubCoder(position, _previousByte).GetPrice(!Base.StateIsCharState(_state), matchByte, currentByte);
+		_optimum[1].MakeAsChar();
+
+		int matchPrice = SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isMatch[(_state << Base.kNumPosStatesBitsMax) + posState]);
+		int repMatchPrice = matchPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isRep[_state]);
+
+		if (matchByte == currentByte)
+		{
+			int shortRepPrice = repMatchPrice + GetRepLen1Price(_state, posState);
+			if (shortRepPrice < _optimum[1].Price)
+			{
+				_optimum[1].Price = shortRepPrice;
+				_optimum[1].MakeAsShortRep();
+			}
+		}
+
+		int lenEnd = ((lenMain >= repLens[repMaxIndex]) ? lenMain : repLens[repMaxIndex]);
+
+		if (lenEnd < 2)
+		{
+			backRes = _optimum[1].BackPrev;
+			return 1;
+		}
+
+		_optimum[1].PosPrev = 0;
+
+		_optimum[0].Backs0 = reps[0];
+		_optimum[0].Backs1 = reps[1];
+		_optimum[0].Backs2 = reps[2];
+		_optimum[0].Backs3 = reps[3];
+
+		int len = lenEnd;
+		do
+			_optimum[len--].Price = kIfinityPrice;
+		while (len >= 2);
+
+		for (i = 0; i < Base.kNumRepDistances; i++)
+		{
+			int repLen = repLens[i];
+			if (repLen < 2)
+				continue;
+			int price = repMatchPrice + GetPureRepPrice(i, _state, posState);
+			do
+			{
+				int curAndLenPrice = price + _repMatchLenEncoder.GetPrice(repLen - 2, posState);
+				Optimal optimum = _optimum[repLen];
+				if (curAndLenPrice < optimum.Price)
+				{
+					optimum.Price = curAndLenPrice;
+					optimum.PosPrev = 0;
+					optimum.BackPrev = i;
+					optimum.Prev1IsChar = false;
+				}
+			}
+			while (--repLen >= 2);
+		}
+
+		int normalMatchPrice = matchPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isRep[_state]);
+
+		len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2);
+		if (len <= lenMain)
+		{
+			int offs = 0;
+			while (len > _matchDistances[offs])
+				offs += 2;
+			for (; ; len++)
+			{
+				int distance = _matchDistances[offs + 1];
+				int curAndLenPrice = normalMatchPrice + GetPosLenPrice(distance, len, posState);
+				Optimal optimum = _optimum[len];
+				if (curAndLenPrice < optimum.Price)
+				{
+					optimum.Price = curAndLenPrice;
+					optimum.PosPrev = 0;
+					optimum.BackPrev = distance + Base.kNumRepDistances;
+					optimum.Prev1IsChar = false;
+				}
+				if (len == _matchDistances[offs])
+				{
+					offs += 2;
+					if (offs == numDistancePairs)
+						break;
+				}
+			}
+		}
+
+		int cur = 0;
+
+		while (true)
+		{
+			cur++;
+			if (cur == lenEnd)
+				return Backward(cur);
+			int newLen = ReadMatchDistances();
+			numDistancePairs = _numDistancePairs;
+			if (newLen >= _numFastBytes)
+			{
+
+				_longestMatchLength = newLen;
+				_longestMatchWasFound = true;
+				return Backward(cur);
+			}
+			position++;
+			int posPrev = _optimum[cur].PosPrev;
+			int state;
+			if (_optimum[cur].Prev1IsChar)
+			{
+				posPrev--;
+				if (_optimum[cur].Prev2)
+				{
+					state = _optimum[_optimum[cur].PosPrev2].State;
+					if (_optimum[cur].BackPrev2 < Base.kNumRepDistances)
+						state = Base.StateUpdateRep(state);
+					else
+						state = Base.StateUpdateMatch(state);
+				}
+				else
+					state = _optimum[posPrev].State;
+				state = Base.StateUpdateChar(state);
+			}
+			else
+				state = _optimum[posPrev].State;
+			if (posPrev == cur - 1)
+			{
+				if (_optimum[cur].IsShortRep())
+					state = Base.StateUpdateShortRep(state);
+				else
+					state = Base.StateUpdateChar(state);
+			}
+			else
+			{
+				int pos;
+				if (_optimum[cur].Prev1IsChar && _optimum[cur].Prev2)
+				{
+					posPrev = _optimum[cur].PosPrev2;
+					pos = _optimum[cur].BackPrev2;
+					state = Base.StateUpdateRep(state);
+				}
+				else
+				{
+					pos = _optimum[cur].BackPrev;
+					if (pos < Base.kNumRepDistances)
+						state = Base.StateUpdateRep(state);
+					else
+						state = Base.StateUpdateMatch(state);
+				}
+				Optimal opt = _optimum[posPrev];
+				if (pos < Base.kNumRepDistances)
+				{
+					if (pos == 0)
+					{
+						reps[0] = opt.Backs0;
+						reps[1] = opt.Backs1;
+						reps[2] = opt.Backs2;
+						reps[3] = opt.Backs3;
+					}
+					else if (pos == 1)
+					{
+						reps[0] = opt.Backs1;
+						reps[1] = opt.Backs0;
+						reps[2] = opt.Backs2;
+						reps[3] = opt.Backs3;
+					}
+					else if (pos == 2)
+					{
+						reps[0] = opt.Backs2;
+						reps[1] = opt.Backs0;
+						reps[2] = opt.Backs1;
+						reps[3] = opt.Backs3;
+					}
+					else
+					{
+						reps[0] = opt.Backs3;
+						reps[1] = opt.Backs0;
+						reps[2] = opt.Backs1;
+						reps[3] = opt.Backs2;
+					}
+				}
+				else
+				{
+					reps[0] = (pos - Base.kNumRepDistances);
+					reps[1] = opt.Backs0;
+					reps[2] = opt.Backs1;
+					reps[3] = opt.Backs2;
+				}
+			}
+			_optimum[cur].State = state;
+			_optimum[cur].Backs0 = reps[0];
+			_optimum[cur].Backs1 = reps[1];
+			_optimum[cur].Backs2 = reps[2];
+			_optimum[cur].Backs3 = reps[3];
+			int curPrice = _optimum[cur].Price;
+
+			currentByte = _matchFinder.GetIndexByte(0 - 1);
+			matchByte = _matchFinder.GetIndexByte(0 - reps[0] - 1 - 1);
+
+			posState = (position & _posStateMask);
+
+			int curAnd1Price = curPrice +
+				SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isMatch[(state << Base.kNumPosStatesBitsMax) + posState]) +
+				_literalEncoder.GetSubCoder(position, _matchFinder.GetIndexByte(0 - 2)).
+				GetPrice(!Base.StateIsCharState(state), matchByte, currentByte);
+
+			Optimal nextOptimum = _optimum[cur + 1];
+
+			boolean nextIsChar = false;
+			if (curAnd1Price < nextOptimum.Price)
+			{
+				nextOptimum.Price = curAnd1Price;
+				nextOptimum.PosPrev = cur;
+				nextOptimum.MakeAsChar();
+				nextIsChar = true;
+			}
+
+			matchPrice = curPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isMatch[(state << Base.kNumPosStatesBitsMax) + posState]);
+			repMatchPrice = matchPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isRep[state]);
+
+			if (matchByte == currentByte &&
+				!(nextOptimum.PosPrev < cur && nextOptimum.BackPrev == 0))
+			{
+				int shortRepPrice = repMatchPrice + GetRepLen1Price(state, posState);
+				if (shortRepPrice <= nextOptimum.Price)
+				{
+					nextOptimum.Price = shortRepPrice;
+					nextOptimum.PosPrev = cur;
+					nextOptimum.MakeAsShortRep();
+					nextIsChar = true;
+				}
+			}
+
+			int numAvailableBytesFull = _matchFinder.GetNumAvailableBytes() + 1;
+			numAvailableBytesFull = Math.min(kNumOpts - 1 - cur, numAvailableBytesFull);
+			numAvailableBytes = numAvailableBytesFull;
+
+			if (numAvailableBytes < 2)
+				continue;
+			if (numAvailableBytes > _numFastBytes)
+				numAvailableBytes = _numFastBytes;
+			if (!nextIsChar && matchByte != currentByte)
+			{
+				// try Literal + rep0
+				int t = Math.min(numAvailableBytesFull - 1, _numFastBytes);
+				int lenTest2 = _matchFinder.GetMatchLen(0, reps[0], t);
+				if (lenTest2 >= 2)
+				{
+					int state2 = Base.StateUpdateChar(state);
+
+					int posStateNext = (position + 1) & _posStateMask;
+					int nextRepMatchPrice = curAnd1Price +
+						SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isMatch[(state2 << Base.kNumPosStatesBitsMax) + posStateNext]) +
+						SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isRep[state2]);
+					{
+						int offset = cur + 1 + lenTest2;
+						while (lenEnd < offset)
+							_optimum[++lenEnd].Price = kIfinityPrice;
+						int curAndLenPrice = nextRepMatchPrice + GetRepPrice(
+								0, lenTest2, state2, posStateNext);
+						Optimal optimum = _optimum[offset];
+						if (curAndLenPrice < optimum.Price)
+						{
+							optimum.Price = curAndLenPrice;
+							optimum.PosPrev = cur + 1;
+							optimum.BackPrev = 0;
+							optimum.Prev1IsChar = true;
+							optimum.Prev2 = false;
+						}
+					}
+				}
+			}
+
+			int startLen = 2; // speed optimization 
+
+			for (int repIndex = 0; repIndex < Base.kNumRepDistances; repIndex++)
+			{
+				int lenTest = _matchFinder.GetMatchLen(0 - 1, reps[repIndex], numAvailableBytes);
+				if (lenTest < 2)
+					continue;
+				int lenTestTemp = lenTest;
+				do
+				{
+					while (lenEnd < cur + lenTest)
+						_optimum[++lenEnd].Price = kIfinityPrice;
+					int curAndLenPrice = repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState);
+					Optimal optimum = _optimum[cur + lenTest];
+					if (curAndLenPrice < optimum.Price)
+					{
+						optimum.Price = curAndLenPrice;
+						optimum.PosPrev = cur;
+						optimum.BackPrev = repIndex;
+						optimum.Prev1IsChar = false;
+					}
+				}
+				while (--lenTest >= 2);
+				lenTest = lenTestTemp;
+
+				if (repIndex == 0)
+					startLen = lenTest + 1;
+
+				// if (_maxMode)
+				if (lenTest < numAvailableBytesFull)
+				{
+					int t = Math.min(numAvailableBytesFull - 1 - lenTest, _numFastBytes);
+					int lenTest2 = _matchFinder.GetMatchLen(lenTest, reps[repIndex], t);
+					if (lenTest2 >= 2)
+					{
+						int state2 = Base.StateUpdateRep(state);
+
+						int posStateNext = (position + lenTest) & _posStateMask;
+						int curAndLenCharPrice =
+								repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState) +
+								SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isMatch[(state2 << Base.kNumPosStatesBitsMax) + posStateNext]) +
+								_literalEncoder.GetSubCoder(position + lenTest,
+								_matchFinder.GetIndexByte(lenTest - 1 - 1)).GetPrice(true,
+								_matchFinder.GetIndexByte(lenTest - 1 - (reps[repIndex] + 1)),
+								_matchFinder.GetIndexByte(lenTest - 1));
+						state2 = Base.StateUpdateChar(state2);
+						posStateNext = (position + lenTest + 1) & _posStateMask;
+						int nextMatchPrice = curAndLenCharPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isMatch[(state2 << Base.kNumPosStatesBitsMax) + posStateNext]);
+						int nextRepMatchPrice = nextMatchPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isRep[state2]);
+
+						// for(; lenTest2 >= 2; lenTest2--)
+						{
+							int offset = lenTest + 1 + lenTest2;
+							while (lenEnd < cur + offset)
+								_optimum[++lenEnd].Price = kIfinityPrice;
+							int curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext);
+							Optimal optimum = _optimum[cur + offset];
+							if (curAndLenPrice < optimum.Price)
+							{
+								optimum.Price = curAndLenPrice;
+								optimum.PosPrev = cur + lenTest + 1;
+								optimum.BackPrev = 0;
+								optimum.Prev1IsChar = true;
+								optimum.Prev2 = true;
+								optimum.PosPrev2 = cur;
+								optimum.BackPrev2 = repIndex;
+							}
+						}
+					}
+				}
+			}
+
+			if (newLen > numAvailableBytes)
+			{
+				newLen = numAvailableBytes;
+				for (numDistancePairs = 0; newLen > _matchDistances[numDistancePairs]; numDistancePairs += 2) ;
+				_matchDistances[numDistancePairs] = newLen;
+				numDistancePairs += 2;
+			}
+			if (newLen >= startLen)
+			{
+				normalMatchPrice = matchPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isRep[state]);
+				while (lenEnd < cur + newLen)
+					_optimum[++lenEnd].Price = kIfinityPrice;
+
+				int offs = 0;
+				while (startLen > _matchDistances[offs])
+					offs += 2;
+
+				for (int lenTest = startLen; ; lenTest++)
+				{
+					int curBack = _matchDistances[offs + 1];
+					int curAndLenPrice = normalMatchPrice + GetPosLenPrice(curBack, lenTest, posState);
+					Optimal optimum = _optimum[cur + lenTest];
+					if (curAndLenPrice < optimum.Price)
+					{
+						optimum.Price = curAndLenPrice;
+						optimum.PosPrev = cur;
+						optimum.BackPrev = curBack + Base.kNumRepDistances;
+						optimum.Prev1IsChar = false;
+					}
+
+					if (lenTest == _matchDistances[offs])
+					{
+						if (lenTest < numAvailableBytesFull)
+						{
+							int t = Math.min(numAvailableBytesFull - 1 - lenTest, _numFastBytes);
+							int lenTest2 = _matchFinder.GetMatchLen(lenTest, curBack, t);
+							if (lenTest2 >= 2)
+							{
+								int state2 = Base.StateUpdateMatch(state);
+
+								int posStateNext = (position + lenTest) & _posStateMask;
+								int curAndLenCharPrice = curAndLenPrice +
+									SevenZip.Compression.RangeCoder.Encoder.GetPrice0(_isMatch[(state2 << Base.kNumPosStatesBitsMax) + posStateNext]) +
+									_literalEncoder.GetSubCoder(position + lenTest,
+									_matchFinder.GetIndexByte(lenTest - 1 - 1)).
+									GetPrice(true,
+									_matchFinder.GetIndexByte(lenTest - (curBack + 1) - 1),
+									_matchFinder.GetIndexByte(lenTest - 1));
+								state2 = Base.StateUpdateChar(state2);
+								posStateNext = (position + lenTest + 1) & _posStateMask;
+								int nextMatchPrice = curAndLenCharPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isMatch[(state2 << Base.kNumPosStatesBitsMax) + posStateNext]);
+								int nextRepMatchPrice = nextMatchPrice + SevenZip.Compression.RangeCoder.Encoder.GetPrice1(_isRep[state2]);
+
+								int offset = lenTest + 1 + lenTest2;
+								while (lenEnd < cur + offset)
+									_optimum[++lenEnd].Price = kIfinityPrice;
+								curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext);
+								optimum = _optimum[cur + offset];
+								if (curAndLenPrice < optimum.Price)
+								{
+									optimum.Price = curAndLenPrice;
+									optimum.PosPrev = cur + lenTest + 1;
+									optimum.BackPrev = 0;
+									optimum.Prev1IsChar = true;
+									optimum.Prev2 = true;
+									optimum.PosPrev2 = cur;
+									optimum.BackPrev2 = curBack + Base.kNumRepDistances;
+								}
+							}
+						}
+						offs += 2;
+						if (offs == numDistancePairs)
+							break;
+					}
+				}
+			}
+		}
+	}
+
+	boolean ChangePair(int smallDist, int bigDist)
+	{
+		int kDif = 7;
+		return (smallDist < (1 << (32 - kDif)) && bigDist >= (smallDist << kDif));
+	}
+
+	void WriteEndMarker(int posState) throws IOException
+	{
+		if (!_writeEndMark)
+			return;
+
+		_rangeEncoder.Encode(_isMatch, (_state << Base.kNumPosStatesBitsMax) + posState, 1);
+		_rangeEncoder.Encode(_isRep, _state, 0);
+		_state = Base.StateUpdateMatch(_state);
+		int len = Base.kMatchMinLen;
+		_lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState);
+		int posSlot = (1 << Base.kNumPosSlotBits) - 1;
+		int lenToPosState = Base.GetLenToPosState(len);
+		_posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot);
+		int footerBits = 30;
+		int posReduced = (1 << footerBits) - 1;
+		_rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits);
+		_posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask);
+	}
+
+	void Flush(int nowPos) throws IOException
+	{
+		ReleaseMFStream();
+		WriteEndMarker(nowPos & _posStateMask);
+		_rangeEncoder.FlushData();
+		_rangeEncoder.FlushStream();
+	}
+
+	public void CodeOneBlock(long[] inSize, long[] outSize, boolean[] finished) throws IOException
+	{
+		inSize[0] = 0;
+		outSize[0] = 0;
+		finished[0] = true;
+
+		if (_inStream != null)
+		{
+			_matchFinder.SetStream(_inStream);
+			_matchFinder.Init();
+			_needReleaseMFStream = true;
+			_inStream = null;
+		}
+
+		if (_finished)
+			return;
+		_finished = true;
+
+
+		long progressPosValuePrev = nowPos64;
+		if (nowPos64 == 0)
+		{
+			if (_matchFinder.GetNumAvailableBytes() == 0)
+			{
+				Flush((int)nowPos64);
+				return;
+			}
+
+			ReadMatchDistances();
+			int posState = (int)(nowPos64) & _posStateMask;
+			_rangeEncoder.Encode(_isMatch, (_state << Base.kNumPosStatesBitsMax) + posState, 0);
+			_state = Base.StateUpdateChar(_state);
+			byte curByte = _matchFinder.GetIndexByte(0 - _additionalOffset);
+			_literalEncoder.GetSubCoder((int)(nowPos64), _previousByte).Encode(_rangeEncoder, curByte);
+			_previousByte = curByte;
+			_additionalOffset--;
+			nowPos64++;
+		}
+		if (_matchFinder.GetNumAvailableBytes() == 0)
+		{
+			Flush((int)nowPos64);
+			return;
+		}
+		while (true)
+		{
+
+			int len = GetOptimum((int)nowPos64);
+			int pos = backRes;
+			int posState = ((int)nowPos64) & _posStateMask;
+			int complexState = (_state << Base.kNumPosStatesBitsMax) + posState;
+			if (len == 1 && pos == -1)
+			{
+				_rangeEncoder.Encode(_isMatch, complexState, 0);
+				byte curByte = _matchFinder.GetIndexByte((int)(0 - _additionalOffset));
+				LiteralEncoder.Encoder2 subCoder = _literalEncoder.GetSubCoder((int)nowPos64, _previousByte);
+				if (!Base.StateIsCharState(_state))
+				{
+					byte matchByte = _matchFinder.GetIndexByte((int)(0 - _repDistances[0] - 1 - _additionalOffset));
+					subCoder.EncodeMatched(_rangeEncoder, matchByte, curByte);
+				}
+				else
+					subCoder.Encode(_rangeEncoder, curByte);
+				_previousByte = curByte;
+				_state = Base.StateUpdateChar(_state);
+			}
+			else
+			{
+				_rangeEncoder.Encode(_isMatch, complexState, 1);
+				if (pos < Base.kNumRepDistances)
+				{
+					_rangeEncoder.Encode(_isRep, _state, 1);
+					if (pos == 0)
+					{
+						_rangeEncoder.Encode(_isRepG0, _state, 0);
+						if (len == 1)
+							_rangeEncoder.Encode(_isRep0Long, complexState, 0);
+						else
+							_rangeEncoder.Encode(_isRep0Long, complexState, 1);
+					}
+					else
+					{
+						_rangeEncoder.Encode(_isRepG0, _state, 1);
+						if (pos == 1)
+							_rangeEncoder.Encode(_isRepG1, _state, 0);
+						else
+						{
+							_rangeEncoder.Encode(_isRepG1, _state, 1);
+							_rangeEncoder.Encode(_isRepG2, _state, pos - 2);
+						}
+					}
+					if (len == 1)
+						_state = Base.StateUpdateShortRep(_state);
+					else
+					{
+						_repMatchLenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState);
+						_state = Base.StateUpdateRep(_state);
+					}
+					int distance = _repDistances[pos];
+					if (pos != 0)
+					{
+						for (int i = pos; i >= 1; i--)
+							_repDistances[i] = _repDistances[i - 1];
+						_repDistances[0] = distance;
+					}
+				}
+				else
+				{
+					_rangeEncoder.Encode(_isRep, _state, 0);
+					_state = Base.StateUpdateMatch(_state);
+					_lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState);
+					pos -= Base.kNumRepDistances;
+					int posSlot = GetPosSlot(pos);
+					int lenToPosState = Base.GetLenToPosState(len);
+					_posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot);
+
+					if (posSlot >= Base.kStartPosModelIndex)
+					{
+						int footerBits = (int)((posSlot >> 1) - 1);
+						int baseVal = ((2 | (posSlot & 1)) << footerBits);
+						int posReduced = pos - baseVal;
+
+						if (posSlot < Base.kEndPosModelIndex)
+							BitTreeEncoder.ReverseEncode(_posEncoders,
+									baseVal - posSlot - 1, _rangeEncoder, footerBits, posReduced);
+						else
+						{
+							_rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits);
+							_posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask);
+							_alignPriceCount++;
+						}
+					}
+					int distance = pos;
+					for (int i = Base.kNumRepDistances - 1; i >= 1; i--)
+						_repDistances[i] = _repDistances[i - 1];
+					_repDistances[0] = distance;
+					_matchPriceCount++;
+				}
+				_previousByte = _matchFinder.GetIndexByte(len - 1 - _additionalOffset);
+			}
+			_additionalOffset -= len;
+			nowPos64 += len;
+			if (_additionalOffset == 0)
+			{
+				// if (!_fastMode)
+				if (_matchPriceCount >= (1 << 7))
+					FillDistancesPrices();
+				if (_alignPriceCount >= Base.kAlignTableSize)
+					FillAlignPrices();
+				inSize[0] = nowPos64;
+				outSize[0] = _rangeEncoder.GetProcessedSizeAdd();
+				if (_matchFinder.GetNumAvailableBytes() == 0)
+				{
+					Flush((int)nowPos64);
+					return;
+				}
+
+				if (nowPos64 - progressPosValuePrev >= (1 << 12))
+				{
+					_finished = false;
+					finished[0] = false;
+					return;
+				}
+			}
+		}
+	}
+
+	void ReleaseMFStream()
+	{
+		if (_matchFinder != null && _needReleaseMFStream)
+		{
+			_matchFinder.ReleaseStream();
+			_needReleaseMFStream = false;
+		}
+	}
+
+	void SetOutStream(java.io.OutputStream outStream)
+	{ _rangeEncoder.SetStream(outStream); }
+	void ReleaseOutStream()
+	{ _rangeEncoder.ReleaseStream(); }
+
+	void ReleaseStreams()
+	{
+		ReleaseMFStream();
+		ReleaseOutStream();
+	}
+
+	void SetStreams(java.io.InputStream inStream, java.io.OutputStream outStream,
+			long inSize, long outSize)
+	{
+		_inStream = inStream;
+		_finished = false;
+		Create();
+		SetOutStream(outStream);
+		Init();
+
+		// if (!_fastMode)
+		{
+			FillDistancesPrices();
+			FillAlignPrices();
+		}
+
+		_lenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen);
+		_lenEncoder.UpdateTables(1 << _posStateBits);
+		_repMatchLenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen);
+		_repMatchLenEncoder.UpdateTables(1 << _posStateBits);
+
+		nowPos64 = 0;
+	}
+
+	long[] processedInSize = new long[1]; long[] processedOutSize = new long[1]; boolean[] finished = new boolean[1];
+	public void Code(java.io.InputStream inStream, java.io.OutputStream outStream,
+			long inSize, long outSize, ICodeProgress progress) throws IOException
+	{
+		_needReleaseMFStream = false;
+		try
+		{
+			SetStreams(inStream, outStream, inSize, outSize);
+			while (true)
+			{
+
+
+
+				CodeOneBlock(processedInSize, processedOutSize, finished);
+				if (finished[0])
+					return;
+				if (progress != null)
+				{
+					progress.SetProgress(processedInSize[0], processedOutSize[0]);
+				}
+			}
+		}
+		finally
+		{
+			ReleaseStreams();
+		}
+	}
+
+	public static final int kPropSize = 5;
+	byte[] properties = new byte[kPropSize];
+
+	public void WriteCoderProperties(java.io.OutputStream outStream) throws IOException
+	{
+		properties[0] = (byte)((_posStateBits * 5 + _numLiteralPosStateBits) * 9 + _numLiteralContextBits);
+		for (int i = 0; i < 4; i++)
+			properties[1 + i] = (byte)(_dictionarySize >> (8 * i));
+		outStream.write(properties, 0, kPropSize);
+	}
+
+	int[] tempPrices = new int[Base.kNumFullDistances];
+	int _matchPriceCount;
+
+	void FillDistancesPrices()
+	{
+		for (int i = Base.kStartPosModelIndex; i < Base.kNumFullDistances; i++)
+		{
+			int posSlot = GetPosSlot(i);
+			int footerBits = (int)((posSlot >> 1) - 1);
+			int baseVal = ((2 | (posSlot & 1)) << footerBits);
+			tempPrices[i] = BitTreeEncoder.ReverseGetPrice(_posEncoders,
+				baseVal - posSlot - 1, footerBits, i - baseVal);
+		}
+
+		for (int lenToPosState = 0; lenToPosState < Base.kNumLenToPosStates; lenToPosState++)
+		{
+			int posSlot;
+			BitTreeEncoder encoder = _posSlotEncoder[lenToPosState];
+
+			int st = (lenToPosState << Base.kNumPosSlotBits);
+			for (posSlot = 0; posSlot < _distTableSize; posSlot++)
+				_posSlotPrices[st + posSlot] = encoder.GetPrice(posSlot);
+			for (posSlot = Base.kEndPosModelIndex; posSlot < _distTableSize; posSlot++)
+				_posSlotPrices[st + posSlot] += ((((posSlot >> 1) - 1) - Base.kNumAlignBits) << SevenZip.Compression.RangeCoder.Encoder.kNumBitPriceShiftBits);
+
+			int st2 = lenToPosState * Base.kNumFullDistances;
+			int i;
+			for (i = 0; i < Base.kStartPosModelIndex; i++)
+				_distancesPrices[st2 + i] = _posSlotPrices[st + i];
+			for (; i < Base.kNumFullDistances; i++)
+				_distancesPrices[st2 + i] = _posSlotPrices[st + GetPosSlot(i)] + tempPrices[i];
+		}
+		_matchPriceCount = 0;
+	}
+
+	void FillAlignPrices()
+	{
+		for (int i = 0; i < Base.kAlignTableSize; i++)
+			_alignPrices[i] = _posAlignEncoder.ReverseGetPrice(i);
+		_alignPriceCount = 0;
+	}
+
+
+	public boolean SetAlgorithm(int algorithm)
+	{
+		/*
+		_fastMode = (algorithm == 0);
+		_maxMode = (algorithm >= 2);
+		*/
+		return true;
+	}
+
+	public boolean SetDictionarySize(int dictionarySize)
+	{
+		int kDicLogSizeMaxCompress = 29;
+		if (dictionarySize < (1 << Base.kDicLogSizeMin) || dictionarySize > (1 << kDicLogSizeMaxCompress))
+			return false;
+		_dictionarySize = dictionarySize;
+		int dicLogSize;
+		for (dicLogSize = 0; dictionarySize > (1 << dicLogSize); dicLogSize++) ;
+		_distTableSize = dicLogSize * 2;
+		return true;
+	}
+
+	public boolean SetNumFastBytes(int numFastBytes)
+	{
+		if (numFastBytes < 5 || numFastBytes > Base.kMatchMaxLen)
+			return false;
+		_numFastBytes = numFastBytes;
+		return true;
+	}
+
+	public boolean SetMatchFinder(int matchFinderIndex)
+	{
+		if (matchFinderIndex < 0 || matchFinderIndex > 2)
+			return false;
+		int matchFinderIndexPrev = _matchFinderType;
+		_matchFinderType = matchFinderIndex;
+		if (_matchFinder != null && matchFinderIndexPrev != _matchFinderType)
+		{
+			_dictionarySizePrev = -1;
+			_matchFinder = null;
+		}
+		return true;
+	}
+
+	public boolean SetLcLpPb(int lc, int lp, int pb)
+	{
+		if (
+				lp < 0 || lp > Base.kNumLitPosStatesBitsEncodingMax ||
+				lc < 0 || lc > Base.kNumLitContextBitsMax ||
+				pb < 0 || pb > Base.kNumPosStatesBitsEncodingMax)
+			return false;
+		_numLiteralPosStateBits = lp;
+		_numLiteralContextBits = lc;
+		_posStateBits = pb;
+		_posStateMask = ((1) << _posStateBits) - 1;
+		return true;
+	}
+
+	public void SetEndMarkerMode(boolean endMarkerMode)
+	{
+		_writeEndMark = endMarkerMode;
+	}
+}
+
diff --git a/third_party/lzma/v4_65/files/Java/SevenZip/Compression/RangeCoder/BitTreeDecoder.java b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/RangeCoder/BitTreeDecoder.java
new file mode 100644
index 0000000..7698581
--- /dev/null
+++ b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/RangeCoder/BitTreeDecoder.java
@@ -0,0 +1,55 @@
+package SevenZip.Compression.RangeCoder;
+
+public class BitTreeDecoder
+{
+	short[] Models;
+	int NumBitLevels;
+	
+	public BitTreeDecoder(int numBitLevels)
+	{
+		NumBitLevels = numBitLevels;
+		Models = new short[1 << numBitLevels];
+	}
+	
+	public void Init()
+	{
+		Decoder.InitBitModels(Models);
+	}
+	
+	public int Decode(Decoder rangeDecoder) throws java.io.IOException
+	{
+		int m = 1;
+		for (int bitIndex = NumBitLevels; bitIndex != 0; bitIndex--)
+			m = (m << 1) + rangeDecoder.DecodeBit(Models, m);
+		return m - (1 << NumBitLevels);
+	}
+	
+	public int ReverseDecode(Decoder rangeDecoder) throws java.io.IOException
+	{
+		int m = 1;
+		int symbol = 0;
+		for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++)
+		{
+			int bit = rangeDecoder.DecodeBit(Models, m);
+			m <<= 1;
+			m += bit;
+			symbol |= (bit << bitIndex);
+		}
+		return symbol;
+	}
+	
+	public static int ReverseDecode(short[] Models, int startIndex,
+			Decoder rangeDecoder, int NumBitLevels) throws java.io.IOException
+	{
+		int m = 1;
+		int symbol = 0;
+		for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++)
+		{
+			int bit = rangeDecoder.DecodeBit(Models, startIndex + m);
+			m <<= 1;
+			m += bit;
+			symbol |= (bit << bitIndex);
+		}
+		return symbol;
+	}
+}
diff --git a/third_party/lzma/v4_65/files/Java/SevenZip/Compression/RangeCoder/BitTreeEncoder.java b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/RangeCoder/BitTreeEncoder.java
new file mode 100644
index 0000000..f9e97db
--- /dev/null
+++ b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/RangeCoder/BitTreeEncoder.java
@@ -0,0 +1,99 @@
+package SevenZip.Compression.RangeCoder;
+import java.io.IOException;
+
+public class BitTreeEncoder
+{
+	short[] Models;
+	int NumBitLevels;
+	
+	public BitTreeEncoder(int numBitLevels)
+	{
+		NumBitLevels = numBitLevels;
+		Models = new short[1 << numBitLevels];
+	}
+	
+	public void Init()
+	{
+		Decoder.InitBitModels(Models);
+	}
+	
+	public void Encode(Encoder rangeEncoder, int symbol) throws IOException
+	{
+		int m = 1;
+		for (int bitIndex = NumBitLevels; bitIndex != 0; )
+		{
+			bitIndex--;
+			int bit = (symbol >>> bitIndex) & 1;
+			rangeEncoder.Encode(Models, m, bit);
+			m = (m << 1) | bit;
+		}
+	}
+	
+	public void ReverseEncode(Encoder rangeEncoder, int symbol) throws IOException
+	{
+		int m = 1;
+		for (int  i = 0; i < NumBitLevels; i++)
+		{
+			int bit = symbol & 1;
+			rangeEncoder.Encode(Models, m, bit);
+			m = (m << 1) | bit;
+			symbol >>= 1;
+		}
+	}
+	
+	public int GetPrice(int symbol)
+	{
+		int price = 0;
+		int m = 1;
+		for (int bitIndex = NumBitLevels; bitIndex != 0; )
+		{
+			bitIndex--;
+			int bit = (symbol >>> bitIndex) & 1;
+			price += Encoder.GetPrice(Models[m], bit);
+			m = (m << 1) + bit;
+		}
+		return price;
+	}
+	
+	public int ReverseGetPrice(int symbol)
+	{
+		int price = 0;
+		int m = 1;
+		for (int i = NumBitLevels; i != 0; i--)
+		{
+			int bit = symbol & 1;
+			symbol >>>= 1;
+			price += Encoder.GetPrice(Models[m], bit);
+			m = (m << 1) | bit;
+		}
+		return price;
+	}
+	
+	public static int ReverseGetPrice(short[] Models, int startIndex,
+			int NumBitLevels, int symbol)
+	{
+		int price = 0;
+		int m = 1;
+		for (int i = NumBitLevels; i != 0; i--)
+		{
+			int bit = symbol & 1;
+			symbol >>>= 1;
+			price += Encoder.GetPrice(Models[startIndex + m], bit);
+			m = (m << 1) | bit;
+		}
+		return price;
+	}
+	
+	public static void ReverseEncode(short[] Models, int startIndex,
+			Encoder rangeEncoder, int NumBitLevels, int symbol) throws IOException
+	{
+		int m = 1;
+		for (int i = 0; i < NumBitLevels; i++)
+		{
+			int bit = symbol & 1;
+			rangeEncoder.Encode(Models, startIndex + m, bit);
+			m = (m << 1) | bit;
+			symbol >>= 1;
+		}
+	}
+}
diff --git a/third_party/lzma/v4_65/files/Java/SevenZip/Compression/RangeCoder/Decoder.java b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/RangeCoder/Decoder.java
new file mode 100644
index 0000000..85b3150
--- /dev/null
+++ b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/RangeCoder/Decoder.java
@@ -0,0 +1,88 @@
+package SevenZip.Compression.RangeCoder;
+import java.io.IOException;
+
+public class Decoder
+{
+	static final int kTopMask = ~((1 << 24) - 1);
+	
+	static final int kNumBitModelTotalBits = 11;
+	static final int kBitModelTotal = (1 << kNumBitModelTotalBits);
+	static final int kNumMoveBits = 5;
+	
+	int Range;
+	int Code;
+
+	java.io.InputStream Stream;
+	
+	public final void SetStream(java.io.InputStream stream)
+	{ 
+		Stream = stream; 
+	}
+	
+	public final void ReleaseStream()
+	{ 
+		Stream = null; 
+	}
+	
+	public final void Init() throws IOException
+	{
+		Code = 0;
+		Range = -1;
+		for (int i = 0; i < 5; i++)
+			Code = (Code << 8) | Stream.read();
+	}
+	
+	public final int DecodeDirectBits(int numTotalBits) throws IOException
+	{
+		int result = 0;
+		for (int i = numTotalBits; i != 0; i--)
+		{
+			Range >>>= 1;
+			int t = ((Code - Range) >>> 31);
+			Code -= Range & (t - 1);
+			result = (result << 1) | (1 - t);
+			
+			if ((Range & kTopMask) == 0)
+			{
+				Code = (Code << 8) | Stream.read();
+				Range <<= 8;
+			}
+		}
+		return result;
+	}
+	
+	public int DecodeBit(short []probs, int index) throws IOException
+	{
+		int prob = probs[index];
+		int newBound = (Range >>> kNumBitModelTotalBits) * prob;
+		if ((Code ^ 0x80000000) < (newBound ^ 0x80000000))
+		{
+			Range = newBound;
+			probs[index] = (short)(prob + ((kBitModelTotal - prob) >>> kNumMoveBits));
+			if ((Range & kTopMask) == 0)
+			{
+				Code = (Code << 8) | Stream.read();
+				Range <<= 8;
+			}
+			return 0;
+		}
+		else
+		{
+			Range -= newBound;
+			Code -= newBound;
+			probs[index] = (short)(prob - ((prob) >>> kNumMoveBits));
+			if ((Range & kTopMask) == 0)
+			{
+				Code = (Code << 8) | Stream.read();
+				Range <<= 8;
+			}
+			return 1;
+		}
+	}
+	
+	public static void InitBitModels(short []probs)
+	{
+		for (int i = 0; i < probs.length; i++)
+			probs[i] = (kBitModelTotal >>> 1);
+	}
+}
diff --git a/third_party/lzma/v4_65/files/Java/SevenZip/Compression/RangeCoder/Encoder.java b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/RangeCoder/Encoder.java
new file mode 100644
index 0000000..d21b983
--- /dev/null
+++ b/third_party/lzma/v4_65/files/Java/SevenZip/Compression/RangeCoder/Encoder.java
@@ -0,0 +1,151 @@
+package SevenZip.Compression.RangeCoder;
+import java.io.IOException;
+
+public class Encoder
+{
+	static final int kTopMask = ~((1 << 24) - 1);
+	
+	static final int kNumBitModelTotalBits = 11;
+	static final int kBitModelTotal = (1 << kNumBitModelTotalBits);
+	static final int kNumMoveBits = 5;
+	
+	java.io.OutputStream Stream;
+
+	long Low;
+	int Range;
+	int _cacheSize;
+	int _cache;
+	
+	long _position;
+	
+	public void SetStream(java.io.OutputStream stream)
+	{
+		Stream = stream;
+	}
+	
+	public void ReleaseStream()
+	{
+		Stream = null;
+	}
+	
+	public void Init()
+	{
+		_position = 0;
+		Low = 0;
+		Range = -1;
+		_cacheSize = 1;
+		_cache = 0;
+	}
+	
+	public void FlushData() throws IOException
+	{
+		for (int i = 0; i < 5; i++)
+			ShiftLow();
+	}
+	
+	public void FlushStream() throws IOException
+	{
+		Stream.flush();
+	}
+	
+	public void ShiftLow() throws IOException
+	{
+		int LowHi = (int)(Low >>> 32);
+		if (LowHi != 0 || Low < 0xFF000000L)
+		{
+			_position += _cacheSize;
+			int temp = _cache;
+			do
+			{
+				Stream.write(temp + LowHi);
+				temp = 0xFF;
+			}
+			while(--_cacheSize != 0);
+			_cache = (((int)Low) >>> 24);
+		}
+		_cacheSize++;
+		Low = (Low & 0xFFFFFF) << 8;
+	}
+	
+	public void EncodeDirectBits(int v, int numTotalBits) throws IOException
+	{
+		for (int i = numTotalBits - 1; i >= 0; i--)
+		{
+			Range >>>= 1;
+			if (((v >>> i) & 1) == 1)
+				Low += Range;
+			if ((Range & Encoder.kTopMask) == 0)
+			{
+				Range <<= 8;
+				ShiftLow();
+			}
+		}
+	}
+	
+	
+	public long GetProcessedSizeAdd()
+	{
+		return _cacheSize + _position + 4;
+	}
+	
+	
+	
+	static final int kNumMoveReducingBits = 2;
+	public static final int kNumBitPriceShiftBits = 6;
+	
+	public static void InitBitModels(short []probs)
+	{
+		for (int i = 0; i < probs.length; i++)
+			probs[i] = (kBitModelTotal >>> 1);
+	}
+	
+	public void Encode(short []probs, int index, int symbol) throws IOException
+	{
+		int prob = probs[index];
+		int newBound = (Range >>> kNumBitModelTotalBits) * prob;
+		if (symbol == 0)
+		{
+			Range = newBound;
+			probs[index] = (short)(prob + ((kBitModelTotal - prob) >>> kNumMoveBits));
+		}
+		else
+		{
+			Low += (newBound & 0xFFFFFFFFL);
+			Range -= newBound;
+			probs[index] = (short)(prob - ((prob) >>> kNumMoveBits));
+		}
+		if ((Range & kTopMask) == 0)
+		{
+			Range <<= 8;
+			ShiftLow();
+		}
+	}
+	
+	private static int[] ProbPrices = new int[kBitModelTotal >>> kNumMoveReducingBits];
+	
+	static
+	{
+		int kNumBits = (kNumBitModelTotalBits - kNumMoveReducingBits);
+		for (int i = kNumBits - 1; i >= 0; i--)
+		{
+			int start = 1 << (kNumBits - i - 1);
+			int end = 1 << (kNumBits - i);
+			for (int j = start; j < end; j++)
+				ProbPrices[j] = (i << kNumBitPriceShiftBits) +
+						(((end - j) << kNumBitPriceShiftBits) >>> (kNumBits - i - 1));
+		}
+	}
+	
+	static public int GetPrice(int Prob, int symbol)
+	{
+		return ProbPrices[(((Prob - symbol) ^ ((-symbol))) & (kBitModelTotal - 1)) >>> kNumMoveReducingBits];
+	}
+	static public int GetPrice0(int Prob)
+	{ 
+		return ProbPrices[Prob >>> kNumMoveReducingBits]; 
+	}
+	static public int GetPrice1(int Prob)
+	{ 
+		return ProbPrices[(kBitModelTotal - Prob) >>> kNumMoveReducingBits]; 
+	}
+}
diff --git a/third_party/lzma/v4_65/files/Java/SevenZip/ICodeProgress.java b/third_party/lzma/v4_65/files/Java/SevenZip/ICodeProgress.java
new file mode 100644
index 0000000..4cb8d1b
--- /dev/null
+++ b/third_party/lzma/v4_65/files/Java/SevenZip/ICodeProgress.java
@@ -0,0 +1,6 @@
+package SevenZip;
+
+public interface ICodeProgress
+{
+	public void SetProgress(long inSize, long outSize);
+}
diff --git a/third_party/lzma/v4_65/files/Java/SevenZip/LzmaAlone.java b/third_party/lzma/v4_65/files/Java/SevenZip/LzmaAlone.java
new file mode 100644
index 0000000..d1d27f9
--- /dev/null
+++ b/third_party/lzma/v4_65/files/Java/SevenZip/LzmaAlone.java
@@ -0,0 +1,253 @@
+package SevenZip;
+
+public class LzmaAlone
+{
+	static public class CommandLine
+	{
+		public static final int kEncode = 0;
+		public static final int kDecode = 1;
+		public static final int kBenchmak = 2;
+		
+		public int Command = -1;
+		public int NumBenchmarkPasses = 10;
+		
+		public int DictionarySize = 1 << 23;
+		public boolean DictionarySizeIsDefined = false;
+		
+		public int Lc = 3;
+		public int Lp = 0;
+		public int Pb = 2;
+		
+		public int Fb = 128;
+		public boolean FbIsDefined = false;
+		
+		public boolean Eos = false;
+		
+		public int Algorithm = 2;
+		public int MatchFinder = 1;
+		
+		public String InFile;
+		public String OutFile;
+		
+		boolean ParseSwitch(String s)
+		{
+			if (s.startsWith("d"))
+			{
+				DictionarySize = 1 << Integer.parseInt(s.substring(1));
+				DictionarySizeIsDefined = true;
+			}
+			else if (s.startsWith("fb"))
+			{
+				Fb = Integer.parseInt(s.substring(2));
+				FbIsDefined = true;
+			}
+			else if (s.startsWith("a"))
+				Algorithm = Integer.parseInt(s.substring(1));
+			else if (s.startsWith("lc"))
+				Lc = Integer.parseInt(s.substring(2));
+			else if (s.startsWith("lp"))
+				Lp = Integer.parseInt(s.substring(2));
+			else if (s.startsWith("pb"))
+				Pb = Integer.parseInt(s.substring(2));
+			else if (s.startsWith("eos"))
+				Eos = true;
+			else if (s.startsWith("mf"))
+			{
+				String mfs = s.substring(2);
+				if (mfs.equals("bt2"))
+					MatchFinder = 0;
+				else if (mfs.equals("bt4"))
+					MatchFinder = 1;
+				else if (mfs.equals("bt4b"))
+					MatchFinder = 2;
+				else
+					return false;
+			}
+			else
+				return false;
+			return true;
+		}
+		
+		public boolean Parse(String[] args) throws Exception
+		{
+			int pos = 0;
+			boolean switchMode = true;
+			for (int i = 0; i < args.length; i++)
+			{
+				String s = args[i];
+				if (s.length() == 0)
+					return false;
+				if (switchMode)
+				{
+					if (s.compareTo("--") == 0)
+					{
+						switchMode = false;
+						continue;
+					}
+					if (s.charAt(0) == '-')
+					{
+						String sw = s.substring(1).toLowerCase();
+						if (sw.length() == 0)
+							return false;
+						try
+						{
+							if (!ParseSwitch(sw))
+								return false;
+						}
+						catch (NumberFormatException e)
+						{
+							return false;
+						}
+						continue;
+					}
+				}
+				if (pos == 0)
+				{
+					if (s.equalsIgnoreCase("e"))
+						Command = kEncode;
+					else if (s.equalsIgnoreCase("d"))
+						Command = kDecode;
+					else if (s.equalsIgnoreCase("b"))
+						Command = kBenchmak;
+					else
+						return false;
+				}
+				else if(pos == 1)
+				{
+					if (Command == kBenchmak)
+					{
+						try
+						{
+							NumBenchmarkPasses = Integer.parseInt(s);
+							if (NumBenchmarkPasses < 1)
+								return false;
+						}
+						catch (NumberFormatException e)
+						{
+							return false;
+						}
+					}
+					else
+						InFile = s;
+				}
+				else if(pos == 2)
+					OutFile = s;
+				else
+					return false;
+				pos++;
+				continue;
+			}
+			return true;
+		}
+	}
+	
+	
+	static void PrintHelp()
+	{
+		System.out.println(
+				"\nUsage:  LZMA <e|d> [<switches>...] inputFile outputFile\n" +
+				"  e: encode file\n" +
+				"  d: decode file\n" +
+				"  b: Benchmark\n" +
+				"<Switches>\n" +
+				// "  -a{N}:  set compression mode - [0, 1], default: 1 (max)\n" +
+				"  -d{N}:  set dictionary - [0,28], default: 23 (8MB)\n" +
+				"  -fb{N}: set number of fast bytes - [5, 273], default: 128\n" +
+				"  -lc{N}: set number of literal context bits - [0, 8], default: 3\n" +
+				"  -lp{N}: set number of literal pos bits - [0, 4], default: 0\n" +
+				"  -pb{N}: set number of pos bits - [0, 4], default: 2\n" +
+				"  -mf{MF_ID}: set Match Finder: [bt2, bt4], default: bt4\n" +
+				"  -eos:   write End Of Stream marker\n"
+				);
+	}
+	
+	public static void main(String[] args) throws Exception
+	{
+		System.out.println("\nLZMA (Java) 4.61  2008-11-23\n");
+		
+		if (args.length < 1)
+		{
+			PrintHelp();
+			return;
+		}
+		
+		CommandLine params = new CommandLine();
+		if (!params.Parse(args))
+		{
+			System.out.println("\nIncorrect command");
+			return;
+		}
+		
+		if (params.Command == CommandLine.kBenchmak)
+		{
+			int dictionary = (1 << 21);
+			if (params.DictionarySizeIsDefined)
+				dictionary = params.DictionarySize;
+			if (params.MatchFinder > 1)
+				throw new Exception("Unsupported match finder");
+			SevenZip.LzmaBench.LzmaBenchmark(params.NumBenchmarkPasses, dictionary);
+		}
+		else if (params.Command == CommandLine.kEncode || params.Command == CommandLine.kDecode)
+		{
+			java.io.File inFile = new java.io.File(params.InFile);
+			java.io.File outFile = new java.io.File(params.OutFile);
+			
+			java.io.BufferedInputStream inStream  = new java.io.BufferedInputStream(new java.io.FileInputStream(inFile));
+			java.io.BufferedOutputStream outStream = new java.io.BufferedOutputStream(new java.io.FileOutputStream(outFile));
+			
+			boolean eos = false;
+			if (params.Eos)
+				eos = true;
+			if (params.Command == CommandLine.kEncode)
+			{
+				SevenZip.Compression.LZMA.Encoder encoder = new SevenZip.Compression.LZMA.Encoder();
+				if (!encoder.SetAlgorithm(params.Algorithm))
+					throw new Exception("Incorrect compression mode");
+				if (!encoder.SetDictionarySize(params.DictionarySize))
+					throw new Exception("Incorrect dictionary size");
+				if (!encoder.SetNumFastBytes(params.Fb))
+					throw new Exception("Incorrect -fb value");
+				if (!encoder.SetMatchFinder(params.MatchFinder))
+					throw new Exception("Incorrect -mf value");
+				if (!encoder.SetLcLpPb(params.Lc, params.Lp, params.Pb))
+					throw new Exception("Incorrect -lc or -lp or -pb value");
+				encoder.SetEndMarkerMode(eos);
+				encoder.WriteCoderProperties(outStream);
+				long fileSize;
+				if (eos)
+					fileSize = -1;
+				else
+					fileSize = inFile.length();
+				for (int i = 0; i < 8; i++)
+					outStream.write((int)(fileSize >>> (8 * i)) & 0xFF);
+				encoder.Code(inStream, outStream, -1, -1, null);
+			}
+			else
+			{
+				int propertiesSize = 5;
+				byte[] properties = new byte[propertiesSize];
+				if (inStream.read(properties, 0, propertiesSize) != propertiesSize)
+					throw new Exception("input .lzma file is too short");
+				SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder();
+				if (!decoder.SetDecoderProperties(properties))
+					throw new Exception("Incorrect stream properties");
+				long outSize = 0;
+				for (int i = 0; i < 8; i++)
+				{
+					int v = inStream.read();
+					if (v < 0)
+						throw new Exception("Can't read stream size");
+					outSize |= ((long)v) << (8 * i);
+				}
+				if (!decoder.Code(inStream, outStream, outSize))
+					throw new Exception("Error in data stream");
+			}
+			outStream.flush();
+			outStream.close();
+			inStream.close();
+		}
+		else
+			throw new Exception("Incorrect command");
+		return;
+	}
+}
diff --git a/third_party/lzma/v4_65/files/Java/SevenZip/LzmaBench.java b/third_party/lzma/v4_65/files/Java/SevenZip/LzmaBench.java
new file mode 100644
index 0000000..397e7af
--- /dev/null
+++ b/third_party/lzma/v4_65/files/Java/SevenZip/LzmaBench.java
@@ -0,0 +1,392 @@
+package SevenZip;
+
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+public class LzmaBench
+{
+	static final int kAdditionalSize = (1 << 21);
+	static final int kCompressedAdditionalSize = (1 << 10);
+	
+	static class CRandomGenerator
+	{
+		int A1;
+		int A2;
+		public CRandomGenerator() { Init(); }
+		public void Init() { A1 = 362436069; A2 = 521288629; }
+		public int GetRnd()
+		{
+			return
+				((A1 = 36969 * (A1 & 0xffff) + (A1 >>> 16)) << 16) ^
+				((A2 = 18000 * (A2 & 0xffff) + (A2 >>> 16)));
+		}
+	};
+	
+	static class CBitRandomGenerator
+	{
+		CRandomGenerator RG = new CRandomGenerator();
+		int Value;
+		int NumBits;
+		public void Init()
+		{
+			Value = 0;
+			NumBits = 0;
+		}
+		public int GetRnd(int numBits)
+		{
+			int result;
+			if (NumBits > numBits)
+			{
+				result = Value & ((1 << numBits) - 1);
+				Value >>>= numBits;
+				NumBits -= numBits;
+				return result;
+			}
+			numBits -= NumBits;
+			result = (Value << numBits);
+			Value = RG.GetRnd();
+			result |= Value & (((int)1 << numBits) - 1);
+			Value >>>= numBits;
+			NumBits = 32 - numBits;
+			return result;
+		}
+	};
+	
+	static class CBenchRandomGenerator
+	{
+		CBitRandomGenerator RG = new CBitRandomGenerator();
+		int Pos;
+		int Rep0;
+
+		public int BufferSize;
+		public byte[] Buffer = null;
+
+		public CBenchRandomGenerator() { }
+		public void Set(int bufferSize)
+		{
+			Buffer = new byte[bufferSize];
+			Pos = 0;
+			BufferSize = bufferSize;
+		}
+		int GetRndBit() { return RG.GetRnd(1); }
+		int GetLogRandBits(int numBits)
+		{
+			int len = RG.GetRnd(numBits);
+			return RG.GetRnd((int)len);
+		}
+		int GetOffset()
+		{
+			if (GetRndBit() == 0)
+				return GetLogRandBits(4);
+			return (GetLogRandBits(4) << 10) | RG.GetRnd(10);
+		}
+		int GetLen1() { return RG.GetRnd(1 + (int)RG.GetRnd(2)); }
+		int GetLen2() { return RG.GetRnd(2 + (int)RG.GetRnd(2)); }
+		public void Generate()
+		{
+			RG.Init();
+			Rep0 = 1;
+			while (Pos < BufferSize)
+			{
+				if (GetRndBit() == 0 || Pos < 1)
+					Buffer[Pos++] = (byte)(RG.GetRnd(8));
+				else
+				{
+					int len;
+					if (RG.GetRnd(3) == 0)
+						len = 1 + GetLen1();
+					else
+					{
+						do
+							Rep0 = GetOffset();
+						while (Rep0 >= Pos);
+						Rep0++;
+						len = 2 + GetLen2();
+					}
+					for (int i = 0; i < len && Pos < BufferSize; i++, Pos++)
+						Buffer[Pos] = Buffer[Pos - Rep0];
+				}
+			}
+		}
+	};
+	
+	static class CrcOutStream extends java.io.OutputStream
+	{
+		public CRC CRC = new CRC();
+		
+		public void Init()
+		{ 
+			CRC.Init(); 
+		}
+		public int GetDigest()
+		{ 
+			return CRC.GetDigest(); 
+		}
+		public void write(byte[] b)
+		{
+			CRC.Update(b);
+		}
+		public void write(byte[] b, int off, int len)
+		{
+			CRC.Update(b, off, len);
+		}
+		public void write(int b)
+		{
+			CRC.UpdateByte(b);
+		}
+	};
+
+	static class MyOutputStream extends java.io.OutputStream
+	{
+		byte[] _buffer;
+		int _size;
+		int _pos;
+		
+		public MyOutputStream(byte[] buffer)
+		{
+			_buffer = buffer;
+			_size = _buffer.length;
+		}
+		
+		public void reset()
+		{ 
+			_pos = 0; 
+		}
+		
+		public void write(int b) throws IOException
+		{
+			if (_pos >= _size)
+				throw new IOException("Error");
+			_buffer[_pos++] = (byte)b;
+		}
+		
+		public int size()
+		{
+			return _pos;
+		}
+	};
+
+	static class MyInputStream extends java.io.InputStream
+	{
+		byte[] _buffer;
+		int _size;
+		int _pos;
+		
+		public MyInputStream(byte[] buffer, int size)
+		{
+			_buffer = buffer;
+			_size = size;
+		}
+		
+		public void reset()
+		{ 
+			_pos = 0; 
+		}
+		
+		public int read()
+		{
+			if (_pos >= _size)
+				return -1;
+			return _buffer[_pos++] & 0xFF;
+		}
+	};
+	
+	static class CProgressInfo implements ICodeProgress
+	{
+		public long ApprovedStart;
+		public long InSize;
+		public long Time;
+		public void Init()
+		{ InSize = 0; }
+		public void SetProgress(long inSize, long outSize)
+		{
+			if (inSize >= ApprovedStart && InSize == 0)
+			{
+				Time = System.currentTimeMillis();
+				InSize = inSize;
+			}
+		}
+	}
+	static final int kSubBits = 8;
+	
+	static int GetLogSize(int size)
+	{
+		for (int i = kSubBits; i < 32; i++)
+			for (int j = 0; j < (1 << kSubBits); j++)
+				if (size <= ((1) << i) + (j << (i - kSubBits)))
+					return (i << kSubBits) + j;
+		return (32 << kSubBits);
+	}
+	
+	static long MyMultDiv64(long value, long elapsedTime)
+	{
+		long freq = 1000; // ms
+		long elTime = elapsedTime;
+		while (freq > 1000000)
+		{
+			freq >>>= 1;
+			elTime >>>= 1;
+		}
+		if (elTime == 0)
+			elTime = 1;
+		return value * freq / elTime;
+	}
+	
+	static long GetCompressRating(int dictionarySize, long elapsedTime, long size)
+	{
+		long t = GetLogSize(dictionarySize) - (18 << kSubBits);
+		long numCommandsForOne = 1060 + ((t * t * 10) >> (2 * kSubBits));
+		long numCommands = (long)(size) * numCommandsForOne;
+		return MyMultDiv64(numCommands, elapsedTime);
+	}
+	
+	static long GetDecompressRating(long elapsedTime, long outSize, long inSize)
+	{
+		long numCommands = inSize * 220 + outSize * 20;
+		return MyMultDiv64(numCommands, elapsedTime);
+	}
+	
+	static long GetTotalRating(
+			int dictionarySize,
+			long elapsedTimeEn, long sizeEn,
+			long elapsedTimeDe,
+			long inSizeDe, long outSizeDe)
+	{
+		return (GetCompressRating(dictionarySize, elapsedTimeEn, sizeEn) +
+				GetDecompressRating(elapsedTimeDe, inSizeDe, outSizeDe)) / 2;
+	}
+	
+	static void PrintValue(long v)
+	{
+		String s = "";
+		s += v;
+		for (int i = 0; i + s.length() < 6; i++)
+			System.out.print(" ");
+		System.out.print(s);
+	}
+	
+	static void PrintRating(long rating)
+	{
+		PrintValue(rating / 1000000);
+		System.out.print(" MIPS");
+	}
+	
+	static void PrintResults(
+			int dictionarySize,
+			long elapsedTime,
+			long size,
+			boolean decompressMode, long secondSize)
+	{
+		long speed = MyMultDiv64(size, elapsedTime);
+		PrintValue(speed / 1024);
+		System.out.print(" KB/s  ");
+		long rating;
+		if (decompressMode)
+			rating = GetDecompressRating(elapsedTime, size, secondSize);
+		else
+			rating = GetCompressRating(dictionarySize, elapsedTime, size);
+		PrintRating(rating);
+	}
+	
+	static public int LzmaBenchmark(int numIterations, int dictionarySize) throws Exception
+	{
+		if (numIterations <= 0)
+			return 0;
+		if (dictionarySize < (1 << 18))
+		{
+			System.out.println("\nError: dictionary size for benchmark must be >= 18 (256 KB)");
+			return 1;
+		}
+		System.out.print("\n       Compressing                Decompressing\n\n");
+		
+		SevenZip.Compression.LZMA.Encoder encoder = new SevenZip.Compression.LZMA.Encoder();
+		SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder();
+		
+		if (!encoder.SetDictionarySize(dictionarySize))
+			throw new Exception("Incorrect dictionary size");
+		
+		int kBufferSize = dictionarySize + kAdditionalSize;
+		int kCompressedBufferSize = (kBufferSize / 2) + kCompressedAdditionalSize;
+		
+		ByteArrayOutputStream propStream = new ByteArrayOutputStream();
+		encoder.WriteCoderProperties(propStream);
+		byte[] propArray = propStream.toByteArray();
+		decoder.SetDecoderProperties(propArray);
+		
+		CBenchRandomGenerator rg = new CBenchRandomGenerator();
+
+		rg.Set(kBufferSize);
+		rg.Generate();
+		CRC crc = new CRC();
+		crc.Init();
+		crc.Update(rg.Buffer, 0, rg.BufferSize);
+		
+		CProgressInfo progressInfo = new CProgressInfo();
+		progressInfo.ApprovedStart = dictionarySize;
+		
+		long totalBenchSize = 0;
+		long totalEncodeTime = 0;
+		long totalDecodeTime = 0;
+		long totalCompressedSize = 0;
+		
+		MyInputStream inStream = new MyInputStream(rg.Buffer, rg.BufferSize);
+
+		byte[] compressedBuffer = new byte[kCompressedBufferSize];
+		MyOutputStream compressedStream = new MyOutputStream(compressedBuffer);
+		CrcOutStream crcOutStream = new CrcOutStream();
+		MyInputStream inputCompressedStream = null;
+		int compressedSize = 0;
+		for (int i = 0; i < numIterations; i++)
+		{
+			progressInfo.Init();
+			inStream.reset();
+			compressedStream.reset();
+			encoder.Code(inStream, compressedStream, -1, -1, progressInfo);
+			long encodeTime = System.currentTimeMillis() - progressInfo.Time;
+			
+			if (i == 0)
+			{
+				compressedSize = compressedStream.size();
+				inputCompressedStream = new MyInputStream(compressedBuffer, compressedSize);
+			}
+			else if (compressedSize != compressedStream.size())
+				throw (new Exception("Encoding error"));
+				
+			if (progressInfo.InSize == 0)
+				throw (new Exception("Internal ERROR 1282"));
+
+			long decodeTime = 0;
+			for (int j = 0; j < 2; j++)
+			{
+				inputCompressedStream.reset();
+				crcOutStream.Init();
+				
+				long outSize = kBufferSize;
+				long startTime = System.currentTimeMillis();
+				if (!decoder.Code(inputCompressedStream, crcOutStream, outSize))
+					throw (new Exception("Decoding Error"));;
+				decodeTime = System.currentTimeMillis() - startTime;
+				if (crcOutStream.GetDigest() != crc.GetDigest())
+					throw (new Exception("CRC Error"));
+			}
+			long benchSize = kBufferSize - (long)progressInfo.InSize;
+			PrintResults(dictionarySize, encodeTime, benchSize, false, 0);
+			System.out.print("     ");
+			PrintResults(dictionarySize, decodeTime, kBufferSize, true, compressedSize);
+			System.out.println();
+			
+			totalBenchSize += benchSize;
+			totalEncodeTime += encodeTime;
+			totalDecodeTime += decodeTime;
+			totalCompressedSize += compressedSize;
+		}
+		System.out.println("---------------------------------------------------");
+		PrintResults(dictionarySize, totalEncodeTime, totalBenchSize, false, 0);
+		System.out.print("     ");
+		PrintResults(dictionarySize, totalDecodeTime,
+				kBufferSize * (long)numIterations, true, totalCompressedSize);
+		System.out.println("    Average");
+		return 0;
+	}
+}
diff --git a/third_party/lzma/v4_65/files/Methods.txt b/third_party/lzma/v4_65/files/Methods.txt
new file mode 100644
index 0000000..5b5cb93
--- /dev/null
+++ b/third_party/lzma/v4_65/files/Methods.txt
@@ -0,0 +1,137 @@
+7-Zip method IDs (4.65)
+-----------------------
+
+Each compression or crypto method in 7z has unique binary value (ID).
+The length of ID in bytes is arbitrary but it can not exceed 63 bits (8 bytes).
+
+If you want to add some new ID, you have two ways:
+1) Write request for allocating IDs to 7-zip developers.
+2) Generate 8-bytes ID:
+
+    3F ZZ ZZ ZZ ZZ ZZ MM MM 
+
+    3F              - Prefix for random IDs (1 byte)
+    ZZ ZZ ZZ ZZ ZZ  - Developer ID (5 bytes). Use real random bytes. 
+                      
+    MM MM           - Method ID (2 bytes)
+
+    You can notify 7-Zip developers about your Developer ID / Method ID.
+
+    Note: Use new ID only if old codec can not decode data encoded with new version.
+
+
+List of defined IDs
+-------------------
+      
+00 - Copy
+
+02 - Common
+   03 Swap
+      - 2 Swap2
+      - 4 Swap4
+
+03 - 7z
+   01 - LZMA
+      01 - Version
+  
+   03 - Branch
+      01 - x86
+         03  - BCJ
+         1B  - BCJ2
+      02 - PPC
+         05 - PPC (Big Endian)
+      03 - Alpha
+         01 - Alpha
+      04 - IA64
+         01 - IA64
+      05 - ARM
+         01 - ARM
+      06 - M68
+         05 - M68 (Big Endian)
+      07 - ARM Thumb
+         01 - ARMT
+      08 - SPARC
+         05 - SPARC
+
+   04 - PPMD
+      01 - Version
+
+   7F -
+      01 - experimental methods.
+
+
+04 - Misc
+   00 - Reserved
+   01 - Zip
+      00 - Copy (not used). Use {00} instead
+      01 - Shrink
+      06 - Implode
+      08 - Deflate
+      09 - Deflate64
+      12 - BZip2 (not used). Use {04 02 02} instead
+   02 - BZip
+      02 - BZip2
+   03 - Rar
+      01 - Rar15
+      02 - Rar20
+      03 - Rar29
+   04 - Arj
+      01 - Arj (1,2,3)
+      02 - Arj 4
+   05 - Z
+   06 - Lzh
+   07 - Reserved for 7z
+   08 - Cab
+   09 - NSIS
+      01 - DeflateNSIS
+      02 - BZip2NSIS
+
+
+06 - Crypto 
+   00 - 
+   01 - AES
+      0x - AES-128
+      4x - AES-192
+      8x - AES-256
+      Cx - AES
+
+      x0 - ECB
+      x1 - CBC
+      x2 - CFB
+      x3 - OFB
+
+   07 - Reserved
+   0F - Reserved
+
+   F0 - Misc Ciphers (Real Ciphers without hashing algo)
+
+   F1 - Misc Ciphers (Combine)
+      01 - Zip
+         01 - Main Zip crypto algo
+      03 - RAR
+         02 - 
+         03 - Rar29 AES-128 + (modified SHA-1)
+      07 - 7z
+         01 - AES-256 + SHA-256
+
+07 - Hash (subject to change)
+   00 - 
+   01 - CRC
+   02 - SHA-1
+   03 - SHA-256
+   04 - SHA-384
+   05 - SHA-512
+
+   F0 - Misc Hash
+
+   F1 - Misc
+      03 - RAR
+         03 - Rar29 Password Hashing (modified SHA1)
+      07 - 7z 
+         01 - SHA-256 Password Hashing
+    
+   
+
+
+---
+End of document
diff --git a/third_party/lzma/v4_65/files/history.txt b/third_party/lzma/v4_65/files/history.txt
new file mode 100644
index 0000000..0141867
--- /dev/null
+++ b/third_party/lzma/v4_65/files/history.txt
@@ -0,0 +1,236 @@
+HISTORY of the LZMA SDK
+-----------------------
+
+4.65           2009-02-03
+-------------------------
+- Some minor fixes
+
+
+4.63           2008-12-31
+-------------------------
+- Some minor fixes
+
+
+4.61 beta      2008-11-23
+-------------------------
+- The bug in ANSI-C LZMA Decoder was fixed:
+    If encoded stream was corrupted, decoder could access memory 
+    outside of allocated range.
+- Some changes in ANSI-C 7z Decoder interfaces.
+- LZMA SDK is placed in the public domain.
+
+
+4.60 beta      2008-08-19
+-------------------------
+- Some minor fixes.
+
+
+4.59 beta      2008-08-13
+-------------------------
+- The bug was fixed:
+    LZMA Encoder in fast compression mode could access memory outside of 
+    allocated range in some rare cases.
+
+
+4.58 beta      2008-05-05
+-------------------------
+- ANSI-C LZMA Decoder was rewritten for speed optimizations.
+- ANSI-C LZMA Encoder was included to LZMA SDK.
+- C++ LZMA code now is just wrapper over ANSI-C code.
+
+
+4.57           2007-12-12
+-------------------------
+- Speed optimizations in Ñ++ LZMA Decoder. 
+- Small changes for more compatibility with some C/C++ compilers.
+
+
+4.49 beta      2007-07-05
+-------------------------
+- .7z ANSI-C Decoder:
+     - now it supports BCJ and BCJ2 filters
+     - now it supports files larger than 4 GB.
+     - now it supports "Last Write Time" field for files.
+- C++ code for .7z archives compressing/decompressing from 7-zip 
+  was included to LZMA SDK.
+  
+
+4.43           2006-06-04
+-------------------------
+- Small changes for more compatibility with some C/C++ compilers.
+  
+
+4.42           2006-05-15
+-------------------------
+- Small changes in .h files in ANSI-C version.
+  
+
+4.39 beta      2006-04-14
+-------------------------
+- The bug in versions 4.33b:4.38b was fixed:
+  C++ version of LZMA encoder could not correctly compress 
+  files larger than 2 GB with HC4 match finder (-mfhc4).
+  
+
+4.37 beta      2005-04-06
+-------------------------
+- Fixes in C++ code: code could no be compiled if _NO_EXCEPTIONS was defined. 
+
+
+4.35 beta      2005-03-02
+-------------------------
+- The bug was fixed in C++ version of LZMA Decoder:
+    If encoded stream was corrupted, decoder could access memory 
+    outside of allocated range.
+
+
+4.34 beta      2006-02-27
+-------------------------
+- Compressing speed and memory requirements for compressing were increased
+- LZMA now can use only these match finders: HC4, BT2, BT3, BT4
+
+
+4.32           2005-12-09
+-------------------------
+- Java version of LZMA SDK was included
+
+
+4.30           2005-11-20
+-------------------------
+- Compression ratio was improved in -a2 mode
+- Speed optimizations for compressing in -a2 mode
+- -fb switch now supports values up to 273
+- The bug in 7z_C (7zIn.c) was fixed:
+  It used Alloc/Free functions from different memory pools.
+  So if program used two memory pools, it worked incorrectly.
+- 7z_C: .7z format supporting was improved
+- LZMA# SDK (C#.NET version) was included
+
+
+4.27 (Updated) 2005-09-21
+-------------------------
+- Some GUIDs/interfaces in C++ were changed.
+ IStream.h:
+   ISequentialInStream::Read now works as old ReadPart
+   ISequentialOutStream::Write now works as old WritePart
+
+
+4.27           2005-08-07
+-------------------------
+- The bug in LzmaDecodeSize.c was fixed:
+   if _LZMA_IN_CB and _LZMA_OUT_READ were defined,
+   decompressing worked incorrectly.
+
+
+4.26           2005-08-05
+-------------------------
+- Fixes in 7z_C code and LzmaTest.c:
+  previous versions could work incorrectly,
+  if malloc(0) returns 0
+
+
+4.23           2005-06-29
+-------------------------
+- Small fixes in C++ code
+
+
+4.22           2005-06-10
+-------------------------
+- Small fixes
+
+
+4.21           2005-06-08
+-------------------------
+- Interfaces for ANSI-C LZMA Decoder (LzmaDecode.c) were changed
+- New additional version of ANSI-C LZMA Decoder with zlib-like interface:
+    - LzmaStateDecode.h
+    - LzmaStateDecode.c
+    - LzmaStateTest.c
+- ANSI-C LZMA Decoder now can decompress files larger than 4 GB
+
+
+4.17           2005-04-18
+-------------------------
+- New example for RAM->RAM compressing/decompressing: 
+  LZMA + BCJ (filter for x86 code):
+    - LzmaRam.h
+    - LzmaRam.cpp
+    - LzmaRamDecode.h
+    - LzmaRamDecode.c
+    - -f86 switch for lzma.exe
+
+
+4.16           2005-03-29
+-------------------------
+- The bug was fixed in LzmaDecode.c (ANSI-C LZMA Decoder): 
+   If _LZMA_OUT_READ was defined, and if encoded stream was corrupted,
+   decoder could access memory outside of allocated range.
+- Speed optimization of ANSI-C LZMA Decoder (now it's about 20% faster).
+  Old version of LZMA Decoder now is in file LzmaDecodeSize.c. 
+  LzmaDecodeSize.c can provide slightly smaller code than LzmaDecode.c
+- Small speed optimization in LZMA C++ code
+- filter for SPARC's code was added
+- Simplified version of .7z ANSI-C Decoder was included
+
+
+4.06           2004-09-05
+-------------------------
+- The bug in v4.05 was fixed:
+    LZMA-Encoder didn't release output stream in some cases.
+
+
+4.05           2004-08-25
+-------------------------
+- Source code of filters for x86, IA-64, ARM, ARM-Thumb 
+  and PowerPC code was included to SDK
+- Some internal minor changes
+
+
+4.04           2004-07-28
+-------------------------
+- More compatibility with some C++ compilers
+
+
+4.03           2004-06-18
+-------------------------
+- "Benchmark" command was added. It measures compressing 
+  and decompressing speed and shows rating values. 
+  Also it checks hardware errors.
+
+
+4.02           2004-06-10
+-------------------------
+- C++ LZMA Encoder/Decoder code now is more portable
+  and it can be compiled by GCC on Linux.
+
+
+4.01           2004-02-15
+-------------------------
+- Some detection of data corruption was enabled.
+    LzmaDecode.c / RangeDecoderReadByte
+    .....
+    {
+      rd->ExtraBytes = 1;
+      return 0xFF;
+    }
+
+
+4.00           2004-02-13
+-------------------------
+- Original version of LZMA SDK
+
+
+
+HISTORY of the LZMA
+-------------------
+  2001-2008:  Improvements to LZMA compressing/decompressing code, 
+              keeping compatibility with original LZMA format
+  1996-2001:  Development of LZMA compression format
+
+  Some milestones:
+
+  2001-08-30: LZMA compression was added to 7-Zip
+  1999-01-02: First version of 7-Zip was released
+  
+
+End of document
diff --git a/third_party/lzma/v4_65/files/lzma.exe b/third_party/lzma/v4_65/files/lzma.exe
new file mode 100644
index 0000000..1000f23
--- /dev/null
+++ b/third_party/lzma/v4_65/files/lzma.exe
Binary files differ
diff --git a/third_party/lzma/v4_65/files/lzma.txt b/third_party/lzma/v4_65/files/lzma.txt
new file mode 100644
index 0000000..715792d
--- /dev/null
+++ b/third_party/lzma/v4_65/files/lzma.txt
@@ -0,0 +1,594 @@
+LZMA SDK 4.65
+-------------
+
+LZMA SDK provides the documentation, samples, header files, libraries, 
+and tools you need to develop applications that use LZMA compression.
+
+LZMA is default and general compression method of 7z format
+in 7-Zip compression program (www.7-zip.org). LZMA provides high 
+compression ratio and very fast decompression.
+
+LZMA is an improved version of famous LZ77 compression algorithm. 
+It was improved in way of maximum increasing of compression ratio,
+keeping high decompression speed and low memory requirements for 
+decompressing.
+
+
+
+LICENSE
+-------
+
+LZMA SDK is written and placed in the public domain by Igor Pavlov.
+
+
+LZMA SDK Contents
+-----------------
+
+LZMA SDK includes:
+
+  - ANSI-C/C++/C#/Java source code for LZMA compressing and decompressing
+  - Compiled file->file LZMA compressing/decompressing program for Windows system
+
+
+UNIX/Linux version 
+------------------
+To compile C++ version of file->file LZMA encoding, go to directory
+C++/7zip/Compress/LZMA_Alone 
+and call make to recompile it:
+  make -f makefile.gcc clean all
+
+In some UNIX/Linux versions you must compile LZMA with static libraries.
+To compile with static libraries, you can use 
+LIB = -lm -static
+
+
+Files
+---------------------
+lzma.txt     - LZMA SDK description (this file)
+7zFormat.txt - 7z Format description
+7zC.txt      - 7z ANSI-C Decoder description
+methods.txt  - Compression method IDs for .7z
+lzma.exe     - Compiled file->file LZMA encoder/decoder for Windows
+history.txt  - history of the LZMA SDK
+
+
+Source code structure
+---------------------
+
+C/  - C files
+        7zCrc*.*   - CRC code
+        Alloc.*    - Memory allocation functions
+        Bra*.*     - Filters for x86, IA-64, ARM, ARM-Thumb, PowerPC and SPARC code
+        LzFind.*   - Match finder for LZ (LZMA) encoders 
+        LzFindMt.* - Match finder for LZ (LZMA) encoders for multithreading encoding
+        LzHash.h   - Additional file for LZ match finder
+        LzmaDec.*  - LZMA decoding
+        LzmaEnc.*  - LZMA encoding
+        LzmaLib.*  - LZMA Library for DLL calling
+        Types.h    - Basic types for another .c files
+	Threads.*  - The code for multithreading.
+
+    LzmaLib  - LZMA Library (.DLL for Windows)
+    
+    LzmaUtil - LZMA Utility (file->file LZMA encoder/decoder).
+
+    Archive - files related to archiving
+      7z     - 7z ANSI-C Decoder
+
+CPP/ -- CPP files
+
+  Common  - common files for C++ projects
+  Windows - common files for Windows related code
+
+  7zip    - files related to 7-Zip Project
+
+    Common   - common files for 7-Zip
+
+    Compress - files related to compression/decompression
+
+      Copy         - Copy coder
+      RangeCoder   - Range Coder (special code of compression/decompression)
+      LZMA         - LZMA compression/decompression on C++
+      LZMA_Alone   - file->file LZMA compression/decompression
+      Branch       - Filters for x86, IA-64, ARM, ARM-Thumb, PowerPC and SPARC code
+
+    Archive - files related to archiving
+
+      Common   - common files for archive handling
+      7z       - 7z C++ Encoder/Decoder
+
+    Bundles    - Modules that are bundles of other modules
+  
+      Alone7z           - 7zr.exe: Standalone version of 7z.exe that supports only 7z/LZMA/BCJ/BCJ2
+      Format7zR         - 7zr.dll: Reduced version of 7za.dll: extracting/compressing to 7z/LZMA/BCJ/BCJ2
+      Format7zExtractR  - 7zxr.dll: Reduced version of 7zxa.dll: extracting from 7z/LZMA/BCJ/BCJ2.
+
+    UI        - User Interface files
+         
+      Client7z - Test application for 7za.dll,  7zr.dll, 7zxr.dll
+      Common   - Common UI files
+      Console  - Code for console archiver
+
+
+
+CS/ - C# files
+  7zip
+    Common   - some common files for 7-Zip
+    Compress - files related to compression/decompression
+      LZ     - files related to LZ (Lempel-Ziv) compression algorithm
+      LZMA         - LZMA compression/decompression
+      LzmaAlone    - file->file LZMA compression/decompression
+      RangeCoder   - Range Coder (special code of compression/decompression)
+
+Java/  - Java files
+  SevenZip
+    Compression    - files related to compression/decompression
+      LZ           - files related to LZ (Lempel-Ziv) compression algorithm
+      LZMA         - LZMA compression/decompression
+      RangeCoder   - Range Coder (special code of compression/decompression)
+
+
+C/C++ source code of LZMA SDK is part of 7-Zip project.
+7-Zip source code can be downloaded from 7-Zip's SourceForge page:
+
+  http://sourceforge.net/projects/sevenzip/
+
+
+
+LZMA features
+-------------
+  - Variable dictionary size (up to 1 GB)
+  - Estimated compressing speed: about 2 MB/s on 2 GHz CPU
+  - Estimated decompressing speed: 
+      - 20-30 MB/s on 2 GHz Core 2 or AMD Athlon 64
+      - 1-2 MB/s on 200 MHz ARM, MIPS, PowerPC or other simple RISC
+  - Small memory requirements for decompressing (16 KB + DictionarySize)
+  - Small code size for decompressing: 5-8 KB
+
+LZMA decoder uses only integer operations and can be 
+implemented in any modern 32-bit CPU (or on 16-bit CPU with some conditions).
+
+Some critical operations that affect the speed of LZMA decompression:
+  1) 32*16 bit integer multiply
+  2) Misspredicted branches (penalty mostly depends from pipeline length)
+  3) 32-bit shift and arithmetic operations
+
+The speed of LZMA decompressing mostly depends from CPU speed.
+Memory speed has no big meaning. But if your CPU has small data cache, 
+overall weight of memory speed will slightly increase.
+
+
+How To Use
+----------
+
+Using LZMA encoder/decoder executable
+--------------------------------------
+
+Usage:  LZMA <e|d> inputFile outputFile [<switches>...]
+
+  e: encode file
+
+  d: decode file
+
+  b: Benchmark. There are two tests: compressing and decompressing 
+     with LZMA method. Benchmark shows rating in MIPS (million 
+     instructions per second). Rating value is calculated from 
+     measured speed and it is normalized with Intel's Core 2 results.
+     Also Benchmark checks possible hardware errors (RAM 
+     errors in most cases). Benchmark uses these settings:
+     (-a1, -d21, -fb32, -mfbt4). You can change only -d parameter. 
+     Also you can change the number of iterations. Example for 30 iterations:
+       LZMA b 30
+     Default number of iterations is 10.
+
+<Switches>
+  
+
+  -a{N}:  set compression mode 0 = fast, 1 = normal
+          default: 1 (normal)
+
+  d{N}:   Sets Dictionary size - [0, 30], default: 23 (8MB)
+          The maximum value for dictionary size is 1 GB = 2^30 bytes.
+          Dictionary size is calculated as DictionarySize = 2^N bytes. 
+          For decompressing file compressed by LZMA method with dictionary 
+          size D = 2^N you need about D bytes of memory (RAM).
+
+  -fb{N}: set number of fast bytes - [5, 273], default: 128
+          Usually big number gives a little bit better compression ratio 
+          and slower compression process.
+
+  -lc{N}: set number of literal context bits - [0, 8], default: 3
+          Sometimes lc=4 gives gain for big files.
+
+  -lp{N}: set number of literal pos bits - [0, 4], default: 0
+          lp switch is intended for periodical data when period is 
+          equal 2^N. For example, for 32-bit (4 bytes) 
+          periodical data you can use lp=2. Often it's better to set lc0, 
+          if you change lp switch.
+
+  -pb{N}: set number of pos bits - [0, 4], default: 2
+          pb switch is intended for periodical data 
+          when period is equal 2^N.
+
+  -mf{MF_ID}: set Match Finder. Default: bt4. 
+              Algorithms from hc* group doesn't provide good compression 
+              ratio, but they often works pretty fast in combination with 
+              fast mode (-a0).
+
+              Memory requirements depend from dictionary size 
+              (parameter "d" in table below). 
+
+               MF_ID     Memory                   Description
+
+                bt2    d *  9.5 + 4MB  Binary Tree with 2 bytes hashing.
+                bt3    d * 11.5 + 4MB  Binary Tree with 3 bytes hashing.
+                bt4    d * 11.5 + 4MB  Binary Tree with 4 bytes hashing.
+                hc4    d *  7.5 + 4MB  Hash Chain with 4 bytes hashing.
+
+  -eos:   write End Of Stream marker. By default LZMA doesn't write 
+          eos marker, since LZMA decoder knows uncompressed size 
+          stored in .lzma file header.
+
+  -si:    Read data from stdin (it will write End Of Stream marker).
+  -so:    Write data to stdout
+
+
+Examples:
+
+1) LZMA e file.bin file.lzma -d16 -lc0 
+
+compresses file.bin to file.lzma with 64 KB dictionary (2^16=64K)  
+and 0 literal context bits. -lc0 allows to reduce memory requirements 
+for decompression.
+
+
+2) LZMA e file.bin file.lzma -lc0 -lp2
+
+compresses file.bin to file.lzma with settings suitable 
+for 32-bit periodical data (for example, ARM or MIPS code).
+
+3) LZMA d file.lzma file.bin
+
+decompresses file.lzma to file.bin.
+
+
+Compression ratio hints
+-----------------------
+
+Recommendations
+---------------
+
+To increase the compression ratio for LZMA compressing it's desirable 
+to have aligned data (if it's possible) and also it's desirable to locate
+data in such order, where code is grouped in one place and data is 
+grouped in other place (it's better than such mixing: code, data, code,
+data, ...).
+
+
+Filters
+-------
+You can increase the compression ratio for some data types, using
+special filters before compressing. For example, it's possible to 
+increase the compression ratio on 5-10% for code for those CPU ISAs: 
+x86, IA-64, ARM, ARM-Thumb, PowerPC, SPARC.
+
+You can find C source code of such filters in C/Bra*.* files
+
+You can check the compression ratio gain of these filters with such 
+7-Zip commands (example for ARM code):
+No filter:
+  7z a a1.7z a.bin -m0=lzma
+
+With filter for little-endian ARM code:
+  7z a a2.7z a.bin -m0=arm -m1=lzma        
+
+It works in such manner:
+Compressing    = Filter_encoding + LZMA_encoding
+Decompressing  = LZMA_decoding + Filter_decoding
+
+Compressing and decompressing speed of such filters is very high,
+so it will not increase decompressing time too much.
+Moreover, it reduces decompression time for LZMA_decoding, 
+since compression ratio with filtering is higher.
+
+These filters convert CALL (calling procedure) instructions 
+from relative offsets to absolute addresses, so such data becomes more 
+compressible.
+
+For some ISAs (for example, for MIPS) it's impossible to get gain from such filter.
+
+
+LZMA compressed file format
+---------------------------
+Offset Size Description
+  0     1   Special LZMA properties (lc,lp, pb in encoded form)
+  1     4   Dictionary size (little endian)
+  5     8   Uncompressed size (little endian). -1 means unknown size
+ 13         Compressed data
+
+
+ANSI-C LZMA Decoder
+~~~~~~~~~~~~~~~~~~~
+
+Please note that interfaces for ANSI-C code were changed in LZMA SDK 4.58.
+If you want to use old interfaces you can download previous version of LZMA SDK
+from sourceforge.net site.
+
+To use ANSI-C LZMA Decoder you need the following files:
+1) LzmaDec.h + LzmaDec.c + Types.h
+LzmaUtil/LzmaUtil.c is example application that uses these files.
+
+
+Memory requirements for LZMA decoding
+-------------------------------------
+
+Stack usage of LZMA decoding function for local variables is not 
+larger than 200-400 bytes.
+
+LZMA Decoder uses dictionary buffer and internal state structure.
+Internal state structure consumes
+  state_size = (4 + (1.5 << (lc + lp))) KB
+by default (lc=3, lp=0), state_size = 16 KB.
+
+
+How To decompress data
+----------------------
+
+LZMA Decoder (ANSI-C version) now supports 2 interfaces:
+1) Single-call Decompressing
+2) Multi-call State Decompressing (zlib-like interface)
+
+You must use external allocator:
+Example:
+void *SzAlloc(void *p, size_t size) { p = p; return malloc(size); }
+void SzFree(void *p, void *address) { p = p; free(address); }
+ISzAlloc alloc = { SzAlloc, SzFree };
+
+You can use p = p; operator to disable compiler warnings.
+
+
+Single-call Decompressing
+-------------------------
+When to use: RAM->RAM decompressing
+Compile files: LzmaDec.h + LzmaDec.c + Types.h
+Compile defines: no defines
+Memory Requirements:
+  - Input buffer: compressed size
+  - Output buffer: uncompressed size
+  - LZMA Internal Structures: state_size (16 KB for default settings) 
+
+Interface:
+  int LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
+      const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, 
+      ELzmaStatus *status, ISzAlloc *alloc);
+  In: 
+    dest     - output data
+    destLen  - output data size
+    src      - input data
+    srcLen   - input data size
+    propData - LZMA properties  (5 bytes)
+    propSize - size of propData buffer (5 bytes)
+    finishMode - It has meaning only if the decoding reaches output limit (*destLen).
+	 LZMA_FINISH_ANY - Decode just destLen bytes.
+	 LZMA_FINISH_END - Stream must be finished after (*destLen).
+                           You can use LZMA_FINISH_END, when you know that 
+                           current output buffer covers last bytes of stream. 
+    alloc    - Memory allocator.
+
+  Out: 
+    destLen  - processed output size 
+    srcLen   - processed input size 
+
+  Output:
+    SZ_OK
+      status:
+        LZMA_STATUS_FINISHED_WITH_MARK
+        LZMA_STATUS_NOT_FINISHED 
+        LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
+    SZ_ERROR_DATA - Data error
+    SZ_ERROR_MEM  - Memory allocation error
+    SZ_ERROR_UNSUPPORTED - Unsupported properties
+    SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src).
+
+  If LZMA decoder sees end_marker before reaching output limit, it returns OK result,
+  and output value of destLen will be less than output buffer size limit.
+
+  You can use multiple checks to test data integrity after full decompression:
+    1) Check Result and "status" variable.
+    2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize.
+    3) Check that output(srcLen) = compressedSize, if you know real compressedSize. 
+       You must use correct finish mode in that case. */ 
+
+
+Multi-call State Decompressing (zlib-like interface)
+----------------------------------------------------
+
+When to use: file->file decompressing 
+Compile files: LzmaDec.h + LzmaDec.c + Types.h
+
+Memory Requirements:
+ - Buffer for input stream: any size (for example, 16 KB)
+ - Buffer for output stream: any size (for example, 16 KB)
+ - LZMA Internal Structures: state_size (16 KB for default settings) 
+ - LZMA dictionary (dictionary size is encoded in LZMA properties header)
+
+1) read LZMA properties (5 bytes) and uncompressed size (8 bytes, little-endian) to header:
+   unsigned char header[LZMA_PROPS_SIZE + 8];
+   ReadFile(inFile, header, sizeof(header)
+
+2) Allocate CLzmaDec structures (state + dictionary) using LZMA properties
+
+  CLzmaDec state;
+  LzmaDec_Constr(&state);
+  res = LzmaDec_Allocate(&state, header, LZMA_PROPS_SIZE, &g_Alloc);
+  if (res != SZ_OK)
+    return res;
+
+3) Init LzmaDec structure before any new LZMA stream. And call LzmaDec_DecodeToBuf in loop
+
+  LzmaDec_Init(&state);
+  for (;;)
+  {
+    ... 
+    int res = LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, 
+    	const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode);
+    ...
+  }
+
+
+4) Free all allocated structures
+  LzmaDec_Free(&state, &g_Alloc);
+
+For full code example, look at C/LzmaUtil/LzmaUtil.c code.
+
+
+How To compress data
+--------------------
+
+Compile files: LzmaEnc.h + LzmaEnc.c + Types.h +
+LzFind.c + LzFind.h + LzFindMt.c + LzFindMt.h + LzHash.h
+
+Memory Requirements:
+  - (dictSize * 11.5 + 6 MB) + state_size
+
+Lzma Encoder can use two memory allocators:
+1) alloc - for small arrays.
+2) allocBig - for big arrays.
+
+For example, you can use Large RAM Pages (2 MB) in allocBig allocator for 
+better compression speed. Note that Windows has bad implementation for 
+Large RAM Pages. 
+It's OK to use same allocator for alloc and allocBig.
+
+
+Single-call Compression with callbacks
+--------------------------------------
+
+Check C/LzmaUtil/LzmaUtil.c as example, 
+
+When to use: file->file decompressing 
+
+1) you must implement callback structures for interfaces:
+ISeqInStream
+ISeqOutStream
+ICompressProgress
+ISzAlloc
+
+static void *SzAlloc(void *p, size_t size) { p = p; return MyAlloc(size); }
+static void SzFree(void *p, void *address) {  p = p; MyFree(address); }
+static ISzAlloc g_Alloc = { SzAlloc, SzFree };
+
+  CFileSeqInStream inStream;
+  CFileSeqOutStream outStream;
+
+  inStream.funcTable.Read = MyRead;
+  inStream.file = inFile;
+  outStream.funcTable.Write = MyWrite;
+  outStream.file = outFile;
+
+
+2) Create CLzmaEncHandle object;
+
+  CLzmaEncHandle enc;
+
+  enc = LzmaEnc_Create(&g_Alloc);
+  if (enc == 0)
+    return SZ_ERROR_MEM;
+
+
+3) initialize CLzmaEncProps properties;
+
+  LzmaEncProps_Init(&props);
+
+  Then you can change some properties in that structure.
+
+4) Send LZMA properties to LZMA Encoder
+
+  res = LzmaEnc_SetProps(enc, &props);
+
+5) Write encoded properties to header
+
+    Byte header[LZMA_PROPS_SIZE + 8];
+    size_t headerSize = LZMA_PROPS_SIZE;
+    UInt64 fileSize;
+    int i;
+
+    res = LzmaEnc_WriteProperties(enc, header, &headerSize);
+    fileSize = MyGetFileLength(inFile);
+    for (i = 0; i < 8; i++)
+      header[headerSize++] = (Byte)(fileSize >> (8 * i));
+    MyWriteFileAndCheck(outFile, header, headerSize)
+
+6) Call encoding function:
+      res = LzmaEnc_Encode(enc, &outStream.funcTable, &inStream.funcTable, 
+        NULL, &g_Alloc, &g_Alloc);
+
+7) Destroy LZMA Encoder Object
+  LzmaEnc_Destroy(enc, &g_Alloc, &g_Alloc);
+
+
+If callback function return some error code, LzmaEnc_Encode also returns that code.
+
+
+Single-call RAM->RAM Compression
+--------------------------------
+
+Single-call RAM->RAM Compression is similar to Compression with callbacks,
+but you provide pointers to buffers instead of pointers to stream callbacks:
+
+HRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
+    CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, 
+    ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
+
+Return code:
+  SZ_OK               - OK
+  SZ_ERROR_MEM        - Memory allocation error 
+  SZ_ERROR_PARAM      - Incorrect paramater
+  SZ_ERROR_OUTPUT_EOF - output buffer overflow
+  SZ_ERROR_THREAD     - errors in multithreading functions (only for Mt version)
+
+
+
+LZMA Defines
+------------
+
+_LZMA_SIZE_OPT - Enable some optimizations in LZMA Decoder to get smaller executable code.
+
+_LZMA_PROB32   - It can increase the speed on some 32-bit CPUs, but memory usage for 
+                 some structures will be doubled in that case.
+
+_LZMA_UINT32_IS_ULONG  - Define it if int is 16-bit on your compiler and long is 32-bit.
+
+_LZMA_NO_SYSTEM_SIZE_T  - Define it if you don't want to use size_t type.
+
+
+C++ LZMA Encoder/Decoder 
+~~~~~~~~~~~~~~~~~~~~~~~~
+C++ LZMA code use COM-like interfaces. So if you want to use it, 
+you can study basics of COM/OLE.
+C++ LZMA code is just wrapper over ANSI-C code.
+
+
+C++ Notes
+~~~~~~~~~~~~~~~~~~~~~~~~
+If you use some C++ code folders in 7-Zip (for example, C++ code for .7z handling),
+you must check that you correctly work with "new" operator.
+7-Zip can be compiled with MSVC 6.0 that doesn't throw "exception" from "new" operator.
+So 7-Zip uses "CPP\Common\NewHandler.cpp" that redefines "new" operator:
+operator new(size_t size)
+{
+  void *p = ::malloc(size);
+  if (p == 0)
+    throw CNewException();
+  return p;
+}
+If you use MSCV that throws exception for "new" operator, you can compile without 
+"NewHandler.cpp". So standard exception will be used. Actually some code of 
+7-Zip catches any exception in internal code and converts it to HRESULT code.
+So you don't need to catch CNewException, if you call COM interfaces of 7-Zip.
+
+---
+
+http://www.7-zip.org
+http://www.7-zip.org/sdk.html
+http://www.7-zip.org/support.html
diff --git a/third_party/minicrt/build.scons b/third_party/minicrt/build.scons
index 803b8c2..0415b71 100644
--- a/third_party/minicrt/build.scons
+++ b/third_party/minicrt/build.scons
@@ -21,7 +21,6 @@
 local_env = env.Clone()
 local_env.Append(
     CCFLAGS = [
-        '/wd4255',  # no function prototype given: converting '()' to '(void)'
         '/wd4287',  # unsigned/negative constant mismatch
         '/wd4706',  # assignment within conditional expression
         ],
@@ -36,25 +35,25 @@
 _inputs = [
     'alloc.cc',
     'alloc2.cc',
-    'allocsup.cc',
-    'argcargv.cc',
-    'bsearch.c',
-    'charmax.c',
-    'crt0tcon.cc',
+    #'allocsup.cc',
+    #'argcargv.cc',
+    #'bsearch.c',
+    #'charmax.c',
+    #'crt0tcon.cc',
     'crt0twin.cc',
-    'dllcrt0.cc',
-    'fullpath.cc',
+    #'dllcrt0.cc',
+    #'fullpath.cc',
     'initterm.cc',
-    'isctype.cc',
+    #'isctype.cc',
     'memory.cc',
     'newdel.cc',
     'pesect.c',
-    'puts.cc',
+    #'puts.cc',
     'resetstk.c',
     'security.cc',
-    'stricmp.cc',
+    #'stricmp.cc',
     'string.c',
-    'struplwr.cc',
+    #'struplwr.cc',
 
     'alloca16.obj',
     'chandler4.obj',
diff --git a/third_party/minicrt/memory.cc b/third_party/minicrt/memory.cc
index c5a5f4e..5509bcd 100644
--- a/third_party/minicrt/memory.cc
+++ b/third_party/minicrt/memory.cc
@@ -74,9 +74,9 @@
       return 0;
   }
 
-  if (dst != NULL) return EINVAL;
-  if (src != NULL) return EINVAL;
-  if (size_in_bytes >= count) return ERANGE;
+  if (!dst) return EINVAL;
+  if (!src) return EINVAL;
+  if (size_in_bytes < count) return ERANGE;
 
   memmove(dst, src, count);
   return 0;
@@ -90,9 +90,9 @@
         return 0;
   }
 
-  if (dst != NULL) return EINVAL;
-  if (src != NULL) return EINVAL;
-  if (size_in_bytes >= count) return ERANGE;
+  if (!dst) return EINVAL;
+  if (!src) return EINVAL;
+  if (size_in_bytes < count) return ERANGE;
 
   memcpy(dst, src, count);
   return 0;
diff --git a/tools/ApplyTag/apply_tag_tool.cc b/tools/ApplyTag/apply_tag_tool.cc
index 9fd80c1..bdea398 100644
--- a/tools/ApplyTag/apply_tag_tool.cc
+++ b/tools/ApplyTag/apply_tag_tool.cc
@@ -16,10 +16,10 @@
 // The main file for a simple tool to apply a tag to a signed file.
 #include <Windows.h>
 #include <TCHAR.h>
-#include "omaha/common/apply_tag.h"
-#include "omaha/common/file.h"
-#include "omaha/common/path.h"
-#include "omaha/common/utils.h"
+#include "omaha/base/apply_tag.h"
+#include "omaha/base/file.h"
+#include "omaha/base/path.h"
+#include "omaha/base/utils.h"
 
 using omaha::CreateDir;
 using omaha::ConcatenatePath;
diff --git a/tools/ApplyTag/build.scons b/tools/ApplyTag/build.scons
index 5276f01..149c2df 100644
--- a/tools/ApplyTag/build.scons
+++ b/tools/ApplyTag/build.scons
@@ -32,7 +32,7 @@
         'userenv.lib',
         'version.lib',
         'wtsapi32.lib',
-        '$LIB_DIR/common.lib',
+        '$LIB_DIR/base.lib',
         ],
     CPPDEFINES = [
         'UNICODE',
@@ -44,12 +44,12 @@
 local_env.FilterOut(LINKFLAGS = ['/SUBSYSTEM:WINDOWS'])
 local_env['LINKFLAGS'] += ['/SUBSYSTEM:CONSOLE']
 
-local_env.Dir('.').addRepository(local_env.Dir('$MAIN_DIR/common'))
+local_env.Dir('.').addRepository(local_env.Dir('$MAIN_DIR/base'))
 
 target_name = 'ApplyTag'
 
 inputs = [
-    'apply_tag.cc',  # Comes from $MAIN_DIR/common
+    'apply_tag.cc',  # Comes from $MAIN_DIR/base
     'apply_tag_tool.cc',
     ]
 if env.Bit('use_precompiled_headers'):
diff --git a/tools/CrashProcess/build.scons b/tools/CrashProcess/build.scons
index e98b417..552b07b 100644
--- a/tools/CrashProcess/build.scons
+++ b/tools/CrashProcess/build.scons
@@ -17,10 +17,12 @@
 Import('env')
 
 local_env = env.Clone()
+local_env.FilterOut(CPPDEFINES=['_ATL_DEBUG_INTERFACES'])
 
 local_env.Append(
     LIBS = [
         ('libcmt.lib', 'libcmtd.lib')[local_env.Bit('debug')],
+        ('libcpmt.lib', 'libcpmtd.lib')[local_env.Bit('debug')],
         'psapi.lib',
         ],
     CPPDEFINES = [
@@ -32,9 +34,17 @@
 local_env.FilterOut(LINKFLAGS = ['/SUBSYSTEM:WINDOWS'])
 local_env['LINKFLAGS'] += ['/SUBSYSTEM:CONSOLE']
 
+target_name = 'CrashProcess'
+
+inputs = [
+    'crash_process.cc',
+    ]
+if local_env.Bit('use_precompiled_headers'):
+  inputs += local_env.EnablePrecompile(target_name)
 
 local_env.ComponentTestProgram(
-    prog_name='CrashProcess',
-    source=['crash_process.cc'],
+    prog_name=target_name,
+    source=inputs,
     COMPONENT_TEST_RUNNABLE=False
 )
+
diff --git a/tools/OmahaCompatibility/console_writer.cc b/tools/OmahaCompatibility/console_writer.cc
index 970e0e6..bdcd184 100644
--- a/tools/OmahaCompatibility/console_writer.cc
+++ b/tools/OmahaCompatibility/console_writer.cc
@@ -71,6 +71,10 @@
     // Other events.
     case PingEvent::EVENT_INSTALL_OEM_FIRST_CHECK:
       return _T("EVENT_INSTALL_OEM_FIRST_CHECK");
+    case PingEvent::EVENT_APP_COMMAND_BEGIN:
+      return _T("EVENT_APP_COMMAND_BEGIN");
+    case PingEvent::EVENT_APP_COMMAND_COMPLETE:
+      return _T("EVENT_APP_COMMAND_COMPLETE");
 
     // Failure report events - not part of the normal flow.
     case PingEvent::EVENT_SETUP_INSTALL_FAILURE:
diff --git a/tools/build.scons b/tools/build.scons
index e4b3640..96d8ae6 100644
--- a/tools/build.scons
+++ b/tools/build.scons
@@ -15,18 +15,12 @@
 
 Import('env')
 
-# Goopdump must always be built because there is a unit test for it.
-subdirs = ['goopdump']
+subdirs = []
 
 if not env.Bit('min'):
   subdirs += [
       'ApplyTag',
       'CrashProcess',
-      'OmahaCompatibility',
-      'longrunning',
-      'ReadTag',
-      'PerformOnDemand',
-      'SetShutDownEvent',
       ]
 
 for dir in subdirs:
diff --git a/tools/generate_omaha3_idl.py b/tools/generate_omaha3_idl.py
new file mode 100644
index 0000000..7e0445d
--- /dev/null
+++ b/tools/generate_omaha3_idl.py
@@ -0,0 +1,116 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2011 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========================================================================
+
+"""Generates IDL file Omaha3 internfaces."""
+
+import commands
+import getopt
+import os
+import sys
+
+
+def _GetStatusOutput(cmd):
+  """Return (status, output) of executing cmd in a shell."""
+  if os.name == "nt":
+    pipe = os.popen(cmd + " 2>&1", "r")
+    text = pipe.read()
+    sts = pipe.close()
+    if sts is None: sts = 0
+    if text[-1:] == "\n": text = text[:-1]
+    return sts, text
+  else:
+    return commands.getstatusoutput(cmd)
+
+
+def _GenerateGuid():
+  (status, guid) = _GetStatusOutput("uuidgen.exe /c")
+  if status != 0:
+    raise SystemError("Failed to get GUID: %s" % guid)
+  return guid
+
+
+def _GenerateIDLText(idl_template):
+  guid_placehold_marker = "___AUTO_GENERATED_GUID___"
+  while guid_placehold_marker in idl_template:
+    idl_template = idl_template.replace(guid_placehold_marker,
+                                        _GenerateGuid(),
+                                        1)
+  return idl_template
+
+
+def _GenerateIDLFile(idl_template_filename, idl_output_filename):
+  f_in = open(idl_template_filename, "r")
+  idl_template = f_in.read()
+  f_in.close()
+
+  idl_output = _GenerateIDLText(idl_template)
+
+  f_out = open(idl_output_filename, "w")
+  f_out.write("// *** AUTOGENERATED FILE. DO NOT HAND-EDIT ***\n\n")
+  f_out.write(idl_output)
+  f_out.close()
+
+
+def _Usage():
+  """Prints out script usage information."""
+  print """
+generate_omaha3_idl.py: Write out the given IDL file.
+
+Usage:
+  generate_omaha3_idl.py [--help
+                          | --idl_template_file filename
+                            --idl_output_file filename]
+
+Options:
+  --help                        Show this information.
+  --idl_output_file filename    Path/name of output IDL filename.
+  --idl_template_file filename  Path/name of input IDL template.
+"""
+
+
+def _Main():
+  """Generates IDL file."""
+  # use getopt to parse the option and argument list; this may raise, but
+  # don't catch it
+  argument_list = ["help", "idl_template_file=", "idl_output_file="]
+  (opts, unused_args) = getopt.getopt(sys.argv[1:], "", argument_list)
+  if not opts or ("--help", "") in opts:
+    _Usage()
+    sys.exit()
+
+  idl_template_filename = ""
+  idl_output_filename = ""
+
+  for (o, v) in opts:
+    if o == "--idl_template_file":
+      idl_template_filename = v
+    if o == "--idl_output_file":
+      idl_output_filename = v
+
+  # make sure we have work to do
+  if not idl_template_filename:
+    raise StandardError("no idl_template_filename specified")
+  if not idl_output_filename:
+    raise StandardError("no idl_output_filename specified")
+
+  _GenerateIDLFile(idl_template_filename, idl_output_filename)
+  sys.exit()
+
+
+if __name__ == "__main__":
+  _Main()
+
diff --git a/tools/longrunning/do_not_elevate.manifest b/tools/longrunning/do_not_elevate.manifest
deleted file mode 100644
index c3263b5..0000000
--- a/tools/longrunning/do_not_elevate.manifest
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
-  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
-    <security>
-      <requestedPrivileges>
-        <requestedExecutionLevel level="asInvoker" />
-      </requestedPrivileges>
-    </security>
-  </trustInfo>
-</assembly>
diff --git a/tools/longrunning/resource.rc b/tools/longrunning/resource.rc
deleted file mode 100644
index 352da28..0000000
--- a/tools/longrunning/resource.rc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "afxres.h"
-
-LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
-#pragma code_page(1252)
-
-1 RT_MANIFEST "do_not_elevate.manifest"
diff --git a/tools/proxy_clsid_utils.py b/tools/proxy_clsid_utils.py
new file mode 100644
index 0000000..827a914
--- /dev/null
+++ b/tools/proxy_clsid_utils.py
@@ -0,0 +1,152 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2011 Google Inc. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========================================================================
+
+"""Generates Omaha build file and Omaha customization unit test file."""
+
+import commands
+import os
+
+
+omaha_project_path = os.path.dirname(__file__) + "\\.."
+proxy_clsid_file_name = omaha_project_path + "\\proxy_clsids.txt"
+customization_ut_file_name = (omaha_project_path +
+                              "\\common\\omaha_customization_proxy_clsid.h")
+
+
+def _GetStatusOutput(cmd):
+  """Return (status, output) of executing cmd in a shell."""
+  if os.name == "nt":
+    pipe = os.popen(cmd + " 2>&1", "r")
+    text = pipe.read()
+    sts = pipe.close()
+    if sts is None: sts = 0
+    if text[-1:] == "\n": text = text[:-1]
+    return sts, text
+  else:
+    return commands.getstatusoutput(cmd)
+
+
+def _GenerateGuid():
+  (status, guid) = _GetStatusOutput("uuidgen.exe /c")
+  if status != 0:
+    raise SystemError("Failed to get GUID: %s" % guid)
+  return guid
+
+
+def _GuidToCStructFormat(guid):
+  return ("{0x%s, 0x%s, 0x%s, "
+          "{0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s}}") % (
+              guid[0:8], guid[9:13], guid[14:18],
+              guid[19:21], guid[21:23], guid[24:26], guid[26:28],
+              guid[28:30], guid[30:32], guid[32:34], guid[34:36])
+
+
+def _GenerateProxySconsText(machine_proxy_clsid,
+                            user_proxy_clsid):
+  proxy_clsid_text_format = ("PROXY_CLSID_IS_MACHINE=%s\n"
+                             "PROXY_CLSID_IS_USER=%s\n")
+  return proxy_clsid_text_format % (_GuidToCStructFormat(machine_proxy_clsid),
+                                    _GuidToCStructFormat(user_proxy_clsid))
+
+
+def _GenerateCustomizationUTText(machine_proxy_clsid, user_proxy_clsid):
+  customization_ut_template = (
+      "//\n"
+      "// !!! AUTOGENERATED FILE. DO NOT HAND-EDIT !!!\n"
+      "//\n\n"
+      "namespace omaha {\n\n"
+      "%s\n%s\n}  // namespace omaha\n")
+  customization_ut_machine_proxy_clsid_string = (
+      "// PROXY_CLSID_IS_MACHINE = {%s}\n"
+      "const GUID kProxyClsidIsMachineGuid =\n"
+      "    %s;\n") % (machine_proxy_clsid,
+                      _GuidToCStructFormat(machine_proxy_clsid))
+
+  customization_ut_user_proxy_clsid_string = (
+      "// PROXY_CLSID_IS_USER = {%s}\n"
+      "const GUID kProxyClsidIsUserGuid =\n"
+      "    %s;\n") % (user_proxy_clsid,
+                      _GuidToCStructFormat(user_proxy_clsid))
+
+  return customization_ut_template % (
+      customization_ut_machine_proxy_clsid_string,
+      customization_ut_user_proxy_clsid_string)
+
+
+def _GenerateProxyClsidFile(machine_proxy_clsid,
+                            user_proxy_clsid):
+  proxy_clsid_output = _GenerateProxySconsText(machine_proxy_clsid,
+                                               user_proxy_clsid)
+  f_out = open(proxy_clsid_file_name, "w")
+  f_out.write(proxy_clsid_output)
+  f_out.close()
+
+
+def _GenerateCustomizationUnitTestFile(machine_proxy_clsid,
+                                       user_proxy_clsid):
+  customization_ut_output = _GenerateCustomizationUTText(machine_proxy_clsid,
+                                                         user_proxy_clsid)
+  f_out = open(customization_ut_file_name, "w")
+  f_out.write(customization_ut_output)
+  f_out.close()
+
+
+def _GenerateProxyClsidsFiles():
+  if (os.path.isfile(proxy_clsid_file_name) and
+      os.path.isfile(customization_ut_file_name)):
+    return
+
+  machine_proxy_clsid = _GenerateGuid()
+  user_proxy_clsid = _GenerateGuid()
+
+  _GenerateProxyClsidFile(machine_proxy_clsid, user_proxy_clsid)
+  _GenerateCustomizationUnitTestFile(machine_proxy_clsid,
+                                     user_proxy_clsid)
+
+
+def _GetProxyClsidsFromFile(target_proxy_clsid):
+  proxy_clsid = ""
+  f = open(proxy_clsid_file_name, "r")
+  for line in f:
+    if not line.startswith("#") and target_proxy_clsid in line:
+      proxy_clsid = line[len(target_proxy_clsid):].rstrip()
+      break
+  f.close()
+
+  if not proxy_clsid:
+    raise StandardError("Failed to get auto-generated proxy CLSID")
+
+  return proxy_clsid
+
+
+def GetMachineProxyClsid():
+  """Loads machine proxy CLSID from the generated file."""
+  return _GetProxyClsidsFromFile("PROXY_CLSID_IS_MACHINE=")
+
+
+def GetUserProxyClsid():
+  """Loads user proxy CLSID from the generated file."""
+  return _GetProxyClsidsFromFile("PROXY_CLSID_IS_USER=")
+
+
+def _Main():
+  """Generates proxy_clsids.txt and customization unit test file."""
+  _GenerateProxyClsidsFiles()
+
+
+if __name__ == "__main__":
+  _Main()
diff --git a/ui/build.scons b/ui/build.scons
new file mode 100644
index 0000000..dc5d16e
--- /dev/null
+++ b/ui/build.scons
@@ -0,0 +1,72 @@
+# Copyright 2008-2010 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ========================================================================
+
+Import('env')
+
+local_env = env.Clone()
+
+inputs = [
+    'complete_wnd.cc',
+    'yes_no_dialog.cc',
+    'progress_wnd.cc',
+    'splash_screen.cc',
+    'ui.cc',
+    'ui_displayed_event.cc',
+    'ui_metrics.cc',
+
+    'uilib/node_state.cc',
+    'uilib/static_ex.cc',
+    'uilib/static_line.cc',
+    ]
+
+# Need to look in output dir to find .h files generated by midl compiler.
+# This also allows Hammer to understand dependencies between this subdir
+# and the .idl files in the goopdate folder.
+local_env['CPPPATH'] += ['$OBJ_ROOT']
+
+
+# Build these into a static library.
+local_env.ComponentStaticLibrary('ui', inputs)
+
+ui_test_env = env.Clone()
+ui_test_env.Append(
+    CPPPATH = [
+        # Need to look in output dir to find .h files generated by midl compiler.
+        # This also allows Hammer to understand dependencies between this subdir
+        # and the .idl files in the goopdate folder.
+        '$OBJ_ROOT',
+        ],
+)
+
+ui_test_env.OmahaUnittest(
+    name='omaha_ui_unittest',
+    source=[
+        'splash_screen_test.cc',
+        'progress_wnd_unittest.cc',
+        'yes_no_dialog_unittest.cc',
+    ],
+    LIBS=[
+        '$LIB_DIR/client.lib',
+        '$LIB_DIR/common.lib',       # Required by client.
+        '$LIB_DIR/goopdate_lib.lib', # Required by splashscreen UT.
+        '$LIB_DIR/logging.lib',      # Required by statsreport.
+        '$LIB_DIR/omaha3_idl.lib',
+        '$LIB_DIR/statsreport.lib',  # Required by client.
+        '$LIB_DIR/ui.lib',
+    ],
+    all_in_one=False,
+    COMPONENT_TEST_SIZE='small',
+    is_small_tests_using_resources=True,
+)
diff --git a/ui/complete_wnd.cc b/ui/complete_wnd.cc
new file mode 100644
index 0000000..724e31e
--- /dev/null
+++ b/ui/complete_wnd.cc
@@ -0,0 +1,224 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include "omaha/ui/complete_wnd.h"
+#include "omaha/base/const_addresses.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/safe_format.h"
+#include "omaha/base/omaha_version.h"
+#include "omaha/base/vistautil.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/ui/ui_metrics.h"
+
+namespace omaha {
+
+CompleteWnd::CompleteWnd(CMessageLoop* message_loop, HWND parent)
+    : OmahaWnd(IDD_PROGRESS, message_loop, parent),
+      events_sink_(NULL),
+      control_classes_(ICC_STANDARD_CLASSES) {
+  CORE_LOG(L3, (_T("[CompleteWnd::CompleteWnd]")));
+}
+
+// dialog_id specifies the dialog resource to use.
+// control_classes specifies the control classes required for dialog_id.
+CompleteWnd::CompleteWnd(int dialog_id,
+                         DWORD control_classes,
+                         CMessageLoop* message_loop,
+                         HWND parent)
+    : OmahaWnd(dialog_id, message_loop, parent),
+      events_sink_(NULL),
+      control_classes_(control_classes | ICC_STANDARD_CLASSES) {
+  CORE_LOG(L3, (_T("[CompleteWnd::CompleteWnd]")));
+}
+
+HRESULT CompleteWnd::Initialize() {
+  CORE_LOG(L3, (_T("[CompleteWnd::Initialize]")));
+
+  HRESULT hr = InitializeCommonControls(control_classes_);
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  return OmahaWnd::Initialize();
+}
+
+void CompleteWnd::SetEventSink(CompleteWndEvents* ev) {
+  events_sink_ = ev;
+  OmahaWnd::SetEventSink(events_sink_);
+}
+
+LRESULT CompleteWnd::OnInitDialog(UINT message,
+                                  WPARAM w_param,
+                                  LPARAM l_param,
+                                  BOOL& handled) {  // NOLINT
+  CORE_LOG(L3, (_T("[CompleteWnd::OnInitDialog]")));
+  UNREFERENCED_PARAMETER(message);
+  UNREFERENCED_PARAMETER(w_param);
+  UNREFERENCED_PARAMETER(l_param);
+  handled = true;
+
+  InitializeDialog();
+
+  SetControlAttributes(IDC_COMPLETE_TEXT, kDisabledNonButtonAttributes);
+  SetControlAttributes(IDC_ERROR_TEXT, kDisabledNonButtonAttributes);
+  SetControlAttributes(IDC_IMAGE, kDisabledNonButtonAttributes);
+  SetControlAttributes(IDC_GET_HELP_TEXT, kDisabledNonButtonAttributes);
+  SetControlAttributes(IDC_CLOSE, kDefaultActiveButtonAttributes);
+
+  return 1;  // Let the system set the focus.
+}
+
+LRESULT CompleteWnd::OnClickedButton(WORD notify_code,
+                                     WORD id,
+                                     HWND wnd_ctl,
+                                     BOOL& handled) {  // NOLINT
+  CORE_LOG(L3, (_T("[CompleteWnd::OnClickedButton]")));
+  UNREFERENCED_PARAMETER(id);
+  UNREFERENCED_PARAMETER(notify_code);
+  UNREFERENCED_PARAMETER(wnd_ctl);
+  ASSERT1(id == IDC_CLOSE);
+  ASSERT1(is_complete());
+  handled = true;
+
+  VERIFY1(SUCCEEDED(CloseWindow()));
+
+  return 0;
+}
+
+LRESULT CompleteWnd::OnUrlClicked(int, LPNMHDR params, BOOL& handled) {  // NOLINT
+  CORE_LOG(L3, (_T("[CompleteWnd::OnUrlClicked]")));
+  ASSERT1(params);
+
+  if (IDC_GET_HELP_TEXT == params->idFrom) {
+    ++metric_worker_ui_get_help_click;
+  }
+
+  NMSTATICEX* notification = reinterpret_cast<NMSTATICEX*>(params);
+  ASSERT1(events_sink_);
+  if (events_sink_) {
+    bool is_launched = events_sink_->DoLaunchBrowser(notification->action);
+    // Assert that the launch succeeded because this code should not be called
+    // if the launch mechanism (i.e. IProcessLauncher when running elevated) is
+    // not in place. This could also fail if the default browser has been
+    // uninstalled, but that is unlikely.
+    ASSERT1(is_launched);
+    // TODO(omaha): Consider doing something if the browser launch failed.
+    // Could display a message in English saying it failed for some reason,
+    // please CTRL-C this dialog, get the URL and paste it into a browser.
+  }
+
+  handled = true;
+  return 1;
+}
+
+bool CompleteWnd::MaybeCloseWindow() {
+  VERIFY1(SUCCEEDED(CloseWindow()));
+  return true;
+}
+
+void CompleteWnd::DisplayCompletionDialog(bool is_success,
+                                          const CString& text,
+                                          const CString& help_url) {
+  CORE_LOG(L3, (_T("[CompleteWnd::DisplayCompletionDialog]")
+                _T("[success=%d][text=%s]"), is_success, text));
+  ASSERT1(!text.IsEmpty());
+
+  // FormatMessage() converts all LFs to CRLFs, which display as boxes in UI.
+  // We have also observed some BITS error messages with boxes that may have
+  // been caused by CRLFs.
+  // To avoid boxes, convert all CRLFs to LFs, which result in line breaks.
+  CString display_text = text;
+  display_text.Replace(_T("\r\n"), _T("\n"));
+
+  if (!OmahaWnd::OnComplete()) {
+    return;
+  }
+
+  // It is possible for the OnComplete callback to be called multiple times.
+  // Subclassing the control multiple times results in a crash, therefore
+  // unsubclass the control if the control has been created and subclassed
+  // before.
+  if (complete_text_ != NULL) {
+    // TODO(omaha3): I'm not sure this can happen in the polling model.
+    ASSERT1(false);
+
+    complete_text_->UnsubclassWindow(true);
+    complete_text_.reset(NULL);
+  }
+
+  CString s;
+  if (is_success) {
+    VERIFY1(s.LoadString(IDS_CLOSE));
+    VERIFY1(::SetWindowText(GetDlgItem(IDC_CLOSE), s));
+    complete_text_.reset(new StaticEx);
+    complete_text_->SubclassWindow(GetDlgItem(IDC_COMPLETE_TEXT));
+    VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), text));
+  } else {
+    VERIFY1(s.LoadString(IDS_CLOSE));
+    VERIFY1(::SetWindowText(GetDlgItem(IDC_CLOSE), s));
+    complete_text_.reset(new StaticEx);
+    complete_text_->SubclassWindow(GetDlgItem(IDC_ERROR_TEXT));
+    VERIFY1(::SetWindowText(GetDlgItem(IDC_ERROR_TEXT), display_text));
+    VERIFY1(SUCCEEDED(ShowGetHelpLink(help_url)));
+  }
+
+  VERIFY1(SUCCEEDED(SetControlState(is_success)));
+
+  return;
+}
+
+
+HRESULT CompleteWnd::SetControlState(bool is_success) {
+  SetControlAttributes(is_success ? IDC_COMPLETE_TEXT : IDC_ERROR_TEXT,
+                       kVisibleTextAttributes);
+  if (is_success) {
+    SetControlAttributes(IDC_IMAGE, kVisibleImageAttributes);
+  } else {
+    SetControlAttributes(IDC_GET_HELP_TEXT, kVisibleTextAttributes);
+  }
+  SetControlAttributes(IDC_CLOSE, kDefaultActiveButtonAttributes);
+
+  return S_OK;
+}
+
+// If help_url is empty, no link will be displayed.
+HRESULT CompleteWnd::ShowGetHelpLink(const CString& help_url) {
+  // If there is no event sink, clicking the URL will fail.
+  ASSERT1(events_sink_);
+
+  if (help_url.IsEmpty()) {
+    return S_OK;
+  }
+  ASSERT1(0 == help_url.Find(_T("http://")));
+
+  const TCHAR* const kLinkFormat = _T("<b><a=%s>%s</a></b>");
+  CString display_text;
+  VERIFY1(display_text.LoadString(IDS_GET_HELP_TEXT));
+  CString link_string;
+  SafeCStringFormat(&link_string, kLinkFormat, help_url, display_text);
+
+  get_help_text_.reset(new StaticEx);
+  get_help_text_->SubclassWindow(GetDlgItem(IDC_GET_HELP_TEXT));
+  VERIFY1(::SetWindowText(GetDlgItem(IDC_GET_HELP_TEXT), link_string));
+
+  ++metric_worker_ui_get_help_displayed;
+  return S_OK;
+}
+
+}  // namespace omaha
+
diff --git a/ui/complete_wnd.h b/ui/complete_wnd.h
new file mode 100644
index 0000000..16b0c5b
--- /dev/null
+++ b/ui/complete_wnd.h
@@ -0,0 +1,91 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_UI_COMPLETE_WND_H_
+#define OMAHA_UI_COMPLETE_WND_H_
+
+#include "base/scoped_ptr.h"
+#include "omaha/ui/ui.h"
+#include "omaha/ui/uilib/static_ex.h"
+
+namespace omaha {
+
+class CompleteWndEvents : public OmahaWndEvents {
+ public:
+  // Launches the browser non-privileged and returns whether the browser was
+  // successfully launched.
+  virtual bool DoLaunchBrowser(const CString& url) = 0;
+};
+
+class CompleteWnd : public OmahaWnd {
+ public:
+  CompleteWnd(CMessageLoop* message_loop, HWND parent);
+
+  virtual HRESULT Initialize();
+
+  void SetEventSink(CompleteWndEvents* ev);
+
+  void DisplayCompletionDialog(bool is_success,
+                               const CString& text,
+                               const CString& help_url);
+
+  BEGIN_MSG_MAP(ErrorWnd)
+    MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
+    NOTIFY_CODE_HANDLER(NM_STATICEX, OnUrlClicked)
+    COMMAND_HANDLER(IDC_CLOSE, BN_CLICKED, OnClickedButton)
+    CHAIN_MSG_MAP(OmahaWnd)
+  END_MSG_MAP()
+
+ protected:
+  // Constructor to override the default dialog resource ID and control classes.
+  CompleteWnd(int dialog_id,
+              DWORD control_classes,
+              CMessageLoop* message_loop,
+              HWND parent);
+
+  // Message and command handlers.
+  LRESULT OnInitDialog(UINT msg,
+                       WPARAM wparam,
+                       LPARAM lparam,
+                       BOOL& handled);  // NOLINT
+  LRESULT OnClickedButton(WORD notify_code,
+                          WORD id,
+                          HWND wnd_ctl,
+                          BOOL& handled);   // NOLINT
+  LRESULT OnUrlClicked(int idCtrl, LPNMHDR pnmh, BOOL& bHandled);   // NOLINT
+
+ private:
+  // Handles requests to close the window. Returns true if the window is closed.
+  virtual bool MaybeCloseWindow();
+
+  HRESULT SetControlState(bool is_success);
+
+  HRESULT ShowGetHelpLink(const CString& help_url);
+
+  // Due to a repaint issue in StaticEx we prefer to manage their lifetime
+  // very aggressively so we contain them by reference instead of value.
+  scoped_ptr<StaticEx> complete_text_;
+  scoped_ptr<StaticEx> get_help_text_;
+
+  CompleteWndEvents* events_sink_;
+  const DWORD control_classes_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(CompleteWnd);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_UI_COMPLETE_WND_H_
+
diff --git a/ui/progress_wnd.cc b/ui/progress_wnd.cc
new file mode 100644
index 0000000..59852cd
--- /dev/null
+++ b/ui/progress_wnd.cc
@@ -0,0 +1,840 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include "omaha/ui/progress_wnd.h"
+#include "base/basictypes.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/utils.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/ui/ui_ctls.h"
+#include "omaha/ui/ui_metrics.h"
+
+namespace omaha {
+
+namespace {
+
+// The current UI is only able to show one completion type. If apps in the
+// bundle have different completion type, then we need to decide which
+// one should be shown to the user. The following array lists the types
+// from low priority to high priority. The completion type with highest
+// priority will be shown to the user.
+const CompletionCodes kCompletionCodesActionPriority[] = {
+  COMPLETION_CODE_EXIT_SILENTLY,
+  COMPLETION_CODE_EXIT_SILENTLY_ON_LAUNCH_COMMAND,
+  COMPLETION_CODE_SUCCESS,
+  COMPLETION_CODE_LAUNCH_COMMAND,
+  COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY,
+  COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY,
+  COMPLETION_CODE_RESTART_BROWSER,
+  COMPLETION_CODE_RESTART_ALL_BROWSERS,
+  COMPLETION_CODE_REBOOT_NOTICE_ONLY,
+  COMPLETION_CODE_REBOOT,
+  COMPLETION_CODE_ERROR,
+  COMPLETION_CODE_INSTALL_FINISHED_BEFORE_CANCEL,
+};
+
+// kCompletionCodesActionPriority should have all the values in enumeration
+// CompletionCodes. The enumeration value starts from 1 so the array size
+// should match the last value in the enumeration.
+COMPILE_ASSERT(arraysize(kCompletionCodesActionPriority) ==
+    COMPLETION_CODE_INSTALL_FINISHED_BEFORE_CANCEL,
+    CompletionCodesActionPriority_missing_completion_code);
+
+int GetActionPriority(CompletionCodes code) {
+  for (int i = 0; i < arraysize(kCompletionCodesActionPriority); ++i) {
+    if (kCompletionCodesActionPriority[i] == code) {
+      return i;
+    }
+  }
+
+  ASSERT1(false);
+  return -1;
+}
+
+bool AreAllAppsCanceled(const std::vector<AppCompletionInfo>& apps_info) {
+  for (size_t i = 0; i < apps_info.size(); ++i) {
+    if (!apps_info[i].is_canceled) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+}   // namespace
+
+InstallStoppedWnd::InstallStoppedWnd(CMessageLoop* message_loop, HWND parent)
+    : message_loop_(message_loop),
+      parent_(parent) {
+  CORE_LOG(L3, (_T("[InstallStoppedWnd::InstallStoppedWnd]")));
+  ASSERT1(message_loop);
+  ASSERT1(::IsWindow(parent));
+}
+
+InstallStoppedWnd::~InstallStoppedWnd() {
+  CORE_LOG(L3, (_T("[InstallStoppedWnd::~InstallStoppedWnd]")));
+  if (IsWindow()) {
+    VERIFY1(SUCCEEDED(CloseWindow()));
+  }
+}
+
+// Enables the parent window and destroys this window.
+// Enabling the parent window before destroying this one causes the parent
+// window to get the focus and avoids a visible momentary lack of focus if we
+// instead call SetFocus for the parent after the window is destroyed.
+HRESULT InstallStoppedWnd::CloseWindow() {
+  ASSERT1(IsWindow());
+  VERIFY1(::EnableWindow(parent_, true));
+
+  return DestroyWindow() ? S_OK : HRESULTFromLastError();
+}
+
+// Disables the parent window.
+LRESULT InstallStoppedWnd::OnInitDialog(UINT,
+                                        WPARAM,
+                                        LPARAM,
+                                        BOOL& handled) {    // NOLINT
+  VERIFY1(!::EnableWindow(parent_, false));
+  VERIFY1(message_loop_->AddMessageFilter(this));
+  handled = true;
+  return 1;
+}
+
+// By letting the parent destroy this window, the parent to manage the entire
+// lifetime of this window and avoid creating a synchronization problem by
+// changing the value of IsInstallStoppedWindowPresent() during the middle of
+// one of the parent's methods.
+LRESULT InstallStoppedWnd::OnClickButton(WORD,
+                                         WORD id,
+                                         HWND,
+                                         BOOL& handled) {   // NOLINT
+  CORE_LOG(L3, (_T("[InstallStoppedWnd::OnClickButton]")));
+  ASSERT1(id == IDOK || id == IDCANCEL);
+  VERIFY1(::PostMessage(parent_, WM_INSTALL_STOPPED, id, 0));
+  handled = true;
+  return 0;
+}
+
+LRESULT InstallStoppedWnd::OnDestroy(UINT,
+                                     WPARAM,
+                                     LPARAM,
+                                     BOOL& handled) {  // NOLINT
+  VERIFY1(message_loop_->RemoveMessageFilter(this));
+  handled = true;
+  return 0;
+}
+
+ProgressWnd::ProgressWnd(CMessageLoop* message_loop, HWND parent)
+    : CompleteWnd(IDD_PROGRESS,
+                  ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS,
+                  message_loop,
+                  parent),
+      cur_state_(STATE_INIT),
+      events_sink_(NULL),
+      is_canceled_(false) {
+  CORE_LOG(L3, (_T("[ProgressWnd::ProgressWnd]")));
+}
+
+ProgressWnd::~ProgressWnd() {
+  CORE_LOG(L3, (_T("[ProgressWnd::~ProgressWnd]")));
+  ASSERT1(!IsWindow());
+  cur_state_ = STATE_END;
+}
+
+void ProgressWnd::SetEventSink(ProgressWndEvents* ev) {
+  events_sink_ = ev;
+  CompleteWnd::SetEventSink(events_sink_);
+}
+
+LRESULT ProgressWnd::OnInitDialog(UINT message,
+                                  WPARAM w_param,
+                                  LPARAM l_param,
+                                  BOOL& handled) {  // NOLINT
+  CORE_LOG(L3, (_T("[ProgressWnd::OnInitDialog]")));
+  UNREFERENCED_PARAMETER(message);
+  UNREFERENCED_PARAMETER(w_param);
+  UNREFERENCED_PARAMETER(l_param);
+  UNREFERENCED_PARAMETER(handled);
+
+  InitializeDialog();
+
+  pause_resume_text_.reset(new StaticEx);
+  pause_resume_text_->SubclassWindow(GetDlgItem(IDC_PAUSE_RESUME_TEXT));
+
+  CString state_text;
+  VERIFY1(state_text.LoadString(IDS_INITIALIZING));
+  VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), state_text));
+  VERIFY1(SUCCEEDED(SetMarqueeMode(true)));
+  VERIFY1(SUCCEEDED(ChangeControlState()));
+
+  metrics_timer_.reset(new HighresTimer);
+
+  return 1;  // Let the system set the focus.
+}
+
+// If closing is disabled, does not close the window.
+// If in a completion state, the window is closed.
+// Otherwise, the InstallStoppedWnd is displayed and the window is closed only
+// if the user decides to cancel.
+bool ProgressWnd::MaybeCloseWindow() {
+  if (!is_close_enabled()) {
+    return false;
+  }
+
+  if (cur_state_ != STATE_COMPLETE_SUCCESS &&
+      cur_state_ != STATE_COMPLETE_ERROR &&
+      cur_state_ != STATE_COMPLETE_RESTART_BROWSER &&
+      cur_state_ != STATE_COMPLETE_RESTART_ALL_BROWSERS &&
+      cur_state_ != STATE_COMPLETE_REBOOT) {
+    // The UI is not in final state: ask the user to proceed with closing it.
+    // A modal dialog opens and sends a message back to this window to
+    // communicate the user decision.
+    install_stopped_wnd_.reset(new InstallStoppedWnd(message_loop(), *this));
+    HWND hwnd = install_stopped_wnd_->Create(*this);
+    ASSERT1(hwnd);
+    if (hwnd) {
+      CString title;
+      VERIFY1(title.LoadString(IDS_INSTALLATION_STOPPED_WINDOW_TITLE));
+      VERIFY1(install_stopped_wnd_->SetWindowText(title));
+
+      CString button_text;
+      VERIFY1(button_text.LoadString(IDS_RESUME_INSTALLATION));
+      VERIFY1(::SetWindowText(
+          install_stopped_wnd_->GetDlgItem(IDOK), button_text));
+
+      VERIFY1(button_text.LoadString(IDS_CANCEL_INSTALLATION));
+      VERIFY1(::SetWindowText(
+          install_stopped_wnd_->GetDlgItem(IDCANCEL), button_text));
+
+      CString s;
+      s.FormatMessage(IDS_INSTALL_STOPPED, bundle_name());
+      VERIFY1(::SetWindowText(
+          install_stopped_wnd_->GetDlgItem(IDC_INSTALL_STOPPED_TEXT), s));
+
+      VERIFY1(install_stopped_wnd_->CenterWindow(*this));
+      VERIFY1(!install_stopped_wnd_->ShowWindow(SW_SHOWDEFAULT));
+      return false;
+    }
+  }
+
+  VERIFY1(SUCCEEDED(CloseWindow()));
+  return true;
+}
+
+LRESULT ProgressWnd::OnClickedButton(WORD notify_code,
+                                     WORD id,
+                                     HWND wnd_ctl,
+                                     BOOL& handled) {   // NOLINT
+  CORE_LOG(L3, (_T("[ProgressWnd::OnClickedButton]")));
+  ASSERT1(id == IDC_BUTTON1 || id == IDC_BUTTON2 || id == IDC_CLOSE);
+  ASSERT1(events_sink_);
+
+#pragma warning(push)
+// C4061: enumerator 'xxx' in switch of enum 'yyy' is not explicitly handled by
+// a case label.
+#pragma warning(disable : 4061)
+
+  switch (id) {
+    case IDC_BUTTON1:
+      // TODO(omaha): Consider doing something if the callbacks fail.
+      switch (cur_state_) {
+        case STATE_COMPLETE_RESTART_BROWSER:
+          ++metric_worker_ui_restart_browser_now_click;
+          VERIFY1(events_sink_->DoRestartBrowser(false, post_install_urls_));
+          break;
+        case STATE_COMPLETE_RESTART_ALL_BROWSERS:
+          ++metric_worker_ui_restart_all_browsers_now_click;
+          VERIFY1(events_sink_->DoRestartBrowser(true, post_install_urls_));
+          break;
+        case STATE_COMPLETE_REBOOT:
+          ++metric_worker_ui_reboot_now_click;
+          VERIFY1(events_sink_->DoReboot());
+          break;
+        default:
+          ASSERT1(false);
+      }
+      break;
+    case IDC_BUTTON2:
+      switch (cur_state_) {
+        case STATE_COMPLETE_RESTART_BROWSER:
+        case STATE_COMPLETE_RESTART_ALL_BROWSERS:
+        case STATE_COMPLETE_REBOOT:
+          break;
+        default:
+          ASSERT1(false);
+      }
+      break;
+    case IDC_CLOSE:
+      switch (cur_state_) {
+        case STATE_COMPLETE_SUCCESS:
+        case STATE_COMPLETE_ERROR:
+          return CompleteWnd::OnClickedButton(notify_code,
+                                              id,
+                                              wnd_ctl,
+                                              handled);
+          break;
+        default:
+          ASSERT1(false);
+      }
+      break;
+    default:
+      ASSERT1(false);
+  }
+#pragma warning(pop)
+
+  // TODO(omaha3): In closing the Window here, we assume that none of the above
+  // code does anything that might delay the UI response. This should be true
+  // since we won't actually be restarting browsers, etc. from the UI.
+  handled = true;
+  VERIFY1(SUCCEEDED(CloseWindow()));
+
+  return 0;
+}
+
+LRESULT ProgressWnd::OnInstallStopped(UINT msg,
+                                      WPARAM wparam,
+                                      LPARAM,
+                                      BOOL& handled) {  // NOLINT
+  CORE_LOG(L3, (_T("[ProgressWnd::OnInstallStopped]")));
+  UNREFERENCED_PARAMETER(msg);
+
+  install_stopped_wnd_.reset();
+
+  ASSERT1(msg == WM_INSTALL_STOPPED);
+  ASSERT1(wparam == IDOK || wparam == IDCANCEL);
+  // TODO(omaha): Swap the meaning of IDOK and IDCANCEL. IDCANCEL gets passed
+  // when the user hits the esc key. Successive esc presses result in the window
+  // disappearing. Instead, we would like the default (set in the .rc files) and
+  // esc key option to both resume. Changing this requires swapping all uses
+  // in this file as well as in the IDD_INSTALL_STOPPED definition.
+  // Maybe use different constants internally too since these values are used
+  // by different classes and ProgressWnd should not need to know how
+  // InstallStoppedWnd is implemented.
+  // It's possible this will also fix arrow key problem (http://b/1338787).
+  switch (wparam) {
+    case IDOK:
+      // TODO(omaha): Implement "Resume" here.
+      break;
+    case IDCANCEL:
+      HandleCancelRequest();
+      break;
+    default:
+      ASSERT1(false);
+      break;
+  }
+
+  handled = true;
+  return 0;
+}
+
+void ProgressWnd::HandleCancelRequest() {
+  CString s;
+  VERIFY1(s.LoadString(IDS_CANCELING));
+  VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s));
+
+  if (is_canceled_) {
+    return;
+  }
+  is_canceled_ = true;
+
+  // The user has decided to cancel.
+  metric_worker_ui_cancel_ms.AddSample(metrics_timer_->GetElapsedMs());
+  ++metric_worker_ui_cancels;
+
+  if (events_sink_) {
+    events_sink_->DoCancel();
+  }
+}
+
+void ProgressWnd::OnCheckingForUpdate() {
+  CORE_LOG(L3, (_T("[ProgressWnd::OnCheckingForUpdate]")));
+  ASSERT1(thread_id() == ::GetCurrentThreadId());
+  if (!IsWindow()) {
+    return;
+  }
+
+  cur_state_ = STATE_CHECKING_FOR_UPDATE;
+
+  CString s;
+  VERIFY1(s.LoadString(IDS_WAITING_TO_CONNECT));
+  VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s));
+
+  VERIFY1(SUCCEEDED(SetMarqueeMode(true)));
+  VERIFY1(SUCCEEDED(ChangeControlState()));
+}
+
+void ProgressWnd::OnUpdateAvailable(const CString& app_name,
+                                    const CString& version_string) {
+  CORE_LOG(L3, (_T("[ProgressWnd::OnUpdateAvailable][%s][%s]"),
+                app_name, version_string));
+  UNREFERENCED_PARAMETER(app_name);
+  UNREFERENCED_PARAMETER(version_string);
+
+  ASSERT1(thread_id() == ::GetCurrentThreadId());
+  if (!IsWindow()) {
+    return;
+  }
+}
+
+void ProgressWnd::OnWaitingToDownload(const CString& app_name) {
+  CORE_LOG(L3, (_T("[ProgressWnd::OnWaitingToDownload][%s]"), app_name));
+  ASSERT1(thread_id() == ::GetCurrentThreadId());
+  if (!IsWindow()) {
+    return;
+  }
+
+  cur_state_ = STATE_WAITING_TO_DOWNLOAD;
+
+  CString s;
+  s.FormatMessage(IDS_WAITING_TO_DOWNLOAD, app_name);
+  VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s));
+
+  VERIFY1(SUCCEEDED(SetMarqueeMode(true)));
+  VERIFY1(SUCCEEDED(ChangeControlState()));
+}
+
+// May be called repeatedly during download.
+void ProgressWnd::OnDownloading(const CString& app_name,
+                                int time_remaining_ms,
+                                int pos) {
+  CORE_LOG(L5, (_T("[ProgressWnd::OnDownloading][%s][remaining ms=%d][pos=%d]"),
+                app_name, time_remaining_ms, pos));
+  ASSERT1(thread_id() == ::GetCurrentThreadId());
+  if (!IsWindow()) {
+    return;
+  }
+
+  ASSERT1(0 <= pos && pos <= 100);
+
+  cur_state_ = STATE_DOWNLOADING;
+
+// This resource is not included in the resource files since it's not used.
+#if 0
+    VERIFY1(s.LoadString(IDS_PAUSE));
+    VERIFY1(::SetWindowText(GetDlgItem(IDC_PAUSE_RESUME_TEXT), s));
+#endif
+
+  CString s;
+
+  int time_remaining_sec = CeilingDivide(time_remaining_ms, kMsPerSec);
+  if (time_remaining_ms < 0) {
+    s.FormatMessage(IDS_WAITING_TO_DOWNLOAD, app_name);
+  } else if (time_remaining_ms == 0) {
+    s.FormatMessage(IDS_DOWNLOADING_COMPLETED, app_name);
+  } else if (time_remaining_sec < kSecPerMin) {
+    // Less than one minute remaining.
+    s.FormatMessage(IDS_DOWNLOADING_SHORT, app_name, time_remaining_sec);
+  } else if (time_remaining_sec < kSecondsPerHour) {
+    // Less than one hour remaining.
+    int time_remaining_minute = CeilingDivide(time_remaining_sec, kSecPerMin);
+    s.FormatMessage(IDS_DOWNLOADING_LONG, app_name, time_remaining_minute);
+  } else {
+    int time_remaining_hour = CeilingDivide(time_remaining_sec,
+                                            kSecondsPerHour);
+    s.FormatMessage(IDS_DOWNLOADING_VERY_LONG, app_name, time_remaining_hour);
+  }
+
+  VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s));
+  VERIFY1(SUCCEEDED(ChangeControlState()));
+
+  // When the network is connecting keep the marquee moving, otherwise
+  // the user has no indication something is still going on.
+  // TODO(omaha): when resuming an incomplete download this will not work.
+  VERIFY1(SUCCEEDED(SetMarqueeMode(pos == 0)));
+  ::SendMessage(GetDlgItem(IDC_PROGRESS), PBM_SETPOS, pos, 0);
+}
+
+void ProgressWnd::OnWaitingRetryDownload(const CString& app_name,
+                                         time64 next_retry_time) {
+  CORE_LOG(L5, (_T("[ProgressWnd::OnWaitingRetryDownload][%s][retry at:%llu]"),
+                app_name, next_retry_time));
+  ASSERT1(thread_id() == ::GetCurrentThreadId());
+  if (!IsWindow()) {
+    return;
+  }
+
+  time64 now = GetCurrent100NSTime();
+  if (now < next_retry_time) {
+    CString s;
+    int retry_time_in_sec =
+        static_cast<int>(CeilingDivide(next_retry_time - now, kSecsTo100ns));
+    s.FormatMessage(IDS_DOWNLOAD_RETRY, app_name, retry_time_in_sec);
+    VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s));
+    VERIFY1(SUCCEEDED(ChangeControlState()));
+  }
+}
+
+void ProgressWnd::OnWaitingToInstall(const CString& app_name,
+                                     bool* can_start_install) {
+  CORE_LOG(L3, (_T("[ProgressWnd::OnWaitingToInstall][%s]"), app_name));
+  ASSERT1(thread_id() == ::GetCurrentThreadId());
+  ASSERT1(can_start_install);
+  if (!IsWindow()) {
+    return;
+  }
+
+  if (STATE_WAITING_TO_INSTALL != cur_state_) {
+    cur_state_ = STATE_WAITING_TO_INSTALL;
+
+    CString s;
+    s.FormatMessage(IDS_WAITING_TO_INSTALL, app_name);
+    VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s));
+
+    VERIFY1(SUCCEEDED(SetMarqueeMode(true)));
+    VERIFY1(SUCCEEDED(ChangeControlState()));
+  }
+
+  // If we want to instead close the window and start install, call
+  // CloseInstallStoppedWindow() and return *can_start_install = true.
+  *can_start_install = !IsInstallStoppedWindowPresent();
+}
+
+// May be called repeatedly during install.
+void ProgressWnd::OnInstalling(const CString& app_name) {
+  CORE_LOG(L5, (_T("[ProgressWnd::OnInstalling][%s]"), app_name));
+  ASSERT1(thread_id() == ::GetCurrentThreadId());
+  if (!IsWindow()) {
+    return;
+  }
+
+  // TODO(omaha3): This can now occur because installs are not gated.
+  // ASSERT1(!IsInstallStoppedWindowPresent());
+
+  if (STATE_INSTALLING != cur_state_) {
+    cur_state_ = STATE_INSTALLING;
+
+    CString s;
+    s.FormatMessage(IDS_INSTALLING, app_name);
+    VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s));
+
+    VERIFY1(SUCCEEDED(SetMarqueeMode(true)));
+    VERIFY1(SUCCEEDED(ChangeControlState()));
+  }
+}
+
+// TODO(omaha): Should this message display the app name or bundle name? Is the
+// entire bundle paused?
+void ProgressWnd::OnPause() {
+  CORE_LOG(L3, (_T("[ProgressWnd::OnPause]")));
+  ASSERT(false, (_T("These strings are not in the .rc files.")));
+  ASSERT1(thread_id() == ::GetCurrentThreadId());
+  if (!IsWindow()) {
+    return;
+  }
+
+  cur_state_ = STATE_PAUSED;
+
+// These resources are not included in resource files since they are not used.
+#if 0
+  CString s;
+  s.FormatMessage(IDS_DOWNLOAD_PAUSED, bundle_name());
+  VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s));
+
+  VERIFY1(s.LoadString(IDS_RESUME));
+  VERIFY1(::SetWindowText(GetDlgItem(IDC_PAUSE_RESUME_TEXT), s));
+#endif
+
+  // TODO(omaha): implement time left.
+
+  VERIFY1(SUCCEEDED(ChangeControlState()));
+}
+
+void ProgressWnd::DeterminePostInstallUrls(const ObserverCompletionInfo& info) {
+  ASSERT1(post_install_urls_.empty());
+  post_install_urls_.clear();
+
+  for (size_t i = 0; i < info.apps_info.size(); ++i) {
+    const AppCompletionInfo& app_info = info.apps_info[i];
+    if (!app_info.post_install_url.IsEmpty() &&
+        (app_info.completion_code == COMPLETION_CODE_RESTART_ALL_BROWSERS ||
+         app_info.completion_code == COMPLETION_CODE_RESTART_BROWSER)) {
+      post_install_urls_.push_back(app_info.post_install_url);
+    }
+  }
+  ASSERT1(!post_install_urls_.empty());
+}
+
+// TODO(omaha): We can eliminate this function is we have a better UI that can
+// show compeltion status for each app in the bundle.
+//
+// Overall completion code is determined by apps' completion codes and bundle
+// completion code. If bundle installation fails or installation completed after
+// a cancel is attempted, returns bundle completion code.
+// Otherwise the app's completion code that has the greatest priority is
+// returned.
+CompletionCodes ProgressWnd::GetBundleOverallCompletionCode(
+    const ObserverCompletionInfo& info) const {
+  if (info.completion_code == COMPLETION_CODE_ERROR ||
+      info.completion_code == COMPLETION_CODE_INSTALL_FINISHED_BEFORE_CANCEL) {
+    return info.completion_code;
+  }
+
+  ASSERT1(info.completion_code == COMPLETION_CODE_SUCCESS);
+
+  CompletionCodes overall_completion_code =
+      kCompletionCodesActionPriority[0];
+  for (size_t i = 0; i < info.apps_info.size(); ++i) {
+    if (GetActionPriority(overall_completion_code) <
+        GetActionPriority(info.apps_info[i].completion_code)) {
+      overall_completion_code = info.apps_info[i].completion_code;
+    }
+  }
+
+  return overall_completion_code;
+}
+
+// TODO(omaha3): How should we display the restart browser and reboot messages
+// when multiple apps are being installed, some of which may have failed? Should
+// we use the app name or bundle name?
+void ProgressWnd::OnComplete(const ObserverCompletionInfo& observer_info) {
+  CORE_LOG(L3, (_T("[ProgressWnd::OnComplete][%s]"), observer_info.ToString()));
+  ASSERT1(thread_id() == ::GetCurrentThreadId());
+
+  if (!CompleteWnd::OnComplete()) {
+    return;
+  }
+
+  // Close the 'Install Stop' window if it is on the screen.
+  // TODO(omaha3): This had been before all main dialog UI. Make sure looks OK.
+  CloseInstallStoppedWindow();
+
+  // TODO(omaha3): Do we want to avoid launching commands during an interactive
+  // /ua update? If so, we'll need to handle that somehow. Using the observer
+  // handles the silent update and install cases as well as the OnDemand case.
+  bool launch_commands_succeeded = LaunchCmdLines(observer_info);
+
+  CString s;
+  CompletionCodes overall_completion_code =
+      GetBundleOverallCompletionCode(observer_info);
+  CORE_LOG(L3, (_T("[overall completion code: %d]"), overall_completion_code));
+  switch (overall_completion_code) {
+    case COMPLETION_CODE_SUCCESS:
+    case COMPLETION_CODE_LAUNCH_COMMAND:
+    case COMPLETION_CODE_INSTALL_FINISHED_BEFORE_CANCEL:
+      cur_state_ = STATE_COMPLETE_SUCCESS;
+
+      // TODO(omaha): Do not inherit from CompleteWnd once we have the new
+      // bundle-supporting UI. Among other things, calling
+      // DisplayCompletionDialog causes second call to OmahaWnd::OnComplete().
+      CompleteWnd::DisplayCompletionDialog(true,
+                                           observer_info.completion_text,
+                                           observer_info.help_url);
+      break;
+    case COMPLETION_CODE_ERROR:
+      // If all apps are canceled, no need to display any dialog.
+      if (AreAllAppsCanceled(observer_info.apps_info)) {
+        VERIFY1(SUCCEEDED(CloseWindow()));
+        return;
+      } else {
+        cur_state_ = STATE_COMPLETE_ERROR;
+        CompleteWnd::DisplayCompletionDialog(false,
+                                             observer_info.completion_text,
+                                             observer_info.help_url);
+      }
+      break;
+    case COMPLETION_CODE_RESTART_ALL_BROWSERS:
+      cur_state_ = STATE_COMPLETE_RESTART_ALL_BROWSERS;
+      VERIFY1(s.LoadString(IDS_RESTART_NOW));
+      VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON1), s));
+      VERIFY1(s.LoadString(IDS_RESTART_LATER));
+      VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON2), s));
+      s.FormatMessage(IDS_TEXT_RESTART_ALL_BROWSERS, bundle_name());
+      VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), s));
+      DeterminePostInstallUrls(observer_info);
+      ++metric_worker_ui_restart_all_browsers_buttons_displayed;
+      break;
+    case COMPLETION_CODE_RESTART_BROWSER:
+      cur_state_ = STATE_COMPLETE_RESTART_BROWSER;
+      VERIFY1(s.LoadString(IDS_RESTART_NOW));
+      VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON1), s));
+      VERIFY1(s.LoadString(IDS_RESTART_LATER));
+      VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON2), s));
+      s.FormatMessage(IDS_TEXT_RESTART_BROWSER, bundle_name());
+      VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), s));
+      DeterminePostInstallUrls(observer_info);
+      ++metric_worker_ui_restart_browser_buttons_displayed;
+      break;
+    case COMPLETION_CODE_REBOOT:
+      ASSERT(false, (_T("The button actions are not implemented.")));
+      cur_state_ = STATE_COMPLETE_REBOOT;
+      VERIFY1(s.LoadString(IDS_RESTART_NOW));
+      VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON1), s));
+      VERIFY1(s.LoadString(IDS_RESTART_LATER));
+      VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON2), s));
+      s.FormatMessage(IDS_TEXT_RESTART_COMPUTER, bundle_name());
+      VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), s));
+      ++metric_worker_ui_reboot_buttons_displayed;
+      break;
+    // TODO(omaha3): We may be able to eliminate these by having the caller
+    // specify the appropriate success text. That is the only difference from
+    // the COMPLETION_CODE_SUCCESS case. Alternatively, we can make a decision
+    // in this class based on, for example, whether the browser is supported.
+    case COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY:
+      cur_state_ = STATE_COMPLETE_SUCCESS;
+      s.FormatMessage(IDS_TEXT_RESTART_ALL_BROWSERS, bundle_name());
+      CompleteWnd::DisplayCompletionDialog(true, s, observer_info.help_url);
+      break;
+    case COMPLETION_CODE_REBOOT_NOTICE_ONLY:
+      cur_state_ = STATE_COMPLETE_SUCCESS;
+      s.FormatMessage(IDS_TEXT_RESTART_COMPUTER, bundle_name());
+      CompleteWnd::DisplayCompletionDialog(true, s, observer_info.help_url);
+      break;
+    case COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY:
+      cur_state_ = STATE_COMPLETE_SUCCESS;
+      s.FormatMessage(IDS_TEXT_RESTART_BROWSER, bundle_name());
+      CompleteWnd::DisplayCompletionDialog(true, s, observer_info.help_url);
+      break;
+    case COMPLETION_CODE_EXIT_SILENTLY_ON_LAUNCH_COMMAND:
+      cur_state_ = STATE_COMPLETE_SUCCESS;
+      if (launch_commands_succeeded) {
+        VERIFY1(SUCCEEDED(CloseWindow()));
+        return;
+      }
+
+      CompleteWnd::DisplayCompletionDialog(true,
+                                           observer_info.completion_text,
+                                           observer_info.help_url);
+      break;
+    case COMPLETION_CODE_EXIT_SILENTLY:
+      cur_state_ = STATE_COMPLETE_SUCCESS;
+      VERIFY1(SUCCEEDED(CloseWindow()));
+      return;
+    default:
+      ASSERT1(false);
+      break;
+  }
+
+  VERIFY1(SUCCEEDED(ChangeControlState()));
+}
+
+HRESULT ProgressWnd::LaunchCmdLine(const AppCompletionInfo& app_info) {
+  CORE_LOG(L3, (_T("[ProgressWnd::LaunchCmdLine][%s]"),
+                app_info.post_install_launch_command_line));
+  if (app_info.post_install_launch_command_line.IsEmpty()) {
+    return S_OK;
+  }
+
+  if (app_info.completion_code != COMPLETION_CODE_LAUNCH_COMMAND &&
+      app_info.completion_code !=
+          COMPLETION_CODE_EXIT_SILENTLY_ON_LAUNCH_COMMAND) {
+    CORE_LOG(LW, (_T("Launch command line [%s] is not empty but completion ")
+                  _T("code [%d] doesn't require a launch"),
+                  app_info.post_install_launch_command_line.GetString(),
+                  app_info.completion_code));
+    return S_OK;
+  }
+
+  ASSERT1(SUCCEEDED(app_info.error_code));
+  ASSERT1(!app_info.is_noupdate);
+
+  HRESULT hr = goopdate_utils::LaunchCmdLine(
+      is_machine(), app_info.post_install_launch_command_line);
+  if (FAILED(hr)) {
+    CORE_LOG(LE, (_T("[goopdate_utils::LaunchCmdLine failed][0x%x]"), hr));
+    return hr;
+  }
+
+  return S_OK;
+}
+
+bool ProgressWnd::LaunchCmdLines(const ObserverCompletionInfo& info) {
+  bool  result = true;
+
+  CORE_LOG(L3, (_T("[ProgressWnd::LaunchCmdLines]")));
+  for (size_t i = 0; i < info.apps_info.size(); ++i) {
+    const AppCompletionInfo& app_info = info.apps_info[i];
+    if (FAILED(app_info.error_code)) {
+      continue;
+    }
+    result &= SUCCEEDED(LaunchCmdLine(app_info));
+    VERIFY1(result);
+  }
+
+  return result;
+}
+
+HRESULT ProgressWnd::ChangeControlState() {
+  for (size_t i = 0; i != arraysize(ctls_); ++i) {
+    const ControlState& ctl_state = ctls_[i];
+    SetControlAttributes(ctl_state.id_, ctl_state.attr_[cur_state_]);
+  }
+  return S_OK;
+}
+
+HRESULT ProgressWnd::SetMarqueeMode(bool is_marquee) {
+  if (!SystemInfo::IsRunningOnXPOrLater()) {
+    // Marquee is not supported on OSes below XP.
+    return S_OK;
+  }
+
+  HWND progress_bar = GetDlgItem(IDC_PROGRESS);
+  if (!progress_bar) {
+    return GOOPDATE_E_UI_INTERNAL_ERROR;
+  }
+
+  LONG style = ::GetWindowLong(progress_bar, GWL_STYLE);
+  if (!style) {
+    return HRESULTFromLastError();
+  }
+
+  if (is_marquee) {
+    if (style & PBS_MARQUEE) {
+      return S_OK;
+    }
+
+    style |= PBS_MARQUEE;
+    style = ::SetWindowLong(progress_bar, GWL_STYLE, style);
+    if (!style) {
+      return HRESULTFromLastError();
+    }
+
+    bool result = ::SendMessage(progress_bar, PBM_SETMARQUEE,
+                                is_marquee, kMarqueeModeUpdatesMs) != 0;
+    return result ? S_OK : GOOPDATE_E_UI_INTERNAL_ERROR;
+  } else {
+    if (!(style & PBS_MARQUEE)) {
+      return S_OK;
+    }
+
+    style &= ~PBS_MARQUEE;
+    style = ::SetWindowLong(progress_bar, GWL_STYLE, style);
+    if (!style) {
+      return HRESULTFromLastError();
+    }
+    return S_OK;
+  }
+}
+
+bool ProgressWnd::IsInstallStoppedWindowPresent() {
+  return install_stopped_wnd_.get() && install_stopped_wnd_->IsWindow();
+}
+
+bool ProgressWnd::CloseInstallStoppedWindow() {
+  if (IsInstallStoppedWindowPresent()) {
+    VERIFY1(SUCCEEDED(install_stopped_wnd_->CloseWindow()));
+    install_stopped_wnd_.reset();
+    return true;
+  } else {
+    return false;
+  }
+}
+
+}  // namespace omaha
+
diff --git a/ui/progress_wnd.h b/ui/progress_wnd.h
new file mode 100644
index 0000000..145bc63
--- /dev/null
+++ b/ui/progress_wnd.h
@@ -0,0 +1,233 @@
+// Copyright 2008-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_UI_PROGRESS_WND_H_
+#define OMAHA_UI_PROGRESS_WND_H_
+
+#include <atlbase.h>
+#include <vector>
+#include "base/scoped_ptr.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/time.h"
+#include "omaha/base/wtl_atlapp_wrapper.h"
+#include "omaha/client/install_progress_observer.h"
+#include "omaha/ui/complete_wnd.h"
+#include "omaha/ui/uilib/static_ex.h"
+
+namespace omaha {
+
+class HighresTimer;
+
+// The message is used to communicate between InstallStoppedWnd and
+// ProgressWnd.
+const DWORD WM_INSTALL_STOPPED = WM_APP;
+
+class ProgressWndEvents : public CompleteWndEvents {
+ public:
+  // Restarts the browser(s) and returns whether the browser was successfully
+  // restarted.
+  // If restart_all_browsers is true, all known browsers will be shutdown.
+  // Otherwise only one type of browser will be shutdown. The concrete class
+  // is expected to know which browser to shutdown in that case.
+  // After that, only one type of browser will be restarted with the given URLs.
+  //
+  // Major browsers that support multi-tab differ how to open multiple URLs:
+  // IExplorer (8.0): opens each URL in a separate window.
+  // Firefox (3.6): by default if there is Firefox instance running, the URLs
+  //          will be opened in tabs in the same window. Otherwise each URL will
+  //          have its own window. Since we shutdown browser(s) first, it is
+  //          more likely to result in multiple windows.
+  // Chrome (5.0): opens each URL in a separate tab in the same window.
+  virtual bool DoRestartBrowser(bool restart_all_browsers,
+                                const std::vector<CString>& urls) = 0;
+
+  // Initiates a reboot and returns whether it was iniated successfully.
+  virtual bool DoReboot() = 0;
+
+  // Indicates that current operation is canceled.
+  virtual void DoCancel() = 0;
+};
+
+// Implements the "Installation Stopped" window. InstallStoppedWnd is
+// modal relative to its parent. When InstallStoppedWnd is closed it sends
+// a user message to its parent to notify which button the user has clicked on.
+class InstallStoppedWnd
+    : public CAxDialogImpl<InstallStoppedWnd>,
+      public CMessageFilter {
+  typedef CAxDialogImpl<InstallStoppedWnd> Base;
+ public:
+  static const int IDD = IDD_INSTALL_STOPPED;
+
+  InstallStoppedWnd(CMessageLoop* message_loop, HWND parent);
+  ~InstallStoppedWnd();
+
+  // Closes the window, handling transition back to the parent window.
+  HRESULT CloseWindow();
+
+  BOOL PreTranslateMessage(MSG* msg) {
+    return CWindow::IsDialogMessage(msg);
+  }
+
+  BEGIN_MSG_MAP(InstallStoppedWnd)
+    MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
+    MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
+    COMMAND_ID_HANDLER(IDOK, OnClickButton)
+    COMMAND_ID_HANDLER(IDCANCEL, OnClickButton)
+    CHAIN_MSG_MAP(Base)
+  END_MSG_MAP()
+
+ private:
+  // Message and command handlers.
+  LRESULT OnInitDialog(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);    // NOLINT
+  LRESULT OnClickButton(WORD notify_code, WORD id, HWND wnd_ctl, BOOL& handled);  // NOLINT
+  LRESULT OnDestroy(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);       // NOLINT
+
+  CMessageLoop* message_loop_;
+  HWND parent_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(InstallStoppedWnd);
+};
+
+
+// Implements the UI progress window.
+class ProgressWnd
+    : public CompleteWnd,
+      public InstallProgressObserver {
+ public:
+  ProgressWnd(CMessageLoop* message_loop, HWND parent);
+  virtual ~ProgressWnd();
+
+  void SetEventSink(ProgressWndEvents* ev);
+
+  // InstallProgressObserver methods.
+  // TODO(omaha3): Update this comment.
+  // These methods are called by the job to transition the UI from
+  // one state to another. The methods are always executed by the thread
+  // that created this window.
+  virtual void OnCheckingForUpdate();
+  virtual void OnUpdateAvailable(const CString& app_name,
+                                 const CString& version_string);
+  virtual void OnWaitingToDownload(const CString& app_name);
+  virtual void OnDownloading(const CString& app_name,
+                             int time_remaining_ms,
+                             int pos);
+  virtual void OnWaitingRetryDownload(const CString& app_name,
+                                      time64 next_retry_time);
+  virtual void OnWaitingToInstall(const CString& app_name,
+                                  bool* can_start_install);
+  virtual void OnInstalling(const CString& app_name);
+  virtual void OnPause();
+  virtual void OnComplete(const ObserverCompletionInfo& observer_info);
+
+  BEGIN_MSG_MAP(ProgressWnd)
+    MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
+    MESSAGE_HANDLER(WM_INSTALL_STOPPED, OnInstallStopped)
+    COMMAND_HANDLER(IDC_BUTTON1, BN_CLICKED, OnClickedButton)
+    COMMAND_HANDLER(IDC_BUTTON2, BN_CLICKED, OnClickedButton)
+    COMMAND_HANDLER(IDC_CLOSE,   BN_CLICKED, OnClickedButton)
+    CHAIN_MSG_MAP(CompleteWnd)
+  END_MSG_MAP()
+
+ private:
+  // Message and command handlers.
+  LRESULT OnInitDialog(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);  // NOLINT
+  LRESULT OnInstallStopped(UINT msg,
+                           WPARAM wparam,
+                           LPARAM lparam,
+                           BOOL& handled);  // NOLINT
+  LRESULT OnClickedButton(WORD notify_code,
+                          WORD id,
+                          HWND wnd_ctl,
+                          BOOL& handled);   // NOLINT
+
+  // Handles requests to close the window. Returns true if the window is closed.
+  virtual bool MaybeCloseWindow();
+
+  // Helpers.
+  HRESULT LaunchCmdLine(const AppCompletionInfo& app_info);
+  bool LaunchCmdLines(const ObserverCompletionInfo& info);
+  HRESULT ChangeControlState();
+  HRESULT SetMarqueeMode(bool is_marquee);
+
+  bool IsInstallStoppedWindowPresent();
+
+  void HandleCancelRequest();
+
+  // Closes the Installation Stopped window if present. Returns true if closed.
+  bool CloseInstallStoppedWindow();
+
+  void DeterminePostInstallUrls(const ObserverCompletionInfo& info);
+  CompletionCodes GetBundleOverallCompletionCode(
+      const ObserverCompletionInfo& info) const;
+
+  // TODO(omaha3): These states are used to control the UI elements. Otherwise,
+  // we could have just a single "complete" state and track restart/reboot state
+  // separately.
+  // The states are used as indexes in zero-based arrays so they should
+  // start at 0.
+  enum States {
+    STATE_INIT = 0,
+    STATE_CHECKING_FOR_UPDATE,
+    STATE_WAITING_TO_DOWNLOAD,
+    STATE_DOWNLOADING,
+    STATE_WAITING_TO_INSTALL,
+    STATE_INSTALLING,
+    STATE_PAUSED,
+    STATE_COMPLETE_SUCCESS,
+    STATE_COMPLETE_ERROR,
+    // TODO(omaha): Collapse these two into one state. Controls are the same.
+    // Add another variable to remember what to do in this state.
+    STATE_COMPLETE_RESTART_BROWSER,
+    STATE_COMPLETE_RESTART_ALL_BROWSERS,
+    STATE_COMPLETE_REBOOT,
+    STATE_END,
+  };
+
+  scoped_ptr<HighresTimer> metrics_timer_;
+
+  // Due to a repaint issue in StaticEx we prefer to manage their lifetime
+  // very aggressively so we contain them by reference instead of value.
+  scoped_ptr<StaticEx> pause_resume_text_;
+
+  States cur_state_;
+
+  scoped_ptr<InstallStoppedWnd> install_stopped_wnd_;
+
+  ProgressWndEvents* events_sink_;
+  std::vector<CString> post_install_urls_;
+  bool is_canceled_;
+
+#pragma warning(disable : 4510 4610)
+// C4510: default constructor could not be generated
+// C4610: struct can never be instantiated - user defined constructor required
+  struct ControlState {
+    const int id_;
+    const ControlAttributes attr_[ProgressWnd::STATE_END + 1];
+  };
+#pragma warning(default : 4510 4610)
+
+  static const ControlState ctls_[];
+
+  // The speed by which the progress bar moves in marquee mode.
+  static const int kMarqueeModeUpdatesMs = 75;
+
+  friend class UITest;
+  DISALLOW_EVIL_CONSTRUCTORS(ProgressWnd);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_UI_PROGRESS_WND_H_
+
diff --git a/ui/progress_wnd_unittest.cc b/ui/progress_wnd_unittest.cc
new file mode 100644
index 0000000..ed3f1d2
--- /dev/null
+++ b/ui/progress_wnd_unittest.cc
@@ -0,0 +1,318 @@
+// Copyright 2007-2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// This unit test is driving the UI through its states so that we can
+// visually inspect all the controls are there in their right state and
+// position. To go from state to state, simply close the window on the screen.
+//
+// The unit test is useful for debugging UI states so different tests are
+// enabled/disabled at compile time, depending what needs to be tested.
+
+#include <windows.h>
+#include "omaha/base/utils.h"
+#include "omaha/client/install_progress_observer.h"
+#include "omaha/testing/unit_test.h"
+#include "omaha/ui/ui.h"
+#include "omaha/ui/progress_wnd.h"
+
+namespace omaha {
+
+class UITest : public testing::Test,
+               public ProgressWndEvents,
+               public WaitCallbackInterface {
+ protected:
+  UITest() : progress_wnd_(&progress_wnd_message_loop_, NULL) {
+  }
+
+  static void SetUpTestCase() {
+    message_loop_.set_message_handler(&message_handler_);
+    reset(ev_, ::CreateEvent(NULL, false, false, NULL));
+  }
+
+  virtual void SetUp() {
+    ASSERT_TRUE(::ResetEvent(get(ev_)));
+    ASSERT_TRUE(message_loop_.RegisterWaitForSingleObject(get(ev_), this));
+    /*
+    progress_wnd_.SetEventSink(this);
+    progress_wnd_.set_bundle_name(_T("FooBar"));
+    EXPECT_SUCCEEDED(progress_wnd_.Initialize());
+    EXPECT_TRUE(progress_wnd_.CenterWindow(NULL));
+    progress_wnd_.SetVisible(true);
+    */
+  }
+
+  virtual void TearDown() {
+    message_loop_.UnregisterWait(get(ev_));
+  }
+
+  //
+  // ProgressWndEvents
+  //
+  virtual void DoPause() {
+  }
+
+  virtual void DoResume() {
+  }
+
+  virtual void DoClose() {
+    ASSERT_TRUE(::SetEvent(get(ev_)));
+  }
+
+  virtual void DoExit() {
+    ASSERT_TRUE(::SetEvent(get(ev_)));
+  }
+
+  virtual void DoCancel() {
+    ASSERT_TRUE(::SetEvent(get(ev_)));
+  }
+
+  virtual bool DoRestartBrowser(bool restart_all_browsers,
+                                const std::vector<CString>& urls) {
+    UNREFERENCED_PARAMETER(restart_all_browsers);
+    UNREFERENCED_PARAMETER(urls);
+
+    EXPECT_TRUE(::SetEvent(get(ev_)));
+    return true;
+  }
+
+  virtual bool DoReboot() {
+    EXPECT_TRUE(::SetEvent(get(ev_)));
+    return true;
+  }
+
+  virtual bool DoLaunchBrowser(const CString&) {
+    return true;
+  }
+
+  //
+  // WaitCallbackInterface
+  //
+  virtual bool HandleSignaled(HANDLE) {
+    // Makes the message pump stop.
+    return false;
+  }
+
+  void FormatWindowTitle(const TCHAR* text) {
+    CString title;
+    progress_wnd_.GetWindowText(CStrBuf(title, 256), 256);
+    CString new_title;
+    new_title.Format(_T("%s - %s"), title, text);
+    progress_wnd_.SetWindowText(new_title);
+  }
+
+  CompletionCodes GetBundleOverallCompletionCode(
+      const ObserverCompletionInfo& observer_info) {
+    return progress_wnd_.GetBundleOverallCompletionCode(observer_info);
+  }
+
+  static BasicMessageHandler message_handler_;
+  static MessageLoopWithWait message_loop_;
+  ProgressWnd progress_wnd_;
+  CMessageLoop progress_wnd_message_loop_;
+  static scoped_event ev_;
+};
+
+BasicMessageHandler UITest::message_handler_;
+MessageLoopWithWait UITest::message_loop_;
+scoped_event UITest::ev_;
+
+const CompletionCodes kCompletionCodesPriority[] = {
+  COMPLETION_CODE_EXIT_SILENTLY,
+  COMPLETION_CODE_EXIT_SILENTLY_ON_LAUNCH_COMMAND,
+  COMPLETION_CODE_SUCCESS,
+  COMPLETION_CODE_LAUNCH_COMMAND,
+  COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY,
+  COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY,
+  COMPLETION_CODE_RESTART_BROWSER,
+  COMPLETION_CODE_RESTART_ALL_BROWSERS,
+  COMPLETION_CODE_REBOOT_NOTICE_ONLY,
+  COMPLETION_CODE_REBOOT,
+  COMPLETION_CODE_ERROR,
+};
+
+TEST_F(UITest, GetBundleOverallCompletionCode_BundleFailed) {
+  ObserverCompletionInfo observer_info(COMPLETION_CODE_ERROR);
+  AppCompletionInfo app_info;
+
+  for (int i = 0; i < arraysize(kCompletionCodesPriority); ++i) {
+    app_info.completion_code = kCompletionCodesPriority[i];
+    observer_info.apps_info.push_back(app_info);
+  }
+
+  EXPECT_EQ(COMPLETION_CODE_ERROR,
+            GetBundleOverallCompletionCode(observer_info));
+}
+
+TEST_F(UITest, GetBundleOverallCompletionCode_BundleSucceeded) {
+  for (int i = 0; i < arraysize(kCompletionCodesPriority); ++i) {
+    ObserverCompletionInfo observer_info(COMPLETION_CODE_SUCCESS);
+    AppCompletionInfo app_info;
+
+    for (int j = 0; j <= i; ++j) {
+      app_info.completion_code = kCompletionCodesPriority[i];
+      observer_info.apps_info.push_back(app_info);
+    }
+
+    EXPECT_EQ(kCompletionCodesPriority[i],
+              GetBundleOverallCompletionCode(observer_info));
+  }
+}
+
+/*
+TEST_F(UITest, Initialize) {
+  FormatWindowTitle(_T("Initialize"));
+  message_loop_.Process();
+}
+
+TEST_F(UITest, OnWaitingToDownload) {
+  FormatWindowTitle(_T("Waiting to download"));
+  progress_wnd_.OnWaitingToDownload();
+  message_loop_.Process();
+}
+
+TEST_F(UITest, OnDownloading1) {
+  FormatWindowTitle(_T("Downloading"));
+  progress_wnd_.OnDownloading(10000, 0);
+  message_loop_.Process();
+}
+
+TEST_F(UITest, OnDownloading2) {
+  FormatWindowTitle(_T("Downloading"));
+  progress_wnd_.OnDownloading(5000, 50);
+  message_loop_.Process();
+}
+
+TEST_F(UITest, OnDownloading3) {
+  FormatWindowTitle(_T("Downloading"));
+  progress_wnd_.OnDownloading(0, 100);
+  message_loop_.Process();
+}
+
+TEST_F(UITest, OnWaitingToInstall) {
+  FormatWindowTitle(_T("Waiting to install"));
+  progress_wnd_.OnWaitingToInstall();
+  message_loop_.Process();
+}
+
+TEST_F(UITest, OnInstall) {
+  FormatWindowTitle(_T("Installing"));
+  progress_wnd_.OnInstalling();
+  message_loop_.Process();
+}
+
+TEST_F(UITest, OnPause) {
+  FormatWindowTitle(_T("Paused"));
+  progress_wnd_.OnPause();
+  message_loop_.Process();
+}
+
+TEST_F(UITest, OnCompleteSuccess) {
+  FormatWindowTitle(_T("Complete success"));
+  progress_wnd_.OnComplete(
+      COMPLETION_CODE_SUCCESS,
+      _T("Thanks for installing Gears. For more information on using ")
+      _T("Gears visit the ")
+      _T("<a=http://www.google.com/gears/>Gears</a> web site."),
+      0);
+  message_loop_.Process();
+}
+
+TEST_F(UITest, OnCompleteError) {
+  FormatWindowTitle(_T("Complete error"));
+  progress_wnd_.OnComplete(
+      COMPLETION_CODE_ERROR,
+      _T("An error occured while installing Gears: an existing copy of ")
+      _T("Gears is currently running. Please exit the software and ")
+      _T("retry installation. For more information visit the ")
+      _T("<a=http://www.google.com/gears/>Gears</a> web site."),
+      11);
+  message_loop_.Process();
+}
+
+TEST_F(UITest, OnCompleteRestartAllBrowsers) {
+  FormatWindowTitle(_T("Restart browsers"));
+  progress_wnd_.OnComplete(COMPLETION_CODE_RESTART_ALL_BROWSERS, NULL, 0);
+  message_loop_.Process();
+}
+
+TEST_F(UITest, OnCompleteReboot) {
+  FormatWindowTitle(_T("Reboot"));
+  progress_wnd_.OnComplete(COMPLETION_CODE_REBOOT, NULL, 0);
+  message_loop_.Process();
+}
+
+TEST_F(UITest, OnCompleteRestartBrowser) {
+  FormatWindowTitle(_T("Restart browser"));
+  progress_wnd_.OnComplete(COMPLETION_CODE_RESTART_BROWSER, NULL, 0);
+  message_loop_.Process();
+}
+
+TEST_F(UITest, OnCompleteRestartAllBrowsersNoticeOnly) {
+  FormatWindowTitle(_T("Restart browsers"));
+  progress_wnd_.OnComplete(COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY,
+                           NULL,
+                           0);
+  message_loop_.Process();
+}
+
+TEST_F(UITest, OnCompleteRebootNoticeOnly) {
+  FormatWindowTitle(_T("Reboot"));
+  progress_wnd_.OnComplete(COMPLETION_CODE_REBOOT_NOTICE_ONLY, NULL, 0);
+  message_loop_.Process();
+}
+
+TEST_F(UITest, OnCompleteRestartBrowserNoticeOnly) {
+  FormatWindowTitle(_T("Restart browser"));
+  progress_wnd_.OnComplete(COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY,
+                           NULL,
+                           0);
+  message_loop_.Process();
+}
+
+// Test the OnComplete can be called multiple times.
+TEST_F(UITest, OnMultipleCompletes) {
+  FormatWindowTitle(_T("Complete success"));
+  progress_wnd_.OnComplete(
+      COMPLETION_CODE_SUCCESS,
+      _T("Thanks for installing Gears. For more information on using ")
+      _T("Gears visit the ")
+      _T("<a=http://www.google.com/gears/>Gears</a> web site."),
+      0);
+
+  FormatWindowTitle(_T("Complete error"));
+  progress_wnd_.OnComplete(
+      COMPLETION_CODE_ERROR,
+      _T("An error occured while installing Gears: an existing copy of ")
+      _T("Gears is currently running. Please exit the software and ")
+      _T("retry installation. For more information visit the ")
+      _T("<a=http://www.google.com/gears/>Gears</a> web site."),
+      0);
+
+  progress_wnd_.OnComplete(
+      COMPLETION_CODE_ERROR,
+      _T("An error occured while installing Gears: an existing copy of ")
+      _T("Gears is currently running. Please exit the software and ")
+      _T("retry installation. For more information visit the ")
+      _T("<a=http://www.google.com/gears/>Gears</a> web site."),
+      0);
+
+  FormatWindowTitle(_T("Restart browsers"));
+  progress_wnd_.OnComplete(COMPLETION_CODE_RESTART_ALL_BROWSERS, NULL, 0);
+  message_loop_.Process();
+}
+*/
+
+}   // namespace omaha
+
diff --git a/ui/scoped_gdi.h b/ui/scoped_gdi.h
new file mode 100644
index 0000000..bdceeed
--- /dev/null
+++ b/ui/scoped_gdi.h
@@ -0,0 +1,101 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// The scoped_* classes in this file help to make GDI settings easier. The
+// constructors of these classes sets/selects provided new value into the
+// context (HDC) and stores the original value in a class member. The
+// destructors restore the context to its original state by setting the stored
+// value back.
+//
+// Example usage:
+// {
+//   // Selects the font into hdc.
+//   scoped_select_object select_font(hdc, ::GetStockObject(DEFAULT_GUI_FONT));
+//
+//   // Changes the bk mode in hdc.
+//   scoped_set_bk_mode set_bk_mode(hdc, TRANSPARENT);
+//
+//   // Changes the bk color in hdc.
+//   scoped_set_bk_color set_bk_color(hdc, kBackgroundColor);
+//
+//   /* Do painting operations with hdc here */
+//   ...
+//
+// }  // scoped_* goes out of scope, restoring hdc back to its original state.
+//
+
+#ifndef OMAHA_UI_SCOPED_GDI_H_
+#define OMAHA_UI_SCOPED_GDI_H_
+
+#include <wingdi.h>
+#include "omaha/base/scoped_any.h"
+
+namespace omaha {
+
+template<typename C, typename T, class set_policy, class invalid_type>
+class scoped_context_set {
+ public:
+  scoped_context_set(C context, T new_value) : context_(context) {
+    original_value_ = set_policy::set(context, new_value);
+  }
+
+  ~scoped_context_set() {
+    invalid_type invalid_value;
+    if (original_value_ != invalid_value) {
+      set_policy::set(context_, original_value_);
+    }
+  }
+
+ private:
+  T original_value_;
+  C context_;
+};
+
+template<typename Fn, Fn Pfn>
+class set_function {
+ public:
+  template<typename C, typename T>
+  static T set(C context, T value) {
+    return Pfn(context, value);
+  }
+};
+
+typedef set_function<HGDIOBJ (__stdcall *)(HDC hdc, HGDIOBJ gdiobj),  // NOLINT
+    ::SelectObject> select_object;
+typedef set_function<int (__stdcall *)(HDC hdc, int mode),            // NOLINT
+    ::SetBkMode> set_bk_mode;
+typedef set_function<COLORREF (__stdcall *)(HDC hdc, COLORREF mode),  // NOLINT
+    ::SetBkColor> set_bk_color;
+
+typedef value_const<HGDIOBJ, NULL> invalid_gdi_obj_value;
+typedef value_const<int, 0> invalid_bk_mode_value;
+typedef value_const<COLORREF, CLR_INVALID> invalid_bk_color_value;
+
+typedef scoped_context_set<HDC,
+                           HGDIOBJ,
+                           select_object,
+                           invalid_gdi_obj_value> ScopedSelectObject;
+typedef scoped_context_set<HDC,
+                           int,
+                           set_bk_mode,
+                           invalid_bk_mode_value> ScopedSetBkMode;
+typedef scoped_context_set<HDC,
+                           COLORREF,
+                           set_bk_color,
+                           invalid_bk_color_value> ScopedSetBkColor;
+
+}  // namespace omaha
+
+#endif  // OMAHA_UI_SCOPED_GDI_H_
diff --git a/ui/splash_screen.cc b/ui/splash_screen.cc
new file mode 100644
index 0000000..e106857
--- /dev/null
+++ b/ui/splash_screen.cc
@@ -0,0 +1,285 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/ui/splash_screen.h"
+#include "base/basictypes.h"
+#include "omaha/base/app_util.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/smart_handle.h"
+#include "omaha/base/utils.h"
+#include "omaha/base/window_utils.h"
+#include "omaha/client/client_utils.h"
+#include "omaha/client/resource.h"
+#include "omaha/common/lang.h"
+#include "omaha/google_update/resource.h"   // For the IDI_APP
+#include "omaha/ui/scoped_gdi.h"
+
+namespace {
+
+const int kClosingTimerID = 1;
+// Frequency that the window changes alpah blending value during fading stage.
+const int kTimerInterval = 100;
+
+// Alpha blending values for the fading effect.
+const int kDefaultAlphaScale = 100;
+const int kAlphaScales[] = { 0, 30, 47, 62, 75, 85, 93, kDefaultAlphaScale };
+
+uint8 AlphaScaleToAlphaValue(int alpha_scale) {
+  ASSERT1(alpha_scale >= 0 && alpha_scale <= 100);
+  return static_cast<uint8>(alpha_scale * 255 / 100);
+}
+
+}  // namespace
+
+namespace omaha {
+
+SplashScreen::SplashScreen(const CString& bundle_name)
+    : IDD(IDD_PROGRESS),
+      alpha_index_(0),
+      timer_created_(false) {
+  CORE_LOG(L3, (_T("[SplashScreen::SplashScreen]")));
+  caption_ = client_utils::GetInstallerDisplayName(bundle_name);
+  text_.FormatMessage(IDS_SPLASH_SCREEN_MESSAGE, caption_);
+
+  SwitchToState(STATE_CREATED);
+}
+
+SplashScreen::~SplashScreen() {
+  CORE_LOG(L3, (_T("[SplashScreen::~SplashScreen]")));
+
+  const int kWaitTimeoutInMillisecond = 60000;
+
+  // Before the object goes out of scope, waits the thread to exit to avoid
+  // it accessing the object after that.
+  if (thread_.Running() && !thread_.WaitTillExit(kWaitTimeoutInMillisecond)) {
+    CORE_LOG(LW, (_T("[SplashScreen: thread failed to exit gracefully]")));
+    return;
+  }
+
+  ASSERT1(state_ == STATE_CREATED || state_ == STATE_CLOSED);
+}
+
+void SplashScreen::Show() {
+  AutoSync get_lock(lock_);
+
+  if (state_ == STATE_CREATED) {
+    thread_.Start(this);
+  } else {
+    ASSERT1(false);
+  }
+}
+
+void SplashScreen::Dismiss() {
+  AutoSync get_lock(lock_);
+
+  switch (state_) {
+    case STATE_CREATED:
+      SwitchToState(STATE_CLOSED);
+      break;
+
+    case STATE_SHOW_NORMAL:
+      SwitchToState(STATE_FADING);
+      break;
+
+    case STATE_CLOSED:
+    case STATE_FADING:
+    case STATE_INITIALIZED:
+      break;
+
+    default:
+      ASSERT1(false);
+      break;
+  }
+}
+
+HRESULT SplashScreen::Initialize() {
+  CORE_LOG(L3, (_T("[SplashScreen::Initialize]")));
+
+  ASSERT1(!IsWindow());
+  ASSERT1(state_ == STATE_CREATED);
+
+  if (!Create(NULL)) {
+    return GOOPDATE_E_UI_INTERNAL_ERROR;
+  }
+
+  VERIFY1(SetWindowText(caption_));
+
+  EnableSystemButtons(false);
+  GetDlgItem(IDC_IMAGE).ShowWindow(SW_HIDE);
+
+  CWindow text_wnd = GetDlgItem(IDC_INSTALLER_STATE_TEXT);
+  text_wnd.ShowWindow(SW_SHOWNORMAL);
+  text_wnd.SetWindowText(text_);
+
+  InitProgressBar();
+
+  ::SetLayeredWindowAttributes(
+      m_hWnd,
+      0,
+      AlphaScaleToAlphaValue(kDefaultAlphaScale),
+      LWA_ALPHA);
+
+  VERIFY1(CenterWindow(NULL));
+  HRESULT hr = WindowUtils::SetWindowIcon(m_hWnd, IDI_APP, address(hicon_));
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[SetWindowIcon failed][0x%08x]"), hr));
+  }
+  SwitchToState(STATE_INITIALIZED);
+  return S_OK;
+}
+
+void SplashScreen::EnableSystemButtons(bool enable) {
+  const LONG kSysStyleMask = WS_MINIMIZEBOX | WS_SYSMENU | WS_MAXIMIZEBOX;
+
+  if (enable) {
+    SetWindowLong(GWL_STYLE, GetWindowLong(GWL_STYLE) | kSysStyleMask);
+  } else {
+    SetWindowLong(GWL_STYLE, GetWindowLong(GWL_STYLE) & ~kSysStyleMask);
+  }
+}
+
+void SplashScreen::InitProgressBar() {
+  const LONG kStyle = WS_CHILD | WS_VISIBLE | PBS_MARQUEE | PBS_SMOOTH;
+
+  CWindow progress_bar = GetDlgItem(IDC_PROGRESS);
+  LONG style = progress_bar.GetWindowLong(GWL_STYLE) | kStyle;
+  progress_bar.SetWindowLong(GWL_STYLE, style);
+  progress_bar.SendMessage(PBM_SETMARQUEE, TRUE, 60);
+}
+
+LRESULT SplashScreen::OnTimer(UINT message,
+                              WPARAM wparam,
+                              LPARAM lparam,
+                              BOOL& handled) {
+  UNREFERENCED_PARAMETER(message);
+  UNREFERENCED_PARAMETER(wparam);
+  UNREFERENCED_PARAMETER(lparam);
+
+  ASSERT1(state_ == STATE_FADING);
+  ASSERT1(alpha_index_ > 0);
+  if (--alpha_index_) {
+    ::SetLayeredWindowAttributes(
+        m_hWnd,
+        0,
+        AlphaScaleToAlphaValue(kAlphaScales[alpha_index_]),
+        LWA_ALPHA);
+  } else {
+    Close();
+  }
+
+  handled = TRUE;
+  return 0;
+}
+
+LRESULT SplashScreen::OnClose(UINT message,
+                              WPARAM wparam,
+                              LPARAM lparam,
+                              BOOL& handled) {
+  UNREFERENCED_PARAMETER(message);
+  UNREFERENCED_PARAMETER(wparam);
+  UNREFERENCED_PARAMETER(lparam);
+
+  DestroyWindow();
+  handled = TRUE;
+  return 0;
+}
+
+LRESULT SplashScreen::OnDestroy(UINT message,
+                                WPARAM wparam,
+                                LPARAM lparam,
+                                BOOL& handled) {
+  UNREFERENCED_PARAMETER(message);
+  UNREFERENCED_PARAMETER(wparam);
+  UNREFERENCED_PARAMETER(lparam);
+
+  if (timer_created_) {
+    ASSERT1(IsWindow());
+    KillTimer(kClosingTimerID);
+  }
+
+  ::PostQuitMessage(0);
+
+  handled = TRUE;
+  return 0;
+}
+
+void SplashScreen::SwitchToState(WindowState new_state) {
+  AutoSync get_lock(lock_);
+
+  state_ = new_state;
+  switch (new_state) {
+    case STATE_CREATED:
+    case STATE_INITIALIZED:
+      break;
+    case STATE_SHOW_NORMAL:
+      alpha_index_ = arraysize(kAlphaScales) - 1;
+      break;
+    case STATE_FADING:
+      ASSERT1(IsWindow());
+      timer_created_ = (SetTimer(kClosingTimerID, kTimerInterval, NULL) != 0);
+      if (!timer_created_) {
+        CORE_LOG(LW,
+                 (_T("[SetTimer failed, closing window directly.][0x%08x]"),
+                  HRESULTFromLastError()));
+        Close();
+      }
+      break;
+    case STATE_CLOSED:
+      break;
+    default:
+      ASSERT1(false);
+      break;
+  }
+}
+
+void SplashScreen::Run() {
+  {
+    AutoSync get_lock(lock_);
+
+    if (state_ != STATE_CREATED) {
+      return;
+    }
+
+    // Initialize() has to be called in this thread so that it is the owner of
+    // the window and window messages can be correctly routed by the message
+    // loop.
+    if (FAILED(Initialize())) {
+      return;
+    }
+
+    ASSERT1(IsWindow());
+    ShowWindow(SW_SHOWNORMAL);
+    SwitchToState(STATE_SHOW_NORMAL);
+  }
+
+  CMessageLoop message_loop;
+  message_loop.Run();
+
+  SwitchToState(STATE_CLOSED);
+}
+
+void SplashScreen::Close() {
+  AutoSync get_lock(lock_);
+
+  if (state_ != STATE_CLOSED && IsWindow()) {
+    PostMessage(WM_CLOSE, 0, 0);
+  }
+}
+
+}  // namespace omaha
diff --git a/ui/splash_screen.h b/ui/splash_screen.h
new file mode 100644
index 0000000..564d56d
--- /dev/null
+++ b/ui/splash_screen.h
@@ -0,0 +1,119 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_UI_SPLASH_SCREEN_H_
+#define OMAHA_UI_SPLASH_SCREEN_H_
+
+#include "base/scoped_ptr.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/synchronized.h"
+#include "omaha/base/thread.h"
+#include "omaha/base/wtl_atlapp_wrapper.h"
+
+namespace omaha {
+
+// Displays a splash screen while lengthy operations occur. Before the lengthy
+// operation, the caller creates an instance of this class and calls Show()
+// function on it. The Show() function then creates a thread to display the
+// splash screen. The caller must call Dismiss() function to hide and destroy
+// the splash screen once the lengthy operation is done.
+class SplashScreen
+    : public CAxDialogImpl<SplashScreen>,
+      public Runnable {
+ public:
+  explicit SplashScreen(const CString& bundle_name);
+
+  // The desctructor waits up to 60 seconds for the message loop thread to exit
+  // if it is running.
+  virtual ~SplashScreen();
+
+  // The dialog resource ID as required by CAxDialogImpl.
+  const int IDD;
+
+  // Spawns a thread which creates and shows the window.
+  void Show();
+
+  // Closes the window gradually if the window is visible.
+  void Dismiss();
+
+  // Runnable interface method.
+  virtual void Run();
+
+  BEGIN_MSG_MAP(SplashScreen)
+    MESSAGE_HANDLER(WM_TIMER, OnTimer)
+    MESSAGE_HANDLER(WM_CLOSE, OnClose)
+    MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
+  END_MSG_MAP()
+
+ private:
+  friend class SplashScreenTest;
+
+  // States that help to determine the splash screen life cycle and
+  // allowed operations at each stage.
+  enum WindowState {
+    STATE_CREATED,
+    STATE_INITIALIZED,
+    STATE_SHOW_NORMAL,
+    STATE_FADING,
+    STATE_CLOSED,
+  };
+
+  // Creates the window and adjusts its size and apperance.
+  HRESULT Initialize();
+
+  void InitProgressBar();
+  void EnableSystemButtons(bool enable);
+
+  void SwitchToState(WindowState new_state);
+
+  // Posts a WM_CLOSE message to close the window if the window is valid.
+  void Close();
+
+  // Message and command handlers.
+  LRESULT OnTimer(UINT msg,
+                  WPARAM wparam,
+                  LPARAM lparam,
+                  BOOL& handled);     // NOLINT(runtime/references)
+  LRESULT OnClose(UINT msg,
+                  WPARAM wparam,
+                  LPARAM lparam,
+                  BOOL& handled);     // NOLINT(runtime/references)
+  LRESULT OnDestroy(UINT msg,
+                    WPARAM wparam,
+                    LPARAM lparam,
+                    BOOL& handled);   // NOLINT(runtime/references)
+
+  LLock lock_;    // Lock for access synchronization of this object.
+  Thread thread_;  // Thread that creats the window and runs the message loop.
+  WindowState state_;   // State of the object.
+
+  // Indicates whether timer for fading effect has been created.
+  bool timer_created_;
+
+  int alpha_index_;   // Array index of current alpha blending value.
+
+  CString text_;  // Message text shows on the window.
+  CString caption_;  // Dialog title.
+
+  // Handle to large icon to show when ALT-TAB
+  scoped_hicon hicon_;
+
+  DISALLOW_COPY_AND_ASSIGN(SplashScreen);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_UI_SPLASH_SCREEN_H_
+
diff --git a/ui/splash_screen_test.cc b/ui/splash_screen_test.cc
new file mode 100644
index 0000000..ac1fe39
--- /dev/null
+++ b/ui/splash_screen_test.cc
@@ -0,0 +1,79 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// This unit test is driving the UI through its states so that we can
+// visually inspect all the controls are there in their right state and
+// position. To go from state to state, simply close the window on the screen.
+//
+// The unit test is useful for debugging UI states so different tests are
+// enabled/disabled at compile time, depending what needs to be tested.
+
+#include <windows.h>
+#include "omaha/base/app_util.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/utils.h"
+#include "omaha/goopdate/resource_manager.h"
+#include "omaha/testing/unit_test.h"
+#include "omaha/ui/splash_screen.h"
+
+namespace omaha {
+
+class SplashScreenTest : public testing::Test {
+ protected:
+  SplashScreenTest() {}
+
+  static void SetUpTestCase() {
+    CString resource_dir = app_util::GetModuleDirectory(NULL);
+    EXPECT_HRESULT_SUCCEEDED(
+        ResourceManager::Create(false, resource_dir, _T("en")));
+  }
+  static void TearDownTestCase() {
+    ResourceManager::Delete();
+  }
+
+  static void PostCloseMessage(const SplashScreen& splash_screen) {
+    ASSERT_TRUE(splash_screen.IsWindow());
+    ::PostMessage(splash_screen.m_hWnd, WM_CLOSE, 0, 0);
+  }
+};
+
+TEST_F(SplashScreenTest, SplashScreen) {
+  SplashScreen splash_screen(NULL);
+  splash_screen.Show();
+  ::Sleep(200);
+  splash_screen.Dismiss();
+}
+
+TEST_F(SplashScreenTest, SplashScreen_QuickRelease) {
+  SplashScreen splash_screen(_T("Sample bundle"));
+  splash_screen.Show();
+  splash_screen.Dismiss();
+}
+
+TEST_F(SplashScreenTest, SplashScreen_NoShow) {
+  SplashScreen splash_screen(_T("You Should Not See This"));
+}
+
+TEST_F(SplashScreenTest, SplashScreen_PostCloseMessage) {
+  SplashScreen splash_screen(NULL);
+  splash_screen.Show();
+  ::Sleep(100);
+  SplashScreenTest::PostCloseMessage(splash_screen);
+  ::Sleep(100);
+  splash_screen.Dismiss();
+}
+
+}   // namespace omaha
+
diff --git a/ui/ui.cc b/ui/ui.cc
new file mode 100644
index 0000000..b1eb836
--- /dev/null
+++ b/ui/ui.cc
@@ -0,0 +1,234 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include "omaha/ui/ui.h"
+#include "base/basictypes.h"
+#include "omaha/base/constants.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/system_info.h"
+#include "omaha/base/window_utils.h"
+#include "omaha/client/client_utils.h"
+#include "omaha/google_update/resource.h"   // For the IDI_APP
+#include "omaha/ui/ui_displayed_event.h"
+#include "omaha/ui/ui_metrics.h"
+
+namespace omaha {
+
+const OmahaWnd::ControlAttributes OmahaWnd::kVisibleTextAttributes =
+    { true,  true,  false, false };
+const OmahaWnd::ControlAttributes OmahaWnd::kDefaultActiveButtonAttributes =
+    { true,  true,  true,  true  };
+const OmahaWnd::ControlAttributes OmahaWnd::kNonDefaultActiveButtonAttributes =
+    { true,  true,  true,  false };
+const OmahaWnd::ControlAttributes OmahaWnd::kVisibleImageAttributes =
+    { true,  false, false, false };
+const OmahaWnd::ControlAttributes OmahaWnd::kDisabledNonButtonAttributes =
+    { false, false, false, false };
+
+OmahaWnd::OmahaWnd(int dialog_id, CMessageLoop* message_loop, HWND parent)
+    : IDD(dialog_id),
+      message_loop_(message_loop),
+      parent_(parent),
+      thread_id_(::GetCurrentThreadId()),
+      is_complete_(false),
+      is_close_enabled_(true),
+      events_sink_(NULL),
+      is_machine_(false) {
+  ASSERT1(message_loop);
+  CORE_LOG(L3, (_T("[OmahaWnd::OmahaWnd]")));
+}
+
+OmahaWnd::~OmahaWnd() {
+  CORE_LOG(L3, (_T("[OmahaWnd::~OmahaWnd]")));
+  ASSERT1(thread_id_ == ::GetCurrentThreadId());
+  ASSERT1(!IsWindow());
+}
+
+HRESULT OmahaWnd::Initialize() {
+  CORE_LOG(L3, (_T("[OmahaWnd::Initialize]")));
+  ASSERT1(thread_id_ == ::GetCurrentThreadId());
+
+  if (!Create(parent_)) {
+    CORE_LOG(LEVEL_ERROR, (_T("[Failed to create the window]")));
+    return GOOPDATE_E_UI_INTERNAL_ERROR;
+  }
+  VERIFY1(message_loop_->AddMessageFilter(this));
+
+  return S_OK;
+}
+
+void OmahaWnd::InitializeDialog() {    // NOLINT
+  CORE_LOG(L3, (_T("[OmahaWnd::InitializeDialog]")));
+
+  VERIFY1(SetWindowText(client_utils::GetInstallerDisplayName(bundle_name_)));
+
+  VERIFY1(CenterWindow(NULL));
+  VERIFY1(SUCCEEDED(WindowUtils::SetWindowIcon(m_hWnd,
+                                               IDI_APP,
+                                               address(hicon_))));
+}
+
+LRESULT OmahaWnd::OnClose(UINT,
+                          WPARAM,
+                          LPARAM,
+                          BOOL& handled) {         // NOLINT
+  CORE_LOG(L3, (_T("[OmahaWnd::OnClose]")));
+
+  ++metric_worker_ui_click_x;
+
+  MaybeCloseWindow();
+  handled = true;
+  return 0;
+}
+
+HRESULT OmahaWnd::CloseWindow() {
+  HRESULT hr = DestroyWindow() ? S_OK : HRESULTFromLastError();
+  if (events_sink_) {
+    events_sink_->DoClose();
+  }
+  return hr;
+}
+
+void OmahaWnd::MaybeRequestExitProcess() {
+  CORE_LOG(L3, (_T("[OmahaWnd::MaybeRequestExitProcess]")));
+  if (!is_complete_) {
+    return;
+  }
+
+  RequestExitProcess();
+}
+
+void OmahaWnd::RequestExitProcess() {
+  CORE_LOG(L3, (_T("[OmahaWnd::RequestExitProcess]")));
+
+  if (events_sink_) {
+    events_sink_->DoExit();
+  }
+}
+
+LRESULT OmahaWnd::OnNCDestroy(UINT, WPARAM, LPARAM, BOOL& handled) {  // NOLINT
+  CORE_LOG(L3, (_T("[OmahaWnd::OnNCDestroy]")));
+  VERIFY1(message_loop_->RemoveMessageFilter(this));
+  MaybeRequestExitProcess();
+  handled = false;  // Let ATL default processing handle the WM_NCDESTROY.
+  return 0;
+}
+
+// Called when esc key is hit.
+// If close is disabled, does nothing because we don't want the window to close.
+LRESULT OmahaWnd::OnCancel(WORD, WORD id, HWND, BOOL& handled) {  // NOLINT
+  VERIFY1(id == IDCANCEL);
+
+  if (!is_close_enabled_) {
+    return 0;
+  }
+
+  ++metric_worker_ui_esc_key_total;
+  MaybeCloseWindow();
+  handled = true;
+  return 0;
+}
+
+void OmahaWnd::Show() {
+  CORE_LOG(L3, (_T("[OmahaWnd::Show]")));
+  ASSERT1(thread_id_ == ::GetCurrentThreadId());
+  if (!IsWindow() || IsWindowVisible()) {
+    return;
+  }
+
+  CenterWindow(NULL);
+  SetVisible(true);
+
+  if (!::SetForegroundWindow(*this)) {
+    CORE_LOG(LW, (_T("[::SetForegroundWindow failed %d]"), ::GetLastError()));
+  }
+
+  UIDisplayedEventManager::SignalEvent(is_machine_);
+}
+
+bool OmahaWnd::OnComplete() {
+  CORE_LOG(L3, (_T("[OmahaWnd::OnComplete]")));
+  ASSERT1(thread_id_ == ::GetCurrentThreadId());
+
+  if (!IsWindow()) {
+    RequestExitProcess();
+    return false;
+  }
+
+  is_complete_ = true;
+
+  VERIFY1(SUCCEEDED(EnableClose(true)));
+
+  return true;
+}
+
+void OmahaWnd::SetControlAttributes(int control_id,
+                                    const ControlAttributes& attributes) {
+  HWND hwnd = GetDlgItem(control_id);
+  ASSERT1(hwnd);
+  ::ShowWindow(hwnd, attributes.is_visible_ ? SW_SHOW : SW_HIDE);
+  ::EnableWindow(hwnd, attributes.is_enabled_ ? true : false);
+  if (attributes.is_button_ && attributes.is_default_) {
+    // We ask the dialog manager to give the default push button the focus, so
+    // for instance the <Enter> key works as expected.
+    GotoDlgCtrl(hwnd);
+    LONG style = ::GetWindowLong(hwnd, GWL_STYLE);
+    if (style) {
+      style |= BS_DEFPUSHBUTTON;
+      ::SetWindowLong(hwnd, GWL_STYLE, style);
+    }
+  }
+}
+
+HRESULT OmahaWnd::EnableClose(bool enable) {
+  is_close_enabled_ = enable;
+  return EnableSystemCloseButton(is_close_enabled_);
+}
+
+HRESULT OmahaWnd::EnableSystemCloseButton(bool enable) {
+  HMENU menu = ::GetSystemMenu(*this, false);
+  ASSERT1(menu);
+  uint32 flags = MF_BYCOMMAND;
+  flags |= enable ? MF_ENABLED : MF_GRAYED;
+  VERIFY1(::EnableMenuItem(menu, SC_CLOSE, flags) != -1);
+  return S_OK;
+}
+
+// For the InitCommonControlsEx call to succeed on XP, a manifest is needed to
+// declare "Microsoft.Windows.Common-Controls" as a dependent assembly.
+// Further work may be needed to ensure W2K compatibility.
+HRESULT InitializeCommonControls(DWORD control_classes) {
+  INITCOMMONCONTROLSEX init_ctrls = { sizeof(INITCOMMONCONTROLSEX), 0 };
+  ASSERT1(init_ctrls.dwSize == sizeof(init_ctrls));
+  init_ctrls.dwICC = control_classes;
+  if (!::InitCommonControlsEx(&init_ctrls)) {
+    // In the case of XP RTM and XP SP1, InitCommonControlsEx is failing but
+    // the UI initializes fine and works correctly. Because of this we only log
+    // the failure and do not error out.
+    const DWORD error = ::GetLastError();
+    CORE_LOG(LEVEL_ERROR, (_T("[InitCommonControlsEx failed][%u]"), error));
+    ASSERT1(ERROR_CLASS_ALREADY_EXISTS == error);
+    if (ERROR_CLASS_ALREADY_EXISTS != error) {
+      return HRESULT_FROM_WIN32(error);
+    }
+  }
+
+  return S_OK;
+}
+
+}  // namespace omaha
diff --git a/ui/ui.h b/ui/ui.h
new file mode 100644
index 0000000..68eedbf
--- /dev/null
+++ b/ui/ui.h
@@ -0,0 +1,166 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_UI_UI_H_
+#define OMAHA_UI_UI_H_
+
+#include <atlbase.h>
+#include <atlstr.h>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/wtl_atlapp_wrapper.h"
+// TODO(omaha3): Depending on how separate the UI is, we may want to separate
+// the UI-specific resources, especially the dialogs. Can we handle bidi
+// independent of languages? This would allow simple replacement of Omaha's
+// UI without impacting the core or error messages.
+#include "omaha/client/resource.h"
+
+namespace omaha {
+
+class HighresTimer;
+
+class OmahaWndEvents {
+ public:
+  virtual ~OmahaWndEvents() {}
+  virtual void DoClose() = 0;
+  virtual void DoExit() = 0;
+};
+
+// Implements the UI progress window.
+class OmahaWnd
+    : public CAxDialogImpl<OmahaWnd>,
+      public CMessageFilter {
+  typedef CAxDialogImpl<OmahaWnd> Base;
+ public:
+  virtual ~OmahaWnd();
+
+  // The dialog resource ID as required by CAxDialogImpl.
+  const int IDD;
+
+  virtual HRESULT Initialize();
+
+  // CMessageFilter interface method.
+  BOOL PreTranslateMessage(MSG* msg) {
+    return CWindow::IsDialogMessage(msg);
+  }
+
+  void SetEventSink(OmahaWndEvents* ev) { events_sink_ = ev; }
+
+  // TODO(omaha3): Move these to constructor. They are fundamental to the UI.
+  // TODO(omaha3): Move is_machine_ to CompleteWnd if we do not use UI displayed
+  // events. There it will be used to create the URL if we do not move that too.
+  void set_is_machine(bool is_machine) { is_machine_ = is_machine; }
+  void set_bundle_name(const CString& name) { bundle_name_ = name; }
+
+  virtual void Show();
+
+  BEGIN_MSG_MAP(OmahaWnd)
+    MESSAGE_HANDLER(WM_CLOSE, OnClose)
+    MESSAGE_HANDLER(WM_NCDESTROY, OnNCDestroy)
+    COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
+    CHAIN_MSG_MAP(Base)
+  END_MSG_MAP()
+
+ protected:
+#pragma warning(disable : 4510 4610)
+// C4510: default constructor could not be generated
+// C4610: struct can never be instantiated - user defined constructor required
+  struct ControlAttributes {
+    const bool is_visible_;
+    const bool is_enabled_;
+    const bool is_button_;
+    const bool is_default_;
+  };
+#pragma warning(default : 4510 4610)
+
+  OmahaWnd(int dialog_id, CMessageLoop* message_loop, HWND parent);
+
+  // Message and command handlers.
+  LRESULT OnClose(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);       // NOLINT
+  LRESULT OnNCDestroy(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);   // NOLINT
+  LRESULT OnCancel(WORD notify_code, WORD id, HWND wnd_ctl, BOOL& handled);     // NOLINT
+
+  // Handles requests to close the window. Returns true if the window is closed.
+  virtual bool MaybeCloseWindow() = 0;
+
+  // Handles entering the completion mode.
+  // Returns whether to continue with OnComplete operations.
+  bool OnComplete();
+
+  // TODO(omaha3): May need to be public to implement
+  // COMPLETION_CODE_SUCCESS_CLOSE_UI outside the UI.
+  // Closes the window.
+  HRESULT CloseWindow();
+  void InitializeDialog();
+
+  HRESULT EnableClose(bool enable);
+  HRESULT EnableSystemCloseButton(bool enable);
+
+  void SetControlAttributes(int control_id,
+                            const ControlAttributes& attributes);
+
+  void SetVisible(bool visible) {
+    ShowWindow(visible ? SW_SHOWNORMAL : SW_HIDE);
+  }
+
+  DWORD thread_id() { return thread_id_; }
+  CMessageLoop* message_loop() { return message_loop_; }
+  bool is_complete() { return is_complete_; }
+  bool is_close_enabled() { return is_close_enabled_; }
+  bool is_machine() { return is_machine_; }
+  const CString& bundle_name() { return bundle_name_; }
+
+  static const ControlAttributes kVisibleTextAttributes;
+  static const ControlAttributes kDefaultActiveButtonAttributes;
+  static const ControlAttributes kNonDefaultActiveButtonAttributes;
+  static const ControlAttributes kVisibleImageAttributes;
+  static const ControlAttributes kDisabledNonButtonAttributes;
+
+ private:
+  HRESULT SetWindowIcon();
+
+  void MaybeRequestExitProcess();
+  void RequestExitProcess();
+
+  CMessageLoop* message_loop_;
+  HWND parent_;
+  DWORD thread_id_;
+
+  bool is_complete_;
+  bool is_close_enabled_;
+
+  OmahaWndEvents* events_sink_;
+
+  bool is_machine_;
+  CString bundle_name_;
+
+
+  // Handle to large icon to show when ALT-TAB
+  scoped_hicon hicon_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(OmahaWnd);
+};
+
+// Registers the specified common control classes from the common control DLL.
+// Calls are cumulative, meaning control_classes are added to existing classes.
+// UIs that use common controls should call this method to ensure that the UI
+// supports visual styles.
+HRESULT InitializeCommonControls(DWORD control_classes);
+
+}  // namespace omaha
+
+#endif  // OMAHA_UI_UI_H_
diff --git a/ui/ui_ctls.h b/ui/ui_ctls.h
new file mode 100644
index 0000000..c1f9e8d
--- /dev/null
+++ b/ui/ui_ctls.h
@@ -0,0 +1,211 @@
+// Copyright 2007-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// This is a description on UI elements for different states of the UI.
+// We have only one dialog which changes between different UI states. Some
+// controls are hidded, some controls are disabled, etc...
+
+#ifndef OMAHA_UI_UI_CTLS_H__
+#define OMAHA_UI_UI_CTLS_H__
+
+#include "omaha/ui/progress_wnd.h"
+
+namespace omaha {
+
+const ProgressWnd::ControlState ProgressWnd::ctls_[] = {
+    // The struct values are:
+    // is_visible, is_enabled, is_button, is_default
+  { IDC_PROGRESS,
+    { { true,  true,  false, false },     // STATE_INIT
+      { true,  true,  false, false },     // STATE_CHECKING_FOR_UPDATE
+      { true,  true,  false, false },     // STATE_WAITING_TO_DOWNLOAD
+      { true,  true,  false, false },     // STATE_DOWNLOADING
+      { true,  true,  false, false },     // STATE_WAITING_TO_INSTALL
+      { true,  true,  false, false },     // STATE_INSTALLING
+      { true,  true,  false, false },     // STATE_PAUSED
+      { false, false, false, false },     // STATE_COMPLETE_SUCCESS
+      { false, false, false, false },     // STATE_COMPLETE_ERROR
+      { false, false, false, false },     // STATE_COMPLETE_RESTART_BROWSER
+      { false, false, false, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
+      { false, false, false, false },     // STATE_COMPLETE_REBOOT
+      { false, false, false, false },     // STATE_END
+    },
+  },
+  { IDC_PAUSE_RESUME_TEXT,
+    { { false, false, false, false },     // STATE_INIT
+      { false, false, false, false },     // STATE_CHECKING_FOR_UPDATE
+      { false, false, false, false },     // STATE_WAITING_TO_DOWNLOAD
+      { false,  true, false, false },     // STATE_DOWNLOADING
+      { false, false, false, false },     // STATE_WAITING_TO_INSTALL
+      { false, false, false, false },     // STATE_INSTALLING
+      { false,  true, false, false },     // STATE_PAUSED
+      { false, false, false, false },     // STATE_COMPLETE_SUCCESS
+      { false, false, false, false },     // STATE_COMPLETE_ERROR
+      { false, false, false, false },     // STATE_COMPLETE_RESTART_BROWSER
+      { false, false, false, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
+      { false, false, false, false },     // STATE_COMPLETE_REBOOT
+      { false, false, false, false },     // STATE_END
+    },
+  },
+  { IDC_INFO_TEXT,
+    { { false, false, false, false },     // STATE_INIT
+      { false, false, false, false },     // STATE_CHECKING_FOR_UPDATE
+      { false, false, false, false },     // STATE_WAITING_TO_DOWNLOAD
+      { false, true,  false, false },     // STATE_DOWNLOADING
+      { false, false, false, false },     // STATE_WAITING_TO_INSTALL
+      { false, false, false, false },     // STATE_INSTALLING
+      { false, false, false, false },     // STATE_PAUSED
+      { false, false, false, false },     // STATE_COMPLETE_SUCCESS
+      { false, false, false, false },     // STATE_COMPLETE_ERROR
+      { false, false, false, false },     // STATE_COMPLETE_RESTART_BROWSER
+      { false, false, false, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
+      { false, false, false, false },     // STATE_COMPLETE_REBOOT
+      { false, false, false, false },     // STATE_END
+    },
+  },
+  { IDC_INSTALLER_STATE_TEXT,
+    { { true,  true,  false, false },     // STATE_INIT
+      { true,  true,  false, false },     // STATE_CHECKING_FOR_UPDATE
+      { true,  true,  false, false },     // STATE_WAITING_TO_DOWNLOAD
+      { true,  true,  false, false },     // STATE_DOWNLOADING
+      { true,  true,  false, false },     // STATE_WAITING_TO_INSTALL
+      { true,  true,  false, false },     // STATE_INSTALLING
+      { true,  true,  false, false },     // STATE_PAUSED
+      { false, false, false, false },     // STATE_COMPLETE_SUCCESS
+      { false, false, false, false },     // STATE_COMPLETE_ERROR
+      { false, false, false, false },     // STATE_COMPLETE_RESTART_BROWSER
+      { false, false, false, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
+      { false, false, false, false },     // STATE_COMPLETE_REBOOT
+      { false, false, false, false },     // STATE_END
+    },
+  },
+  { IDC_COMPLETE_TEXT,
+    { { false, false, false, false },     // STATE_INIT
+      { false, false, false, false },     // STATE_CHECKING_FOR_UPDATE
+      { false, false, false, false },     // STATE_WAITING_TO_DOWNLOAD
+      { false, false, false, false },     // STATE_DOWNLOADING
+      { false, false, false, false },     // STATE_WAITING_TO_INSTALL
+      { false, false, false, false },     // STATE_INSTALLING
+      { false, false, false, false },     // STATE_PAUSED
+      { true,  true,  false, false },     // STATE_COMPLETE_SUCCESS
+      { false, false, false, false },     // STATE_COMPLETE_ERROR
+      { true,  true,  false, false },     // STATE_COMPLETE_RESTART_BROWSER
+      { true,  true,  false, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
+      { true,  true,  false, false },     // STATE_COMPLETE_REBOOT
+      { false, false, false, false },     // STATE_END
+    },
+  },
+  { IDC_ERROR_TEXT,
+    { { false, false, false, false },     // STATE_INIT
+      { false, false, false, false },     // STATE_CHECKING_FOR_UPDATE
+      { false, false, false, false },     // STATE_WAITING_TO_DOWNLOAD
+      { false, false, false, false },     // STATE_DOWNLOADING
+      { false, false, false, false },     // STATE_WAITING_TO_INSTALL
+      { false, false, false, false },     // STATE_INSTALLING
+      { false, false, false, false },     // STATE_PAUSED
+      { false, false, false, false },     // STATE_COMPLETE_SUCCESS
+      { true,  true,  false, false },     // STATE_COMPLETE_ERROR
+      { false, false, false, false },     // STATE_COMPLETE_RESTART_BROWSER
+      { false, false, false, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
+      { false, false, false, false },     // STATE_COMPLETE_REBOOT
+      { false, false, false, false },     // STATE_END
+    },
+  },
+  { IDC_GET_HELP_TEXT,
+    { { false, false, false, false },     // STATE_INIT
+      { false, false, false, false },     // STATE_CHECKING_FOR_UPDATE
+      { false, false, false, false },     // STATE_WAITING_TO_DOWNLOAD
+      { false, false, false, false },     // STATE_DOWNLOADING
+      { false, false, false, false },     // STATE_WAITING_TO_INSTALL
+      { false, false, false, false },     // STATE_INSTALLING
+      { false, false, false, false },     // STATE_PAUSED
+      { false, false, false, false },     // STATE_COMPLETE_SUCCESS
+      { true,  true,  false, false },     // STATE_COMPLETE_ERROR
+      { false, false, false, false },     // STATE_COMPLETE_RESTART_BROWSER
+      { false, false, false, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
+      { false, false, false, false },     // STATE_COMPLETE_REBOOT
+      { false, false, false, false },     // STATE_END
+    },
+  },
+  { IDC_BUTTON1,
+    { { false, false, true, false },     // STATE_INIT
+      { false, false, true, false },     // STATE_CHECKING_FOR_UPDATE
+      { false, false, true, false },     // STATE_WAITING_TO_DOWNLOAD
+      { false, false, true, false },     // STATE_DOWNLOADING
+      { false, false, true, false },     // STATE_WAITING_TO_INSTALL
+      { false, false, true, false },     // STATE_INSTALLING
+      { false, false, true, false },     // STATE_PAUSED
+      { false, false, true, false },     // STATE_COMPLETE_SUCCESS
+      { false, false, true, false },     // STATE_COMPLETE_ERROR
+      { true,  true,  true, true  },     // STATE_COMPLETE_RESTART_BROWSER
+      { true,  true,  true, true  },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
+      { true,  true,  true, true  },     // STATE_COMPLETE_REBOOT
+      { false, false, true, false },     // STATE_END
+    },
+  },
+  { IDC_BUTTON2,
+    { { false, false, true, false },     // STATE_INIT
+      { false, false, true, false },     // STATE_CHECKING_FOR_UPDATE
+      { false, false, true, false },     // STATE_WAITING_TO_DOWNLOAD
+      { false, false, true, false },     // STATE_DOWNLOADING
+      { false, false, true, false },     // STATE_WAITING_TO_INSTALL
+      { false, false, true, false },     // STATE_INSTALLING
+      { false, false, true, false },     // STATE_PAUSED
+      { false, false, true, false },     // STATE_COMPLETE_SUCCESS
+      { false, false, true, false },     // STATE_COMPLETE_ERROR
+      { true,  true,  true, false },     // STATE_COMPLETE_RESTART_BROWSER
+      { true,  true,  true, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
+      { true,  true,  true, false },     // STATE_COMPLETE_REBOOT
+      { false, false, true, false },     // STATE_END
+    },
+  },
+  { IDC_CLOSE,
+    { { false, false, true, false },     // STATE_INIT
+      { false, false, true, false },     // STATE_CHECKING_FOR_UPDATE
+      { false, false, true, false },     // STATE_WAITING_TO_DOWNLOAD
+      { false, false, true, false },     // STATE_DOWNLOADING
+      { false, false, true, false },     // STATE_WAITING_TO_INSTALL
+      { false, false, true, false },     // STATE_INSTALLING
+      { false, false, true, false },     // STATE_PAUSED
+      { true,  true,  true, true  },     // STATE_COMPLETE_SUCCESS
+      { true,  true,  true, true  },     // STATE_COMPLETE_ERROR
+      { false, false, true, false },     // STATE_COMPLETE_RESTART_BROWSER
+      { false, false, true, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
+      { false, false, true, false },     // STATE_COMPLETE_REBOOT
+      { false, false, true, false },     // STATE_END
+    },
+  },
+  { IDC_IMAGE,
+    { { false, false, false, false },     // STATE_INIT
+      { false, false, false, false },     // STATE_CHECKING_FOR_UPDATE
+      { false, false, false, false },     // STATE_WAITING_TO_DOWNLOAD
+      { false, false, false, false },     // STATE_DOWNLOADING
+      { false, false, false, false },     // STATE_WAITING_TO_INSTALL
+      { false, false, false, false },     // STATE_INSTALLING
+      { false, false, false, false },     // STATE_PAUSED
+      { true,  false, false, false },     // STATE_COMPLETE_SUCCESS
+      { false, false, false, false },     // STATE_COMPLETE_ERROR
+      { true,  false, false, false },     // STATE_COMPLETE_RESTART_BROWSER
+      { true,  false, false, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
+      { true,  false, false, false },     // STATE_COMPLETE_REBOOT
+      { false, false, false, false },     // STATE_END
+    },
+  },
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_UI_UI_CTLS_H__
+
diff --git a/ui/ui_displayed_event.cc b/ui/ui_displayed_event.cc
new file mode 100644
index 0000000..4130a75
--- /dev/null
+++ b/ui/ui_displayed_event.cc
@@ -0,0 +1,84 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/base/const_object_names.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/logging.h"
+#include "omaha/common/goopdate_utils.h"
+#include "omaha/ui/ui_displayed_event.h"
+
+namespace omaha {
+
+HRESULT UIDisplayedEventManager::CreateEvent(bool is_machine) {
+  ASSERT1(!IsEventHandleInitialized());
+  return goopdate_utils::CreateUniqueEventInEnvironment(
+        kLegacyUiDisplayedEventEnvironmentVariableName,
+        is_machine,
+        address(ui_displayed_event_));
+}
+
+// Caller does not own the event handle and must not close it.
+// There is a single event handle for each process. The handle is closed when
+// the process exits.
+HRESULT UIDisplayedEventManager::GetEvent(bool is_machine,
+                                          HANDLE* ui_displayed_event) {
+  ASSERT1(ui_displayed_event);
+  *ui_displayed_event = NULL;
+  if (IsEventHandleInitialized()) {
+    *ui_displayed_event = get(ui_displayed_event_);
+    return S_OK;
+  }
+
+  HRESULT hr = goopdate_utils::OpenUniqueEventFromEnvironment(
+      kLegacyUiDisplayedEventEnvironmentVariableName,
+      is_machine,
+      address(ui_displayed_event_));
+  if (FAILED(hr)) {
+    return hr;
+  }
+
+  *ui_displayed_event = get(ui_displayed_event_);
+  return S_OK;
+}
+
+// Creates the event if it does not already exist in the environment.
+void UIDisplayedEventManager::SignalEvent(bool is_machine) {
+  CORE_LOG(L2, (_T("[SignalEvent]")));
+
+  if (!IsEventHandleInitialized()) {
+    HRESULT hr = GetEvent(is_machine, address(ui_displayed_event_));
+    if (HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND) == hr) {
+      // The event was not created by an earlier process. This can happen when
+      // developers run the /handoff process directly.
+      hr = CreateEvent(is_machine);
+    }
+    if (FAILED(hr)) {
+      reset(ui_displayed_event_);
+      // We may display two UIs.
+      return;
+    }
+  }
+
+  ASSERT1(IsEventHandleInitialized());
+  VERIFY1(::SetEvent(get(ui_displayed_event_)));
+}
+
+bool UIDisplayedEventManager::IsEventHandleInitialized() {
+  return valid(ui_displayed_event_);
+}
+
+scoped_handle UIDisplayedEventManager::ui_displayed_event_;
+
+}  // namespace omaha
diff --git a/ui/ui_displayed_event.h b/ui/ui_displayed_event.h
new file mode 100644
index 0000000..50fe728
--- /dev/null
+++ b/ui/ui_displayed_event.h
@@ -0,0 +1,55 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+// This is for Omaha2 backwards compatibility.
+// The installed Omaha3 handoff process sets an event to tell an Omaha2 setup
+// worker running from the temp directory that a UI has been displayed so that
+// the Omaha2 worker will not display a second UI on error. The event's name is
+// passed in an environment variable name by the Omaha2 worker.
+
+#ifndef OMAHA_UI_UI_DISPLAYED_EVENT_H_
+#define OMAHA_UI_UI_DISPLAYED_EVENT_H_
+
+#include <windows.h>
+#include "omaha/base/scoped_any.h"
+
+namespace omaha {
+
+// Manages the UI Displayed Event, which is used to communicate whether a UI
+// has been displayed between processes.
+// This class is not thread safe.
+class UIDisplayedEventManager {
+ public:
+  // Signals the event. Creates it if its name does not already exist in the
+  // environment variable.
+  static void SignalEvent(bool is_machine);
+
+ private:
+   // Creates the event and sets its name in the environment variable.
+   static HRESULT CreateEvent(bool is_machine);
+
+   // Gets the event from the name in the environment variable.
+   static HRESULT GetEvent(bool is_machine, HANDLE* ui_displayed_event);
+
+  // Returns whether this process's event handle has been initialized.
+  static bool IsEventHandleInitialized();
+
+  // A single instance of the UI Displayed Event handle to be used for the
+  // lifetime of this process.
+  static scoped_handle ui_displayed_event_;
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_UI_UI_DISPLAYED_EVENT_H_
diff --git a/ui/ui_metrics.cc b/ui/ui_metrics.cc
new file mode 100644
index 0000000..377c9f2
--- /dev/null
+++ b/ui/ui_metrics.cc
@@ -0,0 +1,39 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+
+#include "omaha/ui/ui_metrics.h"
+
+namespace omaha {
+
+DEFINE_METRIC_timing(worker_ui_cancel_ms);
+DEFINE_METRIC_count(worker_ui_cancels);
+
+DEFINE_METRIC_count(worker_ui_click_x);
+
+DEFINE_METRIC_count(worker_ui_esc_key_total);
+
+DEFINE_METRIC_count(worker_ui_restart_browser_buttons_displayed);
+DEFINE_METRIC_count(worker_ui_restart_browser_now_click);
+DEFINE_METRIC_count(worker_ui_restart_all_browsers_buttons_displayed);
+DEFINE_METRIC_count(worker_ui_restart_all_browsers_now_click);
+DEFINE_METRIC_count(worker_ui_reboot_buttons_displayed);
+DEFINE_METRIC_count(worker_ui_reboot_now_click);
+
+DEFINE_METRIC_count(worker_ui_get_help_displayed);
+DEFINE_METRIC_count(worker_ui_get_help_click);
+
+
+}  // namespace omaha
diff --git a/ui/ui_metrics.h b/ui/ui_metrics.h
new file mode 100644
index 0000000..bb459ee
--- /dev/null
+++ b/ui/ui_metrics.h
@@ -0,0 +1,56 @@
+// Copyright 2008-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+// Declares the usage metrics used by the worker module.
+
+#ifndef OMAHA_UI_UI_METRICS_H__
+#define OMAHA_UI_UI_METRICS_H__
+
+#include "omaha/statsreport/metrics.h"
+
+namespace omaha {
+
+// Time (ms) until the user canceled.
+DECLARE_METRIC_timing(worker_ui_cancel_ms);
+// How many times the user canceled. Only includes confirmed cancels.
+DECLARE_METRIC_count(worker_ui_cancels);
+
+// How many times the user has clicked on the x button of the UI.
+DECLARE_METRIC_count(worker_ui_click_x);
+
+// How many times the user has hit the esc key.
+DECLARE_METRIC_count(worker_ui_esc_key_total);
+
+// How many times the "Restart Browser Now/Later" buttons were displayed.
+DECLARE_METRIC_count(worker_ui_restart_browser_buttons_displayed);
+// How many times the user clicked "Restart Browser Now".
+DECLARE_METRIC_count(worker_ui_restart_browser_now_click);
+// How many times the "Restart Browsers Now/Later" buttons were displayed.
+DECLARE_METRIC_count(worker_ui_restart_all_browsers_buttons_displayed);
+// How many times the user clicked "Restart Browsers Now".
+DECLARE_METRIC_count(worker_ui_restart_all_browsers_now_click);
+// How many times the "Restart Now/Later" (reboot) buttons were displayed.
+DECLARE_METRIC_count(worker_ui_reboot_buttons_displayed);
+// How many times the user clicked "Restart Now" (reboot).
+DECLARE_METRIC_count(worker_ui_reboot_now_click);
+
+// How many times the user was offered the "Help Me Fix This" link.
+DECLARE_METRIC_count(worker_ui_get_help_displayed);
+// How many times the user clicked the "Help Me Fix This" link.
+DECLARE_METRIC_count(worker_ui_get_help_click);
+
+}  // namespace omaha
+
+#endif  // OMAHA_UI_UI_METRICS_H__
diff --git a/ui/uilib/node.h b/ui/uilib/node.h
new file mode 100644
index 0000000..af00810
--- /dev/null
+++ b/ui/uilib/node.h
@@ -0,0 +1,40 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// node.h : Declaration of the Node
+
+#ifndef OMAHA_UI_UILIB_NODE_H_
+#define OMAHA_UI_UILIB_NODE_H_
+
+#include "omaha/ui/uilib/node_state.h"
+
+class Node {
+ public:
+
+  explicit Node(HWND window) : node_state_(window) {}
+  virtual ~Node() {}
+
+  CString node_text() const { return node_text_; }
+  void AddText(const TCHAR* text) { node_text_ += text; }
+
+  void set_node_state(const NodeState& node_state) { node_state_ = node_state; }
+  const NodeState& node_state() const { return node_state_; }
+
+ private:
+  CString       node_text_;
+  NodeState     node_state_;
+};
+
+#endif  // OMAHA_UI_UILIB_NODE_H_
diff --git a/ui/uilib/node_state.cc b/ui/uilib/node_state.cc
new file mode 100644
index 0000000..c4d9c89
--- /dev/null
+++ b/ui/uilib/node_state.cc
@@ -0,0 +1,237 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+//
+
+#include "omaha/ui/uilib/node_state.h"
+
+// Add a list item tag
+NodeState::Tags NodeState::tags_[] = {
+// name_to_match        length_name_to_match       action      no_parameters;
+  { _T("<b>"),       static_cast<int>(_tcslen(_T("<b>"))),      BOLD_ON,        true  },  // NOLINT
+  { _T("</b>"),      static_cast<int>(_tcslen(_T("</b>"))),     BOLD_OFF,       true  },  // NOLINT
+  { _T("<i>"),       static_cast<int>(_tcslen(_T("<i>"))),      ITALIC_ON,      true  },  // NOLINT
+  { _T("</i>"),      static_cast<int>(_tcslen(_T("</i>"))),     ITALIC_OFF,     true  },  // NOLINT
+  { _T("<u>"),       static_cast<int>(_tcslen(_T("<u>"))),      UNDERLINE_ON,   true  },  // NOLINT
+  { _T("</u>"),      static_cast<int>(_tcslen(_T("</u>"))),     UNDERLINE_OFF,  true  },  // NOLINT
+  { _T("<color="),   static_cast<int>(_tcslen(_T("<color="))),  TEXTCOLOR_ON,   false },  // NOLINT
+  { _T("</color>"),  static_cast<int>(_tcslen(_T("</color>"))), TEXTCOLOR_OFF,  true  },  // NOLINT
+  { _T("<size="),    static_cast<int>(_tcslen(_T("<size="))),   TEXTSIZE_ON,    false },  // NOLINT
+  { _T("</size>"),   static_cast<int>(_tcslen(_T("</size>"))),  TEXTSIZE_OFF,   true  },  // NOLINT
+  { _T("<a="),       static_cast<int>(_tcslen(_T("<a="))),      URL_ON,         false },  // NOLINT
+  { _T("</a>"),      static_cast<int>(_tcslen(_T("</a>"))),     URL_OFF,        true  },  // NOLINT
+};
+
+NodeState::NodeState(HWND window)
+    : default_font_(NULL),
+      font_(NULL),
+      bold_(false),
+      italic_(false),
+      underline_(false),
+      text_color_(0),
+      text_size_(8),
+      owner_window_(window) {
+}
+
+NodeState::~NodeState() {
+}
+
+
+void NodeState::SetStdFont(HFONT font) {
+  default_font_ = font;
+}
+
+
+HFONT NodeState::GetFont() const {
+  if (IsDefaultFont())
+    return default_font_;
+
+  if (font_)
+    return font_;
+
+  if (default_font_) {
+    HDC dc = GetDC(owner_window_);
+
+    LOGFONT log_font;
+    GetObject(default_font_, sizeof(LOGFONT), &log_font);
+    log_font.lfWeight    = bold_ ? FW_BOLD : FW_NORMAL;
+    log_font.lfItalic    = italic_;
+    log_font.lfUnderline = underline_;
+    log_font.lfHeight    =
+        -MulDiv(text_size_, GetDeviceCaps(dc, LOGPIXELSY), 72);
+    font_ = CreateFontIndirect(&log_font);
+  }
+
+  return font_;
+}
+
+
+bool NodeState::IsDefaultFont() const {
+    if (bold_ || italic_ || underline_)
+        return false;
+
+    if (text_size_ != 8)
+        return false;
+
+    return true;
+}
+
+
+int NodeState::ConsumeTag(const TCHAR* string) {
+  int size = sizeof(tags_) / sizeof(tags_[0]);
+  for (int i = 0; i < size; i++) {
+    if (_tcsnicmp(string, tags_[i].name_to_match,
+                  tags_[i].length_name_to_match) == 0) {
+      if (tags_[i].no_parameters) {
+        ApplyAction(tags_[i].action, NULL);
+        return tags_[i].length_name_to_match;
+      } else {
+        return tags_[i].length_name_to_match +
+               ApplyAction(tags_[i].action, string +
+                           tags_[i].length_name_to_match) + 1;
+      }
+    }
+  }
+
+  return 0;
+}
+
+
+int NodeState::ApplyAction(Actions action, const TCHAR* string/*=NULL*/) {
+  int read = 0;
+  switch (action) {
+  case BOLD_ON :
+    bold_ = true;
+    break;
+
+  case BOLD_OFF :
+    bold_ = false;
+    break;
+
+  case ITALIC_ON :
+    italic_ = true;
+    break;
+
+  case ITALIC_OFF :
+    italic_ = false;
+    break;
+
+  case UNDERLINE_ON :
+    underline_ = true;
+    break;
+
+  case UNDERLINE_OFF :
+    underline_ = false;
+    break;
+
+  case TEXTCOLOR_ON :
+    ATLASSERT(string);
+    if (string) {
+      int nParam = 0;
+      read = ReadColorRef(string, &nParam);
+      text_color_ = nParam;
+    }
+    break;
+
+  case TEXTCOLOR_OFF :
+    text_color_ = 0;
+    break;
+
+  case TEXTSIZE_ON :
+    ATLASSERT(string);
+    if (string)
+      read = ReadNumParameter(string, &text_size_);
+    break;
+
+  case TEXTSIZE_OFF :
+    text_size_ = 8;
+    break;
+
+  case URL_ON :
+    underline_ = true;
+    text_color_ = RGB(0, 0, 0xff);
+    ATLASSERT(string);
+    if (string)
+      read = ReadString(string, &url_);
+    break;
+
+  case URL_OFF :
+    underline_ = false;
+    text_color_ = 0;
+    url_ = _T("");
+    break;
+
+  case UNKNOWN:
+    // fall thru
+
+  default:
+    ATLASSERT(false);
+  }
+  return read;
+}
+
+int NodeState::ReadNumParameter(const TCHAR* string, int* param) {
+  if (!param)
+    return 0;
+
+  *param = 0;
+  const TCHAR* current_pos = string;
+  while (current_pos && _istdigit(*current_pos)) {
+    *param *= 10;
+    *param += *current_pos - _T('0');
+    current_pos++;
+  }
+  return static_cast<int>(current_pos - string);
+}
+
+int NodeState::ReadHexParameter(const TCHAR* string, int* param) {
+  if (!param)
+    return 0;
+
+  *param = 0;
+  const TCHAR* current_pos = string;
+  while (current_pos && _istxdigit(*current_pos)) {
+    *param = ((*param) << 4);
+    if (_istdigit(*current_pos))
+      *param += *current_pos - _T('0');
+    else
+      *param += (*current_pos | 0x20) - _T('a') + 10;
+    current_pos++;
+  }
+  return static_cast<int>(current_pos - string);
+}
+
+int NodeState::ReadColorRef(const TCHAR* string, int* param) {
+  if (!param)
+    return 0;
+
+  int read = ReadHexParameter(string, param);
+  *param = RGB((*param) >> 16, ((*param) >> 8) & 0xff, (*param) & 0xff);
+  return read;
+}
+
+int NodeState::ReadString(const TCHAR* string, CString* string_out) {
+  if (!string_out)
+    return 0;
+
+  int length = 0;
+  int position = _tcscspn(string, _T(" >"));
+  if (position >= 0) {
+    *string_out = CString(string, position);
+    length = position;
+  }
+
+  return length;
+}
diff --git a/ui/uilib/node_state.h b/ui/uilib/node_state.h
new file mode 100644
index 0000000..77ce30a
--- /dev/null
+++ b/ui/uilib/node_state.h
@@ -0,0 +1,81 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+
+#ifndef OMAHA_UI_UILIB_NODE_STATE_H_
+#define OMAHA_UI_UILIB_NODE_STATE_H_
+
+#include <atlstr.h>
+
+class NodeState {
+ public:
+  explicit NodeState(HWND window);
+  virtual ~NodeState();
+
+  void SetStdFont(HFONT font);
+  HFONT GetFont() const;
+  COLORREF text_color() const { return text_color_; }
+  bool IsURL() const { return !url_.IsEmpty(); }
+  CString url() const { return url_; }
+
+  int ConsumeTag(const TCHAR* string);
+
+ private:
+  enum Actions {
+    UNKNOWN,
+    BOLD_ON,
+    BOLD_OFF,
+    ITALIC_ON,
+    ITALIC_OFF,
+    UNDERLINE_ON,
+    UNDERLINE_OFF,
+    TEXTCOLOR_ON,
+    TEXTCOLOR_OFF,
+    TEXTSIZE_ON,
+    TEXTSIZE_OFF,
+    URL_ON,
+    URL_OFF,
+  };
+
+  struct Tags {
+    const TCHAR*  name_to_match;
+    int           length_name_to_match;
+    Actions       action;
+    bool          no_parameters;
+  };
+
+  int ApplyAction(Actions action, const TCHAR* string);
+  int ReadNumParameter(const TCHAR* string, int* param);
+  int ReadHexParameter(const TCHAR* szString, int* param);
+  int ReadColorRef(const TCHAR* string, int* param);
+  int ReadString(const TCHAR* string, CString* string_out);
+  bool IsDefaultFont() const;
+
+  // Data
+  static Tags    tags_[];
+
+  HWND           owner_window_;
+  HFONT          default_font_;
+  mutable HFONT  font_;
+
+  bool           bold_;
+  bool           italic_;
+  bool           underline_;
+  COLORREF       text_color_;
+  int            text_size_;
+  CString        url_;
+};
+
+#endif  // OMAHA_UI_UILIB_NODE_STATE_H_
diff --git a/ui/uilib/static_ex.cc b/ui/uilib/static_ex.cc
new file mode 100644
index 0000000..c038f65
--- /dev/null
+++ b/ui/uilib/static_ex.cc
@@ -0,0 +1,586 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+
+// TODO(omaha): need to handle WM_GETTEXT
+// TODO(omaha): need to handle WM_SIZE
+// TODO(omaha): nice to have transparent mode
+
+#include "omaha/ui/uilib/static_ex.h"
+
+#include <shellapi.h>
+#include <strsafe.h>
+#include "omaha/ui/uilib/node_state.h"
+
+const int StaticEx::kBorderNone    = 0;
+const int StaticEx::kBorderLeft    = 1;
+const int StaticEx::kBorderTop     = 2;
+const int StaticEx::kBorderRight   = 4;
+const int StaticEx::kBorderBottom  = 8;
+const int StaticEx::kBorderAll     = kBorderLeft | kBorderTop | kBorderRight |
+                                     kBorderBottom;
+
+HCURSOR StaticEx::hand_cursor_ = NULL;
+
+StaticEx::StaticEx()
+    : margins_(1, 0, 1, 0),  // see comment in h file
+      background_color_(0xffffff),
+      use_background_color_(false),
+      ellipsis_(0),
+      border_(kBorderNone),
+      border_color_(0),
+      default_font_(NULL) {
+}
+
+StaticEx::~StaticEx() {
+  EraseNodes();
+  EraseLines(&lines_);
+}
+
+void StaticEx::Reset() {
+  text_.Empty();
+  EraseNodes();
+  EraseLines(&lines_);
+  default_font_ = NULL;
+}
+
+BOOL StaticEx::SubclassWindow(HWND window) {
+  Reset();
+  // first get text from exising control
+  unsigned length = ::SendMessage(window, WM_GETTEXTLENGTH, 0, 0);
+  CString text;
+  if (length > 0) {
+    TCHAR* buffer = text.GetBufferSetLength(length);
+    ::SendMessage(window,
+                  WM_GETTEXT,
+                  length + 1,
+                  reinterpret_cast<LPARAM>(buffer));
+    text.ReleaseBuffer(-1);
+  }
+
+  // then subclass
+  BOOL result = CWindowImpl<StaticEx>::SubclassWindow(window);
+
+  // set text back (it will parse it and replace text in subclassed control
+  // with readble text)
+  if (result && length > 0) {
+    SetWindowText(text);
+  }
+
+  return result;
+}
+
+HWND StaticEx::UnsubclassWindow(BOOL force /*= FALSE*/) {
+  Reset();  // clean up an old state
+  return CWindowImpl<StaticEx>::UnsubclassWindow(force);
+}
+
+LRESULT StaticEx::OnSetText(UINT msg, WPARAM wparam, LPARAM lparam,
+                            BOOL& handled) {
+  // parse text first, because we will need to get "readable" text
+  text_ = reinterpret_cast<const TCHAR*>(lparam);
+  ParseText();
+
+  // set readable text to subclassed control, this text will be return by
+  // GetWindowText() or WM_GETTEXT. (when GetWindowText is called from another
+  // process it doesn't send WM_GETTEXT but reads text directly from contol)
+  // so we need to set text to it.
+  // Disable redraw, without it calling DefWindowProc would redraw control
+  // immediately without sending WM_PAINT message
+  SetRedraw(FALSE);
+  DefWindowProc(msg, wparam,
+      reinterpret_cast<LPARAM>(static_cast<const TCHAR*>(GetReadableText())));
+  SetRedraw(TRUE);
+
+  // now invalidate to display new text
+  Invalidate();
+
+  handled = TRUE;
+  return 1;
+}
+
+LRESULT StaticEx::OnGetText(UINT, WPARAM wparam, LPARAM lparam, BOOL& handled) {  // NOLINT
+  if (!lparam) return 0;
+  unsigned size = static_cast<unsigned>(wparam);
+  TCHAR* buffer = reinterpret_cast<TCHAR*>(lparam);
+
+  handled = TRUE;
+  unsigned my_size = text_.GetLength();
+  if (my_size < size) {
+    StringCchCopy(buffer, size, text_);
+    return my_size;
+  }
+
+  StringCchCopyN(buffer, size, text_, size - 1);
+  buffer[size - 1] = 0;
+
+  return size - 1;
+}
+
+LRESULT StaticEx::OnGetTextLength(UINT, WPARAM, LPARAM, BOOL& handled) {  // NOLINT
+  handled = TRUE;
+  return text_.GetLength();
+}
+
+void StaticEx::set_background_color(COLORREF back_color) {
+  background_color_ = back_color;
+  use_background_color_ = true;
+  Invalidate();
+}
+
+void StaticEx::set_margins(const RECT& rect) {
+  margins_ = rect;
+  Invalidate();
+}
+
+void StaticEx::set_margins(int left, int top, int right, int bottom) {
+  margins_.SetRect(left, top, right, bottom);
+  Invalidate();
+}
+
+
+LRESULT StaticEx::OnLButtonUp(UINT, WPARAM, LPARAM lparam, BOOL&) {
+  CPoint point(LOWORD(lparam), HIWORD(lparam));
+
+  int height = margins_.top + (border_ & kBorderTop) ? 1 : 0;
+  size_t size = lines_.size();
+  for (size_t i = 0; i < size; i++) {
+    height += lines_[i]->height();
+    if (point.y < height) {
+      CString action;
+      if (lines_[i]->IsUrlUnderMouse(point, &action) && !action.IsEmpty()) {
+        // Notify the parent window to handle the click.
+        LRESULT handled = 0;
+        NMSTATICEX notification = {0};
+        notification.header.hwndFrom = m_hWnd;
+        notification.header.idFrom = GetDlgCtrlID();
+        notification.header.code = NM_STATICEX;
+        notification.action = action;
+
+        HWND parent = GetParent();
+        if (parent) {
+          handled = ::SendMessage(parent, WM_NOTIFY, notification.header.idFrom,
+                                  reinterpret_cast<LPARAM>(&notification));
+          ATLASSERT(handled);
+        }
+      }
+      break;
+    }
+  }
+  return 0;
+}
+
+
+LRESULT StaticEx::OnSetCursor(UINT, WPARAM, LPARAM lparam, BOOL& handled) {  // NOLINT
+  int hit_test = LOWORD(lparam);
+  handled = FALSE;
+  if (hit_test != HTCLIENT) {
+    return 0;
+  }
+
+  POINT position;
+  if (!GetCursorPos(&position))
+    return 0;
+
+  ScreenToClient(&position);
+
+  int offset = margins_.top + (border_ & kBorderTop) ? 1 : 0;
+  size_t size = lines_.size();
+  for (size_t i = 0; i < size; i++) {
+    offset += lines_[i]->height();
+    if (position.y < offset) {
+      if (lines_[i]->IsUrlUnderMouse(position, NULL)) {
+        ::SetCursor(GetHandCursor());
+        handled = TRUE;
+      }
+      break;
+    }
+  }
+
+  return 0;
+}
+
+void StaticEx::set_ellipsis(int ellipsis) {
+  if (ellipsis == DT_END_ELLIPSIS  ||
+      ellipsis == DT_WORD_ELLIPSIS ||
+      ellipsis == DT_PATH_ELLIPSIS ||
+      ellipsis == 0) {
+    ellipsis_ = ellipsis;
+    if (ellipsis != 0)
+      ModifyStyle(0, SS_LEFTNOWORDWRAP);
+    Invalidate();
+  }
+}
+
+void StaticEx::set_border(int border) {
+  border_ = border;
+  Invalidate();
+}
+
+void StaticEx::set_border_color(COLORREF border_color) {
+  border_color_ = border_color;
+  Invalidate();
+}
+
+void StaticEx::ParseText() {
+  EraseNodes();
+  EraseLines(&lines_);
+
+  if (text_.IsEmpty())
+    return;
+
+  if (!default_font_)
+    default_font_ = GetFont();
+
+  NodeState node_state(m_hWnd);
+  node_state.SetStdFont(default_font_);
+
+  const TCHAR* current_string = text_;
+  int current_offset = 0;
+  bool had_good_tag = true;
+  while (*current_string) {
+    current_offset = 0;
+
+    if (had_good_tag) {
+      // if it was a good tag we consumed it and need to start with a new node
+      Node* node = new Node(m_hWnd);
+      nodes_.push_back(node);
+
+      // -1 if there is no Open Bracket "<"
+      current_offset = FindOpenBracket(current_string);
+
+      if (current_offset < 0) {
+        // no tags left, just plain text
+        node->AddText(current_string);
+        node->set_node_state(node_state);
+        break;
+      }
+
+      if (*current_string != _T('<')) {
+        // has some text before the tag
+        node->AddText(CString(current_string, current_offset));
+        node->set_node_state(node_state);
+        current_string += current_offset;
+        continue;
+      }
+
+      int next_offset = node_state.ConsumeTag(current_string + current_offset);
+
+      if (next_offset > 0) {
+        // it was a known tag
+        had_good_tag = true;
+        current_string += current_offset + next_offset;
+      }  else  {
+        // unknown tag, will keep looking
+        had_good_tag = false;
+        node->AddText(CString(current_string, current_offset + 1));
+        node->set_node_state(node_state);
+        current_string += current_offset + 1;
+        continue;
+      }
+    } else {
+      had_good_tag = true;
+    }
+    delete nodes_.back();
+    nodes_.pop_back();
+  }
+}
+
+CString StaticEx::GetReadableText() {
+  CString text;
+  for (size_t i = 0; i < nodes_.size(); i++) {
+    text += nodes_[i]->node_text();
+  }
+  return text;
+}
+
+void StaticEx::EraseNodes() {
+  for (size_t i = 0; i < nodes_.size(); ++i) {
+    delete nodes_[i];
+  }
+  nodes_.clear();
+}
+
+void StaticEx::EraseLines(std::vector<StaticLine*>* lines) {
+  size_t size = lines->size();
+  for (size_t i = 0; i < size; i++) {
+    delete (*lines)[i];
+  }
+  lines->clear();
+}
+
+int StaticEx::FindOpenBracket(const TCHAR* string) {
+  const TCHAR* left_bracket = _tcschr(string, _T('<'));
+
+  if (left_bracket == NULL)
+    return -1;
+
+  return static_cast<int>(left_bracket - string);
+}
+
+LRESULT StaticEx::OnPaint(UINT, WPARAM, LPARAM, BOOL&) {
+  PAINTSTRUCT paint_struct;
+  HDC hdc = BeginPaint(&paint_struct);
+
+  CRect client_rect;
+  GetClientRect(&client_rect);
+
+  CRect working_rect(client_rect);
+
+  working_rect.DeflateRect(margins_);
+  working_rect.DeflateRect((border_ & kBorderLeft)   ? 1 : 0,
+                           (border_ & kBorderTop)    ? 1 : 0,
+                           (border_ & kBorderRight)  ? 1 : 0,
+                           (border_ & kBorderBottom) ? 1 : 0);
+
+  DWORD style = GetStyle();
+
+  EraseLines(&lines_);
+  PrePaint(hdc, &lines_, nodes_, working_rect, style, ellipsis_);
+
+  if (use_background_color_) {
+    FillRect(hdc, &client_rect, CreateSolidBrush(background_color_));
+  } else {
+    HBRUSH brush = reinterpret_cast<HBRUSH>(::SendMessage(GetParent(),
+        WM_CTLCOLORSTATIC, reinterpret_cast<WPARAM>(hdc),
+        reinterpret_cast<LPARAM>(m_hWnd)));
+    if (brush) {
+      ::FillRect(hdc, &client_rect, brush);
+    }
+  }
+
+  if (border_ != kBorderNone)
+    DrawBorder(hdc, client_rect);
+
+  Paint(hdc, lines_, working_rect, style, ellipsis_);
+
+  EndPaint(&paint_struct);
+  return 0;
+}
+
+void StaticEx::PrePaint(HDC hdc, std::vector<StaticLine*>* lines,
+                        const std::vector<Node*>& nodes, RECT rect, DWORD style,
+                        int ellipsis) {
+  if (nodes.empty())
+    return;
+
+  int x = 0;
+  int width = rect.right - rect.left;
+  StaticLine* line = new StaticLine;
+  lines->push_back(line);
+  bool done = false;
+  size_t size = nodes.size();
+  for (size_t i = 0; i < size; ++i) {
+    Node* node = nodes[i];
+    const NodeState& node_state = node->node_state();
+    CString text = node->node_text();
+    int string_len = text.GetLength();
+
+    HFONT font = node_state.GetFont();
+    if (!font)
+      return;
+
+    HFONT old_font = static_cast<HFONT>(SelectObject(hdc, font));
+
+    TEXTMETRIC text_metrics;
+    GetTextMetrics(hdc, &text_metrics);
+
+    int height    = text_metrics.tmHeight + text_metrics.tmExternalLeading;
+    int base_line = text_metrics.tmHeight + text_metrics.tmExternalLeading -
+                    text_metrics.tmDescent;
+    line->AdjustHeight(height);
+    line->AdjustBaseLine(base_line);
+
+    bool single_line = (style & SS_LEFTNOWORDWRAP) != 0;
+
+    int  current_pos = 0;
+    bool more_left   = false;
+    while (true) {
+      int current_length = string_len - current_pos;
+
+      // find LF if any
+      int lf_position = text.Find(_T('\n'), current_pos);
+      if (lf_position == current_pos) {
+        if (single_line) {
+          if (ellipsis)
+            line->AddEllipses();
+          break;
+        }
+        line = new StaticLine;
+        lines->push_back(line);
+        line->AdjustHeight(height);
+        line->AdjustBaseLine(base_line);
+        x = 0;
+
+        current_pos++;
+
+        continue;
+      } else if (lf_position > 0) {
+        current_length = lf_position - current_pos;
+        more_left = true;
+      }
+
+      // check if it will fit in one line
+      int fit  = 0;
+      SIZE string_size;
+      GetTextExtentExPoint(hdc, static_cast<const TCHAR*>(text) + current_pos,
+                           current_length, width - x, &fit, NULL, &string_size);
+
+      if (fit < current_length) {
+        // string doesn't fit, need to move to the next line
+        // find last space
+        int fit_saved = fit;
+        for (; fit > 0; fit--) {
+          if (text.GetAt(current_pos + fit) == _T(' ')) {
+            break;
+          }
+        }
+
+        // if a first word of a node doesn't fit and it starts in a first half
+        // of control then wrap the word on a last char that fits
+        // otherwise move whole node to the next line
+        if ((fit <= 0) && (x < width / 2))
+          fit = fit_saved;
+
+        if (fit > 0) {
+          line->AddNode(node, current_pos, fit, height, base_line, width - x);
+        }
+
+        if (single_line) {
+          if (ellipsis)
+            line->AddEllipses();
+          done = true;
+          break;
+        }
+        line = new StaticLine;
+        lines->push_back(line);
+        line->AdjustHeight(height);
+        line->AdjustBaseLine(base_line);
+        x = 0;
+
+        current_pos += fit;
+        // skip spaces
+        while (text.GetAt(current_pos) == _T(' '))
+          current_pos++;
+        continue;
+      } else {
+        line->AddNode(node, current_pos, fit, height, base_line,
+                      string_size.cx);
+      }
+
+      // done, it fits
+      x += string_size.cx;
+      if (!more_left)
+        break;
+
+      current_pos += current_length;
+      more_left = false;
+    }
+
+    if (old_font)
+      SelectObject(hdc, old_font);
+
+    if (done)
+      break;
+  }
+}
+
+
+void StaticEx::Paint(HDC hdc, const std::vector<StaticLine*>& lines, RECT rect,
+                     DWORD style, int ellipsis) {
+  if ((style & SS_LEFTNOWORDWRAP) == 0) {
+    ellipsis = 0;
+  }
+
+  size_t size = lines.size();
+  int y = rect.top;
+  for (size_t i = 0; i < size; i++) {
+    int height = lines[i]->Paint(hdc, rect.left, rect.right, y, style,
+                                 ellipsis);
+    y += height;
+  }
+}
+
+LRESULT StaticEx::OnEraseBkgnd(UINT /*msg*/, WPARAM /*wparam*/,
+                               LPARAM /*lparam*/, BOOL& handled) {
+  handled = TRUE;
+  return 0;
+}
+
+void StaticEx::DrawBorder(HDC hdc, const CRect& rect) {
+  HGDIOBJ old_object = SelectObject(hdc, GetStockObject(DC_PEN));
+  SetDCPenColor(hdc, border_color_);
+  if (border_ & kBorderLeft) {
+    MoveToEx(hdc, rect.left, rect.top, NULL);
+    LineTo(hdc, rect.left, rect.bottom);
+  }
+  if (border_ & kBorderTop) {
+    MoveToEx(hdc, rect.left, rect.top, NULL);
+    LineTo(hdc, rect.right, rect.top);
+  }
+  if (border_ & kBorderRight) {
+    MoveToEx(hdc, rect.right - 1, rect.top, NULL);
+    LineTo(hdc, rect.right - 1, rect.bottom);
+  }
+  if (border_ & kBorderBottom) {
+    MoveToEx(hdc, rect.left, rect.bottom - 1, NULL);
+    LineTo(hdc, rect.right, rect.bottom - 1);
+  }
+  SelectObject(hdc, old_object);
+}
+
+int StaticEx::GetMinimumHeight(int width) {
+  HDC device_context = CreateCompatibleDC(NULL);
+
+  CRect client_rect;
+  if (width <= 0)
+    GetClientRect(&client_rect);
+  else
+    client_rect.SetRect(0, 0, width, 100);  // last value is not used
+
+  CRect working_rect(client_rect);
+
+  working_rect.DeflateRect(margins_);
+  working_rect.DeflateRect((border_ & kBorderLeft)   ? 1 : 0,
+                           (border_ & kBorderTop)    ? 1 : 0,
+                           (border_ & kBorderRight)  ? 1 : 0,
+                           (border_ & kBorderBottom) ? 1 : 0);
+
+  DWORD style = GetStyle();
+
+  std::vector<StaticLine*> lines;
+  PrePaint(device_context, &lines, nodes_, working_rect, style, ellipsis_);
+
+  DeleteDC(device_context);
+
+  int height = 0;
+  for (unsigned i = 0; i < lines.size(); i++) {
+    height += lines[i]->height();
+  }
+  height += margins_.top + margins_.bottom;
+  height += (border_ & kBorderTop) ? 1 : 0;
+  height += (border_ & kBorderBottom) ? 1 : 0;
+
+  return height;
+}
+
+
+HCURSOR StaticEx::GetHandCursor() {
+  if (hand_cursor_ == NULL) {
+    // Load cursor resource
+    hand_cursor_ = (HCURSOR)LoadCursor(NULL, IDC_HAND);  // doesn't work on NT4!
+  }
+  return hand_cursor_;
+}
diff --git a/ui/uilib/static_ex.h b/ui/uilib/static_ex.h
new file mode 100644
index 0000000..f11d7ea
--- /dev/null
+++ b/ui/uilib/static_ex.h
@@ -0,0 +1,159 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+// static_ex.h : This class extends static control functionality to display
+// formatted text and hyper-links
+//
+// Currently it supports the following formatting options:
+//   bold         - <b>bold</b>
+//   italic       - <i>italic</i>
+//   underscore   - <u>underlined</u>
+//   color        - <color=ff0000>red</color>
+//   size         - <size=14>14 points text</size>
+//   hyperlink    - <a=http://www.google.com>click here</a>
+// formatting options could be nested (except hyperlink)
+//
+// Some fonts (including Tahoma) often overhang one pixel (for example in "W")
+// so StaticEx is created with default 1 pixel margin on the left and right,
+// use set_margins() to overwrite default values if you need to.
+
+
+#ifndef OMAHA_UI_UILIB_STATIC_EX_H_
+#define OMAHA_UI_UILIB_STATIC_EX_H_
+
+#include <windows.h>
+#include <atlbase.h>
+#include <atlwin.h>
+#include <string>
+#include <vector>
+#include "omaha/ui/uilib/node.h"
+#include "omaha/ui/uilib/static_line.h"
+
+
+// Windows control notification codes are all negative, so any positive number
+// should do here. http://goo.gl/NPaBF.
+const UINT NM_STATICEX = NM_FIRST + 0x400;
+
+// extension of NMHDR to provide StaticEx specific info in notification message
+struct NMSTATICEX {
+  NMHDR header;
+  const TCHAR* action;
+};
+
+class StaticEx : public CWindowImpl<StaticEx> {
+ public:
+  DECLARE_WND_SUPERCLASS(NULL, _T("STATIC"))
+
+  StaticEx();
+  virtual ~StaticEx();
+
+  void set_margins(const RECT& rect);
+  void set_margins(int left, int top, int right, int bottom);
+  RECT margins() const { return margins_; }
+
+  void set_background_color(COLORREF back_color);
+  COLORREF background_color() const { return background_color_; }
+  void ResetBackgroundColor() { use_background_color_ = false; }
+
+  // set ellipsis style (DT_END_ELLIPSIS | DT_WORD_ELLIPSIS |DT_PATH_ELLIPSIS)
+  // elipsis are supported only in a single line control, calling this function
+  // with not 0 argument will set control style to SS_LEFTNOWORDWRAP
+  void set_ellipsis(int ellipsis);
+  int ellipsis() const { return ellipsis_; }
+
+  static const int kBorderNone;
+  static const int kBorderLeft;
+  static const int kBorderTop;
+  static const int kBorderRight;
+  static const int kBorderBottom;
+  static const int kBorderAll;
+
+  // use constants above to set border, you can combine them using "|"
+  void set_border(int border);
+  int border() const { return border_; }
+
+  void set_border_color(COLORREF border_color);
+  COLORREF border_color() const { return border_color_; }
+
+  // this function doesn't change how control is shown
+  // it just calculates minimum control height to fit the text given
+  // the control width. if width is 0 it will use current control width
+  int GetMinimumHeight(int width);
+
+  BEGIN_MSG_MAP(StaticEx)
+    MESSAGE_HANDLER(WM_SETTEXT, OnSetText)
+    MESSAGE_HANDLER(kGetTextMessage, OnGetText)
+    MESSAGE_HANDLER(kGetTextLengthMessage, OnGetTextLength)
+    MESSAGE_HANDLER(WM_PAINT, OnPaint)
+    MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
+    MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
+    MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
+  END_MSG_MAP()
+
+  LRESULT OnSetText(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);         // NOLINT
+  // OnGetText and OnGetTextLength work with full text including formatting tags
+  // to get readable text (without formatting info) call GetWindowText or
+  // send WM_GETTEXT
+  LRESULT OnGetText(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);         // NOLINT
+  LRESULT OnGetTextLength(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);   // NOLINT
+
+  LRESULT OnPaint(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);           // NOLINT
+  LRESULT OnEraseBkgnd(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);      // NOLINT
+  LRESULT OnLButtonUp(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);       // NOLINT
+  LRESULT OnSetCursor(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);       // NOLINT
+
+  BOOL SubclassWindow(HWND hWnd);
+  HWND UnsubclassWindow(BOOL bForce = FALSE);
+
+ private:
+  void Reset();
+  void ParseText();
+  CString GetReadableText();
+  int FindOpenBracket(const TCHAR* string);
+  void EraseNodes();
+  void EraseLines(std::vector<StaticLine*>* lines);
+  HFONT default_font() const { return default_font_; }
+
+  void PrePaint(HDC dc, std::vector<StaticLine*>* lines,
+                const std::vector<Node*>& nodes, RECT rect, DWORD style,
+                int ellipsis);
+  void Paint(HDC hdc, const std::vector<StaticLine*>& lines, RECT rect,
+             DWORD style, int ellipsis);
+  void DrawBorder(HDC hdc, const CRect& rect);
+  HCURSOR GetHandCursor();
+
+  CString               text_;
+
+  CRect                 margins_;
+  COLORREF              background_color_;
+  bool                  use_background_color_;
+  int                   ellipsis_;
+  int                   border_;
+  COLORREF              border_color_;
+
+  std::vector<Node*>         nodes_;
+  std::vector<StaticLine*>   lines_;
+
+  HFONT                 default_font_;
+
+  static HCURSOR hand_cursor_;
+
+  static const UINT kGetTextMessage       = WM_APP + 1;
+  static const UINT kGetTextLengthMessage = WM_APP + 2;
+
+  DISALLOW_EVIL_CONSTRUCTORS(StaticEx);
+};
+
+#endif  // OMAHA_UI_UILIB_STATIC_EX_H_
diff --git a/ui/uilib/static_line.cc b/ui/uilib/static_line.cc
new file mode 100644
index 0000000..717e2dc
--- /dev/null
+++ b/ui/uilib/static_line.cc
@@ -0,0 +1,122 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+//
+
+#include "omaha/ui/uilib/static_line.h"
+#include "omaha/ui/uilib/node.h"
+#include "omaha/ui/uilib/static_ex.h"
+
+
+StaticLine::StaticLine()
+    : base_line_(0),
+      height_(0),
+      elipses_(false) {
+}
+
+
+StaticLine::~StaticLine() {
+}
+
+
+int StaticLine::AdjustHeight(int height) {
+  height_ = std::max(height_, height);
+  return height_;
+}
+
+
+int StaticLine::AdjustBaseLine(int base_line) {
+  base_line_ = std::max(base_line_, base_line);
+  return base_line_;
+}
+
+
+void StaticLine::AddNode(Node* node, int start, int length, int height,
+                         int base_line, int width) {
+  nodes_.push_back(Nodes(node, start, length, height, base_line, width));
+  AdjustHeight(height);
+  AdjustBaseLine(base_line);
+}
+
+
+int StaticLine::Paint(HDC hdc, int left, int right, int y, DWORD window_style,
+                      int ellipsis) {
+  int old_bk_mode = SetBkMode(hdc, TRANSPARENT);
+  bool single_line = (window_style & SS_LEFTNOWORDWRAP) != 0;
+
+  size_t size = nodes_.size();
+  for (size_t i = 0; i < size; i++) {
+    Node* node    = nodes_[i].node;
+    int start     = nodes_[i].start;
+    int length    = nodes_[i].length;
+    int base_line = nodes_[i].base_line;
+    int width     = nodes_[i].width;
+
+    CString text(static_cast<LPCTSTR>(node->node_text()) + start, length);
+    if (elipses_ && (i == (size - 1)))
+      text += "...";
+
+    CRect rect(left, y + base_line_ - base_line, left + width, y + height_);
+    if (single_line)
+      rect.right = right;
+
+    nodes_[i].rect = rect;
+
+    const NodeState& nodeState = node->node_state();
+    HFONT font = nodeState.GetFont();
+    if (!font)
+      return height_;
+
+    HFONT old_font = static_cast<HFONT>(SelectObject(hdc, font));
+    COLORREF old_text_color = SetTextColor(hdc, nodeState.text_color());
+
+    DWORD draw_style = 0;
+    draw_style = DT_LEFT | DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE;
+    if (ellipsis && (i == (size - 1)))
+      draw_style = draw_style | ellipsis;
+
+    DrawText(hdc, text, text.GetLength(), rect, draw_style);
+    left += width;
+
+    SetTextColor(hdc, old_text_color);
+    if (old_font)
+      SelectObject(hdc, old_font);
+  }
+
+  SetBkMode(hdc, old_bk_mode);
+  return height_;
+}
+
+
+int StaticLine::HitTest(CPoint point) {
+  size_t size = nodes_.size();
+  for (size_t i = 0; i < size; i++) {
+    if (nodes_[i].node->node_state().IsURL()) {
+      if (nodes_[i].rect.PtInRect(point)) {
+        return static_cast<int>(i);
+      }
+    }
+  }
+
+  return -1;
+}
+
+
+bool StaticLine::IsUrlUnderMouse(CPoint point, CString* action) {
+  int index = HitTest(point);
+  if (index >= 0 && action) {
+    *action = nodes_[index].node->node_state().url();
+  }
+  return (index >= 0);
+}
diff --git a/ui/uilib/static_line.h b/ui/uilib/static_line.h
new file mode 100644
index 0000000..4802dca
--- /dev/null
+++ b/ui/uilib/static_line.h
@@ -0,0 +1,76 @@
+// Copyright 2006-2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+//
+// static_line.h
+
+#ifndef OMAHA_UI_UILIB_STATIC_LINE_H_
+#define OMAHA_UI_UILIB_STATIC_LINE_H_
+
+#include <atlstr.h>
+#include <atltypes.h>
+#include <vector>
+#include "base/basictypes.h"
+
+class Node;
+
+class StaticLine {
+ public:
+  StaticLine();
+  virtual ~StaticLine();
+
+  int AdjustBaseLine(int base_line);
+  int AdjustHeight(int height);
+
+  int base_line() const { return base_line_; }
+  int height() const { return height_; }
+
+  void AddNode(Node* node, int start, int end, int height, int base_line,
+               int width);
+  void AddEllipses() { elipses_ = true; }
+
+  bool IsUrlUnderMouse(CPoint point, CString* action);
+
+  int  Paint(HDC hdc, int left, int right, int y, DWORD window_style,
+             int ellipsis);
+
+ protected:
+  int     HitTest(CPoint point);
+
+  int   base_line_;
+  int   height_;
+
+  struct Nodes {
+    Node*   node;
+    int     start;     // first char to output
+    int     length;    // number of chars
+    int     height;
+    int     base_line;
+    int     width;
+    CRect   rect;
+
+    Nodes(Node* node, int start, int length, int height, int base_line,
+          int width)
+        : node(node), start(start), length(length), height(height),
+          base_line(base_line), width(width), rect(0, 0, 0, 0) {}
+  };
+
+  std::vector<Nodes>   nodes_;
+  bool            elipses_;
+
+  DISALLOW_EVIL_CONSTRUCTORS(StaticLine);
+};
+
+#endif  // OMAHA_UI_UILIB_STATIC_LINE_H_
diff --git a/ui/yes_no_dialog.cc b/ui/yes_no_dialog.cc
new file mode 100644
index 0000000..393a91d
--- /dev/null
+++ b/ui/yes_no_dialog.cc
@@ -0,0 +1,149 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include "omaha/ui/yes_no_dialog.h"
+#include "base/basictypes.h"
+#include "omaha/base/debug.h"
+#include "omaha/base/error.h"
+#include "omaha/base/logging.h"
+#include "omaha/base/window_utils.h"
+#include "omaha/google_update/resource.h"
+
+namespace omaha {
+
+YesNoDialog::YesNoDialog(CMessageLoop* message_loop, HWND parent)
+    :  message_loop_(message_loop),
+       parent_(parent),
+       yes_clicked_(false) {
+  ASSERT1(message_loop);
+  CORE_LOG(L3, (_T("[YesNoDialog::YesNoDialog]")));
+}
+
+
+YesNoDialog::~YesNoDialog() {
+  CORE_LOG(L3, (_T("[YesNoDialog::~YesNoDialog]")));
+  ASSERT1(!IsWindow());
+}
+
+HRESULT YesNoDialog::Initialize(const CString& yes_no_title,
+                                const CString& yes_no_text) {
+  ASSERT1(!IsWindow());
+
+  if (!Create(parent_)) {
+    CORE_LOG(LE, (_T("[Failed to create YesNoDialog]")));
+    return GOOPDATE_E_UI_INTERNAL_ERROR;
+  }
+
+  VERIFY1(message_loop_->AddMessageFilter(this));
+
+  VERIFY1(SetWindowText(yes_no_title));
+  VERIFY1(::SetWindowText(GetDlgItem(IDC_YES_NO_TEXT), yes_no_text));
+
+  CString yes;
+  VERIFY1(yes.LoadString(IDS_YES));
+  CString no;
+  VERIFY1(no.LoadString(IDS_NO));
+
+  VERIFY1(::SetWindowText(GetDlgItem(IDOK), yes));
+  VERIFY1(::SetWindowText(GetDlgItem(IDCANCEL), no));
+
+  HRESULT hr = WindowUtils::SetWindowIcon(m_hWnd, IDI_APP, address(hicon_));
+  if (FAILED(hr)) {
+    CORE_LOG(LW, (_T("[Failed to SetWindowIcon][0x%x]"), hr));
+  }
+
+  return S_OK;
+}
+
+HRESULT YesNoDialog::Show() {
+  ASSERT1(IsWindow());
+  ASSERT1(!IsWindowVisible());
+
+  VERIFY1(CenterWindow(NULL));
+  ShowWindow(SW_SHOWNORMAL);
+
+  return S_OK;
+}
+
+LRESULT YesNoDialog::OnClickedButton(WORD notify_code,
+                                     WORD id,
+                                     HWND wnd_ctl,
+                                     BOOL& handled) {   // NOLINT
+  UNREFERENCED_PARAMETER(notify_code);
+  UNREFERENCED_PARAMETER(wnd_ctl);
+
+  CORE_LOG(L3, (_T("[YesNoDialog::OnClickedButton]")));
+  ASSERT1(id == IDOK || id == IDCANCEL);
+
+  #pragma warning(push)
+  // C4061: enumerator 'xxx' in switch of enum 'yyy' is not explicitly handled
+  // by a case label.
+  #pragma warning(disable : 4061)
+
+  switch (id) {
+    case IDOK:
+      yes_clicked_ = true;
+      break;
+
+    case IDCANCEL:
+      yes_clicked_ = false;
+      break;
+
+    default:
+      ASSERT1(false);
+      yes_clicked_ = false;
+      break;
+  }
+  #pragma warning(pop)
+
+  handled = true;
+  SendMessage(WM_CLOSE, 0, 0);
+
+  return 0;
+}
+
+LRESULT YesNoDialog::OnClose(UINT message,
+                              WPARAM wparam,
+                              LPARAM lparam,
+                              BOOL& handled) {
+  UNREFERENCED_PARAMETER(message);
+  UNREFERENCED_PARAMETER(wparam);
+  UNREFERENCED_PARAMETER(lparam);
+
+  DestroyWindow();
+
+  handled = TRUE;
+  return 0;
+}
+
+LRESULT YesNoDialog::OnNCDestroy(UINT message,
+                                 WPARAM wparam,
+                                 LPARAM lparam,
+                                 BOOL& handled) {
+  UNREFERENCED_PARAMETER(message);
+  UNREFERENCED_PARAMETER(wparam);
+  UNREFERENCED_PARAMETER(lparam);
+
+  CORE_LOG(L3, (_T("[YesNoDialog::OnNCDestroy]")));
+  VERIFY1(message_loop_->RemoveMessageFilter(this));
+
+  ::PostQuitMessage(0);
+
+  handled = FALSE;  // Let ATL default processing handle the WM_NCDESTROY.
+  return 0;
+}
+
+}  // namespace omaha
+
diff --git a/ui/yes_no_dialog.h b/ui/yes_no_dialog.h
new file mode 100644
index 0000000..97214d9
--- /dev/null
+++ b/ui/yes_no_dialog.h
@@ -0,0 +1,85 @@
+// Copyright 2011 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#ifndef OMAHA_UI_YES_NO_DIALOG_H_
+#define OMAHA_UI_YES_NO_DIALOG_H_
+
+#include "omaha/base/scoped_any.h"
+#include "omaha/base/wtl_atlapp_wrapper.h"
+#include "omaha/client/resource.h"
+
+namespace omaha {
+
+class YesNoDialog
+    : public CAxDialogImpl<YesNoDialog>,
+      public CMessageFilter {
+  typedef CAxDialogImpl<YesNoDialog> Base;
+
+ public:
+  static const int IDD = IDD_YES_NO;
+
+  YesNoDialog(CMessageLoop* message_loop, HWND parent);
+  ~YesNoDialog();
+
+  HRESULT Initialize(const CString& yes_no_title,
+                     const CString& yes_no_text);
+  HRESULT Show();
+
+  bool yes_clicked() const {
+    return yes_clicked_;
+  }
+
+  // CMessageFilter interface method.
+  BOOL PreTranslateMessage(MSG* msg) {
+    return CWindow::IsDialogMessage(msg);
+  }
+
+  BEGIN_MSG_MAP(YesNoDialog)
+    COMMAND_HANDLER(IDOK, BN_CLICKED, OnClickedButton)
+    COMMAND_ID_HANDLER(IDCANCEL, OnClickedButton)
+    MESSAGE_HANDLER(WM_CLOSE, OnClose)
+    MESSAGE_HANDLER(WM_NCDESTROY, OnNCDestroy)
+    CHAIN_MSG_MAP(Base)
+  END_MSG_MAP()
+
+ private:
+  // Message and command handlers.
+  LRESULT OnClickedButton(WORD notify_code,
+                          WORD id,
+                          HWND wnd_ctl,
+                          BOOL& handled);  // NOLINT(runtime/references)
+  LRESULT OnClose(UINT msg,
+                  WPARAM wparam,
+                  LPARAM lparam,
+                  BOOL& handled);  // NOLINT(runtime/references)
+  LRESULT OnNCDestroy(UINT msg,
+                      WPARAM wparam,
+                      LPARAM lparam,
+                      BOOL& handled);  // NOLINT(runtime/references)
+
+  CMessageLoop* message_loop_;
+  HWND parent_;
+  bool yes_clicked_;
+
+  // Handle to large icon to show when ALT-TAB.
+  scoped_hicon hicon_;
+
+  DISALLOW_COPY_AND_ASSIGN(YesNoDialog);
+};
+
+}  // namespace omaha
+
+#endif  // OMAHA_UI_YES_NO_DIALOG_H_
+
diff --git a/ui/yes_no_dialog_unittest.cc b/ui/yes_no_dialog_unittest.cc
new file mode 100644
index 0000000..3acd7f4
--- /dev/null
+++ b/ui/yes_no_dialog_unittest.cc
@@ -0,0 +1,56 @@
+// Copyright 2010 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ========================================================================
+
+#include <windows.h>
+#include "omaha/base/app_util.h"
+#include "omaha/goopdate/resource_manager.h"
+#include "omaha/testing/unit_test.h"
+#include "omaha/ui/yes_no_dialog.h"
+
+namespace omaha {
+
+class YesNoDialogTest : public testing::Test {
+ protected:
+  YesNoDialogTest() {}
+
+  static void SetUpTestCase() {
+    CString resource_dir = app_util::GetModuleDirectory(NULL);
+    EXPECT_HRESULT_SUCCEEDED(
+        ResourceManager::Create(false, resource_dir, _T("en")));
+  }
+
+  static void TearDownTestCase() {
+    ResourceManager::Delete();
+  }
+
+  static void SendCloseMessage(const YesNoDialog& yes_no_dialog) {
+    EXPECT_TRUE(yes_no_dialog.IsWindow());
+    ::SendMessage(yes_no_dialog.m_hWnd, WM_CLOSE, 0, 0);
+  }
+};
+
+TEST_F(YesNoDialogTest, YesNoDialog) {
+  CString title(_T("YesNoDialog"));
+  CString text(_T("This is a test. Continue?"));
+
+  CMessageLoop message_loop;
+  YesNoDialog yes_no_dialog(&message_loop, NULL);
+  EXPECT_SUCCEEDED(yes_no_dialog.Initialize(title, text));
+  EXPECT_SUCCEEDED(yes_no_dialog.Show());
+  YesNoDialogTest::SendCloseMessage(yes_no_dialog);
+}
+
+}   // namespace omaha
+
diff --git a/worker/app_request.h b/worker/app_request.h
deleted file mode 100644
index 49262ab..0000000
--- a/worker/app_request.h
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// app_request.h:  Class that encapsulates a product-level request for
-// install/update that is converted into the XML that goes to the server.  Will
-// hold one AppRequestData for the main product and 0..n AppRequestData objects
-// in a list for the components of the product that we know/care about.
-
-#ifndef OMAHA_WORKER_APP_REQUEST_H__
-#define OMAHA_WORKER_APP_REQUEST_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include <vector>
-#include "base/basictypes.h"
-#include "omaha/common/debug.h"
-#include "omaha/worker/app_request_data.h"
-
-namespace omaha {
-
-class AppRequest {
- public:
-  AppRequest() {}
-  explicit AppRequest(const AppRequestData& request_data) {
-    request_data_ = request_data;
-  }
-  ~AppRequest() {}
-
-  void set_request_data(const AppRequestData& request_data) {
-    request_data_ = request_data;
-  }
-  const AppRequestData& request_data() const { return request_data_; }
-  void AddComponentRequest(const AppRequestData& component_data) {
-    components_.push_back(component_data);
-  }
-
-  AppRequestDataVector::const_iterator components_begin() const {
-    return components_.begin();
-  }
-
-  AppRequestDataVector::const_iterator components_end() const {
-    return components_.end();
-  }
-
-  size_t num_components() const { return components_.size(); }
-
- private:
-  AppRequestData request_data_;
-  AppRequestDataVector components_;
-};
-
-typedef std::vector<AppRequest> AppRequestVector;
-
-}  // namespace omaha.
-
-#endif  // OMAHA_WORKER_APP_REQUEST_H__
-
diff --git a/worker/app_request_data.h b/worker/app_request_data.h
deleted file mode 100644
index 3607b2d..0000000
--- a/worker/app_request_data.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// app_request_data.h:  The app or component level data required to generate the
-// XML that asks the server about that app or component.  Also used for pings in
-// addition to install/update requests.  These are contained within AppRequest
-// objects to represent the full product hierarchy.
-
-#ifndef OMAHA_WORKER_APP_REQUEST_DATA_H__
-#define OMAHA_WORKER_APP_REQUEST_DATA_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include <vector>
-#include "base/basictypes.h"
-#include "omaha/common/debug.h"
-#include "omaha/worker/application_data.h"
-#include "omaha/worker/ping_event.h"
-
-namespace omaha {
-
-class AppRequestData {
- public:
-  AppRequestData() {}
-  explicit AppRequestData(const AppData& app_data) {
-    app_data_ = app_data;
-  }
-
-  void set_app_data(const AppData& app_data) {
-    app_data_ = app_data;
-  }
-  const AppData& app_data() const { return app_data_; }
-
-  void AddPingEvent(const PingEvent& ping_event) {
-    ping_events_.push_back(ping_event);
-  }
-
-  PingEventVector::const_iterator ping_events_begin() const {
-    return ping_events_.begin();
-  }
-
-  PingEventVector::const_iterator ping_events_end() const {
-    return ping_events_.end();
-  }
-
-  size_t num_ping_events() const { return ping_events_.size(); }
-
- private:
-  AppData app_data_;
-  PingEventVector ping_events_;
-};
-
-typedef std::vector<AppRequestData> AppRequestDataVector;
-
-}  // namespace omaha.
-
-#endif  // OMAHA_WORKER_APP_REQUEST_DATA_H__
-
diff --git a/worker/application_data.h b/worker/application_data.h
deleted file mode 100644
index 7f0e513..0000000
--- a/worker/application_data.h
+++ /dev/null
@@ -1,236 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// application_data.h: Class encapsulates the application registration
-// and state information.
-
-#ifndef OMAHA_WORKER_APPLICATION_DATA_H__
-#define OMAHA_WORKER_APPLICATION_DATA_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include <vector>
-#include "base/basictypes.h"
-#include "omaha/common/browser_utils.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/reg_key.h"
-
-namespace omaha {
-
-// Encapsulates all the knowledge about the application. All the code inside
-// omaha should use this class to query and update information associated with
-// applications.
-// This class represents a snapshot of the information in the registry. This
-// information could have changed after this snap short has been taken.
-// Implementation notes:
-// 1. Think about synchronizing access between two workers.
-// 2. The code should be able to get the values for params that are present
-//    inside client_state. I.e. we should not read from only clients. This is
-//    important for language.
-
-class AppData {
- public:
-  enum ActiveStates {
-    ACTIVE_NOTRUN = 0,
-    ACTIVE_RUN,
-    ACTIVE_UNKNOWN
-  };
-
-  AppData()
-      : app_guid_(GUID_NULL),
-        parent_app_guid_(GUID_NULL),
-        is_machine_app_(false),
-        iid_(GUID_NULL),
-        install_time_diff_sec_(0),
-        is_oem_install_(false),
-        is_eula_accepted_(true),  // Safe default.
-        browser_type_(BROWSER_UNKNOWN),
-        usage_stats_enable_(TRISTATE_NONE),
-        did_run_(ACTIVE_UNKNOWN),
-        days_since_last_active_ping_(0),
-        days_since_last_roll_call_(0),
-        is_uninstalled_(false),
-        is_update_disabled_(false) { }
-
-  AppData(const GUID& app_guid, bool is_machine_app)
-      : app_guid_(app_guid),
-        parent_app_guid_(GUID_NULL),
-        is_machine_app_(is_machine_app),
-        iid_(GUID_NULL),
-        install_time_diff_sec_(0),
-        is_oem_install_(false),
-        is_eula_accepted_(true),  // Safe default.
-        browser_type_(BROWSER_UNKNOWN),
-        usage_stats_enable_(TRISTATE_NONE),
-        did_run_(ACTIVE_UNKNOWN),
-        days_since_last_active_ping_(0),
-        days_since_last_roll_call_(0),
-        is_uninstalled_(false),
-        is_update_disabled_(false) { }
-
-  GUID app_guid() const { return app_guid_; }
-  void set_app_guid(const GUID& guid) { app_guid_ = guid; }
-
-  GUID parent_app_guid() const { return parent_app_guid_; }
-  void set_parent_app_guid(const GUID& guid) { parent_app_guid_ = guid; }
-
-  bool is_machine_app() const { return is_machine_app_; }
-  void set_is_machine_app(bool is_machine_app) {
-    is_machine_app_ = is_machine_app;
-  }
-
-  CString version() const { return version_; }
-  void set_version(const CString version) { version_ = version; }
-
-  CString previous_version() const { return previous_version_; }
-  void set_previous_version(const CString& previous_version) {
-    previous_version_ = previous_version;
-  }
-
-  CString language() const { return language_; }
-  void set_language(const CString& language) { language_ = language; }
-
-  CString ap() const { return ap_; }
-  void set_ap(const CString& ap) { ap_ = ap; }
-
-  CString tt_token() const { return tt_token_; }
-  void set_tt_token(const CString& tt_token) { tt_token_ = tt_token; }
-
-  GUID iid() const { return iid_; }
-  void set_iid(const GUID& iid) { iid_ = iid; }
-
-  CString brand_code() const { return brand_code_; }
-  void set_brand_code(const CString& brand_code) { brand_code_ = brand_code; }
-
-  CString client_id() const { return client_id_; }
-  void set_client_id(const CString& client_id) { client_id_ = client_id; }
-
-  CString referral_id() const { return referral_id_; }
-  void set_referral_id(const CString& referral_id) {
-      referral_id_ = referral_id;
-  }
-
-  uint32 install_time_diff_sec() const { return install_time_diff_sec_; }
-  void set_install_time_diff_sec(uint32 install_time_diff_sec) {
-      install_time_diff_sec_ = install_time_diff_sec;
-  }
-
-  bool is_oem_install() const { return is_oem_install_; }
-  void set_is_oem_install(bool is_oem_install) {
-    is_oem_install_ = is_oem_install;
-  }
-
-  bool is_eula_accepted() const { return is_eula_accepted_; }
-  void set_is_eula_accepted(bool is_eula_accepted) {
-    is_eula_accepted_ = is_eula_accepted;
-  }
-
-  CString display_name() const { return display_name_; }
-  void set_display_name(const CString& display_name) {
-    display_name_ = display_name;
-  }
-
-  BrowserType browser_type() const { return browser_type_; }
-  void set_browser_type(BrowserType type) { browser_type_ = type; }
-
-  CString install_source() const { return install_source_; }
-  void set_install_source(const CString& install_source) {
-    install_source_ = install_source;
-  }
-
-  CString encoded_installer_data() const { return encoded_installer_data_; }
-  void set_encoded_installer_data(const CString& encoded_installer_data) {
-    encoded_installer_data_ = encoded_installer_data;
-  }
-
-  CString install_data_index() const { return install_data_index_; }
-  void set_install_data_index(const CString& install_data_index) {
-    install_data_index_ = install_data_index;
-  }
-
-  Tristate usage_stats_enable() const { return usage_stats_enable_; }
-  void set_usage_stats_enable(Tristate usage_stats_enable) {
-    usage_stats_enable_ = usage_stats_enable;
-  }
-
-  ActiveStates did_run() const { return did_run_; }
-  void set_did_run(AppData::ActiveStates did_run) {
-    did_run_ = did_run;
-  }
-
-  int days_since_last_active_ping() const {
-    return days_since_last_active_ping_;
-  }
-  int set_days_since_last_active_ping(int days) {
-    return days_since_last_active_ping_ = days;
-  }
-
-  int days_since_last_roll_call() const {
-    return days_since_last_roll_call_;
-  }
-  int set_days_since_last_roll_call(int days) {
-    return days_since_last_roll_call_ = days;
-  }
-
-  bool is_uninstalled() const { return is_uninstalled_; }
-  void set_is_uninstalled(bool is_uninstalled) {
-    is_uninstalled_ = is_uninstalled;
-  }
-
-  bool is_update_disabled() const { return is_update_disabled_; }
-  void set_is_update_disabled(bool is_update_disabled) {
-    is_update_disabled_ = is_update_disabled;
-  }
-
- private:
-  GUID app_guid_;
-  GUID parent_app_guid_;
-  bool is_machine_app_;
-
-  CString version_;
-  CString previous_version_;
-  CString language_;
-
-  CString ap_;
-  CString tt_token_;
-  GUID iid_;
-  CString brand_code_;
-  CString client_id_;
-  CString referral_id_;
-  uint32 install_time_diff_sec_;
-  bool is_oem_install_;
-  bool is_eula_accepted_;
-
-  CString display_name_;
-  BrowserType browser_type_;
-  CString install_source_;
-  CString encoded_installer_data_;
-  CString install_data_index_;
-  Tristate usage_stats_enable_;
-
-  ActiveStates did_run_;
-  int days_since_last_active_ping_;
-  int days_since_last_roll_call_;
-
-  bool is_uninstalled_;
-  bool is_update_disabled_;
-};
-
-typedef std::vector<AppData> AppDataVector;
-
-}  // namespace omaha.
-
-#endif  // OMAHA_WORKER_APPLICATION_DATA_H__
diff --git a/worker/application_data_unittest.cc b/worker/application_data_unittest.cc
deleted file mode 100644
index 8322514..0000000
--- a/worker/application_data_unittest.cc
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// ApplicationData unit tests
-
-#include <vector>
-#include "base/scoped_ptr.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/time.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/testing/unit_test.h"
-#include "omaha/worker/application_data.h"
-#include "omaha/worker/job.h"
-
-namespace omaha {
-
-const TCHAR* const kGuid1 = _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}");
-const TCHAR* const kGuid2 = _T("{A979ACBD-1F55-4b12-A35F-4DBCA5A7CCB8}");
-const TCHAR* const kGuid3 = _T("{661045C5-4429-4140-BC48-8CEA241D1DEF}");
-
-  void ValidateDefaltValues(const AppData& data) {
-    EXPECT_TRUE(::IsEqualGUID(GUID_NULL, data.app_guid()));
-    EXPECT_TRUE(::IsEqualGUID(GUID_NULL, data.parent_app_guid()));
-    EXPECT_FALSE(data.is_machine_app());
-    EXPECT_TRUE(data.version().IsEmpty());
-    EXPECT_TRUE(data.previous_version().IsEmpty());
-    EXPECT_TRUE(data.language().IsEmpty());
-    EXPECT_TRUE(data.ap().IsEmpty());
-    EXPECT_TRUE(data.tt_token().IsEmpty());
-    EXPECT_TRUE(::IsEqualGUID(GUID_NULL, data.iid()));
-    EXPECT_TRUE(data.brand_code().IsEmpty());
-    EXPECT_TRUE(data.client_id().IsEmpty());
-    EXPECT_TRUE(data.referral_id().IsEmpty());
-    EXPECT_EQ(0, data.install_time_diff_sec());
-    EXPECT_FALSE(data.is_oem_install());
-    EXPECT_TRUE(data.is_eula_accepted());
-    EXPECT_TRUE(data.display_name().IsEmpty());
-    EXPECT_EQ(BROWSER_UNKNOWN, data.browser_type());
-    EXPECT_TRUE(data.install_source().IsEmpty());
-    EXPECT_TRUE(data.encoded_installer_data().IsEmpty());
-    EXPECT_TRUE(data.install_data_index().IsEmpty());
-    EXPECT_EQ(TRISTATE_NONE, data.usage_stats_enable());
-    EXPECT_EQ(AppData::ACTIVE_UNKNOWN, data.did_run());
-    EXPECT_EQ(0, data.days_since_last_active_ping());
-    EXPECT_EQ(0, data.days_since_last_roll_call());
-    EXPECT_FALSE(data.is_uninstalled());
-    EXPECT_FALSE(data.is_update_disabled());
-  }
-
-  void ValidateExpectedValues(const AppData& expected, const AppData& actual) {
-    EXPECT_STREQ(GuidToString(expected.app_guid()),
-                 GuidToString(actual.app_guid()));
-    EXPECT_STREQ(GuidToString(expected.parent_app_guid()),
-                 GuidToString(actual.parent_app_guid()));
-    EXPECT_EQ(expected.is_machine_app(), actual.is_machine_app());
-    EXPECT_STREQ(expected.version(), actual.version());
-    EXPECT_STREQ(expected.previous_version(), actual.previous_version());
-    EXPECT_STREQ(expected.language(), actual.language());
-    EXPECT_STREQ(expected.ap(), actual.ap());
-    EXPECT_STREQ(expected.tt_token(), actual.tt_token());
-    EXPECT_STREQ(GuidToString(expected.iid()), GuidToString(actual.iid()));
-    EXPECT_STREQ(expected.brand_code(), actual.brand_code());
-    EXPECT_STREQ(expected.client_id(), actual.client_id());
-    EXPECT_STREQ(expected.referral_id(), actual.referral_id());
-    EXPECT_EQ(expected.install_time_diff_sec(), actual.install_time_diff_sec());
-    EXPECT_EQ(expected.is_oem_install(), actual.is_oem_install());
-    EXPECT_EQ(expected.is_eula_accepted(), actual.is_eula_accepted());
-    EXPECT_STREQ(expected.display_name(), actual.display_name());
-    EXPECT_EQ(expected.browser_type(), actual.browser_type());
-    EXPECT_STREQ(expected.install_source(), actual.install_source());
-    EXPECT_STREQ(expected.encoded_installer_data(),
-                 actual.encoded_installer_data());
-    EXPECT_STREQ(expected.install_data_index(), actual.install_data_index());
-    EXPECT_EQ(expected.usage_stats_enable(), actual.usage_stats_enable());
-    EXPECT_EQ(expected.did_run(), actual.did_run());
-    EXPECT_EQ(expected.days_since_last_active_ping(),
-              actual.days_since_last_active_ping());
-    EXPECT_EQ(expected.days_since_last_roll_call(),
-              actual.days_since_last_roll_call());
-    EXPECT_EQ(expected.is_uninstalled(), actual.is_uninstalled());
-    EXPECT_EQ(expected.is_update_disabled(), actual.is_update_disabled());
-  }
-
-void FillAppData(AppData* app_data) {
-  const GUID app_id = StringToGuid(kGuid1);
-  const bool is_machine_app = true;
-  const GUID parent_app_id = StringToGuid(kGuid2);
-  const CString version = _T("12345");
-  const CString previous_version = _T("11111");
-  const CString language = _T("en");
-  const AppData::ActiveStates did_run = AppData::ACTIVE_RUN;
-  const int days_since_last_active_ping = 2;
-  const int days_since_last_roll_call = 1;
-  const CString ap = _T("some_ap_value");
-  const GUID iid = StringToGuid(kGuid3);
-  const CString brand_code = _T("GOOG");
-  const CString client_id = _T("some_client_id");
-  const CString referral_id = _T("ABC987");
-  const uint32 install_time_diff_sec = 98765;
-  const bool is_oem_install = true;
-  const bool is_eula_accepted = false;
-  const CString encoded_installer_data = _T("%20foobar");
-  const CString install_data_index = _T("foobar");
-  const bool is_uninstalled = true;
-  const bool is_update_disabled = false;
-
-  AppData actual(app_id, is_machine_app);
-  app_data->set_parent_app_guid(parent_app_id);
-  app_data->set_version(version);
-  app_data->set_previous_version(previous_version);
-  app_data->set_language(language);
-  app_data->set_did_run(did_run);
-  app_data->set_days_since_last_active_ping(days_since_last_active_ping);
-  app_data->set_days_since_last_roll_call(days_since_last_roll_call);
-  app_data->set_ap(ap);
-  app_data->set_iid(iid);
-  app_data->set_brand_code(brand_code);
-  app_data->set_client_id(client_id);
-  app_data->set_referral_id(referral_id);
-  app_data->set_install_time_diff_sec(install_time_diff_sec);
-  app_data->set_is_oem_install(is_oem_install);
-  app_data->set_is_eula_accepted(is_eula_accepted);
-  app_data->set_encoded_installer_data(encoded_installer_data);
-  app_data->set_install_data_index(install_data_index);
-  app_data->set_is_uninstalled(is_uninstalled);
-  app_data->set_is_update_disabled(is_update_disabled);
-}
-
-TEST(AppDataTest, TestAllParams) {
-  const GUID app_guid = StringToGuid(kGuid1);
-  const bool is_machine_app = true;
-  const GUID parent_app_guid = StringToGuid(kGuid2);
-  const CString version = _T("12345");
-  const CString previous_version = _T("11111");
-  const CString language = _T("en");
-  const AppData::ActiveStates did_run = AppData::ACTIVE_RUN;
-  const int days_since_last_active_ping = 3;
-  const int days_since_last_roll_call = 2;
-  const CString ap = _T("some_ap_value");
-  const CString tt_token = _T("some_tt_token_value");
-  const GUID iid = StringToGuid(kGuid3);
-  const CString brand_code = _T("GOOG");
-  const CString client_id = _T("some_client_id");
-  const CString referral_id = _T("123456");
-  const uint32 install_time_diff_sec = 123498;
-  const bool is_oem_install = true;
-  const bool is_eula_accepted = false;
-  const CString encoded_installer_data = _T("%20foobar");
-  const CString install_data_index = _T("foobar");
-  const bool is_uninstalled = true;
-  const bool is_update_disabled = false;
-
-  AppData actual(app_guid, is_machine_app);
-  actual.set_parent_app_guid(parent_app_guid);
-  actual.set_version(version);
-  actual.set_previous_version(previous_version);
-  actual.set_language(language);
-  actual.set_did_run(did_run);
-  actual.set_days_since_last_active_ping(days_since_last_active_ping);
-  actual.set_days_since_last_roll_call(days_since_last_roll_call);
-  actual.set_ap(ap);
-  actual.set_tt_token(tt_token);
-  actual.set_iid(iid);
-  actual.set_brand_code(brand_code);
-  actual.set_client_id(client_id);
-  actual.set_referral_id(referral_id);
-  actual.set_install_time_diff_sec(install_time_diff_sec);
-  actual.set_is_oem_install(is_oem_install);
-  actual.set_is_eula_accepted(is_eula_accepted);
-  actual.set_encoded_installer_data(encoded_installer_data);
-  actual.set_install_data_index(install_data_index);
-  actual.set_is_uninstalled(is_uninstalled);
-  actual.set_is_update_disabled(is_update_disabled);
-
-  EXPECT_TRUE(::IsEqualGUID(app_guid, actual.app_guid()));
-  EXPECT_TRUE(::IsEqualGUID(parent_app_guid, actual.parent_app_guid()));
-  EXPECT_EQ(is_machine_app, actual.is_machine_app());
-  EXPECT_STREQ(version, actual.version());
-  EXPECT_STREQ(previous_version, actual.previous_version());
-  EXPECT_STREQ(language, actual.language());
-  EXPECT_EQ(did_run, actual.did_run());
-  EXPECT_EQ(days_since_last_active_ping, actual.days_since_last_active_ping());
-  EXPECT_EQ(days_since_last_roll_call, actual.days_since_last_roll_call());
-  EXPECT_STREQ(ap, actual.ap());
-  EXPECT_STREQ(tt_token, actual.tt_token());
-  EXPECT_TRUE(::IsEqualGUID(iid, actual.iid()));
-  EXPECT_STREQ(brand_code, actual.brand_code());
-  EXPECT_STREQ(client_id, actual.client_id());
-  EXPECT_STREQ(referral_id, actual.referral_id());
-  EXPECT_EQ(install_time_diff_sec, actual.install_time_diff_sec());
-  EXPECT_EQ(is_oem_install, actual.is_oem_install());
-  EXPECT_EQ(is_eula_accepted, actual.is_eula_accepted());
-  EXPECT_STREQ(encoded_installer_data, actual.encoded_installer_data());
-  EXPECT_STREQ(install_data_index, actual.install_data_index());
-  EXPECT_EQ(is_uninstalled, actual.is_uninstalled());
-  EXPECT_EQ(is_update_disabled, actual.is_update_disabled());
-}
-
-TEST(AppDataTest, TestInitialized) {
-  AppData app_data1;
-  ValidateDefaltValues(app_data1);
-
-  AppData app_data2(GUID_NULL, false);
-  ValidateDefaltValues(app_data2);
-}
-
-TEST(AppDataTest, TestAssignment) {
-  AppData app_data;
-  FillAppData(&app_data);
-  AppData app_data2;
-  app_data2 = app_data;
-
-  ValidateExpectedValues(app_data, app_data2);
-}
-
-TEST(AppDataTest, TestCopyConstructor) {
-  AppData app_data;
-  FillAppData(&app_data);
-  AppData app_data2(app_data);
-
-  ValidateExpectedValues(app_data, app_data2);
-}
-
-}  // namespace omaha
diff --git a/worker/application_manager.cc b/worker/application_manager.cc
deleted file mode 100644
index d6f0b6c..0000000
--- a/worker/application_manager.cc
+++ /dev/null
@@ -1,1120 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// Contains the logic to encapsulate access to the application data
-// stored in the registry.
-
-#include "omaha/worker/application_manager.h"
-
-#include <algorithm>
-#include <cstdlib>
-#include <functional>
-#include <vector>
-#include "base/scoped_ptr.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/common/time.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/worker/application_usage_data.h"
-#include "omaha/worker/application_data.h"
-#include "omaha/worker/job.h"
-
-namespace omaha {
-
-namespace {
-
-// Returns the number of days haven been passed since the given time.
-// The parameter time is in the same format as C time() returns.
-int GetNumberOfDaysSince(int time) {
-  ASSERT1(time >= 0);
-  const int now = Time64ToInt32(GetCurrent100NSTime());
-  ASSERT1(now >= time);
-
-  if (now < time) {
-    // In case the client computer clock is adjusted in between.
-    return 0;
-  }
-  return (now - time) / kSecondsPerDay;
-}
-
-// Determines if an application is registered with Google Update.
-class IsAppRegisteredFunc
-    : public std::unary_function<const CString&, HRESULT> {
- public:
-  explicit IsAppRegisteredFunc(const CString& guid)
-      : is_registered_(false),
-        guid_(guid) {}
-
-  bool is_registered() const { return is_registered_; }
-
-  HRESULT operator() (const CString& guid) {
-    if (guid.CompareNoCase(guid_) == 0) {
-      is_registered_ = true;
-    }
-    return S_OK;
-  }
- private:
-  CString guid_;
-  bool is_registered_;
-};
-
-// Accumulates ProductData.
-class CollectProductsFunc
-    : public std::unary_function<const CString&, HRESULT> {
- public:
-  CollectProductsFunc(ProductDataVector* products,
-                      bool is_machine,
-                      bool collect_registered_products)
-      : products_(products),
-        is_machine_(is_machine),
-        collect_registered_products_(collect_registered_products) {
-    ASSERT1(products);
-  }
-
-  // Ignores errors and accumulates as many applications as possible.
-  HRESULT operator() (const CString& guid) {
-    AppManager app_manager(is_machine_);
-    ProductData product_data;
-    if (SUCCEEDED(app_manager.ReadProductDataFromStore(StringToGuid(guid),
-                                                       &product_data))) {
-      ASSERT(!collect_registered_products_ ||
-             !product_data.app_data().is_uninstalled(),
-             (_T("Should not be finding uninstalled apps while looking for ")
-              _T("registered apps; may be enumerating the wrong key.")));
-      if (collect_registered_products_ ||
-          product_data.app_data().is_uninstalled()) {
-        CORE_LOG(L3, (_T("[Found %s product][%s]"),
-                      collect_registered_products_ ? _T("registered") :
-                                                      _T("uninstalled"),
-                      guid));
-        products_->push_back(product_data);
-      }
-    }
-    return S_OK;
-  }
-
- private:
-  bool collect_registered_products_;
-  bool is_machine_;
-  ProductDataVector* products_;
-};
-
-// Accumulates AppData for components of a product.
-class CollectComponentsFunc
-    : public std::unary_function<const CString&, HRESULT> {
- public:
-  CollectComponentsFunc(ProductData* product_data,
-                        bool is_machine)
-      : product_data_(product_data),
-        is_machine_(is_machine) {
-    ASSERT1(product_data);
-  }
-
-  // Ignores errors and accumulates as many components as possible.
-  HRESULT operator() (const CString& guid) {
-    AppManager app_manager(is_machine_);
-    AppData component_data;
-    if (SUCCEEDED(app_manager.ReadAppDataFromStore(
-        product_data_->app_data().app_guid(),
-        StringToGuid(guid),
-        &component_data))) {
-      product_data_->AddComponent(component_data);
-    }
-    return S_OK;
-  }
-
- private:
-  ProductData* product_data_;
-  bool is_machine_;
-};
-
-
-// Enumerates all sub keys of the key and calls the functor for each of them.
-template <typename T>
-HRESULT EnumerateSubKeys(const TCHAR* key_name, T* functor) {
-  RegKey client_key;
-  HRESULT hr = client_key.Open(key_name, KEY_READ);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  int num_sub_keys = client_key.GetSubkeyCount();
-  for (int i = 0; i < num_sub_keys; ++i) {
-    CString sub_key_name;
-    hr = client_key.GetSubkeyNameAt(i, &sub_key_name);
-    if (SUCCEEDED(hr)) {
-      (*functor)(sub_key_name);
-    }
-  }
-
-  return S_OK;
-}
-
-}  // namespace
-
-AppManager::AppManager(bool is_machine)
-    : is_machine_(is_machine) {
-  CORE_LOG(L3, (_T("[AppManager::AppManager][is_machine=%d]"), is_machine));
-}
-
-bool AppManager::IsProductRegistered(const GUID& app_guid) const {
-  IsAppRegisteredFunc func(GuidToString(app_guid));
-  HRESULT hr = EnumerateSubKeys(
-      ConfigManager::Instance()->registry_clients(is_machine_),
-      &func);
-  if (FAILED(hr)) {
-    return false;
-  }
-
-  return func.is_registered();
-}
-
-// TODO(omaha): Consider making AppManager a namespace.
-void AppManager::ConvertCommandLineToProductData(const CommandLineArgs& args,
-                                                 ProductDataVector* products) {
-  ASSERT1(products);
-
-  // TODO(omaha):  Need to update this to read the bundle info to build up
-  // multiple AppData objects (and also to read components) and add unit test.
-  for (size_t i = 0; i < args.extra.apps.size(); ++i) {
-    const CommandLineAppArgs& extra_arg = args.extra.apps[i];
-    const GUID& app_guid = extra_arg.app_guid;
-    AppData app_data(app_guid, is_machine_);
-    app_data.set_language(args.extra.language);
-    app_data.set_ap(extra_arg.ap);
-    app_data.set_tt_token(extra_arg.tt_token);
-    app_data.set_iid(args.extra.installation_id);
-    app_data.set_brand_code(args.extra.brand_code);
-    app_data.set_client_id(args.extra.client_id);
-    app_data.set_referral_id(args.extra.referral_id);
-
-    // install_time_diff_sec is set based on the current state of the system.
-    if (IsProductRegistered(app_guid)) {
-      app_data.set_install_time_diff_sec(GetInstallTimeDiffSec(app_guid));
-    } else {
-      // The product is not already installed. We differentiate this from no
-      // install time being present (i.e. the app was installed before
-      // installtime was implemented) by setting the diff to -1 days.
-      // This makes an assumption about the XML parser but works for now.
-      const int kNewInstallValue = -1 * kSecondsPerDay;
-      app_data.set_install_time_diff_sec(kNewInstallValue);
-    }
-
-    // Do not set is_oem_install because it is not based on the command line.
-    app_data.set_is_eula_accepted(!args.is_eula_required_set);
-    app_data.set_display_name(extra_arg.app_name);
-    app_data.set_browser_type(args.extra.browser_type);
-    app_data.set_install_source(args.install_source);
-    app_data.set_usage_stats_enable(args.extra.usage_stats_enable);
-    app_data.set_encoded_installer_data(extra_arg.encoded_installer_data);
-    app_data.set_install_data_index(extra_arg.install_data_index);
-
-    ProductData product_data(app_data);
-    products->push_back(product_data);
-  }
-
-  ASSERT1(products->size() == args.extra.apps.size());
-}
-
-
-HRESULT AppManager::GetRegisteredProducts(ProductDataVector* products) const {
-  ASSERT1(products);
-
-  CollectProductsFunc func(products, is_machine_, true);
-  return EnumerateSubKeys(
-      ConfigManager::Instance()->registry_clients(is_machine_),
-      &func);
-}
-
-HRESULT AppManager::GetUnRegisteredProducts(ProductDataVector* products) const {
-  ASSERT1(products);
-
-  CollectProductsFunc func(products, is_machine_, false);
-  return EnumerateSubKeys(
-      ConfigManager::Instance()->registry_client_state(is_machine_),
-      &func);
-}
-
-CString AppManager::GetProductClientKeyName(const GUID& app_guid) {
-  return goopdate_utils::GetAppClientsKey(is_machine_, GuidToString(app_guid));
-}
-
-CString AppManager::GetProductClientComponentsKeyName(const GUID& app_guid) {
-  return AppendRegKeyPath(GetProductClientKeyName(app_guid),
-                          kComponentsRegKeyName);
-}
-
-CString AppManager::GetProductClientStateComponentsKeyName(
-    const GUID& app_guid) {
-  return AppendRegKeyPath(GetProductClientStateKeyName(app_guid),
-                          kComponentsRegKeyName);
-}
-
-CString AppManager::GetComponentClientKeyName(const GUID& parent_app_guid,
-                                              const GUID& app_guid) {
-  return AppendRegKeyPath(GetProductClientComponentsKeyName(parent_app_guid),
-                          GuidToString(app_guid));
-}
-
-CString AppManager::GetProductClientStateKeyName(const GUID& app_guid) {
-  return goopdate_utils::GetAppClientStateKey(is_machine_,
-                                              GuidToString(app_guid));
-}
-
-CString AppManager::GetComponentClientStateKeyName(const GUID& parent_app_guid,
-                                                   const GUID& app_guid) {
-  return AppendRegKeyPath(
-      GetProductClientStateComponentsKeyName(parent_app_guid),
-      GuidToString(app_guid));
-}
-
-CString AppManager::GetProductClientStateMediumKeyName(const GUID& app_guid) {
-  ASSERT1(is_machine_);
-  return goopdate_utils::GetAppClientStateMediumKey(is_machine_,
-                                                    GuidToString(app_guid));
-}
-
-CString AppManager::GetClientKeyName(const GUID& parent_app_guid,
-                                     const GUID& app_guid) {
-  if (::IsEqualGUID(parent_app_guid, GUID_NULL)) {
-    return GetProductClientKeyName(app_guid);
-  } else {
-    return GetComponentClientKeyName(parent_app_guid, app_guid);
-  }
-}
-
-CString AppManager::GetClientStateKeyName(const GUID& parent_app_guid,
-                                          const GUID& app_guid) {
-  if (::IsEqualGUID(parent_app_guid, GUID_NULL)) {
-    return GetProductClientStateKeyName(app_guid);
-  } else {
-    return GetComponentClientStateKeyName(parent_app_guid, app_guid);
-  }
-}
-
-HRESULT AppManager::OpenClientKey(const GUID& parent_app_guid,
-                                  const GUID& app_guid,
-                                  RegKey* client_key) {
-  ASSERT1(client_key);
-  return client_key->Open(GetClientKeyName(parent_app_guid, app_guid),
-                          KEY_READ);
-}
-
-HRESULT AppManager::OpenClientStateKey(const GUID& parent_app_guid,
-                                       const GUID& app_guid,
-                                       REGSAM sam_desired,
-                                       RegKey* client_state_key) {
-  ASSERT1(client_state_key);
-  CString key_name = GetClientStateKeyName(parent_app_guid, app_guid);
-  return client_state_key->Open(key_name, sam_desired);
-}
-
-// Also creates the ClientStateMedium key for machine apps, ensuring it exists
-// whenever ClientState exists.  Does not create ClientStateMedium for Omaha.
-// This method is called for self-updates, so it must explicitly avoid this.
-HRESULT AppManager::CreateClientStateKey(const GUID& parent_app_guid,
-                                         const GUID& app_guid,
-                                         RegKey* client_state_key) {
-  ASSERT(::IsEqualGUID(parent_app_guid, GUID_NULL),
-         (_T("Legacy components not supported; ClientStateMedium ignores.")));
-
-  ASSERT1(client_state_key);
-  const CString key_name = GetClientStateKeyName(parent_app_guid, app_guid);
-  HRESULT hr = client_state_key->Create(key_name);
-  if (FAILED(hr)) {
-    CORE_LOG(L3, (_T("[RegKey::Create failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  if (!is_machine_) {
-    return S_OK;
-  }
-
-  if (::IsEqualGUID(kGoopdateGuid, app_guid)) {
-    return S_OK;
-  }
-
-  const CString medium_key_name = GetProductClientStateMediumKeyName(app_guid);
-  hr = RegKey::CreateKey(medium_key_name);
-  if (FAILED(hr)) {
-    CORE_LOG(L3, (_T("[RegKey::Create ClientStateMedium failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-// Reads the following values from the registry:
-// Clients Key
-//   product version
-//   language
-// Client State Key
-//   previous product version.
-//   last checked
-//   ap
-//   client id
-//   iid
-// Clients key in HKCU/HKLM/Low integrity
-//   did run
-// Note: If the application is uninstalled, the clients key may not exist.
-HRESULT AppManager::ReadProductDataFromStore(const GUID& app_guid,
-                                             ProductData* product_data) {
-  ASSERT1(product_data);
-
-  AppData app_data;
-  HRESULT hr = ReadAppDataFromStore(GUID_NULL, app_guid, &app_data);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[AppManager::ReadAppDataFromStore failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  ProductData product(app_data);
-
-  // Read components for this product from the registry.
-  CollectComponentsFunc func(&product, is_machine_);
-  EnumerateSubKeys(GetProductClientComponentsKeyName(app_data.app_guid()),
-                   &func);
-
-  *product_data = product;
-  return S_OK;
-}
-
-HRESULT AppManager::ReadAppDataFromStore(const GUID& parent_app_guid,
-                                         const GUID& app_guid,
-                                         AppData* app_data) {
-  ASSERT1(app_data);
-  AppData temp_data(app_guid, is_machine_);
-  temp_data.set_parent_app_guid(parent_app_guid);
-
-  bool client_key_exists = false;
-  RegKey client_key;
-  HRESULT hr = OpenClientKey(parent_app_guid, app_guid, &client_key);
-  if (SUCCEEDED(hr)) {
-    CString version;
-    hr = client_key.GetValue(kRegValueProductVersion, &version);
-    temp_data.set_version(version);
-    CORE_LOG(L3, (_T("[AppManager::ReadAppDataFromStore]")
-                  _T("[parent_app_guid=%s]")
-                  _T("[app_guid=%s]")
-                  _T("[version=%s]"),
-                  GuidToString(parent_app_guid),
-                  GuidToString(app_guid),
-                  version));
-    if (FAILED(hr)) {
-      return hr;
-    }
-
-    // Language might not be written by an installer, so ignore failures.
-    CString language;
-    client_key.GetValue(kRegValueLanguage, &language);
-    temp_data.set_language(language);
-    client_key_exists = true;
-  }
-
-  // If ClientState registry key doesn't exist, the function could return.
-  // Before opening the key, set days_since_last* to -1, which is the
-  // default value if reg key doesn't exist. If later we find that the values
-  // are readable, new values will overwrite current ones.
-  temp_data.set_days_since_last_active_ping(-1);
-  temp_data.set_days_since_last_roll_call(-1);
-
-  RegKey client_state_key;
-  hr = OpenClientStateKey(parent_app_guid,
-                          app_guid,
-                          KEY_READ,
-                          &client_state_key);
-  if (FAILED(hr)) {
-    // It is possible that the client state key has not yet been populated.
-    // In this case just return the information that we have gathered thus far.
-    // However if both keys dont exist, then we are doing something wrong.
-    CORE_LOG(LW, (_T("[AppManager::ReadAppDataFromStore - No ClientState]")));
-    if (client_key_exists) {
-      *app_data = temp_data;
-      return S_OK;
-    } else {
-      return hr;
-    }
-  }
-
-  // The value is not essential for Omaha's operation, so ignore errors.
-  CString previous_version;
-  HRESULT previous_version_hr =
-      client_state_key.GetValue(kRegValueProductVersion, &previous_version);
-  temp_data.set_previous_version(previous_version);
-
-  // An app is considered uninstalled if:
-  //  * The app's Clients key does not exist AND
-  //  * The app's ClientState key exists and contains the pv value.
-  // Omaha may create the app's ClientState key and write values from the
-  // metainstaller tag before running the installer, which creates the Client
-  // key. Requiring pv in ClientState avoids mistakenly determining that the
-  // Omaha-created key indicates an uninstall.
-  bool app_is_uninstalled =
-      !client_key_exists &&
-      HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) != previous_version_hr;
-  temp_data.set_is_uninstalled(app_is_uninstalled);
-
-  CString ap;
-  client_state_key.GetValue(kRegValueAdditionalParams, &ap);
-  temp_data.set_ap(ap);
-
-  CString tt_token;
-  client_state_key.GetValue(kRegValueTTToken, &tt_token);
-  temp_data.set_tt_token(tt_token);
-
-  CString iid;
-  client_state_key.GetValue(kRegValueInstallationId, &iid);
-  temp_data.set_iid(StringToGuid(iid));
-
-  CString brand_code;
-  client_state_key.GetValue(kRegValueBrandCode, &brand_code);
-  ASSERT1(brand_code.GetLength() <= kBrandIdLength);
-  temp_data.set_brand_code(brand_code);
-
-  CString client_id;
-  client_state_key.GetValue(kRegValueClientId, &client_id);
-  temp_data.set_client_id(client_id);
-
-  DWORD last_active_ping_sec(0);
-  if (SUCCEEDED(client_state_key.GetValue(kRegValueActivePingDayStartSec,
-                                          &last_active_ping_sec))) {
-    int days_since_last_active_ping =
-        GetNumberOfDaysSince(static_cast<int32>(last_active_ping_sec));
-    temp_data.set_days_since_last_active_ping(days_since_last_active_ping);
-  }
-
-  DWORD last_roll_call_sec(0);
-  if (SUCCEEDED(client_state_key.GetValue(kRegValueRollCallDayStartSec,
-                                          &last_roll_call_sec))) {
-    int days_since_last_roll_call =
-        GetNumberOfDaysSince(static_cast<int32>(last_roll_call_sec));
-    temp_data.set_days_since_last_roll_call(days_since_last_roll_call);
-  }
-
-  // We do not need the referral_id.
-
-  ASSERT(::IsEqualGUID(parent_app_guid, GUID_NULL),
-         (_T("Legacy components not supported.")));
-  temp_data.set_install_time_diff_sec(GetInstallTimeDiffSec(app_guid));
-
-  temp_data.set_is_oem_install(client_state_key.HasValue(kRegValueOemInstall));
-
-  bool is_eula_accepted = true;
-  DWORD eula_accepted = 0;
-
-  ASSERT(::IsEqualGUID(parent_app_guid, GUID_NULL),
-         (_T("Legacy components not supported; IsAppEulaAccepted ignores.")));
-  temp_data.set_is_eula_accepted(
-      goopdate_utils::IsAppEulaAccepted(is_machine_,
-                                        GuidToString(app_guid),
-                                        false));
-
-  if (temp_data.language().IsEmpty()) {
-    // Read the language from the client state key if we did not find
-    // it in the client key.
-    CString language;
-    client_state_key.GetValue(kRegValueLanguage, &language);
-    temp_data.set_language(language);
-  }
-
-  // Read the did run value.
-  // TODO(omaha): Try to move this logic into the application_usage_data class.
-  ApplicationUsageData app_usage(is_machine_, vista_util::IsVistaOrLater());
-  hr = app_usage.ReadDidRun(GuidToString(app_guid));
-  if (FAILED(hr)) {
-    CORE_LOG(LEVEL_WARNING, (_T("[ReadDidRun failed][0x%08x]"), hr));
-  }
-  AppData::ActiveStates active = AppData::ACTIVE_NOTRUN;
-  if (app_usage.exists()) {
-    active = app_usage.did_run() ? AppData::ACTIVE_RUN :
-                                   AppData::ACTIVE_NOTRUN;
-  } else {
-    active = AppData::ACTIVE_UNKNOWN;
-  }
-  temp_data.set_did_run(active);
-
-  *app_data = temp_data;
-  return S_OK;
-}
-
-// Sets brand code, client ID, usage stats, browser, ap, and Omaha language in
-// the app's ClientState. Also, sets the installed by OEM flag if appropriate.
-// Sets eulaaccepted=0 if the app is not already registered and the app's EULA
-// has not been accepted. Deletes eulaaccepted if the EULA has been accepted.
-// Only call for initial or over-installs. Do not call for updates to avoid
-// mistakenly replacing data, such as the application's language, and causing
-// unexpected changes to the app during a silent update.
-HRESULT AppManager::WritePreInstallData(const AppData& app_data) {
-  CORE_LOG(L3, (_T("[AppManager::WritePreInstallData]")));
-
-  RegKey client_state_key;
-  HRESULT hr = CreateClientStateKey(app_data.parent_app_guid(),
-                                    app_data.app_guid(),
-                                    &client_state_key);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  if (app_data.is_eula_accepted()) {
-    hr = goopdate_utils::ClearAppEulaNotAccepted(
-             is_machine_,
-             GuidToString(app_data.app_guid()));
-  } else {
-    if (!IsProductRegistered(app_data.app_guid())) {
-      hr = goopdate_utils::SetAppEulaNotAccepted(
-               is_machine_,
-               GuidToString(app_data.app_guid()));
-    }
-  }
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  CString state_key_path = GetClientStateKeyName(app_data.parent_app_guid(),
-                                                 app_data.app_guid());
-  VERIFY1(SUCCEEDED(goopdate_utils::SetAppBranding(state_key_path,
-                                                   app_data.brand_code(),
-                                                   app_data.client_id(),
-                                                   app_data.referral_id())));
-
-  ASSERT(::IsEqualGUID(app_data.parent_app_guid(), GUID_NULL),
-         (_T("Legacy components not supported; SetUsageStatsEnable ignores.")));
-  if (TRISTATE_NONE != app_data.usage_stats_enable()) {
-    VERIFY1(SUCCEEDED(goopdate_utils::SetUsageStatsEnable(
-                          is_machine_,
-                          GuidToString(app_data.app_guid()),
-                          app_data.usage_stats_enable())));
-  }
-
-  if (BROWSER_UNKNOWN == app_data.browser_type()) {
-    VERIFY1(SUCCEEDED(client_state_key.DeleteValue(kRegValueBrowser)));
-  } else {
-    DWORD browser_type = app_data.browser_type();
-    VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueBrowser,
-                                                browser_type)));
-  }
-
-  if (app_data.ap().IsEmpty()) {
-    VERIFY1(SUCCEEDED(
-        client_state_key.DeleteValue(kRegValueAdditionalParams)));
-  } else {
-    VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueAdditionalParams,
-                                                app_data.ap())));
-  }
-
-  if (!app_data.language().IsEmpty()) {
-    VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueLanguage,
-                                                app_data.language())));
-  }
-
-  if (ConfigManager::Instance()->IsOemInstalling(is_machine_)) {
-    ASSERT1(is_machine_);
-    VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueOemInstall, _T("1"))));
-  }
-
-  return S_OK;
-}
-
-// Sets installation ID in the app's ClientState.
-// Copies version and language from clients to client state reg key.
-HRESULT AppManager::InitializeApplicationState(AppData* app_data) {
-  CORE_LOG(L3, (_T("[AppManager::InitializeApplicationState]")));
-  ASSERT1(app_data);
-  RegKey client_state_key;
-  HRESULT hr = CreateClientStateKey(app_data->parent_app_guid(),
-                                    app_data->app_guid(),
-                                    &client_state_key);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  RegKey client_key;
-  hr = OpenClientKey(app_data->parent_app_guid(),
-                     app_data->app_guid(),
-                     &client_key);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  if (!::IsEqualGUID(app_data->iid(), GUID_NULL)) {
-    VERIFY1(SUCCEEDED(client_state_key.SetValue(
-                          kRegValueInstallationId,
-                          GuidToString(app_data->iid()))));
-  }
-
-  return CopyVersionAndLanguageToClientState(app_data,
-                                             client_state_key,
-                                             client_key);
-}
-
-// Copies language and version from clients into client state reg key.
-HRESULT AppManager::UpdateApplicationState(AppData* app_data) {
-  ASSERT1(app_data);
-  RegKey client_state_key;
-  HRESULT hr = CreateClientStateKey(app_data->parent_app_guid(),
-                                    app_data->app_guid(),
-                                    &client_state_key);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  RegKey client_key;
-  hr = OpenClientKey(app_data->parent_app_guid(),
-                     app_data->app_guid(),
-                     &client_key);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = CopyVersionAndLanguageToClientState(app_data,
-                                           client_state_key,
-                                           client_key);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT AppManager::WriteTTToken(const AppData& app_data,
-                                 const UpdateResponseData& response_data) {
-  CORE_LOG(L3, (_T("[WriteTTToken][app_data token=%s][response_data token=%s]"),
-                app_data.tt_token(), response_data.tt_token()));
-  RegKey client_state_key;
-  HRESULT hr = CreateClientStateKey(app_data.parent_app_guid(),
-                                    app_data.app_guid(),
-                                    &client_state_key);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  if (response_data.tt_token().IsEmpty()) {
-    VERIFY1(SUCCEEDED(client_state_key.DeleteValue(kRegValueTTToken)));
-  } else {
-    VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueTTToken,
-                                                response_data.tt_token())));
-  }
-
-  return S_OK;
-}
-
-// The registry reads and writes are not thread safe, but there should not be
-// other threads or processes calling this at the same time.
-void AppManager::UpdateUpdateAvailableStats(const GUID& parent_app_guid,
-                                            const GUID& app_guid) {
-  RegKey state_key;
-  HRESULT hr = CreateClientStateKey(parent_app_guid, app_guid, &state_key);
-  if (FAILED(hr)) {
-    ASSERT1(false);
-    return;
-  }
-
-  DWORD update_available_count(0);
-  hr = state_key.GetValue(kRegValueUpdateAvailableCount,
-                          &update_available_count);
-  if (FAILED(hr)) {
-    update_available_count = 0;
-  }
-  ++update_available_count;
-  VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueUpdateAvailableCount,
-                                       update_available_count)));
-
-  DWORD64 update_available_since_time(0);
-  hr = state_key.GetValue(kRegValueUpdateAvailableSince,
-                          &update_available_since_time);
-  if (FAILED(hr)) {
-    // There is no existing value, so this must be the first update notice.
-    VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueUpdateAvailableSince,
-                                         GetCurrent100NSTime())));
-
-    // TODO(omaha): It would be nice to report the version that we were first
-    // told to update to. This is available in UpdateResponse but we do not
-    // currently send it down in update responses. If we start using it, add
-    // kRegValueFirstUpdateResponseVersion.
-  }
-}
-
-void AppManager::ClearUpdateAvailableStats(const GUID& parent_app_guid,
-                                           const GUID& app_guid) {
-  RegKey state_key;
-  HRESULT hr =
-      OpenClientStateKey(parent_app_guid, app_guid, KEY_ALL_ACCESS, &state_key);
-  if (FAILED(hr)) {
-    return;
-  }
-
-  VERIFY1(SUCCEEDED(state_key.DeleteValue(kRegValueUpdateAvailableCount)));
-  VERIFY1(SUCCEEDED(state_key.DeleteValue(kRegValueUpdateAvailableSince)));
-}
-
-void AppManager::ClearOemInstalled(const GUID& parent_app_guid,
-                                   const GUID& app_guid) {
-  RegKey state_key;
-  HRESULT hr =
-      OpenClientStateKey(parent_app_guid, app_guid, KEY_ALL_ACCESS, &state_key);
-  ASSERT1(SUCCEEDED(hr));
-  if (FAILED(hr)) {
-    return;
-  }
-
-  VERIFY1(SUCCEEDED(state_key.DeleteValue(kRegValueOemInstall)));
-}
-
-// Returns 0 for any values that are not found.
-void AppManager::ReadUpdateAvailableStats(
-    const GUID& parent_app_guid,
-    const GUID& app_guid,
-    DWORD* update_responses,
-    DWORD64* time_since_first_response_ms) {
-  ASSERT1(update_responses);
-  ASSERT1(time_since_first_response_ms);
-  *update_responses = 0;
-  *time_since_first_response_ms = 0;
-
-  RegKey state_key;
-  HRESULT hr = OpenClientStateKey(parent_app_guid,
-                                  app_guid,
-                                  KEY_READ,
-                                  &state_key);
-  if (FAILED(hr)) {
-    CORE_LOG(LW, (_T("[App ClientState key does not exist][%s]"),
-                  GuidToString(app_guid)));
-    return;
-  }
-
-  DWORD update_responses_in_reg(0);
-  hr = state_key.GetValue(kRegValueUpdateAvailableCount,
-                          &update_responses_in_reg);
-  if (SUCCEEDED(hr)) {
-    *update_responses = update_responses_in_reg;
-  }
-
-  DWORD64 update_available_since_time(0);
-  hr = state_key.GetValue(kRegValueUpdateAvailableSince,
-                          &update_available_since_time);
-  if (SUCCEEDED(hr)) {
-    const DWORD64 current_time = GetCurrent100NSTime();
-    ASSERT1(update_available_since_time <= current_time);
-    const DWORD64 time_since_first_response_in_100ns =
-        current_time - update_available_since_time;
-    *time_since_first_response_ms =
-        time_since_first_response_in_100ns / kMillisecsTo100ns;
-  }
-}
-
-// The update success and update check success times are not updated for
-// installs even if it is an over-install. At this point, we do not know for
-// sure that it was an online update.
-void AppManager::RecordSuccessfulInstall(const GUID& parent_app_guid,
-                                         const GUID& app_guid,
-                                         bool is_update,
-                                         bool is_offline) {
-  ASSERT1(!is_update || !is_offline);
-
-  ClearUpdateAvailableStats(parent_app_guid, app_guid);
-
-  if (!is_offline) {
-    // Assumes that all updates are online.
-    RecordSuccessfulUpdateCheck(parent_app_guid, app_guid);
-  }
-
-  if (is_update) {
-    RegKey state_key;
-    HRESULT hr = CreateClientStateKey(parent_app_guid, app_guid, &state_key);
-    if (FAILED(hr)) {
-      ASSERT1(false);
-      return;
-    }
-
-    const DWORD now = Time64ToInt32(GetCurrent100NSTime());
-    VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueLastUpdateTimeSec, now)));
-  }
-}
-
-void AppManager::RecordSuccessfulUpdateCheck(const GUID& parent_app_guid,
-                                             const GUID& app_guid) {
-  RegKey state_key;
-  HRESULT hr = CreateClientStateKey(parent_app_guid, app_guid, &state_key);
-  if (FAILED(hr)) {
-    ASSERT1(false);
-    return;
-  }
-
-  const DWORD now = Time64ToInt32(GetCurrent100NSTime());
-  VERIFY1(SUCCEEDED(state_key.SetValue(kRegValueLastSuccessfulCheckSec, now)));
-}
-
-// Assumes the app is registered and has a ClientState. The new install case
-// should be handled differently.
-uint32 AppManager::GetInstallTimeDiffSec(const GUID& app_guid) {
-  RegKey client_state_key;
-  HRESULT hr = OpenClientStateKey(GUID_NULL,
-                                  app_guid,
-                                  KEY_READ,
-                                  &client_state_key);
-  ASSERT(SUCCEEDED(hr), (_T("Assumes app is registered.")));
-  if (FAILED(hr)) {
-    return 0;
-  }
-
-  DWORD install_time(0);
-  DWORD install_time_diff_sec(0);
-  if (SUCCEEDED(client_state_key.GetValue(kRegValueInstallTimeSec,
-                                          &install_time))) {
-    const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-    if (0 != install_time && now >= install_time) {
-      install_time_diff_sec = now - install_time;
-      // TODO(omaha3): Restore this assert. In Omaha 2, this method gets called
-      // as part of installation verification and Job::UpdateJob(), so the value
-      // can be 0. This will not be the case in Omaha 3.
-      // ASSERT1(install_time_diff_sec != 0);
-    }
-  }
-
-  return install_time_diff_sec;
-}
-
-// Clear the Installation ID if at least one of the conditions is true:
-// 1) DidRun==yes. First run is the last time we want to use the Installation
-//    ID. So delete Installation ID if it is present.
-// 2) kMaxLifeOfInstallationIDSec has passed since the app was installed. This
-//    is to ensure that Installation ID is cleared even if DidRun is never set.
-// 3) The app is Omaha. Always delete Installation ID if it is present
-//    because DidRun does not apply.
-HRESULT AppManager::ClearInstallationId(AppData* app_data,
-                                        const RegKey& client_state_key) {
-  ASSERT1(app_data);
-  if (::IsEqualGUID(app_data->iid(), GUID_NULL)) {
-    return S_OK;
-  }
-
-  if ((AppData::ACTIVE_RUN == app_data->did_run()) ||
-      (kMaxLifeOfInstallationIDSec <= app_data->install_time_diff_sec()) ||
-      (::IsEqualGUID(kGoopdateGuid, app_data->app_guid()))) {
-    CORE_LOG(L1, (_T("[Deleting iid for app][%s]"),
-                  GuidToString(app_data->app_guid())));
-    // Relies on installation_id not empty to indicate state_key is valid.
-    VERIFY1(S_OK == client_state_key.DeleteValue(kRegValueInstallationId));
-    app_data->set_iid(GUID_NULL);
-  }
-
-  return S_OK;
-}
-
-void AppManager::ResetDidRun(AppData* app_data) {
-  ApplicationUsageData app_usage(app_data->is_machine_app(),
-                                 vista_util::IsVistaOrLater());
-  VERIFY1(SUCCEEDED(app_usage.ResetDidRun(GuidToString(app_data->app_guid()))));
-  app_data->set_did_run(app_usage.exists() ? AppData::ACTIVE_NOTRUN :
-                                             AppData::ACTIVE_UNKNOWN);
-}
-
-void AppManager::SetLastPingDayStartTime(int time_since_midnight_sec,
-                                         AppData* app_data,
-                                         const RegKey& client_state_key) {
-  ASSERT1(time_since_midnight_sec >= 0);
-  ASSERT1(time_since_midnight_sec < kMaxTimeSinceMidnightSec);
-  ASSERT1(app_data);
-
-  int now = Time64ToInt32(GetCurrent100NSTime());
-
-  bool did_send_active_ping = (app_data->did_run() == AppData::ACTIVE_RUN &&
-                               app_data->days_since_last_active_ping() != 0);
-  if (did_send_active_ping) {
-    VERIFY1(SUCCEEDED(client_state_key.SetValue(
-                          kRegValueActivePingDayStartSec,
-                          static_cast<DWORD>(now - time_since_midnight_sec))));
-    app_data->set_days_since_last_active_ping(0);
-  }
-
-  bool did_send_roll_call = (app_data->days_since_last_roll_call() != 0);
-  if (did_send_roll_call) {
-    VERIFY1(SUCCEEDED(client_state_key.SetValue(
-                          kRegValueRollCallDayStartSec,
-                          static_cast<DWORD>(now - time_since_midnight_sec))));
-    app_data->set_days_since_last_roll_call(0);
-  }
-}
-
-// Writes the day start time when last active ping/roll call happened to
-// registry if the corresponding ping has been sent.
-// Removes installation id, if did run = true or if goopdate.
-// Clears did run.
-HRESULT AppManager::HandleSuccessfulUpdateCheckRequestSend(
-    int time_since_midnight_sec,
-    AppData* app_data) {
-  RegKey client_state_key;
-  HRESULT hr = CreateClientStateKey(app_data->parent_app_guid(),
-                                    app_data->app_guid(),
-                                    &client_state_key);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // Handle the installation id.
-  hr = ClearInstallationId(app_data, client_state_key);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[ClearInstallationId failed][hr=0x%08x]"), hr));
-  }
-
-  SetLastPingDayStartTime(time_since_midnight_sec, app_data, client_state_key);
-
-  // Reset did_run after updating the days_since_last_active_ping and the
-  // installation ID since these updates need previous did_run status to
-  // determine the right actions.
-  ResetDidRun(app_data);
-
-  return hr;
-}
-
-// Only replaces the language in ClientState and app_data if the language in
-// Clients is not empty.
-HRESULT AppManager::CopyVersionAndLanguageToClientState(
-    AppData* app_data,
-    const RegKey& client_state_key,
-    const RegKey& client_key) {
-  // TODO(omaha):  need to handle components too.
-  ASSERT1(app_data);
-  // Read the version and language from the client key.
-  CString version;
-  HRESULT hr = client_key.GetValue(kRegValueProductVersion, &version);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  CString language;
-  client_key.GetValue(kRegValueLanguage, &language);
-
-  // Write the version and language in the client state key.
-  hr = client_state_key.SetValue(kRegValueProductVersion, version);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  app_data->set_version(version);
-  app_data->set_previous_version(version);
-
-  if (!language.IsEmpty()) {
-    VERIFY1(SUCCEEDED(client_state_key.SetValue(kRegValueLanguage, language)));
-    app_data->set_language(language);
-  }
-
-  return S_OK;
-}
-
-HRESULT AppManager::RemoveClientState(const AppData& app_data) {
-  ASSERT(::IsEqualGUID(app_data.parent_app_guid(), GUID_NULL),
-         (_T("Legacy components not supported; ClientStateMedium ignores.")));
-
-  ASSERT1(app_data.is_uninstalled());
-  const CString state_key = GetClientStateKeyName(app_data.parent_app_guid(),
-                                                  app_data.app_guid());
-  HRESULT state_hr = RegKey::DeleteKey(state_key, true);
-
-  if (!is_machine_) {
-    return state_hr;
-  }
-
-  const CString state_medium_key =
-      GetProductClientStateMediumKeyName(app_data.app_guid());
-  HRESULT state_medium_hr = RegKey::DeleteKey(state_medium_key, true);
-
-  return FAILED(state_hr) ? state_hr : state_medium_hr;
-}
-
-// Returns true if the absolute difference between time moments is greater than
-// the interval between update checks.
-// Deals with clocks rolling backwards, in scenarios where the clock indicates
-// some time in the future, for example next year, last_checked_ is updated to
-// reflect that time, and then the clock is adjusted back to present.
-bool AppManager::ShouldCheckForUpdates() const {
-  ConfigManager* cm = ConfigManager::Instance();
-  bool is_period_overridden = false;
-  const int update_interval = cm->GetLastCheckPeriodSec(&is_period_overridden);
-  if (0 == update_interval) {
-    ASSERT1(is_period_overridden);
-    OPT_LOG(L1, (_T("[ShouldCheckForUpdates returned 0][checks disabled]")));
-    return false;
-  }
-
-  const int time_difference = cm->GetTimeSinceLastCheckedSec(is_machine_);
-
-  const bool result = time_difference >= update_interval ? true : false;
-  CORE_LOG(L3, (_T("[ShouldCheckForUpdates returned %d][%u]"),
-                result, is_period_overridden));
-  return result;
-}
-
-HRESULT AppManager::UpdateLastChecked() {
-  // Set the last check value to the current value.
-  DWORD now = Time64ToInt32(GetCurrent100NSTime());
-  HRESULT hr = ConfigManager::Instance()->SetLastCheckedTime(is_machine_, now);
-  CORE_LOG(L3, (_T("[AppManager::UpdateLastChecked][now %d]"), now));
-  if (FAILED(hr)) {
-    CORE_LOG(LW, (_T("[UpdateLastChecked returned 0x%08x]"), hr));
-    return hr;
-  }
-  return S_OK;
-}
-
-HRESULT AppManager::ReadProductDataFromUserOrMachineStore(
-    const GUID& guid,
-    ProductData* product_data) {
-  ASSERT1(product_data);
-  AppManager app_manager_user(false);
-  HRESULT hr = app_manager_user.ReadProductDataFromStore(guid, product_data);
-  if (SUCCEEDED(hr)) {
-    return hr;
-  }
-
-  AppManager app_manager_machine(true);
-  return app_manager_machine.ReadProductDataFromStore(guid, product_data);
-}
-
-// Writes 0.0.0.1 to pv. This value avoids any special cases, such as initial
-// install rules, for 0.0.0.0, while being unlikely to be higher than the
-// product's actual current version.
-HRESULT AppManager::RegisterProduct(const GUID& product_guid,
-                                    const CString& product_name) {
-  const TCHAR* const kRegisterProductVersion = _T("0.0.0.1");
-
-  RegKey client_key;
-  HRESULT hr = client_key.Create(GetClientKeyName(GUID_NULL, product_guid));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = client_key.SetValue(kRegValueProductVersion, kRegisterProductVersion);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // AppName is not a required parameter since it's only used for being able to
-  // easily tell what application is there when reading the registry.
-  VERIFY1(SUCCEEDED(client_key.SetValue(kRegValueAppName, product_name)));
-
-  return S_OK;
-}
-
-HRESULT AppManager::UnregisterProduct(const GUID& product_guid) {
-  return RegKey::DeleteKey(GetClientKeyName(GUID_NULL, product_guid), true);
-}
-
-}  // namespace omaha.
-
diff --git a/worker/application_manager.h b/worker/application_manager.h
deleted file mode 100644
index 2489f5c..0000000
--- a/worker/application_manager.h
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Class allows the worker to access products and components.
-
-#ifndef OMAHA_WORKER_APPLICATION_MANAGER_H__
-#define OMAHA_WORKER_APPLICATION_MANAGER_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include <vector>
-#include "base/basictypes.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/request.h"
-#include "omaha/goopdate/update_response_data.h"
-#include "omaha/worker/application_data.h"
-#include "omaha/worker/product_data.h"
-
-namespace omaha {
-
-// Application manager allows the worker to access registry state.
-class AppManager {
- public:
-  explicit AppManager(bool is_machine);
-
-  bool IsProductRegistered(const GUID& app_guid) const;
-  bool IsComponentRegistered(const GUID& app_guid, const GUID& component_guid);
-
-  void ConvertCommandLineToProductData(const CommandLineArgs& args,
-                                       ProductDataVector* products);
-
-  HRESULT GetRegisteredProducts(ProductDataVector* products) const;
-
-  HRESULT GetUnRegisteredProducts(ProductDataVector* products) const;
-
-  // Reads the application state from the registry.
-  HRESULT ReadProductDataFromStore(const GUID& app_guid,
-                                   ProductData* product_data);
-
-  // Reads an individual AppData from the store.  Will read either product or
-  // component level based on whether there's a parent or not.  This will not
-  // recurse to read children since it's only the AppData portion.
-  HRESULT ReadAppDataFromStore(const GUID& parent_app_guid,
-                               const GUID& component_guid,
-                               AppData* app_data);
-
-  // Sets dynamic install parameters that the installer or app may use.
-  // Call this method before calling the installer.
-  HRESULT WritePreInstallData(const AppData& app_data);
-
-  // Sets the initial state of the application in the registry. Call this
-  // method after the initial install/update has completed.
-  HRESULT InitializeApplicationState(AppData* app_data);
-
-  // Updates the state of the application in the registry. Call this method
-  // to update the state of the application after an update check.
-  HRESULT UpdateApplicationState(AppData* app_data);
-
-  // Updates application state after server confirms that it has suscessfully
-  // received an update check.
-  HRESULT HandleSuccessfulUpdateCheckRequestSend(int time_since_midnight_sec,
-                                                 AppData* app_data);
-
-  // Write the TT Token with what the server returned.
-  HRESULT WriteTTToken(const AppData& app_data,
-                       const UpdateResponseData& response_data);
-
-  // Stores information about the update available event for the app.
-  // Call each time an update is available.
-  void UpdateUpdateAvailableStats(const GUID& parent_app_guid,
-                                  const GUID& app_guid);
-  // Clears the stored information about update available events for the app.
-  // Call when an update has succeeded.
-  void ClearUpdateAvailableStats(const GUID& parent_app_guid,
-                                 const GUID& app_guid);
-
-  // Clears the OEM-installed flag for the app.
-  void ClearOemInstalled(const GUID& parent_app_guid, const GUID& app_guid);
-
-  // Obtains usage stats information from the stored information about update
-  // available events for the app.
-  void ReadUpdateAvailableStats(const GUID& parent_app_guid,
-                                const GUID& app_guid,
-                                DWORD* update_responses,
-                                DWORD64* time_since_first_response_ms);
-
-  // Updates the application state after a successful install or update.
-  void RecordSuccessfulInstall(const GUID& parent_app_guid,
-                               const GUID& app_guid,
-                               bool is_update,
-                               bool is_offline);
-
-  // Updates the application state after a successful update check event, which
-  // is either a "noupdate" response or a successful online update.
-  void RecordSuccessfulUpdateCheck(const GUID& parent_app_guid,
-                                   const GUID& app_guid);
-
-  // Removes all the registration entries under the client state for the
-  // application.
-  HRESULT RemoveClientState(const AppData& app_data);
-
-  // Returns true if a server update check is due.
-  bool ShouldCheckForUpdates() const;
-  HRESULT UpdateLastChecked();
-
-  static HRESULT ReadProductDataFromUserOrMachineStore(
-      const GUID& guid,
-      ProductData* product_data);
-
-  HRESULT RegisterProduct(const GUID& product_guid,
-                          const CString& product_name);
-  HRESULT UnregisterProduct(const GUID& product_guid);
-
- private:
-  CString GetClientKeyName(const GUID& parent_app_guid, const GUID& app_guid);
-  CString GetClientStateKeyName(const GUID& parent_app_guid,
-                                const GUID& app_guid);
-
-  // Opens the app's Client key for read access.
-  HRESULT OpenClientKey(const GUID& parent_app_guid,
-                        const GUID& app_guid,
-                        RegKey* client_key);
-  // Opens the app's ClientState key with the specified access.
-  HRESULT OpenClientStateKey(const GUID& parent_app_guid,
-                             const GUID& app_guid,
-                             REGSAM sam_desired,
-                             RegKey* client_state_key);
-  // Creates the app's ClientState key.
-  HRESULT CreateClientStateKey(const GUID& parent_app_guid,
-                               const GUID& app_guid,
-                               RegKey* client_state_key);
-
-  CString GetProductClientKeyName(const GUID& app_guid);
-  CString GetProductClientComponentsKeyName(const GUID& app_guid);
-  CString GetProductClientStateComponentsKeyName(const GUID& app_guid);
-  CString GetComponentClientKeyName(const GUID& parent_app_guid,
-                                    const GUID& app_guid);
-  CString GetProductClientStateKeyName(const GUID& app_guid);
-  CString GetProductClientStateMediumKeyName(const GUID& app_guid);
-  CString GetComponentClientStateKeyName(const GUID& parent_app_guid,
-                                         const GUID& app_guid);
-
-  // Gets the time since InstallTime was written. Returns 0 if InstallTime
-  // could not be read. This could occur if the app is not already installed or
-  // there is no valid install time in the registry, which can occur for apps
-  // installed before installtime was implemented.
-  uint32 GetInstallTimeDiffSec(const GUID& app_guid);
-
-  HRESULT ClearInstallationId(AppData* app_data,
-                              const RegKey& client_state_key);
-  HRESULT CopyVersionAndLanguageToClientState(AppData* app_data,
-                                              const RegKey& client_state_key,
-                                              const RegKey& client_key);
-  static void ResetDidRun(AppData* app_data);
-
-  // Writes the day start time when last active ping/roll call happened to
-  // registry.
-  static void SetLastPingDayStartTime(int time_since_midnight_sec,
-                                      AppData* app_data,
-                                      const RegKey& client_state_key);
-
-  const bool is_machine_;
-
-  friend class AppManagerTest;
-
-  DISALLOW_EVIL_CONSTRUCTORS(AppManager);
-};
-
-}  // namespace omaha.
-
-#endif  // OMAHA_WORKER_APPLICATION_MANAGER_H__
-
diff --git a/worker/application_manager_unittest.cc b/worker/application_manager_unittest.cc
deleted file mode 100644
index 2001015..0000000
--- a/worker/application_manager_unittest.cc
+++ /dev/null
@@ -1,1840 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// ApplicationManager unit tests
-
-#include <vector>
-#include "base/scoped_ptr.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/time.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/enterprise/const_group_policy.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/setup/setup_google_update.h"
-#include "omaha/testing/unit_test.h"
-#include "omaha/worker/application_data.h"
-#include "omaha/worker/job.h"
-
-namespace omaha {
-
-namespace {
-
-const TCHAR* const kGuid1 = _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}");
-const TCHAR* const kGuid2 = _T("{A979ACBD-1F55-4b12-A35F-4DBCA5A7CCB8}");
-const TCHAR* const kGuid3 = _T("{661045C5-4429-4140-BC48-8CEA241D1DEF}");
-const TCHAR* const kGuid4 = _T("{AAFA1CF9-E94F-42e6-A899-4CD27F37D5A7}");
-const TCHAR* const kGuid5 = _T("{3B1A3CCA-0525-4418-93E6-A0DB3398EC9B}");
-const TCHAR* const kGuid6 = _T("{F3F2CFD4-5F98-4bf0-ABB0-BEEEA46C62B4}");
-const TCHAR* const kGuid7 = _T("{6FD2272F-8583-4bbd-895A-E65F8003FC7B}");
-const TCHAR* const kIid1  = _T("{F723495F-8ACF-4746-8240-643741C797B5}");
-
-const TCHAR* const kGuid1ClientsKeyPathUser =
-    _T("HKCU\\Software\\Google\\Update\\Clients\\")
-    _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}");
-const TCHAR* const kGuid1ClientStateKeyPathUser =
-    _T("HKCU\\Software\\Google\\Update\\ClientState\\")
-    _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}");
-const TCHAR* const kGuid1ClientStateKeyPathMachine =
-    _T("HKLM\\Software\\Google\\Update\\ClientState\\")
-    _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}");
-
-}  // namespace
-
-void ValidateExpectedValues(const AppData& expected, const AppData& actual);
-void VerifyHklmKeyHasMediumIntegrity(const CString& key_full_name);
-void VerifyHklmKeyHasDefaultIntegrity(const CString& key_full_name);
-
-class AppManagerTest : public testing::Test {
- public:
-  // Creates the application registration entries based on the passed in data.
-  // If passed an application that is uninstalled, the method only creates
-  // the registration entries in the client state and no information is written
-  // in the clients.
-  static void CreateAppRegistryState(const AppData& data) {
-    bool is_machine = data.is_machine_app();
-    CString client_key_name = AppendRegKeyPath(
-        ConfigManager::Instance()->registry_clients(is_machine),
-        GuidToString(data.app_guid()));
-    CString client_state_key_name = AppendRegKeyPath(
-        ConfigManager::Instance()->registry_client_state(is_machine),
-        GuidToString(data.app_guid()));
-    const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-    RegKey client_key;
-    if (!data.is_uninstalled()) {
-      ASSERT_SUCCEEDED(client_key.Create(client_key_name));
-
-      if (!data.version().IsEmpty()) {
-        ASSERT_SUCCEEDED(client_key.SetValue(kRegValueProductVersion,
-                                             data.version()));
-      }
-    }
-
-    RegKey client_state_key;
-    ASSERT_SUCCEEDED(client_state_key.Create(client_state_key_name));
-
-    if (!data.previous_version().IsEmpty()) {
-      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueProductVersion,
-                                                 data.previous_version()));
-    }
-
-    if (!data.language().IsEmpty()) {
-      if (data.is_uninstalled()) {
-        ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueLanguage,
-                                                   data.language()));
-      } else {
-        ASSERT_SUCCEEDED(client_key.SetValue(kRegValueLanguage,
-                                             data.language()));
-      }
-    }
-
-    if (data.did_run() != AppData::ACTIVE_UNKNOWN) {
-      CString dr = (data.did_run() == AppData::ACTIVE_NOTRUN) ? _T("0") :
-                                                                _T("1");
-      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueDidRun,
-                                                 dr));
-    }
-
-    if (!data.ap().IsEmpty()) {
-      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueAdditionalParams,
-                                                 data.ap()));
-    }
-
-    if (!data.tt_token().IsEmpty()) {
-      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueTTToken,
-                                                 data.tt_token()));
-    }
-
-    if (!::IsEqualGUID(data.iid(), GUID_NULL)) {
-      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueInstallationId,
-                                                 GuidToString(data.iid())));
-    }
-
-    if (!data.brand_code().IsEmpty()) {
-      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueBrandCode,
-                                                 data.brand_code()));
-    }
-
-    if (!data.client_id().IsEmpty()) {
-      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueClientId,
-                                                 data.client_id()));
-    }
-
-    if (!data.referral_id().IsEmpty()) {
-      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueReferralId,
-                                                 data.referral_id()));
-    }
-
-    if (!data.referral_id().IsEmpty()) {
-      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueReferralId,
-                                                 data.referral_id()));
-    }
-
-    if (data.install_time_diff_sec()) {
-      const DWORD install_time = now - data.install_time_diff_sec();
-      ASSERT_SUCCEEDED(client_state_key.SetValue(kRegValueInstallTimeSec,
-                                                 install_time));
-    }
-
-    if (!data.is_eula_accepted()) {
-      ASSERT_SUCCEEDED(client_state_key.SetValue(_T("eulaaccepted"),
-                                                 static_cast<DWORD>(0)));
-    }
-
-    int days = data.days_since_last_active_ping();
-
-    if (days != -1) {
-      EXPECT_GE(days, 0);
-      ASSERT1(now > static_cast<uint32>(days * kSecondsPerDay));
-      uint32 last_active_time = now - days * kSecondsPerDay;
-
-      ASSERT_SUCCEEDED(client_state_key.SetValue(
-          kRegValueActivePingDayStartSec,
-          static_cast<DWORD>(last_active_time)));
-    }
-
-    days = data.days_since_last_roll_call();
-    if (days != -1) {
-      EXPECT_GE(days, 0);
-      EXPECT_GE(now, static_cast<uint32>(days * kSecondsPerDay));
-
-      uint32 last_roll_call_time = now - days * kSecondsPerDay;
-
-      ASSERT_SUCCEEDED(client_state_key.SetValue(
-          kRegValueRollCallDayStartSec,
-          static_cast<DWORD>(last_roll_call_time)));
-    }
-  }
-
-  static void PopulateExpectedAppData1(AppData* expected_app) {
-    ASSERT_TRUE(expected_app);
-    expected_app->set_version(_T("1.1.1.3"));
-    expected_app->set_previous_version(_T("1.0.0.0"));
-    expected_app->set_language(_T("abc"));
-    expected_app->set_ap(_T("Test ap"));
-    expected_app->set_tt_token(_T("Test TT Token"));
-    expected_app->set_iid(StringToGuid(kIid1));
-    expected_app->set_brand_code(_T("GOOG"));
-    expected_app->set_client_id(_T("someclient"));
-    // Do not set referral_id or install_time_diff_sec because these are not
-    // expected in most cases.
-    // This value must be ACTIVE_RUN for UpdateApplicationStateTest to work.
-    expected_app->set_did_run(AppData::ACTIVE_RUN);
-    expected_app->set_days_since_last_active_ping(3);
-    expected_app->set_days_since_last_roll_call(1);
-  }
-
-  static void PopulateExpectedAppData1InvalidBrand(AppData* expected_app) {
-    PopulateExpectedAppData1(expected_app);
-    expected_app->set_brand_code(_T("GOOG1122"));
-  }
-
-  static void PopulateExpectedAppData2(AppData* expected_app) {
-    ASSERT_TRUE(expected_app);
-    expected_app->set_version(_T("1.2.1.3"));
-    expected_app->set_previous_version(_T("1.1.0.0"));
-    expected_app->set_language(_T("de"));
-    expected_app->set_ap(_T("beta"));
-    expected_app->set_tt_token(_T("beta TT Token"));
-    expected_app->set_iid(
-        StringToGuid(_T("{431EC961-CFD8-49ea-AB7B-2B99BCA274AD}")));
-    expected_app->set_brand_code(_T("GooG"));
-    expected_app->set_client_id(_T("anotherclient"));
-    expected_app->set_did_run(AppData::ACTIVE_NOTRUN);
-
-    expected_app->set_days_since_last_active_ping(100);
-    expected_app->set_days_since_last_roll_call(1);
-  }
-
-  static void PopulateExpectedUninstalledAppData(AppData* expected_app) {
-    ASSERT_TRUE(expected_app);
-    PopulateExpectedAppData2(expected_app);
-
-    // Make the AppData represent an uninstall.
-    expected_app->set_version(_T(""));
-    expected_app->set_is_uninstalled(true);
-  }
-
- protected:
-  AppManagerTest()
-      : hive_override_key_name_(kRegistryHiveOverrideRoot),
-        guid1_(StringToGuid(kGuid1)) {}
-
-  virtual void SetUp() {
-    RegKey::DeleteKey(hive_override_key_name_);
-    OverrideRegistryHives(hive_override_key_name_);
-  }
-
-  virtual void TearDown() {
-    RestoreRegistryHives();
-    RegKey::DeleteKey(hive_override_key_name_);
-  }
-
-  void ClearUpdateAvailableStats(const GUID& parent_app_guid,
-                                 const GUID& app_guid,
-                                 AppManager* app_manager) {
-    ASSERT_TRUE(app_manager);
-    app_manager->ClearUpdateAvailableStats(parent_app_guid, app_guid);
-  }
-
-  bool IsClientStateKeyPresent(const AppData& data) {
-    CString client_state_key_name = AppendRegKeyPath(
-        ConfigManager::Instance()->registry_client_state(data.is_machine_app()),
-        GuidToString(data.app_guid()));
-
-    return RegKey::HasKey(client_state_key_name);
-  }
-
-  void InitializeApplicationStateTest(bool is_machine) {
-    // Create the test data.
-    AppData expected_data(guid1_, is_machine);
-    expected_data.set_version(_T("4.5.6.7"));
-    expected_data.set_language(_T("de"));
-    expected_data.set_days_since_last_active_ping(-1);
-    expected_data.set_days_since_last_roll_call(-1);
-    CreateAppRegistryState(expected_data);
-
-    // Create the job that contains the test data.
-
-    CommandLineAppArgs extra;
-    extra.app_guid = guid1_;
-    extra.app_name = _T("foo");
-    extra.ap = _T("test ap");
-    extra.tt_token = _T("test TT Token");
-
-    CommandLineArgs args;
-    args.extra.installation_id =
-        StringToGuid(_T("{64333341-CA93-490d-9FB7-7FC5728721F4}"));
-    args.extra.brand_code = _T("g00g");
-    args.extra.client_id = _T("myclient");
-    args.extra.referral_id = _T("somereferrer");
-    args.extra.language = _T("en");
-    args.extra.apps.push_back(extra);
-
-    AppManager app_manager(is_machine);
-    ProductDataVector products;
-    app_manager.ConvertCommandLineToProductData(args, &products);
-    ASSERT_EQ(1, products.size());
-
-    AppData product_app_data = products[0].app_data();
-
-    EXPECT_TRUE(product_app_data.is_eula_accepted());
-
-    // Test the method.
-    ASSERT_SUCCEEDED(
-        app_manager.InitializeApplicationState(&product_app_data));
-
-    // Validate the results.
-    CString client_state_key_name = AppendRegKeyPath(
-        ConfigManager::Instance()->registry_client_state(is_machine),
-        kGuid1);
-    RegKey client_state_key;
-    ASSERT_SUCCEEDED(client_state_key.Create(client_state_key_name));
-
-    ValidateClientStateMedium(is_machine, kGuid1);
-
-    // Check version is copied to the client state.
-    CString previous_version;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueProductVersion,
-                                               &previous_version));
-    EXPECT_STREQ(_T("4.5.6.7"), product_app_data.version());
-    EXPECT_STREQ(_T("4.5.6.7"), previous_version);
-
-    // Check language is copied to the client state.
-    CString language;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueLanguage, &language));
-    EXPECT_STREQ(_T("de"), product_app_data.language());
-    EXPECT_STREQ(_T("de"), language);
-
-    // Check iid is set correctly in ClientState.
-    CString iid;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueInstallationId, &iid));
-    EXPECT_STREQ(_T("{64333341-CA93-490D-9FB7-7FC5728721F4}"),
-                 GuidToString(product_app_data.iid()));
-    EXPECT_STREQ(_T("{64333341-CA93-490D-9FB7-7FC5728721F4}"), iid);
-
-    // Check other values were not written.
-    EXPECT_FALSE(client_state_key.HasValue(kRegValueAdditionalParams));
-    EXPECT_FALSE(client_state_key.HasValue(kRegValueBrandCode));
-    EXPECT_FALSE(client_state_key.HasValue(kRegValueBrowser));
-    EXPECT_FALSE(client_state_key.HasValue(kRegValueClientId));
-    EXPECT_FALSE(client_state_key.HasValue(kRegValueDidRun));
-    EXPECT_FALSE(client_state_key.HasValue(kRegValueOemInstall));
-    EXPECT_FALSE(client_state_key.HasValue(kRegValueReferralId));
-    EXPECT_FALSE(client_state_key.HasValue(kRegValueEulaAccepted));
-    EXPECT_FALSE(client_state_key.HasValue(kRegValueUsageStats));
-    EXPECT_FALSE(client_state_key.HasValue(kRegValueInstallTimeSec));
-    EXPECT_FALSE(client_state_key.HasValue(kRegValueTTToken));
-    EXPECT_FALSE(client_state_key.HasValue(kRegValueActivePingDayStartSec));
-    EXPECT_FALSE(client_state_key.HasValue(kRegValueRollCallDayStartSec));
-  }
-
-  void HandleSuccessfulUpdateCheckRequestSendTest_AllUpdated(bool is_machine) {
-    const CString client_state_key_name = AppendRegKeyPath(
-        ConfigManager::Instance()->registry_client_state(is_machine),
-        kGuid1);
-
-    // Create the test data.
-    AppData expected_data(guid1_, is_machine);
-    expected_data.set_version(_T("1.0.0.0"));
-    expected_data.set_iid(StringToGuid(kIid1));
-    expected_data.set_did_run(AppData::ACTIVE_RUN);
-    // Set non-zero values for activities so that the registry values can
-    // be updated.
-    expected_data.set_days_since_last_active_ping(4);
-    expected_data.set_days_since_last_roll_call(2);
-    CreateAppRegistryState(expected_data);
-
-    ProductData product_data;
-    AppManager app_manager(is_machine);
-    EXPECT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_,
-                                                          &product_data));
-    AppData app_data_temp = product_data.app_data();
-
-    // We only want to make sure the timestamps in the registry are updated and
-    // don't care about time_since_midnight_sec here, so just pass a 0 as the
-    // first parameter.
-    EXPECT_SUCCEEDED(app_manager.HandleSuccessfulUpdateCheckRequestSend(
-        0, &app_data_temp));
-
-    // Validate the results.
-    RegKey client_state_key;
-    EXPECT_SUCCEEDED(client_state_key.Open(client_state_key_name));
-
-    // Check installation id removed.
-    EXPECT_FALSE(client_state_key.HasValue(kRegValueInstallationId));
-
-    // Check ping timestamps are updated.
-    const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-    DWORD last_active_ping_day_start_sec = 0;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueActivePingDayStartSec,
-        &last_active_ping_day_start_sec));
-    EXPECT_GE(now, last_active_ping_day_start_sec);
-    EXPECT_LE(now, last_active_ping_day_start_sec + kMaxTimeSinceMidnightSec);
-    EXPECT_EQ(0, app_data_temp.days_since_last_active_ping());
-
-    DWORD last_roll_call_day_start_sec = 0;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueRollCallDayStartSec,
-        &last_roll_call_day_start_sec));
-    EXPECT_GE(now, last_roll_call_day_start_sec);
-    EXPECT_LE(now, last_roll_call_day_start_sec + kMaxTimeSinceMidnightSec);
-    EXPECT_EQ(0, app_data_temp.days_since_last_roll_call());
-
-    // Check did_run is cleared.
-    CString did_run;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueDidRun, &did_run));
-    EXPECT_STREQ(_T("0"), did_run);
-  }
-
-  void HandleSuccessfulUpdateCheckRequestSendTest_NotRun(bool is_machine) {
-    const CString client_state_key_name = AppendRegKeyPath(
-        ConfigManager::Instance()->registry_client_state(is_machine),
-        kGuid1);
-    const int kDaysSinceLastActivePing = 2;
-
-    // Create the test data.
-    AppData expected_data(guid1_, is_machine);
-    expected_data.set_version(_T("1.0.0.0"));
-    expected_data.set_iid(StringToGuid(kIid1));
-    expected_data.set_did_run(AppData::ACTIVE_NOTRUN);
-    expected_data.set_days_since_last_active_ping(kDaysSinceLastActivePing);
-    expected_data.set_days_since_last_roll_call(0);
-    CreateAppRegistryState(expected_data);
-
-    // Choose a time that is close to current time but with some skew so that
-    // if the registry is rewritten, we won't write the same value again and
-    // the change would be detected.
-    const uint32 base_time = Time64ToInt32(GetCurrent100NSTime()) - 2;
-
-    RegKey client_state_key;
-    EXPECT_SUCCEEDED(client_state_key.Open(client_state_key_name));
-    uint32 last_active_time =
-        base_time - kDaysSinceLastActivePing * kSecondsPerDay;
-    ASSERT_SUCCEEDED(client_state_key.SetValue(
-        kRegValueActivePingDayStartSec,
-        static_cast<DWORD>(last_active_time)));
-
-    ASSERT_SUCCEEDED(client_state_key.SetValue(
-        kRegValueRollCallDayStartSec,
-        static_cast<DWORD>(base_time)));
-
-    ProductData product_data;
-    AppManager app_manager(is_machine);
-    EXPECT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_,
-                                                          &product_data));
-    AppData app_data_temp = product_data.app_data();
-
-    // We only want to make sure the timestamps in the registry are updated and
-    // don't care about time_since_midnight_sec here, so just pass a 0 as the
-    // first parameter.
-    EXPECT_SUCCEEDED(app_manager.HandleSuccessfulUpdateCheckRequestSend(
-        0, &app_data_temp));
-
-    // Validate the results.
-
-    // did_run is false so installation id should still exist.
-    CString iid;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueInstallationId, &iid));
-    EXPECT_STREQ(kIid1, iid);
-
-
-    // did_run is false so active ping timestamp should not be updated.
-    DWORD last_active_ping_day_start_sec = 0;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueActivePingDayStartSec,
-        &last_active_ping_day_start_sec));
-    EXPECT_EQ(last_active_time, last_active_ping_day_start_sec);
-    EXPECT_EQ(kDaysSinceLastActivePing,
-              app_data_temp.days_since_last_active_ping());
-
-    // Previous days_since_last_roll_call is 0 so that timestamp should
-    // not change.
-    DWORD last_roll_call_day_start_sec = 0;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueRollCallDayStartSec,
-        &last_roll_call_day_start_sec));
-    EXPECT_EQ(base_time, last_roll_call_day_start_sec);
-    EXPECT_EQ(0, app_data_temp.days_since_last_roll_call());
-
-    // did_run is still not set.
-    CString did_run;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueDidRun, &did_run));
-    EXPECT_STREQ(_T("0"), did_run);
-  }
-
-  void HandleSuccessfulUpdateCheckRequestSendTest_NoPreviousPing(
-      bool is_machine) {
-    const CString client_state_key_name = AppendRegKeyPath(
-        ConfigManager::Instance()->registry_client_state(is_machine),
-        kGuid1);
-    const int kDaysSinceLastActivePing = 2;
-
-    // Create the test data.
-    AppData expected_data(guid1_, is_machine);
-    expected_data.set_version(_T("1.0.0.0"));
-    expected_data.set_iid(StringToGuid(kIid1));
-    expected_data.set_did_run(AppData::ACTIVE_UNKNOWN);
-    expected_data.set_days_since_last_active_ping(-1);
-    expected_data.set_days_since_last_roll_call(-1);
-    CreateAppRegistryState(expected_data);
-
-    ProductData product_data;
-    AppManager app_manager(is_machine);
-    EXPECT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_,
-                                                          &product_data));
-    AppData app_data_temp = product_data.app_data();
-
-    // We only want to make sure the timestamps in the registry are updated and
-    // don't care about time_since_midnight_sec here, so just pass a 0 as the
-    // first parameter.
-    EXPECT_SUCCEEDED(app_manager.HandleSuccessfulUpdateCheckRequestSend(
-        0, &app_data_temp));
-
-    // Validate the results.
-    RegKey client_state_key;
-    EXPECT_SUCCEEDED(client_state_key.Open(client_state_key_name));
-
-    // did_run is unknown so installation id should still exist.
-    CString iid;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueInstallationId, &iid));
-    EXPECT_STREQ(kIid1, iid);
-
-    const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-    // did_run is unknown so active ping timestamp should not be updated.
-    EXPECT_FALSE(client_state_key.HasValue(kRegValueActivePingDayStartSec));
-    EXPECT_EQ(-1, app_data_temp.days_since_last_active_ping());
-
-    DWORD last_roll_call_day_start_sec = 0;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueRollCallDayStartSec,
-        &last_roll_call_day_start_sec));
-    EXPECT_GE(now, last_roll_call_day_start_sec);
-    EXPECT_LE(now, last_roll_call_day_start_sec + kMaxTimeSinceMidnightSec);
-    EXPECT_EQ(0, app_data_temp.days_since_last_roll_call());
-
-    // did_run is unknown.
-    CString did_run;
-    EXPECT_FALSE(client_state_key.HasValue(kRegValueDidRun));
-  }
-
-  void UpdateApplicationStateTest(bool is_machine, const CString& app_id) {
-    const CString client_state_key_name = AppendRegKeyPath(
-        ConfigManager::Instance()->registry_client_state(is_machine),
-        app_id);
-
-    // Create the test data.
-    AppData expected_data(StringToGuid(app_id), is_machine);
-    PopulateExpectedAppData1(&expected_data);
-    expected_data.set_referral_id(_T("referrer"));
-    expected_data.set_is_eula_accepted(false);
-    expected_data.set_install_time_diff_sec(141516);
-    CreateAppRegistryState(expected_data);
-
-    EXPECT_TRUE(RegKey::HasValue(client_state_key_name, _T("referral")));
-
-    // Call the test method.
-    ProductData product_data;
-    AppManager app_manager(is_machine);
-    EXPECT_SUCCEEDED(app_manager.ReadProductDataFromStore(StringToGuid(app_id),
-                                                          &product_data));
-
-    EXPECT_TRUE(product_data.app_data().referral_id().IsEmpty());
-
-    AppData app_data_temp = product_data.app_data();
-
-    EXPECT_SUCCEEDED(app_manager.UpdateApplicationState(&app_data_temp));
-
-    product_data.set_app_data(app_data_temp);
-
-    EXPECT_TRUE(app_data_temp.referral_id().IsEmpty());
-
-    // Need to call again to refresh the values created/copied by
-    // UpdateApplicationState().
-    EXPECT_SUCCEEDED(app_manager.ReadProductDataFromStore(StringToGuid(app_id),
-                                                          &product_data));
-    const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-    EXPECT_TRUE(product_data.app_data().referral_id().IsEmpty());
-
-    // Validate the results.
-    RegKey client_state_key;
-    EXPECT_SUCCEEDED(client_state_key.Open(client_state_key_name));
-
-    // Check version and language have been copied to client state.
-    CString previous_version;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueProductVersion,
-                                               &previous_version));
-    EXPECT_STREQ(expected_data.version(), previous_version);
-
-    CString language;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueLanguage,
-                                               &language));
-    EXPECT_STREQ(expected_data.language(), language);
-
-    // Check that ap, brand_code, and client_id are not changed.
-    CString ap;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueAdditionalParams,
-                                               &ap));
-    EXPECT_STREQ(expected_data.ap(), ap);
-
-    CString tt_token;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueTTToken,
-                                               &tt_token));
-    EXPECT_STREQ(expected_data.tt_token(), tt_token);
-
-    CString brand_code;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueBrandCode,
-                                               &brand_code));
-    EXPECT_STREQ(expected_data.brand_code(), brand_code);
-
-    CString client_id;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueClientId,
-                                               &client_id));
-    EXPECT_STREQ(expected_data.client_id(), client_id);
-
-    // install_time_diff_sec should be roughly the same as now - installed.
-    DWORD install_time(0);
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueInstallTimeSec,
-                                               &install_time));
-    const DWORD calculated_install_diff = now - install_time;
-    EXPECT_GE(calculated_install_diff, expected_data.install_time_diff_sec());
-    EXPECT_GE(static_cast<uint32>(500),
-              calculated_install_diff - expected_data.install_time_diff_sec());
-
-    DWORD eula_accepted = 0;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(_T("eulaaccepted"),
-                                               &eula_accepted));
-    EXPECT_EQ(0, eula_accepted);
-    EXPECT_FALSE(expected_data.is_eula_accepted());
-  }
-
-  void WritePreInstallDataTest(const AppData& app_data_in) {
-    const bool is_machine = app_data_in.is_machine_app();
-    const CString client_key_name = AppendRegKeyPath(
-        ConfigManager::Instance()->registry_clients(is_machine), kGuid1);
-    const CString client_state_key_name = AppendRegKeyPath(
-        ConfigManager::Instance()->registry_client_state(is_machine),
-        kGuid1);
-
-    const bool expect_has_client_key = RegKey::HasKey(client_key_name);
-
-    // Populate the test data.
-    AppData app_data(app_data_in);
-    app_data.set_brand_code(_T("GGLG"));
-    app_data.set_client_id(_T("someclient"));
-    app_data.set_referral_id(_T("referrer"));
-    app_data.set_install_time_diff_sec(657812);   // Not used.
-    app_data.set_usage_stats_enable(TRISTATE_TRUE);
-    app_data.set_browser_type(BROWSER_FIREFOX);
-    app_data.set_ap(_T("test_ap"));
-    app_data.set_language(_T("en"));
-    app_data.set_version(_T("1.2.3.4"));
-
-    AppManager app_manager(is_machine);
-    app_manager.WritePreInstallData(app_data);
-    const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-    // Validate the results.
-
-    // WritePreInstallData should never write to client_key, so it shouldn't
-    // exist if it did not before the method call.
-    EXPECT_EQ(expect_has_client_key, RegKey::HasKey(client_key_name));
-
-    // ClientStateKey should exist.
-    RegKey client_state_key;
-    EXPECT_SUCCEEDED(client_state_key.Open(client_state_key_name));
-
-    ValidateClientStateMedium(is_machine, kGuid1);
-
-    CString brand_code;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueBrandCode,
-                                               &brand_code));
-    EXPECT_STREQ(_T("GGLG"), brand_code);
-
-    CString client_id;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueClientId, &client_id));
-    EXPECT_STREQ(_T("someclient"), client_id);
-
-    CString referral_id;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueReferralId,
-                                               &referral_id));
-    EXPECT_STREQ(_T("referrer"), referral_id);
-
-    DWORD install_time(0);
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueInstallTimeSec,
-                                               &install_time));
-    EXPECT_GE(now, install_time);
-    EXPECT_GE(static_cast<uint32>(200), now - install_time);
-
-    DWORD usage_stats_enable = 0;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(_T("usagestats"),
-                                               &usage_stats_enable));
-    EXPECT_EQ(TRISTATE_TRUE, usage_stats_enable);
-
-    DWORD browser_type = 0;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueBrowser,
-                                               &browser_type));
-    EXPECT_EQ(BROWSER_FIREFOX, browser_type);
-
-    CString ap;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueAdditionalParams, &ap));
-    EXPECT_STREQ(_T("test_ap"), ap);
-
-    CString lang;
-    EXPECT_SUCCEEDED(client_state_key.GetValue(kRegValueLanguage, &lang));
-    EXPECT_STREQ(_T("en"), lang);
-
-    // Version should not be written to clientstate by WritePreInstallData().
-    EXPECT_FALSE(RegKey::HasValue(client_state_key_name,
-                                  kRegValueProductVersion));
-  }
-
-  void ValidateClientStateMedium(bool is_machine, const CString& app_guid) {
-    const CString client_state_medium_key_name = AppendRegKeyPath(
-        ConfigManager::Instance()->machine_registry_client_state_medium(),
-        app_guid);
-    if (is_machine) {
-      RegKey client_state_medium_key;
-      EXPECT_SUCCEEDED(
-          client_state_medium_key.Open(client_state_medium_key_name));
-      EXPECT_EQ(0, client_state_medium_key.GetValueCount());
-    } else {
-      EXPECT_FALSE(RegKey::HasKey(client_state_medium_key_name));
-      // There is no such thing as a user ClientStateMedium key.
-      const CString user_client_state_medium_key_name = AppendRegKeyPath(
-          USER_KEY GOOPDATE_REG_RELATIVE_CLIENT_STATE_MEDIUM,
-          app_guid);
-      EXPECT_FALSE(RegKey::HasKey(user_client_state_medium_key_name));
-      return;
-    }
-  }
-
-  // Uses SetupGoogleUpdate to create the ClientStateMedium key with the
-  // appropriate permissions. Used to test that the permissions are inherited.
-  void CreateClientStateMediumKey() {
-    CommandLineArgs args;
-    SetupGoogleUpdate setup_google_update(true, &args);
-    EXPECT_SUCCEEDED(setup_google_update.CreateClientStateMedium());
-  }
-
-  CString hive_override_key_name_;
-  const GUID guid1_;
-};
-
-TEST_F(AppManagerTest, ConvertCommandLineToProductData_Succeeds) {
-  CommandLineAppArgs extra1;
-  extra1.app_guid = guid1_;
-  extra1.app_name = _T("foo");
-  extra1.needs_admin = false;
-  extra1.ap = _T("Test ap");
-  extra1.tt_token = _T("Test TT Token");
-  extra1.encoded_installer_data = _T("%20foobar");
-  extra1.install_data_index = _T("foobar");
-
-  CommandLineAppArgs extra2;
-  extra2.app_guid = StringToGuid(kGuid2);
-  extra2.app_name = _T("bar");
-  extra2.needs_admin = true;    // This gets ignored.
-  extra2.ap = _T("beta");
-  extra2.tt_token = _T("beta TT Token");
-
-  CommandLineAppArgs extra3;
-  extra3.app_guid = StringToGuid(kGuid3);
-  extra3.app_name = _T("bar");
-  extra3.needs_admin = true;    // This gets ignored.
-  extra3.ap = _T("beta");
-  extra3.tt_token = _T("beta TT Token");
-
-  CommandLineArgs args;
-  args.is_interactive_set = true;  // Not used.
-  args.is_machine_set = true;  // Not used.
-  args.is_crash_handler_disabled = true;  // Not used.
-  args.is_eula_required_set = true;
-  args.is_eula_required_set = true;  // Not used.
-  args.webplugin_urldomain = _T("http://nothing.google.com");  // Not used.
-  args.webplugin_args = _T("blah");  // Not used.
-  args.install_source = _T("one_click");
-  args.code_red_metainstaller_path = _T("foo.exe");  // Not used.
-  args.legacy_manifest_path = _T("bar.exe");  // Not used.
-  args.crash_filename = _T("foo.dmp");  // Not used.
-  args.extra.installation_id = StringToGuid(kIid1);
-  args.extra.brand_code = _T("GOOG");
-  args.extra.client_id = _T("someclient");
-  args.extra.referral_id = _T("referrer1");
-  args.extra.browser_type = BROWSER_IE;
-  args.extra.language = _T("abc");
-  args.extra.usage_stats_enable = TRISTATE_TRUE;
-  args.extra.apps.push_back(extra1);
-  args.extra.apps.push_back(extra2);
-  args.extra.apps.push_back(extra3);
-
-  AppData expected_data1(guid1_, false);
-  PopulateExpectedAppData1(&expected_data1);
-  expected_data1.set_version(_T(""));  // Clear value.
-  expected_data1.set_previous_version(_T(""));  // Clear value.
-  expected_data1.set_did_run(AppData::ACTIVE_UNKNOWN);  // Clear value.
-  expected_data1.set_display_name(_T("foo"));
-  expected_data1.set_browser_type(BROWSER_IE);
-  expected_data1.set_install_source(_T("one_click"));
-  expected_data1.set_encoded_installer_data(_T("%20foobar"));
-  expected_data1.set_install_data_index(_T("foobar"));
-  expected_data1.set_usage_stats_enable(TRISTATE_TRUE);
-  expected_data1.set_referral_id(_T("referrer1"));
-  expected_data1.set_install_time_diff_sec(
-      static_cast<uint32>(-1 * kSecondsPerDay));  // New install.
-  expected_data1.set_is_eula_accepted(false);
-  expected_data1.set_days_since_last_active_ping(0);
-  expected_data1.set_days_since_last_roll_call(0);
-
-  AppData expected_data2(StringToGuid(kGuid2), false);
-
-  // Make the first app appear to already be installed but without an
-  // InstallTime. This affects install_time_diff_sec.
-  expected_data2.set_version(_T("4.5.6.7"));
-  CreateAppRegistryState(expected_data2);
-
-  PopulateExpectedAppData2(&expected_data2);
-  expected_data2.set_version(_T(""));  // Clear value.
-  expected_data2.set_previous_version(_T(""));  // Clear value.
-  expected_data2.set_did_run(AppData::ACTIVE_UNKNOWN);  // Clear value.
-  expected_data2.set_language(_T("abc"));
-  expected_data2.set_display_name(_T("bar"));
-  expected_data2.set_browser_type(BROWSER_IE);
-  expected_data2.set_install_source(_T("one_click"));
-  expected_data2.set_usage_stats_enable(TRISTATE_TRUE);
-  // Override unique expected data because the args apply to all apps.
-  expected_data2.set_iid(StringToGuid(kIid1));
-  expected_data2.set_brand_code(_T("GOOG"));
-  expected_data2.set_client_id(_T("someclient"));
-  expected_data2.set_referral_id(_T("referrer1"));
-  expected_data2.set_install_time_diff_sec(0);  // InstallTime is unknown.
-  expected_data2.set_is_eula_accepted(false);
-  expected_data2.set_days_since_last_active_ping(0);
-  expected_data2.set_days_since_last_roll_call(0);
-
-  AppData expected_data3(StringToGuid(kGuid3), false);
-
-  // Make the first app appear to already be installed with a valid InstallTime.
-  // This affects install_time_diff_sec.
-  expected_data3.set_version(_T("4.5.6.7"));
-  expected_data3.set_install_time_diff_sec(123456);  // Known original time.
-  CreateAppRegistryState(expected_data3);
-
-  PopulateExpectedAppData2(&expected_data3);
-  expected_data3.set_version(_T(""));  // Clear value.
-  expected_data3.set_previous_version(_T(""));  // Clear value.
-  expected_data3.set_did_run(AppData::ACTIVE_UNKNOWN);  // Clear value.
-  expected_data3.set_language(_T("abc"));
-  expected_data3.set_display_name(_T("bar"));
-  expected_data3.set_browser_type(BROWSER_IE);
-  expected_data3.set_install_source(_T("one_click"));
-  expected_data3.set_usage_stats_enable(TRISTATE_TRUE);
-  // Override unique expected data because the args apply to all apps.
-  expected_data3.set_iid(StringToGuid(kIid1));
-  expected_data3.set_brand_code(_T("GOOG"));
-  expected_data3.set_client_id(_T("someclient"));
-  expected_data3.set_referral_id(_T("referrer1"));
-  expected_data3.set_is_eula_accepted(false);
-  expected_data3.set_days_since_last_active_ping(0);
-  expected_data3.set_days_since_last_roll_call(0);
-
-  ProductDataVector products;
-  AppManager app_manager(false);
-  app_manager.ConvertCommandLineToProductData(args, &products);
-
-  ASSERT_EQ(3, products.size());
-  EXPECT_EQ(0, products[0].num_components());
-  EXPECT_EQ(0, products[1].num_components());
-  EXPECT_EQ(0, products[2].num_components());
-  ValidateExpectedValues(expected_data1, products[0].app_data());
-  ValidateExpectedValues(expected_data2, products[1].app_data());
-
-  // install_time_diff_sec may be off by a second or so.
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-  EXPECT_GE(products[2].app_data().install_time_diff_sec(),
-            expected_data3.install_time_diff_sec());
-  EXPECT_GE(static_cast<uint32>(500),
-            products[2].app_data().install_time_diff_sec() -
-            expected_data3.install_time_diff_sec());
-  // Fix up expected_data3 or it might fail verification.
-  expected_data3.set_install_time_diff_sec(
-      products[2].app_data().install_time_diff_sec());
-
-  ValidateExpectedValues(expected_data3, products[2].app_data());
-}
-
-TEST_F(AppManagerTest, WritePreInstallData_Machine) {
-  AppData app_data(guid1_, true);
-  ASSERT1(app_data.is_eula_accepted());
-  WritePreInstallDataTest(app_data);
-
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathMachine,
-                                _T("oeminstall")));
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathMachine,
-                                _T("eulaaccepted")));
-}
-
-TEST_F(AppManagerTest, WritePreInstallData_Machine_IsOem) {
-  const DWORD now = Time64ToInt32(GetCurrent100NSTime());
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("OemInstallTime"),
-                                    now));
-  if (vista_util::IsVistaOrLater()) {
-    ASSERT_SUCCEEDED(RegKey::SetValue(
-        _T("HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State"),
-        _T("ImageState"),
-        _T("IMAGE_STATE_UNDEPLOYABLE")));
-  } else {
-    ASSERT_SUCCEEDED(RegKey::SetValue(_T("HKLM\\System\\Setup"),
-                                      _T("AuditInProgress"),
-                                      static_cast<DWORD>(1)));
-  }
-
-  AppData app_data(guid1_, true);
-  ASSERT1(app_data.is_eula_accepted());
-  WritePreInstallDataTest(app_data);
-
-  CString oeminstall;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kGuid1ClientStateKeyPathMachine,
-                                    _T("oeminstall"),
-                                    &oeminstall));
-  EXPECT_STREQ(_T("1"), oeminstall);
-
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathMachine,
-                                _T("eulaaccepted")));
-}
-
-// Creates the ClientStateMedium key with the appropriate permissions then
-// verifies that the created app subkey inherits those.
-// The Update key must be created first to avoid applying ClientStateMedium's
-// permissions to all its parent keys.
-// This keys in this test need to inherit the HKLM privileges, so put the
-// override root in HKLM.
-TEST_F(AppManagerTest,
-       WritePreInstallData_Machine_CheckClientStateMediumPermissions) {
-  const TCHAR kRegistryHiveOverrideRootInHklm[] =
-      _T("HKLM\\Software\\Google\\Update\\UnitTest\\");
-  RestoreRegistryHives();
-  hive_override_key_name_ = kRegistryHiveOverrideRootInHklm;
-  RegKey::DeleteKey(hive_override_key_name_);
-  OverrideRegistryHives(hive_override_key_name_);
-
-  EXPECT_SUCCEEDED(RegKey::CreateKey(
-      ConfigManager::Instance()->machine_registry_update()));
-  CreateClientStateMediumKey();
-
-  AppData app_data(guid1_, true);
-  ASSERT1(app_data.is_eula_accepted());
-  WritePreInstallDataTest(app_data);
-
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathMachine,
-                                _T("oeminstall")));
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathMachine,
-                                _T("eulaaccepted")));
-
-  const CString app_client_state_medium_key_name = AppendRegKeyPath(
-      _T("HKLM\\Software\\Google\\Update\\ClientStateMedium\\"),
-      kGuid1);
-  VerifyHklmKeyHasMediumIntegrity(app_client_state_medium_key_name);
-  VerifyHklmKeyHasDefaultIntegrity(
-      _T("HKLM\\Software\\Google\\Update\\ClientStateMedium\\"));
-}
-
-TEST_F(AppManagerTest,
-       WritePreInstallData_Machine_ClearClientStateMediumUsageStats) {
-  const CString client_state_key_name =
-      AppendRegKeyPath(MACHINE_REG_CLIENT_STATE_MEDIUM, kGuid1);
-  EXPECT_SUCCEEDED(RegKey::SetValue(client_state_key_name,
-                                    _T("usagestats"),
-                                    static_cast<DWORD>(1)));
-
-  AppData app_data(guid1_, true);
-  WritePreInstallDataTest(app_data);
-
-  EXPECT_FALSE(RegKey::HasValue(client_state_key_name, _T("usagestats")));
-}
-
-// Tests the EULA accepted case too.
-TEST_F(AppManagerTest, WritePreInstallData_User) {
-  AppData app_data(guid1_, false);
-  ASSERT1(app_data.is_eula_accepted());
-  WritePreInstallDataTest(app_data);
-
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("oeminstall")));
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("eulaaccepted")));
-}
-
-TEST_F(AppManagerTest,
-       WritePreInstallData_User_EulaNotAcceptedAppNotRegistered) {
-  AppData app_data(guid1_, false);
-  app_data.set_is_eula_accepted(false);
-
-  WritePreInstallDataTest(app_data);
-
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("oeminstall")));
-
-  DWORD eula_accepted = 99;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("eulaaccepted"),
-                                    &eula_accepted));
-  EXPECT_EQ(0, eula_accepted);
-}
-
-TEST_F(AppManagerTest,
-       WritePreInstallData_User_EulaNotAcceptedAppAlreadyInstalled) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  AppData app_data(guid1_, false);
-  app_data.set_is_eula_accepted(false);
-
-  WritePreInstallDataTest(app_data);
-
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("oeminstall")));
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("eulaaccepted")));
-}
-
-TEST_F(AppManagerTest,
-       WritePreInstallData_User_EulaAcceptedAppAlreadyInstalled) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-
-  AppData app_data(guid1_, false);
-  app_data.set_is_eula_accepted(true);
-
-  WritePreInstallDataTest(app_data);
-
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("oeminstall")));
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("eulaaccepted")));
-}
-
-TEST_F(AppManagerTest,
-       WritePreInstallData_User_EulaAcceptedAppAlreadyInstalledAccepted) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-
-  AppData app_data(guid1_, false);
-  app_data.set_is_eula_accepted(true);
-
-  WritePreInstallDataTest(app_data);
-
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("oeminstall")));
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("eulaaccepted")));
-}
-
-TEST_F(AppManagerTest,
-       WritePreInstallData_User_EulaAcceptedAppAlreadyInstalledNotAccepted) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientsKeyPathUser,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  AppData app_data(guid1_, false);
-  app_data.set_is_eula_accepted(true);
-
-  WritePreInstallDataTest(app_data);
-
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("oeminstall")));
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("eulaaccepted")));
-}
-
-TEST_F(AppManagerTest, ReadProductDataFromStore_MachineNoAppTest) {
-  ProductData product_data;
-  AppManager app_manager(true);
-  ASSERT_FAILED(app_manager.ReadProductDataFromStore(guid1_, &product_data));
-}
-
-TEST_F(AppManagerTest, ReadProductDataFromStore_UserAppTest) {
-  AppData expected_data(guid1_, false);
-  PopulateExpectedAppData1(&expected_data);
-  CreateAppRegistryState(expected_data);
-
-  AppManager app_manager(false);
-  ProductData product_data;
-  ASSERT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_, &product_data));
-  ValidateExpectedValues(expected_data, product_data.app_data());
-}
-
-TEST_F(AppManagerTest, ReadProductDataFromStore_MachineAppTest) {
-  AppData expected_data(guid1_, true);
-  PopulateExpectedAppData1(&expected_data);
-  CreateAppRegistryState(expected_data);
-
-  AppManager app_manager(true);
-  ProductData product_data;
-  ASSERT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_, &product_data));
-  ValidateExpectedValues(expected_data, product_data.app_data());
-}
-
-TEST_F(AppManagerTest, ReadProductDataFromStore_UserAppTest_EulaNotAccepted) {
-  AppData expected_data(guid1_, false);
-  PopulateExpectedAppData1(&expected_data);
-  CreateAppRegistryState(expected_data);
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  expected_data.set_is_eula_accepted(false);
-
-  AppManager app_manager(false);
-  ProductData product_data;
-  ASSERT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_, &product_data));
-  ValidateExpectedValues(expected_data, product_data.app_data());
-}
-
-TEST_F(AppManagerTest, ReadProductDataFromStore_UserAppTest_EulaAccepted) {
-  AppData expected_data(guid1_, false);
-  PopulateExpectedAppData1(&expected_data);
-  CreateAppRegistryState(expected_data);
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  expected_data.set_is_eula_accepted(true);
-
-  AppManager app_manager(false);
-  ProductData product_data;
-  ASSERT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_, &product_data));
-  ValidateExpectedValues(expected_data, product_data.app_data());
-}
-
-TEST_F(AppManagerTest,
-       ReadProductDataFromStore_MachineAppTest_EulaNotAccepted) {
-  AppData expected_data(guid1_, true);
-  PopulateExpectedAppData1(&expected_data);
-  CreateAppRegistryState(expected_data);
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathMachine,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  expected_data.set_is_eula_accepted(false);
-
-  AppManager app_manager(true);
-  ProductData product_data;
-  ASSERT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_, &product_data));
-  ValidateExpectedValues(expected_data, product_data.app_data());
-}
-
-TEST_F(AppManagerTest,
-       ReadProductDataFromStore_MachineAppTest_EulaAccepted) {
-  AppData expected_data(guid1_, true);
-  PopulateExpectedAppData1(&expected_data);
-  CreateAppRegistryState(expected_data);
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathMachine,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-  expected_data.set_is_eula_accepted(true);
-
-  AppManager app_manager(true);
-  ProductData product_data;
-  ASSERT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_, &product_data));
-  ValidateExpectedValues(expected_data, product_data.app_data());
-}
-
-TEST_F(AppManagerTest, ReadProductDataFromStore_TwoUserAppTest) {
-  AppData expected_data1(guid1_, false);
-  PopulateExpectedAppData1(&expected_data1);
-  CreateAppRegistryState(expected_data1);
-
-  AppData expected_data2(StringToGuid(kGuid2), false);
-  PopulateExpectedAppData1(&expected_data2);
-  CreateAppRegistryState(expected_data2);
-
-  AppManager app_manager(false);
-  ProductData data1;
-  ASSERT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_, &data1));
-  ValidateExpectedValues(expected_data1, data1.app_data());
-
-  ProductData data2;
-  ASSERT_SUCCEEDED(app_manager.ReadProductDataFromStore(StringToGuid(kGuid2),
-                                                        &data2));
-  ValidateExpectedValues(expected_data2, data2.app_data());
-}
-
-TEST_F(AppManagerTest, ReadProductDataFromStore_UserAppNoClientStateTest) {
-  AppData expected_data(guid1_, false);
-  PopulateExpectedAppData1(&expected_data);
-  CreateAppRegistryState(expected_data);
-
-  AppManager app_manager(false);
-  ProductData data;
-  ASSERT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_, &data));
-  ValidateExpectedValues(expected_data, data.app_data());
-}
-
-TEST_F(AppManagerTest, ReadProductDataFromStore_UninstalledUserApp) {
-  AppData expected_data(guid1_, false);
-  PopulateExpectedUninstalledAppData(&expected_data);
-  CreateAppRegistryState(expected_data);
-
-  AppManager app_manager(false);
-  ProductData data;
-  ASSERT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_, &data));
-  ValidateExpectedValues(expected_data, data.app_data());
-}
-
-TEST_F(AppManagerTest, ReadProductDataFromStore_UninstalledMachineApp) {
-  AppData expected_data(guid1_, true);
-  PopulateExpectedUninstalledAppData(&expected_data);
-  CreateAppRegistryState(expected_data);
-
-  AppManager app_manager(true);
-  ProductData data;
-  ASSERT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_, &data));
-  ValidateExpectedValues(expected_data, data.app_data());
-}
-
-TEST_F(AppManagerTest,
-       ReadProductDataFromStore_UninstalledUserApp_EulaNotAccepted) {
-  AppData expected_data(guid1_, false);
-  PopulateExpectedUninstalledAppData(&expected_data);
-  expected_data.set_is_eula_accepted(false);
-  CreateAppRegistryState(expected_data);
-
-  AppManager app_manager(false);
-  ProductData data;
-  ASSERT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_, &data));
-  ValidateExpectedValues(expected_data, data.app_data());
-}
-
-// Tests the case where Omaha has created the Client State key before running
-// the installer. Uses PopulateExpectedUninstalledAppData then clears pv before
-// writing the data to the registry. is_uninstalled_ is not set to false until
-// after CreateAppRegistryState to prevent Client key from being created.
-TEST_F(AppManagerTest,
-       ReadProductDataFromStore_UserClientStateExistsWithoutPvOrClientKey) {
-  AppData expected_data(guid1_, false);
-  PopulateExpectedUninstalledAppData(&expected_data);
-  expected_data.set_previous_version(_T(""));
-  CreateAppRegistryState(expected_data);
-
-  AppManager app_manager(false);
-  ProductData product_data;
-  ASSERT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_, &product_data));
-
-  expected_data.set_is_uninstalled(false);
-  ValidateExpectedValues(expected_data, product_data.app_data());
-}
-
-TEST_F(AppManagerTest,
-       ReadProductDataFromStore_MachineClientStateExistsWithoutPvOrClientKey) {
-  AppData expected_data(guid1_, true);
-  PopulateExpectedUninstalledAppData(&expected_data);
-  expected_data.set_previous_version(_T(""));
-  CreateAppRegistryState(expected_data);
-
-  AppManager app_manager(true);
-  ProductData data;
-  ASSERT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_, &data));
-  expected_data.set_is_uninstalled(false);
-  ValidateExpectedValues(expected_data, data.app_data());
-}
-
-// An empty pv value is the same as a populated one for uninstall checks.
-TEST_F(AppManagerTest,
-       ReadProductDataFromStore_UserClientStateExistsWithEmptyPvNoClientKey) {
-  AppData expected_data(guid1_, false);
-  PopulateExpectedUninstalledAppData(&expected_data);
-  expected_data.set_previous_version(_T(""));
-  CreateAppRegistryState(expected_data);
-
-  // Write the empty pv value.
-  CString client_state_key_name = AppendRegKeyPath(
-      ConfigManager::Instance()->registry_client_state(false),
-      kGuid1);
-  ASSERT_SUCCEEDED(RegKey::SetValue(client_state_key_name,
-                                    kRegValueProductVersion,
-                                    _T("")));
-
-  AppManager app_manager(false);
-  ProductData product_data;
-  ASSERT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_, &product_data));
-
-  expected_data.set_is_uninstalled(true);
-  ValidateExpectedValues(expected_data, product_data.app_data());
-}
-
-TEST_F(AppManagerTest, InitializeApplicationState_UserTest) {
-  InitializeApplicationStateTest(false);
-}
-
-TEST_F(AppManagerTest, InitializeApplicationState_MachineTest) {
-  InitializeApplicationStateTest(true);
-}
-
-TEST_F(AppManagerTest,
-       HandleSuccessfulUpdateCheckRequestSendTest_AllUpdated_UserTest) {
-  HandleSuccessfulUpdateCheckRequestSendTest_AllUpdated(false);
-}
-
-TEST_F(AppManagerTest,
-       HandleSuccessfulUpdateCheckRequestSendTest_AllUpdated_MachineTest) {
-  HandleSuccessfulUpdateCheckRequestSendTest_AllUpdated(true);
-}
-
-TEST_F(AppManagerTest,
-       HandleSuccessfulUpdateCheckRequestSendTest_NotRun_UserTest) {
-  HandleSuccessfulUpdateCheckRequestSendTest_NotRun(false);
-}
-
-TEST_F(AppManagerTest,
-       HandleSuccessfulUpdateCheckRequestSendTest_NotRun_MachineTest) {
-  HandleSuccessfulUpdateCheckRequestSendTest_NotRun(true);
-}
-
-TEST_F(AppManagerTest,
-       HandleSuccessfulUpdateCheckRequestSendTest_NoPreviousPing_UserTest) {
-  HandleSuccessfulUpdateCheckRequestSendTest_NoPreviousPing(false);
-}
-
-TEST_F(AppManagerTest,
-       HandleSuccessfulUpdateCheckRequestSendTest_NoPreviousPing_MachineTest) {
-  HandleSuccessfulUpdateCheckRequestSendTest_NoPreviousPing(true);
-}
-
-
-TEST_F(AppManagerTest, UpdateApplicationState_UserTest) {
-  UpdateApplicationStateTest(false, kGuid1);
-
-  ValidateClientStateMedium(false, kGuid1);
-}
-
-TEST_F(AppManagerTest, UpdateApplicationState_MachineTest) {
-  UpdateApplicationStateTest(true, kGuid1);
-
-  ValidateClientStateMedium(true, kGuid1);
-}
-
-// Should not create ClientStateMedium key.
-TEST_F(AppManagerTest, UpdateApplicationState_MachineTest_Omaha) {
-  UpdateApplicationStateTest(true, kGoogleUpdateAppId);
-
-  const CString client_state_medium_key_name = AppendRegKeyPath(
-    ConfigManager::Instance()->machine_registry_client_state_medium(),
-    kGoogleUpdateAppId);
-  EXPECT_FALSE(RegKey::HasKey(client_state_medium_key_name));
-}
-
-TEST_F(AppManagerTest, UpdateUpdateAvailableStats_NoExistingStats) {
-  const time64 before_time_in_100ns(GetCurrent100NSTime());
-
-  AppManager app_manager(false);
-  app_manager.UpdateUpdateAvailableStats(GUID_NULL, guid1_);
-
-  const time64 after_time_in_100ns(GetCurrent100NSTime());
-
-  DWORD update_available_count(0);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableCount"),
-                                    &update_available_count));
-  EXPECT_EQ(1, update_available_count);
-
-  DWORD64 update_available_since_time(0);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableSince"),
-                                    &update_available_since_time));
-  EXPECT_LE(before_time_in_100ns, update_available_since_time);
-  EXPECT_GE(after_time_in_100ns, update_available_since_time);
-  const DWORD64 time_since_first_update_available =
-      after_time_in_100ns - update_available_since_time;
-  EXPECT_GT(10 * kSecsTo100ns, time_since_first_update_available);
-}
-
-TEST_F(AppManagerTest, UpdateUpdateAvailableStats_WithExistingStats) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableCount"),
-                                    static_cast<DWORD>(123456)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableSince"),
-                                    static_cast<DWORD64>(9876543210)));
-
-  AppManager app_manager(false);
-  app_manager.UpdateUpdateAvailableStats(GUID_NULL, guid1_);
-
-  DWORD update_available_count(0);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableCount"),
-                                    &update_available_count));
-  EXPECT_EQ(123457, update_available_count);
-
-  DWORD64 update_available_since_time(0);
-  EXPECT_SUCCEEDED(RegKey::GetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableSince"),
-                                    &update_available_since_time));
-  EXPECT_EQ(9876543210, update_available_since_time);
-}
-
-TEST_F(AppManagerTest, ClearUpdateAvailableStats_KeyNotPresent) {
-  AppManager app_manager(false);
-  ClearUpdateAvailableStats(GUID_NULL, guid1_, &app_manager);
-}
-
-TEST_F(AppManagerTest, ClearUpdateAvailableStats_DataPresent) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableCount"),
-                                    static_cast<DWORD>(123456)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableSince"),
-                                    static_cast<DWORD64>(9876543210)));
-
-  AppManager app_manager(false);
-  ClearUpdateAvailableStats(GUID_NULL, guid1_, &app_manager);
-
-  EXPECT_TRUE(RegKey::HasKey(kGuid1ClientStateKeyPathUser));
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("UpdateAvailableCount")));
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("UpdateAvailableSince")));
-}
-
-TEST_F(AppManagerTest, ReadUpdateAvailableStats_DataNotPresent) {
-  RegKey::CreateKey(kGuid1ClientStateKeyPathUser);
-
-  DWORD update_responses(1);
-  DWORD64 time_since_first_response_ms(1);
-  AppManager app_manager(false);
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       guid1_,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-
-  EXPECT_EQ(0, update_responses);
-  EXPECT_EQ(0, time_since_first_response_ms);
-}
-
-TEST_F(AppManagerTest, ReadUpdateAvailableStats_DataPresent) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableCount"),
-                                    static_cast<DWORD>(123456)));
-  const DWORD64 kUpdateAvailableSince =
-    GetCurrent100NSTime() - 2 * kMillisecsTo100ns;
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableSince"),
-                                    kUpdateAvailableSince));
-
-  DWORD update_responses(0);
-  DWORD64 time_since_first_response_ms(0);
-  AppManager app_manager(false);
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       guid1_,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-
-  EXPECT_EQ(123456, update_responses);
-  EXPECT_LE(2, time_since_first_response_ms);
-  EXPECT_GT(10 * kMsPerSec, time_since_first_response_ms);
-}
-
-// TODO(omaha): Add *UpdateAvailableStats tests with components when
-// component design is finalized and implemented
-
-TEST_F(AppManagerTest, RecordSuccessfulInstall_Install_Online) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableCount"),
-                                    static_cast<DWORD>(123456)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableSince"),
-                                    static_cast<DWORD64>(9876543210)));
-
-  AppManager app_manager(false);
-  app_manager.RecordSuccessfulInstall(GUID_NULL, guid1_, false, false);
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  // Verify ClearUpdateAvailableStats() was called.
-  EXPECT_TRUE(RegKey::HasKey(kGuid1ClientStateKeyPathUser));
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("UpdateAvailableCount")));
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("UpdateAvailableSince")));
-
-  // Verify update check value is written but update value is not.
-  const uint32 last_check_sec = GetDwordValue(kGuid1ClientStateKeyPathUser,
-                                              kRegValueLastSuccessfulCheckSec);
-  EXPECT_GE(now, last_check_sec);
-  EXPECT_GE(static_cast<uint32>(200), now - last_check_sec);
-
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                kRegValueLastUpdateTimeSec));
-}
-
-TEST_F(AppManagerTest, RecordSuccessfulInstall_Install_Offline) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableCount"),
-                                    static_cast<DWORD>(123456)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableSince"),
-                                    static_cast<DWORD64>(9876543210)));
-
-  AppManager app_manager(false);
-  app_manager.RecordSuccessfulInstall(GUID_NULL, guid1_, false, true);
-
-  // Verify ClearUpdateAvailableStats() was called.
-  EXPECT_TRUE(RegKey::HasKey(kGuid1ClientStateKeyPathUser));
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("UpdateAvailableCount")));
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("UpdateAvailableSince")));
-
-  // Verify update values are not written.
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                kRegValueLastSuccessfulCheckSec));
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                kRegValueLastUpdateTimeSec));
-}
-
-TEST_F(AppManagerTest, RecordSuccessfulInstall_Update_ExistingTimes) {
-  const DWORD kExistingUpdateValues = 0x70123456;
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableCount"),
-                                    static_cast<DWORD>(123456)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableSince"),
-                                    static_cast<DWORD64>(9876543210)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    kRegValueLastSuccessfulCheckSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    kRegValueLastUpdateTimeSec,
-                                    kExistingUpdateValues));
-
-  AppManager app_manager(false);
-  app_manager.RecordSuccessfulInstall(GUID_NULL, guid1_, true, false);
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  // Verify ClearUpdateAvailableStats() was called.
-  EXPECT_TRUE(RegKey::HasKey(kGuid1ClientStateKeyPathUser));
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("UpdateAvailableCount")));
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                _T("UpdateAvailableSince")));
-
-  // Verify update values updated.
-  const uint32 last_check_sec = GetDwordValue(kGuid1ClientStateKeyPathUser,
-                                              kRegValueLastSuccessfulCheckSec);
-  EXPECT_NE(kExistingUpdateValues, last_check_sec);
-  EXPECT_GE(now, last_check_sec);
-  EXPECT_GE(static_cast<uint32>(200), now - last_check_sec);
-
-  const uint32 last_update_sec =
-      GetDwordValue(kGuid1ClientStateKeyPathUser, kRegValueLastUpdateTimeSec);
-  EXPECT_NE(kExistingUpdateValues, last_update_sec);
-  EXPECT_GE(now, last_update_sec);
-  EXPECT_GE(static_cast<uint32>(200), now - last_update_sec);
-}
-
-TEST_F(AppManagerTest, RecordSuccessfulInstall_Update_StateKeyDoesNotExist) {
-  AppManager app_manager(false);
-  app_manager.RecordSuccessfulInstall(GUID_NULL, guid1_, true, false);
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  // Verify update values updated.
-  const uint32 last_check_sec = GetDwordValue(kGuid1ClientStateKeyPathUser,
-                                              kRegValueLastSuccessfulCheckSec);
-  EXPECT_GE(now, last_check_sec);
-  EXPECT_GE(static_cast<uint32>(200), now - last_check_sec);
-
-  const uint32 last_update_sec =
-      GetDwordValue(kGuid1ClientStateKeyPathUser, kRegValueLastUpdateTimeSec);
-  EXPECT_GE(now, last_update_sec);
-  EXPECT_GE(static_cast<uint32>(200), now - last_update_sec);
-}
-
-TEST_F(AppManagerTest, RecordSuccessfulUpdateCheck_ExistingTime) {
-  const DWORD kExistingUpdateValue = 0x12345678;
-  EXPECT_SUCCEEDED(RegKey::SetValue(kGuid1ClientStateKeyPathUser,
-                                    kRegValueLastSuccessfulCheckSec,
-                                    kExistingUpdateValue));
-
-  AppManager app_manager(false);
-  app_manager.RecordSuccessfulUpdateCheck(GUID_NULL, guid1_);
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  const uint32 last_check_sec = GetDwordValue(kGuid1ClientStateKeyPathUser,
-                                              kRegValueLastSuccessfulCheckSec);
-  EXPECT_NE(kExistingUpdateValue, last_check_sec);
-  EXPECT_GE(now, last_check_sec);
-  EXPECT_GE(static_cast<uint32>(200), now - last_check_sec);
-
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                kRegValueLastUpdateTimeSec));
-}
-
-TEST_F(AppManagerTest, RecordSuccessfulUpdateCheck_StateKeyDoesNotExist) {
-  AppManager app_manager(false);
-  app_manager.RecordSuccessfulUpdateCheck(GUID_NULL, guid1_);
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  const uint32 last_check_sec = GetDwordValue(kGuid1ClientStateKeyPathUser,
-                                              kRegValueLastSuccessfulCheckSec);
-  EXPECT_GE(now, last_check_sec);
-  EXPECT_GE(static_cast<uint32>(200), now - last_check_sec);
-
-  EXPECT_FALSE(RegKey::HasValue(kGuid1ClientStateKeyPathUser,
-                                kRegValueLastUpdateTimeSec));
-}
-
-TEST_F(AppManagerTest, RemoveClientState_Uninstalled) {
-  AppData expected_data(guid1_, true);
-  PopulateExpectedUninstalledAppData(&expected_data);
-  CreateAppRegistryState(expected_data);
-
-  AppManager app_manager(true);
-  ProductData data;
-  ASSERT_SUCCEEDED(app_manager.ReadProductDataFromStore(guid1_, &data));
-  ASSERT_SUCCEEDED(app_manager.RemoveClientState(data.app_data()));
-  ASSERT_FALSE(IsClientStateKeyPresent(expected_data));
-}
-
-class AppManagerTest2 : public testing::Test {
- protected:
-  AppManagerTest2()
-      : hive_override_key_name_(kRegistryHiveOverrideRoot) {}
-
-  virtual void SetUp() {
-    RegKey::DeleteKey(hive_override_key_name_);
-    OverrideRegistryHives(hive_override_key_name_);
-  }
-
-  virtual void TearDown() {
-    RestoreRegistryHives();
-    RegKey::DeleteKey(hive_override_key_name_);
-  }
-
-  CString hive_override_key_name_;
-};
-
-// Create 2 registered app and 1 unregistered app and populates the parameters.
-// Each is written to the registry.
-// Also creates partial Clients and ClientState keys and creates an a registered
-// and unregistered app in the opposite registry hive.
-void PopulateDataAndRegistryForRegisteredAndUnRegisteredApplicationsTests(
-    bool is_machine,
-    AppData* expected_data1,
-    AppData* expected_data2,
-    AppData* expected_data3) {
-
-  expected_data1->set_app_guid(StringToGuid(kGuid1));
-  expected_data1->set_is_machine_app(is_machine);
-  AppManagerTest::PopulateExpectedAppData1(expected_data1);
-  AppManagerTest::CreateAppRegistryState(*expected_data1);
-
-  expected_data2->set_app_guid(StringToGuid(kGuid2));
-  expected_data2->set_is_machine_app(is_machine);
-  AppManagerTest::PopulateExpectedAppData2(expected_data2);
-  AppManagerTest::CreateAppRegistryState(*expected_data2);
-
-  expected_data3->set_app_guid(StringToGuid(kGuid3));
-  expected_data3->set_is_machine_app(is_machine);
-  AppManagerTest::PopulateExpectedUninstalledAppData(expected_data3);
-  AppManagerTest::CreateAppRegistryState(*expected_data3);
-
-  // Add incomplete Clients and ClientState entries.
-  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(
-      AppendRegKeyPath(is_machine ? MACHINE_REG_CLIENTS : USER_REG_CLIENTS,
-                       kGuid4),
-      _T("name"),
-      _T("foo")));
-
-  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(
-      AppendRegKeyPath(is_machine ? MACHINE_REG_CLIENT_STATE :
-                                    USER_REG_CLIENT_STATE,
-                       kGuid5),
-      kRegValueDidRun,
-      _T("1")));
-
-  // Add registered and unregistered app to the opposite registry hive.
-  AppData opposite_hive_data1(StringToGuid(kGuid6), !is_machine);
-  AppManagerTest::PopulateExpectedAppData2(&opposite_hive_data1);
-  AppManagerTest::CreateAppRegistryState(opposite_hive_data1);
-
-  AppData opposite_hive_data2(StringToGuid(kGuid7), !is_machine);
-  AppManagerTest::PopulateExpectedUninstalledAppData(&opposite_hive_data2);
-  AppManagerTest::CreateAppRegistryState(opposite_hive_data2);
-}
-
-TEST_F(AppManagerTest2, GetRegisteredApplications_machine) {
-  AppData expected_data1, expected_data2, expected_data3;
-  PopulateDataAndRegistryForRegisteredAndUnRegisteredApplicationsTests(
-      true,
-      &expected_data1,
-      &expected_data2,
-      &expected_data3);
-
-  AppManager app_manager(true);
-
-  ProductDataVector products;
-  ASSERT_HRESULT_SUCCEEDED(app_manager.GetRegisteredProducts(&products));
-  ASSERT_EQ(2, products.size());
-
-  ASSERT_TRUE(::IsEqualGUID(products[0].app_data().app_guid(),
-              StringToGuid(kGuid1)));
-  ValidateExpectedValues(expected_data1, products[0].app_data());
-
-  ASSERT_TRUE(::IsEqualGUID(products[1].app_data().app_guid(),
-              StringToGuid(kGuid2)));
-  ValidateExpectedValues(expected_data2, products[1].app_data());
-}
-
-TEST_F(AppManagerTest2, GetRegisteredApplications_user) {
-  AppData expected_data1, expected_data2, expected_data3;
-  PopulateDataAndRegistryForRegisteredAndUnRegisteredApplicationsTests(
-      false,
-      &expected_data1,
-      &expected_data2,
-      &expected_data3);
-
-  AppManager app_manager(false);
-
-
-  ProductDataVector products;
-  ASSERT_HRESULT_SUCCEEDED(app_manager.GetRegisteredProducts(&products));
-  ASSERT_EQ(2, products.size());
-
-  ASSERT_TRUE(::IsEqualGUID(products[0].app_data().app_guid(),
-              StringToGuid(kGuid1)));
-  ValidateExpectedValues(expected_data1, products[0].app_data());
-
-  ASSERT_TRUE(::IsEqualGUID(products[1].app_data().app_guid(),
-              StringToGuid(kGuid2)));
-  ValidateExpectedValues(expected_data2, products[1].app_data());
-}
-
-TEST_F(AppManagerTest2, GetUnRegisteredApplications_machine) {
-  AppData expected_data1, expected_data2, expected_data3;
-  PopulateDataAndRegistryForRegisteredAndUnRegisteredApplicationsTests(
-      true,
-      &expected_data1,
-      &expected_data2,
-      &expected_data3);
-
-  AppManager app_manager(true);
-
-  ProductDataVector unreg_products;
-  ASSERT_HRESULT_SUCCEEDED(
-      app_manager.GetUnRegisteredProducts(&unreg_products));
-  ASSERT_EQ(1, unreg_products.size());
-
-  ValidateExpectedValues(expected_data3, unreg_products[0].app_data());
-}
-
-TEST_F(AppManagerTest2, GetUnRegisteredApplications_user) {
-  AppData expected_data1, expected_data2, expected_data3;
-  PopulateDataAndRegistryForRegisteredAndUnRegisteredApplicationsTests(
-      false,
-      &expected_data1,
-      &expected_data2,
-      &expected_data3);
-  AppManager app_manager(false);
-
-  ProductDataVector unreg_products;
-  ASSERT_HRESULT_SUCCEEDED(
-      app_manager.GetUnRegisteredProducts(&unreg_products));
-  ASSERT_EQ(1, unreg_products.size());
-
-  ValidateExpectedValues(expected_data3, unreg_products[0].app_data());
-}
-
-TEST_F(AppManagerTest2, UpdateLastChecked) {
-  AppManager app_manager(false);
-
-  EXPECT_SUCCEEDED(app_manager.UpdateLastChecked());
-  EXPECT_FALSE(app_manager.ShouldCheckForUpdates());
-
-  ConfigManager::Instance()->SetLastCheckedTime(false, 0);
-  EXPECT_TRUE(app_manager.ShouldCheckForUpdates());
-}
-
-TEST_F(AppManagerTest, ShouldCheckForUpdates_NoLastCheckedPresent) {
-  AppManager app_manager(false);
-  EXPECT_TRUE(app_manager.ShouldCheckForUpdates());
-}
-
-TEST_F(AppManagerTest, ShouldCheckForUpdates_LastCheckedPresent) {
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-  AppManager app_manager(false);
-
-  ConfigManager::Instance()->SetLastCheckedTime(false, now - 10);
-  EXPECT_FALSE(app_manager.ShouldCheckForUpdates());
-
-  ConfigManager::Instance()->SetLastCheckedTime(false,
-                                                now - kLastCheckPeriodSec - 1);
-  EXPECT_TRUE(app_manager.ShouldCheckForUpdates());
-}
-
-TEST_F(AppManagerTest, ShouldCheckForUpdates_LastCheckedInFuture) {
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-  AppManager app_manager(false);
-
-  // The absolute difference is within the check period.
-  ConfigManager::Instance()->SetLastCheckedTime(false, now + 600);
-  EXPECT_FALSE(app_manager.ShouldCheckForUpdates());
-
-  // The absolute difference is greater than the check period.
-  ConfigManager::Instance()->SetLastCheckedTime(false,
-                                                now + kLastCheckPeriodSec + 1);
-  EXPECT_TRUE(app_manager.ShouldCheckForUpdates());
-}
-
-TEST_F(AppManagerTest, ShouldCheckForUpdates_PeriodZero) {
-  EXPECT_SUCCEEDED(
-      RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
-                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
-                       static_cast<DWORD>(0)));
-
-  AppManager app_manager(false);
-  EXPECT_FALSE(app_manager.ShouldCheckForUpdates());
-}
-
-TEST_F(AppManagerTest, ShouldCheckForUpdates_PeriodOverride) {
-  const DWORD kOverrideMinutes = 10;
-  const DWORD kOverrideSeconds = kOverrideMinutes * 60;
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-  AppManager app_manager(false);
-
-  EXPECT_SUCCEEDED(
-      RegKey::SetValue(kRegKeyGoopdateGroupPolicy,
-                       kRegValueAutoUpdateCheckPeriodOverrideMinutes,
-                       kOverrideMinutes));
-
-  ConfigManager::Instance()->SetLastCheckedTime(false, now - 10);
-  EXPECT_FALSE(app_manager.ShouldCheckForUpdates());
-
-  ConfigManager::Instance()->SetLastCheckedTime(false,
-                                                now - kOverrideSeconds - 1);
-  EXPECT_TRUE(app_manager.ShouldCheckForUpdates());
-}
-
-}  // namespace omaha
diff --git a/worker/application_usage_data.cc b/worker/application_usage_data.cc
deleted file mode 100644
index 78dcbce..0000000
--- a/worker/application_usage_data.cc
+++ /dev/null
@@ -1,288 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/worker/application_usage_data.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
-
-namespace omaha {
-
-ApplicationUsageData::ApplicationUsageData(bool is_machine,
-                                           bool check_low_integrity)
-    : exists_(false),
-      did_run_(false),
-      is_machine_(is_machine),
-      is_pre_update_check_(true),
-      check_low_integrity_(check_low_integrity) {
-}
-
-ApplicationUsageData::~ApplicationUsageData() {
-}
-
-HRESULT ApplicationUsageData::ReadDidRun(const CString& app_guid) {
-  CORE_LOG(L4, (_T("[ApplicationUsageData::ReadDidRun][%s]"), app_guid));
-  is_pre_update_check_ = true;
-  return ProcessDidRun(app_guid);
-}
-
-HRESULT ApplicationUsageData::ResetDidRun(const CString& app_guid) {
-  CORE_LOG(L4, (_T("[ApplicationUsageData::ResetDidRun][%s]"), app_guid));
-  is_pre_update_check_ = false;
-  return ProcessDidRun(app_guid);
-}
-
-HRESULT ApplicationUsageData::ProcessDidRun(const CString& app_guid) {
-  CORE_LOG(L4, (_T("[ApplicationUsageData::ProcessDidRun][%s]"), app_guid));
-  return is_machine_ ? ProcessMachineDidRun(app_guid) :
-                       ProcessUserDidRun(app_guid);
-}
-
-HRESULT ApplicationUsageData::ProcessMachineDidRun(const CString& app_guid) {
-  ASSERT1(is_machine_);
-
-  // Logic is as follows:
-  // for each user under HKU\<sid>
-  //   pre/post process HKU\<sid>
-  //   if vista
-  //     pre/post process HKU\<lowintegrity IE>\<sid>
-  // pre/post process HKLM
-  RegKey users_key;
-  HRESULT hr = users_key.Open(USERS_KEY, KEY_READ);
-  if (SUCCEEDED(hr)) {
-    uint32 num_users = users_key.GetSubkeyCount();
-    for (uint32 i = 0; i < num_users; ++i) {
-      CString sub_key_name;
-      hr = users_key.GetSubkeyNameAt(i, &sub_key_name);
-      if (FAILED(hr)) {
-        CORE_LOG(LEVEL_WARNING, (_T("[Key enum failed.][0x%08x][%d][%s]"),
-                                 hr, i, USERS_KEY));
-        continue;
-      }
-
-      CString temp_key = AppendRegKeyPath(USERS_KEY,
-                                          sub_key_name,
-                                          GOOPDATE_REG_RELATIVE_CLIENT_STATE);
-      CString user_state_key_name = AppendRegKeyPath(temp_key, app_guid);
-      hr = ProcessKey(user_state_key_name);
-      if (FAILED(hr)) {
-        CORE_LOG(L4, (_T("[ProcessKey failed][%s][0x%08x]"), app_guid, hr));
-      }
-
-      if (check_low_integrity_) {
-        // If we are running on vista we need to also look at the low
-        // integrity IE key where IE can write to. Note that we cannot
-        // use the IEGetWriteableHKCU function since this function assumes
-        // that we are running with the user's credentials.
-        CString temp_key = AppendRegKeyPath(USERS_KEY,
-                                            sub_key_name,
-                                            USER_REG_VISTA_LOW_INTEGRITY_HKCU);
-        CString li_hkcu_name = AppendRegKeyPath(
-                                   AppendRegKeyPath(
-                                       temp_key,
-                                       sub_key_name,
-                                       GOOPDATE_REG_RELATIVE_CLIENT_STATE),
-                                   app_guid);
-        hr = ProcessKey(li_hkcu_name);
-        if (FAILED(hr)) {
-          CORE_LOG(L4, (_T("[ProcessKey failed][%s][0x%08x]"), app_guid, hr));
-        }
-      }
-    }  // End of for
-
-    // Now Process the machine did run value also.
-    CString machine_state_key_name =
-        goopdate_utils::GetAppClientStateKey(true, app_guid);
-    hr = ProcessBackWardCompatKey(machine_state_key_name);
-    if (FAILED(hr)) {
-      CORE_LOG(L4, (_T("[ProcessBackWardCompatKey failed][0x%08x][%s]"),
-                    hr, machine_state_key_name));
-    }
-  } else {
-    CORE_LOG(LW, (_T("[Key open failed.][0x%08x][%s]"), hr, USERS_KEY));
-  }
-
-  return S_OK;
-}
-
-HRESULT ApplicationUsageData::ProcessUserDidRun(const CString& app_guid) {
-  ASSERT1(!is_machine_);
-
-  // Logic:
-  // Pre/Post process HKCU\
-  // if vista:
-  //    Pre/Post process HKCU\LowIntegrity
-  CString state_key_name = goopdate_utils::GetAppClientStateKey(false,
-                                                                app_guid);
-  HRESULT hr = ProcessKey(state_key_name);
-  if (FAILED(hr)) {
-      CORE_LOG(L4, (_T("[ProcessKey failed][0x%08x][%s]"),
-                    hr, state_key_name));
-  }
-
-  if (check_low_integrity_) {
-    // If we are running on vista we need to also look at the low
-    // integrity IE key where IE can write to. To avoid loading
-    // ieframe.dll into our process, we just use the registry
-    // key location directly instead of using IEGetWriteableHKCU
-    CString sid;
-    hr = user_info::GetCurrentUser(NULL, NULL, &sid);
-    if (FAILED(hr)) {
-      CORE_LOG(LEVEL_WARNING, (_T("[GetCurrentUser failed][0x%08x][%s]"),
-                               hr, app_guid));
-      return hr;
-    }
-
-    CString temp_name = AppendRegKeyPath(USER_KEY_NAME,
-                                         USER_REG_VISTA_LOW_INTEGRITY_HKCU,
-                                         sid);
-    CString lowintegrity_hkcu_name = AppendRegKeyPath(
-                                         temp_name,
-                                         GOOPDATE_REG_RELATIVE_CLIENT_STATE,
-                                         app_guid);
-    hr = ProcessKey(lowintegrity_hkcu_name);
-    if (FAILED(hr)) {
-      CORE_LOG(LEVEL_WARNING, (_T("[Could not ProcessKey][0x%08x][%s]"),
-                               hr, app_guid));
-    }
-  }
-
-  return S_OK;
-}
-
-HRESULT ApplicationUsageData::ProcessKey(const CString& key_name) {
-  return is_pre_update_check_ ? ProcessPreUpdateCheck(key_name) :
-                                ProcessPostUpdateCheck(key_name);
-}
-
-HRESULT ApplicationUsageData::ProcessPreUpdateCheck(const CString& key_name) {
-  // Read in the regkey value if it exists, and or it with the previous value.
-  RegKey key;
-  HRESULT hr = key.Open(key_name, KEY_READ);
-  if (FAILED(hr)) {
-    CORE_LOG(L4, (_T("[failed to open key][%s][0x%08x]"), key_name, hr));
-    return hr;
-  }
-
-  // Now that we have the key, we should try and read the value of the
-  // did run key.
-  CString did_run_str(_T("0"));
-  hr = key.GetValue(kRegValueDidRun, &did_run_str);
-  if (FAILED(hr)) {
-    CORE_LOG(L3, (_T("[RegKey::GetValue failed][0x%08x][%s][%s]"),
-                  hr, key_name, kRegValueDidRun));
-    return hr;
-  }
-
-  if (did_run_str == _T("1")) {
-    did_run_ |= true;
-  }
-  exists_ |= true;
-
-  return hr;
-}
-
-HRESULT ApplicationUsageData::ProcessBackWardCompatKey(
-    const CString& key_name) {
-  // This method exists to support the installers that have not been
-  // updated to write to the HKCU key. Remove when we have all the installers
-  // correcly updated.
-  if (is_pre_update_check_) {
-    // Read in the regkey value if it exists, and or it with the previous value.
-    RegKey key;
-    HRESULT hr = key.Open(key_name, KEY_READ);
-    if (FAILED(hr)) {
-      CORE_LOG(L4, (_T("[failed to open key][%s][0x%08x]"), key_name, hr));
-      return hr;
-    }
-
-    // Now that we have the key, we should try and read the value of the
-    // did run key.
-    CString did_run_str(_T("0"));
-    hr = key.GetValue(kRegValueDidRun, &did_run_str);
-    if (FAILED(hr)) {
-      CORE_LOG(L3, (_T("[RegKey::GetValue failed][0x%08x][%s][%s]"),
-                    hr, key_name, kRegValueDidRun));
-      return hr;
-    }
-
-    if (did_run_str == _T("1")) {
-      did_run_ |= true;
-    }
-    exists_ |= true;
-
-    return hr;
-  } else {
-    RegKey key;
-    HRESULT hr = key.Open(key_name);
-    if (FAILED(hr)) {
-      CORE_LOG(L4, (_T("[failed to open key][%s][0x%08x]"), key_name, hr));
-      return hr;
-    }
-
-    // If the value exists, then it means that the installer has been updated,
-    // and we delete the machine value.
-    if (exists_) {
-      hr = RegKey::DeleteValue(key_name, kRegValueDidRun);
-      if (FAILED(hr)) {
-        CORE_LOG(LEVEL_WARNING, (_T("[RegKey::DeleteValue failed][0x%08x][%s]"),
-                                 hr, key_name));
-        return hr;
-      }
-    } else {
-      // Since the value does not exist else where, we reset the value in the
-      // HKLM key to zero.
-      exists_ |= true;
-      CString did_run_str(_T("0"));
-      if (SUCCEEDED(key.GetValue(kRegValueDidRun, &did_run_str))) {
-        hr = key.SetValue(kRegValueDidRun, _T("0"));
-        if (FAILED(hr)) {
-          CORE_LOG(LEVEL_WARNING, (_T("[RegKey::SetValue failed][0x%08x][%s]"),
-                                   hr, key_name));
-          return hr;
-        }
-      }
-    }
-  }
-
-  return S_OK;
-}
-
-HRESULT ApplicationUsageData::ProcessPostUpdateCheck(const CString& key_name) {
-  RegKey key;
-  HRESULT hr = key.Open(key_name);
-  if (FAILED(hr)) {
-    CORE_LOG(L4, (_T("[failed to open key][%s][0x%08x]"), key_name, hr));
-    return hr;
-  }
-
-  CString did_run_str(_T("0"));
-  if (SUCCEEDED(key.GetValue(kRegValueDidRun, &did_run_str))) {
-    exists_ |= true;
-    hr = key.SetValue(kRegValueDidRun, _T("0"));
-    if (FAILED(hr)) {
-      CORE_LOG(LEVEL_WARNING, (_T("[RegKey::SetValue failed][0x%08x][%s]"),
-                               hr, key_name));
-      return hr;
-    }
-  }
-  return S_OK;
-}
-
-}  // namespace omaha
diff --git a/worker/application_usage_data.h b/worker/application_usage_data.h
deleted file mode 100644
index d6f5219..0000000
--- a/worker/application_usage_data.h
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// application_usage_data.h : Includes methods to deal with application
-// usage data. Currently it only deals with the did_run key.
-// The class provides methods to process the application data, before and
-// after the update check. In case of the did_run key we read the key
-// pre-update check and clear it post-update check.
-
-#ifndef OMAHA_GOOPDATE_APPLICATION_USAGE_DATA_H__
-#define OMAHA_GOOPDATE_APPLICATION_USAGE_DATA_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include "base/basictypes.h"
-
-namespace omaha {
-
-class ApplicationUsageData {
- public:
-  ApplicationUsageData(bool is_machine, bool check_low_integrity);
-  ~ApplicationUsageData();
-
-  // Reads the did run values for the application indentified by the app_guid.
-  HRESULT ReadDidRun(const CString& app_guid);
-
-  // Clears and performs the post processing after an update ckeck for the
-  // did run key.
-  HRESULT ResetDidRun(const CString& app_guid);
-
-  bool exists() const { return exists_; }
-  bool did_run() const { return did_run_; }
-
- private:
-  // Processes the did run value for the machine goopdate.
-  HRESULT ProcessMachineDidRun(const CString& app_guid);
-
-  // Processes the did run value for the user goopdate.
-  HRESULT ProcessUserDidRun(const CString& app_guid);
-
-  // Calls the pre or the post update check methods based on the
-  // is_pre_update_check_ value.
-  HRESULT ProcessDidRun(const CString& app_guid);
-
-  // Pre or post process the key that is passed in.
-  HRESULT ProcessKey(const CString& key_name);
-
-  // Reads the did run value and populates did_run_ and exists_.
-  HRESULT ProcessPreUpdateCheck(const CString& key_name);
-
-  // Clears the did_run value.
-  HRESULT ProcessPostUpdateCheck(const CString& key_name);
-
-  // Reads and updates the did_run key for the machine. This is a backward
-  // compatibility requirement, since applications have not been updated to
-  // write to HKCU yet.
-  HRESULT ProcessBackWardCompatKey(const CString& key_name);
-
-  bool exists_;                // Whether the did_run value exists.
-  bool did_run_;               // The value of did_run.
-  bool is_machine_;            // Whether this is a machine instance.
-  bool is_pre_update_check_;   // Internal state of pre or post update.
-  bool check_low_integrity_;   // Whether to check the low integrity registry
-                               // location.
-
-  DISALLOW_EVIL_CONSTRUCTORS(ApplicationUsageData);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_APPLICATION_USAGE_DATA_H__
diff --git a/worker/application_usage_data_unittest.cc b/worker/application_usage_data_unittest.cc
deleted file mode 100644
index 26121bd..0000000
--- a/worker/application_usage_data_unittest.cc
+++ /dev/null
@@ -1,593 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// ApplicationUsageData unit tests
-
-#include "omaha/common/reg_key.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/testing/unit_test.h"
-#include "omaha/worker/application_usage_data.h"
-
-namespace omaha {
-
-const TCHAR kAppDidRunValueName[] = _T("dr");
-const TCHAR kHKCUClientStateKeyName[] =
-    _T("HKCU\\Software\\Google\\Update\\ClientState\\")
-    _T("{6ACB7D4D-E5BA-48b0-85FE-A4051500A1BD}");
-const TCHAR kMachineClientState[] =
-    _T("HKLM\\Software\\Google\\Update\\ClientState\\")
-    _T("{6ACB7D4D-E5BA-48b0-85FE-A4051500A1BD}");
-const TCHAR kLowIntegrityIEHKCU[] =
-    _T("HKCU\\Software\\Microsoft\\Internet Explorer\\")
-    _T("InternetRegistry\\REGISTRY\\USER\\");
-const TCHAR kAppGuid[] = _T("{6ACB7D4D-E5BA-48b0-85FE-A4051500A1BD}");
-const TCHAR kRelativeClientState[] =
-    _T("Software\\Google\\Update\\ClientState\\")
-    _T("{6ACB7D4D-E5BA-48b0-85FE-A4051500A1BD}");
-
-// TODO(omaha): Expected and actual are reversed throughout this file. Fix.
-
-class ApplicationUsageDataTest : public testing::Test {
- protected:
-  virtual void SetUp() {
-    CString sid;
-    ASSERT_SUCCEEDED(user_info::GetCurrentUser(NULL, NULL, &sid));
-    low_integrity_key_name_ = AppendRegKeyPath(kLowIntegrityIEHKCU,
-                                               sid,
-                                               kRelativeClientState);
-    TearDown();
-  }
-
-  virtual void TearDown() {
-    RegKey::DeleteKey(kHKCUClientStateKeyName);
-    RegKey::DeleteKey(kMachineClientState);
-    RegKey::DeleteKey(low_integrity_key_name_);
-  }
-
-  void CreateMachineDidRunValue(bool value) {
-    if (!vista_util::IsUserAdmin()) {
-      return;
-    }
-    RegKey key;
-    ASSERT_SUCCEEDED(key.Create(kMachineClientState));
-    ASSERT_SUCCEEDED(key.SetValue(kAppDidRunValueName,
-                                  value == true ? _T("1") : _T("0")));
-  }
-
-  bool MachineDidRunValueExists() {
-    if (!vista_util::IsUserAdmin()) {
-      return true;
-    }
-    RegKey key;
-    if (FAILED(key.Open(kMachineClientState))) {
-      return false;
-    }
-
-    CString did_run_str(_T("0"));
-    if (FAILED(key.GetValue(kAppDidRunValueName, &did_run_str))) {
-      return false;
-    }
-
-    return true;
-  }
-
-  void DeleteMachineDidRunValue() {
-    if (!vista_util::IsUserAdmin()) {
-      return;
-    }
-    ASSERT_SUCCEEDED(RegKey::DeleteValue(kMachineClientState,
-                                         kAppDidRunValueName));
-  }
-
-  void CheckMachineDidRunValue(bool expected) {
-    if (!vista_util::IsUserAdmin()) {
-      return;
-    }
-    RegKey key;
-    ASSERT_SUCCEEDED(key.Open(kMachineClientState));
-
-    CString did_run_str(_T("0"));
-    ASSERT_SUCCEEDED(key.GetValue(kAppDidRunValueName, &did_run_str));
-    bool value = (did_run_str == _T("1")) ? true : false;
-
-    ASSERT_EQ(value, expected);
-  }
-
-  void CreateUserDidRunValue(bool value) {
-    RegKey key;
-    ASSERT_SUCCEEDED(key.Create(kHKCUClientStateKeyName));
-    ASSERT_SUCCEEDED(key.SetValue(kAppDidRunValueName,
-                                  (value == true) ? _T("1") : _T("0")));
-  }
-
-  void DeleteUserDidRunValue() {
-    ASSERT_SUCCEEDED(RegKey::DeleteValue(kHKCUClientStateKeyName,
-                                         kAppDidRunValueName));
-  }
-
-  void CheckUserDidRunValue(bool expected) {
-    RegKey key;
-    ASSERT_SUCCEEDED(key.Open(kHKCUClientStateKeyName));
-
-    CString did_run_str(_T("0"));
-    ASSERT_SUCCEEDED(key.GetValue(kAppDidRunValueName, &did_run_str));
-    bool value = (did_run_str == _T("1")) ? true : false;
-
-    ASSERT_EQ(value, expected);
-  }
-
-  bool UserDidRunValueExists() {
-    RegKey key;
-    if (FAILED(key.Open(kHKCUClientStateKeyName))) {
-      return false;
-    }
-
-    CString did_run_str(_T("0"));
-    if (FAILED(key.GetValue(kAppDidRunValueName, &did_run_str))) {
-      return false;
-    }
-
-    return true;
-  }
-
-  void CreateLowIntegrityUserDidRunValue(bool value) {
-    RegKey key;
-    ASSERT_SUCCEEDED(key.Create(low_integrity_key_name_));
-    ASSERT_SUCCEEDED(key.SetValue(kAppDidRunValueName,
-                                  (value == true) ? _T("1") : _T("0")));
-  }
-
-  void DeleteLowIntegrityUserDidRunValue() {
-    ASSERT_SUCCEEDED(RegKey::DeleteValue(low_integrity_key_name_,
-                                         kAppDidRunValueName));
-  }
-
-  void CheckLowIntegrityUserDidRunValue(bool expected) {
-    RegKey key;
-    ASSERT_SUCCEEDED(key.Open(low_integrity_key_name_));
-
-    CString did_run_str(_T("0"));
-    ASSERT_SUCCEEDED(key.GetValue(kAppDidRunValueName, &did_run_str));
-    bool value = (did_run_str == _T("1")) ? true : false;
-
-    ASSERT_EQ(value, expected);
-  }
-
-  bool LowIntegrityUserDidRunValueExists() {
-    RegKey key;
-    if (FAILED(key.Open(low_integrity_key_name_))) {
-      return false;
-    }
-
-    CString did_run_str(_T("0"));
-    if (FAILED(key.GetValue(kAppDidRunValueName, &did_run_str))) {
-      return false;
-    }
-
-    return true;
-  }
-
-  // This method takes in machine_did_run, user_did_run and
-  // low_user_did_run as int's. The idea is that the test tries to simulate
-  // all of these values as being not-present, and if present then true or
-  // false.
-  // -1 indicates non-presense, 1 indicates true, and 0 false. The caller
-  // then loops over all these values to capture testing all the permutations.
-  void TestUserAndMachineDidRun(int machine_did_run,
-                                int user_did_run,
-                                int low_user_did_run,
-                                bool expected_exists,
-                                bool expected_did_run,
-                                int is_vista) {
-    ApplicationUsageData data(true, is_vista ? true : false);
-
-    // Set up the registry for the test.
-    if (machine_did_run != -1) {
-      CreateMachineDidRunValue((machine_did_run == 1) ? true: false);
-    }
-
-    if (user_did_run != -1) {
-      CreateUserDidRunValue((user_did_run == 1) ? true: false);
-    }
-
-    if (low_user_did_run != -1) {
-      CreateLowIntegrityUserDidRunValue((low_user_did_run == 1) ? true: false);
-    }
-
-    // Perform the test.
-    ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
-    ASSERT_EQ(data.exists(), expected_exists);
-    ASSERT_EQ(data.did_run(), expected_did_run);
-
-    // Check the return values.
-    if (machine_did_run == -1) {
-      ASSERT_FALSE(MachineDidRunValueExists());
-    } else {
-      CheckMachineDidRunValue((machine_did_run == 1) ? true: false);
-    }
-
-    if (user_did_run == -1) {
-      ASSERT_FALSE(UserDidRunValueExists());
-    } else {
-      CheckUserDidRunValue((user_did_run == 1) ? true: false);
-    }
-
-    if (low_user_did_run == -1) {
-      ASSERT_FALSE(LowIntegrityUserDidRunValueExists());
-    } else {
-      CheckLowIntegrityUserDidRunValue((low_user_did_run == 1) ? true: false);
-    }
-  }
-
-  void TestUserAndMachineDidRunPostProcess(int machine_did_run,
-                                           int user_did_run,
-                                           int low_user_did_run,
-                                           bool expected_exists,
-                                           int is_vista) {
-    ApplicationUsageData data(true, is_vista ? true : false);
-
-    // Setup the registry for the test.
-    if (machine_did_run != -1) {
-      CreateMachineDidRunValue((machine_did_run == 1) ? true: false);
-    }
-
-    if (user_did_run != -1) {
-      CreateUserDidRunValue((user_did_run == 1) ? true: false);
-    }
-
-    if (low_user_did_run != -1) {
-      CreateLowIntegrityUserDidRunValue((low_user_did_run == 1) ? true: false);
-    }
-
-    // Run the test.
-    ASSERT_SUCCEEDED(data.ResetDidRun(kAppGuid));
-    if (user_did_run == -1) {
-      ASSERT_FALSE(UserDidRunValueExists());
-    } else {
-      CheckUserDidRunValue(false);
-    }
-
-    if (low_user_did_run == -1) {
-      ASSERT_FALSE(LowIntegrityUserDidRunValueExists());
-    } else {
-      if (is_vista) {
-        CheckLowIntegrityUserDidRunValue(false);
-      } else {
-        CheckLowIntegrityUserDidRunValue((low_user_did_run == 1) ? true: false);
-      }
-    }
-
-    if (machine_did_run == -1) {
-      ASSERT_FALSE(MachineDidRunValueExists());
-    } else {
-      if (user_did_run != -1 ||  (is_vista && low_user_did_run != -1)) {
-        // This means that the user keys exists for this application
-        // we should have delete the machine key.
-        ASSERT_EQ(MachineDidRunValueExists(), false);
-      } else {
-        CheckMachineDidRunValue(false);
-      }
-    }
-
-    ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
-    ASSERT_EQ(data.exists(), expected_exists);
-    ASSERT_EQ(data.did_run(), false);
-  }
-
-  void UserTestDidRunPreProcess(int user_did_run,
-                                int low_user_did_run,
-                                int is_vista,
-                                bool expected_exists,
-                                bool expected_did_run) {
-    ApplicationUsageData data(false, is_vista ? true : false);
-
-    // Set up the registry for the test.
-    CreateMachineDidRunValue(true);
-
-    if (user_did_run != -1) {
-      CreateUserDidRunValue((user_did_run == 1) ? true: false);
-    }
-
-    if (low_user_did_run != -1) {
-      CreateLowIntegrityUserDidRunValue((low_user_did_run == 1) ? true: false);
-    }
-
-    // Perform the test.
-    ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
-    ASSERT_EQ(data.exists(), expected_exists);
-    ASSERT_EQ(data.did_run(), expected_did_run);
-
-    // The machine value should not have changed from what we set it to.
-    CheckMachineDidRunValue(true);
-    if (user_did_run == -1) {
-      // If we did not create the user value it should not exist.
-      ASSERT_FALSE(UserDidRunValueExists());
-    }
-
-    if (low_user_did_run == -1) {
-      // If we did not create the low integrity user value it should not exist.
-      ASSERT_FALSE(LowIntegrityUserDidRunValueExists());
-    }
-  }
-
-  void UserTestDidRunPostProcess(int user_did_run,
-                                 int low_user_did_run,
-                                 int is_vista) {
-    // Create a user ApplicationUsageData class.
-    ApplicationUsageData data(false, is_vista ? true : false);
-
-    // This should not affect the test.
-    CreateMachineDidRunValue(true);
-
-    if (user_did_run != -1) {
-      CreateUserDidRunValue((user_did_run == 1) ? true: false);
-    }
-
-    if (low_user_did_run != -1) {
-      CreateLowIntegrityUserDidRunValue((low_user_did_run == 1) ? true: false);
-    }
-
-    ASSERT_SUCCEEDED(data.ResetDidRun(kAppGuid));
-
-    // The machine did run shold never get affected.
-    CheckMachineDidRunValue(true);
-    if (user_did_run == -1) {
-      ASSERT_FALSE(UserDidRunValueExists());
-    } else {
-      // In all cases if the HKCU did run is set, it should get cleared.
-      CheckUserDidRunValue(false);
-    }
-
-    if (low_user_did_run == -1) {
-      ASSERT_FALSE(LowIntegrityUserDidRunValueExists());
-    } else {
-      // In case of vista, the low integrity user value should get reset.
-      CheckLowIntegrityUserDidRunValue(is_vista ? false :
-                                       (low_user_did_run == 1) ? true : false);
-    }
-  }
-
- private:
-  CString low_integrity_key_name_;
-};
-
-TEST_F(ApplicationUsageDataTest, ReadDidRunUser1) {
-  ApplicationUsageData data(true, false);
-
-  ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
-  ASSERT_EQ(data.exists(), false);
-  ASSERT_EQ(data.did_run(), false);
-
-  // Test with false user value.
-  CreateUserDidRunValue(false);
-  ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
-  ASSERT_EQ(data.exists(), true);
-  ASSERT_EQ(data.did_run(), false);
-}
-
-TEST_F(ApplicationUsageDataTest, ReadDidRunUser2) {
-  // Test with true user value.
-  ApplicationUsageData data1(true, false);
-  CreateUserDidRunValue(true);
-  ASSERT_SUCCEEDED(data1.ReadDidRun(kAppGuid));
-  ASSERT_EQ(data1.exists(), true);
-  ASSERT_EQ(data1.did_run(), true);
-}
-
-TEST_F(ApplicationUsageDataTest, ReadDidRunUser3) {
-  // low integrity user = false, vista
-  ApplicationUsageData data2(true, true);
-  CreateLowIntegrityUserDidRunValue(false);
-  ASSERT_SUCCEEDED(data2.ReadDidRun(kAppGuid));
-  ASSERT_EQ(data2.exists(), true);
-  ASSERT_EQ(data2.did_run(), false);
-}
-
-TEST_F(ApplicationUsageDataTest, ReadDidRunUser4) {
-  // low integrity user = true, vista
-  ApplicationUsageData data2(true, true);
-  CreateLowIntegrityUserDidRunValue(true);
-  ASSERT_SUCCEEDED(data2.ReadDidRun(kAppGuid));
-  ASSERT_EQ(data2.exists(), true);
-  ASSERT_EQ(data2.did_run(), true);
-}
-
-TEST_F(ApplicationUsageDataTest, ReadDidRunUser5) {
-  // low integrity user = true, not vista
-  ApplicationUsageData data2(true, false);
-  CreateLowIntegrityUserDidRunValue(true);
-  ASSERT_SUCCEEDED(data2.ReadDidRun(kAppGuid));
-  ASSERT_EQ(data2.exists(), false);
-  ASSERT_EQ(data2.did_run(), false);
-}
-
-TEST_F(ApplicationUsageDataTest, ReadDidRunMachine1) {
-  if (!vista_util::IsUserAdmin()) {
-    return;
-  }
-
-  ApplicationUsageData data(true, true);
-
-  // create machine application key and test
-  CreateMachineDidRunValue(false);
-  ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
-  ASSERT_EQ(data.exists(), true);
-  ASSERT_EQ(data.did_run(), false);
-}
-
-TEST_F(ApplicationUsageDataTest, ReadDidRunMachine2) {
-  if (!vista_util::IsUserAdmin()) {
-    return;
-  }
-
-  ApplicationUsageData data1(true, true);
-  CreateMachineDidRunValue(true);
-  ASSERT_SUCCEEDED(data1.ReadDidRun(kAppGuid));
-  ASSERT_EQ(data1.exists(), true);
-  ASSERT_EQ(data1.did_run(), true);
-}
-
-TEST_F(ApplicationUsageDataTest, ReadDidRunBoth1) {
-  if (!vista_util::IsUserAdmin()) {
-    return;
-  }
-
-  // We try all combinations of machine, user and low integrity user
-  // registry value for did run. -1 indicates the value does not exist
-  // 1 indicates true and 0 indicates false.
-  for (int vista = 0; vista < 2; ++vista) {
-    for (int machine = -1; machine < 2; ++machine) {
-      for (int user = -1; user < 2; ++user) {
-        for (int lowuser = -1; lowuser < 2; ++lowuser) {
-          bool expected_did_run = false;
-          bool expected_exists = false;
-
-          if (machine > -1 || user > -1 || (vista && lowuser > -1)) {
-            expected_exists = true;
-          }
-
-          if (machine > 0 || user > 0 || (vista && lowuser > 0)) {
-            expected_did_run = true;
-          }
-
-          TestUserAndMachineDidRun(machine, user, lowuser,
-                                   expected_exists,
-                                   expected_did_run,
-                                   vista);
-          TearDown();
-        }
-      }
-    }
-  }
-}
-
-TEST_F(ApplicationUsageDataTest, ResetDidRunUser1) {
-  ApplicationUsageData data(true, true);
-
-  // create user application key and test
-  CreateUserDidRunValue(false);
-  ASSERT_SUCCEEDED(data.ResetDidRun(kAppGuid));
-  CheckUserDidRunValue(false);
-
-  ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
-  ASSERT_EQ(data.exists(), true);
-  ASSERT_EQ(data.did_run(), false);
-}
-
-TEST_F(ApplicationUsageDataTest, ResetDidRunUser2) {
-  ApplicationUsageData data1(true, true);
-  CreateUserDidRunValue(true);
-  ASSERT_SUCCEEDED(data1.ResetDidRun(kAppGuid));
-  CheckUserDidRunValue(false);
-
-  ASSERT_SUCCEEDED(data1.ReadDidRun(kAppGuid));
-  ASSERT_EQ(data1.exists(), true);
-  ASSERT_EQ(data1.did_run(), false);
-}
-
-TEST_F(ApplicationUsageDataTest, ResetDidRunMachine1) {
-  if (!vista_util::IsUserAdmin()) {
-    return;
-  }
-
-  ApplicationUsageData data(true, true);
-  CreateMachineDidRunValue(false);
-  ASSERT_SUCCEEDED(data.ResetDidRun(kAppGuid));
-  CheckMachineDidRunValue(false);
-
-  ASSERT_SUCCEEDED(data.ReadDidRun(kAppGuid));
-  ASSERT_EQ(data.exists(), true);
-  ASSERT_EQ(data.did_run(), false);
-}
-
-TEST_F(ApplicationUsageDataTest, ResetDidRunMachine2) {
-  if (!vista_util::IsUserAdmin()) {
-    return;
-  }
-
-  ApplicationUsageData data1(true, true);
-  CreateMachineDidRunValue(true);
-  ASSERT_SUCCEEDED(data1.ResetDidRun(kAppGuid));
-  CheckMachineDidRunValue(false);
-
-  ASSERT_SUCCEEDED(data1.ReadDidRun(kAppGuid));
-  ASSERT_EQ(data1.exists(), true);
-  ASSERT_EQ(data1.did_run(), false);
-}
-
-TEST_F(ApplicationUsageDataTest, ResetDidRunBoth) {
-  if (!vista_util::IsUserAdmin()) {
-    return;
-  }
-
-  // We try all combinations of machine, user and low integrity user
-  // registry value for did run. -1 indicates the value does not exist
-  // 1 indicates true and 0 indicates false.
-  for (int vista = 0; vista < 2; ++vista) {
-    for (int machine = -1; machine < 2; ++machine) {
-      for (int user = -1; user < 2; ++user) {
-        for (int lowuser = -1; lowuser < 2; ++lowuser) {
-          bool expected_exists = false;
-          if (machine > -1 || user > -1 || (vista && lowuser > -1)) {
-            expected_exists = true;
-          }
-
-          TestUserAndMachineDidRunPostProcess(machine, user, lowuser,
-                                              expected_exists,
-                                              vista);
-          TearDown();
-        }
-      }
-    }
-  }
-}
-
-TEST_F(ApplicationUsageDataTest, UserReadDidRunUser) {
-  for (int vista = 0; vista < 2; ++vista) {
-      for (int user = -1; user < 2; ++user) {
-        for (int lowuser = -1; lowuser < 2; ++lowuser) {
-          bool expected_exists = false;
-          bool expected_did_run = false;
-
-          if (user != -1 || (vista && lowuser != -1)) {
-            expected_exists = true;
-          }
-
-          if (user > 0 || (vista && lowuser > 0)) {
-            expected_did_run = true;
-          }
-
-          UserTestDidRunPreProcess(user, lowuser, vista, expected_exists,
-                                   expected_did_run);
-          TearDown();
-        }
-      }
-  }
-}
-
-TEST_F(ApplicationUsageDataTest, UserResetDidRunUser1) {
-  for (int vista = 0; vista < 2; ++vista) {
-    for (int user = -1; user < 2; ++user) {
-      for (int lowuser = -1; lowuser < 2; ++lowuser) {
-        UserTestDidRunPostProcess(user, lowuser, vista);
-        TearDown();
-      }
-    }
-  }
-}
-
-}  // namespace omaha
diff --git a/worker/build.scons b/worker/build.scons
deleted file mode 100644
index 1aadf94..0000000
--- a/worker/build.scons
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright 2008-2009 Google Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-# ========================================================================
-
-Import('env')
-
-inputs = [
-    'application_manager.cc',
-    'application_usage_data.cc',
-    'com_wrapper_shutdown_handler.cc',
-    'download_manager.cc',
-    'install_manager.cc',
-    'job.cc',
-    'job_creator.cc',
-    'job_observer.cc',
-    'ping.cc',
-    'ping_utils.cc',
-    'ui.cc',
-    'worker.cc',
-    'worker_com_wrapper.cc',
-    'worker_event_logger.cc',
-    'worker_job.cc',
-    'worker_job_strategy.cc',
-    'worker_metrics.cc',
-
-    'uilib/node_state.cc',
-    'uilib/static_ex.cc',
-    'uilib/static_line.cc',
-    ]
-
-
-local_env = env.Clone()
-
-# Need to look in output dir to find .h files generated by midl compiler.
-# This also allows Hammer to understand dependencies between this subdir
-# and the .idl files in the goopdate folder.
-local_env['CPPPATH'] += ['$OBJ_ROOT']
-
-# Build these into a library.
-local_env.ComponentLibrary('worker', inputs)
diff --git a/worker/com_wrapper_shutdown_handler.cc b/worker/com_wrapper_shutdown_handler.cc
deleted file mode 100644
index 1825e6c..0000000
--- a/worker/com_wrapper_shutdown_handler.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// The WorkerComWrapperShutdownCallBack implements the shutdown
-// handler for OnDemandUpdates. The class posts a WM_QUIT message
-// to the main thread. In case of the OnDemandUpdates this
-// main thread loop is running a message loop provided by
-// CAtlExeModuleT::RunMessageLoop().
-
-#include "omaha/worker/com_wrapper_shutdown_handler.h"
-
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/goopdate/google_update.h"
-
-namespace omaha {
-
-int WorkerComWrapperShutdownCallBack::ReleaseIgnoreShutdown() {
-  int release_count = InternalRelease();
-  if (!release_count && shutdown_on_final_release_) {
-    VERIFY1(SUCCEEDED(Shutdown()));
-  }
-
-  return release_count;
-}
-
-HRESULT WorkerComWrapperShutdownCallBack::Shutdown() {
-  if (ShouldIgnoreShutdown()) {
-    return S_OK;
-  }
-
-  GoogleUpdate* google_update = static_cast<GoogleUpdate*>(_pAtlModule);
-  ASSERT1(google_update);
-  if (!::PostThreadMessage(google_update->m_dwMainThreadID, WM_QUIT, 0, 0)) {
-    HRESULT hr = HRESULTFromLastError();
-    CORE_LOG(LE, (_T("[PostThreadMessage failed][0x%08x]"), hr));
-    return hr;
-  }
-  return S_OK;
-}
-
-}  // namespace omaha
-
diff --git a/worker/com_wrapper_shutdown_handler.h b/worker/com_wrapper_shutdown_handler.h
deleted file mode 100644
index cc93db1..0000000
--- a/worker/com_wrapper_shutdown_handler.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Contains WorkerComWrapperShutdownCallBack which implements the
-// shutdown handling code for the OnDemandUpdate COM Server.
-
-#ifndef OMAHA_GOOPDATE_COM_WRAPPER_SHUTDOWN_HANDLER_H__
-#define OMAHA_GOOPDATE_COM_WRAPPER_SHUTDOWN_HANDLER_H__
-
-#include <windows.h>
-#include <atlbase.h>
-#include <atlcom.h>
-#include "omaha/common/debug.h"
-#include "omaha/common/shutdown_callback.h"
-#include "omaha/common/synchronized.h"
-
-namespace omaha {
-
-class WorkerComWrapperShutdownCallBack
-    : public ShutdownCallback,
-      public CComObjectRootEx<CComMultiThreadModel> {
- public:
-  WorkerComWrapperShutdownCallBack(bool shutdown_on_final_release)
-      : shutdown_on_final_release_(shutdown_on_final_release) {}
-  virtual ~WorkerComWrapperShutdownCallBack() {}
-  virtual HRESULT Shutdown();
-
-  // Clears the ignore shutdown flag.
-  int ReleaseIgnoreShutdown();
-
-  // Atomically sets the ignore shutdown flag and returns its previous value.
-  int AddRefIgnoreShutdown() {
-    return InternalAddRef();
-  }
-
-  // Returns the value of the ignore shutdown flag.
-  bool ShouldIgnoreShutdown() {
-    InternalAddRef();
-    return !!InternalRelease();
-  }
-
- private:
-  bool shutdown_on_final_release_;
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_GOOPDATE_COM_WRAPPER_SHUTDOWN_HANDLER_H__
-
diff --git a/worker/download_manager.cc b/worker/download_manager.cc
deleted file mode 100644
index 668db87..0000000
--- a/worker/download_manager.cc
+++ /dev/null
@@ -1,448 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// The download manager uses the network request to download the remote file.
-// When running as local system, the network request impersonates one of the
-// logged on users. To save the file, the network request needs write access to
-// a directory, both when running impersonated and not.
-// The directory is obtained by calling SHGetFolderLocation with
-// CSIDL_COMMON_APPDATA. In order to ensure the directory is accessible
-// even in cases when impersonatation is used, BuildUniqueDownloadFilePath
-// impersonates before calling SHGetFolderLocation.
-//
-// Once the download is complete, The download manager copies the file to either
-// the machine secure location or the user secure location and then
-// it validates the hash.
-
-#include "omaha/worker/download_manager.h"
-
-#include <vector>
-#include <algorithm>
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/path.h"
-#include "omaha/common/scoped_impersonation.h"
-#include "omaha/common/string.h"
-#include "omaha/common/user_rights.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/resource.h"
-#include "omaha/net/bits_request.h"
-#include "omaha/net/browser_request.h"
-#include "omaha/net/http_client.h"
-#include "omaha/net/network_request.h"
-#include "omaha/net/net_utils.h"
-#include "omaha/net/simple_request.h"
-#include "omaha/worker/job.h"
-#include "omaha/worker/worker_metrics.h"
-
-namespace omaha {
-
-namespace {
-
-// Creates and initializes an instance of the NetworkRequest for the
-// Downloadmanager to use. Defines a fallback chain: BITS, WinHttp, browser.
-NetworkRequest* CreateNetworkRequest(bool is_logged_on) {
-  const NetworkConfig::Session& session(NetworkConfig::Instance().session());
-  NetworkRequest* network_request(new NetworkRequest(session));
-
-  // TODO(omaha): provide a mechanism for different timeout values in
-  // silent and interactive downloads.
-
-  // TODO(omaha): background downloads are not supported yet.
-
-  // BITS transfers files only when the job owner is logged on. If the
-  // process "Run As" another user, an empty BITS job gets created in suspended
-  // state but there is no way to manipulate the job, nor cancel it.
-  if (is_logged_on) {
-    BitsRequest* bits_request(new BitsRequest);
-    bits_request->set_minimum_retry_delay(60);
-    bits_request->set_no_progress_timeout(15);
-    network_request->AddHttpRequest(bits_request);
-  }
-
-  network_request->AddHttpRequest(new SimpleRequest);
-  network_request->AddHttpRequest(new BrowserRequest);
-
-  network_request->set_num_retries(1);
-  return network_request;
-}
-
-}  // namespace
-
-DownloadManager::DownloadManager(bool is_machine)
-    : job_(NULL),
-      is_machine_(is_machine),
-      impersonation_token_(NULL),
-      is_logged_on_(false) {
-  HRESULT hr = IsUserLoggedOn(&is_logged_on_);
-
-  // Assumes the caller is not logged on if the function failed.
-  ASSERT1(SUCCEEDED(hr) || !is_logged_on_);
-
-  http_client_.reset(CreateHttpClient());
-  ASSERT1(http_client_.get());
-}
-
-void DownloadManager::SetErrorInfo(HRESULT hr) {
-  ASSERT1(job_);
-  CString msg;
-  // TODO(omaha):  job_->app_data().display_name() may not be correct
-  // for bundles.
-  if (!goopdate_utils::FormatMessageForNetworkError(
-          hr,
-          job_->app_data().display_name(),
-          &msg)) {
-    msg.FormatMessage(IDS_DOWNLOAD_ERROR, hr);
-  }
-  error_info_ = CompletionInfo(COMPLETION_ERROR, hr, msg);
-}
-
-HRESULT DownloadManager::DownloadFile(Job* job) {
-  ASSERT1(job);
-  ASSERT1(job_ == NULL);
-
-  ASSERT1(ConfigManager::Instance()->CanUseNetwork(is_machine_));
-
-  ++metric_worker_download_total;
-
-  job_ = job;
-  HRESULT hr = DownloadCurrentJob();
-  if (SUCCEEDED(hr)) {
-    ++metric_worker_download_succeeded;
-  }
-  job_ = NULL;
-  return hr;
-}
-
-HRESULT DownloadManager::DownloadCurrentJob() {
-  ASSERT1(job_);
-
-  HRESULT hr = BuildUniqueDownloadFilePath(&local_download_file_path_);
-  if (FAILED(hr)) {
-    CORE_LOG(LW,
-        (_T("[BuildUniqueDownloadFilePath failed][0x%08x]"), hr));
-    SetErrorInfo(hr);
-    return hr;
-  }
-
-  network_request_.reset(CreateNetworkRequest(is_logged_on_));
-  network_request_->set_low_priority(job_->is_background());
-
-  CString path = is_machine_ ?
-      ConfigManager::Instance()->GetMachineSecureDownloadStorageDir() :
-      ConfigManager::Instance()->GetUserDownloadStorageDir();
-
-  if (IsCached(path)) {
-    OPT_LOG(L1, (_T("[Using cached version of the download file %s]"),
-                 local_download_file_path_));
-    return S_OK;
-  }
-
-  network_request_->set_callback(job_);
-  OPT_LOG(L1, (_T("[Starting file download from %s to %s]"),
-               job_->response_data().url(),
-               local_download_file_path_));
-  hr = network_request_->DownloadFile(job_->response_data().url(),
-                                      local_download_file_path_);
-  if (FAILED(hr)) {
-    goopdate_utils::AddNetworkRequestDataToEventLog(network_request_.get(), hr);
-    SetErrorInfo(hr);
-  }
-  VERIFY1(SUCCEEDED(network_request_->Close()));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = MoveFile();
-  if (FAILED(hr)) {
-    SetErrorInfo(hr);
-    return hr;
-  }
-
-  hr = ValidateDownloadedFile(local_download_file_path_);
-  if (FAILED(hr)) {
-    CString msg;
-    msg.FormatMessage(IDS_DOWNLOAD_HASH_MISMATCH, hr);
-    error_info_ = CompletionInfo(COMPLETION_ERROR, hr, msg);
-    LogValidationFailure();
-    return hr;
-  }
-
-  return S_OK;
-}
-
-void DownloadManager::LogValidationFailure() const {
-  const int kDownloadFileBytesToLog = 256;
-
-  bool exists = File::Exists(local_download_file_path_);
-  uint32 file_size(0);
-  std::vector<char> download_file_bytes(kDownloadFileBytesToLog + 1);
-  if (exists) {
-    if (FAILED(File::GetFileSizeUnopen(local_download_file_path_,
-                                       &file_size))) {
-      return;
-    }
-
-    File downloaded_file;
-    if (SUCCEEDED(downloaded_file.Open(local_download_file_path_,
-                                       false, false))) {
-      uint32 bytes_read = 0;
-      if (SUCCEEDED(downloaded_file.ReadFromStartOfFile(
-              kDownloadFileBytesToLog,
-              reinterpret_cast<unsigned char*>(&download_file_bytes.front()),
-              &bytes_read))) {
-        download_file_bytes.resize(bytes_read);
-        std::replace_if(download_file_bytes.begin(), download_file_bytes.end(),
-                        std::not1(std::ptr_fun(isprint)), '.');
-        download_file_bytes.push_back('\0');
-      }
-    }
-  }
-
-  REPORT_LOG(L1, (_T("[DownloadValidationFail filename=%s exists=%d size=%d ")
-                  _T("expected size=%d expected hash=%s filebytes=%hS]"),
-                  local_download_file_path_,
-                  exists,
-                  file_size,
-                  job_->response_data().size(),
-                  job_->response_data().hash(),
-                  &download_file_bytes.front()));
-}
-
-HRESULT DownloadManager::Cancel() {
-  return network_request_.get() ? network_request_->Cancel() : S_OK;
-}
-
-// The installer is initially downloaded to a temporary unique name.
-// Once the download succeeds the file is copied to
-// DownloadDir\<Guid>\<name> where
-// DownloadDir = User or machine download dir returned by ConfigManager.
-// guid        = Guid used for temp name.
-// name        = Name specified in the update response.
-// The reason to copy the file using a sub-directory structure is to account
-// for the case where the same file is downloaded by multiple processes or
-// threads.
-HRESULT DownloadManager::BuildUniqueDownloadFilePath(CString* file) const {
-  ASSERT1(file);
-
-  // Impersonate the user if a valid impersonation token is presented.
-  // Continue unimpersonated if the impersonation fails. We do the
-  // impersonation here to get the correct download folder for
-  // impersonated clients. (For more information refer to the comment
-  // at the top of the file.)
-  scoped_impersonation impersonate_user(impersonation_token_);
-  if (impersonation_token_) {
-    DWORD result = impersonate_user.result();
-    ASSERT(result == ERROR_SUCCESS, (_T("impersonation failed %d"), result));
-  }
-
-  GUID guid(GUID_NULL);
-  HRESULT hr = ::CoCreateGuid(&guid);
-  if (FAILED(hr)) {
-    CORE_LOG(L3, (_T("[CoCreateGuid failed 0x%08x]"), hr));
-    return hr;
-  }
-
-  CString path(ConfigManager::Instance()->GetTempDownloadDir());
-  *file = ConcatenatePath(path, GuidToString(guid));
-  if (file->IsEmpty()) {
-    ASSERT1(false);
-    return GOOPDATEDOWNLOAD_E_UNIQUE_FILE_PATH_EMPTY;
-  }
-  return S_OK;
-}
-
-HRESULT DownloadManager::GetFileNameFromDownloadUrl(const CString& url,
-                                                    CString* file_name) const {
-  CORE_LOG(L3, (_T("[DownloadManager::GetFileNameFromDownloadUrl]")));
-  ASSERT1(job_);
-  ASSERT1(http_client_.get());
-  ASSERT1(file_name);
-
-  CString url_path;
-  int port = 0;
-  CString extra_info;
-  HRESULT hr = http_client_->CrackUrl(url, 0, NULL, NULL, &port,
-                                      &url_path, &extra_info);
-  if (FAILED(hr)) {
-    OPT_LOG(LE, (_T("[CrackUrl failed 0x%08x]"), hr));
-    return GOOPDATEDOWNLOAD_E_CRACKURL_FAILED;
-  }
-
-  int start_file_name_idx = url_path.ReverseFind(_T('/'));
-  if (start_file_name_idx == -1) {
-    CORE_LOG(LW, (_T("[No filename found in download url.]")));
-    return GOOPDATEDOWNLOAD_E_INVALID_PATH;
-  }
-  ASSERT1(url_path.GetLength() >= start_file_name_idx - 1);
-  CString dst_file_name =
-      url_path.Right(url_path.GetLength() - start_file_name_idx - 1);
-  if (dst_file_name.IsEmpty()) {
-    OPT_LOG(LE, (_T("[Empty filename in download url]")));
-    return GOOPDATEDOWNLOAD_E_FILE_NAME_EMPTY;
-  }
-  ASSERT1(!dst_file_name.IsEmpty());
-  *file_name = dst_file_name;
-
-  return S_OK;
-}
-
-bool DownloadManager::IsCached(const CString& store) {
-  OPT_LOG(L3, (_T("[DownloadManager::IsCached]")));
-  ASSERT1(job_);
-
-  if (!job_->is_background()) {
-    return false;
-  }
-
-  CString dst_file_name;
-  HRESULT hr = GetFileNameFromDownloadUrl(job_->response_data().url(),
-                                          &dst_file_name);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[GetFileNameFromDownloadUrl failed][0x%08x]"), hr));
-    return false;
-  }
-  ASSERT1(!dst_file_name.IsEmpty());
-
-  std::vector<CString> files;
-  hr = FindFileRecursive(store, dst_file_name, &files);
-  if (FAILED(hr)) {
-    CORE_LOG(L3, (_T("[FindFileRecursive failed][0x%08x]"), hr));
-    return false;
-  }
-
-  for (size_t i = 0; i < files.size(); ++i) {
-    ASSERT1(File::Exists(files[i]));
-    if (SUCCEEDED(ValidateDownloadedFile(files[i]))) {
-      OPT_LOG(L2, (_T("[Found cached file %s.]"), files[i]));
-      local_download_file_path_ = files[i];
-      job_->set_download_file_name(local_download_file_path_);
-      return true;
-    } else {
-      OPT_LOG(L2, (_T("[Found cached file %s validation failed.]"), files[i]));
-    }
-  }
-
-  return false;
-}
-
-HRESULT DownloadManager::ValidateDownloadedFile(
-    const CString& file_name) const {
-  return goopdate_utils::ValidateDownloadedFile(file_name,
-      job_->response_data().hash(),
-      static_cast<uint32>(job_->response_data().size()));
-}
-
-HRESULT DownloadManager::BuildDestinationDirectory(CString* dest_path) const {
-  ASSERT1(dest_path);
-  dest_path->Empty();
-
-  const CString path = is_machine_ ?
-      ConfigManager::Instance()->GetMachineSecureDownloadStorageDir() :
-      ConfigManager::Instance()->GetUserDownloadStorageDir();
-
-  CORE_LOG(L3, (_T("[Download Storage Dir][%s]"), path));
-
-  if (!File::Exists(path)) {
-    return GOOPDATEDOWNLOAD_E_STORAGE_DIR_NOT_EXIST;
-  }
-
-  GUID guid(GUID_NULL);
-  HRESULT hr = ::CoCreateGuid(&guid);
-  if (FAILED(hr)) {
-    OPT_LOG(LW, (_T("[CoCreateGuid failed 0x%08x]"), hr));
-    return hr;
-  }
-
-  CString destination_path = ConcatenatePath(path, GuidToString(guid));
-  if (destination_path.IsEmpty()) {
-    ASSERT1(false);
-    return GOOPDATEDOWNLOAD_E_DEST_PATH_EMPTY;
-  }
-
-  hr = CreateDir(destination_path, NULL);
-  if (FAILED(hr)) {
-    // Since the directory creation failed, we will fall back to the destination
-    // directory returned by the ConfigManager.
-    OPT_LOG(LW, (_T("[CreateDir '%s' failed][0x%08x]"), destination_path, hr));
-    destination_path = path;
-  }
-
-  OPT_LOG(L1, (_T("[The destination directory is '%s']"), destination_path));
-  *dest_path = destination_path;
-
-  return S_OK;
-}
-
-HRESULT DownloadManager::MoveFile() {
-  ++metric_worker_download_move_total;
-
-  CString dest_path;
-  HRESULT hr = BuildDestinationDirectory(&dest_path);
-  if (FAILED(hr)) {
-    OPT_LOG(LW, (_T("[Build destination directory failed][0x%08x]"), hr));
-    return hr;
-  }
-  CORE_LOG(L3, (_T("[Download Directory][%s]"), dest_path));
-  ASSERT1(!dest_path.IsEmpty());
-  ASSERT1(File::Exists(dest_path));
-
-  CString dst_file_name;
-  hr = GetFileNameFromDownloadUrl(job_->response_data().url(),
-                                  &dst_file_name);
-  if (FAILED(hr)) {
-    CORE_LOG(LW, (_T("[GetFileNameFromDownloadUrl failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  if (dst_file_name.IsEmpty()) {
-    ASSERT1(false);
-    return GOOPDATEDOWNLOAD_E_DEST_FILENAME_EMPTY;
-  }
-  CORE_LOG(L3, (_T("[Destination filename][%s]"), dst_file_name));
-
-  CString dest_file_path = ConcatenatePath(dest_path, dst_file_name);
-  if (dest_file_path.IsEmpty()) {
-    ASSERT1(false);
-    return GOOPDATEDOWNLOAD_E_DEST_FILE_PATH_EMPTY;
-  }
-
-  OPT_LOG(L1, (_T("[Moving download file from %s to %s]"),
-               local_download_file_path_, dest_file_path));
-  // Uses ::CopyFile. ::CopyFile, done without impersonation, will reset the
-  // ownership of the destination file, and make sure that it inherits ACEs from
-  // the new parent directory.
-  hr = File::Copy(local_download_file_path_, dest_file_path, true);
-  if (FAILED(hr)) {
-    OPT_LOG(LE, (_T("[Could not copy '%s' to '%s'][0x%08x]"),
-                 local_download_file_path_, dest_file_path, hr));
-    job_->set_extra_code1(hr);
-    return GOOPDATEDOWNLOAD_E_FAILED_MOVE;
-  }
-  VERIFY1(SUCCEEDED(File::Remove(local_download_file_path_)));
-
-  local_download_file_path_ = dest_file_path;
-  job_->set_download_file_name(dest_file_path);
-
-  ++metric_worker_download_move_succeeded;
-  return S_OK;
-}
-
-}  // namespace omaha
diff --git a/worker/download_manager.h b/worker/download_manager.h
deleted file mode 100644
index c50e0b8..0000000
--- a/worker/download_manager.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// Download manager supports downloading one file at a time.
-// The DownloadFile is a blocking call. All errors are reported through the
-// return value of this method. Progress is reported on the
-// NetworkRequestCallback callback that the job object implements.
-
-#ifndef OMAHA_WORKER_DOWNLOAD_MANAGER_H__
-#define OMAHA_WORKER_DOWNLOAD_MANAGER_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include "omaha/net/http_client.h"
-#include "base/scoped_ptr.h"
-#include "omaha/worker/job.h"
-
-namespace omaha {
-
-class Job;
-class NetworkRequest;
-
-class DownloadManager {
- public:
-  explicit DownloadManager(bool is_machine);
-  HRESULT DownloadFile(Job* job);
-  HRESULT Cancel();
-  CompletionInfo error_info() const { return error_info_; }
-
-  HANDLE impersonation_token() const { return impersonation_token_; }
-  void set_impersonation_token(HANDLE token) { impersonation_token_ = token; }
- private:
-  HRESULT GetFileNameFromDownloadUrl(const CString& url,
-                                     CString* file_name) const;
-  HRESULT BuildDestinationDirectory(CString* dest_path) const;
-  HRESULT BuildUniqueDownloadFilePath(CString* file) const;
-  HRESULT DownloadCurrentJob();
-  bool IsCached(const CString& store);
-  HRESULT ValidateDownloadedFile(const CString& file_name) const;
-  HRESULT MoveFile();
-  void SetErrorInfo(HRESULT hr);
-  void LogValidationFailure() const;
-
-  CString local_download_file_path_;
-  CompletionInfo error_info_;
-  Job* job_;
-  bool is_machine_;
-  scoped_ptr<NetworkRequest> network_request_;
-  HANDLE impersonation_token_;    // Token to impersonate with, if available.
-
-  // True if the code runs in the current interactive session. BITS only allows
-  // jobs to run when the owner is logged in. Local System is always logged on.
-  bool is_logged_on_;
-  scoped_ptr<HttpClient> http_client_;
-
-  friend class DownloadManagerTest;
-  DISALLOW_EVIL_CONSTRUCTORS(DownloadManager);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_DOWNLOAD_MANAGER_H__
diff --git a/worker/download_manager_unittest.cc b/worker/download_manager_unittest.cc
deleted file mode 100644
index 32eefd0..0000000
--- a/worker/download_manager_unittest.cc
+++ /dev/null
@@ -1,508 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include <windows.h>
-#include <atlstr.h>
-#include "omaha/common/app_util.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/path.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/common/scoped_ptr_cotask.h"
-#include "omaha/common/system.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/testing/unit_test.h"
-#include "omaha/worker/download_manager.h"
-#include "omaha/worker/job.h"
-#include "omaha/worker/ping.h"
-
-namespace omaha {
-
-namespace {
-
-// Hash = Igq6bYaeXFJCjH770knXyJ6V53s=, size = 479848
-const TCHAR kTestExeHash[] = _T("Igq6bYaeXFJCjH770knXyJ6V53s=");
-const int kTestExeSize = 479848;
-const TCHAR kWrongTestExeHash[] = _T("F006bYaeXFJCjH770knXyJ6V53s=");
-// Hash = ImV9skETZqGFMjs32vbZTvzAYJU=, size = 870400
-const TCHAR kTestMsiHash[] = _T("ImV9skETZqGFMjs32vbZTvzAYJU=");
-const int kTestMsiSize = 870400;
-
-}  // namespace
-
-class DownloadManagerTest : public testing::Test {
- protected:
-  static void SetUpTestCase() {
-    TCHAR module_path[MAX_PATH] = {0};
-    ASSERT_NE(0, ::GetModuleFileName(NULL, module_path, MAX_PATH));
-    CString path = GetDirectoryFromPath(module_path);
-
-    cache_test_dir_ =
-        ConcatenatePath(path, _T("unittest_support\\download_cache_test"));
-
-    CString expected_path_exe = (_T("{7101D597-3481-4971-AD23-455542964072}")
-                                 _T("\\livelysetup.exe"));
-    expected_file_name_exe_ =
-        ConcatenatePath(cache_test_dir_, expected_path_exe);
-
-    CString expected_path_msi = (_T("{89640431-FE64-4da8-9860-1A1085A60E13}")
-                                 _T("\\gears-win32-opt.msi"));
-    expected_file_name_msi_ =
-        ConcatenatePath(cache_test_dir_, expected_path_msi);
-  }
-
-  virtual void SetUp() {
-    job_.reset(CreateJob());
-  }
-
-  void Initialize(bool is_machine) {
-    download_manager_.reset(new DownloadManager(is_machine));
-  }
-
-  virtual void TearDown() {
-    download_manager_.reset();
-    job_.reset();
-  }
-
-  HRESULT BuildDestinationDirectory(CString* dir) {
-    return download_manager_->BuildDestinationDirectory(dir);
-  }
-
-  HRESULT BuildUniqueDownloadFilePath(CString* file) {
-    return download_manager_->BuildUniqueDownloadFilePath(file);
-  }
-
-  void SetJob(Job* job) {
-    download_manager_->job_ = job;
-  }
-
-  bool IsCached(const CString& path) {
-    return download_manager_->IsCached(path);
-  }
-
-  HRESULT GetFileNameFromDownloadUrl(const CString& url,
-                                     CString* out) {
-    return download_manager_->GetFileNameFromDownloadUrl(url, out);
-  }
-
-  HRESULT ValidateDownloadedFile(const CString& file_name) const {
-    return download_manager_->ValidateDownloadedFile(file_name);
-  }
-
-  void SetErrorInfo(HRESULT hr) {
-    download_manager_->SetErrorInfo(hr);
-  }
-
-  Job* CreateJob() {
-    Initialize(false);
-    UpdateResponseData response_data;
-    response_data.set_url(_T("http://dl.google.com/update2/UpdateData.bin"));
-    response_data.set_hash(_T("YF2z/br/S6E3KTca0MT7qziJN44="));
-    scoped_ptr<Job> job(new Job(true, &ping_));
-    job->set_is_background(true);
-    job->set_update_response_data(response_data);
-    return job.release();
-  }
-
-  Job* CreateJob(const UpdateResponseData& data) {
-    scoped_ptr<Job> job(new Job(true, &ping_));
-    job->set_is_background(true);
-    job->set_update_response_data(data);
-    return job.release();
-  }
-
-  void SetLocalDownloadFilepath(const CString download_file) {
-    download_manager_->local_download_file_path_ = download_file;
-  }
-
-  void VerifyCompletionInfo(JobCompletionStatus status,
-                            DWORD error_code,
-                            const CString& text) {
-    EXPECT_EQ(status, download_manager_->error_info().status);
-    EXPECT_EQ(error_code, download_manager_->error_info().error_code);
-    EXPECT_STREQ(text, download_manager_->error_info().text);
-  }
-
-  scoped_ptr<Job> job_;
-  scoped_ptr<DownloadManager> download_manager_;
-  Ping ping_;
-
-  static CString cache_test_dir_;
-  static CString expected_file_name_exe_;
-  static CString expected_file_name_msi_;
-};
-
-CString DownloadManagerTest::cache_test_dir_;
-CString DownloadManagerTest::expected_file_name_exe_;
-CString DownloadManagerTest::expected_file_name_msi_;
-
-// Download a file via an http: URL.
-TEST_F(DownloadManagerTest, DownloadViaHttp) {
-  Initialize(false);
-  scoped_ptr<Job> job1(CreateJob());
-  EXPECT_HRESULT_SUCCEEDED(job1->Download(download_manager_.get()));
-  EXPECT_TRUE(::DeleteFile(job1->download_file_name()));
-
-  scoped_ptr<Job> job2(CreateJob());
-  EXPECT_HRESULT_SUCCEEDED(job2->Download(download_manager_.get()));
-  EXPECT_TRUE(::DeleteFile(job2->download_file_name()));
-}
-
-TEST_F(DownloadManagerTest, BuildDestinationDirectory_User) {
-  Initialize(false);
-  CString path = ConfigManager::Instance()->GetUserDownloadStorageDir();
-
-  CString dir;
-  EXPECT_HRESULT_SUCCEEDED(BuildDestinationDirectory(&dir));
-
-  CString common_prefix;
-  int common_path_len = ::PathCommonPrefix(dir,
-                                           path,
-                                           CStrBuf(common_prefix, MAX_PATH));
-  EXPECT_EQ(path.GetLength(), common_path_len);
-  EXPECT_STREQ(common_prefix, path);
-
-  CString dir2;
-  EXPECT_HRESULT_SUCCEEDED(BuildDestinationDirectory(&dir2));
-  CString common_prefix2;
-  common_path_len = ::PathCommonPrefix(dir2,
-                                       path,
-                                       CStrBuf(common_prefix2, MAX_PATH));
-  EXPECT_EQ(path.GetLength(), common_path_len);
-  EXPECT_STREQ(common_prefix2, path);
-
-  EXPECT_STRNE(dir, dir2);
-}
-
-TEST_F(DownloadManagerTest, BuildUniqueDownloadFilePath_User) {
-  Initialize(false);
-
-  CString file;
-  EXPECT_HRESULT_SUCCEEDED(BuildUniqueDownloadFilePath(&file));
-  CString str_guid = GetFileFromPath(file);
-  EXPECT_TRUE(!str_guid.IsEmpty());
-  GUID guid = StringToGuid(str_guid);
-  EXPECT_TRUE(!::IsEqualGUID(guid, GUID_NULL));
-}
-
-TEST_F(DownloadManagerTest, BuildDestinationDirectory_Machine) {
-  if (!vista_util::IsUserAdmin()) {
-    return;
-  }
-
-  Initialize(true);
-  CString path =
-      ConfigManager::Instance()->GetMachineSecureDownloadStorageDir();
-
-  CString dir;
-  EXPECT_HRESULT_SUCCEEDED(BuildDestinationDirectory(&dir));
-
-  CString common_prefix;
-  int common_path_len = ::PathCommonPrefix(dir,
-                                           path,
-                                           CStrBuf(common_prefix, MAX_PATH));
-  EXPECT_EQ(path.GetLength(), common_path_len);
-  EXPECT_STREQ(common_prefix, path);
-
-  CString dir2;
-  EXPECT_HRESULT_SUCCEEDED(BuildDestinationDirectory(&dir2));
-  CString common_prefix2;
-  common_path_len = ::PathCommonPrefix(dir2,
-                                       path,
-                                       CStrBuf(common_prefix2, MAX_PATH));
-  EXPECT_EQ(path.GetLength(), common_path_len);
-  EXPECT_STREQ(common_prefix2, path);
-
-  EXPECT_STRNE(dir, dir2);
-}
-
-TEST_F(DownloadManagerTest, BuildUniqueDownloadFilePath_Machine) {
-  if (!vista_util::IsUserAdmin()) {
-    return;
-  }
-
-  Initialize(true);
-  CString file;
-  EXPECT_HRESULT_SUCCEEDED(BuildUniqueDownloadFilePath(&file));
-  CString str_guid = GetFileFromPath(file);
-  EXPECT_TRUE(!str_guid.IsEmpty());
-  GUID guid = StringToGuid(str_guid);
-  EXPECT_TRUE(!::IsEqualGUID(guid, GUID_NULL));
-}
-
-TEST_F(DownloadManagerTest, IsCached_TestExe) {
-  UpdateResponseData response_data;
-  response_data.set_url(_T("http://dl.google.com/livelysetup.exe"));
-  response_data.set_hash(kTestExeHash);
-  response_data.set_size(kTestExeSize);
-  scoped_ptr<Job> job(CreateJob(response_data));
-  SetJob(job.get());
-
-  EXPECT_TRUE(IsCached(cache_test_dir_));
-  EXPECT_STREQ(expected_file_name_exe_, job->download_file_name());
-}
-
-TEST_F(DownloadManagerTest, IsCached_TestMSI) {
-  UpdateResponseData response_data;
-  response_data.set_url(_T("http://dl.google.com/gears-win32-opt.msi"));
-  response_data.set_hash(kTestMsiHash);
-  response_data.set_size(kTestMsiSize);
-  scoped_ptr<Job> job(CreateJob(response_data));
-  SetJob(job.get());
-
-  EXPECT_TRUE(IsCached(cache_test_dir_));
-  EXPECT_STREQ(expected_file_name_msi_, job->download_file_name());
-}
-
-TEST_F(DownloadManagerTest, IsCached_FileNotPresent) {
-  UpdateResponseData response_data;
-  response_data.set_url(_T("dl.google.com/not_present.msi"));
-  response_data.set_hash(kTestMsiHash);
-  response_data.set_size(kTestMsiSize);
-  scoped_ptr<Job> job(CreateJob(response_data));
-  SetJob(job.get());
-
-  EXPECT_FALSE(IsCached(cache_test_dir_));
-  EXPECT_TRUE(job->download_file_name().IsEmpty());
-}
-
-TEST_F(DownloadManagerTest, IsCached_InvalidHash) {
-  UpdateResponseData response_data;
-  response_data.set_url(_T("http://dl.google.com/gears-win32-opt.msi"));
-  response_data.set_hash(_T("BAADBAADBAADMjs32vbZTvzAYJU="));
-  response_data.set_size(kTestMsiSize);
-  scoped_ptr<Job> job(CreateJob(response_data));
-  SetJob(job.get());
-
-  EXPECT_FALSE(IsCached(cache_test_dir_));
-  EXPECT_TRUE(job->download_file_name().IsEmpty());
-}
-
-TEST_F(DownloadManagerTest, IsCached_NotUpdateJob) {
-  scoped_ptr<Job> job(new Job(false, &ping_));
-  UpdateResponseData response_data;
-  response_data.set_url(_T("dl.google.com/livelysetup.exe"));
-  response_data.set_hash(kTestExeHash);
-  response_data.set_size(kTestExeSize);
-  job->set_update_response_data(response_data);
-  SetJob(job.get());
-
-  EXPECT_FALSE(IsCached(cache_test_dir_));
-  EXPECT_TRUE(job->download_file_name().IsEmpty());
-}
-
-TEST_F(DownloadManagerTest, ValidateDownloadedFile_Valid) {
-  UpdateResponseData response_data;
-  response_data.set_url(_T("dl.google.com/livelysetup.exe"));
-  response_data.set_hash(kTestExeHash);
-  response_data.set_size(kTestExeSize);
-  scoped_ptr<Job> job(CreateJob(response_data));
-  SetJob(job.get());
-  SetLocalDownloadFilepath(expected_file_name_exe_);
-
-  EXPECT_SUCCEEDED(ValidateDownloadedFile(expected_file_name_exe_));
-}
-
-TEST_F(DownloadManagerTest, ValidateDownloadedFile_HashFailsCorrectSize) {
-  UpdateResponseData response_data;
-  response_data.set_url(_T("dl.google.com/livelysetup.exe"));
-  response_data.set_hash(kWrongTestExeHash);
-  response_data.set_size(kTestExeSize);
-  scoped_ptr<Job> job(CreateJob(response_data));
-  SetJob(job.get());
-  SetLocalDownloadFilepath(expected_file_name_exe_);
-
-  EXPECT_EQ(SIGS_E_INVALID_SIGNATURE,
-            ValidateDownloadedFile(expected_file_name_exe_));
-}
-
-TEST_F(DownloadManagerTest, ValidateDownloadedFile_ValidHashSizeIncorrect) {
-  UpdateResponseData response_data;
-  response_data.set_url(_T("http://dl.google.com/livelysetup.exe"));
-  response_data.set_hash(kTestExeHash);
-  response_data.set_size(kTestExeSize + 10);
-  scoped_ptr<Job> job(CreateJob(response_data));
-  SetJob(job.get());
-
-  EXPECT_SUCCEEDED(ValidateDownloadedFile(expected_file_name_exe_));
-}
-
-TEST_F(DownloadManagerTest, ValidateDownloadedFile_HashFailsSizeZero) {
-  const TCHAR kEmtpyFileName[] = _T("emptyfile.txt");
-
-  UpdateResponseData response_data;
-  response_data.set_url(_T("http://dl.google.com/livelysetup.exe"));
-  response_data.set_hash(kWrongTestExeHash);
-  response_data.set_size(kTestExeSize);
-  scoped_ptr<Job> job(CreateJob(response_data));
-  SetJob(job.get());
-
-  EXPECT_SUCCEEDED(File::Remove(kEmtpyFileName));
-  File empty_file;
-  EXPECT_SUCCEEDED(empty_file.Open(kEmtpyFileName, true, false));
-  EXPECT_SUCCEEDED(empty_file.Close());
-
-  EXPECT_EQ(GOOPDATEDOWNLOAD_E_FILE_SIZE_ZERO,
-            ValidateDownloadedFile(kEmtpyFileName));
-
-  EXPECT_SUCCEEDED(File::Remove(kEmtpyFileName));
-}
-
-TEST_F(DownloadManagerTest, ValidateDownloadedFile_HashFailsSizeSmaller) {
-  UpdateResponseData response_data;
-  response_data.set_url(_T("http://dl.google.com/livelysetup.exe"));
-  response_data.set_hash(kWrongTestExeHash);
-  response_data.set_size(kTestExeSize + 1);
-  scoped_ptr<Job> job(CreateJob(response_data));
-  SetJob(job.get());
-
-  EXPECT_EQ(GOOPDATEDOWNLOAD_E_FILE_SIZE_SMALLER,
-            ValidateDownloadedFile(expected_file_name_exe_));
-}
-
-TEST_F(DownloadManagerTest, ValidateDownloadedFile_HashFailsSizeLarger) {
-  UpdateResponseData response_data;
-  response_data.set_url(_T("dl.google.com/livelysetup.exe"));
-  response_data.set_hash(kWrongTestExeHash);
-  response_data.set_size(kTestExeSize - 1);
-  scoped_ptr<Job> job(CreateJob(response_data));
-  SetJob(job.get());
-
-  EXPECT_EQ(GOOPDATEDOWNLOAD_E_FILE_SIZE_LARGER,
-            ValidateDownloadedFile(expected_file_name_exe_));
-}
-
-TEST_F(DownloadManagerTest, ValidateDownloadedFile_FileDoesNotExist) {
-  UpdateResponseData response_data;
-  response_data.set_url(_T("http://dl.google.com/livelysetup.exe"));
-  response_data.set_hash(kWrongTestExeHash);
-  response_data.set_size(kTestExeSize);
-  scoped_ptr<Job> job(CreateJob(response_data));
-  SetJob(job.get());
-
-  ExpectAsserts expect_asserts;
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            ValidateDownloadedFile(_T("nosuchfile.txt")));
-}
-
-TEST_F(DownloadManagerTest, GetFileNameFromDownloadUrl_QueryParams) {
-  SetJob(CreateJob());
-
-  CString url = _T("http://foo10.bar.google.com:26190/update2/test/machinefoo/test.msi?tttoken=1.DQAAA3_cykoeVgF7b8ZT1Ycsv_tfjbAofLplQp3xHrwOOJGZkb1ZakYVTIN0QMJvA5IlMzULa0AEP-JWYZVVKz-gQTD35u30pRAirXjKjsEC5KHKQKqBipa00ni8krbzYawfTKQKAAmlTan_eLHDOnH7NiHcrCLkLSE_5Un1S7p5_DegLgFGfUXffwHS6S-Z5LHCHdqUXCW&test=10&hello"); // NOLINT
-  CString file_name;
-  ASSERT_HRESULT_SUCCEEDED(GetFileNameFromDownloadUrl(url, &file_name));
-  ASSERT_STREQ(_T("test.msi"), file_name);
-}
-
-TEST_F(DownloadManagerTest, GetFileNameFromDownloadUrl_NoQueryParams) {
-  SetJob(CreateJob());
-
-  CString url = _T("http://foo10.google.com:26190/test/machinefoo/test.msi");
-  CString file_name;
-  ASSERT_HRESULT_SUCCEEDED(GetFileNameFromDownloadUrl(url, &file_name));
-  ASSERT_STREQ(_T("test.msi"), file_name);
-
-  url = _T("http://dl.google.com/update2/1.2.121.9/GoogleUpdateSetup.exe");
-  ASSERT_HRESULT_SUCCEEDED(GetFileNameFromDownloadUrl(url, &file_name));
-  ASSERT_STREQ(_T("GoogleUpdateSetup.exe"), file_name);
-
-  url = _T("http://dl.google.com/foo/plugin/4.3.9543.7852/foo-plugin-win.exe");
-  ASSERT_HRESULT_SUCCEEDED(GetFileNameFromDownloadUrl(url, &file_name));
-  ASSERT_STREQ(_T("foo-plugin-win.exe"), file_name);
-}
-
-TEST_F(DownloadManagerTest, GetFileNameFromDownloadUrl_WithOutPath) {
-  SetJob(CreateJob());
-
-  CString url = _T("http://foo10.bar.google.com:26190/test.exe");
-  CString file_name;
-  ASSERT_HRESULT_SUCCEEDED(GetFileNameFromDownloadUrl(url, &file_name));
-  ASSERT_STREQ(_T("test.exe"), file_name);
-}
-
-TEST_F(DownloadManagerTest, GetFileNameFromDownloadUrl_InvalidPaths) {
-  SetJob(CreateJob());
-
-  CString url = _T("http://foo10.bar.google.com:26190/test/");
-  CString file_name;
-  EXPECT_HRESULT_FAILED(GetFileNameFromDownloadUrl(url, &file_name));
-
-  url = _T("http://foo10.bar.google.com:26190/test/?");
-  EXPECT_EQ(GOOPDATEDOWNLOAD_E_FILE_NAME_EMPTY,
-            GetFileNameFromDownloadUrl(url, &file_name));
-
-  url = _T("foo10.bar.google.com:26190test");
-  EXPECT_HRESULT_FAILED(GetFileNameFromDownloadUrl(url, &file_name));
-}
-
-
-TEST_F(DownloadManagerTest, SetErrorInfo) {
-  AppData app_data;
-  app_data.set_display_name(_T("Test App"));
-  Job job(false, &ping_);
-  job.set_app_data(app_data);
-  SetJob(&job);
-
-  SetErrorInfo(GOOPDATE_E_NO_NETWORK);
-  VerifyCompletionInfo(
-      COMPLETION_ERROR,
-      static_cast<DWORD>(GOOPDATE_E_NO_NETWORK),
-      _T("Installation failed. Ensure that your computer is connected to the ")
-      _T("Internet and that your firewall allows GoogleUpdate.exe to connect ")
-      _T("and then try again. Error code = 0x80040801."));
-
-  SetErrorInfo(GOOPDATE_E_NETWORK_UNAUTHORIZED);
-  VerifyCompletionInfo(
-      COMPLETION_ERROR,
-      static_cast<DWORD>(GOOPDATE_E_NETWORK_UNAUTHORIZED),
-      _T("The Test App installer could not connect to the Internet because of ")
-      _T("an HTTP 401 Unauthorized response. This is likely a proxy ")
-      _T("configuration issue.  Please configure the proxy server to allow ")
-      _T("network access and try again or contact your network administrator. ")
-      _T("Error code = 0x80042191"));
-
-  SetErrorInfo(GOOPDATE_E_NETWORK_FORBIDDEN);
-  VerifyCompletionInfo(
-      COMPLETION_ERROR,
-      static_cast<DWORD>(GOOPDATE_E_NETWORK_FORBIDDEN),
-      _T("The Test App installer could not connect to the Internet because of ")
-      _T("an HTTP 403 Forbidden response. This is likely a proxy ")
-      _T("configuration issue.  Please configure the proxy server to allow ")
-      _T("network access and try again or contact your network administrator. ")
-      _T("Error code = 0x80042193"));
-
-  SetErrorInfo(GOOPDATE_E_NETWORK_PROXYAUTHREQUIRED);
-  VerifyCompletionInfo(
-      COMPLETION_ERROR,
-      static_cast<DWORD>(GOOPDATE_E_NETWORK_PROXYAUTHREQUIRED),
-      _T("The Test App installer could not connect to the Internet because a ")
-      _T("proxy server required user authentication. Please configure the ")
-      _T("proxy server to allow network access and try again or contact your ")
-      _T("network administrator. Error code = 0x80042197"));
-
-  SetErrorInfo(E_FAIL);
-  VerifyCompletionInfo(
-      COMPLETION_ERROR,
-      static_cast<DWORD>(E_FAIL),
-      _T("Installer download failed. Error code = 0x80004005"));
-}
-
-}  // namespace omaha
-
diff --git a/worker/i_job_observer_mock.h b/worker/i_job_observer_mock.h
deleted file mode 100644
index 0c9a180..0000000
--- a/worker/i_job_observer_mock.h
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#ifndef OMAHA_WORKER_I_JOB_OBSERVER_MOCK_H_
-#define OMAHA_WORKER_I_JOB_OBSERVER_MOCK_H_
-
-#pragma once
-#include <windows.h>
-#include <atlbase.h>
-#include <atlcom.h>
-#include "goopdate\google_update_idl.h"  // NOLINT
-#include "omaha/worker/job_observer_mock.h"
-
-namespace omaha {
-
-// A basic implementation of the IJobObserver interface for use by unit tests.
-// Wraps JobObserverMock.
-class IJobObserverMock
-  : public CComObjectRootEx<CComSingleThreadModel>,
-    public IJobObserver {
- public:
-  BEGIN_COM_MAP(IJobObserverMock)
-    COM_INTERFACE_ENTRY(IJobObserver)
-  END_COM_MAP()
-
-  virtual ~IJobObserverMock() {
-  }
-
-  // IJobObserver implementation.
-  STDMETHOD(OnShow)() {
-    job_observer_mock.OnShow();
-    return S_OK;
-  }
-
-  STDMETHOD(OnCheckingForUpdate)() {
-    job_observer_mock.OnCheckingForUpdate();
-    return S_OK;
-  }
-
-  STDMETHOD(OnUpdateAvailable)(const TCHAR* version_string) {
-    UNREFERENCED_PARAMETER(version_string);
-    return S_OK;
-  }
-
-  STDMETHOD(OnWaitingToDownload)() {
-    job_observer_mock.OnWaitingToDownload();
-    return S_OK;
-  }
-
-  STDMETHOD(OnDownloading)(int time_remaining_ms, int pos) {
-    job_observer_mock.OnDownloading(time_remaining_ms, pos);
-    return S_OK;
-  }
-
-  STDMETHOD(OnWaitingToInstall)() {
-    job_observer_mock.OnWaitingToInstall();
-    return S_OK;
-  }
-
-  STDMETHOD(OnInstalling)() {
-    job_observer_mock.OnInstalling();
-    return S_OK;
-  }
-
-  STDMETHOD(OnPause)() {
-    job_observer_mock.OnPause();
-    return S_OK;
-  }
-
-  // The COM API does not have an error_code parameter. Pass unexpected error.
-  STDMETHOD(OnComplete)(CompletionCodes code, const TCHAR* text) {
-    job_observer_mock.OnComplete(code, text, static_cast<DWORD>(E_UNEXPECTED));
-    ::PostThreadMessage(::GetCurrentThreadId(), WM_QUIT, 0, 0);
-    return S_OK;
-  }
-
-  // JobObserverMock uses a different sink type, so cannot pass event_sink.
-  // For now, there are not tests that need the sink, so do nothing.
-  STDMETHOD(SetEventSink)(IProgressWndEvents* event_sink) {
-    UNREFERENCED_PARAMETER(event_sink);
-    return S_OK;
-  }
-
-  // Stores data about events that tests can check.
-  JobObserverMock job_observer_mock;
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_I_JOB_OBSERVER_MOCK_H_
diff --git a/worker/install_manager.cc b/worker/install_manager.cc
deleted file mode 100644
index 18e1268..0000000
--- a/worker/install_manager.cc
+++ /dev/null
@@ -1,843 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/worker/install_manager.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/path.h"
-#include "omaha/common/process.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/common/system_info.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/resource.h"
-#include "omaha/worker/job.h"
-#include "omaha/worker/worker_metrics.h"
-
-namespace omaha {
-
-bool GetMessageForSystemErrorCode(DWORD system_error_code, CString* message);
-
-namespace {
-
-// This is the base retry delay between retries when msiexec returns
-// ERROR_INSTALL_ALREADY_RUNNING. We exponentially backoff from this value.
-// Note that there is an additional delay for the MSI call, so the tries may
-// be a few seconds further apart.
-const int kMsiAlreadyRunningRetryDelayBaseMs = 5000;
-// Number of retries. Updates are silent so we can wait longer.
-const int kNumMsiAlreadyRunningInteractiveMaxTries = 4;  // Up to 35 seconds.
-const int kNumMsiAlreadyRunningSilentMaxTries      = 7;  // Up to 6.25 minutes.
-
-// Interval to wait for installer completion.
-const int kInstallManagerCompleteIntervalMs = 15 * 60 * 1000;
-
-// Gets the installer exit code.
-HRESULT GetInstallerExitCode(const Process& p, uint32* exit_code) {
-  ASSERT1(exit_code);
-
-  if (p.Running()) {
-    ASSERT(false,
-           (_T("GetInstallerExitCode called while the process is running.")));
-    return GOOPDATEINSTALL_E_INSTALLER_INTERNAL_ERROR;
-  }
-
-  if (!p.GetExitCode(exit_code)) {
-    ASSERT(false,
-           (_T("[Failed to get the installer exit code for some reason.]")));
-    return GOOPDATEINSTALL_E_INSTALLER_INTERNAL_ERROR;
-  }
-
-  CORE_LOG(L2, (_T("[Installer exit code][%u]"), *exit_code));
-
-  return S_OK;
-}
-
-// Obtains the localized text for Omaha errors that may occur in the
-// DoInstallation path.
-void GetOmahaErrorTextToReport(HRESULT omaha_hr,
-                               const CString& installer_filename,
-                               const CString& app_name,
-                               CString* error_text) {
-  ASSERT1(error_text);
-
-  switch (omaha_hr) {
-    case GOOPDATEINSTALL_E_FILENAME_INVALID:
-      error_text->FormatMessage(IDS_INVALID_INSTALLER_FILENAME,
-                                installer_filename);
-      break;
-    case GOOPDATEINSTALL_E_INSTALLER_FAILED_START:
-      error_text->FormatMessage(IDS_INSTALLER_FAILED_TO_START);
-      break;
-    case GOOPDATEINSTALL_E_INSTALLER_TIMED_OUT:
-      error_text->FormatMessage(IDS_INSTALLER_TIMED_OUT, app_name);
-      break;
-    case GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENT_KEY:
-    case GOOPDATEINSTALL_E_INSTALLER_DID_NOT_CHANGE_VERSION:
-    case GOOPDATE_E_INVALID_INSTALL_DATA_INDEX:
-    case GOOPDATE_E_INVALID_INSTALLER_DATA_IN_APPARGS:
-      error_text->FormatMessage(IDS_INSTALL_FAILED, omaha_hr);
-      break;
-    case GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING:
-      error_text->FormatMessage(IDS_MSI_INSTALL_ALREADY_RUNNING, app_name);
-      break;
-    case GOOPDATEINSTALL_E_INSTALLER_FAILED:
-      ASSERT(false,
-             (_T("[GetOmahaErrorTextToReport]")
-              _T("GOOPDATEINSTALL_E_INSTALLER_FAILED should never be reported ")
-              _T("directly. The installer error string should be reported.")));
-      error_text->FormatMessage(IDS_INSTALL_FAILED, omaha_hr);
-      break;
-    case GOOPDATEINSTALL_E_INSTALLER_INTERNAL_ERROR:
-    default:
-      ASSERT(false, (_T("[GetOmahaErrorTextToReport]")
-                     _T("[An Omaha error occurred that this method does not ")
-                     _T("know how to report.][0x%08x]"), omaha_hr));
-
-      error_text->FormatMessage(IDS_INSTALL_FAILED, omaha_hr);
-      break;
-  }
-
-  ASSERT1(!error_text->IsEmpty());
-}
-
-// Gets the errors string for the specified system error.
-// Assumes error_code represents a system error.
-void GetSystemErrorString(uint32 error_code, CString* error_string) {
-  ASSERT1(error_string);
-  ASSERT1(ERROR_SUCCESS != error_code);
-
-  const CString error_code_string = FormatErrorCode(error_code);
-
-  CString error_message;
-  if (GetMessageForSystemErrorCode(error_code, &error_message)) {
-    ASSERT(!error_message.IsEmpty(),
-           (_T("[GetMessageForSystemErrorCode succeeded ")
-            _T("but the error message is empty.]")));
-
-    error_string->FormatMessage(IDS_INSTALLER_FAILED_WITH_MESSAGE,
-                                error_code_string,
-                                error_message);
-  } else {
-    error_string->FormatMessage(IDS_INSTALLER_FAILED_NO_MESSAGE,
-                                error_code_string);
-  }
-
-  OPT_LOG(LEVEL_ERROR, (_T("[installer system error][%u][%s]"),
-                        error_code, *error_string));
-  ASSERT1(!error_string->IsEmpty());
-}
-
-}  // namespace
-
-// Obtains the message for a System Error Code in the user's language.
-// Returns whether the message was successfully obtained.
-// It does not support "insert sequences". The sequence will be returned in the
-// string.
-bool GetMessageForSystemErrorCode(DWORD system_error_code, CString* message) {
-  CORE_LOG(L3, (_T("[GetMessageForSystemErrorCode][%u]"), system_error_code));
-
-  ASSERT1(message);
-
-  message->Empty();
-
-  TCHAR* system_allocated_buffer = NULL;
-  const DWORD kFormatOptions = FORMAT_MESSAGE_ALLOCATE_BUFFER |
-                               FORMAT_MESSAGE_FROM_SYSTEM |
-                               FORMAT_MESSAGE_IGNORE_INSERTS |
-                               FORMAT_MESSAGE_MAX_WIDTH_MASK;
-  DWORD tchars_written = ::FormatMessage(
-      kFormatOptions,
-      NULL,
-      system_error_code,
-      0,
-      reinterpret_cast<LPWSTR>(&system_allocated_buffer),
-      0,
-      NULL);
-
-  if (0 < tchars_written) {
-    ASSERT1(system_allocated_buffer);
-    *message = system_allocated_buffer;
-
-    VERIFY1(!::LocalFree(system_allocated_buffer));
-    return true;
-  } else {
-    DWORD format_message_error = ::GetLastError();
-    ASSERT(false, (_T("[::FormatMessage failed][%u]"), format_message_error));
-    ASSERT1(!system_allocated_buffer);
-
-    return false;
-  }
-}
-
-InstallManager::InstallManager(bool is_machine)
-    : is_machine_(is_machine) {
-}
-
-HRESULT InstallManager::InstallJob(Job* job) {
-  ASSERT1(job);
-  job_ = job;
-
-  NamedObjectAttributes lock_attr;
-  GetNamedObjectAttributes(kInstallManagerSerializer, is_machine_, &lock_attr);
-  if (!installer_lock_.InitializeWithSecAttr(lock_attr.name, &lock_attr.sa)) {
-    OPT_LOG(LEVEL_ERROR, (_T("[Could not init install manager lock]")));
-    HRESULT hr = GOOPDATEINSTALL_E_CANNOT_GET_INSTALLER_LOCK;
-    error_info_.status = COMPLETION_ERROR;
-    error_info_.error_code = hr;
-    error_info_.text.FormatMessage(IDS_INSTALL_FAILED, hr);
-    return hr;
-  }
-
-  HRESULT hr = InstallDownloadedFile();
-
-  // error_info_ should have the default values unless the installer failed in
-  // which case it is already popluated correctly.
-  ASSERT1(IsCompletionSuccess(error_info_) ||
-          (GOOPDATEINSTALL_E_INSTALLER_FAILED == hr &&
-           IsCompletionInstallerError(error_info_)) ||
-          (GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING == hr &&
-           (COMPLETION_INSTALLER_ERROR_MSI == error_info_.status ||
-            COMPLETION_INSTALLER_ERROR_SYSTEM == error_info_.status)));
-  ASSERT1(!error_info_.text.IsEmpty() ==
-          (GOOPDATEINSTALL_E_INSTALLER_FAILED == hr ||
-          GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING == hr));
-
-  if (SUCCEEDED(hr)) {
-    // Omaha and the installer succeeded.
-    error_info_.text.FormatMessage(IDS_APPLICATION_INSTALLED_SUCCESSFULLY,
-                                   job_->app_data().display_name());
-    return S_OK;
-  } else {
-    CORE_LOG(LEVEL_ERROR, (_T("[InstallDownloadedFile failed][0x%08x]"), hr));
-
-    if (GOOPDATEINSTALL_E_INSTALLER_FAILED != hr) {
-      // Omaha failed.
-      error_info_.status = COMPLETION_ERROR;
-      error_info_.error_code = hr;
-      GetOmahaErrorTextToReport(hr,
-                                job_->download_file_name(),
-                                job_->app_data().display_name(),
-                                &error_info_.text);
-    }
-
-    return hr;
-  }
-}
-
-HRESULT InstallManager::BuildCommandLineFromFilename(
-    const CString& filename,
-    const CString& arguments,
-    const CString& installer_data,
-    CString* executable_name,
-    CString* command_line,
-    InstallerType* installer_type) {
-  CORE_LOG(L3, (_T("[BuildCommandLineFromFilename]")));
-
-  ASSERT1(executable_name);
-  ASSERT1(command_line);
-  ASSERT1(installer_type);
-
-  *executable_name = _T("");
-  *command_line = _T("");
-  *installer_type = UNKNOWN_INSTALLER;
-
-  // The app's installer owns the lifetime of installer data file if it has been
-  // created, so Omaha does not delete it.
-  CString enclosed_installer_data_file_path;
-
-  VERIFY1(SUCCEEDED(goopdate_utils::WriteInstallerDataToTempFile(
-      installer_data,
-      &enclosed_installer_data_file_path)));
-  if (!enclosed_installer_data_file_path.IsEmpty()) {
-    EnclosePath(&enclosed_installer_data_file_path);
-  }
-
-  // PathFindExtension returns the address of the trailing NUL character if an
-  // extension is not found. It does not return NULL.
-  const TCHAR* ext = ::PathFindExtension(filename);
-  ASSERT1(ext);
-  if (*ext != _T('\0')) {
-    ext++;  // Skip the period.
-    if (0 == lstrcmpi(ext, _T("exe"))) {
-      *executable_name = filename;
-      if (enclosed_installer_data_file_path.IsEmpty()) {
-        *command_line = arguments;
-      } else {
-        command_line->Format(_T("%s /installerdata=%s"),
-                             arguments,
-                             enclosed_installer_data_file_path);
-      }
-      *installer_type = CUSTOM_INSTALLER;
-
-      CORE_LOG(L2, (_T("[BuildCommandLineFromFilename][exe][%s][%s]"),
-                    *executable_name, *command_line));
-    } else if (0 == lstrcmpi(ext, _T("msi"))) {
-      *executable_name = _T("msiexec");
-      *command_line = BuildMsiCommandLine(arguments,
-                                          filename,
-                                          enclosed_installer_data_file_path);
-      *installer_type = MSI_INSTALLER;
-
-      CORE_LOG(L2, (_T("[BuildCommandLineFromFilename][msi][%s]"),
-                    *command_line));
-    } else {
-      *executable_name = _T("");
-      *command_line = _T("");
-      *installer_type = UNKNOWN_INSTALLER;
-
-      OPT_LOG(LEVEL_ERROR, (_T("[Unsupported extension '%s' in %s]"),
-                            ext, filename));
-      return GOOPDATEINSTALL_E_FILENAME_INVALID;
-    }
-  } else {
-    OPT_LOG(LEVEL_ERROR, (_T("[No extension found in %s]"), filename));
-    return GOOPDATEINSTALL_E_FILENAME_INVALID;
-  }
-
-  return S_OK;
-}
-
-// Calls DoExecuteAndWaitForInstaller to do the work. If an MSI installer
-// returns, ERROR_INSTALL_ALREADY_RUNNING waits and retries several times or
-// until the installation succeeds.
-HRESULT InstallManager::ExecuteAndWaitForInstaller(
-    const CString& executable_name,
-    const CString& command_line,
-    const CString& app_guid,
-    InstallerType installer_type) {
-  CORE_LOG(L3, (_T("[InstallManager::ExecuteAndWaitForInstaller]")));
-
-  ++metric_worker_install_execute_total;
-  if (MSI_INSTALLER == installer_type) {
-    ++metric_worker_install_execute_msi_total;
-  }
-
-  // Run the installer, retrying if necessary.
-  const int max_tries = job_->is_background() ?
-                        kNumMsiAlreadyRunningSilentMaxTries :
-                        kNumMsiAlreadyRunningInteractiveMaxTries;
-  int retry_delay = kMsiAlreadyRunningRetryDelayBaseMs;
-  int num_tries(0);
-  bool retry(true);
-  for (num_tries = 0; retry && num_tries < max_tries; ++num_tries) {
-    // Reset the completion info - it contains the previous error when retrying.
-    error_info_ = CompletionInfo();
-
-    if (0 < num_tries) {
-      // Retrying - wait between attempts.
-      ASSERT1(MSI_INSTALLER == installer_type);
-      ::Sleep(retry_delay);
-      retry_delay *= 2;  // Double the retry delay next time.
-    }
-
-    HRESULT hr = DoExecuteAndWaitForInstaller(executable_name,
-                                              command_line,
-                                              app_guid,
-                                              installer_type);
-    if (FAILED(hr)) {
-      return hr;
-    }
-
-    retry = (COMPLETION_INSTALLER_ERROR_MSI == error_info_.status ||
-             COMPLETION_INSTALLER_ERROR_SYSTEM == error_info_.status) &&
-            ERROR_INSTALL_ALREADY_RUNNING == error_info_.error_code;
-  }
-
-  if (1 < num_tries) {
-    // Record metrics about the ERROR_INSTALL_ALREADY_RUNNING retries.
-    ASSERT1(MSI_INSTALLER == installer_type);
-
-    if (!job_->is_update()) {
-      ++metric_worker_install_msi_in_progress_detected_install;
-      if (IsCompletionSuccess(error_info_)) {
-        ++metric_worker_install_msi_in_progress_retry_succeeded_install;
-        metric_worker_install_msi_in_progress_retry_succeeded_tries_install
-            = num_tries;
-      }
-    } else {
-      ++metric_worker_install_msi_in_progress_detected_update;
-      if (IsCompletionSuccess(error_info_)) {
-        ++metric_worker_install_msi_in_progress_retry_succeeded_update;
-        metric_worker_install_msi_in_progress_retry_succeeded_tries_update
-            = num_tries;
-      }
-    }
-  }
-
-  if ((COMPLETION_INSTALLER_ERROR_MSI == error_info_.status ||
-       COMPLETION_INSTALLER_ERROR_SYSTEM == error_info_.status) &&
-      ERROR_INSTALL_ALREADY_RUNNING == error_info_.error_code) {
-    return GOOPDATEINSTALL_E_MSI_INSTALL_ALREADY_RUNNING;
-  }
-
-  if (IsCompletionInstallerError(error_info_)) {
-    return GOOPDATEINSTALL_E_INSTALLER_FAILED;
-  }
-
-  return S_OK;
-}
-
-HRESULT InstallManager::DoExecuteAndWaitForInstaller(
-    const CString& executable_name,
-    const CString& command_line,
-    const CString& app_guid,
-    InstallerType installer_type) {
-  OPT_LOG(L1, (_T("[Running installer]")
-               _T("[%s][%s][%s]"), executable_name, command_line, app_guid));
-
-  CleanupInstallerResultRegistry(app_guid);
-
-  Process p(executable_name, NULL);
-
-  if (!p.Start(command_line)) {
-    OPT_LOG(LEVEL_ERROR, (_T("[Could not start process]")
-                          _T("[%s][%s]"), executable_name, command_line));
-    return GOOPDATEINSTALL_E_INSTALLER_FAILED_START;
-  }
-
-  if (app_guid.CompareNoCase(kGoogleUpdateAppId) == 0) {
-    // Do not wait for the installer when installing Omaha.
-    return S_OK;
-  }
-
-  if (!p.WaitUntilDead(kInstallManagerCompleteIntervalMs)) {
-    OPT_LOG(LEVEL_WARNING, (_T("[Installer has timed out]")
-                            _T("[%s][%s]"), executable_name, command_line));
-    return GOOPDATEINSTALL_E_INSTALLER_TIMED_OUT;
-  }
-
-  HRESULT hr = GetInstallerResult(app_guid, installer_type, p, &error_info_);
-  if (FAILED(hr)) {
-    CORE_LOG(LEVEL_ERROR, (_T("[GetInstallerResult failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  if (IsCompletionInstallerError(error_info_)) {
-    OPT_LOG(LE, (_T("[Installer failed][%s][%s][%u]"),
-                 executable_name, command_line, error_info_.error_code));
-  }
-
-  return S_OK;
-}
-
-CString InstallManager::BuildMsiCommandLine(
-    const CString& arguments,
-    const CString& filename,
-    const CString& enclosed_installer_data_file_path) {
-  CORE_LOG(L3, (_T("[InstallManager::CreateMsiCommandLine]")));
-
-  CString command_line;
-  // Suppressing reboots can lead to an inconsistent state until the user
-  // reboots, but automatically rebooting is unacceptable. The user will be
-  // informed by the string for ERROR_SUCCESS_REBOOT_REQUIRED that a reboot is
-  // necessary. See http://b/1184091 for details.
-
-  if (!enclosed_installer_data_file_path.IsEmpty()) {
-    command_line.Format(_T("INSTALLERDATA=%s "),
-                        enclosed_installer_data_file_path);
-  }
-
-  command_line.AppendFormat(_T("%s REBOOT=ReallySuppress /qn /i \"%s\""),
-                            arguments, filename);
-
-  // The msiexec version in XP SP2 (V 3.01) and higher supports the /log switch.
-  if (SystemInfo::OSWinXPSP2OrLater()) {
-    CString logfile(filename);
-    logfile.Append(_T(".log"));
-
-    command_line.AppendFormat(_T(" /log \"%s\""), logfile);
-  }
-
-  CORE_LOG(L2, (_T("[msiexec command line][%s]"), command_line));
-  return command_line;
-}
-
-HRESULT InstallManager::GetInstallerResult(const CString& app_guid,
-                                           InstallerType installer_type,
-                                           const Process& p,
-                                           CompletionInfo* completion_info) {
-  CORE_LOG(L3, (_T("[InstallManager::GetInstallerResult]")));
-  ASSERT1(completion_info);
-
-  uint32 exit_code = 0;
-  HRESULT hr = GetInstallerExitCode(p, &exit_code);
-  if (FAILED(hr)) {
-    CORE_LOG(LEVEL_ERROR, (_T("[GetInstallerExitCode failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  GetInstallerResultHelper(app_guid,
-                           installer_type,
-                           exit_code,
-                           completion_info);
-  return S_OK;
-}
-
-// The default InstallerResult behavior can be overridden in the registry.
-// By default, error_code is the exit code. For some InstallerResults, it
-// can be overridden by InstallerError in the registry.
-// The success string cannot be overridden.
-void InstallManager::GetInstallerResultHelper(const CString& app_guid,
-                                              InstallerType installer_type,
-                                              uint32 exit_code,
-                                              CompletionInfo* completion_info) {
-  completion_info->error_code = exit_code;
-  completion_info->text.Empty();
-
-  const CString app_client_state_key =
-      goopdate_utils::GetAppClientStateKey(is_machine_, app_guid);
-  const InstallerResult installer_result =
-      GetInstallerResultType(app_client_state_key);
-  OPT_LOG(L1, (_T("[InstallerResult][%s][%u]"), app_guid, installer_result));
-
-
-  switch (installer_result) {
-    case INSTALLER_RESULT_SUCCESS:
-      completion_info->status = COMPLETION_SUCCESS;
-      ReadInstallerErrorOverride(app_client_state_key,
-                                 &completion_info->error_code);
-      break;
-    case INSTALLER_RESULT_FAILED_CUSTOM_ERROR:
-      completion_info->status = COMPLETION_INSTALLER_ERROR_OTHER;
-      ReadInstallerErrorOverride(app_client_state_key,
-                                 &completion_info->error_code);
-      ReadInstallerResultUIStringOverride(app_client_state_key,
-                                          &completion_info->text);
-      break;
-    case INSTALLER_RESULT_FAILED_MSI_ERROR:
-      completion_info->status = COMPLETION_INSTALLER_ERROR_MSI;
-      ReadInstallerErrorOverride(app_client_state_key,
-                                 &completion_info->error_code);
-      break;
-    case INSTALLER_RESULT_FAILED_SYSTEM_ERROR:
-      completion_info->status = COMPLETION_INSTALLER_ERROR_SYSTEM;
-      ReadInstallerErrorOverride(app_client_state_key,
-                                 &completion_info->error_code);
-      break;
-    case INSTALLER_RESULT_EXIT_CODE:
-      if (0 == exit_code) {
-        completion_info->status = COMPLETION_SUCCESS;
-        completion_info->error_code = 0;
-      } else {
-        switch (installer_type) {
-          case MSI_INSTALLER:
-            completion_info->status = COMPLETION_INSTALLER_ERROR_MSI;
-            break;
-          case UNKNOWN_INSTALLER:
-          case CUSTOM_INSTALLER:
-          case MAX_INSTALLER:
-          default:
-            completion_info->status = COMPLETION_INSTALLER_ERROR_OTHER;
-            break;
-        }
-      }
-      break;
-    case INSTALLER_RESULT_MAX:
-    default:
-      ASSERT1(false);
-      break;
-  }
-
-  // Handle the reboot required case.
-  if ((COMPLETION_INSTALLER_ERROR_MSI == completion_info->status ||
-       COMPLETION_INSTALLER_ERROR_SYSTEM == completion_info->status) &&
-      (ERROR_SUCCESS_REBOOT_REQUIRED == completion_info->error_code)) {
-    // This is a success, but the user should be notified.
-    completion_info->status = COMPLETION_SUCCESS_REBOOT_REQUIRED;
-    completion_info->error_code = 0;
-  }
-
-  // CompletionInfo status has been finalized. Handle launch command and make
-  // sure all errors have error strings.
-  switch (completion_info->status) {
-    case COMPLETION_SUCCESS: {
-        CString cmd_line;
-        if (SUCCEEDED(RegKey::GetValue(app_client_state_key,
-                                       kRegValueInstallerSuccessLaunchCmdLine,
-                                       &cmd_line)) &&
-            !cmd_line.IsEmpty()) {
-          job_->set_launch_cmd_line(cmd_line);
-        }
-      }
-      break;
-    case COMPLETION_SUCCESS_REBOOT_REQUIRED:
-      break;
-    case COMPLETION_INSTALLER_ERROR_MSI:
-    case COMPLETION_INSTALLER_ERROR_SYSTEM:
-      ASSERT1(completion_info->text.IsEmpty());
-      GetSystemErrorString(completion_info->error_code, &completion_info->text);
-      break;
-    case COMPLETION_INSTALLER_ERROR_OTHER:
-      if (completion_info->text.IsEmpty()) {
-        completion_info->text.FormatMessage(
-              IDS_INSTALLER_FAILED_NO_MESSAGE,
-              FormatErrorCode(completion_info->error_code));
-      }
-      break;
-    case COMPLETION_ERROR:
-    case COMPLETION_CANCELLED:
-    default:
-      ASSERT1(false);
-  }
-
-  OPT_LOG(L1, (_T("[%s]"), completion_info->ToString()));
-  CleanupInstallerResultRegistry(app_guid);
-}
-
-void InstallManager::CleanupInstallerResultRegistry(const CString& app_guid) {
-  CString app_client_state_key =
-      goopdate_utils::GetAppClientStateKey(is_machine_, app_guid);
-  CString update_key = ConfigManager::Instance()->registry_update(is_machine_);
-
-  // Delete the old LastXXX values.  These may not exist, so don't care if they
-  // fail.
-  RegKey::DeleteValue(app_client_state_key,
-                      kRegValueLastInstallerResult);
-  RegKey::DeleteValue(app_client_state_key,
-                      kRegValueLastInstallerResultUIString);
-  RegKey::DeleteValue(app_client_state_key,
-                      kRegValueLastInstallerError);
-  RegKey::DeleteValue(app_client_state_key,
-                      kRegValueLastInstallerSuccessLaunchCmdLine);
-
-  // Also delete any values from Google\Update.
-  // TODO(Omaha): This is a temporary fix for bug 1539293. Need a better
-  // long-term solution.
-  RegKey::DeleteValue(update_key,
-                      kRegValueLastInstallerResult);
-  RegKey::DeleteValue(update_key,
-                      kRegValueLastInstallerResultUIString);
-  RegKey::DeleteValue(update_key,
-                      kRegValueLastInstallerError);
-  RegKey::DeleteValue(update_key,
-                      kRegValueLastInstallerSuccessLaunchCmdLine);
-
-  // Rename current InstallerResultXXX values to LastXXX.
-  RegKey::RenameValue(app_client_state_key,
-                      kRegValueInstallerResult,
-                      kRegValueLastInstallerResult);
-  RegKey::RenameValue(app_client_state_key,
-                      kRegValueInstallerError,
-                      kRegValueLastInstallerError);
-  RegKey::RenameValue(app_client_state_key,
-                      kRegValueInstallerResultUIString,
-                      kRegValueLastInstallerResultUIString);
-  RegKey::RenameValue(app_client_state_key,
-                      kRegValueInstallerSuccessLaunchCmdLine,
-                      kRegValueLastInstallerSuccessLaunchCmdLine);
-
-  // Copy over to the Google\Update key.
-  // TODO(Omaha): This is a temporary fix for bug 1539293. Need a better
-  // long-term solution.
-  RegKey::CopyValue(app_client_state_key,
-                    update_key,
-                    kRegValueLastInstallerResult);
-  RegKey::CopyValue(app_client_state_key,
-                    update_key,
-                    kRegValueLastInstallerError);
-  RegKey::CopyValue(app_client_state_key,
-                    update_key,
-                    kRegValueLastInstallerResultUIString);
-  RegKey::CopyValue(app_client_state_key,
-                    update_key,
-                    kRegValueLastInstallerSuccessLaunchCmdLine);
-}
-
-HRESULT InstallManager::CheckApplicationRegistration(
-    const CString& previous_version) {
-  CORE_LOG(L2,
-           (_T("[InstallManager::CheckApplicationRegistration][%s]"),
-            GuidToString(job_->app_data().app_guid())));
-  ASSERT1(!::IsEqualGUID(kGoopdateGuid, job_->app_data().app_guid()));
-
-  AppManager app_manager(is_machine_);
-  if (!app_manager.IsProductRegistered(job_->app_data().app_guid())) {
-    OPT_LOG(LE, (_T("[Installer did not register][%s]"),
-                 GuidToString(job_->app_data().app_guid())));
-    job_->set_extra_code1(-1);
-    return GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENT_KEY;
-  }
-
-  ProductData product_data;
-  // TODO(omaha): Should we check that each individual component was registered?
-  HRESULT hr = app_manager.ReadProductDataFromStore(job_->app_data().app_guid(),
-                                                    &product_data);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[CheckApplicationRegistration]")
-                  _T("[ReadProductDataFromStore failed][guid=%s][0x%08x]"),
-                  GuidToString(job_->app_data().app_guid()), hr));
-    job_->set_extra_code1(hr);
-    return GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENT_KEY;
-  }
-
-  CORE_LOG(L2, (_T("[CheckApplicationRegistration]")
-                _T("[guid=%s][version=%s][previous_version=%s]"),
-                GuidToString(job_->app_data().app_guid()),
-                product_data.app_data().version(),
-                previous_version));
-
-  if (product_data.app_data().version().IsEmpty()) {
-    OPT_LOG(LE, (_T("[Installer did not write version][%s]"),
-                 GuidToString(job_->app_data().app_guid())));
-    job_->set_extra_code1(-2);
-    return GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENT_KEY;
-  }
-
-  if (job_->is_update() &&
-      previous_version == product_data.app_data().version()) {
-    OPT_LOG(LE, (_T("[Installer did not change version][%s]"),
-                 GuidToString(job_->app_data().app_guid())));
-    return GOOPDATEINSTALL_E_INSTALLER_DID_NOT_CHANGE_VERSION;
-  }
-
-  // TODO(omaha): Log warning if version does not match version in config when
-  // present.
-
-  return S_OK;
-}
-
-// This method is used extensively for unit testing installation because it
-// is the highest level method that returns an error.
-// Assumes installer_lock_ has been initialized.
-HRESULT InstallManager::DoInstallation() {
-  OPT_LOG(L1, (_T("[InstallManager::DoInstallation][%s][%s][%s][%s]"),
-               job_->app_data().display_name(),
-               GuidToString(job_->app_data().app_guid()),
-               job_->download_file_name(),
-               job_->response_data().arguments()));
-
-  CString executable_name;
-  CString command_line;
-  InstallerType installer_type = UNKNOWN_INSTALLER;
-
-  CString arguments(job_->response_data().arguments());
-  const TCHAR* const kChromeGuid = _T("{8A69D345-D564-463C-AFF1-A69D9E530F96}");
-  const TCHAR* const kChromePerMachineArg = _T("--system-level");
-  if (::IsEqualGUID(StringToGuid(kChromeGuid), job_->app_data().app_guid()) &&
-      is_machine_) {
-    arguments.AppendFormat(_T(" %s"), kChromePerMachineArg);
-  }
-
-  CString installer_data;
-  HRESULT hr = job_->GetInstallerData(&installer_data);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[GetInstallerData failed][0x%x]"), hr));
-    return hr;
-  }
-
-  hr = BuildCommandLineFromFilename(job_->download_file_name(),
-                                    arguments,
-                                    installer_data,
-                                    &executable_name,
-                                    &command_line,
-                                    &installer_type);
-
-  if (FAILED(hr)) {
-    CORE_LOG(LW, (_T("[BuildCommandLineFromFilename failed][0x%08x]"), hr));
-    ASSERT1(GOOPDATEINSTALL_E_FILENAME_INVALID == hr);
-    return hr;
-  }
-
-  // We need this scope for the mutex.
-  {
-    // Acquire the global lock here. This will ensure that we are the only
-    // installer running of the multiple goopdates.
-    __mutexScope(installer_lock_);
-
-    hr = ExecuteAndWaitForInstaller(executable_name,
-                                    command_line,
-                                    GuidToString(job_->app_data().app_guid()),
-                                    installer_type);
-    if (FAILED(hr)) {
-      CORE_LOG(LW, (_T("[ExecuteAndWaitForInstaller failed][0x%08x][%s]"),
-                    hr, GuidToString(job_->app_data().app_guid())));
-    }
-  }
-
-  return hr;
-}
-
-HRESULT InstallManager::InstallDownloadedFile() {
-  CString previous_version = job_->app_data().previous_version();
-
-  HRESULT hr = DoInstallation();
-  if (FAILED(hr)) {
-    CORE_LOG(LEVEL_ERROR, (_T("[DoInstallation failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  if (::IsEqualGUID(kGoopdateGuid, job_->app_data().app_guid())) {
-    // Do not check application registration because the installer has not
-    // completed.
-    return S_OK;
-  }
-
-  // Ensure that the app installer wrote the minimum required registry values.
-  hr = CheckApplicationRegistration(previous_version);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return S_OK;
-}
-
-InstallManager::InstallerResult InstallManager::GetInstallerResultType(
-    const CString& app_client_state_key) {
-  InstallerResult installer_result = INSTALLER_RESULT_DEFAULT;
-  if (SUCCEEDED(RegKey::GetValue(
-      app_client_state_key,
-      kRegValueInstallerResult,
-      reinterpret_cast<DWORD*>(&installer_result)))) {
-    CORE_LOG(L2, (_T("[InstallerResult in registry][%u]"), installer_result));
-  }
-  if (INSTALLER_RESULT_MAX <= installer_result) {
-    CORE_LOG(LW, (_T("[Unsupported InstallerResult value]")));
-    installer_result = INSTALLER_RESULT_DEFAULT;
-  }
-
-  return installer_result;
-}
-
-void InstallManager::ReadInstallerErrorOverride(
-    const CString& app_client_state_key,
-    DWORD* installer_error) {
-  ASSERT1(installer_error);
-  if (FAILED(RegKey::GetValue(app_client_state_key,
-                              kRegValueInstallerError,
-                              installer_error))) {
-    CORE_LOG(LW, (_T("[InstallerError not found in registry]")));
-  }
-}
-
-void InstallManager::ReadInstallerResultUIStringOverride(
-    const CString& app_client_state_key,
-    CString* installer_result_uistring) {
-  ASSERT1(installer_result_uistring);
-  if (FAILED(RegKey::GetValue(app_client_state_key,
-                              kRegValueInstallerResultUIString,
-                              installer_result_uistring))) {
-    CORE_LOG(LW, (_T("[InstallerResultUIString not found in registry]")));
-  }
-}
-
-}  // namespace omaha
diff --git a/worker/install_manager.h b/worker/install_manager.h
deleted file mode 100644
index f80f1ae..0000000
--- a/worker/install_manager.h
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-
-#ifndef OMAHA_WORKER_INSTALL_MANAGER_H__
-#define OMAHA_WORKER_INSTALL_MANAGER_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include <queue>
-#include <utility>
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "omaha/common/synchronized.h"
-#include "omaha/worker/job.h"
-
-namespace omaha {
-
-class Process;
-
-class InstallManager {
- public:
-  explicit InstallManager(bool is_machine);
-  HRESULT InstallJob(Job* job);
-  CompletionInfo error_info() const { return error_info_; }
-
- private:
-  // These values are a public API. Do not remove or move existing values.
-  enum InstallerResult {
-    INSTALLER_RESULT_SUCCESS = 0,
-    INSTALLER_RESULT_FAILED_CUSTOM_ERROR = 1,
-    INSTALLER_RESULT_FAILED_MSI_ERROR = 2,
-    INSTALLER_RESULT_FAILED_SYSTEM_ERROR = 3,
-    INSTALLER_RESULT_EXIT_CODE = 4,
-    INSTALLER_RESULT_DEFAULT = INSTALLER_RESULT_EXIT_CODE,
-    INSTALLER_RESULT_MAX,
-  };
-
-  // Types of installers that Omaha supports.
-  enum InstallerType {
-    UNKNOWN_INSTALLER = 0,
-    CUSTOM_INSTALLER,
-    MSI_INSTALLER,
-    MAX_INSTALLER  // Last Installer Type value.
-  };
-
-  // Gets the info about the signaled job and performs the installation.
-  HRESULT StartAndMonitorInstaller();
-
-  // Determines the executable, command line, and installer type for
-  // the installation based on the filename.
-  HRESULT BuildCommandLineFromFilename(const CString& filename,
-                                       const CString& arguments,
-                                       const CString& installer_data,
-                                       CString* executable_name,
-                                       CString* command_line,
-                                       InstallerType* installer_type);
-
-  // Executes the installer and waits for it to complete. Retries if necessary.
-  HRESULT ExecuteAndWaitForInstaller(const CString& executable_name,
-                                     const CString& command_line,
-                                     const CString& app_guid,
-                                     InstallerType installer_type);
-
-  // Executes the installer for ExecuteAndWaitForInstaller.
-  HRESULT DoExecuteAndWaitForInstaller(const CString& executable_name,
-                                       const CString& command_line,
-                                       const CString& app_guid,
-                                       InstallerType installer_type);
-
-  // Builds and returns the command line that is used to launch the msi.
-  CString BuildMsiCommandLine(const CString& arguments,
-                              const CString& filename,
-                              const CString& enclosed_installer_data_file_path);
-
-  // Determines whether the installer succeeded and returns completion info.
-  HRESULT GetInstallerResult(const CString& app_guid,
-                             InstallerType installer_type,
-                             const Process& p,
-                             CompletionInfo* completion_info);
-
-  // Does most of the work for GetInstallerResult.
-  void GetInstallerResultHelper(const CString& app_guid,
-                                InstallerType installer_type,
-                                uint32 exit_code,
-                                CompletionInfo* completion_info);
-
-  // Cleans up the registry from an installer that set custom result values.
-  void CleanupInstallerResultRegistry(const CString& app_guid);
-
-  // Executes the specified installer.
-  HRESULT DoInstallation();
-
-  // Installs the specified application and reports the results.
-  HRESULT InstallDownloadedFile();
-
-  // Validate that the installer wrote the client key and the product version.
-  HRESULT CheckApplicationRegistration(const CString& previous_version);
-
-  // TODO(omaha): consider helpers that take an app_guid and return
-  // the installer results. They might be better since they abstract out
-  // where the values are stored in registry. The current code is less
-  // encapsulated and a lot more calling code needs to change when the physical
-  // location changes in registry. Ideally only a few lines of code should
-  // change, in the event the registry location changes.
-
-  // Gets the InstallerResult type, possibly from the registry.
-  static InstallerResult GetInstallerResultType(
-      const CString& app_client_state_key);
-
-  // Reads the InstallerError value from the registry if present.
-  static void ReadInstallerErrorOverride(const CString& app_client_state_key,
-                                         DWORD* installer_error);
-
-  // Reads the InstallerResultUIString value from the registry if present.
-  static void InstallManager::ReadInstallerResultUIStringOverride(
-      const CString& app_client_state_key,
-      CString* installer_result_uistring);
-
-  // Whether this object is running in a machine Goopdate instance.
-  bool is_machine_;
-
-  // The error information for the install.
-  CompletionInfo error_info_;
-
-  // Ensures that a single installer is run by us at a time.
-  // Not sure if we can run installers in different sessions without
-  // interference. In that case we can use a local lock instead of a
-  // global lock.
-  GLock installer_lock_;
-
-  // The job instance.
-  Job* job_;
-
-  friend class InstallManagerTest;
-
-  DISALLOW_EVIL_CONSTRUCTORS(InstallManager);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_INSTALL_MANAGER_H__
-
diff --git a/worker/install_manager_unittest.cc b/worker/install_manager_unittest.cc
deleted file mode 100644
index 3add208..0000000
--- a/worker/install_manager_unittest.cc
+++ /dev/null
@@ -1,1404 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <string>
-#include "omaha/common/app_util.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/path.h"
-#include "omaha/common/reg_key.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/common/sta.h"
-#include "omaha/common/system.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/resource_manager.h"
-#include "omaha/testing/unit_test.h"
-#include "omaha/worker/install_manager.h"
-#include "omaha/worker/ping.h"
-
-namespace {
-
-// Arbitrary values to fill in the job.
-// We use this value for all installers, including test_foo.
-const TCHAR kJobGuid[] = _T("{B18BC01B-E0BD-4BF0-A33E-1133055E5FDE}");
-const int kJobSize = 31415;
-const TCHAR kJobHash[] = _T("InvalidUnusedHashValue");
-const TCHAR kJobUrl[] = _T("invalid://url/to/nowhere/");
-
-const TCHAR kFullJobAppClientsKeyPath[] =
-    _T("HKCU\\Software\\Google\\Update\\Clients\\")
-    _T("{B18BC01B-E0BD-4BF0-A33E-1133055E5FDE}");
-const TCHAR kFullJobAppClientStateKeyPath[] =
-    _T("HKCU\\Software\\Google\\Update\\ClientState\\")
-    _T("{B18BC01B-E0BD-4BF0-A33E-1133055E5FDE}");
-const TCHAR kFullFooAppClientKeyPath[] =
-    _T("HKLM\\Software\\Google\\Update\\Clients\\")
-    _T("{D6B08267-B440-4C85-9F79-E195E80D9937}");
-const TCHAR kFullFooAppClientStateKeyPath[] =
-    _T("HKLM\\Software\\Google\\Update\\ClientState\\")
-    _T("{D6B08267-B440-4C85-9F79-E195E80D9937}");
-
-const TCHAR kSetupFooV1RelativeLocation[] =
-  _T("unittest_support\\test_foo_v1.0.101.0.msi");
-const TCHAR kFooGuid[] = _T("{D6B08267-B440-4C85-9F79-E195E80D9937}");
-const TCHAR kFooInstallerBarPropertyArg[] = _T("PROPBAR=7");
-const TCHAR kFooInstallerBarValueName[] = _T("propbar");
-
-// Meaningful values that we do something with.
-const TCHAR kJobExecutable[] = _T("cmd.exe");
-const TCHAR kExecuteCommandAndTerminateSwitch[] = _T("/c %s");
-
-const TCHAR kMsiLogFormat[] = _T("%s.log");
-
-const TCHAR kMsiUninstallArguments[] = _T("/quiet /uninstall %s");
-const TCHAR kMsiCommand[] = _T("msiexec");
-
-const DWORD kInitialErrorValue = 5;
-const TCHAR kMeaninglessErrorString[] = _T("This is an error string.");
-
-// The US English error string for ERROR_INSTALL_PACKAGE_OPEN_FAILED.
-// It is slightly different on Vista than XP - a space was removed.
-// Therefore, the comparison must ignore that space.
-const TCHAR kMsiPackageOpenFailedStringPartA[] =
-    _T("This installation package could not be opened. ");
-const TCHAR kMsiPackageOpenFailedStringPartB[] =
-    _T("Verify that the package exists and that you can access it, ")
-    _T("or contact the application vendor to verify that this is a ")
-    _T("valid Windows Installer package. ");
-
-void VerifyStringIsMsiPackageOpenFailedString(const CString& str) {
-  EXPECT_STREQ(kMsiPackageOpenFailedStringPartA,
-               str.Left(arraysize(kMsiPackageOpenFailedStringPartA) - 1));
-  EXPECT_STREQ(kMsiPackageOpenFailedStringPartB,
-               str.Right(arraysize(kMsiPackageOpenFailedStringPartB) - 1));
-}
-
-const TCHAR* const kError1603Text =
-    _T("The installer encountered error 1603: Fatal error during ")
-    _T("installation. ");
-
-const TCHAR* const kError0x800B010FText =
-    _T("The installer encountered error 0x800b010f: ")
-    _T("The certificate's CN name does not match the passed value. ");
-
-  const TCHAR* const kLaunchCmdLine =
-      _T("\"C:\\Local\\Google\\Chrome\\Application\\chrome.exe\" -home");
-
-}  // namespace
-
-namespace omaha {
-
-class InstallManagerTest : public testing::Test {
- protected:
-  explicit InstallManagerTest(bool is_machine)
-      : is_machine_(is_machine),
-        hive_override_key_name_(kRegistryHiveOverrideRoot) {
-  }
-
-  virtual void SetUp() {
-    install_manager_.reset(new InstallManager(is_machine_));
-
-    RegKey::DeleteKey(hive_override_key_name_, true);
-    OverrideRegistryHivesWithExecutionPermissions(hive_override_key_name_);
-
-    ResourceManager manager(is_machine_, app_util::GetCurrentModuleDirectory());
-    ASSERT_SUCCEEDED(manager.LoadResourceDll(_T("en")));
-  }
-
-  virtual void TearDown() {
-    RestoreRegistryHives();
-    ASSERT_SUCCEEDED(RegKey::DeleteKey(hive_override_key_name_, true));
-  }
-
-  void SetInstallManagerJob(Job* job) {
-    ASSERT_TRUE(job);
-    install_manager_->job_ = job;
-  }
-
-  HRESULT CheckApplicationRegistration(const CString& previous_version,
-                                       Job* job) {
-    ASSERT1(job);
-    install_manager_->job_ = job;
-    return install_manager_->CheckApplicationRegistration(previous_version);
-  }
-
-  void SetUpdateResponseDataArguments(const CString& arguments, Job* job) {
-    ASSERT1(job);
-    job->update_response_data_.set_arguments(arguments);
-  }
-
-  void SetupInstallerResultRegistry(const CString& app_guid,
-                                    bool set_installer_result,
-                                    DWORD installer_result,
-                                    bool set_installer_error,
-                                    DWORD installer_error,
-                                    bool set_installer_result_uistring,
-                                    const CString& installer_result_uistring,
-                                    bool set_installer_launch_cmd_line,
-                                    const CString& installer_launch_cmd_line) {
-    CString app_client_state_key =
-        goopdate_utils::GetAppClientStateKey(is_machine_, app_guid);
-    RegKey::CreateKey(app_client_state_key);
-    if (set_installer_result) {
-      RegKey::SetValue(app_client_state_key,
-                       kRegValueInstallerResult,
-                       installer_result);
-    }
-
-    if (set_installer_error) {
-      RegKey::SetValue(app_client_state_key,
-                       kRegValueInstallerError,
-                       installer_error);
-    }
-
-    if (set_installer_result_uistring) {
-      RegKey::SetValue(app_client_state_key,
-                       kRegValueInstallerResultUIString,
-                       installer_result_uistring);
-    }
-
-    if (set_installer_launch_cmd_line) {
-      RegKey::SetValue(app_client_state_key,
-                       kRegValueInstallerSuccessLaunchCmdLine,
-                       installer_launch_cmd_line);
-    }
-  }
-
-  void VerifyLastRegistryValues(const CString& app_guid,
-                                bool expect_installer_result,
-                                DWORD expected_installer_result,
-                                bool expect_installer_error,
-                                DWORD expected_installer_error,
-                                bool expect_installer_result_uistring,
-                                const CString& expected_result_uistring,
-                                bool expect_installer_launch_cmd_line,
-                                const CString& expected_launch_cmd_line) {
-    ASSERT_TRUE(expect_installer_result || !expected_installer_result);
-    ASSERT_TRUE(expect_installer_error || !expected_installer_error);
-    ASSERT_TRUE(expect_installer_result_uistring ||
-                expected_result_uistring.IsEmpty());
-    ASSERT_TRUE(expect_installer_launch_cmd_line ||
-                expected_launch_cmd_line.IsEmpty());
-
-    CString app_client_state_key =
-        goopdate_utils::GetAppClientStateKey(is_machine_, app_guid);
-    EXPECT_FALSE(RegKey::HasValue(app_client_state_key,
-                                  kRegValueInstallerResult));
-    EXPECT_FALSE(RegKey::HasValue(app_client_state_key,
-                                  kRegValueInstallerError));
-    EXPECT_FALSE(RegKey::HasValue(app_client_state_key,
-                                  kRegValueInstallerResultUIString));
-
-    if (expect_installer_result) {
-      EXPECT_TRUE(RegKey::HasValue(app_client_state_key,
-                                   kRegValueLastInstallerResult));
-      DWORD last_installer_result = 0;
-      EXPECT_SUCCEEDED(RegKey::GetValue(app_client_state_key,
-                                        kRegValueLastInstallerResult,
-                                        &last_installer_result));
-      EXPECT_EQ(expected_installer_result, last_installer_result);
-    } else {
-      EXPECT_FALSE(RegKey::HasValue(app_client_state_key,
-                                    kRegValueLastInstallerResult));
-    }
-
-    if (expect_installer_error) {
-      EXPECT_TRUE(RegKey::HasValue(app_client_state_key,
-                                   kRegValueLastInstallerError));
-      DWORD last_installer_error = 0;
-      EXPECT_SUCCEEDED(RegKey::GetValue(app_client_state_key,
-                                        kRegValueLastInstallerError,
-                                        &last_installer_error));
-      EXPECT_EQ(expected_installer_error, last_installer_error);
-    } else {
-      EXPECT_FALSE(RegKey::HasValue(app_client_state_key,
-                                    kRegValueLastInstallerError));
-    }
-
-    if (expect_installer_result_uistring) {
-      EXPECT_TRUE(RegKey::HasValue(app_client_state_key,
-                                   kRegValueLastInstallerResultUIString));
-      CString last_installer_result_uistring;
-      EXPECT_SUCCEEDED(RegKey::GetValue(app_client_state_key,
-                                        kRegValueLastInstallerResultUIString,
-                                        &last_installer_result_uistring));
-      EXPECT_STREQ(expected_result_uistring,
-                   last_installer_result_uistring);
-    } else {
-      EXPECT_FALSE(RegKey::HasValue(app_client_state_key,
-                                    kRegValueLastInstallerResultUIString));
-    }
-
-    if (expect_installer_launch_cmd_line) {
-      EXPECT_TRUE(RegKey::HasValue(app_client_state_key,
-                                   kRegValueLastInstallerSuccessLaunchCmdLine));
-      CString last_installer_launch_cmd_line;
-      EXPECT_SUCCEEDED(
-          RegKey::GetValue(app_client_state_key,
-                           kRegValueLastInstallerSuccessLaunchCmdLine,
-                           &last_installer_launch_cmd_line));
-      EXPECT_STREQ(expected_launch_cmd_line,
-                   last_installer_launch_cmd_line);
-    } else {
-      EXPECT_FALSE(RegKey::HasValue(
-          app_client_state_key,
-          kRegValueLastInstallerSuccessLaunchCmdLine));
-    }
-  }
-
-  void VerifyNoLastRegistryValues(const CString& app_guid) {
-    VerifyLastRegistryValues(app_guid,
-                             false, 0,
-                             false, 0,
-                             false, _T(""),
-                             false, _T(""));
-  }
-
-  void GetInstallerResultHelper(const CString& app_guid,
-                                int installer_type,
-                                uint32 exit_code,
-                                CompletionInfo* completion_info) {
-    install_manager_->GetInstallerResultHelper(
-        app_guid,
-        static_cast<InstallManager::InstallerType>(installer_type),
-        exit_code,
-        completion_info);
-  }
-
-  static const int kResultSuccess = InstallManager::INSTALLER_RESULT_SUCCESS;
-  static const int kResultFailedCustomError =
-      InstallManager::INSTALLER_RESULT_FAILED_CUSTOM_ERROR;
-  static const int kResultFailedMsiError =
-      InstallManager::INSTALLER_RESULT_FAILED_MSI_ERROR;
-  static const int kResultFailedSystemError =
-      InstallManager::INSTALLER_RESULT_FAILED_SYSTEM_ERROR;
-  static const int kResultExitCode = InstallManager::INSTALLER_RESULT_EXIT_CODE;
-
-  static const int kMsiInstaller = InstallManager::MSI_INSTALLER;
-  static const int kOtherInstaller = InstallManager::CUSTOM_INSTALLER;
-
-  bool is_machine_;
-  CString hive_override_key_name_;
-  scoped_ptr<InstallManager> install_manager_;
-  Ping ping_;
-};
-
-class InstallManagerMachineTest : public InstallManagerTest {
- protected:
-  InstallManagerMachineTest()
-    : InstallManagerTest(true) {
-  }
-};
-
-class InstallManagerUserTest : public InstallManagerTest {
- protected:
-  InstallManagerUserTest()
-    : InstallManagerTest(false) {
-  }
-};
-
-class InstallManagerUserGetInstallerResultHelperTest
-    : public InstallManagerUserTest {
- protected:
-  InstallManagerUserGetInstallerResultHelperTest()
-      : InstallManagerUserTest() {
-  }
-
-  virtual void SetUp() {
-    InstallManagerUserTest::SetUp();
-
-    completion_info_.error_code = kInitialErrorValue;
-    completion_info_.text = kMeaninglessErrorString;
-
-    job_.reset(new Job(false, &ping_));
-    SetInstallManagerJob(job_.get());
-  }
-
-  CompletionInfo completion_info_;
-  scoped_ptr<Job> job_;
-};
-
-
-//
-// Helper method tests
-//
-
-bool GetMessageForSystemErrorCode(DWORD system_error_code, CString* message);
-
-TEST(InstallManagerTest, TestGetMessageForSystemErrorCode) {
-  CString message;
-
-  EXPECT_TRUE(GetMessageForSystemErrorCode(ERROR_INSTALL_PACKAGE_OPEN_FAILED,
-                                           &message));
-  VerifyStringIsMsiPackageOpenFailedString(message);
-}
-
-TEST_F(InstallManagerUserTest,
-       CheckApplicationRegistration_InstallFailsWhenClientsKeyAbsent) {
-  AppData app_data(StringToGuid(kJobGuid), is_machine_);
-  Job job(false, &ping_);
-  job.set_app_data(app_data);
-
-  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENT_KEY,
-            CheckApplicationRegistration(CString(), &job));
-
-  EXPECT_FALSE(RegKey::HasKey(kFullJobAppClientsKeyPath));
-  EXPECT_FALSE(RegKey::HasKey(kFullJobAppClientStateKeyPath));
-}
-
-TEST_F(InstallManagerUserTest,
-       CheckApplicationRegistration_InstallFailsWhenVersionValueAbsent) {
-  ASSERT_SUCCEEDED(RegKey::CreateKey(kFullJobAppClientsKeyPath));
-
-  AppData app_data(StringToGuid(kJobGuid), is_machine_);
-  Job job(false, &ping_);
-  job.set_app_data(app_data);
-
-  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENT_KEY,
-            CheckApplicationRegistration(CString(), &job));
-
-  EXPECT_TRUE(RegKey::HasKey(kFullJobAppClientsKeyPath));
-  EXPECT_FALSE(RegKey::HasKey(kFullJobAppClientStateKeyPath));
-}
-
-TEST_F(InstallManagerUserTest,
-       CheckApplicationRegistration_InstallSucceedsWhenStateKeyAbsent) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(kFullJobAppClientsKeyPath,
-                                    kRegValueProductVersion,
-                                    _T("0.9.68.4")));
-
-  AppData app_data(StringToGuid(kJobGuid), is_machine_);
-  Job job(false, &ping_);
-  job.set_app_data(app_data);
-
-  EXPECT_SUCCEEDED(CheckApplicationRegistration(CString(), &job));
-}
-
-TEST_F(InstallManagerUserTest,
-       CheckApplicationRegistration_InstallSucceedsWhenStateKeyPresent) {
-  const TCHAR* keys_to_create[] = {kFullJobAppClientsKeyPath,
-                                   kFullJobAppClientStateKeyPath};
-  ASSERT_SUCCEEDED(RegKey::CreateKeys(keys_to_create, 2));
-  ASSERT_TRUE(RegKey::HasKey(kFullJobAppClientsKeyPath));
-  ASSERT_TRUE(RegKey::HasKey(kFullJobAppClientStateKeyPath));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kFullJobAppClientsKeyPath,
-                                    kRegValueProductVersion,
-                                    _T("0.9.70.0")));
-
-  AppData app_data(StringToGuid(kJobGuid), is_machine_);
-  Job job(false, &ping_);
-  job.set_app_data(app_data);
-
-  // The install should succeed even if the version is the same.
-  EXPECT_SUCCEEDED(CheckApplicationRegistration(_T("0.9.70.0"), &job));
-}
-
-TEST_F(InstallManagerUserTest, CheckApplicationRegistration_UpdateSucceeds) {
-  const TCHAR* keys_to_create[] = {kFullJobAppClientsKeyPath,
-                                   kFullJobAppClientStateKeyPath};
-  ASSERT_SUCCEEDED(RegKey::CreateKeys(keys_to_create, 2));
-  ASSERT_TRUE(RegKey::HasKey(kFullJobAppClientsKeyPath));
-  ASSERT_TRUE(RegKey::HasKey(kFullJobAppClientStateKeyPath));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kFullJobAppClientsKeyPath,
-                                    kRegValueProductVersion,
-                                    _T("0.9.70.0")));
-
-  AppData app_data(StringToGuid(kJobGuid), is_machine_);
-  Job job(true, &ping_);
-  job.set_is_background(true);
-  job.set_app_data(app_data);
-
-  EXPECT_SUCCEEDED(CheckApplicationRegistration(_T("0.9.70.1"), &job));
-}
-
-TEST_F(InstallManagerUserTest,
-       CheckApplicationRegistration_UpdateFailsWhenVersionDoesNotChange) {
-  const TCHAR* keys_to_create[] = {kFullJobAppClientsKeyPath,
-                                   kFullJobAppClientStateKeyPath};
-  ASSERT_SUCCEEDED(RegKey::CreateKeys(keys_to_create,
-                                      arraysize(keys_to_create)));
-  ASSERT_TRUE(RegKey::HasKey(kFullJobAppClientsKeyPath));
-  ASSERT_TRUE(RegKey::HasKey(kFullJobAppClientStateKeyPath));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kFullJobAppClientsKeyPath,
-                                    kRegValueProductVersion,
-                                    _T("0.9.70.0")));
-
-  AppData app_data(StringToGuid(kJobGuid), is_machine_);
-  Job job(true, &ping_);
-  job.set_is_background(true);
-  job.set_app_data(app_data);
-
-  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_CHANGE_VERSION,
-            CheckApplicationRegistration(_T("0.9.70.0"), &job));
-}
-
-//
-// Negative Tests
-//
-
-TEST_F(InstallManagerUserTest, InstallJob_InstallerWithoutFilenameExtension) {
-  AppData app_data(StringToGuid(kJobGuid), is_machine_);
-  Job job(false, &ping_);
-  job.set_download_file_name(_T("foo"));
-  job.set_app_data(app_data);
-
-  EXPECT_EQ(GOOPDATEINSTALL_E_FILENAME_INVALID,
-            install_manager_->InstallJob(&job));
-
-  const CompletionInfo completion_info = install_manager_->error_info();
-  EXPECT_EQ(COMPLETION_ERROR, completion_info.status);
-  EXPECT_EQ(GOOPDATEINSTALL_E_FILENAME_INVALID, completion_info.error_code);
-  EXPECT_STREQ(_T("The installer filename foo is invalid or unsupported."),
-               completion_info.text);
-}
-
-TEST_F(InstallManagerUserTest,
-       InstallJob_UnsupportedInstallerFilenameExtension) {
-  AppData app_data(StringToGuid(kJobGuid), is_machine_);
-  Job job(false, &ping_);
-  job.set_download_file_name(_T("foo.bar"));
-  job.set_app_data(app_data);
-
-  EXPECT_EQ(GOOPDATEINSTALL_E_FILENAME_INVALID,
-            install_manager_->InstallJob(&job));
-
-  const CompletionInfo completion_info = install_manager_->error_info();
-  EXPECT_EQ(COMPLETION_ERROR, completion_info.status);
-  EXPECT_EQ(GOOPDATEINSTALL_E_FILENAME_INVALID, completion_info.error_code);
-  EXPECT_STREQ(_T("The installer filename foo.bar is invalid or unsupported."),
-               completion_info.text);
-}
-
-TEST_F(InstallManagerUserTest, InstallJob_InstallerEmtpyFilename) {
-  AppData app_data(StringToGuid(kJobGuid), is_machine_);
-  Job job(false, &ping_);
-  job.set_app_data(app_data);
-
-  EXPECT_EQ(GOOPDATEINSTALL_E_FILENAME_INVALID,
-            install_manager_->InstallJob(&job));
-
-  const CompletionInfo completion_info = install_manager_->error_info();
-  EXPECT_EQ(COMPLETION_ERROR, completion_info.status);
-  EXPECT_EQ(GOOPDATEINSTALL_E_FILENAME_INVALID, completion_info.error_code);
-  EXPECT_STREQ(_T("The installer filename  is invalid or unsupported."),
-               completion_info.text);
-}
-
-TEST_F(InstallManagerUserTest, InstallJob_ExeFileDoesNotExist) {
-  AppData app_data(StringToGuid(kJobGuid), is_machine_);
-  Job job(false, &ping_);
-  job.set_download_file_name(_T("foo.exe"));
-  job.set_app_data(app_data);
-
-  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED_START,
-            install_manager_->InstallJob(&job));
-
-  const CompletionInfo completion_info = install_manager_->error_info();
-  EXPECT_EQ(COMPLETION_ERROR, completion_info.status);
-  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED_START,
-            completion_info.error_code);
-  EXPECT_STREQ(_T("The installer failed to start."), completion_info.text);
-
-  EXPECT_FALSE(RegKey::HasKey(kFullJobAppClientsKeyPath));
-  EXPECT_FALSE(RegKey::HasKey(kFullJobAppClientStateKeyPath));
-}
-
-//
-// MSI Installer Fails Tests
-//
-
-TEST_F(InstallManagerUserTest, InstallJob_MsiFileDoesNotExist) {
-  const TCHAR kLogFileName[] = _T("foo.msi.log");
-  const TCHAR kExpectedErrorStringPartA[] =
-      _T("The installer encountered error 1619: ");
-  const int kExpectedErrorStringPartALength =
-      arraysize(kExpectedErrorStringPartA) - 1;
-
-  ASSERT_SUCCEEDED(File::Remove(kLogFileName));
-  ASSERT_FALSE(File::Exists(kLogFileName));
-
-  AppData app_data(StringToGuid(kJobGuid), is_machine_);
-  Job job(false, &ping_);
-  job.set_download_file_name(_T("foo.msi"));
-  job.set_app_data(app_data);
-
-  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED,
-            install_manager_->InstallJob(&job));
-
-  const CompletionInfo completion_info = install_manager_->error_info();
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_MSI, completion_info.status);
-  EXPECT_EQ(ERROR_INSTALL_PACKAGE_OPEN_FAILED, completion_info.error_code);
-  EXPECT_STREQ(kExpectedErrorStringPartA,
-               completion_info.text.Left(kExpectedErrorStringPartALength));
-  VerifyStringIsMsiPackageOpenFailedString(
-      completion_info.text.Mid(kExpectedErrorStringPartALength));
-
-  // msiexec creates an empty log file.
-  EXPECT_TRUE(File::Exists(kLogFileName));
-  EXPECT_SUCCEEDED(File::Remove(kLogFileName));
-}
-
-//
-// EXE Installer Tests
-//
-
-// This test uses cmd.exe as an installer that leaves the payload
-// kPayloadFileName.
-TEST_F(InstallManagerUserTest, InstallJob_ExeInstallerWithArgumentsSucceeds) {
-  const TCHAR kPayloadFileName[] = _T("exe_payload.txt");
-  const TCHAR kCommandToExecute[] = _T("echo \"hi\" > %s");
-
-  CString full_command_to_execute;
-  full_command_to_execute.Format(kCommandToExecute, kPayloadFileName);
-  CString arguments;
-  arguments.Format(kExecuteCommandAndTerminateSwitch, full_command_to_execute);
-
-  ASSERT_SUCCEEDED(File::Remove(kPayloadFileName));
-  ASSERT_FALSE(File::Exists(kPayloadFileName));
-
-  // Create the Clients key since this isn't an actual installer.
-  ASSERT_SUCCEEDED(RegKey::CreateKey(kFullJobAppClientsKeyPath));
-  ASSERT_TRUE(RegKey::HasKey(kFullJobAppClientsKeyPath));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kFullJobAppClientsKeyPath,
-                                    kRegValueProductVersion,
-                                    _T("0.10.69.5")));
-
-  AppData app_data(StringToGuid(kJobGuid), is_machine_);
-  app_data.set_display_name(_T("Exe App"));
-  Job job(false, &ping_);
-  job.set_download_file_name(kJobExecutable);
-  job.set_app_data(app_data);
-  SetUpdateResponseDataArguments(arguments, &job);
-
-  EXPECT_SUCCEEDED(install_manager_->InstallJob(&job));
-
-  const CompletionInfo completion_info = install_manager_->error_info();
-  EXPECT_EQ(COMPLETION_SUCCESS, completion_info.status);
-  EXPECT_EQ(S_OK, completion_info.error_code);
-  EXPECT_STREQ(_T("Thanks for installing Exe App."), completion_info.text);
-
-  EXPECT_TRUE(File::Exists(kPayloadFileName));
-  EXPECT_SUCCEEDED(File::Remove(kPayloadFileName));
-}
-
-// The command we execute causes an exit code of 1 because the F and B colors
-// are the same.
-TEST_F(InstallManagerUserTest, InstallJob_ExeInstallerReturnsNonZeroExitCode) {
-  const TCHAR kCommandToExecute[] = _T("color 00");
-
-  CString arguments;
-  arguments.Format(kExecuteCommandAndTerminateSwitch, kCommandToExecute);
-
-  // Create the Clients key since this isn't an actual installer.
-  ASSERT_SUCCEEDED(RegKey::CreateKey(kFullJobAppClientsKeyPath));
-  ASSERT_TRUE(RegKey::HasKey(kFullJobAppClientsKeyPath));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kFullJobAppClientsKeyPath,
-                                    kRegValueProductVersion,
-                                    _T("0.10.69.5")));
-
-  AppData app_data(StringToGuid(kJobGuid), is_machine_);
-  Job job(false, &ping_);
-  job.set_download_file_name(kJobExecutable);
-  job.set_app_data(app_data);
-  SetUpdateResponseDataArguments(arguments, &job);
-
-  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED,
-            install_manager_->InstallJob(&job));
-
-  const CompletionInfo completion_info = install_manager_->error_info();
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_OTHER, completion_info.status);
-  EXPECT_EQ(1, completion_info.error_code);
-  EXPECT_STREQ(_T("The installer encountered error 1."),
-               completion_info.text);
-}
-
-/* TODO(omaha): Figure out a way to perform this test.
-   CleanupInstallerResultRegistry clears the result values it sets.
-   TODO(omaha): Add another test that reports an error in using registry API.
-// Also tests that the launch cmd is set.
-TEST_F(InstallManagerUserTest,
-       InstallJob_ExeInstallerReturnsNonZeroExitCode_InstallerResultSuccess) {
-  const TCHAR kCommandToExecute[] = _T("color 00");
-
-  CString arguments;
-  arguments.Format(kExecuteCommandAndTerminateSwitch, kCommandToExecute);
-
-  // Create the Clients key since this isn't an actual installer.
-  ASSERT_SUCCEEDED(RegKey::CreateKey(kFullJobAppClientsKeyPath));
-  ASSERT_TRUE(RegKey::HasKey(kFullJobAppClientsKeyPath));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kFullJobAppClientsKeyPath,
-                                    kRegValueProductVersion,
-                                    _T("0.10.69.5")));
-  SetupInstallerResultRegistry(kJobGuid,
-                               true, kResultSuccess,
-                               false, 0,
-                               false, _T(""),
-                               true, kLaunchCmdLine);
-
-  AppData app_data(StringToGuid(kJobGuid), is_machine_);
-  app_data.set_display_name(_T("color 00"));
-  Job job(false, &ping_);
-  job.set_download_file_name(kJobExecutable);
-  job.set_app_data(app_data);
-  SetUpdateResponseDataArguments(arguments, &job);
-
-  EXPECT_SUCCEEDED(install_manager_->InstallJob(&job));
-
-  const CompletionInfo completion_info = install_manager_->error_info();
-  EXPECT_EQ(COMPLETION_SUCCESS, completion_info.status);
-  EXPECT_EQ(1, completion_info.error_code);
-  EXPECT_STREQ(_T("Thanks for installing color 00."), completion_info.text);
-  EXPECT_STREQ(kLaunchCmdLine, job.launch_cmd_line());
-
-  VerifyLastRegistryValues(kJobGuid,
-                           true, kResultSuccess,
-                           false, 0,
-                           false, _T(""),
-                           true, kLaunchCmdLine);
-}
-*/
-
-TEST_F(InstallManagerMachineTest, InstallJob_MsiInstallerSucceeds) {
-  if (!vista_util::IsUserAdmin()) {
-    std::wcout << _T("\tTest did not run because the user is not an admin.")
-               << std::endl;
-    return;
-  }
-  const TCHAR expected_iid_string[] =
-      _T("{BF66411E-8FAC-4E2C-920C-849DF562621C}");
-  const GUID expected_iid = StringToGuid(expected_iid_string);
-
-  // We can't fake the registry keys because we are interacting with a real
-  // installer.
-  RestoreRegistryHives();
-
-  CString installer_full_path(
-      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                      kSetupFooV1RelativeLocation));
-  ASSERT_TRUE(File::Exists(installer_full_path));
-
-  CString installer_log_full_path;
-  installer_log_full_path.Format(kMsiLogFormat, installer_full_path);
-
-  ASSERT_SUCCEEDED(File::Remove(installer_log_full_path));
-  ASSERT_FALSE(File::Exists(installer_log_full_path));
-
-  RegKey::DeleteKey(kFullFooAppClientKeyPath);
-  ASSERT_FALSE(RegKey::HasKey(kFullFooAppClientKeyPath));
-  RegKey::DeleteKey(kFullFooAppClientStateKeyPath);
-  ASSERT_FALSE(RegKey::HasKey(kFullFooAppClientStateKeyPath));
-
-  AppData app_data(StringToGuid(kFooGuid), is_machine_);
-  app_data.set_display_name(_T("Foo"));
-  app_data.set_language(_T("en"));
-  app_data.set_ap(_T("test_ap"));
-  app_data.set_tt_token(_T("test_tt_token"));
-  app_data.set_iid(expected_iid);
-  app_data.set_brand_code(_T("GOOG"));
-  app_data.set_client_id(_T("_some_partner"));
-
-  Job job(false, &ping_);
-  job.set_download_file_name(installer_full_path);
-  job.set_app_data(app_data);
-
-  EXPECT_SUCCEEDED(install_manager_->InstallJob(&job));
-
-  const CompletionInfo completion_info = install_manager_->error_info();
-  EXPECT_EQ(COMPLETION_SUCCESS, completion_info.status);
-  EXPECT_EQ(S_OK, completion_info.error_code);
-  EXPECT_STREQ(_T("Thanks for installing Foo."), completion_info.text);
-
-  EXPECT_TRUE(File::Exists(installer_log_full_path));
-
-  EXPECT_TRUE(RegKey::HasKey(kFullFooAppClientKeyPath));
-  EXPECT_FALSE(RegKey::HasKey(kFullFooAppClientStateKeyPath));
-  // Verify the value that is written based on an MSI property we didn't
-  // specify wasn't written.
-  EXPECT_FALSE(RegKey::HasValue(kFullFooAppClientKeyPath,
-                                kFooInstallerBarValueName));
-  EXPECT_SUCCEEDED(RegKey::DeleteKey(kFullFooAppClientKeyPath));
-
-  CString uninstall_arguments;
-  uninstall_arguments.Format(kMsiUninstallArguments, installer_full_path);
-  EXPECT_SUCCEEDED(System::ShellExecuteProcess(kMsiCommand,
-                                               uninstall_arguments,
-                                               NULL,
-                                               NULL));
-}
-
-TEST_F(InstallManagerMachineTest, InstallJob_MsiInstallerWithArgumentSucceeds) {
-  if (!vista_util::IsUserAdmin()) {
-    std::wcout << _T("\tTest did not run because the user is not an admin.")
-               << std::endl;
-    return;
-  }
-
-  // We can't fake the registry keys because we are interacting with a real
-  // installer.
-  RestoreRegistryHives();
-
-  CString installer_full_path(
-      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                      kSetupFooV1RelativeLocation));
-  ASSERT_TRUE(File::Exists(installer_full_path));
-
-  CString installer_log_full_path;
-  installer_log_full_path.Format(kMsiLogFormat, installer_full_path);
-
-  ASSERT_SUCCEEDED(File::Remove(installer_log_full_path));
-  ASSERT_FALSE(File::Exists(installer_log_full_path));
-
-  RegKey::DeleteKey(kFullFooAppClientKeyPath);
-  ASSERT_FALSE(RegKey::HasKey(kFullFooAppClientKeyPath));
-  RegKey::DeleteKey(kFullFooAppClientStateKeyPath);
-  ASSERT_FALSE(RegKey::HasKey(kFullFooAppClientStateKeyPath));
-
-  AppData app_data(StringToGuid(kFooGuid), is_machine_);
-  app_data.set_display_name(_T("Foo"));
-  app_data.set_language(_T("en"));
-  Job job(false, &ping_);
-  job.set_download_file_name(installer_full_path);
-  job.set_app_data(app_data);
-  SetUpdateResponseDataArguments(kFooInstallerBarPropertyArg, &job);
-
-  EXPECT_SUCCEEDED(install_manager_->InstallJob(&job));
-
-  const CompletionInfo completion_info = install_manager_->error_info();
-
-  EXPECT_EQ(COMPLETION_SUCCESS, completion_info.status);
-  EXPECT_EQ(S_OK, completion_info.error_code);
-  EXPECT_STREQ(_T("Thanks for installing Foo."), completion_info.text);
-
-  EXPECT_TRUE(File::Exists(installer_log_full_path));
-
-  EXPECT_TRUE(RegKey::HasKey(kFullFooAppClientKeyPath));
-  EXPECT_FALSE(RegKey::HasKey(kFullFooAppClientStateKeyPath));
-  EXPECT_TRUE(RegKey::HasValue(kFullFooAppClientKeyPath,
-                               kFooInstallerBarValueName));
-  DWORD barprop_value;
-  EXPECT_SUCCEEDED(RegKey::GetValue(kFullFooAppClientKeyPath,
-                                    kFooInstallerBarValueName,
-                                    &barprop_value));
-  EXPECT_EQ(7, barprop_value);
-
-  EXPECT_SUCCEEDED(RegKey::DeleteKey(kFullFooAppClientKeyPath));
-
-  CString uninstall_arguments;
-  uninstall_arguments.Format(kMsiUninstallArguments, installer_full_path);
-  EXPECT_SUCCEEDED(System::ShellExecuteProcess(kMsiCommand,
-                                               uninstall_arguments,
-                                               NULL,
-                                               NULL));
-}
-
-// The use of kGoogleUpdateAppId is the key to this test.
-// Note that the version is not changed - this is the normal self-update case.
-// Among other things, this test verifies that CheckApplicationRegistration() is
-// not called for self-updates.
-TEST_F(InstallManagerUserTest, InstallJob_UpdateOmahaSucceeds) {
-  CString arguments;
-  arguments.Format(kExecuteCommandAndTerminateSwitch, _T("echo hi"));
-
-  const CString kExistingVersion(_T("0.9.69.5"));
-
-  AppData app_data(kGoopdateGuid, is_machine_);
-  app_data.set_previous_version(kExistingVersion);
-  Job job(true, &ping_);
-  job.set_is_background(true);
-  job.set_download_file_name(kJobExecutable);
-  job.set_app_data(app_data);
-  SetUpdateResponseDataArguments(arguments, &job);
-
-  // Because we don't actually run the Omaha installer, we need to make sure
-  // its Clients key and pv value exist to avoid an error.
-  ASSERT_SUCCEEDED(RegKey::CreateKey(USER_REG_CLIENTS_GOOPDATE));
-  ASSERT_TRUE(RegKey::HasKey(USER_REG_CLIENTS_GOOPDATE));
-  ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENTS_GOOPDATE,
-                                    kRegValueProductVersion,
-                                    kExistingVersion));
-
-  EXPECT_SUCCEEDED(install_manager_->InstallJob(&job));
-
-  const CompletionInfo completion_info = install_manager_->error_info();
-
-  EXPECT_EQ(COMPLETION_SUCCESS, completion_info.status);
-  EXPECT_EQ(S_OK, completion_info.error_code);
-  // The user never sees this, but it is odd we put this text in the structure.
-  EXPECT_STREQ(_T("Thanks for installing ."), completion_info.text);
-
-  EXPECT_TRUE(RegKey::HasKey(USER_REG_CLIENTS_GOOPDATE));
-  CString version;
-  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_CLIENTS_GOOPDATE,
-                                    kRegValueProductVersion,
-                                    &version));
-  EXPECT_STREQ(kExistingVersion, version);
-}
-
-// The main purpose of this test is to ensure that self-updates don't fail if
-// Omaha's Clients key doesn't exist for some reason.
-// In other words, it tests that CheckApplicationRegistration() is not called.
-TEST_F(InstallManagerUserTest,
-       InstallJob_UpdateOmahaSucceedsWhenClientsKeyAbsent) {
-  CString arguments;
-  arguments.Format(kExecuteCommandAndTerminateSwitch, _T("echo hi"));
-
-  const CString kExistingVersion(_T("0.9.69.5"));
-
-  AppData app_data(kGoopdateGuid, is_machine_);
-  app_data.set_previous_version(kExistingVersion);
-  Job job(true, &ping_);
-  job.set_is_background(true);
-  job.set_download_file_name(kJobExecutable);
-  job.set_app_data(app_data);
-  SetUpdateResponseDataArguments(arguments, &job);
-
-  EXPECT_SUCCEEDED(install_manager_->InstallJob(&job));
-
-  EXPECT_FALSE(RegKey::HasKey(USER_REG_CLIENTS_GOOPDATE));
-}
-
-TEST_F(InstallManagerUserTest, InstallJob_InstallerDoesNotWriteClientsKey) {
-  CString arguments;
-  arguments.Format(kExecuteCommandAndTerminateSwitch, _T("echo hi"));
-
-  AppData app_data(StringToGuid(kJobGuid), is_machine_);
-  app_data.set_display_name(_T("Some App"));
-  Job job(false, &ping_);
-  job.set_download_file_name(kJobExecutable);
-  job.set_app_data(app_data);
-  SetUpdateResponseDataArguments(arguments, &job);
-
-  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENT_KEY,
-            install_manager_->InstallJob(&job));
-
-  EXPECT_FALSE(RegKey::HasKey(kFullJobAppClientsKeyPath));
-  EXPECT_FALSE(RegKey::HasKey(kFullJobAppClientStateKeyPath));
-
-  const CompletionInfo completion_info = install_manager_->error_info();
-  EXPECT_EQ(COMPLETION_ERROR, completion_info.status);
-  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_DID_NOT_WRITE_CLIENT_KEY,
-            completion_info.error_code);
-  EXPECT_STREQ(
-      _T("Installation failed. Please try again. Error code = 0x80040905"),
-      completion_info.text);}
-
-TEST_F(InstallManagerUserTest, InstallJob_InstallerFailureMsiFileDoesNotExist) {
-  const TCHAR kLogFileName[] = _T("foo.msi.log");
-  const TCHAR kExpectedErrorStringPartA[] =
-      _T("The installer encountered error 1619: ");
-  const int kExpectedErrorStringPartALength =
-      arraysize(kExpectedErrorStringPartA) - 1;
-
-  ASSERT_SUCCEEDED(File::Remove(kLogFileName));
-  ASSERT_FALSE(File::Exists(kLogFileName));
-
-  AppData app_data(StringToGuid(kJobGuid), is_machine_);
-  Job job(false, &ping_);
-  job.set_download_file_name(_T("foo.msi"));
-  job.set_app_data(app_data);
-
-  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED,
-            install_manager_->InstallJob(&job));
-
-  const CompletionInfo completion_info = install_manager_->error_info();
-
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_MSI, completion_info.status);
-  EXPECT_EQ(ERROR_INSTALL_PACKAGE_OPEN_FAILED, completion_info.error_code);
-  EXPECT_STREQ(kExpectedErrorStringPartA,
-            completion_info.text.Left(kExpectedErrorStringPartALength));
-  VerifyStringIsMsiPackageOpenFailedString(
-      completion_info.text.Mid(kExpectedErrorStringPartALength));
-
-  // msiexec creates an empty log file.
-  EXPECT_TRUE(File::Exists(kLogFileName));
-  EXPECT_SUCCEEDED(File::Remove(kLogFileName));
-
-  EXPECT_FALSE(RegKey::HasKey(kFullJobAppClientsKeyPath));
-  EXPECT_FALSE(RegKey::HasKey(kFullJobAppClientStateKeyPath));
-}
-
-//
-// GetInstallerResultHelper tests
-//
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_NoRegistry_MSI_ZeroExitCode) {
-  GetInstallerResultHelper(kJobGuid, kMsiInstaller, 0, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_SUCCESS, completion_info_.status);
-  EXPECT_EQ(0, completion_info_.error_code);
-  EXPECT_TRUE(completion_info_.text.IsEmpty());
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty());
-
-  VerifyNoLastRegistryValues(kJobGuid);
-}
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_NoRegistry_MSI_NonZeroExitCode) {
-  GetInstallerResultHelper(kJobGuid, kMsiInstaller, 1603, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_MSI, completion_info_.status);
-  EXPECT_EQ(1603, completion_info_.error_code);
-  EXPECT_STREQ(kError1603Text, completion_info_.text);
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty());
-
-  VerifyNoLastRegistryValues(kJobGuid);
-}
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_NoRegistry_EXE_ZeroExitCode) {
-  GetInstallerResultHelper(kJobGuid, kOtherInstaller, 0, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_SUCCESS, completion_info_.status);
-  EXPECT_EQ(0, completion_info_.error_code);
-  EXPECT_TRUE(completion_info_.text.IsEmpty());
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty());
-
-  VerifyNoLastRegistryValues(kJobGuid);
-}
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_NoRegistry_EXE_NonZeroExitCode_SmallNumber) {
-  GetInstallerResultHelper(kJobGuid, kOtherInstaller, 8, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_OTHER, completion_info_.status);
-  EXPECT_EQ(8, completion_info_.error_code);
-  EXPECT_STREQ(_T("The installer encountered error 8."), completion_info_.text);
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty());
-
-  VerifyNoLastRegistryValues(kJobGuid);
-}
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_NoRegistry_EXE_NonZeroExitCode_HRESULTFailure) {
-  GetInstallerResultHelper(
-      kJobGuid, kOtherInstaller, 0x80004005, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_OTHER, completion_info_.status);
-  EXPECT_EQ(0x80004005, completion_info_.error_code);
-  EXPECT_STREQ(_T("The installer encountered error 0x80004005."),
-               completion_info_.text);
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty());
-
-  VerifyNoLastRegistryValues(kJobGuid);
-}
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_ExitCode_MSI) {
-  SetupInstallerResultRegistry(kJobGuid,
-                               true, kResultExitCode,
-                               false, 0,
-                               false, _T(""),
-                               false, _T(""));
-
-  GetInstallerResultHelper(kJobGuid, kMsiInstaller, 1603, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_MSI, completion_info_.status);
-  EXPECT_EQ(1603, completion_info_.error_code);
-  EXPECT_STREQ(kError1603Text, completion_info_.text);
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty());
-
-  VerifyLastRegistryValues(kJobGuid,
-                           true, kResultExitCode,
-                           false, 0,
-                           false, _T(""),
-                           false, _T(""));
-}
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_NoRegistry_MSI_RebootRequired) {
-  GetInstallerResultHelper(kJobGuid,
-                           kMsiInstaller,
-                           ERROR_SUCCESS_REBOOT_REQUIRED,
-                           &completion_info_);
-
-  EXPECT_EQ(COMPLETION_SUCCESS_REBOOT_REQUIRED, completion_info_.status);
-  EXPECT_EQ(0, completion_info_.error_code);
-  EXPECT_TRUE(completion_info_.text.IsEmpty());
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty());
-
-  VerifyNoLastRegistryValues(kJobGuid);
-}
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_SystemError_EXE_RebootRequired) {
-  SetupInstallerResultRegistry(kJobGuid,
-                               true, kResultFailedSystemError,
-                               true, ERROR_SUCCESS_REBOOT_REQUIRED,
-                               false, _T(""),
-                               true, kLaunchCmdLine);
-
-  GetInstallerResultHelper(kJobGuid, kOtherInstaller, 1, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_SUCCESS_REBOOT_REQUIRED, completion_info_.status);
-  EXPECT_EQ(0, completion_info_.error_code);
-  EXPECT_TRUE(completion_info_.text.IsEmpty());
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty()) <<
-      _T("Command line is not supported with reboot.");
-
-  VerifyLastRegistryValues(kJobGuid,
-                           true, kResultFailedSystemError,
-                           true, ERROR_SUCCESS_REBOOT_REQUIRED,
-                           false, _T(""),
-                           true, kLaunchCmdLine);
-}
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_Success_NoErrorCode) {
-  SetupInstallerResultRegistry(kJobGuid,
-                               true, kResultSuccess,
-                               false, 0,
-                               false, _T(""),
-                               false, _T(""));
-
-  GetInstallerResultHelper(kJobGuid, kOtherInstaller, 99, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_SUCCESS, completion_info_.status);
-  EXPECT_EQ(99, completion_info_.error_code);
-  EXPECT_TRUE(completion_info_.text.IsEmpty());
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty());
-
-  VerifyLastRegistryValues(kJobGuid,
-                           true, kResultSuccess,
-                           false, 0,
-                           false, _T(""),
-                           false, _T(""));
-}
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_Success_AllValues) {
-  SetupInstallerResultRegistry(kJobGuid,
-                               true, kResultSuccess,
-                               true, 555,
-                               true, _T("an ignored error"),
-                               true, kLaunchCmdLine);
-
-  GetInstallerResultHelper(kJobGuid, kOtherInstaller, 99, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_SUCCESS, completion_info_.status);
-  EXPECT_EQ(555, completion_info_.error_code) <<
-      _T("InstallerError overwrites exit code.");
-  EXPECT_TRUE(completion_info_.text.IsEmpty()) <<
-      _T("UIString is ignored for Success.");
-  EXPECT_STREQ(kLaunchCmdLine, job_->launch_cmd_line());
-
-  VerifyLastRegistryValues(kJobGuid,
-                           true, kResultSuccess,
-                           true, 555,
-                           true, _T("an ignored error"),
-                           true, kLaunchCmdLine);
-}
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_LaunchCmdOnly_MSI_ZeroExitCode) {
-  SetupInstallerResultRegistry(kJobGuid,
-                               false, 0,
-                               false, 0,
-                               false, _T(""),
-                               true, kLaunchCmdLine);
-
-  GetInstallerResultHelper(kJobGuid, kMsiInstaller, 0, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_SUCCESS, completion_info_.status);
-  EXPECT_EQ(0, completion_info_.error_code);
-  EXPECT_TRUE(completion_info_.text.IsEmpty());
-  EXPECT_STREQ(kLaunchCmdLine, job_->launch_cmd_line());
-
-  VerifyLastRegistryValues(kJobGuid,
-                           false, 0,
-                           false, 0,
-                           false, _T(""),
-                           true, kLaunchCmdLine);
-}
-
-// Exit code is used when no error code is present. It's interpreted as a system
-// error even though the installer is not an MSI.
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_Failed_NoErrorCodeOrUiString) {
-  SetupInstallerResultRegistry(kJobGuid,
-                               true, kResultFailedCustomError,
-                               false, 0,
-                               false, _T(""),
-                               false, _T(""));
-
-  GetInstallerResultHelper(kJobGuid, kOtherInstaller, 8, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_OTHER, completion_info_.status);
-  EXPECT_EQ(8, completion_info_.error_code);
-  EXPECT_STREQ(_T("The installer encountered error 8."), completion_info_.text);
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty());
-
-  VerifyLastRegistryValues(kJobGuid,
-                           true, kResultFailedCustomError,
-                           false, 0,
-                           false, _T(""),
-                           false, _T(""));
-}
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_Failed_WithErrorCode) {
-  SetupInstallerResultRegistry(kJobGuid,
-                               true, kResultFailedCustomError,
-                               true, 8,
-                               false, _T(""),
-                               false, _T(""));
-
-  GetInstallerResultHelper(kJobGuid, kOtherInstaller, 1618, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_OTHER, completion_info_.status);
-  EXPECT_EQ(8, completion_info_.error_code);
-  EXPECT_STREQ(_T("The installer encountered error 8."), completion_info_.text);
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty());
-
-  VerifyLastRegistryValues(kJobGuid,
-                           true, kResultFailedCustomError,
-                           true, 8,
-                           false, _T(""),
-                           false, _T(""));
-}
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_Failed_AllValues) {
-  const DWORD kInstallerErrorValue = 8;
-  const TCHAR* const kUiString = _T("a message from the installer");
-
-  SetupInstallerResultRegistry(kJobGuid,
-                               true, kResultFailedCustomError,
-                               true, kInstallerErrorValue,
-                               true, kUiString,
-                               true, kLaunchCmdLine);
-
-  GetInstallerResultHelper(kJobGuid, kOtherInstaller, 1618, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_OTHER, completion_info_.status);
-  EXPECT_EQ(kInstallerErrorValue, completion_info_.error_code);
-  EXPECT_STREQ(kUiString, completion_info_.text);
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty()) <<
-      _T("Command line is not supported with errors.");
-
-  VerifyLastRegistryValues(kJobGuid,
-                           true, kResultFailedCustomError,
-                           true, kInstallerErrorValue,
-                           true, kUiString,
-                           true, kLaunchCmdLine);
-}
-
-// Exit code is used and interpreted as MSI error when no error code present.
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_FailedMsiError_NoErrorCode) {
-  SetupInstallerResultRegistry(kJobGuid,
-                               true, kResultFailedMsiError,
-                               false, 0,
-                               false, _T(""),
-                               false, _T(""));
-
-  GetInstallerResultHelper(kJobGuid, kOtherInstaller, 1603, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_MSI, completion_info_.status);
-  EXPECT_EQ(1603, completion_info_.error_code);
-  EXPECT_STREQ(kError1603Text, completion_info_.text);
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty());
-
-  VerifyLastRegistryValues(kJobGuid,
-                           true, kResultFailedMsiError,
-                           false, 0,
-                           false, _T(""),
-                           false, _T(""));
-}
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_FailedMsiError_WithErrorCode) {
-  const DWORD kInstallerErrorValue = 1603;
-
-  SetupInstallerResultRegistry(kJobGuid,
-                               true, kResultFailedMsiError,
-                               true, kInstallerErrorValue,
-                               false, _T(""),
-                               false, _T(""));
-
-  GetInstallerResultHelper(kJobGuid, kOtherInstaller, 1618, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_MSI, completion_info_.status);
-  EXPECT_EQ(kInstallerErrorValue, completion_info_.error_code);
-  EXPECT_STREQ(kError1603Text, completion_info_.text);
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty());
-
-  VerifyLastRegistryValues(kJobGuid,
-                           true, kResultFailedMsiError,
-                           true, kInstallerErrorValue,
-                           false, _T(""),
-                           false, _T(""));
-}
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_FailedMsiError_AllValues) {
-  const DWORD kInstallerErrorValue = 1603;
-
-  SetupInstallerResultRegistry(kJobGuid,
-                               true, kResultFailedMsiError,
-                               true, kInstallerErrorValue,
-                               true, _T("an ignored error"),
-                               true, kLaunchCmdLine);
-
-  GetInstallerResultHelper(kJobGuid, kOtherInstaller, 1618, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_MSI, completion_info_.status);
-  EXPECT_EQ(kInstallerErrorValue, completion_info_.error_code);
-  EXPECT_STREQ(kError1603Text, completion_info_.text) <<
-      _T("UIString is ignored.");
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty()) <<
-      _T("Command line is not supported with errors.");
-
-  VerifyLastRegistryValues(kJobGuid,
-                           true, kResultFailedMsiError,
-                           true, kInstallerErrorValue,
-                           true, _T("an ignored error"),
-                           true, kLaunchCmdLine);
-}
-
-// Exit code is used and interpreted as system error when no error code present.
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_FailedSystemError_NoErrorCode) {
-  SetupInstallerResultRegistry(kJobGuid,
-                               true, kResultFailedSystemError,
-                               false, 0,
-                               false, _T(""),
-                               false, _T(""));
-
-  GetInstallerResultHelper(kJobGuid, kOtherInstaller, 1603, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_SYSTEM, completion_info_.status);
-  EXPECT_EQ(1603, completion_info_.error_code);
-  EXPECT_STREQ(kError1603Text, completion_info_.text);
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty());
-
-  VerifyLastRegistryValues(kJobGuid,
-                           true, kResultFailedSystemError,
-                           false, 0,
-                           false, _T(""),
-                           false, _T(""));
-}
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_FailedSystemError_WithErrorCode) {
-  const DWORD kInstallerErrorValue = 1603;
-
-  SetupInstallerResultRegistry(kJobGuid,
-                               true, kResultFailedSystemError,
-                               true, kInstallerErrorValue,
-                               false, _T(""),
-                               false, _T(""));
-
-  GetInstallerResultHelper(kJobGuid, kOtherInstaller, 1618, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_SYSTEM, completion_info_.status);
-  EXPECT_EQ(kInstallerErrorValue, completion_info_.error_code);
-  EXPECT_STREQ(kError1603Text, completion_info_.text);
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty());
-
-  VerifyLastRegistryValues(kJobGuid,
-                           true, kResultFailedSystemError,
-                           true, kInstallerErrorValue,
-                           false, _T(""),
-                           false, _T(""));
-}
-
-// INSTALLER_RESULT_FAILED_SYSTEM_ERROR supports values beyond the basic
-// "System Error Codes" and their HRESULT equivalents.
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_FailedSystemError_WithHRESULTSystemError) {
-  const DWORD kInstallerErrorValue = 0x800B010F;
-
-  SetupInstallerResultRegistry(kJobGuid,
-                               true, kResultFailedSystemError,
-                               true, kInstallerErrorValue,
-                               false, _T(""),
-                               false, _T(""));
-
-  GetInstallerResultHelper(kJobGuid, kOtherInstaller, 1618, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_SYSTEM, completion_info_.status);
-  EXPECT_EQ(kInstallerErrorValue, completion_info_.error_code);
-  EXPECT_STREQ(kError0x800B010FText, completion_info_.text);
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty());
-
-  VerifyLastRegistryValues(kJobGuid,
-                           true, kResultFailedSystemError,
-                           true, kInstallerErrorValue,
-                           false, _T(""),
-                           false, _T(""));
-}
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_FailedSystemError_WithUnrecognizedError) {
-  const DWORD kInstallerErrorValue = 0x80040200;
-
-  SetupInstallerResultRegistry(kJobGuid,
-                               true, kResultFailedSystemError,
-                               true, kInstallerErrorValue,
-                               false, _T(""),
-                               false, _T(""));
-
-  // GetMessageForSystemErrorCode expects a valid system error.
-  ExpectAsserts expect_asserts;
-
-  GetInstallerResultHelper(kJobGuid, kOtherInstaller, 1618, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_SYSTEM, completion_info_.status);
-  EXPECT_EQ(kInstallerErrorValue, completion_info_.error_code);
-  EXPECT_STREQ(_T("The installer encountered error 0x80040200."),
-               completion_info_.text);
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty());
-
-  VerifyLastRegistryValues(kJobGuid,
-                           true, kResultFailedSystemError,
-                           true, kInstallerErrorValue,
-                           false, _T(""),
-                           false, _T(""));
-}
-
-TEST_F(InstallManagerUserGetInstallerResultHelperTest,
-       GetInstallerResultHelper_FailedSystemError_AllValues) {
-  const DWORD kInstallerErrorValue = 1603;
-
-  SetupInstallerResultRegistry(kJobGuid,
-                               true, kResultFailedSystemError,
-                               true, kInstallerErrorValue,
-                               true, _T("an ignored error"),
-                               true, kLaunchCmdLine);
-
-  GetInstallerResultHelper(kJobGuid, kOtherInstaller, 1618, &completion_info_);
-
-  EXPECT_EQ(COMPLETION_INSTALLER_ERROR_SYSTEM, completion_info_.status);
-  EXPECT_EQ(kInstallerErrorValue, completion_info_.error_code);
-  EXPECT_STREQ(kError1603Text, completion_info_.text) <<
-      _T("UIString is ignored.");
-  EXPECT_TRUE(job_->launch_cmd_line().IsEmpty()) <<
-      _T("Command line is not supported with errors.");
-
-  VerifyLastRegistryValues(kJobGuid,
-                           true, kResultFailedSystemError,
-                           true, kInstallerErrorValue,
-                           true, _T("an ignored error"),
-                           true, kLaunchCmdLine);
-}
-
-// TODO(omaha): Add a machine test.
-
-}  // namespace omaha
-
diff --git a/worker/job.cc b/worker/job.cc
deleted file mode 100644
index eb74c0e..0000000
--- a/worker/job.cc
+++ /dev/null
@@ -1,677 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/worker/job.h"
-
-#include <windows.h>
-#include <winhttp.h>
-#include <vector>
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/path.h"
-#include "omaha/common/sta_call.h"
-#include "omaha/common/time.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/resource.h"
-#include "omaha/goopdate/update_response_data.h"
-#include "omaha/worker/application_data.h"
-#include "omaha/worker/application_manager.h"
-#include "omaha/worker/download_manager.h"
-#include "omaha/worker/install_manager.h"
-#include "omaha/worker/job_observer.h"
-#include "omaha/worker/ping.h"
-#include "omaha/worker/ping_utils.h"
-#include "omaha/worker/worker_event_logger.h"
-#include "omaha/worker/worker_metrics.h"
-
-namespace omaha {
-
-// The caller retains ownership of ping. Delete this object before deleting
-// the Ping.
-Job::Job(bool is_update, Ping* ping)
-    : extra_code1_(0),
-      job_state_(JOBSTATE_START),
-      job_observer_(NULL),
-      user_explorer_pid_(0),
-      bytes_downloaded_(0),
-      bytes_total_(0),
-      ping_(ping),
-      is_update_(is_update),
-      is_offline_(false),
-      is_background_(false),
-      is_update_check_only_(false),
-      did_launch_cmd_fail_(false) {
-  CORE_LOG(L1, (_T("[Job::Job]")));
-}
-
-Job::~Job() {
-  CORE_LOG(L1, (_T("[Job::~Job][%s]"), GuidToString(app_data_.app_guid())));
-}
-
-void Job::ChangeState(JobState state, bool send_ping) {
-  CORE_LOG(L1, (_T("[Job::ChangeState moving job from %d to %d.]"),
-                job_state_, state));
-  const JobState previous_state = job_state_;
-  job_state_ = state;
-
-  // Ignore failures.
-  if (send_ping && job_state_ != JOBSTATE_START) {
-    SendStateChangePing(previous_state);
-  }
-  VERIFY1(SUCCEEDED(NotifyUI()));
-}
-
-void Job::NotifyDownloadStarted() {
-  ASSERT1(job_state_ == JOBSTATE_START);
-  ChangeState(JOBSTATE_DOWNLOADSTARTED, true);
-}
-
-void Job::NotifyDownloadComplete() {
-  ASSERT1(job_state_ == JOBSTATE_DOWNLOADSTARTED);
-  ChangeState(JOBSTATE_DOWNLOADCOMPLETED, true);
-}
-
-void Job::NotifyInstallStarted() {
-  ChangeState(JOBSTATE_INSTALLERSTARTED, true);
-}
-
-void Job::NotifyCompleted(const CompletionInfo& info) {
-  info_ = info;
-
-  if (!is_update_check_only_) {
-    WriteJobCompletedEvent(app_data().is_machine_app(), *this);
-  }
-
-  bool is_successful_self_update =
-      is_update_ &&
-      IsCompletionSuccess(info_) &&
-      ::IsEqualGUID(kGoopdateGuid, app_data().app_guid());
-  if (is_successful_self_update) {
-    CORE_LOG(L2, (_T("[self-update successfully invoked; not sending ping]")));
-  }
-
-  // Always send a ping unless:
-  // - the state is successful self-update completed, or
-  // - this is an update check only job.
-  // The exception exists because in the first case we do not know the actual
-  // outcome of the job. The ping will be sent by the installer when setup
-  // completes the self-update.
-  // In the second case, update check only jobs do not actually complete.
-  // They are used as containers for the update check response and they
-  // are not downloaded nor installed.
-  bool send_ping = !(is_successful_self_update || is_update_check_only_);
-  ChangeState(JOBSTATE_COMPLETED, send_ping);
-}
-
-void Job::OnProgress(int bytes, int bytes_total,
-                     int status, const TCHAR* status_text) {
-  UNREFERENCED_PARAMETER(status);
-  UNREFERENCED_PARAMETER(status_text);
-  ASSERT1(status == WINHTTP_CALLBACK_STATUS_READ_COMPLETE ||
-          status == WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER);
-  ASSERT1(job_state_ == JOBSTATE_DOWNLOADSTARTED);
-  bytes_downloaded_ = bytes;
-  bytes_total_ = bytes_total;
-  VERIFY1(SUCCEEDED(NotifyUI()));
-}
-
-HRESULT Job::NotifyUI() {
-  if (!job_observer_) {
-    return S_OK;
-  }
-
-  switch (job_state_) {
-    case JOBSTATE_DOWNLOADSTARTED: {
-      int pos = 0;
-      if (bytes_total_) {
-        pos = static_cast<int>(
-            (static_cast<double>(bytes_downloaded_) / bytes_total_) * 100);
-      }
-      job_observer_->OnDownloading(0, pos);
-      break;
-    }
-    case JOBSTATE_DOWNLOADCOMPLETED:
-      job_observer_->OnWaitingToInstall();
-      break;
-    case JOBSTATE_INSTALLERSTARTED:
-      job_observer_->OnInstalling();
-      break;
-    case JOBSTATE_COMPLETED:
-      return DoCompleteJob();
-    case JOBSTATE_START:
-    default:
-      ASSERT1(false);
-      return E_UNEXPECTED;
-  }
-
-  return S_OK;
-}
-
-HRESULT Job::ShouldRestartBrowser() const {
-  return !update_response_data_.success_url().IsEmpty();
-}
-
-HRESULT Job::DeleteJobDownloadDirectory() const {
-  CORE_LOG(L3, (_T("[DeleteJobDownloadDirectory]")));
-  ASSERT1(IsCompletionSuccess(info_));
-
-  // Do no delete the downloaded file if the installer is omaha.
-  if (::IsEqualGUID(app_data_.app_guid(), kGoopdateGuid)) {
-    CORE_LOG(L3, (_T("[Not deleting goopdate self update.]")));
-    return S_OK;
-  }
-
-  // In case of ondemand updates checks, it is possible that we do not have
-  // a downloaded file.
-  if (download_file_name_.IsEmpty() &&
-      is_update_check_only_) {
-    return S_OK;
-  }
-
-  ASSERT1(!download_file_name_.IsEmpty());
-  ASSERT1(!File::IsDirectory(download_file_name_));
-  CString path = GetDirectoryFromPath(download_file_name_);
-  HRESULT hr = DeleteDirectory(path);
-  if (FAILED(hr)) {
-    CORE_LOG(L3, (_T("[Download file directory delete failed.][%s][0x%08x]"),
-                  path, hr));
-  }
-  return hr;
-}
-
-// The SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD case assumes that
-// the launch command was run and did_launch_cmd_fail_ set correctly if
-// launch_cmd_line_ is not empty.
-CompletionCodes Job::CompletionStatusToCompletionCode(
-    JobCompletionStatus status) const {
-  CompletionCodes code(COMPLETION_CODE_ERROR);
-  switch (status) {
-    case COMPLETION_SUCCESS: {
-      if ((update_response_data_.success_action() ==
-           SUCCESS_ACTION_EXIT_SILENTLY) ||
-          (update_response_data_.success_action() ==
-           SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD &&
-           !launch_cmd_line_.IsEmpty())) {
-        // If launch failed, display the success UI anyway.
-        code = did_launch_cmd_fail_ ? COMPLETION_CODE_SUCCESS :
-                                      COMPLETION_CODE_SUCCESS_CLOSE_UI;
-      } else if (ShouldRestartBrowser()) {
-        if (app_data_.browser_type() == BROWSER_UNKNOWN ||
-            app_data_.browser_type() == BROWSER_DEFAULT ||
-            app_data_.browser_type() > BROWSER_MAX) {
-          code = update_response_data_.terminate_all_browsers() ?
-                     COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY :
-                     COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY;
-        } else {
-          code = update_response_data_.terminate_all_browsers() ?
-                     COMPLETION_CODE_RESTART_ALL_BROWSERS :
-                     COMPLETION_CODE_RESTART_BROWSER;
-        }
-      } else {
-        code = COMPLETION_CODE_SUCCESS;
-      }
-      break;
-    }
-    case COMPLETION_SUCCESS_REBOOT_REQUIRED:
-      code = COMPLETION_CODE_REBOOT_NOTICE_ONLY;
-      break;
-    case COMPLETION_ERROR:
-    case COMPLETION_INSTALLER_ERROR_MSI:
-    case COMPLETION_INSTALLER_ERROR_SYSTEM:
-    case COMPLETION_INSTALLER_ERROR_OTHER:
-      code = COMPLETION_CODE_ERROR;
-      break;
-    case COMPLETION_CANCELLED:
-      code = COMPLETION_CODE_ERROR;
-      break;
-    default:
-      ASSERT1(false);
-  }
-
-  return code;
-}
-
-HRESULT Job::DoCompleteJob() {
-  CompletionCodes completion_code =
-      CompletionStatusToCompletionCode(info_.status);
-
-  if (IsCompletionSuccess(info_)) {
-    VERIFY1(SUCCEEDED(DeleteJobDownloadDirectory()));
-  }
-
-  OPT_LOG(L1, (_T("[job completed]")
-               _T("[status %d][completion code %d][error 0x%08x][text \"%s\"]"),
-               info_.status, completion_code, info_.error_code, info_.text));
-
-  if (job_observer_) {
-    job_observer_->OnComplete(completion_code,
-                              info_.text,
-                              info_.error_code);
-  }
-
-  return S_OK;
-}
-
-HRESULT Job::SendStateChangePing(JobState previous_state) {
-  Request request(app_data().is_machine_app());
-
-  // TODO(omaha): Will need to update this to determine if this is a product or
-  // component level job and create the ProductAppData appropriately.
-
-  AppRequestData app_request_data(app_data());
-
-  PingEvent::Types event_type = JobStateToEventType();
-  PingEvent::Results event_result =
-      ping_utils::CompletionStatusToPingEventResult(info().status);
-  // TODO(omaha): Remove this value when circular log buffer is implemented.
-  // If extra_code1 is not already used and there is an error, specify the state
-  // in which the error occurred in extra_code1.
-  const int extra_code1 = (!extra_code1_ && info().error_code) ?
-                              kJobStateExtraCodeMask | previous_state :
-                              extra_code1_;
-  PingEvent ping_event(event_type,
-                       event_result,
-                       info().error_code,
-                       extra_code1,
-                       app_data().previous_version());
-  app_request_data.AddPingEvent(ping_event);
-
-  AppRequest app_request(app_request_data);
-  request.AddAppRequest(app_request);
-
-  // Clear the extra code for the next state/ping.
-  extra_code1_ = 0;
-
-  if (event_result == COMPLETION_CANCELLED) {
-    // This ping is sending the last ping after the cancel it cannot
-    // be canceled.
-    Ping cancel_ping;
-    return cancel_ping.SendPing(&request);
-  } else {
-    ASSERT1(ping_);
-    return ping_->SendPing(&request);
-  }
-}
-
-PingEvent::Types Job::JobStateToEventType() const {
-  PingEvent::Types type = PingEvent::EVENT_UNKNOWN;
-  switch (job_state_) {
-    case JOBSTATE_START:
-      break;
-    case JOBSTATE_DOWNLOADSTARTED:
-      type = is_update_ ? PingEvent::EVENT_UPDATE_DOWNLOAD_START :
-                          PingEvent::EVENT_INSTALL_DOWNLOAD_START;
-      break;
-    case JOBSTATE_DOWNLOADCOMPLETED:
-      type = is_update_ ? PingEvent::EVENT_UPDATE_DOWNLOAD_FINISH :
-                          PingEvent::EVENT_INSTALL_DOWNLOAD_FINISH;
-      break;
-    case JOBSTATE_INSTALLERSTARTED:
-      type = is_update_ ? PingEvent::EVENT_UPDATE_INSTALLER_START :
-                          PingEvent::EVENT_INSTALL_INSTALLER_START;
-      break;
-    case JOBSTATE_COMPLETED:
-      type = is_update_ ? PingEvent::EVENT_UPDATE_COMPLETE :
-                          PingEvent::EVENT_INSTALL_COMPLETE;
-      break;
-    default:
-      break;
-  }
-  ASSERT1(PingEvent::EVENT_UNKNOWN != type);
-  return type;
-}
-
-void Job::RestartBrowsers() {
-  // CompletionStatusToCompletionCode does not set a restart completion code
-  // if the browser type is not a specific browser, so this method should never
-  // be called in those cases.
-  ASSERT1(app_data_.browser_type() != BROWSER_UNKNOWN &&
-          app_data_.browser_type() != BROWSER_DEFAULT &&
-          app_data_.browser_type() < BROWSER_MAX);
-
-  CORE_LOG(L3, (_T("[Job::RestartBrowsers]")));
-  TerminateBrowserResult browser_res;
-  TerminateBrowserResult default_res;
-  if (update_response_data_.terminate_all_browsers()) {
-    goopdate_utils::TerminateAllBrowsers(app_data_.browser_type(),
-                                         &browser_res,
-                                         &default_res);
-  } else {
-    goopdate_utils::TerminateBrowserProcesses(app_data_.browser_type(),
-                                              &browser_res,
-                                              &default_res);
-  }
-
-  BrowserType default_type = BROWSER_UNKNOWN;
-  HRESULT hr = GetDefaultBrowserType(&default_type);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[GetDefaultBrowserType failed][0x%08x]"), hr));
-    return;
-  }
-
-  BrowserType browser_type = BROWSER_UNKNOWN;
-  if (!goopdate_utils::GetBrowserToRestart(app_data_.browser_type(),
-                                           default_type,
-                                           browser_res,
-                                           default_res,
-                                           &browser_type)) {
-    CORE_LOG(LE, (_T("[GetBrowserToRestart returned false. Not launching.]")));
-    return;
-  }
-
-  ASSERT1(BROWSER_UNKNOWN != browser_type);
-  VERIFY1(SUCCEEDED(goopdate_utils::LaunchBrowser(
-      browser_type,
-      update_response_data_.success_url())));
-}
-
-void Job::LaunchBrowser(const CString& url) {
-  CORE_LOG(L3, (_T("[Job::LaunchBrowser]")));
-  BrowserType browser_type =
-      app_data_.browser_type() == BROWSER_UNKNOWN ?
-                                  BROWSER_DEFAULT :
-                                  app_data_.browser_type();
-  VERIFY1(SUCCEEDED(goopdate_utils::LaunchBrowser(browser_type, url)));
-}
-
-void Job::set_app_data(const AppData& app_data) {
-  app_data_ = app_data;
-}
-
-// On Vista with UAC on for an interactive install, LaunchCmdLine will run the
-// command line at medium integrity even if the installer was running at high
-// integrity.
-HRESULT Job::LaunchCmdLine() {
-  CORE_LOG(L3, (_T("[Job::LaunchCmdLine][%s]"), launch_cmd_line_));
-  ASSERT1(!is_update_);
-
-  if (launch_cmd_line_.IsEmpty()) {
-    return S_OK;
-  }
-
-  // InstallerSuccessLaunchCmdLine should not be set if the install failed.
-  ASSERT1(IsCompletionSuccess(info_));
-
-  HRESULT hr = goopdate_utils::LaunchCmdLine(launch_cmd_line_);
-  if (FAILED(hr)) {
-    OPT_LOG(LE, (_T("[goopdate_utils::LaunchCmdLine failed][0x%x]"), hr));
-    did_launch_cmd_fail_ = true;
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT Job::Download(DownloadManager* dl_manager) {
-  CORE_LOG(L2, (_T("[Job::DownloadJob][%s]"),
-                GuidToString(app_data_.app_guid())));
-  ASSERT1(dl_manager);
-
-  NotifyDownloadStarted();
-  HRESULT hr = dl_manager->DownloadFile(this);
-  if (FAILED(hr)) {
-    CORE_LOG(LW, (_T("[Job::DownloadJob DownloadFile failed][0x%08x][%s]"),
-                  hr, GuidToString(app_data_.app_guid())));
-    CompletionInfo info = dl_manager->error_info();
-    if (!IsCompletionSuccess(info)) {
-      NotifyCompleted(info);
-    }
-    return hr;
-  }
-
-  NotifyDownloadComplete();
-  return S_OK;
-}
-
-HRESULT Job::Install() {
-  CORE_LOG(L2, (_T("[Job::InstallJob]")));
-  NotifyInstallStarted();
-
-  CompletionInfo completion_info;
-  AppData new_app_data;
-  HRESULT hr = DoInstall(&completion_info, &new_app_data);
-  // Do not return until after NotifyCompleted() has been called.
-
-  NotifyCompleted(completion_info);
-  app_data_ = new_app_data;
-
-  return hr;
-}
-
-HRESULT Job::DoInstall(CompletionInfo* completion_info,
-                       AppData* new_app_data) {
-  CORE_LOG(L3, (_T("[Job::DoInstall]")));
-  ASSERT1(completion_info);
-  ASSERT1(new_app_data);
-
-  if (!is_update_) {
-    AppManager app_manager(app_data_.is_machine_app());
-    HRESULT hr = app_manager.WritePreInstallData(app_data_);
-    if (FAILED(hr)) {
-      CORE_LOG(LE, (_T("[AppManager::WritePreInstallData failed][0x%08x][%s]"),
-                    hr, GuidToString(app_data_.app_guid())));
-      completion_info->status = COMPLETION_ERROR;
-      completion_info->error_code = hr;
-      completion_info->text.FormatMessage(IDS_INSTALL_FAILED, hr);
-      return hr;
-    }
-  }
-
-  InstallManager install_manager(app_data_.is_machine_app());
-  AppManager app_manager(app_data_.is_machine_app());
-
-  HRESULT hr = install_manager.InstallJob(this);
-  *completion_info = install_manager.error_info();
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[InstallManager::InstallJob failed][0x%08x][%s]"),
-                  hr, GuidToString(app_data_.app_guid())));
-
-    // If we failed the install job and the product wasn't registered, it's safe
-    // to delete the ClientState key.  We need to remove it because it contains
-    // data like "ap", browsertype, language, etc. that need to be cleaned up in
-    // case user tries to install again in the future.
-    if (!is_update_ && !app_manager.IsProductRegistered(app_data_.app_guid())) {
-      // Need to set is_uninstalled to true or else we'll assert in
-      // RemoveClientState().
-      app_data_.set_is_uninstalled(true);
-      app_manager.RemoveClientState(app_data_);
-    }
-
-    return hr;
-  }
-
-  hr = UpdateJob();
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[UpdateJob failed][0x%08x][%s]"),
-                  hr, GuidToString(app_data_.app_guid())));
-    completion_info->status = COMPLETION_ERROR;
-    completion_info->error_code = hr;
-    completion_info->text.FormatMessage(IDS_INSTALL_FAILED, hr);
-    return hr;
-  }
-
-  hr = UpdateRegistry(new_app_data);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[UpdateRegistry failed][0x%08x][%s]"),
-                  hr, GuidToString(app_data_.app_guid())));
-    completion_info->status = COMPLETION_ERROR;
-    completion_info->error_code = hr;
-    completion_info->text.FormatMessage(IDS_INSTALL_FAILED, hr);
-    return hr;
-  }
-
-  // We do not know whether Goopdate has succeeded because its installer has
-  // not completed.
-  if (!::IsEqualGUID(kGoopdateGuid, app_data_.app_guid())) {
-    app_manager.RecordSuccessfulInstall(app_data_.parent_app_guid(),
-                                        app_data_.app_guid(),
-                                        is_update_,
-                                        is_offline_);
-  }
-
-  return S_OK;
-}
-
-// Update the version and the language in the job using the values written by
-// the installer.
-HRESULT Job::UpdateJob() {
-  CORE_LOG(L2, (_T("[Job::UpdateJob]")));
-  AppManager app_manager(app_data_.is_machine_app());
-  AppData data;
-  HRESULT hr = app_manager.ReadAppDataFromStore(app_data_.parent_app_guid(),
-                                                app_data_.app_guid(),
-                                                &data);
-  ASSERT1(SUCCEEDED(hr));
-  if (SUCCEEDED(hr)) {
-    app_data_.set_version(data.version());
-    app_data_.set_language(data.language());
-  } else {
-    CORE_LOG(LW, (_T("[ReadApplicationData failed][0x%08x][%s]"),
-                  hr, GuidToString(app_data_.app_guid())));
-    // Continue without the data from the registry.
-  }
-
-  return S_OK;
-}
-
-// Update the registry with the information from the job.
-HRESULT Job::UpdateRegistry(AppData* new_app_data) {
-  CORE_LOG(L2, (_T("[Job::UpdateRegistry]")));
-  ASSERT1(new_app_data);
-
-  *new_app_data = app_data_;
-  AppManager app_manager(app_data_.is_machine_app());
-  // Update the client registry information and the client state with the
-  // information in this job.
-  HRESULT hr = app_manager.InitializeApplicationState(new_app_data);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[WriteAppParamsToRegistry failed][0x%08x][%s]"),
-                  hr, GuidToString(new_app_data->app_guid())));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT Job::GetInstallerData(CString* installer_data) const {
-  ASSERT1(installer_data);
-  installer_data->Empty();
-
-  if (!app_data().encoded_installer_data().IsEmpty()) {
-    CString decoded_installer_data;
-    HRESULT hr = Utf8UrlEncodedStringToWideString(
-                     app_data().encoded_installer_data(),
-                     &decoded_installer_data);
-    ASSERT(SUCCEEDED(hr), (_T("[Utf8UrlEncodedStringToWideString][0x%x]"), hr));
-
-    if (FAILED(hr) || CString(decoded_installer_data).Trim().IsEmpty()) {
-      return GOOPDATE_E_INVALID_INSTALLER_DATA_IN_APPARGS;
-    }
-    *installer_data = decoded_installer_data;
-    return S_OK;
-  }
-
-  if (app_data().install_data_index().IsEmpty()) {
-    return S_OK;
-  }
-
-  CString data(response_data().GetInstallData(app_data().install_data_index()));
-  if (CString(data).Trim().IsEmpty()) {
-    return GOOPDATE_E_INVALID_INSTALL_DATA_INDEX;
-  }
-
-  *installer_data = data;
-  return S_OK;
-}
-
-bool IsCompletionStatusSuccess(JobCompletionStatus status) {
-  switch (status) {
-    case COMPLETION_SUCCESS:
-    case COMPLETION_SUCCESS_REBOOT_REQUIRED:
-      return true;
-    case COMPLETION_ERROR:
-    case COMPLETION_INSTALLER_ERROR_MSI:
-    case COMPLETION_INSTALLER_ERROR_SYSTEM:
-    case COMPLETION_INSTALLER_ERROR_OTHER:
-    case COMPLETION_CANCELLED:
-    default:
-      return false;
-  }
-}
-
-bool IsCompletionStatusInstallerError(JobCompletionStatus status) {
-  switch (status) {
-    case COMPLETION_INSTALLER_ERROR_MSI:
-    case COMPLETION_INSTALLER_ERROR_SYSTEM:
-    case COMPLETION_INSTALLER_ERROR_OTHER:
-      return true;
-    case COMPLETION_SUCCESS:
-    case COMPLETION_SUCCESS_REBOOT_REQUIRED:
-    case COMPLETION_ERROR:
-    case COMPLETION_CANCELLED:
-    default:
-      return false;
-  }
-}
-
-CString CompletionInfo::ToString() const {
-  CString error_code_string = FormatErrorCode(error_code);
-
-  // Get the format string for the status. Must have %s placeholder for code.
-  CString status_format;
-  switch (status) {
-    case COMPLETION_SUCCESS:
-      status_format = _T("Installer succeeded with code %s.");
-      break;
-    case COMPLETION_SUCCESS_REBOOT_REQUIRED:
-      status_format = _T("Installer succeeded but reboot required. Code %s.");
-      break;
-    case COMPLETION_ERROR:
-      status_format = _T("Omaha failed with error code %s.");
-      break;
-    case COMPLETION_INSTALLER_ERROR_MSI:
-      status_format = _T("Installer failed with MSI error %s.");
-      break;
-    case COMPLETION_INSTALLER_ERROR_SYSTEM:
-      status_format = _T("Installer failed with system error %s.");
-      break;
-    case COMPLETION_INSTALLER_ERROR_OTHER:
-      status_format = _T("Installer failed with error code %s.");
-      break;
-    case COMPLETION_CANCELLED:
-      status_format = _T("Operation canceled. Code %s.");
-      break;
-    default:
-      status_format = _T("Unknown status. Code %s.");
-      ASSERT1(false);
-  }
-
-  CString output;
-  output.Format(status_format, error_code_string);
-
-  if (!text.IsEmpty()) {
-    output.Append(_T(" ") + text);
-  }
-
-  return output;
-}
-
-}  // namespace omaha
-
diff --git a/worker/job.h b/worker/job.h
deleted file mode 100644
index 9a0f2c5..0000000
--- a/worker/job.h
+++ /dev/null
@@ -1,262 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// job.h : Contains the JobList and the Job class.
-// The Job goes through a number of states as represented by the following
-// state diagram for updates and installs.
-//
-//                  START
-//                    |
-//               DOWNLOAD STARTED ----------|
-//                    |                     |-> COMPLETED(ERROR)
-//              DOWNLOAD COMPLETED ---------|
-//                    |                     |
-//              INSTALL STARTED    ---------|
-//                    |
-//          COMPLETED(SUCCESS|RESTART BROWSER)
-//
-
-#ifndef OMAHA_WORKER_JOB_H__
-#define OMAHA_WORKER_JOB_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include <vector>
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "goopdate/google_update_idl.h"
-#include "omaha/common/browser_utils.h"
-#include "omaha/common/constants.h"
-#include "omaha/goopdate/update_response.h"
-#include "omaha/net/network_request.h"
-#include "omaha/worker/application_data.h"
-#include "omaha/worker/application_manager.h"
-
-namespace omaha {
-
-class DownloadManager;
-class InstallManager;
-class JobObserver;
-class Ping;
-class UpdateResponse;
-
-enum JobCompletionStatus {
-  COMPLETION_SUCCESS = 0,
-  COMPLETION_SUCCESS_REBOOT_REQUIRED,
-  COMPLETION_ERROR,
-  COMPLETION_INSTALLER_ERROR_MSI,
-  COMPLETION_INSTALLER_ERROR_SYSTEM,
-  COMPLETION_INSTALLER_ERROR_OTHER,
-  COMPLETION_CANCELLED,
-};
-
-// Represents the text and the error code of an error message.
-struct CompletionInfo {
-  CompletionInfo()
-      : status(COMPLETION_SUCCESS),
-        error_code(S_OK) {}
-  CompletionInfo(JobCompletionStatus stat,
-                 DWORD error,
-                 CString txt)
-      : status(stat),
-        error_code(error),
-        text(txt) {}
-
-  CString ToString() const;
-
-  JobCompletionStatus status;  // Job status on completion.
-  DWORD error_code;
-  CString text;                // Success or failure text.
-};
-
-bool IsCompletionStatusSuccess(JobCompletionStatus status);
-bool IsCompletionStatusInstallerError(JobCompletionStatus status);
-inline bool IsCompletionSuccess(CompletionInfo info) {
-  return IsCompletionStatusSuccess(info.status);
-}
-inline bool IsCompletionInstallerError(CompletionInfo info) {
-  return IsCompletionStatusInstallerError(info.status);
-}
-
-
-// These are reported in failure pings. If changing existing values, let the
-// team know and maybe change the kJobStateExtraCodeMask.
-enum JobState {
-  JOBSTATE_START = 1,               // Initial state.
-  JOBSTATE_DOWNLOADSTARTED,         // Started the download of the installer.
-  JOBSTATE_DOWNLOADCOMPLETED,       // Completed the download from the server.
-  JOBSTATE_INSTALLERSTARTED,        // Installer run.
-  JOBSTATE_COMPLETED                // Job completed.
-};
-
-// The job contains information about install or update jobs.
-class Job : public NetworkRequestCallback {
- public:
-  // Differentiates JobState values from other extra_code1 values.
-  static const int kJobStateExtraCodeMask = 0x10000000;
-
-  Job(bool is_update, Ping* ping);
-  virtual ~Job();
-
-  // Transition the job into an completed state.
-  virtual void NotifyCompleted(const CompletionInfo& info);
-
-  // NetworkRequestCallback implementation.
-  virtual void OnProgress(int bytes,
-                          int bytes_total,
-                          int status,
-                          const TCHAR* status_text);
-
-  void RestartBrowsers();
-  void LaunchBrowser(const CString& url);
-
-  // If the installer has set a custom command line in the installer results
-  // registry, we run the command after a successful interactive install.
-  HRESULT LaunchCmdLine();
-
-  virtual HRESULT Download(DownloadManager* manager);
-  virtual HRESULT Install();
-
-
-
-  // Getters and Setters for all the data members.
-
-  const AppData& app_data() const { return app_data_; }
-  void set_app_data(const AppData& app_data);
-
-  CString download_file_name() const { return download_file_name_; }
-  void set_download_file_name(const CString& download_file_name) {
-    download_file_name_ = download_file_name;
-  }
-
-  CString launch_cmd_line() const { return launch_cmd_line_; }
-  void set_launch_cmd_line(const CString& launch_cmd_line) {
-    launch_cmd_line_ = launch_cmd_line;
-  }
-
-  uint32 user_explorer_pid() const { return user_explorer_pid_; }
-  void set_user_explorer_pid(uint32 explorer_pid) {
-    user_explorer_pid_ = explorer_pid;
-  }
-
-  const CompletionInfo& info() const { return info_; }
-  void set_extra_code1(int extra_code1) {
-    extra_code1_ = extra_code1;
-  }
-  JobState job_state() const { return job_state_; }
-  int bytes_downloaded() const { return bytes_downloaded_; }
-  int bytes_total() const { return bytes_total_; }
-  void set_job_observer(JobObserver* job_observer) {
-    job_observer_ = job_observer;
-  }
-  bool is_machine_app() const { return app_data_.is_machine_app(); }
-
-  void set_update_response_data(const UpdateResponseData& response_data) {
-    update_response_data_ = response_data;
-  }
-
-  const UpdateResponseData& response_data() const {
-    return update_response_data_;
-  }
-
-  bool is_update() const {
-    return is_update_;
-  }
-  bool is_offline() const {
-    return is_offline_;
-  }
-  void set_is_offline(bool is_offline) {
-    is_offline_ = is_offline;
-  }
-  bool is_background() const {
-    return is_background_;
-  }
-  void set_is_background(bool is_background) {
-    is_background_ = is_background;
-  }
-  void set_is_update_check_only(bool is_update_check_only) {
-    is_update_check_only_ = is_update_check_only;
-  }
-
-  HRESULT GetInstallerData(CString* installer_data) const;
-
- private:
-  HRESULT ShouldRestartBrowser() const;
-  CompletionCodes CompletionStatusToCompletionCode(
-      JobCompletionStatus status) const;
-  HRESULT DoCompleteJob();
-  HRESULT SendStateChangePing(JobState previous_state);
-  PingEvent::Types JobStateToEventType() const;
-  void ChangeState(JobState state, bool send_ping);
-  HRESULT NotifyUI();
-  void NotifyDownloadStarted();
-  void NotifyDownloadComplete();
-  void NotifyInstallStarted();
-  HRESULT DoInstall(CompletionInfo* completion_info, AppData* new_app_data);
-  HRESULT UpdateJob();
-  HRESULT UpdateRegistry(AppData* new_app_data);
-  HRESULT DeleteJobDownloadDirectory() const;
-
-  AppData app_data_;
-  CString download_file_name_;   // The name of the downloaded file.
-  CString launch_cmd_line_;      // Custom command to run after successful
-                                 // install.
-  CompletionInfo info_;          // The completion info.
-  int extra_code1_;              // Extra info to send in pings.
-                                 // Cleared after each ping.
-  JobState job_state_;           // The state of the job.
-  JobObserver* job_observer_;    // Pointer to an observer for the job
-                                 // events.
-
-  // Other miscellaneous information.
-  uint32 user_explorer_pid_;     // Identifies user that initiated job.
-
-  UpdateResponseData update_response_data_;
-
-  // Represent the job information during download.
-  int bytes_downloaded_;         // The number of bytes downloaded.
-  int bytes_total_;              // The download file size in bytes.
-  Ping* ping_;                   // The Ping object used to send pings.
-
-  // True if the job is any kind of update and false if it is an install.
-  bool is_update_;
-
-  // True if the job is using offline data instead of an online update check.
-  bool is_offline_;
-
-  // True if the job is running silently in the background but not if running
-  // silently at the request of another process.
-  bool is_background_;
-
-  // True if the job is intended to do an update check only, for instance, in
-  // an on demand check for updates case.
-  bool is_update_check_only_;
-
-  // True if the launch command failed to launch. Only set if the launch was
-  // attempted and failed. Does not reflect successful execution or exit code.
-  bool did_launch_cmd_fail_;
-
-  friend class InstallManagerTest;
-  friend class JobTest;
-
-  DISALLOW_EVIL_CONSTRUCTORS(Job);
-};
-
-typedef std::vector<Job*> Jobs;
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_JOB_H__
-
diff --git a/worker/job_creator.cc b/worker/job_creator.cc
deleted file mode 100644
index 2ccf072..0000000
--- a/worker/job_creator.cc
+++ /dev/null
@@ -1,647 +0,0 @@
-// Copyright 2008-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/worker/job_creator.h"
-
-#include <windows.h>
-#include <atlcom.h>
-#include <cstring>
-#include <map>
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/path.h"
-#include "omaha/common/time.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_xml_parser.h"
-#include "omaha/goopdate/request.h"
-#include "omaha/goopdate/resource.h"
-#include "omaha/goopdate/update_response_data.h"
-#include "omaha/worker/application_data.h"
-#include "omaha/worker/ping.h"
-#include "omaha/worker/worker_event_logger.h"
-#include "omaha/worker/worker_metrics.h"
-
-namespace omaha {
-
-HRESULT JobCreator::CreateJobsFromResponses(
-    const UpdateResponses& responses,
-    const ProductDataVector& products,
-    Jobs* jobs,
-    Request* ping_request,
-    CString* event_log_text,
-    CompletionInfo* completion_info) {
-  ASSERT1(jobs);
-  ASSERT1(ping_request);
-  ASSERT1(event_log_text);
-  ASSERT1(completion_info);
-  CORE_LOG(L2, (_T("[JobCreator::CreateJobsFromResponses]")));
-
-  // First check to see if GoogleUpdate update is available, if so we only
-  // create the job for googleupdate and don't create jobs for the rest.
-  if (IsGoopdateUpdateAvailable(responses)) {
-    return CreateGoopdateJob(responses,
-                             products,
-                             jobs,
-                             ping_request,
-                             event_log_text,
-                             completion_info);
-  }
-
-  return CreateJobsFromResponsesInternal(responses,
-                                         products,
-                                         jobs,
-                                         ping_request,
-                                         event_log_text,
-                                         completion_info);
-}
-
-bool JobCreator::IsGoopdateUpdateAvailable(const UpdateResponses& responses) {
-  UpdateResponses::const_iterator iter = responses.find(kGoopdateGuid);
-  if (iter == responses.end()) {
-    return false;
-  }
-
-  const UpdateResponse& update_response = iter->second;
-  return (_tcsicmp(kResponseStatusOkValue,
-                   update_response.update_response_data().status()) == 0);
-}
-
-void JobCreator::AddAppUpdateDeferredPing(const AppData& product_data,
-                                          Request* ping_request) {
-  ASSERT1(ping_request);
-
-  AppRequest ping_app_request;
-  AppRequestData ping_app_request_data(product_data);
-  PingEvent ping_event(PingEvent::EVENT_UPDATE_COMPLETE,
-                       PingEvent::EVENT_RESULT_UPDATE_DEFERRED,
-                       0,
-                       0,  // extra code 1
-                       product_data.previous_version());
-  ping_app_request_data.AddPingEvent(ping_event);
-  ping_app_request.set_request_data(ping_app_request_data);
-  ping_request->AddAppRequest(ping_app_request);
-}
-
-HRESULT JobCreator::CreateGoopdateJob(const UpdateResponses& responses,
-                                      const ProductDataVector& products,
-                                      Jobs* jobs,
-                                      Request* ping_request,
-                                      CString* event_log_text,
-                                      CompletionInfo* completion_info) {
-  ASSERT1(jobs);
-  ASSERT1(ping_request);
-  ASSERT1(event_log_text);
-  ASSERT1(completion_info);
-
-  UpdateResponses::const_iterator it = responses.find(kGoopdateGuid);
-  if (it == responses.end()) {
-    return E_INVALIDARG;
-  }
-
-  UpdateResponses goopdate_responses;
-  goopdate_responses[kGoopdateGuid] = it->second;
-
-  // Create a job for GoogleUpdate only and defer updates for other products,
-  // if updates are available.
-  ProductDataVector goopdate_products;
-  bool is_other_app_update_available(false);
-  for (size_t i = 0; i < products.size(); ++i) {
-    const GUID app_id = products[i].app_data().app_guid();
-    if (::IsEqualGUID(app_id, kGoopdateGuid)) {
-      goopdate_products.push_back(products[i]);
-    } else {
-      it = responses.find(app_id);
-      if (it != responses.end() && IsUpdateAvailable(it->second)) {
-        is_other_app_update_available = true;
-        AddAppUpdateDeferredPing(products[i].app_data(), ping_request);
-      }
-    }
-  }
-  ASSERT1(goopdate_products.size() == 1);
-
-  if (is_other_app_update_available) {
-    ++metric_worker_skipped_app_update_for_self_update;
-  }
-
-  return CreateJobsFromResponsesInternal(goopdate_responses,
-                                         goopdate_products,
-                                         jobs,
-                                         ping_request,
-                                         event_log_text,
-                                         completion_info);
-}
-
-HRESULT JobCreator::ReadOfflineManifest(const CString& offline_dir,
-                                        const CString& app_guid,
-                                        UpdateResponse* response) {
-  ASSERT1(response);
-
-  CString manifest_filename = app_guid + _T(".gup");
-  CString manifest_path = ConcatenatePath(offline_dir, manifest_filename);
-  if (!File::Exists(manifest_path)) {
-    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
-  }
-
-  UpdateResponses responses;
-  HRESULT hr = GoopdateXmlParser::ParseManifestFile(manifest_path, &responses);
-  if (FAILED(hr)) {
-    OPT_LOG(LE, (_T("[Could not parse manifest][%s]"), manifest_path));
-    return hr;
-  }
-  ASSERT1(!responses.empty());
-  ASSERT1(1 == responses.size());
-
-  UpdateResponses::const_iterator iter = responses.begin();
-  *response = iter->second;
-  ASSERT1(!app_guid.CompareNoCase(GuidToString(
-      response->update_response_data().guid())));
-  return S_OK;
-}
-
-HRESULT JobCreator::FindOfflineFilePath(const CString& offline_dir,
-                                        const CString& app_guid,
-                                        CString* file_path) {
-  ASSERT1(file_path);
-  file_path->Empty();
-
-  CString offline_app_dir = ConcatenatePath(offline_dir, app_guid);
-  CString pattern(_T("*"));
-  std::vector<CString> files;
-  HRESULT hr = FindFiles(offline_app_dir, pattern, &files);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  CString local_file_path;
-  // Skip over "." and "..".
-  size_t i = 0;
-  for (; i < files.size(); ++i) {
-    local_file_path = ConcatenatePath(offline_app_dir, files[i]);
-    if (!File::IsDirectory(local_file_path)) {
-      break;
-    }
-  }
-  if (i == files.size()) {
-    return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
-  }
-
-  *file_path = local_file_path;
-  return S_OK;
-}
-
-HRESULT JobCreator::CreateOfflineJobs(const CString& offline_dir,
-                                      const ProductDataVector& products,
-                                      Jobs* jobs,
-                                      Request* ping_request,
-                                      CString* event_log_text,
-                                      CompletionInfo* completion_info) {
-  CORE_LOG(L2, (_T("[JobCreator::CreateOfflineJobs]")));
-  ASSERT1(jobs);
-  ASSERT1(ping_request);
-  ASSERT1(event_log_text);
-  ASSERT1(completion_info);
-
-  ProductDataVector::const_iterator products_it = products.begin();
-  for (; products_it != products.end(); ++products_it) {
-    const ProductData& product_data = *products_it;
-    CString guid(GuidToString(product_data.app_data().app_guid()));
-    UpdateResponse response;
-    CString file_path;
-
-    HRESULT hr = ReadOfflineManifest(offline_dir, guid, &response);
-    if (SUCCEEDED(hr)) {
-      hr = FindOfflineFilePath(offline_dir, guid, &file_path);
-    }
-    if (SUCCEEDED(hr)) {
-      hr = goopdate_utils::ValidateDownloadedFile(
-               file_path,
-               response.update_response_data().hash(),
-               static_cast<uint32>(response.update_response_data().size()));
-    }
-    if (FAILED(hr)) {
-      *completion_info = CompletionInfo(COMPLETION_ERROR, hr, _T(""));
-      return hr;
-    }
-
-    Job* product_job = NULL;
-    hr = HandleProductUpdateIsAvailable(product_data,
-                                        response,
-                                        &product_job,
-                                        ping_request,
-                                        event_log_text,
-                                        completion_info);
-    if (FAILED(hr)) {
-      return hr;
-    }
-
-    product_job->set_download_file_name(file_path);
-    product_job->set_is_offline(true);
-    jobs->push_back(product_job);
-  }
-
-  return S_OK;
-}
-
-HRESULT JobCreator::CreateJobsFromResponsesInternal(
-    const UpdateResponses& responses,
-    const ProductDataVector& products,
-    Jobs* jobs,
-    Request* ping_request,
-    CString* event_log_text,
-    CompletionInfo* completion_info) {
-  ASSERT1(jobs);
-  ASSERT1(ping_request);
-  ASSERT1(event_log_text);
-  ASSERT1(completion_info);
-  CORE_LOG(L2, (_T("[JobCreator::CreateJobsFromResponsesInternal]")));
-
-  // The ordering needs to be done based on the products array, instead of
-  // basing this on the responses. This is because the first element in the
-  // job that is created is considered the primary app.
-  ProductDataVector::const_iterator products_it = products.begin();
-  for (; products_it != products.end(); ++products_it) {
-    const ProductData& product_data = *products_it;
-    AppData product_app_data = product_data.app_data();
-
-    UpdateResponses::const_iterator iter =
-        responses.find(product_app_data.app_guid());
-    if (iter == responses.end()) {
-      AppRequestData ping_app_request_data(product_app_data);
-      HandleResponseNotFound(product_app_data,
-                             &ping_app_request_data,
-                             event_log_text);
-      continue;
-    }
-
-    const UpdateResponse& response = iter->second;
-
-    AppManager app_manager(is_machine_);
-    // If this is an update job, need to call
-    // AppManager::UpdateApplicationState() to make sure the registry is
-    // initialized properly.
-    if (is_update_) {
-      app_manager.UpdateApplicationState(&product_app_data);
-    }
-
-    // Write the TT Token, if any, with what the server returned. The server
-    // may ask us to clear the token as well.
-    app_manager.WriteTTToken(product_app_data, response.update_response_data());
-
-    if (IsUpdateAvailable(response)) {
-      if (product_data.app_data().is_update_disabled()) {
-        ASSERT1(is_update_);
-        ASSERT1(!fail_if_update_not_available_);
-        CORE_LOG(L1, (_T("[Update available for update-disabled app][%s]"),
-                      GuidToString(product_data.app_data().app_guid())));
-        app_manager.ClearUpdateAvailableStats(
-            GUID_NULL,
-            product_data.app_data().app_guid());
-      } else {
-        ProductData modified_product_data(product_data);
-        modified_product_data.set_app_data(product_app_data);
-        Job* product_job = NULL;
-        HRESULT hr = HandleProductUpdateIsAvailable(modified_product_data,
-                                                    response,
-                                                    &product_job,
-                                                    ping_request,
-                                                    event_log_text,
-                                                    completion_info);
-        if (FAILED(hr)) {
-          return hr;
-        }
-
-        jobs->push_back(product_job);
-      }
-    } else {
-      AppRequest ping_app_request;
-      AppRequestData ping_app_request_data(product_app_data);
-      HRESULT hr = HandleUpdateNotAvailable(product_app_data,
-                                            response.update_response_data(),
-                                            &ping_app_request_data,
-                                            event_log_text,
-                                            completion_info);
-      // TODO(omaha): It's unclear why the ping is added before the FAILED
-      // check here but after it in HandleProductUpdateIsAvailable(). Is this
-      // intentional?
-      ping_app_request.set_request_data(ping_app_request_data);
-      ping_request->AddAppRequest(ping_app_request);
-      if (FAILED(hr)) {
-        return hr;
-      }
-    }
-  }
-
-  return S_OK;
-}
-
-HRESULT JobCreator::HandleProductUpdateIsAvailable(
-    const ProductData& product_data,
-    const UpdateResponse& response,
-    Job** product_job,
-    Request* ping_request,
-    CString* event_log_text,
-    CompletionInfo* completion_info) {
-  ASSERT1(product_job);
-
-  AppData product_app_data = product_data.app_data();
-  AppRequest ping_app_request;
-  AppRequestData ping_app_request_data(product_app_data);
-
-  if (is_update_) {
-    // previous_version should have been populated above.
-    ASSERT1(!product_app_data.previous_version().IsEmpty());
-  } else {
-    // Copy the current version of app, if available, to previous version
-    // in the AppData object so it is sent in pings.
-    AppData existing_app_data;
-    AppManager app_manager(is_machine_);
-    HRESULT hr = app_manager.ReadAppDataFromStore(
-                     GUID_NULL,
-                     product_app_data.app_guid(),
-                     &existing_app_data);
-    if (SUCCEEDED(hr)) {
-      product_app_data.set_previous_version(existing_app_data.version());
-    }
-  }
-
-  ASSERT1(::IsEqualGUID(GUID_NULL, product_app_data.parent_app_guid()));
-  HandleUpdateIsAvailable(product_app_data,
-                          response.update_response_data(),
-                          &ping_app_request_data,
-                          event_log_text,
-                          product_job);
-  ASSERT1(*product_job);
-  ping_app_request.set_request_data(ping_app_request_data);
-
-  ping_request->AddAppRequest(ping_app_request);
-  return S_OK;
-}
-
-void JobCreator::HandleResponseNotFound(const AppData& app_data,
-                                        AppRequestData* ping_app_request_data,
-                                        CString* event_log_text) {
-  ASSERT1(event_log_text);
-  ASSERT1(ping_app_request_data);
-
-  PingEvent::Types event_type = is_update_ ?
-                                PingEvent::EVENT_UPDATE_COMPLETE :
-                                PingEvent::EVENT_INSTALL_COMPLETE;
-  PingEvent ping_event(event_type,
-                       PingEvent::EVENT_RESULT_ERROR,
-                       GOOPDATE_E_NO_SERVER_RESPONSE,
-                       0,  // extra code 1
-                       app_data.previous_version());
-  ping_app_request_data->AddPingEvent(ping_event);
-
-  event_log_text->AppendFormat(
-      _T("App=%s, Ver=%s, Status=no-response-received\n"),
-      GuidToString(app_data.app_guid()),
-      app_data.version());
-}
-
-void JobCreator::HandleUpdateIsAvailable(
-    const AppData& app_data,
-    const UpdateResponseData& response_data,
-    AppRequestData* ping_app_request_data,
-    CString* event_log_text,
-    Job** job) {
-  ASSERT1(ping_app_request_data);
-  ASSERT1(event_log_text);
-  ASSERT1(job);
-  ASSERT1(!app_data.is_update_disabled());
-
-  if (is_auto_update_) {
-    if (::IsEqualGUID(kGoopdateGuid, app_data.app_guid())) {
-      ++metric_worker_self_updates_available;
-    } else {
-      ++metric_worker_app_updates_available;
-    }
-
-    // Only record an update available event for updates.
-    // We have other mechanisms, including IID, to track install success.
-    AppManager app_manager(is_machine_);
-    app_manager.UpdateUpdateAvailableStats(app_data.parent_app_guid(),
-                                           app_data.app_guid());
-  }
-
-  // Ping and record events only for "real" jobs. On demand update checks only
-  // jobs should not ping, since no update is actually applied.
-  if (!is_update_check_only_) {
-    PingEvent::Types event_type = is_update_ ?
-                                  PingEvent::EVENT_UPDATE_APPLICATION_BEGIN :
-                                  PingEvent::EVENT_INSTALL_APPLICATION_BEGIN;
-    PingEvent ping_event(event_type,
-                         PingEvent::EVENT_RESULT_SUCCESS,
-                         0,  // error code
-                         0,  // extra code 1
-                         app_data.previous_version());
-    ping_app_request_data->AddPingEvent(ping_event);
-  }
-  const TCHAR* status = is_update_check_only_ ? _T("check only") :
-                        is_update_ ? _T("update") : _T("install");
-  event_log_text->AppendFormat(_T("App=%s, Ver=%s, Status=%s\n"),
-                               GuidToString(app_data.app_guid()),
-                               app_data.version(),
-                               status);
-
-  // TODO(omaha): When components are implemented, how do we handle the case
-  // that this is just a place holder and does not really need an update?
-  // We may be in an app that only needs an update here because it's child
-  // components need updates so we'll need to flag the job of that somehow.
-  // Unless it's already tagged in the response_data (which does know that
-  // there's nothing to download) but the job state will need to be updated.
-  scoped_ptr<Job> new_job(new Job(is_update_, ping_));
-  new_job->set_app_data(app_data);
-  new_job->set_update_response_data(response_data);
-  new_job->set_is_background(is_auto_update_);
-  new_job->set_is_update_check_only(is_update_check_only_);
-
-  *job = new_job.release();
-}
-
-HRESULT JobCreator::HandleUpdateNotAvailable(
-    const AppData& app_data,
-    const UpdateResponseData& response_data,
-    AppRequestData* ping_app_request_data,
-    CString* event_log_text,
-    CompletionInfo* completion_info) {
-  ASSERT1(ping_app_request_data);
-  ASSERT1(event_log_text);
-  ASSERT1(completion_info);
-
-  PingEvent::Results result_type = PingEvent::EVENT_RESULT_ERROR;
-  CompletionInfo info = UpdateResponseDataToCompletionInfo(
-      response_data,
-      ping_app_request_data->app_data().display_name());
-  switch (info.error_code) {
-    case static_cast<DWORD>(GOOPDATE_E_NO_UPDATE_RESPONSE):
-      event_log_text->AppendFormat(
-          _T("App=%s, Ver=%s, Status=no-update\n"),
-          GuidToString(app_data.app_guid()),
-          app_data.version());
-      if (is_update_) {
-        // In case of updates, a "noupdate" response is not an error.
-        result_type = PingEvent::EVENT_RESULT_NOUPDATE;
-        info.status = COMPLETION_SUCCESS;
-        info.error_code = NOERROR;
-
-        AppManager app_manager(is_machine_);
-        app_manager.ClearUpdateAvailableStats(app_data.parent_app_guid(),
-                                              app_data.app_guid());
-        app_manager.RecordSuccessfulUpdateCheck(app_data.parent_app_guid(),
-                                                app_data.app_guid());
-      }
-      break;
-    case static_cast<DWORD>(GOOPDATE_E_RESTRICTED_SERVER_RESPONSE):
-      event_log_text->AppendFormat(
-          _T("App=%s, Ver=%s, Status=restricted\n"),
-          GuidToString(app_data.app_guid()),
-          app_data.version());
-      break;
-    case GOOPDATE_E_UNKNOWN_APP_SERVER_RESPONSE:
-    case GOOPDATE_E_INTERNAL_ERROR_SERVER_RESPONSE:
-    case GOOPDATE_E_SERVER_RESPONSE_NO_HASH:
-    case GOOPDATE_E_SERVER_RESPONSE_UNSUPPORTED_PROTOCOL:
-    case GOOPDATE_E_UNKNOWN_SERVER_RESPONSE:
-    default:
-      event_log_text->AppendFormat(_T("App=%s, Ver=%s, Status=error:0x%08x\n"),
-                                   GuidToString(app_data.app_guid()),
-                                   app_data.version(),
-                                   info.error_code);
-      break;
-  }
-
-  // Only ping for server responses other than "noupdate" or errors.
-  if (result_type != PingEvent::EVENT_RESULT_NOUPDATE ||
-      info.error_code != NOERROR) {
-    PingEvent::Types event_type = is_update_ ?
-                                  PingEvent::EVENT_UPDATE_COMPLETE :
-                                  PingEvent::EVENT_INSTALL_COMPLETE;
-    PingEvent ping_event(event_type,
-                         result_type,
-                         info.error_code,
-                         0,  // extra code 1
-                         app_data.previous_version());
-    ping_app_request_data->AddPingEvent(ping_event);
-  }
-
-  if (fail_if_update_not_available_) {
-    *completion_info = info;
-    return info.error_code;
-  }
-
-  return S_OK;
-}
-
-// app_name in UpdateResponseData is not filled in. See the TODO in that class.
-CompletionInfo JobCreator::UpdateResponseDataToCompletionInfo(
-    const UpdateResponseData& response_data,
-    const CString& display_name) {
-  CompletionInfo info;
-  const CString& status = response_data.status();
-  if (_tcsicmp(kResponseStatusOkValue, status) == 0) {
-    info.status = COMPLETION_SUCCESS;
-    info.error_code = 0;
-  } else if (_tcsicmp(kResponseStatusNoUpdate, status) == 0) {
-    // "noupdate" is considered an error but the calling code can map it to
-    // a successful completion, in the cases of silent and on demand updates.
-    info.status = COMPLETION_ERROR;
-    info.error_code = static_cast<DWORD>(GOOPDATE_E_NO_UPDATE_RESPONSE);
-    VERIFY1(info.text.LoadString(IDS_NO_UPDATE_RESPONSE));
-  } else if (_tcsicmp(kResponseStatusRestrictedExportCountry, status) == 0) {
-    // "restricted"
-    info.status = COMPLETION_ERROR;
-    info.error_code =
-        static_cast<DWORD>(GOOPDATE_E_RESTRICTED_SERVER_RESPONSE);
-    VERIFY1(info.text.LoadString(IDS_RESTRICTED_RESPONSE_FROM_SERVER));
-  } else if (_tcsicmp(kResponseStatusUnKnownApplication, status) == 0) {
-    // "error-UnKnownApplication"
-    info.status = COMPLETION_ERROR;
-    info.error_code =
-        static_cast<DWORD>(GOOPDATE_E_UNKNOWN_APP_SERVER_RESPONSE);
-    VERIFY1(info.text.LoadString(IDS_UNKNOWN_APPLICATION));
-  } else if (_tcsicmp(kResponseStatusOsNotSupported, status) == 0) {
-    // "error-OsNotSupported"
-    info.status = COMPLETION_ERROR;
-    info.error_code = static_cast<DWORD>(GOOPDATE_E_OS_NOT_SUPPORTED);
-    if (response_data.error_url().IsEmpty()) {
-      info.text.FormatMessage(IDS_NON_OK_RESPONSE_FROM_SERVER, status);
-    } else {
-      info.text.FormatMessage(IDS_OS_NOT_SUPPORTED,
-                              display_name,
-                              response_data.error_url());
-    }
-  } else if (_tcsicmp(kResponseStatusInternalError, status) == 0) {
-    // "error-internal"
-    info.status = COMPLETION_ERROR;
-    info.error_code =
-        static_cast<DWORD>(GOOPDATE_E_INTERNAL_ERROR_SERVER_RESPONSE);
-    info.text.FormatMessage(IDS_NON_OK_RESPONSE_FROM_SERVER, status);
-  } else if (_tcsicmp(kResponseStatusHashError, status) == 0) {
-    // "error-hash"
-    info.status = COMPLETION_ERROR;
-    info.error_code =
-        static_cast<DWORD>(GOOPDATE_E_SERVER_RESPONSE_NO_HASH);
-    info.text.FormatMessage(IDS_NON_OK_RESPONSE_FROM_SERVER, status);
-  } else if (_tcsicmp(kResponseStatusUnsupportedProtocol, status) == 0) {
-    // "error-unsupportedprotocol"
-    info.status = COMPLETION_ERROR;
-    info.error_code =
-        static_cast<DWORD>(GOOPDATE_E_SERVER_RESPONSE_UNSUPPORTED_PROTOCOL);
-    // TODO(omaha): Ideally, we would provide an app-specific URL instead of
-    // just the publisher name. If it was a link, we could use point to a
-    // redirect URL and provide the app GUID rather than somehow obtaining the
-    // app-specific URL.
-    info.text.FormatMessage(IDS_INSTALLER_OLD, kPublisherName);
-  } else {
-    // Unknown response.
-    info.status = COMPLETION_ERROR;
-    info.error_code =
-        static_cast<DWORD>(GOOPDATE_E_UNKNOWN_SERVER_RESPONSE);
-    info.text.FormatMessage(IDS_NON_OK_RESPONSE_FROM_SERVER, status);
-  }
-
-  return info;
-}
-
-bool JobCreator::IsUpdateAvailable(const UpdateResponseData& response_data) {
-  return _tcsicmp(kResponseStatusOkValue, response_data.status()) == 0;
-}
-
-// Check response and all of its children to see if any of them are in a state
-// that requires an update job to be created.
-bool JobCreator::IsUpdateAvailable(const UpdateResponse& response) {
-  // If the top level response needs updating, short circuit here.
-  if (IsUpdateAvailable(response.update_response_data())) {
-    return true;
-  }
-
-  UpdateResponseDatas::const_iterator it;
-  for (it = response.components_begin();
-       it != response.components_begin();
-       ++it) {
-    if (IsUpdateAvailable(it->second)) {
-      return true;
-    }
-  }
-
-  return false;
-}
-
-}  // namespace omaha
-
diff --git a/worker/job_creator.h b/worker/job_creator.h
deleted file mode 100644
index 0c92d8c..0000000
--- a/worker/job_creator.h
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// job_creator.h:  Class that handles creating jobs out of ProductData that's
-// being requested to be installed/updated and the UpdateResponse objects
-// received from the server.  Builds up proper job hierarchy, ping data, error
-// handling, and event logging since this overall operation is pretty complex.
-
-#ifndef OMAHA_WORKER_JOB_CREATOR_H__
-#define OMAHA_WORKER_JOB_CREATOR_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include "base/basictypes.h"
-#include "omaha/common/debug.h"
-#include "omaha/goopdate/request.h"
-#include "omaha/goopdate/update_response.h"
-#include "omaha/worker/job.h"
-#include "omaha/worker/product_data.h"
-
-namespace omaha {
-
-class AppData;
-
-class JobCreator {
- public:
-  // The caller retains ownership of ping. Delete this object and the Jobs it
-  // creates before deleting the Ping.
-  JobCreator(bool is_machine, bool is_update, Ping* ping)
-      : ping_(ping),
-        is_machine_(is_machine),
-        is_update_(is_update),
-        is_auto_update_(false),
-        is_update_check_only_(false),
-        fail_if_update_not_available_(false) {}
-  ~JobCreator() {}
-  HRESULT CreateJobsFromResponses(const UpdateResponses& responses,
-                                  const ProductDataVector& products,
-                                  Jobs* jobs,
-                                  Request* ping_request,
-                                  CString* event_log_text,
-                                  CompletionInfo* completion_info);
-
-  // Creates jobs based on offline installers that are stored in {guid}
-  // subdirectories under offline_dir. offline_dir is typically
-  // Google\Update\Offline\.
-  HRESULT CreateOfflineJobs(const CString& offline_dir,
-                            const ProductDataVector& products,
-                            Jobs* jobs,
-                            Request* ping_request,
-                            CString* event_log_text,
-                            CompletionInfo* completion_info);
-
-  void set_is_auto_update(bool is_auto_update) {
-    is_auto_update_ = is_auto_update;
-  }
-  void set_is_update_check_only(bool is_update_check_only) {
-    is_update_check_only_ = is_update_check_only;
-  }
-  void set_fail_if_update_not_available(bool fail_if_update_not_available) {
-    fail_if_update_not_available_ = fail_if_update_not_available;
-  }
-
- private:
-  // Read offline manifest offline_dir\{guid}.gup.
-  static HRESULT ReadOfflineManifest(const CString& offline_dir,
-                                     const CString& app_guid,
-                                     UpdateResponse* response);
-
-  // Finds offline installer stored under offline_dir\{guid}.
-  static HRESULT FindOfflineFilePath(const CString& offline_dir,
-                                     const CString& app_guid,
-                                     CString* file_path);
-
-  HRESULT CreateJobsFromResponsesInternal(
-      const UpdateResponses& responses,
-      const ProductDataVector& products,
-      Jobs* jobs,
-      Request* ping_request,
-      CString* event_log_text,
-      CompletionInfo* completion_info);
-
-  HRESULT HandleProductUpdateIsAvailable(
-      const ProductData& product_data,
-      const UpdateResponse& response,
-      Job** product_job,
-      Request* ping_request,
-      CString* event_log_text,
-      CompletionInfo* completion_info);
-
-  HRESULT CreateGoopdateJob(const UpdateResponses& responses,
-                            const ProductDataVector& products,
-                            Jobs* jobs,
-                            Request* ping_request,
-                            CString* event_log_text,
-                            CompletionInfo* completion_info);
-
-  bool IsGoopdateUpdateAvailable(const UpdateResponses& responses);
-
-  void HandleResponseNotFound(const AppData& app_data,
-                              AppRequestData* ping_app_request_data,
-                              CString* event_log_text);
-  void HandleUpdateIsAvailable(const AppData& app_data,
-                               const UpdateResponseData& response_data,
-                               AppRequestData* ping_app_request_data,
-                               CString* event_log_text,
-                               Job** job);
-  HRESULT HandleUpdateNotAvailable(const AppData& app_data,
-                                   const UpdateResponseData& response_data,
-                                   AppRequestData* ping_app_request_data,
-                                   CString* event_log_text,
-                                   CompletionInfo* completion_info);
-  CompletionInfo UpdateResponseDataToCompletionInfo(
-      const UpdateResponseData& response_data,
-      const CString& display_name);
-  bool IsUpdateAvailable(const UpdateResponseData& response_data);
-  bool IsUpdateAvailable(const UpdateResponse& response);
-  void AddAppUpdateDeferredPing(const AppData& product_data,
-                                Request* ping_request);
-
-  Ping* ping_;
-  bool is_machine_;
-  bool is_update_;
-  // True, if the jobs is intended to do an update check only, for instance, in
-  // an on demand check for updates case.
-  bool is_auto_update_;
-  bool is_update_check_only_;
-  bool fail_if_update_not_available_;
-
-  friend class JobCreatorTest;
-  DISALLOW_EVIL_CONSTRUCTORS(JobCreator);
-};
-
-}  // namespace omaha.
-
-#endif  // OMAHA_WORKER_JOB_CREATOR_H__
-
-
diff --git a/worker/job_creator_unittest.cc b/worker/job_creator_unittest.cc
deleted file mode 100644
index 1a11383..0000000
--- a/worker/job_creator_unittest.cc
+++ /dev/null
@@ -1,1366 +0,0 @@
-// Copyright 2008-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <windows.h>
-#include "omaha/common/app_util.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/path.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/common/time.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/testing/unit_test.h"
-#include "omaha/worker/job_creator.h"
-#include "omaha/worker/ping.h"
-#include "omaha/worker/worker_metrics.h"
-
-namespace omaha {
-
-namespace {
-
-const TCHAR* kGuidApp1 = _T("{CDABE316-39CD-43BA-8440-6D1E0547AEE6}");
-const TCHAR* kGuidApp2 = _T("{28A93830-1746-4F0B-90F5-CF44B41169F3}");
-const TCHAR* kGuidApp3 = _T("{E5D3562E-BFAE-48c6-B9C5-4E293F695E0E}");
-const TCHAR* kGuidApp4 = _T("{F9346563-85DA-4dc1-A621-FAF6F869680A}");
-
-const TCHAR* const kApp1ClientStateKeyPathMachine =
-    _T("HKLM\\Software\\Google\\Update\\ClientState\\")
-    _T("{CDABE316-39CD-43BA-8440-6D1E0547AEE6}");
-const TCHAR* const kApp2ClientStateKeyPathMachine =
-    _T("HKLM\\Software\\Google\\Update\\ClientState\\")
-    _T("{28A93830-1746-4F0B-90F5-CF44B41169F3}");
-const TCHAR* const kApp3ClientStateKeyPathMachine =
-    _T("HKLM\\Software\\Google\\Update\\ClientState\\")
-    _T("{E5D3562E-BFAE-48c6-B9C5-4E293F695E0E}");
-const TCHAR* const kApp4ClientStateKeyPathMachine =
-    _T("HKLM\\Software\\Google\\Update\\ClientState\\")
-    _T("{F9346563-85DA-4dc1-A621-FAF6F869680A}");
-const TCHAR* const kApp1ClientsKeyPathMachine =
-    _T("HKLM\\Software\\Google\\Update\\Clients\\")
-    _T("{CDABE316-39CD-43BA-8440-6D1E0547AEE6}");
-const TCHAR* const kApp2ClientsKeyPathMachine =
-    _T("HKLM\\Software\\Google\\Update\\Clients\\")
-    _T("{28A93830-1746-4F0B-90F5-CF44B41169F3}");
-
-}  // namespace
-
-class JobCreatorTest : public testing::Test {
- protected:
-  JobCreatorTest()
-      : app1_guid_(StringToGuid(kGuidApp1)),
-        app2_guid_(StringToGuid(kGuidApp2)),
-        app3_guid_(StringToGuid(kGuidApp3)),
-        app4_guid_(StringToGuid(kGuidApp4)) {
-  }
-
-  virtual void SetUp() {
-    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
-    OverrideRegistryHives(kRegistryHiveOverrideRoot);
-
-    metric_worker_skipped_app_update_for_self_update.Reset();
-  }
-
-  virtual void TearDown() {
-    RestoreRegistryHives();
-    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
-  }
-
-  CompletionInfo UpdateResponseDataToCompletionInfo(
-      const UpdateResponseData& response_data,
-      const CString& display_name) {
-    JobCreator job_creator(false, false, &ping_);
-    job_creator.set_fail_if_update_not_available(true);
-    return job_creator.UpdateResponseDataToCompletionInfo(response_data,
-                                                          display_name);
-  }
-
-  static HRESULT CallFindOfflineFilePath(const CString& offline_dir,
-                                         const CString& app_guid,
-                                         CString* file_path) {
-    return JobCreator::FindOfflineFilePath(offline_dir, app_guid, file_path);
-  }
-
-  static HRESULT CallReadOfflineManifest(const CString& offline_dir,
-                                         const CString& app_guid,
-                                         UpdateResponse* response) {
-    return JobCreator::ReadOfflineManifest(offline_dir, app_guid, response);
-  }
-
-  Ping ping_;
-  const GUID app1_guid_;
-  const GUID app2_guid_;
-  const GUID app3_guid_;
-  const GUID app4_guid_;
-};
-
-TEST_F(JobCreatorTest, CreateJobsFromResponses_UpdateMultipleAppsAndStatuses) {
-  const CString version_goopdate = _T("1.2.75.3");
-  const CString version_app1 = _T("1.1.2.3");
-  const CString version_app2 = _T("2.0.0.5");
-  const int kElapsedTimeSec = 12345;
-
-  JobCreator job_creator(true, true, &ping_);
-  job_creator.set_is_auto_update(true);
-  AppManager app_manager(true);
-
-  const DWORD kExistingUpdateValues = 0x70123456;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                    kRegValueLastSuccessfulCheckSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                    kRegValueLastUpdateTimeSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp1ClientStateKeyPathMachine,
-                                    kRegValueLastSuccessfulCheckSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp1ClientStateKeyPathMachine,
-                                    kRegValueLastUpdateTimeSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2ClientStateKeyPathMachine,
-                                    kRegValueLastSuccessfulCheckSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2ClientStateKeyPathMachine,
-                                    kRegValueLastUpdateTimeSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(
-      RegKey::SetValue(kApp2ClientsKeyPathMachine, _T("pv"), version_app2));
-
-  ProductDataVector products;
-
-  AppData app_data_omaha;
-  app_data_omaha.set_app_guid(kGoopdateGuid);
-  app_data_omaha.set_is_machine_app(true);
-  app_data_omaha.set_version(version_goopdate);
-  ProductData product_omaha(app_data_omaha);
-  products.push_back(product_omaha);
-
-  AppData app_data1;
-  app_data1.set_app_guid(app1_guid_);
-  app_data1.set_is_machine_app(true);
-  app_data1.set_version(version_app1);
-  ProductData product1(app_data1);
-  products.push_back(product1);
-
-  AppData app_data2;
-  app_data2.set_app_guid(app2_guid_);
-  app_data2.set_is_machine_app(true);
-  app_data2.set_version(version_app2);
-  app_data2.set_did_run(AppData::ACTIVE_RUN);
-
-  // Set days_since_last_active_ping and days_since_last_roll_call to non-zero
-  // values so their timestamp gets a chance to update. The values themselves
-  // are not important as long as they are not 0s.
-  app_data2.set_days_since_last_active_ping(3);
-  app_data2.set_days_since_last_roll_call(1);
-
-  ProductData product2(app_data2);
-  products.push_back(product2);
-
-  UpdateResponses responses;
-
-  UpdateResponseData resp_data_omaha;
-  resp_data_omaha.set_guid(kGoopdateGuid);
-  resp_data_omaha.set_needs_admin(NEEDS_ADMIN_YES);
-  resp_data_omaha.set_status(kResponseStatusNoUpdate);
-  UpdateResponse resp_omaha(resp_data_omaha);
-  responses.insert(std::pair<GUID, UpdateResponse>(kGoopdateGuid, resp_omaha));
-
-  UpdateResponseData resp_data1;
-  resp_data1.set_guid(app1_guid_);
-  resp_data1.set_needs_admin(NEEDS_ADMIN_YES);
-  resp_data1.set_status(kResponseStatusNoUpdate);
-  UpdateResponse resp1(resp_data1);
-  responses.insert(std::pair<GUID, UpdateResponse>(app1_guid_, resp1));
-
-  UpdateResponseData resp_data2;
-  resp_data2.set_guid(app2_guid_);
-  resp_data2.set_needs_admin(NEEDS_ADMIN_YES);
-  resp_data2.set_status(kResponseStatusOkValue);
-  // TODO(omaha): Add component responses here.
-  UpdateResponse resp2(resp_data2);
-  resp2.set_time_since_midnight_sec(kElapsedTimeSec);
-  responses.insert(std::pair<GUID, UpdateResponse>(app2_guid_, resp2));
-
-  Jobs jobs;
-  Request ping_request(true);
-  CString event_log_text;
-  CompletionInfo completion_info;
-
-  // This should succeed for an update since it's OK to have "no update
-  // available" for updates.
-  EXPECT_SUCCEEDED(job_creator.CreateJobsFromResponses(responses,
-                                                       products,
-                                                       &jobs,
-                                                       &ping_request,
-                                                       &event_log_text,
-                                                       &completion_info));
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  // Should be a job for Resp2 since Resp1 was status "No Update".
-  ASSERT_EQ(1, jobs.size());
-  EXPECT_EQ(3, ping_request.get_request_count());
-  EXPECT_EQ(COMPLETION_SUCCESS, completion_info.status);
-  EXPECT_EQ(0, completion_info.error_code);
-  EXPECT_TRUE(completion_info.text.IsEmpty());
-  EXPECT_FALSE(jobs[0]->is_offline());
-
-  // Sleep so that there is a time difference between the time written in the
-  // registry and now.
-  ::Sleep(20);
-
-  // Omaha.
-  // Update Stats should not have been set because there was no update.
-  // Successful update check set for noupdate but successful update not updated.
-  DWORD update_responses(1);
-  DWORD64 time_since_first_response_ms(1);
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       kGoopdateGuid,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(0, update_responses);
-  EXPECT_EQ(0, time_since_first_response_ms);
-
-  const uint32 last_check_sec_omaha =
-      GetDwordValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                    kRegValueLastSuccessfulCheckSec);
-  EXPECT_NE(kExistingUpdateValues, last_check_sec_omaha);
-  EXPECT_GE(now, last_check_sec_omaha);
-  EXPECT_GE(static_cast<uint32>(200), now - last_check_sec_omaha);
-  EXPECT_EQ(kExistingUpdateValues,
-            GetDwordValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                          kRegValueLastUpdateTimeSec));
-
-  // App 1.
-  // Update Stats should not have been set because there was no update.
-  // Successful update check set for noupdate but successful update not updated.
-  update_responses = 1;
-  time_since_first_response_ms = 1;
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       app1_guid_,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(0, update_responses);
-  EXPECT_EQ(0, time_since_first_response_ms);
-
-  const uint32 last_check_sec_app1 =
-      GetDwordValue(kApp1ClientStateKeyPathMachine,
-                    kRegValueLastSuccessfulCheckSec);
-  EXPECT_NE(kExistingUpdateValues, last_check_sec_app1);
-  EXPECT_GE(now, last_check_sec_app1);
-  EXPECT_GE(static_cast<uint32>(200), now - last_check_sec_app1);
-  EXPECT_EQ(kExistingUpdateValues,
-            GetDwordValue(kApp1ClientStateKeyPathMachine,
-                          kRegValueLastUpdateTimeSec));
-
-  // App 2.
-  // Update Stats should have been set.
-  // Neither successful update check nor successful update are updated because
-  // the update has not been completed.
-  update_responses = 0;
-  time_since_first_response_ms = 0;
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       app2_guid_,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(1, update_responses);
-  EXPECT_LT(0, time_since_first_response_ms);
-  EXPECT_GT(10 * kMsPerSec, time_since_first_response_ms);
-
-  EXPECT_EQ(kExistingUpdateValues,
-            GetDwordValue(kApp2ClientStateKeyPathMachine,
-                          kRegValueLastSuccessfulCheckSec));
-  EXPECT_EQ(kExistingUpdateValues,
-            GetDwordValue(kApp2ClientStateKeyPathMachine,
-                          kRegValueLastUpdateTimeSec));
-}
-
-TEST_F(JobCreatorTest, CreateJobsFromResponses_UpdateForUpdateDisabledApp) {
-  const CString version_goopdate = _T("1.2.75.3");
-  const CString version_app1 = _T("1.1.2.3");
-  const CString version_app2 = _T("2.0.0.5");
-  const CString version_app3 = _T("11.0.0.5");
-  const CString version_app4 = _T("5.0.6.7");
-
-  JobCreator job_creator(true, true, &ping_);
-  job_creator.set_is_auto_update(true);
-  AppManager app_manager(true);
-
-  const DWORD kExistingUpdateValues = 0x70123456;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                    kRegValueLastSuccessfulCheckSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                    kRegValueLastUpdateTimeSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp1ClientStateKeyPathMachine,
-                                    kRegValueLastSuccessfulCheckSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp1ClientStateKeyPathMachine,
-                                    kRegValueLastUpdateTimeSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2ClientStateKeyPathMachine,
-                                    kRegValueLastSuccessfulCheckSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2ClientStateKeyPathMachine,
-                                    kRegValueLastUpdateTimeSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp3ClientStateKeyPathMachine,
-                                    kRegValueLastSuccessfulCheckSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp3ClientStateKeyPathMachine,
-                                    kRegValueLastUpdateTimeSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp4ClientStateKeyPathMachine,
-                                    kRegValueLastSuccessfulCheckSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp4ClientStateKeyPathMachine,
-                                    kRegValueLastUpdateTimeSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(
-      RegKey::SetValue(kApp1ClientsKeyPathMachine, _T("pv"), version_app1));
-  EXPECT_SUCCEEDED(
-      RegKey::SetValue(kApp2ClientsKeyPathMachine, _T("pv"), version_app2));
-
-  // Required for testing deletion of this data when updates are disabled (app1)
-  // and noupdate is returned (Omaha and app3).
-  const DWORD64 kUpdateAvailableSince =
-    GetCurrent100NSTime() - 200 * kMsPerSec * kMillisecsTo100ns;
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                    kRegValueUpdateAvailableCount,
-                                    static_cast<DWORD>(123456)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                    kRegValueUpdateAvailableSince,
-                                    kUpdateAvailableSince));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp1ClientStateKeyPathMachine,
-                                    kRegValueUpdateAvailableCount,
-                                    static_cast<DWORD>(123)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp1ClientStateKeyPathMachine,
-                                    kRegValueUpdateAvailableSince,
-                                    kUpdateAvailableSince));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2ClientStateKeyPathMachine,
-                                    kRegValueUpdateAvailableCount,
-                                    static_cast<DWORD>(2345)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp2ClientStateKeyPathMachine,
-                                    kRegValueUpdateAvailableSince,
-                                    kUpdateAvailableSince));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp3ClientStateKeyPathMachine,
-                                    kRegValueUpdateAvailableCount,
-                                    static_cast<DWORD>(456)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp3ClientStateKeyPathMachine,
-                                    kRegValueUpdateAvailableSince,
-                                    kUpdateAvailableSince));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp4ClientStateKeyPathMachine,
-                                    kRegValueUpdateAvailableCount,
-                                    static_cast<DWORD>(98)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kApp4ClientStateKeyPathMachine,
-                                    kRegValueUpdateAvailableSince,
-                                    kUpdateAvailableSince));
-  // Verify the data is set correctly.
-  DWORD update_responses(0);
-  DWORD64 time_since_first_response_ms(0);
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       kGoopdateGuid,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(123456, update_responses);
-  EXPECT_LE(200000, time_since_first_response_ms);
-  update_responses = 0;
-  time_since_first_response_ms = 0;
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       StringToGuid(kGuidApp1),
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(123, update_responses);
-  EXPECT_LE(200000, time_since_first_response_ms);
-  update_responses = 0;
-  time_since_first_response_ms = 0;
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       StringToGuid(kGuidApp2),
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(2345, update_responses);
-  EXPECT_LE(200000, time_since_first_response_ms);
-  update_responses = 0;
-  time_since_first_response_ms = 0;
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       StringToGuid(kGuidApp3),
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(456, update_responses);
-  EXPECT_LE(200000, time_since_first_response_ms);
-  update_responses = 0;
-  time_since_first_response_ms = 0;
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       StringToGuid(kGuidApp4),
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(98, update_responses);
-  EXPECT_LE(200000, time_since_first_response_ms);
-
-  ProductDataVector products;
-
-  AppData app_data_omaha;
-  app_data_omaha.set_app_guid(kGoopdateGuid);
-  app_data_omaha.set_is_machine_app(true);
-  app_data_omaha.set_version(version_goopdate);
-  ProductData product_omaha(app_data_omaha);
-  products.push_back(product_omaha);
-
-  AppData app_data1;
-  app_data1.set_app_guid(app1_guid_);
-  app_data1.set_is_machine_app(true);
-  app_data1.set_version(version_app1);
-  app_data1.set_is_update_disabled(true);
-  ProductData product1(app_data1);
-  products.push_back(product1);
-
-  AppData app_data2;
-  app_data2.set_app_guid(app2_guid_);
-  app_data2.set_is_machine_app(true);
-  app_data2.set_version(version_app2);
-  ProductData product2(app_data2);
-  products.push_back(product2);
-
-  AppData app_data3;
-  app_data3.set_app_guid(app3_guid_);
-  app_data3.set_is_machine_app(true);
-  app_data3.set_version(version_app3);
-  app_data3.set_is_update_disabled(true);
-  ProductData product3(app_data3);
-  products.push_back(product3);
-
-  AppData app_data4;
-  app_data4.set_app_guid(app4_guid_);
-  app_data4.set_is_machine_app(true);
-  app_data4.set_version(version_app4);
-  ProductData product4(app_data4);
-  products.push_back(product4);
-
-  UpdateResponses responses;
-
-  UpdateResponseData resp_data_omaha;
-  resp_data_omaha.set_guid(kGoopdateGuid);
-  resp_data_omaha.set_needs_admin(NEEDS_ADMIN_YES);
-  resp_data_omaha.set_status(kResponseStatusNoUpdate);
-  UpdateResponse resp_omaha(resp_data_omaha);
-  responses.insert(std::pair<GUID, UpdateResponse>(kGoopdateGuid, resp_omaha));
-
-  UpdateResponseData resp_data1;
-  resp_data1.set_guid(app1_guid_);
-  resp_data1.set_needs_admin(NEEDS_ADMIN_YES);
-  resp_data1.set_status(kResponseStatusOkValue);
-  UpdateResponse resp1(resp_data1);
-  responses.insert(std::pair<GUID, UpdateResponse>(app1_guid_, resp1));
-
-  UpdateResponseData resp_data2;
-  resp_data2.set_guid(app2_guid_);
-  resp_data2.set_needs_admin(NEEDS_ADMIN_YES);
-  resp_data2.set_status(kResponseStatusOkValue);
-  UpdateResponse resp2(resp_data2);
-  responses.insert(std::pair<GUID, UpdateResponse>(app2_guid_, resp2));
-
-  UpdateResponseData resp_data3;
-  resp_data3.set_guid(app3_guid_);
-  resp_data3.set_needs_admin(NEEDS_ADMIN_YES);
-  resp_data3.set_status(kResponseStatusNoUpdate);
-  UpdateResponse resp3(resp_data3);
-  responses.insert(std::pair<GUID, UpdateResponse>(app3_guid_, resp3));
-
-  UpdateResponseData resp_data4;
-  resp_data4.set_guid(app4_guid_);
-  resp_data4.set_needs_admin(NEEDS_ADMIN_YES);
-  resp_data4.set_status(kResponseStatusInternalError);
-  UpdateResponse resp4(resp_data4);
-  responses.insert(std::pair<GUID, UpdateResponse>(app4_guid_, resp4));
-
-  Jobs jobs;
-  Request ping_request(true);
-  CString event_log_text;
-  CompletionInfo completion_info;
-
-  EXPECT_SUCCEEDED(job_creator.CreateJobsFromResponses(responses,
-                                                       products,
-                                                       &jobs,
-                                                       &ping_request,
-                                                       &event_log_text,
-                                                       &completion_info));
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  // Should be a job for Resp2 only since Resp1 was had updates disabled.
-  ASSERT_EQ(1, jobs.size());
-  // Pings for the update available app, the error-internal app, and the
-  // noupdate apps. No ping for the disabled app with update available.
-  // Not sure why the noupdate apps get a ping.
-  EXPECT_EQ(4, ping_request.get_request_count());
-  EXPECT_EQ(COMPLETION_SUCCESS, completion_info.status);
-  EXPECT_EQ(0, completion_info.error_code);
-  EXPECT_TRUE(completion_info.text.IsEmpty());
-  EXPECT_FALSE(jobs[0]->is_offline());
-
-  // Sleep so that there is a time difference between the time written in the
-  // registry and now.
-  ::Sleep(20);
-
-  // Omaha.
-  // Update Stats should have been cleared because there was no update - we
-  // should not keep around old data if there is no update to apply.
-  // Successful update check set for noupdate but successful update not updated.
-  update_responses = 1;
-  time_since_first_response_ms = 1;
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       kGoopdateGuid,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(0, update_responses);
-  EXPECT_EQ(0, time_since_first_response_ms);
-
-  const uint32 last_check_sec_omaha =
-      GetDwordValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                    kRegValueLastSuccessfulCheckSec);
-  EXPECT_NE(kExistingUpdateValues, last_check_sec_omaha);
-  EXPECT_GE(now, last_check_sec_omaha);
-  EXPECT_GE(static_cast<uint32>(200), now - last_check_sec_omaha);
-  EXPECT_EQ(kExistingUpdateValues,
-            GetDwordValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                          kRegValueLastUpdateTimeSec));
-
-  // App 1.
-  // Update Stats should have been cleared because these values are used to
-  // anaylze the success of Omaha and disabled updates would break these stats.
-  // Successful update check and successful update are not set because and
-  // update is available but disabled.
-  update_responses = 1;
-  time_since_first_response_ms = 1;
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       app1_guid_,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(0, update_responses);
-  EXPECT_EQ(0, time_since_first_response_ms);
-
-  EXPECT_EQ(kExistingUpdateValues,
-            GetDwordValue(kApp1ClientStateKeyPathMachine,
-                          kRegValueLastSuccessfulCheckSec));
-  EXPECT_EQ(kExistingUpdateValues,
-            GetDwordValue(kApp1ClientStateKeyPathMachine,
-                          kRegValueLastUpdateTimeSec));
-
-  // App 2.
-  // Update Stats: responses should have been incremented and time since first
-  // response should be based on the value set above.
-  // Neither successful update check nor successful update are updated because
-  // the update has not been completed.
-  update_responses = 0;
-  time_since_first_response_ms = 0;
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       app2_guid_,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(2346, update_responses);
-  EXPECT_LT(200 * kMsPerSec + 20, time_since_first_response_ms);
-  EXPECT_GT(202 * kMsPerSec, time_since_first_response_ms);
-
-  EXPECT_EQ(kExistingUpdateValues,
-            GetDwordValue(kApp2ClientStateKeyPathMachine,
-                          kRegValueLastSuccessfulCheckSec));
-  EXPECT_EQ(kExistingUpdateValues,
-            GetDwordValue(kApp2ClientStateKeyPathMachine,
-                          kRegValueLastUpdateTimeSec));
-
-  // App 3.
-  // Update Stats should have been cleared because there was no update - we
-  // should not keep around old data if there is no update to apply.
-  // Successful update check set for noupdate but successful update not updated.
-  update_responses = 1;
-  time_since_first_response_ms = 1;
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       app3_guid_,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(0, update_responses);
-  EXPECT_EQ(0, time_since_first_response_ms);
-  const uint32 last_check_sec_app3 =
-      GetDwordValue(kApp3ClientStateKeyPathMachine,
-                    kRegValueLastSuccessfulCheckSec);
-  EXPECT_NE(kExistingUpdateValues, last_check_sec_app3);
-  EXPECT_GE(now, last_check_sec_app3);
-  EXPECT_GE(static_cast<uint32>(200), now - last_check_sec_app3);
-  EXPECT_EQ(kExistingUpdateValues,
-            GetDwordValue(kApp3ClientStateKeyPathMachine,
-                          kRegValueLastUpdateTimeSec));
-
-  // App 4.
-  // Update Stats: responses should have not been incremented and time since
-  // first response should be based on the value set above.
-  // Neither successful update check nor successful update are updated because
-  // the update has not been completed.
-  update_responses = 0;
-  time_since_first_response_ms = 0;
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       app4_guid_,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(98, update_responses);
-  EXPECT_LT(200 * kMsPerSec + 20, time_since_first_response_ms);
-  EXPECT_GT(202 * kMsPerSec, time_since_first_response_ms);
-
-  EXPECT_EQ(kExistingUpdateValues,
-            GetDwordValue(kApp4ClientStateKeyPathMachine,
-                          kRegValueLastSuccessfulCheckSec));
-  EXPECT_EQ(kExistingUpdateValues,
-            GetDwordValue(kApp4ClientStateKeyPathMachine,
-                          kRegValueLastUpdateTimeSec));
-}
-
-// This should fail for an install since it's not OK to have "no update
-// available" for clean installs
-TEST_F(JobCreatorTest, CreateJobsFromResponses_InstallFailure) {
-  const CString version_app1 = _T("1.1.2.3");
-  const CString version_app2 = _T("2.0.0.5");
-
-  JobCreator job_creator(true, false, &ping_);
-  job_creator.set_fail_if_update_not_available(true);
-  AppManager app_manager(true);
-
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kApp1ClientStateKeyPathMachine));
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kApp2ClientStateKeyPathMachine));
-
-  ProductDataVector products;
-  AppData app_data1;
-  app_data1.set_app_guid(app1_guid_);
-  app_data1.set_is_machine_app(true);
-  app_data1.set_version(version_app1);
-  ProductData product1(app_data1);
-  products.push_back(product1);
-
-  AppData app_data2;
-  app_data2.set_app_guid(app2_guid_);
-  app_data2.set_is_machine_app(true);
-  app_data2.set_version(version_app2);
-  ProductData product2(app_data2);
-  products.push_back(product2);
-
-  UpdateResponses responses;
-
-  UpdateResponseData resp_data1;
-  resp_data1.set_guid(app1_guid_);
-  resp_data1.set_needs_admin(NEEDS_ADMIN_YES);
-  resp_data1.set_status(kResponseStatusNoUpdate);
-  UpdateResponse resp1(resp_data1);
-  responses.insert(std::pair<GUID, UpdateResponse>(app1_guid_, resp1));
-
-  UpdateResponseData resp_data2;
-  resp_data2.set_guid(app2_guid_);
-  resp_data2.set_needs_admin(NEEDS_ADMIN_YES);
-  resp_data2.set_status(kResponseStatusOkValue);
-  // TODO(omaha): Add component responses here.
-  UpdateResponse resp2(resp_data2);
-  responses.insert(std::pair<GUID, UpdateResponse>(app2_guid_, resp2));
-
-  Jobs jobs;
-  Request ping_request(true);
-  CString event_log_text;
-  CompletionInfo completion_info;
-  EXPECT_FAILED(job_creator.CreateJobsFromResponses(responses,
-                                                    products,
-                                                    &jobs,
-                                                    &ping_request,
-                                                    &event_log_text,
-                                                    &completion_info));
-
-  EXPECT_EQ(0, jobs.size());
-  EXPECT_EQ(1, ping_request.get_request_count());
-  EXPECT_EQ(GOOPDATE_E_NO_UPDATE_RESPONSE, completion_info.error_code);
-  EXPECT_EQ(COMPLETION_ERROR, completion_info.status);
-  EXPECT_STREQ(_T("No update is available."), completion_info.text);
-
-  // Sleep so that there would be a time difference between the time written in
-  // the registry and now.
-  ::Sleep(20);
-
-  // There should not be any data because this is an install.
-  DWORD update_responses(1);
-  DWORD64 time_since_first_response_ms(1);
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       app1_guid_,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(0, update_responses);
-  EXPECT_EQ(0, time_since_first_response_ms);
-
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       app2_guid_,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(0, update_responses);
-  EXPECT_EQ(0, time_since_first_response_ms);
-
-  EXPECT_FALSE(RegKey::HasValue(kApp1ClientStateKeyPathMachine,
-                                kRegValueLastSuccessfulCheckSec));
-  EXPECT_FALSE(RegKey::HasValue(kApp1ClientStateKeyPathMachine,
-                                kRegValueLastUpdateTimeSec));
-  EXPECT_FALSE(RegKey::HasValue(kApp2ClientStateKeyPathMachine,
-                                kRegValueLastSuccessfulCheckSec));
-  EXPECT_FALSE(RegKey::HasValue(kApp2ClientStateKeyPathMachine,
-                                kRegValueLastUpdateTimeSec));
-}
-
-TEST_F(JobCreatorTest, CreateJobsFromResponses_InstallSuccess) {
-  const CString version_app1 = _T("1.1.2.3");
-  const CString version_app2 = _T("2.0.0.5");
-
-  JobCreator job_creator(true, false, &ping_);
-  job_creator.set_fail_if_update_not_available(true);
-  AppManager app_manager(true);
-
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kApp1ClientStateKeyPathMachine));
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kApp2ClientStateKeyPathMachine));
-
-  ProductDataVector products;
-  AppData app_data1;
-  app_data1.set_app_guid(app1_guid_);
-  app_data1.set_is_machine_app(true);
-  app_data1.set_version(version_app1);
-  ProductData product1(app_data1);
-  products.push_back(product1);
-
-  AppData app_data2;
-  app_data2.set_app_guid(app2_guid_);
-  app_data2.set_is_machine_app(true);
-  app_data2.set_version(version_app2);
-  ProductData product2(app_data2);
-  products.push_back(product2);
-
-  UpdateResponses responses;
-
-  UpdateResponseData resp_data1;
-  resp_data1.set_guid(app1_guid_);
-  resp_data1.set_needs_admin(NEEDS_ADMIN_YES);
-  resp_data1.set_status(kResponseStatusOkValue);
-  UpdateResponse resp1(resp_data1);
-  responses.insert(std::pair<GUID, UpdateResponse>(app1_guid_, resp1));
-
-  UpdateResponseData resp_data2;
-  resp_data2.set_guid(app2_guid_);
-  resp_data2.set_needs_admin(NEEDS_ADMIN_YES);
-  resp_data2.set_status(kResponseStatusOkValue);
-  // TODO(omaha): Add component responses here.
-  UpdateResponse resp2(resp_data2);
-  responses.insert(std::pair<GUID, UpdateResponse>(app2_guid_, resp2));
-
-  Jobs jobs;
-  Request ping_request(true);
-  CString event_log_text;
-  CompletionInfo completion_info;
-  EXPECT_SUCCEEDED(job_creator.CreateJobsFromResponses(responses,
-                                                       products,
-                                                       &jobs,
-                                                       &ping_request,
-                                                       &event_log_text,
-                                                       &completion_info));
-
-  ASSERT_EQ(2, jobs.size());
-  EXPECT_EQ(2, ping_request.get_request_count());
-  EXPECT_EQ(COMPLETION_SUCCESS, completion_info.status);
-  EXPECT_EQ(0, completion_info.error_code);
-  EXPECT_TRUE(completion_info.text.IsEmpty());
-  EXPECT_FALSE(jobs[0]->is_offline());
-  EXPECT_FALSE(jobs[1]->is_offline());
-
-  // Sleep so that there would be a time difference between the time written in
-  // the registry and now.
-  ::Sleep(20);
-
-  // There should not be any data because this is an install.
-  DWORD update_responses(1);
-  DWORD64 time_since_first_response_ms(1);
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       app1_guid_,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(0, update_responses);
-  EXPECT_EQ(0, time_since_first_response_ms);
-
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       app2_guid_,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(0, update_responses);
-  EXPECT_EQ(0, time_since_first_response_ms);
-
-  EXPECT_FALSE(RegKey::HasValue(kApp1ClientStateKeyPathMachine,
-                                kRegValueLastSuccessfulCheckSec));
-  EXPECT_FALSE(RegKey::HasValue(kApp1ClientStateKeyPathMachine,
-                                kRegValueLastUpdateTimeSec));
-  EXPECT_FALSE(RegKey::HasValue(kApp2ClientStateKeyPathMachine,
-                                kRegValueLastSuccessfulCheckSec));
-  EXPECT_FALSE(RegKey::HasValue(kApp2ClientStateKeyPathMachine,
-                                kRegValueLastUpdateTimeSec));
-}
-
-TEST_F(JobCreatorTest, CreateJobsFromResponses_UpdateGoopdateUpdateAvailable) {
-  const CString version_app1 = _T("1.1.2.3");
-  const CString version_goopdate = _T("1.2.75.3");
-
-  JobCreator job_creator(true, true, &ping_);
-  job_creator.set_is_auto_update(true);
-  AppManager app_manager(true);
-
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kApp1ClientStateKeyPathMachine));
-  EXPECT_SUCCEEDED(RegKey::CreateKey(MACHINE_REG_CLIENT_STATE_GOOPDATE));
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENTS_GOOPDATE,
-                                    _T("pv"),
-                                    version_goopdate));
-
-  ProductDataVector products;
-
-  AppData app_data1;
-  app_data1.set_app_guid(app1_guid_);
-  app_data1.set_is_machine_app(true);
-  app_data1.set_version(version_app1);
-  ProductData product1(app_data1);
-  products.push_back(product1);
-
-  // Add in Goopdate as one of the products.
-  AppData app_data2;
-  app_data2.set_app_guid(kGoopdateGuid);
-  app_data2.set_is_machine_app(true);
-  app_data2.set_version(version_goopdate);
-  ProductData product2(app_data2);
-  products.push_back(product2);
-  UpdateResponses responses;
-
-  // Have the first app have an update available, but it should get deferred
-  // later since there's a Goopdate update available.
-  UpdateResponseData resp_data1;
-  resp_data1.set_guid(app1_guid_);
-  resp_data1.set_needs_admin(NEEDS_ADMIN_YES);
-  resp_data1.set_status(kResponseStatusOkValue);
-  UpdateResponse resp1(resp_data1);
-  responses.insert(std::pair<GUID, UpdateResponse>(app1_guid_, resp1));
-
-  // Add in response for Goopdate, making it available.
-  UpdateResponseData resp_data2;
-  resp_data2.set_guid(kGoopdateGuid);
-  resp_data2.set_needs_admin(NEEDS_ADMIN_YES);
-  resp_data2.set_status(kResponseStatusOkValue);
-  // TODO(omaha): Add component responses here.
-  UpdateResponse resp2(resp_data2);
-  responses.insert(std::pair<GUID, UpdateResponse>(kGoopdateGuid, resp2));
-
-  Jobs jobs;
-  Request ping_request(true);
-  CString event_log_text;
-  CompletionInfo completion_info;
-
-  EXPECT_SUCCEEDED(job_creator.CreateJobsFromResponses(responses,
-                                                       products,
-                                                       &jobs,
-                                                       &ping_request,
-                                                       &event_log_text,
-                                                       &completion_info));
-
-  // Should be a job for Resp2 since Resp2 was for Goopdate.
-  ASSERT_EQ(1, jobs.size());
-  EXPECT_TRUE(::IsEqualGUID(jobs[0]->app_data().app_guid(), kGoopdateGuid));
-  EXPECT_FALSE(jobs[0]->is_offline());
-
-  // Validate the ping data that is produced by the test method.
-  EXPECT_EQ(2, ping_request.get_request_count());
-  AppRequestData goopdate_request;
-  AppRequestData other_request;
-
-  AppRequestVector::const_iterator iter = ping_request.app_requests_begin();
-  const AppRequest& app_request = *iter;
-  if (::IsEqualGUID(app_request.request_data().app_data().app_guid(),
-                    kGoopdateGuid)) {
-    goopdate_request = app_request.request_data();
-    other_request = (*++iter).request_data();
-  } else {
-    goopdate_request = (*++iter).request_data();
-    other_request = app_request.request_data();
-  }
-
-  EXPECT_EQ(1, goopdate_request.num_ping_events());
-  EXPECT_EQ(PingEvent::EVENT_UPDATE_APPLICATION_BEGIN,
-            (*goopdate_request.ping_events_begin()).event_type());
-  EXPECT_EQ(PingEvent::EVENT_RESULT_SUCCESS,
-            (*goopdate_request.ping_events_begin()).event_result());
-
-  EXPECT_EQ(1, other_request.num_ping_events());
-  EXPECT_EQ(PingEvent::EVENT_UPDATE_COMPLETE,
-            (*other_request.ping_events_begin()).event_type());
-  EXPECT_EQ(PingEvent::EVENT_RESULT_UPDATE_DEFERRED,
-            (*other_request.ping_events_begin()).event_result());
-  EXPECT_EQ(0, (*other_request.ping_events_begin()).error_code());
-  EXPECT_EQ(0, (*other_request.ping_events_begin()).extra_code1());
-
-  // Validate the completion info generated by the test method.
-  EXPECT_EQ(COMPLETION_SUCCESS, completion_info.status);
-  EXPECT_EQ(0, completion_info.error_code);
-  EXPECT_TRUE(completion_info.text.IsEmpty());
-
-  EXPECT_EQ(1, metric_worker_skipped_app_update_for_self_update.value());
-
-  // Sleep so that there is a time difference between the time written in the
-  // registry and now.
-  ::Sleep(20);
-
-  // Stats for app1 should not have been set because we did not process it.
-  DWORD update_responses(0);
-  DWORD64 time_since_first_response_ms(0);
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       app1_guid_,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(0, update_responses);
-  EXPECT_EQ(0, time_since_first_response_ms);
-
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       kGoopdateGuid,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(1, update_responses);
-  EXPECT_LT(0, time_since_first_response_ms);
-  EXPECT_GT(10 * kMsPerSec, time_since_first_response_ms);
-
-  EXPECT_FALSE(RegKey::HasValue(kApp1ClientStateKeyPathMachine,
-                                kRegValueLastSuccessfulCheckSec));
-  EXPECT_FALSE(RegKey::HasValue(kApp1ClientStateKeyPathMachine,
-                                kRegValueLastUpdateTimeSec));
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                kRegValueLastSuccessfulCheckSec));
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                kRegValueLastUpdateTimeSec));
-}
-
-// Tests that in the case of an update for GoogleUpdate only, there is no
-// ping sent on behalf of other applications that had no update in the
-// first place.
-TEST_F(JobCreatorTest, CreateJobsFromResponses_UpdateGoopdateUpdateOnly) {
-  const CString version_app1 = _T("1.1.2.3");
-  const CString version_goopdate = _T("1.2.75.3");
-
-  JobCreator job_creator(true, true, &ping_);
-  job_creator.set_is_auto_update(true);
-  AppManager app_manager(true);
-
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kApp1ClientStateKeyPathMachine));
-  EXPECT_SUCCEEDED(RegKey::CreateKey(MACHINE_REG_CLIENT_STATE_GOOPDATE));
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENTS_GOOPDATE,
-                                    _T("pv"),
-                                    version_goopdate));
-
-  ProductDataVector products;
-
-  AppData app_data1;
-  app_data1.set_app_guid(app1_guid_);
-  app_data1.set_is_machine_app(true);
-  app_data1.set_version(version_app1);
-  ProductData product1(app_data1);
-  products.push_back(product1);
-
-  // Add in Goopdate as one of the products.
-  AppData app_data2;
-  app_data2.set_app_guid(kGoopdateGuid);
-  app_data2.set_is_machine_app(true);
-  app_data2.set_version(version_goopdate);
-  ProductData product2(app_data2);
-  products.push_back(product2);
-  UpdateResponses responses;
-
-  // Have the first app have no update available, but it should get ignored
-  // later since there's a Goopdate update available.
-  UpdateResponseData resp_data1;
-  resp_data1.set_guid(app1_guid_);
-  resp_data1.set_needs_admin(NEEDS_ADMIN_YES);
-  resp_data1.set_status(kResponseStatusNoUpdate);
-  UpdateResponse resp1(resp_data1);
-  responses.insert(std::pair<GUID, UpdateResponse>(app1_guid_, resp1));
-
-  // Add in response for Goopdate, making it available.
-  UpdateResponseData resp_data2;
-  resp_data2.set_guid(kGoopdateGuid);
-  resp_data2.set_needs_admin(NEEDS_ADMIN_YES);
-  resp_data2.set_status(kResponseStatusOkValue);
-  UpdateResponse resp2(resp_data2);
-  responses.insert(std::pair<GUID, UpdateResponse>(kGoopdateGuid, resp2));
-
-  Jobs jobs;
-  Request ping_request(true);
-  CString event_log_text;
-  CompletionInfo completion_info;
-
-  EXPECT_SUCCEEDED(job_creator.CreateJobsFromResponses(responses,
-                                                       products,
-                                                       &jobs,
-                                                       &ping_request,
-                                                       &event_log_text,
-                                                       &completion_info));
-
-  // Should be a job for Resp2 since Resp2 was for Goopdate.
-  ASSERT_EQ(1, jobs.size());
-  EXPECT_TRUE(::IsEqualGUID(jobs[0]->app_data().app_guid(), kGoopdateGuid));
-  EXPECT_FALSE(jobs[0]->is_offline());
-
-  // Validate the ping data that is produced by the test method.
-  EXPECT_EQ(1, ping_request.get_request_count());
-
-  AppRequestVector::const_iterator iter = ping_request.app_requests_begin();
-  const AppRequest& app_request = *iter;
-  AppRequestData goopdate_request = app_request.request_data();
-
-  EXPECT_EQ(1, goopdate_request.num_ping_events());
-  EXPECT_EQ(PingEvent::EVENT_UPDATE_APPLICATION_BEGIN,
-            (*goopdate_request.ping_events_begin()).event_type());
-  EXPECT_EQ(PingEvent::EVENT_RESULT_SUCCESS,
-            (*goopdate_request.ping_events_begin()).event_result());
-
-  // Validate the completion info generated by the test method.
-  EXPECT_EQ(COMPLETION_SUCCESS, completion_info.status);
-  EXPECT_EQ(0, completion_info.error_code);
-  EXPECT_TRUE(completion_info.text.IsEmpty());
-
-  EXPECT_EQ(0, metric_worker_skipped_app_update_for_self_update.value());
-
-  // Sleep so that there is a time difference between the time written in the
-  // registry and now.
-  ::Sleep(20);
-
-  // Stats for app1 should not have been set because we did not process it.
-  DWORD update_responses(0);
-  DWORD64 time_since_first_response_ms(0);
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       app1_guid_,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(0, update_responses);
-  EXPECT_EQ(0, time_since_first_response_ms);
-
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       kGoopdateGuid,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  EXPECT_EQ(1, update_responses);
-  EXPECT_LT(0, time_since_first_response_ms);
-  EXPECT_GT(10 * kMsPerSec, time_since_first_response_ms);
-
-  // kRegValueLastSuccessfulCheckSec is not set for app1's "noupdate" response
-  // because it is not processed due to the Goopdate update available.
-  EXPECT_FALSE(RegKey::HasValue(kApp1ClientStateKeyPathMachine,
-                                kRegValueLastSuccessfulCheckSec));
-  EXPECT_FALSE(RegKey::HasValue(kApp1ClientStateKeyPathMachine,
-                                kRegValueLastUpdateTimeSec));
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                kRegValueLastSuccessfulCheckSec));
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                kRegValueLastUpdateTimeSec));
-}
-
-TEST_F(JobCreatorTest, UpdateResponseDataToCompletionInfo_Ok) {
-  UpdateResponseData response_data;
-  response_data.set_status(_T("ok"));
-  CompletionInfo info = UpdateResponseDataToCompletionInfo(response_data,
-                                                           _T("foo"));
-  EXPECT_EQ(COMPLETION_SUCCESS, info.status);
-  EXPECT_EQ(0, info.error_code);
-  EXPECT_TRUE(info.text.IsEmpty());
-}
-
-TEST_F(JobCreatorTest, UpdateResponseDataToCompletionInfo_NoUpdate) {
-  UpdateResponseData response_data;
-  response_data.set_status(_T("NoUpDaTe"));
-  CompletionInfo info = UpdateResponseDataToCompletionInfo(response_data,
-                                                           _T("foo"));
-  EXPECT_EQ(COMPLETION_ERROR, info.status);
-  EXPECT_EQ(GOOPDATE_E_NO_UPDATE_RESPONSE, info.error_code);
-  EXPECT_STREQ(_T("No update is available."), info.text);
-}
-
-TEST_F(JobCreatorTest, UpdateResponseDataToCompletionInfo_Restricted) {
-  UpdateResponseData response_data;
-  response_data.set_status(_T("ReStRiCtEd"));
-  CompletionInfo info = UpdateResponseDataToCompletionInfo(response_data,
-                                                           _T("foo"));
-  EXPECT_EQ(COMPLETION_ERROR, info.status);
-  EXPECT_EQ(GOOPDATE_E_RESTRICTED_SERVER_RESPONSE, info.error_code);
-  EXPECT_STREQ(_T("Access to this application is restricted."), info.text);
-}
-
-TEST_F(JobCreatorTest,
-       UpdateResponseDataToCompletionInfo_OsNotSupported) {
-  UpdateResponseData response_data;
-  response_data.set_guid(
-      StringToGuid(_T("{563CEB0C-A031-4f77-925D-590B2095DE8D}")));
-  response_data.set_status(_T("ErRoR-OsNoTsUpPoRtEd"));
-  response_data.set_error_url(
-      _T("http://foo.google.com/support/article.py?id=12345"));
-  CompletionInfo info = UpdateResponseDataToCompletionInfo(response_data,
-                                                           _T("My App"));
-  EXPECT_EQ(COMPLETION_ERROR, info.status);
-  EXPECT_EQ(GOOPDATE_E_OS_NOT_SUPPORTED, info.error_code);
-  EXPECT_STREQ(_T("My App does not support your version of Windows. ")
-               _T("<a=http://foo.google.com/support/article.py?id=12345>")
-               _T("Click here for additional information.</a>"), info.text);
-}
-
-TEST_F(JobCreatorTest,
-       UpdateResponseDataToCompletionInfo_OsNotSupported_NoOsUrl) {
-  UpdateResponseData response_data;
-  response_data.set_status(_T("ErRoR-OsNoTsUpPoRtEd"));
-  CompletionInfo info = UpdateResponseDataToCompletionInfo(response_data,
-                                                           _T("My App"));
-  EXPECT_EQ(COMPLETION_ERROR, info.status);
-  EXPECT_EQ(GOOPDATE_E_OS_NOT_SUPPORTED, info.error_code);
-  EXPECT_STREQ(_T("Server returned the following error: ErRoR-OsNoTsUpPoRtEd. ")
-               _T("Please try again later."), info.text);
-}
-
-TEST_F(JobCreatorTest, UpdateResponseDataToCompletionInfo_UnknownApp) {
-  UpdateResponseData response_data;
-  response_data.set_status(_T("eRrOr-UnKnOwNaPpLiCaTiOn"));
-  CompletionInfo info = UpdateResponseDataToCompletionInfo(response_data,
-                                                           _T("My App"));
-  EXPECT_EQ(COMPLETION_ERROR, info.status);
-  EXPECT_EQ(GOOPDATE_E_UNKNOWN_APP_SERVER_RESPONSE, info.error_code);
-  EXPECT_STREQ(_T("The installer could not install the requested application ")
-               _T("due to a server side error. Please try again later. We ")
-               _T("apologize for the inconvenience."), info.text);
-}
-
-TEST_F(JobCreatorTest, UpdateResponseDataToCompletionInfo_InternalError) {
-  UpdateResponseData response_data;
-  response_data.set_status(_T("eRrOr-InTeRnAl"));
-  CompletionInfo info = UpdateResponseDataToCompletionInfo(response_data,
-                                                           _T("My App"));
-  EXPECT_EQ(COMPLETION_ERROR, info.status);
-  EXPECT_EQ(GOOPDATE_E_INTERNAL_ERROR_SERVER_RESPONSE, info.error_code);
-  EXPECT_STREQ(_T("Server returned the following error: eRrOr-InTeRnAl. ")
-               _T("Please try again later."), info.text);
-}
-
-TEST_F(JobCreatorTest, UpdateResponseDataToCompletionInfo_HashError) {
-  UpdateResponseData response_data;
-  response_data.set_status(_T("eRrOr-HaSh"));
-  CompletionInfo info = UpdateResponseDataToCompletionInfo(response_data,
-                                                           _T("My App"));
-  EXPECT_EQ(COMPLETION_ERROR, info.status);
-  EXPECT_EQ(GOOPDATE_E_SERVER_RESPONSE_NO_HASH, info.error_code);
-  EXPECT_STREQ(_T("Server returned the following error: eRrOr-HaSh. ")
-               _T("Please try again later."), info.text);
-}
-
-TEST_F(JobCreatorTest, UpdateResponseDataToCompletionInfo_UnsupportedProtocol) {
-  UpdateResponseData response_data;
-  response_data.set_status(_T("eRrOr-UnSuPpOrTeDpRoToCoL"));
-  CompletionInfo info = UpdateResponseDataToCompletionInfo(response_data,
-                                                           _T("My App"));
-  EXPECT_EQ(COMPLETION_ERROR, info.status);
-  EXPECT_EQ(GOOPDATE_E_SERVER_RESPONSE_UNSUPPORTED_PROTOCOL, info.error_code);
-  EXPECT_STREQ(
-      _T("This installer is no longer supported. Please download a new ")
-      _T("version from Google."), info.text);
-}
-
-TEST_F(JobCreatorTest, UpdateResponseDataToCompletionInfo_UnknownResponse) {
-  UpdateResponseData response_data;
-  response_data.set_status(_T("unknown error string"));
-  CompletionInfo info = UpdateResponseDataToCompletionInfo(response_data,
-                                                           _T("My App"));
-  EXPECT_EQ(COMPLETION_ERROR, info.status);
-  EXPECT_EQ(GOOPDATE_E_UNKNOWN_SERVER_RESPONSE, info.error_code);
-  EXPECT_STREQ(_T("Server returned the following error: unknown error string. ")
-               _T("Please try again later."), info.text);
-}
-
-TEST_F(JobCreatorTest, CreateOfflineJobs_Success) {
-  // This test does not require registry hive overrides.
-  TearDown();
-
-  JobCreator job_creator(true, false, &ping_);
-  job_creator.set_fail_if_update_not_available(true);
-
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kApp1ClientStateKeyPathMachine));
-
-  ProductDataVector products;
-  AppData app_data1;
-  app_data1.set_app_guid(app1_guid_);
-  app_data1.set_display_name(_T("Test App 1"));
-  app_data1.set_is_machine_app(true);
-  app_data1.set_language(_T("en"));
-  ProductData product1(app_data1);
-  products.push_back(product1);
-
-  CString offline_manifest_path(kGuidApp1);
-  offline_manifest_path += _T(".gup");
-  offline_manifest_path = ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                          offline_manifest_path);
-  ASSERT_SUCCEEDED(File::Copy(
-      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                      _T("server_manifest_one_app.xml")),
-      offline_manifest_path,
-      true));
-
-  CString installer_exe = _T("foo_installer.exe");
-  CString installer_path = ConcatenatePath(
-                                app_util::GetCurrentModuleDirectory(),
-                                kGuidApp1);
-  ASSERT_SUCCEEDED(CreateDir(installer_path, NULL));
-  // The hash of SaveArguments_OmahaTestSigned.exe needs to be kept in sync, in
-  // server_manifest_one_app.xml, for this test to succeed.
-  ASSERT_SUCCEEDED(File::Copy(
-      ConcatenatePath(ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                      _T("unittest_support")),
-                                      _T("SaveArguments_OmahaTestSigned.exe")),
-      ConcatenatePath(installer_path, installer_exe),
-      true));
-
-  Jobs jobs;
-  Request ping_request(true);
-  CString event_log_text;
-  CompletionInfo completion_info;
-  ASSERT_SUCCEEDED(job_creator.CreateOfflineJobs(
-      app_util::GetCurrentModuleDirectory(),
-      products,
-      &jobs,
-      &ping_request,
-      &event_log_text,
-      &completion_info));
-
-  ASSERT_EQ(1, jobs.size());
-  EXPECT_EQ(1, ping_request.get_request_count());
-  EXPECT_EQ(COMPLETION_SUCCESS, completion_info.status);
-  EXPECT_EQ(0, completion_info.error_code);
-  EXPECT_TRUE(completion_info.text.IsEmpty());
-  EXPECT_EQ(SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD,
-            jobs[0]->response_data().success_action());
-  EXPECT_TRUE(jobs[0]->is_offline());
-
-  EXPECT_FALSE(RegKey::HasValue(kApp1ClientStateKeyPathMachine,
-                                kRegValueLastSuccessfulCheckSec));
-  EXPECT_FALSE(RegKey::HasValue(kApp1ClientStateKeyPathMachine,
-                                kRegValueLastUpdateTimeSec));
-
-  EXPECT_SUCCEEDED(DeleteDirectory(installer_path));
-  EXPECT_SUCCEEDED(File::Remove(offline_manifest_path));
-}
-
-TEST_F(JobCreatorTest, CreateOfflineJobs_Failure) {
-  JobCreator job_creator(true, false, &ping_);
-  job_creator.set_fail_if_update_not_available(true);
-
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kApp1ClientStateKeyPathMachine));
-
-  ProductDataVector products;
-  AppData app_data1;
-  app_data1.set_app_guid(app1_guid_);
-  app_data1.set_display_name(_T("Test App 1"));
-  app_data1.set_is_machine_app(true);
-  app_data1.set_language(_T("en"));
-  ProductData product1(app_data1);
-  products.push_back(product1);
-
-  Jobs jobs;
-  Request ping_request(true);
-  CString event_log_text;
-  CompletionInfo completion_info;
-  ASSERT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            job_creator.CreateOfflineJobs(
-                            app_util::GetCurrentModuleDirectory(),
-                            products,
-                            &jobs,
-                            &ping_request,
-                            &event_log_text,
-                            &completion_info));
-
-  EXPECT_EQ(0, jobs.size());
-  EXPECT_EQ(0, ping_request.get_request_count());
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            completion_info.error_code);
-  EXPECT_EQ(COMPLETION_ERROR, completion_info.status);
-  EXPECT_STREQ(_T(""), completion_info.text);
-
-  EXPECT_FALSE(RegKey::HasValue(kApp1ClientStateKeyPathMachine,
-                                kRegValueLastSuccessfulCheckSec));
-  EXPECT_FALSE(RegKey::HasValue(kApp1ClientStateKeyPathMachine,
-                                kRegValueLastUpdateTimeSec));
-}
-
-TEST_F(JobCreatorTest, FindOfflineFilePath_Success) {
-  CString installer_exe = _T("foo_installer.exe");
-  CString installer_path = ConcatenatePath(
-                                app_util::GetCurrentModuleDirectory(),
-                                kGuidApp1);
-  EXPECT_SUCCEEDED(CreateDir(installer_path, NULL));
-  EXPECT_SUCCEEDED(File::Copy(
-      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                      _T("unittest_support\\SaveArguments.exe")),
-      ConcatenatePath(installer_path, installer_exe),
-      true));
-
-  CString file_path;
-  EXPECT_SUCCEEDED(CallFindOfflineFilePath(
-                       app_util::GetCurrentModuleDirectory(),
-                       kGuidApp1,
-                       &file_path));
-  EXPECT_STREQ(ConcatenatePath(installer_path, installer_exe), file_path);
-
-  EXPECT_SUCCEEDED(DeleteDirectory(installer_path));
-}
-
-TEST_F(JobCreatorTest, FindOfflineFilePath_Failure) {
-  CString file_path;
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND),
-            CallFindOfflineFilePath(app_util::GetCurrentModuleDirectory(),
-                                    kGuidApp1,
-                                    &file_path));
-  EXPECT_TRUE(file_path.IsEmpty());
-}
-
-TEST_F(JobCreatorTest, ReadOfflineManifest_Success) {
-  // This test does not require registry hive overrides.
-  TearDown();
-
-  CString offline_manifest_path(kGuidApp1);
-  offline_manifest_path += _T(".gup");
-  offline_manifest_path = ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                          offline_manifest_path);
-  ASSERT_SUCCEEDED(File::Copy(
-      ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                      _T("server_manifest_one_app.xml")),
-      offline_manifest_path,
-      true));
-
-  UpdateResponse response;
-  EXPECT_SUCCEEDED(CallReadOfflineManifest(
-                       app_util::GetCurrentModuleDirectory(),
-                       kGuidApp1,
-                       &response));
-
-  EXPECT_EQ(SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD,
-            response.update_response_data().success_action());
-
-  EXPECT_SUCCEEDED(File::Remove(offline_manifest_path));
-}
-
-TEST_F(JobCreatorTest, ReadOfflineManifest_FileDoesNotExist) {
-  UpdateResponse response;
-  ASSERT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-            CallReadOfflineManifest(app_util::GetCurrentModuleDirectory(),
-                                    kGuidApp1,
-                                    &response));
-}
-
-}  // namespace omaha
diff --git a/worker/job_observer.cc b/worker/job_observer.cc
deleted file mode 100644
index a109f2e..0000000
--- a/worker/job_observer.cc
+++ /dev/null
@@ -1,358 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/worker/job_observer.h"
-#include "omaha/common/error.h"
-#include "omaha/common/sta_call.h"
-#include "omaha/worker/com_wrapper_shutdown_handler.h"
-#include "omaha/worker/ui.h"
-
-namespace omaha {
-
-JobObserverCallMethodDecorator::JobObserverCallMethodDecorator(
-    ProgressWnd* job_observer)
-      : job_observer_(job_observer),
-        progress_wnd_events_(NULL),
-        ui_thread_id_(::GetCurrentThreadId()),
-        worker_job_thread_id_(0),
-        sta_(0) {
-  ASSERT1(job_observer_);
-}
-
-HRESULT JobObserverCallMethodDecorator::Initialize() {
-  job_observer_->SetEventSink(this);
-  HRESULT hr = sta_.result();
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return job_observer_->Initialize();
-}
-
-void JobObserverCallMethodDecorator::OnShow() {
-  worker_job_thread_id_ = ::GetCurrentThreadId();
-
-  if (ui_thread_id_ != ::GetCurrentThreadId()) {
-    CallMethod(job_observer_, &ProgressWnd::OnShow);
-  } else  {
-    job_observer_->OnShow();
-  }
-}
-
-void JobObserverCallMethodDecorator::OnCheckingForUpdate() {
-  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
-
-  if (ui_thread_id_ != ::GetCurrentThreadId()) {
-    CallMethod(job_observer_, &ProgressWnd::OnCheckingForUpdate);
-  } else  {
-    job_observer_->OnCheckingForUpdate();
-  }
-}
-
-void JobObserverCallMethodDecorator::OnUpdateAvailable(
-    const TCHAR* version_string) {
-  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
-
-  if (ui_thread_id_ != ::GetCurrentThreadId()) {
-    CallMethod(job_observer_,
-               &ProgressWnd::OnUpdateAvailable,
-               version_string);
-  } else  {
-    job_observer_->OnUpdateAvailable(version_string);
-  }
-}
-
-void JobObserverCallMethodDecorator::OnWaitingToDownload() {
-  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
-
-  if (ui_thread_id_ != ::GetCurrentThreadId()) {
-    CallMethod(job_observer_, &ProgressWnd::OnWaitingToDownload);
-  } else {
-    job_observer_->OnWaitingToDownload();
-  }
-}
-
-void JobObserverCallMethodDecorator::OnDownloading(int time_remaining_ms,
-                                                   int pos) {
-  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
-
-  if (ui_thread_id_ != ::GetCurrentThreadId()) {
-    CallMethod(job_observer_,
-               &ProgressWnd::OnDownloading,
-               time_remaining_ms,
-               pos);
-  } else {
-    job_observer_->OnDownloading(time_remaining_ms, pos);
-  }
-}
-
-void JobObserverCallMethodDecorator::OnWaitingToInstall() {
-  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
-
-  if (ui_thread_id_ != ::GetCurrentThreadId()) {
-    CallMethod(job_observer_, &ProgressWnd::OnWaitingToInstall);
-  } else {
-    job_observer_->OnWaitingToInstall();
-  }
-}
-
-void JobObserverCallMethodDecorator::OnInstalling() {
-  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
-
-  if (ui_thread_id_ != ::GetCurrentThreadId()) {
-    CallMethod(job_observer_, &ProgressWnd::OnInstalling);
-  } else {
-    job_observer_->OnInstalling();
-  }
-}
-
-void JobObserverCallMethodDecorator::OnPause() {
-  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
-
-  if (ui_thread_id_ != ::GetCurrentThreadId()) {
-    CallMethod(job_observer_, &ProgressWnd::OnPause);
-  } else {
-    job_observer_->OnPause();
-  }
-}
-
-void JobObserverCallMethodDecorator::OnComplete(CompletionCodes code,
-                                                const TCHAR* text,
-                                                DWORD error_code) {
-  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
-  if (!job_observer_) {
-    // This method has been called twice.
-    return;
-  }
-
-  if (ui_thread_id_ != ::GetCurrentThreadId()) {
-    CallMethod(job_observer_,
-               &ProgressWnd::OnComplete,
-               code,
-               text,
-               error_code);
-  } else {
-    job_observer_->OnComplete(code, text, error_code);
-  }
-
-  // Unhook the observer. We do not unhook the progress_wnd_events_, because the
-  // UI can still call restart browsers or close.
-  job_observer_ = NULL;
-}
-
-void JobObserverCallMethodDecorator::SetEventSink(
-    ProgressWndEvents* event_sink) {
-  ASSERT1(event_sink);
-  progress_wnd_events_ = event_sink;
-}
-
-
-void JobObserverCallMethodDecorator::DoPause() {
-  ASSERT1(progress_wnd_events_);
-  progress_wnd_events_->DoPause();
-}
-
-void JobObserverCallMethodDecorator::DoResume() {
-  ASSERT1(progress_wnd_events_);
-  progress_wnd_events_->DoResume();
-}
-
-void JobObserverCallMethodDecorator::DoClose() {
-  if (progress_wnd_events_) {
-    progress_wnd_events_->DoClose();
-  }
-}
-
-void JobObserverCallMethodDecorator::DoRestartBrowsers() {
-  ASSERT1(progress_wnd_events_);
-  progress_wnd_events_->DoRestartBrowsers();
-}
-
-void JobObserverCallMethodDecorator::DoReboot() {
-  ASSERT1(progress_wnd_events_);
-  progress_wnd_events_->DoReboot();
-}
-
-void JobObserverCallMethodDecorator::DoLaunchBrowser(const CString& url) {
-  ASSERT1(progress_wnd_events_);
-  progress_wnd_events_->DoLaunchBrowser(url);
-}
-
-JobObserverCOMDecorator::JobObserverCOMDecorator()
-    : job_observer_(NULL),
-      progress_wnd_events_(NULL),
-      worker_job_thread_id_(0) {
-}
-
-JobObserverCOMDecorator::~JobObserverCOMDecorator() {
-  Uninitialize();
-}
-
-void JobObserverCOMDecorator::Initialize(
-    IJobObserver* job_observer,
-    WorkerComWrapperShutdownCallBack* call_back) {
-  ASSERT1(call_back);
-  ASSERT1(job_observer);
-  shutdown_callback_ = call_back;
-  job_observer_ = job_observer;
-  job_observer->SetEventSink(this);
-}
-
-void JobObserverCOMDecorator::Uninitialize() {
-}
-
-void JobObserverCOMDecorator::OnShow() {
-  ASSERT1(job_observer_);
-  worker_job_thread_id_ = ::GetCurrentThreadId();
-
-  job_observer_->OnShow();
-}
-
-void JobObserverCOMDecorator::OnCheckingForUpdate() {
-  ASSERT1(job_observer_);
-  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
-
-  job_observer_->OnCheckingForUpdate();
-}
-
-void JobObserverCOMDecorator::OnUpdateAvailable(const TCHAR* version_string) {
-  ASSERT1(job_observer_);
-  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
-
-  job_observer_->OnUpdateAvailable(version_string);
-}
-
-void JobObserverCOMDecorator::OnWaitingToDownload() {
-  ASSERT1(job_observer_);
-  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
-
-  job_observer_->OnWaitingToDownload();
-}
-
-void JobObserverCOMDecorator::OnDownloading(int time_remaining_ms, int pos) {
-  ASSERT1(job_observer_);
-  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
-
-  job_observer_->OnDownloading(time_remaining_ms, pos);
-}
-
-void JobObserverCOMDecorator::OnWaitingToInstall() {
-  ASSERT1(job_observer_);
-  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
-
-  job_observer_->OnWaitingToInstall();
-}
-
-void JobObserverCOMDecorator::OnInstalling() {
-  ASSERT1(job_observer_);
-  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
-
-  job_observer_->OnInstalling();
-}
-
-void JobObserverCOMDecorator::OnPause() {
-  ASSERT1(job_observer_);
-  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
-
-  job_observer_->OnPause();
-}
-
-void JobObserverCOMDecorator::OnComplete(CompletionCodes code,
-                                         const TCHAR* text,
-                                         DWORD) {
-  UNREFERENCED_PARAMETER(text);
-  if (!job_observer_) {
-    return;
-  }
-  ASSERT1(worker_job_thread_id_ == ::GetCurrentThreadId());
-
-  SetEventSink(NULL);
-
-  // We do not want to send Omaha strings to the application, so pass an empty
-  // string instead.
-  job_observer_->OnComplete(code, L"");
-  job_observer_ = NULL;
-
-  shutdown_callback_->ReleaseIgnoreShutdown();
-  shutdown_callback_ = NULL;
-}
-
-void JobObserverCOMDecorator::SetEventSink(ProgressWndEvents* event_sink) {
-  set_progress_wnd_events(event_sink);
-}
-
-
-STDMETHODIMP JobObserverCOMDecorator::DoPause() {
-  ProgressWndEvents* progress_events(progress_wnd_events());
-  if (!progress_events) {
-    return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL;
-  }
-
-  progress_events->DoPause();
-  return S_OK;
-}
-
-STDMETHODIMP JobObserverCOMDecorator::DoResume() {
-  ProgressWndEvents* progress_events(progress_wnd_events());
-  if (!progress_events) {
-    return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL;
-  }
-
-  progress_events->DoResume();
-  return S_OK;
-}
-
-STDMETHODIMP JobObserverCOMDecorator::DoClose() {
-  ProgressWndEvents* progress_events(progress_wnd_events());
-  if (!progress_events) {
-    return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL;
-  }
-
-  progress_events->DoClose();
-  return S_OK;
-}
-
-STDMETHODIMP JobObserverCOMDecorator::DoRestartBrowsers() {
-  ProgressWndEvents* progress_events(progress_wnd_events());
-  if (!progress_events) {
-    return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL;
-  }
-
-  progress_events->DoRestartBrowsers();
-  return S_OK;
-}
-
-STDMETHODIMP JobObserverCOMDecorator::DoReboot() {
-  ProgressWndEvents* progress_events(progress_wnd_events());
-  if (!progress_events) {
-    return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL;
-  }
-
-  progress_events->DoReboot();
-  return S_OK;
-}
-
-STDMETHODIMP JobObserverCOMDecorator::DoLaunchBrowser(const WCHAR* url) {
-  ProgressWndEvents* progress_events(progress_wnd_events());
-  if (!progress_events) {
-    return GOOPDATE_E_OBSERVER_PROGRESS_WND_EVENTS_NULL;
-  }
-
-  progress_events->DoLaunchBrowser(url);
-  return S_OK;
-}
-
-}  // namespace omaha
-
diff --git a/worker/job_observer.h b/worker/job_observer.h
deleted file mode 100644
index fa106b5..0000000
--- a/worker/job_observer.h
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_WORKER_JOB_OBSERVER_H__
-#define OMAHA_WORKER_JOB_OBSERVER_H__
-
-#include <windows.h>
-#include <atlbase.h>
-#include <atlcom.h>
-#include "base/scoped_ptr.h"
-#include "goopdate/google_update_idl.h"
-#include "omaha/common/sta.h"
-
-namespace omaha {
-
-class ProgressWnd;
-class WorkerComWrapperShutdownCallBack;
-
-// The UI generates events when interesting things happens with it,
-// mostly due to the user clicking on controls.
-class ProgressWndEvents {
- public:
-  virtual ~ProgressWndEvents() {}
-  virtual void DoPause() = 0;
-  virtual void DoResume() = 0;
-  virtual void DoClose() = 0;
-  virtual void DoRestartBrowsers() = 0;
-  virtual void DoReboot() = 0;
-  virtual void DoLaunchBrowser(const CString& url) = 0;
-};
-
-class JobObserver {
- public:
-  virtual ~JobObserver() {}
-  virtual void OnShow() = 0;
-  virtual void OnCheckingForUpdate() = 0;
-  virtual void OnUpdateAvailable(const TCHAR* version_string) = 0;
-  virtual void OnWaitingToDownload() = 0;
-  virtual void OnDownloading(int time_remaining_ms, int pos) = 0;
-  virtual void OnWaitingToInstall() = 0;
-  virtual void OnInstalling() = 0;
-  virtual void OnPause() = 0;
-  virtual void OnComplete(CompletionCodes code,
-                          const TCHAR* text,
-                          DWORD error_code) = 0;
-  virtual void SetEventSink(ProgressWndEvents* event_sink) = 0;
-  virtual void Uninitialize() = 0;
-};
-
-// Class that delegates Job progress calls to the UI, and delegates UI events
-// to the Job. Handles the case where UI thread is different than the calling
-// thread.
-class JobObserverCallMethodDecorator
-    : public JobObserver,
-      public ProgressWndEvents {
- public:
-  explicit JobObserverCallMethodDecorator(ProgressWnd* observer);
-  virtual ~JobObserverCallMethodDecorator() {}
-  HRESULT Initialize();
-
-  // JobObserver implementation.
-  virtual void OnShow();
-  virtual void OnCheckingForUpdate();
-  virtual void OnUpdateAvailable(const TCHAR* version_string);
-  virtual void OnWaitingToDownload();
-  virtual void OnDownloading(int time_remaining_ms, int pos);
-  virtual void OnWaitingToInstall();
-  virtual void OnInstalling();
-  virtual void OnPause();
-  virtual void OnComplete(CompletionCodes code,
-                          const TCHAR* text,
-                          DWORD error_code);
-  virtual void SetEventSink(ProgressWndEvents* event_sink);
-  virtual void Uninitialize() {}
-
-  // ProgressWndEvents implementation.
-  virtual void DoPause();
-  virtual void DoResume();
-  virtual void DoClose();
-  virtual void DoRestartBrowsers();
-  virtual void DoReboot();
-  virtual void DoLaunchBrowser(const CString& url);
-
- private:
-  scoped_sta sta_;
-  ProgressWnd* job_observer_;
-  ProgressWndEvents* progress_wnd_events_;
-  DWORD ui_thread_id_;
-  DWORD worker_job_thread_id_;
-};
-
-class JobObserverCOMDecorator
-  : public CComObjectRootEx<CComMultiThreadModel>,
-    public JobObserver,
-    public IProgressWndEvents {
- public:
-  BEGIN_COM_MAP(JobObserverCOMDecorator)
-    COM_INTERFACE_ENTRY(IProgressWndEvents)
-  END_COM_MAP()
-
-  JobObserverCOMDecorator();
-  virtual ~JobObserverCOMDecorator();
-  void Initialize(IJobObserver* job_observer,
-                  WorkerComWrapperShutdownCallBack* call_back);
-
-  // JobObserver implementation.
-  virtual void OnShow();
-  virtual void OnCheckingForUpdate();
-  virtual void OnUpdateAvailable(const TCHAR* version_string);
-  virtual void OnWaitingToDownload();
-  virtual void OnDownloading(int time_remaining_ms, int pos);
-  virtual void OnWaitingToInstall();
-  virtual void OnInstalling();
-  virtual void OnPause();
-  virtual void OnComplete(CompletionCodes code,
-                          const TCHAR* text,
-                          DWORD error_code);
-  virtual void SetEventSink(ProgressWndEvents* event_sink);
-  void Uninitialize();
-
-  // IProgressWndEvents.
-  STDMETHOD(DoPause)();
-  STDMETHOD(DoResume)();
-  STDMETHOD(DoClose)();
-  STDMETHOD(DoRestartBrowsers)();
-  STDMETHOD(DoReboot)();
-  STDMETHOD(DoLaunchBrowser)(const WCHAR* url);
-
- private:
-  // The DoXXX() methods can be called from multiple COM threads since the
-  // code is running in an MTA. Write access to progress_wnd_events_ member
-  // must be atomic.
-  // The OnXXX() calls on the other hand are always called from a single
-  // thread, so no synchronization is needed.
-  ProgressWndEvents* progress_wnd_events() {
-    return progress_wnd_events_;
-  }
-
-  void set_progress_wnd_events(ProgressWndEvents* progress_wnd_events) {
-    // InterlockedExchangePointer is broken due to ATL defining a function with
-    // the same name in the global namespace and hiding the Win32 API.
-    // InterlockedExchange introduces a full memory barrier.
-    ::InterlockedExchange(
-        reinterpret_cast<volatile LONG*>(&progress_wnd_events_),
-        reinterpret_cast<LONG>(progress_wnd_events));
-  }
-
-  CComPtr<IJobObserver> job_observer_;
-  ProgressWndEvents* volatile progress_wnd_events_;
-  WorkerComWrapperShutdownCallBack* shutdown_callback_;
-  DWORD thread_id_;
-  DWORD worker_job_thread_id_;
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_JOB_OBSERVER_H__
-
diff --git a/worker/job_observer_mock.h b/worker/job_observer_mock.h
deleted file mode 100644
index 88db9b6..0000000
--- a/worker/job_observer_mock.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_WORKER_JOB_OBSERVER_MOCK_H_
-#define OMAHA_WORKER_JOB_OBSERVER_MOCK_H_
-
-#include "omaha/worker/job_observer.h"
-
-namespace omaha {
-
-class JobObserverMock : public JobObserver {
- public:
-  JobObserverMock()
-      : completion_code(static_cast<CompletionCodes>(-1)),
-        completion_error_code(S_OK) {}
-  virtual void OnShow() {}
-  virtual void OnCheckingForUpdate() {}
-  virtual void OnUpdateAvailable(const TCHAR* version_string) {
-    UNREFERENCED_PARAMETER(version_string);
-  }
-  virtual void OnWaitingToDownload() {}
-  virtual void OnDownloading(int time_remaining_ms, int pos) {
-    UNREFERENCED_PARAMETER(time_remaining_ms);
-    UNREFERENCED_PARAMETER(pos);
-  }
-  virtual void OnWaitingToInstall() {}
-  virtual void OnInstalling() {}
-  virtual void OnPause() {}
-  virtual void OnComplete(CompletionCodes code,
-                          const TCHAR* text,
-                          DWORD error_code) {
-    completion_code = code;
-    completion_text = text;
-    completion_error_code = error_code;
-  }
-  virtual void SetEventSink(ProgressWndEvents* event_sink) {
-    UNREFERENCED_PARAMETER(event_sink);
-  }
-  virtual void Uninitialize() {}
-
-  CompletionCodes completion_code;
-  CString completion_text;
-  DWORD completion_error_code;
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_JOB_OBSERVER_MOCK_H_
-
diff --git a/worker/job_unittest.cc b/worker/job_unittest.cc
deleted file mode 100644
index 71bcc28..0000000
--- a/worker/job_unittest.cc
+++ /dev/null
@@ -1,1445 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/app_util.h"
-#include "omaha/common/error.h"
-#include "omaha/common/file.h"
-#include "omaha/common/path.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/common/system.h"
-#include "omaha/common/time.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/goopdate_xml_parser.h"
-#include "omaha/testing/unit_test.h"
-#include "omaha/worker/application_data.h"
-#include "omaha/worker/application_manager.h"
-#include "omaha/worker/job.h"
-#include "omaha/worker/job_observer_mock.h"
-#include "omaha/worker/ping.h"
-
-namespace omaha {
-
-namespace {
-
-/*
-void CreateTestAppData(AppData* app_data) {
-  ASSERT_TRUE(app_data != NULL);
-  app_data->set_version(_T("1.1.1.3"));
-  app_data->set_previous_version(_T("1.0.0.0"));
-  app_data->set_language(_T("abc"));
-  app_data->set_ap(_T("Test ap"));
-  app_data->set_tt_token(_T("Test TT Token"));
-  app_data->set_iid(StringToGuid(_T("{F723495F-8ACF-4746-824d-643741C797B5}")));
-  app_data->set_brand_code(_T("GOOG"));
-  app_data->set_client_id(_T("someclient"));
-  app_data->set_did_run(AppData::ACTIVE_RUN);
-  app_data->set_install_source(_T("twoclick"));
-}
-*/
-
-const TCHAR kFooGuid[] = _T("{D6B08267-B440-4C85-9F79-E195E80D9937}");
-const TCHAR kFullFooAppClientKeyPath[] =
-    _T("HKLM\\Software\\Google\\Update\\Clients\\")
-    _T("{D6B08267-B440-4C85-9F79-E195E80D9937}");
-const TCHAR kFullFooAppClientStateKeyPath[] =
-    _T("HKLM\\Software\\Google\\Update\\ClientState\\")
-    _T("{D6B08267-B440-4C85-9F79-E195E80D9937}");
-const TCHAR kSetupFooV1RelativeLocation[] =
-    _T("unittest_support\\test_foo_v1.0.101.0.msi");
-
-const TCHAR kMsiLogFormat[] = _T("%s.log");
-const TCHAR kMsiUninstallArguments[] = _T("/quiet /uninstall %s");
-const TCHAR kMsiCommand[] = _T("msiexec");
-
-const TCHAR kJobExecutable[] = _T("cmd.exe");
-const TCHAR kExecuteCommandAndTerminateSwitch[] = _T("/c %s");
-
-const TCHAR expected_iid_string[] =
-    _T("{BF66411E-8FAC-4E2C-920C-849DF562621C}");
-
-CString CreateUniqueTempDir() {
-  GUID guid(GUID_NULL);
-  EXPECT_HRESULT_SUCCEEDED(::CoCreateGuid(&guid));
-  CString unique_dir_path =
-      ConcatenatePath(app_util::GetTempDir(), GuidToString(guid));
-  EXPECT_HRESULT_SUCCEEDED(CreateDir(unique_dir_path, NULL));
-  return unique_dir_path;
-}
-
-}  // namespace
-
-class JobTest : public testing::Test {
- protected:
-  JobTest() : is_machine_(true) {}
-
-  void SetUp() {
-    // Default to an auto-update job.
-    job_.reset(new Job(true, &ping_));
-    job_->is_background_ = true;
-  }
-
-  void set_info(const CompletionInfo& info) {
-    job_->info_ = info;
-  }
-
-  void set_job_state(JobState job_state) {
-    job_->job_state_ = job_state;
-  }
-
-  void SetIsInstallJob() {
-    job_->is_update_ = false;
-    job_->is_background_ = false;
-  }
-
-  HRESULT DoCompleteJob() {
-    return job_->DoCompleteJob();
-  }
-
-  HRESULT SendStateChangePing(JobState previous_state) {
-    return job_->SendStateChangePing(previous_state);
-  }
-
-  void SetUpdateResponseDataArguments(const CString& arguments) {
-    job_->update_response_data_.set_arguments(arguments);
-  }
-
-  void SetUpdateResponseSuccessAction(SuccessfulInstallAction success_action) {
-    job_->update_response_data_.set_success_action(success_action);
-  }
-
-  void SetAppData(const AppData& app_data) {
-    job_->set_app_data(app_data);
-  }
-
-  void set_download_file_name(const CString& download_file) {
-    job_->download_file_name_ = download_file;
-  }
-
-  HRESULT UpdateRegistry(AppData* data) {
-    return job_->UpdateRegistry(data);
-  }
-
-  HRESULT UpdateJob() {
-    return job_->UpdateJob();
-  }
-
-  HRESULT DeleteJobDownloadDirectory() const {
-    return job_->DeleteJobDownloadDirectory();
-  }
-
-  void set_launch_cmd_line(const CString launch_cmd_line) {
-    job_->launch_cmd_line_ = launch_cmd_line;
-  }
-
-  bool did_launch_cmd_fail() { return job_->did_launch_cmd_fail_; }
-  void set_did_launch_cmd_fail(bool did_launch_cmd_fail) {
-    job_->did_launch_cmd_fail_ = did_launch_cmd_fail;
-  }
-
-  scoped_ptr<Job> job_;
-  Ping ping_;
-  bool is_machine_;
-};
-
-// Does not override registry hives because it would not affect the installer.
-class JobInstallFooTest : public JobTest {
- protected:
-  virtual void SetUp() {
-    JobTest::SetUp();
-
-    foo_installer_path_ = ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                          kSetupFooV1RelativeLocation);
-    ASSERT_TRUE(File::Exists(foo_installer_path_));
-
-    foo_installer_log_path_.Format(kMsiLogFormat, foo_installer_path_);
-
-    ASSERT_HRESULT_SUCCEEDED(File::Remove(foo_installer_log_path_));
-    ASSERT_FALSE(File::Exists(foo_installer_log_path_));
-
-    RegKey::DeleteKey(kFullFooAppClientKeyPath);
-    ASSERT_FALSE(RegKey::HasKey(kFullFooAppClientKeyPath));
-    RegKey::DeleteKey(kFullFooAppClientStateKeyPath);
-    ASSERT_FALSE(RegKey::HasKey(kFullFooAppClientStateKeyPath));
-  }
-
-  virtual void TearDown() {
-    RegKey::DeleteKey(kFullFooAppClientKeyPath);
-    RegKey::DeleteKey(kFullFooAppClientStateKeyPath);
-  }
-
-  AppData PopulateFooAppData() {
-    AppData app_data(StringToGuid(kFooGuid), is_machine_);
-    app_data.set_display_name(_T("Foo"));
-    app_data.set_language(_T("en"));
-    app_data.set_ap(_T("test_ap"));
-    app_data.set_iid(StringToGuid(expected_iid_string));
-    app_data.set_brand_code(_T("GOOG"));
-    app_data.set_client_id(_T("_some_partner"));
-    app_data.set_browser_type(BROWSER_IE);
-    app_data.set_usage_stats_enable(TRISTATE_TRUE);
-    return app_data;
-  }
-
-  // Verifies the values that are written to ClientState before installing.
-  // Assumes the is Foo.
-  void VerifyFooClientStateValuesWrittenBeforeInstall(bool is_first_install) {
-    CString str_value;
-    DWORD value;
-
-    if (is_first_install) {
-      EXPECT_HRESULT_SUCCEEDED(RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                                                kRegValueBrandCode,
-                                                &str_value));
-      EXPECT_STREQ(_T("GOOG"), str_value);
-      EXPECT_HRESULT_SUCCEEDED(RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                                                kRegValueClientId,
-                                                &str_value));
-      EXPECT_STREQ(_T("_some_partner"), str_value);
-      const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-      DWORD install_time(0);
-      EXPECT_SUCCEEDED(RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                                        kRegValueInstallTimeSec,
-                                        &install_time));
-      EXPECT_GE(now, install_time);
-      EXPECT_GE(static_cast<uint32>(500), now - install_time);
-    } else {
-      EXPECT_HRESULT_SUCCEEDED(RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                                                kRegValueBrandCode,
-                                                &str_value));
-      EXPECT_STREQ(_T("g00g"), str_value);
-      EXPECT_FALSE(RegKey::HasValue(kFullFooAppClientStateKeyPath,
-                                    kRegValueClientId));
-      EXPECT_FALSE(RegKey::HasValue(kFullFooAppClientStateKeyPath,
-                                    kRegValueInstallTimeSec));
-    }
-
-    EXPECT_HRESULT_SUCCEEDED(RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                                              kRegValueAdditionalParams,
-                                              &str_value));
-    EXPECT_STREQ(_T("test_ap"), str_value);
-    EXPECT_HRESULT_SUCCEEDED(RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                                              kRegValueBrowser,
-                                              &value));
-    EXPECT_EQ(BROWSER_IE, value);
-    EXPECT_HRESULT_SUCCEEDED(RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                                              _T("usagestats"),
-                                              &value));
-    EXPECT_EQ(TRISTATE_TRUE, value);
-    EXPECT_HRESULT_SUCCEEDED(RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                                              kRegValueLanguage,
-                                              &str_value));
-    EXPECT_STREQ(_T("en"), str_value);
-  }
-
-  void VerifyFooClientStateValuesWrittenBeforeInstallNotPresent(
-      bool is_brand_code_present) {
-    CString str_value;
-    DWORD value;
-
-    HRESULT hr = RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                                  kRegValueBrandCode,
-                                  &str_value);
-    if (is_brand_code_present) {
-      EXPECT_HRESULT_SUCCEEDED(hr);
-    } else {
-      EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), hr);
-    }
-    EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-              RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                               kRegValueClientId,
-                               &str_value));
-    EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-              RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                               kRegValueBrowser,
-                               &value));
-    EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-              RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                               _T("usagestats"),
-                               &value));
-    EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-              RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                               kRegValueLanguage,
-                               &str_value));
-    EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-              RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                               kRegValueAdditionalParams,
-                               &str_value));
-    EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
-              RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                               kRegValueTTToken,
-                               &str_value));
-  }
-
-  // Verifies the values that are written to ClientState after successfully
-  // installing. Assumes the is Foo.
-  void VerifyFooClientStateValuesWrittenAfterSuccessfulInstall() {
-    CString str_value;
-    EXPECT_HRESULT_SUCCEEDED(RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                                              kRegValueProductVersion,
-                                              &str_value));
-    EXPECT_STREQ(_T("1.0.101.0"), str_value);
-    // TODO(omaha): Verify language. Requires changing the MSI. Make sure the
-    // language the MSI writes is different than in PopulateFooAppData().
-    // When we do this, make sure we also have a test where the app does not
-    // write lang in Client to verify that the ClientState value is not erased.
-    EXPECT_HRESULT_SUCCEEDED(RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                                              kRegValueInstallationId,
-                                              &str_value));
-    EXPECT_STREQ(expected_iid_string, str_value);
-  }
-
-  void Install_MsiInstallerSucceeds(bool is_update,
-                                    bool is_first_install,
-                                    bool is_offline);
-  void Install_InstallerFailed_WhenNoExistingPreInstallData(bool is_update);
-  void Install_InstallerFailed_WhenExistingPreInstallData(bool is_update);
-
-  CString foo_installer_path_;
-  CString foo_installer_log_path_;
-};
-
-// TODO(omaha): Test all methods of Job
-
-// No attempt is made to delete it the directory in this case.
-TEST_F(JobTest, DeleteJobDownloadDirectory_Omaha_Test) {
-  const TCHAR* kNnonExistantDir = _T("testdirfoo");
-  CompletionInfo info(COMPLETION_SUCCESS, 0, _T(""));
-
-  set_info(info);
-  set_download_file_name(ConcatenatePath(kNnonExistantDir, _T("foo.msi")));
-
-  AppData app_data;
-  app_data.set_app_guid(kGoopdateGuid);
-  SetAppData(app_data);
-
-  ASSERT_HRESULT_SUCCEEDED(DeleteJobDownloadDirectory());
-}
-
-// The download file name is not set and no attempt to delete it is made for
-// update checks only.
-TEST_F(JobTest, DeleteJobDownloadDirectory_OnDemandUpdateCheckOnly) {
-  job_.reset(new Job(true, &ping_));
-  job_->set_is_update_check_only(true);
-
-  CompletionInfo info(COMPLETION_SUCCESS, 0, _T(""));
-
-  set_info(info);
-
-  AppData app_data;
-  app_data.set_app_guid(
-      StringToGuid(_T("{55B9A9BD-16FC-4060-B667-892B312CAAA5}")));
-  SetAppData(app_data);
-
-  ASSERT_HRESULT_SUCCEEDED(DeleteJobDownloadDirectory());
-}
-
-TEST_F(JobTest, DeleteJobDownloadDirectory_OnDemandUpdate) {
-  job_.reset(new Job(true, &ping_));
-
-  CompletionInfo info(COMPLETION_SUCCESS, 0, _T(""));
-  set_info(info);
-  const CString destination_path = CreateUniqueTempDir();
-  set_download_file_name(ConcatenatePath(destination_path, _T("foo.msi")));
-
-  AppData app_data;
-  app_data.set_app_guid(
-      StringToGuid(_T("{55B9A9BD-16FC-4060-B667-892B312CAAA5}")));
-  SetAppData(app_data);
-
-  ASSERT_HRESULT_SUCCEEDED(DeleteJobDownloadDirectory());
-  ASSERT_FALSE(File::Exists(destination_path));
-}
-
-TEST_F(JobTest, DeleteJobDownloadDirectory_Success) {
-  CompletionInfo info(COMPLETION_SUCCESS, 0, _T(""));
-  set_info(info);
-  const CString destination_path = CreateUniqueTempDir();
-  set_download_file_name(ConcatenatePath(destination_path, _T("foo.msi")));
-
-  AppData app_data;
-  app_data.set_app_guid(
-      StringToGuid(_T("{55B9A9BD-16FC-4060-B667-892B312CAAA5}")));
-  SetAppData(app_data);
-
-  ASSERT_HRESULT_SUCCEEDED(DeleteJobDownloadDirectory());
-  ASSERT_FALSE(File::Exists(destination_path));
-}
-
-TEST_F(JobTest, SendStateChangePing) {
-  if (!vista_util::IsUserAdmin()) {
-    std::wcout << _T("\tTest did not run because the user is not an admin.")
-               << std::endl;
-    return;
-  }
-
-  AppManager app_manager(is_machine_);
-  AppData app_data(StringToGuid(_T("{4F1A02DC-E965-4518-AED4-E15A3E1B1219}")),
-                   is_machine_);
-
-  app_data.set_language(_T("abc"));
-  app_data.set_ap(_T("Test ap"));
-  app_data.set_tt_token(_T("Test TT Token"));
-  app_data.set_iid(StringToGuid(_T("{C16050EA-6D4C-4275-A8EC-22D4C59E942A}")));
-  app_data.set_brand_code(_T("GOOG"));
-  app_data.set_client_id(_T("otherclient"));
-  app_data.set_display_name(_T("UnitTest"));
-  app_data.set_browser_type(BROWSER_DEFAULT);
-  job_->set_app_data(app_data);
-
-  set_job_state(JOBSTATE_INSTALLERSTARTED);
-  ASSERT_SUCCEEDED(SendStateChangePing(JOBSTATE_DOWNLOADCOMPLETED));
-
-  CompletionInfo info(COMPLETION_ERROR, 123, _T(""));
-
-  RegKey::DeleteKey(kRegistryHiveOverrideRoot);
-  OverrideRegistryHives(kRegistryHiveOverrideRoot);
-  CString app_goopdate_key_name = goopdate_utils::GetAppClientsKey(
-      is_machine_,
-      GOOPDATE_APP_ID);
-
-  RegKey goopdate_key;
-  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(app_goopdate_key_name,
-                                            kRegValueProductVersion,
-                                            _T("1.2.3.4")));
-  job_->NotifyCompleted(info);
-
-  RestoreRegistryHives();
-  RegKey::DeleteKey(kRegistryHiveOverrideRoot);
-  ASSERT_SUCCEEDED(SendStateChangePing(JOBSTATE_INSTALLERSTARTED));
-}
-
-TEST_F(JobTest, UpdateRegistry) {
-  RegKey::DeleteKey(kRegistryHiveOverrideRoot, true);
-  OverrideRegistryHivesWithExecutionPermissions(kRegistryHiveOverrideRoot);
-
-  CString app_guid = _T("{4F1A02DC-E965-4518-AED4-E15A3E1B1219}");
-  CString expected_version = _T("1.3.3.3");
-  CString previous_version = _T("1.0.0.0");
-  CString expected_lang = _T("abc");
-  CString reg_key_path = AppendRegKeyPath(MACHINE_REG_CLIENTS, app_guid);
-  ASSERT_SUCCEEDED(RegKey::CreateKey(reg_key_path));
-  ASSERT_SUCCEEDED(RegKey::SetValue(reg_key_path,
-                                    kRegValueProductVersion,
-                                    expected_version));
-  ASSERT_SUCCEEDED(RegKey::SetValue(reg_key_path,
-                                    kRegValueLanguage,
-                                    expected_lang));
-
-  AppManager app_manager(is_machine_);
-  AppData app_data(StringToGuid(app_guid), is_machine_);
-  app_data.set_version(expected_version);
-  app_data.set_previous_version(previous_version);
-  app_data.set_language(expected_lang);
-  app_data.set_ap(_T("Test ap"));
-  app_data.set_tt_token(_T("Test TT Token"));
-  app_data.set_iid(StringToGuid(_T("{C16050EA-6D4C-4275-A8EC-22D4C59E942A}")));
-  app_data.set_brand_code(_T("GOOG"));
-  app_data.set_client_id(_T("otherclient"));
-  app_data.set_display_name(_T("UnitTest"));
-  app_data.set_browser_type(BROWSER_DEFAULT);
-  app_data.set_install_source(_T("install source"));
-  job_->set_app_data(app_data);
-  set_job_state(JOBSTATE_COMPLETED);
-
-  // Call the test method.
-  AppData new_app_data;
-  ASSERT_SUCCEEDED(UpdateRegistry(&new_app_data));
-
-  // Check the results.
-  reg_key_path = AppendRegKeyPath(MACHINE_REG_CLIENT_STATE, app_guid);
-  ASSERT_SUCCEEDED(RegKey::HasKey(reg_key_path));
-
-  CString actual_previous_version;
-  ASSERT_SUCCEEDED(RegKey::GetValue(reg_key_path,
-                                    kRegValueProductVersion,
-                                    &actual_previous_version));
-  CString actual_lang;
-  ASSERT_SUCCEEDED(RegKey::GetValue(reg_key_path,
-                                    kRegValueLanguage,
-                                    &actual_lang));
-
-  // The client state registry should have been updated.
-  EXPECT_STREQ(expected_version, actual_previous_version);
-  EXPECT_STREQ(expected_lang, actual_lang);
-
-  // The job's previous version should not have changed.
-  EXPECT_STREQ(expected_version, job_->app_data().version());
-  EXPECT_STREQ(previous_version, job_->app_data().previous_version());
-
-  // new_app_data's previous_version should have been updated.
-  EXPECT_STREQ(expected_version, new_app_data.version());
-  EXPECT_STREQ(expected_version, new_app_data.previous_version());
-
-  RestoreRegistryHives();
-  ASSERT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
-}
-
-TEST_F(JobTest, UpdateJob) {
-  RegKey::DeleteKey(kRegistryHiveOverrideRoot, true);
-  OverrideRegistryHivesWithExecutionPermissions(kRegistryHiveOverrideRoot);
-
-  CString app_guid = _T("{4F1A02DC-E965-4518-AED4-E15A3E1B1219}");
-  CString expected_version = _T("1.3.3.3");
-  CString previous_version = _T("1.0.0.0");
-  CString expected_lang = _T("abc");
-  CString reg_key_path = AppendRegKeyPath(MACHINE_REG_CLIENTS, app_guid);
-  ASSERT_SUCCEEDED(RegKey::CreateKey(reg_key_path));
-  ASSERT_SUCCEEDED(RegKey::SetValue(reg_key_path,
-                                    kRegValueProductVersion,
-                                    expected_version));
-  ASSERT_SUCCEEDED(RegKey::SetValue(reg_key_path,
-                                    kRegValueLanguage,
-                                    expected_lang));
-
-  AppManager app_manager(is_machine_);
-  AppData app_data(StringToGuid(app_guid), is_machine_);
-  app_data.set_version(_T("4.5.6.6"));
-  app_data.set_previous_version(previous_version);
-  app_data.set_language(expected_lang);
-  app_data.set_ap(_T("Test ap"));
-  app_data.set_tt_token(_T("Test TT Token"));
-  app_data.set_iid(
-      StringToGuid(_T("{C16050EA-6D4C-4275-A8EC-22D4C59E942A}")));
-  app_data.set_brand_code(_T("GOOG"));
-  app_data.set_client_id(_T("otherclient"));
-  app_data.set_display_name(_T("UnitTest"));
-  app_data.set_browser_type(BROWSER_DEFAULT);
-  app_data.set_install_source(_T("install source"));
-  job_->set_app_data(app_data);
-  set_job_state(JOBSTATE_COMPLETED);
-
-  // Call the test method.
-  ASSERT_SUCCEEDED(UpdateJob());
-
-  // Check the results.
-  reg_key_path = AppendRegKeyPath(MACHINE_REG_CLIENT_STATE, app_guid);
-  CString version;
-  EXPECT_HRESULT_FAILED(RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                         kRegValueProductVersion,
-                                         &version));
-
-  // The job's information should have been changed.
-  EXPECT_STREQ(expected_version, job_->app_data().version());
-  EXPECT_STREQ(previous_version, job_->app_data().previous_version());
-
-  RestoreRegistryHives();
-  ASSERT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
-}
-
-
-// The use of kGoogleUpdateAppId is the key to this test.
-// Overrides the registry hives.
-TEST_F(JobTest, Install_UpdateOmahaSucceeds) {
-  if (!vista_util::IsUserAdmin()) {
-    std::wcout << _T("\tTest did not run because the user is not an admin.")
-               << std::endl;
-    return;
-  }
-
-  RegKey::DeleteKey(kRegistryHiveOverrideRoot, true);
-  OverrideRegistryHivesWithExecutionPermissions(kRegistryHiveOverrideRoot);
-
-  CString arguments;
-  arguments.Format(kExecuteCommandAndTerminateSwitch, _T("echo hi"));
-
-  AppData app_data(kGoopdateGuid, is_machine_);
-  job_->set_download_file_name(kJobExecutable);
-  job_->set_app_data(app_data);
-  SetUpdateResponseDataArguments(arguments);
-
-  CString expected_version(_T("0.9.69.5"));
-  CString expected_lang(_T("en"));
-
-  // Because we don't actually run the Omaha installer, we need to make sure
-  // its Clients key and pv value exist to avoid an error.
-  ASSERT_SUCCEEDED(RegKey::CreateKey(MACHINE_REG_CLIENTS_GOOPDATE));
-  ASSERT_TRUE(RegKey::HasKey(MACHINE_REG_CLIENTS_GOOPDATE));
-  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENTS_GOOPDATE,
-                                            kRegValueProductVersion,
-                                            expected_version));
-  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENTS_GOOPDATE,
-                                            kRegValueLanguage,
-                                            expected_lang));
-
-  ASSERT_FALSE(RegKey::HasKey(MACHINE_REG_CLIENT_STATE_GOOPDATE));
-
-  set_job_state(JOBSTATE_DOWNLOADCOMPLETED);
-  EXPECT_HRESULT_SUCCEEDED(job_->Install());
-
-  EXPECT_EQ(JOBSTATE_COMPLETED, job_->job_state());
-
-  EXPECT_EQ(COMPLETION_SUCCESS, job_->info().status);
-  EXPECT_EQ(S_OK, job_->info().error_code);
-  // The user never sees this, but it is odd we put this text in the structure.
-  EXPECT_STREQ(_T("Thanks for installing ."), job_->info().text);
-
-  EXPECT_TRUE(RegKey::HasKey(MACHINE_REG_CLIENTS_GOOPDATE));
-  EXPECT_TRUE(RegKey::HasKey(MACHINE_REG_CLIENT_STATE_GOOPDATE));
-
-  CString version;
-  EXPECT_HRESULT_SUCCEEDED(RegKey::GetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                            kRegValueProductVersion,
-                                            &version));
-  EXPECT_STREQ(expected_version, version);
-
-  RestoreRegistryHives();
-  ASSERT_HRESULT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
-}
-
-// Update values should not be changed because we do not know whether
-// self-updates have succeeded yet.
-// Successful update and check values are not changed either.
-TEST_F(JobTest, Install_SuccessfulOmahaUpdateDoesNotClearUpdateAvailableStats) {
-  is_machine_ = false;
-
-  RegKey::DeleteKey(kRegistryHiveOverrideRoot, true);
-  OverrideRegistryHivesWithExecutionPermissions(kRegistryHiveOverrideRoot);
-
-  CString arguments;
-  arguments.Format(kExecuteCommandAndTerminateSwitch, _T("echo hi"));
-
-  AppData app_data(kGoopdateGuid, is_machine_);
-  job_->set_download_file_name(kJobExecutable);
-  job_->set_app_data(app_data);
-  SetUpdateResponseDataArguments(arguments);
-
-  CString expected_version(_T("0.9.69.5"));
-  CString expected_lang(_T("en"));
-
-  // Because we don't actually run the Omaha installer, we need to make sure
-  // its Clients key and pv value exist to avoid an error.
-  ASSERT_SUCCEEDED(RegKey::CreateKey(USER_REG_CLIENTS_GOOPDATE));
-  ASSERT_TRUE(RegKey::HasKey(USER_REG_CLIENTS_GOOPDATE));
-  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENTS_GOOPDATE,
-                                            kRegValueProductVersion,
-                                            expected_version));
-  ASSERT_HRESULT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENTS_GOOPDATE,
-                                            kRegValueLanguage,
-                                            expected_lang));
-
-  // Set update values so we can verify they are not modified or deleted.
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("UpdateAvailableCount"),
-                                    static_cast<DWORD>(123456)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("UpdateAvailableSince"),
-                                    static_cast<DWORD64>(9876543210)));
-  const DWORD kExistingUpdateValues = 0x70123456;
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                                    kRegValueLastSuccessfulCheckSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                                    kRegValueLastUpdateTimeSec,
-                                    kExistingUpdateValues));
-
-  set_job_state(JOBSTATE_DOWNLOADCOMPLETED);
-  EXPECT_HRESULT_SUCCEEDED(job_->Install());
-
-  EXPECT_EQ(JOBSTATE_COMPLETED, job_->job_state());
-
-  EXPECT_EQ(COMPLETION_SUCCESS, job_->info().status);
-  EXPECT_EQ(S_OK, job_->info().error_code);
-  // The user never sees this, but it is odd we put this text in the structure.
-  EXPECT_STREQ(_T("Thanks for installing ."), job_->info().text);
-
-  EXPECT_TRUE(RegKey::HasKey(USER_REG_CLIENTS_GOOPDATE));
-  EXPECT_TRUE(RegKey::HasKey(USER_REG_CLIENT_STATE_GOOPDATE));
-
-  CString version;
-  EXPECT_HRESULT_SUCCEEDED(RegKey::GetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                                            kRegValueProductVersion,
-                                            &version));
-  EXPECT_STREQ(expected_version, version);
-
-  DWORD update_available_count(0);
-  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("UpdateAvailableCount"),
-                                    &update_available_count));
-  EXPECT_EQ(123456, update_available_count);
-
-  DWORD64 update_available_since_time(0);
-  EXPECT_SUCCEEDED(RegKey::GetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("UpdateAvailableSince"),
-                                    &update_available_since_time));
-  EXPECT_EQ(9876543210, update_available_since_time);
-  EXPECT_EQ(kExistingUpdateValues,
-            GetDwordValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                          kRegValueLastSuccessfulCheckSec));
-  EXPECT_EQ(kExistingUpdateValues,
-            GetDwordValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                          kRegValueLastUpdateTimeSec));
-
-  RestoreRegistryHives();
-  ASSERT_HRESULT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
-}
-
-TEST_F(JobTest, GetInstallerData) {
-  UpdateResponses responses;
-  CString file_name(ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                    _T("server_manifest.xml")));
-  GUID guid = StringToGuid(_T("{D6B08267-B440-4C85-9F79-E195E80D9937}"));
-
-  const TCHAR* kVerboseLog = _T("\n  {\n    \"distribution\": {\n      ")
-                             _T("\"verbose_logging\": true\n    }\n  }\n  ");
-  const TCHAR* kSkipFirstRun = _T("{\n    \"distribution\": {\n      \"")
-                               _T("skip_first_run_ui\": true,\n    }\n  }\n  ");
-  CString skip_first_run_encoded;
-  EXPECT_SUCCEEDED(WideStringToUtf8UrlEncodedString(kSkipFirstRun,
-                                                    &skip_first_run_encoded));
-
-  ASSERT_SUCCEEDED(GoopdateXmlParser::ParseManifestFile(file_name, &responses));
-  UpdateResponseData response_data = responses[guid].update_response_data();
-  job_->set_update_response_data(response_data);
-
-  // Only set install_data_index to a valid value.
-  AppData app_data1(guid, is_machine_);
-  app_data1.set_install_data_index(_T("verboselogging"));
-  job_->set_app_data(app_data1);
-
-  CString installer_data1;
-  EXPECT_SUCCEEDED(job_->GetInstallerData(&installer_data1));
-  EXPECT_STREQ(kVerboseLog, installer_data1);
-
-  // Set both installer_data and install_data_index to valid values.
-  AppData app_data2(guid, is_machine_);
-  app_data2.set_encoded_installer_data(skip_first_run_encoded);
-  app_data2.set_install_data_index(_T("verboselogging"));
-  job_->set_app_data(app_data2);
-
-  CString installer_data2;
-  EXPECT_SUCCEEDED(job_->GetInstallerData(&installer_data2));
-  EXPECT_STREQ(kSkipFirstRun, installer_data2);
-
-  // Set installer_data to invalid value, and install_data_index to valid value.
-  AppData app_data3(guid, is_machine_);
-  app_data3.set_encoded_installer_data(_T("%20%20"));
-  app_data3.set_install_data_index(_T("verboselogging"));
-  job_->set_app_data(app_data3);
-
-  CString installer_data3;
-  EXPECT_EQ(GOOPDATE_E_INVALID_INSTALLER_DATA_IN_APPARGS,
-            job_->GetInstallerData(&installer_data3));
-
-  // Set installer_data to valid value, and install_data_index to invalid value.
-  AppData app_data4(guid, is_machine_);
-  app_data4.set_encoded_installer_data(skip_first_run_encoded);
-  app_data4.set_install_data_index(_T("foobar"));
-  job_->set_app_data(app_data4);
-
-  CString installer_data4;
-  EXPECT_SUCCEEDED(job_->GetInstallerData(&installer_data4));
-  EXPECT_STREQ(kSkipFirstRun, installer_data4);
-
-  // Set only install_data_index to invalid value.
-  AppData app_data5(guid, is_machine_);
-  app_data5.set_install_data_index(_T("foobar"));
-  job_->set_app_data(app_data5);
-
-  CString installer_data5;
-  EXPECT_EQ(GOOPDATE_E_INVALID_INSTALL_DATA_INDEX,
-            job_->GetInstallerData(&installer_data5));
-
-  // Set neither installer_data nor install_data_index.
-  AppData app_data6(guid, is_machine_);
-  job_->set_app_data(app_data6);
-
-  CString installer_data6;
-  EXPECT_SUCCEEDED(job_->GetInstallerData(&installer_data6));
-  EXPECT_TRUE(installer_data6.IsEmpty());
-}
-
-// It would be nice if the Foo installer actually used the installer data as a
-// way to verify the data file is passed correctly. Alternatively, we could mock
-// the installer execution.
-TEST_F(JobInstallFooTest, InstallerData_ValidIndex) {
-  if (!vista_util::IsUserAdmin()) {
-    std::wcout << _T("\tTest did not run because the user is not an admin.")
-               << std::endl;
-    return;
-  }
-
-  UpdateResponses responses;
-  CString file_name(ConcatenatePath(app_util::GetCurrentModuleDirectory(),
-                                    _T("server_manifest.xml")));
-  GUID guid = StringToGuid(_T("{D6B08267-B440-4C85-9F79-E195E80D9937}"));
-
-  ASSERT_SUCCEEDED(GoopdateXmlParser::ParseManifestFile(file_name, &responses));
-  UpdateResponseData response_data = responses[guid].update_response_data();
-  job_->set_update_response_data(response_data);
-
-  AppData app_data(PopulateFooAppData());
-  app_data.set_install_data_index(_T("verboselogging"));
-  job_->set_download_file_name(foo_installer_path_);
-  job_->set_app_data(app_data);
-
-  set_job_state(JOBSTATE_DOWNLOADCOMPLETED);
-  EXPECT_HRESULT_SUCCEEDED(job_->Install());
-
-  EXPECT_EQ(JOBSTATE_COMPLETED, job_->job_state());
-
-  EXPECT_EQ(COMPLETION_SUCCESS, job_->info().status);
-  EXPECT_EQ(S_OK, job_->info().error_code);
-  EXPECT_STREQ(_T("Thanks for installing Foo."), job_->info().text);
-}
-
-// Set installer_data to invalid value, and install_data_index to valid value.
-TEST_F(JobInstallFooTest, InstallerData_InvalidData) {
-  if (!vista_util::IsUserAdmin()) {
-    std::wcout << _T("\tTest did not run because the user is not an admin.")
-               << std::endl;
-    return;
-  }
-
-  AppData app_data(PopulateFooAppData());
-  app_data.set_encoded_installer_data(_T("%20%20"));
-  app_data.set_install_data_index(_T("verboselogging"));
-  job_->set_app_data(app_data);
-
-  set_job_state(JOBSTATE_DOWNLOADCOMPLETED);
-  EXPECT_EQ(GOOPDATE_E_INVALID_INSTALLER_DATA_IN_APPARGS, job_->Install());
-
-  EXPECT_EQ(JOBSTATE_COMPLETED, job_->job_state());
-
-  EXPECT_EQ(COMPLETION_ERROR, job_->info().status);
-  EXPECT_EQ(GOOPDATE_E_INVALID_INSTALLER_DATA_IN_APPARGS,
-            job_->info().error_code);
-  EXPECT_STREQ(
-      _T("Installation failed. Please try again. Error code = 0x8004090a"),
-      job_->info().text);
-}
-
-TEST_F(JobInstallFooTest, InstallerData_NonExistentIndex) {
-  if (!vista_util::IsUserAdmin()) {
-    std::wcout << _T("\tTest did not run because the user is not an admin.")
-               << std::endl;
-    return;
-  }
-
-  AppData app_data(PopulateFooAppData());
-  app_data.set_install_data_index(_T("foobar"));
-  job_->set_app_data(app_data);
-
-  set_job_state(JOBSTATE_DOWNLOADCOMPLETED);
-  EXPECT_EQ(GOOPDATE_E_INVALID_INSTALL_DATA_INDEX, job_->Install());
-
-  EXPECT_EQ(JOBSTATE_COMPLETED, job_->job_state());
-
-  EXPECT_EQ(COMPLETION_ERROR, job_->info().status);
-  EXPECT_EQ(GOOPDATE_E_INVALID_INSTALL_DATA_INDEX, job_->info().error_code);
-  EXPECT_STREQ(
-      _T("Installation failed. Please try again. Error code = 0x80040909"),
-      job_->info().text);
-}
-
-/*
-// TODO(omaha): Adapt into a test for SendStateChangePing by mocking ping.
-TEST_F(JobTest, BuildPingRequestFromJob_PopulatesAppRequest) {
-  const TCHAR* const kGuid = _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}");
-  AppData app_data(StringToGuid(kGuid), true);
-  CreateTestAppData(&app_data);
-  job_->set_app_data(app_data);
-  set_job_state(JOBSTATE_COMPLETED);
-
-  CompletionInfo info(COMPLETION_INSTALLER_ERROR_MSI, 0x12345678, _T("foo"));
-  set_info(info);
-
-  Request req(true);
-  BuildPingRequestFromJob(&req);
-  ASSERT_EQ(1, req.get_app_count());
-
-  const AppRequest* app_request = req.GetApp(StringToGuid(kGuid));
-  EXPECT_STREQ(_T("{21CD0965-0B0E-47CF-B421-2D191C16C0E2}"),
-               GuidToString(app_request->guid()));
-  EXPECT_EQ(_T("1.1.1.3"), app_request->version());
-  EXPECT_EQ(_T("abc"), app_request->language());
-  EXPECT_EQ(AppData::ACTIVE_UNKNOWN, app_request->active());
-  EXPECT_TRUE(app_request->tag().IsEmpty());
-  EXPECT_STREQ(_T("{F723495F-8ACF-4746-824D-643741C797B5}"),
-               GuidToString(app_request->installation_id()));
-  EXPECT_EQ(_T("GOOG"), app_request->brand_code());
-  EXPECT_EQ(_T("someclient"), app_request->client_id());
-  EXPECT_EQ(_T("twoclick"), app_request->install_source());
-
-  ASSERT_TRUE(app_request->events_end() == ++app_request->events_begin());
-  const AppEvent* app_event = *app_request->events_begin();
-  EXPECT_EQ(AppEvent::EVENT_UPDATE_COMPLETE, app_event->event_type());
-  EXPECT_EQ(AppEvent::EVENT_RESULT_INSTALLER_ERROR_MSI,
-            app_event->event_result());
-  EXPECT_EQ(0x12345678, app_event->error_code());
-  EXPECT_EQ(0, app_event->extra_code1());
-  EXPECT_EQ(_T("1.0.0.0"), app_event->previous_version());
-}
-
-// TODO(omaha): Adapt into a test for CreateRequestFromProducts
-TEST_F(JobTest, BuildRequest_PopulatesAppRequest) {
-  const TCHAR* const kGuid = _T("{21CD0965-0B0E-47cf-B421-2D191C16C0E2}");
-  AppData app_data(StringToGuid(kGuid), true);
-  CreateTestAppData(&app_data);
-  job_->set_app_data(app_data);
-
-  Request req(true);
-  ASSERT_SUCCEEDED(job_->BuildRequest(&req));
-  ASSERT_EQ(1, req.get_app_count());
-
-  const AppRequest* app_request = req.GetApp(StringToGuid(kGuid));
-  EXPECT_STREQ(_T("{21CD0965-0B0E-47CF-B421-2D191C16C0E2}"),
-               GuidToString(app_request->guid()));
-  EXPECT_EQ(_T("1.1.1.3"), app_request->version());
-  EXPECT_EQ(_T("abc"), app_request->language());
-  EXPECT_EQ(AppData::ACTIVE_RUN, app_request->active());
-  EXPECT_EQ(_T("Test ap"), app_request->tag());
-  EXPECT_STREQ(_T("{F723495F-8ACF-4746-824D-643741C797B5}"),
-               GuidToString(app_request->installation_id()));
-  EXPECT_EQ(_T("GOOG"), app_request->brand_code());
-  EXPECT_EQ(_T("someclient"), app_request->client_id());
-  EXPECT_EQ(_T("twoclick"), app_request->install_source());
-}
-
-// TODO(omaha): Move to some other test file.
-TEST(RequestTest, TestInitialized) {
-  AppRequest app_request;
-  EXPECT_TRUE(::IsEqualGUID(GUID_NULL, app_request.guid()));
-  EXPECT_TRUE(app_request.version().IsEmpty());
-  EXPECT_TRUE(app_request.language().IsEmpty());
-  EXPECT_EQ(AppData::ACTIVE_UNKNOWN, app_request.active());
-  EXPECT_TRUE(app_request.tag().IsEmpty());
-  EXPECT_TRUE(::IsEqualGUID(GUID_NULL, app_request.installation_id()));
-  EXPECT_TRUE(app_request.brand_code().IsEmpty());
-  EXPECT_TRUE(app_request.client_id().IsEmpty());
-  EXPECT_TRUE(app_request.install_source().IsEmpty());
-}
-*/
-
-void JobInstallFooTest::Install_MsiInstallerSucceeds(bool is_update,
-                                                     bool is_first_install,
-                                                     bool is_offline) {
-  ASSERT_TRUE(!is_update || !is_offline);
-
-  const DWORD kExistingUpdateValues = 0x70123456;
-
-  // TODO(omaha): Use UserFoo instead, change is_machine in the base class,
-  // and remove all IsUserAdmin checks.
-  if (!vista_util::IsUserAdmin()) {
-    std::wcout << _T("\tTest did not run because the user is not an admin.")
-               << std::endl;
-    return;
-  }
-
-#ifdef _DEBUG
-  if (!is_update) {
-    // Event::WriteEvent() expects Omaha's version to exist.
-    // Write it if it doesn't exist.
-    ConfigManager& config_mgr = *ConfigManager::Instance();
-    CString key_name = config_mgr.registry_clients_goopdate(is_machine_);
-    if (!RegKey::HasValue(key_name, kRegValueLanguage)) {
-      EXPECT_HRESULT_SUCCEEDED(
-          RegKey::SetValue(key_name, kRegValueLanguage, _T("it")));
-    }
-    if (!RegKey::HasValue(key_name, kRegValueProductVersion)) {
-      EXPECT_HRESULT_SUCCEEDED(
-          RegKey::SetValue(key_name, kRegValueProductVersion, _T("0.1.0.0")));
-    }
-  }
-#endif
-
-  if (!is_update) {
-    SetIsInstallJob();
-  }
-
-  if (!is_first_install) {
-    // Make it appear to Omaha that Foo was already installed. This does not
-    // affect the installer.
-    EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kFullFooAppClientStateKeyPath,
-                                              kRegValueBrandCode,
-                                              _T("g00g")));
-
-    // Set update available stats so can verify they are deleted.
-    EXPECT_SUCCEEDED(RegKey::SetValue(kFullFooAppClientStateKeyPath,
-                                      _T("UpdateAvailableCount"),
-                                      static_cast<DWORD>(123456)));
-    EXPECT_SUCCEEDED(RegKey::SetValue(kFullFooAppClientStateKeyPath,
-                                      _T("UpdateAvailableSince"),
-                                      static_cast<DWORD64>(9876543210)));
-    EXPECT_SUCCEEDED(RegKey::SetValue(kFullFooAppClientStateKeyPath,
-                                      kRegValueLastSuccessfulCheckSec,
-                                      kExistingUpdateValues));
-    EXPECT_SUCCEEDED(RegKey::SetValue(kFullFooAppClientStateKeyPath,
-                                      kRegValueLastUpdateTimeSec,
-                                      kExistingUpdateValues));
-  }
-
-  job_->set_is_offline(is_offline);
-
-  AppData app_data(PopulateFooAppData());
-  job_->set_download_file_name(foo_installer_path_);
-  job_->set_app_data(app_data);
-
-  set_job_state(JOBSTATE_DOWNLOADCOMPLETED);
-  EXPECT_HRESULT_SUCCEEDED(job_->Install());
-  const uint32 now = Time64ToInt32(GetCurrent100NSTime());
-
-  EXPECT_EQ(JOBSTATE_COMPLETED, job_->job_state());
-
-  EXPECT_EQ(COMPLETION_SUCCESS, job_->info().status);
-  EXPECT_EQ(S_OK, job_->info().error_code);
-  EXPECT_STREQ(_T("Thanks for installing Foo."), job_->info().text);
-
-  EXPECT_TRUE(File::Exists(foo_installer_log_path_));
-
-  EXPECT_TRUE(RegKey::HasKey(kFullFooAppClientKeyPath));
-  EXPECT_TRUE(RegKey::HasKey(kFullFooAppClientStateKeyPath));
-
-  if (is_update) {
-    VerifyFooClientStateValuesWrittenBeforeInstallNotPresent(!is_first_install);
-  } else {
-    VerifyFooClientStateValuesWrittenBeforeInstall(is_first_install);
-  }
-  VerifyFooClientStateValuesWrittenAfterSuccessfulInstall();
-
-  EXPECT_FALSE(RegKey::HasValue(kFullFooAppClientStateKeyPath,
-                                _T("UpdateAvailableCount")));
-  EXPECT_FALSE(RegKey::HasValue(kFullFooAppClientStateKeyPath,
-                                _T("UpdateAvailableSince")));
-  if (is_update) {
-    // Verify update values updated.
-    const uint32 last_check_sec =
-        GetDwordValue(kFullFooAppClientStateKeyPath,
-                      kRegValueLastSuccessfulCheckSec);
-    EXPECT_NE(kExistingUpdateValues, last_check_sec);
-    EXPECT_GE(now, last_check_sec);
-    EXPECT_GE(static_cast<uint32>(200), now - last_check_sec);
-
-    const uint32 last_update_sec = GetDwordValue(kFullFooAppClientStateKeyPath,
-                                                 kRegValueLastUpdateTimeSec);
-    EXPECT_NE(kExistingUpdateValues, last_update_sec);
-    EXPECT_GE(now, last_update_sec);
-    EXPECT_GE(static_cast<uint32>(200), now - last_update_sec);
-  } else {
-    // LastSuccessfulCheckSec is written for online installs but never cleared.
-    if (!is_offline) {
-      const uint32 last_check_sec =
-          GetDwordValue(kFullFooAppClientStateKeyPath,
-                        kRegValueLastSuccessfulCheckSec);
-      EXPECT_NE(kExistingUpdateValues, last_check_sec);
-      EXPECT_GE(now, last_check_sec);
-      EXPECT_GE(static_cast<uint32>(200), now - last_check_sec);
-    } else if (!is_first_install) {
-      EXPECT_EQ(kExistingUpdateValues,
-                GetDwordValue(kFullFooAppClientStateKeyPath,
-                              kRegValueLastSuccessfulCheckSec));
-    } else {
-      EXPECT_FALSE(RegKey::HasValue(kFullFooAppClientStateKeyPath,
-                                    kRegValueLastSuccessfulCheckSec));
-    }
-
-    // kRegValueLastUpdateTimeSec is never written for installs.
-    if (!is_first_install) {
-      EXPECT_EQ(kExistingUpdateValues,
-                GetDwordValue(kFullFooAppClientStateKeyPath,
-                              kRegValueLastUpdateTimeSec));
-    } else {
-      EXPECT_FALSE(RegKey::HasValue(kFullFooAppClientStateKeyPath,
-                                    kRegValueLastUpdateTimeSec));
-    }
-  }
-
-  CString uninstall_arguments;
-  uninstall_arguments.Format(kMsiUninstallArguments, foo_installer_path_);
-  EXPECT_HRESULT_SUCCEEDED(System::ShellExecuteProcess(kMsiCommand,
-                                                       uninstall_arguments,
-                                                       NULL,
-                                                       NULL));
-}
-
-TEST_F(JobInstallFooTest, Install_MsiInstallerSucceeds_FirstInstall_Online) {
-  Install_MsiInstallerSucceeds(false, true, false);
-}
-
-TEST_F(JobInstallFooTest, Install_MsiInstallerSucceeds_OverInstall_Online) {
-  Install_MsiInstallerSucceeds(false, false, false);
-}
-
-TEST_F(JobInstallFooTest, Install_MsiInstallerSucceeds_FirstInstall_Offline) {
-  Install_MsiInstallerSucceeds(false, true, true);
-}
-
-TEST_F(JobInstallFooTest, Install_MsiInstallerSucceeds_OverInstall_Offline) {
-  Install_MsiInstallerSucceeds(false, false, true);
-}
-
-TEST_F(JobInstallFooTest,
-       Install_MsiInstallerSucceeds_UpdateWhenNoExistingPreInstallData) {
-  // AppManager::ClearUpdateAvailableStats() asserts that key open succeeds.
-  EXPECT_SUCCEEDED(RegKey::CreateKey(kFullFooAppClientStateKeyPath));
-  Install_MsiInstallerSucceeds(true, true, false);
-}
-
-TEST_F(JobInstallFooTest,
-       Install_MsiInstallerSucceeds_UpdateWhenExistingPreInstallData) {
-  Install_MsiInstallerSucceeds(true, false, false);
-}
-
-
-void JobInstallFooTest::Install_InstallerFailed_WhenNoExistingPreInstallData(
-    bool is_update) {
-  if (!vista_util::IsUserAdmin()) {
-    std::wcout << _T("\tTest did not run because the user is not an admin.")
-               << std::endl;
-    return;
-  }
-
-  if (!is_update) {
-    SetIsInstallJob();
-  }
-
-  RegKey::DeleteKey(kRegistryHiveOverrideRoot, true);
-  OverrideRegistryHivesWithExecutionPermissions(kRegistryHiveOverrideRoot);
-#ifdef _DEBUG
-  // Event::WriteEvent() expects Omaha's language and version to exist.
-  ConfigManager& config_mgr = *ConfigManager::Instance();
-  CString key_name = config_mgr.registry_clients_goopdate(is_machine_);
-  EXPECT_HRESULT_SUCCEEDED(
-      RegKey::SetValue(key_name, kRegValueLanguage, _T("it")));
-  EXPECT_HRESULT_SUCCEEDED(
-      RegKey::SetValue(key_name, kRegValueProductVersion, _T("0.1.2.3")));
-#endif
-
-  AppData app_data(PopulateFooAppData());
-  job_->set_download_file_name(_T("DoesNotExist.exe"));
-  job_->set_app_data(app_data);
-
-  set_job_state(JOBSTATE_DOWNLOADCOMPLETED);
-  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED_START, job_->Install());
-
-  EXPECT_EQ(JOBSTATE_COMPLETED, job_->job_state());
-
-  EXPECT_EQ(COMPLETION_ERROR, job_->info().status);
-  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED_START, job_->info().error_code);
-  EXPECT_STREQ(_T("The installer failed to start."), job_->info().text);
-
-  EXPECT_FALSE(RegKey::HasKey(kFullFooAppClientKeyPath));
-  EXPECT_FALSE(RegKey::HasKey(kFullFooAppClientStateKeyPath));
-
-  // TODO(omaha): Install the job successfully and verify that the brand data
-  // is replaced by the successful install.
-
-  RestoreRegistryHives();
-  ASSERT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
-}
-
-// Overrides the registry hives.
-TEST_F(JobInstallFooTest,
-       Install_InstallerFailedFirstInstall_WhenNoExistingPreInstallData) {
-  Install_InstallerFailed_WhenNoExistingPreInstallData(false);
-}
-
-TEST_F(JobInstallFooTest,
-       Install_InstallerFailedUpdate_WhenNoExistingPreInstallData) {
-  Install_InstallerFailed_WhenNoExistingPreInstallData(true);
-}
-
-void JobInstallFooTest::Install_InstallerFailed_WhenExistingPreInstallData(
-    bool is_update) {
-  if (!vista_util::IsUserAdmin()) {
-    std::wcout << _T("\tTest did not run because the user is not an admin.")
-               << std::endl;
-    return;
-  }
-
-  if (!is_update) {
-    SetIsInstallJob();
-  }
-
-  RegKey::DeleteKey(kRegistryHiveOverrideRoot, true);
-  OverrideRegistryHivesWithExecutionPermissions(kRegistryHiveOverrideRoot);
-
-#ifdef _DEBUG
-  // Event::WriteEvent() expects Omaha's language and version to exist.
-  ConfigManager& config_mgr = *ConfigManager::Instance();
-  CString key_name = config_mgr.registry_clients_goopdate(is_machine_);
-  EXPECT_HRESULT_SUCCEEDED(
-      RegKey::SetValue(key_name, kRegValueLanguage, _T("it")));
-  EXPECT_HRESULT_SUCCEEDED(
-      RegKey::SetValue(key_name, kRegValueProductVersion, _T("0.1.2.3")));
-#endif
-
-  // Prepopulate data for this app in the ClientState registry
-  EXPECT_HRESULT_SUCCEEDED(RegKey::SetValue(kFullFooAppClientStateKeyPath,
-                                            kRegValueProductVersion,
-                                            _T("0.1.2.3")));
-
-  // Set update available stats so can verify they are not modified or deleted.
-  EXPECT_SUCCEEDED(RegKey::SetValue(kFullFooAppClientStateKeyPath,
-                                    _T("UpdateAvailableCount"),
-                                    static_cast<DWORD>(123456)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kFullFooAppClientStateKeyPath,
-                                    _T("UpdateAvailableSince"),
-                                    static_cast<DWORD64>(9876543210)));
-  const DWORD kExistingUpdateValues = 0x70123456;
-  EXPECT_SUCCEEDED(RegKey::SetValue(kFullFooAppClientStateKeyPath,
-                                    kRegValueLastSuccessfulCheckSec,
-                                    kExistingUpdateValues));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kFullFooAppClientStateKeyPath,
-                                    kRegValueLastUpdateTimeSec,
-                                    kExistingUpdateValues));
-
-  AppData app_data(PopulateFooAppData());
-  job_->set_download_file_name(_T("DoesNotExist.exe"));
-  job_->set_app_data(app_data);
-
-  set_job_state(JOBSTATE_DOWNLOADCOMPLETED);
-  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED_START, job_->Install());
-
-  EXPECT_EQ(JOBSTATE_COMPLETED, job_->job_state());
-
-  EXPECT_EQ(COMPLETION_ERROR, job_->info().status);
-  EXPECT_EQ(GOOPDATEINSTALL_E_INSTALLER_FAILED_START, job_->info().error_code);
-  EXPECT_STREQ(_T("The installer failed to start."), job_->info().text);
-
-  EXPECT_EQ(is_update, RegKey::HasKey(kFullFooAppClientStateKeyPath));
-  EXPECT_FALSE(RegKey::HasKey(kFullFooAppClientKeyPath));
-
-  // When an update fails, data remains. When an install fails, the entire
-  // ClientState key is deleted as verified above.
-  if (is_update) {
-    DWORD update_available_count(0);
-    EXPECT_SUCCEEDED(RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                                      _T("UpdateAvailableCount"),
-                                      &update_available_count));
-    EXPECT_EQ(123456, update_available_count);
-
-    DWORD64 update_available_since_time(0);
-    EXPECT_SUCCEEDED(RegKey::GetValue(kFullFooAppClientStateKeyPath,
-                                      _T("UpdateAvailableSince"),
-                                      &update_available_since_time));
-    EXPECT_EQ(9876543210, update_available_since_time);
-    EXPECT_EQ(kExistingUpdateValues,
-              GetDwordValue(kFullFooAppClientStateKeyPath,
-                            kRegValueLastSuccessfulCheckSec));
-    EXPECT_EQ(kExistingUpdateValues,
-              GetDwordValue(kFullFooAppClientStateKeyPath,
-                            kRegValueLastUpdateTimeSec));
-  }
-
-  RestoreRegistryHives();
-  ASSERT_SUCCEEDED(RegKey::DeleteKey(kRegistryHiveOverrideRoot, true));
-}
-
-TEST_F(JobInstallFooTest,
-       Install_InstallerFailedFirstInstall_WhenExistingPreInstallData) {
-  Install_InstallerFailed_WhenExistingPreInstallData(false);
-}
-
-TEST_F(JobInstallFooTest,
-       Install_InstallerFailedUpdate_WhenExistingPreInstallData) {
-  Install_InstallerFailed_WhenExistingPreInstallData(true);
-}
-
-TEST_F(JobTest, LaunchCmdLine_EmptyCommand) {
-  SetIsInstallJob();
-  EXPECT_SUCCEEDED(job_->LaunchCmdLine());
-  EXPECT_FALSE(did_launch_cmd_fail());
-}
-
-TEST_F(JobTest, LaunchCmdLine_LaunchFails) {
-  SetIsInstallJob();
-  set_launch_cmd_line(_T("no_such_file.exe"));
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), job_->LaunchCmdLine());
-  EXPECT_TRUE(did_launch_cmd_fail());
-}
-
-TEST_F(JobTest, LaunchCmdLine_Succeeds) {
-  SetIsInstallJob();
-  set_launch_cmd_line(_T("cmd /c"));
-  EXPECT_SUCCEEDED(job_->LaunchCmdLine());
-  EXPECT_FALSE(did_launch_cmd_fail());
-}
-
-// LaunchCmdLine should not be called for update jobs.
-TEST_F(JobTest, LaunchCmdLine_IsUpdateJob) {
-  ExpectAsserts expect_asserts;
-  set_launch_cmd_line(_T("cmd /c"));
-  EXPECT_SUCCEEDED(job_->LaunchCmdLine());
-  EXPECT_FALSE(did_launch_cmd_fail());
-}
-
-class JobDoCompleteJobJobSuccessTest : public JobTest {
- protected:
-  virtual void SetUp() {
-    JobTest::SetUp();
-
-    destination_path_ = CreateUniqueTempDir();
-    set_download_file_name(ConcatenatePath(destination_path_, _T("foo.msi")));
-    job_->set_job_observer(&job_observer_);
-
-    CompletionInfo info(COMPLETION_SUCCESS, 0, _T(""));
-    set_info(info);
-  }
-
-  CString destination_path_;
-  JobObserverMock job_observer_;
-};
-
-TEST_F(JobDoCompleteJobJobSuccessTest, DefaultSuccessAction) {
-  EXPECT_SUCCEEDED(DoCompleteJob());
-
-  EXPECT_EQ(COMPLETION_CODE_SUCCESS, job_observer_.completion_code);
-  EXPECT_TRUE(job_observer_.completion_text.IsEmpty());
-  EXPECT_EQ(0, job_observer_.completion_error_code);
-
-  EXPECT_FALSE(File::Exists(destination_path_));
-}
-
-// download_file_name_ is not set. No assert indicates that
-// DeleteJobDownloadDirectory is not called in error cases.
-TEST_F(JobTest, DefaultSuccessAction_Omaha) {
-  JobObserverMock job_observer;
-  job_->set_job_observer(&job_observer);
-
-  CompletionInfo info(COMPLETION_SUCCESS, 0, _T(""));
-  set_info(info);
-
-  AppData app_data;
-  app_data.set_app_guid(kGoopdateGuid);
-  SetAppData(app_data);
-
-  EXPECT_SUCCEEDED(DoCompleteJob());
-
-  EXPECT_EQ(COMPLETION_CODE_SUCCESS, job_observer.completion_code);
-  EXPECT_TRUE(job_observer.completion_text.IsEmpty());
-  EXPECT_EQ(0, job_observer.completion_error_code);
-}
-
-TEST_F(JobDoCompleteJobJobSuccessTest,
-       DefaultSuccessAction_LaunchCmdNotFailed) {
-  SetIsInstallJob();
-
-  EXPECT_SUCCEEDED(DoCompleteJob());
-
-  EXPECT_EQ(COMPLETION_CODE_SUCCESS, job_observer_.completion_code);
-  EXPECT_TRUE(job_observer_.completion_text.IsEmpty());
-  EXPECT_EQ(0, job_observer_.completion_error_code);
-}
-
-TEST_F(JobDoCompleteJobJobSuccessTest,
-       DefaultSuccessAction_LaunchCmdFailed) {
-  SetIsInstallJob();
-  set_did_launch_cmd_fail(true);
-
-  EXPECT_SUCCEEDED(DoCompleteJob());
-
-  EXPECT_EQ(COMPLETION_CODE_SUCCESS, job_observer_.completion_code);
-  EXPECT_TRUE(job_observer_.completion_text.IsEmpty());
-  EXPECT_EQ(0, job_observer_.completion_error_code);
-}
-
-TEST_F(JobDoCompleteJobJobSuccessTest,
-       SuccessActionExitSilently_NoLaunchCmd) {
-  SetIsInstallJob();
-  SetUpdateResponseSuccessAction(SUCCESS_ACTION_EXIT_SILENTLY);
-
-  EXPECT_SUCCEEDED(DoCompleteJob());
-
-  EXPECT_EQ(COMPLETION_CODE_SUCCESS_CLOSE_UI, job_observer_.completion_code);
-  EXPECT_TRUE(job_observer_.completion_text.IsEmpty());
-  EXPECT_EQ(0, job_observer_.completion_error_code);
-}
-
-TEST_F(JobDoCompleteJobJobSuccessTest,
-       SuccessActionExitSilently_LaunchCmdNotFailed) {
-  SetIsInstallJob();
-  set_launch_cmd_line(_T("cmd /c"));
-  SetUpdateResponseSuccessAction(SUCCESS_ACTION_EXIT_SILENTLY);
-
-  EXPECT_SUCCEEDED(DoCompleteJob());
-
-  EXPECT_EQ(COMPLETION_CODE_SUCCESS_CLOSE_UI, job_observer_.completion_code);
-  EXPECT_TRUE(job_observer_.completion_text.IsEmpty());
-  EXPECT_EQ(0, job_observer_.completion_error_code);
-}
-
-TEST_F(JobDoCompleteJobJobSuccessTest,
-       SuccessActionExitSilently_LaunchCmdFailed) {
-  SetIsInstallJob();
-  set_launch_cmd_line(_T("cmd /c"));
-  SetUpdateResponseSuccessAction(SUCCESS_ACTION_EXIT_SILENTLY);
-  set_did_launch_cmd_fail(true);
-
-  EXPECT_SUCCEEDED(DoCompleteJob());
-
-  EXPECT_EQ(COMPLETION_CODE_SUCCESS, job_observer_.completion_code);
-  EXPECT_TRUE(job_observer_.completion_text.IsEmpty());
-  EXPECT_EQ(0, job_observer_.completion_error_code);
-}
-
-TEST_F(JobDoCompleteJobJobSuccessTest,
-       SuccessActionExitSilentlyOnCmd_NoLaunchCmd) {
-  SetIsInstallJob();
-  SetUpdateResponseSuccessAction(SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD);
-
-  EXPECT_SUCCEEDED(DoCompleteJob());
-
-  EXPECT_EQ(COMPLETION_CODE_SUCCESS, job_observer_.completion_code);
-  EXPECT_TRUE(job_observer_.completion_text.IsEmpty());
-  EXPECT_EQ(0, job_observer_.completion_error_code);
-}
-
-TEST_F(JobDoCompleteJobJobSuccessTest,
-       SuccessActionExitSilentlyOnCmd_CmdNotFailed) {
-  SetIsInstallJob();
-  set_launch_cmd_line(_T("cmd /c"));
-  SetUpdateResponseSuccessAction(SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD);
-
-  EXPECT_SUCCEEDED(DoCompleteJob());
-
-  EXPECT_EQ(COMPLETION_CODE_SUCCESS_CLOSE_UI, job_observer_.completion_code);
-  EXPECT_TRUE(job_observer_.completion_text.IsEmpty());
-  EXPECT_EQ(0, job_observer_.completion_error_code);
-}
-
-TEST_F(JobDoCompleteJobJobSuccessTest,
-       DefaultSuccessActionOnCmd_LaunchCmdFailed) {
-  SetIsInstallJob();
-  set_launch_cmd_line(_T("cmd /c"));
-  SetUpdateResponseSuccessAction(SUCCESS_ACTION_EXIT_SILENTLY_ON_LAUNCH_CMD);
-  set_did_launch_cmd_fail(true);
-
-  EXPECT_SUCCEEDED(DoCompleteJob());
-
-  EXPECT_EQ(COMPLETION_CODE_SUCCESS, job_observer_.completion_code);
-  EXPECT_TRUE(job_observer_.completion_text.IsEmpty());
-  EXPECT_EQ(0, job_observer_.completion_error_code);
-}
-
-// download_file_name_ is not set. No assert indicates that
-// DeleteJobDownloadDirectory is not called in error cases.
-TEST_F(JobTest, DoCompleteJob_JobError) {
-  JobObserverMock job_observer_;
-  job_->set_job_observer(&job_observer_);
-  CompletionInfo info(COMPLETION_ERROR, static_cast<DWORD>(E_FAIL), _T("blah"));
-  set_info(info);
-
-  EXPECT_SUCCEEDED(DoCompleteJob());
-
-  EXPECT_EQ(COMPLETION_CODE_ERROR, job_observer_.completion_code);
-  EXPECT_STREQ(_T("blah"), job_observer_.completion_text);
-  EXPECT_EQ(E_FAIL, job_observer_.completion_error_code);
-}
-
-}  // namespace omaha
diff --git a/worker/ping.cc b/worker/ping.cc
deleted file mode 100644
index 90c617b..0000000
--- a/worker/ping.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// Ping requests use http protocol. When pinging over http fails, a fallback
-// using https protocol is attempted.
-
-#include "omaha/worker/ping.h"
-
-#include <atlstr.h>
-#include <vector>
-#include "omaha/common/string.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/goopdate_xml_parser.h"
-#include "omaha/goopdate/request.h"
-#include "omaha/net/browser_request.h"
-#include "omaha/net/network_request.h"
-#include "omaha/net/simple_request.h"
-#include "omaha/worker/worker_metrics.h"
-
-namespace omaha {
-
-Ping::Ping() {
-  // The ping request does not use CUP since its response does not require
-  // authentication.
-  const NetworkConfig::Session& session(NetworkConfig::Instance().session());
-  network_request_.reset(new NetworkRequest(session));
-  network_request_->AddHttpRequest(new SimpleRequest);
-  network_request_->AddHttpRequest(new BrowserRequest);
-}
-
-Ping::~Ping() {
-}
-
-// Returns S_OK without sending the ping in OEM mode.
-HRESULT Ping::SendPing(Request* req) {
-  CORE_LOG(L2, (_T("[Ping::SendPing]")));
-  ASSERT1(req);
-
-  // Do not access the network during an OEM install.
-  if (!ConfigManager::Instance()->CanUseNetwork(req->is_machine())) {
-    CORE_LOG(L1, (_T("[Ping not sent because network use prohibited]")));
-    return GOOPDATE_E_CANNOT_USE_NETWORK;
-  }
-
-  // Do not send a request which contains no ping events.
-  bool has_ping_events = false;
-  for (AppRequestVector::const_iterator it = req->app_requests_begin();
-       it != req->app_requests_end();
-       ++it) {
-    if ((*it).request_data().num_ping_events() != 0) {
-      has_ping_events = true;
-      break;
-    }
-  }
-  if (!has_ping_events) {
-    return HRESULT_FROM_WIN32(ERROR_NO_DATA);
-  }
-
-  HighresTimer metrics_timer;
-
-  CString request_string;
-  HRESULT hr = GoopdateXmlParser::GenerateRequest(*req,
-                                                  false,
-                                                  &request_string);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[GenerateRequest failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  CString ping_url;
-  hr = ConfigManager::Instance()->GetPingUrl(&ping_url);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = DoSendPing(ping_url, request_string);
-  if (FAILED(hr)) {
-    metric_ping_failed_ms.AddSample(metrics_timer.GetElapsedMs());
-    return hr;
-  }
-
-  metric_ping_succeeded_ms.AddSample(metrics_timer.GetElapsedMs());
-  return S_OK;
-}
-
-HRESULT Ping::DoSendPing(const CString& url,
-                         const CString& request_string) {
-  CString response;
-  return PostRequest(network_request_.get(),
-                     true,                    // Fall back to https.
-                     url,
-                     request_string,
-                     &response);
-}
-
-HRESULT Ping::Cancel() {
-  if (network_request_.get()) {
-    HRESULT hr = network_request_->Cancel();
-    if (FAILED(hr)) {
-      CORE_LOG(LE, (_T("[NetworkRequest::Cancel failed][0x%08x]"), hr));
-      return hr;
-    }
-  }
-
-  return S_OK;
-}
-
-}  // namespace omaha
diff --git a/worker/ping.h b/worker/ping.h
deleted file mode 100644
index e341906..0000000
--- a/worker/ping.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_WORKER_PING_H__
-#define OMAHA_WORKER_PING_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-
-namespace omaha {
-
-class Job;
-class NetworkRequest;
-class Request;
-
-class Ping {
- public:
-  Ping();
-  virtual ~Ping();
-
-  // Sends a ping request. Returns an error if the request does not contain
-  // any ping events.
-  virtual HRESULT SendPing(Request* req);
-  virtual HRESULT Cancel();
-
- private:
-  HRESULT DoSendPing(const CString& url,
-                     const CString& request_string);
-  scoped_ptr<NetworkRequest> network_request_;
-  DISALLOW_EVIL_CONSTRUCTORS(Ping);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_PING_H__
diff --git a/worker/ping_event.h b/worker/ping_event.h
deleted file mode 100644
index 31d6bc3..0000000
--- a/worker/ping_event.h
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// ping_event.h: Encapsulates events to attach to AppRequests for pings.
-
-#ifndef OMAHA_WORKER_PING_EVENT_H__
-#define OMAHA_WORKER_PING_EVENT_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include <vector>
-#include "base/basictypes.h"
-#include "omaha/common/debug.h"
-
-namespace omaha {
-
-class PingEvent {
- public:
-  // When updating this enum, also update the protocol file on the server.
-  // These values get reported to the server, so do not change existing ones.
-  //
-  // Checkpoints:
-  //  "EVENT_INSTALL_*" events report the progress of initial installs.
-  //  "EVENT_UPDATE_*" events report the progress of silent updates.
-  //  These checkpoints represent the "START" or "FINISH" of a phase.
-  // Actions:
-  //  "EVENT_*_BEGIN" events report the start of a specific action (i.e. job).
-  //  "EVENT_*_COMPLETE" events represent the end of such actions and report
-  // successful completion or the error that occurred during the action.
-  enum Types {
-    EVENT_UNKNOWN = 0,
-    EVENT_INSTALL_DOWNLOAD_FINISH = 1,
-    EVENT_INSTALL_COMPLETE = 2,
-    EVENT_UPDATE_COMPLETE = 3,
-    EVENT_UNINSTALL = 4,
-    EVENT_INSTALL_DOWNLOAD_START = 5,
-    EVENT_INSTALL_INSTALLER_START = 6,
-    // Never used = 7
-    // No longer used - EVENT_INSTALLED_GOOPDATE_STARTED = 8,
-    EVENT_INSTALL_APPLICATION_BEGIN = 9,
-
-    // Install Setup events.
-    EVENT_SETUP_INSTALL_BEGIN = 10,
-    EVENT_SETUP_INSTALL_COMPLETE = 11,
-
-    // Update Events.
-    // The Update Event = 3 above is used for update completion.
-    EVENT_UPDATE_APPLICATION_BEGIN = 12,
-    EVENT_UPDATE_DOWNLOAD_START = 13,
-    EVENT_UPDATE_DOWNLOAD_FINISH = 14,
-    EVENT_UPDATE_INSTALLER_START = 15,
-
-    // Self-update Setup events.
-    EVENT_SETUP_UPDATE_BEGIN = 16,
-    EVENT_SETUP_UPDATE_COMPLETE = 17,
-
-    // Ping when installed via /registerproduct.
-    EVENT_REGISTER_PRODUCT_COMPLETE = 20,
-
-    // Ping when an end user first boots a new system with an OEM-installed app.
-    EVENT_INSTALL_OEM_FIRST_CHECK = 30,
-
-    // Failure report events - not part of the normal flow.
-    EVENT_SETUP_INSTALL_FAILURE = 100,
-    // No longer used - EVENT_GOOPDATE_DLL_FAILURE = 101,
-    EVENT_SETUP_COM_SERVER_FAILURE = 102,
-    EVENT_SETUP_UPDATE_FAILURE = 103,
-  };
-
-  // When updating this enum, also update the identical one in
-  // omaha_extensions.proto.
-  // These values get reported to the server, so do not change existing ones.
-  enum Results {
-    EVENT_RESULT_ERROR = 0,
-    EVENT_RESULT_SUCCESS = 1,
-    EVENT_RESULT_SUCCESS_REBOOT = 2,
-    //  EVENT_RESULT_SUCCESS_RESTART_BROWSER = 3,
-    EVENT_RESULT_CANCELLED = 4,
-    EVENT_RESULT_INSTALLER_ERROR_MSI = 5,
-    EVENT_RESULT_INSTALLER_ERROR_OTHER = 6,
-    EVENT_RESULT_NOUPDATE = 7,
-    EVENT_RESULT_INSTALLER_ERROR_SYSTEM = 8,
-    EVENT_RESULT_UPDATE_DEFERRED = 9,
-  };
-
-  // TODO(omaha): consider making previous_version part of the app element
-  // instead of the event element.
-  PingEvent(Types type,
-            Results result,
-            int error_code,
-            int extra_code1,
-            const CString& previous_version)
-    :  event_type_(type),
-       event_result_(result),
-       error_code_(error_code),
-       extra_code1_(extra_code1),
-       previous_version_(previous_version) {
-    ASSERT1(EVENT_UNKNOWN != event_type_);
-  }
-
-  Types event_type() const { return event_type_; }
-  Results event_result() const { return event_result_; }
-  int error_code() const { return error_code_; }
-  int extra_code1() const { return extra_code1_; }
-  CString previous_version() const { return previous_version_; }
-
- private:
-  Types event_type_;
-  Results event_result_;
-  int error_code_;
-  int extra_code1_;
-  CString previous_version_;
-};
-
-typedef std::vector<PingEvent> PingEventVector;
-
-}  // namespace omaha.
-
-#endif  // OMAHA_WORKER_PING_EVENT_H__
-
diff --git a/worker/ping_mock.h b/worker/ping_mock.h
deleted file mode 100644
index 34e2957..0000000
--- a/worker/ping_mock.h
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2009-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_WORKER_PING_MOCK_H__
-#define OMAHA_WORKER_PING_MOCK_H__
-
-#include <windows.h>
-#include <vector>
-#include "omaha/goopdate/request.h"
-#include "omaha/worker/ping.h"
-#include "omaha/testing/unit_test.h"
-
-namespace omaha {
-
-class PingMock : public Ping {
- public:
-  PingMock() {}
-
-  virtual ~PingMock() {
-    for (size_t i = 0; i < ping_requests_.size(); ++i) {
-      delete ping_requests_[i];
-    }
-  }
-
-  // Creates a copy of req and stores it in the ping_requests_ vector.
-  // The implementation must be kept in sync with Request's members.
-  virtual HRESULT SendPing(Request* req) {
-    ASSERT1(req);
-
-    Request* request = new Request(req->is_machine());
-    request->version_ = req->version();
-    request->os_version_ = req->os_version();
-    request->os_service_pack_ = req->os_service_pack();
-    request->test_source_ = req->test_source();
-    request->request_id_ = req->request_id();
-    request->app_requests_ = req->app_requests_;
-
-    ping_requests_.push_back(request);
-    return S_OK;
-  }
-
-  const std::vector<Request*>& ping_requests() const { return ping_requests_; }
-
- private:
-  std::vector<Request*> ping_requests_;
-
- private:
-  DISALLOW_EVIL_CONSTRUCTORS(PingMock);
-};
-
-// Returns the event from an AppRequest that contains a single event.
-inline const PingEvent& GetSingleEventFromAppRequest(
-    const AppRequest& app_request,
-    const GUID& expected_app_guid,
-    bool expected_is_machine) {
-  const AppRequestData& app_request_data = app_request.request_data();
-
-  const AppData& app_data = app_request_data.app_data();
-  EXPECT_TRUE(::IsEqualGUID(expected_app_guid, app_data.app_guid()));
-  EXPECT_TRUE(::IsEqualGUID(GUID_NULL, app_data.parent_app_guid()));
-  EXPECT_EQ(expected_is_machine, app_data.is_machine_app());
-  EXPECT_TRUE(!app_data.version().IsEmpty());
-  EXPECT_TRUE(!app_data.previous_version().IsEmpty());
-
-  EXPECT_EQ(1, app_request_data.num_ping_events());
-  return *app_request_data.ping_events_begin();
-}
-
-inline const PingEvent& GetSingleEventFromRequest(const Request& request,
-                                                  const GUID& expected_app_guid,
-                                                  bool expected_is_machine) {
-  EXPECT_EQ(expected_is_machine, request.is_machine());
-  EXPECT_TRUE(!request.version().IsEmpty());
-  EXPECT_TRUE(!request.os_version().IsEmpty());
-  // Skip checking request.os_service_pack() as it can be empty for RTM.
-#if defined(DEBUG) || !OFFICIAL_BUILD
-  EXPECT_TRUE(!request.test_source().IsEmpty());
-#else
-  EXPECT_TRUE(request.test_source().IsEmpty());
-#endif
-  EXPECT_TRUE(!request.request_id().IsEmpty());
-  EXPECT_EQ(1, request.get_request_count());
-  return GetSingleEventFromAppRequest(*request.app_requests_begin(),
-                                      expected_app_guid,
-                                      expected_is_machine);
-}
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_PING_MOCK_H__
diff --git a/worker/ping_unittest.cc b/worker/ping_unittest.cc
deleted file mode 100644
index b3bd353..0000000
--- a/worker/ping_unittest.cc
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <windows.h>
-#include <objbase.h>
-#include "base/scoped_ptr.h"
-#include "omaha/common/error.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/thread_pool.h"
-#include "omaha/common/timer.h"
-#include "omaha/goopdate/request.h"
-#include "omaha/testing/unit_test.h"
-#include "omaha/worker/application_data.h"
-#include "omaha/worker/ping.h"
-
-namespace omaha {
-
-// Sends a ping when run in a thread pool.
-class PingJob : public UserWorkItem {
- public:
-  PingJob(Ping* ping, Request* request, HRESULT* result)
-      : ping_(ping),
-        request_(request),
-        result_(result) {}
-
- private:
-  virtual void DoProcess() {
-    scoped_co_init init_com_apt(COINIT_MULTITHREADED);
-    *result_ = ping_->SendPing(request_);
-  }
-
-  Ping* ping_;
-  Request* request_;
-  HRESULT* result_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(PingJob);
-};
-
-class PingTest : public testing::Test {
- public:
-  PingTest() : ping_result_(E_FAIL) {}
-
- protected:
-  virtual void SetUp() {
-    ping_result_ = E_FAIL;
-    thread_pool_.reset(new ThreadPool);
-    EXPECT_HRESULT_SUCCEEDED(thread_pool_->Initialize(kShutdownDelayMs));
-
-    ping_.reset(new Ping);
-
-    AppData data(StringToGuid(_T("{C6E96DF3-BFEA-4403-BAA7-8920CE0B494A}")),
-                              true);
-    CreateTestAppData(&data);
-    request_.reset(CreateRequest(true,
-                                 data,
-                                 PingEvent::EVENT_INSTALL_DOWNLOAD_FINISH,
-                                 PingEvent::EVENT_RESULT_SUCCESS,
-                                 0));
-    ASSERT_TRUE(request_.get());
-  }
-
-  virtual void TearDown() {
-    // ThreadPool destructor blocks waiting for the work items to complete.
-    thread_pool_.reset();
-    request_.reset();
-    ping_.reset();
-  }
-
-  void CreateTestAppData(AppData* expected_app) {
-    ASSERT_TRUE(expected_app != NULL);
-    expected_app->set_version(_T("1.1.1.3"));
-    expected_app->set_previous_version(_T("1.0.0.0"));
-    expected_app->set_language(_T("abc"));
-    expected_app->set_did_run(AppData::ACTIVE_RUN);
-    expected_app->set_ap(_T("Test ap"));
-    expected_app->set_tt_token(_T("Test TT Token"));
-    expected_app->set_iid(
-        StringToGuid(_T("{F723495F-8ACF-4746-8240-643741C797B5}")));
-    expected_app->set_brand_code(_T("GOOG"));
-    expected_app->set_client_id(_T("someclient"));
-  }
-
-  Request* CreateRequest(bool is_machine,
-                         const AppData& app,
-                         PingEvent::Types type,
-                         PingEvent::Results result,
-                         int error_code) {
-    scoped_ptr<Request> req(new Request(is_machine));
-
-    AppRequestData app_request_data(app);
-    PingEvent ping_event(type,
-                         result,
-                         error_code,
-                         8675309,
-                         app.previous_version());
-    app_request_data.AddPingEvent(ping_event);
-    AppRequest app_request(app_request_data);
-    req->AddAppRequest(app_request);
-    return req.release();
-  }
-
-  HRESULT ping_result_;
-  scoped_ptr<ThreadPool> thread_pool_;
-  scoped_ptr<Ping> ping_;
-  scoped_ptr<Request> request_;
-
-  static const int kShutdownDelayMs = 60 * 1000;  // 60 seconds.
-};
-
-TEST_F(PingTest, SendPing) {
-  // The same Ping instance can send multiple pings.
-  EXPECT_HRESULT_SUCCEEDED(ping_->SendPing(request_.get()));
-  EXPECT_HRESULT_SUCCEEDED(ping_->SendPing(request_.get()));
-}
-
-// Runs a ping instance in a thread pool and attempts to cancel it.
-// Either the ping succeeds or it is canceled.
-TEST_F(PingTest, CancelPing) {
-  scoped_ptr<UserWorkItem> work_item(new PingJob(ping_.get(),
-                                                 request_.get(),
-                                                 &ping_result_));
-  EXPECT_HRESULT_SUCCEEDED(thread_pool_->QueueUserWorkItem(work_item.get(),
-                           WT_EXECUTEDEFAULT));
-  work_item.release();
-
-  // Sleep for a while to give the ping some time to run.
-  ::Sleep(100);
-  EXPECT_HRESULT_SUCCEEDED(ping_->Cancel());
-
-  // Wait for the ping work item to complete.
-  LowResTimer timer(true);
-  while (thread_pool_->HasWorkItems() && timer.GetSeconds() <= 60) {
-    ::Sleep(20);
-  }
-
-  EXPECT_TRUE(SUCCEEDED(ping_result_) ||
-              ping_result_ == OMAHA_NET_E_REQUEST_CANCELLED);
-}
-
-TEST_F(PingTest, SendEmptyPing) {
-  Request req(false);
-  req.AddAppRequest(AppRequest(AppRequestData(AppData())));
-  EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_NO_DATA), ping_->SendPing(&req));
-}
-
-TEST_F(PingTest, SendPing_GoogleUpdateEulaNotAccepted) {
-  RegKey::DeleteKey(kRegistryHiveOverrideRoot);
-  OverrideRegistryHives(kRegistryHiveOverrideRoot);
-
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_EQ(GOOPDATE_E_CANNOT_USE_NETWORK, ping_->SendPing(request_.get()));
-
-  RestoreRegistryHives();
-  RegKey::DeleteKey(kRegistryHiveOverrideRoot);
-}
-
-}  // namespace omaha
diff --git a/worker/ping_utils.cc b/worker/ping_utils.cc
deleted file mode 100644
index 2a6016d..0000000
--- a/worker/ping_utils.cc
+++ /dev/null
@@ -1,300 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include "omaha/worker/ping_utils.h"
-#include <atlstr.h>
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/worker/application_data.h"
-#include "omaha/worker/application_manager.h"
-#include "omaha/worker/app_request.h"
-#include "omaha/worker/app_request_data.h"
-#include "omaha/worker/job.h"
-#include "omaha/worker/ping.h"
-#include "omaha/worker/ping_event.h"
-#include "omaha/worker/product_data.h"
-
-namespace omaha {
-
-namespace ping_utils {
-
-// If version is NULL, the current version will be used.
-HRESULT SendGoopdatePing(bool is_machine,
-                         const CommandLineExtraArgs& extra_args,
-                         const CString& install_source,
-                         PingEvent::Types type,
-                         HRESULT result,
-                         int extra_code1,
-                         const TCHAR* version,
-                         Ping* ping) {
-  CORE_LOG(L2, (_T("[SendGoopdatePing]")));
-  ASSERT1(ping);
-
-  CString previous_version;
-  AppRequestData app_request_data_goopdate;
-  BuildGoogleUpdateAppRequestData(is_machine,
-                                  extra_args,
-                                  install_source,
-                                  &previous_version,
-                                  &app_request_data_goopdate);
-
-  PingEvent::Results event_result = (S_OK == result) ?
-                                    PingEvent::EVENT_RESULT_SUCCESS :
-                                    PingEvent::EVENT_RESULT_ERROR;
-
-  PingEvent ping_event(type,
-                       event_result,
-                       result,
-                       extra_code1,
-                       previous_version);
-
-  app_request_data_goopdate.AddPingEvent(ping_event);
-
-  Request request(is_machine);
-  if (version) {
-    request.set_version(version);
-  }
-
-  AppRequest app_request(app_request_data_goopdate);
-  request.AddAppRequest(app_request);
-
-  HRESULT hr = ping->SendPing(&request);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[SendPing failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-// Initializes an AppRequest structure with information about
-// GoogleUpdate. If extra contains data, its values are used. Otherwise,
-// attempts to obtain data from the registry.
-void BuildGoogleUpdateAppRequestData(bool is_machine,
-                                     const CommandLineExtraArgs& extra_args,
-                                     const CString& install_source,
-                                     CString* previous_version,
-                                     AppRequestData* app_request_data) {
-  CORE_LOG(L3, (_T("[Ping::BuildGoogleUpdateAppRequest]")));
-
-  ASSERT1(app_request_data);
-
-  if (previous_version) {
-    *previous_version = _T("");
-  }
-
-  AppData app_data_goopdate(kGoopdateGuid, is_machine);
-  app_data_goopdate.set_version(GetVersionString());
-
-  AppManager app_manager(is_machine);
-  ProductData product_data;
-  // TODO(omaha): Should we just call ReadAppDataFromStore?
-  HRESULT hr = app_manager.ReadProductDataFromStore(kGoopdateGuid,
-                                                    &product_data);
-  if (SUCCEEDED(hr)) {
-    if (previous_version) {
-      *previous_version = product_data.app_data().previous_version();
-    }
-    app_data_goopdate.set_iid(product_data.app_data().iid());
-    app_data_goopdate.set_brand_code(product_data.app_data().brand_code());
-    app_data_goopdate.set_client_id(product_data.app_data().client_id());
-    app_data_goopdate.set_did_run(product_data.app_data().did_run());
-  } else {
-    CORE_LOG(LEVEL_WARNING, (_T("[ReadProductDataFromStore failed]")
-                             _T("[0x%x][%s]"), hr, kGoogleUpdateAppId));
-
-    // Use branding data from the command line if present.
-    // We do not want to use branding data from the command line if this is not
-    // the first install of Google Update.
-    if (!extra_args.brand_code.IsEmpty()) {
-      app_data_goopdate.set_brand_code(extra_args.brand_code);
-    }
-    if (!extra_args.client_id.IsEmpty()) {
-      app_data_goopdate.set_client_id(extra_args.client_id);
-    }
-  }
-
-  // Always use the installation ID and install source from the command line if
-  // present.
-  if (GUID_NULL != extra_args.installation_id) {
-    app_data_goopdate.set_iid(extra_args.installation_id);
-  }
-  app_data_goopdate.set_install_source(install_source);
-
-  AppRequestData app_request_data_temp(app_data_goopdate);
-  *app_request_data = app_request_data_temp;
-}
-
-HRESULT SendPostSetupPing(HRESULT result,
-                          int extra_code1,
-                          const CString& previous_version,
-                          bool is_machine,
-                          bool is_self_update,
-                          const CommandLineExtraArgs& extra,
-                          const CString& install_source,
-                          Ping* ping) {
-  CORE_LOG(L3, (_T("[Ping::SendPostSetupPing]")));
-  ASSERT1(ping);
-
-  scoped_ptr<Request> request(new Request(is_machine));
-  AppRequestData app_request_data;
-  BuildGoogleUpdateAppRequestData(
-      is_machine,
-      extra,
-      install_source,
-      NULL,
-      &app_request_data);
-
-  const PingEvent::Results event_result = (S_OK == result) ?
-                                          PingEvent::EVENT_RESULT_SUCCESS :
-                                          PingEvent::EVENT_RESULT_ERROR;
-  const PingEvent::Types type = is_self_update ?
-                                PingEvent::EVENT_SETUP_UPDATE_COMPLETE :
-                                PingEvent::EVENT_SETUP_INSTALL_COMPLETE;
-
-  PingEvent ping_event(type,
-                       event_result,
-                       result,
-                       extra_code1,
-                       previous_version);
-  app_request_data.AddPingEvent(ping_event);
-
-  if (is_self_update) {
-    // In case of self updates we also indicate that we completed the update
-    // job started by the previous version of Omaha.
-    PingEvent ping_event2(PingEvent::EVENT_UPDATE_COMPLETE,
-                          event_result,
-                          result,
-                          extra_code1,
-                          previous_version);
-    app_request_data.AddPingEvent(ping_event2);
-  }
-
-  AppRequest app_request(app_request_data);
-  request->AddAppRequest(app_request);
-
-  HRESULT hr = ping->SendPing(request.get());
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[SendSetupPing(completed) failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-
-PingEvent::Results CompletionStatusToPingEventResult(
-    JobCompletionStatus status) {
-  PingEvent::Results result = PingEvent::EVENT_RESULT_ERROR;
-  switch (status) {
-    case COMPLETION_SUCCESS:
-      result = PingEvent::EVENT_RESULT_SUCCESS;
-      break;
-    case COMPLETION_SUCCESS_REBOOT_REQUIRED:
-      result = PingEvent::EVENT_RESULT_SUCCESS_REBOOT;
-      break;
-    case COMPLETION_ERROR:
-      result = PingEvent::EVENT_RESULT_ERROR;
-      break;
-    case COMPLETION_INSTALLER_ERROR_MSI:
-      result = PingEvent::EVENT_RESULT_INSTALLER_ERROR_MSI;
-      break;
-    case COMPLETION_INSTALLER_ERROR_SYSTEM:
-      result = PingEvent::EVENT_RESULT_INSTALLER_ERROR_SYSTEM;
-      break;
-    case COMPLETION_INSTALLER_ERROR_OTHER:
-      result = PingEvent::EVENT_RESULT_INSTALLER_ERROR_OTHER;
-      break;
-    case COMPLETION_CANCELLED:
-      result = PingEvent::EVENT_RESULT_CANCELLED;
-      break;
-    default:
-      ASSERT1(false);
-      break;
-  }
-  return result;
-}
-
-HRESULT BuildCompletedPingForAllProducts(const ProductDataVector& products,
-                                         bool is_update,
-                                         const CompletionInfo& info,
-                                         Request* request) {
-  CORE_LOG(L2, (_T("[BuildCompletedPingForAllProducts]")));
-  ASSERT1(request);
-
-  PingEvent::Types type = is_update ? PingEvent::EVENT_UPDATE_COMPLETE :
-                                      PingEvent::EVENT_INSTALL_COMPLETE;
-  PingEvent::Results result = CompletionStatusToPingEventResult(info.status);
-  for (size_t i = 0; i < products.size(); ++i) {
-    const ProductData& product_data = products[i];
-    AppRequestData app_request_data(product_data.app_data());
-
-    // Create and add the ping event.
-    CString previous_version = app_request_data.app_data().previous_version();
-    // TODO(omaha): Remove this value when circular log buffer is implemented.
-    // This value must not be a valid Job::JobState.
-    const int kAppProductsPingJobState = 0xff;
-    PingEvent ping_event(type,
-                         result,
-                         info.error_code,
-                         Job::kJobStateExtraCodeMask | kAppProductsPingJobState,
-                         previous_version);
-    app_request_data.AddPingEvent(ping_event);
-
-    AppRequest app_request(app_request_data);
-    request->AddAppRequest(app_request);
-  }
-  ASSERT1(products.size() ==
-          static_cast<size_t>(request->get_request_count()));
-  return S_OK;
-}
-
-HRESULT SendCompletedPingsForAllProducts(const ProductDataVector& products,
-                                         bool is_machine,
-                                         bool is_update,
-                                         const CompletionInfo& info,
-                                         Ping* ping) {
-  CORE_LOG(L2, (_T("[SendCompletedPingsForAllProducts]")));
-  ASSERT1(ping);
-
-  scoped_ptr<Request> request(new Request(is_machine));
-  HRESULT hr = BuildCompletedPingForAllProducts(products,
-                                                is_update,
-                                                info,
-                                                request.get());
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[BuildCompletedPingForAllProducts failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  if (!products.empty()) {
-    HRESULT hr = ping->SendPing(request.get());
-    if (FAILED(hr)) {
-      CORE_LOG(LE, (_T("[SendPing failed][0x%08x]"), hr));
-      return hr;
-    }
-  }
-
-  return S_OK;
-}
-
-}  // namespace ping_utils
-
-}  // namespace omaha
-
diff --git a/worker/ping_utils.h b/worker/ping_utils.h
deleted file mode 100644
index 5a7a1f7..0000000
--- a/worker/ping_utils.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_WORKER_PING_UTILS_H__
-#define OMAHA_WORKER_PING_UTILS_H__
-
-#include <atlstr.h>
-#include <windows.h>
-#include "omaha/worker/ping_event.h"
-#include "omaha/worker/product_data.h"
-
-namespace omaha {
-
-class AppRequestData;
-class Ping;
-class Request;
-enum JobCompletionStatus;
-struct CommandLineExtraArgs;
-struct CompletionInfo;
-
-// Utility functions for Ping.
-namespace ping_utils {
-
-// TODO(omaha): Put common params in the same order.
-HRESULT SendGoopdatePing(bool is_machine,
-                         const CommandLineExtraArgs& extra_args,
-                         const CString& install_source,
-                         PingEvent::Types type,
-                         HRESULT error,
-                         int extra_code1,
-                         const TCHAR* version,
-                         Ping* ping);
-
-HRESULT SendPostSetupPing(HRESULT result,
-                          int extra_code1,
-                          const CString& previous_version,
-                          bool is_machine,
-                          bool is_interactive,
-                          const CommandLineExtraArgs& extra,
-                          const CString& install_source,
-                          Ping* ping);
-
-PingEvent::Results CompletionStatusToPingEventResult(
-    JobCompletionStatus status);
-
-HRESULT BuildCompletedPingForAllProducts(const ProductDataVector& products,
-                                         bool is_update,
-                                         const CompletionInfo& info,
-                                         Request* request);
-
-HRESULT SendCompletedPingsForAllProducts(const ProductDataVector& products,
-                                         bool is_machine,
-                                         bool is_update,
-                                         const CompletionInfo& info,
-                                         Ping* ping);
-
-void BuildGoogleUpdateAppRequestData(bool is_machine,
-                                     const CommandLineExtraArgs& extra_args,
-                                     const CString& install_source,
-                                     CString* previous_version,
-                                     AppRequestData* app_request_data);
-
-}  // namespace ping_utils
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_PING_UTILS_H__
diff --git a/worker/ping_utils_unittest.cc b/worker/ping_utils_unittest.cc
deleted file mode 100644
index 5509f79..0000000
--- a/worker/ping_utils_unittest.cc
+++ /dev/null
@@ -1,280 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <windows.h>
-#include <objbase.h>
-#include "base/scoped_ptr.h"
-#include "omaha/common/utils.h"
-#include "omaha/testing/unit_test.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/worker/application_data.h"
-#include "omaha/worker/job.h"
-#include "omaha/worker/ping.h"
-#include "omaha/worker/ping_utils.h"
-#include "omaha/worker/product_data.h"
-
-namespace omaha {
-
-class PingUtilsTest : public testing::Test {
- protected:
-  virtual void SetUp() {
-    ping_.reset(new Ping);
-  }
-
-  scoped_ptr<Ping> ping_;
-};
-
-TEST_F(PingUtilsTest, SendGoopdatePing) {
-  CommandLineExtraArgs extra_args;
-
-  // Test with no language.
-  EXPECT_SUCCEEDED(
-      ping_utils::SendGoopdatePing(false,
-                                   extra_args,
-                                   _T(""),
-                                   PingEvent::EVENT_SETUP_INSTALL_FAILURE,
-                                   S_OK,
-                                   0,
-                                   NULL,
-                                   ping_.get()));
-
-  // Test with a language.
-  extra_args.language = _T("en");
-  EXPECT_SUCCEEDED(
-      ping_utils::SendGoopdatePing(false,
-                                   extra_args,
-                                   _T(""),
-                                   PingEvent::EVENT_SETUP_INSTALL_FAILURE,
-                                   S_OK,
-                                   0,
-                                   NULL,
-                                   ping_.get()));
-
-  // Test with additional data.
-  CommandLineAppArgs extra;
-  extra_args.installation_id = StringToGuid(
-      _T("{98CEC468-9429-4984-AEDE-4F53C6A14869}"));
-  extra_args.language = _T("de");
-  extra_args.brand_code = _T("g00g");
-  extra_args.client_id = _T("_some_partner");
-  extra_args.browser_type = BROWSER_IE;
-  extra_args.usage_stats_enable = TRISTATE_TRUE;
-  extra_args.apps.push_back(extra);
-  EXPECT_SUCCEEDED(ping_utils::SendGoopdatePing(
-                       true,
-                       extra_args,
-                       _T("sourcefoo"),
-                       PingEvent::EVENT_SETUP_UPDATE_FAILURE,
-                       E_FAIL,
-                       1234567890,
-                       NULL,
-                       ping_.get()));
-}
-
-TEST_F(PingUtilsTest, SendCompletedPingsForAllProducts_EmptyProducts) {
-  ProductDataVector products;
-  CompletionInfo info(COMPLETION_ERROR, 10, _T("test"));
-
-  ASSERT_HRESULT_SUCCEEDED(ping_utils::SendCompletedPingsForAllProducts(
-      products,
-      false,
-      false,
-      info,
-      ping_.get()));
-}
-
-TEST_F(PingUtilsTest, SendCompletedPingsForAllProducts) {
-  ProductDataVector products;
-
-  AppData data1(StringToGuid(_T("{E66F2139-5469-BAAD-AC99-7863798E3A0A}")),
-                false);
-  data1.set_version(_T("1.1.1.1"));
-  data1.set_previous_version(_T("1.0.0.0"));
-  data1.set_language(_T("en"));
-
-  ProductData product_data1;
-  product_data1.set_app_data(data1);
-  products.push_back(product_data1);
-
-  AppData data2(StringToGuid(_T("{E66F3140-5179-41ec-BAAD-7863798E3A0A}")),
-                false);
-  data2.set_version(_T("1.1.1.1"));
-  data2.set_previous_version(_T("1.0.0.0"));
-  data2.set_language(_T("de"));
-
-  ProductData product_data2;
-  product_data2.set_app_data(data2);
-  products.push_back(product_data2);
-
-  CompletionInfo info(COMPLETION_ERROR, 10, _T("test"));
-  ASSERT_HRESULT_SUCCEEDED(ping_utils::SendCompletedPingsForAllProducts(
-      products,
-      false,
-      false,
-      info,
-      ping_.get()));
-}
-
-void ValidateRequest(const ProductDataVector& products,
-                     const CompletionInfo& expected_info,
-                     const PingEvent::Types expected_type,
-                     const Request& actual_request) {
-  EXPECT_EQ(2, actual_request.get_request_count());
-  AppRequestVector::const_iterator iter = actual_request.app_requests_begin();
-  for (int i = 0; iter != actual_request.app_requests_end(); ++iter, ++i) {
-    const AppRequest& app_request = *iter;
-    const AppRequestData& app_request_data = app_request.request_data();
-
-    EXPECT_EQ(1, app_request_data.num_ping_events());
-    EXPECT_STREQ(GuidToString(app_request_data.app_data().app_guid()),
-                 GuidToString(products[i].app_data().app_guid()));
-    EXPECT_STREQ(app_request_data.app_data().version(),
-                 products[i].app_data().version());
-    EXPECT_STREQ(app_request_data.app_data().previous_version(),
-                 products[i].app_data().previous_version());
-    EXPECT_STREQ(app_request_data.app_data().language(),
-                 products[i].app_data().language());
-
-    PingEventVector::const_iterator iter =
-        app_request_data.ping_events_begin();
-    const PingEvent& ping_event = *iter;
-
-    EXPECT_EQ(expected_type, ping_event.event_type());
-    EXPECT_EQ(
-        ping_utils::CompletionStatusToPingEventResult(expected_info.status),
-        ping_event.event_result());
-    EXPECT_EQ(expected_info.error_code, ping_event.error_code());
-    EXPECT_EQ(app_request_data.app_data().previous_version(),
-              ping_event.previous_version());
-  }
-}
-
-TEST_F(PingUtilsTest, BuildCompletedPingForAllProducts_Failure) {
-  ProductDataVector products;
-  bool is_machine = false;
-  AppData data1(StringToGuid(_T("{E66F2139-5469-BAAD-AC99-7863798E3A0A}")),
-                is_machine);
-  data1.set_version(_T("1.1.1.1"));
-  data1.set_previous_version(_T("1.0.0.0"));
-  data1.set_language(_T("en"));
-
-  ProductData product_data1;
-  product_data1.set_app_data(data1);
-  products.push_back(product_data1);
-
-  AppData data2(StringToGuid(_T("{E66F3140-5179-41ec-BAAD-7863798E3A0A}")),
-                is_machine);
-  data2.set_version(_T("1.1.1.1"));
-  data2.set_previous_version(_T("1.0.0.0"));
-  data2.set_language(_T("de"));
-
-  ProductData product_data2;
-  product_data2.set_app_data(data2);
-  products.push_back(product_data2);
-
-  CompletionInfo info(COMPLETION_ERROR, 10, _T("test"));
-
-  Request actual_request(is_machine);
-  ASSERT_HRESULT_SUCCEEDED(ping_utils::BuildCompletedPingForAllProducts(
-      products,
-      false,
-      info,
-      &actual_request));
-  ValidateRequest(products,
-                  info,
-                  PingEvent::EVENT_INSTALL_COMPLETE,
-                  actual_request);
-}
-
-TEST_F(PingUtilsTest, BuildCompletedPingForAllProducts_Update) {
-  ProductDataVector products;
-  bool is_machine = false;
-
-  AppData data1(StringToGuid(_T("{E66F2139-5469-BAAD-AC99-7863798E3A0A}")),
-                is_machine);
-  data1.set_version(_T("1.1.1.1"));
-  data1.set_previous_version(_T("1.0.0.0"));
-  data1.set_language(_T("en"));
-
-  ProductData product_data1;
-  product_data1.set_app_data(data1);
-  products.push_back(product_data1);
-
-  AppData data2(StringToGuid(_T("{E66F3140-5179-41ec-BAAD-7863798E3A0A}")),
-                is_machine);
-  data2.set_version(_T("1.1.1.1"));
-  data2.set_previous_version(_T("1.0.0.0"));
-  data2.set_language(_T("de"));
-
-  ProductData product_data2;
-  product_data2.set_app_data(data2);
-  products.push_back(product_data2);
-
-  CompletionInfo info(COMPLETION_ERROR, 10, _T("test"));
-
-  Request actual_request(is_machine);
-  ASSERT_HRESULT_SUCCEEDED(ping_utils::BuildCompletedPingForAllProducts(
-      products,
-      true,
-      info,
-      &actual_request));
-
-  ValidateRequest(products,
-                  info,
-                  PingEvent::EVENT_UPDATE_COMPLETE,
-                  actual_request);
-}
-
-TEST_F(PingUtilsTest, BuildCompletedPingForAllProducts_Success) {
-  ProductDataVector products;
-  bool is_machine = false;
-
-  AppData data1(StringToGuid(_T("{E66F2139-5469-BAAD-AC99-7863798E3A0A}")),
-                is_machine);
-  data1.set_version(_T("1.1.1.1"));
-  data1.set_previous_version(_T("1.0.0.0"));
-  data1.set_language(_T("en"));
-
-  ProductData product_data1;
-  product_data1.set_app_data(data1);
-  products.push_back(product_data1);
-
-  AppData data2(StringToGuid(_T("{E66F3140-5179-41ec-BAAD-7863798E3A0A}")),
-                is_machine);
-  data2.set_version(_T("1.1.1.1"));
-  data2.set_previous_version(_T("1.0.0.0"));
-  data2.set_language(_T("de"));
-
-  ProductData product_data2;
-  product_data2.set_app_data(data2);
-  products.push_back(product_data2);
-
-  CompletionInfo info(COMPLETION_SUCCESS, 0, _T("test"));
-
-  Request actual_request(is_machine);
-  ASSERT_HRESULT_SUCCEEDED(ping_utils::BuildCompletedPingForAllProducts(
-      products,
-      false,
-      info,
-      &actual_request));
-
-  ValidateRequest(products,
-                  info,
-                  PingEvent::EVENT_INSTALL_COMPLETE,
-                  actual_request);
-}
-
-
-}  // namespace omaha
diff --git a/worker/product_data.h b/worker/product_data.h
deleted file mode 100644
index bbbd2b6..0000000
--- a/worker/product_data.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// product_data.h: Class encapsulates the hierarchy of products and components.
-
-#ifndef OMAHA_WORKER_PRODUCT_DATA_H__
-#define OMAHA_WORKER_PRODUCT_DATA_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include <vector>
-#include "base/basictypes.h"
-#include "omaha/common/debug.h"
-#include "omaha/worker/application_data.h"
-
-namespace omaha {
-
-// TODO(omaha):  This hierarchy pattern is similar between ProductData,
-// AppRequest, UpdateResponse.  We should make a common class or a template to
-// handle this instead of duplicating the pattern.
-class ProductData {
- public:
-  ProductData() {}
-  explicit ProductData(const AppData& app_data) { app_data_ = app_data; }
-
-  void set_app_data(const AppData& app_data) {
-    app_data_ = app_data;
-  }
-  const AppData& app_data() const { return app_data_; }
-  void AddComponent(const AppData& component_data) {
-    components_.push_back(component_data);
-  }
-
-  AppDataVector::const_iterator components_begin() const {
-    return components_.begin();
-  }
-
-  AppDataVector::const_iterator components_end() const {
-    return components_.end();
-  }
-
-  size_t num_components() const { return components_.size(); }
-
- private:
-  AppData app_data_;
-  AppDataVector components_;
-};
-
-typedef std::vector<ProductData> ProductDataVector;
-
-}  // namespace omaha.
-
-#endif  // OMAHA_WORKER_PRODUCT_DATA_H__
-
diff --git a/worker/progresswnd_unittest.cc b/worker/progresswnd_unittest.cc
deleted file mode 100644
index 55cac82..0000000
--- a/worker/progresswnd_unittest.cc
+++ /dev/null
@@ -1,255 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// This unit test is driving the UI through its states so that we can
-// visually inspect all the controls are there in their right state and
-// position. To go from state to state, simply close the window on the screen.
-//
-// The unit test is useful for debugging UI states so different tests are
-// enabled/disabled at compile time, depending what needs to be tested.
-
-#include <windows.h>
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/utils.h"
-#include "goopdate/google_update_idl.h"
-#include "omaha/testing/unit_test.h"
-#include "omaha/worker/ui.h"
-
-namespace omaha {
-
-class UITest : public testing::Test,
-               public ProgressWndEvents,
-               public WaitCallbackInterface {
- protected:
-  UITest() : progress_wnd_(&progress_wnd_message_loop_, NULL) {
-  }
-
-  static void SetUpTestCase() {
-    message_loop_.set_message_handler(&message_handler_);
-    reset(ev_, ::CreateEvent(NULL, false, false, NULL));
-  }
-
-  virtual void SetUp() {
-    ASSERT_TRUE(::ResetEvent(get(ev_)));
-    ASSERT_TRUE(message_loop_.RegisterWaitForSingleObject(get(ev_), this));
-    progress_wnd_.SetEventSink(this);
-    progress_wnd_.set_product_name(_T("FooBar"));
-    EXPECT_SUCCEEDED(progress_wnd_.Initialize());
-    EXPECT_TRUE(progress_wnd_.CenterWindow(NULL));
-    progress_wnd_.SetVisible(true);
-  }
-
-  virtual void TearDown() {
-    message_loop_.UnregisterWait(get(ev_));
-  }
-
-  //
-  // ProgressWndEvents
-  //
-  virtual void DoPause() {
-  }
-
-  virtual void DoResume() {
-  }
-
-  virtual void DoClose() {
-    ASSERT_TRUE(::SetEvent(get(ev_)));
-  }
-
-  virtual void DoRestartBrowsers() {
-    ASSERT_TRUE(::SetEvent(get(ev_)));
-  }
-
-  virtual void DoReboot() {
-    ASSERT_TRUE(::SetEvent(get(ev_)));
-  }
-
-  virtual void DoLaunchBrowser(const CString&) {
-  }
-
-
-  //
-  // WaitCallbackInterface
-  //
-  virtual bool HandleSignaled(HANDLE) {
-    // Makes the message pump stop.
-    return false;
-  }
-
-  void FormatWindowTitle(const TCHAR* text) {
-    CString title;
-    progress_wnd_.GetWindowText(CStrBuf(title, 256), 256);
-    CString new_title;
-    new_title.Format(_T("%s - %s"), title, text);
-    progress_wnd_.SetWindowText(new_title);
-  }
-
-  static BasicMessageHandler message_handler_;
-  static MessageLoopWithWait message_loop_;
-  ProgressWnd progress_wnd_;
-  CMessageLoop progress_wnd_message_loop_;
-  static scoped_event ev_;
-};
-
-BasicMessageHandler UITest::message_handler_;
-MessageLoopWithWait UITest::message_loop_;
-scoped_event UITest::ev_;
-
-/*
-TEST_F(UITest, Initialize) {
-  FormatWindowTitle(_T("Initialize"));
-  message_loop_.Process();
-}
-
-TEST_F(UITest, OnWaitingToDownload) {
-  FormatWindowTitle(_T("Waiting to download"));
-  progress_wnd_.OnWaitingToDownload();
-  message_loop_.Process();
-}
-
-TEST_F(UITest, OnDownloading1) {
-  FormatWindowTitle(_T("Downloading"));
-  progress_wnd_.OnDownloading(10000, 0);
-  message_loop_.Process();
-}
-
-TEST_F(UITest, OnDownloading2) {
-  FormatWindowTitle(_T("Downloading"));
-  progress_wnd_.OnDownloading(5000, 50);
-  message_loop_.Process();
-}
-
-TEST_F(UITest, OnDownloading3) {
-  FormatWindowTitle(_T("Downloading"));
-  progress_wnd_.OnDownloading(0, 100);
-  message_loop_.Process();
-}
-
-TEST_F(UITest, OnWaitingToInstall) {
-  FormatWindowTitle(_T("Waiting to install"));
-  progress_wnd_.OnWaitingToInstall();
-  message_loop_.Process();
-}
-
-TEST_F(UITest, OnInstall) {
-  FormatWindowTitle(_T("Installing"));
-  progress_wnd_.OnInstalling();
-  message_loop_.Process();
-}
-
-TEST_F(UITest, OnPause) {
-  FormatWindowTitle(_T("Paused"));
-  progress_wnd_.OnPause();
-  message_loop_.Process();
-}
-*/
-
-TEST_F(UITest, OnCompleteSuccess) {
-  FormatWindowTitle(_T("Complete success"));
-  progress_wnd_.OnComplete(
-      COMPLETION_CODE_SUCCESS,
-      _T("Thanks for installing Gears. For more information on using ")
-      _T("Gears visit the ")
-      _T("<a=http://www.google.com/gears/>Gears</a> web site."),
-      0);
-  message_loop_.Process();
-}
-
-TEST_F(UITest, OnCompleteError) {
-  FormatWindowTitle(_T("Complete error"));
-  progress_wnd_.OnComplete(
-      COMPLETION_CODE_ERROR,
-      _T("An error occured while installing Gears: an existing copy of ")
-      _T("Gears is currently running. Please exit the software and ")
-      _T("retry installation. For more information visit the ")
-      _T("<a=http://www.google.com/gears/>Gears</a> web site."),
-      11);
-  message_loop_.Process();
-}
-
-TEST_F(UITest, OnCompleteRestartAllBrowsers) {
-  FormatWindowTitle(_T("Restart browsers"));
-  progress_wnd_.OnComplete(COMPLETION_CODE_RESTART_ALL_BROWSERS, NULL, 0);
-  message_loop_.Process();
-}
-
-TEST_F(UITest, OnCompleteReboot) {
-  FormatWindowTitle(_T("Reboot"));
-  progress_wnd_.OnComplete(COMPLETION_CODE_REBOOT, NULL, 0);
-  message_loop_.Process();
-}
-
-TEST_F(UITest, OnCompleteRestartBrowser) {
-  FormatWindowTitle(_T("Restart browser"));
-  progress_wnd_.OnComplete(COMPLETION_CODE_RESTART_BROWSER, NULL, 0);
-  message_loop_.Process();
-}
-
-TEST_F(UITest, OnCompleteRestartAllBrowsersNoticeOnly) {
-  FormatWindowTitle(_T("Restart browsers"));
-  progress_wnd_.OnComplete(COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY,
-                           NULL,
-                           0);
-  message_loop_.Process();
-}
-
-TEST_F(UITest, OnCompleteRebootNoticeOnly) {
-  FormatWindowTitle(_T("Reboot"));
-  progress_wnd_.OnComplete(COMPLETION_CODE_REBOOT_NOTICE_ONLY, NULL, 0);
-  message_loop_.Process();
-}
-
-TEST_F(UITest, OnCompleteRestartBrowserNoticeOnly) {
-  FormatWindowTitle(_T("Restart browser"));
-  progress_wnd_.OnComplete(COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY,
-                           NULL,
-                           0);
-  message_loop_.Process();
-}
-
-// Test the OnComplete can be called multiple times.
-TEST_F(UITest, OnMultipleCompletes) {
-  FormatWindowTitle(_T("Complete success"));
-  progress_wnd_.OnComplete(
-      COMPLETION_CODE_SUCCESS,
-      _T("Thanks for installing Gears. For more information on using ")
-      _T("Gears visit the ")
-      _T("<a=http://www.google.com/gears/>Gears</a> web site."),
-      0);
-
-  FormatWindowTitle(_T("Complete error"));
-  progress_wnd_.OnComplete(
-      COMPLETION_CODE_ERROR,
-      _T("An error occured while installing Gears: an existing copy of ")
-      _T("Gears is currently running. Please exit the software and ")
-      _T("retry installation. For more information visit the ")
-      _T("<a=http://www.google.com/gears/>Gears</a> web site."),
-      0);
-
-  progress_wnd_.OnComplete(
-      COMPLETION_CODE_ERROR,
-      _T("An error occured while installing Gears: an existing copy of ")
-      _T("Gears is currently running. Please exit the software and ")
-      _T("retry installation. For more information visit the ")
-      _T("<a=http://www.google.com/gears/>Gears</a> web site."),
-      0);
-
-  FormatWindowTitle(_T("Restart browsers"));
-  progress_wnd_.OnComplete(COMPLETION_CODE_RESTART_ALL_BROWSERS, NULL, 0);
-  message_loop_.Process();
-}
-
-}   // namespace omaha
-
diff --git a/worker/ui.cc b/worker/ui.cc
deleted file mode 100644
index a15ae35..0000000
--- a/worker/ui.cc
+++ /dev/null
@@ -1,843 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include "omaha/worker/ui.h"
-
-#include "base/basictypes.h"
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/exception_barrier.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/system_info.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/ui_displayed_event.h"
-#include "omaha/worker/ui_ctls.h"
-#include "omaha/worker/worker_metrics.h"
-
-namespace omaha {
-
-InstallStoppedWnd::InstallStoppedWnd(CMessageLoop* message_loop, HWND parent)
-    : message_loop_(message_loop),
-      parent_(parent) {
-  CORE_LOG(L3, (_T("[InstallStoppedWnd::InstallStoppedWnd]")));
-  ASSERT1(message_loop);
-  ASSERT1(::IsWindow(parent));
-}
-
-InstallStoppedWnd::~InstallStoppedWnd() {
-  CORE_LOG(L3, (_T("[InstallStoppedWnd::~InstallStoppedWnd]")));
-  if (IsWindow()) {
-    VERIFY1(SUCCEEDED(CloseWindow()));
-  }
-}
-
-// Enables the parent window and destroys this window.
-// Enabling the parent window before destroying this one causes the parent
-// window to get the focus and avoids a visible momentary lack of focus if we
-// instead call SetFocus for the parent after the window is destroyed.
-HRESULT InstallStoppedWnd::CloseWindow() {
-  ASSERT1(IsWindow());
-  VERIFY1(::EnableWindow(parent_, true));
-
-  return DestroyWindow() ? S_OK : HRESULTFromLastError();
-}
-
-// Disables the parent window.
-LRESULT InstallStoppedWnd::OnInitDialog(UINT,
-                                        WPARAM,
-                                        LPARAM,
-                                        BOOL& handled) {    // NOLINT
-  VERIFY1(!::EnableWindow(parent_, false));
-  VERIFY1(message_loop_->AddMessageFilter(this));
-  handled = true;
-  return 1;
-}
-
-LRESULT InstallStoppedWnd::OnClickButton(WORD,
-                                         WORD id,
-                                         HWND,
-                                         BOOL& handled) {   // NOLINT
-  ExceptionBarrier eb;
-  CORE_LOG(L3, (_T("[InstallStoppedWnd::OnClickButton]")));
-  ASSERT1(id == IDOK || id == IDCANCEL);
-  VERIFY1(::PostMessage(parent_, WM_INSTALL_STOPPED, id, 0));
-  handled = true;
-  return 0;
-}
-
-LRESULT InstallStoppedWnd::OnDestroy(UINT,
-                                     WPARAM,
-                                     LPARAM,
-                                     BOOL& handled) {  // NOLINT
-  VERIFY1(message_loop_->RemoveMessageFilter(this));
-  handled = true;
-  return 0;
-}
-
-ProgressWnd::ProgressWnd(CMessageLoop* message_loop, HWND parent)
-    : message_loop_(message_loop),
-      parent_(parent),
-      thread_id_(::GetCurrentThreadId()),
-      cur_state_(STATE_INIT),
-      is_close_enabled_(true),
-      events_sink_(NULL),
-      is_machine_(false),
-      product_guid_(GUID_NULL) {
-  ASSERT1(message_loop);
-  CORE_LOG(L3, (_T("[ProgressWnd::ProgressWnd]")));
-}
-
-ProgressWnd::~ProgressWnd() {
-  CORE_LOG(L3, (_T("[ProgressWnd::~ProgressWnd]")));
-  ASSERT1(thread_id_ == ::GetCurrentThreadId());
-  ASSERT1(!IsWindow());
-  cur_state_ = STATE_END;
-}
-
-HRESULT ProgressWnd::Initialize() {
-  CORE_LOG(L3, (_T("[ProgressWnd::Initialize]")));
-  cur_state_ = STATE_INIT;
-  ASSERT1(thread_id_ == ::GetCurrentThreadId());
-
-  // For the InitCommonControlsEx call to succeed on XP a manifest is
-  // needed to declare "Microsoft.Windows.Common-Controls" as a dependent
-  // assembly. Further work may be needed to ensure W2K compatibility.
-  INITCOMMONCONTROLSEX init_ctrls = { sizeof(INITCOMMONCONTROLSEX), 0 };
-  ASSERT1(init_ctrls.dwSize == sizeof(init_ctrls));
-  init_ctrls.dwICC = ICC_STANDARD_CLASSES | ICC_PROGRESS_CLASS;
-  if (!::InitCommonControlsEx(&init_ctrls)) {
-    // In case of XP RTM and XP SP1, InitCommonControlsEx is failing,
-    // however the UI initializes fine and works correctly. Because of this
-    // we only log the failure and do not error out.
-    HRESULT hr = HRESULTFromLastError();
-    CORE_LOG(LEVEL_ERROR, (_T("[InitCommonControlsEx failed][0x%08x]"), hr));
-    ASSERT1(hr == 0x80070582);
-  }
-
-  if (!Create(parent_)) {
-    CORE_LOG(LEVEL_ERROR, (_T("[Failed to create the window]")));
-    return GOOPDATE_E_UI_INTERNAL_ERROR;
-  }
-  VERIFY1(message_loop_->AddMessageFilter(this));
-  return S_OK;
-}
-
-HRESULT ProgressWnd::GetWindowTitle(TCHAR* title, int size) {
-  CORE_LOG(L3, (_T("[ProgressWnd::GetWindowTitle]")));
-  ASSERT1(size);
-  if (!size) return E_INVALIDARG;
-
-  // The text is truncated if there is no enough space in the buffer.
-  int result = GetWindowText(title, size);
-  CORE_LOG(L4, (_T("[title=%s]"), title));
-  return result ? S_OK : HRESULTFromLastError();
-}
-
-HRESULT ProgressWnd::SetWindowTitle(const TCHAR* title) {
-  CORE_LOG(L3, (_T("[ProgressWnd::SetWindowTitle][%s]"), title));
-  return SetWindowText(title) ? S_OK : HRESULTFromLastError();
-}
-
-LRESULT ProgressWnd::OnInitDialog(UINT,
-                                  WPARAM,
-                                  LPARAM,
-                                  BOOL& handled) {    // NOLINT
-  ExceptionBarrier eb;
-  CORE_LOG(L3, (_T("[ProgressWnd::OnInitDialog]")));
-  handled = true;
-  ASSERT1(!product_name_.IsEmpty());
-  CString s;
-  s.FormatMessage(IDS_WINDOW_TITLE, product_name_);
-  VERIFY1(SetWindowText(s));
-  pause_resume_text_.reset(new StaticEx);
-  pause_resume_text_->SubclassWindow(GetDlgItem(IDC_PAUSE_RESUME_TEXT));
-  VERIFY1(CenterWindow(NULL));
-
-  VERIFY1(s.LoadString(IDS_INITIALIZING));
-  VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s));
-  VERIFY1(SUCCEEDED(SetMarqueeMode(true)));
-  VERIFY1(SUCCEEDED(ChangeControlState()));
-  VERIFY1(SUCCEEDED(SetWindowIcon()));
-
-  metrics_timer_.reset(new HighresTimer);
-  return 1;  // Let the system set the focus.
-}
-
-LRESULT ProgressWnd::OnClose(UINT,
-                             WPARAM,
-                             LPARAM,
-                             BOOL& handled) {         // NOLINT
-  ExceptionBarrier eb;
-  CORE_LOG(L3, (_T("[ProgressWnd::OnClose]")));
-
-  ++metric_worker_ui_click_x;
-
-  MaybeCloseWindow();
-  handled = true;
-  return 0;
-}
-
-HRESULT ProgressWnd::CloseWindow() {
-  HRESULT hr = DestroyWindow() ? S_OK : HRESULTFromLastError();
-  if (events_sink_) {
-    events_sink_->DoClose();
-  }
-  return hr;
-}
-
-void ProgressWnd::MaybeRequestExitProcess() {
-  CORE_LOG(L3, (_T("[ProgressWnd::MaybeRequestExitProcess]")));
-  if (cur_state_ != STATE_COMPLETE_SUCCESS &&
-      cur_state_ != STATE_COMPLETE_ERROR &&
-      cur_state_ != STATE_COMPLETE_RESTART_BROWSER &&
-      cur_state_ != STATE_COMPLETE_RESTART_ALL_BROWSERS &&
-      cur_state_ != STATE_COMPLETE_REBOOT) {
-    return;
-  }
-
-  RequestExitProcess();
-}
-
-void ProgressWnd::RequestExitProcess() {
-  CORE_LOG(L3, (_T("[ProgressWnd::RequestExitProcess]")));
-  ::PostQuitMessage(0);
-}
-
-bool ProgressWnd::MaybeCloseWindow() {
-  if (cur_state_ != STATE_COMPLETE_SUCCESS &&
-      cur_state_ != STATE_COMPLETE_ERROR &&
-      cur_state_ != STATE_COMPLETE_RESTART_BROWSER &&
-      cur_state_ != STATE_COMPLETE_RESTART_ALL_BROWSERS &&
-      cur_state_ != STATE_COMPLETE_REBOOT) {
-    // The UI is not in final state: ask the user to proceed with closing it.
-    // A modal dialog opens and sends a message back to this window to
-    // communicate the user decision.
-    install_stopped_wnd_.reset(new InstallStoppedWnd(message_loop_, *this));
-    HWND hwnd = install_stopped_wnd_->Create(*this);
-    if (hwnd) {
-      CString title;
-      VERIFY1(title.LoadString(IDS_INSTALLATION_STOPPED_WINDOW_TITLE));
-      VERIFY1(install_stopped_wnd_->SetWindowText(title));
-
-      CString button_text;
-      VERIFY1(button_text.LoadString(IDS_RESUME_INSTALLATION));
-      VERIFY1(::SetWindowText(
-          install_stopped_wnd_->GetDlgItem(IDOK), button_text));
-
-      VERIFY1(button_text.LoadString(IDS_CANCEL_INSTALLATION));
-      VERIFY1(::SetWindowText(
-          install_stopped_wnd_->GetDlgItem(IDCANCEL), button_text));
-
-      CString s;
-      s.FormatMessage(IDS_INSTALL_STOPPED, product_name_);
-      VERIFY1(::SetWindowText(
-          install_stopped_wnd_->GetDlgItem(IDC_INSTALL_STOPPED_TEXT), s));
-
-      VERIFY1(install_stopped_wnd_->CenterWindow(*this));
-      VERIFY1(!install_stopped_wnd_->ShowWindow(SW_SHOWDEFAULT));
-      return false;
-    } else {
-      ASSERT1(false);
-    }
-  }
-
-  // The UI is in a final state or the dialog box failed to open: proceed with
-  // closing the UI.
-  VERIFY1(SUCCEEDED(CloseWindow()));
-  return true;
-}
-
-LRESULT ProgressWnd::OnNCDestroy(UINT,
-                                 WPARAM,
-                                 LPARAM,
-                                 BOOL& handled) {         // NOLINT
-  ExceptionBarrier eb;
-  CORE_LOG(L3, (_T("[ProgressWnd::OnNCDestroy]")));
-  VERIFY1(message_loop_->RemoveMessageFilter(this));
-  MaybeRequestExitProcess();
-  handled = false;  // Let ATL default processing handle the WM_NCDESTROY.
-  return 0;
-}
-
-LRESULT ProgressWnd::OnClickedButton(WORD,
-                                     WORD id,
-                                     HWND,
-                                     BOOL& handled) {   // NOLINT
-  ExceptionBarrier eb;
-  CORE_LOG(L3, (_T("[ProgressWnd::OnClickedButton]")));
-  ASSERT1(id == IDC_BUTTON1 || id == IDC_BUTTON2 || id == IDC_CLOSE);
-  handled = true;
-  DestroyWindow();
-
-  if (events_sink_) {
-#pragma warning(push)
-// C4061: enumerator 'xxx' in switch of enum 'yyy' is not explicitly handled by
-// a case label.
-#pragma warning(disable : 4061)
-
-    switch (id) {
-      case IDC_BUTTON1:
-        switch (cur_state_) {
-          case STATE_COMPLETE_RESTART_BROWSER:
-            ++metric_worker_ui_restart_browser_now_click;
-            events_sink_->DoRestartBrowsers();
-            break;
-          case STATE_COMPLETE_RESTART_ALL_BROWSERS:
-            ++metric_worker_ui_restart_all_browsers_now_click;
-            events_sink_->DoRestartBrowsers();
-            break;
-          case STATE_COMPLETE_REBOOT:
-            ++metric_worker_ui_reboot_now_click;
-            events_sink_->DoReboot();
-            break;
-          default:
-            ASSERT1(false);
-        }
-        break;
-      case IDC_BUTTON2:
-        switch (cur_state_) {
-          case STATE_COMPLETE_RESTART_BROWSER:
-          case STATE_COMPLETE_RESTART_ALL_BROWSERS:
-          case STATE_COMPLETE_REBOOT:
-            break;
-          default:
-            ASSERT1(false);
-        }
-        break;
-      case IDC_CLOSE:
-        switch (cur_state_) {
-          case STATE_COMPLETE_SUCCESS:
-          case STATE_COMPLETE_ERROR:
-            break;
-          default:
-            ASSERT1(false);
-        }
-        break;
-      default:
-        ASSERT1(false);
-    }
-
-#pragma warning(pop)
-
-    events_sink_->DoClose();
-  }
-  return 0;
-}
-
-// Called when esc key is hit.
-// If close is disabled, does nothing because we don't want the window to close.
-LRESULT ProgressWnd::OnCancel(WORD, WORD id,
-                              HWND, BOOL& handled) {    // NOLINT
-  ExceptionBarrier eb;
-  VERIFY1(id == IDCANCEL);
-
-  if (!is_close_enabled_) {
-    return 0;
-  }
-
-  ++metric_worker_ui_esc_key_total;
-  MaybeCloseWindow();
-  handled = true;
-  return 0;
-}
-
-LRESULT ProgressWnd::OnUrlClicked(int,
-                                  LPNMHDR params,
-                                  BOOL& handled) {      // NOLINT
-  CORE_LOG(L3, (_T("[ProgressWnd::OnUrlClicked]")));
-  ASSERT1(params);
-  handled = true;
-
-  if (IDC_GET_HELP_TEXT == params->idFrom) {
-    ++metric_worker_ui_get_help_click;
-  }
-
-  NM_STATICEX* notification = reinterpret_cast<NM_STATICEX*>(params);
-  events_sink_->DoLaunchBrowser(notification->action);
-
-  return 1;
-}
-
-LRESULT ProgressWnd::OnInstallStopped(UINT msg,
-                                      WPARAM wparam,
-                                      LPARAM,
-                                      BOOL& handled) {  // NOLINT
-  CORE_LOG(L3, (_T("[ProgressWnd::OnInstallStopped]")));
-  UNREFERENCED_PARAMETER(msg);
-
-  install_stopped_wnd_.reset();
-
-  ASSERT1(msg == WM_INSTALL_STOPPED);
-  ASSERT1(wparam == IDOK || wparam == IDCANCEL);
-  switch (wparam) {
-    case IDOK:
-      // TODO(omaha): Implement "Resume" here.
-      break;
-    case IDCANCEL:
-      // The user has decided to cancel.
-      metric_worker_ui_cancel_ms.AddSample(metrics_timer_->GetElapsedMs());
-      ++metric_worker_ui_cancels;
-      DestroyWindow();
-      if (events_sink_) {
-        events_sink_->DoClose();
-      }
-      break;
-    default:
-      ASSERT1(false);
-      break;
-  }
-
-  handled = true;
-  return 0;
-}
-
-void ProgressWnd::OnCheckingForUpdate() {
-  CORE_LOG(L3, (_T("[ProgressWnd::OnCheckingForUpdate]")));
-  ASSERT1(thread_id_ == ::GetCurrentThreadId());
-  if (!IsWindow()) {
-    return;
-  }
-
-  cur_state_ = STATE_CHECKING_FOR_UPDATE;
-
-  CString s;
-  VERIFY1(s.LoadString(IDS_WAITING_TO_CONNECT));
-  VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s));
-
-  VERIFY1(SUCCEEDED(SetMarqueeMode(true)));
-  VERIFY1(SUCCEEDED(ChangeControlState()));
-}
-
-void ProgressWnd::OnUpdateAvailable(const TCHAR* version_string) {
-  UNREFERENCED_PARAMETER(version_string);
-  CORE_LOG(L3, (_T("[ProgressWnd::OnUpdateAvailable][%s]"), version_string));
-  ASSERT1(thread_id_ == ::GetCurrentThreadId());
-  if (!IsWindow()) {
-    return;
-  }
-}
-
-void ProgressWnd::OnWaitingToDownload() {
-  CORE_LOG(L3, (_T("[ProgressWnd::OnWaitingToDownload]")));
-  ASSERT1(thread_id_ == ::GetCurrentThreadId());
-  if (!IsWindow()) {
-    return;
-  }
-
-  cur_state_ = STATE_WAITING_TO_DOWNLOAD;
-
-  CString s;
-  s.FormatMessage(IDS_WAITING_TO_DOWNLOAD, product_name_);
-  VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s));
-
-  VERIFY1(SUCCEEDED(SetMarqueeMode(true)));
-  VERIFY1(SUCCEEDED(ChangeControlState()));
-}
-
-void ProgressWnd::OnDownloading(int time_remaining_ms, int pos) {
-  CORE_LOG(L5, (_T("[ProgressWnd::OnDownloading][pos=%d]"), pos));
-  ASSERT1(thread_id_ == ::GetCurrentThreadId());
-  if (!IsWindow()) {
-    return;
-  }
-
-  ASSERT1(time_remaining_ms >=0);
-  ASSERT1(0 <= pos && pos <= 100);
-
-  time_remaining_ms;  // unreferenced formal parameter
-
-  cur_state_ = STATE_DOWNLOADING;
-
-  CString s;
-  s.FormatMessage(IDS_DOWNLOADING, product_name_);
-  VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s));
-
-  VERIFY1(s.LoadString(IDS_PAUSE));
-  VERIFY1(::SetWindowText(GetDlgItem(IDC_PAUSE_RESUME_TEXT), s));
-
-  // TODO(omaha): implement time left.
-
-  // When the network is connecting keep the marquee moving, otherwise
-  // the user has no indication something is still going on.
-  // TODO(omaha): when resuming an incomplete download this will not work.
-  VERIFY1(SUCCEEDED(SetMarqueeMode(pos == 0)));
-  ::SendMessage(GetDlgItem(IDC_PROGRESS), PBM_SETPOS, pos, 0);
-  VERIFY1(SUCCEEDED(ChangeControlState()));
-}
-
-void ProgressWnd::OnWaitingToInstall() {
-  CORE_LOG(L3, (_T("[ProgressWnd::OnWaitingToInstall]")));
-  ASSERT1(thread_id_ == ::GetCurrentThreadId());
-  if (!IsWindow()) {
-    return;
-  }
-
-  cur_state_ = STATE_WAITING_TO_INSTALL;
-
-  CString s;
-  s.FormatMessage(IDS_WAITING_TO_INSTALL, product_name_);
-  VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s));
-
-  VERIFY1(SUCCEEDED(SetMarqueeMode(true)));
-  VERIFY1(SUCCEEDED(ChangeControlState()));
-}
-
-void ProgressWnd::OnInstalling() {
-  CORE_LOG(L3, (_T("[ProgressWnd::OnInstalling]")));
-  ASSERT1(thread_id_ == ::GetCurrentThreadId());
-  if (!IsWindow()) {
-    return;
-  }
-
-  cur_state_ = STATE_INSTALLING;
-
-  // Close the 'Install Stop' window if it is on the screen because the user
-  // cannot cancel an install anyway.
-  CloseInstallStoppedWindow();
-
-  CString s;
-  s.FormatMessage(IDS_INSTALLING, product_name_);
-  VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s));
-
-  VERIFY1(SUCCEEDED(EnableClose(false)));
-  VERIFY1(SUCCEEDED(SetMarqueeMode(true)));
-  VERIFY1(SUCCEEDED(ChangeControlState()));
-}
-
-void ProgressWnd::OnPause() {
-  CORE_LOG(L3, (_T("[ProgressWnd::OnPause]")));
-  ASSERT(false, (_T("These strings are not translated or in .rc files.")));
-  ASSERT1(thread_id_ == ::GetCurrentThreadId());
-  if (!IsWindow()) {
-    return;
-  }
-
-  cur_state_ = STATE_PAUSED;
-
-  CString s;
-  s.FormatMessage(IDS_DOWNLOAD_PAUSED, product_name_);
-  VERIFY1(::SetWindowText(GetDlgItem(IDC_INSTALLER_STATE_TEXT), s));
-
-  VERIFY1(s.LoadString(IDS_RESUME));
-  VERIFY1(::SetWindowText(GetDlgItem(IDC_PAUSE_RESUME_TEXT), s));
-
-  // TODO(omaha): implement time left.
-
-  VERIFY1(SUCCEEDED(ChangeControlState()));
-}
-
-void ProgressWnd::OnShow() {
-  CORE_LOG(L3, (_T("[ProgressWnd::OnShow]")));
-  ASSERT1(thread_id_ == ::GetCurrentThreadId());
-  if (!IsWindow() || IsWindowVisible()) {
-    return;
-  }
-
-  CenterWindow(NULL);
-  SetVisible(true);
-
-  if (!::SetForegroundWindow(*this)) {
-    CORE_LOG(LW, (_T("[::SetForegroundWindow failed %d]"), ::GetLastError()));
-  }
-
-  UIDisplayedEventManager::SignalEvent(is_machine_);
-}
-
-// The 'error_code' can contain an HRESULT or an installer error.
-void ProgressWnd::OnComplete(CompletionCodes code,
-                             const TCHAR* text,
-                             DWORD error_code) {
-  CORE_LOG(L3, (_T("[ProgressWnd::OnComplete]")
-                _T("[code=%d][error_code=0x%08x]"), code, error_code));
-  ASSERT1(thread_id_ == ::GetCurrentThreadId());
-
-  if (!IsWindow()) {
-    RequestExitProcess();
-    return;
-  }
-
-  // It is possible for the OnComplete callback to be called multiple times.
-  // Subclassing the control multiple times results in a crash, therefore
-  // unsubclass the control if the control has been created and subclassed
-  // before.
-  if (complete_text_ != NULL) {
-    complete_text_->UnsubclassWindow(true);
-    complete_text_.reset(NULL);
-  }
-
-  // Close the 'Install Stop' window if it is on the screen.
-  CloseInstallStoppedWindow();
-
-  CString s;
-  switch (code) {
-    case COMPLETION_CODE_SUCCESS_CLOSE_UI:
-      cur_state_ = STATE_COMPLETE_SUCCESS;
-      VERIFY1(SUCCEEDED(CloseWindow()));
-      return;
-
-    case COMPLETION_CODE_SUCCESS:
-      cur_state_ = STATE_COMPLETE_SUCCESS;
-      VERIFY1(s.LoadString(IDS_CLOSE));
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_CLOSE), s));
-      complete_text_.reset(new StaticEx);
-      complete_text_->SubclassWindow(GetDlgItem(IDC_COMPLETE_TEXT));
-      ASSERT1(text);
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), text));
-      break;
-    case COMPLETION_CODE_ERROR:
-      cur_state_ = STATE_COMPLETE_ERROR;
-      VERIFY1(s.LoadString(IDS_CLOSE));
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_CLOSE), s));
-      complete_text_.reset(new StaticEx);
-      complete_text_->SubclassWindow(GetDlgItem(IDC_ERROR_TEXT));
-      ASSERT1(text);
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_ERROR_TEXT), text));
-      VERIFY1(SUCCEEDED(ShowGetHelpLink(error_code)));
-      break;
-    case COMPLETION_CODE_RESTART_ALL_BROWSERS:
-      cur_state_ = STATE_COMPLETE_RESTART_ALL_BROWSERS;
-      VERIFY1(s.LoadString(IDS_RESTART_ALL_BROWSERS_NOW));
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON1), s));
-      VERIFY1(s.LoadString(IDS_RESTART_ALL_BROWSERS_LATER));
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON2), s));
-      s.FormatMessage(IDS_TEXT_RESTART_ALL_BROWSERS, product_name_);
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), s));
-      ++metric_worker_ui_restart_all_browsers_buttons_displayed;
-      break;
-    case COMPLETION_CODE_RESTART_BROWSER:
-      cur_state_ = STATE_COMPLETE_RESTART_BROWSER;
-      VERIFY1(s.LoadString(IDS_RESTART_BROWSER_NOW));
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON1), s));
-      VERIFY1(s.LoadString(IDS_RESTART_BROWSER_LATER));
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON2), s));
-      s.FormatMessage(IDS_TEXT_RESTART_BROWSER, product_name_);
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), s));
-      ++metric_worker_ui_restart_browser_buttons_displayed;
-      break;
-    case COMPLETION_CODE_REBOOT:
-      ASSERT(false, (_T("The button actions are not implemented.")));
-      cur_state_ = STATE_COMPLETE_REBOOT;
-      VERIFY1(s.LoadString(IDS_RESTART_NOW));
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON1), s));
-      VERIFY1(s.LoadString(IDS_RESTART_LATER));
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_BUTTON2), s));
-      s.FormatMessage(IDS_TEXT_REBOOT, product_name_);
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), s));
-      ++metric_worker_ui_reboot_buttons_displayed;
-      break;
-    case COMPLETION_CODE_RESTART_ALL_BROWSERS_NOTICE_ONLY:
-      cur_state_ = STATE_COMPLETE_SUCCESS;
-      VERIFY1(s.LoadString(IDS_CLOSE));
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_CLOSE), s));
-      s.FormatMessage(IDS_TEXT_RESTART_ALL_BROWSERS, product_name_);
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), s));
-      break;
-    case COMPLETION_CODE_REBOOT_NOTICE_ONLY:
-      cur_state_ = STATE_COMPLETE_SUCCESS;
-      VERIFY1(s.LoadString(IDS_CLOSE));
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_CLOSE), s));
-      s.FormatMessage(IDS_TEXT_REBOOT, product_name_);
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), s));
-      break;
-    case COMPLETION_CODE_RESTART_BROWSER_NOTICE_ONLY:
-      cur_state_ = STATE_COMPLETE_SUCCESS;
-      VERIFY1(s.LoadString(IDS_CLOSE));
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_CLOSE), s));
-      s.FormatMessage(IDS_TEXT_RESTART_BROWSER, product_name_);
-      VERIFY1(::SetWindowText(GetDlgItem(IDC_COMPLETE_TEXT), s));
-      break;
-    case COMPLETION_CODE_RUN_COMMAND:
-    default:
-      ASSERT1(false);
-      break;
-  }
-
-  VERIFY1(SUCCEEDED(EnableClose(true)));
-  VERIFY1(SUCCEEDED(ChangeControlState()));
-}
-
-HRESULT ProgressWnd::ChangeControlState() {
-  for (size_t i = 0; i != arraysize(ProgressWnd::ctls_); ++i) {
-    const ControlState& ctl_state = ctls_[i];
-    HWND hwnd = GetDlgItem(ctls_[i].id_);
-    ASSERT1(hwnd);
-    const ControlAttributes& attr = ctl_state.attr_[cur_state_];
-    ::ShowWindow(hwnd, attr.is_visible_ ? SW_SHOW : SW_HIDE);
-    ::EnableWindow(hwnd, attr.is_enabled_ ? true : false);
-    if (attr.is_button_ && attr.is_default_) {
-      // We ask the dialog manager to give the default push button the focus, so
-      // for instance the <Enter> key works as expected.
-      GotoDlgCtrl(hwnd);
-      LONG style = ::GetWindowLong(hwnd, GWL_STYLE);
-      if (style) {
-        style |= BS_DEFPUSHBUTTON;
-        ::SetWindowLong(hwnd, GWL_STYLE, style);
-      }
-    }
-  }
-  return S_OK;
-}
-
-HRESULT ProgressWnd::SetMarqueeMode(bool is_marquee) {
-  if (!SystemInfo::IsRunningOnXPOrLater()) {
-    // Marquee is not supported on OSes below XP.
-    return S_OK;
-  }
-
-  HWND progress_bar = GetDlgItem(IDC_PROGRESS);
-  if (!progress_bar) {
-    return GOOPDATE_E_UI_INTERNAL_ERROR;
-  }
-
-  LONG style = ::GetWindowLong(progress_bar, GWL_STYLE);
-  if (!style) {
-    return HRESULTFromLastError();
-  }
-
-  if (is_marquee) {
-    if (style & PBS_MARQUEE) {
-      return S_OK;
-    }
-
-    style |= PBS_MARQUEE;
-    style = ::SetWindowLong(progress_bar, GWL_STYLE, style);
-    if (!style) {
-      return HRESULTFromLastError();
-    }
-
-    bool result = ::SendMessage(progress_bar, PBM_SETMARQUEE,
-                                is_marquee, kMarqueeModeUpdatesMs) != 0;
-    return result ? S_OK : GOOPDATE_E_UI_INTERNAL_ERROR;
-  } else {
-    if (!(style & PBS_MARQUEE)) {
-      return S_OK;
-    }
-
-    style &= ~PBS_MARQUEE;
-    style = ::SetWindowLong(progress_bar, GWL_STYLE, style);
-    if (!style) {
-      return HRESULTFromLastError();
-    }
-    return S_OK;
-  }
-}
-
-HRESULT ProgressWnd::EnableClose(bool enable) {
-  is_close_enabled_ = enable;
-  return EnableSystemCloseButton(is_close_enabled_);
-}
-
-HRESULT ProgressWnd::EnableSystemCloseButton(bool enable) {
-  HMENU menu = ::GetSystemMenu(*this, false);
-  ASSERT1(menu);
-  uint32 flags = MF_BYCOMMAND;
-  flags |= enable ? MF_ENABLED : MF_GRAYED;
-  VERIFY1(::EnableMenuItem(menu, SC_CLOSE, flags) != -1);
-  return S_OK;
-}
-
-// The system displays the system large icon in the ALT+TAB dialog box.
-// We do not need any small icon in the window caption. However, setting
-// ICON_BIG has the side effect of the window displaying a scaled down
-// version of it in the window caption. We could not find any way to
-// hide that icon, including setting the icon to NULL or handling WM_GETICON
-// message.
-HRESULT ProgressWnd::SetWindowIcon() {
-  const int cx = ::GetSystemMetrics(SM_CXICON);
-  const int cy = ::GetSystemMetrics(SM_CYICON);
-  HINSTANCE exe_instance = reinterpret_cast<HINSTANCE>(kExeLoadingAddress);
-  reset(hicon_,
-        reinterpret_cast<HICON>(::LoadImage(exe_instance,
-                                            MAKEINTRESOURCE(IDI_APP),
-                                            IMAGE_ICON,
-                                            cx,
-                                            cy,
-                                            LR_DEFAULTCOLOR)));
-  if (!hicon_) {
-    HRESULT hr = HRESULTFromLastError();
-    CORE_LOG(LEVEL_ERROR, (_T("[LoadImage failed 0x%08x]"), hr));
-    return hr;
-  }
-  VERIFY1(SendMessage(WM_SETICON,
-                      ICON_BIG,
-                      reinterpret_cast<LPARAM>(get(hicon_))) == NULL);
-  return S_OK;
-}
-
-HRESULT ProgressWnd::ShowGetHelpLink(HRESULT error_code) {
-  // When running elevated and ProcessLauncherClass is not registered, the
-  // browser launch from the link will fail. Don't display a link that will not
-  // work.
-  // TODO(omaha): Determine if ProcessLauncherClass is registered. Maybe move
-  // this code to the Worker.
-  if (vista_util::IsVistaOrLater() && vista_util::IsUserAdmin()) {
-    return S_OK;
-  }
-
-  // Do not display the link if the error already has a link.
-  if (GOOPDATE_E_OS_NOT_SUPPORTED == error_code) {
-    return S_OK;
-  }
-
-  const TCHAR* const kHelpLinkSourceId = _T("gethelp");
-  CString url;
-  HRESULT hr = goopdate_utils::BuildHttpGetString(
-      kUrlMoreInformation,
-      error_code,
-      0,   // extra code 1
-      0,   // extra code 2
-      GuidToString(product_guid_),
-      GetVersionString(),
-      is_machine_,
-      language_,
-      iid_,
-      brand_code_,
-      kHelpLinkSourceId,
-      &url);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  const TCHAR* const kLinkFormat = _T("<a=%s>%s</a>");
-  CString display_text;
-  VERIFY1(display_text.LoadString(IDS_HELP_ME_FIX_THIS_TEXT));
-  CString link_string;
-  link_string.Format(kLinkFormat, url, display_text);
-
-  get_help_text_.reset(new StaticEx);
-  get_help_text_->SubclassWindow(GetDlgItem(IDC_GET_HELP_TEXT));
-  VERIFY1(::SetWindowText(GetDlgItem(IDC_GET_HELP_TEXT), link_string));
-
-  ++metric_worker_ui_get_help_displayed;
-  return S_OK;
-}
-
-bool ProgressWnd::CloseInstallStoppedWindow() {
-  if (install_stopped_wnd_.get() && install_stopped_wnd_->IsWindow()) {
-    VERIFY1(SUCCEEDED(install_stopped_wnd_->CloseWindow()));
-    install_stopped_wnd_.reset();
-    return true;
-  } else {
-    return false;
-  }
-}
-
-}  // namespace omaha
-
diff --git a/worker/ui.h b/worker/ui.h
deleted file mode 100644
index 6202fc6..0000000
--- a/worker/ui.h
+++ /dev/null
@@ -1,248 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_WORKER_UI_H__
-#define OMAHA_WORKER_UI_H__
-
-#include <atlbase.h>
-#include <vector>
-#include "base/scoped_ptr.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/wtl_atlapp_wrapper.h"
-#include "omaha/goopdate/resource.h"
-#include "omaha/worker/job_observer.h"
-#include "omaha/worker/uilib/static_ex.h"
-
-namespace omaha {
-
-class HighresTimer;
-
-// The message is used to communicate between InstallStoppedWnd and
-// ProgressWnd.
-const DWORD WM_INSTALL_STOPPED = WM_APP;
-
-// Implements the "Installation Stopped" window. InstallStoppedWnd is
-// modal relative to its parent. When InstallStoppedWnd is closed it sends
-// a user message to its parent to notify which button the user has clicked on.
-class InstallStoppedWnd
-    : public CAxDialogImpl<InstallStoppedWnd>,
-      public CMessageFilter {
-  typedef CAxDialogImpl<InstallStoppedWnd> Base;
- public:
-
-  static const int IDD = IDD_INSTALL_STOPPED;
-
-  InstallStoppedWnd(CMessageLoop* message_loop, HWND parent);
-  ~InstallStoppedWnd();
-
-  // Closes the window, handling transition back to the parent window.
-  HRESULT CloseWindow();
-
-  BOOL PreTranslateMessage(MSG* msg) {
-    return CWindow::IsDialogMessage(msg);
-  }
-
-  BEGIN_MSG_MAP(InstallStoppedWnd)
-    MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
-    MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
-    COMMAND_ID_HANDLER(IDOK, OnClickButton)
-    COMMAND_ID_HANDLER(IDCANCEL, OnClickButton)
-    CHAIN_MSG_MAP(Base)
-  END_MSG_MAP()
-
- private:
-  // Message and command handlers.
-  // All message handlers must be protected by exception barriers.
-  LRESULT OnInitDialog(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);    // NOLINT
-  LRESULT OnClickButton(WORD notify_code, WORD id, HWND wnd_ctl, BOOL& handled);  // NOLINT
-  LRESULT OnDestroy(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);       // NOLINT
-
-  CMessageLoop* message_loop_;
-  HWND parent_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(InstallStoppedWnd);
-};
-
-class ProgressWndEvents;
-
-// Implements the UI progress window.
-class ProgressWnd
-    : public CAxDialogImpl<ProgressWnd>,
-      public CMessageFilter,
-      public JobObserver {
-  typedef CAxDialogImpl<ProgressWnd> Base;
- public:
-  static const int IDD = IDD_PROGRESS;
-
-  ProgressWnd(CMessageLoop* message_loop, HWND parent);
-  ~ProgressWnd();
-
-  HRESULT Initialize();
-
-  void SetVisible(bool visible) {
-    ShowWindow(visible ? SW_SHOWNORMAL : SW_HIDE);
-  }
-
-  BOOL PreTranslateMessage(MSG* msg) {
-    return CWindow::IsDialogMessage(msg);
-  }
-
-  HRESULT GetWindowTitle(TCHAR* title, int size);
-  HRESULT SetWindowTitle(const TCHAR* title);
-
-  void set_is_machine(bool is_machine) { is_machine_ = is_machine; }
-  void set_language(const CString& language) { language_ = language; }
-  void set_product_name(const CString& name) { product_name_ = name; }
-  void set_product_guid(const GUID& guid) { product_guid_ = guid; }
-  void set_iid(const GUID& iid) { iid_ = iid; }
-  void set_brand_code(const CString& brand_code) { brand_code_ = brand_code; }
-
-  // These methods are called by the job to transition the UI from
-  // one state to another. The methods are always executed by the thread
-  // that created this window.
-  virtual void OnShow();
-  virtual void OnCheckingForUpdate();
-  virtual void OnUpdateAvailable(const TCHAR* version_string);
-  virtual void OnWaitingToDownload();
-  virtual void OnDownloading(int time_remaining_ms, int pos);
-  virtual void OnWaitingToInstall();
-  virtual void OnInstalling();
-  virtual void OnPause();
-  virtual void OnComplete(CompletionCodes code,
-                          const TCHAR* text,
-                          DWORD error_code);
-  virtual void SetEventSink(ProgressWndEvents* ev) { events_sink_ = ev; }
-  virtual void Uninitialize() {}
-
-  BEGIN_MSG_MAP(ProgressWnd)
-    MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
-    MESSAGE_HANDLER(WM_CLOSE, OnClose)
-    MESSAGE_HANDLER(WM_NCDESTROY, OnNCDestroy)
-    MESSAGE_HANDLER(WM_INSTALL_STOPPED, OnInstallStopped)
-    NOTIFY_CODE_HANDLER(MK_LBUTTON, OnUrlClicked)
-    COMMAND_HANDLER(IDC_BUTTON1, BN_CLICKED, OnClickedButton)
-    COMMAND_HANDLER(IDC_BUTTON2, BN_CLICKED, OnClickedButton)
-    COMMAND_HANDLER(IDC_CLOSE,   BN_CLICKED, OnClickedButton)
-    COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
-    CHAIN_MSG_MAP(Base)
-  END_MSG_MAP()
-
- private:
-  // Message and command handlers.
-  // All message handlers must be protected by exception barriers.
-  LRESULT OnInitDialog(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);  // NOLINT
-  LRESULT OnClose(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);       // NOLINT
-  LRESULT OnNCDestroy(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);   // NOLINT
-  LRESULT OnInstallStopped(UINT msg, WPARAM wparam,
-                           LPARAM lparam, BOOL& handled);   // NOLINT
-  LRESULT OnClickedButton(WORD notify_code, WORD id,
-                          HWND wnd_ctl, BOOL& handled);     // NOLINT
-  LRESULT OnCancel(WORD notify_code, WORD id, HWND wnd_ctl, BOOL& handled);     // NOLINT
-  LRESULT OnUrlClicked(int idCtrl, LPNMHDR pnmh, BOOL& bHandled);               // NOLINT
-
-  // Helpers.
-  HRESULT ChangeControlState();
-  HRESULT SetMarqueeMode(bool is_marquee);
-  HRESULT EnableClose(bool enable);
-  HRESULT EnableSystemCloseButton(bool enable);
-  HRESULT SetWindowIcon();
-  HRESULT ShowGetHelpLink(HRESULT error_code);
-
-  // Returns true if the window is closed.
-  bool MaybeCloseWindow();
-
-  // Closes the Installation Stopped window if present. Returns true if closed.
-  bool CloseInstallStoppedWindow();
-
-  // Closes the window.
-  HRESULT CloseWindow();
-
-  void MaybeRequestExitProcess();
-  void RequestExitProcess();
-
-  // The states are used as indexes in zero-based arrays so they should
-  // start at 0.
-  enum States {
-    STATE_INIT = 0,
-    STATE_CHECKING_FOR_UPDATE,
-    STATE_WAITING_TO_DOWNLOAD,
-    STATE_DOWNLOADING,
-    STATE_WAITING_TO_INSTALL,
-    STATE_INSTALLING,
-    STATE_PAUSED,
-    STATE_COMPLETE_SUCCESS,
-    STATE_COMPLETE_ERROR,
-    STATE_COMPLETE_RESTART_BROWSER,
-    STATE_COMPLETE_RESTART_ALL_BROWSERS,
-    STATE_COMPLETE_REBOOT,
-    STATE_END,
-  };
-
-#pragma warning(disable : 4510 4610)
-// C4510: default constructor could not be generated
-// C4610: struct can never be instantiated - user defined constructor required
-  struct ControlAttributes {
-    const bool is_visible_;
-    const bool is_enabled_;
-    const bool is_button_;
-    const bool is_default_;
-  };
-
-  struct ControlState {
-    const int id_;
-    const ControlAttributes attr_[ProgressWnd::STATE_END + 1];
-  };
-#pragma warning(default : 4510 4610)
-
-  static const ControlState ctls_[];
-
-  CMessageLoop* message_loop_;
-  HWND parent_;
-  DWORD thread_id_;
-
-  scoped_ptr<HighresTimer> metrics_timer_;
-
-  // Due to a repaint issue in StaticEx we prefer to manage their lifetime
-  // very aggressively so we contain them by reference instead of value.
-  scoped_ptr<StaticEx> pause_resume_text_;
-  scoped_ptr<StaticEx> complete_text_;
-  scoped_ptr<StaticEx> get_help_text_;
-
-  States cur_state_;
-  bool is_close_enabled_;
-
-  ProgressWndEvents* events_sink_;
-
-  bool is_machine_;
-  CString language_;
-  CString product_name_;
-  GUID product_guid_;
-  GUID iid_;
-  CString brand_code_;
-
-  // The speed by which the progress bar moves in marquee mode.
-  static const int kMarqueeModeUpdatesMs = 75;
-
-  scoped_ptr<InstallStoppedWnd> install_stopped_wnd_;
-  scoped_hicon  hicon_;   // Handle to large icon to show when ALT-TAB
-
-  friend class UITest;
-  DISALLOW_EVIL_CONSTRUCTORS(ProgressWnd);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_UI_H__
-
diff --git a/worker/ui_ctls.h b/worker/ui_ctls.h
deleted file mode 100644
index 4e4cc83..0000000
--- a/worker/ui_ctls.h
+++ /dev/null
@@ -1,211 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// This is a description on UI elements for different states of the UI.
-// We have only one dialog which changes between different UI states. Some
-// controls are hidded, some controls are disabled, etc...
-
-#ifndef OMAHA_WORKER_UI_CTLS_H__
-#define OMAHA_WORKER_UI_CTLS_H__
-
-#include "omaha/worker/ui.h"
-
-namespace omaha {
-
-const ProgressWnd::ControlState ProgressWnd::ctls_[] = {
-    // The struct values are:
-    // is_visible, is_enabled, is_button, is_default
-  { IDC_PROGRESS,
-    { { true,  true,  false, false },     // STATE_INIT
-      { true,  true,  false, false },     // STATE_CHECKING_FOR_UPDATE
-      { true,  true,  false, false },     // STATE_WAITING_TO_DOWNLOAD
-      { true,  true,  false, false },     // STATE_DOWNLOADING
-      { true,  true,  false, false },     // STATE_WAITING_TO_INSTALL
-      { true,  true,  false, false },     // STATE_INSTALLING
-      { true,  true,  false, false },     // STATE_PAUSED
-      { false, false, false, false },     // STATE_COMPLETE_SUCCESS
-      { false, false, false, false },     // STATE_COMPLETE_ERROR
-      { false, false, false, false },     // STATE_COMPLETE_RESTART_BROWSER
-      { false, false, false, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
-      { false, false, false, false },     // STATE_COMPLETE_REBOOT
-      { false, false, false, false },     // STATE_END
-    },
-  },
-  { IDC_PAUSE_RESUME_TEXT,
-    { { false, false, false, false },     // STATE_INIT
-      { false, false, false, false },     // STATE_CHECKING_FOR_UPDATE
-      { false, false, false, false },     // STATE_WAITING_TO_DOWNLOAD
-      { false,  true, false, false },     // STATE_DOWNLOADING
-      { false, false, false, false },     // STATE_WAITING_TO_INSTALL
-      { false, false, false, false },     // STATE_INSTALLING
-      { false,  true, false, false },     // STATE_PAUSED
-      { false, false, false, false },     // STATE_COMPLETE_SUCCESS
-      { false, false, false, false },     // STATE_COMPLETE_ERROR
-      { false, false, false, false },     // STATE_COMPLETE_RESTART_BROWSER
-      { false, false, false, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
-      { false, false, false, false },     // STATE_COMPLETE_REBOOT
-      { false, false, false, false },     // STATE_END
-    },
-  },
-  { IDC_INFO_TEXT,
-    { { false, false, false, false },     // STATE_INIT
-      { false, false, false, false },     // STATE_CHECKING_FOR_UPDATE
-      { false, false, false, false },     // STATE_WAITING_TO_DOWNLOAD
-      { false, true,  false, false },     // STATE_DOWNLOADING
-      { false, false, false, false },     // STATE_WAITING_TO_INSTALL
-      { false, false, false, false },     // STATE_INSTALLING
-      { false, false, false, false },     // STATE_PAUSED
-      { false, false, false, false },     // STATE_COMPLETE_SUCCESS
-      { false, false, false, false },     // STATE_COMPLETE_ERROR
-      { false, false, false, false },     // STATE_COMPLETE_RESTART_BROWSER
-      { false, false, false, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
-      { false, false, false, false },     // STATE_COMPLETE_REBOOT
-      { false, false, false, false },     // STATE_END
-    },
-  },
-  { IDC_INSTALLER_STATE_TEXT,
-    { { true,  true,  false, false },     // STATE_INIT
-      { true,  true,  false, false },     // STATE_CHECKING_FOR_UPDATE
-      { true,  true,  false, false },     // STATE_WAITING_TO_DOWNLOAD
-      { true,  true,  false, false },     // STATE_DOWNLOADING
-      { true,  true,  false, false },     // STATE_WAITING_TO_INSTALL
-      { true,  true,  false, false },     // STATE_INSTALLING
-      { true,  true,  false, false },     // STATE_PAUSED
-      { false, false, false, false },     // STATE_COMPLETE_SUCCESS
-      { false, false, false, false },     // STATE_COMPLETE_ERROR
-      { false, false, false, false },     // STATE_COMPLETE_RESTART_BROWSER
-      { false, false, false, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
-      { false, false, false, false },     // STATE_COMPLETE_REBOOT
-      { false, false, false, false },     // STATE_END
-    },
-  },
-  { IDC_COMPLETE_TEXT,
-    { { false, false, false, false },     // STATE_INIT
-      { false, false, false, false },     // STATE_CHECKING_FOR_UPDATE
-      { false, false, false, false },     // STATE_WAITING_TO_DOWNLOAD
-      { false, false, false, false },     // STATE_DOWNLOADING
-      { false, false, false, false },     // STATE_WAITING_TO_INSTALL
-      { false, false, false, false },     // STATE_INSTALLING
-      { false, false, false, false },     // STATE_PAUSED
-      { true,  true,  false, false },     // STATE_COMPLETE_SUCCESS
-      { false, false, false, false },     // STATE_COMPLETE_ERROR
-      { true,  true,  false, false },     // STATE_COMPLETE_RESTART_BROWSER
-      { true,  true,  false, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
-      { true,  true,  false, false },     // STATE_COMPLETE_REBOOT
-      { false, false, false, false },     // STATE_END
-    },
-  },
-  { IDC_ERROR_TEXT,
-    { { false, false, false, false },     // STATE_INIT
-      { false, false, false, false },     // STATE_CHECKING_FOR_UPDATE
-      { false, false, false, false },     // STATE_WAITING_TO_DOWNLOAD
-      { false, false, false, false },     // STATE_DOWNLOADING
-      { false, false, false, false },     // STATE_WAITING_TO_INSTALL
-      { false, false, false, false },     // STATE_INSTALLING
-      { false, false, false, false },     // STATE_PAUSED
-      { false, false, false, false },     // STATE_COMPLETE_SUCCESS
-      { true,  true,  false, false },     // STATE_COMPLETE_ERROR
-      { false, false, false, false },     // STATE_COMPLETE_RESTART_BROWSER
-      { false, false, false, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
-      { false, false, false, false },     // STATE_COMPLETE_REBOOT
-      { false, false, false, false },     // STATE_END
-    },
-  },
-  { IDC_GET_HELP_TEXT,
-    { { false, false, false, false },     // STATE_INIT
-      { false, false, false, false },     // STATE_CHECKING_FOR_UPDATE
-      { false, false, false, false },     // STATE_WAITING_TO_DOWNLOAD
-      { false, false, false, false },     // STATE_DOWNLOADING
-      { false, false, false, false },     // STATE_WAITING_TO_INSTALL
-      { false, false, false, false },     // STATE_INSTALLING
-      { false, false, false, false },     // STATE_PAUSED
-      { false, false, false, false },     // STATE_COMPLETE_SUCCESS
-      { true,  true,  false, false },     // STATE_COMPLETE_ERROR
-      { false, false, false, false },     // STATE_COMPLETE_RESTART_BROWSER
-      { false, false, false, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
-      { false, false, false, false },     // STATE_COMPLETE_REBOOT
-      { false, false, false, false },     // STATE_END
-    },
-  },
-  { IDC_BUTTON1,
-    { { false, false, true, false },     // STATE_INIT
-      { false, false, true, false },     // STATE_CHECKING_FOR_UPDATE
-      { false, false, true, false },     // STATE_WAITING_TO_DOWNLOAD
-      { false, false, true, false },     // STATE_DOWNLOADING
-      { false, false, true, false },     // STATE_WAITING_TO_INSTALL
-      { false, false, true, false },     // STATE_INSTALLING
-      { false, false, true, false },     // STATE_PAUSED
-      { false, false, true, false },     // STATE_COMPLETE_SUCCESS
-      { false, false, true, false },     // STATE_COMPLETE_ERROR
-      { true,  true,  true, true  },     // STATE_COMPLETE_RESTART_BROWSER
-      { true,  true,  true, true  },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
-      { true,  true,  true, true  },     // STATE_COMPLETE_REBOOT
-      { false, false, true, false },     // STATE_END
-    },
-  },
-  { IDC_BUTTON2,
-    { { false, false, true, false },     // STATE_INIT
-      { false, false, true, false },     // STATE_CHECKING_FOR_UPDATE
-      { false, false, true, false },     // STATE_WAITING_TO_DOWNLOAD
-      { false, false, true, false },     // STATE_DOWNLOADING
-      { false, false, true, false },     // STATE_WAITING_TO_INSTALL
-      { false, false, true, false },     // STATE_INSTALLING
-      { false, false, true, false },     // STATE_PAUSED
-      { false, false, true, false },     // STATE_COMPLETE_SUCCESS
-      { false, false, true, false },     // STATE_COMPLETE_ERROR
-      { true,  true,  true, false },     // STATE_COMPLETE_RESTART_BROWSER
-      { true,  true,  true, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
-      { true,  true,  true, false },     // STATE_COMPLETE_REBOOT
-      { false, false, true, false },     // STATE_END
-    },
-  },
-  { IDC_CLOSE,
-    { { false, false, true, false },     // STATE_INIT
-      { false, false, true, false },     // STATE_CHECKING_FOR_UPDATE
-      { false, false, true, false },     // STATE_WAITING_TO_DOWNLOAD
-      { false, false, true, false },     // STATE_DOWNLOADING
-      { false, false, true, false },     // STATE_WAITING_TO_INSTALL
-      { false, false, true, false },     // STATE_INSTALLING
-      { false, false, true, false },     // STATE_PAUSED
-      { true,  true,  true, true  },     // STATE_COMPLETE_SUCCESS
-      { true,  true,  true, true  },     // STATE_COMPLETE_ERROR
-      { false, false, true, false },     // STATE_COMPLETE_RESTART_BROWSER
-      { false, false, true, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
-      { false, false, true, false },     // STATE_COMPLETE_REBOOT
-      { false, false, true, false },     // STATE_END
-    },
-  },
-  { IDC_IMAGE,
-    { { false, false, false, false },     // STATE_INIT
-      { false, false, false, false },     // STATE_CHECKING_FOR_UPDATE
-      { false, false, false, false },     // STATE_WAITING_TO_DOWNLOAD
-      { false, false, false, false },     // STATE_DOWNLOADING
-      { false, false, false, false },     // STATE_WAITING_TO_INSTALL
-      { false, false, false, false },     // STATE_INSTALLING
-      { false, false, false, false },     // STATE_PAUSED
-      { true,  false, false, false },     // STATE_COMPLETE_SUCCESS
-      { false, false, false, false },     // STATE_COMPLETE_ERROR
-      { true,  false, false, false },     // STATE_COMPLETE_RESTART_BROWSER
-      { true,  false, false, false },     // STATE_COMPLETE_RESTART_ALL_BROWSERS
-      { true,  false, false, false },     // STATE_COMPLETE_REBOOT
-      { false, false, false, false },     // STATE_END
-    },
-  },
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_UI_CTLS_H__
-
diff --git a/worker/uilib/node.h b/worker/uilib/node.h
deleted file mode 100644
index 4521fb2..0000000
--- a/worker/uilib/node.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// node.h : Declaration of the Node
-
-#ifndef OMAHA_WORKER_UILIB_NODE_H_
-#define OMAHA_WORKER_UILIB_NODE_H_
-
-#include "omaha/worker/uilib/node_state.h"
-
-class Node {
- public:
-
-  explicit Node(HWND window) : node_state_(window) {}
-  virtual ~Node() {}
-
-  CString node_text() const { return node_text_; }
-  void AddText(const TCHAR* text) { node_text_ += text; }
-
-  void set_node_state(const NodeState& node_state) { node_state_ = node_state; }
-  const NodeState& node_state() const { return node_state_; }
-
- private:
-  CString       node_text_;
-  NodeState     node_state_;
-};
-
-#endif  // OMAHA_WORKER_UILIB_NODE_H_
diff --git a/worker/uilib/node_state.cc b/worker/uilib/node_state.cc
deleted file mode 100644
index 90c56d9..0000000
--- a/worker/uilib/node_state.cc
+++ /dev/null
@@ -1,237 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-
-#include "omaha/worker/uilib/node_state.h"
-
-// Add a list item tag
-NodeState::Tags NodeState::tags_[] = {
-// name_to_match        length_name_to_match       action      no_parameters;
-  { _T("<b>"),       static_cast<int>(_tcslen(_T("<b>"))),      BOLD_ON,        true  },  // NOLINT
-  { _T("</b>"),      static_cast<int>(_tcslen(_T("</b>"))),     BOLD_OFF,       true  },  // NOLINT
-  { _T("<i>"),       static_cast<int>(_tcslen(_T("<i>"))),      ITALIC_ON,      true  },  // NOLINT
-  { _T("</i>"),      static_cast<int>(_tcslen(_T("</i>"))),     ITALIC_OFF,     true  },  // NOLINT
-  { _T("<u>"),       static_cast<int>(_tcslen(_T("<u>"))),      UNDERLINE_ON,   true  },  // NOLINT
-  { _T("</u>"),      static_cast<int>(_tcslen(_T("</u>"))),     UNDERLINE_OFF,  true  },  // NOLINT
-  { _T("<color="),   static_cast<int>(_tcslen(_T("<color="))),  TEXTCOLOR_ON,   false },  // NOLINT
-  { _T("</color>"),  static_cast<int>(_tcslen(_T("</color>"))), TEXTCOLOR_OFF,  true  },  // NOLINT
-  { _T("<size="),    static_cast<int>(_tcslen(_T("<size="))),   TEXTSIZE_ON,    false },  // NOLINT
-  { _T("</size>"),   static_cast<int>(_tcslen(_T("</size>"))),  TEXTSIZE_OFF,   true  },  // NOLINT
-  { _T("<a="),       static_cast<int>(_tcslen(_T("<a="))),      URL_ON,         false },  // NOLINT
-  { _T("</a>"),      static_cast<int>(_tcslen(_T("</a>"))),     URL_OFF,        true  },  // NOLINT
-};
-
-NodeState::NodeState(HWND window)
-    : default_font_(NULL),
-      font_(NULL),
-      bold_(false),
-      italic_(false),
-      underline_(false),
-      text_color_(0),
-      text_size_(8),
-      owner_window_(window) {
-}
-
-NodeState::~NodeState() {
-}
-
-
-void NodeState::SetStdFont(HFONT font) {
-  default_font_ = font;
-}
-
-
-HFONT NodeState::GetFont() const {
-  if (IsDefaultFont())
-    return default_font_;
-
-  if (font_)
-    return font_;
-
-  if (default_font_) {
-    HDC dc = GetDC(owner_window_);
-
-    LOGFONT log_font;
-    GetObject(default_font_, sizeof(LOGFONT), &log_font);
-    log_font.lfWeight    = bold_ ? FW_BOLD : FW_NORMAL;
-    log_font.lfItalic    = italic_;
-    log_font.lfUnderline = underline_;
-    log_font.lfHeight    =
-        -MulDiv(text_size_, GetDeviceCaps(dc, LOGPIXELSY), 72);
-    font_ = CreateFontIndirect(&log_font);
-  }
-
-  return font_;
-}
-
-
-bool NodeState::IsDefaultFont() const {
-    if (bold_ || italic_ || underline_)
-        return false;
-
-    if (text_size_ != 8)
-        return false;
-
-    return true;
-}
-
-
-int NodeState::ConsumeTag(const TCHAR* string) {
-  int size = sizeof(tags_) / sizeof(tags_[0]);
-  for (int i = 0; i < size; i++) {
-    if (_tcsnicmp(string, tags_[i].name_to_match,
-                  tags_[i].length_name_to_match) == 0) {
-      if (tags_[i].no_parameters) {
-        ApplyAction(tags_[i].action, NULL);
-        return tags_[i].length_name_to_match;
-      } else {
-        return tags_[i].length_name_to_match +
-               ApplyAction(tags_[i].action, string +
-                           tags_[i].length_name_to_match) + 1;
-      }
-    }
-  }
-
-  return 0;
-}
-
-
-int NodeState::ApplyAction(Actions action, const TCHAR* string/*=NULL*/) {
-  int read = 0;
-  switch (action) {
-  case BOLD_ON :
-    bold_ = true;
-    break;
-
-  case BOLD_OFF :
-    bold_ = false;
-    break;
-
-  case ITALIC_ON :
-    italic_ = true;
-    break;
-
-  case ITALIC_OFF :
-    italic_ = false;
-    break;
-
-  case UNDERLINE_ON :
-    underline_ = true;
-    break;
-
-  case UNDERLINE_OFF :
-    underline_ = false;
-    break;
-
-  case TEXTCOLOR_ON :
-    ATLASSERT(string);
-    if (string) {
-      int nParam = 0;
-      read = ReadColorRef(string, &nParam);
-      text_color_ = nParam;
-    }
-    break;
-
-  case TEXTCOLOR_OFF :
-    text_color_ = 0;
-    break;
-
-  case TEXTSIZE_ON :
-    ATLASSERT(string);
-    if (string)
-      read = ReadNumParameter(string, &text_size_);
-    break;
-
-  case TEXTSIZE_OFF :
-    text_size_ = 8;
-    break;
-
-  case URL_ON :
-    underline_ = true;
-    text_color_ = RGB(0, 0, 0xff);
-    ATLASSERT(string);
-    if (string)
-      read = ReadString(string, &url_);
-    break;
-
-  case URL_OFF :
-    underline_ = false;
-    text_color_ = 0;
-    url_ = _T("");
-    break;
-
-  case UNKNOWN:
-    // fall thru
-
-  default:
-    ATLASSERT(false);
-  }
-  return read;
-}
-
-int NodeState::ReadNumParameter(const TCHAR* string, int* param) {
-  if (!param)
-    return 0;
-
-  *param = 0;
-  const TCHAR* current_pos = string;
-  while (current_pos && _istdigit(*current_pos)) {
-    *param *= 10;
-    *param += *current_pos - _T('0');
-    current_pos++;
-  }
-  return static_cast<int>(current_pos - string);
-}
-
-int NodeState::ReadHexParameter(const TCHAR* string, int* param) {
-  if (!param)
-    return 0;
-
-  *param = 0;
-  const TCHAR* current_pos = string;
-  while (current_pos && _istxdigit(*current_pos)) {
-    *param = ((*param) << 4);
-    if (_istdigit(*current_pos))
-      *param += *current_pos - _T('0');
-    else
-      *param += (*current_pos | 0x20) - _T('a') + 10;
-    current_pos++;
-  }
-  return static_cast<int>(current_pos - string);
-}
-
-int NodeState::ReadColorRef(const TCHAR* string, int* param) {
-  if (!param)
-    return 0;
-
-  int read = ReadHexParameter(string, param);
-  *param = RGB((*param) >> 16, ((*param) >> 8) & 0xff, (*param) & 0xff);
-  return read;
-}
-
-int NodeState::ReadString(const TCHAR* string, CString* string_out) {
-  if (!string_out)
-    return 0;
-
-  int length = 0;
-  int position = _tcscspn(string, _T(" >"));
-  if (position >= 0) {
-    *string_out = CString(string, position);
-    length = position;
-  }
-
-  return length;
-}
diff --git a/worker/uilib/node_state.h b/worker/uilib/node_state.h
deleted file mode 100644
index 9254dd7..0000000
--- a/worker/uilib/node_state.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-
-#ifndef OMAHA_WORKER_UILIB_NODE_STATE_H_
-#define OMAHA_WORKER_UILIB_NODE_STATE_H_
-
-#include <atlstr.h>
-
-class NodeState {
- public:
-  explicit NodeState(HWND window);
-  virtual ~NodeState();
-
-  void SetStdFont(HFONT font);
-  HFONT GetFont() const;
-  COLORREF text_color() const { return text_color_; }
-  bool IsURL() const { return !url_.IsEmpty(); }
-  CString url() const { return url_; }
-
-  int ConsumeTag(const TCHAR* string);
-
- private:
-  enum Actions {
-    UNKNOWN,
-    BOLD_ON,
-    BOLD_OFF,
-    ITALIC_ON,
-    ITALIC_OFF,
-    UNDERLINE_ON,
-    UNDERLINE_OFF,
-    TEXTCOLOR_ON,
-    TEXTCOLOR_OFF,
-    TEXTSIZE_ON,
-    TEXTSIZE_OFF,
-    URL_ON,
-    URL_OFF,
-  };
-
-  struct Tags {
-    const TCHAR*  name_to_match;
-    int           length_name_to_match;
-    Actions       action;
-    bool          no_parameters;
-  };
-
-  int ApplyAction(Actions action, const TCHAR* string);
-  int ReadNumParameter(const TCHAR* string, int* param);
-  int ReadHexParameter(const TCHAR* szString, int* param);
-  int ReadColorRef(const TCHAR* string, int* param);
-  int ReadString(const TCHAR* string, CString* string_out);
-  bool IsDefaultFont() const;
-
-  // Data
-  static Tags    tags_[];
-
-  HWND           owner_window_;
-  HFONT          default_font_;
-  mutable HFONT  font_;
-
-  bool           bold_;
-  bool           italic_;
-  bool           underline_;
-  COLORREF       text_color_;
-  int            text_size_;
-  CString        url_;
-};
-
-#endif  // OMAHA_WORKER_UILIB_NODE_STATE_H_
diff --git a/worker/uilib/static_ex.cc b/worker/uilib/static_ex.cc
deleted file mode 100644
index 5474016..0000000
--- a/worker/uilib/static_ex.cc
+++ /dev/null
@@ -1,595 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-
-// TODO(omaha): need to handle WM_GETTEXT
-// TODO(omaha): need to handle WM_SIZE
-// TODO(omaha): nice to have transparent mode
-
-#include "omaha/worker/uilib/static_ex.h"
-
-#include <shellapi.h>
-#include <strsafe.h>
-#include "omaha/worker/uilib/node_state.h"
-
-const int StaticEx::kBorderNone    = 0;
-const int StaticEx::kBorderLeft    = 1;
-const int StaticEx::kBorderTop     = 2;
-const int StaticEx::kBorderRight   = 4;
-const int StaticEx::kBorderBottom  = 8;
-const int StaticEx::kBorderAll     = kBorderLeft | kBorderTop | kBorderRight |
-                                     kBorderBottom;
-
-HCURSOR StaticEx::hand_cursor_ = NULL;
-
-StaticEx::StaticEx()
-    : margins_(1, 0, 1, 0),  // see comment in h file
-      background_color_(0xffffff),
-      use_background_color_(false),
-      ellipsis_(0),
-      border_(kBorderNone),
-      border_color_(0),
-      default_font_(NULL) {
-}
-
-StaticEx::~StaticEx() {
-  EraseNodes();
-  EraseLines(&lines_);
-}
-
-void StaticEx::Reset() {
-  text_.Empty();
-  EraseNodes();
-  EraseLines(&lines_);
-  default_font_ = NULL;
-}
-
-BOOL StaticEx::SubclassWindow(HWND window) {
-  Reset();
-  // first get text from exising control
-  unsigned length = ::SendMessage(window, WM_GETTEXTLENGTH, 0, 0);
-  CString text;
-  if (length > 0) {
-    TCHAR* buffer = text.GetBufferSetLength(length);
-    ::SendMessage(window,
-                  WM_GETTEXT,
-                  length + 1,
-                  reinterpret_cast<LPARAM>(buffer));
-    text.ReleaseBuffer(-1);
-  }
-
-  // then subclass
-  BOOL result = CWindowImpl<StaticEx>::SubclassWindow(window);
-
-  // set text back (it will parse it and replace text in subclassed control
-  // with readble text)
-  if (result && length > 0) {
-    SetWindowText(text);
-  }
-
-  return result;
-}
-
-HWND StaticEx::UnsubclassWindow(BOOL force /*= FALSE*/) {
-  Reset();  // clean up an old state
-  return CWindowImpl<StaticEx>::UnsubclassWindow(force);
-}
-
-LRESULT StaticEx::OnSetText(UINT msg, WPARAM wparam, LPARAM lparam,
-                            BOOL& handled) {
-  // parse text first, because we will need to get "readable" text
-  text_ = reinterpret_cast<const TCHAR*>(lparam);
-  ParseText();
-
-  // set readable text to subclassed control, this text will be return by
-  // GetWindowText() or WM_GETTEXT. (when GetWindowText is called from another
-  // process it doesn't send WM_GETTEXT but reads text directly from contol)
-  // so we need to set text to it.
-  // Disable redraw, without it calling DefWindowProc would redraw control
-  // immediately without sending WM_PAINT message
-  SetRedraw(FALSE);
-  DefWindowProc(msg, wparam,
-      reinterpret_cast<LPARAM>(static_cast<const TCHAR*>(GetReadableText())));
-  SetRedraw(TRUE);
-
-  // now invalidate to display new text
-  Invalidate();
-
-  handled = TRUE;
-  return 1;
-}
-
-LRESULT StaticEx::OnGetText(UINT, WPARAM wparam, LPARAM lparam, BOOL& handled) {  // NOLINT
-  if (!lparam) return 0;
-  unsigned size = static_cast<unsigned>(wparam);
-  TCHAR* buffer = reinterpret_cast<TCHAR*>(lparam);
-
-  handled = TRUE;
-  unsigned my_size = text_.GetLength();
-  if (my_size < size) {
-    StringCchCopy(buffer, size, text_);
-    return my_size;
-  }
-
-  StringCchCopyN(buffer, size, text_, size - 1);
-  buffer[size - 1] = 0;
-
-  return size - 1;
-}
-
-LRESULT StaticEx::OnGetTextLength(UINT, WPARAM, LPARAM, BOOL& handled) {  // NOLINT
-  handled = TRUE;
-  return text_.GetLength();
-}
-
-void StaticEx::set_background_color(COLORREF back_color) {
-  background_color_ = back_color;
-  use_background_color_ = true;
-  Invalidate();
-}
-
-void StaticEx::set_margins(const RECT& rect) {
-  margins_ = rect;
-  Invalidate();
-}
-
-void StaticEx::set_margins(int left, int top, int right, int bottom) {
-  margins_.SetRect(left, top, right, bottom);
-  Invalidate();
-}
-
-
-LRESULT StaticEx::OnLButtonDown(UINT, WPARAM wparam, LPARAM lparam, BOOL&) {
-  if (wparam != MK_LBUTTON)
-    return 0;
-
-  CPoint point(LOWORD(lparam), HIWORD(lparam));
-
-  int height = margins_.top + (border_ & kBorderTop) ? 1 : 0;
-  size_t size = lines_.size();
-  for (size_t i = 0; i < size; i++) {
-    height += lines_[i]->height();
-    if (point.y < height) {
-      CString action;
-      if (lines_[i]->IsUrlUnderMouse(point, &action) && !action.IsEmpty()) {
-        // First let the parent window handle the click.
-        LRESULT handled = 0;
-        NM_STATICEX notification = {0};
-        notification.header.hwndFrom = m_hWnd;
-        notification.header.idFrom = GetDlgCtrlID();
-        notification.header.code = wparam;
-        notification.action = action;
-
-        HWND parent = GetParent();
-        if (parent) {
-          handled = ::SendMessage(parent, WM_NOTIFY, notification.header.idFrom,
-                                  reinterpret_cast<LPARAM>(&notification));
-        }
-
-        // If the parent window did not handle the click, then we should try and
-        // handle the click ourself.
-        if (handled != 1 && _tcsnicmp(action, _T("http"), 4) == 0) {
-          // open URL in a browser
-          ShellExecute(m_hWnd, _T("open"), action, NULL, NULL, SW_SHOWNORMAL);
-        }
-      }
-      break;
-    }
-  }
-  return 0;
-}
-
-
-LRESULT StaticEx::OnSetCursor(UINT, WPARAM, LPARAM lparam, BOOL& handled) {  // NOLINT
-  int hit_test = LOWORD(lparam);
-  handled = FALSE;
-  if (hit_test != HTCLIENT) {
-    return 0;
-  }
-
-  POINT position;
-  if (!GetCursorPos(&position))
-    return 0;
-
-  ScreenToClient(&position);
-
-  int offset = margins_.top + (border_ & kBorderTop) ? 1 : 0;
-  size_t size = lines_.size();
-  for (size_t i = 0; i < size; i++) {
-    offset += lines_[i]->height();
-    if (position.y < offset) {
-      if (lines_[i]->IsUrlUnderMouse(position, NULL)) {
-        ::SetCursor(GetHandCursor());
-        handled = TRUE;
-      }
-      break;
-    }
-  }
-
-  return 0;
-}
-
-void StaticEx::set_ellipsis(int ellipsis) {
-  if (ellipsis == DT_END_ELLIPSIS  ||
-      ellipsis == DT_WORD_ELLIPSIS ||
-      ellipsis == DT_PATH_ELLIPSIS ||
-      ellipsis == 0) {
-    ellipsis_ = ellipsis;
-    if (ellipsis != 0)
-      ModifyStyle(0, SS_LEFTNOWORDWRAP);
-    Invalidate();
-  }
-}
-
-void StaticEx::set_border(int border) {
-  border_ = border;
-  Invalidate();
-}
-
-void StaticEx::set_border_color(COLORREF border_color) {
-  border_color_ = border_color;
-  Invalidate();
-}
-
-void StaticEx::ParseText() {
-  EraseNodes();
-  EraseLines(&lines_);
-
-  if (text_.IsEmpty())
-    return;
-
-  if (!default_font_)
-    default_font_ = GetFont();
-
-  NodeState node_state(m_hWnd);
-  node_state.SetStdFont(default_font_);
-
-  const TCHAR* current_string = text_;
-  int current_offset = 0;
-  bool had_good_tag = true;
-  while (*current_string) {
-    current_offset = 0;
-
-    if (had_good_tag) {
-      // if it was a good tag we consumed it and need to start with a new node
-      Node* node = new Node(m_hWnd);
-      nodes_.push_back(node);
-
-      // -1 if there is no Open Bracket "<"
-      current_offset = FindOpenBracket(current_string);
-
-      if (current_offset < 0) {
-        // no tags left, just plain text
-        node->AddText(current_string);
-        node->set_node_state(node_state);
-        break;
-      }
-
-      if (*current_string != _T('<')) {
-        // has some text before the tag
-        node->AddText(CString(current_string, current_offset));
-        node->set_node_state(node_state);
-        current_string += current_offset;
-        continue;
-      }
-
-      int next_offset = node_state.ConsumeTag(current_string + current_offset);
-
-      if (next_offset > 0) {
-        // it was a known tag
-        had_good_tag = true;
-        current_string += current_offset + next_offset;
-      }  else  {
-        // unknown tag, will keep looking
-        had_good_tag = false;
-        node->AddText(CString(current_string, current_offset + 1));
-        node->set_node_state(node_state);
-        current_string += current_offset + 1;
-        continue;
-      }
-    } else {
-      had_good_tag = true;
-    }
-    delete nodes_.back();
-    nodes_.pop_back();
-  }
-}
-
-CString StaticEx::GetReadableText() {
-  CString text;
-  for (size_t i = 0; i < nodes_.size(); i++) {
-    text += nodes_[i]->node_text();
-  }
-  return text;
-}
-
-void StaticEx::EraseNodes() {
-  for (size_t i = 0; i < nodes_.size(); ++i) {
-    delete nodes_[i];
-  }
-  nodes_.clear();
-}
-
-void StaticEx::EraseLines(std::vector<StaticLine*>* lines) {
-  size_t size = lines->size();
-  for (size_t i = 0; i < size; i++) {
-    delete (*lines)[i];
-  }
-  lines->clear();
-}
-
-int StaticEx::FindOpenBracket(const TCHAR* string) {
-  const TCHAR* left_bracket = _tcschr(string, _T('<'));
-
-  if (left_bracket == NULL)
-    return -1;
-
-  return static_cast<int>(left_bracket - string);
-}
-
-LRESULT StaticEx::OnPaint(UINT, WPARAM, LPARAM, BOOL&) {
-  PAINTSTRUCT paint_struct;
-  HDC hdc = BeginPaint(&paint_struct);
-
-  CRect client_rect;
-  GetClientRect(&client_rect);
-
-  CRect working_rect(client_rect);
-
-  working_rect.DeflateRect(margins_);
-  working_rect.DeflateRect((border_ & kBorderLeft)   ? 1 : 0,
-                           (border_ & kBorderTop)    ? 1 : 0,
-                           (border_ & kBorderRight)  ? 1 : 0,
-                           (border_ & kBorderBottom) ? 1 : 0);
-
-  DWORD style = GetStyle();
-
-  EraseLines(&lines_);
-  PrePaint(hdc, &lines_, nodes_, working_rect, style, ellipsis_);
-
-  if (use_background_color_) {
-    FillRect(hdc, &client_rect, CreateSolidBrush(background_color_));
-  } else {
-    HBRUSH brush = reinterpret_cast<HBRUSH>(::SendMessage(GetParent(),
-        WM_CTLCOLORSTATIC, reinterpret_cast<WPARAM>(hdc),
-        reinterpret_cast<LPARAM>(m_hWnd)));
-    if (brush) {
-      ::FillRect(hdc, &client_rect, brush);
-    }
-  }
-
-  if (border_ != kBorderNone)
-    DrawBorder(hdc, client_rect);
-
-  Paint(hdc, lines_, working_rect, style, ellipsis_);
-
-  EndPaint(&paint_struct);
-  return 0;
-}
-
-void StaticEx::PrePaint(HDC hdc, std::vector<StaticLine*>* lines,
-                        const std::vector<Node*>& nodes, RECT rect, DWORD style,
-                        int ellipsis) {
-  if (nodes.empty())
-    return;
-
-  int x = 0;
-  int width = rect.right - rect.left;
-  StaticLine* line = new StaticLine;
-  lines->push_back(line);
-  bool done = false;
-  size_t size = nodes.size();
-  for (size_t i = 0; i < size; ++i) {
-    Node* node = nodes[i];
-    const NodeState& node_state = node->node_state();
-    CString text = node->node_text();
-    int string_len = text.GetLength();
-
-    HFONT font = node_state.GetFont();
-    if (!font)
-      return;
-
-    HFONT old_font = static_cast<HFONT>(SelectObject(hdc, font));
-
-    TEXTMETRIC text_metrics;
-    GetTextMetrics(hdc, &text_metrics);
-
-    int height    = text_metrics.tmHeight + text_metrics.tmExternalLeading;
-    int base_line = text_metrics.tmHeight + text_metrics.tmExternalLeading -
-                    text_metrics.tmDescent;
-    line->AdjustHeight(height);
-    line->AdjustBaseLine(base_line);
-
-    bool single_line = (style & SS_LEFTNOWORDWRAP) != 0;
-
-    int  current_pos = 0;
-    bool more_left   = false;
-    while (true) {
-      int current_length = string_len - current_pos;
-
-      // find LF if any
-      int lf_position = text.Find(_T('\n'), current_pos);
-      if (lf_position == current_pos) {
-        if (single_line) {
-          if (ellipsis)
-            line->AddEllipses();
-          break;
-        }
-        line = new StaticLine;
-        lines->push_back(line);
-        line->AdjustHeight(height);
-        line->AdjustBaseLine(base_line);
-        x = 0;
-
-        current_pos++;
-
-        continue;
-      } else if (lf_position > 0) {
-        current_length = lf_position - current_pos;
-        more_left = true;
-      }
-
-      // check if it will fit in one line
-      int fit  = 0;
-      SIZE string_size;
-      GetTextExtentExPoint(hdc, static_cast<const TCHAR*>(text) + current_pos,
-                           current_length, width - x, &fit, NULL, &string_size);
-
-      if (fit < current_length) {
-        // string doesn't fit, need to move to the next line
-        // find last space
-        int fit_saved = fit;
-        for (; fit > 0; fit--) {
-          if (text.GetAt(current_pos + fit) == _T(' ')) {
-            break;
-          }
-        }
-
-        // if a first word of a node doesn't fit and it starts in a first half
-        // of control then wrap the word on a last char that fits
-        // otherwise move whole node to the next line
-        if ((fit <= 0) && (x < width / 2))
-          fit = fit_saved;
-
-        if (fit > 0) {
-          line->AddNode(node, current_pos, fit, height, base_line, width - x);
-        }
-
-        if (single_line) {
-          if (ellipsis)
-            line->AddEllipses();
-          done = true;
-          break;
-        }
-        line = new StaticLine;
-        lines->push_back(line);
-        line->AdjustHeight(height);
-        line->AdjustBaseLine(base_line);
-        x = 0;
-
-        current_pos += fit;
-        // skip spaces
-        while (text.GetAt(current_pos) == _T(' '))
-          current_pos++;
-        continue;
-      } else {
-        line->AddNode(node, current_pos, fit, height, base_line,
-                      string_size.cx);
-      }
-
-      // done, it fits
-      x += string_size.cx;
-      if (!more_left)
-        break;
-
-      current_pos += current_length;
-      more_left = false;
-    }
-
-    if (old_font)
-      SelectObject(hdc, old_font);
-
-    if (done)
-      break;
-  }
-}
-
-
-void StaticEx::Paint(HDC hdc, const std::vector<StaticLine*>& lines, RECT rect,
-                     DWORD style, int ellipsis) {
-  if ((style & SS_LEFTNOWORDWRAP) == 0) {
-    ellipsis = 0;
-  }
-
-  size_t size = lines.size();
-  int y = rect.top;
-  for (size_t i = 0; i < size; i++) {
-    int height = lines[i]->Paint(hdc, rect.left, rect.right, y, style,
-                                 ellipsis);
-    y += height;
-  }
-}
-
-LRESULT StaticEx::OnEraseBkgnd(UINT /*msg*/, WPARAM /*wparam*/,
-                               LPARAM /*lparam*/, BOOL& handled) {
-  handled = TRUE;
-  return 0;
-}
-
-void StaticEx::DrawBorder(HDC hdc, const CRect& rect) {
-  HGDIOBJ old_object = SelectObject(hdc, GetStockObject(DC_PEN));
-  SetDCPenColor(hdc, border_color_);
-  if (border_ & kBorderLeft) {
-    MoveToEx(hdc, rect.left, rect.top, NULL);
-    LineTo(hdc, rect.left, rect.bottom);
-  }
-  if (border_ & kBorderTop) {
-    MoveToEx(hdc, rect.left, rect.top, NULL);
-    LineTo(hdc, rect.right, rect.top);
-  }
-  if (border_ & kBorderRight) {
-    MoveToEx(hdc, rect.right - 1, rect.top, NULL);
-    LineTo(hdc, rect.right - 1, rect.bottom);
-  }
-  if (border_ & kBorderBottom) {
-    MoveToEx(hdc, rect.left, rect.bottom - 1, NULL);
-    LineTo(hdc, rect.right, rect.bottom - 1);
-  }
-  SelectObject(hdc, old_object);
-}
-
-int StaticEx::GetMinimumHeight(int width) {
-  HDC device_context = CreateCompatibleDC(NULL);
-
-  CRect client_rect;
-  if (width <= 0)
-    GetClientRect(&client_rect);
-  else
-    client_rect.SetRect(0, 0, width, 100);  // last value is not used
-
-  CRect working_rect(client_rect);
-
-  working_rect.DeflateRect(margins_);
-  working_rect.DeflateRect((border_ & kBorderLeft)   ? 1 : 0,
-                           (border_ & kBorderTop)    ? 1 : 0,
-                           (border_ & kBorderRight)  ? 1 : 0,
-                           (border_ & kBorderBottom) ? 1 : 0);
-
-  DWORD style = GetStyle();
-
-  std::vector<StaticLine*> lines;
-  PrePaint(device_context, &lines, nodes_, working_rect, style, ellipsis_);
-
-  DeleteDC(device_context);
-
-  int height = 0;
-  for (unsigned i = 0; i < lines.size(); i++) {
-    height += lines[i]->height();
-  }
-  height += margins_.top + margins_.bottom;
-  height += (border_ & kBorderTop) ? 1 : 0;
-  height += (border_ & kBorderBottom) ? 1 : 0;
-
-  return height;
-}
-
-
-HCURSOR StaticEx::GetHandCursor() {
-  if (hand_cursor_ == NULL) {
-    // Load cursor resource
-    hand_cursor_ = (HCURSOR)LoadCursor(NULL, IDC_HAND);  // doesn't work on NT4!
-  }
-  return hand_cursor_;
-}
diff --git a/worker/uilib/static_ex.h b/worker/uilib/static_ex.h
deleted file mode 100644
index def36d3..0000000
--- a/worker/uilib/static_ex.h
+++ /dev/null
@@ -1,154 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// static_ex.h : This class extends static control functionality to display
-// formatted text and hyper-links
-//
-// Currently it supports the following formatting options:
-//   bold         - <b>bold</b>
-//   italic       - <i>italic</i>
-//   underscore   - <u>underlined</u>
-//   color        - <color=ff0000>red</color>
-//   size         - <size=14>14 points text</size>
-//   hyperlink    - <a=http://www.google.com>click here</a>
-// formatting options could be nested (except hyperlink)
-//
-// Some fonts (including Tahoma) often overhang one pixel (for example in "W")
-// so StaticEx is created with default 1 pixel margin on the left and right,
-// use set_margins() to overwrite default values if you need to.
-
-
-#ifndef OMAHA_WORKER_UILIB_STATIC_EX_H_
-#define OMAHA_WORKER_UILIB_STATIC_EX_H_
-
-#include <windows.h>
-#include <atlbase.h>
-#include <atlwin.h>
-#include <vector>
-#include "omaha/worker/uilib/node.h"
-#include "omaha/worker/uilib/static_line.h"
-
-
-// extension of NMHDR to provide StaticEx specific info in notification message
-struct NM_STATICEX {
-  NMHDR header;
-  const TCHAR* action;
-};
-
-class StaticEx : public CWindowImpl<StaticEx> {
- public:
-  DECLARE_WND_SUPERCLASS(NULL, _T("STATIC"))
-
-  StaticEx();
-  virtual ~StaticEx();
-
-  void set_margins(const RECT& rect);
-  void set_margins(int left, int top, int right, int bottom);
-  RECT margins() const { return margins_; }
-
-  void set_background_color(COLORREF back_color);
-  COLORREF background_color() const { return background_color_; }
-  void ResetBackgroundColor() { use_background_color_ = false; }
-
-  // set ellipsis style (DT_END_ELLIPSIS | DT_WORD_ELLIPSIS |DT_PATH_ELLIPSIS)
-  // elipsis are supported only in a single line control, calling this function
-  // with not 0 argument will set control style to SS_LEFTNOWORDWRAP
-  void set_ellipsis(int ellipsis);
-  int ellipsis() const { return ellipsis_; }
-
-  static const int kBorderNone;
-  static const int kBorderLeft;
-  static const int kBorderTop;
-  static const int kBorderRight;
-  static const int kBorderBottom;
-  static const int kBorderAll;
-
-  // use constants above to set border, you can combine them using "|"
-  void set_border(int border);
-  int border() const { return border_; }
-
-  void set_border_color(COLORREF border_color);
-  COLORREF border_color() const { return border_color_; }
-
-  // this function doesn't change how control is shown
-  // it just calculates minimum control height to fit the text given
-  // the control width. if width is 0 it will use current control width
-  int GetMinimumHeight(int width);
-
-  BEGIN_MSG_MAP(StaticEx)
-    MESSAGE_HANDLER(WM_SETTEXT, OnSetText)
-    MESSAGE_HANDLER(kGetTextMessage, OnGetText)
-    MESSAGE_HANDLER(kGetTextLengthMessage, OnGetTextLength)
-    MESSAGE_HANDLER(WM_PAINT, OnPaint)
-    MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
-    MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
-    MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
-  END_MSG_MAP()
-
-  LRESULT OnSetText(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);         // NOLINT
-  // OnGetText and OnGetTextLength work with full text including formatting tags
-  // to get readable text (without formatting info) call GetWindowText or
-  // send WM_GETTEXT
-  LRESULT OnGetText(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);         // NOLINT
-  LRESULT OnGetTextLength(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);   // NOLINT
-
-  LRESULT OnPaint(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);           // NOLINT
-  LRESULT OnEraseBkgnd(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);      // NOLINT
-  LRESULT OnLButtonDown(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);     // NOLINT
-  LRESULT OnSetCursor(UINT msg, WPARAM wparam, LPARAM lparam, BOOL& handled);       // NOLINT
-
-  BOOL SubclassWindow(HWND hWnd);
-  HWND UnsubclassWindow(BOOL bForce = FALSE);
-
- private:
-  void Reset();
-  void ParseText();
-  CString GetReadableText();
-  int FindOpenBracket(const TCHAR* string);
-  void EraseNodes();
-  void EraseLines(std::vector<StaticLine*>* lines);
-  HFONT default_font() const { return default_font_; }
-
-  void PrePaint(HDC dc, std::vector<StaticLine*>* lines,
-                const std::vector<Node*>& nodes, RECT rect, DWORD style,
-                int ellipsis);
-  void Paint(HDC hdc, const std::vector<StaticLine*>& lines, RECT rect,
-             DWORD style, int ellipsis);
-  void DrawBorder(HDC hdc, const CRect& rect);
-  HCURSOR GetHandCursor();
-
-  CString               text_;
-
-  CRect                 margins_;
-  COLORREF              background_color_;
-  bool                  use_background_color_;
-  int                   ellipsis_;
-  int                   border_;
-  COLORREF              border_color_;
-
-  std::vector<Node*>         nodes_;
-  std::vector<StaticLine*>   lines_;
-
-  HFONT                 default_font_;
-
-  static HCURSOR hand_cursor_;
-
-  static const UINT kGetTextMessage       = WM_APP + 1;
-  static const UINT kGetTextLengthMessage = WM_APP + 2;
-
-  DISALLOW_EVIL_CONSTRUCTORS(StaticEx);
-};
-
-#endif  // OMAHA_WORKER_UILIB_STATIC_EX_H_
diff --git a/worker/uilib/static_line.cc b/worker/uilib/static_line.cc
deleted file mode 100644
index 35f812d..0000000
--- a/worker/uilib/static_line.cc
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-
-#include "omaha/worker/uilib/static_line.h"
-#include "omaha/worker/uilib/node.h"
-#include "omaha/worker/uilib/static_ex.h"
-
-
-StaticLine::StaticLine()
-    : base_line_(0),
-      height_(0),
-      elipses_(false) {
-}
-
-
-StaticLine::~StaticLine() {
-}
-
-
-int StaticLine::AdjustHeight(int height) {
-  height_ = std::max(height_, height);
-  return height_;
-}
-
-
-int StaticLine::AdjustBaseLine(int base_line) {
-  base_line_ = std::max(base_line_, base_line);
-  return base_line_;
-}
-
-
-void StaticLine::AddNode(Node* node, int start, int length, int height,
-                         int base_line, int width) {
-  nodes_.push_back(Nodes(node, start, length, height, base_line, width));
-  AdjustHeight(height);
-  AdjustBaseLine(base_line);
-}
-
-
-int StaticLine::Paint(HDC hdc, int left, int right, int y, DWORD window_style,
-                      int ellipsis) {
-  int old_bk_mode = SetBkMode(hdc, TRANSPARENT);
-  bool single_line = (window_style & SS_LEFTNOWORDWRAP) != 0;
-
-  size_t size = nodes_.size();
-  for (size_t i = 0; i < size; i++) {
-    Node* node    = nodes_[i].node;
-    int start     = nodes_[i].start;
-    int length    = nodes_[i].length;
-    int base_line = nodes_[i].base_line;
-    int width     = nodes_[i].width;
-
-    CString text(static_cast<LPCTSTR>(node->node_text()) + start, length);
-    if (elipses_ && (i == (size - 1)))
-      text += "...";
-
-    CRect rect(left, y + base_line_ - base_line, left + width, y + height_);
-    if (single_line)
-      rect.right = right;
-
-    nodes_[i].rect = rect;
-
-    const NodeState& nodeState = node->node_state();
-    HFONT font = nodeState.GetFont();
-    if (!font)
-      return height_;
-
-    HFONT old_font = static_cast<HFONT>(SelectObject(hdc, font));
-    COLORREF old_text_color = SetTextColor(hdc, nodeState.text_color());
-
-    DWORD draw_style = 0;
-    draw_style = DT_LEFT | DT_NOCLIP | DT_NOPREFIX | DT_SINGLELINE;
-    if (ellipsis && (i == (size - 1)))
-      draw_style = draw_style | ellipsis;
-
-    DrawText(hdc, text, text.GetLength(), rect, draw_style);
-    left += width;
-
-    SetTextColor(hdc, old_text_color);
-    if (old_font)
-      SelectObject(hdc, old_font);
-  }
-
-  SetBkMode(hdc, old_bk_mode);
-  return height_;
-}
-
-
-int StaticLine::HitTest(CPoint point) {
-  size_t size = nodes_.size();
-  for (size_t i = 0; i < size; i++) {
-    if (nodes_[i].node->node_state().IsURL()) {
-      if (nodes_[i].rect.PtInRect(point)) {
-        return static_cast<int>(i);
-      }
-    }
-  }
-
-  return -1;
-}
-
-
-bool StaticLine::IsUrlUnderMouse(CPoint point, CString* action) {
-  int index = HitTest(point);
-  if (index >= 0 && action) {
-    *action = nodes_[index].node->node_state().url();
-  }
-  return (index >= 0);
-}
diff --git a/worker/uilib/static_line.h b/worker/uilib/static_line.h
deleted file mode 100644
index df7b355..0000000
--- a/worker/uilib/static_line.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2006-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-// static_line.h
-
-#ifndef OMAHA_WORKER_UILIB_STATIC_LINE_H_
-#define OMAHA_WORKER_UILIB_STATIC_LINE_H_
-
-#include <atlstr.h>
-#include <atltypes.h>
-#include <vector>
-#include "base/basictypes.h"
-
-class Node;
-
-class StaticLine {
- public:
-  StaticLine();
-  virtual ~StaticLine();
-
-  int AdjustBaseLine(int base_line);
-  int AdjustHeight(int height);
-
-  int base_line() const { return base_line_; }
-  int height() const { return height_; }
-
-  void AddNode(Node* node, int start, int end, int height, int base_line,
-               int width);
-  void AddEllipses() { elipses_ = true; }
-
-  bool IsUrlUnderMouse(CPoint point, CString* action);
-
-  int  Paint(HDC hdc, int left, int right, int y, DWORD window_style,
-             int ellipsis);
-
- protected:
-  int     HitTest(CPoint point);
-
-  int   base_line_;
-  int   height_;
-
-  struct Nodes {
-    Node*   node;
-    int     start;     // first char to output
-    int     length;    // number of chars
-    int     height;
-    int     base_line;
-    int     width;
-    CRect   rect;
-
-    Nodes(Node* node, int start, int length, int height, int base_line,
-          int width)
-        : node(node), start(start), length(length), height(height),
-          base_line(base_line), width(width), rect(0, 0, 0, 0) {}
-  };
-
-  std::vector<Nodes>   nodes_;
-  bool            elipses_;
-
-  DISALLOW_EVIL_CONSTRUCTORS(StaticLine);
-};
-
-#endif  // OMAHA_WORKER_UILIB_STATIC_LINE_H_
diff --git a/worker/worker-internal.h b/worker/worker-internal.h
deleted file mode 100644
index 36a6746..0000000
--- a/worker/worker-internal.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_WORKER_WORKER_INTERNAL_H_
-#define OMAHA_WORKER_WORKER_INTERNAL_H_
-
-namespace omaha {
-
-namespace internal {
-
-void RecordUpdateAvailableUsageStats(bool is_machine);
-
-// Sends a ping with self-update failure information if present in the registry.
-void SendSelfUpdateFailurePing(bool is_machine);
-
-// Sends an uninstall ping for any uninstalled products before Google Update
-// uninstalls itself.
-HRESULT SendFinalUninstallPingForApps(bool is_machine);
-
-}  // namespace internal
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_WORKER_INTERNAL_H_
-
diff --git a/worker/worker.cc b/worker/worker.cc
deleted file mode 100644
index e85f1a2..0000000
--- a/worker/worker.cc
+++ /dev/null
@@ -1,890 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// TODO(omaha): maybe introduce a new logging facility: WORK_LOG.
-
-// TODO(omaha): Dig out the RefHolder in scope_guard.h so we can use const
-// references instead pointers. This TODO was added for some code that no longer
-// exists, but it is still a good idea.
-
-#include "omaha/worker/worker.h"
-
-#include <atlbase.h>
-#include <atlstr.h>
-#include <atlapp.h>
-#include <atlsecurity.h>
-#include "omaha/common/app_util.h"
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/firewall_product_detection.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/module_utils.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/reactor.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/common/shutdown_handler.h"
-#include "omaha/common/sta_call.h"
-#include "omaha/common/system.h"
-#include "omaha/common/string.h"
-#include "omaha/common/thread_pool.h"
-#include "omaha/common/time.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vistautil.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/resource.h"
-#include "omaha/goopdate/stats_uploader.h"
-#include "omaha/goopdate/ui_displayed_event.h"
-#include "omaha/net/network_config.h"
-#include "omaha/net/network_request.h"
-#include "omaha/setup/setup.h"
-#include "omaha/worker/application_manager.h"
-#include "omaha/worker/ping.h"
-#include "omaha/worker/ping_utils.h"
-#include "omaha/worker/worker_event_logger.h"
-#include "omaha/worker/worker_job.h"
-#include "omaha/worker/worker_metrics.h"
-#include "omaha/worker/ui.h"
-
-// Since the net code is linked in as a lib, force the registration code to
-// be a dependency, otherwise the linker is optimizing in out.
-//
-// TODO(omaha): fix this clunkiness and require explicit registration of the
-// http creators with the factory. Not ideal but better then linker options.
-//
-// Design Notes:
-// Following are the mutexes that are taken by the worker
-// 1. SingleUpdateWorker. Only taken by the update worker.
-// 2. SingleInstallWorker. This is application specific. Only taken by the
-//    install worker and for the specific application.
-// 3. Before install, the install manager takes the global install lock.
-// 4. A key thing to add to this code is after taking the install lock,
-//    to validate that the version of the applicaion that is present in the
-//    registry is the same as that we queried for. The reason to do this
-//    is to ensure that there are no races between update and install workers.
-// 5. Termination of the worker happens because of four reasons:
-//    a. Shutdown event - Only applicable to the update worker. When this event
-//       is signalled, the main thread comes out of the wait. It then tries to
-//       destroy the contained thread pool, which causes a timed wait for the
-//       worker thread. The worker thread is notified by setting a
-//       cancelled flag on the worker.
-//    b. Install completes, user closes UI - Only applicable for the
-//       interactive installs. In this case the main thread comes out of
-//       the message loop and deletes the thread pool. The delete happens
-//       immediately, since the worker is doing nothing.
-//    c. User cancels install - Only applicable in case if interactive installs.
-//       The main thread sets the cancelled flag on the workerjob and comes out
-//       of the message loop. It then tries to delete the thread pool, causing
-//       a timed wait. The worker job queries the cancelled flag periodically
-//       and quits as soon as possible.
-//    d. The update worker completes - In this case we do not run on a thread
-//       pool.
-
-#pragma comment(linker, "/INCLUDE:_kRegisterWinHttp")
-
-namespace omaha {
-
-namespace {
-
-uint64 GetGuidMostSignificantUint64(const GUID& guid) {
-  return (static_cast<uint64>(guid.Data1) << 32) +
-         (static_cast<uint64>(guid.Data2) << 16) +
-         static_cast<uint64>(guid.Data3);
-}
-
-class WorkItem : public UserWorkItem {
- public:
-  WorkItem(WorkerJob* worker_job, bool delete_after_run)
-      : worker_job_(worker_job),
-        delete_after_run_(delete_after_run) {
-    ASSERT1(worker_job);
-  }
- private:
-  void DoProcess() {
-    worker_job_->DoProcess();
-    if (delete_after_run_) {
-      delete worker_job_;
-      worker_job_ = NULL;
-    }
-  }
-
-  WorkerJob* worker_job_;
-  bool delete_after_run_;
-  DISALLOW_EVIL_CONSTRUCTORS(WorkItem);
-};
-
-class ErrorWndEvents : public ProgressWndEvents {
- public:
-  virtual void DoPause() {}
-  virtual void DoResume() {}
-  virtual void DoClose() {}
-  virtual void DoRestartBrowsers() {}
-  virtual void DoReboot() {}
-  virtual void DoLaunchBrowser(const CString& url) {
-    VERIFY1(SUCCEEDED(goopdate_utils::LaunchBrowser(BROWSER_DEFAULT, url)));
-  }
-};
-
-// Users with UAC disabled may need to be updated to a new build in 2010. Set
-// the ap so we can make sure they get updated. See http://b/2194722.
-// Do not replace any existing APs.
-// TODO(omaha): Remove this code after June 2010.
-void Set2010UpdateAp(bool is_machine) {
-  const TCHAR* const k2010UpdateAp = _T("2010update");
-  // Only shell 1.2.131.7 is affected.
-  const ULONGLONG kAffectedShellVersion = 0x0001000200830007;
-
-  if (!vista_util::IsVistaOrLater()) {
-    return;
-  }
-
-  if (!vista_util::IsUACDisabled()) {
-    return;
-  }
-
-  const CString key =
-      ConfigManager::Instance()->registry_client_state_goopdate(is_machine);
-
-  if (RegKey::HasValue(key, kRegValueAdditionalParams)) {
-    return;
-  }
-
-  CString shell_path;
-  if (FAILED(GetModuleFileName(NULL, &shell_path))) {
-    return;
-  }
-
-  if (app_util::GetVersionFromFile(shell_path) != kAffectedShellVersion) {
-    return;
-  }
-
-  VERIFY1(SUCCEEDED(
-      RegKey::SetValue(key, kRegValueAdditionalParams, k2010UpdateAp)));
-}
-
-}  // namespace
-
-namespace internal {
-
-void RecordUpdateAvailableUsageStats(bool is_machine) {
-  AppManager app_manager(is_machine);
-
-  DWORD update_responses(0);
-  DWORD64 time_since_first_response_ms(0);
-  app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                       kGoopdateGuid,
-                                       &update_responses,
-                                       &time_since_first_response_ms);
-  if (update_responses) {
-    metric_worker_self_update_responses = update_responses;
-  }
-  if (time_since_first_response_ms) {
-    metric_worker_self_update_response_time_since_first_ms =
-        time_since_first_response_ms;
-  }
-
-  ProductDataVector products;
-  HRESULT hr = app_manager.GetRegisteredProducts(&products);
-  if (FAILED(hr)) {
-    ASSERT1(false);
-    return;
-  }
-
-  // These store information about the app with the most update responses.
-  GUID max_responses_product(GUID_NULL);
-  DWORD max_responses(0);
-  DWORD64 max_responses_time_since_first_response_ms(0);
-
-  for (size_t i = 0; i < products.size(); ++i) {
-    const ProductData& product_data = products[i];
-    const GUID& product_guid = product_data.app_data().app_guid();
-
-    if (::IsEqualGUID(kGoopdateGuid, product_guid)) {
-      continue;
-    }
-
-    DWORD update_responses(0);
-    DWORD64 time_since_first_response_ms(0);
-    app_manager.ReadUpdateAvailableStats(GUID_NULL,
-                                         product_guid,
-                                         &update_responses,
-                                         &time_since_first_response_ms);
-
-    if (max_responses < update_responses) {
-      max_responses_product = product_guid;
-      max_responses = update_responses;
-      max_responses_time_since_first_response_ms = time_since_first_response_ms;
-    }
-  }
-
-  if (max_responses) {
-    metric_worker_app_max_update_responses_app_high =
-        GetGuidMostSignificantUint64(max_responses_product);
-    metric_worker_app_max_update_responses = max_responses;
-    metric_worker_app_max_update_responses_ms_since_first =
-        max_responses_time_since_first_response_ms;
-  }
-}
-
-void SendSelfUpdateFailurePing(bool is_machine) {
-  DWORD self_update_error_code(0);
-  DWORD self_update_extra_code1(0);
-  CString self_update_version;
-
-  if (!Setup::ReadAndClearUpdateErrorInfo(is_machine,
-                                          &self_update_error_code,
-                                          &self_update_extra_code1,
-                                          &self_update_version)) {
-    return;
-  }
-
-  Ping ping;
-  HRESULT hr = ping_utils::SendGoopdatePing(
-      is_machine,
-      CommandLineExtraArgs(),
-      _T(""),
-      PingEvent::EVENT_SETUP_UPDATE_FAILURE,
-      self_update_error_code,
-      self_update_extra_code1,
-      self_update_version,
-      &ping);
-  if (FAILED(hr)) {
-    CORE_LOG(LW, (_T("[SendGoopdatePing failed][0x%08x]"), hr));
-    // TODO(omaha): Consider writing the values back with
-    // Setup::PersistUpdateErrorInfo(so we can try pinging later. This would be
-    // useful if the user happens to be offline when update worker runs.
-  }
-}
-
-HRESULT SendFinalUninstallPingForApps(bool is_machine) {
-  CORE_LOG(L2, (_T("[SendFinalUninstallPingForApps]")));
-  scoped_ptr<Request> uninstall_ping;
-  HRESULT hr = BuildUninstallPing(is_machine, address(uninstall_ping));
-  if (SUCCEEDED(hr) && uninstall_ping->get_request_count()) {
-    Ping ping;
-    hr = ping.SendPing(uninstall_ping.get());
-  }
-  return hr;
-}
-
-}  // namespace internal
-
-Worker::Worker(bool is_machine)
-    : is_machine_(is_machine),
-      is_local_system_(false),
-      has_uninstalled_(false) {
-  CORE_LOG(L1, (_T("[Worker::Worker]")));
-}
-
-Worker::~Worker() {
-  CORE_LOG(L1, (_T("[Worker::~Worker]")));
-
-  // Ensure the threads are cleaned up regardless of the entry point or flow.
-  StopWorker(NULL);
-
-  CollectAmbientUsageStats();
-}
-
-// This method could be called multiple times, and thus needs to be idempotent.
-// worker_job_error_code can be NULL.
-void Worker::StopWorker(HRESULT* worker_job_error_code) {
-  // Stop the concurrent objects to avoid spurious events.
-  shutdown_handler_.reset();
-  reactor_.reset();
-
-  if (worker_job_.get()) {
-    worker_job_->Cancel();
-  }
-
-  // The thread pool destructor waits for any remaining jobs to complete.
-  thread_pool_.reset();
-
-  if (worker_job_.get() && worker_job_error_code) {
-    *worker_job_error_code = worker_job_->error_code();
-  }
-
-  // Uninstall should happen as late as possible. Since uninstall may occur
-  // after this method, destroy all objects now.
-  worker_job_.reset();
-}
-
-// This method must not depend on Omaha being registered because that may not
-// occur until DoRun.
-// Assumes is_machine_ is set correctly.
-HRESULT Worker::Main(Goopdate* goopdate) {
-  ASSERT1(goopdate);
-  args_ = goopdate->args();
-  cmd_line_ = goopdate->cmd_line();
-  is_local_system_ = goopdate->is_local_system();
-
-  HRESULT hr = DoRun();
-  // If this is an interactive instance, UI should have been shown either by
-  // WorkerJob or an error.
-  ASSERT1(UIDisplayedEventManager::HasUIBeenDisplayed(is_machine_) ||
-          (args_.mode != COMMANDLINE_MODE_IG &&
-           args_.mode != COMMANDLINE_MODE_HANDOFF_INSTALL) ||
-          args_.is_silent_set);
-
-  // Stop WorkerJob and thread pool so we can get the final error code.
-  HRESULT worker_job_error_code(S_OK);
-  StopWorker(&worker_job_error_code);
-
-  // Get the error code from WorkerJob thread to return to the caller.
-  if (SUCCEEDED(hr)) {
-    hr = worker_job_error_code;
-  }
-
-  if (FAILED(hr) && args_.mode == COMMANDLINE_MODE_IG) {
-    MaybeUninstallGoogleUpdate();
-  }
-
-  return hr;
-}
-
-// This method must not depend on any Omaha "registration" because Google Update
-// may not be completely installed yet.
-HRESULT Worker::DoRun() {
-  OPT_LOG(L1, (_T("[Worker::DoRun]")));
-
-  HRESULT hr = InitializeThreadPool();
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // The /install case does not really use the Worker and needs to be moved
-  // outside. This will be done as part of unifying Setup.
-  if (COMMANDLINE_MODE_INSTALL == args_.mode) {
-    return DoInstallGoogleUpdateAndApp();
-  }
-
-  if ((COMMANDLINE_MODE_IG == args_.mode ||
-       COMMANDLINE_MODE_HANDOFF_INSTALL == args_.mode) &&
-      !args_.is_silent_set) {
-    HRESULT hr = InitializeUI();
-    if (FAILED(hr)) {
-      CString error_text;
-      error_text.FormatMessage(IDS_INSTALL_FAILED, hr);
-      DisplayErrorInMessageBox(error_text);
-      return hr;
-    }
-
-    if (!args_.is_offline_set) {
-      CString caption;
-      caption.FormatMessage(IDS_WINDOW_TITLE, GetPrimaryJobInfo().app_name);
-      CString message;
-      message.LoadString(IDS_PROXY_PROMPT_MESSAGE);
-      const uint32 kProxyMaxPrompts = 1;
-      NetworkConfig::Instance().ConfigureProxyAuth(caption,
-                                                   message,
-                                                   *progress_wnd_,
-                                                   kProxyMaxPrompts);
-    }
-  }
-
-  worker_job_.reset(WorkerJobFactory::CreateWorkerJob(is_machine_,
-                                                      args_,
-                                                      job_observer_.get()));
-
-  // TODO(omaha): It would be nice if we could move all the differences
-  // between installs and updates into the WorkerJobStrategy and just call
-  // StartWorkerJob at this point.
-#pragma warning(push)
-// C4061: enumerator 'xxx' in switch of enum 'yyy' is not explicitly handled by
-// a case label.
-#pragma warning(disable : 4061)
-  switch (args_.mode) {
-    case COMMANDLINE_MODE_IG:
-    case COMMANDLINE_MODE_HANDOFF_INSTALL:
-      hr = DoInstall();
-      break;
-    case COMMANDLINE_MODE_UA:
-      hr = DoUpdateApps();
-      OPT_LOG(L2, (_T("[Update worker finished]")));
-      break;
-    default:
-      ASSERT(false, (_T("Nothing to do in the worker.")));
-      hr = E_UNEXPECTED;
-  }
-#pragma warning(pop)
-
-  return hr;
-}
-
-bool Worker::EnsureSingleAppInstaller(const CString& guid) {
-  // We allow only one instance of interactive install per application.
-  CString mutex_name;
-  mutex_name.Format(kSingleInstallWorker, guid);
-  single_install_worker_.reset(new ProgramInstance(mutex_name));
-  return !single_install_worker_->EnsureSingleInstance();
-}
-
-bool Worker::EnsureSingleUpdateWorker() {
-  // We allow only one instance of the update worker per user.
-  NamedObjectAttributes single_update_worker_attr;
-  GetNamedObjectAttributes(kSingleupdateWorker,
-                           is_machine_,
-                           &single_update_worker_attr);
-
-  single_update_worker_.reset(new ProgramInstance(
-      single_update_worker_attr.name));
-  return !single_update_worker_->EnsureSingleInstance();
-}
-
-HRESULT Worker::InitializeUI() {
-  OPT_LOG(L1, (_T("[InitializeUI]")));
-  ASSERT1((COMMANDLINE_MODE_IG == args_.mode ||
-           COMMANDLINE_MODE_HANDOFF_INSTALL == args_.mode ||
-           COMMANDLINE_MODE_INSTALL == args_.mode) &&
-          !args_.is_silent_set);
-
-  progress_wnd_.reset(new ProgressWnd(&message_loop_, NULL));
-
-  progress_wnd_->set_is_machine(is_machine_);
-  progress_wnd_->set_language(args_.extra.language);
-  ASSERT1(!args_.extra.apps.empty());
-  progress_wnd_->set_product_name(GetPrimaryJobInfo().app_name);
-  progress_wnd_->set_product_guid(GetPrimaryJobInfo().app_guid);
-  progress_wnd_->set_iid(args_.extra.installation_id);
-  progress_wnd_->set_brand_code(args_.extra.brand_code);
-
-  JobObserverCallMethodDecorator* decorator =
-      new JobObserverCallMethodDecorator(progress_wnd_.get());
-  job_observer_.reset(decorator);
-  HRESULT hr = decorator->Initialize();
-  if (FAILED(hr)) {
-    OPT_LOG(L1, (_T("JobObserver initialize failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT Worker::DoInstall() {
-  OPT_LOG(L1, (_T("[DoInstall]")));
-
-  const bool is_already_installing =
-      EnsureSingleAppInstaller(GuidToString(GetPrimaryJobInfo().app_guid));
-
-  if (is_already_installing) {
-    OPT_LOG(L1, (_T("[Another Install of this application is running in the ")
-                 _T("same session. Exiting.]")));
-    ++metric_worker_another_install_in_progress;
-    HRESULT hr = GOOPDATE_E_APP_BEING_INSTALLED;
-    CString error_text;
-    error_text.FormatMessage(IDS_APPLICATION_ALREADY_INSTALLING,
-                             GetPrimaryJobInfo().app_name);
-    DisplayError(error_text, hr);
-    return hr;
-  }
-
-  HRESULT hr = StartWorkerJob();
-  if (FAILED(hr)) {
-    CString error_text;
-    error_text.FormatMessage(IDS_INSTALL_FAILED, hr);
-
-    DisplayError(error_text, hr);
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT Worker::InitializeShutDownHandler(ShutdownCallback* callback) {
-  CORE_LOG(L3, (_T("[InitializeShutDownHandler]")));
-  ASSERT1(callback);
-
-  reactor_.reset(new Reactor);
-  shutdown_handler_.reset(new ShutdownHandler);
-  return shutdown_handler_->Initialize(reactor_.get(),
-                                       callback,
-                                       is_machine_);
-}
-
-HRESULT Worker::DoUpdateApps() {
-  OPT_LOG(L1, (_T("[DoUpdateApps]")));
-
-  WriteUpdateAppsWorkerStartEvent(is_machine_);
-
-  if (EnsureSingleUpdateWorker()) {
-    OPT_LOG(L1, (_T("[Another worker is already running. Exiting.]")));
-    ++metric_worker_another_update_in_progress;
-    return GOOPDATE_E_WORKER_ALREADY_RUNNING;
-  }
-
-  HRESULT hr = InitializeShutDownHandler(this);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[InitializeShutDownHandler failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  internal::RecordUpdateAvailableUsageStats(is_machine_);
-
-  // A tentative uninstall check is done here. There are stronger checks,
-  // protected by locks, which are done by Setup.
-  size_t num_clients(0);
-  const bool is_uninstall =
-      FAILED(goopdate_utils::GetNumClients(is_machine_, &num_clients)) ||
-      num_clients <= 1;
-  CORE_LOG(L4, (_T("[Worker::DoUpdateApps][%u]"), num_clients));
-
-  if (is_uninstall) {
-    // Attempt a conditional uninstall and always return S_OK to avoid
-    // executing error handling code in the case of an actual uninstall.
-    // Do not attempt to uninstall if MSI is busy to avoid spurious uninstalls.
-    // See http://b/1436223. The call to WaitForMSIExecute blocks with a
-    // timeout. It is better to block here than block while holding the setup
-    // lock.
-    hr = WaitForMSIExecute(kWaitForMSIExecuteMs);
-    CORE_LOG(L2, (_T("[WaitForMSIExecute returned 0x%08x]"), hr));
-    if (SUCCEEDED(hr)) {
-      // Destroy all the objects before uninstalling.
-      StopWorker(NULL);
-      MaybeUninstallGoogleUpdate();
-    }
-    return S_OK;
-  }
-
-  hr = StartWorkerJob();
-
-  internal::SendSelfUpdateFailurePing(is_machine_);
-
-  return hr;
-}
-
-HRESULT Worker::DoInstallGoogleUpdateAndApp() {
-  OPT_LOG(L1, (_T("[DoInstallGoogleUpdateAndApp]")));
-
-  // For machine installs, do not use the UI displayed event in the non-elevated
-  // instance.
-  if (!is_machine_ || vista_util::IsUserAdmin()) {
-    VERIFY1(SUCCEEDED(UIDisplayedEventManager::CreateEvent(is_machine_)));
-  }
-
-  Setup setup(is_machine_, &args_);
-  HRESULT hr = setup.Install(cmd_line_);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[Setup::Install failed][0x%08x]"), hr));
-    VERIFY1(SUCCEEDED(HandleSetupError(hr, setup.extra_code1())));
-    return hr;
-  }
-
-  return S_OK;
-}
-
-HRESULT Worker::InitializeThreadPool() {
-  CORE_LOG(L3, (_T("[Worker::InitializeThreadPool]")));
-  thread_pool_.reset(new ThreadPool);
-  return thread_pool_->Initialize(kThreadPoolShutdownDelayMs);
-}
-
-HRESULT Worker::StartWorkerJob() {
-  CORE_LOG(L2, (_T("[Worker::StartWorker]")));
-  if (args_.is_silent_set || COMMANDLINE_MODE_UA == args_.mode) {
-    return worker_job_->DoProcess();
-  }
-
-  HRESULT hr = QueueWorkerJob(worker_job_.get(), false);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  message_loop_.Run();
-  return S_OK;
-}
-
-HRESULT Worker::QueueWorkerJob(WorkerJob* worker_job, bool delete_after_run) {
-  CORE_LOG(L2, (_T("[Worker::QueueWorkerJob]")));
-  ASSERT1(thread_pool_.get());
-
-  scoped_ptr<WorkItem> work_item;
-  work_item.reset(new WorkItem(worker_job, delete_after_run));
-  HRESULT hr = thread_pool_->QueueUserWorkItem(work_item.get(),
-                                               WT_EXECUTELONGFUNCTION);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  work_item.release();
-  return S_OK;
-}
-
-// Displays an error in the Google Update UI if not silent then sends a ping if
-// allowed If the UI fails, uses a system message box as a fallback.
-HRESULT Worker::HandleSetupError(HRESULT error, int extra_code1) {
-  ASSERT1(FAILED(error));
-
-  CString error_text;
-  ASSERT1(!args_.extra.apps.empty());
-  switch (error) {
-    case GOOPDATE_E_INSTALL_ELEVATED_PROCESS_NEEDS_ELEVATION:
-    case GOOPDATE_E_NONADMIN_INSTALL_ADMIN_APP:
-      error_text.FormatMessage(IDS_NEED_ADMIN_TO_INSTALL,
-                               GetPrimaryJobInfo().app_name);
-      break;
-    case GOOPDATE_E_ELEVATION_FAILED_ADMIN:
-    case GOOPDATE_E_ELEVATION_FAILED_NON_ADMIN:
-      error_text.FormatMessage(IDS_ELEVATION_FAILED,
-                               GetPrimaryJobInfo().app_name);
-      break;
-    case GOOPDATE_E_FAILED_TO_GET_LOCK:
-    case GOOPDATE_E_FAILED_TO_GET_LOCK_MATCHING_INSTALL_PROCESS_RUNNING:
-    case GOOPDATE_E_FAILED_TO_GET_LOCK_NONMATCHING_INSTALL_PROCESS_RUNNING:
-    case GOOPDATE_E_FAILED_TO_GET_LOCK_UPDATE_PROCESS_RUNNING:
-      error_text.FormatMessage(IDS_APPLICATION_INSTALLING_GOOGLE_UPDATE,
-                               GetPrimaryJobInfo().app_name);
-      break;
-    case GOOPDATE_E_INSTANCES_RUNNING:
-      error_text.FormatMessage(IDS_INSTANCES_RUNNING_AFTER_SHUTDOWN,
-                               GetPrimaryJobInfo().app_name);
-      break;
-    case GOOPDATE_E_RUNNING_INFERIOR_MSXML:
-      error_text.FormatMessage(IDS_WINDOWS_IS_NOT_UP_TO_DATE,
-                               GetPrimaryJobInfo().app_name);
-      break;
-    case GOOPDATE_E_HANDOFF_FAILED:
-      error_text.FormatMessage(IDS_HANDOFF_FAILED,
-                               GetPrimaryJobInfo().app_name);
-      break;
-    default:
-      error_text.FormatMessage(IDS_SETUP_FAILED, error);
-      break;
-  }
-
-  OPT_LOG(LE, (_T("[Failed to install Google Update][0x%08x][%s]"),
-               error, error_text));
-
-  if (UIDisplayedEventManager::HasUIBeenDisplayed(is_machine_)) {
-    // Do not display another UI, launch the web page, or ping.
-    CORE_LOG(L2, (_T("[second instance has UI; not displaying error UI]")));
-    return S_OK;
-  }
-
-  HRESULT hr = S_OK;
-  if (!args_.is_silent_set) {
-    hr = InitializeUI();
-    if (SUCCEEDED(hr)) {
-      DisplayError(error_text, error);
-    } else {
-      DisplayErrorInMessageBox(error_text);
-    }
-  }
-
-  // Do not ping. Cannot rely on the ping code to not send the ping because
-  // Setup may have failed before it could write eulaccepted=0 to the registry.
-  if (args().is_eula_required_set) {
-    return hr;
-  }
-
-  if (args().is_oem_set) {
-    // Do not ping.
-    // Cannot rely on the ping code to not send the ping because the OEM Mode
-    // value is not set after Setup returns.
-    return hr;
-  }
-
-  // This ping may cause a firewall prompt, but we are willing to cause a prompt
-  // in failure cases.
-  Request request(is_machine_);
-  Ping ping;
-  HRESULT hr_ping = ping_utils::SendGoopdatePing(
-      is_machine_,
-      args().extra,
-      args().install_source,
-      PingEvent::EVENT_SETUP_INSTALL_FAILURE,
-      error,
-      extra_code1,
-      NULL,
-      &ping);
-  if (FAILED(hr_ping)) {
-    CORE_LOG(LW, (_T("[SendGoopdatePing failed][0x%08x]"), hr_ping));
-  }
-
-  return hr;
-}
-
-HRESULT Worker::Shutdown() {
-  CORE_LOG(L2, (_T("[Worker::Shutdown]")));
-  ASSERT1(args_.mode == COMMANDLINE_MODE_UA);
-  if (worker_job_.get()) {
-    worker_job_->Cancel();
-  }
-  return S_OK;
-}
-
-HRESULT Worker::DoOnDemand(const WCHAR* guid,
-                           const CString& lang,
-                           IJobObserver* observer,
-                           bool is_update_check_only) {
-  CORE_LOG(L3, (_T("[Worker::DoOnDemand][%d][%s][%d][%d]"),
-                is_machine_, guid, observer, is_update_check_only));
-  ASSERT1(guid);
-  ASSERT1(observer);
-
-  if (!is_update_check_only && is_machine_ && !vista_util::IsUserAdmin()) {
-    ASSERT(false, (_T("Need to be elevated for machine application.")));
-    return HRESULT_FROM_WIN32(ERROR_ELEVATION_REQUIRED);
-  }
-
-  // Create a fresh WorkerJob for each OnDemand request that comes in.
-  scoped_ptr<WorkerJob> worker_job;
-  HRESULT hr = WorkerJobFactory::CreateOnDemandWorkerJob(
-      is_machine_,
-      is_update_check_only,
-      lang,
-      StringToGuid(guid),
-      observer,
-      shutdown_callback_.get(),
-      address(worker_job));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = QueueWorkerJob(worker_job.get(), true);
-  if (FAILED(hr)) {
-    CORE_LOG(LW, (_T("[QueueWorkerJob failed][0x%08x]"), hr));
-    return hr;
-  }
-  worker_job.release();
-
-  return S_OK;
-}
-
-void Worker::CollectAmbientUsageStats() {
-  if (args_.mode != COMMANDLINE_MODE_UA) {
-    return;
-  }
-
-  // Check every time the /ua worker exits in case the user later disables UAC.
-  Set2010UpdateAp(is_machine_);
-
-  CString name, version;
-  HRESULT hr = firewall_detection::Detect(&name, &version);
-  bool has_software_firewall = SUCCEEDED(hr) && !name.IsEmpty();
-  metric_worker_has_software_firewall.Set(has_software_firewall);
-
-  if (System::IsRunningOnBatteries()) {
-    ++metric_worker_silent_update_running_on_batteries;
-  }
-
-  metric_worker_shell_version = app_util::GetVersionFromModule(NULL);
-
-  metric_worker_is_windows_installing.Set(IsWindowsInstalling());
-  metric_worker_is_uac_disabled.Set(vista_util::IsUACDisabled());
-  metric_worker_is_clickonce_disabled.Set(IsClickOnceDisabled());
-}
-
-// Uninstall is a tricky use case. Uninstall can primarily happen in three cases
-// and there are two mechanisms to uninstall. The cases in which Omaha
-// uninstalls are:
-// 1. The last registered application uninstalls. Omaha monitors the
-// client keys and it will trigger an immediate uninstall in this case.
-// 2. The core starts an update worker, if there are no registered
-// applications, the update worker will do the uninstall.
-// 3. An error, including user cancel, happens during Omaha or app installation
-// and there are no registered applications.
-// The uninstall is implemented in terms of the following mechanisms:
-// * An update worker launched with "/ua /uninstalled" by the core, in the
-// first two cases above.
-// * A direct uninstall, in the case of errors or user cancellations, in the
-// last case above.
-//
-// Omaha can uninstall only if there are no install workers running and no
-// registered applications. This check is done under the setup lock protection.
-// In addition, the uninstall worker takes the update worker lock. Acquiring
-// this lock is important since the silent installers can modify the
-// registration of apps and trigger uninstalls workers. Therefore, both
-// setup lock and the update worker locks are needed.
-//
-// In the direct uninstall case there is a small race condition, since there is
-// no other single lock that can be acquired to prevent changes to the
-// application registration. The code looks for install workers but the test is
-// racy if not protected by locks.
-void Worker::MaybeUninstallGoogleUpdate() {
-  CORE_LOG(L1, (_T("[Worker::MaybeUninstallGoogleUpdate]")));
-  ASSERT1(args_.mode == COMMANDLINE_MODE_UA ||
-          args_.mode == COMMANDLINE_MODE_IG);
-  internal::SendFinalUninstallPingForApps(is_machine_);
-
-  Setup setup(is_machine_, &args_);
-  has_uninstalled_ = !!SUCCEEDED(setup.Uninstall());
-}
-
-const CommandLineAppArgs& Worker::GetPrimaryJobInfo() const {
-  ASSERT1(!args_.extra.apps.empty());
-  return args_.extra.apps[0];
-}
-
-void Worker::DisplayError(const CString& error_text, HRESULT error) {
-  ASSERT1(!UIDisplayedEventManager::HasUIBeenDisplayed(is_machine_));
-
-#pragma warning(push)
-// C4061: enumerator 'xxx' in switch of enum 'yyy' is not explicitly handled by
-// a case label.
-#pragma warning(disable : 4061)
-  switch (args_.mode) {
-    case COMMANDLINE_MODE_INSTALL:
-    case COMMANDLINE_MODE_HANDOFF_INSTALL:
-    case COMMANDLINE_MODE_IG:
-      if (args_.is_silent_set) {
-        return;
-      }
-      break;
-    case COMMANDLINE_MODE_UA:
-    case COMMANDLINE_MODE_UNKNOWN:  // OnDemand
-      return;
-      break;
-    default:
-      ASSERT1(false);
-      return;
-  }
-#pragma warning(pop)
-
-  ASSERT1(job_observer_.get());
-  ErrorWndEvents error_wnd_events;
-
-  // error_wnd_events must not be destroyed until CMessageLoop::Run() returns.
-  job_observer_->SetEventSink(&error_wnd_events);
-  job_observer_->OnShow();
-
-  job_observer_->OnComplete(COMPLETION_CODE_ERROR, error_text, error);
-
-  message_loop_.Run();
-}
-
-void Worker::DisplayErrorInMessageBox(const CString& error_text) {
-  ASSERT1(args_.mode == COMMANDLINE_MODE_INSTALL ||
-          args_.mode == COMMANDLINE_MODE_IG ||
-          args_.mode == COMMANDLINE_MODE_HANDOFF_INSTALL);
-
-  if (args_.is_silent_set) {
-    return;
-  }
-
-  CString primary_app_name;
-  ASSERT1(!args_.extra.apps.empty());
-  if (!args_.extra.apps.empty()) {
-    primary_app_name = GetPrimaryJobInfo().app_name;
-  }
-
-  goopdate_utils::DisplayErrorInMessageBox(error_text, primary_app_name);
-}
-
-}  // namespace omaha
-
diff --git a/worker/worker.h b/worker/worker.h
deleted file mode 100644
index bf228bf..0000000
--- a/worker/worker.h
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_WORKER_WORKER_H__
-#define OMAHA_WORKER_WORKER_H__
-
-#include <atlbase.h>
-#include <atlstr.h>
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "omaha/common/atlregmapex.h"
-#include "omaha/common/const_cmd_line.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/common/shutdown_callback.h"
-#include "omaha/common/shutdown_handler.h"
-#include "omaha/common/thread_pool.h"
-#include "omaha/common/wtl_atlapp_wrapper.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/program_instance.h"
-#include "omaha/goopdate/resource_manager.h"
-#include "omaha/worker/com_wrapper_shutdown_handler.h"
-
-interface IJobObserver;
-
-namespace omaha {
-
-static const int kThreadPoolShutdownDelayMs = 60000;
-
-class Goopdate;
-class JobObserver;
-class ProgressWnd;
-class Reactor;
-class Setup;
-class UserInterface;
-class WorkerJobStrategy;
-class WorkerJob;
-class WorkerShutdownHandler;
-
-class Worker : public ShutdownCallback {
- public:
-  explicit Worker(bool is_machine);
-  virtual ~Worker();
-
-  HRESULT Main(Goopdate* goopdate);
-  HRESULT DoOnDemand(const WCHAR* guid,
-                     const CString& lang,
-                     IJobObserver* observer,
-                     bool is_update_check_only);
-
-  HRESULT Shutdown();
-  HRESULT InitializeThreadPool();
-  HRESULT InitializeShutDownHandler(ShutdownCallback* callback);
-
-  Reactor* reactor() const { return reactor_.get(); }
-  const CommandLineArgs& args() const { return args_; }
-  CString cmd_line() const { return cmd_line_; }
-  bool is_machine() const { return is_machine_; }
-  bool is_local_system() const { return is_local_system_; }
-  bool has_uninstalled() const { return has_uninstalled_; }
-  WorkerComWrapperShutdownCallBack* shutdown_callback() const {
-    return shutdown_callback_.get();
-  }
-  void set_shutdown_callback(WorkerComWrapperShutdownCallBack* callback) {
-    ASSERT1(callback);
-    shutdown_callback_.reset(callback);
-  }
-
- private:
-  HRESULT StartWorkerJob();
-  HRESULT QueueWorkerJob(WorkerJob* worker_job, bool delete_after_run);
-  // Stops and destroys the Worker and its members.
-  void StopWorker(HRESULT* worker_job_error_code);
-  HRESULT DoRun();
-  bool EnsureSingleAppInstaller(const CString& guid);
-  bool EnsureSingleUpdateWorker();
-  HRESULT InitializeUI();
-
-  HRESULT DoInstall();
-  HRESULT DoUpdateApps();
-  HRESULT DoInstallGoogleUpdateAndApp();
-
-  // Uninstalls GoogleUpdate conditionally.
-  void MaybeUninstallGoogleUpdate();
-
-  HRESULT HandleSetupError(HRESULT error, int extra_code1);
-
-  void CollectAmbientUsageStats();
-
-  const CommandLineAppArgs& GetPrimaryJobInfo() const;
-
-  // Displays an error using the normal UI. Does not display a message box when
-  // running silently.
-  void DisplayError(const CString& error_text, HRESULT error);
-
-
-  // Displays an error in a Windows Message Box. Useful when UI initialization
-  // fails. Does not display a message box when running silently.
-  virtual void DisplayErrorInMessageBox(const CString& error_text);
-
-
-  bool is_machine_;
-  bool is_local_system_;
-  bool has_uninstalled_;        // True if the worker has uninstalled Omaha.
-  CString cmd_line_;            // Command line, as provided by the OS.
-  scoped_ptr<ProgramInstance> single_update_worker_;
-  scoped_ptr<ProgramInstance> single_install_worker_;
-  // The ProgressWnd is owned by this class so that
-  // JobObserverCallMethodDecorator can release it without destroying it as
-  // expected in ProgressWnd and required for calling OnComplete in the same
-  // thread as the message loop when displaying an early error.
-  scoped_ptr<ProgressWnd> progress_wnd_;
-  scoped_ptr<JobObserver> job_observer_;
-  scoped_ptr<Reactor>     reactor_;
-  scoped_ptr<ShutdownHandler> shutdown_handler_;
-  CommandLineArgs args_;
-  scoped_ptr<WorkerJob> worker_job_;
-  scoped_ptr<WorkerComWrapperShutdownCallBack> shutdown_callback_;
-
-  // Message loop for non-COM modes.
-  CMessageLoop message_loop_;
-  scoped_ptr<ThreadPool>  thread_pool_;
-
-  friend class WorkerTest;
-
-  DISALLOW_EVIL_CONSTRUCTORS(Worker);
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_WORKER_H__
-
diff --git a/worker/worker_com_wrapper.cc b/worker/worker_com_wrapper.cc
deleted file mode 100644
index b4e1d00..0000000
--- a/worker/worker_com_wrapper.cc
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-//
-#include "omaha/worker/worker_com_wrapper.h"
-
-#include <atlbase.h>
-#include <atlstr.h>
-#include <atlsecurity.h>
-#include "omaha/common/error.h"
-#include "omaha/common/exception_barrier.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/goopdate/google_update.h"
-#include "omaha/worker/com_wrapper_shutdown_handler.h"
-#include "omaha/worker/worker.h"
-
-namespace omaha {
-
-HRESULT OnDemandCOMClass::FinalConstruct() {
-  CORE_LOG(L2, (_T("[OnDemandCOMClass::FinalConstruct]")));
-
-  GoogleUpdate* google_update = static_cast<GoogleUpdate*>(_pAtlModule);
-  worker_ = google_update->worker();
-  ASSERT1(worker_);
-
-  HRESULT hr = worker_->InitializeThreadPool();
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // For update checks on machine applications where the user is not an
-  // administrator, the COM server will shutdown automatically after it returns
-  // from a COM method invocation. Two reasons for this policy:
-  // * Only update checks on a machine app can be done as a non-admin user. To
-  //   call Update requires an elevated instance of the COM server. So this COM
-  //   server is useless after the UpdateCheck call.
-  // * Listening on the shutdown handler requires admin privileges.
-  bool shutdown_after_invocation = worker_->is_machine() && !::IsUserAnAdmin();
-  worker_->set_shutdown_callback(new WorkerComWrapperShutdownCallBack(
-                                         shutdown_after_invocation));
-
-  return shutdown_after_invocation ?
-             S_OK :
-             worker_->InitializeShutDownHandler(worker_->shutdown_callback());
-}
-
-void OnDemandCOMClass::FinalRelease() {
-  CORE_LOG(L2, (_T("[OnDemandCOMClass::FinalRelease]")));
-  worker_ = NULL;
-}
-
-void OnDemandCOMClass::AddRefIgnoreShutdownEvent() const {
-  CORE_LOG(L2, (_T("[OnDemandCOMClass::AddRefIgnoreShutdownEvent]")));
-  ASSERT1(worker_);
-  WorkerComWrapperShutdownCallBack* callback = worker_->shutdown_callback();
-  ASSERT1(callback);
-  callback->AddRefIgnoreShutdown();
-}
-
-void OnDemandCOMClass::ResetStateOnError() const {
-  CORE_LOG(L2, (_T("[OnDemandCOMClass::ResetStateOnError]")));
-  ASSERT1(worker_);
-  WorkerComWrapperShutdownCallBack* callback = worker_->shutdown_callback();
-  ASSERT1(callback);
-  callback->ReleaseIgnoreShutdown();
-}
-
-HRESULT OnDemandCOMClass::DoOnDemand(bool is_update_check_only,
-                                     const WCHAR* guid,
-                                     IJobObserver* observer) {
-  CORE_LOG(L2, (_T("[OnDemandCOMClass::DoOnDemand][%d][%s][%d]"),
-                is_update_check_only, guid, observer));
-  // The exception barrier is needed, because any exceptions that are thrown
-  // in this method will get caught by the COM run time. We compile with
-  // exceptions off, and do not expect to throw any exceptions. This barrier
-  // will treat an exception in this method as a unhandled exception.
-  ExceptionBarrier barrier;
-
-  ASSERT1(guid);
-  ASSERT1(observer);
-  if (!guid || StringToGuid(guid) == GUID_NULL || !observer) {
-    return E_INVALIDARG;
-  }
-
-  AddRefIgnoreShutdownEvent();
-  // Ensure that the reset method is called in all error cases.
-  ScopeGuard guard = MakeObjGuard(*this, &OnDemandCOMClass::ResetStateOnError);
-
-  HRESULT hr = worker_->DoOnDemand(guid,
-                                   CString(),
-                                   observer,
-                                   is_update_check_only);
-  if (FAILED(hr)) {
-    OPT_LOG(LE, (_T("[DoOnDemand failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  // Dismiss the scope guard, since the DoOnDemand succeeded and the worker job
-  // thread is now responsible for the cleanup.
-  guard.Dismiss();
-  return S_OK;
-}
-
-STDMETHODIMP OnDemandCOMClass::Update(const WCHAR* guid,
-                                      IJobObserver* observer) {
-  return DoOnDemand(false, guid, observer);
-}
-
-STDMETHODIMP OnDemandCOMClass::CheckForUpdate(const WCHAR* guid,
-                                              IJobObserver* observer) {
-  return DoOnDemand(true, guid, observer);
-}
-
-}  // namespace omaha
-
diff --git a/worker/worker_com_wrapper.h b/worker/worker_com_wrapper.h
deleted file mode 100644
index 7109661..0000000
--- a/worker/worker_com_wrapper.h
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_WORKER_WORKER_COM_WRAPPER_H__
-#define OMAHA_WORKER_WORKER_COM_WRAPPER_H__
-
-#include <atlbase.h>
-#include <atlcom.h>
-#include <atlstr.h>
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "goopdate/google_update_idl.h"
-#include "omaha/common/atlregmapex.h"
-#include "omaha/common/const_cmd_line.h"
-#include "omaha/common/preprocessor_fun.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/resource.h"
-#include "omaha/goopdate/resources/goopdate_dll/goopdate_dll.grh"
-
-namespace omaha {
-
-const TCHAR* const kOnDemandCOMClassUserProgId =
-    _T("GoogleUpdate.OnDemandCOMClassUser");
-const TCHAR* const kOnDemandCOMClassMachineProgId =
-    _T("GoogleUpdate.OnDemandCOMClassMachine");
-const TCHAR* const kOnDemandCOMClassDescription =
-    _T("GoogleUpdate.OnDemandCOMClass");
-
-class Worker;
-
-class OnDemandCOMClass
-    : public CComObjectRootEx<CComMultiThreadModel>,
-      public CComCoClass<OnDemandCOMClass>,
-      public IGoogleUpdate {
- public:
-  OnDemandCOMClass()
-      : worker_(NULL) {
-  }
-  virtual ~OnDemandCOMClass() {}
-
-  DECLARE_NOT_AGGREGATABLE(OnDemandCOMClass)
-  DECLARE_PROTECT_FINAL_CONSTRUCT()
-
-  DECLARE_REGISTRY_RESOURCEID_EX(IDR_GOOGLE_UPDATE_WORKER_CLASS)
-
-  #pragma warning(push)
-  // C4640: construction of local static object is not thread-safe
-  #pragma warning(disable : 4640)
-  BEGIN_REGISTRY_MAP()
-    REGMAP_ENTRY(_T("HKROOT"),       goopdate_utils::GetHKRoot())
-    REGMAP_EXE_MODULE(_T("MODULE"))
-    REGMAP_ENTRY(_T("VERSION"),      _T("1.0"))
-    REGMAP_ENTRY(_T("PROGID"),
-                 goopdate_utils::IsRunningFromOfficialGoopdateDir(true) ?
-                     kOnDemandCOMClassMachineProgId :
-                     kOnDemandCOMClassUserProgId)
-    REGMAP_ENTRY(_T("DESCRIPTION"),  kOnDemandCOMClassDescription)
-    REGMAP_UUID(_T("CLSID"),
-                goopdate_utils::IsRunningFromOfficialGoopdateDir(true) ?
-                    __uuidof(OnDemandMachineAppsClass) :
-                    __uuidof(OnDemandUserAppsClass))
-    REGMAP_UUID(_T("LIBID"),         LIBID_GoogleUpdateLib)
-    REGMAP_ENTRY(_T("STRINGRESID"),
-                 PP_STRINGIZE(IDS_ELEVATION_MONIKER_DISPLAYNAME))
-    REGMAP_ENTRY(_T("ICONRESID"), PP_STRINGIZE(IDI_ELEVATION_MONIKER_ICON))
-  END_REGISTRY_MAP()
-  #pragma warning(pop)
-
-  // C4505: unreferenced IUnknown local functions have been removed
-  #pragma warning(disable : 4505)
-  BEGIN_COM_MAP(OnDemandCOMClass)
-    COM_INTERFACE_ENTRY(IGoogleUpdate)
-  END_COM_MAP()
-
-  STDMETHOD(CheckForUpdate)(const WCHAR* guid, IJobObserver* observer);
-  STDMETHOD(Update)(const WCHAR* guid, IJobObserver* observer);
-  HRESULT FinalConstruct();
-  void FinalRelease();
-
- private:
-  void AddRefIgnoreShutdownEvent() const;
-  HRESULT DoOnDemand(bool is_update_check_only,
-                     const TCHAR* guid,
-                     IJobObserver* observer);
-
-  // Uninitializes the observer and allows COM calls.
-  void ResetStateOnError() const;
-
-  Worker* worker_;
-};
-
-class OnDemandCOMClassMachine : public OnDemandCOMClass {
- public:
-  DECLARE_REGISTRY_RESOURCEID_EX(IDR_GOOGLE_UPDATE_WORKER_CLASS_MACHINE)
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_WORKER_COM_WRAPPER_H__
-
diff --git a/worker/worker_event_logger.cc b/worker/worker_event_logger.cc
deleted file mode 100644
index 57d9b56..0000000
--- a/worker/worker_event_logger.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/worker/worker_event_logger.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/time.h"
-#include "omaha/common/utils.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/event_logger.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/resource_manager.h"
-#include "omaha/worker/job.h"
-
-namespace omaha {
-
-const TCHAR* const kUpdateCheckEventDesc = _T("Update check. Status = 0x%08x");
-const TCHAR* const kUpdateEventDesc           = _T("Application update");
-const TCHAR* const kInstallEventDesc          = _T("Application install");
-const TCHAR* const kUpdateAppsWorkerEventDesc = _T("Update worker start");
-
-void WriteUpdateCheckEvent(bool is_machine, HRESULT hr, const CString& text) {
-  const int event_type = SUCCEEDED(hr) ? EVENTLOG_INFORMATION_TYPE :
-                                         EVENTLOG_WARNING_TYPE;
-  const int event_id   = kUpdateCheckEventId;
-
-  CString event_description, event_text;
-  event_description.Format(kUpdateCheckEventDesc, hr);
-
-  CString url;
-  VERIFY1(SUCCEEDED(ConfigManager::Instance()->GetUpdateCheckUrl(&url)));
-  event_text.Format(_T("url=%s\n%s"), url, text);
-
-  GoogleUpdateLogEvent update_check_event(event_type, event_id, is_machine);
-  update_check_event.set_event_desc(event_description);
-  update_check_event.set_event_text(event_text);
-  update_check_event.WriteEvent();
-}
-
-void WriteJobCompletedEvent(bool is_machine, const Job& job) {
-  int type = IsCompletionSuccess(job.info()) ? EVENTLOG_INFORMATION_TYPE :
-                                               EVENTLOG_WARNING_TYPE;
-
-  GoogleUpdateLogEvent update_event(type, kUpdateEventId, is_machine);
-  CString desc = job.is_update() ? kUpdateEventDesc :
-                                   kInstallEventDesc;
-  update_event.set_event_desc(desc);
-  CString event_text;
-  event_text.AppendFormat(_T("App=%s, Ver=%s, PrevVer=%s, Status=0x%08x"),
-                          GuidToString(job.app_data().app_guid()),
-                          job.app_data().version(),
-                          job.app_data().previous_version(),
-                          job.info().error_code);
-  update_event.set_event_text(event_text);
-  update_event.WriteEvent();
-}
-
-void WriteUpdateAppsWorkerStartEvent(bool is_machine) {
-  GoogleUpdateLogEvent update_event(EVENTLOG_INFORMATION_TYPE,
-                                    kWorkerStartEventId,
-                                    is_machine);
-  update_event.set_event_desc(kUpdateAppsWorkerEventDesc);
-
-  ConfigManager& cm = *ConfigManager::Instance();
-
-  int au_check_period_ms = cm.GetAutoUpdateTimerIntervalMs();
-  int time_since_last_checked_sec = cm.GetTimeSinceLastCheckedSec(is_machine);
-  bool is_period_overridden = false;
-  int last_check_period_ms = cm.GetLastCheckPeriodSec(&is_period_overridden);
-
-  CString event_text;
-  event_text.Format(
-    _T("AuCheckPeriodMs=%d, TimeSinceLastCheckedSec=%d, ")
-    _T("LastCheckedPeriodSec=%d"),
-    au_check_period_ms, time_since_last_checked_sec, last_check_period_ms);
-
-  update_event.set_event_text(event_text);
-  update_event.WriteEvent();
-}
-
-}  // namespace omaha
diff --git a/worker/worker_event_logger.h b/worker/worker_event_logger.h
deleted file mode 100644
index 06202ae..0000000
--- a/worker/worker_event_logger.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_WORKER_WORKER_EVENT_LOGGER_H__
-#define OMAHA_WORKER_WORKER_EVENT_LOGGER_H__
-
-#include <windows.h>
-#include <atlstr.h>
-
-namespace omaha {
-
-class Job;
-
-void WriteUpdateAppsWorkerStartEvent(bool is_machine);
-void WriteUpdateCheckEvent(bool is_machine, HRESULT hr, const CString& text);
-void WriteJobCompletedEvent(bool is_machine, const Job& job);
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_WORKER_EVENT_LOGGER_H__
diff --git a/worker/worker_job.cc b/worker/worker_job.cc
deleted file mode 100644
index b05088b..0000000
--- a/worker/worker_job.cc
+++ /dev/null
@@ -1,940 +0,0 @@
-// Copyright 2007-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-//
-// TODO(omaha): The jobs are not being moved to the completed state in the
-// cancel/shutdown case.
-// Should be able to ASSERT1(job->job_state() == COMPLETED) in the worker
-// destructor.
-#include "omaha/worker/worker_job.h"
-
-#include <windows.h>
-#include <atlcom.h>
-#include <cstring>
-#include <vector>
-#include "omaha/common/const_addresses.h"
-#include "omaha/common/const_object_names.h"
-#include "omaha/common/constants.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/common/sta_call.h"
-#include "omaha/common/string.h"
-#include "omaha/common/user_info.h"
-#include "omaha/common/utils.h"
-#include "omaha/common/vista_utils.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/goopdate_xml_parser.h"
-#include "omaha/goopdate/request.h"
-#include "omaha/goopdate/resource.h"
-#include "omaha/goopdate/update_response_data.h"
-#include "omaha/net/browser_request.h"
-#include "omaha/net/cup_request.h"
-#include "omaha/net/network_request.h"
-#include "omaha/net/simple_request.h"
-#include "omaha/worker/application_data.h"
-#include "omaha/worker/application_manager.h"
-#include "omaha/worker/download_manager.h"
-#include "omaha/worker/install_manager.h"
-#include "omaha/worker/job.h"
-#include "omaha/worker/job_creator.h"
-#include "omaha/worker/ping.h"
-#include "omaha/worker/ping_utils.h"
-#include "omaha/worker/worker_event_logger.h"
-#include "omaha/worker/worker_job_strategy.h"
-#include "omaha/worker/worker_metrics.h"
-
-// Generated by MIDL in the "BUILD_MODE.OBJ_ROOT + SETTINGS.SUBDIR".
-#include "goopdate/google_update_idl.h"
-
-namespace omaha {
-
-// job_observer can be NULL.
-WorkerJob::WorkerJob(WorkerJobStrategy* strategy, JobObserver* job_observer)
-    : is_canceled_(false),
-      cur_job_(NULL),
-      bundle_dl_size_(0),
-      bundle_bytes_downloaded_(0),
-      job_observer_(job_observer),
-      no_jobs_completed_ping_sent_(false),
-      error_code_(S_OK) {
-  CORE_LOG(L2, (_T("[WorkerJob::WorkerJob]")));
-  ASSERT1(strategy);
-  strategy_.reset(strategy);
-  strategy_->set_worker_job(this);
-
-  is_machine_ = strategy_->is_machine();
-  running_version_  = GetVersionString();
-
-  const NetworkConfig::Session& session(NetworkConfig::Instance().session());
-
-  // The network request used by the update checks that do not require
-  // encryption.
-  network_request_.reset(new NetworkRequest(session));
-  network_request_->AddHttpRequest(new CupRequest(new SimpleRequest));
-  network_request_->AddHttpRequest(new SimpleRequest);
-  network_request_->AddHttpRequest(new CupRequest(new BrowserRequest));
-  network_request_->set_num_retries(1);
-
-  // The network request used by the encrypted update checks. For trusted tester
-  // update requests, because the TT token is a secret but is sent in the clear,
-  // we need https.
-  network_request_encrypted_.reset(new NetworkRequest(session));
-  network_request_encrypted_->AddHttpRequest(new SimpleRequest);
-  network_request_encrypted_->AddHttpRequest(new BrowserRequest);
-  network_request_encrypted_->set_num_retries(1);
-
-  // TODO(omaha): consider providing a download directory where file is
-  // to be downloaded. This directory should be know very early during the
-  // execution of the program and transmited as a parameter to DownloadManager.
-  download_manager_.reset(new DownloadManager(is_machine_));
-  download_manager_->set_impersonation_token(session.impersonation_token);
-
-  ping_.reset(new Ping());
-}
-
-WorkerJob::~WorkerJob() {
-  CORE_LOG(L2, (_T("[WorkerJob::~WorkerJob]")));
-  for (size_t i = 0; i < jobs_.size(); ++i) {
-    delete jobs_[i];
-  }
-}
-
-HRESULT BuildUninstallPing(bool is_machine, Request** uninstall_ping) {
-  CORE_LOG(L2, (_T("[BuildUninstallPing]")));
-  ASSERT1(uninstall_ping);
-
-  AppManager app_manager(is_machine);
-  ProductDataVector products;
-  HRESULT hr = app_manager.GetUnRegisteredProducts(&products);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[GetUnRegisteredProducts failed 0x%08x]"), hr));
-    return hr;
-  }
-
-  scoped_ptr<Request> request(new Request(is_machine));
-  for (size_t i = 0; i < products.size(); ++i) {
-    ProductData& product_data = products[i];
-    ASSERT1(product_data.app_data().is_uninstalled());
-
-    CORE_LOG(L2, (_T("[found uninstalled product][parent %s][app guid %s]"),
-                  GuidToString(product_data.app_data().parent_app_guid()),
-                  GuidToString(product_data.app_data().app_guid())));
-
-    // TODO(omaha): Do we have uninstall pings for components?
-    AppRequestData app_request_data(product_data.app_data());
-    PingEvent ping_event(PingEvent::EVENT_UNINSTALL,
-                         PingEvent::EVENT_RESULT_SUCCESS,
-                         0,  // error code
-                         0,  // extra code 1
-                         product_data.app_data().previous_version());
-    app_request_data.AddPingEvent(ping_event);
-    AppRequest app_request(app_request_data);
-    request->AddAppRequest(app_request);
-
-    VERIFY1(SUCCEEDED(app_manager.RemoveClientState(product_data.app_data())));
-  }
-
-  *uninstall_ping = request.release();
-  return S_OK;
-}
-
-HRESULT WorkerJob::DoProcess() {
-  CORE_LOG(L2, (_T("[WorkerJob::DoProcess]")));
-
-  ProductDataVector products;
-  HRESULT hr = DoProcessInternal(&products);
-
-  if (FAILED(hr)) {
-    CString msg;
-    switch (hr) {
-      case GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY:
-        ASSERT1(!strategy_->first_disallowed_app_name().IsEmpty());
-        msg.FormatMessage(IDS_APP_INSTALL_DISABLED_BY_GROUP_POLICY,
-                          strategy_->first_disallowed_app_name());
-        break;
-
-      case GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY:
-      case GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED:
-      default:
-        msg.FormatMessage(IDS_INSTALL_FAILED, hr);
-        break;
-    }
-    ASSERT1(!msg.IsEmpty());
-    JobCompletionStatus status = (hr == GOOPDATE_E_WORKER_CANCELLED) ?
-                                 COMPLETION_CANCELLED :
-                                 COMPLETION_ERROR;
-    NotifyCompleted(status, hr, msg, products);
-  }
-
-  return hr;
-}
-
-HRESULT WorkerJob::DoProcessInternal(ProductDataVector* products) {
-  CORE_LOG(L2, (_T("[WorkerJob::DoProcessInternal]")));
-  ASSERT1(products);
-  ASSERT1(strategy_.get());
-
-  scoped_co_init init_com_apt(COINIT_MULTITHREADED);
-  HRESULT hr(init_com_apt.hresult());
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[init_com_apt failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  if (job_observer_) {
-    job_observer_->SetEventSink(this);
-    job_observer_->OnShow();
-  }
-
-  hr = strategy_->PreUpdateCheck(products);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[PreUpdateCheck failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  if (products->empty()) {
-    OPT_LOG(L1, (_T("[No products to check updates for. Exiting.]")));
-    // We should always have products in all cases except for updates.
-    ASSERT1(strategy_->IsAutoUpdate());
-    NotifyCompleted(COMPLETION_SUCCESS, S_OK, _T("No products!"), *products);
-    return S_OK;
-  }
-
-  hr = strategy_->RemoveDisallowedApps(products);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[RemoveDisallowedApps failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  hr = strategy_->DoUpdateCheck(*products);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[DoUpdateCheck failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  hr = strategy_->PostUpdateCheck();
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[PostUpdateCheck failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  hr = strategy_->ProcessApps();
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[ProcessApps failed][0x%08x]"), hr));
-    return hr;
-  }
-
-  return strategy_->PostInstall();
-}
-
-int WorkerJob::CalculateBundleSize() const {
-  CORE_LOG(L2, (_T("[WorkerJob::CalculateBundleSize]")));
-  int bundle_dl_size = 0;
-  for (size_t i = 0; i < jobs_.size(); ++i) {
-    bundle_dl_size += jobs_[i]->response_data().size();
-  }
-  return bundle_dl_size;
-}
-
-HRESULT WorkerJob::DownloadJobs() {
-  CORE_LOG(L2, (_T("[WorkerJob::DownloadJobs]")));
-  bundle_dl_size_ = CalculateBundleSize();
-
-  // Download all jobs.
-  for (size_t i = 0; i < jobs_.size(); ++i) {
-    if (is_canceled_) {
-      return GOOPDATE_E_WORKER_CANCELLED;
-    }
-
-    cur_job_ = jobs_[i];
-    ASSERT1(cur_job_);
-
-    if (cur_job_->job_state() != JOBSTATE_COMPLETED) {
-      cur_job_->set_job_observer(this);
-      HRESULT hr = cur_job_->Download(download_manager_.get());
-      cur_job_->set_job_observer(NULL);
-      cur_job_ = NULL;
-      if (FAILED(hr)) {
-        CORE_LOG(LE, (_T("[DownloadJob failed with 0x%08x]"), hr));
-        continue;
-      }
-    }
-
-    cur_job_ = NULL;
-  }
-
-  return S_OK;
-}
-
-HRESULT WorkerJob::InstallJobs() {
-  CORE_LOG(L2, (_T("[WorkerJob::InstallJobs]")));
-  // Install all jobs. We install the jobs in the reverse order, since we want
-  // the primary job, which is the first in the list to be installed last.
-  // The primary job can then perform user visible actions such as start
-  // the application.
-  Jobs::reverse_iterator iter = jobs_.rbegin();
-  for (; iter != jobs_.rend(); ++iter) {
-    if (is_canceled_) {
-      return GOOPDATE_E_WORKER_CANCELLED;
-    }
-
-    cur_job_ = *iter;
-    ASSERT1(cur_job_);
-
-    if (cur_job_->job_state() != JOBSTATE_COMPLETED) {
-      cur_job_->set_job_observer(this);
-      HRESULT hr = cur_job_->Install();
-
-      if (SUCCEEDED(hr)) {
-        if (!::IsEqualGUID(kGoopdateGuid, cur_job_->app_data().app_guid()) &&
-            strategy_->IsAutoUpdate()) {
-            ++metric_worker_app_updates_succeeded;
-        }
-      }
-
-      cur_job_->set_job_observer(NULL);
-      if (FAILED(hr)) {
-        cur_job_ = NULL;
-        CORE_LOG(LE, (_T("[InstallJob failed with 0x%08x]"), hr));
-        // The error is reported as necessary by Job::Install.
-        continue;
-      }
-    }
-    cur_job_ = NULL;
-  }
-
-  return S_OK;
-}
-
-void WorkerJob::CreateRequestFromProducts(
-    const ProductDataVector& products,
-    Request** request,
-    bool* encrypt_connection) {
-  CORE_LOG(L2, (_T("[WorkerJob::CreateRequestFromProducts]")));
-  ASSERT1(request);
-  ASSERT1(encrypt_connection);
-
-  *encrypt_connection = false;
-  scoped_ptr<Request> req(new Request(is_machine_));
-  for (size_t i = 0; i < products.size(); ++i) {
-    const ProductData& product_data = products[i];
-    if (!product_data.app_data().tt_token().IsEmpty()) {
-      *encrypt_connection = true;
-    }
-    AppRequestData app_request_data(product_data.app_data());
-    AppRequest app_request(app_request_data);
-    for (AppDataVector::const_iterator it = product_data.components_begin();
-         it != product_data.components_end();
-         ++it) {
-      AppRequestData component_request_data(*it);
-      app_request.AddComponentRequest(component_request_data);
-    }
-    req->AddAppRequest(app_request);
-  }
-  ASSERT1(products.size() == static_cast<size_t>(req->get_request_count()));
-
-  *request = req.release();
-}
-
-// If the job is interactive the job will notify the UI.
-// This method could be called multiple times, and thus needs to be idempotent.
-void WorkerJob::NotifyCompleted(JobCompletionStatus status,
-                                DWORD error,
-                                const CString& text,
-                                const ProductDataVector& products) {
-  CORE_LOG(L2, (_T("[WorkerJob::NotifyCompleted][%d][0x%08x]"), status, error));
-  ASSERT1(IsCompletionStatusSuccess(status) || !text.IsEmpty());
-
-  if (IsCompletionStatusSuccess(status)) {
-    ASSERT1(S_OK == error);
-    error_code_ = S_OK;
-  } else {
-    error_code_ = error;
-    ASSERT1(FAILED(error_code_));
-    if (SUCCEEDED(error_code_)) {
-      error_code_ = E_FAIL;
-    }
-  }
-
-  CompletionInfo info(status, error, text);
-  if (jobs_.empty()) {
-    if (job_observer_) {
-      job_observer_->OnComplete(IsCompletionStatusSuccess(status) ?
-                                    COMPLETION_CODE_SUCCESS :
-                                    COMPLETION_CODE_ERROR,
-                                text,
-                                error);
-      if (!no_jobs_completed_ping_sent_) {
-        const bool is_update = strategy_->IsUpdate();
-
-        // This is a pretty big assumption, unfortunately hard to enforce in
-        // the code: since the jobs are empty, it means that the server did
-        // not respond with updates available. In the case of silent and
-        // on demand updates, this is a not an error. There is no need to send
-        // a completion ping in this case.
-        if (is_update &&
-            IsCompletionStatusSuccess(status) &&
-            error == NOERROR) {
-          return;
-        }
-
-        no_jobs_completed_ping_sent_ = true;
-        HRESULT hr = ping_utils::SendCompletedPingsForAllProducts(
-            products,
-            is_machine_,
-            is_update,
-            info,
-            ping_.get());
-        if (FAILED(hr)) {
-          CORE_LOG(LW, (_T("[SendCompletedPingsForAllProducts failed][0x%08x]"),
-                        hr));
-        }
-      }
-    }
-
-    return;
-  }
-
-  // cur_job_->NotifyCompleted will eventually bubble back up to
-  // WorkerJob::OnCompleted which will complete the rest of the jobs.
-  if (cur_job_) {
-    cur_job_->NotifyCompleted(info);
-  } else {
-    // If there is no current job, and we are being asked to complete, then we
-    // set the primary application as the current job and complete it.
-    ASSERT1(!jobs_.empty());
-    cur_job_ = jobs_[0];
-    cur_job_->set_job_observer(this);
-    cur_job_->NotifyCompleted(info);
-    cur_job_->set_job_observer(NULL);
-    cur_job_ = NULL;
-  }
-}
-
-// Does the following:
-// * Performs some pre-processing on the AppData.
-// * Converts Job to Request.
-// * Converts Request into a post string.
-// * Sends the post.
-// * Parses the response.
-// * Saves the result inside the job.
-// * Performs post-processing on the AppData.
-HRESULT WorkerJob::DoUpdateCheck(const ProductDataVector& products) {
-  CORE_LOG(L2, (_T("[WorkerJob::DoUpdateCheck]")));
-
-  if (!ConfigManager::Instance()->CanUseNetwork(is_machine_)) {
-    CORE_LOG(L1, (_T("[Update check failed because network use prohibited]")));
-    return GOOPDATE_E_CANNOT_USE_NETWORK;
-  }
-
-  const bool use_update_metrics = strategy_->IsUpdate();
-
-  if (use_update_metrics) {
-    ++metric_worker_update_check_total;
-  }
-
-  // Google Update setup has completed if necessary before this point.
-  // This binary's version should be the installed version.
-  CString installed_version;
-  ASSERT1(SUCCEEDED(RegKey::GetValue(
-      ConfigManager::Instance()->registry_update(is_machine_),
-      kRegValueInstalledVersion,
-      &installed_version)));
-  ASSERT1(GetVersionString() == installed_version);
-
-  ASSERT1(!products.empty());
-
-  // Notify the UI that we've started.
-  OnCheckingForUpdate();
-
-  Request* request = NULL;
-  bool encrypt_connection = false;
-  CreateRequestFromProducts(products, &request, &encrypt_connection);
-  ASSERT1(request);
-  scoped_ptr<Request> req(request);
-
-  // Serialize the request.
-  // TODO(omaha): the request string can be serialized as UTF-8 to avoid
-  // additional copying of the request buffer when sending.
-  CString request_string;
-  HRESULT hr = GoopdateXmlParser::GenerateRequest(*req.get(),
-                                                  true,
-                                                  &request_string);
-  if (FAILED(hr)) {
-    CString msg;
-    msg.FormatMessage(IDS_INSTALL_FAILED, hr);
-    NotifyCompleted(COMPLETION_ERROR, hr, msg, products);
-    return hr;
-  }
-  ASSERT1(!request_string.IsEmpty());
-
-  // Get the url to send to.
-  CString update_url;
-  hr = ConfigManager::Instance()->GetUpdateCheckUrl(&update_url);
-  if (FAILED(hr)) {
-    CString msg;
-    msg.FormatMessage(IDS_INSTALL_FAILED, hr);
-    NotifyCompleted(COMPLETION_ERROR, hr, msg, products);
-    return hr;
-  }
-
-  if (is_canceled_) {
-    return GOOPDATE_E_WORKER_CANCELLED;
-  }
-
-  // Send the network request.
-  CORE_LOG(L2, (_T("[Sending update check...]")));
-  std::vector<uint8> response_buffer;
-  NetworkRequest* network_request = encrypt_connection ?
-                                    network_request_encrypted_.get() :
-                                    network_request_.get();
-  ASSERT1(network_request);
-  hr = network_request->PostString(update_url,
-                                   request_string,
-                                   &response_buffer);
-  if (hr == OMAHA_NET_E_REQUEST_CANCELLED) {
-    return hr;
-  }
-  if (FAILED(hr)) {
-    goopdate_utils::AddNetworkRequestDataToEventLog(network_request, hr);
-
-    if (strategy_->ShouldLaunchBrowserOnUpdateCheckError()) {
-      const TCHAR* const kUpdateCheckSourceId = _T("updatecheck");
-      CString url;
-      HRESULT hres = goopdate_utils::BuildHttpGetString(
-          kUrlMoreInformation,
-          hr,
-          0,
-          0,
-          GuidToString(products[0].app_data().app_guid()),
-          running_version_,
-          is_machine_,
-          strategy_->language(),
-          products[0].app_data().iid(),
-          products[0].app_data().brand_code(),
-          kUpdateCheckSourceId,
-          &url);
-      if (SUCCEEDED(hres)) {
-        VERIFY1(SUCCEEDED(goopdate_utils::LaunchBrowser(BROWSER_DEFAULT, url)));
-      } else {
-        CORE_LOG(LW, (_T("[BuildHttpGetString failed][0x%08x]"), hres));
-      }
-    }
-
-    CString msg;
-    // Ignore the return value because the default message is what we want.
-    goopdate_utils::FormatMessageForNetworkError(
-          hr,
-          products[0].app_data().display_name(),
-          &msg);
-
-    NotifyCompleted(COMPLETION_ERROR, hr, msg, products);
-    return hr;
-  }
-  ASSERT1(network_request->http_status_code() == HTTP_STATUS_OK);
-
-  // Parse the response.
-  CORE_LOG(L2, (_T("[Parse update check][%s]"),
-                Utf8BufferToWideChar(response_buffer)));
-  UpdateResponses responses;
-  hr = GoopdateXmlParser::ParseManifestBytes(response_buffer, &responses);
-  if (FAILED(hr)) {
-    CString msg;
-    msg.FormatMessage(IDS_INSTALL_FAILED, hr);
-    NotifyCompleted(COMPLETION_ERROR, hr, msg, products);
-    return hr;
-  }
-  ASSERT1(static_cast<size_t>(req->get_request_count()) == responses.size());
-
-  if (strategy_->IsUpdate()) {
-    HandleSuccessfulUpdateCheckRequestSend(responses, products);
-  }
-
-  hr = CreateJobs(false, responses, products);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  if (use_update_metrics) {
-    ++metric_worker_update_check_succeeded;
-  }
-  return S_OK;
-}
-
-void WorkerJob::HandleSuccessfulUpdateCheckRequestSend(
-    const UpdateResponses& responses,
-    const ProductDataVector& products) {
-  AppManager app_manager(is_machine_);
-
-  for (ProductDataVector::const_iterator products_it = products.begin();
-       products_it != products.end(); ++products_it) {
-    const ProductData& product_data = *products_it;
-    AppData app_data = product_data.app_data();
-    UpdateResponses::const_iterator it = responses.find(app_data.app_guid());
-
-    if (it != responses.end()) {
-      const UpdateResponse& response = it->second;
-
-      VERIFY1(SUCCEEDED(app_manager.HandleSuccessfulUpdateCheckRequestSend(
-          response.time_since_midnight_sec(), &app_data)));
-    }
-  }
-}
-
-HRESULT WorkerJob::CreateJobs(bool is_offline,
-                              const UpdateResponses& responses,
-                              const ProductDataVector& products) {
-  ASSERT1(!products.empty());
-  ASSERT1(is_offline == responses.empty());
-
-  CString event_log_text;
-  Request ping_request(is_machine_);
-  JobCreator job_creator(is_machine_, strategy_->IsUpdate(), ping_.get());
-  job_creator.set_is_auto_update(strategy_->IsAutoUpdate());
-  job_creator.set_is_update_check_only(strategy_->IsUpdateCheckOnly());
-  job_creator.set_fail_if_update_not_available(
-      strategy_->ShouldFailOnUpdateNotAvailable());
-  CompletionInfo completion_info;
-
-  HRESULT hr(S_OK);
-  if (is_offline) {
-    CString offline_dir =
-        is_machine_ ?
-        ConfigManager::Instance()->GetMachineSecureOfflineStorageDir() :
-        ConfigManager::Instance()->GetUserOfflineStorageDir();
-    hr = job_creator.CreateOfflineJobs(offline_dir,
-                                       products,
-                                       &jobs_,
-                                       &ping_request,
-                                       &event_log_text,
-                                       &completion_info);
-  } else {
-    hr = job_creator.CreateJobsFromResponses(responses,
-                                             products,
-                                             &jobs_,
-                                             &ping_request,
-                                             &event_log_text,
-                                             &completion_info);
-  }
-
-  WriteUpdateCheckEvent(is_machine_, hr, event_log_text);
-
-  // Send ping_request to the server to report results of job creation.
-  if (ping_request.get_request_count() > 0) {
-    ping_->SendPing(&ping_request);
-  }
-
-  if (FAILED(hr)) {
-    NotifyCompleted(completion_info.status,
-                    completion_info.error_code,
-                    completion_info.text,
-                    products);
-    return hr;
-  }
-
-  for (size_t i = 0; i < jobs_.size(); ++i) {
-    OnUpdateAvailable(jobs_[i]->response_data().version());
-  }
-
-  return S_OK;
-}
-
-HRESULT WorkerJob::CreateOfflineJobs(const ProductDataVector& products) {
-  CORE_LOG(L2, (_T("[WorkerJob::CreateOfflineJobs]")));
-  UpdateResponses responses;
-  return CreateJobs(true, responses, products);
-}
-
-// ::InterlockedExchange is used to have a memory barrier and flush the caches.
-HRESULT WorkerJob::Cancel() {
-  CORE_LOG(L2, (_T("[WorkerJob::Cancel]")));
-  ASSERT1(ping_.get());
-  ::InterlockedExchange(&is_canceled_, true);
-
-  VERIFY1(SUCCEEDED(network_request_->Cancel()));
-  VERIFY1(SUCCEEDED(network_request_encrypted_->Cancel()));
-  VERIFY1(SUCCEEDED(download_manager_->Cancel()));
-  VERIFY1(SUCCEEDED(ping_->Cancel()));
-  return S_OK;
-}
-
-void WorkerJob::CompleteAllNonCompletedJobs(JobCompletionStatus status,
-                                            DWORD error,
-                                            const CString& text) {
-  CORE_LOG(L2, (_T("[WorkerJob::CompleteAllNonCompletedJobs]")));
-  CompletionInfo info(status, error, text);
-  for (size_t i = 0; i < jobs_.size(); ++i) {
-    Job* job = jobs_[i];
-    ASSERT1(job);
-
-    if (cur_job_ != job && job->job_state() != JOBSTATE_COMPLETED) {
-      job->NotifyCompleted(info);
-    }
-  }
-}
-
-// Observer interface implementation.
-void WorkerJob::OnShow() {
-  CORE_LOG(L2, (_T("[WorkerJob::OnShow]")));
-  if (job_observer_) {
-    job_observer_->OnShow();
-  }
-}
-
-void WorkerJob::OnCheckingForUpdate() {
-  CORE_LOG(L2, (_T("[WorkerJob::OnCheckingForUpdate]")));
-  if (job_observer_) {
-    job_observer_->OnCheckingForUpdate();
-  }
-}
-
-void WorkerJob::OnUpdateAvailable(const TCHAR* version_string) {
-  CORE_LOG(L2, (_T("[WorkerJob::OnUpdateAvailable]")));
-  if (job_observer_) {
-    job_observer_->OnUpdateAvailable(version_string);
-  }
-}
-
-void WorkerJob::OnWaitingToDownload() {
-  CORE_LOG(L2, (_T("[WorkerJob::OnWaitingToDownload]")));
-  if (job_observer_) {
-    job_observer_->OnWaitingToDownload();
-  }
-}
-
-void WorkerJob::OnDownloading(int time_remaining_ms, int pos) {
-  CORE_LOG(L2, (_T("[WorkerJob::OnDownloading %d %d]"),
-                time_remaining_ms, pos));
-  UNREFERENCED_PARAMETER(pos);
-  ASSERT1(cur_job_);
-
-  int bytes_dl = bundle_bytes_downloaded_ + cur_job_->bytes_downloaded();
-  ASSERT1(bytes_dl <= bundle_dl_size_);
-  if (cur_job_->bytes_total() != 0) {
-    int expected_pos = static_cast<int>((
-        static_cast<double>(cur_job_->bytes_downloaded()) /
-         cur_job_->bytes_total()) * 100);
-    ASSERT1(expected_pos == pos);
-  }
-
-  int total_pos = 0;
-  if (bundle_dl_size_) {
-    total_pos = static_cast<int>((static_cast<float>(bytes_dl) /
-                                              bundle_dl_size_) * 100);
-  }
-
-  if (job_observer_) {
-    job_observer_->OnDownloading(time_remaining_ms, total_pos);
-  }
-}
-
-void WorkerJob::OnWaitingToInstall() {
-  CORE_LOG(L2, (_T("[WorkerJob::OnWaitingToInstall]")));
-  ASSERT1(cur_job_);
-  bundle_bytes_downloaded_ += cur_job_->response_data().size();
-
-  // We switch over to waiting for install only when all jobs are downloaded.
-  if (cur_job_ == jobs_.back() && job_observer_) {
-    job_observer_->OnWaitingToInstall();
-  }
-}
-
-void WorkerJob::OnInstalling() {
-  CORE_LOG(L2, (_T("[WorkerJob::OnInstalling]")));
-  ASSERT1(cur_job_);
-
-  if (cur_job_ == jobs_.back() && job_observer_) {
-    job_observer_->OnInstalling();
-  }
-}
-
-void WorkerJob::OnPause() {
-  CORE_LOG(L2, (_T("[WorkerJob::OnPause]")));
-  ASSERT1(cur_job_);
-
-  if (cur_job_ == jobs_.front() && job_observer_) {
-    job_observer_->OnPause();
-  }
-}
-
-void WorkerJob::OnComplete(CompletionCodes code,
-                           const TCHAR* text,
-                           DWORD error_code) {
-  CORE_LOG(L2, (_T("[WorkerJob::OnComplete][%d][0x%08x]"), code, error_code));
-  ASSERT1(cur_job_);
-  ASSERT1(!jobs_.empty());
-
-  if (code == COMPLETION_CODE_ERROR) {
-    if (IsCompletionInstallerError(cur_job_->info())) {
-      error_code_ = GOOPDATEINSTALL_E_INSTALLER_FAILED;
-    } else {
-      error_code_ = error_code;
-    }
-
-    ASSERT1(FAILED(error_code_));
-    if (SUCCEEDED(error_code_)) {
-      error_code_ = E_FAIL;
-    }
-  } else {
-    error_code_ = S_OK;
-  }
-
-  if (strategy_->IsAutoUpdate()) {
-    // TODO(Omaha): Refactor this code for bundles etc. Maybe move to strategy.
-    return;
-  }
-
-  Job* primary_job = jobs_[0];
-  ASSERT1(primary_job);
-
-  if (code == COMPLETION_CODE_ERROR) {
-    CString msg = text;
-    if (primary_job != cur_job_) {
-      // If there is an error because of a non-primary job,
-      // we need to change the error message to reflect this.
-      msg.FormatMessage(IDS_BUNDLE_INSTALL_FAILED,
-                        cur_job_->app_data().display_name(),
-                        text);
-    }
-    if (job_observer_) {
-      job_observer_->OnComplete(code, msg, error_code);
-    }
-
-    // We need to complete all the other jobs in the bundle, since one of the
-    // jobs failed. The jobs that have completed thus far, will not be
-    // altered, however the jobs from this point on will be completed with
-    // COMPLETION_ERROR.
-    CompleteAllNonCompletedJobs(COMPLETION_ERROR,
-                                static_cast<DWORD>(GOOPDATE_E_BUNDLE_ERROR),
-                                msg);
-
-    return;
-  }
-
-  if (cur_job_ == primary_job && job_observer_) {
-    // If we are dealing with the primary application and the result is a
-    // success we need to complete the UI.
-    job_observer_->OnComplete(code, text, error_code);
-  }
-}
-
-void WorkerJob::SetEventSink(ProgressWndEvents* event_sink) {
-  CORE_LOG(L2, (_T("[WorkerJob::SetEventSink]")));
-  UNREFERENCED_PARAMETER(event_sink);
-  return;
-}
-
-// ProgressWndEvents implementation.
-void WorkerJob::DoPause() {
-  ASSERT(false, (_T("Pause is not currently supported")));
-}
-
-void WorkerJob::DoResume() {
-  ASSERT(false, (_T("Pause is not currently supported")));
-}
-
-void WorkerJob::DoClose() {
-  CORE_LOG(L2, (_T("[WorkerJob::DoClose]")));
-  Cancel();
-}
-
-void WorkerJob::DoRestartBrowsers() {
-  CORE_LOG(L2, (_T("[WorkerJob::DoRestartBrowsers]")));
-  ASSERT1(!jobs_.empty());
-  Job* primary_job = jobs_[0];
-  ASSERT1(primary_job);
-  primary_job->RestartBrowsers();
-}
-
-void WorkerJob::DoReboot() {
-  ASSERT(false, (_T("Reboot is not currently supported")));
-}
-
-void WorkerJob::DoLaunchBrowser(const CString& url) {
-  CORE_LOG(L2, (_T("[WorkerJob::DoLaunchBrowser %s]"), url));
-  if (jobs_.empty()) {
-    VERIFY1(SUCCEEDED(goopdate_utils::LaunchBrowser(BROWSER_DEFAULT, url)));
-  } else {
-    Job* primary_job = jobs_[0];
-    ASSERT1(primary_job);
-    primary_job->LaunchBrowser(url);
-  }
-}
-
-// job_observer can be NULL.
-WorkerJob* WorkerJobFactory::CreateWorkerJob(bool is_machine,
-                                             const CommandLineArgs& args,
-                                             JobObserver* job_observer) {
-  WorkerJobStrategy* strategy(NULL);
-#pragma warning(push)
-// C4061: enumerator 'xxx' in switch of enum 'yyy' is not explicitly handled by
-// a case label.
-#pragma warning(disable : 4061)
-
-  switch (args.mode) {
-    case COMMANDLINE_MODE_IG:
-    case COMMANDLINE_MODE_HANDOFF_INSTALL:
-      strategy = WorkerJobStrategyFactory::CreateInstallStrategy(is_machine,
-                                                                 args,
-                                                                 job_observer);
-      break;
-    case COMMANDLINE_MODE_UA:
-      strategy =
-          WorkerJobStrategyFactory::CreateUpdateAppsStrategy(is_machine, args);
-      break;
-    default:
-      ASSERT(false, (_T("Invalid mode for a WorkerJob.")));
-  }
-
-#pragma warning(pop)
-
-  return new WorkerJob(strategy, job_observer);
-}
-
-HRESULT WorkerJobFactory::CreateOnDemandWorkerJob(
-    bool is_machine,
-    bool is_update_check_only,
-    const CString& lang,
-    const GUID& guid,
-    IJobObserver* observer,
-    WorkerComWrapperShutdownCallBack* call_back,
-    WorkerJob** worker_job) {
-  ASSERT1(observer);
-  ASSERT1(call_back);
-  ASSERT1(worker_job);
-  *worker_job = NULL;
-
-  scoped_ptr<OnDemandUpdateStrategy> strategy;
-  strategy.reset(WorkerJobStrategyFactory::CreateOnDemandStrategy(
-      is_update_check_only,
-      lang,
-      is_machine));
-
-  // TODO(omaha): Should all the stuff in Init really be in this class?
-  HRESULT hr = strategy->Init(guid, observer, call_back);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  *worker_job = new WorkerJob(strategy.release(), strategy->GetJobObserver());
-  return S_OK;
-}
-
-}  // namespace omaha
-
diff --git a/worker/worker_job.h b/worker/worker_job.h
deleted file mode 100644
index 6b093e0..0000000
--- a/worker/worker_job.h
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#ifndef OMAHA_WORKER_WORKER_JOB_H__
-#define OMAHA_WORKER_WORKER_JOB_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include <vector>
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "omaha/common/scoped_any.h"
-#include "omaha/goopdate/update_response.h"
-#include "omaha/worker/job.h"
-#include "omaha/worker/job_observer.h"
-
-namespace omaha {
-
-class AppData;
-class DownloadManager;
-class Ping;
-class Request;
-class WorkerJobStrategy;
-
-class WorkerJob : public JobObserver,
-                  public ProgressWndEvents {
- public:
-  explicit WorkerJob(WorkerJobStrategy* strategy, JobObserver* job_observer);
-  ~WorkerJob();
-  HRESULT DoProcess();
-  HRESULT Cancel();
-
-  Ping* ping() { return ping_.get(); }
-  bool is_machine() const { return is_machine_; }
-  const Jobs& jobs() const { return jobs_; }
-  WorkerJobStrategy* strategy() const { return strategy_.get(); }
-
-  // Observer interface.
-  virtual void OnShow();
-  virtual void OnCheckingForUpdate();
-  virtual void OnUpdateAvailable(const TCHAR* version_string);
-  virtual void OnWaitingToDownload();
-  virtual void OnDownloading(int time_remaining_ms, int pos);
-  virtual void OnWaitingToInstall();
-  virtual void OnInstalling();
-  virtual void OnPause();
-  virtual void OnComplete(CompletionCodes code,
-                          const TCHAR* text,
-                          DWORD error_code);
-  virtual void SetEventSink(ProgressWndEvents* event_sink);
-  virtual void Uninitialize() {}
-
-  // ProgressWndEvents implementation.
-  virtual void DoPause();
-  virtual void DoResume();
-  virtual void DoClose();
-  virtual void DoRestartBrowsers();
-  virtual void DoReboot();
-  virtual void DoLaunchBrowser(const CString& url);
-
-
-  // TODO(omaha): Consider making these methods private.
-  // The reason we need this bloated interface is because the strategies
-  // call into the workerjob that is treated as the context for the calls.
-  // One way to achieve this is to take in the workerjob in these methods
-  // and make them static non member methods.
-  HRESULT DownloadJobs();
-  HRESULT InstallJobs();
-  void NotifyCompleted(JobCompletionStatus status,
-                       DWORD error,
-                       const CString& text,
-                       const ProductDataVector& products);
-  void CompleteAllNonCompletedJobs(JobCompletionStatus status,
-                                   DWORD error,
-                                   const CString& text);
-  HRESULT DoUpdateCheck(const ProductDataVector& products);
-  HRESULT CreateOfflineJobs(const ProductDataVector& products);
-
-  HRESULT error_code() const { return error_code_; }
-
-  bool is_canceled() const { return !!is_canceled_; }
-
- private:
-  void HandleSuccessfulUpdateCheckRequestSend(
-      const UpdateResponses& responses, const ProductDataVector& products);
-
-  HRESULT CreateJobs(bool is_offline,
-                     const UpdateResponses& responses,
-                     const ProductDataVector& products);
-  HRESULT DoProcessInternal(ProductDataVector* products);
-
-  void UpdateResponseToCompletionInfo(const UpdateResponse& response,
-                                      CompletionInfo* info);
-
-  static bool IsUpdateAvailable(const UpdateResponse& response);
-  HRESULT CreateRequestFromApplications(const AppDataVector& applications,
-                                        Request** request);
-  void CreateRequestFromProducts(const ProductDataVector& products,
-                                 Request** request,
-                                 bool* encrypt_connection);
-
-  // Sets the UI displayed event, telling the other instance not to display an
-  // error UI.
-  void SetUiDisplayedEvent() const;
-
-  // Calculates the size of the download bundle.
-  int CalculateBundleSize() const;
-
-  Job* cur_job_;
-  int bundle_dl_size_;
-  int bundle_bytes_downloaded_;
-
-  bool is_machine_;
-  bool is_update_apps_worker_;
-  std::vector<Job*> jobs_;
-  scoped_ptr<NetworkRequest> network_request_;
-  scoped_ptr<NetworkRequest> network_request_encrypted_;
-  scoped_ptr<DownloadManager> download_manager_;
-  CString running_version_;
-  volatile LONG is_canceled_;
-  bool no_jobs_completed_ping_sent_;
-  scoped_ptr<Ping> ping_;
-  JobObserver* job_observer_;
-  scoped_ptr<WorkerJobStrategy> strategy_;
-  HRESULT error_code_;
-  friend class WorkerJobTest;
-
-  DISALLOW_EVIL_CONSTRUCTORS(WorkerJob);
-};
-
-class WorkerJobFactory {
- public:
-  static WorkerJob* CreateWorkerJob(bool is_machine,
-                                    const CommandLineArgs& args,
-                                    JobObserver* job_observer);
-
-  static HRESULT CreateOnDemandWorkerJob(
-      bool is_machine,
-      bool is_update_check_only,
-      const CString& lang,
-      const GUID& guid,
-      IJobObserver* observer,
-      WorkerComWrapperShutdownCallBack* call_back,
-      WorkerJob** worker_job);
-};
-
-// Creates a request containing the pings for the uninstalled products.
-// The caller has the ownership of the request object. Building the
-// uninstall ping is not an idempotent operation. The client state is
-// cleaned up after building the request.
-HRESULT BuildUninstallPing(bool is_machine, Request** uninstall_ping);
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_WORKER_JOB_H__
diff --git a/worker/worker_job_strategy.cc b/worker/worker_job_strategy.cc
deleted file mode 100644
index 0c6c32a..0000000
--- a/worker/worker_job_strategy.cc
+++ /dev/null
@@ -1,614 +0,0 @@
-// Copyright 2007-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/worker/worker_job_strategy.h"
-#include <functional>
-#include <algorithm>
-#include "omaha/common/const_cmd_line.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/error.h"
-#include "omaha/common/logging.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/common/scope_guard.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/goopdate_helper.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/stats_uploader.h"
-#include "omaha/setup/setup.h"
-#include "omaha/worker/application_manager.h"
-#include "omaha/worker/application_data.h"
-#include "omaha/worker/job_observer.h"
-#include "omaha/worker/ping.h"
-#include "omaha/worker/ping_utils.h"
-#include "omaha/worker/worker_job.h"
-#include "omaha/worker/worker_metrics.h"
-
-namespace omaha {
-
-namespace {
-
-// Google Update should never be not accepted since its EULA state is kept in
-// the Update key.
-// Uses RefHolder because std::not1 uses a reference to the parameter type,
-// which results in an illegal reference to a reference if the parameter is
-// just a const reference.
-bool IsEulaAccepted(const RefHolder<ProductData> product_holder) {
-  const ProductData& product = product_holder;
-  const bool is_accepted = product.app_data().is_eula_accepted();
-  ASSERT1(is_accepted ||
-          !::IsEqualGUID(kGoopdateGuid, product.app_data().app_guid()));
-  return is_accepted;
-}
-
-bool IsInstallDisabled(const ProductData& product) {
-  return !ConfigManager::Instance()->CanInstallApp(
-      product.app_data().app_guid());
-}
-
-bool IsUpdateDisabled(const ProductData& product, bool is_manual) {
-  return !ConfigManager::Instance()->CanUpdateApp(
-      product.app_data().app_guid(),
-      is_manual);
-}
-
-// Removes apps that are not allowed to be updated because the app's EULA has
-// not been accepted.
-// Returns true if any apps were removed.
-bool RemoveAppsIfUpdateDisallowedByEula(ProductDataVector* products) {
-  ASSERT1(products);
-
-  bool were_apps_removed = false;
-
-  const ProductDataVector::iterator new_end = std::remove_if(
-      products->begin(),
-      products->end(),
-      std::not1(std::ptr_fun(IsEulaAccepted)));
-  if (new_end != products->end()) {
-    const size_t previous_size = products->size();
-    products->erase(new_end, products->end());
-    were_apps_removed = true;
-    metric_worker_apps_not_updated_eula = previous_size - products->size();
-  }
-
-  return were_apps_removed;
-}
-
-// Removes apps that are not allowed to be installed because Group Policy has
-// disabled this.
-// Returns true if any apps were removed.
-bool RemoveAppsDisallowedByInstallGroupPolicy(
-    ProductDataVector* products,
-    CString* first_disallowed_app_name) {
-  ASSERT1(products);
-  ASSERT1(first_disallowed_app_name);
-  ASSERT1(first_disallowed_app_name->IsEmpty());
-
-  bool were_apps_removed = false;
-
-  const ProductDataVector::iterator new_end = std::remove_if(
-      products->begin(),
-      products->end(),
-      IsInstallDisabled);
-  if (new_end != products->end()) {
-    const size_t previous_size = products->size();
-    *first_disallowed_app_name = new_end->app_data().display_name();
-
-    products->erase(new_end, products->end());
-
-    were_apps_removed = true;
-    metric_worker_apps_not_installed_group_policy =
-        previous_size - products->size();
-  }
-
-  return were_apps_removed;
-}
-
-// Disables updates for apps  for which this type of update is disallowed by
-// Group Policy.
-// Returns true if updates were disabled for any app.
-bool DisableUpdateForAppsDisallowedByUpdateGroupPolicy(
-    bool is_manual,
-    ProductDataVector* products,
-    CString* first_disallowed_app_name) {
-  ASSERT1(products);
-  ASSERT1(first_disallowed_app_name);
-  ASSERT1(first_disallowed_app_name->IsEmpty());
-
-  bool were_apps_disabled = false;
-
-  for (ProductDataVector::iterator iter = products->begin();
-       iter != products->end();
-       ++iter) {
-    if (!IsUpdateDisabled(*iter, is_manual)) {
-      continue;
-    }
-
-    if (first_disallowed_app_name->IsEmpty()) {
-      *first_disallowed_app_name = iter->app_data().display_name();
-    }
-
-    AppData modified_app_data = iter->app_data();
-    modified_app_data.set_is_update_disabled(true);
-    iter->set_app_data(modified_app_data);
-
-    were_apps_disabled = true;
-    metric_worker_apps_not_updated_group_policy++;
-  }
-
-  return were_apps_disabled;
-}
-
-class OnlineStrategy : public NetworkStrategy {
- public:
-  explicit OnlineStrategy(WorkerJobStrategy* worker_job_strategy)
-      : NetworkStrategy(worker_job_strategy) {}
-  HRESULT DoUpdateCheck(const ProductDataVector& products) {
-    return worker_job()->DoUpdateCheck(products);
-  }
-  HRESULT DownloadJobs() {
-    return worker_job()->DownloadJobs();
-  }
- private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(OnlineStrategy);
-};
-
-class OfflineStrategy : public NetworkStrategy {
- public:
-  explicit OfflineStrategy(WorkerJobStrategy* worker_job_strategy)
-      : NetworkStrategy(worker_job_strategy) {}
-  HRESULT DoUpdateCheck(const ProductDataVector& products) {
-    return worker_job()->CreateOfflineJobs(products);
-  }
-  HRESULT DownloadJobs() {
-    return S_OK;
-  }
- private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(OfflineStrategy);
-};
-
-}  // namespace
-
-WorkerJobStrategy::WorkerJobStrategy(bool is_machine,
-                                     const CString& lang)
-    : is_machine_(is_machine),
-      language_(lang),
-      worker_job_(NULL) {
-}
-
-HRESULT WorkerJobStrategy::ProcessApps() {
-  HRESULT hr = network_strategy()->DownloadJobs();
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return worker_job()->InstallJobs();
-}
-
-HRESULT WorkerJobStrategy::DoUpdateCheck(const ProductDataVector& products) {
-  return network_strategy()->DoUpdateCheck(products);
-}
-
-HRESULT WorkerJobStrategy::PostInstall() {
-  return S_OK;
-}
-
-HRESULT UpdateAppsStrategy::PreUpdateCheck(ProductDataVector* products) {
-  ASSERT1(products);
-  ASSERT1(worker_job());
-
-  HRESULT hr = PingUninstalledProducts();
-  if (FAILED(hr)) {
-    CORE_LOG(LW, (_T("[PingUninstalledApps failed][0x%08x]"), hr));
-  }
-
-  // Fill in products from the registered apps database.
-  AppManager app_manager(is_machine());
-  if (!app_manager.ShouldCheckForUpdates()) {
-    OPT_LOG(L1, (_T("[Update check not needed at this time]")));
-    return S_OK;
-  }
-
-  hr = app_manager.GetRegisteredProducts(products);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-#ifdef _DEBUG
-  for (size_t i = 0; i < products->size(); ++i) {
-    const GUID& app_guid = (*products)[i].app_data().app_guid();
-    const CString client_state_key_path =
-        goopdate_utils::GetAppClientStateKey(is_machine(),
-                                             GuidToString(app_guid));
-    ASSERT(RegKey::HasKey(client_state_key_path),
-           (_T("[App Clients key does not have matching ClientState key][%s]"),
-            GuidToString(app_guid)));
-  }
-#endif
-
-  if (args_.install_source.IsEmpty()) {
-    return S_OK;
-  }
-
-  for (size_t i = 0; i < products->size(); ++i) {
-    AppData app_data = (*products)[i].app_data();
-    app_data.set_install_source(args_.install_source);
-    (*products)[i].set_app_data(app_data);
-  }
-
-  return S_OK;
-}
-
-// Not being able to update one or more apps is not an error as long as there is
-// at least one app to update. This should always be the case because Omaha can
-// always be updated.
-HRESULT UpdateAppsStrategy::RemoveDisallowedApps(ProductDataVector* products) {
-  ASSERT1(products);
-  RemoveAppsIfUpdateDisallowedByEula(products);
-  if (products->empty()) {
-    return GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED;
-  }
-
-  DisableUpdateForAppsDisallowedByUpdateGroupPolicy(
-      false,
-      products,
-      &first_disallowed_app_name_);
-  if (products->empty()) {
-    return GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY;
-  }
-
-  return S_OK;
-}
-
-HRESULT UpdateAppsStrategy::DoUpdateCheck(const ProductDataVector& products) {
-  HRESULT hr = SendInstalledByOemPing(products);
-  if (FAILED(hr)) {
-    CORE_LOG(LW, (_T("[SendInstalledByOemPing failed][0x%08x]"), hr));
-  }
-
-  return WorkerJobStrategy::DoUpdateCheck(products);
-}
-
-// Updates the LastChecked value; called only after successful update checks.
-HRESULT UpdateAppsStrategy::PostUpdateCheck() {
-  AppManager app_manager(is_machine());
-  HRESULT hr = app_manager.UpdateLastChecked();
-
-  if (FAILED(hr)) {
-    CORE_LOG(LW, (_T("[UpdateLastChecked failed][0x%08x]"), hr));
-  }
-  return S_OK;
-}
-
-HRESULT UpdateAppsStrategy::PingUninstalledProducts() const {
-  CORE_LOG(L2, (_T("[UpdateAppsStrategy::PingUninstalledProducts]")));
-  ASSERT1(worker_job()->ping());
-
-  scoped_ptr<Request> uninstall_ping;
-  HRESULT hr = BuildUninstallPing(is_machine(), address(uninstall_ping));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  if (!uninstall_ping->get_request_count()) {
-    return S_OK;
-  }
-
-  if (worker_job()->is_canceled()) {
-    return GOOPDATE_E_WORKER_CANCELLED;
-  }
-
-  CORE_LOG(L2, (_T("[Sending uninstall ping]")));
-  return worker_job()->ping()->SendPing(uninstall_ping.get());
-}
-
-HRESULT UpdateAppsStrategy::SendInstalledByOemPing(
-    const ProductDataVector& products) const {
-  CORE_LOG(L2, (_T("[UpdateAppsStrategy::SendInstalledByOemPing()]")));
-  ASSERT1(!products.empty());
-
-  Request request(is_machine());
-
-  for (size_t i = 0; i < products.size(); ++i) {
-    const ProductData& product_data = products[i];
-    const AppData& app_data = product_data.app_data();
-    if (app_data.is_oem_install()) {
-      ASSERT1(!::IsEqualGUID(kGoopdateGuid, app_data.app_guid()));
-      AppRequestData app_request_data(app_data);
-      PingEvent ping_event(PingEvent::EVENT_INSTALL_OEM_FIRST_CHECK,
-                           PingEvent::EVENT_RESULT_SUCCESS,
-                           0,  // error code
-                           0,  // extra code 1
-                           app_data.previous_version());
-      app_request_data.AddPingEvent(ping_event);
-      AppRequest app_request(app_request_data);
-      request.AddAppRequest(app_request);
-    }
-  }
-
-  if (!request.get_request_count()) {
-    return S_OK;
-  }
-
-  if (worker_job()->is_canceled()) {
-    return GOOPDATE_E_WORKER_CANCELLED;
-  }
-
-  HRESULT hr = worker_job()->ping()->SendPing(&request);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  AppManager app_manager(is_machine());
-  for (size_t i = 0; i < products.size(); ++i) {
-    const ProductData& product_data = products[i];
-    const AppData& app_data = product_data.app_data();
-    if (app_data.is_oem_install()) {
-      app_manager.ClearOemInstalled(app_data.parent_app_guid(),
-                                    app_data.app_guid());
-    }
-  }
-
-  return S_OK;
-}
-
-InstallAppsStrategy::InstallAppsStrategy(bool is_machine,
-                                         const CommandLineArgs& args)
-    : WorkerJobStrategy(is_machine, args.extra.language),
-      args_(args) {
-}
-
-HRESULT InstallAppsStrategy::PreUpdateCheck(ProductDataVector* products) {
-  ASSERT1(products);
-  ASSERT1(worker_job());
-  AppManager app_manager(is_machine());
-  app_manager.ConvertCommandLineToProductData(args_, products);
-
-  return S_OK;
-}
-
-HRESULT InstallAppsStrategy::RemoveDisallowedApps(ProductDataVector* products) {
-  if (RemoveAppsDisallowedByInstallGroupPolicy(products,
-                                               &first_disallowed_app_name_)) {
-    return GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY;
-  }
-
-  return S_OK;
-}
-
-HRESULT InstallAppsStrategy::PostUpdateCheck() {
-  return S_OK;
-}
-
-HRESULT InstallAppsStrategy::PostInstall() {
-  // TODO(omaha): Maybe move to UI class.
-  if (args_.is_silent_set) {
-    return S_OK;
-  }
-
-  const Jobs& jobs = worker_job()->jobs();
-  if (jobs.empty()) {
-    return S_OK;
-  }
-
-  Job* primary_job = jobs[0];
-
-  // This is a temporary workaround for Earth-Chrome bundles until we implement
-  // better bundle support. Chrome is installed last and is thus the
-  // "primary app", but we want to launch Earth so specify it as primary here.
-  // Earth has two GUIDs that must be supported.
-  // Using StringToGuid below ensure there is no mismatch in upper/lower case.
-  // Note that Chrome's onsuccess value is still used to determine whether to
-  // close the UI.
-  const TCHAR* const kChromeGuid = _T("{8A69D345-D564-463C-AFF1-A69D9E530F96}");
-  const TCHAR* const kEarthMachineGuid =
-      _T("{74AF07D8-FB8F-4D51-8AC7-927721D56EBB}");
-  const TCHAR* const kEarthUserGuid =
-      _T("{0A52903D-0FBF-439A-93E4-CB609A2F63DB}");
-
-  if (jobs.size() == 2 &&
-      jobs[0]->app_data().app_guid() == StringToGuid(kChromeGuid) &&
-      (jobs[1]->app_data().app_guid() == StringToGuid(kEarthMachineGuid) ||
-       jobs[1]->app_data().app_guid() == StringToGuid(kEarthUserGuid))) {
-    primary_job = jobs[1];
-  }
-
-  ASSERT1(primary_job);
-  VERIFY1(SUCCEEDED(primary_job->LaunchCmdLine()));
-
-  return S_OK;
-}
-
-bool InstallAppsStrategy::ShouldLaunchBrowserOnUpdateCheckError() const {
-  return !args_.is_silent_set;
-}
-
-HRESULT InstallGoopdateAndAppsStrategy::PreUpdateCheck(
-    ProductDataVector* products) {
-  ASSERT1(products);
-  HRESULT hr = InstallAppsStrategy::PreUpdateCheck(products);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = FinishGoogleUpdateInstall(args_,
-                                 is_machine(),
-                                 false,
-                                 worker_job()->ping(),
-                                 job_observer_);
-  if (FAILED(hr)) {
-    CORE_LOG(LE, (_T("[FinishGoogleUpdateInstall failed][0x%08x]"), hr));
-    return hr;
-  }
-  return S_OK;
-}
-
-HRESULT OnDemandUpdateStrategy::InitializeDecorator(
-    IJobObserver* observer,
-    WorkerComWrapperShutdownCallBack* call_back) {
-  CORE_LOG(L2, (_T("[OnDemandUpdateStrategy::InitializeDecorator]")));
-  HRESULT hr =
-      CComObject<JobObserverCOMDecorator>::CreateInstance(&job_observer_com_);
-  ASSERT(SUCCEEDED(hr),
-      (_T("[JobObserverCOMDecorator CreateInstance returned 0x%x]"), hr));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  job_observer_com_scoped_holder_ = job_observer_com_;
-  job_observer_com_->Initialize(observer, call_back);
-  return S_OK;
-}
-
-HRESULT OnDemandUpdateStrategy::Init(
-    GUID guid,
-    IJobObserver* observer,
-    WorkerComWrapperShutdownCallBack* call_back) {
-  CORE_LOG(L3, (_T("[OnDemandUpdateStrategy::Init][%s]"), GuidToString(guid)));
-  AppManager app_manager(is_machine());
-  ProductData product_data;
-
-  HRESULT hr = app_manager.ReadProductDataFromStore(guid, &product_data);
-  if (FAILED(hr)) {
-    ASSERT1(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr);
-    return GOOPDATE_E_APP_NOT_REGISTERED;
-  }
-  if (product_data.app_data().is_uninstalled()) {
-    return GOOPDATE_E_APP_UNINSTALLED;
-  }
-
-  AppData app_data = product_data.app_data();
-  app_data.set_install_source(is_update_check_only_ ?
-                              kCmdLineInstallSource_OnDemandCheckForUpdate :
-                              kCmdLineInstallSource_OnDemandUpdate);
-  app_data.set_previous_version(app_data.version());
-  product_data.set_app_data(app_data);
-
-  products_.push_back(product_data);
-
-  hr = InitializeDecorator(observer, call_back);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  return S_OK;
-}
-
-JobObserver* OnDemandUpdateStrategy::GetJobObserver() const {
-  return job_observer_com_;
-}
-
-HRESULT OnDemandUpdateStrategy::PreUpdateCheck(ProductDataVector* products) {
-  ASSERT1(products);
-  ASSERT1(worker_job());
-  ASSERT1(!products_.empty());
-  *products = products_;
-  return S_OK;
-}
-
-HRESULT OnDemandUpdateStrategy::RemoveDisallowedApps(
-    ProductDataVector* products) {
-  if (RemoveAppsIfUpdateDisallowedByEula(products)) {
-    return GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED;
-  }
-  if (DisableUpdateForAppsDisallowedByUpdateGroupPolicy(
-          true,
-          products,
-          &first_disallowed_app_name_)) {
-    return GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY;
-  }
-
-  return S_OK;
-}
-
-HRESULT OnDemandUpdateStrategy::PostUpdateCheck() {
-  return S_OK;
-}
-
-HRESULT OnDemandUpdateStrategy::ProcessApps() {
-  HRESULT hr = S_OK;
-  if (!is_update_check_only_ && !worker_job()->jobs().empty()) {
-    // Only call process apps if we have been asked to perform
-    // an update(and not just an update check), and an update is
-    // available.
-    return WorkerJobStrategy::ProcessApps();
-  } else {
-    const TCHAR* const text = _T("Update Check Completed");
-    worker_job()->NotifyCompleted(COMPLETION_SUCCESS, S_OK, text, products_);
-    worker_job()->CompleteAllNonCompletedJobs(COMPLETION_SUCCESS, S_OK, text);
-  }
-
-  return hr;
-}
-
-// job_observer can be NULL.
-// TODO(omaha): Remove job_observer parameter after unifying Setup.
-WorkerJobStrategy* WorkerJobStrategyFactory::CreateInstallStrategy(
-    bool is_machine,
-    const CommandLineArgs& args,
-    JobObserver* job_observer) {
-  WorkerJobStrategy* strategy(NULL);
-
-#pragma warning(push)
-// C4061: enumerator 'xxx' in switch of enum 'yyy' is not explicitly handled by
-// a case label.
-#pragma warning(disable : 4061)
-  switch (args.mode) {
-    // TODO(omaha): Remove Install mode after unifying Setup. It is only here
-    // to support error reporting from DisplaySetupError().
-    case COMMANDLINE_MODE_INSTALL:
-    case COMMANDLINE_MODE_IG:
-      strategy = new InstallGoopdateAndAppsStrategy(is_machine,
-                                                    args,
-                                                    job_observer);
-      break;
-    case COMMANDLINE_MODE_HANDOFF_INSTALL:
-      strategy = new InstallAppsStrategy(is_machine, args);
-      break;
-    default:
-      ASSERT1(false);
-  }
-#pragma warning(pop)
-
-  NetworkStrategy* network_strategy(NULL);
-  if (args.is_offline_set) {
-    network_strategy = new OfflineStrategy(strategy);
-  } else {
-    network_strategy = new OnlineStrategy(strategy);
-  }
-
-  strategy->set_network_strategy(network_strategy);
-
-  return strategy;
-}
-
-WorkerJobStrategy* WorkerJobStrategyFactory::CreateUpdateAppsStrategy(
-    bool is_machine, const CommandLineArgs& args) {
-  WorkerJobStrategy* strategy = new UpdateAppsStrategy(is_machine, args);
-  strategy->set_network_strategy(new OnlineStrategy(strategy));
-  return strategy;
-}
-
-OnDemandUpdateStrategy* WorkerJobStrategyFactory::CreateOnDemandStrategy(
-    bool is_update_check_only,
-    const CString& lang,
-    bool is_machine) {
-  OnDemandUpdateStrategy* strategy =
-      new OnDemandUpdateStrategy(is_update_check_only, lang, is_machine);
-  strategy->set_network_strategy(new OnlineStrategy(strategy));
-  return strategy;
-}
-
-}  // namespace omaha
diff --git a/worker/worker_job_strategy.h b/worker/worker_job_strategy.h
deleted file mode 100644
index f6715d1..0000000
--- a/worker/worker_job_strategy.h
+++ /dev/null
@@ -1,211 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#ifndef OMAHA_WORKER_WORKER_JOB_STRATEGY_H__
-#define OMAHA_WORKER_WORKER_JOB_STRATEGY_H__
-
-#include <windows.h>
-#include <atlstr.h>
-#include "base/scoped_ptr.h"
-#include "goopdate/google_update_idl.h"
-#include "omaha/common/debug.h"
-#include "omaha/common/logging.h"
-#include "omaha/goopdate/command_line.h"
-#include "omaha/worker/com_wrapper_shutdown_handler.h"
-#include "omaha/worker/product_data.h"
-
-namespace omaha {
-
-class JobObserver;
-class JobObserverCOMDecorator;
-class NetworkStrategy;
-class Ping;
-class WorkerJob;
-struct CommandLineArgs;
-
-class WorkerJobStrategy {
- public:
-  WorkerJobStrategy(bool is_machine, const CString& lang);
-  virtual ~WorkerJobStrategy() {}
-
-  void set_worker_job(WorkerJob* worker_job) {
-    ASSERT1(worker_job);
-    worker_job_ = worker_job;
-  }
-
-  virtual HRESULT PreUpdateCheck(ProductDataVector* products) = 0;
-  virtual HRESULT RemoveDisallowedApps(ProductDataVector* products) = 0;
-  virtual HRESULT DoUpdateCheck(const ProductDataVector& products);
-  virtual HRESULT PostUpdateCheck() = 0;
-  virtual HRESULT ProcessApps();
-  virtual HRESULT PostInstall();
-
-  virtual bool ShouldFailOnUpdateNotAvailable() const { return false; }
-  virtual bool IsAutoUpdate() const { return false; }
-  virtual bool IsUpdate() const { return false; }
-  virtual bool IsUpdateCheckOnly() const { return false; }
-  virtual bool ShouldLaunchBrowserOnUpdateCheckError() const { return false; }
-
-  const CString language() const { return language_; }
-  bool is_machine() const { return is_machine_; }
-  CString first_disallowed_app_name() const {
-      return first_disallowed_app_name_;
-  }
-
- protected:
-  WorkerJob* worker_job() const { return worker_job_; }
-  NetworkStrategy* network_strategy() const { return network_strategy_.get(); }
-  virtual void set_network_strategy(NetworkStrategy* network_strategy) {
-    network_strategy_.reset(network_strategy);
-  }
-  CString first_disallowed_app_name_;
-
- private:
-  bool is_machine_;
-  CString language_;
-  WorkerJob* worker_job_;
-  scoped_ptr<NetworkStrategy> network_strategy_;
-
-  friend class WorkerJobStrategyFactory;
-  friend class NetworkStrategy;
-
-  DISALLOW_EVIL_CONSTRUCTORS(WorkerJobStrategy);
-};
-
-class UpdateAppsStrategy : public WorkerJobStrategy {
- public:
-  virtual HRESULT PreUpdateCheck(ProductDataVector* products);
-  virtual HRESULT RemoveDisallowedApps(ProductDataVector* products_);
-  virtual HRESULT DoUpdateCheck(const ProductDataVector& products);
-  virtual HRESULT PostUpdateCheck();
-
-  virtual bool IsAutoUpdate() const { return true; }
-  virtual bool IsUpdate() const { return true; }
-
- private:
-  explicit UpdateAppsStrategy(bool is_machine, const CommandLineArgs& args)
-      : WorkerJobStrategy(is_machine, args.extra.language),
-        args_(args) {
-  }
-  virtual HRESULT PingUninstalledProducts() const;
-
-  // Sends an "install by OEM" ping for any of the products that were installed
-  // by an OEM. If the ping is sent successfully, the OEM install flag is
-  // deleted so the ping will not be sent again.
-  virtual HRESULT SendInstalledByOemPing(
-      const ProductDataVector& products) const;
-
-  const CommandLineArgs& args_;
-
-  friend class WorkerJobStrategyFactory;
-};
-
-class InstallAppsStrategy : public WorkerJobStrategy {
- public:
-  virtual HRESULT PreUpdateCheck(ProductDataVector* products);
-  virtual HRESULT RemoveDisallowedApps(ProductDataVector* products_);
-  virtual HRESULT PostUpdateCheck();
-  virtual HRESULT PostInstall();
-
-  virtual bool ShouldFailOnUpdateNotAvailable() const { return true; }
-  virtual bool ShouldLaunchBrowserOnUpdateCheckError() const;
-
- protected:
-  InstallAppsStrategy(bool is_machine,
-                      const CommandLineArgs& args);
-  const CommandLineArgs& args_;
-
-  friend class WorkerJobStrategyFactory;
-};
-
-class InstallGoopdateAndAppsStrategy : public InstallAppsStrategy {
- public:
-  virtual HRESULT PreUpdateCheck(ProductDataVector* products);
-
- private:
-  InstallGoopdateAndAppsStrategy(bool is_machine,
-                                 const CommandLineArgs& args,
-                                 JobObserver* job_observer)
-      : InstallAppsStrategy(is_machine, args),
-        job_observer_(job_observer) {}
-  JobObserver* job_observer_;
-
-  friend class WorkerJobStrategyFactory;
-};
-
-class OnDemandUpdateStrategy : public WorkerJobStrategy {
- public:
-  HRESULT Init(GUID guid,
-               IJobObserver* observer,
-               WorkerComWrapperShutdownCallBack* call_back);
-  virtual JobObserver* GetJobObserver() const;
-  virtual HRESULT PreUpdateCheck(ProductDataVector* products);
-  virtual HRESULT RemoveDisallowedApps(ProductDataVector* products_);
-  virtual HRESULT PostUpdateCheck();
-  virtual HRESULT ProcessApps();
-
-  virtual bool ShouldFailOnUpdateNotAvailable() const { return true; }
-  virtual bool IsUpdate() const { return true; }
-  virtual bool IsUpdateCheckOnly() const { return is_update_check_only_; }
-
- private:
-  OnDemandUpdateStrategy(bool is_update_check_only,
-                         const CString& lang,
-                         bool is_machine)
-      : WorkerJobStrategy(is_machine, lang),
-        is_update_check_only_(is_update_check_only),
-        job_observer_com_(NULL) {}
-  HRESULT InitializeDecorator(IJobObserver* observer,
-                              WorkerComWrapperShutdownCallBack* call_back);
-
-  bool is_update_check_only_;
-  CComObject<JobObserverCOMDecorator>* job_observer_com_;
-  CComPtr<IUnknown> job_observer_com_scoped_holder_;
-  ProductDataVector products_;
-
-  friend class WorkerJobStrategyFactory;
-};
-
-class WorkerJobStrategyFactory {
- public:
-  static WorkerJobStrategy* CreateInstallStrategy(bool is_machine,
-                                                  const CommandLineArgs& args,
-                                                  JobObserver* job_observer);
-  static WorkerJobStrategy* CreateUpdateAppsStrategy(
-      bool is_machine, const CommandLineArgs& args);
-  static OnDemandUpdateStrategy* CreateOnDemandStrategy(bool update_check_only,
-                                                        const CString& lang,
-                                                        bool is_machine);
-};
-
-class NetworkStrategy {
- public:
-  virtual ~NetworkStrategy() {}
-  virtual HRESULT DoUpdateCheck(const ProductDataVector& products) = 0;
-  virtual HRESULT DownloadJobs() = 0;
- protected:
-  explicit NetworkStrategy(WorkerJobStrategy* worker_job_strategy)
-      : worker_job_strategy_(worker_job_strategy) {}
-  WorkerJob* worker_job() {
-    return worker_job_strategy_->worker_job();
-  }
-
-  WorkerJobStrategy* worker_job_strategy_;
-};
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_WORKER_JOB_STRATEGY_H__
diff --git a/worker/worker_job_unittest.cc b/worker/worker_job_unittest.cc
deleted file mode 100644
index 9222741..0000000
--- a/worker/worker_job_unittest.cc
+++ /dev/null
@@ -1,1904 +0,0 @@
-// Copyright 2008-2010 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include <atlbase.h>
-#include <msxml2.h>
-#include "omaha/common/app_util.h"
-#include "omaha/common/error.h"
-#include "omaha/common/omaha_version.h"
-#include "omaha/common/path.h"
-#include "omaha/common/process.h"
-#include "omaha/common/scoped_ptr_address.h"
-#include "omaha/goopdate/config_manager.h"
-#include "omaha/goopdate/const_goopdate.h"
-#include "omaha/goopdate/goopdate_utils.h"
-#include "omaha/goopdate/request.h"
-#include "omaha/goopdate/stats_uploader.h"
-#include "omaha/net/cup_request.h"
-#include "omaha/net/simple_request.h"
-#include "omaha/testing/unit_test.h"
-#include "omaha/worker/i_job_observer_mock.h"
-#include "omaha/worker/job_creator.h"
-#include "omaha/worker/job_observer_mock.h"
-#include "omaha/worker/ping_event.h"
-#include "omaha/worker/ping_mock.h"
-#include "omaha/worker/worker_job.h"
-#include "omaha/worker/worker_job_strategy.h"
-#include "omaha/worker/worker_metrics.h"
-
-CComModule module;
-
-namespace omaha {
-
-namespace {
-
-#define APP_GUID _T("{2D8F7DCC-86F9-464c-AF80-B986B551927B}")
-#define APP_GUID2 _T("{7234E9E5-3870-4561-9533-B1D91696A8BA}")
-#define APP_GUID3 _T("{5F5FC3BC-A40E-4dfc-AE0B-FD039A343EE8}")
-const TCHAR* const kAppGuid = APP_GUID;
-const TCHAR* const kAppGuid2 = APP_GUID2;
-const TCHAR* const kAppGuid3 = APP_GUID3;
-
-const TCHAR* const kPolicyKey =
-    _T("HKLM\\Software\\Policies\\Google\\Update\\");
-const TCHAR* const kInstallPolicyApp = _T("Install") APP_GUID;
-const TCHAR* const kUpdatePolicyApp = _T("Update") APP_GUID;
-const TCHAR* const kInstallPolicyApp2 = _T("Install") APP_GUID2;
-const TCHAR* const kUpdatePolicyApp2 = _T("Update") APP_GUID2;
-const TCHAR* const kInstallPolicyApp3 = _T("Install") APP_GUID3;
-const TCHAR* const kUpdatePolicyApp3 = _T("Update") APP_GUID3;
-const TCHAR* const kUpdatePolicyGoopdate = _T("Update") GOOPDATE_APP_ID;
-
-const TCHAR* const kMachineClientStatePathApp =
-    _T("HKLM\\Software\\Google\\Update\\ClientState\\") APP_GUID;
-const TCHAR* const kMachineClientStatePathApp2 =
-    _T("HKLM\\Software\\Google\\Update\\ClientState\\") APP_GUID2;
-const TCHAR* const kMachineClientStateMediumPathApp =
-    _T("HKLM\\Software\\Google\\Update\\ClientStateMedium\\") APP_GUID;
-
-const CString handoff_cmd_line(_T("/handoff /lang en"));
-const CString finish_setup_cmd_line(_T("/ig /lang en"));
-const CString false_extra_args(
-    _T("\"appguid={2D8F7DCC-86F9-464c-AF80-B986B551927B}")
-    _T("&appname=FooBar&needsadmin=False\""));
-const CString true_extra_args(
-    _T("\"appguid={2D8F7DCC-86F9-464c-AF80-B986B551927B}")
-    _T("&appname=FooBar&needsadmin=True\""));
-
-// Helper to write policies to the registry. Eliminates ambiguity of which
-// overload of SetValue to use without the need for static_cast.
-HRESULT SetPolicy(const TCHAR* policy_name, DWORD value) {
-  return RegKey::SetValue(kPolicyKey, policy_name, value);
-}
-
-// Returns E_FAIL from SendPing().
-class PingMockFail : public PingMock {
- public:
-  virtual HRESULT SendPing(Request* req) {
-    VERIFY1(SUCCEEDED(PingMock::SendPing(req)));
-    return E_FAIL;
-  }
-};
-
-// Records the last request buffer and if a request has been sent.
-class MockRequestSave : public SimpleRequest {
- public:
-  MockRequestSave() : is_sent_(false) {}
-
-  // Sets is_sent and performs the send.
-  virtual HRESULT Send() {
-    is_sent_ = true;
-    return SimpleRequest::Send();
-  }
-
-  // Assumes the buffer is valid UTF-8.
-  virtual void set_request_buffer(const void* buffer, size_t buffer_length) {
-    sent_request_utf8_.SetString(static_cast<const char*>(buffer),
-                                 buffer_length);
-    SimpleRequest::set_request_buffer(buffer, buffer_length);
-  }
-
-  const CStringA& sent_request_utf8() const { return sent_request_utf8_; }
-  bool is_sent() const { return is_sent_; }
-
- private:
-  CStringA sent_request_utf8_;
-  bool is_sent_;
-};
-
-
-// Performs the Send(), but always returns true and a response with "noupdate"
-// for the specified apps.
-// The actual return value of Send() is inconsistent behavior due to HKLM being
-// overridden because DNS lookup may or may not fail depending on earlier tests.
-class MockRequestSaveNoUpdate : public MockRequestSave {
- public:
-  explicit MockRequestSaveNoUpdate(
-      const std::vector<CString>& expected_app_guids)
-      : expected_app_guids_(expected_app_guids) {
-  }
-
-  virtual HRESULT Send() {
-    MockRequestSave::Send();
-    return S_OK;
-  }
-
-  virtual int GetHttpStatusCode() const {
-    return 200;
-  }
-
-  virtual std::vector<uint8> GetResponse() const {
-    CStringA no_update_response =
-        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
-        "<gupdate xmlns=\"http://www.google.com/update2/response\" "
-        "protocol=\"2.0\">";
-    for (size_t i = 0; i < expected_app_guids_.size(); ++i) {
-      no_update_response.Append("<app appid=\"");
-      no_update_response.Append(WideToAnsiDirect(expected_app_guids_[i]));
-      no_update_response.Append(
-        "\" status=\"ok\">"
-        "  <updatecheck status=\"noupdate\"/><ping status=\"ok\"/>"
-        "</app>");
-    }
-    no_update_response.Append("</gupdate>");
-
-    const size_t response_length = strlen(no_update_response);
-    std::vector<uint8> response;
-    response.resize(response_length);
-    EXPECT_EQ(0, memcpy_s(&response[0],
-                          response_length,
-                          no_update_response,
-                          response_length));
-    return response;
-  }
-
- private:
-  std::vector<CString> expected_app_guids_;
-
-  DISALLOW_IMPLICIT_CONSTRUCTORS(MockRequestSaveNoUpdate);
-};
-
-// Returns kUpdateCheckForcedFailure from Send().
-class MockRequestSaveFail : public MockRequestSave {
- public:
-  // Performs the send and fails with kUpdateCheckForcedFailure regardless of
-  // the result.
-  virtual HRESULT Send() {
-    MockRequestSave::Send();
-    return kUpdateCheckForcedFailure;
-  }
-
-  static const HRESULT kUpdateCheckForcedFailure = 0x81234567;
-};
-
-void VerifyUpdateCheckInRequest(const std::vector<CString>& expected_app_guids,
-                                const std::vector<CString>& disabled_app_guids,
-                                const CStringA& request_utf8,
-                                bool is_install,
-                                bool is_on_demand) {
-  EXPECT_NE(0, expected_app_guids.size());
-  const char* kOmahaRequestUpdate =
-      "<o:app appid=\"{430FD4D0-B729-4F61-AA34-91526481799D}\" "
-      "version=\"5.6.7.8\" lang=\"\" brand=\"\" client=\"\"><o:updatecheck/>"
-      "<o:ping r=\"-1\"/>"
-      "</o:app>";
-
-  // The 'c' in "464c" element of the app GUID is capitalized in the request.
-  const char* kAppRequestFormat=
-      "<o:app appid=\"%s\" "
-      "version=\"%s\" lang=\"\" brand=\"\" client=\"\"%s%s><o:updatecheck%s/>"
-      "%s</o:app>";
-
-  EXPECT_EQ(0, request_utf8.Find(
-      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
-      "<o:gupdate xmlns:o=\"http://www.google.com/update2/request\" "
-      "protocol=\"2.0\" version=\"1.2."));
-  EXPECT_NE(-1, request_utf8.Find("\" ismachine=\"1\" "));
-  EXPECT_NE(-1, request_utf8.Find("\" requestid=\"{"));
-  EXPECT_NE(-1, request_utf8.Find("}\"><o:os platform=\"win\" version=\""));
-  EXPECT_NE(-1, request_utf8.Find("\" sp=\""));
-  EXPECT_NE(-1, request_utf8.Find("\"/><o:app"));
-
-  // Verify the expected number of app elements.
-  int app_element_index = request_utf8.Find("<o:app");
-  int num_app_elements = 0;
-  while (-1 != app_element_index) {
-    num_app_elements++;
-    app_element_index = request_utf8.Find("<o:app", app_element_index + 1);
-  }
-  EXPECT_EQ(num_app_elements, expected_app_guids.size());
-
-  for (size_t i = 0; i < expected_app_guids.size(); ++i) {
-    const CString& expected_app = expected_app_guids[i];
-
-    bool is_disabled_expected = false;
-    for (size_t j = 0; j < disabled_app_guids.size(); ++j) {
-      if (expected_app == disabled_app_guids[j]) {
-        is_disabled_expected = true;
-        break;
-      }
-    }
-
-    if (expected_app == kGoogleUpdateAppId) {
-      ASSERT1(!is_on_demand && !is_install);
-      EXPECT_NE(-1, request_utf8.Find(kOmahaRequestUpdate));
-    } else {
-      ASSERT1(expected_app == kAppGuid || expected_app == kAppGuid2);
-      const CStringA app_guid = WideToAnsiDirect(expected_app == kAppGuid ?
-                                                 CString(kAppGuid).MakeUpper() :
-                                                 kAppGuid2);
-      CStringA expected_app_element;
-      expected_app_element.Format(
-          kAppRequestFormat,
-          app_guid,
-          is_install ? "" : "1.2.3.4",
-          is_install ? " installage=\"-1\"" : "",
-          is_on_demand ? " installsource=\"ondemandupdate\"" : "",
-          is_disabled_expected ? " updatedisabled=\"true\"" : "",
-          is_install ? "" : "<o:ping r=\"-1\"/>");
-      EXPECT_NE(-1, request_utf8.Find(expected_app_element)) <<
-          _T("Expected: ") <<
-          Utf8ToWideChar(expected_app_element.GetString(),
-                         expected_app_element.GetLength()).GetString() <<
-          std::endl << _T("In: ") <<
-          Utf8ToWideChar(request_utf8.GetString(),
-                         request_utf8.GetLength()).GetString() << std::endl;
-    }
-  }
-
-  EXPECT_NE(-1, request_utf8.Find("</o:app></o:gupdate>"));
-
-  EXPECT_FALSE(::testing::Test::HasFailure()) <<
-      _T("Actual Request: ") << request_utf8;
-}
-
-}  // namespace
-
-class WorkerJobTest : public testing::Test {
- protected:
-  WorkerJobTest()
-      : mock_network_request_(NULL),
-        mock_encryped_request_(NULL) {
-    exe_to_test_ = ConcatenatePath(
-        app_util::GetCurrentModuleDirectory(),
-        _T("unittest_support\\does_not_shutdown\\GoogleUpdate.exe"));
-  }
-
-  void SetIsMachine(bool is_machine) {
-    worker_job_->is_machine_ = is_machine;
-  }
-
-  bool IsAppInstallWorkerRunning() {
-    return goopdate_utils::IsAppInstallWorkerRunning(worker_job_->is_machine_);
-  }
-
-  PingEvent::Types GetPingType(const Request& request) {
-    const AppRequest& app_request = *(request.app_requests_begin());
-    const AppRequestData& app_request_data = app_request.request_data();
-    const PingEvent& ping_event = *(app_request_data.ping_events_begin());
-    return ping_event.event_type();
-  }
-
-  void SetWorkerJobPing(Ping* ping) {
-    worker_job_->ping_.reset(ping);
-  }
-
-  // Sets WorkerJob to use MockRequestSave for all types of requests.
-  void SetMockMockRequestSave() {
-    SetMockRequest(new MockRequestSave, new MockRequestSave);
-  }
-
-  // Sets WorkerJob to use MockRequestSaveNoUpdate for all types of requests.
-  void SetMockRequestSaveNoUpdate(
-      const std::vector<CString>& expected_app_guids) {
-    SetMockRequest(new MockRequestSaveNoUpdate(expected_app_guids),
-                   new MockRequestSaveNoUpdate(expected_app_guids));
-  }
-
-  // Sets WorkerJob to use MockRequestSaveFail for all types of requests.
-  void SetMockMockRequestSaveFail() {
-    SetMockRequest(new MockRequestSaveFail, new MockRequestSaveFail);
-  }
-
-  // Sets WorkerJob to use the specified HTTP request objects.
-  // Prevents multiple calls to the mock request by specifying the Config and
-  // avoiding auto-detection.
-  void SetMockRequest(MockRequestSave* normal_request,
-                      MockRequestSave* encryped_request) {
-    ASSERT1(worker_job_.get());
-    const NetworkConfig::Session& session(NetworkConfig::Instance().session());
-    const Config config;
-
-    mock_network_request_ = normal_request;
-    mock_encryped_request_ = encryped_request;
-
-    worker_job_->network_request_.reset(new NetworkRequest(session));
-    worker_job_->network_request_->set_network_configuration(&config);
-    worker_job_->network_request_->AddHttpRequest(normal_request);
-
-    worker_job_->network_request_encrypted_.reset(new NetworkRequest(session));
-    worker_job_->network_request_encrypted_->set_network_configuration(&config);
-    worker_job_->network_request_encrypted_->AddHttpRequest(
-        encryped_request);
-  }
-
-  void VerifyInstallUpdateCheck(
-      const std::vector<CString>& expected_app_guids) const {
-    ASSERT1(mock_network_request_ && mock_encryped_request_);
-    EXPECT_TRUE(mock_network_request_->is_sent());
-    VerifyUpdateCheckInRequest(expected_app_guids,
-                               std::vector<CString>(),
-                               mock_network_request_->sent_request_utf8(),
-                               true,
-                               false);
-    EXPECT_FALSE(mock_encryped_request_->is_sent());
-  }
-
-  void VerifyAutoUpdateCheckWithDisabledApps(
-      const std::vector<CString>& expected_app_guids,
-      const std::vector<CString>& disabled_app_guids) const {
-    ASSERT1(mock_network_request_ && mock_encryped_request_);
-    EXPECT_TRUE(mock_network_request_->is_sent());
-    VerifyUpdateCheckInRequest(expected_app_guids,
-                               disabled_app_guids,
-                               mock_network_request_->sent_request_utf8(),
-                               false,
-                               false);
-    EXPECT_FALSE(mock_encryped_request_->is_sent());
-  }
-
-  void VerifyAutoUpdateCheck(
-      const std::vector<CString>& expected_app_guids) const {
-      VerifyAutoUpdateCheckWithDisabledApps(expected_app_guids,
-                                            std::vector<CString>());
-  }
-
-  void VerifyOnDemandUpdateCheck(
-      const std::vector<CString>& expected_app_guids) const {
-    ASSERT1(mock_network_request_ && mock_encryped_request_);
-    EXPECT_TRUE(mock_network_request_->is_sent());
-    VerifyUpdateCheckInRequest(expected_app_guids,
-                               std::vector<CString>(),
-                               mock_network_request_->sent_request_utf8(),
-                               false,
-                               true);
-    EXPECT_FALSE(mock_encryped_request_->is_sent());
-  }
-
-  void VerifyNoUpdateCheckSent() const {
-    ASSERT1(mock_network_request_ && mock_encryped_request_);
-    EXPECT_FALSE(mock_network_request_->is_sent());
-    EXPECT_FALSE(mock_encryped_request_->is_sent());
-  }
-
-  scoped_ptr<WorkerJob> worker_job_;
-  CString exe_to_test_;
-  MockRequestSave* mock_network_request_;
-  MockRequestSave* mock_encryped_request_;
-};
-
-class WorkerJobRegistryProtectedTest : public WorkerJobTest {
- protected:
-  WorkerJobRegistryProtectedTest()
-      : hive_override_key_name_(kRegistryHiveOverrideRoot), xml_cookie_(0) {
-  }
-
-  virtual void SetUp() {
-    // Registers the MSXML CoClass before doing registry redirection. Without
-    // this registration, CoCreation of the DOMDocument2 within the test would
-    // fail on Vista, as a side-effect of the registry redirection.
-    CComPtr<IClassFactory> factory;
-    EXPECT_SUCCEEDED(::CoGetClassObject(__uuidof(DOMDocument2),
-                                        CLSCTX_INPROC_SERVER,
-                                        NULL,
-                                        IID_IClassFactory,
-                                        reinterpret_cast<void**>(&factory)));
-    EXPECT_SUCCEEDED(::CoRegisterClassObject(__uuidof(DOMDocument2),
-                                             factory,
-                                             CLSCTX_INPROC_SERVER,
-                                             REGCLS_MULTIPLEUSE,
-                                             &xml_cookie_));
-    RegKey::DeleteKey(hive_override_key_name_, true);
-    OverrideRegistryHives(hive_override_key_name_);
-  }
-
-  virtual void TearDown() {
-    RestoreRegistryHives();
-    EXPECT_SUCCEEDED(RegKey::DeleteKey(hive_override_key_name_, true));
-    if (xml_cookie_) {
-      EXPECT_SUCCEEDED(::CoRevokeClassObject(xml_cookie_));
-    }
-  }
-
-  CString hive_override_key_name_;
-  DWORD xml_cookie_;
-};
-
-class WorkerJobDoProcessTest : public WorkerJobRegistryProtectedTest {
- protected:
-  // Must be re-entrant for statics because it is called once for each subclass.
-  static void SetUpTestCase() {
-    // Initialize the global metrics collection.
-    stats_report::g_global_metrics.Initialize();
-
-    if (app_guids_omaha_.empty()) {
-      app_guids_omaha_.push_back(kGoogleUpdateAppId);
-    }
-
-    if (app_guids_omaha_app1_.empty()) {
-      app_guids_omaha_app1_ = app_guids_omaha_;
-      app_guids_omaha_app1_.push_back(kAppGuid);
-    }
-
-    if (app_guids_omaha_app2_.empty()) {
-      app_guids_omaha_app2_ = app_guids_omaha_;
-      app_guids_omaha_app2_.push_back(kAppGuid2);
-    }
-
-    if (app_guids_omaha_app1_app2_.empty()) {
-      app_guids_omaha_app1_app2_ = app_guids_omaha_app1_;
-      app_guids_omaha_app1_app2_ .push_back(kAppGuid2);
-    }
-
-    if (app_guids_app1_.empty()) {
-      app_guids_app1_.push_back(kAppGuid);
-    }
-
-    if (app_guids_app2_.empty()) {
-      app_guids_app2_.push_back(kAppGuid2);
-    }
-  }
-
-  static void TearDownTestCase() {
-    // The global metrics collection must be uninitialized before the metrics
-    // destructors are called.
-    stats_report::g_global_metrics.Uninitialize();
-  }
-
-  virtual void SetUp() {
-    WorkerJobRegistryProtectedTest::SetUp();
-#ifdef _DEBUG
-    // There is an assert that expects this value to be set.
-    ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                      kRegValueInstalledVersion,
-                                      GetVersionString()));
-#endif
-    metric_worker_apps_not_updated_eula.Set(0);
-    metric_worker_apps_not_updated_group_policy.Set(0);
-    metric_worker_apps_not_installed_group_policy.Set(0);
-  }
-
-  // These tests cause the action to fail. This method validates the app request
-  // for an individual app generated by
-  // ping_utils::SendCompletedPingsForAllProducts().
-  // The expected ping data depends on whether the WorkerJob was canceled.
-  static void ValidateCompletedPingForProduct(const AppRequest& app_request,
-                                              const GUID& expected_app_id,
-                                              bool is_canceled) {
-    const PingEvent& ping_event = GetSingleEventFromAppRequest(app_request,
-                                                               expected_app_id,
-                                                               true);
-    EXPECT_EQ(PingEvent::EVENT_UPDATE_COMPLETE, ping_event.event_type());
-    EXPECT_EQ(is_canceled ? PingEvent::EVENT_RESULT_CANCELLED :
-                            PingEvent::EVENT_RESULT_ERROR,
-              ping_event.event_result());
-    EXPECT_EQ(is_canceled ? GOOPDATE_E_WORKER_CANCELLED :
-                            MockRequestSaveFail::kUpdateCheckForcedFailure,
-              ping_event.error_code());
-    EXPECT_EQ(0x100000ff, ping_event.extra_code1());
-
-    EXPECT_EQ(::IsEqualGUID(kGoopdateGuid, expected_app_id) ? _T("5.6.7.8") :
-                                                              _T("1.2.3.4"),
-              ping_event.previous_version());
-  }
-
-  // TODO(omaha): I think this is a bug that we ping without an event.
-  // If not, fix this and replace this method with a check for no pings.
-  static void ValidateNoPingEventForProduct(const AppRequest& app_request,
-                                            const GUID& expected_app_id) {
-    const AppRequestData& app_request_data = app_request.request_data();
-
-    const AppData& app_data = app_request_data.app_data();
-    EXPECT_TRUE(::IsEqualGUID(expected_app_id, app_data.app_guid()));
-    EXPECT_TRUE(::IsEqualGUID(GUID_NULL, app_data.parent_app_guid()));
-    EXPECT_EQ(true, app_data.is_machine_app());
-    EXPECT_TRUE(!app_data.version().IsEmpty());
-    EXPECT_TRUE(!app_data.previous_version().IsEmpty());
-
-    EXPECT_EQ(0, app_request_data.num_ping_events());
-  }
-
-  static void ValidateForcedFailureObserved(
-      const JobObserverMock& job_observer) {
-    EXPECT_EQ(COMPLETION_CODE_ERROR, job_observer.completion_code);
-    EXPECT_STREQ(_T("Installation failed. Please try again. Error code = ")
-                 _T("0x81234567"), job_observer.completion_text);
-    EXPECT_EQ(MockRequestSaveFail::kUpdateCheckForcedFailure,
-              job_observer.completion_error_code);
-  }
-
-  static void ValidateNoEventObserved(
-      const JobObserverMock& job_observer) {
-    EXPECT_EQ(static_cast<CompletionCodes>(-1), job_observer.completion_code);
-    EXPECT_TRUE(job_observer.completion_text.IsEmpty());
-    EXPECT_EQ(0, job_observer.completion_error_code);
-  }
-
-  // Only applies to installs.
-  static void ValidateNoUpdateErrorObserved(
-      const JobObserverMock& job_observer) {
-    EXPECT_EQ(COMPLETION_CODE_ERROR, job_observer.completion_code);
-    EXPECT_STREQ(_T("Installation failed. Please try again. Error code = ")
-                 _T("0x80040809"), job_observer.completion_text);
-    EXPECT_EQ(GOOPDATE_E_NO_UPDATE_RESPONSE,
-              job_observer.completion_error_code);
-  }
-
-  // JobObserverCOMDecorator does not pass along the text and the COM interface
-  // does not support passing the error code so the COM mock sets E_UNEXPECTED.
-  static void ValidateComFailureObserved(
-      const JobObserverMock& job_observer) {
-    EXPECT_EQ(COMPLETION_CODE_ERROR, job_observer.completion_code);
-    EXPECT_TRUE(job_observer.completion_text.IsEmpty());
-    EXPECT_EQ(E_UNEXPECTED, job_observer.completion_error_code);
-  }
-
-  static void ValidateComSuccessObserved(
-      const JobObserverMock& job_observer) {
-    EXPECT_EQ(COMPLETION_CODE_SUCCESS, job_observer.completion_code);
-    EXPECT_TRUE(job_observer.completion_text.IsEmpty());
-    EXPECT_EQ(E_UNEXPECTED, job_observer.completion_error_code);
-  }
-
-  const std::vector<CString>& app_guids_omaha() const {
-      return app_guids_omaha_;
-  }
-  const std::vector<CString>& app_guids_omaha_app1() const {
-      return app_guids_omaha_app1_;
-  }
-  const std::vector<CString>& app_guids_omaha_app2() const {
-      return app_guids_omaha_app2_;
-  }
-  const std::vector<CString>& app_guids_omaha_app1_app2() const {
-      return app_guids_omaha_app1_app2_;
-  }
-  const std::vector<CString>& app_guids_app1() const { return app_guids_app1_; }
-  const std::vector<CString>& app_guids_app2() const { return app_guids_app2_; }
-
- private:
-  static std::vector<CString> app_guids_omaha_;
-  static std::vector<CString> app_guids_omaha_app1_;
-  static std::vector<CString> app_guids_omaha_app2_;
-  static std::vector<CString> app_guids_omaha_app1_app2_;
-  static std::vector<CString> app_guids_app1_;
-  static std::vector<CString> app_guids_app2_;
-};
-
-std::vector<CString> WorkerJobDoProcessTest::app_guids_omaha_;
-std::vector<CString> WorkerJobDoProcessTest::app_guids_omaha_app1_;
-std::vector<CString> WorkerJobDoProcessTest::app_guids_omaha_app2_;
-std::vector<CString> WorkerJobDoProcessTest::app_guids_omaha_app1_app2_;
-std::vector<CString> WorkerJobDoProcessTest::app_guids_app1_;
-std::vector<CString> WorkerJobDoProcessTest::app_guids_app2_;
-
-class WorkerJobDoProcessUpdateMachineTest : public WorkerJobDoProcessTest {
- protected:
-  virtual void SetUp() {
-    WorkerJobDoProcessTest::SetUp();
-
-    // Omaha is always registered.
-    ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENTS_GOOPDATE,
-                                      _T("pv"),
-                                      _T("5.6.7.8")));
-    ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE_GOOPDATE,
-                                      _T("pv"),
-                                      _T("5.6.7.8")));
-
-    // Register the product to check for updates.
-    ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENTS APP_GUID,
-                                      _T("pv"),
-                                      _T("1.2.3.4")));
-    ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE APP_GUID,
-                                      _T("pv"),
-                                      _T("1.2.3.4")));
-
-    args_.mode = COMMANDLINE_MODE_UA;
-    worker_job_.reset(
-        WorkerJobFactory::CreateWorkerJob(true, args_, &job_observer_));
-
-    ping_mock_ = new PingMock;
-    SetWorkerJobPing(ping_mock_);
-  }
-
-  // These tests cause the action to fail. This method validates the ping
-  // generated by ping_utils::SendCompletedPingsForAllProducts().
-  void ValidateCompletedPingsForAllProducts(const Request& ping_request,
-                                            bool is_canceled) {
-    EXPECT_TRUE(ping_request.is_machine());
-    EXPECT_EQ(2, ping_request.get_request_count());
-
-    AppRequestVector::const_iterator iter = ping_request.app_requests_begin();
-    ValidateCompletedPingForProduct(*iter, StringToGuid(kAppGuid), is_canceled);
-
-    ++iter;
-    ValidateCompletedPingForProduct(*iter, kGoopdateGuid, is_canceled);
-    StringToGuid(kAppGuid);
-  }
-
-  // TODO(omaha): I think this is a bug that we ping without an event.
-  // If not, fix this and replace this method with a check for no pings.
-  void ValidateNoPingEventForAllProducts(const Request& ping_request) {
-    EXPECT_TRUE(ping_request.is_machine());
-    EXPECT_EQ(2, ping_request.get_request_count());
-
-    AppRequestVector::const_iterator iter = ping_request.app_requests_begin();
-    ValidateNoPingEventForProduct(*iter, StringToGuid(kAppGuid));
-
-    ++iter;
-    ValidateNoPingEventForProduct(*iter, kGoopdateGuid);
-    StringToGuid(kAppGuid);
-  }
-
-  CommandLineArgs args_;
-  JobObserverMock job_observer_;
-  PingMock* ping_mock_;
-};
-
-class WorkerJobDoProcessInstallMachineTest : public WorkerJobDoProcessTest {
- protected:
-  virtual void SetUp() {
-    WorkerJobDoProcessTest::SetUp();
-
-    args_.is_silent_set = true;  // Prevent browser from launching on errors.
-    args_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-    args_.extra.apps.push_back(CommandLineAppArgs());
-    args_.extra.apps[0].app_guid = StringToGuid(kAppGuid);
-    args_.extra.apps[0].app_name = _T("Foo Bar");
-    args_.extra.apps[0].needs_admin = true;
-
-    worker_job_.reset(
-        WorkerJobFactory::CreateWorkerJob(true, args_, &job_observer_));
-  }
-
-  CommandLineArgs args_;
-  JobObserverMock job_observer_;
-};
-
-class WorkerJobDoProcessInstallGoogleUpdateMachineTest
-    : public WorkerJobDoProcessTest {
- protected:
-  virtual void SetUp() {
-    WorkerJobDoProcessTest::SetUp();
-
-    args_.is_silent_set = true;  // Prevent browser from launching on errors.
-    args_.mode = COMMANDLINE_MODE_IG;
-    args_.extra.apps.push_back(CommandLineAppArgs());
-    args_.extra.apps[0].app_guid = StringToGuid(kAppGuid);
-    args_.extra.apps[0].needs_admin = true;
-
-    AppData app_data;
-    app_data.set_app_guid(StringToGuid(kAppGuid));
-    app_data.set_is_machine_app(true);
-    products_.push_back(ProductData(app_data));
-
-    worker_job_.reset(
-        WorkerJobFactory::CreateWorkerJob(true, args_, &job_observer_));
-  }
-
-  CommandLineArgs args_;
-  ProductDataVector products_;
-  JobObserverMock job_observer_;
-};
-
-class WorkerJobDoProcessOnDemandUpdateMachineTest
-    : public WorkerJobDoProcessTest {
- protected:
-  virtual void SetUp() {
-    WorkerJobDoProcessTest::SetUp();
-
-    // Register the product as required by OnDemandUpdateStrategy::Init().
-    ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENTS APP_GUID,
-                                      _T("pv"),
-                                      _T("1.2.3.4")));
-
-    HRESULT hr = CComObject<IJobObserverMock>::CreateInstance(&job_observer_);
-    ASSERT_EQ(S_OK, hr);
-    job_holder_ = job_observer_;
-
-    worker_job_.reset();
-    ASSERT_SUCCEEDED(WorkerJobFactory::CreateOnDemandWorkerJob(
-        true,   // is_machine
-        false,  // is_update_check_only
-        _T("en"),
-        StringToGuid(kAppGuid),
-        job_holder_,
-        new WorkerComWrapperShutdownCallBack(false),
-        address(worker_job_)));
-  }
-
-  CComObject<IJobObserverMock>* job_observer_;
-  CComPtr<IJobObserver> job_holder_;
-};
-
-class WorkerJobIsAppInstallWorkerRunningTest : public WorkerJobTest {
- protected:
-  WorkerJobIsAppInstallWorkerRunningTest() {
-    args_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-    CommandLineAppArgs app_args;
-    app_args.app_name = _T("WorkerJobIsAppInstallWorkerRunningTest");
-    args_.extra.apps.push_back(app_args);
-  }
-
-  void TestIsAppInstallWorkerRunning(const CString& cmd_line,
-                                     bool is_machine,
-                                     bool expected_running) {
-    Process p(exe_to_test_, NULL);
-    ASSERT_TRUE(p.Start(cmd_line));
-
-    // Wait for the process to be ready. IsAppInstallWorkerRunning uses
-    // Process::GetCommandLine, which fails if it cannot ::ReadProcessMemory().
-    // Waiting for GetCommandLine() to succeed should ensure that the process is
-    // sufficiently initialized for this test.
-    // TODO(omaha): If we change to using Job Objects, we will not need this
-    // if we use ::AssignProcessToJobObject() from this test.
-    HRESULT hr = E_FAIL;
-    CString process_cmd;
-    for (int tries = 0; tries < 100 && FAILED(hr); ++tries) {
-      ::Sleep(50);
-      hr = Process::GetCommandLine(p.GetId(), &process_cmd);
-    }
-    EXPECT_SUCCEEDED(hr);
-
-    SetIsMachine(is_machine);
-    EXPECT_EQ(expected_running, IsAppInstallWorkerRunning());
-    EXPECT_TRUE(p.Terminate(1000));
-  }
-
-  CommandLineArgs args_;
-  CString cmd_line_;
-  JobObserverMock job_observer_;
-};
-
-// TODO(omaha): Test all methods of WorkerJob.
-
-TEST_F(WorkerJobDoProcessTest, OnDemandUpdate_AppNotRegistered) {
-  CComObject<IJobObserverMock>* job_observer;
-  CComPtr<IJobObserver> job_holder;
-
-  HRESULT hr = CComObject<IJobObserverMock>::CreateInstance(&job_observer);
-  ASSERT_EQ(S_OK, hr);
-  job_holder = job_observer;
-
-  worker_job_.reset();
-  EXPECT_EQ(GOOPDATE_E_APP_NOT_REGISTERED,
-      WorkerJobFactory::CreateOnDemandWorkerJob(
-      true,   // is_machine
-      false,  // is_update_check_only
-      _T("en"),
-      StringToGuid(kAppGuid),
-      job_holder,
-      new WorkerComWrapperShutdownCallBack(false),
-      address(worker_job_)));
-}
-
-//
-// Update apps tests.
-//
-
-// Also tests that the OemInstallPing is not sent.
-TEST_F(WorkerJobDoProcessUpdateMachineTest, UpdateCheckFails) {
-  SetMockMockRequestSaveFail();
-
-  EXPECT_EQ(MockRequestSaveFail::kUpdateCheckForcedFailure,
-            worker_job_->DoProcess());
-  EXPECT_EQ(MockRequestSaveFail::kUpdateCheckForcedFailure,
-            worker_job_->error_code());
-
-  ValidateForcedFailureObserved(job_observer_);
-
-  ASSERT_EQ(1, ping_mock_->ping_requests().size());
-  ValidateCompletedPingsForAllProducts(*ping_mock_->ping_requests()[0], false);
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-// Also tests that the OemInstallPing is not sent.
-TEST_F(WorkerJobDoProcessUpdateMachineTest, GroupPolicy_NoPolicy) {
-  SetMockRequestSaveNoUpdate(app_guids_omaha_app1());
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyAutoUpdateCheck(app_guids_omaha_app1());
-  ValidateNoEventObserved(job_observer_);
-
-  // TODO(omaha): I don't think there should be any pings.
-  ASSERT_EQ(1, ping_mock_->ping_requests().size());
-  ValidateNoPingEventForAllProducts(*ping_mock_->ping_requests()[0]);
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessUpdateMachineTest, GroupPolicy_InstallProhibited) {
-  SetMockRequestSaveNoUpdate(app_guids_omaha_app1());
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp, 0));
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyAutoUpdateCheck(app_guids_omaha_app1());
-  ValidateNoEventObserved(job_observer_);
-
-  ASSERT_EQ(1, ping_mock_->ping_requests().size());
-  ValidateNoPingEventForAllProducts(*ping_mock_->ping_requests()[0]);
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessUpdateMachineTest,
-       GroupPolicy_UpdateProhibitedForSingleApp) {
-  SetMockRequestSaveNoUpdate(app_guids_omaha_app1());
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp, 0));
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyAutoUpdateCheckWithDisabledApps(app_guids_omaha_app1(),
-                                        app_guids_app1());
-  ValidateNoEventObserved(job_observer_);
-
-  ASSERT_EQ(1, ping_mock_->ping_requests().size());
-  ValidateNoPingEventForAllProducts(*ping_mock_->ping_requests()[0]);
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(1, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessUpdateMachineTest,
-       GroupPolicy_ManualUpdateOnlyForSingleApp) {
-  SetMockRequestSaveNoUpdate(app_guids_omaha_app1());
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp, 2));
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyAutoUpdateCheckWithDisabledApps(app_guids_omaha_app1(),
-                                        app_guids_app1());
-  ValidateNoEventObserved(job_observer_);
-
-  ASSERT_EQ(1, ping_mock_->ping_requests().size());
-  ValidateNoPingEventForAllProducts(*ping_mock_->ping_requests()[0]);
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(1, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-// Use the default behavior when the value is not supported.
-TEST_F(WorkerJobDoProcessUpdateMachineTest, GroupPolicy_InvalidUpdateValue) {
-  SetMockRequestSaveNoUpdate(app_guids_omaha_app1());
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp, 3));
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyAutoUpdateCheck(app_guids_omaha_app1());
-  ValidateNoEventObserved(job_observer_);
-
-  ASSERT_EQ(1, ping_mock_->ping_requests().size());
-  ValidateNoPingEventForAllProducts(*ping_mock_->ping_requests()[0]);
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-// Omaha updates will always be performed.
-TEST_F(WorkerJobDoProcessUpdateMachineTest,
-       GroupPolicy_UpdateProhibitedForOmahaAndAllApps) {
-  SetMockRequestSaveNoUpdate(app_guids_omaha_app1());
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyGoopdate, 0));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp, 0));
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyAutoUpdateCheckWithDisabledApps(app_guids_omaha_app1(),
-                                        app_guids_app1());
-  ValidateNoEventObserved(job_observer_);
-
-  ASSERT_EQ(1, ping_mock_->ping_requests().size());
-  ValidateNoPingEventForAllProducts(*ping_mock_->ping_requests()[0]);
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(1, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-// This case should not happen because Omaha is not registered.
-TEST_F(WorkerJobDoProcessUpdateMachineTest,
-       GroupPolicy_UpdateProhibitedForAllRegisteredApps) {
-  SetMockRequestSaveNoUpdate(app_guids_app1());
-  ASSERT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_CLIENTS_GOOPDATE, _T("pv")));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp, 0));
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyAutoUpdateCheckWithDisabledApps(app_guids_app1(), app_guids_app1());
-
-  ASSERT_EQ(1, ping_mock_->ping_requests().size());
-  const Request& ping_request = *ping_mock_->ping_requests()[0];
-  EXPECT_TRUE(ping_request.is_machine());
-  EXPECT_EQ(1, ping_request.get_request_count());
-  ValidateNoPingEventForProduct(*ping_request.app_requests_begin(),
-                                StringToGuid(kAppGuid));
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(1, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-// This case should not happen because Omaha is not registered.
-TEST_F(WorkerJobDoProcessUpdateMachineTest,
-       GroupPolicy_UpdateProhibitedForAllAppsByPolicyOrEula) {
-  SetMockRequestSaveNoUpdate(app_guids_app1());
-  ASSERT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_CLIENTS_GOOPDATE, _T("pv")));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp, 0));
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENTS APP_GUID2,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE APP_GUID2,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kMachineClientStatePathApp2,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyAutoUpdateCheckWithDisabledApps(app_guids_app1(), app_guids_app1());
-
-  ASSERT_EQ(1, ping_mock_->ping_requests().size());
-  const Request& ping_request = *ping_mock_->ping_requests()[0];
-  EXPECT_TRUE(ping_request.is_machine());
-  EXPECT_EQ(1, ping_request.get_request_count());
-  ValidateNoPingEventForProduct(*ping_request.app_requests_begin(),
-                                StringToGuid(kAppGuid));
-
-  EXPECT_EQ(1, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(1, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-// Although a ping request is received, the real Ping object would not send it.
-TEST_F(WorkerJobDoProcessUpdateMachineTest, GoogleUpdateEulaNotAccepted) {
-  SetMockMockRequestSave();
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  EXPECT_EQ(GOOPDATE_E_CANNOT_USE_NETWORK, worker_job_->DoProcess());
-  EXPECT_EQ(GOOPDATE_E_CANNOT_USE_NETWORK, worker_job_->error_code());
-
-  VerifyNoUpdateCheckSent();
-
-  ASSERT_EQ(1, ping_mock_->ping_requests().size());
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessUpdateMachineTest, AppEulaNotAccepted) {
-  SetMockRequestSaveNoUpdate(app_guids_omaha_app1());
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENTS APP_GUID2,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE APP_GUID2,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kMachineClientStatePathApp2,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyAutoUpdateCheck(app_guids_omaha_app1());
-
-  ASSERT_EQ(1, ping_mock_->ping_requests().size());
-  // Update checks are sent for Omaha and App1 but not App2.
-  ValidateNoPingEventForAllProducts(*ping_mock_->ping_requests()[0]);
-
-  EXPECT_EQ(1, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessUpdateMachineTest, AppEulaAcceptedInClientState) {
-  SetMockRequestSaveNoUpdate(app_guids_omaha_app1());
-  EXPECT_SUCCEEDED(RegKey::SetValue(kMachineClientStatePathApp,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyAutoUpdateCheck(app_guids_omaha_app1());
-
-  ASSERT_EQ(1, ping_mock_->ping_requests().size());
-  ValidateNoPingEventForAllProducts(*ping_mock_->ping_requests()[0]);
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessUpdateMachineTest,
-       AppEulaNotAcceptedInClientStateButIsInClientStateMedium) {
-  SetMockRequestSaveNoUpdate(app_guids_omaha_app1());
-  EXPECT_SUCCEEDED(RegKey::SetValue(kMachineClientStatePathApp,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kMachineClientStateMediumPathApp,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(1)));
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyAutoUpdateCheck(app_guids_omaha_app1());
-
-  ASSERT_EQ(1, ping_mock_->ping_requests().size());
-  ValidateNoPingEventForAllProducts(*ping_mock_->ping_requests()[0]);
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-// EULA is checked first.
-TEST_F(WorkerJobDoProcessUpdateMachineTest,
-       AppEulaNotAcceptedAndUpdateProhibited) {
-  SetMockRequestSaveNoUpdate(app_guids_omaha());
-  EXPECT_SUCCEEDED(RegKey::SetValue(kMachineClientStatePathApp,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp, 0));
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyAutoUpdateCheck(app_guids_omaha());
-
-  ASSERT_EQ(1, ping_mock_->ping_requests().size());
-
-  // Only Omaha is in the completed ping because the app is disabled by EULA.
-  const Request& ping_request = *ping_mock_->ping_requests()[0];
-  EXPECT_TRUE(ping_request.is_machine());
-  EXPECT_EQ(1, ping_request.get_request_count());
-  ValidateNoPingEventForProduct(*ping_request.app_requests_begin(),
-                                kGoopdateGuid);
-
-  EXPECT_EQ(1, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessUpdateMachineTest,
-       AppEulaNotAcceptedOtherAppUpdateProhibited) {
-  SetMockRequestSaveNoUpdate(app_guids_omaha_app2());
-  EXPECT_SUCCEEDED(RegKey::SetValue(kMachineClientStatePathApp,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENTS APP_GUID2,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE APP_GUID2,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp2, 0));
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyAutoUpdateCheckWithDisabledApps(app_guids_omaha_app2(),
-                                        app_guids_app2());
-
-  ASSERT_EQ(1, ping_mock_->ping_requests().size());
-
-  // Only Omaha is in the completed ping because both apps are disabled.
-  const Request& ping_request = *ping_mock_->ping_requests()[0];
-  EXPECT_TRUE(ping_request.is_machine());
-  EXPECT_EQ(2, ping_request.get_request_count());
-  ValidateNoPingEventForProduct(*ping_request.app_requests_begin(),
-                                kGoopdateGuid);
-  ValidateNoPingEventForProduct(*(++ping_request.app_requests_begin()),
-                                StringToGuid(kAppGuid2));
-
-  EXPECT_EQ(1, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(1, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessUpdateMachineTest, OemInstallPing) {
-  SetMockRequestSaveNoUpdate(app_guids_omaha_app1());
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE APP_GUID,
-                                    _T("oeminstall"),
-                                    _T("1")));
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyAutoUpdateCheck(app_guids_omaha_app1());
-
-  ASSERT_EQ(2, ping_mock_->ping_requests().size());
-
-  const PingEvent& ping_event = GetSingleEventFromRequest(
-      *ping_mock_->ping_requests()[0],
-      StringToGuid(kAppGuid),
-      true);
-  EXPECT_EQ(PingEvent::EVENT_INSTALL_OEM_FIRST_CHECK, ping_event.event_type());
-  EXPECT_EQ(PingEvent::EVENT_RESULT_SUCCESS, ping_event.event_result());
-  EXPECT_EQ(0, ping_event.error_code());
-  EXPECT_EQ(0, ping_event.extra_code1());
-  EXPECT_EQ(_T("1.2.3.4"), ping_event.previous_version());
-
-  ValidateNoPingEventForAllProducts(*ping_mock_->ping_requests()[1]);
-
-  EXPECT_FALSE(RegKey::HasValue(MACHINE_REG_CLIENT_STATE APP_GUID,
-                                _T("oeminstall")));
-}
-
-TEST_F(WorkerJobDoProcessUpdateMachineTest, OemInstallPing_Failed) {
-  SetMockRequestSaveNoUpdate(app_guids_omaha_app1());
-  ping_mock_ = new PingMockFail();
-  SetWorkerJobPing(ping_mock_);
-
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE APP_GUID,
-                                    _T("oeminstall"),
-                                    _T("1")));
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyAutoUpdateCheck(app_guids_omaha_app1());
-
-  ASSERT_EQ(2, ping_mock_->ping_requests().size());
-
-  const PingEvent& ping_event = GetSingleEventFromRequest(
-      *ping_mock_->ping_requests()[0],
-      StringToGuid(kAppGuid),
-      true);
-  EXPECT_EQ(PingEvent::EVENT_INSTALL_OEM_FIRST_CHECK, ping_event.event_type());
-  EXPECT_EQ(PingEvent::EVENT_RESULT_SUCCESS, ping_event.event_result());
-  EXPECT_EQ(0, ping_event.error_code());
-  EXPECT_EQ(0, ping_event.extra_code1());
-  EXPECT_EQ(_T("1.2.3.4"), ping_event.previous_version());
-
-  ValidateNoPingEventForAllProducts(*ping_mock_->ping_requests()[1]);
-
-  EXPECT_TRUE(RegKey::HasValue(MACHINE_REG_CLIENT_STATE APP_GUID,
-                               _T("oeminstall")));
-}
-
-TEST_F(WorkerJobDoProcessUpdateMachineTest, OemInstallPing_WorkerJobCanceled) {
-  SetMockMockRequestSave();
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE APP_GUID,
-                                    _T("oeminstall"),
-                                    _T("1")));
-  worker_job_->Cancel();
-
-  EXPECT_EQ(GOOPDATE_E_WORKER_CANCELLED, worker_job_->DoProcess());
-  EXPECT_EQ(GOOPDATE_E_WORKER_CANCELLED, worker_job_->error_code());
-
-  VerifyNoUpdateCheckSent();
-
-  ASSERT_EQ(1, ping_mock_->ping_requests().size());
-  ValidateCompletedPingsForAllProducts(*ping_mock_->ping_requests()[0], true);
-
-  EXPECT_TRUE(RegKey::HasValue(MACHINE_REG_CLIENT_STATE APP_GUID,
-                               _T("oeminstall")));
-}
-
-//
-// Install tests.
-//
-
-TEST_F(WorkerJobDoProcessInstallMachineTest, UpdateCheckFails) {
-  SetMockMockRequestSaveFail();
-
-  EXPECT_EQ(MockRequestSaveFail::kUpdateCheckForcedFailure,
-            worker_job_->DoProcess());
-  EXPECT_EQ(MockRequestSaveFail::kUpdateCheckForcedFailure,
-            worker_job_->error_code());
-
-  ValidateForcedFailureObserved(job_observer_);
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessInstallMachineTest, GroupPolicy_NoPolicy) {
-  SetMockRequestSaveNoUpdate(app_guids_app1());
-
-  EXPECT_EQ(GOOPDATE_E_NO_UPDATE_RESPONSE, worker_job_->DoProcess());
-  EXPECT_EQ(GOOPDATE_E_NO_UPDATE_RESPONSE, worker_job_->error_code());
-
-  VerifyInstallUpdateCheck(app_guids_app1());
-  ValidateNoUpdateErrorObserved(job_observer_);
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessInstallMachineTest, GroupPolicy_InstallProhibited) {
-  SetMockMockRequestSave();
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp, 0));
-
-  EXPECT_EQ(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY,
-            worker_job_->DoProcess());
-  EXPECT_EQ(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY,
-            worker_job_->error_code());
-
-  EXPECT_EQ(COMPLETION_CODE_ERROR, job_observer_.completion_code);
-  EXPECT_STREQ(_T("Your network administrator has applied a Group Policy that ")
-               _T("prevents installation of Foo Bar."),
-               job_observer_.completion_text);
-  EXPECT_EQ(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY,
-            job_observer_.completion_error_code);
-
-  VerifyNoUpdateCheckSent();
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(1, metric_worker_apps_not_installed_group_policy.value());
-}
-
-// Use the default behavior when the value is not supported.
-TEST_F(WorkerJobDoProcessInstallMachineTest, GroupPolicy_InvalidInstallValue) {
-  SetMockRequestSaveNoUpdate(app_guids_app1());
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp, 2));
-
-  EXPECT_EQ(GOOPDATE_E_NO_UPDATE_RESPONSE, worker_job_->DoProcess());
-  EXPECT_EQ(GOOPDATE_E_NO_UPDATE_RESPONSE, worker_job_->error_code());
-
-  VerifyInstallUpdateCheck(app_guids_app1());
-  ValidateNoUpdateErrorObserved(job_observer_);
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-// Tests which app name is displayed.
-TEST_F(WorkerJobDoProcessInstallMachineTest,
-       GroupPolicy_InstallThreeAppsLastTwoProhibited) {
-  SetMockMockRequestSave();
-  args_.extra.apps.push_back(CommandLineAppArgs());
-  args_.extra.apps[1].app_guid = StringToGuid(kAppGuid2);
-  args_.extra.apps[1].app_name = _T("App 2");
-  args_.extra.apps[1].needs_admin = true;
-  args_.extra.apps.push_back(CommandLineAppArgs());
-  args_.extra.apps[2].app_guid = StringToGuid(kAppGuid3);
-  args_.extra.apps[2].app_name = _T("Third App");
-  args_.extra.apps[2].needs_admin = true;
-
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp2, 0));
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp3, 0));
-
-  EXPECT_EQ(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY,
-            worker_job_->DoProcess());
-  EXPECT_EQ(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY,
-            worker_job_->error_code());
-
-  VerifyNoUpdateCheckSent();
-
-  EXPECT_EQ(COMPLETION_CODE_ERROR, job_observer_.completion_code);
-  EXPECT_STREQ(_T("Your network administrator has applied a Group Policy that ")
-               _T("prevents installation of App 2."),
-               job_observer_.completion_text);
-  EXPECT_EQ(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY,
-            job_observer_.completion_error_code);
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(2, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessInstallMachineTest, GroupPolicy_UpdateProhibited) {
-  SetMockRequestSaveNoUpdate(app_guids_app1());
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp, 0));
-
-  EXPECT_EQ(GOOPDATE_E_NO_UPDATE_RESPONSE, worker_job_->DoProcess());
-  EXPECT_EQ(GOOPDATE_E_NO_UPDATE_RESPONSE, worker_job_->error_code());
-
-  VerifyInstallUpdateCheck(app_guids_app1());
-  ValidateNoUpdateErrorObserved(job_observer_);
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessInstallMachineTest,
-       GroupPolicy_InstallAndUpdateProhibited) {
-  SetMockMockRequestSave();
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp, 0));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp, 0));
-
-  EXPECT_EQ(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY,
-            worker_job_->DoProcess());
-  EXPECT_EQ(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY,
-            worker_job_->error_code());
-
-  VerifyNoUpdateCheckSent();
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(1, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessInstallMachineTest,
-       GroupPolicy_InstallOfDifferentAppProhibited) {
-  SetMockRequestSaveNoUpdate(app_guids_app1());
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp2, 0));
-
-  EXPECT_EQ(GOOPDATE_E_NO_UPDATE_RESPONSE, worker_job_->DoProcess());
-  EXPECT_EQ(GOOPDATE_E_NO_UPDATE_RESPONSE, worker_job_->error_code());
-
-  VerifyInstallUpdateCheck(app_guids_app1());
-  ValidateNoUpdateErrorObserved(job_observer_);
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessInstallMachineTest,
-       GroupPolicy_InstallTwoAppsOneProhibited) {
-  SetMockMockRequestSave();
-  args_.extra.apps.push_back(CommandLineAppArgs());
-  args_.extra.apps[1].app_guid = StringToGuid(kAppGuid2);
-  args_.extra.apps[1].app_name = _T("App 2");
-  args_.extra.apps[1].needs_admin = true;
-
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp, 0));
-
-  EXPECT_EQ(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY,
-            worker_job_->DoProcess());
-  EXPECT_EQ(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY,
-            worker_job_->error_code());
-
-  VerifyNoUpdateCheckSent();
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(1, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessInstallMachineTest, AppEulaNotAccepted) {
-  SetMockRequestSaveNoUpdate(app_guids_app1());
-  EXPECT_SUCCEEDED(RegKey::SetValue(kMachineClientStatePathApp,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  EXPECT_EQ(GOOPDATE_E_NO_UPDATE_RESPONSE, worker_job_->DoProcess());
-  EXPECT_EQ(GOOPDATE_E_NO_UPDATE_RESPONSE, worker_job_->error_code());
-
-  VerifyInstallUpdateCheck(app_guids_app1());
-  ValidateNoUpdateErrorObserved(job_observer_);
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessInstallMachineTest,
-       AppEulaNotAcceptedAndInstallProhibited) {
-  SetMockMockRequestSave();
-  EXPECT_SUCCEEDED(RegKey::SetValue(kMachineClientStatePathApp,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp, 0));
-  EXPECT_EQ(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY,
-            worker_job_->DoProcess());
-  EXPECT_EQ(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY,
-            worker_job_->error_code());
-
-  VerifyNoUpdateCheckSent();
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(1, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessInstallMachineTest,
-       AppEulaNotAcceptedAndOtherAppInstallProhibited) {
-  SetMockRequestSaveNoUpdate(app_guids_app1());
-  EXPECT_SUCCEEDED(RegKey::SetValue(kMachineClientStatePathApp,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENTS APP_GUID2,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE APP_GUID2,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp2, 0));
-
-  EXPECT_EQ(GOOPDATE_E_NO_UPDATE_RESPONSE, worker_job_->DoProcess());
-  EXPECT_EQ(GOOPDATE_E_NO_UPDATE_RESPONSE, worker_job_->error_code());
-
-  VerifyInstallUpdateCheck(app_guids_app1());
-  ValidateNoUpdateErrorObserved(job_observer_);
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-//
-// Install Google Update and app tests.
-//
-// We cannot run DoProcess for the InstallGoopdateAndAppsStrategy case because
-// it attempts to install Google Update before checking the Group Policy.
-// Therefore, test RemoveDisallowedApps() directly.
-
-TEST_F(WorkerJobDoProcessInstallGoogleUpdateMachineTest, GroupPolicy_NoPolicy) {
-  EXPECT_SUCCEEDED(worker_job_->strategy()->RemoveDisallowedApps(&products_));
-  EXPECT_EQ(1, products_.size());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessInstallGoogleUpdateMachineTest,
-       GroupPolicy_InstallProhibited) {
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp, 0));
-
-  EXPECT_EQ(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY,
-            worker_job_->strategy()->RemoveDisallowedApps(&products_));
-  EXPECT_TRUE(products_.empty());
-
-  EXPECT_SUCCEEDED(worker_job_->error_code()) <<
-      _T("error_code code does not run.");
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(1, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessInstallGoogleUpdateMachineTest,
-       GroupPolicy_UpdateProhibited) {
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp, 0));
-
-  EXPECT_SUCCEEDED(worker_job_->strategy()->RemoveDisallowedApps(&products_));
-  EXPECT_EQ(1, products_.size());
-
-  EXPECT_SUCCEEDED(worker_job_->error_code()) <<
-      _T("error_code code does not run.");
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessInstallGoogleUpdateMachineTest,
-       GroupPolicy_InstallAndUpdateProhibited) {
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp, 0));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp, 0));
-
-  EXPECT_EQ(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY,
-            worker_job_->strategy()->RemoveDisallowedApps(&products_));
-  EXPECT_TRUE(products_.empty());
-
-  EXPECT_SUCCEEDED(worker_job_->error_code()) <<
-      _T("error_code code does not run.");
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(1, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessInstallGoogleUpdateMachineTest,
-       GroupPolicy_InstallOfDifferentAppProhibited) {
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp2, 0));
-
-  EXPECT_SUCCEEDED(worker_job_->strategy()->RemoveDisallowedApps(&products_));
-  EXPECT_EQ(1, products_.size());
-
-  EXPECT_SUCCEEDED(worker_job_->error_code()) <<
-      _T("error_code code does not run.");
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessInstallGoogleUpdateMachineTest,
-       GroupPolicy_InstallTwoAppsOneProhibited) {
-  args_.extra.apps.push_back(CommandLineAppArgs());
-  args_.extra.apps[1].app_guid = StringToGuid(kAppGuid2);
-  args_.extra.apps[1].app_name = _T("App 2");
-  args_.extra.apps[1].needs_admin = true;
-
-  AppData app_data;
-  app_data.set_app_guid(StringToGuid(kAppGuid2));
-  app_data.set_is_machine_app(true);
-  products_.push_back(ProductData(app_data));
-  ASSERT_EQ(2, products_.size());
-
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp, 0));
-
-  EXPECT_EQ(GOOPDATE_E_APP_INSTALL_DISABLED_BY_POLICY,
-            worker_job_->strategy()->RemoveDisallowedApps(&products_));
-  EXPECT_EQ(1, products_.size());  // One of two was removed.
-
-  EXPECT_SUCCEEDED(worker_job_->error_code()) <<
-      _T("error_code code does not run.");
-
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(1, metric_worker_apps_not_installed_group_policy.value());
-}
-
-//
-// On-demand update tests.
-//
-
-TEST_F(WorkerJobDoProcessOnDemandUpdateMachineTest, UpdateCheckFails) {
-  SetMockMockRequestSaveFail();
-
-  EXPECT_EQ(MockRequestSaveFail::kUpdateCheckForcedFailure,
-            worker_job_->DoProcess());
-  EXPECT_EQ(MockRequestSaveFail::kUpdateCheckForcedFailure,
-            worker_job_->error_code());
-
-  ValidateComFailureObserved(job_observer_->job_observer_mock);
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessOnDemandUpdateMachineTest, GroupPolicy_NoPolicy) {
-  SetMockRequestSaveNoUpdate(app_guids_app1());
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyOnDemandUpdateCheck(app_guids_app1());
-  ValidateComSuccessObserved(job_observer_->job_observer_mock);
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessOnDemandUpdateMachineTest,
-       GroupPolicy_InstallProhibited) {
-  SetMockRequestSaveNoUpdate(app_guids_app1());
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp, 0));
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyOnDemandUpdateCheck(app_guids_app1());
-  ValidateComSuccessObserved(job_observer_->job_observer_mock);
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessOnDemandUpdateMachineTest,
-       GroupPolicy_UpdateProhibited) {
-  SetMockMockRequestSave();
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp, 0));
-
-  EXPECT_EQ(GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY, worker_job_->DoProcess());
-  EXPECT_EQ(GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY,
-            worker_job_->error_code());
-
-  VerifyNoUpdateCheckSent();
-  ValidateComFailureObserved(job_observer_->job_observer_mock);
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(1, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessOnDemandUpdateMachineTest,
-       GroupPolicy_ManualUpdateOnly) {
-  SetMockRequestSaveNoUpdate(app_guids_app1());
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp, 2));
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyOnDemandUpdateCheck(app_guids_app1());
-  ValidateComSuccessObserved(job_observer_->job_observer_mock);
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessOnDemandUpdateMachineTest,
-       GroupPolicy_InstallAndUpdateProhibited) {
-  SetMockMockRequestSave();
-  EXPECT_SUCCEEDED(SetPolicy(kInstallPolicyApp, 0));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp, 0));
-
-  EXPECT_EQ(GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY, worker_job_->DoProcess());
-  EXPECT_EQ(GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY,
-            worker_job_->error_code());
-
-  VerifyNoUpdateCheckSent();
-  ValidateComFailureObserved(job_observer_->job_observer_mock);
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(1, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessOnDemandUpdateMachineTest,
-       GroupPolicy_UpdateOfDifferentAppProhibited) {
-  SetMockRequestSaveNoUpdate(app_guids_app1());
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp2, 0));
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyOnDemandUpdateCheck(app_guids_app1());
-  ValidateComSuccessObserved(job_observer_->job_observer_mock);
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessOnDemandUpdateMachineTest, AppEulaNotAccepted) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kMachineClientStatePathApp,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  worker_job_.reset();
-  EXPECT_SUCCEEDED(WorkerJobFactory::CreateOnDemandWorkerJob(
-      true,   // is_machine
-      false,  // is_update_check_only
-      _T("en"),
-      StringToGuid(kAppGuid),
-      job_holder_,
-      new WorkerComWrapperShutdownCallBack(false),
-      address(worker_job_)));
-  SetMockMockRequestSave();
-
-  EXPECT_EQ(GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED,
-            worker_job_->DoProcess());
-  EXPECT_EQ(GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED,
-            worker_job_->error_code());
-
-  VerifyNoUpdateCheckSent();
-  ValidateComFailureObserved(job_observer_->job_observer_mock);
-  EXPECT_EQ(1, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-TEST_F(WorkerJobDoProcessOnDemandUpdateMachineTest, OtherAppEulaNotAccepted) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENTS APP_GUID2,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  ASSERT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_CLIENT_STATE APP_GUID2,
-                                    _T("pv"),
-                                    _T("1.2.3.4")));
-  EXPECT_SUCCEEDED(RegKey::SetValue(kMachineClientStatePathApp2,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-
-  worker_job_.reset();
-  EXPECT_SUCCEEDED(WorkerJobFactory::CreateOnDemandWorkerJob(
-      true,   // is_machine
-      false,  // is_update_check_only
-      _T("en"),
-      StringToGuid(kAppGuid),
-      job_holder_,
-      new WorkerComWrapperShutdownCallBack(false),
-      address(worker_job_)));
-  SetMockRequestSaveNoUpdate(app_guids_app1());
-
-  EXPECT_SUCCEEDED(worker_job_->DoProcess());
-  EXPECT_SUCCEEDED(worker_job_->error_code());
-
-  VerifyOnDemandUpdateCheck(app_guids_app1());
-  ValidateComSuccessObserved(job_observer_->job_observer_mock);
-  EXPECT_EQ(0, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-// EULA is checked first.
-TEST_F(WorkerJobDoProcessOnDemandUpdateMachineTest,
-       AppEulaNotAcceptedAndUpdateProhibited) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(kMachineClientStatePathApp,
-                                    _T("eulaaccepted"),
-                                    static_cast<DWORD>(0)));
-  EXPECT_SUCCEEDED(SetPolicy(kUpdatePolicyApp, 0));
-
-  worker_job_.reset();
-  EXPECT_SUCCEEDED(WorkerJobFactory::CreateOnDemandWorkerJob(
-      true,   // is_machine
-      false,  // is_update_check_only
-      _T("en"),
-      StringToGuid(kAppGuid),
-      job_holder_,
-      new WorkerComWrapperShutdownCallBack(false),
-      address(worker_job_)));
-  SetMockMockRequestSave();
-
-  EXPECT_EQ(GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED,
-            worker_job_->DoProcess());
-  EXPECT_EQ(GOOPDATE_E_APP_UPDATE_DISABLED_EULA_NOT_ACCEPTED,
-            worker_job_->error_code());
-
-  VerifyNoUpdateCheckSent();
-  ValidateComFailureObserved(job_observer_->job_observer_mock);
-  EXPECT_EQ(1, metric_worker_apps_not_updated_eula.value());
-  EXPECT_EQ(0, metric_worker_apps_not_updated_group_policy.value());
-  EXPECT_EQ(0, metric_worker_apps_not_installed_group_policy.value());
-}
-
-// TODO(omaha): Add some user tests for the above.
-
-// Create a worker with the /handoff switch and needs_admin=false.
-// Call IsAppInstallWorkerRunning for machine. Should return false.
-TEST_F(WorkerJobIsAppInstallWorkerRunningTest, HandoffNeedsAdminFalseMachine) {
-  args_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  worker_job_.reset(
-      WorkerJobFactory::CreateWorkerJob(true, args_, &job_observer_));
-  cmd_line_.Format(_T("%s %s"), handoff_cmd_line, false_extra_args);
-  TestIsAppInstallWorkerRunning(cmd_line_, true, false);
-}
-
-// Create a worker with the /handoff switch and needs_admin=true.
-// Call IsAppInstallWorkerRunning for machine. Should return true.
-TEST_F(WorkerJobIsAppInstallWorkerRunningTest, HandoffNeedsAdminTrueMachine) {
-  args_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  worker_job_.reset(
-      WorkerJobFactory::CreateWorkerJob(true, args_, &job_observer_));
-  cmd_line_.Format(_T("%s %s"), handoff_cmd_line, true_extra_args);
-  TestIsAppInstallWorkerRunning(cmd_line_, true, true);
-}
-
-// Create a worker with the /handoff switch and needs_admin=false.
-// Call IsAppInstallWorkerRunning for user. Should return true.
-TEST_F(WorkerJobIsAppInstallWorkerRunningTest, HandoffNeedsAdminFalseUser) {
-  args_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  worker_job_.reset(
-      WorkerJobFactory::CreateWorkerJob(false, args_, &job_observer_));
-  cmd_line_.Format(_T("%s %s"), handoff_cmd_line, false_extra_args);
-  TestIsAppInstallWorkerRunning(cmd_line_, false, true);
-}
-
-// Create a worker with the /handoff switch and needs_admin=true.
-// Call IsAppInstallWorkerRunning for user. Should return false.
-TEST_F(WorkerJobIsAppInstallWorkerRunningTest, HandoffNeedsAdminTrueUser) {
-  args_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  worker_job_.reset(
-      WorkerJobFactory::CreateWorkerJob(false, args_, &job_observer_));
-  cmd_line_.Format(_T("%s %s"), handoff_cmd_line, true_extra_args);
-  TestIsAppInstallWorkerRunning(cmd_line_, false, false);
-}
-
-// Create a worker with the /ig switch and needs_admin=false.
-// Call IsAppInstallWorkerRunning for machine. Should return false.
-TEST_F(WorkerJobIsAppInstallWorkerRunningTest, IgNeedsAdminFalseMachine) {
-  args_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  worker_job_.reset(
-      WorkerJobFactory::CreateWorkerJob(true, args_, &job_observer_));
-  cmd_line_.Format(_T("%s %s"), finish_setup_cmd_line, false_extra_args);
-  TestIsAppInstallWorkerRunning(cmd_line_, true, false);
-}
-
-// Create a worker with the /ig switch and needs_admin=true.
-// Call IsAppInstallWorkerRunning for machine. Should return true.
-TEST_F(WorkerJobIsAppInstallWorkerRunningTest, IgNeedsAdminTrueMachine) {
-  args_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  worker_job_.reset(
-      WorkerJobFactory::CreateWorkerJob(true, args_, &job_observer_));
-  cmd_line_.Format(_T("%s %s"), finish_setup_cmd_line, true_extra_args);
-  TestIsAppInstallWorkerRunning(cmd_line_, true, true);
-}
-
-// Create a worker with the /ig switch and needs_admin=false.
-// Call IsAppInstallWorkerRunning for user. Should return true.
-TEST_F(WorkerJobIsAppInstallWorkerRunningTest, IgNeedsAdminFalseUser) {
-  args_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  worker_job_.reset(
-      WorkerJobFactory::CreateWorkerJob(false, args_, &job_observer_));
-  cmd_line_.Format(_T("%s %s"), finish_setup_cmd_line, false_extra_args);
-  TestIsAppInstallWorkerRunning(cmd_line_, false, true);
-}
-
-// Create a worker with the /ig switch and needs_admin=true.
-// Call IsAppInstallWorkerRunning for user. Should return false.
-TEST_F(WorkerJobIsAppInstallWorkerRunningTest, IgNeedsAdminTrueUser) {
-  args_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  worker_job_.reset(
-      WorkerJobFactory::CreateWorkerJob(false, args_, &job_observer_));
-  cmd_line_.Format(_T("%s %s"), finish_setup_cmd_line, true_extra_args);
-  TestIsAppInstallWorkerRunning(cmd_line_, false, false);
-}
-
-TEST_F(WorkerJobIsAppInstallWorkerRunningTest, NoArgsUser) {
-  args_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  worker_job_.reset(
-      WorkerJobFactory::CreateWorkerJob(false, args_, &job_observer_));
-  TestIsAppInstallWorkerRunning(_T(""), false, false);
-}
-
-TEST_F(WorkerJobIsAppInstallWorkerRunningTest, NoArgsMachine) {
-  args_.mode = COMMANDLINE_MODE_HANDOFF_INSTALL;
-  worker_job_.reset(
-      WorkerJobFactory::CreateWorkerJob(true, args_, &job_observer_));
-  TestIsAppInstallWorkerRunning(_T(""), true, false);
-}
-
-TEST_F(WorkerJobRegistryProtectedTest,
-       BuildUninstallPing_User_NoUninstalledApp) {
-  ASSERT_SUCCEEDED(RegKey::CreateKey(USER_REG_CLIENT_STATE));
-
-  scoped_ptr<Request> uninstall_ping;
-  EXPECT_HRESULT_SUCCEEDED(BuildUninstallPing(false, address(uninstall_ping)));
-  EXPECT_EQ(0, uninstall_ping->get_request_count());
-}
-
-// Create a mock client state for one app and build an uninstall ping.
-// Expect the client state to be cleared at the end.
-TEST_F(WorkerJobRegistryProtectedTest, BuildUninstallPing_User_UninstalledApp) {
-  CString client_state_appkey(USER_REG_CLIENT_STATE);
-  client_state_appkey.Append(_T("{C78D67E2-D7E9-4b62-9869-FCDCFC4C9323}"));
-  EXPECT_HRESULT_SUCCEEDED(
-    RegKey::SetValue(client_state_appkey, kRegValueProductVersion, _T("1.0")));
-
-  scoped_ptr<Request> uninstall_ping;
-  EXPECT_HRESULT_SUCCEEDED(BuildUninstallPing(false, address(uninstall_ping)));
-  EXPECT_EQ(1, uninstall_ping->get_request_count());
-  EXPECT_EQ(PingEvent::EVENT_UNINSTALL, GetPingType(*uninstall_ping));
-  EXPECT_FALSE(RegKey::HasKey(client_state_appkey));
-}
-
-TEST_F(WorkerJobRegistryProtectedTest,
-       BuildUninstallPing_Machine_NoUninstalledApp) {
-  ASSERT_SUCCEEDED(RegKey::CreateKey(MACHINE_REG_CLIENT_STATE));
-
-  scoped_ptr<Request> uninstall_ping;
-  EXPECT_HRESULT_SUCCEEDED(BuildUninstallPing(true, address(uninstall_ping)));
-  EXPECT_EQ(0, uninstall_ping->get_request_count());
-}
-
-// Create a mock client state for one app and build an uninstall ping.
-// Expect the client state to be cleared at the end.
-TEST_F(WorkerJobRegistryProtectedTest,
-       BuildUninstallPing_Machine_UninstalledApp) {
-  CString client_state_appkey = MACHINE_REG_CLIENT_STATE;
-  client_state_appkey.Append(_T("{C78D67E2-D7E9-4b62-9869-FCDCFC4C9323}"));
-  EXPECT_HRESULT_SUCCEEDED(
-    RegKey::SetValue(client_state_appkey, kRegValueProductVersion, _T("1.0")));
-
-  scoped_ptr<Request> uninstall_ping;
-  EXPECT_HRESULT_SUCCEEDED(BuildUninstallPing(true, address(uninstall_ping)));
-  EXPECT_EQ(1, uninstall_ping->get_request_count());
-  EXPECT_EQ(PingEvent::EVENT_UNINSTALL, GetPingType(*uninstall_ping));
-  EXPECT_FALSE(RegKey::HasKey(client_state_appkey));
-}
-
-}  // namespace omaha
diff --git a/worker/worker_metrics.cc b/worker/worker_metrics.cc
deleted file mode 100644
index a9b008d..0000000
--- a/worker/worker_metrics.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-
-#include "omaha/worker/worker_metrics.h"
-
-namespace omaha {
-
-DEFINE_METRIC_count(worker_download_total);
-DEFINE_METRIC_count(worker_download_succeeded);
-
-DEFINE_METRIC_count(worker_download_move_total);
-DEFINE_METRIC_count(worker_download_move_succeeded);
-
-DEFINE_METRIC_count(worker_another_install_in_progress);
-DEFINE_METRIC_count(worker_another_update_in_progress);
-
-DEFINE_METRIC_timing(worker_ui_cancel_ms);
-DEFINE_METRIC_count(worker_ui_cancels);
-
-DEFINE_METRIC_count(worker_ui_click_x);
-
-DEFINE_METRIC_count(worker_ui_esc_key_total);
-
-DEFINE_METRIC_count(worker_ui_restart_browser_buttons_displayed);
-DEFINE_METRIC_count(worker_ui_restart_browser_now_click);
-DEFINE_METRIC_count(worker_ui_restart_all_browsers_buttons_displayed);
-DEFINE_METRIC_count(worker_ui_restart_all_browsers_now_click);
-DEFINE_METRIC_count(worker_ui_reboot_buttons_displayed);
-DEFINE_METRIC_count(worker_ui_reboot_now_click);
-
-DEFINE_METRIC_count(worker_ui_get_help_displayed);
-DEFINE_METRIC_count(worker_ui_get_help_click);
-
-DEFINE_METRIC_count(worker_install_execute_total);
-DEFINE_METRIC_count(worker_install_execute_msi_total);
-
-DEFINE_METRIC_count(worker_install_msi_in_progress_detected_update);
-DEFINE_METRIC_count(worker_install_msi_in_progress_retry_succeeded_update);
-DEFINE_METRIC_integer(
-    worker_install_msi_in_progress_retry_succeeded_tries_update);
-
-DEFINE_METRIC_count(worker_install_msi_in_progress_detected_install);
-DEFINE_METRIC_count(worker_install_msi_in_progress_retry_succeeded_install);
-DEFINE_METRIC_integer(
-    worker_install_msi_in_progress_retry_succeeded_tries_install);
-
-DEFINE_METRIC_integer(worker_shell_version);
-
-DEFINE_METRIC_bool(worker_is_windows_installing);
-
-DEFINE_METRIC_bool(worker_is_uac_disabled);
-
-DEFINE_METRIC_bool(worker_is_clickonce_disabled);
-
-DEFINE_METRIC_bool(worker_has_software_firewall);
-
-DEFINE_METRIC_count(worker_silent_update_running_on_batteries);
-
-DEFINE_METRIC_count(worker_update_check_total);
-DEFINE_METRIC_count(worker_update_check_succeeded);
-
-DEFINE_METRIC_integer(worker_apps_not_updated_eula);
-DEFINE_METRIC_integer(worker_apps_not_updated_group_policy);
-DEFINE_METRIC_integer(worker_apps_not_installed_group_policy);
-
-DEFINE_METRIC_count(worker_skipped_app_update_for_self_update);
-
-DEFINE_METRIC_count(worker_self_updates_available);
-DEFINE_METRIC_count(worker_self_updates_succeeded);
-
-DEFINE_METRIC_count(worker_app_updates_available);
-DEFINE_METRIC_count(worker_app_updates_succeeded);
-
-DEFINE_METRIC_integer(worker_self_update_responses);
-DEFINE_METRIC_integer(worker_self_update_response_time_since_first_ms);
-
-DEFINE_METRIC_integer(worker_app_max_update_responses_app_high);
-DEFINE_METRIC_integer(worker_app_max_update_responses);
-DEFINE_METRIC_integer(worker_app_max_update_responses_ms_since_first);
-
-DEFINE_METRIC_timing(ping_failed_ms);
-DEFINE_METRIC_timing(ping_succeeded_ms);
-
-}  // namespace omaha
diff --git a/worker/worker_metrics.h b/worker/worker_metrics.h
deleted file mode 100644
index ebe4ed7..0000000
--- a/worker/worker_metrics.h
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-// Declares the usage metrics used by the worker module.
-
-#ifndef OMAHA_WORKER_WORKER_METRICS_H__
-#define OMAHA_WORKER_WORKER_METRICS_H__
-
-#include "omaha/statsreport/metrics.h"
-
-namespace omaha {
-
-// How many times the download manager attempted to download a file.
-DECLARE_METRIC_count(worker_download_total);
-// How many times the download manager successfully downloaded a file.
-DECLARE_METRIC_count(worker_download_succeeded);
-
-// How many times the download manager attempted to copy the temporary file
-// to the download directory.
-DECLARE_METRIC_count(worker_download_move_total);
-// How many times the download manager successfully copied the temporary file
-// to the download directory.
-DECLARE_METRIC_count(worker_download_move_succeeded);
-
-// How many times the install worker encountered another worker that
-// is already running.
-DECLARE_METRIC_count(worker_another_install_in_progress);
-
-// How many times the update worker encountered another worker that
-// is already running.
-DECLARE_METRIC_count(worker_another_update_in_progress);
-
-// Time (ms) until the user canceled.
-DECLARE_METRIC_timing(worker_ui_cancel_ms);
-// How many times the user canceled. Only includes confirmed cancels.
-DECLARE_METRIC_count(worker_ui_cancels);
-
-// How many times the user has clicked on the x button of the UI.
-DECLARE_METRIC_count(worker_ui_click_x);
-
-// How many times the user has hit the esc key.
-DECLARE_METRIC_count(worker_ui_esc_key_total);
-
-// How many times the "Restart Browser Now/Later" buttons were displayed.
-DECLARE_METRIC_count(worker_ui_restart_browser_buttons_displayed);
-// How many times the user clicked "Restart Browser Now".
-DECLARE_METRIC_count(worker_ui_restart_browser_now_click);
-// How many times the "Restart Browsers Now/Later" buttons were displayed.
-DECLARE_METRIC_count(worker_ui_restart_all_browsers_buttons_displayed);
-// How many times the user clicked "Restart Browsers Now".
-DECLARE_METRIC_count(worker_ui_restart_all_browsers_now_click);
-// How many times the "Restart Now/Later" (reboot) buttons were displayed.
-DECLARE_METRIC_count(worker_ui_reboot_buttons_displayed);
-// How many times the user clicked "Restart Now" (reboot).
-DECLARE_METRIC_count(worker_ui_reboot_now_click);
-
-// How many times the user was offered the "Help Me Fix This" link.
-DECLARE_METRIC_count(worker_ui_get_help_displayed);
-// How many times the user clicked the "Help Me Fix This" link.
-DECLARE_METRIC_count(worker_ui_get_help_click);
-
-// How many times ExecuteAndWaitForInstaller was called.
-DECLARE_METRIC_count(worker_install_execute_total);
-// How many times ExecuteAndWaitForInstaller was called for an MSI.
-DECLARE_METRIC_count(worker_install_execute_msi_total);
-
-// How many MSI install attempts encountered ERROR_INSTALL_ALREADY_RUNNING
-// during an update.
-DECLARE_METRIC_count(worker_install_msi_in_progress_detected_update);
-// How many times successfully installed MSI in a retry after
-// encountering ERROR_INSTALL_ALREADY_RUNNING during an update.
-DECLARE_METRIC_count(worker_install_msi_in_progress_retry_succeeded_update);
-// Number of retries attempted because of ERROR_INSTALL_ALREADY_RUNNING before
-// succeeded during an update.
-DECLARE_METRIC_integer(
-    worker_install_msi_in_progress_retry_succeeded_tries_update);
-
-// How many MSI install attempts encountered ERROR_INSTALL_ALREADY_RUNNING
-// during an install.
-DECLARE_METRIC_count(worker_install_msi_in_progress_detected_install);
-// How many times successfully installed MSI in a retry after
-// encountering ERROR_INSTALL_ALREADY_RUNNING during an install.
-DECLARE_METRIC_count(worker_install_msi_in_progress_retry_succeeded_install);
-// Number of retries attempted because of ERROR_INSTALL_ALREADY_RUNNING before
-// succeeded during an install.
-DECLARE_METRIC_integer(
-    worker_install_msi_in_progress_retry_succeeded_tries_install);
-
-// Version of the GoogleUpdate.exe shell in use.
-DECLARE_METRIC_integer(worker_shell_version);
-
-// True if Windows is installing (is in audit mode). This should never be true.
-DECLARE_METRIC_bool(worker_is_windows_installing);
-
-// True if UAC is disabled.
-DECLARE_METRIC_bool(worker_is_uac_disabled);
-
-// True if ClickOnce is disabled for the Internet zone for the current user.
-DECLARE_METRIC_bool(worker_is_clickonce_disabled);
-
-// True if a software firewall is detected.
-DECLARE_METRIC_bool(worker_has_software_firewall);
-
-// How many times the computer was on batteries when doing an update check
-// for apps.
-DECLARE_METRIC_count(worker_silent_update_running_on_batteries);
-
-// How many times an update check was attempted. Does not include installs.
-DECLARE_METRIC_count(worker_update_check_total);
-// How many times an update check succeeded. Does not include installs.
-DECLARE_METRIC_count(worker_update_check_succeeded);
-
-// Number of apps for which update checks skipped because EULA is not accepted.
-DECLARE_METRIC_integer(worker_apps_not_updated_eula);
-// Number of apps for which update checks skipped because of Group Policy.
-DECLARE_METRIC_integer(worker_apps_not_updated_group_policy);
-// Number of apps not installed because of Group Policy.
-// In most case, this will be 0 or 1 because it saves only the value from the
-// last install process.
-DECLARE_METRIC_integer(worker_apps_not_installed_group_policy);
-
-// How many times Omaha did not update an app because an Omaha update was
-// available at the same time. Only incremented if both an Omaha and app update
-// are available in the same update check. Max one increment per update check.
-DECLARE_METRIC_count(worker_skipped_app_update_for_self_update);
-
-// How many times a self update was available.
-DECLARE_METRIC_count(worker_self_updates_available);
-// How many times a self update succeeded.
-DECLARE_METRIC_count(worker_self_updates_succeeded);
-
-// How many updates have been available. Each app in an update check is counted.
-// If a self-update was available, no apps are counted.
-DECLARE_METRIC_count(worker_app_updates_available);
-// How many app updates succeeded.
-DECLARE_METRIC_count(worker_app_updates_succeeded);
-
-// Number of times Omaha has received a self-update response without
-// successfully updating.
-DECLARE_METRIC_integer(worker_self_update_responses);
-// The time (ms) since the first time Omaha received a self-update response.
-// Only reported if Omaha fails to update after first such response.
-DECLARE_METRIC_integer(worker_self_update_response_time_since_first_ms);
-
-// The most significant/left half of the GUID for the app for which Omaha has
-// received the most update responses without successfully updating.
-DECLARE_METRIC_integer(worker_app_max_update_responses_app_high);
-// Maximum number of times for any app that Omaha has received an update
-// response without successfully updating.
-DECLARE_METRIC_integer(worker_app_max_update_responses);
-// The time (ms) since the first time Omaha received an update response for the
-// app with the most update failures.
-DECLARE_METRIC_integer(worker_app_max_update_responses_ms_since_first);
-
-// Time (ms) spent in SendPing() when DoSendPing() fails.
-DECLARE_METRIC_timing(ping_failed_ms);
-// Time (ms) spent in SendPing() when the ping succeeds.
-DECLARE_METRIC_timing(ping_succeeded_ms);
-
-}  // namespace omaha
-
-#endif  // OMAHA_WORKER_WORKER_METRICS_H__
diff --git a/worker/worker_unittest.cc b/worker/worker_unittest.cc
deleted file mode 100644
index 0576e83..0000000
--- a/worker/worker_unittest.cc
+++ /dev/null
@@ -1,330 +0,0 @@
-// Copyright 2008-2009 Google Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// ========================================================================
-
-#include "omaha/common/time.h"
-#include "omaha/goopdate/stats_uploader.h"
-#include "omaha/statsreport/aggregator.h"
-#include "omaha/testing/unit_test.h"
-#include "omaha/worker/application_manager.h"
-#include "omaha/worker/worker.h"
-#include "omaha/worker/worker_metrics.h"
-#include "omaha/worker/worker-internal.h"
-
-namespace omaha {
-
-namespace {
-
-const TCHAR* const kDailyUsageStatsKeyPath =
-    _T("HKCU\\Software\\Google\\Update\\UsageStats\\Daily");
-
-// The alphabetical order of these is important for
-// RecordUpdateAvailableUsageStatsTest.
-const TCHAR* const kApp1 = _T("{0C480772-AC73-418f-9603-66303DA4C7AA}");
-const TCHAR* const kApp2 = _T("{89906BCD-4D12-4c9b-B5BA-8286051CB8D9}");
-const TCHAR* const kApp3 = _T("{F5A1FE97-CF5A-47b8-8B28-2A72F9A57A45}");
-
-const uint64 kApp1GuidUpper = 0x0C480772AC73418f;
-const uint64 kApp2GuidUpper = 0x89906BCD4D124c9b;
-
-const TCHAR* const kApp1ClientsKeyPathUser =
-    _T("HKCU\\Software\\Google\\Update\\Clients\\")
-    _T("{0C480772-AC73-418f-9603-66303DA4C7AA}");
-const TCHAR* const kApp2ClientsKeyPathUser =
-    _T("HKCU\\Software\\Google\\Update\\Clients\\")
-    _T("{89906BCD-4D12-4c9b-B5BA-8286051CB8D9}");
-const TCHAR* const kApp3ClientsKeyPathUser =
-    _T("HKCU\\Software\\Google\\Update\\Clients\\")
-    _T("{F5A1FE97-CF5A-47b8-8B28-2A72F9A57A45}");
-
-const TCHAR* const kApp1ClientStateKeyPathUser =
-    _T("HKCU\\Software\\Google\\Update\\ClientState\\")
-    _T("{0C480772-AC73-418f-9603-66303DA4C7AA}");
-const TCHAR* const kApp2ClientStateKeyPathUser =
-    _T("HKCU\\Software\\Google\\Update\\ClientState\\")
-    _T("{89906BCD-4D12-4c9b-B5BA-8286051CB8D9}");
-const TCHAR* const kApp3ClientStateKeyPathUser =
-    _T("HKCU\\Software\\Google\\Update\\ClientState\\")
-    _T("{F5A1FE97-CF5A-47b8-8B28-2A72F9A57A45}");
-
-}  // namespace
-
-class WorkerTest : public testing::Test {
- protected:
-  Worker worker_;
-};
-
-// TODO(omaha): Test all methods of Worker
-
-class RecordUpdateAvailableUsageStatsTest : public testing::Test {
- protected:
-  RecordUpdateAvailableUsageStatsTest() : is_machine_(false) {}
-
-  static void SetUpTestCase() {
-    // Initialize the global metrics collection.
-    stats_report::g_global_metrics.Initialize();
-  }
-
-  static void TearDownTestCase() {
-    // The global metrics collection must be uninitialized before the metrics
-    // destructors are called.
-    stats_report::g_global_metrics.Uninitialize();
-  }
-
-  virtual void SetUp() {
-    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
-    OverrideRegistryHives(kRegistryHiveOverrideRoot);
-
-    metric_worker_self_update_responses.Set(0);
-    metric_worker_self_update_response_time_since_first_ms.Set(0);
-    metric_worker_app_max_update_responses_app_high.Set(0);
-    metric_worker_app_max_update_responses.Set(0);
-    metric_worker_app_max_update_responses_ms_since_first.Set(0);
-
-    ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENTS_GOOPDATE,
-                                      kRegValueProductVersion,
-                                      _T("0.1.0.0")));
-    ASSERT_SUCCEEDED(RegKey::SetValue(kApp1ClientsKeyPathUser,
-                                      kRegValueProductVersion,
-                                      _T("0.1")));
-
-    ASSERT_SUCCEEDED(RegKey::CreateKey(USER_REG_CLIENT_STATE_GOOPDATE));
-    ASSERT_SUCCEEDED(RegKey::CreateKey(kApp1ClientStateKeyPathUser));
-  }
-
-  int GetNumProducts() {
-    AppManager app_manager(is_machine_);
-    ProductDataVector products;
-    VERIFY1(SUCCEEDED(app_manager.GetRegisteredProducts(&products)));
-    return products.size();
-  }
-
-  virtual void TearDown() {
-    RestoreRegistryHives();
-    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
-  }
-
-  bool is_machine_;
-  scoped_ptr<AppManager> app_manager_;
-};
-
-TEST_F(RecordUpdateAvailableUsageStatsTest, NoData) {
-  ASSERT_EQ(2, GetNumProducts());
-
-  internal::RecordUpdateAvailableUsageStats(is_machine_);
-
-  EXPECT_EQ(0, metric_worker_self_update_responses.value());
-  EXPECT_EQ(0, metric_worker_self_update_response_time_since_first_ms.value());
-  EXPECT_EQ(0, metric_worker_app_max_update_responses_app_high.value());
-  EXPECT_EQ(0, metric_worker_app_max_update_responses.value());
-  EXPECT_EQ(0, metric_worker_app_max_update_responses_ms_since_first.value());
-}
-
-TEST_F(RecordUpdateAvailableUsageStatsTest, OmahaDataOnly) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("UpdateAvailableCount"),
-                                    static_cast<DWORD>(123456)));
-  ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("UpdateAvailableSince"),
-                                    static_cast<DWORD64>(10)));
-
-  ASSERT_EQ(2, GetNumProducts());
-
-  const time64 current_time_100ns(GetCurrent100NSTime());
-  const uint64 expected_ms_since_first_update =
-      (current_time_100ns - 10) / kMillisecsTo100ns;
-
-  internal::RecordUpdateAvailableUsageStats(is_machine_);
-
-  EXPECT_EQ(123456, metric_worker_self_update_responses.value());
-
-  EXPECT_LE(expected_ms_since_first_update,
-            metric_worker_self_update_response_time_since_first_ms.value());
-  EXPECT_GT(expected_ms_since_first_update + 10 * kMsPerSec,
-            metric_worker_self_update_response_time_since_first_ms.value());
-
-  EXPECT_EQ(0, metric_worker_app_max_update_responses_app_high.value());
-  EXPECT_EQ(0, metric_worker_app_max_update_responses.value());
-  EXPECT_EQ(0, metric_worker_app_max_update_responses_ms_since_first.value());
-}
-
-TEST_F(RecordUpdateAvailableUsageStatsTest, OneAppOnly) {
-  ASSERT_SUCCEEDED(RegKey::SetValue(kApp1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableCount"),
-                                    static_cast<DWORD>(123456)));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kApp1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableSince"),
-                                    static_cast<DWORD64>(10)));
-
-  ASSERT_EQ(2, GetNumProducts());
-
-  const time64 current_time_100ns(GetCurrent100NSTime());
-  const uint64 expected_ms_since_first_update =
-      (current_time_100ns - 10) / kMillisecsTo100ns;
-
-  internal::RecordUpdateAvailableUsageStats(is_machine_);
-
-  EXPECT_EQ(0, metric_worker_self_update_responses.value());
-  EXPECT_EQ(0, metric_worker_self_update_response_time_since_first_ms.value());
-
-  EXPECT_EQ(kApp1GuidUpper,
-            metric_worker_app_max_update_responses_app_high.value());
-  EXPECT_EQ(123456, metric_worker_app_max_update_responses.value());
-  EXPECT_LE(expected_ms_since_first_update,
-            metric_worker_app_max_update_responses_ms_since_first.value());
-  EXPECT_GT(expected_ms_since_first_update + 10 * kMsPerSec,
-            metric_worker_app_max_update_responses_ms_since_first.value());
-}
-
-// It is important that Omaha's count is the largest.
-// All app data should be from app 2, which has the greatest count, a middle
-// time, and an alphabetically middle GUID
-TEST_F(RecordUpdateAvailableUsageStatsTest, OmahaAndSeveralApps) {
-  const DWORD64 kApp2SinceTime = 1000 * kSecsTo100ns;
-
-  ASSERT_SUCCEEDED(RegKey::SetValue(kApp2ClientsKeyPathUser,
-                                    kRegValueProductVersion,
-                                    _T("1.2")));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kApp3ClientsKeyPathUser,
-                                    kRegValueProductVersion,
-                                    _T("2.3")));
-
-  ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("UpdateAvailableCount"),
-                                    static_cast<DWORD>(0x99887766)));
-  ASSERT_SUCCEEDED(RegKey::SetValue(USER_REG_CLIENT_STATE_GOOPDATE,
-                                    _T("UpdateAvailableSince"),
-                                    static_cast<DWORD64>(1)));
-
-  ASSERT_SUCCEEDED(RegKey::SetValue(kApp1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableCount"),
-                                    static_cast<DWORD>(1)));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kApp1ClientStateKeyPathUser,
-                                    _T("UpdateAvailableSince"),
-                                    static_cast<DWORD64>(1)));
-
-  ASSERT_SUCCEEDED(RegKey::SetValue(kApp2ClientStateKeyPathUser,
-                                    _T("UpdateAvailableCount"),
-                                    static_cast<DWORD>(9876543)));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kApp2ClientStateKeyPathUser,
-                                    _T("UpdateAvailableSince"),
-                                    kApp2SinceTime));
-
-  ASSERT_SUCCEEDED(RegKey::SetValue(kApp3ClientStateKeyPathUser,
-                                    _T("UpdateAvailableCount"),
-                                    static_cast<DWORD>(234)));
-  ASSERT_SUCCEEDED(RegKey::SetValue(kApp3ClientStateKeyPathUser,
-                                    _T("UpdateAvailableSince"),
-                                    static_cast<DWORD64>(128580000000000000)));
-
-  ASSERT_EQ(4, GetNumProducts());
-
-  const time64 current_time_100ns(GetCurrent100NSTime());
-  const uint64 goopdate_expected_ms_since_first_update =
-      (current_time_100ns - 1) / kMillisecsTo100ns;
-
-  const uint64 app_expected_ms_since_first_update =
-      (current_time_100ns - kApp2SinceTime) / kMillisecsTo100ns;
-
-  internal::RecordUpdateAvailableUsageStats(is_machine_);
-
-  EXPECT_EQ(0x99887766, metric_worker_self_update_responses.value());
-  EXPECT_LE(goopdate_expected_ms_since_first_update,
-            metric_worker_self_update_response_time_since_first_ms.value());
-  EXPECT_GT(goopdate_expected_ms_since_first_update + 10 * kMsPerSec,
-            metric_worker_self_update_response_time_since_first_ms.value());
-
-  EXPECT_EQ(kApp2GuidUpper,
-            metric_worker_app_max_update_responses_app_high.value());
-  EXPECT_EQ(9876543, metric_worker_app_max_update_responses.value());
-  EXPECT_LE(app_expected_ms_since_first_update,
-            metric_worker_app_max_update_responses_ms_since_first.value());
-  EXPECT_GT(app_expected_ms_since_first_update + 10 * kMsPerSec,
-            metric_worker_app_max_update_responses_ms_since_first.value());
-}
-
-class SendSelfUpdateFailurePingTest : public testing::Test {
- protected:
-  virtual void SetUp() {
-    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
-    // Overriding HKLM prevents the Windows DNS resolver from working.
-    // Only override HKCU and run the tests as user.
-    OverrideSpecifiedRegistryHives(kRegistryHiveOverrideRoot, false, true);
-  }
-
-  virtual void TearDown() {
-    RestoreRegistryHives();
-    RegKey::DeleteKey(kRegistryHiveOverrideRoot);
-  }
-};
-
-TEST_F(SendSelfUpdateFailurePingTest, UserKeyDoesNotExist) {
-  ExpectAsserts expect_asserts;
-  internal::SendSelfUpdateFailurePing(false);
-}
-
-TEST_F(SendSelfUpdateFailurePingTest, MachineKeyDoesNotExist) {
-  OverrideRegistryHives(kRegistryHiveOverrideRoot);
-  ExpectAsserts expect_asserts;
-  internal::SendSelfUpdateFailurePing(true);
-}
-
-TEST_F(SendSelfUpdateFailurePingTest, UserUpdateErrorCodeDoesNotExist) {
-  EXPECT_SUCCEEDED(RegKey::CreateKey(USER_REG_UPDATE));
-  internal::SendSelfUpdateFailurePing(false);
-}
-
-TEST_F(SendSelfUpdateFailurePingTest, MachineUpdateErrorCodeDoesNotExist) {
-  OverrideRegistryHives(kRegistryHiveOverrideRoot);
-  EXPECT_SUCCEEDED(RegKey::CreateKey(MACHINE_REG_UPDATE));
-  internal::SendSelfUpdateFailurePing(true);
-}
-
-TEST_F(SendSelfUpdateFailurePingTest, UserAllValuesPresent) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    kRegValueSelfUpdateErrorCode,
-                                    static_cast<DWORD>(0x87654321)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    kRegValueSelfUpdateExtraCode1,
-                                    static_cast<DWORD>(55)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(USER_REG_UPDATE,
-                                    kRegValueSelfUpdateVersion,
-                                    _T("0.2.4.8")));
-  internal::SendSelfUpdateFailurePing(false);
-}
-
-TEST_F(SendSelfUpdateFailurePingTest, UserValuesPresentInMachineOnly) {
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    kRegValueSelfUpdateErrorCode,
-                                    static_cast<DWORD>(0x87654321)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    kRegValueSelfUpdateExtraCode1,
-                                    static_cast<DWORD>(55)));
-  EXPECT_SUCCEEDED(RegKey::SetValue(MACHINE_REG_UPDATE,
-                                    kRegValueSelfUpdateVersion,
-                                    _T("0.2.4.8")));
-
-  ExpectAsserts expect_asserts;
-  internal::SendSelfUpdateFailurePing(false);
-
-  // Clean up HKLM, which isn't overridden.
-  EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE,
-                                       kRegValueSelfUpdateErrorCode));
-  EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE,
-                                       kRegValueSelfUpdateExtraCode1));
-  EXPECT_SUCCEEDED(RegKey::DeleteValue(MACHINE_REG_UPDATE,
-                                       kRegValueSelfUpdateVersion));
-}
-
-}  // namespace omaha